<commit_before>package drain

import (
	"fmt"
	"io"
	"net"

	"github.com/nanopack/logvac/config"
	"github.com/nanopack/logvac/core"
)

// Papertrail drain implements the publisher interface for publishing logs to papertrail.
type Papertrail struct {
	ID		string				 // the app id or name
	Conn 	io.WriteCloser // connection to forward logs through
}

// NewPapertrailClient creates a new mist publisher
func NewPapertrailClient(uri, id string) (*Papertrail, error) {
	addr, err := net.ResolveUDPAddr("udp", uri)
	if err != nil {
		return nil, fmt.Errorf("Failed to resolve papertrail address - %s", err.Error())
	}

	conn, err := net.DialUDP("udp", nil, addr)
	if err != nil {
		return nil, fmt.Errorf("Failed to dial papertrail - %s", err.Error())
	}

	config.Log.Info("Connection to papertrail endpoint established")

	return &Papertrail{Conn: conn, ID: id}, nil
}

// Init initializes a connection to mist
func (p *Papertrail) Init() error {

	// add drain
	logvac.AddDrain("papertrail", p.Publish)

	return nil
}

// Publish utilizes mist's Publish to "drain" a log message
func (p *Papertrail) Publish(msg logvac.Message) {
	date := fmt.Sprintf("%s %02d %02d:%02d:%02d",
		msg.Time.Month().String()[:3],
		msg.Time.Day(),
		msg.Time.Hour(),
		msg.Time.Minute(),
		msg.Time.Second())
	id := fmt.Sprintf("%s.%s", p.ID, msg.Id)
	tag := msg.Tag[0]

	// the final message
	message := fmt.Sprintf("<%d>%s %s %s: %s\n",
		msg.Priority, date, id, tag, msg.Content)

	config.Log.Info("%s", message)
	p.Conn.Write([]byte(message))
}

// Close closes the connection to papertrail.
func (p *Papertrail) Close() error {
	if p.Conn == nil {
		return nil
	}
	return p.Conn.Close()
}
<commit_msg>Remove unecessary log entry<commit_after>package drain

import (
	"fmt"
	"io"
	"net"

	"github.com/nanopack/logvac/config"
	"github.com/nanopack/logvac/core"
)

// Papertrail drain implements the publisher interface for publishing logs to papertrail.
type Papertrail struct {
	ID		string				 // the app id or name
	Conn 	io.WriteCloser // connection to forward logs through
}

// NewPapertrailClient creates a new mist publisher
func NewPapertrailClient(uri, id string) (*Papertrail, error) {
	addr, err := net.ResolveUDPAddr("udp", uri)
	if err != nil {
		return nil, fmt.Errorf("Failed to resolve papertrail address - %s", err.Error())
	}

	conn, err := net.DialUDP("udp", nil, addr)
	if err != nil {
		return nil, fmt.Errorf("Failed to dial papertrail - %s", err.Error())
	}

	config.Log.Info("Connection to papertrail endpoint established")

	return &Papertrail{Conn: conn, ID: id}, nil
}

// Init initializes a connection to mist
func (p *Papertrail) Init() error {

	// add drain
	logvac.AddDrain("papertrail", p.Publish)

	return nil
}

// Publish utilizes mist's Publish to "drain" a log message
func (p *Papertrail) Publish(msg logvac.Message) {
	date := fmt.Sprintf("%s %02d %02d:%02d:%02d",
		msg.Time.Month().String()[:3],
		msg.Time.Day(),
		msg.Time.Hour(),
		msg.Time.Minute(),
		msg.Time.Second())
	id := fmt.Sprintf("%s.%s", p.ID, msg.Id)
	tag := msg.Tag[0]

	// the final message
	message := fmt.Sprintf("<%d>%s %s %s: %s\n",
		msg.Priority, date, id, tag, msg.Content)

	p.Conn.Write([]byte(message))
}

// Close closes the connection to papertrail.
func (p *Papertrail) Close() error {
	if p.Conn == nil {
		return nil
	}
	return p.Conn.Close()
}
<|endoftext|>


<commit_before>package drain

import (
	"fmt"
	"io"
	"net"

	"github.com/nanopack/logvac/config"
	"github.com/nanopack/logvac/core"
)

// Papertrail drain implements the publisher interface for publishing logs to papertrail.
type Papertrail struct {
	Conn io.WriteCloser // connection to forward logs through
}

// NewPapertrailClient creates a new mist publisher
func NewPapertrailClient(uri string) (*Papertrail, error) {
	addr, err := net.ResolveUDPAddr("udp", uri)
	if err != nil {
		return nil, fmt.Errorf("Failed to resolve papertrail address - %s", err.Error())
	}
	config.Log.Info("Papertrail address resolved IP: %s - Port: %d", addr.IP, addr.Port)

	conn, err := net.DialUDP("udp", nil, addr)
	if err != nil {
		return nil, fmt.Errorf("Failed to dial papertrail - %s", err.Error())
	}

	config.Log.Info("Connection to papertrail endpoint established")

	return &Papertrail{conn}, nil
}

// Init initializes a connection to mist
func (p *Papertrail) Init() error {

	// add drain
	logvac.AddDrain("papertrail", p.Publish)

	return nil
}

// Publish utilizes mist's Publish to "drain" a log message
func (p *Papertrail) Publish(msg logvac.Message) {
	config.Log.Info("Write 'papertrail' -> %s", msg.Raw)
	p.Conn.Write(msg.Raw)
}

// Close closes the connection to papertrail.
func (p *Papertrail) Close() error {
	if p.Conn == nil {
		return nil
	}
	return p.Conn.Close()
}
<commit_msg>Log the papertrail uri<commit_after>package drain

import (
	"fmt"
	"io"
	"net"

	"github.com/nanopack/logvac/config"
	"github.com/nanopack/logvac/core"
)

// Papertrail drain implements the publisher interface for publishing logs to papertrail.
type Papertrail struct {
	Conn io.WriteCloser // connection to forward logs through
}

// NewPapertrailClient creates a new mist publisher
func NewPapertrailClient(uri string) (*Papertrail, error) {
	config.Log.Info("Papertrail URI: %s", uri)
	addr, err := net.ResolveUDPAddr("udp", uri)
	if err != nil {
		return nil, fmt.Errorf("Failed to resolve papertrail address - %s", err.Error())
	}
	config.Log.Info("Papertrail address resolved IP: %s - Port: %d", addr.IP, addr.Port)

	conn, err := net.DialUDP("udp", nil, addr)
	if err != nil {
		return nil, fmt.Errorf("Failed to dial papertrail - %s", err.Error())
	}

	config.Log.Info("Connection to papertrail endpoint established")

	return &Papertrail{conn}, nil
}

// Init initializes a connection to mist
func (p *Papertrail) Init() error {

	// add drain
	logvac.AddDrain("papertrail", p.Publish)

	return nil
}

// Publish utilizes mist's Publish to "drain" a log message
func (p *Papertrail) Publish(msg logvac.Message) {
	config.Log.Info("Write 'papertrail' -> %s", msg.Raw)
	p.Conn.Write(msg.Raw)
}

// Close closes the connection to papertrail.
func (p *Papertrail) Close() error {
	if p.Conn == nil {
		return nil
	}
	return p.Conn.Close()
}
<|endoftext|>


<commit_before>package main

import (
	"net/url"

	"github.com/kataras/iris"

	"github.com/kataras/iris/core/host"
)

func main() {
	app := iris.New()

	app.Get("/", func(ctx iris.Context) {
		ctx.Writef("Hello from the SECURE server")
	})

	app.Get("/mypath", func(ctx iris.Context) {
		ctx.Writef("Hello from the SECURE server on path /mypath")
	})

	// to start a new server listening at :80 and redirects
	// to the secure address, then:
	target, _ := url.Parse("https://127.0.1:443")
	go host.NewProxy("127.0.0.1:80", target).ListenAndServe()

	// start the server (HTTPS) on port 443, this is a blocking func
	app.Run(iris.TLS("127.0.0.1:443", "mycert.cert", "mykey.key"))

}
<commit_msg>Replace NewProxy with NewRedirection #1302<commit_after>package main

import (
	"net/url"

	"github.com/kataras/iris"

	"github.com/kataras/iris/core/host"
)

func main() {
	app := iris.New()

	app.Get("/", func(ctx iris.Context) {
		ctx.Writef("Hello from the SECURE server")
	})

	app.Get("/mypath", func(ctx iris.Context) {
		ctx.Writef("Hello from the SECURE server on path /mypath")
	})

	// to start a new server listening at :80 and redirects
	// to the secure address, then:
	target, _ := url.Parse("https://127.0.0.1:443")
	go host.NewRedirection("127.0.0.1:80", target, 301).ListenAndServe()

	// start the server (HTTPS) on port 443, this is a blocking func
	app.Run(iris.TLS("127.0.0.1:443", "mycert.cert", "mykey.key"))
}
<|endoftext|>


<commit_before>package container

import (
	"testing"

	"fmt"
	"regexp"
	"strings"

	dockerclient "github.com/ivanilves/lstags/docker/client"
	dockerconfig "github.com/ivanilves/lstags/docker/config"
	"github.com/ivanilves/lstags/util/wait"
)

func TestRandomPort(t *testing.T) {
	const repeat = 5

	memory := make(map[int]int)

	for r := 0; r < repeat; r++ {
		port := getRandomPort()

		n, defined := memory[port]
		if defined {
			t.Fatalf(
				"already got port %d at repetition %d (current: %d)",
				port, n, r,
			)
		}

		memory[port] = r
	}
}

func TestGetHostname(t *testing.T) {
	port := getRandomPort()

	hostname := getHostname(port)
	endsWith := fmt.Sprintf(":%d", port)

	if !strings.HasSuffix(hostname, endsWith) {
		t.Fatalf("'%s' does not end with '%s'", hostname, endsWith)
	}
}

func TestRun(t *testing.T) {
	dc, _ := getDockerClient()

	port := getRandomPort()

	if _, err := run(dc, port); err != nil {
		t.Fatal(err.Error())
	}
}

func TestRunGuaranteedFailure(t *testing.T) {
	dc, _ := getDockerClient()

	const port = 2375

	if _, err := run(dc, port); err == nil {
		t.Fatal("how could you forward Docker's own port?")
	}
}

func testVerify(t *testing.T) {
	c, _ := Launch()

	defer c.Destroy()

	if err := verify(c.Hostname()); err != nil {
		t.Fatal(err.Error())
	}
}

func testVerifyGuaranteedFailure(t *testing.T) {
	const badHostname = "i.do.not.exist:8888"

	if err := verify(badHostname); err == nil {
		t.Fatalf("shoud fail on bad hostname: %s", badHostname)
	}
}

func TestLaunchAndThanDestroyIt(t *testing.T) {
	c, err := Launch()
	if err != nil {
		t.Fatal(err)
	}

	const idExpr = "^[a-f0-9]{64}$"
	if matched, _ := regexp.MatchString(idExpr, c.ID()); !matched {
		t.Fatalf("id '%s' does not match regex: %s", c.ID(), idExpr)
	}

	const hostnameExpr = "^[a-z0-9][a-z0-9\\-\\.]+[a-z0-9]:[0-9]{4,5}$"
	if matched, _ := regexp.MatchString(hostnameExpr, c.Hostname()); !matched {
		t.Fatalf("hostname '%s' does not match regex: %s", c.Hostname(), hostnameExpr)
	}

	if err := c.Destroy(); err != nil {
		t.Fatal(err)
	}

	if err := c.Destroy(); err == nil {
		t.Fatalf("Container can not be destroyed more than once: %s", c.ID())
	}
}

func TestLaunchManyContainersWithoutNamingCollisions(t *testing.T) {
	const createContainers = 3

	done := make(chan error, createContainers)

	for c := 0; c < createContainers; c++ {
		go func() {
			c, err := Launch()
			if err != nil {
				done <- err
				return
			}

			defer c.Destroy()

			done <- nil
		}()
	}

	if err := wait.Until(done); err != nil {
		t.Error(err)
	}
}

func TestSeedContainerWithImages(t *testing.T) {
	c, err := Launch()
	if err != nil {
		t.Fatal(err)
	}

	defer c.Destroy()

	refs, err := c.SeedWithImages("alpine:3.7", "busybox:latest")
	if err != nil {
		t.Fatal(err)
	}

	dockerConfig, err := dockerconfig.Load(dockerconfig.DefaultDockerJSON)
	if err != nil {
		t.Fatal(err)
	}

	dockerClient, err := dockerclient.New(dockerConfig)
	if err != nil {
		t.Fatal(err)
	}

	done := make(chan error, len(refs))

	for _, ref := range refs {
		go func(ref string) {
			resp, err := dockerClient.Pull(ref)
			if err != nil {
				done <- err
				return
			}

			logDebugData(resp)

			done <- nil
		}(ref)
	}

	if err := wait.Until(done); err != nil {
		t.Fatal(err)
	}
}

func TestSeedContainerWithImagesGuaranteedFailure(t *testing.T) {
	c, err := Launch()
	if err != nil {
		t.Fatal(err)
	}

	defer c.Destroy()

	if _, err := c.SeedWithImages(); err == nil {
		t.Fatal("should not process nil as image list")
	}

	if _, err := c.SeedWithImages([]string{}...); err == nil {
		t.Fatal("should not process empty image list")
	}

	if _, err := c.SeedWithImages([]string{"", "", ""}...); err == nil {
		t.Fatal("should not process list of empty strings")
	}

	if _, err := c.SeedWithImages([]string{"1u[pine~!.*/"}...); err == nil {
		t.Fatal("should not process invalid references")
	}

	if _, err := c.SeedWithImages([]string{"alpine"}...); err == nil {
		t.Fatal("should not process references without tag specified")
	}
}
<commit_msg>test(api/v1/registry/container): Fix `TestRun` cleanup<commit_after>package container

import (
	"testing"

	"fmt"
	"regexp"
	"strings"

	dockerclient "github.com/ivanilves/lstags/docker/client"
	dockerconfig "github.com/ivanilves/lstags/docker/config"
	"github.com/ivanilves/lstags/util/wait"
)

func TestRandomPort(t *testing.T) {
	const repeat = 5

	memory := make(map[int]int)

	for r := 0; r < repeat; r++ {
		port := getRandomPort()

		n, defined := memory[port]
		if defined {
			t.Fatalf(
				"already got port %d at repetition %d (current: %d)",
				port, n, r,
			)
		}

		memory[port] = r
	}
}

func TestGetHostname(t *testing.T) {
	port := getRandomPort()

	hostname := getHostname(port)
	endsWith := fmt.Sprintf(":%d", port)

	if !strings.HasSuffix(hostname, endsWith) {
		t.Fatalf("'%s' does not end with '%s'", hostname, endsWith)
	}
}

func TestRun(t *testing.T) {
	dc, _ := getDockerClient()

	port := getRandomPort()

	id, err := run(dc, port)
	if err != nil {
		t.Fatal(err.Error())
	}

	dc.ForceRemove(id)
}

func TestRunGuaranteedFailure(t *testing.T) {
	dc, _ := getDockerClient()

	const port = 2375

	if _, err := run(dc, port); err == nil {
		t.Fatal("how could you forward Docker's own port?")
	}
}

func testVerify(t *testing.T) {
	c, _ := Launch()

	defer c.Destroy()

	if err := verify(c.Hostname()); err != nil {
		t.Fatal(err.Error())
	}
}

func testVerifyGuaranteedFailure(t *testing.T) {
	const badHostname = "i.do.not.exist:8888"

	if err := verify(badHostname); err == nil {
		t.Fatalf("shoud fail on bad hostname: %s", badHostname)
	}
}

func TestLaunchAndThanDestroyIt(t *testing.T) {
	c, err := Launch()
	if err != nil {
		t.Fatal(err)
	}

	const idExpr = "^[a-f0-9]{64}$"
	if matched, _ := regexp.MatchString(idExpr, c.ID()); !matched {
		t.Fatalf("id '%s' does not match regex: %s", c.ID(), idExpr)
	}

	const hostnameExpr = "^[a-z0-9][a-z0-9\\-\\.]+[a-z0-9]:[0-9]{4,5}$"
	if matched, _ := regexp.MatchString(hostnameExpr, c.Hostname()); !matched {
		t.Fatalf("hostname '%s' does not match regex: %s", c.Hostname(), hostnameExpr)
	}

	if err := c.Destroy(); err != nil {
		t.Fatal(err)
	}

	if err := c.Destroy(); err == nil {
		t.Fatalf("Container can not be destroyed more than once: %s", c.ID())
	}
}

func TestLaunchManyContainersWithoutNamingCollisions(t *testing.T) {
	const createContainers = 3

	done := make(chan error, createContainers)

	for c := 0; c < createContainers; c++ {
		go func() {
			c, err := Launch()
			if err != nil {
				done <- err
				return
			}

			defer c.Destroy()

			done <- nil
		}()
	}

	if err := wait.Until(done); err != nil {
		t.Error(err)
	}
}

func TestSeedContainerWithImages(t *testing.T) {
	c, err := Launch()
	if err != nil {
		t.Fatal(err)
	}

	defer c.Destroy()

	refs, err := c.SeedWithImages("alpine:3.7", "busybox:latest")
	if err != nil {
		t.Fatal(err)
	}

	dockerConfig, err := dockerconfig.Load(dockerconfig.DefaultDockerJSON)
	if err != nil {
		t.Fatal(err)
	}

	dockerClient, err := dockerclient.New(dockerConfig)
	if err != nil {
		t.Fatal(err)
	}

	done := make(chan error, len(refs))

	for _, ref := range refs {
		go func(ref string) {
			resp, err := dockerClient.Pull(ref)
			if err != nil {
				done <- err
				return
			}

			logDebugData(resp)

			done <- nil
		}(ref)
	}

	if err := wait.Until(done); err != nil {
		t.Fatal(err)
	}
}

func TestSeedContainerWithImagesGuaranteedFailure(t *testing.T) {
	c, err := Launch()
	if err != nil {
		t.Fatal(err)
	}

	defer c.Destroy()

	if _, err := c.SeedWithImages(); err == nil {
		t.Fatal("should not process nil as image list")
	}

	if _, err := c.SeedWithImages([]string{}...); err == nil {
		t.Fatal("should not process empty image list")
	}

	if _, err := c.SeedWithImages([]string{"", "", ""}...); err == nil {
		t.Fatal("should not process list of empty strings")
	}

	if _, err := c.SeedWithImages([]string{"1u[pine~!.*/"}...); err == nil {
		t.Fatal("should not process invalid references")
	}

	if _, err := c.SeedWithImages([]string{"alpine"}...); err == nil {
		t.Fatal("should not process references without tag specified")
	}
}
<|endoftext|>


<commit_before>package engine

import (
	"adexchange/lib"
	m "adexchange/models"
	//"bytes"
	"encoding/json"
	"github.com/astaxie/beego"
	"github.com/franela/goreq"
	"net/url"
	"time"
)

func invokeMH(demand *Demand) {

	adRequest := demand.AdRequest
	beego.Debug("Start Invoke MH,bid:" + adRequest.Bid)
	item := url.Values{}

	//item.Set("bid", lib.GenerateBid(demand.AdspaceKey))
	item.Set("bid", adRequest.Bid)
	item.Set("adspaceid", demand.AdspaceKey)
	//hard code 2 to request MH as hero app
	item.Set("adtype", "2")

	if len(demand.PkgName) > 0 {
		item.Set("pkgname", demand.PkgName)
	} else {
		item.Set("pkgname", adRequest.Pkgname)
	}

	if len(demand.PkgName) > 0 {
		item.Set("appname", demand.AppName)
	} else {
		item.Set("appname", adRequest.Appname)
	}

	if len(demand.PkgName) > 0 {
		item.Set("pcat", lib.ConvertIntToString(demand.Pcat))
	} else {
		item.Set("pcat", adRequest.Pcat)
	}

	if len(demand.PkgName) > 0 {
		item.Set("ua", demand.Ua)
	} else {
		item.Set("ua", adRequest.Ua)
	}

	item.Set("conn", adRequest.Conn)
	item.Set("carrier", adRequest.Carrier)
	//hard code 2 to return json response
	item.Set("apitype", "2")
	item.Set("os", lib.ConvertIntToString(adRequest.Os))
	item.Set("osv", adRequest.Osv)
	item.Set("imei", adRequest.Imei)
	item.Set("wma", adRequest.Wma)
	item.Set("aid", adRequest.Aid)
	item.Set("aaid", adRequest.Aaid)
	item.Set("idfa", adRequest.Idfa)
	item.Set("oid", adRequest.Oid)
	item.Set("uid", adRequest.Uid)
	item.Set("device", adRequest.Device)

	item.Set("ip", adRequest.Ip)
	item.Set("width", adRequest.Width)
	item.Set("height", adRequest.Height)
	item.Set("density", adRequest.Density)
	item.Set("lon", lib.ConvertFloatToString(adRequest.Lon))
	item.Set("lat", lib.ConvertFloatToString(adRequest.Lat))

	res, err := goreq.Request{
		Uri:         demand.URL,
		QueryString: item,
		//ShowDebug:   true,
		Timeout: time.Duration(demand.Timeout) * time.Millisecond,
	}.Do()

	adResponse := initAdResponse(demand)

	var strResponse string
	if serr, ok := err.(*goreq.Error); ok {
		beego.Critical(err.Error())
		if serr.Timeout() {
			//adResponse = generateErrorResponse(adRequest, demand.AdspaceKey, lib.ERROR_TIMEOUT_ERROR)
			adResponse.StatusCode = lib.ERROR_TIMEOUT_ERROR
		} else {
			//adResponse = generateErrorResponse(adRequest, demand.AdspaceKey, lib.ERROR_MHSERVER_ERROR)
			adResponse.StatusCode = lib.ERROR_MHSERVER_ERROR
		}

	} else {
		var resultMap map[string]*m.MHAdUnit

		//flg, _ := beego.AppConfig.Bool("log_demand_body")
		//var err error
		//if flg {
		//	strResponse, _ = res.Body.ToString()
		//	err = json.Unmarshal([]byte(strResponse), &resultMap)

		//} else {
		//	err = res.Body.FromJsonTo(&resultMap)
		//}
		strResponse, _ = res.Body.ToString()
		err = json.Unmarshal([]byte(strResponse), &resultMap)
		defer res.Body.Close()

		if err != nil {
			beego.Critical(err.Error())
			//adResponse = generateErrorResponse(adRequest, demand.AdspaceKey, lib.ERROR_MAP_ERROR)
			adResponse.StatusCode = lib.ERROR_MAP_ERROR
			//demand.Result <- adResponse
		} else {
			if resultMap != nil {
				for _, v := range resultMap {
					mapMHResult(adResponse, v)
					//adResponse.Bid = adRequest.Bid
					//adResponse.SetDemandAdspaceKey(demand.AdspaceKey)
					//demand.Result <- adResponse
					break
				}
			} else {
				//adResponse = generateErrorResponse(adRequest, demand.AdspaceKey, lib.ERROR_MAP_ERROR)
				//demand.Result <- adResponse
				adResponse.StatusCode = lib.ERROR_MAP_ERROR
			}
		}
	}

	//todo
	//if adResponse.StatusCode != lib.STATUS_SUCCESS {
	//	adResponse.ResBody = strResponse
	//}
	adResponse.ResBody = strResponse
	demand.Result <- adResponse
}

func mapMHResult(adResponse *m.AdResponse, mhAdunit *m.MHAdUnit) {

	adResponse.StatusCode = mhAdunit.Returncode

	if adResponse.StatusCode == 200 {
		adUnit := new(m.AdUnit)
		adResponse.Adunit = adUnit
		adUnit.Cid = mhAdunit.Cid
		adUnit.ClickUrl = mhAdunit.Clickurl
		//todo hardcode 3 for MH, only support picture ad
		//adUnit.CreativeType = 3
		adUnit.CreativeUrls = []string{mhAdunit.Imgurl}
		adUnit.ImpTrackingUrls = mhAdunit.Imgtracking
		adUnit.ClkTrackingUrls = mhAdunit.Thclkurl
		adUnit.AdWidth = mhAdunit.Adwidth
		adUnit.AdHeight = mhAdunit.Adheight
	}

	//return adResponse
}
<commit_msg>fix ctrl char in the demand response<commit_after>package engine

import (
	"adexchange/lib"
	m "adexchange/models"
	//"bytes"
	"encoding/json"
	"github.com/astaxie/beego"
	"github.com/franela/goreq"
	"net/url"
	"time"
)

func invokeMH(demand *Demand) {

	adRequest := demand.AdRequest
	beego.Debug("Start Invoke MH,bid:" + adRequest.Bid)
	item := url.Values{}

	//item.Set("bid", lib.GenerateBid(demand.AdspaceKey))
	item.Set("bid", adRequest.Bid)
	item.Set("adspaceid", demand.AdspaceKey)
	//hard code 2 to request MH as hero app
	item.Set("adtype", "2")

	if len(demand.PkgName) > 0 {
		item.Set("pkgname", demand.PkgName)
	} else {
		item.Set("pkgname", adRequest.Pkgname)
	}

	if len(demand.PkgName) > 0 {
		item.Set("appname", demand.AppName)
	} else {
		item.Set("appname", adRequest.Appname)
	}

	if len(demand.PkgName) > 0 {
		item.Set("pcat", lib.ConvertIntToString(demand.Pcat))
	} else {
		item.Set("pcat", adRequest.Pcat)
	}

	if len(demand.PkgName) > 0 {
		item.Set("ua", demand.Ua)
	} else {
		item.Set("ua", adRequest.Ua)
	}

	item.Set("conn", adRequest.Conn)
	item.Set("carrier", adRequest.Carrier)
	//hard code 2 to return json response
	item.Set("apitype", "2")
	item.Set("os", lib.ConvertIntToString(adRequest.Os))
	item.Set("osv", adRequest.Osv)
	item.Set("imei", adRequest.Imei)
	item.Set("wma", adRequest.Wma)
	item.Set("aid", adRequest.Aid)
	item.Set("aaid", adRequest.Aaid)
	item.Set("idfa", adRequest.Idfa)
	item.Set("oid", adRequest.Oid)
	item.Set("uid", adRequest.Uid)
	item.Set("device", adRequest.Device)

	item.Set("ip", adRequest.Ip)
	item.Set("width", adRequest.Width)
	item.Set("height", adRequest.Height)
	item.Set("density", adRequest.Density)
	item.Set("lon", lib.ConvertFloatToString(adRequest.Lon))
	item.Set("lat", lib.ConvertFloatToString(adRequest.Lat))

	res, err := goreq.Request{
		Uri:         demand.URL,
		QueryString: item,
		//ShowDebug:   true,
		Timeout: time.Duration(demand.Timeout) * time.Millisecond,
	}.Do()

	adResponse := initAdResponse(demand)

	var strResponse string
	if serr, ok := err.(*goreq.Error); ok {
		beego.Critical(err.Error())
		if serr.Timeout() {
			//adResponse = generateErrorResponse(adRequest, demand.AdspaceKey, lib.ERROR_TIMEOUT_ERROR)
			adResponse.StatusCode = lib.ERROR_TIMEOUT_ERROR
		} else {
			//adResponse = generateErrorResponse(adRequest, demand.AdspaceKey, lib.ERROR_MHSERVER_ERROR)
			adResponse.StatusCode = lib.ERROR_MHSERVER_ERROR
		}

	} else {
		var resultMap map[string]*m.MHAdUnit

		//flg, _ := beego.AppConfig.Bool("log_demand_body")
		//var err error
		//if flg {
		//	strResponse, _ = res.Body.ToString()
		//	err = json.Unmarshal([]byte(strResponse), &resultMap)

		//} else {
		//	err = res.Body.FromJsonTo(&resultMap)
		//}
		strResponse, _ = res.Body.ToString()
		err = json.Unmarshal(lib.EscapeCtrl([]byte(strResponse)), &resultMap)
		defer res.Body.Close()

		if err != nil {
			beego.Critical(err.Error())
			//adResponse = generateErrorResponse(adRequest, demand.AdspaceKey, lib.ERROR_MAP_ERROR)
			adResponse.StatusCode = lib.ERROR_MAP_ERROR
			//demand.Result <- adResponse
		} else {
			if resultMap != nil {
				for _, v := range resultMap {
					mapMHResult(adResponse, v)
					//adResponse.Bid = adRequest.Bid
					//adResponse.SetDemandAdspaceKey(demand.AdspaceKey)
					//demand.Result <- adResponse
					break
				}
			} else {
				//adResponse = generateErrorResponse(adRequest, demand.AdspaceKey, lib.ERROR_MAP_ERROR)
				//demand.Result <- adResponse
				adResponse.StatusCode = lib.ERROR_MAP_ERROR
			}
		}
	}

	//todo
	//if adResponse.StatusCode != lib.STATUS_SUCCESS {
	//	adResponse.ResBody = strResponse
	//}
	adResponse.ResBody = strResponse
	demand.Result <- adResponse
}

func mapMHResult(adResponse *m.AdResponse, mhAdunit *m.MHAdUnit) {

	adResponse.StatusCode = mhAdunit.Returncode

	if adResponse.StatusCode == 200 {
		adUnit := new(m.AdUnit)
		adResponse.Adunit = adUnit
		adUnit.Cid = mhAdunit.Cid
		adUnit.ClickUrl = mhAdunit.Clickurl
		//todo hardcode 3 for MH, only support picture ad
		//adUnit.CreativeType = 3
		adUnit.CreativeUrls = []string{mhAdunit.Imgurl}
		adUnit.ImpTrackingUrls = mhAdunit.Imgtracking
		adUnit.ClkTrackingUrls = mhAdunit.Thclkurl
		adUnit.AdWidth = mhAdunit.Adwidth
		adUnit.AdHeight = mhAdunit.Adheight
	}

	//return adResponse
}
<|endoftext|>


<commit_before>package main

import (
	"database/sql"
	"io/ioutil"
	"net/http"
	"strconv"

	"github.com/gorilla/mux"

	"github.com/ovh/cds/engine/api/cache"
	"github.com/ovh/cds/engine/api/context"
	"github.com/ovh/cds/engine/api/pipeline"
	"github.com/ovh/cds/engine/log"
	"github.com/ovh/cds/sdk"
)

func addStageHandler(w http.ResponseWriter, r *http.Request, db *sql.DB, c *context.Context) {

	// Get project name in URL
	vars := mux.Vars(r)
	projectKey := vars["key"]
	pipelineKey := vars["permPipelineKey"]

	// Get body
	data, err := ioutil.ReadAll(r.Body)
	if err != nil {
		log.Warning("addStageHandler> cannot read body: %s", err)
		WriteError(w, r, sdk.ErrWrongRequest)
		return
	}

	stageData, err := sdk.NewStage("").FromJSON(data)
	if err != nil {
		log.Warning("addStageHandler> cannot unmarshal body: %s", err)
		WriteError(w, r, sdk.ErrWrongRequest)
		return
	}

	// Check if pipeline exist
	pipelineData, err := pipeline.LoadPipeline(db, projectKey, pipelineKey, false)
	if err != nil {
		WriteError(w, r, err)
		return
	}

	if err := pipeline.LoadPipelineStage(db, pipelineData); err != nil {
		log.Warning("addStageHandler> Cannot load pipeline stages: %s", err)
		WriteError(w, r, err)
		return
	}

	stageData.BuildOrder = len(pipelineData.Stages) + 1
	stageData.PipelineID = pipelineData.ID

	tx, err := db.Begin()
	if err != nil {
		log.Warning("addStageHandler> Cannot start transaction: %s", err)
		WriteError(w, r, err)
	}
	defer tx.Rollback()

	if err := pipeline.InsertStage(db, stageData); err != nil {
		log.Warning("addStageHandler> Cannot insert stage: %s", err)
		WriteError(w, r, err)
		return
	}

	if err := pipeline.UpdatePipelineLastModified(tx, pipelineData); err != nil {
		log.Warning("addStageHandler> Cannot update pipeline last modified date: %s", err)
		WriteError(w, r, err)
		return
	}

	if err := tx.Commit(); err != nil {
		log.Warning("addStageHandler> Cannot commit transaction: %s", err)
		WriteError(w, r, err)
	}

	if err := pipeline.LoadPipelineStage(db, pipelineData); err != nil {
		log.Warning("addStageHandler> Cannot load pipeline stages: %s", err)
		WriteError(w, r, err)
		return
	}

	k := cache.Key("application", projectKey, "*")
	cache.DeleteAll(k)
	cache.Delete(cache.Key("pipeline", projectKey, pipelineKey))

	WriteJSON(w, r, stageData, http.StatusCreated)
}

func getStageHandler(w http.ResponseWriter, r *http.Request, db *sql.DB, c *context.Context) {
	// Get project name in URL
	vars := mux.Vars(r)
	projectKey := vars["key"]
	pipelineKey := vars["permPipelineKey"]
	stageIDString := vars["stageID"]

	stageID, err := strconv.ParseInt(stageIDString, 10, 60)
	if err != nil {
		log.Warning("getStageHandler> Stage ID must be an int: %s", err)
		WriteError(w, r, sdk.ErrWrongRequest)
		return
	}

	// Check if pipeline exist
	pipelineData, err := pipeline.LoadPipeline(db, projectKey, pipelineKey, false)
	if err != nil {
		WriteError(w, r, err)
		return
	}

	s, err := pipeline.LoadStage(db, pipelineData.ID, stageID)
	if err != nil {
		WriteError(w, r, err)
		return
	}

	WriteJSON(w, r, s, http.StatusOK)
}

func moveStageHandler(w http.ResponseWriter, r *http.Request, db *sql.DB, c *context.Context) {

	// Get project name in URL
	vars := mux.Vars(r)
	projectKey := vars["key"]
	pipelineKey := vars["permPipelineKey"]

	// Get body
	data, err := ioutil.ReadAll(r.Body)
	if err != nil {
		log.Warning("moveStageHandler> cannot read body: %s", err)
		WriteError(w, r, sdk.ErrWrongRequest)
		return
	}

	// get stage to move
	stageData, err := sdk.NewStage("").FromJSON(data)
	if err != nil {
		log.Warning("moveStageHandler> Cannot unmarshal body: %s", err)
		WriteError(w, r, sdk.ErrWrongRequest)
		return
	}

	if stageData.BuildOrder < 1 {
		log.Warning("moveStageHandler> Build Order must be greater than 0")
		WriteError(w, r, sdk.ErrWrongRequest)
	}

	// Check if pipeline exist
	pipelineData, err := pipeline.LoadPipeline(db, projectKey, pipelineKey, false)
	if err != nil {
		WriteError(w, r, err)
		return
	}

	// count stage for this pipeline
	nbStage, err := pipeline.CountStageByPipelineID(db, pipelineData.ID)
	if err != nil {
		log.Warning("moveStageHandler> Cannot count stage for pipeline %s : %s", pipelineData.Name, err)
		WriteError(w, r, err)
		return
	}

	if stageData.BuildOrder <= nbStage {
		// check if stage exist
		s, err := pipeline.LoadStage(db, pipelineData.ID, stageData.ID)
		if err != nil {
			log.Warning("moveStageHandler> Cannot load stage: %s", err)
			WriteError(w, r, err)
			return
		}

		if err := pipeline.MoveStage(db, s, stageData.BuildOrder, pipelineData); err != nil {
			log.Warning("moveStageHandler> Cannot move stage: %s", err)
			WriteError(w, r, err)
			return
		}
	}

	if err := pipeline.LoadPipelineStage(db, pipelineData); err != nil {
		log.Warning("moveStageHandler> Cannot load stages: %s", err)
		WriteError(w, r, err)
		return
	}

	k := cache.Key("application", projectKey, "*")
	cache.DeleteAll(k)
	cache.Delete(cache.Key("pipeline", projectKey, pipelineKey))

	WriteJSON(w, r, pipelineData, http.StatusOK)
}

func updateStageHandler(w http.ResponseWriter, r *http.Request, db *sql.DB, c *context.Context) {

	// Get project name in URL
	vars := mux.Vars(r)
	projectKey := vars["key"]
	pipelineKey := vars["permPipelineKey"]
	stageIDString := vars["stageID"]

	// Get body
	data, err := ioutil.ReadAll(r.Body)
	if err != nil {
		log.Warning("addStageHandler> cannot read body: %s", err)
		WriteError(w, r, sdk.ErrWrongRequest)
		return
	}

	stageData, err := sdk.NewStage("").FromJSON(data)
	if err != nil {
		log.Warning("addStageHandler> Cannot unmarshal body: %s", err)
		WriteError(w, r, sdk.ErrWrongRequest)
		return
	}

	stageID, err := strconv.ParseInt(stageIDString, 10, 60)
	if err != nil {
		log.Warning("addStageHandler> Stage ID must be an int: %s", err)
		WriteError(w, r, sdk.ErrInvalidID)
		return
	}
	if stageID != stageData.ID {
		log.Warning("addStageHandler> Stage ID doest not match")
		WriteError(w, r, sdk.ErrInvalidID)
		return
	}

	// Check if pipeline exist
	pipelineData, err := pipeline.LoadPipeline(db, projectKey, pipelineKey, false)
	if err != nil {
		WriteError(w, r, err)
		return
	}

	// check if stage exist
	s, err := pipeline.LoadStage(db, pipelineData.ID, stageData.ID)
	if err != nil {
		log.Warning("addStageHandler> Cannot Load stage: %s", err)
		WriteError(w, r, err)
		return
	}
	stageData.ID = s.ID

	tx, err := db.Begin()
	if err != nil {
		log.Warning("addStageHandler> Cannot start transaction: %s", err)
		WriteError(w, r, err)
		return
	}
	defer tx.Rollback()

	if err := pipeline.UpdateStage(tx, stageData); err != nil {
		log.Warning("addStageHandler> Cannot update stage: %s", err)
		WriteError(w, r, err)
		return
	}

	if err := pipeline.UpdatePipelineLastModified(tx, pipelineData); err != nil {
		log.Warning("addStageHandler> Cannot update pipeline last_modified: %s", err)
		WriteError(w, r, err)
		return
	}

	err = tx.Commit()
	if err != nil {
		log.Warning("addStageHandler> Cannot commit transaction: %s", err)
		WriteError(w, r, err)
		return
	}

	if err := pipeline.LoadPipelineStage(db, pipelineData); err != nil {
		log.Warning("addStageHandler> Cannot load stages: %s", err)
		WriteError(w, r, err)
		return
	}

	k := cache.Key("application", projectKey, "*")
	cache.DeleteAll(k)
	cache.Delete(cache.Key("pipeline", projectKey, pipelineKey))

	WriteJSON(w, r, pipelineData, http.StatusOK)
}

func deleteStageHandler(w http.ResponseWriter, r *http.Request, db *sql.DB, c *context.Context) {

	// Get project name in URL
	vars := mux.Vars(r)
	projectKey := vars["key"]
	pipelineKey := vars["permPipelineKey"]
	stageIDString := vars["stageID"]

	// Check if pipeline exist
	pipelineData, err := pipeline.LoadPipeline(db, projectKey, pipelineKey, false)
	if err != nil {
		log.Warning("deleteStageHandler> Cannot load pipeline %s: %s", pipelineKey, err)
		WriteError(w, r, err)
		return
	}

	stageID, err := strconv.ParseInt(stageIDString, 10, 60)
	if err != nil {
		log.Warning("deleteStageHandler> Stage ID must be an int: %s", err)
		WriteError(w, r, sdk.ErrInvalidID)
		return
	}

	// check if stage exist
	s, err := pipeline.LoadStage(db, pipelineData.ID, stageID)
	if err != nil {
		log.Warning("deleteStageHandler> Cannot Load stage: %s", err)
		WriteError(w, r, err)
		return
	}

	tx, err := db.Begin()
	if err != nil {
		log.Warning("deleteStageHandler> Cannot start transaction: %s", err)
		WriteError(w, r, err)
		return
	}
	defer tx.Rollback()

	if err := pipeline.DeleteStageByID(tx, s, c.User.ID); err != nil {
		log.Warning("deleteStageHandler> Cannot Delete stage: %s", err)
		WriteError(w, r, err)
		return
	}

	if err := pipeline.UpdatePipelineLastModified(tx, pipelineData); err != nil {
		log.Warning("deleteStageHandler> Cannot Update pipeline last_modified: %s", err)
		WriteError(w, r, err)
		return
	}

	if err := tx.Commit(); err != nil {
		log.Warning("deleteStageHandler> Cannot commit transaction: %s", err)
		WriteError(w, r, err)
		return
	}

	if err := pipeline.LoadPipelineStage(db, pipelineData); err != nil {
		log.Warning("deleteStageHandler> Cannot load stages: %s", err)
		WriteError(w, r, err)
		return
	}

	k := cache.Key("application", projectKey, "*")
	cache.DeleteAll(k)
	cache.Delete(cache.Key("pipeline", projectKey, pipelineKey))

	WriteJSON(w, r, pipelineData, http.StatusOK)
}
<commit_msg>fix: return pipeline + list of stages<commit_after>package main

import (
	"database/sql"
	"io/ioutil"
	"net/http"
	"strconv"

	"github.com/gorilla/mux"

	"github.com/ovh/cds/engine/api/cache"
	"github.com/ovh/cds/engine/api/context"
	"github.com/ovh/cds/engine/api/pipeline"
	"github.com/ovh/cds/engine/log"
	"github.com/ovh/cds/sdk"
)

func addStageHandler(w http.ResponseWriter, r *http.Request, db *sql.DB, c *context.Context) {

	// Get project name in URL
	vars := mux.Vars(r)
	projectKey := vars["key"]
	pipelineKey := vars["permPipelineKey"]

	// Get body
	data, err := ioutil.ReadAll(r.Body)
	if err != nil {
		log.Warning("addStageHandler> cannot read body: %s", err)
		WriteError(w, r, sdk.ErrWrongRequest)
		return
	}

	stageData, err := sdk.NewStage("").FromJSON(data)
	if err != nil {
		log.Warning("addStageHandler> cannot unmarshal body: %s", err)
		WriteError(w, r, sdk.ErrWrongRequest)
		return
	}

	// Check if pipeline exist
	pipelineData, err := pipeline.LoadPipeline(db, projectKey, pipelineKey, false)
	if err != nil {
		WriteError(w, r, err)
		return
	}

	if err := pipeline.LoadPipelineStage(db, pipelineData); err != nil {
		log.Warning("addStageHandler> Cannot load pipeline stages: %s", err)
		WriteError(w, r, err)
		return
	}

	stageData.BuildOrder = len(pipelineData.Stages) + 1
	stageData.PipelineID = pipelineData.ID

	tx, err := db.Begin()
	if err != nil {
		log.Warning("addStageHandler> Cannot start transaction: %s", err)
		WriteError(w, r, err)
	}
	defer tx.Rollback()

	if err := pipeline.InsertStage(db, stageData); err != nil {
		log.Warning("addStageHandler> Cannot insert stage: %s", err)
		WriteError(w, r, err)
		return
	}

	if err := pipeline.UpdatePipelineLastModified(tx, pipelineData); err != nil {
		log.Warning("addStageHandler> Cannot update pipeline last modified date: %s", err)
		WriteError(w, r, err)
		return
	}

	if err := tx.Commit(); err != nil {
		log.Warning("addStageHandler> Cannot commit transaction: %s", err)
		WriteError(w, r, err)
	}

	if err := pipeline.LoadPipelineStage(db, pipelineData); err != nil {
		log.Warning("addStageHandler> Cannot load pipeline stages: %s", err)
		WriteError(w, r, err)
		return
	}

	k := cache.Key("application", projectKey, "*")
	cache.DeleteAll(k)
	cache.Delete(cache.Key("pipeline", projectKey, pipelineKey))

	WriteJSON(w, r, pipelineData, http.StatusCreated)
}

func getStageHandler(w http.ResponseWriter, r *http.Request, db *sql.DB, c *context.Context) {
	// Get project name in URL
	vars := mux.Vars(r)
	projectKey := vars["key"]
	pipelineKey := vars["permPipelineKey"]
	stageIDString := vars["stageID"]

	stageID, err := strconv.ParseInt(stageIDString, 10, 60)
	if err != nil {
		log.Warning("getStageHandler> Stage ID must be an int: %s", err)
		WriteError(w, r, sdk.ErrWrongRequest)
		return
	}

	// Check if pipeline exist
	pipelineData, err := pipeline.LoadPipeline(db, projectKey, pipelineKey, false)
	if err != nil {
		WriteError(w, r, err)
		return
	}

	s, err := pipeline.LoadStage(db, pipelineData.ID, stageID)
	if err != nil {
		WriteError(w, r, err)
		return
	}

	WriteJSON(w, r, s, http.StatusOK)
}

func moveStageHandler(w http.ResponseWriter, r *http.Request, db *sql.DB, c *context.Context) {

	// Get project name in URL
	vars := mux.Vars(r)
	projectKey := vars["key"]
	pipelineKey := vars["permPipelineKey"]

	// Get body
	data, err := ioutil.ReadAll(r.Body)
	if err != nil {
		log.Warning("moveStageHandler> cannot read body: %s", err)
		WriteError(w, r, sdk.ErrWrongRequest)
		return
	}

	// get stage to move
	stageData, err := sdk.NewStage("").FromJSON(data)
	if err != nil {
		log.Warning("moveStageHandler> Cannot unmarshal body: %s", err)
		WriteError(w, r, sdk.ErrWrongRequest)
		return
	}

	if stageData.BuildOrder < 1 {
		log.Warning("moveStageHandler> Build Order must be greater than 0")
		WriteError(w, r, sdk.ErrWrongRequest)
	}

	// Check if pipeline exist
	pipelineData, err := pipeline.LoadPipeline(db, projectKey, pipelineKey, false)
	if err != nil {
		WriteError(w, r, err)
		return
	}

	// count stage for this pipeline
	nbStage, err := pipeline.CountStageByPipelineID(db, pipelineData.ID)
	if err != nil {
		log.Warning("moveStageHandler> Cannot count stage for pipeline %s : %s", pipelineData.Name, err)
		WriteError(w, r, err)
		return
	}

	if stageData.BuildOrder <= nbStage {
		// check if stage exist
		s, err := pipeline.LoadStage(db, pipelineData.ID, stageData.ID)
		if err != nil {
			log.Warning("moveStageHandler> Cannot load stage: %s", err)
			WriteError(w, r, err)
			return
		}

		if err := pipeline.MoveStage(db, s, stageData.BuildOrder, pipelineData); err != nil {
			log.Warning("moveStageHandler> Cannot move stage: %s", err)
			WriteError(w, r, err)
			return
		}
	}

	if err := pipeline.LoadPipelineStage(db, pipelineData); err != nil {
		log.Warning("moveStageHandler> Cannot load stages: %s", err)
		WriteError(w, r, err)
		return
	}

	k := cache.Key("application", projectKey, "*")
	cache.DeleteAll(k)
	cache.Delete(cache.Key("pipeline", projectKey, pipelineKey))

	WriteJSON(w, r, pipelineData, http.StatusOK)
}

func updateStageHandler(w http.ResponseWriter, r *http.Request, db *sql.DB, c *context.Context) {

	// Get project name in URL
	vars := mux.Vars(r)
	projectKey := vars["key"]
	pipelineKey := vars["permPipelineKey"]
	stageIDString := vars["stageID"]

	// Get body
	data, err := ioutil.ReadAll(r.Body)
	if err != nil {
		log.Warning("addStageHandler> cannot read body: %s", err)
		WriteError(w, r, sdk.ErrWrongRequest)
		return
	}

	stageData, err := sdk.NewStage("").FromJSON(data)
	if err != nil {
		log.Warning("addStageHandler> Cannot unmarshal body: %s", err)
		WriteError(w, r, sdk.ErrWrongRequest)
		return
	}

	stageID, err := strconv.ParseInt(stageIDString, 10, 60)
	if err != nil {
		log.Warning("addStageHandler> Stage ID must be an int: %s", err)
		WriteError(w, r, sdk.ErrInvalidID)
		return
	}
	if stageID != stageData.ID {
		log.Warning("addStageHandler> Stage ID doest not match")
		WriteError(w, r, sdk.ErrInvalidID)
		return
	}

	// Check if pipeline exist
	pipelineData, err := pipeline.LoadPipeline(db, projectKey, pipelineKey, false)
	if err != nil {
		WriteError(w, r, err)
		return
	}

	// check if stage exist
	s, err := pipeline.LoadStage(db, pipelineData.ID, stageData.ID)
	if err != nil {
		log.Warning("addStageHandler> Cannot Load stage: %s", err)
		WriteError(w, r, err)
		return
	}
	stageData.ID = s.ID

	tx, err := db.Begin()
	if err != nil {
		log.Warning("addStageHandler> Cannot start transaction: %s", err)
		WriteError(w, r, err)
		return
	}
	defer tx.Rollback()

	if err := pipeline.UpdateStage(tx, stageData); err != nil {
		log.Warning("addStageHandler> Cannot update stage: %s", err)
		WriteError(w, r, err)
		return
	}

	if err := pipeline.UpdatePipelineLastModified(tx, pipelineData); err != nil {
		log.Warning("addStageHandler> Cannot update pipeline last_modified: %s", err)
		WriteError(w, r, err)
		return
	}

	err = tx.Commit()
	if err != nil {
		log.Warning("addStageHandler> Cannot commit transaction: %s", err)
		WriteError(w, r, err)
		return
	}

	if err := pipeline.LoadPipelineStage(db, pipelineData); err != nil {
		log.Warning("addStageHandler> Cannot load stages: %s", err)
		WriteError(w, r, err)
		return
	}

	k := cache.Key("application", projectKey, "*")
	cache.DeleteAll(k)
	cache.Delete(cache.Key("pipeline", projectKey, pipelineKey))

	WriteJSON(w, r, pipelineData, http.StatusOK)
}

func deleteStageHandler(w http.ResponseWriter, r *http.Request, db *sql.DB, c *context.Context) {

	// Get project name in URL
	vars := mux.Vars(r)
	projectKey := vars["key"]
	pipelineKey := vars["permPipelineKey"]
	stageIDString := vars["stageID"]

	// Check if pipeline exist
	pipelineData, err := pipeline.LoadPipeline(db, projectKey, pipelineKey, false)
	if err != nil {
		log.Warning("deleteStageHandler> Cannot load pipeline %s: %s", pipelineKey, err)
		WriteError(w, r, err)
		return
	}

	stageID, err := strconv.ParseInt(stageIDString, 10, 60)
	if err != nil {
		log.Warning("deleteStageHandler> Stage ID must be an int: %s", err)
		WriteError(w, r, sdk.ErrInvalidID)
		return
	}

	// check if stage exist
	s, err := pipeline.LoadStage(db, pipelineData.ID, stageID)
	if err != nil {
		log.Warning("deleteStageHandler> Cannot Load stage: %s", err)
		WriteError(w, r, err)
		return
	}

	tx, err := db.Begin()
	if err != nil {
		log.Warning("deleteStageHandler> Cannot start transaction: %s", err)
		WriteError(w, r, err)
		return
	}
	defer tx.Rollback()

	if err := pipeline.DeleteStageByID(tx, s, c.User.ID); err != nil {
		log.Warning("deleteStageHandler> Cannot Delete stage: %s", err)
		WriteError(w, r, err)
		return
	}

	if err := pipeline.UpdatePipelineLastModified(tx, pipelineData); err != nil {
		log.Warning("deleteStageHandler> Cannot Update pipeline last_modified: %s", err)
		WriteError(w, r, err)
		return
	}

	if err := tx.Commit(); err != nil {
		log.Warning("deleteStageHandler> Cannot commit transaction: %s", err)
		WriteError(w, r, err)
		return
	}

	if err := pipeline.LoadPipelineStage(db, pipelineData); err != nil {
		log.Warning("deleteStageHandler> Cannot load stages: %s", err)
		WriteError(w, r, err)
		return
	}

	k := cache.Key("application", projectKey, "*")
	cache.DeleteAll(k)
	cache.Delete(cache.Key("pipeline", projectKey, pipelineKey))

	WriteJSON(w, r, pipelineData, http.StatusOK)
}
<|endoftext|>


<commit_before>// Copyright 2013 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package all

// Register all the available providers.
import (
	_ "launchpad.net/juju-core/environs/ec2"
	_ "launchpad.net/juju-core/environs/maas"
	_ "launchpad.net/juju-core/environs/openstack"
)
<commit_msg>Add local provider to all list.<commit_after>// Copyright 2013 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package all

// Register all the available providers.
import (
	_ "launchpad.net/juju-core/environs/ec2"
	_ "launchpad.net/juju-core/environs/local"
	_ "launchpad.net/juju-core/environs/maas"
	_ "launchpad.net/juju-core/environs/openstack"
)
<|endoftext|>


<commit_before>package main

import (
	"crypto/rand"
	"encoding/base64"
	"encoding/hex"
	"encoding/json"
	"encoding/xml"
	"flag"
	"fmt"
	"io"
	"log"
	"os"
	"path/filepath"
	"strings"

	"github.com/agl/xmpp"
	"github.com/jroimartin/gocui"
	"github.com/rakoo/goax"
)

var (
	privIdentity [32]byte

	xmppClient *xmpp.Conn

	// contact type indexed by jid
	contacts map[string]*contact
)

var (
	configPath = flag.String("config", filepath.Join(os.Getenv("HOME"), ".config", "goax", "config.json"), "The path to config file")
)

type axoParams struct {
	Identity []byte
	Dh       []byte
	Dh1      []byte
}

type contact struct {
	ratchet *goax.Ratchet
	jid     string
	status  string
}

func (c *contact) HasAxo() bool {
	if c.ratchet == nil {
		return false
	}

	_, err := c.ratchet.GetKeyExchangeMaterial()
	// if err != nil, ratchet is ready
	return err != nil
}

func (c *contact) String() string {
	return c.jid
}

// Convert a xmpp status ("away", "dnd") into a status, defaulting to
// "available"
func statusFromStatus(xstatus string) string {
	if xstatus == "" {
		return "available"
	}
	return xstatus
}

func main() {
	flag.Parse()
	var err error
	xmppClient, err = getXmppClient(*configPath)
	if err != nil {
		log.Fatal(err)
	}

	io.ReadFull(rand.Reader, privIdentity[:])

	// The ui
	g := gocui.NewGui()
	if err := g.Init(); err != nil {
		log.Panicln(err)
	}
	defer g.Close()
	g.SetLayout(layout)
	if err := keybindings(g); err != nil {
		log.Panicln(err)
	}
	g.SelBgColor = gocui.ColorGreen
	g.SelFgColor = gocui.ColorBlack
	g.ShowCursor = true

	go func() {
		for {
			st, err := xmppClient.Next()
			if err != nil {
				debugf(g, "! Error at next stanza: %s\n", err)
				continue
			}

			switch v := st.Value.(type) {
			case *xmpp.ClientPresence:
				if len(contacts) == 0 {
					contacts = make(map[string]*contact)
				}

				var wantNewRatchet bool
				c, ok := contacts[v.From]
				if ok {
					if v.Type == "unavailable" {
						log.Printf("%s disconnected\n", v.From)
						delete(contacts, v.From)
						setContacts(g, contacts)
					} else if c.status != statusFromStatus(v.Status) {
						c.status = statusFromStatus(v.Status)
						wantNewRatchet = true
					}
				} else if v.Type != "unavailable" {
					contacts[v.From] = &contact{
						jid:    v.From,
						status: statusFromStatus(v.Status),
					}
					setContacts(g, contacts)
					wantNewRatchet = true
				}

				if wantNewRatchet {
					go queryAxo(g, v.From)
				}
			case *xmpp.ClientIQ:
				var q axoQuery
				err := xml.Unmarshal(v.Query, &q)
				if err != nil {
					debugf(g, "! Not an axolotl query: %s\n", string(v.Query))
					continue
				}

				c, ok := contacts[v.From]
				if !ok {
					continue
				}
				if c.ratchet == nil {
					c.ratchet = goax.New(rand.Reader, privIdentity)
				}
				kx, err := c.ratchet.GetKeyExchangeMaterial()
				if err != nil {
					continue
				}
				resp := axoQuery{
					Identity: hex.EncodeToString(kx.IdentityPublic[:]),
					Dh:       hex.EncodeToString(kx.Dh[:]),
					Dh1:      hex.EncodeToString(kx.Dh1[:]),
				}
				xmppClient.SendIQReply(v.From, "result", v.Id, resp)
			case *xmpp.ClientMessage:
				c, ok := contacts[v.From]
				if !ok || !c.HasAxo() {
					continue
				}

				raw, err := base64.StdEncoding.DecodeString(v.Body)
				if err != nil {
					debugf(g, "! Couldn't base64-decode: %s\n", err)
					continue
				}
				decrypted, err := c.ratchet.Decrypt(raw)
				if err != nil {
					debugf(g, "! Couldn't decrypt message: %s\n", err)
					continue
				}
				displayTimestamped(g, v.From, string(decrypted))
			default:
				debugf(g, "! Got stanza: %v\n", st.Name)
			}
		}
	}()

	err = g.MainLoop()
	if err != nil && err != gocui.ErrorQuit {
		log.Panicln(err)
	}
}

func sendMessage(to, msg string) error {
	contact, ok := contacts[to]
	if !ok {
		return nil
	}
	encrypted := contact.ratchet.Encrypt([]byte(msg))
	based := base64.StdEncoding.EncodeToString(encrypted)
	xmppClient.Send(to, based)

	return nil
}

type axoQuery struct {
	XMLName  xml.Name `xml:"axolotl"`
	Identity string   `xml:"identity,omitempty"`
	Dh       string   `xml:"dh,omitempty"`
	Dh1      string   `xml:"dh1,omitempty"`
	Nonce    string   `xml:"nonce,omitempty"`
}

func queryAxo(g *gocui.Gui, to string) error {
	c, ok := contacts[to]
	if !ok {
		return nil
	}
	c.ratchet = goax.New(rand.Reader, privIdentity)

	resp, _, err := xmppClient.SendIQ(to, "get", axoQuery{})
	if err != nil {
		debugf(g, "! Couldn't query axolotl parameters for %s: %s", to, err)
	}
	response := <-resp
	switch v := response.Value.(type) {
	case *xmpp.ClientIQ:
		if v.Error.Type == "cancel" {
			return nil
		}

		c, ok := contacts[v.From]
		if !ok {
			return nil
		}

		var q axoQuery
		err := xml.Unmarshal(v.Query, &q)
		if err != nil {
			debugf(g, "! Not an axolotl query: %s\n", string(v.Query))
			return nil
		}

		id, err := hex.DecodeString(q.Identity)
		if err != nil {
			return err
		}
		dh, err := hex.DecodeString(q.Dh)
		if err != nil {
			return err
		}
		dh1, err := hex.DecodeString(q.Dh1)
		if err != nil {
			return err
		}

		remoteKx := &goax.KeyExchange{}
		copy(remoteKx.IdentityPublic[:], id)
		copy(remoteKx.Dh[:], dh)
		copy(remoteKx.Dh1[:], dh1)

		err = c.ratchet.CompleteKeyExchange(*remoteKx)
		if err != nil {
			debug(g, err.Error())
			return nil
		}
		setContacts(g, contacts)
	}
	return nil
}

type config struct {
	Jid                     string `json:"jid"`
	Password                string `json:"password"`
	ServerCertificateSHA256 string
}

func getXmppClient(configPath string) (*xmpp.Conn, error) {
	// The xmpp connection
	configFile, err := os.Open(configPath)
	if err != nil {
		return nil, fmt.Errorf("Couldn't open config file: %s", err)
	}

	var conf config
	err = json.NewDecoder(configFile).Decode(&conf)
	if err != nil {
		return nil, fmt.Errorf("Couldn't decode json config: %s", err)
	}

	parts := strings.SplitN(conf.Jid, "@", 2)
	if len(parts) != 2 {
		return nil, fmt.Errorf("xmpp: invalid username (want user@domain): %s" + conf.Jid)
	}
	user := parts[0]
	domain := parts[1]

	host, port, err := xmpp.Resolve(domain)
	if err != nil {
		return nil, fmt.Errorf("Failed to resolve xmpp host for domain %s: %s", domain, err)
	}
	addr := fmt.Sprintf("%s:%d", host, port)

	rawCert, err := hex.DecodeString(conf.ServerCertificateSHA256)
	if err != nil {
		return nil, fmt.Errorf("Bad server certificate : %s", err)
	}

	logfile, err := os.Create("log")
	cfg := &xmpp.Config{
		InLog: logfile,
		Private: true,
		ServerCertificateSHA256: rawCert,
	}

	xmppClient, err := xmpp.Dial(addr, user, domain, conf.Password, cfg)
	if err != nil {
		return nil, fmt.Errorf("Couldn't connect to server: %s", err)
	}
	xmppClient.SignalPresence("alive")

	return xmppClient, nil
}
<commit_msg>Use our own fork of xmpp<commit_after>package main

import (
	"crypto/rand"
	"encoding/base64"
	"encoding/hex"
	"encoding/json"
	"encoding/xml"
	"flag"
	"fmt"
	"io"
	"log"
	"os"
	"path/filepath"
	"strings"

	"github.com/jroimartin/gocui"
	"github.com/rakoo/goax"
	"github.com/rakoo/xmpp"
)

var (
	privIdentity [32]byte

	xmppClient *xmpp.Conn

	// contact type indexed by jid
	contacts map[string]*contact

	fullJid string
)

var (
	configPath = flag.String("config", filepath.Join(os.Getenv("HOME"), ".config", "goax", "config.json"), "The path to config file")
)

type axoParams struct {
	Identity []byte
	Dh       []byte
	Dh1      []byte
}

type contact struct {
	ratchet *goax.Ratchet
	jid     string
	status  string
}

func (c *contact) HasAxo() bool {
	if c.ratchet == nil {
		return false
	}

	_, err := c.ratchet.GetKeyExchangeMaterial()
	// if err != nil, ratchet is ready
	return err != nil
}

func (c *contact) String() string {
	return c.jid
}

// Convert a xmpp status ("away", "dnd") into a status, defaulting to
// "available"
func statusFromStatus(xstatus string) string {
	if xstatus == "" {
		return "available"
	}
	return xstatus
}

func main() {
	flag.Parse()
	var err error
	xmppClient, err = getXmppClient(*configPath)
	if err != nil {
		log.Fatal(err)
	}

	io.ReadFull(rand.Reader, privIdentity[:])

	// The ui
	g := gocui.NewGui()
	if err := g.Init(); err != nil {
		log.Panicln(err)
	}
	defer g.Close()
	g.SetLayout(layout)
	if err := keybindings(g); err != nil {
		log.Panicln(err)
	}
	g.SelBgColor = gocui.ColorGreen
	g.SelFgColor = gocui.ColorBlack
	g.ShowCursor = true

	go func() {
		for {
			st, err := xmppClient.Next()
			if err != nil {
				debugf(g, "! Error at next stanza: %s\n", err)
				continue
			}

			switch v := st.Value.(type) {
			case *xmpp.ClientPresence:
				if v.From == fullJid || v.Type == "error" {
					continue
				}
				if len(contacts) == 0 {
					contacts = make(map[string]*contact)
				}

				var wantNewRatchet bool
				c, ok := contacts[v.From]
				if ok {
					if v.Type == "unavailable" {
						log.Printf("%s disconnected\n", v.From)
						delete(contacts, v.From)
						setContacts(g, contacts)
					} else if c.status != statusFromStatus(v.Status) {
						c.status = statusFromStatus(v.Status)
						wantNewRatchet = true
					}
				} else if v.Type != "unavailable" {
					contacts[v.From] = &contact{
						jid:    v.From,
						status: statusFromStatus(v.Status),
					}
					setContacts(g, contacts)
					wantNewRatchet = true
				}

				if wantNewRatchet {
					go queryAxo(g, v.From)
				}
			case *xmpp.ClientIQ:
				var q axoQuery
				err := xml.Unmarshal(v.Query, &q)
				if err != nil {
					debugf(g, "! Not an axolotl query: %s\n", string(v.Query))
					continue
				}

				c, ok := contacts[v.From]
				if !ok {
					continue
				}
				if c.ratchet == nil {
					c.ratchet = goax.New(rand.Reader, privIdentity)
				}
				kx, err := c.ratchet.GetKeyExchangeMaterial()
				if err != nil {
					continue
				}
				resp := axoQuery{
					Identity: hex.EncodeToString(kx.IdentityPublic[:]),
					Dh:       hex.EncodeToString(kx.Dh[:]),
					Dh1:      hex.EncodeToString(kx.Dh1[:]),
				}
				xmppClient.SendIQReply(v.From, "result", v.Id, resp)
			case *xmpp.ClientMessage:
				c, ok := contacts[v.From]
				if !ok || !c.HasAxo() {
					continue
				}

				raw, err := base64.StdEncoding.DecodeString(v.Body)
				if err != nil {
					debugf(g, "! Couldn't base64-decode: %s\n", err)
					continue
				}
				decrypted, err := c.ratchet.Decrypt(raw)
				if err != nil {
					debugf(g, "! Couldn't decrypt message: %s\n", err)
					continue
				}
				displayTimestamped(g, v.From, string(decrypted))
			default:
				debugf(g, "! Got stanza: %v\n", st.Name)
			}
		}
	}()

	err = g.MainLoop()
	if err != nil && err != gocui.ErrorQuit {
		log.Panicln(err)
	}
}

func sendMessage(to, msg string) error {
	contact, ok := contacts[to]
	if !ok {
		return nil
	}
	encrypted := contact.ratchet.Encrypt([]byte(msg))
	based := base64.StdEncoding.EncodeToString(encrypted)
	xmppClient.Send(to, based)

	return nil
}

type axoQuery struct {
	XMLName  xml.Name `xml:"axolotl"`
	Identity string   `xml:"identity,omitempty"`
	Dh       string   `xml:"dh,omitempty"`
	Dh1      string   `xml:"dh1,omitempty"`
	Nonce    string   `xml:"nonce,omitempty"`
}

func queryAxo(g *gocui.Gui, to string) error {
	c, ok := contacts[to]
	if !ok {
		return nil
	}
	c.ratchet = goax.New(rand.Reader, privIdentity)

	resp, _, err := xmppClient.SendIQ(to, "get", axoQuery{})
	if err != nil {
		debugf(g, "! Couldn't query axolotl parameters for %s: %s", to, err)
	}
	response := <-resp
	switch v := response.Value.(type) {
	case *xmpp.ClientIQ:
		if v.Error.Type == "cancel" {
			return nil
		}

		c, ok := contacts[v.From]
		if !ok {
			return nil
		}

		var q axoQuery
		err := xml.Unmarshal(v.Query, &q)
		if err != nil {
			debugf(g, "! Not an axolotl query: %s\n", string(v.Query))
			return nil
		}

		id, err := hex.DecodeString(q.Identity)
		if err != nil {
			return err
		}
		dh, err := hex.DecodeString(q.Dh)
		if err != nil {
			return err
		}
		dh1, err := hex.DecodeString(q.Dh1)
		if err != nil {
			return err
		}

		remoteKx := &goax.KeyExchange{}
		copy(remoteKx.IdentityPublic[:], id)
		copy(remoteKx.Dh[:], dh)
		copy(remoteKx.Dh1[:], dh1)

		err = c.ratchet.CompleteKeyExchange(*remoteKx)
		if err != nil {
			debug(g, err.Error())
			return nil
		}
		setContacts(g, contacts)
	}
	return nil
}

type config struct {
	Jid                     string `json:"jid"`
	Password                string `json:"password"`
	ServerCertificateSHA256 string
}

func getXmppClient(configPath string) (*xmpp.Conn, error) {
	// The xmpp connection
	configFile, err := os.Open(configPath)
	if err != nil {
		return nil, fmt.Errorf("Couldn't open config file: %s", err)
	}

	var conf config
	err = json.NewDecoder(configFile).Decode(&conf)
	if err != nil {
		return nil, fmt.Errorf("Couldn't decode json config: %s", err)
	}

	parts := strings.SplitN(conf.Jid, "@", 2)
	if len(parts) != 2 {
		return nil, fmt.Errorf("xmpp: invalid username (want user@domain): %s" + conf.Jid)
	}
	user := parts[0]
	domain := parts[1]

	host, port, err := xmpp.Resolve(domain)
	if err != nil {
		return nil, fmt.Errorf("Failed to resolve xmpp host for domain %s: %s", domain, err)
	}
	addr := fmt.Sprintf("%s:%d", host, port)

	rawCert, err := hex.DecodeString(conf.ServerCertificateSHA256)
	if err != nil {
		return nil, fmt.Errorf("Bad server certificate : %s", err)
	}

	logfile, err := os.Create("log")
	cfg := &xmpp.Config{
		Private: true,
		OutLog:  logfile,
		InLog:   logfile,
		ServerCertificateSHA256: rawCert,
	}

	xmppClient, err := xmpp.Dial(addr, user, domain, conf.Password, cfg)
	if err != nil {
		return nil, fmt.Errorf("Couldn't connect to server: %s", err)
	}
	xmppClient.SignalPresence("alive")

	fullJid = xmppClient.Jid
	return xmppClient, nil
}
<|endoftext|>


<commit_before>//
//  Synchronized publisher.
//
//  This diverts from the C example by introducing time delays.
//  Without these delays, the subscribers won't catch the END message.
//

package main

import (
	zmq "github.com/pebbe/zmq2"

	"fmt"
	"time"
)

const (
	//  We wait for 10 subscribers
	SUBSCRIBERS_EXPECTED = 10
)

func main() {

	//  Socket to talk to clients
	publisher, _ := zmq.NewSocket(zmq.PUB)
	defer publisher.Close()
	publisher.Bind("tcp://*:5561")

	//  Socket to receive signals
	syncservice, _ := zmq.NewSocket(zmq.REP)
	defer syncservice.Close()
	syncservice.Bind("tcp://*:5562")

	//  Get synchronization from subscribers
	fmt.Println("Waiting for subscribers")
	for subscribers := 0; subscribers < SUBSCRIBERS_EXPECTED; subscribers++ {
		//  - wait for synchronization request
		syncservice.Recv(0)
		//  - send synchronization reply
		syncservice.Send("", 0)
	}
	//  Now broadcast exactly 1M updates followed by END
	fmt.Println("Broadcasting messages")
	for update_nbr := 0; update_nbr < 1000000; update_nbr++ {
		publisher.Send("Rhubarb", 0)
		// subscribers don't get all messages if publisher is too fast
		// a one microsecond pause may still be too short
		time.Sleep(time.Microsecond)
	}

	// a longer pause ensures subscribers are ready to receive this
	time.Sleep(time.Second)
	publisher.Send("END", 0)

	// what's another second?
	time.Sleep(time.Second)
}
<commit_msg>examples/syncpub.go: using high water mark instead of time delay<commit_after>//
//  Synchronized publisher.
//

package main

import (
	zmq "github.com/pebbe/zmq2"

	"fmt"
)

const (
	//  We wait for 10 subscribers
	SUBSCRIBERS_EXPECTED = 10
)

func main() {

	ctx, _ := zmq.NewContext(1)
	defer ctx.Term()

	//  Socket to talk to clients
	publisher, _ := ctx.NewSocket(zmq.PUB)
	defer publisher.Close()
	publisher.SetHwm(1100000)
	publisher.Bind("tcp://*:5561")

	//  Socket to receive signals
	syncservice, _ := ctx.NewSocket(zmq.REP)
	defer syncservice.Close()
	syncservice.Bind("tcp://*:5562")

	//  Get synchronization from subscribers
	fmt.Println("Waiting for subscribers")
	for subscribers := 0; subscribers < SUBSCRIBERS_EXPECTED; subscribers++ {
		//  - wait for synchronization request
		syncservice.Recv(0)
		//  - send synchronization reply
		syncservice.Send("", 0)
	}
	//  Now broadcast exactly 1M updates followed by END
	fmt.Println("Broadcasting messages")
	for update_nbr := 0; update_nbr < 1000000; update_nbr++ {
		publisher.Send("Rhubarb", 0)
	}

	publisher.Send("END", 0)

}
<|endoftext|>


<commit_before>package backoff

import (
	"testing"
	"time"
)

func TestNextExponentialBackoff(t *testing.T) {
	e := Exponential()
	e.Interval = 1 * time.Second
	e.MaxRetries = 5

	expectedRetries := []int{1, 2, 3, 4, 5, 6, 7}
	expectedDelays := []time.Duration{1, 3, 7, 15, 15, 15, 15}
	for i, v := range expectedDelays {
		expectedDelays[i] = v * time.Second
	}

	for i, expected := range expectedRetries {
		e.Next()
		assertEquals(t, expected, e.Retries)
		assertEquals(t, expectedDelays[i], e.Delay)
	}
}

func TestResetExponential(t *testing.T) {
	e := Exponential()
	e.Interval = 1 * time.Second
	e.MaxRetries = 5

	e.Next()
	assertEquals(t, e.Retries, 1)
	assertEquals(t, e.Delay, time.Duration(1*time.Second))
	e.Reset()
	assertEquals(t, e.Retries, 0)
	assertEquals(t, e.Delay, time.Duration(0*time.Second))
}

func assertEquals(t *testing.T, expected interface{}, actual interface{}) {
	if expected != actual {
		t.Errorf("error. got %d, expected: %d", actual, expected)
	}
}
<commit_msg>Add retry test.<commit_after>package backoff

import (
	"testing"
	"time"
  "errors"
)

func TestNextExponentialBackoff(t *testing.T) {
	e := Exponential()
	e.Interval = 1 * time.Second
	e.MaxRetries = 5

	expectedRetries := []int{1, 2, 3, 4, 5, 6, 7}
	expectedDelays := []time.Duration{1, 3, 7, 15, 15, 15, 15}
	for i, v := range expectedDelays {
		expectedDelays[i] = v * time.Second
	}

	for i, expected := range expectedRetries {
		e.Next()
		assertEquals(t, expected, e.Retries)
		assertEquals(t, expectedDelays[i], e.Delay)
	}
}

func TestRetryExponential(t *testing.T) {
  e := Exponential()
  e.Interval = 1 * time.Millisecond
  e.MaxRetries = 5

  retries := 0

  test := func () error {
    retries++
    return errors.New("an error occurred")
  }
  e.Retry(test)

  if retries != e.Retries {
    t.Errorf("retries count does not match e.Retries: got %d, expected %d", retries, e.Retries)
  }

  if e.Retries > e.MaxRetries {
    t.Errorf("overflow: retries %d greater than maximum retries %d", e.Retries, e.MaxRetries)
  }

  e.Reset()
  retries = 0

  test = func () error {
    retries++
    return nil
  }

  err := e.Retry(test)

  if e.Retries > 0 && err != nil {
    t.Errorf("failure in retry logic. expected success but got a failure: %+v", err)
  }
}

func TestResetExponential(t *testing.T) {
	e := Exponential()
	e.Interval = 1 * time.Second
	e.MaxRetries = 5

	e.Next()
	assertEquals(t, e.Retries, 1)
	assertEquals(t, e.Delay, time.Duration(1*time.Second))
	e.Reset()
	assertEquals(t, e.Retries, 0)
	assertEquals(t, e.Delay, time.Duration(0*time.Second))
}

func assertEquals(t *testing.T, expected interface{}, actual interface{}) {
	if expected != actual {
		t.Errorf("error. got %d, expected: %d", actual, expected)
	}
}
<|endoftext|>


<commit_before>package f5

import (
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"net/http"
	"strconv"
	"strings"
)

// Paths for file upload.
const (
	PathUploadImage = "/mgmt/cm/autodeploy/software-image-uploads"
	PathUploadFile  = "/mgmt/shared/file-transfer/uploads"
	PathUploadUCS   = "mgmt/shared/file-transfer/ucs-uploads"

	// For backward compatibility
	// DEPRECATED
	UploadRESTPath = PathUploadFile
)

// Paths for file download.
const (
	PathDownloadUCS   = "/mgmt/shared/file-transfer/ucs-downloads"
	PathDownloadImage = "/mgmt/cm/autodeploy/software-image-downloads"
)

// MaxChunkSize is the maximum chunk size allowed by the iControl REST
const MaxChunkSize = 1048576

// DownloadUCS downloads an UCS file and writes its content to w.
func (c *Client) DownloadUCS(w io.Writer, filename string) (n int64, err error) {
	// BigIP 12.x.x only support download requests with a Content-Range header,
	// thus, it is required to know the size of the file to download beforehand.
	//
	// BigIP 13.x.x automatically download the first chunk and provide the
	// Content-Range header with all information in the response, which is far
	// more convenient. Unfortunately, we need to support BigIP 12 and as a
	// result, we need to first retrieve the UCS file size information.
	resp, err := c.SendRequest("GET", "/mgmt/tm/sys/ucs", nil)
	if err != nil {
		return 0, fmt.Errorf("cannot retrieve info for ucs file: %v", err)
	}
	defer resp.Body.Close()
	if err := c.ReadError(resp); err != nil {
		return 0, fmt.Errorf("cannot retrieve info for ucs file: %v", err)
	}

	// As far as I know, there is no direct way to fetch UCS file info for a
	// specific file and therefore we need to list all UCS files and search
	// for the one we want in the list.
	var ucsInfo struct {
		Items []struct {
			APIRawValues struct {
				Filename string `json:"filename"`
				FileSize string `json:"file_size"`
			} `json:"apiRawValues"`
		} `json:"items"`
	}
	dec := json.NewDecoder(resp.Body)
	if err := dec.Decode(&ucsInfo); err != nil {
		return 0, fmt.Errorf("cannot decode ucs file info: %v", err)
	}

	// File size is a raw string and we need to parse it in order to extract the
	// size as an integer.
	var rawFileSize string
	for _, item := range ucsInfo.Items {
		if strings.HasSuffix(item.APIRawValues.Filename, filename) {
			rawFileSize = strings.TrimSuffix(item.APIRawValues.FileSize, " (in bytes)")
			break
		}
	}
	if rawFileSize == "" {
		return 0, errors.New("ucs file does not exist")
	}
	fileSize, err := strconv.ParseInt(rawFileSize, 10, 64)
	if err != nil {
		return 0, fmt.Errorf("malformed file size in ucs file info: %v", err)
	}

	if n, err = c.download(w, PathDownloadUCS+"/"+filename, fileSize, MaxChunkSize); err != nil {
		return 0, fmt.Errorf("cannot download ucs file: %v", err)
	}
	return
}

// DownloadImage downloads BIG-IP images from the API and writes it to w.
//
// Download can take some time due to the size of the image files.
func (c *Client) DownloadImage(w io.Writer, filename string) (n int64, err error) {
	// This is necessary to get the filesize first using bash command in order
	// to support BIG-IP 12.x.x.
	out, err := c.Exec("wc -c /shared/images/" + filename)
	if err != nil {
		return 0, fmt.Errorf("cannot get file size: %v", err)
	}
	fileSizeStr := strings.TrimSpace(out.CommandResult)
	pos := strings.Index(fileSizeStr, " ")
	if pos != -1 {
		fileSizeStr = fileSizeStr[:pos]
	}
	fileSize, err := strconv.ParseInt(fileSizeStr, 10, 64)
	if err != nil {
		return 0, fmt.Errorf("cannot read file size: %v", err)
	}

	if n, err = c.download(w, PathDownloadImage+"/"+filename, fileSize, MaxChunkSize); err != nil {
		return 0, fmt.Errorf("cannot download image file: %v", err)
	}
	return
}

func (c *Client) download(w io.Writer, restPath string, filesize, chunkSize int64) (n int64, err error) {
	if filesize < chunkSize {
		chunkSize = filesize
	}
	return c.downloadByChunks(w, restPath, filesize, 0, chunkSize)
}

func (c *Client) downloadByChunks(w io.Writer, restPath string, filesize, offset, chunkSize int64) (n int64, err error) {
	req, err := c.MakeRequest("GET", restPath, nil)
	if err != nil {
		return 0, err
	}

	// Bound limit to filesize
	limit := offset + chunkSize - 1
	if limit >= filesize {
		limit = filesize - 1
	}

	req.Header.Set("Content-Range", fmt.Sprintf("%d-%d/%d", offset, limit, filesize))

	resp, err := c.Do(req)
	if err != nil {
		return 0, err
	}
	defer resp.Body.Close()

	if err := c.ReadError(resp); err != nil {
		return 0, err
	}

	if n, err = io.Copy(w, resp.Body); err != nil {
		return 0, err
	}

	if limit < filesize-1 {
		nn, err := c.downloadByChunks(w, restPath, filesize, offset+chunkSize, chunkSize)
		if err != nil {
			return 0, err
		}
		n += nn
	}

	return
}

// An UploadResponse holds the responses send by the BigIP API while uploading
// files.
type UploadResponse struct {
	RemainingByteCount int64          `json:"remainingByteCount"`
	UsedChunks         map[string]int `json:"usedChunks"`
	TotalByteCount     int64          `json:"totalByteCount"`
	LocalFilePath      string         `json:"localFilePath"`
	TemporaryFilePath  string         `json:"temporaryFilePath"`
	Generation         int64          `json:"generation"`
	LastUpdateMicros   int64          `json:"lastUpdateMicros"`
}

// UploadFile reads the content of a file from r and uploads it to the BigIP.
// The uploaded file will be named according to the provided filename.
//
// filesize must be the exact file of the file.
//
// The file is split into small chunk, therefore this method may send multiple
// request.
//
// This method returns the latest upload response received.
func (c *Client) UploadFile(r io.Reader, filename string, filesize int64) (*UploadResponse, error) {
	return c.upload(r, PathUploadFile, filename, filesize)
}

// UploadImage reads the content of an disk image from r and uploads it to the
// BigIP.
//
// The uploaded image will be named according to the provided filename.
//
// filesize must be the exact file of the file.
//
// The file is split into small chunk, therefore this method may send multiple
// request.
//
// This method returns the latest upload response received.
func (c *Client) UploadImage(r io.Reader, filename string, filesize int64) (*UploadResponse, error) {
	return c.upload(r, PathUploadImage, filename, filesize)
}

// UploadUCS reads the content of an UCS archive from r and uploads it to the
// BigIP.
//
// The uploaded UCS archive will be named according to the provided filename.
//
// filesize must be the exact file of the file.
//
// The file is split into small chunk, therefore this method may send multiple
// request.
//
// This method returns the latest upload response received.
func (c *Client) UploadUCS(r io.Reader, filename string, filesize int64) (*UploadResponse, error) {
	return c.upload(r, PathUploadUCS, filename, filesize)
}

func (c *Client) upload(r io.Reader, restPath, filename string, filesize int64) (*UploadResponse, error) {
	var uploadResp UploadResponse
	for bytesSent := int64(0); bytesSent < filesize; {
		var chunk int64
		if remainingBytes := filesize - bytesSent; remainingBytes >= 512*1024 {
			chunk = 512 * 1024
		} else {
			chunk = remainingBytes
		}

		req, err := c.makeUploadRequest(restPath+"/"+filename, io.LimitReader(r, chunk), bytesSent, chunk, filesize)
		if err != nil {
			return nil, err
		}
		resp, err := c.Do(req)
		if err != nil {
			return nil, err
		}
		if err := c.ReadError(resp); err != nil {
			resp.Body.Close()
			return nil, err
		}

		if filesize-bytesSent <= 512*1024 {
			dec := json.NewDecoder(resp.Body)
			if err := dec.Decode(&uploadResp); err != nil {
				resp.Body.Close()
				return nil, err
			}
		}

		resp.Body.Close()

		bytesSent += chunk
	}
	return &uploadResp, nil
}

// makeUploadRequest constructs a single upload request.
//
// restPath can be any of the Path* constants defined at the top of this file.
//
// The file to be uploaded is read from r and must not exceed 524288 bytes.
//
// off represents the number of bytes already sent while chunk is the size of
// chunk to be send in this request.
//
// filesize denotes the size of the entire file.
func (c *Client) makeUploadRequest(restPath string, r io.Reader, off, chunk, filesize int64) (*http.Request, error) {
	if chunk > 512*1024 {
		return nil, fmt.Errorf("chunk size greater than %d is not supported", 512*1024)
	}
	req, err := http.NewRequest("POST", c.makeURL(restPath), r)
	if err != nil {
		return nil, fmt.Errorf("failed to create F5 authenticated request: %v", err)
	}
	req.Header.Add("Accept", "application/json")
	req.Header.Set("Content-Range", fmt.Sprintf("%d-%d/%d", off, off+chunk-1, filesize))
	req.Header.Set("Content-Type", "application/octet-stream")
	if err := c.makeAuth(req); err != nil {
		return nil, err
	}
	return req, nil
}
<commit_msg>f5: add option to download UCS using SFTP<commit_after>package f5

import (
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"net/http"
	"net/url"
	"path/filepath"
	"strconv"
	"strings"

	"github.com/pkg/sftp"
	"golang.org/x/crypto/ssh"
)

// Paths for file upload.
const (
	PathUploadImage = "/mgmt/cm/autodeploy/software-image-uploads"
	PathUploadFile  = "/mgmt/shared/file-transfer/uploads"
	PathUploadUCS   = "mgmt/shared/file-transfer/ucs-uploads"

	// For backward compatibility
	// DEPRECATED
	UploadRESTPath = PathUploadFile
)

// Paths for file download.
const (
	PathDownloadUCS   = "/mgmt/shared/file-transfer/ucs-downloads"
	PathDownloadImage = "/mgmt/cm/autodeploy/software-image-downloads"
)

// MaxChunkSize is the maximum chunk size allowed by the iControl REST
const MaxChunkSize = 1048576

// FileTransferOptions contains SSH configuration for downloading UCS using
// SFTP.
type FileTransferOptions struct {
	UseSCP       bool
	ClientConfig ssh.ClientConfig
}

// FileTransferOption is a function type to set the transfer options.
type FileTransferOption func(*FileTransferOptions)

// WithSFTP sets the ssh configuration for file transfer.
func WithSFTP(config ssh.ClientConfig) FileTransferOption {
	return func(o *FileTransferOptions) {
		o.UseSCP = true
		o.ClientConfig = config
	}
}

// DownloadUCS downloads an UCS file and writes its content to w.
func (c *Client) DownloadUCS(w io.Writer, filename string, opts ...FileTransferOption) (n int64, err error) {
	// BigIP 12.x.x only support download requests with a Content-Range header,
	// thus, it is required to know the size of the file to download beforehand.
	//
	// BigIP 13.x.x automatically download the first chunk and provide the
	// Content-Range header with all information in the response, which is far
	// more convenient. Unfortunately, we need to support BigIP 12 and as a
	// result, we need to first retrieve the UCS file size information.
	options := FileTransferOptions{}
	for _, o := range opts {
		o(&options)
	}

	if options.UseSCP {
		return c.downloadUsingSSH(w, filename, options)
	}

	resp, err := c.SendRequest("GET", "/mgmt/tm/sys/ucs", nil)
	if err != nil {
		return 0, fmt.Errorf("cannot retrieve info for ucs file: %v", err)
	}
	defer resp.Body.Close()
	if err := c.ReadError(resp); err != nil {
		return 0, fmt.Errorf("cannot retrieve info for ucs file: %v", err)
	}

	// As far as I know, there is no direct way to fetch UCS file info for a
	// specific file and therefore we need to list all UCS files and search
	// for the one we want in the list.
	var ucsInfo struct {
		Items []struct {
			APIRawValues struct {
				Filename string `json:"filename"`
				FileSize string `json:"file_size"`
			} `json:"apiRawValues"`
		} `json:"items"`
	}
	dec := json.NewDecoder(resp.Body)
	if err := dec.Decode(&ucsInfo); err != nil {
		return 0, fmt.Errorf("cannot decode ucs file info: %v", err)
	}

	// File size is a raw string and we need to parse it in order to extract the
	// size as an integer.
	var rawFileSize string
	for _, item := range ucsInfo.Items {
		if strings.HasSuffix(item.APIRawValues.Filename, filename) {
			rawFileSize = strings.TrimSuffix(item.APIRawValues.FileSize, " (in bytes)")
			break
		}
	}
	if rawFileSize == "" {
		return 0, errors.New("ucs file does not exist")
	}
	fileSize, err := strconv.ParseInt(rawFileSize, 10, 64)
	if err != nil {
		return 0, fmt.Errorf("malformed file size in ucs file info: %v", err)
	}

	if n, err = c.download(w, PathDownloadUCS+"/"+filename, fileSize, MaxChunkSize); err != nil {
		return 0, fmt.Errorf("cannot download ucs file: %v", err)
	}
	return
}

func (c *Client) downloadUsingSSH(w io.Writer, filename string, opts FileTransferOptions) (int64, error) {

	path := filepath.Join("var", "local", "ucs", filename)

	parsedURL, err := url.Parse(c.baseURL)
	if err != nil {
		return 0, fmt.Errorf("downloadUsingSSH: cannot parse baseURL: %w", err)
	}

	conn, err := ssh.Dial("tcp", parsedURL.Hostname(), &opts.ClientConfig)
	if err != nil {
		return 0, fmt.Errorf("downloadUsingSSH: cannot connect via ssh: %w", err)
	}
	defer conn.Close()

	sftpClient, err := sftp.NewClient(conn)
	if err != nil {
		return 0, fmt.Errorf("downloadUsingSSH: cannot create sftp client: %w", err)
	}
	defer sftpClient.Close()

	file, err := sftpClient.Open(path)
	if err != nil {
		return 0, fmt.Errorf("downloadUsingSSH: cannot open file %q: %w", filename, err)
	}
	defer file.Close()

	return io.Copy(w, file)
}

// DownloadImage downloads BIG-IP images from the API and writes it to w.
//
// Download can take some time due to the size of the image files.
func (c *Client) DownloadImage(w io.Writer, filename string) (n int64, err error) {
	// This is necessary to get the filesize first using bash command in order
	// to support BIG-IP 12.x.x.
	out, err := c.Exec("wc -c /shared/images/" + filename)
	if err != nil {
		return 0, fmt.Errorf("cannot get file size: %v", err)
	}
	fileSizeStr := strings.TrimSpace(out.CommandResult)
	pos := strings.Index(fileSizeStr, " ")
	if pos != -1 {
		fileSizeStr = fileSizeStr[:pos]
	}
	fileSize, err := strconv.ParseInt(fileSizeStr, 10, 64)
	if err != nil {
		return 0, fmt.Errorf("cannot read file size: %v", err)
	}

	if n, err = c.download(w, PathDownloadImage+"/"+filename, fileSize, MaxChunkSize); err != nil {
		return 0, fmt.Errorf("cannot download image file: %v", err)
	}
	return
}

func (c *Client) download(w io.Writer, restPath string, filesize, chunkSize int64) (n int64, err error) {
	if filesize < chunkSize {
		chunkSize = filesize
	}
	return c.downloadByChunks(w, restPath, filesize, 0, chunkSize)
}

func (c *Client) downloadByChunks(w io.Writer, restPath string, filesize, offset, chunkSize int64) (n int64, err error) {
	req, err := c.MakeRequest("GET", restPath, nil)
	if err != nil {
		return 0, err
	}

	// Bound limit to filesize
	limit := offset + chunkSize - 1
	if limit >= filesize {
		limit = filesize - 1
	}

	req.Header.Set("Content-Range", fmt.Sprintf("%d-%d/%d", offset, limit, filesize))

	resp, err := c.Do(req)
	if err != nil {
		return 0, err
	}
	defer resp.Body.Close()

	if err := c.ReadError(resp); err != nil {
		return 0, err
	}

	if n, err = io.Copy(w, resp.Body); err != nil {
		return 0, err
	}

	if limit < filesize-1 {
		nn, err := c.downloadByChunks(w, restPath, filesize, offset+chunkSize, chunkSize)
		if err != nil {
			return 0, err
		}
		n += nn
	}

	return
}

// An UploadResponse holds the responses send by the BigIP API while uploading
// files.
type UploadResponse struct {
	RemainingByteCount int64          `json:"remainingByteCount"`
	UsedChunks         map[string]int `json:"usedChunks"`
	TotalByteCount     int64          `json:"totalByteCount"`
	LocalFilePath      string         `json:"localFilePath"`
	TemporaryFilePath  string         `json:"temporaryFilePath"`
	Generation         int64          `json:"generation"`
	LastUpdateMicros   int64          `json:"lastUpdateMicros"`
}

// UploadFile reads the content of a file from r and uploads it to the BigIP.
// The uploaded file will be named according to the provided filename.
//
// filesize must be the exact file of the file.
//
// The file is split into small chunk, therefore this method may send multiple
// request.
//
// This method returns the latest upload response received.
func (c *Client) UploadFile(r io.Reader, filename string, filesize int64) (*UploadResponse, error) {
	return c.upload(r, PathUploadFile, filename, filesize)
}

// UploadImage reads the content of an disk image from r and uploads it to the
// BigIP.
//
// The uploaded image will be named according to the provided filename.
//
// filesize must be the exact file of the file.
//
// The file is split into small chunk, therefore this method may send multiple
// request.
//
// This method returns the latest upload response received.
func (c *Client) UploadImage(r io.Reader, filename string, filesize int64) (*UploadResponse, error) {
	return c.upload(r, PathUploadImage, filename, filesize)
}

// UploadUCS reads the content of an UCS archive from r and uploads it to the
// BigIP.
//
// The uploaded UCS archive will be named according to the provided filename.
//
// filesize must be the exact file of the file.
//
// The file is split into small chunk, therefore this method may send multiple
// request.
//
// This method returns the latest upload response received.
func (c *Client) UploadUCS(r io.Reader, filename string, filesize int64) (*UploadResponse, error) {
	return c.upload(r, PathUploadUCS, filename, filesize)
}

func (c *Client) upload(r io.Reader, restPath, filename string, filesize int64) (*UploadResponse, error) {
	var uploadResp UploadResponse
	for bytesSent := int64(0); bytesSent < filesize; {
		var chunk int64
		if remainingBytes := filesize - bytesSent; remainingBytes >= 512*1024 {
			chunk = 512 * 1024
		} else {
			chunk = remainingBytes
		}

		req, err := c.makeUploadRequest(restPath+"/"+filename, io.LimitReader(r, chunk), bytesSent, chunk, filesize)
		if err != nil {
			return nil, err
		}
		resp, err := c.Do(req)
		if err != nil {
			return nil, err
		}
		if err := c.ReadError(resp); err != nil {
			resp.Body.Close()
			return nil, err
		}

		if filesize-bytesSent <= 512*1024 {
			dec := json.NewDecoder(resp.Body)
			if err := dec.Decode(&uploadResp); err != nil {
				resp.Body.Close()
				return nil, err
			}
		}

		resp.Body.Close()

		bytesSent += chunk
	}
	return &uploadResp, nil
}

// makeUploadRequest constructs a single upload request.
//
// restPath can be any of the Path* constants defined at the top of this file.
//
// The file to be uploaded is read from r and must not exceed 524288 bytes.
//
// off represents the number of bytes already sent while chunk is the size of
// chunk to be send in this request.
//
// filesize denotes the size of the entire file.
func (c *Client) makeUploadRequest(restPath string, r io.Reader, off, chunk, filesize int64) (*http.Request, error) {
	if chunk > 512*1024 {
		return nil, fmt.Errorf("chunk size greater than %d is not supported", 512*1024)
	}
	req, err := http.NewRequest("POST", c.makeURL(restPath), r)
	if err != nil {
		return nil, fmt.Errorf("failed to create F5 authenticated request: %v", err)
	}
	req.Header.Add("Accept", "application/json")
	req.Header.Set("Content-Range", fmt.Sprintf("%d-%d/%d", off, off+chunk-1, filesize))
	req.Header.Set("Content-Type", "application/octet-stream")
	if err := c.makeAuth(req); err != nil {
		return nil, err
	}
	return req, nil
}
<|endoftext|>


<commit_before>package zipfs

import (
	"archive/zip"
	"crypto/md5"
	"fmt"
	"io"
	"os"
	"strings"
	"testing"

	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
)

func TestFileSystem(t *testing.T) {
	assert := assert.New(t)
	require := require.New(t)

	fs, err := New("testdata/testdata.zip")
	require.NoError(err)
	require.NotNil(fs)

	f, err := fs.Open("/xxx")
	assert.Error(err)
	assert.Nil(f)

	f, err = fs.Open("test.html")
	assert.NoError(err)
	assert.NotNil(f)

}

func TestOpen(t *testing.T) {
	assert := assert.New(t)
	require := require.New(t)
	fs, err := New("testdata/testdata.zip")
	require.NoError(err)

	testCases := []struct {
		Path  string
		Error string
	}{
		{
			Path:  "/does/not/exist",
			Error: "file does not exist",
		},
		{
			Path:  "/img",
			Error: "",
		},
		{
			Path:  "/img/circle.png",
			Error: "",
		},
	}
	for _, tc := range testCases {
		f, err := fs.Open(tc.Path)
		if tc.Error == "" {
			assert.NoError(err)
			assert.NotNil(f)
			f.Close()

			// testing error after closing
			var buf [50]byte
			_, err := f.Read(buf[:])
			assert.Error(err)
			_, err = f.Seek(20, 0)
			assert.Error(err)
		} else {
			assert.Error(err)
			assert.True(strings.Contains(err.Error(), tc.Error), err.Error())
			assert.True(strings.Contains(err.Error(), tc.Path), err.Error())
		}
	}

	err = fs.Close()
	assert.NoError(err)
	f, err := fs.Open("/img/circle.png")
	assert.Error(err)
	assert.Nil(f)
	assert.True(strings.Contains(err.Error(), "filesystem closed"), err.Error())
}

func TestReaddir(t *testing.T) {
	assert := assert.New(t)
	require := require.New(t)
	fs, err := New("testdata/testdata.zip")
	require.NoError(err)

	testCases := []struct {
		Path  string
		Count int
		Error string
		Files []string
	}{
		{
			Path:  "/img",
			Error: "",
			Files: []string{
				"another-circle.png",
				"circle.png",
			},
		},
		{
			Path:  "/",
			Error: "",
			Files: []string{
				"empty",
				"img",
				"index.html",
				"js",
				"lots-of-files",
				"not-a-zip-file.txt",
				"random.dat",
				"test.html",
			},
		},
		{
			Path:  "/lots-of-files",
			Error: "",
			Files: []string{
				"file-01",
				"file-02",
				"file-03",
				"file-04",
				"file-05",
				"file-06",
				"file-07",
				"file-08",
				"file-09",
				"file-10",
				"file-11",
				"file-12",
				"file-13",
				"file-14",
				"file-15",
				"file-16",
				"file-17",
				"file-18",
				"file-19",
				"file-20",
			},
		},
		{
			Path:  "/img/circle.png",
			Error: "not a directory",
		},
		{
			Path:  "/img/circle.png",
			Error: "not a directory",
			Count: 2,
		},
	}

	for _, tc := range testCases {
		f, err := fs.Open(tc.Path)
		require.NoError(err)
		require.NotNil(f)

		files, err := f.Readdir(tc.Count)
		if tc.Error == "" {
			assert.NoError(err)
			assert.NotNil(files)
			printError := false
			if len(files) != len(tc.Files) {
				printError = true
			} else {
				for i, file := range files {
					if file.Name() != tc.Files[i] {
						printError = true
						break
					}
				}
			}
			if printError {
				t.Log(tc.Path, "Readdir expected:")
				for i, f := range tc.Files {
					t.Logf("    %d: %s\n", i, f)
				}
				t.Log(tc.Path, "Readdir actual:")
				for i, f := range files {
					t.Logf("    %d: %s\n", i, f.Name())
				}
				t.Error("Readdir failed test")
			}
		} else {
			assert.Error(err)
			assert.Nil(files)
			assert.True(strings.Contains(err.Error(), tc.Error), err.Error())
			assert.True(strings.Contains(err.Error(), tc.Path), err.Error())
		}
	}

	file, err := fs.Open("/lots-of-files")
	require.NoError(err)
	for i := 0; i < 10; i++ {
		a, err := file.Readdir(2)
		require.NoError(err)
		assert.Equal(len(a), 2)
		assert.Equal(fmt.Sprintf("file-%02d", i*2+1), a[0].Name())
		assert.Equal(fmt.Sprintf("file-%02d", i*2+2), a[1].Name())
	}
	a, err := file.Readdir(2)
	assert.Error(err)
	assert.Equal(io.EOF, err)
	assert.Equal(0, len(a))
}

// TestFileInfo tests the os.FileInfo associated with the http.File
func TestFileInfo(t *testing.T) {
	require := require.New(t)
	assert := assert.New(t)
	fs, err := New("testdata/testdata.zip")
	require.NoError(err)

	testCases := []struct {
		Path       string
		Name       string
		Size       int64
		Mode       os.FileMode
		IsDir      bool
		HasZipFile bool
	}{
		// Don't use any text files here because the sizes
		// are different between Windows and Unix-like OSs.
		{
			Path:       "/img/circle.png",
			Name:       "circle.png",
			Size:       5973,
			Mode:       0444,
			IsDir:      false,
			HasZipFile: true,
		},
		{
			Path:       "/img/",
			Name:       "img",
			Size:       0,
			Mode:       os.ModeDir | 0555,
			IsDir:      true,
			HasZipFile: true,
		},
		{
			Path:       "/",
			Name:       "/",
			Size:       0,
			Mode:       os.ModeDir | 0555,
			IsDir:      true,
			HasZipFile: true,
		},
	}

	for _, tc := range testCases {
		file, err := fs.Open(tc.Path)
		require.NoError(err)
		fi, err := file.Stat()
		require.NoError(err)
		assert.Equal(tc.Name, fi.Name())
		assert.Equal(tc.Size, fi.Size())
		assert.Equal(tc.Mode, fi.Mode())
		assert.Equal(tc.IsDir, fi.IsDir())
		_, hasZipFile := fi.Sys().(*zip.File)
		assert.Equal(tc.HasZipFile, hasZipFile, fi.Name())
		assert.False(fi.ModTime().IsZero())
	}
}

// TestFile tests the file reading capabilities.
func TestFile(t *testing.T) {
	require := require.New(t)
	assert := assert.New(t)
	fs, err := New("testdata/testdata.zip")
	require.NoError(err)

	testCases := []struct {
		Path string
		Size int
		MD5  string
	}{
		{
			Path: "/random.dat",
			Size: 10000,
			MD5:  "3c9fe0521cabb2ab38484cd1c024a61d",
		},
		{
			Path: "/img/circle.png",
			Size: 5973,
			MD5:  "05e3048db45e71749e06658ccfc0753b",
		},
	}

	calcMD5 := func(r io.ReadSeeker, size int, seek bool) string {
		if seek {
			n, err := r.Seek(0, 0)
			require.NoError(err)
			require.Equal(int64(0), n)
		}
		buf := make([]byte, size)
		n, err := r.Read(buf)
		require.NoError(err)
		require.Equal(size, n)
		md5Text := fmt.Sprintf("%x", md5.Sum(buf))
		n, err = r.Read(buf)
		require.Error(err)
		require.Equal(io.EOF, err)
		require.Equal(0, n)
		return md5Text
	}

	for _, tc := range testCases {
		file, err := fs.Open(tc.Path)
		assert.NoError(err)
		assert.Equal(tc.MD5, calcMD5(file, tc.Size, false))

		// seek back to the beginning, should not have
		// to create a temporary file
		nseek, err := file.Seek(0, 0)
		assert.NoError(err)
		assert.Equal(int64(0), nseek)
		assert.Equal(tc.MD5, calcMD5(file, tc.Size, true))

		nSeek, err := file.Seek(int64(tc.Size/2), 0)
		assert.NoError(err)
		assert.Equal(int64(tc.Size/2), nSeek)
		assert.Equal(tc.MD5, calcMD5(file, tc.Size, true))

		file.Close()
	}
}
<commit_msg>Fix unit test<commit_after>package zipfs

import (
	"archive/zip"
	"crypto/md5"
	"fmt"
	"io"
	"os"
	"strings"
	"testing"

	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
)

func TestFileSystem(t *testing.T) {
	assert := assert.New(t)
	require := require.New(t)

	fs, err := New("testdata/testdata.zip")
	require.NoError(err)
	require.NotNil(fs)

	f, err := fs.Open("/xxx")
	assert.Error(err)
	assert.Nil(f)

	f, err = fs.Open("test.html")
	assert.NoError(err)
	assert.NotNil(f)

}

func TestOpen(t *testing.T) {
	assert := assert.New(t)
	require := require.New(t)
	fs, err := New("testdata/testdata.zip")
	require.NoError(err)

	testCases := []struct {
		Path  string
		Error string
	}{
		{
			Path:  "/does/not/exist",
			Error: "file does not exist",
		},
		{
			Path:  "/img",
			Error: "",
		},
		{
			Path:  "/img/circle.png",
			Error: "",
		},
	}
	for _, tc := range testCases {
		f, err := fs.Open(tc.Path)
		if tc.Error == "" {
			assert.NoError(err)
			assert.NotNil(f)
			f.Close()

			// testing error after closing
			var buf [50]byte
			_, err := f.Read(buf[:])
			assert.Error(err)
			_, err = f.Seek(20, 0)
			assert.Error(err)
		} else {
			assert.Error(err)
			assert.True(strings.Contains(err.Error(), tc.Error), err.Error())
			assert.True(strings.Contains(err.Error(), tc.Path), err.Error())
		}
	}

	err = fs.Close()
	assert.NoError(err)
	f, err := fs.Open("/img/circle.png")
	assert.Error(err)
	assert.Nil(f)
	assert.True(strings.Contains(err.Error(), "filesystem closed"), err.Error())
}

func TestReaddir(t *testing.T) {
	assert := assert.New(t)
	require := require.New(t)
	fs, err := New("testdata/testdata.zip")
	require.NoError(err)

	testCases := []struct {
		Path  string
		Count int
		Error string
		Files []string
	}{
		{
			Path:  "/img",
			Error: "",
			Files: []string{
				"another-circle.png",
				"circle.png",
			},
		},
		{
			Path:  "/",
			Error: "",
			Files: []string{
				"empty",
				"img",
				"index.html",
				"js",
				"lots-of-files",
				"not-a-zip-file.txt",
				"random.dat",
				"test.html",
			},
		},
		{
			Path:  "/lots-of-files",
			Error: "",
			Files: []string{
				"file-01",
				"file-02",
				"file-03",
				"file-04",
				"file-05",
				"file-06",
				"file-07",
				"file-08",
				"file-09",
				"file-10",
				"file-11",
				"file-12",
				"file-13",
				"file-14",
				"file-15",
				"file-16",
				"file-17",
				"file-18",
				"file-19",
				"file-20",
			},
		},
		{
			Path:  "/img/circle.png",
			Error: "not a directory",
		},
		{
			Path:  "/img/circle.png",
			Error: "not a directory",
			Count: 2,
		},
	}

	for _, tc := range testCases {
		f, err := fs.Open(tc.Path)
		require.NoError(err)
		require.NotNil(f)

		files, err := f.Readdir(tc.Count)
		if tc.Error == "" {
			assert.NoError(err)
			assert.NotNil(files)
			printError := false
			if len(files) != len(tc.Files) {
				printError = true
			} else {
				for i, file := range files {
					if file.Name() != tc.Files[i] {
						printError = true
						break
					}
				}
			}
			if printError {
				t.Log(tc.Path, "Readdir expected:")
				for i, f := range tc.Files {
					t.Logf("    %d: %s\n", i, f)
				}
				t.Log(tc.Path, "Readdir actual:")
				for i, f := range files {
					t.Logf("    %d: %s\n", i, f.Name())
				}
				t.Error("Readdir failed test")
			}
		} else {
			assert.Error(err)
			assert.Nil(files)
			assert.True(strings.Contains(err.Error(), tc.Error), err.Error())
			assert.True(strings.Contains(err.Error(), tc.Path), err.Error())
		}
	}

	file, err := fs.Open("/lots-of-files")
	require.NoError(err)
	for i := 0; i < 10; i++ {
		a, err := file.Readdir(2)
		require.NoError(err)
		assert.Equal(len(a), 2)
		assert.Equal(fmt.Sprintf("file-%02d", i*2+1), a[0].Name())
		assert.Equal(fmt.Sprintf("file-%02d", i*2+2), a[1].Name())
	}
	a, err := file.Readdir(2)
	assert.Error(err)
	assert.Equal(io.EOF, err)
	assert.Equal(0, len(a))
}

// TestFileInfo tests the os.FileInfo associated with the http.File
func TestFileInfo(t *testing.T) {
	require := require.New(t)
	assert := assert.New(t)
	fs, err := New("testdata/testdata.zip")
	require.NoError(err)

	testCases := []struct {
		Path       string
		Name       string
		Size       int64
		Mode       os.FileMode
		IsDir      bool
		HasZipFile bool
	}{
		// Don't use any text files here because the sizes
		// are different between Windows and Unix-like OSs.
		{
			Path:       "/img/circle.png",
			Name:       "circle.png",
			Size:       5973,
			Mode:       0444,
			IsDir:      false,
			HasZipFile: true,
		},
		{
			Path:       "/img/",
			Name:       "img",
			Size:       0,
			Mode:       os.ModeDir | 0555,
			IsDir:      true,
			HasZipFile: true,
		},
		{
			Path:       "/",
			Name:       "/",
			Size:       0,
			Mode:       os.ModeDir | 0555,
			IsDir:      true,
			HasZipFile: true,
		},
	}

	for _, tc := range testCases {
		file, err := fs.Open(tc.Path)
		require.NoError(err)
		fi, err := file.Stat()
		require.NoError(err)
		assert.Equal(tc.Name, fi.Name())
		assert.Equal(tc.Size, fi.Size())
		assert.Equal(tc.Mode, fi.Mode())
		assert.Equal(tc.IsDir, fi.IsDir())
		_, hasZipFile := fi.Sys().(*zip.File)
		assert.Equal(tc.HasZipFile, hasZipFile, fi.Name())
		assert.False(fi.ModTime().IsZero())
	}
}

// TestFile tests the file reading capabilities.
func TestFile(t *testing.T) {
	require := require.New(t)
	assert := assert.New(t)
	fs, err := New("testdata/testdata.zip")
	require.NoError(err)

	testCases := []struct {
		Path string
		Size int
		MD5  string
	}{
		{
			Path: "/random.dat",
			Size: 10000,
			MD5:  "3c9fe0521cabb2ab38484cd1c024a61d",
		},
		{
			Path: "/img/circle.png",
			Size: 5973,
			MD5:  "05e3048db45e71749e06658ccfc0753b",
		},
	}

	calcMD5 := func(r io.ReadSeeker, size int, seek bool) string {
		if seek {
			n, err := r.Seek(0, 0)
			require.NoError(err)
			require.Equal(int64(0), n)
		}
		buf := make([]byte, size)
		n, err := io.ReadFull(r, buf)
		require.NoError(err)
		require.Equal(size, n)
		md5Text := fmt.Sprintf("%x", md5.Sum(buf))
		n, err = r.Read(buf)
		require.Error(err)
		require.Equal(io.EOF, err)
		require.Equal(0, n)
		return md5Text
	}

	for _, tc := range testCases {
		file, err := fs.Open(tc.Path)
		assert.NoError(err)
		assert.Equal(tc.MD5, calcMD5(file, tc.Size, false))

		// seek back to the beginning, should not have
		// to create a temporary file
		nseek, err := file.Seek(0, 0)
		assert.NoError(err)
		assert.Equal(int64(0), nseek)
		assert.Equal(tc.MD5, calcMD5(file, tc.Size, true))

		nSeek, err := file.Seek(int64(tc.Size/2), 0)
		assert.NoError(err)
		assert.Equal(int64(tc.Size/2), nSeek)
		assert.Equal(tc.MD5, calcMD5(file, tc.Size, true))

		file.Close()
	}
}
<|endoftext|>


<commit_before>package wellington

import (
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"testing"
	"time"
)

func TestWatch_rebuild(t *testing.T) {
	tdir, err := ioutil.TempDir(os.TempDir(), "testwatch_")
	if err != nil {
		t.Fatal(err)
	}
	tfile := filepath.Join(tdir, "_new.scss")
	fh, err := os.Create(tfile)
	if err != nil {
		t.Fatal(err)
	}

	w := NewWatcher()
	w.Dirs = []string{tdir}
	w.PartialMap.AddRelation("tswif", tfile)
	err = w.Watch()
	if err != nil {
		t.Fatal(err)
	}
	rebuildChan = make(chan []string, 1)
	done := make(chan bool, 1)
	go func(t *testing.T) {
		select {
		case <-rebuildChan:
			done <- true
		case <-time.After(2 * time.Second):
			done <- false
		}
		done <- true
	}(t)
	fh.WriteString("boom")
	success := <-done
	if !success {
		t.Fatal("Timeout waiting for rebuild")
	}

}

func TestWatch(t *testing.T) {
	w := NewWatcher()
	err := w.Watch()
	if err == nil {
		t.Error("No errors thrown for nil directories")
	}
	w.FileWatcher.Close()

	watcherChan = make(chan string, 1)
	w = NewWatcher()
	w.Dirs = []string{"test"}
	err = w.Watch()

	// Test file creation event
	go func() {
		select {
		case <-watcherChan:
			break
		case <-time.After(2 * time.Second):
			fmt.Printf("timeout %d\n", len(watcherChan))
			t.Error("Timeout without creating file")
		}
	}()

	testFile := "test/watchfile.lock"
	f, err := os.OpenFile(testFile, os.O_WRONLY|os.O_CREATE, 0666)
	defer func() {
		// Give time for filesystem to sync before deleting file
		time.Sleep(50 * time.Millisecond)
		os.Remove(testFile)
		f.Close()
	}()
	if err != nil {
		t.Fatalf("creating test file failed: %s", err)
	}
	f.Sync()

	// Test file modification event
	go func() {
		select {
		case <-watcherChan:
			break
		case <-time.After(2 * time.Second):
			fmt.Printf("timeout %d\n", len(watcherChan))
			t.Error("Timeout without detecting write")
		}
	}()

	f.WriteString("data")
	f.Sync()

}

func TestRebuild(t *testing.T) {
	w := NewWatcher()
	err := w.rebuild("file/event")

	if e := fmt.Sprintf("build args are nil"); e != err.Error() {
		t.Fatalf("wanted: %s\ngot: %s", e, err)
	}
}
<commit_msg>test appendUnique<commit_after>package wellington

import (
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"testing"
	"time"
)

func TestWatch_rebuild(t *testing.T) {
	tdir, err := ioutil.TempDir(os.TempDir(), "testwatch_")
	if err != nil {
		t.Fatal(err)
	}
	tfile := filepath.Join(tdir, "_new.scss")
	fh, err := os.Create(tfile)
	if err != nil {
		t.Fatal(err)
	}

	w := NewWatcher()
	w.Dirs = []string{tdir}
	w.PartialMap.AddRelation("tswif", tfile)
	err = w.Watch()
	if err != nil {
		t.Fatal(err)
	}
	rebuildChan = make(chan []string, 1)
	done := make(chan bool, 1)
	go func(t *testing.T) {
		select {
		case <-rebuildChan:
			done <- true
		case <-time.After(2 * time.Second):
			done <- false
		}
		done <- true
	}(t)
	fh.WriteString("boom")
	success := <-done
	if !success {
		t.Fatal("Timeout waiting for rebuild")
	}

}

func TestWatch(t *testing.T) {
	w := NewWatcher()
	err := w.Watch()
	if err == nil {
		t.Error("No errors thrown for nil directories")
	}
	w.FileWatcher.Close()

	watcherChan = make(chan string, 1)
	w = NewWatcher()
	w.Dirs = []string{"test"}
	err = w.Watch()

	// Test file creation event
	go func() {
		select {
		case <-watcherChan:
			break
		case <-time.After(2 * time.Second):
			fmt.Printf("timeout %d\n", len(watcherChan))
			t.Error("Timeout without creating file")
		}
	}()

	testFile := "test/watchfile.lock"
	f, err := os.OpenFile(testFile, os.O_WRONLY|os.O_CREATE, 0666)
	defer func() {
		// Give time for filesystem to sync before deleting file
		time.Sleep(50 * time.Millisecond)
		os.Remove(testFile)
		f.Close()
	}()
	if err != nil {
		t.Fatalf("creating test file failed: %s", err)
	}
	f.Sync()

	// Test file modification event
	go func() {
		select {
		case <-watcherChan:
			break
		case <-time.After(2 * time.Second):
			fmt.Printf("timeout %d\n", len(watcherChan))
			t.Error("Timeout without detecting write")
		}
	}()

	f.WriteString("data")
	f.Sync()

}

func TestRebuild(t *testing.T) {
	w := NewWatcher()
	err := w.rebuild("file/event")

	if e := fmt.Sprintf("build args are nil"); e != err.Error() {
		t.Fatalf("wanted: %s\ngot: %s", e, err)
	}
}

func TestAppendUnique(t *testing.T) {
	lst := []string{"a", "b", "c"}
	new := appendUnique(lst, "a")
	if len(new) != len(lst) {
		t.Errorf("got: %d wanted: %d", len(new), len(lst))
	}
}
<|endoftext|>