<commit_before>/*
 Copyright 2020 Qiniu Cloud (qiniu.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 parser

import (
	"fmt"
	"go/token"
	"reflect"
	"testing"

	"github.com/qiniu/qlang/v6/ast"
	"github.com/qiniu/qlang/v6/ast/asttest"
)

// -----------------------------------------------------------------------------

var fsTestStd = asttest.NewSingleFileFS("/foo", "bar.ql", `package bar; import "io"
	// comment
	x := 0
	if t := false; t {
		x = 3
	} else {
		x = 5
	}
	println("x:", x)

	// comment 1
	// comment 2
	x = 0
	switch s := "Hello"; s {
	default:
		x = 7
	case "world", "hi":
		x = 5
	case "xsw":
		x = 3
	}
	println("x:", x)

	c := make(chan bool, 100)
	select {
	case c <- true:
	case v := <-c:
	default:
		panic("error")
	}
`)

func TestStd(t *testing.T) {
	fset := token.NewFileSet()
	pkgs, err := ParseFSDir(fset, fsTestStd, "/foo", nil, ParseComments)
	if err != nil || len(pkgs) != 1 {
		t.Fatal("ParseFSDir failed:", err, len(pkgs))
	}
	bar := pkgs["bar"]
	file := bar.Files["/foo/bar.ql"]
	fmt.Println("Pkg:", file.Name)
	for _, decl := range file.Decls {
		fmt.Println("decl:", reflect.TypeOf(decl))
		switch d := decl.(type) {
		case *ast.GenDecl:
			for _, spec := range d.Specs {
				switch v := spec.(type) {
				case *ast.ImportSpec:
					fmt.Println(" - import:", v.Path.Value)
				}
			}
		case *ast.FuncDecl:
			fmt.Println(" - func:", d.Name.Name)
		}
	}
}

// -----------------------------------------------------------------------------

var fsTestStd2 = asttest.NewSingleFileFS("/foo", "bar.ql", `package bar; import "io"
	x := []float64{1, 3.4, 5}
	y := map[string]float64{"Hello": 1, "xsw": 3.4}
	println("x:", x, "y:", y)

	a := [...]float64{1, 3.4, 5}
	b := [...]float64{1, 3: 3.4, 5}
	c := []float64{2: 1.2, 3, 6: 4.5}
	println("a:", a, "b:", b, "c:", c)
`)

func TestStd2(t *testing.T) {
	fset := token.NewFileSet()
	pkgs, err := ParseFSDir(fset, fsTestStd2, "/foo", nil, 0)
	if err != nil || len(pkgs) != 1 {
		t.Fatal("ParseFSDir failed:", err, len(pkgs))
	}
	bar := pkgs["bar"]
	file := bar.Files["/foo/bar.ql"]
	fmt.Println("Pkg:", file.Name)
	for _, decl := range file.Decls {
		fmt.Println("decl:", reflect.TypeOf(decl))
		switch d := decl.(type) {
		case *ast.GenDecl:
			for _, spec := range d.Specs {
				switch v := spec.(type) {
				case *ast.ImportSpec:
					fmt.Println(" - import:", v.Path.Value)
				}
			}
		case *ast.FuncDecl:
			fmt.Println(" - func:", d.Name.Name)
		}
	}
}

// -----------------------------------------------------------------------------

var fsTestStdFor = asttest.NewSingleFileFS("/foo", "bar.ql", `
	n := 0
	for range [1, 3, 5, 7, 11] {
		n++
	}
	println("n:", n)

	sum := 0
	for _, x := range [1, 3, 5, 7, 11] {
		if x > 3 {
			sum += x
		}
	}
	println("sum(1,3,5,7,11):", sum)

	sum = 0
	for i := 1; i < 100; i++ {
		sum += i
	}
	println("sum(1-100):", sum)
`)

func TestStdFor(t *testing.T) {
	fset := token.NewFileSet()
	pkgs, err := ParseFSDir(fset, fsTestStdFor, "/foo", nil, 0)
	if err != nil || len(pkgs) != 1 {
		t.Fatal("ParseFSDir failed:", err, len(pkgs))
	}
	bar := pkgs["main"]
	file := bar.Files["/foo/bar.ql"]
	fmt.Println("Pkg:", file.Name)
	for _, decl := range file.Decls {
		fmt.Println("decl:", reflect.TypeOf(decl))
		switch d := decl.(type) {
		case *ast.GenDecl:
			for _, spec := range d.Specs {
				switch v := spec.(type) {
				case *ast.ImportSpec:
					fmt.Println(" - import:", v.Path.Value)
				}
			}
		case *ast.FuncDecl:
			fmt.Println(" - func:", d.Name.Name)
		}
	}
}

// -----------------------------------------------------------------------------

var fsTestBuild = asttest.NewSingleFileFS("/foo", "bar.ql", `
	type cstring string

	title := "Hello,world!2020-05-27"
	s := (*cstring)(&title)
	println(title[0:len(title)-len("2006-01-02")])
`)

func TestBuild(t *testing.T) {
	fset := token.NewFileSet()
	pkgs, err := ParseFSDir(fset, fsTestBuild, "/foo", nil, 0)
	if err != nil || len(pkgs) != 1 {
		t.Fatal("ParseFSDir failed:", err, len(pkgs))
	}
	bar := pkgs["main"]
	file := bar.Files["/foo/bar.ql"]
	fmt.Println("Pkg:", file.Name)
	for _, decl := range file.Decls {
		fmt.Println("decl:", reflect.TypeOf(decl))
		switch d := decl.(type) {
		case *ast.GenDecl:
			for _, spec := range d.Specs {
				switch v := spec.(type) {
				case *ast.ImportSpec:
					fmt.Println(" - import:", v.Path.Value)
				}
			}
		case *ast.FuncDecl:
			fmt.Println(" - func:", d.Name.Name)
		}
	}
}

// -----------------------------------------------------------------------------
<commit_msg>parser: TestFromTestdata<commit_after>/*
 Copyright 2020 Qiniu Cloud (qiniu.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 parser

import (
	"fmt"
	"io/ioutil"
	"os"
	"path"
	"reflect"
	"testing"

	"github.com/qiniu/qlang/v6/ast"
	"github.com/qiniu/qlang/v6/ast/asttest"
	"github.com/qiniu/qlang/v6/token"
)

// -----------------------------------------------------------------------------

var fsTestStd = asttest.NewSingleFileFS("/foo", "bar.ql", `package bar; import "io"
	// comment
	x := 0
	if t := false; t {
		x = 3
	} else {
		x = 5
	}
	println("x:", x)

	// comment 1
	// comment 2
	x = 0
	switch s := "Hello"; s {
	default:
		x = 7
	case "world", "hi":
		x = 5
	case "xsw":
		x = 3
	}
	println("x:", x)

	c := make(chan bool, 100)
	select {
	case c <- true:
	case v := <-c:
	default:
		panic("error")
	}
`)

func TestStd(t *testing.T) {
	fset := token.NewFileSet()
	pkgs, err := ParseFSDir(fset, fsTestStd, "/foo", nil, ParseComments)
	if err != nil || len(pkgs) != 1 {
		t.Fatal("ParseFSDir failed:", err, len(pkgs))
	}
	bar := pkgs["bar"]
	file := bar.Files["/foo/bar.ql"]
	fmt.Println("Pkg:", file.Name)
	for _, decl := range file.Decls {
		fmt.Println("decl:", reflect.TypeOf(decl))
		switch d := decl.(type) {
		case *ast.GenDecl:
			for _, spec := range d.Specs {
				switch v := spec.(type) {
				case *ast.ImportSpec:
					fmt.Println(" - import:", v.Path.Value)
				}
			}
		case *ast.FuncDecl:
			fmt.Println(" - func:", d.Name.Name)
		}
	}
}

// -----------------------------------------------------------------------------

var fsTestStd2 = asttest.NewSingleFileFS("/foo", "bar.ql", `package bar; import "io"
	x := []float64{1, 3.4, 5}
	y := map[string]float64{"Hello": 1, "xsw": 3.4}
	println("x:", x, "y:", y)

	a := [...]float64{1, 3.4, 5}
	b := [...]float64{1, 3: 3.4, 5}
	c := []float64{2: 1.2, 3, 6: 4.5}
	println("a:", a, "b:", b, "c:", c)
`)

func TestStd2(t *testing.T) {
	fset := token.NewFileSet()
	pkgs, err := ParseFSDir(fset, fsTestStd2, "/foo", nil, 0)
	if err != nil || len(pkgs) != 1 {
		t.Fatal("ParseFSDir failed:", err, len(pkgs))
	}
	bar := pkgs["bar"]
	file := bar.Files["/foo/bar.ql"]
	fmt.Println("Pkg:", file.Name)
	for _, decl := range file.Decls {
		fmt.Println("decl:", reflect.TypeOf(decl))
		switch d := decl.(type) {
		case *ast.GenDecl:
			for _, spec := range d.Specs {
				switch v := spec.(type) {
				case *ast.ImportSpec:
					fmt.Println(" - import:", v.Path.Value)
				}
			}
		case *ast.FuncDecl:
			fmt.Println(" - func:", d.Name.Name)
		}
	}
}

// -----------------------------------------------------------------------------

var fsTestStdFor = asttest.NewSingleFileFS("/foo", "bar.ql", `
	n := 0
	for range [1, 3, 5, 7, 11] {
		n++
	}
	println("n:", n)

	sum := 0
	for _, x := range [1, 3, 5, 7, 11] {
		if x > 3 {
			sum += x
		}
	}
	println("sum(1,3,5,7,11):", sum)

	sum = 0
	for i := 1; i < 100; i++ {
		sum += i
	}
	println("sum(1-100):", sum)
`)

func TestStdFor(t *testing.T) {
	fset := token.NewFileSet()
	pkgs, err := ParseFSDir(fset, fsTestStdFor, "/foo", nil, 0)
	if err != nil || len(pkgs) != 1 {
		t.Fatal("ParseFSDir failed:", err, len(pkgs))
	}
	bar := pkgs["main"]
	file := bar.Files["/foo/bar.ql"]
	fmt.Println("Pkg:", file.Name)
	for _, decl := range file.Decls {
		fmt.Println("decl:", reflect.TypeOf(decl))
		switch d := decl.(type) {
		case *ast.GenDecl:
			for _, spec := range d.Specs {
				switch v := spec.(type) {
				case *ast.ImportSpec:
					fmt.Println(" - import:", v.Path.Value)
				}
			}
		case *ast.FuncDecl:
			fmt.Println(" - func:", d.Name.Name)
		}
	}
}

// -----------------------------------------------------------------------------

var fsTestBuild = asttest.NewSingleFileFS("/foo", "bar.ql", `
	type cstring string

	title := "Hello,world!2020-05-27"
	s := (*cstring)(&title)
	println(title[0:len(title)-len("2006-01-02")])
`)

func TestBuild(t *testing.T) {
	fset := token.NewFileSet()
	pkgs, err := ParseFSDir(fset, fsTestBuild, "/foo", nil, 0)
	if err != nil || len(pkgs) != 1 {
		t.Fatal("ParseFSDir failed:", err, len(pkgs))
	}
	bar := pkgs["main"]
	file := bar.Files["/foo/bar.ql"]
	fmt.Println("Pkg:", file.Name)
	for _, decl := range file.Decls {
		fmt.Println("decl:", reflect.TypeOf(decl))
		switch d := decl.(type) {
		case *ast.GenDecl:
			for _, spec := range d.Specs {
				switch v := spec.(type) {
				case *ast.ImportSpec:
					fmt.Println(" - import:", v.Path.Value)
				}
			}
		case *ast.FuncDecl:
			fmt.Println(" - func:", d.Name.Name)
		}
	}
}

// -----------------------------------------------------------------------------

func testFrom(t *testing.T, pkgDir string) {
	fset := token.NewFileSet()
	pkgs, err := ParseDir(fset, pkgDir, nil, 0)
	if err != nil || len(pkgs) != 1 {
		t.Fatal("ParseDir failed:", err, len(pkgs))
	}
}

func TestFromTestdata(t *testing.T) {
	dir, err := os.Getwd()
	if err != nil {
		t.Fatal("Getwd failed:", err)
	}
	dir = path.Join(dir, "../exec/golang/testdata")
	fis, err := ioutil.ReadDir(dir)
	if err != nil {
		t.Fatal("ReadDir failed:", err)
	}
	for _, fi := range fis {
		testFrom(t, dir+"/"+fi.Name())
	}
}

// -----------------------------------------------------------------------------
<|endoftext|>


<commit_before>package main

import (
	"crypto/sha256"
	"encoding/json"
	"flag"
	"fmt"
	"gnat"
	"log"
	"net/http"

	b58 "github.com/jbenet/go-base58"
)

var addr = flag.String("localhost", ":80", "http service address")
var dht *gnat.DHT
var hub *Hub

func main() {
	initializeDHT()
	setupServer()
}

func onForwardRequestReceived(forwardToIP string, rqst []byte) {
	hub.sendMessageToAddr(forwardToIP, rqst)
}

func onForwardData(fromAddr string, header map[string]string, data []byte) {

	resp := map[string]string{}

	sendTo := header["send_to"]
	if sendTo == "" {
		return
	}

	fmt.Println("Received forwarding request from " + fromAddr)

	resp["from"] = fromAddr

	respHeader, _ := json.Marshal(resp)
	forwardMessage(sendTo, append(respHeader, data...))
}

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

	// generate digest hash of IP address
	ipDigest := sha256.Sum256([]byte(r.RemoteAddr))
	id := b58.Encode(ipDigest[:])

	// find the node connected to this client ip
	node, err := dht.FindNode(id)

	if err == nil {

		if string(node.ID) == string(dht.GetSelfID()) {
			fmt.Println("Client accepted by " + node.IP.String())
			log.Println(r.URL)

			if r.URL.Path != "/" {
				http.Error(w, "Not found", 404)
				return
			}

			if r.Method != "GET" {
				http.Error(w, "Method not allowed", 405)
				return
			}

			http.ServeFile(w, r, "./static/home.html")

		} else {
			fmt.Println("Redirecting to http:/" + node.IP.String())
			http.Redirect(w, r, "http:/"+node.IP.String(), 301)
		}

	} else {
		fmt.Println(err)
	}
}

func setupServer() {
	flag.Parse()
	hub = newHub()
	go hub.run(onForwardData)
	http.HandleFunc("/", handConnectionRequest)
	http.HandleFunc("/ws", func(w http.ResponseWriter, r *http.Request) {
		serveWs(hub, w, r)
	})
	fmt.Println("Waiting for clients on " + *addr)
	err := http.ListenAndServe(*addr, nil)
	if err != nil {
		log.Fatal("ListenAndServe: ", err)
	}
}

func initializeDHT() {
	var ip = flag.String("ip", "0.0.0.0", "IP Address to use")
	var port = flag.String("port", "2222", "Port to use")
	var bIP = flag.String("bip", "", "IP Address to bootstrap against")
	var bPort = flag.String("bport", "", "Port to bootstrap against")
	var stun = flag.Bool("stun", false, "Use STUN")

	flag.Parse()

	var bootstrapNodes []*gnat.NetworkNode
	if *bIP != "" || *bPort != "" {
		bootstrapNode := gnat.NewNetworkNode(*bIP, *bPort)
		bootstrapNodes = append(bootstrapNodes, bootstrapNode)
	}

	var err error
	dht, err = gnat.NewDHT(&gnat.Options{
		BootstrapNodes:   bootstrapNodes,
		IP:               *ip,
		Port:             *port,
		UseStun:          *stun,
		OnForwardRequest: onForwardRequestReceived,
	})

	fmt.Println("Opening socket..")

	if *stun {
		fmt.Println("Discovering public address using STUN..")
	}

	err = dht.CreateSocket()
	if err != nil {
		panic(err)
	}
	fmt.Println("..done")

	go func() {
		fmt.Println("GNAT Kademlia listening on " + dht.GetNetworkAddr())
		err := dht.Listen()
		panic(err)
	}()

	if len(bootstrapNodes) > 0 {
		fmt.Println("Bootstrapping..")
		dht.Bootstrap()
		fmt.Println("..done")
	}
}

func forwardMessage(ip string, msg []byte) {
	ipDigest := sha256.Sum256([]byte(ip))
	id := b58.Encode(ipDigest[:])
	fmt.Println("Searching for forwarding node...")
	node, err := dht.FindNode(id)

	if err != nil {
		fmt.Println(err.Error())
	} else {
		fmt.Println("..forwarding node found:", node.IP.String())
		dht.ForwardData(node, gnat.NewNetworkNode(ip, "0"), msg)
	}
}
<commit_msg>added printing and network testing<commit_after>package main

import (
	"crypto/sha256"
	"encoding/json"
	"flag"
	"fmt"
	"gnat"
	"log"
	"net/http"

	"github.com/ccding/go-stun/stun"
	b58 "github.com/jbenet/go-base58"
)

var addr = flag.String("localhost", ":2222", "http service address")
var dht *gnat.DHT
var hub *Hub

func main() {
	// test the network to discovery what type of NAT (if any)
	// the client is behind.

	fmt.Println("GNAT Node v0.0.1")
	fmt.Println("  * Documentation:  https://gnat.cs.brown.edu/docs")
	fmt.Println("  * Support:  	     https://gnat.cs.brown.edu/support")
	fmt.Println("  * GitHub:  	     https://github.com/ogisan/gnat")
	fmt.Println("  For more information, visit: http://gnat.cs.brown.edu")
	fmt.Println("--------------------------------------------------------")
	fmt.Print("1) Testing network...")
	nat, host, err := stun.NewClient().Discover()
	if err != nil {
		fmt.Println("Error:a problem occured while testing your network.")
		fmt.Println("TODO: try again later.")
	}

	fmt.Println("done.")
	// acceptable type of NATs
	if nat == stun.NATNone || nat == stun.NATFull {
		fmt.Println("Network NAT configuration: " + nat.String())
		fmt.Println("Node address: " + host.String())
		initializeDHT()
		setupServer()
		fmt.Println("GNAT node setup and running!")
	} else {
		fmt.Println("Error: your network configuration does not support running a GNAT node.")
		fmt.Println("TODO: update your router settings to have less restrictive settings and try again.")
	}
}

func onForwardRequestReceived(forwardToIP string, rqst []byte) {
	hub.sendMessageToAddr(forwardToIP, rqst)
}

func onForwardData(fromAddr string, header map[string]string, data []byte) {

	resp := map[string]string{}

	sendTo := header["send_to"]
	if sendTo == "" {
		return
	}

	fmt.Println("Received forwarding request from " + fromAddr)

	resp["from"] = fromAddr
	respHeader, _ := json.Marshal(resp)
	forwardMessage(sendTo, append(respHeader, data...))
}

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

	// generate digest hash of IP address
	ipDigest := sha256.Sum256([]byte(r.RemoteAddr))
	id := b58.Encode(ipDigest[:])

	// find the node connected to this client ip
	node, err := dht.FindNode(id)

	if err == nil {

		if string(node.ID) == string(dht.GetSelfID()) {
			fmt.Println("Client accepted by " + node.IP.String())
			log.Println(r.URL)

			if r.URL.Path != "/" {
				http.Error(w, "Not found", 404)
				return
			}

			if r.Method != "GET" {
				http.Error(w, "Method not allowed", 405)
				return
			}

			http.ServeFile(w, r, "./static/home.html")

		} else {
			fmt.Println("Redirecting to http:/" + node.IP.String())
			http.Redirect(w, r, "http:/"+node.IP.String(), 301)
		}

	} else {
		fmt.Println(err)
	}
}

func setupServer() {

	fmt.Print("4) Setting up HTTP server...")
	flag.Parse()
	hub = newHub()
	go hub.run(onForwardData)
	http.HandleFunc("/", handConnectionRequest)
	http.HandleFunc("/ws", func(w http.ResponseWriter, r *http.Request) {
		serveWs(hub, w, r)
	})
	fmt.Println("done.")
	fmt.Println("Listening on http://127.0.0.1" + *addr)
	err := http.ListenAndServe(*addr, nil)
	if err != nil {
		log.Fatal("ListenAndServe: ", err)
	}
}

func initializeDHT() {
	var ip = flag.String("ip", "0.0.0.0", "IP Address to use")
	var port = flag.String("port", "1443", "Port to use")
	var bIP = flag.String("bip", "45.55.18.163", "IP Address to bootstrap against")
	var bPort = flag.String("bport", "1443", "Port to bootstrap against")
	var stun = flag.Bool("stun", false, "Use STUN")

	flag.Parse()

	var bootstrapNodes []*gnat.NetworkNode
	if *bIP != "" || *bPort != "" {
		bootstrapNode := gnat.NewNetworkNode(*bIP, *bPort)
		bootstrapNodes = append(bootstrapNodes, bootstrapNode)
	}

	var err error
	dht, err = gnat.NewDHT(&gnat.Options{
		BootstrapNodes:   bootstrapNodes,
		IP:               *ip,
		Port:             *port,
		UseStun:          *stun,
		OnForwardRequest: onForwardRequestReceived,
	})

	fmt.Print("2) Opening socket...")

	err = dht.CreateSocket()
	if err != nil {
		panic(err)
	}
	fmt.Println("done.")

	go func() {
		fmt.Println("--Socket open on " + dht.GetNetworkAddr())
		err := dht.Listen()
		panic(err)
	}()

	if len(bootstrapNodes) > 0 {
		fmt.Print("3) Bootstrapping into GNAT network...")
		dht.Bootstrap()
		fmt.Println("done.")
	}
}

func forwardMessage(ip string, msg []byte) {
	ipDigest := sha256.Sum256([]byte(ip))
	id := b58.Encode(ipDigest[:])
	fmt.Println("Searching for forwarding node...")
	node, err := dht.FindNode(id)

	if err != nil {
		fmt.Println(err.Error())
	} else {
		fmt.Println("..forwarding node found:", node.IP.String())
		dht.ForwardData(node, gnat.NewNetworkNode(ip, "0"), msg)
	}
}
<|endoftext|>


<commit_before>// +build !windows

package parser

import (
	"fmt"
	"os"
	"path"
	"strings"
)

func getPkgPath(fname string, isDir bool) (string, error) {
	if !path.IsAbs(fname) {
		pwd, err := os.Getwd()
		if err != nil {
			return "", err
		}
		fname = path.Join(pwd, fname)
	}

	gopath := os.Getenv("GOPATH")
	if gopath == "" {
		var err error
		gopath, err = getDefaultGoPath()
		if err != nil {
			return "", fmt.Errorf("cannot determine GOPATH: %s", err)
		}
	}

	for _, p := range strings.Split(os.Getenv("GOPATH"), ":") {
		prefix := path.Join(p, "src") + "/"
		if rel := strings.TrimPrefix(fname, prefix); rel != fname {
			if !isDir {
				return path.Dir(rel), nil
			} else {
				return path.Clean(rel), nil
			}
		}
	}

	return "", fmt.Errorf("file '%v' is not in GOPATH", fname)
}
<commit_msg> #162 use default GOPATH<commit_after>// +build !windows

package parser

import (
	"fmt"
	"os"
	"path"
	"strings"
)

func getPkgPath(fname string, isDir bool) (string, error) {
	if !path.IsAbs(fname) {
		pwd, err := os.Getwd()
		if err != nil {
			return "", err
		}
		fname = path.Join(pwd, fname)
	}

	gopath := os.Getenv("GOPATH")
	if gopath == "" {
		var err error
		gopath, err = getDefaultGoPath()
		if err != nil {
			return "", fmt.Errorf("cannot determine GOPATH: %s", err)
		}
	}

	for _, p := range strings.Split(gopath, ":") {
		prefix := path.Join(p, "src") + "/"
		if rel := strings.TrimPrefix(fname, prefix); rel != fname {
			if !isDir {
				return path.Dir(rel), nil
			} else {
				return path.Clean(rel), nil
			}
		}
	}

	return "", fmt.Errorf("file '%v' is not in GOPATH", fname)
}
<|endoftext|>


<commit_before>// Package errs is a modified copy of the upspin.io/errors package.
// Originally, I used quite a bit of the upspin.io/errors package,
// but have moved to only use a very small amount of it. Even still,
// I think it's appropriate to leave the license information in...
//
// Copyright 2016 The Upspin Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Package errs defines the error handling used by all Upspin software.
package errs

import (
	"fmt"
	"runtime"

	"github.com/pkg/errors"
)

// Error is the type that implements the error interface.
// It contains a number of fields, each of different type.
// An Error value may leave some values unset.
type Error struct {
	// User is the username of the user attempting the operation.
	User UserName
	// Kind is the class of error, such as permission failure,
	// or "Other" if its class is unknown or irrelevant.
	Kind Kind
	// Param is for when the error is parameter-specific and represents the parameter
	// related to the error.
	Param Parameter
	// Code is a human-readable, short representation of the error
	Code Code
	// The underlying error that triggered this one, if any.
	Err error
}

func (e *Error) isZero() bool {
	return e.User == "" && e.Kind == 0 && e.Param == "" && e.Code == "" && e.Err == nil
}

// Unwrap method allows for unwrapping errors using errors.As
func (e Error) Unwrap() error {
	return e.Err
}

func (e *Error) Error() string {
	return e.Err.Error()
}

// UserName is a string representing a user
type UserName string

// Kind defines the kind of error this is, mostly for use by systems
// such as FUSE that must act differently depending on the error.
type Kind uint8

// Parameter is for parameter-specific errors and represents
// the parameter related to the error.
type Parameter string

// Code is a human-readable, short representation of the error
type Code string

// Kinds of errors.
//
// The values of the error kinds are common between both
// clients and servers. Do not reorder this list or remove
// any items since that will change their values.
// New items must be added only to the end.
const (
	Other           Kind = iota // Unclassified error. This value is not printed in the error message.
	Invalid                     // Invalid operation for this type of item.
	Permission                  // Permission denied.
	IO                          // External I/O error such as network failure.
	Exist                       // Item already exists.
	NotExist                    // Item does not exist.
	Private                     // Information withheld.
	Internal                    // Internal error or inconsistency.
	BrokenLink                  // Link target does not exist.
	Database                    // Error from database.
	Validation                  // Input validation error.
	Unanticipated               // Unanticipated error.
	InvalidRequest              // Invalid Request
	Unauthenticated             // User did not properly authenticate
	Unauthorized                // User is not authorized for the resource
)

func (k Kind) String() string {
	switch k {
	case Other:
		return "other_error"
	case Invalid:
		return "invalid_operation"
	case Permission:
		return "permission_denied"
	case IO:
		return "I/O_error"
	case Exist:
		return "item_already_exists"
	case NotExist:
		return "item_does_not_exist"
	case BrokenLink:
		return "link_target_does_not_exist"
	case Private:
		return "information_withheld"
	case Internal:
		return "internal_error"
	case Database:
		return "database_error"
	case Validation:
		return "input_validation_error"
	case Unanticipated:
		return "unanticipated_error"
	case InvalidRequest:
		return "invalid_request_error"
	case Unauthenticated:
		return "unauthenticated"
	case Unauthorized:
		return "unauthorized"
	}
	return "unknown_error_kind"
}

// E builds an error value from its arguments.
// There must be at least one argument or E panics.
// The type of each argument determines its meaning.
// If more than one argument of a given type is presented,
// only the last one is recorded.
//
// The types are:
//	UserName
//		The username of the user attempting the operation.
//	string
//		Treated as an error message and assigned to the
//		Err field after a call to errors.New.
//	errors.Kind
//		The class of error, such as permission failure.
//	error
//		The underlying error that triggered this one.
//
// If the error is printed, only those items that have been
// set to non-zero values will appear in the result.
//
// If Kind is not specified or Other, we set it to the Kind of
// the underlying error.
//
func E(args ...interface{}) error {
	type stackTracer interface {
		StackTrace() errors.StackTrace
	}

	if len(args) == 0 {
		panic("call to errors.E with no arguments")
	}
	e := &Error{}
	for _, arg := range args {
		switch arg := arg.(type) {
		case UserName:
			e.User = arg
		case string:
			e.Err = errors.New(arg)
		case Kind:
			e.Kind = arg
		case *Error:
			e.Err = errors.WithStack(arg)
		case error:
			// if the error implements stackTracer, then it is
			// a pkg/errors error type and does not need to have
			// the stack added
			_, ok := arg.(stackTracer)
			if ok {
				e.Err = arg
			} else {
				e.Err = errors.WithStack(arg)
			}
		case Code:
			e.Code = arg
		case Parameter:
			e.Param = arg
		default:
			_, file, line, _ := runtime.Caller(1)
			return fmt.Errorf("errors.E: bad call from %s:%d: %v, unknown type %T, value %v in error call", file, line, args, arg, arg)
		}
	}

	prev, ok := e.Err.(*Error)
	if !ok {
		return e
	}
	// If this error has Kind unset or Other, pull up the inner one.
	if e.Kind == Other {
		e.Kind = prev.Kind
		prev.Kind = Other
	}

	if prev.Code == e.Code {
		prev.Code = ""
	}
	// If this error has Code == "", pull up the inner one.
	if e.Code == "" {
		e.Code = prev.Code
		prev.Code = ""
	}

	if prev.Param == e.Param {
		prev.Param = ""
	}
	// If this error has Code == "", pull up the inner one.
	if e.Param == "" {
		e.Param = prev.Param
		prev.Param = ""
	}

	return e
}

// Match compares its two error arguments. It can be used to check
// for expected errors in tests. Both arguments must have underlying
// type *Error or Match will return false. Otherwise it returns true
// iff every non-zero element of the first error is equal to the
// corresponding element of the second.
// If the Err field is a *Error, Match recurs on that field;
// otherwise it compares the strings returned by the Error methods.
// Elements that are in the second argument but not present in
// the first are ignored.
//
// For example,
//	Match(errors.E(upspin.UserName("joe@schmoe.com"), errors.Permission), err)
// tests whether err is an Error with Kind=Permission and User=joe@schmoe.com.
func Match(err1, err2 error) bool {
	e1, ok := err1.(*Error)
	if !ok {
		return false
	}
	e2, ok := err2.(*Error)
	if !ok {
		return false
	}
	if e1.User != "" && e2.User != e1.User {
		return false
	}
	if e1.Kind != Other && e2.Kind != e1.Kind {
		return false
	}
	if e1.Err != nil {
		if _, ok := e1.Err.(*Error); ok {
			return Match(e1.Err, e2.Err)
		}
		if e2.Err == nil || e2.Err.Error() != e1.Err.Error() {
			return false
		}
	}
	return true
}

// KindIs reports whether err is an *Error of the given Kind.
// If err is nil then KindIs returns false.
func KindIs(kind Kind, err error) bool {
	e, ok := err.(*Error)
	if !ok {
		return false
	}
	if e.Kind != Other {
		return e.Kind == kind
	}
	if e.Err != nil {
		return KindIs(kind, e.Err)
	}
	return false
}
<commit_msg>remove unnecessary WithStack call<commit_after>// Package errs is a modified copy of the upspin.io/errors package.
// Originally, I used quite a bit of the upspin.io/errors package,
// but have moved to only use a very small amount of it. Even still,
// I think it's appropriate to leave the license information in...
//
// Copyright 2016 The Upspin Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Package errs defines the error handling used by all Upspin software.
package errs

import (
	"fmt"
	"runtime"

	"github.com/pkg/errors"
)

// Error is the type that implements the error interface.
// It contains a number of fields, each of different type.
// An Error value may leave some values unset.
type Error struct {
	// User is the username of the user attempting the operation.
	User UserName
	// Kind is the class of error, such as permission failure,
	// or "Other" if its class is unknown or irrelevant.
	Kind Kind
	// Param represents the parameter related to the error.
	Param Parameter
	// Code is a human-readable, short representation of the error
	Code Code
	// The underlying error that triggered this one, if any.
	Err error
}

func (e *Error) isZero() bool {
	return e.User == "" && e.Kind == 0 && e.Param == "" && e.Code == "" && e.Err == nil
}

// Unwrap method allows for unwrapping errors using errors.As
func (e Error) Unwrap() error {
	return e.Err
}

func (e *Error) Error() string {
	return e.Err.Error()
}

// UserName is a string representing a user
type UserName string

// Kind defines the kind of error this is, mostly for use by systems
// such as FUSE that must act differently depending on the error.
type Kind uint8

// Parameter represents the parameter related to the error.
type Parameter string

// Code is a human-readable, short representation of the error
type Code string

// Kinds of errors.
//
// The values of the error kinds are common between both
// clients and servers. Do not reorder this list or remove
// any items since that will change their values.
// New items must be added only to the end.
const (
	Other           Kind = iota // Unclassified error. This value is not printed in the error message.
	Invalid                     // Invalid operation for this type of item.
	Permission                  // Permission denied.
	IO                          // External I/O error such as network failure.
	Exist                       // Item already exists.
	NotExist                    // Item does not exist.
	Private                     // Information withheld.
	Internal                    // Internal error or inconsistency.
	BrokenLink                  // Link target does not exist.
	Database                    // Error from database.
	Validation                  // Input validation error.
	Unanticipated               // Unanticipated error.
	InvalidRequest              // Invalid Request
	Unauthenticated             // User did not properly authenticate
	Unauthorized                // User is not authorized for the resource
)

func (k Kind) String() string {
	switch k {
	case Other:
		return "other_error"
	case Invalid:
		return "invalid_operation"
	case Permission:
		return "permission_denied"
	case IO:
		return "I/O_error"
	case Exist:
		return "item_already_exists"
	case NotExist:
		return "item_does_not_exist"
	case BrokenLink:
		return "link_target_does_not_exist"
	case Private:
		return "information_withheld"
	case Internal:
		return "internal_error"
	case Database:
		return "database_error"
	case Validation:
		return "input_validation_error"
	case Unanticipated:
		return "unanticipated_error"
	case InvalidRequest:
		return "invalid_request_error"
	case Unauthenticated:
		return "unauthenticated"
	case Unauthorized:
		return "unauthorized"
	}
	return "unknown_error_kind"
}

// E builds an error value from its arguments.
// There must be at least one argument or E panics.
// The type of each argument determines its meaning.
// If more than one argument of a given type is presented,
// only the last one is recorded.
//
// The types are:
//	UserName
//		The username of the user attempting the operation.
//	string
//		Treated as an error message and assigned to the
//		Err field after a call to errors.New.
//	errors.Kind
//		The class of error, such as permission failure.
//	error
//		The underlying error that triggered this one.
//
// If the error is printed, only those items that have been
// set to non-zero values will appear in the result.
//
// If Kind is not specified or Other, we set it to the Kind of
// the underlying error.
//
func E(args ...interface{}) error {
	type stackTracer interface {
		StackTrace() errors.StackTrace
	}

	if len(args) == 0 {
		panic("call to errors.E with no arguments")
	}
	e := &Error{}
	for _, arg := range args {
		switch arg := arg.(type) {
		case UserName:
			e.User = arg
		case string:
			e.Err = errors.New(arg)
		case Kind:
			e.Kind = arg
		case *Error:
			e.Err = arg
		case error:
			// if the error implements stackTracer, then it is
			// a pkg/errors error type and does not need to have
			// the stack added
			_, ok := arg.(stackTracer)
			if ok {
				e.Err = arg
			} else {
				e.Err = errors.WithStack(arg)
			}
		case Code:
			e.Code = arg
		case Parameter:
			e.Param = arg
		default:
			_, file, line, _ := runtime.Caller(1)
			return fmt.Errorf("errors.E: bad call from %s:%d: %v, unknown type %T, value %v in error call", file, line, args, arg, arg)
		}
	}

	prev, ok := e.Err.(*Error)
	if !ok {
		return e
	}
	// If this error has Kind unset or Other, pull up the inner one.
	if e.Kind == Other {
		e.Kind = prev.Kind
		prev.Kind = Other
	}

	if prev.Code == e.Code {
		prev.Code = ""
	}
	// If this error has Code == "", pull up the inner one.
	if e.Code == "" {
		e.Code = prev.Code
		prev.Code = ""
	}

	if prev.Param == e.Param {
		prev.Param = ""
	}
	// If this error has Code == "", pull up the inner one.
	if e.Param == "" {
		e.Param = prev.Param
		prev.Param = ""
	}

	return e
}

// Match compares its two error arguments. It can be used to check
// for expected errors in tests. Both arguments must have underlying
// type *Error or Match will return false. Otherwise it returns true
// iff every non-zero element of the first error is equal to the
// corresponding element of the second.
// If the Err field is a *Error, Match recurs on that field;
// otherwise it compares the strings returned by the Error methods.
// Elements that are in the second argument but not present in
// the first are ignored.
//
// For example,
//	Match(errors.E(upspin.UserName("joe@schmoe.com"), errors.Permission), err)
// tests whether err is an Error with Kind=Permission and User=joe@schmoe.com.
func Match(err1, err2 error) bool {
	e1, ok := err1.(*Error)
	if !ok {
		return false
	}
	e2, ok := err2.(*Error)
	if !ok {
		return false
	}
	if e1.User != "" && e2.User != e1.User {
		return false
	}
	if e1.Kind != Other && e2.Kind != e1.Kind {
		return false
	}
	if e1.Err != nil {
		if _, ok := e1.Err.(*Error); ok {
			return Match(e1.Err, e2.Err)
		}
		if e2.Err == nil || e2.Err.Error() != e1.Err.Error() {
			return false
		}
	}
	return true
}

// KindIs reports whether err is an *Error of the given Kind.
// If err is nil then KindIs returns false.
func KindIs(kind Kind, err error) bool {
	e, ok := err.(*Error)
	if !ok {
		return false
	}
	if e.Kind != Other {
		return e.Kind == kind
	}
	if e.Err != nil {
		return KindIs(kind, e.Err)
	}
	return false
}
<|endoftext|>


<commit_before>package pbf

import (
	"runtime"
	"sync"

	"github.com/omniscale/imposm3/element"
)

type Parser struct {
	pbf       *pbf
	coords    chan []element.Node
	nodes     chan []element.Node
	ways      chan []element.Way
	relations chan []element.Relation
	nParser   int
	wg        sync.WaitGroup
	waySync   *barrier
	relSync   *barrier
}

func NewParser(
	filename string,
) (*Parser, error) {
	pbf, err := open(filename)
	if err != nil {
		return nil, err
	}
	return &Parser{
		pbf:     pbf,
		nParser: runtime.NumCPU(),
		wg:      sync.WaitGroup{},
	}, nil
}

func (p *Parser) Header() Header {
	return *p.pbf.header
}

func (p *Parser) Parse(
	coords chan []element.Node,
	nodes chan []element.Node,
	ways chan []element.Way,
	relations chan []element.Relation,
) {
	p.coords = coords
	p.nodes = nodes
	p.ways = ways
	p.relations = relations
	blocks := p.pbf.BlockPositions()
	for i := 0; i < p.nParser; i++ {
		p.wg.Add(1)
		go func() {
			for block := range blocks {
				p.parseBlock(block)
			}
			if p.waySync != nil {
				p.waySync.doneWait()
			}
			if p.relSync != nil {
				p.relSync.doneWait()
			}
			p.wg.Done()
		}()
	}
	p.wg.Wait()
}

// RegisterFirstWayCallback registers a callback that gets called when the
// the first way is parsed. The callback should block until it is
// safe to send ways to the way channel.
// This only works when the PBF file is ordered by type (nodes before ways before relations).
func (p *Parser) RegisterFirstWayCallback(cb func()) {
	p.waySync = newBarrier(cb)
	p.waySync.add(p.nParser)
}

// RegisterFirstRelationCallback registers a callback that gets called when the
// the first relation is parsed. The callback should block until it is
// safe to send relations to the relation channel.
// This only works when the PBF file is ordered by type (nodes before ways before relations).
func (p *Parser) RegisterFirstRelationCallback(cb func()) {
	p.relSync = newBarrier(cb)
	p.relSync.add(p.nParser)
}

func (p *Parser) parseBlock(pos block) {
	block := readPrimitiveBlock(pos)
	stringtable := newStringTable(block.GetStringtable())

	for _, group := range block.Primitivegroup {
		if p.coords != nil || p.nodes != nil {
			dense := group.GetDense()
			if dense != nil {
				parsedCoords, parsedNodes := readDenseNodes(dense, block, stringtable)
				if len(parsedCoords) > 0 && p.coords != nil {
					p.coords <- parsedCoords
				}
				if len(parsedNodes) > 0 && p.nodes != nil {
					p.nodes <- parsedNodes
				}
			}
			if len(group.Nodes) > 0 {
				parsedCoords, parsedNodes := readNodes(group.Nodes, block, stringtable)
				if len(parsedCoords) > 0 && p.coords != nil {
					p.coords <- parsedCoords
				}
				if len(parsedNodes) > 0 && p.nodes != nil {
					p.nodes <- parsedNodes
				}
			}
		}
		if len(group.Ways) > 0 && p.ways != nil {
			parsedWays := readWays(group.Ways, block, stringtable)
			if len(parsedWays) > 0 {
				if p.waySync != nil {
					p.waySync.doneWait()
				}
				p.ways <- parsedWays
			}
		}
		if len(group.Relations) > 0 && p.relations != nil {
			parsedRelations := readRelations(group.Relations, block, stringtable)
			if len(parsedRelations) > 0 {
				if p.waySync != nil {
					p.waySync.doneWait()
				}
				if p.relSync != nil {
					p.relSync.doneWait()
				}
				p.relations <- parsedRelations
			}
		}
	}
}

// barrier is a struct to synchronize multiple goroutines.
// Works similar to a WaitGroup. Except:
// Calls callback function once all goroutines called doneWait().
// doneWait() blocks until the callback returns. doneWait() does not
// block after all goroutines were blocked once.
type barrier struct {
	synced     bool
	wg         sync.WaitGroup
	once       sync.Once
	callbackWg sync.WaitGroup
	callback   func()
}

func newBarrier(callback func()) *barrier {
	s := &barrier{callback: callback}
	s.callbackWg.Add(1)
	return s
}

func (s *barrier) add(delta int) {
	s.wg.Add(delta)
}

func (s *barrier) doneWait() {
	if s.synced {
		return
	}
	s.wg.Done()
	s.wg.Wait()
	s.once.Do(s.call)
	s.callbackWg.Wait()
}

func (s *barrier) call() {
	s.callback()
	s.synced = true
	s.callbackWg.Done()
}
<commit_msg>fix data race in barrier for first way|relation callbacks<commit_after>package pbf

import (
	"runtime"
	"sync"
	"sync/atomic"

	"github.com/omniscale/imposm3/element"
)

type Parser struct {
	pbf       *pbf
	coords    chan []element.Node
	nodes     chan []element.Node
	ways      chan []element.Way
	relations chan []element.Relation
	nParser   int
	wg        sync.WaitGroup
	waySync   *barrier
	relSync   *barrier
}

func NewParser(
	filename string,
) (*Parser, error) {
	pbf, err := open(filename)
	if err != nil {
		return nil, err
	}
	return &Parser{
		pbf:     pbf,
		nParser: runtime.NumCPU(),
		wg:      sync.WaitGroup{},
	}, nil
}

func (p *Parser) Header() Header {
	return *p.pbf.header
}

func (p *Parser) Parse(
	coords chan []element.Node,
	nodes chan []element.Node,
	ways chan []element.Way,
	relations chan []element.Relation,
) {
	p.coords = coords
	p.nodes = nodes
	p.ways = ways
	p.relations = relations
	blocks := p.pbf.BlockPositions()
	for i := 0; i < p.nParser; i++ {
		p.wg.Add(1)
		go func() {
			for block := range blocks {
				p.parseBlock(block)
			}
			if p.waySync != nil {
				p.waySync.doneWait()
			}
			if p.relSync != nil {
				p.relSync.doneWait()
			}
			p.wg.Done()
		}()
	}
	p.wg.Wait()
}

// RegisterFirstWayCallback registers a callback that gets called when the
// the first way is parsed. The callback should block until it is
// safe to send ways to the way channel.
// This only works when the PBF file is ordered by type (nodes before ways before relations).
func (p *Parser) RegisterFirstWayCallback(cb func()) {
	p.waySync = newBarrier(cb)
	p.waySync.add(p.nParser)
}

// RegisterFirstRelationCallback registers a callback that gets called when the
// the first relation is parsed. The callback should block until it is
// safe to send relations to the relation channel.
// This only works when the PBF file is ordered by type (nodes before ways before relations).
func (p *Parser) RegisterFirstRelationCallback(cb func()) {
	p.relSync = newBarrier(cb)
	p.relSync.add(p.nParser)
}

func (p *Parser) parseBlock(pos block) {
	block := readPrimitiveBlock(pos)
	stringtable := newStringTable(block.GetStringtable())

	for _, group := range block.Primitivegroup {
		if p.coords != nil || p.nodes != nil {
			dense := group.GetDense()
			if dense != nil {
				parsedCoords, parsedNodes := readDenseNodes(dense, block, stringtable)
				if len(parsedCoords) > 0 && p.coords != nil {
					p.coords <- parsedCoords
				}
				if len(parsedNodes) > 0 && p.nodes != nil {
					p.nodes <- parsedNodes
				}
			}
			if len(group.Nodes) > 0 {
				parsedCoords, parsedNodes := readNodes(group.Nodes, block, stringtable)
				if len(parsedCoords) > 0 && p.coords != nil {
					p.coords <- parsedCoords
				}
				if len(parsedNodes) > 0 && p.nodes != nil {
					p.nodes <- parsedNodes
				}
			}
		}
		if len(group.Ways) > 0 && p.ways != nil {
			parsedWays := readWays(group.Ways, block, stringtable)
			if len(parsedWays) > 0 {
				if p.waySync != nil {
					p.waySync.doneWait()
				}
				p.ways <- parsedWays
			}
		}
		if len(group.Relations) > 0 && p.relations != nil {
			parsedRelations := readRelations(group.Relations, block, stringtable)
			if len(parsedRelations) > 0 {
				if p.waySync != nil {
					p.waySync.doneWait()
				}
				if p.relSync != nil {
					p.relSync.doneWait()
				}
				p.relations <- parsedRelations
			}
		}
	}
}

// barrier is a struct to synchronize multiple goroutines.
// Works similar to a WaitGroup. Except:
// Calls callback function once all goroutines called doneWait().
// doneWait() blocks until the callback returns. doneWait() does not
// block after all goroutines were blocked once.
type barrier struct {
	synced     int32
	wg         sync.WaitGroup
	once       sync.Once
	callbackWg sync.WaitGroup
	callback   func()
}

func newBarrier(callback func()) *barrier {
	s := &barrier{callback: callback}
	s.callbackWg.Add(1)
	return s
}

func (s *barrier) add(delta int) {
	s.wg.Add(delta)
}

func (s *barrier) doneWait() {
	if atomic.LoadInt32(&s.synced) == 1 {
		return
	}
	s.wg.Done()
	s.wg.Wait()
	s.once.Do(s.call)
	s.callbackWg.Wait()
}

func (s *barrier) call() {
	s.callback()
	atomic.StoreInt32(&s.synced, 1)
	s.callbackWg.Done()
}
<|endoftext|>


<commit_before>package server

import (
	"bufio"
	"bytes"
	"errors"
	"net"
	"strconv"
	"strings"

	"github.com/absolute8511/redcon"
)

var (
	ErrUnknownCommand         = errors.New("unknown command")
	ErrWrongNumberOfArguments = errors.New("wrong number of arguments")
	ErrDisabled               = errors.New("disabled")
)

func GetIPv4ForInterfaceName(ifname string) string {
	interfaces, _ := net.Interfaces()
	for _, inter := range interfaces {
		//log.Printf("found interface: %s\n", inter.Name)
		if inter.Name == ifname {
			if addrs, err := inter.Addrs(); err == nil {
				for _, addr := range addrs {
					switch ip := addr.(type) {
					case *net.IPNet:
						if ip.IP.DefaultMask() != nil {
							return ip.IP.String()
						}
					}
				}
			}
		}
	}
	return ""
}

// pipelineCommand creates a single command from a pipeline.
// should handle some pipeline command which across multi partitions
// since plget response is a bit complicated (order require), we do not handle pipeline for get
func pipelineCommand(conn redcon.Conn, cmd redcon.Command) (int, redcon.Command, error) {
	if conn == nil {
		return 0, cmd, nil
	}
	pcmds := conn.PeekPipeline()
	if len(pcmds) == 0 {
		return 0, cmd, nil
	}
	args := make([][]byte, 0, 64)
	switch qcmdlower(cmd.Args[0]) {
	default:
		return 0, cmd, nil
	case "plget", "plset":
		return 0, redcon.Command{}, ErrUnknownCommand
	case "set":
		if len(cmd.Args) != 3 {
			return 0, cmd, nil
		}
		// convert to a PLSET command which is similar to an MSET
		for _, pcmd := range pcmds {
			if qcmdlower(pcmd.Args[0]) != "set" || len(pcmd.Args) != 3 {
				return 0, cmd, nil
			}
		}
		args = append(args, []byte("plset"))
		for _, pcmd := range append([]redcon.Command{cmd}, pcmds...) {
			args = append(args, pcmd.Args[1], pcmd.Args[2])
		}
	}

	// remove the peeked items off the pipeline
	conn.ReadPipeline()

	ncmd := buildCommand(args)
	return len(pcmds) + 1, ncmd, nil
}
func buildCommand(args [][]byte) redcon.Command {
	// build a pipeline command
	buf := make([]byte, 0, 128)
	buf = append(buf, '*')
	buf = append(buf, strconv.FormatInt(int64(len(args)), 10)...)
	buf = append(buf, '\r', '\n')

	poss := make([]int, 0, len(args)*2)
	for _, arg := range args {
		buf = append(buf, '$')
		buf = append(buf, strconv.FormatInt(int64(len(arg)), 10)...)
		buf = append(buf, '\r', '\n')
		poss = append(poss, len(buf), len(buf)+len(arg))
		buf = append(buf, arg...)
		buf = append(buf, '\r', '\n')
	}

	// reformat a new command
	var ncmd redcon.Command
	ncmd.Raw = buf
	ncmd.Args = make([][]byte, len(poss)/2)
	for i, j := 0, 0; i < len(poss); i, j = i+2, j+1 {
		ncmd.Args[j] = ncmd.Raw[poss[i]:poss[i+1]]
	}
	return ncmd
}

func parseCommand(raw []byte) (redcon.Command, error) {
	var cmd redcon.Command
	cmd.Raw = raw
	pos := 0
	rd := bufio.NewReader(bytes.NewBuffer(raw))
	c, err := rd.ReadByte()
	if err != nil {
		return cmd, err
	}
	pos++
	if c != '*' {
		return cmd, errors.New("invalid command")
	}
	line, err := rd.ReadString('\n')
	if err != nil {
		return cmd, err
	}
	pos += len(line)
	if len(line) < 2 || line[len(line)-2] != '\r' {
		return cmd, errors.New("invalid command")
	}
	n, err := strconv.ParseUint(line[:len(line)-2], 10, 64)
	if err != nil {
		return cmd, err
	}
	if n == 0 {
		return cmd, errors.New("invalid command")
	}
	for i := uint64(0); i < n; i++ {
		c, err := rd.ReadByte()
		if err != nil {
			return cmd, err
		}
		pos++
		if c != '$' {
			return cmd, errors.New("invalid command")
		}
		line, err := rd.ReadString('\n')
		if err != nil {
			return cmd, err
		}
		pos += len(line)
		if len(line) < 2 || line[len(line)-2] != '\r' {
			return cmd, errors.New("invalid command")
		}
		n, err := strconv.ParseUint(line[:len(line)-2], 10, 64)
		if err != nil {
			return cmd, err
		}
		if _, err := rd.Discard(int(n) + 2); err != nil {
			return cmd, err
		}
		s := pos
		pos += int(n) + 2
		if raw[pos-2] != '\r' || raw[pos-1] != '\n' {
			return cmd, errors.New("invalid command")
		}
		cmd.Args = append(cmd.Args, raw[s:pos-2])
	}
	return cmd, nil
}

// qcmdlower for common optimized command lowercase conversions.
func qcmdlower(n []byte) string {
	switch len(n) {
	case 3:
		if (n[0] == 's' || n[0] == 'S') &&
			(n[1] == 'e' || n[1] == 'E') &&
			(n[2] == 't' || n[2] == 'T') {
			return "set"
		}
		if (n[0] == 'g' || n[0] == 'G') &&
			(n[1] == 'e' || n[1] == 'E') &&
			(n[2] == 't' || n[2] == 'T') {
			return "get"
		}
	case 4:
		if (n[0] == 'm' || n[0] == 'M') &&
			(n[1] == 's' || n[1] == 'S') &&
			(n[2] == 'e' || n[2] == 'E') &&
			(n[3] == 't' || n[3] == 'T') {
			return "mset"
		}
		if (n[0] == 'm' || n[0] == 'M') &&
			(n[1] == 'g' || n[1] == 'G') &&
			(n[2] == 'e' || n[2] == 'E') &&
			(n[3] == 't' || n[3] == 'T') {
			return "mget"
		}
		if (n[0] == 'e' || n[0] == 'E') &&
			(n[1] == 'v' || n[1] == 'V') &&
			(n[2] == 'a' || n[2] == 'A') &&
			(n[3] == 'l' || n[3] == 'L') {
			return "eval"
		}
	case 5:
		if (n[0] == 'p' || n[0] == 'P') &&
			(n[1] == 'l' || n[1] == 'L') &&
			(n[2] == 's' || n[2] == 'S') &&
			(n[3] == 'e' || n[3] == 'E') &&
			(n[4] == 't' || n[4] == 'T') {
			return "plset"
		}
		if (n[0] == 'p' || n[0] == 'P') &&
			(n[1] == 'l' || n[1] == 'L') &&
			(n[2] == 'g' || n[2] == 'G') &&
			(n[3] == 'e' || n[3] == 'E') &&
			(n[4] == 't' || n[4] == 'T') {
			return "plget"
		}
	case 6:
		if (n[0] == 'e' || n[0] == 'E') &&
			(n[1] == 'v' || n[1] == 'V') &&
			(n[2] == 'a' || n[2] == 'A') &&
			(n[3] == 'l' || n[3] == 'L') &&
			(n[4] == 'r' || n[4] == 'R') &&
			(n[5] == 'o' || n[5] == 'O') {
			return "evalro"
		}
	}
	return strings.ToLower(string(n))
}
<commit_msg>lite refactor<commit_after>package server

import (
	"bufio"
	"bytes"
	"errors"
	"net"
	"strconv"
	"strings"

	"github.com/absolute8511/redcon"
)

var (
	ErrUnknownCommand         = errors.New("unknown command")
	ErrWrongNumberOfArguments = errors.New("wrong number of arguments")
	ErrDisabled               = errors.New("disabled")
)

func GetIPv4ForInterfaceName(ifname string) string {
	inter, err := net.InterfaceByName(ifname)
	if err != nil {
		return ""
	}

	addrs, err := inter.Addrs()
	if err != nil {
		return ""
	}

	for _, addr := range addrs {
		if ip, ok := addr.(*net.IPNet); ok {
			if ip.IP.DefaultMask() != nil {
				return ip.IP.String()
			}
		}
	}

	return ""
}

// pipelineCommand creates a single command from a pipeline.
// should handle some pipeline command which across multi partitions
// since plget response is a bit complicated (order require), we do not handle pipeline for get
func pipelineCommand(conn redcon.Conn, cmd redcon.Command) (int, redcon.Command, error) {
	if conn == nil {
		return 0, cmd, nil
	}
	pcmds := conn.PeekPipeline()
	if len(pcmds) == 0 {
		return 0, cmd, nil
	}
	args := make([][]byte, 0, 64)
	switch qcmdlower(cmd.Args[0]) {
	default:
		return 0, cmd, nil
	case "plget", "plset":
		return 0, redcon.Command{}, ErrUnknownCommand
	case "set":
		if len(cmd.Args) != 3 {
			return 0, cmd, nil
		}
		// convert to a PLSET command which is similar to an MSET
		for _, pcmd := range pcmds {
			if qcmdlower(pcmd.Args[0]) != "set" || len(pcmd.Args) != 3 {
				return 0, cmd, nil
			}
		}
		args = append(args, []byte("plset"))
		for _, pcmd := range append([]redcon.Command{cmd}, pcmds...) {
			args = append(args, pcmd.Args[1], pcmd.Args[2])
		}
	}

	// remove the peeked items off the pipeline
	conn.ReadPipeline()

	ncmd := buildCommand(args)
	return len(pcmds) + 1, ncmd, nil
}
func buildCommand(args [][]byte) redcon.Command {
	// build a pipeline command
	buf := make([]byte, 0, 128)
	buf = append(buf, '*')
	buf = append(buf, strconv.FormatInt(int64(len(args)), 10)...)
	buf = append(buf, '\r', '\n')

	poss := make([]int, 0, len(args)*2)
	for _, arg := range args {
		buf = append(buf, '$')
		buf = append(buf, strconv.FormatInt(int64(len(arg)), 10)...)
		buf = append(buf, '\r', '\n')
		poss = append(poss, len(buf), len(buf)+len(arg))
		buf = append(buf, arg...)
		buf = append(buf, '\r', '\n')
	}

	// reformat a new command
	var ncmd redcon.Command
	ncmd.Raw = buf
	ncmd.Args = make([][]byte, len(poss)/2)
	for i, j := 0, 0; i < len(poss); i, j = i+2, j+1 {
		ncmd.Args[j] = ncmd.Raw[poss[i]:poss[i+1]]
	}
	return ncmd
}

func parseCommand(raw []byte) (redcon.Command, error) {
	var cmd redcon.Command
	cmd.Raw = raw
	pos := 0
	rd := bufio.NewReader(bytes.NewBuffer(raw))
	c, err := rd.ReadByte()
	if err != nil {
		return cmd, err
	}
	pos++
	if c != '*' {
		return cmd, errors.New("invalid command")
	}
	line, err := rd.ReadString('\n')
	if err != nil {
		return cmd, err
	}
	pos += len(line)
	if len(line) < 2 || line[len(line)-2] != '\r' {
		return cmd, errors.New("invalid command")
	}
	n, err := strconv.ParseUint(line[:len(line)-2], 10, 64)
	if err != nil {
		return cmd, err
	}
	if n == 0 {
		return cmd, errors.New("invalid command")
	}
	for i := uint64(0); i < n; i++ {
		c, err := rd.ReadByte()
		if err != nil {
			return cmd, err
		}
		pos++
		if c != '$' {
			return cmd, errors.New("invalid command")
		}
		line, err := rd.ReadString('\n')
		if err != nil {
			return cmd, err
		}
		pos += len(line)
		if len(line) < 2 || line[len(line)-2] != '\r' {
			return cmd, errors.New("invalid command")
		}
		n, err := strconv.ParseUint(line[:len(line)-2], 10, 64)
		if err != nil {
			return cmd, err
		}
		if _, err := rd.Discard(int(n) + 2); err != nil {
			return cmd, err
		}
		s := pos
		pos += int(n) + 2
		if raw[pos-2] != '\r' || raw[pos-1] != '\n' {
			return cmd, errors.New("invalid command")
		}
		cmd.Args = append(cmd.Args, raw[s:pos-2])
	}
	return cmd, nil
}

// qcmdlower for common optimized command lowercase conversions.
func qcmdlower(n []byte) string {
	switch len(n) {
	case 3:
		if (n[0] == 's' || n[0] == 'S') &&
			(n[1] == 'e' || n[1] == 'E') &&
			(n[2] == 't' || n[2] == 'T') {
			return "set"
		}
		if (n[0] == 'g' || n[0] == 'G') &&
			(n[1] == 'e' || n[1] == 'E') &&
			(n[2] == 't' || n[2] == 'T') {
			return "get"
		}
	case 4:
		if (n[0] == 'm' || n[0] == 'M') &&
			(n[1] == 's' || n[1] == 'S') &&
			(n[2] == 'e' || n[2] == 'E') &&
			(n[3] == 't' || n[3] == 'T') {
			return "mset"
		}
		if (n[0] == 'm' || n[0] == 'M') &&
			(n[1] == 'g' || n[1] == 'G') &&
			(n[2] == 'e' || n[2] == 'E') &&
			(n[3] == 't' || n[3] == 'T') {
			return "mget"
		}
		if (n[0] == 'e' || n[0] == 'E') &&
			(n[1] == 'v' || n[1] == 'V') &&
			(n[2] == 'a' || n[2] == 'A') &&
			(n[3] == 'l' || n[3] == 'L') {
			return "eval"
		}
	case 5:
		if (n[0] == 'p' || n[0] == 'P') &&
			(n[1] == 'l' || n[1] == 'L') &&
			(n[2] == 's' || n[2] == 'S') &&
			(n[3] == 'e' || n[3] == 'E') &&
			(n[4] == 't' || n[4] == 'T') {
			return "plset"
		}
		if (n[0] == 'p' || n[0] == 'P') &&
			(n[1] == 'l' || n[1] == 'L') &&
			(n[2] == 'g' || n[2] == 'G') &&
			(n[3] == 'e' || n[3] == 'E') &&
			(n[4] == 't' || n[4] == 'T') {
			return "plget"
		}
	case 6:
		if (n[0] == 'e' || n[0] == 'E') &&
			(n[1] == 'v' || n[1] == 'V') &&
			(n[2] == 'a' || n[2] == 'A') &&
			(n[3] == 'l' || n[3] == 'L') &&
			(n[4] == 'r' || n[4] == 'R') &&
			(n[5] == 'o' || n[5] == 'O') {
			return "evalro"
		}
	}
	return strings.ToLower(string(n))
}
<|endoftext|>


<commit_before>package store_test

import (
	"encoding/json"
	"io/ioutil"
	. "launchpad.net/gocheck"
	"launchpad.net/juju/go/charm"
	"launchpad.net/juju/go/store"
	"net/http"
	"net/http/httptest"
	"net/url"
	"strconv"
	"time"
)

func (s *StoreSuite) prepareServer(c *C) (*store.Server, *charm.URL) {
	curl := charm.MustParseURL("cs:oneiric/wordpress")
	pub, err := s.store.CharmPublisher([]*charm.URL{curl}, "some-digest")
	c.Assert(err, IsNil)
	err = pub.Publish(&FakeCharmDir{})
	c.Assert(err, IsNil)

	server, err := store.NewServer(s.store)
	c.Assert(err, IsNil)
	return server, curl
}

func (s *StoreSuite) TestServerCharmInfo(c *C) {
	server, curl := s.prepareServer(c)
	req, err := http.NewRequest("GET", "/charm-info", nil)
	c.Assert(err, IsNil)

	var tests = []struct{ url, sha, err string }{
		{curl.String(), fakeRevZeroSha, ""},
		{"cs:oneiric/non-existent", "", "entry not found"},
		{"cs:bad", "", `charm URL without series: "cs:bad"`},
	}

	for _, t := range tests {
		req.Form = url.Values{"charms": []string{t.url}}
		rec := httptest.NewRecorder()
		server.ServeHTTP(rec, req)

		expected := make(map[string]interface{})
		if t.sha != "" {
			expected[t.url] = map[string]interface{}{
				"revision": float64(0),
				"sha256": t.sha,
			}
		} else {
			expected[t.url] = map[string]interface{}{
				"revision": float64(0),
				"errors": []interface{}{t.err},
			}
		}
		obtained := map[string]interface{}{}
		err = json.NewDecoder(rec.Body).Decode(&obtained)
		c.Assert(err, IsNil)
		c.Assert(obtained, DeepEquals, expected)
		c.Assert(rec.Header().Get("Content-Type"), Equals, "application/json")
	}

	s.checkCounterSum(c, []string{"charm-info", curl.Series, curl.Name}, false, 1)
	s.checkCounterSum(c, []string{"charm-missing", "oneiric", "non-existent"}, false, 1)
}

// checkCounterSum checks that statistics are properly collected.
// It retries a few times as they are generally collected in background.
func (s *StoreSuite) checkCounterSum(c *C, key []string, prefix bool, expected int64) {
	var sum int64
	var err error
	for retry := 0; retry < 10; retry++ {
		time.Sleep(1e8)
		sum, err = s.store.SumCounter(key, prefix)
		c.Assert(err, IsNil)
		if sum == expected {
			if expected == 0 && retry < 2 {
				continue // Wait a bit to make sure.
			}
			return
		}
	}
	c.Errorf("counter sum for %#v is %d, want %d", key, sum, expected)
}

func (s *StoreSuite) TestCharmStreaming(c *C) {
	server, curl := s.prepareServer(c)

	req, err := http.NewRequest("GET", "/charm/"+curl.String()[3:], nil)
	c.Assert(err, IsNil)
	rec := httptest.NewRecorder()
	server.ServeHTTP(rec, req)

	data, err := ioutil.ReadAll(rec.Body)
	c.Assert(string(data), Equals, "charm-revision-0")

	c.Assert(rec.Header().Get("Connection"), Equals, "close")
	c.Assert(rec.Header().Get("Content-Type"), Equals, "application/octet-stream")
	c.Assert(rec.Header().Get("Content-Length"), Equals, "16")

	// Check that it was accounted for in statistics.
	s.checkCounterSum(c, []string{"charm-bundle", curl.Series, curl.Name}, false, 1)
}

func (s *StoreSuite) TestDisableStats(c *C) {
	server, curl := s.prepareServer(c)

	req, err := http.NewRequest("GET", "/charm-info", nil)
	c.Assert(err, IsNil)
	req.Form = url.Values{"charms": []string{curl.String()}, "stats": []string{"0"}}
	server.ServeHTTP(httptest.NewRecorder(), req)

	req, err = http.NewRequest("GET", "/charm/"+curl.String()[3:], nil)
	c.Assert(err, IsNil)
	req.Form = url.Values{"stats": []string{"0"}}
	server.ServeHTTP(httptest.NewRecorder(), req)

	// No statistics should have been collected given the use of stats=0.
	for _, prefix := range []string{"charm-info", "charm-bundle", "charm-missing"} {
		s.checkCounterSum(c, []string{prefix}, true, 0)
	}
}

func (s *StoreSuite) TestServerStatus(c *C) {
	server, err := store.NewServer(s.store)
	c.Assert(err, IsNil)
	tests := []struct {
		path string
		code int
	}{
		{"/charm-info/any", 404},
		{"/charm/bad-url", 404},
		{"/charm/bad-series/wordpress", 404},
		{"/stats/counter/", 403},
		{"/stats/counter/*", 403},
		{"/stats/counter/any/", 404},
		{"/stats/", 404},
		{"/stats/any", 404},
	}
	for _, test := range tests {
		req, err := http.NewRequest("GET", test.path, nil)
		c.Assert(err, IsNil)
		rec := httptest.NewRecorder()
		server.ServeHTTP(rec, req)
		c.Assert(rec.Code, Equals, test.code, Commentf("Path: %s", test.path))
	}
}

func (s *StoreSuite) TestRootRedirect(c *C) {
	server, err := store.NewServer(s.store)
	c.Assert(err, IsNil)
	req, err := http.NewRequest("GET", "/", nil)
	c.Assert(err, IsNil)
	rec := httptest.NewRecorder()
	server.ServeHTTP(rec, req)
	c.Assert(rec.Code, Equals, 303)
	c.Assert(rec.Header().Get("Location"), Equals, "https://juju.ubuntu.com")
}

func (s *StoreSuite) TestStatsCounter(c *C) {
	for _, key := range [][]string{{"a", "b"}, {"a", "b"}, {"a"}} {
		err := s.store.IncCounter(key)
		c.Assert(err, IsNil)
	}

	server, _ := s.prepareServer(c)

	expected := map[string]string{
		"a:b": "2",
		"a:*": "3",
		"a":   "1",
	}

	for counter, n := range expected {
		req, err := http.NewRequest("GET", "/stats/counter/" + counter, nil)
		c.Assert(err, IsNil)
		rec := httptest.NewRecorder()
		server.ServeHTTP(rec, req)

		data, err := ioutil.ReadAll(rec.Body)
		c.Assert(string(data), Equals, n)

		c.Assert(rec.Header().Get("Content-Type"), Equals, "text/plain")
		c.Assert(rec.Header().Get("Content-Length"), Equals, strconv.Itoa(len(n)))
	}
}

func (s *StoreSuite) TestBlitzKey(c *C) {
	server, _ := s.prepareServer(c)

	// This is just a validation key to allow blitz.io to run
	// performance tests against the site.
	req, err := http.NewRequest("GET", "/mu-35700a31-6bf320ca-a800b670-05f845ee", nil)
	c.Assert(err, IsNil)
	rec := httptest.NewRecorder()
	server.ServeHTTP(rec, req)

	data, err := ioutil.ReadAll(rec.Body)
	c.Assert(string(data), Equals, "42")

	c.Assert(rec.Header().Get("Connection"), Equals, "close")
	c.Assert(rec.Header().Get("Content-Type"), Equals, "text/plain")
	c.Assert(rec.Header().Get("Content-Length"), Equals, "2")
}
<commit_msg>Check that requests succeed in the test to avoid further problems, as recommended by Roger.<commit_after>package store_test

import (
	"encoding/json"
	"io/ioutil"
	. "launchpad.net/gocheck"
	"launchpad.net/juju/go/charm"
	"launchpad.net/juju/go/store"
	"net/http"
	"net/http/httptest"
	"net/url"
	"strconv"
	"time"
)

func (s *StoreSuite) prepareServer(c *C) (*store.Server, *charm.URL) {
	curl := charm.MustParseURL("cs:oneiric/wordpress")
	pub, err := s.store.CharmPublisher([]*charm.URL{curl}, "some-digest")
	c.Assert(err, IsNil)
	err = pub.Publish(&FakeCharmDir{})
	c.Assert(err, IsNil)

	server, err := store.NewServer(s.store)
	c.Assert(err, IsNil)
	return server, curl
}

func (s *StoreSuite) TestServerCharmInfo(c *C) {
	server, curl := s.prepareServer(c)
	req, err := http.NewRequest("GET", "/charm-info", nil)
	c.Assert(err, IsNil)

	var tests = []struct{ url, sha, err string }{
		{curl.String(), fakeRevZeroSha, ""},
		{"cs:oneiric/non-existent", "", "entry not found"},
		{"cs:bad", "", `charm URL without series: "cs:bad"`},
	}

	for _, t := range tests {
		req.Form = url.Values{"charms": []string{t.url}}
		rec := httptest.NewRecorder()
		server.ServeHTTP(rec, req)

		expected := make(map[string]interface{})
		if t.sha != "" {
			expected[t.url] = map[string]interface{}{
				"revision": float64(0),
				"sha256": t.sha,
			}
		} else {
			expected[t.url] = map[string]interface{}{
				"revision": float64(0),
				"errors": []interface{}{t.err},
			}
		}
		obtained := map[string]interface{}{}
		err = json.NewDecoder(rec.Body).Decode(&obtained)
		c.Assert(err, IsNil)
		c.Assert(obtained, DeepEquals, expected)
		c.Assert(rec.Header().Get("Content-Type"), Equals, "application/json")
	}

	s.checkCounterSum(c, []string{"charm-info", curl.Series, curl.Name}, false, 1)
	s.checkCounterSum(c, []string{"charm-missing", "oneiric", "non-existent"}, false, 1)
}

// checkCounterSum checks that statistics are properly collected.
// It retries a few times as they are generally collected in background.
func (s *StoreSuite) checkCounterSum(c *C, key []string, prefix bool, expected int64) {
	var sum int64
	var err error
	for retry := 0; retry < 10; retry++ {
		time.Sleep(1e8)
		sum, err = s.store.SumCounter(key, prefix)
		c.Assert(err, IsNil)
		if sum == expected {
			if expected == 0 && retry < 2 {
				continue // Wait a bit to make sure.
			}
			return
		}
	}
	c.Errorf("counter sum for %#v is %d, want %d", key, sum, expected)
}

func (s *StoreSuite) TestCharmStreaming(c *C) {
	server, curl := s.prepareServer(c)

	req, err := http.NewRequest("GET", "/charm/"+curl.String()[3:], nil)
	c.Assert(err, IsNil)
	rec := httptest.NewRecorder()
	server.ServeHTTP(rec, req)

	data, err := ioutil.ReadAll(rec.Body)
	c.Assert(string(data), Equals, "charm-revision-0")

	c.Assert(rec.Header().Get("Connection"), Equals, "close")
	c.Assert(rec.Header().Get("Content-Type"), Equals, "application/octet-stream")
	c.Assert(rec.Header().Get("Content-Length"), Equals, "16")

	// Check that it was accounted for in statistics.
	s.checkCounterSum(c, []string{"charm-bundle", curl.Series, curl.Name}, false, 1)
}

func (s *StoreSuite) TestDisableStats(c *C) {
	server, curl := s.prepareServer(c)

	req, err := http.NewRequest("GET", "/charm-info", nil)
	c.Assert(err, IsNil)
	req.Form = url.Values{"charms": []string{curl.String()}, "stats": []string{"0"}}
	rec := httptest.NewRecorder()
	server.ServeHTTP(rec, req)
	c.Assert(rec.Code, Equals, 200)

	req, err = http.NewRequest("GET", "/charm/"+curl.String()[3:], nil)
	c.Assert(err, IsNil)
	req.Form = url.Values{"stats": []string{"0"}}
	rec = httptest.NewRecorder()
	server.ServeHTTP(rec, req)
	c.Assert(rec.Code, Equals, 200)

	// No statistics should have been collected given the use of stats=0.
	for _, prefix := range []string{"charm-info", "charm-bundle", "charm-missing"} {
		s.checkCounterSum(c, []string{prefix}, true, 0)
	}
}

func (s *StoreSuite) TestServerStatus(c *C) {
	server, err := store.NewServer(s.store)
	c.Assert(err, IsNil)
	tests := []struct {
		path string
		code int
	}{
		{"/charm-info/any", 404},
		{"/charm/bad-url", 404},
		{"/charm/bad-series/wordpress", 404},
		{"/stats/counter/", 403},
		{"/stats/counter/*", 403},
		{"/stats/counter/any/", 404},
		{"/stats/", 404},
		{"/stats/any", 404},
	}
	for _, test := range tests {
		req, err := http.NewRequest("GET", test.path, nil)
		c.Assert(err, IsNil)
		rec := httptest.NewRecorder()
		server.ServeHTTP(rec, req)
		c.Assert(rec.Code, Equals, test.code, Commentf("Path: %s", test.path))
	}
}

func (s *StoreSuite) TestRootRedirect(c *C) {
	server, err := store.NewServer(s.store)
	c.Assert(err, IsNil)
	req, err := http.NewRequest("GET", "/", nil)
	c.Assert(err, IsNil)
	rec := httptest.NewRecorder()
	server.ServeHTTP(rec, req)
	c.Assert(rec.Code, Equals, 303)
	c.Assert(rec.Header().Get("Location"), Equals, "https://juju.ubuntu.com")
}

func (s *StoreSuite) TestStatsCounter(c *C) {
	for _, key := range [][]string{{"a", "b"}, {"a", "b"}, {"a"}} {
		err := s.store.IncCounter(key)
		c.Assert(err, IsNil)
	}

	server, _ := s.prepareServer(c)

	expected := map[string]string{
		"a:b": "2",
		"a:*": "3",
		"a":   "1",
	}

	for counter, n := range expected {
		req, err := http.NewRequest("GET", "/stats/counter/" + counter, nil)
		c.Assert(err, IsNil)
		rec := httptest.NewRecorder()
		server.ServeHTTP(rec, req)

		data, err := ioutil.ReadAll(rec.Body)
		c.Assert(string(data), Equals, n)

		c.Assert(rec.Header().Get("Content-Type"), Equals, "text/plain")
		c.Assert(rec.Header().Get("Content-Length"), Equals, strconv.Itoa(len(n)))
	}
}

func (s *StoreSuite) TestBlitzKey(c *C) {
	server, _ := s.prepareServer(c)

	// This is just a validation key to allow blitz.io to run
	// performance tests against the site.
	req, err := http.NewRequest("GET", "/mu-35700a31-6bf320ca-a800b670-05f845ee", nil)
	c.Assert(err, IsNil)
	rec := httptest.NewRecorder()
	server.ServeHTTP(rec, req)

	data, err := ioutil.ReadAll(rec.Body)
	c.Assert(string(data), Equals, "42")

	c.Assert(rec.Header().Get("Connection"), Equals, "close")
	c.Assert(rec.Header().Get("Content-Type"), Equals, "text/plain")
	c.Assert(rec.Header().Get("Content-Length"), Equals, "2")
}
<|endoftext|>


<commit_before>package main

import (
	"testing"
)

// TestValidPayload tests if we can create a proper object from
// a valid JSON payload.
func TestValidPayload(t *testing.T) {
	json := `[{
		"message" : {
		   "reqHost" : "www.example.com",
		   "respLen" : "276248",
		   "cliIP" : "123.123.123.123",
		   "status" : "503",
		   "bytes" : "123440",
		   "protoVer" : "1.1",
		   "respCT" : "text/html",
		   "UA" : "Mozilla%2f5.0%20(Macintosh%3b%20Intel%20Mac%20OS%20X%2010.9%3b%20rv%3a28.0)%20Gecko%2f20100101%20Firefox%2f28.0%20(FlipboardProxy%2f1.1%3b%20+http%3a%2f%2fflipboard.com%2fbrowserproxy)",
		   "reqMethod" : "POST",
		   "fwdHost" : "www.example.com",
		   "proto" : "http",
		   "reqPort" : "80",
		   "reqPath" : "%2f"
		},
		"netPerf" : {
		   "asnum" : "8523",
		   "downloadTime" : "1",
		   "edgeIP" : "165.254.92.141",
		   "lastByte" : "0",
		   "lastMileRTT" : "102",
		   "firstByte" : "0",
		   "cacheStatus" : "0"
		},
		"network" : {
		   "asnum" : "8523",
		   "edgeIP" : "165.254.92.141",
		   "networkType" : "",
		   "network" : ""
		},
		"cp" : "123456",
		"id" : "915cfea5570f824cc27112-a",
		"version" : "1.0",
		"start" : "1460634188.565",
		"type" : "cloud_monitor",
		"format" : "default",
		"respHdr" : {
		   "server" : "Microsoft-IIS/8.5",
		   "contEnc" : "identity"
		},
		"geo" : {
		   "lat" : "59.33",
		   "region" : "AB",
		   "long" : "18.05",
		   "country" : "DE",
		   "city" : "dummy"
		},
		"reqHdr" : {
		   "cookie" : "drbanan%3d1"
		}
	 }]`

	payloads, err := CreateObjects([]byte(json))
	if err != nil {
		t.Errorf("Error while trying to decode valid JSON payload: %s", err)
	}

	if len(payloads) != 1 {
		t.Errorf("Unexpected number of payloads in JSON: Should be 1, is %d", len(payloads))
	}

	payload := payloads[0]
	if payload.CP != "123456" {
		t.Errorf("CP not correct in payload. Should be 123456, is %s", payload.CP)
	}
	if payload.ID != "915cfea5570f824cc27112-a" {
		t.Errorf("ID not correct in payload. Should be 915cfea5570f824cc27112-a, is %s", payload.ID)
	}
	if payload.Geo["country"] != "DE" {
		t.Errorf("Country not correct in payload. Should be DE, is %s", payload.Geo["country"])
	}
}
<commit_msg>Update unit testing based on new struct<commit_after>package main

import (
	"testing"
)

// TestValidPayload tests if we can create a proper object from
// a valid JSON payload.
func TestValidPayload(t *testing.T) {
	json := `[{
		"message" : {
		   "reqHost" : "www.example.com",
		   "respLen" : "276248",
		   "cliIP" : "123.123.123.123",
		   "status" : "503",
		   "bytes" : "123440",
		   "protoVer" : "1.1",
		   "respCT" : "text/html",
		   "UA" : "Mozilla%2f5.0%20(Macintosh%3b%20Intel%20Mac%20OS%20X%2010.9%3b%20rv%3a28.0)%20Gecko%2f20100101%20Firefox%2f28.0%20(FlipboardProxy%2f1.1%3b%20+http%3a%2f%2fflipboard.com%2fbrowserproxy)",
		   "reqMethod" : "POST",
		   "fwdHost" : "www.example.com",
		   "proto" : "http",
		   "reqPort" : "80",
		   "reqPath" : "%2f"
		},
		"netPerf" : {
		   "asnum" : "8523",
		   "downloadTime" : "1",
		   "edgeIP" : "165.254.92.141",
		   "lastByte" : "0",
		   "lastMileRTT" : "102",
		   "firstByte" : "0",
		   "cacheStatus" : "0"
		},
		"network" : {
		   "asnum" : "8523",
		   "edgeIP" : "165.254.92.141",
		   "networkType" : "",
		   "network" : ""
		},
		"cp" : "123456",
		"id" : "915cfea5570f824cc27112-a",
		"version" : "1.0",
		"start" : "1460634188.565",
		"type" : "cloud_monitor",
		"format" : "default",
		"respHdr" : {
		   "server" : "Microsoft-IIS/8.5",
		   "contEnc" : "identity"
		},
		"geo" : {
		   "lat" : "59.33",
		   "region" : "AB",
		   "long" : "18.05",
		   "country" : "DE",
		   "city" : "dummy"
		},
		"reqHdr" : {
		   "cookie" : "drbanan%3d1"
		}
	 }]`

	payloads, err := CreateObjects([]byte(json))
	if err != nil {
		t.Errorf("Error while trying to decode valid JSON payload: %s", err)
	}

	if len(payloads) != 1 {
		t.Errorf("Unexpected number of payloads in JSON: Should be 1, is %d", len(payloads))
	}

	payload := payloads[0]
	if payload.CP != "123456" {
		t.Errorf("CP not correct in payload. Should be 123456, is %s", payload.CP)
	}
	if payload.ID != "915cfea5570f824cc27112-a" {
		t.Errorf("ID not correct in payload. Should be 915cfea5570f824cc27112-a, is %s", payload.ID)
	}
	if payload.Geo.Country != "DE" {
		t.Errorf("Country not correct in payload. Should be DE, is %s", payload.Geo.Country)
	}
}
<|endoftext|>


<commit_before>package service

import (
	"bufio"
	"encoding/json"
	"fmt"
	"io"
	"os"
	"strconv"
	"strings"

	"github.com/spf13/cobra"

	"stash.ovh.net/sailabove/sailgo/internal"
)

var cmdAddLink []string
var cmdAddNetworkAllow string
var addPublish []string
var cmdAddGateway string
var cmdAddVolume string
var batch bool
var cmdAddRedeploy bool
var cmdAddBody Add
var cmdAddNetwork []string

func addCmd() *cobra.Command {

	cmd := &cobra.Command{
		Use:   "add",
		Short: "Add a new docker service",
		Long: `add [<namespace>/]<repository>[:tag] [namespace/]<service-name>
		--model         Container model
		--number        Number of container to run
		[--link         name:alias]
		[--network      {public|private|<namespace name>}]
		[--network-allow [network:]ip[/mask] Use IPs whitelist]
		[--publish, -p  Publish a container's port to the host]
		[                 format: network:publishedPort:containerPort, network::containerPort, publishedPort:containerPort, containerPort]
		[--gateway      network-input:network-output
		[--restart {no|always[:<max>]|on-failure[:<max>]}]
		[--volume       /path:size] (Size in GB)
		[--batch        do not attach console on start]
		[--redeploy     if the service already exists, redeploy instead]

		override docker options:
			--user
			--entrypoint
			--command
			--workdir
			--environment KEY=val
		other options:
		`,
		Run: cmdAdd,
	}
	cmd.Flags().StringVarP(&cmdAddBody.ContainerModel, "model", "", "x1", "Container model")
	cmd.Flags().IntVarP(&cmdAddBody.ContainerNumber, "number", "", 1, "Number of container to run")
	cmd.Flags().StringSliceVarP(&cmdAddLink, "link", "", nil, "name:alias")
	cmd.Flags().StringSliceVar(&cmdAddNetwork, "network", []string{"public", "private"}, "public|private|<namespace name>")
	cmd.Flags().StringVarP(&cmdAddNetworkAllow, "network-allow", "", "", "[network:]ip[/mask] Use IPs whitelist")
	cmd.Flags().StringSliceVarP(&addPublish, "publish", "p", nil, "Publish a container's port to the host")
	cmd.Flags().StringVarP(&cmdAddGateway, "gateway", "", "", "network-input:network-output")
	cmd.Flags().StringVarP(&cmdAddBody.RestartPolicy, "restart", "", "no", "{no|always[:<max>]|on-failure[:<max>]}")
	cmd.Flags().StringVarP(&cmdAddVolume, "volume", "", "", "/path:size] (Size in GB)")
	cmd.Flags().BoolVarP(&batch, "batch", "", false, "do not attach console on start")
	cmd.Flags().BoolVarP(&cmdAddRedeploy, "redeploy", "", false, "if the service already exists, redeploy instead")
	cmd.Flags().StringSliceVarP(&cmdAddBody.ContainerEnvironment, "env", "e", nil, "override docker environment")
	// TODO [--pool <name>  use private hosts pool <name>]
	return cmd
}

// PortConfig is a parameter of Add to modify exposed container ports
type PortConfig struct {
	PublishedPort string `json:"published_port"`
	Network       string `json:"network,omitempty"`
}

// Add struct holds all parameters sent to /applications/%s/services/%s?stream
type Add struct {
	Service              string                       `json:"-"`
	Volumes              map[string]string            `json:"volumes,omitempty"`
	Repository           string                       `json:"repository"`
	ContainerUser        string                       `json:"container_user"`
	RestartPolicy        string                       `json:"restart_policy"`
	ContainerCommand     []string                     `json:"container_command,omitempty"`
	ContainerNetwork     map[string]map[string]string `json:"container_network"`
	ContainerEntrypoint  string                       `json:"container_user"`
	ContainerNumber      int                          `json:"container_number"`
	RepositoryTag        string                       `json:"repository_tag"`
	Links                map[string]string            `json:"links"`
	Application          string                       `json:"namespace"`
	ContainerWorkdir     string                       `json:"container_workdir"`
	ContainerEnvironment []string                     `json:"container_environment"`
	ContainerModel       string                       `json:"container_model"`
	ContainerPorts       map[string][]PortConfig      `json:"container_ports"`
}

func cmdAdd(cmd *cobra.Command, args []string) {
	cmdAddBody.ContainerNetwork = make(map[string]map[string]string)
	cmdAddBody.Links = make(map[string]string)
	cmdAddBody.Volumes = make(map[string]string)
	cmdAddBody.ContainerPorts = make(map[string][]PortConfig)
	cmdAddBody.ContainerCommand = make([]string, 0)

	if len(args) != 2 {
		fmt.Printf("Invalid usage. sailgo service add <application>/<repository>[:tag] <service>. Please see sailgo service add --help\n")
		return
	}

	// Get args
	cmdAddBody.Repository = args[0]
	cmdAddBody.Service = args[1]

	// Split repo URL and tag
	split := strings.Split(cmdAddBody.Repository, ":")
	if len(split) > 1 {
		cmdAddBody.Repository = split[0]
		cmdAddBody.RepositoryTag = split[1]
	}

	// Split namespace and repository
	split = strings.Split(cmdAddBody.Repository, "/")
	if len(split) > 1 {
		cmdAddBody.Application = split[0]
		cmdAddBody.Repository = split[1]
	}

	serviceAdd(cmdAddBody)
}

func serviceAdd(args Add) {

	if args.ContainerEnvironment == nil {
		args.ContainerEnvironment = make([]string, 0)
	}

	// Parse links
	for _, link := range cmdAddLink {
		t := strings.Split(link, ":")
		if len(t) == 1 {
			args.Links[t[0]] = t[0]
		} else {
			args.Links[t[0]] = t[1]
		}
	}

	// Parse ContainerNetworks arguments
	for _, network := range cmdAddNetwork {
		args.ContainerNetwork[network] = make(map[string]string)
	}

	// Parse ContainerPorts
	args.ContainerPorts = parsePublishedPort(addPublish)

	path := fmt.Sprintf("/applications/%s/services/%s", args.Application, args.Service)
	body, err := json.MarshalIndent(args, " ", " ")
	if err != nil {
		fmt.Printf("Fatal: %s\n", err)
		return
	}

	if batch {
		ret, code, err := internal.Request("POST", path, body)

		// http.Request failed for some reason
		if err != nil {
			fmt.Printf("Error: %s\n", err)
			return
		}

		//  If we are in ensure mode, fallback to redeploy
		if code == 409 && cmdAddRedeploy {
			ensureMode(args)
			return
		}

		// If API returned a json error
		e := internal.DecodeError(ret)
		if e != nil {
			fmt.Printf("%s\n", e)
			return
		}

		// Just print data
		fmt.Printf("%s\n", ret)
		return
	}

	buffer, code, err := internal.Stream("POST", path+"?stream", body)
	if err != nil {
		fmt.Printf("Error: %s\n", err)
		return
	}

	if code == 409 && cmdAddRedeploy {
		ensureMode(args)
		return
	}

	reader := bufio.NewReader(buffer)

	for {
		line, err := reader.ReadBytes('\n')
		m := internal.DecodeMessage(line)
		if m != nil {
			fmt.Println(m.Message)
		}
		e := internal.DecodeError(line)
		if e != nil {
			fmt.Println(e)
			if e.Code == 409 && cmdAddRedeploy {
				fmt.Printf("Starting redeploy...\n")
				ensureMode(args)
				return
			}
			os.Exit(1)
		}
		if err != nil && err == io.EOF {
			break
		}
		if err != nil {
			fmt.Printf("Error: %s\n", err)
			os.Exit(1)
		}
	}

	fmt.Printf("Starting service %s/%s...\n", args.Application, args.Service)
	serviceStart(args.Application, args.Service, batch)
}

func ensureMode(args Add) {
	redeployBatch = batch
	redeployBody := Redeploy{
		Service:              args.Service,
		Volumes:              args.Volumes,
		Repository:           args.Repository,
		ContainerUser:        args.ContainerUser,
		RestartPolicy:        args.RestartPolicy,
		ContainerCommand:     args.ContainerCommand,
		ContainerNetwork:     args.ContainerNetwork,
		ContainerEntrypoint:  args.ContainerEntrypoint,
		ContainerNumber:      args.ContainerNumber,
		RepositoryTag:        args.RepositoryTag,
		Links:                args.Links,
		Application:          args.Application,
		ContainerWorkdir:     args.ContainerWorkdir,
		ContainerEnvironment: args.ContainerEnvironment,
		ContainerModel:       args.ContainerModel,
		ContainerPorts:       args.ContainerPorts,
	}
	serviceRedeploy(redeployBody)
}

func parsePublishedPort(args []string) map[string][]PortConfig {
	v := make(map[string][]PortConfig)

	for _, pub := range args {
		split := strings.Split(pub, ":")
		if len(split) == 1 { // containerPort
			v[split[0]+"/tcp"] = []PortConfig{PortConfig{PublishedPort: split[0]}}
		} else if len(split) == 2 { // network::containerPort, publishedPort:containerPort
			_, err := strconv.Atoi("-42")
			if err != nil { // network::containerPort
				key := split[0] + "/" + split[1]
				v[key] = append(v[key], PortConfig{PublishedPort: split[0], Network: split[1]})
			} else { // publishedPort:containerPort
				key := split[0] + "/tcp"
				v[key] = append(v[key], PortConfig{PublishedPort: split[1]})
			}
		} else if len(split) == 3 { // network:publishedPort:containerPort
			if split[1] == "" {
				split[1] = split[2]
			}

			key := split[1] + "/" + split[0]
			v[key] = append(v[key], PortConfig{PublishedPort: split[2], Network: split[0]})
		}
	}

	return v
}
<commit_msg>fix : omitempty on workdir<commit_after>package service

import (
	"bufio"
	"encoding/json"
	"fmt"
	"io"
	"os"
	"strconv"
	"strings"

	"github.com/spf13/cobra"

	"stash.ovh.net/sailabove/sailgo/internal"
)

var cmdAddLink []string
var cmdAddNetworkAllow string
var addPublish []string
var cmdAddGateway string
var cmdAddVolume string
var batch bool
var cmdAddRedeploy bool
var cmdAddBody Add
var cmdAddNetwork []string

func addCmd() *cobra.Command {

	cmd := &cobra.Command{
		Use:   "add",
		Short: "Add a new docker service",
		Long: `add [<namespace>/]<repository>[:tag] [namespace/]<service-name>
		--model         Container model
		--number        Number of container to run
		[--link         name:alias]
		[--network      {public|private|<namespace name>}]
		[--network-allow [network:]ip[/mask] Use IPs whitelist]
		[--publish, -p  Publish a container's port to the host]
		[                 format: network:publishedPort:containerPort, network::containerPort, publishedPort:containerPort, containerPort]
		[--gateway      network-input:network-output
		[--restart {no|always[:<max>]|on-failure[:<max>]}]
		[--volume       /path:size] (Size in GB)
		[--batch        do not attach console on start]
		[--redeploy     if the service already exists, redeploy instead]

		override docker options:
			--user
			--entrypoint
			--command
			--workdir
			--environment KEY=val
		other options:
		`,
		Run: cmdAdd,
	}
	cmd.Flags().StringVarP(&cmdAddBody.ContainerModel, "model", "", "x1", "Container model")
	cmd.Flags().IntVarP(&cmdAddBody.ContainerNumber, "number", "", 1, "Number of container to run")
	cmd.Flags().StringSliceVarP(&cmdAddLink, "link", "", nil, "name:alias")
	cmd.Flags().StringSliceVar(&cmdAddNetwork, "network", []string{"public", "private"}, "public|private|<namespace name>")
	cmd.Flags().StringVarP(&cmdAddNetworkAllow, "network-allow", "", "", "[network:]ip[/mask] Use IPs whitelist")
	cmd.Flags().StringSliceVarP(&addPublish, "publish", "p", nil, "Publish a container's port to the host")
	cmd.Flags().StringVarP(&cmdAddGateway, "gateway", "", "", "network-input:network-output")
	cmd.Flags().StringVarP(&cmdAddBody.RestartPolicy, "restart", "", "no", "{no|always[:<max>]|on-failure[:<max>]}")
	cmd.Flags().StringVarP(&cmdAddVolume, "volume", "", "", "/path:size] (Size in GB)")
	cmd.Flags().BoolVarP(&batch, "batch", "", false, "do not attach console on start")
	cmd.Flags().BoolVarP(&cmdAddRedeploy, "redeploy", "", false, "if the service already exists, redeploy instead")
	cmd.Flags().StringSliceVarP(&cmdAddBody.ContainerEnvironment, "env", "e", nil, "override docker environment")
	// TODO [--pool <name>  use private hosts pool <name>]
	return cmd
}

// PortConfig is a parameter of Add to modify exposed container ports
type PortConfig struct {
	PublishedPort string `json:"published_port"`
	Network       string `json:"network,omitempty"`
}

// Add struct holds all parameters sent to /applications/%s/services/%s?stream
type Add struct {
	Service              string                       `json:"-"`
	Volumes              map[string]string            `json:"volumes,omitempty"`
	Repository           string                       `json:"repository"`
	ContainerUser        string                       `json:"container_user"`
	RestartPolicy        string                       `json:"restart_policy"`
	ContainerCommand     []string                     `json:"container_command,omitempty"`
	ContainerNetwork     map[string]map[string]string `json:"container_network"`
	ContainerEntrypoint  string                       `json:"container_user"`
	ContainerNumber      int                          `json:"container_number"`
	RepositoryTag        string                       `json:"repository_tag"`
	Links                map[string]string            `json:"links"`
	Application          string                       `json:"namespace"`
	ContainerWorkdir     string                       `json:"container_workdir,omitempty"`
	ContainerEnvironment []string                     `json:"container_environment"`
	ContainerModel       string                       `json:"container_model"`
	ContainerPorts       map[string][]PortConfig      `json:"container_ports"`
}

func cmdAdd(cmd *cobra.Command, args []string) {
	cmdAddBody.ContainerNetwork = make(map[string]map[string]string)
	cmdAddBody.Links = make(map[string]string)
	cmdAddBody.Volumes = make(map[string]string)
	cmdAddBody.ContainerPorts = make(map[string][]PortConfig)
	cmdAddBody.ContainerCommand = make([]string, 0)

	if len(args) != 2 {
		fmt.Printf("Invalid usage. sailgo service add <application>/<repository>[:tag] <service>. Please see sailgo service add --help\n")
		return
	}

	// Get args
	cmdAddBody.Repository = args[0]
	cmdAddBody.Service = args[1]

	// Split repo URL and tag
	split := strings.Split(cmdAddBody.Repository, ":")
	if len(split) > 1 {
		cmdAddBody.Repository = split[0]
		cmdAddBody.RepositoryTag = split[1]
	}

	// Split namespace and repository
	split = strings.Split(cmdAddBody.Repository, "/")
	if len(split) > 1 {
		cmdAddBody.Application = split[0]
		cmdAddBody.Repository = split[1]
	}

	serviceAdd(cmdAddBody)
}

func serviceAdd(args Add) {

	if args.ContainerEnvironment == nil {
		args.ContainerEnvironment = make([]string, 0)
	}

	// Parse links
	for _, link := range cmdAddLink {
		t := strings.Split(link, ":")
		if len(t) == 1 {
			args.Links[t[0]] = t[0]
		} else {
			args.Links[t[0]] = t[1]
		}
	}

	// Parse ContainerNetworks arguments
	for _, network := range cmdAddNetwork {
		args.ContainerNetwork[network] = make(map[string]string)
	}

	// Parse ContainerPorts
	args.ContainerPorts = parsePublishedPort(addPublish)

	path := fmt.Sprintf("/applications/%s/services/%s", args.Application, args.Service)
	body, err := json.MarshalIndent(args, " ", " ")
	if err != nil {
		fmt.Printf("Fatal: %s\n", err)
		return
	}

	if batch {
		ret, code, err := internal.Request("POST", path, body)

		// http.Request failed for some reason
		if err != nil {
			fmt.Printf("Error: %s\n", err)
			return
		}

		//  If we are in ensure mode, fallback to redeploy
		if code == 409 && cmdAddRedeploy {
			ensureMode(args)
			return
		}

		// If API returned a json error
		e := internal.DecodeError(ret)
		if e != nil {
			fmt.Printf("%s\n", e)
			return
		}

		// Just print data
		fmt.Printf("%s\n", ret)
		return
	}

	buffer, code, err := internal.Stream("POST", path+"?stream", body)
	if err != nil {
		fmt.Printf("Error: %s\n", err)
		return
	}

	if code == 409 && cmdAddRedeploy {
		ensureMode(args)
		return
	}

	reader := bufio.NewReader(buffer)

	for {
		line, err := reader.ReadBytes('\n')
		m := internal.DecodeMessage(line)
		if m != nil {
			fmt.Println(m.Message)
		}
		e := internal.DecodeError(line)
		if e != nil {
			fmt.Println(e)
			if e.Code == 409 && cmdAddRedeploy {
				fmt.Printf("Starting redeploy...\n")
				ensureMode(args)
				return
			}
			os.Exit(1)
		}
		if err != nil && err == io.EOF {
			break
		}
		if err != nil {
			fmt.Printf("Error: %s\n", err)
			os.Exit(1)
		}
	}

	fmt.Printf("Starting service %s/%s...\n", args.Application, args.Service)
	serviceStart(args.Application, args.Service, batch)
}

func ensureMode(args Add) {
	redeployBatch = batch
	redeployBody := Redeploy{
		Service:              args.Service,
		Volumes:              args.Volumes,
		Repository:           args.Repository,
		ContainerUser:        args.ContainerUser,
		RestartPolicy:        args.RestartPolicy,
		ContainerCommand:     args.ContainerCommand,
		ContainerNetwork:     args.ContainerNetwork,
		ContainerEntrypoint:  args.ContainerEntrypoint,
		ContainerNumber:      args.ContainerNumber,
		RepositoryTag:        args.RepositoryTag,
		Links:                args.Links,
		Application:          args.Application,
		ContainerWorkdir:     args.ContainerWorkdir,
		ContainerEnvironment: args.ContainerEnvironment,
		ContainerModel:       args.ContainerModel,
		ContainerPorts:       args.ContainerPorts,
	}
	serviceRedeploy(redeployBody)
}

func parsePublishedPort(args []string) map[string][]PortConfig {
	v := make(map[string][]PortConfig)

	for _, pub := range args {
		split := strings.Split(pub, ":")
		if len(split) == 1 { // containerPort
			v[split[0]+"/tcp"] = []PortConfig{PortConfig{PublishedPort: split[0]}}
		} else if len(split) == 2 { // network::containerPort, publishedPort:containerPort
			_, err := strconv.Atoi("-42")
			if err != nil { // network::containerPort
				key := split[0] + "/" + split[1]
				v[key] = append(v[key], PortConfig{PublishedPort: split[0], Network: split[1]})
			} else { // publishedPort:containerPort
				key := split[0] + "/tcp"
				v[key] = append(v[key], PortConfig{PublishedPort: split[1]})
			}
		} else if len(split) == 3 { // network:publishedPort:containerPort
			if split[1] == "" {
				split[1] = split[2]
			}

			key := split[1] + "/" + split[0]
			v[key] = append(v[key], PortConfig{PublishedPort: split[2], Network: split[0]})
		}
	}

	return v
}
<|endoftext|>


<commit_before>package registry

import (
	"errors"

	"github.com/rusenask/docker-registry-client/registry"

	log "github.com/Sirupsen/logrus"
)

// errors
var (
	ErrTagNotSupplied = errors.New("tag not supplied")
)

// Repository - holds repository related info
type Repository struct {
	Name string
	Tags []string // available tags
}

type Client interface {
	Get(opts Opts) (*Repository, error)
	Digest(opts Opts) (digest string, err error)
}

func New() *DefaultClient {
	return &DefaultClient{}
}

type DefaultClient struct {
}

type Opts struct {
	Registry, Name, Tag string
	Username, Password  string // if "" - anonymous
}

// Get - get repository
func (c *DefaultClient) Get(opts Opts) (*Repository, error) {

	repo := &Repository{}
	hub, err := registry.New(opts.Registry, opts.Username, opts.Password)
	if err != nil {
		return nil, err
	}

	tags, err := hub.Tags(opts.Name)
	if err != nil {
		return nil, err
	}
	repo.Tags = tags

	return repo, nil
}

// Digest - get digest for repo
func (c *DefaultClient) Digest(opts Opts) (digest string, err error) {
	if opts.Tag == "" {
		return "", ErrTagNotSupplied
	}

	log.WithFields(log.Fields{
		"registry":   opts.Registry,
		"repository": opts.Name,
		"tag":        opts.Tag,
	}).Info("registry client: getting digest")

	hub, err := registry.New(opts.Registry, opts.Username, opts.Password)
	if err != nil {
		return
	}

	manifestDigest, err := hub.ManifestDigest(opts.Name, opts.Tag)
	if err != nil {
		return
	}

	return manifestDigest.String(), nil
}
<commit_msg>registry tags<commit_after>package registry

import (
	"errors"

	"github.com/rusenask/docker-registry-client/registry"

	log "github.com/Sirupsen/logrus"
)

// errors
var (
	ErrTagNotSupplied = errors.New("tag not supplied")
)

// Repository - holds repository related info
type Repository struct {
	Name string
	Tags []string // available tags
}

type Client interface {
	Get(opts Opts) (*Repository, error)
	Digest(opts Opts) (digest string, err error)
}

func New() *DefaultClient {
	return &DefaultClient{}
}

type DefaultClient struct {
}

type Opts struct {
	Registry, Name, Tag string
	Username, Password  string // if "" - anonymous
}

// LogFormatter - formatter callback passed into registry client
func LogFormatter(format string, args ...interface{}) {
	log.Debugf(format, args...)
}

// Get - get repository
func (c *DefaultClient) Get(opts Opts) (*Repository, error) {

	repo := &Repository{}
	hub, err := registry.New(opts.Registry, opts.Username, opts.Password)
	if err != nil {
		return nil, err
	}
	hub.Logf = LogFormatter

	tags, err := hub.Tags(opts.Name)
	if err != nil {
		return nil, err
	}
	repo.Tags = tags

	return repo, nil
}

// Digest - get digest for repo
func (c *DefaultClient) Digest(opts Opts) (digest string, err error) {
	if opts.Tag == "" {
		return "", ErrTagNotSupplied
	}

	log.WithFields(log.Fields{
		"registry":   opts.Registry,
		"repository": opts.Name,
		"tag":        opts.Tag,
	}).Info("registry client: getting digest")

	hub, err := registry.New(opts.Registry, opts.Username, opts.Password)
	if err != nil {
		return
	}
	hub.Logf = LogFormatter

	manifestDigest, err := hub.ManifestDigest(opts.Name, opts.Tag)
	if err != nil {
		return
	}

	return manifestDigest.String(), nil
}
<|endoftext|>