<commit_before>package main

import (
	"bufio"
	"flag"
	"fmt"
	"github.com/gin-gonic/gin"
	"github.com/pboehm/ddns/connection"
	"html/template"
	"log"
	"net"
	"net/http"
	"os"
	"strings"
)

var DdnsDomain string

func HandleErr(err error) {
	if err != nil {
		log.Fatal(err)
	}
}

func RunBackend() {
	conn := connection.OpenConnection()
	defer conn.Close()

	fmt.Printf("OK\tDDNS Go Backend\n")

	bio := bufio.NewReader(os.Stdin)

	for {
		line, _, err := bio.ReadLine()
		HandleErr(err)

		parts := strings.Split(string(line), "\t")
		if len(parts) == 6 {
			query_name := parts[1]
			fqdn_parts := strings.Split(query_name, ".")

			query_class := parts[2]
			query_id := parts[4]

			if len(fqdn_parts) > 0 {
				if conn.HostExist(fqdn_parts[0]) {
					host := conn.GetHost(fqdn_parts[0])

					record := "A"
					if !host.IsIPv4() {
						record = "AAAA"
					}

					fmt.Printf("DATA\t%s\t%s\t%s\t10\t%s\t%s\n",
						query_name, query_class, record, query_id, host.Ip)
				}
			}
		}

		fmt.Printf("END\n")
	}
}

func RunWebService() {
	conn := connection.OpenConnection()
	defer conn.Close()

	// Add index template from bindata
	index_content, err := Asset("templates/index.html")
	HandleErr(err)

	html, err := template.New("index.html").Parse(string(index_content))
	HandleErr(err)

	r := gin.Default()
	r.HTMLTemplates = html

	r.GET("/", func(g *gin.Context) {
		g.HTML(200, "index.html", gin.H{"domain": DdnsDomain})
	})

	r.GET("/available/:hostname", func(c *gin.Context) {
		hostname := c.Params.ByName("hostname")

		c.JSON(200, gin.H{
			"available": !conn.HostExist(hostname),
		})
	})

	r.GET("/new/:hostname", func(c *gin.Context) {
		hostname := c.Params.ByName("hostname")

		if conn.HostExist(hostname) {
			c.String(403, "This hostname has already been registered.")
			return
		}

		host := &connection.Host{Hostname: hostname, Ip: "127.0.0.1"}
		host.GenerateAndSetToken()

		conn.SaveHost(host)

		c.JSON(200, gin.H{
			"hostname":    host.Hostname,
			"token":       host.Token,
			"update_link": fmt.Sprintf("/update/%s/%s", host.Hostname, host.Token),
		})
	})

	r.GET("/update/:hostname/:token", func(c *gin.Context) {
		hostname := c.Params.ByName("hostname")
		token := c.Params.ByName("token")

		if !conn.HostExist(hostname) {
			c.String(404,
				"This hostname has not been registered or is expired.")
			return
		}

		host := conn.GetHost(hostname)

		if host.Token != token {
			c.String(403,
				"You have supplied the wrong token to manipulate this host")
			return
		}

		ip, err := GetRemoteAddr(c.Req)
		if err != nil {
			c.String(500, "Your sender IP address is not in the right format")
		}

		host.Ip = ip
		conn.SaveHost(host)

		c.String(200, fmt.Sprintf("Your current IP is %s", ip))
	})

	r.Run(":8080")
}

// Get the Remote Address of the client. At First we try to get the
// X-Forwarded-For Header which holds the IP if we are behind a proxy,
// otherwise the RemoteAddr is used
func GetRemoteAddr(req *http.Request) (string, error) {
	header_data, ok := req.Header["X-Forwarded-For"]

	if ok {
		return header_data[0], nil
	} else {
		ip, _, err := net.SplitHostPort(req.RemoteAddr)
		return ip, err
	}
}

func ValidateCommandArgs() {
	if DdnsDomain == "" {
		log.Fatal("You have to supply the domain via --domain=DOMAIN")
	} else if !strings.HasPrefix(DdnsDomain, ".") {
		// get the domain in the right format
		DdnsDomain = "." + DdnsDomain
	}
}

func init() {
	flag.StringVar(&DdnsDomain, "domain", "",
		"The subdomain which should be handled by DDNS")
}

func main() {
	flag.Parse()
	ValidateCommandArgs()

	if len(flag.Args()) != 1 {
		usage()
	}
	cmd := flag.Args()[0]

	switch cmd {
	case "backend":
		log.Printf("Starting PDNS Backend\n")
		RunBackend()
	case "web":
		log.Printf("Starting Web Service\n")
		RunWebService()
	default:
		usage()
	}
}

func usage() {
	log.Fatal("Usage: ./ddns [backend|web]")
}
<commit_msg>Backend now determines the hostname by subtracting the domain<commit_after>package main

import (
	"bufio"
	"flag"
	"fmt"
	"github.com/gin-gonic/gin"
	"github.com/pboehm/ddns/connection"
	"html/template"
	"log"
	"net"
	"net/http"
	"os"
	"strings"
)

var DdnsDomain string

func HandleErr(err error) {
	if err != nil {
		log.Fatal(err)
	}
}

func RunBackend() {
	conn := connection.OpenConnection()
	defer conn.Close()

	fmt.Printf("OK\tDDNS Go Backend\n")

	bio := bufio.NewReader(os.Stdin)

	for {
		line, _, err := bio.ReadLine()
		HandleErr(err)

		parts := strings.Split(string(line), "\t")
		if len(parts) == 6 {
			query_name := parts[1]

			// get the host part of the fqdn
			// pi.d.example.org -> pi
			hostname := ""
			if strings.HasSuffix(query_name, DdnsDomain) {
				hostname = query_name[:len(query_name)-len(DdnsDomain)]
			}

			query_class := parts[2]
			query_type := parts[3]
			query_id := parts[4]

			if hostname != "" {
				// check for existance and create response
				if conn.HostExist(hostname) {
					host := conn.GetHost(hostname)

					record := "A"
					if !host.IsIPv4() {
						record = "AAAA"
					}

					fmt.Printf("DATA\t%s\t%s\t%s\t10\t%s\t%s\n",
						query_name, query_class, record, query_id, host.Ip)
				}
			}
		}

		fmt.Printf("END\n")
	}
}

func RunWebService() {
	conn := connection.OpenConnection()
	defer conn.Close()

	// Add index template from bindata
	index_content, err := Asset("templates/index.html")
	HandleErr(err)

	html, err := template.New("index.html").Parse(string(index_content))
	HandleErr(err)

	r := gin.Default()
	r.HTMLTemplates = html

	r.GET("/", func(g *gin.Context) {
		g.HTML(200, "index.html", gin.H{"domain": DdnsDomain})
	})

	r.GET("/available/:hostname", func(c *gin.Context) {
		hostname := c.Params.ByName("hostname")

		c.JSON(200, gin.H{
			"available": !conn.HostExist(hostname),
		})
	})

	r.GET("/new/:hostname", func(c *gin.Context) {
		hostname := c.Params.ByName("hostname")

		if conn.HostExist(hostname) {
			c.String(403, "This hostname has already been registered.")
			return
		}

		host := &connection.Host{Hostname: hostname, Ip: "127.0.0.1"}
		host.GenerateAndSetToken()

		conn.SaveHost(host)

		c.JSON(200, gin.H{
			"hostname":    host.Hostname,
			"token":       host.Token,
			"update_link": fmt.Sprintf("/update/%s/%s", host.Hostname, host.Token),
		})
	})

	r.GET("/update/:hostname/:token", func(c *gin.Context) {
		hostname := c.Params.ByName("hostname")
		token := c.Params.ByName("token")

		if !conn.HostExist(hostname) {
			c.String(404,
				"This hostname has not been registered or is expired.")
			return
		}

		host := conn.GetHost(hostname)

		if host.Token != token {
			c.String(403,
				"You have supplied the wrong token to manipulate this host")
			return
		}

		ip, err := GetRemoteAddr(c.Req)
		if err != nil {
			c.String(500, "Your sender IP address is not in the right format")
		}

		host.Ip = ip
		conn.SaveHost(host)

		c.String(200, fmt.Sprintf("Your current IP is %s", ip))
	})

	r.Run(":8080")
}

// Get the Remote Address of the client. At First we try to get the
// X-Forwarded-For Header which holds the IP if we are behind a proxy,
// otherwise the RemoteAddr is used
func GetRemoteAddr(req *http.Request) (string, error) {
	header_data, ok := req.Header["X-Forwarded-For"]

	if ok {
		return header_data[0], nil
	} else {
		ip, _, err := net.SplitHostPort(req.RemoteAddr)
		return ip, err
	}
}

func ValidateCommandArgs() {
	if DdnsDomain == "" {
		log.Fatal("You have to supply the domain via --domain=DOMAIN")
	} else if !strings.HasPrefix(DdnsDomain, ".") {
		// get the domain in the right format
		DdnsDomain = "." + DdnsDomain
	}
}

func init() {
	flag.StringVar(&DdnsDomain, "domain", "",
		"The subdomain which should be handled by DDNS")
}

func main() {
	flag.Parse()
	ValidateCommandArgs()

	if len(flag.Args()) != 1 {
		usage()
	}
	cmd := flag.Args()[0]

	switch cmd {
	case "backend":
		log.Printf("Starting PDNS Backend\n")
		RunBackend()
	case "web":
		log.Printf("Starting Web Service\n")
		RunWebService()
	default:
		usage()
	}
}

func usage() {
	log.Fatal("Usage: ./ddns [backend|web]")
}
<|endoftext|>


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

package cbft

import (
	"fmt"
	"io"
	"sync/atomic"

	"github.com/rcrowley/go-metrics"
)

type DestExtrasType uint16

const DEST_EXTRAS_TYPE_NIL = DestExtrasType(0)

type Dest interface {
	// Invoked by PIndex.Close().
	Close() error

	// Invoked when there's a new mutation from a data source for a
	// partition.  Dest implementation is responsible for making its
	// own copies of the key, val and extras data.
	DataUpdate(partition string, key []byte, seq uint64, val []byte,
		cas uint64,
		extrasType DestExtrasType, extras []byte) error

	// Invoked by the data source when there's a data deletion in a
	// partition.  Dest implementation is responsible for making its
	// own copies of the key and extras data.
	DataDelete(partition string, key []byte, seq uint64,
		cas uint64,
		extrasType DestExtrasType, extras []byte) error

	// An callback invoked by the data source when there's a start of
	// a new snapshot for a partition.  The Receiver implementation,
	// for example, might choose to optimize persistence perhaps by
	// preparing a batch write to application-specific storage.
	SnapshotStart(partition string, snapStart, snapEnd uint64) error

	// OpaqueGet() should return the opaque value previously
	// provided by an earlier call to OpaqueSet().  If there was no
	// previous call to OpaqueSet(), such as in the case of a brand
	// new instance of a Dest (as opposed to a restarted or reloaded
	// Dest), the Dest should return (nil, 0, nil) for (value,
	// lastSeq, err), respectively.  The lastSeq should be the last
	// sequence number received and persisted during calls to the
	// Dest's DataUpdate() & DataDelete() methods.
	OpaqueGet(partition string) (value []byte, lastSeq uint64, err error)

	// The Dest implementation should persist the value parameter of
	// OpaqueSet() for retrieval during some future call to
	// OpaqueGet() by the system.  The metadata value should be
	// considered "in-stream", or as part of the sequence history of
	// mutations.  That is, a later Rollback() to some previous
	// sequence number for a particular partition should rollback
	// both persisted metadata and regular data.  The Dest
	// implementation should make its own copy of the value data.
	OpaqueSet(partition string, value []byte) error

	// Invoked by when the datasource signals a rollback during dest
	// initialization.  Note that both regular data and opaque data
	// should be rolled back to at a maximum of the rollbackSeq.  Of
	// note, the Dest is allowed to rollback even further, even all
	// the way back to the start or to zero.
	Rollback(partition string, rollbackSeq uint64) error

	// Blocks until the Dest has reached the desired consistency for
	// the partition or until the cancelCh is readable or closed by
	// some goroutine related to the calling goroutine.  The error
	// response might be a ErrorConsistencyWait instance, which has
	// StartEndSeqs information.  The seqStart is the seq number when
	// the operation started waiting and the seqEnd is the seq number
	// at the end of operation (even when cancelled or error), so that
	// the caller might get a rough idea of ingest velocity.
	ConsistencyWait(partition, partitionUUID string,
		consistencyLevel string,
		consistencySeq uint64,
		cancelCh <-chan bool) error

	// Counts the underlying pindex implementation.
	Count(pindex *PIndex, cancelCh <-chan bool) (uint64, error)

	// Queries the underlying pindex implementation, blocking if
	// needed for the Dest to reach the desired consistency.
	Query(pindex *PIndex, req []byte, w io.Writer,
		cancelCh <-chan bool) error

	Stats(io.Writer) error
}

type DestStats struct {
	TotError uint64

	TimerDataUpdate    metrics.Timer
	TimerDataDelete    metrics.Timer
	TimerSnapshotStart metrics.Timer
	TimerOpaqueGet     metrics.Timer
	TimerOpaqueSet     metrics.Timer
	TimerRollback      metrics.Timer
}

func NewDestStats() *DestStats {
	return &DestStats{
		TimerDataUpdate:    metrics.NewTimer(),
		TimerDataDelete:    metrics.NewTimer(),
		TimerSnapshotStart: metrics.NewTimer(),
		TimerOpaqueGet:     metrics.NewTimer(),
		TimerOpaqueSet:     metrics.NewTimer(),
		TimerRollback:      metrics.NewTimer(),
	}
}

func (d *DestStats) WriteJSON(w io.Writer) {
	t := atomic.LoadUint64(&d.TotError)
	fmt.Fprintf(w, `{"TotError":%d`, t)

	w.Write([]byte(`,"TimerDataUpdate":`))
	WriteTimerJSON(w, d.TimerDataUpdate)
	w.Write([]byte(`,"TimerDataDelete":`))
	WriteTimerJSON(w, d.TimerDataDelete)
	w.Write([]byte(`,"TimerSnapshotStart":`))
	WriteTimerJSON(w, d.TimerSnapshotStart)
	w.Write([]byte(`,"TimerOpaqueGet":`))
	WriteTimerJSON(w, d.TimerOpaqueGet)
	w.Write([]byte(`,"TimerOpaqueSet":`))
	WriteTimerJSON(w, d.TimerOpaqueSet)
	w.Write([]byte(`,"TimerRollback":`))
	WriteTimerJSON(w, d.TimerRollback)

	w.Write(jsonCloseBrace)
}

type DestPartitionFunc func(partition string, key []byte,
	dests map[string]Dest) (Dest, error)

// This basic partition func first tries a direct lookup by partition
// string, else it tries the "" partition.
func BasicPartitionFunc(partition string, key []byte,
	dests map[string]Dest) (Dest, error) {
	dest, exists := dests[partition]
	if exists {
		return dest, nil
	}
	dest, exists = dests[""]
	if exists {
		return dest, nil
	}
	return nil, fmt.Errorf("dest: no dest for key: %s,"+
		" partition: %s, dests: %#v", key, partition, dests)
}
<commit_msg>doc code comments for dest.go<commit_after>//  Copyright (c) 2014 Couchbase, Inc.
//  Licensed under the Apache License, Version 2.0 (the "License");
//  you may not use this file except in compliance with the
//  License. You may obtain a copy of the License at
//    http://www.apache.org/licenses/LICENSE-2.0
//  Unless required by applicable law or agreed to in writing,
//  software distributed under the License is distributed on an "AS
//  IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
//  express or implied. See the License for the specific language
//  governing permissions and limitations under the License.

package cbft

import (
	"fmt"
	"io"
	"sync/atomic"

	"github.com/rcrowley/go-metrics"
)

// Dest interface defines the data sink or destination for data that
// cames from a data-source.  In other words, a data-source (or a Feed
// instance) is hooked up to one or more Dest instances.  As a Feed
// receives incoming data, the Feed will invoke methods on its Dest
// instances.
type Dest interface {
	// Invoked by PIndex.Close().
	Close() error

	// Invoked when there's a new mutation from a data source for a
	// partition.  Dest implementation is responsible for making its
	// own copies of the key, val and extras data.
	DataUpdate(partition string, key []byte, seq uint64, val []byte,
		cas uint64,
		extrasType DestExtrasType, extras []byte) error

	// Invoked by the data source when there's a data deletion in a
	// partition.  Dest implementation is responsible for making its
	// own copies of the key and extras data.
	DataDelete(partition string, key []byte, seq uint64,
		cas uint64,
		extrasType DestExtrasType, extras []byte) error

	// An callback invoked by the data source when there's a start of
	// a new snapshot for a partition.  The Receiver implementation,
	// for example, might choose to optimize persistence perhaps by
	// preparing a batch write to application-specific storage.
	SnapshotStart(partition string, snapStart, snapEnd uint64) error

	// OpaqueGet() should return the opaque value previously
	// provided by an earlier call to OpaqueSet().  If there was no
	// previous call to OpaqueSet(), such as in the case of a brand
	// new instance of a Dest (as opposed to a restarted or reloaded
	// Dest), the Dest should return (nil, 0, nil) for (value,
	// lastSeq, err), respectively.  The lastSeq should be the last
	// sequence number received and persisted during calls to the
	// Dest's DataUpdate() & DataDelete() methods.
	OpaqueGet(partition string) (value []byte, lastSeq uint64, err error)

	// The Dest implementation should persist the value parameter of
	// OpaqueSet() for retrieval during some future call to
	// OpaqueGet() by the system.  The metadata value should be
	// considered "in-stream", or as part of the sequence history of
	// mutations.  That is, a later Rollback() to some previous
	// sequence number for a particular partition should rollback
	// both persisted metadata and regular data.  The Dest
	// implementation should make its own copy of the value data.
	OpaqueSet(partition string, value []byte) error

	// Invoked by when the datasource signals a rollback during dest
	// initialization.  Note that both regular data and opaque data
	// should be rolled back to at a maximum of the rollbackSeq.  Of
	// note, the Dest is allowed to rollback even further, even all
	// the way back to the start or to zero.
	Rollback(partition string, rollbackSeq uint64) error

	// Blocks until the Dest has reached the desired consistency for
	// the partition or until the cancelCh is readable or closed by
	// some goroutine related to the calling goroutine.  The error
	// response might be a ErrorConsistencyWait instance, which has
	// StartEndSeqs information.  The seqStart is the seq number when
	// the operation started waiting and the seqEnd is the seq number
	// at the end of operation (even when cancelled or error), so that
	// the caller might get a rough idea of ingest velocity.
	ConsistencyWait(partition, partitionUUID string,
		consistencyLevel string,
		consistencySeq uint64,
		cancelCh <-chan bool) error

	// Counts the underlying pindex implementation.
	Count(pindex *PIndex, cancelCh <-chan bool) (uint64, error)

	// Queries the underlying pindex implementation, blocking if
	// needed for the Dest to reach the desired consistency.
	Query(pindex *PIndex, req []byte, w io.Writer,
		cancelCh <-chan bool) error

	Stats(io.Writer) error
}

// DestExtrasType represents the encoding for the
// Dest.DataUpdate/DataDelete() extras parameter.
type DestExtrasType uint16

// DEST_EXTRAS_TYPE_NIL means there are no extras as part of a
// Dest.DataUpdate/DataDelete invocation.
const DEST_EXTRAS_TYPE_NIL = DestExtrasType(0)

// DestStats holds the common stats or metrics for a Dest.
type DestStats struct {
	TotError uint64

	TimerDataUpdate    metrics.Timer
	TimerDataDelete    metrics.Timer
	TimerSnapshotStart metrics.Timer
	TimerOpaqueGet     metrics.Timer
	TimerOpaqueSet     metrics.Timer
	TimerRollback      metrics.Timer
}

// NewDestStats creates a new, ready-to-use DestStats.
func NewDestStats() *DestStats {
	return &DestStats{
		TimerDataUpdate:    metrics.NewTimer(),
		TimerDataDelete:    metrics.NewTimer(),
		TimerSnapshotStart: metrics.NewTimer(),
		TimerOpaqueGet:     metrics.NewTimer(),
		TimerOpaqueSet:     metrics.NewTimer(),
		TimerRollback:      metrics.NewTimer(),
	}
}

func (d *DestStats) WriteJSON(w io.Writer) {
	t := atomic.LoadUint64(&d.TotError)
	fmt.Fprintf(w, `{"TotError":%d`, t)

	w.Write([]byte(`,"TimerDataUpdate":`))
	WriteTimerJSON(w, d.TimerDataUpdate)
	w.Write([]byte(`,"TimerDataDelete":`))
	WriteTimerJSON(w, d.TimerDataDelete)
	w.Write([]byte(`,"TimerSnapshotStart":`))
	WriteTimerJSON(w, d.TimerSnapshotStart)
	w.Write([]byte(`,"TimerOpaqueGet":`))
	WriteTimerJSON(w, d.TimerOpaqueGet)
	w.Write([]byte(`,"TimerOpaqueSet":`))
	WriteTimerJSON(w, d.TimerOpaqueSet)
	w.Write([]byte(`,"TimerRollback":`))
	WriteTimerJSON(w, d.TimerRollback)

	w.Write(jsonCloseBrace)
}

// A DestPartitionFunc allows a level of indirection/abstraction for
// the Feed-to-Dest relationship.  A Feed is hooked up in a
// one-to-many relationship with multiple Dest instances.  The
// DestPartitionFunc provided to a Feed instance defines the mapping
// of which Dest the Feed should invoke when the Feed receives an
// incoming data item.
//
// The partition parameter is encoded as a string, instead of a uint16
// or number, to allow for future range partitioning functionality.
type DestPartitionFunc func(partition string, key []byte,
	dests map[string]Dest) (Dest, error)

// This basic partition func first tries a direct lookup by partition
// string, else it tries the "" partition.
func BasicPartitionFunc(partition string, key []byte,
	dests map[string]Dest) (Dest, error) {
	dest, exists := dests[partition]
	if exists {
		return dest, nil
	}
	dest, exists = dests[""]
	if exists {
		return dest, nil
	}
	return nil, fmt.Errorf("dest: no dest for key: %s,"+
		" partition: %s, dests: %#v", key, partition, dests)
}
<|endoftext|>


<commit_before>package amqputil

import (
	"time"

	"github.com/streadway/amqp"
)

type Conn struct {
	conn *amqp.Connection

	// closure info of connection
	dialFn   func() error
	attempts uint8
}

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

func (conn *Conn) Dial(uri string) error {
	// closure the uri for handle reconnects
	conn.dialFn = func() error {
		var err error

		conn.conn, err = amqp.Dial(uri)

		if err != nil {
			return err
		}

		return nil
	}

	return conn.dialFn()
}

func (conn *Conn) Close() error {
	return conn.conn.Close()
}

// AutoRedial manages the automatic redial of connection when unexpected closed.
// outChan is an unbuffered channel required to receive the errors that results from
// attempts of reconnect. On successfully reconnected, the function onSuccess
// is invoked.
//
// The outChan parameter can receive *amqp.Error for AMQP connection errors
// or errors.Error for any other net/tcp internal error.
//
// Redial strategy:
// If the connection is closed in an unexpected way (opposite of conn.Close()), then
// AutoRedial will try to automatically reconnect waiting for N seconds before each
// attempt, where N is the number of attempts of reconnecting. If the number of
// attempts reach 60, it will be zero'ed.
func (conn *Conn) AutoRedial(outChan chan error, onSuccess func()) {
	errChan := conn.conn.NotifyClose(make(chan *amqp.Error))

	go func() {
		var err error

		select {
		case amqpErr := <-errChan:
			err = amqpErr

			if amqpErr == nil {
				// Gracefull connection close
				return
			}
		attempts:
			outChan <- err

			if conn.attempts > 60 {
				conn.attempts = 0
			}

			// Wait n Seconds where n == conn.attempts...
			time.Sleep(time.Duration(conn.attempts) * time.Second)

			err = conn.dialFn()

			if err != nil {
				conn.attempts += 1
				goto attempts
			}

			conn.attempts = 0

			// enabled AutoRedial on the new connection
			conn.AutoRedial(outChan, onSuccess)
			onSuccess()
			return
		}
	}()
}
<commit_msg>[CHANGE] golint the code<commit_after>package amqputil

import (
	"time"

	"github.com/streadway/amqp"
)

// Conn is the amqp connection
type Conn struct {
	conn *amqp.Connection

	// closure info of connection
	dialFn   func() error
	attempts uint8
}

// New do what the fucking name says...
func New() *Conn {
	return &Conn{}
}

// Dial to rabbitmq
func (conn *Conn) Dial(uri string) error {
	// closure the uri for handle reconnects
	conn.dialFn = func() error {
		var err error

		conn.conn, err = amqp.Dial(uri)

		if err != nil {
			return err
		}

		return nil
	}

	return conn.dialFn()
}

// Close the connection
func (conn *Conn) Close() error {
	return conn.conn.Close()
}

// AutoRedial manages the automatic redial of connection when unexpected closed.
// outChan is an unbuffered channel required to receive the errors that results from
// attempts of reconnect. On successfully reconnected, the function onSuccess
// is invoked.
//
// The outChan parameter can receive *amqp.Error for AMQP connection errors
// or errors.Error for any other net/tcp internal error.
//
// Redial strategy:
// If the connection is closed in an unexpected way (opposite of conn.Close()), then
// AutoRedial will try to automatically reconnect waiting for N seconds before each
// attempt, where N is the number of attempts of reconnecting. If the number of
// attempts reach 60, it will be zero'ed.
func (conn *Conn) AutoRedial(outChan chan error, onSuccess func()) {
	errChan := conn.conn.NotifyClose(make(chan *amqp.Error))

	go func() {
		var err error

		select {
		case amqpErr := <-errChan:
			err = amqpErr

			if amqpErr == nil {
				// Gracefull connection close
				return
			}
		attempts:
			outChan <- err

			if conn.attempts > 60 {
				conn.attempts = 0
			}

			// Wait n Seconds where n == conn.attempts...
			time.Sleep(time.Duration(conn.attempts) * time.Second)

			err = conn.dialFn()

			if err != nil {
				conn.attempts++
				goto attempts
			}

			conn.attempts = 0

			// enabled AutoRedial on the new connection
			conn.AutoRedial(outChan, onSuccess)
			onSuccess()
			return
		}
	}()
}
<|endoftext|>


<commit_before>package diff

import (
	"bytes"
	"fmt"
	"io"
)

// A type that satisfies Diffable can be diffed using Myers.
// A is the initial state; B is the final state.
type Diffable interface {
	LenA() int
	LenB() int
	Equal(ai, bi int) bool
}

// A type that is Writeable can be written using WriteUnified.
// A is the initial state; B is the final state.
type Writeable interface {
	WriteToA(w io.Writer, i int) (int, error)
	WriteToB(w io.Writer, i int) (int, error)
	FilenameA() string
	FilenameB() string
}

// DiffWriteable is the union of Diffable and Writeable.
// TODO: better name
type DiffWriteable interface {
	Diffable
	Writeable
}

// TODO: consider adding a StringIntern type, something like:
//
// type StringIntern struct {
// 	s map[string]*string
// }
//
// func (i *StringIntern) Bytes(b []byte) *string
// func (i *StringIntern) String(s string) *string
//
// And document what it is and why to use it.
// And consider adding helper functions to Strings and Bytes to use it.
// The reason to use it is that a lot of the execution time in diffing
// (which is an expensive operation) is taken up doing string comparisons.
// If you have paid the O(n) cost to intern all strings involved in both A and B,
// then string comparisons are reduced to cheap pointer comparisons.

// An op is a edit operation used to transform A into B.
type op int8

//go:generate stringer -type op

const (
	del op = -1
	eq  op = 0
	ins op = 1
)

// A segment is a set of steps of the same op.
type segment struct {
	FromA, ToA int // Beginning and ending indices into A of this operation
	FromB, ToB int // ditto, for B
}

func (s segment) op() op {
	if s.FromA == s.ToA {
		return ins
	}
	if s.FromB == s.ToB {
		return del
	}
	return eq
}

func (s segment) String() string {
	// This output is helpful when hacking on a Myers diff.
	// In other contexts it is usually more natural to group FromA, ToA and FromB, ToB.
	return fmt.Sprintf("(%d, %d) -- %s %d --> (%d, %d)", s.FromA, s.FromB, s.op(), s.Len(), s.ToA, s.ToB)
}

func (s segment) Len() int {
	if s.FromA == s.ToA {
		return s.ToB - s.FromB
	}
	return s.ToA - s.FromA
}

// An EditScript is an edit script to alter A into B.
type EditScript struct {
	segs []segment
}

// IsIdentity reports whether e is the identity edit script, that is, whether A and B are identical.
// See the TestHelper example.
func (e *EditScript) IsIdentity() bool {
	for _, seg := range e.segs {
		if seg.op() != eq {
			return false
		}
	}
	return true
}

// TODO: consider adding an "it just works" test helper that accepts two slices (via interface{}),
// diffs them using Strings or Bytes or Slices (using reflect.DeepEqual) as appropriate,
// and calls t.Errorf with a generated diff if they're not equal.

// scriptWithSegments returns an EditScript containing s.
// It is used to reduce line noise.
func scriptWithSegments(s ...segment) EditScript {
	return EditScript{segs: s}
}

// dump formats s for debugging.
func (s EditScript) dump() string {
	buf := new(bytes.Buffer)
	for _, seg := range s.segs {
		fmt.Fprintln(buf, seg)
	}
	return buf.String()
}
<commit_msg>diff: make EditScript.IsIdentity use a non-pointer receiver<commit_after>package diff

import (
	"bytes"
	"fmt"
	"io"
)

// A type that satisfies Diffable can be diffed using Myers.
// A is the initial state; B is the final state.
type Diffable interface {
	LenA() int
	LenB() int
	Equal(ai, bi int) bool
}

// A type that is Writeable can be written using WriteUnified.
// A is the initial state; B is the final state.
type Writeable interface {
	WriteToA(w io.Writer, i int) (int, error)
	WriteToB(w io.Writer, i int) (int, error)
	FilenameA() string
	FilenameB() string
}

// DiffWriteable is the union of Diffable and Writeable.
// TODO: better name
type DiffWriteable interface {
	Diffable
	Writeable
}

// TODO: consider adding a StringIntern type, something like:
//
// type StringIntern struct {
// 	s map[string]*string
// }
//
// func (i *StringIntern) Bytes(b []byte) *string
// func (i *StringIntern) String(s string) *string
//
// And document what it is and why to use it.
// And consider adding helper functions to Strings and Bytes to use it.
// The reason to use it is that a lot of the execution time in diffing
// (which is an expensive operation) is taken up doing string comparisons.
// If you have paid the O(n) cost to intern all strings involved in both A and B,
// then string comparisons are reduced to cheap pointer comparisons.

// An op is a edit operation used to transform A into B.
type op int8

//go:generate stringer -type op

const (
	del op = -1
	eq  op = 0
	ins op = 1
)

// A segment is a set of steps of the same op.
type segment struct {
	FromA, ToA int // Beginning and ending indices into A of this operation
	FromB, ToB int // ditto, for B
}

func (s segment) op() op {
	if s.FromA == s.ToA {
		return ins
	}
	if s.FromB == s.ToB {
		return del
	}
	return eq
}

func (s segment) String() string {
	// This output is helpful when hacking on a Myers diff.
	// In other contexts it is usually more natural to group FromA, ToA and FromB, ToB.
	return fmt.Sprintf("(%d, %d) -- %s %d --> (%d, %d)", s.FromA, s.FromB, s.op(), s.Len(), s.ToA, s.ToB)
}

func (s segment) Len() int {
	if s.FromA == s.ToA {
		return s.ToB - s.FromB
	}
	return s.ToA - s.FromA
}

// An EditScript is an edit script to alter A into B.
type EditScript struct {
	segs []segment
}

// IsIdentity reports whether e is the identity edit script, that is, whether A and B are identical.
// See the TestHelper example.
func (e EditScript) IsIdentity() bool {
	for _, seg := range e.segs {
		if seg.op() != eq {
			return false
		}
	}
	return true
}

// TODO: consider adding an "it just works" test helper that accepts two slices (via interface{}),
// diffs them using Strings or Bytes or Slices (using reflect.DeepEqual) as appropriate,
// and calls t.Errorf with a generated diff if they're not equal.

// scriptWithSegments returns an EditScript containing s.
// It is used to reduce line noise.
func scriptWithSegments(s ...segment) EditScript {
	return EditScript{segs: s}
}

// dump formats s for debugging.
func (e EditScript) dump() string {
	buf := new(bytes.Buffer)
	for _, seg := range e.segs {
		fmt.Fprintln(buf, seg)
	}
	return buf.String()
}
<|endoftext|>


<commit_before>package main

import (
	"context"
	"io"
	"log"
	"os"
	"path"
	"path/filepath"
	"strings"
	"time"

	"github.com/cncd/pipeline/pipeline"
	"github.com/cncd/pipeline/pipeline/backend"
	"github.com/cncd/pipeline/pipeline/backend/docker"
	"github.com/cncd/pipeline/pipeline/frontend"
	"github.com/cncd/pipeline/pipeline/frontend/yaml"
	"github.com/cncd/pipeline/pipeline/frontend/yaml/compiler"
	"github.com/cncd/pipeline/pipeline/interrupt"
	"github.com/cncd/pipeline/pipeline/multipart"
	"github.com/drone/envsubst"

	"github.com/urfave/cli"
)

var execCmd = cli.Command{
	Name:  "exec",
	Usage: "execute a local build",
	Action: func(c *cli.Context) {
		if err := exec(c); err != nil {
			log.Fatalln(err)
		}
	},
	Flags: []cli.Flag{
		cli.BoolTFlag{
			Name:   "local",
			Usage:  "build from local directory",
			EnvVar: "DRONE_LOCAL",
		},
		cli.DurationFlag{
			Name:   "timeout",
			Usage:  "build timeout",
			Value:  time.Hour,
			EnvVar: "DRONE_TIMEOUT",
		},
		cli.StringSliceFlag{
			Name:   "volumes",
			Usage:  "build volumes",
			EnvVar: "DRONE_VOLUMES",
		},
		cli.StringSliceFlag{
			Name:  "privileged",
			Usage: "privileged plugins",
			Value: &cli.StringSlice{
				"plugins/docker",
				"plugins/gcr",
				"plugins/ecr",
			},
		},

		//
		// Please note the below flags are mirrored in the pipec and
		// should be kept synchronized. Do not edit directly
		// https://github.com/cncd/pipeline/pipec
		//

		//
		// workspace default
		//
		cli.StringFlag{
			Name:   "workspace-base",
			Value:  "/pipeline",
			EnvVar: "DRONE_WORKSPACE_BASE",
		},
		cli.StringFlag{
			Name:   "workspace-path",
			Value:  "src",
			EnvVar: "DRONE_WORKSPACE_PATH",
		},
		//
		// netrc parameters
		//
		cli.StringFlag{
			Name:   "netrc-username",
			EnvVar: "DRONE_NETRC_USERNAME",
		},
		cli.StringFlag{
			Name:   "netrc-password",
			EnvVar: "DRONE_NETRC_PASSWORD",
		},
		cli.StringFlag{
			Name:   "netrc-machine",
			EnvVar: "DRONE_NETRC_MACHINE",
		},
		//
		// metadata parameters
		//
		cli.StringFlag{
			Name:   "system-arch",
			Value:  "linux/amd64",
			EnvVar: "DRONE_SYSTEM_ARCH",
		},
		cli.StringFlag{
			Name:   "system-name",
			Value:  "pipec",
			EnvVar: "DRONE_SYSTEM_NAME",
		},
		cli.StringFlag{
			Name:   "system-link",
			Value:  "https://github.com/cncd/pipec",
			EnvVar: "DRONE_SYSTEM_LINK",
		},
		cli.StringFlag{
			Name:   "repo-name",
			EnvVar: "DRONE_REPO_NAME",
		},
		cli.StringFlag{
			Name:   "repo-link",
			EnvVar: "DRONE_REPO_LINK",
		},
		cli.StringFlag{
			Name:   "repo-remote-url",
			EnvVar: "DRONE_REPO_REMOTE",
		},
		cli.StringFlag{
			Name:   "repo-private",
			EnvVar: "DRONE_REPO_PRIVATE",
		},
		cli.IntFlag{
			Name:   "build-number",
			EnvVar: "DRONE_BUILD_NUMBER",
		},
		cli.Int64Flag{
			Name:   "build-created",
			EnvVar: "DRONE_BUILD_CREATED",
		},
		cli.Int64Flag{
			Name:   "build-started",
			EnvVar: "DRONE_BUILD_STARTED",
		},
		cli.Int64Flag{
			Name:   "build-finished",
			EnvVar: "DRONE_BUILD_FINISHED",
		},
		cli.StringFlag{
			Name:   "build-status",
			EnvVar: "DRONE_BUILD_STATUS",
		},
		cli.StringFlag{
			Name:   "build-event",
			EnvVar: "DRONE_BUILD_EVENT",
		},
		cli.StringFlag{
			Name:   "build-link",
			EnvVar: "DRONE_BUILD_LINK",
		},
		cli.StringFlag{
			Name:   "build-target",
			EnvVar: "DRONE_BUILD_TARGET",
		},
		cli.StringFlag{
			Name:   "commit-sha",
			EnvVar: "DRONE_COMMIT_SHA",
		},
		cli.StringFlag{
			Name:   "commit-ref",
			EnvVar: "DRONE_COMMIT_REF",
		},
		cli.StringFlag{
			Name:   "commit-refspec",
			EnvVar: "DRONE_COMMIT_REFSPEC",
		},
		cli.StringFlag{
			Name:   "commit-branch",
			EnvVar: "DRONE_COMMIT_BRANCH",
		},
		cli.StringFlag{
			Name:   "commit-message",
			EnvVar: "DRONE_COMMIT_MESSAGE",
		},
		cli.StringFlag{
			Name:   "commit-author-name",
			EnvVar: "DRONE_COMMIT_AUTHOR_NAME",
		},
		cli.StringFlag{
			Name:   "commit-author-avatar",
			EnvVar: "DRONE_COMMIT_AUTHOR_AVATAR",
		},
		cli.StringFlag{
			Name:   "commit-author-email",
			EnvVar: "DRONE_COMMIT_AUTHOR_EMAIL",
		},
		cli.IntFlag{
			Name:   "prev-build-number",
			EnvVar: "DRONE_PREV_BUILD_NUMBER",
		},
		cli.Int64Flag{
			Name:   "prev-build-created",
			EnvVar: "DRONE_PREV_BUILD_CREATED",
		},
		cli.Int64Flag{
			Name:   "prev-build-started",
			EnvVar: "DRONE_PREV_BUILD_STARTED",
		},
		cli.Int64Flag{
			Name:   "prev-build-finished",
			EnvVar: "DRONE_PREV_BUILD_FINISHED",
		},
		cli.StringFlag{
			Name:   "prev-build-status",
			EnvVar: "DRONE_PREV_BUILD_STATUS",
		},
		cli.StringFlag{
			Name:   "prev-build-event",
			EnvVar: "DRONE_PREV_BUILD_EVENT",
		},
		cli.StringFlag{
			Name:   "prev-build-link",
			EnvVar: "DRONE_PREV_BUILD_LINK",
		},
		cli.StringFlag{
			Name:   "prev-commit-sha",
			EnvVar: "DRONE_PREV_COMMIT_SHA",
		},
		cli.StringFlag{
			Name:   "prev-commit-ref",
			EnvVar: "DRONE_PREV_COMMIT_REF",
		},
		cli.StringFlag{
			Name:   "prev-commit-refspec",
			EnvVar: "DRONE_PREV_COMMIT_REFSPEC",
		},
		cli.StringFlag{
			Name:   "prev-commit-branch",
			EnvVar: "DRONE_PREV_COMMIT_BRANCH",
		},
		cli.StringFlag{
			Name:   "prev-commit-message",
			EnvVar: "DRONE_PREV_COMMIT_MESSAGE",
		},
		cli.StringFlag{
			Name:   "prev-commit-author-name",
			EnvVar: "DRONE_PREV_COMMIT_AUTHOR_NAME",
		},
		cli.StringFlag{
			Name:   "prev-commit-author-avatar",
			EnvVar: "DRONE_PREV_COMMIT_AUTHOR_AVATAR",
		},
		cli.StringFlag{
			Name:   "prev-commit-author-email",
			EnvVar: "DRONE_PREV_COMMIT_AUTHOR_EMAIL",
		},
		cli.IntFlag{
			Name:   "job-number",
			EnvVar: "DRONE_JOB_NUMBER",
		},
	},
}

func exec(c *cli.Context) error {
	file := c.Args().First()
	if file == "" {
		file = ".drone.yml"
	}

	metadata := metadataFromContext(c)
	environ := metadata.Environ()
	for k, v := range metadata.EnvironDrone() {
		environ[k] = v
	}
	for _, env := range os.Environ() {
		k := strings.Split(env, "=")[0]
		v := strings.Split(env, "=")[1]
		environ[k] = v
	}

	tmpl, err := envsubst.ParseFile(file)
	if err != nil {
		return err
	}
	confstr, err := tmpl.Execute(func(name string) string {
		return environ[name]
	})
	if err != nil {
		return err
	}

	conf, err := yaml.ParseString(confstr)
	if err != nil {
		return err
	}

	// configure volumes for local execution
	volumes := c.StringSlice("volumes")
	if c.Bool("local") {
		var (
			workspaceBase = conf.Workspace.Base
			workspacePath = conf.Workspace.Path
		)
		if workspaceBase == "" {
			workspaceBase = c.String("workspace-base")
		}
		if workspacePath == "" {
			workspacePath = c.String("workspace-path")
		}
		dir, _ := filepath.Abs(filepath.Dir(file))
		volumes = append(volumes, dir+":"+path.Join(workspaceBase, workspacePath))
	}

	// compiles the yaml file
	compiled := compiler.New(
		compiler.WithEscalated(
			c.StringSlice("privileged")...,
		),
		compiler.WithVolumes(volumes...),
		compiler.WithWorkspace(
			c.String("workspace-base"),
			c.String("workspace-path"),
		),
		compiler.WithPrefix(
			c.String("prefix"),
		),
		compiler.WithProxy(),
		compiler.WithLocal(
			c.Bool("local"),
		),
		compiler.WithNetrc(
			c.String("netrc-username"),
			c.String("netrc-password"),
			c.String("netrc-machine"),
		),
		compiler.WithMetadata(metadata),
	).Compile(conf)

	engine, err := docker.NewEnv()
	if err != nil {
		return err
	}

	ctx, cancel := context.WithTimeout(context.Background(), c.Duration("timeout"))
	defer cancel()
	ctx = interrupt.WithContext(ctx)

	return pipeline.New(compiled,
		pipeline.WithContext(ctx),
		pipeline.WithLogger(defaultLogger),
		pipeline.WithTracer(pipeline.DefaultTracer),
		pipeline.WithLogger(defaultLogger),
		pipeline.WithEngine(engine),
	).Run()
}

// return the metadata from the cli context.
func metadataFromContext(c *cli.Context) frontend.Metadata {
	return frontend.Metadata{
		Repo: frontend.Repo{
			Name:    c.String("repo-name"),
			Link:    c.String("repo-link"),
			Remote:  c.String("repo-remote-url"),
			Private: c.Bool("repo-private"),
		},
		Curr: frontend.Build{
			Number:   c.Int("build-number"),
			Created:  c.Int64("build-created"),
			Started:  c.Int64("build-started"),
			Finished: c.Int64("build-finished"),
			Status:   c.String("build-status"),
			Event:    c.String("build-event"),
			Link:     c.String("build-link"),
			Target:   c.String("build-target"),
			Commit: frontend.Commit{
				Sha:     c.String("commit-sha"),
				Ref:     c.String("commit-ref"),
				Refspec: c.String("commit-refspec"),
				Branch:  c.String("commit-branch"),
				Message: c.String("commit-message"),
				Author: frontend.Author{
					Name:   c.String("commit-author-name"),
					Email:  c.String("commit-author-email"),
					Avatar: c.String("commit-author-avatar"),
				},
			},
		},
		Prev: frontend.Build{
			Number:   c.Int("prev-build-number"),
			Created:  c.Int64("prev-build-created"),
			Started:  c.Int64("prev-build-started"),
			Finished: c.Int64("prev-build-finished"),
			Status:   c.String("prev-build-status"),
			Event:    c.String("prev-build-event"),
			Link:     c.String("prev-build-link"),
			Commit: frontend.Commit{
				Sha:     c.String("prev-commit-sha"),
				Ref:     c.String("prev-commit-ref"),
				Refspec: c.String("prev-commit-refspec"),
				Branch:  c.String("prev-commit-branch"),
				Message: c.String("prev-commit-message"),
				Author: frontend.Author{
					Name:   c.String("prev-commit-author-name"),
					Email:  c.String("prev-commit-author-email"),
					Avatar: c.String("prev-commit-author-avatar"),
				},
			},
		},
		Job: frontend.Job{
			Number: c.Int("job-number"),
		},
		Sys: frontend.System{
			Name: c.String("system-name"),
			Link: c.String("system-link"),
			Arch: c.String("system-arch"),
		},
	}
}

var defaultLogger = pipeline.LogFunc(func(proc *backend.Step, rc multipart.Reader) error {
	part, err := rc.NextPart()
	if err != nil {
		return err
	}
	io.Copy(os.Stderr, part)
	return nil
})
<commit_msg>provide default prefix for drone exec<commit_after>package main

import (
	"context"
	"io"
	"log"
	"os"
	"path"
	"path/filepath"
	"strings"
	"time"

	"github.com/cncd/pipeline/pipeline"
	"github.com/cncd/pipeline/pipeline/backend"
	"github.com/cncd/pipeline/pipeline/backend/docker"
	"github.com/cncd/pipeline/pipeline/frontend"
	"github.com/cncd/pipeline/pipeline/frontend/yaml"
	"github.com/cncd/pipeline/pipeline/frontend/yaml/compiler"
	"github.com/cncd/pipeline/pipeline/interrupt"
	"github.com/cncd/pipeline/pipeline/multipart"
	"github.com/drone/envsubst"

	"github.com/urfave/cli"
)

var execCmd = cli.Command{
	Name:  "exec",
	Usage: "execute a local build",
	Action: func(c *cli.Context) {
		if err := exec(c); err != nil {
			log.Fatalln(err)
		}
	},
	Flags: []cli.Flag{
		cli.BoolTFlag{
			Name:   "local",
			Usage:  "build from local directory",
			EnvVar: "DRONE_LOCAL",
		},
		cli.DurationFlag{
			Name:   "timeout",
			Usage:  "build timeout",
			Value:  time.Hour,
			EnvVar: "DRONE_TIMEOUT",
		},
		cli.StringSliceFlag{
			Name:   "volumes",
			Usage:  "build volumes",
			EnvVar: "DRONE_VOLUMES",
		},
		cli.StringSliceFlag{
			Name:   "network",
			Usage:  "external networks",
			EnvVar: "DRONE_NETWORKS",
		},
		cli.StringFlag{
			Name:   "prefix",
			Value:  "drone",
			Usage:  "prefix containers created by drone",
			EnvVar: "DRONE_DOCKER_PREFIX",
			Hidden: true,
		},
		cli.StringSliceFlag{
			Name:  "privileged",
			Usage: "privileged plugins",
			Value: &cli.StringSlice{
				"plugins/docker",
				"plugins/gcr",
				"plugins/ecr",
			},
		},

		//
		// Please note the below flags are mirrored in the pipec and
		// should be kept synchronized. Do not edit directly
		// https://github.com/cncd/pipeline/pipec
		//

		//
		// workspace default
		//
		cli.StringFlag{
			Name:   "workspace-base",
			Value:  "/pipeline",
			EnvVar: "DRONE_WORKSPACE_BASE",
		},
		cli.StringFlag{
			Name:   "workspace-path",
			Value:  "src",
			EnvVar: "DRONE_WORKSPACE_PATH",
		},
		//
		// netrc parameters
		//
		cli.StringFlag{
			Name:   "netrc-username",
			EnvVar: "DRONE_NETRC_USERNAME",
		},
		cli.StringFlag{
			Name:   "netrc-password",
			EnvVar: "DRONE_NETRC_PASSWORD",
		},
		cli.StringFlag{
			Name:   "netrc-machine",
			EnvVar: "DRONE_NETRC_MACHINE",
		},
		//
		// metadata parameters
		//
		cli.StringFlag{
			Name:   "system-arch",
			Value:  "linux/amd64",
			EnvVar: "DRONE_SYSTEM_ARCH",
		},
		cli.StringFlag{
			Name:   "system-name",
			Value:  "pipec",
			EnvVar: "DRONE_SYSTEM_NAME",
		},
		cli.StringFlag{
			Name:   "system-link",
			Value:  "https://github.com/cncd/pipec",
			EnvVar: "DRONE_SYSTEM_LINK",
		},
		cli.StringFlag{
			Name:   "repo-name",
			EnvVar: "DRONE_REPO_NAME",
		},
		cli.StringFlag{
			Name:   "repo-link",
			EnvVar: "DRONE_REPO_LINK",
		},
		cli.StringFlag{
			Name:   "repo-remote-url",
			EnvVar: "DRONE_REPO_REMOTE",
		},
		cli.StringFlag{
			Name:   "repo-private",
			EnvVar: "DRONE_REPO_PRIVATE",
		},
		cli.IntFlag{
			Name:   "build-number",
			EnvVar: "DRONE_BUILD_NUMBER",
		},
		cli.Int64Flag{
			Name:   "build-created",
			EnvVar: "DRONE_BUILD_CREATED",
		},
		cli.Int64Flag{
			Name:   "build-started",
			EnvVar: "DRONE_BUILD_STARTED",
		},
		cli.Int64Flag{
			Name:   "build-finished",
			EnvVar: "DRONE_BUILD_FINISHED",
		},
		cli.StringFlag{
			Name:   "build-status",
			EnvVar: "DRONE_BUILD_STATUS",
		},
		cli.StringFlag{
			Name:   "build-event",
			EnvVar: "DRONE_BUILD_EVENT",
		},
		cli.StringFlag{
			Name:   "build-link",
			EnvVar: "DRONE_BUILD_LINK",
		},
		cli.StringFlag{
			Name:   "build-target",
			EnvVar: "DRONE_BUILD_TARGET",
		},
		cli.StringFlag{
			Name:   "commit-sha",
			EnvVar: "DRONE_COMMIT_SHA",
		},
		cli.StringFlag{
			Name:   "commit-ref",
			EnvVar: "DRONE_COMMIT_REF",
		},
		cli.StringFlag{
			Name:   "commit-refspec",
			EnvVar: "DRONE_COMMIT_REFSPEC",
		},
		cli.StringFlag{
			Name:   "commit-branch",
			EnvVar: "DRONE_COMMIT_BRANCH",
		},
		cli.StringFlag{
			Name:   "commit-message",
			EnvVar: "DRONE_COMMIT_MESSAGE",
		},
		cli.StringFlag{
			Name:   "commit-author-name",
			EnvVar: "DRONE_COMMIT_AUTHOR_NAME",
		},
		cli.StringFlag{
			Name:   "commit-author-avatar",
			EnvVar: "DRONE_COMMIT_AUTHOR_AVATAR",
		},
		cli.StringFlag{
			Name:   "commit-author-email",
			EnvVar: "DRONE_COMMIT_AUTHOR_EMAIL",
		},
		cli.IntFlag{
			Name:   "prev-build-number",
			EnvVar: "DRONE_PREV_BUILD_NUMBER",
		},
		cli.Int64Flag{
			Name:   "prev-build-created",
			EnvVar: "DRONE_PREV_BUILD_CREATED",
		},
		cli.Int64Flag{
			Name:   "prev-build-started",
			EnvVar: "DRONE_PREV_BUILD_STARTED",
		},
		cli.Int64Flag{
			Name:   "prev-build-finished",
			EnvVar: "DRONE_PREV_BUILD_FINISHED",
		},
		cli.StringFlag{
			Name:   "prev-build-status",
			EnvVar: "DRONE_PREV_BUILD_STATUS",
		},
		cli.StringFlag{
			Name:   "prev-build-event",
			EnvVar: "DRONE_PREV_BUILD_EVENT",
		},
		cli.StringFlag{
			Name:   "prev-build-link",
			EnvVar: "DRONE_PREV_BUILD_LINK",
		},
		cli.StringFlag{
			Name:   "prev-commit-sha",
			EnvVar: "DRONE_PREV_COMMIT_SHA",
		},
		cli.StringFlag{
			Name:   "prev-commit-ref",
			EnvVar: "DRONE_PREV_COMMIT_REF",
		},
		cli.StringFlag{
			Name:   "prev-commit-refspec",
			EnvVar: "DRONE_PREV_COMMIT_REFSPEC",
		},
		cli.StringFlag{
			Name:   "prev-commit-branch",
			EnvVar: "DRONE_PREV_COMMIT_BRANCH",
		},
		cli.StringFlag{
			Name:   "prev-commit-message",
			EnvVar: "DRONE_PREV_COMMIT_MESSAGE",
		},
		cli.StringFlag{
			Name:   "prev-commit-author-name",
			EnvVar: "DRONE_PREV_COMMIT_AUTHOR_NAME",
		},
		cli.StringFlag{
			Name:   "prev-commit-author-avatar",
			EnvVar: "DRONE_PREV_COMMIT_AUTHOR_AVATAR",
		},
		cli.StringFlag{
			Name:   "prev-commit-author-email",
			EnvVar: "DRONE_PREV_COMMIT_AUTHOR_EMAIL",
		},
		cli.IntFlag{
			Name:   "job-number",
			EnvVar: "DRONE_JOB_NUMBER",
		},
	},
}

func exec(c *cli.Context) error {
	file := c.Args().First()
	if file == "" {
		file = ".drone.yml"
	}

	metadata := metadataFromContext(c)
	environ := metadata.Environ()
	for k, v := range metadata.EnvironDrone() {
		environ[k] = v
	}
	for _, env := range os.Environ() {
		k := strings.Split(env, "=")[0]
		v := strings.Split(env, "=")[1]
		environ[k] = v
	}

	tmpl, err := envsubst.ParseFile(file)
	if err != nil {
		return err
	}
	confstr, err := tmpl.Execute(func(name string) string {
		return environ[name]
	})
	if err != nil {
		return err
	}

	conf, err := yaml.ParseString(confstr)
	if err != nil {
		return err
	}

	// configure volumes for local execution
	volumes := c.StringSlice("volumes")
	if c.Bool("local") {
		var (
			workspaceBase = conf.Workspace.Base
			workspacePath = conf.Workspace.Path
		)
		if workspaceBase == "" {
			workspaceBase = c.String("workspace-base")
		}
		if workspacePath == "" {
			workspacePath = c.String("workspace-path")
		}
		dir, _ := filepath.Abs(filepath.Dir(file))
		volumes = append(volumes, dir+":"+path.Join(workspaceBase, workspacePath))
	}

	// compiles the yaml file
	compiled := compiler.New(
		compiler.WithEscalated(
			c.StringSlice("privileged")...,
		),
		compiler.WithVolumes(volumes...),
		compiler.WithWorkspace(
			c.String("workspace-base"),
			c.String("workspace-path"),
		),
		compiler.WithNetworks(
			c.StringSlice("network")...,
		),
		compiler.WithPrefix(
			c.String("prefix"),
		),
		compiler.WithProxy(),
		compiler.WithLocal(
			c.Bool("local"),
		),
		compiler.WithNetrc(
			c.String("netrc-username"),
			c.String("netrc-password"),
			c.String("netrc-machine"),
		),
		compiler.WithMetadata(metadata),
	).Compile(conf)

	engine, err := docker.NewEnv()
	if err != nil {
		return err
	}

	ctx, cancel := context.WithTimeout(context.Background(), c.Duration("timeout"))
	defer cancel()
	ctx = interrupt.WithContext(ctx)

	return pipeline.New(compiled,
		pipeline.WithContext(ctx),
		pipeline.WithLogger(defaultLogger),
		pipeline.WithTracer(pipeline.DefaultTracer),
		pipeline.WithLogger(defaultLogger),
		pipeline.WithEngine(engine),
	).Run()
}

// return the metadata from the cli context.
func metadataFromContext(c *cli.Context) frontend.Metadata {
	return frontend.Metadata{
		Repo: frontend.Repo{
			Name:    c.String("repo-name"),
			Link:    c.String("repo-link"),
			Remote:  c.String("repo-remote-url"),
			Private: c.Bool("repo-private"),
		},
		Curr: frontend.Build{
			Number:   c.Int("build-number"),
			Created:  c.Int64("build-created"),
			Started:  c.Int64("build-started"),
			Finished: c.Int64("build-finished"),
			Status:   c.String("build-status"),
			Event:    c.String("build-event"),
			Link:     c.String("build-link"),
			Target:   c.String("build-target"),
			Commit: frontend.Commit{
				Sha:     c.String("commit-sha"),
				Ref:     c.String("commit-ref"),
				Refspec: c.String("commit-refspec"),
				Branch:  c.String("commit-branch"),
				Message: c.String("commit-message"),
				Author: frontend.Author{
					Name:   c.String("commit-author-name"),
					Email:  c.String("commit-author-email"),
					Avatar: c.String("commit-author-avatar"),
				},
			},
		},
		Prev: frontend.Build{
			Number:   c.Int("prev-build-number"),
			Created:  c.Int64("prev-build-created"),
			Started:  c.Int64("prev-build-started"),
			Finished: c.Int64("prev-build-finished"),
			Status:   c.String("prev-build-status"),
			Event:    c.String("prev-build-event"),
			Link:     c.String("prev-build-link"),
			Commit: frontend.Commit{
				Sha:     c.String("prev-commit-sha"),
				Ref:     c.String("prev-commit-ref"),
				Refspec: c.String("prev-commit-refspec"),
				Branch:  c.String("prev-commit-branch"),
				Message: c.String("prev-commit-message"),
				Author: frontend.Author{
					Name:   c.String("prev-commit-author-name"),
					Email:  c.String("prev-commit-author-email"),
					Avatar: c.String("prev-commit-author-avatar"),
				},
			},
		},
		Job: frontend.Job{
			Number: c.Int("job-number"),
		},
		Sys: frontend.System{
			Name: c.String("system-name"),
			Link: c.String("system-link"),
			Arch: c.String("system-arch"),
		},
	}
}

var defaultLogger = pipeline.LogFunc(func(proc *backend.Step, rc multipart.Reader) error {
	part, err := rc.NextPart()
	if err != nil {
		return err
	}
	io.Copy(os.Stderr, part)
	return nil
})
<|endoftext|>


<commit_before>package dsl

import (
	"bufio"
	"bytes"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"log"
	"net"
	"net/url"
	"regexp"
	"strconv"
	"strings"
	"time"

	"github.com/pact-foundation/pact-go/client"
	"github.com/pact-foundation/pact-go/types"
)

// Client is the interface
type Client interface {
	// StartServer starts a remote Pact Mock Server.
	StartServer(args []string, port int) *types.MockServer

	// ListServers lists all known Mock Servers
	ListServers() []*types.MockServer

	// StopServer stops a remote Pact Mock Server.
	StopServer(server *types.MockServer) (*types.MockServer, error)

	// RemoveAllServers stops all remote Pact Mock Servers.
	RemoveAllServers(server *types.MockServer) []*types.MockServer

	// VerifyProvider runs the verification process against a running Provider.
	VerifyProvider(request types.VerifyRequest) ([]types.ProviderVerifierResponse, error)

	// UpdateMessagePact adds a pact message to a contract file
	UpdateMessagePact(request types.PactMessageRequest) error

	// ReifyMessage takes a structured object, potentially containing nested Matchers
	// and returns an object with just the example (generated) content
	// The object may be a simple JSON primitive e.g. string or number or a complex object
	ReifyMessage(request *types.PactReificationRequest) (res *types.ReificationResponse, err error)

	// PublishPacts publishes pact files to a Pact Broker
	PublishPacts(request types.PublishRequest) error
}

// PactClient is the main interface into starting/stopping
// the underlying Pact CLI subsystem
type PactClient struct {
	pactMockSvcManager     client.Service
	verificationSvcManager client.Service
	messageSvcManager      client.Service
	publishSvcManager      client.Service

	// Track mock servers
	Servers []MockService

	// Network Daemon is listening on
	Network string

	// Address the Daemon is listening on
	Address string

	// TimeoutDuration specifies how long to wait for Pact CLI to start
	TimeoutDuration time.Duration
}

// newClient creates a new Pact client manager with the provided services
func newClient(MockServiceManager client.Service, verificationServiceManager client.Service, messageServiceManager client.Service, publishServiceManager client.Service) *PactClient {
	MockServiceManager.Setup()
	verificationServiceManager.Setup()
	messageServiceManager.Setup()
	publishServiceManager.Setup()

	return &PactClient{
		pactMockSvcManager:     MockServiceManager,
		verificationSvcManager: verificationServiceManager,
		messageSvcManager:      messageServiceManager,
		publishSvcManager:      publishServiceManager,
		TimeoutDuration:        10 * time.Second,
	}
}

// NewClient creates a new Pact client manager with defaults
func NewClient() *PactClient {
	return newClient(&client.MockService{}, &client.VerificationService{}, &client.MessageService{}, &client.PublishService{})
}

// StartServer starts a remote Pact Mock Server.
func (p *PactClient) StartServer(args []string, port int) *types.MockServer {
	log.Println("[DEBUG] client: starting a server with args:", args, "port:", port)
	args = append(args, []string{"--port", strconv.Itoa(port)}...)
	svc := p.pactMockSvcManager.NewService(args)
	cmd := svc.Start()

	waitForPort(port, p.getNetworkInterface(), p.Address, p.TimeoutDuration,
		fmt.Sprintf(`Timed out waiting for Mock Server to start on port %d - are you sure it's running?`, port))

	return &types.MockServer{
		Pid:  cmd.Process.Pid,
		Port: port,
	}
}

// ListServers lists all known Mock Servers
func (p *PactClient) ListServers() []*types.MockServer {
	log.Println("[DEBUG] client: starting a server")

	var servers []*types.MockServer

	for port, s := range p.pactMockSvcManager.List() {
		servers = append(servers, &types.MockServer{
			Pid:  s.Process.Pid,
			Port: port,
		})
	}

	return servers
}

// StopServer stops a remote Pact Mock Server.
func (p *PactClient) StopServer(server *types.MockServer) (*types.MockServer, error) {
	log.Println("[DEBUG] client: stop server")

	// TODO: Need to be able to get a non-zero exit code here!
	_, server.Error = p.pactMockSvcManager.Stop(server.Pid)
	return server, server.Error
}

// RemoveAllServers stops all remote Pact Mock Servers.
func (p *PactClient) RemoveAllServers(server *types.MockServer) []*types.MockServer {
	log.Println("[DEBUG] client: stop server")

	for _, s := range p.verificationSvcManager.List() {
		if s != nil {
			p.pactMockSvcManager.Stop(s.Process.Pid)
		}
	}
	return nil
}

// VerifyProvider runs the verification process against a running Provider.
// TODO: extract/refactor the stdout/error streaems from these functions
func (p *PactClient) VerifyProvider(request types.VerifyRequest) ([]types.ProviderVerifierResponse, error) {
	log.Println("[DEBUG] client: verifying a provider")
	response := make([]types.ProviderVerifierResponse, 0)

	// Convert request into flags, and validate request
	err := request.Validate()
	if err != nil {
		return response, err
	}

	address := getAddress(request.ProviderBaseURL)
	port := getPort(request.ProviderBaseURL)

	err = waitForPort(port, p.getNetworkInterface(), address, p.TimeoutDuration,
		fmt.Sprintf(`Timed out waiting for Provider API to start on port %d - are you sure it's running?`, port))

	if err != nil {
		return response, err
	}

	// Run command, splitting out stderr and stdout. The command can fail for
	// several reasons:
	// 1. Command is unable to run at all.
	// 2. Command runs, but fails for unknown reason.
	// 3. Command runs, and returns exit status 1 because the tests fail.
	//
	// First, attempt to decode the response of the stdout.
	// If that is successful, we are at case 3. Return stdout as message, no error.
	// Else, return an error, include stderr and stdout in both the error and message.
	svc := p.verificationSvcManager.NewService(request.Args)
	cmd := svc.Command()

	stdOutPipe, err := cmd.StdoutPipe()
	if err != nil {
		return response, err
	}
	stdErrPipe, err := cmd.StderrPipe()
	if err != nil {
		return response, err
	}

	// Buffered channel: wait for all reading to complete
	done := make(chan struct{}, 2)
	verifications := []string{}
	var stdErr strings.Builder

	// Split by lines, as the content is JSONL formatted
	// Each pact is verified by line, and the results (as JSON) sent to stdout.
	// See https://github.com/pact-foundation/pact-go/issues/88#issuecomment-404686337
	stdOutScanner := bufio.NewScanner(stdOutPipe)
	go func() {
		stdOutBuf := make([]byte, bufio.MaxScanTokenSize)
		stdOutScanner.Buffer(stdOutBuf, 64*1024*1024)

		for stdOutScanner.Scan() {
			verifications = append(verifications, stdOutScanner.Text())
		}

		done <- struct{}{}
	}()

	// Scrape errors
	stdErrScanner := bufio.NewScanner(stdErrPipe)
	go func() {
		for stdErrScanner.Scan() {
			stdErr.WriteString(fmt.Sprintf("%s\n", stdErrScanner.Text()))
		}

		done <- struct{}{}
	}()

	err = cmd.Start()
	if err != nil {
		return response, err
	}

	// Wait for watch goroutine before Cmd.Wait(), race condition!
	<-done

	err = cmd.Wait()

	var verification types.ProviderVerifierResponse
	for _, v := range verifications {
		v = strings.TrimSpace(v)

		// TODO: fix once https://github.com/pact-foundation/pact-provider-verifier/issues/26
		//       is addressed
		// logging to stdout breaks the JSON response
		// https://github.com/pact-foundation/pact-ruby/commit/06fa61581512ba5570c315d089f2c0fc23c8cb11
		if v != "" && strings.Index(v, "INFO") != 0 {
			dErr := json.Unmarshal([]byte(v), &verification)

			response = append(response, verification)

			if dErr != nil {
				err = dErr
			}
		}
	}

	if err == nil {
		return response, err
	}

	return response, fmt.Errorf("error verifying provider: %s\n\nSTDERR:\n%s\n\nSTDOUT:\n%s", err, stdErr.String(), strings.Join(verifications, "\n"))
}

// UpdateMessagePact adds a pact message to a contract file
func (p *PactClient) UpdateMessagePact(request types.PactMessageRequest) error {
	log.Println("[DEBUG] client: adding pact message...")

	// Convert request into flags, and validate request
	err := request.Validate()
	if err != nil {
		return err
	}

	svc := p.messageSvcManager.NewService(request.Args)
	cmd := svc.Command()

	stdOutPipe, err := cmd.StdoutPipe()
	if err != nil {
		return err
	}
	stdErrPipe, err := cmd.StderrPipe()
	if err != nil {
		return err
	}
	err = cmd.Start()
	if err != nil {
		return err
	}
	stdOut, err := ioutil.ReadAll(stdOutPipe)
	if err != nil {
		return err
	}
	stdErr, err := ioutil.ReadAll(stdErrPipe)
	if err != nil {
		return err
	}

	err = cmd.Wait()

	if err == nil {
		return nil
	}

	return fmt.Errorf("error creating message: %s\n\nSTDERR:\n%s\n\nSTDOUT:\n%s", err, stdErr, stdOut)
}

// PublishPacts publishes a set of pacts to a pact broker
func (p *PactClient) PublishPacts(request types.PublishRequest) error {
	svc := p.publishSvcManager.NewService(request.Args)
	log.Println("[DEBUG] about to publish pacts")
	cmd := svc.Start()

	log.Println("[DEBUG] waiting for response")
	err := cmd.Wait()

	log.Println("[DEBUG] response from publish", err)

	return err
}

// ReifyMessage takes a structured object, potentially containing nested Matchers
// and returns an object with just the example (generated) content
// The object may be a simple JSON primitive e.g. string or number or a complex object
func (p *PactClient) ReifyMessage(request *types.PactReificationRequest) (res *types.ReificationResponse, err error) {
	log.Println("[DEBUG] client: adding pact message...")

	var responseObject interface{}
	res = &types.ReificationResponse{
		Response: responseObject,
	}

	// Convert request into flags, and validate request
	err = request.Validate()
	if err != nil {
		return
	}

	svc := p.messageSvcManager.NewService(request.Args)
	cmd := svc.Command()

	stdOutPipe, err := cmd.StdoutPipe()
	if err != nil {
		return
	}
	stdErrPipe, err := cmd.StderrPipe()
	if err != nil {
		return
	}
	err = cmd.Start()
	if err != nil {
		return
	}
	stdOut, err := ioutil.ReadAll(stdOutPipe)
	if err != nil {
		return
	}
	stdErr, err := ioutil.ReadAll(stdErrPipe)
	if err != nil {
		return
	}

	err = cmd.Wait()

	res.ResponseRaw = stdOut
	decoder := json.NewDecoder(bytes.NewReader(stdOut))

	dErr := decoder.Decode(&res.Response)
	if dErr == nil {
		return
	}

	if err == nil {
		err = dErr
	}

	if err == nil {
		return
	}

	err = fmt.Errorf("error creating message: %s\n\nSTDERR:\n%s\n\nSTDOUT:\n%s", err, stdErr, stdOut)

	return
}

// Get a port given a URL
func getPort(rawURL string) int {
	parsedURL, err := url.Parse(rawURL)
	if err == nil {
		splitHost := strings.Split(parsedURL.Host, ":")
		if len(splitHost) == 2 {
			port, err := strconv.Atoi(splitHost[1])
			if err == nil {
				return port
			}
		}
		if parsedURL.Scheme == "https" {
			return 443
		}
		return 80
	}

	return -1
}

// Get the address given a URL
func getAddress(rawURL string) string {
	parsedURL, err := url.Parse(rawURL)
	if err != nil {
		return ""
	}

	splitHost := strings.Split(parsedURL.Host, ":")
	return splitHost[0]
}

// Use this to wait for a port to be running prior
// to running tests.
var waitForPort = func(port int, network string, address string, timeoutDuration time.Duration, message string) error {
	log.Println("[DEBUG] waiting for port", port, "to become available")
	timeout := time.After(timeoutDuration)

	for {
		select {
		case <-timeout:
			log.Printf("[ERROR] Expected server to start < %s. %s", timeoutDuration, message)
			return fmt.Errorf("Expected server to start < %s. %s", timeoutDuration, message)
		case <-time.After(50 * time.Millisecond):
			_, err := net.Dial(network, fmt.Sprintf("%s:%d", address, port))
			if err == nil {
				return nil
			}
		}
	}
}

// sanitiseRubyResponse removes Ruby-isms from the response content
// making the output much more human readable
func sanitiseRubyResponse(response string) string {
	log.Println("[TRACE] response from Ruby process pre-sanitisation:", response)

	r := regexp.MustCompile("(?m)^\\s*#.*$")
	s := r.ReplaceAllString(response, "")

	r = regexp.MustCompile("(?m).*bundle exec rake pact:verify.*$")
	s = r.ReplaceAllString(s, "")

	r = regexp.MustCompile("\\n+")
	s = r.ReplaceAllString(s, "\n")

	return s
}

// getNetworkInterface returns a default interface to communicate to the Daemon
// if none specified
func (p *PactClient) getNetworkInterface() string {
	if p.Network == "" {
		return "tcp"
	}
	return p.Network
}
<commit_msg>fix: race in v2 client<commit_after>package dsl

import (
	"bufio"
	"bytes"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"log"
	"net"
	"net/url"
	"regexp"
	"strconv"
	"strings"
	"sync"
	"time"

	"github.com/pact-foundation/pact-go/client"
	"github.com/pact-foundation/pact-go/types"
)

// Client is the interface
type Client interface {
	// StartServer starts a remote Pact Mock Server.
	StartServer(args []string, port int) *types.MockServer

	// ListServers lists all known Mock Servers
	ListServers() []*types.MockServer

	// StopServer stops a remote Pact Mock Server.
	StopServer(server *types.MockServer) (*types.MockServer, error)

	// RemoveAllServers stops all remote Pact Mock Servers.
	RemoveAllServers(server *types.MockServer) []*types.MockServer

	// VerifyProvider runs the verification process against a running Provider.
	VerifyProvider(request types.VerifyRequest) ([]types.ProviderVerifierResponse, error)

	// UpdateMessagePact adds a pact message to a contract file
	UpdateMessagePact(request types.PactMessageRequest) error

	// ReifyMessage takes a structured object, potentially containing nested Matchers
	// and returns an object with just the example (generated) content
	// The object may be a simple JSON primitive e.g. string or number or a complex object
	ReifyMessage(request *types.PactReificationRequest) (res *types.ReificationResponse, err error)

	// PublishPacts publishes pact files to a Pact Broker
	PublishPacts(request types.PublishRequest) error
}

// PactClient is the main interface into starting/stopping
// the underlying Pact CLI subsystem
type PactClient struct {
	pactMockSvcManager     client.Service
	verificationSvcManager client.Service
	messageSvcManager      client.Service
	publishSvcManager      client.Service

	// Track mock servers
	Servers []MockService

	// Network Daemon is listening on
	Network string

	// Address the Daemon is listening on
	Address string

	// TimeoutDuration specifies how long to wait for Pact CLI to start
	TimeoutDuration time.Duration
}

// newClient creates a new Pact client manager with the provided services
func newClient(MockServiceManager client.Service, verificationServiceManager client.Service, messageServiceManager client.Service, publishServiceManager client.Service) *PactClient {
	MockServiceManager.Setup()
	verificationServiceManager.Setup()
	messageServiceManager.Setup()
	publishServiceManager.Setup()

	return &PactClient{
		pactMockSvcManager:     MockServiceManager,
		verificationSvcManager: verificationServiceManager,
		messageSvcManager:      messageServiceManager,
		publishSvcManager:      publishServiceManager,
		TimeoutDuration:        10 * time.Second,
	}
}

// NewClient creates a new Pact client manager with defaults
func NewClient() *PactClient {
	return newClient(&client.MockService{}, &client.VerificationService{}, &client.MessageService{}, &client.PublishService{})
}

// StartServer starts a remote Pact Mock Server.
func (p *PactClient) StartServer(args []string, port int) *types.MockServer {
	log.Println("[DEBUG] client: starting a server with args:", args, "port:", port)
	args = append(args, []string{"--port", strconv.Itoa(port)}...)
	svc := p.pactMockSvcManager.NewService(args)
	cmd := svc.Start()

	waitForPort(port, p.getNetworkInterface(), p.Address, p.TimeoutDuration,
		fmt.Sprintf(`Timed out waiting for Mock Server to start on port %d - are you sure it's running?`, port))

	return &types.MockServer{
		Pid:  cmd.Process.Pid,
		Port: port,
	}
}

// ListServers lists all known Mock Servers
func (p *PactClient) ListServers() []*types.MockServer {
	log.Println("[DEBUG] client: starting a server")

	var servers []*types.MockServer

	for port, s := range p.pactMockSvcManager.List() {
		servers = append(servers, &types.MockServer{
			Pid:  s.Process.Pid,
			Port: port,
		})
	}

	return servers
}

// StopServer stops a remote Pact Mock Server.
func (p *PactClient) StopServer(server *types.MockServer) (*types.MockServer, error) {
	log.Println("[DEBUG] client: stop server")

	// TODO: Need to be able to get a non-zero exit code here!
	_, server.Error = p.pactMockSvcManager.Stop(server.Pid)
	return server, server.Error
}

// RemoveAllServers stops all remote Pact Mock Servers.
func (p *PactClient) RemoveAllServers(server *types.MockServer) []*types.MockServer {
	log.Println("[DEBUG] client: stop server")

	for _, s := range p.verificationSvcManager.List() {
		if s != nil {
			p.pactMockSvcManager.Stop(s.Process.Pid)
		}
	}
	return nil
}

// VerifyProvider runs the verification process against a running Provider.
// TODO: extract/refactor the stdout/error streaems from these functions
func (p *PactClient) VerifyProvider(request types.VerifyRequest) ([]types.ProviderVerifierResponse, error) {
	log.Println("[DEBUG] client: verifying a provider")
	response := make([]types.ProviderVerifierResponse, 0)

	// Convert request into flags, and validate request
	err := request.Validate()
	if err != nil {
		return response, err
	}

	address := getAddress(request.ProviderBaseURL)
	port := getPort(request.ProviderBaseURL)

	err = waitForPort(port, p.getNetworkInterface(), address, p.TimeoutDuration,
		fmt.Sprintf(`Timed out waiting for Provider API to start on port %d - are you sure it's running?`, port))

	if err != nil {
		return response, err
	}

	// Run command, splitting out stderr and stdout. The command can fail for
	// several reasons:
	// 1. Command is unable to run at all.
	// 2. Command runs, but fails for unknown reason.
	// 3. Command runs, and returns exit status 1 because the tests fail.
	//
	// First, attempt to decode the response of the stdout.
	// If that is successful, we are at case 3. Return stdout as message, no error.
	// Else, return an error, include stderr and stdout in both the error and message.
	svc := p.verificationSvcManager.NewService(request.Args)
	cmd := svc.Command()

	stdOutPipe, err := cmd.StdoutPipe()
	if err != nil {
		return response, err
	}
	stdErrPipe, err := cmd.StderrPipe()
	if err != nil {
		return response, err
	}

	// Buffered channel: wait for all reading to complete
	var wg sync.WaitGroup
	verifications := []string{}
	var stdErr strings.Builder

	// Split by lines, as the content is JSONL formatted
	// Each pact is verified by line, and the results (as JSON) sent to stdout.
	// See https://github.com/pact-foundation/pact-go/issues/88#issuecomment-404686337
	stdOutScanner := bufio.NewScanner(stdOutPipe)
	go func() {
		wg.Add(1)
		defer wg.Done()
		stdOutBuf := make([]byte, bufio.MaxScanTokenSize)
		stdOutScanner.Buffer(stdOutBuf, 64*1024*1024)

		for stdOutScanner.Scan() {
			verifications = append(verifications, stdOutScanner.Text())
		}
	}()

	// Scrape errors
	stdErrScanner := bufio.NewScanner(stdErrPipe)
	go func() {
		wg.Add(1)
		defer wg.Done()
		for stdErrScanner.Scan() {
			stdErr.WriteString(fmt.Sprintf("%s\n", stdErrScanner.Text()))
		}

	}()

	err = cmd.Start()
	if err != nil {
		return response, err
	}

	// Wait for watch goroutine before Cmd.Wait(), race condition!
	err = cmd.Wait()
	wg.Wait()

	var verification types.ProviderVerifierResponse
	for _, v := range verifications {
		v = strings.TrimSpace(v)

		// TODO: fix once https://github.com/pact-foundation/pact-provider-verifier/issues/26
		//       is addressed
		// logging to stdout breaks the JSON response
		// https://github.com/pact-foundation/pact-ruby/commit/06fa61581512ba5570c315d089f2c0fc23c8cb11
		if v != "" && strings.Index(v, "INFO") != 0 {
			dErr := json.Unmarshal([]byte(v), &verification)

			response = append(response, verification)

			if dErr != nil {
				err = dErr
			}
		}
	}

	if err == nil {
		return response, err
	}

	return response, fmt.Errorf("error verifying provider: %s\n\nSTDERR:\n%s\n\nSTDOUT:\n%s", err, stdErr.String(), strings.Join(verifications, "\n"))
}

// UpdateMessagePact adds a pact message to a contract file
func (p *PactClient) UpdateMessagePact(request types.PactMessageRequest) error {
	log.Println("[DEBUG] client: adding pact message...")

	// Convert request into flags, and validate request
	err := request.Validate()
	if err != nil {
		return err
	}

	svc := p.messageSvcManager.NewService(request.Args)
	cmd := svc.Command()

	stdOutPipe, err := cmd.StdoutPipe()
	if err != nil {
		return err
	}
	stdErrPipe, err := cmd.StderrPipe()
	if err != nil {
		return err
	}
	err = cmd.Start()
	if err != nil {
		return err
	}
	stdOut, err := ioutil.ReadAll(stdOutPipe)
	if err != nil {
		return err
	}
	stdErr, err := ioutil.ReadAll(stdErrPipe)
	if err != nil {
		return err
	}

	err = cmd.Wait()

	if err == nil {
		return nil
	}

	return fmt.Errorf("error creating message: %s\n\nSTDERR:\n%s\n\nSTDOUT:\n%s", err, stdErr, stdOut)
}

// PublishPacts publishes a set of pacts to a pact broker
func (p *PactClient) PublishPacts(request types.PublishRequest) error {
	svc := p.publishSvcManager.NewService(request.Args)
	log.Println("[DEBUG] about to publish pacts")
	cmd := svc.Start()

	log.Println("[DEBUG] waiting for response")
	err := cmd.Wait()

	log.Println("[DEBUG] response from publish", err)

	return err
}

// ReifyMessage takes a structured object, potentially containing nested Matchers
// and returns an object with just the example (generated) content
// The object may be a simple JSON primitive e.g. string or number or a complex object
func (p *PactClient) ReifyMessage(request *types.PactReificationRequest) (res *types.ReificationResponse, err error) {
	log.Println("[DEBUG] client: adding pact message...")

	var responseObject interface{}
	res = &types.ReificationResponse{
		Response: responseObject,
	}

	// Convert request into flags, and validate request
	err = request.Validate()
	if err != nil {
		return
	}

	svc := p.messageSvcManager.NewService(request.Args)
	cmd := svc.Command()

	stdOutPipe, err := cmd.StdoutPipe()
	if err != nil {
		return
	}
	stdErrPipe, err := cmd.StderrPipe()
	if err != nil {
		return
	}
	err = cmd.Start()
	if err != nil {
		return
	}
	stdOut, err := ioutil.ReadAll(stdOutPipe)
	if err != nil {
		return
	}
	stdErr, err := ioutil.ReadAll(stdErrPipe)
	if err != nil {
		return
	}

	err = cmd.Wait()

	res.ResponseRaw = stdOut
	decoder := json.NewDecoder(bytes.NewReader(stdOut))

	dErr := decoder.Decode(&res.Response)
	if dErr == nil {
		return
	}

	if err == nil {
		err = dErr
	}

	if err == nil {
		return
	}

	err = fmt.Errorf("error creating message: %s\n\nSTDERR:\n%s\n\nSTDOUT:\n%s", err, stdErr, stdOut)

	return
}

// Get a port given a URL
func getPort(rawURL string) int {
	parsedURL, err := url.Parse(rawURL)
	if err == nil {
		splitHost := strings.Split(parsedURL.Host, ":")
		if len(splitHost) == 2 {
			port, err := strconv.Atoi(splitHost[1])
			if err == nil {
				return port
			}
		}
		if parsedURL.Scheme == "https" {
			return 443
		}
		return 80
	}

	return -1
}

// Get the address given a URL
func getAddress(rawURL string) string {
	parsedURL, err := url.Parse(rawURL)
	if err != nil {
		return ""
	}

	splitHost := strings.Split(parsedURL.Host, ":")
	return splitHost[0]
}

// Use this to wait for a port to be running prior
// to running tests.
var waitForPort = func(port int, network string, address string, timeoutDuration time.Duration, message string) error {
	log.Println("[DEBUG] waiting for port", port, "to become available")
	timeout := time.After(timeoutDuration)

	for {
		select {
		case <-timeout:
			log.Printf("[ERROR] Expected server to start < %s. %s", timeoutDuration, message)
			return fmt.Errorf("Expected server to start < %s. %s", timeoutDuration, message)
		case <-time.After(50 * time.Millisecond):
			_, err := net.Dial(network, fmt.Sprintf("%s:%d", address, port))
			if err == nil {
				return nil
			}
		}
	}
}

// sanitiseRubyResponse removes Ruby-isms from the response content
// making the output much more human readable
func sanitiseRubyResponse(response string) string {
	log.Println("[TRACE] response from Ruby process pre-sanitisation:", response)

	r := regexp.MustCompile("(?m)^\\s*#.*$")
	s := r.ReplaceAllString(response, "")

	r = regexp.MustCompile("(?m).*bundle exec rake pact:verify.*$")
	s = r.ReplaceAllString(s, "")

	r = regexp.MustCompile("\\n+")
	s = r.ReplaceAllString(s, "\n")

	return s
}

// getNetworkInterface returns a default interface to communicate to the Daemon
// if none specified
func (p *PactClient) getNetworkInterface() string {
	if p.Network == "" {
		return "tcp"
	}
	return p.Network
}
<|endoftext|>


<commit_before>package brightbox

import (
	"fmt"
	"testing"

	"github.com/brightbox/gobrightbox"
	"github.com/hashicorp/terraform/helper/acctest"
	"github.com/hashicorp/terraform/helper/resource"
	"github.com/hashicorp/terraform/terraform"
)

func TestAccBrightboxCloudip_Basic(t *testing.T) {
	var cloudip brightbox.CloudIP
	rInt := acctest.RandInt()

	resource.Test(t, resource.TestCase{
		PreCheck:     func() { testAccPreCheck(t) },
		Providers:    testAccProviders,
		CheckDestroy: testAccCheckBrightboxCloudipDestroy,
		Steps: []resource.TestStep{
			resource.TestStep{
				Config: testAccCheckBrightboxCloudipConfig_basic(rInt),
				Check: resource.ComposeTestCheckFunc(
					testAccCheckBrightboxCloudipExists("brightbox_cloudip.foobar", &cloudip),
					testAccCheckBrightboxCloudipAttributes(&cloudip, rInt),
					resource.TestCheckResourceAttr(
						"brightbox_cloudip.foobar", "name", fmt.Sprintf("foo-%d", rInt)),
					resource.TestCheckNoResourceAttr(
						"brightbox_cloudip.foobar", "target"),
				),
			},
		},
	})
}

func TestAccBrightboxCloudip_clear_name(t *testing.T) {
	var cloudip brightbox.CloudIP
	rInt := acctest.RandInt()

	resource.Test(t, resource.TestCase{
		PreCheck:     func() { testAccPreCheck(t) },
		Providers:    testAccProviders,
		CheckDestroy: testAccCheckBrightboxCloudipDestroy,
		Steps: []resource.TestStep{
			resource.TestStep{
				Config: testAccCheckBrightboxCloudipConfig_basic(rInt),
				Check: resource.ComposeTestCheckFunc(
					testAccCheckBrightboxCloudipExists("brightbox_cloudip.foobar", &cloudip),
					testAccCheckBrightboxCloudipAttributes(&cloudip, rInt),
					resource.TestCheckResourceAttr(
						"brightbox_cloudip.foobar", "name", fmt.Sprintf("foo-%d", rInt)),
					resource.TestCheckNoResourceAttr(
						"brightbox_cloudip.foobar", "target"),
				),
			},
			resource.TestStep{
				Config: testAccCheckBrightboxCloudipConfig_empty_name,
				Check: resource.ComposeTestCheckFunc(
					testAccCheckBrightboxCloudipExists("brightbox_cloudip.foobar", &cloudip),
					resource.TestCheckResourceAttr(
						"brightbox_cloudip.foobar", "name", ""),
					resource.TestCheckNoResourceAttr(
						"brightbox_cloudip.foobar", "target"),
				),
			},
		},
	})
}

func TestAccBrightboxCloudip_Mapped(t *testing.T) {
	var cloudip brightbox.CloudIP
	rInt := acctest.RandInt()

	resource.Test(t, resource.TestCase{
		PreCheck:     func() { testAccPreCheck(t) },
		Providers:    testAccProviders,
		CheckDestroy: testAccCheckBrightboxCloudipDestroy,
		Steps: []resource.TestStep{
			resource.TestStep{
				Config: testAccCheckBrightboxCloudipConfig_mapped(rInt),
				Check: resource.ComposeTestCheckFunc(
					testAccCheckBrightboxCloudipExists("brightbox_cloudip.foobar", &cloudip),
					resource.TestCheckResourceAttr(
						"brightbox_cloudip.foobar", "name", fmt.Sprintf("bar-%d", rInt)),
				),
			},
		},
	})
}

func TestAccBrightboxCloudip_Remapped(t *testing.T) {
	var cloudip brightbox.CloudIP
	rInt := acctest.RandInt()

	resource.Test(t, resource.TestCase{
		PreCheck:     func() { testAccPreCheck(t) },
		Providers:    testAccProviders,
		CheckDestroy: testAccCheckBrightboxCloudipDestroy,
		Steps: []resource.TestStep{
			resource.TestStep{
				Config: testAccCheckBrightboxCloudipConfig_basic(rInt),
				Check: resource.ComposeTestCheckFunc(
					testAccCheckBrightboxCloudipExists("brightbox_cloudip.foobar", &cloudip),
					resource.TestCheckResourceAttr(
						"brightbox_cloudip.foobar", "name", fmt.Sprintf("foo-%d", rInt)),
				),
			},
			resource.TestStep{
				Config: testAccCheckBrightboxCloudipConfig_mapped(rInt),
				Check: resource.ComposeTestCheckFunc(
					testAccCheckBrightboxCloudipExists("brightbox_cloudip.foobar", &cloudip),
					resource.TestCheckResourceAttr(
						"brightbox_cloudip.foobar", "name", fmt.Sprintf("bar-%d", rInt)),
				),
			},
			resource.TestStep{
				Config: testAccCheckBrightboxCloudipConfig_remapped(rInt),
				Check: resource.ComposeTestCheckFunc(
					testAccCheckBrightboxCloudipExists("brightbox_cloudip.foobar", &cloudip),
					resource.TestCheckResourceAttr(
						"brightbox_cloudip.foobar", "name", fmt.Sprintf("baz-%d", rInt)),
				),
			},
			resource.TestStep{
				Config: testAccCheckBrightboxCloudipConfig_basic(rInt),
				Check: resource.ComposeTestCheckFunc(
					testAccCheckBrightboxCloudipExists("brightbox_cloudip.foobar", &cloudip),
					resource.TestCheckResourceAttr(
						"brightbox_cloudip.foobar", "name", fmt.Sprintf("foo-%d", rInt)),
				),
			},
		},
	})
}

func testAccCheckBrightboxCloudipDestroy(s *terraform.State) error {
	client := testAccProvider.Meta().(*CompositeClient).ApiClient

	for _, rs := range s.RootModule().Resources {
		if rs.Type != "brightbox_cloudip" {
			continue
		}

		// Try to find the CloudIP
		_, err := client.CloudIP(rs.Primary.ID)

		// Wait

		if err != nil {
			apierror := err.(brightbox.ApiError)
			if apierror.StatusCode != 404 {
				return fmt.Errorf(
					"Error waiting for cloudip %s to be destroyed: %s",
					rs.Primary.ID, err)
			}
		}
	}

	return nil
}

func testAccCheckBrightboxCloudipExists(n string, cloudip *brightbox.CloudIP) resource.TestCheckFunc {
	return func(s *terraform.State) error {
		rs, ok := s.RootModule().Resources[n]
		if !ok {
			return fmt.Errorf("Not found: %s", n)
		}

		if rs.Primary.ID == "" {
			return fmt.Errorf("No CloudIP ID is set")
		}

		client := testAccProvider.Meta().(*CompositeClient).ApiClient

		// Try to find the CloudIP
		retrieveCloudip, err := client.CloudIP(rs.Primary.ID)

		if err != nil {
			return err
		}

		if retrieveCloudip.Id != rs.Primary.ID {
			return fmt.Errorf("CloudIP not found")
		}

		*cloudip = *retrieveCloudip

		return nil
	}
}

func testAccCheckBrightboxCloudipAttributes(cloudip *brightbox.CloudIP, rInt int) resource.TestCheckFunc {
	return func(s *terraform.State) error {

		if cloudip.Name != fmt.Sprintf("foo-%d", rInt) {
			return fmt.Errorf("Bad name: %s", cloudip.Name)
		}
		return nil
	}
}

func testAccCheckBrightboxCloudipConfig_basic(rInt int) string {
	return fmt.Sprintf(`
resource "brightbox_cloudip" "foobar" {
	name = "foo-%d"
}
`, rInt)
}

const testAccCheckBrightboxCloudipConfig_empty_name = `

resource "brightbox_cloudip" "foobar" {
	name = ""
}
`

func testAccCheckBrightboxCloudipConfig_mapped(rInt int) string {
	return fmt.Sprintf(`

resource "brightbox_cloudip" "foobar" {
	name = "bar-%d"
	target = "${brightbox_server.boofar.interface}"
}

resource "brightbox_server" "boofar" {
	image = "${data.brightbox_image.foobar.id}"
	name = "bar-%d"
}
%s`, rInt, rInt, TestAccBrightboxImageDataSourceConfig_blank_disk)
}

func testAccCheckBrightboxCloudipConfig_remapped(rInt int) string {
	return fmt.Sprintf(`

resource "brightbox_cloudip" "foobar" {
	name = "baz-%d"
	target = "${brightbox_server.fred.interface}"
}

resource "brightbox_server" "boofar" {
	image = "${data.brightbox_image.foobar.id}"
	name = "bar-%d"
}

resource "brightbox_server" "fred" {
	image = "${data.brightbox_image.foobar.id}"
	name = "baz-%d"
}
%s`, rInt, rInt, rInt, TestAccBrightboxImageDataSourceConfig_blank_disk)
}
<commit_msg>Remove redundant test in cloudip<commit_after>package brightbox

import (
	"fmt"
	"testing"

	"github.com/brightbox/gobrightbox"
	"github.com/hashicorp/terraform/helper/acctest"
	"github.com/hashicorp/terraform/helper/resource"
	"github.com/hashicorp/terraform/terraform"
)

const (
	resourceName = "brightbox_cloudip.foobar"
)

func TestAccBrightboxCloudip_Basic(t *testing.T) {
	resourceName := resourceName
	var cloudip brightbox.CloudIP
	rInt := acctest.RandInt()

	resource.Test(t, resource.TestCase{
		PreCheck:     func() { testAccPreCheck(t) },
		Providers:    testAccProviders,
		CheckDestroy: testAccCheckBrightboxCloudipDestroy,
		Steps: []resource.TestStep{
			resource.TestStep{
				Config: testAccCheckBrightboxCloudipConfig_basic(rInt),
				Check: resource.ComposeTestCheckFunc(
					testAccCheckBrightboxCloudipExists(resourceName, &cloudip),
					resource.TestCheckResourceAttr(
						resourceName, "name", fmt.Sprintf("foo-%d", rInt)),
					resource.TestCheckNoResourceAttr(
						resourceName, "target"),
				),
			},
		},
	})
}

func TestAccBrightboxCloudip_clear_name(t *testing.T) {
	var cloudip brightbox.CloudIP
	rInt := acctest.RandInt()

	resource.Test(t, resource.TestCase{
		PreCheck:     func() { testAccPreCheck(t) },
		Providers:    testAccProviders,
		CheckDestroy: testAccCheckBrightboxCloudipDestroy,
		Steps: []resource.TestStep{
			resource.TestStep{
				Config: testAccCheckBrightboxCloudipConfig_basic(rInt),
				Check: resource.ComposeTestCheckFunc(
					testAccCheckBrightboxCloudipExists(resourceName, &cloudip),
					resource.TestCheckResourceAttr(
						resourceName, "name", fmt.Sprintf("foo-%d", rInt)),
					resource.TestCheckNoResourceAttr(
						resourceName, "target"),
				),
			},
			resource.TestStep{
				Config: testAccCheckBrightboxCloudipConfig_empty_name,
				Check: resource.ComposeTestCheckFunc(
					testAccCheckBrightboxCloudipExists(resourceName, &cloudip),
					resource.TestCheckResourceAttr(
						resourceName, "name", ""),
					resource.TestCheckNoResourceAttr(
						resourceName, "target"),
				),
			},
		},
	})
}

func TestAccBrightboxCloudip_Mapped(t *testing.T) {
	var cloudip brightbox.CloudIP
	rInt := acctest.RandInt()

	resource.Test(t, resource.TestCase{
		PreCheck:     func() { testAccPreCheck(t) },
		Providers:    testAccProviders,
		CheckDestroy: testAccCheckBrightboxCloudipDestroy,
		Steps: []resource.TestStep{
			resource.TestStep{
				Config: testAccCheckBrightboxCloudipConfig_mapped(rInt),
				Check: resource.ComposeTestCheckFunc(
					testAccCheckBrightboxCloudipExists(resourceName, &cloudip),
					resource.TestCheckResourceAttr(
						resourceName, "name", fmt.Sprintf("bar-%d", rInt)),
				),
			},
		},
	})
}

func TestAccBrightboxCloudip_Remapped(t *testing.T) {
	var cloudip brightbox.CloudIP
	rInt := acctest.RandInt()

	resource.Test(t, resource.TestCase{
		PreCheck:     func() { testAccPreCheck(t) },
		Providers:    testAccProviders,
		CheckDestroy: testAccCheckBrightboxCloudipDestroy,
		Steps: []resource.TestStep{
			resource.TestStep{
				Config: testAccCheckBrightboxCloudipConfig_basic(rInt),
				Check: resource.ComposeTestCheckFunc(
					testAccCheckBrightboxCloudipExists(resourceName, &cloudip),
					resource.TestCheckResourceAttr(
						resourceName, "name", fmt.Sprintf("foo-%d", rInt)),
				),
			},
			resource.TestStep{
				Config: testAccCheckBrightboxCloudipConfig_mapped(rInt),
				Check: resource.ComposeTestCheckFunc(
					testAccCheckBrightboxCloudipExists(resourceName, &cloudip),
					resource.TestCheckResourceAttr(
						resourceName, "name", fmt.Sprintf("bar-%d", rInt)),
				),
			},
			resource.TestStep{
				Config: testAccCheckBrightboxCloudipConfig_remapped(rInt),
				Check: resource.ComposeTestCheckFunc(
					testAccCheckBrightboxCloudipExists(resourceName, &cloudip),
					resource.TestCheckResourceAttr(
						resourceName, "name", fmt.Sprintf("baz-%d", rInt)),
				),
			},
			resource.TestStep{
				Config: testAccCheckBrightboxCloudipConfig_basic(rInt),
				Check: resource.ComposeTestCheckFunc(
					testAccCheckBrightboxCloudipExists(resourceName, &cloudip),
					resource.TestCheckResourceAttr(
						resourceName, "name", fmt.Sprintf("foo-%d", rInt)),
				),
			},
		},
	})
}

func testAccCheckBrightboxCloudipDestroy(s *terraform.State) error {
	client := testAccProvider.Meta().(*CompositeClient).ApiClient

	for _, rs := range s.RootModule().Resources {
		if rs.Type != "brightbox_cloudip" {
			continue
		}

		// Try to find the CloudIP
		_, err := client.CloudIP(rs.Primary.ID)

		// Wait

		if err != nil {
			apierror := err.(brightbox.ApiError)
			if apierror.StatusCode != 404 {
				return fmt.Errorf(
					"Error waiting for cloudip %s to be destroyed: %s",
					rs.Primary.ID, err)
			}
		}
	}

	return nil
}

func testAccCheckBrightboxCloudipExists(n string, cloudip *brightbox.CloudIP) resource.TestCheckFunc {
	return func(s *terraform.State) error {
		rs, ok := s.RootModule().Resources[n]
		if !ok {
			return fmt.Errorf("Not found: %s", n)
		}

		if rs.Primary.ID == "" {
			return fmt.Errorf("No CloudIP ID is set")
		}

		client := testAccProvider.Meta().(*CompositeClient).ApiClient

		// Try to find the CloudIP
		retrieveCloudip, err := client.CloudIP(rs.Primary.ID)

		if err != nil {
			return err
		}

		if retrieveCloudip.Id != rs.Primary.ID {
			return fmt.Errorf("CloudIP not found")
		}

		*cloudip = *retrieveCloudip

		return nil
	}
}

func testAccCheckBrightboxCloudipConfig_basic(rInt int) string {
	return fmt.Sprintf(`
resource "brightbox_cloudip" "foobar" {
	name = "foo-%d"
}
`, rInt)
}

const testAccCheckBrightboxCloudipConfig_empty_name = `

resource "brightbox_cloudip" "foobar" {
	name = ""
}
`

func testAccCheckBrightboxCloudipConfig_mapped(rInt int) string {
	return fmt.Sprintf(`

resource "brightbox_cloudip" "foobar" {
	name = "bar-%d"
	target = "${brightbox_server.boofar.interface}"
}

resource "brightbox_server" "boofar" {
	image = "${data.brightbox_image.foobar.id}"
	name = "bar-%d"
}
%s`, rInt, rInt, TestAccBrightboxImageDataSourceConfig_blank_disk)
}

func testAccCheckBrightboxCloudipConfig_remapped(rInt int) string {
	return fmt.Sprintf(`

resource "brightbox_cloudip" "foobar" {
	name = "baz-%d"
	target = "${brightbox_server.fred.interface}"
}

resource "brightbox_server" "boofar" {
	image = "${data.brightbox_image.foobar.id}"
	name = "bar-%d"
}

resource "brightbox_server" "fred" {
	image = "${data.brightbox_image.foobar.id}"
	name = "baz-%d"
}
%s`, rInt, rInt, rInt, TestAccBrightboxImageDataSourceConfig_blank_disk)
}
<|endoftext|>


<commit_before>package e2e

import (
	"bytes"
	"fmt"
	"io/ioutil"
	"os"
	"os/exec"
	"path/filepath"
	"regexp"
	"runtime/debug"
	"strconv"
	"testing"
	"time"

	bazel "github.com/bazelbuild/bazel-integration-testing/go"
)

// Maximum amount of time to wait before failing a test for not matching your expectations.
var delay = 10 * time.Second

type IBazelTester struct {
	bazel *bazel.TestingBazel
	t     *testing.T

	cmd          *exec.Cmd
	stderrBuffer *bytes.Buffer
	stderrOld    string
	stdoutBuffer *bytes.Buffer
	stdoutOld    string
}

func NewIBazelTester(t *testing.T, bazel *bazel.TestingBazel) *IBazelTester {
	return &IBazelTester{
		bazel: bazel,
		t:     t,
	}
}

func (i *IBazelTester) bazelPath() string {
	return i.bazel.GetBazel()
}

func (i *IBazelTester) Build(target string) {
	i.build(target, []string{})
}

func (i *IBazelTester) Run(target string) {
	i.run(target, []string{})
}

func (i *IBazelTester) RunWithProfiler(target string, profiler string) {
	i.run(target, []string{"--profile_dev="+profiler})
}

func (i *IBazelTester) GetOutput() string {
	return string(i.stdoutBuffer.Bytes())
}

func (i *IBazelTester) ExpectOutput(want string) {
	i.Expect(want, i.GetOutput, &i.stdoutOld)
}

func (i *IBazelTester) ExpectError(want string) {
	i.Expect(want, i.GetError, &i.stderrOld)
}

func (i *IBazelTester) Expect(want string, stream func() string, history *string) {
	stopAt := time.Now().Add(delay)
	for time.Now().Before(stopAt) {
		time.Sleep(5 * time.Millisecond)

		// Grab the output and strip output that was available last time we passed
		// a test.
		out := stream()[len(*history):]
		if match, err := regexp.MatchString(want, out); match == true && err == nil {
			// Save the current output value for the next iteration.
			*history = stream()
			return
		}
	}

	if match, err := regexp.MatchString(want, stream()); match == false || err != nil {
		i.t.Errorf("Expected iBazel output after %v to be:\nWanted [%v], got [%v]", delay, want, stream())
		debug.PrintStack()

		// In order to prevent cascading errors where the first result failing to
		// match ruins the error output for the rest of the runs, persist the old
		// stdout.
		*history = stream()
	}
}

func (i *IBazelTester) GetError() string {
	return string(i.stderrBuffer.Bytes())
}

func (i *IBazelTester) GetSubprocessPid() int64 {
	f, err := os.Open(filepath.Join(os.TempDir(), "ibazel_e2e_subprocess_launcher.pid"))
	if err != nil {
		panic(err)
	}

	rawPid, err := ioutil.ReadAll(f)
	if err != nil {
		panic(err)
	}

	pid, err := strconv.ParseInt(string(rawPid), 10, 32)
	if err != nil {
		panic(err)
	}
	return pid
}

func (i *IBazelTester) Kill() {
	if err := i.cmd.Process.Kill(); err != nil {
		panic(err)
	}
}

func (i *IBazelTester) build(target string, additionalArgs []string) {
	args := []string{"--bazel_path=" + i.bazelPath()}
	args = append(args, additionalArgs...)
	args = append(args, "build")
	args = append(args, target)
	i.cmd = exec.Command(ibazelPath, args...)

	i.stdoutBuffer = &bytes.Buffer{}
	i.cmd.Stdout = i.stdoutBuffer

	i.stderrBuffer = &bytes.Buffer{}
	i.cmd.Stderr = i.stderrBuffer

	if err := i.cmd.Start(); err != nil {
		fmt.Printf("Command: %s", i.cmd)
		panic(err)
	}
}

func (i *IBazelTester) run(target string, additionalArgs []string) {
	args := []string{"--bazel_path="+i.bazelPath(), "--log_to_file=/tmp/ibazel_output.log"}
	args = append(args, additionalArgs...)
	args = append(args, "run")
	args = append(args, target)
	i.cmd = exec.Command(ibazelPath, args...)

	errCode, buildStdout, buildStderr := i.bazel.RunBazel([]string{"build", target})
	if errCode != 0 {
		panic(fmt.Sprintf("Unable to build target. Error code: %d\nStdout:\n%s\nStderr:\n%s", errCode, buildStdout, buildStderr))
	}

	i.stdoutBuffer = &bytes.Buffer{}
	i.cmd.Stdout = i.stdoutBuffer

	i.stderrBuffer = &bytes.Buffer{}
	i.cmd.Stderr = i.stderrBuffer

	if err := i.cmd.Start(); err != nil {
		fmt.Printf("Command: %s", i.cmd)
		panic(err)
	}
}
<commit_msg>Increase e2e timeout (#179)<commit_after>package e2e

import (
	"bytes"
	"fmt"
	"io/ioutil"
	"os"
	"os/exec"
	"path/filepath"
	"regexp"
	"runtime/debug"
	"strconv"
	"testing"
	"time"

	bazel "github.com/bazelbuild/bazel-integration-testing/go"
)

// Maximum amount of time to wait before failing a test for not matching your expectations.
var delay = 20 * time.Second

type IBazelTester struct {
	bazel *bazel.TestingBazel
	t     *testing.T

	cmd          *exec.Cmd
	stderrBuffer *bytes.Buffer
	stderrOld    string
	stdoutBuffer *bytes.Buffer
	stdoutOld    string
}

func NewIBazelTester(t *testing.T, bazel *bazel.TestingBazel) *IBazelTester {
	return &IBazelTester{
		bazel: bazel,
		t:     t,
	}
}

func (i *IBazelTester) bazelPath() string {
	return i.bazel.GetBazel()
}

func (i *IBazelTester) Build(target string) {
	i.build(target, []string{})
}

func (i *IBazelTester) Run(target string) {
	i.run(target, []string{})
}

func (i *IBazelTester) RunWithProfiler(target string, profiler string) {
	i.run(target, []string{"--profile_dev="+profiler})
}

func (i *IBazelTester) GetOutput() string {
	return string(i.stdoutBuffer.Bytes())
}

func (i *IBazelTester) ExpectOutput(want string) {
	i.Expect(want, i.GetOutput, &i.stdoutOld)
}

func (i *IBazelTester) ExpectError(want string) {
	i.Expect(want, i.GetError, &i.stderrOld)
}

func (i *IBazelTester) Expect(want string, stream func() string, history *string) {
	stopAt := time.Now().Add(delay)
	for time.Now().Before(stopAt) {
		time.Sleep(5 * time.Millisecond)

		// Grab the output and strip output that was available last time we passed
		// a test.
		out := stream()[len(*history):]
		if match, err := regexp.MatchString(want, out); match == true && err == nil {
			// Save the current output value for the next iteration.
			*history = stream()
			return
		}
	}

	if match, err := regexp.MatchString(want, stream()); match == false || err != nil {
		i.t.Errorf("Expected iBazel output after %v to be:\nWanted [%v], got [%v]", delay, want, stream())
		debug.PrintStack()

		// In order to prevent cascading errors where the first result failing to
		// match ruins the error output for the rest of the runs, persist the old
		// stdout.
		*history = stream()
	}
}

func (i *IBazelTester) GetError() string {
	return string(i.stderrBuffer.Bytes())
}

func (i *IBazelTester) GetSubprocessPid() int64 {
	f, err := os.Open(filepath.Join(os.TempDir(), "ibazel_e2e_subprocess_launcher.pid"))
	if err != nil {
		panic(err)
	}

	rawPid, err := ioutil.ReadAll(f)
	if err != nil {
		panic(err)
	}

	pid, err := strconv.ParseInt(string(rawPid), 10, 32)
	if err != nil {
		panic(err)
	}
	return pid
}

func (i *IBazelTester) Kill() {
	if err := i.cmd.Process.Kill(); err != nil {
		panic(err)
	}
}

func (i *IBazelTester) build(target string, additionalArgs []string) {
	args := []string{"--bazel_path=" + i.bazelPath()}
	args = append(args, additionalArgs...)
	args = append(args, "build")
	args = append(args, target)
	i.cmd = exec.Command(ibazelPath, args...)

	i.stdoutBuffer = &bytes.Buffer{}
	i.cmd.Stdout = i.stdoutBuffer

	i.stderrBuffer = &bytes.Buffer{}
	i.cmd.Stderr = i.stderrBuffer

	if err := i.cmd.Start(); err != nil {
		fmt.Printf("Command: %s", i.cmd)
		panic(err)
	}
}

func (i *IBazelTester) run(target string, additionalArgs []string) {
	args := []string{"--bazel_path="+i.bazelPath(), "--log_to_file=/tmp/ibazel_output.log"}
	args = append(args, additionalArgs...)
	args = append(args, "run")
	args = append(args, target)
	i.cmd = exec.Command(ibazelPath, args...)

	errCode, buildStdout, buildStderr := i.bazel.RunBazel([]string{"build", target})
	if errCode != 0 {
		panic(fmt.Sprintf("Unable to build target. Error code: %d\nStdout:\n%s\nStderr:\n%s", errCode, buildStdout, buildStderr))
	}

	i.stdoutBuffer = &bytes.Buffer{}
	i.cmd.Stdout = i.stdoutBuffer

	i.stderrBuffer = &bytes.Buffer{}
	i.cmd.Stderr = i.stderrBuffer

	if err := i.cmd.Start(); err != nil {
		fmt.Printf("Command: %s", i.cmd)
		panic(err)
	}
}
<|endoftext|>