<commit_before>package main

import (
	"net/http"
	"time"

	"github.com/gonfire/oauth2"
)

func authorizationEndpoint(w http.ResponseWriter, r *http.Request) {
	// parse authorization request
	req, err := oauth2.ParseAuthorizationRequest(r)
	if err != nil {
		oauth2.WriteError(w, err)
		return
	}

	// make sure the response type is known
	if !req.ResponseType.Known() {
		oauth2.WriteError(w, oauth2.InvalidRequest(req.State, "Unknown response type"))
		return
	}

	// get client
	client, found := clients[req.ClientID]
	if !found {
		oauth2.WriteError(w, oauth2.InvalidClient(req.State, "Unknown client"))
		return
	}

	// validate redirect uri
	if client.redirectURI != req.RedirectURI {
		oauth2.WriteError(w, oauth2.InvalidRequest(req.State, "Invalid redirect URI"))
		return
	}

	// show info notice on a GET request
	if r.Method == "GET" {
		w.Write([]byte("This authentication server does not provide an authorization form."))
		return
	}

	// triage based on response type
	if req.ResponseType.Token() {
		handleImplicitGrant(w, r, req)
	} else if req.ResponseType.Code() {
		handleAuthorizationCodeGrantAuthorization(w, r, req)
	}
}

func handleImplicitGrant(w http.ResponseWriter, r *http.Request, req *oauth2.AuthorizationRequest) {
	// validate scope
	if !allowedScope.Includes(req.Scope) {
		oauth2.RedirectError(w, req.RedirectURI, true, oauth2.InvalidScope(req.State, oauth2.NoDescription))
		return
	}

	// validate user credentials
	owner, found := users[r.PostForm.Get("username")]
	if !found || !sameHash(owner.secret, r.PostForm.Get("password")) {
		oauth2.RedirectError(w, req.RedirectURI, true, oauth2.AccessDenied(req.State, oauth2.NoDescription))
		return
	}

	// generate new access token
	accessToken, err := oauth2.GenerateToken(secret, 32)
	if err != nil {
		panic(err)
	}

	// prepare response
	res := oauth2.NewBearerTokenResponse(accessToken.String(), int(tokenLifespan/time.Second))

	// set granted scope
	res.Scope = req.Scope

	// set state
	res.State = req.State

	// save access token
	accessTokens[accessToken.SignatureString()] = token{
		clientID:  req.ClientID,
		username:  owner.id,
		signature: accessToken.SignatureString(),
		expiresAt: time.Now().Add(tokenLifespan),
		scope:     req.Scope,
	}

	// write response
	oauth2.RedirectTokenResponse(w, req.RedirectURI, res)
}

func handleAuthorizationCodeGrantAuthorization(w http.ResponseWriter, r *http.Request, req *oauth2.AuthorizationRequest) {
	// validate scope
	if !allowedScope.Includes(req.Scope) {
		oauth2.RedirectError(w, req.RedirectURI, false, oauth2.InvalidScope(req.State, oauth2.NoDescription))
		return
	}

	// validate user credentials
	owner, found := users[r.PostForm.Get("username")]
	if !found || !sameHash(owner.secret, r.PostForm.Get("password")) {
		oauth2.RedirectError(w, req.RedirectURI, false, oauth2.AccessDenied(req.State, oauth2.NoDescription))
		return
	}

	// generate new authorization code
	authorizationCode, err := oauth2.GenerateToken(secret, 32)
	if err != nil {
		panic(err)
	}

	// prepare response
	res := oauth2.NewCodeResponse(authorizationCode.String())

	// set state
	res.State = req.State

	// save authorization code
	authorizationCodes[authorizationCode.SignatureString()] = token{
		clientID:    req.ClientID,
		username:    owner.id,
		signature:   authorizationCode.SignatureString(),
		expiresAt:   time.Now().Add(authorizationCodeLifespan),
		scope:       req.Scope,
		redirectURI: req.RedirectURI,
	}

	// write response
	oauth2.RedirectCodeResponse(w, req.RedirectURI, res)
}

func tokenEndpoint(w http.ResponseWriter, r *http.Request) {
	// parse token request
	req, err := oauth2.ParseTokenRequest(r)
	if err != nil {
		oauth2.WriteError(w, err)
		return
	}

	// make sure the grant type is known
	if !req.GrantType.Known() {
		oauth2.WriteError(w, oauth2.InvalidRequest(req.State, "Unknown grant type"))
		return
	}

	// check if client is confidential
	if !req.Confidential() {
		oauth2.WriteError(w, oauth2.InvalidRequest(req.State, "Non confidential client"))
		return
	}

	// authenticate client
	client, found := clients[req.ClientID]
	if !found || !sameHash(client.secret, req.ClientSecret) {
		oauth2.WriteError(w, oauth2.InvalidClient(req.State, "Unknown client"))
		return
	}

	// triage grant type
	if req.GrantType.Password() {
		handleResourceOwnerPasswordCredentialsGrant(w, req)
	} else if req.GrantType.ClientCredentials() {
		handleClientCredentialsGrant(w, req)
	} else if req.GrantType.AuthorizationCode() {
		handleAuthorizationCodeGrant(w, req)
	} else if req.GrantType.RefreshToken() {
		handleRefreshTokenGrant(w, req)
	}
}

func handleResourceOwnerPasswordCredentialsGrant(w http.ResponseWriter, req *oauth2.TokenRequest) {
	// authenticate resource owner
	owner, found := users[req.Username]
	if !found || !sameHash(owner.secret, req.Password) {
		oauth2.WriteError(w, oauth2.AccessDenied(req.State, oauth2.NoDescription))
		return
	}

	// check scope
	if !allowedScope.Includes(req.Scope) {
		oauth2.WriteError(w, oauth2.InvalidScope(req.State, oauth2.NoDescription))
		return
	}

	// issue access and refresh token
	at, rt, res := createTokensAndResponse(req)

	// save tokens
	saveTokens(at, rt, req.Scope, req.ClientID, req.Username)

	// write response
	oauth2.WriteTokenResponse(w, res)
}

func handleClientCredentialsGrant(w http.ResponseWriter, req *oauth2.TokenRequest) {
	// check scope
	if !allowedScope.Includes(req.Scope) {
		oauth2.WriteError(w, oauth2.InvalidScope(req.State, oauth2.NoDescription))
		return
	}

	// issue access and refresh token
	at, rt, res := createTokensAndResponse(req)

	// save tokens
	saveTokens(at, rt, req.Scope, req.ClientID, "")

	// write response
	oauth2.WriteTokenResponse(w, res)
}

func handleAuthorizationCodeGrant(w http.ResponseWriter, req *oauth2.TokenRequest) {
	// parse authorization code
	authorizationCode, err := oauth2.ParseToken(secret, req.Code)
	if err != nil {
		oauth2.WriteError(w, oauth2.InvalidRequest(req.State, err.Error()))
		return
	}

	// get stored authorization code by signature
	storedAuthorizationCode, found := authorizationCodes[authorizationCode.SignatureString()]
	if !found {
		oauth2.WriteError(w, oauth2.InvalidGrant(req.State, "Unkown authorization code"))
		return
	}

	// validate ownership
	if storedAuthorizationCode.clientID != req.ClientID {
		oauth2.WriteError(w, oauth2.InvalidGrant(req.State, "Invalid authorization code ownership"))
		return
	}

	// validate scope and expiration
	if !storedAuthorizationCode.scope.Includes(req.Scope) || storedAuthorizationCode.expiresAt.Before(time.Now()) {
		oauth2.WriteError(w, oauth2.InvalidScope(req.State, "Scope exceeds originaly granted scope"))
		return
	}

	// validate redirect uri
	if storedAuthorizationCode.redirectURI != req.RedirectURI {
		oauth2.WriteError(w, oauth2.InvalidGrant(req.State, "Changed redirect uri"))
	}

	// issue new access and refresh token
	at, rt, res := createTokensAndResponse(req)

	// save tokens
	saveTokens(at, rt, req.Scope, req.ClientID, "")

	// delete used authorization code
	delete(authorizationCodes, authorizationCode.SignatureString())

	// write response
	oauth2.WriteTokenResponse(w, res)
}

func handleRefreshTokenGrant(w http.ResponseWriter, req *oauth2.TokenRequest) {
	// parse refresh token
	refreshToken, err := oauth2.ParseToken(secret, req.RefreshToken)
	if err != nil {
		oauth2.WriteError(w, oauth2.InvalidRequest(req.State, err.Error()))
		return
	}

	// get stored refresh token by signature
	storedRefreshToken, found := refreshTokens[refreshToken.SignatureString()]
	if !found {
		oauth2.WriteError(w, oauth2.InvalidGrant(req.State, "Unknown refresh token"))
		return
	}

	// validate ownership
	if storedRefreshToken.clientID != req.ClientID {
		oauth2.WriteError(w, oauth2.InvalidGrant(req.State, "Invalid refresh token ownership"))
		return
	}

	// validate scope and expiration
	if !storedRefreshToken.scope.Includes(req.Scope) || storedRefreshToken.expiresAt.Before(time.Now()) {
		oauth2.WriteError(w, oauth2.InvalidScope(req.State, "Scope exceeds originaly granted scope"))
		return
	}

	// issue new access and refresh token
	at, rt, res := createTokensAndResponse(req)

	// save tokens
	saveTokens(at, rt, req.Scope, req.ClientID, storedRefreshToken.username)

	// delete used refresh token
	delete(refreshTokens, refreshToken.SignatureString())

	// write response
	oauth2.WriteTokenResponse(w, res)
}

func createTokensAndResponse(req *oauth2.TokenRequest) (*oauth2.Token, *oauth2.Token, *oauth2.TokenResponse) {
	// generate new access token
	accessToken, err := oauth2.GenerateToken(secret, 32)
	if err != nil {
		panic(err)
	}

	// generate new refresh token
	refreshToken, err := oauth2.GenerateToken(secret, 32)
	if err != nil {
		panic(err)
	}

	// prepare response
	res := oauth2.NewBearerTokenResponse(accessToken.String(), int(tokenLifespan/time.Second))

	// set granted scope
	res.Scope = req.Scope

	// set refresh token
	res.RefreshToken = refreshToken.String()

	return accessToken, refreshToken, res
}

func saveTokens(accessToken, refreshToken *oauth2.Token, scope oauth2.Scope, clientID, username string) {
	// save access token
	accessTokens[accessToken.SignatureString()] = token{
		clientID:  clientID,
		username:  username,
		signature: accessToken.SignatureString(),
		expiresAt: time.Now().Add(tokenLifespan),
		scope:     scope,
	}

	// save refresh token
	refreshTokens[refreshToken.SignatureString()] = token{
		clientID:  clientID,
		username:  username,
		signature: refreshToken.SignatureString(),
		expiresAt: time.Now().Add(tokenLifespan),
		scope:     scope,
	}
}
<commit_msg>fix typos<commit_after>package main

import (
	"net/http"
	"time"

	"github.com/gonfire/oauth2"
)

func authorizationEndpoint(w http.ResponseWriter, r *http.Request) {
	// parse authorization request
	req, err := oauth2.ParseAuthorizationRequest(r)
	if err != nil {
		oauth2.WriteError(w, err)
		return
	}

	// make sure the response type is known
	if !req.ResponseType.Known() {
		oauth2.WriteError(w, oauth2.InvalidRequest(req.State, "Unknown response type"))
		return
	}

	// get client
	client, found := clients[req.ClientID]
	if !found {
		oauth2.WriteError(w, oauth2.InvalidClient(req.State, "Unknown client"))
		return
	}

	// validate redirect uri
	if client.redirectURI != req.RedirectURI {
		oauth2.WriteError(w, oauth2.InvalidRequest(req.State, "Invalid redirect URI"))
		return
	}

	// show info notice on a GET request
	if r.Method == "GET" {
		w.Write([]byte("This authentication server does not provide an authorization form."))
		return
	}

	// triage based on response type
	if req.ResponseType.Token() {
		handleImplicitGrant(w, r, req)
	} else if req.ResponseType.Code() {
		handleAuthorizationCodeGrantAuthorization(w, r, req)
	}
}

func handleImplicitGrant(w http.ResponseWriter, r *http.Request, req *oauth2.AuthorizationRequest) {
	// validate scope
	if !allowedScope.Includes(req.Scope) {
		oauth2.RedirectError(w, req.RedirectURI, true, oauth2.InvalidScope(req.State, oauth2.NoDescription))
		return
	}

	// validate user credentials
	owner, found := users[r.PostForm.Get("username")]
	if !found || !sameHash(owner.secret, r.PostForm.Get("password")) {
		oauth2.RedirectError(w, req.RedirectURI, true, oauth2.AccessDenied(req.State, oauth2.NoDescription))
		return
	}

	// generate new access token
	accessToken, err := oauth2.GenerateToken(secret, 32)
	if err != nil {
		panic(err)
	}

	// prepare response
	res := oauth2.NewBearerTokenResponse(accessToken.String(), int(tokenLifespan/time.Second))

	// set granted scope
	res.Scope = req.Scope

	// set state
	res.State = req.State

	// save access token
	accessTokens[accessToken.SignatureString()] = token{
		clientID:  req.ClientID,
		username:  owner.id,
		signature: accessToken.SignatureString(),
		expiresAt: time.Now().Add(tokenLifespan),
		scope:     req.Scope,
	}

	// write response
	oauth2.RedirectTokenResponse(w, req.RedirectURI, res)
}

func handleAuthorizationCodeGrantAuthorization(w http.ResponseWriter, r *http.Request, req *oauth2.AuthorizationRequest) {
	// validate scope
	if !allowedScope.Includes(req.Scope) {
		oauth2.RedirectError(w, req.RedirectURI, false, oauth2.InvalidScope(req.State, oauth2.NoDescription))
		return
	}

	// validate user credentials
	owner, found := users[r.PostForm.Get("username")]
	if !found || !sameHash(owner.secret, r.PostForm.Get("password")) {
		oauth2.RedirectError(w, req.RedirectURI, false, oauth2.AccessDenied(req.State, oauth2.NoDescription))
		return
	}

	// generate new authorization code
	authorizationCode, err := oauth2.GenerateToken(secret, 32)
	if err != nil {
		panic(err)
	}

	// prepare response
	res := oauth2.NewCodeResponse(authorizationCode.String())

	// set state
	res.State = req.State

	// save authorization code
	authorizationCodes[authorizationCode.SignatureString()] = token{
		clientID:    req.ClientID,
		username:    owner.id,
		signature:   authorizationCode.SignatureString(),
		expiresAt:   time.Now().Add(authorizationCodeLifespan),
		scope:       req.Scope,
		redirectURI: req.RedirectURI,
	}

	// write response
	oauth2.RedirectCodeResponse(w, req.RedirectURI, res)
}

func tokenEndpoint(w http.ResponseWriter, r *http.Request) {
	// parse token request
	req, err := oauth2.ParseTokenRequest(r)
	if err != nil {
		oauth2.WriteError(w, err)
		return
	}

	// make sure the grant type is known
	if !req.GrantType.Known() {
		oauth2.WriteError(w, oauth2.InvalidRequest(req.State, "Unknown grant type"))
		return
	}

	// check if client is confidential
	if !req.Confidential() {
		oauth2.WriteError(w, oauth2.InvalidRequest(req.State, "Non confidential client"))
		return
	}

	// authenticate client
	client, found := clients[req.ClientID]
	if !found || !sameHash(client.secret, req.ClientSecret) {
		oauth2.WriteError(w, oauth2.InvalidClient(req.State, "Unknown client"))
		return
	}

	// triage grant type
	if req.GrantType.Password() {
		handleResourceOwnerPasswordCredentialsGrant(w, req)
	} else if req.GrantType.ClientCredentials() {
		handleClientCredentialsGrant(w, req)
	} else if req.GrantType.AuthorizationCode() {
		handleAuthorizationCodeGrant(w, req)
	} else if req.GrantType.RefreshToken() {
		handleRefreshTokenGrant(w, req)
	}
}

func handleResourceOwnerPasswordCredentialsGrant(w http.ResponseWriter, req *oauth2.TokenRequest) {
	// authenticate resource owner
	owner, found := users[req.Username]
	if !found || !sameHash(owner.secret, req.Password) {
		oauth2.WriteError(w, oauth2.AccessDenied(req.State, oauth2.NoDescription))
		return
	}

	// check scope
	if !allowedScope.Includes(req.Scope) {
		oauth2.WriteError(w, oauth2.InvalidScope(req.State, oauth2.NoDescription))
		return
	}

	// issue access and refresh token
	at, rt, res := createTokensAndResponse(req)

	// save tokens
	saveTokens(at, rt, req.Scope, req.ClientID, req.Username)

	// write response
	oauth2.WriteTokenResponse(w, res)
}

func handleClientCredentialsGrant(w http.ResponseWriter, req *oauth2.TokenRequest) {
	// check scope
	if !allowedScope.Includes(req.Scope) {
		oauth2.WriteError(w, oauth2.InvalidScope(req.State, oauth2.NoDescription))
		return
	}

	// issue access and refresh token
	at, rt, res := createTokensAndResponse(req)

	// save tokens
	saveTokens(at, rt, req.Scope, req.ClientID, "")

	// write response
	oauth2.WriteTokenResponse(w, res)
}

func handleAuthorizationCodeGrant(w http.ResponseWriter, req *oauth2.TokenRequest) {
	// parse authorization code
	authorizationCode, err := oauth2.ParseToken(secret, req.Code)
	if err != nil {
		oauth2.WriteError(w, oauth2.InvalidRequest(req.State, err.Error()))
		return
	}

	// get stored authorization code by signature
	storedAuthorizationCode, found := authorizationCodes[authorizationCode.SignatureString()]
	if !found {
		oauth2.WriteError(w, oauth2.InvalidGrant(req.State, "Unknown authorization code"))
		return
	}

	// validate ownership
	if storedAuthorizationCode.clientID != req.ClientID {
		oauth2.WriteError(w, oauth2.InvalidGrant(req.State, "Invalid authorization code ownership"))
		return
	}

	// validate scope and expiration
	if !storedAuthorizationCode.scope.Includes(req.Scope) || storedAuthorizationCode.expiresAt.Before(time.Now()) {
		oauth2.WriteError(w, oauth2.InvalidScope(req.State, "Scope exceeds the originally granted scope"))
		return
	}

	// validate redirect uri
	if storedAuthorizationCode.redirectURI != req.RedirectURI {
		oauth2.WriteError(w, oauth2.InvalidGrant(req.State, "Changed redirect uri"))
	}

	// issue new access and refresh token
	at, rt, res := createTokensAndResponse(req)

	// save tokens
	saveTokens(at, rt, req.Scope, req.ClientID, "")

	// delete used authorization code
	delete(authorizationCodes, authorizationCode.SignatureString())

	// write response
	oauth2.WriteTokenResponse(w, res)
}

func handleRefreshTokenGrant(w http.ResponseWriter, req *oauth2.TokenRequest) {
	// parse refresh token
	refreshToken, err := oauth2.ParseToken(secret, req.RefreshToken)
	if err != nil {
		oauth2.WriteError(w, oauth2.InvalidRequest(req.State, err.Error()))
		return
	}

	// get stored refresh token by signature
	storedRefreshToken, found := refreshTokens[refreshToken.SignatureString()]
	if !found {
		oauth2.WriteError(w, oauth2.InvalidGrant(req.State, "Unknown refresh token"))
		return
	}

	// validate ownership
	if storedRefreshToken.clientID != req.ClientID {
		oauth2.WriteError(w, oauth2.InvalidGrant(req.State, "Invalid refresh token ownership"))
		return
	}

	// validate scope and expiration
	if !storedRefreshToken.scope.Includes(req.Scope) || storedRefreshToken.expiresAt.Before(time.Now()) {
		oauth2.WriteError(w, oauth2.InvalidScope(req.State, "Scope exceeds the originally granted scope"))
		return
	}

	// issue new access and refresh token
	at, rt, res := createTokensAndResponse(req)

	// save tokens
	saveTokens(at, rt, req.Scope, req.ClientID, storedRefreshToken.username)

	// delete used refresh token
	delete(refreshTokens, refreshToken.SignatureString())

	// write response
	oauth2.WriteTokenResponse(w, res)
}

func createTokensAndResponse(req *oauth2.TokenRequest) (*oauth2.Token, *oauth2.Token, *oauth2.TokenResponse) {
	// generate new access token
	accessToken, err := oauth2.GenerateToken(secret, 32)
	if err != nil {
		panic(err)
	}

	// generate new refresh token
	refreshToken, err := oauth2.GenerateToken(secret, 32)
	if err != nil {
		panic(err)
	}

	// prepare response
	res := oauth2.NewBearerTokenResponse(accessToken.String(), int(tokenLifespan/time.Second))

	// set granted scope
	res.Scope = req.Scope

	// set refresh token
	res.RefreshToken = refreshToken.String()

	return accessToken, refreshToken, res
}

func saveTokens(accessToken, refreshToken *oauth2.Token, scope oauth2.Scope, clientID, username string) {
	// save access token
	accessTokens[accessToken.SignatureString()] = token{
		clientID:  clientID,
		username:  username,
		signature: accessToken.SignatureString(),
		expiresAt: time.Now().Add(tokenLifespan),
		scope:     scope,
	}

	// save refresh token
	refreshTokens[refreshToken.SignatureString()] = token{
		clientID:  clientID,
		username:  username,
		signature: refreshToken.SignatureString(),
		expiresAt: time.Now().Add(tokenLifespan),
		scope:     scope,
	}
}
<|endoftext|>


<commit_before>// cmd houses shared command logic between git-duet commands
//
// This package should not be depended on and will be not be able to be
// referenced when Go 1.5 rolls out support for internal packages to all
// repositories

package cmd

import (
	"fmt"
	"os"
	"os/exec"

	"git-duet"
)

type Command struct {
	Signoff bool
	Subcommand string
	Args []string
}

func New(subcommand string, args ...string) Command {
	cmd := Command{}
	cmd.Subcommand = subcommand

	if len(args) == 0 {
		cmd.Args = os.Args[1:]
	} else {
		cmd.Args = args
	}

	return cmd
}

func NewWithSignoff(subcommand string, args ...string) Command {
	cmd := New(subcommand, args...)
	cmd.Signoff = true

	return cmd
}

func (duetcmd Command) Execute() error {
	configuration, err := duet.NewConfiguration()
	if err != nil {
		return err
	}

	gitConfig, err := duet.GetAuthorConfig(configuration.Namespace)
	if err != nil {
		return err
	}

	author, err := gitConfig.GetAuthor()
	if err != nil {
		return err
	}

	if author == nil {
		return err
	}

	committer, err := gitConfig.GetCommitter()
	if err != nil {
		return err
	}

	if committer != nil && duetcmd.Signoff {
		duetcmd.Args = append([]string{"--signoff"}, duetcmd.Args...)
	} else {
		committer = author
	}

	cmd := exec.Command("git", append([]string{duetcmd.Subcommand}, duetcmd.Args...)...)
	cmd.Stdin = os.Stdin
	cmd.Stderr = os.Stderr
	cmd.Stdout = os.Stdout
	cmd.Env = append(os.Environ(),
		fmt.Sprintf("GIT_AUTHOR_NAME=%s", author.Name),
		fmt.Sprintf("GIT_AUTHOR_EMAIL=%s", author.Email),
		fmt.Sprintf("GIT_COMMITTER_NAME=%s", committer.Name),
		fmt.Sprintf("GIT_COMMITTER_EMAIL=%s", committer.Email),
	)
	err = cmd.Run()
	if err != nil {
		return err
	}

	return nil
}
<commit_msg>Add comment explaining when we use user args<commit_after>// cmd houses shared command logic between git-duet commands
//
// This package should not be depended on and will be not be able to be
// referenced when Go 1.5 rolls out support for internal packages to all
// repositories

package cmd

import (
	"fmt"
	"os"
	"os/exec"

	"git-duet"
)

type Command struct {
	Signoff bool
	Subcommand string
	Args []string
}

func New(subcommand string, args ...string) Command {
	cmd := Command{}
	cmd.Subcommand = subcommand

	// If we're explicitly providing args, use them.
	// Otherwise, we're forwarding from user input.
	if len(args) == 0 {
		cmd.Args = os.Args[1:]
	} else {
		cmd.Args = args
	}

	return cmd
}

func NewWithSignoff(subcommand string, args ...string) Command {
	cmd := New(subcommand, args...)
	cmd.Signoff = true

	return cmd
}

func (duetcmd Command) Execute() error {
	configuration, err := duet.NewConfiguration()
	if err != nil {
		return err
	}

	gitConfig, err := duet.GetAuthorConfig(configuration.Namespace)
	if err != nil {
		return err
	}

	author, err := gitConfig.GetAuthor()
	if err != nil {
		return err
	}

	if author == nil {
		return err
	}

	committer, err := gitConfig.GetCommitter()
	if err != nil {
		return err
	}

	if committer != nil && duetcmd.Signoff {
		duetcmd.Args = append([]string{"--signoff"}, duetcmd.Args...)
	} else {
		committer = author
	}

	cmd := exec.Command("git", append([]string{duetcmd.Subcommand}, duetcmd.Args...)...)
	cmd.Stdin = os.Stdin
	cmd.Stderr = os.Stderr
	cmd.Stdout = os.Stdout
	cmd.Env = append(os.Environ(),
		fmt.Sprintf("GIT_AUTHOR_NAME=%s", author.Name),
		fmt.Sprintf("GIT_AUTHOR_EMAIL=%s", author.Email),
		fmt.Sprintf("GIT_COMMITTER_NAME=%s", committer.Name),
		fmt.Sprintf("GIT_COMMITTER_EMAIL=%s", committer.Email),
	)
	err = cmd.Run()
	if err != nil {
		return err
	}

	return nil
}
<|endoftext|>


<commit_before>package tar

// special thanks to this medium article:
// https://medium.com/@skdomino/taring-untaring-files-in-go-6b07cf56bc07

import (
	"archive/tar"
	"fmt"
	"io"
	"os"
	"path/filepath"
	"strings"

	log "github.com/Sirupsen/logrus"

	. "github.com/drone/drone-cache-lib/archive"
)

type tarArchive struct{}

// NewTarArchive creates an Archive that uses the .tar file format.
func New() Archive {
	return &tarArchive{}
}

func (a *tarArchive) Pack(srcs []string, w io.Writer) error {
	tw := tar.NewWriter(w)
	defer tw.Close()

	// Loop through each source
	var fwErr error
	for _, s := range srcs {
		// ensure the src actually exists before trying to tar it
		if _, err := os.Stat(s); err != nil {
			return err
		}

		// walk path
		fwErr = filepath.Walk(s, func(path string, fi os.FileInfo, err error) error {
			if err != nil {
				return err
			}

			header, err := tar.FileInfoHeader(fi, fi.Name())
			if err != nil {
				return err
			}

			var link string
			if fi.Mode()&os.ModeSymlink == os.ModeSymlink {
				if link, err = os.Readlink(path); err != nil {
					return err
				}
				log.Infof("Symbolic link found at %s to %s", path, link)

				// Rewrite header for SymLink
				header, err = tar.FileInfoHeader(fi, link)
				if err != nil {
					return err
				}
			}

			header.Name = strings.TrimPrefix(filepath.ToSlash(path), "/")

			if err = tw.WriteHeader(header); err != nil {
				return err
			}

			if !fi.Mode().IsRegular() {
				log.Debugf("Directory found at %s", path)
				return nil
			}

			log.Debugf("File found at %s", path)

			file, err := os.Open(path)
			if err != nil {
				return err
			}

			defer file.Close()
			_, err = io.Copy(tw, file)
			return err
		})

		if fwErr != nil {
			return fwErr
		}
	}

	return fwErr
}

func (a *tarArchive) Unpack(dst string, r io.Reader) error {
	tr := tar.NewReader(r)

	for {
		header, err := tr.Next()

		switch {

		// if no more files are found return
		case err == io.EOF:
			return nil

		// return any other error
		case err != nil:
			return err

		// if the header is nil, just skip it (not sure how this happens)
		case header == nil:
			continue
		}

		// the target location where the dir/file should be created
		target := filepath.Join(dst, header.Name)

		// the following switch could also be done using fi.Mode(), not sure if there
		// a benefit of using one vs. the other.
		// fi := header.FileInfo()

		// check the file type
		switch header.Typeflag {

		// if its a symlink and it doesn't exist create it
		case tar.TypeSymlink:
			log.Debugf("Symlink found at %s", target)

			// Check if something already exists
			_, err := os.Stat(target)
			if err == nil {
				return fmt.Errorf("Failed to create symlink because file already exists at %s", target)
			}

			// Create the link
			log.Infof("Creating link %s to %s", target, header.Linkname)
			err = os.Symlink(header.Linkname, target)

			if err != nil {
				log.Infof("Failed creating link %s to %s", target, header.Linkname)
				return err
			}

		// if its a dir and it doesn't exist create it
		case tar.TypeDir:
			log.Debugf("Directory found at %s", target)
			if _, err := os.Stat(target); err != nil {
				if err := os.MkdirAll(target, 0755); err != nil {
					return err
				}
			}

		// if it's a file create it
		case tar.TypeReg:
			log.Debugf("File found at %s", target)
			f, err := os.OpenFile(target, os.O_CREATE|os.O_RDWR, os.FileMode(header.Mode))
			if err != nil {
				return err
			}

			// copy over contents
			_, err = io.Copy(f, tr)

			// Explicitly close otherwise too many files remain open
			f.Close()

			if err != nil {
				return err
			}
		}
	}
}
<commit_msg>Have logging regarding symlinks be debug level (#4)<commit_after>package tar

// special thanks to this medium article:
// https://medium.com/@skdomino/taring-untaring-files-in-go-6b07cf56bc07

import (
	"archive/tar"
	"fmt"
	"io"
	"os"
	"path/filepath"
	"strings"

	log "github.com/Sirupsen/logrus"

	. "github.com/drone/drone-cache-lib/archive"
)

type tarArchive struct{}

// NewTarArchive creates an Archive that uses the .tar file format.
func New() Archive {
	return &tarArchive{}
}

func (a *tarArchive) Pack(srcs []string, w io.Writer) error {
	tw := tar.NewWriter(w)
	defer tw.Close()

	// Loop through each source
	var fwErr error
	for _, s := range srcs {
		// ensure the src actually exists before trying to tar it
		if _, err := os.Stat(s); err != nil {
			return err
		}

		// walk path
		fwErr = filepath.Walk(s, func(path string, fi os.FileInfo, err error) error {
			if err != nil {
				return err
			}

			header, err := tar.FileInfoHeader(fi, fi.Name())
			if err != nil {
				return err
			}

			var link string
			if fi.Mode()&os.ModeSymlink == os.ModeSymlink {
				if link, err = os.Readlink(path); err != nil {
					return err
				}
				log.Debugf("Symbolic link found at %s to %s", path, link)

				// Rewrite header for SymLink
				header, err = tar.FileInfoHeader(fi, link)
				if err != nil {
					return err
				}
			}

			header.Name = strings.TrimPrefix(filepath.ToSlash(path), "/")

			if err = tw.WriteHeader(header); err != nil {
				return err
			}

			if !fi.Mode().IsRegular() {
				log.Debugf("Directory found at %s", path)
				return nil
			}

			log.Debugf("File found at %s", path)

			file, err := os.Open(path)
			if err != nil {
				return err
			}

			defer file.Close()
			_, err = io.Copy(tw, file)
			return err
		})

		if fwErr != nil {
			return fwErr
		}
	}

	return fwErr
}

func (a *tarArchive) Unpack(dst string, r io.Reader) error {
	tr := tar.NewReader(r)

	for {
		header, err := tr.Next()

		switch {

		// if no more files are found return
		case err == io.EOF:
			return nil

		// return any other error
		case err != nil:
			return err

		// if the header is nil, just skip it (not sure how this happens)
		case header == nil:
			continue
		}

		// the target location where the dir/file should be created
		target := filepath.Join(dst, header.Name)

		// the following switch could also be done using fi.Mode(), not sure if there
		// a benefit of using one vs. the other.
		// fi := header.FileInfo()

		// check the file type
		switch header.Typeflag {

		// if its a symlink and it doesn't exist create it
		case tar.TypeSymlink:
			log.Debugf("Symlink found at %s", target)

			// Check if something already exists
			_, err := os.Stat(target)
			if err == nil {
				return fmt.Errorf("Failed to create symlink because file already exists at %s", target)
			}

			// Create the link
			log.Debugf("Creating link %s to %s", target, header.Linkname)
			err = os.Symlink(header.Linkname, target)

			if err != nil {
				log.Infof("Failed creating link %s to %s", target, header.Linkname)
				return err
			}

		// if its a dir and it doesn't exist create it
		case tar.TypeDir:
			log.Debugf("Directory found at %s", target)
			if _, err := os.Stat(target); err != nil {
				if err := os.MkdirAll(target, 0755); err != nil {
					return err
				}
			}

		// if it's a file create it
		case tar.TypeReg:
			log.Debugf("File found at %s", target)
			f, err := os.OpenFile(target, os.O_CREATE|os.O_RDWR, os.FileMode(header.Mode))
			if err != nil {
				return err
			}

			// copy over contents
			_, err = io.Copy(f, tr)

			// Explicitly close otherwise too many files remain open
			f.Close()

			if err != nil {
				return err
			}
		}
	}
}
<|endoftext|>


<commit_before>package composition

import (
	"github.com/golang/mock/gomock"
	"github.com/stretchr/testify/assert"
	"io"
	"io/ioutil"
	"reflect"
	"strings"
	"testing"
)

func Test_CacheLoader_Found(t *testing.T) {
	ctrl := gomock.NewController(t)
	defer ctrl.Finish()
	a := assert.New(t)

	// given:
	fd := NewFetchDefinition("/foo")
	c := NewMemoryContent()
	c.url = "/foo"

	// and a cache returning the memory content by the hash
	cacheMocK := NewMockCache(ctrl)
	cacheMocK.EXPECT().Get(fd.Hash()).Return(c, true)

	// when: we load the object
	loader := NewCachingContentLoader(cacheMocK)

	// it is returned
	result, err := loader.Load(fd)
	a.NoError(err)
	a.Equal(c, result)
}

func Test_CacheLoader_NotFound(t *testing.T) {
	tests := []struct {
		url      string
		method   string
		cachable bool
	}{
		{"http://example.de", "GET", true},
		{"file:///some/file", "GET", true},
		{"http://example.de", "POST", false},
	}
	for _, test := range tests {
		ctrl := gomock.NewController(t)
		a := assert.New(t)

		// given:
		c := NewMemoryContent()
		c.url = test.url
		c.httpStatusCode = 200
		fd := NewFetchDefinition(c.url)
		fd.Method = test.method

		// and a cache returning nothing
		cacheMocK := NewMockCache(ctrl)
		cacheMocK.EXPECT().Get(gomock.Any()).Return(nil, false)
		if test.cachable {
			cacheMocK.EXPECT().Set(fd.Hash(), fd.URL, c.MemorySize(), c)
		}
		// and a loader delegating to
		loaderMock := NewMockContentLoader(ctrl)
		loaderMock.EXPECT().Load(gomock.Any()).Return(c, nil)

		// when: we load the object
		loader := NewCachingContentLoader(cacheMocK)
		if test.url == "file:///some/file" {
			loader.fileContentLoader = loaderMock
		} else {
			loader.httpContentLoader = loaderMock
		}

		// it is returned
		result, err := loader.Load(fd)
		a.NoError(err)
		a.Equal(c, result)
		ctrl.Finish()
	}
}

func Test_CacheLoader_NotFound_With_Stream(t *testing.T) {
	tests := []struct {
		url      string
		method   string
		reader   io.ReadCloser
		cachable bool
	}{
		{"http://example.de", "GET", ioutil.NopCloser(strings.NewReader("foobar")), true},
		{"file:///some/file", "GET", ioutil.NopCloser(strings.NewReader("foobar")), true},
	}
	for _, test := range tests {
		ctrl := gomock.NewController(t)
		a := assert.New(t)

		// given:
		c := NewMemoryContent()
		c.url = test.url
		c.httpStatusCode = 200
		c.reader = test.reader
		fd := NewFetchDefinition(c.url)
		fd.Method = test.method

		// and a cache returning nothing
		cacheMocK := NewMockCache(ctrl)
		cacheMocK.EXPECT().Get(gomock.Any()).Return(nil, false)
		if test.cachable {
			cacheMocK.EXPECT().Set(fd.Hash(), fd.URL, c.MemorySize(), CWMatcher{})
		}
		// and a loader delegating to
		loaderMock := NewMockContentLoader(ctrl)
		loaderMock.EXPECT().Load(gomock.Any()).Return(c, nil)

		// when: we load the object
		loader := NewCachingContentLoader(cacheMocK)
		if test.url == "file:///some/file" {
			loader.fileContentLoader = loaderMock
		} else {
			loader.httpContentLoader = loaderMock
		}

		// it is returned
		result, err := loader.Load(fd)
		resultbytes, err := ioutil.ReadAll(result.Reader())
		resultstring := string(resultbytes)
		a.NoError(err)
		a.Equal("foobar", resultstring)
		ctrl.Finish()
	}
}

type CWMatcher struct {
}

//Checks if a given object is a ContentWrapper
func (CWMatcher) Matches(cw interface{}) bool {
	if reflect.TypeOf(cw) == reflect.TypeOf(&ContentWrapper{}) {
		return true
	}
	return false
}
func (CWMatcher) String() string {
	return "is a ContentWrapper"
}
<commit_msg>Unittest for contentwrapper in cache:loader.go<commit_after>package composition

import (
	"github.com/golang/mock/gomock"
	"github.com/stretchr/testify/assert"
	"io"
	"io/ioutil"
	"reflect"
	"strings"
	"testing"
)

func Test_CacheLoader_Found(t *testing.T) {
	ctrl := gomock.NewController(t)
	defer ctrl.Finish()
	a := assert.New(t)

	// given:
	fd := NewFetchDefinition("/foo")
	c := NewMemoryContent()
	c.url = "/foo"

	// and a cache returning the memory content by the hash
	cacheMocK := NewMockCache(ctrl)
	cacheMocK.EXPECT().Get(fd.Hash()).Return(c, true)

	// when: we load the object
	loader := NewCachingContentLoader(cacheMocK)

	// it is returned
	result, err := loader.Load(fd)
	a.NoError(err)
	a.Equal(c, result)
}

func Test_CacheLoader_NotFound(t *testing.T) {
	tests := []struct {
		url      string
		method   string
		cachable bool
	}{
		{"http://example.de", "GET", true},
		{"file:///some/file", "GET", true},
		{"http://example.de", "POST", false},
	}
	for _, test := range tests {
		ctrl := gomock.NewController(t)
		a := assert.New(t)

		// given:
		c := NewMemoryContent()
		c.url = test.url
		c.httpStatusCode = 200
		fd := NewFetchDefinition(c.url)
		fd.Method = test.method

		// and a cache returning nothing
		cacheMocK := NewMockCache(ctrl)
		cacheMocK.EXPECT().Get(gomock.Any()).Return(nil, false)
		if test.cachable {
			cacheMocK.EXPECT().Set(fd.Hash(), fd.URL, c.MemorySize(), c)
		}
		// and a loader delegating to
		loaderMock := NewMockContentLoader(ctrl)
		loaderMock.EXPECT().Load(gomock.Any()).Return(c, nil)

		// when: we load the object
		loader := NewCachingContentLoader(cacheMocK)
		if test.url == "file:///some/file" {
			loader.fileContentLoader = loaderMock
		} else {
			loader.httpContentLoader = loaderMock
		}

		// it is returned
		result, err := loader.Load(fd)
		a.NoError(err)
		a.Equal(c, result)
		ctrl.Finish()
	}
}

func Test_CacheLoader_NotFound_With_Stream(t *testing.T) {
	tests := []struct {
		url      string
		method   string
		reader   io.ReadCloser
		cachable bool
	}{
		{"http://example.de", "GET", ioutil.NopCloser(strings.NewReader("foobar")), true},
		{"file:///some/file", "GET", ioutil.NopCloser(strings.NewReader("foobar")), true},
	}
	for _, test := range tests {
		ctrl := gomock.NewController(t)
		a := assert.New(t)

		// given:
		c := NewMemoryContent()
		c.url = test.url
		c.httpStatusCode = 200
		c.reader = test.reader
		fd := NewFetchDefinition(c.url)
		fd.Method = test.method

		// and a cache returning nothing
		cacheMocK := NewMockCache(ctrl)
		cacheMocK.EXPECT().Get(gomock.Any()).Return(nil, false)
		if test.cachable {
			cacheMocK.EXPECT().Set(fd.Hash(), fd.URL, c.MemorySize(), CWMatcher{})
		}
		// and a loader delegating to
		loaderMock := NewMockContentLoader(ctrl)
		loaderMock.EXPECT().Load(gomock.Any()).Return(c, nil)

		// when: we load the object
		loader := NewCachingContentLoader(cacheMocK)
		if test.url == "file:///some/file" {
			loader.fileContentLoader = loaderMock
		} else {
			loader.httpContentLoader = loaderMock
		}

		// it is returned
		result, err := loader.Load(fd)
		resultbytes, err := ioutil.ReadAll(result.Reader())
		resultstring := string(resultbytes)
		a.NoError(err)
		a.Equal("foobar", resultstring)
		ctrl.Finish()
	}
}

func Test_Content_Wrapper_Reader(t *testing.T) {
	//given
	toTest := &ContentWrapper{streamBytes: []byte("foobar")}

	//when
	result, err := ioutil.ReadAll(toTest.Reader())
	resultStr := string(result)

	//then
	assert.NoError(t, err)
	assert.Equal(t, "foobar", resultStr)

}

type CWMatcher struct {
}

//Checks if a given object is a ContentWrapper
func (CWMatcher) Matches(cw interface{}) bool {
	if reflect.TypeOf(cw) == reflect.TypeOf(&ContentWrapper{}) {
		return true
	}
	return false
}
func (CWMatcher) String() string {
	return "is a ContentWrapper"
}
<|endoftext|>


<commit_before>package logredis

import (
	"encoding/json"
	"fmt"
	"os"
	"strings"
	"time"

	"github.com/Sirupsen/logrus"
	"github.com/garyburd/redigo/redis"
)

// RedisHook to sends logs to Redis server
type RedisHook struct {
	RedisPool      *redis.Pool
	RedisHost      string
	RedisKey       string
	LogstashFormat string
	AppName		string
	RedisPort      int
}

// LogstashMessageV0 represents v0 format
type LogstashMessageV0 struct {
	Type       string `json:"@type,omitempty"`
	Timestamp  string `json:"@timestamp"`
	Sourcehost string `json:"@source_host"`
	Message    string `json:"@message"`
//	Level      string `json:"@level"`
	Fields     struct {
		Application    string `json:application`
		File      string `json:"file"`
		Level     string `json:"level"`
	} `json:"@fields"`
}

// LogstashMessageV1 represents v1 format
type LogstashMessageV1 struct {
	Type       string `json:"@type,omitempty"`
	Timestamp  string `json:"@timestamp"`
	Sourcehost string `json:"host"`
	Message    string `json:"message"`
	Application string `json:"application"`
	File      string `json:"file"`
	Level     string `json:"level"`
}

// NewHook creates a hook to be added to an instance of logger
func NewHook(host string, port int, key string, format string, appname string) (*RedisHook, error) {
	pool := newRedisConnectionPool(host, port)

	// test if connection with REDIS can be established
	conn := pool.Get()
	defer conn.Close()

	// check connection
	_, err := conn.Do("PING")
	if err != nil {
		return nil, fmt.Errorf("unable to connect to REDIS: %s", err)
	}

	// by default, use V0 format
	if strings.ToLower(format) != "v0" && strings.ToLower(format) != "v1" {
		format = "v0"
	}

	return &RedisHook{
		RedisHost:      host,
		RedisPool:      pool,
		RedisKey:       key,
		LogstashFormat: format,
		AppName:        appname,
	}, nil
}

// Fire is called when a log event is fired.
func (hook *RedisHook) Fire(entry *logrus.Entry) error {
	var msg interface{}

	switch hook.LogstashFormat {
	case "v0":
		msg = createV0Message(entry)
	case "v1":
		msg = createV1Message(entry)
	}

	js, err := json.Marshal(msg)
	if err != nil {
		return fmt.Errorf("error creating message for REDIS: %s", err)
	}

	conn := hook.RedisPool.Get()
	defer conn.Close()

	_, err = conn.Do("RPUSH", hook.RedisKey, js)
	if err != nil {
		return fmt.Errorf("error sending message to REDIS: %s", err)
	}
	return nil
}

// Levels returns the available logging levels.
func (hook *RedisHook) Levels() []logrus.Level {
	return []logrus.Level{
		logrus.DebugLevel,
		logrus.InfoLevel,
		logrus.WarnLevel,
		logrus.ErrorLevel,
		logrus.FatalLevel,
		logrus.PanicLevel,
	}
}

func createV0Message(entry *logrus.Entry) LogstashMessageV0 {
	m := LogstashMessageV0{}
	m.Timestamp = entry.Time.UTC().Format(time.RFC3339Nano)
	m.Sourcehost = reportHostname()
	m.Message = entry.Message
	m.Fields.Level = entry.Level.String()
	m.Fields.Application = entry.AppName
	return m
}

func createV1Message(entry *logrus.Entry) LogstashMessageV1 {
	m := LogstashMessageV1{}
	m.Timestamp = entry.Time.UTC().Format(time.RFC3339Nano)
	m.Sourcehost = reportHostname()
	m.Message = entry.Message
	m.Level = entry.Level.String()
	return m
}

func newRedisConnectionPool(server string, port int) *redis.Pool {
	hostPort := fmt.Sprintf("%s:%d", server, port)
	return &redis.Pool{
		MaxIdle:     3,
		IdleTimeout: 240 * time.Second,
		Dial: func() (redis.Conn, error) {
			c, err := redis.Dial("tcp", hostPort)
			if err != nil {
				return nil, err
			}

			// if password != "" {
			// 	if _, err := c.Do("AUTH", password); err != nil {
			// 		c.Close()
			// 		return nil, err
			// 	}
			// }

			return c, err
		},
		TestOnBorrow: func(c redis.Conn, t time.Time) error {
			_, err := c.Do("PING")
			return err
		},
	}
}

func reportHostname() string {
	h, err := os.Hostname()
	if err != nil {
		return "unknown"
	}
	return h
}
<commit_msg>commit<commit_after>package logredis

import (
	"encoding/json"
	"fmt"
	"os"
	"strings"
	"time"

	"github.com/Sirupsen/logrus"
	"github.com/garyburd/redigo/redis"
)

// RedisHook to sends logs to Redis server
type RedisHook struct {
	RedisPool      *redis.Pool
	RedisHost      string
	RedisKey       string
	LogstashFormat string
	AppName        string
	RedisPort      int
}

// LogstashMessageV0 represents v0 format
type LogstashMessageV0 struct {
	Type       string `json:"@type,omitempty"`
	Timestamp  string `json:"@timestamp"`
	Sourcehost string `json:"@source_host"`
	Message    string `json:"@message"`
	//	Level      string `json:"@level"`
	Fields struct {
		Application string `json:"application"`
		File        string `json:"file"`
		Level       string `json:"level"`
	} `json:"@fields"`
}

// LogstashMessageV1 represents v1 format
type LogstashMessageV1 struct {
	Type        string `json:"@type,omitempty"`
	Timestamp   string `json:"@timestamp"`
	Sourcehost  string `json:"host"`
	Message     string `json:"message"`
	Application string `json:"application"`
	File        string `json:"file"`
	Level       string `json:"level"`
}

// NewHook creates a hook to be added to an instance of logger
func NewHook(host string, port int, key string, format string, appname string) (*RedisHook, error) {
	pool := newRedisConnectionPool(host, port)

	// test if connection with REDIS can be established
	conn := pool.Get()
	defer conn.Close()

	// check connection
	_, err := conn.Do("PING")
	if err != nil {
		return nil, fmt.Errorf("unable to connect to REDIS: %s", err)
	}

	// by default, use V0 format
	if strings.ToLower(format) != "v0" && strings.ToLower(format) != "v1" {
		format = "v0"
	}

	return &RedisHook{
		RedisHost:      host,
		RedisPool:      pool,
		RedisKey:       key,
		LogstashFormat: format,
		AppName:        appname,
	}, nil
}

// Fire is called when a log event is fired.
func (hook *RedisHook) Fire(entry *logrus.Entry) error {
	var msg interface{}

	switch hook.LogstashFormat {
	case "v0":
		msg = createV0Message(entry, hook.AppName)
	case "v1":
		msg = createV1Message(entry, hook.AppName)
	}

	js, err := json.Marshal(msg)
	if err != nil {
		return fmt.Errorf("error creating message for REDIS: %s", err)
	}

	conn := hook.RedisPool.Get()
	defer conn.Close()

	_, err = conn.Do("RPUSH", hook.RedisKey, js)
	if err != nil {
		return fmt.Errorf("error sending message to REDIS: %s", err)
	}
	return nil
}

// Levels returns the available logging levels.
func (hook *RedisHook) Levels() []logrus.Level {
	return []logrus.Level{
		logrus.DebugLevel,
		logrus.InfoLevel,
		logrus.WarnLevel,
		logrus.ErrorLevel,
		logrus.FatalLevel,
		logrus.PanicLevel,
	}
}

func createV0Message(entry *logrus.Entry, appName string) LogstashMessageV0 {
	m := LogstashMessageV0{}
	m.Timestamp = entry.Time.UTC().Format(time.RFC3339Nano)
	m.Sourcehost = reportHostname()
	m.Message = entry.Message
	m.Fields.Level = entry.Level.String()
	m.Fields.Application = appName
	return m
}

func createV1Message(entry *logrus.Entry, appName string) LogstashMessageV1 {
	m := LogstashMessageV1{}
	m.Timestamp = entry.Time.UTC().Format(time.RFC3339Nano)
	m.Sourcehost = reportHostname()
	m.Message = entry.Message
	m.Level = entry.Level.String()
	m.Application = appName
	return m
}

func newRedisConnectionPool(server string, port int) *redis.Pool {
	hostPort := fmt.Sprintf("%s:%d", server, port)
	return &redis.Pool{
		MaxIdle:     3,
		IdleTimeout: 240 * time.Second,
		Dial: func() (redis.Conn, error) {
			c, err := redis.Dial("tcp", hostPort)
			if err != nil {
				return nil, err
			}

			// if password != "" {
			// 	if _, err := c.Do("AUTH", password); err != nil {
			// 		c.Close()
			// 		return nil, err
			// 	}
			// }

			return c, err
		},
		TestOnBorrow: func(c redis.Conn, t time.Time) error {
			_, err := c.Do("PING")
			return err
		},
	}
}

func reportHostname() string {
	h, err := os.Hostname()
	if err != nil {
		return "unknown"
	}
	return h
}
<|endoftext|>


<commit_before>// Copyright 2020 Google LLC
//
// 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
//
//     https://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 bin_test

import (
	"os"
	"os/exec"

	"github.com/phst/runfiles"
)

func Example() {
	bin, err := runfiles.Path("phst_rules_elisp/examples/bin")
	if err != nil {
		panic(err)
	}
	env, err := runfiles.Env()
	if err != nil {
		panic(err)
	}
	// You can run the programs produced by elisp_binary rules like any
	// other binary.
	cmd := exec.Command(bin, "human")
	cmd.Stdout = os.Stdout
	// Note: Emacs writes to stderr, but the example runner only captures
	// stdout.
	cmd.Stderr = os.Stdout
	// The working directory doesn’t matter.  Binaries still find their
	// runfiles.  Be sure to pass environment variables to find runfiles.
	// We also set GCOV_PREFIX (see
	// https://gcc.gnu.org/onlinedocs/gcc/Cross-profiling.html) to a
	// directory that’s hopefully writable, to avoid logspam when running
	// with “bazel coverage”.
	cmd.Dir = "/"
	cmd.Env = append(env, "PATH="+os.Getenv("PATH"), "GCOV_PREFIX="+os.TempDir())
	if err := cmd.Run(); err != nil {
		panic(err)
	}
	// Output:
	// hi from bin, ("human")
	// hi from lib-2
	// hi from lib-4
	// hi from lib-1
	// hi from data dependency
}
<commit_msg>Avoid test flakiness depending on sandbox mtimes.<commit_after>// Copyright 2020 Google LLC
//
// 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
//
//     https://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 bin_test

import (
	"bufio"
	"fmt"
	"io"
	"os"
	"os/exec"
	"regexp"

	"github.com/phst/runfiles"
)

func Example() {
	bin, err := runfiles.Path("phst_rules_elisp/examples/bin")
	if err != nil {
		panic(err)
	}
	env, err := runfiles.Env()
	if err != nil {
		panic(err)
	}
	// You can run the programs produced by elisp_binary rules like any
	// other binary.
	cmd := exec.Command(bin, "human")
	cmd.Stdout = os.Stdout
	// Note: Emacs writes to stderr, but the example runner only captures
	// stdout.  We filter out some irrelevant messages that can cause
	// spurious failures.
	r, w := io.Pipe()
	defer r.Close()
	defer w.Close()
	go filter(r, os.Stdout)
	cmd.Stderr = w
	// The working directory doesn’t matter.  Binaries still find their
	// runfiles.  Be sure to pass environment variables to find runfiles.
	// We also set GCOV_PREFIX (see
	// https://gcc.gnu.org/onlinedocs/gcc/Cross-profiling.html) to a
	// directory that’s hopefully writable, to avoid logspam when running
	// with “bazel coverage”.
	cmd.Dir = "/"
	cmd.Env = append(env, "PATH="+os.Getenv("PATH"), "GCOV_PREFIX="+os.TempDir())
	if err := cmd.Run(); err != nil {
		panic(err)
	}
	// Output:
	// hi from bin, ("human")
	// hi from lib-2
	// hi from lib-4
	// hi from lib-1
	// hi from data dependency
}

func filter(r io.Reader, w io.Writer) {
	s := bufio.NewScanner(r)
	for s.Scan() {
		line := s.Text()
		if !irrelevant.MatchString(line) {
			fmt.Fprintln(w, line)
		}
	}
}

// This message can happen depending on the mtime of files in the Bazel
// sandbox.  It shouldn’t influence the test outcome.
var irrelevant = regexp.MustCompile(`^Source file .+ newer than byte-compiled file; using older file$`)
<|endoftext|>


<commit_before>package main

import (
	"context"
	"flag"
	"fmt"
	"log"
	"os"
	"sync"
	"time"

	dariadb "github.com/lysevi/dariadb/go"
)

type query struct {
	queryJSON []byte
	paramname string
	v         float64
}

var asyncWriterChanel chan query
var networkMutex sync.Mutex

var server string
var disableReader bool
var paramsFrom int
var paramsTo int

var rawParams []string
var allParams []string
var db *dariadb.Dariadb

func init() {
	flag.StringVar(&server, "host", "http://localhost:2002", "host with dariadb")
	flag.IntVar(&paramsFrom, "from", 0, "first param number")
	flag.IntVar(&paramsTo, "to", 10, "last param number")
	flag.BoolVar(&disableReader, "disableReader", false, "enable reader")
	flag.Parse()
}

var values map[string]interface{}

func printInfoValues() {
	curtime := time.Now()
	from := time.Date(curtime.Year(), curtime.Month(), curtime.Day(), 0, 0, 0, 0, time.UTC)
	to := time.Date(curtime.Year(), curtime.Month(), curtime.Day(), 23, 59, 59, 999999, time.UTC)

	res, err := db.Interval(allParams, dariadb.MakeTimestampFrom(from), dariadb.MakeTimestampFrom(to), dariadb.Flag(0))

	if err == nil {
		for k, v := range res {
			log.Printf("%v => %v", k, len(v))
		}
	} else {
		log.Println(err)
	}
}

func initScheme() {
	hostname, err := os.Hostname()
	if err != nil {
		hostname = "localhost"
	}
	paramPrefix := hostname + ".param"
	paramsSuffix := [...]string{".raw", ".average.minute", ".median.minute", ".average.halfhour", ".median.halfhour", ".average.hour", ".average.day"}

	paramsCount := paramsTo - paramsFrom
	rawParams = make([]string, 0, paramsCount)
	allParams = make([]string, 0, paramsCount*len(paramsSuffix))

	for i := paramsFrom; i < paramsTo; i++ {
		params := make([]string, len(paramsSuffix))
		j := 0
		for _, suff := range paramsSuffix {
			params[j] = fmt.Sprintf("%s_%d%s", paramPrefix, i, suff)
			allParams = append(allParams, params[j])
			j++
		}
		rawParams = append(rawParams, params[0])
		db.AddToScheme(params)
	}
}

func writeValues(ctx context.Context, paramname string) {
	var v = float64(0.0)

	for {
		select {
		case <-ctx.Done():
			break
		case <-time.After(100 * time.Millisecond):
			err := db.Append(paramname, dariadb.MakeTimestamp(), dariadb.Flag(0), dariadb.Value(v))
			if err != nil {
				log.Printf("append error: %v", err)
			} else {
				log.Printf("%v = %v", paramname, v)
			}
			v = (v + 0.1)
			if v > 1000 {
				v = 0.0
			}

		}
	}
}

func main() {
	db = dariadb.New(server)

	initScheme()

	printInfoValues()

	ctx := context.Background()
	asyncWriterChanel = make(chan query)
	// var cancel context.CancelFunc
	//
	// ctx, cancel = context.WithCancel(context.Background())

	scheme, _ := db.Scheme()

	log.Println(scheme)
	for _, v := range rawParams {
		go writeValues(ctx, v)
	}

	for {
		if !disableReader {
			printInfoValues()
		}
		time.Sleep(time.Second)
	}
}
<commit_msg>example/go: readonly mode<commit_after>package main

import (
	"context"
	"flag"
	"fmt"
	"log"
	"os"
	"sync"
	"time"

	dariadb "github.com/lysevi/dariadb/go"
)

type query struct {
	queryJSON []byte
	paramname string
	v         float64
}

var asyncWriterChanel chan query
var networkMutex sync.Mutex

var server string
var readOnly bool
var paramsFrom int
var paramsTo int

var rawParams []string
var allParams []string
var db *dariadb.Dariadb

func init() {
	flag.StringVar(&server, "host", "http://localhost:2002", "host with dariadb")
	flag.IntVar(&paramsFrom, "from", 0, "first param number")
	flag.IntVar(&paramsTo, "to", 10, "last param number")
	flag.BoolVar(&readOnly, "readOnly", false, "readOnly")
	flag.Parse()
}

var values map[string]interface{}

func printInfoValues() {
	curtime := time.Now()
	from := time.Date(curtime.Year(), curtime.Month(), curtime.Day(), 0, 0, 0, 0, time.UTC)
	to := time.Date(curtime.Year(), curtime.Month(), curtime.Day(), 23, 59, 59, 999999, time.UTC)

	res, err := db.Interval(allParams, dariadb.MakeTimestampFrom(from), dariadb.MakeTimestampFrom(to), dariadb.Flag(0))

	if err == nil {
		for k, v := range res {
			log.Printf("%v => %v", k, len(v))
		}
	} else {
		log.Println(err)
	}
}

func initScheme() {
	hostname, err := os.Hostname()
	if err != nil {
		hostname = "localhost"
	}
	paramPrefix := hostname + ".param"
	paramsSuffix := [...]string{".raw", ".average.minute", ".median.minute", ".average.halfhour", ".median.halfhour", ".average.hour", ".average.day"}

	paramsCount := paramsTo - paramsFrom
	rawParams = make([]string, 0, paramsCount)
	allParams = make([]string, 0, paramsCount*len(paramsSuffix))

	for i := paramsFrom; i < paramsTo; i++ {
		params := make([]string, len(paramsSuffix))
		j := 0
		for _, suff := range paramsSuffix {
			params[j] = fmt.Sprintf("%s_%d%s", paramPrefix, i, suff)
			allParams = append(allParams, params[j])
			j++
		}
		rawParams = append(rawParams, params[0])
		db.AddToScheme(params)
	}
}

func writeValues(ctx context.Context, paramname string) {
	var v = float64(0.0)

	for {
		select {
		case <-ctx.Done():
			break
		case <-time.After(100 * time.Millisecond):
			err := db.Append(paramname, dariadb.MakeTimestamp(), dariadb.Flag(0), dariadb.Value(v))
			if err != nil {
				log.Printf("append error: %v", err)
			} else {
				log.Printf("%v = %v", paramname, v)
			}
			v = (v + 0.1)
			if v > 1000 {
				v = 0.0
			}

		}
	}
}

func main() {
	db = dariadb.New(server)

	initScheme()

	if !readOnly {

		ctx := context.Background()
		asyncWriterChanel = make(chan query)
		// var cancel context.CancelFunc
		//
		// ctx, cancel = context.WithCancel(context.Background())

		scheme, _ := db.Scheme()

		log.Println(scheme)
		for _, v := range rawParams {
			go writeValues(ctx, v)
		}
	}

	for {
		if readOnly {
			printInfoValues()
		}
		log.Println("***********************")
		time.Sleep(1000 * time.Millisecond)
	}
}
<|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.

// +build example jsgo

package main

import (
	"log"
	"math"
	"strings"

	"github.com/hajimehoshi/ebiten"
	"github.com/hajimehoshi/ebiten/audio"
	"github.com/hajimehoshi/ebiten/ebitenutil"
)

const (
	screenWidth  = 320
	screenHeight = 240
	sampleRate   = 44100
)

var audioContext *audio.Context

func init() {
	var err error
	audioContext, err = audio.NewContext(sampleRate)
	if err != nil {
		log.Fatal(err)
	}
}

const (
	freqA  = 440.0
	freqAS = 466.2
	freqB  = 493.9
	freqC  = 523.3
	freqCS = 554.4
	freqD  = 587.3
	freqDS = 622.3
	freqE  = 659.3
	freqF  = 698.5
	freqFS = 740.0
	freqG  = 784.0
	freqGS = 830.6
)

// Twinkle, Twinkle, Little Star
var score = strings.Replace(
	`CCGGAAGR FFEEDDCR GGFFEEDR GGFFEEDR CCGGAAGR FFEEDDCR`,
	" ", "", -1)

// square fills out with square wave values with the specified volume, frequency and sequence.
func square(out []int16, volume float64, freq float64, sequence float64) {
	if freq == 0 {
		for i := 0; i < len(out); i++ {
			out[i] = 0
		}
		return
	}
	length := int(float64(sampleRate) / freq)
	if length == 0 {
		panic("invalid freq")
	}
	for i := 0; i < len(out); i++ {
		a := int16(volume * math.MaxInt16)
		if i%length < int(float64(length)*sequence) {
			a = -a
		}
		out[i] = a
	}
}

// toBytes returns the 2ch little endian 16bit byte sequence with the given left/right sequence.
func toBytes(l, r []int16) []byte {
	if len(l) != len(r) {
		panic("len(l) must equal to len(r)")
	}
	b := make([]byte, len(l)*4)
	for i := range l {
		b[4*i] = byte(l[i])
		b[4*i+1] = byte(l[i] >> 8)
		b[4*i+2] = byte(r[i])
		b[4*i+3] = byte(r[i] >> 8)
	}
	return b
}

// playNote plays the note at scoreIndex of the score.
func playNote(scoreIndex int) rune {
	note := score[scoreIndex]

	// If the note is 'rest', play nothing.
	if note == 'R' {
		return rune(note)
	}

	freqs := []float64{freqC, freqD, freqE, freqF, freqG, freqA * 2, freqB * 2}
	freq := 0.0
	switch {
	case 'A' <= note && note <= 'B':
		freq = freqs[int(note)+len(freqs)-int('C')]
	case 'C' <= note && note <= 'G':
		freq = freqs[note-'C']
	default:
		panic("note out of range")
	}

	const vol = 1.0 / 16.0
	size := 30 * sampleRate / ebiten.MaxTPS()
	l := make([]int16, size)
	r := make([]int16, size)
	square(l, vol, freq, 0.25)
	square(r, vol, freq, 0.25)

	p, _ := audio.NewPlayerFromBytes(audioContext, toBytes(l, r))
	p.Play()

	return rune(note)
}

var (
	scoreIndex  = 0
	frames      = 0
	currentNote rune
)

func update(screen *ebiten.Image) error {
	// Play notes for each half second.
	if frames%30 == 0 && audioContext.IsReady() {
		currentNote = playNote(scoreIndex)
		scoreIndex++
		scoreIndex %= len(score)
	}
	frames++

	if ebiten.IsDrawingSkipped() {
		return nil
	}

	msg := "Note: "
	if currentNote == 'R' || currentNote == 0 {
		msg += "-"
	} else {
		msg += string(currentNote)
	}
	if !audioContext.IsReady() {
		msg += "\n\n(If the audio doesn't start,\n click the screen or press keys)"
	}
	ebitenutil.DebugPrint(screen, msg)
	return nil
}

func main() {
	if err := ebiten.Run(update, screenWidth, screenHeight, 2, "PCM (Ebiten Demo)"); err != nil {
		log.Fatal(err)
	}
}
<commit_msg>examples/pcm: Bug fix: audio.Player refs should be held not to be GCed<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.

// +build example jsgo

package main

import (
	"log"
	"math"
	"strings"

	"github.com/hajimehoshi/ebiten"
	"github.com/hajimehoshi/ebiten/audio"
	"github.com/hajimehoshi/ebiten/ebitenutil"
)

const (
	screenWidth  = 320
	screenHeight = 240
	sampleRate   = 44100
)

var audioContext *audio.Context

func init() {
	var err error
	audioContext, err = audio.NewContext(sampleRate)
	if err != nil {
		log.Fatal(err)
	}
}

const (
	freqA  = 440.0
	freqAS = 466.2
	freqB  = 493.9
	freqC  = 523.3
	freqCS = 554.4
	freqD  = 587.3
	freqDS = 622.3
	freqE  = 659.3
	freqF  = 698.5
	freqFS = 740.0
	freqG  = 784.0
	freqGS = 830.6
)

// Twinkle, Twinkle, Little Star
var score = strings.Replace(
	`CCGGAAGR FFEEDDCR GGFFEEDR GGFFEEDR CCGGAAGR FFEEDDCR`,
	" ", "", -1)

// square fills out with square wave values with the specified volume, frequency and sequence.
func square(out []int16, volume float64, freq float64, sequence float64) {
	if freq == 0 {
		for i := 0; i < len(out); i++ {
			out[i] = 0
		}
		return
	}
	length := int(float64(sampleRate) / freq)
	if length == 0 {
		panic("invalid freq")
	}
	for i := 0; i < len(out); i++ {
		a := int16(volume * math.MaxInt16)
		if i%length < int(float64(length)*sequence) {
			a = -a
		}
		out[i] = a
	}
}

// players holds audio.Player objects not to be GCed.
var players = map[*audio.Player]struct{}{}

// toBytes returns the 2ch little endian 16bit byte sequence with the given left/right sequence.
func toBytes(l, r []int16) []byte {
	if len(l) != len(r) {
		panic("len(l) must equal to len(r)")
	}
	b := make([]byte, len(l)*4)
	for i := range l {
		b[4*i] = byte(l[i])
		b[4*i+1] = byte(l[i] >> 8)
		b[4*i+2] = byte(r[i])
		b[4*i+3] = byte(r[i] >> 8)
	}
	return b
}

// playNote plays the note at scoreIndex of the score.
func playNote(scoreIndex int) rune {
	note := score[scoreIndex]

	// If the note is 'rest', play nothing.
	if note == 'R' {
		return rune(note)
	}

	freqs := []float64{freqC, freqD, freqE, freqF, freqG, freqA * 2, freqB * 2}
	freq := 0.0
	switch {
	case 'A' <= note && note <= 'B':
		freq = freqs[int(note)+len(freqs)-int('C')]
	case 'C' <= note && note <= 'G':
		freq = freqs[note-'C']
	default:
		panic("note out of range")
	}

	const vol = 1.0 / 16.0
	size := 30 * sampleRate / ebiten.MaxTPS()
	l := make([]int16, size)
	r := make([]int16, size)
	square(l, vol, freq, 0.25)
	square(r, vol, freq, 0.25)

	p, _ := audio.NewPlayerFromBytes(audioContext, toBytes(l, r))
	p.Play()
	players[p] = struct{}{}

	return rune(note)
}

var (
	scoreIndex  = 0
	frames      = 0
	currentNote rune
)

func update(screen *ebiten.Image) error {
	// Play notes for each half second.
	if frames%30 == 0 && audioContext.IsReady() {
		currentNote = playNote(scoreIndex)
		scoreIndex++
		scoreIndex %= len(score)
	}
	frames++

	// Close players that alrady finish.
	closed := []*audio.Player{}
	for p := range players {
		if p.IsPlaying() {
			continue
		}
		closed = append(closed, p)
	}
	for _, p := range closed {
		delete(players, p)
	}

	if ebiten.IsDrawingSkipped() {
		return nil
	}

	msg := "Note: "
	if currentNote == 'R' || currentNote == 0 {
		msg += "-"
	} else {
		msg += string(currentNote)
	}
	if !audioContext.IsReady() {
		msg += "\n\n(If the audio doesn't start,\n click the screen or press keys)"
	}
	ebitenutil.DebugPrint(screen, msg)
	return nil
}

func main() {
	if err := ebiten.Run(update, screenWidth, screenHeight, 2, "PCM (Ebiten Demo)"); err != nil {
		log.Fatal(err)
	}
}
<|endoftext|>


<commit_before>/*
 *
 * pig is a very simple game involving dice rolls.
 *
 */
package pig

import (
	"github.com/jkomoros/boardgame"
)

//go:generate autoreader

type gameDelegate struct {
	boardgame.DefaultGameDelegate
}

func (g *gameDelegate) Name() string {
	return "pig"
}

func (g *gameDelegate) DisplayName() string {
	return "Pig"
}

func (g *gameDelegate) EmptyGameState() boardgame.MutableSubState {
	dice := g.Manager().Chest().Deck(diceDeckName)

	if dice == nil {
		return nil
	}

	return &gameState{
		CurrentPlayer: 0,
		Die:           boardgame.NewSizedStack(dice, 1),
	}
}

func (g *gameDelegate) EmptyPlayerState(index boardgame.PlayerIndex) boardgame.MutablePlayerState {
	return &playerState{
		playerIndex: index,
		TotalScore:  0,
		RoundScore:  0,
	}
}

func NewManager(storage boardgame.StorageManager) *boardgame.GameManager {
	chest := boardgame.NewComponentChest()

	dice := boardgame.NewDeck()

	dice.AddComponent(DefaultDie())

	chest.AddDeck(diceDeckName, dice)

	manager := boardgame.NewGameManager(&gameDelegate{}, chest, storage)

	if manager == nil {
		panic("No manager returned")
	}

	//TODO: configure moves here

	manager.SetUp()

	return manager
}
<commit_msg>Return DynamicComponentValues for the dice stack. Part of #372.<commit_after>/*
 *
 * pig is a very simple game involving dice rolls.
 *
 */
package pig

import (
	"github.com/jkomoros/boardgame"
)

//go:generate autoreader

type gameDelegate struct {
	boardgame.DefaultGameDelegate
}

func (g *gameDelegate) Name() string {
	return "pig"
}

func (g *gameDelegate) DisplayName() string {
	return "Pig"
}

func (g *gameDelegate) EmptyGameState() boardgame.MutableSubState {
	dice := g.Manager().Chest().Deck(diceDeckName)

	if dice == nil {
		return nil
	}

	return &gameState{
		CurrentPlayer: 0,
		Die:           boardgame.NewSizedStack(dice, 1),
	}
}

func (g *gameDelegate) EmptyPlayerState(index boardgame.PlayerIndex) boardgame.MutablePlayerState {
	return &playerState{
		playerIndex: index,
		TotalScore:  0,
		RoundScore:  0,
	}
}

func (g *gameDelegate) EmptyDynamicComponentValues(deck *boardgame.Deck) boardgame.MutableSubState {
	if deck.Name() == diceDeckName {
		return &dieDynamicValue{
			Value: 1,
		}
	}
	return nil
}

func NewManager(storage boardgame.StorageManager) *boardgame.GameManager {
	chest := boardgame.NewComponentChest()

	dice := boardgame.NewDeck()

	dice.AddComponent(DefaultDie())

	chest.AddDeck(diceDeckName, dice)

	manager := boardgame.NewGameManager(&gameDelegate{}, chest, storage)

	if manager == nil {
		panic("No manager returned")
	}

	//TODO: configure moves here

	manager.SetUp()

	return manager
}
<|endoftext|>


<commit_before>// Copyright 2017 Jeff Foley. All rights reserved.
// Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file.

package sources

import (
	"encoding/json"
	"fmt"
	"strings"
	"time"

	"github.com/OWASP/Amass/amass/core"
	"github.com/OWASP/Amass/amass/utils"
	"github.com/dghubble/go-twitter/twitter"
	"golang.org/x/oauth2"
)

// Twitter is the Service that handles access to the Twitter data source.
type Twitter struct {
	core.BaseService

	API        *core.APIKey
	SourceType string
	RateLimit  time.Duration
	client     *twitter.Client
}

// NewTwitter returns he object initialized, but not yet started.
func NewTwitter(config *core.Config, bus *core.EventBus) *Twitter {
	t := &Twitter{
		SourceType: core.API,
		RateLimit:  3 * time.Second,
	}

	t.BaseService = *core.NewBaseService(t, "Twitter", config, bus)
	return t
}

// OnStart implements the Service interface
func (t *Twitter) OnStart() error {
	t.BaseService.OnStart()

	t.API = t.Config().GetAPIKey(t.String())
	if t.API == nil || t.API.Key == "" || t.API.Secret == "" {
		t.Config().Log.Printf("%s: API key data was not provided", t.String())
	}
	if t.API != nil && t.API.Key != "" && t.API.Secret != "" {
		if bearer, err := t.getBearerToken(); err == nil {
			config := &oauth2.Config{}
			token := &oauth2.Token{AccessToken: bearer}
			// OAuth2 http.Client will automatically authorize Requests
			httpClient := config.Client(oauth2.NoContext, token)
			// Twitter client
			t.client = twitter.NewClient(httpClient)
		}
	}

	go t.processRequests()
	return nil
}

func (t *Twitter) processRequests() {
	last := time.Now()

	for {
		select {
		case <-t.Quit():
			return
		case req := <-t.DNSRequestChan():
			if t.Config().IsDomainInScope(req.Domain) {
				if time.Now().Sub(last) < t.RateLimit {
					time.Sleep(t.RateLimit)
				}
				last = time.Now()
				t.executeQuery(req.Domain)
				last = time.Now()
			}
		case <-t.AddrRequestChan():
		case <-t.ASNRequestChan():
		case <-t.WhoisRequestChan():
		}
	}
}

func (t *Twitter) executeQuery(domain string) {
	re := t.Config().DomainRegex(domain)
	if t.client == nil || re == nil {
		return
	}

	searchParams := &twitter.SearchTweetParams{
		Query: domain,
		Count: 100,
	}
	t.SetActive()
	search, _, err := t.client.Search.Tweets(searchParams)
	if err != nil {
		t.Config().Log.Printf("%s: %v", t.String(), err)
		return
	}

	for _, tweet := range search.Statuses {
		for _, name := range re.FindAllString(tweet.Text, -1) {
			t.Bus().Publish(core.NewNameTopic, &core.DNSRequest{
				Name:   name,
				Domain: domain,
				Tag:    t.SourceType,
				Source: t.String(),
			})
		}
	}
}

func (t *Twitter) getBearerToken() (string, error) {
	headers := map[string]string{"Content-Type": "application/x-www-form-urlencoded;charset=UTF-8"}
	page, err := utils.RequestWebPage(
		"https://api.twitter.com/oauth2/token",
		strings.NewReader("grant_type=client_credentials"),
		headers, t.API.Key, t.API.Secret)
	if err != nil {
		return "", fmt.Errorf("token request failed: %+v", err)
	}

	var v struct {
		AccessToken string `json:"access_token"`
	}
	if err := json.Unmarshal([]byte(page), &v); err != nil {
		return "", fmt.Errorf("error parsing json in token response: %+v", err)
	}
	if v.AccessToken == "" {
		return "", fmt.Errorf("token response does not have access_token")
	}
	return v.AccessToken, nil
}
<commit_msg>fixed the Twitter data source service<commit_after>// Copyright 2017 Jeff Foley. All rights reserved.
// Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file.

package sources

import (
	"encoding/json"
	"fmt"
	"strings"
	"time"

	"github.com/OWASP/Amass/amass/core"
	"github.com/OWASP/Amass/amass/utils"
	"github.com/dghubble/go-twitter/twitter"
	"golang.org/x/oauth2"
)

// Twitter is the Service that handles access to the Twitter data source.
type Twitter struct {
	core.BaseService

	API        *core.APIKey
	SourceType string
	RateLimit  time.Duration
	client     *twitter.Client
}

// NewTwitter returns he object initialized, but not yet started.
func NewTwitter(config *core.Config, bus *core.EventBus) *Twitter {
	t := &Twitter{
		SourceType: core.API,
		RateLimit:  3 * time.Second,
	}

	t.BaseService = *core.NewBaseService(t, "Twitter", config, bus)
	return t
}

// OnStart implements the Service interface
func (t *Twitter) OnStart() error {
	t.BaseService.OnStart()

	t.API = t.Config().GetAPIKey(t.String())
	if t.API == nil || t.API.Key == "" || t.API.Secret == "" {
		t.Config().Log.Printf("%s: API key data was not provided", t.String())
	}
	if t.API != nil && t.API.Key != "" && t.API.Secret != "" {
		if bearer, err := t.getBearerToken(); err == nil {
			config := &oauth2.Config{}
			token := &oauth2.Token{AccessToken: bearer}
			// OAuth2 http.Client will automatically authorize Requests
			httpClient := config.Client(oauth2.NoContext, token)
			// Twitter client
			t.client = twitter.NewClient(httpClient)
		}
	}

	go t.processRequests()
	return nil
}

func (t *Twitter) processRequests() {
	last := time.Now()

	for {
		select {
		case <-t.Quit():
			return
		case req := <-t.DNSRequestChan():
			if t.Config().IsDomainInScope(req.Domain) {
				if time.Now().Sub(last) < t.RateLimit {
					time.Sleep(t.RateLimit)
				}
				last = time.Now()
				t.executeQuery(req.Domain)
				last = time.Now()
			}
		case <-t.AddrRequestChan():
		case <-t.ASNRequestChan():
		case <-t.WhoisRequestChan():
		}
	}
}

func (t *Twitter) executeQuery(domain string) {
	re := t.Config().DomainRegex(domain)
	if t.client == nil || re == nil {
		return
	}

	searchParams := &twitter.SearchTweetParams{
		Query: domain,
		Count: 100,
	}
	t.SetActive()
	search, _, err := t.client.Search.Tweets(searchParams)
	if err != nil {
		t.Config().Log.Printf("%s: %v", t.String(), err)
		return
	}

	for _, tweet := range search.Statuses {

		// Urls in the tweet body
		for _, url_entity := range tweet.Entities.Urls {

			for _, name := range re.FindAllString(url_entity.ExpandedURL, -1) {
				t.Bus().Publish(core.NewNameTopic, &core.DNSRequest{
					Name:   name,
					Domain: domain,
					Tag:    t.SourceType,
					Source: t.String(),
				})
			}
		}

		// Source of the tweet
		for _, name := range re.FindAllString(tweet.Source, -1) {
			t.Bus().Publish(core.NewNameTopic, &core.DNSRequest{
				Name:   name,
				Domain: domain,
				Tag:    t.SourceType,
				Source: t.String(),
			})
		}
	}
}

func (t *Twitter) getBearerToken() (string, error) {
	headers := map[string]string{"Content-Type": "application/x-www-form-urlencoded;charset=UTF-8"}
	page, err := utils.RequestWebPage(
		"https://api.twitter.com/oauth2/token",
		strings.NewReader("grant_type=client_credentials"),
		headers, t.API.Key, t.API.Secret)
	if err != nil {
		return "", fmt.Errorf("token request failed: %+v", err)
	}

	var v struct {
		AccessToken string `json:"access_token"`
	}
	if err := json.Unmarshal([]byte(page), &v); err != nil {
		return "", fmt.Errorf("error parsing json in token response: %+v", err)
	}
	if v.AccessToken == "" {
		return "", fmt.Errorf("token response does not have access_token")
	}
	return v.AccessToken, nil
}
<|endoftext|>


<commit_before>package config

import (
	"appengine"
	"appengine/file"
	"appengine/urlfetch"
	"golang.org/x/oauth2"
	"golang.org/x/oauth2/google"
	"google.golang.org/cloud"
	"google.golang.org/cloud/storage"
	"gopkg.in/yaml.v2"
	"io/ioutil"
	"net/http"
	"os"
	"path"
)

var Config ConfigData

type ConfigData map[string]string

func Load(c ...appengine.Context) ConfigData {
	if Config != nil {
		if len(c) > 0 {
			c[0].Debugf("loaded config from memory: %v", Config)
		}
		return Config
	}
	var configFile []byte
	if appengine.IsDevAppServer() {
		configFile, _ = ioutil.ReadFile(path.Join(os.Getenv("PWD"), "config.yaml"))
	} else {
		client := &http.Client{
			Transport: &oauth2.Transport{
				Source: google.AppEngineTokenSource(c[0], storage.ScopeReadOnly),
				Base: &urlfetch.Transport{
					Context: c[0],
				},
			},
		}
		bucket, _ := file.DefaultBucketName(c[0])
		ctx := cloud.NewContext("davine-web", client)
		rc, err := storage.NewReader(ctx, bucket, "config.yaml")
		if err != nil {
			c[0].Errorf("error reading config: %v", err.Error())
		}
		configFile, err = ioutil.ReadAll(rc)
		rc.Close()
		if err != nil {
			c[0].Errorf("error reading config: %v", err.Error())
		}
	}
	yaml.Unmarshal(configFile, &Config)
	return Config
}
<commit_msg>Removed unncessary debug log from config.go.<commit_after>package config

import (
	"appengine"
	"appengine/file"
	"appengine/urlfetch"
	"golang.org/x/oauth2"
	"golang.org/x/oauth2/google"
	"google.golang.org/cloud"
	"google.golang.org/cloud/storage"
	"gopkg.in/yaml.v2"
	"io/ioutil"
	"net/http"
	"os"
	"path"
)

var Config ConfigData

type ConfigData map[string]string

func Load(c ...appengine.Context) ConfigData {
	if Config != nil {
		return Config
	}
	var configFile []byte
	if appengine.IsDevAppServer() {
		configFile, _ = ioutil.ReadFile(path.Join(os.Getenv("PWD"), "config.yaml"))
	} else {
		client := &http.Client{
			Transport: &oauth2.Transport{
				Source: google.AppEngineTokenSource(c[0], storage.ScopeReadOnly),
				Base: &urlfetch.Transport{
					Context: c[0],
				},
			},
		}
		bucket, _ := file.DefaultBucketName(c[0])
		ctx := cloud.NewContext("davine-web", client)
		rc, err := storage.NewReader(ctx, bucket, "config.yaml")
		if err != nil {
			c[0].Errorf("error reading config: %v", err.Error())
		}
		configFile, err = ioutil.ReadAll(rc)
		rc.Close()
		if err != nil {
			c[0].Errorf("error reading config: %v", err.Error())
		}
	}
	yaml.Unmarshal(configFile, &Config)
	return Config
}
<|endoftext|>


<commit_before>package gps

import (
	"log"
	"bufio"

	"github.com/tarm/serial"
)

func InitGPS() {
	log.Printf("In gps.InitGPS()\n")

	// eventually I would like to come up with a reliable autodetection scheme for different types of gps.
	// for now I'll just have entry points into different configurations that get uncommented here

	initUltimateGPS()
}

// for the Adafruit Ultimate GPS Hat (https://www.adafruit.com/products/2324)
// MT3339 chipset
func initUltimateGPS() error {

	// module is attached via serial UART, shows up as /dev/ttyAMA0 on rpi
	device := "/dev/ttyAMA0"
	log.Printf("Using %s for GPS\n", device)

	// module comes up in 9600baud, 1hz mode
	serialConfig := &serial.Config{Name: device, Baud: 9600}
	p, err := serial.OpenPort(serialConfig)
	if err != nil { return fmt.Errorf("Error opening serial port: %v", err) }

	scanner := bufio.NewScanner(p)

	for scanner.Scan() {
		log.Printf("gps data: %s\n", scanner.Text())
	}
	if err := scanner.Err(); err != nil {
		log.Printf("Error reading serial data: %v\n", err)
	}
}<commit_msg>build fixes<commit_after>package gps

import (
	"fmt"
	"log"
	"bufio"

	"github.com/tarm/serial"
)

func InitGPS() {
	log.Printf("In gps.InitGPS()\n")

	// eventually I would like to come up with a reliable autodetection scheme for different types of gps.
	// for now I'll just have entry points into different configurations that get uncommented here

	initUltimateGPS()
}

// for the Adafruit Ultimate GPS Hat (https://www.adafruit.com/products/2324)
// MT3339 chipset
func initUltimateGPS() error {

	// module is attached via serial UART, shows up as /dev/ttyAMA0 on rpi
	device := "/dev/ttyAMA0"
	log.Printf("Using %s for GPS\n", device)

	// module comes up in 9600baud, 1hz mode
	serialConfig := &serial.Config{Name: device, Baud: 9600}
	p, err := serial.OpenPort(serialConfig)
	if err != nil { return fmt.Errorf("Error opening serial port: %v", err) }

	scanner := bufio.NewScanner(p)

	for scanner.Scan() {
		log.Printf("gps data: %s\n", scanner.Text())
	}
	if err := scanner.Err(); err != nil {
		log.Printf("Error reading serial data: %v\n", err)
	}

	return nil
}
<|endoftext|>


<commit_before>/*
Copyright 2020 gRPC authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package v1

import (
	corev1 "k8s.io/api/core/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

// NOTE: AFTER EDITS, YOU MUST RUN `make manifests` AND `make` TO REGENERATE
// CODE.

// Clone defines expectations regarding which repository and snapshot the test
// should use.
type Clone struct {
	// Image is the name of the container image that can clone code placing
	// it in a /src/workspace directory.
	//
	// This field is optional. When omitted, a container that can clone
	// public GitHub repos over HTTPs is used.
	// +optional
	Image *string `json:"image,omitempty"`

	// Repo is the URL to clone a git repository. With GitHub, this should
	// end in a `.git` extension.
	// +optional
	Repo *string `json:"repo,omitempty"`

	// GitRef is a branch, tag or commit hash to checkout after a
	// successful clone. This will be the version of the code in the
	// /src/workspace directory.
	// +optional
	GitRef *string `json:"gitRef,omitempty"`
}

// Build defines expectations regarding which container image,
// command, arguments and environment variables are used to build the
// component.
type Build struct {
	// Image is the name of the container image that can build code,
	// placing an executable in the /src/workspace directory.
	//
	// This field is optional when a Language is specified on the
	// Component. For example, a developer may specify a "java" server.
	// Then, this image will default to the most recent gradle image.
	// +optional
	Image *string `json:"image,omitempty"`

	// Command is the path to the executable that will build the code in
	// the /src/workspace directory. If unspecified, the entrypoint for
	// the container is used.
	// +optional
	Command []string `json:"command,omitempty"`

	// Args provide command line arguments to the command. If a command
	// is not specified, these arguments will be ignored in favor of the
	// default arguments for container's entrypoint.
	// +optional
	Args []string `json:"args,omitempty"`

	// Env are environment variables that should be set within the build
	// container. This is provided for compilers that alter behavior due
	// to certain environment variables.
	// +optional
	Env []corev1.EnvVar `json:"env,omitempty"`
}

// Run defines expectations regarding the runtime environment for the
// test component itself.
type Run struct {
	// Image is the name of the container image that provides the
	// runtime for the test component.
	//
	// This field is optional when a Language is specified on the
	// Component. For example, a developer may specify a "python3"
	// client. This field will be implicitly set to the most recent
	// supported python3 image.
	// +optional
	Image *string `json:"image,omitempty"`

	// Command is the path to the executable that will run the component
	// of the test. When unset, the entrypoint of the container image
	// will be used.
	// +optional
	Command []string `json:"command,omitempty"`

	// Args provide command line arguments to the command.
	// +optional
	Args []string `json:"args,omitempty"`

	// Env are environment variables that should be set within the
	// running container.
	// +optional
	Env []corev1.EnvVar `json:"env,omitempty"`

	// VolumeMounts permit sharing directories across containers.
	// +optional
	VolumeMounts []corev1.VolumeMount `json:"volumeMounts,omitempty"`
}

// Component defines a runnable unit of the test.
type Component struct {
	// Name is a string which uniquely identifies the component when
	// compared to other components in the load test. If omitted, the
	// system will assign a globally unique name.
	// +optional
	Name *string `json:"name,omitempty"`

	// Language is the code that identifies the programming language used by
	// the component. For example, "cxx" may represent C++.
	//
	// Specifying a language is required. Aside from metadata, It allows the
	// image field on the Build and Run objects to be inferred.
	Language string `json:"language"`

	// Pool specifies the name of the set of nodes where this component should
	// be scheduled. If unset, the controller will choose a pool based on the
	// type of component.
	Pool *string `json:"pool,omitempty"`

	// Clone specifies the repository and snapshot where the code can be
	// found. This is used to build and run tests.
	// +optional
	Clone *Clone `json:"clone,omitempty"`

	// Build describes how the cloned code should be built, including any
	// compiler arguments or flags.
	// +optional
	Build *Build `json:"build,omitempty"`

	// Run describes the runtime of the container during the test
	// itself. This is required, because the system must run some
	// container.
	Run Run `json:"run"`
}

// Driver defines a component that orchestrates the server and clients
// in the test.
type Driver struct {
	Component `json:",inline"`
}

// Server defines a component that receives traffic from a set of client
// components.
type Server struct {
	Component `json:",inline"`
}

// Client defines a component that sends traffic to a server component.
type Client struct {
	Component `json:",inline"`
}

// Results defines where and how test results and artifacts should be
// stored.
type Results struct {
	// BigQueryTable names a dataset where the results of the test
	// should be stored. If omitted, no results are saved to BigQuery.
	// +optional
	BigQueryTable *string `json:"bigQueryTable,omitempty"`
}

// Scenario references a ConfigMap with the configuration for the driver
// and the server clients under test.
type Scenario struct {
	// Name identifies the name of the ConfigMap with the scenario data.
	Name string `json:"name"`
}

// LoadTestSpec defines the desired state of LoadTest
type LoadTestSpec struct {
	// Driver is the component that orchestrates the test. It may be
	// unspecified, allowing the system to choose the appropriate driver.
	// +optional
	Driver *Driver `json:"driver,omitempty"`

	// Servers are a list of components that receive traffic from
	// clients.
	// +optional
	Servers []Server `json:"servers,omitempty"`

	// Clients are a list of components that send traffic to servers.
	// +optional
	Clients []Client `json:"clients,omitempty"`

	// Results configures where the results of the test should be
	// stored. When omitted, the results will only be stored in
	// Kubernetes for a limited time.
	// +optional
	Results *Results `json:"results,omitempty"`

	// Scenarios provides a list of configurations for testing.
	// +optional
	Scenarios []Scenario `json:"scenarios,omitempty"`
}

// LoadTestState is a possible state, conveying the progress of setting
// up, running and tearing down a load test.
// +kubebuilder:default=Unrecognized
type LoadTestState string

const (
	// UnrecognizedState indicates that the controller has not yet
	// acknowledged or started reconiling the load test.
	UnrecognizedState LoadTestState = "Unrecognized"

	// WaitingState indicates that the load test is waiting for
	// sufficient machine availability in order to be scheduled.
	WaitingState = "Waiting"

	// ProvisioningState indicates that the load test's resources are
	// being created.
	ProvisioningState = "Provisioning"

	// PendingState indicates that the load test's resources are healthy
	// The load test will remain in this state until the status of one
	// of its resources changes.
	PendingState = "Pending"

	// FailState indicates that a resource in the load test has
	// terminated unsuccessfully.
	FailState = "Failed"

	// SuccessState indicates that a resource terminated with a
	// successful status.
	SuccessState = "Succeeded"

	// ErrorState indicates that something went wrong, preventing the
	// controller for reconciling the load test.
	ErrorState = "Error"
)

// LoadTestStatus defines the observed state of LoadTest
type LoadTestStatus struct {
	// State identifies the current state of the load test. It is
	// important to note that this state is level-based. This means its
	// transition is non-deterministic.
	State LoadTestState `json:"state"`

	// AcknowledgeTime marks when the controller first responded to the
	// load test.
	// +optional
	AcknowledgeTime *metav1.Time `json:"acknowledgeTime,omitempty"`

	// ProvisionTime marks the time when the controller began to
	// provision the resources for the load test.
	// +optional
	ProvisionTime *metav1.Time `json:"provisionTime,omitempty"`

	// PendingTime marks the time when the load test's resources were
	// found to be in the pending state.
	// +optional
	PendingTime *metav1.Time `json:"pendingTime,omitempty"`

	// TerminateTime marks the time when a resource for the load test
	// was marked as terminated.
	// +optional
	TerminateTime *metav1.Time `json:"terminateTime,omitempty"`
}

// +kubebuilder:object:root=true
// +kubebuilder:subresource:status

// LoadTest is the Schema for the loadtests API
type LoadTest struct {
	metav1.TypeMeta   `json:",inline"`
	metav1.ObjectMeta `json:"metadata,omitempty"`

	Spec   LoadTestSpec   `json:"spec,omitempty"`
	Status LoadTestStatus `json:"status,omitempty"`
}

// +kubebuilder:object:root=true

// LoadTestList contains a list of LoadTest
type LoadTestList struct {
	metav1.TypeMeta `json:",inline"`
	metav1.ListMeta `json:"metadata,omitempty"`
	Items           []LoadTest `json:"items"`
}

func init() {
	SchemeBuilder.Register(&LoadTest{}, &LoadTestList{})
}
<commit_msg>Add missing comma in api/v1/loadtest_types.go<commit_after>/*
Copyright 2020 gRPC authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package v1

import (
	corev1 "k8s.io/api/core/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

// NOTE: AFTER EDITS, YOU MUST RUN `make manifests` AND `make` TO REGENERATE
// CODE.

// Clone defines expectations regarding which repository and snapshot the test
// should use.
type Clone struct {
	// Image is the name of the container image that can clone code, placing
	// it in a /src/workspace directory.
	//
	// This field is optional. When omitted, a container that can clone
	// public GitHub repos over HTTPs is used.
	// +optional
	Image *string `json:"image,omitempty"`

	// Repo is the URL to clone a git repository. With GitHub, this should
	// end in a `.git` extension.
	// +optional
	Repo *string `json:"repo,omitempty"`

	// GitRef is a branch, tag or commit hash to checkout after a
	// successful clone. This will be the version of the code in the
	// /src/workspace directory.
	// +optional
	GitRef *string `json:"gitRef,omitempty"`
}

// Build defines expectations regarding which container image,
// command, arguments and environment variables are used to build the
// component.
type Build struct {
	// Image is the name of the container image that can build code,
	// placing an executable in the /src/workspace directory.
	//
	// This field is optional when a Language is specified on the
	// Component. For example, a developer may specify a "java" server.
	// Then, this image will default to the most recent gradle image.
	// +optional
	Image *string `json:"image,omitempty"`

	// Command is the path to the executable that will build the code in
	// the /src/workspace directory. If unspecified, the entrypoint for
	// the container is used.
	// +optional
	Command []string `json:"command,omitempty"`

	// Args provide command line arguments to the command. If a command
	// is not specified, these arguments will be ignored in favor of the
	// default arguments for container's entrypoint.
	// +optional
	Args []string `json:"args,omitempty"`

	// Env are environment variables that should be set within the build
	// container. This is provided for compilers that alter behavior due
	// to certain environment variables.
	// +optional
	Env []corev1.EnvVar `json:"env,omitempty"`
}

// Run defines expectations regarding the runtime environment for the
// test component itself.
type Run struct {
	// Image is the name of the container image that provides the
	// runtime for the test component.
	//
	// This field is optional when a Language is specified on the
	// Component. For example, a developer may specify a "python3"
	// client. This field will be implicitly set to the most recent
	// supported python3 image.
	// +optional
	Image *string `json:"image,omitempty"`

	// Command is the path to the executable that will run the component
	// of the test. When unset, the entrypoint of the container image
	// will be used.
	// +optional
	Command []string `json:"command,omitempty"`

	// Args provide command line arguments to the command.
	// +optional
	Args []string `json:"args,omitempty"`

	// Env are environment variables that should be set within the
	// running container.
	// +optional
	Env []corev1.EnvVar `json:"env,omitempty"`

	// VolumeMounts permit sharing directories across containers.
	// +optional
	VolumeMounts []corev1.VolumeMount `json:"volumeMounts,omitempty"`
}

// Component defines a runnable unit of the test.
type Component struct {
	// Name is a string which uniquely identifies the component when
	// compared to other components in the load test. If omitted, the
	// system will assign a globally unique name.
	// +optional
	Name *string `json:"name,omitempty"`

	// Language is the code that identifies the programming language used by
	// the component. For example, "cxx" may represent C++.
	//
	// Specifying a language is required. Aside from metadata, It allows the
	// image field on the Build and Run objects to be inferred.
	Language string `json:"language"`

	// Pool specifies the name of the set of nodes where this component should
	// be scheduled. If unset, the controller will choose a pool based on the
	// type of component.
	Pool *string `json:"pool,omitempty"`

	// Clone specifies the repository and snapshot where the code can be
	// found. This is used to build and run tests.
	// +optional
	Clone *Clone `json:"clone,omitempty"`

	// Build describes how the cloned code should be built, including any
	// compiler arguments or flags.
	// +optional
	Build *Build `json:"build,omitempty"`

	// Run describes the runtime of the container during the test
	// itself. This is required, because the system must run some
	// container.
	Run Run `json:"run"`
}

// Driver defines a component that orchestrates the server and clients
// in the test.
type Driver struct {
	Component `json:",inline"`
}

// Server defines a component that receives traffic from a set of client
// components.
type Server struct {
	Component `json:",inline"`
}

// Client defines a component that sends traffic to a server component.
type Client struct {
	Component `json:",inline"`
}

// Results defines where and how test results and artifacts should be
// stored.
type Results struct {
	// BigQueryTable names a dataset where the results of the test
	// should be stored. If omitted, no results are saved to BigQuery.
	// +optional
	BigQueryTable *string `json:"bigQueryTable,omitempty"`
}

// Scenario references a ConfigMap with the configuration for the driver
// and the server clients under test.
type Scenario struct {
	// Name identifies the name of the ConfigMap with the scenario data.
	Name string `json:"name"`
}

// LoadTestSpec defines the desired state of LoadTest
type LoadTestSpec struct {
	// Driver is the component that orchestrates the test. It may be
	// unspecified, allowing the system to choose the appropriate driver.
	// +optional
	Driver *Driver `json:"driver,omitempty"`

	// Servers are a list of components that receive traffic from
	// clients.
	// +optional
	Servers []Server `json:"servers,omitempty"`

	// Clients are a list of components that send traffic to servers.
	// +optional
	Clients []Client `json:"clients,omitempty"`

	// Results configures where the results of the test should be
	// stored. When omitted, the results will only be stored in
	// Kubernetes for a limited time.
	// +optional
	Results *Results `json:"results,omitempty"`

	// Scenarios provides a list of configurations for testing.
	// +optional
	Scenarios []Scenario `json:"scenarios,omitempty"`
}

// LoadTestState is a possible state, conveying the progress of setting
// up, running and tearing down a load test.
// +kubebuilder:default=Unrecognized
type LoadTestState string

const (
	// UnrecognizedState indicates that the controller has not yet
	// acknowledged or started reconiling the load test.
	UnrecognizedState LoadTestState = "Unrecognized"

	// WaitingState indicates that the load test is waiting for
	// sufficient machine availability in order to be scheduled.
	WaitingState = "Waiting"

	// ProvisioningState indicates that the load test's resources are
	// being created.
	ProvisioningState = "Provisioning"

	// PendingState indicates that the load test's resources are healthy
	// The load test will remain in this state until the status of one
	// of its resources changes.
	PendingState = "Pending"

	// FailState indicates that a resource in the load test has
	// terminated unsuccessfully.
	FailState = "Failed"

	// SuccessState indicates that a resource terminated with a
	// successful status.
	SuccessState = "Succeeded"

	// ErrorState indicates that something went wrong, preventing the
	// controller for reconciling the load test.
	ErrorState = "Error"
)

// LoadTestStatus defines the observed state of LoadTest
type LoadTestStatus struct {
	// State identifies the current state of the load test. It is
	// important to note that this state is level-based. This means its
	// transition is non-deterministic.
	State LoadTestState `json:"state"`

	// AcknowledgeTime marks when the controller first responded to the
	// load test.
	// +optional
	AcknowledgeTime *metav1.Time `json:"acknowledgeTime,omitempty"`

	// ProvisionTime marks the time when the controller began to
	// provision the resources for the load test.
	// +optional
	ProvisionTime *metav1.Time `json:"provisionTime,omitempty"`

	// PendingTime marks the time when the load test's resources were
	// found to be in the pending state.
	// +optional
	PendingTime *metav1.Time `json:"pendingTime,omitempty"`

	// TerminateTime marks the time when a resource for the load test
	// was marked as terminated.
	// +optional
	TerminateTime *metav1.Time `json:"terminateTime,omitempty"`
}

// +kubebuilder:object:root=true
// +kubebuilder:subresource:status

// LoadTest is the Schema for the loadtests API
type LoadTest struct {
	metav1.TypeMeta   `json:",inline"`
	metav1.ObjectMeta `json:"metadata,omitempty"`

	Spec   LoadTestSpec   `json:"spec,omitempty"`
	Status LoadTestStatus `json:"status,omitempty"`
}

// +kubebuilder:object:root=true

// LoadTestList contains a list of LoadTest
type LoadTestList struct {
	metav1.TypeMeta `json:",inline"`
	metav1.ListMeta `json:"metadata,omitempty"`
	Items           []LoadTest `json:"items"`
}

func init() {
	SchemeBuilder.Register(&LoadTest{}, &LoadTestList{})
}
<|endoftext|>