<commit_before>// Copyright 2017 The Prometheus Authors
// 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 tsdb

import (
	"encoding/binary"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"path/filepath"
	"sync"

	"github.com/pkg/errors"
	"github.com/prometheus/tsdb/encoding"
)

const tombstoneFilename = "tombstones"

const (
	// MagicTombstone is 4 bytes at the head of a tombstone file.
	MagicTombstone = 0x0130BA30

	tombstoneFormatV1 = 1
)

// TombstoneReader gives access to tombstone intervals by series reference.
type TombstoneReader interface {
	// Get returns deletion intervals for the series with the given reference.
	Get(ref uint64) (Intervals, error)

	// Iter calls the given function for each encountered interval.
	Iter(func(uint64, Intervals) error) error

	// Total returns the total count of tombstones.
	Total() uint64

	// Close any underlying resources
	Close() error
}

func writeTombstoneFile(dir string, tr TombstoneReader) error {
	path := filepath.Join(dir, tombstoneFilename)
	tmp := path + ".tmp"
	hash := newCRC32()

	f, err := os.Create(tmp)
	if err != nil {
		return err
	}
	defer func() {
		if f != nil {
			f.Close()
		}
	}()

	buf := encoding.Encbuf{B: make([]byte, 3*binary.MaxVarintLen64)}
	buf.Reset()
	// Write the meta.
	buf.PutBE32(MagicTombstone)
	buf.PutByte(tombstoneFormatV1)
	_, err = f.Write(buf.Get())
	if err != nil {
		return err
	}

	mw := io.MultiWriter(f, hash)

	if err := tr.Iter(func(ref uint64, ivs Intervals) error {
		for _, iv := range ivs {
			buf.Reset()

			buf.PutUvarint64(ref)
			buf.PutVarint64(iv.Mint)
			buf.PutVarint64(iv.Maxt)

			_, err = mw.Write(buf.Get())
			if err != nil {
				return err
			}
		}
		return nil
	}); err != nil {
		return fmt.Errorf("error writing tombstones: %v", err)
	}

	_, err = f.Write(hash.Sum(nil))
	if err != nil {
		return err
	}

	if err = f.Close(); err != nil {
		return err
	}
	f = nil
	return renameFile(tmp, path)
}

// Stone holds the information on the posting and time-range
// that is deleted.
type Stone struct {
	ref       uint64
	intervals Intervals
}

func readTombstones(dir string) (TombstoneReader, SizeReader, error) {
	b, err := ioutil.ReadFile(filepath.Join(dir, tombstoneFilename))
	if os.IsNotExist(err) {
		return newMemTombstones(), nil, nil
	} else if err != nil {
		return nil, nil, err
	}

	sr := &TombstoneFile{
		size: int64(len(b)),
	}

	if len(b) < 5 {
		return nil, sr, errors.Wrap(encoding.ErrInvalidSize, "tombstones header")
	}

	d := &encoding.Decbuf{B: b[:len(b)-4]} // 4 for the checksum.
	if mg := d.Be32(); mg != MagicTombstone {
		return nil, sr, fmt.Errorf("invalid magic number %x", mg)
	}
	if flag := d.Byte(); flag != tombstoneFormatV1 {
		return nil, sr, fmt.Errorf("invalid tombstone format %x", flag)
	}

	if d.Err() != nil {
		return nil, sr, d.Err()
	}

	// Verify checksum.
	hash := newCRC32()
	if _, err := hash.Write(d.Get()); err != nil {
		return nil, sr, errors.Wrap(err, "write to hash")
	}
	if binary.BigEndian.Uint32(b[len(b)-4:]) != hash.Sum32() {
		return nil, sr, errors.New("checksum did not match")
	}

	stonesMap := newMemTombstones()

	for d.Len() > 0 {
		k := d.Uvarint64()
		mint := d.Varint64()
		maxt := d.Varint64()
		if d.Err() != nil {
			return nil, sr, d.Err()
		}

		stonesMap.addInterval(k, Interval{mint, maxt})
	}

	return stonesMap, sr, nil
}

type memTombstones struct {
	intvlGroups map[uint64]Intervals
	mtx         sync.RWMutex
}

// newMemTombstones creates new in memory TombstoneReader
// that allows adding new intervals.
func newMemTombstones() *memTombstones {
	return &memTombstones{intvlGroups: make(map[uint64]Intervals)}
}

func (t *memTombstones) Get(ref uint64) (Intervals, error) {
	t.mtx.RLock()
	defer t.mtx.RUnlock()
	return t.intvlGroups[ref], nil
}

func (t *memTombstones) Iter(f func(uint64, Intervals) error) error {
	t.mtx.RLock()
	defer t.mtx.RUnlock()
	for ref, ivs := range t.intvlGroups {
		if err := f(ref, ivs); err != nil {
			return err
		}
	}
	return nil
}

func (t *memTombstones) Total() uint64 {
	t.mtx.RLock()
	defer t.mtx.RUnlock()

	total := uint64(0)
	for _, ivs := range t.intvlGroups {
		total += uint64(len(ivs))
	}
	return total
}

// addInterval to an existing memTombstones
func (t *memTombstones) addInterval(ref uint64, itvs ...Interval) {
	t.mtx.Lock()
	defer t.mtx.Unlock()
	for _, itv := range itvs {
		t.intvlGroups[ref] = t.intvlGroups[ref].add(itv)
	}
}

// TombstoneFile holds information about the tombstone file.
type TombstoneFile struct {
	size int64
}

// Size returns the tombstone file size.
func (t *TombstoneFile) Size() int64 {
	return t.size
}

func (*memTombstones) Close() error {
	return nil
}

// Interval represents a single time-interval.
type Interval struct {
	Mint, Maxt int64
}

func (tr Interval) inBounds(t int64) bool {
	return t >= tr.Mint && t <= tr.Maxt
}

func (tr Interval) isSubrange(dranges Intervals) bool {
	for _, r := range dranges {
		if r.inBounds(tr.Mint) && r.inBounds(tr.Maxt) {
			return true
		}
	}

	return false
}

// Intervals represents	a set of increasing and non-overlapping time-intervals.
type Intervals []Interval

// add the new time-range to the existing ones.
// The existing ones must be sorted.
func (itvs Intervals) add(n Interval) Intervals {
	for i, r := range itvs {
		// TODO(gouthamve): Make this codepath easier to digest.
		if r.inBounds(n.Mint-1) || r.inBounds(n.Mint) {
			if n.Maxt > r.Maxt {
				itvs[i].Maxt = n.Maxt
			}

			j := 0
			for _, r2 := range itvs[i+1:] {
				if n.Maxt < r2.Mint {
					break
				}
				j++
			}
			if j != 0 {
				if itvs[i+j].Maxt > n.Maxt {
					itvs[i].Maxt = itvs[i+j].Maxt
				}
				itvs = append(itvs[:i+1], itvs[i+j+1:]...)
			}
			return itvs
		}

		if r.inBounds(n.Maxt+1) || r.inBounds(n.Maxt) {
			if n.Mint < r.Maxt {
				itvs[i].Mint = n.Mint
			}
			return itvs
		}

		if n.Mint < r.Mint {
			newRange := make(Intervals, i, len(itvs[:i])+1)
			copy(newRange, itvs[:i])
			newRange = append(newRange, n)
			newRange = append(newRange, itvs[i:]...)

			return newRange
		}
	}

	itvs = append(itvs, n)
	return itvs
}
<commit_msg>force persisting the tombstone file (#578)<commit_after>// Copyright 2017 The Prometheus Authors
// 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 tsdb

import (
	"encoding/binary"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"path/filepath"
	"sync"

	"github.com/pkg/errors"
	"github.com/prometheus/tsdb/encoding"
	tsdb_errors "github.com/prometheus/tsdb/errors"
)

const tombstoneFilename = "tombstones"

const (
	// MagicTombstone is 4 bytes at the head of a tombstone file.
	MagicTombstone = 0x0130BA30

	tombstoneFormatV1 = 1
)

// TombstoneReader gives access to tombstone intervals by series reference.
type TombstoneReader interface {
	// Get returns deletion intervals for the series with the given reference.
	Get(ref uint64) (Intervals, error)

	// Iter calls the given function for each encountered interval.
	Iter(func(uint64, Intervals) error) error

	// Total returns the total count of tombstones.
	Total() uint64

	// Close any underlying resources
	Close() error
}

func writeTombstoneFile(dir string, tr TombstoneReader) error {
	path := filepath.Join(dir, tombstoneFilename)
	tmp := path + ".tmp"
	hash := newCRC32()

	f, err := os.Create(tmp)
	if err != nil {
		return err
	}
	defer func() {
		if f != nil {
			f.Close()
		}
	}()

	buf := encoding.Encbuf{B: make([]byte, 3*binary.MaxVarintLen64)}
	buf.Reset()
	// Write the meta.
	buf.PutBE32(MagicTombstone)
	buf.PutByte(tombstoneFormatV1)
	_, err = f.Write(buf.Get())
	if err != nil {
		return err
	}

	mw := io.MultiWriter(f, hash)

	if err := tr.Iter(func(ref uint64, ivs Intervals) error {
		for _, iv := range ivs {
			buf.Reset()

			buf.PutUvarint64(ref)
			buf.PutVarint64(iv.Mint)
			buf.PutVarint64(iv.Maxt)

			_, err = mw.Write(buf.Get())
			if err != nil {
				return err
			}
		}
		return nil
	}); err != nil {
		return fmt.Errorf("error writing tombstones: %v", err)
	}

	_, err = f.Write(hash.Sum(nil))
	if err != nil {
		return err
	}

	var merr tsdb_errors.MultiError
	if merr.Add(f.Sync()); merr.Err() != nil {
		merr.Add(f.Close())
		return merr.Err()
	}

	if err = f.Close(); err != nil {
		return err
	}
	f = nil
	return renameFile(tmp, path)
}

// Stone holds the information on the posting and time-range
// that is deleted.
type Stone struct {
	ref       uint64
	intervals Intervals
}

func readTombstones(dir string) (TombstoneReader, SizeReader, error) {
	b, err := ioutil.ReadFile(filepath.Join(dir, tombstoneFilename))
	if os.IsNotExist(err) {
		return newMemTombstones(), nil, nil
	} else if err != nil {
		return nil, nil, err
	}

	sr := &TombstoneFile{
		size: int64(len(b)),
	}

	if len(b) < 5 {
		return nil, sr, errors.Wrap(encoding.ErrInvalidSize, "tombstones header")
	}

	d := &encoding.Decbuf{B: b[:len(b)-4]} // 4 for the checksum.
	if mg := d.Be32(); mg != MagicTombstone {
		return nil, sr, fmt.Errorf("invalid magic number %x", mg)
	}
	if flag := d.Byte(); flag != tombstoneFormatV1 {
		return nil, sr, fmt.Errorf("invalid tombstone format %x", flag)
	}

	if d.Err() != nil {
		return nil, sr, d.Err()
	}

	// Verify checksum.
	hash := newCRC32()
	if _, err := hash.Write(d.Get()); err != nil {
		return nil, sr, errors.Wrap(err, "write to hash")
	}
	if binary.BigEndian.Uint32(b[len(b)-4:]) != hash.Sum32() {
		return nil, sr, errors.New("checksum did not match")
	}

	stonesMap := newMemTombstones()

	for d.Len() > 0 {
		k := d.Uvarint64()
		mint := d.Varint64()
		maxt := d.Varint64()
		if d.Err() != nil {
			return nil, sr, d.Err()
		}

		stonesMap.addInterval(k, Interval{mint, maxt})
	}

	return stonesMap, sr, nil
}

type memTombstones struct {
	intvlGroups map[uint64]Intervals
	mtx         sync.RWMutex
}

// newMemTombstones creates new in memory TombstoneReader
// that allows adding new intervals.
func newMemTombstones() *memTombstones {
	return &memTombstones{intvlGroups: make(map[uint64]Intervals)}
}

func (t *memTombstones) Get(ref uint64) (Intervals, error) {
	t.mtx.RLock()
	defer t.mtx.RUnlock()
	return t.intvlGroups[ref], nil
}

func (t *memTombstones) Iter(f func(uint64, Intervals) error) error {
	t.mtx.RLock()
	defer t.mtx.RUnlock()
	for ref, ivs := range t.intvlGroups {
		if err := f(ref, ivs); err != nil {
			return err
		}
	}
	return nil
}

func (t *memTombstones) Total() uint64 {
	t.mtx.RLock()
	defer t.mtx.RUnlock()

	total := uint64(0)
	for _, ivs := range t.intvlGroups {
		total += uint64(len(ivs))
	}
	return total
}

// addInterval to an existing memTombstones
func (t *memTombstones) addInterval(ref uint64, itvs ...Interval) {
	t.mtx.Lock()
	defer t.mtx.Unlock()
	for _, itv := range itvs {
		t.intvlGroups[ref] = t.intvlGroups[ref].add(itv)
	}
}

// TombstoneFile holds information about the tombstone file.
type TombstoneFile struct {
	size int64
}

// Size returns the tombstone file size.
func (t *TombstoneFile) Size() int64 {
	return t.size
}

func (*memTombstones) Close() error {
	return nil
}

// Interval represents a single time-interval.
type Interval struct {
	Mint, Maxt int64
}

func (tr Interval) inBounds(t int64) bool {
	return t >= tr.Mint && t <= tr.Maxt
}

func (tr Interval) isSubrange(dranges Intervals) bool {
	for _, r := range dranges {
		if r.inBounds(tr.Mint) && r.inBounds(tr.Maxt) {
			return true
		}
	}

	return false
}

// Intervals represents	a set of increasing and non-overlapping time-intervals.
type Intervals []Interval

// add the new time-range to the existing ones.
// The existing ones must be sorted.
func (itvs Intervals) add(n Interval) Intervals {
	for i, r := range itvs {
		// TODO(gouthamve): Make this codepath easier to digest.
		if r.inBounds(n.Mint-1) || r.inBounds(n.Mint) {
			if n.Maxt > r.Maxt {
				itvs[i].Maxt = n.Maxt
			}

			j := 0
			for _, r2 := range itvs[i+1:] {
				if n.Maxt < r2.Mint {
					break
				}
				j++
			}
			if j != 0 {
				if itvs[i+j].Maxt > n.Maxt {
					itvs[i].Maxt = itvs[i+j].Maxt
				}
				itvs = append(itvs[:i+1], itvs[i+j+1:]...)
			}
			return itvs
		}

		if r.inBounds(n.Maxt+1) || r.inBounds(n.Maxt) {
			if n.Mint < r.Maxt {
				itvs[i].Mint = n.Mint
			}
			return itvs
		}

		if n.Mint < r.Mint {
			newRange := make(Intervals, i, len(itvs[:i])+1)
			copy(newRange, itvs[:i])
			newRange = append(newRange, n)
			newRange = append(newRange, itvs[i:]...)

			return newRange
		}
	}

	itvs = append(itvs, n)
	return itvs
}
<|endoftext|>


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

package main

import (
	"crypto/rand"
	"fmt"
	"github.com/unixdj/smtplike"
	"io"
	"net"
	"regexp" // i'm so lazy
	"strconv"
)

var netKeyRE = regexp.MustCompile(`^[0-9a-fA-F]{64}$`)

func mgmtGreet(args []string, c *smtplike.Conn) (int, string) {
	return smtplike.Hello, "benchnet-management-0 hello"
}

func mgmtAddJob(args []string, c *smtplike.Conn) (int, string) {
	if len(args) < 6 {
		return 501, "invalid syntax"
	}
	var (
		j   job
		tmp int64
		err error
	)
	if j.Id, err = strconv.ParseUint(args[0], 0, 64); err != nil {
		return 501, args[0] + ": " + err.Error()
	}
	if tmp, err = strconv.ParseInt(args[1], 0, 32); err != nil {
		return 501, args[1] + ": " + err.Error()
	}
	j.Period = int(tmp)
	if tmp, err = strconv.ParseInt(args[2], 0, 32); err != nil {
		return 501, args[2] + ": " + err.Error()
	}
	j.Start = int(tmp)
	if tmp, err = strconv.ParseInt(args[3], 0, 32); err != nil {
		return 501, args[3] + ": " + err.Error()
	}
	j.capa = int(tmp)
	if tmp, err = strconv.ParseInt(args[4], 0, 32); err != nil {
		return 501, args[4] + ": " + err.Error()
	}
	j.nodes = make([]uint64, 0, int(tmp))
	j.Check = args[5:]
	if jp := getJob(j.Id); jp != nil {
		return 550, "job already exists"
	}
	addJob(&j)
	return 200, "ok"
}

func mgmtRmJob(args []string, c *smtplike.Conn) (int, string) {
	if len(args) != 1 {
		return 501, "invalid syntax"
	}
	id, err := strconv.ParseUint(args[0], 0, 64)
	if err != nil {
		return 501, args[0] + ": " + err.Error()
	}
	if j := getJob(id); j != nil {
		rmJob(j)
	} else {
		return 550, "job does not exist"
	}
	return 200, "ok"
}

func mgmtAddNode(args []string, c *smtplike.Conn) (int, string) {
	if len(args) < 3 || len(args) > 4 {
		return 501, "invalid syntax"
	}
	var (
		n   node
		err error
	)
	if n.id, err = strconv.ParseUint(args[0], 0, 64); err != nil {
		return 501, args[0] + ": " + err.(*strconv.NumError).Err.Error()
	}
	if tmp, err := strconv.ParseInt(args[1], 0, 32); err != nil {
		return 501, args[1] + ": " + err.Error()
	} else {
		n.capa = int(tmp)
	}
	if tmp, err := strconv.ParseUint(args[2], 0, 64); err != nil {
		return 501, args[2] + ": " + err.Error()
	} else {
		n.loc = geoloc(tmp)
	}
	n.key = make([]byte, 32)
	if len(args) == 4 {
		l, err := io.ReadFull(rand.Reader, n.key)
		if l != len(n.key) || err != nil {
			return 501, "rand: " + err.Error()
		}
	} else {
		if !netKeyRE.MatchString(args[3]) {
			return 501, args[3] + ": must be 64 hexadecimal digits"
		}
		fmt.Sscanf(args[3], "%x", n.key)
	}
	if np := getJob(n.id); np != nil {
		return 550, "node already exists"
	}
	addNode(&n)
	return 200, "ok"
}

func mgmtRmNode(args []string, c *smtplike.Conn) (int, string) {
	if len(args) != 1 {
		return 501, "invalid syntax"
	}
	id, err := strconv.ParseUint(args[0], 0, 64)
	if err != nil {
		return 501, args[0] + ": " + err.Error()
	}
	if n := getNode(id); n != nil {
		rmNode(n)
	} else {
		return 550, "node does not exist"
	}
	return 200, "ok"
}

func mgmtList(args []string, c *smtplike.Conn) (int, string) {
	if len(args) != 0 {
		return 501, "invalid syntax"
	}
	s := nodes.String() + jobs.String()
	if len(s) >= 2 {
		s = s[:len(s)-2]
	}
	return 210, s
}

func mgmtSched(args []string, c *smtplike.Conn) (code int, msg string) {
	if len(args) != 0 {
		return 501, "invalid syntax"
	}
	requestSchedule()
	return 210, "ok"
}

func mgmtCommit(args []string, c *smtplike.Conn) (code int, msg string) {
	if len(args) != 0 {
		return 501, "invalid syntax"
	}
	requestCommit()
	return 210, "ok"
}

func mgmtHelp(args []string, c *smtplike.Conn) (code int, msg string) {
	if len(args) != 0 {
		return 501, "invalid syntax"
	}
	return 214, `commands:
commit
    commit changes to database
h|help
    help
job <id> <period> <start> <capacity> <times> <check>...
    add job
list
    list nodes and jobs
node <id> <capacity> <geoloc> [<key>]
    add node
quit
    quit
rmjob <id>
    remove job
rmnode <id>
    remove node
sched
    run scheduler and commit changes to database`
}

func mgmtQuit(args []string, c *smtplike.Conn) (code int, msg string) {
	if len(args) != 0 {
		return 501, "invalid syntax"
	}
	return smtplike.Goodbye, "bye"
}

var mgmt = smtplike.Proto{
	{"", mgmtGreet},
	{"h", mgmtHelp},
	{"help", mgmtHelp},
	{"job", mgmtAddJob},
	{"list", mgmtList},
	{"node", mgmtAddNode},
	{"rmjob", mgmtRmJob},
	{"rmnode", mgmtRmNode},
	{"sched", mgmtSched},
	{"commit", mgmtCommit},
	{"quit", mgmtQuit},
}

func mgmtHandle(c net.Conn) {
	if err := mgmt.Run(c, nil); err != nil {
		log.Err("management connection terminated: " + err.Error())
		return
	}
	log.Notice("management connection completed")
}
<commit_msg>Srv: fix key scsnning in mgmt<commit_after>// Benchnet
//
// Copyright 2012 Vadim Vygonets
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package main

import (
	"crypto/rand"
	"fmt"
	"github.com/unixdj/smtplike"
	"io"
	"net"
	"regexp" // i'm so lazy
	"strconv"
)

var netKeyRE = regexp.MustCompile(`^[0-9a-fA-F]{64}$`)

func mgmtGreet(args []string, c *smtplike.Conn) (int, string) {
	return smtplike.Hello, "benchnet-management-0 hello"
}

func mgmtAddJob(args []string, c *smtplike.Conn) (int, string) {
	if len(args) < 6 {
		return 501, "invalid syntax"
	}
	var (
		j   job
		tmp int64
		err error
	)
	if j.Id, err = strconv.ParseUint(args[0], 0, 64); err != nil {
		return 501, args[0] + ": " + err.Error()
	}
	if tmp, err = strconv.ParseInt(args[1], 0, 32); err != nil {
		return 501, args[1] + ": " + err.Error()
	}
	j.Period = int(tmp)
	if tmp, err = strconv.ParseInt(args[2], 0, 32); err != nil {
		return 501, args[2] + ": " + err.Error()
	}
	j.Start = int(tmp)
	if tmp, err = strconv.ParseInt(args[3], 0, 32); err != nil {
		return 501, args[3] + ": " + err.Error()
	}
	j.capa = int(tmp)
	if tmp, err = strconv.ParseInt(args[4], 0, 32); err != nil {
		return 501, args[4] + ": " + err.Error()
	}
	j.nodes = make([]uint64, 0, int(tmp))
	j.Check = args[5:]
	if jp := getJob(j.Id); jp != nil {
		return 550, "job already exists"
	}
	addJob(&j)
	return 200, "ok"
}

func mgmtRmJob(args []string, c *smtplike.Conn) (int, string) {
	if len(args) != 1 {
		return 501, "invalid syntax"
	}
	id, err := strconv.ParseUint(args[0], 0, 64)
	if err != nil {
		return 501, args[0] + ": " + err.Error()
	}
	if j := getJob(id); j != nil {
		rmJob(j)
	} else {
		return 550, "job does not exist"
	}
	return 200, "ok"
}

func mgmtAddNode(args []string, c *smtplike.Conn) (int, string) {
	if len(args) < 3 || len(args) > 4 {
		return 501, "invalid syntax"
	}
	var (
		n   node
		err error
	)
	if n.id, err = strconv.ParseUint(args[0], 0, 64); err != nil {
		return 501, args[0] + ": " + err.(*strconv.NumError).Err.Error()
	}
	if tmp, err := strconv.ParseInt(args[1], 0, 32); err != nil {
		return 501, args[1] + ": " + err.Error()
	} else {
		n.capa = int(tmp)
	}
	if tmp, err := strconv.ParseUint(args[2], 0, 64); err != nil {
		return 501, args[2] + ": " + err.Error()
	} else {
		n.loc = geoloc(tmp)
	}
	n.key = make([]byte, 32)
	if len(args) == 3 {
		l, err := io.ReadFull(rand.Reader, n.key)
		if l != len(n.key) || err != nil {
			return 501, "rand: " + err.Error()
		}
	} else {
		if !netKeyRE.MatchString(args[3]) {
			return 501, args[3] + ": must be 64 hexadecimal digits"
		}
		fmt.Sscanf(args[3], "%x", &n.key)
	}
	if np := getNode(n.id); np != nil {
		return 550, "node already exists"
	}
	addNode(&n)
	return 200, "ok"
}

func mgmtRmNode(args []string, c *smtplike.Conn) (int, string) {
	if len(args) != 1 {
		return 501, "invalid syntax"
	}
	id, err := strconv.ParseUint(args[0], 0, 64)
	if err != nil {
		return 501, args[0] + ": " + err.Error()
	}
	if n := getNode(id); n != nil {
		rmNode(n)
	} else {
		return 550, "node does not exist"
	}
	return 200, "ok"
}

func mgmtList(args []string, c *smtplike.Conn) (int, string) {
	if len(args) != 0 {
		return 501, "invalid syntax"
	}
	s := nodes.String() + jobs.String()
	if len(s) >= 2 {
		s = s[:len(s)-2]
	}
	return 210, s
}

func mgmtSched(args []string, c *smtplike.Conn) (code int, msg string) {
	if len(args) != 0 {
		return 501, "invalid syntax"
	}
	requestSchedule()
	return 210, "ok"
}

func mgmtCommit(args []string, c *smtplike.Conn) (code int, msg string) {
	if len(args) != 0 {
		return 501, "invalid syntax"
	}
	requestCommit()
	return 210, "ok"
}

func mgmtHelp(args []string, c *smtplike.Conn) (code int, msg string) {
	if len(args) != 0 {
		return 501, "invalid syntax"
	}
	return 214, `commands:
commit
    commit changes to database
h|help
    help
job <id> <period> <start> <capacity> <times> <check>...
    add job
list
    list nodes and jobs
node <id> <capacity> <geoloc> [<key>]
    add node
quit
    quit
rmjob <id>
    remove job
rmnode <id>
    remove node
sched
    run scheduler and commit changes to database`
}

func mgmtQuit(args []string, c *smtplike.Conn) (code int, msg string) {
	if len(args) != 0 {
		return 501, "invalid syntax"
	}
	return smtplike.Goodbye, "bye"
}

var mgmt = smtplike.Proto{
	{"", mgmtGreet},
	{"h", mgmtHelp},
	{"help", mgmtHelp},
	{"job", mgmtAddJob},
	{"list", mgmtList},
	{"node", mgmtAddNode},
	{"rmjob", mgmtRmJob},
	{"rmnode", mgmtRmNode},
	{"sched", mgmtSched},
	{"commit", mgmtCommit},
	{"quit", mgmtQuit},
}

func mgmtHandle(c net.Conn) {
	if err := mgmt.Run(c, nil); err != nil {
		log.Err("management connection terminated: " + err.Error())
		return
	}
	log.Notice("management connection completed")
}
<|endoftext|>


<commit_before>package router

import (
	"fmt"
	"math"
	"math/rand"
	"net/http"
	"net/http/httptest"
	"testing"

	"golang.scot/liberty/middleware"
)

/*func TestExactMatch(t *testing.T) {
	router := &HTTPRouter{}
	mux := http.NewServeMux()
	sg := &ServerGroup{servers: []*server{{s: &http.Server{Handler: mux}, handler: mux}}}
	if err := router.put("http://www.example.com", sg); err != nil {
		t.Errorf("insertion error: foo")
	}
	if match := router.Getc("http://www.example.com"); match == nil {
		t.Errorf("bad search: foo")
	}
}
*/

func newServerGroup() http.Handler {
	mux := http.NewServeMux()
	return mux
	//return &balancer.ServerGroup{servers: []*server{{s: &http.Server{Handler: mux}, handler: mux}}}
}

func httpWriterRequest(urlPath string) (http.ResponseWriter, *http.Request) {
	w := httptest.NewRecorder()
	req, _ := http.NewRequest("GET", urlPath, nil)
	return w, req
}

func newRouter() *HTTPRouter {
	router := NewHTTPRouter()
	router.Use(
		[]middleware.Chainable{&middleware.HelloWorld{}},
	)

	return router
}

func TestRouteMatch(t *testing.T) {
	router := newRouter()
	mux := http.NewServeMux()

	ctx := ctxPool.Get().(*Context)
	ctx.Reset()

	if err := router.Handle("/test/example/path", mux); err != nil {
		t.Error(err)
	}
	match := router.match("/test/example/path", ctx)
	if match == nil {
		t.Errorf("bad search:")
	}

	if fmt.Sprintf("%p", mux) != fmt.Sprintf("%p", match) {
		t.Errorf("address mismatch: - h: %#v,  match: %#v", mux, match)
	}

	ctxPool.Put(ctx)
}

func TestMatchLastVar(t *testing.T) {

	router := NewHTTPRouter()
	mux := http.NewServeMux()
	ctx := ctxPool.Get().(*Context)
	ctx.Reset()
	//w := httptest.NewRecorder()

	if err := router.Handle("/test/:var1", mux); err != nil {
		t.Error(err)
	}

	match := router.match("/test/foo", ctx)
	if match == nil {
		t.Errorf("bad search:")
	}

	if fmt.Sprintf("%p", mux) != fmt.Sprintf("%p", match) {
		t.Errorf("address mismatch: - h: %#v,  match: %#v", mux, match)
	}

	ctxPool.Put(ctx)
}

func TestRouteMatchOneVar(t *testing.T) {

	router := NewHTTPRouter()
	mux := http.NewServeMux()
	ctx := ctxPool.Get().(*Context)
	ctx.Reset()

	if err := router.Handle("/test/:varone/bar", mux); err != nil {
		t.Error(err)
	}

	match := router.match("/test/foo/bar", ctx)
	if match == nil {
		t.Errorf("bad search:")
	}

	if fmt.Sprintf("%p", mux) != fmt.Sprintf("%p", match) {
		t.Errorf("address mismatch: - h: %#v,  match: %#v", mux, match)
	}

	ctxPool.Put(ctx)
}

func TestRouteMatchTwoVar(t *testing.T) {

	router := NewHTTPRouter()
	mux := http.NewServeMux()
	ctx := ctxPool.Get().(*Context)
	ctx.Reset()

	if err := router.Handle("/test/example/:var1/path/:var2", mux); err != nil {
		t.Error(err)
	}

	match := router.match("/test/example/foobar/path/barbaz", ctx)
	if match == nil {
		t.Errorf("bad search:")
	}

	if fmt.Sprintf("%p", mux) != fmt.Sprintf("%p", match) {
		t.Errorf("address mismatch: - h: %#v,  match: %#v", mux, match)
	}

	ctxPool.Put(ctx)
}

func TestRouteMatchLongest(t *testing.T) {

	router := &HTTPRouter{}
	mux := http.NewServeMux()
	ctx := ctxPool.Get().(*Context)
	ctx.Reset()

	router.Handle("http://www.example.com/*", mux)
	match := router.match("http://www.example.com/foo/", ctx)
	if match == nil {
		t.Errorf("bad search:")
	}
	if fmt.Sprintf("%p", mux) != fmt.Sprintf("%p", match) {
		t.Errorf("address mismatch: - h: %#v,  match: %#v", mux, match)
	}

	ctxPool.Put(ctx)
}

func TestBenchFail(t *testing.T) {
	router := &HTTPRouter{}
	mux := http.NewServeMux()
	ctx := ctxPool.Get().(*Context)
	ctx.Reset()
	testRoute := "/users/:user/following"
	router.Handle(testRoute, mux)
	match := router.match("/users/foobar/following", ctx)
	if match == nil {
		t.Errorf("bad search:")
	}
	if fmt.Sprintf("%p", mux) != fmt.Sprintf("%p", match) {
		t.Errorf("address mismatch: - h: %#v,  match: %#v", mux, match)
	}

	ctxPool.Put(ctx)
}

/*
func TestLongesPrefixtMatch(t *testing.T) {
	router := &HTTPRouter{}
	mux1 := http.NewServeMux()
	mux2 := http.NewServeMux()
	h1 := &ServerGroup{servers: []*server{{s: &http.Server{Handler: mux1}, handler: mux1}}}
	h2 := &ServerGroup{servers: []*server{{s: &http.Server{Handler: mux2}, handler: mux2}}}
	router.put("http://www.example.com/", h1)
	router.put("http://www.example.com/foo/", h2)
	match := router.Getc("http://www.example.com/foo/bar")
	if match == nil {
		t.Errorf("bad search: no match")
	}
	if fmt.Sprintf("%p", mux2) != fmt.Sprintf("%p", match) {
		t.Errorf("address mismatch: h2: %#v,  match: %#v", mux2, match)
	}
}
*/

/*func BenchmarkTreePut(b *testing.B) {
	router := &HTTPRouter{}
	rand.Seed(42)

	b.ResetTimer()

	for n := 0; n < b.N; n++ {
		key := []rune{}
		mux := http.NewServeMux()
		for n := 0; n < rand.Intn(1000); n++ {
			key = append(key, rune(rand.Intn(94)+32))
		}

		router.put(string(key), mux)
	}
}

func BenchmarkMapPut(b *testing.B) {
	hash := make(map[string]http.Handler)
	rand.Seed(42)

	b.ResetTimer()

	for n := 0; n < b.N; n++ {
		key := []rune{}
		mux := http.NewServeMux()
		for n := 0; n < rand.Intn(1000); n++ {
			key = append(key, rune(rand.Intn(94)+32))
		}

		hash[string(key)] = mux
	}
}*/

func valuesForBenchmark(numValues int, cb func(string)) {
	rand.Seed(42)
	for i := 0; i < numValues; i++ {
		key := []rune{}
		if i == int(math.Floor(float64(numValues/2.0))) {
			key = []rune("www.match.com/api/path")
		} else {
			for j := 0; j < rand.Intn(1000)+1; j++ {
				key = append(key, rune(rand.Intn(94)+32))
			}
		}
		cb(string(key))
	}
}

func loadGithubApi(cb func(string) error) {
	for _, route := range githubAPI {
		cb(string(route.path))
	}
}

func BenchmarkTreeGet1000(b *testing.B) {
	router := newRouter()
	sg := newServerGroup()
	loadGithubApi(func(key string) error {
		return router.Handle(key, sg)
	})

	w, req := httpWriterRequest("/user/repos")

	b.ReportAllocs()
	b.ResetTimer()

	//ctx := ctxPool.Get().(*Context)
	//ctx.Reset()

	for n := 0; n < b.N; n++ {
		router.ServeHTTP(w, req)
		//	_ = router.match("/user/repos", ctx)
	}

	//ctxPool.Put(ctx)
}

func BenchmarkTreeGetVar1000(b *testing.B) {
	router := newRouter()
	sg := newServerGroup()

	loadGithubApi(func(key string) error {
		return router.Handle(key, sg)
	})

	w, req := httpWriterRequest("/user/repos")

	b.ReportAllocs()
	b.ResetTimer()

	//ctx := ctxPool.Get().(*Context)
	//ctx.Reset()

	for n := 0; n < b.N; n++ {
		router.ServeHTTP(w, req)
		//_ = router.match("/users/graham/gists", ctx)
	}

	//ctxPool.Put(ctx)
}
<commit_msg>test for deeper path placeholders<commit_after>package router

import (
	"fmt"
	"math"
	"math/rand"
	"net/http"
	"net/http/httptest"
	"testing"

	"golang.scot/liberty/middleware"
)

/*func TestExactMatch(t *testing.T) {
	router := &HTTPRouter{}
	mux := http.NewServeMux()
	sg := &ServerGroup{servers: []*server{{s: &http.Server{Handler: mux}, handler: mux}}}
	if err := router.put("http://www.example.com", sg); err != nil {
		t.Errorf("insertion error: foo")
	}
	if match := router.Getc("http://www.example.com"); match == nil {
		t.Errorf("bad search: foo")
	}
}
*/

func newServerGroup() http.Handler {
	mux := http.NewServeMux()
	return mux
	//return &balancer.ServerGroup{servers: []*server{{s: &http.Server{Handler: mux}, handler: mux}}}
}

func httpWriterRequest(urlPath string) (http.ResponseWriter, *http.Request) {
	w := httptest.NewRecorder()
	req, _ := http.NewRequest("GET", urlPath, nil)
	return w, req
}

func newRouter() *HTTPRouter {
	router := NewHTTPRouter()
	router.Use(
		[]middleware.Chainable{&middleware.HelloWorld{}},
	)

	return router
}

func TestRouteMatch(t *testing.T) {
	router := newRouter()
	mux := http.NewServeMux()

	ctx := ctxPool.Get().(*Context)
	ctx.Reset()

	if err := router.Handle("/test/example/path", mux); err != nil {
		t.Error(err)
	}
	match := router.match("/test/example/path", ctx)
	if match == nil {
		t.Errorf("bad search:")
	}

	if fmt.Sprintf("%p", mux) != fmt.Sprintf("%p", match) {
		t.Errorf("address mismatch: - h: %#v,  match: %#v", mux, match)
	}

	ctxPool.Put(ctx)
}

func TestBenchMatchFail01(t *testing.T) {
	testPath := "/test/test/test/test/test"
	fiveColon := "/:a/:b/:c/:d/:e"

	router := newRouter()
	mux := http.NewServeMux()

	ctx := ctxPool.Get().(*Context)
	ctx.Reset()

	if err := router.Handle(fiveColon, mux); err != nil {
		t.Error(err)
	}
	match := router.match(testPath, ctx)
	if match == nil {
		t.Errorf("bad search:")
	}

	if fmt.Sprintf("%p", mux) != fmt.Sprintf("%p", match) {
		t.Errorf("address mismatch: - h: %#v,  match: %#v", mux, match)
	}

	ctxPool.Put(ctx)

}

func TestMatchLastVar(t *testing.T) {

	router := NewHTTPRouter()
	mux := http.NewServeMux()
	ctx := ctxPool.Get().(*Context)
	ctx.Reset()
	//w := httptest.NewRecorder()

	if err := router.Handle("/test/:var1", mux); err != nil {
		t.Error(err)
	}

	match := router.match("/test/foo", ctx)
	if match == nil {
		t.Errorf("bad search:")
	}

	if fmt.Sprintf("%p", mux) != fmt.Sprintf("%p", match) {
		t.Errorf("address mismatch: - h: %#v,  match: %#v", mux, match)
	}

	ctxPool.Put(ctx)
}

func TestRouteMatchOneVar(t *testing.T) {

	router := NewHTTPRouter()
	mux := http.NewServeMux()
	ctx := ctxPool.Get().(*Context)
	ctx.Reset()

	if err := router.Handle("/test/:varone/bar", mux); err != nil {
		t.Error(err)
	}

	match := router.match("/test/foo/bar", ctx)
	if match == nil {
		t.Errorf("bad search:")
	}

	if fmt.Sprintf("%p", mux) != fmt.Sprintf("%p", match) {
		t.Errorf("address mismatch: - h: %#v,  match: %#v", mux, match)
	}

	ctxPool.Put(ctx)
}

func TestRouteMatchTwoVar(t *testing.T) {

	router := NewHTTPRouter()
	mux := http.NewServeMux()
	ctx := ctxPool.Get().(*Context)
	ctx.Reset()

	if err := router.Handle("/test/example/:var1/path/:var2", mux); err != nil {
		t.Error(err)
	}

	match := router.match("/test/example/foobar/path/barbaz", ctx)
	if match == nil {
		t.Errorf("bad search:")
	}

	if fmt.Sprintf("%p", mux) != fmt.Sprintf("%p", match) {
		t.Errorf("address mismatch: - h: %#v,  match: %#v", mux, match)
	}

	ctxPool.Put(ctx)
}

func TestRouteMatchLongest(t *testing.T) {

	router := &HTTPRouter{}
	mux := http.NewServeMux()
	ctx := ctxPool.Get().(*Context)
	ctx.Reset()

	router.Handle("http://www.example.com/*", mux)
	match := router.match("http://www.example.com/foo/", ctx)
	if match == nil {
		t.Errorf("bad search:")
	}
	if fmt.Sprintf("%p", mux) != fmt.Sprintf("%p", match) {
		t.Errorf("address mismatch: - h: %#v,  match: %#v", mux, match)
	}

	ctxPool.Put(ctx)
}

func TestBenchFail(t *testing.T) {
	router := &HTTPRouter{}
	mux := http.NewServeMux()
	ctx := ctxPool.Get().(*Context)
	ctx.Reset()
	testRoute := "/users/:user/following"
	router.Handle(testRoute, mux)
	match := router.match("/users/foobar/following", ctx)
	if match == nil {
		t.Errorf("bad search:")
	}
	if fmt.Sprintf("%p", mux) != fmt.Sprintf("%p", match) {
		t.Errorf("address mismatch: - h: %#v,  match: %#v", mux, match)
	}

	ctxPool.Put(ctx)
}

/*
func TestLongesPrefixtMatch(t *testing.T) {
	router := &HTTPRouter{}
	mux1 := http.NewServeMux()
	mux2 := http.NewServeMux()
	h1 := &ServerGroup{servers: []*server{{s: &http.Server{Handler: mux1}, handler: mux1}}}
	h2 := &ServerGroup{servers: []*server{{s: &http.Server{Handler: mux2}, handler: mux2}}}
	router.put("http://www.example.com/", h1)
	router.put("http://www.example.com/foo/", h2)
	match := router.Getc("http://www.example.com/foo/bar")
	if match == nil {
		t.Errorf("bad search: no match")
	}
	if fmt.Sprintf("%p", mux2) != fmt.Sprintf("%p", match) {
		t.Errorf("address mismatch: h2: %#v,  match: %#v", mux2, match)
	}
}
*/

/*func BenchmarkTreePut(b *testing.B) {
	router := &HTTPRouter{}
	rand.Seed(42)

	b.ResetTimer()

	for n := 0; n < b.N; n++ {
		key := []rune{}
		mux := http.NewServeMux()
		for n := 0; n < rand.Intn(1000); n++ {
			key = append(key, rune(rand.Intn(94)+32))
		}

		router.put(string(key), mux)
	}
}

func BenchmarkMapPut(b *testing.B) {
	hash := make(map[string]http.Handler)
	rand.Seed(42)

	b.ResetTimer()

	for n := 0; n < b.N; n++ {
		key := []rune{}
		mux := http.NewServeMux()
		for n := 0; n < rand.Intn(1000); n++ {
			key = append(key, rune(rand.Intn(94)+32))
		}

		hash[string(key)] = mux
	}
}*/

func valuesForBenchmark(numValues int, cb func(string)) {
	rand.Seed(42)
	for i := 0; i < numValues; i++ {
		key := []rune{}
		if i == int(math.Floor(float64(numValues/2.0))) {
			key = []rune("www.match.com/api/path")
		} else {
			for j := 0; j < rand.Intn(1000)+1; j++ {
				key = append(key, rune(rand.Intn(94)+32))
			}
		}
		cb(string(key))
	}
}

func loadGithubApi(cb func(string) error) {
	for _, route := range githubAPI {
		cb(string(route.path))
	}
}

func BenchmarkTreeGet1000(b *testing.B) {
	router := newRouter()
	sg := newServerGroup()
	loadGithubApi(func(key string) error {
		return router.Handle(key, sg)
	})

	w, req := httpWriterRequest("/user/repos")

	b.ReportAllocs()
	b.ResetTimer()

	//ctx := ctxPool.Get().(*Context)
	//ctx.Reset()

	for n := 0; n < b.N; n++ {
		router.ServeHTTP(w, req)
		//	_ = router.match("/user/repos", ctx)
	}

	//ctxPool.Put(ctx)
}

func BenchmarkTreeGetVar1000(b *testing.B) {
	router := newRouter()
	sg := newServerGroup()

	loadGithubApi(func(key string) error {
		return router.Handle(key, sg)
	})

	w, req := httpWriterRequest("/user/repos")

	b.ReportAllocs()
	b.ResetTimer()

	//ctx := ctxPool.Get().(*Context)
	//ctx.Reset()

	for n := 0; n < b.N; n++ {
		router.ServeHTTP(w, req)
		//_ = router.match("/users/graham/gists", ctx)
	}

	//ctxPool.Put(ctx)
}
<|endoftext|>


<commit_before><commit_msg>Update meta action holder<commit_after><|endoftext|>


<commit_before>package pkg

import (
	// Stdlib
	"fmt"

	// Internal
	"github.com/salsaflow/salsaflow/errs"
	"github.com/salsaflow/salsaflow/log"
	"github.com/salsaflow/salsaflow/prompt"

	// Other
	"github.com/google/go-github/github"
)

type InstallOptions struct {
	GitHubOwner     string
	GitHubRepo      string
	TargetDirectory string
}

func Install(version string, opts *InstallOptions) error {
	// Get GitHub owner and repository names.
	var (
		owner     = DefaultGitHubOwner
		repo      = DefaultGitHubRepo
		targetDir string
	)
	if opts != nil {
		if opts.GitHubOwner != "" {
			owner = opts.GitHubOwner
		}
		if opts.GitHubRepo != "" {
			repo = opts.GitHubRepo
		}
		targetDir = opts.TargetDirectory
	}

	// Instantiate a GitHub client.
	task := "Instantiate a GitHub client"
	client, err := newGitHubClient()
	if err != nil {
		return errs.NewError(task, err)
	}

	// Fetch the list of available GitHub releases.
	task = fmt.Sprintf("Fetch GitHub releases for %v/%v", owner, repo)
	log.Run(task)
	releases, err := listReleases(client, owner, repo)
	if err != nil {
		return errs.NewError(task, err)
	}

	// Get the release matching the chosen version string.
	task = "Get the release metadata"
	var (
		release *github.RepositoryRelease
		tagName = "v" + version
	)
	for _, r := range releases {
		if *r.TagName == tagName {
			release = &r
			break
		}
	}
	if release == nil {
		return errs.NewError(task, fmt.Errorf("SalsaFlow version %v not found", version))
	}

	// Prompt the user to confirm the the installation.
	task = "Prompt the user to confirm the installation"
	fmt.Println()
	confirmed, err := prompt.Confirm(fmt.Sprintf(
		"SalsaFlow version %v is about to be installed. Shall we proceed?", version), true)
	if err != nil {
		return errs.NewError(task, err)
	}
	if !confirmed {
		return ErrAborted
	}
	fmt.Println()

	// Proceed to actually install the executables.
	return doInstall(client, owner, repo, release.Assets, version, targetDir)
}
<commit_msg>pkg install: Reject drafts and pre-releases<commit_after>package pkg

import (
	// Stdlib
	"fmt"

	// Internal
	"github.com/salsaflow/salsaflow/errs"
	"github.com/salsaflow/salsaflow/log"
	"github.com/salsaflow/salsaflow/prompt"

	// Other
	"github.com/google/go-github/github"
)

type InstallOptions struct {
	GitHubOwner     string
	GitHubRepo      string
	TargetDirectory string
}

func Install(version string, opts *InstallOptions) error {
	// Get GitHub owner and repository names.
	var (
		owner     = DefaultGitHubOwner
		repo      = DefaultGitHubRepo
		targetDir string
	)
	if opts != nil {
		if opts.GitHubOwner != "" {
			owner = opts.GitHubOwner
		}
		if opts.GitHubRepo != "" {
			repo = opts.GitHubRepo
		}
		targetDir = opts.TargetDirectory
	}

	// Instantiate a GitHub client.
	task := "Instantiate a GitHub client"
	client, err := newGitHubClient()
	if err != nil {
		return errs.NewError(task, err)
	}

	// Fetch the list of available GitHub releases.
	task = fmt.Sprintf("Fetch GitHub releases for %v/%v", owner, repo)
	log.Run(task)
	releases, err := listReleases(client, owner, repo)
	if err != nil {
		return errs.NewError(task, err)
	}

	// Get the release matching the chosen version string.
	tagName := "v" + version
	task = fmt.Sprintf("Search for the GitHub release associated with tag '%v'", tagName)

	var release *github.RepositoryRelease
	for _, r := range releases {
		if *r.TagName == tagName {
			release = &r
			break
		}
	}

	// Make sure we got a valid release.
	switch {
	case release == nil:
		return errs.NewError(task, fmt.Errorf("SalsaFlow version %v not found", version))
	case *release.Draft:
		return errs.NewError(task, fmt.Errorf("SalsaFlow version %v is a release draft", version))
	case *release.Prerelease:
		return errs.NewError(task, fmt.Errorf("SalsaFlow version %v is a pre-release", version))
	}

	// Prompt the user to confirm the the installation.
	task = "Prompt the user to confirm the installation"
	fmt.Println()
	confirmed, err := prompt.Confirm(fmt.Sprintf(
		"SalsaFlow version %v is about to be installed. Shall we proceed?", version), true)
	if err != nil {
		return errs.NewError(task, err)
	}
	if !confirmed {
		return ErrAborted
	}
	fmt.Println()

	// Proceed to actually install the executables.
	return doInstall(client, owner, repo, release.Assets, version, targetDir)
}
<|endoftext|>


<commit_before>// Copyright 2014 The Gogs Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.

package log

import (
	"fmt"
	"os"
	"path/filepath"
	"runtime"
	"strings"
	"sync"
)

var (
	loggers []*Logger
)

func NewLogger(bufLen int64, mode, config string) {
	logger := newLogger(bufLen)

	isExist := false
	for _, l := range loggers {
		if l.adapter == mode {
			isExist = true
			l = logger
		}
	}
	if !isExist {
		loggers = append(loggers, logger)
	}
	if err := logger.SetLogger(mode, config); err != nil {
		Fatal(1, "Fail to set logger(%s): %v", mode, err)
	}
}

func Trace(format string, v ...interface{}) {
	for _, logger := range loggers {
		logger.Trace(format, v...)
	}
}

func Debug(format string, v ...interface{}) {
	for _, logger := range loggers {
		logger.Debug(format, v...)
	}
}

func Info(format string, v ...interface{}) {
	for _, logger := range loggers {
		logger.Info(format, v...)
	}
}

func Warn(format string, v ...interface{}) {
	for _, logger := range loggers {
		logger.Warn(format, v...)
	}
}

func Error(skip int, format string, v ...interface{}) {
	for _, logger := range loggers {
		logger.Error(skip, format, v...)
	}
}

func Critical(skip int, format string, v ...interface{}) {
	for _, logger := range loggers {
		logger.Critical(skip, format, v...)
	}
}

func Fatal(skip int, format string, v ...interface{}) {
	Error(skip, format, v...)
	for _, l := range loggers {
		l.Close()
	}
	os.Exit(1)
}

func Close() {
	for _, l := range loggers {
		l.Close()
		// delete the logger.
		l = nil
	}
	// clear the loggers slice.
	loggers = nil
}

// .___        __                 _____
// |   | _____/  |_  ____________/ ____\____    ____  ____
// |   |/    \   __\/ __ \_  __ \   __\\__  \ _/ ___\/ __ \
// |   |   |  \  | \  ___/|  | \/|  |   / __ \\  \__\  ___/
// |___|___|  /__|  \___  >__|   |__|  (____  /\___  >___  >
//          \/          \/                  \/     \/    \/

type LogLevel int

const (
	TRACE LogLevel = iota
	DEBUG
	INFO
	WARN
	ERROR
	CRITICAL
	FATAL
)

// LoggerInterface represents behaviors of a logger provider.
type LoggerInterface interface {
	Init(config string) error
	WriteMsg(msg string, skip int, level LogLevel) error
	Destroy()
	Flush()
}

type loggerType func() LoggerInterface

var adapters = make(map[string]loggerType)

// Register registers given logger provider to adapters.
func Register(name string, log loggerType) {
	if log == nil {
		panic("log: register provider is nil")
	}
	if _, dup := adapters[name]; dup {
		panic("log: register called twice for provider \"" + name + "\"")
	}
	adapters[name] = log
}

type logMsg struct {
	skip  int
	level LogLevel
	msg   string
}

// Logger is default logger in beego application.
// it can contain several providers and log message into all providers.
type Logger struct {
	adapter string
	lock    sync.Mutex
	level   LogLevel
	msg     chan *logMsg
	outputs map[string]LoggerInterface
	quit    chan bool
}

// newLogger initializes and returns a new logger.
func newLogger(buffer int64) *Logger {
	l := &Logger{
		msg:     make(chan *logMsg, buffer),
		outputs: make(map[string]LoggerInterface),
		quit:    make(chan bool),
	}
	go l.StartLogger()
	return l
}

// SetLogger sets new logger instanse with given logger adapter and config.
func (l *Logger) SetLogger(adapter string, config string) error {
	l.lock.Lock()
	defer l.lock.Unlock()
	if log, ok := adapters[adapter]; ok {
		lg := log()
		if err := lg.Init(config); err != nil {
			return err
		}
		l.outputs[adapter] = lg
		l.adapter = adapter
	} else {
		panic("log: unknown adapter \"" + adapter + "\" (forgotten register?)")
	}
	return nil
}

// DelLogger removes a logger adapter instance.
func (l *Logger) DelLogger(adapter string) error {
	l.lock.Lock()
	defer l.lock.Unlock()
	if lg, ok := l.outputs[adapter]; ok {
		lg.Destroy()
		delete(l.outputs, adapter)
	} else {
		panic("log: unknown adapter \"" + adapter + "\" (forgotten register?)")
	}
	return nil
}

func (l *Logger) writerMsg(skip int, level LogLevel, msg string) error {
	if l.level > level {
		return nil
	}
	lm := &logMsg{
		skip:  skip,
		level: level,
	}

	// Only error information needs locate position for debugging.
	if lm.level >= ERROR {
		pc, file, line, ok := runtime.Caller(skip)
		if ok {
			// Get caller function name.
			fn := runtime.FuncForPC(pc)
			var fnName string
			if fn == nil {
				fnName = "?()"
			} else {
				fnName = strings.TrimLeft(filepath.Ext(fn.Name()), ".") + "()"
			}

			lm.msg = fmt.Sprintf("[%s:%d %s] %s", filepath.Base(file), line, fnName, msg)
		} else {
			lm.msg = msg
		}
	} else {
		lm.msg = msg
	}
	l.msg <- lm
	return nil
}

// StartLogger starts logger chan reading.
func (l *Logger) StartLogger() {
	for {
		select {
		case bm := <-l.msg:
			for _, l := range l.outputs {
				if err := l.WriteMsg(bm.msg, bm.skip, bm.level); err != nil {
					fmt.Println("ERROR, unable to WriteMsg:", err)
				}
			}
		case <-l.quit:
			return
		}
	}
}

// Flush flushs all chan data.
func (l *Logger) Flush() {
	for _, l := range l.outputs {
		l.Flush()
	}
}

// Close closes logger, flush all chan data and destroy all adapter instances.
func (l *Logger) Close() {
	l.quit <- true
	for {
		if len(l.msg) > 0 {
			bm := <-l.msg
			for _, l := range l.outputs {
				if err := l.WriteMsg(bm.msg, bm.skip, bm.level); err != nil {
					fmt.Println("ERROR, unable to WriteMsg:", err)
				}
			}
		} else {
			break
		}
	}
	for _, l := range l.outputs {
		l.Flush()
		l.Destroy()
	}
}

func (l *Logger) Trace(format string, v ...interface{}) {
	msg := fmt.Sprintf("[T] "+format, v...)
	l.writerMsg(0, TRACE, msg)
}

func (l *Logger) Debug(format string, v ...interface{}) {
	msg := fmt.Sprintf("[D] "+format, v...)
	l.writerMsg(0, DEBUG, msg)
}

func (l *Logger) Info(format string, v ...interface{}) {
	msg := fmt.Sprintf("[I] "+format, v...)
	l.writerMsg(0, INFO, msg)
}

func (l *Logger) Warn(format string, v ...interface{}) {
	msg := fmt.Sprintf("[W] "+format, v...)
	l.writerMsg(0, WARN, msg)
}

func (l *Logger) Error(skip int, format string, v ...interface{}) {
	msg := fmt.Sprintf("[E] "+format, v...)
	l.writerMsg(skip, ERROR, msg)
}

func (l *Logger) Critical(skip int, format string, v ...interface{}) {
	msg := fmt.Sprintf("[C] "+format, v...)
	l.writerMsg(skip, CRITICAL, msg)
}

func (l *Logger) Fatal(skip int, format string, v ...interface{}) {
	msg := fmt.Sprintf("[F] "+format, v...)
	l.writerMsg(skip, FATAL, msg)
	l.Close()
	os.Exit(1)
}
<commit_msg>prevent needless cpu burning in unused levels. fix  #3898<commit_after>// Copyright 2014 The Gogs Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.

package log

import (
	"fmt"
	"os"
	"path/filepath"
	"runtime"
	"strings"
	"sync"
)

var (
	loggers []*Logger
)

func NewLogger(bufLen int64, mode, config string) {
	logger := newLogger(bufLen)

	isExist := false
	for _, l := range loggers {
		if l.adapter == mode {
			isExist = true
			l = logger
		}
	}
	if !isExist {
		loggers = append(loggers, logger)
	}
	if err := logger.SetLogger(mode, config); err != nil {
		Fatal(1, "Fail to set logger(%s): %v", mode, err)
	}
}

func Trace(format string, v ...interface{}) {
	for _, logger := range loggers {
		logger.Trace(format, v...)
	}
}

func Debug(format string, v ...interface{}) {
	for _, logger := range loggers {
		logger.Debug(format, v...)
	}
}

func Info(format string, v ...interface{}) {
	for _, logger := range loggers {
		logger.Info(format, v...)
	}
}

func Warn(format string, v ...interface{}) {
	for _, logger := range loggers {
		logger.Warn(format, v...)
	}
}

func Error(skip int, format string, v ...interface{}) {
	for _, logger := range loggers {
		logger.Error(skip, format, v...)
	}
}

func Critical(skip int, format string, v ...interface{}) {
	for _, logger := range loggers {
		logger.Critical(skip, format, v...)
	}
}

func Fatal(skip int, format string, v ...interface{}) {
	Error(skip, format, v...)
	for _, l := range loggers {
		l.Close()
	}
	os.Exit(1)
}

func Close() {
	for _, l := range loggers {
		l.Close()
		// delete the logger.
		l = nil
	}
	// clear the loggers slice.
	loggers = nil
}

// .___        __                 _____
// |   | _____/  |_  ____________/ ____\____    ____  ____
// |   |/    \   __\/ __ \_  __ \   __\\__  \ _/ ___\/ __ \
// |   |   |  \  | \  ___/|  | \/|  |   / __ \\  \__\  ___/
// |___|___|  /__|  \___  >__|   |__|  (____  /\___  >___  >
//          \/          \/                  \/     \/    \/

type LogLevel int

const (
	TRACE LogLevel = iota
	DEBUG
	INFO
	WARN
	ERROR
	CRITICAL
	FATAL
)

// LoggerInterface represents behaviors of a logger provider.
type LoggerInterface interface {
	Init(config string) error
	WriteMsg(msg string, skip int, level LogLevel) error
	Destroy()
	Flush()
}

type loggerType func() LoggerInterface

var adapters = make(map[string]loggerType)

// Register registers given logger provider to adapters.
func Register(name string, log loggerType) {
	if log == nil {
		panic("log: register provider is nil")
	}
	if _, dup := adapters[name]; dup {
		panic("log: register called twice for provider \"" + name + "\"")
	}
	adapters[name] = log
}

type logMsg struct {
	skip  int
	level LogLevel
	msg   string
}

// Logger is default logger in beego application.
// it can contain several providers and log message into all providers.
type Logger struct {
	adapter string
	lock    sync.Mutex
	level   LogLevel
	msg     chan *logMsg
	outputs map[string]LoggerInterface
	quit    chan bool
}

// newLogger initializes and returns a new logger.
func newLogger(buffer int64) *Logger {
	l := &Logger{
		msg:     make(chan *logMsg, buffer),
		outputs: make(map[string]LoggerInterface),
		quit:    make(chan bool),
	}
	go l.StartLogger()
	return l
}

// SetLogger sets new logger instanse with given logger adapter and config.
func (l *Logger) SetLogger(adapter string, config string) error {
	l.lock.Lock()
	defer l.lock.Unlock()
	if log, ok := adapters[adapter]; ok {
		lg := log()
		if err := lg.Init(config); err != nil {
			return err
		}
		l.outputs[adapter] = lg
		l.adapter = adapter
	} else {
		panic("log: unknown adapter \"" + adapter + "\" (forgotten register?)")
	}
	return nil
}

// DelLogger removes a logger adapter instance.
func (l *Logger) DelLogger(adapter string) error {
	l.lock.Lock()
	defer l.lock.Unlock()
	if lg, ok := l.outputs[adapter]; ok {
		lg.Destroy()
		delete(l.outputs, adapter)
	} else {
		panic("log: unknown adapter \"" + adapter + "\" (forgotten register?)")
	}
	return nil
}

func (l *Logger) writerMsg(skip int, level LogLevel, msg string) error {
	lm := &logMsg{
		skip:  skip,
		level: level,
	}

	// Only error information needs locate position for debugging.
	if lm.level >= ERROR {
		pc, file, line, ok := runtime.Caller(skip)
		if ok {
			// Get caller function name.
			fn := runtime.FuncForPC(pc)
			var fnName string
			if fn == nil {
				fnName = "?()"
			} else {
				fnName = strings.TrimLeft(filepath.Ext(fn.Name()), ".") + "()"
			}

			lm.msg = fmt.Sprintf("[%s:%d %s] %s", filepath.Base(file), line, fnName, msg)
		} else {
			lm.msg = msg
		}
	} else {
		lm.msg = msg
	}
	l.msg <- lm
	return nil
}

// StartLogger starts logger chan reading.
func (l *Logger) StartLogger() {
	for {
		select {
		case bm := <-l.msg:
			for _, l := range l.outputs {
				if err := l.WriteMsg(bm.msg, bm.skip, bm.level); err != nil {
					fmt.Println("ERROR, unable to WriteMsg:", err)
				}
			}
		case <-l.quit:
			return
		}
	}
}

// Flush flushs all chan data.
func (l *Logger) Flush() {
	for _, l := range l.outputs {
		l.Flush()
	}
}

// Close closes logger, flush all chan data and destroy all adapter instances.
func (l *Logger) Close() {
	l.quit <- true
	for {
		if len(l.msg) > 0 {
			bm := <-l.msg
			for _, l := range l.outputs {
				if err := l.WriteMsg(bm.msg, bm.skip, bm.level); err != nil {
					fmt.Println("ERROR, unable to WriteMsg:", err)
				}
			}
		} else {
			break
		}
	}
	for _, l := range l.outputs {
		l.Flush()
		l.Destroy()
	}
}

func (l *Logger) Trace(format string, v ...interface{}) {
	if l.level > TRACE {
		return
	}
	msg := fmt.Sprintf("[T] "+format, v...)
	l.writerMsg(0, TRACE, msg)
}

func (l *Logger) Debug(format string, v ...interface{}) {
	if l.level > DEBUG {
		return
	}
	msg := fmt.Sprintf("[D] "+format, v...)
	l.writerMsg(0, DEBUG, msg)
}

func (l *Logger) Info(format string, v ...interface{}) {
	if l.level > INFO {
		return
	}
	msg := fmt.Sprintf("[I] "+format, v...)
	l.writerMsg(0, INFO, msg)
}

func (l *Logger) Warn(format string, v ...interface{}) {
	if l.level > WARN {
		return
	}
	msg := fmt.Sprintf("[W] "+format, v...)
	l.writerMsg(0, WARN, msg)
}

func (l *Logger) Error(skip int, format string, v ...interface{}) {
	if l.level > ERROR {
		return
	}
	msg := fmt.Sprintf("[E] "+format, v...)
	l.writerMsg(skip, ERROR, msg)
}

func (l *Logger) Critical(skip int, format string, v ...interface{}) {
	if l.level > CRITICAL {
		return
	}
	msg := fmt.Sprintf("[C] "+format, v...)
	l.writerMsg(skip, CRITICAL, msg)
}

func (l *Logger) Fatal(skip int, format string, v ...interface{}) {
	if l.level > FATAL {
		return
	}
	msg := fmt.Sprintf("[F] "+format, v...)
	l.writerMsg(skip, FATAL, msg)
	l.Close()
	os.Exit(1)
}
<|endoftext|>


<commit_before>package mux

import (
	"errors"
	"fmt"
	"log"
	"sync"

	"github.com/bitfinexcom/bitfinex-api-go/pkg/models/event"
	"github.com/bitfinexcom/bitfinex-api-go/pkg/mux/client"
	"github.com/bitfinexcom/bitfinex-api-go/pkg/mux/msg"
)

// Mux will manage all connections and subscriptions. Will check if subscriptions
// limit is reached and spawn new connection when that happens. It will also listen
// to all incomming client messages and reconnect client with all its subscriptions
// in case of a failure
type Mux struct {
	cid           int
	publicChan    chan msg.Msg
	publicClients map[int]*client.Client
	privateChan   chan msg.Msg
	privateClient *client.Client
	mtx           *sync.RWMutex
	Err           error
	transform     bool
	apikey        string
	apisec        string
	subInfo       map[int64]event.Info
	authenticated bool
}

// New returns pointer to instance of mux
func New() *Mux {
	return &Mux{
		publicChan:    make(chan msg.Msg),
		privateChan:   make(chan msg.Msg),
		publicClients: make(map[int]*client.Client),
		mtx:           &sync.RWMutex{},
		subInfo:       map[int64]event.Info{},
	}
}

// TransformRaw enables data transformation and mapping to appropriate
// models before sending it to consumer
func (m *Mux) TransformRaw() *Mux {
	m.transform = true
	return m
}

// WithAPIKEY accepts and persists api key
func (m *Mux) WithAPIKEY(key string) *Mux {
	m.apikey = key
	return m
}

// WithAPISEC accepts and persists api sec
func (m *Mux) WithAPISEC(sec string) *Mux {
	m.apisec = sec
	return m
}

// Subscribe - given the details in form of event.Subscribe,
// subscribes client to public channels
func (m *Mux) Subscribe(sub event.Subscribe) *Mux {
	m.mtx.Lock()
	defer m.mtx.Unlock()

	if m.Err != nil {
		return m
	}

	if alreadySubscribed := m.publicClients[m.cid].Subs.Added(sub); alreadySubscribed {
		return m
	}

	m.publicClients[m.cid].Subscribe(sub)

	if limitReached := m.publicClients[m.cid].Subs.LimitReached(); limitReached {
		log.Printf("subs limit is reached on cid: %d, spawning new conn\n", m.cid)
		m.addClient()
	}
	return m
}

// Start creates initial clients for accepting connections
func (m *Mux) Start() *Mux {
	return m.addClient()
}

// Listen accepts a callback func that will get called each time mux
// receives a message from any of its clients/subscriptions. It
// should be called last, after all setup calls are made
func (m *Mux) Listen(cb func(interface{}, error)) error {
	if m.Err != nil {
		return m.Err
	}

	for {
		select {
		case ms, ok := <-m.publicChan:
			if !ok {
				return errors.New("channel has closed unexpectedly")
			}
			if ms.Err != nil {
				cb(nil, fmt.Errorf("conn:%d has failed | err:%s | reconnecting", ms.CID, ms.Err))
				m.reconnect(ms.CID)
				continue
			}
			// return raw payload data if transform is off
			if !m.transform {
				cb(ms.Data, nil)
				continue
			}
			// handle event type message
			if ms.IsEvent() {
				cb(m.handleEvent(ms.ProcessEvent()))
				continue
			}
			// handle data type message
			if ms.IsRaw() {
				cb(ms.ProcessRaw(m.subInfo))
				continue
			}
			cb(nil, fmt.Errorf("unrecognized msg signature: %s", ms.Data))
		case ms, ok := <-m.privateChan:
			if !ok {
				return errors.New("private channel has closed unexpectedly")
			}
			if ms.Err != nil {
				cb(nil, fmt.Errorf("conn has failed | err:%s | reconnecting", ms.Err))
				// m.reconnectPrivate(ms.CID) // TODO
				continue
			}
			// return raw payload data if transform is off
			if !m.transform {
				cb(ms.Data, nil)
				continue
			}
			// handle event type message
			if ms.IsEvent() {
				cb(m.handleEvent(ms.ProcessEvent()))
				continue
			}
			// handle data type message
			if ms.IsRaw() {
				cb(ms.ProcessPrivateRaw())
				continue
			}
			cb(nil, fmt.Errorf("unrecognized msg signature: %s", ms.Data))
		}
	}
}

// Send meant for authenticated input, takes payload in form of interface
// and calls client with it
func (m *Mux) Send(pld interface{}) error {
	if !m.authenticated || m.privateClient == nil {
		return errors.New("not authorized")
	}
	return m.privateClient.Send(pld)
}

func (m *Mux) hasAPIKeys() bool {
	return len(m.apikey) != 0 && len(m.apisec) != 0
}

func (m *Mux) addClient() *Mux {
	if m.Err != nil {
		return m
	}

	if m.hasAPIKeys() && m.privateClient == nil {
		m.addPrivateClient()
	}

	return m.addPublicClient()
}

func (m *Mux) handleEvent(i event.Info, err error) (event.Info, error) {
	switch i.Event {
	case "subscribed":
		m.subInfo[i.ChanID] = i
	case "auth":
		if i.Status == "OK" {
			m.subInfo[i.ChanID] = i
			m.authenticated = true
		}
	default:
		fmt.Printf("unhandled evtn: %+v\n", i)
	}
	return i, err
}

func (m *Mux) reconnect(cid int) {
	m.mtx.Lock()
	defer m.mtx.Unlock()
	// pull old client subscriptions
	subs := m.publicClients[cid].Subs.GetAll()
	// add fresh client
	m.addClient()
	// resubscribe old events
	for _, sub := range subs {
		log.Printf("resubscribing: %+v\n", sub)
		m.Subscribe(sub)
	}
	// remove old, closed channel from the lost
	delete(m.publicClients, cid)
}

func (m *Mux) addPublicClient() *Mux {
	// adding new client so making sure we increment cid
	m.cid++
	// create new public client and pass error to mux if any
	c := client.New(m.cid).Public()
	if c.Err != nil {
		m.Err = c.Err
		return m
	}
	// add new client to list for later reference
	m.publicClients[m.cid] = c
	// start listening for incoming client messages
	go c.Read(m.publicChan)
	return m
}

func (m *Mux) addPrivateClient() *Mux {
	// create new private client and pass error to mux if any
	c := client.
		New(m.cid).
		Private(m.apikey, m.apisec)

	if c.Err != nil {
		m.Err = c.Err
		return m
	}

	m.privateClient = c
	go c.Read(m.privateChan)
	return m
}
<commit_msg>handling private and public client reset upon failure<commit_after>package mux

import (
	"errors"
	"fmt"
	"log"
	"sync"

	"github.com/bitfinexcom/bitfinex-api-go/pkg/models/event"
	"github.com/bitfinexcom/bitfinex-api-go/pkg/mux/client"
	"github.com/bitfinexcom/bitfinex-api-go/pkg/mux/msg"
)

// Mux will manage all connections and subscriptions. Will check if subscriptions
// limit is reached and spawn new connection when that happens. It will also listen
// to all incomming client messages and reconnect client with all its subscriptions
// in case of a failure
type Mux struct {
	cid           int
	publicChan    chan msg.Msg
	publicClients map[int]*client.Client
	privateChan   chan msg.Msg
	privateClient *client.Client
	mtx           *sync.RWMutex
	Err           error
	transform     bool
	apikey        string
	apisec        string
	subInfo       map[int64]event.Info
	authenticated bool
}

// New returns pointer to instance of mux
func New() *Mux {
	return &Mux{
		publicChan:    make(chan msg.Msg),
		privateChan:   make(chan msg.Msg),
		publicClients: make(map[int]*client.Client),
		mtx:           &sync.RWMutex{},
		subInfo:       map[int64]event.Info{},
	}
}

// TransformRaw enables data transformation and mapping to appropriate
// models before sending it to consumer
func (m *Mux) TransformRaw() *Mux {
	m.transform = true
	return m
}

// WithAPIKEY accepts and persists api key
func (m *Mux) WithAPIKEY(key string) *Mux {
	m.apikey = key
	return m
}

// WithAPISEC accepts and persists api sec
func (m *Mux) WithAPISEC(sec string) *Mux {
	m.apisec = sec
	return m
}

// Subscribe - given the details in form of event.Subscribe,
// subscribes client to public channels
func (m *Mux) Subscribe(sub event.Subscribe) *Mux {
	m.mtx.Lock()
	defer m.mtx.Unlock()

	if m.Err != nil {
		return m
	}

	if alreadySubscribed := m.publicClients[m.cid].Subs.Added(sub); alreadySubscribed {
		return m
	}

	m.publicClients[m.cid].Subscribe(sub)

	if limitReached := m.publicClients[m.cid].Subs.LimitReached(); limitReached {
		log.Printf("subs limit is reached on cid: %d, spawning new conn\n", m.cid)
		m.addPublicClient()
	}
	return m
}

// Start creates initial clients for accepting connections
func (m *Mux) Start() *Mux {
	if m.Err != nil {
		return m
	}

	if m.hasAPIKeys() && m.privateClient == nil {
		m.addPrivateClient()
	}

	return m.addPublicClient()
}

// Listen accepts a callback func that will get called each time mux
// receives a message from any of its clients/subscriptions. It
// should be called last, after all setup calls are made
func (m *Mux) Listen(cb func(interface{}, error)) error {
	if m.Err != nil {
		return m.Err
	}

	for {
		select {
		case ms, ok := <-m.publicChan:
			if !ok {
				return errors.New("channel has closed unexpectedly")
			}
			if ms.Err != nil {
				cb(nil, fmt.Errorf("conn:%d has failed | err:%s | reconnecting", ms.CID, ms.Err))
				m.resetPublicClient(ms.CID)
				continue
			}
			// return raw payload data if transform is off
			if !m.transform {
				cb(ms.Data, nil)
				continue
			}
			// handle event type message
			if ms.IsEvent() {
				cb(m.handleEvent(ms.ProcessEvent()))
				continue
			}
			// handle data type message
			if ms.IsRaw() {
				cb(ms.ProcessRaw(m.subInfo))
				continue
			}
			cb(nil, fmt.Errorf("unrecognized msg signature: %s", ms.Data))
		case ms, ok := <-m.privateChan:
			if !ok {
				return errors.New("private channel has closed unexpectedly")
			}
			if ms.Err != nil {
				cb(nil, fmt.Errorf("err: %s | reconnecting", ms.Err))
				m.resetPrivateClient()
				continue
			}
			// return raw payload data if transform is off
			if !m.transform {
				cb(ms.Data, nil)
				continue
			}
			// handle event type message
			if ms.IsEvent() {
				cb(m.handleEvent(ms.ProcessEvent()))
				continue
			}
			// handle data type message
			if ms.IsRaw() {
				cb(ms.ProcessPrivateRaw())
				continue
			}
			cb(nil, fmt.Errorf("unrecognized msg signature: %s", ms.Data))
		}
	}
}

// Send meant for authenticated input, takes payload in form of interface
// and calls client with it
func (m *Mux) Send(pld interface{}) error {
	if !m.authenticated || m.privateClient == nil {
		return errors.New("not authorized")
	}
	return m.privateClient.Send(pld)
}

func (m *Mux) hasAPIKeys() bool {
	return len(m.apikey) != 0 && len(m.apisec) != 0
}

func (m *Mux) handleEvent(i event.Info, err error) (event.Info, error) {
	switch i.Event {
	case "subscribed":
		m.subInfo[i.ChanID] = i
	case "auth":
		if i.Status == "OK" {
			m.subInfo[i.ChanID] = i
			m.authenticated = true
		}
	}
	// add more cases if/when needed
	return i, err
}

func (m *Mux) resetPublicClient(cid int) {
	m.mtx.Lock()
	defer m.mtx.Unlock()
	// pull old client subscriptions
	subs := m.publicClients[cid].Subs.GetAll()
	// add fresh client
	m.addPublicClient()
	// resubscribe old events
	for _, sub := range subs {
		log.Printf("resubscribing: %+v\n", sub)
		m.Subscribe(sub)
	}
	// remove old, closed channel from the list
	delete(m.publicClients, cid)
}

func (m *Mux) resetPrivateClient() {
	m.mtx.Lock()
	defer m.mtx.Unlock()
	m.privateClient = nil
	m.addPrivateClient()
}

func (m *Mux) addPublicClient() *Mux {
	// adding new client so making sure we increment cid
	m.cid++
	// create new public client and pass error to mux if any
	c := client.New(m.cid).Public()
	if c.Err != nil {
		m.Err = c.Err
		return m
	}
	// add new client to list for later reference
	m.publicClients[m.cid] = c
	// start listening for incoming client messages
	go c.Read(m.publicChan)
	return m
}

func (m *Mux) addPrivateClient() *Mux {
	// create new private client and pass error to mux if any
	c := client.New(0).Private(m.apikey, m.apisec)
	if c.Err != nil {
		m.Err = c.Err
		return m
	}

	m.privateClient = c
	go c.Read(m.privateChan)
	return m
}
<|endoftext|>


<commit_before>package sse

import (
	"bufio"
	"bytes"
	"encoding/json"
	"io"
	"net/http"
	"sync"
)

type SSEWriter interface {
	Write([]byte) (int, error)
	Flush()
}

func NewSSEWriter(w io.Writer) SSEWriter {
	return &Writer{Writer: w}
}

type Writer struct {
	io.Writer
	sync.Mutex
}

func (w *Writer) Write(p []byte) (int, error) {
	w.Lock()
	defer w.Unlock()

	if _, err := w.Writer.Write([]byte("data: ")); err != nil {
		return 0, err
	}
	if _, err := w.Writer.Write(p); err != nil {
		return 0, err
	}
	_, err := w.Writer.Write([]byte("\n\n"))
	return len(p), err
}

func (w *Writer) Flush() {
	if fw, ok := w.Writer.(http.Flusher); ok {
		fw.Flush()
	}
}

type Reader struct {
	*bufio.Reader
}

func (r *Reader) Read() ([]byte, error) {
	for {
		line, err := r.ReadBytes('\n')
		if err != nil {
			return nil, err
		}
		if bytes.HasPrefix(line, []byte("data: ")) {
			data := bytes.TrimPrefix(line, []byte("data: "))
			return data, nil
		}
	}
}

type Decoder struct {
	*Reader
}

func NewDecoder(r *bufio.Reader) *Decoder {
	return &Decoder{&Reader{r}}
}

// Decode finds the next "data" field and decodes it into v
func (dec *Decoder) Decode(v interface{}) error {
	data, err := dec.Reader.Read()
	if err != nil {
		return err
	}
	return json.Unmarshal(data, v)
}
<commit_msg>sse: Trim trailing newline from the received data.<commit_after>package sse

import (
	"bufio"
	"bytes"
	"encoding/json"
	"io"
	"net/http"
	"sync"
)

type SSEWriter interface {
	Write([]byte) (int, error)
	Flush()
}

func NewSSEWriter(w io.Writer) SSEWriter {
	return &Writer{Writer: w}
}

type Writer struct {
	io.Writer
	sync.Mutex
}

func (w *Writer) Write(p []byte) (int, error) {
	w.Lock()
	defer w.Unlock()

	if _, err := w.Writer.Write([]byte("data: ")); err != nil {
		return 0, err
	}
	if _, err := w.Writer.Write(p); err != nil {
		return 0, err
	}
	_, err := w.Writer.Write([]byte("\n\n"))
	return len(p), err
}

func (w *Writer) Flush() {
	if fw, ok := w.Writer.(http.Flusher); ok {
		fw.Flush()
	}
}

type Reader struct {
	*bufio.Reader
}

func (r *Reader) Read() ([]byte, error) {
	for {
		line, err := r.ReadBytes('\n')
		if err != nil {
			return nil, err
		}
		if bytes.HasPrefix(line, []byte("data: ")) {
			data := bytes.TrimSuffix(bytes.TrimPrefix(line, []byte("data: ")), []byte("\n"))
			return data, nil
		}
	}
}

type Decoder struct {
	*Reader
}

func NewDecoder(r *bufio.Reader) *Decoder {
	return &Decoder{&Reader{r}}
}

// Decode finds the next "data" field and decodes it into v
func (dec *Decoder) Decode(v interface{}) error {
	data, err := dec.Reader.Read()
	if err != nil {
		return err
	}
	return json.Unmarshal(data, v)
}
<|endoftext|>


<commit_before>package infrastructure

import (
	"database/sql"

	"github.com/GerardSoleCa/PubKeyManager/interfaces"
	// Blank import for go-sqlcipher
	_ "github.com/xeodou/go-sqlcipher"
)

// SqliteHandler struct
type SqliteHandler struct {
	Conn *sql.DB
}

// Execute function contained on SqliteHandler
func (handler *SqliteHandler) Execute(statement string, args ...interface{}) (interfaces.Result, error) {
	result, err := handler.Conn.Exec(statement, args...)
	if err != nil {
		return new(SqliteResult), err
	}
	r := new(SqliteResult)
	r.Result = result
	return r, nil
}

// Query function contained on SqliteHandler
func (handler *SqliteHandler) Query(statement string, args ...interface{}) (interfaces.Row, error) {
	rows, err := handler.Conn.Query(statement, args...)
	if err != nil {
		return new(SqliteRow), err
	}
	row := new(SqliteRow)
	row.Rows = rows
	return row, nil
}

// SqliteResult struct
type SqliteResult struct {
	Result sql.Result
}

// LastInsertId function contained on SqliteResult
func (r SqliteResult) LastInsertId() (int64, error) {
	return r.Result.LastInsertId()
}

// RowsAffected function contained on SqliteResult
func (r SqliteResult) RowsAffected() (int64, error) {
	return r.Result.RowsAffected()
}

// SqliteRow struct
type SqliteRow struct {
	Rows *sql.Rows
}

// Scan function contained on SqliteResult
func (r SqliteRow) Scan(dest ...interface{}) {
	r.Rows.Scan(dest...)
}

// Next function contained on SqliteRow
func (r SqliteRow) Next() bool {
	return r.Rows.Next()
}
// Close function contained on SqliteRow
func (r SqliteRow) Close() {
	r.Rows.Close()
}

// NewSqliteHanlder creates a new Ciphered SqliteHandler
func NewSqliteHandler(dbfileName string, password string) *SqliteHandler {
	conn, _ := sql.Open("sqlite3", dbfileName)

	p := "PRAGMA key = '" + password + "';"
	_, err := conn.Exec(p)
	if err != nil {
		panic(err)
	}

	sqliteHandler := new(SqliteHandler)
	sqliteHandler.Conn = conn
	return sqliteHandler
}
<commit_msg>Linting files<commit_after>package infrastructure

import (
	"database/sql"

	"github.com/GerardSoleCa/PubKeyManager/interfaces"
	// Blank import for go-sqlcipher
	_ "github.com/xeodou/go-sqlcipher"
)

// SqliteHandler struct
type SqliteHandler struct {
	Conn *sql.DB
}

// Execute function contained on SqliteHandler
func (handler *SqliteHandler) Execute(statement string, args ...interface{}) (interfaces.Result, error) {
	result, err := handler.Conn.Exec(statement, args...)
	if err != nil {
		return new(SqliteResult), err
	}
	r := new(SqliteResult)
	r.Result = result
	return r, nil
}

// Query function contained on SqliteHandler
func (handler *SqliteHandler) Query(statement string, args ...interface{}) (interfaces.Row, error) {
	rows, err := handler.Conn.Query(statement, args...)
	if err != nil {
		return new(SqliteRow), err
	}
	row := new(SqliteRow)
	row.Rows = rows
	return row, nil
}

// SqliteResult struct
type SqliteResult struct {
	Result sql.Result
}

// LastInsertId function contained on SqliteResult
func (r SqliteResult) LastInsertId() (int64, error) {
	return r.Result.LastInsertId()
}

// RowsAffected function contained on SqliteResult
func (r SqliteResult) RowsAffected() (int64, error) {
	return r.Result.RowsAffected()
}

// SqliteRow struct
type SqliteRow struct {
	Rows *sql.Rows
}

// Scan function contained on SqliteResult
func (r SqliteRow) Scan(dest ...interface{}) {
	r.Rows.Scan(dest...)
}

// Next function contained on SqliteRow
func (r SqliteRow) Next() bool {
	return r.Rows.Next()
}

// Close function contained on SqliteRow
func (r SqliteRow) Close() {
	r.Rows.Close()
}

// NewSqliteHanlder creates a new Ciphered SqliteHandler
func NewSqliteHandler(dbfileName string, password string) *SqliteHandler {
	conn, _ := sql.Open("sqlite3", dbfileName)

	p := "PRAGMA key = '" + password + "';"
	_, err := conn.Exec(p)
	if err != nil {
		panic(err)
	}

	sqliteHandler := new(SqliteHandler)
	sqliteHandler.Conn = conn
	return sqliteHandler
}
<|endoftext|>


<commit_before>package aws

// AWS converts AWS related config.
type AWS struct {
	AssetsS3BucketName        string `json:"tectonic_aws_assets_s3_bucket_name,omitempty" yaml:"assetsS3BucketName,omitempty"`
	AutoScalingGroupExtraTags string `json:"tectonic_autoscaling_group_extra_tags,omitempty" yaml:"autoScalingGroupExtraTags,omitempty"`
	EC2AMIOverride            string `json:"tectonic_aws_ec2_ami_override,omitempty" yaml:"ec2AMIOverride,omitempty"`
	Etcd                      `json:",inline" yaml:"etcd,omitempty"`
	External                  `json:",inline" yaml:"external,omitempty"`
	ExtraTags                 string `json:"tectonic_aws_extra_tags,omitempty" yaml:"extraTags,omitempty"`
	InstallerRole             string `json:"tectonic_aws_installer_role,omitempty" yaml:"installerRole,omitempty"`
	Master                    `json:",inline" yaml:"master,omitempty"`
	PrivateEndpoints          bool   `json:"tectonic_aws_private_endpoints,omitempty" yaml:"privateEndpoints,omitempty"`
	Profile                   string `json:"tectonic_aws_profile,omitempty" yaml:"profile,omitempty"`
	PublicEndpoints           bool   `json:"tectonic_aws_public_endpoints,omitempty" yaml:"publicEndpoints,omitempty"`
	Region                    string `json:"tectonic_aws_region,omitempty" yaml:"region,omitempty"`
	SSHKey                    string `json:"tectonic_aws_ssh_key,omitempty" yaml:"sshKey,omitempty"`
	VPCCIDRBlock              string `json:"tectonic_aws_vpc_cidr_block,omitempty" yaml:"vpcCIDRBlock,omitempty"`
	Worker                    `json:",inline" yaml:"worker,omitempty"`
}

// External converts external related config.
type External struct {
	MasterSubnetIDs string `json:"tectonic_aws_external_master_subnet_ids,omitempty" yaml:"masterSubnetIDs,omitempty"`
	PrivateZone     string `json:"tectonic_aws_external_private_zone,omitempty" yaml:"privateZone,omitempty"`
	VPCID           string `json:"tectonic_aws_external_vpc_id,omitempty" yaml:"vpcID,omitempty"`
	WorkerSubnetIDs string `json:"tectonic_aws_external_worker_subnet_ids,omitempty" yaml:"workerSubnetIDs,omitempty"`
}

// Etcd converts etcd related config.
type Etcd struct {
	EC2Type        string `json:"tectonic_aws_etcd_ec2_type,omitempty" yaml:"ec2Type,omitempty"`
	ExtraSGIDs     string `json:"tectonic_aws_etcd_extra_sg_ids,omitempty" yaml:"extraSGIDs,omitempty"`
	IAMRoleName    string `json:"tectonic_aws_etcd_iam_role_name,omitempty" yaml:"iamRoleName,omitempty"`
	EtcdRootVolume `json:",inline" yaml:"rootVolume,omitempty"`
}

// EtcdRootVolume converts etcd rool volume related config.
type EtcdRootVolume struct {
	IOPS int    `json:"tectonic_aws_etcd_root_volume_iops,omitempty" yaml:"iops,omitempty"`
	Size int    `json:"tectonic_aws_etcd_root_volume_size,omitempty" yaml:"size,omitempty"`
	Type string `json:"tectonic_aws_etcd_root_volume_type,omitempty" yaml:"type,omitempty"`
}

// Master converts master related config.
type Master struct {
	CustomSubnets    string `json:"tectonic_aws_master_custom_subnets,omitempty" yaml:"customSubnets,omitempty"`
	EC2Type          string `json:"tectonic_aws_master_ec2_type,omitempty" yaml:"ec2Type,omitempty"`
	ExtraSGIDs       string `json:"tectonic_aws_master_extra_sg_ids,omitempty" yaml:"extraSGIDs,omitempty"`
	IAMRoleName      string `json:"tectonic_aws_master_iam_role_name,omitempty" yaml:"iamRoleName,omitempty"`
	MasterRootVolume `json:",inline" yaml:"rootVolume,omitempty"`
}

// MasterRootVolume converts master rool volume related config.
type MasterRootVolume struct {
	IOPS int    `json:"tectonic_aws_master_root_volume_iops,omitempty" yaml:"iops,omitempty"`
	Size int    `json:"tectonic_aws_master_root_volume_size,omitempty" yaml:"size,omitempty"`
	Type string `json:"tectonic_aws_master_root_volume_type,omitempty" yaml:"type,omitempty"`
}

// Worker converts worker related config.
type Worker struct {
	CustomSubnets    string `json:"tectonic_aws_worker_custom_subnets,omitempty" yaml:"customSubnets,omitempty"`
	EC2Type          string `json:"tectonic_aws_worker_ec2_type,omitempty" yaml:"ec2Type,omitempty"`
	ExtraSGIDs       string `json:"tectonic_aws_worker_extra_sg_ids,omitempty" yaml:"extraSGIDs,omitempty"`
	IAMRoleName      string `json:"tectonic_aws_worker_iam_role_name,omitempty" yaml:"iamRoleName,omitempty"`
	LoadBalancers    string `json:"tectonic_aws_worker_load_balancers,omitempty" yaml:"loadBalancers,omitempty"`
	WorkerRootVolume `json:",inline" yaml:"rootVolume,omitempty"`
}

// WorkerRootVolume converts worker rool volume related config.
type WorkerRootVolume struct {
	IOPS int    `json:"tectonic_aws_worker_root_volume_iops,omitempty" yaml:"iops,omitempty"`
	Size int    `json:"tectonic_aws_worker_root_volume_size,omitempty" yaml:"size,omitempty"`
	Type string `json:"tectonic_aws_worker_root_volume_type,omitempty" yaml:"type,omitempty"`
}
<commit_msg>installer: Fix the plural definition of the aws struct.<commit_after>package aws

// AWS converts AWS related config.
type AWS struct {
	AssetsS3BucketName        string              `json:"tectonic_aws_assets_s3_bucket_name,omitempty" yaml:"assetsS3BucketName,omitempty"`
	AutoScalingGroupExtraTags []map[string]string `json:"tectonic_autoscaling_group_extra_tags,omitempty" yaml:"autoScalingGroupExtraTags,omitempty"`
	EC2AMIOverride            string              `json:"tectonic_aws_ec2_ami_override,omitempty" yaml:"ec2AMIOverride,omitempty"`
	Etcd                      `json:",inline" yaml:"etcd,omitempty"`
	External                  `json:",inline" yaml:"external,omitempty"`
	ExtraTags                 map[string]string `json:"tectonic_aws_extra_tags,omitempty" yaml:"extraTags,omitempty"`
	InstallerRole             string            `json:"tectonic_aws_installer_role,omitempty" yaml:"installerRole,omitempty"`
	Master                    `json:",inline" yaml:"master,omitempty"`
	PrivateEndpoints          bool   `json:"tectonic_aws_private_endpoints,omitempty" yaml:"privateEndpoints,omitempty"`
	Profile                   string `json:"tectonic_aws_profile,omitempty" yaml:"profile,omitempty"`
	PublicEndpoints           bool   `json:"tectonic_aws_public_endpoints,omitempty" yaml:"publicEndpoints,omitempty"`
	Region                    string `json:"tectonic_aws_region,omitempty" yaml:"region,omitempty"`
	SSHKey                    string `json:"tectonic_aws_ssh_key,omitempty" yaml:"sshKey,omitempty"`
	VPCCIDRBlock              string `json:"tectonic_aws_vpc_cidr_block,omitempty" yaml:"vpcCIDRBlock,omitempty"`
	Worker                    `json:",inline" yaml:"worker,omitempty"`
}

// External converts external related config.
type External struct {
	MasterSubnetIDs []string `json:"tectonic_aws_external_master_subnet_ids,omitempty" yaml:"masterSubnetIDs,omitempty"`
	PrivateZone     string   `json:"tectonic_aws_external_private_zone,omitempty" yaml:"privateZone,omitempty"`
	VPCID           string   `json:"tectonic_aws_external_vpc_id,omitempty" yaml:"vpcID,omitempty"`
	WorkerSubnetIDs []string `json:"tectonic_aws_external_worker_subnet_ids,omitempty" yaml:"workerSubnetIDs,omitempty"`
}

// Etcd converts etcd related config.
type Etcd struct {
	EC2Type        string   `json:"tectonic_aws_etcd_ec2_type,omitempty" yaml:"ec2Type,omitempty"`
	ExtraSGIDs     []string `json:"tectonic_aws_etcd_extra_sg_ids,omitempty" yaml:"extraSGIDs,omitempty"`
	IAMRoleName    string   `json:"tectonic_aws_etcd_iam_role_name,omitempty" yaml:"iamRoleName,omitempty"`
	EtcdRootVolume `json:",inline" yaml:"rootVolume,omitempty"`
}

// EtcdRootVolume converts etcd rool volume related config.
type EtcdRootVolume struct {
	IOPS int    `json:"tectonic_aws_etcd_root_volume_iops,omitempty" yaml:"iops,omitempty"`
	Size int    `json:"tectonic_aws_etcd_root_volume_size,omitempty" yaml:"size,omitempty"`
	Type string `json:"tectonic_aws_etcd_root_volume_type,omitempty" yaml:"type,omitempty"`
}

// Master converts master related config.
type Master struct {
	CustomSubnets    map[string]string `json:"tectonic_aws_master_custom_subnets,omitempty" yaml:"customSubnets,omitempty"`
	EC2Type          string            `json:"tectonic_aws_master_ec2_type,omitempty" yaml:"ec2Type,omitempty"`
	ExtraSGIDs       []string          `json:"tectonic_aws_master_extra_sg_ids,omitempty" yaml:"extraSGIDs,omitempty"`
	IAMRoleName      string            `json:"tectonic_aws_master_iam_role_name,omitempty" yaml:"iamRoleName,omitempty"`
	MasterRootVolume `json:",inline" yaml:"rootVolume,omitempty"`
}

// MasterRootVolume converts master rool volume related config.
type MasterRootVolume struct {
	IOPS int    `json:"tectonic_aws_master_root_volume_iops,omitempty" yaml:"iops,omitempty"`
	Size int    `json:"tectonic_aws_master_root_volume_size,omitempty" yaml:"size,omitempty"`
	Type string `json:"tectonic_aws_master_root_volume_type,omitempty" yaml:"type,omitempty"`
}

// Worker converts worker related config.
type Worker struct {
	CustomSubnets    map[string]string `json:"tectonic_aws_worker_custom_subnets,omitempty" yaml:"customSubnets,omitempty"`
	EC2Type          string            `json:"tectonic_aws_worker_ec2_type,omitempty" yaml:"ec2Type,omitempty"`
	ExtraSGIDs       []string          `json:"tectonic_aws_worker_extra_sg_ids,omitempty" yaml:"extraSGIDs,omitempty"`
	IAMRoleName      string            `json:"tectonic_aws_worker_iam_role_name,omitempty" yaml:"iamRoleName,omitempty"`
	LoadBalancers    []string          `json:"tectonic_aws_worker_load_balancers,omitempty" yaml:"loadBalancers,omitempty"`
	WorkerRootVolume `json:",inline" yaml:"rootVolume,omitempty"`
}

// WorkerRootVolume converts worker rool volume related config.
type WorkerRootVolume struct {
	IOPS int    `json:"tectonic_aws_worker_root_volume_iops,omitempty" yaml:"iops,omitempty"`
	Size int    `json:"tectonic_aws_worker_root_volume_size,omitempty" yaml:"size,omitempty"`
	Type string `json:"tectonic_aws_worker_root_volume_type,omitempty" yaml:"type,omitempty"`
}
<|endoftext|>


<commit_before>package integration

import (
	"fmt"
	"testing"

	"github.com/apex/log"
	"github.com/apex/log/handlers/cli"
	"github.com/aws/aws-sdk-go/aws"
	"github.com/stretchr/testify/assert"
	"github.com/versent/unicreds"
)

func init() {
	log.SetHandler(cli.Default)
	log.SetLevel(log.DebugLevel)
}

func TestIntegrationGetSecret(t *testing.T) {

	var err error

	unicreds.SetRegion(aws.String("us-west-2"))

	for i := 0; i < 15; i++ {
		err = unicreds.PutSecret(aws.String("credential-store"), "alias/accounting", "Integration1", "secret1", fmt.Sprintf("%d", i))
		if err != nil {
			log.Errorf("put err: %v", err)
		}

		assert.Nil(t, err)
	}

	cred, err := unicreds.GetSecret(aws.String("credential-store"), "Integration1")
	assert.Nil(t, err)
	assert.Equal(t, cred.Name, "Integration1")
	assert.Equal(t, cred.Secret, "secret1")

	creds, err := unicreds.GetAllSecrets(aws.String("credential-store"), true)
	assert.Nil(t, err)
	assert.Len(t, creds, 24)

	err = unicreds.DeleteSecret(aws.String("credential-store"), "Integration1")
	assert.Nil(t, err)

}
<commit_msg>Restore test integration flag.<commit_after>// +build integration

package integration

import (
	"fmt"
	"testing"

	"github.com/apex/log"
	"github.com/apex/log/handlers/cli"
	"github.com/aws/aws-sdk-go/aws"
	"github.com/stretchr/testify/assert"
	"github.com/versent/unicreds"
)

func init() {
	log.SetHandler(cli.Default)
	log.SetLevel(log.DebugLevel)
}

func TestIntegrationGetSecret(t *testing.T) {

	var err error

	unicreds.SetRegion(aws.String("us-west-2"))

	for i := 0; i < 15; i++ {
		err = unicreds.PutSecret(aws.String("credential-store"), "alias/accounting", "Integration1", "secret1", fmt.Sprintf("%d", i))
		if err != nil {
			log.Errorf("put err: %v", err)
		}

		assert.Nil(t, err)
	}

	cred, err := unicreds.GetSecret(aws.String("credential-store"), "Integration1")
	assert.Nil(t, err)
	assert.Equal(t, cred.Name, "Integration1")
	assert.Equal(t, cred.Secret, "secret1")

	creds, err := unicreds.GetAllSecrets(aws.String("credential-store"), true)
	assert.Nil(t, err)
	assert.Len(t, creds, 24)

	err = unicreds.DeleteSecret(aws.String("credential-store"), "Integration1")
	assert.Nil(t, err)

}
<|endoftext|>