<commit_before>//Copyright 2011 Siyabonga Dlamini (siyabonga.dlamini@gmail.com). All rights reserved.
//
//Redistribution and use in source and binary forms, with or without
//modification, are permitted provided that the following conditions
//are met:
//
//  1. Redistributions of source code must retain the above copyright
//     notice, this list of conditions and the following disclaimer.
//
//  2. Redistributions in binary form must reproduce the above copyright
//     notice, this list of conditions and the following disclaimer
//     in the documentation and/or other materials provided with the
//     distribution.
//
//THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
//IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
//OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
//IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
//SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
//PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
//OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
//WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
//OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
//ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

package gorest

import (
	"log"
	"reflect"
	"strings"
)

type argumentData struct {
	parameter param
	data      string
}
type param struct {
	positionInPath int
	name           string
	typeName       string
}

var aLLOWED_PAR_TYPES = []string{"string", "int", "bool", "float32", "float64"}

func prepServiceMetaData(root string, tags reflect.StructTag, i interface{}, name string) serviceMetaData {
	md := new(serviceMetaData)

	if tag := tags.Get("root"); tag != "" {
		md.root = tag
	}
	if root != "" {
		md.root = root + md.root
	}
	log.Println("All EndPoints for service [", name, "] , registered under root path: ", md.root)
	if tag := tags.Get("consumes"); tag != "" {
		md.consumesMime = tag
		if GetMarshallerByMime(tag) == nil {
			log.Panic("The Marshaller for mime-type:[" + tag + "], is not registered. Please register this type before registering your service.")
		}
	} else {
		md.consumesMime = Application_Json //Default
	}
	if tag := tags.Get("produces"); tag != "" {
		md.producesMime = tag
		if GetMarshallerByMime(tag) == nil {
			log.Panic("The Marshaller for mime-type:[" + tag + "], is not registered. Please register this type before registering your service.")
		}
	} else {
		md.consumesMime = Application_Json //Default
	}

	if tag := tags.Get("realm"); tag != "" {
		md.realm = tag
		if GetAuthorizer(tag) == nil {
			log.Panic("The realm:[" + tag + "], is not registered. Please register this realm before registering your service.")
		}
	}

	md.template = i
	return *md
}

func makeEndPointStruct(tags reflect.StructTag, serviceRoot string) endPointStruct {

	ms := new(endPointStruct)

	if tag := tags.Get("method"); tag != "" {
		if tag == "GET" {
			ms.requestMethod = GET
		} else if tag == "POST" {
			ms.requestMethod = POST
		} else if tag == "PUT" {
			ms.requestMethod = PUT
		} else if tag == "DELETE" {
			ms.requestMethod = DELETE
		} else if tag == "HEAD" {
			ms.requestMethod = HEAD
		} else if tag == "OPTIONS" {
			ms.requestMethod = OPTIONS
		} else {
			log.Panic("Unknown method type:[" + tag + "] in endpoint declaration. Allowed types {GET,POST,PUT,DELETE,HEAD,OPTIONS}")
		}

		if tag := tags.Get("path"); tag != "" {
			serviceRoot = strings.TrimRight(serviceRoot, "/")
			ms.signiture = serviceRoot + "/" + strings.Trim(tag, "/")
		} else {
			log.Panic("Endpoint declaration must have the tags 'method' and 'path' ")
		}

		if tag := tags.Get("output"); tag != "" {
			ms.outputType = tag
			if strings.HasPrefix(tag, "[]") { //Check for slice/array/list types.
				ms.outputTypeIsArray = true
				ms.outputType = ms.outputType[2:]
			}
			if strings.HasPrefix(tag, "map[") { //Check for map[string]. We only handle string keyed maps!!!

				if ms.outputType[4:10] == "string" {
					ms.outputTypeIsMap = true
					ms.outputType = ms.outputType[11:]
				} else {
					log.Panic("Only string keyed maps e.g( map[string]... ) are allowed on the [output] tag. Endpoint: " + ms.signiture)
				}

			}
		}

		if tag := tags.Get("input"); tag != "" {
			ms.inputMime = tag
		}
		if tag := tags.Get("role"); tag != "" {
			ms.role = tag
		}

		if tag := tags.Get("postdata"); tag != "" {
			ms.postdataType = tag
			if strings.HasPrefix(tag, "[]") { //Check for slice/array/list types.
				ms.postdataTypeIsArray = true
				ms.postdataType = ms.postdataType[2:]
			}
			if strings.HasPrefix(tag, "map[") { //Check for map[string]. We only handle string keyed maps!!!

				if ms.postdataType[4:10] == "string" {
					ms.postdataTypeIsMap = true
					ms.postdataType = ms.postdataType[11:]
				} else {
					log.Panic("Only string keyed maps e.g( map[string]... ) are allowed on the [postdata] tag. Endpoint: " + ms.signiture)
				}

			}
		}

		parseParams(ms)
		return *ms
	}

	log.Panic("Endpoint declaration must have the tags 'method' and 'path' ")
	return *ms //Should not get here

}
func parseParams(e *endPointStruct) {
	e.signiture = strings.Trim(e.signiture, "/")
	e.params = make([]param, 0)
	e.queryParams = make([]param, 0)
	e.nonParamPathPart = make(map[int]string, 0)

	pathPart := e.signiture
	queryPart := ""

	if i := strings.Index(e.signiture, "?"); i != -1 {

		pathPart = e.signiture[:i]
		//e.root = pathPart
		pathPart = strings.TrimRight(pathPart, "/")
		queryPart = e.signiture[i+1:]

		//Extract Query Parameters

		for pos, str1 := range strings.Split(queryPart, "&") {
			if strings.HasPrefix(str1, "{") && strings.HasSuffix(str1, "}") {
				parName, typeName := getVarTypePair(str1, e.signiture)

				for _, par := range e.queryParams {
					if par.name == parName {
						log.Panic("Duplicate Query Parameter name(" + parName + ") in REST path: " + e.signiture)
					}
				}
				//e.queryParams[len(e.queryParams)] = param{pos, parName, typeName}
				e.queryParams = append(e.queryParams, param{pos, parName, typeName})
			} else {
				log.Panic("Please check that your Query Parameters are configured correctly for endpoint: " + e.signiture)
			}
		}
	}

	if i := strings.Index(pathPart, "{"); i != -1 {
		e.root = pathPart[:i]
	} else {
		e.root = pathPart
	}

	//Extract Path Parameters
	for pos, str1 := range strings.Split(pathPart, "/") {
		e.signitureLen++

		if strings.HasPrefix(str1, "{") && strings.HasSuffix(str1, "}") { //This just ensures we re dealing with a varibale not normal path.

			parName, typeName := getVarTypePair(str1, e.signiture)

			if parName == "..." {
				e.isVariableLength = true
				parName, typeName := getVarTypePair(str1, e.signiture)
				e.params = append(e.params, param{pos, parName, typeName})
				e.paramLen++
				break
			}
			for _, par := range e.params {
				if par.name == parName {
					log.Panic("Duplicate Path Parameter name(" + parName + ") in REST path: " + e.signiture)
				}
			}

			e.params = append(e.params, param{pos, parName, typeName})
			e.paramLen++
		} else {
			e.nonParamPathPart[pos] = str1

		}
	}

	e.root = strings.TrimRight(e.root, "/")

	if e.isVariableLength && e.paramLen > 1 {
		log.Panic("Variable length endpoints can only have one parameter declaration: " + pathPart)
	}

	for key, ep := range _manager().endpoints {
		if ep.root == e.root && ep.signitureLen == e.signitureLen && ep.requestMethod == e.requestMethod {
			log.Panic("Can not register two endpoints with same request-method(" + ep.requestMethod + "), same root and same amount of parameters: " + e.signiture + " VS " + ep.signiture)
		}
		if ep.requestMethod == e.requestMethod && pathPart == key {
			log.Panic("Endpoint already registered: " + pathPart)
		}
		if e.isVariableLength && (strings.Index(ep.root+"/", e.root+"/") == 0 || strings.Index(e.root+"/", ep.root+"/") == 0) && ep.requestMethod == e.requestMethod {
			log.Panic("Variable length endpoints can only be mounted on a unique root. Root already used: " + ep.root + " <> " + e.root)
		}
	}
}

func getVarTypePair(part string, sign string) (parName string, typeName string) {

	temp := strings.Trim(part, "{}")
	ind := 0
	if ind = strings.Index(temp, ":"); ind == -1 {
		log.Panic("Please ensure that parameter names(" + temp + ") have associated types in REST path: " + sign)
	}
	parName = temp[:ind]
	typeName = temp[ind+1:]

	if !isAllowedParamType(typeName) {
		log.Panic("Type " + typeName + " is not allowed for Path/Query-parameters in REST path: " + sign)
	}

	return
}

func isAllowedParamType(typeName string) bool {
	for _, s := range aLLOWED_PAR_TYPES {
		if s == strings.ToLower(typeName) {
			return true
		}
	}
	return false
}

func getEndPointByUrl(method string, url string) (endPointStruct, map[string]string, map[string]string, string, bool) {
	//println("Getting:",url)

	pathPart := url
	queryPart := ""

	if i := strings.Index(url, "?"); i != -1 {
		pathPart = url[:i]
		queryPart = url[i+1:]
	}

	pathPart = strings.Trim(pathPart, "/")
	totalParts := strings.Count(pathPart, "/")
	totalParts++

	epRet := new(endPointStruct)
	pathArgs := make(map[string]string, 0)
	queryArgs := make(map[string]string, 0)

	var ep *endPointStruct
	for _, loopEp := range _manager().endpoints {
		//		println(method, ":", loopEp.requestMethod, pathPart, ":", loopEp.root, totalParts, ":", loopEp.signitureLen, "Variable?", loopEp.isVariableLength)
		if loopEp.isVariableLength && (strings.Index(pathPart+"/", loopEp.root+"/") == 0) && loopEp.requestMethod == method {
			ep = &loopEp
			varsPart := strings.Trim(pathPart[len(loopEp.root):], "/")
			//			println("::::::::::::::::Root", pathPart, ">>>>>>>Vars", varsPart)
			for upos, str1 := range strings.Split(varsPart, "/") {
				pathArgs[string(upos)] = strings.Trim(str1, " ")
			}
		} else if (strings.Index(pathPart+"/", loopEp.root+"/") == 0) && loopEp.signitureLen == totalParts && loopEp.requestMethod == method {
			ep = &loopEp
			//We first make sure that the other parts of the path that are not parameters do actully match with the signature.
			//If not we exit. We do not have to cary on looking since we only allow one registration per root and length.
			for pos, name := range ep.nonParamPathPart {
				for upos, str1 := range strings.Split(pathPart, "/") {
					if upos == pos {
						if name != str1 {
							log.Println("Not found:", pathPart)
							return *epRet, pathArgs, queryArgs, "", false //Path not found
						}
						break
					}
				}
			}
			//Extract Path Arguments
			for _, par := range ep.params {
				for upos, str1 := range strings.Split(pathPart, "/") {

					if par.positionInPath == upos {
						pathArgs[par.name] = strings.Trim(str1, " ")
						break
					}
				}
			}
		}

		if ep != nil {
			xsrft := ""
			//Extract Query Arguments: These are optional in the query, so some or all of them might not be there.
			//Also, if they are there, they do not have to be in the same order they were sepcified in on the declaration signature.
			for _, str1 := range strings.Split(queryPart, "&") {
				if i := strings.Index(str1, "="); i != -1 {
					pName := str1[:i]
					dataString := str1[i+1:]
					if pName == XSXRF_PARAM_NAME {
						xsrft = strings.Trim(dataString, " ")
						log.Print("Session Id:", xsrft)
					} else {
						for _, par := range ep.queryParams {
							if par.name == pName {
								queryArgs[pName] = strings.Trim(dataString, " ")
								break
							}
						}
					}

				}
			}

			return *ep, pathArgs, queryArgs, xsrft, true //Path found
		}
	}

	return *epRet, pathArgs, queryArgs, "", false //Path not found
}
<commit_msg>Add  int32 and int64<commit_after>//Copyright 2011 Siyabonga Dlamini (siyabonga.dlamini@gmail.com). All rights reserved.
//
//Redistribution and use in source and binary forms, with or without
//modification, are permitted provided that the following conditions
//are met:
//
//  1. Redistributions of source code must retain the above copyright
//     notice, this list of conditions and the following disclaimer.
//
//  2. Redistributions in binary form must reproduce the above copyright
//     notice, this list of conditions and the following disclaimer
//     in the documentation and/or other materials provided with the
//     distribution.
//
//THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
//IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
//OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
//IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
//SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
//PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
//OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
//WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
//OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
//ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

package gorest

import (
	"log"
	"reflect"
	"strings"
)

type argumentData struct {
	parameter param
	data      string
}
type param struct {
	positionInPath int
	name           string
	typeName       string
}

var aLLOWED_PAR_TYPES = []string{"string", "int", "int32", "int64", "bool", "float32", "float64"}

func prepServiceMetaData(root string, tags reflect.StructTag, i interface{}, name string) serviceMetaData {
	md := new(serviceMetaData)

	if tag := tags.Get("root"); tag != "" {
		md.root = tag
	}
	if root != "" {
		md.root = root + md.root
	}
	log.Println("All EndPoints for service [", name, "] , registered under root path: ", md.root)
	if tag := tags.Get("consumes"); tag != "" {
		md.consumesMime = tag
		if GetMarshallerByMime(tag) == nil {
			log.Panic("The Marshaller for mime-type:[" + tag + "], is not registered. Please register this type before registering your service.")
		}
	} else {
		md.consumesMime = Application_Json //Default
	}
	if tag := tags.Get("produces"); tag != "" {
		md.producesMime = tag
		if GetMarshallerByMime(tag) == nil {
			log.Panic("The Marshaller for mime-type:[" + tag + "], is not registered. Please register this type before registering your service.")
		}
	} else {
		md.consumesMime = Application_Json //Default
	}

	if tag := tags.Get("realm"); tag != "" {
		md.realm = tag
		if GetAuthorizer(tag) == nil {
			log.Panic("The realm:[" + tag + "], is not registered. Please register this realm before registering your service.")
		}
	}

	md.template = i
	return *md
}

func makeEndPointStruct(tags reflect.StructTag, serviceRoot string) endPointStruct {

	ms := new(endPointStruct)

	if tag := tags.Get("method"); tag != "" {
		if tag == "GET" {
			ms.requestMethod = GET
		} else if tag == "POST" {
			ms.requestMethod = POST
		} else if tag == "PUT" {
			ms.requestMethod = PUT
		} else if tag == "DELETE" {
			ms.requestMethod = DELETE
		} else if tag == "HEAD" {
			ms.requestMethod = HEAD
		} else if tag == "OPTIONS" {
			ms.requestMethod = OPTIONS
		} else {
			log.Panic("Unknown method type:[" + tag + "] in endpoint declaration. Allowed types {GET,POST,PUT,DELETE,HEAD,OPTIONS}")
		}

		if tag := tags.Get("path"); tag != "" {
			serviceRoot = strings.TrimRight(serviceRoot, "/")
			ms.signiture = serviceRoot + "/" + strings.Trim(tag, "/")
		} else {
			log.Panic("Endpoint declaration must have the tags 'method' and 'path' ")
		}

		if tag := tags.Get("output"); tag != "" {
			ms.outputType = tag
			if strings.HasPrefix(tag, "[]") { //Check for slice/array/list types.
				ms.outputTypeIsArray = true
				ms.outputType = ms.outputType[2:]
			}
			if strings.HasPrefix(tag, "map[") { //Check for map[string]. We only handle string keyed maps!!!

				if ms.outputType[4:10] == "string" {
					ms.outputTypeIsMap = true
					ms.outputType = ms.outputType[11:]
				} else {
					log.Panic("Only string keyed maps e.g( map[string]... ) are allowed on the [output] tag. Endpoint: " + ms.signiture)
				}

			}
		}

		if tag := tags.Get("input"); tag != "" {
			ms.inputMime = tag
		}
		if tag := tags.Get("role"); tag != "" {
			ms.role = tag
		}

		if tag := tags.Get("postdata"); tag != "" {
			ms.postdataType = tag
			if strings.HasPrefix(tag, "[]") { //Check for slice/array/list types.
				ms.postdataTypeIsArray = true
				ms.postdataType = ms.postdataType[2:]
			}
			if strings.HasPrefix(tag, "map[") { //Check for map[string]. We only handle string keyed maps!!!

				if ms.postdataType[4:10] == "string" {
					ms.postdataTypeIsMap = true
					ms.postdataType = ms.postdataType[11:]
				} else {
					log.Panic("Only string keyed maps e.g( map[string]... ) are allowed on the [postdata] tag. Endpoint: " + ms.signiture)
				}

			}
		}

		parseParams(ms)
		return *ms
	}

	log.Panic("Endpoint declaration must have the tags 'method' and 'path' ")
	return *ms //Should not get here

}
func parseParams(e *endPointStruct) {
	e.signiture = strings.Trim(e.signiture, "/")
	e.params = make([]param, 0)
	e.queryParams = make([]param, 0)
	e.nonParamPathPart = make(map[int]string, 0)

	pathPart := e.signiture
	queryPart := ""

	if i := strings.Index(e.signiture, "?"); i != -1 {

		pathPart = e.signiture[:i]
		//e.root = pathPart
		pathPart = strings.TrimRight(pathPart, "/")
		queryPart = e.signiture[i+1:]

		//Extract Query Parameters

		for pos, str1 := range strings.Split(queryPart, "&") {
			if strings.HasPrefix(str1, "{") && strings.HasSuffix(str1, "}") {
				parName, typeName := getVarTypePair(str1, e.signiture)

				for _, par := range e.queryParams {
					if par.name == parName {
						log.Panic("Duplicate Query Parameter name(" + parName + ") in REST path: " + e.signiture)
					}
				}
				//e.queryParams[len(e.queryParams)] = param{pos, parName, typeName}
				e.queryParams = append(e.queryParams, param{pos, parName, typeName})
			} else {
				log.Panic("Please check that your Query Parameters are configured correctly for endpoint: " + e.signiture)
			}
		}
	}

	if i := strings.Index(pathPart, "{"); i != -1 {
		e.root = pathPart[:i]
	} else {
		e.root = pathPart
	}

	//Extract Path Parameters
	for pos, str1 := range strings.Split(pathPart, "/") {
		e.signitureLen++

		if strings.HasPrefix(str1, "{") && strings.HasSuffix(str1, "}") { //This just ensures we re dealing with a varibale not normal path.

			parName, typeName := getVarTypePair(str1, e.signiture)

			if parName == "..." {
				e.isVariableLength = true
				parName, typeName := getVarTypePair(str1, e.signiture)
				e.params = append(e.params, param{pos, parName, typeName})
				e.paramLen++
				break
			}
			for _, par := range e.params {
				if par.name == parName {
					log.Panic("Duplicate Path Parameter name(" + parName + ") in REST path: " + e.signiture)
				}
			}

			e.params = append(e.params, param{pos, parName, typeName})
			e.paramLen++
		} else {
			e.nonParamPathPart[pos] = str1

		}
	}

	e.root = strings.TrimRight(e.root, "/")

	if e.isVariableLength && e.paramLen > 1 {
		log.Panic("Variable length endpoints can only have one parameter declaration: " + pathPart)
	}

	for key, ep := range _manager().endpoints {
		if ep.root == e.root && ep.signitureLen == e.signitureLen && ep.requestMethod == e.requestMethod {
			log.Panic("Can not register two endpoints with same request-method(" + ep.requestMethod + "), same root and same amount of parameters: " + e.signiture + " VS " + ep.signiture)
		}
		if ep.requestMethod == e.requestMethod && pathPart == key {
			log.Panic("Endpoint already registered: " + pathPart)
		}
		if e.isVariableLength && (strings.Index(ep.root+"/", e.root+"/") == 0 || strings.Index(e.root+"/", ep.root+"/") == 0) && ep.requestMethod == e.requestMethod {
			log.Panic("Variable length endpoints can only be mounted on a unique root. Root already used: " + ep.root + " <> " + e.root)
		}
	}
}

func getVarTypePair(part string, sign string) (parName string, typeName string) {

	temp := strings.Trim(part, "{}")
	ind := 0
	if ind = strings.Index(temp, ":"); ind == -1 {
		log.Panic("Please ensure that parameter names(" + temp + ") have associated types in REST path: " + sign)
	}
	parName = temp[:ind]
	typeName = temp[ind+1:]

	if !isAllowedParamType(typeName) {
		log.Panic("Type " + typeName + " is not allowed for Path/Query-parameters in REST path: " + sign)
	}

	return
}

func isAllowedParamType(typeName string) bool {
	for _, s := range aLLOWED_PAR_TYPES {
		if s == strings.ToLower(typeName) {
			return true
		}
	}
	return false
}

func getEndPointByUrl(method string, url string) (endPointStruct, map[string]string, map[string]string, string, bool) {
	//println("Getting:",url)

	pathPart := url
	queryPart := ""

	if i := strings.Index(url, "?"); i != -1 {
		pathPart = url[:i]
		queryPart = url[i+1:]
	}

	pathPart = strings.Trim(pathPart, "/")
	totalParts := strings.Count(pathPart, "/")
	totalParts++

	epRet := new(endPointStruct)
	pathArgs := make(map[string]string, 0)
	queryArgs := make(map[string]string, 0)

	var ep *endPointStruct
	for _, loopEp := range _manager().endpoints {
		//		println(method, ":", loopEp.requestMethod, pathPart, ":", loopEp.root, totalParts, ":", loopEp.signitureLen, "Variable?", loopEp.isVariableLength)
		if loopEp.isVariableLength && (strings.Index(pathPart+"/", loopEp.root+"/") == 0) && loopEp.requestMethod == method {
			ep = &loopEp
			varsPart := strings.Trim(pathPart[len(loopEp.root):], "/")
			//			println("::::::::::::::::Root", pathPart, ">>>>>>>Vars", varsPart)
			for upos, str1 := range strings.Split(varsPart, "/") {
				pathArgs[string(upos)] = strings.Trim(str1, " ")
			}
		} else if (strings.Index(pathPart+"/", loopEp.root+"/") == 0) && loopEp.signitureLen == totalParts && loopEp.requestMethod == method {
			ep = &loopEp
			//We first make sure that the other parts of the path that are not parameters do actully match with the signature.
			//If not we exit. We do not have to cary on looking since we only allow one registration per root and length.
			for pos, name := range ep.nonParamPathPart {
				for upos, str1 := range strings.Split(pathPart, "/") {
					if upos == pos {
						if name != str1 {
							log.Println("Not found:", pathPart)
							return *epRet, pathArgs, queryArgs, "", false //Path not found
						}
						break
					}
				}
			}
			//Extract Path Arguments
			for _, par := range ep.params {
				for upos, str1 := range strings.Split(pathPart, "/") {

					if par.positionInPath == upos {
						pathArgs[par.name] = strings.Trim(str1, " ")
						break
					}
				}
			}
		}

		if ep != nil {
			xsrft := ""
			//Extract Query Arguments: These are optional in the query, so some or all of them might not be there.
			//Also, if they are there, they do not have to be in the same order they were sepcified in on the declaration signature.
			for _, str1 := range strings.Split(queryPart, "&") {
				if i := strings.Index(str1, "="); i != -1 {
					pName := str1[:i]
					dataString := str1[i+1:]
					if pName == XSXRF_PARAM_NAME {
						xsrft = strings.Trim(dataString, " ")
						log.Print("Session Id:", xsrft)
					} else {
						for _, par := range ep.queryParams {
							if par.name == pName {
								queryArgs[pName] = strings.Trim(dataString, " ")
								break
							}
						}
					}

				}
			}

			return *ep, pathArgs, queryArgs, xsrft, true //Path found
		}
	}

	return *epRet, pathArgs, queryArgs, "", false //Path not found
}
<|endoftext|>


<commit_before>// Copyright 2015 LinkedIn Corp. 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.Copyright [201X] LinkedIn Corp. 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.

package main

import (
	"github.com/linkedin/goavro"
	"log"
	"os"
)

func main() {
	recordSchema := `
{
  "type": "record",
  "name": "comments",
  "doc:": "A basic schema for storing blog comments",
  "namespace": "com.example",
  "fields": [
    {
      "doc": "Name of user",
      "type": "string",
      "name": "username"
    },
    {
      "doc": "The content of the user's message",
      "type": "string",
      "name": "comment"
    },
    {
      "doc": "Unix epoch time in milliseconds",
      "type": "long",
      "name": "timestamp"
    }
  ]
}
`
	fh, err := os.Create("test.avro")
	if err != nil {
		log.Fatal("cannot create file: ", err)
	}
	defer fh.Close()
	fw, err := goavro.NewWriter(
		goavro.BlockSize(2),
		// goavro.Compression(goavro.CompressionDeflate),
		// goavro.Compression(goavro.CompressionSnappy),
		goavro.WriterSchema(recordSchema),
		goavro.ToWriter(fh))
	if err != nil {
		log.Fatal("cannot create Writer: ", err)
	}
	defer fw.Close()

	// create a record that matches the schema we want to encode
	someRecord, err := goavro.NewRecord(goavro.RecordSchema(recordSchema))
	if err != nil {
		log.Fatal(err)
	}
	// identify field name to set datum for
	someRecord.Set("username", "Aquaman")
	someRecord.Set("comment", "The Atlantic is oddly cold this morning!")
	// you can fully qualify the field name
	someRecord.Set("com.example.timestamp", int64(1082196484))
	fw.Write(someRecord)

	// create another record
	someRecord, err = goavro.NewRecord(goavro.RecordSchema(recordSchema))
	if err != nil {
		log.Fatal(err)
	}
	someRecord.Set("username", "Batman")
	someRecord.Set("comment", "Who are all of these crazies?")
	someRecord.Set("com.example.timestamp", int64(1427383430))
	fw.Write(someRecord)
}
<commit_msg>writer example does not have snappy reference<commit_after>// Copyright 2015 LinkedIn Corp. 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.Copyright [201X] LinkedIn Corp. 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.

package main

import (
	"github.com/linkedin/goavro"
	"log"
	"os"
)

func main() {
	recordSchema := `
{
  "type": "record",
  "name": "comments",
  "doc:": "A basic schema for storing blog comments",
  "namespace": "com.example",
  "fields": [
    {
      "doc": "Name of user",
      "type": "string",
      "name": "username"
    },
    {
      "doc": "The content of the user's message",
      "type": "string",
      "name": "comment"
    },
    {
      "doc": "Unix epoch time in milliseconds",
      "type": "long",
      "name": "timestamp"
    }
  ]
}
`
	fh, err := os.Create("test.avro")
	if err != nil {
		log.Fatal("cannot create file: ", err)
	}
	defer fh.Close()
	fw, err := goavro.NewWriter(
		goavro.BlockSize(2),
		goavro.Compression(goavro.CompressionDeflate),
		goavro.WriterSchema(recordSchema),
		goavro.ToWriter(fh))
	if err != nil {
		log.Fatal("cannot create Writer: ", err)
	}
	defer fw.Close()

	// create a record that matches the schema we want to encode
	someRecord, err := goavro.NewRecord(goavro.RecordSchema(recordSchema))
	if err != nil {
		log.Fatal(err)
	}
	// identify field name to set datum for
	someRecord.Set("username", "Aquaman")
	someRecord.Set("comment", "The Atlantic is oddly cold this morning!")
	// you can fully qualify the field name
	someRecord.Set("com.example.timestamp", int64(1082196484))
	fw.Write(someRecord)

	// create another record
	someRecord, err = goavro.NewRecord(goavro.RecordSchema(recordSchema))
	if err != nil {
		log.Fatal(err)
	}
	someRecord.Set("username", "Batman")
	someRecord.Set("comment", "Who are all of these crazies?")
	someRecord.Set("com.example.timestamp", int64(1427383430))
	fw.Write(someRecord)
}
<|endoftext|>


<commit_before>package http

import (
	"net/http"
	"strings"

	"golang.org/x/net/context"

	"github.com/travis-ci/cloud-brain/cbcontext"
	"github.com/travis-ci/cloud-brain/cloudbrain"
)

func handleInstances(ctx context.Context, core *cloudbrain.Core) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		ctx = cbcontext.FromRequestID(ctx, r.Header.Get("X-Request-ID"))

		switch r.Method {
		case "GET":
			handleInstancesGet(ctx, core, w, r)
		case "POST":
			handleInstancesPost(ctx, core, w, r)
		default:
			respondError(w, http.StatusMethodNotAllowed, nil)
		}
	})
}

func handleInstancesGet(ctx context.Context, core *cloudbrain.Core, w http.ResponseWriter, r *http.Request) {
	// Determine the path...
	prefix := "/instances/"
	if !strings.HasPrefix(r.URL.Path, prefix) {
		respondError(w, http.StatusNotFound, nil)
		return
	}
	path := r.URL.Path[len(prefix):]
	if path == "" {
		respondError(w, http.StatusNotFound, nil)
		return
	}

	instance, err := core.GetInstance(ctx, path)
	if err != nil {
		// TODO(henrikhodne): Log error
		respondError(w, http.StatusInternalServerError, nil)
		return
	}
	if instance == nil {
		respondError(w, http.StatusNotFound, nil)
		return
	}

	respondOk(w, instanceToResponse(instance))
}

func handleInstancesPost(ctx context.Context, core *cloudbrain.Core, w http.ResponseWriter, r *http.Request) {
	var req CreateInstanceRequest

	if err := parseRequest(r, &req); err != nil {
		respondError(w, http.StatusBadRequest, err)
		return
	}

	instance, err := core.CreateInstance(ctx, req.Provider, cloudbrain.CreateInstanceAttributes{
		ImageName:    req.Image,
		InstanceType: req.InstanceType,
		PublicSSHKey: req.PublicSSHKey,
	})
	if err != nil {
		respondError(w, http.StatusInternalServerError, err)
		return
	}

	respondOk(w, instanceToResponse(instance))
}

func instanceToResponse(instance *cloudbrain.Instance) *InstanceResponse {
	body := &InstanceResponse{
		ID:           instance.ID,
		ProviderName: instance.ProviderName,
		Image:        instance.Image,
		State:        instance.State,
	}
	if instance.IPAddress != "" {
		body.IPAddress = &instance.IPAddress
	}

	return body
}

// An InstanceResponse is returned by the HTTP API that contains information
// about an instance.
type InstanceResponse struct {
	ID           string  `json:"id"`
	ProviderName string  `json:"provider"`
	Image        string  `json:"image"`
	IPAddress    *string `json:"ip_address"`
	State        string  `json:"state"`
}

// CreateInstanceRequest contains the data in the request body for a create
// instance request.
type CreateInstanceRequest struct {
	Provider     string `json:"provider"`
	Image        string `json:"image"`
	InstanceType string `json:"instance_type"`
	PublicSSHKey string `json:"public_ssh_key"`
}
<commit_msg>Log error when unable to get instance<commit_after>package http

import (
	"net/http"
	"strings"

	"golang.org/x/net/context"

	"github.com/travis-ci/cloud-brain/cbcontext"
	"github.com/travis-ci/cloud-brain/cloudbrain"
)

func handleInstances(ctx context.Context, core *cloudbrain.Core) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		ctx = cbcontext.FromRequestID(ctx, r.Header.Get("X-Request-ID"))

		switch r.Method {
		case "GET":
			handleInstancesGet(ctx, core, w, r)
		case "POST":
			handleInstancesPost(ctx, core, w, r)
		default:
			respondError(w, http.StatusMethodNotAllowed, nil)
		}
	})
}

func handleInstancesGet(ctx context.Context, core *cloudbrain.Core, w http.ResponseWriter, r *http.Request) {
	// Determine the path...
	prefix := "/instances/"
	if !strings.HasPrefix(r.URL.Path, prefix) {
		respondError(w, http.StatusNotFound, nil)
		return
	}
	path := r.URL.Path[len(prefix):]
	if path == "" {
		respondError(w, http.StatusNotFound, nil)
		return
	}

	instance, err := core.GetInstance(ctx, path)
	if err != nil {
		cbcontext.LoggerFromContext(ctx).WithField("err", err).Error("couldn't get instance")
		respondError(w, http.StatusInternalServerError, nil)
		return
	}
	if instance == nil {
		respondError(w, http.StatusNotFound, nil)
		return
	}

	respondOk(w, instanceToResponse(instance))
}

func handleInstancesPost(ctx context.Context, core *cloudbrain.Core, w http.ResponseWriter, r *http.Request) {
	var req CreateInstanceRequest

	if err := parseRequest(r, &req); err != nil {
		respondError(w, http.StatusBadRequest, err)
		return
	}

	instance, err := core.CreateInstance(ctx, req.Provider, cloudbrain.CreateInstanceAttributes{
		ImageName:    req.Image,
		InstanceType: req.InstanceType,
		PublicSSHKey: req.PublicSSHKey,
	})
	if err != nil {
		respondError(w, http.StatusInternalServerError, err)
		return
	}

	respondOk(w, instanceToResponse(instance))
}

func instanceToResponse(instance *cloudbrain.Instance) *InstanceResponse {
	body := &InstanceResponse{
		ID:           instance.ID,
		ProviderName: instance.ProviderName,
		Image:        instance.Image,
		State:        instance.State,
	}
	if instance.IPAddress != "" {
		body.IPAddress = &instance.IPAddress
	}

	return body
}

// An InstanceResponse is returned by the HTTP API that contains information
// about an instance.
type InstanceResponse struct {
	ID           string  `json:"id"`
	ProviderName string  `json:"provider"`
	Image        string  `json:"image"`
	IPAddress    *string `json:"ip_address"`
	State        string  `json:"state"`
}

// CreateInstanceRequest contains the data in the request body for a create
// instance request.
type CreateInstanceRequest struct {
	Provider     string `json:"provider"`
	Image        string `json:"image"`
	InstanceType string `json:"instance_type"`
	PublicSSHKey string `json:"public_ssh_key"`
}
<|endoftext|>


<commit_before>/*
Copyright 2012 Google Inc.

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

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

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

// peers.go defines how processes find and communicate with their peers.

package groupcache

import (
	"context"

	pb "github.com/golang/groupcache/groupcachepb"
)

// ProtoGetter is the interface that must be implemented by a peer.
type ProtoGetter interface {
	Get(ctx context.Context, in *pb.GetRequest, out *pb.GetResponse) error
}

// PeerPicker is the interface that must be implemented to locate
// the peer that owns a specific key.
type PeerPicker interface {
	// PickPeer returns the peer that owns the specific key
	// and true to indicate that a remote peer was nominated.
	// It returns nil, false if the key owner is the current peer.
	PickPeer(key string) (peer ProtoGetter, ok bool)
}

// NoPeers is an implementation of PeerPicker that never finds a peer.
type NoPeers struct{}

func (NoPeers) PickPeer(key string) (peer ProtoGetter, ok bool) { return }

var (
	portPicker func(groupName string) PeerPicker
)

// RegisterPeerPicker registers the peer initialization function.
// It is called once, when the first group is created.
// Either RegisterPeerPicker or RegisterPerGroupPeerPicker should be
// called exactly once, but not both.
func RegisterPeerPicker(fn func() PeerPicker) {
	if portPicker != nil {
		panic("RegisterPeerPicker called more than once")
	}
	portPicker = func(_ string) PeerPicker { return fn() }
}

// RegisterPerGroupPeerPicker registers the peer initialization function,
// which takes the groupName, to be used in choosing a PeerPicker.
// It is called once, when the first group is created.
// Either RegisterPeerPicker or RegisterPerGroupPeerPicker should be
// called exactly once, but not both.
func RegisterPerGroupPeerPicker(fn func(groupName string) PeerPicker) {
	if portPicker != nil {
		panic("RegisterPeerPicker called more than once")
	}
	portPicker = fn
}

func getPeers(groupName string) PeerPicker {
	if portPicker == nil {
		return NoPeers{}
	}
	pk := portPicker(groupName)
	if pk == nil {
		pk = NoPeers{}
	}
	return pk
}
<commit_msg>add back Context type as alias to context.Context (#148)<commit_after>/*
Copyright 2012 Google Inc.

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

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

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

// peers.go defines how processes find and communicate with their peers.

package groupcache

import (
	"context"

	pb "github.com/golang/groupcache/groupcachepb"
)

// Context is an alias to context.Context for backwards compatibility purposes.
type Context = context.Context

// ProtoGetter is the interface that must be implemented by a peer.
type ProtoGetter interface {
	Get(ctx context.Context, in *pb.GetRequest, out *pb.GetResponse) error
}

// PeerPicker is the interface that must be implemented to locate
// the peer that owns a specific key.
type PeerPicker interface {
	// PickPeer returns the peer that owns the specific key
	// and true to indicate that a remote peer was nominated.
	// It returns nil, false if the key owner is the current peer.
	PickPeer(key string) (peer ProtoGetter, ok bool)
}

// NoPeers is an implementation of PeerPicker that never finds a peer.
type NoPeers struct{}

func (NoPeers) PickPeer(key string) (peer ProtoGetter, ok bool) { return }

var (
	portPicker func(groupName string) PeerPicker
)

// RegisterPeerPicker registers the peer initialization function.
// It is called once, when the first group is created.
// Either RegisterPeerPicker or RegisterPerGroupPeerPicker should be
// called exactly once, but not both.
func RegisterPeerPicker(fn func() PeerPicker) {
	if portPicker != nil {
		panic("RegisterPeerPicker called more than once")
	}
	portPicker = func(_ string) PeerPicker { return fn() }
}

// RegisterPerGroupPeerPicker registers the peer initialization function,
// which takes the groupName, to be used in choosing a PeerPicker.
// It is called once, when the first group is created.
// Either RegisterPeerPicker or RegisterPerGroupPeerPicker should be
// called exactly once, but not both.
func RegisterPerGroupPeerPicker(fn func(groupName string) PeerPicker) {
	if portPicker != nil {
		panic("RegisterPeerPicker called more than once")
	}
	portPicker = fn
}

func getPeers(groupName string) PeerPicker {
	if portPicker == nil {
		return NoPeers{}
	}
	pk := portPicker(groupName)
	if pk == nil {
		pk = NoPeers{}
	}
	return pk
}
<|endoftext|>


<commit_before>//go:build linux

package docker

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

	"github.com/hashicorp/nomad/ci"
	"github.com/hashicorp/nomad/client/testutil"
	"github.com/hashicorp/nomad/helper"
	"github.com/hashicorp/nomad/helper/freeport"
	tu "github.com/hashicorp/nomad/testutil"
	"github.com/stretchr/testify/require"
)

func TestDockerDriver_authFromHelper(t *testing.T) {
	ci.Parallel(t)
	testutil.DockerCompatible(t)

	dir := t.TempDir()
	helperPayload := "{\"Username\":\"hashi\",\"Secret\":\"nomad\"}"
	helperContent := []byte(fmt.Sprintf("#!/bin/sh\ncat > %s/helper-$1.out;echo '%s'", dir, helperPayload))

	helperFile := filepath.Join(dir, "docker-credential-testnomad")
	err := ioutil.WriteFile(helperFile, helperContent, 0777)
	require.NoError(t, err)

	path := os.Getenv("PATH")
	t.Setenv("PATH", fmt.Sprintf("%s:%s", path, dir))

	authHelper := authFromHelper("testnomad")
	creds, err := authHelper("registry.local:5000/repo/image")
	require.NoError(t, err)
	require.NotNil(t, creds)
	require.Equal(t, "hashi", creds.Username)
	require.Equal(t, "nomad", creds.Password)

	if _, err := os.Stat(filepath.Join(dir, "helper-get.out")); os.IsNotExist(err) {
		t.Fatalf("Expected helper-get.out to exist")
	}
	content, err := ioutil.ReadFile(filepath.Join(dir, "helper-get.out"))
	require.NoError(t, err)
	require.Equal(t, "registry.local:5000", string(content))
}

func TestDockerDriver_PluginConfig_PidsLimit(t *testing.T) {
	ci.Parallel(t)
	testutil.DockerCompatible(t)

	dh := dockerDriverHarness(t, nil)
	driver := dh.Impl().(*Driver)
	driver.config.PidsLimit = 5

	task, cfg, ports := dockerTask(t)
	defer freeport.Return(ports)
	require.NoError(t, task.EncodeConcreteDriverConfig(cfg))

	cfg.PidsLimit = 7
	_, err := driver.createContainerConfig(task, cfg, "org/repo:0.1")
	require.Error(t, err)
	require.Contains(t, err.Error(), `pids_limit cannot be greater than nomad plugin config pids_limit`)

	// Task PidsLimit should override plugin PidsLimit.
	cfg.PidsLimit = 3
	opts, err := driver.createContainerConfig(task, cfg, "org/repo:0.1")
	require.NoError(t, err)
	require.Equal(t, helper.Int64ToPtr(3), opts.HostConfig.PidsLimit)
}

func TestDockerDriver_PidsLimit(t *testing.T) {
	ci.Parallel(t)
	testutil.DockerCompatible(t)

	task, cfg, ports := dockerTask(t)
	defer freeport.Return(ports)

	cfg.PidsLimit = 1
	cfg.Command = "/bin/sh"
	cfg.Args = []string{"-c", "sleep 5 & sleep 5 & sleep 5"}
	require.NoError(t, task.EncodeConcreteDriverConfig(cfg))

	_, _, _, cleanup := dockerSetup(t, task, nil)
	defer cleanup()

	// XXX Logging doesn't work on OSX so just test on Linux
	// Check that data was written to the directory.
	outputFile := filepath.Join(task.TaskDir().LogDir, "redis-demo.stderr.0")
	exp := "can't fork"
	tu.WaitForResult(func() (bool, error) {
		act, err := ioutil.ReadFile(outputFile)
		if err != nil {
			return false, err
		}
		if !strings.Contains(string(act), exp) {
			return false, fmt.Errorf("Expected %q in output %q", exp, string(act))
		}
		return true, nil
	}, func(err error) {
		require.NoError(t, err)
	})
}
<commit_msg>docker: remove dead comment<commit_after>//go:build linux

package docker

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

	"github.com/hashicorp/nomad/ci"
	"github.com/hashicorp/nomad/client/testutil"
	"github.com/hashicorp/nomad/helper"
	"github.com/hashicorp/nomad/helper/freeport"
	tu "github.com/hashicorp/nomad/testutil"
	"github.com/stretchr/testify/require"
)

func TestDockerDriver_authFromHelper(t *testing.T) {
	ci.Parallel(t)
	testutil.DockerCompatible(t)

	dir := t.TempDir()
	helperPayload := "{\"Username\":\"hashi\",\"Secret\":\"nomad\"}"
	helperContent := []byte(fmt.Sprintf("#!/bin/sh\ncat > %s/helper-$1.out;echo '%s'", dir, helperPayload))

	helperFile := filepath.Join(dir, "docker-credential-testnomad")
	err := ioutil.WriteFile(helperFile, helperContent, 0777)
	require.NoError(t, err)

	path := os.Getenv("PATH")
	t.Setenv("PATH", fmt.Sprintf("%s:%s", path, dir))

	authHelper := authFromHelper("testnomad")
	creds, err := authHelper("registry.local:5000/repo/image")
	require.NoError(t, err)
	require.NotNil(t, creds)
	require.Equal(t, "hashi", creds.Username)
	require.Equal(t, "nomad", creds.Password)

	if _, err := os.Stat(filepath.Join(dir, "helper-get.out")); os.IsNotExist(err) {
		t.Fatalf("Expected helper-get.out to exist")
	}
	content, err := ioutil.ReadFile(filepath.Join(dir, "helper-get.out"))
	require.NoError(t, err)
	require.Equal(t, "registry.local:5000", string(content))
}

func TestDockerDriver_PluginConfig_PidsLimit(t *testing.T) {
	ci.Parallel(t)
	testutil.DockerCompatible(t)

	dh := dockerDriverHarness(t, nil)
	driver := dh.Impl().(*Driver)
	driver.config.PidsLimit = 5

	task, cfg, ports := dockerTask(t)
	defer freeport.Return(ports)
	require.NoError(t, task.EncodeConcreteDriverConfig(cfg))

	cfg.PidsLimit = 7
	_, err := driver.createContainerConfig(task, cfg, "org/repo:0.1")
	require.Error(t, err)
	require.Contains(t, err.Error(), `pids_limit cannot be greater than nomad plugin config pids_limit`)

	// Task PidsLimit should override plugin PidsLimit.
	cfg.PidsLimit = 3
	opts, err := driver.createContainerConfig(task, cfg, "org/repo:0.1")
	require.NoError(t, err)
	require.Equal(t, helper.Int64ToPtr(3), opts.HostConfig.PidsLimit)
}

func TestDockerDriver_PidsLimit(t *testing.T) {
	ci.Parallel(t)
	testutil.DockerCompatible(t)

	task, cfg, ports := dockerTask(t)
	defer freeport.Return(ports)

	cfg.PidsLimit = 1
	cfg.Command = "/bin/sh"
	cfg.Args = []string{"-c", "sleep 5 & sleep 5 & sleep 5"}
	require.NoError(t, task.EncodeConcreteDriverConfig(cfg))

	_, _, _, cleanup := dockerSetup(t, task, nil)
	defer cleanup()

	// Check that data was written to the directory.
	outputFile := filepath.Join(task.TaskDir().LogDir, "redis-demo.stderr.0")
	exp := "can't fork"
	tu.WaitForResult(func() (bool, error) {
		act, err := ioutil.ReadFile(outputFile)
		if err != nil {
			return false, err
		}
		if !strings.Contains(string(act), exp) {
			return false, fmt.Errorf("Expected %q in output %q", exp, string(act))
		}
		return true, nil
	}, func(err error) {
		require.NoError(t, err)
	})
}
<|endoftext|>


<commit_before>/*
Copyright 2019 The OpenEBS 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.

This code was taken from https://github.com/rancher/local-path-provisioner
and modified to work with the configuration options used by OpenEBS
*/

package app

import (
	//"fmt"
	//"path/filepath"
	//"strings"
	"time"

	"github.com/golang/glog"
	//"github.com/pkg/errors"
	errors "github.com/openebs/maya/pkg/errors/v1alpha1"

	//hostpath "github.com/openebs/maya/pkg/hostpath/v1alpha1"

	//container "github.com/openebs/maya/pkg/kubernetes/container/v1alpha1"
	//pod "github.com/openebs/maya/pkg/kubernetes/pod/v1alpha1"
	//volume "github.com/openebs/maya/pkg/kubernetes/volume/v1alpha1"
	blockdevice "github.com/openebs/maya/pkg/blockdevice/v1alpha2"
	blockdeviceclaim "github.com/openebs/maya/pkg/blockdeviceclaim/v1alpha1"
	corev1 "k8s.io/api/core/v1"
	//ndmv1alpha1 "github.com/openebs/maya/pkg/apis/openebs.io/ndm/v1alpha1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

const (
	bdcStorageClassAnnotation = "local.openebs.io/blockdeviceclaim"
)

//TODO
var (
	//WaitForBDTimeoutCounts specifies the duration to wait for BDC to be associated with a BD
	//The duration is the value specified here multiplied by 5
	WaitForBDTimeoutCounts = 12
)

// HelperBlockDeviceOptions contains the options that
// will launch a BDC on a specific node (nodeName)
type HelperBlockDeviceOptions struct {
	nodeName string
	name     string
	capacity string
	//	deviceType string
	bdcName string
}

// validate checks that the required fields to create BDC
// are available
func (blkDevOpts *HelperBlockDeviceOptions) validate() error {
	glog.Infof("Validate Block Device Options")
	if blkDevOpts.name == "" || blkDevOpts.nodeName == "" {
		return errors.Errorf("invalid empty name or node")
	}
	return nil
}

// hasBDC checks if the bdcName has already been determined
func (blkDevOpts *HelperBlockDeviceOptions) hasBDC() bool {
	glog.Infof("Already has BDC %t", blkDevOpts.bdcName != "")
	return blkDevOpts.bdcName != ""
}

// setBlcokDeviceClaimFromPV inspects the PV and fetches the BDC associated
//  with the Local PV.
func (blkDevOpts *HelperBlockDeviceOptions) setBlockDeviceClaimFromPV(pv *corev1.PersistentVolume) {
	glog.Infof("Setting Block Device Claim From PV")
	bdc, found := pv.Annotations[bdcStorageClassAnnotation]
	if found {
		blkDevOpts.bdcName = bdc
	}
}

// createBlockDeviceClaim creates a new BlockDeviceClaim for a given
//  Local PV
func (p *Provisioner) createBlockDeviceClaim(blkDevOpts *HelperBlockDeviceOptions) error {
	glog.Infof("Creating Block Device Claim")
	if err := blkDevOpts.validate(); err != nil {
		return err
	}

	//if blkDevOpts.hasBDC() {
	//	//already created
	//	glog.Infof("Volume %v has been initialized with BDC:%v", blkDevOpts.name, blkDevOpts.bdcName)
	//	return nil
	//}

	//Create a BDC for this PV (of type device). NDM will
	//look for the device matching the capacity and node on which
	//pod is being scheduled. Since this BDC is specific to a PV
	//use the name of the bdc to be:  "bdc-<pvname>"
	//TODO: Look into setting the labels and owner references
	//on BDC with PV/PVC details.
	bdcName := "bdc-" + blkDevOpts.name

	bdcObj, err := blockdeviceclaim.NewBuilder().
		WithNamespace(p.namespace).
		WithName(bdcName).
		WithHostName(blkDevOpts.nodeName).
		WithCapacity(blkDevOpts.capacity).
		Build()

	if err != nil {
		//TODO : Need to relook at this error
		return errors.Wrapf(err, "unable to build BDC")
	}

	_, err = blockdeviceclaim.NewKubeClient().
		WithNamespace(p.namespace).
		Create(bdcObj.Object)

	if err != nil {
		//TODO : Need to relook at this error
		//If the error is about BDC being already present, then return nil
		return errors.Wrapf(err, "failed to create BDC{%v}", blkDevOpts.name)
	}

	blkDevOpts.bdcName = bdcName

	return nil
}

// getBlockDevicePath fetches the BDC associated with this Local PV
// or creates one. From the BDC, fetch the BD and get the path
func (p *Provisioner) getBlockDevicePath(blkDevOpts *HelperBlockDeviceOptions) (string, string, error) {

	glog.Infof("Getting Block Device Path")
	if !blkDevOpts.hasBDC() {
		err := p.createBlockDeviceClaim(blkDevOpts)
		if err != nil {
			return "", "", err
		}
	}

	//TODO
	glog.Infof("Getting Block Device Path from BDC %v", blkDevOpts.bdcName)
	bdName := ""
	//Check if the BDC is created
	for i := 0; i < WaitForBDTimeoutCounts; i++ {

		bdc, err := blockdeviceclaim.NewKubeClient().
			WithNamespace(p.namespace).
			Get(blkDevOpts.bdcName, metav1.GetOptions{})
		if err != nil {
			//TODO : Need to relook at this error
			//If the error is about BDC being already present, then return nil
			return "", "", errors.Errorf("unable to get BDC %v associated with PV:%v %v", blkDevOpts.bdcName, blkDevOpts.name, err)
		}

		bdName = bdc.Spec.BlockDeviceName
		//Check if the BDC is associated with a BD
		if bdName == "" {
			time.Sleep(5 * time.Second)
		} else {
			break
		}
	}

	//Get the BD Path.
	bd, err := blockdevice.NewKubeClient().
		WithNamespace(p.namespace).
		Get(bdName, metav1.GetOptions{})
	if err != nil {
		//TODO : Need to relook at this error
		//If the error is about BDC being already present, then return nil
		return "", "", errors.Errorf("unable to find BD:%v for BDC:%v associated with PV:%v", bdName, blkDevOpts.bdcName, blkDevOpts.name)
	}

	path := bd.Spec.FileSystem.Mountpoint
	blkPath := bd.Spec.Path
	if len(bd.Spec.DevLinks) > 0 {
		//TODO : Iterate and get the first path by id.
		blkPath = bd.Spec.DevLinks[0].Links[0]
	}

	return path, blkPath, nil
}

// deleteBlockDeviceClaim deletes the BlockDeviceClaim associated with the
//  PV being deleted.
func (p *Provisioner) deleteBlockDeviceClaim(blkDevOpts *HelperBlockDeviceOptions) error {
	glog.Infof("Delete Block Device Claim")
	if !blkDevOpts.hasBDC() {
		return nil
	}

	//TODO: Issue a delete BDC request
	err := blockdeviceclaim.NewKubeClient().
		WithNamespace(p.namespace).
		Delete(blkDevOpts.bdcName, &metav1.DeleteOptions{})

	if err != nil {
		//TODO : Need to relook at this error
		return errors.Errorf("unable to delete BDC %v associated with PV:%v", blkDevOpts.bdcName, blkDevOpts.name)
	}
	return nil
}
<commit_msg>fix(localpv): handle case of BDC-BD taking longer time (#1262)<commit_after>/*
Copyright 2019 The OpenEBS 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.

This code was taken from https://github.com/rancher/local-path-provisioner
and modified to work with the configuration options used by OpenEBS
*/

package app

import (
	//"fmt"
	//"path/filepath"
	//"strings"
	"time"

	"github.com/golang/glog"
	//"github.com/pkg/errors"
	errors "github.com/openebs/maya/pkg/errors/v1alpha1"

	//hostpath "github.com/openebs/maya/pkg/hostpath/v1alpha1"

	//container "github.com/openebs/maya/pkg/kubernetes/container/v1alpha1"
	//pod "github.com/openebs/maya/pkg/kubernetes/pod/v1alpha1"
	//volume "github.com/openebs/maya/pkg/kubernetes/volume/v1alpha1"
	blockdevice "github.com/openebs/maya/pkg/blockdevice/v1alpha2"
	blockdeviceclaim "github.com/openebs/maya/pkg/blockdeviceclaim/v1alpha1"
	corev1 "k8s.io/api/core/v1"
	//ndmv1alpha1 "github.com/openebs/maya/pkg/apis/openebs.io/ndm/v1alpha1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

const (
	bdcStorageClassAnnotation = "local.openebs.io/blockdeviceclaim"
)

//TODO
var (
	//WaitForBDTimeoutCounts specifies the duration to wait for BDC to be associated with a BD
	//The duration is the value specified here multiplied by 5
	WaitForBDTimeoutCounts = 12
)

// HelperBlockDeviceOptions contains the options that
// will launch a BDC on a specific node (nodeName)
type HelperBlockDeviceOptions struct {
	nodeName string
	name     string
	capacity string
	//	deviceType string
	bdcName string
}

// validate checks that the required fields to create BDC
// are available
func (blkDevOpts *HelperBlockDeviceOptions) validate() error {
	glog.Infof("Validate Block Device Options")
	if blkDevOpts.name == "" || blkDevOpts.nodeName == "" {
		return errors.Errorf("invalid empty name or node")
	}
	return nil
}

// hasBDC checks if the bdcName has already been determined
func (blkDevOpts *HelperBlockDeviceOptions) hasBDC() bool {
	glog.Infof("Already has BDC %t", blkDevOpts.bdcName != "")
	return blkDevOpts.bdcName != ""
}

// setBlcokDeviceClaimFromPV inspects the PV and fetches the BDC associated
//  with the Local PV.
func (blkDevOpts *HelperBlockDeviceOptions) setBlockDeviceClaimFromPV(pv *corev1.PersistentVolume) {
	glog.Infof("Setting Block Device Claim From PV")
	bdc, found := pv.Annotations[bdcStorageClassAnnotation]
	if found {
		blkDevOpts.bdcName = bdc
	}
}

// createBlockDeviceClaim creates a new BlockDeviceClaim for a given
//  Local PV
func (p *Provisioner) createBlockDeviceClaim(blkDevOpts *HelperBlockDeviceOptions) error {
	glog.Infof("Creating Block Device Claim")
	if err := blkDevOpts.validate(); err != nil {
		return err
	}

	//Create a BDC for this PV (of type device). NDM will
	//look for the device matching the capacity and node on which
	//pod is being scheduled. Since this BDC is specific to a PV
	//use the name of the bdc to be:  "bdc-<pvname>"
	//TODO: Look into setting the labels and owner references
	//on BDC with PV/PVC details.
	bdcName := "bdc-" + blkDevOpts.name

	//Check if the BDC is already created. This can happen
	//if the previous reconcilation of PVC-PV, resulted in
	//creating a BDC, but BD was not yet available for 60+ seconds
	_, err := blockdeviceclaim.NewKubeClient().
		WithNamespace(p.namespace).
		Get(bdcName, metav1.GetOptions{})
	if err == nil {
		blkDevOpts.bdcName = bdcName
		glog.Infof("Volume %v has been initialized with BDC:%v", blkDevOpts.name, bdcName)
		return nil
	}

	bdcObj, err := blockdeviceclaim.NewBuilder().
		WithNamespace(p.namespace).
		WithName(bdcName).
		WithHostName(blkDevOpts.nodeName).
		WithCapacity(blkDevOpts.capacity).
		Build()

	if err != nil {
		//TODO : Need to relook at this error
		return errors.Wrapf(err, "unable to build BDC")
	}

	_, err = blockdeviceclaim.NewKubeClient().
		WithNamespace(p.namespace).
		Create(bdcObj.Object)

	if err != nil {
		//TODO : Need to relook at this error
		//If the error is about BDC being already present, then return nil
		return errors.Wrapf(err, "failed to create BDC{%v}", bdcName)
	}

	blkDevOpts.bdcName = bdcName

	return nil
}

// getBlockDevicePath fetches the BDC associated with this Local PV
// or creates one. From the BDC, fetch the BD and get the path
func (p *Provisioner) getBlockDevicePath(blkDevOpts *HelperBlockDeviceOptions) (string, string, error) {

	glog.Infof("Getting Block Device Path")
	if !blkDevOpts.hasBDC() {
		err := p.createBlockDeviceClaim(blkDevOpts)
		if err != nil {
			return "", "", err
		}
	}

	//TODO
	glog.Infof("Getting Block Device Path from BDC %v", blkDevOpts.bdcName)
	bdName := ""
	//Check if the BDC is created
	for i := 0; i < WaitForBDTimeoutCounts; i++ {

		bdc, err := blockdeviceclaim.NewKubeClient().
			WithNamespace(p.namespace).
			Get(blkDevOpts.bdcName, metav1.GetOptions{})
		if err != nil {
			//TODO : Need to relook at this error
			//If the error is about BDC being already present, then return nil
			return "", "", errors.Errorf("unable to get BDC %v associated with PV:%v %v", blkDevOpts.bdcName, blkDevOpts.name, err)
		}

		bdName = bdc.Spec.BlockDeviceName
		//Check if the BDC is associated with a BD
		if bdName == "" {
			time.Sleep(5 * time.Second)
		} else {
			break
		}
	}

	//Get the BD Path.
	bd, err := blockdevice.NewKubeClient().
		WithNamespace(p.namespace).
		Get(bdName, metav1.GetOptions{})
	if err != nil {
		//TODO : Need to relook at this error
		//If the error is about BDC being already present, then return nil
		return "", "", errors.Errorf("unable to find BD:%v for BDC:%v associated with PV:%v", bdName, blkDevOpts.bdcName, blkDevOpts.name)
	}

	path := bd.Spec.FileSystem.Mountpoint
	blkPath := bd.Spec.Path
	if len(bd.Spec.DevLinks) > 0 {
		//TODO : Iterate and get the first path by id.
		blkPath = bd.Spec.DevLinks[0].Links[0]
	}

	return path, blkPath, nil
}

// deleteBlockDeviceClaim deletes the BlockDeviceClaim associated with the
//  PV being deleted.
func (p *Provisioner) deleteBlockDeviceClaim(blkDevOpts *HelperBlockDeviceOptions) error {
	glog.Infof("Delete Block Device Claim")
	if !blkDevOpts.hasBDC() {
		return nil
	}

	//TODO: Issue a delete BDC request
	err := blockdeviceclaim.NewKubeClient().
		WithNamespace(p.namespace).
		Delete(blkDevOpts.bdcName, &metav1.DeleteOptions{})

	if err != nil {
		//TODO : Need to relook at this error
		return errors.Errorf("unable to delete BDC %v associated with PV:%v", blkDevOpts.bdcName, blkDevOpts.name)
	}
	return nil
}
<|endoftext|>


<commit_before>package main

import (
	"testing"

	_ "github.com/Spatially/go-environ"
	_ "github.com/Spatially/go-flagged"
	_ "github.com/go-kit/kit/log"
	_ "github.com/go-kit/kit/transport/http"
	_ "github.com/gorilla/mux"
	_ "golang.org/x/net/context"
)

func TestFreezer(t *testing.T) {
	t.Log("TODO")
}
<commit_msg>Remove dependency on Spatially/go-environ<commit_after>package main

import (
	"testing"

	_ "github.com/Spatially/go-flagged"
	_ "github.com/go-kit/kit/log"
	_ "github.com/go-kit/kit/transport/http"
	_ "github.com/gorilla/mux"
	_ "golang.org/x/net/context"
)

func TestFreezer(t *testing.T) {
	t.Log("TODO")
}
<|endoftext|>


<commit_before>// Copyright 2012-2015 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package state

import (
	"gopkg.in/mgo.v2"
)

// The capped collection used for transaction logs defaults to 10MB.
// It's tweaked in export_test.go to 1MB to avoid the overhead of
// creating and deleting the large file repeatedly in tests.
var (
	txnLogSize      = 10000000
	txnLogSizeTests = 1000000
)

// allCollections should be the single source of truth for information about
// any collection we use. It's broken up into 4 main sections:
//
//  * infrastructure: we really don't have any business touching these once
//    we've created them. They should have the rawAccess attribute set, so that
//    multiEnvRunner will consider them forbidden.
//
//  * global: these hold information external to environments. They may include
//    environment metadata, or references; but they're generally not relevant
//    from the perspective of a given environment.
//
//  * local (in opposition to global; and for want of a better term): these
//    hold information relevant *within* specific environments (machines,
//    services, relations, settings, bookkeeping, etc) and should generally be
//    read via an envStateCollection, and written via a multiEnvRunner. This is
//    the most common form of collection, and the above access should usually
//    be automatic via Database.Collection and Database.Runner.
//
//  * raw-access: there's certainly data that's a poor fit for mgo/txn. Most
//    forms of logs, for example, will benefit both from the speedy insert and
//    worry-free bulk deletion; so raw-access collections are fine. Just don't
//    try to run transactions that reference them.
//
// Please do not use collections not referenced here; and when adding new
// collections, please document them, and make an effort to put them in an
// appropriate section.
func allCollections() collectionSchema {
	return collectionSchema{

		// Infrastructure collections
		// ==========================

		txnsC: {
			// This collection is used exclusively by mgo/txn to record transactions.
			global:         true,
			rawAccess:      true,
			explicitCreate: &mgo.CollectionInfo{},
		},
		txnLogC: {
			// This collection is used by mgo/txn to record the set of documents
			// affected by each successful transaction; and by state/watcher to
			// generate a stream of document-resolution events that are delivered
			// to, and interpreted by, both state and state/multiwatcher.
			global:    true,
			rawAccess: true,
			explicitCreate: &mgo.CollectionInfo{
				Capped:   true,
				MaxBytes: txnLogSize,
			},
		},

		// ------------------

		// Global collections
		// ==================

		// This collection holds the details of the state servers hosting, well,
		// everything in state.
		stateServersC: {global: true},

		// This collection is used to track progress when restoring a
		// state server from backup.
		restoreInfoC: {global: true},

		// This collection is used by the state servers to coordinate binary
		// upgrades and schema migrations.
		upgradeInfoC: {global: true},

		// This collection holds a convenient representation of the content of
		// the simplestreams data source pointing to binaries required by juju.
		toolsmetadataC: {global: true},

		// This collection holds environment information; in particular its
		// Life and its UUID.
		environmentsC: {global: true},

		// This collection holds user information that's not specific to any
		// one environment.
		usersC: {
			global: true,
			indexes: []mgo.Index{{
				// TODO(thumper): schema change to remove this index.
				Key: []string{"name"},
			}},
		},

		// This collection is used as a unique key restraint. The _id field is
		// a concatenation of multiple fields that form a compound index,
		// allowing us to ensure users cannot have the same name for two
		// different environments at a time.
		userenvnameC: {global: true},

		// This collection holds workload metrics reported by certain charms
		// for passing onward to other tools.
		metricsC: {global: true},

		// This collection holds persistent state for the metrics manager.
		metricsManagerC: {global: true},

		// This collection holds lease data, which is per-environment, but is
		// not itself multi-environment-aware; happily it will imminently be
		// deprecated in favour of the non-global leasesC below.
		// TODO(fwereade): drop leaseC entirely so can't use wrong const.
		leaseC: {global: true},

		// This collection was deprecated before multi-environment support
		// was implemented.
		actionresultsC: {global: true},

		// -----------------

		// Local collections
		// =================

		// This collection is basically a standard SQL intersection table; it
		// references the global records of the users allowed access to a
		// given collection.
		envUsersC: {},

		// This collection contains governors that prevent certain kinds of
		// changes from being accepted.
		blocksC: {},

		// This collection is used for internal bookkeeping; certain complex
		// or tedious state changes are deferred by recording a cleanup doc
		// for later handling.
		cleanupsC: {},

		// This collection contains incrementing integers, subdivided by name,
		// to ensure various IDs aren't reused.
		sequenceC: {},

		// This collection holds lease data. It's currently only used to
		// implement service leadership, but is namespaced and available
		// for use by other clients in future.
		leasesC: {
			indexes: []mgo.Index{{
				Key: []string{"env-uuid", "type"},
			}, {
				Key: []string{"env-uuid", "namespace"},
			}},
		},

		// -----

		// These collections hold information associated with services.
		charmsC:   {},
		servicesC: {},
		unitsC: {
			indexes: []mgo.Index{{
				Key: []string{"env-uuid", "service"},
			}, {
				Key: []string{"env-uuid", "principal"},
			}, {
				Key: []string{"env-uuid", "machineid"},
			}},
		},
		minUnitsC: {},

		// meterStatusC is the collection used to store meter status information.
		meterStatusC:  {},
		settingsrefsC: {},
		relationsC: {
			indexes: []mgo.Index{{
				Key: []string{"env-uuid", "endpoints.relationname"},
			}, {
				Key: []string{"env-uuid", "endpoints.servicename"},
			}},
		},
		relationScopesC: {},

		// -----

		// These collections hold information associated with machines.
		containerRefsC: {},
		instanceDataC:  {},
		machinesC:      {},
		rebootC:        {},

		// -----

		// These collections hold information associated with storage.
		blockDevicesC: {
			indexes: []mgo.Index{{
				Key: []string{"env-uuid", "machineid"},
			}},
		},
		filesystemsC: {
			indexes: []mgo.Index{{
				Key: []string{"env-uuid", "storageid"},
			}},
		},
		filesystemAttachmentsC: {},
		storageInstancesC: {
			indexes: []mgo.Index{{
				Key: []string{"env-uuid", "owner"},
			}},
		},
		storageAttachmentsC: {
			indexes: []mgo.Index{{
				Key: []string{"env-uuid", "storageid"},
			}, {
				Key: []string{"env-uuid", "unitid"},
			}},
		},
		volumesC: {
			indexes: []mgo.Index{{
				Key: []string{"env-uuid", "storageid"},
			}},
		},
		volumeAttachmentsC: {},

		// -----

		// These collections hold information associated with networking.
		ipaddressesC: {
			indexes: []mgo.Index{{
				Key: []string{"uuid"},
			}, {
				Key: []string{"env-uuid", "state"},
			}, {
				Key: []string{"env-uuid", "subnetid"},
			}},
		},
		networkInterfacesC: {
			indexes: []mgo.Index{{
				Key:    []string{"env-uuid", "interfacename", "machineid"},
				Unique: true,
			}, {
				Key:    []string{"env-uuid", "macaddress", "networkname"},
				Unique: true,
			}, {
				Key: []string{"env-uuid", "machineid"},
			}, {
				Key: []string{"env-uuid", "networkname"},
			}},
		},
		networksC: {
			indexes: []mgo.Index{{
				Key:    []string{"env-uuid", "providerid"},
				Unique: true,
			}},
		},
		openedPortsC:       {},
		requestedNetworksC: {},
		subnetsC: {
			indexes: []mgo.Index{{
				// TODO(dimitern): make unique per-environment, not globally.
				Key: []string{"providerid"},
				// Not always present; but, if present, must be unique; hence
				// both unique and sparse.
				Unique: true,
				Sparse: true,
			}},
		},

		// -----

		// These collections hold information associated with actions.
		actionsC:             {},
		actionNotificationsC: {},

		// -----

		// The remaining non-global collections share the property of being
		// relevant to multiple other kinds of entities, and are thus generally
		// indexed by globalKey(). This is unhelpfully named in this context --
		// it's meant to imply "global within an environment", because it was
		// named before multi-env support.

		// This collection holds user annotations for various entities. They
		// shouldn't be written or interpreted by juju.
		annotationsC: {},

		// This collection in particular holds an astounding number of
		// different sorts of data: service config settings by charm version,
		// unit relation settings, environment config, etc etc etc.
		settingsC: {},

		constraintsC:        {},
		storageConstraintsC: {},
		statusesC:           {},
		statusesHistoryC: {
			indexes: []mgo.Index{{
				Key: []string{"env-uuid", "globalkey"},
			}},
		},

		// This collection holds information about custom cloud image metadata.
		cloudimagemetadataC: {
			indexes: []mgo.Index{{
				Key: []string{"env-uuid"},
			}},
		},

		// ----------------------

		// Raw-access collections
		// ======================

		// metrics; status-history; logs; ..?
	}
}

// These constants are used to avoid sprinkling the package with any more
// magic strings. If a collection deserves documentation, please document
// it in allCollections, above; and please keep this list sorted for easy
// inspection.
const (
	actionNotificationsC   = "actionnotifications"
	actionresultsC         = "actionresults"
	actionsC               = "actions"
	annotationsC           = "annotations"
	blockDevicesC          = "blockdevices"
	blocksC                = "blocks"
	charmsC                = "charms"
	cleanupsC              = "cleanups"
	cloudimagemetadataC    = "cloudimagemetadata"
	constraintsC           = "constraints"
	containerRefsC         = "containerRefs"
	envUsersC              = "envusers"
	environmentsC          = "environments"
	filesystemAttachmentsC = "filesystemAttachments"
	filesystemsC           = "filesystems"
	instanceDataC          = "instanceData"
	ipaddressesC           = "ipaddresses"
	leaseC                 = "lease"
	leasesC                = "leases"
	machinesC              = "machines"
	meterStatusC           = "meterStatus"
	metricsC               = "metrics"
	metricsManagerC        = "metricsmanager"
	minUnitsC              = "minunits"
	networkInterfacesC     = "networkinterfaces"
	networksC              = "networks"
	openedPortsC           = "openedPorts"
	rebootC                = "reboot"
	relationScopesC        = "relationscopes"
	relationsC             = "relations"
	requestedNetworksC     = "requestednetworks"
	restoreInfoC           = "restoreInfo"
	sequenceC              = "sequence"
	servicesC              = "services"
	settingsC              = "settings"
	settingsrefsC          = "settingsrefs"
	stateServersC          = "stateServers"
	statusesC              = "statuses"
	statusesHistoryC       = "statuseshistory"
	storageAttachmentsC    = "storageattachments"
	storageConstraintsC    = "storageconstraints"
	storageInstancesC      = "storageinstances"
	subnetsC               = "subnets"
	toolsmetadataC         = "toolsmetadata"
	txnLogC                = "txns.log"
	txnsC                  = "txns"
	unitsC                 = "units"
	upgradeInfoC           = "upgradeInfo"
	userenvnameC           = "userenvname"
	usersC                 = "users"
	volumeAttachmentsC     = "volumeattachments"
	volumesC               = "volumes"
)
<commit_msg>Removed index<commit_after>// Copyright 2012-2015 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package state

import (
	"gopkg.in/mgo.v2"
)

// The capped collection used for transaction logs defaults to 10MB.
// It's tweaked in export_test.go to 1MB to avoid the overhead of
// creating and deleting the large file repeatedly in tests.
var (
	txnLogSize      = 10000000
	txnLogSizeTests = 1000000
)

// allCollections should be the single source of truth for information about
// any collection we use. It's broken up into 4 main sections:
//
//  * infrastructure: we really don't have any business touching these once
//    we've created them. They should have the rawAccess attribute set, so that
//    multiEnvRunner will consider them forbidden.
//
//  * global: these hold information external to environments. They may include
//    environment metadata, or references; but they're generally not relevant
//    from the perspective of a given environment.
//
//  * local (in opposition to global; and for want of a better term): these
//    hold information relevant *within* specific environments (machines,
//    services, relations, settings, bookkeeping, etc) and should generally be
//    read via an envStateCollection, and written via a multiEnvRunner. This is
//    the most common form of collection, and the above access should usually
//    be automatic via Database.Collection and Database.Runner.
//
//  * raw-access: there's certainly data that's a poor fit for mgo/txn. Most
//    forms of logs, for example, will benefit both from the speedy insert and
//    worry-free bulk deletion; so raw-access collections are fine. Just don't
//    try to run transactions that reference them.
//
// Please do not use collections not referenced here; and when adding new
// collections, please document them, and make an effort to put them in an
// appropriate section.
func allCollections() collectionSchema {
	return collectionSchema{

		// Infrastructure collections
		// ==========================

		txnsC: {
			// This collection is used exclusively by mgo/txn to record transactions.
			global:         true,
			rawAccess:      true,
			explicitCreate: &mgo.CollectionInfo{},
		},
		txnLogC: {
			// This collection is used by mgo/txn to record the set of documents
			// affected by each successful transaction; and by state/watcher to
			// generate a stream of document-resolution events that are delivered
			// to, and interpreted by, both state and state/multiwatcher.
			global:    true,
			rawAccess: true,
			explicitCreate: &mgo.CollectionInfo{
				Capped:   true,
				MaxBytes: txnLogSize,
			},
		},

		// ------------------

		// Global collections
		// ==================

		// This collection holds the details of the state servers hosting, well,
		// everything in state.
		stateServersC: {global: true},

		// This collection is used to track progress when restoring a
		// state server from backup.
		restoreInfoC: {global: true},

		// This collection is used by the state servers to coordinate binary
		// upgrades and schema migrations.
		upgradeInfoC: {global: true},

		// This collection holds a convenient representation of the content of
		// the simplestreams data source pointing to binaries required by juju.
		toolsmetadataC: {global: true},

		// This collection holds environment information; in particular its
		// Life and its UUID.
		environmentsC: {global: true},

		// This collection holds user information that's not specific to any
		// one environment.
		usersC: {
			global: true,
			indexes: []mgo.Index{{
				// TODO(thumper): schema change to remove this index.
				Key: []string{"name"},
			}},
		},

		// This collection is used as a unique key restraint. The _id field is
		// a concatenation of multiple fields that form a compound index,
		// allowing us to ensure users cannot have the same name for two
		// different environments at a time.
		userenvnameC: {global: true},

		// This collection holds workload metrics reported by certain charms
		// for passing onward to other tools.
		metricsC: {global: true},

		// This collection holds persistent state for the metrics manager.
		metricsManagerC: {global: true},

		// This collection holds lease data, which is per-environment, but is
		// not itself multi-environment-aware; happily it will imminently be
		// deprecated in favour of the non-global leasesC below.
		// TODO(fwereade): drop leaseC entirely so can't use wrong const.
		leaseC: {global: true},

		// This collection was deprecated before multi-environment support
		// was implemented.
		actionresultsC: {global: true},

		// -----------------

		// Local collections
		// =================

		// This collection is basically a standard SQL intersection table; it
		// references the global records of the users allowed access to a
		// given collection.
		envUsersC: {},

		// This collection contains governors that prevent certain kinds of
		// changes from being accepted.
		blocksC: {},

		// This collection is used for internal bookkeeping; certain complex
		// or tedious state changes are deferred by recording a cleanup doc
		// for later handling.
		cleanupsC: {},

		// This collection contains incrementing integers, subdivided by name,
		// to ensure various IDs aren't reused.
		sequenceC: {},

		// This collection holds lease data. It's currently only used to
		// implement service leadership, but is namespaced and available
		// for use by other clients in future.
		leasesC: {
			indexes: []mgo.Index{{
				Key: []string{"env-uuid", "type"},
			}, {
				Key: []string{"env-uuid", "namespace"},
			}},
		},

		// -----

		// These collections hold information associated with services.
		charmsC:   {},
		servicesC: {},
		unitsC: {
			indexes: []mgo.Index{{
				Key: []string{"env-uuid", "service"},
			}, {
				Key: []string{"env-uuid", "principal"},
			}, {
				Key: []string{"env-uuid", "machineid"},
			}},
		},
		minUnitsC: {},

		// meterStatusC is the collection used to store meter status information.
		meterStatusC:  {},
		settingsrefsC: {},
		relationsC: {
			indexes: []mgo.Index{{
				Key: []string{"env-uuid", "endpoints.relationname"},
			}, {
				Key: []string{"env-uuid", "endpoints.servicename"},
			}},
		},
		relationScopesC: {},

		// -----

		// These collections hold information associated with machines.
		containerRefsC: {},
		instanceDataC:  {},
		machinesC:      {},
		rebootC:        {},

		// -----

		// These collections hold information associated with storage.
		blockDevicesC: {
			indexes: []mgo.Index{{
				Key: []string{"env-uuid", "machineid"},
			}},
		},
		filesystemsC: {
			indexes: []mgo.Index{{
				Key: []string{"env-uuid", "storageid"},
			}},
		},
		filesystemAttachmentsC: {},
		storageInstancesC: {
			indexes: []mgo.Index{{
				Key: []string{"env-uuid", "owner"},
			}},
		},
		storageAttachmentsC: {
			indexes: []mgo.Index{{
				Key: []string{"env-uuid", "storageid"},
			}, {
				Key: []string{"env-uuid", "unitid"},
			}},
		},
		volumesC: {
			indexes: []mgo.Index{{
				Key: []string{"env-uuid", "storageid"},
			}},
		},
		volumeAttachmentsC: {},

		// -----

		// These collections hold information associated with networking.
		ipaddressesC: {
			indexes: []mgo.Index{{
				Key: []string{"uuid"},
			}, {
				Key: []string{"env-uuid", "state"},
			}, {
				Key: []string{"env-uuid", "subnetid"},
			}},
		},
		networkInterfacesC: {
			indexes: []mgo.Index{{
				Key:    []string{"env-uuid", "interfacename", "machineid"},
				Unique: true,
			}, {
				Key:    []string{"env-uuid", "macaddress", "networkname"},
				Unique: true,
			}, {
				Key: []string{"env-uuid", "machineid"},
			}, {
				Key: []string{"env-uuid", "networkname"},
			}},
		},
		networksC: {
			indexes: []mgo.Index{{
				Key:    []string{"env-uuid", "providerid"},
				Unique: true,
			}},
		},
		openedPortsC:       {},
		requestedNetworksC: {},
		subnetsC: {
			indexes: []mgo.Index{{
				// TODO(dimitern): make unique per-environment, not globally.
				Key: []string{"providerid"},
				// Not always present; but, if present, must be unique; hence
				// both unique and sparse.
				Unique: true,
				Sparse: true,
			}},
		},

		// -----

		// These collections hold information associated with actions.
		actionsC:             {},
		actionNotificationsC: {},

		// -----

		// The remaining non-global collections share the property of being
		// relevant to multiple other kinds of entities, and are thus generally
		// indexed by globalKey(). This is unhelpfully named in this context --
		// it's meant to imply "global within an environment", because it was
		// named before multi-env support.

		// This collection holds user annotations for various entities. They
		// shouldn't be written or interpreted by juju.
		annotationsC: {},

		// This collection in particular holds an astounding number of
		// different sorts of data: service config settings by charm version,
		// unit relation settings, environment config, etc etc etc.
		settingsC: {},

		constraintsC:        {},
		storageConstraintsC: {},
		statusesC:           {},
		statusesHistoryC: {
			indexes: []mgo.Index{{
				Key: []string{"env-uuid", "globalkey"},
			}},
		},

		// This collection holds information about custom cloud image metadata.
		cloudimagemetadataC: {},

		// ----------------------

		// Raw-access collections
		// ======================

		// metrics; status-history; logs; ..?
	}
}

// These constants are used to avoid sprinkling the package with any more
// magic strings. If a collection deserves documentation, please document
// it in allCollections, above; and please keep this list sorted for easy
// inspection.
const (
	actionNotificationsC   = "actionnotifications"
	actionresultsC         = "actionresults"
	actionsC               = "actions"
	annotationsC           = "annotations"
	blockDevicesC          = "blockdevices"
	blocksC                = "blocks"
	charmsC                = "charms"
	cleanupsC              = "cleanups"
	cloudimagemetadataC    = "cloudimagemetadata"
	constraintsC           = "constraints"
	containerRefsC         = "containerRefs"
	envUsersC              = "envusers"
	environmentsC          = "environments"
	filesystemAttachmentsC = "filesystemAttachments"
	filesystemsC           = "filesystems"
	instanceDataC          = "instanceData"
	ipaddressesC           = "ipaddresses"
	leaseC                 = "lease"
	leasesC                = "leases"
	machinesC              = "machines"
	meterStatusC           = "meterStatus"
	metricsC               = "metrics"
	metricsManagerC        = "metricsmanager"
	minUnitsC              = "minunits"
	networkInterfacesC     = "networkinterfaces"
	networksC              = "networks"
	openedPortsC           = "openedPorts"
	rebootC                = "reboot"
	relationScopesC        = "relationscopes"
	relationsC             = "relations"
	requestedNetworksC     = "requestednetworks"
	restoreInfoC           = "restoreInfo"
	sequenceC              = "sequence"
	servicesC              = "services"
	settingsC              = "settings"
	settingsrefsC          = "settingsrefs"
	stateServersC          = "stateServers"
	statusesC              = "statuses"
	statusesHistoryC       = "statuseshistory"
	storageAttachmentsC    = "storageattachments"
	storageConstraintsC    = "storageconstraints"
	storageInstancesC      = "storageinstances"
	subnetsC               = "subnets"
	toolsmetadataC         = "toolsmetadata"
	txnLogC                = "txns.log"
	txnsC                  = "txns"
	unitsC                 = "units"
	upgradeInfoC           = "upgradeInfo"
	userenvnameC           = "userenvname"
	usersC                 = "users"
	volumeAttachmentsC     = "volumeattachments"
	volumesC               = "volumes"
)
<|endoftext|>