<commit_before>package main

import "fmt"
import "os"
import "net/http"
import "io/ioutil"
import "log"
import "strings"
import "bufio"
import "sort"

import "github.com/moovweb/gokogiri"
import "github.com/moovweb/gokogiri/xml"
import "github.com/moovweb/gokogiri/xpath"
import "github.com/codegangsta/cli"
import "github.com/gosuri/uitable"

// A map from IP to FQDN
type Hosts map[string]string

// Ok, it's a horrible hack... but I don't know yet how to propagated
// this variable from Arguments to Printf!
var hosts Hosts

// Take a hosts file in the same format than /etc/hosts file.
// Currently, the only two first elements are used.
func LoadHostsFile(filepath string) Hosts {
	file, err := os.Open(filepath)
	if err != nil {
		log.Fatal(err)
	}
	scanner := bufio.NewScanner(file)
	m := make(Hosts)
	for scanner.Scan() {
		line := scanner.Text()
		ips := strings.Split(line, " ")
		m[ips[0]] = ips[1]
	}
	return m
}

func load(url string, fromFile bool) *xml.XmlDocument {
	var data []byte
	if fromFile {
		file, _ := os.Open(url)
		data, _ = ioutil.ReadAll(file)
	} else {
		resp, err := http.Get(url)
		if err != nil {
			log.Fatal(err)
		}
		data, _ = ioutil.ReadAll(resp.Body)
	}
	doc, _ := gokogiri.ParseXml(data)
	return (doc)
}

func multiple(vrouter string, vrf_name string, count bool) {
	url := "http://" + vrouter + ":8085" + "/Snh_PageReq?x=begin:-1,end:-1,table:" + vrf_name + ".uc.route.0,"

	var doc = load(url, false)
	defer doc.Free()
	xps := xpath.Compile("//route_list/list/RouteUcSandeshData/path_list/list/PathSandeshData/nh/NhSandeshData/mc_list/../../../../../../src_ip/text()")
	ss, _ := doc.Root().Search(xps)
	if count {
		fmt.Printf("%d\n", len(ss))
	} else {
		for _, s := range ss {
			fmt.Printf("%s\n", s)
		}
	}
}

type Page struct {
	VrouterUrl string
	Table      string
}

type File struct {
	Path string
}

type LoadAble interface {
	Load(descCol DescCol) Collection
}

// Parse data to XML
func fromDataToCollection(data []byte, descCol DescCol, url string) Collection {
	doc, _ := gokogiri.ParseXml(data)
	ss, _ := doc.Search(descCol.BaseXpath)
	if len(ss) < 1 {
		log.Fatal(fmt.Sprintf("%d Failed to search xpath '%s'", len(ss), descCol.BaseXpath))
	}
	col := Collection{node: ss[0], descCol: descCol, url: url}
	col.Init()
	return col
}

func (file File) Load(descCol DescCol) Collection {
	f, _ := os.Open(file.Path)
	data, _ := ioutil.ReadAll(f)
	return fromDataToCollection(data, descCol, "file://"+file.Path)
}

func (page Page) Load(descCol DescCol) Collection {
	url := "http://" + page.VrouterUrl + ":8085/Snh_PageReq?x=begin:-1,end:-1,table:" + page.Table + ","
	resp, err := http.Get(url)
	if err != nil {
		log.Fatal(err)
	}
	data, _ := ioutil.ReadAll(resp.Body)
	return fromDataToCollection(data, descCol, url)
}

type Collection struct {
	url      string
	descCol  DescCol
	doc      *xml.XmlDocument
	node     xml.Node
	elements []Element
}

type DescCol struct {
	PageArgs    []string
	PageBuilder (func([]string) LoadAble)
	BaseXpath   string
	DescElt     DescElement
	SearchXpath (func(string) string)
}

type DescElement struct {
	ShortDetailXpath string
	LongDetail       LongAble
}

type Element struct {
	node xml.Node
	desc DescElement
}

func (col *Collection) Init() {
	ss, _ := col.node.Search("*")
	col.elements = make([]Element, len(ss))
	for i, s := range ss {
		col.elements[i] = Element{node: s, desc: col.descCol.DescElt}
	}
}

func (col *Collection) Search(pattern string) Elements {
	ss, _ := col.node.Search(col.descCol.SearchXpath(pattern))
	var elements []Element = make([]Element, len(ss))
	for i, s := range ss {
		elements[i] = Element{node: s, desc: col.descCol.DescElt}
	}
	return Elements(elements)
}

type Show interface {
	Long()
	Short()
	Xml()
}

type Elements []Element

func (e Element) Xml() {
	fmt.Printf("%s", e.node)
}
func (elts Elements) Xml() {
	for _, e := range elts {
		e.Xml()
	}
}
func (c Collection) Xml() {
	fmt.Printf("%s", c.node)
}

func (e Element) Short() {
	s, _ := e.node.Search(e.desc.ShortDetailXpath)
	if len(s) != 1 {
		log.Fatal("Xpath '" + e.desc.ShortDetailXpath + "' is not valid")
	}
	fmt.Printf("%s\n", s[0])
}
func (col Collection) Short() {
	Elements(col.elements).Short()
}
func (elts Elements) Short() {
	for _, e := range elts {
		e.Short()
	}
}
func (e Element) Long() {
	e.desc.LongDetail.Long(e)
}
func (col Collection) Long() {
	Elements(col.elements).Long()
}
func (elts Elements) Long() {
	for _, e := range elts {
		e.Long()
		fmt.Printf("\n")
	}
}

func DescItf() DescCol {
	return DescCol{
		BaseXpath: "__ItfResp_list/ItfResp/itf_list/list",
		DescElt: DescElement{
			ShortDetailXpath: "name/text()",
			LongDetail:       LongXpaths([]string{"uuid/text()", "name/text()", "vrf_name/text()"}),
		},
		PageArgs: []string{"vrouter-fqdn"},
		PageBuilder: func(args []string) LoadAble {
			return Page{Table: "db.interface.0", VrouterUrl: args[0]}
		},
	}
}
func DescRoute() DescCol {
	return DescCol{
		PageArgs: []string{"vrouter-fqdn", "vrf-name"},
		PageBuilder: func(args []string) LoadAble {
			return Page{VrouterUrl: args[0], Table: args[1] + ".uc.route.0,"}
		},
		BaseXpath: "__Inet4UcRouteResp_list/Inet4UcRouteResp/route_list/list",
		DescElt: DescElement{
			ShortDetailXpath: "src_ip/text()",
			LongDetail:       LongFunc(routeDetail)},
		SearchXpath: func(pattern string) string {
			return "RouteUcSandeshData/src_ip[contains(text(),'" + pattern + "')]/.."
		},
	}
}
func DescVrf() DescCol {
	return DescCol{
		PageArgs: []string{"vrouter-fqdn"},
		PageBuilder: func(args []string) LoadAble {
			return Page{Table: "db.vrf.0", VrouterUrl: args[0]}
		},
		BaseXpath: "__VrfListResp_list/VrfListResp/vrf_list/list",
		DescElt: DescElement{
			ShortDetailXpath: "name/text()",
			LongDetail:       LongXpaths([]string{"name/text()"}),
		},
		SearchXpath: func(pattern string) string {
			return "VrfSandeshData/name[contains(text(),'" + pattern + "')]/.."
		},
	}
}

type LongFunc (func(Element))
type LongXpaths []string

type LongAble interface {
	Long(e Element)
}

func (lf LongFunc) Long(e Element) {
	lf(e)
}
func (xpaths LongXpaths) Long(e Element) {
	for _, xpath := range xpaths {
		s, _ := e.node.Search(xpath)
		fmt.Printf("%s ", s[0])
	}
}

func ResolveIp(ip string) string {
	fqdn, ok := hosts[ip]
	if ok {
		return fqdn
	} else {
		return ip
	}
}

func Pretty(nodes []xml.Node) string {
	ret := make([]string, len(nodes))
	for i, n := range nodes {
		ret[i] = ResolveIp(n.Content())
	}
	sort.Strings(ret)
	return strings.Join(ret, " ; ")
}

func routeDetail(e Element) {
	srcIp, _ := e.node.Search("src_ip/text()")
	fmt.Printf("Src %s\n", srcIp[0])
	paths, _ := e.node.Search("path_list/list/PathSandeshData")

	table := uitable.New()
	table.MaxColWidth = 80
	table.AddRow("    Dst", "Peers", "MPLS label", "Interface", "Dest VN")
	for _, path := range paths {
		nhs, _ := path.Search("nh/NhSandeshData//dip/text()")
		peers, _ := path.Search("peer/text()")
		label, _ := path.Search("label/text()")
		destvn, _ := path.Search("dest_vn/text()")
		itf, _ := path.Search("nh/NhSandeshData/itf/text()")
		table.AddRow("    "+Pretty(nhs), Pretty(peers), Pretty(label), Pretty(itf), Pretty(destvn))
	}
	fmt.Println(table)
}

func GenCommand(descCol DescCol, name string, usage string) cli.Command {
	return cli.Command{
		Name:      name,
		Aliases:   []string{"a"},
		Usage:     usage,
		ArgsUsage: fmt.Sprintf("%s\n", strings.Join(descCol.PageArgs, " ")),
		Flags: []cli.Flag{
			cli.BoolFlag{
				Name:  "long, l",
				Usage: "Long version format",
			},
			cli.BoolFlag{
				Name:  "xml, x",
				Usage: "XML output format",
			},
			cli.BoolFlag{
				Name:  "from-file",
				Usage: "Load file instead URL (for debugging)",
			},
			cli.BoolFlag{
				Name:  "url, u",
				Usage: "Just show used URL",
			},
			cli.StringFlag{
				Name:  "search, s",
				Usage: "Search pattern",
				Value: "",
			},
		},
		Action: func(c *cli.Context) {
			var page LoadAble
			if c.IsSet("from-file") {
				page = File{Path: c.Args()[0]}
			} else {
				if c.NArg() < len(descCol.PageArgs) {
					cli.ShowSubcommandHelp(c)
					os.Exit(1)
				}
				page = descCol.PageBuilder(c.Args())
			}
			col := page.Load(descCol)
			if c.IsSet("url") {
				fmt.Println(col.url)
				return
			}

			var list Show

			if c.String("s") != "" {
				list = col.Search(c.String("s"))
			} else {
				list = col
			}

			if c.IsSet("xml") {
				list.Xml()
				return
			}
			if c.IsSet("long") {
				list.Long()
				return
			}
			list.Short()
		},
	}
}

func main() {
	var count bool
	var hosts_file string

	app := cli.NewApp()
	app.Name = "contrail-introspect-cli"
	app.Usage = "CLI on ContraiL Introspects"
	app.Version = "0.0.1"
	app.Before = func(c *cli.Context) error {
		if c.GlobalIsSet("hosts") {
			hosts = LoadHostsFile(c.GlobalString("hosts"))
			return nil
		}
		return nil
	}
	app.Flags = []cli.Flag{
		cli.StringFlag{
			Name:        "hosts",
			Usage:       "host file to do DNS resolution",
			Destination: &hosts_file,
		}}
	app.Commands = []cli.Command{
		GenCommand(DescRoute(), "route", "Show routes"),
		GenCommand(DescItf(), "itf", "Show interfaces"),
		GenCommand(DescVrf(), "vrf", "Show vrfs"),
		{
			Name:      "multiple",
			Usage:     "List routes with multiple nexthops",
			ArgsUsage: "vrouter vrf_name",
			Flags: []cli.Flag{
				cli.BoolFlag{
					Name:        "count",
					Destination: &count,
				}},
			Action: func(c *cli.Context) {
				if c.NArg() != 2 {
					log.Fatal("Wrong argument number!")
				}
				vrouter := c.Args()[0]
				vrf_name := c.Args()[1]
				multiple(vrouter, vrf_name, count)
			},
		},
	}
	app.Run(os.Args)
}
<commit_msg>Add VN command<commit_after>package main

import "fmt"
import "os"
import "net/http"
import "io/ioutil"
import "log"
import "strings"
import "bufio"
import "sort"

import "github.com/moovweb/gokogiri"
import "github.com/moovweb/gokogiri/xml"
import "github.com/moovweb/gokogiri/xpath"
import "github.com/codegangsta/cli"
import "github.com/gosuri/uitable"

// A map from IP to FQDN
type Hosts map[string]string

// Ok, it's a horrible hack... but I don't know yet how to propagated
// this variable from Arguments to Printf!
var hosts Hosts

// Take a hosts file in the same format than /etc/hosts file.
// Currently, the only two first elements are used.
func LoadHostsFile(filepath string) Hosts {
	file, err := os.Open(filepath)
	if err != nil {
		log.Fatal(err)
	}
	scanner := bufio.NewScanner(file)
	m := make(Hosts)
	for scanner.Scan() {
		line := scanner.Text()
		ips := strings.Split(line, " ")
		m[ips[0]] = ips[1]
	}
	return m
}

func load(url string, fromFile bool) *xml.XmlDocument {
	var data []byte
	if fromFile {
		file, _ := os.Open(url)
		data, _ = ioutil.ReadAll(file)
	} else {
		resp, err := http.Get(url)
		if err != nil {
			log.Fatal(err)
		}
		data, _ = ioutil.ReadAll(resp.Body)
	}
	doc, _ := gokogiri.ParseXml(data)
	return (doc)
}

func multiple(vrouter string, vrf_name string, count bool) {
	url := "http://" + vrouter + ":8085" + "/Snh_PageReq?x=begin:-1,end:-1,table:" + vrf_name + ".uc.route.0,"

	var doc = load(url, false)
	defer doc.Free()
	xps := xpath.Compile("//route_list/list/RouteUcSandeshData/path_list/list/PathSandeshData/nh/NhSandeshData/mc_list/../../../../../../src_ip/text()")
	ss, _ := doc.Root().Search(xps)
	if count {
		fmt.Printf("%d\n", len(ss))
	} else {
		for _, s := range ss {
			fmt.Printf("%s\n", s)
		}
	}
}

type Page struct {
	VrouterUrl string
	Table      string
}

type File struct {
	Path string
}

type LoadAble interface {
	Load(descCol DescCol) Collection
}

// Parse data to XML
func fromDataToCollection(data []byte, descCol DescCol, url string) Collection {
	doc, _ := gokogiri.ParseXml(data)
	ss, _ := doc.Search(descCol.BaseXpath)
	if len(ss) < 1 {
		log.Fatal(fmt.Sprintf("%d Failed to search xpath '%s'", len(ss), descCol.BaseXpath))
	}
	col := Collection{node: ss[0], descCol: descCol, url: url}
	col.Init()
	return col
}

func (file File) Load(descCol DescCol) Collection {
	f, _ := os.Open(file.Path)
	data, _ := ioutil.ReadAll(f)
	return fromDataToCollection(data, descCol, "file://"+file.Path)
}

func (page Page) Load(descCol DescCol) Collection {
	url := "http://" + page.VrouterUrl + ":8085/Snh_PageReq?x=begin:-1,end:-1,table:" + page.Table + ","
	resp, err := http.Get(url)
	if err != nil {
		log.Fatal(err)
	}
	data, _ := ioutil.ReadAll(resp.Body)
	return fromDataToCollection(data, descCol, url)
}

type Collection struct {
	url      string
	descCol  DescCol
	doc      *xml.XmlDocument
	node     xml.Node
	elements []Element
}

type DescCol struct {
	PageArgs    []string
	PageBuilder (func([]string) LoadAble)
	BaseXpath   string
	DescElt     DescElement
	SearchXpath (func(string) string)
}

type DescElement struct {
	ShortDetailXpath string
	LongDetail       LongAble
}

type Element struct {
	node xml.Node
	desc DescElement
}

func (col *Collection) Init() {
	ss, _ := col.node.Search("*")
	col.elements = make([]Element, len(ss))
	for i, s := range ss {
		col.elements[i] = Element{node: s, desc: col.descCol.DescElt}
	}
}

func (col *Collection) Search(pattern string) Elements {
	ss, _ := col.node.Search(col.descCol.SearchXpath(pattern))
	var elements []Element = make([]Element, len(ss))
	for i, s := range ss {
		elements[i] = Element{node: s, desc: col.descCol.DescElt}
	}
	return Elements(elements)
}

type Show interface {
	Long()
	Short()
	Xml()
}

type Elements []Element

func (e Element) Xml() {
	fmt.Printf("%s", e.node)
}
func (elts Elements) Xml() {
	for _, e := range elts {
		e.Xml()
	}
}
func (c Collection) Xml() {
	fmt.Printf("%s", c.node)
}

func (e Element) Short() {
	s, _ := e.node.Search(e.desc.ShortDetailXpath)
	if len(s) != 1 {
		log.Fatal("Xpath '" + e.desc.ShortDetailXpath + "' is not valid")
	}
	fmt.Printf("%s\n", s[0])
}
func (col Collection) Short() {
	Elements(col.elements).Short()
}
func (elts Elements) Short() {
	for _, e := range elts {
		e.Short()
	}
}
func (e Element) Long() {
	e.desc.LongDetail.Long(e)
}
func (col Collection) Long() {
	Elements(col.elements).Long()
}
func (elts Elements) Long() {
	for _, e := range elts {
		e.Long()
		fmt.Printf("\n")
	}
}

func DescItf() DescCol {
	return DescCol{
		BaseXpath: "__ItfResp_list/ItfResp/itf_list/list",
		DescElt: DescElement{
			ShortDetailXpath: "name/text()",
			LongDetail:       LongXpaths([]string{"uuid/text()", "name/text()", "vrf_name/text()"}),
		},
		PageArgs: []string{"vrouter-fqdn"},
		PageBuilder: func(args []string) LoadAble {
			return Page{Table: "db.interface.0", VrouterUrl: args[0]}
		},
	}
}
func DescRoute() DescCol {
	return DescCol{
		PageArgs: []string{"vrouter-fqdn", "vrf-name"},
		PageBuilder: func(args []string) LoadAble {
			return Page{VrouterUrl: args[0], Table: args[1] + ".uc.route.0,"}
		},
		BaseXpath: "__Inet4UcRouteResp_list/Inet4UcRouteResp/route_list/list",
		DescElt: DescElement{
			ShortDetailXpath: "src_ip/text()",
			LongDetail:       LongFunc(routeDetail)},
		SearchXpath: func(pattern string) string {
			return "RouteUcSandeshData/src_ip[contains(text(),'" + pattern + "')]/.."
		},
	}
}
func DescVrf() DescCol {
	return DescCol{
		PageArgs: []string{"vrouter-fqdn"},
		PageBuilder: func(args []string) LoadAble {
			return Page{Table: "db.vrf.0", VrouterUrl: args[0]}
		},
		BaseXpath: "__VrfListResp_list/VrfListResp/vrf_list/list",
		DescElt: DescElement{
			ShortDetailXpath: "name/text()",
			LongDetail:       LongXpaths([]string{"name/text()"}),
		},
		SearchXpath: func(pattern string) string {
			return "VrfSandeshData/name[contains(text(),'" + pattern + "')]/.."
		},
	}
}
func DescVn() DescCol {
	return DescCol{
		PageArgs: []string{"vrouter-fqdn"},
		PageBuilder: func(args []string) LoadAble {
			return Page{Table: "db.vn.0", VrouterUrl: args[0]}
		},
		BaseXpath: "__VnListResp_list/VnListResp/vn_list/list",
		DescElt: DescElement{
			ShortDetailXpath: "name/text()",
			LongDetail:       LongXpaths([]string{"name/text()", "vrf_name/text()"}),
		},
		SearchXpath: func(pattern string) string {
			return "VnSandeshData/name[contains(text(),'" + pattern + "')]/.."
		},
	}
}

type LongFunc (func(Element))
type LongXpaths []string

type LongAble interface {
	Long(e Element)
}

func (lf LongFunc) Long(e Element) {
	lf(e)
}
func (xpaths LongXpaths) Long(e Element) {
	for _, xpath := range xpaths {
		s, _ := e.node.Search(xpath)
		fmt.Printf("%s ", s[0])
	}
}

func ResolveIp(ip string) string {
	fqdn, ok := hosts[ip]
	if ok {
		return fqdn
	} else {
		return ip
	}
}

func Pretty(nodes []xml.Node) string {
	ret := make([]string, len(nodes))
	for i, n := range nodes {
		ret[i] = ResolveIp(n.Content())
	}
	sort.Strings(ret)
	return strings.Join(ret, " ; ")
}

func routeDetail(e Element) {
	srcIp, _ := e.node.Search("src_ip/text()")
	fmt.Printf("Src %s\n", srcIp[0])
	paths, _ := e.node.Search("path_list/list/PathSandeshData")

	table := uitable.New()
	table.MaxColWidth = 80
	table.AddRow("    Dst", "Peers", "MPLS label", "Interface", "Dest VN")
	for _, path := range paths {
		nhs, _ := path.Search("nh/NhSandeshData//dip/text()")
		peers, _ := path.Search("peer/text()")
		label, _ := path.Search("label/text()")
		destvn, _ := path.Search("dest_vn/text()")
		itf, _ := path.Search("nh/NhSandeshData/itf/text()")
		table.AddRow("    "+Pretty(nhs), Pretty(peers), Pretty(label), Pretty(itf), Pretty(destvn))
	}
	fmt.Println(table)
}

func GenCommand(descCol DescCol, name string, usage string) cli.Command {
	return cli.Command{
		Name:      name,
		Aliases:   []string{"a"},
		Usage:     usage,
		ArgsUsage: fmt.Sprintf("%s\n", strings.Join(descCol.PageArgs, " ")),
		Flags: []cli.Flag{
			cli.BoolFlag{
				Name:  "long, l",
				Usage: "Long version format",
			},
			cli.BoolFlag{
				Name:  "xml, x",
				Usage: "XML output format",
			},
			cli.BoolFlag{
				Name:  "from-file",
				Usage: "Load file instead URL (for debugging)",
			},
			cli.BoolFlag{
				Name:  "url, u",
				Usage: "Just show used URL",
			},
			cli.StringFlag{
				Name:  "search, s",
				Usage: "Search pattern",
				Value: "",
			},
		},
		Action: func(c *cli.Context) {
			var page LoadAble
			if c.IsSet("from-file") {
				page = File{Path: c.Args()[0]}
			} else {
				if c.NArg() < len(descCol.PageArgs) {
					cli.ShowSubcommandHelp(c)
					os.Exit(1)
				}
				page = descCol.PageBuilder(c.Args())
			}
			col := page.Load(descCol)
			if c.IsSet("url") {
				fmt.Println(col.url)
				return
			}

			var list Show

			if c.String("s") != "" {
				list = col.Search(c.String("s"))
			} else {
				list = col
			}

			if c.IsSet("xml") {
				list.Xml()
				return
			}
			if c.IsSet("long") {
				list.Long()
				return
			}
			list.Short()
		},
	}
}

func main() {
	var count bool
	var hosts_file string

	app := cli.NewApp()
	app.Name = "contrail-introspect-cli"
	app.Usage = "CLI on ContraiL Introspects"
	app.Version = "0.0.1"
	app.Before = func(c *cli.Context) error {
		if c.GlobalIsSet("hosts") {
			hosts = LoadHostsFile(c.GlobalString("hosts"))
			return nil
		}
		return nil
	}
	app.Flags = []cli.Flag{
		cli.StringFlag{
			Name:        "hosts",
			Usage:       "host file to do DNS resolution",
			Destination: &hosts_file,
		}}
	app.Commands = []cli.Command{
		GenCommand(DescRoute(), "route", "Show routes"),
		GenCommand(DescItf(), "itf", "Show interfaces"),
		GenCommand(DescVrf(), "vrf", "Show vrfs"),
		GenCommand(DescVn(), "vn", "Show virtual network"),
		{
			Name:      "multiple",
			Usage:     "List routes with multiple nexthops",
			ArgsUsage: "vrouter vrf_name",
			Flags: []cli.Flag{
				cli.BoolFlag{
					Name:        "count",
					Destination: &count,
				}},
			Action: func(c *cli.Context) {
				if c.NArg() != 2 {
					log.Fatal("Wrong argument number!")
				}
				vrouter := c.Args()[0]
				vrf_name := c.Args()[1]
				multiple(vrouter, vrf_name, count)
			},
		},
	}
	app.Run(os.Args)
}
<|endoftext|>


<commit_before>// Copyright 2017 Diego Bernardes. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package resource

import (
	"encoding/json"
	"fmt"
	"net/http"
	"net/url"
	"strings"
	"time"

	"github.com/pkg/errors"
	"github.com/satori/go.uuid"

	"github.com/diegobernardes/flare"
)

type pagination flare.Pagination

func (p *pagination) MarshalJSON() ([]byte, error) {
	return json.Marshal(&struct {
		Limit  int `json:"limit"`
		Offset int `json:"offset"`
		Total  int `json:"total"`
	}{
		Limit:  p.Limit,
		Total:  p.Total,
		Offset: p.Offset,
	})
}

type resource flare.Resource

func (r *resource) MarshalJSON() ([]byte, error) {
	change := map[string]string{
		"kind":  r.Change.Kind,
		"field": r.Change.Field,
	}

	if r.Change.DateFormat != "" {
		change["dateFormat"] = r.Change.DateFormat
	}

	return json.Marshal(&struct {
		Id        string            `json:"id"`
		Addresses []string          `json:"addresses"`
		Path      string            `json:"path"`
		Change    map[string]string `json:"change"`
		CreatedAt string            `json:"createdAt"`
	}{
		Id:        r.Id,
		Addresses: r.Addresses,
		Path:      r.Path,
		Change:    change,
		CreatedAt: r.CreatedAt.Format(time.RFC3339),
	})
}

type resourceCreateChange struct {
	Kind       string `json:"kind"`
	Field      string `json:"field"`
	DateFormat string `json:"dateFormat"`
}

type resourceCreate struct {
	Path      string               `json:"path"`
	Addresses []string             `json:"addresses"`
	Change    resourceCreateChange `json:"change"`
}

func (r *resourceCreate) valid() error {
	if err := r.validAddresses(); err != nil {
		return err
	}

	if r.Path == "" {
		return errors.New("missing path")
	}

	if r.Path[0] != '/' {
		return errors.New("path should start with a slash")
	}

	if err := r.validWildcard(); err != nil {
		return err
	}

	if r.Change.Field == "" {
		return errors.New("missing change")
	}

	switch r.Change.Kind {
	case flare.ResourceChangeInteger, flare.ResourceChangeString:
	case flare.ResourceChangeDate:
		if r.Change.DateFormat == "" {
			return errors.New("missing change.dateFormat")
		}
	default:
		return errors.New("invalid change.kind")
	}

	return nil
}

func (r *resourceCreate) validWildcard() error {
	var hasWildcard bool
	for _, value := range strings.Split(r.Path, "/") {
		if value == "" {
			continue
		}

		if value[0] == '{' && value[len(value)-1] == '}' {
			hasWildcard = true
		}

		if strings.Count(value, "{") > 1 || strings.Count(value, "}") > 1 {
			return errors.New("could not use brackets inside a wildcard or next to another wildcard")
		}
	}

	if !hasWildcard {
		return errors.New("missing wildcard")
	}
	return nil
}

func (r *resourceCreate) validAddresses() error {
	if len(r.Addresses) == 0 {
		return errors.New("missing addresses")
	}

	for _, address := range r.Addresses {
		d, err := url.Parse(address)
		if err != nil {
			return errors.Wrap(err, fmt.Sprintf("error during address parse '%s'", address))
		}

		if d.Path != "" {
			return fmt.Errorf("address is invalid because it has a path '%s'", d.Path)
		}

		if d.RawQuery != "" {
			return fmt.Errorf("address is invalid because it has query string '%s'", d.RawQuery)
		}

		if d.Fragment != "" {
			return fmt.Errorf("address is invalid because it has fragment '%s'", d.Fragment)
		}

		switch d.Scheme {
		case "http", "https":
			continue
		case "":
			return errors.Errorf("missing scheme on address '%s'", address)
		default:
			return errors.Errorf("invalid scheme on address '%s'", address)
		}
	}

	return nil
}

func (r *resourceCreate) toFlareResource() *flare.Resource {
	return &flare.Resource{
		Id:        uuid.NewV4().String(),
		Addresses: r.Addresses,
		Path:      r.Path,
		Change: flare.ResourceChange{
			Kind:       r.Change.Kind,
			Field:      r.Change.Field,
			DateFormat: r.Change.DateFormat,
		},
	}
}

func transformResources(r []flare.Resource) []resource {
	result := make([]resource, len(r))
	for i := 0; i < len(r); i++ {
		result[i] = (resource)(r[i])
	}
	return result
}

func transformResource(r *flare.Resource) *resource { return (*resource)(r) }

func transformPagination(p *flare.Pagination) *pagination { return (*pagination)(p) }

type response struct {
	Pagination *pagination
	Error      *responseError
	Resources  []resource
	Resource   *resource
}

func (r *response) MarshalJSON() ([]byte, error) {
	var result interface{}

	if r.Error != nil {
		result = map[string]*responseError{"error": r.Error}
	} else if r.Resource != nil {
		result = r.Resource
	} else {
		result = map[string]interface{}{
			"pagination": r.Pagination,
			"resources":  r.Resources,
		}
	}

	return json.Marshal(result)
}

type responseError struct {
	Status int    `json:"status"`
	Title  string `json:"title"`
	Detail string `json:"detail,omitempty"`
}

func (s *Service) writeError(w http.ResponseWriter, err error, title string, status int) {
	resp := &response{Error: &responseError{Status: status}}

	if err != nil {
		resp.Error.Detail = err.Error()
	}

	if title != "" {
		resp.Error.Title = title
	}

	s.writeResponse(w, resp, status, nil)
}
<commit_msg>resource: validate if the path don't end with a slash<commit_after>// Copyright 2017 Diego Bernardes. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package resource

import (
	"encoding/json"
	"fmt"
	"net/http"
	"net/url"
	"strings"
	"time"

	"github.com/pkg/errors"
	"github.com/satori/go.uuid"

	"github.com/diegobernardes/flare"
)

type pagination flare.Pagination

func (p *pagination) MarshalJSON() ([]byte, error) {
	return json.Marshal(&struct {
		Limit  int `json:"limit"`
		Offset int `json:"offset"`
		Total  int `json:"total"`
	}{
		Limit:  p.Limit,
		Total:  p.Total,
		Offset: p.Offset,
	})
}

type resource flare.Resource

func (r *resource) MarshalJSON() ([]byte, error) {
	change := map[string]string{
		"kind":  r.Change.Kind,
		"field": r.Change.Field,
	}

	if r.Change.DateFormat != "" {
		change["dateFormat"] = r.Change.DateFormat
	}

	return json.Marshal(&struct {
		Id        string            `json:"id"`
		Addresses []string          `json:"addresses"`
		Path      string            `json:"path"`
		Change    map[string]string `json:"change"`
		CreatedAt string            `json:"createdAt"`
	}{
		Id:        r.Id,
		Addresses: r.Addresses,
		Path:      r.Path,
		Change:    change,
		CreatedAt: r.CreatedAt.Format(time.RFC3339),
	})
}

type resourceCreateChange struct {
	Kind       string `json:"kind"`
	Field      string `json:"field"`
	DateFormat string `json:"dateFormat"`
}

type resourceCreate struct {
	Path      string               `json:"path"`
	Addresses []string             `json:"addresses"`
	Change    resourceCreateChange `json:"change"`
}

func (r *resourceCreate) valid() error {
	if err := r.validAddresses(); err != nil {
		return err
	}

	if r.Path == "" {
		return errors.New("missing path")
	}

	if r.Path[0] != '/' {
		return errors.New("path should start with a slash")
	}

	if r.Path[len(r.Path)-1] == '/' {
		return errors.New("path should not end with a slash")
	}

	if err := r.validWildcard(); err != nil {
		return err
	}

	if r.Change.Field == "" {
		return errors.New("missing change")
	}

	switch r.Change.Kind {
	case flare.ResourceChangeInteger, flare.ResourceChangeString:
	case flare.ResourceChangeDate:
		if r.Change.DateFormat == "" {
			return errors.New("missing change.dateFormat")
		}
	default:
		return errors.New("invalid change.kind")
	}

	return nil
}

func (r *resourceCreate) validWildcard() error {
	var hasWildcard bool
	for _, value := range strings.Split(r.Path, "/") {
		if value == "" {
			continue
		}

		if value[0] == '{' && value[len(value)-1] == '}' {
			hasWildcard = true
		}

		if strings.Count(value, "{") > 1 || strings.Count(value, "}") > 1 {
			return errors.New("could not use brackets inside a wildcard or next to another wildcard")
		}
	}

	if !hasWildcard {
		return errors.New("missing wildcard")
	}
	return nil
}

func (r *resourceCreate) validAddresses() error {
	if len(r.Addresses) == 0 {
		return errors.New("missing addresses")
	}

	for _, address := range r.Addresses {
		d, err := url.Parse(address)
		if err != nil {
			return errors.Wrap(err, fmt.Sprintf("error during address parse '%s'", address))
		}

		if d.Path != "" {
			return fmt.Errorf("address is invalid because it has a path '%s'", d.Path)
		}

		if d.RawQuery != "" {
			return fmt.Errorf("address is invalid because it has query string '%s'", d.RawQuery)
		}

		if d.Fragment != "" {
			return fmt.Errorf("address is invalid because it has fragment '%s'", d.Fragment)
		}

		switch d.Scheme {
		case "http", "https":
			continue
		case "":
			return errors.Errorf("missing scheme on address '%s'", address)
		default:
			return errors.Errorf("invalid scheme on address '%s'", address)
		}
	}

	return nil
}

func (r *resourceCreate) toFlareResource() *flare.Resource {
	return &flare.Resource{
		Id:        uuid.NewV4().String(),
		Addresses: r.Addresses,
		Path:      r.Path,
		Change: flare.ResourceChange{
			Kind:       r.Change.Kind,
			Field:      r.Change.Field,
			DateFormat: r.Change.DateFormat,
		},
	}
}

func transformResources(r []flare.Resource) []resource {
	result := make([]resource, len(r))
	for i := 0; i < len(r); i++ {
		result[i] = (resource)(r[i])
	}
	return result
}

func transformResource(r *flare.Resource) *resource { return (*resource)(r) }

func transformPagination(p *flare.Pagination) *pagination { return (*pagination)(p) }

type response struct {
	Pagination *pagination
	Error      *responseError
	Resources  []resource
	Resource   *resource
}

func (r *response) MarshalJSON() ([]byte, error) {
	var result interface{}

	if r.Error != nil {
		result = map[string]*responseError{"error": r.Error}
	} else if r.Resource != nil {
		result = r.Resource
	} else {
		result = map[string]interface{}{
			"pagination": r.Pagination,
			"resources":  r.Resources,
		}
	}

	return json.Marshal(result)
}

type responseError struct {
	Status int    `json:"status"`
	Title  string `json:"title"`
	Detail string `json:"detail,omitempty"`
}

func (s *Service) writeError(w http.ResponseWriter, err error, title string, status int) {
	resp := &response{Error: &responseError{Status: status}}

	if err != nil {
		resp.Error.Detail = err.Error()
	}

	if title != "" {
		resp.Error.Title = title
	}

	s.writeResponse(w, resp, status, nil)
}
<|endoftext|>


<commit_before>// SNS endpoint

package sns

import(
	"strconv"

	SDK "github.com/aws/aws-sdk-go/service/sns"
)

// SNSEndpoint is struct for endpoint(device / )
type SNSEndpoint struct {
	svc      *AmazonSNS
	arn      string
	protocol string
	token string
	enable bool
}

// Publish sends push notification to the endpoint
func (e *SNSEndpoint) Publish(msg string, badge int) error {
	return e.svc.Publish(e.arn, msg, map[string]interface{}{"badge": badge})
}

// GetARN returns endpoint ARN
func (e *SNSEndpoint) GetARN() string {
	return e.arn
}

// GetToken returns endpoint Token
func (e *SNSEndpoint) GetToken() string {
	return e.token
}

// SetToken set endpoint Token
func (e *SNSEndpoint) SetToken(token string) {
	e.token = token
}

// UpdateTokenAsEnable updates token and enabled as true
func (e *SNSEndpoint) UpdateTokenAsEnable() error {
	e.enable = true
	return e.UpdateToken()
}

// UpdateTokenAsEnable updates token and enabled as false
func (e *SNSEndpoint) UpdateTokenAsDisable() error {
	e.enable = false
	return e.UpdateToken()
}

// UpdateToken updates endpoint attributes
func (e *SNSEndpoint) UpdateToken() error {
	in := &SDK.SetEndpointAttributesInput{
		EndpointARN: String(e.arn),
		Attributes: map[string]*string{
			"Enabled": String(strconv.FormatBool(e.enable)),
			"Token": String(e.token),
		},
	}
	_, err := e.svc.Client.SetEndpointAttributes(in)
	return err
}
<commit_msg>Add enable method<commit_after>// SNS endpoint

package sns

import (
	"strconv"

	SDK "github.com/aws/aws-sdk-go/service/sns"
)

// SNSEndpoint is struct for endpoint(device / )
type SNSEndpoint struct {
	svc      *AmazonSNS
	arn      string
	protocol string
	token    string
	enable   bool
}

// Publish sends push notification to the endpoint
func (e *SNSEndpoint) Publish(msg string, badge int) error {
	return e.svc.Publish(e.arn, msg, map[string]interface{}{"badge": badge})
}

// GetARN returns endpoint ARN
func (e *SNSEndpoint) GetARN() string {
	return e.arn
}

// GetToken returns endpoint Token
func (e *SNSEndpoint) GetToken() string {
	return e.token
}

// SetToken set endpoint Token
func (e *SNSEndpoint) SetToken(token string) {
	e.token = token
}

// Enable returns endpoint Enable
func (e *SNSEndpoint) Enable() bool {
	return e.enable
}

// UpdateTokenAsEnable updates token and enabled as true
func (e *SNSEndpoint) UpdateTokenAsEnable() error {
	e.enable = true
	return e.UpdateToken()
}

// UpdateTokenAsEnable updates token and enabled as false
func (e *SNSEndpoint) UpdateTokenAsDisable() error {
	e.enable = false
	return e.UpdateToken()
}

// UpdateToken updates endpoint attributes
func (e *SNSEndpoint) UpdateToken() error {
	in := &SDK.SetEndpointAttributesInput{
		EndpointARN: String(e.arn),
		Attributes: map[string]*string{
			"Enabled": String(strconv.FormatBool(e.enable)),
			"Token":   String(e.token),
		},
	}
	_, err := e.svc.Client.SetEndpointAttributes(in)
	return err
}
<|endoftext|>


<commit_before>package brats_test

import (
	"os"

	"io/ioutil"

	bratsutils "github.com/cloudfoundry/bosh-release-acceptance-tests/brats-utils"

	. "github.com/onsi/ginkgo"
	. "github.com/onsi/ginkgo/extensions/table"
	. "github.com/onsi/gomega"
)

var _ = Describe("Director external database TLS connections", func() {
	testDBConnectionOverTLS := func(databaseType string, mutualTLSEnabled bool, useIncorrectCA bool) {
		tmpCertDir, err := ioutil.TempDir("", "db_tls")
		Expect(err).ToNot(HaveOccurred())
		dbConfig := bratsutils.LoadExternalDBConfig(databaseType, mutualTLSEnabled, tmpCertDir)
		bratsutils.CreateDB(dbConfig)
		defer os.RemoveAll(tmpCertDir)
		defer bratsutils.DeleteDB(dbConfig)

		startInnerBoshArgs := bratsutils.InnerBoshWithExternalDBOptions(dbConfig)

		bratsutils.StartInnerBosh(startInnerBoshArgs...)
		defer bratsutils.StopInnerBosh()
		bratsutils.UploadRelease("https://bosh.io/d/github.com/cloudfoundry/syslog-release?v=11")
	}

	Context("RDS", func() {
		var mutualTLSEnabled = false

		DescribeTable("Regular TLS", testDBConnectionOverTLS,
			Entry("allows TLS connections to POSTGRES", "rds_postgres", mutualTLSEnabled),
			Entry("allows TLS connections to MYSQL", "rds_mysql", mutualTLSEnabled),
		)
	})

	Context("GCP", func() {
		Context("Regular TLS", func() {
			Context("With valid CA", func() {
				var mutualTLSEnabled = false

				DescribeTable("DB Connections", testDBConnectionOverTLS,
					Entry("allows TLS connections to MYSQL", "gcp_mysql", mutualTLSEnabled),
					Entry("allows TLS connections to POSTGRES", "gcp_postgres", mutualTLSEnabled),
				)
			})
		})

		Context("Mutual TLS", func() {
			var mutualTLSEnabled = true

			DescribeTable("DB Connections", testDBConnectionOverTLS,
				Entry("allows TLS connections to MYSQL", "gcp_mysql", mutualTLSEnabled),
				Entry("allows TLS connections to POSTGRES", "gcp_postgres", mutualTLSEnabled),
			)
		})
	})
})
<commit_msg>[brats] fix bad syntax in table entry<commit_after>package brats_test

import (
	"os"

	"io/ioutil"

	bratsutils "github.com/cloudfoundry/bosh-release-acceptance-tests/brats-utils"

	. "github.com/onsi/ginkgo"
	. "github.com/onsi/ginkgo/extensions/table"
	. "github.com/onsi/gomega"
)

var _ = Describe("Director external database TLS connections", func() {
	testDBConnectionOverTLS := func(databaseType string, mutualTLSEnabled bool) {
		tmpCertDir, err := ioutil.TempDir("", "db_tls")
		Expect(err).ToNot(HaveOccurred())
		dbConfig := bratsutils.LoadExternalDBConfig(databaseType, mutualTLSEnabled, tmpCertDir)
		bratsutils.CreateDB(dbConfig)
		defer os.RemoveAll(tmpCertDir)
		defer bratsutils.DeleteDB(dbConfig)

		startInnerBoshArgs := bratsutils.InnerBoshWithExternalDBOptions(dbConfig)

		bratsutils.StartInnerBosh(startInnerBoshArgs...)
		defer bratsutils.StopInnerBosh()
		bratsutils.UploadRelease("https://bosh.io/d/github.com/cloudfoundry/syslog-release?v=11")
	}

	Context("RDS", func() {
		var mutualTLSEnabled = false

		DescribeTable("Regular TLS", testDBConnectionOverTLS,
			Entry("allows TLS connections to POSTGRES", "rds_postgres", mutualTLSEnabled),
			Entry("allows TLS connections to MYSQL", "rds_mysql", mutualTLSEnabled),
		)
	})

	Context("GCP", func() {
		Context("Regular TLS", func() {
			Context("With valid CA", func() {
				var mutualTLSEnabled = false

				DescribeTable("DB Connections", testDBConnectionOverTLS,
					Entry("allows TLS connections to MYSQL", "gcp_mysql", mutualTLSEnabled),
					Entry("allows TLS connections to POSTGRES", "gcp_postgres", mutualTLSEnabled),
				)
			})
		})

		Context("Mutual TLS", func() {
			var mutualTLSEnabled = true

			DescribeTable("DB Connections", testDBConnectionOverTLS,
				Entry("allows TLS connections to MYSQL", "gcp_mysql", mutualTLSEnabled),
				Entry("allows TLS connections to POSTGRES", "gcp_postgres", mutualTLSEnabled),
			)
		})
	})
})
<|endoftext|>


<commit_before>// Copyright 2015 CloudAwan LLC
//
// 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 restapi

import (
	"fmt"
	"github.com/cloudawan/cloudone/control"
	"github.com/emicklei/go-restful"
	"net/http"
	"strconv"
)

type Namesapce struct {
	Name string
}

func registerWebServiceNamespace() {
	ws := new(restful.WebService)
	ws.Path("/api/v1/namespaces")
	ws.Consumes(restful.MIME_JSON)
	ws.Produces(restful.MIME_JSON)
	restful.Add(ws)

	ws.Route(ws.GET("/").Filter(authorize).Filter(auditLog).To(getAllKubernetesNamespaceName).
		Doc("Get all of the kubernetes namespace name").
		Param(ws.QueryParameter("kubeapihost", "Kubernetes host").DataType("string")).
		Param(ws.QueryParameter("kubeapiport", "Kubernetes port").DataType("int")).
		Do(returns200AllKubernetesNamesapceName, returns400, returns404, returns500))

	ws.Route(ws.POST("/").Filter(authorize).Filter(auditLog).To(postKubernetesNamespace).
		Doc("Add a kubernetes namespace").
		Param(ws.QueryParameter("kubeapihost", "Kubernetes host").DataType("string")).
		Param(ws.QueryParameter("kubeapiport", "Kubernetes port").DataType("int")).
		Do(returns200, returns400, returns404, returns500).
		Reads(Namesapce{}))

	ws.Route(ws.DELETE("/{namespace}").Filter(authorize).Filter(auditLog).To(deleteKubernetesNamespace).
		Doc("Delete the kubernetes namespace").
		Param(ws.PathParameter("namespace", "Kubernetes namespace").DataType("string")).
		Param(ws.QueryParameter("kubeapihost", "Kubernetes host").DataType("string")).
		Param(ws.QueryParameter("kubeapiport", "Kubernetes port").DataType("int")).
		Do(returns200, returns400, returns404, returns500))
}

func getAllKubernetesNamespaceName(request *restful.Request, response *restful.Response) {
	kubeapiHost := request.QueryParameter("kubeapihost")
	kubeapiPortText := request.QueryParameter("kubeapiport")
	if kubeapiHost == "" || kubeapiPortText == "" {
		errorText := fmt.Sprintf("Input text is incorrect kubeapiHost %s kubeapiPort %s", kubeapiHost, kubeapiPortText)
		log.Error(errorText)
		response.WriteErrorString(400, `{"Error": "`+errorText+`"}`)
		return
	}
	kubeapiPort, err := strconv.Atoi(kubeapiPortText)
	if err != nil {
		errorText := fmt.Sprintf("Could not parse kubeapiPortText %s with error %s", kubeapiPortText, err)
		log.Error(errorText)
		response.WriteErrorString(400, `{"Error": "`+errorText+`"}`)
		return
	}

	nameSlice, err := control.GetAllNamespaceName(kubeapiHost, kubeapiPort)
	if err != nil {
		errorText := fmt.Sprintf("Get all namespace name failure kubeapiHost %s kubeapiPort %s", kubeapiHost, kubeapiPortText)
		log.Error(errorText)
		response.WriteErrorString(404, `{"Error": "`+errorText+`"}`)
		return
	}

	response.WriteJson(nameSlice, "[]string")
}

func postKubernetesNamespace(request *restful.Request, response *restful.Response) {
	kubeapiHost := request.QueryParameter("kubeapihost")
	kubeapiPortText := request.QueryParameter("kubeapiport")
	if kubeapiHost == "" || kubeapiPortText == "" {
		errorText := fmt.Sprintf("Input text is incorrect kubeapiHost %s kubeapiPort %s", kubeapiHost, kubeapiPortText)
		log.Error(errorText)
		response.WriteErrorString(400, `{"Error": "`+errorText+`"}`)
		return
	}
	kubeapiPort, err := strconv.Atoi(kubeapiPortText)
	if err != nil {
		errorText := fmt.Sprintf("Could not parse kubeapiPortText %s with error %s", kubeapiPortText, err)
		log.Error(errorText)
		response.WriteErrorString(400, `{"Error": "`+errorText+`"}`)
		return
	}

	namespace := new(Namesapce)
	err = request.ReadEntity(&namespace)

	if err != nil {
		errorText := fmt.Sprintf("POST namespace %s kubeapiHost %s kubeapiPort %s failure with error %s", namespace, kubeapiHost, kubeapiPort, err)
		log.Error(errorText)
		response.WriteErrorString(400, `{"Error": "`+errorText+`"}`)
		return
	}

	err = control.CreateNamespace(kubeapiHost, kubeapiPort, namespace.Name)

	if err != nil {
		errorText := fmt.Sprintf("Create namespace failure kubeapiHost %s kubeapiPort %s namespace %s", kubeapiHost, kubeapiPort, namespace)
		log.Error(errorText)
		response.WriteErrorString(404, `{"Error": "`+errorText+`"}`)
		return
	}
}

func deleteKubernetesNamespace(request *restful.Request, response *restful.Response) {
	kubeapiHost := request.QueryParameter("kubeapihost")
	kubeapiPortText := request.QueryParameter("kubeapiport")
	namespace := request.PathParameter("namespace")
	if kubeapiHost == "" || kubeapiPortText == "" || namespace == "" {
		errorText := fmt.Sprintf("Input text is incorrect kubeapiHost %s kubeapiHost %s namespace %s", kubeapiHost, kubeapiPortText, namespace)
		log.Error(errorText)
		response.WriteErrorString(400, `{"Error": "`+errorText+`"}`)
		return
	}
	kubeapiPort, err := strconv.Atoi(kubeapiPortText)
	if err != nil {
		errorText := fmt.Sprintf("Could not parse kubeapiPortText %s with error %s", kubeapiPortText, err)
		log.Error(errorText)
		response.WriteErrorString(400, `{"Error": "`+errorText+`"}`)
		return
	}

	err = control.DeleteNamespace(kubeapiHost, kubeapiPort, namespace)

	if err != nil {
		errorText := fmt.Sprintf("Delete namespace failure kubeapiHost %s kubeapiPort %s namespace %s error %s", kubeapiHost, kubeapiPort, namespace, err)
		log.Error(errorText)
		response.WriteErrorString(404, `{"Error": "`+errorText+`"}`)
		return
	}
}

func returns200AllKubernetesNamesapceName(b *restful.RouteBuilder) {
	b.Returns(http.StatusOK, "OK", []string{})
}
<commit_msg>Add error message<commit_after>// Copyright 2015 CloudAwan LLC
//
// 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 restapi

import (
	"fmt"
	"github.com/cloudawan/cloudone/control"
	"github.com/emicklei/go-restful"
	"net/http"
	"strconv"
)

type Namesapce struct {
	Name string
}

func registerWebServiceNamespace() {
	ws := new(restful.WebService)
	ws.Path("/api/v1/namespaces")
	ws.Consumes(restful.MIME_JSON)
	ws.Produces(restful.MIME_JSON)
	restful.Add(ws)

	ws.Route(ws.GET("/").Filter(authorize).Filter(auditLog).To(getAllKubernetesNamespaceName).
		Doc("Get all of the kubernetes namespace name").
		Param(ws.QueryParameter("kubeapihost", "Kubernetes host").DataType("string")).
		Param(ws.QueryParameter("kubeapiport", "Kubernetes port").DataType("int")).
		Do(returns200AllKubernetesNamesapceName, returns400, returns404, returns500))

	ws.Route(ws.POST("/").Filter(authorize).Filter(auditLog).To(postKubernetesNamespace).
		Doc("Add a kubernetes namespace").
		Param(ws.QueryParameter("kubeapihost", "Kubernetes host").DataType("string")).
		Param(ws.QueryParameter("kubeapiport", "Kubernetes port").DataType("int")).
		Do(returns200, returns400, returns404, returns500).
		Reads(Namesapce{}))

	ws.Route(ws.DELETE("/{namespace}").Filter(authorize).Filter(auditLog).To(deleteKubernetesNamespace).
		Doc("Delete the kubernetes namespace").
		Param(ws.PathParameter("namespace", "Kubernetes namespace").DataType("string")).
		Param(ws.QueryParameter("kubeapihost", "Kubernetes host").DataType("string")).
		Param(ws.QueryParameter("kubeapiport", "Kubernetes port").DataType("int")).
		Do(returns200, returns400, returns404, returns500))
}

func getAllKubernetesNamespaceName(request *restful.Request, response *restful.Response) {
	kubeapiHost := request.QueryParameter("kubeapihost")
	kubeapiPortText := request.QueryParameter("kubeapiport")
	if kubeapiHost == "" || kubeapiPortText == "" {
		errorText := fmt.Sprintf("Input text is incorrect kubeapiHost %s kubeapiPort %s", kubeapiHost, kubeapiPortText)
		log.Error(errorText)
		response.WriteErrorString(400, `{"Error": "`+errorText+`"}`)
		return
	}
	kubeapiPort, err := strconv.Atoi(kubeapiPortText)
	if err != nil {
		errorText := fmt.Sprintf("Could not parse kubeapiPortText %s with error %s", kubeapiPortText, err)
		log.Error(errorText)
		response.WriteErrorString(400, `{"Error": "`+errorText+`"}`)
		return
	}

	nameSlice, err := control.GetAllNamespaceName(kubeapiHost, kubeapiPort)
	if err != nil {
		errorText := fmt.Sprintf("Get all namespace name failure kubeapiHost %s kubeapiPort %s", kubeapiHost, kubeapiPortText)
		log.Error(errorText)
		response.WriteErrorString(404, `{"Error": "`+errorText+`"}`)
		return
	}

	response.WriteJson(nameSlice, "[]string")
}

func postKubernetesNamespace(request *restful.Request, response *restful.Response) {
	kubeapiHost := request.QueryParameter("kubeapihost")
	kubeapiPortText := request.QueryParameter("kubeapiport")
	if kubeapiHost == "" || kubeapiPortText == "" {
		errorText := fmt.Sprintf("Input text is incorrect kubeapiHost %s kubeapiPort %s", kubeapiHost, kubeapiPortText)
		log.Error(errorText)
		response.WriteErrorString(400, `{"Error": "`+errorText+`"}`)
		return
	}
	kubeapiPort, err := strconv.Atoi(kubeapiPortText)
	if err != nil {
		errorText := fmt.Sprintf("Could not parse kubeapiPortText %s with error %s", kubeapiPortText, err)
		log.Error(errorText)
		response.WriteErrorString(400, `{"Error": "`+errorText+`"}`)
		return
	}

	namespace := new(Namesapce)
	err = request.ReadEntity(&namespace)

	if err != nil {
		errorText := fmt.Sprintf("POST namespace %s kubeapiHost %s kubeapiPort %s failure with error %s", namespace, kubeapiHost, kubeapiPort, err)
		log.Error(errorText)
		response.WriteErrorString(400, `{"Error": "`+errorText+`"}`)
		return
	}

	err = control.CreateNamespace(kubeapiHost, kubeapiPort, namespace.Name)

	if err != nil {
		errorText := fmt.Sprintf("Create namespace failure kubeapiHost %s kubeapiPort %s namespace %s error %s", kubeapiHost, kubeapiPort, namespace, err)
		log.Error(errorText)
		response.WriteErrorString(404, `{"Error": "`+errorText+`"}`)
		return
	}
}

func deleteKubernetesNamespace(request *restful.Request, response *restful.Response) {
	kubeapiHost := request.QueryParameter("kubeapihost")
	kubeapiPortText := request.QueryParameter("kubeapiport")
	namespace := request.PathParameter("namespace")
	if kubeapiHost == "" || kubeapiPortText == "" || namespace == "" {
		errorText := fmt.Sprintf("Input text is incorrect kubeapiHost %s kubeapiHost %s namespace %s", kubeapiHost, kubeapiPortText, namespace)
		log.Error(errorText)
		response.WriteErrorString(400, `{"Error": "`+errorText+`"}`)
		return
	}
	kubeapiPort, err := strconv.Atoi(kubeapiPortText)
	if err != nil {
		errorText := fmt.Sprintf("Could not parse kubeapiPortText %s with error %s", kubeapiPortText, err)
		log.Error(errorText)
		response.WriteErrorString(400, `{"Error": "`+errorText+`"}`)
		return
	}

	err = control.DeleteNamespace(kubeapiHost, kubeapiPort, namespace)

	if err != nil {
		errorText := fmt.Sprintf("Delete namespace failure kubeapiHost %s kubeapiPort %s namespace %s error %s", kubeapiHost, kubeapiPort, namespace, err)
		log.Error(errorText)
		response.WriteErrorString(404, `{"Error": "`+errorText+`"}`)
		return
	}
}

func returns200AllKubernetesNamesapceName(b *restful.RouteBuilder) {
	b.Returns(http.StatusOK, "OK", []string{})
}
<|endoftext|>


<commit_before>/* Copyright 2020 Google Inc.

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

    https://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 cert

import (
	"context"
	"crypto/x509"
	"crypto/x509/pkix"
	"errors"
	"fmt"
	"reflect"
	"testing"

	"github.com/google/gnxi/gnoi/cert/pb"
	"google.golang.org/grpc"
)

type rotateRequestMap struct {
	req  *pb.RotateCertificateRequest
	resp *pb.RotateCertificateResponse
}
type rotateClient struct {
	pb.CertificateManagement_RotateClient
	reqMap  []*rotateRequestMap
	i       int
	recv    chan int
	recvErr chan error
}

type mockClient struct {
	pb.CertificateManagementClient
	rotate *rotateClient
}

func (c *rotateClient) Send(req *pb.RotateCertificateRequest) error {
	if c.i < len(c.reqMap) {
		if reflect.TypeOf(req.RotateRequest) == reflect.TypeOf(c.reqMap[c.i].req.RotateRequest) {
			c.recv <- c.i
		} else {
			c.recvErr <- errors.New("error")
		}
		c.i++
	}
	return nil
}

func (c *rotateClient) Recv() (*pb.RotateCertificateResponse, error) {
	select {
	case i := <-c.recv:
		return c.reqMap[i].resp, nil
	case err := <-c.recvErr:
		return nil, err
	}
}

func (c *mockClient) Rotate(ctx context.Context, opts ...grpc.CallOption) (pb.CertificateManagement_RotateClient, error) {
	return c.rotate, nil
}

func TestClientRotate(t *testing.T) {
	tests := []struct {
		name     string
		reqMap   []*rotateRequestMap
		err      error
		sign     func(*x509.CertificateRequest) (*x509.Certificate, error)
		caBundle []*x509.Certificate
		validate func() error
	}{
		{
			"failed to receive RotateCertificateResponse",
			[]*rotateRequestMap{
				{
					&pb.RotateCertificateRequest{RotateRequest: nil},
					&pb.RotateCertificateResponse{},
				},
			},
			errors.New("failed to receive RotateCertificateResponse: error"),
			func(_ *x509.CertificateRequest) (*x509.Certificate, error) { return &x509.Certificate{}, nil },
			[]*x509.Certificate{},
			func() error { return nil },
		},
		{
			"expected GenerateCSRRequest",
			[]*rotateRequestMap{
				{
					&pb.RotateCertificateRequest{RotateRequest: &pb.RotateCertificateRequest_GenerateCsr{}},
					&pb.RotateCertificateResponse{RotateResponse: nil},
				},
			},
			errors.New("expected GenerateCSRRequest, got something else"),
			func(_ *x509.CertificateRequest) (*x509.Certificate, error) { return &x509.Certificate{}, nil },
			[]*x509.Certificate{},
			func() error { return nil },
		},
		{
			"fail to sign the CSR",
			[]*rotateRequestMap{
				{
					&pb.RotateCertificateRequest{RotateRequest: &pb.RotateCertificateRequest_GenerateCsr{}},
					&pb.RotateCertificateResponse{RotateResponse: &pb.RotateCertificateResponse_GeneratedCsr{GeneratedCsr: &pb.GenerateCSRResponse{Csr: &pb.CSR{}}}},
				},
			},
			errors.New("failed to sign the CSR: error"),
			func(_ *x509.CertificateRequest) (*x509.Certificate, error) {
				return nil, errors.New("error")
			},
			[]*x509.Certificate{},
			func() error { return nil },
		},
	}
	x509toPEM = func(cert *x509.Certificate) []byte {
		return []byte{}
	}
	parseCSR = func(genCSR *pb.GenerateCSRResponse) (*x509.CertificateRequest, error) {
		return &x509.CertificateRequest{}, nil
	}
	for _, test := range tests {
		t.Run(test.name, func(t *testing.T) {
			client := &Client{client: &mockClient{rotate: &rotateClient{
				reqMap:  test.reqMap,
				recv:    make(chan int, 1),
				recvErr: make(chan error, 1),
			}}}
			if err := client.Rotate(
				context.Background(), "", 0, pkix.Name{
					Country:            []string{""},
					Organization:       []string{""},
					OrganizationalUnit: []string{""},
					Province:           []string{""},
				}, "", test.sign, test.caBundle, test.validate,
			); fmt.Sprintf("%v", err) != fmt.Sprintf("%v", test.err) {
				t.Errorf("Wanted error: **%v** but got error: **%v**", test.err, err)
			}
		})
	}
}
<commit_msg>Finished Rotate test<commit_after>/* Copyright 2020 Google Inc.

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

    https://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 cert

import (
	"context"
	"crypto/x509"
	"crypto/x509/pkix"
	"errors"
	"fmt"
	"reflect"
	"testing"

	"github.com/google/gnxi/gnoi/cert/pb"
	"google.golang.org/grpc"
)

type rotateRequestMap struct {
	req  *pb.RotateCertificateRequest
	resp *pb.RotateCertificateResponse
}
type rotateClient struct {
	pb.CertificateManagement_RotateClient
	reqMap  []*rotateRequestMap
	i       int
	recv    chan int
	recvErr chan error
}

type mockClient struct {
	pb.CertificateManagementClient
	rotate *rotateClient
}

func (c *rotateClient) Send(req *pb.RotateCertificateRequest) error {
	if c.i < len(c.reqMap) {
		if reflect.TypeOf(req.RotateRequest) == reflect.TypeOf(c.reqMap[c.i].req.RotateRequest) {
			c.recv <- c.i
		} else {
			c.recvErr <- errors.New("error")
		}
		c.i++
	}
	return nil
}

func (c *rotateClient) Recv() (*pb.RotateCertificateResponse, error) {
	select {
	case i := <-c.recv:
		return c.reqMap[i].resp, nil
	case err := <-c.recvErr:
		return nil, err
	}
}

func (c *mockClient) Rotate(ctx context.Context, opts ...grpc.CallOption) (pb.CertificateManagement_RotateClient, error) {
	return c.rotate, nil
}

func TestClientRotate(t *testing.T) {
	tests := []struct {
		name     string
		reqMap   []*rotateRequestMap
		err      error
		sign     func(*x509.CertificateRequest) (*x509.Certificate, error)
		caBundle []*x509.Certificate
		validate func() error
	}{
		{
			"Failed to receive RotateCertificateResponse",
			[]*rotateRequestMap{
				{
					&pb.RotateCertificateRequest{RotateRequest: nil},
					&pb.RotateCertificateResponse{},
				},
			},
			errors.New("failed to receive RotateCertificateResponse: error"),
			func(_ *x509.CertificateRequest) (*x509.Certificate, error) { return &x509.Certificate{}, nil },
			[]*x509.Certificate{},
			func() error { return nil },
		},
		{
			"Expected GenerateCSRRequest",
			[]*rotateRequestMap{
				{
					&pb.RotateCertificateRequest{RotateRequest: &pb.RotateCertificateRequest_GenerateCsr{}},
					&pb.RotateCertificateResponse{RotateResponse: nil},
				},
			},
			errors.New("expected GenerateCSRRequest, got something else"),
			func(_ *x509.CertificateRequest) (*x509.Certificate, error) { return &x509.Certificate{}, nil },
			[]*x509.Certificate{},
			func() error { return nil },
		},
		{
			"Fail to sign the CSR",
			[]*rotateRequestMap{
				{
					&pb.RotateCertificateRequest{RotateRequest: &pb.RotateCertificateRequest_GenerateCsr{}},
					&pb.RotateCertificateResponse{RotateResponse: &pb.RotateCertificateResponse_GeneratedCsr{GeneratedCsr: &pb.GenerateCSRResponse{Csr: &pb.CSR{}}}},
				},
			},
			errors.New("failed to sign the CSR: error"),
			func(_ *x509.CertificateRequest) (*x509.Certificate, error) {
				return nil, errors.New("error")
			},
			[]*x509.Certificate{},
			func() error { return nil },
		},
		{
			"Failed to receive RotateCertificateResponse",
			[]*rotateRequestMap{
				{
					&pb.RotateCertificateRequest{RotateRequest: &pb.RotateCertificateRequest_GenerateCsr{}},
					&pb.RotateCertificateResponse{RotateResponse: &pb.RotateCertificateResponse_GeneratedCsr{GeneratedCsr: &pb.GenerateCSRResponse{Csr: &pb.CSR{}}}},
				},
				{
					&pb.RotateCertificateRequest{RotateRequest: nil},
					nil,
				},
			},
			errors.New("failed to receive RotateCertificateResponse: error"),
			func(_ *x509.CertificateRequest) (*x509.Certificate, error) { return &x509.Certificate{}, nil },
			[]*x509.Certificate{},
			func() error { return nil },
		},
		{
			"expected LoadCertificateResponse, got something else",
			[]*rotateRequestMap{
				{
					&pb.RotateCertificateRequest{RotateRequest: &pb.RotateCertificateRequest_GenerateCsr{}},
					&pb.RotateCertificateResponse{RotateResponse: &pb.RotateCertificateResponse_GeneratedCsr{GeneratedCsr: &pb.GenerateCSRResponse{Csr: &pb.CSR{}}}},
				},
				{
					&pb.RotateCertificateRequest{RotateRequest: &pb.RotateCertificateRequest_LoadCertificate{}},
					&pb.RotateCertificateResponse{RotateResponse: nil},
				},
			},
			errors.New("expected LoadCertificateResponse, got something else"),
			func(_ *x509.CertificateRequest) (*x509.Certificate, error) { return &x509.Certificate{}, nil },
			[]*x509.Certificate{},
			func() error { return nil },
		},
		{
			"Validation error",
			[]*rotateRequestMap{
				{
					&pb.RotateCertificateRequest{RotateRequest: &pb.RotateCertificateRequest_GenerateCsr{}},
					&pb.RotateCertificateResponse{RotateResponse: &pb.RotateCertificateResponse_GeneratedCsr{GeneratedCsr: &pb.GenerateCSRResponse{Csr: &pb.CSR{}}}},
				},
				{
					&pb.RotateCertificateRequest{RotateRequest: &pb.RotateCertificateRequest_LoadCertificate{}},
					&pb.RotateCertificateResponse{RotateResponse: &pb.RotateCertificateResponse_LoadCertificate{LoadCertificate: &pb.LoadCertificateResponse{}}},
				},
			},
			errors.New("failed to validate rotated certificate: error"),
			func(_ *x509.CertificateRequest) (*x509.Certificate, error) { return &x509.Certificate{}, nil },
			[]*x509.Certificate{},
			func() error { return errors.New("error") },
		},
		{
			"Successful",
			[]*rotateRequestMap{
				{
					&pb.RotateCertificateRequest{RotateRequest: &pb.RotateCertificateRequest_GenerateCsr{}},
					&pb.RotateCertificateResponse{RotateResponse: &pb.RotateCertificateResponse_GeneratedCsr{GeneratedCsr: &pb.GenerateCSRResponse{Csr: &pb.CSR{}}}},
				},
				{
					&pb.RotateCertificateRequest{RotateRequest: &pb.RotateCertificateRequest_LoadCertificate{}},
					&pb.RotateCertificateResponse{RotateResponse: &pb.RotateCertificateResponse_LoadCertificate{LoadCertificate: &pb.LoadCertificateResponse{}}},
				},
				{
					&pb.RotateCertificateRequest{RotateRequest: &pb.RotateCertificateRequest_FinalizeRotation{FinalizeRotation: &pb.FinalizeRequest{}}},
					nil,
				},
			},
			nil,
			func(_ *x509.CertificateRequest) (*x509.Certificate, error) { return &x509.Certificate{}, nil },
			[]*x509.Certificate{{}},
			func() error { return nil },
		},
	}
	x509toPEM = func(cert *x509.Certificate) []byte {
		return []byte{}
	}
	parseCSR = func(genCSR *pb.GenerateCSRResponse) (*x509.CertificateRequest, error) {
		return &x509.CertificateRequest{}, nil
	}
	for _, test := range tests {
		t.Run(test.name, func(t *testing.T) {
			client := &Client{client: &mockClient{rotate: &rotateClient{
				reqMap:  test.reqMap,
				recv:    make(chan int, 1),
				recvErr: make(chan error, 1),
			}}}
			if err := client.Rotate(
				context.Background(), "", 0, pkix.Name{
					Country:            []string{""},
					Organization:       []string{""},
					OrganizationalUnit: []string{""},
					Province:           []string{""},
				}, "", test.sign, test.caBundle, test.validate,
			); fmt.Sprintf("%v", err) != fmt.Sprintf("%v", test.err) {
				t.Errorf("Wanted error: **%v** but got error: **%v**", test.err, err)
			}
		})
	}
}
<|endoftext|>


<commit_before>// Package ndpcmd provides the commands for the ndp utility.
package ndpcmd

import (
	"context"
	"fmt"
	"log"
	"net"
	"os"

	"github.com/mdlayher/ndp"
)

// Run runs the ndp utility.
func Run(ctx context.Context, c *ndp.Conn, ifi *net.Interface, op string, target net.IP) error {
	switch op {
	// listen is the default when no op is specified..
	case "listen", "":
		return listen(ctx, c)
	case "ns":
		return sendNS(ctx, c, ifi.HardwareAddr, target)
	case "rs":
		return sendRS(ctx, c, ifi.HardwareAddr)
	default:
		return fmt.Errorf("unrecognized operation: %q", op)
	}
}

func listen(ctx context.Context, c *ndp.Conn) error {
	ll := log.New(os.Stderr, "ndp listen> ", 0)
	ll.Println("listening for messages")

	// Also listen for router solicitations from other hosts, even though we
	// will never reply to them.
	if err := c.JoinGroup(net.IPv6linklocalallrouters); err != nil {
		return err
	}

	if err := receiveLoop(ctx, c, ll); err != nil {
		return fmt.Errorf("failed to read message: %v", err)
	}

	return nil
}

func sendNS(ctx context.Context, c *ndp.Conn, addr net.HardwareAddr, target net.IP) error {
	ll := log.New(os.Stderr, "ndp ns> ", 0)

	ll.Printf("neighbor solicitation:\n    - source link-layer address: %s", addr.String())

	// Always multicast the message to the target's solicited-node multicast
	// group as if we have no knowledge of its MAC address.
	snm, err := ndp.SolicitedNodeMulticast(target)
	if err != nil {
		return fmt.Errorf("failed to determine solicited-node multicast address: %v", err)
	}

	m := &ndp.NeighborSolicitation{
		TargetAddress: target,
		Options: []ndp.Option{
			&ndp.LinkLayerAddress{
				Direction: ndp.Source,
				Addr:      addr,
			},
		},
	}

	// Expect neighbor advertisement messages with the correct target address.
	check := func(m ndp.Message) bool {
		na, ok := m.(*ndp.NeighborAdvertisement)
		if !ok {
			return false
		}

		return na.TargetAddress.Equal(target)
	}

	if err := sendReceiveLoop(ctx, c, ll, m, snm, check); err != nil {
		if err == context.Canceled {
			return err
		}

		return fmt.Errorf("failed to send neighbor solicitation: %v", err)
	}

	return nil
}

func sendRS(ctx context.Context, c *ndp.Conn, addr net.HardwareAddr) error {
	ll := log.New(os.Stderr, "ndp rs> ", 0)

	ll.Printf("router solicitation:\n    - source link-layer address: %s", addr.String())

	m := &ndp.RouterSolicitation{
		Options: []ndp.Option{
			&ndp.LinkLayerAddress{
				Direction: ndp.Source,
				Addr:      addr,
			},
		},
	}

	// Expect any router advertisement message.
	check := func(m ndp.Message) bool {
		_, ok := m.(*ndp.RouterAdvertisement)
		return ok
	}

	if err := sendReceiveLoop(ctx, c, ll, m, net.IPv6linklocalallrouters, check); err != nil {
		if err == context.Canceled {
			return err
		}

		return fmt.Errorf("failed to send router solicitation: %v", err)
	}

	return nil
}
<commit_msg>internal/ndpcmd: -t is only valid for NS operation<commit_after>// Package ndpcmd provides the commands for the ndp utility.
package ndpcmd

import (
	"context"
	"errors"
	"fmt"
	"log"
	"net"
	"os"

	"github.com/mdlayher/ndp"
)

var (
	errTargetOp = errors.New("flag '-t' is only valid for neighbor solicitation operation")
)

// Run runs the ndp utility.
func Run(ctx context.Context, c *ndp.Conn, ifi *net.Interface, op string, target net.IP) error {
	switch op {
	// listen is the default when no op is specified.
	case "listen", "":
		if target != nil {
			return errTargetOp
		}

		return listen(ctx, c)
	case "ns":
		return sendNS(ctx, c, ifi.HardwareAddr, target)
	case "rs":
		if target != nil {
			return errTargetOp
		}

		return sendRS(ctx, c, ifi.HardwareAddr)
	default:
		return fmt.Errorf("unrecognized operation: %q", op)
	}
}

func listen(ctx context.Context, c *ndp.Conn) error {
	ll := log.New(os.Stderr, "ndp listen> ", 0)
	ll.Println("listening for messages")

	// Also listen for router solicitations from other hosts, even though we
	// will never reply to them.
	if err := c.JoinGroup(net.IPv6linklocalallrouters); err != nil {
		return err
	}

	if err := receiveLoop(ctx, c, ll); err != nil {
		return fmt.Errorf("failed to read message: %v", err)
	}

	return nil
}

func sendNS(ctx context.Context, c *ndp.Conn, addr net.HardwareAddr, target net.IP) error {
	ll := log.New(os.Stderr, "ndp ns> ", 0)

	ll.Printf("neighbor solicitation:\n    - source link-layer address: %s", addr.String())

	// Always multicast the message to the target's solicited-node multicast
	// group as if we have no knowledge of its MAC address.
	snm, err := ndp.SolicitedNodeMulticast(target)
	if err != nil {
		return fmt.Errorf("failed to determine solicited-node multicast address: %v", err)
	}

	m := &ndp.NeighborSolicitation{
		TargetAddress: target,
		Options: []ndp.Option{
			&ndp.LinkLayerAddress{
				Direction: ndp.Source,
				Addr:      addr,
			},
		},
	}

	// Expect neighbor advertisement messages with the correct target address.
	check := func(m ndp.Message) bool {
		na, ok := m.(*ndp.NeighborAdvertisement)
		if !ok {
			return false
		}

		return na.TargetAddress.Equal(target)
	}

	if err := sendReceiveLoop(ctx, c, ll, m, snm, check); err != nil {
		if err == context.Canceled {
			return err
		}

		return fmt.Errorf("failed to send neighbor solicitation: %v", err)
	}

	return nil
}

func sendRS(ctx context.Context, c *ndp.Conn, addr net.HardwareAddr) error {
	ll := log.New(os.Stderr, "ndp rs> ", 0)

	ll.Printf("router solicitation:\n    - source link-layer address: %s", addr.String())

	m := &ndp.RouterSolicitation{
		Options: []ndp.Option{
			&ndp.LinkLayerAddress{
				Direction: ndp.Source,
				Addr:      addr,
			},
		},
	}

	// Expect any router advertisement message.
	check := func(m ndp.Message) bool {
		_, ok := m.(*ndp.RouterAdvertisement)
		return ok
	}

	if err := sendReceiveLoop(ctx, c, ll, m, net.IPv6linklocalallrouters, check); err != nil {
		if err == context.Canceled {
			return err
		}

		return fmt.Errorf("failed to send router solicitation: %v", err)
	}

	return nil
}
<|endoftext|>


<commit_before>// Copyright 2015 Hajime Hoshi
//
// 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.

// +build darwin freebsd linux windows
// +build !js
// +build !android
// +build !ios

package ui

import (
	"errors"
	"runtime"
	"sync"
	"time"

	"github.com/go-gl/glfw/v3.2/glfw"
	"github.com/hajimehoshi/ebiten/internal/opengl"
)

type userInterface struct {
	window          *glfw.Window
	width           int
	height          int
	scale           float64
	deviceScale     float64
	glfwScale       float64
	fullscreen      bool
	fullscreenScale float64
	funcs           chan func()
	running         bool
	sizeChanged     bool
	origPosX        int
	origPosY        int
	m               sync.Mutex
}

var currentUI *userInterface

func init() {
	if err := initialize(); err != nil {
		panic(err)
	}
}

func initialize() error {
	runtime.LockOSThread()

	if err := glfw.Init(); err != nil {
		return err
	}
	glfw.WindowHint(glfw.Visible, glfw.False)
	glfw.WindowHint(glfw.Resizable, glfw.False)
	glfw.WindowHint(glfw.ContextVersionMajor, 2)
	glfw.WindowHint(glfw.ContextVersionMinor, 1)

	// As start, create an window with temporary size to create OpenGL context thread.
	window, err := glfw.CreateWindow(16, 16, "", nil, nil)
	if err != nil {
		return err
	}
	hideConsoleWindowOnWindows()
	u := &userInterface{
		window:      window,
		funcs:       make(chan func()),
		sizeChanged: true,
		origPosX:    -1,
		origPosY:    -1,
	}
	u.window.MakeContextCurrent()
	glfw.SwapInterval(1)
	currentUI = u
	return nil
}

func RunMainThreadLoop(ch <-chan error) error {
	// TODO: Check this is done on the main thread.
	currentUI.setRunning(true)
	defer func() {
		currentUI.setRunning(false)
	}()
	for {
		select {
		case f := <-currentUI.funcs:
			f()
		case err := <-ch:
			// ch returns a value not only when an error occur but also it is closed.
			return err
		}
	}
}

func (u *userInterface) isRunning() bool {
	u.m.Lock()
	defer u.m.Unlock()
	return u.running
}

func (u *userInterface) setRunning(running bool) {
	u.m.Lock()
	defer u.m.Unlock()
	u.running = running
}

func (u *userInterface) runOnMainThread(f func() error) error {
	if u.funcs == nil {
		// already closed
		return nil
	}
	ch := make(chan struct{})
	var err error
	u.funcs <- func() {
		err = f()
		close(ch)
	}
	<-ch
	return err
}

func SetScreenSize(width, height int) bool {
	u := currentUI
	if !u.isRunning() {
		panic("ui: Run is not called yet")
	}
	r := false
	_ = u.runOnMainThread(func() error {
		r = u.setScreenSize(width, height, u.scale, u.fullscreen)
		return nil
	})
	return r
}

func SetScreenScale(scale float64) bool {
	u := currentUI
	if !u.isRunning() {
		panic("ui: Run is not called yet")
	}
	r := false
	_ = u.runOnMainThread(func() error {
		r = u.setScreenSize(u.width, u.height, scale, u.fullscreen)
		return nil
	})
	return r
}

func SetFullscreen(fullscreen bool) bool {
	u := currentUI
	if !u.isRunning() {
		panic("ui: Run is not called yet")
	}
	r := false
	_ = u.runOnMainThread(func() error {
		r = u.setScreenSize(u.width, u.height, u.scale, fullscreen)
		return nil
	})
	return r
}

func ScreenScale() float64 {
	u := currentUI
	if !u.isRunning() {
		return 0
	}
	s := 0.0
	_ = u.runOnMainThread(func() error {
		s = u.scale
		return nil
	})
	return s
}

func IsFullscreen() bool {
	u := currentUI
	if !u.isRunning() {
		return false
	}
	f := false
	_ = u.runOnMainThread(func() error {
		f = u.fullscreen
		return nil
	})
	return f
}

func SetCursorVisibility(visible bool) {
	// This can be called before Run: change the state asyncly.
	go func() {
		_ = currentUI.runOnMainThread(func() error {
			c := glfw.CursorNormal
			if !visible {
				c = glfw.CursorHidden
			}
			currentUI.window.SetInputMode(glfw.CursorMode, c)
			return nil
		})
	}()
}

func Run(width, height int, scale float64, title string, g GraphicsContext) error {
	u := currentUI
	// GLContext must be created before setting the screen size, which requires
	// swapping buffers.
	opengl.Init(currentUI.runOnMainThread)
	if err := u.runOnMainThread(func() error {
		m := glfw.GetPrimaryMonitor()
		v := m.GetVideoMode()
		if !u.setScreenSize(width, height, scale, false) {
			return errors.New("ui: Fail to set the screen size")
		}
		u.window.SetTitle(title)
		u.window.Show()

		w, h := u.glfwSize()
		x := (v.Width - w) / 2
		y := (v.Height - h) / 3
		x, y = adjustWindowPosition(x, y)
		u.window.SetPos(x, y)
		return nil
	}); err != nil {
		return err
	}
	return u.loop(g)
}

func (u *userInterface) glfwSize() (int, int) {
	if u.glfwScale == 0 {
		u.glfwScale = glfwScale()
	}
	return int(float64(u.width) * u.scale * u.glfwScale), int(float64(u.height) * u.scale * u.glfwScale)
}

func (u *userInterface) getScale() float64 {
	if !u.fullscreen {
		return u.scale
	}
	if u.fullscreenScale == 0 {
		m := glfw.GetPrimaryMonitor()
		v := m.GetVideoMode()
		sw := float64(v.Width) / float64(u.width)
		sh := float64(v.Height) / float64(u.height)
		s := sw
		if s > sh {
			s = sh
		}
		u.fullscreenScale = s
	}
	return u.fullscreenScale
}

func (u *userInterface) actualScreenScale() float64 {
	if u.deviceScale == 0 {
		u.deviceScale = deviceScale()
	}
	return u.getScale() * u.deviceScale
}

func (u *userInterface) pollEvents() {
	glfw.PollEvents()
	if u.glfwScale == 0 {
		u.glfwScale = glfwScale()
	}
	currentInput.update(u.window, u.getScale()*u.glfwScale)
}

func (u *userInterface) update(g GraphicsContext) error {
	shouldClose := false
	_ = u.runOnMainThread(func() error {
		shouldClose = u.window.ShouldClose()
		return nil
	})
	if shouldClose {
		return &RegularTermination{}
	}

	actualScale := 0.0
	_ = u.runOnMainThread(func() error {
		if !u.sizeChanged {
			return nil
		}
		u.sizeChanged = false
		actualScale = u.actualScreenScale()
		return nil
	})
	if 0 < actualScale {
		g.SetSize(u.width, u.height, actualScale)
	}

	_ = u.runOnMainThread(func() error {
		u.pollEvents()
		for u.window.GetAttrib(glfw.Focused) == 0 {
			// Wait for an arbitrary period to avoid busy loop.
			time.Sleep(time.Second / 60)
			u.pollEvents()
			if u.window.ShouldClose() {
				return nil
			}
		}
		return nil
	})
	if err := g.Update(); err != nil {
		return err
	}
	return nil
}

func (u *userInterface) loop(g GraphicsContext) error {
	defer func() {
		_ = u.runOnMainThread(func() error {
			glfw.Terminate()
			return nil
		})
	}()
	for {
		if err := u.update(g); err != nil {
			return err
		}
		// The bound framebuffer must be the default one (0) before swapping buffers.
		if err := opengl.GetContext().BindScreenFramebuffer(); err != nil {
			return err
		}
		_ = u.runOnMainThread(func() error {
			u.swapBuffers()
			return nil
		})
	}
}

func (u *userInterface) swapBuffers() {
	u.window.SwapBuffers()
}

func (u *userInterface) setScreenSize(width, height int, scale float64, fullscreen bool) bool {
	if u.width == width && u.height == height && u.scale == scale && u.fullscreen == fullscreen {
		return false
	}

	origScale := u.scale
	u.scale = scale

	// On Windows, giving a too small width doesn't call a callback (#165).
	// To prevent hanging up, return asap if the width is too small.
	// 252 is an arbitrary number and I guess this is small enough.
	// TODO: The same check should be in ui_js.go
	const minWindowWidth = 252
	if int(float64(width)*u.actualScreenScale()) < minWindowWidth {
		u.scale = origScale
		return false
	}
	u.width = width
	u.height = height

	// To make sure the current existing framebuffers are rendered,
	// swap buffers here before SetSize is called.
	u.swapBuffers()

	u.fullscreen = fullscreen

	window := u.window
	m := glfw.GetPrimaryMonitor()
	v := m.GetVideoMode()
	if u.fullscreen {
		u.origPosX, u.origPosY = window.GetPos()
		window.SetMonitor(m, 0, 0, v.Width, v.Height, v.RefreshRate)
	} else {
		window.SetMonitor(nil, 0, 0, 16, 16, v.RefreshRate)
		ch := make(chan struct{})
		window.SetFramebufferSizeCallback(func(_ *glfw.Window, width, height int) {
			window.SetFramebufferSizeCallback(nil)
			close(ch)
		})
		w, h := u.glfwSize()
		window.SetSize(w, h)
	event:
		for {
			glfw.PollEvents()
			select {
			case <-ch:
				break event
			default:
			}
		}
		// Reverted from fullscreen
		if u.origPosX >= 0 && u.origPosY >= 0 {
			window.SetPos(u.origPosX, u.origPosY)
		}
	}
	// TODO: Rename this variable?
	u.sizeChanged = true
	return true
}
<commit_msg>ui: Bug fix: position was wrong when changing window size<commit_after>// Copyright 2015 Hajime Hoshi
//
// 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.

// +build darwin freebsd linux windows
// +build !js
// +build !android
// +build !ios

package ui

import (
	"errors"
	"runtime"
	"sync"
	"time"

	"github.com/go-gl/glfw/v3.2/glfw"
	"github.com/hajimehoshi/ebiten/internal/opengl"
)

type userInterface struct {
	window          *glfw.Window
	width           int
	height          int
	scale           float64
	deviceScale     float64
	glfwScale       float64
	fullscreen      bool
	fullscreenScale float64
	funcs           chan func()
	running         bool
	sizeChanged     bool
	origPosX        int
	origPosY        int
	m               sync.Mutex
}

var currentUI *userInterface

func init() {
	if err := initialize(); err != nil {
		panic(err)
	}
}

func initialize() error {
	runtime.LockOSThread()

	if err := glfw.Init(); err != nil {
		return err
	}
	glfw.WindowHint(glfw.Visible, glfw.False)
	glfw.WindowHint(glfw.Resizable, glfw.False)
	glfw.WindowHint(glfw.ContextVersionMajor, 2)
	glfw.WindowHint(glfw.ContextVersionMinor, 1)

	// As start, create an window with temporary size to create OpenGL context thread.
	window, err := glfw.CreateWindow(16, 16, "", nil, nil)
	if err != nil {
		return err
	}
	hideConsoleWindowOnWindows()
	u := &userInterface{
		window:      window,
		funcs:       make(chan func()),
		sizeChanged: true,
		origPosX:    -1,
		origPosY:    -1,
	}
	u.window.MakeContextCurrent()
	glfw.SwapInterval(1)
	currentUI = u
	return nil
}

func RunMainThreadLoop(ch <-chan error) error {
	// TODO: Check this is done on the main thread.
	currentUI.setRunning(true)
	defer func() {
		currentUI.setRunning(false)
	}()
	for {
		select {
		case f := <-currentUI.funcs:
			f()
		case err := <-ch:
			// ch returns a value not only when an error occur but also it is closed.
			return err
		}
	}
}

func (u *userInterface) isRunning() bool {
	u.m.Lock()
	defer u.m.Unlock()
	return u.running
}

func (u *userInterface) setRunning(running bool) {
	u.m.Lock()
	defer u.m.Unlock()
	u.running = running
}

func (u *userInterface) runOnMainThread(f func() error) error {
	if u.funcs == nil {
		// already closed
		return nil
	}
	ch := make(chan struct{})
	var err error
	u.funcs <- func() {
		err = f()
		close(ch)
	}
	<-ch
	return err
}

func SetScreenSize(width, height int) bool {
	u := currentUI
	if !u.isRunning() {
		panic("ui: Run is not called yet")
	}
	r := false
	_ = u.runOnMainThread(func() error {
		r = u.setScreenSize(width, height, u.scale, u.fullscreen)
		return nil
	})
	return r
}

func SetScreenScale(scale float64) bool {
	u := currentUI
	if !u.isRunning() {
		panic("ui: Run is not called yet")
	}
	r := false
	_ = u.runOnMainThread(func() error {
		r = u.setScreenSize(u.width, u.height, scale, u.fullscreen)
		return nil
	})
	return r
}

func SetFullscreen(fullscreen bool) bool {
	u := currentUI
	if !u.isRunning() {
		panic("ui: Run is not called yet")
	}
	r := false
	_ = u.runOnMainThread(func() error {
		r = u.setScreenSize(u.width, u.height, u.scale, fullscreen)
		return nil
	})
	return r
}

func ScreenScale() float64 {
	u := currentUI
	if !u.isRunning() {
		return 0
	}
	s := 0.0
	_ = u.runOnMainThread(func() error {
		s = u.scale
		return nil
	})
	return s
}

func IsFullscreen() bool {
	u := currentUI
	if !u.isRunning() {
		return false
	}
	f := false
	_ = u.runOnMainThread(func() error {
		f = u.fullscreen
		return nil
	})
	return f
}

func SetCursorVisibility(visible bool) {
	// This can be called before Run: change the state asyncly.
	go func() {
		_ = currentUI.runOnMainThread(func() error {
			c := glfw.CursorNormal
			if !visible {
				c = glfw.CursorHidden
			}
			currentUI.window.SetInputMode(glfw.CursorMode, c)
			return nil
		})
	}()
}

func Run(width, height int, scale float64, title string, g GraphicsContext) error {
	u := currentUI
	// GLContext must be created before setting the screen size, which requires
	// swapping buffers.
	opengl.Init(currentUI.runOnMainThread)
	if err := u.runOnMainThread(func() error {
		m := glfw.GetPrimaryMonitor()
		v := m.GetVideoMode()
		if !u.setScreenSize(width, height, scale, false) {
			return errors.New("ui: Fail to set the screen size")
		}
		u.window.SetTitle(title)
		u.window.Show()

		w, h := u.glfwSize()
		x := (v.Width - w) / 2
		y := (v.Height - h) / 3
		x, y = adjustWindowPosition(x, y)
		u.window.SetPos(x, y)
		return nil
	}); err != nil {
		return err
	}
	return u.loop(g)
}

func (u *userInterface) glfwSize() (int, int) {
	if u.glfwScale == 0 {
		u.glfwScale = glfwScale()
	}
	return int(float64(u.width) * u.scale * u.glfwScale), int(float64(u.height) * u.scale * u.glfwScale)
}

func (u *userInterface) getScale() float64 {
	if !u.fullscreen {
		return u.scale
	}
	if u.fullscreenScale == 0 {
		m := glfw.GetPrimaryMonitor()
		v := m.GetVideoMode()
		sw := float64(v.Width) / float64(u.width)
		sh := float64(v.Height) / float64(u.height)
		s := sw
		if s > sh {
			s = sh
		}
		u.fullscreenScale = s
	}
	return u.fullscreenScale
}

func (u *userInterface) actualScreenScale() float64 {
	if u.deviceScale == 0 {
		u.deviceScale = deviceScale()
	}
	return u.getScale() * u.deviceScale
}

func (u *userInterface) pollEvents() {
	glfw.PollEvents()
	if u.glfwScale == 0 {
		u.glfwScale = glfwScale()
	}
	currentInput.update(u.window, u.getScale()*u.glfwScale)
}

func (u *userInterface) update(g GraphicsContext) error {
	shouldClose := false
	_ = u.runOnMainThread(func() error {
		shouldClose = u.window.ShouldClose()
		return nil
	})
	if shouldClose {
		return &RegularTermination{}
	}

	actualScale := 0.0
	_ = u.runOnMainThread(func() error {
		if !u.sizeChanged {
			return nil
		}
		u.sizeChanged = false
		actualScale = u.actualScreenScale()
		return nil
	})
	if 0 < actualScale {
		g.SetSize(u.width, u.height, actualScale)
	}

	_ = u.runOnMainThread(func() error {
		u.pollEvents()
		for u.window.GetAttrib(glfw.Focused) == 0 {
			// Wait for an arbitrary period to avoid busy loop.
			time.Sleep(time.Second / 60)
			u.pollEvents()
			if u.window.ShouldClose() {
				return nil
			}
		}
		return nil
	})
	if err := g.Update(); err != nil {
		return err
	}
	return nil
}

func (u *userInterface) loop(g GraphicsContext) error {
	defer func() {
		_ = u.runOnMainThread(func() error {
			glfw.Terminate()
			return nil
		})
	}()
	for {
		if err := u.update(g); err != nil {
			return err
		}
		// The bound framebuffer must be the default one (0) before swapping buffers.
		if err := opengl.GetContext().BindScreenFramebuffer(); err != nil {
			return err
		}
		_ = u.runOnMainThread(func() error {
			u.swapBuffers()
			return nil
		})
	}
}

func (u *userInterface) swapBuffers() {
	u.window.SwapBuffers()
}

func (u *userInterface) setScreenSize(width, height int, scale float64, fullscreen bool) bool {
	if u.width == width && u.height == height && u.scale == scale && u.fullscreen == fullscreen {
		return false
	}

	origScale := u.scale
	u.scale = scale

	// On Windows, giving a too small width doesn't call a callback (#165).
	// To prevent hanging up, return asap if the width is too small.
	// 252 is an arbitrary number and I guess this is small enough.
	// TODO: The same check should be in ui_js.go
	const minWindowWidth = 252
	if int(float64(width)*u.actualScreenScale()) < minWindowWidth {
		u.scale = origScale
		return false
	}
	u.width = width
	u.height = height

	// To make sure the current existing framebuffers are rendered,
	// swap buffers here before SetSize is called.
	u.swapBuffers()

	u.fullscreen = fullscreen

	window := u.window
	m := glfw.GetPrimaryMonitor()
	v := m.GetVideoMode()
	if u.fullscreen {
		u.origPosX, u.origPosY = window.GetPos()
		window.SetMonitor(m, 0, 0, v.Width, v.Height, v.RefreshRate)
	} else {
		x, y := window.GetPos()
		// TODO: y must be mirroed on macOS?
		window.SetMonitor(nil, x, y, 16, 16, v.RefreshRate)
		ch := make(chan struct{})
		window.SetFramebufferSizeCallback(func(_ *glfw.Window, width, height int) {
			window.SetFramebufferSizeCallback(nil)
			close(ch)
		})
		w, h := u.glfwSize()
		window.SetSize(w, h)
	event:
		for {
			glfw.PollEvents()
			select {
			case <-ch:
				break event
			default:
			}
		}
		// Reverted from fullscreen
		if u.origPosX >= 0 && u.origPosY >= 0 {
			window.SetPos(u.origPosX, u.origPosY)
		}
	}
	// TODO: Rename this variable?
	u.sizeChanged = true
	return true
}
<|endoftext|>


<commit_before>// Copyright 2018 The Prometheus 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 prom2json

import (
	"fmt"
	"io"
	"mime"
	"net/http"

	"github.com/matttproud/golang_protobuf_extensions/pbutil"
	"github.com/prometheus/common/expfmt"

	dto "github.com/prometheus/client_model/go"
)

const acceptHeader = `application/vnd.google.protobuf;proto=io.prometheus.client.MetricFamily;encoding=delimited;q=0.7,text/plain;version=0.0.4;q=0.3`

// Family mirrors the MetricFamily proto message.
type Family struct {
	//Time    time.Time
	Name    string        `json:"name"`
	Help    string        `json:"help"`
	Type    string        `json:"type"`
	Metrics []interface{} `json:"metrics,omitempty"` // Either metric or summary.
}

// Metric is for all "single value" metrics, i.e. Counter, Gauge, and Untyped.
type Metric struct {
	Labels map[string]string `json:"labels,omitempty"`
	Value  string            `json:"value"`
}

// Summary mirrors the Summary proto message.
type Summary struct {
	Labels    map[string]string `json:"labels,omitempty"`
	Quantiles map[string]string `json:"quantiles,omitempty"`
	Count     string            `json:"count"`
	Sum       string            `json:"sum"`
}

// Histogram mirrors the Histogram proto message.
type Histogram struct {
	Labels  map[string]string `json:"labels,omitempty"`
	Buckets map[string]string `json:"buckets,omitempty"`
	Count   string            `json:"count"`
	Sum     string            `json:"sum"`
}

// NewFamily consumes a MetricFamily and transforms it to the local Family type.
func NewFamily(dtoMF *dto.MetricFamily) *Family {
	mf := &Family{
		//Time:    time.Now(),
		Name:    dtoMF.GetName(),
		Help:    dtoMF.GetHelp(),
		Type:    dtoMF.GetType().String(),
		Metrics: make([]interface{}, len(dtoMF.Metric)),
	}
	for i, m := range dtoMF.Metric {
		if dtoMF.GetType() == dto.MetricType_SUMMARY {
			mf.Metrics[i] = Summary{
				Labels:    makeLabels(m),
				Quantiles: makeQuantiles(m),
				Count:     fmt.Sprint(m.GetSummary().GetSampleCount()),
				Sum:       fmt.Sprint(m.GetSummary().GetSampleSum()),
			}
		} else if dtoMF.GetType() == dto.MetricType_HISTOGRAM {
			mf.Metrics[i] = Histogram{
				Labels:  makeLabels(m),
				Buckets: makeBuckets(m),
				Count:   fmt.Sprint(m.GetHistogram().GetSampleCount()),
				Sum:     fmt.Sprint(m.GetSummary().GetSampleSum()),
			}
		} else {
			mf.Metrics[i] = Metric{
				Labels: makeLabels(m),
				Value:  fmt.Sprint(getValue(m)),
			}
		}
	}
	return mf
}

func getValue(m *dto.Metric) float64 {
	if m.Gauge != nil {
		return m.GetGauge().GetValue()
	}
	if m.Counter != nil {
		return m.GetCounter().GetValue()
	}
	if m.Untyped != nil {
		return m.GetUntyped().GetValue()
	}
	return 0.
}

func makeLabels(m *dto.Metric) map[string]string {
	result := map[string]string{}
	for _, lp := range m.Label {
		result[lp.GetName()] = lp.GetValue()
	}
	return result
}

func makeQuantiles(m *dto.Metric) map[string]string {
	result := map[string]string{}
	for _, q := range m.GetSummary().Quantile {
		result[fmt.Sprint(q.GetQuantile())] = fmt.Sprint(q.GetValue())
	}
	return result
}

func makeBuckets(m *dto.Metric) map[string]string {
	result := map[string]string{}
	for _, b := range m.GetHistogram().Bucket {
		result[fmt.Sprint(b.GetUpperBound())] = fmt.Sprint(b.GetCumulativeCount())
	}
	return result
}

// FetchMetricFamilies retrieves metrics from the provided URL, decodes them
// into MetricFamily proto messages, and sends them to the provided channel. It
// returns after all MetricFamilies have been sent. The provided http.Transport
// may be nil (in which case the default Transport is used).
func FetchMetricFamilies(url string, ch chan<- *dto.MetricFamily, t *http.Transport) error {
	req, err := http.NewRequest("GET", url, nil)
	if err != nil {
		return fmt.Errorf("creating GET request for URL %q failed: %v", url, err)
	}
	req.Header.Add("Accept", acceptHeader)
	client := http.Client{Transport: t}
	resp, err := client.Do(req)
	if err != nil {
		return fmt.Errorf("executing GET request for URL %q failed: %v", url, err)
	}
	defer resp.Body.Close()
	if resp.StatusCode != http.StatusOK {
		return fmt.Errorf("GET request for URL %q returned HTTP status %s", url, resp.Status)
	}
	return ParseResponse(resp, ch)
}

// ParseResponse consumes an http.Response and pushes it to the MetricFamily
// channel. It returns when all MetricFamilies are parsed and put on the
// channel.
func ParseResponse(resp *http.Response, ch chan<- *dto.MetricFamily) error {
	mediatype, params, err := mime.ParseMediaType(resp.Header.Get("Content-Type"))
	if err == nil && mediatype == "application/vnd.google.protobuf" &&
		params["encoding"] == "delimited" &&
		params["proto"] == "io.prometheus.client.MetricFamily" {
		defer close(ch)
		for {
			mf := &dto.MetricFamily{}
			if _, err = pbutil.ReadDelimited(resp.Body, mf); err != nil {
				if err == io.EOF {
					break
				}
				return fmt.Errorf("reading metric family protocol buffer failed: %v", err)
			}
			ch <- mf
		}
	} else {
		if err := ParseReader(resp.Body, ch); err != nil {
			return err
		}
	}
	return nil
}

// ParseReader consumes an io.Reader and pushes it to the MetricFamily
// channel. It returns when all MetricFamilies are parsed and put on the
// channel.
func ParseReader(in io.Reader, ch chan<- *dto.MetricFamily) error {
	defer close(ch)
	// We could do further content-type checks here, but the
	// fallback for now will anyway be the text format
	// version 0.0.4, so just go for it and see if it works.
	var parser expfmt.TextParser
	metricFamilies, err := parser.TextToMetricFamilies(in)
	if err != nil {
		return fmt.Errorf("reading text format failed: %v", err)
	}
	for _, mf := range metricFamilies {
		ch <- mf
	}
	return nil
}

// AddLabel allows to add key/value labels to an already existing Family.
func (f *Family) AddLabel(key, val string) {
	for i, item := range f.Metrics {
		switch item.(type) {
		case Metric:
			m := item.(Metric)
			m.Labels[key] = val
			f.Metrics[i] = m
		}
	}
}
<commit_msg>Fix a staticcheck warning<commit_after>// Copyright 2018 The Prometheus 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 prom2json

import (
	"fmt"
	"io"
	"mime"
	"net/http"

	"github.com/matttproud/golang_protobuf_extensions/pbutil"
	"github.com/prometheus/common/expfmt"

	dto "github.com/prometheus/client_model/go"
)

const acceptHeader = `application/vnd.google.protobuf;proto=io.prometheus.client.MetricFamily;encoding=delimited;q=0.7,text/plain;version=0.0.4;q=0.3`

// Family mirrors the MetricFamily proto message.
type Family struct {
	//Time    time.Time
	Name    string        `json:"name"`
	Help    string        `json:"help"`
	Type    string        `json:"type"`
	Metrics []interface{} `json:"metrics,omitempty"` // Either metric or summary.
}

// Metric is for all "single value" metrics, i.e. Counter, Gauge, and Untyped.
type Metric struct {
	Labels map[string]string `json:"labels,omitempty"`
	Value  string            `json:"value"`
}

// Summary mirrors the Summary proto message.
type Summary struct {
	Labels    map[string]string `json:"labels,omitempty"`
	Quantiles map[string]string `json:"quantiles,omitempty"`
	Count     string            `json:"count"`
	Sum       string            `json:"sum"`
}

// Histogram mirrors the Histogram proto message.
type Histogram struct {
	Labels  map[string]string `json:"labels,omitempty"`
	Buckets map[string]string `json:"buckets,omitempty"`
	Count   string            `json:"count"`
	Sum     string            `json:"sum"`
}

// NewFamily consumes a MetricFamily and transforms it to the local Family type.
func NewFamily(dtoMF *dto.MetricFamily) *Family {
	mf := &Family{
		//Time:    time.Now(),
		Name:    dtoMF.GetName(),
		Help:    dtoMF.GetHelp(),
		Type:    dtoMF.GetType().String(),
		Metrics: make([]interface{}, len(dtoMF.Metric)),
	}
	for i, m := range dtoMF.Metric {
		if dtoMF.GetType() == dto.MetricType_SUMMARY {
			mf.Metrics[i] = Summary{
				Labels:    makeLabels(m),
				Quantiles: makeQuantiles(m),
				Count:     fmt.Sprint(m.GetSummary().GetSampleCount()),
				Sum:       fmt.Sprint(m.GetSummary().GetSampleSum()),
			}
		} else if dtoMF.GetType() == dto.MetricType_HISTOGRAM {
			mf.Metrics[i] = Histogram{
				Labels:  makeLabels(m),
				Buckets: makeBuckets(m),
				Count:   fmt.Sprint(m.GetHistogram().GetSampleCount()),
				Sum:     fmt.Sprint(m.GetSummary().GetSampleSum()),
			}
		} else {
			mf.Metrics[i] = Metric{
				Labels: makeLabels(m),
				Value:  fmt.Sprint(getValue(m)),
			}
		}
	}
	return mf
}

func getValue(m *dto.Metric) float64 {
	if m.Gauge != nil {
		return m.GetGauge().GetValue()
	}
	if m.Counter != nil {
		return m.GetCounter().GetValue()
	}
	if m.Untyped != nil {
		return m.GetUntyped().GetValue()
	}
	return 0.
}

func makeLabels(m *dto.Metric) map[string]string {
	result := map[string]string{}
	for _, lp := range m.Label {
		result[lp.GetName()] = lp.GetValue()
	}
	return result
}

func makeQuantiles(m *dto.Metric) map[string]string {
	result := map[string]string{}
	for _, q := range m.GetSummary().Quantile {
		result[fmt.Sprint(q.GetQuantile())] = fmt.Sprint(q.GetValue())
	}
	return result
}

func makeBuckets(m *dto.Metric) map[string]string {
	result := map[string]string{}
	for _, b := range m.GetHistogram().Bucket {
		result[fmt.Sprint(b.GetUpperBound())] = fmt.Sprint(b.GetCumulativeCount())
	}
	return result
}

// FetchMetricFamilies retrieves metrics from the provided URL, decodes them
// into MetricFamily proto messages, and sends them to the provided channel. It
// returns after all MetricFamilies have been sent. The provided http.Transport
// may be nil (in which case the default Transport is used).
func FetchMetricFamilies(url string, ch chan<- *dto.MetricFamily, t *http.Transport) error {
	req, err := http.NewRequest("GET", url, nil)
	if err != nil {
		return fmt.Errorf("creating GET request for URL %q failed: %v", url, err)
	}
	req.Header.Add("Accept", acceptHeader)
	client := http.Client{Transport: t}
	resp, err := client.Do(req)
	if err != nil {
		return fmt.Errorf("executing GET request for URL %q failed: %v", url, err)
	}
	defer resp.Body.Close()
	if resp.StatusCode != http.StatusOK {
		return fmt.Errorf("GET request for URL %q returned HTTP status %s", url, resp.Status)
	}
	return ParseResponse(resp, ch)
}

// ParseResponse consumes an http.Response and pushes it to the MetricFamily
// channel. It returns when all MetricFamilies are parsed and put on the
// channel.
func ParseResponse(resp *http.Response, ch chan<- *dto.MetricFamily) error {
	mediatype, params, err := mime.ParseMediaType(resp.Header.Get("Content-Type"))
	if err == nil && mediatype == "application/vnd.google.protobuf" &&
		params["encoding"] == "delimited" &&
		params["proto"] == "io.prometheus.client.MetricFamily" {
		defer close(ch)
		for {
			mf := &dto.MetricFamily{}
			if _, err = pbutil.ReadDelimited(resp.Body, mf); err != nil {
				if err == io.EOF {
					break
				}
				return fmt.Errorf("reading metric family protocol buffer failed: %v", err)
			}
			ch <- mf
		}
	} else {
		if err := ParseReader(resp.Body, ch); err != nil {
			return err
		}
	}
	return nil
}

// ParseReader consumes an io.Reader and pushes it to the MetricFamily
// channel. It returns when all MetricFamilies are parsed and put on the
// channel.
func ParseReader(in io.Reader, ch chan<- *dto.MetricFamily) error {
	defer close(ch)
	// We could do further content-type checks here, but the
	// fallback for now will anyway be the text format
	// version 0.0.4, so just go for it and see if it works.
	var parser expfmt.TextParser
	metricFamilies, err := parser.TextToMetricFamilies(in)
	if err != nil {
		return fmt.Errorf("reading text format failed: %v", err)
	}
	for _, mf := range metricFamilies {
		ch <- mf
	}
	return nil
}

// AddLabel allows to add key/value labels to an already existing Family.
func (f *Family) AddLabel(key, val string) {
	for i, item := range f.Metrics {
		switch m := item.(type) {
		case Metric:
			m.Labels[key] = val
			f.Metrics[i] = m
		}
	}
}
<|endoftext|>