<commit_before>// Package setup_instruments manages the configuration of
// performance_schema.setup_instruments.
package setup_instruments

import (
	"database/sql"
	"fmt"
	"log"

	"github.com/sjmudd/ps-top/logger"
)

// List of expected errors to an UPDATE statement.  Checks are only
// done against the error numbers.
var expectedErrors = []string{
	"Error 1142: UPDATE command denied to user 'myuser'@'10.11.12.13' for table 'setup_instruments'",
	"Error 1290: The MySQL server is running with the --read-only option so it cannot execute this statement",
}

// Row contains one row of performance_schema.setup_instruments
type Row struct {
	name    string
	enabled string
	timed   string
}

// Rows contains a slice of Row
type Rows []Row

// SetupInstruments "object"
type SetupInstruments struct {
	updateTried     bool
	updateSucceeded bool
	rows            Rows
	dbh             *sql.DB
}

// NewSetupInstruments returns a pointer to a newly initialised
// SetupInstruments.
func NewSetupInstruments(dbh *sql.DB) *SetupInstruments {
	return &SetupInstruments{dbh: dbh}
}

// EnableMonitoring enables mutex and stage monitoring
func (si *SetupInstruments) EnableMonitoring() {
	si.EnableMutexMonitoring()
	si.EnableStageMonitoring()
}

// EnableStageMonitoring change settings to monitor stage/sql/%
func (si *SetupInstruments) EnableStageMonitoring() {
	logger.Println("EnableStageMonitoring")
	sqlMatch := "stage/sql/%"
	sqlSelect := "SELECT NAME, ENABLED, TIMED FROM setup_instruments WHERE NAME LIKE '" + sqlMatch + "' AND 'YES' NOT IN (ENABLED,TIMED)"

	collecting := "Collecting setup_instruments stage/sql configuration settings"
	updating := "Updating setup_instruments configuration for: stage/sql"

	si.Configure(sqlSelect, collecting, updating)
	logger.Println("EnableStageMonitoring finishes")
}

// EnableMutexMonitoring changes settings to monitor wait/synch/mutex/%
func (si *SetupInstruments) EnableMutexMonitoring() {
	logger.Println("EnableMutexMonitoring")
	sqlMatch := "wait/synch/mutex/%"
	sqlSelect := "SELECT NAME, ENABLED, TIMED FROM setup_instruments WHERE NAME LIKE '" + sqlMatch + "' AND 'YES' NOT IN (ENABLED,TIMED)"
	collecting := "Collecting setup_instruments wait/synch/mutex configuration settings"
	updating := "Updating setup_instruments configuration for: wait/synch/mutex"

	si.Configure(sqlSelect, collecting, updating)
	logger.Println("EnableMutexMonitoring finishes")
}

// isExpectedError returns true if the error is in the expected list of errors
// - we only match on the error number
func isExpectedError(actualError string) bool {
	logger.Println("checking if", actualError, "is in", expectedErrors)
	e := actualError[0:11]
	expected := false
	for _, val := range expectedErrors {
		if e == val[0:11] {
			logger.Println("found expected error", val[0:11])
			expected = true
			break
		}
	}
	logger.Println("returning", expected)
	return expected
}

// Configure updates setup_instruments so we can monitor tables correctly.
func (si *SetupInstruments) Configure(sqlSelect string, collecting, updating string) {
	const updateSQL = "UPDATE setup_instruments SET enabled = ?, TIMED = ? WHERE NAME = ?"

	logger.Println(fmt.Sprintf("Configure(%q,%q,%q)", sqlSelect, collecting, updating))
	// skip if we've tried and failed
	if si.updateTried && !si.updateSucceeded {
		logger.Println("SetupInstruments.Configure() - Skipping further configuration")
		return
	}

	// setup the old values in case they're not set
	if si.rows == nil {
		si.rows = make([]Row, 0, 500)
	}

	logger.Println(collecting)

	logger.Println("dbh.query", sqlSelect)
	rows, err := si.dbh.Query(sqlSelect)
	if err != nil {
		log.Fatal(err)
	}
	defer rows.Close()

	count := 0
	for rows.Next() {
		var r Row
		if err := rows.Scan(
			&r.name,
			&r.enabled,
			&r.timed); err != nil {
			log.Fatal(err)
		}
		si.rows = append(si.rows, r)
		count++
	}
	if err := rows.Err(); err != nil {
		log.Fatal(err)
	}
	logger.Println("- found", count, "rows whose configuration need changing")

	// update the rows which need to be set - do multiple updates but I don't care
	logger.Println(updating)

	logger.Println("Preparing statement:", updateSQL)
	si.updateTried = true
	logger.Println("dbh.Prepare", updateSQL)
	stmt, err := si.dbh.Prepare(updateSQL)
	if err != nil {
		logger.Println("- prepare gave error:", err.Error())
		if !isExpectedError(err.Error()) {
			log.Fatal("Not expected error so giving up")
		} else {
			logger.Println("- expected error so not running statement")
		}
	} else {
		logger.Println("Prepare succeeded, trying to update", len(si.rows), "row(s)")
		count = 0
		for i := range si.rows {
			logger.Println("- changing row:", si.rows[i].name)
			logger.Println("stmt.Exec", "YES", "YES", si.rows[i].name)
			if res, err := stmt.Exec("YES", "YES", si.rows[i].name); err == nil {
				logger.Println("update succeeded")
				si.updateSucceeded = true
				c, _ := res.RowsAffected()
				count += int(c)
			} else {
				si.updateSucceeded = false
				if isExpectedError(err.Error()) {
					logger.Println("Insufficient privileges to UPDATE setup_instruments: " + err.Error())
					logger.Println("Not attempting further updates")
					return
				}
				log.Fatal(err)
			}
		}
		if si.updateSucceeded {
			logger.Println(count, "rows changed in p_s.setup_instruments")
		}
		stmt.Close()
	}
	logger.Println("Configure() returns updateTried", si.updateTried, ", updateSucceeded", si.updateSucceeded)
}

// RestoreConfiguration restores setup_instruments rows to their previous settings (if changed previously).
func (si *SetupInstruments) RestoreConfiguration() {
	logger.Println("RestoreConfiguration()")
	// If the previous update didn't work then don't try to restore
	if !si.updateSucceeded {
		logger.Println("Not restoring p_s.setup_instruments to original settings as initial configuration attempt failed")
		return
	}
	logger.Println("Restoring p_s.setup_instruments to its original settings")

	// update the rows which need to be set - do multiple updates but I don't care
	updateSQL := "UPDATE setup_instruments SET enabled = ?, TIMED = ? WHERE NAME = ?"
	logger.Println("dbh.Prepare(", updateSQL, ")")
	stmt, err := si.dbh.Prepare(updateSQL)
	if err != nil {
		log.Fatal(err)
	}
	count := 0
	for i := range si.rows {
		logger.Println("stmt.Exec(", si.rows[i].enabled, si.rows[i].timed, si.rows[i].name, ")")
		if _, err := stmt.Exec(si.rows[i].enabled, si.rows[i].timed, si.rows[i].name); err != nil {
			log.Fatal(err)
		}
		count++
	}
	logger.Println("stmt.Close()")
	stmt.Close()
	logger.Println(count, "rows changed in p_s.setup_instruments")
}
<commit_msg>Remove unneeded Rows == []Row type<commit_after>// Package setup_instruments manages the configuration of
// performance_schema.setup_instruments.
package setup_instruments

import (
	"database/sql"
	"fmt"
	"log"

	"github.com/sjmudd/ps-top/logger"
)

// List of expected errors to an UPDATE statement.  Checks are only
// done against the error numbers.
var expectedErrors = []string{
	"Error 1142: UPDATE command denied to user 'myuser'@'10.11.12.13' for table 'setup_instruments'",
	"Error 1290: The MySQL server is running with the --read-only option so it cannot execute this statement",
}

// Row contains one row of performance_schema.setup_instruments
type Row struct {
	name    string
	enabled string
	timed   string
}

// SetupInstruments "object"
type SetupInstruments struct {
	updateTried     bool
	updateSucceeded bool
	rows            []Row
	dbh             *sql.DB
}

// NewSetupInstruments returns a pointer to a newly initialised
// SetupInstruments.
func NewSetupInstruments(dbh *sql.DB) *SetupInstruments {
	return &SetupInstruments{dbh: dbh}
}

// EnableMonitoring enables mutex and stage monitoring
func (si *SetupInstruments) EnableMonitoring() {
	si.EnableMutexMonitoring()
	si.EnableStageMonitoring()
}

// EnableStageMonitoring change settings to monitor stage/sql/%
func (si *SetupInstruments) EnableStageMonitoring() {
	logger.Println("EnableStageMonitoring")
	sqlMatch := "stage/sql/%"
	sqlSelect := "SELECT NAME, ENABLED, TIMED FROM setup_instruments WHERE NAME LIKE '" + sqlMatch + "' AND 'YES' NOT IN (ENABLED,TIMED)"

	collecting := "Collecting setup_instruments stage/sql configuration settings"
	updating := "Updating setup_instruments configuration for: stage/sql"

	si.Configure(sqlSelect, collecting, updating)
	logger.Println("EnableStageMonitoring finishes")
}

// EnableMutexMonitoring changes settings to monitor wait/synch/mutex/%
func (si *SetupInstruments) EnableMutexMonitoring() {
	logger.Println("EnableMutexMonitoring")
	sqlMatch := "wait/synch/mutex/%"
	sqlSelect := "SELECT NAME, ENABLED, TIMED FROM setup_instruments WHERE NAME LIKE '" + sqlMatch + "' AND 'YES' NOT IN (ENABLED,TIMED)"
	collecting := "Collecting setup_instruments wait/synch/mutex configuration settings"
	updating := "Updating setup_instruments configuration for: wait/synch/mutex"

	si.Configure(sqlSelect, collecting, updating)
	logger.Println("EnableMutexMonitoring finishes")
}

// isExpectedError returns true if the error is in the expected list of errors
// - we only match on the error number
func isExpectedError(actualError string) bool {
	logger.Println("checking if", actualError, "is in", expectedErrors)
	e := actualError[0:11]
	expected := false
	for _, val := range expectedErrors {
		if e == val[0:11] {
			logger.Println("found expected error", val[0:11])
			expected = true
			break
		}
	}
	logger.Println("returning", expected)
	return expected
}

// Configure updates setup_instruments so we can monitor tables correctly.
func (si *SetupInstruments) Configure(sqlSelect string, collecting, updating string) {
	const updateSQL = "UPDATE setup_instruments SET enabled = ?, TIMED = ? WHERE NAME = ?"

	logger.Println(fmt.Sprintf("Configure(%q,%q,%q)", sqlSelect, collecting, updating))
	// skip if we've tried and failed
	if si.updateTried && !si.updateSucceeded {
		logger.Println("SetupInstruments.Configure() - Skipping further configuration")
		return
	}

	// setup the old values in case they're not set
	if si.rows == nil {
		si.rows = make([]Row, 0, 500)
	}

	logger.Println(collecting)

	logger.Println("dbh.query", sqlSelect)
	rows, err := si.dbh.Query(sqlSelect)
	if err != nil {
		log.Fatal(err)
	}
	defer rows.Close()

	count := 0
	for rows.Next() {
		var r Row
		if err := rows.Scan(
			&r.name,
			&r.enabled,
			&r.timed); err != nil {
			log.Fatal(err)
		}
		si.rows = append(si.rows, r)
		count++
	}
	if err := rows.Err(); err != nil {
		log.Fatal(err)
	}
	logger.Println("- found", count, "rows whose configuration need changing")

	// update the rows which need to be set - do multiple updates but I don't care
	logger.Println(updating)

	logger.Println("Preparing statement:", updateSQL)
	si.updateTried = true
	logger.Println("dbh.Prepare", updateSQL)
	stmt, err := si.dbh.Prepare(updateSQL)
	if err != nil {
		logger.Println("- prepare gave error:", err.Error())
		if !isExpectedError(err.Error()) {
			log.Fatal("Not expected error so giving up")
		} else {
			logger.Println("- expected error so not running statement")
		}
	} else {
		logger.Println("Prepare succeeded, trying to update", len(si.rows), "row(s)")
		count = 0
		for i := range si.rows {
			logger.Println("- changing row:", si.rows[i].name)
			logger.Println("stmt.Exec", "YES", "YES", si.rows[i].name)
			if res, err := stmt.Exec("YES", "YES", si.rows[i].name); err == nil {
				logger.Println("update succeeded")
				si.updateSucceeded = true
				c, _ := res.RowsAffected()
				count += int(c)
			} else {
				si.updateSucceeded = false
				if isExpectedError(err.Error()) {
					logger.Println("Insufficient privileges to UPDATE setup_instruments: " + err.Error())
					logger.Println("Not attempting further updates")
					return
				}
				log.Fatal(err)
			}
		}
		if si.updateSucceeded {
			logger.Println(count, "rows changed in p_s.setup_instruments")
		}
		stmt.Close()
	}
	logger.Println("Configure() returns updateTried", si.updateTried, ", updateSucceeded", si.updateSucceeded)
}

// RestoreConfiguration restores setup_instruments rows to their previous settings (if changed previously).
func (si *SetupInstruments) RestoreConfiguration() {
	logger.Println("RestoreConfiguration()")
	// If the previous update didn't work then don't try to restore
	if !si.updateSucceeded {
		logger.Println("Not restoring p_s.setup_instruments to original settings as initial configuration attempt failed")
		return
	}
	logger.Println("Restoring p_s.setup_instruments to its original settings")

	// update the rows which need to be set - do multiple updates but I don't care
	updateSQL := "UPDATE setup_instruments SET enabled = ?, TIMED = ? WHERE NAME = ?"
	logger.Println("dbh.Prepare(", updateSQL, ")")
	stmt, err := si.dbh.Prepare(updateSQL)
	if err != nil {
		log.Fatal(err)
	}
	count := 0
	for i := range si.rows {
		logger.Println("stmt.Exec(", si.rows[i].enabled, si.rows[i].timed, si.rows[i].name, ")")
		if _, err := stmt.Exec(si.rows[i].enabled, si.rows[i].timed, si.rows[i].name); err != nil {
			log.Fatal(err)
		}
		count++
	}
	logger.Println("stmt.Close()")
	stmt.Close()
	logger.Println(count, "rows changed in p_s.setup_instruments")
}
<|endoftext|>


<commit_before>package multiverse

import "strings"

type nGram struct {
	grams []string
	size  int
}

func newNGram(phrase string, size int) nGram {
	var n = nGram{nil, size}
	words := strings.Split(phrase, " ")

	for _, word := range words {
		for i := size; i <= len(word); i++ {
			n.grams = append(n.grams, word[i-size:i])
		}
	}
	return n
}

func (n nGram) Similarity(phrase string) float32 {
	result := float32(0)
	m := newNGram(phrase, n.size)

	for _, myGram := range n.grams {
		for _, oGram := range m.grams {
			if myGram == oGram {
				result += float32(n.size)
			}
		}
	}

	return result
}
<commit_msg>Avoided extra allocations by doing nGram similarity on the stack.<commit_after>package multiverse

import "strings"

type nGram struct {
	grams []string
	size  int
}

func newNGram(phrase string, size int) nGram {
	var n = nGram{nil, size}
	words := strings.Split(phrase, " ")

	for _, word := range words {
		for i := size; i <= len(word); i++ {
			n.grams = append(n.grams, word[i-size:i])
		}
	}
	return n
}

func (n nGram) Similarity(phrase string) float32 {
	result := float32(0)

	for i := 0; i < len(phrase)-n.size; i++ {
		for _, myGram := range n.grams {
			if phrase[i:i+n.size] == myGram {
				result += float32(n.size)
			}
		}
	}

	return result
}
<|endoftext|>


<commit_before>package cfutil

import (
	"errors"
	"regexp"

	cfenv "github.com/cloudfoundry-community/go-cfenv"
	vault "github.com/hashicorp/vault/api"
)

var v1Regex = regexp.MustCompile(`/v1/`)

type VaultClient struct {
	vault.Client
	Endpoint           string
	RoleID             string
	SecretID           string
	ServiceSecretPath  string
	ServiceTransitPath string
	SpaceSecretPath    string
	OrgSecretPath      string
	Secret             *vault.Secret
}

func (v *VaultClient) Login() (err error) {
	path := "auth/approle/login"
	options := map[string]interface{}{
		"role_id":   v.RoleID,
		"secret_id": v.SecretID,
	}
	v.Secret, err = v.Logical().Write(path, options)
	v.SetToken(v.Secret.Auth.ClientToken)
	return err
}

func (v *VaultClient) ReadSpaceString(path string) (string, error) {
	return v.ReadString(v.SpaceSecretPath, path)
}

func (v *VaultClient) ReadOrgString(path string) (string, error) {
	return v.ReadString(v.OrgSecretPath, path)
}

func (v *VaultClient) ReadString(prefix, path string) (string, error) {
	err := v.Login()
	if err != nil {
		return "", err
	}
	secret, err := v.Logical().Read(prefix + "/" + path)
	if err != nil {
		return "", err
	}
	str, ok := secret.Data["value"].(string)
	if !ok {
		return "", errors.New("Missing value string")
	}
	return str, nil
}

func NewVaultClient(serviceName string) (*VaultClient, error) {
	appEnv, _ := Current()
	service := &cfenv.Service{}
	err := errors.New("")
	if serviceName != "" {
		service, err = serviceByName(appEnv, serviceName)
	} else {
		service, err = serviceByTag(appEnv, "Vault")
	}
	if err != nil {
		return nil, err
	}

	var vaultClient VaultClient

	if str, ok := service.Credentials["role_id"].(string); ok {
		vaultClient.RoleID = str
	}
	if str, ok := service.Credentials["secret_id"].(string); ok {
		vaultClient.SecretID = str
	}
	if str, ok := service.Credentials["org_secret_path"].(string); ok {
		vaultClient.OrgSecretPath = v1Regex.ReplaceAllString(str, "")
	}
	if str, ok := service.Credentials["service_secret_path"].(string); ok {
		vaultClient.ServiceSecretPath = v1Regex.ReplaceAllString(str, "")
	}
	if str, ok := service.Credentials["endpoint"].(string); ok {
		vaultClient.Endpoint = str
	}
	if str, ok := service.Credentials["space_secret_path"].(string); ok {
		vaultClient.SpaceSecretPath = v1Regex.ReplaceAllString(str, "")
	}
	if str, ok := service.Credentials["service_transit_path"].(string); ok {
		vaultClient.ServiceTransitPath = v1Regex.ReplaceAllString(str, "")
	}

	client, err := vault.NewClient(&vault.Config{
		Address: vaultClient.Endpoint,
	})
	if err != nil {
		return nil, err
	}
	vaultClient.Client = *client
	err = vaultClient.Login()
	if err != nil {
		return nil, err
	}
	return &vaultClient, vaultClient.Login()
}
<commit_msg>Better error reporting<commit_after>package cfutil

import (
	"errors"
	"fmt"
	"regexp"

	cfenv "github.com/cloudfoundry-community/go-cfenv"
	vault "github.com/hashicorp/vault/api"
)

var v1Regex = regexp.MustCompile(`/v1/`)

type VaultClient struct {
	vault.Client
	Endpoint           string
	RoleID             string
	SecretID           string
	ServiceSecretPath  string
	ServiceTransitPath string
	SpaceSecretPath    string
	OrgSecretPath      string
	Secret             *vault.Secret
}

func (v *VaultClient) Login() (err error) {
	path := "auth/approle/login"
	options := map[string]interface{}{
		"role_id":   v.RoleID,
		"secret_id": v.SecretID,
	}
	v.Secret, err = v.Logical().Write(path, options)
	v.SetToken(v.Secret.Auth.ClientToken)
	return err
}

func (v *VaultClient) ReadSpaceString(path string) (string, error) {
	return v.ReadString(v.SpaceSecretPath, path)
}

func (v *VaultClient) ReadOrgString(path string) (string, error) {
	return v.ReadString(v.OrgSecretPath, path)
}

func (v *VaultClient) ReadString(prefix, path string) (string, error) {
	err := v.Login()
	if err != nil {
		return "", err
	}
	location := prefix + "/" + path
	secret, err := v.Logical().Read(location)
	if err != nil {
		return "", err
	}
	str, ok := secret.Data["value"].(string)
	if !ok {
		return "", fmt.Errorf("Missing value on path %s", location)
	}
	return str, nil
}

func NewVaultClient(serviceName string) (*VaultClient, error) {
	appEnv, _ := Current()
	service := &cfenv.Service{}
	err := errors.New("")
	if serviceName != "" {
		service, err = serviceByName(appEnv, serviceName)
	} else {
		service, err = serviceByTag(appEnv, "Vault")
	}
	if err != nil {
		return nil, err
	}

	var vaultClient VaultClient

	if str, ok := service.Credentials["role_id"].(string); ok {
		vaultClient.RoleID = str
	}
	if str, ok := service.Credentials["secret_id"].(string); ok {
		vaultClient.SecretID = str
	}
	if str, ok := service.Credentials["org_secret_path"].(string); ok {
		vaultClient.OrgSecretPath = v1Regex.ReplaceAllString(str, "")
	}
	if str, ok := service.Credentials["service_secret_path"].(string); ok {
		vaultClient.ServiceSecretPath = v1Regex.ReplaceAllString(str, "")
	}
	if str, ok := service.Credentials["endpoint"].(string); ok {
		vaultClient.Endpoint = str
	}
	if str, ok := service.Credentials["space_secret_path"].(string); ok {
		vaultClient.SpaceSecretPath = v1Regex.ReplaceAllString(str, "")
	}
	if str, ok := service.Credentials["service_transit_path"].(string); ok {
		vaultClient.ServiceTransitPath = v1Regex.ReplaceAllString(str, "")
	}

	client, err := vault.NewClient(&vault.Config{
		Address: vaultClient.Endpoint,
	})
	if err != nil {
		return nil, err
	}
	vaultClient.Client = *client
	err = vaultClient.Login()
	if err != nil {
		return nil, err
	}
	return &vaultClient, vaultClient.Login()
}
<|endoftext|>


<commit_before>package main
<commit_msg>Modifed tests to now call AddQuery, AddLimit, etc on factualRead.  Added another test to, test out the generic Get method<commit_after>package factual

import (
	"fmt"
	"strings"
	"testing"
)

func TestToken(t *testing.T) {
	fR := NewToken("Pbu7jRdBErgLW07g9c25JtGcwwt1KmpoxRTfFL3x", "vC4AgocPBhxe0GFkTsetoiuEAJEgqz6MCbAnXEoO")

	nR, err := NewRead(fR, "restaurants-us")

	if err != nil {
		fmt.Println(err)
		return
	}

	nR.AddQuery("pizza")
	nR.AddLimit(5)
	nR.AddSelect("factual_id")

	fmt.Println("frData", nR.GetReadURL())

	s, err := nR.Get()

	if err != nil {
		fmt.Println(err)
		return
	}

	if strings.Contains(s, "error") {
		t.Error("response returned an error")
	}

	fmt.Println(s)

}

func TestSchema(t *testing.T) {
	fR := NewToken("Pbu7jRdBErgLW07g9c25JtGcwwt1KmpoxRTfFL3x", "vC4AgocPBhxe0GFkTsetoiuEAJEgqz6MCbAnXEoO")
	_, err := fR.Get("http://api.v3.factual.com/t/restaurants-us/schema")
	if err != nil {
		t.Error(err)
		return
	}

	//fmt.Println(s)

}
<|endoftext|>


<commit_before>package junos

import (
	"encoding/xml"
	"errors"
	"strings"

	"github.com/Juniper/go-netconf/netconf"
)

// ArpTable contains the ARP table on the device.
type ArpTable struct {
	Count   string     `xml:"arp-entry-count"`
	Entries []ArpEntry `xml:"arp-table-entry"`
}

// ArpEntry holds each individual ARP entry.
type ArpEntry struct {
	MACAddress string `xml:"mac-address"`
	IPAddress  string `xml:"ip-address"`
	Interface  string `xml:"interface-name"`
}

// RoutingTable contains every routing table on the device.
type RoutingTable struct {
	RouteTables []RouteTable `xml:"route-table"`
}

// RouteTable holds all the route information for each table.
type RouteTable struct {
	Name           string  `xml:"table-name"`
	TotalRoutes    int     `xml:"total-route-count"`
	ActiveRoutes   int     `xml:"active-route-count"`
	HolddownRoutes int     `xml:"holddown-route-count"`
	HiddenRoutes   int     `xml:"hidden-routes"`
	Entries        []Route `xml:"rt"`
}

// Route holds information about each individual route.
type Route struct {
	Destination           string `xml:"rt-destination"`
	Active                string `xml:"rt-entry>active-tag"`
	Protocol              string `xml:"rt-entry>protocol-name"`
	Preference            int    `xml:"rt-entry>preference"`
	Age                   string `xml:"rt-entry>age"`
	NextHop               string `xml:"rt-entry>nh>to,omitempty"`
	NextHopInterface      string `xml:"rt-entry>nh>via,omitempty"`
	NextHopTable          string `xml:"rt-entry>nh>nh-table,omitempty"`
	NextHopLocalInterface string `xml:"rt-entry>nh>nh-local-interface,omitempty"`
}

// Interfaces contains information about every interface on the device.
type Interfaces struct {
	Entries []PhysicalInterface `xml:"physical-interface"`
}

// PhysicalInterface contains information about each individual physical interface.
type PhysicalInterface struct {
	Name                    string             `xml:"name"`
	AdminStatus             string             `xml:"admin-status"`
	OperStatus              string             `xml:"oper-status"`
	LocalIndex              int                `xml:"local-index"`
	SNMPIndex               int                `xml:"snmp-index"`
	LinkLevelType           string             `xml:"link-level-type"`
	MTU                     string             `xml:"mtu"`
	LinkMode                string             `xml:"link-mode"`
	Speed                   string             `xml:"speed"`
	FlowControl             string             `xml:"if-flow-control"`
	AutoNegotiation         string             `xml:"if-auto-negotiation"`
	HardwarePhysicalAddress string             `xml:"hardware-physical-address"`
	Flapped                 string             `xml:"interface-flapped"`
	InputBps                int                `xml:"traffic-statistics>input-bps"`
	InputPps                int                `xml:"traffic-statistics>input-pps"`
	OutputBps               int                `xml:"traffic-statistics>output-bps"`
	OutputPps               int                `xml:"traffic-statistics>output-pps"`
	LogicalInterfaces       []LogicalInterface `xml:"logical-interface"`
}

// LogicalInterface contains information about the logical interfaces tied to a physical interface.
type LogicalInterface struct {
	Name               string `xml:"name"`
	MTU                string `xml:"address-family>mtu"`
	IPAddress          string `xml:"address-family>interface-address>ifa-local"`
	LocalIndex         int    `xml:"local-index"`
	SNMPIndex          int    `xml:"snmp-index"`
	Encapsulation      string `xml:"encapsulation"`
	LAGInputPackets    int    `xml:"lag-traffic-statistics>lag-bundle>input-packets"`
	LAGInputPps        int    `xml:"lag-traffic-statistics>lag-bundle>input-pps"`
	LAGInputBytes      int    `xml:"lag-traffic-statistics>lag-bundle>input-bytes"`
	LAGInputBps        int    `xml:"lag-traffic-statistics>lag-bundle>input-bps"`
	LAGOutputPackets   int    `xml:"lag-traffic-statistics>lag-bundle>output-packets"`
	LAGOutputPps       int    `xml:"lag-traffic-statistics>lag-bundle>output-pps"`
	LAGOutputBytes     int    `xml:"lag-traffic-statistics>lag-bundle>output-bytes"`
	LAGOutputBps       int    `xml:"lag-traffic-statistics>lag-bundle>output-bps"`
	ZoneName           string `xml:"logical-interface-zone-name"`
	InputPackets       int    `xml:"traffic-statistics>input-packets"`
	OutputPackets      int    `xml:"traffic-statistics>output-packets"`
	AddressFamily      string `xml:"address-family>address-family-name"`
	AggregatedEthernet string `xml:"address-family>ae-bundle-name,omitempty"`
}

// Vlans contains all of the VLAN information on the device.
type Vlans struct {
	Entries []Vlan `xml:"l2ng-l2ald-vlan-instance-group"`
}

// Vlan contains information about each individual VLAN.
type Vlan struct {
	Name             string   `xml:"l2ng-l2rtb-vlan-name"`
	Tag              int      `xml:"l2ng-l2rtb-vlan-tag"`
	MemberInterfaces []string `xml:"l2ng-l2rtb-vlan-member>l2ng-l2rtb-vlan-member-interface"`
}

// EthernetSwitchingTable contains the ethernet-switching table on the device.
type EthernetSwitchingTable struct {
	Entries []L2MACEntry `xml:"l2ng-l2ald-mac-entry-vlan"`
}

// L2MACEntry contains information about every MAC address on each VLAN.
type L2MACEntry struct {
	GlobalMACCount  int        `xml:"mac-count-global"`
	LearnedMACCount int        `xml:"learnt-mac-count"`
	RoutingInstance string     `xml:"l2ng-l2-mac-routing-instance"`
	VlanID          int        `xml:"l2ng-l2-vlan-id"`
	MACEntries      []MACEntry `xml:"l2ng-mac-entry"`
}

// MACEntry contains information about each individual MAC address. Flags are: S - static MAC, D - dynamic MAC,
// L - locally learned, P - persistent static, SE - statistics enabled, NM - non configured MAC, R - remote PE MAC,
// O - ovsdb MAC.
type MACEntry struct {
	VlanName         string `xml:"l2ng-l2-mac-vlan-name"`
	MACAddress       string `xml:"l2ng-l2-mac-address"`
	Age              string `xml:"l2ng-l2-mac-age"`
	Flags            string `xml:"l2ng-l2-mac-flags"`
	LogicalInterface string `xml:"l2ng-l2-mac-logical-interface"`
}

// HardwareInventory contains all the hardware information about the device.
type HardwareInventory struct {
	Chassis []Chassis `xml:"chassis"`
}

type srxHardwareInventory struct {
	Chassis []Chassis `xml:"multi-routing-engine-item>chassis-inventory>chassis"`
}

// Chassis contains all of the hardware information for each chassis, such as a clustered pair of SRX's or a
// virtual-chassis configuration.
type Chassis struct {
	Name         string   `xml:"name"`
	SerialNumber string   `xml:"serial-number"`
	Description  string   `xml:"description"`
	Modules      []Module `xml:"chassis-module"`
}

// Module contains information about each individual module.
type Module struct {
	Name         string      `xml:"name"`
	Version      string      `xml:"version,omitempty"`
	PartNumber   string      `xml:"part-number"`
	SerialNumber string      `xml:"serial-number"`
	Description  string      `xml:"description"`
	CLEICode     string      `xml:"clei-code"`
	ModuleNumber string      `xml:"module-number"`
	SubModules   []SubModule `xml:"chassis-sub-module"`
}

// SubModule contains information about each individual sub-module.
type SubModule struct {
	Name          string         `xml:"name"`
	Version       string         `xml:"version,omitempty"`
	PartNumber    string         `xml:"part-number"`
	SerialNumber  string         `xml:"serial-number"`
	Description   string         `xml:"description"`
	CLEICode      string         `xml:"clei-code"`
	ModuleNumber  string         `xml:"module-number"`
	SubSubModules []SubSubModule `xml:"chassis-sub-sub-module"`
}

// SubSubModule contains information about each sub-sub module, such as SFP's.
type SubSubModule struct {
	Name         string `xml:"name"`
	Version      string `xml:"version,omitempty"`
	PartNumber   string `xml:"part-number"`
	SerialNumber string `xml:"serial-number"`
	Description  string `xml:"description"`
}

// VirtualChassis contains information regarding the virtual-chassis setup for the device.
type VirtualChassis struct {
	PreProvisionedVCID   string     `xml:"preprovisioned-virtual-chassis-information>virtual-chassis-id"`
	PreProvisionedVCMode string     `xml:"preprovisioned-virtual-chassis-information>virtual-chassis-mode"`
	Members              []VCMember `xml:"member-list>member"`
}

// VCMember contains information about each individual virtual-chassis member.
type VCMember struct {
	Status       string             `xml:"member-status"`
	ID           int                `xml:"member-id"`
	FPCSlot      string             `xml:"fpc-slot"`
	SerialNumber string             `xml:"member-serial-number"`
	Model        string             `xml:"member-model"`
	Priority     int                `xml:"member-priority"`
	MixedMode    string             `xml:"member-mixed-mode"`
	RouteMode    string             `xml:"member-route-mode"`
	Role         string             `xml:"member-role"`
	Neighbors    []VCMemberNeighbor `xml:"neighbor-list>neighbor"`
}

// VCMemberNeighbor contains information about each virtual-chassis member neighbor.
type VCMemberNeighbor struct {
	ID        int    `xml:"neighbor-id"`
	Interface string `xml:"neighbor-interface"`
}

// Views contains the information for the specific views. Note that some views aren't available for specific
// hardware platforms, such as the "VirtualChassis" view on an SRX.
type Views struct {
	Arp            ArpTable
	Route          RoutingTable
	Interface      Interfaces
	Vlan           Vlans
	EthernetSwitch EthernetSwitchingTable
	Inventory      HardwareInventory
	VirtualChassis VirtualChassis
}

var (
	viewCategories = map[string]string{
		"arp":            "<get-arp-table-information><no-resolve/></get-arp-table-information>",
		"route":          "<get-route-information/>",
		"interface":      "<get-interface-information/>",
		"vlan":           "<get-vlan-information/>",
		"ethernetswitch": "<get-ethernet-switching-table-information/>",
		"inventory":      "<get-chassis-inventory/>",
		"virtualchassis": "<get-virtual-chassis-information/>",
	}
)

func validatePlatform(j *Junos, v string) error {
	switch v {
	case "ethernetswitch":
		if strings.Contains(j.Platform[0].Model, "SRX") || strings.Contains(j.Platform[0].Model, "MX") {
			return errors.New("ethernet-switching information is not available on this platform")
		}
	case "virtualchassis":
		if strings.Contains(j.Platform[0].Model, "SRX") || strings.Contains(j.Platform[0].Model, "MX") {
			return errors.New("virtual-chassis information is not available on this platform")
		}
	}

	return nil
}

// Views gathers information on the device given the "view" specified. These views can be interrated/looped over to view the
// data (i.e. ARP table entries, interface details/statistics, routing tables, etc.). Supported views are:
// arp, route, interface, vlan, ethernetswitch, inventory.
func (j *Junos) Views(view string) (*Views, error) {
	var results Views

	if strings.Contains(j.Platform[0].Model, "SRX") || strings.Contains(j.Platform[0].Model, "MX") {
		err := validatePlatform(j, view)

		if err != nil {
			return nil, err
		}
	}

	reply, err := j.Session.Exec(netconf.RawMethod(viewCategories[view]))
	if err != nil {
		return nil, err
	}

	if reply.Errors != nil {
		for _, m := range reply.Errors {
			return nil, errors.New(m.Message)
		}
	}

	if reply.Data == "" {
		return nil, errors.New("no output available - please check the syntax of your command")
	}

	switch view {
	case "arp":
		var arpTable ArpTable
		formatted := strings.Replace(reply.Data, "\n", "", -1)

		if err := xml.Unmarshal([]byte(formatted), &arpTable); err != nil {
			return nil, err
		}

		results.Arp = arpTable
	case "route":
		var routingTable RoutingTable
		formatted := strings.Replace(reply.Data, "\n", "", -1)

		if err := xml.Unmarshal([]byte(formatted), &routingTable); err != nil {
			return nil, err
		}

		results.Route = routingTable
	case "interface":
		var ints Interfaces
		formatted := strings.Replace(reply.Data, "\n", "", -1)

		if err := xml.Unmarshal([]byte(formatted), &ints); err != nil {
			return nil, err
		}

		results.Interface = ints
	case "vlan":
		var vlan Vlans
		formatted := strings.Replace(reply.Data, "\n", "", -1)

		if err := xml.Unmarshal([]byte(formatted), &vlan); err != nil {
			return nil, err
		}

		results.Vlan = vlan
	case "ethernetswitch":
		var ethtable EthernetSwitchingTable
		formatted := strings.Replace(reply.Data, "\n", "", -1)

		if err := xml.Unmarshal([]byte(formatted), &ethtable); err != nil {
			return nil, err
		}

		results.EthernetSwitch = ethtable
	case "inventory":
		var inventory HardwareInventory
		formatted := strings.Replace(reply.Data, "\n", "", -1)

		if strings.Contains(reply.Data, "multi-routing-engine-results") {
			var srxinventory srxHardwareInventory

			if err := xml.Unmarshal([]byte(formatted), &srxinventory); err != nil {
				return nil, err
			}

			for _, c := range srxinventory.Chassis {
				inventory.Chassis = append(inventory.Chassis, c)
			}

			results.Inventory = inventory
		} else {
			if err := xml.Unmarshal([]byte(formatted), &inventory); err != nil {
				return nil, err
			}

			results.Inventory = inventory
		}
	case "virtualchassis":
		var vc VirtualChassis
		formatted := strings.Replace(reply.Data, "\n", "", -1)

		if err := xml.Unmarshal([]byte(formatted), &vc); err != nil {
			return nil, err
		}

		results.VirtualChassis = vc
	}

	return &results, nil
}
<commit_msg>Added more sub-module processing<commit_after>package junos

import (
	"encoding/xml"
	"errors"
	"strings"

	"github.com/Juniper/go-netconf/netconf"
)

// ArpTable contains the ARP table on the device.
type ArpTable struct {
	Count   string     `xml:"arp-entry-count"`
	Entries []ArpEntry `xml:"arp-table-entry"`
}

// ArpEntry holds each individual ARP entry.
type ArpEntry struct {
	MACAddress string `xml:"mac-address"`
	IPAddress  string `xml:"ip-address"`
	Interface  string `xml:"interface-name"`
}

// RoutingTable contains every routing table on the device.
type RoutingTable struct {
	RouteTables []RouteTable `xml:"route-table"`
}

// RouteTable holds all the route information for each table.
type RouteTable struct {
	Name           string  `xml:"table-name"`
	TotalRoutes    int     `xml:"total-route-count"`
	ActiveRoutes   int     `xml:"active-route-count"`
	HolddownRoutes int     `xml:"holddown-route-count"`
	HiddenRoutes   int     `xml:"hidden-routes"`
	Entries        []Route `xml:"rt"`
}

// Route holds information about each individual route.
type Route struct {
	Destination           string `xml:"rt-destination"`
	Active                string `xml:"rt-entry>active-tag"`
	Protocol              string `xml:"rt-entry>protocol-name"`
	Preference            int    `xml:"rt-entry>preference"`
	Age                   string `xml:"rt-entry>age"`
	NextHop               string `xml:"rt-entry>nh>to,omitempty"`
	NextHopInterface      string `xml:"rt-entry>nh>via,omitempty"`
	NextHopTable          string `xml:"rt-entry>nh>nh-table,omitempty"`
	NextHopLocalInterface string `xml:"rt-entry>nh>nh-local-interface,omitempty"`
}

// Interfaces contains information about every interface on the device.
type Interfaces struct {
	Entries []PhysicalInterface `xml:"physical-interface"`
}

// PhysicalInterface contains information about each individual physical interface.
type PhysicalInterface struct {
	Name                    string             `xml:"name"`
	AdminStatus             string             `xml:"admin-status"`
	OperStatus              string             `xml:"oper-status"`
	LocalIndex              int                `xml:"local-index"`
	SNMPIndex               int                `xml:"snmp-index"`
	LinkLevelType           string             `xml:"link-level-type"`
	MTU                     string             `xml:"mtu"`
	LinkMode                string             `xml:"link-mode"`
	Speed                   string             `xml:"speed"`
	FlowControl             string             `xml:"if-flow-control"`
	AutoNegotiation         string             `xml:"if-auto-negotiation"`
	HardwarePhysicalAddress string             `xml:"hardware-physical-address"`
	Flapped                 string             `xml:"interface-flapped"`
	InputBps                int                `xml:"traffic-statistics>input-bps"`
	InputPps                int                `xml:"traffic-statistics>input-pps"`
	OutputBps               int                `xml:"traffic-statistics>output-bps"`
	OutputPps               int                `xml:"traffic-statistics>output-pps"`
	LogicalInterfaces       []LogicalInterface `xml:"logical-interface"`
}

// LogicalInterface contains information about the logical interfaces tied to a physical interface.
type LogicalInterface struct {
	Name               string `xml:"name"`
	MTU                string `xml:"address-family>mtu"`
	IPAddress          string `xml:"address-family>interface-address>ifa-local"`
	LocalIndex         int    `xml:"local-index"`
	SNMPIndex          int    `xml:"snmp-index"`
	Encapsulation      string `xml:"encapsulation"`
	LAGInputPackets    int    `xml:"lag-traffic-statistics>lag-bundle>input-packets"`
	LAGInputPps        int    `xml:"lag-traffic-statistics>lag-bundle>input-pps"`
	LAGInputBytes      int    `xml:"lag-traffic-statistics>lag-bundle>input-bytes"`
	LAGInputBps        int    `xml:"lag-traffic-statistics>lag-bundle>input-bps"`
	LAGOutputPackets   int    `xml:"lag-traffic-statistics>lag-bundle>output-packets"`
	LAGOutputPps       int    `xml:"lag-traffic-statistics>lag-bundle>output-pps"`
	LAGOutputBytes     int    `xml:"lag-traffic-statistics>lag-bundle>output-bytes"`
	LAGOutputBps       int    `xml:"lag-traffic-statistics>lag-bundle>output-bps"`
	ZoneName           string `xml:"logical-interface-zone-name"`
	InputPackets       int    `xml:"traffic-statistics>input-packets"`
	OutputPackets      int    `xml:"traffic-statistics>output-packets"`
	AddressFamily      string `xml:"address-family>address-family-name"`
	AggregatedEthernet string `xml:"address-family>ae-bundle-name,omitempty"`
}

// Vlans contains all of the VLAN information on the device.
type Vlans struct {
	Entries []Vlan `xml:"l2ng-l2ald-vlan-instance-group"`
}

// Vlan contains information about each individual VLAN.
type Vlan struct {
	Name             string   `xml:"l2ng-l2rtb-vlan-name"`
	Tag              int      `xml:"l2ng-l2rtb-vlan-tag"`
	MemberInterfaces []string `xml:"l2ng-l2rtb-vlan-member>l2ng-l2rtb-vlan-member-interface"`
}

// EthernetSwitchingTable contains the ethernet-switching table on the device.
type EthernetSwitchingTable struct {
	Entries []L2MACEntry `xml:"l2ng-l2ald-mac-entry-vlan"`
}

// L2MACEntry contains information about every MAC address on each VLAN.
type L2MACEntry struct {
	GlobalMACCount  int        `xml:"mac-count-global"`
	LearnedMACCount int        `xml:"learnt-mac-count"`
	RoutingInstance string     `xml:"l2ng-l2-mac-routing-instance"`
	VlanID          int        `xml:"l2ng-l2-vlan-id"`
	MACEntries      []MACEntry `xml:"l2ng-mac-entry"`
}

// MACEntry contains information about each individual MAC address. Flags are: S - static MAC, D - dynamic MAC,
// L - locally learned, P - persistent static, SE - statistics enabled, NM - non configured MAC, R - remote PE MAC,
// O - ovsdb MAC.
type MACEntry struct {
	VlanName         string `xml:"l2ng-l2-mac-vlan-name"`
	MACAddress       string `xml:"l2ng-l2-mac-address"`
	Age              string `xml:"l2ng-l2-mac-age"`
	Flags            string `xml:"l2ng-l2-mac-flags"`
	LogicalInterface string `xml:"l2ng-l2-mac-logical-interface"`
}

// HardwareInventory contains all the hardware information about the device.
type HardwareInventory struct {
	Chassis []Chassis `xml:"chassis"`
}

type srxHardwareInventory struct {
	Chassis []Chassis `xml:"multi-routing-engine-item>chassis-inventory>chassis"`
}

// Chassis contains all of the hardware information for each chassis, such as a clustered pair of SRX's or a
// virtual-chassis configuration.
type Chassis struct {
	Name         string   `xml:"name"`
	SerialNumber string   `xml:"serial-number"`
	Description  string   `xml:"description"`
	Modules      []Module `xml:"chassis-module"`
}

// Module contains information about each individual module.
type Module struct {
	Name         string      `xml:"name"`
	Version      string      `xml:"version,omitempty"`
	PartNumber   string      `xml:"part-number"`
	SerialNumber string      `xml:"serial-number"`
	Description  string      `xml:"description"`
	CLEICode     string      `xml:"clei-code"`
	ModuleNumber string      `xml:"module-number"`
	SubModules   []SubModule `xml:"chassis-sub-module"`
}

// SubModule contains information about each individual sub-module.
type SubModule struct {
	Name          string         `xml:"name"`
	Version       string         `xml:"version,omitempty"`
	PartNumber    string         `xml:"part-number"`
	SerialNumber  string         `xml:"serial-number"`
	Description   string         `xml:"description"`
	CLEICode      string         `xml:"clei-code"`
	ModuleNumber  string         `xml:"module-number"`
	SubSubModules []SubSubModule `xml:"chassis-sub-sub-module"`
}

// SubSubModule contains information about each sub-sub module, such as SFP's.
type SubSubModule struct {
	Name             string            `xml:"name"`
	Version          string            `xml:"version,omitempty"`
	PartNumber       string            `xml:"part-number"`
	SerialNumber     string            `xml:"serial-number"`
	Description      string            `xml:"description"`
	SubSubSubModules []SubSubSubModule `xml:"chassis-sub-sub-sub-module"`
}

// SubSubSubModule contains information about each sub-sub-sub module, such as SFP's on a
// PIC, which is tied to a MIC on an MX.
type SubSubSubModule struct {
	Name         string `xml:"name"`
	Version      string `xml:"version,omitempty"`
	PartNumber   string `xml:"part-number"`
	SerialNumber string `xml:"serial-number"`
	Description  string `xml:"description"`
}

// VirtualChassis contains information regarding the virtual-chassis setup for the device.
type VirtualChassis struct {
	PreProvisionedVCID   string     `xml:"preprovisioned-virtual-chassis-information>virtual-chassis-id"`
	PreProvisionedVCMode string     `xml:"preprovisioned-virtual-chassis-information>virtual-chassis-mode"`
	Members              []VCMember `xml:"member-list>member"`
}

// VCMember contains information about each individual virtual-chassis member.
type VCMember struct {
	Status       string             `xml:"member-status"`
	ID           int                `xml:"member-id"`
	FPCSlot      string             `xml:"fpc-slot"`
	SerialNumber string             `xml:"member-serial-number"`
	Model        string             `xml:"member-model"`
	Priority     int                `xml:"member-priority"`
	MixedMode    string             `xml:"member-mixed-mode"`
	RouteMode    string             `xml:"member-route-mode"`
	Role         string             `xml:"member-role"`
	Neighbors    []VCMemberNeighbor `xml:"neighbor-list>neighbor"`
}

// VCMemberNeighbor contains information about each virtual-chassis member neighbor.
type VCMemberNeighbor struct {
	ID        int    `xml:"neighbor-id"`
	Interface string `xml:"neighbor-interface"`
}

// Views contains the information for the specific views. Note that some views aren't available for specific
// hardware platforms, such as the "VirtualChassis" view on an SRX.
type Views struct {
	Arp            ArpTable
	Route          RoutingTable
	Interface      Interfaces
	Vlan           Vlans
	EthernetSwitch EthernetSwitchingTable
	Inventory      HardwareInventory
	VirtualChassis VirtualChassis
}

var (
	viewCategories = map[string]string{
		"arp":            "<get-arp-table-information><no-resolve/></get-arp-table-information>",
		"route":          "<get-route-information/>",
		"interface":      "<get-interface-information/>",
		"vlan":           "<get-vlan-information/>",
		"ethernetswitch": "<get-ethernet-switching-table-information/>",
		"inventory":      "<get-chassis-inventory/>",
		"virtualchassis": "<get-virtual-chassis-information/>",
	}
)

func validatePlatform(j *Junos, v string) error {
	switch v {
	case "ethernetswitch":
		if strings.Contains(j.Platform[0].Model, "SRX") || strings.Contains(j.Platform[0].Model, "MX") {
			return errors.New("ethernet-switching information is not available on this platform")
		}
	case "virtualchassis":
		if strings.Contains(j.Platform[0].Model, "SRX") || strings.Contains(j.Platform[0].Model, "MX") {
			return errors.New("virtual-chassis information is not available on this platform")
		}
	}

	return nil
}

// Views gathers information on the device given the "view" specified. These views can be interrated/looped over to view the
// data (i.e. ARP table entries, interface details/statistics, routing tables, etc.). Supported views are:
// arp, route, interface, vlan, ethernetswitch, inventory.
func (j *Junos) Views(view string) (*Views, error) {
	var results Views

	if strings.Contains(j.Platform[0].Model, "SRX") || strings.Contains(j.Platform[0].Model, "MX") {
		err := validatePlatform(j, view)

		if err != nil {
			return nil, err
		}
	}

	reply, err := j.Session.Exec(netconf.RawMethod(viewCategories[view]))
	if err != nil {
		return nil, err
	}

	if reply.Errors != nil {
		for _, m := range reply.Errors {
			return nil, errors.New(m.Message)
		}
	}

	if reply.Data == "" {
		return nil, errors.New("no output available - please check the syntax of your command")
	}

	switch view {
	case "arp":
		var arpTable ArpTable
		formatted := strings.Replace(reply.Data, "\n", "", -1)

		if err := xml.Unmarshal([]byte(formatted), &arpTable); err != nil {
			return nil, err
		}

		results.Arp = arpTable
	case "route":
		var routingTable RoutingTable
		formatted := strings.Replace(reply.Data, "\n", "", -1)

		if err := xml.Unmarshal([]byte(formatted), &routingTable); err != nil {
			return nil, err
		}

		results.Route = routingTable
	case "interface":
		var ints Interfaces
		formatted := strings.Replace(reply.Data, "\n", "", -1)

		if err := xml.Unmarshal([]byte(formatted), &ints); err != nil {
			return nil, err
		}

		results.Interface = ints
	case "vlan":
		var vlan Vlans
		formatted := strings.Replace(reply.Data, "\n", "", -1)

		if err := xml.Unmarshal([]byte(formatted), &vlan); err != nil {
			return nil, err
		}

		results.Vlan = vlan
	case "ethernetswitch":
		var ethtable EthernetSwitchingTable
		formatted := strings.Replace(reply.Data, "\n", "", -1)

		if err := xml.Unmarshal([]byte(formatted), &ethtable); err != nil {
			return nil, err
		}

		results.EthernetSwitch = ethtable
	case "inventory":
		var inventory HardwareInventory
		formatted := strings.Replace(reply.Data, "\n", "", -1)

		if strings.Contains(reply.Data, "multi-routing-engine-results") {
			var srxinventory srxHardwareInventory

			if err := xml.Unmarshal([]byte(formatted), &srxinventory); err != nil {
				return nil, err
			}

			for _, c := range srxinventory.Chassis {
				inventory.Chassis = append(inventory.Chassis, c)
			}

			results.Inventory = inventory
		} else {
			if err := xml.Unmarshal([]byte(formatted), &inventory); err != nil {
				return nil, err
			}

			results.Inventory = inventory
		}
	case "virtualchassis":
		var vc VirtualChassis
		formatted := strings.Replace(reply.Data, "\n", "", -1)

		if err := xml.Unmarshal([]byte(formatted), &vc); err != nil {
			return nil, err
		}

		results.VirtualChassis = vc
	}

	return &results, nil
}
<|endoftext|>


<commit_before>package adapter

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"strings"

	"github.com/cf-platform-eng/mongodb-on-demand-release/src/mongodb-service-adapter/digest"
)

type OMClient struct {
	Url      string
	Username string
	ApiKey   string
}

type Automation struct {
	MongoDbVersions []MongoDbVersionsType
}

type MongoDbVersionsType struct {
	Name string
}

type Group struct {
	ID          string         `json:"id"`
	Name        string         `json:"name"`
	AgentAPIKey string         `json:"agentApiKey"`
	HostCounts  map[string]int `json:"hostCounts"`
}

type GroupHosts struct {
	TotalCount int `json:"totalCount"`
}

type DocContext struct {
	ID                   string
	Key                  string
	AdminPassword        string
	Version              string
	CompatibilityVersion string
	Nodes                []string
	Cluster              *Cluster
	Password             string
}

type Cluster struct {
	Routers       []string
	ConfigServers []string
	Shards        [][]string
}

func (oc *OMClient) LoadDoc(p string, ctx *DocContext) (string, error) {
	t, ok := plans[p]
	if !ok {
		return "", fmt.Errorf("plan %q not found", p)
	}

	if ctx.Password == "" {
		var err error
		ctx.Password, err = GenerateString(32)
		if err != nil {
			panic(err)
		}
	}

	if strings.HasPrefix(ctx.Version, "3.4") {
		ctx.CompatibilityVersion = "3.4"
	}

	b := bytes.Buffer{}
	if err := t.Execute(&b, ctx); err != nil {
		return "", err
	}
	return b.String(), nil
}

func (oc *OMClient) CreateGroup(id string) (Group, error) {
	var group Group

	name := fmt.Sprintf("pcf_%s", id)
	b, err := oc.doRequest("POST", "/api/public/v1.0/groups", strings.NewReader(`{
		"name": "`+name+`"
	}`))
	if err != nil {
		return group, err
	}

	if err = json.Unmarshal(b, &group); err != nil {
		return group, err
	}
	return group, nil
}

func (oc *OMClient) GetGroup(groupID string) (Group, error) {
	var group Group

	b, err := oc.doRequest("GET", fmt.Sprintf("/api/public/v1.0/groups/%s", groupID), nil)
	if err != nil {
		return group, err
	}

	if err = json.Unmarshal(b, &group); err != nil {
		return group, err
	}
	return group, nil
}

func (oc *OMClient) DeleteGroup(groupID string) error {
	_, err := oc.doRequest("DELETE", fmt.Sprintf("/api/public/v1.0/groups/%s", groupID), nil)
	return err
}

func (oc *OMClient) GetGroupHosts(groupID string) (GroupHosts, error) {
	var groupHosts GroupHosts

	b, err := oc.doRequest("GET", fmt.Sprintf("/api/public/v1.0/groups/%s/hosts", groupID), nil)
	if err != nil {
		return groupHosts, err
	}

	if err = json.Unmarshal(b, &groupHosts); err != nil {
		return groupHosts, err
	}
	return groupHosts, nil
}

func (oc *OMClient) ConfigureGroup(configurationDoc string, groupID string) error {
	u := fmt.Sprintf("/api/public/v1.0/groups/%s/automationConfig", groupID)
	b, err := oc.doRequest("PUT", u, strings.NewReader(configurationDoc))
	if err != nil {
		return err
	}
	log.Println(string(b))

	return nil
}

func (oc *OMClient) GetAvailableVersions(groupID string) (Automation, error) {
	var versions Automation

	b, err := oc.doRequest("GET", fmt.Sprintf("/api/public/v1.0/groups/%s/automationConfig", groupID), nil)
	if err != nil {
		return versions, err
	}

	if err = json.Unmarshal(b, &versions); err != nil {
		return versions, err
	}
	return versions, nil
}

func (oc *OMClient) GetLatestVersion(groupID string) string {
	cfg, err := oc.GetAvailableVersions(groupID)
	if err != nil {
		panic(err)
	}

	b := cfg.MongoDbVersions[len(cfg.MongoDbVersions)-1].Name

	return b
}

func (oc *OMClient) doRequest(method string, path string, body io.Reader) ([]byte, error) {
	uri := fmt.Sprintf("%s%s", oc.Url, path)
	req, err := http.NewRequest(method, uri, body)
	if err != nil {
		return nil, err
	}

	req.Header.Set("Content-Type", "application/json")
	if err = digest.ApplyDigestAuth(oc.Username, oc.ApiKey, uri, req); err != nil {
		return nil, err
	}

	res, err := http.DefaultClient.Do(req)
	if err != nil {
		log.Fatalf("%s %s error: %v", method, uri, err)
		return nil, err
	}
	defer res.Body.Close()

	b, err := ioutil.ReadAll(res.Body)
	if err != nil {
		return nil, err
	}

	if res.StatusCode < 200 && res.StatusCode >= 300 {
		return nil, fmt.Errorf("%s %s request error: code=%d body=%q", method, path, res.StatusCode, b)
	}
	return b, nil
}
<commit_msg>fix featureCompatibilityVersion for mongodb 3.6<commit_after>package adapter

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"strings"

	"github.com/cf-platform-eng/mongodb-on-demand-release/src/mongodb-service-adapter/digest"
)

type OMClient struct {
	Url      string
	Username string
	ApiKey   string
}

type Automation struct {
	MongoDbVersions []MongoDbVersionsType
}

type MongoDbVersionsType struct {
	Name string
}

type Group struct {
	ID          string         `json:"id"`
	Name        string         `json:"name"`
	AgentAPIKey string         `json:"agentApiKey"`
	HostCounts  map[string]int `json:"hostCounts"`
}

type GroupHosts struct {
	TotalCount int `json:"totalCount"`
}

type DocContext struct {
	ID                   string
	Key                  string
	AdminPassword        string
	Version              string
	CompatibilityVersion string
	Nodes                []string
	Cluster              *Cluster
	Password             string
}

type Cluster struct {
	Routers       []string
	ConfigServers []string
	Shards        [][]string
}

func (oc *OMClient) LoadDoc(p string, ctx *DocContext) (string, error) {
	t, ok := plans[p]
	if !ok {
		return "", fmt.Errorf("plan %q not found", p)
	}

	if ctx.Password == "" {
		var err error
		ctx.Password, err = GenerateString(32)
		if err != nil {
			panic(err)
		}
	}

	if strings.HasPrefix(ctx.Version, "3.4") {
		ctx.CompatibilityVersion = "3.4"
	} else if strings.HasPrefix(ctx.Version, "3.6") {
		ctx.CompatibilityVersion = "3.6"
	}

	b := bytes.Buffer{}
	if err := t.Execute(&b, ctx); err != nil {
		return "", err
	}
	return b.String(), nil
}

func (oc *OMClient) CreateGroup(id string) (Group, error) {
	var group Group

	name := fmt.Sprintf("pcf_%s", id)
	b, err := oc.doRequest("POST", "/api/public/v1.0/groups", strings.NewReader(`{
		"name": "`+name+`"
	}`))
	if err != nil {
		return group, err
	}

	if err = json.Unmarshal(b, &group); err != nil {
		return group, err
	}
	return group, nil
}

func (oc *OMClient) GetGroup(groupID string) (Group, error) {
	var group Group

	b, err := oc.doRequest("GET", fmt.Sprintf("/api/public/v1.0/groups/%s", groupID), nil)
	if err != nil {
		return group, err
	}

	if err = json.Unmarshal(b, &group); err != nil {
		return group, err
	}
	return group, nil
}

func (oc *OMClient) DeleteGroup(groupID string) error {
	_, err := oc.doRequest("DELETE", fmt.Sprintf("/api/public/v1.0/groups/%s", groupID), nil)
	return err
}

func (oc *OMClient) GetGroupHosts(groupID string) (GroupHosts, error) {
	var groupHosts GroupHosts

	b, err := oc.doRequest("GET", fmt.Sprintf("/api/public/v1.0/groups/%s/hosts", groupID), nil)
	if err != nil {
		return groupHosts, err
	}

	if err = json.Unmarshal(b, &groupHosts); err != nil {
		return groupHosts, err
	}
	return groupHosts, nil
}

func (oc *OMClient) ConfigureGroup(configurationDoc string, groupID string) error {
	u := fmt.Sprintf("/api/public/v1.0/groups/%s/automationConfig", groupID)
	b, err := oc.doRequest("PUT", u, strings.NewReader(configurationDoc))
	if err != nil {
		return err
	}
	log.Println(string(b))

	return nil
}

func (oc *OMClient) GetAvailableVersions(groupID string) (Automation, error) {
	var versions Automation

	b, err := oc.doRequest("GET", fmt.Sprintf("/api/public/v1.0/groups/%s/automationConfig", groupID), nil)
	if err != nil {
		return versions, err
	}

	if err = json.Unmarshal(b, &versions); err != nil {
		return versions, err
	}
	return versions, nil
}

func (oc *OMClient) GetLatestVersion(groupID string) string {
	cfg, err := oc.GetAvailableVersions(groupID)
	if err != nil {
		panic(err)
	}

	b := cfg.MongoDbVersions[len(cfg.MongoDbVersions)-1].Name

	return b
}

func (oc *OMClient) doRequest(method string, path string, body io.Reader) ([]byte, error) {
	uri := fmt.Sprintf("%s%s", oc.Url, path)
	req, err := http.NewRequest(method, uri, body)
	if err != nil {
		return nil, err
	}

	req.Header.Set("Content-Type", "application/json")
	if err = digest.ApplyDigestAuth(oc.Username, oc.ApiKey, uri, req); err != nil {
		return nil, err
	}

	res, err := http.DefaultClient.Do(req)
	if err != nil {
		log.Fatalf("%s %s error: %v", method, uri, err)
		return nil, err
	}
	defer res.Body.Close()

	b, err := ioutil.ReadAll(res.Body)
	if err != nil {
		return nil, err
	}

	if res.StatusCode < 200 && res.StatusCode >= 300 {
		return nil, fmt.Errorf("%s %s request error: code=%d body=%q", method, path, res.StatusCode, b)
	}
	return b, nil
}
<|endoftext|>


<commit_before>package public

import (
	"appengine"
	"github.com/russross/blackfriday"
	"html/template"
	"models/presentation"
	"net/http"
	"strconv"
	"util"
)

const perPage = 5

//Index redirects user to the first page of presentation listing.
func Index(c util.Context) {
	http.Redirect(c.W, c.R, "/presentation?p=1", 301)
}

//Presentations shows listing of presentations in paginated form.
func Presentations(c util.Context) {
	page, err := strconv.Atoi(c.R.FormValue("p"))
	if err != nil {
		util.Log500(err, c)
		return
	}
	ps, err := presentation.GetListing(page, perPage, c.Ac)
	if err != nil {
		util.Log500(err, c)
		return
	}

	type templateData struct {
		P presentation.Presentation
		D template.HTML
	}

	data := make([]templateData, len(ps), len(ps))

	for _, p := range ps {
		data = append(data, templateData{P: *p, D: template.HTML(blackfriday.MarkdownCommon(p.Description))})
	}

	maxPages, err := presentation.PageCount(perPage, c.Ac)
	if err != nil {
		util.Log500(err, c)
		return
	}

	c.Ac.Infof("Hostname: %v", appengine.DefaultVersionHostname(c.Ac))

	util.RenderLayout("index.html", "Zoznam vysielaní", struct {
		Page     int
		MaxPages int
		Data     []templateData
		Domain   string
	}{Page: page, MaxPages: maxPages, Data: data, Domain: appengine.DefaultVersionHostname(c.Ac)}, c, "/static/js/index.js")
}
<commit_msg>Converted public controllers to new error handling thus finishng the conversion.<commit_after>package public

import (
	"appengine"
	"github.com/russross/blackfriday"
	"html/template"
	"models/presentation"
	"net/http"
	"strconv"
	"util"
)

const perPage = 5

//Index redirects user to the first page of presentation listing.
func Index(c util.Context) (err error) {
	http.Redirect(c.W, c.R, "/presentation?p=1", 301)
	return
}

//Presentations shows listing of presentations in paginated form.
func Presentations(c util.Context) (err error) {
	page, err := strconv.Atoi(c.R.FormValue("p"))
	if err != nil {
		return
	}
	ps, err := presentation.GetListing(page, perPage, c.Ac)
	if err != nil {
		return
	}

	type templateData struct {
		P presentation.Presentation
		D template.HTML
	}

	data := make([]templateData, len(ps), len(ps))

	for _, p := range ps {
		data = append(data, templateData{P: *p, D: template.HTML(blackfriday.MarkdownCommon(p.Description))})
	}

	maxPages, err := presentation.PageCount(perPage, c.Ac)
	if err != nil {
		return
	}

	c.Ac.Infof("Hostname: %v", appengine.DefaultVersionHostname(c.Ac))

	util.RenderLayout("index.html", "Zoznam vysielaní", struct {
		Page     int
		MaxPages int
		Data     []templateData
		Domain   string
	}{Page: page, MaxPages: maxPages, Data: data, Domain: appengine.DefaultVersionHostname(c.Ac)}, c, "/static/js/index.js")
	return
}
<|endoftext|>


<commit_before>// Copyright 2017 by caixw, All rights reserved.
// Use of this source code is governed by a MIT
// license that can be found in the LICENSE file.

// Package context 用于处理单个请求的上下文关系。
package context

import (
	"bytes"
	"io/ioutil"
	"log"
	"net/http"
	"strings"

	xencoding "golang.org/x/text/encoding"
	"golang.org/x/text/language"
	"golang.org/x/text/message"
	"golang.org/x/text/transform"

	"github.com/issue9/web/encoding"
	"github.com/issue9/web/internal/accept"
)

// 需要作比较，所以得是经过 http.CanonicalHeaderKey 处理的标准名称。
var (
	contentTypeKey     = http.CanonicalHeaderKey("Content-Type")
	contentLanguageKey = http.CanonicalHeaderKey("Content-Language")
)

// Context 是对当前请求内容的封装，仅与当前请求相关。
type Context struct {
	Response http.ResponseWriter
	Request  *http.Request

	// 指定输出时所使用的媒体类型，以及名称
	OutputMimeType     encoding.MarshalFunc
	OutputMimeTypeName string

	// 输出到客户端的字符集
	//
	// 若值为 xencoding.Nop 或是空，表示为 utf-8
	OutputCharset     xencoding.Encoding
	OutputCharsetName string

	// 客户端内容所使用的媒体类型。
	InputMimeType encoding.UnmarshalFunc

	// 客户端内容所使用的字符集
	//
	// 若值为 xencoding.Nop 或是空，表示为 utf-8
	InputCharset xencoding.Encoding

	// 输出语言的相关设置项。
	OutputTag     language.Tag
	LocalePrinter *message.Printer

	// 从客户端获取的内容，已经解析为 utf-8 方式。
	body []byte
}

// New 根据当前请求内容生成 Context 对象
//
// 如果 Accept 的内容与当前配置无法匹配，
// 则退出(panic)并输出 NotAcceptable 状态码。
//
// errlog 为错误信息输出通道，在 New() 非正常退出时，除了输出一个 HTTP 的状态码之外，
// 若还指定了 errlog，则还会将错误信息输出到该通道上，为 nil，则不输出任何错误信息。
//
// 一些特殊类型的请求，比如上传操作等，可能无法直接通过 New 构造一个合适的 Context，
// 此时可以直接使用 &Context{} 的方法手动指定 Context 的各个变量值。
func New(w http.ResponseWriter, r *http.Request, errlog *log.Logger) *Context {
	checkError := func(err error, status int) {
		if err == nil {
			return
		}

		if errlog != nil {
			errlog.Println(err)
		}
		Exit(status)
	}

	header := r.Header.Get("Accept")
	outputMimeType, marshal, err := encoding.AcceptMimeType(header)
	checkError(err, http.StatusNotAcceptable)

	header = r.Header.Get("Accept-Charset")
	outputCharsetName, outputCharset, err := encoding.AcceptCharset(header)
	checkError(err, http.StatusNotAcceptable)

	tag, err := acceptLanguage(r.Header.Get("Accept-Language"))
	checkError(err, http.StatusNotAcceptable)

	ctx := &Context{
		Response:           w,
		Request:            r,
		OutputMimeType:     marshal,
		OutputMimeTypeName: outputMimeType,
		OutputCharset:      outputCharset,
		OutputCharsetName:  outputCharsetName,
		OutputTag:          tag,
		LocalePrinter:      message.NewPrinter(tag),
	}

	// 只在有请求内容的时候，才会获取其输出转码函数
	// 当请求 body 为空时，r.Body == http.NoBody，与请求方法无关。
	if r.Body != nil && r.Body != http.NoBody {
		header = r.Header.Get(contentTypeKey)
		ctx.InputMimeType, ctx.InputCharset, err = encoding.ContentType(header)
		checkError(err, http.StatusUnsupportedMediaType)
	}

	return ctx
}

// Body 获取用户提交的内容。
//
// 相对于 ctx.Request().Body，此函数可多次读取。
// 不存在 body 时，返回 nil
func (ctx *Context) Body() (body []byte, err error) {
	if ctx.body != nil {
		return ctx.body, nil
	}

	if ctx.body, err = ioutil.ReadAll(ctx.Request.Body); err != nil {
		return nil, err
	}

	if encoding.CharsetIsNop(ctx.InputCharset) {
		return ctx.body, nil
	}

	d := ctx.InputCharset.NewDecoder()
	reader := transform.NewReader(bytes.NewReader(ctx.body), d)
	ctx.body, err = ioutil.ReadAll(reader)
	return ctx.body, err
}

// Unmarshal 将提交的内容转换成 v 对象。
func (ctx *Context) Unmarshal(v interface{}) error {
	body, err := ctx.Body()
	if err != nil {
		return err
	}

	if ctx.InputMimeType != nil {
		return ctx.InputMimeType(body, v)
	}

	return nil
}

// Marshal 将 v 解码并发送给客户端。
//
// 若 v 是一个 nil 值，则不会向客户端输出任何内容；
// 若是需要正常输出一个 nil 类型到客户端（json 中会输出 null），
// 可以使用 encoding.Nil 变量代替。
//
// NOTE: 如果需要指定一个特定的 Content-Type 和 Content-Language，
// 可以在 headers 中指定，否则使用当前的编码和语言名称。
func (ctx *Context) Marshal(status int, v interface{}, headers map[string]string) error {
	header := ctx.Response.Header()
	var contentTypeFound, contentLanguageFound bool
	for k, v := range headers {
		k = http.CanonicalHeaderKey(k)

		contentTypeFound = (contentTypeFound || k == contentTypeKey)
		contentLanguageFound = (contentLanguageFound || k == contentLanguageKey)
		header.Set(k, v)
	}

	if !contentTypeFound {
		ct := encoding.BuildContentType(ctx.OutputMimeTypeName, ctx.OutputCharsetName)
		header.Set(contentTypeKey, ct)
	}

	if !contentLanguageFound && ctx.OutputTag != language.Und {
		header.Set(contentLanguageKey, ctx.OutputTag.String())
	}

	if v == nil {
		ctx.Response.WriteHeader(status)
		return nil
	}

	data, err := ctx.OutputMimeType(v)
	if err != nil {
		return err
	}

	ctx.Response.WriteHeader(status)

	if encoding.CharsetIsNop(ctx.OutputCharset) {
		_, err = ctx.Response.Write(data)
		return err
	}

	w := transform.NewWriter(ctx.Response, ctx.OutputCharset.NewEncoder())
	if _, err = w.Write(data); err != nil {
		w.Close()
		return err
	}
	return w.Close()
}

// Read 从客户端读取数据并转换成 v 对象。
//
// 功能与 Unmarshal() 相同，只不过 Read() 在出错时，
// 会直接调用 Error() 处理：输出 422 的状态码，
// 并返回一个 false，告知用户转换失败。
func (ctx *Context) Read(v interface{}) (ok bool) {
	if err := ctx.Unmarshal(v); err != nil {
		ctx.Error(http.StatusUnprocessableEntity, err)
		return false
	}

	return true
}

// Render 将 v 渲染给客户端。
//
// 功能与 Marshal() 相同，只不过 Render() 在出错时，
// 会直接调用 Error() 处理，输出 500 的状态码。
//
// 如果需要具体控制出错后的处理方式，可以使用 Marshal 函数。
func (ctx *Context) Render(status int, v interface{}, headers map[string]string) {
	if err := ctx.Marshal(status, v, headers); err != nil {
		ctx.Error(http.StatusInternalServerError, err)
	}
}

// ClientIP 返回客户端的 IP 地址。
//
// 获取顺序如下：
//  - X-Forwarded-For 的第一个元素
//  - Remote-Addr 报头
//  - X-Read-IP 报头
func (ctx *Context) ClientIP() string {
	ip := ctx.Request.Header.Get("X-Forwarded-For")
	if index := strings.IndexByte(ip, ','); index > 0 {
		ip = ip[:index]
	}
	if ip == "" && ctx.Request.RemoteAddr != "" {
		ip = ctx.Request.RemoteAddr
	}
	if ip == "" {
		ip = ctx.Request.Header.Get("X-Real-IP")
	}

	return strings.TrimSpace(ip)
}

func acceptLanguage(header string) (language.Tag, error) {
	if header == "" {
		return language.Und, nil
	}

	al, err := accept.Parse(header)
	if err != nil {
		return language.Und, err
	}

	prefs := make([]language.Tag, 0, len(al))
	for _, l := range al {
		prefs = append(prefs, language.Make(l.Value))
	}

	tag, _, _ := message.DefaultCatalog.Matcher().Match(prefs...)
	return tag, nil
}
<commit_msg>添加 readed 变量，用于表示 body 是否已经被读取过<commit_after>// Copyright 2017 by caixw, All rights reserved.
// Use of this source code is governed by a MIT
// license that can be found in the LICENSE file.

// Package context 用于处理单个请求的上下文关系。
package context

import (
	"bytes"
	"io/ioutil"
	"log"
	"net/http"
	"strings"

	xencoding "golang.org/x/text/encoding"
	"golang.org/x/text/language"
	"golang.org/x/text/message"
	"golang.org/x/text/transform"

	"github.com/issue9/web/encoding"
	"github.com/issue9/web/internal/accept"
)

// 需要作比较，所以得是经过 http.CanonicalHeaderKey 处理的标准名称。
var (
	contentTypeKey     = http.CanonicalHeaderKey("Content-Type")
	contentLanguageKey = http.CanonicalHeaderKey("Content-Language")
)

// Context 是对当前请求内容的封装，仅与当前请求相关。
type Context struct {
	Response http.ResponseWriter
	Request  *http.Request

	// 指定输出时所使用的媒体类型，以及名称
	OutputMimeType     encoding.MarshalFunc
	OutputMimeTypeName string

	// 输出到客户端的字符集
	//
	// 若值为 xencoding.Nop 或是空，表示为 utf-8
	OutputCharset     xencoding.Encoding
	OutputCharsetName string

	// 客户端内容所使用的媒体类型。
	InputMimeType encoding.UnmarshalFunc

	// 客户端内容所使用的字符集
	//
	// 若值为 xencoding.Nop 或是空，表示为 utf-8
	InputCharset xencoding.Encoding

	// 输出语言的相关设置项。
	OutputTag     language.Tag
	LocalePrinter *message.Printer

	// 从客户端获取的内容，已经解析为 utf-8 方式。
	body   []byte
	readed bool // 是否已经从 r.Body 中加载过
}

// New 根据当前请求内容生成 Context 对象
//
// 如果 Accept 的内容与当前配置无法匹配，
// 则退出(panic)并输出 NotAcceptable 状态码。
//
// errlog 为错误信息输出通道，在 New() 非正常退出时，除了输出一个 HTTP 的状态码之外，
// 若还指定了 errlog，则还会将错误信息输出到该通道上，为 nil，则不输出任何错误信息。
//
// 一些特殊类型的请求，比如上传操作等，可能无法直接通过 New 构造一个合适的 Context，
// 此时可以直接使用 &Context{} 的方法手动指定 Context 的各个变量值。
func New(w http.ResponseWriter, r *http.Request, errlog *log.Logger) *Context {
	checkError := func(err error, status int) {
		if err == nil {
			return
		}

		if errlog != nil {
			errlog.Println(err)
		}
		Exit(status)
	}

	header := r.Header.Get("Accept")
	outputMimeType, marshal, err := encoding.AcceptMimeType(header)
	checkError(err, http.StatusNotAcceptable)

	header = r.Header.Get("Accept-Charset")
	outputCharsetName, outputCharset, err := encoding.AcceptCharset(header)
	checkError(err, http.StatusNotAcceptable)

	tag, err := acceptLanguage(r.Header.Get("Accept-Language"))
	checkError(err, http.StatusNotAcceptable)

	ctx := &Context{
		Response:           w,
		Request:            r,
		OutputMimeType:     marshal,
		OutputMimeTypeName: outputMimeType,
		OutputCharset:      outputCharset,
		OutputCharsetName:  outputCharsetName,
		OutputTag:          tag,
		LocalePrinter:      message.NewPrinter(tag),
	}

	// 只在有请求内容的时候，才会获取其输出转码函数
	// 当请求 body 为空时，r.Body == http.NoBody，与请求方法无关。
	if r.Body != nil && r.Body != http.NoBody {
		header = r.Header.Get(contentTypeKey)
		ctx.InputMimeType, ctx.InputCharset, err = encoding.ContentType(header)
		checkError(err, http.StatusUnsupportedMediaType)
	} else {
		ctx.readed = true
	}

	return ctx
}

// Body 获取用户提交的内容。
//
// 相对于 ctx.Request().Body，此函数可多次读取。
// 不存在 body 时，返回 nil
func (ctx *Context) Body() (body []byte, err error) {
	if ctx.readed {
		return ctx.body, nil
	}

	if ctx.body, err = ioutil.ReadAll(ctx.Request.Body); err != nil {
		return nil, err
	}

	if encoding.CharsetIsNop(ctx.InputCharset) {
		ctx.readed = true
		return ctx.body, nil
	}

	d := ctx.InputCharset.NewDecoder()
	reader := transform.NewReader(bytes.NewReader(ctx.body), d)
	ctx.body, err = ioutil.ReadAll(reader)
	ctx.readed = true
	return ctx.body, err
}

// Unmarshal 将提交的内容转换成 v 对象。
func (ctx *Context) Unmarshal(v interface{}) error {
	body, err := ctx.Body()
	if err != nil {
		return err
	}

	if ctx.InputMimeType != nil {
		return ctx.InputMimeType(body, v)
	}

	return nil
}

// Marshal 将 v 解码并发送给客户端。
//
// 若 v 是一个 nil 值，则不会向客户端输出任何内容；
// 若是需要正常输出一个 nil 类型到客户端（json 中会输出 null），
// 可以使用 encoding.Nil 变量代替。
//
// NOTE: 如果需要指定一个特定的 Content-Type 和 Content-Language，
// 可以在 headers 中指定，否则使用当前的编码和语言名称。
func (ctx *Context) Marshal(status int, v interface{}, headers map[string]string) error {
	header := ctx.Response.Header()
	var contentTypeFound, contentLanguageFound bool
	for k, v := range headers {
		k = http.CanonicalHeaderKey(k)

		contentTypeFound = (contentTypeFound || k == contentTypeKey)
		contentLanguageFound = (contentLanguageFound || k == contentLanguageKey)
		header.Set(k, v)
	}

	if !contentTypeFound {
		ct := encoding.BuildContentType(ctx.OutputMimeTypeName, ctx.OutputCharsetName)
		header.Set(contentTypeKey, ct)
	}

	if !contentLanguageFound && ctx.OutputTag != language.Und {
		header.Set(contentLanguageKey, ctx.OutputTag.String())
	}

	if v == nil {
		ctx.Response.WriteHeader(status)
		return nil
	}

	data, err := ctx.OutputMimeType(v)
	if err != nil {
		return err
	}

	ctx.Response.WriteHeader(status)

	if encoding.CharsetIsNop(ctx.OutputCharset) {
		_, err = ctx.Response.Write(data)
		return err
	}

	w := transform.NewWriter(ctx.Response, ctx.OutputCharset.NewEncoder())
	if _, err = w.Write(data); err != nil {
		w.Close()
		return err
	}
	return w.Close()
}

// Read 从客户端读取数据并转换成 v 对象。
//
// 功能与 Unmarshal() 相同，只不过 Read() 在出错时，
// 会直接调用 Error() 处理：输出 422 的状态码，
// 并返回一个 false，告知用户转换失败。
func (ctx *Context) Read(v interface{}) (ok bool) {
	if err := ctx.Unmarshal(v); err != nil {
		ctx.Error(http.StatusUnprocessableEntity, err)
		return false
	}

	return true
}

// Render 将 v 渲染给客户端。
//
// 功能与 Marshal() 相同，只不过 Render() 在出错时，
// 会直接调用 Error() 处理，输出 500 的状态码。
//
// 如果需要具体控制出错后的处理方式，可以使用 Marshal 函数。
func (ctx *Context) Render(status int, v interface{}, headers map[string]string) {
	if err := ctx.Marshal(status, v, headers); err != nil {
		ctx.Error(http.StatusInternalServerError, err)
	}
}

// ClientIP 返回客户端的 IP 地址。
//
// 获取顺序如下：
//  - X-Forwarded-For 的第一个元素
//  - Remote-Addr 报头
//  - X-Read-IP 报头
func (ctx *Context) ClientIP() string {
	ip := ctx.Request.Header.Get("X-Forwarded-For")
	if index := strings.IndexByte(ip, ','); index > 0 {
		ip = ip[:index]
	}
	if ip == "" && ctx.Request.RemoteAddr != "" {
		ip = ctx.Request.RemoteAddr
	}
	if ip == "" {
		ip = ctx.Request.Header.Get("X-Real-IP")
	}

	return strings.TrimSpace(ip)
}

func acceptLanguage(header string) (language.Tag, error) {
	if header == "" {
		return language.Und, nil
	}

	al, err := accept.Parse(header)
	if err != nil {
		return language.Und, err
	}

	prefs := make([]language.Tag, 0, len(al))
	for _, l := range al {
		prefs = append(prefs, language.Make(l.Value))
	}

	tag, _, _ := message.DefaultCatalog.Matcher().Match(prefs...)
	return tag, nil
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"io"
	"log"
	"os/exec"
)

var signal = make(chan int)

func sendSig() {
	signal <- 1
}

func pipeOutput(out io.ReadCloser, dest io.WriteCloser, logBuf *bytes.Buffer) {
	tempBuf := make([]byte, 1024)
	writeErr := error(nil)
	r, readErr := int(0), error(nil)

	defer out.Close()
	defer dest.Close()
	defer sendSig()

	for readErr == nil {
		r, readErr = out.Read(tempBuf)
		logBuf.Write(tempBuf[0:r])

		if r != 0 && writeErr == nil {
			_, writeErr := dest.Write(tempBuf[0:r])
			if writeErr != nil {
				log.Print(writeErr)
			}
		}
	}
}

func main() {
	cmd := exec.Command("timeout", "0.02", "./test.sh")
	stdout, err := cmd.StdoutPipe()
	if err != nil {
		log.Fatal(err)
	}
	stdin, err := cmd.StdinPipe()
	if err != nil {
		log.Fatal(err)
	}

	var b bytes.Buffer
	cmd.Start()
	go pipeOutput(stdout, stdin, &b)

	<-signal
	log.Print(b.String())
}
<commit_msg>Launch docker container with commited code<commit_after>package main

import (
	"bytes"
	"io"
	"log"
	"os"
	"os/exec"
)

var signal = make(chan int)

func sendSig() {
	signal <- 1
}

func pipeOutput(out io.ReadCloser, dest io.WriteCloser, logBuf *bytes.Buffer) {
	tempBuf := make([]byte, 1024)
	writeErr := error(nil)
	r, readErr := int(0), error(nil)

	defer out.Close()
	defer dest.Close()
	defer sendSig()

	for readErr == nil {
		r, readErr = out.Read(tempBuf)
		logBuf.Write(tempBuf[0:r])

		if r != 0 && writeErr == nil {
			_, writeErr := dest.Write(tempBuf[0:r])
			if writeErr != nil {
				log.Print(writeErr)
			}
		}
	}
}

func main() {
	if len(os.Args) != 3 {
		log.Fatal("Invalid number of arguments. This should never have happend")
	}

	commit := os.Args[1]
	tmpdir := os.Args[2]

	cmd := exec.Command("sudo", "docker", "run", "--rm", "-v", tmpdir+":/app", "coduno/base")
	stdout, err := cmd.StdoutPipe()
	if err != nil {
		log.Fatal(err)
	}
	stderr, err := cmd.StderrPipe()
	if err != nil {
		log.Fatal(err)
	}
	stdin, err := cmd.StdinPipe()
	if err != nil {
		log.Fatal(err)
	}

	var b1 bytes.Buffer
	var b2 bytes.Buffer
	cmd.Start()
	go pipeOutput(stdout, stdin, &b1)
	go pipeOutput(stderr, stdin, &b2)

	<-signal
	<-signal
	log.Print(b1.String())
	log.Print(b2.String())
	log.Print(commit)
}
<|endoftext|>


<commit_before>/*
Package lmdbscan provides a wrapper for lmdb.Cursor to simplify iteration.
This package is experimental and it's API may change.
*/
package lmdbscan

import (
	"fmt"

	"github.com/bmatsuo/lmdb-go/lmdb"
)

// Scanner is a low level construct for scanning databases inside a
// transaction.
type Scanner struct {
	dbi     lmdb.DBI
	dbflags uint
	txn     *lmdb.Txn
	cur     *lmdb.Cursor
	op      uint
	setop   *uint
	setkey  []byte
	setval  []byte
	key     []byte
	val     []byte
	err     error
}

// New allocates and intializes a Scanner for dbi within txn.  When the Scanner
// returned by New is no longer needed its Close method must be called.
func New(txn *lmdb.Txn, dbi lmdb.DBI) *Scanner {
	s := &Scanner{
		dbi: dbi,
		txn: txn,
	}
	if dbi != 0 {
		s.dbflags, s.err = txn.Flags(dbi)
		if s.err != nil {
			return s
		}
	}
	s.op = lmdb.Next

	s.cur, s.err = txn.OpenCursor(dbi)
	return s
}

// Cursor returns the lmdb.Cursor underlying s.  Cursor returns nil if the
// scanner is closed.
func (s *Scanner) Cursor() *lmdb.Cursor {
	return s.cur
}

// Del will delete the key at the current cursor location.
//
// Del is deprecated.  Instead use s.Cursor().Del(flags).
func (s *Scanner) Del(flags uint) error {
	if s.cur == nil {
		return fmt.Errorf("scanner is closed")
	}
	return s.cur.Del(flags)
}

// Key returns the key read during the last call to Scan.
func (s *Scanner) Key() []byte {
	return s.key
}

// Val returns the value read during the last call to Scan.
func (s *Scanner) Val() []byte {
	return s.val
}

// Set marks the starting position for iteration.  On the next call to s.Scan()
// the underlying cursor will be moved as c.Get(k, v, opset).
func (s *Scanner) Set(k, v []byte, opset uint) {
	if s.err != nil {
		return
	}
	s.setop = new(uint)
	*s.setop = opset
	s.setkey = k
	s.setval = v
}

// SetNext determines the cursor behavior for subsequent calls to s.Scan().
// The immediately following call to s.Scan() behaves as if s.Set(k,v,opset)
// was called.  Subsequent calls move the cursor as c.Get(nil, nil, opnext)
func (s *Scanner) SetNext(k, v []byte, opset, opnext uint) {
	s.Set(k, v, opset)
	s.op = opnext
}

// Scan gets key-value successive pairs with the underlying cursor until one
// matches the supplied filters.  If all filters return a nil error for the
// current pair, true is returned.  Scan returns false if all key-value pairs
// where exhausted.
func (s *Scanner) Scan() bool {
	if s.setop == nil {
		s.key, s.val, s.err = s.cur.Get(nil, nil, s.op)
	} else {
		s.key, s.val, s.err = s.cur.Get(s.setkey, s.setval, *s.setop)
		s.setkey = nil
		s.setval = nil
		s.setop = nil
	}
	return s.err == nil
}

// Err returns a non-nil error if and only if the previous call to s.Scan()
// resulted in an error other than lmdb.ErrNotFound.
func (s *Scanner) Err() error {
	if lmdb.IsNotFound(s.err) {
		return nil
	}
	return s.err
}

// Close closes the cursor underlying s and clears its ows internal structures.
// Close does not attempt to terminate the enclosing transaction.
//
// Scan must not be called after Close.
func (s *Scanner) Close() {
	s.txn = nil
	if s.cur != nil {
		s.cur.Close()
		s.cur = nil
	}
}
<commit_msg>Docs: Scanner.Scan godoc was outdated and didn't make sense anymore<commit_after>/*
Package lmdbscan provides a wrapper for lmdb.Cursor to simplify iteration.
This package is experimental and it's API may change.
*/
package lmdbscan

import (
	"fmt"

	"github.com/bmatsuo/lmdb-go/lmdb"
)

// Scanner is a low level construct for scanning databases inside a
// transaction.
type Scanner struct {
	dbi     lmdb.DBI
	dbflags uint
	txn     *lmdb.Txn
	cur     *lmdb.Cursor
	op      uint
	setop   *uint
	setkey  []byte
	setval  []byte
	key     []byte
	val     []byte
	err     error
}

// New allocates and intializes a Scanner for dbi within txn.  When the Scanner
// returned by New is no longer needed its Close method must be called.
func New(txn *lmdb.Txn, dbi lmdb.DBI) *Scanner {
	s := &Scanner{
		dbi: dbi,
		txn: txn,
	}
	if dbi != 0 {
		s.dbflags, s.err = txn.Flags(dbi)
		if s.err != nil {
			return s
		}
	}
	s.op = lmdb.Next

	s.cur, s.err = txn.OpenCursor(dbi)
	return s
}

// Cursor returns the lmdb.Cursor underlying s.  Cursor returns nil if the
// scanner is closed.
func (s *Scanner) Cursor() *lmdb.Cursor {
	return s.cur
}

// Del will delete the key at the current cursor location.
//
// Del is deprecated.  Instead use s.Cursor().Del(flags).
func (s *Scanner) Del(flags uint) error {
	if s.cur == nil {
		return fmt.Errorf("scanner is closed")
	}
	return s.cur.Del(flags)
}

// Key returns the key read during the last call to Scan.
func (s *Scanner) Key() []byte {
	return s.key
}

// Val returns the value read during the last call to Scan.
func (s *Scanner) Val() []byte {
	return s.val
}

// Set marks the starting position for iteration.  On the next call to s.Scan()
// the underlying cursor will be moved as c.Get(k, v, opset).
func (s *Scanner) Set(k, v []byte, opset uint) {
	if s.err != nil {
		return
	}
	s.setop = new(uint)
	*s.setop = opset
	s.setkey = k
	s.setval = v
}

// SetNext determines the cursor behavior for subsequent calls to s.Scan().
// The immediately following call to s.Scan() behaves as if s.Set(k,v,opset)
// was called.  Subsequent calls move the cursor as c.Get(nil, nil, opnext)
func (s *Scanner) SetNext(k, v []byte, opset, opnext uint) {
	s.Set(k, v, opset)
	s.op = opnext
}

// Scan gets successive key-value pairs using the underlying cursor.  Scan
// returns false when key-value pairs are exhausted or another error is
// encountered.
func (s *Scanner) Scan() bool {
	if s.setop == nil {
		s.key, s.val, s.err = s.cur.Get(nil, nil, s.op)
	} else {
		s.key, s.val, s.err = s.cur.Get(s.setkey, s.setval, *s.setop)
		s.setkey = nil
		s.setval = nil
		s.setop = nil
	}
	return s.err == nil
}

// Err returns a non-nil error if and only if the previous call to s.Scan()
// resulted in an error other than lmdb.ErrNotFound.
func (s *Scanner) Err() error {
	if lmdb.IsNotFound(s.err) {
		return nil
	}
	return s.err
}

// Close closes the cursor underlying s and clears its ows internal structures.
// Close does not attempt to terminate the enclosing transaction.
//
// Scan must not be called after Close.
func (s *Scanner) Close() {
	s.txn = nil
	if s.cur != nil {
		s.cur.Close()
		s.cur = nil
	}
}
<|endoftext|>


<commit_before>package analysistest_test

import (
	"fmt"
	"log"
	"os"
	"reflect"
	"strings"
	"testing"

	"golang.org/x/tools/go/analysis/analysistest"
	"golang.org/x/tools/go/analysis/passes/findcall"
)

func init() {
	// This test currently requires GOPATH mode.
	// Explicitly disabling module mode should suffix, but
	// we'll also turn off GOPROXY just for good measure.
	if err := os.Setenv("GO111MODULE", "off"); err != nil {
		log.Fatal(err)
	}
	if err := os.Setenv("GOPROXY", "off"); err != nil {
		log.Fatal(err)
	}
}

// TestTheTest tests the analysistest testing infrastructure.
func TestTheTest(t *testing.T) {
	// We'll simulate a partly failing test of the findcall analysis,
	// which (by default) reports calls to functions named 'println'.
	findcall.Analyzer.Flags.Set("name", "println")

	filemap := map[string]string{"a/b.go": `package main

func main() {
	// The expectation is ill-formed:
	print() // want: "diagnostic"
	print() // want foo"fact"
	print() // want foo:
	print() // want "\xZZ scan error"

	// A dignostic is reported at this line, but the expectation doesn't match:
	println("hello, world") // want "wrong expectation text"

	// An unexpected diagnostic is reported at this line:
	println() // trigger an unexpected diagnostic

	// No diagnostic is reported at this line:
	print()	// want "unsatisfied expectation"

	// OK
	println("hello, world") // want "call of println"

	// OK (multiple expectations on same line)
	println(); println() // want "call of println(...)" "call of println(...)"
}

// OK (facts and diagnostics on same line)
func println(...interface{}) { println() } // want println:"found" "call of println(...)"

`}
	dir, cleanup, err := analysistest.WriteFiles(filemap)
	if err != nil {
		t.Fatal(err)
	}
	defer cleanup()

	var got []string
	t2 := errorfunc(func(s string) { got = append(got, s) }) // a fake *testing.T
	analysistest.Run(t2, dir, findcall.Analyzer, "a")

	want := []string{
		`a/b.go:5: in 'want' comment: unexpected ":"`,
		`a/b.go:6: in 'want' comment: got String after foo, want ':'`,
		`a/b.go:7: in 'want' comment: got EOF, want regular expression`,
		`a/b.go:8: in 'want' comment: illegal char escape`,
		`a/b.go:11:9: diagnostic "call of println(...)" does not match pattern "wrong expectation text"`,
		`a/b.go:14:9: unexpected diagnostic: call of println(...)`,
		`a/b.go:11: no diagnostic was reported matching "wrong expectation text"`,
		`a/b.go:17: no diagnostic was reported matching "unsatisfied expectation"`,
	}
	if !reflect.DeepEqual(got, want) {
		t.Errorf("got:\n%s\nwant:\n%s",
			strings.Join(got, "\n"),
			strings.Join(want, "\n"))
	}
}

type errorfunc func(string)

func (f errorfunc) Errorf(format string, args ...interface{}) {
	f(fmt.Sprintf(format, args...))
}
<commit_msg>go/analysis/analysistest: change error message checked in expectation<commit_after>package analysistest_test

import (
	"fmt"
	"log"
	"os"
	"reflect"
	"strings"
	"testing"

	"golang.org/x/tools/go/analysis/analysistest"
	"golang.org/x/tools/go/analysis/passes/findcall"
)

func init() {
	// This test currently requires GOPATH mode.
	// Explicitly disabling module mode should suffix, but
	// we'll also turn off GOPROXY just for good measure.
	if err := os.Setenv("GO111MODULE", "off"); err != nil {
		log.Fatal(err)
	}
	if err := os.Setenv("GOPROXY", "off"); err != nil {
		log.Fatal(err)
	}
}

// TestTheTest tests the analysistest testing infrastructure.
func TestTheTest(t *testing.T) {
	// We'll simulate a partly failing test of the findcall analysis,
	// which (by default) reports calls to functions named 'println'.
	findcall.Analyzer.Flags.Set("name", "println")

	filemap := map[string]string{"a/b.go": `package main

func main() {
	// The expectation is ill-formed:
	print() // want: "diagnostic"
	print() // want foo"fact"
	print() // want foo:
	print() // want "\xZZ scan error"

	// A dignostic is reported at this line, but the expectation doesn't match:
	println("hello, world") // want "wrong expectation text"

	// An unexpected diagnostic is reported at this line:
	println() // trigger an unexpected diagnostic

	// No diagnostic is reported at this line:
	print()	// want "unsatisfied expectation"

	// OK
	println("hello, world") // want "call of println"

	// OK (multiple expectations on same line)
	println(); println() // want "call of println(...)" "call of println(...)"
}

// OK (facts and diagnostics on same line)
func println(...interface{}) { println() } // want println:"found" "call of println(...)"

`}
	dir, cleanup, err := analysistest.WriteFiles(filemap)
	if err != nil {
		t.Fatal(err)
	}
	defer cleanup()

	var got []string
	t2 := errorfunc(func(s string) { got = append(got, s) }) // a fake *testing.T
	analysistest.Run(t2, dir, findcall.Analyzer, "a")

	want := []string{
		`a/b.go:5: in 'want' comment: unexpected ":"`,
		`a/b.go:6: in 'want' comment: got String after foo, want ':'`,
		`a/b.go:7: in 'want' comment: got EOF, want regular expression`,
		`a/b.go:8: in 'want' comment: invalid char escape`,
		`a/b.go:11:9: diagnostic "call of println(...)" does not match pattern "wrong expectation text"`,
		`a/b.go:14:9: unexpected diagnostic: call of println(...)`,
		`a/b.go:11: no diagnostic was reported matching "wrong expectation text"`,
		`a/b.go:17: no diagnostic was reported matching "unsatisfied expectation"`,
	}
	// Go 1.13's scanner error messages uses the word invalid where Go 1.12 used illegal. Convert them
	// to keep tests compatible with both.
	// TODO(matloob): Remove this once Go 1.13 is released.
	for i := range got {
		got[i] = strings.Replace(got[i], "illegal", "invalid", -1)
	} //
	if !reflect.DeepEqual(got, want) {
		t.Errorf("got:\n%s\nwant:\n%s",
			strings.Join(got, "\n"),
			strings.Join(want, "\n"))
	}
}

type errorfunc func(string)

func (f errorfunc) Errorf(format string, args ...interface{}) {
	f(fmt.Sprintf(format, args...))
}
<|endoftext|>