<commit_before>package main

import (
	"fmt"
	"gopkg.in/yaml.v2"
	"html/template"
	"io/ioutil"
	"os"
	"path/filepath"
)

type Page struct {
	TemplatePath string `yaml:"template"`
	TitlePath    string `yaml:"title"`
	TitleRaw     string `yaml:"title-raw"`
	HeaderPath   string `yaml:"header"`
	HeaderRaw    string `yaml:"header-raw"`
	BodyPath     string `yaml:"body"`
	BodyRaw      string `yaml:"body-raw"`
	FooterPath   string `yaml:"footer"`
	FooterRaw    string `yaml:"footer-raw"`
	Output       string `yaml:"output-html,omitempty"`
}

func (p Page) String() string {
	return fmt.Sprintf("%v\n%s\n%s\n%s\n", p.Title, p.Header, p.Body, p.Footer)
}

// Returns a template.HTML type with the HTML content from the raw string or
// path in a Page pointer. Used to construct specific getter methods.
func (page *Page) gethtml(raw *string, path *string) template.HTML {
	var html string
	if len(*raw) == 0 {
		htmlbytes, err := ioutil.ReadFile(*path)
		if err != nil {
			fmt.Println(err)
		}
		html = string(htmlbytes)
	} else {
		html = *raw
	}
	return template.HTML(html)
}

// Return HTML types for page content
func (page *Page) Title() template.HTML {
	return page.gethtml(&page.TitleRaw, &page.TitlePath)
}

func (page *Page) Header() template.HTML {
	return page.gethtml(&page.HeaderRaw, &page.HeaderPath)
}

func (page *Page) Body() template.HTML {
	return page.gethtml(&page.BodyRaw, &page.BodyPath)
}

func (page *Page) Footer() template.HTML {
	return page.gethtml(&page.FooterRaw, &page.FooterPath)
}

// Read a configuration file and return a pointer to a Page struct
func ReadConfig(path string) (*Page, error) {
	var page Page
	var err error

	data, err := ioutil.ReadFile(path)
	if err != nil {
		return &page, err
	}
	err = yaml.Unmarshal(data, &page)

	basepath := filepath.Dir(path)
	if len(page.TitlePath) != 0 {
		page.TitlePath = filepath.Join(basepath, page.TitlePath)
	}
	if len(page.HeaderPath) != 0 {
		page.HeaderPath = filepath.Join(basepath, page.HeaderPath)
	}
	if len(page.BodyPath) != 0 {
		page.BodyPath = filepath.Join(basepath, page.BodyPath)
	}
	if len(page.FooterPath) != 0 {
		page.FooterPath = filepath.Join(basepath, page.FooterPath)
	}

	return &page, err
}

func BuildPage(rootpath string, fout *os.File, page *Page) error {
	templatepath, _ := filepath.Rel(rootpath, page.TemplatePath)
	templatebytes, err := ioutil.ReadFile(templatepath)
	if err != nil {
		return err
	}
	t := template.Must(template.New("unnamed").Parse(string(templatebytes)))
	err = t.Execute(fout, page)
	return err
}

func OutputPath(page *Page, configpath, rootpath string) string {
	var fnmhtml string
	if len(page.Output) == 0 {
		fnmhtml = filepath.Base(configpath[:len(configpath)-5]) + ".html"
	} else {
		fnmhtml, _ = filepath.Rel(rootpath, page.Output)
	}
	return fnmhtml
}

func main() {

	var args []string
	args = os.Args[1:]
	if len(args) == 0 {
		fmt.Println("At least one content file must be specified")
	}

	var pagePtr *Page
	var fnmhtml, rootpath string
	for _, configpath := range args {
		if _, err := os.Stat(configpath); !os.IsNotExist(err) {

			// Read configuration
			fmt.Println("parsing", configpath)
			rootpath = filepath.Dir(configpath)
			pagePtr, err = ReadConfig(configpath)
			if err != nil {
				fmt.Println(err)
				break
			}
			fmt.Println("using template at", pagePtr.TemplatePath)

			fnmhtml = OutputPath(pagePtr, configpath, rootpath)
			fmt.Println("writing to", fnmhtml)

			// Write output
			fout, err := os.Create(fnmhtml)
			if err != nil {
				fmt.Println(err)
				break
			}

			err = BuildPage(rootpath, fout, pagePtr)
			if err != nil {
				fmt.Println(err)
			}
		} else {
			fmt.Println(configpath, "does not exist")
		}
	}
}
<commit_msg>fix #1; returning blank HTML when field missing<commit_after>package main

import (
	"fmt"
	"gopkg.in/yaml.v2"
	"html/template"
	"io/ioutil"
	"os"
	"path/filepath"
)

type Page struct {
	TemplatePath string `yaml:"template"`
	TitlePath    string `yaml:"title"`
	TitleRaw     string `yaml:"title-raw"`
	HeaderPath   string `yaml:"header"`
	HeaderRaw    string `yaml:"header-raw"`
	BodyPath     string `yaml:"body"`
	BodyRaw      string `yaml:"body-raw"`
	FooterPath   string `yaml:"footer"`
	FooterRaw    string `yaml:"footer-raw"`
	Output       string `yaml:"output-html,omitempty"`
}

func (p Page) String() string {
	return fmt.Sprintf("%v\n%s\n%s\n%s\n", p.Title, p.Header, p.Body, p.Footer)
}

// Returns a template.HTML type with the HTML content from the raw string or
// path in a Page pointer. Used to construct specific getter methods.
func (page *Page) gethtml(raw *string, path *string) template.HTML {
	var html string
	if len(*raw) == 0 {
		if len(*path) == 0 {
			html = ""
		} else {
			htmlbytes, err := ioutil.ReadFile(*path)
			if err != nil {
				fmt.Println(err)
			}
			html = string(htmlbytes)
		}
	} else {
		html = *raw
	}
	return template.HTML(html)
}

// Return HTML types for page content
func (page *Page) Title() template.HTML {
	return page.gethtml(&page.TitleRaw, &page.TitlePath)
}

func (page *Page) Header() template.HTML {
	return page.gethtml(&page.HeaderRaw, &page.HeaderPath)
}

func (page *Page) Body() template.HTML {
	return page.gethtml(&page.BodyRaw, &page.BodyPath)
}

func (page *Page) Footer() template.HTML {
	return page.gethtml(&page.FooterRaw, &page.FooterPath)
}

// Read a configuration file and return a pointer to a Page struct
func ReadConfig(path string) (*Page, error) {
	var page Page
	var err error

	data, err := ioutil.ReadFile(path)
	if err != nil {
		return &page, err
	}
	err = yaml.Unmarshal(data, &page)

	basepath := filepath.Dir(path)
	if len(page.TitlePath) != 0 {
		page.TitlePath = filepath.Join(basepath, page.TitlePath)
	}
	if len(page.HeaderPath) != 0 {
		page.HeaderPath = filepath.Join(basepath, page.HeaderPath)
	}
	if len(page.BodyPath) != 0 {
		page.BodyPath = filepath.Join(basepath, page.BodyPath)
	}
	if len(page.FooterPath) != 0 {
		page.FooterPath = filepath.Join(basepath, page.FooterPath)
	}

	return &page, err
}

func BuildPage(rootpath string, fout *os.File, page *Page) error {
	templatepath := filepath.Join(rootpath, page.TemplatePath)
	templatebytes, err := ioutil.ReadFile(templatepath)
	if err != nil {
		return err
	}
	t := template.Must(template.New("unnamed").Parse(string(templatebytes)))
	err = t.Execute(fout, page)
	return err
}

func OutputPath(page *Page, configpath, rootpath string) string {
	var fnmhtml string
	if len(page.Output) == 0 {
		fnmhtml = filepath.Base(configpath[:len(configpath)-5]) + ".html"
	} else {
		fnmhtml = filepath.Join(rootpath, page.Output)
	}
	return fnmhtml
}

func main() {

	var args []string
	args = os.Args[1:]
	if len(args) == 0 {
		fmt.Println("At least one content file must be specified")
	}

	var pagePtr *Page
	var fnmhtml, rootpath string
	for _, configpath := range args {
		if _, err := os.Stat(configpath); !os.IsNotExist(err) {

			// Read configuration
			fmt.Println("parsing", configpath)
			rootpath = filepath.Dir(configpath)
			pagePtr, err = ReadConfig(configpath)
			if err != nil {
				fmt.Println(err)
				break
			}
			fmt.Println("using template at", pagePtr.TemplatePath)
			fnmhtml = OutputPath(pagePtr, configpath, rootpath)
			fmt.Println("writing to", fnmhtml)

			// Write output
			fout, err := os.Create(fnmhtml)
			if err != nil {
				fmt.Println(err)
				break
			}

			err = BuildPage(rootpath, fout, pagePtr)
			if err != nil {
				fmt.Println(err)
			}
		} else {
			fmt.Println(configpath, "does not exist")
		}
	}
}
<|endoftext|>


<commit_before>package blackfridaytext

import (
	"testing"
)

func TestBasic(t *testing.T) {
	in := "Basic Test"
	out := string(MarkdownToTextNoMetadata([]byte(in), nil))
	exp := "Basic Test\n"
	if out != exp {
		t.Errorf("%#v != %#v", out, exp)
	}
}

func TestMetadata(t *testing.T) {
	in := `One: Two
Three: Four
`
	out, pos := MarkdownMetadata([]byte(in))
	exp := [][]string{
		[]string{"One", "Two"},
		[]string{"Three", "Four"},
	}
	expPos := 25
	if !metadataEqualForTesting(out, exp) {
		t.Errorf("%#v != %#v", out, exp)
	}
	if pos != expPos {
		t.Errorf("%#v != %#v", pos, expPos)
	}
	in = `One: Two
Three: Four

And: More
Text: Here
`
	out, pos = MarkdownMetadata([]byte(in))
	exp = [][]string{
		[]string{"One", "Two"},
		[]string{"Three", "Four"},
	}
	expPos = 25
	if !metadataEqualForTesting(out, exp) {
		t.Errorf("%#v != %#v", out, exp)
	}
	if pos != expPos {
		t.Errorf("%#v != %#v", pos, expPos)
	}
	in = `One: Two
Three: Four

Above items don't have trailing two spaces, unlike the tests before.
`
	out, pos = MarkdownMetadata([]byte(in))
	exp = [][]string{
		[]string{"One", "Two"},
		[]string{"Three", "Four"},
	}
	expPos = 21
	if !metadataEqualForTesting(out, exp) {
		t.Errorf("%#v != %#v", out, exp)
	}
	if pos != expPos {
		t.Errorf("%#v != %#v", pos, expPos)
	}
	in = `One: Two
Three: Four`
	out, pos = MarkdownMetadata([]byte(in))
	exp = [][]string{
		[]string{"One", "Two"},
		[]string{"Three", "Four"},
	}
	expPos = 19
	if !metadataEqualForTesting(out, exp) {
		t.Errorf("%#v != %#v", out, exp)
	}
	if pos != expPos {
		t.Errorf("%#v != %#v", pos, expPos)
	}
	in = `One: Two
Three: Four
and text, which indicates the metadata was just looking like metadata.`
	out, pos = MarkdownMetadata([]byte(in))
	exp = [][]string{}
	expPos = 0
	if !metadataEqualForTesting(out, exp) {
		t.Errorf("%#v != %#v", out, exp)
	}
	if pos != expPos {
		t.Errorf("%#v != %#v", pos, expPos)
	}
	in = `One: Two
Three: Four
and text, which indicates the metadata was just looking like metadata.

///

With a summary (before the ///).`
	out, pos = MarkdownMetadata([]byte(in))
	exp = [][]string{[]string{"Summary", `One: Two
Three: Four
and text, which indicates the metadata was just looking like metadata.
`}}
	expPos = 0
	if !metadataEqualForTesting(out, exp) {
		t.Errorf("%#v != %#v", out, exp)
	}
	if pos != expPos {
		t.Errorf("%#v != %#v", pos, expPos)
	}
	in = `One: Two
Three: Four
and text, which indicates the metadata was just looking like metadata.

///
///

With a summary (before the ///).`
	out, pos = MarkdownMetadata([]byte(in))
	exp = [][]string{[]string{"Summary", `One: Two
Three: Four
and text, which indicates the metadata was just looking like metadata.
`}}
	expPos = 101
	if !metadataEqualForTesting(out, exp) {
		t.Errorf("%#v != %#v", out, exp)
	}
	if pos != expPos {
		t.Errorf("%#v != %#v", pos, expPos)
	}
	in = `One: Two
Three: Four

Blahzay blah.

///

With a summary (before the ///).`
	out, pos = MarkdownMetadata([]byte(in))
	exp = [][]string{
		[]string{"One", "Two"},
		[]string{"Three", "Four"},
		[]string{"Summary", "\nBlahzay blah.\n"},
	}
	expPos = 21
	if !metadataEqualForTesting(out, exp) {
		t.Errorf("%#v != %#v", out, exp)
	}
	if pos != expPos {
		t.Errorf("%#v != %#v", pos, expPos)
	}
	in = `One: Two
Three: Four

Blahzay blah.

///
///

With a summary (before the ///).`
	out, pos = MarkdownMetadata([]byte(in))
	exp = [][]string{
		[]string{"One", "Two"},
		[]string{"Three", "Four"},
		[]string{"Summary", "\nBlahzay blah.\n"},
	}
	expPos = 45
	if !metadataEqualForTesting(out, exp) {
		t.Errorf("%#v != %#v", out, exp)
	}
	if pos != expPos {
		t.Errorf("%#v != %#v", pos, expPos)
	}
}

func TestGeneral(t *testing.T) {
	in := `This is a general test of features.

# Heading One

Normal paragraph with a reasonable amount of text in order to test wrapping.

Longer paragraph to test much the same, but with
new lines embedded to test they
are ignored.

## Heading Two

Some *emphasized*, **doubly emphasized**, and ***triply emphasized*** text.

A bit of ~~striked through~~ text as well.

# Heading Three

 *  A simple
     *  list
     *  of
 *  items

Name  | Age
------|----
Bob   | 27
Alice | 23

` + "```" + `
A block of code
   that will
  not wrap, of course
` + "```" + `

Also, a quick bit of ` + "`inline code`" + ` to test.

Autolinking http://example.com for a test.

---

> Block quote at level
> one.
>
>> Block quote at level
>> two
>
> Back to
> level one.

Test an ![image](image.png) and a [link](http://example.com).

Force a line break
just above this.

`
	out := string(MarkdownToTextNoMetadata([]byte(in), &Options{Width: 40}))
	exp := `This is a general test of features.

--[ Heading One ]--

    Normal paragraph with a reasonable
    amount of text in order to test
    wrapping.

    Longer paragraph to test much the
    same, but with new lines embedded
    to test they are ignored.

    --[ Heading Two ]--

        Some *emphasized*, **doubly
        emphasized**, and ***triply
        emphasized*** text.

        A bit of ~~striked through~~
        text as well.

--[ Heading Three ]--

      * A simple
          * list
          * of
      * items

    +-------+-----+
    | Name  | Age |
    +-------+-----+
    | Bob   | 27  |
    | Alice | 23  |
    +-------+-----+

    A block of code
       that will
      not wrap, of course

    Also, a quick bit of "inline code"
    to test.

    Autolinking http://example.com for
    a test.

    ------------------------------------

    > Block quote at level one.

    > > Block quote at level two

    > Back to level one.

    Test an [image] image.png and a
    [link] http://example.com.

    Force a line break
    just above this.
`
	if out != exp {
		t.Errorf("%#v != %#v", out, exp)
	}
}

func metadataEqualForTesting(m1 [][]string, m2 [][]string) bool {
	if len(m1) != len(m2) {
		return false
	}
	for i := 0; i < len(m1); i++ {
		if len(m1[i]) != len(m2[i]) {
			return false
		}
		for j := 0; j < len(m1[i]); j++ {
			if m1[i][j] != m2[i][j] {
				return false
			}
		}
	}
	return true
}
<commit_msg>Copyright header<commit_after>// Copyright 2014 Gregory Holt. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package blackfridaytext

import (
	"testing"
)

func TestBasic(t *testing.T) {
	in := "Basic Test"
	out := string(MarkdownToTextNoMetadata([]byte(in), nil))
	exp := "Basic Test\n"
	if out != exp {
		t.Errorf("%#v != %#v", out, exp)
	}
}

func TestMetadata(t *testing.T) {
	in := `One: Two
Three: Four
`
	out, pos := MarkdownMetadata([]byte(in))
	exp := [][]string{
		[]string{"One", "Two"},
		[]string{"Three", "Four"},
	}
	expPos := 25
	if !metadataEqualForTesting(out, exp) {
		t.Errorf("%#v != %#v", out, exp)
	}
	if pos != expPos {
		t.Errorf("%#v != %#v", pos, expPos)
	}
	in = `One: Two
Three: Four

And: More
Text: Here
`
	out, pos = MarkdownMetadata([]byte(in))
	exp = [][]string{
		[]string{"One", "Two"},
		[]string{"Three", "Four"},
	}
	expPos = 25
	if !metadataEqualForTesting(out, exp) {
		t.Errorf("%#v != %#v", out, exp)
	}
	if pos != expPos {
		t.Errorf("%#v != %#v", pos, expPos)
	}
	in = `One: Two
Three: Four

Above items don't have trailing two spaces, unlike the tests before.
`
	out, pos = MarkdownMetadata([]byte(in))
	exp = [][]string{
		[]string{"One", "Two"},
		[]string{"Three", "Four"},
	}
	expPos = 21
	if !metadataEqualForTesting(out, exp) {
		t.Errorf("%#v != %#v", out, exp)
	}
	if pos != expPos {
		t.Errorf("%#v != %#v", pos, expPos)
	}
	in = `One: Two
Three: Four`
	out, pos = MarkdownMetadata([]byte(in))
	exp = [][]string{
		[]string{"One", "Two"},
		[]string{"Three", "Four"},
	}
	expPos = 19
	if !metadataEqualForTesting(out, exp) {
		t.Errorf("%#v != %#v", out, exp)
	}
	if pos != expPos {
		t.Errorf("%#v != %#v", pos, expPos)
	}
	in = `One: Two
Three: Four
and text, which indicates the metadata was just looking like metadata.`
	out, pos = MarkdownMetadata([]byte(in))
	exp = [][]string{}
	expPos = 0
	if !metadataEqualForTesting(out, exp) {
		t.Errorf("%#v != %#v", out, exp)
	}
	if pos != expPos {
		t.Errorf("%#v != %#v", pos, expPos)
	}
	in = `One: Two
Three: Four
and text, which indicates the metadata was just looking like metadata.

///

With a summary (before the ///).`
	out, pos = MarkdownMetadata([]byte(in))
	exp = [][]string{[]string{"Summary", `One: Two
Three: Four
and text, which indicates the metadata was just looking like metadata.
`}}
	expPos = 0
	if !metadataEqualForTesting(out, exp) {
		t.Errorf("%#v != %#v", out, exp)
	}
	if pos != expPos {
		t.Errorf("%#v != %#v", pos, expPos)
	}
	in = `One: Two
Three: Four
and text, which indicates the metadata was just looking like metadata.

///
///

With a summary (before the ///).`
	out, pos = MarkdownMetadata([]byte(in))
	exp = [][]string{[]string{"Summary", `One: Two
Three: Four
and text, which indicates the metadata was just looking like metadata.
`}}
	expPos = 101
	if !metadataEqualForTesting(out, exp) {
		t.Errorf("%#v != %#v", out, exp)
	}
	if pos != expPos {
		t.Errorf("%#v != %#v", pos, expPos)
	}
	in = `One: Two
Three: Four

Blahzay blah.

///

With a summary (before the ///).`
	out, pos = MarkdownMetadata([]byte(in))
	exp = [][]string{
		[]string{"One", "Two"},
		[]string{"Three", "Four"},
		[]string{"Summary", "\nBlahzay blah.\n"},
	}
	expPos = 21
	if !metadataEqualForTesting(out, exp) {
		t.Errorf("%#v != %#v", out, exp)
	}
	if pos != expPos {
		t.Errorf("%#v != %#v", pos, expPos)
	}
	in = `One: Two
Three: Four

Blahzay blah.

///
///

With a summary (before the ///).`
	out, pos = MarkdownMetadata([]byte(in))
	exp = [][]string{
		[]string{"One", "Two"},
		[]string{"Three", "Four"},
		[]string{"Summary", "\nBlahzay blah.\n"},
	}
	expPos = 45
	if !metadataEqualForTesting(out, exp) {
		t.Errorf("%#v != %#v", out, exp)
	}
	if pos != expPos {
		t.Errorf("%#v != %#v", pos, expPos)
	}
}

func TestGeneral(t *testing.T) {
	in := `This is a general test of features.

# Heading One

Normal paragraph with a reasonable amount of text in order to test wrapping.

Longer paragraph to test much the same, but with
new lines embedded to test they
are ignored.

## Heading Two

Some *emphasized*, **doubly emphasized**, and ***triply emphasized*** text.

A bit of ~~striked through~~ text as well.

# Heading Three

 *  A simple
     *  list
     *  of
 *  items

Name  | Age
------|----
Bob   | 27
Alice | 23

` + "```" + `
A block of code
   that will
  not wrap, of course
` + "```" + `

Also, a quick bit of ` + "`inline code`" + ` to test.

Autolinking http://example.com for a test.

---

> Block quote at level
> one.
>
>> Block quote at level
>> two
>
> Back to
> level one.

Test an ![image](image.png) and a [link](http://example.com).

Force a line break
just above this.

`
	out := string(MarkdownToTextNoMetadata([]byte(in), &Options{Width: 40}))
	exp := `This is a general test of features.

--[ Heading One ]--

    Normal paragraph with a reasonable
    amount of text in order to test
    wrapping.

    Longer paragraph to test much the
    same, but with new lines embedded
    to test they are ignored.

    --[ Heading Two ]--

        Some *emphasized*, **doubly
        emphasized**, and ***triply
        emphasized*** text.

        A bit of ~~striked through~~
        text as well.

--[ Heading Three ]--

      * A simple
          * list
          * of
      * items

    +-------+-----+
    | Name  | Age |
    +-------+-----+
    | Bob   | 27  |
    | Alice | 23  |
    +-------+-----+

    A block of code
       that will
      not wrap, of course

    Also, a quick bit of "inline code"
    to test.

    Autolinking http://example.com for
    a test.

    ------------------------------------

    > Block quote at level one.

    > > Block quote at level two

    > Back to level one.

    Test an [image] image.png and a
    [link] http://example.com.

    Force a line break
    just above this.
`
	if out != exp {
		t.Errorf("%#v != %#v", out, exp)
	}
}

func metadataEqualForTesting(m1 [][]string, m2 [][]string) bool {
	if len(m1) != len(m2) {
		return false
	}
	for i := 0; i < len(m1); i++ {
		if len(m1[i]) != len(m2[i]) {
			return false
		}
		for j := 0; j < len(m1[i]); j++ {
			if m1[i][j] != m2[i][j] {
				return false
			}
		}
	}
	return true
}
<|endoftext|>


<commit_before>package restfulspec

import (
	"net/http"
	"reflect"
	"regexp"
	"sort"
	"strconv"
	"strings"

	"github.com/go-openapi/spec"

	"github.com/emicklei/go-restful/v3"
)

const (
	// KeyOpenAPITags is a Metadata key for a restful Route
	KeyOpenAPITags = "openapi.tags"

	// ExtensionPrefix is the only prefix accepted for VendorExtensible extension keys
	ExtensionPrefix = "x-"

	arrayType      = "array"
	definitionRoot = "#/definitions/"
)

func buildPaths(ws *restful.WebService, cfg Config) spec.Paths {
	p := spec.Paths{Paths: map[string]spec.PathItem{}}
	for _, each := range ws.Routes() {
		path, patterns := sanitizePath(each.Path)
		existingPathItem, ok := p.Paths[path]
		if !ok {
			existingPathItem = spec.PathItem{}
		}
		p.Paths[path] = buildPathItem(ws, each, existingPathItem, patterns, cfg)
	}
	return p
}

// sanitizePath removes regex expressions from named path params,
// since openapi only supports setting the pattern as a property named "pattern".
// Expressions like "/api/v1/{name:[a-z]}/" are converted to "/api/v1/{name}/".
// The second return value is a map which contains the mapping from the path parameter
// name to the extracted pattern
func sanitizePath(restfulPath string) (string, map[string]string) {
	openapiPath := ""
	patterns := map[string]string{}
	for _, fragment := range strings.Split(restfulPath, "/") {
		if fragment == "" {
			continue
		}
		if strings.HasPrefix(fragment, "{") && strings.Contains(fragment, ":") {
			split := strings.Split(fragment, ":")
			fragment = split[0][1:]
			pattern := split[1][:len(split[1])-1]
			patterns[fragment] = pattern
			fragment = "{" + fragment + "}"
		}
		openapiPath += "/" + fragment
	}
	return openapiPath, patterns
}

func buildPathItem(ws *restful.WebService, r restful.Route, existingPathItem spec.PathItem, patterns map[string]string, cfg Config) spec.PathItem {
	op := buildOperation(ws, r, patterns, cfg)
	switch r.Method {
	case http.MethodGet:
		existingPathItem.Get = op
	case http.MethodPost:
		existingPathItem.Post = op
	case http.MethodPut:
		existingPathItem.Put = op
	case http.MethodDelete:
		existingPathItem.Delete = op
	case http.MethodPatch:
		existingPathItem.Patch = op
	case http.MethodOptions:
		existingPathItem.Options = op
	case http.MethodHead:
		existingPathItem.Head = op
	}
	return existingPathItem
}

func buildOperation(ws *restful.WebService, r restful.Route, patterns map[string]string, cfg Config) *spec.Operation {
	o := spec.NewOperation(r.Operation)
	o.Description = r.Notes
	o.Summary = stripTags(r.Doc)
	o.Consumes = r.Consumes
	o.Produces = r.Produces
	o.Deprecated = r.Deprecated
	if r.Metadata != nil {
		if tags, ok := r.Metadata[KeyOpenAPITags]; ok {
			if tagList, ok := tags.([]string); ok {
				o.Tags = tagList
			}
		}
	}

	extractVendorExtensions(&o.VendorExtensible, r.ExtensionProperties)

	// collect any path parameters
	for _, param := range ws.PathParameters() {
		p := buildParameter(r, param, patterns[param.Data().Name], cfg)
		o.Parameters = append(o.Parameters, p)
	}
	// route specific params
	for _, param := range r.ParameterDocs {
		p := buildParameter(r, param, patterns[param.Data().Name], cfg)
		o.Parameters = append(o.Parameters, p)
	}
	o.Responses = new(spec.Responses)
	props := &o.Responses.ResponsesProps
	props.StatusCodeResponses = make(map[int]spec.Response, len(r.ResponseErrors))
	for k, v := range r.ResponseErrors {
		r := buildResponse(v, cfg)
		props.StatusCodeResponses[k] = r
	}
	if r.DefaultResponse != nil {
		rsp := buildResponse(*r.DefaultResponse, cfg)
		o.Responses.Default = &rsp
	}
	if len(o.Responses.StatusCodeResponses) == 0 {
		o.Responses.StatusCodeResponses[200] = spec.Response{ResponseProps: spec.ResponseProps{Description: http.StatusText(http.StatusOK)}}
	}
	return o
}

// stringAutoType automatically picks the correct type from an ambiguously typed
// string. Ex. numbers become int, true/false become bool, etc.
func stringAutoType(ambiguous string) interface{} {
	if ambiguous == "" {
		return nil
	}
	if parsedInt, err := strconv.ParseInt(ambiguous, 10, 64); err == nil {
		return parsedInt
	}
	if parsedBool, err := strconv.ParseBool(ambiguous); err == nil {
		return parsedBool
	}
	return ambiguous
}

func extractVendorExtensions(extensible *spec.VendorExtensible, extensions restful.ExtensionProperties) {
	if len(extensions.Extensions) > 0 {
		for key := range extensions.Extensions {
			if strings.HasPrefix(key, ExtensionPrefix) {
				extensible.AddExtension(key, extensions.Extensions[key])
			}
		}
	}
}

func buildParameter(r restful.Route, restfulParam *restful.Parameter, pattern string, cfg Config) spec.Parameter {
	p := spec.Parameter{}
	param := restfulParam.Data()
	p.In = asParamType(param.Kind)

	if param.AllowMultiple {
		// If the param is an array apply the validations to the items in it
		p.Type = arrayType
		p.Items = spec.NewItems()
		p.Items.Type = param.DataType
		p.Items.Pattern = param.Pattern
		p.Items.MinLength = param.MinLength
		p.Items.MaxLength = param.MaxLength
		p.CollectionFormat = param.CollectionFormat
		p.MinItems = param.MinItems
		p.MaxItems = param.MaxItems
		p.UniqueItems = param.UniqueItems
	} else {
		// Otherwise, for non-arrays, apply the validations directly to the param
		p.Type = param.DataType
		p.MinLength = param.MinLength
		p.MaxLength = param.MaxLength
		p.Minimum = param.Minimum
		p.Maximum = param.Maximum
	}

	// Prefer PossibleValues over deprecated AllowableValues
	if numPossible := len(param.PossibleValues); numPossible > 0 {
		// init Enum to our known size and populate it
		p.Enum = make([]interface{}, 0, numPossible)
		for _, value := range param.PossibleValues {
			p.Enum = append(p.Enum, value)
		}
	} else {
		if numAllowable := len(param.AllowableValues); numAllowable > 0 {
			// If allowable values are defined, set the enum array to the sorted values
			allowableSortedKeys := make([]string, 0, numAllowable)
			for k := range param.AllowableValues {
				allowableSortedKeys = append(allowableSortedKeys, k)
			}

			// sort away
			sort.Strings(allowableSortedKeys)

			// init Enum to our known size and populate it
			p.Enum = make([]interface{}, 0, numAllowable)
			for _, key := range allowableSortedKeys {
				p.Enum = append(p.Enum, param.AllowableValues[key])
			}
		}
	}

	p.Description = param.Description
	p.Name = param.Name
	p.Required = param.Required
	p.AllowEmptyValue = param.AllowEmptyValue

	if param.Kind == restful.PathParameterKind {
		p.Pattern = pattern
	} else if !param.AllowMultiple {
		p.Pattern = param.Pattern
	}
	st := reflect.TypeOf(r.ReadSample)
	if param.Kind == restful.BodyParameterKind && r.ReadSample != nil && param.DataType == st.String() {
		p.Schema = new(spec.Schema)
		p.SimpleSchema = spec.SimpleSchema{}
		if st.Kind() == reflect.Array || st.Kind() == reflect.Slice {
			dataTypeName := keyFrom(st.Elem(), cfg)
			p.Schema.Type = []string{arrayType}
			p.Schema.Items = &spec.SchemaOrArray{
				Schema: &spec.Schema{},
			}
			isPrimitive := isPrimitiveType(dataTypeName)
			if isPrimitive {
				mapped := jsonSchemaType(dataTypeName)
				p.Schema.Items.Schema.Type = []string{mapped}
			} else {
				p.Schema.Items.Schema.Ref = spec.MustCreateRef(definitionRoot + dataTypeName)
			}
		} else {
			dataTypeName := keyFrom(st, cfg)
			p.Schema.Ref = spec.MustCreateRef(definitionRoot + dataTypeName)
		}

	} else {
		if param.AllowMultiple {
			p.Type = arrayType
			p.Items = spec.NewItems()
			p.Items.Type = param.DataType
			p.CollectionFormat = param.CollectionFormat
		} else {
			p.Type = param.DataType
		}
		p.Default = stringAutoType(param.DefaultValue)
		p.Format = param.DataFormat
	}

	extractVendorExtensions(&p.VendorExtensible, param.ExtensionProperties)

	return p
}

func buildResponse(e restful.ResponseError, cfg Config) (r spec.Response) {
	r.Description = e.Message
	if e.Model != nil {
		st := reflect.TypeOf(e.Model)
		if st.Kind() == reflect.Ptr {
			// For pointer type, use element type as the key; otherwise we'll
			// endup with '#/definitions/*Type' which violates openapi spec.
			st = st.Elem()
		}
		r.Schema = new(spec.Schema)
		if st.Kind() == reflect.Array || st.Kind() == reflect.Slice {
			modelName := keyFrom(st.Elem(), cfg)
			r.Schema.Type = []string{arrayType}
			r.Schema.Items = &spec.SchemaOrArray{
				Schema: &spec.Schema{},
			}
			isPrimitive := isPrimitiveType(modelName)
			if isPrimitive {
				mapped := jsonSchemaType(modelName)
				r.Schema.Items.Schema.Type = []string{mapped}
			} else {
				r.Schema.Items.Schema.Ref = spec.MustCreateRef(definitionRoot + modelName)
			}
		} else {
			modelName := keyFrom(st, cfg)
			if isPrimitiveType(modelName) {
				// If the response is a primitive type, then don't reference any definitions.
				// Instead, set the schema's "type" to the model name.
				r.Schema.AddType(modelName, "")
			} else {
				modelName := keyFrom(st, cfg)
				r.Schema.Ref = spec.MustCreateRef(definitionRoot + modelName)
			}
		}
	}

	if len(e.Headers) > 0 {
		r.Headers = make(map[string]spec.Header, len(e.Headers))
		for k, v := range e.Headers {
			r.Headers[k] = buildHeader(v)
		}
	}

	extractVendorExtensions(&r.VendorExtensible, e.ExtensionProperties)
	return r
}

// buildHeader builds a specification header structure from restful.Header
func buildHeader(header restful.Header) spec.Header {
	responseHeader := spec.Header{}
	responseHeader.Type = header.Type
	responseHeader.Description = header.Description

	// If type is "array" items field is required
	if header.Type == arrayType {
		responseHeader.Items = buildHeadersItems(header.Items)
	}

	return responseHeader
}

// buildHeadersItems builds
func buildHeadersItems(items *restful.Items) *spec.Items {
	responseItems := spec.NewItems()
	responseItems.Format = items.Format
	responseItems.Type = items.Type
	responseItems.Default = items.Default
	responseItems.CollectionFormat = items.CollectionFormat
	if items.Items != nil {
		responseItems.Items = buildHeadersItems(items.Items)
	}

	return responseItems
}

// stripTags takes a snippet of HTML and returns only the text content.
// For example, `<b><Hi!></b> <br>` -> `<Hi!> `.
func stripTags(html string) string {
	re := regexp.MustCompile("<[^>]*>")
	return re.ReplaceAllString(html, "")
}

func isPrimitiveType(modelName string) bool {
	if len(modelName) == 0 {
		return false
	}
	return strings.Contains("uint uint8 uint16 uint32 uint64 int int8 int16 int32 int64 float32 float64 bool string byte rune time.Time time.Duration", modelName)
}

func jsonSchemaType(modelName string) string {
	schemaMap := map[string]string{
		"uint":   "integer",
		"uint8":  "integer",
		"uint16": "integer",
		"uint32": "integer",
		"uint64": "integer",

		"int":   "integer",
		"int8":  "integer",
		"int16": "integer",
		"int32": "integer",
		"int64": "integer",

		"byte":          "integer",
		"float64":       "number",
		"float32":       "number",
		"bool":          "boolean",
		"time.Time":     "string",
		"time.Duration": "integer",
	}
	mapped, ok := schemaMap[modelName]
	if !ok {
		return modelName // use as is (custom or struct)
	}
	return mapped
}
<commit_msg>Add missing fields of response header object (#89)<commit_after>package restfulspec

import (
	"net/http"
	"reflect"
	"regexp"
	"sort"
	"strconv"
	"strings"

	"github.com/go-openapi/spec"

	"github.com/emicklei/go-restful/v3"
)

const (
	// KeyOpenAPITags is a Metadata key for a restful Route
	KeyOpenAPITags = "openapi.tags"

	// ExtensionPrefix is the only prefix accepted for VendorExtensible extension keys
	ExtensionPrefix = "x-"

	arrayType      = "array"
	definitionRoot = "#/definitions/"
)

func buildPaths(ws *restful.WebService, cfg Config) spec.Paths {
	p := spec.Paths{Paths: map[string]spec.PathItem{}}
	for _, each := range ws.Routes() {
		path, patterns := sanitizePath(each.Path)
		existingPathItem, ok := p.Paths[path]
		if !ok {
			existingPathItem = spec.PathItem{}
		}
		p.Paths[path] = buildPathItem(ws, each, existingPathItem, patterns, cfg)
	}
	return p
}

// sanitizePath removes regex expressions from named path params,
// since openapi only supports setting the pattern as a property named "pattern".
// Expressions like "/api/v1/{name:[a-z]}/" are converted to "/api/v1/{name}/".
// The second return value is a map which contains the mapping from the path parameter
// name to the extracted pattern
func sanitizePath(restfulPath string) (string, map[string]string) {
	openapiPath := ""
	patterns := map[string]string{}
	for _, fragment := range strings.Split(restfulPath, "/") {
		if fragment == "" {
			continue
		}
		if strings.HasPrefix(fragment, "{") && strings.Contains(fragment, ":") {
			split := strings.Split(fragment, ":")
			fragment = split[0][1:]
			pattern := split[1][:len(split[1])-1]
			patterns[fragment] = pattern
			fragment = "{" + fragment + "}"
		}
		openapiPath += "/" + fragment
	}
	return openapiPath, patterns
}

func buildPathItem(ws *restful.WebService, r restful.Route, existingPathItem spec.PathItem, patterns map[string]string, cfg Config) spec.PathItem {
	op := buildOperation(ws, r, patterns, cfg)
	switch r.Method {
	case http.MethodGet:
		existingPathItem.Get = op
	case http.MethodPost:
		existingPathItem.Post = op
	case http.MethodPut:
		existingPathItem.Put = op
	case http.MethodDelete:
		existingPathItem.Delete = op
	case http.MethodPatch:
		existingPathItem.Patch = op
	case http.MethodOptions:
		existingPathItem.Options = op
	case http.MethodHead:
		existingPathItem.Head = op
	}
	return existingPathItem
}

func buildOperation(ws *restful.WebService, r restful.Route, patterns map[string]string, cfg Config) *spec.Operation {
	o := spec.NewOperation(r.Operation)
	o.Description = r.Notes
	o.Summary = stripTags(r.Doc)
	o.Consumes = r.Consumes
	o.Produces = r.Produces
	o.Deprecated = r.Deprecated
	if r.Metadata != nil {
		if tags, ok := r.Metadata[KeyOpenAPITags]; ok {
			if tagList, ok := tags.([]string); ok {
				o.Tags = tagList
			}
		}
	}

	extractVendorExtensions(&o.VendorExtensible, r.ExtensionProperties)

	// collect any path parameters
	for _, param := range ws.PathParameters() {
		p := buildParameter(r, param, patterns[param.Data().Name], cfg)
		o.Parameters = append(o.Parameters, p)
	}
	// route specific params
	for _, param := range r.ParameterDocs {
		p := buildParameter(r, param, patterns[param.Data().Name], cfg)
		o.Parameters = append(o.Parameters, p)
	}
	o.Responses = new(spec.Responses)
	props := &o.Responses.ResponsesProps
	props.StatusCodeResponses = make(map[int]spec.Response, len(r.ResponseErrors))
	for k, v := range r.ResponseErrors {
		r := buildResponse(v, cfg)
		props.StatusCodeResponses[k] = r
	}
	if r.DefaultResponse != nil {
		rsp := buildResponse(*r.DefaultResponse, cfg)
		o.Responses.Default = &rsp
	}
	if len(o.Responses.StatusCodeResponses) == 0 {
		o.Responses.StatusCodeResponses[200] = spec.Response{ResponseProps: spec.ResponseProps{Description: http.StatusText(http.StatusOK)}}
	}
	return o
}

// stringAutoType automatically picks the correct type from an ambiguously typed
// string. Ex. numbers become int, true/false become bool, etc.
func stringAutoType(ambiguous string) interface{} {
	if ambiguous == "" {
		return nil
	}
	if parsedInt, err := strconv.ParseInt(ambiguous, 10, 64); err == nil {
		return parsedInt
	}
	if parsedBool, err := strconv.ParseBool(ambiguous); err == nil {
		return parsedBool
	}
	return ambiguous
}

func extractVendorExtensions(extensible *spec.VendorExtensible, extensions restful.ExtensionProperties) {
	if len(extensions.Extensions) > 0 {
		for key := range extensions.Extensions {
			if strings.HasPrefix(key, ExtensionPrefix) {
				extensible.AddExtension(key, extensions.Extensions[key])
			}
		}
	}
}

func buildParameter(r restful.Route, restfulParam *restful.Parameter, pattern string, cfg Config) spec.Parameter {
	p := spec.Parameter{}
	param := restfulParam.Data()
	p.In = asParamType(param.Kind)

	if param.AllowMultiple {
		// If the param is an array apply the validations to the items in it
		p.Type = arrayType
		p.Items = spec.NewItems()
		p.Items.Type = param.DataType
		p.Items.Pattern = param.Pattern
		p.Items.MinLength = param.MinLength
		p.Items.MaxLength = param.MaxLength
		p.CollectionFormat = param.CollectionFormat
		p.MinItems = param.MinItems
		p.MaxItems = param.MaxItems
		p.UniqueItems = param.UniqueItems
	} else {
		// Otherwise, for non-arrays, apply the validations directly to the param
		p.Type = param.DataType
		p.MinLength = param.MinLength
		p.MaxLength = param.MaxLength
		p.Minimum = param.Minimum
		p.Maximum = param.Maximum
	}

	// Prefer PossibleValues over deprecated AllowableValues
	if numPossible := len(param.PossibleValues); numPossible > 0 {
		// init Enum to our known size and populate it
		p.Enum = make([]interface{}, 0, numPossible)
		for _, value := range param.PossibleValues {
			p.Enum = append(p.Enum, value)
		}
	} else {
		if numAllowable := len(param.AllowableValues); numAllowable > 0 {
			// If allowable values are defined, set the enum array to the sorted values
			allowableSortedKeys := make([]string, 0, numAllowable)
			for k := range param.AllowableValues {
				allowableSortedKeys = append(allowableSortedKeys, k)
			}

			// sort away
			sort.Strings(allowableSortedKeys)

			// init Enum to our known size and populate it
			p.Enum = make([]interface{}, 0, numAllowable)
			for _, key := range allowableSortedKeys {
				p.Enum = append(p.Enum, param.AllowableValues[key])
			}
		}
	}

	p.Description = param.Description
	p.Name = param.Name
	p.Required = param.Required
	p.AllowEmptyValue = param.AllowEmptyValue

	if param.Kind == restful.PathParameterKind {
		p.Pattern = pattern
	} else if !param.AllowMultiple {
		p.Pattern = param.Pattern
	}
	st := reflect.TypeOf(r.ReadSample)
	if param.Kind == restful.BodyParameterKind && r.ReadSample != nil && param.DataType == st.String() {
		p.Schema = new(spec.Schema)
		p.SimpleSchema = spec.SimpleSchema{}
		if st.Kind() == reflect.Array || st.Kind() == reflect.Slice {
			dataTypeName := keyFrom(st.Elem(), cfg)
			p.Schema.Type = []string{arrayType}
			p.Schema.Items = &spec.SchemaOrArray{
				Schema: &spec.Schema{},
			}
			isPrimitive := isPrimitiveType(dataTypeName)
			if isPrimitive {
				mapped := jsonSchemaType(dataTypeName)
				p.Schema.Items.Schema.Type = []string{mapped}
			} else {
				p.Schema.Items.Schema.Ref = spec.MustCreateRef(definitionRoot + dataTypeName)
			}
		} else {
			dataTypeName := keyFrom(st, cfg)
			p.Schema.Ref = spec.MustCreateRef(definitionRoot + dataTypeName)
		}

	} else {
		if param.AllowMultiple {
			p.Type = arrayType
			p.Items = spec.NewItems()
			p.Items.Type = param.DataType
			p.CollectionFormat = param.CollectionFormat
		} else {
			p.Type = param.DataType
		}
		p.Default = stringAutoType(param.DefaultValue)
		p.Format = param.DataFormat
	}

	extractVendorExtensions(&p.VendorExtensible, param.ExtensionProperties)

	return p
}

func buildResponse(e restful.ResponseError, cfg Config) (r spec.Response) {
	r.Description = e.Message
	if e.Model != nil {
		st := reflect.TypeOf(e.Model)
		if st.Kind() == reflect.Ptr {
			// For pointer type, use element type as the key; otherwise we'll
			// endup with '#/definitions/*Type' which violates openapi spec.
			st = st.Elem()
		}
		r.Schema = new(spec.Schema)
		if st.Kind() == reflect.Array || st.Kind() == reflect.Slice {
			modelName := keyFrom(st.Elem(), cfg)
			r.Schema.Type = []string{arrayType}
			r.Schema.Items = &spec.SchemaOrArray{
				Schema: &spec.Schema{},
			}
			isPrimitive := isPrimitiveType(modelName)
			if isPrimitive {
				mapped := jsonSchemaType(modelName)
				r.Schema.Items.Schema.Type = []string{mapped}
			} else {
				r.Schema.Items.Schema.Ref = spec.MustCreateRef(definitionRoot + modelName)
			}
		} else {
			modelName := keyFrom(st, cfg)
			if isPrimitiveType(modelName) {
				// If the response is a primitive type, then don't reference any definitions.
				// Instead, set the schema's "type" to the model name.
				r.Schema.AddType(modelName, "")
			} else {
				modelName := keyFrom(st, cfg)
				r.Schema.Ref = spec.MustCreateRef(definitionRoot + modelName)
			}
		}
	}

	if len(e.Headers) > 0 {
		r.Headers = make(map[string]spec.Header, len(e.Headers))
		for k, v := range e.Headers {
			r.Headers[k] = buildHeader(v)
		}
	}

	extractVendorExtensions(&r.VendorExtensible, e.ExtensionProperties)
	return r
}

// buildHeader builds a specification header structure from restful.Header
func buildHeader(header restful.Header) spec.Header {
	responseHeader := spec.Header{}
	responseHeader.Type = header.Type
	responseHeader.Description = header.Description
	responseHeader.Format = header.Format
	responseHeader.Default = header.Default

	// If type is "array" items field is required
	if header.Type == arrayType {
		responseHeader.CollectionFormat = header.CollectionFormat
		responseHeader.Items = buildHeadersItems(header.Items)
	}

	return responseHeader
}

// buildHeadersItems builds
func buildHeadersItems(items *restful.Items) *spec.Items {
	responseItems := spec.NewItems()
	responseItems.Format = items.Format
	responseItems.Type = items.Type
	responseItems.Default = items.Default
	responseItems.CollectionFormat = items.CollectionFormat
	if items.Items != nil {
		responseItems.Items = buildHeadersItems(items.Items)
	}

	return responseItems
}

// stripTags takes a snippet of HTML and returns only the text content.
// For example, `<b><Hi!></b> <br>` -> `<Hi!> `.
func stripTags(html string) string {
	re := regexp.MustCompile("<[^>]*>")
	return re.ReplaceAllString(html, "")
}

func isPrimitiveType(modelName string) bool {
	if len(modelName) == 0 {
		return false
	}
	return strings.Contains("uint uint8 uint16 uint32 uint64 int int8 int16 int32 int64 float32 float64 bool string byte rune time.Time time.Duration", modelName)
}

func jsonSchemaType(modelName string) string {
	schemaMap := map[string]string{
		"uint":   "integer",
		"uint8":  "integer",
		"uint16": "integer",
		"uint32": "integer",
		"uint64": "integer",

		"int":   "integer",
		"int8":  "integer",
		"int16": "integer",
		"int32": "integer",
		"int64": "integer",

		"byte":          "integer",
		"float64":       "number",
		"float32":       "number",
		"bool":          "boolean",
		"time.Time":     "string",
		"time.Duration": "integer",
	}
	mapped, ok := schemaMap[modelName]
	if !ok {
		return modelName // use as is (custom or struct)
	}
	return mapped
}
<|endoftext|>


<commit_before>package swugger

import (
	"github.com/emicklei/go-restful"
	"github.com/emicklei/go-restful/swagger"
	"github.com/julienschmidt/httprouter"
	"net/http"
	"strings"
)

type HttpRouterSwagger struct {
	HttpRouter *httprouter.Router
	GoRestfulContainer *restful.Container
	GoRestfulWebServices []*restful.WebService
	SwaggerConfig *swagger.Config
	isSwaggerServiceRegistered bool
}

//Not sure if this should have defaults or not, but it feels like there should be recommended swagger uri accross implementations.
func (hrs *HttpRouterSwagger) Init(webServiceAddr string) {
	hrs.GoRestfulContainer = restful.NewContainer()
	hrs.SwaggerConfig = &swagger.Config{
		WebServicesUrl: webServiceAddr,
		ApiPath:        "/doc/apidocs.json",
		// Optionally, specifiy where the UI is located
		SwaggerPath:     "/doc/apidocs/",
		SwaggerFilePath: "../swagger-ui/dist"}

}
func NewHRS(webServiceAddr string, httpRouter *httprouter.Router) *HttpRouterSwagger {
	hrs := &HttpRouterSwagger{}
	hrs.Init(webServiceAddr)
	hrs.HttpRouter = httpRouter
	return hrs
}

func (hrs *HttpRouterSwagger) AddService(path string, serviceDoc ServiceDoc) *restful.WebService {
	ws := new(restful.WebService)
	ws.Path(path).
		Doc(serviceDoc.Doc).
		Consumes(serviceDoc.Consumes...).
		Produces(serviceDoc.Produces...)
	hrs.GoRestfulContainer.Add(ws)
	//update
	hrs.SwaggerConfig.WebServices =  hrs.GoRestfulContainer.RegisteredWebServices()

	return ws
}

func (hrs *HttpRouterSwagger) GetSwaggerHandler() http.Handler {
	if !hrs.isSwaggerServiceRegistered {
		hrs.RegisterSwaggerService()
		hrs.isSwaggerServiceRegistered = true
	}
	return hrs.GoRestfulContainer
}

func (hrs *HttpRouterSwagger) RegisterSwaggerService() {
	swagger.RegisterSwaggerService(*hrs.SwaggerConfig, hrs.GoRestfulContainer)
}

func (hrs *HttpRouterSwagger) AddRoute(ws *restful.WebService, method string, path string, function httprouter.Handle, methodDoc MethodDoc) *restful.RouteBuilder {
	hrs.HttpRouter.Handle(method, path, function)
	pathGoRestful := path
	pathParts := strings.Split(path, ":")
	if len(pathParts) > 1 {
		pathGoRestful = pathParts[0]
		for i, part := range pathParts {
			if i > 0 {
				pathPartParts := strings.SplitN(part,"/", 2)
				pathGoRestful = pathGoRestful + "{" + pathPartParts[0] + "}"
				if len(pathPartParts) > 1 {
					pathGoRestful = pathGoRestful + "/" + pathPartParts[1]
				}
			}
		}
	}
	//This is the ugly part. As it stands, go-restful requires that you need to actually have a route in order for its documentation to work.
	rb := ws.Method(method).Path(pathGoRestful).To(func (req *restful.Request, resp *restful.Response) {
		// just reject it.
		resp.WriteErrorString(http.StatusNotFound, "Page not found")
		})
	if methodDoc.Operation != "" {
		rb.Operation(methodDoc.Operation)
	}
	if methodDoc.Doc != "" {
		rb.Doc(methodDoc.Doc)
	}
	if methodDoc.Writes != nil {
		rb.Writes(methodDoc.Writes)
	}
	if methodDoc.Reads != nil {
		rb.Reads(methodDoc.Reads)
	}
	if methodDoc.Params != nil {
		for _, p := range methodDoc.Params {
			switch p.Type {
			case "header":
				param := ws.HeaderParameter(p.Name, p.Doc).DataType(p.DataType).Required(true)
				rb.Param(param)
			default:
				param := ws.PathParameter(p.Name, p.Doc).DataType(p.DataType)
				rb.Param(param)
			}

		}
	}
	ws.Route(rb)
	return rb
}

<commit_msg>AddService now returns a local type WebService, which retains path information for later<commit_after>package swugger

import (
	"github.com/emicklei/go-restful"
	"github.com/emicklei/go-restful/swagger"
	"github.com/julienschmidt/httprouter"
	"net/http"
	"strings"
)

type HttpRouterSwagger struct {
	HttpRouter *httprouter.Router
	GoRestfulContainer *restful.Container
	//GoRestfulWebServices []*restful.WebService
	SwaggerConfig *swagger.Config
	isSwaggerServiceRegistered bool
}

//Not sure if this should have defaults or not, but it feels like there should be recommended swagger uri accross implementations.
func (hrs *HttpRouterSwagger) Init(webServiceAddr string) {
	hrs.GoRestfulContainer = restful.NewContainer()
	hrs.SwaggerConfig = &swagger.Config{
		WebServicesUrl: webServiceAddr,
		ApiPath:        "/doc/apidocs.json",
		// Optionally, specifiy where the UI is located
		SwaggerPath:     "/doc/apidocs/",
		SwaggerFilePath: "../swagger-ui/dist"}

}
func NewHRS(webServiceAddr string, httpRouter *httprouter.Router) *HttpRouterSwagger {
	hrs := &HttpRouterSwagger{}
	hrs.Init(webServiceAddr)
	hrs.HttpRouter = httpRouter
	return hrs
}

//wrapper for restful.WebService
type WebService struct {
	*restful.WebService
	Hrs *HttpRouterSwagger
	Path string
}

func (hrs *HttpRouterSwagger) AddService(path string, serviceDoc ServiceDoc) *WebService {
	ws := new(restful.WebService)
	mws := &WebService{ws, hrs, path}
	ws.Path(path).
		Doc(serviceDoc.Doc).
		Consumes(serviceDoc.Consumes...).
		Produces(serviceDoc.Produces...)
	hrs.GoRestfulContainer.Add(ws)
	//update
	hrs.SwaggerConfig.WebServices =  hrs.GoRestfulContainer.RegisteredWebServices()

	return mws
}

func (hrs *HttpRouterSwagger) GetSwaggerHandler() http.Handler {
	if !hrs.isSwaggerServiceRegistered {
		hrs.RegisterSwaggerService()
		hrs.isSwaggerServiceRegistered = true
	}
	return hrs.GoRestfulContainer
}

func (hrs *HttpRouterSwagger) RegisterSwaggerService() {
	swagger.RegisterSwaggerService(*hrs.SwaggerConfig, hrs.GoRestfulContainer)
}

func (hrs *HttpRouterSwagger) AddRoute(ws *WebService, method string, path string, function httprouter.Handle, methodDoc MethodDoc) *restful.RouteBuilder {
	return ws.AddRoute(method, path, function, methodDoc)
}

func (ws *WebService) AddRoute(method string, path string, function httprouter.Handle, methodDoc MethodDoc) *restful.RouteBuilder {
	fullPath := ws.Path + path
	ws.Hrs.HttpRouter.Handle(method, fullPath, function)
	pathGoRestful := path
	pathParts := strings.Split(path, ":")
	if len(pathParts) > 1 {
		pathGoRestful = pathParts[0]
		for i, part := range pathParts {
			if i > 0 {
				pathPartParts := strings.SplitN(part,"/", 2)
				pathGoRestful = pathGoRestful + "{" + pathPartParts[0] + "}"
				if len(pathPartParts) > 1 {
					pathGoRestful = pathGoRestful + "/" + pathPartParts[1]
				}
			}
		}
	}
	//This is the ugly part. As it stands, go-restful requires that you need to actually have a route in order for its documentation to work.
	rb := ws.Method(method).Path(pathGoRestful).To(func (req *restful.Request, resp *restful.Response) {
		// just reject it.
		resp.WriteErrorString(http.StatusNotFound, "Page not found")
		})
	if methodDoc.Operation != "" {
		rb.Operation(methodDoc.Operation)
	}
	if methodDoc.Doc != "" {
		rb.Doc(methodDoc.Doc)
	}
	if methodDoc.Writes != nil {
		rb.Writes(methodDoc.Writes)
	}
	if methodDoc.Reads != nil {
		rb.Reads(methodDoc.Reads)
	}
	if methodDoc.Params != nil {
		for _, p := range methodDoc.Params {
			switch p.Type {
			case "header":
				param := ws.HeaderParameter(p.Name, p.Doc).DataType(p.DataType).Required(true)
				rb.Param(param)
			case "query":
				param := ws.QueryParameter(p.Name, p.Doc).DataType(p.DataType)
				rb.Param(param)
			case "form":
				param := ws.FormParameter(p.Name, p.Doc).DataType(p.DataType)
				rb.Param(param)
			default:
				param := ws.PathParameter(p.Name, p.Doc).DataType(p.DataType)
				rb.Param(param)
			}
		}
	}
	ws.Route(rb)
	return rb
}

<|endoftext|>


<commit_before><commit_msg>remove fmt.Println<commit_after><|endoftext|>


<commit_before>//usr/bin/env go run $0 $@; exit
//
// Copyright 2015 The elastic.go authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//
// Author: Robin Hahling <robin.hahling@gw-computing.net>

// elastic.go is a command line tool to query the Elasticsearch REST API.
package main

import (
	"bufio"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
	"os"
	"strings"

	"github.com/codegangsta/cli"
	"github.com/gilliek/go-xterm256/xterm256"
	"github.com/hokaccha/go-prettyjson"
)

func main() {
	app := cli.NewApp()
	app.Name = "elastic"
	app.Usage = "A command line tool to query the Elasticsearch REST API"
	app.Version = "1.0.0"
	app.Author = "Robin Hahling"
	app.Email = "robin.hahling@gw-computing.net"
	app.Flags = []cli.Flag{
		cli.StringFlag{
			Name:  "baseurl",
			Value: "http://localhost:9200/",
			Usage: "Base API URL",
		},
	}
	app.Commands = []cli.Command{
		{
			Name:      "cluster",
			ShortName: "c",
			Usage:     "Get cluster information ",
			Subcommands: []cli.Command{
				{
					Name:      "health",
					ShortName: "he",
					Usage:     "Get cluster health",
					Action: func(c *cli.Context) {
						out, err := getJSON(cmdCluster(c, "health"))
						if err != nil {
							fatal(err)
						}
						fmt.Println(out)
					},
				},
				{
					Name:      "state",
					ShortName: "s",
					Usage:     "Get cluster state",
					Action: func(c *cli.Context) {
						out, err := getJSON(cmdCluster(c, "state"))
						if err != nil {
							fatal(err)
						}
						fmt.Println(out)
					},
				},
			},
		},
		{
			Name:      "index",
			ShortName: "i",
			Usage:     "Get index information",
			Subcommands: []cli.Command{
				{
					Name:      "docs-count",
					ShortName: "dc",
					Usage:     "Get index documents count",
					Action: func(c *cli.Context) {
						list, err := getRaw(cmdIndex(c, "list"))
						if err != nil {
							fatal(err)
						}
						for _, idx := range filteredDocsCountIndexes(list) {
							fmt.Println(idx)
						}
					},
				},
				{
					Name:      "list",
					ShortName: "l",
					Usage:     "List all indexes",
					Action: func(c *cli.Context) {
						list, err := getRaw(cmdIndex(c, "list"))
						if err != nil {
							fatal(err)
						}
						for _, idx := range filteredListIndexes(list) {
							fmt.Println(idx)
						}
					},
				},
				{
					Name:      "size",
					ShortName: "si",
					Usage:     "Get index size",
					Action: func(c *cli.Context) {
						list, err := getRaw(cmdIndex(c, "list"))
						if err != nil {
							fatal(err)
						}
						for _, idx := range filteredSizeIndexes(list) {
							fmt.Println(idx)
						}
					},
				},
				{
					Name:      "status",
					ShortName: "st",
					Usage:     "Get index status",
					Action: func(c *cli.Context) {
						list, err := getRaw(cmdIndex(c, "list"))
						if err != nil {
							fatal(err)
						}
						for _, idx := range filteredStatusIndexes(list) {
							fmt.Println(idx)
						}
					},
				},
				{
					Name:      "verbose",
					ShortName: "v",
					Usage:     "List indexes information with many stats",
					Action: func(c *cli.Context) {
						list, err := getRaw(cmdIndex(c, "list"))
						if err != nil {
							fatal(err)
						}
						fmt.Println(list)
					},
				},
			},
		},
		{
			Name:      "node",
			ShortName: "n",
			Usage:     "Get cluster nodes information",
			Subcommands: []cli.Command{
				{
					Name:      "list",
					ShortName: "l",
					Usage:     "List nodes information",
					Action: func(c *cli.Context) {
						out, err := getJSON(cmdNode(c, "list"))
						if err != nil {
							fatal(err)
						}
						fmt.Println(out)
					},
				},
			},
		},
		{
			Name:      "query",
			ShortName: "q",
			Usage:     "Perform any ES API GET query",
			Action: func(c *cli.Context) {
				var out string
				var err error
				if strings.Contains(c.Args().First(), "_cat/") {
					out, err = getRaw(cmdQuery(c))
				} else {
					out, err = getJSON(cmdQuery(c))
				}
				if err != nil {
					fatal(err)
				}
				fmt.Println(out)
			},
		},
		{
			Name:      "stats",
			ShortName: "s",
			Usage:     "Get statistics",
			Subcommands: []cli.Command{
				{
					Name:      "size",
					ShortName: "s",
					Usage:     "Get index sizes",
					Action: func(c *cli.Context) {
						out, err := getJSON(cmdStats(c, "size"))
						if err != nil {
							fatal(err)
						}
						fmt.Println(out)
					},
				},
			},
		},
	}

	app.Run(os.Args)
}

func fatal(err error) {
	fmt.Fprintln(os.Stderr, err)
	os.Exit(1)
}

func getJSON(route string) (string, error) {
	r, err := http.Get(route)
	if err != nil {
		return "", err
	}
	defer r.Body.Close()

	var b interface{}
	if err := json.NewDecoder(r.Body).Decode(&b); err != nil {
		return "", err
	}
	out, err := prettyjson.Marshal(b)
	return string(out), err
}

func getRaw(route string) (string, error) {
	r, err := http.Get(route)
	if err != nil {
		return "", err
	}
	defer r.Body.Close()

	out, err := ioutil.ReadAll(r.Body)
	return string(out), err
}

// processing functions
func filteredDocsCountIndexes(list string) []string {
	var out []string
	scanner := bufio.NewScanner(strings.NewReader(list))
	for scanner.Scan() {
		elmts := strings.Fields(scanner.Text())
		if len(elmts) < 6 {
			continue
		}
		out = append(out, fmt.Sprintf("%10s %s", colorizeStatus(elmts[5]), elmts[2]))
	}
	return out
}

func filteredListIndexes(list string) []string {
	var out []string
	scanner := bufio.NewScanner(strings.NewReader(list))
	for scanner.Scan() {
		elmts := strings.Fields(scanner.Text())
		if len(elmts) < 3 {
			continue
		}
		out = append(out, elmts[2])
	}
	return out
}

func filteredStatusIndexes(list string) []string {
	var out []string
	scanner := bufio.NewScanner(strings.NewReader(list))
	for scanner.Scan() {
		elmts := strings.Fields(scanner.Text())
		if len(elmts) < 3 {
			continue
		}
		out = append(out, fmt.Sprintf("%22s %s", colorizeStatus(elmts[0]), elmts[2]))
	}
	return out
}

func filteredSizeIndexes(list string) []string {
	var out []string
	scanner := bufio.NewScanner(strings.NewReader(list))
	for scanner.Scan() {
		elmts := strings.Fields(scanner.Text())
		if len(elmts) < 8 {
			continue
		}
		out = append(out, fmt.Sprintf("%10s %s", elmts[7], elmts[2]))
	}
	return out
}

func colorizeStatus(status string) string {
	var color xterm256.Color
	switch status {
	case "red":
		color = xterm256.Red
	case "green":
		color = xterm256.Green
	case "yellow":
		color = xterm256.Yellow
	default:
		return status
	}
	return xterm256.Sprint(color, status)
}

// command-line commands from now on
func cmdCluster(c *cli.Context, subCmd string) string {
	route := "_cluster/"
	url := c.GlobalString("baseurl")

	var arg string
	switch subCmd {
	case "health":
		arg = "/health"
	case "state":
		arg = "/state"
	default:
		arg = ""
	}
	return url + route + arg
}

func cmdIndex(c *cli.Context, subCmd string) string {
	var route string
	url := c.GlobalString("baseurl")
	switch subCmd {
	case "list":
		route = "_cat/indices?v"
	default:
		route = ""
	}
	return url + route
}

func cmdNode(c *cli.Context, subCmd string) string {
	var route string
	url := c.GlobalString("baseurl")
	switch subCmd {
	case "list":
		route = "_nodes/_all/host,ip"
	default:
		route = ""
	}
	return url + route
}

func cmdQuery(c *cli.Context) string {
	route := c.Args().First()
	url := c.GlobalString("baseurl")
	return url + route
}

func cmdStats(c *cli.Context, subCmd string) string {
	var route string
	url := c.GlobalString("baseurl")
	switch subCmd {
	case "size":
		route = "_stats/index,store"
	default:
		route = ""
	}
	return url + route
}
<commit_msg>improve error checking when fetching data (either raw or JSON)<commit_after>//usr/bin/env go run $0 $@; exit
//
// Copyright 2015 The elastic.go authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//
// Author: Robin Hahling <robin.hahling@gw-computing.net>

// elastic.go is a command line tool to query the Elasticsearch REST API.
package main

import (
	"bufio"
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"mime"
	"net/http"
	"os"
	"strings"

	"github.com/codegangsta/cli"
	"github.com/gilliek/go-xterm256/xterm256"
	"github.com/hokaccha/go-prettyjson"
)

func main() {
	app := cli.NewApp()
	app.Name = "elastic"
	app.Usage = "A command line tool to query the Elasticsearch REST API"
	app.Version = "1.0.0"
	app.Author = "Robin Hahling"
	app.Email = "robin.hahling@gw-computing.net"
	app.Flags = []cli.Flag{
		cli.StringFlag{
			Name:  "baseurl",
			Value: "http://localhost:9200/",
			Usage: "Base API URL",
		},
	}
	app.Commands = []cli.Command{
		{
			Name:      "cluster",
			ShortName: "c",
			Usage:     "Get cluster information ",
			Subcommands: []cli.Command{
				{
					Name:      "health",
					ShortName: "he",
					Usage:     "Get cluster health",
					Action: func(c *cli.Context) {
						out, err := getJSON(cmdCluster(c, "health"))
						if err != nil {
							fatal(err)
						}
						fmt.Println(out)
					},
				},
				{
					Name:      "state",
					ShortName: "s",
					Usage:     "Get cluster state",
					Action: func(c *cli.Context) {
						out, err := getJSON(cmdCluster(c, "state"))
						if err != nil {
							fatal(err)
						}
						fmt.Println(out)
					},
				},
			},
		},
		{
			Name:      "index",
			ShortName: "i",
			Usage:     "Get index information",
			Subcommands: []cli.Command{
				{
					Name:      "docs-count",
					ShortName: "dc",
					Usage:     "Get index documents count",
					Action: func(c *cli.Context) {
						list, err := getRaw(cmdIndex(c, "list"))
						if err != nil {
							fatal(err)
						}
						for _, idx := range filteredDocsCountIndexes(list) {
							fmt.Println(idx)
						}
					},
				},
				{
					Name:      "list",
					ShortName: "l",
					Usage:     "List all indexes",
					Action: func(c *cli.Context) {
						list, err := getRaw(cmdIndex(c, "list"))
						if err != nil {
							fatal(err)
						}
						for _, idx := range filteredListIndexes(list) {
							fmt.Println(idx)
						}
					},
				},
				{
					Name:      "size",
					ShortName: "si",
					Usage:     "Get index size",
					Action: func(c *cli.Context) {
						list, err := getRaw(cmdIndex(c, "list"))
						if err != nil {
							fatal(err)
						}
						for _, idx := range filteredSizeIndexes(list) {
							fmt.Println(idx)
						}
					},
				},
				{
					Name:      "status",
					ShortName: "st",
					Usage:     "Get index status",
					Action: func(c *cli.Context) {
						list, err := getRaw(cmdIndex(c, "list"))
						if err != nil {
							fatal(err)
						}
						for _, idx := range filteredStatusIndexes(list) {
							fmt.Println(idx)
						}
					},
				},
				{
					Name:      "verbose",
					ShortName: "v",
					Usage:     "List indexes information with many stats",
					Action: func(c *cli.Context) {
						list, err := getRaw(cmdIndex(c, "list"))
						if err != nil {
							fatal(err)
						}
						fmt.Println(list)
					},
				},
			},
		},
		{
			Name:      "node",
			ShortName: "n",
			Usage:     "Get cluster nodes information",
			Subcommands: []cli.Command{
				{
					Name:      "list",
					ShortName: "l",
					Usage:     "List nodes information",
					Action: func(c *cli.Context) {
						out, err := getJSON(cmdNode(c, "list"))
						if err != nil {
							fatal(err)
						}
						fmt.Println(out)
					},
				},
			},
		},
		{
			Name:      "query",
			ShortName: "q",
			Usage:     "Perform any ES API GET query",
			Action: func(c *cli.Context) {
				var out string
				var err error
				if strings.Contains(c.Args().First(), "_cat/") {
					out, err = getRaw(cmdQuery(c))
				} else {
					out, err = getJSON(cmdQuery(c))
				}
				if err != nil {
					fatal(err)
				}
				fmt.Println(out)
			},
		},
		{
			Name:      "stats",
			ShortName: "s",
			Usage:     "Get statistics",
			Subcommands: []cli.Command{
				{
					Name:      "size",
					ShortName: "s",
					Usage:     "Get index sizes",
					Action: func(c *cli.Context) {
						out, err := getJSON(cmdStats(c, "size"))
						if err != nil {
							fatal(err)
						}
						fmt.Println(out)
					},
				},
			},
		},
	}

	app.Run(os.Args)
}

func fatal(err error) {
	fmt.Fprintln(os.Stderr, err)
	os.Exit(1)
}

func getJSON(route string) (string, error) {
	r, err := http.Get(route)
	if err != nil {
		return "", err
	}
	defer r.Body.Close()

	if r.StatusCode != 200 {
		return "", fmt.Errorf("unexpected status code: %s", r.Status)
	}

	mediatype, _, err := mime.ParseMediaType(r.Header.Get("Content-Type"))
	if err != nil {
		return "", err
	}
	if mediatype == "" {
		return "", errors.New("mediatype not set")
	}
	if mediatype != "application/json" {
		return "", fmt.Errorf("mediatype is '%s', 'application/json' expected", mediatype)
	}

	var b interface{}
	if err := json.NewDecoder(r.Body).Decode(&b); err != nil {
		return "", err
	}
	out, err := prettyjson.Marshal(b)
	return string(out), err
}

func getRaw(route string) (string, error) {
	r, err := http.Get(route)
	if err != nil {
		return "", err
	}
	defer r.Body.Close()

	if r.StatusCode != 200 {
		return "", fmt.Errorf("unexpected status code: %s", r.Status)
	}

	out, err := ioutil.ReadAll(r.Body)
	return string(out), err
}

// processing functions
func filteredDocsCountIndexes(list string) []string {
	var out []string
	scanner := bufio.NewScanner(strings.NewReader(list))
	for scanner.Scan() {
		elmts := strings.Fields(scanner.Text())
		if len(elmts) < 6 {
			continue
		}
		out = append(out, fmt.Sprintf("%10s %s", colorizeStatus(elmts[5]), elmts[2]))
	}
	return out
}

func filteredListIndexes(list string) []string {
	var out []string
	scanner := bufio.NewScanner(strings.NewReader(list))
	for scanner.Scan() {
		elmts := strings.Fields(scanner.Text())
		if len(elmts) < 3 {
			continue
		}
		out = append(out, elmts[2])
	}
	return out
}

func filteredStatusIndexes(list string) []string {
	var out []string
	scanner := bufio.NewScanner(strings.NewReader(list))
	for scanner.Scan() {
		elmts := strings.Fields(scanner.Text())
		if len(elmts) < 3 {
			continue
		}
		out = append(out, fmt.Sprintf("%22s %s", colorizeStatus(elmts[0]), elmts[2]))
	}
	return out
}

func filteredSizeIndexes(list string) []string {
	var out []string
	scanner := bufio.NewScanner(strings.NewReader(list))
	for scanner.Scan() {
		elmts := strings.Fields(scanner.Text())
		if len(elmts) < 8 {
			continue
		}
		out = append(out, fmt.Sprintf("%10s %s", elmts[7], elmts[2]))
	}
	return out
}

func colorizeStatus(status string) string {
	var color xterm256.Color
	switch status {
	case "red":
		color = xterm256.Red
	case "green":
		color = xterm256.Green
	case "yellow":
		color = xterm256.Yellow
	default:
		return status
	}
	return xterm256.Sprint(color, status)
}

// command-line commands from now on
func cmdCluster(c *cli.Context, subCmd string) string {
	route := "_cluster/"
	url := c.GlobalString("baseurl")

	var arg string
	switch subCmd {
	case "health":
		arg = "/health"
	case "state":
		arg = "/state"
	default:
		arg = ""
	}
	return url + route + arg
}

func cmdIndex(c *cli.Context, subCmd string) string {
	var route string
	url := c.GlobalString("baseurl")
	switch subCmd {
	case "list":
		route = "_cat/indices?v"
	default:
		route = ""
	}
	return url + route
}

func cmdNode(c *cli.Context, subCmd string) string {
	var route string
	url := c.GlobalString("baseurl")
	switch subCmd {
	case "list":
		route = "_nodes/_all/host,ip"
	default:
		route = ""
	}
	return url + route
}

func cmdQuery(c *cli.Context) string {
	route := c.Args().First()
	url := c.GlobalString("baseurl")
	return url + route
}

func cmdStats(c *cli.Context, subCmd string) string {
	var route string
	url := c.GlobalString("baseurl")
	switch subCmd {
	case "size":
		route = "_stats/index,store"
	default:
		route = ""
	}
	return url + route
}
<|endoftext|>


<commit_before><commit_msg>simplify the emr interface some more<commit_after><|endoftext|>


<commit_before>package main

import (
	"os"

	"github.com/codegangsta/cli"
	"github.com/coreos/etcdctl/command"
)

func main() {
	app := cli.NewApp()
	app.Name = "etcdctl"
	app.Version = releaseVersion
	app.Usage = "A simple command line client for etcd."
	app.Flags = []cli.Flag{
		cli.BoolFlag{"debug", "output cURL commands which can be used to reproduce the request"},
		cli.StringFlag{"output, o", "simple", "output response in the given format (`simple` or `json`)"},
		cli.StringSliceFlag{"C", &cli.StringSlice{"127.0.0.1:4001"}, "a comma seperated list of machine addresses in the cluster"},
	}
	app.Commands = []cli.Command{
		command.NewCreateCommand(),
		command.NewCreateDirCommand(),
		command.NewDeleteCommand(),
		command.NewGetCommand(),
		command.NewLsCommand(),
		command.NewSetCommand(),
		command.NewSetDirCommand(),
		command.NewUpdateCommand(),
		command.NewUpdateDirCommand(),
		command.NewWatchCommand(),
		command.NewExecWatchCommand(),
	}
	app.Run(os.Args)
}
<commit_msg>feat(etcdctl): accept -peers like etcd<commit_after>package main

import (
	"os"

	"github.com/codegangsta/cli"
	"github.com/coreos/etcdctl/command"
)

func main() {
	app := cli.NewApp()
	app.Name = "etcdctl"
	app.Version = releaseVersion
	app.Usage = "A simple command line client for etcd."
	app.Flags = []cli.Flag{
		cli.BoolFlag{"debug", "output cURL commands which can be used to reproduce the request"},
		cli.StringFlag{"output, o", "simple", "output response in the given format (`simple` or `json`)"},
		cli.StringSliceFlag{"peers, C", &cli.StringSlice{"127.0.0.1:4001"}, "a comma seperated list of machine addresses in the cluster"},
	}
	app.Commands = []cli.Command{
		command.NewCreateCommand(),
		command.NewCreateDirCommand(),
		command.NewDeleteCommand(),
		command.NewGetCommand(),
		command.NewLsCommand(),
		command.NewSetCommand(),
		command.NewSetDirCommand(),
		command.NewUpdateCommand(),
		command.NewUpdateDirCommand(),
		command.NewWatchCommand(),
		command.NewExecWatchCommand(),
	}
	app.Run(os.Args)
}
<|endoftext|>


<commit_before>/*
Copyright The Kubernetes Authors.

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

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

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

// Code generated by main. DO NOT EDIT.

package k3s

import (
	"context"
	"time"

	clientset "github.com/rancher/k3s/pkg/generated/clientset/versioned"
	informers "github.com/rancher/k3s/pkg/generated/informers/externalversions"
	"github.com/rancher/wrangler/pkg/generic"
	"k8s.io/apimachinery/pkg/runtime/schema"
	"k8s.io/client-go/rest"
)

type Factory struct {
	synced            bool
	informerFactory   informers.SharedInformerFactory
	clientset         clientset.Interface
	controllerManager *generic.ControllerManager
	threadiness       map[schema.GroupVersionKind]int
}

func NewFactoryFromConfigOrDie(config *rest.Config) *Factory {
	f, err := NewFactoryFromConfig(config)
	if err != nil {
		panic(err)
	}
	return f
}

func NewFactoryFromConfig(config *rest.Config) (*Factory, error) {
	cs, err := clientset.NewForConfig(config)
	if err != nil {
		return nil, err
	}

	informerFactory := informers.NewSharedInformerFactory(cs, 2*time.Hour)
	return NewFactory(cs, informerFactory), nil
}

func NewFactoryFromConfigWithNamespace(config *rest.Config, namespace string) (*Factory, error) {
	if namespace == "" {
		return NewFactoryFromConfig(config)
	}

	cs, err := clientset.NewForConfig(config)
	if err != nil {
		return nil, err
	}

	informerFactory := informers.NewSharedInformerFactoryWithOptions(cs, 2*time.Hour, informers.WithNamespace(namespace))
	return NewFactory(cs, informerFactory), nil
}

func NewFactory(clientset clientset.Interface, informerFactory informers.SharedInformerFactory) *Factory {
	return &Factory{
		threadiness:       map[schema.GroupVersionKind]int{},
		controllerManager: &generic.ControllerManager{},
		clientset:         clientset,
		informerFactory:   informerFactory,
	}
}

func (c *Factory) SetThreadiness(gvk schema.GroupVersionKind, threadiness int) {
	c.threadiness[gvk] = threadiness
}

func (c *Factory) Sync(ctx context.Context) error {
	c.informerFactory.Start(ctx.Done())
	c.informerFactory.WaitForCacheSync(ctx.Done())
	return nil
}

func (c *Factory) Start(ctx context.Context, defaultThreadiness int) error {
	if err := c.Sync(ctx); err != nil {
		return err
	}

	return c.controllerManager.Start(ctx, defaultThreadiness, c.threadiness)
}

func (c *Factory) K3s() Interface {
	return New(c.controllerManager, c.informerFactory.K3s(), c.clientset)
}
<commit_msg>Generated changes<commit_after>/*
Copyright The Kubernetes Authors.

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

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

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

// Code generated by main. DO NOT EDIT.

package k3s

import (
	"context"
	"time"

	clientset "github.com/rancher/k3s/pkg/generated/clientset/versioned"
	scheme "github.com/rancher/k3s/pkg/generated/clientset/versioned/scheme"
	informers "github.com/rancher/k3s/pkg/generated/informers/externalversions"
	"github.com/rancher/wrangler/pkg/generic"
	"github.com/rancher/wrangler/pkg/schemes"
	"k8s.io/apimachinery/pkg/runtime/schema"
	"k8s.io/client-go/rest"
)

func init() {
	scheme.AddToScheme(schemes.All)
}

type Factory struct {
	synced            bool
	informerFactory   informers.SharedInformerFactory
	clientset         clientset.Interface
	controllerManager *generic.ControllerManager
	threadiness       map[schema.GroupVersionKind]int
}

func NewFactoryFromConfigOrDie(config *rest.Config) *Factory {
	f, err := NewFactoryFromConfig(config)
	if err != nil {
		panic(err)
	}
	return f
}

func NewFactoryFromConfig(config *rest.Config) (*Factory, error) {
	cs, err := clientset.NewForConfig(config)
	if err != nil {
		return nil, err
	}

	informerFactory := informers.NewSharedInformerFactory(cs, 2*time.Hour)
	return NewFactory(cs, informerFactory), nil
}

func NewFactoryFromConfigWithNamespace(config *rest.Config, namespace string) (*Factory, error) {
	if namespace == "" {
		return NewFactoryFromConfig(config)
	}

	cs, err := clientset.NewForConfig(config)
	if err != nil {
		return nil, err
	}

	informerFactory := informers.NewSharedInformerFactoryWithOptions(cs, 2*time.Hour, informers.WithNamespace(namespace))
	return NewFactory(cs, informerFactory), nil
}

func NewFactory(clientset clientset.Interface, informerFactory informers.SharedInformerFactory) *Factory {
	return &Factory{
		threadiness:       map[schema.GroupVersionKind]int{},
		controllerManager: &generic.ControllerManager{},
		clientset:         clientset,
		informerFactory:   informerFactory,
	}
}

func (c *Factory) SetThreadiness(gvk schema.GroupVersionKind, threadiness int) {
	c.threadiness[gvk] = threadiness
}

func (c *Factory) Sync(ctx context.Context) error {
	c.informerFactory.Start(ctx.Done())
	c.informerFactory.WaitForCacheSync(ctx.Done())
	return nil
}

func (c *Factory) Start(ctx context.Context, defaultThreadiness int) error {
	if err := c.Sync(ctx); err != nil {
		return err
	}

	return c.controllerManager.Start(ctx, defaultThreadiness, c.threadiness)
}

func (c *Factory) K3s() Interface {
	return New(c.controllerManager, c.informerFactory.K3s(), c.clientset)
}
<|endoftext|>


<commit_before>/*
Copyright 2021 The Kubernetes Authors All rights reserved.

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

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

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

package constants

var (
	KubeadmImages = map[string]map[string]string{
		"v1.25": {
			"coredns/coredns": "v1.9.3",
			"etcd":            "3.5.4-0",
			"pause":           "3.7",
		},
		"v1.24": {
			"coredns/coredns": "v1.8.6",
			"etcd":            "3.5.3-0",
			"pause":           "3.7",
		},
		"v1.23": {
			"coredns/coredns": "v1.8.6",
			"etcd":            "3.5.1-0",
			"pause":           "3.6",
		},
		"v1.22": {
			"coredns/coredns": "v1.8.4",
			"etcd":            "3.5.0-0",
			"pause":           "3.5",
		},
		"v1.21": {
			"coredns/coredns": "v1.8.0",
			"etcd":            "3.4.13-0",
			"pause":           "3.4.1",
		},
		"v1.20": {
			"coredns": "1.7.0",
			"etcd":    "3.4.13-0",
			"pause":   "3.2",
		},
		"v1.19": {
			"coredns": "1.7.0",
			"etcd":    "3.4.9-1",
			"pause":   "3.2",
		},
		"v1.18": {
			"coredns": "1.6.7",
			"etcd":    "3.4.3-0",
			"pause":   "3.2",
		},
		"v1.17": {
			"coredns": "1.6.5",
			"etcd":    "3.4.3-0",
			"pause":   "3.1",
		},
		"v1.16": {
			"coredns": "1.6.2",
			"etcd":    "3.3.15-0",
			"pause":   "3.1",
		},
		"v1.15": {
			"coredns": "1.3.1",
			"etcd":    "3.3.10",
			"pause":   "3.1",
		},
		"v1.14": {
			"coredns": "1.3.1",
			"etcd":    "3.3.10",
			"pause":   "3.1",
		},
		"v1.13": {
			"coredns": "1.2.6",
			"etcd":    "3.2.24",
			"pause":   "3.1",
		},
		"v1.12": {
			"coredns": "1.2.2",
			"etcd":    "3.2.24",
			"pause":   "3.1",
		},
		"v1.11": {
			"coredns":     "1.1.3",
			"etcd-amd64":  "3.2.18",
			"pause-amd64": "3.1",
		},
	}
)
<commit_msg>update image constants for kubeadm images<commit_after>/*
Copyright 2021 The Kubernetes Authors All rights reserved.

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

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

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

package constants

var (
	KubeadmImages = map[string]map[string]string{
		"v1.25": {
			"coredns/coredns": "v1.9.3",
			"etcd":            "3.5.4-0",
			"pause":           "3.8",
		},
		"v1.24": {
			"coredns/coredns": "v1.8.6",
			"etcd":            "3.5.3-0",
			"pause":           "3.7",
		},
		"v1.23": {
			"coredns/coredns": "v1.8.6",
			"etcd":            "3.5.1-0",
			"pause":           "3.6",
		},
		"v1.22": {
			"coredns/coredns": "v1.8.4",
			"etcd":            "3.5.0-0",
			"pause":           "3.5",
		},
		"v1.21": {
			"coredns/coredns": "v1.8.0",
			"etcd":            "3.4.13-0",
			"pause":           "3.4.1",
		},
		"v1.20": {
			"coredns": "1.7.0",
			"etcd":    "3.4.13-0",
			"pause":   "3.2",
		},
		"v1.19": {
			"coredns": "1.7.0",
			"etcd":    "3.4.9-1",
			"pause":   "3.2",
		},
		"v1.18": {
			"coredns": "1.6.7",
			"etcd":    "3.4.3-0",
			"pause":   "3.2",
		},
		"v1.17": {
			"coredns": "1.6.5",
			"etcd":    "3.4.3-0",
			"pause":   "3.1",
		},
		"v1.16": {
			"coredns": "1.6.2",
			"etcd":    "3.3.15-0",
			"pause":   "3.1",
		},
		"v1.15": {
			"coredns": "1.3.1",
			"etcd":    "3.3.10",
			"pause":   "3.1",
		},
		"v1.14": {
			"coredns": "1.3.1",
			"etcd":    "3.3.10",
			"pause":   "3.1",
		},
		"v1.13": {
			"coredns": "1.2.6",
			"etcd":    "3.2.24",
			"pause":   "3.1",
		},
		"v1.12": {
			"coredns": "1.2.2",
			"etcd":    "3.2.24",
			"pause":   "3.1",
		},
		"v1.11": {
			"coredns":     "1.1.3",
			"etcd-amd64":  "3.2.18",
			"pause-amd64": "3.1",
		},
	}
)
<|endoftext|>


<commit_before>/*
Copyright 2016 The Kubernetes Authors.

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

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

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

package rest

import (
	"fmt"
	"time"

	"github.com/golang/glog"

	utilruntime "k8s.io/apimachinery/pkg/util/runtime"
	"k8s.io/apimachinery/pkg/util/wait"
	"k8s.io/apiserver/pkg/registry/generic"
	"k8s.io/apiserver/pkg/registry/rest"
	genericapiserver "k8s.io/apiserver/pkg/server"
	serverstorage "k8s.io/apiserver/pkg/server/storage"
	"k8s.io/kubernetes/pkg/api"
	"k8s.io/kubernetes/pkg/apis/extensions"
	extensionsapiv1beta1 "k8s.io/kubernetes/pkg/apis/extensions/v1beta1"
	extensionsclient "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/typed/extensions/internalversion"
	expcontrollerstore "k8s.io/kubernetes/pkg/registry/extensions/controller/storage"
	daemonstore "k8s.io/kubernetes/pkg/registry/extensions/daemonset/storage"
	deploymentstore "k8s.io/kubernetes/pkg/registry/extensions/deployment/storage"
	ingressstore "k8s.io/kubernetes/pkg/registry/extensions/ingress/storage"
	networkpolicystore "k8s.io/kubernetes/pkg/registry/extensions/networkpolicy/storage"
	pspstore "k8s.io/kubernetes/pkg/registry/extensions/podsecuritypolicy/storage"
	replicasetstore "k8s.io/kubernetes/pkg/registry/extensions/replicaset/storage"
	thirdpartyresourcestore "k8s.io/kubernetes/pkg/registry/extensions/thirdpartyresource/storage"
)

type RESTStorageProvider struct {
	ResourceInterface ResourceInterface
}

func (p RESTStorageProvider) NewRESTStorage(apiResourceConfigSource serverstorage.APIResourceConfigSource, restOptionsGetter generic.RESTOptionsGetter) (genericapiserver.APIGroupInfo, bool) {
	apiGroupInfo := genericapiserver.NewDefaultAPIGroupInfo(extensions.GroupName, api.Registry, api.Scheme, api.ParameterCodec, api.Codecs)

	if apiResourceConfigSource.AnyResourcesForVersionEnabled(extensionsapiv1beta1.SchemeGroupVersion) {
		apiGroupInfo.VersionedResourcesStorageMap[extensionsapiv1beta1.SchemeGroupVersion.Version] = p.v1beta1Storage(apiResourceConfigSource, restOptionsGetter)
		apiGroupInfo.GroupMeta.GroupVersion = extensionsapiv1beta1.SchemeGroupVersion
	}

	return apiGroupInfo, true
}

func (p RESTStorageProvider) v1beta1Storage(apiResourceConfigSource serverstorage.APIResourceConfigSource, restOptionsGetter generic.RESTOptionsGetter) map[string]rest.Storage {
	version := extensionsapiv1beta1.SchemeGroupVersion

	storage := map[string]rest.Storage{}

	// This is a dummy replication controller for scale subresource purposes.
	// TODO: figure out how to enable this only if needed as a part of scale subresource GA.
	controllerStorage := expcontrollerstore.NewStorage(restOptionsGetter)
	storage["replicationcontrollers"] = controllerStorage.ReplicationController
	storage["replicationcontrollers/scale"] = controllerStorage.Scale

	if apiResourceConfigSource.ResourceEnabled(version.WithResource("thirdpartyresources")) {
		thirdPartyResourceStorage := thirdpartyresourcestore.NewREST(restOptionsGetter)
		storage["thirdpartyresources"] = thirdPartyResourceStorage
	}

	if apiResourceConfigSource.ResourceEnabled(version.WithResource("daemonsets")) {
		daemonSetStorage, daemonSetStatusStorage := daemonstore.NewREST(restOptionsGetter)
		storage["daemonsets"] = daemonSetStorage
		storage["daemonsets/status"] = daemonSetStatusStorage
	}
	if apiResourceConfigSource.ResourceEnabled(version.WithResource("deployments")) {
		deploymentStorage := deploymentstore.NewStorage(restOptionsGetter)
		storage["deployments"] = deploymentStorage.Deployment
		storage["deployments/status"] = deploymentStorage.Status
		storage["deployments/rollback"] = deploymentStorage.Rollback
		storage["deployments/scale"] = deploymentStorage.Scale
	}
	if apiResourceConfigSource.ResourceEnabled(version.WithResource("ingresses")) {
		ingressStorage, ingressStatusStorage := ingressstore.NewREST(restOptionsGetter)
		storage["ingresses"] = ingressStorage
		storage["ingresses/status"] = ingressStatusStorage
	}
	if apiResourceConfigSource.ResourceEnabled(version.WithResource("podsecuritypolicy")) || apiResourceConfigSource.ResourceEnabled(version.WithResource("podsecuritypolicies")) {
		podSecurityExtensionsStorage := pspstore.NewREST(restOptionsGetter)
		storage["podSecurityPolicies"] = podSecurityExtensionsStorage
	}
	if apiResourceConfigSource.ResourceEnabled(version.WithResource("replicasets")) {
		replicaSetStorage := replicasetstore.NewStorage(restOptionsGetter)
		storage["replicasets"] = replicaSetStorage.ReplicaSet
		storage["replicasets/status"] = replicaSetStorage.Status
		storage["replicasets/scale"] = replicaSetStorage.Scale
	}
	if apiResourceConfigSource.ResourceEnabled(version.WithResource("networkpolicies")) {
		networkExtensionsStorage := networkpolicystore.NewREST(restOptionsGetter)
		storage["networkpolicies"] = networkExtensionsStorage
	}

	return storage
}

func (p RESTStorageProvider) PostStartHook() (string, genericapiserver.PostStartHookFunc, error) {
	return "extensions/third-party-resources", p.postStartHookFunc, nil
}
func (p RESTStorageProvider) postStartHookFunc(hookContext genericapiserver.PostStartHookContext) error {
	clientset, err := extensionsclient.NewForConfig(hookContext.LoopbackClientConfig)
	if err != nil {
		utilruntime.HandleError(fmt.Errorf("unable to initialize clusterroles: %v", err))
		return nil
	}

	thirdPartyControl := ThirdPartyController{
		master: p.ResourceInterface,
		client: clientset,
	}
	go wait.Forever(func() {
		if err := thirdPartyControl.SyncResources(); err != nil {
			glog.Warningf("third party resource sync failed: %v", err)
		}
	}, 10*time.Second)

	return nil
}

func (p RESTStorageProvider) GroupName() string {
	return extensions.GroupName
}
<commit_msg>fix err message in storage extensions<commit_after>/*
Copyright 2016 The Kubernetes Authors.

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

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

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

package rest

import (
	"fmt"
	"time"

	"github.com/golang/glog"

	utilruntime "k8s.io/apimachinery/pkg/util/runtime"
	"k8s.io/apimachinery/pkg/util/wait"
	"k8s.io/apiserver/pkg/registry/generic"
	"k8s.io/apiserver/pkg/registry/rest"
	genericapiserver "k8s.io/apiserver/pkg/server"
	serverstorage "k8s.io/apiserver/pkg/server/storage"
	"k8s.io/kubernetes/pkg/api"
	"k8s.io/kubernetes/pkg/apis/extensions"
	extensionsapiv1beta1 "k8s.io/kubernetes/pkg/apis/extensions/v1beta1"
	extensionsclient "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/typed/extensions/internalversion"
	expcontrollerstore "k8s.io/kubernetes/pkg/registry/extensions/controller/storage"
	daemonstore "k8s.io/kubernetes/pkg/registry/extensions/daemonset/storage"
	deploymentstore "k8s.io/kubernetes/pkg/registry/extensions/deployment/storage"
	ingressstore "k8s.io/kubernetes/pkg/registry/extensions/ingress/storage"
	networkpolicystore "k8s.io/kubernetes/pkg/registry/extensions/networkpolicy/storage"
	pspstore "k8s.io/kubernetes/pkg/registry/extensions/podsecuritypolicy/storage"
	replicasetstore "k8s.io/kubernetes/pkg/registry/extensions/replicaset/storage"
	thirdpartyresourcestore "k8s.io/kubernetes/pkg/registry/extensions/thirdpartyresource/storage"
)

type RESTStorageProvider struct {
	ResourceInterface ResourceInterface
}

func (p RESTStorageProvider) NewRESTStorage(apiResourceConfigSource serverstorage.APIResourceConfigSource, restOptionsGetter generic.RESTOptionsGetter) (genericapiserver.APIGroupInfo, bool) {
	apiGroupInfo := genericapiserver.NewDefaultAPIGroupInfo(extensions.GroupName, api.Registry, api.Scheme, api.ParameterCodec, api.Codecs)

	if apiResourceConfigSource.AnyResourcesForVersionEnabled(extensionsapiv1beta1.SchemeGroupVersion) {
		apiGroupInfo.VersionedResourcesStorageMap[extensionsapiv1beta1.SchemeGroupVersion.Version] = p.v1beta1Storage(apiResourceConfigSource, restOptionsGetter)
		apiGroupInfo.GroupMeta.GroupVersion = extensionsapiv1beta1.SchemeGroupVersion
	}

	return apiGroupInfo, true
}

func (p RESTStorageProvider) v1beta1Storage(apiResourceConfigSource serverstorage.APIResourceConfigSource, restOptionsGetter generic.RESTOptionsGetter) map[string]rest.Storage {
	version := extensionsapiv1beta1.SchemeGroupVersion

	storage := map[string]rest.Storage{}

	// This is a dummy replication controller for scale subresource purposes.
	// TODO: figure out how to enable this only if needed as a part of scale subresource GA.
	controllerStorage := expcontrollerstore.NewStorage(restOptionsGetter)
	storage["replicationcontrollers"] = controllerStorage.ReplicationController
	storage["replicationcontrollers/scale"] = controllerStorage.Scale

	if apiResourceConfigSource.ResourceEnabled(version.WithResource("thirdpartyresources")) {
		thirdPartyResourceStorage := thirdpartyresourcestore.NewREST(restOptionsGetter)
		storage["thirdpartyresources"] = thirdPartyResourceStorage
	}

	if apiResourceConfigSource.ResourceEnabled(version.WithResource("daemonsets")) {
		daemonSetStorage, daemonSetStatusStorage := daemonstore.NewREST(restOptionsGetter)
		storage["daemonsets"] = daemonSetStorage
		storage["daemonsets/status"] = daemonSetStatusStorage
	}
	if apiResourceConfigSource.ResourceEnabled(version.WithResource("deployments")) {
		deploymentStorage := deploymentstore.NewStorage(restOptionsGetter)
		storage["deployments"] = deploymentStorage.Deployment
		storage["deployments/status"] = deploymentStorage.Status
		storage["deployments/rollback"] = deploymentStorage.Rollback
		storage["deployments/scale"] = deploymentStorage.Scale
	}
	if apiResourceConfigSource.ResourceEnabled(version.WithResource("ingresses")) {
		ingressStorage, ingressStatusStorage := ingressstore.NewREST(restOptionsGetter)
		storage["ingresses"] = ingressStorage
		storage["ingresses/status"] = ingressStatusStorage
	}
	if apiResourceConfigSource.ResourceEnabled(version.WithResource("podsecuritypolicy")) || apiResourceConfigSource.ResourceEnabled(version.WithResource("podsecuritypolicies")) {
		podSecurityExtensionsStorage := pspstore.NewREST(restOptionsGetter)
		storage["podSecurityPolicies"] = podSecurityExtensionsStorage
	}
	if apiResourceConfigSource.ResourceEnabled(version.WithResource("replicasets")) {
		replicaSetStorage := replicasetstore.NewStorage(restOptionsGetter)
		storage["replicasets"] = replicaSetStorage.ReplicaSet
		storage["replicasets/status"] = replicaSetStorage.Status
		storage["replicasets/scale"] = replicaSetStorage.Scale
	}
	if apiResourceConfigSource.ResourceEnabled(version.WithResource("networkpolicies")) {
		networkExtensionsStorage := networkpolicystore.NewREST(restOptionsGetter)
		storage["networkpolicies"] = networkExtensionsStorage
	}

	return storage
}

func (p RESTStorageProvider) PostStartHook() (string, genericapiserver.PostStartHookFunc, error) {
	return "extensions/third-party-resources", p.postStartHookFunc, nil
}
func (p RESTStorageProvider) postStartHookFunc(hookContext genericapiserver.PostStartHookContext) error {
	clientset, err := extensionsclient.NewForConfig(hookContext.LoopbackClientConfig)
	if err != nil {
		utilruntime.HandleError(fmt.Errorf("unable to initialize client: %v", err))
		return nil
	}

	thirdPartyControl := ThirdPartyController{
		master: p.ResourceInterface,
		client: clientset,
	}
	go wait.Forever(func() {
		if err := thirdPartyControl.SyncResources(); err != nil {
			glog.Warningf("third party resource sync failed: %v", err)
		}
	}, 10*time.Second)

	return nil
}

func (p RESTStorageProvider) GroupName() string {
	return extensions.GroupName
}
<|endoftext|>


<commit_before><commit_msg>clean doc<commit_after><|endoftext|>


<commit_before>package ut

import (
	"reflect"
	"testing"
)

type CallRecord struct {
	Name   string
	Params []interface{}
}

type CallRecords struct {
	Calls []CallRecord
}

func (cr *CallRecords) AddCall(name string, params ...interface{}) *CallRecords {
	cr.Calls = append(cr.Calls, CallRecord{Name: name, Params: params})
	return cr
}

func (cr *CallRecords) AssertEquals(t *testing.T, exp *CallRecords) {
	if len(cr.Calls) != len(exp.Calls) {
		t.Fatalf("Number of calls %d does nt match expected %d", len(cr.Calls), len(exp.Calls))
	}

	for i, c := range cr.Calls {
		e := exp.Calls[i]
		if c.Name != e.Name {
			t.Fatalf("Call %d expected %s, got %s", i, e.Name, c.Name)
		}
		if len(c.Params) != len(e.Params) {
			t.Fatalf("Call %d (%s) expected %d params, got %d", i, c.Name, len(e.Params), len(c.Params))
		}
		for j, ap := range c.Params {
			ep := e.Params[j]
			switch ep := ep.(type) {
			case func(actual interface{}):
				ep(ap)
			default:
				if !reflect.DeepEqual(ap, ep) {
					t.Fatalf("Call %d (%s) parameter %d got %v (%T) does not match expected %v (%T)", i, c.Name, j, ap, ap, ep, ep)
				}
			}
		}
	}
}
<commit_msg>re-jig to add tracking return parameters<commit_after>package ut

import (
	"reflect"
	"testing"
)

type CallTracker interface {
	// AddCall() is used by tests to add an expected call to the tracker
	AddCall(name string, params ...interface{}) CallTracker

	// SetReturns() is called immediately after AddCall() to set the return values for the call.
	SetReturns(returns ...interface{}) CallTracker

	// TrackCall() is called within mocks to track a call to the Mock. It returns the return values
	// registered via SetReturns()
	TrackCall(name string, params ...interface{}) []interface{}

	// AssertDone() should be called at the end of a test to confirm all the expected calls have
	// been made
	AssertDone()
}

type callRecord struct {
	name    string
	params  []interface{}
	returns []interface{}
}

func (e *callRecord) assert(t *testing.T, name string, params ...interface{}) {
	if name != e.name {
		t.Fatalf("Expected call to  %s, got call to %s", e.name, name)
	}
	if len(params) != len(e.params) {
		t.Fatalf("Call to (%s) expected %d params, got %d", name, len(e.params), len(params))
	}
	for i, ap := range params {
		ep := e.params[i]
		switch ep := ep.(type) {
		case func(actual interface{}):
			ep(ap)
		default:
			if !reflect.DeepEqual(ap, ep) {
				t.Fatalf("Call to (%s) parameter %d got %v (%T) does not match expected %v (%T)", name, i, ap, ap, ep, ep)
			}
		}
	}
}

type callRecords struct {
	t       *testing.T
	calls   []callRecord
	current int
}

func NewCallRecords(t *testing.T) CallTracker {
	return &callRecords{
		t: t,
	}
}

func (cr *callRecords) AddCall(name string, params ...interface{}) CallTracker {
	cr.calls = append(cr.calls, callRecord{name: name, params: params})
	return cr
}

func (cr *callRecords) SetReturns(returns ...interface{}) CallTracker {
	cr.calls[len(cr.calls)-1].returns = returns
	return cr
}

func (cr *callRecords) TrackCall(name string, params ...interface{}) []interface{} {
	expectedCall := cr.calls[cr.current]
	expectedCall.assert(cr.t, name, params...)
	cr.current += 1
	return expectedCall.returns
}

func (cr *callRecords) AssertDone() {
	if cr.current < len(cr.calls) {
		cr.t.Fatalf("Only %d of %d expected calls made", cr.current, len(cr.calls))
	}
}
<|endoftext|>