<commit_before>package graph

import (
	"errors"
	"fmt"
	"io"
	"strings"

	mapset "github.com/deckarep/golang-set"
)

// ErrCircularDependency is returned when the graph cannot be
// topologically sorted because of circular dependencies
var ErrCircularDependency = errors.New("Circular dependency found in graph")

// Graph represents a DAG graph
type Graph struct {
	nodes map[string]*Node
}

// New creates a new DAG graph
func New() *Graph {
	g := &Graph{
		nodes: make(map[string]*Node),
	}

	return g
}

// AddNode adds nodes to the graph
func (g *Graph) AddNode(nodes ...*Node) {
	for _, node := range nodes {
		g.nodes[node.Name] = node
	}
}

// AddEdge connects a node with other nodes in the graph
func (g *Graph) AddEdge(node *Node, edges ...*Node) {
	for _, edge := range edges {
		node.edges = append(node.edges, edge)
	}
}

// GetNode retrieves the node from the graph with the given name
func (g *Graph) GetNode(name string) (*Node, bool) {
	n, ok := g.nodes[name]

	return n, ok
}

// Sort performs a topological sort of the graph
// https://en.wikipedia.org/wiki/Topological_sorting
//
// If the graph can be topologically sorted the result will
// contain the sorted nodes.
//
// If the graph cannot be sorted in case of circular dependencies,
// then the result will contain the remaining nodes from the graph,
// which are the ones causing the circular dependency.
func (g *Graph) Sort() ([]*Node, error) {
	var sorted []*Node

	// Iteratively find and remove nodes from the graph which have no edges.
	// If at some point there are still nodes in the graph and we cannot find
	// nodes without edges, that means we have a circular dependency
	for len(g.nodes) > 0 {
		// Contains the ready nodes, which have no edges to other nodes
		//ready := make([]*Node, 0)
		ready := mapset.NewSet()

		// Find the nodes with no edges
		for _, node := range g.nodes {
			if len(node.edges) == 0 {
				ready.Add(node)
			}
		}

		// If there aren't any ready nodes, then we have a cicular dependency
		if ready.Cardinality() == 0 {
			// The remaining nodes in the graph are the ones causing the
			// circular dependency.
			var remaining []*Node
			for _, n := range g.nodes {
				remaining = append(remaining, n)
			}
			return remaining, ErrCircularDependency
		}

		// Remove the ready nodes and add them to the sorted result
		for item := range ready.Iter() {
			node := item.(*Node)
			delete(g.nodes, node.Name)
			sorted = append(sorted, node)
		}

		// Remove ready nodes from the remaining node edges as well
		for _, node := range g.nodes {
			// Add the remaining nodes in a set
			currentEdgeSet := mapset.NewSet()
			for _, edge := range node.edges {
				currentEdgeSet.Add(edge)
			}

			newEdgeSet := currentEdgeSet.Difference(ready)
			node.edges = make([]*Node, 0)
			for edge := range newEdgeSet.Iter() {
				node.edges = append(node.edges, edge.(*Node))
			}
		}
	}

	return sorted, nil
}

// AsDot generates a DOT representation for the graph
// https://en.wikipedia.org/wiki/DOT_(graph_description_language)
func (g *Graph) AsDot(name string, w io.Writer) {
	var dotNodes []string
	dotHeader := []byte(fmt.Sprintf("digraph %s {\n", name))
	dotFooter := []byte("\n}\n")

	for _, node := range g.nodes {
		// Insert the node as the first item in the DOT object
		var obj []string
		obj = append(obj, fmt.Sprintf("\t%q", node.Name))

		// Now add the node edges as well
		for _, edge := range node.edges {
			obj = append(obj, fmt.Sprintf("%q", edge.Name))
		}

		dotObj := strings.Join(obj, " -> ")
		dotNodes = append(dotNodes, dotObj)
	}

	w.Write(dotHeader)
	w.Write([]byte(strings.Join(dotNodes, "\n")))
	w.Write(dotFooter)
}
<commit_msg>graph: fix dot dependency representation<commit_after>package graph

import (
	"errors"
	"fmt"
	"io"

	mapset "github.com/deckarep/golang-set"
)

// ErrCircularDependency is returned when the graph cannot be
// topologically sorted because of circular dependencies
var ErrCircularDependency = errors.New("Circular dependency found in graph")

// Graph represents a DAG graph
type Graph struct {
	nodes map[string]*Node
}

// New creates a new DAG graph
func New() *Graph {
	g := &Graph{
		nodes: make(map[string]*Node),
	}

	return g
}

// AddNode adds nodes to the graph
func (g *Graph) AddNode(nodes ...*Node) {
	for _, node := range nodes {
		g.nodes[node.Name] = node
	}
}

// AddEdge connects a node with other nodes in the graph
func (g *Graph) AddEdge(node *Node, edges ...*Node) {
	for _, edge := range edges {
		node.edges = append(node.edges, edge)
	}
}

// GetNode retrieves the node from the graph with the given name
func (g *Graph) GetNode(name string) (*Node, bool) {
	n, ok := g.nodes[name]

	return n, ok
}

// Sort performs a topological sort of the graph
// https://en.wikipedia.org/wiki/Topological_sorting
//
// If the graph can be topologically sorted the result will
// contain the sorted nodes.
//
// If the graph cannot be sorted in case of circular dependencies,
// then the result will contain the remaining nodes from the graph,
// which are the ones causing the circular dependency.
func (g *Graph) Sort() ([]*Node, error) {
	var sorted []*Node

	// Iteratively find and remove nodes from the graph which have no edges.
	// If at some point there are still nodes in the graph and we cannot find
	// nodes without edges, that means we have a circular dependency
	for len(g.nodes) > 0 {
		// Contains the ready nodes, which have no edges to other nodes
		//ready := make([]*Node, 0)
		ready := mapset.NewSet()

		// Find the nodes with no edges
		for _, node := range g.nodes {
			if len(node.edges) == 0 {
				ready.Add(node)
			}
		}

		// If there aren't any ready nodes, then we have a cicular dependency
		if ready.Cardinality() == 0 {
			// The remaining nodes in the graph are the ones causing the
			// circular dependency.
			var remaining []*Node
			for _, n := range g.nodes {
				remaining = append(remaining, n)
			}
			return remaining, ErrCircularDependency
		}

		// Remove the ready nodes and add them to the sorted result
		for item := range ready.Iter() {
			node := item.(*Node)
			delete(g.nodes, node.Name)
			sorted = append(sorted, node)
		}

		// Remove ready nodes from the remaining node edges as well
		for _, node := range g.nodes {
			// Add the remaining nodes in a set
			currentEdgeSet := mapset.NewSet()
			for _, edge := range node.edges {
				currentEdgeSet.Add(edge)
			}

			newEdgeSet := currentEdgeSet.Difference(ready)
			node.edges = make([]*Node, 0)
			for edge := range newEdgeSet.Iter() {
				node.edges = append(node.edges, edge.(*Node))
			}
		}
	}

	return sorted, nil
}

// AsDot generates a DOT representation for the graph
// https://en.wikipedia.org/wiki/DOT_(graph_description_language)
func (g *Graph) AsDot(name string, w io.Writer) {

	w.Write([]byte(fmt.Sprintf("digraph %s {\n", name)))
	w.Write([]byte("\tnodesep=1.0\n"))
	w.Write([]byte("\tnode [shape=box]\n"))
	w.Write([]byte("\tedge [style=dashed]\n"))

	for _, node := range g.nodes {
		for _, edge := range node.edges {
			w.Write([]byte(fmt.Sprintf("\t%q -> %q\n", node.Name, edge.Name)))
		}
	}

	w.Write([]byte("}\n"))
}
<|endoftext|>


<commit_before>//===-- llvm-go.go - go tool wrapper for LLVM -----------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// This tool lets us build LLVM components within the tree by setting up a
// $GOPATH that resembles a tree fetched in the normal way with "go get".
//
//===----------------------------------------------------------------------===//

package main

import (
	"fmt"
	"io/ioutil"
	"os"
	"os/exec"
	"path/filepath"
	"runtime"
	"strings"
)

const (
	linkmodeComponentLibs = "component-libs"
	linkmodeDylib         = "dylib"
)

type pkg struct {
	llvmpath, pkgpath string
}

var packages = []pkg{
	{"bindings/go/llvm", "llvm.org/llvm/bindings/go/llvm"},
}

type compilerFlags struct {
	cpp, cxx, ld string
}

var components = []string{
	"all-targets",
	"analysis",
	"asmparser",
	"asmprinter",
	"bitreader",
	"bitwriter",
	"codegen",
	"core",
	"coroutines",
	"debuginfodwarf",
	"executionengine",
	"instrumentation",
	"interpreter",
	"ipo",
	"irreader",
	"linker",
	"mc",
	"mcjit",
	"objcarcopts",
	"option",
	"profiledata",
	"scalaropts",
	"support",
	"target",
}

func llvmConfig(args ...string) string {
	configpath := os.Getenv("LLVM_CONFIG")
	if configpath == "" {
		bin, _ := filepath.Split(os.Args[0])
		configpath = filepath.Join(bin, "llvm-config")
	}

	cmd := exec.Command(configpath, args...)
	cmd.Stderr = os.Stderr
	out, err := cmd.Output()
	if err != nil {
		panic(err.Error())
	}

	outstr := string(out)
	outstr = strings.TrimSuffix(outstr, "\n")
	outstr = strings.Replace(outstr, "\n", " ", -1)
	return outstr
}

func llvmFlags() compilerFlags {
	args := append([]string{"--ldflags", "--libs", "--system-libs"}, components...)
	ldflags := llvmConfig(args...)
	if runtime.GOOS != "darwin" {
		// OS X doesn't like -rpath with cgo. See:
		// https://github.com/golang/go/issues/7293
		ldflags = "-Wl,-rpath," + llvmConfig("--libdir") + " " + ldflags
	}
	return compilerFlags{
		cpp: llvmConfig("--cppflags"),
		cxx: "-std=c++11",
		ld:  ldflags,
	}
}

func addTag(args []string, tag string) []string {
	args = append([]string{}, args...)
	addedTag := false
	for i, a := range args {
		if strings.HasPrefix(a, "-tags=") {
			args[i] = a + " " + tag
			addedTag = true
		} else if a == "-tags" && i+1 < len(args) {
			args[i+1] = args[i+1] + " " + tag
			addedTag = true
		}
	}
	if !addedTag {
		args = append([]string{args[0], "-tags", tag}, args[1:]...)
	}
	return args
}

func printComponents() {
	fmt.Println(strings.Join(components, " "))
}

func printConfig() {
	flags := llvmFlags()

	fmt.Printf(`// +build !byollvm

// This file is generated by llvm-go, do not edit.

package llvm

/*
#cgo CPPFLAGS: %s
#cgo CXXFLAGS: %s
#cgo LDFLAGS: %s
*/
import "C"

type (run_build_sh int)
`, flags.cpp, flags.cxx, flags.ld)
}

func runGoWithLLVMEnv(args []string, cc, cxx, gocmd, llgo, cppflags, cxxflags, ldflags string, packages []pkg) {
	args = addTag(args, "byollvm")

	srcdir := llvmConfig("--src-root")

	tmpgopath, err := ioutil.TempDir("", "gopath")
	if err != nil {
		panic(err.Error())
	}

	for _, p := range packages {
		path := filepath.Join(tmpgopath, "src", p.pkgpath)
		err := os.MkdirAll(filepath.Dir(path), os.ModePerm)
		if err != nil {
			panic(err.Error())
		}

		abspath := p.llvmpath
		if !filepath.IsAbs(abspath) {
			abspath = filepath.Join(srcdir, abspath)
		}

		err = os.Symlink(abspath, path)
		if err != nil {
			panic(err.Error())
		}
	}

	newpath := os.Getenv("PATH")

	newgopathlist := []string{tmpgopath}
	newgopathlist = append(newgopathlist, filepath.SplitList(os.Getenv("GOPATH"))...)
	newgopath := strings.Join(newgopathlist, string(filepath.ListSeparator))

	flags := llvmFlags()

	newenv := []string{
		"CC=" + cc,
		"CXX=" + cxx,
		"CGO_CPPFLAGS=" + flags.cpp + " " + cppflags,
		"CGO_CXXFLAGS=" + flags.cxx + " " + cxxflags,
		"CGO_LDFLAGS=" + flags.ld + " " + ldflags,
		"GOPATH=" + newgopath,
		"PATH=" + newpath,
	}
	if llgo != "" {
		newenv = append(newenv, "GCCGO="+llgo)
	}

	for _, v := range os.Environ() {
		if !strings.HasPrefix(v, "CC=") &&
			!strings.HasPrefix(v, "CXX=") &&
			!strings.HasPrefix(v, "CGO_CPPFLAGS=") &&
			!strings.HasPrefix(v, "CGO_CXXFLAGS=") &&
			!strings.HasPrefix(v, "CGO_LDFLAGS=") &&
			!strings.HasPrefix(v, "GCCGO=") &&
			!strings.HasPrefix(v, "GOPATH=") &&
			!strings.HasPrefix(v, "PATH=") {
			newenv = append(newenv, v)
		}
	}

	gocmdpath, err := exec.LookPath(gocmd)
	if err != nil {
		panic(err.Error())
	}

	proc, err := os.StartProcess(gocmdpath, append([]string{gocmd}, args...),
		&os.ProcAttr{
			Env:   newenv,
			Files: []*os.File{os.Stdin, os.Stdout, os.Stderr},
		})
	if err != nil {
		panic(err.Error())
	}
	ps, err := proc.Wait()
	if err != nil {
		panic(err.Error())
	}

	os.RemoveAll(tmpgopath)

	if !ps.Success() {
		os.Exit(1)
	}
}

func usage() {
	fmt.Println(`Usage: llvm-go subcommand [flags]

Available subcommands: build get install run test print-components print-config`)
	os.Exit(0)
}

func main() {
	cc := os.Getenv("CC")
	cxx := os.Getenv("CXX")
	cppflags := os.Getenv("CGO_CPPFLAGS")
	cxxflags := os.Getenv("CGO_CXXFLAGS")
	ldflags := os.Getenv("CGO_LDFLAGS")
	gocmd := "go"
	llgo := ""
	packagesString := ""

	flags := []struct {
		name string
		dest *string
	}{
		{"cc", &cc},
		{"cxx", &cxx},
		{"go", &gocmd},
		{"llgo", &llgo},
		{"cppflags", &cppflags},
		{"ldflags", &ldflags},
		{"packages", &packagesString},
	}

	args := os.Args[1:]
LOOP:
	for {
		if len(args) == 0 {
			usage()
		}
		for _, flag := range flags {
			if strings.HasPrefix(args[0], flag.name+"=") {
				*flag.dest = args[0][len(flag.name)+1:]
				args = args[1:]
				continue LOOP
			}
		}
		break
	}

	packages := packages
	if packagesString != "" {
		for _, field := range strings.Fields(packagesString) {
			pos := strings.IndexRune(field, '=')
			if pos == -1 {
				fmt.Fprintf(os.Stderr, "invalid packages value %q, expected 'pkgpath=llvmpath [pkgpath=llvmpath ...]'\n", packagesString)
				os.Exit(1)
			}
			packages = append(packages, pkg{
				pkgpath:  field[:pos],
				llvmpath: field[pos+1:],
			})
		}
	}

	switch args[0] {
	case "build", "get", "install", "run", "test":
		runGoWithLLVMEnv(args, cc, cxx, gocmd, llgo, cppflags, cxxflags, ldflags, packages)
	case "print-components":
		printComponents()
	case "print-config":
		printConfig()
	default:
		usage()
	}
}
<commit_msg>[tests][go]Add -stdlib=libc++ to build GO test if LLVM is built with libc++<commit_after>//===-- llvm-go.go - go tool wrapper for LLVM -----------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// This tool lets us build LLVM components within the tree by setting up a
// $GOPATH that resembles a tree fetched in the normal way with "go get".
//
//===----------------------------------------------------------------------===//

package main

import (
	"fmt"
	"io/ioutil"
	"os"
	"os/exec"
	"path/filepath"
	"runtime"
	"strings"
)

const (
	linkmodeComponentLibs = "component-libs"
	linkmodeDylib         = "dylib"
)

type pkg struct {
	llvmpath, pkgpath string
}

var packages = []pkg{
	{"bindings/go/llvm", "llvm.org/llvm/bindings/go/llvm"},
}

type compilerFlags struct {
	cpp, cxx, ld string
}

var components = []string{
	"all-targets",
	"analysis",
	"asmparser",
	"asmprinter",
	"bitreader",
	"bitwriter",
	"codegen",
	"core",
	"coroutines",
	"debuginfodwarf",
	"executionengine",
	"instrumentation",
	"interpreter",
	"ipo",
	"irreader",
	"linker",
	"mc",
	"mcjit",
	"objcarcopts",
	"option",
	"profiledata",
	"scalaropts",
	"support",
	"target",
}

func llvmConfig(args ...string) string {
	configpath := os.Getenv("LLVM_CONFIG")
	if configpath == "" {
		bin, _ := filepath.Split(os.Args[0])
		configpath = filepath.Join(bin, "llvm-config")
	}

	cmd := exec.Command(configpath, args...)
	cmd.Stderr = os.Stderr
	out, err := cmd.Output()
	if err != nil {
		panic(err.Error())
	}

	outstr := string(out)
	outstr = strings.TrimSuffix(outstr, "\n")
	outstr = strings.Replace(outstr, "\n", " ", -1)
	return outstr
}

func llvmFlags() compilerFlags {
	args := append([]string{"--ldflags", "--libs", "--system-libs"}, components...)
	ldflags := llvmConfig(args...)
	stdLibOption := ""
	if strings.Contains(llvmConfig("--cxxflags"), "-stdlib=libc++") {
		// If libc++ is used to build LLVM libraries, -stdlib=libc++ is
		// needed to resolve dependent symbols
		stdLibOption = "-stdlib=libc++"
	}
	if runtime.GOOS != "darwin" {
		// OS X doesn't like -rpath with cgo. See:
		// https://github.com/golang/go/issues/7293
		ldflags = "-Wl,-rpath," + llvmConfig("--libdir") + " " + ldflags
	}
	return compilerFlags{
		cpp: llvmConfig("--cppflags"),
		cxx: "-std=c++11" + " " + stdLibOption,
		ld:  ldflags,
	}
}

func addTag(args []string, tag string) []string {
	args = append([]string{}, args...)
	addedTag := false
	for i, a := range args {
		if strings.HasPrefix(a, "-tags=") {
			args[i] = a + " " + tag
			addedTag = true
		} else if a == "-tags" && i+1 < len(args) {
			args[i+1] = args[i+1] + " " + tag
			addedTag = true
		}
	}
	if !addedTag {
		args = append([]string{args[0], "-tags", tag}, args[1:]...)
	}
	return args
}

func printComponents() {
	fmt.Println(strings.Join(components, " "))
}

func printConfig() {
	flags := llvmFlags()

	fmt.Printf(`// +build !byollvm

// This file is generated by llvm-go, do not edit.

package llvm

/*
#cgo CPPFLAGS: %s
#cgo CXXFLAGS: %s
#cgo LDFLAGS: %s
*/
import "C"

type (run_build_sh int)
`, flags.cpp, flags.cxx, flags.ld)
}

func runGoWithLLVMEnv(args []string, cc, cxx, gocmd, llgo, cppflags, cxxflags, ldflags string, packages []pkg) {
	args = addTag(args, "byollvm")

	srcdir := llvmConfig("--src-root")

	tmpgopath, err := ioutil.TempDir("", "gopath")
	if err != nil {
		panic(err.Error())
	}

	for _, p := range packages {
		path := filepath.Join(tmpgopath, "src", p.pkgpath)
		err := os.MkdirAll(filepath.Dir(path), os.ModePerm)
		if err != nil {
			panic(err.Error())
		}

		abspath := p.llvmpath
		if !filepath.IsAbs(abspath) {
			abspath = filepath.Join(srcdir, abspath)
		}

		err = os.Symlink(abspath, path)
		if err != nil {
			panic(err.Error())
		}
	}

	newpath := os.Getenv("PATH")

	newgopathlist := []string{tmpgopath}
	newgopathlist = append(newgopathlist, filepath.SplitList(os.Getenv("GOPATH"))...)
	newgopath := strings.Join(newgopathlist, string(filepath.ListSeparator))

	flags := llvmFlags()

	newenv := []string{
		"CC=" + cc,
		"CXX=" + cxx,
		"CGO_CPPFLAGS=" + flags.cpp + " " + cppflags,
		"CGO_CXXFLAGS=" + flags.cxx + " " + cxxflags,
		"CGO_LDFLAGS=" + flags.ld + " " + ldflags,
		"GOPATH=" + newgopath,
		"PATH=" + newpath,
	}
	if llgo != "" {
		newenv = append(newenv, "GCCGO="+llgo)
	}

	for _, v := range os.Environ() {
		if !strings.HasPrefix(v, "CC=") &&
			!strings.HasPrefix(v, "CXX=") &&
			!strings.HasPrefix(v, "CGO_CPPFLAGS=") &&
			!strings.HasPrefix(v, "CGO_CXXFLAGS=") &&
			!strings.HasPrefix(v, "CGO_LDFLAGS=") &&
			!strings.HasPrefix(v, "GCCGO=") &&
			!strings.HasPrefix(v, "GOPATH=") &&
			!strings.HasPrefix(v, "PATH=") {
			newenv = append(newenv, v)
		}
	}

	gocmdpath, err := exec.LookPath(gocmd)
	if err != nil {
		panic(err.Error())
	}

	proc, err := os.StartProcess(gocmdpath, append([]string{gocmd}, args...),
		&os.ProcAttr{
			Env:   newenv,
			Files: []*os.File{os.Stdin, os.Stdout, os.Stderr},
		})
	if err != nil {
		panic(err.Error())
	}
	ps, err := proc.Wait()
	if err != nil {
		panic(err.Error())
	}

	os.RemoveAll(tmpgopath)

	if !ps.Success() {
		os.Exit(1)
	}
}

func usage() {
	fmt.Println(`Usage: llvm-go subcommand [flags]

Available subcommands: build get install run test print-components print-config`)
	os.Exit(0)
}

func main() {
	cc := os.Getenv("CC")
	cxx := os.Getenv("CXX")
	cppflags := os.Getenv("CGO_CPPFLAGS")
	cxxflags := os.Getenv("CGO_CXXFLAGS")
	ldflags := os.Getenv("CGO_LDFLAGS")
	gocmd := "go"
	llgo := ""
	packagesString := ""

	flags := []struct {
		name string
		dest *string
	}{
		{"cc", &cc},
		{"cxx", &cxx},
		{"go", &gocmd},
		{"llgo", &llgo},
		{"cppflags", &cppflags},
		{"ldflags", &ldflags},
		{"packages", &packagesString},
	}

	args := os.Args[1:]
LOOP:
	for {
		if len(args) == 0 {
			usage()
		}
		for _, flag := range flags {
			if strings.HasPrefix(args[0], flag.name+"=") {
				*flag.dest = args[0][len(flag.name)+1:]
				args = args[1:]
				continue LOOP
			}
		}
		break
	}

	packages := packages
	if packagesString != "" {
		for _, field := range strings.Fields(packagesString) {
			pos := strings.IndexRune(field, '=')
			if pos == -1 {
				fmt.Fprintf(os.Stderr, "invalid packages value %q, expected 'pkgpath=llvmpath [pkgpath=llvmpath ...]'\n", packagesString)
				os.Exit(1)
			}
			packages = append(packages, pkg{
				pkgpath:  field[:pos],
				llvmpath: field[pos+1:],
			})
		}
	}

	switch args[0] {
	case "build", "get", "install", "run", "test":
		runGoWithLLVMEnv(args, cc, cxx, gocmd, llgo, cppflags, cxxflags, ldflags, packages)
	case "print-components":
		printComponents()
	case "print-config":
		printConfig()
	default:
		usage()
	}
}
<|endoftext|>


<commit_before>package graph

import (
	"encoding/json"
	"fmt"
	"github.com/dotcloud/docker/future"
	"io/ioutil"
	"os"
	"path"
	"strings"
	"syscall"
	"time"
)

type Image struct {
	Id      string
	Parent  string
	Comment string
	Created time.Time
	graph   *Graph
}

func LoadImage(root string) (*Image, error) {
	// Load the json data
	jsonData, err := ioutil.ReadFile(jsonPath(root))
	if err != nil {
		return nil, err
	}
	var img Image
	if err := json.Unmarshal(jsonData, &img); err != nil {
		return nil, err
	}
	if err := ValidateId(img.Id); err != nil {
		return nil, err
	}
	// Check that the filesystem layer exists
	if stat, err := os.Stat(layerPath(root)); err != nil {
		if os.IsNotExist(err) {
			return nil, fmt.Errorf("Couldn't load image %s: no filesystem layer", img.Id)
		} else {
			return nil, err
		}
	} else if !stat.IsDir() {
		return nil, fmt.Errorf("Couldn't load image %s: %s is not a directory", img.Id, layerPath(root))
	}
	return &img, nil
}

func StoreImage(img *Image, layerData Archive, root string) error {
	// Check that root doesn't already exist
	if _, err := os.Stat(root); err == nil {
		return fmt.Errorf("Image %s already exists", img.Id)
	} else if !os.IsNotExist(err) {
		return err
	}
	// Store the layer
	layer := layerPath(root)
	if err := os.MkdirAll(layer, 0700); err != nil {
		return err
	}
	if err := Untar(layerData, layer); err != nil {
		return err
	}
	// Store the json ball
	jsonData, err := json.Marshal(img)
	if err != nil {
		return err
	}
	if err := ioutil.WriteFile(jsonPath(root), jsonData, 0600); err != nil {
		return err
	}
	return nil
}

func layerPath(root string) string {
	return path.Join(root, "layer")
}

func jsonPath(root string) string {
	return path.Join(root, "json")
}

func MountAUFS(ro []string, rw string, target string) error {
	// FIXME: Now mount the layers
	rwBranch := fmt.Sprintf("%v=rw", rw)
	roBranches := ""
	for _, layer := range ro {
		roBranches += fmt.Sprintf("%v=ro:", layer)
	}
	branches := fmt.Sprintf("br:%v:%v", rwBranch, roBranches)
	return mount("none", target, "aufs", 0, branches)
}

func Unmount(target string) error {
	if err := syscall.Unmount(target, 0); err != nil {
		return err
	}
	// Even though we just unmounted the filesystem, AUFS will prevent deleting the mntpoint
	// for some time. We'll just keep retrying until it succeeds.
	for retries := 0; retries < 1000; retries++ {
		err := os.Remove(target)
		if err == nil {
			// rm mntpoint succeeded
			return nil
		}
		if os.IsNotExist(err) {
			// mntpoint doesn't exist anymore. Success.
			return nil
		}
		// fmt.Printf("(%v) Remove %v returned: %v\n", retries, target, err)
		time.Sleep(10 * time.Millisecond)
	}
	return fmt.Errorf("Umount: Failed to umount %v", target)
}

func (image *Image) Mount(root, rw string) error {
	layers, err := image.layers()
	if err != nil {
		return err
	}
	// FIXME: @creack shouldn't we do this after going over changes?
	if err := MountAUFS(layers, rw, root); err != nil {
		return err
	}
	// FIXME: Create tests for deletion
	// FIXME: move this part to change.go
	// Retrieve the changeset from the parent and apply it to the container
	//  - Retrieve the changes
	changes, err := Changes(layers, layers[0])
	if err != nil {
		return err
	}
	// Iterate on changes
	for _, c := range changes {
		// If there is a delete
		if c.Kind == ChangeDelete {
			// Make sure the directory exists
			file_path, file_name := path.Dir(c.Path), path.Base(c.Path)
			if err := os.MkdirAll(path.Join(rw, file_path), 0755); err != nil {
				return err
			}
			// And create the whiteout (we just need to create empty file, discard the return)
			if _, err := os.Create(path.Join(path.Join(rw, file_path),
				".wh."+path.Base(file_name))); err != nil {
				return err
			}
		}
	}
	return nil
}

func ValidateId(id string) error {
	if id == "" {
		return fmt.Errorf("Image id can't be empty")
	}
	if strings.Contains(id, ":") {
		return fmt.Errorf("Invalid character in image id: ':'")
	}
	return nil
}

func GenerateId() string {
	future.Seed()
	return future.RandomId()
}

// Image includes convenience proxy functions to its graph
// These functions will return an error if the image is not registered
// (ie. if image.graph == nil)

func (img *Image) History() ([]*Image, error) {
	var parents []*Image
	if err := img.WalkHistory(
		func(img *Image) {
			parents = append(parents, img)
		},
	); err != nil {
		return nil, err
	}
	return parents, nil
}

// layers returns all the filesystem layers needed to mount an image
func (img *Image) layers() ([]string, error) {
	var list []string
	var e error
	if err := img.WalkHistory(
		func(img *Image) {
			if layer, err := img.layer(); err != nil {
				e = err
			} else if layer != "" {
				list = append(list, layer)
			}
		},
	); err != nil {
		return nil, err
	} else if e != nil { // Did an error occur inside the handler?
		return nil, e
	}
	if len(list) == 0 {
		return nil, fmt.Errorf("No layer found for image %s\n", img.Id)
	}
	return list, nil
}

func (img *Image) WalkHistory(handler func(*Image)) error {
	var err error
	currentImg := img
	for currentImg != nil {
		if handler != nil {
			handler(currentImg)
		}
		currentImg, err = currentImg.GetParent()
		if err != nil {
			return fmt.Errorf("Error while getting parent image: %v", err)
		}
	}
	return nil
}

func (img *Image) GetParent() (*Image, error) {
	if img.Parent == "" {
		return nil, nil
	}
	if img.graph == nil {
		return nil, fmt.Errorf("Can't lookup parent of unregistered image")
	}
	return img.graph.Get(img.Parent)
}

func (img *Image) root() (string, error) {
	if img.graph == nil {
		return "", fmt.Errorf("Can't lookup root of unregistered image")
	}
	return img.graph.imageRoot(img.Id), nil
}

// Return the path of an image's layer
func (img *Image) layer() (string, error) {
	root, err := img.root()
	if err != nil {
		return "", err
	}
	return layerPath(root), nil
}
<commit_msg>Image.Changes(): list all changes between an image and a rw directory<commit_after>package graph

import (
	"encoding/json"
	"fmt"
	"github.com/dotcloud/docker/future"
	"io/ioutil"
	"os"
	"path"
	"strings"
	"syscall"
	"time"
)

type Image struct {
	Id      string
	Parent  string
	Comment string
	Created time.Time
	graph   *Graph
}

func LoadImage(root string) (*Image, error) {
	// Load the json data
	jsonData, err := ioutil.ReadFile(jsonPath(root))
	if err != nil {
		return nil, err
	}
	var img Image
	if err := json.Unmarshal(jsonData, &img); err != nil {
		return nil, err
	}
	if err := ValidateId(img.Id); err != nil {
		return nil, err
	}
	// Check that the filesystem layer exists
	if stat, err := os.Stat(layerPath(root)); err != nil {
		if os.IsNotExist(err) {
			return nil, fmt.Errorf("Couldn't load image %s: no filesystem layer", img.Id)
		} else {
			return nil, err
		}
	} else if !stat.IsDir() {
		return nil, fmt.Errorf("Couldn't load image %s: %s is not a directory", img.Id, layerPath(root))
	}
	return &img, nil
}

func StoreImage(img *Image, layerData Archive, root string) error {
	// Check that root doesn't already exist
	if _, err := os.Stat(root); err == nil {
		return fmt.Errorf("Image %s already exists", img.Id)
	} else if !os.IsNotExist(err) {
		return err
	}
	// Store the layer
	layer := layerPath(root)
	if err := os.MkdirAll(layer, 0700); err != nil {
		return err
	}
	if err := Untar(layerData, layer); err != nil {
		return err
	}
	// Store the json ball
	jsonData, err := json.Marshal(img)
	if err != nil {
		return err
	}
	if err := ioutil.WriteFile(jsonPath(root), jsonData, 0600); err != nil {
		return err
	}
	return nil
}

func layerPath(root string) string {
	return path.Join(root, "layer")
}

func jsonPath(root string) string {
	return path.Join(root, "json")
}

func MountAUFS(ro []string, rw string, target string) error {
	// FIXME: Now mount the layers
	rwBranch := fmt.Sprintf("%v=rw", rw)
	roBranches := ""
	for _, layer := range ro {
		roBranches += fmt.Sprintf("%v=ro:", layer)
	}
	branches := fmt.Sprintf("br:%v:%v", rwBranch, roBranches)
	return mount("none", target, "aufs", 0, branches)
}

func Unmount(target string) error {
	if err := syscall.Unmount(target, 0); err != nil {
		return err
	}
	// Even though we just unmounted the filesystem, AUFS will prevent deleting the mntpoint
	// for some time. We'll just keep retrying until it succeeds.
	for retries := 0; retries < 1000; retries++ {
		err := os.Remove(target)
		if err == nil {
			// rm mntpoint succeeded
			return nil
		}
		if os.IsNotExist(err) {
			// mntpoint doesn't exist anymore. Success.
			return nil
		}
		// fmt.Printf("(%v) Remove %v returned: %v\n", retries, target, err)
		time.Sleep(10 * time.Millisecond)
	}
	return fmt.Errorf("Umount: Failed to umount %v", target)
}

func (image *Image) Mount(root, rw string) error {
	layers, err := image.layers()
	if err != nil {
		return err
	}
	// FIXME: @creack shouldn't we do this after going over changes?
	if err := MountAUFS(layers, rw, root); err != nil {
		return err
	}
	// FIXME: Create tests for deletion
	// FIXME: move this part to change.go
	// Retrieve the changeset from the parent and apply it to the container
	//  - Retrieve the changes
	changes, err := Changes(layers, layers[0])
	if err != nil {
		return err
	}
	// Iterate on changes
	for _, c := range changes {
		// If there is a delete
		if c.Kind == ChangeDelete {
			// Make sure the directory exists
			file_path, file_name := path.Dir(c.Path), path.Base(c.Path)
			if err := os.MkdirAll(path.Join(rw, file_path), 0755); err != nil {
				return err
			}
			// And create the whiteout (we just need to create empty file, discard the return)
			if _, err := os.Create(path.Join(path.Join(rw, file_path),
				".wh."+path.Base(file_name))); err != nil {
				return err
			}
		}
	}
	return nil
}

func (image *Image) Changes(rw string) ([]Change, error) {
	layers, err := image.layers()
	if err != nil {
		return nil, err
	}
	return Changes(layers, rw)
}

func ValidateId(id string) error {
	if id == "" {
		return fmt.Errorf("Image id can't be empty")
	}
	if strings.Contains(id, ":") {
		return fmt.Errorf("Invalid character in image id: ':'")
	}
	return nil
}

func GenerateId() string {
	future.Seed()
	return future.RandomId()
}

// Image includes convenience proxy functions to its graph
// These functions will return an error if the image is not registered
// (ie. if image.graph == nil)

func (img *Image) History() ([]*Image, error) {
	var parents []*Image
	if err := img.WalkHistory(
		func(img *Image) {
			parents = append(parents, img)
		},
	); err != nil {
		return nil, err
	}
	return parents, nil
}

// layers returns all the filesystem layers needed to mount an image
func (img *Image) layers() ([]string, error) {
	var list []string
	var e error
	if err := img.WalkHistory(
		func(img *Image) {
			if layer, err := img.layer(); err != nil {
				e = err
			} else if layer != "" {
				list = append(list, layer)
			}
		},
	); err != nil {
		return nil, err
	} else if e != nil { // Did an error occur inside the handler?
		return nil, e
	}
	if len(list) == 0 {
		return nil, fmt.Errorf("No layer found for image %s\n", img.Id)
	}
	return list, nil
}

func (img *Image) WalkHistory(handler func(*Image)) error {
	var err error
	currentImg := img
	for currentImg != nil {
		if handler != nil {
			handler(currentImg)
		}
		currentImg, err = currentImg.GetParent()
		if err != nil {
			return fmt.Errorf("Error while getting parent image: %v", err)
		}
	}
	return nil
}

func (img *Image) GetParent() (*Image, error) {
	if img.Parent == "" {
		return nil, nil
	}
	if img.graph == nil {
		return nil, fmt.Errorf("Can't lookup parent of unregistered image")
	}
	return img.graph.Get(img.Parent)
}

func (img *Image) root() (string, error) {
	if img.graph == nil {
		return "", fmt.Errorf("Can't lookup root of unregistered image")
	}
	return img.graph.imageRoot(img.Id), nil
}

// Return the path of an image's layer
func (img *Image) layer() (string, error) {
	root, err := img.root()
	if err != nil {
		return "", err
	}
	return layerPath(root), nil
}
<|endoftext|>


<commit_before>package main

import (
	"sync"

	log "github.com/Sirupsen/logrus"
	"github.com/prometheus/client_golang/prometheus"
	"regexp"
	"strings"
)

type exporter struct {
	mutex               sync.RWMutex
	queueMetricsGauge   map[string]*prometheus.GaugeVec
	queueMetricsCounter map[string]*prometheus.Desc
	overviewMetrics     map[string]prometheus.Gauge
	upMetric            prometheus.Gauge
	exchangeMetrics     map[string]*prometheus.Desc
	overviewFetched     bool
	queuesFetched       bool
	exchangesFetched    bool
}

func newExporter() *exporter {
	return &exporter{
		queueMetricsGauge:   queueGaugeVec,
		queueMetricsCounter: queueCounterVec,
		overviewMetrics:     overviewMetricDescription,
		upMetric:            upMetricDescription,
		exchangeMetrics:     exchangeCounterVec,
	}
}

func (e *exporter) fetchRabbit(ch chan<- prometheus.Metric) {
	rabbitMqOverviewData, overviewError := getMetricMap(config, "overview")
	rabbitMqQueueData, queueError := getStatsInfo(config, "queues")
	exchangeData, exchangeError := getStatsInfo(config, "exchanges")

	e.overviewFetched = overviewError == nil
	e.queuesFetched = queueError == nil
	e.exchangesFetched = exchangeError == nil

	if overviewError != nil || queueError != nil || exchangeError != nil {
		e.upMetric.Set(0)
	} else {
		e.upMetric.Set(1)
	}

	log.WithField("overviewData", rabbitMqOverviewData).Debug("Overview data")
	for key, gauge := range e.overviewMetrics {
		if value, ok := rabbitMqOverviewData[key]; ok {
			log.WithFields(log.Fields{"key": key, "value": value}).Debug("Set overview metric for key")
			gauge.Set(value)
		} else {
			log.WithFields(log.Fields{"key": key}).Warn("Overview data not found")
		}
	}

	log.WithField("queueData", rabbitMqQueueData).Debug("Queue data")
	for key, gaugevec := range e.queueMetricsGauge {
		for _, queue := range rabbitMqQueueData {
			if value, ok := queue.metrics[key]; ok {
				if match, _ := regexp.MatchString(config.SkipQueues, strings.ToLower(queue.name)); !match {
					log.WithFields(log.Fields{"vhost": queue.vhost, "queue": queue.name, "key": key, "value": value}).Debug("Set queue metric for key")
					gaugevec.WithLabelValues(queue.vhost, queue.name).Set(value)
				}
			}
		}
	}
	for key, countvec := range e.queueMetricsCounter {
		for _, queue := range rabbitMqQueueData {
			if value, ok := queue.metrics[key]; ok {
				if match, _ := regexp.MatchString(config.SkipQueues, strings.ToLower(queue.name)); !match {
					log.WithFields(log.Fields{"vhost": queue.vhost, "queue": queue.name, "key": key, "value": value}).Debug("Set queue metric for key")
					ch <- prometheus.MustNewConstMetric(countvec, prometheus.CounterValue, value, queue.vhost, queue.name)
				}
			} else {
				ch <- prometheus.MustNewConstMetric(countvec, prometheus.CounterValue, 0, queue.vhost, queue.name)
			}
		}
	}

	for key, countvec := range e.exchangeMetrics {
		for _, exchange := range exchangeData {
			if value, ok := exchange.metrics[key]; ok {
				log.WithFields(log.Fields{"vhost": exchange.vhost, "exchange": exchange.name, "key": key, "value": value}).Debug("Set exchange metric for key")
				ch <- prometheus.MustNewConstMetric(countvec, prometheus.CounterValue, value, exchange.vhost, exchange.name)
			}
		}
	}

	log.Info("Metrics updated successfully.")
}

func (e *exporter) Describe(ch chan<- *prometheus.Desc) {
	for _, gauge := range e.overviewMetrics {
		gauge.Describe(ch)
	}

	for _, gaugevec := range e.queueMetricsGauge {
		gaugevec.Describe(ch)
	}
	for _, countervec := range e.queueMetricsCounter {
		ch <- countervec
	}
	for _, exchangeMetric := range e.exchangeMetrics {
		ch <- exchangeMetric
	}

	e.upMetric.Describe(ch)
	BuildInfo.Describe(ch)
}

func (e *exporter) Collect(ch chan<- prometheus.Metric) {
	e.mutex.Lock() // To protect metrics from concurrent collects.
	defer e.mutex.Unlock()

	for _, gaugevec := range e.queueMetricsGauge {
		gaugevec.Reset()
	}

	e.fetchRabbit(ch)

	e.upMetric.Collect(ch)

	if e.overviewFetched {
		for _, gauge := range e.overviewMetrics {
			gauge.Collect(ch)
		}
	}

	if e.queuesFetched {
		for _, gaugevec := range e.queueMetricsGauge {
			gaugevec.Collect(ch)
		}
	}

	BuildInfo.Collect(ch)
}
<commit_msg>Move regexp check outside if for queueMetricsCounter<commit_after>package main

import (
	"sync"

	log "github.com/Sirupsen/logrus"
	"github.com/prometheus/client_golang/prometheus"
	"regexp"
	"strings"
)

type exporter struct {
	mutex               sync.RWMutex
	queueMetricsGauge   map[string]*prometheus.GaugeVec
	queueMetricsCounter map[string]*prometheus.Desc
	overviewMetrics     map[string]prometheus.Gauge
	upMetric            prometheus.Gauge
	exchangeMetrics     map[string]*prometheus.Desc
	overviewFetched     bool
	queuesFetched       bool
	exchangesFetched    bool
}

func newExporter() *exporter {
	return &exporter{
		queueMetricsGauge:   queueGaugeVec,
		queueMetricsCounter: queueCounterVec,
		overviewMetrics:     overviewMetricDescription,
		upMetric:            upMetricDescription,
		exchangeMetrics:     exchangeCounterVec,
	}
}

func (e *exporter) fetchRabbit(ch chan<- prometheus.Metric) {
	rabbitMqOverviewData, overviewError := getMetricMap(config, "overview")
	rabbitMqQueueData, queueError := getStatsInfo(config, "queues")
	exchangeData, exchangeError := getStatsInfo(config, "exchanges")

	e.overviewFetched = overviewError == nil
	e.queuesFetched = queueError == nil
	e.exchangesFetched = exchangeError == nil

	if overviewError != nil || queueError != nil || exchangeError != nil {
		e.upMetric.Set(0)
	} else {
		e.upMetric.Set(1)
	}

	log.WithField("overviewData", rabbitMqOverviewData).Debug("Overview data")
	for key, gauge := range e.overviewMetrics {
		if value, ok := rabbitMqOverviewData[key]; ok {
			log.WithFields(log.Fields{"key": key, "value": value}).Debug("Set overview metric for key")
			gauge.Set(value)
		} else {
			log.WithFields(log.Fields{"key": key}).Warn("Overview data not found")
		}
	}

	log.WithField("queueData", rabbitMqQueueData).Debug("Queue data")
	for key, gaugevec := range e.queueMetricsGauge {
		for _, queue := range rabbitMqQueueData {
			if value, ok := queue.metrics[key]; ok {
				if match, _ := regexp.MatchString(config.SkipQueues, strings.ToLower(queue.name)); !match {
					log.WithFields(log.Fields{"vhost": queue.vhost, "queue": queue.name, "key": key, "value": value}).Debug("Set queue metric for key")
					gaugevec.WithLabelValues(queue.vhost, queue.name).Set(value)
				}
			}
		}
	}
	for key, countvec := range e.queueMetricsCounter {
		for _, queue := range rabbitMqQueueData {
			if match, _ := regexp.MatchString(config.SkipQueues, strings.ToLower(queue.name)); !match {
				if value, ok := queue.metrics[key]; ok {
					log.WithFields(log.Fields{"vhost": queue.vhost, "queue": queue.name, "key": key, "value": value}).Debug("Set queue metric for key")
					ch <- prometheus.MustNewConstMetric(countvec, prometheus.CounterValue, value, queue.vhost, queue.name)
				} else {
					ch <- prometheus.MustNewConstMetric(countvec, prometheus.CounterValue, 0, queue.vhost, queue.name)
				}
			}
		}
	}

	for key, countvec := range e.exchangeMetrics {
		for _, exchange := range exchangeData {
			if value, ok := exchange.metrics[key]; ok {
				log.WithFields(log.Fields{"vhost": exchange.vhost, "exchange": exchange.name, "key": key, "value": value}).Debug("Set exchange metric for key")
				ch <- prometheus.MustNewConstMetric(countvec, prometheus.CounterValue, value, exchange.vhost, exchange.name)
			}
		}
	}

	log.Info("Metrics updated successfully.")
}

func (e *exporter) Describe(ch chan<- *prometheus.Desc) {
	for _, gauge := range e.overviewMetrics {
		gauge.Describe(ch)
	}

	for _, gaugevec := range e.queueMetricsGauge {
		gaugevec.Describe(ch)
	}
	for _, countervec := range e.queueMetricsCounter {
		ch <- countervec
	}
	for _, exchangeMetric := range e.exchangeMetrics {
		ch <- exchangeMetric
	}

	e.upMetric.Describe(ch)
	BuildInfo.Describe(ch)
}

func (e *exporter) Collect(ch chan<- prometheus.Metric) {
	e.mutex.Lock() // To protect metrics from concurrent collects.
	defer e.mutex.Unlock()

	for _, gaugevec := range e.queueMetricsGauge {
		gaugevec.Reset()
	}

	e.fetchRabbit(ch)

	e.upMetric.Collect(ch)

	if e.overviewFetched {
		for _, gauge := range e.overviewMetrics {
			gauge.Collect(ch)
		}
	}

	if e.queuesFetched {
		for _, gaugevec := range e.queueMetricsGauge {
			gaugevec.Collect(ch)
		}
	}

	BuildInfo.Collect(ch)
}
<|endoftext|>


<commit_before>package bridge

import (
	"bytes"
	"encoding/base64"
	"encoding/json"
	"fmt"
	"github.com/viant/toolbox"
	"io"
	"io/ioutil"
	"log"
	"net"
	"net/http"
	"net/http/httputil"
	"net/url"
	"os"
	"path"
	"sync"
	"time"
	"unicode"
	"unicode/utf8"
)

//HttpBridgeConfig represent http bridge config
type HttpBridgeEndpointConfig struct {
	Port           string
	ReadTimeoutMs  int
	WriteTimeoutMs int
	MaxHeaderBytes int
}

//HttpBridgeProxyRoute represent http proxy route
type HttpBridgeProxyRoute struct {
	Pattern          string
	TargetURL        *url.URL
	ResponseModifier func(*http.Response) error
	Listener         func(request *http.Request, response *http.Response)
}

//HttpBridgeProxyConfig represent proxy config
type HttpBridgeProxyConfig struct {
	MaxIdleConnections    int
	RequestTimeoutMs      int
	KeepAliveTimeMs       int
	TLSHandshakeTimeoutMs int
	BufferPoolSize        int
	BufferSize            int
}

//HttpBridgeConfig represents HttpBridgeConfig config
type HttpBridgeConfig struct {
	Endpoint *HttpBridgeEndpointConfig
	Proxy    *HttpBridgeProxyConfig
	Routes   []*HttpBridgeProxyRoute
}

//ProxyHandlerFactory proxy handler factory
type HttpBridgeProxyHandlerFactory func(proxyConfig *HttpBridgeProxyConfig, route *HttpBridgeProxyRoute) (http.Handler, error)

//HttpBridge represents http bridge
type HttpBridge struct {
	Config   *HttpBridgeConfig
	Server   *http.Server
	Handlers map[string]http.Handler
}

//ListenAndServe start http endpoint
func (r *HttpBridge) ListenAndServe() error {
	return r.Server.ListenAndServe()
}

//ListenAndServe start http endpoint on secure port
func (r *HttpBridge) ListenAndServeTLS(certFile, keyFile string) error {
	return r.Server.ListenAndServeTLS(certFile, keyFile)
}

//NewHttpBridge creates a new instance of NewHttpBridge
func NewHttpBridge(config *HttpBridgeConfig, factory HttpBridgeProxyHandlerFactory) (*HttpBridge, error) {
	mux := http.NewServeMux()
	var handlers = make(map[string]http.Handler)
	for _, route := range config.Routes {
		handler, err := factory(config.Proxy, route)
		if err != nil {
			return nil, err
		}
		mux.Handle(route.Pattern, handler)
		handlers[route.Pattern] = handler
	}
	server := &http.Server{
		Addr:           ":" + config.Endpoint.Port,
		Handler:        mux,
		ReadTimeout:    time.Millisecond * time.Duration(config.Endpoint.ReadTimeoutMs),
		WriteTimeout:   time.Millisecond * time.Duration(config.Endpoint.WriteTimeoutMs),
		MaxHeaderBytes: config.Endpoint.MaxHeaderBytes,
	}
	return &HttpBridge{
		Server:   server,
		Config:   config,
		Handlers: handlers,
	}, nil
}

type handlerWrapper struct {
	Handler http.Handler
}

func (h *handlerWrapper) ServeHTTP(writer http.ResponseWriter, request *http.Request) {
	fmt.Printf("Request: %v %v\n", request.URL, request.Method)
	h.Handler.ServeHTTP(writer, request)
}

//NewProxyHandler creates a new proxy handler
func NewProxyHandler(proxyConfig *HttpBridgeProxyConfig, route *HttpBridgeProxyRoute) (http.Handler, error) {
	roundTripper := &http.Transport{
		Proxy: http.ProxyFromEnvironment,
		Dial: (&net.Dialer{
			Timeout:   time.Duration(proxyConfig.RequestTimeoutMs) * time.Millisecond,
			KeepAlive: time.Duration(proxyConfig.KeepAliveTimeMs) * time.Millisecond,
		}).Dial,
		TLSHandshakeTimeout: time.Duration(proxyConfig.TLSHandshakeTimeoutMs) * time.Millisecond,
		MaxIdleConnsPerHost: proxyConfig.MaxIdleConnections,
	}

	var director func(*http.Request)

	if route.TargetURL != nil {
		director = func(request *http.Request) {
			request.URL.Scheme = route.TargetURL.Scheme
			request.URL.Host = route.TargetURL.Host
		}
	}
	reverseProxy := &httputil.ReverseProxy{
		Transport:      roundTripper,
		BufferPool:     toolbox.NewBytesBufferPool(proxyConfig.BufferPoolSize, proxyConfig.BufferSize),
		ModifyResponse: route.ResponseModifier,
		Director:       director,
	}
	var handler http.Handler = &handlerWrapper{reverseProxy}
	return handler, nil
}

//HTTPTrip represents recorded round trip.
type HttpTrip struct {
	responseWriter     http.ResponseWriter
	Request            *http.Request
	responseBody       *bytes.Buffer
	responseStatusCode int
}

func (w *HttpTrip) Response() *http.Response {
	return &http.Response{
		Request:    w.Request,
		StatusCode: w.responseStatusCode,
		Header:     w.responseWriter.Header(),
		Body:       ioutil.NopCloser(bytes.NewReader(w.responseBody.Bytes())),
	}
}

func (w *HttpTrip) Write(b []byte) (int, error) {
	w.responseBody.Write(b)
	return w.responseWriter.Write(b)
}

func (w *HttpTrip) Header() http.Header {
	return w.responseWriter.Header()
}

func (w *HttpTrip) WriteHeader(status int) {
	w.responseStatusCode = status
	w.responseWriter.WriteHeader(status)
}

func (w *HttpTrip) Flush() {
	if flusher, ok := w.responseWriter.(http.Flusher); ok {
		flusher.Flush()
	}
}

func (w *HttpTrip) CloseNotify() <-chan bool {
	if closer, ok := w.responseWriter.(http.CloseNotifier); ok {
		return closer.CloseNotify()
	}
	return make(chan bool, 1)
}

//ListeningTripHandler represents endpoint recording handler
type ListeningTripHandler struct {
	handler         http.Handler
	pool            httputil.BufferPool
	listener        func(request *http.Request, response *http.Response)
	roundTripsMutex *sync.RWMutex
}

func (h *ListeningTripHandler) Notify(roundTrip *HttpTrip) {
	if h.listener != nil {
		h.listener(roundTrip.Request, roundTrip.Response())
	}
}

//drainBody reads all of b to memory and then returns two equivalent (modified version from  httputil)
func (h ListeningTripHandler) drainBody(reader io.ReadCloser) (io.ReadCloser, io.ReadCloser, error) {
	if reader == http.NoBody {
		return http.NoBody, http.NoBody, nil
	}
	var buf = new(bytes.Buffer)
	toolbox.CopyWithBufferPool(reader, buf, h.pool)
	return ioutil.NopCloser(bytes.NewReader(buf.Bytes())), ioutil.NopCloser(bytes.NewReader(buf.Bytes())), nil
}

func (h ListeningTripHandler) ServeHTTP(responseWriter http.ResponseWriter, request *http.Request) {

	fmt.Printf("ListeningTripHandler %v %v\n", request.URL, request.Method)

	var err error
	var originalRequest = request.WithContext(request.Context())
	if request.ContentLength > 0 {
		request.Body, originalRequest.Body, err = h.drainBody(request.Body)
		if err != nil {
			log.Printf("Faled to serve request :%v due to %v\n", request, err)
			return
		}
	}
	var recordedRoundTrip = &HttpTrip{
		responseWriter: responseWriter,
		Request:        originalRequest,
		responseBody:   new(bytes.Buffer),
	}
	responseWriter = http.ResponseWriter(recordedRoundTrip)
	defer h.Notify(recordedRoundTrip)
	h.handler.ServeHTTP(responseWriter, request)
}

func NewListeningHandler(handler http.Handler, bufferPoolSize, bufferSize int, listener func(request *http.Request, response *http.Response)) *ListeningTripHandler {
	var result = &ListeningTripHandler{
		handler:         handler,
		listener:        listener,
		pool:            toolbox.NewBytesBufferPool(bufferPoolSize, bufferSize),
		roundTripsMutex: &sync.RWMutex{},
	}
	return result
}

func NewProxyRecordingHandler(proxyConfig *HttpBridgeProxyConfig, route *HttpBridgeProxyRoute) (http.Handler, error) {
	handler, err := NewProxyHandler(proxyConfig, route)
	if err != nil {
		return nil, err
	}
	response := NewListeningHandler(handler, proxyConfig.BufferPoolSize, proxyConfig.BufferSize, route.Listener)
	return response, nil
}

func AsListeningTripHandler(handler http.Handler) *ListeningTripHandler {
	if result, ok := handler.(*ListeningTripHandler); ok {
		return result
	}
	return nil
}

//HttpRequest represents JSON serializable http request
type HttpRequest struct {
	Method string      `json:",omitempty"`
	URL    string      `json:",omitempty"`
	Header http.Header `json:",omitempty"`
	Body   string      `json:",omitempty"`
}

//NewHTTPRequest create a new instance of http request
func NewHTTPRequest(method, url, body string, header http.Header) *HttpRequest {
	return &HttpRequest{
		Method: method,
		URL:    url,
		Body:   body,
		Header: header,
	}
}

//HttpResponse represents JSON serializable http response
type HttpResponse struct {
	Code   int
	Header http.Header `json:",omitempty"`
	Body   string      `json:",omitempty"`
}

func ReaderAsText(reader io.Reader) string {
	if reader == nil {
		return ""
	}
	body, err := ioutil.ReadAll(reader)
	if err != nil {
		log.Fatal(err)
	}
	if isBinary(body) {
		buf := new(bytes.Buffer)
		encoder := base64.NewEncoder(base64.StdEncoding, buf)
		encoder.Write(body)
		encoder.Close()
		return fmt.Sprintf("base64:%v", string(buf.Bytes()))

	} else if len(body) > 0 {
		return fmt.Sprintf("text:%v", string(body))
	}
	return ""
}

func isBinary(input []byte) bool {
	for i, w := 0, 0; i < len(input); i += w {
		runeValue, width := utf8.DecodeRune(input[i:])
		if unicode.IsControl(runeValue) {
			return true
		}
		w = width
	}
	return false
}

func writeData(filename string, source interface{}, printStrOut bool) error {
	if toolbox.FileExists(filename) {
		os.Remove(filename)
	}

	logfile, err := os.OpenFile(filename, os.O_RDWR|os.O_CREATE, 0666)
	if err != nil {
		log.Fatalf("error opening file: %v, %v", err, filename)
	}
	defer logfile.Close()

	buf, err := json.MarshalIndent(source, "", "\t")
	if err != nil {
		return err
	}
	_, err = logfile.Write(buf)

	if printStrOut {
		fmt.Printf("%v: %v\n", filename, string(buf))
	}

	return err
}

//HttpFileRecorder returns http route listener that will record request response to the passed in directory
func HttpFileRecorder(directory string, printStdOut bool) func(request *http.Request, response *http.Response) {
	tripCounter := 0

	err := toolbox.CreateDirIfNotExist(directory)
	if err != nil {
		fmt.Printf("failed to create directory%v %v\n, ", err, directory)
	}
	return func(request *http.Request, response *http.Response) {
		var body string
		if request.Body != nil {
			body = ReaderAsText(request.Body)
		}

		fmt.Printf("length: %v %v\n", response.ContentLength, len(body))
		httpRequest := &HttpRequest{
			Method: request.Method,
			URL:    request.URL.String(),
			Header: request.Header,
			Body:   body,
		}

		err = writeData(path.Join(directory, fmt.Sprintf("%T-%v.json", *httpRequest, tripCounter)), httpRequest, printStdOut)
		if err != nil {
			fmt.Printf("failed to write request %v %v\n, ", err, request)
		}

		body = ReaderAsText(response.Body)
		request.Body = nil
		httpResponse := &HttpResponse{
			Code:   response.StatusCode,
			Header: response.Header,
			Body:   body,
		}

		err = writeData(path.Join(directory, fmt.Sprintf("%T-%v.json", *httpResponse, tripCounter)), httpResponse, printStdOut)
		if err != nil {
			fmt.Printf("failed to write response %v %v\n, ", err, response)
		}

		tripCounter++
	}

}
<commit_msg>removed debug info<commit_after>package bridge

import (
	"bytes"
	"encoding/base64"
	"encoding/json"
	"fmt"
	"github.com/viant/toolbox"
	"io"
	"io/ioutil"
	"log"
	"net"
	"net/http"
	"net/http/httputil"
	"net/url"
	"os"
	"path"
	"sync"
	"time"
	"unicode"
	"unicode/utf8"
)

//HttpBridgeConfig represent http bridge config
type HttpBridgeEndpointConfig struct {
	Port           string
	ReadTimeoutMs  int
	WriteTimeoutMs int
	MaxHeaderBytes int
}

//HttpBridgeProxyRoute represent http proxy route
type HttpBridgeProxyRoute struct {
	Pattern          string
	TargetURL        *url.URL
	ResponseModifier func(*http.Response) error
	Listener         func(request *http.Request, response *http.Response)
}

//HttpBridgeProxyConfig represent proxy config
type HttpBridgeProxyConfig struct {
	MaxIdleConnections    int
	RequestTimeoutMs      int
	KeepAliveTimeMs       int
	TLSHandshakeTimeoutMs int
	BufferPoolSize        int
	BufferSize            int
}

//HttpBridgeConfig represents HttpBridgeConfig config
type HttpBridgeConfig struct {
	Endpoint *HttpBridgeEndpointConfig
	Proxy    *HttpBridgeProxyConfig
	Routes   []*HttpBridgeProxyRoute
}

//ProxyHandlerFactory proxy handler factory
type HttpBridgeProxyHandlerFactory func(proxyConfig *HttpBridgeProxyConfig, route *HttpBridgeProxyRoute) (http.Handler, error)

//HttpBridge represents http bridge
type HttpBridge struct {
	Config   *HttpBridgeConfig
	Server   *http.Server
	Handlers map[string]http.Handler
}

//ListenAndServe start http endpoint
func (r *HttpBridge) ListenAndServe() error {
	return r.Server.ListenAndServe()
}

//ListenAndServe start http endpoint on secure port
func (r *HttpBridge) ListenAndServeTLS(certFile, keyFile string) error {
	return r.Server.ListenAndServeTLS(certFile, keyFile)
}

//NewHttpBridge creates a new instance of NewHttpBridge
func NewHttpBridge(config *HttpBridgeConfig, factory HttpBridgeProxyHandlerFactory) (*HttpBridge, error) {
	mux := http.NewServeMux()
	var handlers = make(map[string]http.Handler)
	for _, route := range config.Routes {
		handler, err := factory(config.Proxy, route)
		if err != nil {
			return nil, err
		}
		mux.Handle(route.Pattern, handler)
		handlers[route.Pattern] = handler
	}
	server := &http.Server{
		Addr:           ":" + config.Endpoint.Port,
		Handler:        mux,
		ReadTimeout:    time.Millisecond * time.Duration(config.Endpoint.ReadTimeoutMs),
		WriteTimeout:   time.Millisecond * time.Duration(config.Endpoint.WriteTimeoutMs),
		MaxHeaderBytes: config.Endpoint.MaxHeaderBytes,
	}
	return &HttpBridge{
		Server:   server,
		Config:   config,
		Handlers: handlers,
	}, nil
}

type handlerWrapper struct {
	Handler http.Handler
}

func (h *handlerWrapper) ServeHTTP(writer http.ResponseWriter, request *http.Request) {
	h.Handler.ServeHTTP(writer, request)
}

//NewProxyHandler creates a new proxy handler
func NewProxyHandler(proxyConfig *HttpBridgeProxyConfig, route *HttpBridgeProxyRoute) (http.Handler, error) {
	roundTripper := &http.Transport{
		Proxy: http.ProxyFromEnvironment,
		Dial: (&net.Dialer{
			Timeout:   time.Duration(proxyConfig.RequestTimeoutMs) * time.Millisecond,
			KeepAlive: time.Duration(proxyConfig.KeepAliveTimeMs) * time.Millisecond,
		}).Dial,
		TLSHandshakeTimeout: time.Duration(proxyConfig.TLSHandshakeTimeoutMs) * time.Millisecond,
		MaxIdleConnsPerHost: proxyConfig.MaxIdleConnections,
	}

	var director func(*http.Request)

	if route.TargetURL != nil {
		director = func(request *http.Request) {
			request.URL.Scheme = route.TargetURL.Scheme
			request.URL.Host = route.TargetURL.Host
		}
	}
	reverseProxy := &httputil.ReverseProxy{
		Transport:      roundTripper,
		BufferPool:     toolbox.NewBytesBufferPool(proxyConfig.BufferPoolSize, proxyConfig.BufferSize),
		ModifyResponse: route.ResponseModifier,
		Director:       director,
	}
	var handler http.Handler = &handlerWrapper{reverseProxy}
	return handler, nil
}

//HTTPTrip represents recorded round trip.
type HttpTrip struct {
	responseWriter     http.ResponseWriter
	Request            *http.Request
	responseBody       *bytes.Buffer
	responseStatusCode int
}

func (w *HttpTrip) Response() *http.Response {
	return &http.Response{
		Request:    w.Request,
		StatusCode: w.responseStatusCode,
		Header:     w.responseWriter.Header(),
		Body:       ioutil.NopCloser(bytes.NewReader(w.responseBody.Bytes())),
	}
}

func (w *HttpTrip) Write(b []byte) (int, error) {
	w.responseBody.Write(b)
	return w.responseWriter.Write(b)
}

func (w *HttpTrip) Header() http.Header {
	return w.responseWriter.Header()
}

func (w *HttpTrip) WriteHeader(status int) {
	w.responseStatusCode = status
	w.responseWriter.WriteHeader(status)
}

func (w *HttpTrip) Flush() {
	if flusher, ok := w.responseWriter.(http.Flusher); ok {
		flusher.Flush()
	}
}

func (w *HttpTrip) CloseNotify() <-chan bool {
	if closer, ok := w.responseWriter.(http.CloseNotifier); ok {
		return closer.CloseNotify()
	}
	return make(chan bool, 1)
}

//ListeningTripHandler represents endpoint recording handler
type ListeningTripHandler struct {
	handler         http.Handler
	pool            httputil.BufferPool
	listener        func(request *http.Request, response *http.Response)
	roundTripsMutex *sync.RWMutex
}

func (h *ListeningTripHandler) Notify(roundTrip *HttpTrip) {
	if h.listener != nil {
		h.listener(roundTrip.Request, roundTrip.Response())
	}
}

//drainBody reads all of b to memory and then returns two equivalent (modified version from  httputil)
func (h ListeningTripHandler) drainBody(reader io.ReadCloser) (io.ReadCloser, io.ReadCloser, error) {
	if reader == http.NoBody {
		return http.NoBody, http.NoBody, nil
	}
	var buf = new(bytes.Buffer)
	toolbox.CopyWithBufferPool(reader, buf, h.pool)
	return ioutil.NopCloser(bytes.NewReader(buf.Bytes())), ioutil.NopCloser(bytes.NewReader(buf.Bytes())), nil
}

func (h ListeningTripHandler) ServeHTTP(responseWriter http.ResponseWriter, request *http.Request) {
	var err error
	var originalRequest = request.WithContext(request.Context())
	if request.ContentLength > 0 {
		request.Body, originalRequest.Body, err = h.drainBody(request.Body)
		if err != nil {
			log.Printf("Faled to serve request :%v due to %v\n", request, err)
			return
		}
	}
	var recordedRoundTrip = &HttpTrip{
		responseWriter: responseWriter,
		Request:        originalRequest,
		responseBody:   new(bytes.Buffer),
	}
	responseWriter = http.ResponseWriter(recordedRoundTrip)
	defer h.Notify(recordedRoundTrip)
	h.handler.ServeHTTP(responseWriter, request)
}

func NewListeningHandler(handler http.Handler, bufferPoolSize, bufferSize int, listener func(request *http.Request, response *http.Response)) *ListeningTripHandler {
	var result = &ListeningTripHandler{
		handler:         handler,
		listener:        listener,
		pool:            toolbox.NewBytesBufferPool(bufferPoolSize, bufferSize),
		roundTripsMutex: &sync.RWMutex{},
	}
	return result
}

func NewProxyRecordingHandler(proxyConfig *HttpBridgeProxyConfig, route *HttpBridgeProxyRoute) (http.Handler, error) {
	handler, err := NewProxyHandler(proxyConfig, route)
	if err != nil {
		return nil, err
	}
	response := NewListeningHandler(handler, proxyConfig.BufferPoolSize, proxyConfig.BufferSize, route.Listener)
	return response, nil
}

func AsListeningTripHandler(handler http.Handler) *ListeningTripHandler {
	if result, ok := handler.(*ListeningTripHandler); ok {
		return result
	}
	return nil
}

//HttpRequest represents JSON serializable http request
type HttpRequest struct {
	Method string      `json:",omitempty"`
	URL    string      `json:",omitempty"`
	Header http.Header `json:",omitempty"`
	Body   string      `json:",omitempty"`
}

//NewHTTPRequest create a new instance of http request
func NewHTTPRequest(method, url, body string, header http.Header) *HttpRequest {
	return &HttpRequest{
		Method: method,
		URL:    url,
		Body:   body,
		Header: header,
	}
}

//HttpResponse represents JSON serializable http response
type HttpResponse struct {
	Code   int
	Header http.Header `json:",omitempty"`
	Body   string      `json:",omitempty"`
}

func ReaderAsText(reader io.Reader) string {
	if reader == nil {
		return ""
	}
	body, err := ioutil.ReadAll(reader)
	if err != nil {
		log.Fatal(err)
	}
	if isBinary(body) {
		buf := new(bytes.Buffer)
		encoder := base64.NewEncoder(base64.StdEncoding, buf)
		encoder.Write(body)
		encoder.Close()
		return fmt.Sprintf("base64:%v", string(buf.Bytes()))

	} else if len(body) > 0 {
		return fmt.Sprintf("text:%v", string(body))
	}
	return ""
}

func isBinary(input []byte) bool {
	for i, w := 0, 0; i < len(input); i += w {
		runeValue, width := utf8.DecodeRune(input[i:])
		if unicode.IsControl(runeValue) {
			return true
		}
		w = width
	}
	return false
}

func writeData(filename string, source interface{}, printStrOut bool) error {
	if toolbox.FileExists(filename) {
		os.Remove(filename)
	}

	logfile, err := os.OpenFile(filename, os.O_RDWR|os.O_CREATE, 0666)
	if err != nil {
		log.Fatalf("error opening file: %v, %v", err, filename)
	}
	defer logfile.Close()

	buf, err := json.MarshalIndent(source, "", "\t")
	if err != nil {
		return err
	}
	_, err = logfile.Write(buf)

	if printStrOut {
		fmt.Printf("%v: %v\n", filename, string(buf))
	}

	return err
}

//HttpFileRecorder returns http route listener that will record request response to the passed in directory
func HttpFileRecorder(directory string, printStdOut bool) func(request *http.Request, response *http.Response) {
	tripCounter := 0

	err := toolbox.CreateDirIfNotExist(directory)
	if err != nil {
		fmt.Printf("failed to create directory%v %v\n, ", err, directory)
	}
	return func(request *http.Request, response *http.Response) {
		var body string
		if request.Body != nil {
			body = ReaderAsText(request.Body)
		}
		httpRequest := &HttpRequest{
			Method: request.Method,
			URL:    request.URL.String(),
			Header: request.Header,
			Body:   body,
		}

		err = writeData(path.Join(directory, fmt.Sprintf("%T-%v.json", *httpRequest, tripCounter)), httpRequest, printStdOut)
		if err != nil {
			fmt.Printf("failed to write request %v %v\n, ", err, request)
		}

		body = ReaderAsText(response.Body)
		request.Body = nil
		httpResponse := &HttpResponse{
			Code:   response.StatusCode,
			Header: response.Header,
			Body:   body,
		}

		err = writeData(path.Join(directory, fmt.Sprintf("%T-%v.json", *httpResponse, tripCounter)), httpResponse, printStdOut)
		if err != nil {
			fmt.Printf("failed to write response %v %v\n, ", err, response)
		}

		tripCounter++
	}

}
<|endoftext|>


<commit_before>package godirwalk

import (
	"os"
	"path/filepath"
	"testing"
)

func TestReadDirents(t *testing.T) {
	actual, err := ReadDirents(filepath.Join(testRoot, "d0"), nil)

	ensureError(t, err)

	expected := Dirents{
		&Dirent{
			name:     "d1",
			modeType: os.ModeDir,
		},
		&Dirent{
			name:     "f1",
			modeType: os.FileMode(0),
		},
		&Dirent{
			name:     "skips",
			modeType: os.ModeDir,
		},
		&Dirent{
			name:     "symlinks",
			modeType: os.ModeDir,
		},
	}

	ensureDirentsMatch(t, actual, expected)
}

func TestReadDirentsSymlinks(t *testing.T) {
	osDirname := filepath.Join(testRoot, "d0/symlinks")

	actual, err := ReadDirents(osDirname, nil)

	ensureError(t, err)

	// Because some platforms set multiple mode type bits, when we create the
	// expected slice, we need to ensure the mode types are set appropriately.
	var expected Dirents
	for _, pathname := range []string{"nothing", "toAbs", "toD1", "toF1", "d4"} {
		info, err := os.Lstat(filepath.Join(osDirname, pathname))
		if err != nil {
			t.Fatal(err)
		}
		expected = append(expected, &Dirent{name: pathname, modeType: info.Mode() & os.ModeType})
	}

	ensureDirentsMatch(t, actual, expected)
}

func TestReadDirnames(t *testing.T) {
	t.Skip("FIXME")
	actual, err := ReadDirnames(testRoot, nil)
	ensureError(t, err)
	expected := []string{"dir1", "dir2", "dir3", "symlinks", "dir5", "dir6", "dir4", "file3"}
	ensureStringSlicesMatch(t, actual, expected)
}
<commit_msg>ReadDirnames<commit_after>package godirwalk

import (
	"os"
	"path/filepath"
	"testing"
)

func TestReadDirents(t *testing.T) {
	actual, err := ReadDirents(filepath.Join(testRoot, "d0"), nil)

	ensureError(t, err)

	expected := Dirents{
		&Dirent{
			name:     "d1",
			modeType: os.ModeDir,
		},
		&Dirent{
			name:     "f1",
			modeType: os.FileMode(0),
		},
		&Dirent{
			name:     "skips",
			modeType: os.ModeDir,
		},
		&Dirent{
			name:     "symlinks",
			modeType: os.ModeDir,
		},
	}

	ensureDirentsMatch(t, actual, expected)
}

func TestReadDirentsSymlinks(t *testing.T) {
	osDirname := filepath.Join(testRoot, "d0/symlinks")

	actual, err := ReadDirents(osDirname, nil)

	ensureError(t, err)

	// Because some platforms set multiple mode type bits, when we create the
	// expected slice, we need to ensure the mode types are set appropriately.
	var expected Dirents
	for _, pathname := range []string{"nothing", "toAbs", "toD1", "toF1", "d4"} {
		info, err := os.Lstat(filepath.Join(osDirname, pathname))
		if err != nil {
			t.Fatal(err)
		}
		expected = append(expected, &Dirent{name: pathname, modeType: info.Mode() & os.ModeType})
	}

	ensureDirentsMatch(t, actual, expected)
}

func TestReadDirnames(t *testing.T) {
	actual, err := ReadDirnames(filepath.Join(testRoot, "d0"), nil)
	ensureError(t, err)
	expected := []string{"d1", "f1", "skips", "symlinks"}
	ensureStringSlicesMatch(t, actual, expected)
}
<|endoftext|>


<commit_before>/*
Copyright 2015 Nodetemple <hostmaster@nodetemple.com>

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 main

import (
	"strings"
	"text/template"

	"github.com/spf13/cobra"
	"github.com/nodetemple/nodetemple/version"
)

var (
	cmdHelp = &cobra.Command{
		Use:   "help [command]",
		Short: "Help about any command",
		Long:  "Help provides help for any command in the application.\nSimply type " + cliName + " help [command] for full details",
		//Run:   runWrapper(runHelp),
	}

	commandUsageTemplate *template.Template
	templFuncs           = template.FuncMap{
		"descToLines": func(s string) []string {
			return strings.Split(strings.Trim(s, "\n\t "), "\n")
		},
		"cmdName": func(cmd *cobra.Command, startCmd *cobra.Command) string {
			parts := []string{cmd.Name()}
			for cmd.HasParent() && cmd.Parent().Name() != startCmd.Name() {
				cmd = cmd.Parent()
				parts = append([]string{cmd.Name()}, parts...)
			}
			return strings.Join(parts, " ")
		},
	}
)

func init() {
	cmdNodectl.AddCommand(cmdHelp)

	commandUsage := `
{{ $cmd := .Cmd }}\
{{ $cmdname := cmdName .Cmd .Cmd.Root }}\
NAME:
{{ if not .Cmd.HasParent }}\
{{printf "\t%s - %s" .Cmd.Name .Cmd.Short}}
{{else}}\
{{printf "\t%s - %s" $cmdname .Cmd.Short}}
{{end}}\

USAGE:
{{printf "\t%s" .Cmd.UseLine}}
{{ if not .Cmd.HasParent }}\

VERSION:
{{printf "\t%s" .Version}}
{{end}}\
{{if .Cmd.HasSubCommands}}\

COMMANDS:
{{range .SubCommands}}\
{{ $cmdname := cmdName . $cmd }}\
{{ if .Runnable }}\
{{printf "\t%s\t%s" $cmdname .Short}}
{{end}}\
{{end}}\
{{end}}\
{{ if .Cmd.Long }}\

DESCRIPTION:
{{range $line := descToLines .Cmd.Long}}{{printf "\t%s" $line}}
{{end}}\
{{end}}\
{{if .Cmd.HasLocalFlags}}\

{{ if not .Cmd.HasParent }}GLOBAL {{end}}FLAGS:
{{.Cmd.LocalFlags.FlagUsages}}\
{{ if not .Cmd.HasParent }}
Global flags can also be configured via upper-case environment variables prefixed with "{{.EnvFlag}}_"
For example: "--some-flag" => "{{.EnvFlag}}_SOME_FLAG"
{{end}}\
{{end}}\
{{if .Cmd.HasInheritedFlags}}\

GLOBAL FLAGS:
{{.Cmd.InheritedFlags.FlagUsages}}
Global flags can also be configured via upper-case environment variables prefixed with "{{.EnvFlag}}_"
For example: "--some-flag" => "{{.EnvFlag}}_SOME_FLAG"
{{end}}\
{{ if .Cmd.HasSubCommands }}
Run "{{.Cmd.CommandPath}} help [command]" for more information about a specific command
{{else}}
Run "{{.Executable}} help" for more information about a common usage
{{end}}`[1:]

	commandUsageTemplate = template.Must(template.New("command_usage").Funcs(templFuncs).Parse(strings.Replace(commandUsage, "\\\n", "", -1)))
}

func getSubCommands(cmd *cobra.Command) []*cobra.Command {
	subCommands := []*cobra.Command{}
	for _, subCmd := range cmd.Commands() {
		subCommands = append(subCommands, subCmd)
		subCommands = append(subCommands, getSubCommands(subCmd)...)
	}
	return subCommands
}

func usageFunc(cmd *cobra.Command) error {
	subCommands := getSubCommands(cmd)
	err := commandUsageTemplate.Execute(tabOut, struct {
		Executable  string
		Cmd         *cobra.Command
		SubCommands []*cobra.Command
		EnvFlag     string
		Version     string
	}{
		cliName,
		cmd,
		subCommands,
		strings.ToUpper(cliName),
		version.Version,
	})
	tabOut.Flush()
	return err
}
<commit_msg>Run help wrapper<commit_after>/*
Copyright 2015 Nodetemple <hostmaster@nodetemple.com>

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 main

import (
	"strings"
	"text/template"

	"github.com/spf13/cobra"
	"github.com/nodetemple/nodetemple/version"
)

var (
	cmdHelp = &cobra.Command{
		Use:   "help [command]",
		Short: "Help about any command",
		Long:  "Help provides help for any command in the application.\nSimply type " + cliName + " help [command] for full details",
		Run:   runWrapper(cobra.HelpFunc()),
	}

	commandUsageTemplate *template.Template
	templFuncs           = template.FuncMap{
		"descToLines": func(s string) []string {
			return strings.Split(strings.Trim(s, "\n\t "), "\n")
		},
		"cmdName": func(cmd *cobra.Command, startCmd *cobra.Command) string {
			parts := []string{cmd.Name()}
			for cmd.HasParent() && cmd.Parent().Name() != startCmd.Name() {
				cmd = cmd.Parent()
				parts = append([]string{cmd.Name()}, parts...)
			}
			return strings.Join(parts, " ")
		},
	}
)

func init() {
	cmdNodectl.AddCommand(cmdHelp)

	commandUsage := `
{{ $cmd := .Cmd }}\
{{ $cmdname := cmdName .Cmd .Cmd.Root }}\
NAME:
{{ if not .Cmd.HasParent }}\
{{printf "\t%s - %s" .Cmd.Name .Cmd.Short}}
{{else}}\
{{printf "\t%s - %s" $cmdname .Cmd.Short}}
{{end}}\

USAGE:
{{printf "\t%s" .Cmd.UseLine}}
{{ if not .Cmd.HasParent }}\

VERSION:
{{printf "\t%s" .Version}}
{{end}}\
{{if .Cmd.HasSubCommands}}\

COMMANDS:
{{range .SubCommands}}\
{{ $cmdname := cmdName . $cmd }}\
{{ if .Runnable }}\
{{printf "\t%s\t%s" $cmdname .Short}}
{{end}}\
{{end}}\
{{end}}\
{{ if .Cmd.Long }}\

DESCRIPTION:
{{range $line := descToLines .Cmd.Long}}{{printf "\t%s" $line}}
{{end}}\
{{end}}\
{{if .Cmd.HasLocalFlags}}\

{{ if not .Cmd.HasParent }}GLOBAL {{end}}FLAGS:
{{.Cmd.LocalFlags.FlagUsages}}\
{{ if not .Cmd.HasParent }}
Global flags can also be configured via upper-case environment variables prefixed with "{{.EnvFlag}}_"
For example: "--some-flag" => "{{.EnvFlag}}_SOME_FLAG"
{{end}}\
{{end}}\
{{if .Cmd.HasInheritedFlags}}\

GLOBAL FLAGS:
{{.Cmd.InheritedFlags.FlagUsages}}
Global flags can also be configured via upper-case environment variables prefixed with "{{.EnvFlag}}_"
For example: "--some-flag" => "{{.EnvFlag}}_SOME_FLAG"
{{end}}\
{{ if .Cmd.HasSubCommands }}
Run "{{.Cmd.CommandPath}} help [command]" for more information about a specific command
{{else}}
Run "{{.Executable}} help" for more information about a common usage
{{end}}`[1:]

	commandUsageTemplate = template.Must(template.New("command_usage").Funcs(templFuncs).Parse(strings.Replace(commandUsage, "\\\n", "", -1)))
}

func getSubCommands(cmd *cobra.Command) []*cobra.Command {
	subCommands := []*cobra.Command{}
	for _, subCmd := range cmd.Commands() {
		subCommands = append(subCommands, subCmd)
		subCommands = append(subCommands, getSubCommands(subCmd)...)
	}
	return subCommands
}

func usageFunc(cmd *cobra.Command) error {
	subCommands := getSubCommands(cmd)
	err := commandUsageTemplate.Execute(tabOut, struct {
		Executable  string
		Cmd         *cobra.Command
		SubCommands []*cobra.Command
		EnvFlag     string
		Version     string
	}{
		cliName,
		cmd,
		subCommands,
		strings.ToUpper(cliName),
		version.Version,
	})
	tabOut.Flush()
	return err
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"github.com/dynport/digo"
	"github.com/dynport/gocli"
)

func init() {
	cli.Register("version",
		&gocli.Action {
			Description: "Print version and revision",
			Handler: VersionAction,
		},
		)
}

func VersionAction(args *gocli.Args) error {
	table := gocli.NewTable()
	table.Add("Version", digo.VERSION)
	table.Add("Revision", digo.GITCOMMIT)
	fmt.Printf(table.String())
	return nil
}
<commit_msg>only print revision when defined<commit_after>package main

import (
	"fmt"
	"github.com/dynport/digo"
	"github.com/dynport/gocli"
)

func init() {
	cli.Register("version",
		&gocli.Action{
			Description: "Print version and revision",
			Handler:     VersionAction,
		},
	)
}

func VersionAction(args *gocli.Args) error {
	table := gocli.NewTable()
	table.Add("Version", digo.VERSION)
	if len(digo.GITCOMMIT) > 0 {
		table.Add("Revision", digo.GITCOMMIT)
	}
	fmt.Printf(table.String())
	return nil
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"errors"
	"fmt"
	"net/http"

	"github.com/zenazn/goji/web"
	"gopkg.in/mgo.v2"
)

var AddDeviceMissingDeviceIdError = ErrorJson{"POST to /devices must be a JSON with a DeviceId property."}

type SidewinderDirector struct {
	MongoDB          string
	session          *mgo.Session
	ApnsCommunicator *APNSCommunicator
}

func NewSidewinderDirector(mongoDB string, communicator *APNSCommunicator) (*SidewinderDirector, error) {
	session, err := mgo.Dial("mongo,localhost")
	if err != nil {
		return nil, err
	}
	return &SidewinderDirector{mongoDB, session, communicator}, nil
}

func (self *SidewinderDirector) Store() *SidewinderStore {
	return &SidewinderStore{self.MongoDB, self.session.Copy()}
}

func (self *SidewinderDirector) DatastoreInfo(context web.C, writer http.ResponseWriter, request *http.Request) error {
	session := self.Store().session

	buildInfo, err := session.BuildInfo()
	if err != nil {
		return fmt.Errorf("Could not connect to MongoDB.\n%v", err.Error())
	}
	writer.Header().Set("Content-Type", "application/json")

	databases, err := session.DatabaseNames()
	if err != nil {
		return fmt.Errorf("Could not retrieve database names.\n%v", err.Error())
	}

	dataStoreInfo := DatastoreInfo{buildInfo, session.LiveServers(), databases}

	return json.NewEncoder(writer).Encode(&dataStoreInfo)
}

func (self *SidewinderDirector) postDevice(context web.C, writer http.ResponseWriter, request *http.Request) error {
	sentJSON := decodeDeviceDocument(request)
	if sentJSON == nil {
		return writeJson(400, AddDeviceMissingDeviceIdError, writer)
	}
	recordWasCreated, err := self.Store().AddDevice(sentJSON.DeviceId)
	if err != nil {
		return err
	} else if recordWasCreated {
		return writeJson(201, sentJSON, writer)
	} else {
		return writeJson(200, sentJSON, writer)
	}
}

func decodeDeviceDocument(request *http.Request) *DeviceDocument {
	var sentJSON DeviceDocument
	if decodeErr := json.NewDecoder(request.Body).Decode(&sentJSON); decodeErr == nil && sentJSON.DeviceId != "" {
		return &sentJSON
	} else {
		return nil
	}
}

type DeviceHandler func(id string, writer http.ResponseWriter, request *http.Request) error

func (self DeviceHandler) ServeHTTPC(context web.C, writer http.ResponseWriter, request *http.Request) {
	deviceId := context.URLParams["id"]
	err := self(deviceId, writer, request)
	if err != nil {
		writeJson(500, ErrorJson{err.Error()}, writer)
	}
}

func (self *SidewinderDirector) deleteDevice(deviceId string, writer http.ResponseWriter, request *http.Request) error {
	result, err := self.Store().FindDevice(deviceId)
	if err != nil {
		return err
	}

	if err := self.Store().DeleteDevice(deviceId); err != nil {
		return err
	}

	return writeJson(200, result, writer)
}

func (self *SidewinderDirector) PostNotification(deviceId string, writer http.ResponseWriter, request *http.Request) error {
	var notification map[string]string
	if decodeErr := json.NewDecoder(request.Body).Decode(&notification); decodeErr != nil {
		return decodeErr
	}

	if err := self.ApnsCommunicator.sendPushNotification(deviceId, notification["Alert"]); err != nil {
		return err
	}
	return writeJson(201, notification, writer)
}

func (self *SidewinderDirector) CircleNotify(context web.C, writer http.ResponseWriter, request *http.Request) error {

	fmt.Fprintln(writer, "About to write the recieved header:")
	request.Header.Write(writer)
	fmt.Fprintln(writer, "Just wrote the recieved header:")

	var notification map[string]interface{}
	if decodeErr := json.NewDecoder(request.Body).Decode(&notification); decodeErr != nil {
		return decodeErr
	}

	payload, ok := notification["payload"].(map[string]interface{})
	if !ok {
		return errors.New("Sent JSON did not have a 'payload' object.")
	}

	vcsUrl, ok := payload["vcs_url"].(string)
	if !ok {
		return errors.New("Sent JSON did not have a 'vcs_url' string.")
	}

	fmt.Printf("Time to notify everyone registered for project %v\n", vcsUrl)
	return nil
}

func (self *SidewinderDirector) TravisNotify(context web.C, writer http.ResponseWriter, request *http.Request) error {

	fmt.Fprintln(writer, "About to write the recieved header:")
	request.Header.Write(writer)
	fmt.Fprintln(writer, "Just wrote the recieved header:")

	var notification map[string]interface{}
	if decodeErr := json.NewDecoder(request.Body).Decode(&notification); decodeErr != nil {
		return decodeErr
	}

	payload, ok := notification["payload"].(map[string]interface{})
	if !ok {
		return errors.New("Sent JSON did not have a 'payload' object.")
	}

	vcsUrl, ok := payload["vcs_url"].(string)
	if !ok {
		return errors.New("Sent JSON did not have a 'vcs_url' string.")
	}

	fmt.Printf("Time to notify everyone registered for project %v\n", vcsUrl)
	return nil
}
<commit_msg>Making the travis callback more coherent, outputting hte headers to the correct out stream<commit_after>package main

import (
	"encoding/json"
	"errors"
	"fmt"
	"net/http"
	"os"

	"github.com/zenazn/goji/web"
	"gopkg.in/mgo.v2"
)

var AddDeviceMissingDeviceIdError = ErrorJson{"POST to /devices must be a JSON with a DeviceId property."}

type SidewinderDirector struct {
	MongoDB          string
	session          *mgo.Session
	ApnsCommunicator *APNSCommunicator
}

func NewSidewinderDirector(mongoDB string, communicator *APNSCommunicator) (*SidewinderDirector, error) {
	session, err := mgo.Dial("mongo,localhost")
	if err != nil {
		return nil, err
	}
	return &SidewinderDirector{mongoDB, session, communicator}, nil
}

func (self *SidewinderDirector) Store() *SidewinderStore {
	return &SidewinderStore{self.MongoDB, self.session.Copy()}
}

func (self *SidewinderDirector) DatastoreInfo(context web.C, writer http.ResponseWriter, request *http.Request) error {
	session := self.Store().session

	buildInfo, err := session.BuildInfo()
	if err != nil {
		return fmt.Errorf("Could not connect to MongoDB.\n%v", err.Error())
	}
	writer.Header().Set("Content-Type", "application/json")

	databases, err := session.DatabaseNames()
	if err != nil {
		return fmt.Errorf("Could not retrieve database names.\n%v", err.Error())
	}

	dataStoreInfo := DatastoreInfo{buildInfo, session.LiveServers(), databases}

	return json.NewEncoder(writer).Encode(&dataStoreInfo)
}

func (self *SidewinderDirector) postDevice(context web.C, writer http.ResponseWriter, request *http.Request) error {
	sentJSON := decodeDeviceDocument(request)
	if sentJSON == nil {
		return writeJson(400, AddDeviceMissingDeviceIdError, writer)
	}
	recordWasCreated, err := self.Store().AddDevice(sentJSON.DeviceId)
	if err != nil {
		return err
	} else if recordWasCreated {
		return writeJson(201, sentJSON, writer)
	} else {
		return writeJson(200, sentJSON, writer)
	}
}

func decodeDeviceDocument(request *http.Request) *DeviceDocument {
	var sentJSON DeviceDocument
	if decodeErr := json.NewDecoder(request.Body).Decode(&sentJSON); decodeErr == nil && sentJSON.DeviceId != "" {
		return &sentJSON
	} else {
		return nil
	}
}

type DeviceHandler func(id string, writer http.ResponseWriter, request *http.Request) error

func (self DeviceHandler) ServeHTTPC(context web.C, writer http.ResponseWriter, request *http.Request) {
	deviceId := context.URLParams["id"]
	err := self(deviceId, writer, request)
	if err != nil {
		writeJson(500, ErrorJson{err.Error()}, writer)
	}
}

func (self *SidewinderDirector) deleteDevice(deviceId string, writer http.ResponseWriter, request *http.Request) error {
	result, err := self.Store().FindDevice(deviceId)
	if err != nil {
		return err
	}

	if err := self.Store().DeleteDevice(deviceId); err != nil {
		return err
	}

	return writeJson(200, result, writer)
}

func (self *SidewinderDirector) PostNotification(deviceId string, writer http.ResponseWriter, request *http.Request) error {
	var notification map[string]string
	if decodeErr := json.NewDecoder(request.Body).Decode(&notification); decodeErr != nil {
		return decodeErr
	}

	if err := self.ApnsCommunicator.sendPushNotification(deviceId, notification["Alert"]); err != nil {
		return err
	}
	return writeJson(201, notification, writer)
}

func (self *SidewinderDirector) CircleNotify(context web.C, writer http.ResponseWriter, request *http.Request) error {
	fmt.Fprintln(os.Stdout, "About to write the recieved header:")
	request.Header.Write(os.Stdout)
	fmt.Fprintln(os.Stdout, "Just wrote the recieved header:")

	var notification map[string]interface{}
	if decodeErr := json.NewDecoder(request.Body).Decode(&notification); decodeErr != nil {
		return decodeErr
	}

	payload, ok := notification["payload"].(map[string]interface{})
	if !ok {
		return errors.New("Sent JSON did not have a 'payload' object.")
	}

	vcsUrl, ok := payload["vcs_url"].(string)
	if !ok {
		return errors.New("Sent JSON did not have a 'vcs_url' string.")
	}

	fmt.Printf("Time to notify everyone registered for project %v\n", vcsUrl)
	return nil
}

func (self *SidewinderDirector) TravisNotify(context web.C, writer http.ResponseWriter, request *http.Request) error {

	fmt.Fprintln(os.Stdout, "About to write the recieved header:")
	request.Header.Write(os.Stdout)
	fmt.Fprintln(os.Stdout, "Just wrote the recieved header:")

	var notification map[string]interface{}
	if decodeErr := json.NewDecoder(request.Body).Decode(&notification); decodeErr != nil {
		return decodeErr
	}
	json.NewEncoder(os.Stdout).Encode(notification)
	return nil
}
<|endoftext|>


<commit_before>package piazza

import (
	"bytes"
	"encoding/json"
	"errors"
	"flag"
	"fmt"
	"log"
	"net/http"
	"os"
)

// (1) determine what my own address is
//
//     if -local, then
//         set serverAddress to "localhost:1234x"
//     else if $VCAP_APPLICATION is set, then
//         parse the value as JSON
//         set serverAddress to the "application_uris" string
//     else
//         panic
//     endif
//
// (2) determine where pz-discover lives
//
//     if -local, then
//         set discoverAddress to "localhost:3000"
//     else
//         set discoverAddress to "pz-discover.cf.piazzageo.io"
//     endif
//
// (3) register myself with pz-discover
//
//     do a POST to discoverAddress:
//         {
//             "name": "pz-myname",
//             "data": {
//                 "type": "core-service",
//                 "address": serverAddress,
//                 # other per-service stuff
//              }
//          }
//
// (4) start server
//
//     if -local, then
//         start server on "localhost:1234x"   # from mpg's dev settings
//     else if $PORT set, then
//         start server on ":$PORT"
//     else
//         panic
//     endif

type Config struct {
	Local           bool
	ServiceName     string
	ServerAddress   string
	BindTo          string
	DiscoverAddress string
}

func GetConfig(serviceName string, local bool) (*Config, error) {

	var config *Config
	var err error

	if local {
		config = getLocalConfig(serviceName)
	} else {
		config, err = getCFConfig(serviceName)
		if err != nil {
			return nil, err
		}
	}

	log.Printf("Config.Local: %t", config.Local)
	log.Printf("Config.ServerAddress: %s", config.ServerAddress)
	log.Printf("Config.ServiceName: %s", config.ServiceName)
	log.Printf("Config.DiscoverAddress: %s", config.DiscoverAddress)
	log.Printf("Config.BindTo: %s", config.BindTo)

	return config, err
}

func IsLocalConfig() bool {
	localFlag := flag.Bool("local", false, "use localhost ports")
	flag.Parse()
	return *localFlag
}

func getLocalConfig(serviceName string) *Config {

	var localHosts = map[string]string{
		"pz-logger":   "localhost:12341",
		"pz-uuidgen":  "localhost:12340",
		"pz-alerter":  "localhost:12342",
		"pz-discover": "localhost:3000",
	}

	config := Config{
		Local:           true,
		ServiceName:     serviceName,
		ServerAddress:   localHosts[serviceName],
		DiscoverAddress: localHosts["pz-discover"],
		BindTo:          localHosts[serviceName],
	}

	return &config
}

func getCFConfig(serviceName string) (*Config, error) {

	const nonlocalDiscoverHost = "pz-discover.cf.piazzageo.io"

	var config Config
	var err error

	config.Local = false

	config.ServiceName, config.ServerAddress, err = determineVcapServerAddress()
	if err != nil {
		return nil, err
	}

	config.DiscoverAddress = nonlocalDiscoverHost

	port := os.Getenv("PORT")
	if port == "" {
		return nil, errors.New("$PORT not found: unable to determine bindto address")
	}
	config.BindTo = ":" + port
	if err != nil {
		return nil, err
	}

	return &config, nil
}

func determineVcapServerAddress() (serviceName string, serverAddress string, err error) {

	vcapString := os.Getenv("VCAP_APPLICATION")
	if vcapString == "" {
		return "", "", errors.New("$VCAP_APPLICATION not found: unable to determine server address")
	}
	type VcapData struct {
		ApplicationID   string `json:"application_id"`
		ApplicationName string `json:"application_name"`
		ApplicationURIs []string `json:"application_uris"`
	}
	var vcap VcapData
	err = json.Unmarshal([]byte(vcapString), &vcap)
	if err != nil {
		return "", "", err
	}
	serviceName = vcap.ApplicationName
	serverAddress = vcap.ApplicationURIs[0]
	return serviceName, serverAddress, nil
}

type discoverDataDetail struct {
	Type    string `json:"type"`

	// TODO: which one of these to use?
	Host    string `json:"host"`
	Brokers string `json:"brokers"`
	Address string `json:"address"`
	DbUri   string `json:"db-uri"`
}
type discoverData struct {
	Name string      `json:"name"`
	Data interface{} `json:"data"`
}

func (config *Config) RegisterServiceWithDiscover() error {
	discDataDetail := discoverDataDetail{Type: "core-service", Host: config.ServerAddress}
	discData := discoverData{Name: config.ServiceName, Data: discDataDetail}
	data, err := json.Marshal(discData)
	if err != nil {
		return err
	}

	discoverUrl := fmt.Sprintf("http://%s/api/v1/resources", config.DiscoverAddress)
	log.Printf("registering to %s: %s", discoverUrl, string(data))
	resp, err := Put(discoverUrl, ContentTypeJSON, bytes.NewBuffer(data))
	if err != nil {
		return err
	}
	if resp.StatusCode != http.StatusCreated && resp.StatusCode != http.StatusOK {
		return errors.New("registration failed: " + resp.Status)
	}

	return nil
}
<commit_msg>fix json output<commit_after>package piazza

import (
	"bytes"
	"encoding/json"
	"errors"
	"flag"
	"fmt"
	"log"
	"net/http"
	"os"
)

// (1) determine what my own address is
//
//     if -local, then
//         set serverAddress to "localhost:1234x"
//     else if $VCAP_APPLICATION is set, then
//         parse the value as JSON
//         set serverAddress to the "application_uris" string
//     else
//         panic
//     endif
//
// (2) determine where pz-discover lives
//
//     if -local, then
//         set discoverAddress to "localhost:3000"
//     else
//         set discoverAddress to "pz-discover.cf.piazzageo.io"
//     endif
//
// (3) register myself with pz-discover
//
//     do a POST to discoverAddress:
//         {
//             "name": "pz-myname",
//             "data": {
//                 "type": "core-service",
//                 "address": serverAddress,
//                 # other per-service stuff
//              }
//          }
//
// (4) start server
//
//     if -local, then
//         start server on "localhost:1234x"   # from mpg's dev settings
//     else if $PORT set, then
//         start server on ":$PORT"
//     else
//         panic
//     endif

type Config struct {
	Local           bool
	ServiceName     string
	ServerAddress   string
	BindTo          string
	DiscoverAddress string
}

func GetConfig(serviceName string, local bool) (*Config, error) {

	var config *Config
	var err error

	if local {
		config = getLocalConfig(serviceName)
	} else {
		config, err = getCFConfig(serviceName)
		if err != nil {
			return nil, err
		}
	}

	log.Printf("Config.Local: %t", config.Local)
	log.Printf("Config.ServerAddress: %s", config.ServerAddress)
	log.Printf("Config.ServiceName: %s", config.ServiceName)
	log.Printf("Config.DiscoverAddress: %s", config.DiscoverAddress)
	log.Printf("Config.BindTo: %s", config.BindTo)

	return config, err
}

func IsLocalConfig() bool {
	localFlag := flag.Bool("local", false, "use localhost ports")
	flag.Parse()
	return *localFlag
}

func getLocalConfig(serviceName string) *Config {

	var localHosts = map[string]string{
		"pz-logger":   "localhost:12341",
		"pz-uuidgen":  "localhost:12340",
		"pz-alerter":  "localhost:12342",
		"pz-discover": "localhost:3000",
	}

	config := Config{
		Local:           true,
		ServiceName:     serviceName,
		ServerAddress:   localHosts[serviceName],
		DiscoverAddress: localHosts["pz-discover"],
		BindTo:          localHosts[serviceName],
	}

	return &config
}

func getCFConfig(serviceName string) (*Config, error) {

	const nonlocalDiscoverHost = "pz-discover.cf.piazzageo.io"

	var config Config
	var err error

	config.Local = false

	config.ServiceName, config.ServerAddress, err = determineVcapServerAddress()
	if err != nil {
		return nil, err
	}

	config.DiscoverAddress = nonlocalDiscoverHost

	port := os.Getenv("PORT")
	if port == "" {
		return nil, errors.New("$PORT not found: unable to determine bindto address")
	}
	config.BindTo = ":" + port
	if err != nil {
		return nil, err
	}

	return &config, nil
}

func determineVcapServerAddress() (serviceName string, serverAddress string, err error) {

	vcapString := os.Getenv("VCAP_APPLICATION")
	if vcapString == "" {
		return "", "", errors.New("$VCAP_APPLICATION not found: unable to determine server address")
	}
	type VcapData struct {
		ApplicationID   string `json:"application_id"`
		ApplicationName string `json:"application_name"`
		ApplicationURIs []string `json:"application_uris"`
	}
	var vcap VcapData
	err = json.Unmarshal([]byte(vcapString), &vcap)
	if err != nil {
		return "", "", err
	}
	serviceName = vcap.ApplicationName
	serverAddress = vcap.ApplicationURIs[0]
	return serviceName, serverAddress, nil
}

type discoverDataDetail struct {
	Type    string `json:"type"`

	// TODO: which one of these to use?
	Host    string `json:"host"`
	Brokers string `json:"brokers,omitempty"`
	Address string `json:"address,omitempty"`
	DbUri   string `json:"db-uri,omitempty"`
}
type discoverData struct {
	Name string      `json:"name"`
	Data interface{} `json:"data"`
}

func (config *Config) RegisterServiceWithDiscover() error {
	discDataDetail := discoverDataDetail{Type: "core-service", Host: config.ServerAddress}
	discData := discoverData{Name: config.ServiceName, Data: discDataDetail}
	data, err := json.Marshal(discData)
	if err != nil {
		return err
	}

	discoverUrl := fmt.Sprintf("http://%s/api/v1/resources", config.DiscoverAddress)
	log.Printf("registering to %s: %s", discoverUrl, string(data))
	resp, err := Put(discoverUrl, ContentTypeJSON, bytes.NewBuffer(data))
	if err != nil {
		return err
	}
	if resp.StatusCode != http.StatusCreated && resp.StatusCode != http.StatusOK {
		return errors.New("registration failed: " + resp.Status)
	}

	return nil
}
<|endoftext|>