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

package api

import (
	"fmt"
	stdLog "log"
	"net/http"
	"os"
	"reflect"
	"time"

	"github.com/codegangsta/negroni"
	"github.com/nu7hatch/gouuid"
	"github.com/tsuru/config"
	"github.com/tsuru/tsuru/api/context"
	"github.com/tsuru/tsuru/app"
	"github.com/tsuru/tsuru/auth"
	"github.com/tsuru/tsuru/errors"
	"github.com/tsuru/tsuru/io"
	"github.com/tsuru/tsuru/log"
)

const (
	tsuruMin      = "1.0.1"
	craneMin      = "1.0.0"
	tsuruAdminMin = "1.0.0"
)

func validate(token string, r *http.Request) (auth.Token, error) {
	t, err := app.AuthScheme.Auth(token)
	if err != nil {
		t, err = auth.APIAuth(token)
		if err != nil {
			return nil, err
		}
	}
	if t.IsAppToken() {
		if q := r.URL.Query().Get(":app"); q != "" && t.GetAppName() != q {
			return nil, &errors.HTTP{
				Code:    http.StatusForbidden,
				Message: fmt.Sprintf("app token mismatch, token for %q, request for %q", t.GetAppName(), q),
			}
		}
	} else {
		if q := r.URL.Query().Get(":app"); q != "" {
			_, err = getAppFromContext(q, r)
			if err != nil {
				return nil, err
			}
		}
	}
	return t, nil
}

func contextClearerMiddleware(w http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
	defer context.Clear(r)
	next(w, r)
}

func flushingWriterMiddleware(w http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
	defer func() {
		if r.Body != nil {
			r.Body.Close()
		}
	}()
	fw := io.FlushingWriter{ResponseWriter: w}
	next(&fw, r)
}

func setRequestIDHeaderMiddleware(w http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
	requestIDHeader, _ := config.GetString("request-id-header")
	if requestIDHeader == "" {
		next(w, r)
		return
	}
	requestID := r.Header.Get(requestIDHeader)
	if requestID == "" {
		unparsedID, err := uuid.NewV4()
		if err != nil {
			log.Errorf("unable to generate request id: %s", err)
			next(w, r)
			return
		}
		requestID = unparsedID.String()
	}
	context.SetRequestID(r, requestIDHeader, requestID)
	next(w, r)
}

func setVersionHeadersMiddleware(w http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
	w.Header().Set("Supported-Tsuru", tsuruMin)
	w.Header().Set("Supported-Crane", craneMin)
	w.Header().Set("Supported-Tsuru-Admin", tsuruAdminMin)
	next(w, r)
}

func errorHandlingMiddleware(w http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
	next(w, r)
	err := context.GetRequestError(r)
	if err != nil {
		code := http.StatusInternalServerError
		if e, ok := err.(*errors.HTTP); ok {
			code = e.Code
		}
		flushing, ok := w.(*io.FlushingWriter)
		if ok && flushing.Wrote() {
			fmt.Fprintln(w, err)
		} else {
			http.Error(w, err.Error(), code)
		}
		log.Errorf("failure running HTTP request %s %s (%d): %s", r.Method, r.URL.Path, code, err)
	}
}

func authTokenMiddleware(w http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
	token := r.Header.Get("Authorization")
	if token != "" {
		t, err := validate(token, r)
		if err != nil {
			if err != auth.ErrInvalidToken {
				context.AddRequestError(r, err)
				return
			}
			log.Debugf("Ignored invalid token for %s: %s", r.URL.Path, err.Error())
		} else {
			context.SetAuthToken(r, t)
		}
	}
	next(w, r)
}

type appLockMiddleware struct {
	excludedHandlers []http.Handler
}

var lockWaitDuration time.Duration = 10 * time.Second

func (m *appLockMiddleware) ServeHTTP(w http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
	if r.Method == "GET" {
		next(w, r)
		return
	}
	currentHandler := context.GetDelayedHandler(r)
	if currentHandler != nil {
		currentHandlerPtr := reflect.ValueOf(currentHandler).Pointer()
		for _, h := range m.excludedHandlers {
			if reflect.ValueOf(h).Pointer() == currentHandlerPtr {
				next(w, r)
				return
			}
		}
	}
	appName := r.URL.Query().Get(":app")
	if appName == "" {
		appName = r.URL.Query().Get(":appname")
	}
	if appName == "" {
		next(w, r)
		return
	}
	t := context.GetAuthToken(r)
	var owner string
	if t != nil {
		if t.IsAppToken() {
			owner = t.GetAppName()
		} else {
			owner = t.GetUserName()
		}
	}
	_, err := app.GetByName(appName)
	if err == app.ErrAppNotFound {
		context.AddRequestError(r, &errors.HTTP{Code: http.StatusNotFound, Message: err.Error()})
		return
	}
	ok, err := app.AcquireApplicationLockWait(appName, owner, fmt.Sprintf("%s %s", r.Method, r.URL.Path), lockWaitDuration)
	if err != nil {
		context.AddRequestError(r, fmt.Errorf("Error trying to acquire application lock: %s", err))
		return
	}
	if ok {
		defer func() {
			if !context.IsPreventUnlock(r) {
				app.ReleaseApplicationLock(appName)
			}
		}()
		next(w, r)
		return
	}
	a, err := app.GetByName(appName)
	httpErr := &errors.HTTP{Code: http.StatusInternalServerError}
	if err != nil {
		if err == app.ErrAppNotFound {
			httpErr.Code = http.StatusNotFound
			httpErr.Message = err.Error()
		} else {
			httpErr.Message = fmt.Sprintf("Error to get application: %s", err)
		}
	} else {
		httpErr.Code = http.StatusConflict
		if a.Lock.Locked {
			httpErr.Message = fmt.Sprintf("%s", &a.Lock)
		} else {
			httpErr.Message = "Not locked anymore, please try again."
		}
	}
	context.AddRequestError(r, httpErr)
}

func runDelayedHandler(w http.ResponseWriter, r *http.Request) {
	h := context.GetDelayedHandler(r)
	if h != nil {
		h.ServeHTTP(w, r)
	}
}

type loggerMiddleware struct {
	logger *stdLog.Logger
}

func newLoggerMiddleware() *loggerMiddleware {
	return &loggerMiddleware{
		logger: stdLog.New(os.Stdout, "", 0),
	}
}

func (l *loggerMiddleware) ServeHTTP(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
	start := time.Now()
	next(rw, r)
	duration := time.Since(start)
	statusCode := rw.(negroni.ResponseWriter).Status()
	if statusCode == 0 {
		statusCode = 200
	}
	nowFormatted := time.Now().Format(time.RFC3339Nano)
	requestIDHeader, _ := config.GetString("request-id-header")
	var requestID string
	if requestIDHeader != "" {
		requestID = context.GetRequestID(r, requestIDHeader)
		if requestID != "" {
			requestID = fmt.Sprintf(" [%s: %s]", requestIDHeader, requestID)
		}
	}
	l.logger.Printf("%s %s %s %d in %0.6fms%s", nowFormatted, r.Method, r.URL.Path, statusCode, float64(duration)/float64(time.Millisecond), requestID)
}
<commit_msg>define client versions<commit_after>// Copyright 2016 tsuru authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package api

import (
	"fmt"
	stdLog "log"
	"net/http"
	"os"
	"reflect"
	"time"

	"github.com/codegangsta/negroni"
	"github.com/nu7hatch/gouuid"
	"github.com/tsuru/config"
	"github.com/tsuru/tsuru/api/context"
	"github.com/tsuru/tsuru/app"
	"github.com/tsuru/tsuru/auth"
	"github.com/tsuru/tsuru/errors"
	"github.com/tsuru/tsuru/io"
	"github.com/tsuru/tsuru/log"
)

const (
	tsuruMin      = "1.0.1-rc1"
	craneMin      = "1.0.0-rc1"
	tsuruAdminMin = "1.0.0-rc1"
)

func validate(token string, r *http.Request) (auth.Token, error) {
	t, err := app.AuthScheme.Auth(token)
	if err != nil {
		t, err = auth.APIAuth(token)
		if err != nil {
			return nil, err
		}
	}
	if t.IsAppToken() {
		if q := r.URL.Query().Get(":app"); q != "" && t.GetAppName() != q {
			return nil, &errors.HTTP{
				Code:    http.StatusForbidden,
				Message: fmt.Sprintf("app token mismatch, token for %q, request for %q", t.GetAppName(), q),
			}
		}
	} else {
		if q := r.URL.Query().Get(":app"); q != "" {
			_, err = getAppFromContext(q, r)
			if err != nil {
				return nil, err
			}
		}
	}
	return t, nil
}

func contextClearerMiddleware(w http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
	defer context.Clear(r)
	next(w, r)
}

func flushingWriterMiddleware(w http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
	defer func() {
		if r.Body != nil {
			r.Body.Close()
		}
	}()
	fw := io.FlushingWriter{ResponseWriter: w}
	next(&fw, r)
}

func setRequestIDHeaderMiddleware(w http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
	requestIDHeader, _ := config.GetString("request-id-header")
	if requestIDHeader == "" {
		next(w, r)
		return
	}
	requestID := r.Header.Get(requestIDHeader)
	if requestID == "" {
		unparsedID, err := uuid.NewV4()
		if err != nil {
			log.Errorf("unable to generate request id: %s", err)
			next(w, r)
			return
		}
		requestID = unparsedID.String()
	}
	context.SetRequestID(r, requestIDHeader, requestID)
	next(w, r)
}

func setVersionHeadersMiddleware(w http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
	w.Header().Set("Supported-Tsuru", tsuruMin)
	w.Header().Set("Supported-Crane", craneMin)
	w.Header().Set("Supported-Tsuru-Admin", tsuruAdminMin)
	next(w, r)
}

func errorHandlingMiddleware(w http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
	next(w, r)
	err := context.GetRequestError(r)
	if err != nil {
		code := http.StatusInternalServerError
		if e, ok := err.(*errors.HTTP); ok {
			code = e.Code
		}
		flushing, ok := w.(*io.FlushingWriter)
		if ok && flushing.Wrote() {
			fmt.Fprintln(w, err)
		} else {
			http.Error(w, err.Error(), code)
		}
		log.Errorf("failure running HTTP request %s %s (%d): %s", r.Method, r.URL.Path, code, err)
	}
}

func authTokenMiddleware(w http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
	token := r.Header.Get("Authorization")
	if token != "" {
		t, err := validate(token, r)
		if err != nil {
			if err != auth.ErrInvalidToken {
				context.AddRequestError(r, err)
				return
			}
			log.Debugf("Ignored invalid token for %s: %s", r.URL.Path, err.Error())
		} else {
			context.SetAuthToken(r, t)
		}
	}
	next(w, r)
}

type appLockMiddleware struct {
	excludedHandlers []http.Handler
}

var lockWaitDuration time.Duration = 10 * time.Second

func (m *appLockMiddleware) ServeHTTP(w http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
	if r.Method == "GET" {
		next(w, r)
		return
	}
	currentHandler := context.GetDelayedHandler(r)
	if currentHandler != nil {
		currentHandlerPtr := reflect.ValueOf(currentHandler).Pointer()
		for _, h := range m.excludedHandlers {
			if reflect.ValueOf(h).Pointer() == currentHandlerPtr {
				next(w, r)
				return
			}
		}
	}
	appName := r.URL.Query().Get(":app")
	if appName == "" {
		appName = r.URL.Query().Get(":appname")
	}
	if appName == "" {
		next(w, r)
		return
	}
	t := context.GetAuthToken(r)
	var owner string
	if t != nil {
		if t.IsAppToken() {
			owner = t.GetAppName()
		} else {
			owner = t.GetUserName()
		}
	}
	_, err := app.GetByName(appName)
	if err == app.ErrAppNotFound {
		context.AddRequestError(r, &errors.HTTP{Code: http.StatusNotFound, Message: err.Error()})
		return
	}
	ok, err := app.AcquireApplicationLockWait(appName, owner, fmt.Sprintf("%s %s", r.Method, r.URL.Path), lockWaitDuration)
	if err != nil {
		context.AddRequestError(r, fmt.Errorf("Error trying to acquire application lock: %s", err))
		return
	}
	if ok {
		defer func() {
			if !context.IsPreventUnlock(r) {
				app.ReleaseApplicationLock(appName)
			}
		}()
		next(w, r)
		return
	}
	a, err := app.GetByName(appName)
	httpErr := &errors.HTTP{Code: http.StatusInternalServerError}
	if err != nil {
		if err == app.ErrAppNotFound {
			httpErr.Code = http.StatusNotFound
			httpErr.Message = err.Error()
		} else {
			httpErr.Message = fmt.Sprintf("Error to get application: %s", err)
		}
	} else {
		httpErr.Code = http.StatusConflict
		if a.Lock.Locked {
			httpErr.Message = fmt.Sprintf("%s", &a.Lock)
		} else {
			httpErr.Message = "Not locked anymore, please try again."
		}
	}
	context.AddRequestError(r, httpErr)
}

func runDelayedHandler(w http.ResponseWriter, r *http.Request) {
	h := context.GetDelayedHandler(r)
	if h != nil {
		h.ServeHTTP(w, r)
	}
}

type loggerMiddleware struct {
	logger *stdLog.Logger
}

func newLoggerMiddleware() *loggerMiddleware {
	return &loggerMiddleware{
		logger: stdLog.New(os.Stdout, "", 0),
	}
}

func (l *loggerMiddleware) ServeHTTP(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
	start := time.Now()
	next(rw, r)
	duration := time.Since(start)
	statusCode := rw.(negroni.ResponseWriter).Status()
	if statusCode == 0 {
		statusCode = 200
	}
	nowFormatted := time.Now().Format(time.RFC3339Nano)
	requestIDHeader, _ := config.GetString("request-id-header")
	var requestID string
	if requestIDHeader != "" {
		requestID = context.GetRequestID(r, requestIDHeader)
		if requestID != "" {
			requestID = fmt.Sprintf(" [%s: %s]", requestIDHeader, requestID)
		}
	}
	l.logger.Printf("%s %s %s %d in %0.6fms%s", nowFormatted, r.Method, r.URL.Path, statusCode, float64(duration)/float64(time.Millisecond), requestID)
}
<|endoftext|>


<commit_before>/*
   Copyright (c) 2016 VMware, Inc. All Rights Reserved.
   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 api

import (
	"encoding/json"
	"net/http"
	"os"
	"strconv"
	"strings"
	"time"

	"github.com/docker/distribution/manifest/schema1"
	"github.com/vmware/harbor/dao"
	"github.com/vmware/harbor/models"
	svc_utils "github.com/vmware/harbor/service/utils"
	"github.com/vmware/harbor/utils/log"
	"github.com/vmware/harbor/utils/registry"
	"github.com/vmware/harbor/utils/registry/auth"
	"github.com/vmware/harbor/utils/registry/errors"
)

// RepositoryAPI handles request to /api/repositories /api/repositories/tags /api/repositories/manifests, the parm has to be put
// in the query string as the web framework can not parse the URL if it contains veriadic sectors.
// For repostiories, we won't check the session in this API due to search functionality, querying manifest will be contorlled by
// the security of registry
type RepositoryAPI struct {
	BaseAPI
	userID   int
	username string
}

// Prepare will set a non existent user ID in case the request tries to view repositories under a project he doesn't has permission.
func (ra *RepositoryAPI) Prepare() {
	userID, ok := ra.GetSession("userId").(int)
	if !ok {
		userID = dao.NonExistUserID
	}
	ra.userID = userID

	username, ok := ra.GetSession("username").(string)
	if ok {
		ra.username = username
	}
}

// Get ...
func (ra *RepositoryAPI) Get() {
	projectID, err0 := ra.GetInt64("project_id")
	if err0 != nil {
		log.Errorf("Failed to get project id, error: %v", err0)
		ra.RenderError(http.StatusBadRequest, "Invalid project id")
		return
	}
	p, err := dao.GetProjectByID(projectID)
	if err != nil {
		log.Errorf("Error occurred in GetProjectById, error: %v", err)
		ra.CustomAbort(http.StatusInternalServerError, "Internal error.")
	}
	if p == nil {
		log.Warningf("Project with Id: %d does not exist", projectID)
		ra.RenderError(http.StatusNotFound, "")
		return
	}
	if p.Public == 0 && !checkProjectPermission(ra.userID, projectID) {
		ra.RenderError(http.StatusForbidden, "")
		return
	}

	repoList, err := svc_utils.GetRepoFromCache()
	if err != nil {
		log.Errorf("Failed to get repo from cache, error: %v", err)
		ra.RenderError(http.StatusInternalServerError, "internal sever error")
	}

	projectName := p.Name
	q := ra.GetString("q")
	var resp []string
	if len(q) > 0 {
		for _, r := range repoList {
			if strings.Contains(r, "/") && strings.Contains(r[strings.LastIndex(r, "/")+1:], q) && r[0:strings.LastIndex(r, "/")] == projectName {
				resp = append(resp, r)
			}
		}
		ra.Data["json"] = resp
	} else if len(projectName) > 0 {
		for _, r := range repoList {
			if strings.Contains(r, "/") && r[0:strings.LastIndex(r, "/")] == projectName {
				resp = append(resp, r)
			}
		}
		ra.Data["json"] = resp
	} else {
		ra.Data["json"] = repoList
	}
	ra.ServeJSON()
}

// Delete ...
func (ra *RepositoryAPI) Delete() {
	repoName := ra.GetString("repo_name")
	if len(repoName) == 0 {
		ra.CustomAbort(http.StatusBadRequest, "repo_name is nil")
	}

	rc, err := ra.initializeRepositoryClient(repoName)
	if err != nil {
		log.Errorf("error occurred while initializing repository client for %s: %v", repoName, err)
		ra.CustomAbort(http.StatusInternalServerError, "internal error")
	}

	tags := []string{}
	tag := ra.GetString("tag")
	if len(tag) == 0 {
		tagList, err := rc.ListTag()
		if err != nil {
			e, ok := errors.ParseError(err)
			if ok {
				log.Info(e)
				ra.CustomAbort(e.StatusCode, e.Message)
			} else {
				log.Error(err)
				ra.CustomAbort(http.StatusInternalServerError, "internal error")
			}
		}
		tags = append(tags, tagList...)
	} else {
		tags = append(tags, tag)
	}

	for _, t := range tags {
		if err := rc.DeleteTag(t); err != nil {
			e, ok := errors.ParseError(err)
			if ok {
				ra.CustomAbort(e.StatusCode, e.Message)
			} else {
				log.Error(err)
				ra.CustomAbort(http.StatusInternalServerError, "internal error")
			}
		}
		log.Infof("delete tag: %s %s", repoName, t)
	}

	go func() {
		log.Debug("refreshing catalog cache")
		if err := svc_utils.RefreshCatalogCache(); err != nil {
			log.Errorf("error occurred while refresh catalog cache: %v", err)
		}
	}()

}

type tag struct {
	Name string   `json:"name"`
	Tags []string `json:"tags"`
}

// GetTags handles GET /api/repositories/tags
func (ra *RepositoryAPI) GetTags() {
	repoName := ra.GetString("repo_name")
	if len(repoName) == 0 {
		ra.CustomAbort(http.StatusBadRequest, "repo_name is nil")
	}

	rc, err := ra.initializeRepositoryClient(repoName)
	if err != nil {
		log.Errorf("error occurred while initializing repository client for %s: %v", repoName, err)
		ra.CustomAbort(http.StatusInternalServerError, "internal error")
	}

	tags := []string{}

	ts, err := rc.ListTag()
	if err != nil {
		e, ok := errors.ParseError(err)
		if ok {
			ra.CustomAbort(e.StatusCode, e.Message)
		} else {
			log.Error(err)
			ra.CustomAbort(http.StatusInternalServerError, "internal error")
		}
	}

	tags = append(tags, ts...)

	ra.Data["json"] = tags
	ra.ServeJSON()
}

// GetManifests handles GET /api/repositories/manifests
func (ra *RepositoryAPI) GetManifests() {
	repoName := ra.GetString("repo_name")
	tag := ra.GetString("tag")

	if len(repoName) == 0 || len(tag) == 0 {
		ra.CustomAbort(http.StatusBadRequest, "repo_name or tag is nil")
	}

	rc, err := ra.initializeRepositoryClient(repoName)
	if err != nil {
		log.Errorf("error occurred while initializing repository client for %s: %v", repoName, err)
		ra.CustomAbort(http.StatusInternalServerError, "internal error")
	}

	item := models.RepoItem{}

	mediaTypes := []string{schema1.MediaTypeManifest}
	_, _, payload, err := rc.PullManifest(tag, mediaTypes)
	if err != nil {
		e, ok := errors.ParseError(err)
		if ok {
			ra.CustomAbort(e.StatusCode, e.Message)
		} else {
			log.Error(err)
			ra.CustomAbort(http.StatusInternalServerError, "internal error")
		}
	}
	mani := models.Manifest{}
	err = json.Unmarshal(payload, &mani)
	if err != nil {
		log.Errorf("Failed to decode json from response for manifests, repo name: %s, tag: %s, error: %v", repoName, tag, err)
		ra.RenderError(http.StatusInternalServerError, "Internal Server Error")
		return
	}
	v1Compatibility := mani.History[0].V1Compatibility

	err = json.Unmarshal([]byte(v1Compatibility), &item)
	if err != nil {
		log.Errorf("Failed to decode V1 field for repo, repo name: %s, tag: %s, error: %v", repoName, tag, err)
		ra.RenderError(http.StatusInternalServerError, "Internal Server Error")
		return
	}
	item.DurationDays = strconv.Itoa(int(time.Since(item.Created).Hours()/24)) + " days"

	ra.Data["json"] = item
	ra.ServeJSON()
}

func (ra *RepositoryAPI) initializeRepositoryClient(repoName string) (r *registry.Repository, err error) {
	endpoint := os.Getenv("REGISTRY_URL")

	//no session, use basic auth
	if ra.userID == dao.NonExistUserID {
		username, password, _ := ra.Ctx.Request.BasicAuth()
		credential := auth.NewBasicAuthCredential(username, password)

		return registry.NewRepositoryWithCredential(repoName, endpoint, credential)

	}

	//session exists, use username
	if len(ra.username) == 0 {
		u := models.User{
			UserID: ra.userID,
		}
		user, err := dao.GetUser(u)
		if err != nil {
			return nil, err
		}

		ra.username = user.Username
	}

	return registry.NewRepositoryWithUsername(repoName, endpoint, ra.username)
}
<commit_msg>add authtication to repository API<commit_after>/*
   Copyright (c) 2016 VMware, Inc. All Rights Reserved.
   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 api

import (
	"encoding/json"
	"net/http"
	"os"
	"strconv"
	"strings"
	"time"

	"github.com/docker/distribution/manifest/schema1"
	"github.com/vmware/harbor/dao"
	"github.com/vmware/harbor/models"
	svc_utils "github.com/vmware/harbor/service/utils"
	"github.com/vmware/harbor/utils/log"
	"github.com/vmware/harbor/utils/registry"
	"github.com/vmware/harbor/utils/registry/errors"
)

// RepositoryAPI handles request to /api/repositories /api/repositories/tags /api/repositories/manifests, the parm has to be put
// in the query string as the web framework can not parse the URL if it contains veriadic sectors.
// For repostiories, we won't check the session in this API due to search functionality, querying manifest will be contorlled by
// the security of registry
type RepositoryAPI struct {
	BaseAPI
	userID int
}

// Prepare will set a non existent user ID in case the request tries to view repositories under a project he doesn't has permission.
func (ra *RepositoryAPI) Prepare() {
	ra.userID = ra.ValidateUser()
}

// Get ...
func (ra *RepositoryAPI) Get() {
	projectID, err0 := ra.GetInt64("project_id")
	if err0 != nil {
		log.Errorf("Failed to get project id, error: %v", err0)
		ra.RenderError(http.StatusBadRequest, "Invalid project id")
		return
	}
	p, err := dao.GetProjectByID(projectID)
	if err != nil {
		log.Errorf("Error occurred in GetProjectById, error: %v", err)
		ra.CustomAbort(http.StatusInternalServerError, "Internal error.")
	}
	if p == nil {
		log.Warningf("Project with Id: %d does not exist", projectID)
		ra.RenderError(http.StatusNotFound, "")
		return
	}
	if p.Public == 0 && !checkProjectPermission(ra.userID, projectID) {
		ra.RenderError(http.StatusForbidden, "")
		return
	}

	repoList, err := svc_utils.GetRepoFromCache()
	if err != nil {
		log.Errorf("Failed to get repo from cache, error: %v", err)
		ra.RenderError(http.StatusInternalServerError, "internal sever error")
	}

	projectName := p.Name
	q := ra.GetString("q")
	var resp []string
	if len(q) > 0 {
		for _, r := range repoList {
			if strings.Contains(r, "/") && strings.Contains(r[strings.LastIndex(r, "/")+1:], q) && r[0:strings.LastIndex(r, "/")] == projectName {
				resp = append(resp, r)
			}
		}
		ra.Data["json"] = resp
	} else if len(projectName) > 0 {
		for _, r := range repoList {
			if strings.Contains(r, "/") && r[0:strings.LastIndex(r, "/")] == projectName {
				resp = append(resp, r)
			}
		}
		ra.Data["json"] = resp
	} else {
		ra.Data["json"] = repoList
	}
	ra.ServeJSON()
}

// Delete ...
func (ra *RepositoryAPI) Delete() {
	repoName := ra.GetString("repo_name")
	if len(repoName) == 0 {
		ra.CustomAbort(http.StatusBadRequest, "repo_name is nil")
	}

	rc, err := ra.initializeRepositoryClient(repoName)
	if err != nil {
		log.Errorf("error occurred while initializing repository client for %s: %v", repoName, err)
		ra.CustomAbort(http.StatusInternalServerError, "internal error")
	}

	tags := []string{}
	tag := ra.GetString("tag")
	if len(tag) == 0 {
		tagList, err := rc.ListTag()
		if err != nil {
			e, ok := errors.ParseError(err)
			if ok {
				log.Info(e)
				ra.CustomAbort(e.StatusCode, e.Message)
			} else {
				log.Error(err)
				ra.CustomAbort(http.StatusInternalServerError, "internal error")
			}
		}
		tags = append(tags, tagList...)
	} else {
		tags = append(tags, tag)
	}

	for _, t := range tags {
		if err := rc.DeleteTag(t); err != nil {
			e, ok := errors.ParseError(err)
			if ok {
				ra.CustomAbort(e.StatusCode, e.Message)
			} else {
				log.Error(err)
				ra.CustomAbort(http.StatusInternalServerError, "internal error")
			}
		}
		log.Infof("delete tag: %s %s", repoName, t)
	}

	go func() {
		log.Debug("refreshing catalog cache")
		if err := svc_utils.RefreshCatalogCache(); err != nil {
			log.Errorf("error occurred while refresh catalog cache: %v", err)
		}
	}()

}

type tag struct {
	Name string   `json:"name"`
	Tags []string `json:"tags"`
}

// GetTags handles GET /api/repositories/tags
func (ra *RepositoryAPI) GetTags() {
	repoName := ra.GetString("repo_name")
	if len(repoName) == 0 {
		ra.CustomAbort(http.StatusBadRequest, "repo_name is nil")
	}

	rc, err := ra.initializeRepositoryClient(repoName)
	if err != nil {
		log.Errorf("error occurred while initializing repository client for %s: %v", repoName, err)
		ra.CustomAbort(http.StatusInternalServerError, "internal error")
	}

	tags := []string{}

	ts, err := rc.ListTag()
	if err != nil {
		e, ok := errors.ParseError(err)
		if ok {
			ra.CustomAbort(e.StatusCode, e.Message)
		} else {
			log.Error(err)
			ra.CustomAbort(http.StatusInternalServerError, "internal error")
		}
	}

	tags = append(tags, ts...)

	ra.Data["json"] = tags
	ra.ServeJSON()
}

// GetManifests handles GET /api/repositories/manifests
func (ra *RepositoryAPI) GetManifests() {
	repoName := ra.GetString("repo_name")
	tag := ra.GetString("tag")

	if len(repoName) == 0 || len(tag) == 0 {
		ra.CustomAbort(http.StatusBadRequest, "repo_name or tag is nil")
	}

	rc, err := ra.initializeRepositoryClient(repoName)
	if err != nil {
		log.Errorf("error occurred while initializing repository client for %s: %v", repoName, err)
		ra.CustomAbort(http.StatusInternalServerError, "internal error")
	}

	item := models.RepoItem{}

	mediaTypes := []string{schema1.MediaTypeManifest}
	_, _, payload, err := rc.PullManifest(tag, mediaTypes)
	if err != nil {
		e, ok := errors.ParseError(err)
		if ok {
			ra.CustomAbort(e.StatusCode, e.Message)
		} else {
			log.Error(err)
			ra.CustomAbort(http.StatusInternalServerError, "internal error")
		}
	}
	mani := models.Manifest{}
	err = json.Unmarshal(payload, &mani)
	if err != nil {
		log.Errorf("Failed to decode json from response for manifests, repo name: %s, tag: %s, error: %v", repoName, tag, err)
		ra.RenderError(http.StatusInternalServerError, "Internal Server Error")
		return
	}
	v1Compatibility := mani.History[0].V1Compatibility

	err = json.Unmarshal([]byte(v1Compatibility), &item)
	if err != nil {
		log.Errorf("Failed to decode V1 field for repo, repo name: %s, tag: %s, error: %v", repoName, tag, err)
		ra.RenderError(http.StatusInternalServerError, "Internal Server Error")
		return
	}
	item.DurationDays = strconv.Itoa(int(time.Since(item.Created).Hours()/24)) + " days"

	ra.Data["json"] = item
	ra.ServeJSON()
}

func (ra *RepositoryAPI) initializeRepositoryClient(repoName string) (r *registry.Repository, err error) {
	u := models.User{
		UserID: ra.userID,
	}
	user, err := dao.GetUser(u)
	if err != nil {
		return nil, err
	}

	endpoint := os.Getenv("REGISTRY_URL")

	return registry.NewRepositoryWithUsername(repoName, endpoint, user.Username)
}
<|endoftext|>


<commit_before>package main

import (
	"errors"
	"github.com/fatih/color"
	"strings"
	"sync"
	"testing"
)

func TestConsoleReporterReport_ErrorAfterPassedExp_Reported(t *testing.T) {
	// given
	reportedError := "test err"
	results := []TestResult{
		{
			Traces: []*CallTrace{
				{
					ExpDesc:    map[string]bool{"Status code is 200": false},
					ErrorCause: errors.New(reportedError),
				},
			},
		}, // error without expectation description
	}

	writer := MockWriter{
		expectedWriting: reportedError,
	}

	reporter := &ConsoleReporter{ExitCode: 0, Writer: &writer, ioMutex: &sync.Mutex{}, LogHTTP: false}

	color.Output = &writer // prevent stdout and invalid test result parsing in IDE (reacts on words 'FAILED')

	// when
	reporter.Report(results)

	// then
	if !writer.passed() {
		t.Errorf("Expected writing %s was not met in %s", writer.expectedWriting, writer.actualWriting)
	}
}

type MockWriter struct {
	expectedWriting string
	actualWriting   string
}

func (mw *MockWriter) Write(p []byte) (n int, err error) {

	in := string(p)
	mw.actualWriting += in

	//os.Stdout.Write(p)

	return len(p), nil
}

func (mw *MockWriter) passed() bool {
	return strings.Contains(mw.actualWriting, mw.expectedWriting)
}
<commit_msg>#88 Unclear failure message in case of invalid 'remember' 'bodyPath' - put back replaced test<commit_after>package main

import (
	"errors"
	"github.com/fatih/color"
	"strings"
	"sync"
	"testing"
)

func TestConsoleReporterReport_ErrorAfterPassedExp_Reported(t *testing.T) {
	// given
	reportedError := "test err"
	results := []TestResult{
		{
			Traces: []*CallTrace{
				{
					ExpDesc:    map[string]bool{"Status code is 200": false},
					ErrorCause: errors.New(reportedError),
				},
			},
		}, // error without expectation description
	}

	writer := MockWriter{
		expectedWriting: reportedError,
	}

	reporter := &ConsoleReporter{ExitCode: 0, Writer: &writer, ioMutex: &sync.Mutex{}, LogHTTP: false}

	color.Output = &writer // prevent stdout and invalid test result parsing in IDE (reacts on words 'FAILED')

	// when
	reporter.Report(results)

	// then
	if !writer.passed() {
		t.Errorf("Expected writing %s was not met in %s", writer.expectedWriting, writer.actualWriting)
	}
}

type MockWriter struct {
	expectedWriting string
	actualWriting   string
}

func (mw *MockWriter) Write(p []byte) (n int, err error) {

	in := string(p)
	mw.actualWriting += in

	//os.Stdout.Write(p)

	return len(p), nil
}

func (mw *MockWriter) passed() bool {
	return strings.Contains(mw.actualWriting, mw.expectedWriting)
}

func TestJUnitReporterEmptyResults(t *testing.T) {
	// given

	// when
	NewJUnitReporter("").Report([]TestResult{})

	// then
	// no nil pointer panic
}
<|endoftext|>


<commit_before>package cmd

import (
	"bufio"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"os"
	"strings"
	"text/tabwriter"
	"time"

	"google.golang.org/grpc"
	"google.golang.org/grpc/grpclog"

	"github.com/pachyderm/pachyderm/src/client"
	"github.com/pachyderm/pachyderm/src/client/version"
	pfscmds "github.com/pachyderm/pachyderm/src/server/pfs/cmds"
	deploycmds "github.com/pachyderm/pachyderm/src/server/pkg/deploy/cmds"
	ppscmds "github.com/pachyderm/pachyderm/src/server/pps/cmds"
	"github.com/spf13/cobra"
	"go.pedge.io/lion"
	"go.pedge.io/pb/go/google/protobuf"
	"go.pedge.io/pkg/cobra"
	"go.pedge.io/pkg/exec"
	"go.pedge.io/proto/version"
	"golang.org/x/net/context"
)

// PachctlCmd takes a pachd host-address and creates a cobra.Command
// which may interact with the host.
func PachctlCmd(address string) (*cobra.Command, error) {
	var verbose bool
	rootCmd := &cobra.Command{
		Use: os.Args[0],
		Long: `Access the Pachyderm API.

Envronment variables:
  ADDRESS=0.0.0.0:30650, the server to connect to.
`,
		PersistentPreRun: func(cmd *cobra.Command, args []string) {
			if !verbose {
				// Silence any grpc logs
				grpclog.SetLogger(log.New(ioutil.Discard, "", 0))
				// Silence our FUSE logs
				lion.SetLevel(lion.LevelNone)
			}
		},
	}
	rootCmd.PersistentFlags().BoolVarP(&verbose, "verbose", "v", false, "Output verbose logs")

	pfsCmds := pfscmds.Cmds(address)
	for _, cmd := range pfsCmds {
		rootCmd.AddCommand(cmd)
	}
	ppsCmds, err := ppscmds.Cmds(address)
	if err != nil {
		return nil, sanitizeErr(err)
	}
	for _, cmd := range ppsCmds {
		rootCmd.AddCommand(cmd)
	}
	rootCmd.AddCommand(deploycmds.DeployCmd())

	version := &cobra.Command{
		Use:   "version",
		Short: "Return version information.",
		Long:  "Return version information.",
		Run: pkgcobra.RunFixedArgs(0, func(args []string) error {
			writer := tabwriter.NewWriter(os.Stdout, 20, 1, 3, ' ', 0)
			printVersionHeader(writer)
			printVersion(writer, "pachctl", version.Version)
			writer.Flush()

			versionClient, err := getVersionAPIClient(address)
			if err != nil {
				return sanitizeErr(err)
			}
			ctx, _ := context.WithTimeout(context.Background(), time.Second)
			version, err := versionClient.GetVersion(ctx, &google_protobuf.Empty{})

			if err != nil {
				fmt.Fprintf(writer, "pachd\t(version unknown) : error connecting to pachd server at address (%v): %v\n\nplease make sure pachd is up (`kubectl get all`) and portforwarding is enabled\n", address, sanitizeErr(err))
				return writer.Flush()
			}

			printVersion(writer, "pachd", version)
			return writer.Flush()
		}),
	}
	deleteAll := &cobra.Command{
		Use:   "delete-all",
		Short: "Delete everything.",
		Long: `Delete all repos, commits, files, pipelines and jobs.
This resets the cluster to its initial state.`,
		Run: pkgcobra.RunFixedArgs(0, func(args []string) error {
			client, err := client.NewFromAddress(address)
			if err != nil {
				return sanitizeErr(err)
			}
			fmt.Printf("Are you sure you want to delete all repos, commits, files, pipelines and jobs? yN\n")
			r := bufio.NewReader(os.Stdin)
			bytes, err := r.ReadBytes('\n')
			if err != nil {
				return err
			}
			if bytes[0] == 'y' || bytes[0] == 'Y' {
				return client.DeleteAll()
			}
			return nil
		}),
	}
	var port int
	portForward := &cobra.Command{
		Use:   "port-forward",
		Short: "Forward a port on the local machine to pachd. This command blocks.",
		Long:  "Forward a port on the local machine to pachd. This command blocks.",
		Run: pkgcobra.RunFixedArgs(0, func(args []string) error {
			stdin := strings.NewReader(fmt.Sprintf(`
set -x
pod=$(kubectl get pod -l app=pachd |  awk '{if (NR!=1) { print $1; exit 0 }}')
kubectl port-forward "$pod" %d:650
`, port))
			return pkgexec.RunStdin(stdin, "sh")
		}),
	}
	portForward.Flags().IntVarP(&port, "port", "p", 30650, "The local port to bind to.")
	rootCmd.AddCommand(version)
	rootCmd.AddCommand(deleteAll)
	rootCmd.AddCommand(portForward)
	return rootCmd, nil
}

func getVersionAPIClient(address string) (protoversion.APIClient, error) {
	clientConn, err := grpc.Dial(address, grpc.WithInsecure())
	if err != nil {
		return nil, err
	}
	return protoversion.NewAPIClient(clientConn), nil
}

func printVersionHeader(w io.Writer) {
	fmt.Fprintf(w, "COMPONENT\tVERSION\t\n")
}

func printVersion(w io.Writer, component string, v *protoversion.Version) {
	fmt.Fprintf(w, "%s\t%s\t\n", component, version.PrettyPrintVersion(v))
}

func sanitizeErr(err error) error {
	if err == nil {
		return nil
	}

	return errors.New(grpc.ErrorDesc(err))
}
<commit_msg>Don't swallow subcommands stderr.<commit_after>package cmd

import (
	"bufio"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"os"
	"strings"
	"text/tabwriter"
	"time"

	"google.golang.org/grpc"
	"google.golang.org/grpc/grpclog"

	"github.com/pachyderm/pachyderm/src/client"
	"github.com/pachyderm/pachyderm/src/client/version"
	pfscmds "github.com/pachyderm/pachyderm/src/server/pfs/cmds"
	deploycmds "github.com/pachyderm/pachyderm/src/server/pkg/deploy/cmds"
	ppscmds "github.com/pachyderm/pachyderm/src/server/pps/cmds"
	"github.com/spf13/cobra"
	"go.pedge.io/lion"
	"go.pedge.io/pb/go/google/protobuf"
	"go.pedge.io/pkg/cobra"
	"go.pedge.io/pkg/exec"
	"go.pedge.io/proto/version"
	"golang.org/x/net/context"
)

// PachctlCmd takes a pachd host-address and creates a cobra.Command
// which may interact with the host.
func PachctlCmd(address string) (*cobra.Command, error) {
	var verbose bool
	rootCmd := &cobra.Command{
		Use: os.Args[0],
		Long: `Access the Pachyderm API.

Envronment variables:
  ADDRESS=0.0.0.0:30650, the server to connect to.
`,
		PersistentPreRun: func(cmd *cobra.Command, args []string) {
			if !verbose {
				// Silence any grpc logs
				grpclog.SetLogger(log.New(ioutil.Discard, "", 0))
				// Silence our FUSE logs
				lion.SetLevel(lion.LevelNone)
			}
		},
	}
	rootCmd.PersistentFlags().BoolVarP(&verbose, "verbose", "v", false, "Output verbose logs")

	pfsCmds := pfscmds.Cmds(address)
	for _, cmd := range pfsCmds {
		rootCmd.AddCommand(cmd)
	}
	ppsCmds, err := ppscmds.Cmds(address)
	if err != nil {
		return nil, sanitizeErr(err)
	}
	for _, cmd := range ppsCmds {
		rootCmd.AddCommand(cmd)
	}
	rootCmd.AddCommand(deploycmds.DeployCmd())

	version := &cobra.Command{
		Use:   "version",
		Short: "Return version information.",
		Long:  "Return version information.",
		Run: pkgcobra.RunFixedArgs(0, func(args []string) error {
			writer := tabwriter.NewWriter(os.Stdout, 20, 1, 3, ' ', 0)
			printVersionHeader(writer)
			printVersion(writer, "pachctl", version.Version)
			writer.Flush()

			versionClient, err := getVersionAPIClient(address)
			if err != nil {
				return sanitizeErr(err)
			}
			ctx, _ := context.WithTimeout(context.Background(), time.Second)
			version, err := versionClient.GetVersion(ctx, &google_protobuf.Empty{})

			if err != nil {
				fmt.Fprintf(writer, "pachd\t(version unknown) : error connecting to pachd server at address (%v): %v\n\nplease make sure pachd is up (`kubectl get all`) and portforwarding is enabled\n", address, sanitizeErr(err))
				return writer.Flush()
			}

			printVersion(writer, "pachd", version)
			return writer.Flush()
		}),
	}
	deleteAll := &cobra.Command{
		Use:   "delete-all",
		Short: "Delete everything.",
		Long: `Delete all repos, commits, files, pipelines and jobs.
This resets the cluster to its initial state.`,
		Run: pkgcobra.RunFixedArgs(0, func(args []string) error {
			client, err := client.NewFromAddress(address)
			if err != nil {
				return sanitizeErr(err)
			}
			fmt.Printf("Are you sure you want to delete all repos, commits, files, pipelines and jobs? yN\n")
			r := bufio.NewReader(os.Stdin)
			bytes, err := r.ReadBytes('\n')
			if err != nil {
				return err
			}
			if bytes[0] == 'y' || bytes[0] == 'Y' {
				return client.DeleteAll()
			}
			return nil
		}),
	}
	var port int
	portForward := &cobra.Command{
		Use:   "port-forward",
		Short: "Forward a port on the local machine to pachd. This command blocks.",
		Long:  "Forward a port on the local machine to pachd. This command blocks.",
		Run: pkgcobra.RunFixedArgs(0, func(args []string) error {
			stdin := strings.NewReader(fmt.Sprintf(`
pod=$(kubectl get pod -l app=pachd |  awk '{if (NR!=1) { print $1; exit 0 }}')
kubectl port-forward "$pod" %d:650
`, port))
			return pkgexec.RunIO(pkgexec.IO{
				Stdin:  stdin,
				Stderr: os.Stderr,
			}, "sh")
		}),
	}
	portForward.Flags().IntVarP(&port, "port", "p", 30650, "The local port to bind to.")
	rootCmd.AddCommand(version)
	rootCmd.AddCommand(deleteAll)
	rootCmd.AddCommand(portForward)
	return rootCmd, nil
}

func getVersionAPIClient(address string) (protoversion.APIClient, error) {
	clientConn, err := grpc.Dial(address, grpc.WithInsecure())
	if err != nil {
		return nil, err
	}
	return protoversion.NewAPIClient(clientConn), nil
}

func printVersionHeader(w io.Writer) {
	fmt.Fprintf(w, "COMPONENT\tVERSION\t\n")
}

func printVersion(w io.Writer, component string, v *protoversion.Version) {
	fmt.Fprintf(w, "%s\t%s\t\n", component, version.PrettyPrintVersion(v))
}

func sanitizeErr(err error) error {
	if err == nil {
		return nil
	}

	return errors.New(grpc.ErrorDesc(err))
}
<|endoftext|>


<commit_before>/*
Copyright 2015 The Kubernetes Authors.

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

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

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

package remotecommand

import (
	"context"
	"fmt"
	"io"
	"net/http"
	"net/url"

	"k8s.io/klog/v2"

	"k8s.io/apimachinery/pkg/util/httpstream"
	"k8s.io/apimachinery/pkg/util/remotecommand"
	"k8s.io/apimachinery/pkg/util/runtime"
	restclient "k8s.io/client-go/rest"
	spdy "k8s.io/client-go/transport/spdy"
)

// StreamOptions holds information pertaining to the current streaming session:
// input/output streams, if the client is requesting a TTY, and a terminal size queue to
// support terminal resizing.
type StreamOptions struct {
	Stdin             io.Reader
	Stdout            io.Writer
	Stderr            io.Writer
	Tty               bool
	TerminalSizeQueue TerminalSizeQueue
}

// Executor is an interface for transporting shell-style streams.
type Executor interface {
	// Deprecated: use StreamWithContext instead to avoid possible resource leaks.
	// See https://github.com/kubernetes/kubernetes/pull/103177 for details.
	Stream(options StreamOptions) error

	// StreamWithContext initiates the transport of the standard shell streams. It will
	// transport any non-nil stream to a remote system, and return an error if a problem
	// occurs. If tty is set, the stderr stream is not used (raw TTY manages stdout and
	// stderr over the stdout stream).
	// The context controls the entire lifetime of stream execution.
	StreamWithContext(ctx context.Context, options StreamOptions) error
}

type streamCreator interface {
	CreateStream(headers http.Header) (httpstream.Stream, error)
}

type streamProtocolHandler interface {
	stream(conn streamCreator) error
}

// streamExecutor handles transporting standard shell streams over an httpstream connection.
type streamExecutor struct {
	upgrader  spdy.Upgrader
	transport http.RoundTripper

	method    string
	url       *url.URL
	protocols []string
}

// NewSPDYExecutor connects to the provided server and upgrades the connection to
// multiplexed bidirectional streams.
func NewSPDYExecutor(config *restclient.Config, method string, url *url.URL) (Executor, error) {
	wrapper, upgradeRoundTripper, err := spdy.RoundTripperFor(config)
	if err != nil {
		return nil, err
	}
	return NewSPDYExecutorForTransports(wrapper, upgradeRoundTripper, method, url)
}

// NewSPDYExecutorForTransports connects to the provided server using the given transport,
// upgrades the response using the given upgrader to multiplexed bidirectional streams.
func NewSPDYExecutorForTransports(transport http.RoundTripper, upgrader spdy.Upgrader, method string, url *url.URL) (Executor, error) {
	return NewSPDYExecutorForProtocols(
		transport, upgrader, method, url,
		remotecommand.StreamProtocolV4Name,
		remotecommand.StreamProtocolV3Name,
		remotecommand.StreamProtocolV2Name,
		remotecommand.StreamProtocolV1Name,
	)
}

// NewSPDYExecutorForProtocols connects to the provided server and upgrades the connection to
// multiplexed bidirectional streams using only the provided protocols. Exposed for testing, most
// callers should use NewSPDYExecutor or NewSPDYExecutorForTransports.
func NewSPDYExecutorForProtocols(transport http.RoundTripper, upgrader spdy.Upgrader, method string, url *url.URL, protocols ...string) (Executor, error) {
	return &streamExecutor{
		upgrader:  upgrader,
		transport: transport,
		method:    method,
		url:       url,
		protocols: protocols,
	}, nil
}

// Stream opens a protocol streamer to the server and streams until a client closes
// the connection or the server disconnects.
func (e *streamExecutor) Stream(options StreamOptions) error {
	return e.StreamWithContext(context.Background(), options)
}

// newConnectionAndStream creates a new SPDY connection and a stream protocol handler upon it.
func (e *streamExecutor) newConnectionAndStream(ctx context.Context, options StreamOptions) (httpstream.Connection, streamProtocolHandler, error) {
	req, err := http.NewRequestWithContext(ctx, e.method, e.url.String(), nil)
	if err != nil {
		return nil, nil, fmt.Errorf("error creating request: %v", err)
	}

	conn, protocol, err := spdy.Negotiate(
		e.upgrader,
		&http.Client{Transport: e.transport},
		req,
		e.protocols...,
	)
	if err != nil {
		return nil, nil, err
	}

	var streamer streamProtocolHandler

	switch protocol {
	case remotecommand.StreamProtocolV4Name:
		streamer = newStreamProtocolV4(options)
	case remotecommand.StreamProtocolV3Name:
		streamer = newStreamProtocolV3(options)
	case remotecommand.StreamProtocolV2Name:
		streamer = newStreamProtocolV2(options)
	case "":
		klog.V(4).Infof("The server did not negotiate a streaming protocol version. Falling back to %s", remotecommand.StreamProtocolV1Name)
		fallthrough
	case remotecommand.StreamProtocolV1Name:
		streamer = newStreamProtocolV1(options)
	}

	return conn, streamer, nil
}

// StreamWithContext opens a protocol streamer to the server and streams until a client closes
// the connection or the server disconnects or the context is done.
func (e *streamExecutor) StreamWithContext(ctx context.Context, options StreamOptions) error {
	conn, streamer, err := e.newConnectionAndStream(ctx, options)
	if err != nil {
		return err
	}
	defer conn.Close()

	errorChan := make(chan error, 1)
	go func() {
		defer runtime.HandleCrash()
		defer close(errorChan)
		errorChan <- streamer.stream(conn)
	}()

	select {
	case err := <-errorChan:
		return err
	case <-ctx.Done():
		return ctx.Err()
	}
}
<commit_msg>Propagate the panic with a channel<commit_after>/*
Copyright 2015 The Kubernetes Authors.

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

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

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

package remotecommand

import (
	"context"
	"fmt"
	"io"
	"net/http"
	"net/url"

	"k8s.io/klog/v2"

	"k8s.io/apimachinery/pkg/util/httpstream"
	"k8s.io/apimachinery/pkg/util/remotecommand"
	restclient "k8s.io/client-go/rest"
	"k8s.io/client-go/transport/spdy"
)

// StreamOptions holds information pertaining to the current streaming session:
// input/output streams, if the client is requesting a TTY, and a terminal size queue to
// support terminal resizing.
type StreamOptions struct {
	Stdin             io.Reader
	Stdout            io.Writer
	Stderr            io.Writer
	Tty               bool
	TerminalSizeQueue TerminalSizeQueue
}

// Executor is an interface for transporting shell-style streams.
type Executor interface {
	// Deprecated: use StreamWithContext instead to avoid possible resource leaks.
	// See https://github.com/kubernetes/kubernetes/pull/103177 for details.
	Stream(options StreamOptions) error

	// StreamWithContext initiates the transport of the standard shell streams. It will
	// transport any non-nil stream to a remote system, and return an error if a problem
	// occurs. If tty is set, the stderr stream is not used (raw TTY manages stdout and
	// stderr over the stdout stream).
	// The context controls the entire lifetime of stream execution.
	StreamWithContext(ctx context.Context, options StreamOptions) error
}

type streamCreator interface {
	CreateStream(headers http.Header) (httpstream.Stream, error)
}

type streamProtocolHandler interface {
	stream(conn streamCreator) error
}

// streamExecutor handles transporting standard shell streams over an httpstream connection.
type streamExecutor struct {
	upgrader  spdy.Upgrader
	transport http.RoundTripper

	method    string
	url       *url.URL
	protocols []string
}

// NewSPDYExecutor connects to the provided server and upgrades the connection to
// multiplexed bidirectional streams.
func NewSPDYExecutor(config *restclient.Config, method string, url *url.URL) (Executor, error) {
	wrapper, upgradeRoundTripper, err := spdy.RoundTripperFor(config)
	if err != nil {
		return nil, err
	}
	return NewSPDYExecutorForTransports(wrapper, upgradeRoundTripper, method, url)
}

// NewSPDYExecutorForTransports connects to the provided server using the given transport,
// upgrades the response using the given upgrader to multiplexed bidirectional streams.
func NewSPDYExecutorForTransports(transport http.RoundTripper, upgrader spdy.Upgrader, method string, url *url.URL) (Executor, error) {
	return NewSPDYExecutorForProtocols(
		transport, upgrader, method, url,
		remotecommand.StreamProtocolV4Name,
		remotecommand.StreamProtocolV3Name,
		remotecommand.StreamProtocolV2Name,
		remotecommand.StreamProtocolV1Name,
	)
}

// NewSPDYExecutorForProtocols connects to the provided server and upgrades the connection to
// multiplexed bidirectional streams using only the provided protocols. Exposed for testing, most
// callers should use NewSPDYExecutor or NewSPDYExecutorForTransports.
func NewSPDYExecutorForProtocols(transport http.RoundTripper, upgrader spdy.Upgrader, method string, url *url.URL, protocols ...string) (Executor, error) {
	return &streamExecutor{
		upgrader:  upgrader,
		transport: transport,
		method:    method,
		url:       url,
		protocols: protocols,
	}, nil
}

// Stream opens a protocol streamer to the server and streams until a client closes
// the connection or the server disconnects.
func (e *streamExecutor) Stream(options StreamOptions) error {
	return e.StreamWithContext(context.Background(), options)
}

// newConnectionAndStream creates a new SPDY connection and a stream protocol handler upon it.
func (e *streamExecutor) newConnectionAndStream(ctx context.Context, options StreamOptions) (httpstream.Connection, streamProtocolHandler, error) {
	req, err := http.NewRequestWithContext(ctx, e.method, e.url.String(), nil)
	if err != nil {
		return nil, nil, fmt.Errorf("error creating request: %v", err)
	}

	conn, protocol, err := spdy.Negotiate(
		e.upgrader,
		&http.Client{Transport: e.transport},
		req,
		e.protocols...,
	)
	if err != nil {
		return nil, nil, err
	}

	var streamer streamProtocolHandler

	switch protocol {
	case remotecommand.StreamProtocolV4Name:
		streamer = newStreamProtocolV4(options)
	case remotecommand.StreamProtocolV3Name:
		streamer = newStreamProtocolV3(options)
	case remotecommand.StreamProtocolV2Name:
		streamer = newStreamProtocolV2(options)
	case "":
		klog.V(4).Infof("The server did not negotiate a streaming protocol version. Falling back to %s", remotecommand.StreamProtocolV1Name)
		fallthrough
	case remotecommand.StreamProtocolV1Name:
		streamer = newStreamProtocolV1(options)
	}

	return conn, streamer, nil
}

// StreamWithContext opens a protocol streamer to the server and streams until a client closes
// the connection or the server disconnects or the context is done.
func (e *streamExecutor) StreamWithContext(ctx context.Context, options StreamOptions) error {
	conn, streamer, err := e.newConnectionAndStream(ctx, options)
	if err != nil {
		return err
	}
	defer conn.Close()

	panicChan := make(chan any, 1)
	errorChan := make(chan error, 1)
	go func() {
		defer func() {
			if p := recover(); p != nil {
				panicChan <- p
			}
		}()
		errorChan <- streamer.stream(conn)
	}()

	select {
	case p := <-panicChan:
		panic(p)
	case err := <-errorChan:
		return err
	case <-ctx.Done():
		return ctx.Err()
	}
}
<|endoftext|>


<commit_before>package builds

import (
	"net/http"
	"sync"
	"time"

	"github.com/winston-ci/redgreen/logbuffer"
)

type Build struct {
	Guid      string      `json:"guid"`
	CreatedAt time.Time   `json:"created_at"`
	Image     string      `json:"image"`
	Path      string      `json:"path"`
	Script    string      `json:"script"`
	Env       [][2]string `json:"env"`
	Status    string      `json:"status,omitempty"`

	bits        chan *http.Request
	servingBits *sync.WaitGroup

	logBuffer *logbuffer.LogBuffer
}

type BuildResult struct {
	Status string `json:"status"`
}
<commit_msg>remove unused fields<commit_after>package builds

import "time"

type Build struct {
	Guid      string      `json:"guid"`
	CreatedAt time.Time   `json:"created_at"`
	Image     string      `json:"image"`
	Path      string      `json:"path"`
	Script    string      `json:"script"`
	Env       [][2]string `json:"env"`
	Status    string      `json:"status,omitempty"`
}

type BuildResult struct {
	Status string `json:"status"`
}
<|endoftext|>


<commit_before>// Copyright 2015 realglobe, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// TA 間連携受け入れ代行。
package coop

import (
	"bytes"
	"crypto/tls"
	"encoding/json"
	"github.com/realglobe-Inc/edo-auth/database/token"
	keydb "github.com/realglobe-Inc/edo-id-provider/database/key"
	idpdb "github.com/realglobe-Inc/edo-idp-selector/database/idp"
	idperr "github.com/realglobe-Inc/edo-idp-selector/error"
	requtil "github.com/realglobe-Inc/edo-idp-selector/request"
	"github.com/realglobe-Inc/edo-lib/jwt"
	logutil "github.com/realglobe-Inc/edo-lib/log"
	"github.com/realglobe-Inc/edo-lib/rand"
	"github.com/realglobe-Inc/edo-lib/server"
	"github.com/realglobe-Inc/go-lib/erro"
	"github.com/realglobe-Inc/go-lib/rglog/level"
	"net"
	"net/http"
	"time"
)

type handler struct {
	stopper *server.Stopper

	selfId string
	sigAlg string
	sigKid string

	sessLabel  string
	tokTagLen  int
	tokDbExpIn time.Duration
	jtiLen     int
	jtiExpIn   time.Duration

	keyDb keydb.Db
	idpDb idpdb.Db
	tokDb token.Db

	noVeri bool

	idGen rand.Generator
}

func New(
	stopper *server.Stopper,
	selfId string,
	sigAlg string,
	sigKid string,
	sessLabel string,
	tokTagLen int,
	tokDbExpIn time.Duration,
	jtiLen int,
	jtiExpIn time.Duration,
	keyDb keydb.Db,
	idpDb idpdb.Db,
	tokDb token.Db,
	noVeri bool,
	idGen rand.Generator,
) http.Handler {
	return &handler{
		stopper:    stopper,
		selfId:     selfId,
		sigAlg:     sigAlg,
		sigKid:     sigKid,
		sessLabel:  sessLabel,
		tokTagLen:  tokTagLen,
		tokDbExpIn: tokDbExpIn,
		jtiLen:     jtiLen,
		jtiExpIn:   jtiExpIn,
		keyDb:      keyDb,
		idpDb:      idpDb,
		tokDb:      tokDb,
		noVeri:     noVeri,
		idGen:      idGen,
	}
}

// http.DefaultTransport を参考にした。
var noVeriTr = &http.Transport{
	Proxy: http.ProxyFromEnvironment,
	Dial: (&net.Dialer{
		Timeout:   30 * time.Second,
		KeepAlive: 30 * time.Second,
	}).Dial,
	TLSHandshakeTimeout: 10 * time.Second,
	TLSClientConfig:     &tls.Config{InsecureSkipVerify: true},
}

func (this *handler) httpClient() *http.Client {
	if this.noVeri {
		return &http.Client{Transport: noVeriTr}
	} else {
		return &http.Client{}
	}
}

func (this *handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	var sender *requtil.Request

	// panic 対策。
	defer func() {
		if rcv := recover(); rcv != nil {
			idperr.RespondApiError(w, r, erro.New(rcv), sender)
			return
		}
	}()

	if this.stopper != nil {
		this.stopper.Stop()
		defer this.stopper.Unstop()
	}

	//////////////////////////////
	server.LogRequest(level.DEBUG, r, true)
	//////////////////////////////

	sender = requtil.Parse(r, this.sessLabel)
	log.Info(sender, ": Received cooperation request")
	defer log.Info(sender, ": Handled cooperation request")

	if err := this.serve(w, r, sender); err != nil {
		idperr.RespondApiError(w, r, erro.Wrap(err), sender)
		return
	}
}

func (this *handler) serve(w http.ResponseWriter, r *http.Request, sender *requtil.Request) error {
	req, err := parseRequest(r)
	if err != nil {
		return erro.Wrap(idperr.New(idperr.Invalid_request, erro.Unwrap(err).Error(), http.StatusBadRequest, err))
	}

	var acntTag string
	tags := map[string]bool{}
	var refHash string
	type idpUnit struct {
		idp    idpdb.Element
		codTok *codeToken
	}
	units := []*idpUnit{}
	for _, rawCodTok := range req.codeTokens() {
		codTok, err := parseCodeToken(rawCodTok)
		if err != nil {
			return erro.Wrap(idperr.New(idperr.Invalid_request, erro.Unwrap(err).Error(), http.StatusBadRequest, err))
		} else if !codTok.audience()[this.selfId] {
			return erro.Wrap(idperr.New(idperr.Invalid_request, "invalid audience", http.StatusBadRequest, nil))
		} else if codTok.referralHash() == "" && len(req.codeTokens()) > 1 {
			return erro.Wrap(idperr.New(idperr.Invalid_request, "no referral hash", http.StatusBadRequest, nil))
		} else if codTok.accountTag() != "" {
			if acntTag != "" {
				return erro.Wrap(idperr.New(idperr.Invalid_request, "two main account tags", http.StatusBadRequest, nil))
			}
			acntTag = codTok.accountTag()
			log.Debug(sender, ": Main account tag is "+acntTag)
		}

		for tag := range codTok.accountTags() {
			if tags[tag] {
				return erro.Wrap(idperr.New(idperr.Invalid_request, "tag "+tag+" overlaps", http.StatusBadRequest, nil))
			}
			tags[tag] = true
			log.Debug(sender, ": Account tag is "+tag)
		}

		if codTok.referralHash() != "" {
			if refHash == "" {
				refHash = codTok.referralHash()
			} else if codTok.referralHash() != refHash {
				return erro.Wrap(idperr.New(idperr.Invalid_request, "invalid referral hash", http.StatusBadRequest, nil))
			}
		}

		var idp idpdb.Element
		if idp, err = this.idpDb.Get(codTok.idProvider()); err != nil {
			return erro.Wrap(err)
		} else if idp == nil {
			return erro.Wrap(idperr.New(idperr.Invalid_request, "ID provider "+codTok.idProvider()+" is not exist", http.StatusBadRequest, nil))
		}

		log.Debug(sender, ": ID provider "+idp.Id()+" is exist")

		if err := codTok.verify(idp.Keys()); err != nil {
			return erro.Wrap(idperr.New(idperr.Invalid_request, erro.Unwrap(err).Error(), http.StatusBadRequest, err))
		}

		log.Debug(sender, ": Verified cooperation code")

		units = append(units, &idpUnit{idp, codTok})
	}
	if acntTag == "" {
		return erro.Wrap(idperr.New(idperr.Invalid_request, "no main account tag", http.StatusBadRequest, nil))
	}

	log.Debug(sender, ": Cooperation codes are OK")

	var tok *token.Element
	var mainAttrs map[string]interface{}
	tagToAttrs := map[string]map[string]interface{}{}
	var frTa string
	for _, unit := range units {
		var tToA map[string]map[string]interface{}
		var fT string
		if unit.codTok.accountTag() != "" {
			fT, tok, tToA, err = this.getInfoFromMainIdProvider(unit.idp, unit.codTok, sender)
			if err != nil {
				return erro.Wrap(err)
			}
			log.Debug(sender, ": Got account info from main ID provider "+unit.idp.Id())
		} else {
			fT, tToA, err = this.getInfoFromSubIdProvider(sender)
			if err != nil {
				return erro.Wrap(err)
			}
			log.Debug(sender, ": Got account info from sub ID provider "+unit.idp.Id())
		}
		for tag, attrs := range tToA {
			if tag == acntTag {
				attrs[tagIss] = unit.idp.Id()
				attrs[tagAt_tag] = tok.Tag()
				attrs[tagAt_exp] = tok.Expires().Unix()
				mainAttrs = attrs
			} else {
				attrs[tagIss] = unit.idp.Id()
				tagToAttrs[tag] = attrs
			}
		}
		if frTa == "" {
			frTa = fT
		} else if frTa != fT {
			return erro.Wrap(idperr.New(idperr.Invalid_request, "two from-TA ID", http.StatusBadRequest, nil))
		}
	}

	log.Debug(sender, ": Got all account info")

	jt := jwt.New()
	jt.SetHeader(tagAlg, tagNone)
	for k, v := range mainAttrs {
		jt.SetClaim(k, v)
	}
	mainInfo, err := jt.Encode()
	if err != nil {
		return erro.Wrap(err)
	}

	var relInfo []byte
	if len(tagToAttrs) > 0 {
		jt = jwt.New()
		jt.SetHeader(tagAlg, tagNone)
		for k, v := range tagToAttrs {
			jt.SetClaim(k, v)
		}
		relInfo, err = jt.Encode()
		if err != nil {
			return erro.Wrap(err)
		}
	}

	w.Header().Set(tagX_auth_user, string(mainInfo))
	w.Header().Set(tagX_auth_user_tag, acntTag)
	w.Header().Set(tagX_auth_from_id, frTa)
	if relInfo != nil {
		w.Header().Set(tagX_auth_users, string(relInfo))
	}

	return nil
}

func (this *handler) getInfoFromMainIdProvider(idp idpdb.Element, codTok *codeToken, sender *requtil.Request) (frTa string, tok *token.Element, tagToAttrs map[string]map[string]interface{}, err error) {
	params := map[string]interface{}{}

	// grant_type
	params[tagGrant_type] = tagCooperation_code

	// code
	params[tagCode] = codTok.code()

	// claims
	// TODO 受け取り方を考えないと。

	// user_claims
	// TODO 受け取り方を考えないと。

	// client_assertion_type
	params[tagClient_assertion_type] = cliAssTypeJwt_bearer

	// client_assertion
	keys, err := this.keyDb.Get()
	if err != nil {
		return "", nil, nil, erro.Wrap(err)
	}

	{
		jt := jwt.New()
		jt.SetHeader(tagAlg, this.sigAlg)
		if this.sigKid != "" {
			jt.SetHeader(tagKid, this.sigKid)
		}
		jt.SetClaim(tagIss, this.selfId)
		jt.SetClaim(tagSub, this.selfId)
		jt.SetClaim(tagAud, idp.CoopToUri())
		jt.SetClaim(tagJti, this.idGen.String(this.jtiLen))
		now := time.Now()
		jt.SetClaim(tagExp, now.Add(this.jtiExpIn).Unix())
		jt.SetClaim(tagIat, now.Unix())
		if err := jt.Sign(keys); err != nil {
			return "", nil, nil, erro.Wrap(err)
		}
		assData, err := jt.Encode()
		if err != nil {
			return "", nil, nil, erro.Wrap(err)
		}
		params[tagClient_assertion] = string(assData)
	}

	data, err := json.Marshal(params)
	if err != nil {
		return "", nil, nil, erro.Wrap(err)
	}

	r, err := http.NewRequest("POST", idp.CoopToUri(), bytes.NewReader(data))
	r.Header.Set(tagContent_type, contTypeJson)
	log.Debug(sender, ": Made main cooperation-to request")

	server.LogRequest(level.DEBUG, r, true)
	resp, err := this.httpClient().Do(r)
	if err != nil {
		return "", nil, nil, erro.Wrap(err)
	}
	defer resp.Body.Close()
	server.LogResponse(level.DEBUG, resp, true)

	coopResp, err := parseCoopResponse(resp)
	if err != nil {
		return "", nil, nil, erro.Wrap(idperr.New(idperr.Access_denied, erro.Unwrap(err).Error(), http.StatusForbidden, err))
	}

	idsTok, err := parseIdsToken(coopResp.idsToken())
	if err != nil {
		return "", nil, nil, erro.Wrap(idperr.New(idperr.Access_denied, erro.Unwrap(err).Error(), http.StatusForbidden, err))
	} else if err := idsTok.verify(idp.Keys()); err != nil {
		return "", nil, nil, erro.Wrap(idperr.New(idperr.Access_denied, erro.Unwrap(err).Error(), http.StatusForbidden, err))
	}

	now := time.Now()
	tok = token.New(coopResp.token(), this.idGen.String(this.tokTagLen), now.Add(coopResp.expiresIn()), idsTok.idProvider(), coopResp.scope())
	log.Info(sender, ": Got access token "+logutil.Mosaic(tok.Id()))

	if err := this.tokDb.Save(tok, now.Add(this.tokDbExpIn)); err != nil {
		return "", nil, nil, erro.Wrap(err)
	}
	log.Info(sender, ": Saved access token "+logutil.Mosaic(tok.Id()))

	return idsTok.fromTa(), tok, idsTok.attributes(), nil
}

func (this *handler) getInfoFromSubIdProvider(sender *requtil.Request) (frTa string, tagToAttrs map[string]map[string]interface{}, err error) {
	panic("not yet implemented")
}
<commit_msg>ID プロバイダからエラーを受け取ったらそれを返すようにした<commit_after>// Copyright 2015 realglobe, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// TA 間連携受け入れ代行。
package coop

import (
	"bytes"
	"crypto/tls"
	"encoding/json"
	"github.com/realglobe-Inc/edo-auth/database/token"
	keydb "github.com/realglobe-Inc/edo-id-provider/database/key"
	idpdb "github.com/realglobe-Inc/edo-idp-selector/database/idp"
	idperr "github.com/realglobe-Inc/edo-idp-selector/error"
	requtil "github.com/realglobe-Inc/edo-idp-selector/request"
	"github.com/realglobe-Inc/edo-lib/jwt"
	logutil "github.com/realglobe-Inc/edo-lib/log"
	"github.com/realglobe-Inc/edo-lib/rand"
	"github.com/realglobe-Inc/edo-lib/server"
	"github.com/realglobe-Inc/go-lib/erro"
	"github.com/realglobe-Inc/go-lib/rglog/level"
	"net"
	"net/http"
	"time"
)

type handler struct {
	stopper *server.Stopper

	selfId string
	sigAlg string
	sigKid string

	sessLabel  string
	tokTagLen  int
	tokDbExpIn time.Duration
	jtiLen     int
	jtiExpIn   time.Duration

	keyDb keydb.Db
	idpDb idpdb.Db
	tokDb token.Db

	noVeri bool

	idGen rand.Generator
}

func New(
	stopper *server.Stopper,
	selfId string,
	sigAlg string,
	sigKid string,
	sessLabel string,
	tokTagLen int,
	tokDbExpIn time.Duration,
	jtiLen int,
	jtiExpIn time.Duration,
	keyDb keydb.Db,
	idpDb idpdb.Db,
	tokDb token.Db,
	noVeri bool,
	idGen rand.Generator,
) http.Handler {
	return &handler{
		stopper:    stopper,
		selfId:     selfId,
		sigAlg:     sigAlg,
		sigKid:     sigKid,
		sessLabel:  sessLabel,
		tokTagLen:  tokTagLen,
		tokDbExpIn: tokDbExpIn,
		jtiLen:     jtiLen,
		jtiExpIn:   jtiExpIn,
		keyDb:      keyDb,
		idpDb:      idpDb,
		tokDb:      tokDb,
		noVeri:     noVeri,
		idGen:      idGen,
	}
}

// http.DefaultTransport を参考にした。
var noVeriTr = &http.Transport{
	Proxy: http.ProxyFromEnvironment,
	Dial: (&net.Dialer{
		Timeout:   30 * time.Second,
		KeepAlive: 30 * time.Second,
	}).Dial,
	TLSHandshakeTimeout: 10 * time.Second,
	TLSClientConfig:     &tls.Config{InsecureSkipVerify: true},
}

func (this *handler) httpClient() *http.Client {
	if this.noVeri {
		return &http.Client{Transport: noVeriTr}
	} else {
		return &http.Client{}
	}
}

func (this *handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	var sender *requtil.Request

	// panic 対策。
	defer func() {
		if rcv := recover(); rcv != nil {
			idperr.RespondApiError(w, r, erro.New(rcv), sender)
			return
		}
	}()

	if this.stopper != nil {
		this.stopper.Stop()
		defer this.stopper.Unstop()
	}

	//////////////////////////////
	server.LogRequest(level.DEBUG, r, true)
	//////////////////////////////

	sender = requtil.Parse(r, this.sessLabel)
	log.Info(sender, ": Received cooperation request")
	defer log.Info(sender, ": Handled cooperation request")

	if err := this.serve(w, r, sender); err != nil {
		idperr.RespondApiError(w, r, erro.Wrap(err), sender)
		return
	}
}

func (this *handler) serve(w http.ResponseWriter, r *http.Request, sender *requtil.Request) error {
	req, err := parseRequest(r)
	if err != nil {
		return erro.Wrap(idperr.New(idperr.Invalid_request, erro.Unwrap(err).Error(), http.StatusBadRequest, err))
	}

	var acntTag string
	tags := map[string]bool{}
	var refHash string
	type idpUnit struct {
		idp    idpdb.Element
		codTok *codeToken
	}
	units := []*idpUnit{}
	for _, rawCodTok := range req.codeTokens() {
		codTok, err := parseCodeToken(rawCodTok)
		if err != nil {
			return erro.Wrap(idperr.New(idperr.Invalid_request, erro.Unwrap(err).Error(), http.StatusBadRequest, err))
		} else if !codTok.audience()[this.selfId] {
			return erro.Wrap(idperr.New(idperr.Invalid_request, "invalid audience", http.StatusBadRequest, nil))
		} else if codTok.referralHash() == "" && len(req.codeTokens()) > 1 {
			return erro.Wrap(idperr.New(idperr.Invalid_request, "no referral hash", http.StatusBadRequest, nil))
		} else if codTok.accountTag() != "" {
			if acntTag != "" {
				return erro.Wrap(idperr.New(idperr.Invalid_request, "two main account tags", http.StatusBadRequest, nil))
			}
			acntTag = codTok.accountTag()
			log.Debug(sender, ": Main account tag is "+acntTag)
		}

		for tag := range codTok.accountTags() {
			if tags[tag] {
				return erro.Wrap(idperr.New(idperr.Invalid_request, "tag "+tag+" overlaps", http.StatusBadRequest, nil))
			}
			tags[tag] = true
			log.Debug(sender, ": Account tag is "+tag)
		}

		if codTok.referralHash() != "" {
			if refHash == "" {
				refHash = codTok.referralHash()
			} else if codTok.referralHash() != refHash {
				return erro.Wrap(idperr.New(idperr.Invalid_request, "invalid referral hash", http.StatusBadRequest, nil))
			}
		}

		var idp idpdb.Element
		if idp, err = this.idpDb.Get(codTok.idProvider()); err != nil {
			return erro.Wrap(err)
		} else if idp == nil {
			return erro.Wrap(idperr.New(idperr.Invalid_request, "ID provider "+codTok.idProvider()+" is not exist", http.StatusBadRequest, nil))
		}

		log.Debug(sender, ": ID provider "+idp.Id()+" is exist")

		if err := codTok.verify(idp.Keys()); err != nil {
			return erro.Wrap(idperr.New(idperr.Invalid_request, erro.Unwrap(err).Error(), http.StatusBadRequest, err))
		}

		log.Debug(sender, ": Verified cooperation code")

		units = append(units, &idpUnit{idp, codTok})
	}
	if acntTag == "" {
		return erro.Wrap(idperr.New(idperr.Invalid_request, "no main account tag", http.StatusBadRequest, nil))
	}

	log.Debug(sender, ": Cooperation codes are OK")

	var tok *token.Element
	var mainAttrs map[string]interface{}
	tagToAttrs := map[string]map[string]interface{}{}
	var frTa string
	for _, unit := range units {
		var tToA map[string]map[string]interface{}
		var fT string
		if unit.codTok.accountTag() != "" {
			fT, tok, tToA, err = this.getInfoFromMainIdProvider(unit.idp, unit.codTok, sender)
			if err != nil {
				return erro.Wrap(err)
			}
			log.Debug(sender, ": Got account info from main ID provider "+unit.idp.Id())
		} else {
			fT, tToA, err = this.getInfoFromSubIdProvider(sender)
			if err != nil {
				return erro.Wrap(err)
			}
			log.Debug(sender, ": Got account info from sub ID provider "+unit.idp.Id())
		}
		for tag, attrs := range tToA {
			if tag == acntTag {
				attrs[tagIss] = unit.idp.Id()
				attrs[tagAt_tag] = tok.Tag()
				attrs[tagAt_exp] = tok.Expires().Unix()
				mainAttrs = attrs
			} else {
				attrs[tagIss] = unit.idp.Id()
				tagToAttrs[tag] = attrs
			}
		}
		if frTa == "" {
			frTa = fT
		} else if frTa != fT {
			return erro.Wrap(idperr.New(idperr.Invalid_request, "two from-TA ID", http.StatusBadRequest, nil))
		}
	}

	log.Debug(sender, ": Got all account info")

	jt := jwt.New()
	jt.SetHeader(tagAlg, tagNone)
	for k, v := range mainAttrs {
		jt.SetClaim(k, v)
	}
	mainInfo, err := jt.Encode()
	if err != nil {
		return erro.Wrap(err)
	}

	var relInfo []byte
	if len(tagToAttrs) > 0 {
		jt = jwt.New()
		jt.SetHeader(tagAlg, tagNone)
		for k, v := range tagToAttrs {
			jt.SetClaim(k, v)
		}
		relInfo, err = jt.Encode()
		if err != nil {
			return erro.Wrap(err)
		}
	}

	w.Header().Set(tagX_auth_user, string(mainInfo))
	w.Header().Set(tagX_auth_user_tag, acntTag)
	w.Header().Set(tagX_auth_from_id, frTa)
	if relInfo != nil {
		w.Header().Set(tagX_auth_users, string(relInfo))
	}

	return nil
}

func (this *handler) getInfoFromMainIdProvider(idp idpdb.Element, codTok *codeToken, sender *requtil.Request) (frTa string, tok *token.Element, tagToAttrs map[string]map[string]interface{}, err error) {
	params := map[string]interface{}{}

	// grant_type
	params[tagGrant_type] = tagCooperation_code

	// code
	params[tagCode] = codTok.code()

	// claims
	// TODO 受け取り方を考えないと。

	// user_claims
	// TODO 受け取り方を考えないと。

	// client_assertion_type
	params[tagClient_assertion_type] = cliAssTypeJwt_bearer

	// client_assertion
	keys, err := this.keyDb.Get()
	if err != nil {
		return "", nil, nil, erro.Wrap(err)
	}

	{
		jt := jwt.New()
		jt.SetHeader(tagAlg, this.sigAlg)
		if this.sigKid != "" {
			jt.SetHeader(tagKid, this.sigKid)
		}
		jt.SetClaim(tagIss, this.selfId)
		jt.SetClaim(tagSub, this.selfId)
		jt.SetClaim(tagAud, idp.CoopToUri())
		jt.SetClaim(tagJti, this.idGen.String(this.jtiLen))
		now := time.Now()
		jt.SetClaim(tagExp, now.Add(this.jtiExpIn).Unix())
		jt.SetClaim(tagIat, now.Unix())
		if err := jt.Sign(keys); err != nil {
			return "", nil, nil, erro.Wrap(err)
		}
		assData, err := jt.Encode()
		if err != nil {
			return "", nil, nil, erro.Wrap(err)
		}
		params[tagClient_assertion] = string(assData)
	}

	data, err := json.Marshal(params)
	if err != nil {
		return "", nil, nil, erro.Wrap(err)
	}

	r, err := http.NewRequest("POST", idp.CoopToUri(), bytes.NewReader(data))
	r.Header.Set(tagContent_type, contTypeJson)
	log.Debug(sender, ": Made main cooperation-to request")

	server.LogRequest(level.DEBUG, r, true)
	resp, err := this.httpClient().Do(r)
	if err != nil {
		return "", nil, nil, erro.Wrap(err)
	}
	defer resp.Body.Close()
	server.LogResponse(level.DEBUG, resp, true)

	if resp.StatusCode != http.StatusOK {
		var buff struct {
			Error             string
			Error_description string
		}
		if err := json.NewDecoder(resp.Body).Decode(&buff); err != nil {
			return "", nil, nil, erro.Wrap(err)
		}
		return "", nil, nil, erro.Wrap(idperr.New(buff.Error, buff.Error_description, resp.StatusCode, nil))
	}
	coopResp, err := parseCoopResponse(resp)
	if err != nil {
		return "", nil, nil, erro.Wrap(idperr.New(idperr.Access_denied, erro.Unwrap(err).Error(), http.StatusForbidden, err))
	}

	idsTok, err := parseIdsToken(coopResp.idsToken())
	if err != nil {
		return "", nil, nil, erro.Wrap(idperr.New(idperr.Access_denied, erro.Unwrap(err).Error(), http.StatusForbidden, err))
	} else if err := idsTok.verify(idp.Keys()); err != nil {
		return "", nil, nil, erro.Wrap(idperr.New(idperr.Access_denied, erro.Unwrap(err).Error(), http.StatusForbidden, err))
	}

	now := time.Now()
	tok = token.New(coopResp.token(), this.idGen.String(this.tokTagLen), now.Add(coopResp.expiresIn()), idsTok.idProvider(), coopResp.scope())
	log.Info(sender, ": Got access token "+logutil.Mosaic(tok.Id()))

	if err := this.tokDb.Save(tok, now.Add(this.tokDbExpIn)); err != nil {
		return "", nil, nil, erro.Wrap(err)
	}
	log.Info(sender, ": Saved access token "+logutil.Mosaic(tok.Id()))

	return idsTok.fromTa(), tok, idsTok.attributes(), nil
}

func (this *handler) getInfoFromSubIdProvider(sender *requtil.Request) (frTa string, tagToAttrs map[string]map[string]interface{}, err error) {
	panic("not yet implemented")
}
<|endoftext|>


<commit_before>package game_engine

import (
    "fmt"
    "encoding/json"
    "errors"
    "requests"
)

type Game struct {
    terrain [][]terrain
    unitMap map[location]*unit
    players []*player
    numPlayers int
    turnOwner int
}

func NewGame(playerIds []int, worldId int) (*Game, error) {
    numPlayers := len(playerIds)
    if numPlayers > 4 || numPlayers < 1 {
        return nil, errors.New("must have between 1 and 4 players")
    }
    players := make([]*player, numPlayers)
    for i, playerId := range(playerIds) {
        players[i] = newPlayer(playerId, nation(i), team(i))
    }
    ret_game := &Game{
        terrain: [][]terrain{
            []terrain{plains, plains, plains, plains, plains, plains, plains, plains},
            []terrain{plains, plains, plains, plains, plains, plains, plains, plains},
            []terrain{plains, plains, plains, plains, plains, plains, plains, plains},
            []terrain{plains, plains, plains, plains, plains, plains, plains, plains},
            []terrain{plains, plains, plains, plains, plains, plains, plains, plains},
            []terrain{plains, plains, plains, plains, plains, plains, plains, plains}},
        unitMap: make(map[location]*unit),
        players: players,
        numPlayers: numPlayers,
        turnOwner: 0}
    ret_game.AddUnit(newLocation(0, 0), tank(red))
    return ret_game, nil
}

func (game *Game) verifyTurnOwner(playerId int) error {
    if playerId != game.players[game.turnOwner].playerId {
        return errors.New("Not the turn owner")
    }
    return nil
}

func (game *Game) AddUnit(location location, unit *unit) error {
    fmt.Println("adding unit")
    _, ok := game.unitMap[location]; if !ok {
        game.unitMap[location] = unit
        fmt.Println("added unit")
        return nil
    } else {
        fmt.Println("failed to add unit")
        return errors.New("location already occupied")
    }
}

func (game *Game) EndTurn(playerId int) error {
    ownerError := game.verifyTurnOwner(playerId)
    if ownerError != nil {
        return ownerError
    }
    nextOwner := game.turnOwner + 1
    if nextOwner >= game.numPlayers {
        game.turnOwner = 0
    } else {
        game.turnOwner = nextOwner
    }
    return nil
}

func (game *Game) MoveUnit(playerId int, rawLocations []requests.LocationStruct) error {
    ownerError := game.verifyTurnOwner(playerId)
    if ownerError != nil {
        return ownerError
    }
    locations := make([]location, len(rawLocations))
    for i, location := range(rawLocations) {
        locations[i] = newLocation(location.X, location.Y)
    }
    if len(locations) < 1 {
        message := "must supply more than zero  locations"
        fmt.Println(message)
        return errors.New(message)
    }

    tiles := make([]terrain, len(locations))
    for i, location := range(locations) {
        tiles[i] = game.terrain[location.x][location.y]
    }
    //fmt.Println(tiles)
    //fmt.Println(game.unitMap)
    //fmt.Println(locations)
    unit, ok := game.unitMap[locations[0]]; if ok {
        moveErr := validMove(
            unit.movement.distance,
            unit.movement,
            tiles,
            locations)
        if moveErr == nil {
            end := len(locations)
            unit = game.unitMap[newLocation(locations[0].x, locations[0].y)]
            game.unitMap[newLocation(locations[end-1].x, locations[end-1].y)] = unit
            delete(game.unitMap, newLocation(locations[0].x, locations[0].y))
            return nil
        } else {
            fmt.Println(moveErr)
            return moveErr
        }
    } else {
        message := "Invalid starting location"
        fmt.Println(message)
        return errors.New(message)
    }
}

func (game *Game) Serialize(playerId int) ([]byte, error) {
    players := make([]*requests.PlayerStruct, len(game.players))
    for i, player := range(game.players) {
        players[i] = player.serialize()
    }
    terrainInts := make([][]int, len(game.terrain))
    for i, t := range(game.terrain) {
        thoriz := make([]int, len(t))
        for j, t_ := range(t) {
            thoriz[j] = int(t_)
        }
        terrainInts[i] = thoriz
    }
    units := make([]*requests.UnitStruct, len(game.unitMap))
    i := 0
    for location, unit := range(game.unitMap) {
        units[i] = unit.serialize(location)
        i += 1
    }
    return json.Marshal(requests.WorldStruct{
        Terrain: terrainInts,
        Units: units,
        Players: players,
        TurnOwner: game.players[game.turnOwner].playerId})
}
<commit_msg>Initial worldId recognition and cleanup<commit_after>package game_engine

import (
    "fmt"
    "encoding/json"
    "errors"
    "requests"
)

type Game struct {
    terrain [][]terrain
    unitMap map[location]*unit
    players []*player
    numPlayers int
    turnOwner int
}

func NewGame(playerIds []int, worldId int) (*Game, error) {
    numPlayers := len(playerIds)
    if numPlayers > 4 || numPlayers < 1 {
        return nil, errors.New("must have between 1 and 4 players")
    }
    players := make([]*player, numPlayers)
    for i, playerId := range(playerIds) {
        players[i] = newPlayer(playerId, nation(i), team(i))
    }
    ret_game := &Game{
        terrain: [][]terrain{
            []terrain{plains, plains, plains, plains, plains, plains, plains, plains},
            []terrain{plains, plains, plains, plains, plains, plains, plains, plains},
            []terrain{plains, plains, plains, plains, plains, plains, plains, plains},
            []terrain{plains, plains, plains, plains, plains, plains, plains, plains},
            []terrain{plains, plains, plains, plains, plains, plains, plains, plains},
            []terrain{plains, plains, plains, plains, plains, plains, plains, plains}},
        unitMap: make(map[location]*unit),
        players: players,
        numPlayers: numPlayers,
        turnOwner: 0}
    if worldId == 0 {
        ret_game.AddUnit(newLocation(0, 0), tank(red))
    }
    return ret_game, nil
}

func (game *Game) verifyTurnOwner(playerId int) error {
    if playerId != game.players[game.turnOwner].playerId {
        return errors.New("Not the turn owner")
    }
    return nil
}

func (game *Game) AddUnit(location location, unit *unit) error {
    fmt.Println("adding unit")
    _, ok := game.unitMap[location]; if !ok {
        game.unitMap[location] = unit
        fmt.Println("added unit")
        return nil
    } else {
        fmt.Println("failed to add unit")
        return errors.New("location already occupied")
    }
}

func (game *Game) EndTurn(playerId int) error {
    ownerError := game.verifyTurnOwner(playerId)
    if ownerError != nil {
        return ownerError
    }
    nextOwner := game.turnOwner + 1
    if nextOwner >= game.numPlayers {
        game.turnOwner = 0
    } else {
        game.turnOwner = nextOwner
    }
    return nil
}

func (game *Game) MoveUnit(playerId int, rawLocations []requests.LocationStruct) error {
    ownerError := game.verifyTurnOwner(playerId)
    if ownerError != nil {
        return ownerError
    }
    locations := make([]location, len(rawLocations))
    for i, location := range(rawLocations) {
        locations[i] = newLocation(location.X, location.Y)
    }
    if len(locations) < 1 {
        message := "must supply more than zero  locations"
        fmt.Println(message)
        return errors.New(message)
    }

    tiles := make([]terrain, len(locations))
    for i, location := range(locations) {
        tiles[i] = game.terrain[location.x][location.y]
    }
    //fmt.Println(tiles)
    //fmt.Println(game.unitMap)
    //fmt.Println(locations)
    unit, ok := game.unitMap[locations[0]]; if ok {
        moveErr := validMove(
            unit.movement.distance,
            unit.movement, tiles, locations)
        if moveErr == nil {
            end := len(locations)
            unit = game.unitMap[newLocation(locations[0].x, locations[0].y)]
            game.unitMap[newLocation(locations[end-1].x, locations[end-1].y)] = unit
            delete(game.unitMap, newLocation(locations[0].x, locations[0].y))
            return nil
        } else {
            fmt.Println(moveErr)
            return moveErr
        }
    } else {
        message := "Invalid starting location"
        fmt.Println(message)
        return errors.New(message)
    }
}

func (game *Game) Serialize(playerId int) ([]byte, error) {
    players := make([]*requests.PlayerStruct, len(game.players))
    for i, player := range(game.players) {
        players[i] = player.serialize()
    }
    terrainInts := make([][]int, len(game.terrain))
    for i, t := range(game.terrain) {
        thoriz := make([]int, len(t))
        for j, t_ := range(t) {
            thoriz[j] = int(t_)
        }
        terrainInts[i] = thoriz
    }
    units := make([]*requests.UnitStruct, len(game.unitMap))
    i := 0
    for location, unit := range(game.unitMap) {
        units[i] = unit.serialize(location)
        i += 1
    }
    return json.Marshal(requests.WorldStruct{
        Terrain: terrainInts,
        Units: units,
        Players: players,
        TurnOwner: game.players[game.turnOwner].playerId})
}
<|endoftext|>


<commit_before>// Copyright 2016 The Go Authors. All rights reserved.
// Use of this source code is governed by
// license that can be found in the LICENSE file.

// Package daemon windows version
package daemon

import (
	"errors"
	"fmt"
	"os/exec"
	"syscall"
	"unicode/utf16"
	"unsafe"
	"regexp"
)

// windowsRecord - standard record (struct) for windows version of daemon package
type windowsRecord struct {
	name         string
	description  string
	dependencies []string
}

func newDaemon(name, description string, dependencies []string) (Daemon, error) {

	return &windowsRecord{name, description, dependencies}, nil
}

// Install the service
func (windows *windowsRecord) Install(args ...string) (string, error) {
	installAction := "Install " + windows.description + ":"

	execp, err := execPath()

	if err != nil {
		return installAction + failed, err
	}

	cmdArgs := []string{"create", windows.name, "start=auto", "binPath="+execp}
	cmdArgs = append(cmdArgs, args...)

	cmd := exec.Command("sc", cmdArgs...)
	_, err = cmd.Output()
	if exiterr, ok := err.(*exec.ExitError); ok {
		if status, ok := exiterr.Sys().(syscall.WaitStatus); ok {
			return installAction + failed, getWindowsError(status.ExitCode)
		}
	}
	return installAction + " completed.", nil
}

// Remove the service
func (windows *windowsRecord) Remove() (string, error) {
	removeAction := "Removing " + windows.description + ":"
	cmd := exec.Command("sc", "delete", windows.name, "confirm")
	err := cmd.Run()
	if exiterr, ok := err.(*exec.ExitError); ok {
		if status, ok := exiterr.Sys().(syscall.WaitStatus); ok {
			return removeAction + failed, getWindowsError(status.ExitCode)
		}
	}
	return removeAction + " completed.", nil
}

// Start the service
func (windows *windowsRecord) Start() (string, error) {
	startAction := "Starting " + windows.description + ":"
	cmd := exec.Command("sc", "start", windows.name)
	err := cmd.Run()
	if exiterr, ok := err.(*exec.ExitError); ok {
		if status, ok := exiterr.Sys().(syscall.WaitStatus); ok {
			return startAction + failed, getWindowsError(status.ExitCode)
		}
	}
	return startAction + " completed.", nil
}

// Stop the service
func (windows *windowsRecord) Stop() (string, error) {
	stopAction := "Stopping " + windows.description + ":"
	cmd := exec.Command("sc", "stop", windows.name)
	err := cmd.Run()
	if exiterr, ok := err.(*exec.ExitError); ok {
		if status, ok := exiterr.Sys().(syscall.WaitStatus); ok {
			return stopAction + failed, getWindowsError(status.ExitCode)
		}
	}
	return stopAction + " completed.", nil
}

// Status - Get service status
func (windows *windowsRecord) Status() (string, error) {
	cmd := exec.Command("sc", "query", windows.name)
	out, err := cmd.Output()
	if exiterr, ok := err.(*exec.ExitError); ok {
		if status, ok := exiterr.Sys().(syscall.WaitStatus); ok {
			return "Getting status:" + failed, getWindowsError(status.ExitCode)
		}
	}
	return "Status: " + "SERVICE_"+getWindowsServiceState(out), nil
}

// Get executable path
func execPath() (string, error) {
	var n uint32
	b := make([]uint16, syscall.MAX_PATH)
	size := uint32(len(b))

	r0, _, e1 := syscall.MustLoadDLL(
		"kernel32.dll",
	).MustFindProc(
		"GetModuleFileNameW",
	).Call(0, uintptr(unsafe.Pointer(&b[0])), uintptr(size))
	n = uint32(r0)
	if n == 0 {
		return "", e1
	}
	return string(utf16.Decode(b[0:n])), nil
}

// Get windows error
func getWindowsError(statusCode uint32) error {
	return errors.New(fmt.Sprintf("\n %s: %s \n %s", WinErrCode[statusCode].Title, WinErrCode[statusCode].Description, WinErrCode[statusCode].Action))
}

// Get windows service state
func getWindowsServiceState(out []byte) string {
	regex := regexp.MustCompile("STATE.*: (?P<state_code>[0-9])  (?P<state>.*) ")
	service := regex.FindAllStringSubmatch(string(out), -1)[0]

	return service[2]
}<commit_msg>some improvements in windows depended daemon<commit_after>// Copyright 2016 The Go Authors. All rights reserved.
// Use of this source code is governed by
// license that can be found in the LICENSE file.

// Package daemon windows version
package daemon

import (
	"errors"
	"fmt"
	"os/exec"
	"regexp"
	"syscall"
	"unicode/utf16"
	"unsafe"
)

// windowsRecord - standard record (struct) for windows version of daemon package
type windowsRecord struct {
	name         string
	description  string
	dependencies []string
}

func newDaemon(name, description string, dependencies []string) (Daemon, error) {

	return &windowsRecord{name, description, dependencies}, nil
}

// Install the service
func (windows *windowsRecord) Install(args ...string) (string, error) {
	installAction := "Install " + windows.description + ":"

	execp, err := execPath()

	if err != nil {
		return installAction + failed, err
	}

	cmdArgs := []string{"create", windows.name, "start=auto", "binPath=" + execp}
	cmdArgs = append(cmdArgs, args...)

	cmd := exec.Command("sc", cmdArgs...)
	_, err = cmd.Output()
	if err != nil {
		return installAction + failed, getWindowsError(err)
	}
	return installAction + " completed.", nil
}

// Remove the service
func (windows *windowsRecord) Remove() (string, error) {
	removeAction := "Removing " + windows.description + ":"
	cmd := exec.Command("sc", "delete", windows.name, "confirm")
	err := cmd.Run()
	if err != nil {
		return removeAction + failed, getWindowsError(err)
	}
	return removeAction + " completed.", nil
}

// Start the service
func (windows *windowsRecord) Start() (string, error) {
	startAction := "Starting " + windows.description + ":"
	cmd := exec.Command("sc", "start", windows.name)
	err := cmd.Run()
	if err != nil {
		return startAction + failed, getWindowsError(err)
	}
	return startAction + " completed.", nil
}

// Stop the service
func (windows *windowsRecord) Stop() (string, error) {
	stopAction := "Stopping " + windows.description + ":"
	cmd := exec.Command("sc", "stop", windows.name)
	err := cmd.Run()
	if err != nil {
		return stopAction + failed, err
	}
	return stopAction + " completed.", nil
}

// Status - Get service status
func (windows *windowsRecord) Status() (string, error) {
	cmd := exec.Command("sc", "query", windows.name)
	out, err := cmd.Output()
	if err != nil {
		return "Getting status:" + failed, getWindowsError(err)
	}
	return "Status: " + "SERVICE_" + getWindowsServiceState(out), nil
}

// Get executable path
func execPath() (string, error) {
	var n uint32
	b := make([]uint16, syscall.MAX_PATH)
	size := uint32(len(b))

	r0, _, e1 := syscall.MustLoadDLL(
		"kernel32.dll",
	).MustFindProc(
		"GetModuleFileNameW",
	).Call(0, uintptr(unsafe.Pointer(&b[0])), uintptr(size))
	n = uint32(r0)
	if n == 0 {
		return "", e1
	}
	return string(utf16.Decode(b[0:n])), nil
}

// Get windows error
func getWindowsError(inputError error) error {
	if exiterr, ok := inputError.(*exec.ExitError); ok {
		if status, ok := exiterr.Sys().(syscall.WaitStatus); ok {
			if sysErr, ok := WinErrCode[status.ExitStatus()]; ok {
				return errors.New(fmt.Sprintf("\n %s: %s \n %s", sysErr.Title, sysErr.Description, sysErr.Action))
			}
		}
	}

	return inputError
}

// Get windows service state
func getWindowsServiceState(out []byte) string {
	regex := regexp.MustCompile("STATE.*: (?P<state_code>[0-9])  (?P<state>.*) ")
	service := regex.FindAllStringSubmatch(string(out), -1)[0]

	return service[2]
}
<|endoftext|>


<commit_before>// (c) 2019-2020, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.

package avm

import (
	"testing"

	"github.com/ava-labs/gecko/database/memdb"
	"github.com/ava-labs/gecko/ids"
	"github.com/ava-labs/gecko/snow/engine/common"
	"github.com/ava-labs/gecko/vms/secp256k1fx"
)

func TestGetAssetDescription(t *testing.T) {
	genesisBytes := BuildGenesisTest(t)

	ctx.Lock.Lock()
	defer ctx.Lock.Unlock()

	vm := &VM{}
	err := vm.Initialize(
		ctx,
		memdb.New(),
		genesisBytes,
		make(chan common.Message, 1),
		[]*common.Fx{&common.Fx{
			ID: ids.Empty,
			Fx: &secp256k1fx.Fx{},
		}},
	)
	if err != nil {
		t.Fatal(err)
	}
	defer vm.Shutdown()

	genesisTx := GetFirstTxFromGenesisTest(genesisBytes, t)

	avaAssetID := genesisTx.ID()

	s := Service{vm: vm}

	reply := GetAssetDescriptionReply{}
	err = s.GetAssetDescription(nil, &GetAssetDescriptionArgs{
		AssetID: avaAssetID.String(),
	}, &reply)
	if err != nil {
		t.Fatal(err)
	}

	if reply.Name != "myFixedCapAsset" {
		t.Fatalf("Wrong name returned from GetAssetDescription %s", reply.Name)
	}
	if reply.Symbol != "MFCA" {
		t.Fatalf("Wrong name returned from GetAssetDescription %s", reply.Symbol)
	}
}

func TestGetBalance(t *testing.T) {
	genesisBytes := BuildGenesisTest(t)

	ctx.Lock.Lock()
	defer ctx.Lock.Unlock()

	vm := &VM{}
	err := vm.Initialize(
		ctx,
		memdb.New(),
		genesisBytes,
		make(chan common.Message, 1),
		[]*common.Fx{&common.Fx{
			ID: ids.Empty,
			Fx: &secp256k1fx.Fx{},
		}},
	)
	if err != nil {
		t.Fatal(err)
	}
	defer vm.Shutdown()

	genesisTx := GetFirstTxFromGenesisTest(genesisBytes, t)

	avaAssetID := genesisTx.ID()

	s := Service{vm: vm}

	reply := GetBalanceReply{}
	err = s.GetBalance(nil, &GetBalanceArgs{
		Address: vm.Format(keys[0].PublicKey().Address().Bytes()),
		AssetID: avaAssetID.String(),
	}, &reply)
	if err != nil {
		t.Fatal(err)
	}

	if reply.Balance != 300000 {
		t.Fatalf("Wrong balance returned from GetBalance %d", reply.Balance)
	}
}

func TestCreateFixedCapAsset(t *testing.T) {
	genesisBytes := BuildGenesisTest(t)

	ctx.Lock.Lock()
	defer ctx.Lock.Unlock()

	vm := &VM{}
	err := vm.Initialize(
		ctx,
		memdb.New(),
		genesisBytes,
		make(chan common.Message, 1),
		[]*common.Fx{&common.Fx{
			ID: ids.Empty,
			Fx: &secp256k1fx.Fx{},
		}},
	)
	if err != nil {
		t.Fatal(err)
	}
	defer vm.Shutdown()

	s := Service{vm: vm}

	reply := CreateFixedCapAssetReply{}
	err = s.CreateFixedCapAsset(nil, &CreateFixedCapAssetArgs{
		Name:         "test asset",
		Symbol:       "test",
		Denomination: 1,
		InitialHolders: []*Holder{&Holder{
			Amount:  123456789,
			Address: vm.Format(keys[0].PublicKey().Address().Bytes()),
		}},
	}, &reply)
	if err != nil {
		t.Fatal(err)
	}

	if reply.AssetID.String() != "wWBk78PGAU4VkXhESr3jiYyMCEzzPPcnVYeEnNr9g4JuvYs2x" {
		t.Fatalf("Wrong assetID returned from CreateFixedCapAsset %s", reply.AssetID)
	}
}

func TestCreateVariableCapAsset(t *testing.T) {
	genesisBytes := BuildGenesisTest(t)

	ctx.Lock.Lock()
	defer ctx.Lock.Unlock()

	vm := &VM{}
	err := vm.Initialize(
		ctx,
		memdb.New(),
		genesisBytes,
		make(chan common.Message, 1),
		[]*common.Fx{&common.Fx{
			ID: ids.Empty,
			Fx: &secp256k1fx.Fx{},
		}},
	)
	if err != nil {
		t.Fatal(err)
	}
	defer vm.Shutdown()

	s := Service{vm: vm}

	reply := CreateVariableCapAssetReply{}
	err = s.CreateVariableCapAsset(nil, &CreateVariableCapAssetArgs{
		Name:   "test asset",
		Symbol: "test",
		MinterSets: []Owners{
			Owners{
				Threshold: 1,
				Minters: []string{
					vm.Format(keys[0].PublicKey().Address().Bytes()),
				},
			},
		},
	}, &reply)
	if err != nil {
		t.Fatal(err)
	}

	if reply.AssetID.String() != "SscTvpQFCZPNiRXyueDc7LdHT9EstHiva3AK6kuTgHTMd7DsU" {
		t.Fatalf("Wrong assetID returned from CreateFixedCapAsset %s", reply.AssetID)
	}
}
<commit_msg>vms: Facter service setup out of AVM service tests<commit_after>// (c) 2019-2020, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.

package avm

import (
	"testing"

	"github.com/ava-labs/gecko/database/memdb"
	"github.com/ava-labs/gecko/ids"
	"github.com/ava-labs/gecko/snow/engine/common"
	"github.com/ava-labs/gecko/vms/secp256k1fx"
)

func setup(t *testing.T) ([]byte, *VM, *Service) {
	genesisBytes := BuildGenesisTest(t)

	// TODO: Could this initialization be replaced by a call to GenesisVM()
	ctx.Lock.Lock()

	vm := &VM{}
	err := vm.Initialize(
		ctx,
		memdb.New(),
		genesisBytes,
		make(chan common.Message, 1),
		[]*common.Fx{&common.Fx{
			ID: ids.Empty,
			Fx: &secp256k1fx.Fx{},
		}},
	)
	if err != nil {
		t.Fatal(err)
	}
	s := &Service{vm: vm}
	return genesisBytes, vm, s
}

func TestGetAssetDescription(t *testing.T) {
	genesisBytes, vm, s := setup(t)
	defer ctx.Lock.Unlock()
	defer vm.Shutdown()

	genesisTx := GetFirstTxFromGenesisTest(genesisBytes, t)

	avaAssetID := genesisTx.ID()

	reply := GetAssetDescriptionReply{}
	err := s.GetAssetDescription(nil, &GetAssetDescriptionArgs{
		AssetID: avaAssetID.String(),
	}, &reply)
	if err != nil {
		t.Fatal(err)
	}

	if reply.Name != "myFixedCapAsset" {
		t.Fatalf("Wrong name returned from GetAssetDescription %s", reply.Name)
	}
	if reply.Symbol != "MFCA" {
		t.Fatalf("Wrong name returned from GetAssetDescription %s", reply.Symbol)
	}
}

func TestGetBalance(t *testing.T) {
	genesisBytes, vm, s := setup(t)
	defer ctx.Lock.Unlock()
	defer vm.Shutdown()

	genesisTx := GetFirstTxFromGenesisTest(genesisBytes, t)

	avaAssetID := genesisTx.ID()

	reply := GetBalanceReply{}
	err := s.GetBalance(nil, &GetBalanceArgs{
		Address: vm.Format(keys[0].PublicKey().Address().Bytes()),
		AssetID: avaAssetID.String(),
	}, &reply)
	if err != nil {
		t.Fatal(err)
	}

	if reply.Balance != 300000 {
		t.Fatalf("Wrong balance returned from GetBalance %d", reply.Balance)
	}
}

func TestCreateFixedCapAsset(t *testing.T) {
	_, vm, s := setup(t)
	defer ctx.Lock.Unlock()
	defer vm.Shutdown()

	reply := CreateFixedCapAssetReply{}
	err := s.CreateFixedCapAsset(nil, &CreateFixedCapAssetArgs{
		Name:         "test asset",
		Symbol:       "test",
		Denomination: 1,
		InitialHolders: []*Holder{&Holder{
			Amount:  123456789,
			Address: vm.Format(keys[0].PublicKey().Address().Bytes()),
		}},
	}, &reply)
	if err != nil {
		t.Fatal(err)
	}

	if reply.AssetID.String() != "wWBk78PGAU4VkXhESr3jiYyMCEzzPPcnVYeEnNr9g4JuvYs2x" {
		t.Fatalf("Wrong assetID returned from CreateFixedCapAsset %s", reply.AssetID)
	}
}

func TestCreateVariableCapAsset(t *testing.T) {
	_, vm, s := setup(t)
	defer ctx.Lock.Unlock()
	defer vm.Shutdown()

	reply := CreateVariableCapAssetReply{}
	err := s.CreateVariableCapAsset(nil, &CreateVariableCapAssetArgs{
		Name:   "test asset",
		Symbol: "test",
		MinterSets: []Owners{
			Owners{
				Threshold: 1,
				Minters: []string{
					vm.Format(keys[0].PublicKey().Address().Bytes()),
				},
			},
		},
	}, &reply)
	if err != nil {
		t.Fatal(err)
	}

	if reply.AssetID.String() != "SscTvpQFCZPNiRXyueDc7LdHT9EstHiva3AK6kuTgHTMd7DsU" {
		t.Fatalf("Wrong assetID returned from CreateFixedCapAsset %s", reply.AssetID)
	}
}
<|endoftext|>