<commit_before>/* Tradition!

The curl demo:

        curl -i http://127.0.0.1:8080/message

*/
package main

import (
	"github.com/ant0ine/go-json-rest/rest"
	"net/http"
)

type Message struct {
	Body   string
}

func main() {
	handler := rest.ResourceHandler{}
	handler.SetRoutes(
		&rest.Route{
                        "GET",
                        "/message",
                        func(w rest.ResponseWriter, req *rest.Request) {
                                w.WriteJson(&Message{
                                        Body: "Hello World!",
                                })
                        },
                },
	)
	http.ListenAndServe(":8080", &handler)
}
<commit_msg>Try to fix the pygment indentation<commit_after>/* Tradition!

The curl demo:

        curl -i http://127.0.0.1:8080/message

*/
package main

import (
	"github.com/ant0ine/go-json-rest/rest"
	"net/http"
)

type Message struct {
	Body string
}

func main() {
	handler := rest.ResourceHandler{}
	handler.SetRoutes(
		&rest.Route{"GET", "/message", func(w rest.ResponseWriter, req *rest.Request) {
			w.WriteJson(&Message{
				Body: "Hello World!",
			})
		}},
	)
	http.ListenAndServe(":8080", &handler)
}
<|endoftext|>


<commit_before>package hooks

import (
	"fmt"
	"os"
	"strings"

	"github.com/huawei-openlab/oct/utils"
)

func NamespacePostStart(output string) error {
	nsout := utils.GetBetweenStr(output, "[namespace_output_start]", "[namespace_output_end]")
	if strings.EqualFold(nsout, "") {
		return nil
	}
	for _, ns := range strings.Split(nsout, "\n") {
		if !strings.EqualFold(ns, "") {
			if len(strings.Split(ns, ",")) != 2 {
				break
			}
			linkc := strings.Split(ns, ",")[0]
			if len(strings.Split(linkc, ",")) != 2 {
				break
			}
			nsname := strings.Split(linkc, ":")[0]
			path := strings.Split(ns, ",")[1]
			if !strings.EqualFold(path, "") {
				linkh, _ := os.Readlink(path)
				if !strings.EqualFold(linkh, linkc) {
					return fmt.Errorf("%v namespace expected: %v, actual: %v ", nsname, linkh, linkc)

				}
			}
			if strings.EqualFold(path, "") {
				linkh, _ := os.Readlink("/proc/1/ns/" + nsname)
				if strings.EqualFold(linkh, linkc) {
					return fmt.Errorf("namespace %v path is empty, but namespace inside and outside container is the same", nsname)
				}
			}
		}
	}
	return nil
}
<commit_msg>move the oci2aci to factory<commit_after>package hooks

import (
	"fmt"
	"os"
	"strings"

	"github.com/huawei-openlab/oct/utils"
)

func NamespacePostStart(output string) error {
	nsout := utils.GetBetweenStr(output, "[namespace_output_start]", "[namespace_output_end]")
	if strings.EqualFold(nsout, "") {
		return nil
	}
	for _, ns := range strings.Split(nsout, "\n") {
		if !strings.EqualFold(ns, "") {
			if len(strings.Split(ns, ",")) != 2 {
				break
			}
			linkc := strings.Split(ns, ",")[0]
			if len(strings.Split(linkc, ",")) != 2 {
				break
			}
			nsname := strings.Split(linkc, ":")[0]
			path := strings.Split(ns, ",")[1]
			if !strings.EqualFold(path, "") {
				linkh, _ := os.Readlink(path)
				if !strings.EqualFold(linkh, linkc) {
					return fmt.Errorf("%v namespace expected: %v, actual: %v ", nsname, linkh, linkc)

				}
			}
			if strings.EqualFold(path, "") {
				linkh, _ := os.Readlink("/proc/1/ns/" + nsname)
				if strings.EqualFold(linkh, linkc) {
					return fmt.Errorf("namespace %v path is empty, but namespace inside and outside container is the same", nsname)
				}
			}
		}
	}
	return nil
}
<|endoftext|>


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

import (
	"container/heap"
	"time"

	"github.com/blevesearch/bleve/index"
	"github.com/blevesearch/bleve/search"
	"golang.org/x/net/context"
)

type HeapCollector struct {
	size          int
	skip          int
	total         uint64
	maxScore      float64
	took          time.Duration
	sort          search.SortOrder
	results       search.DocumentMatchCollection
	facetsBuilder *search.FacetsBuilder

	needDocIds    bool
	neededFields  []string
	cachedScoring []bool
	cachedDesc    []bool

	lowestMatchOutsideResults *search.DocumentMatch
}

var COLLECT_CHECK_DONE_EVERY = uint64(1024)

func NewHeapCollector(size int, skip int, sort search.SortOrder) *HeapCollector {
	hc := &HeapCollector{size: size, skip: skip, sort: sort}
	// pre-allocate space on the heap, we need size+skip results
	// +1 additional while figuring out which to evict
	hc.results = make(search.DocumentMatchCollection, 0, size+skip+1)
	heap.Init(hc)

	// these lookups traverse an interface, so do once up-front
	if sort.RequiresDocID() {
		hc.needDocIds = true
	}
	hc.neededFields = sort.RequiredFields()
	hc.cachedScoring = sort.CacheIsScore()
	hc.cachedDesc = sort.CacheDescending()

	return hc
}

func (hc *HeapCollector) Collect(ctx context.Context, searcher search.Searcher, reader index.IndexReader) error {
	startTime := time.Now()
	var err error
	var next *search.DocumentMatch

	// search context with enough pre-allocated document matches
	// we keep references to size+skip ourselves
	// plus possibly one extra for the highestMatchOutsideResults
	// plus the amount required by the searcher tree
	searchContext := &search.SearchContext{
		DocumentMatchPool: search.NewDocumentMatchPool(hc.size+hc.skip+1+searcher.DocumentMatchPoolSize(), len(hc.sort)),
	}

	select {
	case <-ctx.Done():
		return ctx.Err()
	default:
		next, err = searcher.Next(searchContext)
	}
	for err == nil && next != nil {
		if hc.total%COLLECT_CHECK_DONE_EVERY == 0 {
			select {
			case <-ctx.Done():
				return ctx.Err()
			default:
			}
		}
		if hc.facetsBuilder != nil {
			err = hc.facetsBuilder.Update(next)
			if err != nil {
				break
			}
		}

		err = hc.collectSingle(searchContext, reader, next)
		if err != nil {
			break
		}

		next, err = searcher.Next(searchContext)
	}
	// compute search duration
	hc.took = time.Since(startTime)
	if err != nil {
		return err
	}
	// finalize actual results
	err = hc.finalizeResults(reader)
	if err != nil {
		return err
	}
	return nil
}

func (hc *HeapCollector) collectSingle(ctx *search.SearchContext, reader index.IndexReader, d *search.DocumentMatch) error {
	// increment total hits
	hc.total++
	d.HitNumber = hc.total

	// update max score
	if d.Score > hc.maxScore {
		hc.maxScore = d.Score
	}

	var err error
	// see if we need to load ID (at this early stage, for example to sort on it)
	if hc.needDocIds {
		d.ID, err = reader.FinalizeDocID(d.IndexInternalID)
		if err != nil {
			return err
		}
	}

	// see if we need to load the stored fields
	if len(hc.neededFields) > 0 {
		// find out which fields haven't been loaded yet
		fieldsToLoad := d.CachedFieldTerms.FieldsNotYetCached(hc.neededFields)
		// look them up
		fieldTerms, err := reader.DocumentFieldTerms(d.IndexInternalID, fieldsToLoad)
		if err != nil {
			return err
		}
		// cache these as well
		if d.CachedFieldTerms == nil {
			d.CachedFieldTerms = make(map[string][]string)
		}
		d.CachedFieldTerms.Merge(fieldTerms)
	}

	// compute this hits sort value
	hc.sort.Value(d)

	// optimization, we track lowest sorting hit already removed from heap
	// with this one comparision, we can avoid all heap operations if
	// this hit would have been added and then immediately removed
	if hc.lowestMatchOutsideResults != nil {
		cmp := hc.sort.Compare(hc.cachedScoring, hc.cachedDesc, d, hc.lowestMatchOutsideResults)
		if cmp >= 0 {
			// this hit can't possibly be in the result set, so avoid heap ops
			ctx.DocumentMatchPool.Put(d)
			return nil
		}
	}

	heap.Push(hc, d)
	if hc.Len() > hc.size+hc.skip {
		removed := heap.Pop(hc).(*search.DocumentMatch)
		if hc.lowestMatchOutsideResults == nil {
			hc.lowestMatchOutsideResults = removed
		} else {
			cmp := hc.sort.Compare(hc.cachedScoring, hc.cachedDesc, removed, hc.lowestMatchOutsideResults)
			if cmp < 0 {
				tmp := hc.lowestMatchOutsideResults
				hc.lowestMatchOutsideResults = removed
				ctx.DocumentMatchPool.Put(tmp)
			}
		}
	}

	return nil
}

func (hc *HeapCollector) SetFacetsBuilder(facetsBuilder *search.FacetsBuilder) {
	hc.facetsBuilder = facetsBuilder
}

// finalizeResults starts with the heap containing the final top size+skip
// it now throws away the results to be skipped
// and does final doc id lookup (if necessary)
func (hc *HeapCollector) finalizeResults(r index.IndexReader) error {
	count := hc.Len()
	size := count - hc.skip
	rv := make(search.DocumentMatchCollection, size)
	for count > 0 {
		count--

		if count >= hc.skip {
			size--
			doc := heap.Pop(hc).(*search.DocumentMatch)
			rv[size] = doc
			if doc.ID == "" {
				// look up the id since we need it for lookup
				var err error
				doc.ID, err = r.FinalizeDocID(doc.IndexInternalID)
				if err != nil {
					return err
				}
			}
		}
	}

	// no longer a heap
	hc.results = rv

	return nil
}

func (hc *HeapCollector) Results() search.DocumentMatchCollection {
	return hc.results
}

func (hc *HeapCollector) Total() uint64 {
	return hc.total
}

func (hc *HeapCollector) MaxScore() float64 {
	return hc.maxScore
}

func (hc *HeapCollector) Took() time.Duration {
	return hc.took
}

func (hc *HeapCollector) FacetResults() search.FacetResults {
	if hc.facetsBuilder != nil {
		return hc.facetsBuilder.Results()
	}
	return search.FacetResults{}
}

// heap interface implementation

func (hc *HeapCollector) Len() int {
	return len(hc.results)
}

func (hc *HeapCollector) Less(i, j int) bool {
	so := hc.sort.Compare(hc.cachedScoring, hc.cachedDesc, hc.results[i], hc.results[j])
	return -so < 0
}

func (hc *HeapCollector) Swap(i, j int) {
	hc.results[i], hc.results[j] = hc.results[j], hc.results[i]
}

func (hc *HeapCollector) Push(x interface{}) {
	hc.results = append(hc.results, x.(*search.DocumentMatch))
}

func (hc *HeapCollector) Pop() interface{} {
	var rv *search.DocumentMatch
	rv, hc.results = hc.results[len(hc.results)-1], hc.results[:len(hc.results)-1]
	return rv
}
<commit_msg>completely avoid dynamic dispatch if only sorting on score<commit_after>//  Copyright (c) 2014 Couchbase, Inc.
//  Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
//  except in compliance with the License. You may obtain a copy of the License at
//    http://www.apache.org/licenses/LICENSE-2.0
//  Unless required by applicable law or agreed to in writing, software distributed under the
//  License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
//  either express or implied. See the License for the specific language governing permissions
//  and limitations under the License.
//
package collectors

import (
	"container/heap"
	"time"

	"github.com/blevesearch/bleve/index"
	"github.com/blevesearch/bleve/search"
	"golang.org/x/net/context"
)

type HeapCollector struct {
	size          int
	skip          int
	total         uint64
	maxScore      float64
	took          time.Duration
	sort          search.SortOrder
	results       search.DocumentMatchCollection
	facetsBuilder *search.FacetsBuilder

	needDocIds    bool
	neededFields  []string
	cachedScoring []bool
	cachedDesc    []bool

	lowestMatchOutsideResults *search.DocumentMatch
}

var COLLECT_CHECK_DONE_EVERY = uint64(1024)

func NewHeapCollector(size int, skip int, sort search.SortOrder) *HeapCollector {
	hc := &HeapCollector{size: size, skip: skip, sort: sort}
	// pre-allocate space on the heap, we need size+skip results
	// +1 additional while figuring out which to evict
	hc.results = make(search.DocumentMatchCollection, 0, size+skip+1)
	heap.Init(hc)

	// these lookups traverse an interface, so do once up-front
	if sort.RequiresDocID() {
		hc.needDocIds = true
	}
	hc.neededFields = sort.RequiredFields()
	hc.cachedScoring = sort.CacheIsScore()
	hc.cachedDesc = sort.CacheDescending()

	return hc
}

func (hc *HeapCollector) Collect(ctx context.Context, searcher search.Searcher, reader index.IndexReader) error {
	startTime := time.Now()
	var err error
	var next *search.DocumentMatch

	// search context with enough pre-allocated document matches
	// we keep references to size+skip ourselves
	// plus possibly one extra for the highestMatchOutsideResults
	// plus the amount required by the searcher tree
	searchContext := &search.SearchContext{
		DocumentMatchPool: search.NewDocumentMatchPool(hc.size+hc.skip+1+searcher.DocumentMatchPoolSize(), len(hc.sort)),
	}

	select {
	case <-ctx.Done():
		return ctx.Err()
	default:
		next, err = searcher.Next(searchContext)
	}
	for err == nil && next != nil {
		if hc.total%COLLECT_CHECK_DONE_EVERY == 0 {
			select {
			case <-ctx.Done():
				return ctx.Err()
			default:
			}
		}
		if hc.facetsBuilder != nil {
			err = hc.facetsBuilder.Update(next)
			if err != nil {
				break
			}
		}

		err = hc.collectSingle(searchContext, reader, next)
		if err != nil {
			break
		}

		next, err = searcher.Next(searchContext)
	}
	// compute search duration
	hc.took = time.Since(startTime)
	if err != nil {
		return err
	}
	// finalize actual results
	err = hc.finalizeResults(reader)
	if err != nil {
		return err
	}
	return nil
}

func (hc *HeapCollector) collectSingle(ctx *search.SearchContext, reader index.IndexReader, d *search.DocumentMatch) error {
	// increment total hits
	hc.total++
	d.HitNumber = hc.total

	// update max score
	if d.Score > hc.maxScore {
		hc.maxScore = d.Score
	}

	var err error
	// see if we need to load ID (at this early stage, for example to sort on it)
	if hc.needDocIds {
		d.ID, err = reader.FinalizeDocID(d.IndexInternalID)
		if err != nil {
			return err
		}
	}

	// see if we need to load the stored fields
	if len(hc.neededFields) > 0 {
		// find out which fields haven't been loaded yet
		fieldsToLoad := d.CachedFieldTerms.FieldsNotYetCached(hc.neededFields)
		// look them up
		fieldTerms, err := reader.DocumentFieldTerms(d.IndexInternalID, fieldsToLoad)
		if err != nil {
			return err
		}
		// cache these as well
		if d.CachedFieldTerms == nil {
			d.CachedFieldTerms = make(map[string][]string)
		}
		d.CachedFieldTerms.Merge(fieldTerms)
	}

	// compute this hits sort value
	if len(hc.sort) > 1 || len(hc.sort) == 1 && !hc.cachedScoring[0] {
		hc.sort.Value(d)
	}

	// optimization, we track lowest sorting hit already removed from heap
	// with this one comparision, we can avoid all heap operations if
	// this hit would have been added and then immediately removed
	if hc.lowestMatchOutsideResults != nil {
		cmp := hc.sort.Compare(hc.cachedScoring, hc.cachedDesc, d, hc.lowestMatchOutsideResults)
		if cmp >= 0 {
			// this hit can't possibly be in the result set, so avoid heap ops
			ctx.DocumentMatchPool.Put(d)
			return nil
		}
	}

	heap.Push(hc, d)
	if hc.Len() > hc.size+hc.skip {
		removed := heap.Pop(hc).(*search.DocumentMatch)
		if hc.lowestMatchOutsideResults == nil {
			hc.lowestMatchOutsideResults = removed
		} else {
			cmp := hc.sort.Compare(hc.cachedScoring, hc.cachedDesc, removed, hc.lowestMatchOutsideResults)
			if cmp < 0 {
				tmp := hc.lowestMatchOutsideResults
				hc.lowestMatchOutsideResults = removed
				ctx.DocumentMatchPool.Put(tmp)
			}
		}
	}

	return nil
}

func (hc *HeapCollector) SetFacetsBuilder(facetsBuilder *search.FacetsBuilder) {
	hc.facetsBuilder = facetsBuilder
}

// finalizeResults starts with the heap containing the final top size+skip
// it now throws away the results to be skipped
// and does final doc id lookup (if necessary)
func (hc *HeapCollector) finalizeResults(r index.IndexReader) error {
	count := hc.Len()
	size := count - hc.skip
	rv := make(search.DocumentMatchCollection, size)
	for count > 0 {
		count--

		if count >= hc.skip {
			size--
			doc := heap.Pop(hc).(*search.DocumentMatch)
			rv[size] = doc
			if doc.ID == "" {
				// look up the id since we need it for lookup
				var err error
				doc.ID, err = r.FinalizeDocID(doc.IndexInternalID)
				if err != nil {
					return err
				}
			}
		}
	}

	// no longer a heap
	hc.results = rv

	return nil
}

func (hc *HeapCollector) Results() search.DocumentMatchCollection {
	return hc.results
}

func (hc *HeapCollector) Total() uint64 {
	return hc.total
}

func (hc *HeapCollector) MaxScore() float64 {
	return hc.maxScore
}

func (hc *HeapCollector) Took() time.Duration {
	return hc.took
}

func (hc *HeapCollector) FacetResults() search.FacetResults {
	if hc.facetsBuilder != nil {
		return hc.facetsBuilder.Results()
	}
	return search.FacetResults{}
}

// heap interface implementation

func (hc *HeapCollector) Len() int {
	return len(hc.results)
}

func (hc *HeapCollector) Less(i, j int) bool {
	so := hc.sort.Compare(hc.cachedScoring, hc.cachedDesc, hc.results[i], hc.results[j])
	return -so < 0
}

func (hc *HeapCollector) Swap(i, j int) {
	hc.results[i], hc.results[j] = hc.results[j], hc.results[i]
}

func (hc *HeapCollector) Push(x interface{}) {
	hc.results = append(hc.results, x.(*search.DocumentMatch))
}

func (hc *HeapCollector) Pop() interface{} {
	var rv *search.DocumentMatch
	rv, hc.results = hc.results[len(hc.results)-1], hc.results[:len(hc.results)-1]
	return rv
}
<|endoftext|>


<commit_before>package echo

import (
	"encoding/json"
	"encoding/xml"
	"errors"
	"fmt"
	"net/http"
	"reflect"
	"strconv"
	"strings"
	"time"

	"github.com/webx-top/tagfast"
	"github.com/webx-top/validation"
)

var DefaultHtmlFilter = func(v string) (r string) {
	return v
}

type (
	// Binder is the interface that wraps the Bind method.
	Binder interface {
		Bind(interface{}, Context) error
	}
	binder struct {
		*Echo
	}
)

func (b *binder) Bind(i interface{}, c Context) (err error) {
	r := c.Request()
	body := r.Body()
	if body == nil {
		err = NewHTTPError(http.StatusBadRequest, "Request body can't be nil")
		return
	}
	defer body.Close()
	ct := r.Header().Get(HeaderContentType)
	err = ErrUnsupportedMediaType
	if strings.HasPrefix(ct, MIMEApplicationJSON) {
		err = json.NewDecoder(body).Decode(i)
	} else if strings.HasPrefix(ct, MIMEApplicationXML) {
		err = xml.NewDecoder(body).Decode(i)
	} else if strings.HasPrefix(ct, MIMEApplicationForm) {
		err = b.structMap(i, r.PostForm().All())
	} else if strings.Contains(ct, MIMEMultipartForm) {
		err = b.structMap(i, r.Form().All())
	}
	return
}

// StructMap function mapping params to controller's properties
func (b *binder) structMap(m interface{}, data map[string][]string) error {
	return NamedStructMap(b.Echo, m, data, ``)
}

// SplitJSON user[name][test]
func SplitJSON(s string) ([]string, error) {
	res := make([]string, 0)
	var begin, end int
	var isleft bool
	for i, r := range s {
		switch r {
		case '[':
			isleft = true
			if i > 0 && s[i-1] != ']' {
				if begin == end {
					return nil, errors.New(`unknow character`)
				}
				res = append(res, s[begin:end+1])
			}
			begin = i + 1
			end = begin
		case ']':
			if !isleft {
				return nil, errors.New(`unknow character`)
			}
			isleft = false
			if begin != end {
				res = append(res, s[begin:end+1])
				begin = i + 1
				end = begin
			}
		default:
			end = i
		}
		if i == len(s)-1 && begin != end {
			res = append(res, s[begin:end+1])
		}
	}
	return res, nil
}

func NamedStructMap(e *Echo, m interface{}, data map[string][]string, topName string) error {
	vc := reflect.ValueOf(m)
	tc := reflect.TypeOf(m)

	switch tc.Kind() {
	case reflect.Struct:
	case reflect.Ptr:
		vc = vc.Elem()
		tc = tc.Elem()
	}
	validator := validation.New()
	for k, t := range data {

		if k == `` || k[0] == '_' {
			continue
		}

		if topName != `` {
			if !strings.HasPrefix(k, topName) {
				continue
			}
			k = k[len(topName)+1:]
		}

		v := t[0]
		names := strings.Split(k, `.`)
		var err error
		length := len(names)
		if length == 1 && strings.HasSuffix(k, `]`) {
			names, err = SplitJSON(k)
			if err != nil {
				e.Logger().Warnf(`Unrecognize form key %v %v`, k, err)
				continue
			}
			length = len(names)
		}
		value := vc
		typev := tc
		for i, name := range names {
			name = strings.Title(name)

			//不是最后一个元素
			if i != length-1 {
				if value.Kind() != reflect.Struct {
					e.Logger().Warnf(`arg error, value kind is %v`, value.Kind())
					break
				}
				value = value.FieldByName(name)
				if !value.IsValid() {
					e.Logger().Warnf(`(%v value is not valid %v)`, name, value)
					break
				}
				if !value.CanSet() {
					e.Logger().Warnf(`can not set %v -> %v`, name, value.Interface())
					break
				}
				if value.Kind() == reflect.Ptr {
					if value.IsNil() {
						value.Set(reflect.New(value.Type().Elem()))
					}
					value = value.Elem()
				}
				typev = value.Type()
				f, _ := typev.FieldByName(name)
				if tagfast.Value(tc, f, `form_options`) == `-` {
					break
				}
			} else {
				if value.Kind() != reflect.Struct {
					e.Logger().Warnf(`arg error, value %v kind is %v`, name, value.Kind())
					break
				}
				tv := value.FieldByName(name)
				if !tv.IsValid() {
					break
				}
				if !tv.CanSet() {
					e.Logger().Warnf(`can not set %v to %v`, k, tv)
					break
				}
				f, _ := typev.FieldByName(name)
				if tagfast.Value(tc, f, `form_options`) == `-` {
					break
				}
				if tv.Kind() == reflect.Ptr {
					tv.Set(reflect.New(tv.Type().Elem()))
					tv = tv.Elem()
				}

				var l interface{}
				switch k := tv.Kind(); k {
				case reflect.String:
					switch tagfast.Value(tc, f, `form_filter`) {
					case `html`:
						v = DefaultHtmlFilter(v)
					}
					l = v
					tv.Set(reflect.ValueOf(l))
				case reflect.Bool:
					l = (v != `false` && v != `0` && v != ``)
					tv.Set(reflect.ValueOf(l))
				case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32:
					dateformat := tagfast.Value(tc, f, `form_format`)
					if dateformat != `` {
						t, err := time.Parse(dateformat, v)
						if err != nil {
							e.Logger().Warnf(`arg %v as int: %v`, v, err)
							break
						}
						l = int(t.Unix())
					} else {
						x, err := strconv.Atoi(v)
						if err != nil {
							e.Logger().Warnf(`arg %v as int: %v`, v, err)
							break
						}
						l = x
					}
					tv.Set(reflect.ValueOf(l))
				case reflect.Int64:
					dateformat := tagfast.Value(tc, f, `form_format`)
					if dateformat != `` {
						t, err := time.Parse(dateformat, v)
						if err != nil {
							e.Logger().Warnf(`arg %v as int64: %v`, v, err)
							break
						}
						l = t.Unix()
					} else {
						x, err := strconv.ParseInt(v, 10, 64)
						if err != nil {
							e.Logger().Warnf(`arg %v as int64: %v`, v, err)
							break
						}
						l = x
					}
					tv.Set(reflect.ValueOf(l))
				case reflect.Float32, reflect.Float64:
					x, err := strconv.ParseFloat(v, 64)
					if err != nil {
						e.Logger().Warnf(`arg %v as float64: %v`, v, err)
						break
					}
					l = x
					tv.Set(reflect.ValueOf(l))
				case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
					dateformat := tagfast.Value(tc, f, `form_format`)
					if dateformat != `` {
						t, err := time.Parse(dateformat, v)
						if err != nil {
							e.Logger().Warnf(`arg %v as uint: %v`, v, err)
							break
						}
						l = uint64(t.Unix())
					} else {
						x, err := strconv.ParseUint(v, 10, 64)
						if err != nil {
							e.Logger().Warnf(`arg %v as uint: %v`, v, err)
							break
						}
						l = x
					}
					tv.Set(reflect.ValueOf(l))
				case reflect.Struct:
					if tvf, ok := tv.Interface().(FromConversion); ok {
						err := tvf.FromString(v)
						if err != nil {
							e.Logger().Warnf(`struct %v invoke FromString faild`, tvf)
						}
					} else if tv.Type().String() == `time.Time` {
						x, err := time.Parse(`2006-01-02 15:04:05.000 -0700`, v)
						if err != nil {
							x, err = time.Parse(`2006-01-02 15:04:05`, v)
							if err != nil {
								x, err = time.Parse(`2006-01-02`, v)
								if err != nil {
									e.Logger().Warnf(`unsupported time format %v, %v`, v, err)
									break
								}
							}
						}
						l = x
						tv.Set(reflect.ValueOf(l))
					} else {
						e.Logger().Warn(`can not set an struct which is not implement Fromconversion interface`)
					}
				case reflect.Ptr:
					e.Logger().Warn(`can not set an ptr of ptr`)
				case reflect.Slice, reflect.Array:
					tt := tv.Type().Elem()
					tk := tt.Kind()

					if tv.IsNil() {
						tv.Set(reflect.MakeSlice(tv.Type(), len(t), len(t)))
					}

					for i, s := range t {
						var err error
						switch tk {
						case reflect.Int, reflect.Int16, reflect.Int32, reflect.Int8, reflect.Int64:
							var v int64
							v, err = strconv.ParseInt(s, 10, tt.Bits())
							if err == nil {
								tv.Index(i).SetInt(v)
							}
						case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
							var v uint64
							v, err = strconv.ParseUint(s, 10, tt.Bits())
							if err == nil {
								tv.Index(i).SetUint(v)
							}
						case reflect.Float32, reflect.Float64:
							var v float64
							v, err = strconv.ParseFloat(s, tt.Bits())
							if err == nil {
								tv.Index(i).SetFloat(v)
							}
						case reflect.Bool:
							var v bool
							v, err = strconv.ParseBool(s)
							if err == nil {
								tv.Index(i).SetBool(v)
							}
						case reflect.String:
							tv.Index(i).SetString(s)
						case reflect.Complex64, reflect.Complex128:
							// TODO:
							err = fmt.Errorf(`unsupported slice element type %v`, tk.String())
						default:
							err = fmt.Errorf(`unsupported slice element type %v`, tk.String())
						}
						if err != nil {
							e.Logger().Warnf(`slice error: %v, %v`, name, err)
							break
						}
					}
				default:
					break
				}
				valid := tagfast.Value(tc, f, `valid`)
				if len(valid) > 0 {
					ok, err := validator.ValidSimple(name, fmt.Sprintf(`%v`, l), valid)
					if !ok {
						return validator.Errors[0]
					}
					if err != nil {
						e.Logger().Warn(err)
					}
				}
			}
		}
	}
	return nil
}

// FromConversion a struct implements this interface can be convert from request param to a struct
type FromConversion interface {
	FromString(content string) error
}

// ToConversion a struct implements this interface can be convert from struct to template variable
// Not Implemented
type ToConversion interface {
	ToString() string
}
<commit_msg>update<commit_after>package echo

import (
	"encoding/json"
	"encoding/xml"
	"errors"
	"fmt"
	"net/http"
	"reflect"
	"strconv"
	"strings"
	"time"

	"github.com/webx-top/tagfast"
	"github.com/webx-top/validation"
)

var DefaultHtmlFilter = func(v string) (r string) {
	return v
}

type (
	// Binder is the interface that wraps the Bind method.
	Binder interface {
		Bind(interface{}, Context) error
	}
	binder struct {
		*Echo
	}
)

func (b *binder) Bind(i interface{}, c Context) (err error) {
	r := c.Request()
	body := r.Body()
	if body == nil {
		err = NewHTTPError(http.StatusBadRequest, "Request body can't be nil")
		return
	}
	defer body.Close()
	ct := r.Header().Get(HeaderContentType)
	err = ErrUnsupportedMediaType
	if strings.HasPrefix(ct, MIMEApplicationJSON) {
		err = json.NewDecoder(body).Decode(i)
	} else if strings.HasPrefix(ct, MIMEApplicationXML) {
		err = xml.NewDecoder(body).Decode(i)
	} else if strings.HasPrefix(ct, MIMEApplicationForm) {
		err = b.structMap(i, r.PostForm().All())
	} else if strings.Contains(ct, MIMEMultipartForm) {
		err = b.structMap(i, r.Form().All())
	}
	return
}

// StructMap function mapping params to controller's properties
func (b *binder) structMap(m interface{}, data map[string][]string) error {
	return NamedStructMap(b.Echo, m, data, ``)
}

// SplitJSON user[name][test]
func SplitJSON(s string) ([]string, error) {
	res := make([]string, 0)
	var begin, end int
	var isleft bool
	for i, r := range s {
		switch r {
		case '[':
			isleft = true
			if i > 0 && s[i-1] != ']' {
				if begin == end {
					return nil, errors.New(`unknow character`)
				}
				res = append(res, s[begin:end+1])
			}
			begin = i + 1
			end = begin
		case ']':
			if !isleft {
				return nil, errors.New(`unknow character`)
			}
			isleft = false
			if begin != end {
				res = append(res, s[begin:end+1])
				begin = i + 1
				end = begin
			}
		default:
			end = i
		}
		if i == len(s)-1 && begin != end {
			res = append(res, s[begin:end+1])
		}
	}
	return res, nil
}

func NamedStructMap(e *Echo, m interface{}, data map[string][]string, topName string) error {
	vc := reflect.ValueOf(m)
	tc := reflect.TypeOf(m)

	switch tc.Kind() {
	case reflect.Struct:
	case reflect.Ptr:
		vc = vc.Elem()
		tc = tc.Elem()
	}
	var validator *validation.Validation
	for k, t := range data {

		if k == `` || k[0] == '_' {
			continue
		}

		if topName != `` {
			if !strings.HasPrefix(k, topName) {
				continue
			}
			k = k[len(topName)+1:]
		}

		v := t[0]
		names := strings.Split(k, `.`)
		var err error
		length := len(names)
		if length == 1 && strings.HasSuffix(k, `]`) {
			names, err = SplitJSON(k)
			if err != nil {
				e.Logger().Warnf(`Unrecognize form key %v %v`, k, err)
				continue
			}
			length = len(names)
		}
		value := vc
		typev := tc
		for i, name := range names {
			name = strings.Title(name)

			//不是最后一个元素
			if i != length-1 {
				if value.Kind() != reflect.Struct {
					e.Logger().Warnf(`arg error, value kind is %v`, value.Kind())
					break
				}
				value = value.FieldByName(name)
				if !value.IsValid() {
					e.Logger().Warnf(`(%v value is not valid %v)`, name, value)
					break
				}
				if !value.CanSet() {
					e.Logger().Warnf(`can not set %v -> %v`, name, value.Interface())
					break
				}
				if value.Kind() == reflect.Ptr {
					if value.IsNil() {
						value.Set(reflect.New(value.Type().Elem()))
					}
					value = value.Elem()
				}
				typev = value.Type()
				f, _ := typev.FieldByName(name)
				if tagfast.Value(tc, f, `form_options`) == `-` {
					break
				}
			} else {
				if value.Kind() != reflect.Struct {
					e.Logger().Warnf(`arg error, value %v kind is %v`, name, value.Kind())
					break
				}
				tv := value.FieldByName(name)
				if !tv.IsValid() {
					break
				}
				if !tv.CanSet() {
					e.Logger().Warnf(`can not set %v to %v`, k, tv)
					break
				}
				f, _ := typev.FieldByName(name)
				if tagfast.Value(tc, f, `form_options`) == `-` {
					break
				}
				if tv.Kind() == reflect.Ptr {
					tv.Set(reflect.New(tv.Type().Elem()))
					tv = tv.Elem()
				}

				var l interface{}
				switch k := tv.Kind(); k {
				case reflect.String:
					switch tagfast.Value(tc, f, `form_filter`) {
					case `html`:
						v = DefaultHtmlFilter(v)
					}
					l = v
					tv.Set(reflect.ValueOf(l))
				case reflect.Bool:
					l = (v != `false` && v != `0` && v != ``)
					tv.Set(reflect.ValueOf(l))
				case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32:
					dateformat := tagfast.Value(tc, f, `form_format`)
					if dateformat != `` {
						t, err := time.Parse(dateformat, v)
						if err != nil {
							e.Logger().Warnf(`arg %v as int: %v`, v, err)
							break
						}
						l = int(t.Unix())
					} else {
						x, err := strconv.Atoi(v)
						if err != nil {
							e.Logger().Warnf(`arg %v as int: %v`, v, err)
							break
						}
						l = x
					}
					tv.Set(reflect.ValueOf(l))
				case reflect.Int64:
					dateformat := tagfast.Value(tc, f, `form_format`)
					if dateformat != `` {
						t, err := time.Parse(dateformat, v)
						if err != nil {
							e.Logger().Warnf(`arg %v as int64: %v`, v, err)
							break
						}
						l = t.Unix()
					} else {
						x, err := strconv.ParseInt(v, 10, 64)
						if err != nil {
							e.Logger().Warnf(`arg %v as int64: %v`, v, err)
							break
						}
						l = x
					}
					tv.Set(reflect.ValueOf(l))
				case reflect.Float32, reflect.Float64:
					x, err := strconv.ParseFloat(v, 64)
					if err != nil {
						e.Logger().Warnf(`arg %v as float64: %v`, v, err)
						break
					}
					l = x
					tv.Set(reflect.ValueOf(l))
				case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
					dateformat := tagfast.Value(tc, f, `form_format`)
					if dateformat != `` {
						t, err := time.Parse(dateformat, v)
						if err != nil {
							e.Logger().Warnf(`arg %v as uint: %v`, v, err)
							break
						}
						l = uint64(t.Unix())
					} else {
						x, err := strconv.ParseUint(v, 10, 64)
						if err != nil {
							e.Logger().Warnf(`arg %v as uint: %v`, v, err)
							break
						}
						l = x
					}
					tv.Set(reflect.ValueOf(l))
				case reflect.Struct:
					if tvf, ok := tv.Interface().(FromConversion); ok {
						err := tvf.FromString(v)
						if err != nil {
							e.Logger().Warnf(`struct %v invoke FromString faild`, tvf)
						}
					} else if tv.Type().String() == `time.Time` {
						x, err := time.Parse(`2006-01-02 15:04:05.000 -0700`, v)
						if err != nil {
							x, err = time.Parse(`2006-01-02 15:04:05`, v)
							if err != nil {
								x, err = time.Parse(`2006-01-02`, v)
								if err != nil {
									e.Logger().Warnf(`unsupported time format %v, %v`, v, err)
									break
								}
							}
						}
						l = x
						tv.Set(reflect.ValueOf(l))
					} else {
						e.Logger().Warn(`can not set an struct which is not implement Fromconversion interface`)
					}
				case reflect.Ptr:
					e.Logger().Warn(`can not set an ptr of ptr`)
				case reflect.Slice, reflect.Array:
					tt := tv.Type().Elem()
					tk := tt.Kind()

					if tv.IsNil() {
						tv.Set(reflect.MakeSlice(tv.Type(), len(t), len(t)))
					}

					for i, s := range t {
						var err error
						switch tk {
						case reflect.Int, reflect.Int16, reflect.Int32, reflect.Int8, reflect.Int64:
							var v int64
							v, err = strconv.ParseInt(s, 10, tt.Bits())
							if err == nil {
								tv.Index(i).SetInt(v)
							}
						case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
							var v uint64
							v, err = strconv.ParseUint(s, 10, tt.Bits())
							if err == nil {
								tv.Index(i).SetUint(v)
							}
						case reflect.Float32, reflect.Float64:
							var v float64
							v, err = strconv.ParseFloat(s, tt.Bits())
							if err == nil {
								tv.Index(i).SetFloat(v)
							}
						case reflect.Bool:
							var v bool
							v, err = strconv.ParseBool(s)
							if err == nil {
								tv.Index(i).SetBool(v)
							}
						case reflect.String:
							tv.Index(i).SetString(s)
						case reflect.Complex64, reflect.Complex128:
							// TODO:
							err = fmt.Errorf(`unsupported slice element type %v`, tk.String())
						default:
							err = fmt.Errorf(`unsupported slice element type %v`, tk.String())
						}
						if err != nil {
							e.Logger().Warnf(`slice error: %v, %v`, name, err)
							break
						}
					}
				default:
					break
				}
				valid := tagfast.Value(tc, f, `valid`)
				if len(valid) > 0 {
					if validator == nil {
						validator = validation.New()
					}
					ok, err := validator.ValidSimple(name, fmt.Sprintf(`%v`, l), valid)
					if !ok {
						return validator.Errors[0]
					}
					if err != nil {
						e.Logger().Warn(err)
					}
				}
			}
		}
	}
	return nil
}

// FromConversion a struct implements this interface can be convert from request param to a struct
type FromConversion interface {
	FromString(content string) error
}

// ToConversion a struct implements this interface can be convert from struct to template variable
// Not Implemented
type ToConversion interface {
	ToString() string
}
<|endoftext|>


<commit_before>package persist

import (
	"bytes"
	"crypto/rand"
	"os"
	"path/filepath"
	"testing"

	"github.com/NebulousLabs/Sia/build"
)

// TestIntegrationRandomSuffix checks that the random suffix creator creates
// valid files.
func TestIntegrationRandomSuffix(t *testing.T) {
	tmpDir := build.TempDir(persistDir, "TestIntegrationRandomSuffix")
	err := os.MkdirAll(tmpDir, 0700)
	if err != nil {
		t.Fatal(err)
	}
	for i := 0; i < 100; i++ {
		suffix := RandomSuffix()
		filename := filepath.Join(tmpDir, "test file - "+suffix+".nil")
		file, err := os.Create(filename)
		if err != nil {
			t.Fatal(err)
		}
		file.Close()
	}
}

// TestAbsolutePathSafeFile tests creating and committing safe files with
// absolute paths.
func TestAbsolutePathSafeFile(t *testing.T) {
	tmpDir := build.TempDir(persistDir, "TestAbsolutePathSafeFile")
	err := os.MkdirAll(tmpDir, 0700)
	if err != nil {
		t.Fatal(err)
	}
	absPath := filepath.Join(tmpDir, "test")

	// Create safe file.
	sf, err := NewSafeFile(absPath)
	defer sf.Close()
	if err != nil {
		t.Fatal(err)
	}

	// Check that the name of the file is not equal to the final name of the
	// file.
	if sf.Name() == absPath {
		t.Errorf("safeFile created with filename: %s has temporary filename that is equivalent to finalName: %s\n", absPath, sf.Name())
	}

	// Write random data to the file and commit.
	data := make([]byte, 10)
	rand.Read(data)
	_, err = sf.Write(data)
	if err != nil {
		t.Fatal(err)
	}
	err = sf.Commit()
	if err != nil {
		t.Fatal(err)
	}

	// Check that the file exists and has same data that was written to it.
	f, err := os.Open(absPath)
	defer f.Close()
	if err != nil {
		t.Fatal(err)
	}
	dataRead := make([]byte, 11)
	n, err := f.Read(dataRead)
	if err != nil {
		t.Fatal(err)
	}
	dataRead = dataRead[:n]
	if !bytes.Equal(data, dataRead) {
		t.Fatalf("Committed file has different data than was written to it: expected %v, got %v\n", data, dataRead)
	}
}

// TestRelativePathSafeFile tests creating and committing safe files with
// relative paths. Relative paths are testing to test that calling os.Chdir
// inbetween creating and committing a safe file doesn't affect the safe file's
// final path. The relative path tested is relative to the working directory.
func TestRelativePathSafeFile(t *testing.T) {
	tmpDir := build.TempDir(persistDir, "TestRelativePathSafeFile")
	err := os.MkdirAll(tmpDir, 0700)
	if err != nil {
		t.Fatal(err)
	}
	absPath := filepath.Join(tmpDir, "test")
	wd, err := os.Getwd()
	if err != nil {
		t.Fatal(err)
	}
	relPath, err := filepath.Rel(wd, absPath)

	// Create safe file.
	sf, err := NewSafeFile(relPath)
	defer sf.Close()
	if err != nil {
		t.Fatal(err)
	}

	// Check that the path of the file is not equal to the final path of the
	// file.
	if sf.Name() == absPath {
		t.Errorf("safeFile created with filename: %s has temporary filename that is equivalent to finalName: %s\n", absPath, sf.Name())
	}

	// Write random data to the file.
	data := make([]byte, 10)
	rand.Read(data)
	_, err = sf.Write(data)
	if err != nil {
		t.Fatal(err)
	}

	// Change directories and commit.
	tmpChdir := build.TempDir(persistDir, "TestRelativePathSafeFileTmpChdir")
	err = os.MkdirAll(tmpChdir, 0700)
	if err != nil {
		t.Fatal(err)
	}
	os.Chdir(tmpChdir)
	defer os.Chdir(wd)
	err = sf.Commit()
	if err != nil {
		t.Fatal(err)
	}

	// Check that the file exists and has same data that was written to it.
	f, err := os.Open(absPath)
	defer f.Close()
	if err != nil {
		t.Fatal(err)
	}
	dataRead := make([]byte, 11)
	n, err := f.Read(dataRead)
	if err != nil {
		t.Fatal(err)
	}
	dataRead = dataRead[:n]
	if !bytes.Equal(data, dataRead) {
		t.Fatalf("Committed file has different data than was written to it: expected %v, got %v\n", data, dataRead)
	}
}
<commit_msg>Cleanup safeFile tests<commit_after>package persist

import (
	"bytes"
	"crypto/rand"
	"io/ioutil"
	"os"
	"path/filepath"
	"testing"

	"github.com/NebulousLabs/Sia/build"
)

// TestIntegrationRandomSuffix checks that the random suffix creator creates
// valid files.
func TestIntegrationRandomSuffix(t *testing.T) {
	tmpDir := build.TempDir(persistDir, "TestIntegrationRandomSuffix")
	err := os.MkdirAll(tmpDir, 0700)
	if err != nil {
		t.Fatal(err)
	}
	for i := 0; i < 100; i++ {
		suffix := RandomSuffix()
		filename := filepath.Join(tmpDir, "test file - "+suffix+".nil")
		file, err := os.Create(filename)
		if err != nil {
			t.Fatal(err)
		}
		file.Close()
	}
}

// TestAbsolutePathSafeFile tests creating and committing safe files with
// absolute paths.
func TestAbsolutePathSafeFile(t *testing.T) {
	tmpDir := build.TempDir(persistDir, "TestAbsolutePathSafeFile")
	err := os.MkdirAll(tmpDir, 0700)
	if err != nil {
		t.Fatal(err)
	}
	absPath := filepath.Join(tmpDir, "test")

	// Create safe file.
	sf, err := NewSafeFile(absPath)
	defer sf.Close()
	if err != nil {
		t.Fatal(err)
	}

	// Check that the name of the file is not equal to the final name of the
	// file.
	if sf.Name() == absPath {
		t.Errorf("safeFile created with filename: %s has temporary filename that is equivalent to finalName: %s\n", absPath, sf.Name())
	}

	// Write random data to the file and commit.
	data := make([]byte, 10)
	rand.Read(data)
	_, err = sf.Write(data)
	if err != nil {
		t.Fatal(err)
	}
	err = sf.Commit()
	if err != nil {
		t.Fatal(err)
	}

	// Check that the file exists and has same data that was written to it.
	dataRead, err := ioutil.ReadFile(absPath)
	if err != nil {
		t.Fatal(err)
	}
	if !bytes.Equal(data, dataRead) {
		t.Fatalf("Committed file has different data than was written to it: expected %v, got %v\n", data, dataRead)
	}
}

// TestRelativePathSafeFile tests creating and committing safe files with
// relative paths. Relative paths are testing to test that calling os.Chdir
// inbetween creating and committing a safe file doesn't affect the safe file's
// final path. The relative path tested is relative to the working directory.
func TestRelativePathSafeFile(t *testing.T) {
	tmpDir := build.TempDir(persistDir, "TestRelativePathSafeFile")
	err := os.MkdirAll(tmpDir, 0700)
	if err != nil {
		t.Fatal(err)
	}
	absPath := filepath.Join(tmpDir, "test")
	wd, err := os.Getwd()
	if err != nil {
		t.Fatal(err)
	}
	relPath, err := filepath.Rel(wd, absPath)

	// Create safe file.
	sf, err := NewSafeFile(relPath)
	defer sf.Close()
	if err != nil {
		t.Fatal(err)
	}

	// Check that the path of the file is not equal to the final path of the
	// file.
	if sf.Name() == absPath {
		t.Errorf("safeFile created with filename: %s has temporary filename that is equivalent to finalName: %s\n", absPath, sf.Name())
	}

	// Write random data to the file.
	data := make([]byte, 10)
	rand.Read(data)
	_, err = sf.Write(data)
	if err != nil {
		t.Fatal(err)
	}

	// Change directories and commit.
	tmpChdir := build.TempDir(persistDir, "TestRelativePathSafeFileTmpChdir")
	err = os.MkdirAll(tmpChdir, 0700)
	if err != nil {
		t.Fatal(err)
	}
	os.Chdir(tmpChdir)
	defer os.Chdir(wd)
	err = sf.Commit()
	if err != nil {
		t.Fatal(err)
	}

	// Check that the file exists and has same data that was written to it.
	dataRead, err := ioutil.ReadFile(absPath)
	if err != nil {
		t.Fatal(err)
	}
	if !bytes.Equal(data, dataRead) {
		t.Fatalf("Committed file has different data than was written to it: expected %v, got %v\n", data, dataRead)
	}
}
<|endoftext|>


<commit_before>package container

import (
	"bytes"
	"fmt"
	"github.com/containrrr/watchtower/pkg/registry"
	"io/ioutil"
	"strings"
	"time"

	t "github.com/containrrr/watchtower/pkg/types"
	"github.com/docker/docker/api/types"
	"github.com/docker/docker/api/types/container"
	"github.com/docker/docker/api/types/filters"
	"github.com/docker/docker/api/types/network"
	sdkClient "github.com/docker/docker/client"
	log "github.com/sirupsen/logrus"
	"golang.org/x/net/context"
)

const defaultStopSignal = "SIGTERM"

// A Client is the interface through which watchtower interacts with the
// Docker API.
type Client interface {
	ListContainers(t.Filter) ([]Container, error)
	GetContainer(containerID string) (Container, error)
	StopContainer(Container, time.Duration) error
	StartContainer(Container) (string, error)
	RenameContainer(Container, string) error
	IsContainerStale(Container) (bool, error)
	ExecuteCommand(containerID string, command string, timeout int) error
	RemoveImageByID(string) error
}

// NewClient returns a new Client instance which can be used to interact with
// the Docker API.
// The client reads its configuration from the following environment variables:
//  * DOCKER_HOST			the docker-engine host to send api requests to
//  * DOCKER_TLS_VERIFY		whether to verify tls certificates
//  * DOCKER_API_VERSION	the minimum docker api version to work with
func NewClient(pullImages bool, includeStopped bool, reviveStopped bool, removeVolumes bool) Client {
	cli, err := sdkClient.NewClientWithOpts(sdkClient.FromEnv)

	if err != nil {
		log.Fatalf("Error instantiating Docker client: %s", err)
	}

	return dockerClient{
		api:            cli,
		pullImages:     pullImages,
		removeVolumes:  removeVolumes,
		includeStopped: includeStopped,
		reviveStopped:  reviveStopped,
	}
}

type dockerClient struct {
	api            sdkClient.CommonAPIClient
	pullImages     bool
	removeVolumes  bool
	includeStopped bool
	reviveStopped  bool
}

func (client dockerClient) ListContainers(fn t.Filter) ([]Container, error) {
	cs := []Container{}
	bg := context.Background()

	if client.includeStopped {
		log.Debug("Retrieving containers including stopped and exited")
	} else {
		log.Debug("Retrieving running containers")
	}

	filter := client.createListFilter()
	containers, err := client.api.ContainerList(
		bg,
		types.ContainerListOptions{
			Filters: filter,
		})

	if err != nil {
		return nil, err
	}

	for _, runningContainer := range containers {

		c, err := client.GetContainer(runningContainer.ID)
		if err != nil {
			return nil, err
		}

		if fn(c) {
			cs = append(cs, c)
		}
	}

	return cs, nil
}

func (client dockerClient) createListFilter() filters.Args {
	filterArgs := filters.NewArgs()
	filterArgs.Add("status", "running")

	if client.includeStopped {
		filterArgs.Add("status", "created")
		filterArgs.Add("status", "exited")
	}

	return filterArgs
}

func (client dockerClient) GetContainer(containerID string) (Container, error) {
	bg := context.Background()

	containerInfo, err := client.api.ContainerInspect(bg, containerID)
	if err != nil {
		return Container{}, err
	}

	imageInfo, _, err := client.api.ImageInspectWithRaw(bg, containerInfo.Image)
	if err != nil {
		return Container{}, err
	}

	container := Container{containerInfo: &containerInfo, imageInfo: &imageInfo}
	return container, nil
}

func (client dockerClient) StopContainer(c Container, timeout time.Duration) error {
	bg := context.Background()
	signal := c.StopSignal()
	if signal == "" {
		signal = defaultStopSignal
	}

	if c.IsRunning() {
		log.Infof("Stopping %s (%s) with %s", c.Name(), c.ID(), signal)
		if err := client.api.ContainerKill(bg, c.ID(), signal); err != nil {
			return err
		}
	}

	// TODO: This should probably be checked.
	_ = client.waitForStopOrTimeout(c, timeout)

	if c.containerInfo.HostConfig.AutoRemove {
		log.Debugf("AutoRemove container %s, skipping ContainerRemove call.", c.ID())
	} else {
		log.Debugf("Removing container %s", c.ID())

		if err := client.api.ContainerRemove(bg, c.ID(), types.ContainerRemoveOptions{Force: true, RemoveVolumes: client.removeVolumes}); err != nil {
			return err
		}
	}

	// Wait for container to be removed. In this case an error is a good thing
	if err := client.waitForStopOrTimeout(c, timeout); err == nil {
		return fmt.Errorf("container %s (%s) could not be removed", c.Name(), c.ID())
	}

	return nil
}

func (client dockerClient) StartContainer(c Container) (string, error) {
	bg := context.Background()
	config := c.runtimeConfig()
	hostConfig := c.hostConfig()
	networkConfig := &network.NetworkingConfig{EndpointsConfig: c.containerInfo.NetworkSettings.Networks}
	// simpleNetworkConfig is a networkConfig with only 1 network.
	// see: https://github.com/docker/docker/issues/29265
	simpleNetworkConfig := func() *network.NetworkingConfig {
		oneEndpoint := make(map[string]*network.EndpointSettings)
		for k, v := range networkConfig.EndpointsConfig {
			oneEndpoint[k] = v
			// we only need 1
			break
		}
		return &network.NetworkingConfig{EndpointsConfig: oneEndpoint}
	}()

	name := c.Name()

	log.Infof("Creating %s", name)
	createdContainer, err := client.api.ContainerCreate(bg, config, hostConfig, simpleNetworkConfig, name)
	if err != nil {
		return "", err
	}

	if !(hostConfig.NetworkMode.IsHost()) {

		for k := range simpleNetworkConfig.EndpointsConfig {
			err = client.api.NetworkDisconnect(bg, k, createdContainer.ID, true)
			if err != nil {
				return "", err
			}
		}

		for k, v := range networkConfig.EndpointsConfig {
			err = client.api.NetworkConnect(bg, k, createdContainer.ID, v)
			if err != nil {
				return "", err
			}
		}

	}

	if !c.IsRunning() && !client.reviveStopped {
		return createdContainer.ID, nil
	}

	return createdContainer.ID, client.doStartContainer(bg, c, createdContainer)

}

func (client dockerClient) doStartContainer(bg context.Context, c Container, creation container.ContainerCreateCreatedBody) error {
	name := c.Name()

	log.Debugf("Starting container %s (%s)", name, creation.ID)
	err := client.api.ContainerStart(bg, creation.ID, types.ContainerStartOptions{})
	if err != nil {
		return err
	}
	return nil
}

func (client dockerClient) RenameContainer(c Container, newName string) error {
	bg := context.Background()
	log.Debugf("Renaming container %s (%s) to %s", c.Name(), c.ID(), newName)
	return client.api.ContainerRename(bg, c.ID(), newName)
}

func (client dockerClient) IsContainerStale(container Container) (bool, error) {
	ctx := context.Background()

	if !client.pullImages {
		log.Debugf("Skipping image pull.")
	} else if err := client.PullImage(ctx, container); err != nil {
		return false, err
	}

	return client.HasNewImage(ctx, container)
}

func (client dockerClient) HasNewImage(ctx context.Context, container Container) (bool, error) {
	oldImageID := container.imageInfo.ID
	imageName := container.ImageName()

	newImageInfo, _, err := client.api.ImageInspectWithRaw(ctx, imageName)
	if err != nil {
		return false, err
	}

	if newImageInfo.ID == oldImageID {
		log.Debugf("No new images found for %s", container.Name())
		return false, nil
	}

	log.Infof("Found new %s image (%s)", imageName, newImageInfo.ID)
	return true, nil
}

func (client dockerClient) PullImage(ctx context.Context, container Container) error {
	containerName := container.Name()
	imageName := container.ImageName()
	log.Debugf("Pulling %s for %s", imageName, containerName)

	opts, err := registry.GetPullOptions(imageName)
	if err != nil {
		log.Debugf("Error loading authentication credentials %s", err)
		return err
	}

	response, err := client.api.ImagePull(ctx, imageName, opts)
	if err != nil {
		log.Debugf("Error pulling image %s, %s", imageName, err)
		return err
	}

	defer response.Close()
	// the pull request will be aborted prematurely unless the response is read
	if _, err = ioutil.ReadAll(response); err != nil {
		log.Error(err)
		return err
	}
	return nil
}

func (client dockerClient) RemoveImageByID(id string) error {
	log.Infof("Removing image %s", id)

	_, err := client.api.ImageRemove(
		context.Background(),
		id,
		types.ImageRemoveOptions{
			Force: true,
		})

	return err
}

func (client dockerClient) ExecuteCommand(containerID string, command string, timeout int) error {
	bg := context.Background()

	// Create the exec
	execConfig := types.ExecConfig{
		Tty:    true,
		Detach: false,
		Cmd:    []string{"sh", "-c", command},
	}

	exec, err := client.api.ContainerExecCreate(bg, containerID, execConfig)
	if err != nil {
		return err
	}

	response, attachErr := client.api.ContainerExecAttach(bg, exec.ID, types.ExecStartCheck{
		Tty:    true,
		Detach: false,
	})
	if attachErr != nil {
		log.Errorf("Failed to extract command exec logs: %v", attachErr)
	}

	// Run the exec
	execStartCheck := types.ExecStartCheck{Detach: false, Tty: true}
	err = client.api.ContainerExecStart(bg, exec.ID, execStartCheck)
	if err != nil {
		return err
	}

	var output string
	if attachErr == nil {
		defer response.Close()
		var writer bytes.Buffer
		written, err := writer.ReadFrom(response.Reader)
		if err != nil {
			log.Error(err)
		} else if written > 0 {
			output = strings.TrimSpace(writer.String())
		}
	}

	// Inspect the exec to get the exit code and print a message if the
	// exit code is not success.
	err = client.waitForExecOrTimeout(bg, exec.ID, output, timeout)
	if err != nil {
		return err
	}

	return nil
}

func (client dockerClient) waitForExecOrTimeout(bg context.Context, ID string, execOutput string, timeout int) error {
	var ctx context.Context
	var cancel context.CancelFunc

	if timeout > 0 {
		ctx, cancel = context.WithTimeout(bg, time.Duration(timeout)*time.Minute)
		defer cancel()
	} else {
		ctx = bg
	}

	for {
		execInspect, err := client.api.ContainerExecInspect(ctx, ID)

		log.WithFields(log.Fields{
			"exit-code": execInspect.ExitCode,
			"exec-id":   execInspect.ExecID,
			"running":   execInspect.Running,
		}).Debug("Awaiting timeout or completion")

		if err != nil {
			return err
		}
		if execInspect.Running == true {
			time.Sleep(1 * time.Second)
			continue
		}
		if len(execOutput) > 0 {
			log.Infof("Command output:\n%v", execOutput)
		}
		if execInspect.ExitCode > 0 {
			log.Errorf("Command exited with code %v.", execInspect.ExitCode)
			log.Error(execOutput)
		}
		break
	}
	return nil
}

func (client dockerClient) waitForStopOrTimeout(c Container, waitTime time.Duration) error {
	bg := context.Background()
	timeout := time.After(waitTime)

	for {
		select {
		case <-timeout:
			return nil
		default:
			if ci, err := client.api.ContainerInspect(bg, c.ID()); err != nil {
				return err
			} else if !ci.State.Running {
				return nil
			}
		}
		time.Sleep(1 * time.Second)
	}
}
<commit_msg>Image of running container no longer needed locally (#571)<commit_after>package container

import (
	"bytes"
	"fmt"
	"github.com/containrrr/watchtower/pkg/registry"
	"io/ioutil"
	"strings"
	"time"

	t "github.com/containrrr/watchtower/pkg/types"
	"github.com/docker/docker/api/types"
	"github.com/docker/docker/api/types/container"
	"github.com/docker/docker/api/types/filters"
	"github.com/docker/docker/api/types/network"
	sdkClient "github.com/docker/docker/client"
	log "github.com/sirupsen/logrus"
	"golang.org/x/net/context"
)

const defaultStopSignal = "SIGTERM"

// A Client is the interface through which watchtower interacts with the
// Docker API.
type Client interface {
	ListContainers(t.Filter) ([]Container, error)
	GetContainer(containerID string) (Container, error)
	StopContainer(Container, time.Duration) error
	StartContainer(Container) (string, error)
	RenameContainer(Container, string) error
	IsContainerStale(Container) (bool, error)
	ExecuteCommand(containerID string, command string, timeout int) error
	RemoveImageByID(string) error
}

// NewClient returns a new Client instance which can be used to interact with
// the Docker API.
// The client reads its configuration from the following environment variables:
//  * DOCKER_HOST			the docker-engine host to send api requests to
//  * DOCKER_TLS_VERIFY		whether to verify tls certificates
//  * DOCKER_API_VERSION	the minimum docker api version to work with
func NewClient(pullImages bool, includeStopped bool, reviveStopped bool, removeVolumes bool) Client {
	cli, err := sdkClient.NewClientWithOpts(sdkClient.FromEnv)

	if err != nil {
		log.Fatalf("Error instantiating Docker client: %s", err)
	}

	return dockerClient{
		api:            cli,
		pullImages:     pullImages,
		removeVolumes:  removeVolumes,
		includeStopped: includeStopped,
		reviveStopped:  reviveStopped,
	}
}

type dockerClient struct {
	api            sdkClient.CommonAPIClient
	pullImages     bool
	removeVolumes  bool
	includeStopped bool
	reviveStopped  bool
}

func (client dockerClient) ListContainers(fn t.Filter) ([]Container, error) {
	cs := []Container{}
	bg := context.Background()

	if client.includeStopped {
		log.Debug("Retrieving containers including stopped and exited")
	} else {
		log.Debug("Retrieving running containers")
	}

	filter := client.createListFilter()
	containers, err := client.api.ContainerList(
		bg,
		types.ContainerListOptions{
			Filters: filter,
		})

	if err != nil {
		return nil, err
	}

	for _, runningContainer := range containers {

		c, err := client.GetContainer(runningContainer.ID)
		if err != nil {
			return nil, err
		}

		if fn(c) {
			cs = append(cs, c)
		}
	}

	return cs, nil
}

func (client dockerClient) createListFilter() filters.Args {
	filterArgs := filters.NewArgs()
	filterArgs.Add("status", "running")

	if client.includeStopped {
		filterArgs.Add("status", "created")
		filterArgs.Add("status", "exited")
	}

	return filterArgs
}

func (client dockerClient) GetContainer(containerID string) (Container, error) {
	bg := context.Background()

	containerInfo, err := client.api.ContainerInspect(bg, containerID)
	if err != nil {
		return Container{}, err
	}

	container := Container{containerInfo: &containerInfo}
	return container, nil
}

func (client dockerClient) StopContainer(c Container, timeout time.Duration) error {
	bg := context.Background()
	signal := c.StopSignal()
	if signal == "" {
		signal = defaultStopSignal
	}

	if c.IsRunning() {
		log.Infof("Stopping %s (%s) with %s", c.Name(), c.ID(), signal)
		if err := client.api.ContainerKill(bg, c.ID(), signal); err != nil {
			return err
		}
	}

	// TODO: This should probably be checked.
	_ = client.waitForStopOrTimeout(c, timeout)

	if c.containerInfo.HostConfig.AutoRemove {
		log.Debugf("AutoRemove container %s, skipping ContainerRemove call.", c.ID())
	} else {
		log.Debugf("Removing container %s", c.ID())

		if err := client.api.ContainerRemove(bg, c.ID(), types.ContainerRemoveOptions{Force: true, RemoveVolumes: client.removeVolumes}); err != nil {
			return err
		}
	}

	// Wait for container to be removed. In this case an error is a good thing
	if err := client.waitForStopOrTimeout(c, timeout); err == nil {
		return fmt.Errorf("container %s (%s) could not be removed", c.Name(), c.ID())
	}

	return nil
}

func (client dockerClient) StartContainer(c Container) (string, error) {
	bg := context.Background()
	config := c.runtimeConfig()
	hostConfig := c.hostConfig()
	networkConfig := &network.NetworkingConfig{EndpointsConfig: c.containerInfo.NetworkSettings.Networks}
	// simpleNetworkConfig is a networkConfig with only 1 network.
	// see: https://github.com/docker/docker/issues/29265
	simpleNetworkConfig := func() *network.NetworkingConfig {
		oneEndpoint := make(map[string]*network.EndpointSettings)
		for k, v := range networkConfig.EndpointsConfig {
			oneEndpoint[k] = v
			// we only need 1
			break
		}
		return &network.NetworkingConfig{EndpointsConfig: oneEndpoint}
	}()

	name := c.Name()

	log.Infof("Creating %s", name)
	createdContainer, err := client.api.ContainerCreate(bg, config, hostConfig, simpleNetworkConfig, name)
	if err != nil {
		return "", err
	}

	if !(hostConfig.NetworkMode.IsHost()) {

		for k := range simpleNetworkConfig.EndpointsConfig {
			err = client.api.NetworkDisconnect(bg, k, createdContainer.ID, true)
			if err != nil {
				return "", err
			}
		}

		for k, v := range networkConfig.EndpointsConfig {
			err = client.api.NetworkConnect(bg, k, createdContainer.ID, v)
			if err != nil {
				return "", err
			}
		}

	}

	if !c.IsRunning() && !client.reviveStopped {
		return createdContainer.ID, nil
	}

	return createdContainer.ID, client.doStartContainer(bg, c, createdContainer)

}

func (client dockerClient) doStartContainer(bg context.Context, c Container, creation container.ContainerCreateCreatedBody) error {
	name := c.Name()

	log.Debugf("Starting container %s (%s)", name, creation.ID)
	err := client.api.ContainerStart(bg, creation.ID, types.ContainerStartOptions{})
	if err != nil {
		return err
	}
	return nil
}

func (client dockerClient) RenameContainer(c Container, newName string) error {
	bg := context.Background()
	log.Debugf("Renaming container %s (%s) to %s", c.Name(), c.ID(), newName)
	return client.api.ContainerRename(bg, c.ID(), newName)
}

func (client dockerClient) IsContainerStale(container Container) (bool, error) {
	ctx := context.Background()

	if !client.pullImages {
		log.Debugf("Skipping image pull.")
	} else if err := client.PullImage(ctx, container); err != nil {
		return false, err
	}

	return client.HasNewImage(ctx, container)
}

func (client dockerClient) HasNewImage(ctx context.Context, container Container) (bool, error) {
	oldImageID := container.containerInfo.ContainerJSONBase.Image
	imageName := container.ImageName()

	newImageInfo, _, err := client.api.ImageInspectWithRaw(ctx, imageName)
	if err != nil {
		return false, err
	}

	if newImageInfo.ID == oldImageID {
		log.Debugf("No new images found for %s", container.Name())
		return false, nil
	}

	log.Infof("Found new %s image (%s)", imageName, newImageInfo.ID)
	return true, nil
}

func (client dockerClient) PullImage(ctx context.Context, container Container) error {
	containerName := container.Name()
	imageName := container.ImageName()
	log.Debugf("Pulling %s for %s", imageName, containerName)

	opts, err := registry.GetPullOptions(imageName)
	if err != nil {
		log.Debugf("Error loading authentication credentials %s", err)
		return err
	}

	response, err := client.api.ImagePull(ctx, imageName, opts)
	if err != nil {
		log.Debugf("Error pulling image %s, %s", imageName, err)
		return err
	}

	defer response.Close()
	// the pull request will be aborted prematurely unless the response is read
	if _, err = ioutil.ReadAll(response); err != nil {
		log.Error(err)
		return err
	}
	return nil
}

func (client dockerClient) RemoveImageByID(id string) error {
	log.Infof("Removing image %s", id)

	_, err := client.api.ImageRemove(
		context.Background(),
		id,
		types.ImageRemoveOptions{
			Force: true,
		})

	return err
}

func (client dockerClient) ExecuteCommand(containerID string, command string, timeout int) error {
	bg := context.Background()

	// Create the exec
	execConfig := types.ExecConfig{
		Tty:    true,
		Detach: false,
		Cmd:    []string{"sh", "-c", command},
	}

	exec, err := client.api.ContainerExecCreate(bg, containerID, execConfig)
	if err != nil {
		return err
	}

	response, attachErr := client.api.ContainerExecAttach(bg, exec.ID, types.ExecStartCheck{
		Tty:    true,
		Detach: false,
	})
	if attachErr != nil {
		log.Errorf("Failed to extract command exec logs: %v", attachErr)
	}

	// Run the exec
	execStartCheck := types.ExecStartCheck{Detach: false, Tty: true}
	err = client.api.ContainerExecStart(bg, exec.ID, execStartCheck)
	if err != nil {
		return err
	}

	var output string
	if attachErr == nil {
		defer response.Close()
		var writer bytes.Buffer
		written, err := writer.ReadFrom(response.Reader)
		if err != nil {
			log.Error(err)
		} else if written > 0 {
			output = strings.TrimSpace(writer.String())
		}
	}

	// Inspect the exec to get the exit code and print a message if the
	// exit code is not success.
	err = client.waitForExecOrTimeout(bg, exec.ID, output, timeout)
	if err != nil {
		return err
	}

	return nil
}

func (client dockerClient) waitForExecOrTimeout(bg context.Context, ID string, execOutput string, timeout int) error {
	var ctx context.Context
	var cancel context.CancelFunc

	if timeout > 0 {
		ctx, cancel = context.WithTimeout(bg, time.Duration(timeout)*time.Minute)
		defer cancel()
	} else {
		ctx = bg
	}

	for {
		execInspect, err := client.api.ContainerExecInspect(ctx, ID)

		log.WithFields(log.Fields{
			"exit-code": execInspect.ExitCode,
			"exec-id":   execInspect.ExecID,
			"running":   execInspect.Running,
		}).Debug("Awaiting timeout or completion")

		if err != nil {
			return err
		}
		if execInspect.Running == true {
			time.Sleep(1 * time.Second)
			continue
		}
		if len(execOutput) > 0 {
			log.Infof("Command output:\n%v", execOutput)
		}
		if execInspect.ExitCode > 0 {
			log.Errorf("Command exited with code %v.", execInspect.ExitCode)
			log.Error(execOutput)
		}
		break
	}
	return nil
}

func (client dockerClient) waitForStopOrTimeout(c Container, waitTime time.Duration) error {
	bg := context.Background()
	timeout := time.After(waitTime)

	for {
		select {
		case <-timeout:
			return nil
		default:
			if ci, err := client.api.ContainerInspect(bg, c.ID()); err != nil {
				return err
			} else if !ci.State.Running {
				return nil
			}
		}
		time.Sleep(1 * time.Second)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"github.com/owulveryck/flue"
	"log"
	"os"
)

func main() {
	//	flue.ParseTopology()
	//	flue.ParseNode()
	if len(os.Args) < 2 {
		log.Println("We are a server...")
		flue.Server("/tmp/mysocket.sock")
	} else {
		log.Println("We are a client...")
		command := &flue.RemoteCommandClient{
			Cmd:    os.Args[1],
			Args:   os.Args[2:],
			Stdin:  os.Stdin,
			Stdout: os.Stdout,
			Stderr: os.Stderr,
			//StatusChan: remoteSender,
		}
		flue.Client(command, "/tmp/mysocket.sock")
	}

}
<commit_msg>Working with the notion of uuid...<commit_after>package main

import (
	"github.com/owulveryck/flue"
	"log"
	"os"
	"github.com/nu7hatch/gouuid"
)

func main() {
	//	flue.ParseTopology()
	//	flue.ParseNode()

	if len(os.Args) < 2 {
		uuid, err := uuid.NewV4()
		log.Println("We are a server, uuid is: ", string(uuid[:]))
		flue.Server("/tmp/mysocket.sock")
	} else {
	    uuid
		log.Println("We are a client...")
		command := &flue.RemoteCommandClient{
			Cmd:    os.Args[1],
			Args:   os.Args[2:],
			Stdin:  os.Stdin,
			Stdout: os.Stdout,
			Stderr: os.Stderr,
			//StatusChan: remoteSender,
		}
		flue.Client(command, "/tmp/mysocket.sock")
	}

}
<|endoftext|>


<commit_before>package specs

import "os"

// LinuxStateDirectory holds the container's state information
const LinuxStateDirectory = "/run/opencontainer/containers"

// LinuxRuntimeSpec is the full specification for linux containers.
type LinuxRuntimeSpec struct {
	RuntimeSpec
	// LinuxRuntime is platform specific configuration for linux based containers.
	Linux LinuxRuntime `json:"linux"`
}

// LinuxRuntime hosts the Linux-only runtime information
type LinuxRuntime struct {
	// UIDMapping specifies user mappings for supporting user namespaces on linux.
	UIDMappings []IDMapping `json:"uidMappings"`
	// GIDMapping specifies group mappings for supporting user namespaces on linux.
	GIDMappings []IDMapping `json:"gidMappings"`
	// Rlimits specifies rlimit options to apply to the container's process.
	Rlimits []Rlimit `json:"rlimits"`
	// Sysctl are a set of key value pairs that are set for the container on start
	Sysctl map[string]string `json:"sysctl"`
	// Resources contain cgroup information for handling resource constraints
	// for the container
	Resources *Resources `json:"resources"`
	// CgroupsPath specifies the path to cgroups that are created and/or joined by the container.
	// The path is expected to be relative to the cgroups mountpoint.
	// If resources are specified, the cgroups at CgroupsPath will be updated based on resources.
	CgroupsPath string `json:"cgroupsPath"`
	// Namespaces contains the namespaces that are created and/or joined by the container
	Namespaces []Namespace `json:"namespaces"`
	// Devices are a list of device nodes that are created and enabled for the container
	Devices []Device `json:"devices"`
	// ApparmorProfile specified the apparmor profile for the container.
	ApparmorProfile string `json:"apparmorProfile"`
	// SelinuxProcessLabel specifies the selinux context that the container process is run as.
	SelinuxProcessLabel string `json:"selinuxProcessLabel"`
	// Seccomp specifies the seccomp security settings for the container.
	Seccomp Seccomp `json:"seccomp"`
	// RootfsPropagation is the rootfs mount propagation mode for the container
	RootfsPropagation string `json:"rootfsPropagation"`
}

// Namespace is the configuration for a linux namespace
type Namespace struct {
	// Type is the type of Linux namespace
	Type NamespaceType `json:"type"`
	// Path is a path to an existing namespace persisted on disk that can be joined
	// and is of the same type
	Path string `json:"path"`
}

// NamespaceType is one of the linux namespaces
type NamespaceType string

const (
	// PIDNamespace for isolating process IDs
	PIDNamespace NamespaceType = "pid"
	// NetworkNamespace for isolating network devices, stacks, ports, etc
	NetworkNamespace = "network"
	// MountNamespace for isolating mount points
	MountNamespace = "mount"
	// IPCNamespace for isolating System V IPC, POSIX message queues
	IPCNamespace = "ipc"
	// UTSNamespace for isolating hostname and NIS domain name
	UTSNamespace = "uts"
	// UserNamespace for isolating user and group IDs
	UserNamespace = "user"
)

// IDMapping specifies UID/GID mappings
type IDMapping struct {
	// HostID is the UID/GID of the host user or group
	HostID uint32 `json:"hostID"`
	// ContainerID is the UID/GID of the container's user or group
	ContainerID uint32 `json:"containerID"`
	// Size is the length of the range of IDs mapped between the two namespaces
	Size uint32 `json:"size"`
}

// Rlimit type and restrictions
type Rlimit struct {
	// Type of the rlimit to set
	Type string `json:"type"`
	// Hard is the hard limit for the specified type
	Hard uint64 `json:"hard"`
	// Soft is the soft limit for the specified type
	Soft uint64 `json:"soft"`
}

// HugepageLimit structure corresponds to limiting kernel hugepages
type HugepageLimit struct {
	// Pagesize is the hugepage size
	Pagesize string `json:"pageSize"`
	// Limit is the limit of "hugepagesize" hugetlb usage
	Limit uint64 `json:"limit"`
}

// InterfacePriority for network interfaces
type InterfacePriority struct {
	// Name is the name of the network interface
	Name string `json:"name"`
	// Priority for the interface
	Priority int64 `json:"priority"`
}

// blockIODevice holds major:minor format supported in blkio cgroup
type blockIODevice struct {
	// Major is the device's major number.
	Major int64 `json:"major"`
	// Minor is the device's minor number.
	Minor int64 `json:"minor"`
}

// WeightDevice struct holds a `major:minor weight` pair for blkioWeightDevice
type WeightDevice struct {
	blockIODevice
	// Weight is the bandwidth rate for the device, range is from 10 to 1000
	Weight uint16 `json:"weight"`
	// LeafWeight is the bandwidth rate for the device while competing with the cgroup's child cgroups, range is from 10 to 1000, cfq scheduler only
	LeafWeight uint16 `json:"leafWeight"`
}

// ThrottleDevice struct holds a `major:minor rate_per_second` pair
type ThrottleDevice struct {
	blockIODevice
	// Rate is the IO rate limit per cgroup per device
	Rate uint64 `json:"rate"`
}

// BlockIO for Linux cgroup 'blkio' resource management
type BlockIO struct {
	// Specifies per cgroup weight, range is from 10 to 1000
	Weight uint16 `json:"blkioWeight"`
	// Specifies tasks' weight in the given cgroup while competing with the cgroup's child cgroups, range is from 10 to 1000, cfq scheduler only
	LeafWeight uint16 `json:"blkioLeafWeight"`
	// Weight per cgroup per device, can override BlkioWeight
	WeightDevice []*WeightDevice `json:"blkioWeightDevice"`
	// IO read rate limit per cgroup per device, bytes per second
	ThrottleReadBpsDevice []*ThrottleDevice `json:"blkioThrottleReadBpsDevice"`
	// IO write rate limit per cgroup per device, bytes per second
	ThrottleWriteBpsDevice []*ThrottleDevice `json:"blkioThrottleWriteBpsDevice"`
	// IO read rate limit per cgroup per device, IO per second
	ThrottleReadIOPSDevice []*ThrottleDevice `json:"blkioThrottleReadIOPSDevice"`
	// IO write rate limit per cgroup per device, IO per second
	ThrottleWriteIOPSDevice []*ThrottleDevice `json:"blkioThrottleWriteIOPSDevice"`
}

// Memory for Linux cgroup 'memory' resource management
type Memory struct {
	// Memory limit (in bytes)
	Limit int64 `json:"limit"`
	// Memory reservation or soft_limit (in bytes)
	Reservation int64 `json:"reservation"`
	// Total memory usage (memory + swap); set `-1' to disable swap
	Swap int64 `json:"swap"`
	// Kernel memory limit (in bytes)
	Kernel int64 `json:"kernel"`
	// How aggressive the kernel will swap memory pages. Range from 0 to 100. Set -1 to use system default
	Swappiness int64 `json:"swappiness"`
}

// CPU for Linux cgroup 'cpu' resource management
type CPU struct {
	// CPU shares (relative weight vs. other cgroups with cpu shares)
	Shares int64 `json:"shares"`
	// CPU hardcap limit (in usecs). Allowed cpu time in a given period
	Quota int64 `json:"quota"`
	// CPU period to be used for hardcapping (in usecs). 0 to use system default
	Period int64 `json:"period"`
	// How many time CPU will use in realtime scheduling (in usecs)
	RealtimeRuntime int64 `json:"realtimeRuntime"`
	// CPU period to be used for realtime scheduling (in usecs)
	RealtimePeriod int64 `json:"realtimePeriod"`
	// CPU to use within the cpuset
	Cpus string `json:"cpus"`
	// MEM to use within the cpuset
	Mems string `json:"mems"`
}

// Pids for Linux cgroup 'pids' resource management (Linux 4.3)
type Pids struct {
	// Maximum number of PIDs. A value < 0 implies "no limit".
	Limit int64 `json:"limit"`
}

// Network identification and priority configuration
type Network struct {
	// Set class identifier for container's network packets
	ClassID string `json:"classId"`
	// Set priority of network traffic for container
	Priorities []InterfacePriority `json:"priorities"`
}

// Resources has container runtime resource constraints
type Resources struct {
	// DisableOOMKiller disables the OOM killer for out of memory conditions
	DisableOOMKiller bool `json:"disableOOMKiller"`
	// Memory restriction configuration
	Memory Memory `json:"memory"`
	// CPU resource restriction configuration
	CPU CPU `json:"cpu"`
	// Task resource restriction configuration.
	Pids Pids `json:"pids"`
	// BlockIO restriction configuration
	BlockIO BlockIO `json:"blockIO"`
	// Hugetlb limit (in bytes)
	HugepageLimits []HugepageLimit `json:"hugepageLimits"`
	// Network restriction configuration
	Network Network `json:"network"`
}

// Device represents the information on a Linux special device file
type Device struct {
	// Path to the device.
	Path string `json:"path"`
	// Device type, block, char, etc.
	Type rune `json:"type"`
	// Major is the device's major number.
	Major int64 `json:"major"`
	// Minor is the device's minor number.
	Minor int64 `json:"minor"`
	// Cgroup permissions format, rwm.
	Permissions string `json:"permissions"`
	// FileMode permission bits for the device.
	FileMode os.FileMode `json:"fileMode"`
	// UID of the device.
	UID uint32 `json:"uid"`
	// Gid of the device.
	GID uint32 `json:"gid"`
}

// Seccomp represents syscall restrictions
type Seccomp struct {
	DefaultAction Action     `json:"defaultAction"`
	Architectures []Arch     `json:"architectures"`
	Syscalls      []*Syscall `json:"syscalls"`
}

// Additional architectures permitted to be used for system calls
// By default only the native architecture of the kernel is permitted
type Arch string

const (
	ArchX86         Arch = "SCMP_ARCH_X86"
	ArchX86_64      Arch = "SCMP_ARCH_X86_64"
	ArchX32         Arch = "SCMP_ARCH_X32"
	ArchARM         Arch = "SCMP_ARCH_ARM"
	ArchAARCH64     Arch = "SCMP_ARCH_AARCH64"
	ArchMIPS        Arch = "SCMP_ARCH_MIPS"
	ArchMIPS64      Arch = "SCMP_ARCH_MIPS64"
	ArchMIPS64N32   Arch = "SCMP_ARCH_MIPS64N32"
	ArchMIPSEL      Arch = "SCMP_ARCH_MIPSEL"
	ArchMIPSEL64    Arch = "SCMP_ARCH_MIPSEL64"
	ArchMIPSEL64N32 Arch = "SCMP_ARCH_MIPSEL64N32"
)

// Action taken upon Seccomp rule match
type Action string

const (
	ActKill  Action = "SCMP_ACT_KILL"
	ActTrap  Action = "SCMP_ACT_TRAP"
	ActErrno Action = "SCMP_ACT_ERRNO"
	ActTrace Action = "SCMP_ACT_TRACE"
	ActAllow Action = "SCMP_ACT_ALLOW"
)

// Operator used to match syscall arguments in Seccomp
type Operator string

const (
	OpNotEqual     Operator = "SCMP_CMP_NE"
	OpLessThan     Operator = "SCMP_CMP_LT"
	OpLessEqual    Operator = "SCMP_CMP_LE"
	OpEqualTo      Operator = "SCMP_CMP_EQ"
	OpGreaterEqual Operator = "SCMP_CMP_GE"
	OpGreaterThan  Operator = "SCMP_CMP_GT"
	OpMaskedEqual  Operator = "SCMP_CMP_MASKED_EQ"
)

// Arg used for matching specific syscall arguments in Seccomp
type Arg struct {
	Index    uint     `json:"index"`
	Value    uint64   `json:"value"`
	ValueTwo uint64   `json:"valueTwo"`
	Op       Operator `json:"op"`
}

// Syscall is used to match a syscall in Seccomp
type Syscall struct {
	Name   string `json:"name"`
	Action Action `json:"action"`
	Args   []*Arg `json:"args"`
}
<commit_msg>change the name of struct `blockIODevice` to `BlockIODevice`<commit_after>package specs

import "os"

// LinuxStateDirectory holds the container's state information
const LinuxStateDirectory = "/run/opencontainer/containers"

// LinuxRuntimeSpec is the full specification for linux containers.
type LinuxRuntimeSpec struct {
	RuntimeSpec
	// LinuxRuntime is platform specific configuration for linux based containers.
	Linux LinuxRuntime `json:"linux"`
}

// LinuxRuntime hosts the Linux-only runtime information
type LinuxRuntime struct {
	// UIDMapping specifies user mappings for supporting user namespaces on linux.
	UIDMappings []IDMapping `json:"uidMappings"`
	// GIDMapping specifies group mappings for supporting user namespaces on linux.
	GIDMappings []IDMapping `json:"gidMappings"`
	// Rlimits specifies rlimit options to apply to the container's process.
	Rlimits []Rlimit `json:"rlimits"`
	// Sysctl are a set of key value pairs that are set for the container on start
	Sysctl map[string]string `json:"sysctl"`
	// Resources contain cgroup information for handling resource constraints
	// for the container
	Resources *Resources `json:"resources"`
	// CgroupsPath specifies the path to cgroups that are created and/or joined by the container.
	// The path is expected to be relative to the cgroups mountpoint.
	// If resources are specified, the cgroups at CgroupsPath will be updated based on resources.
	CgroupsPath string `json:"cgroupsPath"`
	// Namespaces contains the namespaces that are created and/or joined by the container
	Namespaces []Namespace `json:"namespaces"`
	// Devices are a list of device nodes that are created and enabled for the container
	Devices []Device `json:"devices"`
	// ApparmorProfile specified the apparmor profile for the container.
	ApparmorProfile string `json:"apparmorProfile"`
	// SelinuxProcessLabel specifies the selinux context that the container process is run as.
	SelinuxProcessLabel string `json:"selinuxProcessLabel"`
	// Seccomp specifies the seccomp security settings for the container.
	Seccomp Seccomp `json:"seccomp"`
	// RootfsPropagation is the rootfs mount propagation mode for the container
	RootfsPropagation string `json:"rootfsPropagation"`
}

// Namespace is the configuration for a linux namespace
type Namespace struct {
	// Type is the type of Linux namespace
	Type NamespaceType `json:"type"`
	// Path is a path to an existing namespace persisted on disk that can be joined
	// and is of the same type
	Path string `json:"path"`
}

// NamespaceType is one of the linux namespaces
type NamespaceType string

const (
	// PIDNamespace for isolating process IDs
	PIDNamespace NamespaceType = "pid"
	// NetworkNamespace for isolating network devices, stacks, ports, etc
	NetworkNamespace = "network"
	// MountNamespace for isolating mount points
	MountNamespace = "mount"
	// IPCNamespace for isolating System V IPC, POSIX message queues
	IPCNamespace = "ipc"
	// UTSNamespace for isolating hostname and NIS domain name
	UTSNamespace = "uts"
	// UserNamespace for isolating user and group IDs
	UserNamespace = "user"
)

// IDMapping specifies UID/GID mappings
type IDMapping struct {
	// HostID is the UID/GID of the host user or group
	HostID uint32 `json:"hostID"`
	// ContainerID is the UID/GID of the container's user or group
	ContainerID uint32 `json:"containerID"`
	// Size is the length of the range of IDs mapped between the two namespaces
	Size uint32 `json:"size"`
}

// Rlimit type and restrictions
type Rlimit struct {
	// Type of the rlimit to set
	Type string `json:"type"`
	// Hard is the hard limit for the specified type
	Hard uint64 `json:"hard"`
	// Soft is the soft limit for the specified type
	Soft uint64 `json:"soft"`
}

// HugepageLimit structure corresponds to limiting kernel hugepages
type HugepageLimit struct {
	// Pagesize is the hugepage size
	Pagesize string `json:"pageSize"`
	// Limit is the limit of "hugepagesize" hugetlb usage
	Limit uint64 `json:"limit"`
}

// InterfacePriority for network interfaces
type InterfacePriority struct {
	// Name is the name of the network interface
	Name string `json:"name"`
	// Priority for the interface
	Priority int64 `json:"priority"`
}

// BlockIODevice holds major:minor format supported in blkio cgroup
type BlockIODevice struct {
	// Major is the device's major number.
	Major int64 `json:"major"`
	// Minor is the device's minor number.
	Minor int64 `json:"minor"`
}

// WeightDevice struct holds a `major:minor weight` pair for blkioWeightDevice
type WeightDevice struct {
	BlockIODevice
	// Weight is the bandwidth rate for the device, range is from 10 to 1000
	Weight uint16 `json:"weight"`
	// LeafWeight is the bandwidth rate for the device while competing with the cgroup's child cgroups, range is from 10 to 1000, cfq scheduler only
	LeafWeight uint16 `json:"leafWeight"`
}

// ThrottleDevice struct holds a `major:minor rate_per_second` pair
type ThrottleDevice struct {
	BlockIODevice
	// Rate is the IO rate limit per cgroup per device
	Rate uint64 `json:"rate"`
}

// BlockIO for Linux cgroup 'blkio' resource management
type BlockIO struct {
	// Specifies per cgroup weight, range is from 10 to 1000
	Weight uint16 `json:"blkioWeight"`
	// Specifies tasks' weight in the given cgroup while competing with the cgroup's child cgroups, range is from 10 to 1000, cfq scheduler only
	LeafWeight uint16 `json:"blkioLeafWeight"`
	// Weight per cgroup per device, can override BlkioWeight
	WeightDevice []*WeightDevice `json:"blkioWeightDevice"`
	// IO read rate limit per cgroup per device, bytes per second
	ThrottleReadBpsDevice []*ThrottleDevice `json:"blkioThrottleReadBpsDevice"`
	// IO write rate limit per cgroup per device, bytes per second
	ThrottleWriteBpsDevice []*ThrottleDevice `json:"blkioThrottleWriteBpsDevice"`
	// IO read rate limit per cgroup per device, IO per second
	ThrottleReadIOPSDevice []*ThrottleDevice `json:"blkioThrottleReadIOPSDevice"`
	// IO write rate limit per cgroup per device, IO per second
	ThrottleWriteIOPSDevice []*ThrottleDevice `json:"blkioThrottleWriteIOPSDevice"`
}

// Memory for Linux cgroup 'memory' resource management
type Memory struct {
	// Memory limit (in bytes)
	Limit int64 `json:"limit"`
	// Memory reservation or soft_limit (in bytes)
	Reservation int64 `json:"reservation"`
	// Total memory usage (memory + swap); set `-1' to disable swap
	Swap int64 `json:"swap"`
	// Kernel memory limit (in bytes)
	Kernel int64 `json:"kernel"`
	// How aggressive the kernel will swap memory pages. Range from 0 to 100. Set -1 to use system default
	Swappiness int64 `json:"swappiness"`
}

// CPU for Linux cgroup 'cpu' resource management
type CPU struct {
	// CPU shares (relative weight vs. other cgroups with cpu shares)
	Shares int64 `json:"shares"`
	// CPU hardcap limit (in usecs). Allowed cpu time in a given period
	Quota int64 `json:"quota"`
	// CPU period to be used for hardcapping (in usecs). 0 to use system default
	Period int64 `json:"period"`
	// How many time CPU will use in realtime scheduling (in usecs)
	RealtimeRuntime int64 `json:"realtimeRuntime"`
	// CPU period to be used for realtime scheduling (in usecs)
	RealtimePeriod int64 `json:"realtimePeriod"`
	// CPU to use within the cpuset
	Cpus string `json:"cpus"`
	// MEM to use within the cpuset
	Mems string `json:"mems"`
}

// Pids for Linux cgroup 'pids' resource management (Linux 4.3)
type Pids struct {
	// Maximum number of PIDs. A value < 0 implies "no limit".
	Limit int64 `json:"limit"`
}

// Network identification and priority configuration
type Network struct {
	// Set class identifier for container's network packets
	ClassID string `json:"classId"`
	// Set priority of network traffic for container
	Priorities []InterfacePriority `json:"priorities"`
}

// Resources has container runtime resource constraints
type Resources struct {
	// DisableOOMKiller disables the OOM killer for out of memory conditions
	DisableOOMKiller bool `json:"disableOOMKiller"`
	// Memory restriction configuration
	Memory Memory `json:"memory"`
	// CPU resource restriction configuration
	CPU CPU `json:"cpu"`
	// Task resource restriction configuration.
	Pids Pids `json:"pids"`
	// BlockIO restriction configuration
	BlockIO BlockIO `json:"blockIO"`
	// Hugetlb limit (in bytes)
	HugepageLimits []HugepageLimit `json:"hugepageLimits"`
	// Network restriction configuration
	Network Network `json:"network"`
}

// Device represents the information on a Linux special device file
type Device struct {
	// Path to the device.
	Path string `json:"path"`
	// Device type, block, char, etc.
	Type rune `json:"type"`
	// Major is the device's major number.
	Major int64 `json:"major"`
	// Minor is the device's minor number.
	Minor int64 `json:"minor"`
	// Cgroup permissions format, rwm.
	Permissions string `json:"permissions"`
	// FileMode permission bits for the device.
	FileMode os.FileMode `json:"fileMode"`
	// UID of the device.
	UID uint32 `json:"uid"`
	// Gid of the device.
	GID uint32 `json:"gid"`
}

// Seccomp represents syscall restrictions
type Seccomp struct {
	DefaultAction Action     `json:"defaultAction"`
	Architectures []Arch     `json:"architectures"`
	Syscalls      []*Syscall `json:"syscalls"`
}

// Additional architectures permitted to be used for system calls
// By default only the native architecture of the kernel is permitted
type Arch string

const (
	ArchX86         Arch = "SCMP_ARCH_X86"
	ArchX86_64      Arch = "SCMP_ARCH_X86_64"
	ArchX32         Arch = "SCMP_ARCH_X32"
	ArchARM         Arch = "SCMP_ARCH_ARM"
	ArchAARCH64     Arch = "SCMP_ARCH_AARCH64"
	ArchMIPS        Arch = "SCMP_ARCH_MIPS"
	ArchMIPS64      Arch = "SCMP_ARCH_MIPS64"
	ArchMIPS64N32   Arch = "SCMP_ARCH_MIPS64N32"
	ArchMIPSEL      Arch = "SCMP_ARCH_MIPSEL"
	ArchMIPSEL64    Arch = "SCMP_ARCH_MIPSEL64"
	ArchMIPSEL64N32 Arch = "SCMP_ARCH_MIPSEL64N32"
)

// Action taken upon Seccomp rule match
type Action string

const (
	ActKill  Action = "SCMP_ACT_KILL"
	ActTrap  Action = "SCMP_ACT_TRAP"
	ActErrno Action = "SCMP_ACT_ERRNO"
	ActTrace Action = "SCMP_ACT_TRACE"
	ActAllow Action = "SCMP_ACT_ALLOW"
)

// Operator used to match syscall arguments in Seccomp
type Operator string

const (
	OpNotEqual     Operator = "SCMP_CMP_NE"
	OpLessThan     Operator = "SCMP_CMP_LT"
	OpLessEqual    Operator = "SCMP_CMP_LE"
	OpEqualTo      Operator = "SCMP_CMP_EQ"
	OpGreaterEqual Operator = "SCMP_CMP_GE"
	OpGreaterThan  Operator = "SCMP_CMP_GT"
	OpMaskedEqual  Operator = "SCMP_CMP_MASKED_EQ"
)

// Arg used for matching specific syscall arguments in Seccomp
type Arg struct {
	Index    uint     `json:"index"`
	Value    uint64   `json:"value"`
	ValueTwo uint64   `json:"valueTwo"`
	Op       Operator `json:"op"`
}

// Syscall is used to match a syscall in Seccomp
type Syscall struct {
	Name   string `json:"name"`
	Action Action `json:"action"`
	Args   []*Arg `json:"args"`
}
<|endoftext|>


<commit_before>package parser

import (
	"testing"

	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/suite"
	"github.com/teambition/swaggo/swagger"
)

func TestErrorPath(t *testing.T) {
	assert := assert.New(t)
	// error test
	projectPath := "../test"
	swaggerGo := "../test/swagger.go.err"
	dev := true
	as, err := NewAppSuite(projectPath, swaggerGo, dev)
	assert.Nil(as)
	assert.NotNil(err)
}

func TestAppSuite(t *testing.T) {
	assert := assert.New(t)
	// error test
	projectPath := "../test"
	swaggerGo := "../test/swagger.go"
	dev := true
	as, err := NewAppSuite(projectPath, swaggerGo, dev)
	assert.Nil(err)
	assert.NotNil(as)
	suite.Run(t, as)
}

type AppSuite struct {
	suite.Suite
	*swagger.Swagger
}

func NewAppSuite(projectPath, swaggerGo string, dev bool) (*AppSuite, error) {
	as := &AppSuite{Swagger: swagger.NewV2()}
	if err := doc2Swagger(projectPath, swaggerGo, dev, as.Swagger); err != nil {
		return nil, err
	}
	return as, nil
}

func (suite *AppSuite) TestSwagger() {
	assert := assert.New(suite.T())
	assert.Equal("2.0", suite.SwaggerVersion)
	assert.Equal("Swagger Example API", suite.Infos.Title)
	assert.Equal("Swagger Example API", suite.Infos.Description)
	assert.Equal("1.0.0", suite.Infos.Version)
	assert.Equal("http://teambition.com/", suite.Infos.TermsOfService)
	// contact
	assert.Equal("swagger", suite.Infos.Contact.Name)
	assert.Equal("swagger@teambition.com", suite.Infos.Contact.EMail)
	assert.Equal("teambition.com", suite.Infos.Contact.URL)
	// license
	assert.Equal("Apache", suite.Infos.License.Name)
	assert.Equal("http://teambition.com/", suite.Infos.License.URL)
	// schemes
	assert.Equal([]string{"http", "wss"}, suite.Schemes)
	// consumes and produces
	assert.Equal([]string{"application/json", "text/plain", "application/xml", "text/html"}, suite.Consumes)
	assert.Equal([]string{"application/json", "text/plain", "application/xml", "text/html"}, suite.Produces)

	assert.Equal("127.0.0.1:3000", suite.Host)
	assert.Equal("/api", suite.BasePath)
	assert.Equal(7, len(suite.Paths))
	router := suite.Paths["/testapi/get-string-by-int/{some_id}"]
	assert.NotNil(router)
	assert.NotNil(router.Get)
	assert.Equal([]string{"testapi"}, router.Get.Tags)
	assert.Equal("get string by ID summary
multi line", router.Get.Summary)
	assert.Equal("get string by ID desc
multi line", router.Get.Description)
	assert.Equal("testapi.GetStringByInt", router.Get.OperationID)
	assert.Equal([]string{"application/json", "text/plain", "application/xml", "text/html"}, router.Get.Consumes)
	assert.Equal([]string{"application/json", "text/plain", "application/xml", "text/html"}, router.Get.Produces)

	assert.Equal("path", router.Get.Parameters[0].In)
	assert.Equal("path_param", router.Get.Parameters[0].Name)
	assert.Equal("Some ID", router.Get.Parameters[0].Description)
	assert.Equal(true, router.Get.Parameters[0].Required)
	assert.Equal("integer", router.Get.Parameters[0].Type)
	assert.Equal("int32", router.Get.Parameters[0].Format)
	assert.Equal(123, router.Get.Parameters[0].Default)

	// 200
	assert.NotNil(router.Get.Responses["200"])
	assert.Equal("string", router.Get.Responses["200"].Schema.Type)

	// 400
	assert.NotNil(router.Get.Responses["400"])
	assert.Equal("We need ID!!", router.Get.Responses["400"].Description)
	assert.Equal("#/definitions/APIError", router.Get.Responses["400"].Schema.Ref)
	assert.Equal("object", router.Get.Responses["400"].Schema.Type)

	// 404
	assert.NotNil(router.Get.Responses["404"])
	assert.Equal("Can not find ID", router.Get.Responses["404"].Description)
	assert.Equal("#/definitions/APIError", router.Get.Responses["404"].Schema.Ref)
	assert.Equal("object", router.Get.Responses["404"].Schema.Type)

	// definitions
	// APIError
	apiError := suite.Definitions["APIError"]
	assert.NotNil(apiError)
	assert.Equal("APIError", apiError.Title)
	assert.Equal("object", apiError.Type)
	assert.Equal("integer", apiError.Properties["ErrorCode"].Type)
	assert.Equal("int32", apiError.Properties["ErrorCode"].Format)
	assert.Equal("string", apiError.Properties["ErrorMessage"].Type)

	// inherit
	inhertStruct := suite.Definitions["StructureWithEmbededStructure"]
	assert.NotNil(inhertStruct)
	assert.Equal("StructureWithEmbededStructure", inhertStruct.Title)
	assert.Equal("object", inhertStruct.Type)

	// assert.True(reflect.DeepEqual([]string{"id", "name", "age", "ctime", "sub", "i"}, inhertStruct.Required))
	assert.Equal("the user age", inhertStruct.Properties["age"].Description)
	assert.Equal(18, inhertStruct.Properties["age"].Default)
	assert.Equal("integer", inhertStruct.Properties["age"].Type)
	assert.Equal("int32", inhertStruct.Properties["age"].Format)

	assert.Equal("#/definitions/SimpleStructure_1", inhertStruct.Properties["sub"].Ref)
	assert.Equal("object", inhertStruct.Properties["sub"].Type)

	// tags
	assert.Equal(1, len(suite.Tags))
	assert.Equal("testapi", suite.Tags[0].Name)
	assert.Equal("test apis", suite.Tags[0].Description)
}
<commit_msg>test for swagger's required<commit_after>package parser

import (
	"testing"

	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/suite"
	"github.com/teambition/swaggo/swagger"
)

func TestErrorPath(t *testing.T) {
	assert := assert.New(t)
	// error test
	projectPath := "../test"
	swaggerGo := "../test/swagger.go.err"
	dev := true
	as, err := NewAppSuite(projectPath, swaggerGo, dev)
	assert.Nil(as)
	assert.NotNil(err)
}

func TestAppSuite(t *testing.T) {
	assert := assert.New(t)
	// error test
	projectPath := "../test"
	swaggerGo := "../test/swagger.go"
	dev := true
	as, err := NewAppSuite(projectPath, swaggerGo, dev)
	assert.Nil(err)
	assert.NotNil(as)
	suite.Run(t, as)
}

type AppSuite struct {
	suite.Suite
	*swagger.Swagger
}

func NewAppSuite(projectPath, swaggerGo string, dev bool) (*AppSuite, error) {
	as := &AppSuite{Swagger: swagger.NewV2()}
	if err := doc2Swagger(projectPath, swaggerGo, dev, as.Swagger); err != nil {
		return nil, err
	}
	return as, nil
}

func (suite *AppSuite) TestSwagger() {
	assert := assert.New(suite.T())
	assert.Equal("2.0", suite.SwaggerVersion)
	assert.Equal("Swagger Example API", suite.Infos.Title)
	assert.Equal("Swagger Example API", suite.Infos.Description)
	assert.Equal("1.0.0", suite.Infos.Version)
	assert.Equal("http://teambition.com/", suite.Infos.TermsOfService)
	// contact
	assert.Equal("swagger", suite.Infos.Contact.Name)
	assert.Equal("swagger@teambition.com", suite.Infos.Contact.EMail)
	assert.Equal("teambition.com", suite.Infos.Contact.URL)
	// license
	assert.Equal("Apache", suite.Infos.License.Name)
	assert.Equal("http://teambition.com/", suite.Infos.License.URL)
	// schemes
	assert.Equal([]string{"http", "wss"}, suite.Schemes)
	// consumes and produces
	assert.Equal([]string{"application/json", "text/plain", "application/xml", "text/html"}, suite.Consumes)
	assert.Equal([]string{"application/json", "text/plain", "application/xml", "text/html"}, suite.Produces)

	assert.Equal("127.0.0.1:3000", suite.Host)
	assert.Equal("/api", suite.BasePath)
	assert.Equal(7, len(suite.Paths))
	router := suite.Paths["/testapi/get-string-by-int/{some_id}"]
	assert.NotNil(router)
	assert.NotNil(router.Get)
	assert.Equal([]string{"testapi"}, router.Get.Tags)
	assert.Equal("get string by ID summary
multi line", router.Get.Summary)
	assert.Equal("get string by ID desc
multi line", router.Get.Description)
	assert.Equal("testapi.GetStringByInt", router.Get.OperationID)
	assert.Equal([]string{"application/json", "text/plain", "application/xml", "text/html"}, router.Get.Consumes)
	assert.Equal([]string{"application/json", "text/plain", "application/xml", "text/html"}, router.Get.Produces)

	assert.Equal("path", router.Get.Parameters[0].In)
	assert.Equal("path_param", router.Get.Parameters[0].Name)
	assert.Equal("Some ID", router.Get.Parameters[0].Description)
	assert.Equal(true, router.Get.Parameters[0].Required)
	assert.Equal("integer", router.Get.Parameters[0].Type)
	assert.Equal("int32", router.Get.Parameters[0].Format)
	assert.Equal(123, router.Get.Parameters[0].Default)

	// 200
	assert.NotNil(router.Get.Responses["200"])
	assert.Equal("string", router.Get.Responses["200"].Schema.Type)

	// 400
	assert.NotNil(router.Get.Responses["400"])
	assert.Equal("We need ID!!", router.Get.Responses["400"].Description)
	assert.Equal("#/definitions/APIError", router.Get.Responses["400"].Schema.Ref)
	assert.Equal("object", router.Get.Responses["400"].Schema.Type)

	// 404
	assert.NotNil(router.Get.Responses["404"])
	assert.Equal("Can not find ID", router.Get.Responses["404"].Description)
	assert.Equal("#/definitions/APIError", router.Get.Responses["404"].Schema.Ref)
	assert.Equal("object", router.Get.Responses["404"].Schema.Type)

	// definitions
	// APIError
	apiError := suite.Definitions["APIError"]
	assert.NotNil(apiError)
	assert.Equal("APIError", apiError.Title)
	assert.Equal("object", apiError.Type)
	assert.Equal("integer", apiError.Properties["ErrorCode"].Type)
	assert.Equal("int32", apiError.Properties["ErrorCode"].Format)
	assert.Equal("string", apiError.Properties["ErrorMessage"].Type)

	// inherit
	inhertStruct := suite.Definitions["StructureWithEmbededStructure"]
	assert.NotNil(inhertStruct)
	assert.Equal("StructureWithEmbededStructure", inhertStruct.Title)
	assert.Equal("object", inhertStruct.Type)

	assert.True(subset(inhertStruct.Required, []string{"id", "name", "age", "ctime", "sub", "i"}))
	assert.Equal("the user age", inhertStruct.Properties["age"].Description)
	assert.Equal(18, inhertStruct.Properties["age"].Default)
	assert.Equal("integer", inhertStruct.Properties["age"].Type)
	assert.Equal("int32", inhertStruct.Properties["age"].Format)

	assert.Equal("#/definitions/SimpleStructure_1", inhertStruct.Properties["sub"].Ref)
	assert.Equal("object", inhertStruct.Properties["sub"].Type)

	// tags
	assert.Equal(1, len(suite.Tags))
	assert.Equal("testapi", suite.Tags[0].Name)
	assert.Equal("test apis", suite.Tags[0].Description)
}
<|endoftext|>