<commit_before>package app

import (
	"bytes"
	"encoding/json"
	"fmt"
	"github.com/timeredbull/tsuru/api/auth"
	"github.com/timeredbull/tsuru/api/repository"
	"github.com/timeredbull/tsuru/db"
	"github.com/timeredbull/tsuru/errors"
	"io/ioutil"
	"launchpad.net/mgo/bson"
	"net/http"
)

func getAppOrError(name string, u *auth.User) (App, error) {
	app := App{Name: name}
	err := app.Get()
	if err != nil {
		return app, &errors.Http{Code: http.StatusNotFound, Message: "App not found"}
	}
	if !app.CheckUserAccess(u) {
		return app, &errors.Http{Code: http.StatusForbidden, Message: "User does not have access to this app"}
	}
	return app, nil
}

func CloneRepositoryHandler(w http.ResponseWriter, r *http.Request) error {
	app := App{Name: r.URL.Query().Get(":name")}
	err := app.Get()
	if err != nil {
		return &errors.Http{Code: http.StatusNotFound, Message: "App not found"}
	}
	repository.CloneRepository(app.Name)
	fmt.Fprint(w, "success")
	return nil
}

func AppDelete(w http.ResponseWriter, r *http.Request, u *auth.User) error {
	app, err := getAppOrError(r.URL.Query().Get(":name"), u)
	if err != nil {
		return err
	}
	app.Destroy()
	fmt.Fprint(w, "success")
	return nil
}

func AppList(w http.ResponseWriter, r *http.Request, u *auth.User) error {
	var apps []App
	err := db.Session.Apps().Find(bson.M{"teams.users.email": u.Email}).All(&apps)
	if err != nil {
		return err
	}
	if len(apps) == 0 {
		w.WriteHeader(http.StatusNoContent)
		return nil
	}
	b, err := json.Marshal(apps)
	if err != nil {
		return err
	}
	fmt.Fprint(w, bytes.NewBuffer(b).String())
	return nil
}

func AppInfo(w http.ResponseWriter, r *http.Request, u *auth.User) error {
	app, err := getAppOrError(r.URL.Query().Get(":name"), u)
	if err != nil {
		return err
	}
	b, err := json.Marshal(app)
	if err != nil {
		return err
	}
	fmt.Fprint(w, bytes.NewBuffer(b).String())
	return nil
}

func createApp(app App, u *auth.User) ([]byte, error) {
	err := db.Session.Teams().Find(bson.M{"users.email": u.Email}).All(&app.Teams)
	if err != nil {
		return nil, err
	}
	if len(app.Teams) < 1 {
		msg := "In order to create an app, you should be member of at least one team"
		return nil, &errors.Http{Code: http.StatusForbidden, Message: msg}
	}
	err = app.Create()
	if err != nil {
		return nil, err
	}
	msg := map[string]string{
		"status":         "success",
		"repository_url": repository.GetRepositoryUrl(app.Name),
	}
	return json.Marshal(msg)
}

func CreateAppHandler(w http.ResponseWriter, r *http.Request, u *auth.User) error {
	var app App
	defer r.Body.Close()
	body, err := ioutil.ReadAll(r.Body)
	if err != nil {
		return err
	}
	err = json.Unmarshal(body, &app)
	if err != nil {
		return err
	}
	jsonMsg, err := createApp(app, u)
	if err != nil {
		return err
	}
	fmt.Fprint(w, bytes.NewBuffer(jsonMsg).String())
	return nil
}

func GrantAccessToTeamHandler(w http.ResponseWriter, r *http.Request, u *auth.User) error {
	t := new(auth.Team)
	app := &App{Name: r.URL.Query().Get(":app")}
	err := app.Get()
	if err != nil {
		return &errors.Http{Code: http.StatusNotFound, Message: "App not found"}
	}
	if !app.CheckUserAccess(u) {
		return &errors.Http{Code: http.StatusUnauthorized, Message: "User unauthorized"}
	}
	err = db.Session.Teams().Find(bson.M{"name": r.URL.Query().Get(":team")}).One(t)
	if err != nil {
		return &errors.Http{Code: http.StatusNotFound, Message: "Team not found"}
	}
	err = app.GrantAccess(t)
	if err != nil {
		return &errors.Http{Code: http.StatusConflict, Message: err.Error()}
	}
	return db.Session.Apps().Update(bson.M{"name": app.Name}, app)
}

func RevokeAccessFromTeamHandler(w http.ResponseWriter, r *http.Request, u *auth.User) error {
	t := new(auth.Team)
	app := &App{Name: r.URL.Query().Get(":app")}
	err := app.Get()
	if err != nil {
		return &errors.Http{Code: http.StatusNotFound, Message: "App not found"}
	}
	if !app.CheckUserAccess(u) {
		return &errors.Http{Code: http.StatusUnauthorized, Message: "User unauthorized"}
	}
	err = db.Session.Teams().Find(bson.M{"name": r.URL.Query().Get(":team")}).One(t)
	if err != nil {
		return &errors.Http{Code: http.StatusNotFound, Message: "Team not found"}
	}
	if len(app.Teams) == 1 {
		msg := "You can not revoke the access from this team, because it is the unique team with access to the app, and an app can not be orphaned"
		return &errors.Http{Code: http.StatusForbidden, Message: msg}
	}
	err = app.RevokeAccess(t)
	if err != nil {
		return &errors.Http{Code: http.StatusNotFound, Message: err.Error()}
	}
	return db.Session.Apps().Update(bson.M{"name": app.Name}, app)
}
<commit_msg>api/app: extracting (grant|revoke)Access(To|From)Team<commit_after>package app

import (
	"bytes"
	"encoding/json"
	"fmt"
	"github.com/timeredbull/tsuru/api/auth"
	"github.com/timeredbull/tsuru/api/repository"
	"github.com/timeredbull/tsuru/db"
	"github.com/timeredbull/tsuru/errors"
	"io/ioutil"
	"launchpad.net/mgo/bson"
	"net/http"
)

func getAppOrError(name string, u *auth.User) (App, error) {
	app := App{Name: name}
	err := app.Get()
	if err != nil {
		return app, &errors.Http{Code: http.StatusNotFound, Message: "App not found"}
	}
	if !app.CheckUserAccess(u) {
		return app, &errors.Http{Code: http.StatusForbidden, Message: "User does not have access to this app"}
	}
	return app, nil
}

func CloneRepositoryHandler(w http.ResponseWriter, r *http.Request) error {
	app := App{Name: r.URL.Query().Get(":name")}
	err := app.Get()
	if err != nil {
		return &errors.Http{Code: http.StatusNotFound, Message: "App not found"}
	}
	repository.CloneRepository(app.Name)
	fmt.Fprint(w, "success")
	return nil
}

func AppDelete(w http.ResponseWriter, r *http.Request, u *auth.User) error {
	app, err := getAppOrError(r.URL.Query().Get(":name"), u)
	if err != nil {
		return err
	}
	app.Destroy()
	fmt.Fprint(w, "success")
	return nil
}

func AppList(w http.ResponseWriter, r *http.Request, u *auth.User) error {
	var apps []App
	err := db.Session.Apps().Find(bson.M{"teams.users.email": u.Email}).All(&apps)
	if err != nil {
		return err
	}
	if len(apps) == 0 {
		w.WriteHeader(http.StatusNoContent)
		return nil
	}
	b, err := json.Marshal(apps)
	if err != nil {
		return err
	}
	fmt.Fprint(w, bytes.NewBuffer(b).String())
	return nil
}

func AppInfo(w http.ResponseWriter, r *http.Request, u *auth.User) error {
	app, err := getAppOrError(r.URL.Query().Get(":name"), u)
	if err != nil {
		return err
	}
	b, err := json.Marshal(app)
	if err != nil {
		return err
	}
	fmt.Fprint(w, bytes.NewBuffer(b).String())
	return nil
}

func createApp(app App, u *auth.User) ([]byte, error) {
	err := db.Session.Teams().Find(bson.M{"users.email": u.Email}).All(&app.Teams)
	if err != nil {
		return nil, err
	}
	if len(app.Teams) < 1 {
		msg := "In order to create an app, you should be member of at least one team"
		return nil, &errors.Http{Code: http.StatusForbidden, Message: msg}
	}
	err = app.Create()
	if err != nil {
		return nil, err
	}
	msg := map[string]string{
		"status":         "success",
		"repository_url": repository.GetRepositoryUrl(app.Name),
	}
	return json.Marshal(msg)
}

func CreateAppHandler(w http.ResponseWriter, r *http.Request, u *auth.User) error {
	var app App
	defer r.Body.Close()
	body, err := ioutil.ReadAll(r.Body)
	if err != nil {
		return err
	}
	err = json.Unmarshal(body, &app)
	if err != nil {
		return err
	}
	jsonMsg, err := createApp(app, u)
	if err != nil {
		return err
	}
	fmt.Fprint(w, bytes.NewBuffer(jsonMsg).String())
	return nil
}

func grantAccessToTeam(appName, teamName string, u *auth.User) error {
	t := new(auth.Team)
	app := &App{Name: appName}
	err := app.Get()
	if err != nil {
		return &errors.Http{Code: http.StatusNotFound, Message: "App not found"}
	}
	if !app.CheckUserAccess(u) {
		return &errors.Http{Code: http.StatusUnauthorized, Message: "User unauthorized"}
	}
	err = db.Session.Teams().Find(bson.M{"name": teamName}).One(t)
	if err != nil {
		return &errors.Http{Code: http.StatusNotFound, Message: "Team not found"}
	}
	err = app.GrantAccess(t)
	if err != nil {
		return &errors.Http{Code: http.StatusConflict, Message: err.Error()}
	}
	return db.Session.Apps().Update(bson.M{"name": app.Name}, app)
}

func GrantAccessToTeamHandler(w http.ResponseWriter, r *http.Request, u *auth.User) error {
	appName := r.URL.Query().Get(":app")
	teamName := r.URL.Query().Get(":team")
	return grantAccessToTeam(appName, teamName, u)
}

func revokeAccessFromTeam(appName, teamName string, u *auth.User) error {
	t := new(auth.Team)
	app := &App{Name: appName}
	err := app.Get()
	if err != nil {
		return &errors.Http{Code: http.StatusNotFound, Message: "App not found"}
	}
	if !app.CheckUserAccess(u) {
		return &errors.Http{Code: http.StatusUnauthorized, Message: "User unauthorized"}
	}
	err = db.Session.Teams().Find(bson.M{"name": teamName}).One(t)
	if err != nil {
		return &errors.Http{Code: http.StatusNotFound, Message: "Team not found"}
	}
	if len(app.Teams) == 1 {
		msg := "You can not revoke the access from this team, because it is the unique team with access to the app, and an app can not be orphaned"
		return &errors.Http{Code: http.StatusForbidden, Message: msg}
	}
	err = app.RevokeAccess(t)
	if err != nil {
		return &errors.Http{Code: http.StatusNotFound, Message: err.Error()}
	}
	return db.Session.Apps().Update(bson.M{"name": app.Name}, app)
}

func RevokeAccessFromTeamHandler(w http.ResponseWriter, r *http.Request, u *auth.User) error {
	appName := r.URL.Query().Get(":app")
	teamName := r.URL.Query().Get(":team")
	return revokeAccessFromTeam(appName, teamName, u)
}
<|endoftext|>


<commit_before>package api

import (
	"encoding/json"
	"net/http"
	"strings"
)

const (
	before = `<!DOCTYPE html>
<!-- If you are reading this, there is a good chance you would prefer sending an
"Accept: application/json" header and receiving actual JSON responses. -->
<link rel="stylesheet" type="text/css" href="%CSS%" />
<script src="%JS%"></script>
<script>
var curlUser='${RANCHER_ACCESS_KEY}:${RANCHER_SECRET_KEY}';
var schemas="%SCHEMAS%";
//var docs = "http://url-to-your-docs/site";
//BEFORE DATA
var data =
`
	defaultCssUrl = "https://releases.rancher.com/api-ui/1.0.4/ui.min.css"
	defaultJsUrl  = "https://releases.rancher.com/api-ui/1.0.4/ui.min.js"
)

var (
	after = []byte(`;
</script>`)
)

type HtmlWriter struct {
	CssUrl, JsUrl string
	r             *http.Request
}

func (j *HtmlWriter) Write(obj interface{}, rw http.ResponseWriter) error {
	apiContext := GetApiContext(j.r)
	rw.Header().Set("X-API-Schemas", apiContext.UrlBuilder.Collection("schema"))

	if rw.Header().Get("Content-Type") == "" {
		rw.Header().Set("Content-Type", "text/html; charset=utf-8")
	}

	if _, err := rw.Write([]byte(j.before())); err != nil {
		return err
	}

	enc := json.NewEncoder(rw)
	if err := enc.Encode(obj); err != nil {
		return err
	}

	if _, err := rw.Write([]byte(after)); err != nil {
		return err
	}

	return nil
}

func (j *HtmlWriter) before() string {
	css := j.CssUrl
	if css == "" {
		css = defaultCssUrl
	}

	js := j.JsUrl
	if js == "" {
		js = defaultJsUrl
	}

	apiContext := GetApiContext(j.r)

	content := strings.Replace(before, "%CSS%", css, -1)
	content = strings.Replace(content, "%JS%", js, -1)
	content = strings.Replace(content, "%SCHEMAS%", apiContext.UrlBuilder.Collection("schema"), -1)

	return content
}
<commit_msg>Switch to api-ui 1.0.5<commit_after>package api

import (
	"encoding/json"
	"net/http"
	"strings"
)

const (
	before = `<!DOCTYPE html>
<!-- If you are reading this, there is a good chance you would prefer sending an
"Accept: application/json" header and receiving actual JSON responses. -->
<link rel="stylesheet" type="text/css" href="%CSS%" />
<script src="%JS%"></script>
<script>
var curlUser='${RANCHER_ACCESS_KEY}:${RANCHER_SECRET_KEY}';
var schemas="%SCHEMAS%";
//var docs = "http://url-to-your-docs/site";
//BEFORE DATA
var data =
`
	defaultCssUrl = "https://releases.rancher.com/api-ui/1.0.5/ui.css"
	defaultJsUrl  = "https://releases.rancher.com/api-ui/1.0.5/ui.js"
)

var (
	after = []byte(`;
</script>`)
)

type HtmlWriter struct {
	CssUrl, JsUrl string
	r             *http.Request
}

func (j *HtmlWriter) Write(obj interface{}, rw http.ResponseWriter) error {
	apiContext := GetApiContext(j.r)
	rw.Header().Set("X-API-Schemas", apiContext.UrlBuilder.Collection("schema"))

	if rw.Header().Get("Content-Type") == "" {
		rw.Header().Set("Content-Type", "text/html; charset=utf-8")
	}

	if _, err := rw.Write([]byte(j.before())); err != nil {
		return err
	}

	enc := json.NewEncoder(rw)
	if err := enc.Encode(obj); err != nil {
		return err
	}

	if _, err := rw.Write([]byte(after)); err != nil {
		return err
	}

	return nil
}

func (j *HtmlWriter) before() string {
	css := j.CssUrl
	if css == "" {
		css = defaultCssUrl
	}

	js := j.JsUrl
	if js == "" {
		js = defaultJsUrl
	}

	apiContext := GetApiContext(j.r)

	content := strings.Replace(before, "%CSS%", css, -1)
	content = strings.Replace(content, "%JS%", js, -1)
	content = strings.Replace(content, "%SCHEMAS%", apiContext.UrlBuilder.Collection("schema"), -1)

	return content
}
<|endoftext|>


<commit_before>package api

import (
	"context"
	"crypto/tls"
	"crypto/x509"
	"errors"
	"io/ioutil"
	"net/http"
	"os"
	"os/signal"
	"strconv"
	"time"

	"github.com/STNS/STNS/middleware"
	"github.com/STNS/STNS/model"
	"github.com/STNS/STNS/stns"
	"github.com/facebookgo/pidfile"
	"github.com/labstack/echo"
	emiddleware "github.com/labstack/echo/middleware"

	"github.com/labstack/gommon/log"

	"github.com/lestrrat/go-server-starter/listener"
)

type httpServer struct {
	baseServer
}

func newHTTPServer(confPath string) (*httpServer, error) {
	conf, err := stns.NewConfig(confPath)
	if err != nil {
		return nil, err
	}

	s := &httpServer{
		baseServer{config: &conf},
	}
	return s, nil
}

func status(c echo.Context) error {
	return c.String(http.StatusOK, "OK")
}

// Run サーバの起動
func (s *httpServer) Run() error {
	var backends model.Backends
	e := echo.New()
	if os.Getenv("STNS_LOG") != "" {
		f, err := os.OpenFile(os.Getenv("STNS_LOG"), os.O_RDWR|os.O_CREATE|os.O_APPEND, 0644)
		if err != nil {
			return errors.New("error opening file :" + err.Error())
		}
		e.Logger.SetOutput(f)
	} else {
		e.Logger.SetLevel(log.DEBUG)
	}
	e.GET("/status", status)

	if err := pidfile.Write(); err != nil {
		return err
	}

	defer func() {
		if err := os.Remove(pidfile.GetPidfilePath()); err != nil {
			e.Logger.Fatalf("Error removing %s: %s", pidfile.GetPidfilePath(), err)
		}
	}()

	b, err := model.NewBackendTomlFile(s.config.Users, s.config.Groups)
	if err != nil {
		return err
	}
	backends = append(backends, b)

	err = s.loadModules(e.Logger.(*log.Logger), &backends)
	if err != nil {
		return err
	}

	e.Use(middleware.Backends(backends))
	e.Use(middleware.AddHeader(backends))

	e.Use(emiddleware.Recover())
	e.Use(emiddleware.LoggerWithConfig(emiddleware.LoggerConfig{
		Format: `{"time":"${time_rfc3339_nano}","remote_ip":"${remote_ip}","host":"${host}",` +
			`"method":"${method}","uri":"${uri}","status":${status}}` + "\n",
	}))

	if s.config.BasicAuth != nil {
		e.Use(emiddleware.BasicAuthWithConfig(
			emiddleware.BasicAuthConfig{
				Validator: func(username, password string, c echo.Context) (bool, error) {
					if username == s.config.BasicAuth.User && password == s.config.BasicAuth.Password {
						return true, nil
					}
					return false, nil
				},
				Skipper: func(c echo.Context) bool {
					if c.Path() == "/" || c.Path() == "/status" || len(os.Getenv("CI")) > 0 {
						return true
					}
					return false
				},
			}))
	}

	if s.config.TokenAuth != nil {
		e.Use(middleware.TokenAuthWithConfig(middleware.TokenAuthConfig{
			Skipper: func(c echo.Context) bool {

				if c.Path() == "/" || c.Path() == "/status" || len(os.Getenv("CI")) > 0 {
					return true
				}

				return false
			},
			Validator: func(token string) bool {
				for _, a := range s.config.TokenAuth.Tokens {
					if a == token {
						return true
					}
				}
				return false
			},
		}))
	}

	if s.config.UseServerStarter {
		listeners, err := listener.ListenAll()
		if listeners == nil || err != nil {
			return err
		}
		e.Listener = listeners[0]
	}

	go func() {
		customServer := &http.Server{
			WriteTimeout: 1 * time.Minute,
		}
		if e.Listener == nil {
			p := strconv.Itoa(s.config.Port)
			customServer.Addr = ":" + p
		}

		// tls client authentication
		if s.config.TLS != nil {
			if _, err := os.Stat(s.config.TLS.CA); err == nil {
				ca, err := ioutil.ReadFile(s.config.TLS.CA)
				if err != nil {
					e.Logger.Fatal(err)
				}
				caPool := x509.NewCertPool()
				caPool.AppendCertsFromPEM(ca)

				tlsConfig := &tls.Config{
					ClientCAs:              caPool,
					SessionTicketsDisabled: true,
					ClientAuth:             tls.RequireAndVerifyClientCert,
				}

				tlsConfig.BuildNameToCertificate()
				customServer.TLSConfig = tlsConfig
			}
		}

		if s.config.TLS != nil && s.config.TLS.Cert != "" && s.config.TLS.Key != "" {
			if customServer.TLSConfig == nil {
				customServer.TLSConfig = new(tls.Config)
			}
			customServer.TLSConfig.Certificates = make([]tls.Certificate, 1)
			customServer.TLSConfig.Certificates[0], err = tls.LoadX509KeyPair(s.config.TLS.Cert, s.config.TLS.Key)
			if err != nil {
				e.Logger.Fatal(err)
			}

		}

		if err := e.StartServer(customServer); err != nil {
			e.Logger.Fatal("shutting down the server")
		}
	}()

	v1 := e.Group("/v1")
	UserEndpoints(v1)
	GroupEndpoints(v1)

	e.GET("/", func(c echo.Context) error {
		return c.String(http.StatusOK, "Hello! STNS!!1")
	})

	quit := make(chan os.Signal)
	signal.Notify(quit, os.Interrupt)
	<-quit
	ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
	defer cancel()
	if err := e.Shutdown(ctx); err != nil {
		return err
	}
	return nil
}
<commit_msg>check error when boot server<commit_after>package api

import (
	"context"
	"crypto/tls"
	"crypto/x509"
	"errors"
	"io/ioutil"
	"net/http"
	"os"
	"os/signal"
	"strconv"
	"time"

	"github.com/STNS/STNS/middleware"
	"github.com/STNS/STNS/model"
	"github.com/STNS/STNS/stns"
	"github.com/facebookgo/pidfile"
	"github.com/labstack/echo"
	emiddleware "github.com/labstack/echo/middleware"

	"github.com/labstack/gommon/log"

	"github.com/lestrrat/go-server-starter/listener"
)

type httpServer struct {
	baseServer
}

func newHTTPServer(confPath string) (*httpServer, error) {
	conf, err := stns.NewConfig(confPath)
	if err != nil {
		return nil, err
	}

	s := &httpServer{
		baseServer{config: &conf},
	}
	return s, nil
}

func status(c echo.Context) error {
	return c.String(http.StatusOK, "OK")
}

// Run サーバの起動
func (s *httpServer) Run() error {
	var backends model.Backends
	e := echo.New()
	if os.Getenv("STNS_LOG") != "" {
		f, err := os.OpenFile(os.Getenv("STNS_LOG"), os.O_RDWR|os.O_CREATE|os.O_APPEND, 0644)
		if err != nil {
			return errors.New("error opening file :" + err.Error())
		}
		e.Logger.SetOutput(f)
	} else {
		e.Logger.SetLevel(log.DEBUG)
	}
	e.GET("/status", status)

	if err := pidfile.Write(); err != nil {
		return err
	}

	defer func() {
		if err := os.Remove(pidfile.GetPidfilePath()); err != nil {
			e.Logger.Fatalf("Error removing %s: %s", pidfile.GetPidfilePath(), err)
		}
	}()

	b, err := model.NewBackendTomlFile(s.config.Users, s.config.Groups)
	if err != nil {
		return err
	}
	backends = append(backends, b)

	err = s.loadModules(e.Logger.(*log.Logger), &backends)
	if err != nil {
		return err
	}

	e.Use(middleware.Backends(backends))
	e.Use(middleware.AddHeader(backends))

	e.Use(emiddleware.Recover())
	e.Use(emiddleware.LoggerWithConfig(emiddleware.LoggerConfig{
		Format: `{"time":"${time_rfc3339_nano}","remote_ip":"${remote_ip}","host":"${host}",` +
			`"method":"${method}","uri":"${uri}","status":${status}}` + "\n",
	}))

	if s.config.BasicAuth != nil {
		e.Use(emiddleware.BasicAuthWithConfig(
			emiddleware.BasicAuthConfig{
				Validator: func(username, password string, c echo.Context) (bool, error) {
					if username == s.config.BasicAuth.User && password == s.config.BasicAuth.Password {
						return true, nil
					}
					return false, nil
				},
				Skipper: func(c echo.Context) bool {
					if c.Path() == "/" || c.Path() == "/status" || len(os.Getenv("CI")) > 0 {
						return true
					}
					return false
				},
			}))
	}

	if s.config.TokenAuth != nil {
		e.Use(middleware.TokenAuthWithConfig(middleware.TokenAuthConfig{
			Skipper: func(c echo.Context) bool {

				if c.Path() == "/" || c.Path() == "/status" || len(os.Getenv("CI")) > 0 {
					return true
				}

				return false
			},
			Validator: func(token string) bool {
				for _, a := range s.config.TokenAuth.Tokens {
					if a == token {
						return true
					}
				}
				return false
			},
		}))
	}

	if s.config.UseServerStarter {
		listeners, err := listener.ListenAll()
		if listeners == nil || err != nil {
			return err
		}
		e.Listener = listeners[0]
	}

	go func() {
		customServer := &http.Server{
			WriteTimeout: 1 * time.Minute,
		}
		if e.Listener == nil {
			p := strconv.Itoa(s.config.Port)
			customServer.Addr = ":" + p
		}

		// tls client authentication
		if s.config.TLS != nil {
			if _, err := os.Stat(s.config.TLS.CA); err == nil {
				ca, err := ioutil.ReadFile(s.config.TLS.CA)
				if err != nil {
					e.Logger.Fatal(err)
				}
				caPool := x509.NewCertPool()
				caPool.AppendCertsFromPEM(ca)

				tlsConfig := &tls.Config{
					ClientCAs:              caPool,
					SessionTicketsDisabled: true,
					ClientAuth:             tls.RequireAndVerifyClientCert,
				}

				tlsConfig.BuildNameToCertificate()
				customServer.TLSConfig = tlsConfig
			}
		}

		if s.config.TLS != nil && s.config.TLS.Cert != "" && s.config.TLS.Key != "" {
			if customServer.TLSConfig == nil {
				customServer.TLSConfig = new(tls.Config)
			}
			customServer.TLSConfig.Certificates = make([]tls.Certificate, 1)
			customServer.TLSConfig.Certificates[0], err = tls.LoadX509KeyPair(s.config.TLS.Cert, s.config.TLS.Key)
			if err != nil {
				e.Logger.Fatal(err)
			}

		}

		if err := e.StartServer(customServer); err != nil {
			e.Logger.Fatalf("shutting down the server: %s", err)
		}
	}()

	v1 := e.Group("/v1")
	UserEndpoints(v1)
	GroupEndpoints(v1)

	e.GET("/", func(c echo.Context) error {
		return c.String(http.StatusOK, "Hello! STNS!!1")
	})

	quit := make(chan os.Signal)
	signal.Notify(quit, os.Interrupt)
	<-quit
	ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
	defer cancel()
	if err := e.Shutdown(ctx); err != nil {
		return err
	}
	return nil
}
<|endoftext|>


<commit_before>package test

import (
	"sync"
	"testing"

	"github.com/neptulon/jsonrpc"
	"github.com/neptulon/neptulon/test"
)

type echoMsg struct {
	Message string `json:"message"`
}

func TestEcho(t *testing.T) {
	sh := test.NewTCPServerHelper(t).Start()
	defer sh.Close()

	js, err := jsonrpc.NewServer(sh.Server)
	if err != nil {
		t.Fatal(err)
	}

	rout, err := jsonrpc.NewRouter(&js.Middleware)
	if err != nil {
		t.Fatal(err)
	}

	rout.Request("echo", func(ctx *jsonrpc.ReqCtx) error {
		var msg echoMsg
		if err := ctx.Params(&msg); err != nil {
			t.Fatal(err)
		}
		ctx.Res = msg
		return ctx.Next()
	})

	var wg sync.WaitGroup

	ch := sh.GetTCPClientHelper().Connect()
	defer ch.Close()

	jc := jsonrpc.UseClient(ch.Client)
	jc.ResMiddleware(func(ctx *jsonrpc.ResCtx) error {
		defer wg.Done()
		var msg echoMsg
		if err := ctx.Result(&msg); err != nil {
			t.Fatal(err)
		}
		if msg.Message != "Hello!" {
			t.Fatalf("expected: %v got: %v", "Hello!", msg.Message)
		}
		return ctx.Next()
	})

	wg.Add(1)
	jc.SendRequest("echo", echoMsg{Message: "Hello!"})
	wg.Wait()
}
<commit_msg>add notes<commit_after>package test

import (
	"sync"
	"testing"

	"github.com/neptulon/jsonrpc"
	"github.com/neptulon/neptulon/test"
)

type echoMsg struct {
	Message string `json:"message"`
}

func TestEcho(t *testing.T) {
	sh := test.NewTCPServerHelper(t).Start()
	defer sh.Close()

	js, err := jsonrpc.NewServer(sh.Server)
	if err != nil {
		t.Fatal(err)
	}

	rout, err := jsonrpc.NewRouter(&js.Middleware)
	if err != nil {
		t.Fatal(err)
	}

	rout.Request("echo", func(ctx *jsonrpc.ReqCtx) error {
		var msg echoMsg
		if err := ctx.Params(&msg); err != nil {
			t.Fatal(err)
		}
		ctx.Res = msg
		return ctx.Next()
	})

	var wg sync.WaitGroup

	ch := sh.GetTCPClientHelper().Connect()
	defer ch.Close()

	// todo: separate echo middleware into /middleware package
	// todo2: use sender.go rather than this manual handling

	jc := jsonrpc.UseClient(ch.Client)
	jc.ResMiddleware(func(ctx *jsonrpc.ResCtx) error {
		defer wg.Done()
		var msg echoMsg
		if err := ctx.Result(&msg); err != nil {
			t.Fatal(err)
		}
		if msg.Message != "Hello!" {
			t.Fatalf("expected: %v got: %v", "Hello!", msg.Message)
		}
		return ctx.Next()
	})

	wg.Add(1)
	jc.SendRequest("echo", echoMsg{Message: "Hello!"})
	wg.Wait()
}
<|endoftext|>


<commit_before>package driver

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"reflect"
	"syscall"
	"testing"
	"time"

	"github.com/hashicorp/nomad/client/config"
	"github.com/hashicorp/nomad/client/driver/env"
	"github.com/hashicorp/nomad/nomad/structs"
	"github.com/hashicorp/nomad/testutil"

	ctestutils "github.com/hashicorp/nomad/client/testutil"
)

func TestExecDriver_Fingerprint(t *testing.T) {
	t.Parallel()
	ctestutils.ExecCompatible(t)
	driverCtx, _ := testDriverContexts(&structs.Task{Name: "foo"})
	d := NewExecDriver(driverCtx)
	node := &structs.Node{
		Attributes: map[string]string{
			"unique.cgroup.mountpoint": "/sys/fs/cgroup",
		},
	}
	apply, err := d.Fingerprint(&config.Config{}, node)
	if err != nil {
		t.Fatalf("err: %v", err)
	}
	if !apply {
		t.Fatalf("should apply")
	}
	if node.Attributes["driver.exec"] == "" {
		t.Fatalf("missing driver")
	}
}

func TestExecDriver_StartOpen_Wait(t *testing.T) {
	t.Parallel()
	ctestutils.ExecCompatible(t)
	task := &structs.Task{
		Name: "sleep",
		Config: map[string]interface{}{
			"command": "/bin/sleep",
			"args":    []string{"5"},
		},
		Resources: basicResources,
	}

	driverCtx, execCtx := testDriverContexts(task)
	defer execCtx.AllocDir.Destroy()
	d := NewExecDriver(driverCtx)

	handle, err := d.Start(execCtx, task)
	if err != nil {
		t.Fatalf("err: %v", err)
	}
	if handle == nil {
		t.Fatalf("missing handle")
	}

	// Attempt to open
	handle2, err := d.Open(execCtx, handle.ID())
	if err != nil {
		t.Fatalf("err: %v", err)
	}
	if handle2 == nil {
		t.Fatalf("missing handle")
	}

	handle.Kill()
	handle2.Kill()
}

func TestExecDriver_KillUserPid_OnPluginReconnectFailure(t *testing.T) {
	t.Parallel()
	ctestutils.ExecCompatible(t)
	task := &structs.Task{
		Name: "sleep",
		Config: map[string]interface{}{
			"command": "/bin/sleep",
			"args":    []string{"1000000"},
		},
		Resources: basicResources,
	}

	driverCtx, execCtx := testDriverContexts(task)
	defer execCtx.AllocDir.Destroy()
	d := NewExecDriver(driverCtx)

	handle, err := d.Start(execCtx, task)
	defer handle.Kill()
	if err != nil {
		t.Fatalf("err: %v", err)
	}
	if handle == nil {
		t.Fatalf("missing handle")
	}

	id := &execId{}
	if err := json.Unmarshal([]byte(handle.ID()), id); err != nil {
		t.Fatalf("Failed to parse handle '%s': %v", handle.ID(), err)
	}
	pluginPid := id.PluginConfig.Pid
	proc, err := os.FindProcess(pluginPid)
	if err != nil {
		t.Fatalf("can't find plugin pid: %v", pluginPid)
	}
	if err := proc.Kill(); err != nil {
		t.Fatalf("can't kill plugin pid: %v", err)
	}

	// Attempt to open
	handle2, err := d.Open(execCtx, handle.ID())
	if err == nil {
		t.Fatalf("expected error")
	}
	if handle2 != nil {
		handle2.Kill()
		t.Fatalf("expected handle2 to be nil")
	}
	// Test if the userpid is still present
	userProc, err := os.FindProcess(id.UserPid)

	err = userProc.Signal(syscall.Signal(0))

	if err == nil {
		t.Fatalf("expected user process to die")
	}
}

func TestExecDriver_Start_Wait(t *testing.T) {
	t.Parallel()
	ctestutils.ExecCompatible(t)
	task := &structs.Task{
		Name: "sleep",
		Config: map[string]interface{}{
			"command": "/bin/sleep",
			"args":    []string{"2"},
		},
		Resources: basicResources,
	}

	driverCtx, execCtx := testDriverContexts(task)
	defer execCtx.AllocDir.Destroy()
	d := NewExecDriver(driverCtx)

	handle, err := d.Start(execCtx, task)
	if err != nil {
		t.Fatalf("err: %v", err)
	}
	if handle == nil {
		t.Fatalf("missing handle")
	}

	// Update should be a no-op
	err = handle.Update(task)
	if err != nil {
		t.Fatalf("err: %v", err)
	}

	// Task should terminate quickly
	select {
	case res := <-handle.WaitCh():
		if !res.Successful() {
			t.Fatalf("err: %v", res)
		}
	case <-time.After(time.Duration(testutil.TestMultiplier()*5) * time.Second):
		t.Fatalf("timeout")
	}
}

func TestExecDriver_Start_Artifact_basic(t *testing.T) {
	t.Parallel()
	ctestutils.ExecCompatible(t)
	file := "hi_linux_amd64"
	checksum := "sha256:6f99b4c5184726e601ecb062500aeb9537862434dfe1898dbe5c68d9f50c179c"

	task := &structs.Task{
		Name: "sleep",
		Config: map[string]interface{}{
			"artifact_source": fmt.Sprintf("https://dl.dropboxusercontent.com/u/47675/jar_thing/%s?checksum=%s", file, checksum),
			"command":         file,
		},
		Resources: basicResources,
	}

	driverCtx, execCtx := testDriverContexts(task)
	defer execCtx.AllocDir.Destroy()
	d := NewExecDriver(driverCtx)

	handle, err := d.Start(execCtx, task)
	if err != nil {
		t.Fatalf("err: %v", err)
	}
	if handle == nil {
		t.Fatalf("missing handle")
	}

	// Update should be a no-op
	err = handle.Update(task)
	if err != nil {
		t.Fatalf("err: %v", err)
	}

	// Task should terminate quickly
	select {
	case res := <-handle.WaitCh():
		if !res.Successful() {
			t.Fatalf("err: %v", res)
		}
	case <-time.After(time.Duration(testutil.TestMultiplier()*5) * time.Second):
		t.Fatalf("timeout")
	}
}

func TestExecDriver_Start_Artifact_expanded(t *testing.T) {
	t.Parallel()
	ctestutils.ExecCompatible(t)
	file := "hi_linux_amd64"

	task := &structs.Task{
		Name: "sleep",
		Config: map[string]interface{}{
			"artifact_source": fmt.Sprintf("https://dl.dropboxusercontent.com/u/47675/jar_thing/%s", file),
			"command":         "/bin/bash",
			"args":            []string{"-c", fmt.Sprintf("/bin/sleep 1 && %s", file)},
		},
		Resources: basicResources,
	}

	driverCtx, execCtx := testDriverContexts(task)
	defer execCtx.AllocDir.Destroy()
	d := NewExecDriver(driverCtx)

	handle, err := d.Start(execCtx, task)
	if err != nil {
		t.Fatalf("err: %v", err)
	}
	if handle == nil {
		t.Fatalf("missing handle")
	}

	// Update should be a no-op
	err = handle.Update(task)
	if err != nil {
		t.Fatalf("err: %v", err)
	}

	// Task should terminate quickly
	select {
	case res := <-handle.WaitCh():
		if !res.Successful() {
			t.Fatalf("err: %v", res)
		}
	case <-time.After(time.Duration(testutil.TestMultiplier()*15) * time.Second):
		t.Fatalf("timeout")
	}
}
func TestExecDriver_Start_Wait_AllocDir(t *testing.T) {
	t.Parallel()
	ctestutils.ExecCompatible(t)

	exp := []byte{'w', 'i', 'n'}
	file := "output.txt"
	task := &structs.Task{
		Name: "sleep",
		Config: map[string]interface{}{
			"command": "/bin/bash",
			"args": []string{
				"-c",
				fmt.Sprintf(`sleep 1; echo -n %s > ${%s}/%s`, string(exp), env.AllocDir, file),
			},
		},
		Resources: basicResources,
	}

	driverCtx, execCtx := testDriverContexts(task)
	defer execCtx.AllocDir.Destroy()
	d := NewExecDriver(driverCtx)

	handle, err := d.Start(execCtx, task)
	if err != nil {
		t.Fatalf("err: %v", err)
	}
	if handle == nil {
		t.Fatalf("missing handle")
	}

	// Task should terminate quickly
	select {
	case res := <-handle.WaitCh():
		if !res.Successful() {
			t.Fatalf("err: %v", res)
		}
	case <-time.After(time.Duration(testutil.TestMultiplier()*5) * time.Second):
		t.Fatalf("timeout")
	}

	// Check that data was written to the shared alloc directory.
	outputFile := filepath.Join(execCtx.AllocDir.SharedDir, file)
	act, err := ioutil.ReadFile(outputFile)
	if err != nil {
		t.Fatalf("Couldn't read expected output: %v", err)
	}

	if !reflect.DeepEqual(act, exp) {
		t.Fatalf("Command outputted %v; want %v", act, exp)
	}
}

func TestExecDriver_Start_Kill_Wait(t *testing.T) {
	t.Parallel()
	ctestutils.ExecCompatible(t)
	task := &structs.Task{
		Name: "sleep",
		Config: map[string]interface{}{
			"command": "/bin/sleep",
			"args":    []string{"45"},
		},
		Resources: basicResources,
	}

	driverCtx, execCtx := testDriverContexts(task)
	defer execCtx.AllocDir.Destroy()
	d := NewExecDriver(driverCtx)

	handle, err := d.Start(execCtx, task)
	if err != nil {
		t.Fatalf("err: %v", err)
	}
	if handle == nil {
		t.Fatalf("missing handle")
	}

	go func() {
		time.Sleep(100 * time.Millisecond)
		err := handle.Kill()
		if err != nil {
			t.Fatalf("err: %v", err)
		}
	}()

	// Task should terminate quickly
	select {
	case res := <-handle.WaitCh():
		if res.Successful() {
			t.Fatal("should err")
		}
	case <-time.After(time.Duration(testutil.TestMultiplier()*10) * time.Second):
		t.Fatalf("timeout")
	}
}
<commit_msg>more time<commit_after>package driver

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"reflect"
	"syscall"
	"testing"
	"time"

	"github.com/hashicorp/nomad/client/config"
	"github.com/hashicorp/nomad/client/driver/env"
	"github.com/hashicorp/nomad/nomad/structs"
	"github.com/hashicorp/nomad/testutil"

	ctestutils "github.com/hashicorp/nomad/client/testutil"
)

func TestExecDriver_Fingerprint(t *testing.T) {
	t.Parallel()
	ctestutils.ExecCompatible(t)
	driverCtx, _ := testDriverContexts(&structs.Task{Name: "foo"})
	d := NewExecDriver(driverCtx)
	node := &structs.Node{
		Attributes: map[string]string{
			"unique.cgroup.mountpoint": "/sys/fs/cgroup",
		},
	}
	apply, err := d.Fingerprint(&config.Config{}, node)
	if err != nil {
		t.Fatalf("err: %v", err)
	}
	if !apply {
		t.Fatalf("should apply")
	}
	if node.Attributes["driver.exec"] == "" {
		t.Fatalf("missing driver")
	}
}

func TestExecDriver_StartOpen_Wait(t *testing.T) {
	t.Parallel()
	ctestutils.ExecCompatible(t)
	task := &structs.Task{
		Name: "sleep",
		Config: map[string]interface{}{
			"command": "/bin/sleep",
			"args":    []string{"5"},
		},
		Resources: basicResources,
	}

	driverCtx, execCtx := testDriverContexts(task)
	defer execCtx.AllocDir.Destroy()
	d := NewExecDriver(driverCtx)

	handle, err := d.Start(execCtx, task)
	if err != nil {
		t.Fatalf("err: %v", err)
	}
	if handle == nil {
		t.Fatalf("missing handle")
	}

	// Attempt to open
	handle2, err := d.Open(execCtx, handle.ID())
	if err != nil {
		t.Fatalf("err: %v", err)
	}
	if handle2 == nil {
		t.Fatalf("missing handle")
	}

	handle.Kill()
	handle2.Kill()
}

func TestExecDriver_KillUserPid_OnPluginReconnectFailure(t *testing.T) {
	t.Parallel()
	ctestutils.ExecCompatible(t)
	task := &structs.Task{
		Name: "sleep",
		Config: map[string]interface{}{
			"command": "/bin/sleep",
			"args":    []string{"1000000"},
		},
		Resources: basicResources,
	}

	driverCtx, execCtx := testDriverContexts(task)
	defer execCtx.AllocDir.Destroy()
	d := NewExecDriver(driverCtx)

	handle, err := d.Start(execCtx, task)
	defer handle.Kill()
	if err != nil {
		t.Fatalf("err: %v", err)
	}
	if handle == nil {
		t.Fatalf("missing handle")
	}

	id := &execId{}
	if err := json.Unmarshal([]byte(handle.ID()), id); err != nil {
		t.Fatalf("Failed to parse handle '%s': %v", handle.ID(), err)
	}
	pluginPid := id.PluginConfig.Pid
	proc, err := os.FindProcess(pluginPid)
	if err != nil {
		t.Fatalf("can't find plugin pid: %v", pluginPid)
	}
	if err := proc.Kill(); err != nil {
		t.Fatalf("can't kill plugin pid: %v", err)
	}

	// Attempt to open
	handle2, err := d.Open(execCtx, handle.ID())
	if err == nil {
		t.Fatalf("expected error")
	}
	if handle2 != nil {
		handle2.Kill()
		t.Fatalf("expected handle2 to be nil")
	}
	// Test if the userpid is still present
	userProc, err := os.FindProcess(id.UserPid)

	err = userProc.Signal(syscall.Signal(0))

	if err == nil {
		t.Fatalf("expected user process to die")
	}
}

func TestExecDriver_Start_Wait(t *testing.T) {
	t.Parallel()
	ctestutils.ExecCompatible(t)
	task := &structs.Task{
		Name: "sleep",
		Config: map[string]interface{}{
			"command": "/bin/sleep",
			"args":    []string{"2"},
		},
		Resources: basicResources,
	}

	driverCtx, execCtx := testDriverContexts(task)
	defer execCtx.AllocDir.Destroy()
	d := NewExecDriver(driverCtx)

	handle, err := d.Start(execCtx, task)
	if err != nil {
		t.Fatalf("err: %v", err)
	}
	if handle == nil {
		t.Fatalf("missing handle")
	}

	// Update should be a no-op
	err = handle.Update(task)
	if err != nil {
		t.Fatalf("err: %v", err)
	}

	// Task should terminate quickly
	select {
	case res := <-handle.WaitCh():
		if !res.Successful() {
			t.Fatalf("err: %v", res)
		}
	case <-time.After(time.Duration(testutil.TestMultiplier()*5) * time.Second):
		t.Fatalf("timeout")
	}
}

func TestExecDriver_Start_Artifact_basic(t *testing.T) {
	t.Parallel()
	ctestutils.ExecCompatible(t)
	file := "hi_linux_amd64"
	checksum := "sha256:6f99b4c5184726e601ecb062500aeb9537862434dfe1898dbe5c68d9f50c179c"

	task := &structs.Task{
		Name: "sleep",
		Config: map[string]interface{}{
			"artifact_source": fmt.Sprintf("https://dl.dropboxusercontent.com/u/47675/jar_thing/%s?checksum=%s", file, checksum),
			"command":         file,
		},
		Resources: basicResources,
	}

	driverCtx, execCtx := testDriverContexts(task)
	defer execCtx.AllocDir.Destroy()
	d := NewExecDriver(driverCtx)

	handle, err := d.Start(execCtx, task)
	if err != nil {
		t.Fatalf("err: %v", err)
	}
	if handle == nil {
		t.Fatalf("missing handle")
	}

	// Update should be a no-op
	err = handle.Update(task)
	if err != nil {
		t.Fatalf("err: %v", err)
	}

	// Task should terminate quickly
	select {
	case res := <-handle.WaitCh():
		if !res.Successful() {
			t.Fatalf("err: %v", res)
		}
	case <-time.After(time.Duration(testutil.TestMultiplier()*5) * time.Second):
		t.Fatalf("timeout")
	}
}

func TestExecDriver_Start_Artifact_expanded(t *testing.T) {
	t.Parallel()
	ctestutils.ExecCompatible(t)
	file := "hi_linux_amd64"

	task := &structs.Task{
		Name: "sleep",
		Config: map[string]interface{}{
			"artifact_source": fmt.Sprintf("https://dl.dropboxusercontent.com/u/47675/jar_thing/%s", file),
			"command":         "/bin/bash",
			"args":            []string{"-c", fmt.Sprintf("/bin/sleep 1 && %s", file)},
		},
		Resources: basicResources,
	}

	driverCtx, execCtx := testDriverContexts(task)
	defer execCtx.AllocDir.Destroy()
	d := NewExecDriver(driverCtx)

	handle, err := d.Start(execCtx, task)
	if err != nil {
		t.Fatalf("err: %v", err)
	}
	if handle == nil {
		t.Fatalf("missing handle")
	}

	// Update should be a no-op
	err = handle.Update(task)
	if err != nil {
		t.Fatalf("err: %v", err)
	}

	// Task should terminate quickly
	select {
	case res := <-handle.WaitCh():
		if !res.Successful() {
			t.Fatalf("err: %v", res)
		}
	case <-time.After(time.Duration(testutil.TestMultiplier()*15) * time.Second):
		t.Fatalf("timeout")
	}
}
func TestExecDriver_Start_Wait_AllocDir(t *testing.T) {
	t.Parallel()
	ctestutils.ExecCompatible(t)

	exp := []byte{'w', 'i', 'n'}
	file := "output.txt"
	task := &structs.Task{
		Name: "sleep",
		Config: map[string]interface{}{
			"command": "/bin/bash",
			"args": []string{
				"-c",
				fmt.Sprintf(`sleep 1; echo -n %s > ${%s}/%s`, string(exp), env.AllocDir, file),
			},
		},
		Resources: basicResources,
	}

	driverCtx, execCtx := testDriverContexts(task)
	defer execCtx.AllocDir.Destroy()
	d := NewExecDriver(driverCtx)

	handle, err := d.Start(execCtx, task)
	if err != nil {
		t.Fatalf("err: %v", err)
	}
	if handle == nil {
		t.Fatalf("missing handle")
	}

	// Task should terminate quickly
	select {
	case res := <-handle.WaitCh():
		if !res.Successful() {
			t.Fatalf("err: %v", res)
		}
	case <-time.After(time.Duration(testutil.TestMultiplier()*5) * time.Second):
		t.Fatalf("timeout")
	}

	// Check that data was written to the shared alloc directory.
	outputFile := filepath.Join(execCtx.AllocDir.SharedDir, file)
	act, err := ioutil.ReadFile(outputFile)
	if err != nil {
		t.Fatalf("Couldn't read expected output: %v", err)
	}

	if !reflect.DeepEqual(act, exp) {
		t.Fatalf("Command outputted %v; want %v", act, exp)
	}
}

func TestExecDriver_Start_Kill_Wait(t *testing.T) {
	t.Parallel()
	ctestutils.ExecCompatible(t)
	task := &structs.Task{
		Name: "sleep",
		Config: map[string]interface{}{
			"command": "/bin/sleep",
			"args":    []string{"100"},
		},
		Resources:   basicResources,
		KillTimeout: 10 * time.Second,
	}

	driverCtx, execCtx := testDriverContexts(task)
	defer execCtx.AllocDir.Destroy()
	d := NewExecDriver(driverCtx)

	handle, err := d.Start(execCtx, task)
	if err != nil {
		t.Fatalf("err: %v", err)
	}
	if handle == nil {
		t.Fatalf("missing handle")
	}

	go func() {
		time.Sleep(100 * time.Millisecond)
		err := handle.Kill()
		if err != nil {
			t.Fatalf("err: %v", err)
		}
	}()

	// Task should terminate quickly
	select {
	case res := <-handle.WaitCh():
		if res.Successful() {
			t.Fatal("should err")
		}
	case <-time.After(time.Duration(testutil.TestMultiplier()*10) * time.Second):
		t.Fatalf("timeout")
	}
}
<|endoftext|>


<commit_before>package weibo

import (
	_ "fmt"
)

// StatusesService handles communication with the Status related
// methods of the Weibo API.
//
// Weibo API docs: http://open.weibo.com/wiki/%E5%BE%AE%E5%8D%9AAPI
type StatusesService struct {
	client *Client
}

// Status represents a Weibo's status.
type Status struct {
	CreatedAt      *string  `json:"created_at,omitempty"`
	ID             *int64   `json:"id,omitempty"`
	MID            *string  `json:"mid,omitempty"`
	IDStr          *string  `json:"idstr,omitempty"`
	Text           *string  `json:"text,omitempty"`
	Source         *string  `json:"source,omitempty"`
	Favorited      *bool    `json:"favorited,omitempty"`
	Truncated      *bool    `json:"truncated,omitempty"`
	RepostsCount   *int     `json:"reposts_count,omitempty"`
	CommentsCount  *int     `json:"comments_count,omitempty"`
	AttitudesCount *int     `json:"attitudes_count,omitemtpy"`
	Visible        *Visible `json:"visible,omitempty"`
}

// Visible represents visible object of a Weibo status.
type Visible struct {
	VType  *int `json:"type,omitempty"`
	ListID *int `json:"list_id,omitempty"`
}

// Timeline represents Weibo statuses set.
type Timeline struct {
	Statuses       []Status `json:"statuses,omitempty"`
	TotalNumber    *int     `json:"total_number,omitempty"`
	PreviousCursor *int     `json:"previous_cursor,omitempty"`
	NextCursor     *int     `json:"next_cursor,omitempty"`
}

// StatusListOptions specifies the optional parameters to the
// StatusService.UserTimeline method.
type StatusListOptions struct {
	UID        string `url:"uid,omitempty"`
	ScreenName string `url:"screen_name,omitempty"`
	SinceID    string `url:"since_id,omitempty"`
	MaxID      string `url:"max_id,omitempty"`
	ListOptions
}

// UpdateOptions specifies the optional parameters to the
// StatusService.Update method.
type UpdateOptions struct {
	Status      *string  `json:status`
	Visible     *Visible `json:visible,omitempty`
	ListID      *int     `json:list_id,omitempty`
	Lat         *float64 `json:lat,omitempty`
	Long        *float64 `json:long,omitempty`
	Annotations *string  `json:annotations,omitempty`
	RealIP      *string  `json:rip,omitempty`
}

// Timeline for a user. Passing the empty string will return
// timeline for the authenticated user.
//
// Weibo API docs: http://open.weibo.com/wiki/2/statuses/user_timeline
func (s *StatusesService) UserTimeline(opt *StatusListOptions) (*Timeline, *Response, error) {
	u, err := addOptions("statuses/user_timeline.json", opt)
	if err != nil {
		return nil, nil, err
	}

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

	timeline := &Timeline{}
	resp, err := s.client.Do(req, timeline)
	if err != nil {
		return nil, resp, err
	}

	return timeline, resp, err
}

func (s *StatusesService) Update(opt *UpdateOptions) (*Status, *Response, error) {
	u := "statuses/update.json"

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

	status := &Status{}
	resp, err := s.client.Do(req, status)
	if err != nil {
		return nil, resp, err
	}

	return status, resp, err
}
<commit_msg>Fix UpdateOptions json tag.<commit_after>package weibo

import (
	_ "fmt"
)

// StatusesService handles communication with the Status related
// methods of the Weibo API.
//
// Weibo API docs: http://open.weibo.com/wiki/%E5%BE%AE%E5%8D%9AAPI
type StatusesService struct {
	client *Client
}

// Status represents a Weibo's status.
type Status struct {
	CreatedAt      *string  `json:"created_at,omitempty"`
	ID             *int64   `json:"id,omitempty"`
	MID            *string  `json:"mid,omitempty"`
	IDStr          *string  `json:"idstr,omitempty"`
	Text           *string  `json:"text,omitempty"`
	Source         *string  `json:"source,omitempty"`
	Favorited      *bool    `json:"favorited,omitempty"`
	Truncated      *bool    `json:"truncated,omitempty"`
	RepostsCount   *int     `json:"reposts_count,omitempty"`
	CommentsCount  *int     `json:"comments_count,omitempty"`
	AttitudesCount *int     `json:"attitudes_count,omitemtpy"`
	Visible        *Visible `json:"visible,omitempty"`
}

// Visible represents visible object of a Weibo status.
type Visible struct {
	VType  *int `json:"type,omitempty"`
	ListID *int `json:"list_id,omitempty"`
}

// Timeline represents Weibo statuses set.
type Timeline struct {
	Statuses       []Status `json:"statuses,omitempty"`
	TotalNumber    *int     `json:"total_number,omitempty"`
	PreviousCursor *int     `json:"previous_cursor,omitempty"`
	NextCursor     *int     `json:"next_cursor,omitempty"`
}

// StatusListOptions specifies the optional parameters to the
// StatusService.UserTimeline method.
type StatusListOptions struct {
	UID        string `url:"uid,omitempty"`
	ScreenName string `url:"screen_name,omitempty"`
	SinceID    string `url:"since_id,omitempty"`
	MaxID      string `url:"max_id,omitempty"`
	ListOptions
}

// UpdateOptions specifies the optional parameters to the
// StatusService.Update method.
type UpdateOptions struct {
	Status      *string  `json:"status"`
	Visible     *int     `json:"visible,omitempty"`
	ListID      *int     `json:"list_id,omitempty"`
	Lat         *float64 `json:"lat,omitempty"`
	Long        *float64 `json:"long,omitempty"`
	Annotations *string  `json:"annotations,omitempty"`
	RealIP      *string  `json:"rip,omitempty"`
}

// Timeline for a user. Passing the empty string will return
// timeline for the authenticated user.
//
// Weibo API docs: http://open.weibo.com/wiki/2/statuses/user_timeline
func (s *StatusesService) UserTimeline(opt *StatusListOptions) (*Timeline, *Response, error) {
	u, err := addOptions("statuses/user_timeline.json", opt)
	if err != nil {
		return nil, nil, err
	}

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

	timeline := &Timeline{}
	resp, err := s.client.Do(req, timeline)
	if err != nil {
		return nil, resp, err
	}

	return timeline, resp, err
}

func (s *StatusesService) Update(opt *UpdateOptions) (*Status, *Response, error) {
	u := "statuses/update.json"

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

	status := &Status{}
	resp, err := s.client.Do(req, status)
	if err != nil {
		return nil, resp, err
	}

	return status, resp, err
}
<|endoftext|>


<commit_before>// Copyright 2017 Sevki <s@sevki.org>. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package project // import "bldy.build/build/project"

import (
	"flag"
	"fmt"
	"log"
	"os"
	"path/filepath"
	"runtime"
	"strings"

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

var (
	file ini.File

	pp = ""

	copyToRoot = flag.Bool("r", false, "set root of the project as build out. should not be used with watch")
)

func init() {
	wd, _ := os.Getwd()
	pp = GetGitDir(wd)
	var err error
	if file, err = ini.LoadFile(filepath.Join(Root(), "bldy.cfg")); err == nil {
		if err != nil {
			log.Fatalf("error: %v", err)
		}
	}
}
func Root() (ProjectPath string) {
	return pp
}
func RelPPath(p string) string {
	rel, _ := filepath.Rel(Root(), p)
	return rel
}

func BuildOut() string {
	if *copyToRoot  {
		return Root()
	}

	if Getenv("BUILD_OUT") != "" {
		return Getenv("BUILD_OUT")
	} else {
		return filepath.Join(
			Root(),
			"build_out",
		)
	}
}

func GetGitDir(p string) string {
	dirs := strings.Split(p, "/")
	for i := len(dirs) - 1; i > 0; i-- {
		try := fmt.Sprintf("/%s/.git", filepath.Join(dirs[0:i+1]...))
		if _, err := os.Lstat(try); os.IsNotExist(err) {
			continue
		} else if err != nil {
			log.Fatal(err)
		}
		pr, _ := filepath.Split(try)
		return pr
	}
	return ""
}

// Getenv returns the envinroment variable. It looks for the envinroment
// variable in the following order. It checks if the current shell session has
// an envinroment variable, checks if it's set in the OS specific section in
// the .build file, and checks it for common in the .build config file.
func Getenv(s string) string {
	if os.Getenv(s) != "" {
		return os.Getenv(s)
	} else if val, exists := file.Get(runtime.GOOS, s); exists {
		return val
	} else if val, exists := file.Get("", s); exists {
		return val
	} else {
		return ""
	}
}
<commit_msg>project: remove the "should not be used with watch" statement<commit_after>// Copyright 2017 Sevki <s@sevki.org>. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package project // import "bldy.build/build/project"

import (
	"flag"
	"fmt"
	"log"
	"os"
	"path/filepath"
	"runtime"
	"strings"

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

var (
	file ini.File

	pp = ""

	copyToRoot = flag.Bool("r", false, "set root of the project as build out")
)

func init() {
	wd, _ := os.Getwd()
	pp = GetGitDir(wd)
	var err error
	if file, err = ini.LoadFile(filepath.Join(Root(), "bldy.cfg")); err == nil {
		if err != nil {
			log.Fatalf("error: %v", err)
		}
	}
}
func Root() (ProjectPath string) {
	return pp
}
func RelPPath(p string) string {
	rel, _ := filepath.Rel(Root(), p)
	return rel
}

func BuildOut() string {
	if *copyToRoot {
		return Root()
	}

	if Getenv("BUILD_OUT") != "" {
		return Getenv("BUILD_OUT")
	} else {
		return filepath.Join(
			Root(),
			"build_out",
		)
	}
}

func GetGitDir(p string) string {
	dirs := strings.Split(p, "/")
	for i := len(dirs) - 1; i > 0; i-- {
		try := fmt.Sprintf("/%s/.git", filepath.Join(dirs[0:i+1]...))
		if _, err := os.Lstat(try); os.IsNotExist(err) {
			continue
		} else if err != nil {
			log.Fatal(err)
		}
		pr, _ := filepath.Split(try)
		return pr
	}
	return ""
}

// Getenv returns the envinroment variable. It looks for the envinroment
// variable in the following order. It checks if the current shell session has
// an envinroment variable, checks if it's set in the OS specific section in
// the .build file, and checks it for common in the .build config file.
func Getenv(s string) string {
	if os.Getenv(s) != "" {
		return os.Getenv(s)
	} else if val, exists := file.Get(runtime.GOOS, s); exists {
		return val
	} else if val, exists := file.Get("", s); exists {
		return val
	} else {
		return ""
	}
}
<|endoftext|>


<commit_before>package project

import (
	"github.com/hashicorp/hcl"
	"github.com/juhovuori/builder/fetcher"
	uuid "github.com/satori/go.uuid"
)

// VCS defines a version control system type
type VCS string

const (
	// GIT is a version control system type
	GIT VCS = "git"
)

// Project represents a single managed project
type Project interface {
	Manager
	Attributes
}

// Manager represents the manager of a single project
type Manager interface {
	URL() string
	VCS() VCS
	Config() string
	ID() string
	Err() error
}

// Attributes represents the configuration of a single project
type Attributes interface {
	Name() string
	Description() string
	Script() string
}

// defaultProject is the main implementation of Project
type defaultProject struct {
	Purl         string `json:"url"`
	Pid          string `json:"id"`
	Perr         error  `json:"error"`
	Pname        string `json:"name" hcl:"name"`
	Pdescription string `json:"description" hcl:"description"`
	Pscript      string `json:"script" hcl:"script"`
}

func (p *defaultProject) URL() string {
	return p.Purl
}

func (p *defaultProject) Config() string {
	return "project.hcl"
}

func (p *defaultProject) VCS() VCS {
	return GIT
}

func (p *defaultProject) ID() string {
	return p.Pid
}

func (p *defaultProject) Err() error {
	return p.Perr
}

func (p *defaultProject) Name() string {
	return p.Pname
}

func (p *defaultProject) Description() string {
	return p.Pdescription
}

func (p *defaultProject) Script() string {
	return p.Pscript
}

func fetchConfig(filename string) (string, error) {
	bytes, err := fetcher.Fetch(filename)
	return string(bytes), err
}

func newProject(config string, URL string, err error) (*defaultProject, error) {
	var p defaultProject
	err2 := hcl.Decode(&p, config)
	if err == nil {
		err = err2
	}
	p.Purl = URL
	p.Pid = uuid.NewV5(namespace, URL).String()
	p.Perr = err
	return &p, err
}

// NewProject creates a new project
func NewProject(URL string) (Project, error) {
	config, err := fetchConfig(URL)
	return newProject(config, URL, err)
}

// New creates a new project from configuration string
func New(config string) (Project, error) {
	return newProject(config, "", nil)
}

// namespace is a global namespace for project ID generation.
var namespace, _ = uuid.FromString("a7cf1c8b-7b5e-4216-85d3-877e16845ebb")
<commit_msg>project VCS => Repository<commit_after>package project

import (
	"github.com/hashicorp/hcl"
	"github.com/juhovuori/builder/fetcher"
	"github.com/juhovuori/builder/repository"
	uuid "github.com/satori/go.uuid"
)

// Project represents a single managed project
type Project interface {
	Manager
	Attributes
}

// Manager represents the manager of a single project
type Manager interface {
	URL() string
	Repository() repository.Repository
	Config() string
	ID() string
	Err() error
}

// Attributes represents the configuration of a single project
type Attributes interface {
	Name() string
	Description() string
	Script() string
}

// defaultProject is the main implementation of Project
type defaultProject struct {
	Purl         string `json:"url"`
	Pid          string `json:"id"`
	Perr         error  `json:"error"`
	Pname        string `json:"name" hcl:"name"`
	Pdescription string `json:"description" hcl:"description"`
	Pscript      string `json:"script" hcl:"script"`
}

func (p *defaultProject) URL() string {
	return p.Purl
}

func (p *defaultProject) Config() string {
	return "project.hcl"
}

func (p *defaultProject) Repository() repository.Repository {
	return nil
}

func (p *defaultProject) ID() string {
	return p.Pid
}

func (p *defaultProject) Err() error {
	return p.Perr
}

func (p *defaultProject) Name() string {
	return p.Pname
}

func (p *defaultProject) Description() string {
	return p.Pdescription
}

func (p *defaultProject) Script() string {
	return p.Pscript
}

func fetchConfig(filename string) (string, error) {
	bytes, err := fetcher.Fetch(filename)
	return string(bytes), err
}

func newProject(config string, URL string, err error) (*defaultProject, error) {
	var p defaultProject
	err2 := hcl.Decode(&p, config)
	if err == nil {
		err = err2
	}
	p.Purl = URL
	p.Pid = uuid.NewV5(namespace, URL).String()
	p.Perr = err
	return &p, err
}

// NewProject creates a new project
func NewProject(URL string) (Project, error) {
	config, err := fetchConfig(URL)
	return newProject(config, URL, err)
}

// New creates a new project from configuration string
func New(config string) (Project, error) {
	return newProject(config, "", nil)
}

// namespace is a global namespace for project ID generation.
var namespace, _ = uuid.FromString("a7cf1c8b-7b5e-4216-85d3-877e16845ebb")
<|endoftext|>


<commit_before>//
// Copyright 2017, Sander van Harmelen
//
// 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 gitlab

import (
	"fmt"
)

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

// ListProjectMembersOptions represents the available ListProjectMembers() and
// ListAllProjectMembers() options.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/members.html#list-all-members-of-a-group-or-project
type ListProjectMembersOptions struct {
	ListOptions
	Query *string `url:"query,omitempty" json:"query,omitempty"`
}

// ListProjectMembers gets a list of a project's team members viewable by the
// authenticated user. Returns only direct members and not inherited members
// through ancestors groups.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/members.html#list-all-members-of-a-group-or-project
func (s *ProjectMembersService) ListProjectMembers(pid interface{}, opt *ListProjectMembersOptions, options ...RequestOptionFunc) ([]*ProjectMember, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("projects/%s/members", pathEscape(project))

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

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

	return pm, resp, err
}

// ListAllProjectMembers gets a list of a project's team members viewable by the
// authenticated user. Returns a list including inherited members through
// ancestor groups.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/members.html#list-all-members-of-a-group-or-project-including-inherited-members
func (s *ProjectMembersService) ListAllProjectMembers(pid interface{}, opt *ListProjectMembersOptions, options ...RequestOptionFunc) ([]*ProjectMember, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("projects/%s/members/all", pathEscape(project))

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

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

	return pm, resp, err
}

// GetProjectMember gets a project team member.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/members.html#get-a-member-of-a-group-or-project
func (s *ProjectMembersService) GetProjectMember(pid interface{}, user int, options ...RequestOptionFunc) (*ProjectMember, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("projects/%s/members/%d", pathEscape(project), user)

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

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

	return pm, resp, err
}

// GetInheritedProjectMember gets a project team member, including inherited
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/members.html#get-a-member-of-a-group-or-project-including-inherited-members
func (s *ProjectMembersService) GetInheritedProjectMember(pid interface{}, user int, options ...RequestOptionFunc) (*ProjectMember, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("projects/%s/members/all/%d", pathEscape(project), user)

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

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

	return pm, resp, err
}

// AddProjectMemberOptions represents the available AddProjectMember() options.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/members.html#add-a-member-to-a-group-or-project
type AddProjectMemberOptions struct {
	UserID      *int              `url:"user_id,omitempty" json:"user_id,omitempty"`
	AccessLevel *AccessLevelValue `url:"access_level,omitempty" json:"access_level,omitempty"`
	ExpiresAt   *string           `url:"expires_at,omitempty" json:"expires_at"`
}

// AddProjectMember adds a user to a project team. This is an idempotent
// method and can be called multiple times with the same parameters. Adding
// team membership to a user that is already a member does not affect the
// existing membership.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/members.html#add-a-member-to-a-group-or-project
func (s *ProjectMembersService) AddProjectMember(pid interface{}, opt *AddProjectMemberOptions, options ...RequestOptionFunc) (*ProjectMember, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("projects/%s/members", pathEscape(project))

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

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

	return pm, resp, err
}

// EditProjectMemberOptions represents the available EditProjectMember() options.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/members.html#edit-a-member-of-a-group-or-project
type EditProjectMemberOptions struct {
	AccessLevel *AccessLevelValue `url:"access_level,omitempty" json:"access_level,omitempty"`
	ExpiresAt   *string           `url:"expires_at,omitempty" json:"expires_at"`
}

// EditProjectMember updates a project team member to a specified access level..
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/members.html#edit-a-member-of-a-group-or-project
func (s *ProjectMembersService) EditProjectMember(pid interface{}, user int, opt *EditProjectMemberOptions, options ...RequestOptionFunc) (*ProjectMember, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("projects/%s/members/%d", pathEscape(project), user)

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

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

	return pm, resp, err
}

// DeleteProjectMember removes a user from a project team.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/members.html#remove-a-member-from-a-group-or-project
func (s *ProjectMembersService) DeleteProjectMember(pid interface{}, user int, options ...RequestOptionFunc) (*Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, err
	}
	u := fmt.Sprintf("projects/%s/members/%d", pathEscape(project), user)

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

	return s.client.Do(req, nil)
}
<commit_msg>AddProjectMemberOptions.UserId shoud be *string<commit_after>//
// Copyright 2017, Sander van Harmelen
//
// 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 gitlab

import (
	"fmt"
)

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

// ListProjectMembersOptions represents the available ListProjectMembers() and
// ListAllProjectMembers() options.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/members.html#list-all-members-of-a-group-or-project
type ListProjectMembersOptions struct {
	ListOptions
	Query *string `url:"query,omitempty" json:"query,omitempty"`
}

// ListProjectMembers gets a list of a project's team members viewable by the
// authenticated user. Returns only direct members and not inherited members
// through ancestors groups.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/members.html#list-all-members-of-a-group-or-project
func (s *ProjectMembersService) ListProjectMembers(pid interface{}, opt *ListProjectMembersOptions, options ...RequestOptionFunc) ([]*ProjectMember, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("projects/%s/members", pathEscape(project))

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

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

	return pm, resp, err
}

// ListAllProjectMembers gets a list of a project's team members viewable by the
// authenticated user. Returns a list including inherited members through
// ancestor groups.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/members.html#list-all-members-of-a-group-or-project-including-inherited-members
func (s *ProjectMembersService) ListAllProjectMembers(pid interface{}, opt *ListProjectMembersOptions, options ...RequestOptionFunc) ([]*ProjectMember, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("projects/%s/members/all", pathEscape(project))

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

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

	return pm, resp, err
}

// GetProjectMember gets a project team member.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/members.html#get-a-member-of-a-group-or-project
func (s *ProjectMembersService) GetProjectMember(pid interface{}, user int, options ...RequestOptionFunc) (*ProjectMember, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("projects/%s/members/%d", pathEscape(project), user)

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

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

	return pm, resp, err
}

// GetInheritedProjectMember gets a project team member, including inherited
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/members.html#get-a-member-of-a-group-or-project-including-inherited-members
func (s *ProjectMembersService) GetInheritedProjectMember(pid interface{}, user int, options ...RequestOptionFunc) (*ProjectMember, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("projects/%s/members/all/%d", pathEscape(project), user)

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

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

	return pm, resp, err
}

// AddProjectMemberOptions represents the available AddProjectMember() options.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/members.html#add-a-member-to-a-group-or-project
type AddProjectMemberOptions struct {
	UserID      *string           `url:"user_id,omitempty" json:"user_id,omitempty"`
	AccessLevel *AccessLevelValue `url:"access_level,omitempty" json:"access_level,omitempty"`
	ExpiresAt   *string           `url:"expires_at,omitempty" json:"expires_at"`
}

// AddProjectMember adds a user to a project team. This is an idempotent
// method and can be called multiple times with the same parameters. Adding
// team membership to a user that is already a member does not affect the
// existing membership.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/members.html#add-a-member-to-a-group-or-project
func (s *ProjectMembersService) AddProjectMember(pid interface{}, opt *AddProjectMemberOptions, options ...RequestOptionFunc) (*ProjectMember, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("projects/%s/members", pathEscape(project))

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

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

	return pm, resp, err
}

// EditProjectMemberOptions represents the available EditProjectMember() options.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/members.html#edit-a-member-of-a-group-or-project
type EditProjectMemberOptions struct {
	AccessLevel *AccessLevelValue `url:"access_level,omitempty" json:"access_level,omitempty"`
	ExpiresAt   *string           `url:"expires_at,omitempty" json:"expires_at"`
}

// EditProjectMember updates a project team member to a specified access level..
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/members.html#edit-a-member-of-a-group-or-project
func (s *ProjectMembersService) EditProjectMember(pid interface{}, user int, opt *EditProjectMemberOptions, options ...RequestOptionFunc) (*ProjectMember, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("projects/%s/members/%d", pathEscape(project), user)

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

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

	return pm, resp, err
}

// DeleteProjectMember removes a user from a project team.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/members.html#remove-a-member-from-a-group-or-project
func (s *ProjectMembersService) DeleteProjectMember(pid interface{}, user int, options ...RequestOptionFunc) (*Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, err
	}
	u := fmt.Sprintf("projects/%s/members/%d", pathEscape(project), user)

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

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


<commit_before>// Copyright 2014 SteelSeries ApS.  All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// This package implements a basic LISP interpretor for embedding in a go program for scripting.
// This file contains the built-in primitive functions.

package golisp

import (
	"errors"
	"fmt"
)

func RegisterSpecialFormPrimitives() {
	MakePrimitiveFunction("cond", -1, CondImpl)
	MakePrimitiveFunction("case", -1, CaseImpl)
	MakePrimitiveFunction("if", -1, IfImpl)
	MakePrimitiveFunction("lambda", -1, LambdaImpl)
	MakePrimitiveFunction("define", -1, DefineImpl)
	MakePrimitiveFunction("defmacro", -1, DefmacroImpl)
	MakePrimitiveFunction("let", -1, LetImpl)
	MakePrimitiveFunction("begin", -1, BeginImpl)
	MakePrimitiveFunction("do", -1, DoImpl)
	MakePrimitiveFunction("apply", -1, ApplyImpl)
	MakePrimitiveFunction("eval", 1, EvalImpl)
	MakePrimitiveFunction("->", -1, ChainImpl)
	MakePrimitiveFunction("=>", -1, TapImpl)
}

func CondImpl(args *Data, env *SymbolTableFrame) (result *Data, err error) {
	var condition *Data
	for c := args; NotNilP(c); c = Cdr(c) {
		clause := Car(c)
		if !PairP(clause) {
			err = errors.New("Cond expect a sequence of clauses that are lists")
			return
		}
		condition, err = Eval(Car(clause), env)
		if err != nil {
			return
		}
		if BooleanValue(condition) || StringValue(Car(clause)) == "else" {
			for e := Cdr(clause); NotNilP(e); e = Cdr(e) {
				result, err = Eval(Car(e), env)
				if err != nil {
					return
				}
			}
			return
		}
	}
	return
}

func evalList(l *Data, env *SymbolTableFrame) (result *Data, err error) {
	for sexpr := l; NotNilP(sexpr); sexpr = Cdr(sexpr) {
		result, err = Eval(Car(sexpr), env)
		if err != nil {
			return
		}
	}
	return
}

func CaseImpl(args *Data, env *SymbolTableFrame) (result *Data, err error) {
	var keyValue *Data

	keyValue, err = Eval(Car(args), env)
	if err != nil {
		return
	}

	for clauseCell := Cdr(args); NotNilP(clauseCell); clauseCell = Cdr(clauseCell) {
		clause := Car(clauseCell)
		if !PairP(clause) {
			err = errors.New("Case requires non-atomic clauses")
			return
		}
		if ListP(Car(clause)) {
			for v := Car(clause); NotNilP(v); v = Cdr(v) {
				if IsEqual(Car(v), keyValue) {
					return evalList(Cdr(clause), env)
				}
			}
		} else if IsEqual(Car(clause), SymbolWithName("else")) {
			return evalList(Cdr(clause), env)
		}
	}

	return
}

func IfImpl(args *Data, env *SymbolTableFrame) (result *Data, err error) {
	if Length(args) < 2 || Length(args) > 3 {
		err = errors.New(fmt.Sprintf("IF requires 2 or 3 arguments. Received %d.", Length(args)))
		return
	}

	c, err := Eval(Car(args), env)
	if err != nil {
		return
	}
	condition := BooleanValue(c)
	thenClause := Second(args)
	elseClause := Third(args)

	if condition {
		return Eval(thenClause, env)
	} else {
		return Eval(elseClause, env)
	}
}

func LambdaImpl(args *Data, env *SymbolTableFrame) (result *Data, err error) {
	params := Car(args)
	body := Cdr(args)
	return FunctionWithNameParamsBodyAndParent("anonymous", params, body, env), nil
}

func DefineImpl(args *Data, env *SymbolTableFrame) (result *Data, err error) {
	var value *Data
	thing := Car(args)
	if SymbolP(thing) {
		value, err = Eval(Cadr(args), env)
		if err != nil {
			return
		}
	} else if PairP(thing) {
		name := Car(thing)
		params := Cdr(thing)
		thing = name
		if !SymbolP(name) {
			err = errors.New("Function name has to be a symbol")
			return
		}
		body := Cdr(args)
		value = FunctionWithNameParamsBodyAndParent(StringValue(name), params, body, env)
	} else {
		err = errors.New("Invalid definition")
		return
	}
	env.BindLocallyTo(thing, value)
	return value, nil
}

func DefmacroImpl(args *Data, env *SymbolTableFrame) (result *Data, err error) {
	var value *Data
	thing := Car(args)
	if PairP(thing) {
		name := Car(thing)
		params := Cdr(thing)
		thing = name
		if !SymbolP(name) {
			err = errors.New("Macro name has to be a symbol")
			return
		}
		body := Cadr(args)
		value = MacroWithNameParamsBodyAndParent(StringValue(name), params, body, env)
	} else {
		err = errors.New("Invalid macro definition")
		return
	}
	env.BindLocallyTo(thing, value)
	return value, nil
}

func bindLetLocals(bindingForms *Data, env *SymbolTableFrame) (err error) {
	var name *Data
	var value *Data

	for cell := bindingForms; NotNilP(cell); cell = Cdr(cell) {
		bindingPair := Car(cell)
		if !PairP(bindingPair) {
			err = errors.New("Let requires a list of bindings (with are pairs) as it's first argument")
			return
		}
		name = Car(bindingPair)
		if !SymbolP(name) {
			err = errors.New("First part of a let binding pair must be a symbol")
		}
		value, err = Eval(Cadr(bindingPair), env)
		if err != nil {
			return
		}
		env.BindLocallyTo(name, value)
	}
	return
}

func LetImpl(args *Data, env *SymbolTableFrame) (result *Data, err error) {
	if Length(args) < 1 {
		err = errors.New("Let requires at least a list of bindings")
		return
	}

	if !PairP(Car(args)) {
		err = errors.New("Let requires a list of bindings as it's first argument")
		return
	}

	localFrame := NewSymbolTableFrameBelow(env)
	bindLetLocals(Car(args), localFrame)

	for cell := Cdr(args); NotNilP(cell); cell = Cdr(cell) {
		sexpr := Car(cell)
		result, err = Eval(sexpr, localFrame)
		if err != nil {
			return
		}
	}

	return
}

func BeginImpl(args *Data, env *SymbolTableFrame) (result *Data, err error) {
	for cell := args; NotNilP(cell); cell = Cdr(cell) {
		sexpr := Car(cell)
		result, err = Eval(sexpr, env)
		if err != nil {
			return
		}
	}
	return
}

func rebindDoLocals(bindingForms *Data, env *SymbolTableFrame) (err error) {
	var name *Data
	var value *Data

	for cell := bindingForms; NotNilP(cell); cell = Cdr(cell) {
		bindingTuple := Car(cell)
		name = First(bindingTuple)
		if NotNilP(Third(bindingTuple)) {
			value, err = Eval(Third(bindingTuple), env)
			if err != nil {
				return
			}
			env.BindLocallyTo(name, value)
		}
	}
	return
}

func DoImpl(args *Data, env *SymbolTableFrame) (result *Data, err error) {
	if Length(args) < 2 {
		err = errors.New("Do requires at least a list of bindings and a test clause")
		return
	}

	bindings := Car(args)
	if !PairP(bindings) {
		err = errors.New("Do requires a list of bindings as it's first argument")
		return
	}

	testClause := Cadr(args)
	if !PairP(testClause) {
		err = errors.New("Do requires a list as it's second argument")
		return
	}

	localFrame := NewSymbolTableFrameBelow(env)
	bindLetLocals(bindings, localFrame)

	body := Cddr(args)

	var shouldExit *Data

	for true {
		shouldExit, err = Eval(Car(testClause), localFrame)
		if err != nil {
			return
		}

		if BooleanValue(shouldExit) {
			for cell := Cdr(testClause); NotNilP(cell); cell = Cdr(cell) {
				sexpr := Car(cell)
				result, err = Eval(sexpr, localFrame)
				if err != nil {
					return
				}
			}
			return
		}

		for cell := body; NotNilP(cell); cell = Cdr(cell) {
			sexpr := Car(cell)
			result, err = Eval(sexpr, localFrame)
			if err != nil {
				return
			}
		}

		rebindDoLocals(bindings, localFrame)
	}
	return
}

func ApplyImpl(args *Data, env *SymbolTableFrame) (result *Data, err error) {
	if Length(args) < 1 {
		err = errors.New("apply requires at least one argument")
		return
	}

	f, err := Eval(Car(args), env)
	if err != nil {
		return
	}

	if !FunctionP(f) {
		err = errors.New(fmt.Sprintf("apply requires a function as it's first argument, but got %s.", String(f)))
	}

	ary := make([]*Data, 0, Length(args)-1)

	var v *Data
	for c := Cdr(args); NotNilP(c); c = Cdr(c) {
		v, err = Eval(Car(c), env)
		if err != nil {
			return
		}
		ary = append(ary, v)
	}

	var argList *Data
	if ListP(ary[len(ary)-1]) {
		if len(ary) > 1 {
			argList = ArrayToListWithTail(ary[0:len(ary)-1], ary[len(ary)-1])
		} else {
			argList = ary[0]
		}
	} else {
		argList = Cdr(args)
	}

	return Apply(f, argList, env)
}

func EvalImpl(args *Data, env *SymbolTableFrame) (result *Data, err error) {
	sexpr, err := Eval(Car(args), env)
	if err != nil {
		return
	}
	if !ListP(sexpr) {
		err = errors.New(fmt.Sprintf("eval expect a list argument, received a %s.", TypeName(TypeOf(sexpr))))
	}
	return Eval(sexpr, env)
}

func ChainImpl(args *Data, env *SymbolTableFrame) (result *Data, err error) {
	if Length(args) == 0 {
		err = errors.New("-> requires at least an initial value.")
		return
	}

	var value *Data

	value, err = Eval(Car(args), env)
	if err != nil {
		return
	}

	for cell := Cdr(args); NotNilP(cell); cell = Cdr(cell) {
		sexpr := Car(cell)
		var newExpr *Data
		if ListP(sexpr) {
			newExpr = Cons(Car(sexpr), Cons(value, Cdr(sexpr)))
		} else {
			newExpr = Cons(sexpr, Cons(value, nil))
		}
		value, err = Eval(newExpr, env)
		if err != nil {
			return
		}
	}
	result = value
	return
}

func TapImpl(args *Data, env *SymbolTableFrame) (result *Data, err error) {
	if Length(args) == 0 {
		err = errors.New("tap requires at least an initial value.")
		return
	}

	var value *Data

	value, err = Eval(Car(args), env)
	if err != nil {
		return
	}
	result = value

	for cell := Cdr(args); NotNilP(cell); cell = Cdr(cell) {
		sexpr := Car(cell)
		var newExpr *Data
		if ListP(sexpr) {
			newExpr = Cons(Car(sexpr), Cons(value, Cdr(sexpr)))
		} else {
			newExpr = Cons(sexpr, Cons(value, nil))
		}
		_, err = Eval(newExpr, env)
		if err != nil {
			return
		}
	}
	return
}
<commit_msg>Changed apply to behave lisle scheme wrt the final list arg.<commit_after>// Copyright 2014 SteelSeries ApS.  All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// This package implements a basic LISP interpretor for embedding in a go program for scripting.
// This file contains the built-in primitive functions.

package golisp

import (
	"errors"
	"fmt"
)

func RegisterSpecialFormPrimitives() {
	MakePrimitiveFunction("cond", -1, CondImpl)
	MakePrimitiveFunction("case", -1, CaseImpl)
	MakePrimitiveFunction("if", -1, IfImpl)
	MakePrimitiveFunction("lambda", -1, LambdaImpl)
	MakePrimitiveFunction("define", -1, DefineImpl)
	MakePrimitiveFunction("defmacro", -1, DefmacroImpl)
	MakePrimitiveFunction("let", -1, LetImpl)
	MakePrimitiveFunction("begin", -1, BeginImpl)
	MakePrimitiveFunction("do", -1, DoImpl)
	MakePrimitiveFunction("apply", -1, ApplyImpl)
	MakePrimitiveFunction("eval", 1, EvalImpl)
	MakePrimitiveFunction("->", -1, ChainImpl)
	MakePrimitiveFunction("=>", -1, TapImpl)
}

func CondImpl(args *Data, env *SymbolTableFrame) (result *Data, err error) {
	var condition *Data
	for c := args; NotNilP(c); c = Cdr(c) {
		clause := Car(c)
		if !PairP(clause) {
			err = errors.New("Cond expect a sequence of clauses that are lists")
			return
		}
		condition, err = Eval(Car(clause), env)
		if err != nil {
			return
		}
		if BooleanValue(condition) || StringValue(Car(clause)) == "else" {
			for e := Cdr(clause); NotNilP(e); e = Cdr(e) {
				result, err = Eval(Car(e), env)
				if err != nil {
					return
				}
			}
			return
		}
	}
	return
}

func evalList(l *Data, env *SymbolTableFrame) (result *Data, err error) {
	for sexpr := l; NotNilP(sexpr); sexpr = Cdr(sexpr) {
		result, err = Eval(Car(sexpr), env)
		if err != nil {
			return
		}
	}
	return
}

func CaseImpl(args *Data, env *SymbolTableFrame) (result *Data, err error) {
	var keyValue *Data

	keyValue, err = Eval(Car(args), env)
	if err != nil {
		return
	}

	for clauseCell := Cdr(args); NotNilP(clauseCell); clauseCell = Cdr(clauseCell) {
		clause := Car(clauseCell)
		if !PairP(clause) {
			err = errors.New("Case requires non-atomic clauses")
			return
		}
		if ListP(Car(clause)) {
			for v := Car(clause); NotNilP(v); v = Cdr(v) {
				if IsEqual(Car(v), keyValue) {
					return evalList(Cdr(clause), env)
				}
			}
		} else if IsEqual(Car(clause), SymbolWithName("else")) {
			return evalList(Cdr(clause), env)
		}
	}

	return
}

func IfImpl(args *Data, env *SymbolTableFrame) (result *Data, err error) {
	if Length(args) < 2 || Length(args) > 3 {
		err = errors.New(fmt.Sprintf("IF requires 2 or 3 arguments. Received %d.", Length(args)))
		return
	}

	c, err := Eval(Car(args), env)
	if err != nil {
		return
	}
	condition := BooleanValue(c)
	thenClause := Second(args)
	elseClause := Third(args)

	if condition {
		return Eval(thenClause, env)
	} else {
		return Eval(elseClause, env)
	}
}

func LambdaImpl(args *Data, env *SymbolTableFrame) (result *Data, err error) {
	params := Car(args)
	body := Cdr(args)
	return FunctionWithNameParamsBodyAndParent("anonymous", params, body, env), nil
}

func DefineImpl(args *Data, env *SymbolTableFrame) (result *Data, err error) {
	var value *Data
	thing := Car(args)
	if SymbolP(thing) {
		value, err = Eval(Cadr(args), env)
		if err != nil {
			return
		}
	} else if PairP(thing) {
		name := Car(thing)
		params := Cdr(thing)
		thing = name
		if !SymbolP(name) {
			err = errors.New("Function name has to be a symbol")
			return
		}
		body := Cdr(args)
		value = FunctionWithNameParamsBodyAndParent(StringValue(name), params, body, env)
	} else {
		err = errors.New("Invalid definition")
		return
	}
	env.BindLocallyTo(thing, value)
	return value, nil
}

func DefmacroImpl(args *Data, env *SymbolTableFrame) (result *Data, err error) {
	var value *Data
	thing := Car(args)
	if PairP(thing) {
		name := Car(thing)
		params := Cdr(thing)
		thing = name
		if !SymbolP(name) {
			err = errors.New("Macro name has to be a symbol")
			return
		}
		body := Cadr(args)
		value = MacroWithNameParamsBodyAndParent(StringValue(name), params, body, env)
	} else {
		err = errors.New("Invalid macro definition")
		return
	}
	env.BindLocallyTo(thing, value)
	return value, nil
}

func bindLetLocals(bindingForms *Data, env *SymbolTableFrame) (err error) {
	var name *Data
	var value *Data

	for cell := bindingForms; NotNilP(cell); cell = Cdr(cell) {
		bindingPair := Car(cell)
		if !PairP(bindingPair) {
			err = errors.New("Let requires a list of bindings (with are pairs) as it's first argument")
			return
		}
		name = Car(bindingPair)
		if !SymbolP(name) {
			err = errors.New("First part of a let binding pair must be a symbol")
		}
		value, err = Eval(Cadr(bindingPair), env)
		if err != nil {
			return
		}
		env.BindLocallyTo(name, value)
	}
	return
}

func LetImpl(args *Data, env *SymbolTableFrame) (result *Data, err error) {
	if Length(args) < 1 {
		err = errors.New("Let requires at least a list of bindings")
		return
	}

	if !PairP(Car(args)) {
		err = errors.New("Let requires a list of bindings as it's first argument")
		return
	}

	localFrame := NewSymbolTableFrameBelow(env)
	bindLetLocals(Car(args), localFrame)

	for cell := Cdr(args); NotNilP(cell); cell = Cdr(cell) {
		sexpr := Car(cell)
		result, err = Eval(sexpr, localFrame)
		if err != nil {
			return
		}
	}

	return
}

func BeginImpl(args *Data, env *SymbolTableFrame) (result *Data, err error) {
	for cell := args; NotNilP(cell); cell = Cdr(cell) {
		sexpr := Car(cell)
		result, err = Eval(sexpr, env)
		if err != nil {
			return
		}
	}
	return
}

func rebindDoLocals(bindingForms *Data, env *SymbolTableFrame) (err error) {
	var name *Data
	var value *Data

	for cell := bindingForms; NotNilP(cell); cell = Cdr(cell) {
		bindingTuple := Car(cell)
		name = First(bindingTuple)
		if NotNilP(Third(bindingTuple)) {
			value, err = Eval(Third(bindingTuple), env)
			if err != nil {
				return
			}
			env.BindLocallyTo(name, value)
		}
	}
	return
}

func DoImpl(args *Data, env *SymbolTableFrame) (result *Data, err error) {
	if Length(args) < 2 {
		err = errors.New("Do requires at least a list of bindings and a test clause")
		return
	}

	bindings := Car(args)
	if !PairP(bindings) {
		err = errors.New("Do requires a list of bindings as it's first argument")
		return
	}

	testClause := Cadr(args)
	if !PairP(testClause) {
		err = errors.New("Do requires a list as it's second argument")
		return
	}

	localFrame := NewSymbolTableFrameBelow(env)
	bindLetLocals(bindings, localFrame)

	body := Cddr(args)

	var shouldExit *Data

	for true {
		shouldExit, err = Eval(Car(testClause), localFrame)
		if err != nil {
			return
		}

		if BooleanValue(shouldExit) {
			for cell := Cdr(testClause); NotNilP(cell); cell = Cdr(cell) {
				sexpr := Car(cell)
				result, err = Eval(sexpr, localFrame)
				if err != nil {
					return
				}
			}
			return
		}

		for cell := body; NotNilP(cell); cell = Cdr(cell) {
			sexpr := Car(cell)
			result, err = Eval(sexpr, localFrame)
			if err != nil {
				return
			}
		}

		rebindDoLocals(bindings, localFrame)
	}
	return
}

func ApplyImpl(args *Data, env *SymbolTableFrame) (result *Data, err error) {
	if Length(args) < 1 {
		err = errors.New("apply requires at least one argument")
		return
	}

	f, err := Eval(Car(args), env)
	if err != nil {
		return
	}

	if !FunctionP(f) {
		err = errors.New(fmt.Sprintf("apply requires a function as it's first argument, but got %s.", String(f)))
	}

	ary := make([]*Data, 0, Length(args)-1)

	var v *Data
	for c := Cdr(args); NotNilP(c); c = Cdr(c) {
		v, err = Eval(Car(c), env)
		if err != nil {
			return
		}
		ary = append(ary, v)
	}

	var argList *Data
	if ListP(ary[len(ary)-1]) {
		if len(ary) > 1 {
			argList = ArrayToListWithTail(ary[0:len(ary)-1], ary[len(ary)-1])
		} else {
			argList = ary[0]
		}
	} else {
		err = errors.New("The last argument to apply must be a list")
		return
	}

	return Apply(f, argList, env)
}

func EvalImpl(args *Data, env *SymbolTableFrame) (result *Data, err error) {
	sexpr, err := Eval(Car(args), env)
	if err != nil {
		return
	}
	if !ListP(sexpr) {
		err = errors.New(fmt.Sprintf("eval expect a list argument, received a %s.", TypeName(TypeOf(sexpr))))
	}
	return Eval(sexpr, env)
}

func ChainImpl(args *Data, env *SymbolTableFrame) (result *Data, err error) {
	if Length(args) == 0 {
		err = errors.New("-> requires at least an initial value.")
		return
	}

	var value *Data

	value, err = Eval(Car(args), env)
	if err != nil {
		return
	}

	for cell := Cdr(args); NotNilP(cell); cell = Cdr(cell) {
		sexpr := Car(cell)
		var newExpr *Data
		if ListP(sexpr) {
			newExpr = Cons(Car(sexpr), Cons(value, Cdr(sexpr)))
		} else {
			newExpr = Cons(sexpr, Cons(value, nil))
		}
		value, err = Eval(newExpr, env)
		if err != nil {
			return
		}
	}
	result = value
	return
}

func TapImpl(args *Data, env *SymbolTableFrame) (result *Data, err error) {
	if Length(args) == 0 {
		err = errors.New("tap requires at least an initial value.")
		return
	}

	var value *Data

	value, err = Eval(Car(args), env)
	if err != nil {
		return
	}
	result = value

	for cell := Cdr(args); NotNilP(cell); cell = Cdr(cell) {
		sexpr := Car(cell)
		var newExpr *Data
		if ListP(sexpr) {
			newExpr = Cons(Car(sexpr), Cons(value, Cdr(sexpr)))
		} else {
			newExpr = Cons(sexpr, Cons(value, nil))
		}
		_, err = Eval(newExpr, env)
		if err != nil {
			return
		}
	}
	return
}
<|endoftext|>


<commit_before>package websocket

import (
	"log"
	"net/http"
	"os"

	"github.com/gorilla/mux"
	"github.com/gorilla/websocket"
	"github.com/unrolled/render"
	"github.com/urfave/negroni"
	"github.com/velocity-ci/velocity/backend/api/auth"
)

// Controller - Handles Websockets
type Controller struct {
	logger  *log.Logger
	render  *render.Render
	manager *Manager
}

// NewController - returns a new Controller for client Websockets.
func NewController(websocketManager *Manager) *Controller {
	return &Controller{
		logger:  log.New(os.Stdout, "[controller:websocket]", log.Lshortfile),
		render:  render.New(),
		manager: websocketManager,
	}
}

// Setup - Sets up the Websocket Controller
func (c Controller) Setup(router *mux.Router) {

	// /v1/ws
	router.Handle("/v1/ws", negroni.New(
		auth.NewJWT(c.render),
		negroni.Wrap(http.HandlerFunc(c.wsClientHandler)),
	)).Methods("GET")

	c.logger.Println("Set up Websocket controller.")
}

var upgrader = websocket.Upgrader{
	ReadBufferSize:  1024,
	WriteBufferSize: 1024,
	CheckOrigin: func(r *http.Request) bool {
		return true
	},
}

func (c Controller) wsClientHandler(w http.ResponseWriter, r *http.Request) {

	ws, err := upgrader.Upgrade(w, r, nil)
	if err != nil {
		log.Println(err)
		return
	}

	wsClient := NewClient(ws)

	c.manager.Save(wsClient)

	// Monitor for Messages
	go c.monitor(wsClient)
}

func (c *Controller) monitor(client *Client) {
	for {
		message := &PhoenixMessage{}
		err := client.ws.ReadJSON(message)
		if err != nil {
			log.Println(err)
			log.Println("Closing Client WebSocket")
			client.ws.Close()
			c.manager.Remove(client)
			return
		}
		client.HandleMessage(message)

		// if message.Type == "subscribe" {
		// 	client.Subscribe(message.Route)
		// 	if strings.Contains(message.Route, "builds/") {
		// 		routeParts := strings.Split(message.Route, "/")
		// 		projectID := routeParts[1]
		// 		commitHash := routeParts[3]
		// 		buildID := routeParts[5]

		// 		buildIDU, err := strconv.ParseUint(buildID, 10, 64)
		// 		if err != nil {
		// 			log.Fatal(err)
		// 		}
		// 		// build := c.commitManager.GetBuild(projectID, commitHash, buildIDU)
		// 		// for stepNumber, sL := range build.StepLogs {
		// 		// 	for _, ls := range sL.Logs {
		// 		// 		for _, l := range ls {
		// 		// 			client.ws.WriteJSON(
		// 		// 				&EmitMessage{
		// 		// 					Subscription: fmt.Sprintf("project/%s/commits/%s/builds/%d", projectID, commitHash, buildIDU),
		// 		// 					Data: BuildMessage{
		// 		// 						Step:   uint64(stepNumber),
		// 		// 						Status: sL.Status,
		// 		// 						Log: LogMessage{
		// 		// 							Timestamp: l.Timestamp,
		// 		// 							Output:    l.Output,
		// 		// 						},
		// 		// 					},
		// 		// 				},
		// 		// 			)
		// 		// 		}
		// 		// 	}
		// 		// }
		// 	}
		// } else if message.Type == "unsubscribe" {
		// 	client.Unsubscribe(message.Route)
		// }
	}
}
<commit_msg>[backend] Removed websocket authentication for now<commit_after>package websocket

import (
	"log"
	"net/http"
	"os"

	"github.com/gorilla/mux"
	"github.com/gorilla/websocket"
	"github.com/unrolled/render"
	"github.com/urfave/negroni"
	"github.com/velocity-ci/velocity/backend/api/auth"
)

// Controller - Handles Websockets
type Controller struct {
	logger  *log.Logger
	render  *render.Render
	manager *Manager
}

// NewController - returns a new Controller for client Websockets.
func NewController(websocketManager *Manager) *Controller {
	return &Controller{
		logger:  log.New(os.Stdout, "[controller:websocket]", log.Lshortfile),
		render:  render.New(),
		manager: websocketManager,
	}
}

// Setup - Sets up the Websocket Controller
func (c Controller) Setup(router *mux.Router) {

	// /v1/ws
	router.Handle("/v1/ws", negroni.New(
		negroni.Wrap(http.HandlerFunc(c.wsClientHandler)),
	)).Methods("GET")

	c.logger.Println("Set up Websocket controller.")
}

var upgrader = websocket.Upgrader{
	ReadBufferSize:  1024,
	WriteBufferSize: 1024,
	CheckOrigin: func(r *http.Request) bool {
		return true
	},
}

func (c Controller) wsClientHandler(w http.ResponseWriter, r *http.Request) {

	ws, err := upgrader.Upgrade(w, r, nil)
	if err != nil {
		log.Println(err)
		return
	}

	wsClient := NewClient(ws)

	c.manager.Save(wsClient)

	// Monitor for Messages
	go c.monitor(wsClient)
}

func (c *Controller) monitor(client *Client) {
	for {
		message := &PhoenixMessage{}
		err := client.ws.ReadJSON(message)
		if err != nil {
			log.Println(err)
			log.Println("Closing Client WebSocket")
			client.ws.Close()
			c.manager.Remove(client)
			return
		}
		client.HandleMessage(message)
	}
}
<|endoftext|>