<commit_before>/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you 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 main

import (
	"encoding/json"
	"fmt"
	"github.com/gorilla/mux"
	"io"
	"mime"
	"net"
	"net/http"
	"org/apache/htrace/common"
	"org/apache/htrace/conf"
	"org/apache/htrace/resource"
	"path/filepath"
	"strconv"
	"strings"
)

// Set the response headers.
func setResponseHeaders(hdr http.Header) {
	hdr.Set("Content-Type", "application/json")
}

// Write a JSON error response.
func writeError(lg *common.Logger, w http.ResponseWriter, errCode int,
	errStr string) {
	str := strings.Replace(errStr, `"`, `'`, -1)
	lg.Info(str)
	w.WriteHeader(errCode)
	w.Write([]byte(`{ "error" : "` + str + `"}`))
}

type serverInfoHandler struct {
	lg *common.Logger
}

func (hand *serverInfoHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
	setResponseHeaders(w.Header())
	version := common.ServerInfo{ReleaseVersion: RELEASE_VERSION,
		GitVersion: GIT_VERSION}
	buf, err := json.Marshal(&version)
	if err != nil {
		writeError(hand.lg, w, http.StatusInternalServerError,
			fmt.Sprintf("error marshalling ServerInfo: %s\n", err.Error()))
		return
	}
	w.Write(buf)
}

type dataStoreHandler struct {
	lg    *common.Logger
	store *dataStore
}

func (hand *dataStoreHandler) parse64(w http.ResponseWriter, str string) (int64, bool) {
	val, err := strconv.ParseUint(str, 16, 64)
	if err != nil {
		writeError(hand.lg, w, http.StatusBadRequest,
			fmt.Sprintf("Failed to parse span ID %s: %s", str, err.Error()))
		w.Write([]byte("Error parsing : " + err.Error()))
		return -1, false
	}
	return int64(val), true
}

func (hand *dataStoreHandler) getReqField32(fieldName string, w http.ResponseWriter,
	req *http.Request) (int32, bool) {
	str := req.FormValue(fieldName)
	if str == "" {
		writeError(hand.lg, w, http.StatusBadRequest, fmt.Sprintf("No %s specified.", fieldName))
		return -1, false
	}
	val, err := strconv.ParseUint(str, 16, 32)
	if err != nil {
		writeError(hand.lg, w, http.StatusBadRequest,
			fmt.Sprintf("Error parsing %s: %s.", fieldName, err.Error()))
		return -1, false
	}
	return int32(val), true
}

type findSidHandler struct {
	dataStoreHandler
}

func (hand *findSidHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
	setResponseHeaders(w.Header())
	req.ParseForm()
	vars := mux.Vars(req)
	stringSid := vars["id"]
	sid, ok := hand.parse64(w, stringSid)
	if !ok {
		return
	}
	span := hand.store.FindSpan(sid)
	if span == nil {
		writeError(hand.lg, w, http.StatusNoContent, fmt.Sprintf("No such span as %s\n",
			common.SpanId(sid)))
		return
	}
	w.Write(span.ToJson())
}

type findChildrenHandler struct {
	dataStoreHandler
}

func (hand *findChildrenHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
	setResponseHeaders(w.Header())
	req.ParseForm()
	vars := mux.Vars(req)
	stringSid := vars["id"]
	sid, ok := hand.parse64(w, stringSid)
	if !ok {
		return
	}
	var lim int32
	lim, ok = hand.getReqField32("lim", w, req)
	if !ok {
		return
	}
	children := hand.store.FindChildren(sid, lim)
	jbytes, err := json.Marshal(children)
	if err != nil {
		writeError(hand.lg, w, http.StatusInternalServerError,
			fmt.Sprintf("Error marshalling children: %s", err.Error()))
		return
	}
	w.Write(jbytes)
}

type writeSpansHandler struct {
	dataStoreHandler
}

func (hand *writeSpansHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
	setResponseHeaders(w.Header())
	dec := json.NewDecoder(req.Body)
	spans := make([]*common.Span, 0, 32)
	for {
		var span common.Span
		err := dec.Decode(&span)
		if err != nil {
			if err != io.EOF {
				writeError(hand.lg, w, http.StatusBadRequest,
					fmt.Sprintf("Error parsing spans: %s", err.Error()))
				return
			}
			break
		}
		spans = append(spans, &span)
	}
	for spanIdx := range spans {
		hand.lg.Debugf("writing span %s\n", spans[spanIdx].ToJson())
		hand.store.WriteSpan(spans[spanIdx])
	}
}

type queryHandler struct {
	dataStoreHandler
}

func (hand *queryHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
	setResponseHeaders(w.Header())
	_, ok := hand.getReqField32("lim", w, req)
	if !ok {
		return
	}
	var query common.Query
	dec := json.NewDecoder(req.Body)
	err := dec.Decode(&query)
	if err != nil {
		writeError(hand.lg, w, http.StatusBadRequest,
			fmt.Sprintf("Error parsing query: %s", err.Error()))
		return
	}
	var results []*common.Span
	results, err = hand.store.HandleQuery(&query)
	if err != nil {
		writeError(hand.lg, w, http.StatusInternalServerError,
			fmt.Sprintf("Internal error processing query %s: %s",
				query.String(), err.Error()))
		return
	}
	var jbytes []byte
	jbytes, err = json.Marshal(results)
	if err != nil {
		writeError(hand.lg, w, http.StatusInternalServerError,
			fmt.Sprintf("Error marshalling results: %s", err.Error()))
		return
	}
	w.Write(jbytes)
}

type defaultServeHandler struct {
	lg *common.Logger
}

func (hand *defaultServeHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
	ident := strings.TrimLeft(req.URL.Path, "/")
	if ident == "" {
		ident = "index.html" // default to index.html
	}
	ident = strings.Replace(ident, "/", "__", -1)
	rsc := resource.Catalog[ident]
	if rsc == "" {
		hand.lg.Warnf("failed to find entry for %s\n", ident)
		w.WriteHeader(http.StatusNotFound)
		return
	}
	file_ext := filepath.Ext(req.URL.Path)
	mime_type := mime.TypeByExtension(file_ext)
	w.Header().Set("Content-Type", mime_type)
	w.Write([]byte(rsc))
}

type RestServer struct {
	listener net.Listener
	lg       *common.Logger
}

func CreateRestServer(cnf *conf.Config, store *dataStore) (*RestServer, error) {
	var err error
	rsv := &RestServer{}
	rsv.listener, err = net.Listen("tcp", cnf.Get(conf.HTRACE_WEB_ADDRESS))
	if err != nil {
		return nil, err
	}
	var success bool
	defer func() {
		if !success {
			rsv.Close()
		}
	}()
	rsv.lg = common.NewLogger("rest", cnf)

	r := mux.NewRouter().StrictSlash(false)
	// Default Handler. This will serve requests for static requests.
	r.Handle("/", &defaultServeHandler{lg: rsv.lg})

	r.Handle("/server/info", &serverInfoHandler{lg: rsv.lg}).Methods("GET")

	writeSpansH := &writeSpansHandler{dataStoreHandler: dataStoreHandler{
		store: store, lg: rsv.lg}}
	r.Handle("/writeSpans", writeSpansH).Methods("POST")

	queryH := &queryHandler{dataStoreHandler: dataStoreHandler{store: store}}
	r.Handle("/query", queryH).Methods("GET")

	span := r.PathPrefix("/span").Subrouter()
	findSidH := &findSidHandler{dataStoreHandler: dataStoreHandler{store: store, lg: rsv.lg}}
	span.Handle("/{id}", findSidH).Methods("GET")

	findChildrenH := &findChildrenHandler{dataStoreHandler: dataStoreHandler{store: store,
		lg: rsv.lg}}
	span.Handle("/{id}/children", findChildrenH).Methods("GET")

	go http.Serve(rsv.listener, r)

	rsv.lg.Infof("Started REST server on %s...\n", rsv.listener.Addr().String())
	success = true
	return rsv, nil
}

func (rsv *RestServer) Addr() net.Addr {
	return rsv.listener.Addr()
}

func (rsv *RestServer) Close() {
	rsv.listener.Close()
}
<commit_msg>HTRACE-98. Web Server should use PrefixHandler (Abraham Elmahrek via Colin P. McCabe)<commit_after>/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you 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 main

import (
	"encoding/json"
	"fmt"
	"github.com/gorilla/mux"
	"io"
	"mime"
	"net"
	"net/http"
	"org/apache/htrace/common"
	"org/apache/htrace/conf"
	"org/apache/htrace/resource"
	"path/filepath"
	"strconv"
	"strings"
)

// Set the response headers.
func setResponseHeaders(hdr http.Header) {
	hdr.Set("Content-Type", "application/json")
}

// Write a JSON error response.
func writeError(lg *common.Logger, w http.ResponseWriter, errCode int,
	errStr string) {
	str := strings.Replace(errStr, `"`, `'`, -1)
	lg.Info(str)
	w.WriteHeader(errCode)
	w.Write([]byte(`{ "error" : "` + str + `"}`))
}

type serverInfoHandler struct {
	lg *common.Logger
}

func (hand *serverInfoHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
	setResponseHeaders(w.Header())
	version := common.ServerInfo{ReleaseVersion: RELEASE_VERSION,
		GitVersion: GIT_VERSION}
	buf, err := json.Marshal(&version)
	if err != nil {
		writeError(hand.lg, w, http.StatusInternalServerError,
			fmt.Sprintf("error marshalling ServerInfo: %s\n", err.Error()))
		return
	}
	w.Write(buf)
}

type dataStoreHandler struct {
	lg    *common.Logger
	store *dataStore
}

func (hand *dataStoreHandler) parse64(w http.ResponseWriter, str string) (int64, bool) {
	val, err := strconv.ParseUint(str, 16, 64)
	if err != nil {
		writeError(hand.lg, w, http.StatusBadRequest,
			fmt.Sprintf("Failed to parse span ID %s: %s", str, err.Error()))
		w.Write([]byte("Error parsing : " + err.Error()))
		return -1, false
	}
	return int64(val), true
}

func (hand *dataStoreHandler) getReqField32(fieldName string, w http.ResponseWriter,
	req *http.Request) (int32, bool) {
	str := req.FormValue(fieldName)
	if str == "" {
		writeError(hand.lg, w, http.StatusBadRequest, fmt.Sprintf("No %s specified.", fieldName))
		return -1, false
	}
	val, err := strconv.ParseUint(str, 16, 32)
	if err != nil {
		writeError(hand.lg, w, http.StatusBadRequest,
			fmt.Sprintf("Error parsing %s: %s.", fieldName, err.Error()))
		return -1, false
	}
	return int32(val), true
}

type findSidHandler struct {
	dataStoreHandler
}

func (hand *findSidHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
	setResponseHeaders(w.Header())
	req.ParseForm()
	vars := mux.Vars(req)
	stringSid := vars["id"]
	sid, ok := hand.parse64(w, stringSid)
	if !ok {
		return
	}
	span := hand.store.FindSpan(sid)
	if span == nil {
		writeError(hand.lg, w, http.StatusNoContent, fmt.Sprintf("No such span as %s\n",
			common.SpanId(sid)))
		return
	}
	w.Write(span.ToJson())
}

type findChildrenHandler struct {
	dataStoreHandler
}

func (hand *findChildrenHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
	setResponseHeaders(w.Header())
	req.ParseForm()
	vars := mux.Vars(req)
	stringSid := vars["id"]
	sid, ok := hand.parse64(w, stringSid)
	if !ok {
		return
	}
	var lim int32
	lim, ok = hand.getReqField32("lim", w, req)
	if !ok {
		return
	}
	children := hand.store.FindChildren(sid, lim)
	jbytes, err := json.Marshal(children)
	if err != nil {
		writeError(hand.lg, w, http.StatusInternalServerError,
			fmt.Sprintf("Error marshalling children: %s", err.Error()))
		return
	}
	w.Write(jbytes)
}

type writeSpansHandler struct {
	dataStoreHandler
}

func (hand *writeSpansHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
	setResponseHeaders(w.Header())
	dec := json.NewDecoder(req.Body)
	spans := make([]*common.Span, 0, 32)
	for {
		var span common.Span
		err := dec.Decode(&span)
		if err != nil {
			if err != io.EOF {
				writeError(hand.lg, w, http.StatusBadRequest,
					fmt.Sprintf("Error parsing spans: %s", err.Error()))
				return
			}
			break
		}
		spans = append(spans, &span)
	}
	for spanIdx := range spans {
		hand.lg.Debugf("writing span %s\n", spans[spanIdx].ToJson())
		hand.store.WriteSpan(spans[spanIdx])
	}
}

type queryHandler struct {
	dataStoreHandler
}

func (hand *queryHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
	setResponseHeaders(w.Header())
	_, ok := hand.getReqField32("lim", w, req)
	if !ok {
		return
	}
	var query common.Query
	dec := json.NewDecoder(req.Body)
	err := dec.Decode(&query)
	if err != nil {
		writeError(hand.lg, w, http.StatusBadRequest,
			fmt.Sprintf("Error parsing query: %s", err.Error()))
		return
	}
	var results []*common.Span
	results, err = hand.store.HandleQuery(&query)
	if err != nil {
		writeError(hand.lg, w, http.StatusInternalServerError,
			fmt.Sprintf("Internal error processing query %s: %s",
				query.String(), err.Error()))
		return
	}
	var jbytes []byte
	jbytes, err = json.Marshal(results)
	if err != nil {
		writeError(hand.lg, w, http.StatusInternalServerError,
			fmt.Sprintf("Error marshalling results: %s", err.Error()))
		return
	}
	w.Write(jbytes)
}

type defaultServeHandler struct {
	lg *common.Logger
}

func (hand *defaultServeHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
	ident := strings.TrimLeft(req.URL.Path, "/")
	if ident == "" {
		ident = "index.html" // default to index.html
	}
	ident = strings.Replace(ident, "/", "__", -1)
	hand.lg.Debugf("defaultServeHandler(path=%s, ident=%s)\n", req.URL.Path, ident)
	rsc := resource.Catalog[ident]
	if rsc == "" {
		hand.lg.Warnf("failed to find entry for %s\n", ident)
		w.WriteHeader(http.StatusNotFound)
		return
	}
	file_ext := filepath.Ext(req.URL.Path)
	mime_type := mime.TypeByExtension(file_ext)
	w.Header().Set("Content-Type", mime_type)
	w.Write([]byte(rsc))
}

type RestServer struct {
	listener net.Listener
	lg       *common.Logger
}

func CreateRestServer(cnf *conf.Config, store *dataStore) (*RestServer, error) {
	var err error
	rsv := &RestServer{}
	rsv.listener, err = net.Listen("tcp", cnf.Get(conf.HTRACE_WEB_ADDRESS))
	if err != nil {
		return nil, err
	}
	var success bool
	defer func() {
		if !success {
			rsv.Close()
		}
	}()
	rsv.lg = common.NewLogger("rest", cnf)

	r := mux.NewRouter().StrictSlash(false)

	r.Handle("/server/info", &serverInfoHandler{lg: rsv.lg}).Methods("GET")

	writeSpansH := &writeSpansHandler{dataStoreHandler: dataStoreHandler{
		store: store, lg: rsv.lg}}
	r.Handle("/writeSpans", writeSpansH).Methods("POST")

	queryH := &queryHandler{dataStoreHandler: dataStoreHandler{store: store}}
	r.Handle("/query", queryH).Methods("GET")

	span := r.PathPrefix("/span").Subrouter()
	findSidH := &findSidHandler{dataStoreHandler: dataStoreHandler{store: store, lg: rsv.lg}}
	span.Handle("/{id}", findSidH).Methods("GET")

	findChildrenH := &findChildrenHandler{dataStoreHandler: dataStoreHandler{store: store,
		lg: rsv.lg}}
	span.Handle("/{id}/children", findChildrenH).Methods("GET")

	// Default Handler. This will serve requests for static requests.
	r.PathPrefix("/").Handler(&defaultServeHandler{lg: rsv.lg}).Methods("GET")

	go http.Serve(rsv.listener, r)

	rsv.lg.Infof("Started REST server on %s...\n", rsv.listener.Addr().String())
	success = true
	return rsv, nil
}

func (rsv *RestServer) Addr() net.Addr {
	return rsv.listener.Addr()
}

func (rsv *RestServer) Close() {
	rsv.listener.Close()
}
<|endoftext|>


<commit_before>package api

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

	"github.com/anthonynsimon/parrot/model"
	"github.com/anthonynsimon/parrot/render"
	"github.com/pressly/chi"
)

func getUserProjects(w http.ResponseWriter, r *http.Request) {
	id, err := getUserIDFromContext(r.Context())
	if err != nil {
		handleError(w, ErrInternal)
		return
	}

	projects, err := store.GetUserProjects(id)
	if err != nil {
		handleError(w, err)
		return
	}

	render.JSON(w, http.StatusOK, projects)
}

func getProjectUsers(w http.ResponseWriter, r *http.Request) {
	projectID := chi.URLParam(r, "projectID")
	if projectID == "" {
		handleError(w, ErrBadRequest)
		return
	}

	users, err := store.GetProjectUsers(projectID)
	if err != nil {
		handleError(w, err)
		return
	}

	render.JSON(w, http.StatusOK, users)
}

func assignProjectUser(w http.ResponseWriter, r *http.Request) {
	// TODO: decode and validate only required fields. Whitelisting?
	var pu model.ProjectUser
	if err := json.NewDecoder(r.Body).Decode(&pu); err != nil {
		handleError(w, ErrBadRequest)
		return
	}
	projectID := chi.URLParam(r, "projectID")
	if projectID == "" {
		handleError(w, ErrBadRequest)
		return
	}
	if projectID != pu.ProjectID {
		handleError(w, ErrForbiden)
		return
	}

	err := store.AssignProjectUser(pu)
	if err != nil {
		handleError(w, err)
		return
	}

	render.JSON(w, http.StatusOK, pu)
}

func updateProjectUserRole(w http.ResponseWriter, r *http.Request) {
	projectID := chi.URLParam(r, "projectID")
	if projectID == "" {
		handleError(w, ErrBadRequest)
		return
	}
	userID := chi.URLParam(r, "userID")
	if userID == "" {
		handleError(w, ErrBadRequest)
		return
	}

	// Get updated role
	data := struct {
		Role string `json:"role"`
	}{}
	if err := json.NewDecoder(r.Body).Decode(&data); err != nil {
		handleError(w, ErrBadRequest)
		return
	}
	if !isRole(data.Role) {
		handleError(w, ErrBadRequest)
		return
	}

	pu := model.ProjectUser{UserID: userID, ProjectID: projectID, Role: data.Role}

	result, err := store.UpdateProjectUser(pu)
	if err != nil {
		handleError(w, err)
		return
	}

	render.JSON(w, http.StatusOK, result)
}

func revokeProjectUser(w http.ResponseWriter, r *http.Request) {
	projectID := chi.URLParam(r, "projectID")
	if projectID == "" {
		handleError(w, ErrBadRequest)
		return
	}
	userID := chi.URLParam(r, "userID")
	if userID == "" {
		handleError(w, ErrBadRequest)
		return
	}
	pu := model.ProjectUser{UserID: userID, ProjectID: projectID}

	err := store.RevokeProjectUser(pu)
	if err != nil {
		handleError(w, err)
		return
	}

	render.JSON(w, http.StatusNoContent, nil)
}
<commit_msg>Match email to user id if not provided<commit_after>package api

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

	"github.com/anthonynsimon/parrot/model"
	"github.com/anthonynsimon/parrot/render"
	"github.com/pressly/chi"
)

func getUserProjects(w http.ResponseWriter, r *http.Request) {
	id, err := getUserIDFromContext(r.Context())
	if err != nil {
		handleError(w, ErrInternal)
		return
	}

	projects, err := store.GetUserProjects(id)
	if err != nil {
		handleError(w, err)
		return
	}

	render.JSON(w, http.StatusOK, projects)
}

func getProjectUsers(w http.ResponseWriter, r *http.Request) {
	projectID := chi.URLParam(r, "projectID")
	if projectID == "" {
		handleError(w, ErrBadRequest)
		return
	}

	users, err := store.GetProjectUsers(projectID)
	if err != nil {
		handleError(w, err)
		return
	}

	render.JSON(w, http.StatusOK, users)
}

func assignProjectUser(w http.ResponseWriter, r *http.Request) {
	// TODO: decode and validate only required fields. Whitelisting?
	var pu model.ProjectUser
	if err := json.NewDecoder(r.Body).Decode(&pu); err != nil {
		handleError(w, ErrBadRequest)
		return
	}
	projectID := chi.URLParam(r, "projectID")
	if projectID == "" {
		handleError(w, ErrBadRequest)
		return
	}
	// Validate that the url of the request matches the body data
	if projectID != pu.ProjectID {
		handleError(w, ErrForbiden)
		return
	}
	if pu.Email == "" && pu.UserID == "" {
		handleError(w, ErrBadRequest)
		return
	}

	if pu.Email != "" {
		user, err := store.GetUserByEmail(pu.Email)
		if err != nil {
			handleError(w, err)
			return
		}
		pu.UserID = user.ID
	}

	err := store.AssignProjectUser(pu)
	if err != nil {
		handleError(w, err)
		return
	}

	render.JSON(w, http.StatusOK, pu)
}

func updateProjectUserRole(w http.ResponseWriter, r *http.Request) {
	projectID := chi.URLParam(r, "projectID")
	if projectID == "" {
		handleError(w, ErrBadRequest)
		return
	}
	userID := chi.URLParam(r, "userID")
	if userID == "" {
		handleError(w, ErrBadRequest)
		return
	}

	// Get updated role
	data := struct {
		Role string `json:"role"`
	}{}
	if err := json.NewDecoder(r.Body).Decode(&data); err != nil {
		handleError(w, ErrBadRequest)
		return
	}
	if !isRole(data.Role) {
		handleError(w, ErrBadRequest)
		return
	}

	pu := model.ProjectUser{UserID: userID, ProjectID: projectID, Role: data.Role}

	result, err := store.UpdateProjectUser(pu)
	if err != nil {
		handleError(w, err)
		return
	}

	render.JSON(w, http.StatusOK, result)
}

func revokeProjectUser(w http.ResponseWriter, r *http.Request) {
	projectID := chi.URLParam(r, "projectID")
	if projectID == "" {
		handleError(w, ErrBadRequest)
		return
	}
	userID := chi.URLParam(r, "userID")
	if userID == "" {
		handleError(w, ErrBadRequest)
		return
	}
	pu := model.ProjectUser{UserID: userID, ProjectID: projectID}

	err := store.RevokeProjectUser(pu)
	if err != nil {
		handleError(w, err)
		return
	}

	render.JSON(w, http.StatusNoContent, nil)
}
<|endoftext|>


<commit_before>/*
Copyright 2019 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 conversion

import (
	"crypto/sha256"
	"encoding/hex"
	"encoding/json"
	"strings"

	"github.com/pkg/errors"
	"k8s.io/api/core/v1"
	storagev1 "k8s.io/api/storage/v1"
	"k8s.io/apimachinery/pkg/api/meta"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/runtime"
	"k8s.io/apimachinery/pkg/util/sets"
	"k8s.io/apimachinery/pkg/util/validation"
	listersv1 "k8s.io/client-go/listers/core/v1"

	"github.com/kubernetes-sigs/multi-tenancy/incubator/virtualcluster/pkg/apis/tenancy/v1alpha1"
	"github.com/kubernetes-sigs/multi-tenancy/incubator/virtualcluster/pkg/syncer/constants"
)

const (
	masterServiceNamespace = metav1.NamespaceDefault
)

var masterServices = sets.NewString("kubernetes")

// ToClusterKey makes a unique key which is used to create the root namespace in super master for a virtual cluster.
// To avoid name conflict, the key uses the format <namespace>-<hash>-<name>
func ToClusterKey(vc *v1alpha1.Virtualcluster) string {
	fullname := vc.GetNamespace() + "/" + vc.GetName()
	digest := sha256.Sum256([]byte(fullname))
	return vc.GetNamespace() + "-" + hex.EncodeToString(digest[0:])[0:5] + "-" + vc.GetName()
}

func ToSuperMasterNamespace(cluster, ns string) string {
	targetNamespace := strings.Join([]string{cluster, ns}, "-")
	if len(targetNamespace) > validation.DNS1123SubdomainMaxLength {
		digest := sha256.Sum256([]byte(targetNamespace))
		return targetNamespace[0:57] + "-" + hex.EncodeToString(digest[0:])[0:5]
	}
	return targetNamespace
}

func GetVirtualNamespace(nsLister listersv1.NamespaceLister, pNamespace string) (cluster, namespace string, err error) {
	vcInfo, err := nsLister.Get(pNamespace)
	if err != nil {
		return
	}

	if v, ok := vcInfo.GetAnnotations()[constants.LabelCluster]; ok {
		cluster = v
	}
	if v, ok := vcInfo.GetAnnotations()[constants.LabelNamespace]; ok {
		namespace = v
	}
	return
}

func GetVirtualOwner(obj runtime.Object) (cluster, namespace string) {
	meta, err := meta.Accessor(obj)
	if err != nil {
		return "", ""
	}

	cluster = meta.GetAnnotations()[constants.LabelCluster]
	namespace = strings.TrimPrefix(meta.GetNamespace(), cluster+"-")
	return cluster, namespace
}

func BuildMetadata(cluster, targetNamespace string, obj runtime.Object) (runtime.Object, error) {
	target := obj.DeepCopyObject()
	m, err := meta.Accessor(target)
	if err != nil {
		return nil, err
	}

	ownerReferencesStr, err := json.Marshal(m.GetOwnerReferences())
	if err != nil {
		return nil, errors.Wrap(err, "failed to marshal owner references")
	}

	var tenantScopeMetaInAnnotation = map[string]string{
		constants.LabelCluster:         cluster,
		constants.LabelUID:             string(m.GetUID()),
		constants.LabelOwnerReferences: string(ownerReferencesStr),
		constants.LabelNamespace:       m.GetNamespace(),
	}

	ResetMetadata(m)
	if len(targetNamespace) > 0 {
		m.SetNamespace(targetNamespace)
	}

	anno := m.GetAnnotations()
	if anno == nil {
		anno = make(map[string]string)
	}
	for k, v := range tenantScopeMetaInAnnotation {
		anno[k] = v
	}
	m.SetAnnotations(anno)

	return target, nil
}

func BuildSuperMasterNamespace(cluster string, obj runtime.Object) (runtime.Object, error) {
	target := obj.DeepCopyObject()
	m, err := meta.Accessor(target)
	if err != nil {
		return nil, err
	}

	anno := m.GetAnnotations()
	if anno == nil {
		anno = make(map[string]string)
	}
	anno[constants.LabelCluster] = cluster
	anno[constants.LabelUID] = string(m.GetUID())
	anno[constants.LabelNamespace] = m.GetName()
	m.SetAnnotations(anno)

	ResetMetadata(m)

	targetName := strings.Join([]string{cluster, m.GetName()}, "-")
	m.SetName(targetName)
	return target, nil
}

func ResetMetadata(obj metav1.Object) {
	obj.SetSelfLink("")
	obj.SetUID("")
	obj.SetResourceVersion("")
	obj.SetGeneration(0)
	obj.SetCreationTimestamp(metav1.Time{})
	obj.SetDeletionTimestamp(nil)
	obj.SetDeletionGracePeriodSeconds(nil)
	obj.SetOwnerReferences(nil)
	obj.SetFinalizers(nil)
	obj.SetClusterName("")
	obj.SetInitializers(nil)
}

func BuildVirtualPodEvent(cluster string, pEvent *v1.Event, vPod *v1.Pod) *v1.Event {
	vEvent := pEvent.DeepCopy()
	ResetMetadata(vEvent)
	vEvent.SetNamespace(vPod.Namespace)
	vEvent.InvolvedObject.Namespace = vPod.Namespace
	vEvent.InvolvedObject.UID = vPod.UID
	vEvent.InvolvedObject.ResourceVersion = ""

	vEvent.Message = strings.ReplaceAll(vEvent.Message, cluster+"-", "")
	vEvent.Message = strings.ReplaceAll(vEvent.Message, cluster, "")

	return vEvent
}

func BuildVirtualStorageClass(cluster string, pStorageClass *storagev1.StorageClass) *storagev1.StorageClass {
	vStorageClass := pStorageClass.DeepCopy()
	ResetMetadata(vStorageClass)
	return vStorageClass
}

func BuildVirtualPersistentVolume(cluster string, pPV *v1.PersistentVolume, vPVC *v1.PersistentVolumeClaim) *v1.PersistentVolume {
	vPV := pPV.DeepCopy()
	ResetMetadata(vPV)
	// The pv needs to bind with the vPVC
	vPV.Spec.ClaimRef.Namespace = vPVC.Namespace
	vPV.Spec.ClaimRef.UID = vPVC.UID
	return vPV
}
<commit_msg>Fix to use vc.UID to generate hash<commit_after>/*
Copyright 2019 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 conversion

import (
	"crypto/sha256"
	"encoding/hex"
	"encoding/json"
	"strings"

	"github.com/pkg/errors"
	"k8s.io/api/core/v1"
	storagev1 "k8s.io/api/storage/v1"
	"k8s.io/apimachinery/pkg/api/meta"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/runtime"
	"k8s.io/apimachinery/pkg/util/sets"
	"k8s.io/apimachinery/pkg/util/validation"
	listersv1 "k8s.io/client-go/listers/core/v1"

	"github.com/kubernetes-sigs/multi-tenancy/incubator/virtualcluster/pkg/apis/tenancy/v1alpha1"
	"github.com/kubernetes-sigs/multi-tenancy/incubator/virtualcluster/pkg/syncer/constants"
)

const (
	masterServiceNamespace = metav1.NamespaceDefault
)

var masterServices = sets.NewString("kubernetes")

// ToClusterKey makes a unique key which is used to create the root namespace in super master for a virtual cluster.
// To avoid name conflict, the key uses the format <namespace>-<hash>-<name>
func ToClusterKey(vc *v1alpha1.Virtualcluster) string {
	digest := sha256.Sum256([]byte(vc.GetUID()))
	return vc.GetNamespace() + "-" + hex.EncodeToString(digest[0:])[0:6] + "-" + vc.GetName()
}

func ToSuperMasterNamespace(cluster, ns string) string {
	targetNamespace := strings.Join([]string{cluster, ns}, "-")
	if len(targetNamespace) > validation.DNS1123SubdomainMaxLength {
		digest := sha256.Sum256([]byte(targetNamespace))
		return targetNamespace[0:57] + "-" + hex.EncodeToString(digest[0:])[0:5]
	}
	return targetNamespace
}

func GetVirtualNamespace(nsLister listersv1.NamespaceLister, pNamespace string) (cluster, namespace string, err error) {
	vcInfo, err := nsLister.Get(pNamespace)
	if err != nil {
		return
	}

	if v, ok := vcInfo.GetAnnotations()[constants.LabelCluster]; ok {
		cluster = v
	}
	if v, ok := vcInfo.GetAnnotations()[constants.LabelNamespace]; ok {
		namespace = v
	}
	return
}

func GetVirtualOwner(obj runtime.Object) (cluster, namespace string) {
	meta, err := meta.Accessor(obj)
	if err != nil {
		return "", ""
	}

	cluster = meta.GetAnnotations()[constants.LabelCluster]
	namespace = strings.TrimPrefix(meta.GetNamespace(), cluster+"-")
	return cluster, namespace
}

func BuildMetadata(cluster, targetNamespace string, obj runtime.Object) (runtime.Object, error) {
	target := obj.DeepCopyObject()
	m, err := meta.Accessor(target)
	if err != nil {
		return nil, err
	}

	ownerReferencesStr, err := json.Marshal(m.GetOwnerReferences())
	if err != nil {
		return nil, errors.Wrap(err, "failed to marshal owner references")
	}

	var tenantScopeMetaInAnnotation = map[string]string{
		constants.LabelCluster:         cluster,
		constants.LabelUID:             string(m.GetUID()),
		constants.LabelOwnerReferences: string(ownerReferencesStr),
		constants.LabelNamespace:       m.GetNamespace(),
	}

	ResetMetadata(m)
	if len(targetNamespace) > 0 {
		m.SetNamespace(targetNamespace)
	}

	anno := m.GetAnnotations()
	if anno == nil {
		anno = make(map[string]string)
	}
	for k, v := range tenantScopeMetaInAnnotation {
		anno[k] = v
	}
	m.SetAnnotations(anno)

	return target, nil
}

func BuildSuperMasterNamespace(cluster string, obj runtime.Object) (runtime.Object, error) {
	target := obj.DeepCopyObject()
	m, err := meta.Accessor(target)
	if err != nil {
		return nil, err
	}

	anno := m.GetAnnotations()
	if anno == nil {
		anno = make(map[string]string)
	}
	anno[constants.LabelCluster] = cluster
	anno[constants.LabelUID] = string(m.GetUID())
	anno[constants.LabelNamespace] = m.GetName()
	m.SetAnnotations(anno)

	ResetMetadata(m)

	targetName := strings.Join([]string{cluster, m.GetName()}, "-")
	m.SetName(targetName)
	return target, nil
}

func ResetMetadata(obj metav1.Object) {
	obj.SetSelfLink("")
	obj.SetUID("")
	obj.SetResourceVersion("")
	obj.SetGeneration(0)
	obj.SetCreationTimestamp(metav1.Time{})
	obj.SetDeletionTimestamp(nil)
	obj.SetDeletionGracePeriodSeconds(nil)
	obj.SetOwnerReferences(nil)
	obj.SetFinalizers(nil)
	obj.SetClusterName("")
	obj.SetInitializers(nil)
}

func BuildVirtualPodEvent(cluster string, pEvent *v1.Event, vPod *v1.Pod) *v1.Event {
	vEvent := pEvent.DeepCopy()
	ResetMetadata(vEvent)
	vEvent.SetNamespace(vPod.Namespace)
	vEvent.InvolvedObject.Namespace = vPod.Namespace
	vEvent.InvolvedObject.UID = vPod.UID
	vEvent.InvolvedObject.ResourceVersion = ""

	vEvent.Message = strings.ReplaceAll(vEvent.Message, cluster+"-", "")
	vEvent.Message = strings.ReplaceAll(vEvent.Message, cluster, "")

	return vEvent
}

func BuildVirtualStorageClass(cluster string, pStorageClass *storagev1.StorageClass) *storagev1.StorageClass {
	vStorageClass := pStorageClass.DeepCopy()
	ResetMetadata(vStorageClass)
	return vStorageClass
}

func BuildVirtualPersistentVolume(cluster string, pPV *v1.PersistentVolume, vPVC *v1.PersistentVolumeClaim) *v1.PersistentVolume {
	vPV := pPV.DeepCopy()
	ResetMetadata(vPV)
	// The pv needs to bind with the vPVC
	vPV.Spec.ClaimRef.Namespace = vPVC.Namespace
	vPV.Spec.ClaimRef.UID = vPVC.UID
	return vPV
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	log "github.com/Sirupsen/logrus"
	"github.com/codegangsta/cli"
	"github.com/jbdalido/smg/engine"
	"github.com/jbdalido/smg/utils"
	"os"
	"os/signal"
)

var (
	smgapp      *engine.Application
	eng         *engine.Engine
	killChannel chan os.Signal
	endChannel  chan error
)

func main() {

	cliApp := cli.App{
		Name:    "smg",
		Usage:   "Run and Build docker - https://smuggler.io",
		Version: "0.4.1",
	}

	cliApp.Flags = []cli.Flag{
		cli.StringFlag{
			Name:  "docker, d",
			Value: "",
			Usage: "Docker endpoint",
		},
		cli.StringFlag{
			Name:   "config, c",
			Value:  "~/.smg.yml",
			Usage:  "Config file to use",
			EnvVar: "SMG_CONFIG",
		},
	}

	buildFlags := []cli.Flag{
		cli.StringFlag{
			Name:  "start, s",
			Value: "smg.yml",
			Usage: "Specify a different file to use for your smg run (default: smg.yml)",
		},
		cli.BoolFlag{
			Name:  "no-cache, n",
			Usage: "Disable the use of docker cache during run and build with provided dockerfiles",
		},
		cli.BoolFlag{
			Name:  "verbose, v",
			Usage: "Verbose Mode",
		},
		cli.BoolFlag{
			Name:  "push, p",
			Usage: "Push images after a successful build",
		},
		cli.BoolFlag{
			Name:  "last, l",
			Usage: "Download last image for each build",
		},
		cli.BoolFlag{
			Name:  "delete, D",
			Usage: "Delete images created after a successful build",
		},
		cli.StringSliceFlag{
			Name:  "etcd",
			Value: &cli.StringSlice{},
			Usage: "ETCD Storage http endpoint",
		},
	}

	runFlags := []cli.Flag{
		cli.StringFlag{
			Name:  "start, s",
			Value: "smg.yml",
			Usage: "Specify a different file to use for your smg run (default: smg.yml)",
		},
		cli.BoolFlag{
			Name:  "no-cache, n",
			Usage: "Disable the use of docker cache during run and build with provided dockerfiles",
		},
		cli.BoolFlag{
			Name:  "verbose, v",
			Usage: "Verbose Mode",
		},
		cli.StringFlag{
			Name:  "env, e",
			Value: "default",
			Usage: "Environment (commands or dockerfiles) to use for the run",
		},
		cli.StringSliceFlag{
			Name:  "override, o",
			Value: &cli.StringSlice{},
			Usage: "Environment (commands or dockerfiles) to use for the run",
		},
		cli.BoolFlag{
			Name:  "keepalive, k",
			Usage: "Keep containers alive after a run (successful or not)",
		},
		cli.BoolFlag{
			Name:  "shared-folder, S",
			Usage: "Use a shared-folder with the main container",
		},
	}

	cliApp.HideVersion = true

	cliApp.Commands = []cli.Command{
		cli.Command{
			Name:   "run",
			Usage:  "Run containers with the proper environment",
			Flags:  runFlags,
			Action: CmdRun,
		},
		cli.Command{
			Name:   "build",
			Usage:  "Build against the active git branch of the folder and the build setup of the smg file",
			Flags:  buildFlags,
			Action: CmdBuild,
		},
	}

	cliApp.Run(os.Args)

}

func Init(c *cli.Context) error {

	utils.InitLogger(c.Bool("verbose"))
	killChannel = make(chan os.Signal, 1)
	endChannel = make(chan error)

	// Start by checking if config exist
	cfg, err := engine.NewConfig(c.GlobalString("config"), c.GlobalString("docker"))
	if err != nil {
		return fmt.Errorf("Could not start smuggler with adapter %s: %s", c.GlobalString("docker"), err)
	}

	// Start the engine with the right adapter
	eng, err = engine.New(cfg)
	if err != nil {
		return fmt.Errorf("%s: %s", c.GlobalString("docker"), err)
	}

	// Setup the application
	smgapp := &engine.Application{
		FilePath:      c.String("start"),
		Repository:    cfg.Repository,
		UseDockerfile: !c.Bool("shared-folder"),
		Uptodate:      c.Bool("last"),
		NoCache:       c.Bool("no-cache"),
		KeepAlive:     c.Bool("keepalive"),
	}

	// FIXME : setup overrides
	smgapp.SetOverrides(c.StringSlice("override"))

	// Either if it's a build or a run we need to init smuggler
	err = eng.Init(smgapp)
	if err != nil {
		return fmt.Errorf("Init failed with smuggler file, %s", err)
	}

	// catch the CTRL-C
	signal.Notify(killChannel, os.Interrupt)

	return nil
}

func CmdBuild(c *cli.Context) {
	err := Init(c)
	if err != nil {
		log.Fatalf("%s", err)
	}
	go func() {
		endChannel <- eng.Build(c.Bool("push"), c.Bool("delete"), c.StringSlice("etcd"))
	}()
	select {
	case err := <-endChannel:
		{
			if err != nil {
				log.Fatalf("%s", err)
			}
		}
	case <-killChannel:
		eng.Stop()
	}
}

func CmdRun(c *cli.Context) {
	err := Init(c)
	if err != nil {
		log.Fatalf("%s", err)
	}
	go func() {
		endChannel <- eng.Run(c.String("env"))
	}()
	select {
	case err := <-endChannel:
		{
			if err != nil {
				log.Fatalf("%s", err)
			}
		}
	case <-killChannel:
		eng.Stop()
	}
}
<commit_msg>fixe when no args are passed to smg<commit_after>package main

import (
	"fmt"
	log "github.com/Sirupsen/logrus"
	"github.com/codegangsta/cli"
	"github.com/jbdalido/smg/engine"
	"github.com/jbdalido/smg/utils"
	"os"
	"os/signal"
)

var (
	smgapp      *engine.Application
	eng         *engine.Engine
	killChannel chan os.Signal
	endChannel  chan error
)

func main() {

	cliApp := cli.App{
		Name:    "smg",
		Usage:   "Run and Build docker - https://smuggler.io",
		Version: "0.4.1",
	}

	cliApp.Flags = []cli.Flag{
		cli.StringFlag{
			Name:  "docker, d",
			Value: "",
			Usage: "Docker endpoint",
		},
		cli.StringFlag{
			Name:   "config, c",
			Value:  "~/.smg.yml",
			Usage:  "Config file to use",
			EnvVar: "SMG_CONFIG",
		},
	}

	buildFlags := []cli.Flag{
		cli.StringFlag{
			Name:  "start, s",
			Value: "smg.yml",
			Usage: "Specify a different file to use for your smg run (default: smg.yml)",
		},
		cli.BoolFlag{
			Name:  "no-cache, n",
			Usage: "Disable the use of docker cache during run and build with provided dockerfiles",
		},
		cli.BoolFlag{
			Name:  "verbose, v",
			Usage: "Verbose Mode",
		},
		cli.BoolFlag{
			Name:  "push, p",
			Usage: "Push images after a successful build",
		},
		cli.BoolFlag{
			Name:  "last, l",
			Usage: "Download last image for each build",
		},
		cli.BoolFlag{
			Name:  "delete, D",
			Usage: "Delete images created after a successful build",
		},
		cli.StringSliceFlag{
			Name:  "etcd",
			Value: &cli.StringSlice{},
			Usage: "ETCD Storage http endpoint",
		},
	}

	runFlags := []cli.Flag{
		cli.StringFlag{
			Name:  "start, s",
			Value: "smg.yml",
			Usage: "Specify a different file to use for your smg run (default: smg.yml)",
		},
		cli.BoolFlag{
			Name:  "no-cache, n",
			Usage: "Disable the use of docker cache during run and build with provided dockerfiles",
		},
		cli.BoolFlag{
			Name:  "verbose, v",
			Usage: "Verbose Mode",
		},
		cli.StringFlag{
			Name:  "env, e",
			Value: "default",
			Usage: "Environment (commands or dockerfiles) to use for the run",
		},
		cli.StringSliceFlag{
			Name:  "override, o",
			Value: &cli.StringSlice{},
			Usage: "Environment (commands or dockerfiles) to use for the run",
		},
		cli.BoolFlag{
			Name:  "keepalive, k",
			Usage: "Keep containers alive after a run (successful or not)",
		},
		cli.BoolFlag{
			Name:  "shared-folder, S",
			Usage: "Use a shared-folder with the main container",
		},
	}

	cliApp.HideVersion = true

	cliApp.Commands = []cli.Command{
		cli.Command{
			Name:   "run",
			Usage:  "Run containers with the proper environment",
			Flags:  runFlags,
			Action: CmdRun,
		},
		cli.Command{
			Name:   "build",
			Usage:  "Build against the active git branch of the folder and the build setup of the smg file",
			Flags:  buildFlags,
			Action: CmdBuild,
		},
	}

	// TODO : something is not right here !
	if len(os.Args) == 1 {
		os.Args = append(os.Args, "help")
	}
	cliApp.Run(os.Args)

}

func Init(c *cli.Context) error {

	utils.InitLogger(c.Bool("verbose"))
	killChannel = make(chan os.Signal, 1)
	endChannel = make(chan error)

	// Start by checking if config exist
	cfg, err := engine.NewConfig(c.GlobalString("config"), c.GlobalString("docker"))
	if err != nil {
		return fmt.Errorf("Could not start smuggler with adapter %s: %s", c.GlobalString("docker"), err)
	}

	// Start the engine with the right adapter
	eng, err = engine.New(cfg)
	if err != nil {
		return fmt.Errorf("%s: %s", c.GlobalString("docker"), err)
	}

	// Setup the application
	smgapp := &engine.Application{
		FilePath:      c.String("start"),
		Repository:    cfg.Repository,
		UseDockerfile: !c.Bool("shared-folder"),
		Uptodate:      c.Bool("last"),
		NoCache:       c.Bool("no-cache"),
		KeepAlive:     c.Bool("keepalive"),
	}

	// FIXME : setup overrides
	smgapp.SetOverrides(c.StringSlice("override"))

	// Either if it's a build or a run we need to init smuggler
	err = eng.Init(smgapp)
	if err != nil {
		return fmt.Errorf("Init failed with smuggler file, %s", err)
	}

	// catch the CTRL-C
	signal.Notify(killChannel, os.Interrupt)

	return nil
}

func CmdBuild(c *cli.Context) {
	err := Init(c)
	if err != nil {
		log.Fatalf("%s", err)
	}
	go func() {
		endChannel <- eng.Build(c.Bool("push"), c.Bool("delete"), c.StringSlice("etcd"))
	}()
	select {
	case err := <-endChannel:
		{
			if err != nil {
				log.Fatalf("%s", err)
			}
		}
	case <-killChannel:
		eng.Stop()
	}
}

func CmdRun(c *cli.Context) {
	err := Init(c)
	if err != nil {
		log.Fatalf("%s", err)
	}
	go func() {
		endChannel <- eng.Run(c.String("env"))
	}()
	select {
	case err := <-endChannel:
		{
			if err != nil {
				log.Fatalf("%s", err)
			}
		}
	case <-killChannel:
		eng.Stop()
	}
}
<|endoftext|>


<commit_before>/*
Ginkgomon provides ginkgo test helpers.
*/
package ginkgomon

import (
	"fmt"
	"io"
	"os"
	"os/exec"
	"time"

	"github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
	"github.com/onsi/gomega/gbytes"
	"github.com/onsi/gomega/gexec"
)

// Config defines a ginkgomon Runner.
type Config struct {
	Command           *exec.Cmd 		// process to be executed
	Name              string 				// prefixes all output lines
	AnsiColorCode     string 				// colors the output
	StartCheck        string 				// text to match to indicate sucessful start.
	StartCheckTimeout time.Duration // how long to wait to see StartCheck
	Cleanup           func() 				// invoked once the process exits
}

/*
The ginkgomon Runner invokes a new process using gomega's gexec package.

If a start check is defined, the runner will wait until it sees the start check
before declaring ready.

Runner implements gexec.Exiter and gbytes.BufferProvider, so you can test exit
codes and process output using the appropriate gomega matchers:
http://onsi.github.io/gomega/#gexec-testing-external-processes
*/
type Runner struct {
	Command           *exec.Cmd
	Name              string
	AnsiColorCode     string
	StartCheck        string
	StartCheckTimeout time.Duration
	Cleanup           func()
	session           *gexec.Session
	sessionReady      chan struct{}
}

// New creates a ginkgomon Runner from a config object. Runners must be created
// with New to properly initialize their internal state.
func New(config Config) *Runner {
	return &Runner{
		Name:              config.Name,
		Command:           config.Command,
		AnsiColorCode:     config.AnsiColorCode,
		StartCheck:        config.StartCheck,
		StartCheckTimeout: config.StartCheckTimeout,
		Cleanup:           config.Cleanup,
		sessionReady:      make(chan struct{}),
	}
}

// ExitCode returns the exit code of the process, or -1 if the process has not
// exited.  It can be used with the gexec.Exit matcher.
func (r *Runner) ExitCode() int {
	if r.sessionReady == nil {
		ginkgo.Fail(fmt.Sprintf("ginkgomon.Runner '%s' improperly created without using New",r.Name))
	}
	<-r.sessionReady
	return r.session.ExitCode()
}

// Buffer returns a gbytes.Buffer, for use with the gbytes.Say matcher.
func (r *Runner) Buffer() *gbytes.Buffer {
	if r.sessionReady == nil {
		ginkgo.Fail(fmt.Sprintf("ginkgomon.Runner '%s' improperly created without using New",r.Name))
	}
	<-r.sessionReady
	return r.session.Buffer()
}

func (r *Runner) Run(sigChan <-chan os.Signal, ready chan<- struct{}) error {
	defer ginkgo.GinkgoRecover()

	allOutput := gbytes.NewBuffer()

	session, err := gexec.Start(
		r.Command,
		gexec.NewPrefixedWriter(
			fmt.Sprintf("\x1b[32m[o]\x1b[%s[%s]\x1b[0m ", r.AnsiColorCode, r.Name),
			io.MultiWriter(allOutput, ginkgo.GinkgoWriter),
		),
		gexec.NewPrefixedWriter(
			fmt.Sprintf("\x1b[91m[e]\x1b[%s[%s]\x1b[0m ", r.AnsiColorCode, r.Name),
			io.MultiWriter(allOutput, ginkgo.GinkgoWriter),
		),
	)

	Ω(err).ShouldNot(HaveOccurred())

	r.session = session
	if r.sessionReady != nil {
		close(r.sessionReady)
	}

	startCheckDuration := r.StartCheckTimeout
	if startCheckDuration == 0 {
		startCheckDuration = 5 * time.Second
	}

	var startCheckTimeout <-chan time.Time
	if r.StartCheck != "" {
		startCheckTimeout = time.After(startCheckDuration)
	}

	detectStartCheck := allOutput.Detect(r.StartCheck)

	for {
		select {
		case <-detectStartCheck: // works even with empty string
			allOutput.CancelDetects()
			startCheckTimeout = nil
			detectStartCheck = nil
			close(ready)

		case <-startCheckTimeout:
			// clean up hanging process
			session.Kill().Wait()

			// fail to start
			return fmt.Errorf(
				"did not see %s in command's output within %s. full output:\n\n%s",
				r.StartCheck,
				startCheckDuration,
				string(allOutput.Contents()),
			)

		case signal := <-sigChan:
			session.Signal(signal)

		case <-session.Exited:
			if r.Cleanup != nil {
				r.Cleanup()
			}

			if session.ExitCode() == 0 {
				return nil
			}

			return fmt.Errorf("exit status %d", session.ExitCode())
		}
	}
}
<commit_msg>ginkgomon: print spawned process's pid<commit_after>/*
Ginkgomon provides ginkgo test helpers.
*/
package ginkgomon

import (
	"fmt"
	"io"
	"os"
	"os/exec"
	"time"

	"github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
	"github.com/onsi/gomega/gbytes"
	"github.com/onsi/gomega/gexec"
)

// Config defines a ginkgomon Runner.
type Config struct {
	Command           *exec.Cmd     // process to be executed
	Name              string        // prefixes all output lines
	AnsiColorCode     string        // colors the output
	StartCheck        string        // text to match to indicate sucessful start.
	StartCheckTimeout time.Duration // how long to wait to see StartCheck
	Cleanup           func()        // invoked once the process exits
}

/*
The ginkgomon Runner invokes a new process using gomega's gexec package.

If a start check is defined, the runner will wait until it sees the start check
before declaring ready.

Runner implements gexec.Exiter and gbytes.BufferProvider, so you can test exit
codes and process output using the appropriate gomega matchers:
http://onsi.github.io/gomega/#gexec-testing-external-processes
*/
type Runner struct {
	Command           *exec.Cmd
	Name              string
	AnsiColorCode     string
	StartCheck        string
	StartCheckTimeout time.Duration
	Cleanup           func()
	session           *gexec.Session
	sessionReady      chan struct{}
}

// New creates a ginkgomon Runner from a config object. Runners must be created
// with New to properly initialize their internal state.
func New(config Config) *Runner {
	return &Runner{
		Name:              config.Name,
		Command:           config.Command,
		AnsiColorCode:     config.AnsiColorCode,
		StartCheck:        config.StartCheck,
		StartCheckTimeout: config.StartCheckTimeout,
		Cleanup:           config.Cleanup,
		sessionReady:      make(chan struct{}),
	}
}

// ExitCode returns the exit code of the process, or -1 if the process has not
// exited.  It can be used with the gexec.Exit matcher.
func (r *Runner) ExitCode() int {
	if r.sessionReady == nil {
		ginkgo.Fail(fmt.Sprintf("ginkgomon.Runner '%s' improperly created without using New", r.Name))
	}
	<-r.sessionReady
	return r.session.ExitCode()
}

// Buffer returns a gbytes.Buffer, for use with the gbytes.Say matcher.
func (r *Runner) Buffer() *gbytes.Buffer {
	if r.sessionReady == nil {
		ginkgo.Fail(fmt.Sprintf("ginkgomon.Runner '%s' improperly created without using New", r.Name))
	}
	<-r.sessionReady
	return r.session.Buffer()
}

func (r *Runner) Run(sigChan <-chan os.Signal, ready chan<- struct{}) error {
	defer ginkgo.GinkgoRecover()

	allOutput := gbytes.NewBuffer()

	debugWriter := gexec.NewPrefixedWriter(
		fmt.Sprintf("\x1b[32m[d]\x1b[%s[%s]\x1b[0m ", r.AnsiColorCode, r.Name),
		ginkgo.GinkgoWriter,
	)

	session, err := gexec.Start(
		r.Command,
		gexec.NewPrefixedWriter(
			fmt.Sprintf("\x1b[32m[o]\x1b[%s[%s]\x1b[0m ", r.AnsiColorCode, r.Name),
			io.MultiWriter(allOutput, ginkgo.GinkgoWriter),
		),
		gexec.NewPrefixedWriter(
			fmt.Sprintf("\x1b[91m[e]\x1b[%s[%s]\x1b[0m ", r.AnsiColorCode, r.Name),
			io.MultiWriter(allOutput, ginkgo.GinkgoWriter),
		),
	)

	Ω(err).ShouldNot(HaveOccurred())

	fmt.Fprintf(debugWriter, "spawned %s (pid: %d)\n", r.Command.Path, r.Command.Process.Pid)

	r.session = session
	if r.sessionReady != nil {
		close(r.sessionReady)
	}

	startCheckDuration := r.StartCheckTimeout
	if startCheckDuration == 0 {
		startCheckDuration = 5 * time.Second
	}

	var startCheckTimeout <-chan time.Time
	if r.StartCheck != "" {
		startCheckTimeout = time.After(startCheckDuration)
	}

	detectStartCheck := allOutput.Detect(r.StartCheck)

	for {
		select {
		case <-detectStartCheck: // works even with empty string
			allOutput.CancelDetects()
			startCheckTimeout = nil
			detectStartCheck = nil
			close(ready)

		case <-startCheckTimeout:
			// clean up hanging process
			session.Kill().Wait()

			// fail to start
			return fmt.Errorf(
				"did not see %s in command's output within %s. full output:\n\n%s",
				r.StartCheck,
				startCheckDuration,
				string(allOutput.Contents()),
			)

		case signal := <-sigChan:
			session.Signal(signal)

		case <-session.Exited:
			if r.Cleanup != nil {
				r.Cleanup()
			}

			if session.ExitCode() == 0 {
				return nil
			}

			return fmt.Errorf("exit status %d", session.ExitCode())
		}
	}
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"os"
	"os/user"
)

func main() {
	os.Exit(realMain())
}

func realMain() int {
	found, err := nsenterdetect()
	if err != nil {
		fmt.Fprintf(os.Stderr, "cound not detect if nsenter was installed: %s\n", err)
		return 1
	}
	if !found {
		fmt.Fprintf(os.Stderr, "nsenter is not installed\n")
		fmt.Fprintf(os.Stderr, "run boot2docker ssh 'docker run --rm -v /var/lib/boot2docker/:/target bobtfish/nsenter'\n")
		return 1
	}
	/* Woo! We found nsenter, now to move onto more interesting things */
	u, err2 := user.Current()
	if err2 != nil {
		fmt.Fprintf(os.Stderr, "Current: %v", err2)
	}
	if u.HomeDir == "" {
		fmt.Fprintf(os.Stderr, "didn't get a HomeDir")
	}
	if u.Username == "" {
		fmt.Fprintf(os.Stderr, "didn't get a username")
	}

	var container_name = fmt.Sprintf("%s_dockersh", u.Username)

	pid, err, out := dockerpid(container_name)
	if err != nil {
		pid, err, out = dockerstart(u.Username, u.HomeDir, container_name, "busybox")
		if err != nil {
			fmt.Fprintf(os.Stderr, "cound not start container: %s: %s\n", err, out)
			return 1
		}
	}
	// pid int, uid int, git int, wd string, shell string
	nsenterexec(pid, 0, 0, "/", "/bin/ash")
	return 0
}
<commit_msg>Pass the right UID and GID down<commit_after>package main

import (
	"fmt"
	"os"
	"os/user"
	"strconv"
)

func main() {
	os.Exit(realMain())
}

func realMain() int {
	found, err := nsenterdetect()
	if err != nil {
		fmt.Fprintf(os.Stderr, "cound not detect if nsenter was installed: %s\n", err)
		return 1
	}
	if !found {
		fmt.Fprintf(os.Stderr, "nsenter is not installed\n")
		fmt.Fprintf(os.Stderr, "run boot2docker ssh 'docker run --rm -v /var/lib/boot2docker/:/target bobtfish/nsenter'\n")
		return 1
	}
	/* Woo! We found nsenter, now to move onto more interesting things */
	u, err2 := user.Current()
	if err2 != nil {
		fmt.Fprintf(os.Stderr, "Current: %v", err2)
	}
	if u.HomeDir == "" {
		fmt.Fprintf(os.Stderr, "didn't get a HomeDir")
	}
	if u.Username == "" {
		fmt.Fprintf(os.Stderr, "didn't get a username")
	}

	var container_name = fmt.Sprintf("%s_dockersh", u.Username)

	pid, err, out := dockerpid(container_name)
	if err != nil {
		pid, err, out = dockerstart(u.Username, u.HomeDir, container_name, "busybox")
		if err != nil {
			fmt.Fprintf(os.Stderr, "cound not start container: %s: %s\n", err, out)
			return 1
		}
	}
	// pid int, uid int, git int, wd string, shell string
	var uid int
	uid, err = strconv.Atoi(u.Uid)
	var gid int
	gid, err = strconv.Atoi(u.Gid)
	nsenterexec(pid, uid, gid, u.HomeDir, "/bin/ash")
	return 0
}
<|endoftext|>


<commit_before>/*
Copyright 2011 Google 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.
*/

package mysqlindexer

import ()

const requiredSchemaVersion = 18

func SchemaVersion() int {
	return requiredSchemaVersion
}

func SQLCreateTables() []string {
	return []string{

		`CREATE TABLE blobs (
blobref VARCHAR(128) NOT NULL PRIMARY KEY,
size INTEGER NOT NULL,
type VARCHAR(100))`,

		`CREATE TABLE claims (
blobref VARCHAR(128) NOT NULL PRIMARY KEY,
signer VARCHAR(128) NOT NULL,
verifiedkeyid VARCHAR(128) NULL,
date VARCHAR(40) NOT NULL,
  INDEX (signer, date),
  INDEX (verifiedkeyid, date),
unverified CHAR(1) NULL,
claim VARCHAR(50) NOT NULL,
permanode VARCHAR(128) NOT NULL,
  INDEX (permanode, signer, date),
attr VARCHAR(128) NULL,
value VARCHAR(128) NULL)`,

		`CREATE TABLE permanodes (
blobref VARCHAR(128) NOT NULL PRIMARY KEY,
unverified CHAR(1) NULL,
signer VARCHAR(128) NOT NULL DEFAULT '',
lastmod VARCHAR(40) NOT NULL DEFAULT '',
INDEX (signer, lastmod))`,

		`CREATE TABLE bytesfiles (
schemaref VARCHAR(128) NOT NULL,
camlitype VARCHAR(32) NOT NULL,
wholedigest VARCHAR(128) NOT NULL,
filename VARCHAR(255),
size BIGINT,
mime VARCHAR(255),
PRIMARY KEY(schemaref, wholedigest),
INDEX (wholedigest))`,

		// For index.PermanodeOfSignerAttrValue:
		// Rows are one per camliType "claim", for claimType "set-attribute" or "add-attribute",
		// for attribute values that are known (needed to be indexed, e.g. "camliNamedRoot")
		//
		// keyid is verified GPG KeyId (e.g. "2931A67C26F5ABDA")
		// attr is e.g. "camliNamedRoot"
		// value is the claim's "value" field
		// claimdate is the "claimDate" field.
		// blobref is the blobref of the claim.
		// permanode is the claim's "permaNode" field.
		`CREATE TABLE signerattrvalue (
keyid VARCHAR(128) NOT NULL,
attr VARCHAR(128) NOT NULL,
value VARCHAR(255) NOT NULL,
claimdate VARCHAR(40) NOT NULL,
INDEX (keyid, attr, value, claimdate),
blobref VARCHAR(128) NOT NULL,
PRIMARY KEY (blobref),
permanode VARCHAR(128) NOT NULL,
INDEX (permanode))`,

		// "Shadow" copy of signerattrvalue for fulltext searches.
		// Kept in sync witch signerattrvalue directly in the go code for now, not with triggers.
		// As of MySQL 5.5, fulltext search is still only available with MyISAM tables
		// (see http://dev.mysql.com/doc/refman/5.5/en/fulltext-search.html)
		`CREATE TABLE signerattrvalueft (
keyid VARCHAR(128) NOT NULL,
attr VARCHAR(128) NOT NULL,
value VARCHAR(255) NOT NULL,
claimdate VARCHAR(40) NOT NULL,
INDEX (keyid, attr, value, claimdate),
blobref VARCHAR(128) NOT NULL,
PRIMARY KEY (blobref),
permanode VARCHAR(128) NOT NULL,
INDEX (permanode),
FULLTEXT (value)) TYPE=MyISAM`,

		`CREATE TABLE meta (
metakey VARCHAR(255) NOT NULL PRIMARY KEY,
value VARCHAR(255) NOT NULL)`,

		// Map from blobref (of ASCII armored public key) to keyid
		`CREATE TABLE signerkeyid (
blobref VARCHAR(128) NOT NULL,
PRIMARY KEY (blobref),
keyid   VARCHAR(128) NOT NULL,
INDEX (keyid)
)`,

		// Bi-direction index of camliPath claims
		// active is "Y" or "N".
		`CREATE TABLE path (
claimref VARCHAR(128) NOT NULL,
PRIMARY KEY (claimref),
claimdate VARCHAR(40) NOT NULL,
keyid VARCHAR(128) NOT NULL,
baseref VARCHAR(128) NOT NULL,
suffix VARCHAR(255) NOT NULL,
targetref VARCHAR(128) NOT NULL,
active CHAR(1) NOT NULL,
INDEX (keyid, baseref, suffix),
INDEX (targetref, keyid),
INDEX (baseref, keyid)
)`,
	}
}
<commit_msg>shrink some columns, use ENGINE= instead of TYPE=<commit_after>/*
Copyright 2011 Google 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.
*/

package mysqlindexer

import ()

const requiredSchemaVersion = 18

func SchemaVersion() int {
	return requiredSchemaVersion
}

func SQLCreateTables() []string {
	return []string{

		`CREATE TABLE blobs (
blobref VARCHAR(128) NOT NULL PRIMARY KEY,
size INTEGER NOT NULL,
type VARCHAR(100))`,

		`CREATE TABLE claims (
blobref VARCHAR(128) NOT NULL PRIMARY KEY,
signer VARCHAR(128) NOT NULL,
verifiedkeyid VARCHAR(128) NULL,
date VARCHAR(40) NOT NULL,
  INDEX (signer, date),
  INDEX (verifiedkeyid, date),
unverified CHAR(1) NULL,
claim VARCHAR(50) NOT NULL,
permanode VARCHAR(128) NOT NULL,
  INDEX (permanode, signer, date),
attr VARCHAR(128) NULL,
value VARCHAR(128) NULL)`,

		`CREATE TABLE permanodes (
blobref VARCHAR(128) NOT NULL PRIMARY KEY,
unverified CHAR(1) NULL,
signer VARCHAR(128) NOT NULL DEFAULT '',
lastmod VARCHAR(40) NOT NULL DEFAULT '',
INDEX (signer, lastmod))`,

		`CREATE TABLE bytesfiles (
schemaref VARCHAR(128) NOT NULL,
camlitype VARCHAR(32) NOT NULL,
wholedigest VARCHAR(128) NOT NULL,
filename VARCHAR(255),
size BIGINT,
mime VARCHAR(255),
PRIMARY KEY(schemaref, wholedigest),
INDEX (wholedigest))`,

		// For index.PermanodeOfSignerAttrValue:
		// Rows are one per camliType "claim", for claimType "set-attribute" or "add-attribute",
		// for attribute values that are known (needed to be indexed, e.g. "camliNamedRoot")
		//
		// keyid is verified GPG KeyId (e.g. "2931A67C26F5ABDA")
		// attr is e.g. "camliNamedRoot"
		// value is the claim's "value" field
		// claimdate is the "claimDate" field.
		// blobref is the blobref of the claim.
		// permanode is the claim's "permaNode" field.
		`CREATE TABLE signerattrvalue (
keyid VARCHAR(40) NOT NULL,
attr VARCHAR(128) NOT NULL,
value VARCHAR(255) NOT NULL,
claimdate VARCHAR(40) NOT NULL,
INDEX (keyid, attr, value, claimdate),
blobref VARCHAR(128) NOT NULL,
PRIMARY KEY (blobref),
permanode VARCHAR(128) NOT NULL,
INDEX (permanode))`,

		// "Shadow" copy of signerattrvalue for fulltext searches.
		// Kept in sync witch signerattrvalue directly in the go code for now, not with triggers.
		// As of MySQL 5.5, fulltext search is still only available with MyISAM tables
		// (see http://dev.mysql.com/doc/refman/5.5/en/fulltext-search.html)
		`CREATE TABLE signerattrvalueft (
keyid VARCHAR(40) NOT NULL,
attr VARCHAR(128) NOT NULL,
value VARCHAR(255) NOT NULL,
claimdate VARCHAR(40) NOT NULL,
INDEX (keyid, attr, value, claimdate),
blobref VARCHAR(128) NOT NULL,
PRIMARY KEY (blobref),
permanode VARCHAR(128) NOT NULL,
INDEX (permanode),
FULLTEXT (value)) ENGINE=MyISAM`,

		`CREATE TABLE meta (
metakey VARCHAR(255) NOT NULL PRIMARY KEY,
value VARCHAR(255) NOT NULL)`,

		// Map from blobref (of ASCII armored public key) to keyid
		`CREATE TABLE signerkeyid (
blobref VARCHAR(128) NOT NULL,
PRIMARY KEY (blobref),
keyid   VARCHAR(128) NOT NULL,
INDEX (keyid)
)`,

		// Bi-direction index of camliPath claims
		// active is "Y" or "N".
		`CREATE TABLE path (
claimref VARCHAR(128) NOT NULL,
PRIMARY KEY (claimref),
claimdate VARCHAR(40) NOT NULL,
keyid VARCHAR(128) NOT NULL,
baseref VARCHAR(128) NOT NULL,
suffix VARCHAR(255) NOT NULL,
targetref VARCHAR(128) NOT NULL,
active CHAR(1) NOT NULL,
INDEX (keyid, baseref, suffix),
INDEX (targetref, keyid),
INDEX (baseref, keyid)
)`,
	}
}
<|endoftext|>


<commit_before>package engine

import (
	"crypto/rand"
	"fmt"
	"sync"
	"testing"

	"github.com/keybase/client/go/kex2"
	"github.com/keybase/client/go/libkb"
)

func TestKex2Provision(t *testing.T) {
	// device X (provisioner) context:
	tcX := SetupEngineTest(t, "kex2provision")
	defer tcX.Cleanup()

	// provisioner needs to be logged in
	userX := CreateAndSignupFakeUser(tcX, "login")

	// device Y (provisionee) context:
	tcY := SetupEngineTest(t, "kex2provision")
	defer tcY.Cleanup()

	var secretX kex2.Secret
	if _, err := rand.Read(secretX[:]); err != nil {
		t.Fatal(err)
	}

	var secretY kex2.Secret
	if _, err := rand.Read(secretY[:]); err != nil {
		t.Fatal(err)
	}

	var wg sync.WaitGroup

	// start provisionee
	wg.Add(1)
	go func() {
		defer wg.Done()
		ctx := &Context{}
		deviceID, err := libkb.NewDeviceID()
		if err != nil {
			t.Errorf("provisionee device id error: %s", err)
			return
		}
		suffix, err := libkb.RandBytes(5)
		if err != nil {
			t.Errorf("provisionee device suffix error: %s", err)
			return
		}
		dname := fmt.Sprintf("device_%x", suffix)
		device := &libkb.Device{
			ID:          deviceID,
			Description: &dname,
			Type:        libkb.DeviceTypeDesktop,
		}
		provisionee := NewKex2Provisionee(tcY.G, device, secretY)
		if err := RunEngine(provisionee, ctx); err != nil {
			t.Errorf("provisionee error: %s", err)
			return
		}
	}()

	// start provisioner
	wg.Add(1)
	go func() {
		defer wg.Done()
		ctx := &Context{
			SecretUI: userX.NewSecretUI(),
		}
		deviceID, err := libkb.NewDeviceID()
		if err != nil {
			t.Errorf("provisioner device id error: %s", err)
			return
		}
		provisioner := NewKex2Provisioner(tcX.G, deviceID, secretX)
		go provisioner.AddSecret(secretY)
		if err := RunEngine(provisioner, ctx); err != nil {
			t.Errorf("provisioner error: %s", err)
			return
		}
	}()

	wg.Wait()
}
<commit_msg>Fix Kex2 test<commit_after>package engine

import (
	"crypto/rand"
	"fmt"
	"sync"
	"testing"

	"github.com/keybase/client/go/kex2"
	"github.com/keybase/client/go/libkb"
)

func TestKex2Provision(t *testing.T) {
	// device X (provisioner) context:
	tcX := SetupEngineTest(t, "kex2provision")
	defer tcX.Cleanup()

	// provisioner needs to be logged in
	userX := CreateAndSignupFakeUser(tcX, "login")

	// device Y (provisionee) context:
	tcY := SetupEngineTest(t, "kex2provision")
	defer tcY.Cleanup()

	var secretX kex2.Secret
	if _, err := rand.Read(secretX[:]); err != nil {
		t.Fatal(err)
	}

	var secretY kex2.Secret
	if _, err := rand.Read(secretY[:]); err != nil {
		t.Fatal(err)
	}

	var wg sync.WaitGroup

	// start provisionee
	wg.Add(1)
	go func() {
		defer wg.Done()
		ctx := &Context{
			ProvisionUI: &testProvisionUI{secretCh: make(chan kex2.Secret, 1)},
		}
		deviceID, err := libkb.NewDeviceID()
		if err != nil {
			t.Errorf("provisionee device id error: %s", err)
			return
		}
		suffix, err := libkb.RandBytes(5)
		if err != nil {
			t.Errorf("provisionee device suffix error: %s", err)
			return
		}
		dname := fmt.Sprintf("device_%x", suffix)
		device := &libkb.Device{
			ID:          deviceID,
			Description: &dname,
			Type:        libkb.DeviceTypeDesktop,
		}
		provisionee := NewKex2Provisionee(tcY.G, device, secretY)
		if err := RunEngine(provisionee, ctx); err != nil {
			t.Errorf("provisionee error: %s", err)
			return
		}
	}()

	// start provisioner
	wg.Add(1)
	go func() {
		defer wg.Done()
		ctx := &Context{
			SecretUI:    userX.NewSecretUI(),
			ProvisionUI: &testProvisionUI{},
		}
		deviceID, err := libkb.NewDeviceID()
		if err != nil {
			t.Errorf("provisioner device id error: %s", err)
			return
		}
		provisioner := NewKex2Provisioner(tcX.G, deviceID, secretX)
		go provisioner.AddSecret(secretY)
		if err := RunEngine(provisioner, ctx); err != nil {
			t.Errorf("provisioner error: %s", err)
			return
		}
	}()

	wg.Wait()
}
<|endoftext|>


<commit_before>package goblas

import "github.com/gonum/blas"

var _ blas.Float64Level3 = Blasser

const (
	// TODO (btracey): Fix the ld panic messages to be consistent across the package
	badLd string = "goblas: ld must be greater than the number of columns"
)

// Dtrsm solves
//  A X = alpha B
// if side is Left or
//  X A = alpha B
// if side is Right
// where X and B are m x n matrices, and A is a unit or non unit upper or lower
// triangular matrix. The result is stored in place into B. No check is made
// that A is invertible.
func (bl Blas) Dtrsm(s blas.Side, ul blas.Uplo, tA blas.Transpose, d blas.Diag, m, n int, alpha float64, a []float64, lda int, b []float64, ldb int) {
	if s != blas.Left && s != blas.Right {
		panic(badSide)
	}
	if ul != blas.Lower && ul != blas.Upper {
		panic(badUplo)
	}
	if tA != blas.NoTrans && tA != blas.Trans && tA != blas.ConjTrans {
		panic(badTranspose)
	}
	if d != blas.NonUnit && d != blas.Unit {
		panic(badDiag)
	}
	if m < 0 {
		panic(mLT0)
	}
	if n < 0 {
		panic(nLT0)
	}
	if ldb < n {
		panic(badLd)
	}
	if s == blas.Left {
		if lda < m {
			panic(badLd)
		}
	} else {
		if lda < n {
			panic(badLd)
		}
	}

	if m == 0 || n == 0 {
		return
	}

	if alpha == 0 {
		for i := 0; i < m; i++ {
			btmp := b[i*ldb : i*ldb+n]
			for j := range btmp {
				btmp[j] = 0
			}
		}
		return
	}
	nonUnit := d == blas.NonUnit
	if s == blas.Left {
		if tA == blas.NoTrans {
			if ul == blas.Upper {
				for i := m - 1; i >= 0; i-- {
					atmp := a[i*lda : i*lda+m]
					btmp := b[i*ldb : i*ldb+n]
					for j := 0; j < n; j++ {
						btmp[j] *= alpha
					}
					for k := i + 1; k < m; k++ {
						binner := b[k*ldb : k*ldb+n]
						ak := atmp[k]
						for j, v := range binner {
							btmp[j] -= v * ak
						}
					}
					if nonUnit {
						ai := atmp[i]
						for j := 0; j < n; j++ {
							btmp[j] /= ai
						}
					}
				}
				return
			}
			for i := 0; i < m; i++ {
				atmp := a[i*lda : i*lda+m]
				btmp := b[i*ldb : i*ldb+n]
				for j := 0; j < n; j++ {
					btmp[j] *= alpha
				}
				for k := 0; k < i; k++ {
					ak := a[i*lda+k]
					binner := b[k*ldb : k*ldb+n]
					for j, v := range binner {
						btmp[j] -= v * ak
					}
				}
				if nonUnit {
					ai := atmp[i]
					for j := 0; j < n; j++ {
						btmp[j] /= ai
					}
				}
			}
			return
		}
		// Cases where a is transposed.

		// TODO (btracey): There may be a way to do this without
		// this additional loop over b, but I'm struggling to figure it out,
		// as it's not symmetric with the column-major case.
		// This way at least accesses along rows in the inner loops.
		if ul == blas.Upper {
			if alpha != 1 {
				for i := 0; i < m; i++ {
					btmp := b[i*ldb : i*ldb+n]
					for j := 0; j < n; j++ {
						btmp[j] *= alpha
					}
				}
			}
			for i := 0; i < m; i++ {
				btmp := b[i*ldb : i*ldb+n]
				ai := a[i*lda+i]
				for j := 0; j < n; j++ {
					if nonUnit {
						btmp[j] /= ai
					}
				}
				for ktmp, ak := range a[i*lda+i+1 : i*lda+m] {
					k := i + 1 + ktmp
					binner := b[k*ldb : k*ldb+n]
					for j, v := range btmp {
						binner[j] -= ak * v
					}
				}
			}
			return
		}
		if alpha != 1 {
			for i := 0; i < m; i++ {
				btmp := b[i*ldb : i*ldb+n]
				for j := 0; j < n; j++ {
					btmp[j] *= alpha
				}
			}
		}
		for i := m - 1; i >= 0; i-- {
			btmp := b[i*ldb : i*ldb+n]
			if nonUnit {
				ai := a[i*lda+i]
				for j := 0; j < n; j++ {
					btmp[j] /= ai
				}
			}
			for k, ak := range a[i*lda : i*lda+i] {
				binner := b[k*ldb : k*ldb+n]
				for j, bj := range btmp {
					binner[j] -= ak * bj
				}
			}
		}
		return
	}
	/*
		// Cases where A is to the right of X.
		if tA == blas.NoTrans {
			if ul == blas.Upper {
				for i := 0; i < m; i++ {
					for j := 0; j < n; j++ {
						b[i*ldb+j] *= alpha
					}
					if nonUnit {
						ai := atmp[i]
						for j := 0; j < n; j++ {
							btmp[j] /= ai
						}
					}
				}
			}
		}
	*/
}

// Dsymm performs one of
//  C = alpha * A * B + beta * C
//  C = alpha * B * A + beta * C
// where A is a symmetric matrix and B and C are m x n matrices.
func (Blas) Dsymm(s blas.Side, ul blas.Uplo, m, n int, alpha float64, a []float64, lda int, b []float64, ldb int, beta float64, c []float64, ldc int) {
	if s != blas.Right && s != blas.Left {
		panic("goblas: bad side")
	}
	if ul != blas.Lower && ul != blas.Upper {
		panic(badUplo)
	}
	if m < 0 {
		panic(mLT0)
	}
	if n < 0 {
		panic(nLT0)
	}
	if (lda < m && s == blas.Left) || (lda < n && s == blas.Right) {
		panic(badLd)
	}
	if ldb < n || ldc < n {
		panic(badLd)
	}
	if m == 0 || n == 0 {
		return
	}
	if alpha == 0 && beta == 1 {
		return
	}
	if alpha == 0 {
		if beta == 0 {
			for i := 0; i < m; i++ {
				ctmp := c[i*ldc : i*ldc+n]
				for j := range ctmp {
					ctmp[j] = 0
				}
			}
			return
		}
		for i := 0; i < m; i++ {
			ctmp := c[i*ldc : i*ldc+n]
			for j := 0; j < n; j++ {
				ctmp[j] *= beta
			}
		}
		return
	}

	isUpper := ul == blas.Upper
	if s == blas.Left {
		for i := 0; i < m; i++ {
			atmp := alpha * a[i*lda+i]
			btmp := b[i*ldb : i*ldb+n]
			ctmp := c[i*ldc : i*ldc+n]
			for j, v := range btmp {
				ctmp[j] *= beta
				ctmp[j] += atmp * v
			}
			for k := 0; k < i; k++ {
				var atmp float64
				if isUpper {
					atmp = a[k*lda+i]
				} else {
					atmp = a[i*lda+k]
				}
				atmp *= alpha
				btmp := b[k*ldb : k*ldb+n]
				ctmp := c[i*ldc : i*ldc+n]
				for j, v := range btmp {
					ctmp[j] += atmp * v
				}
			}
			for k := i + 1; k < m; k++ {
				var atmp float64
				if isUpper {
					atmp = a[i*lda+k]
				} else {
					atmp = a[k*lda+i]
				}
				atmp *= alpha
				btmp := b[k*ldb : k*ldb+n]
				ctmp := c[i*ldc : i*ldc+n]
				for j, v := range btmp {
					ctmp[j] += atmp * v
				}
			}
		}
		return
	}
	if isUpper {
		for i := 0; i < m; i++ {
			for j := n - 1; j >= 0; j-- {
				tmp := alpha * b[i*ldb+j]
				var tmp2 float64
				atmp := a[j*lda+j+1 : j*lda+n]
				btmp := b[i*ldb+j+1 : i*ldb+n]
				ctmp := c[i*ldc+j+1 : i*ldc+n]
				for k, v := range atmp {
					ctmp[k] += tmp * v
					tmp2 += btmp[k] * v
				}
				c[i*ldc+j] *= beta
				c[i*ldc+j] += tmp*a[j*lda+j] + alpha*tmp2
			}
		}
		return
	}
	for i := 0; i < m; i++ {
		for j := 0; j < n; j++ {
			tmp := alpha * b[i*ldb+j]
			var tmp2 float64
			atmp := a[j*lda : j*lda+j]
			btmp := b[i*ldb : i*ldb+j]
			ctmp := c[i*ldc : i*ldc+j]
			for k, v := range atmp {
				ctmp[k] += tmp * v
				tmp2 += btmp[k] * v
			}
			c[i*ldc+j] *= beta
			c[i*ldc+j] += tmp*a[j*lda+j] + alpha*tmp2
		}
	}
}
func (Blas) Dsyrk(ul blas.Uplo, t blas.Transpose, n, k int, alpha float64, a []float64, lda int, beta float64, c []float64, ldc int) {
	panic("blas: function not implemented")
}
func (Blas) Dsyr2k(ul blas.Uplo, t blas.Transpose, n, k int, alpha float64, a []float64, lda int, b []float64, ldb int, beta float64, c []float64, ldc int) {
	panic("blas: function not implemented")
}
func (Blas) Dtrmm(s blas.Side, ul blas.Uplo, tA blas.Transpose, d blas.Diag, m, n int, alpha float64, a []float64, lda int, b []float64, ldb int) {
	panic("blas: function not implemented")
}
<commit_msg>Full implementation of Dtrmm with previous issues fixed and tests added<commit_after>package goblas

import "github.com/gonum/blas"

var _ blas.Float64Level3 = Blasser

const (
	// TODO (btracey): Fix the ld panic messages to be consistent across the package
	badLd string = "goblas: ld must be greater than the number of columns"
)

// Dtrsm solves
//  A X = alpha B
// where X and B are m x n matrices, and A is a unit or non unit upper or lower
// triangular matrix. The result is stored in place into B. No check is made
// that A is invertible.
func (bl Blas) Dtrsm(s blas.Side, ul blas.Uplo, tA blas.Transpose, d blas.Diag, m, n int, alpha float64, a []float64, lda int, b []float64, ldb int) {
	if s != blas.Left && s != blas.Right {
		panic(badSide)
	}
	if ul != blas.Lower && ul != blas.Upper {
		panic(badUplo)
	}
	if tA != blas.NoTrans && tA != blas.Trans && tA != blas.ConjTrans {
		panic(badTranspose)
	}
	if d != blas.NonUnit && d != blas.Unit {
		panic(badDiag)
	}
	if m < 0 {
		panic(mLT0)
	}
	if n < 0 {
		panic(nLT0)
	}
	if ldb < n {
		panic(badLd)
	}
	if s == blas.Left {
		if lda < m {
			panic(badLd)
		}
	} else {
		if lda < n {
			panic(badLd)
		}
	}

	if m == 0 || n == 0 {
		return
	}

	if alpha == 0 {
		for i := 0; i < m; i++ {
			btmp := b[i*ldb : i*ldb+n]
			for j := range btmp {
				btmp[j] = 0
			}
		}
		return
	}
	nonUnit := d == blas.NonUnit
	if s == blas.Left {
		if tA == blas.NoTrans {
			if ul == blas.Upper {
				for i := m - 1; i >= 0; i-- {
					btmp := b[i*ldb : i*ldb+n]
					if alpha != 1 {
						for j := range btmp {
							btmp[j] *= alpha
						}
					}
					for ka, va := range a[i*lda+i+1 : i*lda+m] {
						k := ka + i + 1
						if va != 0 {
							for j, vb := range b[k*ldb : k*ldb+n] {
								btmp[j] -= va * vb
							}
						}
					}
					if nonUnit {
						tmp := 1 / a[i*lda+i]
						for j := 0; j < n; j++ {
							btmp[j] *= tmp
						}
					}
				}
				return
			}
			for i := 0; i < m; i++ {
				btmp := b[i*ldb : i*ldb+n]
				if alpha != 1 {
					for j := 0; j < n; j++ {
						btmp[j] *= alpha
					}
				}
				for k, va := range a[i*lda : i*lda+i] {
					if va != 0 {
						for j, vb := range b[k*ldb : k*ldb+n] {
							btmp[j] -= va * vb
						}
					}
				}
				if nonUnit {
					tmp := 1 / a[i*lda+i]
					for j := 0; j < n; j++ {
						btmp[j] *= tmp
					}
				}
			}
			return
		}
		// Cases where a is transposed
		if ul == blas.Upper {
			for k := 0; k < m; k++ {
				btmpk := b[k*ldb : k*ldb+n]
				if nonUnit {
					tmp := 1 / a[k*lda+k]
					for j := 0; j < n; j++ {
						btmpk[j] *= tmp
					}
				}
				for ia, va := range a[k*lda+k+1 : k*lda+m] {
					i := ia + k + 1
					btmp := b[i*ldb : i*ldb+n]
					if va != 0 {
						for j, vb := range btmpk {
							btmp[j] -= va * vb
						}
					}
				}
				if alpha != 1 {
					for j := 0; j < n; j++ {
						btmpk[j] *= alpha
					}
				}
			}
			return
		}
		for k := m - 1; k >= 0; k-- {
			btmpk := b[k*ldb : k*ldb+n]
			if nonUnit {
				tmp := 1 / a[k*lda+k]
				for j := 0; j < n; j++ {
					btmpk[j] *= tmp
				}
			}
			for i, va := range a[k*lda : k*lda+k] {
				btmp := b[i*ldb : i*ldb+n]
				if va != 0 {
					for j, vb := range btmpk {
						btmp[j] -= va * vb
					}
				}
			}
			if alpha != 1 {
				for j := 0; j < n; j++ {
					btmpk[j] *= alpha
				}
			}
		}
		return
	}
	// Cases where a is to the right of X.
	if tA == blas.NoTrans {
		if ul == blas.Upper {
			for i := 0; i < m; i++ {
				btmp := b[i*ldb : i*ldb+n]
				if alpha != 1 {
					for j := 0; j < n; j++ {
						btmp[j] *= alpha
					}
				}
				for k, vb := range btmp {
					if vb != 0 {
						if nonUnit {
							btmp[k] /= a[k*lda+k]
						}
						vb = btmp[k]
						for ja, va := range a[k*lda+k+1 : k*lda+n] {
							j := ja + k + 1
							btmp[j] -= vb * va
						}
					}
				}
			}
			return
		}
		for i := 0; i < m; i++ {
			btmp := b[i*lda : i*lda+n]
			if alpha != 1 {
				for j := 0; j < n; j++ {
					btmp[j] *= alpha
				}
			}
			for k := n - 1; k >= 0; k-- {
				if btmp[k] != 0 {
					if nonUnit {
						btmp[k] /= a[k*lda+k]
					}
				}
				vb := btmp[k]
				for j, va := range a[k*lda : k*lda+k] {
					btmp[j] -= vb * va
				}
			}
		}
		return
	}
	// Cases where a is transposed.
	if ul == blas.Upper {
		for i := 0; i < m; i++ {
			btmp := b[i*lda : i*lda+n]
			for j := n - 1; j >= 0; j-- {
				tmp := alpha * btmp[j]
				for ka, va := range a[j*lda+j+1 : j*lda+n] {
					k := ka + j + 1
					tmp -= va * btmp[k]
				}
				if nonUnit {
					tmp /= a[j*lda+j]
				}
				btmp[j] = tmp
			}
		}
		return
	}
	for i := 0; i < m; i++ {
		btmp := b[i*lda : i*lda+n]
		for j := 0; j < n; j++ {
			tmp := alpha * btmp[j]
			for k, v := range a[j*lda : j*lda+j] {
				tmp -= v * btmp[k]
			}
			if nonUnit {
				tmp /= a[j*lda+j]
			}
			btmp[j] = tmp
		}
	}
}

// Dsymm performs one of
//  C = alpha * A * B + beta * C
//  C = alpha * B * A + beta * C
// where A is a symmetric matrix and B and C are m x n matrices.
func (Blas) Dsymm(s blas.Side, ul blas.Uplo, m, n int, alpha float64, a []float64, lda int, b []float64, ldb int, beta float64, c []float64, ldc int) {
	if s != blas.Right && s != blas.Left {
		panic("goblas: bad side")
	}
	if ul != blas.Lower && ul != blas.Upper {
		panic(badUplo)
	}
	if m < 0 {
		panic(mLT0)
	}
	if n < 0 {
		panic(nLT0)
	}
	if (lda < m && s == blas.Left) || (lda < n && s == blas.Right) {
		panic(badLd)
	}
	if ldb < n || ldc < n {
		panic(badLd)
	}
	if m == 0 || n == 0 {
		return
	}
	if alpha == 0 && beta == 1 {
		return
	}
	if alpha == 0 {
		if beta == 0 {
			for i := 0; i < m; i++ {
				ctmp := c[i*ldc : i*ldc+n]
				for j := range ctmp {
					ctmp[j] = 0
				}
			}
			return
		}
		for i := 0; i < m; i++ {
			ctmp := c[i*ldc : i*ldc+n]
			for j := 0; j < n; j++ {
				ctmp[j] *= beta
			}
		}
		return
	}

	isUpper := ul == blas.Upper
	if s == blas.Left {
		for i := 0; i < m; i++ {
			atmp := alpha * a[i*lda+i]
			btmp := b[i*ldb : i*ldb+n]
			ctmp := c[i*ldc : i*ldc+n]
			for j, v := range btmp {
				ctmp[j] *= beta
				ctmp[j] += atmp * v
			}
			for k := 0; k < i; k++ {
				var atmp float64
				if isUpper {
					atmp = a[k*lda+i]
				} else {
					atmp = a[i*lda+k]
				}
				atmp *= alpha
				btmp := b[k*ldb : k*ldb+n]
				ctmp := c[i*ldc : i*ldc+n]
				for j, v := range btmp {
					ctmp[j] += atmp * v
				}
			}
			for k := i + 1; k < m; k++ {
				var atmp float64
				if isUpper {
					atmp = a[i*lda+k]
				} else {
					atmp = a[k*lda+i]
				}
				atmp *= alpha
				btmp := b[k*ldb : k*ldb+n]
				ctmp := c[i*ldc : i*ldc+n]
				for j, v := range btmp {
					ctmp[j] += atmp * v
				}
			}
		}
		return
	}
	if isUpper {
		for i := 0; i < m; i++ {
			for j := n - 1; j >= 0; j-- {
				tmp := alpha * b[i*ldb+j]
				var tmp2 float64
				atmp := a[j*lda+j+1 : j*lda+n]
				btmp := b[i*ldb+j+1 : i*ldb+n]
				ctmp := c[i*ldc+j+1 : i*ldc+n]
				for k, v := range atmp {
					ctmp[k] += tmp * v
					tmp2 += btmp[k] * v
				}
				c[i*ldc+j] *= beta
				c[i*ldc+j] += tmp*a[j*lda+j] + alpha*tmp2
			}
		}
		return
	}
	for i := 0; i < m; i++ {
		for j := 0; j < n; j++ {
			tmp := alpha * b[i*ldb+j]
			var tmp2 float64
			atmp := a[j*lda : j*lda+j]
			btmp := b[i*ldb : i*ldb+j]
			ctmp := c[i*ldc : i*ldc+j]
			for k, v := range atmp {
				ctmp[k] += tmp * v
				tmp2 += btmp[k] * v
			}
			c[i*ldc+j] *= beta
			c[i*ldc+j] += tmp*a[j*lda+j] + alpha*tmp2
		}
	}
}
func (Blas) Dsyrk(ul blas.Uplo, t blas.Transpose, n, k int, alpha float64, a []float64, lda int, beta float64, c []float64, ldc int) {
	panic("blas: function not implemented")
}
func (Blas) Dsyr2k(ul blas.Uplo, t blas.Transpose, n, k int, alpha float64, a []float64, lda int, b []float64, ldb int, beta float64, c []float64, ldc int) {
	panic("blas: function not implemented")
}
func (Blas) Dtrmm(s blas.Side, ul blas.Uplo, tA blas.Transpose, d blas.Diag, m, n int, alpha float64, a []float64, lda int, b []float64, ldb int) {
	panic("blas: function not implemented")
}
<|endoftext|>


<commit_before>package translators_test

import (
	"github.com/markbates/pop/fizz"
	"github.com/markbates/pop/fizz/translators"
)

func (s *SchemaSuite) Test_Schema_TableInfo() {
	r := s.Require()
	schema := map[string]*fizz.Table{}
	ta := &fizz.Table{Name: "testTable"}
	ta.Column("testColumn", "type", nil)
	schema["testTable"] = ta
	ts := translators.CreateSchema("name", "url", schema)
	t, err := ts.TableInfo("testTable")
	r.NoError(err)
	r.Equal("testTable", t.Name)
}

func (s *SchemaSuite) Test_Schema_ColumnInfo() {
	r := s.Require()
	schema := map[string]*fizz.Table{}
	ta := &fizz.Table{Name: "testTable"}
	ta.Column("testColumn", "type", nil)
	schema["testTable"] = ta
	ts := translators.CreateSchema("name", "url", schema)
	c, err := ts.ColumnInfo("testTable", "testCOLUMN")
	r.NoError(err)
	r.Equal("testColumn", c.Name)
}
<commit_msg>*forgot to add indexinfo test and refactored schema building for tests<commit_after>package translators_test

import (
	"github.com/markbates/pop/fizz"
	"github.com/markbates/pop/fizz/translators"
)

func (s *SchemaSuite) buildSchema() translators.Schema {
	schema := map[string]*fizz.Table{}
	ta := &fizz.Table{Name: "testTable"}
	ta.Column("testColumn", "type", nil)
	ta.Indexes = append(ta.Indexes, fizz.Index{Name: "testIndex"})
	schema["testTable"] = ta
	return translators.CreateSchema("name", "url", schema)
}

func (s *SchemaSuite) Test_Schema_TableInfo() {
	r := s.Require()
	ts := s.buildSchema()
	t, err := ts.TableInfo("testTable")
	r.NoError(err)
	r.Equal("testTable", t.Name)
}

func (s *SchemaSuite) Test_Schema_ColumnInfo() {
	r := s.Require()
	ts := s.buildSchema()
	c, err := ts.ColumnInfo("testTable", "testCOLUMN")
	r.NoError(err)
	r.Equal("testColumn", c.Name)
}

func (s *SchemaSuite) Test_Schema_IndexInfo() {
	r := s.Require()
	ts := s.buildSchema()
	c, err := ts.IndexInfo("testTable", "testindEX")
	r.NoError(err)
	r.Equal("testIndex", c.Name)
}
<|endoftext|>


<commit_before>package gqt_test

import (
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"os/exec"
	"path/filepath"
	"strings"

	"code.cloudfoundry.org/garden"
	"code.cloudfoundry.org/guardian/gqt/runner"
	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
	"github.com/onsi/gomega/gbytes"
)

var _ = Describe("Partially shared containers (peas)", func() {
	var (
		gdn           *runner.RunningGarden
		peaRootfs     string
		ctr           garden.Container
		containerSpec garden.ContainerSpec
	)

	BeforeEach(func() {
		peaRootfs = createPeaRootfs()
		containerSpec = garden.ContainerSpec{}
	})

	JustBeforeEach(func() {
		gdn = runner.Start(config)
		var err error
		ctr, err = gdn.Create(containerSpec)
		Expect(err).NotTo(HaveOccurred())
	})

	AfterEach(func() {
		Expect(gdn.DestroyAndStop()).To(Succeed())
	})

	It("should not leak pipes", func() {
		initialPipes := numPipes(gdn.Pid)

		process, err := ctr.Run(garden.ProcessSpec{
			Path:  "echo",
			Args:  []string{"hello"},
			Image: garden.ImageRef{URI: "raw://" + peaRootfs},
		}, garden.ProcessIO{})
		Expect(err).NotTo(HaveOccurred())
		Expect(process.Wait()).To(Equal(0))

		Eventually(func() int { return numPipes(gdn.Pid) }).Should(Equal(initialPipes))
	})

	Context("when run with /etc/passwd username", func() {
		It("should not leak username resolving peas", func() {
			ctr.Run(garden.ProcessSpec{
				User:  "alice",
				Path:  "echo",
				Args:  []string{"hello"},
				Image: garden.ImageRef{URI: "raw://" + peaRootfs},
			}, garden.ProcessIO{})

			for _, pid := range collectPeaPids(ctr.Handle()) {
				Eventually("/proc/"+pid, "10s").ShouldNot(BeAnExistingFile())
			}
		})
	})

	Describe("process limits", func() {
		It("should not leak cgroups", func() {
			stdout := gbytes.NewBuffer()
			process, err := ctr.Run(garden.ProcessSpec{
				ID:    "pea-process",
				Path:  "cat",
				Args:  []string{"/proc/self/cgroup"},
				Image: garden.ImageRef{URI: "raw://" + peaRootfs},
				OverrideContainerLimits: &garden.ProcessLimits{},
			}, garden.ProcessIO{
				Stdout: stdout,
			})

			Expect(err).NotTo(HaveOccurred())
			Expect(process.Wait()).To(Equal(0))

			cgroupsPath := filepath.Join(config.TmpDir, fmt.Sprintf("cgroups-%s", config.Tag))

			peaCgroups := stdout.Contents()
			cgroups := strings.Split(string(peaCgroups), "\n")
			for _, cgroup := range cgroups[:len(cgroups)-1] {
				cgroupData := strings.Split(cgroup, ":")
				Eventually(filepath.Join(cgroupsPath, cgroupData[1], cgroupData[2])).ShouldNot(BeADirectory())
			}
		})

		Context("when a process with cpu limits is created", func() {
			var cgroupPath string

			JustBeforeEach(func() {
				stdout := gbytes.NewBuffer()
				_, err := ctr.Run(garden.ProcessSpec{
					Path:  "sh",
					Args:  []string{"-c", "cat /proc/self/cgroup && echo done && sleep 3600"},
					Image: garden.ImageRef{URI: "raw://" + peaRootfs},
					OverrideContainerLimits: &garden.ProcessLimits{
						CPU: garden.CPULimits{LimitInShares: 128},
					},
				}, garden.ProcessIO{
					Stdout: io.MultiWriter(stdout, GinkgoWriter),
					Stderr: GinkgoWriter,
				})
				Expect(err).NotTo(HaveOccurred())
				Eventually(stdout).Should(gbytes.Say("done"))

				firstCgroupProcLine := strings.Split(string(stdout.Contents()), "\n")[0]
				cgroupRelativePath := strings.Split(firstCgroupProcLine, ":")[2]
				cgroupPath = filepath.Join(gdn.CgroupsRootPath(),
					"cpu", cgroupRelativePath)
			})

			Context("when started with low cpu limit turned on", func() {
				BeforeEach(func() {
					config.CPUQuotaPerShare = uint64ptr(10)
				})

				It("throttles process cpu usage", func() {
					periods, throttled, time, err := parseCpuStats(filepath.Join(cgroupPath, "cpu.stat"))
					Expect(err).NotTo(HaveOccurred())
					Expect(periods).To(BeNumerically(">", 0))
					Expect(throttled).To(BeNumerically(">", 0))
					Expect(time).To(BeNumerically(">", 0))
				})

				It("sets cpu.cfs_period_us to 100000 (100ms)", func() {
					period := readFile(filepath.Join(cgroupPath, "cpu.cfs_period_us"))
					Expect(strings.TrimSpace(period)).To(Equal("100000"))
				})

				It("configures cpu.cfs_quota_us as shares * cpu-quota-per-share", func() {
					period := readFile(filepath.Join(cgroupPath, "cpu.cfs_quota_us"))
					Expect(strings.TrimSpace(period)).To(Equal("1280"))
				})
			})

			Context("when started with low cpu limit turned off", func() {
				It("does not throttle process cpu usage", func() {
					periods, throttled, time, err := parseCpuStats(filepath.Join(cgroupPath, "cpu.stat"))
					Expect(err).NotTo(HaveOccurred())
					Expect(periods).To(BeNumerically("==", 0))
					Expect(throttled).To(BeNumerically("==", 0))
					Expect(time).To(BeNumerically("==", 0))
				})

				It("configures cpu.cfs_quota_us as shares * cpu-quota-per-share", func() {
					period := readFile(filepath.Join(cgroupPath, "cpu.cfs_quota_us"))
					Expect(strings.TrimSpace(period)).To(Equal("-1"))
				})
			})
		})
	})

	Describe("Process dir", func() {
		var processPath string

		JustBeforeEach(func() {
			process, err := ctr.Run(garden.ProcessSpec{
				Path:  "echo",
				Args:  []string{"hello"},
				Image: garden.ImageRef{URI: "raw://" + peaRootfs},
			}, garden.ProcessIO{})
			Expect(err).NotTo(HaveOccurred())
			Expect(process.Wait()).To(Equal(0))
			processPath = filepath.Join(gdn.DepotDir, ctr.Handle(), "processes", process.ID())
		})

		Context("when --cleanup-process-dirs-on-wait is set", func() {
			BeforeEach(func() {
				config.CleanupProcessDirsOnWait = boolptr(true)
			})

			It("should delete pea process dir", func() {
				Expect(processPath).NotTo(BeADirectory())
			})
		})

		Context("when --cleanup-process-dirs-on-wait is not set (default)", func() {
			BeforeEach(func() {
				config.CleanupProcessDirsOnWait = boolptr(false)
			})

			It("should not delete pea process dir", func() {
				Expect(processPath).To(BeADirectory())
			})
		})
	})

	Describe("Bind mounts", func() {
		var testSrcFile *os.File
		destinationFile := "/tmp/file"
		output := gbytes.NewBuffer()

		BeforeEach(func() {
			var err error
			testSrcFile, err = ioutil.TempFile("", "host-file")
			Expect(err).NotTo(HaveOccurred())
			_, err = testSrcFile.WriteString("test-mount")
			Expect(err).NotTo(HaveOccurred())
			Expect(exec.Command("chown", "4294967294", testSrcFile.Name()).Run()).To(Succeed())
		})

		Context("when we create a pea with bind mounts", func() {
			It("should have access to the mounts", func() {
				process, err := ctr.Run(garden.ProcessSpec{
					Path:  "cat",
					Args:  []string{destinationFile},
					Image: garden.ImageRef{URI: "raw://" + peaRootfs},
					BindMounts: []garden.BindMount{
						garden.BindMount{
							SrcPath: testSrcFile.Name(),
							DstPath: destinationFile,
						},
					},
				}, garden.ProcessIO{
					Stdout: io.MultiWriter(GinkgoWriter, output),
					Stderr: GinkgoWriter,
				})
				Expect(err).NotTo(HaveOccurred())
				Expect(process.Wait()).To(Equal(0))
				Expect(output).To(gbytes.Say("test-mount"))
			})
		})

		Context("when there are already bind mounts in the container", func() {
			BeforeEach(func() {
				containerSpec = garden.ContainerSpec{
					BindMounts: []garden.BindMount{
						garden.BindMount{
							SrcPath: testSrcFile.Name(),
							DstPath: destinationFile,
						},
					},
				}
			})

			It("the pea should not have access to the mounts", func() {
				process, err := ctr.Run(garden.ProcessSpec{
					Path:  "cat",
					Args:  []string{destinationFile},
					Image: garden.ImageRef{URI: "raw://" + peaRootfs},
				}, garden.ProcessIO{
					Stdout: GinkgoWriter,
					Stderr: io.MultiWriter(GinkgoWriter, output),
				})
				Expect(err).NotTo(HaveOccurred())
				Expect(process.Wait()).To(Equal(1))
				Expect(output).To(gbytes.Say("No such file or directory"))
			})
		})
	})
})

func collectPeaPids(handle string) []string {
	peaPids := []string{}
	processesDir := filepath.Join(config.DepotDir, handle, "processes")

	err := filepath.Walk(processesDir, func(path string, info os.FileInfo, err error) error {
		if info.IsDir() {
			return nil
		}
		if info.Name() == "pidfile" {
			pid, err := ioutil.ReadFile(path)
			Expect(err).NotTo(HaveOccurred())
			peaPids = append(peaPids, string(pid))
		}
		return nil
	})
	Expect(err).NotTo(HaveOccurred())
	return peaPids
}
<commit_msg>Use slightly more robust cgroup parsing in test<commit_after>package gqt_test

import (
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"os/exec"
	"path/filepath"
	"strings"

	"code.cloudfoundry.org/garden"
	"code.cloudfoundry.org/guardian/gqt/runner"
	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
	"github.com/onsi/gomega/gbytes"
)

var _ = Describe("Partially shared containers (peas)", func() {
	var (
		gdn           *runner.RunningGarden
		peaRootfs     string
		ctr           garden.Container
		containerSpec garden.ContainerSpec
	)

	BeforeEach(func() {
		peaRootfs = createPeaRootfs()
		containerSpec = garden.ContainerSpec{}
	})

	JustBeforeEach(func() {
		gdn = runner.Start(config)
		var err error
		ctr, err = gdn.Create(containerSpec)
		Expect(err).NotTo(HaveOccurred())
	})

	AfterEach(func() {
		Expect(gdn.DestroyAndStop()).To(Succeed())
	})

	It("should not leak pipes", func() {
		initialPipes := numPipes(gdn.Pid)

		process, err := ctr.Run(garden.ProcessSpec{
			Path:  "echo",
			Args:  []string{"hello"},
			Image: garden.ImageRef{URI: "raw://" + peaRootfs},
		}, garden.ProcessIO{})
		Expect(err).NotTo(HaveOccurred())
		Expect(process.Wait()).To(Equal(0))

		Eventually(func() int { return numPipes(gdn.Pid) }).Should(Equal(initialPipes))
	})

	Context("when run with /etc/passwd username", func() {
		It("should not leak username resolving peas", func() {
			ctr.Run(garden.ProcessSpec{
				User:  "alice",
				Path:  "echo",
				Args:  []string{"hello"},
				Image: garden.ImageRef{URI: "raw://" + peaRootfs},
			}, garden.ProcessIO{})

			for _, pid := range collectPeaPids(ctr.Handle()) {
				Eventually("/proc/"+pid, "10s").ShouldNot(BeAnExistingFile())
			}
		})
	})

	Describe("process limits", func() {
		It("should not leak cgroups", func() {
			stdout := gbytes.NewBuffer()
			process, err := ctr.Run(garden.ProcessSpec{
				ID:    "pea-process",
				Path:  "cat",
				Args:  []string{"/proc/self/cgroup"},
				Image: garden.ImageRef{URI: "raw://" + peaRootfs},
				OverrideContainerLimits: &garden.ProcessLimits{},
			}, garden.ProcessIO{
				Stdout: stdout,
			})

			Expect(err).NotTo(HaveOccurred())
			Expect(process.Wait()).To(Equal(0))

			cgroupsPath := filepath.Join(config.TmpDir, fmt.Sprintf("cgroups-%s", config.Tag))

			peaCgroups := stdout.Contents()
			cgroups := strings.Split(string(peaCgroups), "\n")
			for _, cgroup := range cgroups[:len(cgroups)-1] {
				cgroupData := strings.Split(cgroup, ":")
				Eventually(filepath.Join(cgroupsPath, cgroupData[1], cgroupData[2])).ShouldNot(BeADirectory())
			}
		})

		Context("when a process with cpu limits is created", func() {
			var cgroupPath string

			JustBeforeEach(func() {
				stdout := gbytes.NewBuffer()
				_, err := ctr.Run(garden.ProcessSpec{
					Path:  "sh",
					Args:  []string{"-c", "cat /proc/self/cgroup && echo done && sleep 3600"},
					Image: garden.ImageRef{URI: "raw://" + peaRootfs},
					OverrideContainerLimits: &garden.ProcessLimits{
						CPU: garden.CPULimits{LimitInShares: 128},
					},
				}, garden.ProcessIO{
					Stdout: io.MultiWriter(stdout, GinkgoWriter),
					Stderr: GinkgoWriter,
				})
				Expect(err).NotTo(HaveOccurred())
				Eventually(stdout).Should(gbytes.Say("done"))

				cgroupProcLines := strings.Split(string(stdout.Contents()), "\n")
				var cgroupRelativePath string
				for _, procLine := range cgroupProcLines {
					procLineSections := strings.Split(procLine, ":")
					if procLineSections[1] == "memory" {
						cgroupRelativePath = procLineSections[2]
						break
					}
				}
				cgroupPath = filepath.Join(gdn.CgroupsRootPath(),
					"cpu", cgroupRelativePath)
			})

			Context("when started with low cpu limit turned on", func() {
				BeforeEach(func() {
					config.CPUQuotaPerShare = uint64ptr(10)
				})

				It("throttles process cpu usage", func() {
					periods, throttled, time, err := parseCpuStats(filepath.Join(cgroupPath, "cpu.stat"))
					Expect(err).NotTo(HaveOccurred())
					Expect(periods).To(BeNumerically(">", 0))
					Expect(throttled).To(BeNumerically(">", 0))
					Expect(time).To(BeNumerically(">", 0))
				})

				It("sets cpu.cfs_period_us to 100000 (100ms)", func() {
					period := readFile(filepath.Join(cgroupPath, "cpu.cfs_period_us"))
					Expect(strings.TrimSpace(period)).To(Equal("100000"))
				})

				It("configures cpu.cfs_quota_us as shares * cpu-quota-per-share", func() {
					period := readFile(filepath.Join(cgroupPath, "cpu.cfs_quota_us"))
					Expect(strings.TrimSpace(period)).To(Equal("1280"))
				})
			})

			Context("when started with low cpu limit turned off", func() {
				It("does not throttle process cpu usage", func() {
					periods, throttled, time, err := parseCpuStats(filepath.Join(cgroupPath, "cpu.stat"))
					Expect(err).NotTo(HaveOccurred())
					Expect(periods).To(BeNumerically("==", 0))
					Expect(throttled).To(BeNumerically("==", 0))
					Expect(time).To(BeNumerically("==", 0))
				})

				It("configures cpu.cfs_quota_us as shares * cpu-quota-per-share", func() {
					period := readFile(filepath.Join(cgroupPath, "cpu.cfs_quota_us"))
					Expect(strings.TrimSpace(period)).To(Equal("-1"))
				})
			})
		})
	})

	Describe("Process dir", func() {
		var processPath string

		JustBeforeEach(func() {
			process, err := ctr.Run(garden.ProcessSpec{
				Path:  "echo",
				Args:  []string{"hello"},
				Image: garden.ImageRef{URI: "raw://" + peaRootfs},
			}, garden.ProcessIO{})
			Expect(err).NotTo(HaveOccurred())
			Expect(process.Wait()).To(Equal(0))
			processPath = filepath.Join(gdn.DepotDir, ctr.Handle(), "processes", process.ID())
		})

		Context("when --cleanup-process-dirs-on-wait is set", func() {
			BeforeEach(func() {
				config.CleanupProcessDirsOnWait = boolptr(true)
			})

			It("should delete pea process dir", func() {
				Expect(processPath).NotTo(BeADirectory())
			})
		})

		Context("when --cleanup-process-dirs-on-wait is not set (default)", func() {
			BeforeEach(func() {
				config.CleanupProcessDirsOnWait = boolptr(false)
			})

			It("should not delete pea process dir", func() {
				Expect(processPath).To(BeADirectory())
			})
		})
	})

	Describe("Bind mounts", func() {
		var testSrcFile *os.File
		destinationFile := "/tmp/file"
		output := gbytes.NewBuffer()

		BeforeEach(func() {
			var err error
			testSrcFile, err = ioutil.TempFile("", "host-file")
			Expect(err).NotTo(HaveOccurred())
			_, err = testSrcFile.WriteString("test-mount")
			Expect(err).NotTo(HaveOccurred())
			Expect(exec.Command("chown", "4294967294", testSrcFile.Name()).Run()).To(Succeed())
		})

		Context("when we create a pea with bind mounts", func() {
			It("should have access to the mounts", func() {
				process, err := ctr.Run(garden.ProcessSpec{
					Path:  "cat",
					Args:  []string{destinationFile},
					Image: garden.ImageRef{URI: "raw://" + peaRootfs},
					BindMounts: []garden.BindMount{
						garden.BindMount{
							SrcPath: testSrcFile.Name(),
							DstPath: destinationFile,
						},
					},
				}, garden.ProcessIO{
					Stdout: io.MultiWriter(GinkgoWriter, output),
					Stderr: GinkgoWriter,
				})
				Expect(err).NotTo(HaveOccurred())
				Expect(process.Wait()).To(Equal(0))
				Expect(output).To(gbytes.Say("test-mount"))
			})
		})

		Context("when there are already bind mounts in the container", func() {
			BeforeEach(func() {
				containerSpec = garden.ContainerSpec{
					BindMounts: []garden.BindMount{
						garden.BindMount{
							SrcPath: testSrcFile.Name(),
							DstPath: destinationFile,
						},
					},
				}
			})

			It("the pea should not have access to the mounts", func() {
				process, err := ctr.Run(garden.ProcessSpec{
					Path:  "cat",
					Args:  []string{destinationFile},
					Image: garden.ImageRef{URI: "raw://" + peaRootfs},
				}, garden.ProcessIO{
					Stdout: GinkgoWriter,
					Stderr: io.MultiWriter(GinkgoWriter, output),
				})
				Expect(err).NotTo(HaveOccurred())
				Expect(process.Wait()).To(Equal(1))
				Expect(output).To(gbytes.Say("No such file or directory"))
			})
		})
	})
})

func collectPeaPids(handle string) []string {
	peaPids := []string{}
	processesDir := filepath.Join(config.DepotDir, handle, "processes")

	err := filepath.Walk(processesDir, func(path string, info os.FileInfo, err error) error {
		if info.IsDir() {
			return nil
		}
		if info.Name() == "pidfile" {
			pid, err := ioutil.ReadFile(path)
			Expect(err).NotTo(HaveOccurred())
			peaPids = append(peaPids, string(pid))
		}
		return nil
	})
	Expect(err).NotTo(HaveOccurred())
	return peaPids
}
<|endoftext|>