<commit_before>package martini

import (
	"fmt"
	"net/http"
	"reflect"
	"regexp"
	"strconv"
	"sync"
)

// Params is a map of name/value pairs for named routes. An instance of martini.Params is available to be injected into any route handler.
type Params map[string]string

// Router is Martini's de-facto routing interface. Supports HTTP verbs, stacked handlers, and dependency injection.
type Router interface {
	Routes

	// Group adds a group where related routes can be added.
	Group(string, func(Router), ...Handler)
	// Get adds a route for a HTTP GET request to the specified matching pattern.
	Get(string, ...Handler) Route
	// Patch adds a route for a HTTP PATCH request to the specified matching pattern.
	Patch(string, ...Handler) Route
	// Post adds a route for a HTTP POST request to the specified matching pattern.
	Post(string, ...Handler) Route
	// Put adds a route for a HTTP PUT request to the specified matching pattern.
	Put(string, ...Handler) Route
	// Delete adds a route for a HTTP DELETE request to the specified matching pattern.
	Delete(string, ...Handler) Route
	// Options adds a route for a HTTP OPTIONS request to the specified matching pattern.
	Options(string, ...Handler) Route
	// Head adds a route for a HTTP HEAD request to the specified matching pattern.
	Head(string, ...Handler) Route
	// Any adds a route for any HTTP method request to the specified matching pattern.
	Any(string, ...Handler) Route
	// AddRoute adds a route for a given HTTP method request to the specified matching pattern.
	AddRoute(string, string, ...Handler) Route

	// NotFound sets the handlers that are called when a no route matches a request. Throws a basic 404 by default.
	NotFound(...Handler)

	// Handle is the entry point for routing. This is used as a martini.Handler
	Handle(http.ResponseWriter, *http.Request, Context)

	// GetMatchedRoute will return a Martini route if a can be found
	// given a METHOD and path. If none can be found, nil is returned
	GetMatchedRoute(string, string) Route
}

type router struct {
	routes     []*route
	notFounds  []Handler
	groups     []group
	routesLock sync.RWMutex
}

type group struct {
	pattern  string
	handlers []Handler
}

// NewRouter creates a new Router instance.
// If you aren't using ClassicMartini, then you can add Routes as a
// service with:
//
//	m := martini.New()
//	r := martini.NewRouter()
//	m.MapTo(r, (*martini.Routes)(nil))
//
// If you are using ClassicMartini, then this is done for you.
func NewRouter() Router {
	return &router{notFounds: []Handler{http.NotFound}, groups: make([]group, 0)}
}

func (r *router) Group(pattern string, fn func(Router), h ...Handler) {
	r.groups = append(r.groups, group{pattern, h})
	fn(r)
	r.groups = r.groups[:len(r.groups)-1]
}

func (r *router) Get(pattern string, h ...Handler) Route {
	return r.addRoute("GET", pattern, h)
}

func (r *router) Patch(pattern string, h ...Handler) Route {
	return r.addRoute("PATCH", pattern, h)
}

func (r *router) Post(pattern string, h ...Handler) Route {
	return r.addRoute("POST", pattern, h)
}

func (r *router) Put(pattern string, h ...Handler) Route {
	return r.addRoute("PUT", pattern, h)
}

func (r *router) Delete(pattern string, h ...Handler) Route {
	return r.addRoute("DELETE", pattern, h)
}

func (r *router) Options(pattern string, h ...Handler) Route {
	return r.addRoute("OPTIONS", pattern, h)
}

func (r *router) Head(pattern string, h ...Handler) Route {
	return r.addRoute("HEAD", pattern, h)
}

func (r *router) Any(pattern string, h ...Handler) Route {
	return r.addRoute("*", pattern, h)
}

func (r *router) AddRoute(method, pattern string, h ...Handler) Route {
	return r.addRoute(method, pattern, h)
}

func (r *router) Handle(res http.ResponseWriter, req *http.Request, context Context) {
	for _, route := range r.getRoutes() {
		ok, vals := route.Match(req.Method, req.URL.Path)
		if ok {
			params := Params(vals)
			context.Map(params)
			route.Handle(context, res)
			return
		}
	}

	// no routes exist, 404
	c := &routeContext{context, 0, r.notFounds}
	context.MapTo(c, (*Context)(nil))
	c.run()
}

// GetMatchedRoute will return a Martini route if a can be found
// given a METHOD and path. If none can be found, nil is returned
func (r *router) GetMatchedRoute(method string, path string) Route {
	for _, route := range r.routes {
		ok, _ := route.Match(method, path)
		if ok {
			return route
		}
	}

	return nil
}

func (r *router) NotFound(handler ...Handler) {
	r.notFounds = handler
}

func (r *router) addRoute(method string, pattern string, handlers []Handler) *route {
	if len(r.groups) > 0 {
		groupPattern := ""
		h := make([]Handler, 0)
		for _, g := range r.groups {
			groupPattern += g.pattern
			h = append(h, g.handlers...)
		}

		pattern = groupPattern + pattern
		h = append(h, handlers...)
		handlers = h
	}

	route := newRoute(method, pattern, handlers)
	route.Validate()
	r.appendRoute(route)
	return route
}

func (r *router) appendRoute(rt *route) {
	r.routesLock.Lock()
	defer r.routesLock.Unlock()
	r.routes = append(r.routes, rt)
}

func (r *router) getRoutes() []*route {
	r.routesLock.RLock()
	defer r.routesLock.RUnlock()
	return r.routes[:]
}

func (r *router) findRoute(name string) *route {
	for _, route := range r.getRoutes() {
		if route.name == name {
			return route
		}
	}

	return nil
}

// Route is an interface representing a Route in Martini's routing layer.
type Route interface {
	// URLWith returns a rendering of the Route's url with the given string params.
	URLWith([]string) string
	// Name sets a name for the route.
	Name(string)
	// GetName returns the name of the route.
	GetName() string
	// Pattern returns the pattern of the route.
	Pattern() string
	// Method returns the method of the route.
	Method() string
}

type route struct {
	method   string
	regex    *regexp.Regexp
	handlers []Handler
	pattern  string
	name     string
}

func newRoute(method string, pattern string, handlers []Handler) *route {
	route := route{method, nil, handlers, pattern, ""}
	r := regexp.MustCompile(`:[^/#?()\.\\]+`)
	pattern = r.ReplaceAllStringFunc(pattern, func(m string) string {
		return fmt.Sprintf(`(?P<%s>[^/#?]+)`, m[1:])
	})
	r2 := regexp.MustCompile(`\*\*`)
	var index int
	pattern = r2.ReplaceAllStringFunc(pattern, func(m string) string {
		index++
		return fmt.Sprintf(`(?P<_%d>[^#?]*)`, index)
	})
	pattern += `\/?`
	route.regex = regexp.MustCompile(pattern)
	return &route
}

func (r route) MatchMethod(method string) bool {
	return r.method == "*" || method == r.method || (method == "HEAD" && r.method == "GET")
}

func (r route) Match(method string, path string) (bool, map[string]string) {
	// add Any method matching support
	if !r.MatchMethod(method) {
		return false, nil
	}

	matches := r.regex.FindStringSubmatch(path)
	if len(matches) > 0 && matches[0] == path {
		params := make(map[string]string)
		for i, name := range r.regex.SubexpNames() {
			if len(name) > 0 {
				params[name] = matches[i]
			}
		}
		return true, params
	}
	return false, nil
}

func (r *route) Validate() {
	for _, handler := range r.handlers {
		validateHandler(handler)
	}
}

func (r *route) Handle(c Context, res http.ResponseWriter) {
	context := &routeContext{c, 0, r.handlers}
	c.MapTo(context, (*Context)(nil))
	c.MapTo(r, (*Route)(nil))
	context.run()
}

// URLWith returns the url pattern replacing the parameters for its values
func (r *route) URLWith(args []string) string {
	if len(args) > 0 {
		reg := regexp.MustCompile(`:[^/#?()\.\\]+|\(\?P<[a-zA-Z0-9]+>.*\)`)
		argCount := len(args)
		i := 0
		url := reg.ReplaceAllStringFunc(r.pattern, func(m string) string {
			var val interface{}
			if i < argCount {
				val = args[i]
			} else {
				val = m
			}
			i += 1
			return fmt.Sprintf(`%v`, val)
		})

		return url
	}
	return r.pattern
}

func (r *route) Name(name string) {
	r.name = name
}

func (r *route) GetName() string {
	return r.name
}

func (r *route) Pattern() string {
	return r.pattern
}

func (r *route) Method() string {
	return r.method
}

// Routes is a helper service for Martini's routing layer.
type Routes interface {
	// URLFor returns a rendered URL for the given route. Optional params can be passed to fulfill named parameters in the route.
	URLFor(name string, params ...interface{}) string
	// MethodsFor returns an array of methods available for the path
	MethodsFor(path string) []string
	// All returns an array with all the routes in the router.
	All() []Route
}

// URLFor returns the url for the given route name.
func (r *router) URLFor(name string, params ...interface{}) string {
	route := r.findRoute(name)

	if route == nil {
		panic("route not found")
	}

	var args []string
	for _, param := range params {
		switch v := param.(type) {
		case int:
			args = append(args, strconv.FormatInt(int64(v), 10))
		case string:
			args = append(args, v)
		default:
			if v != nil {
				panic("Arguments passed to URLFor must be integers or strings")
			}
		}
	}

	return route.URLWith(args)
}

func (r *router) All() []Route {
	routes := r.getRoutes()
	var ri = make([]Route, len(routes))

	for i, route := range routes {
		ri[i] = Route(route)
	}

	return ri
}

func hasMethod(methods []string, method string) bool {
	for _, v := range methods {
		if v == method {
			return true
		}
	}
	return false
}

// MethodsFor returns all methods available for path
func (r *router) MethodsFor(path string) []string {
	methods := []string{}
	for _, route := range r.getRoutes() {
		matches := route.regex.FindStringSubmatch(path)
		if len(matches) > 0 && matches[0] == path && !hasMethod(methods, route.method) {
			methods = append(methods, route.method)
		}
	}
	return methods
}

type routeContext struct {
	Context
	index    int
	handlers []Handler
}

func (r *routeContext) Next() {
	r.index += 1
	r.run()
}

func (r *routeContext) run() {
	for r.index < len(r.handlers) {
		handler := r.handlers[r.index]
		vals, err := r.Invoke(handler)
		if err != nil {
			panic(err)
		}
		r.index += 1

		// if the handler returned something, write it to the http response
		if len(vals) > 0 {
			ev := r.Get(reflect.TypeOf(ReturnHandler(nil)))
			handleReturn := ev.Interface().(ReturnHandler)
			handleReturn(r, vals)
		}

		if r.Written() {
			return
		}
	}
}
<commit_msg>fixing comment typos<commit_after>package martini

import (
	"fmt"
	"net/http"
	"reflect"
	"regexp"
	"strconv"
	"sync"
)

// Params is a map of name/value pairs for named routes. An instance of martini.Params is available to be injected into any route handler.
type Params map[string]string

// Router is Martini's de-facto routing interface. Supports HTTP verbs, stacked handlers, and dependency injection.
type Router interface {
	Routes

	// Group adds a group where related routes can be added.
	Group(string, func(Router), ...Handler)
	// Get adds a route for a HTTP GET request to the specified matching pattern.
	Get(string, ...Handler) Route
	// Patch adds a route for a HTTP PATCH request to the specified matching pattern.
	Patch(string, ...Handler) Route
	// Post adds a route for a HTTP POST request to the specified matching pattern.
	Post(string, ...Handler) Route
	// Put adds a route for a HTTP PUT request to the specified matching pattern.
	Put(string, ...Handler) Route
	// Delete adds a route for a HTTP DELETE request to the specified matching pattern.
	Delete(string, ...Handler) Route
	// Options adds a route for a HTTP OPTIONS request to the specified matching pattern.
	Options(string, ...Handler) Route
	// Head adds a route for a HTTP HEAD request to the specified matching pattern.
	Head(string, ...Handler) Route
	// Any adds a route for any HTTP method request to the specified matching pattern.
	Any(string, ...Handler) Route
	// AddRoute adds a route for a given HTTP method request to the specified matching pattern.
	AddRoute(string, string, ...Handler) Route

	// NotFound sets the handlers that are called when a no route matches a request. Throws a basic 404 by default.
	NotFound(...Handler)

	// Handle is the entry point for routing. This is used as a martini.Handler
	Handle(http.ResponseWriter, *http.Request, Context)

	// GetMatchedRoute will return a Martini route if one is found
	// given a method and path. If no route is found, nil is returned
	GetMatchedRoute(string, string) Route
}

type router struct {
	routes     []*route
	notFounds  []Handler
	groups     []group
	routesLock sync.RWMutex
}

type group struct {
	pattern  string
	handlers []Handler
}

// NewRouter creates a new Router instance.
// If you aren't using ClassicMartini, then you can add Routes as a
// service with:
//
//	m := martini.New()
//	r := martini.NewRouter()
//	m.MapTo(r, (*martini.Routes)(nil))
//
// If you are using ClassicMartini, then this is done for you.
func NewRouter() Router {
	return &router{notFounds: []Handler{http.NotFound}, groups: make([]group, 0)}
}

func (r *router) Group(pattern string, fn func(Router), h ...Handler) {
	r.groups = append(r.groups, group{pattern, h})
	fn(r)
	r.groups = r.groups[:len(r.groups)-1]
}

func (r *router) Get(pattern string, h ...Handler) Route {
	return r.addRoute("GET", pattern, h)
}

func (r *router) Patch(pattern string, h ...Handler) Route {
	return r.addRoute("PATCH", pattern, h)
}

func (r *router) Post(pattern string, h ...Handler) Route {
	return r.addRoute("POST", pattern, h)
}

func (r *router) Put(pattern string, h ...Handler) Route {
	return r.addRoute("PUT", pattern, h)
}

func (r *router) Delete(pattern string, h ...Handler) Route {
	return r.addRoute("DELETE", pattern, h)
}

func (r *router) Options(pattern string, h ...Handler) Route {
	return r.addRoute("OPTIONS", pattern, h)
}

func (r *router) Head(pattern string, h ...Handler) Route {
	return r.addRoute("HEAD", pattern, h)
}

func (r *router) Any(pattern string, h ...Handler) Route {
	return r.addRoute("*", pattern, h)
}

func (r *router) AddRoute(method, pattern string, h ...Handler) Route {
	return r.addRoute(method, pattern, h)
}

func (r *router) Handle(res http.ResponseWriter, req *http.Request, context Context) {
	for _, route := range r.getRoutes() {
		ok, vals := route.Match(req.Method, req.URL.Path)
		if ok {
			params := Params(vals)
			context.Map(params)
			route.Handle(context, res)
			return
		}
	}

	// no routes exist, 404
	c := &routeContext{context, 0, r.notFounds}
	context.MapTo(c, (*Context)(nil))
	c.run()
}

// GetMatchedRoute will return a Martini route if one is found
// given a method and path. If no route is found, nil is returned
func (r *router) GetMatchedRoute(method string, path string) Route {
	for _, route := range r.routes {
		ok, _ := route.Match(method, path)
		if ok {
			return route
		}
	}

	return nil
}

func (r *router) NotFound(handler ...Handler) {
	r.notFounds = handler
}

func (r *router) addRoute(method string, pattern string, handlers []Handler) *route {
	if len(r.groups) > 0 {
		groupPattern := ""
		h := make([]Handler, 0)
		for _, g := range r.groups {
			groupPattern += g.pattern
			h = append(h, g.handlers...)
		}

		pattern = groupPattern + pattern
		h = append(h, handlers...)
		handlers = h
	}

	route := newRoute(method, pattern, handlers)
	route.Validate()
	r.appendRoute(route)
	return route
}

func (r *router) appendRoute(rt *route) {
	r.routesLock.Lock()
	defer r.routesLock.Unlock()
	r.routes = append(r.routes, rt)
}

func (r *router) getRoutes() []*route {
	r.routesLock.RLock()
	defer r.routesLock.RUnlock()
	return r.routes[:]
}

func (r *router) findRoute(name string) *route {
	for _, route := range r.getRoutes() {
		if route.name == name {
			return route
		}
	}

	return nil
}

// Route is an interface representing a Route in Martini's routing layer.
type Route interface {
	// URLWith returns a rendering of the Route's url with the given string params.
	URLWith([]string) string
	// Name sets a name for the route.
	Name(string)
	// GetName returns the name of the route.
	GetName() string
	// Pattern returns the pattern of the route.
	Pattern() string
	// Method returns the method of the route.
	Method() string
}

type route struct {
	method   string
	regex    *regexp.Regexp
	handlers []Handler
	pattern  string
	name     string
}

func newRoute(method string, pattern string, handlers []Handler) *route {
	route := route{method, nil, handlers, pattern, ""}
	r := regexp.MustCompile(`:[^/#?()\.\\]+`)
	pattern = r.ReplaceAllStringFunc(pattern, func(m string) string {
		return fmt.Sprintf(`(?P<%s>[^/#?]+)`, m[1:])
	})
	r2 := regexp.MustCompile(`\*\*`)
	var index int
	pattern = r2.ReplaceAllStringFunc(pattern, func(m string) string {
		index++
		return fmt.Sprintf(`(?P<_%d>[^#?]*)`, index)
	})
	pattern += `\/?`
	route.regex = regexp.MustCompile(pattern)
	return &route
}

func (r route) MatchMethod(method string) bool {
	return r.method == "*" || method == r.method || (method == "HEAD" && r.method == "GET")
}

func (r route) Match(method string, path string) (bool, map[string]string) {
	// add Any method matching support
	if !r.MatchMethod(method) {
		return false, nil
	}

	matches := r.regex.FindStringSubmatch(path)
	if len(matches) > 0 && matches[0] == path {
		params := make(map[string]string)
		for i, name := range r.regex.SubexpNames() {
			if len(name) > 0 {
				params[name] = matches[i]
			}
		}
		return true, params
	}
	return false, nil
}

func (r *route) Validate() {
	for _, handler := range r.handlers {
		validateHandler(handler)
	}
}

func (r *route) Handle(c Context, res http.ResponseWriter) {
	context := &routeContext{c, 0, r.handlers}
	c.MapTo(context, (*Context)(nil))
	c.MapTo(r, (*Route)(nil))
	context.run()
}

// URLWith returns the url pattern replacing the parameters for its values
func (r *route) URLWith(args []string) string {
	if len(args) > 0 {
		reg := regexp.MustCompile(`:[^/#?()\.\\]+|\(\?P<[a-zA-Z0-9]+>.*\)`)
		argCount := len(args)
		i := 0
		url := reg.ReplaceAllStringFunc(r.pattern, func(m string) string {
			var val interface{}
			if i < argCount {
				val = args[i]
			} else {
				val = m
			}
			i += 1
			return fmt.Sprintf(`%v`, val)
		})

		return url
	}
	return r.pattern
}

func (r *route) Name(name string) {
	r.name = name
}

func (r *route) GetName() string {
	return r.name
}

func (r *route) Pattern() string {
	return r.pattern
}

func (r *route) Method() string {
	return r.method
}

// Routes is a helper service for Martini's routing layer.
type Routes interface {
	// URLFor returns a rendered URL for the given route. Optional params can be passed to fulfill named parameters in the route.
	URLFor(name string, params ...interface{}) string
	// MethodsFor returns an array of methods available for the path
	MethodsFor(path string) []string
	// All returns an array with all the routes in the router.
	All() []Route
}

// URLFor returns the url for the given route name.
func (r *router) URLFor(name string, params ...interface{}) string {
	route := r.findRoute(name)

	if route == nil {
		panic("route not found")
	}

	var args []string
	for _, param := range params {
		switch v := param.(type) {
		case int:
			args = append(args, strconv.FormatInt(int64(v), 10))
		case string:
			args = append(args, v)
		default:
			if v != nil {
				panic("Arguments passed to URLFor must be integers or strings")
			}
		}
	}

	return route.URLWith(args)
}

func (r *router) All() []Route {
	routes := r.getRoutes()
	var ri = make([]Route, len(routes))

	for i, route := range routes {
		ri[i] = Route(route)
	}

	return ri
}

func hasMethod(methods []string, method string) bool {
	for _, v := range methods {
		if v == method {
			return true
		}
	}
	return false
}

// MethodsFor returns all methods available for path
func (r *router) MethodsFor(path string) []string {
	methods := []string{}
	for _, route := range r.getRoutes() {
		matches := route.regex.FindStringSubmatch(path)
		if len(matches) > 0 && matches[0] == path && !hasMethod(methods, route.method) {
			methods = append(methods, route.method)
		}
	}
	return methods
}

type routeContext struct {
	Context
	index    int
	handlers []Handler
}

func (r *routeContext) Next() {
	r.index += 1
	r.run()
}

func (r *routeContext) run() {
	for r.index < len(r.handlers) {
		handler := r.handlers[r.index]
		vals, err := r.Invoke(handler)
		if err != nil {
			panic(err)
		}
		r.index += 1

		// if the handler returned something, write it to the http response
		if len(vals) > 0 {
			ev := r.Get(reflect.TypeOf(ReturnHandler(nil)))
			handleReturn := ev.Interface().(ReturnHandler)
			handleReturn(r, vals)
		}

		if r.Written() {
			return
		}
	}
}
<|endoftext|>


<commit_before>/*
 * Copyright © 2017 Slizov Vadim <z7zmey@gmail.com>
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package main

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

	"github.com/gorilla/mux"
)

// NewRouter TODO
func NewRouter() *mux.Router {
	codeGraphDir, err := filepath.Abs(filepath.Dir(os.Args[0]))
	if err != nil {
		log.Fatal(err)
	}

	if _, err := os.Stat(codeGraphDir+"/ui"); os.IsNotExist(err) {
		codeGraphDir = "/usr/local/lib/codegraph"
	}

	router := mux.NewRouter() //.StrictSlash(true)

	router.PathPrefix("/app").Handler(http.StripPrefix("/app", http.FileServer(http.Dir(codeGraphDir+"/ui/dist/app"))))

	for _, route := range routes {
		var handler http.Handler

		handler = route.HandlerFunc
		handler = Logger(handler, route.Name)

		r := router.
			Methods(route.Method).
			Path(route.Pattern).
			Name(route.Name).
			Handler(handler)

		for key, val := range route.queries {
			r.Queries(key, val)
		}
	}

	return router
}
<commit_msg>fix: file not found by angular routes<commit_after>/*
 * Copyright © 2017 Slizov Vadim <z7zmey@gmail.com>
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package main

import (
	"log"
	"net/http"
	"os"
	"path/filepath"
	"strings"

	"github.com/yookoala/realpath"

	"github.com/gorilla/mux"
)

// NewRouter TODO
func NewRouter() *mux.Router {
	codeGraphDir, err := filepath.Abs(filepath.Dir(os.Args[0]))
	if err != nil {
		log.Fatal(err)
	}

	if _, err := os.Stat(codeGraphDir + "/ui"); os.IsNotExist(err) {
		codeGraphDir = "/usr/local/lib/codegraph"
	}

	router := mux.NewRouter() //.StrictSlash(true)

	router.PathPrefix("/app{_:.*}").Handler(wrapByLogger(StaticFileServer, "app"))

	for _, route := range routes {
		r := router.
			Methods(route.Method).
			Path(route.Pattern).
			Name(route.Name).
			Handler(wrapByLogger(route.HandlerFunc, route.Name))

		for key, val := range route.queries {
			r.Queries(key, val)
		}
	}

	return router
}

func wrapByLogger(f func(w http.ResponseWriter, r *http.Request), name string) http.Handler {
	var appHandler http.Handler
	appHandler = http.HandlerFunc(f)
	return Logger(appHandler, "app")
}

func StaticFileServer(w http.ResponseWriter, r *http.Request) {
	codeGraphDir, err := filepath.Abs(filepath.Dir(os.Args[0]))
	if err != nil {
		log.Fatal(err)
	}

	if _, err := os.Stat(codeGraphDir + "/ui"); os.IsNotExist(err) {
		codeGraphDir = "/usr/local/lib/codegraph"
	}

	path, err := realpath.Realpath(codeGraphDir + "/ui/dist/" + r.URL.Path)

	if err != nil || !strings.HasPrefix(path, codeGraphDir+"/ui/dist/app") {
		http.ServeFile(w, r, codeGraphDir+"/ui/dist/app/index.html")
		return
	}

	if _, err := os.Stat(path); os.IsNotExist(err) { // if target file not exist
		http.ServeFile(w, r, codeGraphDir+"/ui/dist/app/index.html")
	}

	http.ServeFile(w, r, path)
}
<|endoftext|>


<commit_before>package cookoo

import (
	"fmt"
	"strings"
)

// RequestResolver is the interface for the request resolver.
// A request resolver is responsible for transforming a request name to
// a route name. For example, a web-specific resolver may take a URI
// and return a route name. Or it make take an HTTP verb and return a
// route name.
type RequestResolver interface {
	Init(registry *Registry)
	Resolve(path string, cxt Context) (string, error)
}

// Router is the Cookoo router.
// A Cookoo app works by passing a request into a router, and
// relying on the router to execute the appropriate chain of
// commands.
type Router struct {
	registry *Registry
	resolver RequestResolver
}

// BasicRequestResolver is a basic resolver that assumes that the given request
// name *is* the route name.
type BasicRequestResolver struct {
	registry *Registry
	resolver RequestResolver
}

// NewRouter creates a new Router.
func NewRouter(reg *Registry) *Router {
	router := new(Router)
	router.Init(reg)
	return router
}

// Init initializes the BasicRequestResolver.
func (r *BasicRequestResolver) Init(registry *Registry) {
	r.registry = registry
}

// Resolve returns the given path.
// This is a non-transforming resolver.
func (r *BasicRequestResolver) Resolve(path string, cxt Context) (string, error) {
	return path, nil
}

// Init initializes the Router.
func (r *Router) Init(registry *Registry) *Router {
	r.registry = registry
	r.resolver = new(BasicRequestResolver)
	r.resolver.Init(registry)
	return r
}

// SetRegistry sets the registry.
func (r *Router) SetRegistry(reg *Registry) {
	r.registry = reg
}

// SetRequestResolver sets the request resolver.
// The resolver is responsible for taking an arbitrary string and
// resolving it to a registry route.
//
// Example: Take a URI and translate it to a route.
func (r *Router) SetRequestResolver(resolver RequestResolver) {
	r.resolver = resolver
}

// RequestResolver gets the request resolver.
func (r *Router) RequestResolver() RequestResolver {
	return r.resolver
}

// ResolveRequest resolver a given string into a route name.
func (r *Router) ResolveRequest(name string, cxt Context) (string, error) {
	routeName, e := r.resolver.Resolve(name, cxt)

	if e != nil {
		return routeName, e
	}

	return routeName, nil
}

// HandleRequest does a request.
// This executes a request "named" name (this string is passed through the
// request resolver.) The context is cloned (shallow copy) and passed in as the
// base context.
//
// If taint is `true`, then no routes that begin with `@` can be executed. Taint
// should be set to true on anything that relies on a name supplied by an
// external client.
//
// This will do the following:
// - resolve the request name into a route name (using a RequestResolver)
// - look up the route
// - execute each command on the route in order
//
// The following context variables are placed into the context during a run:
//
// 	route.Name - Processed name of the current route
// 	route.Description - Description of the current route
// 	route.RequestName - raw route name as passed by the client
// 	command.Name - current command name (changed with each command)
//
// If an error occurred during processing, an error type is returned.
func (r *Router) HandleRequest(name string, cxt Context, taint bool) error {

	// Not sure why we were passing a copy of the context?
	// baseCxt := cxt.Copy()
	// routeName, e := r.ResolveRequest(name, baseCxt)
	routeName, e := r.ResolveRequest(name, cxt)

	if e != nil {
		return e
	}

	cxt.Put("route.RequestName", name)
	cxt.Put("route.Name", routeName)
	if spec, ok := r.registry.RouteSpec(routeName); ok {
		cxt.Put("route.Description", spec.description)
	}

	// Let an outer routine call go HandleRequest()
	//go r.runRoute(routeName, cxt, taint)
	e = r.runRoute(routeName, cxt, taint)

	return e
}

// HasRoute checks whether or not the route exists.
// Note that this does NOT resolve a request name into a route name. This
// expects a route name.
func (r *Router) HasRoute(name string) bool {
	_, ok := r.registry.RouteSpec(name)
	return ok
}

// PRIVATE ==========================================================

// Given a router, context, and taint, run the route.
func (r *Router) runRoute(route string, cxt Context, taint bool) error {
	if len(route) == 0 {
		return &RouteError{"Empty route name."}
	}
	if taint && route[0] == '@' {
		return &RouteError{"Route is tainted. Refusing to run."}
	}
	spec, ok := r.registry.RouteSpec(route)
	if !ok {
		return &RouteError{fmt.Sprintf("Route %s does not exist.", route)}
	}
	// fmt.Printf("Running route %s: %s\n", spec.name, spec.description)
	for _, cmd := range spec.commands {
		// Provide info for each run.
		cxt.Put("command.Name", cmd.name)

		// fmt.Printf("Command %d is %s (%T)\n", i, cmd.name, cmd.command)
		res, irq := r.doCommand(cmd, cxt)

		// This may store a nil.
		cxt.Put(cmd.name, res)

		// Handle interrupts.
		if irq != nil {
			// If this is a reroute, call runRoute() again.
			reroute, isType := irq.(*Reroute)
			if isType {
				routeName, e := r.ResolveRequest(reroute.RouteTo(), cxt)
				if e != nil {
					return e
				}
				//fmt.Printf("Routing to %s\n", routeName)
				// MPB: I think re-routes should disable taint mode, since they
				// are explicitly called from within the code.
				return r.runRoute(routeName, cxt, /*taint*/ false)
			}

			_, isType = irq.(*Stop)
			if isType {
				return nil
			}

			// If this is a recoverable error, recover and go on.
			err, isType := irq.(*RecoverableError)
			// Otherwise, terminate the route.
			if isType {
				// Swallow the error.
				// XXX: Should this be logged?
				cxt.Logf("warn", "Continuing after Recoverable Error on route %s: %v", route, err)
			} else {
				// return irq.(*FatalError)
				return irq.(error)
			}
		}
	}
	return nil
}

// Do an individual command.
func (r *Router) doCommand(cmd *commandSpec, cxt Context) (interface{}, Interrupt) {
	params := r.resolveParams(cmd, cxt)

	ret, irq := cmd.command(cxt, params)
	return ret, irq
}

// Get the appropriate values for each param.
func (r *Router) resolveParams(cmd *commandSpec, cxt Context) *Params {
	parameters := NewParams(len(cmd.parameters))
	for _, ps := range cmd.parameters {
		sources := parseFromStatement(ps.from)
		val := r.defaultFromSources(sources, cxt)
		if val == nil {
			parameters.set(ps.name, ps.defaultValue)
			val = ps.defaultValue
		}
		parameters.set(ps.name, val)
	}
	return parameters
}

// Get the values from a source.
// Returns the value of the first source to return a non-nil value.
func (r *Router) defaultFromSources(sources []*fromVal, cxt Context) interface{} {
	for _, src := range sources {
		switch src.source {
		case "c", "cxt", "context":
			val, ok := cxt.Has(src.key)
			if ok {
				return val
			}
		case "datasource", "ds":
			ds, ok := cxt.HasDatasource(src.key)
			if ok {
				return ds
			}
		default:
			// If we have a datasource, and the datasource
			// is a KeyValueDatasource, try to return the value.
			if ds, ok := cxt.HasDatasource(src.source); ok {
				store, ok := ds.(KeyValueDatasource)
				if ok {
					v := store.Value(src.key)
					if v != nil {
						return v
					}
					//fmt.Printf("V is nil for %v\n", src)
				}
			}
		}
	}
	return nil
}

// Parse a 'from' statement.
func parseFromStatement(from string) []*fromVal {
	toks := strings.Fields(from)
	ret := make([]*fromVal, len(toks))
	for i, tok := range toks {
		ret[i] = parseFromVal(tok)
	}
	return ret
}

// Represents a 'from' value of a 'from' statement.
type fromVal struct {
	source, key string
}

// Parse a FROM string of the form NAME:VALUE
func parseFromVal(from string) *fromVal {
	vals := strings.SplitN(strings.TrimSpace(from), ":", 2)
	if len(vals) == 1 {
		return &fromVal{vals[0], ""}
	}
	return &fromVal{vals[0], vals[1]}
}

// RouteError indicates that a route cannot be executed successfully.
type RouteError struct {
	Message string
}

// Error returns the error.
func (e *RouteError) Error() string {
	return e.Message
}
<commit_msg>Fixed documentation formatting.<commit_after>package cookoo

import (
	"fmt"
	"strings"
)

// RequestResolver is the interface for the request resolver.
// A request resolver is responsible for transforming a request name to
// a route name. For example, a web-specific resolver may take a URI
// and return a route name. Or it make take an HTTP verb and return a
// route name.
type RequestResolver interface {
	Init(registry *Registry)
	Resolve(path string, cxt Context) (string, error)
}

// Router is the Cookoo router.
// A Cookoo app works by passing a request into a router, and
// relying on the router to execute the appropriate chain of
// commands.
type Router struct {
	registry *Registry
	resolver RequestResolver
}

// BasicRequestResolver is a basic resolver that assumes that the given request
// name *is* the route name.
type BasicRequestResolver struct {
	registry *Registry
	resolver RequestResolver
}

// NewRouter creates a new Router.
func NewRouter(reg *Registry) *Router {
	router := new(Router)
	router.Init(reg)
	return router
}

// Init initializes the BasicRequestResolver.
func (r *BasicRequestResolver) Init(registry *Registry) {
	r.registry = registry
}

// Resolve returns the given path.
// This is a non-transforming resolver.
func (r *BasicRequestResolver) Resolve(path string, cxt Context) (string, error) {
	return path, nil
}

// Init initializes the Router.
func (r *Router) Init(registry *Registry) *Router {
	r.registry = registry
	r.resolver = new(BasicRequestResolver)
	r.resolver.Init(registry)
	return r
}

// SetRegistry sets the registry.
func (r *Router) SetRegistry(reg *Registry) {
	r.registry = reg
}

// SetRequestResolver sets the request resolver.
// The resolver is responsible for taking an arbitrary string and
// resolving it to a registry route.
//
// Example: Take a URI and translate it to a route.
func (r *Router) SetRequestResolver(resolver RequestResolver) {
	r.resolver = resolver
}

// RequestResolver gets the request resolver.
func (r *Router) RequestResolver() RequestResolver {
	return r.resolver
}

// ResolveRequest resolver a given string into a route name.
func (r *Router) ResolveRequest(name string, cxt Context) (string, error) {
	routeName, e := r.resolver.Resolve(name, cxt)

	if e != nil {
		return routeName, e
	}

	return routeName, nil
}

// HandleRequest does a request.
// This executes a request "named" name (this string is passed through the
// request resolver.) The context is cloned (shallow copy) and passed in as the
// base context.
//
// If taint is `true`, then no routes that begin with `@` can be executed. Taint
// should be set to true on anything that relies on a name supplied by an
// external client.
//
// This will do the following:
// 	- resolve the request name into a route name (using a RequestResolver)
// 	- look up the route
// 	- execute each command on the route in order
//
// The following context variables are placed into the context during a run:
//
// 	route.Name - Processed name of the current route
// 	route.Description - Description of the current route
// 	route.RequestName - raw route name as passed by the client
// 	command.Name - current command name (changed with each command)
//
// If an error occurred during processing, an error type is returned.
func (r *Router) HandleRequest(name string, cxt Context, taint bool) error {

	// Not sure why we were passing a copy of the context?
	// baseCxt := cxt.Copy()
	// routeName, e := r.ResolveRequest(name, baseCxt)
	routeName, e := r.ResolveRequest(name, cxt)

	if e != nil {
		return e
	}

	cxt.Put("route.RequestName", name)
	cxt.Put("route.Name", routeName)
	if spec, ok := r.registry.RouteSpec(routeName); ok {
		cxt.Put("route.Description", spec.description)
	}

	// Let an outer routine call go HandleRequest()
	//go r.runRoute(routeName, cxt, taint)
	e = r.runRoute(routeName, cxt, taint)

	return e
}

// HasRoute checks whether or not the route exists.
// Note that this does NOT resolve a request name into a route name. This
// expects a route name.
func (r *Router) HasRoute(name string) bool {
	_, ok := r.registry.RouteSpec(name)
	return ok
}

// PRIVATE ==========================================================

// Given a router, context, and taint, run the route.
func (r *Router) runRoute(route string, cxt Context, taint bool) error {
	if len(route) == 0 {
		return &RouteError{"Empty route name."}
	}
	if taint && route[0] == '@' {
		return &RouteError{"Route is tainted. Refusing to run."}
	}
	spec, ok := r.registry.RouteSpec(route)
	if !ok {
		return &RouteError{fmt.Sprintf("Route %s does not exist.", route)}
	}
	// fmt.Printf("Running route %s: %s\n", spec.name, spec.description)
	for _, cmd := range spec.commands {
		// Provide info for each run.
		cxt.Put("command.Name", cmd.name)

		// fmt.Printf("Command %d is %s (%T)\n", i, cmd.name, cmd.command)
		res, irq := r.doCommand(cmd, cxt)

		// This may store a nil.
		cxt.Put(cmd.name, res)

		// Handle interrupts.
		if irq != nil {
			// If this is a reroute, call runRoute() again.
			reroute, isType := irq.(*Reroute)
			if isType {
				routeName, e := r.ResolveRequest(reroute.RouteTo(), cxt)
				if e != nil {
					return e
				}
				//fmt.Printf("Routing to %s\n", routeName)
				// MPB: I think re-routes should disable taint mode, since they
				// are explicitly called from within the code.
				return r.runRoute(routeName, cxt /*taint*/, false)
			}

			_, isType = irq.(*Stop)
			if isType {
				return nil
			}

			// If this is a recoverable error, recover and go on.
			err, isType := irq.(*RecoverableError)
			// Otherwise, terminate the route.
			if isType {
				// Swallow the error.
				// XXX: Should this be logged?
				cxt.Logf("warn", "Continuing after Recoverable Error on route %s: %v", route, err)
			} else {
				// return irq.(*FatalError)
				return irq.(error)
			}
		}
	}
	return nil
}

// Do an individual command.
func (r *Router) doCommand(cmd *commandSpec, cxt Context) (interface{}, Interrupt) {
	params := r.resolveParams(cmd, cxt)

	ret, irq := cmd.command(cxt, params)
	return ret, irq
}

// Get the appropriate values for each param.
func (r *Router) resolveParams(cmd *commandSpec, cxt Context) *Params {
	parameters := NewParams(len(cmd.parameters))
	for _, ps := range cmd.parameters {
		sources := parseFromStatement(ps.from)
		val := r.defaultFromSources(sources, cxt)
		if val == nil {
			parameters.set(ps.name, ps.defaultValue)
			val = ps.defaultValue
		}
		parameters.set(ps.name, val)
	}
	return parameters
}

// Get the values from a source.
// Returns the value of the first source to return a non-nil value.
func (r *Router) defaultFromSources(sources []*fromVal, cxt Context) interface{} {
	for _, src := range sources {
		switch src.source {
		case "c", "cxt", "context":
			val, ok := cxt.Has(src.key)
			if ok {
				return val
			}
		case "datasource", "ds":
			ds, ok := cxt.HasDatasource(src.key)
			if ok {
				return ds
			}
		default:
			// If we have a datasource, and the datasource
			// is a KeyValueDatasource, try to return the value.
			if ds, ok := cxt.HasDatasource(src.source); ok {
				store, ok := ds.(KeyValueDatasource)
				if ok {
					v := store.Value(src.key)
					if v != nil {
						return v
					}
					//fmt.Printf("V is nil for %v\n", src)
				}
			}
		}
	}
	return nil
}

// Parse a 'from' statement.
func parseFromStatement(from string) []*fromVal {
	toks := strings.Fields(from)
	ret := make([]*fromVal, len(toks))
	for i, tok := range toks {
		ret[i] = parseFromVal(tok)
	}
	return ret
}

// Represents a 'from' value of a 'from' statement.
type fromVal struct {
	source, key string
}

// Parse a FROM string of the form NAME:VALUE
func parseFromVal(from string) *fromVal {
	vals := strings.SplitN(strings.TrimSpace(from), ":", 2)
	if len(vals) == 1 {
		return &fromVal{vals[0], ""}
	}
	return &fromVal{vals[0], vals[1]}
}

// RouteError indicates that a route cannot be executed successfully.
type RouteError struct {
	Message string
}

// Error returns the error.
func (e *RouteError) Error() string {
	return e.Message
}
<|endoftext|>


<commit_before>package buffalo

import (
	"fmt"
	"net/http"
	"path"
	"reflect"
	"sort"
	"strings"

	"github.com/markbates/inflect"
)

// GET maps an HTTP "GET" request to the path and the specified handler.
func (a *App) GET(p string, h Handler) *RouteInfo {
	return a.addRoute("GET", p, h)
}

// POST maps an HTTP "POST" request to the path and the specified handler.
func (a *App) POST(p string, h Handler) *RouteInfo {
	return a.addRoute("POST", p, h)
}

// PUT maps an HTTP "PUT" request to the path and the specified handler.
func (a *App) PUT(p string, h Handler) *RouteInfo {
	return a.addRoute("PUT", p, h)
}

// DELETE maps an HTTP "DELETE" request to the path and the specified handler.
func (a *App) DELETE(p string, h Handler) *RouteInfo {
	return a.addRoute("DELETE", p, h)
}

// HEAD maps an HTTP "HEAD" request to the path and the specified handler.
func (a *App) HEAD(p string, h Handler) *RouteInfo {
	return a.addRoute("HEAD", p, h)
}

// OPTIONS maps an HTTP "OPTIONS" request to the path and the specified handler.
func (a *App) OPTIONS(p string, h Handler) *RouteInfo {
	return a.addRoute("OPTIONS", p, h)
}

// PATCH maps an HTTP "PATCH" request to the path and the specified handler.
func (a *App) PATCH(p string, h Handler) *RouteInfo {
	return a.addRoute("PATCH", p, h)
}

// Redirect from one URL to another URL. Only works for "GET" requests.
func (a *App) Redirect(status int, from, to string) *RouteInfo {
	return a.GET(from, func(c Context) error {
		return c.Redirect(status, to)
	})
}

// ServeFiles maps an path to a directory on disk to serve static files.
// Useful for JavaScript, images, CSS, etc...
/*
	a.ServeFiles("/assets", http.Dir("path/to/assets"))
*/
func (a *App) ServeFiles(p string, root http.FileSystem) {
	a.router.PathPrefix(p).Handler(http.StripPrefix(p, http.FileServer(root)))
}

// Resource maps an implementation of the Resource interface
// to the appropriate RESTful mappings. Resource returns the *App
// associated with this group of mappings so you can set middleware, etc...
// on that group, just as if you had used the a.Group functionality.
/*
	a.Resource("/users", &UsersResource{})

	// Is equal to this:

	ur := &UsersResource{}
	g := a.Group("/users")
	g.GET("/", ur.List) // GET /users => ur.List
	g.GET("/new", ur.New) // GET /users/new => ur.New
	g.GET("/{user_id}", ur.Show) // GET /users/{user_id} => ur.Show
	g.GET("/{user_id}/edit", ur.Edit) // GET /users/{user_id}/edit => ur.Edit
	g.POST("/", ur.Create) // POST /users => ur.Create
	g.PUT("/{user_id}", ur.Update) PUT /users/{user_id} => ur.Update
	g.DELETE("/{user_id}", ur.Destroy) DELETE /users/{user_id} => ur.Destroy
*/
func (a *App) Resource(p string, r Resource) *App {
	paramName := inflect.Singularize(strings.Replace(p, "/", "_", -1))
	if strings.HasPrefix(paramName, "_") {
		paramName = paramName[1:len(paramName)]
	}

	g := a.Group(p)
	p = "/"

	rv := reflect.ValueOf(r)
	if rv.Kind() == reflect.Ptr {
		rv = rv.Elem()
	}
	rt := rv.Type()
	rname := fmt.Sprintf("%s.%s", rt.PkgPath(), rt.Name()) + ".%s"

	spath := path.Join(p, fmt.Sprintf("{%s_id}", paramName))
	setFuncKey(r.List, fmt.Sprintf(rname, "List"))
	g.GET(p, r.List)
	setFuncKey(r.New, fmt.Sprintf(rname, "New"))
	g.GET(path.Join(p, "new"), r.New)
	setFuncKey(r.Show, fmt.Sprintf(rname, "Show"))
	g.GET(path.Join(spath), r.Show)
	setFuncKey(r.Edit, fmt.Sprintf(rname, "Edit"))
	g.GET(path.Join(spath, "edit"), r.Edit)
	setFuncKey(r.Create, fmt.Sprintf(rname, "Create"))
	g.POST(p, r.Create)
	setFuncKey(r.Update, fmt.Sprintf(rname, "Update"))
	g.PUT(path.Join(spath), r.Update)
	setFuncKey(r.Destroy, fmt.Sprintf(rname, "Destroy"))
	g.DELETE(path.Join(spath), r.Destroy)
	return g
}

// ANY accepts a request across any HTTP method for the specified path
// and routes it to the specified Handler.
func (a *App) ANY(p string, h Handler) {
	a.GET(p, h)
	a.POST(p, h)
	a.PUT(p, h)
	a.PATCH(p, h)
	a.HEAD(p, h)
	a.OPTIONS(p, h)
	a.DELETE(p, h)
}

// Group creates a new `*App` that inherits from it's parent `*App`.
// This is useful for creating groups of end-points that need to share
// common functionality, like middleware.
/*
	g := a.Group("/api/v1")
	g.Use(AuthorizeAPIMiddleware)
	g.GET("/users, APIUsersHandler)
	g.GET("/users/:user_id, APIUserShowHandler)
*/
func (a *App) Group(groupPath string) *App {
	g := New(a.Options)

	g.prefix = path.Join(a.prefix, groupPath)

	g.router = a.router
	g.Middleware = a.Middleware.clone()
	g.ErrorHandlers = a.ErrorHandlers
	g.root = a
	if a.root != nil {
		g.root = a.root
	}
	return g
}

func (a *App) addRoute(method string, url string, h Handler) *RouteInfo {
	a.moot.Lock()
	defer a.moot.Unlock()

	url = path.Join(a.prefix, url)

	hs := funcKey(h)
	r := RouteInfo{
		Method:      method,
		Path:        url,
		HandlerName: hs,
		Handler:     h,
		App:         a,
	}

	r.MuxRoute = a.router.Handle(url, r).Methods(method)
	r.Name(buildRouteName(url))

	routes := a.Routes()
	routes = append(routes, r)
	sort.Sort(routes)

	if a.root != nil {
		a.root.routes = routes
	} else {
		a.routes = routes
	}

	return &r
}

//buildRouteName builds a route based on the path passed.
func buildRouteName(path string) string {
	if path == "/" || path == "" {
		return "root"
	}

	resultParts := []string{}
	parts := strings.Split(path, "/")

	for index, part := range parts {

		if strings.Contains(part, "{") || part == "" {
			continue
		}

		shouldSingularize := (len(parts) > index+1) && strings.Contains(parts[index+1], "{")
		if shouldSingularize {
			part = inflect.Singularize(part)
		}

		if parts[index] == "new" || parts[index] == "edit" {
			resultParts = append([]string{part}, resultParts...)
			continue
		}

		if index > 0 && strings.Contains(parts[index-1], "}") {
			resultParts = append(resultParts, part)
			continue
		}

		resultParts = append(resultParts, part)
	}

	underscore := strings.TrimSpace(strings.Join(resultParts, "_"))
	return inflect.CamelizeDownFirst(underscore)
}
<commit_msg>getting param name from the resource name<commit_after>package buffalo

import (
	"fmt"
	"net/http"
	"path"
	"reflect"
	"sort"
	"strings"

	"github.com/markbates/inflect"
)

// GET maps an HTTP "GET" request to the path and the specified handler.
func (a *App) GET(p string, h Handler) *RouteInfo {
	return a.addRoute("GET", p, h)
}

// POST maps an HTTP "POST" request to the path and the specified handler.
func (a *App) POST(p string, h Handler) *RouteInfo {
	return a.addRoute("POST", p, h)
}

// PUT maps an HTTP "PUT" request to the path and the specified handler.
func (a *App) PUT(p string, h Handler) *RouteInfo {
	return a.addRoute("PUT", p, h)
}

// DELETE maps an HTTP "DELETE" request to the path and the specified handler.
func (a *App) DELETE(p string, h Handler) *RouteInfo {
	return a.addRoute("DELETE", p, h)
}

// HEAD maps an HTTP "HEAD" request to the path and the specified handler.
func (a *App) HEAD(p string, h Handler) *RouteInfo {
	return a.addRoute("HEAD", p, h)
}

// OPTIONS maps an HTTP "OPTIONS" request to the path and the specified handler.
func (a *App) OPTIONS(p string, h Handler) *RouteInfo {
	return a.addRoute("OPTIONS", p, h)
}

// PATCH maps an HTTP "PATCH" request to the path and the specified handler.
func (a *App) PATCH(p string, h Handler) *RouteInfo {
	return a.addRoute("PATCH", p, h)
}

// Redirect from one URL to another URL. Only works for "GET" requests.
func (a *App) Redirect(status int, from, to string) *RouteInfo {
	return a.GET(from, func(c Context) error {
		return c.Redirect(status, to)
	})
}

// ServeFiles maps an path to a directory on disk to serve static files.
// Useful for JavaScript, images, CSS, etc...
/*
	a.ServeFiles("/assets", http.Dir("path/to/assets"))
*/
func (a *App) ServeFiles(p string, root http.FileSystem) {
	a.router.PathPrefix(p).Handler(http.StripPrefix(p, http.FileServer(root)))
}

// Resource maps an implementation of the Resource interface
// to the appropriate RESTful mappings. Resource returns the *App
// associated with this group of mappings so you can set middleware, etc...
// on that group, just as if you had used the a.Group functionality.
/*
	a.Resource("/users", &UsersResource{})

	// Is equal to this:

	ur := &UsersResource{}
	g := a.Group("/users")
	g.GET("/", ur.List) // GET /users => ur.List
	g.GET("/new", ur.New) // GET /users/new => ur.New
	g.GET("/{user_id}", ur.Show) // GET /users/{user_id} => ur.Show
	g.GET("/{user_id}/edit", ur.Edit) // GET /users/{user_id}/edit => ur.Edit
	g.POST("/", ur.Create) // POST /users => ur.Create
	g.PUT("/{user_id}", ur.Update) PUT /users/{user_id} => ur.Update
	g.DELETE("/{user_id}", ur.Destroy) DELETE /users/{user_id} => ur.Destroy
*/
func (a *App) Resource(p string, r Resource) *App {
	g := a.Group(p)
	p = "/"

	rv := reflect.ValueOf(r)
	if rv.Kind() == reflect.Ptr {
		rv = rv.Elem()
	}

	rt := rv.Type()
	rname := fmt.Sprintf("%s.%s", rt.PkgPath(), rt.Name()) + ".%s"

	name := strings.Replace(rt.Name(), "Resource", "", 1)
	paramName := inflect.Singularize(inflect.Underscore(name))

	spath := path.Join(p, fmt.Sprintf("{%s_id}", paramName))
	setFuncKey(r.List, fmt.Sprintf(rname, "List"))
	g.GET(p, r.List)
	setFuncKey(r.New, fmt.Sprintf(rname, "New"))
	g.GET(path.Join(p, "new"), r.New)
	setFuncKey(r.Show, fmt.Sprintf(rname, "Show"))
	g.GET(path.Join(spath), r.Show)
	setFuncKey(r.Edit, fmt.Sprintf(rname, "Edit"))
	g.GET(path.Join(spath, "edit"), r.Edit)
	setFuncKey(r.Create, fmt.Sprintf(rname, "Create"))
	g.POST(p, r.Create)
	setFuncKey(r.Update, fmt.Sprintf(rname, "Update"))
	g.PUT(path.Join(spath), r.Update)
	setFuncKey(r.Destroy, fmt.Sprintf(rname, "Destroy"))
	g.DELETE(path.Join(spath), r.Destroy)
	return g
}

// ANY accepts a request across any HTTP method for the specified path
// and routes it to the specified Handler.
func (a *App) ANY(p string, h Handler) {
	a.GET(p, h)
	a.POST(p, h)
	a.PUT(p, h)
	a.PATCH(p, h)
	a.HEAD(p, h)
	a.OPTIONS(p, h)
	a.DELETE(p, h)
}

// Group creates a new `*App` that inherits from it's parent `*App`.
// This is useful for creating groups of end-points that need to share
// common functionality, like middleware.
/*
	g := a.Group("/api/v1")
	g.Use(AuthorizeAPIMiddleware)
	g.GET("/users, APIUsersHandler)
	g.GET("/users/:user_id, APIUserShowHandler)
*/
func (a *App) Group(groupPath string) *App {
	g := New(a.Options)

	g.prefix = path.Join(a.prefix, groupPath)

	g.router = a.router
	g.Middleware = a.Middleware.clone()
	g.ErrorHandlers = a.ErrorHandlers
	g.root = a
	if a.root != nil {
		g.root = a.root
	}
	return g
}

func (a *App) addRoute(method string, url string, h Handler) *RouteInfo {
	a.moot.Lock()
	defer a.moot.Unlock()

	url = path.Join(a.prefix, url)

	hs := funcKey(h)
	r := RouteInfo{
		Method:      method,
		Path:        url,
		HandlerName: hs,
		Handler:     h,
		App:         a,
	}

	r.MuxRoute = a.router.Handle(url, r).Methods(method)
	r.Name(buildRouteName(url))

	routes := a.Routes()
	routes = append(routes, r)
	sort.Sort(routes)

	if a.root != nil {
		a.root.routes = routes
	} else {
		a.routes = routes
	}

	return &r
}

//buildRouteName builds a route based on the path passed.
func buildRouteName(path string) string {
	if path == "/" || path == "" {
		return "root"
	}

	resultParts := []string{}
	parts := strings.Split(path, "/")

	for index, part := range parts {

		if strings.Contains(part, "{") || part == "" {
			continue
		}

		shouldSingularize := (len(parts) > index+1) && strings.Contains(parts[index+1], "{")
		if shouldSingularize {
			part = inflect.Singularize(part)
		}

		if parts[index] == "new" || parts[index] == "edit" {
			resultParts = append([]string{part}, resultParts...)
			continue
		}

		if index > 0 && strings.Contains(parts[index-1], "}") {
			resultParts = append(resultParts, part)
			continue
		}

		resultParts = append(resultParts, part)
	}

	underscore := strings.TrimSpace(strings.Join(resultParts, "_"))
	return inflect.CamelizeDownFirst(underscore)
}
<|endoftext|>


<commit_before>package f

import (
	"github.com/ricallinson/stackr"
	"strings"
)

type Router struct {

	// Holds the route mappings this Router will use.
	Routes []*Route

	// Params that trigger functions
	ParamFuncs map[string]func(*Request, *Response, func())

	// Enable case-sensitive routes
	CaseSensitive bool

	// Enable strict matching for trailing slashes
	Strict bool
}

func (this *Router) AddParamFunc(name string, fn func(*Request, *Response, func())) {
	this.ParamFuncs[name] = fn
}

func (this *Router) AddRoute(verb string, path string, funcs ...func(*Request, *Response, func())) {

	route := &Route{
		Method:        strings.ToUpper(verb),
		Path:          path,
		Funcs:         funcs,
		CaseSensitive: this.CaseSensitive,
		Strict:        this.Strict,
	}

	this.Routes = append(this.Routes, route)
}

/*
	Calls the function defined for each key in "req.Params" if available.
*/
func (this *Router) executeParamFuncs(req *Request, res *Response, next func()) {
	// Call param functions if they are set.
	for name := range req.Params {
		if fn, ok := this.ParamFuncs[name]; ok {
			fn(req, res, next)
			// If the response has been "closed" by a "param" function then return.
			if res.Closed == true {
				return
			}
		}
	}
}

/*
	Calls each function defined in the given route.
*/
func (this *Router) executeRouteFuncs(req *Request, res *Response, next func(), route *Route) {
	for _, fn := range route.Funcs {
		fn(req, res, next)
		// If the response has been "closed" by a "route" function then return.
		if res.Closed == true {
			return
		}
	}
}

func (this *Router) Middleware(app *Server) func(req *stackr.Request, res *stackr.Response, next func()) {

	this.ParamFuncs = map[string]func(*Request, *Response, func()){}

	/*
		This func is in the serving path so has to be super fast!
	*/
	return func(req *stackr.Request, res *stackr.Response, next func()) {

		// Relative to path only so remove the query string.
		path := req.OriginalUrl
		if i := strings.Index(path, "?"); i > 0 {
			path = path[:i]
		}

		// Create the f.Request and f.Response from stackr.Request and stackr.Response
		freq := createRequest(req, app)
		fres := createResponse(res, next, app)
		freq.SetResponse(fres) // Add the Response to the Request
		fres.SetRequest(freq)  // Add the Request to the Response

		for _, route := range this.Routes {
			// If the route matches use it.
			if params, ok := route.Match(req.Method, path); ok {
				// Set the route "params" found in the path.
				freq.Params = params
				// Call "param" functions if they are defined.
				this.executeParamFuncs(freq, fres, next)
				// Call "route" functions if they are defined.
				this.executeRouteFuncs(freq, fres, next, route)
			}
		}
	}
}
<commit_msg>Update to comments<commit_after>package f

import (
	"github.com/ricallinson/stackr"
	"strings"
)

type Router struct {

	// Holds the route mappings this Router will use.
	Routes []*Route

	// Params that trigger functions
	ParamFuncs map[string]func(*Request, *Response, func())

	// Enable case-sensitive routes
	CaseSensitive bool

	// Enable strict matching for trailing slashes
	Strict bool
}

/*
	Add a "param" function to the Router.
*/
func (this *Router) AddParamFunc(name string, fn func(*Request, *Response, func())) {
	this.ParamFuncs[name] = fn
}

/*
	Add a new route to the Router.
*/
func (this *Router) AddRoute(verb string, path string, funcs ...func(*Request, *Response, func())) {

	route := &Route{
		Method:        strings.ToUpper(verb),
		Path:          path,
		Funcs:         funcs,
		CaseSensitive: this.CaseSensitive,
		Strict:        this.Strict,
	}

	this.Routes = append(this.Routes, route)
}

/*
	Calls the function defined for each key in "req.Params" if available.
*/
func (this *Router) executeParamFuncs(req *Request, res *Response, next func()) (bool) {
	// Call each "param" function if one is set.
	for name := range req.Params {
		if pfn, ok := this.ParamFuncs[name]; ok {
			pfn(req, res, next)
			// If the response has been "closed" by a "param" function then return.
			if res.Closed == true {
				return true
			}
		}
	}
	return false
}

/*
	Calls each function defined in the given route.
*/
func (this *Router) executeRouteFuncs(req *Request, res *Response, next func(), route *Route) (bool) {
	for _, rfn := range route.Funcs {
		rfn(req, res, next)
		// If the response has been "closed" by a "route" function then return.
		if res.Closed == true {
			return true
		}
	}
	return false
}

func (this *Router) Middleware(app *Server) func(req *stackr.Request, res *stackr.Response, next func()) {

	this.ParamFuncs = map[string]func(*Request, *Response, func()){}

	/*
		This func is in the serving path so has to be super fast!
	*/
	return func(req *stackr.Request, res *stackr.Response, next func()) {

		// Relative to path only so remove the query string.
		path := req.OriginalUrl
		if i := strings.Index(path, "?"); i > 0 {
			path = path[:i]
		}

		// Create the f.Request and f.Response from stackr.Request and stackr.Response
		freq := createRequest(req, app)
		fres := createResponse(res, next, app)
		freq.SetResponse(fres) // Add the Response to the Request
		fres.SetRequest(freq)  // Add the Request to the Response

		for _, route := range this.Routes {
			// If the route matches use it.
			if params, ok := route.Match(req.Method, path); ok {
				// Set the route "params" found in the path.
				freq.Params = params
				// Call "param" functions if they are defined.
				// If it returns true then the connection has closed so stop processing.
				if this.executeParamFuncs(freq, fres, next) {
					return
				}
				// Call "route" functions if they are defined.
				// If it returns true then the connection has closed so stop processing.
				if this.executeRouteFuncs(freq, fres, next, route) {
					return
				}
			}
		}
	}
}
<|endoftext|>


<commit_before>package pipeline

import (
	"time"
)

// A StatsNode emits internal statistics about the another node at a given interval.
//
// The interval represents how often to emit the statistics based on real time.
// This means the interval time is independent of the times of the data points the other node is receiving.
// As a result the StatsNode is a root node in the task pipeline.
//
//
// The currently available internal statistics:
//
//    * emitted -- the number of points or batches this node has sent to its children.
//
// Each stat is available as a field in the data stream.
//
// The stats are in groups according to the original data.
// Meaning that if the source node is grouped by the tag 'host' as an example,
// then the counts are output per host with the appropriate 'host' tag.
// Since its possible for groups to change when crossing a node only the emitted groups
// are considered.
//
// Example:
//     var data = stream
//         |from()...
//     // Emit statistics every 1 minute and cache them via the HTTP API.
//     data
//         |stats(1m)
//         |httpOut('stats')
//     // Continue normal processing of the data stream
//     data...
//
// WARNING: It is not recommended to join the stats stream with the original data stream.
// Since they operate on different clocks you could potentially create a deadlock.
// This is a limitation of the current implementation and may be removed in the future.
type StatsNode struct {
	chainnode
	// tick:ignore
	SourceNode Node
	// tick:ignore
	Interval time.Duration

	// tick:ignore
	AlignFlag bool `tick:"Align"`
}

func newStatsNode(n Node, interval time.Duration) *StatsNode {
	return &StatsNode{
		chainnode:  newBasicChainNode("stats", StreamEdge, StreamEdge),
		SourceNode: n,
		Interval:   interval,
	}
}

// Round times to the StatsNode.Interval value.
// tick:property
func (n *StatsNode) Align() *StatsNode {
	n.AlignFlag = true
	return n
}
<commit_msg>Add pipeline stats node JSON serialization<commit_after>package pipeline

import (
	"encoding/json"
	"time"
)

// A StatsNode emits internal statistics about the another node at a given interval.
//
// The interval represents how often to emit the statistics based on real time.
// This means the interval time is independent of the times of the data points the other node is receiving.
// As a result the StatsNode is a root node in the task pipeline.
//
//
// The currently available internal statistics:
//
//    * emitted -- the number of points or batches this node has sent to its children.
//
// Each stat is available as a field in the data stream.
//
// The stats are in groups according to the original data.
// Meaning that if the source node is grouped by the tag 'host' as an example,
// then the counts are output per host with the appropriate 'host' tag.
// Since its possible for groups to change when crossing a node only the emitted groups
// are considered.
//
// Example:
//     var data = stream
//         |from()...
//     // Emit statistics every 1 minute and cache them via the HTTP API.
//     data
//         |stats(1m)
//         |httpOut('stats')
//     // Continue normal processing of the data stream
//     data...
//
// WARNING: It is not recommended to join the stats stream with the original data stream.
// Since they operate on different clocks you could potentially create a deadlock.
// This is a limitation of the current implementation and may be removed in the future.
type StatsNode struct {
	chainnode
	// tick:ignore
	SourceNode Node
	// tick:ignore
	Interval time.Duration

	// tick:ignore
	AlignFlag bool `tick:"Align"`
}

func newStatsNode(n Node, interval time.Duration) *StatsNode {
	return &StatsNode{
		chainnode:  newBasicChainNode("stats", StreamEdge, StreamEdge),
		SourceNode: n,
		Interval:   interval,
	}
}

// MarshalJSON converts StatsNode to JSON
func (n *StatsNode) MarshalJSON() ([]byte, error) {
	props := JSONNode{}.
		SetType("stats").
		SetID(n.ID()).
		SetDuration("interval", n.Interval).
		Set("align", n.AlignFlag)

	return json.Marshal(&props)
}

func (n *StatsNode) unmarshal(props JSONNode) error {
	err := props.CheckTypeOf("stats")
	if err != nil {
		return err
	}
	if n.id, err = props.ID(); err != nil {
		return err
	}
	if n.Interval, err = props.Duration("interval"); err != nil {
		return err
	}
	if n.AlignFlag, err = props.Bool("align"); err != nil {
		return err
	}
	return nil
}

// UnmarshalJSON converts JSON to StatsNode
func (n *StatsNode) UnmarshalJSON(data []byte) error {
	props, err := NewJSONNode(data)
	if err != nil {
		return err
	}
	return n.unmarshal(props)
}

// Round times to the StatsNode.Interval value.
// tick:property
func (n *StatsNode) Align() *StatsNode {
	n.AlignFlag = true
	return n
}
<|endoftext|>


<commit_before>// Package jsutil provides utility functions for interacting with native JavaScript APIs.
package jsutil

import (
	"encoding/json"
	"reflect"

	"github.com/gopherjs/gopherjs/js"
	"honnef.co/go/js/dom"
)

// Wrap returns a wrapper func that handles the conversion from native JavaScript *js.Object parameters
// to the following types.
//
// It supports *js.Object (left unmodified), dom.Document, dom.Element, dom.Event, dom.HTMLElement, dom.Node.
// It has to be one of those types exactly; it can't be another type that implements the interface like *dom.BasicElement.
//
// For other types, the input is assumed to be a JSON string which is then unmarshalled into that type.
//
// Here is example usage:
//
// 	Example
//
// 	func Handler(event dom.Event, htmlElement dom.HTMLElement, data someStruct) {
// 		data.Foo = ... // Use event, htmlElement, data.
// 	}
//
// 	func main() {
// 		js.Global.Set("Handler", jsutil.Wrap(Handler))
// 	}
func Wrap(fn interface{}) func(...*js.Object) {
	v := reflect.ValueOf(fn)
	return func(args ...*js.Object) {
		in := make([]reflect.Value, v.Type().NumIn())
		for i := range in {
			switch t := v.Type().In(i); t {
			// *js.Object is passed through.
			case typeOf((**js.Object)(nil)):
				in[i] = reflect.ValueOf(args[i])

			// dom types are wrapped.
			case typeOf((*dom.Document)(nil)):
				in[i] = reflect.ValueOf(dom.WrapDocument(args[i]))
			case typeOf((*dom.Element)(nil)):
				in[i] = reflect.ValueOf(dom.WrapElement(args[i]))
			case typeOf((*dom.Event)(nil)):
				in[i] = reflect.ValueOf(dom.WrapEvent(args[i]))
			case typeOf((*dom.HTMLElement)(nil)):
				in[i] = reflect.ValueOf(dom.WrapHTMLElement(args[i]))
			case typeOf((*dom.Node)(nil)):
				in[i] = reflect.ValueOf(dom.WrapNode(args[i]))

			// Unmarshal incoming encoded JSON into the Go type.
			default:
				p := reflect.New(t)
				err := json.Unmarshal([]byte(args[i].String()), p.Interface())
				if err != nil {
					panic("jsutil: unmarshaling JSON failed: " + err.Error())
				}
				in[i] = reflect.Indirect(p)
			}
		}
		v.Call(in)
	}
}

// typeOf returns the reflect.Type of what the pointer points to.
func typeOf(pointer interface{}) reflect.Type {
	return reflect.TypeOf(pointer).Elem()
}
<commit_msg>gopherjs_http/jsutil: Document panic behavior when argument count mismatch.<commit_after>// Package jsutil provides utility functions for interacting with native JavaScript APIs.
package jsutil

import (
	"encoding/json"
	"fmt"
	"reflect"

	"github.com/gopherjs/gopherjs/js"
	"honnef.co/go/js/dom"
)

// Wrap returns a wrapper func that handles the conversion from native JavaScript *js.Object parameters
// to the following types.
//
// It supports *js.Object (left unmodified), dom.Document, dom.Element, dom.Event, dom.HTMLElement, dom.Node.
// It has to be one of those types exactly; it can't be another type that implements the interface like *dom.BasicElement.
//
// For other types, the input is assumed to be a JSON string which is then unmarshalled into that type.
//
// If the number of arguments provided to the wrapped func doesn't match
// the number of arguments for original func, it panics.
//
// Here is example usage:
//
// 	Example
//
// 	func Handler(event dom.Event, htmlElement dom.HTMLElement, data someStruct) {
// 		data.Foo = ... // Use event, htmlElement, data.
// 	}
//
// 	func main() {
// 		js.Global.Set("Handler", jsutil.Wrap(Handler))
// 	}
func Wrap(fn interface{}) func(...*js.Object) {
	v := reflect.ValueOf(fn)
	return func(args ...*js.Object) {
		if len(args) != v.Type().NumIn() {
			panic(fmt.Errorf("wrapped %v got %v arguments, want %v", v.Type().String(), len(args), v.Type().NumIn()))
		}
		in := make([]reflect.Value, v.Type().NumIn())
		for i := range in {
			switch t := v.Type().In(i); t {
			// *js.Object is passed through.
			case typeOf((**js.Object)(nil)):
				in[i] = reflect.ValueOf(args[i])

			// dom types are wrapped.
			case typeOf((*dom.Document)(nil)):
				in[i] = reflect.ValueOf(dom.WrapDocument(args[i]))
			case typeOf((*dom.Element)(nil)):
				in[i] = reflect.ValueOf(dom.WrapElement(args[i]))
			case typeOf((*dom.Event)(nil)):
				in[i] = reflect.ValueOf(dom.WrapEvent(args[i]))
			case typeOf((*dom.HTMLElement)(nil)):
				in[i] = reflect.ValueOf(dom.WrapHTMLElement(args[i]))
			case typeOf((*dom.Node)(nil)):
				in[i] = reflect.ValueOf(dom.WrapNode(args[i]))

			// Unmarshal incoming encoded JSON into the Go type.
			default:
				p := reflect.New(t)
				err := json.Unmarshal([]byte(args[i].String()), p.Interface())
				if err != nil {
					panic("jsutil: unmarshaling JSON failed: " + err.Error())
				}
				in[i] = reflect.Indirect(p)
			}
		}
		v.Call(in)
	}
}

// typeOf returns the reflect.Type of what the pointer points to.
func typeOf(pointer interface{}) reflect.Type {
	return reflect.TypeOf(pointer).Elem()
}
<|endoftext|>


<commit_before>package gorush

import (
	"fmt"
	"runtime"

	"github.com/gin-gonic/gin"
)

var version string

// SetVersion for setup version string.
func SetVersion(ver string) {
	version = ver
}

// GetVersion for get current version.
func GetVersion() string {
	return version
}

// PrintGoRushVersion provide print server engine
func PrintGoRushVersion() {
	fmt.Printf(`GoRush %s, Compiler: %s %s, Copyright (C) 2016 Bo-Yi Wu, Inc.`,
		version,
		runtime.Compiler,
		runtime.Version())
	fmt.Println()
}

// VersionMiddleware : add version on header.
func VersionMiddleware() gin.HandlerFunc {
	// Set out header value for each response
	return func(c *gin.Context) {
		c.Header("X-DRONE-VERSION", version)
		c.Next()
	}
}
<commit_msg>fix: typo in header.<commit_after>package gorush

import (
	"fmt"
	"runtime"

	"github.com/gin-gonic/gin"
)

var version string

// SetVersion for setup version string.
func SetVersion(ver string) {
	version = ver
}

// GetVersion for get current version.
func GetVersion() string {
	return version
}

// PrintGoRushVersion provide print server engine
func PrintGoRushVersion() {
	fmt.Printf(`GoRush %s, Compiler: %s %s, Copyright (C) 2016 Bo-Yi Wu, Inc.`,
		version,
		runtime.Compiler,
		runtime.Version())
	fmt.Println()
}

// VersionMiddleware : add version on header.
func VersionMiddleware() gin.HandlerFunc {
	// Set out header value for each response
	return func(c *gin.Context) {
		c.Header("X-GORUSH-VERSION", version)
		c.Next()
	}
}
<|endoftext|>


<commit_before>package aws

import (
	"fmt"
	"log"
	"time"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/service/datasync"
	"github.com/hashicorp/terraform-plugin-sdk/helper/schema"
	"github.com/hashicorp/terraform-plugin-sdk/helper/validation"
	"github.com/terraform-providers/terraform-provider-aws/aws/internal/keyvaluetags"
)

func resourceAwsDataSyncLocationFsxWindows() *schema.Resource {
	return &schema.Resource{
		Create: resourceAwsDataSyncLocationFsxWindowsCreate,
		Read:   resourceAwsDataSyncLocationFsxWindowsRead,
		Update: resourceAwsDataSyncLocationFsxWindowsUpdate,
		Delete: resourceAwsDataSyncLocationFsxWindowsDelete,
		Importer: &schema.ResourceImporter{
			State: schema.ImportStatePassthrough,
		},

		Schema: map[string]*schema.Schema{
			"arn": {
				Type:     schema.TypeString,
				Computed: true,
			},
			"fsx_filesystem_arn": {
				Type:         schema.TypeString,
				Required:     true,
				ForceNew:     true,
				ValidateFunc: validateArn,
			},
			"password": {
				Type:         schema.TypeString,
				Required:     true,
				ForceNew:     true,
				Sensitive:    true,
				ValidateFunc: validation.StringLenBetween(1, 104),
			},
			"user": {
				Type:         schema.TypeString,
				Required:     true,
				ForceNew:     true,
				ValidateFunc: validation.StringLenBetween(1, 104),
			},
			"domain": {
				Type:         schema.TypeString,
				Optional:     true,
				ForceNew:     true,
				ValidateFunc: validation.StringLenBetween(1, 253),
			},
			"security_group_arns": {
				Type:     schema.TypeSet,
				Required: true,
				ForceNew: true,
				MinItems: 1,
				MaxItems: 5,
				Elem: &schema.Schema{
					Type:         schema.TypeString,
					ValidateFunc: validateArn,
				},
			},
			"subdirectory": {
				Type:         schema.TypeString,
				Optional:     true,
				ForceNew:     true,
				ValidateFunc: validation.StringLenBetween(1, 4096),
			},
			"tags": tagsSchema(),
			"uri": {
				Type:     schema.TypeString,
				Computed: true,
			},
			"creation_time": {
				Type:     schema.TypeString,
				Computed: true,
			},
		},
	}
}

func resourceAwsDataSyncLocationFsxWindowsCreate(d *schema.ResourceData, meta interface{}) error {
	conn := meta.(*AWSClient).datasyncconn

	input := &datasync.CreateLocationFsxWindowsInput{
		FsxFilesystemArn:  aws.String(d.Get("fsx_filesystem_arn").(string)),
		User:              aws.String(d.Get("user").(string)),
		Password:          aws.String(d.Get("password").(string)),
		SecurityGroupArns: expandStringSet(d.Get("security_group_arns").(*schema.Set)),
		Tags:              keyvaluetags.New(d.Get("tags").(map[string]interface{})).IgnoreAws().DatasyncTags(),
	}

	if v, ok := d.GetOk("subdirectory"); ok {
		input.Subdirectory = aws.String(v.(string))
	}

	if v, ok := d.GetOk("domain"); ok {
		input.Domain = aws.String(v.(string))
	}

	log.Printf("[DEBUG] Creating DataSync Location FsxWindows: %s", input)
	output, err := conn.CreateLocationFsxWindows(input)
	if err != nil {
		return fmt.Errorf("error creating DataSync Location FsxWindows: %s", err)
	}

	d.SetId(aws.StringValue(output.LocationArn))

	return resourceAwsDataSyncLocationFsxWindowsRead(d, meta)
}

func resourceAwsDataSyncLocationFsxWindowsRead(d *schema.ResourceData, meta interface{}) error {
	conn := meta.(*AWSClient).datasyncconn

	input := &datasync.DescribeLocationFsxWindowsInput{
		LocationArn: aws.String(d.Id()),
	}

	log.Printf("[DEBUG] Reading DataSync Location FsxWindows: %s", input)
	output, err := conn.DescribeLocationFsxWindows(input)

	if isAWSErr(err, datasync.ErrCodeInvalidRequestException, "not found") {
		log.Printf("[WARN] DataSync Location FsxWindows %q not found - removing from state", d.Id())
		d.SetId("")
		return nil
	}

	if err != nil {
		return fmt.Errorf("error reading DataSync Location FsxWindows (%s): %s", d.Id(), err)
	}

	subdirectory, err := dataSyncParseLocationURI(aws.StringValue(output.LocationUri))

	if err != nil {
		return fmt.Errorf("error parsing Location FsxWindows (%s) URI (%s): %s", d.Id(), aws.StringValue(output.LocationUri), err)
	}

	d.Set("arn", output.LocationArn)
	d.Set("security_group_arns", flattenStringSet(output.SecurityGroupArns))
	d.Set("subdirectory", subdirectory)
	d.Set("uri", output.LocationUri)
	d.Set("user", output.User)
	d.Set("domain", output.Domain)

	if err := d.Set("creation_time", output.CreationTime.Format(time.RFC3339)); err != nil {
		return fmt.Errorf("error setting creation_time: %s", err)
	}

	tags, err := keyvaluetags.DatasyncListTags(conn, d.Id())

	if err != nil {
		return fmt.Errorf("error listing tags for DataSync Location FsxWindows (%s): %s", d.Id(), err)
	}

	if err := d.Set("tags", tags.IgnoreAws().Map()); err != nil {
		return fmt.Errorf("error setting tags: %s", err)
	}

	return nil
}

func resourceAwsDataSyncLocationFsxWindowsUpdate(d *schema.ResourceData, meta interface{}) error {
	conn := meta.(*AWSClient).datasyncconn

	if d.HasChange("tags") {
		o, n := d.GetChange("tags")

		if err := keyvaluetags.DatasyncUpdateTags(conn, d.Id(), o, n); err != nil {
			return fmt.Errorf("error updating DataSync Location FsxWindows (%s) tags: %s", d.Id(), err)
		}
	}

	return resourceAwsDataSyncLocationFsxWindowsRead(d, meta)
}

func resourceAwsDataSyncLocationFsxWindowsDelete(d *schema.ResourceData, meta interface{}) error {
	conn := meta.(*AWSClient).datasyncconn

	input := &datasync.DeleteLocationInput{
		LocationArn: aws.String(d.Id()),
	}

	log.Printf("[DEBUG] Deleting DataSync Location FsxWindows: %s", input)
	_, err := conn.DeleteLocation(input)

	if isAWSErr(err, datasync.ErrCodeInvalidRequestException, "not found") {
		return nil
	}

	if err != nil {
		return fmt.Errorf("error deleting DataSync Location FsxWindows (%s): %s", d.Id(), err)
	}

	return nil
}
<commit_msg>add option to import fsx arn<commit_after>package aws

import (
	"fmt"
	"log"
	"strings"
	"time"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/service/datasync"
	"github.com/hashicorp/terraform-plugin-sdk/helper/schema"
	"github.com/hashicorp/terraform-plugin-sdk/helper/validation"
	"github.com/terraform-providers/terraform-provider-aws/aws/internal/keyvaluetags"
)

func resourceAwsDataSyncLocationFsxWindows() *schema.Resource {
	return &schema.Resource{
		Create: resourceAwsDataSyncLocationFsxWindowsCreate,
		Read:   resourceAwsDataSyncLocationFsxWindowsRead,
		Update: resourceAwsDataSyncLocationFsxWindowsUpdate,
		Delete: resourceAwsDataSyncLocationFsxWindowsDelete,
		Importer: &schema.ResourceImporter{
			State: schema.ImportStatePassthrough,
		},

		Schema: map[string]*schema.Schema{
			"arn": {
				Type:     schema.TypeString,
				Computed: true,
			},
			"fsx_filesystem_arn": {
				Type:         schema.TypeString,
				Required:     true,
				ForceNew:     true,
				ValidateFunc: validateArn,
			},
			"password": {
				Type:         schema.TypeString,
				Required:     true,
				ForceNew:     true,
				Sensitive:    true,
				ValidateFunc: validation.StringLenBetween(1, 104),
			},
			"user": {
				Type:         schema.TypeString,
				Required:     true,
				ForceNew:     true,
				ValidateFunc: validation.StringLenBetween(1, 104),
			},
			"domain": {
				Type:         schema.TypeString,
				Optional:     true,
				ForceNew:     true,
				ValidateFunc: validation.StringLenBetween(1, 253),
			},
			"security_group_arns": {
				Type:     schema.TypeSet,
				Required: true,
				ForceNew: true,
				MinItems: 1,
				MaxItems: 5,
				Elem: &schema.Schema{
					Type:         schema.TypeString,
					ValidateFunc: validateArn,
				},
			},
			"subdirectory": {
				Type:         schema.TypeString,
				Optional:     true,
				Computed:     true,
				ForceNew:     true,
				ValidateFunc: validation.StringLenBetween(1, 4096),
			},
			"tags": tagsSchema(),
			"uri": {
				Type:     schema.TypeString,
				Computed: true,
			},
			"creation_time": {
				Type:     schema.TypeString,
				Computed: true,
			},
		},
	}
}

func resourceAwsDataSyncLocationFsxWindowsCreate(d *schema.ResourceData, meta interface{}) error {
	conn := meta.(*AWSClient).datasyncconn
	fsxArn := d.Get("fsx_filesystem_arn").(string)

	input := &datasync.CreateLocationFsxWindowsInput{
		FsxFilesystemArn:  aws.String(fsxArn),
		User:              aws.String(d.Get("user").(string)),
		Password:          aws.String(d.Get("password").(string)),
		SecurityGroupArns: expandStringSet(d.Get("security_group_arns").(*schema.Set)),
		Tags:              keyvaluetags.New(d.Get("tags").(map[string]interface{})).IgnoreAws().DatasyncTags(),
	}

	if v, ok := d.GetOk("subdirectory"); ok {
		input.Subdirectory = aws.String(v.(string))
	}

	if v, ok := d.GetOk("domain"); ok {
		input.Domain = aws.String(v.(string))
	}

	log.Printf("[DEBUG] Creating DataSync Location FsxWindows: %s", input)
	output, err := conn.CreateLocationFsxWindows(input)
	if err != nil {
		return fmt.Errorf("error creating DataSync Location FsxWindows: %s", err)
	}

	d.SetId(fmt.Sprintf("%s:%s", aws.StringValue(output.LocationArn), fsxArn))

	return resourceAwsDataSyncLocationFsxWindowsRead(d, meta)
}

func resourceAwsDataSyncLocationFsxWindowsRead(d *schema.ResourceData, meta interface{}) error {
	conn := meta.(*AWSClient).datasyncconn
	locationArn, fsxArn, err := decodeAwsDataSyncLocationFsxWindowsID(d.Id())
	if err != nil {
		return err
	}

	input := &datasync.DescribeLocationFsxWindowsInput{
		LocationArn: aws.String(locationArn),
	}

	log.Printf("[DEBUG] Reading DataSync Location Fsx Windows: %s", input)
	output, err := conn.DescribeLocationFsxWindows(input)

	if isAWSErr(err, datasync.ErrCodeInvalidRequestException, "not found") {
		log.Printf("[WARN] DataSync Location Fsx Windows %q not found - removing from state", locationArn)
		d.SetId("")
		return nil
	}

	if err != nil {
		return fmt.Errorf("error reading DataSync Location Fsx Windows (%s): %s", locationArn, err)
	}

	subdirectory, err := dataSyncParseLocationURI(aws.StringValue(output.LocationUri))

	if err != nil {
		return fmt.Errorf("error parsing Location FsxWindows (%s) URI (%s): %s", d.Id(), aws.StringValue(output.LocationUri), err)
	}

	d.Set("arn", output.LocationArn)
	d.Set("fsx_filesystem_arn", fsxArn)
	d.Set("security_group_arns", flattenStringSet(output.SecurityGroupArns))
	d.Set("subdirectory", subdirectory)
	d.Set("uri", output.LocationUri)
	d.Set("user", output.User)
	d.Set("domain", output.Domain)

	if err := d.Set("creation_time", output.CreationTime.Format(time.RFC3339)); err != nil {
		return fmt.Errorf("error setting creation_time: %s", err)
	}

	tags, err := keyvaluetags.DatasyncListTags(conn, d.Id())

	if err != nil {
		return fmt.Errorf("error listing tags for DataSync Location Fsx Windows (%s): %s", d.Id(), err)
	}

	if err := d.Set("tags", tags.IgnoreAws().Map()); err != nil {
		return fmt.Errorf("error setting tags: %s", err)
	}

	return nil
}

func resourceAwsDataSyncLocationFsxWindowsUpdate(d *schema.ResourceData, meta interface{}) error {
	conn := meta.(*AWSClient).datasyncconn
	locationArn, _, err := decodeAwsDataSyncLocationFsxWindowsID(d.Id())
	if err != nil {
		return err
	}

	if d.HasChange("tags") {
		o, n := d.GetChange("tags")

		if err := keyvaluetags.DatasyncUpdateTags(conn, locationArn, o, n); err != nil {
			return fmt.Errorf("error updating DataSync Location FsxWindows (%s) tags: %s", locationArn, err)
		}
	}

	return resourceAwsDataSyncLocationFsxWindowsRead(d, meta)
}

func resourceAwsDataSyncLocationFsxWindowsDelete(d *schema.ResourceData, meta interface{}) error {
	conn := meta.(*AWSClient).datasyncconn
	locationArn, _, err := decodeAwsDataSyncLocationFsxWindowsID(d.Id())
	if err != nil {
		return err
	}

	input := &datasync.DeleteLocationInput{
		LocationArn: aws.String(locationArn),
	}

	log.Printf("[DEBUG] Deleting DataSync Location FsxWindows: %s", input)
	_, err = conn.DeleteLocation(input)

	if isAWSErr(err, datasync.ErrCodeInvalidRequestException, "not found") {
		return nil
	}

	if err != nil {
		return fmt.Errorf("error deleting DataSync Location Fsx Windows (%s): %s", locationArn, err)
	}

	return nil
}

func decodeAwsDataSyncLocationFsxWindowsID(id string) (string, string, error) {
	parts := strings.Split(id, ":")

	if len(parts) != 2 {
		return "", "", fmt.Errorf("Unexpected format of ID (%q), expected DataSyncLocationArn:FsxArn", id)
	}

	return parts[0], parts[1], nil
}
<|endoftext|>


<commit_before>/*
Copyright 2016 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 e2enode

import (
	"context"
	"time"

	"k8s.io/api/core/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/util/uuid"
	"k8s.io/kubernetes/test/e2e/framework"
	e2epod "k8s.io/kubernetes/test/e2e/framework/pod"

	"fmt"

	"github.com/onsi/ginkgo"
)

var _ = SIGDescribe("Kubelet Volume Manager", func() {
	f := framework.NewDefaultFramework("kubelet-volume-manager")
	ginkgo.Describe("Volume Manager", func() {
		ginkgo.Context("On terminatation of pod with memory backed volume", func() {
			ginkgo.It("should remove the volume from the node [NodeConformance]", func() {
				var (
					memoryBackedPod *v1.Pod
					volumeName      string
				)
				ginkgo.By("Creating a pod with a memory backed volume that exits success without restart", func() {
					volumeName = "memory-volume"
					memoryBackedPod = f.PodClient().Create(&v1.Pod{
						ObjectMeta: metav1.ObjectMeta{
							Name:      "pod" + string(uuid.NewUUID()),
							Namespace: f.Namespace.Name,
						},
						Spec: v1.PodSpec{
							RestartPolicy: v1.RestartPolicyNever,
							Containers: []v1.Container{
								{
									Image:   busyboxImage,
									Name:    "container" + string(uuid.NewUUID()),
									Command: []string{"sh", "-c", "echo"},
									VolumeMounts: []v1.VolumeMount{
										{
											Name:      volumeName,
											MountPath: "/tmp",
										},
									},
								},
							},
							Volumes: []v1.Volume{
								{
									Name: volumeName,
									VolumeSource: v1.VolumeSource{
										EmptyDir: &v1.EmptyDirVolumeSource{Medium: v1.StorageMediumMemory},
									},
								},
							},
						},
					})
					err := e2epod.WaitForPodSuccessInNamespace(f.ClientSet, memoryBackedPod.Name, f.Namespace.Name)
					framework.ExpectNoError(err)
				})
				ginkgo.By("Verifying the memory backed volume was removed from node", func() {
					volumePath := fmt.Sprintf("/tmp/%s/volumes/kubernetes.io~empty-dir/%s", string(memoryBackedPod.UID), volumeName)
					var err error
					for i := 0; i < 10; i++ {
						// need to create a new verification pod on each pass since updates
						//to the HostPath volume aren't propogated to the pod
						pod := f.PodClient().Create(&v1.Pod{
							ObjectMeta: metav1.ObjectMeta{
								Name:      "pod" + string(uuid.NewUUID()),
								Namespace: f.Namespace.Name,
							},
							Spec: v1.PodSpec{
								RestartPolicy: v1.RestartPolicyNever,
								Containers: []v1.Container{
									{
										Image:   busyboxImage,
										Name:    "container" + string(uuid.NewUUID()),
										Command: []string{"sh", "-c", "if [ -d " + volumePath + " ]; then exit 1; fi;"},
										VolumeMounts: []v1.VolumeMount{
											{
												Name:      "kubelet-pods",
												MountPath: "/tmp",
											},
										},
									},
								},
								Volumes: []v1.Volume{
									{
										Name: "kubelet-pods",
										VolumeSource: v1.VolumeSource{
											// TODO: remove hardcoded kubelet volume directory path
											// framework.TestContext.KubeVolumeDir is currently not populated for node e2e
											HostPath: &v1.HostPathVolumeSource{Path: "/var/lib/kubelet/pods"},
										},
									},
								},
							},
						})
						err = e2epod.WaitForPodSuccessInNamespace(f.ClientSet, pod.Name, f.Namespace.Name)
						gp := int64(1)
						f.PodClient().Delete(context.TODO(), pod.Name, metav1.DeleteOptions{GracePeriodSeconds: &gp})
						if err == nil {
							break
						}
						<-time.After(10 * time.Second)
					}
					framework.ExpectNoError(err)
				})
			})
		})
	})
})
<commit_msg>typo fixed<commit_after>/*
Copyright 2016 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 e2enode

import (
	"context"
	"time"

	"k8s.io/api/core/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/util/uuid"
	"k8s.io/kubernetes/test/e2e/framework"
	e2epod "k8s.io/kubernetes/test/e2e/framework/pod"

	"fmt"

	"github.com/onsi/ginkgo"
)

var _ = SIGDescribe("Kubelet Volume Manager", func() {
	f := framework.NewDefaultFramework("kubelet-volume-manager")
	ginkgo.Describe("Volume Manager", func() {
		ginkgo.Context("On termination of pod with memory backed volume", func() {
			ginkgo.It("should remove the volume from the node [NodeConformance]", func() {
				var (
					memoryBackedPod *v1.Pod
					volumeName      string
				)
				ginkgo.By("Creating a pod with a memory backed volume that exits success without restart", func() {
					volumeName = "memory-volume"
					memoryBackedPod = f.PodClient().Create(&v1.Pod{
						ObjectMeta: metav1.ObjectMeta{
							Name:      "pod" + string(uuid.NewUUID()),
							Namespace: f.Namespace.Name,
						},
						Spec: v1.PodSpec{
							RestartPolicy: v1.RestartPolicyNever,
							Containers: []v1.Container{
								{
									Image:   busyboxImage,
									Name:    "container" + string(uuid.NewUUID()),
									Command: []string{"sh", "-c", "echo"},
									VolumeMounts: []v1.VolumeMount{
										{
											Name:      volumeName,
											MountPath: "/tmp",
										},
									},
								},
							},
							Volumes: []v1.Volume{
								{
									Name: volumeName,
									VolumeSource: v1.VolumeSource{
										EmptyDir: &v1.EmptyDirVolumeSource{Medium: v1.StorageMediumMemory},
									},
								},
							},
						},
					})
					err := e2epod.WaitForPodSuccessInNamespace(f.ClientSet, memoryBackedPod.Name, f.Namespace.Name)
					framework.ExpectNoError(err)
				})
				ginkgo.By("Verifying the memory backed volume was removed from node", func() {
					volumePath := fmt.Sprintf("/tmp/%s/volumes/kubernetes.io~empty-dir/%s", string(memoryBackedPod.UID), volumeName)
					var err error
					for i := 0; i < 10; i++ {
						// need to create a new verification pod on each pass since updates
						//to the HostPath volume aren't propogated to the pod
						pod := f.PodClient().Create(&v1.Pod{
							ObjectMeta: metav1.ObjectMeta{
								Name:      "pod" + string(uuid.NewUUID()),
								Namespace: f.Namespace.Name,
							},
							Spec: v1.PodSpec{
								RestartPolicy: v1.RestartPolicyNever,
								Containers: []v1.Container{
									{
										Image:   busyboxImage,
										Name:    "container" + string(uuid.NewUUID()),
										Command: []string{"sh", "-c", "if [ -d " + volumePath + " ]; then exit 1; fi;"},
										VolumeMounts: []v1.VolumeMount{
											{
												Name:      "kubelet-pods",
												MountPath: "/tmp",
											},
										},
									},
								},
								Volumes: []v1.Volume{
									{
										Name: "kubelet-pods",
										VolumeSource: v1.VolumeSource{
											// TODO: remove hardcoded kubelet volume directory path
											// framework.TestContext.KubeVolumeDir is currently not populated for node e2e
											HostPath: &v1.HostPathVolumeSource{Path: "/var/lib/kubelet/pods"},
										},
									},
								},
							},
						})
						err = e2epod.WaitForPodSuccessInNamespace(f.ClientSet, pod.Name, f.Namespace.Name)
						gp := int64(1)
						f.PodClient().Delete(context.TODO(), pod.Name, metav1.DeleteOptions{GracePeriodSeconds: &gp})
						if err == nil {
							break
						}
						<-time.After(10 * time.Second)
					}
					framework.ExpectNoError(err)
				})
			})
		})
	})
})
<|endoftext|>


<commit_before>//go:build integration
// +build integration

/*
Copyright 2021 The Kubernetes Authors All rights reserved.

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

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

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

package integration

import (
	"context"
	"os/exec"
	"testing"
)

// TestMountStart tests using the mount command on start
func TestMountStart(t *testing.T) {
	if NoneDriver() {
		t.Skip("skipping: none driver does not support mount")
	}

	type validateFunc func(context.Context, *testing.T, string)
	profile1 := UniqueProfileName("mount-start-1")
	profile2 := UniqueProfileName("mount-start-2")
	ctx, cancel := context.WithTimeout(context.Background(), Minutes(15))
	defer Cleanup(t, profile1, cancel)
	defer Cleanup(t, profile2, cancel)

	// Serial tests
	t.Run("serial", func(t *testing.T) {
		tests := []struct {
			name      string
			validator validateFunc
			profile   string
		}{
			{"StartWithMountFirst", validateStartWithMount, profile1},
			{"StartWithMountSecond", validateStartWithMount, profile2},
			{"VerifyMountFirst", validateMount, profile1},
			{"VerifyMountSecond", validateMount, profile2},
			{"DeleteFirst", validateDelete, profile1},
			{"VerifyMountPostDelete", validateMount, profile2},
			{"Stop", validateMountStop, profile2},
			{"RestartStopped", validateRestart, profile2},
			{"VerifyMountPostStop", validateMount, profile2},
		}

		for _, test := range tests {
			if ctx.Err() == context.DeadlineExceeded {
				t.Fatalf("Unable to run more tests (deadline exceeded)")
			}

			t.Run(test.name, func(t *testing.T) {
				test.validator(ctx, t, test.profile)
			})
		}
	})
}

// validateStartWithMount starts a cluster with mount enabled
func validateStartWithMount(ctx context.Context, t *testing.T, profile string) {
	defer PostMortemLogs(t, profile)

	args := []string{"start", "-p", profile, "--memory=2048", "--mount"}
	args = append(args, StartArgs()...)
	rr, err := Run(t, exec.CommandContext(ctx, Target(), args...))
	if err != nil {
		t.Fatalf("failed to start minikube with args: %q : %v", rr.Command(), err)
	}
}

// validateMount checks if the cluster has a folder mounted
func validateMount(ctx context.Context, t *testing.T, profile string) {
	defer PostMortemLogs(t, profile)

	args := []string{"-p", profile, "ssh", "ls", "/minikube-host"}
	rr, err := Run(t, exec.CommandContext(ctx, Target(), args...))
	if err != nil {
		t.Fatalf("mount failed: %q : %v", rr.Command(), err)
	}
}

// validateMountStop stops a cluster
func validateMountStop(ctx context.Context, t *testing.T, profile string) {
	defer PostMortemLogs(t, profile)

	args := []string{"stop", "-p", profile}
	rr, err := Run(t, exec.CommandContext(ctx, Target(), args...))
	if err != nil {
		t.Fatalf("stop failed: %q : %v", rr.Command(), err)
	}
}

// validateRestart restarts a cluster
func validateRestart(ctx context.Context, t *testing.T, profile string) {
	defer PostMortemLogs(t, profile)

	args := []string{"start", "-p", profile}
	rr, err := Run(t, exec.CommandContext(ctx, Target(), args...))
	if err != nil {
		t.Fatalf("restart failed: %q : %v", rr.Command(), err)
	}
}
<commit_msg>add tests for new start mount flags<commit_after>//go:build integration
// +build integration

/*
Copyright 2021 The Kubernetes Authors All rights reserved.

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

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

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

package integration

import (
	"context"
	"fmt"
	"os/exec"
	"strings"
	"testing"
)

const (
	mountGID   = "0"
	mountMSize = "6543"
	mountMode  = "777"
	mountPort  = "46464"
	mountUID   = "0"
)

// TestMountStart tests using the mount command on start
func TestMountStart(t *testing.T) {
	if NoneDriver() {
		t.Skip("skipping: none driver does not support mount")
	}

	type validateFunc func(context.Context, *testing.T, string)
	profile1 := UniqueProfileName("mount-start-1")
	profile2 := UniqueProfileName("mount-start-2")
	ctx, cancel := context.WithTimeout(context.Background(), Minutes(15))
	defer Cleanup(t, profile1, cancel)
	defer Cleanup(t, profile2, cancel)

	// Serial tests
	t.Run("serial", func(t *testing.T) {
		tests := []struct {
			name      string
			validator validateFunc
			profile   string
		}{
			{"StartWithMountFirst", validateStartWithMount, profile1},
			{"StartWithMountSecond", validateStartWithMount, profile2},
			{"VerifyMountFirst", validateMount, profile1},
			{"VerifyMountSecond", validateMount, profile2},
			{"DeleteFirst", validateDelete, profile1},
			{"VerifyMountPostDelete", validateMount, profile2},
			{"Stop", validateMountStop, profile2},
			{"RestartStopped", validateRestart, profile2},
			{"VerifyMountPostStop", validateMount, profile2},
		}

		for _, test := range tests {
			if ctx.Err() == context.DeadlineExceeded {
				t.Fatalf("Unable to run more tests (deadline exceeded)")
			}

			t.Run(test.name, func(t *testing.T) {
				test.validator(ctx, t, test.profile)
			})
		}
	})
}

// validateStartWithMount starts a cluster with mount enabled
func validateStartWithMount(ctx context.Context, t *testing.T, profile string) {
	defer PostMortemLogs(t, profile)

	args := []string{"start", "-p", profile, "--memory=2048", "--mount", "--mount-gid", mountGID, "--mount-msize", mountMSize, "--mount-mode", mountMode, "--mount-port", mountPort, "--mount-uid", mountUID}
	args = append(args, StartArgs()...)
	rr, err := Run(t, exec.CommandContext(ctx, Target(), args...))
	if err != nil {
		t.Fatalf("failed to start minikube with args: %q : %v", rr.Command(), err)
	}
}

// validateMount checks if the cluster has a folder mounted
func validateMount(ctx context.Context, t *testing.T, profile string) {
	defer PostMortemLogs(t, profile)

	sshArgs := []string{"-p", profile, "ssh"}

	args := sshArgs
	args = append(args, "ls", "/minikube-host")
	rr, err := Run(t, exec.CommandContext(ctx, Target(), args...))
	if err != nil {
		t.Fatalf("mount failed: %q : %v", rr.Command(), err)
	}

	args = sshArgs
	args = append(args, "stat", "--format", "'%a'", "/minikube-host")
	rr, err = Run(t, exec.CommandContext(ctx, Target(), args...))
	if err != nil {
		t.Fatalf("failed to get directory mode: %v", err)
	}

	if !strings.Contains(rr.Output(), mountMode) {
		t.Errorf("wanted mode to be %q; got: %q", mountMode, rr.Output())
	}

	args = sshArgs
	args = append(args, "mount", "|", "grep", "9p")
	rr, err = Run(t, exec.CommandContext(ctx, Target(), args...))
	if err != nil {
		t.Fatalf("failed to get mount information: %v", err)
	}

	flags := []struct {
		key      string
		expected string
	}{
		{"gid", mountGID},
		{"msize", mountMSize},
		{"port", mountPort},
		{"uid", mountUID},
	}

	for _, flag := range flags {
		want := fmt.Sprintf("%s=%s", flag.key, flag.expected)
		if !strings.Contains(rr.Output(), want) {
			t.Errorf("wanted gid to be: %q; got: %q", want, rr.Output())
		}
	}
}

// validateMountStop stops a cluster
func validateMountStop(ctx context.Context, t *testing.T, profile string) {
	defer PostMortemLogs(t, profile)

	args := []string{"stop", "-p", profile}
	rr, err := Run(t, exec.CommandContext(ctx, Target(), args...))
	if err != nil {
		t.Fatalf("stop failed: %q : %v", rr.Command(), err)
	}
}

// validateRestart restarts a cluster
func validateRestart(ctx context.Context, t *testing.T, profile string) {
	defer PostMortemLogs(t, profile)

	args := []string{"start", "-p", profile}
	rr, err := Run(t, exec.CommandContext(ctx, Target(), args...))
	if err != nil {
		t.Fatalf("restart failed: %q : %v", rr.Command(), err)
	}
}
<|endoftext|>