<commit_before>// Copyright (c) 2018 Cisco and/or its affiliates.
//
// 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

package cmdimpl

import (
	"encoding/json"
	"fmt"
	"github.com/contiv/vpp/plugins/contiv/model/node"
	"github.com/contiv/vpp/plugins/crd/cache/telemetrymodel"
	"github.com/contiv/vpp/plugins/ksr/model/pod"
	"github.com/contiv/vpp/plugins/netctl/http"
	"github.com/coreos/etcd/clientv3"
	"github.com/ligato/cn-infra/db/keyval/etcd"
	"github.com/ligato/cn-infra/logging"
	"github.com/ligato/cn-infra/logging/logrus"
	"github.com/ligato/vpp-agent/plugins/vpp/model/interfaces"
	"os"
	"strings"
	"text/tabwriter"
	"time"
)

type nodeData struct {
	ipam *telemetrymodel.IPamEntry
	ifcs telemetrymodel.NodeInterfaces
}
type nodeDataCache map[string]*nodeData

type podGetter struct {
	etcdConfig *etcd.ClientConfig
	ndCache    nodeDataCache
	logger     logging.Logger
	db         *etcd.BytesConnectionEtcd
	pods       []*pod.Pod
}

// PrintAllPods will print out all of the non local pods in a network in
// a table format.
func PrintAllPods() {
	w := getWriter("HOST-NAME")
	pg := newPodGetter()
	pg.printAllPods(w)
	pg.db.Close()
	w.Flush()
}

//PrintPodsPerNode will print out all of the non-local pods for a certain
// pods along with their tap interface ip address
func PrintPodsPerNode(input string) {
	w := getWriter("")
	pg := newPodGetter()
	pg.printPodsPerNode(w, input, "")
	pg.db.Close()
	w.Flush()
}

func newPodGetter() *podGetter {
	pg := &podGetter{
		etcdConfig: &etcd.ClientConfig{
			Config: &clientv3.Config{
				Endpoints: []string{etcdLocation},
			},
			OpTimeout: 1 * time.Second,
		},
		ndCache: make(nodeDataCache, 0),
		logger:  logrus.DefaultLogger(),
	}

	pg.logger.SetLevel(logging.ErrorLevel)

	// Create connection to etcd.
	var err error
	if pg.db, err = etcd.NewEtcdConnectionWithBytes(*pg.etcdConfig, pg.logger); err != nil {
		fmt.Println(err)
		os.Exit(1)
	}

	pg.pods = make([]*pod.Pod, 0)
	itr, err := pg.db.ListValues("/vnf-agent/contiv-ksr/k8s/pod/")
	if err != nil {
		fmt.Printf("Failed to get pods from etcd, error %s", err)
		os.Exit(2)
	}

	for {
		kv, stop := itr.GetNext()
		if stop {
			break
		}
		buf := kv.GetValue()
		podInfo := &pod.Pod{}
		if err = json.Unmarshal(buf, podInfo); err != nil {
			fmt.Printf("Failed to unmarshall pod, error %s", err)
			continue
		}
		pg.pods = append(pg.pods, podInfo)
	}

	return pg
}

func (pg *podGetter) printAllPods(w *tabwriter.Writer) {

	itr, err := pg.db.ListValues("/vnf-agent/contiv-ksr/allocatedIDs/")
	if err != nil {
		fmt.Printf("Error getting values")
		return
	}

	for {
		kv, stop := itr.GetNext()
		if stop {
			fmt.Println()
			break
		}
		buf := kv.GetValue()
		nodeInfo := &node.NodeInfo{}
		err = json.Unmarshal(buf, nodeInfo)
		fmt.Fprintf(w, "%s:\t\t\t\t\t\t\t\t\n", nodeInfo.Name)
		fmt.Fprintf(w, "%s\t\t\t\t\t\t\t\t\n", strings.Repeat("-", len(nodeInfo.Name)+1))

		pg.printPodsPerNode(w, nodeInfo.ManagementIpAddress, nodeInfo.Name)
		fmt.Fprintln(w, "\t\t\t\t\t\t\t\t")
	}
}

func (pg *podGetter) printPodsPerNode(w *tabwriter.Writer, nodeNameOrIP string, nodeName string) {
	hostIP := resolveNodeOrIP(nodeNameOrIP)

	fmt.Fprintf(w, "POD-NAME\tNAMESPACE\tPOD-IP\tVPP-IP\tIF-IDX\tIF-NAME\tINTERNAL-IF-NAME\tHOST-IP\n")

	for _, podInfo := range pg.pods {
		if podInfo.HostIpAddress != hostIP || podInfo.IpAddress == hostIP {
			continue
		}

		if ipAddress, ifIndex, intName, name, err := pg.getTapInterfaces(podInfo); err == nil {
			fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%d\t%s\t%s\t%s\n",
				podInfo.Name,
				podInfo.Namespace,
				podInfo.IpAddress,
				strings.Split(ipAddress, "/")[0],
				ifIndex,
				intName,
				name,
				podInfo.HostIpAddress)

		} else {
			fmt.Printf("error %s\n", err)
		}
	}
}

func (pg *podGetter) getTapInterfaces(podInfo *pod.Pod) (string, uint32, string, string, error) {
	// If we haven't retrieved node info from the Agent yet, do it now
	if pg.ndCache[podInfo.HostIpAddress] == nil {
		// Get ipam data for the node where the pod is hosted
		b, err := http.GetNodeInfo(podInfo.HostIpAddress, getIpamDataCmd)
		if err != nil {
			return "", 0, "", "", fmt.Errorf("failed to get ipam for node %s, err %s",
				podInfo.HostIpAddress, err)
		}

		ipam := &telemetrymodel.IPamEntry{}
		if err := json.Unmarshal(b, ipam); err != nil {
			return "", 0, "", "", fmt.Errorf("failed to decode ipam for node %s, err %s",
				podInfo.HostIpAddress, err)
		}

		// Get interfaces data for the node where the pod is hosted
		b, err = http.GetNodeInfo(podInfo.HostIpAddress, getInterfaceDataCmd)
		intfs := make(telemetrymodel.NodeInterfaces)
		if err := json.Unmarshal(b, &intfs); err != nil {
			return "", 0, "", "", fmt.Errorf("failed to get pod's interface; pod %s, hostIPAddress %s, err %s",
				podInfo.Name, podInfo.HostIpAddress, err)
		}

		pg.ndCache[podInfo.HostIpAddress] = &nodeData{
			ipam: ipam,
			ifcs: intfs,
		}
	}

	// Determine the tap interface on VPP that connects the pod to the VPP
	podIfIPAddress, podIfIPMask, err := getIPAddressAndMask(pg.ndCache[podInfo.HostIpAddress].ipam.Config.PodIfIPCIDR)
	if err != nil {
		return "", 0, "", "", fmt.Errorf("invalid PodIfIPCIDR address %s, err %s",
			podInfo.HostIpAddress, err)
	}

	podIfIPPrefix := podIfIPAddress &^ podIfIPMask
	podAddr, err := ip2uint32(podInfo.IpAddress)
	if err != nil {
		return "", 0, "", "", fmt.Errorf("invalid podInfo.IpAddress %s, err %s",
			podInfo.HostIpAddress, err)
	}
	podAddrSuffix := podAddr & podIfIPMask

	for _, intf := range pg.ndCache[podInfo.HostIpAddress].ifcs {
		if intf.If.IfType == interfaces.InterfaceType_TAP_INTERFACE {
			for _, ip := range intf.If.IPAddresses {
				ifIPAddr, iffIPMask, err := getIPAddressAndMask(ip)
				if err != nil {
					continue
				}
				if iffIPMask != 0 {
					// TODO: do some error handling
					continue
				}

				ifIPAdrPrefix := ifIPAddr &^ podIfIPMask
				ifIPAdrSuffix := ifIPAddr & podIfIPMask
				if (podIfIPPrefix == ifIPAdrPrefix) && (ifIPAdrSuffix == podAddrSuffix) {
					return ip, intf.IfMeta.SwIfIndex, intf.IfMeta.VppInternalName, intf.If.Name, nil
				}
			}
		}
	}

	return "", 0, "", "", nil
}

func getWriter(hostName string) *tabwriter.Writer {
	w := tabwriter.NewWriter(os.Stdout, 0, 8, 2, ' ', 0)
	return w
}
<commit_msg>Refactor pod printout to account for errored PodIfIPCIDR data<commit_after>// Copyright (c) 2018 Cisco and/or its affiliates.
//
// 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

package cmdimpl

import (
	"encoding/json"
	"fmt"
	"github.com/contiv/vpp/plugins/contiv/model/node"
	"github.com/contiv/vpp/plugins/crd/cache/telemetrymodel"
	"github.com/contiv/vpp/plugins/ksr/model/pod"
	"github.com/contiv/vpp/plugins/netctl/http"
	"github.com/coreos/etcd/clientv3"
	"github.com/ligato/cn-infra/db/keyval/etcd"
	"github.com/ligato/cn-infra/logging"
	"github.com/ligato/cn-infra/logging/logrus"
	"github.com/ligato/vpp-agent/plugins/vpp/model/interfaces"
	"os"
	"strings"
	"text/tabwriter"
	"time"
)

type nodeData struct {
	ipam *telemetrymodel.IPamEntry
	ifcs telemetrymodel.NodeInterfaces
}
type nodeDataCache map[string]*nodeData

type podGetter struct {
	etcdConfig *etcd.ClientConfig
	ndCache    nodeDataCache
	logger     logging.Logger
	db         *etcd.BytesConnectionEtcd
	pods       []*pod.Pod
}

// PrintAllPods will print out all of the non local pods in a network in
// a table format.
func PrintAllPods() {
	w := getWriter("HOST-NAME")
	pg := newPodGetter()
	pg.printAllPods(w)
	pg.db.Close()
	w.Flush()
}

//PrintPodsPerNode will print out all of the non-local pods for a certain
// pods along with their tap interface ip address
func PrintPodsPerNode(input string) {
	w := getWriter("")
	pg := newPodGetter()
	pg.printPodsPerNode(w, input, "")
	pg.db.Close()
	w.Flush()
}

func newPodGetter() *podGetter {
	pg := &podGetter{
		etcdConfig: &etcd.ClientConfig{
			Config: &clientv3.Config{
				Endpoints: []string{etcdLocation},
			},
			OpTimeout: 1 * time.Second,
		},
		ndCache: make(nodeDataCache, 0),
		logger:  logrus.DefaultLogger(),
	}

	pg.logger.SetLevel(logging.ErrorLevel)

	// Create connection to etcd.
	var err error
	if pg.db, err = etcd.NewEtcdConnectionWithBytes(*pg.etcdConfig, pg.logger); err != nil {
		fmt.Println(err)
		os.Exit(1)
	}

	pg.pods = make([]*pod.Pod, 0)
	itr, err := pg.db.ListValues("/vnf-agent/contiv-ksr/k8s/pod/")
	if err != nil {
		fmt.Printf("Failed to get pods from etcd, error %s", err)
		os.Exit(2)
	}

	for {
		kv, stop := itr.GetNext()
		if stop {
			break
		}
		buf := kv.GetValue()
		podInfo := &pod.Pod{}
		if err = json.Unmarshal(buf, podInfo); err != nil {
			fmt.Printf("Failed to unmarshall pod, error %s", err)
			continue
		}
		pg.pods = append(pg.pods, podInfo)
	}

	return pg
}

func (pg *podGetter) printAllPods(w *tabwriter.Writer) {

	itr, err := pg.db.ListValues("/vnf-agent/contiv-ksr/allocatedIDs/")
	if err != nil {
		fmt.Printf("Error getting values")
		return
	}

	for {
		kv, stop := itr.GetNext()
		if stop {
			fmt.Println()
			break
		}
		buf := kv.GetValue()
		nodeInfo := &node.NodeInfo{}
		err = json.Unmarshal(buf, nodeInfo)
		nodeId := fmt.Sprintf("%s (%s):", nodeInfo.Name, nodeInfo.ManagementIpAddress)
		fmt.Fprintf(w, "%s\t\t\t\t\t\t\t\n", nodeId)
		fmt.Fprintf(w, "%s\t\t\t\t\t\t\t\n", strings.Repeat("-", len(nodeId)))

		pg.printPodsPerNode(w, nodeInfo.ManagementIpAddress, nodeInfo.Name)
		fmt.Fprintln(w, "\t\t\t\t\t\t\t\t")
	}
}

func (pg *podGetter) printPodsPerNode(w *tabwriter.Writer, nodeNameOrIP string, nodeName string) {
	hostIP := resolveNodeOrIP(nodeNameOrIP)

	fmt.Fprintf(w, "POD-NAME\tNAMESPACE\tPOD-IP\tVPP-IP\tIF-IDX\tIF-NAME\tINTERNAL-IF-NAME\n")

	for _, podInfo := range pg.pods {
		if podInfo.HostIpAddress != hostIP {
			continue
		} else if podInfo.IpAddress == hostIP {
			fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%s\t%s\t%s\n",
				podInfo.Name,
				podInfo.Namespace,
				podInfo.IpAddress,
				"", "", "", "")
		} else {
			ipAddress, ifIndex, intName, name := pg.getTapInterfaceForPod(podInfo)
			fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%d\t%s\t%s\n",
				podInfo.Name,
				podInfo.Namespace,
				podInfo.IpAddress,
				strings.Split(ipAddress, "/")[0],
				ifIndex,
				intName,
				name)
		}
	}
}

func (pg *podGetter) getTapInterfaceForPod(podInfo *pod.Pod) (string, uint32, string, string) {
	// when we see a pod from a given node for the first time, retrieve its
	// IPAM and interface info
	if pg.ndCache[podInfo.HostIpAddress] == nil {

		// Get ipam data for the node where the pod is hosted
		b, err := http.GetNodeInfo(podInfo.HostIpAddress, getIpamDataCmd)
		if err != nil {
			fmt.Printf("Host '%s', Pod '%s' - failed to get ipam, err %s\n",
				podInfo.HostIpAddress, podInfo.Name, err)
			return "N/A", 0, "N/A", "N/A"
		}

		ipam := &telemetrymodel.IPamEntry{}
		if err := json.Unmarshal(b, ipam); err != nil {
			fmt.Printf("Host '%s', Pod '%s' - failed to decode ipam, err %s\n",
				podInfo.HostIpAddress, podInfo.Name, err)
			return "N/A", 0, "N/A", "N/A"
		}

		// Get interfaces data for the node where the pod is hosted
		b, err = http.GetNodeInfo(podInfo.HostIpAddress, getInterfaceDataCmd)
		intfs := make(telemetrymodel.NodeInterfaces)
		if err := json.Unmarshal(b, &intfs); err != nil {
			fmt.Printf("Host '%s', Pod '%s' - failed to get pod's interface, err %s\n",
				podInfo.HostIpAddress, podInfo.Name, err)
			return "N/A", 0, "N/A", "N/A"
		}

		pg.ndCache[podInfo.HostIpAddress] = &nodeData{
			ipam: ipam,
			ifcs: intfs,
		}
	}

	// Determine the tap interface on VPP that connects the pod to the VPP
	podPfxLen := pg.ndCache[podInfo.HostIpAddress].ipam.Config.VppHostNetworkPrefixLen
	podMask := maskLength2Mask(int(podPfxLen))

	podIfIPAddress, podIfIPMask, err := getIPAddressAndMask(pg.ndCache[podInfo.HostIpAddress].ipam.Config.PodIfIPCIDR)
	if err != nil {
		fmt.Printf("Host '%s', Pod '%s' - invalid PodIfIPCIDR address %s, err %s\n",
			podInfo.HostIpAddress, podInfo.Name, pg.ndCache[podInfo.HostIpAddress].ipam.Config.PodIfIPCIDR, err)
		return "N/A", 0, "N/A", "N/A"
	}

	if podMask != podIfIPMask {
		fmt.Printf("Host '%s', Pod '%s' - vppHostNetworkPrefixLen mismatch: "+
			"PodIfIPCIDR '%s', podNetworkPrefixLen '%d'\n",
			podInfo.HostIpAddress, podInfo.Name,
			pg.ndCache[podInfo.HostIpAddress].ipam.Config.PodIfIPCIDR,
			pg.ndCache[podInfo.HostIpAddress].ipam.Config.PodNetworkPrefixLen)
	}

	podIfIPPrefix := podIfIPAddress &^ podMask
	podAddr, err := ip2uint32(podInfo.IpAddress)
	if err != nil {
		fmt.Printf("Host '%s', Pod '%s' - invalid podInfo.IpAddress %s, err %s",
			podInfo.HostIpAddress, podInfo.Name, podInfo.IpAddress, err)
		return "N/A", 0, "N/A", "N/A"
	}
	podAddrSuffix := podAddr & podMask

	for _, intf := range pg.ndCache[podInfo.HostIpAddress].ifcs {
		if intf.If.IfType == interfaces.InterfaceType_TAP_INTERFACE {
			for _, ip := range intf.If.IPAddresses {
				ifIPAddr, iffIPMask, err := getIPAddressAndMask(ip)
				if err != nil {
					continue
				}
				if iffIPMask != 0 {
					// TODO: do some error handling
					continue
				}

				ifIPAdrPrefix := ifIPAddr &^ podMask
				ifIPAdrSuffix := ifIPAddr & podMask
				if (podIfIPPrefix == ifIPAdrPrefix) && (ifIPAdrSuffix == podAddrSuffix) {
					return ip, intf.IfMeta.SwIfIndex, intf.IfMeta.VppInternalName, intf.If.Name
				}
			}
		}
	}

	return "N/A", 0, "N/A", "N/A"
}

func getWriter(hostName string) *tabwriter.Writer {
	w := tabwriter.NewWriter(os.Stdout, 0, 8, 2, ' ', 0)
	return w
}
<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"bytes"
	"fmt"
	"html/template"
	"io"
	"io/ioutil"
	"net/http"
	"os"
	"regexp"
	"strings"

	"github.com/shurcooL/github_flavored_markdown"
	"github.com/shurcooL/github_flavored_markdown/gfmstyle"
	//"github.com/russross/blackfriday"
	"github.com/urfave/cli"
)

type Page struct {
	Path     string
	Markdown []byte
	Html     []byte
}

type Log struct {
	Path       string
	ParentPath string
	Text       string
}

func convertMdToHtml(pathToFile string) (*Page, error) {

	pageAsMarkdown, error := ioutil.ReadFile(pathToFile)

	appendix := enrichMd(getReadmeAsMarkdown())
	// Make sure that there is at least one newline before our heading
	pageAsMarkdown = append(pageAsMarkdown, "\n"...)
	for _, appendixLine := range appendix {
		pageAsMarkdown = append(pageAsMarkdown, appendixLine+"\n"...)
	}

	pageAsHtml := github_flavored_markdown.Markdown(pageAsMarkdown)

	if error != nil {
		return nil, error
	}

	return &Page{Path: pathToFile, Markdown: pageAsMarkdown, Html: pageAsHtml}, nil
}

/**
 * Adds various informations to the README.md of the container.
 */
func enrichMd(rdmeAsMdArr []string) (enrichedRdmeAsMdArr []string) {

	appendixTemplate, error := template.ParseFiles("appendix_template.md")
	var doc bytes.Buffer
	error = appendixTemplate.Execute(&doc, nil)
	s := doc.String()

	if error != nil {
		fmt.Printf("Error while processing template: >%s<.", error)
		return rdmeAsMdArr
	}

	return append(enrichedRdmeAsMdArr, strings.Split(s, "\n")...)
}

// `/var/log/AdminServer` - sshd log file
func parseLogFilePathsFromMd(pathToFile string) (*[]string, error) {

	var foundPath []string

	pageAsMarkdown, error := os.Open(pathToFile)

	if error != nil {
		return nil, error
	}

	matcher := regexp.MustCompile("`(.*)`.*-.*log.*")
	scanner := bufio.NewScanner(pageAsMarkdown)
	for scanner.Scan() {
		line := scanner.Text()
		match := matcher.FindStringSubmatch(line)
		if len(match) > 0 {
			foundPath = append(foundPath, match[1])
			fmt.Printf("Found path >%s< in file >%s<.\n", match[1], pathToFile)
		}
	}

	fmt.Printf("Found %d paths in file >%s<\n", len(foundPath), pathToFile)
	return &foundPath, nil
}

func readLogFiles(pathToLogFiles *[]string) (*[]Log, error) {

	var logs []Log

	for _, pathToLogFile := range *pathToLogFiles {
		file, err := os.Open(pathToLogFile)

		if err != nil {
			fmt.Printf("Error while reading log file >%s<: >%s<\n", pathToLogFile, err)
		} else {
			fmt.Printf("Successfully read log file >%s<.\n", pathToLogFile)
		}

		defer file.Close()

		var lines []string
		scanner := bufio.NewScanner(file)
		for scanner.Scan() {
			lines = append(lines, scanner.Text())
		}

		log := Log{Path: pathToLogFile, ParentPath: pathToLogFile, Text: strings.Join(lines, "\n")}
		logs = append(logs, log)
	}

	return &logs, nil
}

func init() {
	// By default logger is set to write to stderr device.
	//log.SetOutput(os.Stdout)
}

func main() {

	app := cli.NewApp()

	app.Email = "christoph.zauner@NLLK.net"
	app.Author = "Christoph Zauner"
	app.Version = "0.1.0-alpha"
	// cntrinfod, cntinfod
	app.Usage = "Container Insight: HTTP daemon which exposes and augments the containers REAMDE.md"

	app.Action = func(c *cli.Context) error {

		fmt.Printf("Starting daemon on port 8080...\n")

		http.HandleFunc("/", handler)
		http.HandleFunc("/log", logHandler)

		// Serve the "/assets/gfm.css" file.
		http.Handle("/assets/", http.StripPrefix("/assets", http.FileServer(gfmstyle.Assets)))

		http.ListenAndServe(":8080", nil)

		return nil
	}

	app.Run(os.Args)
}

func getReadmeAsMarkdown() []string {

	var possibleLocations [2]string = [2]string{"/README.md",
		"testdata/README.md"}

	var fd *os.File
	var err error

	for _, possibleLocation := range possibleLocations {
		fd, err = os.Open(possibleLocation)
		if err == nil {
			fmt.Printf("Found README.md file: >%s<\n", possibleLocation)
			break
		} else {
			fmt.Printf("Error when looking for README.md at >%s<: >%s<\n", possibleLocation, err)
		}
	}
	defer fd.Close()

	if err != nil {
		fmt.Printf("Could not find README.md file.\n")
		os.Exit(-1)
	}

	var lines []string
	scanner := bufio.NewScanner(fd)
	for scanner.Scan() {
		lines = append(lines, scanner.Text())
	}

	return lines
}

func handler(w http.ResponseWriter, r *http.Request) {
	page, _ := convertMdToHtml("testdata/README.md")

	io.WriteString(w, `<meta charset="utf-8"><link href="/assets/gfm.css" media="all" rel="stylesheet" type="text/css" /><link href="//cdnjs.cloudflare.com/ajax/libs/octicons/2.1.2/octicons.css" media="all" rel="stylesheet" type="text/css" />
`)
	w.Write(page.Html)
	io.WriteString(w, `</article></body></html>`)
}

func logHandler(w http.ResponseWriter, r *http.Request) {

	pathToLogFiles, _ := parseLogFilePathsFromMd("testdata/README.md")
	logs, _ := readLogFiles(pathToLogFiles)

	t, error := template.ParseFiles("log.html")
	error = t.Execute(w, logs)

	if error != nil {
		fmt.Printf("Error while processing template: >%s<.", error)
	}
}
<commit_msg>Use []byte instead of []string.<commit_after>package main

import (
	"bufio"
	"bytes"
	"fmt"
	"html/template"
	"io"
	"net/http"
	"os"
	"regexp"
	"strings"

	"github.com/shurcooL/github_flavored_markdown"
	"github.com/shurcooL/github_flavored_markdown/gfmstyle"
	"github.com/urfave/cli"
)

type Page struct {
	Path     string
	Markdown []byte
	Html     []byte
}

type Log struct {
	Path       string
	ParentPath string
	Text       string
}

func convertMdToHtml(readme []byte) (*Page, error) {

	enrichedReadme := enrichMd(getReadmeAsMarkdown())

	// Make sure that there is at least one newline before our heading

	readmeAsHtml := github_flavored_markdown.Markdown(enrichedReadme)
	return &Page{Path: "FIXME", Markdown: enrichedReadme, Html: readmeAsHtml}, nil
}

/**
 * Adds various informations to the README.md of the container.
 */
func enrichMd(readme []byte) []byte {

	appendixTemplate, error := template.ParseFiles("appendix_template.md")
	var appendixBuffer bytes.Buffer
	error = appendixTemplate.Execute(&appendixBuffer, nil)

	if error != nil {
		fmt.Printf("Error while processing template: >%s<.", error)
		return readme
	}

	return append(readme, appendixBuffer.Bytes()...)
}

// `/var/log/AdminServer` - sshd log file
func parseLogFilePathsFromMd(pathToFile string) (*[]string, error) {

	var foundPath []string

	pageAsMarkdown, error := os.Open(pathToFile)

	if error != nil {
		return nil, error
	}

	matcher := regexp.MustCompile("`(.*)`.*-.*log.*")
	scanner := bufio.NewScanner(pageAsMarkdown)
	for scanner.Scan() {
		line := scanner.Text()
		match := matcher.FindStringSubmatch(line)
		if len(match) > 0 {
			foundPath = append(foundPath, match[1])
			fmt.Printf("Found path >%s< in file >%s<.\n", match[1], pathToFile)
		}
	}

	fmt.Printf("Found %d paths in file >%s<\n", len(foundPath), pathToFile)
	return &foundPath, nil
}

func readLogFiles(pathToLogFiles *[]string) (*[]Log, error) {

	var logs []Log

	for _, pathToLogFile := range *pathToLogFiles {
		file, err := os.Open(pathToLogFile)

		if err != nil {
			fmt.Printf("Error while reading log file >%s<: >%s<\n", pathToLogFile, err)
		} else {
			fmt.Printf("Successfully read log file >%s<.\n", pathToLogFile)
		}

		defer file.Close()

		var lines []string
		scanner := bufio.NewScanner(file)
		for scanner.Scan() {
			lines = append(lines, scanner.Text())
		}

		log := Log{Path: pathToLogFile, ParentPath: pathToLogFile, Text: strings.Join(lines, "\n")}
		logs = append(logs, log)
	}

	return &logs, nil
}

func init() {
	// By default logger is set to write to stderr device.
	//log.SetOutput(os.Stdout)
}

func main() {

	app := cli.NewApp()

	app.Email = "christoph.zauner@NLLK.net"
	app.Author = "Christoph Zauner"
	app.Version = "0.1.0-alpha"
	// cntrinfod, cntinfod
	app.Usage = "Container Insight: HTTP daemon which exposes and augments the containers REAMDE.md"

	app.Action = func(c *cli.Context) error {

		fmt.Printf("Starting daemon on port 8080...\n")

		http.HandleFunc("/", handler)
		http.HandleFunc("/log", logHandler)

		// Serve the "/assets/gfm.css" file.
		http.Handle("/assets/", http.StripPrefix("/assets", http.FileServer(gfmstyle.Assets)))

		http.ListenAndServe(":8080", nil)

		return nil
	}

	app.Run(os.Args)
}

func getReadmeAsMarkdown() []byte {

	var possibleLocations [2]string = [2]string{"/README.md",
		"testdata/README.md"}

	var fd *os.File
	var err error

	for _, possibleLocation := range possibleLocations {
		fd, err = os.Open(possibleLocation)
		if err == nil {
			fmt.Printf("Found README.md file: >%s<\n", possibleLocation)
			break
		} else {
			fmt.Printf("Error when looking for README.md at >%s<: >%s<\n", possibleLocation, err)
		}
	}
	defer fd.Close()

	if err != nil {
		fmt.Printf("Could not find README.md file.\n")
		os.Exit(-1)
	}

	var readme []byte
	scanner := bufio.NewScanner(fd)
	for scanner.Scan() {
		readme = append(readme, scanner.Bytes()...)
	}

	return readme
}

func handler(w http.ResponseWriter, r *http.Request) {
	page, _ := convertMdToHtml(getReadmeAsMarkdown())

	io.WriteString(w, `<meta charset="utf-8"><link href="/assets/gfm.css" media="all" rel="stylesheet" type="text/css" /><link href="//cdnjs.cloudflare.com/ajax/libs/octicons/2.1.2/octicons.css" media="all" rel="stylesheet" type="text/css" />
`)
	w.Write(page.Html)
	io.WriteString(w, `</article></body></html>`)
}

func logHandler(w http.ResponseWriter, r *http.Request) {

	pathToLogFiles, _ := parseLogFilePathsFromMd("testdata/README.md")
	logs, _ := readLogFiles(pathToLogFiles)

	t, error := template.ParseFiles("log.html")
	error = t.Execute(w, logs)

	if error != nil {
		fmt.Printf("Error while processing template: >%s<.", error)
	}
}
<|endoftext|>


<commit_before>package authredirect

import (
	"net/http"

	"github.com/gorilla/context"
)

var requestURLKey = &struct{}{}

type Tracker struct {
	http.Handler
}

func (tracker Tracker) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	context.Set(r, requestURLKey, r.URL.String())
	tracker.Handler.ServeHTTP(w, r)
}
<commit_msg>don't use pointer for session keys<commit_after>package authredirect

import (
	"net/http"

	"github.com/gorilla/context"
)

var requestURLKey = "original-request-url"

type Tracker struct {
	http.Handler
}

func (tracker Tracker) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	context.Set(r, requestURLKey, r.URL.String())
	tracker.Handler.ServeHTTP(w, r)
}
<|endoftext|>


<commit_before>package gandalf

import (
	"io"
	"io/ioutil"
	"launchpad.net/mgo/bson"
	"net/http"
	"net/http/httptest"
	"strings"
	"testing"
)

func requestProject(b io.Reader, t *testing.T) (*httptest.ResponseRecorder, *http.Request) {
	request, err := http.NewRequest("POST", "/projects", b)
	if err != nil {
		t.Errorf("Error when creating new request: %s", err)
		t.FailNow()
	}
	request.Header.Set("Content-Type", "application/json")
	recorder := httptest.NewRecorder()
	return recorder, request
}

func readBody(b io.Reader, t *testing.T) string {
	body, err := ioutil.ReadAll(b)
	if err != nil {
		t.Errorf("Error when reading body: %s", err)
		t.FailNow()
	}
	return string(body)
}

func TestCreateProject(t *testing.T) {
	// defer delete project
	b := strings.NewReader(`{"name": "some_project"}`)
	recorder, request := requestProject(b, t)
	CreateProject(recorder, request)
	got := readBody(recorder.Body, t)
	expected := "Project some_project successfuly created"
	if got != expected {
		t.Errorf(`Expected body to be "%s", got: "%s"`, expected, got)
	}
}

func TestCreateProjectShouldSaveInDB(t *testing.T) {
	b := strings.NewReader(`{"name": "myProject"}`)
	recorder, request := requestProject(b, t)
	CreateProject(recorder, request)
	c := session.DB("gandalf").C("project")
	var p project
	err := c.Find(bson.M{"name": "myProject"}).One(&p)
	if err != nil {
		t.Errorf(`There was an error while retrieving project: "%s"`, err.Error())
	}
}

func TestCreateProjectShouldReturnErrorWhenNoParametersArePassed(t *testing.T) {
	b := strings.NewReader("{}")
	recorder, request := requestProject(b, t)
	CreateProject(recorder, request)
	if recorder.Code != 400 {
		t.Errorf(`Expected code to be "400", got "%d"`, recorder.Code)
	}
	body := readBody(recorder.Body, t)
	expected := "Project needs a name"
	got := strings.Replace(body, "\n", "", -1)
	if got != expected {
		t.Errorf(`Expected body to matches: "%s", got: "%s"`, expected, got)
	}
}

func TestCreateProjectShouldReturnErrorWhenJsonIsBroken(t *testing.T) {
	b := strings.NewReader("{]ja9aW}")
	recorder, request := requestProject(b, t)
	CreateProject(recorder, request)
	if recorder.Code != 400 {
		t.Errorf(`Expected code to be "400", got "%d"`, recorder.Code)
	}
	body := readBody(recorder.Body, t)
	expected := "Could not parse json: invalid character ']' looking for beginning of object key string"
	got := strings.Replace(body, "\n", "", -1)
	if got != expected {
		t.Errorf(`Expected body to matches: "%s", got: "%s"`, expected, got)
	}
}

func TestCreateProjectShouldReturnErrorWhenBodyIsEmpty(t *testing.T) {
	b := strings.NewReader("")
	recorder, request := requestProject(b, t)
	CreateProject(recorder, request)
	if recorder.Code != 400 {
		t.Errorf(`Expected code to be "400", got "%d"`, recorder.Code)
	}
}
<commit_msg>Refactoring handler test code<commit_after>package gandalf

import (
	"io"
	"io/ioutil"
	"launchpad.net/mgo/bson"
	"net/http"
	"net/http/httptest"
	"strings"
	"testing"
)

func request(url string, b io.Reader, t *testing.T) (*httptest.ResponseRecorder, *http.Request) {
	request, err := http.NewRequest("POST", url, b)
	if err != nil {
		t.Errorf("Error when creating new request: %s", err)
		t.FailNow()
	}
	request.Header.Set("Content-Type", "application/json")
	recorder := httptest.NewRecorder()
	return recorder, request
}

func readBody(b io.Reader, t *testing.T) string {
	body, err := ioutil.ReadAll(b)
	if err != nil {
		t.Errorf("Error when reading body: %s", err)
		t.FailNow()
	}
	return string(body)
}

func TestCreateUser(t *testing.T) {
}

func TestCreateProject(t *testing.T) {
	c := session.DB("gandalf").C("project")
	defer c.Remove(bson.M{"name": "some_project"})
	b := strings.NewReader(`{"name": "some_project"}`)
	recorder, request := request("/projects", b, t)
	CreateProject(recorder, request)
	got := readBody(recorder.Body, t)
	expected := "Project some_project successfuly created"
	if got != expected {
		t.Errorf(`Expected body to be "%s", got: "%s"`, expected, got)
	}
}

func TestCreateProjectShouldSaveInDB(t *testing.T) {
	b := strings.NewReader(`{"name": "myProject"}`)
	recorder, request := request("/projects", b, t)
	CreateProject(recorder, request)
	c := session.DB("gandalf").C("project")
	defer c.Remove(bson.M{"name": "myProject"})
	var p project
	err := c.Find(bson.M{"name": "myProject"}).One(&p)
	if err != nil {
		t.Errorf(`There was an error while retrieving project: "%s"`, err.Error())
	}
}

func TestCreateProjectShouldReturnErrorWhenNoParametersArePassed(t *testing.T) {
	b := strings.NewReader("{}")
	recorder, request := request("/projects", b, t)
	CreateProject(recorder, request)
	if recorder.Code != 400 {
		t.Errorf(`Expected code to be "400", got "%d"`, recorder.Code)
	}
	body := readBody(recorder.Body, t)
	expected := "Project needs a name"
	got := strings.Replace(body, "\n", "", -1)
	if got != expected {
		t.Errorf(`Expected body to matches: "%s", got: "%s"`, expected, got)
	}
}

func TestCreateProjectShouldReturnErrorWhenJsonIsBroken(t *testing.T) {
	b := strings.NewReader("{]ja9aW}")
	recorder, request := request("/projects", b, t)
	CreateProject(recorder, request)
	if recorder.Code != 400 {
		t.Errorf(`Expected code to be "400", got "%d"`, recorder.Code)
	}
	body := readBody(recorder.Body, t)
	expected := "Could not parse json: invalid character ']' looking for beginning of object key string"
	got := strings.Replace(body, "\n", "", -1)
	if got != expected {
		t.Errorf(`Expected body to matches: "%s", got: "%s"`, expected, got)
	}
}

func TestCreateProjectShouldReturnErrorWhenBodyIsEmpty(t *testing.T) {
	b := strings.NewReader("")
	recorder, request := request("/projects", b, t)
	CreateProject(recorder, request)
	if recorder.Code != 400 {
		t.Errorf(`Expected code to be "400", got "%d"`, recorder.Code)
	}
}
<|endoftext|>


<commit_before>package presilo

import (
	"fmt"
	"regexp"
	"strings"
)

func GeneratePython(schema *ObjectSchema, module string, tabstyle string) string {

	var ret *BufferedFormatString

	ret = NewBufferedFormatString(tabstyle)

	generatePythonImports(schema, ret)
	ret.Printfln("")
	generatePythonSignature(schema, ret)
	ret.Printfln("")
	generatePythonConstructor(schema, ret)
	ret.Printfln("")
	generatePythonDeserializer(schema, ret)
	ret.Printfln("")
	generatePythonFunctions(schema, ret)
	ret.Printfln("")

	return ret.String()
}

func ValidatePythonModule(module string) bool {

	// ref: https://www.python.org/dev/peps/pep-0008/#package-and-module-names
	pattern := "^[a-z0-9_]+$"
	matched, err := regexp.MatchString(pattern, module)
	return err == nil && matched
}

func generatePythonImports(schema *ObjectSchema, buffer *BufferedFormatString) {

	buffer.Printfln("import string")
	buffer.Printfln("import json")

	if containsRegexpMatch(schema) {
		buffer.Printfln("import re")
	}
}

func generatePythonSignature(schema *ObjectSchema, buffer *BufferedFormatString) {

	var description string

	description = schema.GetDescription()

	if len(description) != 0 {
		buffer.Printfln("'''\n%s\n'''\n", schema.GetDescription())
	}

	buffer.Printfln("class %s(object):", ToCamelCase(schema.Title))
	buffer.AddIndentation(1)
}

func generatePythonConstructor(schema *ObjectSchema, buffer *BufferedFormatString) {

	var declarations, setters []string
	var propertyName string
	var toWrite string

	buffer.Print("\ndef __init__(self, ")

	// required properties
	for _, propertyName = range schema.RequiredProperties {

		propertyName = ToSnakeCase(propertyName)
		declarations = append(declarations, propertyName)

		toWrite = fmt.Sprintf("\nself.set_%s(%s)", ToSnakeCase(propertyName), propertyName)
		setters = append(setters, toWrite)
	}

	buffer.Print(strings.Join(declarations, ", "))
	buffer.Print("):")

	// use setters
	buffer.AddIndentation(1)

	for _, setter := range setters {
		buffer.Print(setter)
	}

	buffer.AddIndentation(-1)
}

func generatePythonDeserializer(schema *ObjectSchema, buffer *BufferedFormatString) {

	var property TypeSchema
	var ctorArguments []string
	var argument string
	var className string
	var propertyName, casedPropertyName string

	className = ToCamelCase(schema.GetTitle())

	buffer.Printf("\n@staticmethod")
	buffer.Printf("\ndef deserialize_from(map):")
	buffer.AddIndentation(1)

	// use constructor
	buffer.Printf("\nret = %s(", className)

	for _, propertyName = range schema.RequiredProperties {

		argument = fmt.Sprintf("map[\"%s\"]", ToJavaCase(propertyName))
		ctorArguments = append(ctorArguments, argument)
	}

	buffer.Printf("%s)", strings.Join(ctorArguments, ", "))

	// misc setters
	buffer.Printf("\n")
	for _, propertyName = range schema.GetOrderedPropertyNames() {

		property = schema.Properties[propertyName]
		propertyName = ToJavaCase(property.GetTitle())
		casedPropertyName = fmt.Sprintf("map[\"%s\"]", propertyName)

		// if it's already set, skip it.
		if arrayContainsString(ctorArguments, casedPropertyName) {
			continue
		}

		// if it's constrained, use the setter
		if property.HasConstraints() {

			casedPropertyName = ToJavaCase(propertyName)
			buffer.Printf("\nret.set_%s(%s)", ToSnakeCase(propertyName), casedPropertyName)
			continue
		}

		// otherwise set.
		buffer.Printf("\nret.%s = %s", propertyName, casedPropertyName)
	}

	buffer.Printf("\nreturn ret")
	buffer.AddIndentation(-1)
	buffer.Printf("\n")
}

func generatePythonFunctions(schema *ObjectSchema, buffer *BufferedFormatString) {

	var subschema TypeSchema
	var propertyName, snakeName, description string

	for _, propertyName = range schema.GetOrderedPropertyNames() {

		subschema = schema.Properties[propertyName]
		snakeName = ToSnakeCase(propertyName)
		description = subschema.GetDescription()

		// getter
		buffer.Printf("\ndef get_%s(self):", snakeName)
		buffer.AddIndentation(1)

		if len(description) != 0 {
			buffer.Print("\n'''")
			buffer.AddIndentation(1)

			buffer.Printf("\nGets %s, defined as:\n%s\n", snakeName, description)

			buffer.AddIndentation(-1)
			buffer.Print("\n'''")
		}

		buffer.Printf("\nreturn self.%s\n", snakeName)
		buffer.AddIndentation(-1)

		// setter
		buffer.Printf("\ndef set_%s(self, %s):", snakeName, snakeName)
		buffer.AddIndentation(1)

		if len(description) != 0 {
			buffer.Print("\n'''")
			buffer.AddIndentation(1)

			buffer.Printf("\nSets %s, defined as:\n%s\n", snakeName, description)

			buffer.AddIndentation(-1)
			buffer.Print("\n'''")
		}

		switch subschema.GetSchemaType() {
		case SCHEMATYPE_STRING:
			generatePythonStringSetter(subschema.(*StringSchema), buffer)
		case SCHEMATYPE_INTEGER:
			fallthrough
		case SCHEMATYPE_NUMBER:
			generatePythonNumericSetter(subschema.(NumericSchemaType), buffer)
		case SCHEMATYPE_OBJECT:
			generatePythonObjectSetter(subschema.(*ObjectSchema), buffer)
		case SCHEMATYPE_ARRAY:
			generatePythonArraySetter(subschema.(*ArraySchema), buffer)
		}

		buffer.Printf("\nself.%s = value\n", snakeName)
		buffer.AddIndentation(-1)
	}
}

func generatePythonStringSetter(schema *StringSchema, buffer *BufferedFormatString) {

	if !schema.Nullable {
		generatePythonNullCheck(buffer)
	}

	if schema.MinLength != nil {
		generatePythonRangeCheck(*schema.MinLength, "value.length", "was shorter than allowable minimum", "%d", false, "<", "", buffer)
	}

	if schema.MaxLength != nil {
		generatePythonRangeCheck(*schema.MaxLength, "value.length", "was longer than allowable maximum", "%d", false, ">", "", buffer)
	}

	if schema.HasEnum() {
		generatePythonEnumCheck(schema, buffer, schema.GetEnum(), "\"", "\"")
	}

	if schema.Pattern != nil {

		buffer.Printf("\nif(not re.match(\"%s\", value)):", *schema.Pattern)
		buffer.AddIndentation(1)

		buffer.Printf("\nraise ValueError(\"Value '\" + value + \"' did not match pattern '%s'\")", *schema.Pattern)

		buffer.AddIndentation(-1)
	}
}

func generatePythonNumericSetter(schema NumericSchemaType, buffer *BufferedFormatString) {

	if schema.HasMinimum() {
		generatePythonRangeCheck(schema.GetMinimum(), "value", "is under the allowable minimum", schema.GetConstraintFormat(), schema.IsExclusiveMinimum(), "<=", "<", buffer)
	}

	if schema.HasMaximum() {
		generatePythonRangeCheck(schema.GetMaximum(), "value", "is over the allowable maximum", schema.GetConstraintFormat(), schema.IsExclusiveMaximum(), ">=", ">", buffer)
	}

	if schema.HasEnum() {
		generatePythonEnumCheck(schema, buffer, schema.GetEnum(), "", "")
	}

	if schema.HasMultiple() {

		buffer.Printf("\n\t\tif(value %% %f != 0):\n\t", schema.GetMultiple())
		buffer.AddIndentation(1)

		buffer.Printf("\nraise ValueError.new(\"Property '\" + value + \"' was not a multiple of %v\")", schema.GetMultiple())

		buffer.AddIndentation(-1)
	}
}

func generatePythonObjectSetter(schema *ObjectSchema, buffer *BufferedFormatString) {

	if !schema.Nullable {
		generatePythonNullCheck(buffer)
	}
}

func generatePythonArraySetter(schema *ArraySchema, buffer *BufferedFormatString) {

	if !schema.Nullable {
		generatePythonNullCheck(buffer)
	}

	if schema.MinItems != nil {
		generatePythonRangeCheck(*schema.MinItems, "len(value)", "does not have enough items", "%d", false, "<", "", buffer)
	}

	if schema.MaxItems != nil {
		generatePythonRangeCheck(*schema.MaxItems, "len(value)", "does not have enough items", "%d", false, ">", "", buffer)
	}
}

func generatePythonRangeCheck(value interface{}, reference, message, format string, exclusive bool, comparator, exclusiveComparator string, buffer *BufferedFormatString) {

	var compareString string

	if exclusive {
		compareString = exclusiveComparator
	} else {
		compareString = comparator
	}

	buffer.Printf("\nif(%s %s "+format+"):", reference, compareString, value)
	buffer.AddIndentation(1)

	buffer.Printf("\nraise ValueError(\"Property '\"+ value +\"' %s.\")\n", message)

	buffer.AddIndentation(-1)
}

/*
	Generates code which throws an error if the given [parameter]'s value is not contained in the given [validValues].
*/
func generatePythonEnumCheck(schema TypeSchema, buffer *BufferedFormatString, enumValues []interface{}, prefix string, postfix string) {

	var stringValues []string
	var length int

	length = len(enumValues)

	if length <= 0 {
		return
	}

	// convert enum values to strings
	for _, enum := range enumValues {
		stringValues = append(stringValues, fmt.Sprintf("%s%v%s", prefix, enum, postfix))
	}

	// write array of valid values
	buffer.Printf("\nvalidValues = [%s]\n", strings.Join(stringValues, ","))

	// compare
	buffer.Print("\nif(value not in validValues):")
	buffer.AddIndentation(1)

	buffer.Print("\nraise ValueError(\"Given value '\"+value+\"' was not found in list of acceptable values\")\n")

	buffer.AddIndentation(-1)
}

func generatePythonNullCheck(buffer *BufferedFormatString) {

	buffer.Print("\nif(value == None):")
	buffer.AddIndentation(1)
	buffer.Print("\nraise ValueError(\"Cannot set property to null value\")")
	buffer.AddIndentation(-1)
}
<commit_msg>Added python json serializer, and fixed bug where ctor would be made even if no required properties existed<commit_after>package presilo

import (
	"fmt"
	"regexp"
	"strings"
)

func GeneratePython(schema *ObjectSchema, module string, tabstyle string) string {

	var ret *BufferedFormatString

	ret = NewBufferedFormatString(tabstyle)

	generatePythonImports(schema, ret)
	ret.Printfln("")
	generatePythonSignature(schema, ret)
	ret.Printfln("")
	generatePythonConstructor(schema, ret)
	ret.Printfln("")
	generatePythonDeserializer(schema, ret)
	ret.Printfln("")
	generatePythonSerializer(schema, ret)
	ret.Printfln("")
	generatePythonFunctions(schema, ret)
	ret.Printfln("")

	return ret.String()
}

func ValidatePythonModule(module string) bool {

	// ref: https://www.python.org/dev/peps/pep-0008/#package-and-module-names
	pattern := "^[a-z0-9_]+$"
	matched, err := regexp.MatchString(pattern, module)
	return err == nil && matched
}

func generatePythonImports(schema *ObjectSchema, buffer *BufferedFormatString) {

	buffer.Printfln("import string")
	buffer.Printfln("import json")

	if containsRegexpMatch(schema) {
		buffer.Printfln("import re")
	}
}

func generatePythonSignature(schema *ObjectSchema, buffer *BufferedFormatString) {

	var description string

	description = schema.GetDescription()

	if len(description) != 0 {
		buffer.Printfln("'''\n%s\n'''\n", schema.GetDescription())
	}

	buffer.Printfln("class %s(object):", ToCamelCase(schema.Title))
	buffer.AddIndentation(1)
}

func generatePythonConstructor(schema *ObjectSchema, buffer *BufferedFormatString) {

	var declarations, setters []string
	var propertyName string
	var toWrite string

	if(len(schema.RequiredProperties) <= 0) {
		return
	}

	buffer.Print("\ndef __init__(self")

	// required properties
	for _, propertyName = range schema.RequiredProperties {

		propertyName = ToSnakeCase(propertyName)
		declarations = append(declarations, propertyName)

		toWrite = fmt.Sprintf("\nself.set_%s(%s)", ToSnakeCase(propertyName), propertyName)
		setters = append(setters, toWrite)
	}

	buffer.Print(", ")
	buffer.Print(strings.Join(declarations, ", "))
	buffer.Print("):")

	// use setters
	buffer.AddIndentation(1)

	for _, setter := range setters {
		buffer.Print(setter)
	}

	buffer.AddIndentation(-1)
}

func generatePythonDeserializer(schema *ObjectSchema, buffer *BufferedFormatString) {

	var property TypeSchema
	var ctorArguments []string
	var argument string
	var className string
	var propertyName, casedPropertyName string

	className = ToCamelCase(schema.GetTitle())

	buffer.Printf("\n@staticmethod")
	buffer.Printf("\ndef deserialize_from(map):")
	buffer.AddIndentation(1)

	// use constructor
	buffer.Printf("\nret = %s(", className)

	for _, propertyName = range schema.RequiredProperties {

		argument = fmt.Sprintf("map[\"%s\"]", ToJavaCase(propertyName))
		ctorArguments = append(ctorArguments, argument)
	}

	buffer.Printf("%s)", strings.Join(ctorArguments, ", "))

	// misc setters
	buffer.Printf("\n")
	for _, propertyName = range schema.GetOrderedPropertyNames() {

		property = schema.Properties[propertyName]
		propertyName = ToJavaCase(property.GetTitle())
		casedPropertyName = fmt.Sprintf("map[\"%s\"]", propertyName)

		// if it's already set, skip it.
		if arrayContainsString(ctorArguments, casedPropertyName) {
			continue
		}

		// if it's constrained, use the setter
		if property.HasConstraints() {

			casedPropertyName = ToJavaCase(propertyName)
			buffer.Printf("\nret.set_%s(%s)", ToSnakeCase(propertyName), casedPropertyName)
			continue
		}

		// otherwise set.
		buffer.Printf("\nret.%s = %s", propertyName, casedPropertyName)
	}

	buffer.Printf("\nreturn ret")
	buffer.AddIndentation(-1)
	buffer.Printf("\n")
}

func generatePythonSerializer(schema *ObjectSchema, buffer *BufferedFormatString) {

	buffer.Printf("\ndef to_json(self):")
	buffer.AddIndentation(1)
	buffer.Printf("\nreturn json.dumps(self, default=lambda o: o.__dict__, sort_keys=True, indent=4)")
	buffer.AddIndentation(-1)
}

func generatePythonFunctions(schema *ObjectSchema, buffer *BufferedFormatString) {

	var subschema TypeSchema
	var propertyName, snakeName, description string

	for _, propertyName = range schema.GetOrderedPropertyNames() {

		subschema = schema.Properties[propertyName]
		snakeName = ToSnakeCase(propertyName)
		description = subschema.GetDescription()

		// getter
		buffer.Printf("\ndef get_%s(self):", snakeName)
		buffer.AddIndentation(1)

		if len(description) != 0 {
			buffer.Print("\n'''")
			buffer.AddIndentation(1)

			buffer.Printf("\nGets %s, defined as:\n%s\n", snakeName, description)

			buffer.AddIndentation(-1)
			buffer.Print("\n'''")
		}

		buffer.Printf("\nreturn self.%s\n", snakeName)
		buffer.AddIndentation(-1)

		// setter
		buffer.Printf("\ndef set_%s(self, %s):", snakeName, snakeName)
		buffer.AddIndentation(1)

		if len(description) != 0 {
			buffer.Print("\n'''")
			buffer.AddIndentation(1)

			buffer.Printf("\nSets %s, defined as:\n%s\n", snakeName, description)

			buffer.AddIndentation(-1)
			buffer.Print("\n'''")
		}

		switch subschema.GetSchemaType() {
		case SCHEMATYPE_STRING:
			generatePythonStringSetter(subschema.(*StringSchema), buffer)
		case SCHEMATYPE_INTEGER:
			fallthrough
		case SCHEMATYPE_NUMBER:
			generatePythonNumericSetter(subschema.(NumericSchemaType), buffer)
		case SCHEMATYPE_OBJECT:
			generatePythonObjectSetter(subschema.(*ObjectSchema), buffer)
		case SCHEMATYPE_ARRAY:
			generatePythonArraySetter(subschema.(*ArraySchema), buffer)
		}

		buffer.Printf("\nself.%s = value\n", snakeName)
		buffer.AddIndentation(-1)
	}
}

func generatePythonStringSetter(schema *StringSchema, buffer *BufferedFormatString) {

	if !schema.Nullable {
		generatePythonNullCheck(buffer)
	}

	if schema.MinLength != nil {
		generatePythonRangeCheck(*schema.MinLength, "value.length", "was shorter than allowable minimum", "%d", false, "<", "", buffer)
	}

	if schema.MaxLength != nil {
		generatePythonRangeCheck(*schema.MaxLength, "value.length", "was longer than allowable maximum", "%d", false, ">", "", buffer)
	}

	if schema.HasEnum() {
		generatePythonEnumCheck(schema, buffer, schema.GetEnum(), "\"", "\"")
	}

	if schema.Pattern != nil {

		buffer.Printf("\nif(not re.match(\"%s\", value)):", *schema.Pattern)
		buffer.AddIndentation(1)

		buffer.Printf("\nraise ValueError(\"Value '\" + value + \"' did not match pattern '%s'\")", *schema.Pattern)

		buffer.AddIndentation(-1)
	}
}

func generatePythonNumericSetter(schema NumericSchemaType, buffer *BufferedFormatString) {

	if schema.HasMinimum() {
		generatePythonRangeCheck(schema.GetMinimum(), "value", "is under the allowable minimum", schema.GetConstraintFormat(), schema.IsExclusiveMinimum(), "<=", "<", buffer)
	}

	if schema.HasMaximum() {
		generatePythonRangeCheck(schema.GetMaximum(), "value", "is over the allowable maximum", schema.GetConstraintFormat(), schema.IsExclusiveMaximum(), ">=", ">", buffer)
	}

	if schema.HasEnum() {
		generatePythonEnumCheck(schema, buffer, schema.GetEnum(), "", "")
	}

	if schema.HasMultiple() {

		buffer.Printf("\n\t\tif(value %% %f != 0):\n\t", schema.GetMultiple())
		buffer.AddIndentation(1)

		buffer.Printf("\nraise ValueError.new(\"Property '\" + value + \"' was not a multiple of %v\")", schema.GetMultiple())

		buffer.AddIndentation(-1)
	}
}

func generatePythonObjectSetter(schema *ObjectSchema, buffer *BufferedFormatString) {

	if !schema.Nullable {
		generatePythonNullCheck(buffer)
	}
}

func generatePythonArraySetter(schema *ArraySchema, buffer *BufferedFormatString) {

	if !schema.Nullable {
		generatePythonNullCheck(buffer)
	}

	if schema.MinItems != nil {
		generatePythonRangeCheck(*schema.MinItems, "len(value)", "does not have enough items", "%d", false, "<", "", buffer)
	}

	if schema.MaxItems != nil {
		generatePythonRangeCheck(*schema.MaxItems, "len(value)", "does not have enough items", "%d", false, ">", "", buffer)
	}
}

func generatePythonRangeCheck(value interface{}, reference, message, format string, exclusive bool, comparator, exclusiveComparator string, buffer *BufferedFormatString) {

	var compareString string

	if exclusive {
		compareString = exclusiveComparator
	} else {
		compareString = comparator
	}

	buffer.Printf("\nif(%s %s "+format+"):", reference, compareString, value)
	buffer.AddIndentation(1)

	buffer.Printf("\nraise ValueError(\"Property '\"+ value +\"' %s.\")\n", message)

	buffer.AddIndentation(-1)
}

/*
	Generates code which throws an error if the given [parameter]'s value is not contained in the given [validValues].
*/
func generatePythonEnumCheck(schema TypeSchema, buffer *BufferedFormatString, enumValues []interface{}, prefix string, postfix string) {

	var stringValues []string
	var length int

	length = len(enumValues)

	if length <= 0 {
		return
	}

	// convert enum values to strings
	for _, enum := range enumValues {
		stringValues = append(stringValues, fmt.Sprintf("%s%v%s", prefix, enum, postfix))
	}

	// write array of valid values
	buffer.Printf("\nvalidValues = [%s]\n", strings.Join(stringValues, ","))

	// compare
	buffer.Print("\nif(value not in validValues):")
	buffer.AddIndentation(1)

	buffer.Print("\nraise ValueError(\"Given value '\"+value+\"' was not found in list of acceptable values\")\n")

	buffer.AddIndentation(-1)
}

func generatePythonNullCheck(buffer *BufferedFormatString) {

	buffer.Print("\nif(value == None):")
	buffer.AddIndentation(1)
	buffer.Print("\nraise ValueError(\"Cannot set property to null value\")")
	buffer.AddIndentation(-1)
}
<|endoftext|>


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

// +build linux

package inotify

import (
	"os"
	"testing"
	"time"
)

func TestInotifyEvents(t *testing.T) {
	// Create an inotify watcher instance and initialize it
	watcher, err := NewWatcher()
	if err != nil {
		t.Fatalf("NewWatcher() failed: %s", err)
	}

	t.Logf("NEEDS TO BE CONVERTED TO NEW GO TOOL") // TODO
	return

	// Add a watch for "_test"
	err = watcher.Watch("_test")
	if err != nil {
		t.Fatalf("Watcher.Watch() failed: %s", err)
	}

	// Receive errors on the error channel on a separate goroutine
	go func() {
		for err := range watcher.Error {
			t.Fatalf("error received: %s", err)
		}
	}()

	const testFile string = "_test/TestInotifyEvents.testfile"

	// Receive events on the event channel on a separate goroutine
	eventstream := watcher.Event
	var eventsReceived = 0
	done := make(chan bool)
	go func() {
		for event := range eventstream {
			// Only count relevant events
			if event.Name == testFile {
				eventsReceived++
				t.Logf("event received: %s", event)
			} else {
				t.Logf("unexpected event received: %s", event)
			}
		}
		done <- true
	}()

	// Create a file
	// This should add at least one event to the inotify event queue
	_, err = os.OpenFile(testFile, os.O_WRONLY|os.O_CREATE, 0666)
	if err != nil {
		t.Fatalf("creating test file failed: %s", err)
	}

	// We expect this event to be received almost immediately, but let's wait 1 s to be sure
	time.Sleep(1 * time.Second)
	if eventsReceived == 0 {
		t.Fatal("inotify event hasn't been received after 1 second")
	}

	// Try closing the inotify instance
	t.Log("calling Close()")
	watcher.Close()
	t.Log("waiting for the event channel to become closed...")
	select {
	case <-done:
		t.Log("event channel closed")
	case <-time.After(1 * time.Second):
		t.Fatal("event stream was not closed after 1 second")
	}
}

func TestInotifyClose(t *testing.T) {
	watcher, _ := NewWatcher()
	watcher.Close()

	done := false
	go func() {
		watcher.Close()
		done = true
	}()

	time.Sleep(50 * time.Millisecond)
	if !done {
		t.Fatal("double Close() test failed: second Close() call didn't return")
	}

	err := watcher.Watch("_test")
	if err == nil {
		t.Fatal("expected error on Watch() after Close(), got nil")
	}
}
<commit_msg>exp/inotify: fix data race in linux tests Fixes issue 2708.<commit_after>// Copyright 2010 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// +build linux

package inotify

import (
	"os"
	"testing"
	"time"
)

func TestInotifyEvents(t *testing.T) {
	// Create an inotify watcher instance and initialize it
	watcher, err := NewWatcher()
	if err != nil {
		t.Fatalf("NewWatcher() failed: %s", err)
	}

	t.Logf("NEEDS TO BE CONVERTED TO NEW GO TOOL") // TODO
	return

	// Add a watch for "_test"
	err = watcher.Watch("_test")
	if err != nil {
		t.Fatalf("Watcher.Watch() failed: %s", err)
	}

	// Receive errors on the error channel on a separate goroutine
	go func() {
		for err := range watcher.Error {
			t.Fatalf("error received: %s", err)
		}
	}()

	const testFile string = "_test/TestInotifyEvents.testfile"

	// Receive events on the event channel on a separate goroutine
	eventstream := watcher.Event
	var eventsReceived = 0
	done := make(chan bool)
	go func() {
		for event := range eventstream {
			// Only count relevant events
			if event.Name == testFile {
				eventsReceived++
				t.Logf("event received: %s", event)
			} else {
				t.Logf("unexpected event received: %s", event)
			}
		}
		done <- true
	}()

	// Create a file
	// This should add at least one event to the inotify event queue
	_, err = os.OpenFile(testFile, os.O_WRONLY|os.O_CREATE, 0666)
	if err != nil {
		t.Fatalf("creating test file failed: %s", err)
	}

	// We expect this event to be received almost immediately, but let's wait 1 s to be sure
	time.Sleep(1 * time.Second)
	if eventsReceived == 0 {
		t.Fatal("inotify event hasn't been received after 1 second")
	}

	// Try closing the inotify instance
	t.Log("calling Close()")
	watcher.Close()
	t.Log("waiting for the event channel to become closed...")
	select {
	case <-done:
		t.Log("event channel closed")
	case <-time.After(1 * time.Second):
		t.Fatal("event stream was not closed after 1 second")
	}
}

func TestInotifyClose(t *testing.T) {
	watcher, _ := NewWatcher()
	watcher.Close()

	done := make(chan bool)
	go func() {
		watcher.Close()
		done <- true
	}()

	select {
	case <-done:
	case <-time.After(50 * time.Millisecond):
		t.Fatal("double Close() test failed: second Close() call didn't return")
	}

	err := watcher.Watch("_test")
	if err == nil {
		t.Fatal("expected error on Watch() after Close(), got nil")
	}
}
<|endoftext|>


<commit_before>package utp

import (
	"net"
	"testing"
)

func getTCPConnectionPair() (net.Conn, net.Conn, error) {
	lst, err := net.Listen("tcp", "127.0.0.1:0")
	if err != nil {
		return nil, nil, err
	}

	var conn0 net.Conn
	var err0 error
	done := make(chan struct{})
	go func() {
		conn0, err0 = lst.Accept()
		close(done)
	}()

	conn1, err := net.Dial("tcp", lst.Addr().String())
	if err != nil {
		return nil, nil, err
	}

	<-done
	if err0 != nil {
		return nil, nil, err0
	}
	return conn0, conn1, nil
}

func getUTPConnectionPair() (net.Conn, net.Conn, error) {
	lst, err := NewSocket("udp", "127.0.0.1:0")
	if err != nil {
		return nil, nil, err
	}

	var conn0 net.Conn
	var err0 error
	done := make(chan struct{})
	go func() {
		conn0, err0 = lst.Accept()
		close(done)
	}()

	conn1, err := Dial(lst.Addr().String())
	if err != nil {
		return nil, nil, err
	}

	<-done
	if err0 != nil {
		return nil, nil, err0
	}

	return conn0, conn1, nil
}

func benchConnPair(b *testing.B, c0, c1 net.Conn) {
	b.ReportAllocs()
	b.SetBytes(128 << 10)
	b.ResetTimer()

	request := make([]byte, 52)
	response := make([]byte, (128<<10)+8)

	pair := []net.Conn{c0, c1}
	for i := 0; i < b.N; i++ {
		if i%2 == 0 {
			pair[0] = c0
			pair[1] = c1
		} else {
			pair[0] = c1
			pair[1] = c0
		}

		if _, err := pair[0].Write(request); err != nil {
			b.Fatal(err)
		}

		if _, err := pair[1].Read(request[:8]); err != nil {
			b.Fatal(err)
		}
		if _, err := pair[1].Read(request[8:]); err != nil {
			b.Fatal(err)
		}
		if _, err := pair[1].Write(response); err != nil {
			b.Fatal(err)
		}
		if _, err := pair[0].Read(response[:8]); err != nil {
			b.Fatal(err)
		}
		if _, err := pair[0].Read(response[8:]); err != nil {
			b.Fatal(err)
		}
	}
}

func BenchmarkTCP(b *testing.B) {
	conn0, conn1, err := getTCPConnectionPair()
	if err != nil {
		b.Fatal(err)
	}

	defer conn0.Close()
	defer conn1.Close()

	benchConnPair(b, conn0, conn1)
}

func BenchmarkUTP(b *testing.B) {
	conn0, conn1, err := getUTPConnectionPair()
	if err != nil {
		b.Fatal(err)
	}

	defer conn0.Close()
	defer conn1.Close()

	benchConnPair(b, conn0, conn1)
}
<commit_msg>Fix socket leak in syncthing test<commit_after>package utp

import (
	"net"
	"testing"
)

func getTCPConnectionPair() (net.Conn, net.Conn, error) {
	lst, err := net.Listen("tcp", "127.0.0.1:0")
	if err != nil {
		return nil, nil, err
	}

	var conn0 net.Conn
	var err0 error
	done := make(chan struct{})
	go func() {
		conn0, err0 = lst.Accept()
		close(done)
	}()

	conn1, err := net.Dial("tcp", lst.Addr().String())
	if err != nil {
		return nil, nil, err
	}

	<-done
	if err0 != nil {
		return nil, nil, err0
	}
	return conn0, conn1, nil
}

func getUTPConnectionPair() (net.Conn, net.Conn, error) {
	lst, err := NewSocket("udp", "127.0.0.1:0")
	if err != nil {
		return nil, nil, err
	}
	defer lst.Close()

	var conn0 net.Conn
	var err0 error
	done := make(chan struct{})
	go func() {
		conn0, err0 = lst.Accept()
		close(done)
	}()

	conn1, err := Dial(lst.Addr().String())
	if err != nil {
		return nil, nil, err
	}

	<-done
	if err0 != nil {
		return nil, nil, err0
	}

	return conn0, conn1, nil
}

func benchConnPair(b *testing.B, c0, c1 net.Conn) {
	b.ReportAllocs()
	b.SetBytes(128 << 10)
	b.ResetTimer()

	request := make([]byte, 52)
	response := make([]byte, (128<<10)+8)

	pair := []net.Conn{c0, c1}
	for i := 0; i < b.N; i++ {
		if i%2 == 0 {
			pair[0] = c0
			pair[1] = c1
		} else {
			pair[0] = c1
			pair[1] = c0
		}

		if _, err := pair[0].Write(request); err != nil {
			b.Fatal(err)
		}

		if _, err := pair[1].Read(request[:8]); err != nil {
			b.Fatal(err)
		}
		if _, err := pair[1].Read(request[8:]); err != nil {
			b.Fatal(err)
		}
		if _, err := pair[1].Write(response); err != nil {
			b.Fatal(err)
		}
		if _, err := pair[0].Read(response[:8]); err != nil {
			b.Fatal(err)
		}
		if _, err := pair[0].Read(response[8:]); err != nil {
			b.Fatal(err)
		}
	}
}

func BenchmarkTCP(b *testing.B) {
	conn0, conn1, err := getTCPConnectionPair()
	if err != nil {
		b.Fatal(err)
	}

	defer conn0.Close()
	defer conn1.Close()

	benchConnPair(b, conn0, conn1)
}

func BenchmarkUTP(b *testing.B) {
	conn0, conn1, err := getUTPConnectionPair()
	if err != nil {
		b.Fatal(err)
	}

	defer conn0.Close()
	defer conn1.Close()

	benchConnPair(b, conn0, conn1)
}
<|endoftext|>


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

package commands

import (
	"net"
	"strconv"
	"strings"

	"github.com/projectcalico/felix/bpf"

	"github.com/docopt/docopt-go"
	"github.com/pkg/errors"
	log "github.com/sirupsen/logrus"
	"github.com/spf13/cobra"

	"github.com/projectcalico/felix/bpf/nat"
)

func init() {
	natCmd.AddCommand(natDumpCmd)

	natSetCmd.AddCommand(newNatSetFrontend())
	natSetCmd.AddCommand(newNatSetBackend())
	natCmd.AddCommand(natSetCmd)

	natDelCmd.AddCommand(newNatDelFrontend())
	natDelCmd.AddCommand(newNatDelBackend())
	natCmd.AddCommand(natDelCmd)

	rootCmd.AddCommand(natCmd)
}

// conntrackCmd represents the conntrack command
var natCmd = &cobra.Command{
	Use:   "nat",
	Short: "Nanipulates network address translation (nat)",
	Long: "nat manipulates network address translation (nat), " +
		"which implements the bpf-based replacement for kube-proxy",
}

var natDumpCmd = &cobra.Command{
	Use:   "dump",
	Short: "dumps the nat tables",
	Run: func(cmd *cobra.Command, args []string) {
		if err := dump(cmd); err != nil {
			log.WithError(err).Error("Failed to dump NAT maps")
		}
	},
}

var natSetCmd = &cobra.Command{
	Use:   "set",
	Short: "sets an entry in the NAT tables",
}

var natDelCmd = &cobra.Command{
	Use:   "del",
	Short: "deletes an entry from the NAT tables",
}

func dump(cmd *cobra.Command) error {
	mc := &bpf.MapContext{}
	natMap, err := nat.LoadFrontendMap(nat.FrontendMap(mc))
	if err != nil {
		return err
	}

	back, err := nat.LoadBackendMap(nat.BackendMap(mc))
	if err != nil {
		return err
	}

	dumpNice(cmd.Printf, natMap, back)
	return nil
}

type printfFn func(format string, i ...interface{})

func dumpNice(printf printfFn, natMap nat.MapMem, back nat.BackendMapMem) {
	for nk, nv := range natMap {
		count := nv.Count()
		id := nv.ID()
		printf("%s port %d proto %d id %d count %d\n", nk.Addr(), nk.Port(), nk.Proto(), id, count)
		for i := uint32(0); i < count; i++ {
			bk := nat.NewNATBackendKey(id, uint32(i))
			bv, ok := back[bk]
			printf("\t%d:%d\t ", id, i)
			if !ok {
				printf("is missing\n")
			} else {
				printf("%s:%d\n", bv.Addr(), bv.Port())
			}
		}
	}
}

type natFrontend struct {
	*cobra.Command

	Proto string `docopt:"<proto>"`
	IP    string `docopt:"<ip>"`
	Port  string `docopt:"<port>"`
	ID    string `docopt:"<id>"`
	Count string `docopt:"<count>"`

	proto uint8
	ip    net.IP
	port  uint16
	id    uint32
	count uint32
}

func newNatSetFrontend() *cobra.Command {
	cmd := &natFrontend{
		Command: &cobra.Command{
			Use:   "front <ip> <port> <proto> <id> <count>",
			Short: "sets a NAT entry for frontend (virtual IP)",
		},
	}

	cmd.Command.Args = cmd.ArgsSet
	cmd.Command.Run = cmd.RunSet

	return cmd.Command
}

func (cmd *natFrontend) checkArgsCommon() error {
	switch proto := strings.ToLower(cmd.Proto); proto {
	case "udp":
		cmd.proto = 17
	case "tcp":
		cmd.proto = 6
	default:
		return errors.Errorf("unknown protocol %s", proto)
	}

	cmd.ip = net.ParseIP(cmd.IP)
	if cmd.ip == nil {
		return errors.Errorf("ip: %q is not an ip", cmd.IP)
	}

	port, err := strconv.ParseUint(cmd.Port, 0, 16)
	if err != nil {
		return errors.Errorf("port: %q is not 16-bit uint", cmd.Port)
	}
	cmd.port = uint16(port)

	return nil
}

func (cmd *natFrontend) ArgsSet(c *cobra.Command, args []string) error {
	var err error

	a, err := docopt.ParseArgs(makeDocUsage(c), args, "")
	if err != nil {
		return errors.New(err.Error())
	}

	err = a.Bind(cmd)
	if err != nil {
		return errors.New(err.Error())
	}

	if err := cmd.checkArgsCommon(); err != nil {
		return err
	}

	id, err := strconv.ParseUint(cmd.ID, 0, 32)
	if err != nil {
		return errors.Errorf("id: %q is not 32-bit uint", cmd.ID)
	}
	cmd.id = uint32(id)

	count, err := strconv.ParseUint(cmd.Count, 0, 16)
	if err != nil {
		return errors.Errorf("count: %q is not 32-bit uint", cmd.Count)
	}
	cmd.count = uint32(count)

	return nil
}

func (cmd *natFrontend) RunSet(c *cobra.Command, _ []string) {
	natMap := nat.FrontendMap(&bpf.MapContext{})
	if err := natMap.EnsureExists(); err != nil {
		log.WithError(err).Error("Failed to access NATMap")
	}
	k := nat.NewNATKey(cmd.ip, cmd.port, cmd.proto)
	v := nat.NewNATValue(cmd.id, cmd.count, 0, 0)
	if err := natMap.Update(k.AsBytes(), v.AsBytes()); err != nil {
		log.WithError(err).
			WithFields(log.Fields{
				"key":   k,
				"value": v,
			}).Error("Failed to update map entry")
	}
}

func newNatDelFrontend() *cobra.Command {
	cmd := &natFrontend{
		Command: &cobra.Command{
			Use:   "front <ip> <port> <proto>",
			Short: "deletes a NAT entry for frontend (virtual IP)",
		},
	}

	cmd.Command.Args = cmd.ArgsDel
	cmd.Command.Run = cmd.RunDel

	return cmd.Command
}

func (cmd *natFrontend) ArgsDel(c *cobra.Command, args []string) error {
	var err error

	a, err := docopt.ParseArgs(makeDocUsage(c), args, "")
	if err != nil {
		return errors.New(err.Error())
	}

	err = a.Bind(cmd)
	if err != nil {
		return errors.New(err.Error())
	}

	return cmd.checkArgsCommon()
}

func (cmd *natFrontend) RunDel(c *cobra.Command, _ []string) {
	natMap := nat.FrontendMap(&bpf.MapContext{})
	if err := natMap.EnsureExists(); err != nil {
		log.WithError(err).Error("Failed to access NATMap")
	}
	k := nat.NewNATKey(cmd.ip, cmd.port, cmd.proto)
	if err := natMap.Delete(k.AsBytes()); err != nil {
		log.WithError(err).
			WithFields(log.Fields{
				"key": k,
			}).Error("Failed to delete map entry")
	}
}

type natBackend struct {
	*cobra.Command

	IP   string `docopt:"<ip>"`
	Port string `docopt:"<port>"`
	ID   string `docopt:"<id>"`
	Idx  string `docopt:"<idx>"`

	ip   net.IP
	port uint16
	id   uint32
	idx  uint32
}

func newNatSetBackend() *cobra.Command {
	cmd := &natBackend{
		Command: &cobra.Command{
			Use:   "back <id> <idx> <ip> <port>",
			Short: "sets a NAT backend for frontend id",
		},
	}

	cmd.Command.Args = cmd.ArgsSet
	cmd.Command.Run = cmd.RunSet

	return cmd.Command
}

func (cmd *natBackend) checkArgsCommon() error {
	id, err := strconv.ParseUint(cmd.ID, 0, 32)
	if err != nil {
		return errors.Errorf("id: %q is not 32-bit uint", cmd.ID)
	}
	cmd.id = uint32(id)

	idx, err := strconv.ParseUint(cmd.Idx, 0, 32)
	if err != nil {
		return errors.Errorf("idx: %q is not 32-bit uint", cmd.Idx)
	}
	cmd.idx = uint32(idx)

	return nil
}

func (cmd *natBackend) ArgsSet(c *cobra.Command, args []string) error {
	var err error

	a, err := docopt.ParseArgs(makeDocUsage(c), args, "")
	if err != nil {
		return errors.New(err.Error())
	}

	err = a.Bind(cmd)
	if err != nil {
		return errors.New(err.Error())
	}

	cmd.ip = net.ParseIP(cmd.IP)
	if cmd.ip == nil {
		return errors.Errorf("ip: %q is not an ip", cmd.IP)
	}

	port, err := strconv.ParseUint(cmd.Port, 0, 16)
	if err != nil {
		return errors.Errorf("port: %q is not 16-bit uint", cmd.Port)
	}
	cmd.port = uint16(port)

	return cmd.checkArgsCommon()
}

func (cmd *natBackend) RunSet(c *cobra.Command, _ []string) {
	mc := &bpf.MapContext{}
	m := nat.BackendMap(mc)
	if err := m.EnsureExists(); err != nil {
		log.WithError(err).Error("Failed to access NATMap")
	}
	k := nat.NewNATBackendKey(cmd.id, cmd.idx)
	v := nat.NewNATBackendValue(cmd.ip, cmd.port)
	if err := m.Update(k.AsBytes(), v.AsBytes()); err != nil {
		log.WithError(err).
			WithFields(log.Fields{
				"key":   k,
				"value": v,
			}).Error("Failed to update map entry")
	}
}

func newNatDelBackend() *cobra.Command {
	cmd := &natBackend{
		Command: &cobra.Command{
			Use:   "back <id> <idx>",
			Short: "deletes a NAT backend for frontend id",
		},
	}

	cmd.Command.Args = cmd.ArgsDel
	cmd.Command.Run = cmd.RunDel

	return cmd.Command
}

func (cmd *natBackend) ArgsDel(c *cobra.Command, args []string) error {
	var err error

	a, err := docopt.ParseArgs(makeDocUsage(c), args, "")
	if err != nil {
		return errors.New(err.Error())
	}

	err = a.Bind(cmd)
	if err != nil {
		return errors.New(err.Error())
	}

	return cmd.checkArgsCommon()
}

func (cmd *natBackend) RunDel(c *cobra.Command, _ []string) {
	mc := &bpf.MapContext{}
	m := nat.BackendMap(mc)
	if err := m.EnsureExists(); err != nil {
		log.WithError(err).Error("Failed to access NATMap")
	}
	k := nat.NewNATBackendKey(cmd.id, cmd.idx)
	if err := m.Delete(k.AsBytes()); err != nil {
		log.WithError(err).
			WithFields(log.Fields{
				"key": k,
			}).Error("Failed to delete map entry")
	}
}
<commit_msg>cmd/calico-bpf: nat dump shows local counts as well<commit_after>// Copyright (c) 2020 Tigera, Inc. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package commands

import (
	"net"
	"strconv"
	"strings"

	"github.com/projectcalico/felix/bpf"

	"github.com/docopt/docopt-go"
	"github.com/pkg/errors"
	log "github.com/sirupsen/logrus"
	"github.com/spf13/cobra"

	"github.com/projectcalico/felix/bpf/nat"
)

func init() {
	natCmd.AddCommand(natDumpCmd)

	natSetCmd.AddCommand(newNatSetFrontend())
	natSetCmd.AddCommand(newNatSetBackend())
	natCmd.AddCommand(natSetCmd)

	natDelCmd.AddCommand(newNatDelFrontend())
	natDelCmd.AddCommand(newNatDelBackend())
	natCmd.AddCommand(natDelCmd)

	rootCmd.AddCommand(natCmd)
}

// conntrackCmd represents the conntrack command
var natCmd = &cobra.Command{
	Use:   "nat",
	Short: "Nanipulates network address translation (nat)",
	Long: "nat manipulates network address translation (nat), " +
		"which implements the bpf-based replacement for kube-proxy",
}

var natDumpCmd = &cobra.Command{
	Use:   "dump",
	Short: "dumps the nat tables",
	Run: func(cmd *cobra.Command, args []string) {
		if err := dump(cmd); err != nil {
			log.WithError(err).Error("Failed to dump NAT maps")
		}
	},
}

var natSetCmd = &cobra.Command{
	Use:   "set",
	Short: "sets an entry in the NAT tables",
}

var natDelCmd = &cobra.Command{
	Use:   "del",
	Short: "deletes an entry from the NAT tables",
}

func dump(cmd *cobra.Command) error {
	mc := &bpf.MapContext{}
	natMap, err := nat.LoadFrontendMap(nat.FrontendMap(mc))
	if err != nil {
		return err
	}

	back, err := nat.LoadBackendMap(nat.BackendMap(mc))
	if err != nil {
		return err
	}

	dumpNice(cmd.Printf, natMap, back)
	return nil
}

type printfFn func(format string, i ...interface{})

func dumpNice(printf printfFn, natMap nat.MapMem, back nat.BackendMapMem) {
	for nk, nv := range natMap {
		count := nv.Count()
		local := nv.LocalCount()
		id := nv.ID()
		printf("%s port %d proto %d id %d count %d local %d\n",
			nk.Addr(), nk.Port(), nk.Proto(), id, count, local)
		for i := uint32(0); i < count; i++ {
			bk := nat.NewNATBackendKey(id, uint32(i))
			bv, ok := back[bk]
			printf("\t%d:%d\t ", id, i)
			if !ok {
				printf("is missing\n")
			} else {
				printf("%s:%d\n", bv.Addr(), bv.Port())
			}
		}
	}
}

type natFrontend struct {
	*cobra.Command

	Proto string `docopt:"<proto>"`
	IP    string `docopt:"<ip>"`
	Port  string `docopt:"<port>"`
	ID    string `docopt:"<id>"`
	Count string `docopt:"<count>"`

	proto uint8
	ip    net.IP
	port  uint16
	id    uint32
	count uint32
}

func newNatSetFrontend() *cobra.Command {
	cmd := &natFrontend{
		Command: &cobra.Command{
			Use:   "front <ip> <port> <proto> <id> <count>",
			Short: "sets a NAT entry for frontend (virtual IP)",
		},
	}

	cmd.Command.Args = cmd.ArgsSet
	cmd.Command.Run = cmd.RunSet

	return cmd.Command
}

func (cmd *natFrontend) checkArgsCommon() error {
	switch proto := strings.ToLower(cmd.Proto); proto {
	case "udp":
		cmd.proto = 17
	case "tcp":
		cmd.proto = 6
	default:
		return errors.Errorf("unknown protocol %s", proto)
	}

	cmd.ip = net.ParseIP(cmd.IP)
	if cmd.ip == nil {
		return errors.Errorf("ip: %q is not an ip", cmd.IP)
	}

	port, err := strconv.ParseUint(cmd.Port, 0, 16)
	if err != nil {
		return errors.Errorf("port: %q is not 16-bit uint", cmd.Port)
	}
	cmd.port = uint16(port)

	return nil
}

func (cmd *natFrontend) ArgsSet(c *cobra.Command, args []string) error {
	var err error

	a, err := docopt.ParseArgs(makeDocUsage(c), args, "")
	if err != nil {
		return errors.New(err.Error())
	}

	err = a.Bind(cmd)
	if err != nil {
		return errors.New(err.Error())
	}

	if err := cmd.checkArgsCommon(); err != nil {
		return err
	}

	id, err := strconv.ParseUint(cmd.ID, 0, 32)
	if err != nil {
		return errors.Errorf("id: %q is not 32-bit uint", cmd.ID)
	}
	cmd.id = uint32(id)

	count, err := strconv.ParseUint(cmd.Count, 0, 16)
	if err != nil {
		return errors.Errorf("count: %q is not 32-bit uint", cmd.Count)
	}
	cmd.count = uint32(count)

	return nil
}

func (cmd *natFrontend) RunSet(c *cobra.Command, _ []string) {
	natMap := nat.FrontendMap(&bpf.MapContext{})
	if err := natMap.EnsureExists(); err != nil {
		log.WithError(err).Error("Failed to access NATMap")
	}
	k := nat.NewNATKey(cmd.ip, cmd.port, cmd.proto)
	v := nat.NewNATValue(cmd.id, cmd.count, 0, 0)
	if err := natMap.Update(k.AsBytes(), v.AsBytes()); err != nil {
		log.WithError(err).
			WithFields(log.Fields{
				"key":   k,
				"value": v,
			}).Error("Failed to update map entry")
	}
}

func newNatDelFrontend() *cobra.Command {
	cmd := &natFrontend{
		Command: &cobra.Command{
			Use:   "front <ip> <port> <proto>",
			Short: "deletes a NAT entry for frontend (virtual IP)",
		},
	}

	cmd.Command.Args = cmd.ArgsDel
	cmd.Command.Run = cmd.RunDel

	return cmd.Command
}

func (cmd *natFrontend) ArgsDel(c *cobra.Command, args []string) error {
	var err error

	a, err := docopt.ParseArgs(makeDocUsage(c), args, "")
	if err != nil {
		return errors.New(err.Error())
	}

	err = a.Bind(cmd)
	if err != nil {
		return errors.New(err.Error())
	}

	return cmd.checkArgsCommon()
}

func (cmd *natFrontend) RunDel(c *cobra.Command, _ []string) {
	natMap := nat.FrontendMap(&bpf.MapContext{})
	if err := natMap.EnsureExists(); err != nil {
		log.WithError(err).Error("Failed to access NATMap")
	}
	k := nat.NewNATKey(cmd.ip, cmd.port, cmd.proto)
	if err := natMap.Delete(k.AsBytes()); err != nil {
		log.WithError(err).
			WithFields(log.Fields{
				"key": k,
			}).Error("Failed to delete map entry")
	}
}

type natBackend struct {
	*cobra.Command

	IP   string `docopt:"<ip>"`
	Port string `docopt:"<port>"`
	ID   string `docopt:"<id>"`
	Idx  string `docopt:"<idx>"`

	ip   net.IP
	port uint16
	id   uint32
	idx  uint32
}

func newNatSetBackend() *cobra.Command {
	cmd := &natBackend{
		Command: &cobra.Command{
			Use:   "back <id> <idx> <ip> <port>",
			Short: "sets a NAT backend for frontend id",
		},
	}

	cmd.Command.Args = cmd.ArgsSet
	cmd.Command.Run = cmd.RunSet

	return cmd.Command
}

func (cmd *natBackend) checkArgsCommon() error {
	id, err := strconv.ParseUint(cmd.ID, 0, 32)
	if err != nil {
		return errors.Errorf("id: %q is not 32-bit uint", cmd.ID)
	}
	cmd.id = uint32(id)

	idx, err := strconv.ParseUint(cmd.Idx, 0, 32)
	if err != nil {
		return errors.Errorf("idx: %q is not 32-bit uint", cmd.Idx)
	}
	cmd.idx = uint32(idx)

	return nil
}

func (cmd *natBackend) ArgsSet(c *cobra.Command, args []string) error {
	var err error

	a, err := docopt.ParseArgs(makeDocUsage(c), args, "")
	if err != nil {
		return errors.New(err.Error())
	}

	err = a.Bind(cmd)
	if err != nil {
		return errors.New(err.Error())
	}

	cmd.ip = net.ParseIP(cmd.IP)
	if cmd.ip == nil {
		return errors.Errorf("ip: %q is not an ip", cmd.IP)
	}

	port, err := strconv.ParseUint(cmd.Port, 0, 16)
	if err != nil {
		return errors.Errorf("port: %q is not 16-bit uint", cmd.Port)
	}
	cmd.port = uint16(port)

	return cmd.checkArgsCommon()
}

func (cmd *natBackend) RunSet(c *cobra.Command, _ []string) {
	mc := &bpf.MapContext{}
	m := nat.BackendMap(mc)
	if err := m.EnsureExists(); err != nil {
		log.WithError(err).Error("Failed to access NATMap")
	}
	k := nat.NewNATBackendKey(cmd.id, cmd.idx)
	v := nat.NewNATBackendValue(cmd.ip, cmd.port)
	if err := m.Update(k.AsBytes(), v.AsBytes()); err != nil {
		log.WithError(err).
			WithFields(log.Fields{
				"key":   k,
				"value": v,
			}).Error("Failed to update map entry")
	}
}

func newNatDelBackend() *cobra.Command {
	cmd := &natBackend{
		Command: &cobra.Command{
			Use:   "back <id> <idx>",
			Short: "deletes a NAT backend for frontend id",
		},
	}

	cmd.Command.Args = cmd.ArgsDel
	cmd.Command.Run = cmd.RunDel

	return cmd.Command
}

func (cmd *natBackend) ArgsDel(c *cobra.Command, args []string) error {
	var err error

	a, err := docopt.ParseArgs(makeDocUsage(c), args, "")
	if err != nil {
		return errors.New(err.Error())
	}

	err = a.Bind(cmd)
	if err != nil {
		return errors.New(err.Error())
	}

	return cmd.checkArgsCommon()
}

func (cmd *natBackend) RunDel(c *cobra.Command, _ []string) {
	mc := &bpf.MapContext{}
	m := nat.BackendMap(mc)
	if err := m.EnsureExists(); err != nil {
		log.WithError(err).Error("Failed to access NATMap")
	}
	k := nat.NewNATBackendKey(cmd.id, cmd.idx)
	if err := m.Delete(k.AsBytes()); err != nil {
		log.WithError(err).
			WithFields(log.Fields{
				"key": k,
			}).Error("Failed to delete map entry")
	}
}
<|endoftext|>


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

package main

import (
	"bytes"
	"log"
	"path/filepath"
	"testing"

	"github.com/golang/dep"
	"github.com/golang/dep/internal/gps"
	"github.com/golang/dep/internal/test"
	"github.com/pkg/errors"
)

const testGodepProjectRoot = "github.com/golang/notexist"

func TestGodepConfig_Import(t *testing.T) {
	h := test.NewHelper(t)
	defer h.Cleanup()

	cacheDir := "gps-repocache"
	h.TempDir(cacheDir)
	h.TempDir("src")
	h.TempDir(filepath.Join("src", testGodepProjectRoot))
	h.TempCopy(filepath.Join(testGodepProjectRoot, "Godeps", godepJSONName), "godep/Godeps.json")

	projectRoot := h.Path(testGodepProjectRoot)
	sm, err := gps.NewSourceManager(h.Path(cacheDir))
	h.Must(err)
	defer sm.Release()

	// Capture stderr so we can verify output
	verboseOutput := &bytes.Buffer{}
	logger := log.New(verboseOutput, "", 0)

	g := newGodepImporter(logger, false, sm) // Disable verbose so that we don't print values that change each test run
	if !g.HasDepMetadata(projectRoot) {
		t.Fatal("Expected the importer to detect godep configuration file")
	}

	m, l, err := g.Import(projectRoot, testGodepProjectRoot)
	h.Must(err)

	if m == nil {
		t.Fatal("Expected the manifest to be generated")
	}

	if l == nil {
		t.Fatal("Expected the lock to be generated")
	}

	goldenFile := "godep/expected_import_output.txt"
	got := verboseOutput.String()
	want := h.GetTestFileString(goldenFile)
	if want != got {
		if *test.UpdateGolden {
			if err := h.WriteTestFile(goldenFile, got); err != nil {
				t.Fatalf("%+v", errors.Wrapf(err, "Unable to write updated golden file %s", goldenFile))
			}
		} else {
			t.Fatalf("expected %s, got %s", want, got)
		}
	}
}

func TestGodepConfig_JsonLoad(t *testing.T) {
	// This is same as cmd/dep/testdata/Godeps.json
	wantJSON := godepJSON{
		Imports: []godepPackage{
			{
				ImportPath: "github.com/sdboyer/deptest",
				Rev:        "3f4c3bea144e112a69bbe5d8d01c1b09a544253f",
			},
			{
				ImportPath: "github.com/sdboyer/deptestdos",
				Rev:        "5c607206be5decd28e6263ffffdcee067266015e",
				Comment:    "v2.0.0",
			},
		},
	}

	h := test.NewHelper(t)
	defer h.Cleanup()

	ctx := newTestContext(h)

	h.TempCopy(filepath.Join(testGodepProjectRoot, "Godeps", godepJSONName), "godep/Godeps.json")

	projectRoot := h.Path(testGodepProjectRoot)

	g := newGodepImporter(ctx.Err, true, nil)
	err := g.load(projectRoot)
	if err != nil {
		t.Fatalf("Error while loading... %v", err)
	}

	if !equalImports(g.json.Imports, wantJSON.Imports) {
		t.Fatalf("Expected imports to be equal. \n\t(GOT): %v\n\t(WNT): %v", g.json.Imports, wantJSON.Imports)
	}
}

func TestGodepConfig_ConvertProject(t *testing.T) {
	h := test.NewHelper(t)
	defer h.Cleanup()

	ctx := newTestContext(h)
	sm, err := ctx.SourceManager()
	h.Must(err)
	defer sm.Release()

	g := newGodepImporter(discardLogger, true, sm)
	g.json = godepJSON{
		Imports: []godepPackage{
			{
				ImportPath: "github.com/sdboyer/deptest",
				// This revision has 2 versions attached to it, v1.0.0 & v0.8.0.
				Rev:     "ff2948a2ac8f538c4ecd55962e919d1e13e74baf",
				Comment: "v0.8.0",
			},
		},
	}

	manifest, lock, err := g.convert("")
	if err != nil {
		t.Fatal(err)
	}

	d, ok := manifest.Constraints["github.com/sdboyer/deptest"]
	if !ok {
		t.Fatal("Expected the manifest to have a dependency for 'github.com/sdboyer/deptest' but got none")
	}

	v := d.Constraint.String()
	if v != "^0.8.0" {
		t.Fatalf("Expected manifest constraint to be ^0.8.0, got %s", v)
	}

	p := lock.P[0]
	if p.Ident().ProjectRoot != "github.com/sdboyer/deptest" {
		t.Fatalf("Expected the lock to have a project for 'github.com/sdboyer/deptest' but got '%s'", p.Ident().ProjectRoot)
	}

	lv := p.Version()
	lpv, ok := lv.(gps.PairedVersion)
	if !ok {
		t.Fatalf("Expected locked version to be PairedVersion but got %T", lv)
	}

	rev := lpv.Revision()
	if rev != "ff2948a2ac8f538c4ecd55962e919d1e13e74baf" {
		t.Fatalf("Expected locked revision to be 'ff2948a2ac8f538c4ecd55962e919d1e13e74baf', got %s", rev)
	}

	ver := lpv.String()
	if ver != "v0.8.0" {
		t.Fatalf("Expected locked version to be 'v0.8.0', got %s", ver)
	}
}

func TestGodepConfig_ConvertProject_WithSemverSuffix(t *testing.T) {
	h := test.NewHelper(t)
	defer h.Cleanup()

	ctx := newTestContext(h)
	sm, err := ctx.SourceManager()
	h.Must(err)
	defer sm.Release()

	g := newGodepImporter(discardLogger, true, sm)
	g.json = godepJSON{
		Imports: []godepPackage{
			{
				ImportPath: "github.com/sdboyer/deptest",
				Rev:        "ff2948a2ac8f538c4ecd55962e919d1e13e74baf",
				Comment:    "v1.12.0-12-g2fd980e",
			},
		},
	}

	manifest, lock, err := g.convert("")
	if err != nil {
		t.Fatal(err)
	}

	d, ok := manifest.Constraints["github.com/sdboyer/deptest"]
	if !ok {
		t.Fatal("Expected the manifest to have a dependency for 'github.com/sdboyer/deptest' but got none")
	}

	v := d.Constraint.String()
	if v != ">=1.12.0, <=12.0.0-g2fd980e" {
		t.Fatalf("Expected manifest constraint to be >=1.12.0, <=12.0.0-g2fd980e, got %s", v)
	}

	p := lock.P[0]
	if p.Ident().ProjectRoot != "github.com/sdboyer/deptest" {
		t.Fatalf("Expected the lock to have a project for 'github.com/sdboyer/deptest' but got '%s'", p.Ident().ProjectRoot)
	}
}

func TestGodepConfig_ConvertProject_EmptyComment(t *testing.T) {
	h := test.NewHelper(t)
	defer h.Cleanup()
	h.TempDir("src")

	ctx := newTestContext(h)
	sm, err := ctx.SourceManager()
	h.Must(err)
	defer sm.Release()

	g := newGodepImporter(discardLogger, true, sm)
	g.json = godepJSON{
		Imports: []godepPackage{
			{
				ImportPath: "github.com/sdboyer/deptest",
				// This revision has 2 versions attached to it, v1.0.0 & v0.8.0.
				Rev: "ff2948a2ac8f538c4ecd55962e919d1e13e74baf",
			},
		},
	}

	manifest, lock, err := g.convert("")
	if err != nil {
		t.Fatal(err)
	}

	d, ok := manifest.Constraints["github.com/sdboyer/deptest"]
	if !ok {
		t.Fatal("Expected the manifest to have a dependency for 'github.com/sdboyer/deptest' but got none")
	}

	v := d.Constraint.String()
	if v != "^1.0.0" {
		t.Fatalf("Expected manifest constraint to be ^1.0.0, got %s", v)
	}

	p := lock.P[0]
	if p.Ident().ProjectRoot != "github.com/sdboyer/deptest" {
		t.Fatalf("Expected the lock to have a project for 'github.com/sdboyer/deptest' but got '%s'", p.Ident().ProjectRoot)
	}

	lv := p.Version()
	lpv, ok := lv.(gps.PairedVersion)
	if !ok {
		t.Fatalf("Expected locked version to be PairedVersion but got %T", lv)
	}

	rev := lpv.Revision()
	if rev != "ff2948a2ac8f538c4ecd55962e919d1e13e74baf" {
		t.Fatalf("Expected locked revision to be 'ff2948a2ac8f538c4ecd55962e919d1e13e74baf', got %s", rev)
	}

	ver := lpv.String()
	if ver != "v1.0.0" {
		t.Fatalf("Expected locked version to be 'v1.0.0', got %s", ver)
	}
}

func TestGodepConfig_Convert_BadInput_EmptyPackageName(t *testing.T) {
	g := newGodepImporter(discardLogger, true, nil)
	g.json = godepJSON{
		Imports: []godepPackage{{ImportPath: ""}},
	}

	_, _, err := g.convert("")
	if err == nil {
		t.Fatal("Expected conversion to fail because the ImportPath is empty")
	}
}

func TestGodepConfig_Convert_BadInput_EmptyRev(t *testing.T) {
	h := test.NewHelper(t)
	defer h.Cleanup()
	h.TempDir("src")

	ctx := newTestContext(h)
	sm, err := ctx.SourceManager()
	h.Must(err)
	defer sm.Release()

	g := newGodepImporter(discardLogger, true, sm)
	g.json = godepJSON{
		Imports: []godepPackage{
			{
				ImportPath: "github.com/sdboyer/deptest",
			},
		},
	}

	_, _, err = g.convert("")
	if err == nil {
		t.Fatal("Expected conversion to fail because the Rev is empty")
	}
}

func TestGodepConfig_Convert_SubPackages(t *testing.T) {
	h := test.NewHelper(t)
	defer h.Cleanup()
	h.TempDir("src")

	ctx := newTestContext(h)
	sm, err := ctx.SourceManager()
	h.Must(err)
	defer sm.Release()

	g := newGodepImporter(discardLogger, true, sm)
	g.json = godepJSON{
		Imports: []godepPackage{
			{
				ImportPath: "github.com/sdboyer/deptest",
				Rev:        "ff2948a2ac8f538c4ecd55962e919d1e13e74baf",
			},
			{
				ImportPath: "github.com/sdboyer/deptest/foo",
				Rev:        "ff2948a2ac8f538c4ecd55962e919d1e13e74baf",
			},
		},
	}

	manifest, lock, err := g.convert("")
	if err != nil {
		t.Fatal(err)
	}

	if _, present := manifest.Constraints["github.com/sdboyer/deptest"]; !present {
		t.Fatal("Expected the manifest to have a dependency for 'github.com/sdboyer/deptest'")
	}

	if _, present := manifest.Constraints["github.com/sdboyer/deptest/foo"]; present {
		t.Fatal("Expected the manifest to not have a dependency for 'github.com/sdboyer/deptest/foo'")
	}

	if len(lock.P) != 1 {
		t.Fatalf("Expected lock to have 1 project, got %v", len(lock.P))
	}

	if string(lock.P[0].Ident().ProjectRoot) != "github.com/sdboyer/deptest" {
		t.Fatal("Expected lock to have 'github.com/sdboyer/deptest'")
	}
}

func TestGodepConfig_ProjectExistsInLock(t *testing.T) {
	lock := &dep.Lock{}
	pi := gps.ProjectIdentifier{ProjectRoot: gps.ProjectRoot("github.com/sdboyer/deptest")}
	ver := gps.NewVersion("v1.0.0")
	lock.P = append(lock.P, gps.NewLockedProject(pi, ver, nil))

	cases := []struct {
		importPath string
		want       bool
	}{
		{
			importPath: "github.com/sdboyer/deptest",
			want:       true,
		},
		{
			importPath: "github.com/golang/notexist",
			want:       false,
		},
	}

	for _, c := range cases {
		result := projectExistsInLock(lock, c.importPath)

		if result != c.want {
			t.Fatalf("projectExistsInLock result is not as expected: \n\t(GOT) %v \n\t(WNT) %v", result, c.want)
		}
	}
}

// Compares two slices of godepPackage and checks if they are equal.
func equalImports(a, b []godepPackage) bool {

	if a == nil && b == nil {
		return true
	}

	if a == nil || b == nil {
		return false
	}

	if len(a) != len(b) {
		return false
	}

	for i := range a {
		if a[i] != b[i] {
			return false
		}
	}

	return true
}
<commit_msg>Fix testdata<commit_after>// Copyright 2017 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package main

import (
	"bytes"
	"log"
	"path/filepath"
	"testing"

	"github.com/golang/dep"
	"github.com/golang/dep/internal/gps"
	"github.com/golang/dep/internal/test"
	"github.com/pkg/errors"
)

const testGodepProjectRoot = "github.com/golang/notexist"

func TestGodepConfig_Import(t *testing.T) {
	h := test.NewHelper(t)
	defer h.Cleanup()

	cacheDir := "gps-repocache"
	h.TempDir(cacheDir)
	h.TempDir("src")
	h.TempDir(filepath.Join("src", testGodepProjectRoot))
	h.TempCopy(filepath.Join(testGodepProjectRoot, "Godeps", godepJSONName), "godep/Godeps.json")

	projectRoot := h.Path(testGodepProjectRoot)
	sm, err := gps.NewSourceManager(h.Path(cacheDir))
	h.Must(err)
	defer sm.Release()

	// Capture stderr so we can verify output
	verboseOutput := &bytes.Buffer{}
	logger := log.New(verboseOutput, "", 0)

	g := newGodepImporter(logger, false, sm) // Disable verbose so that we don't print values that change each test run
	if !g.HasDepMetadata(projectRoot) {
		t.Fatal("Expected the importer to detect godep configuration file")
	}

	m, l, err := g.Import(projectRoot, testGodepProjectRoot)
	h.Must(err)

	if m == nil {
		t.Fatal("Expected the manifest to be generated")
	}

	if l == nil {
		t.Fatal("Expected the lock to be generated")
	}

	goldenFile := "godep/expected_import_output.txt"
	got := verboseOutput.String()
	want := h.GetTestFileString(goldenFile)
	if want != got {
		if *test.UpdateGolden {
			if err := h.WriteTestFile(goldenFile, got); err != nil {
				t.Fatalf("%+v", errors.Wrapf(err, "Unable to write updated golden file %s", goldenFile))
			}
		} else {
			t.Fatalf("expected %s, got %s", want, got)
		}
	}
}

func TestGodepConfig_JsonLoad(t *testing.T) {
	// This is same as cmd/dep/testdata/Godeps.json
	wantJSON := godepJSON{
		Imports: []godepPackage{
			{
				ImportPath: "github.com/sdboyer/deptest",
				Rev:        "3f4c3bea144e112a69bbe5d8d01c1b09a544253f",
			},
			{
				ImportPath: "github.com/sdboyer/deptestdos",
				Rev:        "5c607206be5decd28e6263ffffdcee067266015e",
				Comment:    "v2.0.0",
			},
		},
	}

	h := test.NewHelper(t)
	defer h.Cleanup()

	ctx := newTestContext(h)

	h.TempCopy(filepath.Join(testGodepProjectRoot, "Godeps", godepJSONName), "godep/Godeps.json")

	projectRoot := h.Path(testGodepProjectRoot)

	g := newGodepImporter(ctx.Err, true, nil)
	err := g.load(projectRoot)
	if err != nil {
		t.Fatalf("Error while loading... %v", err)
	}

	if !equalImports(g.json.Imports, wantJSON.Imports) {
		t.Fatalf("Expected imports to be equal. \n\t(GOT): %v\n\t(WNT): %v", g.json.Imports, wantJSON.Imports)
	}
}

func TestGodepConfig_ConvertProject(t *testing.T) {
	h := test.NewHelper(t)
	defer h.Cleanup()

	ctx := newTestContext(h)
	sm, err := ctx.SourceManager()
	h.Must(err)
	defer sm.Release()

	g := newGodepImporter(discardLogger, true, sm)
	g.json = godepJSON{
		Imports: []godepPackage{
			{
				ImportPath: "github.com/sdboyer/deptest",
				// This revision has 2 versions attached to it, v1.0.0 & v0.8.0.
				Rev:     "ff2948a2ac8f538c4ecd55962e919d1e13e74baf",
				Comment: "v0.8.0",
			},
		},
	}

	manifest, lock, err := g.convert("")
	if err != nil {
		t.Fatal(err)
	}

	d, ok := manifest.Constraints["github.com/sdboyer/deptest"]
	if !ok {
		t.Fatal("Expected the manifest to have a dependency for 'github.com/sdboyer/deptest' but got none")
	}

	v := d.Constraint.String()
	if v != "^0.8.0" {
		t.Fatalf("Expected manifest constraint to be ^0.8.0, got %s", v)
	}

	p := lock.P[0]
	if p.Ident().ProjectRoot != "github.com/sdboyer/deptest" {
		t.Fatalf("Expected the lock to have a project for 'github.com/sdboyer/deptest' but got '%s'", p.Ident().ProjectRoot)
	}

	lv := p.Version()
	lpv, ok := lv.(gps.PairedVersion)
	if !ok {
		t.Fatalf("Expected locked version to be PairedVersion but got %T", lv)
	}

	rev := lpv.Revision()
	if rev != "ff2948a2ac8f538c4ecd55962e919d1e13e74baf" {
		t.Fatalf("Expected locked revision to be 'ff2948a2ac8f538c4ecd55962e919d1e13e74baf', got %s", rev)
	}

	ver := lpv.String()
	if ver != "v0.8.0" {
		t.Fatalf("Expected locked version to be 'v0.8.0', got %s", ver)
	}
}

func TestGodepConfig_ConvertProject_WithSemverSuffix(t *testing.T) {
	h := test.NewHelper(t)
	defer h.Cleanup()

	ctx := newTestContext(h)
	sm, err := ctx.SourceManager()
	h.Must(err)
	defer sm.Release()

	g := newGodepImporter(discardLogger, true, sm)
	g.json = godepJSON{
		Imports: []godepPackage{
			{
				ImportPath: "github.com/sdboyer/deptest",
				Rev:        "ff2948a2ac8f538c4ecd55962e919d1e13e74baf",
				Comment:    "v1.12.0-12-g2fd980e",
			},
		},
	}

	manifest, lock, err := g.convert("")
	if err != nil {
		t.Fatal(err)
	}

	d, ok := manifest.Constraints["github.com/sdboyer/deptest"]
	if !ok {
		t.Fatal("Expected the manifest to have a dependency for 'github.com/sdboyer/deptest' but got none")
	}

	v := d.Constraint.String()
	if v != "^1.12.0-12-g2fd980e" {
		t.Fatalf("Expected manifest constraint to be ^1.12.0-12-g2fd980e, got %s", v)
	}

	p := lock.P[0]
	if p.Ident().ProjectRoot != "github.com/sdboyer/deptest" {
		t.Fatalf("Expected the lock to have a project for 'github.com/sdboyer/deptest' but got '%s'", p.Ident().ProjectRoot)
	}
}

func TestGodepConfig_ConvertProject_EmptyComment(t *testing.T) {
	h := test.NewHelper(t)
	defer h.Cleanup()
	h.TempDir("src")

	ctx := newTestContext(h)
	sm, err := ctx.SourceManager()
	h.Must(err)
	defer sm.Release()

	g := newGodepImporter(discardLogger, true, sm)
	g.json = godepJSON{
		Imports: []godepPackage{
			{
				ImportPath: "github.com/sdboyer/deptest",
				// This revision has 2 versions attached to it, v1.0.0 & v0.8.0.
				Rev: "ff2948a2ac8f538c4ecd55962e919d1e13e74baf",
			},
		},
	}

	manifest, lock, err := g.convert("")
	if err != nil {
		t.Fatal(err)
	}

	d, ok := manifest.Constraints["github.com/sdboyer/deptest"]
	if !ok {
		t.Fatal("Expected the manifest to have a dependency for 'github.com/sdboyer/deptest' but got none")
	}

	v := d.Constraint.String()
	if v != "^1.0.0" {
		t.Fatalf("Expected manifest constraint to be ^1.0.0, got %s", v)
	}

	p := lock.P[0]
	if p.Ident().ProjectRoot != "github.com/sdboyer/deptest" {
		t.Fatalf("Expected the lock to have a project for 'github.com/sdboyer/deptest' but got '%s'", p.Ident().ProjectRoot)
	}

	lv := p.Version()
	lpv, ok := lv.(gps.PairedVersion)
	if !ok {
		t.Fatalf("Expected locked version to be PairedVersion but got %T", lv)
	}

	rev := lpv.Revision()
	if rev != "ff2948a2ac8f538c4ecd55962e919d1e13e74baf" {
		t.Fatalf("Expected locked revision to be 'ff2948a2ac8f538c4ecd55962e919d1e13e74baf', got %s", rev)
	}

	ver := lpv.String()
	if ver != "v1.0.0" {
		t.Fatalf("Expected locked version to be 'v1.0.0', got %s", ver)
	}
}

func TestGodepConfig_Convert_BadInput_EmptyPackageName(t *testing.T) {
	g := newGodepImporter(discardLogger, true, nil)
	g.json = godepJSON{
		Imports: []godepPackage{{ImportPath: ""}},
	}

	_, _, err := g.convert("")
	if err == nil {
		t.Fatal("Expected conversion to fail because the ImportPath is empty")
	}
}

func TestGodepConfig_Convert_BadInput_EmptyRev(t *testing.T) {
	h := test.NewHelper(t)
	defer h.Cleanup()
	h.TempDir("src")

	ctx := newTestContext(h)
	sm, err := ctx.SourceManager()
	h.Must(err)
	defer sm.Release()

	g := newGodepImporter(discardLogger, true, sm)
	g.json = godepJSON{
		Imports: []godepPackage{
			{
				ImportPath: "github.com/sdboyer/deptest",
			},
		},
	}

	_, _, err = g.convert("")
	if err == nil {
		t.Fatal("Expected conversion to fail because the Rev is empty")
	}
}

func TestGodepConfig_Convert_SubPackages(t *testing.T) {
	h := test.NewHelper(t)
	defer h.Cleanup()
	h.TempDir("src")

	ctx := newTestContext(h)
	sm, err := ctx.SourceManager()
	h.Must(err)
	defer sm.Release()

	g := newGodepImporter(discardLogger, true, sm)
	g.json = godepJSON{
		Imports: []godepPackage{
			{
				ImportPath: "github.com/sdboyer/deptest",
				Rev:        "ff2948a2ac8f538c4ecd55962e919d1e13e74baf",
			},
			{
				ImportPath: "github.com/sdboyer/deptest/foo",
				Rev:        "ff2948a2ac8f538c4ecd55962e919d1e13e74baf",
			},
		},
	}

	manifest, lock, err := g.convert("")
	if err != nil {
		t.Fatal(err)
	}

	if _, present := manifest.Constraints["github.com/sdboyer/deptest"]; !present {
		t.Fatal("Expected the manifest to have a dependency for 'github.com/sdboyer/deptest'")
	}

	if _, present := manifest.Constraints["github.com/sdboyer/deptest/foo"]; present {
		t.Fatal("Expected the manifest to not have a dependency for 'github.com/sdboyer/deptest/foo'")
	}

	if len(lock.P) != 1 {
		t.Fatalf("Expected lock to have 1 project, got %v", len(lock.P))
	}

	if string(lock.P[0].Ident().ProjectRoot) != "github.com/sdboyer/deptest" {
		t.Fatal("Expected lock to have 'github.com/sdboyer/deptest'")
	}
}

func TestGodepConfig_ProjectExistsInLock(t *testing.T) {
	lock := &dep.Lock{}
	pi := gps.ProjectIdentifier{ProjectRoot: gps.ProjectRoot("github.com/sdboyer/deptest")}
	ver := gps.NewVersion("v1.0.0")
	lock.P = append(lock.P, gps.NewLockedProject(pi, ver, nil))

	cases := []struct {
		importPath string
		want       bool
	}{
		{
			importPath: "github.com/sdboyer/deptest",
			want:       true,
		},
		{
			importPath: "github.com/golang/notexist",
			want:       false,
		},
	}

	for _, c := range cases {
		result := projectExistsInLock(lock, c.importPath)

		if result != c.want {
			t.Fatalf("projectExistsInLock result is not as expected: \n\t(GOT) %v \n\t(WNT) %v", result, c.want)
		}
	}
}

// Compares two slices of godepPackage and checks if they are equal.
func equalImports(a, b []godepPackage) bool {

	if a == nil && b == nil {
		return true
	}

	if a == nil || b == nil {
		return false
	}

	if len(a) != len(b) {
		return false
	}

	for i := range a {
		if a[i] != b[i] {
			return false
		}
	}

	return true
}
<|endoftext|>


<commit_before>// Copyright (c) 2016- Daniel Oaks <daniel@danieloaks.net>
// released under the MIT license

package main

import (
	"fmt"
	"log"

	"github.com/DanielOaks/oragono/irc"
	"github.com/DanielOaks/oragono/mkcerts"
	"github.com/DanielOaks/oragono/web"
	"github.com/docopt/docopt-go"
)

func main() {
	version := irc.SemVer
	usage := `oragono-web.
Usage:
	oragono-web mkcerts [--conf <filename>] [--quiet]
	oragono-web run [--conf <filename>] [--quiet]
	oragono-web -h | --help
	oragono-web --version
Options:
	--conf <filename>  Configuration file to use [default: web.yaml].
	--quiet            Don't show startup/shutdown lines.
	-h --help          Show this screen.
	--version          Show version.`

	arguments, _ := docopt.Parse(usage, nil, true, version, false)

	configfile := arguments["--conf"].(string)
	config, err := web.LoadConfig(configfile)
	if err != nil {
		log.Fatal("Config file did not load successfully:", err.Error())
	}

	if arguments["mkcerts"].(bool) {
		if !arguments["--quiet"].(bool) {
			log.Println("making self-signed certificates")
		}

		for name, conf := range config.TLSListenersConf {
			log.Printf(" making cert for %s listener\n", name)
			host := config.Host
			err := mkcerts.CreateCert("Oragono web interface", host, conf.Cert, conf.Key)
			if err == nil {
				if !arguments["--quiet"].(bool) {
					log.Printf("  Certificate created at %s : %s\n", conf.Cert, conf.Key)
				}
			} else {
				log.Fatal("  Could not create certificate:", err.Error())
			}
		}
	} else if arguments["run"].(bool) {
		irc.Log.SetLevel(config.Log)
		server := web.NewServer(config)
		if server == nil {
			log.Println("Could not load server")
			return
		}
		if !arguments["--quiet"].(bool) {
			log.Println(fmt.Sprintf("Oragono web interface v%s running", irc.SemVer))
			defer log.Println(irc.SemVer, "exiting")
		}
		server.Run()
	}
}
<commit_msg>Switch to new repo<commit_after>// Copyright (c) 2016- Daniel Oaks <daniel@danieloaks.net>
// released under the MIT license

package main

import (
	"fmt"
	"log"

	"github.com/docopt/docopt-go"
	"github.com/oragono/oragono/mkcerts"
	"github.com/oragono/oragono/web"
)

func main() {
	version := irc.SemVer
	usage := `oragono-web.
Usage:
	oragono-web mkcerts [--conf <filename>] [--quiet]
	oragono-web run [--conf <filename>] [--quiet]
	oragono-web -h | --help
	oragono-web --version
Options:
	--conf <filename>  Configuration file to use [default: web.yaml].
	--quiet            Don't show startup/shutdown lines.
	-h --help          Show this screen.
	--version          Show version.`

	arguments, _ := docopt.Parse(usage, nil, true, version, false)

	configfile := arguments["--conf"].(string)
	config, err := web.LoadConfig(configfile)
	if err != nil {
		log.Fatal("Config file did not load successfully:", err.Error())
	}

	if arguments["mkcerts"].(bool) {
		if !arguments["--quiet"].(bool) {
			log.Println("making self-signed certificates")
		}

		for name, conf := range config.TLSListenersConf {
			log.Printf(" making cert for %s listener\n", name)
			host := config.Host
			err := mkcerts.CreateCert("Oragono web interface", host, conf.Cert, conf.Key)
			if err == nil {
				if !arguments["--quiet"].(bool) {
					log.Printf("  Certificate created at %s : %s\n", conf.Cert, conf.Key)
				}
			} else {
				log.Fatal("  Could not create certificate:", err.Error())
			}
		}
	} else if arguments["run"].(bool) {
		irc.Log.SetLevel(config.Log)
		server := web.NewServer(config)
		if server == nil {
			log.Println("Could not load server")
			return
		}
		if !arguments["--quiet"].(bool) {
			log.Println(fmt.Sprintf("Oragono web interface v%s running", irc.SemVer))
			defer log.Println(irc.SemVer, "exiting")
		}
		server.Run()
	}
}
<|endoftext|>