<commit_before>package udp

import (
    "close/config"
    "fmt"
    "log"
    "net"
    "os"
    "math/rand"
    "close/stats"
    "time"
)

const SOURCE_PORT uint = 0
const SOURCE_PORT_BITS uint = 0

type SendConfig struct {
    DestAddr        string // host:port (or host)
    SourceNet       string // host/mask
    SourcePort      uint
    SourcePortBits  uint

    ID              uint    `json:"id"`     // 64-bit ID, or random
    Rate            uint    `json:"rate"`   // 0 - unrated
    Count           uint    `json:"count"`  // 0 - infinite
    Size            uint    `json:"size"`   // target size of UDP payload
}

type SendStats struct {
    ID              uint64          // send id, to correlated with RecvStats
    Time            time.Time       // stats were reset
    Duration        time.Duration   // stats were collected

    Config          SendConfig
    Rate            RateStats

    // Send.Bytes includes IP+UDP+Payload
    Send            SockStats
}

func (self SendStats) StatsTime() time.Time {
    return self.Time
}

func (self SendStats) CalcRate() float64 {
    return float64(self.Rate.Count) / self.Duration.Seconds()
}

// Return rate-loop utilization between 0..1, with 1.0 being fully utilized (unable to keep up with rate)
func (self SendStats) RateUtil() float64 {
    return 1.0 - self.Rate.SleepDuration.Seconds() / self.Duration.Seconds()
}

// Return the actual rate vs configured rate as a proportional error, with 1.0 being the most accurate
func (self SendStats) RateError() float64 {
    return self.CalcRate() / float64(self.Config.Rate)
}

func (self SendStats) StatsInstance() string {
    return fmt.Sprintf("%016x", self.ID)
}

func (self SendStats) StatsFields() map[string]interface{} {
    return map[string]interface{}{
        // gauges
        "rate_config": self.Config.Rate,
        "rate": self.CalcRate(),
        "rate_error": self.RateError(),
        "rate_util": self.RateUtil(),

        // counters
        "rate_underruns": self.Rate.UnderrunCount,
        "send_packets": self.Send.Packets,
        "send_bytes": self.Send.Bytes,
        "send_errors": self.Send.Errors,
    }
}

func (self SendStats) String() string {
    // pps rate of sent packets; may be lower than Rate() in the case of Send.Errors > 0
    sendRate := float64(self.Send.Packets) / self.Duration.Seconds()

    // achieved througput with IP+UDP headers
    sendMbps := float64(self.Send.Bytes) / 1000 / 1000 * 8 / self.Duration.Seconds()

    return fmt.Sprintf("%5.2f: send %9d with %5d underruns @ %10.2f/s = %8.2fMb/s +%5d errors @ %6.2f%% rate %6.2f%% util",
        self.Duration.Seconds(),
        self.Send.Packets, self.Rate.UnderrunCount,
        sendRate, sendMbps, self.Send.Errors,
        self.RateError() * 100,
        self.RateUtil() * 100,
    )
}

type Send struct {
    dstAddr     net.IPAddr
    dstIP       net.IP
    dstPort     uint16
    srcAddr     net.IP
    srcAddrBits uint
    srcPort     RandPort

    sockSend  SockSend

    config      SendConfig
    configChan  chan config.Config

    statsChan       chan stats.Stats
    statsInterval   time.Duration

    log         *log.Logger
}

func NewSend(config SendConfig) (*Send, error) {
    sender := &Send{
        log:    log.New(os.Stderr, "udp.Send: ", 0),
    }

    if err := sender.init(config); err != nil {
        return nil, err
    } else {
        return sender, nil
    }
}

func (self *Send) init(config SendConfig) error {
    if config.SourceNet == "" && config.SourcePortBits == 0 {
        if err := self.initUDP(config); err != nil {
            return err
        }
    } else {
        if err := self.initIP(config); err != nil {
            return err
        }
    }

    // source
    self.srcPort.init(0) // TODO: seed

    if config.SourceNet == "" {
        if srcAddr, err := self.sockSend.probeSource(); err != nil {
            return err
        } else {
            self.srcAddr = srcAddr.IP
            self.srcAddrBits = 0
            self.srcPort.SetPort(uint(srcAddr.Port))
        }
    } else if _, ipNet, err := net.ParseCIDR(config.SourceNet); err != nil {
        return fmt.Errorf("Parse SourceNet %v: %v", config.SourceNet, err)
    } else {
        maskSet, maskBits := ipNet.Mask.Size()

        self.srcAddr = ipNet.IP
        self.srcAddrBits = uint(maskBits - maskSet)
    }

    if config.SourcePort != 0 {
        self.srcPort.SetPort(config.SourcePort)
    }
    if config.SourcePortBits > 0 {
        self.srcPort.SetRandom(config.SourcePortBits)
    }

    // config
    if config.ID == 0 {
        // generate from dst
        config.ID = uint(rand.Int63())
    }

    self.config = config

    return nil
}

// init with SockUDP sender
func (self *Send) initUDP(config SendConfig) error {
    sockUDP := &SockUDP{}
    if err := sockUDP.initDial(config.DestAddr); err != nil {
        return err
    }

    self.dstIP = sockUDP.udpAddr.IP
    self.dstPort = uint16(sockUDP.udpAddr.Port)

    self.sockSend = sockUDP

    return nil
}

// init with SockIP sender
func (self *Send) initIP(config SendConfig) error {
    // setup dest
    sock := &SockSyscall{}
    if err := sock.init(config.DestAddr); err != nil {
        return err
    }

    self.dstIP = sock.udpAddr.IP
    self.dstPort = uint16(sock.udpAddr.Port)

    self.sockSend = sock

    return nil
}

func (self *Send) ID() string {
    return fmt.Sprintf("%016x", self.config.ID)
}

func (self *Send) GiveStats(interval time.Duration) chan stats.Stats {
    self.statsChan = make(chan stats.Stats)
    self.statsInterval = interval

    return self.statsChan
}

// pull runtime configuration from config source
func (self *Send) ConfigFrom(configRedis *config.Redis) (*config.Sub, error) {
    // copy for updates
    updateConfig := self.config

    if configSub, err := configRedis.Sub(config.SubOptions{"udp", self.ID()}); err != nil {
        return nil, err
    } else if configChan, err := configSub.Start(&updateConfig); err != nil {
        return nil, err
    } else {
        self.configChan = configChan

        return configSub, nil
    }
}

// Generate a sequence of *Packet
//
// Returns once complete, or error
func (self *Send) Run() error {
    var rateClock RateClock

    rateTick := rateClock.Start(self.config.Rate, self.config.Count)

    payload := Payload{
        ID:     uint64(self.config.ID),
        Seq:    0,
    }

    // stats
    statsStart := time.Now()
    statsTick := time.Tick(self.statsInterval)

    for {
        select {
        case sendTime := <-rateTick:
            if sendTime.IsZero() {
                // channel closed
                break
            }

            // send
            packet := Packet{
                SrcIP:      self.srcAddr,
                SrcPort:    self.srcPort.Port(),
                DstIP:      self.dstIP,
                DstPort:    self.dstPort,

                Payload:        payload,
                PayloadSize:    self.config.Size,
            }

            if err := self.sockSend.send(packet); err != nil {
                return err
            }

            payload.Seq++

        case statsTime := <-statsTick:
            self.statsChan <- SendStats{
                ID:         payload.ID,
                Time:       statsTime,
                Duration:   statsTime.Sub(statsStart),

                Config:     self.config,

                Rate:       rateClock.takeStats(),
                Send:       self.sockSend.takeStats(),
            }

            statsStart = statsTime

        case configConfig := <-self.configChan:
            config := configConfig.(*SendConfig)

            self.log.Printf("config: %v\n", config)

            if config.Rate != self.config.Rate || config.Count != self.config.Count {
                self.log.Printf("config rate=%d count=%d\n", config.Rate, config.Count)

                rateClock.Set(config.Rate, config.Count)

                self.config.Rate = config.Rate
                self.config.Count = config.Count
            }

            if config.Size != self.config.Size {
                self.log.Printf("config size=%d\n", config.Size)

                self.config.Size = config.Size
            }
        }
    }

    return nil
}
<commit_msg>udp/send: format ID as plain decimal<commit_after>package udp

import (
    "close/config"
    "fmt"
    "log"
    "net"
    "os"
    "math/rand"
    "close/stats"
    "time"
)

const SOURCE_PORT uint = 0
const SOURCE_PORT_BITS uint = 0

type SendConfig struct {
    DestAddr        string // host:port (or host)
    SourceNet       string // host/mask
    SourcePort      uint
    SourcePortBits  uint

    ID              uint    `json:"id"`     // 64-bit ID, or random
    Rate            uint    `json:"rate"`   // 0 - unrated
    Count           uint    `json:"count"`  // 0 - infinite
    Size            uint    `json:"size"`   // target size of UDP payload
}

type SendStats struct {
    ID              uint64          // send id, to correlated with RecvStats
    Time            time.Time       // stats were reset
    Duration        time.Duration   // stats were collected

    Config          SendConfig
    Rate            RateStats

    // Send.Bytes includes IP+UDP+Payload
    Send            SockStats
}

func (self SendStats) StatsTime() time.Time {
    return self.Time
}

func (self SendStats) CalcRate() float64 {
    return float64(self.Rate.Count) / self.Duration.Seconds()
}

// Return rate-loop utilization between 0..1, with 1.0 being fully utilized (unable to keep up with rate)
func (self SendStats) RateUtil() float64 {
    return 1.0 - self.Rate.SleepDuration.Seconds() / self.Duration.Seconds()
}

// Return the actual rate vs configured rate as a proportional error, with 1.0 being the most accurate
func (self SendStats) RateError() float64 {
    return self.CalcRate() / float64(self.Config.Rate)
}

func (self SendStats) StatsInstance() string {
    return fmt.Sprintf("%d", self.ID)
}

func (self SendStats) StatsFields() map[string]interface{} {
    return map[string]interface{}{
        // gauges
        "rate_config": self.Config.Rate,
        "rate": self.CalcRate(),
        "rate_error": self.RateError(),
        "rate_util": self.RateUtil(),

        // counters
        "rate_underruns": self.Rate.UnderrunCount,
        "send_packets": self.Send.Packets,
        "send_bytes": self.Send.Bytes,
        "send_errors": self.Send.Errors,
    }
}

func (self SendStats) String() string {
    // pps rate of sent packets; may be lower than Rate() in the case of Send.Errors > 0
    sendRate := float64(self.Send.Packets) / self.Duration.Seconds()

    // achieved througput with IP+UDP headers
    sendMbps := float64(self.Send.Bytes) / 1000 / 1000 * 8 / self.Duration.Seconds()

    return fmt.Sprintf("%5.2f: send %9d with %5d underruns @ %10.2f/s = %8.2fMb/s +%5d errors @ %6.2f%% rate %6.2f%% util",
        self.Duration.Seconds(),
        self.Send.Packets, self.Rate.UnderrunCount,
        sendRate, sendMbps, self.Send.Errors,
        self.RateError() * 100,
        self.RateUtil() * 100,
    )
}

type Send struct {
    dstAddr     net.IPAddr
    dstIP       net.IP
    dstPort     uint16
    srcAddr     net.IP
    srcAddrBits uint
    srcPort     RandPort

    sockSend  SockSend

    config      SendConfig
    configChan  chan config.Config

    statsChan       chan stats.Stats
    statsInterval   time.Duration

    log         *log.Logger
}

func NewSend(config SendConfig) (*Send, error) {
    sender := &Send{
        log:    log.New(os.Stderr, "udp.Send: ", 0),
    }

    if err := sender.init(config); err != nil {
        return nil, err
    } else {
        return sender, nil
    }
}

func (self *Send) init(config SendConfig) error {
    if config.SourceNet == "" && config.SourcePortBits == 0 {
        if err := self.initUDP(config); err != nil {
            return err
        }
    } else {
        if err := self.initIP(config); err != nil {
            return err
        }
    }

    // source
    self.srcPort.init(0) // TODO: seed

    if config.SourceNet == "" {
        if srcAddr, err := self.sockSend.probeSource(); err != nil {
            return err
        } else {
            self.srcAddr = srcAddr.IP
            self.srcAddrBits = 0
            self.srcPort.SetPort(uint(srcAddr.Port))
        }
    } else if _, ipNet, err := net.ParseCIDR(config.SourceNet); err != nil {
        return fmt.Errorf("Parse SourceNet %v: %v", config.SourceNet, err)
    } else {
        maskSet, maskBits := ipNet.Mask.Size()

        self.srcAddr = ipNet.IP
        self.srcAddrBits = uint(maskBits - maskSet)
    }

    if config.SourcePort != 0 {
        self.srcPort.SetPort(config.SourcePort)
    }
    if config.SourcePortBits > 0 {
        self.srcPort.SetRandom(config.SourcePortBits)
    }

    // config
    if config.ID == 0 {
        // generate from dst
        config.ID = uint(rand.Int63())
    }

    self.config = config

    return nil
}

// init with SockUDP sender
func (self *Send) initUDP(config SendConfig) error {
    sockUDP := &SockUDP{}
    if err := sockUDP.initDial(config.DestAddr); err != nil {
        return err
    }

    self.dstIP = sockUDP.udpAddr.IP
    self.dstPort = uint16(sockUDP.udpAddr.Port)

    self.sockSend = sockUDP

    return nil
}

// init with SockIP sender
func (self *Send) initIP(config SendConfig) error {
    // setup dest
    sock := &SockSyscall{}
    if err := sock.init(config.DestAddr); err != nil {
        return err
    }

    self.dstIP = sock.udpAddr.IP
    self.dstPort = uint16(sock.udpAddr.Port)

    self.sockSend = sock

    return nil
}

func (self *Send) ID() string {
    return fmt.Sprintf("%d", self.config.ID)
}

func (self *Send) GiveStats(interval time.Duration) chan stats.Stats {
    self.statsChan = make(chan stats.Stats)
    self.statsInterval = interval

    return self.statsChan
}

// pull runtime configuration from config source
func (self *Send) ConfigFrom(configRedis *config.Redis) (*config.Sub, error) {
    // copy for updates
    updateConfig := self.config

    if configSub, err := configRedis.Sub(config.SubOptions{"udp", self.ID()}); err != nil {
        return nil, err
    } else if configChan, err := configSub.Start(&updateConfig); err != nil {
        return nil, err
    } else {
        self.configChan = configChan

        return configSub, nil
    }
}

// Generate a sequence of *Packet
//
// Returns once complete, or error
func (self *Send) Run() error {
    var rateClock RateClock

    rateTick := rateClock.Start(self.config.Rate, self.config.Count)

    payload := Payload{
        ID:     uint64(self.config.ID),
        Seq:    0,
    }

    // stats
    statsStart := time.Now()
    statsTick := time.Tick(self.statsInterval)

    for {
        select {
        case sendTime := <-rateTick:
            if sendTime.IsZero() {
                // channel closed
                break
            }

            // send
            packet := Packet{
                SrcIP:      self.srcAddr,
                SrcPort:    self.srcPort.Port(),
                DstIP:      self.dstIP,
                DstPort:    self.dstPort,

                Payload:        payload,
                PayloadSize:    self.config.Size,
            }

            if err := self.sockSend.send(packet); err != nil {
                return err
            }

            payload.Seq++

        case statsTime := <-statsTick:
            self.statsChan <- SendStats{
                ID:         payload.ID,
                Time:       statsTime,
                Duration:   statsTime.Sub(statsStart),

                Config:     self.config,

                Rate:       rateClock.takeStats(),
                Send:       self.sockSend.takeStats(),
            }

            statsStart = statsTime

        case configConfig := <-self.configChan:
            config := configConfig.(*SendConfig)

            self.log.Printf("config: %v\n", config)

            if config.Rate != self.config.Rate || config.Count != self.config.Count {
                self.log.Printf("config rate=%d count=%d\n", config.Rate, config.Count)

                rateClock.Set(config.Rate, config.Count)

                self.config.Rate = config.Rate
                self.config.Count = config.Count
            }

            if config.Size != self.config.Size {
                self.log.Printf("config size=%d\n", config.Size)

                self.config.Size = config.Size
            }
        }
    }

    return nil
}
<|endoftext|>


<commit_before>package cert

import (
	"crypto/tls"
	"crypto/x509"
	"fmt"
	"log"
	"path"
	"strings"
	"time"

	"github.com/hashicorp/vault/api"
)

// VaultSource implements a certificate source which loads
// TLS and client authorization certificates from a Vault server.
// The Vault token should be set through the VAULT_TOKEN environment
// variable.
//
// The TLS certificates are updated automatically when Refresh
// is not zero. Refresh cannot be less than one second to prevent
// busy loops.
type VaultSource struct {
	Client       *vaultClient
	CertPath     string
	ClientCAPath string
	CAUpgradeCN  string
	Refresh      time.Duration
}

func (s *VaultSource) LoadClientCAs() (*x509.CertPool, error) {
	if s.ClientCAPath == "" {
		return nil, nil
	}
	return newCertPool(s.ClientCAPath, s.CAUpgradeCN, s.load)
}

func (s *VaultSource) Certificates() chan []tls.Certificate {
	ch := make(chan []tls.Certificate, 1)
	go watch(ch, s.Refresh, s.CertPath, s.load)
	return ch
}

func (s *VaultSource) load(path string) (pemBlocks map[string][]byte, err error) {
	pemBlocks = map[string][]byte{}

	// get will read a key=value pair from the secret
	// and store it as <name>-{cert,key}.pem so that
	// they are recognized by the post-processing function
	// which assembles the certificates.
	// The value can be stored either as string or []byte.
	get := func(name, typ string, secret *api.Secret, v2 bool) {
		data := secret.Data
		if v2 {
			x, ok := secret.Data["data"]
			if !ok {
				return
			}
			data, ok = x.(map[string]interface{})
			if !ok {
				return
			}
		}

		v := data[typ]
		if v == nil {
			return
		}

		var b []byte
		switch v.(type) {
		case string:
			b = []byte(v.(string))
		case []byte:
			b = v.([]byte)
		default:
			log.Printf("[WARN] cert: key %s has type %T", name, v)
			return
		}

		pemBlocks[name+"-"+typ+".pem"] = b
	}

	c, err := s.Client.Get()
	if err != nil {
		return nil, fmt.Errorf("vault: client: %s", err)
	}

	mountPath, v2, err := s.isKVv2(path, c)
	if err != nil {
		return nil, fmt.Errorf("vault: query mount path: %s", err)
	}

	// get the subkeys under 'path'.
	// Each subkey refers to a certificate.
	p := path
	if v2 {
		p = s.addPrefixToVKVPath(p, mountPath, "metadata")
	}

	certs, err := c.Logical().List(p)
	if err != nil {
		return nil, fmt.Errorf("vault: list: %s", err)
	}
	if certs == nil || certs.Data["keys"] == nil {
		return nil, nil
	}

	for _, x := range certs.Data["keys"].([]interface{}) {
		name := x.(string)
		p := path + "/" + name
		if v2 {
			p = s.addPrefixToVKVPath(p, mountPath, "data")
		}
		secret, err := c.Logical().Read(p)
		if err != nil {
			log.Printf("[WARN] cert: Failed to read %s from Vault: %s", p, err)
			continue
		}
		get(name, "cert", secret, v2)
		get(name, "key", secret, v2)
	}

	return pemBlocks, nil
}

func (s *VaultSource) addPrefixToVKVPath(p, mountPath, apiPrefix string) string {
	p = strings.TrimPrefix(p, mountPath)
	return path.Join(mountPath, apiPrefix, p)
}

func (s *VaultSource) isKVv2(path string, client *api.Client) (string, bool, error) {
	mountPath, version, err := s.kvPreflightVersionRequest(client, path)
	if err != nil {
		return "", false, err
	}

	return mountPath, version == 2, nil
}

func (s *VaultSource) kvPreflightVersionRequest(client *api.Client, path string) (string, int, error) {
	r := client.NewRequest("GET", "/v1/sys/internal/ui/mounts/"+path)
	resp, err := client.RawRequest(r)
	if resp != nil {
		defer resp.Body.Close()
	}
	if err != nil {
		// If we get a 404 we are using an older version of vault, default to
		// version 1
		if resp != nil && resp.StatusCode == 404 {
			return "", 1, nil
		}

		return "", 0, err
	}

	secret, err := api.ParseSecret(resp.Body)
	if err != nil {
		return "", 0, err
	}
	var mountPath string
	if mountPathRaw, ok := secret.Data["path"]; ok {
		mountPath = mountPathRaw.(string)
	}
	options := secret.Data["options"]
	if options == nil {
		return mountPath, 1, nil
	}
	versionRaw := options.(map[string]interface{})["version"]
	if versionRaw == nil {
		return mountPath, 1, nil
	}
	version := versionRaw.(string)
	switch version {
	case "", "1":
		return mountPath, 1, nil
	case "2":
		return mountPath, 2, nil
	}

	return mountPath, 1, nil
}
<commit_msg>Safely handle missing cert from Vault KV store (#710)<commit_after>package cert

import (
	"crypto/tls"
	"crypto/x509"
	"fmt"
	"log"
	"path"
	"strings"
	"time"

	"github.com/hashicorp/vault/api"
)

// VaultSource implements a certificate source which loads
// TLS and client authorization certificates from a Vault server.
// The Vault token should be set through the VAULT_TOKEN environment
// variable.
//
// The TLS certificates are updated automatically when Refresh
// is not zero. Refresh cannot be less than one second to prevent
// busy loops.
type VaultSource struct {
	Client       *vaultClient
	CertPath     string
	ClientCAPath string
	CAUpgradeCN  string
	Refresh      time.Duration
}

func (s *VaultSource) LoadClientCAs() (*x509.CertPool, error) {
	if s.ClientCAPath == "" {
		return nil, nil
	}
	return newCertPool(s.ClientCAPath, s.CAUpgradeCN, s.load)
}

func (s *VaultSource) Certificates() chan []tls.Certificate {
	ch := make(chan []tls.Certificate, 1)
	go watch(ch, s.Refresh, s.CertPath, s.load)
	return ch
}

func (s *VaultSource) load(path string) (pemBlocks map[string][]byte, err error) {
	pemBlocks = map[string][]byte{}

	// get will read a key=value pair from the secret
	// and store it as <name>-{cert,key}.pem so that
	// they are recognized by the post-processing function
	// which assembles the certificates.
	// The value can be stored either as string or []byte.
	get := func(name, typ string, secret *api.Secret, v2 bool) {
		data := secret.Data
		if v2 {
			x, ok := secret.Data["data"]
			if !ok {
				return
			}
			data, ok = x.(map[string]interface{})
			if !ok {
				return
			}
		}

		v := data[typ]
		if v == nil {
			return
		}

		var b []byte
		switch v.(type) {
		case string:
			b = []byte(v.(string))
		case []byte:
			b = v.([]byte)
		default:
			log.Printf("[WARN] cert: key %s has type %T", name, v)
			return
		}

		pemBlocks[name+"-"+typ+".pem"] = b
	}

	c, err := s.Client.Get()
	if err != nil {
		return nil, fmt.Errorf("vault: client: %s", err)
	}

	mountPath, v2, err := s.isKVv2(path, c)
	if err != nil {
		return nil, fmt.Errorf("vault: query mount path: %s", err)
	}

	// get the subkeys under 'path'.
	// Each subkey refers to a certificate.
	p := path
	if v2 {
		p = s.addPrefixToVKVPath(p, mountPath, "metadata")
	}

	certs, err := c.Logical().List(p)
	if err != nil {
		return nil, fmt.Errorf("vault: list: %s", err)
	}
	if certs == nil || certs.Data["keys"] == nil {
		return nil, nil
	}

	for _, x := range certs.Data["keys"].([]interface{}) {
		name := x.(string)
		p := path + "/" + name
		if v2 {
			p = s.addPrefixToVKVPath(p, mountPath, "data")
		}
		secret, err := c.Logical().Read(p)
		if err != nil {
			log.Printf("[WARN] cert: Failed to read %s from Vault: %s", p, err)
			continue
		}
		if secret == nil {
			log.Printf("[WARN] cert: Failed to find %s in Vault: %s", p, err)
			continue
		}
		get(name, "cert", secret, v2)
		get(name, "key", secret, v2)
	}

	return pemBlocks, nil
}

func (s *VaultSource) addPrefixToVKVPath(p, mountPath, apiPrefix string) string {
	p = strings.TrimPrefix(p, mountPath)
	return path.Join(mountPath, apiPrefix, p)
}

func (s *VaultSource) isKVv2(path string, client *api.Client) (string, bool, error) {
	mountPath, version, err := s.kvPreflightVersionRequest(client, path)
	if err != nil {
		return "", false, err
	}

	return mountPath, version == 2, nil
}

func (s *VaultSource) kvPreflightVersionRequest(client *api.Client, path string) (string, int, error) {
	r := client.NewRequest("GET", "/v1/sys/internal/ui/mounts/"+path)
	resp, err := client.RawRequest(r)
	if resp != nil {
		defer resp.Body.Close()
	}
	if err != nil {
		// If we get a 404 we are using an older version of vault, default to
		// version 1
		if resp != nil && resp.StatusCode == 404 {
			return "", 1, nil
		}

		return "", 0, err
	}

	secret, err := api.ParseSecret(resp.Body)
	if err != nil {
		return "", 0, err
	}
	var mountPath string
	if mountPathRaw, ok := secret.Data["path"]; ok {
		mountPath = mountPathRaw.(string)
	}
	options := secret.Data["options"]
	if options == nil {
		return mountPath, 1, nil
	}
	versionRaw := options.(map[string]interface{})["version"]
	if versionRaw == nil {
		return mountPath, 1, nil
	}
	version := versionRaw.(string)
	switch version {
	case "", "1":
		return mountPath, 1, nil
	case "2":
		return mountPath, 2, nil
	}

	return mountPath, 1, nil
}
<|endoftext|>


<commit_before>package test

import (
	"io"

	"github.com/nb-titan/titan"
	"github.com/neptulon/jsonrpc"
)

// ConnHelper is a neptulon/jsonrpc.Conn wrapper.
// All the functions are wrapped with proper test runner error logging.
type ConnHelper struct {
	conn      *jsonrpc.Client
	cert, key []byte
}

// AsUser attaches given user's client certificate and private key to the connection.
func (c *ConnHelper) AsUser(u *titan.User) *ConnHelper {
	return c.WithCert(u.Cert, u.Key)
}

// WithCert attaches given PEM encoded client certificate and private key to the connection.
func (c *ConnHelper) WithCert(cert, key []byte) *ConnHelper {
	c.cert = cert
	c.key = key
	return c
}

// VerifyConnClosed verifies that the connection is in closed state.
// Verification is done via reading from the channel and checking that returned error is io.EOF.
func (c *ConnHelper) VerifyConnClosed() bool {
	_, _, _, err := c.conn.ReadMsg(nil, nil)
	if err != io.EOF {
		return false
	}

	return true
}
<commit_msg>fix build<commit_after>package test

import "github.com/neptulon/jsonrpc"

// ConnHelper is a neptulon/jsonrpc.Conn wrapper.
// All the functions are wrapped with proper test runner error logging.
type ConnHelper struct {
	conn      *jsonrpc.Client
	cert, key []byte
}

//
// // AsUser attaches given user's client certificate and private key to the connection.
// func (c *ConnHelper) AsUser(u *titan.User) *ConnHelper {
// 	return c.WithCert(u.Cert, u.Key)
// }
//
// // WithCert attaches given PEM encoded client certificate and private key to the connection.
// func (c *ConnHelper) WithCert(cert, key []byte) *ConnHelper {
// 	c.cert = cert
// 	c.key = key
// 	return c
// }
//
// // VerifyConnClosed verifies that the connection is in closed state.
// // Verification is done via reading from the channel and checking that returned error is io.EOF.
// func (c *ConnHelper) VerifyConnClosed() bool {
// 	_, _, _, err := c.conn.ReadMsg(nil, nil)
// 	if err != io.EOF {
// 		return false
// 	}
//
// 	return true
// }
<|endoftext|>


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

package charm_test

import (
	"archive/zip"
	"bytes"
	"fmt"
	"io/ioutil"
	"os"
	"os/exec"
	"path/filepath"
	"strconv"
	"syscall"

	jc "github.com/juju/testing/checkers"
	gc "launchpad.net/gocheck"
	"launchpad.net/goyaml"

	"launchpad.net/juju-core/charm"
	"launchpad.net/juju-core/testing"
	"launchpad.net/juju-core/utils/set"
)

type BundleSuite struct {
	repo       *testing.Repo
	bundlePath string
}

var _ = gc.Suite(&BundleSuite{})

func (s *BundleSuite) SetUpSuite(c *gc.C) {
	s.bundlePath = testing.Charms.BundlePath(c.MkDir(), "dummy")
}

var dummyManifest = []string{
	"config.yaml",
	"empty",
	"hooks",
	"hooks/install",
	"metadata.yaml",
	"revision",
	"src",
	"src/hello.c",
}

func (s *BundleSuite) TestReadBundle(c *gc.C) {
	bundle, err := charm.ReadBundle(s.bundlePath)
	c.Assert(err, gc.IsNil)
	checkDummy(c, bundle, s.bundlePath)
}

func (s *BundleSuite) TestReadBundleWithoutConfig(c *gc.C) {
	path := testing.Charms.BundlePath(c.MkDir(), "varnish")
	bundle, err := charm.ReadBundle(path)
	c.Assert(err, gc.IsNil)

	// A lacking config.yaml file still causes a proper
	// Config value to be returned.
	c.Assert(bundle.Config().Options, gc.HasLen, 0)
}

func (s *BundleSuite) TestReadBundleBytes(c *gc.C) {
	data, err := ioutil.ReadFile(s.bundlePath)
	c.Assert(err, gc.IsNil)

	bundle, err := charm.ReadBundleBytes(data)
	c.Assert(err, gc.IsNil)
	checkDummy(c, bundle, "")
}

func (s *BundleSuite) TestManifest(c *gc.C) {
	bundle, err := charm.ReadBundle(s.bundlePath)
	c.Assert(err, gc.IsNil)
	manifest, err := bundle.Manifest()
	c.Assert(err, gc.IsNil)
	c.Assert(manifest, jc.DeepEquals, set.NewStrings(dummyManifest...))
}

func (s *BundleSuite) TestManifestNoRevision(c *gc.C) {
	bundle, err := charm.ReadBundle(s.bundlePath)
	c.Assert(err, gc.IsNil)
	dirPath := c.MkDir()
	err = bundle.ExpandTo(dirPath)
	c.Assert(err, gc.IsNil)
	err = os.Remove(filepath.Join(dirPath, "revision"))
	c.Assert(err, gc.IsNil)

	bundle = extBundleDir(c, dirPath)
	manifest, err := bundle.Manifest()
	c.Assert(err, gc.IsNil)
	c.Assert(manifest, gc.DeepEquals, set.NewStrings(dummyManifest...))
}

func (s *BundleSuite) TestManifestSymlink(c *gc.C) {
	srcPath := testing.Charms.ClonedDirPath(c.MkDir(), "dummy")
	if err := os.Symlink("../target", filepath.Join(srcPath, "hooks/symlink")); err != nil {
		c.Skip("cannot symlink")
	}
	expected := append([]string{"hooks/symlink"}, dummyManifest...)

	bundle := bundleDir(c, srcPath)
	manifest, err := bundle.Manifest()
	c.Assert(err, gc.IsNil)
	c.Assert(manifest, gc.DeepEquals, set.NewStrings(expected...))
}

func (s *BundleSuite) TestExpandTo(c *gc.C) {
	bundle, err := charm.ReadBundle(s.bundlePath)
	c.Assert(err, gc.IsNil)

	path := filepath.Join(c.MkDir(), "charm")
	err = bundle.ExpandTo(path)
	c.Assert(err, gc.IsNil)

	dir, err := charm.ReadDir(path)
	c.Assert(err, gc.IsNil)
	checkDummy(c, dir, path)
}

func (s *BundleSuite) prepareBundle(c *gc.C, charmDir *charm.Dir, bundlePath string) {
	file, err := os.Create(bundlePath)
	c.Assert(err, gc.IsNil)
	defer file.Close()
	zipw := zip.NewWriter(file)
	defer zipw.Close()

	h := &zip.FileHeader{Name: "revision"}
	h.SetMode(syscall.S_IFREG | 0644)
	w, err := zipw.CreateHeader(h)
	c.Assert(err, gc.IsNil)
	_, err = w.Write([]byte(strconv.Itoa(charmDir.Revision())))

	h = &zip.FileHeader{Name: "metadata.yaml", Method: zip.Deflate}
	h.SetMode(0644)
	w, err = zipw.CreateHeader(h)
	c.Assert(err, gc.IsNil)
	data, err := goyaml.Marshal(charmDir.Meta())
	c.Assert(err, gc.IsNil)
	_, err = w.Write(data)
	c.Assert(err, gc.IsNil)

	for name := range charmDir.Meta().Hooks() {
		hookName := filepath.Join("hooks", name)
		h = &zip.FileHeader{
			Name:   hookName,
			Method: zip.Deflate,
		}
		// Force it non-executable
		h.SetMode(0644)
		w, err := zipw.CreateHeader(h)
		c.Assert(err, gc.IsNil)
		_, err = w.Write([]byte("not important"))
		c.Assert(err, gc.IsNil)
	}
}

func (s *BundleSuite) TestExpandToSetsHooksExecutable(c *gc.C) {
	charmDir := testing.Charms.ClonedDir(c.MkDir(), "all-hooks")
	// Bundle manually, so we can check ExpandTo(), unaffected
	// by BundleTo()'s behavior
	bundlePath := filepath.Join(c.MkDir(), "bundle.charm")
	s.prepareBundle(c, charmDir, bundlePath)
	bundle, err := charm.ReadBundle(bundlePath)
	c.Assert(err, gc.IsNil)

	path := filepath.Join(c.MkDir(), "charm")
	err = bundle.ExpandTo(path)
	c.Assert(err, gc.IsNil)

	_, err = charm.ReadDir(path)
	c.Assert(err, gc.IsNil)

	for name := range bundle.Meta().Hooks() {
		hookName := string(name)
		info, err := os.Stat(filepath.Join(path, "hooks", hookName))
		c.Assert(err, gc.IsNil)
		perm := info.Mode() & 0777
		c.Assert(perm&0100 != 0, gc.Equals, true, gc.Commentf("hook %q is not executable", hookName))
	}
}

func (s *BundleSuite) TestBundleFileModes(c *gc.C) {
	// Apply subtler mode differences than can be expressed in Bazaar.
	srcPath := testing.Charms.ClonedDirPath(c.MkDir(), "dummy")
	modes := []struct {
		path string
		mode os.FileMode
	}{
		{"hooks/install", 0751},
		{"empty", 0750},
		{"src/hello.c", 0614},
	}
	for _, m := range modes {
		err := os.Chmod(filepath.Join(srcPath, m.path), m.mode)
		c.Assert(err, gc.IsNil)
	}
	var haveSymlinks = true
	if err := os.Symlink("../target", filepath.Join(srcPath, "hooks/symlink")); err != nil {
		haveSymlinks = false
	}

	// Bundle and extract the charm to a new directory.
	bundle := bundleDir(c, srcPath)
	path := c.MkDir()
	err := bundle.ExpandTo(path)
	c.Assert(err, gc.IsNil)

	// Check sensible file modes once round-tripped.
	info, err := os.Stat(filepath.Join(path, "src", "hello.c"))
	c.Assert(err, gc.IsNil)
	c.Assert(info.Mode()&0777, gc.Equals, os.FileMode(0644))
	c.Assert(info.Mode()&os.ModeType, gc.Equals, os.FileMode(0))

	info, err = os.Stat(filepath.Join(path, "hooks", "install"))
	c.Assert(err, gc.IsNil)
	c.Assert(info.Mode()&0777, gc.Equals, os.FileMode(0755))
	c.Assert(info.Mode()&os.ModeType, gc.Equals, os.FileMode(0))

	info, err = os.Stat(filepath.Join(path, "empty"))
	c.Assert(err, gc.IsNil)
	c.Assert(info.Mode()&0777, gc.Equals, os.FileMode(0755))

	if haveSymlinks {
		target, err := os.Readlink(filepath.Join(path, "hooks", "symlink"))
		c.Assert(err, gc.IsNil)
		c.Assert(target, gc.Equals, "../target")
	}
}

func (s *BundleSuite) TestBundleRevisionFile(c *gc.C) {
	charmDir := testing.Charms.ClonedDirPath(c.MkDir(), "dummy")
	revPath := filepath.Join(charmDir, "revision")

	// Missing revision file
	err := os.Remove(revPath)
	c.Assert(err, gc.IsNil)

	bundle := extBundleDir(c, charmDir)
	c.Assert(bundle.Revision(), gc.Equals, 0)

	// Missing revision file with old revision in metadata
	file, err := os.OpenFile(filepath.Join(charmDir, "metadata.yaml"), os.O_WRONLY|os.O_APPEND, 0)
	c.Assert(err, gc.IsNil)
	_, err = file.Write([]byte("\nrevision: 1234\n"))
	c.Assert(err, gc.IsNil)

	bundle = extBundleDir(c, charmDir)
	c.Assert(bundle.Revision(), gc.Equals, 1234)

	// Revision file with bad content
	err = ioutil.WriteFile(revPath, []byte("garbage"), 0666)
	c.Assert(err, gc.IsNil)

	path := extBundleDirPath(c, charmDir)
	bundle, err = charm.ReadBundle(path)
	c.Assert(err, gc.ErrorMatches, "invalid revision file")
	c.Assert(bundle, gc.IsNil)
}

func (s *BundleSuite) TestBundleSetRevision(c *gc.C) {
	bundle, err := charm.ReadBundle(s.bundlePath)
	c.Assert(err, gc.IsNil)

	c.Assert(bundle.Revision(), gc.Equals, 1)
	bundle.SetRevision(42)
	c.Assert(bundle.Revision(), gc.Equals, 42)

	path := filepath.Join(c.MkDir(), "charm")
	err = bundle.ExpandTo(path)
	c.Assert(err, gc.IsNil)

	dir, err := charm.ReadDir(path)
	c.Assert(err, gc.IsNil)
	c.Assert(dir.Revision(), gc.Equals, 42)
}

func (s *BundleSuite) TestExpandToWithBadLink(c *gc.C) {
	charmDir := testing.Charms.ClonedDirPath(c.MkDir(), "dummy")
	badLink := filepath.Join(charmDir, "hooks", "badlink")

	// Symlink targeting a path outside of the charm.
	err := os.Symlink("../../target", badLink)
	c.Assert(err, gc.IsNil)

	bundle := extBundleDir(c, charmDir)
	c.Assert(err, gc.IsNil)

	path := filepath.Join(c.MkDir(), "charm")
	err = bundle.ExpandTo(path)
	c.Assert(err, gc.ErrorMatches, `cannot extract "hooks/badlink": symlink "../../target" leads out of scope`)

	// Symlink targeting an absolute path.
	os.Remove(badLink)
	err = os.Symlink("/target", badLink)
	c.Assert(err, gc.IsNil)

	bundle = extBundleDir(c, charmDir)
	c.Assert(err, gc.IsNil)

	path = filepath.Join(c.MkDir(), "charm")
	err = bundle.ExpandTo(path)
	c.Assert(err, gc.ErrorMatches, `cannot extract "hooks/badlink": symlink "/target" is absolute`)
}

func extBundleDirPath(c *gc.C, dirpath string) string {
	path := filepath.Join(c.MkDir(), "bundle.charm")
	c.Logf("%#v", fmt.Sprintf("cd %s; zip --fifo --symlinks -r %s .", dirpath, path))
	cmd := exec.Command("/bin/sh", "-c", fmt.Sprintf("cd %s; zip --fifo --symlinks -r %s .", dirpath, path))
	output, err := cmd.CombinedOutput()
	c.Assert(err, gc.IsNil, gc.Commentf("Command output: %s", output))
	return path
}

func extBundleDir(c *gc.C, dirpath string) *charm.Bundle {
	path := extBundleDirPath(c, dirpath)
	bundle, err := charm.ReadBundle(path)
	c.Assert(err, gc.IsNil)
	return bundle
}

func bundleDir(c *gc.C, dirpath string) *charm.Bundle {
	dir, err := charm.ReadDir(dirpath)
	c.Assert(err, gc.IsNil)
	buf := new(bytes.Buffer)
	err = dir.BundleTo(buf)
	c.Assert(err, gc.IsNil)
	bundle, err := charm.ReadBundleBytes(buf.Bytes())
	c.Assert(err, gc.IsNil)
	return bundle
}
<commit_msg>Remove debug line<commit_after>// Copyright 2011, 2012, 2013 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package charm_test

import (
	"archive/zip"
	"bytes"
	"fmt"
	"io/ioutil"
	"os"
	"os/exec"
	"path/filepath"
	"strconv"
	"syscall"

	jc "github.com/juju/testing/checkers"
	gc "launchpad.net/gocheck"
	"launchpad.net/goyaml"

	"launchpad.net/juju-core/charm"
	"launchpad.net/juju-core/testing"
	"launchpad.net/juju-core/utils/set"
)

type BundleSuite struct {
	repo       *testing.Repo
	bundlePath string
}

var _ = gc.Suite(&BundleSuite{})

func (s *BundleSuite) SetUpSuite(c *gc.C) {
	s.bundlePath = testing.Charms.BundlePath(c.MkDir(), "dummy")
}

var dummyManifest = []string{
	"config.yaml",
	"empty",
	"hooks",
	"hooks/install",
	"metadata.yaml",
	"revision",
	"src",
	"src/hello.c",
}

func (s *BundleSuite) TestReadBundle(c *gc.C) {
	bundle, err := charm.ReadBundle(s.bundlePath)
	c.Assert(err, gc.IsNil)
	checkDummy(c, bundle, s.bundlePath)
}

func (s *BundleSuite) TestReadBundleWithoutConfig(c *gc.C) {
	path := testing.Charms.BundlePath(c.MkDir(), "varnish")
	bundle, err := charm.ReadBundle(path)
	c.Assert(err, gc.IsNil)

	// A lacking config.yaml file still causes a proper
	// Config value to be returned.
	c.Assert(bundle.Config().Options, gc.HasLen, 0)
}

func (s *BundleSuite) TestReadBundleBytes(c *gc.C) {
	data, err := ioutil.ReadFile(s.bundlePath)
	c.Assert(err, gc.IsNil)

	bundle, err := charm.ReadBundleBytes(data)
	c.Assert(err, gc.IsNil)
	checkDummy(c, bundle, "")
}

func (s *BundleSuite) TestManifest(c *gc.C) {
	bundle, err := charm.ReadBundle(s.bundlePath)
	c.Assert(err, gc.IsNil)
	manifest, err := bundle.Manifest()
	c.Assert(err, gc.IsNil)
	c.Assert(manifest, jc.DeepEquals, set.NewStrings(dummyManifest...))
}

func (s *BundleSuite) TestManifestNoRevision(c *gc.C) {
	bundle, err := charm.ReadBundle(s.bundlePath)
	c.Assert(err, gc.IsNil)
	dirPath := c.MkDir()
	err = bundle.ExpandTo(dirPath)
	c.Assert(err, gc.IsNil)
	err = os.Remove(filepath.Join(dirPath, "revision"))
	c.Assert(err, gc.IsNil)

	bundle = extBundleDir(c, dirPath)
	manifest, err := bundle.Manifest()
	c.Assert(err, gc.IsNil)
	c.Assert(manifest, gc.DeepEquals, set.NewStrings(dummyManifest...))
}

func (s *BundleSuite) TestManifestSymlink(c *gc.C) {
	srcPath := testing.Charms.ClonedDirPath(c.MkDir(), "dummy")
	if err := os.Symlink("../target", filepath.Join(srcPath, "hooks/symlink")); err != nil {
		c.Skip("cannot symlink")
	}
	expected := append([]string{"hooks/symlink"}, dummyManifest...)

	bundle := bundleDir(c, srcPath)
	manifest, err := bundle.Manifest()
	c.Assert(err, gc.IsNil)
	c.Assert(manifest, gc.DeepEquals, set.NewStrings(expected...))
}

func (s *BundleSuite) TestExpandTo(c *gc.C) {
	bundle, err := charm.ReadBundle(s.bundlePath)
	c.Assert(err, gc.IsNil)

	path := filepath.Join(c.MkDir(), "charm")
	err = bundle.ExpandTo(path)
	c.Assert(err, gc.IsNil)

	dir, err := charm.ReadDir(path)
	c.Assert(err, gc.IsNil)
	checkDummy(c, dir, path)
}

func (s *BundleSuite) prepareBundle(c *gc.C, charmDir *charm.Dir, bundlePath string) {
	file, err := os.Create(bundlePath)
	c.Assert(err, gc.IsNil)
	defer file.Close()
	zipw := zip.NewWriter(file)
	defer zipw.Close()

	h := &zip.FileHeader{Name: "revision"}
	h.SetMode(syscall.S_IFREG | 0644)
	w, err := zipw.CreateHeader(h)
	c.Assert(err, gc.IsNil)
	_, err = w.Write([]byte(strconv.Itoa(charmDir.Revision())))

	h = &zip.FileHeader{Name: "metadata.yaml", Method: zip.Deflate}
	h.SetMode(0644)
	w, err = zipw.CreateHeader(h)
	c.Assert(err, gc.IsNil)
	data, err := goyaml.Marshal(charmDir.Meta())
	c.Assert(err, gc.IsNil)
	_, err = w.Write(data)
	c.Assert(err, gc.IsNil)

	for name := range charmDir.Meta().Hooks() {
		hookName := filepath.Join("hooks", name)
		h = &zip.FileHeader{
			Name:   hookName,
			Method: zip.Deflate,
		}
		// Force it non-executable
		h.SetMode(0644)
		w, err := zipw.CreateHeader(h)
		c.Assert(err, gc.IsNil)
		_, err = w.Write([]byte("not important"))
		c.Assert(err, gc.IsNil)
	}
}

func (s *BundleSuite) TestExpandToSetsHooksExecutable(c *gc.C) {
	charmDir := testing.Charms.ClonedDir(c.MkDir(), "all-hooks")
	// Bundle manually, so we can check ExpandTo(), unaffected
	// by BundleTo()'s behavior
	bundlePath := filepath.Join(c.MkDir(), "bundle.charm")
	s.prepareBundle(c, charmDir, bundlePath)
	bundle, err := charm.ReadBundle(bundlePath)
	c.Assert(err, gc.IsNil)

	path := filepath.Join(c.MkDir(), "charm")
	err = bundle.ExpandTo(path)
	c.Assert(err, gc.IsNil)

	_, err = charm.ReadDir(path)
	c.Assert(err, gc.IsNil)

	for name := range bundle.Meta().Hooks() {
		hookName := string(name)
		info, err := os.Stat(filepath.Join(path, "hooks", hookName))
		c.Assert(err, gc.IsNil)
		perm := info.Mode() & 0777
		c.Assert(perm&0100 != 0, gc.Equals, true, gc.Commentf("hook %q is not executable", hookName))
	}
}

func (s *BundleSuite) TestBundleFileModes(c *gc.C) {
	// Apply subtler mode differences than can be expressed in Bazaar.
	srcPath := testing.Charms.ClonedDirPath(c.MkDir(), "dummy")
	modes := []struct {
		path string
		mode os.FileMode
	}{
		{"hooks/install", 0751},
		{"empty", 0750},
		{"src/hello.c", 0614},
	}
	for _, m := range modes {
		err := os.Chmod(filepath.Join(srcPath, m.path), m.mode)
		c.Assert(err, gc.IsNil)
	}
	var haveSymlinks = true
	if err := os.Symlink("../target", filepath.Join(srcPath, "hooks/symlink")); err != nil {
		haveSymlinks = false
	}

	// Bundle and extract the charm to a new directory.
	bundle := bundleDir(c, srcPath)
	path := c.MkDir()
	err := bundle.ExpandTo(path)
	c.Assert(err, gc.IsNil)

	// Check sensible file modes once round-tripped.
	info, err := os.Stat(filepath.Join(path, "src", "hello.c"))
	c.Assert(err, gc.IsNil)
	c.Assert(info.Mode()&0777, gc.Equals, os.FileMode(0644))
	c.Assert(info.Mode()&os.ModeType, gc.Equals, os.FileMode(0))

	info, err = os.Stat(filepath.Join(path, "hooks", "install"))
	c.Assert(err, gc.IsNil)
	c.Assert(info.Mode()&0777, gc.Equals, os.FileMode(0755))
	c.Assert(info.Mode()&os.ModeType, gc.Equals, os.FileMode(0))

	info, err = os.Stat(filepath.Join(path, "empty"))
	c.Assert(err, gc.IsNil)
	c.Assert(info.Mode()&0777, gc.Equals, os.FileMode(0755))

	if haveSymlinks {
		target, err := os.Readlink(filepath.Join(path, "hooks", "symlink"))
		c.Assert(err, gc.IsNil)
		c.Assert(target, gc.Equals, "../target")
	}
}

func (s *BundleSuite) TestBundleRevisionFile(c *gc.C) {
	charmDir := testing.Charms.ClonedDirPath(c.MkDir(), "dummy")
	revPath := filepath.Join(charmDir, "revision")

	// Missing revision file
	err := os.Remove(revPath)
	c.Assert(err, gc.IsNil)

	bundle := extBundleDir(c, charmDir)
	c.Assert(bundle.Revision(), gc.Equals, 0)

	// Missing revision file with old revision in metadata
	file, err := os.OpenFile(filepath.Join(charmDir, "metadata.yaml"), os.O_WRONLY|os.O_APPEND, 0)
	c.Assert(err, gc.IsNil)
	_, err = file.Write([]byte("\nrevision: 1234\n"))
	c.Assert(err, gc.IsNil)

	bundle = extBundleDir(c, charmDir)
	c.Assert(bundle.Revision(), gc.Equals, 1234)

	// Revision file with bad content
	err = ioutil.WriteFile(revPath, []byte("garbage"), 0666)
	c.Assert(err, gc.IsNil)

	path := extBundleDirPath(c, charmDir)
	bundle, err = charm.ReadBundle(path)
	c.Assert(err, gc.ErrorMatches, "invalid revision file")
	c.Assert(bundle, gc.IsNil)
}

func (s *BundleSuite) TestBundleSetRevision(c *gc.C) {
	bundle, err := charm.ReadBundle(s.bundlePath)
	c.Assert(err, gc.IsNil)

	c.Assert(bundle.Revision(), gc.Equals, 1)
	bundle.SetRevision(42)
	c.Assert(bundle.Revision(), gc.Equals, 42)

	path := filepath.Join(c.MkDir(), "charm")
	err = bundle.ExpandTo(path)
	c.Assert(err, gc.IsNil)

	dir, err := charm.ReadDir(path)
	c.Assert(err, gc.IsNil)
	c.Assert(dir.Revision(), gc.Equals, 42)
}

func (s *BundleSuite) TestExpandToWithBadLink(c *gc.C) {
	charmDir := testing.Charms.ClonedDirPath(c.MkDir(), "dummy")
	badLink := filepath.Join(charmDir, "hooks", "badlink")

	// Symlink targeting a path outside of the charm.
	err := os.Symlink("../../target", badLink)
	c.Assert(err, gc.IsNil)

	bundle := extBundleDir(c, charmDir)
	c.Assert(err, gc.IsNil)

	path := filepath.Join(c.MkDir(), "charm")
	err = bundle.ExpandTo(path)
	c.Assert(err, gc.ErrorMatches, `cannot extract "hooks/badlink": symlink "../../target" leads out of scope`)

	// Symlink targeting an absolute path.
	os.Remove(badLink)
	err = os.Symlink("/target", badLink)
	c.Assert(err, gc.IsNil)

	bundle = extBundleDir(c, charmDir)
	c.Assert(err, gc.IsNil)

	path = filepath.Join(c.MkDir(), "charm")
	err = bundle.ExpandTo(path)
	c.Assert(err, gc.ErrorMatches, `cannot extract "hooks/badlink": symlink "/target" is absolute`)
}

func extBundleDirPath(c *gc.C, dirpath string) string {
	path := filepath.Join(c.MkDir(), "bundle.charm")
	cmd := exec.Command("/bin/sh", "-c", fmt.Sprintf("cd %s; zip --fifo --symlinks -r %s .", dirpath, path))
	output, err := cmd.CombinedOutput()
	c.Assert(err, gc.IsNil, gc.Commentf("Command output: %s", output))
	return path
}

func extBundleDir(c *gc.C, dirpath string) *charm.Bundle {
	path := extBundleDirPath(c, dirpath)
	bundle, err := charm.ReadBundle(path)
	c.Assert(err, gc.IsNil)
	return bundle
}

func bundleDir(c *gc.C, dirpath string) *charm.Bundle {
	dir, err := charm.ReadDir(dirpath)
	c.Assert(err, gc.IsNil)
	buf := new(bytes.Buffer)
	err = dir.BundleTo(buf)
	c.Assert(err, gc.IsNil)
	bundle, err := charm.ReadBundleBytes(buf.Bytes())
	c.Assert(err, gc.IsNil)
	return bundle
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"os/exec"
	"syscall"
)

const numFifos = 3
const doneCmd = "done"

var fifoDir, stateJson string

func main() {
	if len(os.Args) < 2 {
		log.Fatal("Must specify app file or command.")
		return
	}

	var err error

	if fifoDir, err = os.Getwd(); err != nil {
		log.Fatal(err)
	}
	stateJson = fifoDir + "/.nodeswitch-state.json"

	var state *State
	if exists, err := exists(stateJson); !exists {
		if err != nil {
			log.Fatal(err)
		}

		if os.Args[1] == doneCmd {
			return
		}

		state, err = initNodes()
		if err != nil {
			log.Fatal(err)
		}
	} else {
		state, err = readState(stateJson)
		if err != nil {
			log.Fatal(err)
		}

		if state.Running {
			log.Fatal("nodeswitch is already running.")
		}
	}

	if os.Args[1] == doneCmd {
		err = cleanup(state)
		if err != nil {
			log.Fatal(err)
		}

		return
	}

	state.Running = true
	if err = state.Write(stateJson); err != nil {
		log.Fatal(err)
	}

	// check if the last node process is still running, and if so, kill it
	lastProcess, err := os.FindProcess(state.Nodes[state.CurrNode])
	if err == nil {
		lastProcess.Signal(os.Interrupt)
	}

	// start a new process in its place
	state.StartProcess(state.CurrNode)

	state.CurrNode = (state.CurrNode + 1) % len(state.Nodes)
	if err = state.StartApp(os.Args[1]); err != nil {
		log.Fatal(err)
	}

	state.Running = false
	if err := state.Write(stateJson); err != nil {
		log.Fatal(err)
	}
}

func initNodes() (*State, error) {
	// start every node process except the last one
	state := State{CurrNode: numFifos - 1}
	state.Nodes = make([]int, numFifos)
	for i := range state.Nodes {
		if err := MakeFifo(i); err != nil {
			return nil, err
		}

		if i == len(state.Nodes)-1 {
			continue
		}

		if err := state.StartProcess(i); err != nil {
			return nil, err
		}
	}

	return &state, nil
}

func (this *State) StartProcess(fid int) error {
	cmd := exec.Command("node", fifoPath(fid))
	cmd.Stdin = os.Stdin
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr

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

	this.Nodes[fid] = cmd.Process.Pid

	return nil
}

func (this *State) EndProcess(fid int) error {
	process, err := os.FindProcess(this.Nodes[fid])
	if err != nil {
		return err
	}
	process.Signal(os.Interrupt)

	return nil
}

func MakeFifo(fid int) error {
	path := fifoPath(fid)
	err := os.Remove(path)
	if !os.IsNotExist(err) && err != nil {
		return err
	}

	return syscall.Mkfifo(path, uint32(os.ModeNamedPipe|0644))
}

func fifoPath(fid int) string {
	return fmt.Sprintf("%s/.nodeswitch-fifo%d", fifoDir, fid)
}

func (this *State) StartApp(path string) error {
	// reads in the entire app js file, so it's assumed it isn't too large
	appBytes, err := ioutil.ReadFile(path)
	if err != nil {
		return err
	}

	err = ioutil.WriteFile(fifoPath(this.CurrNode), appBytes, os.ModePerm)
	if err != nil {
		return err
	}

	return nil
}

type State struct {
	Running  bool
	CurrNode int
	Nodes    []int
}

func readState(path string) (*State, error) {
	jsonStr, err := ioutil.ReadFile(path)
	if err != nil {
		return nil, err
	}

	var state State
	err = json.Unmarshal(jsonStr, &state)
	if err != nil {
		return nil, err
	}

	return &state, nil
}

func (this *State) Write(path string) error {
	err := os.Remove(path)
	if !os.IsNotExist(err) && err != nil {
		return err
	}

	file, err := os.Create(path)
	if err != nil {
		return err
	}
	defer file.Close()

	b, err := json.Marshal(this)
	if err != nil {
		return err
	}

	_, err = file.Write(b)
	return err
}

func cleanup(state *State) error {
	for i, pid := range state.Nodes {
		proc, err := os.FindProcess(pid)
		if err == nil {
			proc.Signal(os.Interrupt)
		}

		if err = os.Remove(fifoPath(i)); err != nil {
			return err
		}
	}

	if err := os.Remove(stateJson); err != nil {
		return err
	}

	return nil
}

func exists(path string) (bool, error) {
	_, err := os.Stat(path)
	if err == nil {
		return true, nil
	}
	if os.IsNotExist(err) {
		return false, nil
	}
	return false, err
}
<commit_msg>Remove running state detection<commit_after>package main

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"os/exec"
	"syscall"
)

const numFifos = 3
const doneCmd = "done"

var fifoDir, stateJson string

func main() {
	if len(os.Args) < 2 {
		log.Fatal("Must specify app file or command.")
		return
	}

	var err error

	if fifoDir, err = os.Getwd(); err != nil {
		log.Fatal(err)
	}
	stateJson = fifoDir + "/.nodeswitch-state.json"

	var state *State
	if exists, err := exists(stateJson); !exists {
		if err != nil {
			log.Fatal(err)
		}

		if os.Args[1] == doneCmd {
			return
		}

		state, err = initNodes()
		if err != nil {
			log.Fatal(err)
		}
	} else {
		state, err = readState(stateJson)
		if err != nil {
			log.Fatal(err)
		}
	}

	if os.Args[1] == doneCmd {
		err = cleanup(state)
		if err != nil {
			log.Fatal(err)
		}

		return
	}

	// check if the last node process is still running, and if so, kill it
	lastProcess, err := os.FindProcess(state.Nodes[state.CurrNode])
	if err == nil {
		lastProcess.Signal(os.Interrupt)
	}

	// start a new process in its place
	state.StartProcess(state.CurrNode)

	state.CurrNode = (state.CurrNode + 1) % len(state.Nodes)
	if err = state.StartApp(os.Args[1]); err != nil {
		log.Fatal(err)
	}

	if err := state.Write(stateJson); err != nil {
		log.Fatal(err)
	}
}

func initNodes() (*State, error) {
	// start every node process except the last one
	state := State{CurrNode: numFifos - 1}
	state.Nodes = make([]int, numFifos)
	for i := range state.Nodes {
		if err := MakeFifo(i); err != nil {
			return nil, err
		}

		if i == len(state.Nodes)-1 {
			continue
		}

		if err := state.StartProcess(i); err != nil {
			return nil, err
		}
	}

	return &state, nil
}

func (this *State) StartProcess(fid int) error {
	cmd := exec.Command("node", fifoPath(fid))
	cmd.Stdin = os.Stdin
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr

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

	this.Nodes[fid] = cmd.Process.Pid

	return nil
}

func (this *State) EndProcess(fid int) error {
	process, err := os.FindProcess(this.Nodes[fid])
	if err != nil {
		return err
	}
	process.Signal(os.Interrupt)

	return nil
}

func MakeFifo(fid int) error {
	path := fifoPath(fid)
	err := os.Remove(path)
	if !os.IsNotExist(err) && err != nil {
		return err
	}

	return syscall.Mkfifo(path, uint32(os.ModeNamedPipe|0644))
}

func fifoPath(fid int) string {
	return fmt.Sprintf("%s/.nodeswitch-fifo%d", fifoDir, fid)
}

func (this *State) StartApp(path string) error {
	// reads in the entire app js file, so it's assumed it isn't too large
	appBytes, err := ioutil.ReadFile(path)
	if err != nil {
		return err
	}

	err = ioutil.WriteFile(fifoPath(this.CurrNode), appBytes, os.ModePerm)
	if err != nil {
		return err
	}

	return nil
}

type State struct {
	CurrNode int
	Nodes    []int
}

func readState(path string) (*State, error) {
	jsonStr, err := ioutil.ReadFile(path)
	if err != nil {
		return nil, err
	}

	var state State
	err = json.Unmarshal(jsonStr, &state)
	if err != nil {
		return nil, err
	}

	return &state, nil
}

func (this *State) Write(path string) error {
	err := os.Remove(path)
	if !os.IsNotExist(err) && err != nil {
		return err
	}

	file, err := os.Create(path)
	if err != nil {
		return err
	}
	defer file.Close()

	b, err := json.Marshal(this)
	if err != nil {
		return err
	}

	_, err = file.Write(b)
	return err
}

func cleanup(state *State) error {
	for i, pid := range state.Nodes {
		proc, err := os.FindProcess(pid)
		if err == nil {
			proc.Signal(os.Interrupt)
		}

		if err = os.Remove(fifoPath(i)); err != nil {
			return err
		}
	}

	if err := os.Remove(stateJson); err != nil {
		return err
	}

	return nil
}

func exists(path string) (bool, error) {
	_, err := os.Stat(path)
	if err == nil {
		return true, nil
	}
	if os.IsNotExist(err) {
		return false, nil
	}
	return false, err
}
<|endoftext|>


<commit_before>/*
   Copyright 2015 Daniel Gruber, dgruber@univa.com

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.
*/

package ugego

import (
	"errors"
	"fmt"
	"log"
	"os"
	"os/exec"
	"strconv"
	"strings"
)

// UserList is a Univa Grid Engine access control list or Department.
type UserList struct {
	Name    string
	Type    string
	FShare  int
	OTicket int
	Entries []string
}

// ParseUserList parses an Univa Grid Engine ACL by the given name into an struct.
// The expected structure of ul is a multiline string in the Univa Grid Engine format:
// name    exclude
// type    ACL
// fshare  0
// oticket 0
// entries daniel,root,%wheel
func ParseUserList(ul string) (userList *UserList, err error) {
	var ol UserList
	el := strings.Split(ul, "\n")
	if len(el) != 5 {
		return nil, errors.New(fmt.Sprintf("User list does not have 5 lines, it has %d.\n", len(el)))
	}
	name := strings.Split(el[0], "name")
	if len(name) != 2 {
		return nil, errors.New("Error during name parsing.")
	}
	ol.Name = strings.TrimSpace(name[1])
	t := strings.Split(el[1], "type")
	if len(t) != 2 {
		return nil, errors.New("Error during type parsing.")
	}
	ol.Type = strings.TrimSpace(t[1])
	fs := strings.Split(el[2], "fshare")
	if len(fs) != 2 {
		return nil, errors.New("Error during fshare parsing.")
	}
	ot := strings.Split(el[3], "oticket")
	if len(ot) != 2 {
		return nil, errors.New("Error during oticket parsing.")
	}
	ent := strings.Split(el[4], "entries")
	if len(ent) != 2 {
		return nil, errors.New("Error during entries parsing.")
	}
	if ol.FShare, err = strconv.Atoi(strings.TrimSpace(fs[1])); err != nil {
		return nil, err
	}
	if ol.OTicket, err = strconv.Atoi(strings.TrimSpace(ot[1])); err != nil {
		return nil, err
	}
	ol.Entries = strings.Split(strings.TrimSpace(ent[1]), ",")
	return &ol, nil
}

// GetUserLists calls qconf -su <listOfUl> and parses the output
// into UserList structs.
func GetUserLists(userlist ...string) ([]UserList, error) {
	rootPath := os.Getenv("SGE_ROOT")
	if rootPath == "" {
		return nil, errors.New("$SGE_ROOT environment variable not set")
	}
	qconf := fmt.Sprintf("%s/bin/lx-amd64/qconf", rootPath)

	// create comma separated list of user list names
	var csvUserList string
	for k, v := range userlist {
		if k == 0 {
			csvUserList = fmt.Sprintf("%s", v)
		} else {
			csvUserList = fmt.Sprintf("%s,%s", csvUserList, v)
		}
	}
	log.Printf("Calling: qconf -su %s\n", csvUserList)

	cmd := exec.Command(qconf, "-su", csvUserList)
	if out, errOut := cmd.CombinedOutput(); errOut == nil {
		// empty line is the delimiter
		uls := strings.Split(string(out), "\n\n")
		if len(uls) == 0 {
			return nil, errors.New(fmt.Sprintf("Could not split output: %s", string(out)))
		}
		outputList := make([]UserList, len(uls), len(uls))
		for i, ul := range uls {
			parsedUserList, errParse := ParseUserList(strings.TrimSpace(uls[i]))
			if errParse != nil {
				log.Printf("Error during parsing user list: %s\n%s\n", errParse, ul)
				log.Printf("%s", ul)
				return nil, errParse
			}
			outputList[i] = *parsedUserList
		}
		return outputList, nil
	} else {
		log.Printf("Error: %s\n", out)
		return nil, errOut
	}
}
<commit_msg>DG: Removed logging.<commit_after>/*
   Copyright 2015 Daniel Gruber, dgruber@univa.com

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.
*/

package ugego

import (
	"errors"
	"fmt"
	"log"
	"os"
	"os/exec"
	"strconv"
	"strings"
)

// UserList is a Univa Grid Engine access control list or Department.
type UserList struct {
	Name    string
	Type    string
	FShare  int
	OTicket int
	Entries []string
}

// ParseUserList parses an Univa Grid Engine ACL by the given name into an struct.
// The expected structure of ul is a multiline string in the Univa Grid Engine format:
// name    exclude
// type    ACL
// fshare  0
// oticket 0
// entries daniel,root,%wheel
func ParseUserList(ul string) (userList *UserList, err error) {
	var ol UserList
	el := strings.Split(ul, "\n")
	if len(el) != 5 {
		return nil, errors.New(fmt.Sprintf("User list does not have 5 lines, it has %d.\n", len(el)))
	}
	name := strings.Split(el[0], "name")
	if len(name) != 2 {
		return nil, errors.New("Error during name parsing.")
	}
	ol.Name = strings.TrimSpace(name[1])
	t := strings.Split(el[1], "type")
	if len(t) != 2 {
		return nil, errors.New("Error during type parsing.")
	}
	ol.Type = strings.TrimSpace(t[1])
	fs := strings.Split(el[2], "fshare")
	if len(fs) != 2 {
		return nil, errors.New("Error during fshare parsing.")
	}
	ot := strings.Split(el[3], "oticket")
	if len(ot) != 2 {
		return nil, errors.New("Error during oticket parsing.")
	}
	ent := strings.Split(el[4], "entries")
	if len(ent) != 2 {
		return nil, errors.New("Error during entries parsing.")
	}
	if ol.FShare, err = strconv.Atoi(strings.TrimSpace(fs[1])); err != nil {
		return nil, err
	}
	if ol.OTicket, err = strconv.Atoi(strings.TrimSpace(ot[1])); err != nil {
		return nil, err
	}
	ol.Entries = strings.Split(strings.TrimSpace(ent[1]), ",")
	return &ol, nil
}

// GetUserLists calls qconf -su <listOfUl> and parses the output
// into UserList structs.
func GetUserLists(userlist ...string) ([]UserList, error) {
	rootPath := os.Getenv("SGE_ROOT")
	if rootPath == "" {
		return nil, errors.New("$SGE_ROOT environment variable not set")
	}
	qconf := fmt.Sprintf("%s/bin/lx-amd64/qconf", rootPath)

	// create comma separated list of user list names
	var csvUserList string
	for k, v := range userlist {
		if k == 0 {
			csvUserList = fmt.Sprintf("%s", v)
		} else {
			csvUserList = fmt.Sprintf("%s,%s", csvUserList, v)
		}
	}

	if out, errOut := cmd.CombinedOutput(); errOut == nil {
		// empty line is the delimiter
		uls := strings.Split(string(out), "\n\n")
		if len(uls) == 0 {
			return nil, errors.New(fmt.Sprintf("Could not split output: %s", string(out)))
		}
		outputList := make([]UserList, len(uls), len(uls))
		for i, ul := range uls {
			parsedUserList, errParse := ParseUserList(strings.TrimSpace(uls[i]))
			if errParse != nil {
				log.Printf("Error during parsing user list: %s\n%s\n", errParse, ul)
				log.Printf("%s", ul)
				return nil, errParse
			}
			outputList[i] = *parsedUserList
		}
		return outputList, nil
	} else {
		log.Printf("Error: %s\n", out)
		return nil, errOut
	}
}
<|endoftext|>


<commit_before>// Copyright 2018 by caixw, All rights reserved.
// Use of this source code is governed by a MIT
// license that can be found in the LICENSE file.

package help

import (
	"errors"
	"fmt"
	"os"
)

var errNotExists = errors.New("不存在子命令")

var usages = map[string]func(){}

func init() {
	Register("help", usage)
}

// Do 执行子命令
func Do() error {
	if len(os.Args) < 3 {
		return errNotExists
	}

	fn, found := usages[os.Args[2]]
	if !found {
		return errNotExists
	}

	fn()

	return nil
}

// Register 注册 usage 函数
func Register(name string, fn func()) {
	if _, exists := usages[name]; exists {
		panic(fmt.Sprintln("存在同名的子命令:", name))
	}

	usages[name] = fn
}

func usage() {
	fmt.Println(`用法：web help subcommand

显示名为 subcommand 的子命令的用法。`)
}
<commit_msg>修正 help 包<commit_after>// Copyright 2018 by caixw, All rights reserved.
// Use of this source code is governed by a MIT
// license that can be found in the LICENSE file.

package help

import (
	"fmt"
	"os"
)

var usages = map[string]func(){}

func init() {
	Register("help", usage)
}

// Do 执行子命令
func Do() error {
	fn := usage
	if len(os.Args) >= 3 {
		if f, found := usages[os.Args[2]]; found {
			fn = f
		}
	}

	fn()
	return nil
}

// Register 注册 usage 函数
func Register(name string, fn func()) {
	if _, exists := usages[name]; exists {
		panic(fmt.Sprintln("存在同名的子命令:", name))
	}

	usages[name] = fn
}

func usage() {
	fmt.Println(`用法：web help subcommand

显示名为 subcommand 的子命令的用法。`)
}
<|endoftext|>


<commit_before>package common

import (
	"bytes"
	"errors"
	"strconv"
	"strings"
	"sync"

	"encoding/json"
	"log"

	"github.com/artemnikitin/here-tele-bot/hlp"
)

const (
	limitResult = 5
)

func GetPlacesWithGeocoding(api *hlp.HereApiConfig, q, loc string) (*BotResult, error) {
	res, err := api.DoGeocoding(map[string]string{
		"searchtext": loc,
		"gen":        "9",
	})
	if err != nil {
		return &BotResult{}, errors.New(err.Error())
	}
	if api.Debug {
		bytes, _ := json.Marshal(res)
		log.Println("Geocoding result:\n", string(bytes))
	}
	var radius int
	var lat, lon float64
	if len(res.Response.View) == 0 {
		return &BotResult{}, errors.New("Empty geocoding response")
	}
	switch res.Response.View[0].Result[0].MatchLevel {
	case "city":
		radius = 7000
	case "district":
		radius = 3000
	default:
		radius = 1500
	}
	lat = res.Response.View[0].Result[0].Location.DisplayPosition.Latitude
	lon = res.Response.View[0].Result[0].Location.DisplayPosition.Longitude
	return GetPlacesWithRadius(api, q, LocationToString(lat, lon), radius)
}

func GetPlacesWithRadius(api *hlp.HereApiConfig, q, loc string, radius int) (*BotResult, error) {
	var wg sync.WaitGroup
	res := &BotResult{
		Location: loc,
	}
	places, err := api.GetPlaces(map[string]string{
		"q":           q,
		"in":          loc + ";r=" + strconv.Itoa(radius),
		"refinements": "true",
		"tf":          "plain",
	})
	if err != nil {
		return res, errors.New(err.Error())
	}
	ch := make(chan *BotPlace, limitResult)
	count := 0
	for i := 0; i < len(places.Results.Items); i++ {
		wg.Add(1)
		go func(title, href string, dist int) {
			resp, err := api.GetPlaceDetails(href)
			if err == nil {
				short, err := api.ShortURL(resp.View)
				if err == nil {
					place := &BotPlace{
						Title:    title,
						Distance: dist,
						HereURL:  short.URL,
					}
					if resp.Extended.OpeningHours.IsOpen {
						place.OpeningHours = "Open now"
					} else {
						place.OpeningHours = resp.Extended.OpeningHours.Text
					}
					if len(resp.Contacts.Website) > 0 {
						place.URL = resp.Contacts.Website[0].Value
					}
					if resp.Icon != "" {
						place.IconURL = resp.Icon
					}
					ch <- place
				}
			}
			wg.Done()
		}(places.Results.Items[i].Title, places.Results.Items[i].Href, places.Results.Items[i].Distance)
		count++
		if count == limitResult {
			break
		}
	}
	wg.Wait()
	close(ch)

	for i := range ch {
		res.Places = append(res.Places, i)
	}
	if api.Debug {
		bytes, _ := json.Marshal(res)
		log.Println("Search places result:\n", string(bytes))
	}
	return res, nil
}

// TextForResponse transforms result from HERE API to message
func TextForResponse(results *BotResult) string {
	if results.Places == nil || len(results.Places) == 0 {
		return NothingFound
	}
	var buf bytes.Buffer
	for _, v := range results.Places {
		buf.WriteString("*")
		buf.WriteString(v.Title)
		buf.WriteString("*")
		if v.URL != "" {
			buf.WriteString(" (")
			buf.WriteString(v.URL)
			buf.WriteString(")")
		}
		buf.WriteString("\n")
		buf.WriteString("Distance: ")
		buf.WriteString(strconv.Itoa(v.Distance))
		buf.WriteString(" m. ")
		buf.WriteString("\n")
		if v.OpeningHours != "" {
			buf.WriteString(strings.Replace(v.OpeningHours, "
", " ", -1))
			buf.WriteString("\n")
		}
		buf.WriteString(v.HereURL)
		buf.WriteString("\n\n")
	}
	return buf.String()
}
<commit_msg>Fix goimports<commit_after>package common

import (
	"bytes"
	"encoding/json"
	"errors"
	"log"
	"strconv"
	"strings"
	"sync"

	"github.com/artemnikitin/here-tele-bot/hlp"
)

const (
	limitResult = 5
)

func GetPlacesWithGeocoding(api *hlp.HereApiConfig, q, loc string) (*BotResult, error) {
	res, err := api.DoGeocoding(map[string]string{
		"searchtext": loc,
		"gen":        "9",
	})
	if err != nil {
		return &BotResult{}, errors.New(err.Error())
	}
	if api.Debug {
		bytes, _ := json.Marshal(res)
		log.Println("Geocoding result:\n", string(bytes))
	}
	var radius int
	var lat, lon float64
	if len(res.Response.View) == 0 {
		return &BotResult{}, errors.New("Empty geocoding response")
	}
	switch res.Response.View[0].Result[0].MatchLevel {
	case "city":
		radius = 7000
	case "district":
		radius = 3000
	default:
		radius = 1500
	}
	lat = res.Response.View[0].Result[0].Location.DisplayPosition.Latitude
	lon = res.Response.View[0].Result[0].Location.DisplayPosition.Longitude
	return GetPlacesWithRadius(api, q, LocationToString(lat, lon), radius)
}

func GetPlacesWithRadius(api *hlp.HereApiConfig, q, loc string, radius int) (*BotResult, error) {
	var wg sync.WaitGroup
	res := &BotResult{
		Location: loc,
	}
	places, err := api.GetPlaces(map[string]string{
		"q":           q,
		"in":          loc + ";r=" + strconv.Itoa(radius),
		"refinements": "true",
		"tf":          "plain",
	})
	if err != nil {
		return res, errors.New(err.Error())
	}
	ch := make(chan *BotPlace, limitResult)
	count := 0
	for i := 0; i < len(places.Results.Items); i++ {
		wg.Add(1)
		go func(title, href string, dist int) {
			resp, err := api.GetPlaceDetails(href)
			if err == nil {
				short, err := api.ShortURL(resp.View)
				if err == nil {
					place := &BotPlace{
						Title:    title,
						Distance: dist,
						HereURL:  short.URL,
					}
					if resp.Extended.OpeningHours.IsOpen {
						place.OpeningHours = "Open now"
					} else {
						place.OpeningHours = resp.Extended.OpeningHours.Text
					}
					if len(resp.Contacts.Website) > 0 {
						place.URL = resp.Contacts.Website[0].Value
					}
					if resp.Icon != "" {
						place.IconURL = resp.Icon
					}
					ch <- place
				}
			}
			wg.Done()
		}(places.Results.Items[i].Title, places.Results.Items[i].Href, places.Results.Items[i].Distance)
		count++
		if count == limitResult {
			break
		}
	}
	wg.Wait()
	close(ch)

	for i := range ch {
		res.Places = append(res.Places, i)
	}
	if api.Debug {
		bytes, _ := json.Marshal(res)
		log.Println("Search places result:\n", string(bytes))
	}
	return res, nil
}

// TextForResponse transforms result from HERE API to message
func TextForResponse(results *BotResult) string {
	if results.Places == nil || len(results.Places) == 0 {
		return NothingFound
	}
	var buf bytes.Buffer
	for _, v := range results.Places {
		buf.WriteString("*")
		buf.WriteString(v.Title)
		buf.WriteString("*")
		if v.URL != "" {
			buf.WriteString(" (")
			buf.WriteString(v.URL)
			buf.WriteString(")")
		}
		buf.WriteString("\n")
		buf.WriteString("Distance: ")
		buf.WriteString(strconv.Itoa(v.Distance))
		buf.WriteString(" m. ")
		buf.WriteString("\n")
		if v.OpeningHours != "" {
			buf.WriteString(strings.Replace(v.OpeningHours, "
", " ", -1))
			buf.WriteString("\n")
		}
		buf.WriteString(v.HereURL)
		buf.WriteString("\n\n")
	}
	return buf.String()
}
<|endoftext|>


<commit_before>package util

import (
	"errors"
	"os/exec"

	"github.com/golang-devops/release-co-pilot/logging"
)

func ExecCommand(logger logging.Logger, cmd *exec.Cmd) error {
	out, err := cmd.CombinedOutput()
	if err != nil {
		outStr := ""
		if out != nil {
			outStr = string(out)
		}
		logger.WithError(err).WithFields(map[string]interface{}{
			"output": outStr,
		}).Error("Command failed")
		return errors.New("Command failed")
	}
	return nil
}
<commit_msg>fix: when command fails now escape newline and carriage return chars<commit_after>package util

import (
	"errors"
	"os/exec"
	"strings"

	"github.com/golang-devops/release-co-pilot/logging"
)

func ExecCommand(logger logging.Logger, cmd *exec.Cmd) error {
	out, err := cmd.CombinedOutput()
	if err != nil {
		outStr := ""
		if out != nil {
			outStr = string(out)
		}
		logger.WithError(err).WithFields(map[string]interface{}{
			"output": strings.Replace(strings.Replace(outStr, "\n", "\\n", -1), "\r", "", -1),
		}).Error("Command failed")
		return errors.New("Command failed")
	}
	return nil
}
<|endoftext|>


<commit_before>// Copyright 2015 tsuru-client authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package main

import (
	"os"

	"github.com/tsuru/tsuru/cmd"
)

const (
	version = "0.15.2"
	header  = "Supported-Tsuru"
)

func buildManager(name string) *cmd.Manager {
	lookup := func(context *cmd.Context) error {
		command := plugin{}
		return command.Run(context, nil)
	}
	m := cmd.BuildBaseManager(name, version, header, lookup)
	m.Register(&appRun{})
	m.Register(&appInfo{})
	m.Register(&appCreate{})
	m.Register(&appRemove{})
	m.Register(&unitAdd{})
	m.Register(&unitRemove{})
	m.Register(appList{})
	m.Register(&appLog{})
	m.Register(&appGrant{})
	m.Register(&appRevoke{})
	m.Register(&appRestart{})
	m.Register(&appStart{})
	m.Register(&appStop{})
	m.Register(&cnameAdd{})
	m.Register(&cnameRemove{})
	m.Register(&envGet{})
	m.Register(&envSet{})
	m.Register(&envUnset{})
	m.Register(&keyAdd{})
	m.Register(&keyRemove{})
	m.Register(&keyList{})
	m.Register(serviceList{})
	m.Register(&serviceAdd{})
	m.Register(&serviceRemove{})
	m.Register(serviceDoc{})
	m.Register(serviceInfo{})
	m.Register(serviceInstanceStatus{})
	m.Register(&serviceBind{})
	m.Register(&serviceUnbind{})
	m.Register(platformList{})
	m.Register(&pluginInstall{})
	m.Register(&pluginRemove{})
	m.Register(&pluginList{})
	m.Register(&appSwap{})
	m.Register(&appDeploy{})
	m.Register(&planList{})
	m.Register(&SetTeamOwner{})
	m.Register(&autoScaleEnable{})
	m.Register(&autoScaleDisable{})
	m.Register(&autoScaleConfig{})
	m.Register(&userCreate{})
	m.Register(&resetPassword{})
	m.Register(&userRemove{})
	m.Register(&teamCreate{})
	m.Register(&teamRemove{})
	m.Register(&teamList{})
	m.Register(&teamUserAdd{})
	m.Register(&teamUserRemove{})
	m.Register(teamUserList{})
	m.Register(&changePassword{})
	m.Register(&showAPIToken{})
	m.Register(&regenerateAPIToken{})
	m.Register(&appDeployList{})
	m.Register(&appDeployRollback{})
	m.Register(&cmd.ShellToContainerCmd{})
	m.Register(&poolList{})
	return m
}

func main() {
	name := cmd.ExtractProgramName(os.Args[0])
	manager := buildManager(name)
	manager.Run(os.Args[1:])
}
<commit_msg>tsuru: bump to 0.15.3<commit_after>// Copyright 2015 tsuru-client authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package main

import (
	"os"

	"github.com/tsuru/tsuru/cmd"
)

const (
	version = "0.15.3"
	header  = "Supported-Tsuru"
)

func buildManager(name string) *cmd.Manager {
	lookup := func(context *cmd.Context) error {
		command := plugin{}
		return command.Run(context, nil)
	}
	m := cmd.BuildBaseManager(name, version, header, lookup)
	m.Register(&appRun{})
	m.Register(&appInfo{})
	m.Register(&appCreate{})
	m.Register(&appRemove{})
	m.Register(&unitAdd{})
	m.Register(&unitRemove{})
	m.Register(appList{})
	m.Register(&appLog{})
	m.Register(&appGrant{})
	m.Register(&appRevoke{})
	m.Register(&appRestart{})
	m.Register(&appStart{})
	m.Register(&appStop{})
	m.Register(&cnameAdd{})
	m.Register(&cnameRemove{})
	m.Register(&envGet{})
	m.Register(&envSet{})
	m.Register(&envUnset{})
	m.Register(&keyAdd{})
	m.Register(&keyRemove{})
	m.Register(&keyList{})
	m.Register(serviceList{})
	m.Register(&serviceAdd{})
	m.Register(&serviceRemove{})
	m.Register(serviceDoc{})
	m.Register(serviceInfo{})
	m.Register(serviceInstanceStatus{})
	m.Register(&serviceBind{})
	m.Register(&serviceUnbind{})
	m.Register(platformList{})
	m.Register(&pluginInstall{})
	m.Register(&pluginRemove{})
	m.Register(&pluginList{})
	m.Register(&appSwap{})
	m.Register(&appDeploy{})
	m.Register(&planList{})
	m.Register(&SetTeamOwner{})
	m.Register(&autoScaleEnable{})
	m.Register(&autoScaleDisable{})
	m.Register(&autoScaleConfig{})
	m.Register(&userCreate{})
	m.Register(&resetPassword{})
	m.Register(&userRemove{})
	m.Register(&teamCreate{})
	m.Register(&teamRemove{})
	m.Register(&teamList{})
	m.Register(&teamUserAdd{})
	m.Register(&teamUserRemove{})
	m.Register(teamUserList{})
	m.Register(&changePassword{})
	m.Register(&showAPIToken{})
	m.Register(&regenerateAPIToken{})
	m.Register(&appDeployList{})
	m.Register(&appDeployRollback{})
	m.Register(&cmd.ShellToContainerCmd{})
	m.Register(&poolList{})
	return m
}

func main() {
	name := cmd.ExtractProgramName(os.Args[0])
	manager := buildManager(name)
	manager.Run(os.Args[1:])
}
<|endoftext|>


<commit_before>package locket_test

import (
	"net/http"
	"net/http/httptest"
	"net/http/httputil"
	"net/url"
	"strings"
	"time"

	"code.cloudfoundry.org/cfhttp"
	"code.cloudfoundry.org/consuladapter"
	"code.cloudfoundry.org/locket"
	"github.com/hashicorp/consul/api"

	"code.cloudfoundry.org/clock/fakeclock"
	"code.cloudfoundry.org/lager"
	"code.cloudfoundry.org/lager/lagertest"
	"github.com/tedsuo/ifrit"
	"github.com/tedsuo/ifrit/ginkgomon"

	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
	. "github.com/onsi/gomega/gbytes"
)

var _ = Describe("Presence", func() {
	var (
		presenceKey   string
		presenceValue []byte

		consulClient consuladapter.Client

		presenceRunner             ifrit.Runner
		presenceProcess            ifrit.Process
		retryInterval, presenceTTL time.Duration
		logger                     lager.Logger
		clock                      *fakeclock.FakeClock
	)

	getPresenceValue := func() ([]byte, error) {
		kvPair, _, err := consulClient.KV().Get(presenceKey, nil)
		if err != nil {
			return nil, err
		}

		if kvPair == nil || kvPair.Session == "" {
			return nil, consuladapter.NewKeyNotFoundError(presenceKey)
		}

		return kvPair.Value, nil
	}

	BeforeEach(func() {
		consulClient = consulRunner.NewClient()

		presenceKey = "some-key"
		presenceValue = []byte("some-value")

		retryInterval = 500 * time.Millisecond
		logger = lagertest.NewTestLogger("locket")

		presenceTTL = 5 * time.Second
	})

	JustBeforeEach(func() {
		clock = fakeclock.NewFakeClock(time.Now())
		presenceRunner = locket.NewPresence(logger, consulClient, presenceKey, presenceValue, clock, retryInterval, presenceTTL)
	})

	AfterEach(func() {
		ginkgomon.Kill(presenceProcess)
	})

	Context("When consul is running", func() {
		Context("an error occurs while acquiring the presence", func() {
			BeforeEach(func() {
				presenceKey = ""
			})

			It("continues to retry", func() {
				presenceProcess = ifrit.Background(presenceRunner)

				Consistently(presenceProcess.Ready()).ShouldNot(BeClosed())
				Consistently(presenceProcess.Wait()).ShouldNot(Receive())

				Eventually(logger).Should(Say("failed-setting-presence"))
				clock.WaitForWatcherAndIncrement(6 * time.Second)
				Eventually(logger).Should(Say("recreating-session"))
			})
		})

		Context("and the presence is available", func() {
			It("acquires the presence", func() {
				presenceProcess = ifrit.Background(presenceRunner)
				Eventually(presenceProcess.Ready()).Should(BeClosed())
				Eventually(getPresenceValue).Should(Equal(presenceValue))
			})

			Context("and we have acquired the presence", func() {
				JustBeforeEach(func() {
					presenceProcess = ifrit.Background(presenceRunner)
					Eventually(presenceProcess.Ready()).Should(BeClosed())
				})

				Context("when consul shuts down", func() {
					JustBeforeEach(func() {
						consulRunner.Stop()
					})

					AfterEach(func() {
						consulRunner.Start()
						consulRunner.WaitUntilReady()
					})

					It("loses the presence and retries", func() {
						Eventually(presenceProcess.Wait()).ShouldNot(Receive())
						clock.WaitForWatcherAndIncrement(6 * time.Second)
						Eventually(logger).Should(Say("recreating-session"))
					})
				})

				Context("when consul goes down and comes back up", func() {
					JustBeforeEach(func() {
						consulRunner.Stop()
					})

					It("reacquires presence", func() {
						Eventually(presenceProcess.Wait()).ShouldNot(Receive())
						clock.WaitForWatcherAndIncrement(6 * time.Second)
						Eventually(logger).Should(Say("recreating-session"))

						consulRunner.Start()
						consulRunner.WaitUntilReady()

						clock.WaitForWatcherAndIncrement(6 * time.Second)
						Eventually(logger).Should(Say("succeeded-recreating-session"))
						Eventually(presenceProcess.Ready()).Should(BeClosed())
					})
				})

				Context("and the process is shutting down", func() {
					It("releases the presence and exits", func() {
						ginkgomon.Interrupt(presenceProcess)
						Eventually(presenceProcess.Wait()).Should(Receive(BeNil()))
						_, err := getPresenceValue()
						Expect(err).To(Equal(consuladapter.NewKeyNotFoundError(presenceKey)))
					})
				})

				Context("and consul goes through a period of instability", func() {
					var serveFiveHundreds chan struct{}
					var fakeConsul *httptest.Server

					BeforeEach(func() {
						serveFiveHundreds = make(chan struct{}, 8)

						consulClusterURL, err := url.Parse(consulRunner.URL())
						Expect(err).NotTo(HaveOccurred())
						proxy := httputil.NewSingleHostReverseProxy(consulClusterURL)
						fakeConsul = httptest.NewServer(
							http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
								// We only want to return 500's on the lock monitor query
								if !strings.Contains(r.URL.Path, "/v1/kv") {
									By("forwarding request to " + r.URL.Path)
									proxy.ServeHTTP(w, r)
									return
								}

								select {
								case <-serveFiveHundreds:
									By("returning 500 to " + r.URL.Path)
									w.WriteHeader(http.StatusInternalServerError)
								default:
									By("returning 500 to " + r.URL.Path)
									proxy.ServeHTTP(w, r)
								}
							}),
						)

						fakeConsulURL, err := url.Parse(fakeConsul.URL)
						Expect(err).NotTo(HaveOccurred())

						client, err := api.NewClient(&api.Config{
							Address:    fakeConsulURL.Host,
							Scheme:     fakeConsulURL.Scheme,
							HttpClient: cfhttp.NewStreamingClient(),
						})
						Expect(err).NotTo(HaveOccurred())

						consulClient = consuladapter.NewConsulClient(client)
						presenceTTL = 6 * time.Second
					})

					Context("for longer than the MonitorRetries * MonitorRetryTime", func() {
						It("drops its presence", func() {
							// Serve 500's to simulate a leader election. We know that we need
							// to serve more than lockTTL / 2 500's to lose the lock.
							for i := 0; i < 4; i++ {
								Eventually(serveFiveHundreds).Should(BeSent(struct{}{}))
							}

							By("closing all connections")

							// Close the existing connection with consul so that the
							// lock monitor is forced to retry. This is because consul
							// performs a blocking query until the lock index is changed.
							fakeConsul.CloseClientConnections()

							Eventually(logger, 7*time.Second).Should(Say("presence-lost"))
							Consistently(presenceProcess.Wait()).ShouldNot(Receive())
						})
					})

					Context("for less than the MonitorRetries * MonitorRetryTime", func() {
						It("does not lose the lock", func() {
							// Serve 500's to simulate a leader election. We know that if we
							// serve less than lockTTL / 2 500's, we will not lose the lock.
							for i := 0; i < 2; i++ {
								Eventually(serveFiveHundreds).Should(BeSent(struct{}{}))
							}
							// Close the existing connection with consul so that the
							// lock monitor is forced to retry. This is because consul
							// performs a blocking query until the lock index is changed.
							fakeConsul.CloseClientConnections()

							Consistently(logger, 7*time.Second).ShouldNot(Say("presence-lost"))
							Consistently(presenceProcess.Wait()).ShouldNot(Receive())
						})
					})
				})
			})
		})

		Context("and the presence is unavailable", func() {
			var (
				otherSession *locket.Session
				otherValue   []byte
			)

			BeforeEach(func() {
				otherValue = []byte("doppel-value")
				var err error
				otherSession, err = locket.NewSession("other-session", 10*time.Second, consulClient)
				Expect(err).NotTo(HaveOccurred())

				_, err = otherSession.SetPresence(presenceKey, otherValue)
				Expect(err).NotTo(HaveOccurred())
				Expect(getPresenceValue()).To(Equal(otherValue))
			})

			AfterEach(func() {
				otherSession.Destroy()
			})

			It("waits for the presence to become available", func() {
				presenceProcess = ifrit.Background(presenceRunner)
				Consistently(presenceProcess.Ready()).ShouldNot(BeClosed())
				Expect(getPresenceValue()).To(Equal(otherValue))
			})

			Context("when consul shuts down", func() {
				JustBeforeEach(func() {
					presenceProcess = ifrit.Background(presenceRunner)
					Consistently(presenceProcess.Ready()).ShouldNot(BeClosed())

					consulRunner.Stop()
				})

				AfterEach(func() {
					consulRunner.Start()
					consulRunner.WaitUntilReady()
				})

				It("continues to wait for the presence", func() {
					Consistently(presenceProcess.Ready()).ShouldNot(BeClosed())
					Consistently(presenceProcess.Wait()).ShouldNot(Receive())

					Eventually(logger).Should(Say("failed-setting-presence"))
					clock.WaitForWatcherAndIncrement(6 * time.Second)
					Eventually(logger).Should(Say("recreating-session"))
				})
			})

			Context("and the session is destroyed", func() {
				It("should recreate the session and continue to retry", func() {
					var err error
					presenceProcess = ifrit.Background(presenceRunner)
					Consistently(presenceProcess.Ready()).ShouldNot(BeClosed())

					var sessions []*api.SessionEntry
					Eventually(func() int {
						sessions, _, err = consulClient.Session().List(nil)
						Expect(err).NotTo(HaveOccurred())
						return len(sessions)
					}).Should(Equal(2))

					sessions, _, err = consulClient.Session().List(nil)
					Expect(err).NotTo(HaveOccurred())
					var mostRecentSession *api.SessionEntry
					for _, session := range sessions {
						if mostRecentSession == nil {
							mostRecentSession = session
						} else if session.CreateIndex > mostRecentSession.CreateIndex {
							mostRecentSession = session
						}
					}

					_, err = consulClient.Session().Destroy(mostRecentSession.ID, nil)
					Expect(err).NotTo(HaveOccurred())

					Eventually(logger, 6*time.Second).Should(Say("consul-error"))

					clock.WaitForWatcherAndIncrement(6 * time.Second)
					Eventually(logger).Should(Say("recreating-session"))

					Eventually(func() int {
						sessions, _, err = consulClient.Session().List(nil)
						Expect(err).NotTo(HaveOccurred())
						return len(sessions)
					}).Should(Equal(2))
				})
			})

			Context("and the process is shutting down", func() {
				It("exits", func() {
					presenceProcess = ifrit.Background(presenceRunner)
					Consistently(presenceProcess.Ready()).ShouldNot(BeClosed())

					ginkgomon.Interrupt(presenceProcess)
					Eventually(presenceProcess.Wait()).Should(Receive(BeNil()))
				})
			})

			Context("and the presence is released", func() {
				It("acquires the presence", func() {
					presenceProcess = ifrit.Background(presenceRunner)
					Consistently(presenceProcess.Ready()).ShouldNot(BeClosed())
					Expect(getPresenceValue()).To(Equal(otherValue))

					otherSession.Destroy()

					Eventually(presenceProcess.Ready(), 6*time.Second).Should(BeClosed())
					Expect(getPresenceValue()).To(Equal(presenceValue))
				})
			})
		})
	})

	Context("When consul is down", func() {
		BeforeEach(func() {
			consulRunner.Stop()
		})

		AfterEach(func() {
			consulRunner.Start()
			consulRunner.WaitUntilReady()
		})

		It("continues to retry creating the session", func() {
			presenceProcess = ifrit.Background(presenceRunner)

			Consistently(presenceProcess.Ready()).ShouldNot(BeClosed())
			Consistently(presenceProcess.Wait()).ShouldNot(Receive())

			Eventually(logger).Should(Say("failed-setting-presence"))
			clock.WaitForWatcherAndIncrement(6 * time.Second)
			Eventually(logger).Should(Say("recreating-session"))
			clock.WaitForWatcherAndIncrement(6 * time.Second)
			Eventually(logger).Should(Say("recreating-session"))
		})

		Context("when consul starts up", func() {
			It("acquires the presence", func() {
				presenceProcess = ifrit.Background(presenceRunner)
				Consistently(presenceProcess.Ready()).ShouldNot(BeClosed())

				Eventually(logger).Should(Say("failed-setting-presence"))
				clock.WaitForWatcherAndIncrement(6 * time.Second)
				Eventually(logger).Should(Say("recreating-session"))
				Consistently(presenceProcess.Wait()).ShouldNot(Receive())

				consulRunner.Start()
				consulRunner.WaitUntilReady()

				clock.WaitForWatcherAndIncrement(6 * time.Second)

				Eventually(presenceProcess.Ready()).Should(BeClosed())
				Eventually(getPresenceValue).Should(Equal(presenceValue))
			})
		})
	})
})
<commit_msg>Add timestamps and full url path to the logs<commit_after>package locket_test

import (
	"net/http"
	"net/http/httptest"
	"net/http/httputil"
	"net/url"
	"strings"
	"time"

	"code.cloudfoundry.org/cfhttp"
	"code.cloudfoundry.org/consuladapter"
	"code.cloudfoundry.org/locket"
	"github.com/hashicorp/consul/api"

	"code.cloudfoundry.org/clock/fakeclock"
	"code.cloudfoundry.org/lager"
	"code.cloudfoundry.org/lager/lagertest"
	"github.com/tedsuo/ifrit"
	"github.com/tedsuo/ifrit/ginkgomon"

	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
	. "github.com/onsi/gomega/gbytes"
)

var _ = Describe("Presence", func() {
	var (
		presenceKey   string
		presenceValue []byte

		consulClient consuladapter.Client

		presenceRunner             ifrit.Runner
		presenceProcess            ifrit.Process
		retryInterval, presenceTTL time.Duration
		logger                     lager.Logger
		clock                      *fakeclock.FakeClock
	)

	getPresenceValue := func() ([]byte, error) {
		kvPair, _, err := consulClient.KV().Get(presenceKey, nil)
		if err != nil {
			return nil, err
		}

		if kvPair == nil || kvPair.Session == "" {
			return nil, consuladapter.NewKeyNotFoundError(presenceKey)
		}

		return kvPair.Value, nil
	}

	BeforeEach(func() {
		consulClient = consulRunner.NewClient()

		presenceKey = "some-key"
		presenceValue = []byte("some-value")

		retryInterval = 500 * time.Millisecond
		logger = lagertest.NewTestLogger("locket")

		presenceTTL = 5 * time.Second
	})

	JustBeforeEach(func() {
		clock = fakeclock.NewFakeClock(time.Now())
		presenceRunner = locket.NewPresence(logger, consulClient, presenceKey, presenceValue, clock, retryInterval, presenceTTL)
	})

	AfterEach(func() {
		ginkgomon.Kill(presenceProcess)
	})

	Context("When consul is running", func() {
		Context("an error occurs while acquiring the presence", func() {
			BeforeEach(func() {
				presenceKey = ""
			})

			It("continues to retry", func() {
				presenceProcess = ifrit.Background(presenceRunner)

				Consistently(presenceProcess.Ready()).ShouldNot(BeClosed())
				Consistently(presenceProcess.Wait()).ShouldNot(Receive())

				Eventually(logger).Should(Say("failed-setting-presence"))
				clock.WaitForWatcherAndIncrement(6 * time.Second)
				Eventually(logger).Should(Say("recreating-session"))
			})
		})

		Context("and the presence is available", func() {
			It("acquires the presence", func() {
				presenceProcess = ifrit.Background(presenceRunner)
				Eventually(presenceProcess.Ready()).Should(BeClosed())
				Eventually(getPresenceValue).Should(Equal(presenceValue))
			})

			Context("and we have acquired the presence", func() {
				JustBeforeEach(func() {
					presenceProcess = ifrit.Background(presenceRunner)
					Eventually(presenceProcess.Ready()).Should(BeClosed())
				})

				Context("when consul shuts down", func() {
					JustBeforeEach(func() {
						consulRunner.Stop()
					})

					AfterEach(func() {
						consulRunner.Start()
						consulRunner.WaitUntilReady()
					})

					It("loses the presence and retries", func() {
						Eventually(presenceProcess.Wait()).ShouldNot(Receive())
						clock.WaitForWatcherAndIncrement(6 * time.Second)
						Eventually(logger).Should(Say("recreating-session"))
					})
				})

				Context("when consul goes down and comes back up", func() {
					JustBeforeEach(func() {
						consulRunner.Stop()
					})

					It("reacquires presence", func() {
						Eventually(presenceProcess.Wait()).ShouldNot(Receive())
						clock.WaitForWatcherAndIncrement(6 * time.Second)
						Eventually(logger).Should(Say("recreating-session"))

						consulRunner.Start()
						consulRunner.WaitUntilReady()

						clock.WaitForWatcherAndIncrement(6 * time.Second)
						Eventually(logger).Should(Say("succeeded-recreating-session"))
						Eventually(presenceProcess.Ready()).Should(BeClosed())
					})
				})

				Context("and the process is shutting down", func() {
					It("releases the presence and exits", func() {
						ginkgomon.Interrupt(presenceProcess)
						Eventually(presenceProcess.Wait()).Should(Receive(BeNil()))
						_, err := getPresenceValue()
						Expect(err).To(Equal(consuladapter.NewKeyNotFoundError(presenceKey)))
					})
				})

				Context("and consul goes through a period of instability", func() {
					var serveFiveHundreds chan struct{}
					var fakeConsul *httptest.Server

					BeforeEach(func() {
						serveFiveHundreds = make(chan struct{}, 8)

						consulClusterURL, err := url.Parse(consulRunner.URL())
						Expect(err).NotTo(HaveOccurred())
						proxy := httputil.NewSingleHostReverseProxy(consulClusterURL)
						fakeConsul = httptest.NewServer(
							http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
								// We only want to return 500's on the lock monitor query
								if !strings.Contains(r.URL.Path, "/v1/kv") {
									By(time.Now().String() + ": forwarding request to " + r.URL.String())
									proxy.ServeHTTP(w, r)
									return
								}

								select {
								case <-serveFiveHundreds:
									By(time.Now().String() + ": returning 500 to " + r.URL.String())
									w.WriteHeader(http.StatusInternalServerError)
								default:
									By(time.Now().String() + ": forwarding request to " + r.URL.String())
									proxy.ServeHTTP(w, r)
								}
							}),
						)

						fakeConsulURL, err := url.Parse(fakeConsul.URL)
						Expect(err).NotTo(HaveOccurred())

						client, err := api.NewClient(&api.Config{
							Address:    fakeConsulURL.Host,
							Scheme:     fakeConsulURL.Scheme,
							HttpClient: cfhttp.NewStreamingClient(),
						})
						Expect(err).NotTo(HaveOccurred())

						consulClient = consuladapter.NewConsulClient(client)
						presenceTTL = 6 * time.Second
					})

					Context("for longer than the MonitorRetries * MonitorRetryTime", func() {
						It("drops its presence", func() {
							// Serve 500's to simulate a leader election. We know that we need
							// to serve more than lockTTL / 2 500's to lose the lock.
							for i := 0; i < 4; i++ {
								Eventually(serveFiveHundreds).Should(BeSent(struct{}{}))
							}

							By("closing all connections")

							// Close the existing connection with consul so that the
							// lock monitor is forced to retry. This is because consul
							// performs a blocking query until the lock index is changed.
							fakeConsul.CloseClientConnections()

							Eventually(logger, 7*time.Second).Should(Say("presence-lost"))
							Consistently(presenceProcess.Wait()).ShouldNot(Receive())
						})
					})

					Context("for less than the MonitorRetries * MonitorRetryTime", func() {
						It("does not lose the lock", func() {
							// Serve 500's to simulate a leader election. We know that if we
							// serve less than lockTTL / 2 500's, we will not lose the lock.
							for i := 0; i < 2; i++ {
								Eventually(serveFiveHundreds).Should(BeSent(struct{}{}))
							}
							// Close the existing connection with consul so that the
							// lock monitor is forced to retry. This is because consul
							// performs a blocking query until the lock index is changed.
							fakeConsul.CloseClientConnections()

							Consistently(logger, 7*time.Second).ShouldNot(Say("presence-lost"))
							Consistently(presenceProcess.Wait()).ShouldNot(Receive())
						})
					})
				})
			})
		})

		Context("and the presence is unavailable", func() {
			var (
				otherSession *locket.Session
				otherValue   []byte
			)

			BeforeEach(func() {
				otherValue = []byte("doppel-value")
				var err error
				otherSession, err = locket.NewSession("other-session", 10*time.Second, consulClient)
				Expect(err).NotTo(HaveOccurred())

				_, err = otherSession.SetPresence(presenceKey, otherValue)
				Expect(err).NotTo(HaveOccurred())
				Expect(getPresenceValue()).To(Equal(otherValue))
			})

			AfterEach(func() {
				otherSession.Destroy()
			})

			It("waits for the presence to become available", func() {
				presenceProcess = ifrit.Background(presenceRunner)
				Consistently(presenceProcess.Ready()).ShouldNot(BeClosed())
				Expect(getPresenceValue()).To(Equal(otherValue))
			})

			Context("when consul shuts down", func() {
				JustBeforeEach(func() {
					presenceProcess = ifrit.Background(presenceRunner)
					Consistently(presenceProcess.Ready()).ShouldNot(BeClosed())

					consulRunner.Stop()
				})

				AfterEach(func() {
					consulRunner.Start()
					consulRunner.WaitUntilReady()
				})

				It("continues to wait for the presence", func() {
					Consistently(presenceProcess.Ready()).ShouldNot(BeClosed())
					Consistently(presenceProcess.Wait()).ShouldNot(Receive())

					Eventually(logger).Should(Say("failed-setting-presence"))
					clock.WaitForWatcherAndIncrement(6 * time.Second)
					Eventually(logger).Should(Say("recreating-session"))
				})
			})

			Context("and the session is destroyed", func() {
				It("should recreate the session and continue to retry", func() {
					var err error
					presenceProcess = ifrit.Background(presenceRunner)
					Consistently(presenceProcess.Ready()).ShouldNot(BeClosed())

					var sessions []*api.SessionEntry
					Eventually(func() int {
						sessions, _, err = consulClient.Session().List(nil)
						Expect(err).NotTo(HaveOccurred())
						return len(sessions)
					}).Should(Equal(2))

					sessions, _, err = consulClient.Session().List(nil)
					Expect(err).NotTo(HaveOccurred())
					var mostRecentSession *api.SessionEntry
					for _, session := range sessions {
						if mostRecentSession == nil {
							mostRecentSession = session
						} else if session.CreateIndex > mostRecentSession.CreateIndex {
							mostRecentSession = session
						}
					}

					_, err = consulClient.Session().Destroy(mostRecentSession.ID, nil)
					Expect(err).NotTo(HaveOccurred())

					Eventually(logger, 6*time.Second).Should(Say("consul-error"))

					clock.WaitForWatcherAndIncrement(6 * time.Second)
					Eventually(logger).Should(Say("recreating-session"))

					Eventually(func() int {
						sessions, _, err = consulClient.Session().List(nil)
						Expect(err).NotTo(HaveOccurred())
						return len(sessions)
					}).Should(Equal(2))
				})
			})

			Context("and the process is shutting down", func() {
				It("exits", func() {
					presenceProcess = ifrit.Background(presenceRunner)
					Consistently(presenceProcess.Ready()).ShouldNot(BeClosed())

					ginkgomon.Interrupt(presenceProcess)
					Eventually(presenceProcess.Wait()).Should(Receive(BeNil()))
				})
			})

			Context("and the presence is released", func() {
				It("acquires the presence", func() {
					presenceProcess = ifrit.Background(presenceRunner)
					Consistently(presenceProcess.Ready()).ShouldNot(BeClosed())
					Expect(getPresenceValue()).To(Equal(otherValue))

					otherSession.Destroy()

					Eventually(presenceProcess.Ready(), 6*time.Second).Should(BeClosed())
					Expect(getPresenceValue()).To(Equal(presenceValue))
				})
			})
		})
	})

	Context("When consul is down", func() {
		BeforeEach(func() {
			consulRunner.Stop()
		})

		AfterEach(func() {
			consulRunner.Start()
			consulRunner.WaitUntilReady()
		})

		It("continues to retry creating the session", func() {
			presenceProcess = ifrit.Background(presenceRunner)

			Consistently(presenceProcess.Ready()).ShouldNot(BeClosed())
			Consistently(presenceProcess.Wait()).ShouldNot(Receive())

			Eventually(logger).Should(Say("failed-setting-presence"))
			clock.WaitForWatcherAndIncrement(6 * time.Second)
			Eventually(logger).Should(Say("recreating-session"))
			clock.WaitForWatcherAndIncrement(6 * time.Second)
			Eventually(logger).Should(Say("recreating-session"))
		})

		Context("when consul starts up", func() {
			It("acquires the presence", func() {
				presenceProcess = ifrit.Background(presenceRunner)
				Consistently(presenceProcess.Ready()).ShouldNot(BeClosed())

				Eventually(logger).Should(Say("failed-setting-presence"))
				clock.WaitForWatcherAndIncrement(6 * time.Second)
				Eventually(logger).Should(Say("recreating-session"))
				Consistently(presenceProcess.Wait()).ShouldNot(Receive())

				consulRunner.Start()
				consulRunner.WaitUntilReady()

				clock.WaitForWatcherAndIncrement(6 * time.Second)

				Eventually(presenceProcess.Ready()).Should(BeClosed())
				Eventually(getPresenceValue).Should(Equal(presenceValue))
			})
		})
	})
})
<|endoftext|>