git subrepo clone (merge) https://github.com/kubernetes-incubator/metrics-server.git metrics-server

subrepo:
  subdir:   "metrics-server"
  merged:   "92d8412"
upstream:
  origin:   "https://github.com/kubernetes-incubator/metrics-server.git"
  branch:   "master"
  commit:   "92d8412"
git-subrepo:
  version:  "0.4.0"
  origin:   "???"
  commit:   "???"
diff --git a/metrics-server/vendor/github.com/emicklei/go-restful-swagger12/swagger_webservice.go b/metrics-server/vendor/github.com/emicklei/go-restful-swagger12/swagger_webservice.go
new file mode 100644
index 0000000..d906231
--- /dev/null
+++ b/metrics-server/vendor/github.com/emicklei/go-restful-swagger12/swagger_webservice.go
@@ -0,0 +1,443 @@
+package swagger
+
+import (
+	"fmt"
+
+	"github.com/emicklei/go-restful"
+	// "github.com/emicklei/hopwatch"
+	"net/http"
+	"reflect"
+	"sort"
+	"strings"
+
+	"github.com/emicklei/go-restful/log"
+)
+
+type SwaggerService struct {
+	config            Config
+	apiDeclarationMap *ApiDeclarationList
+}
+
+func newSwaggerService(config Config) *SwaggerService {
+	sws := &SwaggerService{
+		config:            config,
+		apiDeclarationMap: new(ApiDeclarationList)}
+
+	// Build all ApiDeclarations
+	for _, each := range config.WebServices {
+		rootPath := each.RootPath()
+		// skip the api service itself
+		if rootPath != config.ApiPath {
+			if rootPath == "" || rootPath == "/" {
+				// use routes
+				for _, route := range each.Routes() {
+					entry := staticPathFromRoute(route)
+					_, exists := sws.apiDeclarationMap.At(entry)
+					if !exists {
+						sws.apiDeclarationMap.Put(entry, sws.composeDeclaration(each, entry))
+					}
+				}
+			} else { // use root path
+				sws.apiDeclarationMap.Put(each.RootPath(), sws.composeDeclaration(each, each.RootPath()))
+			}
+		}
+	}
+
+	// if specified then call the PostBuilderHandler
+	if config.PostBuildHandler != nil {
+		config.PostBuildHandler(sws.apiDeclarationMap)
+	}
+	return sws
+}
+
+// LogInfo is the function that is called when this package needs to log. It defaults to log.Printf
+var LogInfo = func(format string, v ...interface{}) {
+	// use the restful package-wide logger
+	log.Printf(format, v...)
+}
+
+// InstallSwaggerService add the WebService that provides the API documentation of all services
+// conform the Swagger documentation specifcation. (https://github.com/wordnik/swagger-core/wiki).
+func InstallSwaggerService(aSwaggerConfig Config) {
+	RegisterSwaggerService(aSwaggerConfig, restful.DefaultContainer)
+}
+
+// RegisterSwaggerService add the WebService that provides the API documentation of all services
+// conform the Swagger documentation specifcation. (https://github.com/wordnik/swagger-core/wiki).
+func RegisterSwaggerService(config Config, wsContainer *restful.Container) {
+	sws := newSwaggerService(config)
+	ws := new(restful.WebService)
+	ws.Path(config.ApiPath)
+	ws.Produces(restful.MIME_JSON)
+	if config.DisableCORS {
+		ws.Filter(enableCORS)
+	}
+	ws.Route(ws.GET("/").To(sws.getListing))
+	ws.Route(ws.GET("/{a}").To(sws.getDeclarations))
+	ws.Route(ws.GET("/{a}/{b}").To(sws.getDeclarations))
+	ws.Route(ws.GET("/{a}/{b}/{c}").To(sws.getDeclarations))
+	ws.Route(ws.GET("/{a}/{b}/{c}/{d}").To(sws.getDeclarations))
+	ws.Route(ws.GET("/{a}/{b}/{c}/{d}/{e}").To(sws.getDeclarations))
+	ws.Route(ws.GET("/{a}/{b}/{c}/{d}/{e}/{f}").To(sws.getDeclarations))
+	ws.Route(ws.GET("/{a}/{b}/{c}/{d}/{e}/{f}/{g}").To(sws.getDeclarations))
+	LogInfo("[restful/swagger] listing is available at %v%v", config.WebServicesUrl, config.ApiPath)
+	wsContainer.Add(ws)
+
+	// Check paths for UI serving
+	if config.StaticHandler == nil && config.SwaggerFilePath != "" && config.SwaggerPath != "" {
+		swaggerPathSlash := config.SwaggerPath
+		// path must end with slash /
+		if "/" != config.SwaggerPath[len(config.SwaggerPath)-1:] {
+			LogInfo("[restful/swagger] use corrected SwaggerPath ; must end with slash (/)")
+			swaggerPathSlash += "/"
+		}
+
+		LogInfo("[restful/swagger] %v%v is mapped to folder %v", config.WebServicesUrl, swaggerPathSlash, config.SwaggerFilePath)
+		wsContainer.Handle(swaggerPathSlash, http.StripPrefix(swaggerPathSlash, http.FileServer(http.Dir(config.SwaggerFilePath))))
+
+		//if we define a custom static handler use it
+	} else if config.StaticHandler != nil && config.SwaggerPath != "" {
+		swaggerPathSlash := config.SwaggerPath
+		// path must end with slash /
+		if "/" != config.SwaggerPath[len(config.SwaggerPath)-1:] {
+			LogInfo("[restful/swagger] use corrected SwaggerFilePath ; must end with slash (/)")
+			swaggerPathSlash += "/"
+
+		}
+		LogInfo("[restful/swagger] %v%v is mapped to custom Handler %T", config.WebServicesUrl, swaggerPathSlash, config.StaticHandler)
+		wsContainer.Handle(swaggerPathSlash, config.StaticHandler)
+
+	} else {
+		LogInfo("[restful/swagger] Swagger(File)Path is empty ; no UI is served")
+	}
+}
+
+func staticPathFromRoute(r restful.Route) string {
+	static := r.Path
+	bracket := strings.Index(static, "{")
+	if bracket <= 1 { // result cannot be empty
+		return static
+	}
+	if bracket != -1 {
+		static = r.Path[:bracket]
+	}
+	if strings.HasSuffix(static, "/") {
+		return static[:len(static)-1]
+	} else {
+		return static
+	}
+}
+
+func enableCORS(req *restful.Request, resp *restful.Response, chain *restful.FilterChain) {
+	if origin := req.HeaderParameter(restful.HEADER_Origin); origin != "" {
+		// prevent duplicate header
+		if len(resp.Header().Get(restful.HEADER_AccessControlAllowOrigin)) == 0 {
+			resp.AddHeader(restful.HEADER_AccessControlAllowOrigin, origin)
+		}
+	}
+	chain.ProcessFilter(req, resp)
+}
+
+func (sws SwaggerService) getListing(req *restful.Request, resp *restful.Response) {
+	listing := sws.produceListing()
+	resp.WriteAsJson(listing)
+}
+
+func (sws SwaggerService) produceListing() ResourceListing {
+	listing := ResourceListing{SwaggerVersion: swaggerVersion, ApiVersion: sws.config.ApiVersion, Info: sws.config.Info}
+	sws.apiDeclarationMap.Do(func(k string, v ApiDeclaration) {
+		ref := Resource{Path: k}
+		if len(v.Apis) > 0 { // use description of first (could still be empty)
+			ref.Description = v.Apis[0].Description
+		}
+		listing.Apis = append(listing.Apis, ref)
+	})
+	return listing
+}
+
+func (sws SwaggerService) getDeclarations(req *restful.Request, resp *restful.Response) {
+	decl, ok := sws.produceDeclarations(composeRootPath(req))
+	if !ok {
+		resp.WriteErrorString(http.StatusNotFound, "ApiDeclaration not found")
+		return
+	}
+	// unless WebServicesUrl is given
+	if len(sws.config.WebServicesUrl) == 0 {
+		// update base path from the actual request
+		// TODO how to detect https? assume http for now
+		var host string
+		// X-Forwarded-Host or Host or Request.Host
+		hostvalues, ok := req.Request.Header["X-Forwarded-Host"] // apache specific?
+		if !ok || len(hostvalues) == 0 {
+			forwarded, ok := req.Request.Header["Host"] // without reverse-proxy
+			if !ok || len(forwarded) == 0 {
+				// fallback to Host field
+				host = req.Request.Host
+			} else {
+				host = forwarded[0]
+			}
+		} else {
+			host = hostvalues[0]
+		}
+		// inspect Referer for the scheme (http vs https)
+		scheme := "http"
+		if referer := req.Request.Header["Referer"]; len(referer) > 0 {
+			if strings.HasPrefix(referer[0], "https") {
+				scheme = "https"
+			}
+		}
+		decl.BasePath = fmt.Sprintf("%s://%s", scheme, host)
+	}
+	resp.WriteAsJson(decl)
+}
+
+func (sws SwaggerService) produceAllDeclarations() map[string]ApiDeclaration {
+	decls := map[string]ApiDeclaration{}
+	sws.apiDeclarationMap.Do(func(k string, v ApiDeclaration) {
+		decls[k] = v
+	})
+	return decls
+}
+
+func (sws SwaggerService) produceDeclarations(route string) (*ApiDeclaration, bool) {
+	decl, ok := sws.apiDeclarationMap.At(route)
+	if !ok {
+		return nil, false
+	}
+	decl.BasePath = sws.config.WebServicesUrl
+	return &decl, true
+}
+
+// composeDeclaration uses all routes and parameters to create a ApiDeclaration
+func (sws SwaggerService) composeDeclaration(ws *restful.WebService, pathPrefix string) ApiDeclaration {
+	decl := ApiDeclaration{
+		SwaggerVersion: swaggerVersion,
+		BasePath:       sws.config.WebServicesUrl,
+		ResourcePath:   pathPrefix,
+		Models:         ModelList{},
+		ApiVersion:     ws.Version()}
+
+	// collect any path parameters
+	rootParams := []Parameter{}
+	for _, param := range ws.PathParameters() {
+		rootParams = append(rootParams, asSwaggerParameter(param.Data()))
+	}
+	// aggregate by path
+	pathToRoutes := newOrderedRouteMap()
+	for _, other := range ws.Routes() {
+		if strings.HasPrefix(other.Path, pathPrefix) {
+			if len(pathPrefix) > 1 && len(other.Path) > len(pathPrefix) && other.Path[len(pathPrefix)] != '/' {
+				continue
+			}
+			pathToRoutes.Add(other.Path, other)
+		}
+	}
+	pathToRoutes.Do(func(path string, routes []restful.Route) {
+		api := Api{Path: strings.TrimSuffix(withoutWildcard(path), "/"), Description: ws.Documentation()}
+		voidString := "void"
+		for _, route := range routes {
+			operation := Operation{
+				Method:  route.Method,
+				Summary: route.Doc,
+				Notes:   route.Notes,
+				// Type gets overwritten if there is a write sample
+				DataTypeFields:   DataTypeFields{Type: &voidString},
+				Parameters:       []Parameter{},
+				Nickname:         route.Operation,
+				ResponseMessages: composeResponseMessages(route, &decl, &sws.config)}
+
+			operation.Consumes = route.Consumes
+			operation.Produces = route.Produces
+
+			// share root params if any
+			for _, swparam := range rootParams {
+				operation.Parameters = append(operation.Parameters, swparam)
+			}
+			// route specific params
+			for _, param := range route.ParameterDocs {
+				operation.Parameters = append(operation.Parameters, asSwaggerParameter(param.Data()))
+			}
+
+			sws.addModelsFromRouteTo(&operation, route, &decl)
+			api.Operations = append(api.Operations, operation)
+		}
+		decl.Apis = append(decl.Apis, api)
+	})
+	return decl
+}
+
+func withoutWildcard(path string) string {
+	if strings.HasSuffix(path, ":*}") {
+		return path[0:len(path)-3] + "}"
+	}
+	return path
+}
+
+// composeResponseMessages takes the ResponseErrors (if any) and creates ResponseMessages from them.
+func composeResponseMessages(route restful.Route, decl *ApiDeclaration, config *Config) (messages []ResponseMessage) {
+	if route.ResponseErrors == nil {
+		return messages
+	}
+	// sort by code
+	codes := sort.IntSlice{}
+	for code := range route.ResponseErrors {
+		codes = append(codes, code)
+	}
+	codes.Sort()
+	for _, code := range codes {
+		each := route.ResponseErrors[code]
+		message := ResponseMessage{
+			Code:    code,
+			Message: each.Message,
+		}
+		if each.Model != nil {
+			st := reflect.TypeOf(each.Model)
+			isCollection, st := detectCollectionType(st)
+			// collection cannot be in responsemodel
+			if !isCollection {
+				modelName := modelBuilder{}.keyFrom(st)
+				modelBuilder{Models: &decl.Models, Config: config}.addModel(st, "")
+				message.ResponseModel = modelName
+			}
+		}
+		messages = append(messages, message)
+	}
+	return
+}
+
+// addModelsFromRoute takes any read or write sample from the Route and creates a Swagger model from it.
+func (sws SwaggerService) addModelsFromRouteTo(operation *Operation, route restful.Route, decl *ApiDeclaration) {
+	if route.ReadSample != nil {
+		sws.addModelFromSampleTo(operation, false, route.ReadSample, &decl.Models)
+	}
+	if route.WriteSample != nil {
+		sws.addModelFromSampleTo(operation, true, route.WriteSample, &decl.Models)
+	}
+}
+
+func detectCollectionType(st reflect.Type) (bool, reflect.Type) {
+	isCollection := false
+	if st.Kind() == reflect.Slice || st.Kind() == reflect.Array {
+		st = st.Elem()
+		isCollection = true
+	} else {
+		if st.Kind() == reflect.Ptr {
+			if st.Elem().Kind() == reflect.Slice || st.Elem().Kind() == reflect.Array {
+				st = st.Elem().Elem()
+				isCollection = true
+			}
+		}
+	}
+	return isCollection, st
+}
+
+// addModelFromSample creates and adds (or overwrites) a Model from a sample resource
+func (sws SwaggerService) addModelFromSampleTo(operation *Operation, isResponse bool, sample interface{}, models *ModelList) {
+	mb := modelBuilder{Models: models, Config: &sws.config}
+	if isResponse {
+		sampleType, items := asDataType(sample, &sws.config)
+		operation.Type = sampleType
+		operation.Items = items
+	}
+	mb.addModelFrom(sample)
+}
+
+func asSwaggerParameter(param restful.ParameterData) Parameter {
+	return Parameter{
+		DataTypeFields: DataTypeFields{
+			Type:         &param.DataType,
+			Format:       asFormat(param.DataType, param.DataFormat),
+			DefaultValue: Special(param.DefaultValue),
+		},
+		Name:        param.Name,
+		Description: param.Description,
+		ParamType:   asParamType(param.Kind),
+
+		Required: param.Required}
+}
+
+// Between 1..7 path parameters is supported
+func composeRootPath(req *restful.Request) string {
+	path := "/" + req.PathParameter("a")
+	b := req.PathParameter("b")
+	if b == "" {
+		return path
+	}
+	path = path + "/" + b
+	c := req.PathParameter("c")
+	if c == "" {
+		return path
+	}
+	path = path + "/" + c
+	d := req.PathParameter("d")
+	if d == "" {
+		return path
+	}
+	path = path + "/" + d
+	e := req.PathParameter("e")
+	if e == "" {
+		return path
+	}
+	path = path + "/" + e
+	f := req.PathParameter("f")
+	if f == "" {
+		return path
+	}
+	path = path + "/" + f
+	g := req.PathParameter("g")
+	if g == "" {
+		return path
+	}
+	return path + "/" + g
+}
+
+func asFormat(dataType string, dataFormat string) string {
+	if dataFormat != "" {
+		return dataFormat
+	}
+	return "" // TODO
+}
+
+func asParamType(kind int) string {
+	switch {
+	case kind == restful.PathParameterKind:
+		return "path"
+	case kind == restful.QueryParameterKind:
+		return "query"
+	case kind == restful.BodyParameterKind:
+		return "body"
+	case kind == restful.HeaderParameterKind:
+		return "header"
+	case kind == restful.FormParameterKind:
+		return "form"
+	}
+	return ""
+}
+
+func asDataType(any interface{}, config *Config) (*string, *Item) {
+	// If it's not a collection, return the suggested model name
+	st := reflect.TypeOf(any)
+	isCollection, st := detectCollectionType(st)
+	modelName := modelBuilder{}.keyFrom(st)
+	// if it's not a collection we are done
+	if !isCollection {
+		return &modelName, nil
+	}
+
+	// XXX: This is not very elegant
+	// We create an Item object referring to the given model
+	models := ModelList{}
+	mb := modelBuilder{Models: &models, Config: config}
+	mb.addModelFrom(any)
+
+	elemTypeName := mb.getElementTypeName(modelName, "", st)
+	item := new(Item)
+	if mb.isPrimitiveType(elemTypeName) {
+		mapped := mb.jsonSchemaType(elemTypeName)
+		item.Type = &mapped
+	} else {
+		item.Ref = &elemTypeName
+	}
+	tmp := "array"
+	return &tmp, item
+}