<commit_before>package client

import (
	"bytes"
	"encoding/base64"
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"mime/multipart"
	"net/http"
	"net/url"
	"strconv"
	"strings"
	"sync"

	"github.com/tsileo/blobstash/client/response"
)

// TODO(tsileo): RESHAPE this! with doReq and simpler API (especially for the blob upload).

// ErrBlobNotFound is returned from a get/stat request
// if the blob does not exist.
var ErrBlobNotFound = errors.New("blob not found")
var ErrKeyNotFound = errors.New("key doest not exist")

var defaultServerAddr = "http://localhost:8050"

type KvStore struct {
	ServerAddr string
	client     *http.Client
}

func NewKvStore(serverAddr string) *KvStore {
	if serverAddr == "" {
		serverAddr = defaultServerAddr
	}
	return &KvStore{
		ServerAddr: serverAddr,
		client:     &http.Client{},
	}
}

func (kvs *KvStore) Put(key, value string, version int) (*response.KeyValue, error) {
	data := url.Values{}
	data.Set("value", value)
	if version != -1 {
		data.Set("version", strconv.Itoa(version))
	}
	request, err := http.NewRequest("PUT", kvs.ServerAddr+"/api/v1/vkv/key/"+key, strings.NewReader(data.Encode())) //data.Encode()))
	if err != nil {
		return nil, err
	}
	resp, err := kvs.client.Do(request)
	if err != nil {
		return nil, err
	}
	var body bytes.Buffer
	body.ReadFrom(resp.Body)
	resp.Body.Close()
	switch resp.StatusCode {
	case 200:
		kv := &response.KeyValue{}
		if err := json.Unmarshal(body.Bytes(), kv); err != nil {
			return nil, err
		}
		return kv, nil
	default:
		return nil, fmt.Errorf("failed to put key %v: %v", key, body.String())
	}
}

func (kvs *KvStore) Get(key string, version int) (*response.KeyValue, error) {
	request, err := http.NewRequest("GET", kvs.ServerAddr+"/api/v1/vkv/key/"+key, nil)
	if err != nil {
		return nil, err
	}
	resp, err := kvs.client.Do(request)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return nil, err
	}
	switch {
	case resp.StatusCode == 200:
		kv := &response.KeyValue{}
		if err := json.Unmarshal(body, kv); err != nil {
			return nil, err
		}
		return kv, nil
	case resp.StatusCode == 404:
		return nil, ErrKeyNotFound
	default:
		return nil, fmt.Errorf("failed to get key %v: %v", key, string(body))
	}

}

func (kvs *KvStore) Versions(key string, start, end, limit int) (*response.KeyValueVersions, error) {
	// TODO handle start, end and limit
	request, err := http.NewRequest("GET", kvs.ServerAddr+"/api/v1/vkv/key/"+key+"/versions", nil)
	if err != nil {
		return nil, err
	}
	resp, err := kvs.client.Do(request)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return nil, err
	}
	switch {
	case resp.StatusCode == 200:
		kvversions := &response.KeyValueVersions{}
		if err := json.Unmarshal(body, kvversions); err != nil {
			return nil, err
		}
		return kvversions, nil
	case resp.StatusCode == 404:
		return nil, ErrBlobNotFound
	default:
		return nil, fmt.Errorf("failed to get key %v: %v", key, string(body))
	}
}

// NextKey returns the next key for lexigraphical (key = NextKey(lastkey))
func NextKey(key string) string {
	bkey := []byte(key)
	i := len(bkey)
	for i > 0 {
		i--
		bkey[i]++
		if bkey[i] != 0 {
			break
		}
	}
	return string(bkey)
}

func (kvs *KvStore) Keys(start, end string, limit int) ([]*response.KeyValue, error) {
	request, err := http.NewRequest("GET", kvs.ServerAddr+"/api/v1/vkv/keys?start="+start+"&end="+fmt.Sprintf("%v&limit=%d", end, limit), nil)
	if err != nil {
		return nil, err
	}
	resp, err := kvs.client.Do(request)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return nil, err
	}
	switch {
	case resp.StatusCode == 200:
		keys := &response.KeysResponse{}
		if err := json.Unmarshal(body, keys); err != nil {
			return nil, err
		}
		return keys.Keys, nil
	default:
		return nil, fmt.Errorf("failed to get keys: %v", string(body))
	}
}

type Blob struct {
	Hash string
	Blob string
}

type BlobStore struct {
	pipeline   bool
	wg         sync.WaitGroup
	stop       chan struct{}
	blobs      chan *Blob
	ServerAddr string
	client     *http.Client
}

func NewBlobStore(serverAddr string) *BlobStore {
	if serverAddr == "" {
		serverAddr = defaultServerAddr
	}
	return &BlobStore{
		ServerAddr: serverAddr,
		client:     &http.Client{},
		blobs:      make(chan *Blob),
		stop:       make(chan struct{}),
		pipeline:   false,
	}
}

// Get fetch the given blob.
func (bs *BlobStore) Get(hash string) ([]byte, error) {
	request, err := http.NewRequest("GET", bs.ServerAddr+"/api/v1/blobstore/blob/"+hash, nil)
	if err != nil {
		return nil, err
	}
	resp, err := bs.client.Do(request)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return nil, err
	}
	switch {
	case resp.StatusCode == 200:
		return body, nil
	case resp.StatusCode == 404:
		return nil, ErrBlobNotFound
	default:
		return nil, fmt.Errorf("failed to get blob %v: %v", hash, string(body))
	}
}

type BlobsResp struct {
	Blobs []string `json:"blobs"`
}

func (bs *BlobStore) Enumerate(blobs chan<- string, start, end string, limit int) error {
	request, err := http.NewRequest("GET", bs.ServerAddr+"/api/v1/blobstore/blobs?start="+start+"&end="+end+"&limit="+strconv.Itoa(limit), nil)
	if err != nil {
		return err
	}
	resp, err := bs.client.Do(request)
	if err != nil {
		return err
	}
	defer resp.Body.Close()
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return err
	}
	switch {
	case resp.StatusCode == 200:
		br := &BlobsResp{}
		if err := json.Unmarshal(body, br); err != nil {
			return err
		}
		for _, h := range br.Blobs {
			blobs <- h
		}
		return nil
	default:
		return fmt.Errorf("failed to enumerate blobs: %v", string(body))
	}
}

// Stat checks wether a blob exists or not.
func (bs *BlobStore) Stat(hash string) (bool, error) {
	request, err := http.NewRequest("HEAD", bs.ServerAddr+"/api/v1/blobstore/blob/"+hash, nil)
	if err != nil {
		return false, err
	}
	resp, err := bs.client.Do(request)
	if err != nil {
		return false, err
	}
	resp.Body.Close()
	switch {
	case resp.StatusCode == 200:
		return true, nil
	case resp.StatusCode == 404:
		return false, nil
	default:
		return false, fmt.Errorf("failed to put blob %v", hash)
	}
}

func (bs *BlobStore) WaitBlobs() {
	//close(bs.stop)
	bs.wg.Wait()
	close(bs.blobs)
}

func (bs *BlobStore) processBlobs() {
	//bs.wg.Add(1)
	//defer bs.wg.Done()
	//bb := NewBlobsBuffer(bs)
	//defer func() {
	//	bb.Upload()
	//	bb.Close()
	//}()
	for blob := range bs.blobs {
		//select {
		//case blob := <-bs.blobs:
		data, err := base64.StdEncoding.DecodeString(blob.Blob)
		if err != nil {
			panic(err)
		}
		//if err := bb.AddBlob(blob.Hash, data); err != nil {
		//	panic(err)
		//}
		//if bb.size >= 10 {
		//	if err := bb.Upload(); err != nil {
		//		panic(err)
		//	}
		//}
		//if mpw.Cnt >=
		if err := bs.put(blob.Hash, data); err != nil {
			panic(err)
		}
		bs.wg.Done()
		//case <-bs.stop:
		//	return
		//}
	}
}

func (bs *BlobStore) ProcessBlobs() {
	go func() {
		for i := 0; i < 15; i++ {
			go bs.processBlobs()
		}
	}()
	bs.pipeline = true
}

func (bs *BlobStore) Put(hash string, blob []byte) error {
	if bs.pipeline {
		bs.wg.Add(1)
		bs.blobs <- &Blob{Hash: hash, Blob: base64.StdEncoding.EncodeToString(blob)}
		return nil
	}
	return bs.put(hash, blob)
}

type MultipartWriter struct {
	Buffer *bytes.Buffer
	Writer *multipart.Writer
	Blobs  int
}

func NewMultipartWriter() *MultipartWriter {
	buf := &bytes.Buffer{}
	return &MultipartWriter{
		Buffer: buf,
		Writer: multipart.NewWriter(buf),
	}
}

func (mpw *MultipartWriter) Close() error {
	return mpw.Writer.Close()
}

func (mpw *MultipartWriter) FormDataContentType() string {
	return mpw.Writer.FormDataContentType()
}

func (mpw *MultipartWriter) AddBlob(hash string, blob []byte) error {
	part, err := mpw.Writer.CreateFormFile(hash, hash)
	if err != nil {
		return err
	}
	if _, err := part.Write(blob); err != nil {
		return err
	}
	mpw.Blobs++
	return nil
}

type BlobsBuffer struct {
	mpw  *MultipartWriter
	bs   *BlobStore
	size int
	sync.Mutex
}

func NewBlobsBuffer(bs *BlobStore) *BlobsBuffer {
	return &BlobsBuffer{
		bs:  bs,
		mpw: NewMultipartWriter(),
	}
}
func (bb *BlobsBuffer) AddBlob(hash string, blob []byte) error {
	bb.Lock()
	defer bb.Unlock()
	bb.size++
	return bb.mpw.AddBlob(hash, blob)
}

func (bb *BlobsBuffer) Upload() error {
	if bb.size > 0 {
		bb.Lock()
		cmpw := bb.mpw
		bb.size = 0
		bb.Unlock()
		bb.mpw = NewMultipartWriter()
		if err := bb.bs.putmpw(cmpw); err != nil {
			return err
		}
	}
	return nil
}

func (bb *BlobsBuffer) Close() {
}

// Put upload the given blob, the caller is responsible for computing the blake2b hash
func (bs *BlobStore) put(hash string, blob []byte) error {
	mpw := NewMultipartWriter()
	if err := mpw.AddBlob(hash, blob); err != nil {
		return err
	}
	return bs.putmpw(mpw)
}

func (bs *BlobStore) putmpw(mpw *MultipartWriter) error {
	mpw.Close()
	request, err := http.NewRequest("POST", bs.ServerAddr+"/api/v1/blobstore/upload", mpw.Buffer)
	if err != nil {
		return err
	}
	request.Header.Add("Content-Type", mpw.FormDataContentType())
	resp, err := bs.client.Do(request)
	if err != nil {
		return err
	}
	var body bytes.Buffer
	body.ReadFrom(resp.Body)
	resp.Body.Close()
	if resp.StatusCode != 200 {
		return fmt.Errorf("failed to put blob %v", body.String())
	}
	return nil
}
<commit_msg>client: start to support API key<commit_after>package client

import (
	"bytes"
	"encoding/base64"
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"mime/multipart"
	"net/http"
	"net/url"
	"strconv"
	"strings"
	"sync"

	"github.com/tsileo/blobstash/client/response"
)

// TODO(tsileo): RESHAPE this! with doReq and simpler API (especially for the blob upload).

// ErrBlobNotFound is returned from a get/stat request
// if the blob does not exist.
var ErrBlobNotFound = errors.New("blob not found")
var ErrKeyNotFound = errors.New("key doest not exist")

var defaultServerAddr = "http://localhost:8050"

type KvStore struct {
	ServerAddr string
	client     *http.Client
}

func NewKvStore(serverAddr string) *KvStore {
	if serverAddr == "" {
		serverAddr = defaultServerAddr
	}
	return &KvStore{
		ServerAddr: serverAddr,
		client:     &http.Client{},
	}
}

func (kvs *KvStore) Put(key, value string, version int) (*response.KeyValue, error) {
	data := url.Values{}
	data.Set("value", value)
	if version != -1 {
		data.Set("version", strconv.Itoa(version))
	}
	request, err := http.NewRequest("PUT", kvs.ServerAddr+"/api/v1/vkv/key/"+key, strings.NewReader(data.Encode())) //data.Encode()))
	if err != nil {
		return nil, err
	}
	resp, err := kvs.client.Do(request)
	if err != nil {
		return nil, err
	}
	var body bytes.Buffer
	body.ReadFrom(resp.Body)
	resp.Body.Close()
	switch resp.StatusCode {
	case 200:
		kv := &response.KeyValue{}
		if err := json.Unmarshal(body.Bytes(), kv); err != nil {
			return nil, err
		}
		return kv, nil
	default:
		return nil, fmt.Errorf("failed to put key %v: %v", key, body.String())
	}
}

func (kvs *KvStore) Get(key string, version int) (*response.KeyValue, error) {
	request, err := http.NewRequest("GET", kvs.ServerAddr+"/api/v1/vkv/key/"+key, nil)
	if err != nil {
		return nil, err
	}
	resp, err := kvs.client.Do(request)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return nil, err
	}
	switch {
	case resp.StatusCode == 200:
		kv := &response.KeyValue{}
		if err := json.Unmarshal(body, kv); err != nil {
			return nil, err
		}
		return kv, nil
	case resp.StatusCode == 404:
		return nil, ErrKeyNotFound
	default:
		return nil, fmt.Errorf("failed to get key %v: %v", key, string(body))
	}

}

func (kvs *KvStore) Versions(key string, start, end, limit int) (*response.KeyValueVersions, error) {
	// TODO handle start, end and limit
	request, err := http.NewRequest("GET", kvs.ServerAddr+"/api/v1/vkv/key/"+key+"/versions", nil)
	if err != nil {
		return nil, err
	}
	resp, err := kvs.client.Do(request)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return nil, err
	}
	switch {
	case resp.StatusCode == 200:
		kvversions := &response.KeyValueVersions{}
		if err := json.Unmarshal(body, kvversions); err != nil {
			return nil, err
		}
		return kvversions, nil
	case resp.StatusCode == 404:
		return nil, ErrBlobNotFound
	default:
		return nil, fmt.Errorf("failed to get key %v: %v", key, string(body))
	}
}

// NextKey returns the next key for lexigraphical (key = NextKey(lastkey))
func NextKey(key string) string {
	bkey := []byte(key)
	i := len(bkey)
	for i > 0 {
		i--
		bkey[i]++
		if bkey[i] != 0 {
			break
		}
	}
	return string(bkey)
}

func (kvs *KvStore) Keys(start, end string, limit int) ([]*response.KeyValue, error) {
	request, err := http.NewRequest("GET", kvs.ServerAddr+"/api/v1/vkv/keys?start="+start+"&end="+fmt.Sprintf("%v&limit=%d", end, limit), nil)
	if err != nil {
		return nil, err
	}
	resp, err := kvs.client.Do(request)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return nil, err
	}
	switch {
	case resp.StatusCode == 200:
		keys := &response.KeysResponse{}
		if err := json.Unmarshal(body, keys); err != nil {
			return nil, err
		}
		return keys.Keys, nil
	default:
		return nil, fmt.Errorf("failed to get keys: %v", string(body))
	}
}

type Blob struct {
	Hash string
	Blob string
}

type BlobStore struct {
	pipeline   bool
	wg         sync.WaitGroup
	stop       chan struct{}
	blobs      chan *Blob
	ServerAddr string
	client     *http.Client
	apiKey     string
}

func NewBlobStore(serverAddr string) *BlobStore {
	if serverAddr == "" {
		serverAddr = defaultServerAddr
	}
	return &BlobStore{
		ServerAddr: serverAddr,
		client:     &http.Client{},
		blobs:      make(chan *Blob),
		stop:       make(chan struct{}),
		pipeline:   false,
	}
}
func (bs *BlobStore) SetAPIKey(apiKey string) {
	bs.apiKey = apiKey
}

// Get fetch the given blob.
func (bs *BlobStore) Get(hash string) ([]byte, error) {
	request, err := http.NewRequest("GET", bs.ServerAddr+"/api/v1/blobstore/blob/"+hash, nil)
	if err != nil {
		return nil, err
	}
	if bs.apiKey != "" {
		request.SetBasicAuth("", bs.apiKey)
	}
	resp, err := bs.client.Do(request)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return nil, err
	}
	switch {
	case resp.StatusCode == 200:
		return body, nil
	case resp.StatusCode == 404:
		return nil, ErrBlobNotFound
	default:
		return nil, fmt.Errorf("failed to get blob %v: %v", hash, string(body))
	}
}

type BlobsResp struct {
	Blobs []string `json:"blobs"`
}

func (bs *BlobStore) Enumerate(blobs chan<- string, start, end string, limit int) error {
	request, err := http.NewRequest("GET", bs.ServerAddr+"/api/v1/blobstore/blobs?start="+start+"&end="+end+"&limit="+strconv.Itoa(limit), nil)
	if err != nil {
		return err
	}
	if bs.apiKey != "" {
		request.SetBasicAuth("", bs.apiKey)
	}
	resp, err := bs.client.Do(request)
	if err != nil {
		return err
	}
	defer resp.Body.Close()
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return err
	}
	switch {
	case resp.StatusCode == 200:
		br := &BlobsResp{}
		if err := json.Unmarshal(body, br); err != nil {
			return err
		}
		for _, h := range br.Blobs {
			blobs <- h
		}
		return nil
	default:
		return fmt.Errorf("failed to enumerate blobs: %v", string(body))
	}
}

// Stat checks wether a blob exists or not.
func (bs *BlobStore) Stat(hash string) (bool, error) {
	request, err := http.NewRequest("HEAD", bs.ServerAddr+"/api/v1/blobstore/blob/"+hash, nil)
	if err != nil {
		return false, err
	}
	if bs.apiKey != "" {
		request.SetBasicAuth("", bs.apiKey)
	}
	resp, err := bs.client.Do(request)
	if err != nil {
		return false, err
	}
	resp.Body.Close()
	switch {
	case resp.StatusCode == 200:
		return true, nil
	case resp.StatusCode == 404:
		return false, nil
	default:
		return false, fmt.Errorf("failed to put blob %v", hash)
	}
}

func (bs *BlobStore) WaitBlobs() {
	//close(bs.stop)
	bs.wg.Wait()
	close(bs.blobs)
}

func (bs *BlobStore) processBlobs() {
	//bs.wg.Add(1)
	//defer bs.wg.Done()
	//bb := NewBlobsBuffer(bs)
	//defer func() {
	//	bb.Upload()
	//	bb.Close()
	//}()
	for blob := range bs.blobs {
		//select {
		//case blob := <-bs.blobs:
		data, err := base64.StdEncoding.DecodeString(blob.Blob)
		if err != nil {
			panic(err)
		}
		//if err := bb.AddBlob(blob.Hash, data); err != nil {
		//	panic(err)
		//}
		//if bb.size >= 10 {
		//	if err := bb.Upload(); err != nil {
		//		panic(err)
		//	}
		//}
		//if mpw.Cnt >=
		if err := bs.put(blob.Hash, data); err != nil {
			panic(err)
		}
		bs.wg.Done()
		//case <-bs.stop:
		//	return
		//}
	}
}

func (bs *BlobStore) ProcessBlobs() {
	go func() {
		for i := 0; i < 15; i++ {
			go bs.processBlobs()
		}
	}()
	bs.pipeline = true
}

func (bs *BlobStore) Put(hash string, blob []byte) error {
	if bs.pipeline {
		bs.wg.Add(1)
		bs.blobs <- &Blob{Hash: hash, Blob: base64.StdEncoding.EncodeToString(blob)}
		return nil
	}
	return bs.put(hash, blob)
}

type MultipartWriter struct {
	Buffer *bytes.Buffer
	Writer *multipart.Writer
	Blobs  int
}

func NewMultipartWriter() *MultipartWriter {
	buf := &bytes.Buffer{}
	return &MultipartWriter{
		Buffer: buf,
		Writer: multipart.NewWriter(buf),
	}
}

func (mpw *MultipartWriter) Close() error {
	return mpw.Writer.Close()
}

func (mpw *MultipartWriter) FormDataContentType() string {
	return mpw.Writer.FormDataContentType()
}

func (mpw *MultipartWriter) AddBlob(hash string, blob []byte) error {
	part, err := mpw.Writer.CreateFormFile(hash, hash)
	if err != nil {
		return err
	}
	if _, err := part.Write(blob); err != nil {
		return err
	}
	mpw.Blobs++
	return nil
}

type BlobsBuffer struct {
	mpw  *MultipartWriter
	bs   *BlobStore
	size int
	sync.Mutex
}

func NewBlobsBuffer(bs *BlobStore) *BlobsBuffer {
	return &BlobsBuffer{
		bs:  bs,
		mpw: NewMultipartWriter(),
	}
}
func (bb *BlobsBuffer) AddBlob(hash string, blob []byte) error {
	bb.Lock()
	defer bb.Unlock()
	bb.size++
	return bb.mpw.AddBlob(hash, blob)
}

func (bb *BlobsBuffer) Upload() error {
	if bb.size > 0 {
		bb.Lock()
		cmpw := bb.mpw
		bb.size = 0
		bb.Unlock()
		bb.mpw = NewMultipartWriter()
		if err := bb.bs.putmpw(cmpw); err != nil {
			return err
		}
	}
	return nil
}

func (bb *BlobsBuffer) Close() {
}

// Put upload the given blob, the caller is responsible for computing the blake2b hash
func (bs *BlobStore) put(hash string, blob []byte) error {
	mpw := NewMultipartWriter()
	if err := mpw.AddBlob(hash, blob); err != nil {
		return err
	}
	return bs.putmpw(mpw)
}

func (bs *BlobStore) putmpw(mpw *MultipartWriter) error {
	mpw.Close()
	// FIXME(tsileo): support namespace
	request, err := http.NewRequest("POST", bs.ServerAddr+"/api/v1/blobstore/upload", mpw.Buffer)
	if err != nil {
		return err
	}
	if bs.apiKey != "" {
		request.SetBasicAuth("", bs.apiKey)
	}
	request.Header.Add("Content-Type", mpw.FormDataContentType())
	resp, err := bs.client.Do(request)
	if err != nil {
		return err
	}
	var body bytes.Buffer
	body.ReadFrom(resp.Body)
	resp.Body.Close()
	if resp.StatusCode != 200 {
		return fmt.Errorf("failed to put blob %v", body.String())
	}
	return nil
}
<|endoftext|>


<commit_before>package client

import (
	"io"
	"net/textproto"
	"os"
	"strings"
	"time"
)

type KeyValueMap map[string]string

func list(conn *textproto.Conn) []string {
	id, err := conn.Cmd("list")
	values := make([]string, 0, 16)
	if err != nil {
		panic("error in list connection err is " + err.Error())
		return values
	}
	conn.StartResponse(id)
	for line, err := conn.ReadLine(); err == nil && len(line) > 0; line, err = conn.ReadLine() {
		if line[0] == '#' {
			continue
		}
		values = append(values, strings.Fields(line)...)
		break
	}
	conn.EndResponse(id)
	if err != nil {
		panic("error in list readline err is " + err.Error())
	}
	return values
}

func pair(input, sep string) (string, string) {
	both := strings.SplitN(input, sep, 2)
	return both[0], both[1]
}

func fetch(conn *textproto.Conn, what string) (values KeyValueMap) {
	id, err := conn.Cmd("fetch %s", what)
	values = make(KeyValueMap)
	if err != nil {
		panic("error in fetch connection for " + what + ", err is " + err.Error())
		return values
	}

	conn.StartResponse(id)
	dotlines, err := conn.ReadDotLines()
	conn.EndResponse(id)
	if err != nil {
		panic("error in fetch dotlines for " + what + ", err is " + err.Error())
		return values
	}
	for _, line := range dotlines {
		if line[0] == '.' {
			break
		}
		if line[0] == '#' {
			continue
		}
		key, value := pair(line, " ")
		key, _ = pair(key, ".")
		values[key] = value
	}
	return values
}

func NewMuninClient(connection io.ReadWriteCloser, interval time.Duration, done <-chan os.Signal) <-chan KeyValueMap {
	conn := textproto.NewConn(connection)
	data := make(chan KeyValueMap, 1)
	go func() {
		ticker := time.NewTicker(interval)
		for {
			kv := make(KeyValueMap)
			select {
			case <-ticker.C:
				headers := list(conn)
				for _, prefix := range headers {
					for key, value := range fetch(conn, prefix) {
						kv[prefix+"."+key] = value
					}
				}
				data <- kv
			case <-done:
				ticker.Stop()
				close(data)
				conn.Close()
				return
			}
		}
	}()
	return data
}
<commit_msg>handle initial banner<commit_after>package client

import (
	"io"
	"net/textproto"
	"os"
	"strings"
	"time"
)

type KeyValueMap map[string]string

func list(conn *textproto.Conn) []string {
	id, err := conn.Cmd("list")
	values := make([]string, 0, 16)
	if err != nil {
		panic("error in list connection err is " + err.Error())
		return values
	}
	conn.StartResponse(id)
	for line, err := conn.ReadLine(); err == nil && len(line) > 0; line, err = conn.ReadLine() {
		if line[0] == '#' {
			continue
		}
		values = append(values, strings.Fields(line)...)
		break
	}
	conn.EndResponse(id)
	if err != nil {
		panic("error in list readline err is " + err.Error())
	}
	return values
}

func pair(input, sep string) (string, string) {
	both := strings.SplitN(input, sep, 2)
	return both[0], both[1]
}

func fetch(conn *textproto.Conn, what string) (values KeyValueMap) {
	id, err := conn.Cmd("fetch %s", what)
	values = make(KeyValueMap)
	if err != nil {
		panic("error in fetch connection for " + what + ", err is " + err.Error())
		return values
	}

	conn.StartResponse(id)
	dotlines, err := conn.ReadDotLines()
	conn.EndResponse(id)
	if err != nil {
		panic("error in fetch dotlines for " + what + ", err is " + err.Error())
		return values
	}
	for _, line := range dotlines {
		if line[0] == '.' {
			break
		}
		if line[0] == '#' {
			continue
		}
		key, value := pair(line, " ")
		key, _ = pair(key, ".")
		values[key] = value
	}
	return values
}

func NewMuninClient(connection io.ReadWriteCloser, interval time.Duration, done <-chan os.Signal) <-chan KeyValueMap {
	conn := textproto.NewConn(connection)
	// skip the banner
	if _, err := conn.ReadLine(); err != nil {
		panic(err)
	}
	data := make(chan KeyValueMap, 1)
	go func() {
		ticker := time.NewTicker(interval)
		for {
			kv := make(KeyValueMap)
			select {
			case <-ticker.C:
				headers := list(conn)
				for _, prefix := range headers {
					for key, value := range fetch(conn, prefix) {
						kv[prefix+"."+key] = value
					}
				}
				data <- kv
			case <-done:
				ticker.Stop()
				close(data)
				conn.Close()
				return
			}
		}
	}()
	return data
}
<|endoftext|>


<commit_before>package controller

import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"net/http"
	"net/url"
	"time"

	ct "github.com/flynn/flynn-controller/types"
	"github.com/flynn/rpcplus"
)

func New(uri string) (*Client, error) {
	u, err := url.Parse(uri)
	if err != nil {
		return nil, err
	}
	return &Client{url: uri, addr: u.Host}, nil
}

type Client struct {
	url  string
	addr string
}

var ErrNotFound = errors.New("controller: not found")

func (c *Client) send(method, path string, in, out interface{}) error {
	data, err := json.Marshal(in)
	if err != nil {
		return err
	}
	req, err := http.NewRequest(method, c.url+path, bytes.NewBuffer(data))
	if err != nil {
		return err
	}
	req.Header.Set("Content-Type", "application/json")
	res, err := http.DefaultClient.Do(req)
	if err != nil {
		return err
	}
	defer res.Body.Close()
	if res.StatusCode != 200 {
		return fmt.Errorf("controller: unexpected status %d", res.StatusCode)
	}
	if out != nil {
		return json.NewDecoder(res.Body).Decode(out)
	}
	return nil
}

func (c *Client) put(path string, in, out interface{}) error {
	return c.send("PUT", path, in, out)
}

func (c *Client) post(path string, in, out interface{}) error {
	return c.send("POST", path, in, out)
}

func (c *Client) get(path string, out interface{}) error {
	res, err := http.Get(c.url + path)
	if err != nil {
		return err
	}
	defer res.Body.Close()
	if res.StatusCode != 200 {
		if res.StatusCode == 404 {
			return ErrNotFound
		}
		return fmt.Errorf("controller: unexpected status %d", res.StatusCode)
	}
	return json.NewDecoder(res.Body).Decode(out)
}

func (c *Client) StreamFormations(since *time.Time) (<-chan *ct.ExpandedFormation, *error) {
	if since == nil {
		*since = time.Unix(0, 0)
	}
	// TODO: handle TLS
	client, err := rpcplus.DialHTTP("tcp", c.addr)
	if err != nil {
		return nil, &err
	}
	ch := make(chan *ct.ExpandedFormation)
	return ch, &client.StreamGo("Controller.StreamFormations", since, ch).Error
}

func (c *Client) CreateArtifact(artifact *ct.Artifact) error {
	return c.post("/artifacts", artifact, artifact)
}

func (c *Client) CreateRelease(release *ct.Release) error {
	return c.post("/releases", release, release)
}

func (c *Client) CreateApp(app *ct.App) error {
	return c.post("/apps", app, app)
}

func (c *Client) CreateProvider(provider *ct.Provider) error {
	return c.post("/providers", provider, provider)
}

func (c *Client) ProvisionResource(req *ct.ResourceReq) (*ct.Resource, error) {
	if req.ProviderID == "" {
		return nil, errors.New("controller: missing provider id")
	}
	res := &ct.Resource{}
	err := c.post(fmt.Sprintf("/providers/%s/resources", req.ProviderID), req, res)
	return res, err
}

func (c *Client) PutResource(resource *ct.Resource) error {
	if resource.ID == "" || resource.ProviderID == "" {
		return errors.New("controller: missing id and/or provider id")
	}
	return c.put(fmt.Sprintf("/providers/%s/resources/%s", resource.ProviderID, resource.ID), resource, resource)
}

func (c *Client) PutFormation(formation *ct.Formation) error {
	if formation.AppID == "" || formation.ReleaseID == "" {
		return errors.New("controller: missing app id and/or release id")
	}
	return c.put(fmt.Sprintf("/apps/%s/formations/%s", formation.AppID, formation.ReleaseID), formation, formation)
}

func (c *Client) SetAppRelease(appID, releaseID string) error {
	return c.put("/apps/"+appID+"/release", &ct.Release{ID: releaseID}, nil)
}

func (c *Client) GetAppRelease(appID string) (*ct.Release, error) {
	release := &ct.Release{}
	return release, c.get("/apps/"+appID+"/release", release)
}
<commit_msg>client: Add CreateRoute<commit_after>package controller

import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"net/http"
	"net/url"
	"time"

	ct "github.com/flynn/flynn-controller/types"
	"github.com/flynn/rpcplus"
	"github.com/flynn/strowger/types"
)

func New(uri string) (*Client, error) {
	u, err := url.Parse(uri)
	if err != nil {
		return nil, err
	}
	return &Client{url: uri, addr: u.Host}, nil
}

type Client struct {
	url  string
	addr string
}

var ErrNotFound = errors.New("controller: not found")

func (c *Client) send(method, path string, in, out interface{}) error {
	data, err := json.Marshal(in)
	if err != nil {
		return err
	}
	req, err := http.NewRequest(method, c.url+path, bytes.NewBuffer(data))
	if err != nil {
		return err
	}
	req.Header.Set("Content-Type", "application/json")
	res, err := http.DefaultClient.Do(req)
	if err != nil {
		return err
	}
	defer res.Body.Close()
	if res.StatusCode != 200 {
		return fmt.Errorf("controller: unexpected status %d", res.StatusCode)
	}
	if out != nil {
		return json.NewDecoder(res.Body).Decode(out)
	}
	return nil
}

func (c *Client) put(path string, in, out interface{}) error {
	return c.send("PUT", path, in, out)
}

func (c *Client) post(path string, in, out interface{}) error {
	return c.send("POST", path, in, out)
}

func (c *Client) get(path string, out interface{}) error {
	res, err := http.Get(c.url + path)
	if err != nil {
		return err
	}
	defer res.Body.Close()
	if res.StatusCode != 200 {
		if res.StatusCode == 404 {
			return ErrNotFound
		}
		return fmt.Errorf("controller: unexpected status %d", res.StatusCode)
	}
	return json.NewDecoder(res.Body).Decode(out)
}

func (c *Client) StreamFormations(since *time.Time) (<-chan *ct.ExpandedFormation, *error) {
	if since == nil {
		*since = time.Unix(0, 0)
	}
	// TODO: handle TLS
	client, err := rpcplus.DialHTTP("tcp", c.addr)
	if err != nil {
		return nil, &err
	}
	ch := make(chan *ct.ExpandedFormation)
	return ch, &client.StreamGo("Controller.StreamFormations", since, ch).Error
}

func (c *Client) CreateArtifact(artifact *ct.Artifact) error {
	return c.post("/artifacts", artifact, artifact)
}

func (c *Client) CreateRelease(release *ct.Release) error {
	return c.post("/releases", release, release)
}

func (c *Client) CreateApp(app *ct.App) error {
	return c.post("/apps", app, app)
}

func (c *Client) CreateProvider(provider *ct.Provider) error {
	return c.post("/providers", provider, provider)
}

func (c *Client) ProvisionResource(req *ct.ResourceReq) (*ct.Resource, error) {
	if req.ProviderID == "" {
		return nil, errors.New("controller: missing provider id")
	}
	res := &ct.Resource{}
	err := c.post(fmt.Sprintf("/providers/%s/resources", req.ProviderID), req, res)
	return res, err
}

func (c *Client) PutResource(resource *ct.Resource) error {
	if resource.ID == "" || resource.ProviderID == "" {
		return errors.New("controller: missing id and/or provider id")
	}
	return c.put(fmt.Sprintf("/providers/%s/resources/%s", resource.ProviderID, resource.ID), resource, resource)
}

func (c *Client) PutFormation(formation *ct.Formation) error {
	if formation.AppID == "" || formation.ReleaseID == "" {
		return errors.New("controller: missing app id and/or release id")
	}
	return c.put(fmt.Sprintf("/apps/%s/formations/%s", formation.AppID, formation.ReleaseID), formation, formation)
}

func (c *Client) SetAppRelease(appID, releaseID string) error {
	return c.put(fmt.Sprintf("/apps/%s/release", appID), &ct.Release{ID: releaseID}, nil)
}

func (c *Client) GetAppRelease(appID string) (*ct.Release, error) {
	release := &ct.Release{}
	return release, c.get(fmt.Sprintf("/apps/%s/release", appID), release)
}

func (c *Client) CreateRoute(appID string, route *strowger.Route) error {
	return c.post(fmt.Sprintf("/apps/%s/routes", appID), route, route)
}
<|endoftext|>


<commit_before>package sdk

/*
   Copyright 2016-2017 Alexander I.Grafov <grafov@gmail.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.

   ॐ तारे तुत्तारे तुरे स्व
*/

import (
	"bytes"
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"net/url"
	"path"
)

// DefaultHTTPClient initialized Grafana with appropriate conditions.
// It allows you globally redefine HTTP client.
var DefaultHTTPClient = http.DefaultClient

// Client uses Grafana REST API for interacting with Grafana server.
type Client struct {
	baseURL string
	key     string
	client  *http.Client
}

// StatusMessage reflects status message as it returned by Grafana REST API.
type StatusMessage struct {
	ID      *uint   `json:"id"`
	Message *string `json:"message"`
	Slug    *string `json:"slug"`
	Version *int    `json:"version"`
	Status  *string `json:"resp"`
}

// NewClient initializes client for interacting with an instance of Grafana server.
func NewClient(apiURL, apiKey string, client *http.Client) *Client {
	return &Client{baseURL: apiURL, key: fmt.Sprintf("Bearer %s", apiKey), client: client}
}

func (r *Client) get(query string, params url.Values) ([]byte, int, error) {
	return r.doRequest("GET", query, params, nil)
}

func (r *Client) patch(query string, params url.Values, body []byte) ([]byte, int, error) {
	return r.doRequest("PATCH", query, params, bytes.NewBuffer(body))
}

func (r *Client) put(query string, params url.Values, body []byte) ([]byte, int, error) {
	return r.doRequest("PUT", query, params, bytes.NewBuffer(body))
}

func (r *Client) post(query string, params url.Values, body []byte) ([]byte, int, error) {
	return r.doRequest("POST", query, params, bytes.NewBuffer(body))
}

func (r *Client) delete(query string) ([]byte, int, error) {
	return r.doRequest("DELETE", query, nil, nil)
}

func (r *Client) doRequest(method, query string, params url.Values, buf io.Reader) ([]byte, int, error) {
	u, _ := url.Parse(r.baseURL)
	u.Path = path.Join(u.Path, query)
	if params != nil {
		u.RawQuery = params.Encode()
	}
	req, err := http.NewRequest(method, u.String(), buf)
	req.Header.Set("Authorization", r.key)
	req.Header.Set("Accept", "application/json")
	req.Header.Set("Content-Type", "application/json")
	req.Header.Set("User-Agent", "autograf")
	resp, err := r.client.Do(req)
	if err != nil {
		return nil, 0, err
	}
	data, err := ioutil.ReadAll(resp.Body)
	resp.Body.Close()
	return data, resp.StatusCode, err
}
<commit_msg>allow for basic auth<commit_after>package sdk

/*
   Copyright 2016-2017 Alexander I.Grafov <grafov@gmail.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.

   ॐ तारे तुत्तारे तुरे स्व
*/

import (
	"bytes"
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"net/url"
	"path"
	"strings"
)

// DefaultHTTPClient initialized Grafana with appropriate conditions.
// It allows you globally redefine HTTP client.
var DefaultHTTPClient = http.DefaultClient

// Client uses Grafana REST API for interacting with Grafana server.
type Client struct {
	baseURL   string
	key       string
	basicAuth bool
	client    *http.Client
}

// StatusMessage reflects status message as it returned by Grafana REST API.
type StatusMessage struct {
	ID      *uint   `json:"id"`
	Message *string `json:"message"`
	Slug    *string `json:"slug"`
	Version *int    `json:"version"`
	Status  *string `json:"resp"`
}

// NewClient initializes client for interacting with an instance of Grafana server;
// apiKeyOrBasicAuth accepts either 'username:password' basic authentication credentials,
// or a Grafana API key
func NewClient(apiURL, apiKeyOrBasicAuth string, client *http.Client) *Client {
	key := ""
	basicAuth := strings.Contains(apiKeyOrBasicAuth, ":")
	baseURL, _ := url.Parse(apiURL)
	if !basicAuth {
		key = fmt.Sprintf("Bearer %s", apiKeyOrBasicAuth)
	} else {
		parts := strings.Split(apiKeyOrBasicAuth, ":")
		baseURL.User = url.UserPassword(parts[0], parts[1])
	}
	return &Client{baseURL: baseURL.String(), basicAuth: basicAuth, key: key, client: client}
}

func (r *Client) get(query string, params url.Values) ([]byte, int, error) {
	return r.doRequest("GET", query, params, nil)
}

func (r *Client) patch(query string, params url.Values, body []byte) ([]byte, int, error) {
	return r.doRequest("PATCH", query, params, bytes.NewBuffer(body))
}

func (r *Client) put(query string, params url.Values, body []byte) ([]byte, int, error) {
	return r.doRequest("PUT", query, params, bytes.NewBuffer(body))
}

func (r *Client) post(query string, params url.Values, body []byte) ([]byte, int, error) {
	return r.doRequest("POST", query, params, bytes.NewBuffer(body))
}

func (r *Client) delete(query string) ([]byte, int, error) {
	return r.doRequest("DELETE", query, nil, nil)
}

func (r *Client) doRequest(method, query string, params url.Values, buf io.Reader) ([]byte, int, error) {
	u, _ := url.Parse(r.baseURL)
	u.Path = path.Join(u.Path, query)
	if params != nil {
		u.RawQuery = params.Encode()
	}
	req, err := http.NewRequest(method, u.String(), buf)
	if !r.basicAuth {
		req.Header.Set("Authorization", r.key)
	}
	req.Header.Set("Accept", "application/json")
	req.Header.Set("Content-Type", "application/json")
	req.Header.Set("User-Agent", "autograf")
	resp, err := r.client.Do(req)
	if err != nil {
		return nil, 0, err
	}
	data, err := ioutil.ReadAll(resp.Body)
	resp.Body.Close()
	return data, resp.StatusCode, err
}
<|endoftext|>


<commit_before>package emailclient

import (
	"bytes"
	"mime/multipart"
	"net/textproto"
	"time"
)

// MIMEPart represents a MIME section with headers and message
type MIMEPart struct {
	Headers textproto.MIMEHeader
	Message []byte
}

// NewMIMEPart returns an empty MIME section with headers and message
func NewMIMEPart() MIMEPart {
	part := MIMEPart{}
	part.Headers = make(textproto.MIMEHeader)
	return part
}

func checkedWrite(buffer bytes.Buffer, s string) {
	_, err := buffer.WriteString(s)
	if err != nil {
		panic(err)
	}
}

// ConstructEmail constructs an email to the given recipient consisting of the two MIMEParts
func ConstructEmail(recipient string, part1, part2 MIMEPart) string {
	var mail bytes.Buffer
	mw := multipart.NewWriter(&mail)
	checkedWrite(mail, "\r\n")
	checkedWrite(mail, "From: Test GPG Validation Server <test-gpg-validation-server@tngtech.com>\r\n")
	checkedWrite(mail, "To: "+recipient+"\r\n")
	checkedWrite(mail, "Date: "+time.Now().Format(time.RFC1123Z)+"\r\n")
	checkedWrite(mail, "Subject: GPG Key Validation\r\n")
	// checkedWrite(mail, "X-Pgp-Agent: github.com/TNG/gpg-validation-server\r\n")
	// In-Reply-To: <5719D0CA.7000609@tngtech.com>
	checkedWrite(mail, "Content-Transfer-Encoding: 7bit\r\n")
	// Message-Id: <77777770-1111-2222-3333-444444444444@tngtech.com>
	// References: <88888888-2222-4232-2321-121312312312@tngtech.com> <12389DDA.0000123@tngtech.com>
	// checkedWrite(mail, "Content-Description: OpenPGP encrypted message\r\n")
	checkedWrite(mail, "X-Mailer: github.com/TNG/gpg-validation-server\r\n")

	// Now follow the MIME Headers
	checkedWrite(mail, "Mime-Version: 1.0 (Golang 1.6)\r\n")
	// checkedWrite(mail, "Content-Type: multipart/encrypted; boundary=\"" + mw.Boundary() + "\"; protocol=\"application/pgp-encrypted\";\r\n")
	checkedWrite(mail, "Content-Type: multipart/plain; boundary=\""+mw.Boundary()+"\";\r\n")
	checkedWrite(mail, "\r\n")
	checkedWrite(mail, "This is an OpenPGP/MIME encrypted message (RFC 2440 and 3156)\r\n")

	pw, _ := mw.CreatePart(part1.Headers)
	_, err := pw.Write(part1.Message)
	if err != nil {
		panic(err)
	}
	pw, _ = mw.CreatePart(part2.Headers)
	_, err = pw.Write(part2.Message)
	if err != nil {
		panic(err)
	}
	err = mw.Close()
	if err != nil {
		panic(err)
	}
	return mail.String()
}
<commit_msg>Review comments<commit_after>package emailclient

import (
	"bytes"
	"mime/multipart"
	"net/textproto"
	"time"
)

// MIMEPart represents a MIME section with headers and message
type MIMEPart struct {
	Headers textproto.MIMEHeader
	Message []byte
}

// NewMIMEPart returns an empty MIME section with headers and message
func NewMIMEPart() MIMEPart {
	part := MIMEPart{}
	part.Headers = make(textproto.MIMEHeader)
	return part
}

func checkedWrite(buffer bytes.Buffer, s string) {
	_, err := buffer.WriteString(s)
	if err != nil {
		panic(err)
	}
}

// ConstructEmail constructs an email to the given recipient consisting of the two MIMEParts
func ConstructEmail(recipient string, parts ...MIMEPart) string {
	var mail bytes.Buffer
	multipartWriter := multipart.NewWriter(&mail)
	checkedWrite(mail, "\r\n")
	checkedWrite(mail, "From: Test GPG Validation Server <test-gpg-validation-server@tngtech.com>\r\n")
	checkedWrite(mail, "To: "+recipient+"\r\n")
	checkedWrite(mail, "Date: "+time.Now().Format(time.RFC1123Z)+"\r\n")
	checkedWrite(mail, "Subject: GPG Key Validation\r\n")
	// checkedWrite(mail, "X-Pgp-Agent: github.com/TNG/gpg-validation-server\r\n")
	// In-Reply-To: <5719D0CA.7000609@tngtech.com>
	checkedWrite(mail, "Content-Transfer-Encoding: 7bit\r\n")
	// Message-Id: <77777770-1111-2222-3333-444444444444@tngtech.com>
	// References: <88888888-2222-4232-2321-121312312312@tngtech.com> <12389DDA.0000123@tngtech.com>
	// checkedWrite(mail, "Content-Description: OpenPGP encrypted message\r\n")
	checkedWrite(mail, "X-Mailer: github.com/TNG/gpg-validation-server\r\n")

	// Now follow the MIME Headers
	checkedWrite(mail, "Mime-Version: 1.0 (Golang 1.6)\r\n")
	// checkedWrite(mail, "Content-Type: multipart/encrypted; boundary=\"" + multipartWriter.Boundary() + "\"; protocol=\"application/pgp-encrypted\";\r\n")
	checkedWrite(mail, "Content-Type: multipart/plain; boundary=\""+multipartWriter.Boundary()+"\";\r\n")
	checkedWrite(mail, "\r\n")
	checkedWrite(mail, "This is an OpenPGP/MIME encrypted message (RFC 2440 and 3156)\r\n")

	for _, part := range parts {
		partWriter, _ := multipartWriter.CreatePart(part.Headers)
		_, err := partWriter.Write(part.Message)
		if err != nil {
			panic(err)
		}
	}
	err := multipartWriter.Close()
	if err != nil {
		panic(err)
	}
	return mail.String()
}
<|endoftext|>


<commit_before>package markydown

import "unicode"

// isNewLine checks if a givn rune is a new line.
func isNewLine(r rune) bool {
	return r == '\n' || r == '\r'
}

// isHorizontalSpace checks if a given rune is an horizontal space.
func isHorizontalSpace(r rune) bool {
	return !isNewLine(r) && unicode.IsSpace(r)
}

// isBullet checks if a given rune can be used as bullet in a bulleted list.
func isBullet(r rune) bool {
	return r == '+'
}

// isEmphasis checks if a given rune can be used to emphasize text.
func isEmphasis(r rune) bool {
	return r == '*'
}

// isEscape checks if a given rune can be used to escape other runes.
func isEscape(r rune) bool {
	return r == '\\'
}

// isLinkStart checks if a given rune can be used to start a link.
func isLinkStart(r rune) bool {
	return r == '['
}

// isLinkEnd checks if a given rune can be used to end a link.
func isLinkEnd(r rune) bool {
	return r == ']'
}

// isLinkTargetStart checks if a given rune can be used to start a link target.
func isLinkTargetStart(r rune) bool {
	return r == '('
}

// isLinkTargetEnd checks if a given rune can be used to end a link target.
func isLinkTargetEnd(r rune) bool {
	return r == ')'
}
<commit_msg>Added a missing character class: `isHeading`.<commit_after>package markydown

import "unicode"

// isNewLine checks if a givn rune is a new line.
func isNewLine(r rune) bool {
	return r == '\n' || r == '\r'
}

// isHorizontalSpace checks if a given rune is an horizontal space.
func isHorizontalSpace(r rune) bool {
	return !isNewLine(r) && unicode.IsSpace(r)
}

// isHeading checks if a given rune can be used to mark a heading.
func isHeading(r rune) bool {
	return r == '#'
}

// isBullet checks if a given rune can be used as bullet in a bulleted list.
func isBullet(r rune) bool {
	return r == '+'
}

// isEmphasis checks if a given rune can be used to emphasize text.
func isEmphasis(r rune) bool {
	return r == '*'
}

// isEscape checks if a given rune can be used to escape other runes.
func isEscape(r rune) bool {
	return r == '\\'
}

// isLinkStart checks if a given rune can be used to start a link.
func isLinkStart(r rune) bool {
	return r == '['
}

// isLinkEnd checks if a given rune can be used to end a link.
func isLinkEnd(r rune) bool {
	return r == ']'
}

// isLinkTargetStart checks if a given rune can be used to start a link target.
func isLinkTargetStart(r rune) bool {
	return r == '('
}

// isLinkTargetEnd checks if a given rune can be used to end a link target.
func isLinkTargetEnd(r rune) bool {
	return r == ')'
}
<|endoftext|>


<commit_before>package ccgen

import "github.com/aymerick/raymond"

func init() {
	raymond.RegisterPartials(map[string]string{
		"paramInStruct": paramInStruct,
	})
}

const paramInStruct = `
{{toPascalCase Name}}: payload[{{Key}}],
`
<commit_msg>remove unused file<commit_after><|endoftext|>


<commit_before>package tracking

import (
	"encoding/json"
	"net/http"
	"time"
	"os"

	log "github.com/Sirupsen/logrus"
	"gopkg.in/mgo.v2"
)

// Configuration holds the settings for connecting to outside resources.
type Configuration struct {
	DataFeed       string
	UpdateInterval int
	MongoURL       string
	MongoPort      string
}

// App holds references to Mongo resources.
type App struct {
	Session  *mgo.Session
	Updates  *mgo.Collection
	Vehicles *mgo.Collection
	Routes   *mgo.Collection
	Stops    *mgo.Collection
}

// InitConfig loads and return the app config.
func InitConfig() *Configuration {
	// Read app configuration file
	config, err := readConfiguration("conf.json")
	if err != nil {
		log.Fatalf("error reading configuration file: %v", err)
	}

	return config
}

// InitApp initializes the application given a config and connects to backends.
// It also seeds any needed information to the database.
func InitApp(Config *Configuration) *App {
	// Connect to MongoDB
	session, err := mgo.Dial(Config.MongoURL + ":" + Config.MongoPort)
	if err != nil {
		log.Fatalf("mongoDB connection failed: %v", err)
	}
	// Create Shuttles object to store database session and collections
	app := App{
		session,
		session.DB("shuttle_tracking").C("updates"),
		session.DB("shuttle_tracking").C("vehicles"),
		session.DB("shuttle_tracking").C("routes"),
		session.DB("shuttle_tracking").C("stops"),
	}

	// Ensure unique vehicle identification
  vehicleIndex := mgo.Index{
      Key: []string{"vehicleID"},
      Unique: true,
      DropDups: true}
  app.Vehicles.EnsureIndex(vehicleIndex)

	// Read vehicle configuration file
	serr := readSeedConfiguration("seed/vehicle_seed.json", &app)
	if serr != nil {
		log.Fatalf("error reading vehicle configuration file: %v", serr)
	}

	return &app
}

func readConfiguration(fileName string) (*Configuration, error) {
	// Open config file and decode JSON to Configuration struct
	file, err := os.Open(fileName)
	if err != nil {
		return nil, err
	}
	decoder := json.NewDecoder(file)
	config := Configuration{}
	if err := decoder.Decode(&config); err != nil {
		return nil, err
	}
	return &config, nil
}

//readSeedConfiguration adds a new vehicle to the database from seed.
func readSeedConfiguration(fileName string, app *App) error {
	// Open seed_vehicle config file and decode JSON to app struct
	file, err := os.Open(fileName)

	// Error handling
	if err != nil {
		log.Warn(err)
	}
	// Create a decoder for a file
	fileread := json.NewDecoder(file)

	// Create map for json data and slice for vehicles
	var vehiclesMap map[string][]map[string]interface{}
	Vehicles := []Vehicle{}

	// Call decode on fileread to place items into map
	if err := fileread.Decode(&vehiclesMap); err != nil {
		log.Warn(err)
	}

	// Initialize our vehicles
	for i := range vehiclesMap["Vehicles"] {
		item := vehiclesMap["Vehicles"][i]
		VehicleID, _ := item["VehicleID"].(string)
		VehicleName, _ := item["VehicleName"].(string)
		vehicle := Vehicle{VehicleID, VehicleName, time.Now(), time.Now()}
		Vehicles = append(Vehicles, vehicle)
	}

	// Add vehicles to the database
	for j := range Vehicles {
		app.Vehicles.Insert(&Vehicles[j])
	}

	return nil
}

// WriteJSON writes the data as JSON.
func WriteJSON(w http.ResponseWriter, data interface{}) error {
	w.Header().Set("Content-Type", "application/json")
	b, err := json.MarshalIndent(data, "", " ")
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return err
	}
	w.Write(b)
	return nil
}
<commit_msg>gofmt<commit_after>package tracking

import (
	"encoding/json"
	"net/http"
	"os"
	"time"

	log "github.com/Sirupsen/logrus"
	"gopkg.in/mgo.v2"
)

// Configuration holds the settings for connecting to outside resources.
type Configuration struct {
	DataFeed       string
	UpdateInterval int
	MongoURL       string
	MongoPort      string
}

// App holds references to Mongo resources.
type App struct {
	Session  *mgo.Session
	Updates  *mgo.Collection
	Vehicles *mgo.Collection
	Routes   *mgo.Collection
	Stops    *mgo.Collection
}

// InitConfig loads and return the app config.
func InitConfig() *Configuration {
	// Read app configuration file
	config, err := readConfiguration("conf.json")
	if err != nil {
		log.Fatalf("error reading configuration file: %v", err)
	}

	return config
}

// InitApp initializes the application given a config and connects to backends.
// It also seeds any needed information to the database.
func InitApp(Config *Configuration) *App {
	// Connect to MongoDB
	session, err := mgo.Dial(Config.MongoURL + ":" + Config.MongoPort)
	if err != nil {
		log.Fatalf("mongoDB connection failed: %v", err)
	}
	// Create Shuttles object to store database session and collections
	app := App{
		session,
		session.DB("shuttle_tracking").C("updates"),
		session.DB("shuttle_tracking").C("vehicles"),
		session.DB("shuttle_tracking").C("routes"),
		session.DB("shuttle_tracking").C("stops"),
	}

	// Ensure unique vehicle identification
	vehicleIndex := mgo.Index{
		Key:      []string{"vehicleID"},
		Unique:   true,
		DropDups: true}
	app.Vehicles.EnsureIndex(vehicleIndex)

	// Read vehicle configuration file
	serr := readSeedConfiguration("seed/vehicle_seed.json", &app)
	if serr != nil {
		log.Fatalf("error reading vehicle configuration file: %v", serr)
	}

	return &app
}

func readConfiguration(fileName string) (*Configuration, error) {
	// Open config file and decode JSON to Configuration struct
	file, err := os.Open(fileName)
	if err != nil {
		return nil, err
	}
	decoder := json.NewDecoder(file)
	config := Configuration{}
	if err := decoder.Decode(&config); err != nil {
		return nil, err
	}
	return &config, nil
}

//readSeedConfiguration adds a new vehicle to the database from seed.
func readSeedConfiguration(fileName string, app *App) error {
	// Open seed_vehicle config file and decode JSON to app struct
	file, err := os.Open(fileName)

	// Error handling
	if err != nil {
		log.Warn(err)
	}
	// Create a decoder for a file
	fileread := json.NewDecoder(file)

	// Create map for json data and slice for vehicles
	var vehiclesMap map[string][]map[string]interface{}
	Vehicles := []Vehicle{}

	// Call decode on fileread to place items into map
	if err := fileread.Decode(&vehiclesMap); err != nil {
		log.Warn(err)
	}

	// Initialize our vehicles
	for i := range vehiclesMap["Vehicles"] {
		item := vehiclesMap["Vehicles"][i]
		VehicleID, _ := item["VehicleID"].(string)
		VehicleName, _ := item["VehicleName"].(string)
		vehicle := Vehicle{VehicleID, VehicleName, time.Now(), time.Now()}
		Vehicles = append(Vehicles, vehicle)
	}

	// Add vehicles to the database
	for j := range Vehicles {
		app.Vehicles.Insert(&Vehicles[j])
	}

	return nil
}

// WriteJSON writes the data as JSON.
func WriteJSON(w http.ResponseWriter, data interface{}) error {
	w.Header().Set("Content-Type", "application/json")
	b, err := json.MarshalIndent(data, "", " ")
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return err
	}
	w.Write(b)
	return nil
}
<|endoftext|>


<commit_before>package linker

import(
	tp "tritium/proto"
	proto "goprotobuf.googlecode.com/hg/proto"
	. "tritium/packager"
)

type Executable struct {
	*tp.Executable
}

func NewExecutable(pkg *Package) (*Executable){
	exec := &Executable{
		Executable: &tp.Executable{
			Pkg: pkg.Package,
		},
	}
	return exec
}

func (exec *Executable) ProcessObjects(objs []*tp.ScriptObject) {
	// Add script objects to the exec
	exec.Objects = objs

	// Loop through all the objects, and get the index of every
	// script object name.
	objScriptNameLookupMap := make(map[string]int, 0)
	for objIndex, obj := range(exec.Objects) {
		objScriptNameLookupMap[proto.GetString(obj.Name)] = objIndex
	}

	for _, obj := range(exec.Objects) {
		instructionList := make([]*tp.Instruction, 0)
		instructionList = append(instructionList, obj.Root)
		//(change string in Instruction objects to import_id)
		for i := 0; i < len(instructionList); i++ {
			ins := instructionList[i]
			if ins.Children != nil {
				for _, child := range(ins.Children) {
					instructionList = append(instructionList, child)
				}

			}
			if *ins.Type == tp.Instruction_IMPORT {
				importValue := proto.GetString(ins.Value)
				println("Found import!", importValue)
				println("Index is...", objScriptNameLookupMap[importValue])
			}
		}
	}

	// t.ProcessInstructions()
		// Figure out function signature (name + arg types)
			// have to start at the bottom of the tree (args first) and check types.
		// Is this a real function?
			// aka, text(regexp()) ... have to see that regexp returns Regexp object,
			// which, then, when we go to process text() we notice we don't have a text(Regexp)
			// function, so we need to throw a reasonable error
			// Hrrrm.... need line numbers, huh?
		// Set the function_id if it is real, error otherwise
	// optionally, remove functions from pkg that aren't used (dead code removal)
}
<commit_msg>add a couple notes about how i am looping through ins objects<commit_after>package linker

import(
	tp "tritium/proto"
	proto "goprotobuf.googlecode.com/hg/proto"
	. "tritium/packager"
)

type Executable struct {
	*tp.Executable
}

func NewExecutable(pkg *Package) (*Executable){
	exec := &Executable{
		Executable: &tp.Executable{
			Pkg: pkg.Package,
		},
	}
	return exec
}

func (exec *Executable) ProcessObjects(objs []*tp.ScriptObject) {
	// Add script objects to the exec
	exec.Objects = objs

	// Loop through all the objects, and get the index of every
	// script object name.
	objScriptNameLookupMap := make(map[string]int, 0)
	for objIndex, obj := range(exec.Objects) {
		objScriptNameLookupMap[proto.GetString(obj.Name)] = objIndex
	}

	for _, obj := range(exec.Objects) {
		instructionList := make([]*tp.Instruction, 0)
		instructionList = append(instructionList, obj.Root)

		/*
			This is how I am currently looping through all of the
			instructions. The array above is going to end up being
			an array of *every* instruction. Pushing and popping
			is probably a better idea, but I don't have access to the
			Golang docs right now, so this is my solution. I don't believe
			it will be that slow to do it this way, but feel free to
			change this code to be more efficient in the future.
		*/
		for i := 0; i < len(instructionList); i++ {
			ins := instructionList[i]
			if ins.Children != nil {
				for _, child := range(ins.Children) {
					instructionList = append(instructionList, child)
				}
			}
			// Grab all imports
			if *ins.Type == tp.Instruction_IMPORT {
				// set its import_id and blank the value field
				importValue := proto.GetString(ins.Value)
				println("Found import!", importValue)
				println("Index is...", objScriptNameLookupMap[importValue])
			}
		}
	}

	// t.ProcessInstructions()
		// Figure out function signature (name + arg types)
			// have to start at the bottom of the tree (args first) and check types.
		// Is this a real function?
			// aka, text(regexp()) ... have to see that regexp returns Regexp object,
			// which, then, when we go to process text() we notice we don't have a text(Regexp)
			// function, so we need to throw a reasonable error
			// Hrrrm.... need line numbers, huh?
		// Set the function_id if it is real, error otherwise
	// optionally, remove functions from pkg that aren't used (dead code removal)
}
<|endoftext|>


<commit_before>//go:build integrationtest
// +build integrationtest

package integrationtest

import (
	"fmt"
	"testing"
	"time"

	"github.com/portworx/sched-ops/k8s/apps"
	"github.com/portworx/sched-ops/k8s/core"
	"github.com/portworx/torpedo/drivers/scheduler"
	"github.com/sirupsen/logrus"
	"github.com/stretchr/testify/require"
	appsapi "k8s.io/api/apps/v1"
)

const (
	// node offline timeout just above 4.5 minutes
	// which is the max time stork could take to delete a app pod.
	nodeOfflineTimeout = 271 * time.Second
)

func TestHealthMonitor(t *testing.T) {
	err := setSourceKubeConfig()
	require.NoError(t, err, "failed to set kubeconfig to source cluster: %v", err)

	t.Run("stopDriverTest", stopDriverTest)
	t.Run("stopKubeletTest", stopKubeletTest)
	t.Run("healthCheckFixTest", healthCheckFixTest)

	err = setRemoteConfig("")
	require.NoError(t, err, "setting kubeconfig to default failed")
}

func stopDriverTest(t *testing.T) {
	ctxs, err := schedulerDriver.Schedule(generateInstanceID(t, "stopdrivertest"),
		scheduler.ScheduleOptions{AppKeys: []string{"mysql-1-pvc"}})
	require.NoError(t, err, "Error scheduling task")
	require.Equal(t, 1, len(ctxs), "Only one task should have started")

	err = schedulerDriver.WaitForRunning(ctxs[0], defaultWaitTimeout, defaultWaitInterval)
	require.NoError(t, err, "Error waiting for pod to get to running state")

	scheduledNodes, err := schedulerDriver.GetNodesForApp(ctxs[0])
	require.NoError(t, err, "Error getting node for app")
	require.Equal(t, 1, len(scheduledNodes), "App should be scheduled on one node")

	volumeNames := getVolumeNames(t, ctxs[0])
	require.Equal(t, 1, len(volumeNames), "Should have one volume")

	verifyScheduledNode(t, scheduledNodes[0], volumeNames)

	time.Sleep(1 * time.Minute)

	// Stop the driver and after 3 minutes verify that it moved to another node
	// where the volume is located
	err = volumeDriver.StopDriver(scheduledNodes, false, nil)
	require.NoError(t, err, "Error stopping driver on scheduled Node %+v", scheduledNodes[0])
	stoppedNode := scheduledNodes[0]

	time.Sleep(nodeOfflineTimeout)

	err = schedulerDriver.WaitForRunning(ctxs[0], defaultWaitTimeout, defaultWaitInterval)
	require.NoError(t, err, "Error waiting for pod to get to running state after stopping driver")

	scheduledNodes, err = schedulerDriver.GetNodesForApp(ctxs[0])
	require.NoError(t, err, "Error getting node for app")
	require.Equal(t, 1, len(scheduledNodes), "App should be scheduled on one node")
	require.NotEqual(t, stoppedNode.Name, scheduledNodes[0].Name,
		"App scheduled on node with driver stopped")

	verifyScheduledNode(t, scheduledNodes[0], volumeNames)

	err = volumeDriver.StartDriver(stoppedNode)
	require.NoError(t, err, "Error starting driver on Node %+v", scheduledNodes[0])

	err = volumeDriver.WaitDriverUpOnNode(stoppedNode, defaultWaitTimeout)
	require.NoError(t, err, "Error waiting for Node to start %+v", scheduledNodes[0])

	destroyAndWait(t, ctxs)
}

func stopKubeletTest(t *testing.T) {
	// Cordon node where the test is running. This is so that we don't end up stopping
	// kubelet on the node where the stork-test pod is running
	testPodNode := ""
	testPod, err := core.Instance().GetPodByName("stork-test", "kube-system")
	if err == nil { // if this hits an error, skip below logic to allow running tests outside a pod
		testPodNode = testPod.Spec.NodeName
		err = core.Instance().CordonNode(testPodNode, defaultWaitTimeout, defaultWaitInterval)
		require.NoError(t, err, "Error cordorning k8s node for stork test pod")
	}

	defer func() {
		if len(testPodNode) > 0 {
			err = core.Instance().UnCordonNode(testPodNode, defaultWaitTimeout, defaultWaitInterval)
			require.NoError(t, err, "Error uncordorning k8s node for stork test pod")
		}
	}()

	ctxs, err := schedulerDriver.Schedule(generateInstanceID(t, "stopkubelettest"),
		scheduler.ScheduleOptions{AppKeys: []string{"mysql-ss"}})
	require.NoError(t, err, "Error scheduling task")
	require.Equal(t, 1, len(ctxs), "Only one task should have started")

	err = schedulerDriver.WaitForRunning(ctxs[0], defaultWaitTimeout, defaultWaitInterval)
	require.NoError(t, err, "Error waiting for pod to get to running state")

	scheduledNodes, err := schedulerDriver.GetNodesForApp(ctxs[0])
	require.NoError(t, err, "Error getting node for app")
	require.Equal(t, 1, len(scheduledNodes), "App should be scheduled on one node")

	scheduledNode := scheduledNodes[0]
	err = schedulerDriver.StopSchedOnNode(scheduledNode)
	require.NoError(t, err, fmt.Sprintf("failed to stop scheduler on node: %s", scheduledNode.Name))

	defer func() {
		// restore scheduler
		err = schedulerDriver.StartSchedOnNode(scheduledNode)
		require.NoError(t, err, fmt.Sprintf("failed to start scheduler on node: %s", scheduledNode.Name))

	}()

	// wait for the scheduler daemon on node to stop and pod to get into unknown state
	time.Sleep(6 * time.Minute)

	err = schedulerDriver.WaitForRunning(ctxs[0], defaultWaitTimeout, defaultWaitInterval)
	require.NoError(t, err, "Error waiting for pod to get to running state")

	destroyAndWait(t, ctxs)

}

func healthCheckFixTest(t *testing.T) {
	// When a node's storage is offline stork should not bounce pods right away.
	// It now waits for a minute and checks again to see if the storage driver is still offline.
	// Bringing back node's storage within a minute should not affect anything
	ctxs, err := schedulerDriver.Schedule(generateInstanceID(t, "stopdrivertest"),
		scheduler.ScheduleOptions{AppKeys: []string{"mysql-1-pvc"}})
	require.NoError(t, err, "Error scheduling task")
	require.Equal(t, 1, len(ctxs), "Only one task should have started")

	err = schedulerDriver.WaitForRunning(ctxs[0], defaultWaitTimeout, defaultWaitInterval)
	require.NoError(t, err, "Error waiting for pod to get to running state")

	// Get uuid for the app
	preUIDList := make(map[string]string)
	postUIDList := make(map[string]string)
	for _, spec := range ctxs[0].App.SpecList {
		if dep, ok := spec.(*appsapi.Deployment); ok {
			depPods, err := apps.Instance().GetDeploymentPods(dep)
			require.NoError(t, err, "Error getting pods for deployment ,mysql.")
			for _, pod := range depPods {
				preUIDList[pod.Name] = string(pod.UID)
			}
		}
	}

	scheduledNodes, err := schedulerDriver.GetNodesForApp(ctxs[0])
	require.NoError(t, err, "Error getting node for app")
	require.Equal(t, 1, len(scheduledNodes), "App should be scheduled on one node")
	logrus.Infof("Step: Completed scheduling app on node: %s", scheduledNodes[0].Name)

	volumeNames := getVolumeNames(t, ctxs[0])
	require.Equal(t, 1, len(volumeNames), "Should have one volume")

	verifyScheduledNode(t, scheduledNodes[0], volumeNames)

	// Stop the driver but bring it back in 30 seconds, verify that it has not moved to another node
	err = volumeDriver.StopDriver(scheduledNodes, false, nil)
	require.NoError(t, err, "Error stopping driver on scheduled Node %+v", scheduledNodes[0])
	stoppedNode := scheduledNodes[0]

	time.Sleep(30 * time.Second)

	// Start the driver
	err = volumeDriver.StartDriver(stoppedNode)
	require.NoError(t, err, "Error starting driver on Node %+v", scheduledNodes[0])

	err = volumeDriver.WaitDriverUpOnNode(stoppedNode, defaultWaitTimeout)
	require.NoError(t, err, "Error waiting for Node to start %+v", scheduledNodes[0])
	logrus.Infof("Step: Started volume driver again on node: %s", scheduledNodes[0].Name)

	// Verify that app comes up on the same node
	scheduledNodesPostStop, err := schedulerDriver.GetNodesForApp(ctxs[0])
	require.NoError(t, err, "Error getting node for app")
	logrus.Infof("Step: App scheduled on node after restart: %s", scheduledNodesPostStop[0].Name)

	require.Equal(t, 1, len(scheduledNodesPostStop), "App should be scheduled on one node")
	require.Equal(t, stoppedNode.Name, scheduledNodesPostStop[0].Name,
		"App scheduled on a different node after volume driver stopped for less than a minute")

	verifyScheduledNode(t, scheduledNodesPostStop[0], volumeNames)

	// verify the app has not restarted after volume driver was stopped, by comparing the start time
	for _, spec := range ctxs[0].App.SpecList {
		if dep, ok := spec.(*appsapi.Deployment); ok {
			depPods, err := apps.Instance().GetDeploymentPods(dep)
			require.NoError(t, err, "Error getting pods for deployment ,mysql.")
			for _, pod := range depPods {
				postUIDList[pod.Name] = string(pod.UID)
			}
		}
	}

	require.Equal(t, len(preUIDList), len(postUIDList), "Number of apps pre and post vol driver restart don't match")

	for pod := range preUIDList {
		require.Equal(t, preUIDList[pod], postUIDList[pod], "Uids of apps pre and post vol driver restart don't match")
	}

	destroyAndWait(t, ctxs)
}
<commit_msg>[integration-test] increase nodeoffline timeout to avoid race<commit_after>//go:build integrationtest
// +build integrationtest

package integrationtest

import (
	"fmt"
	"testing"
	"time"

	"github.com/portworx/sched-ops/k8s/apps"
	"github.com/portworx/sched-ops/k8s/core"
	"github.com/portworx/torpedo/drivers/scheduler"
	"github.com/sirupsen/logrus"
	"github.com/stretchr/testify/require"
	appsapi "k8s.io/api/apps/v1"
)

const (
	// node offline timeout just above 4.5 minutes
	// which is the max time stork could take to delete a app pod.
	nodeOfflineTimeout = 295 * time.Second
)

func TestHealthMonitor(t *testing.T) {
	err := setSourceKubeConfig()
	require.NoError(t, err, "failed to set kubeconfig to source cluster: %v", err)

	t.Run("stopDriverTest", stopDriverTest)
	t.Run("stopKubeletTest", stopKubeletTest)
	t.Run("healthCheckFixTest", healthCheckFixTest)

	err = setRemoteConfig("")
	require.NoError(t, err, "setting kubeconfig to default failed")
}

func stopDriverTest(t *testing.T) {
	ctxs, err := schedulerDriver.Schedule(generateInstanceID(t, "stopdrivertest"),
		scheduler.ScheduleOptions{AppKeys: []string{"mysql-1-pvc"}})
	require.NoError(t, err, "Error scheduling task")
	require.Equal(t, 1, len(ctxs), "Only one task should have started")

	err = schedulerDriver.WaitForRunning(ctxs[0], defaultWaitTimeout, defaultWaitInterval)
	require.NoError(t, err, "Error waiting for pod to get to running state")

	scheduledNodes, err := schedulerDriver.GetNodesForApp(ctxs[0])
	require.NoError(t, err, "Error getting node for app")
	require.Equal(t, 1, len(scheduledNodes), "App should be scheduled on one node")

	volumeNames := getVolumeNames(t, ctxs[0])
	require.Equal(t, 1, len(volumeNames), "Should have one volume")

	verifyScheduledNode(t, scheduledNodes[0], volumeNames)

	time.Sleep(1 * time.Minute)

	// Stop the driver and after 3 minutes verify that it moved to another node
	// where the volume is located
	err = volumeDriver.StopDriver(scheduledNodes, false, nil)
	require.NoError(t, err, "Error stopping driver on scheduled Node %+v", scheduledNodes[0])
	stoppedNode := scheduledNodes[0]

	time.Sleep(nodeOfflineTimeout)

	logrus.Infof("Checking if pod got reschedule to online driver node ")
	err = schedulerDriver.WaitForRunning(ctxs[0], defaultWaitTimeout, defaultWaitInterval)
	require.NoError(t, err, "Error waiting for pod to get to running state after stopping driver")

	scheduledNodes, err = schedulerDriver.GetNodesForApp(ctxs[0])
	require.NoError(t, err, "Error getting node for app")
	require.Equal(t, 1, len(scheduledNodes), "App should be scheduled on one node")
	require.NotEqual(t, stoppedNode.Name, scheduledNodes[0].Name,
		"App scheduled on node with driver stopped")

	verifyScheduledNode(t, scheduledNodes[0], volumeNames)

	err = volumeDriver.StartDriver(stoppedNode)
	require.NoError(t, err, "Error starting driver on Node %+v", scheduledNodes[0])

	err = volumeDriver.WaitDriverUpOnNode(stoppedNode, defaultWaitTimeout)
	require.NoError(t, err, "Error waiting for Node to start %+v", scheduledNodes[0])

	destroyAndWait(t, ctxs)
}

func stopKubeletTest(t *testing.T) {
	// Cordon node where the test is running. This is so that we don't end up stopping
	// kubelet on the node where the stork-test pod is running
	testPodNode := ""
	testPod, err := core.Instance().GetPodByName("stork-test", "kube-system")
	if err == nil { // if this hits an error, skip below logic to allow running tests outside a pod
		testPodNode = testPod.Spec.NodeName
		err = core.Instance().CordonNode(testPodNode, defaultWaitTimeout, defaultWaitInterval)
		require.NoError(t, err, "Error cordorning k8s node for stork test pod")
	}

	defer func() {
		if len(testPodNode) > 0 {
			err = core.Instance().UnCordonNode(testPodNode, defaultWaitTimeout, defaultWaitInterval)
			require.NoError(t, err, "Error uncordorning k8s node for stork test pod")
		}
	}()

	ctxs, err := schedulerDriver.Schedule(generateInstanceID(t, "stopkubelettest"),
		scheduler.ScheduleOptions{AppKeys: []string{"mysql-ss"}})
	require.NoError(t, err, "Error scheduling task")
	require.Equal(t, 1, len(ctxs), "Only one task should have started")

	err = schedulerDriver.WaitForRunning(ctxs[0], defaultWaitTimeout, defaultWaitInterval)
	require.NoError(t, err, "Error waiting for pod to get to running state")

	scheduledNodes, err := schedulerDriver.GetNodesForApp(ctxs[0])
	require.NoError(t, err, "Error getting node for app")
	require.Equal(t, 1, len(scheduledNodes), "App should be scheduled on one node")

	scheduledNode := scheduledNodes[0]
	err = schedulerDriver.StopSchedOnNode(scheduledNode)
	require.NoError(t, err, fmt.Sprintf("failed to stop scheduler on node: %s", scheduledNode.Name))

	defer func() {
		// restore scheduler
		err = schedulerDriver.StartSchedOnNode(scheduledNode)
		require.NoError(t, err, fmt.Sprintf("failed to start scheduler on node: %s", scheduledNode.Name))

	}()

	// wait for the scheduler daemon on node to stop and pod to get into unknown state
	time.Sleep(6 * time.Minute)

	err = schedulerDriver.WaitForRunning(ctxs[0], defaultWaitTimeout, defaultWaitInterval)
	require.NoError(t, err, "Error waiting for pod to get to running state")

	destroyAndWait(t, ctxs)

}

func healthCheckFixTest(t *testing.T) {
	// When a node's storage is offline stork should not bounce pods right away.
	// It now waits for a minute and checks again to see if the storage driver is still offline.
	// Bringing back node's storage within a minute should not affect anything
	ctxs, err := schedulerDriver.Schedule(generateInstanceID(t, "stopdrivertest"),
		scheduler.ScheduleOptions{AppKeys: []string{"mysql-1-pvc"}})
	require.NoError(t, err, "Error scheduling task")
	require.Equal(t, 1, len(ctxs), "Only one task should have started")

	err = schedulerDriver.WaitForRunning(ctxs[0], defaultWaitTimeout, defaultWaitInterval)
	require.NoError(t, err, "Error waiting for pod to get to running state")

	// Get uuid for the app
	preUIDList := make(map[string]string)
	postUIDList := make(map[string]string)
	for _, spec := range ctxs[0].App.SpecList {
		if dep, ok := spec.(*appsapi.Deployment); ok {
			depPods, err := apps.Instance().GetDeploymentPods(dep)
			require.NoError(t, err, "Error getting pods for deployment ,mysql.")
			for _, pod := range depPods {
				preUIDList[pod.Name] = string(pod.UID)
			}
		}
	}

	scheduledNodes, err := schedulerDriver.GetNodesForApp(ctxs[0])
	require.NoError(t, err, "Error getting node for app")
	require.Equal(t, 1, len(scheduledNodes), "App should be scheduled on one node")
	logrus.Infof("Step: Completed scheduling app on node: %s", scheduledNodes[0].Name)

	volumeNames := getVolumeNames(t, ctxs[0])
	require.Equal(t, 1, len(volumeNames), "Should have one volume")

	verifyScheduledNode(t, scheduledNodes[0], volumeNames)

	// Stop the driver but bring it back in 30 seconds, verify that it has not moved to another node
	err = volumeDriver.StopDriver(scheduledNodes, false, nil)
	require.NoError(t, err, "Error stopping driver on scheduled Node %+v", scheduledNodes[0])
	stoppedNode := scheduledNodes[0]

	time.Sleep(30 * time.Second)

	// Start the driver
	err = volumeDriver.StartDriver(stoppedNode)
	require.NoError(t, err, "Error starting driver on Node %+v", scheduledNodes[0])

	err = volumeDriver.WaitDriverUpOnNode(stoppedNode, defaultWaitTimeout)
	require.NoError(t, err, "Error waiting for Node to start %+v", scheduledNodes[0])
	logrus.Infof("Step: Started volume driver again on node: %s", scheduledNodes[0].Name)

	// Verify that app comes up on the same node
	scheduledNodesPostStop, err := schedulerDriver.GetNodesForApp(ctxs[0])
	require.NoError(t, err, "Error getting node for app")
	logrus.Infof("Step: App scheduled on node after restart: %s", scheduledNodesPostStop[0].Name)

	require.Equal(t, 1, len(scheduledNodesPostStop), "App should be scheduled on one node")
	require.Equal(t, stoppedNode.Name, scheduledNodesPostStop[0].Name,
		"App scheduled on a different node after volume driver stopped for less than a minute")

	verifyScheduledNode(t, scheduledNodesPostStop[0], volumeNames)

	// verify the app has not restarted after volume driver was stopped, by comparing the start time
	for _, spec := range ctxs[0].App.SpecList {
		if dep, ok := spec.(*appsapi.Deployment); ok {
			depPods, err := apps.Instance().GetDeploymentPods(dep)
			require.NoError(t, err, "Error getting pods for deployment ,mysql.")
			for _, pod := range depPods {
				postUIDList[pod.Name] = string(pod.UID)
			}
		}
	}

	require.Equal(t, len(preUIDList), len(postUIDList), "Number of apps pre and post vol driver restart don't match")

	for pod := range preUIDList {
		require.Equal(t, preUIDList[pod], postUIDList[pod], "Uids of apps pre and post vol driver restart don't match")
	}

	destroyAndWait(t, ctxs)
}
<|endoftext|>


<commit_before>package db

import (
	"reflect"
	"testing"

	"github.com/azmodb/db/pb"
)

var (
	wantRec1 = &Record{
		Record: &pb.Record{Type: unicode, Current: 11, Values: []*pb.Value{
			&pb.Value{Unicode: []byte("v1.1"), Rev: 1},
			&pb.Value{Unicode: []byte("v1.2"), Rev: 2},
			&pb.Value{Unicode: []byte("v1.3"), Rev: 3},
		}},
	}
)

func testDefaultRange(t *testing.T, db *DB) {
	testFunc := func(i int, values []*pb.Value) RangeFunc {
		return func(key []byte, rec *Record) bool {
			if !reflect.DeepEqual(values, rec.Values) {
				t.Fatalf("range #%d: values mismatch\n%+v\n%+v", i, values, rec.Values)
			}
			rec.Close()
			return false
		}
	}

	tests := []struct {
		from, to []byte
		rev      int64
		vers     bool
		values   []*pb.Value
		fn       func(i int, values []*pb.Value) RangeFunc
	}{
		{nil, nil, -1, false, wantRec1.Values[len(wantRec1.Values)-1:], testFunc},
		{nil, nil, -1, true, wantRec1.Values, testFunc},
		{nil, nil, 0, false, wantRec1.Values[len(wantRec1.Values)-1:], testFunc},
		{nil, nil, 0, true, wantRec1.Values, testFunc},

		{nil, nil, 1, false, wantRec1.Values[0:1], testFunc},
		{nil, nil, 2, false, wantRec1.Values[1:2], testFunc},
		{nil, nil, 3, false, wantRec1.Values[2:3], testFunc},
		{nil, nil, 1, true, wantRec1.Values[0:], testFunc},
		{nil, nil, 2, true, wantRec1.Values[1:], testFunc},
		{nil, nil, 3, true, wantRec1.Values[2:], testFunc},

		{nil, nil, 4, false, nil, testFunc},
		{nil, nil, 4, true, nil, testFunc},
		{nil, nil, 5, false, nil, testFunc},
		{nil, nil, 5, true, nil, testFunc},
	}
	for i, test := range tests {
		db.Range(test.from, test.to, test.rev, test.vers, test.fn(i, test.values))
	}
}

func TestRevisionRange(t *testing.T) {
	db := New()
	b := db.Next()
	b.Insert([]byte("k1"), []byte("v1.1"), false)
	b.Insert([]byte("k1"), []byte("v1.2"), false)
	b.Insert([]byte("k1"), []byte("v1.3"), false)
	b.Commit()

	testDefaultRange(t, db)
}
<commit_msg>ForEach unit test<commit_after>package db

import (
	"reflect"
	"testing"

	"github.com/azmodb/db/pb"
)

var (
	wantRec1 = &Record{
		Record: &pb.Record{Type: unicode, Current: 11, Values: []*pb.Value{
			&pb.Value{Unicode: []byte("v1.1"), Rev: 1},
			&pb.Value{Unicode: []byte("v1.2"), Rev: 2},
			&pb.Value{Unicode: []byte("v1.3"), Rev: 3},
		}},
	}
)

func testRevisionForEach(t *testing.T, db *DB) {
	testFunc := func(i int, values []*pb.Value) RangeFunc {
		return func(key []byte, rec *Record) bool {
			if !reflect.DeepEqual(values, rec.Values) {
				t.Fatalf("range #%d: values mismatch\n%+v\n%+v", i, values, rec.Values)
			}
			rec.Close()
			return false
		}
	}

	tests := []struct {
		from, to []byte
		rev      int64
		vers     bool
		values   []*pb.Value
		fn       func(i int, values []*pb.Value) RangeFunc
	}{
		// test ForEach
		{nil, nil, -1, false, wantRec1.Values[len(wantRec1.Values)-1:], testFunc},
		{nil, nil, -1, true, wantRec1.Values, testFunc},
		{nil, nil, 0, false, wantRec1.Values[len(wantRec1.Values)-1:], testFunc},
		{nil, nil, 0, true, wantRec1.Values, testFunc},

		{nil, nil, 1, false, wantRec1.Values[0:1], testFunc},
		{nil, nil, 2, false, wantRec1.Values[1:2], testFunc},
		{nil, nil, 3, false, wantRec1.Values[2:3], testFunc},
		{nil, nil, 1, true, wantRec1.Values[0:], testFunc},
		{nil, nil, 2, true, wantRec1.Values[1:], testFunc},
		{nil, nil, 3, true, wantRec1.Values[2:], testFunc},

		{nil, nil, 4, false, nil, testFunc},
		{nil, nil, 4, true, nil, testFunc},
		{nil, nil, 5, false, nil, testFunc},
		{nil, nil, 5, true, nil, testFunc},

		// test ForEach with end marker
		{nil, []byte("k2"), -1, false, wantRec1.Values[len(wantRec1.Values)-1:], testFunc},
		{nil, []byte("k2"), -1, true, wantRec1.Values, testFunc},
		{nil, []byte("k2"), 0, false, wantRec1.Values[len(wantRec1.Values)-1:], testFunc},
		{nil, []byte("k2"), 0, true, wantRec1.Values, testFunc},

		{nil, []byte("k1"), -1, false, nil, testFunc},
		{nil, []byte("k1"), -1, true, nil, testFunc},
		{nil, []byte("k1"), 0, false, nil, testFunc},
		{nil, []byte("k1"), 0, true, nil, testFunc},

		{nil, []byte("k2"), 1, false, wantRec1.Values[0:1], testFunc},
		{nil, []byte("k2"), 2, false, wantRec1.Values[1:2], testFunc},
		{nil, []byte("k2"), 3, false, wantRec1.Values[2:3], testFunc},
		{nil, []byte("k2"), 1, true, wantRec1.Values[0:], testFunc},
		{nil, []byte("k2"), 2, true, wantRec1.Values[1:], testFunc},
		{nil, []byte("k2"), 3, true, wantRec1.Values[2:], testFunc},

		{nil, []byte("k1"), 1, false, nil, testFunc},
		{nil, []byte("k1"), 2, false, nil, testFunc},
		{nil, []byte("k1"), 3, false, nil, testFunc},
		{nil, []byte("k1"), 1, true, nil, testFunc},
		{nil, []byte("k1"), 2, true, nil, testFunc},
		{nil, []byte("k1"), 3, true, nil, testFunc},
	}
	for i, test := range tests {
		db.Range(test.from, test.to, test.rev, test.vers, test.fn(i, test.values))
	}
}

func TestRevisionRange(t *testing.T) {
	db := New()
	b := db.Next()
	b.Insert([]byte("k1"), []byte("v1.1"), false)
	b.Insert([]byte("k1"), []byte("v1.2"), false)
	b.Insert([]byte("k1"), []byte("v1.3"), false)
	b.Commit()

	testRevisionForEach(t, db)
}
<|endoftext|>


<commit_before>package client

import (
	"strconv"
	"strings"

	"sourcegraph.com/sourcegraph/api_router"
	"sourcegraph.com/sourcegraph/srcgraph/person"
)

type PeopleService interface {
	Get(person PersonSpec) (*person.User, *Response, error)
	List(opt *PersonListOptions) ([]*person.User, *Response, error)
	ListAuthors(person PersonSpec, opt *PersonListAuthorsOptions) ([]*AugmentedPersonUsageByClient, *Response, error)
	ListClients(person PersonSpec, opt *PersonListClientsOptions) ([]*AugmentedPersonUsageOfAuthor, *Response, error)
}

type peopleService struct {
	client *Client
}

var _ PeopleService = &peopleService{}

type PersonSpec struct {
	Email string
	Login string
	UID   int
}

func (s *PersonSpec) PathComponent() string {
	if s.Email != "" {
		return s.Email
	}
	if s.Login != "" {
		return s.Login
	}
	if s.UID > 0 {
		return "$" + strconv.Itoa(s.UID)
	}
	panic("empty PersonSpec")
}

// ParsePersonSpec parses a string generated by (*PersonSpec).String() and
// returns the equivalent PersonSpec struct.
func ParsePersonSpec(pathComponent string) (PersonSpec, error) {
	if strings.HasPrefix(pathComponent, "$") {
		uid, err := strconv.Atoi(pathComponent[1:])
		return PersonSpec{UID: uid}, err
	}
	if strings.Contains(pathComponent, "@") {
		return PersonSpec{Email: pathComponent}, nil
	}
	return PersonSpec{Login: pathComponent}, nil
}

func (s *peopleService) Get(person_ PersonSpec) (*person.User, *Response, error) {
	url, err := s.client.url(api_router.Person, map[string]string{"PersonSpec": person_.PathComponent()}, nil)
	if err != nil {
		return nil, nil, err
	}

	req, err := s.client.NewRequest("GET", url.String(), nil)
	if err != nil {
		return nil, nil, err
	}

	var person__ *person.User
	resp, err := s.client.Do(req, &person__)
	if err != nil {
		return nil, resp, err
	}

	return person__, resp, nil
}

type PersonListOptions struct {
	Query string `url:",omitempty"`

	Sort      string `url:",omitempty"`
	Direction string `url:",omitempty"`

	ListOptions
}

func (s *peopleService) List(opt *PersonListOptions) ([]*person.User, *Response, error) {
	url, err := s.client.url(api_router.People, nil, opt)
	if err != nil {
		return nil, nil, err
	}

	req, err := s.client.NewRequest("GET", url.String(), nil)
	if err != nil {
		return nil, nil, err
	}

	var people []*person.User
	resp, err := s.client.Do(req, &people)
	if err != nil {
		return nil, resp, err
	}

	return people, resp, nil
}

type AugmentedPersonUsageByClient struct {
	Author   *person.User
	RefCount int
}

type PersonListAuthorsOptions PersonListOptions

func (s *peopleService) ListAuthors(person PersonSpec, opt *PersonListAuthorsOptions) ([]*AugmentedPersonUsageByClient, *Response, error) {
	url, err := s.client.url(api_router.PersonAuthors, map[string]string{"PersonSpec": person.PathComponent()}, opt)
	if err != nil {
		return nil, nil, err
	}

	req, err := s.client.NewRequest("GET", url.String(), nil)
	if err != nil {
		return nil, nil, err
	}

	var people []*AugmentedPersonUsageByClient
	resp, err := s.client.Do(req, &people)
	if err != nil {
		return nil, resp, err
	}

	return people, resp, nil
}

type AugmentedPersonUsageOfAuthor struct {
	Client   *person.User
	RefCount int
}

type PersonListClientsOptions PersonListOptions

func (s *peopleService) ListClients(person PersonSpec, opt *PersonListClientsOptions) ([]*AugmentedPersonUsageOfAuthor, *Response, error) {
	url, err := s.client.url(api_router.PersonClients, map[string]string{"PersonSpec": person.PathComponent()}, opt)
	if err != nil {
		return nil, nil, err
	}

	req, err := s.client.NewRequest("GET", url.String(), nil)
	if err != nil {
		return nil, nil, err
	}

	var people []*AugmentedPersonUsageOfAuthor
	resp, err := s.client.Do(req, &people)
	if err != nil {
		return nil, resp, err
	}

	return people, resp, nil
}

type MockPeopleService struct {
	Get_         func(person PersonSpec) (*person.User, *Response, error)
	List_        func(opt *PersonListOptions) ([]*person.User, *Response, error)
	ListAuthors_ func(person PersonSpec, opt *PersonListAuthorsOptions) ([]*AugmentedPersonUsageByClient, *Response, error)
	ListClients_ func(person PersonSpec, opt *PersonListClientsOptions) ([]*AugmentedPersonUsageOfAuthor, *Response, error)
}

var _ PeopleService = MockPeopleService{}

func (s MockPeopleService) Get(person PersonSpec) (*person.User, *Response, error) {
	if s.Get_ == nil {
		return nil, &Response{}, nil
	}
	return s.Get_(person)
}

func (s MockPeopleService) List(opt *PersonListOptions) ([]*person.User, *Response, error) {
	if s.List_ == nil {
		return nil, &Response{}, nil
	}
	return s.List_(opt)
}

func (s MockPeopleService) ListAuthors(person PersonSpec, opt *PersonListAuthorsOptions) ([]*AugmentedPersonUsageByClient, *Response, error) {
	if s.ListAuthors_ == nil {
		return nil, &Response{}, nil
	}
	return s.ListAuthors_(person, opt)
}

func (s MockPeopleService) ListClients(person PersonSpec, opt *PersonListClientsOptions) ([]*AugmentedPersonUsageOfAuthor, *Response, error) {
	if s.ListClients_ == nil {
		return nil, &Response{}, nil
	}
	return s.ListClients_(person, opt)
}
<commit_msg>people authors<commit_after>package client

import (
	"strconv"
	"strings"

	"github.com/sourcegraph/go-nnz/nnz"

	"sourcegraph.com/sourcegraph/api_router"
	"sourcegraph.com/sourcegraph/srcgraph/person"
)

type PeopleService interface {
	Get(person PersonSpec) (*person.User, *Response, error)
	List(opt *PersonListOptions) ([]*person.User, *Response, error)
	ListAuthors(person PersonSpec, opt *PersonListAuthorsOptions) ([]*AugmentedPersonUsageByClient, *Response, error)
	ListClients(person PersonSpec, opt *PersonListClientsOptions) ([]*AugmentedPersonUsageOfAuthor, *Response, error)
}

type peopleService struct {
	client *Client
}

var _ PeopleService = &peopleService{}

type PersonSpec struct {
	Email string
	Login string
	UID   int
}

func (s *PersonSpec) PathComponent() string {
	if s.Email != "" {
		return s.Email
	}
	if s.Login != "" {
		return s.Login
	}
	if s.UID > 0 {
		return "$" + strconv.Itoa(s.UID)
	}
	panic("empty PersonSpec")
}

// ParsePersonSpec parses a string generated by (*PersonSpec).String() and
// returns the equivalent PersonSpec struct.
func ParsePersonSpec(pathComponent string) (PersonSpec, error) {
	if strings.HasPrefix(pathComponent, "$") {
		uid, err := strconv.Atoi(pathComponent[1:])
		return PersonSpec{UID: uid}, err
	}
	if strings.Contains(pathComponent, "@") {
		return PersonSpec{Email: pathComponent}, nil
	}
	return PersonSpec{Login: pathComponent}, nil
}

func (s *peopleService) Get(person_ PersonSpec) (*person.User, *Response, error) {
	url, err := s.client.url(api_router.Person, map[string]string{"PersonSpec": person_.PathComponent()}, nil)
	if err != nil {
		return nil, nil, err
	}

	req, err := s.client.NewRequest("GET", url.String(), nil)
	if err != nil {
		return nil, nil, err
	}

	var person__ *person.User
	resp, err := s.client.Do(req, &person__)
	if err != nil {
		return nil, resp, err
	}

	return person__, resp, nil
}

type PersonListOptions struct {
	Query string `url:",omitempty"`

	Sort      string `url:",omitempty"`
	Direction string `url:",omitempty"`

	ListOptions
}

func (s *peopleService) List(opt *PersonListOptions) ([]*person.User, *Response, error) {
	url, err := s.client.url(api_router.People, nil, opt)
	if err != nil {
		return nil, nil, err
	}

	req, err := s.client.NewRequest("GET", url.String(), nil)
	if err != nil {
		return nil, nil, err
	}

	var people []*person.User
	resp, err := s.client.Do(req, &people)
	if err != nil {
		return nil, resp, err
	}

	return people, resp, nil
}

type PersonUsageByClient struct {
	AuthorUID   nnz.Int    `db:"author_uid"`
	AuthorEmail nnz.String `db:"author_email"`
	RefCount    int        `db:"ref_count"`
}

type AugmentedPersonUsageByClient struct {
	Author *person.User
	*PersonUsageByClient
}

type PersonListAuthorsOptions PersonListOptions

func (s *peopleService) ListAuthors(person PersonSpec, opt *PersonListAuthorsOptions) ([]*AugmentedPersonUsageByClient, *Response, error) {
	url, err := s.client.url(api_router.PersonAuthors, map[string]string{"PersonSpec": person.PathComponent()}, opt)
	if err != nil {
		return nil, nil, err
	}

	req, err := s.client.NewRequest("GET", url.String(), nil)
	if err != nil {
		return nil, nil, err
	}

	var people []*AugmentedPersonUsageByClient
	resp, err := s.client.Do(req, &people)
	if err != nil {
		return nil, resp, err
	}

	return people, resp, nil
}

type PersonUsageOfAuthor struct {
	ClientUID   nnz.Int    `db:"client_uid"`
	ClientEmail nnz.String `db:"client_email"`
	RefCount    int        `db:"ref_count"`
}

type AugmentedPersonUsageOfAuthor struct {
	Client *person.User
	*PersonUsageOfAuthor
}

type PersonListClientsOptions PersonListOptions

func (s *peopleService) ListClients(person PersonSpec, opt *PersonListClientsOptions) ([]*AugmentedPersonUsageOfAuthor, *Response, error) {
	url, err := s.client.url(api_router.PersonClients, map[string]string{"PersonSpec": person.PathComponent()}, opt)
	if err != nil {
		return nil, nil, err
	}

	req, err := s.client.NewRequest("GET", url.String(), nil)
	if err != nil {
		return nil, nil, err
	}

	var people []*AugmentedPersonUsageOfAuthor
	resp, err := s.client.Do(req, &people)
	if err != nil {
		return nil, resp, err
	}

	return people, resp, nil
}

type MockPeopleService struct {
	Get_         func(person PersonSpec) (*person.User, *Response, error)
	List_        func(opt *PersonListOptions) ([]*person.User, *Response, error)
	ListAuthors_ func(person PersonSpec, opt *PersonListAuthorsOptions) ([]*AugmentedPersonUsageByClient, *Response, error)
	ListClients_ func(person PersonSpec, opt *PersonListClientsOptions) ([]*AugmentedPersonUsageOfAuthor, *Response, error)
}

var _ PeopleService = MockPeopleService{}

func (s MockPeopleService) Get(person PersonSpec) (*person.User, *Response, error) {
	if s.Get_ == nil {
		return nil, &Response{}, nil
	}
	return s.Get_(person)
}

func (s MockPeopleService) List(opt *PersonListOptions) ([]*person.User, *Response, error) {
	if s.List_ == nil {
		return nil, &Response{}, nil
	}
	return s.List_(opt)
}

func (s MockPeopleService) ListAuthors(person PersonSpec, opt *PersonListAuthorsOptions) ([]*AugmentedPersonUsageByClient, *Response, error) {
	if s.ListAuthors_ == nil {
		return nil, &Response{}, nil
	}
	return s.ListAuthors_(person, opt)
}

func (s MockPeopleService) ListClients(person PersonSpec, opt *PersonListClientsOptions) ([]*AugmentedPersonUsageOfAuthor, *Response, error) {
	if s.ListClients_ == nil {
		return nil, &Response{}, nil
	}
	return s.ListClients_(person, opt)
}
<|endoftext|>