<commit_before>// Copyright (C) 2015  The GoHBase Authors.  All rights reserved.
// This file is part of GoHBase.
// Use of this source code is governed by the Apache License 2.0
// that can be found in the COPYING file.

package gohbase

import (
	"bytes"
	"errors"
	"fmt"
	"log"
	"strconv"
	"sync"

	"github.com/cznic/b"
	"github.com/golang/protobuf/proto"
	"github.com/tsuna/gohbase/hrpc"
	"github.com/tsuna/gohbase/pb"
	"github.com/tsuna/gohbase/region"
	"github.com/tsuna/gohbase/zk"
)

// Constants
var (
	// Name of the meta region.
	metaTableName = []byte("hbase:meta")

	metaRegionInfo = &region.Info{
		Table:      []byte("hbase:meta"),
		RegionName: []byte("hbase:meta,,1"),
		StopKey:    []byte{},
	}

	infoFamily = map[string][]string{
		"info": nil,
	}
)

// region -> client cache.
type regionClientCache struct {
	m sync.Mutex

	clients map[*region.Info]*region.Client
}

func (rcc *regionClientCache) get(r *region.Info) *region.Client {
	rcc.m.Lock()
	c := rcc.clients[r]
	rcc.m.Unlock()
	return c
}

func (rcc *regionClientCache) put(r *region.Info, c *region.Client) {
	rcc.m.Lock()
	rcc.clients[r] = c
	rcc.m.Unlock()
}

// key -> region cache.
type keyRegionCache struct {
	m sync.Mutex

	// Maps a []byte of a region start key to a *region.Info
	regions *b.Tree
}

func (krc *keyRegionCache) get(key []byte) ([]byte, *region.Info) {
	krc.m.Lock()
	enum, ok := krc.regions.Seek(key)
	if !ok {
		enum.Prev()
	}
	// TODO: It would be nice if we could do just enum.Get() to avoid the
	// unnecessary cost of seeking to the next entry.
	k, v, err := enum.Prev()
	krc.m.Unlock()
	if err != nil {
		return nil, nil
	}
	return k.([]byte), v.(*region.Info)
}

func (krc *keyRegionCache) put(key []byte, reg *region.Info) {
	krc.m.Lock()
	// TODO: return any value that was there previously etc.
	krc.regions.Set(key, reg)
	krc.m.Unlock()
}

// A Client provides access to an HBase cluster.
type Client struct {
	regions keyRegionCache

	// Maps a *region.Info to the *region.Client that we think currently
	// serves it.
	clients regionClientCache

	// Client connected to the RegionServer hosting the hbase:meta table.
	metaClient *region.Client

	zkquorum string
}

// NewClient creates a new HBase client.
func NewClient(zkquorum string) *Client {
	return &Client{
		regions:  keyRegionCache{regions: b.TreeNew(region.CompareGeneric)},
		clients:  regionClientCache{clients: make(map[*region.Info]*region.Client)},
		zkquorum: zkquorum,
	}
}

// CheckTable returns an error if the given table name doesn't exist.
func (c *Client) CheckTable(table string) (*pb.GetResponse, error) {
	resp, err := c.sendRpcToRegion(hrpc.NewGetStr(table, "theKey", nil))
	if err != nil {
		return nil, err
	}
	return resp.(*pb.GetResponse), err
}

// Get returns a single row fetched from HBase.
func (c *Client) Get(table string, rowkey string, families map[string][]string) (*pb.GetResponse, error) {
	resp, err := c.sendRpcToRegion(hrpc.NewGetStr(table, rowkey, families))
	if err != nil {
		return nil, err
	}
	return resp.(*pb.GetResponse), err
}

// Scan retrieves the values specified in families from the given range.
func (c *Client) Scan(table string, families map[string][]string, startRow, stopRow []byte) (*pb.ScanResponse, error) {
	resp, err := c.sendRpcToRegion(hrpc.NewScanStr(table, families, startRow, stopRow))
	if err != nil {
		return nil, err
	}
	return resp.(*pb.ScanResponse), err
}

// Put inserts or updates the values into the given row of the table.
func (c *Client) Put(table string, rowkey string, values map[string]map[string][]byte) (*pb.MutateResponse, error) {
	resp, err := c.sendRpcToRegion(hrpc.NewPutStr(table, rowkey, values))
	if err != nil {
		return nil, err
	}
	return resp.(*pb.MutateResponse), err
}

// Delete removes values from the given row of the table.
func (c *Client) Delete(table, rowkey string, values map[string]map[string][]byte) (*pb.MutateResponse, error) {
	resp, err := c.sendRpcToRegion(hrpc.NewDelStr(table, rowkey, values))
	if err != nil {
		return nil, err
	}
	return resp.(*pb.MutateResponse), err
}

// Append atomically appends all the given values to their current values in HBase.
func (c *Client) Append(table, rowkey string, values map[string]map[string][]byte) (*pb.MutateResponse, error) {
	resp, err := c.sendRpcToRegion(hrpc.NewAppStr(table, rowkey, values))
	if err != nil {
		return nil, err
	}
	return resp.(*pb.MutateResponse), err
}

// Increment atomically increments the given values in HBase.
func (c *Client) Increment(table, rowkey string, values map[string]map[string][]byte) (*pb.MutateResponse, error) {
	resp, err := c.sendRpcToRegion(hrpc.NewIncStr(table, rowkey, values))
	if err != nil {
		return nil, err
	}
	return resp.(*pb.MutateResponse), err
}

// Creates the META key to search for in order to locate the given key.
func createRegionSearchKey(table, key []byte) []byte {
	metaKey := make([]byte, 0, len(table)+len(key)+3)
	metaKey = append(metaKey, table...)
	metaKey = append(metaKey, ',')
	metaKey = append(metaKey, key...)
	metaKey = append(metaKey, ',')
	// ':' is the first byte greater than '9'.  We always want to find the
	// entry with the greatest timestamp, so by looking right before ':'
	// we'll find it.
	metaKey = append(metaKey, ':')
	return metaKey
}

// Checks whether or not the given cache key is for the given table.
func isCacheKeyForTable(table, cacheKey []byte) bool {
	// Check we found an entry that's really for the requested table.
	for i := 0; i < len(table); i++ {
		if table[i] != cacheKey[i] { // This table isn't in the map, we found
			return false // a key which is for another table.
		}
	}

	// Make sure we didn't find another key that's for another table
	// whose name is a prefix of the table name we were given.
	return cacheKey[len(table)] == ','
}

// Searches in the regions cache for the region hosting the given row.
func (c *Client) getRegion(table, key []byte) *region.Info {
	if bytes.Equal(table, metaTableName) {
		return metaRegionInfo
	}
	regionName := createRegionSearchKey(table, key)
	key, region := c.regions.get(regionName)
	if region == nil || !isCacheKeyForTable(table, key) {
		return nil
	}

	if len(region.StopKey) != 0 &&
		// If the stop key is an empty byte array, it means this region is the
		// last region for this table and this key ought to be in that region.
		bytes.Compare(key, region.StopKey) >= 0 {
		return nil
	}

	return region
}

// Returns the client currently known to hose the given region, or NULL.
func (c *Client) clientFor(region *region.Info) *region.Client {
	if region == metaRegionInfo {
		return c.metaClient
	}
	return c.clients.get(region)
}

// Sends an RPC targeted at a particular region to the right RegionServer.
// Returns the response (for now, as the call is synchronous).
func (c *Client) sendRpcToRegion(rpc hrpc.Call) (proto.Message, error) {
	table := rpc.Table()
	key := rpc.Key()
	reg := c.getRegion(table, key)

	var client *region.Client
	if reg != nil {
		client = c.clientFor(reg)
	} else {
		var err error
		client, reg, err = c.locateRegion(table, key)
		if err != nil {
			return nil, err
		}
	}
	rpc.SetRegion(reg.RegionName)
	return client.SendRPC(rpc)
}

// Locates the region in which the given row key for the given table is.
func (c *Client) locateRegion(table, key []byte) (*region.Client, *region.Info, error) {
	if c.metaClient == nil {
		err := c.locateMeta()
		if err != nil {
			return nil, nil, err
		}
	}
	metaKey := createRegionSearchKey(table, key)
	rpc := hrpc.NewGetBefore(metaTableName, metaKey, infoFamily)
	rpc.SetRegion(metaRegionInfo.RegionName)
	resp, err := c.metaClient.SendRPC(rpc)
	if err != nil {
		return nil, nil, err
	}
	return c.discoverRegion(resp.(*pb.GetResponse))
}

// Adds a new region to our regions cache.
func (c *Client) discoverRegion(metaRow *pb.GetResponse) (*region.Client, *region.Info, error) {
	if metaRow.Result == nil {
		return nil, nil, errors.New("table not found")
	}
	var host string
	var port uint16
	var reg *region.Info
	for _, cell := range metaRow.Result.Cell {
		switch string(cell.Qualifier) {
		case "regioninfo":
			var err error
			reg, err = region.InfoFromCell(cell)
			if err != nil {
				return nil, nil, err
			}
		case "server":
			value := cell.Value
			if len(value) == 0 {
				continue // Empty during NSRE.
			}
			colon := bytes.IndexByte(value, ':')
			if colon < 1 { // Colon can't be at the beginning.
				return nil, nil,
					fmt.Errorf("broken meta: no colon found in info:server %q", cell)
			}
			host = string(value[:colon])
			portU64, err := strconv.ParseUint(string(value[colon+1:]), 10, 16)
			if err != nil {
				return nil, nil, err
			}
			port = uint16(portU64)
		default:
			// Other kinds of qualifiers: ignore them.
			// TODO: If this is the parent of a split region, there are two other
			// KVs that could be useful: `info:splitA' and `info:splitB'.
			// Need to investigate whether we can use those as a hint to update our
			// regions_cache with the daughter regions of the split.
		}
	}

	client, err := region.NewClient(host, port)
	if err != nil {
		return nil, nil, err
	}

	// 1. Record the region -> client mapping.
	// This won't be "discoverable" until another map points to it, because
	// at this stage no one knows about this region yet, so another thread
	// may be looking up that region again while we're in the process of
	// publishing our findings.
	c.clients.put(reg, client)

	// 2. Store the region in the sorted map.
	// This will effectively "publish" the result of our work to other
	// threads.  The window between when the previous `put' becomes visible
	// to all other threads and when we're done updating the sorted map is
	// when we may unnecessarily re-lookup the same region again.  It's an
	// acceptable trade-off.  We avoid extra synchronization complexity in
	// exchange of occasional duplicate work (which should be rare anyway).
	c.regions.put(reg.RegionName, reg)
	return client, reg, nil
}

// Looks up the meta region in ZooKeeper.
func (c *Client) locateMeta() error {
	host, port, err := zk.LocateMeta(c.zkquorum)
	if err != nil {
		log.Printf("Error while locating meta: %s", err)
		return err
	}
	log.Printf("Meta @ %s:%d", host, port)
	c.metaClient, err = region.NewClient(host, port)
	return err
}
<commit_msg>Fix runaway client generation and keyRegionCache.put() now returns previous value<commit_after>// Copyright (C) 2015  The GoHBase Authors.  All rights reserved.
// This file is part of GoHBase.
// Use of this source code is governed by the Apache License 2.0
// that can be found in the COPYING file.

package gohbase

import (
	"bytes"
	"errors"
	"fmt"
	"io"
	"log"
	"strconv"
	"sync"

	"github.com/cznic/b"
	"github.com/golang/protobuf/proto"
	"github.com/tsuna/gohbase/hrpc"
	"github.com/tsuna/gohbase/pb"
	"github.com/tsuna/gohbase/region"
	"github.com/tsuna/gohbase/zk"
)

// Constants
var (
	// Name of the meta region.
	metaTableName = []byte("hbase:meta")

	metaRegionInfo = &region.Info{
		Table:      []byte("hbase:meta"),
		RegionName: []byte("hbase:meta,,1"),
		StopKey:    []byte{},
	}

	infoFamily = map[string][]string{
		"info": nil,
	}
)

// region -> client cache.
type regionClientCache struct {
	m sync.Mutex

	clients map[*region.Info]*region.Client
}

func (rcc *regionClientCache) get(r *region.Info) *region.Client {
	rcc.m.Lock()
	c := rcc.clients[r]
	rcc.m.Unlock()
	return c
}

func (rcc *regionClientCache) put(r *region.Info, c *region.Client) {
	rcc.m.Lock()
	rcc.clients[r] = c
	rcc.m.Unlock()
}

// key -> region cache.
type keyRegionCache struct {
	m sync.Mutex

	// Maps a []byte of a region start key to a *region.Info
	regions *b.Tree
}

func (krc *keyRegionCache) get(key []byte) ([]byte, *region.Info) {
	// When seeking - "The Enumerator's position is possibly after the last item in the tree"
	// http://godoc.org/github.com/cznic/b#Tree.Set
	krc.m.Lock()
	enum, ok := krc.regions.Seek(key)
	k, v, err := enum.Prev()
	if err == io.EOF && krc.regions.Len() > 0 {
		// We're past the end of the tree. Return the last element instead.
		// (Without this code we always get a cache miss and create a new client for each req.)
		k, v = krc.regions.Last()
		err = nil
	} else if !ok {
		k, v, err = enum.Prev()
	}
	// TODO: It would be nice if we could do just enum.Get() to avoid the
	// unnecessary cost of seeking to the next entry.
	krc.m.Unlock()
	if err != nil {
		return nil, nil
	}
	return k.([]byte), v.(*region.Info)
}

func (krc *keyRegionCache) put(key []byte, reg *region.Info) *region.Info {
	krc.m.Lock()
	oldV, _ := krc.regions.Put(key, func(interface{}, bool) (interface{}, bool) { return reg, true })
	krc.m.Unlock()
	if oldV == nil {
		return nil
	}
	return oldV.(*region.Info)
}

// A Client provides access to an HBase cluster.
type Client struct {
	regions keyRegionCache

	// Maps a *region.Info to the *region.Client that we think currently
	// serves it.
	clients regionClientCache

	// Client connected to the RegionServer hosting the hbase:meta table.
	metaClient *region.Client

	zkquorum string
}

// NewClient creates a new HBase client.
func NewClient(zkquorum string) *Client {
	return &Client{
		regions:  keyRegionCache{regions: b.TreeNew(region.CompareGeneric)},
		clients:  regionClientCache{clients: make(map[*region.Info]*region.Client)},
		zkquorum: zkquorum,
	}
}

// CheckTable returns an error if the given table name doesn't exist.
func (c *Client) CheckTable(table string) (*pb.GetResponse, error) {
	resp, err := c.sendRpcToRegion(hrpc.NewGetStr(table, "theKey", nil))
	if err != nil {
		return nil, err
	}
	return resp.(*pb.GetResponse), err
}

// Get returns a single row fetched from HBase.
func (c *Client) Get(table string, rowkey string, families map[string][]string) (*pb.GetResponse, error) {
	resp, err := c.sendRpcToRegion(hrpc.NewGetStr(table, rowkey, families))
	if err != nil {
		return nil, err
	}
	return resp.(*pb.GetResponse), err
}

// Scan retrieves the values specified in families from the given range.
func (c *Client) Scan(table string, families map[string][]string, startRow, stopRow []byte) (*pb.ScanResponse, error) {
	resp, err := c.sendRpcToRegion(hrpc.NewScanStr(table, families, startRow, stopRow))
	if err != nil {
		return nil, err
	}
	return resp.(*pb.ScanResponse), err
}

// Put inserts or updates the values into the given row of the table.
func (c *Client) Put(table string, rowkey string, values map[string]map[string][]byte) (*pb.MutateResponse, error) {
	resp, err := c.sendRpcToRegion(hrpc.NewPutStr(table, rowkey, values))
	if err != nil {
		return nil, err
	}
	return resp.(*pb.MutateResponse), err
}

// Delete removes values from the given row of the table.
func (c *Client) Delete(table, rowkey string, values map[string]map[string][]byte) (*pb.MutateResponse, error) {
	resp, err := c.sendRpcToRegion(hrpc.NewDelStr(table, rowkey, values))
	if err != nil {
		return nil, err
	}
	return resp.(*pb.MutateResponse), err
}

// Append atomically appends all the given values to their current values in HBase.
func (c *Client) Append(table, rowkey string, values map[string]map[string][]byte) (*pb.MutateResponse, error) {
	resp, err := c.sendRpcToRegion(hrpc.NewAppStr(table, rowkey, values))
	if err != nil {
		return nil, err
	}
	return resp.(*pb.MutateResponse), err
}

// Increment atomically increments the given values in HBase.
func (c *Client) Increment(table, rowkey string, values map[string]map[string][]byte) (*pb.MutateResponse, error) {
	resp, err := c.sendRpcToRegion(hrpc.NewIncStr(table, rowkey, values))
	if err != nil {
		return nil, err
	}
	return resp.(*pb.MutateResponse), err
}

// Creates the META key to search for in order to locate the given key.
func createRegionSearchKey(table, key []byte) []byte {
	metaKey := make([]byte, 0, len(table)+len(key)+3)
	metaKey = append(metaKey, table...)
	metaKey = append(metaKey, ',')
	metaKey = append(metaKey, key...)
	metaKey = append(metaKey, ',')
	// ':' is the first byte greater than '9'.  We always want to find the
	// entry with the greatest timestamp, so by looking right before ':'
	// we'll find it.
	metaKey = append(metaKey, ':')
	return metaKey
}

// Checks whether or not the given cache key is for the given table.
func isCacheKeyForTable(table, cacheKey []byte) bool {
	// Check we found an entry that's really for the requested table.
	for i := 0; i < len(table); i++ {
		if table[i] != cacheKey[i] { // This table isn't in the map, we found
			return false // a key which is for another table.
		}
	}

	// Make sure we didn't find another key that's for another table
	// whose name is a prefix of the table name we were given.
	return cacheKey[len(table)] == ','
}

// Searches in the regions cache for the region hosting the given row.
func (c *Client) getRegion(table, key []byte) *region.Info {
	if bytes.Equal(table, metaTableName) {
		return metaRegionInfo
	}
	regionName := createRegionSearchKey(table, key)
	key, region := c.regions.get(regionName)
	if region == nil || !isCacheKeyForTable(table, key) {
		return nil
	}

	if len(region.StopKey) != 0 &&
		// If the stop key is an empty byte array, it means this region is the
		// last region for this table and this key ought to be in that region.
		bytes.Compare(key, region.StopKey) >= 0 {
		return nil
	}

	return region
}

// Returns the client currently known to hose the given region, or NULL.
func (c *Client) clientFor(region *region.Info) *region.Client {
	if region == metaRegionInfo {
		return c.metaClient
	}
	return c.clients.get(region)
}

// Sends an RPC targeted at a particular region to the right RegionServer.
// Returns the response (for now, as the call is synchronous).
func (c *Client) sendRpcToRegion(rpc hrpc.Call) (proto.Message, error) {
	table := rpc.Table()
	key := rpc.Key()
	reg := c.getRegion(table, key)

	var client *region.Client
	if reg != nil {
		client = c.clientFor(reg)
	} else {
		var err error
		client, reg, err = c.locateRegion(table, key)
		if err != nil {
			return nil, err
		}
	}
	rpc.SetRegion(reg.RegionName)
	return client.SendRPC(rpc)
}

// Locates the region in which the given row key for the given table is.
func (c *Client) locateRegion(table, key []byte) (*region.Client, *region.Info, error) {
	if c.metaClient == nil {
		err := c.locateMeta()
		if err != nil {
			return nil, nil, err
		}
	}
	metaKey := createRegionSearchKey(table, key)
	rpc := hrpc.NewGetBefore(metaTableName, metaKey, infoFamily)
	rpc.SetRegion(metaRegionInfo.RegionName)
	resp, err := c.metaClient.SendRPC(rpc)
	if err != nil {
		return nil, nil, err
	}
	return c.discoverRegion(resp.(*pb.GetResponse))
}

// Adds a new region to our regions cache.
func (c *Client) discoverRegion(metaRow *pb.GetResponse) (*region.Client, *region.Info, error) {
	if metaRow.Result == nil {
		return nil, nil, errors.New("table not found")
	}
	var host string
	var port uint16
	var reg *region.Info
	for _, cell := range metaRow.Result.Cell {
		switch string(cell.Qualifier) {
		case "regioninfo":
			var err error
			reg, err = region.InfoFromCell(cell)
			if err != nil {
				return nil, nil, err
			}
		case "server":
			value := cell.Value
			if len(value) == 0 {
				continue // Empty during NSRE.
			}
			colon := bytes.IndexByte(value, ':')
			if colon < 1 { // Colon can't be at the beginning.
				return nil, nil,
					fmt.Errorf("broken meta: no colon found in info:server %q", cell)
			}
			host = string(value[:colon])
			portU64, err := strconv.ParseUint(string(value[colon+1:]), 10, 16)
			if err != nil {
				return nil, nil, err
			}
			port = uint16(portU64)
		default:
			// Other kinds of qualifiers: ignore them.
			// TODO: If this is the parent of a split region, there are two other
			// KVs that could be useful: `info:splitA' and `info:splitB'.
			// Need to investigate whether we can use those as a hint to update our
			// regions_cache with the daughter regions of the split.
		}
	}

	client, err := region.NewClient(host, port)
	if err != nil {
		return nil, nil, err
	}

	// 1. Record the region -> client mapping.
	// This won't be "discoverable" until another map points to it, because
	// at this stage no one knows about this region yet, so another thread
	// may be looking up that region again while we're in the process of
	// publishing our findings.
	c.clients.put(reg, client)

	// 2. Store the region in the sorted map.
	// This will effectively "publish" the result of our work to other
	// threads.  The window between when the previous `put' becomes visible
	// to all other threads and when we're done updating the sorted map is
	// when we may unnecessarily re-lookup the same region again.  It's an
	// acceptable trade-off.  We avoid extra synchronization complexity in
	// exchange of occasional duplicate work (which should be rare anyway).
	c.regions.put(reg.RegionName, reg)
	return client, reg, nil
}

// Looks up the meta region in ZooKeeper.
func (c *Client) locateMeta() error {
	host, port, err := zk.LocateMeta(c.zkquorum)
	if err != nil {
		log.Printf("Error while locating meta: %s", err)
		return err
	}
	log.Printf("Meta @ %s:%d", host, port)
	c.metaClient, err = region.NewClient(host, port)
	return err
}
<|endoftext|>


<commit_before>// Copyright (c) 2017 Johan Brandhorst

// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:

// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.

// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.

package grpcweb

import (
	"context"
	"io"

	"github.com/gopherjs/gopherjs/js"
	"google.golang.org/grpc/metadata"

	"github.com/johanbrandhorst/gopherjs-improbable-grpc-web/status"
)

// Client encapsulates all gRPC calls to a
// host-service combination.
type Client struct {
	host    string
	service string
}

// NewClient creates a new Client.
func NewClient(host, service string) *Client {
	return &Client{
		host:    host,
		service: service,
	}
}

// RPCCall performs a unary call to an endpoint, blocking until a
// reply has been received or the context was canceled.
func (c Client) RPCCall(ctx context.Context, method string, req []byte, opts ...CallOption) ([]byte, error) {
	respChan := make(chan []byte, 10)
	errChan := make(chan error, 1)

	onMsg := func(in []byte) { respChan <- in }
	onEnd := func(s *status.Status) {
		if s.Code != status.Ok {
			errChan <- s
		} else {
			errChan <- io.EOF
		}
	}
	err := invoke(ctx, c.host, c.service, method, req, onMsg, onEnd)
	if err != nil {
		return nil, err
	}
	select {
	case resp := <-respChan:
		return resp, nil
	case err := <-errChan:
		return nil, err
	case <-ctx.Done():
		return nil, ctx.Err()
	}
}

// Stream performs a server-to-client streaming RPC call, returning
// a struct which exposes a Go gRPC like streaming interface.
// It is non-blocking.
func (c Client) Stream(ctx context.Context, method string, req []byte, opts ...CallOption) (*StreamClient, error) {
	srv := &StreamClient{
		ctx:      ctx,
		messages: make(chan []byte, 10),
		errors:   make(chan error, 1),
	}

	onMsg := func(in []byte) { srv.messages <- in }
	onEnd := func(s *status.Status) {
		if s.Code != status.Ok {
			srv.errors <- s
		} else {
			srv.errors <- io.EOF
		}
	}
	err := invoke(ctx, c.host, c.service, method, req, onMsg, onEnd)
	if err != nil {
		return nil, err
	}

	return srv, nil
}

// Invoke populates the necessary JS structures and performs the gRPC-web call.
// It attempts to catch any JS errors thrown.
func invoke(ctx context.Context, host, service, method string, req []byte, onMsg onMessageFunc, onEnd onEndFunc) (err error) {
	methodDesc := newMethodDescriptor(newService(service), method, newResponseType())

	md, _ := metadata.FromContext(ctx)
	rawOnEnd := func(code int, msg string, headers *browserHeaders) {
		s := status.New(status.Code(code), msg, headers.headers)
		onEnd(s)
	}
	props := newProperties(host, false, newRequest(req), newBrowserHeaders(md), nil, onMsg, rawOnEnd)

	// Recover any thrown JS errors
	defer func() {
		e := recover()
		if e == nil {
			return
		}

		if e, ok := e.(*js.Error); ok {
			err = e
		} else {
			panic(e)
		}
	}()

	js.Global.Get("grpc").Call("invoke", methodDesc, props)

	return err
}
<commit_msg>Switch to using grpc.Codes in client<commit_after>// Copyright (c) 2017 Johan Brandhorst

// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:

// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.

// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.

package grpcweb

import (
	"context"
	"io"

	"github.com/gopherjs/gopherjs/js"
	"google.golang.org/grpc/codes"
	"google.golang.org/grpc/metadata"

	"github.com/johanbrandhorst/gopherjs-improbable-grpc-web/status"
)

// Client encapsulates all gRPC calls to a
// host-service combination.
type Client struct {
	host    string
	service string
}

// NewClient creates a new Client.
func NewClient(host, service string) *Client {
	return &Client{
		host:    host,
		service: service,
	}
}

// RPCCall performs a unary call to an endpoint, blocking until a
// reply has been received or the context was canceled.
func (c Client) RPCCall(ctx context.Context, method string, req []byte, opts ...CallOption) ([]byte, error) {
	respChan := make(chan []byte, 10)
	errChan := make(chan error, 1)

	onMsg := func(in []byte) { respChan <- in }
	onEnd := func(s *status.Status) {
		if s.Code != codes.OK {
			errChan <- s
		} else {
			errChan <- io.EOF
		}
	}
	err := invoke(ctx, c.host, c.service, method, req, onMsg, onEnd)
	if err != nil {
		return nil, err
	}
	select {
	case resp := <-respChan:
		return resp, nil
	case err := <-errChan:
		return nil, err
	case <-ctx.Done():
		return nil, ctx.Err()
	}
}

// Stream performs a server-to-client streaming RPC call, returning
// a struct which exposes a Go gRPC like streaming interface.
// It is non-blocking.
func (c Client) Stream(ctx context.Context, method string, req []byte, opts ...CallOption) (*StreamClient, error) {
	srv := &StreamClient{
		ctx:      ctx,
		messages: make(chan []byte, 10),
		errors:   make(chan error, 1),
	}

	onMsg := func(in []byte) { srv.messages <- in }
	onEnd := func(s *status.Status) {
		if s.Code != codes.OK {
			srv.errors <- s
		} else {
			srv.errors <- io.EOF
		}
	}
	err := invoke(ctx, c.host, c.service, method, req, onMsg, onEnd)
	if err != nil {
		return nil, err
	}

	return srv, nil
}

// Invoke populates the necessary JS structures and performs the gRPC-web call.
// It attempts to catch any JS errors thrown.
func invoke(ctx context.Context, host, service, method string, req []byte, onMsg onMessageFunc, onEnd onEndFunc) (err error) {
	methodDesc := newMethodDescriptor(newService(service), method, newResponseType())

	md, _ := metadata.FromContext(ctx)
	rawOnEnd := func(code int, msg string, headers *browserHeaders) {
		s := status.New(codes.Code(code), msg, headers.headers)
		onEnd(s)
	}
	props := newProperties(host, false, newRequest(req), newBrowserHeaders(md), nil, onMsg, rawOnEnd)

	// Recover any thrown JS errors
	defer func() {
		e := recover()
		if e == nil {
			return
		}

		if e, ok := e.(*js.Error); ok {
			err = e
		} else {
			panic(e)
		}
	}()

	js.Global.Get("grpc").Call("invoke", methodDesc, props)

	return err
}
<|endoftext|>


<commit_before>package flocker

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

const (
	// From https://github.com/ClusterHQ/flocker-docker-plugin/blob/master/flockerdockerplugin/adapter.py#L18
	defaultVolumeSize = json.Number("107374182400")

	defaultControlServiceHost = "localhost"
	defaultControlServicePort = 4523

	// Flocker connections are authenticated with TLS
	// TODO: It can perhaps be stored somewhere else.
	defaultCACertFile     = "/etc/flocker/cluster.crt"
	defaultClientKeyFile  = "/etc/flocker/apiuser.key"
	defaultClientCertFile = "/etc/flocker/apiuser.crt"
)

var (
	// A volume can take a long time to be available, if we don't want
	// Kubernetes to wait forever we need to stop trying after some time, that
	// time is defined here
	timeoutWaitingForVolume = 2 * time.Minute
	tickerWaitingForVolume  = 5 * time.Second

	errStateNotFound         = errors.New("State not found by Dataset ID")
	errConfigurationNotFound = errors.New("Configuration not found by Name")
)

type flockerClient struct {
	*http.Client

	schema  string
	host    string
	port    int
	version string

	maximumSize json.Number
}

var (
	errFlockerControlServiceHost = errors.New("The volume config must have a key CONTROL_SERVICE_HOST defined in the OtherAttributes field")
	errFlockerControlServicePort = errors.New("The volume config must have a key CONTROL_SERVICE_PORT defined in the OtherAttributes field")
)

// NewClient creates a wrapper over http.Client to communicate with the flocker control service.
func NewClient(host string, port int, caCertPath, keyPath, certPath string) (*flockerClient, error) {
	client, err := newTLSClient(caCertPath, keyPath, certPath)
	if err != nil {
		return nil, err
	}

	return &flockerClient{
		Client:      client,
		schema:      "https",
		host:        host,
		port:        port,
		version:     "v1",
		maximumSize: defaultVolumeSize,
	}, nil
}

/*
 * request do a request using the http.Client embedded to the control service
 * and returns the response or an error in case it happens.
 *
 * Note: you will need to deal with the response body call to Close if you
 * don't want to deal with problems later.
 */
func (c flockerClient) request(method, url string, payload interface{}) (*http.Response, error) {
	var (
		b   []byte
		err error
	)

	if method == "POST" { // Just allow payload on POST
		b, err = json.Marshal(payload)
		if err != nil {
			return nil, err
		}
	}

	req, err := http.NewRequest(method, url, bytes.NewBuffer(b))
	if err != nil {
		return nil, err
	}
	req.Header.Set("Content-Type", "application/json")

	// REMEMBER TO CLOSE THE BODY IN THE OUTSIDE FUNCTION
	return c.Do(req)
}

// post performs a post request with the indicated payload
func (c flockerClient) post(url string, payload interface{}) (*http.Response, error) {
	return c.request("POST", url, payload)
}

// get performs a get request
func (c flockerClient) get(url string) (*http.Response, error) {
	return c.request("GET", url, nil)
}

// getURL returns a full URI to the control service
func (c flockerClient) getURL(path string) string {
	return fmt.Sprintf("%s://%s:%d/%s/%s", c.schema, c.host, c.port, c.version, path)
}

type configurationPayload struct {
	Primary     string          `json:"primary"`
	DatasetID   string          `json:"dataset_id,omitempty"`
	MaximumSize json.Number     `json:"maximum_size,omitempty"`
	Metadata    metadataPayload `json:"metadata,omitempty"`
}

type metadataPayload struct {
	Name string `json:"name,omitempty"`
}

type datasetState struct {
	Path      string `json:"path"`
	DatasetID string `json:"dataset_id"`
}

type datasetStatePayload struct {
	*datasetState
}

type nodeStatePayload struct {
	UUID string `json:"uuid"`
	Host string `json:"host"`
}

// findIDInConfigurationsPayload returns the datasetID if it was found in the
// configurations payload, otherwise it will return an error.
func (c flockerClient) findIDInConfigurationsPayload(body io.ReadCloser, name string) (datasetID string, err error) {
	var configurations []configurationPayload
	if err = json.NewDecoder(body).Decode(&configurations); err == nil {
		for _, r := range configurations {
			if r.Metadata.Name == name {
				return r.DatasetID, nil
			}
		}
		return "", errConfigurationNotFound
	}
	return "", err
}

// findPrimaryUUID returns the UUID of the primary Flocker Control Service for
// the given host.
func (c flockerClient) findPrimaryUUID() (uuid string, err error) {
	resp, err := c.get(c.getURL("state/nodes"))
	if err != nil {
		return "", err
	}
	defer resp.Body.Close()

	var states []nodeStatePayload
	if err = json.NewDecoder(resp.Body).Decode(&states); err == nil {
		for _, s := range states {
			if s.Host == c.host {
				return s.UUID, nil
			}
		}
		return "", errStateNotFound
	}
	return "", err
}

// findPathInDatasetStatePayload returns the path of the given datasetID if it
// was found in the state payload. In case the path is not found it returns an
// error.
func (c flockerClient) findPathInDatasetStatePayload(body io.ReadCloser, datasetID string) (path string, err error) {
	var states []datasetStatePayload
	if err = json.NewDecoder(body).Decode(&states); err == nil {
		for _, s := range states {
			if s.DatasetID == datasetID {
				return s.Path, nil
			}
		}
		return "", errStateNotFound
	}
	return "", err
}

// getDatasetState performs a get request to get the state of the given datasetID, if
// something goes wrong or the datasetID was not found it returns an error.
func (c flockerClient) getDatasetState(datasetID string) (*datasetState, error) {
	resp, err := c.get(c.getURL("state/datasets"))
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

	path, err := c.findPathInDatasetStatePayload(resp.Body, datasetID)
	if err != nil {
		return nil, errStateNotFound
	}

	return &datasetState{
		DatasetID: datasetID,
		Path:      path,
	}, nil
}

// CreateVolume creates a volume in Flocker, waits for it to be ready and
// returns the dataset id.

// This process is a little bit complex but follows this flow:

// 1. Find the Flocker Control Service UUID
// 2. Try to create the dataset
// 3. If it already exists just return
// 4. If it didn't previously exist, wait for it to be ready
func (c flockerClient) CreateVolume(dir string) (datasetID string, err error) {
	// 1) Find the primary Flocker UUID
	// Note: it could be cached, but doing this query we health check it
	primary, err := c.findPrimaryUUID()
	if err != nil {
		return "", err
	}

	// 2) Try to create the dataset in the given Primary
	datasetID = datasetIDFromName(dir)

	payload := configurationPayload{
		Primary:     primary,
		DatasetID:   datasetID,
		MaximumSize: json.Number(defaultVolumeSize),
		Metadata: metadataPayload{
			Name: dir,
		},
	}

	resp, err := c.post(c.getURL("configuration/datasets"), payload)
	if err != nil {
		return "", err
	}
	resp.Body.Close() // StatusCode are enough

	// 3) Return if the dataset was previously created
	if resp.StatusCode == http.StatusConflict {
		return datasetID, nil
	}

	if resp.StatusCode >= 300 {
		return "", fmt.Errorf("Expected: {1,2}xx creating the volume, got: %d", resp.StatusCode)
	}

	// 4) Wait until the dataset is ready for usage. In case it never gets
	// ready there is a timeoutChan that will return an error
	timeoutChan := time.NewTimer(timeoutWaitingForVolume).C
	tickChan := time.NewTicker(tickerWaitingForVolume).C

	for {
		if _, err := c.getDatasetState(datasetID); err == nil {
			return datasetID, nil
		} else if err != errStateNotFound {
			return "", err
		}

		select {
		case <-timeoutChan:
			return "", err
		case <-tickChan:
			break
		}
	}
}
<commit_msg>Remove unused defaults<commit_after>package flocker

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

// From https://github.com/ClusterHQ/flocker-docker-plugin/blob/master/flockerdockerplugin/adapter.py#L18
const defaultVolumeSize = json.Number("107374182400")

var (
	// A volume can take a long time to be available, if we don't want
	// Kubernetes to wait forever we need to stop trying after some time, that
	// time is defined here
	timeoutWaitingForVolume = 2 * time.Minute
	tickerWaitingForVolume  = 5 * time.Second

	errStateNotFound         = errors.New("State not found by Dataset ID")
	errConfigurationNotFound = errors.New("Configuration not found by Name")
)

type flockerClient struct {
	*http.Client

	schema  string
	host    string
	port    int
	version string

	maximumSize json.Number
}

var (
	errFlockerControlServiceHost = errors.New("The volume config must have a key CONTROL_SERVICE_HOST defined in the OtherAttributes field")
	errFlockerControlServicePort = errors.New("The volume config must have a key CONTROL_SERVICE_PORT defined in the OtherAttributes field")
)

// NewClient creates a wrapper over http.Client to communicate with the flocker control service.
func NewClient(host string, port int, caCertPath, keyPath, certPath string) (*flockerClient, error) {
	client, err := newTLSClient(caCertPath, keyPath, certPath)
	if err != nil {
		return nil, err
	}

	return &flockerClient{
		Client:      client,
		schema:      "https",
		host:        host,
		port:        port,
		version:     "v1",
		maximumSize: defaultVolumeSize,
	}, nil
}

/*
 * request do a request using the http.Client embedded to the control service
 * and returns the response or an error in case it happens.
 *
 * Note: you will need to deal with the response body call to Close if you
 * don't want to deal with problems later.
 */
func (c flockerClient) request(method, url string, payload interface{}) (*http.Response, error) {
	var (
		b   []byte
		err error
	)

	if method == "POST" { // Just allow payload on POST
		b, err = json.Marshal(payload)
		if err != nil {
			return nil, err
		}
	}

	req, err := http.NewRequest(method, url, bytes.NewBuffer(b))
	if err != nil {
		return nil, err
	}
	req.Header.Set("Content-Type", "application/json")

	// REMEMBER TO CLOSE THE BODY IN THE OUTSIDE FUNCTION
	return c.Do(req)
}

// post performs a post request with the indicated payload
func (c flockerClient) post(url string, payload interface{}) (*http.Response, error) {
	return c.request("POST", url, payload)
}

// get performs a get request
func (c flockerClient) get(url string) (*http.Response, error) {
	return c.request("GET", url, nil)
}

// getURL returns a full URI to the control service
func (c flockerClient) getURL(path string) string {
	return fmt.Sprintf("%s://%s:%d/%s/%s", c.schema, c.host, c.port, c.version, path)
}

type configurationPayload struct {
	Primary     string          `json:"primary"`
	DatasetID   string          `json:"dataset_id,omitempty"`
	MaximumSize json.Number     `json:"maximum_size,omitempty"`
	Metadata    metadataPayload `json:"metadata,omitempty"`
}

type metadataPayload struct {
	Name string `json:"name,omitempty"`
}

type datasetState struct {
	Path      string `json:"path"`
	DatasetID string `json:"dataset_id"`
}

type datasetStatePayload struct {
	*datasetState
}

type nodeStatePayload struct {
	UUID string `json:"uuid"`
	Host string `json:"host"`
}

// findIDInConfigurationsPayload returns the datasetID if it was found in the
// configurations payload, otherwise it will return an error.
func (c flockerClient) findIDInConfigurationsPayload(body io.ReadCloser, name string) (datasetID string, err error) {
	var configurations []configurationPayload
	if err = json.NewDecoder(body).Decode(&configurations); err == nil {
		for _, r := range configurations {
			if r.Metadata.Name == name {
				return r.DatasetID, nil
			}
		}
		return "", errConfigurationNotFound
	}
	return "", err
}

// findPrimaryUUID returns the UUID of the primary Flocker Control Service for
// the given host.
func (c flockerClient) findPrimaryUUID() (uuid string, err error) {
	resp, err := c.get(c.getURL("state/nodes"))
	if err != nil {
		return "", err
	}
	defer resp.Body.Close()

	var states []nodeStatePayload
	if err = json.NewDecoder(resp.Body).Decode(&states); err == nil {
		for _, s := range states {
			if s.Host == c.host {
				return s.UUID, nil
			}
		}
		return "", errStateNotFound
	}
	return "", err
}

// findPathInDatasetStatePayload returns the path of the given datasetID if it
// was found in the state payload. In case the path is not found it returns an
// error.
func (c flockerClient) findPathInDatasetStatePayload(body io.ReadCloser, datasetID string) (path string, err error) {
	var states []datasetStatePayload
	if err = json.NewDecoder(body).Decode(&states); err == nil {
		for _, s := range states {
			if s.DatasetID == datasetID {
				return s.Path, nil
			}
		}
		return "", errStateNotFound
	}
	return "", err
}

// getDatasetState performs a get request to get the state of the given datasetID, if
// something goes wrong or the datasetID was not found it returns an error.
func (c flockerClient) getDatasetState(datasetID string) (*datasetState, error) {
	resp, err := c.get(c.getURL("state/datasets"))
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

	path, err := c.findPathInDatasetStatePayload(resp.Body, datasetID)
	if err != nil {
		return nil, errStateNotFound
	}

	return &datasetState{
		DatasetID: datasetID,
		Path:      path,
	}, nil
}

// CreateVolume creates a volume in Flocker, waits for it to be ready and
// returns the dataset id.

// This process is a little bit complex but follows this flow:

// 1. Find the Flocker Control Service UUID
// 2. Try to create the dataset
// 3. If it already exists just return
// 4. If it didn't previously exist, wait for it to be ready
func (c flockerClient) CreateVolume(dir string) (datasetID string, err error) {
	// 1) Find the primary Flocker UUID
	// Note: it could be cached, but doing this query we health check it
	primary, err := c.findPrimaryUUID()
	if err != nil {
		return "", err
	}

	// 2) Try to create the dataset in the given Primary
	datasetID = datasetIDFromName(dir)

	payload := configurationPayload{
		Primary:     primary,
		DatasetID:   datasetID,
		MaximumSize: json.Number(defaultVolumeSize),
		Metadata: metadataPayload{
			Name: dir,
		},
	}

	resp, err := c.post(c.getURL("configuration/datasets"), payload)
	if err != nil {
		return "", err
	}
	resp.Body.Close() // StatusCode are enough

	// 3) Return if the dataset was previously created
	if resp.StatusCode == http.StatusConflict {
		return datasetID, nil
	}

	if resp.StatusCode >= 300 {
		return "", fmt.Errorf("Expected: {1,2}xx creating the volume, got: %d", resp.StatusCode)
	}

	// 4) Wait until the dataset is ready for usage. In case it never gets
	// ready there is a timeoutChan that will return an error
	timeoutChan := time.NewTimer(timeoutWaitingForVolume).C
	tickChan := time.NewTicker(tickerWaitingForVolume).C

	for {
		if _, err := c.getDatasetState(datasetID); err == nil {
			return datasetID, nil
		} else if err != errStateNotFound {
			return "", err
		}

		select {
		case <-timeoutChan:
			return "", err
		case <-tickChan:
			break
		}
	}
}
<|endoftext|>


<commit_before>package apns

import (
	"crypto/tls"
	"errors"
	"net"
	"time"
)

// Client contains the fields necessary to communicate
// with Apple, such as the gateway to use and your
// certificate contents.
//
// You'll need to provide your own CertificateFile
// and KeyFile to send notifications. Ideally, you'll
// just set the CertificateFile and KeyFile fields to
// a location on drive where the certs can be loaded,
// but if you prefer you can use the CertificateBase64
// and KeyBase64 fields to store the actual contents.
type Client struct {
	Gateway           string
	CertificateFile   string
	CertificateBase64 string
	KeyFile           string
	KeyBase64         string
}

// BareClient can be used to set the contents of your
// certificate and key blocks manually.
func BareClient(gateway, certificateBase64, keyBase64 string) (c *Client) {
	c = new(Client)
	c.Gateway = gateway
	c.CertificateBase64 = certificateBase64
	c.KeyBase64 = keyBase64
	return
}

// NewClient assumes you'll be passing in paths that
// point to your certificate and key.
func NewClient(gateway, certificateFile, keyFile string) (c *Client) {
	c = new(Client)
	c.Gateway = gateway
	c.CertificateFile = certificateFile
	c.KeyFile = keyFile
	return
}

// Send connects to the APN service and sends your push notification.
// Remember that if the submission is successful, Apple won't reply.
func (client *Client) Send(pn *PushNotification) (resp *PushNotificationResponse) {
	resp = new(PushNotificationResponse)

	payload, err := pn.ToBytes()
	if err != nil {
		resp.Success = false
		resp.Error = err
		return
	}

	err = client.ConnectAndWrite(resp, payload)
	if err != nil {
		resp.Success = false
		resp.Error = err
		return
	}

	resp.Success = true
	resp.Error = nil

	return
}

// ConnectAndWrite establishes the connection to Apple and handles the
// transmission of your push notification, as well as waiting for a reply.
//
// In lieu of a timeout (which would be available in Go 1.1)
// we use a timeout channel pattern instead. We start two goroutines,
// one of which just sleeps for TimeoutSeconds seconds, while the other
// waits for a response from the Apple servers.
//
// Whichever channel puts data on first is the "winner". As such, it's
// possible to get a false positive if Apple takes a long time to respond.
// It's probably not a deal-breaker, but something to be aware of.
func (client *Client) ConnectAndWrite(resp *PushNotificationResponse, payload []byte) (err error) {
	var cert tls.Certificate

	if len(client.CertificateBase64) == 0 && len(client.KeyBase64) == 0 {
		// The user did not specify raw block contents, so check the filesystem.
		cert, err = tls.LoadX509KeyPair(client.CertificateFile, client.KeyFile)
	} else {
		// The user provided the raw block contents, so use that.
		cert, err = tls.X509KeyPair([]byte(client.CertificateBase64), []byte(client.KeyBase64))
	}

	if err != nil {
		return err
	}

	conf := &tls.Config{
		Certificates: []tls.Certificate{cert},
	}

	conn, err := net.Dial("tcp", client.Gateway)
	if err != nil {
		return err
	}
	defer conn.Close()

	tlsConn := tls.Client(conn, conf)
	err = tlsConn.Handshake()
	if err != nil {
		return err
	}
	defer tlsConn.Close()

	_, err = tlsConn.Write(payload)
	if err != nil {
		return err
	}

	// Create one channel that will serve to handle
	// timeouts when the notification succeeds.
	timeoutChannel := make(chan bool, 1)
	go func() {
		time.Sleep(time.Second * TimeoutSeconds)
		timeoutChannel <- true
	}()

	// This channel will contain the binary response
	// from Apple in the event of a failure.
	responseChannel := make(chan []byte, 1)
	go func() {
		buffer := make([]byte, 6, 6)
		tlsConn.Read(buffer)
		responseChannel <- buffer
	}()

	// First one back wins!
	// The data structure for an APN response is as follows:
	//
	// command    -> 1 byte
	// status     -> 1 byte
	// identifier -> 4 bytes
	//
	// The first byte will always be set to 8.
	select {
	case r := <-responseChannel:
		resp.Success = false
		resp.AppleResponse = ApplePushResponses[r[1]]
		err = errors.New(resp.AppleResponse)
	case <-timeoutChannel:
		resp.Success = true
	}

	return err
}
<commit_msg>Maintain client certificate to reduce crytpo library costs<commit_after>package apns

import (
	"crypto/tls"
	"errors"
	"net"
	"time"
)

// Client contains the fields necessary to communicate
// with Apple, such as the gateway to use and your
// certificate contents.
//
// You'll need to provide your own CertificateFile
// and KeyFile to send notifications. Ideally, you'll
// just set the CertificateFile and KeyFile fields to
// a location on drive where the certs can be loaded,
// but if you prefer you can use the CertificateBase64
// and KeyBase64 fields to store the actual contents.
type Client struct {
	Gateway           string
	CertificateFile   string
	CertificateBase64 string
	KeyFile           string
	KeyBase64         string
	Certificate       tls.Certificate
}

// BareClient can be used to set the contents of your
// certificate and key blocks manually.
func BareClient(gateway, certificateBase64, keyBase64 string) (c *Client) {
	c = new(Client)
	c.Gateway = gateway
	c.CertificateBase64 = certificateBase64
	c.KeyBase64 = keyBase64
	return
}

// NewClient assumes you'll be passing in paths that
// point to your certificate and key.
func NewClient(gateway, certificateFile, keyFile string) (c *Client) {
	c = new(Client)
	c.Gateway = gateway
	c.CertificateFile = certificateFile
	c.KeyFile = keyFile
	return
}

// Send connects to the APN service and sends your push notification.
// Remember that if the submission is successful, Apple won't reply.
func (client *Client) Send(pn *PushNotification) (resp *PushNotificationResponse) {
	resp = new(PushNotificationResponse)

	payload, err := pn.ToBytes()
	if err != nil {
		resp.Success = false
		resp.Error = err
		return
	}

	err = client.ConnectAndWrite(resp, payload)
	if err != nil {
		resp.Success = false
		resp.Error = err
		return
	}

	resp.Success = true
	resp.Error = nil

	return
}

// ConnectAndWrite establishes the connection to Apple and handles the
// transmission of your push notification, as well as waiting for a reply.
//
// In lieu of a timeout (which would be available in Go 1.1)
// we use a timeout channel pattern instead. We start two goroutines,
// one of which just sleeps for TimeoutSeconds seconds, while the other
// waits for a response from the Apple servers.
//
// Whichever channel puts data on first is the "winner". As such, it's
// possible to get a false positive if Apple takes a long time to respond.
// It's probably not a deal-breaker, but something to be aware of.
func (client *Client) ConnectAndWrite(resp *PushNotificationResponse, payload []byte) (err error) {
	cert, err := client.getCertificate()

	if err != nil {
		return err
	}

	conf := &tls.Config{
		Certificates: []tls.Certificate{cert},
	}

	conn, err := net.Dial("tcp", client.Gateway)
	if err != nil {
		return err
	}
	defer conn.Close()

	tlsConn := tls.Client(conn, conf)
	err = tlsConn.Handshake()
	if err != nil {
		return err
	}
	defer tlsConn.Close()

	_, err = tlsConn.Write(payload)
	if err != nil {
		return err
	}

	// Create one channel that will serve to handle
	// timeouts when the notification succeeds.
	timeoutChannel := make(chan bool, 1)
	go func() {
		time.Sleep(time.Second * TimeoutSeconds)
		timeoutChannel <- true
	}()

	// This channel will contain the binary response
	// from Apple in the event of a failure.
	responseChannel := make(chan []byte, 1)
	go func() {
		buffer := make([]byte, 6, 6)
		tlsConn.Read(buffer)
		responseChannel <- buffer
	}()

	// First one back wins!
	// The data structure for an APN response is as follows:
	//
	// command    -> 1 byte
	// status     -> 1 byte
	// identifier -> 4 bytes
	//
	// The first byte will always be set to 8.
	select {
	case r := <-responseChannel:
		resp.Success = false
		resp.AppleResponse = ApplePushResponses[r[1]]
		err = errors.New(resp.AppleResponse)
	case <-timeoutChannel:
		resp.Success = true
	}

	return err
}

// Returns a certificate to use to send the notification.
// The certificate is only created once to save on
// the overhead of the crypto libraries.
func (client *Client) getCertificate() (tls.Certificate, error) {
	var err error
	if &client.Certificate == nil {
		if len(client.CertificateBase64) == 0 && len(client.KeyBase64) == 0 {
			// The user did not specify raw block contents, so check the filesystem.
			client.Certificate, err = tls.LoadX509KeyPair(client.CertificateFile, client.KeyFile)
		} else {
			// The user provided the raw block contents, so use that.
			client.Certificate, err = tls.X509KeyPair([]byte(client.CertificateBase64), []byte(client.KeyBase64))
		}
	}

	return client.Certificate, err
}
<|endoftext|>


<commit_before>package main

import (
	"crypto/tls"
	"crypto/x509"
	"flag"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"net"
	"net/url"
	"path"

	"golang.org/x/net/proxy"
	"golang.org/x/net/websocket"
)

var (
	certsDir = flag.String("certs_dir", "", "Directory of certs for TLS connection to AMQP, or empty for non-TLS connection. "+
		"Expected files are: cacert.pem, cert.pem and key.pem.")
	serverName = flag.String("server_name", "", "Name of the server for TLS verification, or empty for default")

	targetHost = flag.String("target_host", "", "The target host:port to tunnel to")
	port       = flag.Int("port", 8080, "The local port to listen on")
)

func getTlsConfig() (*tls.Config, error) {
	if *certsDir == "" {
		return nil, nil
	}

	tlscfg := new(tls.Config)
	tlscfg.RootCAs = x509.NewCertPool()
	if ca, err := ioutil.ReadFile(path.Join(*certsDir, "cacert.pem")); err == nil {
		tlscfg.RootCAs.AppendCertsFromPEM(ca)
	} else {
		return nil, fmt.Errorf("Failed reading CA certificate: %v", err)
	}

	if cert, err := tls.LoadX509KeyPair(path.Join(*certsDir, "/cert.pem"), path.Join(*certsDir, "/key.pem")); err == nil {
		tlscfg.Certificates = append(tlscfg.Certificates, cert)
	} else {
		return nil, fmt.Errorf("Failed reading client certificate: %v", err)
	}

	if *serverName != "" {
		tlscfg.ServerName = *serverName
	}
	return tlscfg, nil
}

func getWsConfig() (*websocket.Config, error) {
	url := url.URL{Scheme: "ws", Host: *targetHost}
	if *certsDir != "" {
		url.Scheme = "wss"
	}

	config, err := websocket.NewConfig(url.String(), "http://localhost/")
	if err != nil {
		return nil, err
	}

	if config.TlsConfig, err = getTlsConfig(); err != nil {
		return nil, err
	}

	return config, nil
}

func iocopy(dst io.Writer, src io.Reader, c chan error) {
	_, err := io.Copy(dst, src)
	c <- err
}

type closeable interface {
	CloseWrite() error
}

func closeWrite(conn net.Conn) {
	if closeme, ok := conn.(closeable); ok {
		closeme.CloseWrite()
	}
}

func handleConnection(wsConfig *websocket.Config, conn net.Conn) {
	defer conn.Close()

	tcp, err := proxy.FromEnvironment().Dial("tcp", wsConfig.Location.Host)
	if err != nil {
		log.Print("proxy.FromEnvironment().Dial(): ", err)
		return
	}

	if *certsDir != "" {
		tcp = tls.Client(tcp, wsConfig.TlsConfig)
	}

	ws, err := websocket.NewClient(wsConfig, tcp)
	if err != nil {
		log.Print("websocket.NewClient(): ", err)
		return
	}
	defer ws.Close()

	c := make(chan error, 2)
	go iocopy(ws, conn, c)
	go iocopy(conn, ws, c)

	for i := 0; i < 2; i++ {
		if err := <-c; err != nil {
			fmt.Print("io.Copy(): ", err)
			return
		}
		// If any of the sides closes the connection, we want to close the write channel.
		closeWrite(conn)
		closeWrite(tcp)
	}
}

func main() {
	flag.Parse()

	wsConfig, err := getWsConfig()
	if err != nil {
		panic(err)
	}

	ln, err := net.Listen("tcp", fmt.Sprintf(":%d", *port))
	if err != nil {
		panic(err)
	}

	for {
		conn, err := ln.Accept()
		if err != nil {
			log.Print("ln.Accept(): ", err)
			continue
		}
		go handleConnection(wsConfig, conn)
	}
}
<commit_msg>Setting default server_name for TLS<commit_after>package main

import (
	"crypto/tls"
	"crypto/x509"
	"flag"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"net"
	"net/url"
	"path"
	"strings"

	"golang.org/x/net/proxy"
	"golang.org/x/net/websocket"
)

var (
	certsDir = flag.String("certs_dir", "", "Directory of certs for TLS connection to AMQP, or empty for non-TLS connection. "+
		"Expected files are: cacert.pem, cert.pem and key.pem.")
	serverName = flag.String("server_name", "", "Name of the server for TLS verification, or empty for default")

	targetHost = flag.String("target_host", "", "The target host:port to tunnel to")
	port       = flag.Int("port", 8080, "The local port to listen on")
)

func getTlsConfig() (*tls.Config, error) {
	if *certsDir == "" {
		return nil, nil
	}

	tlscfg := new(tls.Config)
	tlscfg.RootCAs = x509.NewCertPool()
	if ca, err := ioutil.ReadFile(path.Join(*certsDir, "cacert.pem")); err == nil {
		tlscfg.RootCAs.AppendCertsFromPEM(ca)
	} else {
		return nil, fmt.Errorf("Failed reading CA certificate: %v", err)
	}

	if cert, err := tls.LoadX509KeyPair(path.Join(*certsDir, "/cert.pem"), path.Join(*certsDir, "/key.pem")); err == nil {
		tlscfg.Certificates = append(tlscfg.Certificates, cert)
	} else {
		return nil, fmt.Errorf("Failed reading client certificate: %v", err)
	}

	tlscfg.ServerName = strings.Split(*targetHost, ":")[0]
	if *serverName != "" {
		tlscfg.ServerName = *serverName
	}
	return tlscfg, nil
}

func getWsConfig() (*websocket.Config, error) {
	url := url.URL{Scheme: "ws", Host: *targetHost}
	if *certsDir != "" {
		url.Scheme = "wss"
	}

	config, err := websocket.NewConfig(url.String(), "http://localhost/")
	if err != nil {
		return nil, err
	}

	if config.TlsConfig, err = getTlsConfig(); err != nil {
		return nil, err
	}

	return config, nil
}

func iocopy(dst io.Writer, src io.Reader, c chan error) {
	_, err := io.Copy(dst, src)
	c <- err
}

type closeable interface {
	CloseWrite() error
}

func closeWrite(conn net.Conn) {
	if closeme, ok := conn.(closeable); ok {
		closeme.CloseWrite()
	}
}

func handleConnection(wsConfig *websocket.Config, conn net.Conn) {
	defer conn.Close()

	tcp, err := proxy.FromEnvironment().Dial("tcp", wsConfig.Location.Host)
	if err != nil {
		log.Print("proxy.FromEnvironment().Dial(): ", err)
		return
	}

	if *certsDir != "" {
		tcp = tls.Client(tcp, wsConfig.TlsConfig)
	}

	ws, err := websocket.NewClient(wsConfig, tcp)
	if err != nil {
		log.Print("websocket.NewClient(): ", err)
		return
	}
	defer ws.Close()

	c := make(chan error, 2)
	go iocopy(ws, conn, c)
	go iocopy(conn, ws, c)

	for i := 0; i < 2; i++ {
		if err := <-c; err != nil {
			fmt.Print("io.Copy(): ", err)
			return
		}
		// If any of the sides closes the connection, we want to close the write channel.
		closeWrite(conn)
		closeWrite(tcp)
	}
}

func main() {
	flag.Parse()

	wsConfig, err := getWsConfig()
	if err != nil {
		panic(err)
	}

	ln, err := net.Listen("tcp", fmt.Sprintf(":%d", *port))
	if err != nil {
		panic(err)
	}

	for {
		conn, err := ln.Accept()
		if err != nil {
			log.Print("ln.Accept(): ", err)
			continue
		}
		go handleConnection(wsConfig, conn)
	}
}
<|endoftext|>


<commit_before>package main

import (
    "log"
    "huabot-sched/db"
    "encoding/json"
)

type Client struct {
    sched *Sched
    conn Conn
}

func NewClient(sched *Sched, conn Conn) (client *Client) {
    client = new(Client)
    client.conn = conn
    client.sched = sched
    return
}

func (client *Client) Handle() {
    var payload []byte
    var err error
    var conn = client.conn
    defer conn.Close()
    for {
        payload, err = conn.Receive()
        if err != nil {
            log.Printf("Error: %s\n", err.Error())
            return
        }

        switch payload[0] {
        case SUBMIT_JOB:
            err = client.HandleSubmitJob(payload[2:])
            break
        case STATUS:
            err = client.HandleStatus()
            break
        case PING:
            err = conn.Send(packCmd(PONG))
            break
        default:
            err = conn.Send(packCmd(UNKNOWN))
            break
        }
        if err != nil {
            log.Printf("Error: %s\n", err.Error())
            return
        }
    }
}

func (client *Client) HandleSubmitJob(payload []byte) (err error) {
    var job db.Job
    var e error
    var conn = client.conn
    var sched = client.sched
    e = json.Unmarshal(payload, &job)
    if e != nil {
        err = conn.Send([]byte(e.Error()))
        return
    }
    is_new := true
    jobId, _ := db.GetIndex("job:" + job.Func + ":name", job.Name)
    if jobId > 0 {
        job.Id = jobId
        if oldJob, e := db.GetJob(jobId); e == nil && oldJob.Status == db.JOB_STATUS_PROC {
            sched.DecrStatProc(oldJob)
        }
        is_new = false
    }
    e = job.Save()
    if e != nil {
        err = conn.Send([]byte(e.Error()))
        return
    }
    if is_new {
        sched.IncrStatJob(job)
    }
    sched.Notify()
    err = conn.Send([]byte("ok"))
    return
}

func (client *Client) HandleStatus() (err error) {
    var conn = client.conn
    var sched = client.sched
    data, _ := json.Marshal(sched.Funcs)
    err = conn.Send(data)
    return
}
<commit_msg>Fix, set the default job status when add job<commit_after>package main

import (
    "log"
    "huabot-sched/db"
    "encoding/json"
)

type Client struct {
    sched *Sched
    conn Conn
}

func NewClient(sched *Sched, conn Conn) (client *Client) {
    client = new(Client)
    client.conn = conn
    client.sched = sched
    return
}

func (client *Client) Handle() {
    var payload []byte
    var err error
    var conn = client.conn
    defer conn.Close()
    for {
        payload, err = conn.Receive()
        if err != nil {
            log.Printf("Error: %s\n", err.Error())
            return
        }

        switch payload[0] {
        case SUBMIT_JOB:
            err = client.HandleSubmitJob(payload[2:])
            break
        case STATUS:
            err = client.HandleStatus()
            break
        case PING:
            err = conn.Send(packCmd(PONG))
            break
        default:
            err = conn.Send(packCmd(UNKNOWN))
            break
        }
        if err != nil {
            log.Printf("Error: %s\n", err.Error())
            return
        }
    }
}

func (client *Client) HandleSubmitJob(payload []byte) (err error) {
    var job db.Job
    var e error
    var conn = client.conn
    var sched = client.sched
    e = json.Unmarshal(payload, &job)
    if e != nil {
        err = conn.Send([]byte(e.Error()))
        return
    }
    is_new := true
    job.Status = db.JOB_STATUS_READY
    jobId, _ := db.GetIndex("job:" + job.Func + ":name", job.Name)
    if jobId > 0 {
        job.Id = jobId
        if oldJob, e := db.GetJob(jobId); e == nil && oldJob.Status == db.JOB_STATUS_PROC {
            sched.DecrStatProc(oldJob)
        }
        is_new = false
    }
    e = job.Save()
    if e != nil {
        err = conn.Send([]byte(e.Error()))
        return
    }
    if is_new {
        sched.IncrStatJob(job)
    }
    sched.Notify()
    err = conn.Send([]byte("ok"))
    return
}

func (client *Client) HandleStatus() (err error) {
    var conn = client.conn
    var sched = client.sched
    data, _ := json.Marshal(sched.Funcs)
    err = conn.Send(data)
    return
}
<|endoftext|>


<commit_before>package cmd

import (
	"fmt"

	"github.com/spf13/cobra"
)

// lsCmd represents the ls command
var lsCmd = &cobra.Command{
	Use:   "ls",
	Short: "List files in the repository",
	Long: "List files in the repository",
	Run: func(cmd *cobra.Command, args []string) {
		// TODO: Work your own magic here
		fmt.Println("ls called")
	},
}

func init() {
	RootCmd.AddCommand(lsCmd)
}
<commit_msg>Skeleton for list<commit_after>package cmd

import (
	"fmt"

	"github.com/fwessels/s3git-go"
	"github.com/spf13/cobra"
)

// lsCmd represents the ls command
var lsCmd = &cobra.Command{
	Use:   "ls",
	Short: "List files in the repository",
	Long: "List files in the repository",
	Run: func(cmd *cobra.Command, args []string) {

		repo, err := s3git.OpenRepository(".")
		if err != nil {
			fmt.Println(err)
		}

		for elem := range repo.List() {
			fmt.Println(elem)
		}
	},
}

func init() {
	RootCmd.AddCommand(lsCmd)
}
<|endoftext|>


<commit_before>package httphead

import (
	"bytes"
	"sort"
)

// Option represents a header option.
type Option struct {
	Name       []byte
	Parameters Parameters
}

// Size returns number of bytes need to be allocated for use in opt.Copy.
func (opt Option) Size() int {
	return len(opt.Name) + opt.Parameters.bytes
}

// Copy copies all underlying []byte slices into p and returns new Option.
// Note that p must be at least of opt.Size() length.
func (opt Option) Copy(p []byte) Option {
	n := copy(p, opt.Name)
	opt.Name = p[:n]
	opt.Parameters, p = opt.Parameters.Copy(p[n:])
	return opt
}

// String represents option as a string.
func (opt Option) String() string {
	return "{" + string(opt.Name) + " " + opt.Parameters.String() + "}"
}

// NewOption creates named option with given parameters.
func NewOption(name string, params map[string]string) Option {
	p := Parameters{}
	for k, v := range params {
		p.Set([]byte(k), []byte(v))
	}
	return Option{
		Name:       []byte(name),
		Parameters: p,
	}
}

// Equal reports whether option is equal to b.
func (opt Option) Equal(b Option) bool {
	if bytes.Equal(opt.Name, b.Name) {
		return opt.Parameters.Equal(b.Parameters)
	}
	return false
}

// Parameters represents option's parameters.
type Parameters struct {
	pos   int
	bytes int
	arr   [8]pair
	dyn   []pair
}

// Equal reports whether a equal to b.
func (p Parameters) Equal(b Parameters) bool {
	switch {
	case p.dyn == nil && b.dyn == nil:
	case p.dyn != nil && b.dyn != nil:
	default:
		return false
	}

	ad, bd := p.data(), b.data()
	if len(ad) != len(bd) {
		return false
	}

	sort.Sort(pairs(ad))
	sort.Sort(pairs(bd))

	for i := 0; i < len(ad); i++ {
		av, bv := ad[i], bd[i]
		if !bytes.Equal(av.key, bv.key) || !bytes.Equal(av.value, bv.value) {
			return false
		}
	}
	return true
}

// Size returns number of bytes that needed to copy p.
func (p *Parameters) Size() int {
	return p.bytes
}

// Copy copies all underlying []byte slices into dst and returns new
// Parameters.
// Note that dst must be at least of p.Size() length.
func (p *Parameters) Copy(dst []byte) (Parameters, []byte) {
	ret := Parameters{
		pos:   p.pos,
		bytes: p.bytes,
	}
	if p.dyn != nil {
		ret.dyn = make([]pair, len(p.dyn))
		for i, v := range p.dyn {
			ret.dyn[i], dst = v.copy(dst)
		}
	} else {
		for i, p := range p.arr {
			ret.arr[i], dst = p.copy(dst)
		}
	}
	return ret, dst
}

// Get returns value by key and flag about existence such value.
func (p *Parameters) Get(key string) (value []byte, ok bool) {
	for _, v := range p.data() {
		if string(v.key) == key {
			return v.value, true
		}
	}
	return nil, false
}

// Set sets value by key.
func (p *Parameters) Set(key, value []byte) {
	p.bytes += len(key) + len(value)

	if p.pos < len(p.arr) {
		p.arr[p.pos] = pair{key, value}
		p.pos++
		return
	}

	if p.dyn == nil {
		p.dyn = make([]pair, len(p.arr), len(p.arr)+1)
		copy(p.dyn, p.arr[:])
	}
	p.dyn = append(p.dyn, pair{key, value})
}

// ForEach iterates over parameters key-value pairs and calls cb for each one.
func (p *Parameters) ForEach(cb func(k, v []byte) bool) {
	for _, v := range p.data() {
		if !cb(v.key, v.value) {
			break
		}
	}
}

// String represents parameters as a string.
func (p *Parameters) String() (ret string) {
	ret = "["
	for i, v := range p.data() {
		if i > 0 {
			ret += " "
		}
		ret += string(v.key) + ":" + string(v.value)
	}
	return ret + "]"
}

func (p *Parameters) data() []pair {
	if p.dyn != nil {
		return p.dyn
	}
	return p.arr[:p.pos]
}

type pair struct {
	key, value []byte
}

func (p pair) copy(dst []byte) (pair, []byte) {
	n := copy(dst, p.key)
	p.key = dst[:n]
	m := n + copy(dst[n:], p.value)
	p.value = dst[n:m]

	dst = dst[m:]

	return p, dst
}

type pairs []pair

func (p pairs) Len() int           { return len(p) }
func (p pairs) Less(a, b int) bool { return bytes.Compare(p[a].key, p[b].key) == -1 }
func (p pairs) Swap(a, b int)      { p[a], p[b] = p[b], p[a] }
<commit_msg>option: Clone() method<commit_after>package httphead

import (
	"bytes"
	"sort"
)

// Option represents a header option.
type Option struct {
	Name       []byte
	Parameters Parameters
}

// Size returns number of bytes need to be allocated for use in opt.Copy.
func (opt Option) Size() int {
	return len(opt.Name) + opt.Parameters.bytes
}

// Copy copies all underlying []byte slices into p and returns new Option.
// Note that p must be at least of opt.Size() length.
func (opt Option) Copy(p []byte) Option {
	n := copy(p, opt.Name)
	opt.Name = p[:n]
	opt.Parameters, p = opt.Parameters.Copy(p[n:])
	return opt
}

// Clone is a shorthand for making slice of opt.Size() sequenced with Copy()
// call.
func (opt Option) Clone() Option {
	return opt.Copy(make([]byte, opt.Size()))
}

// String represents option as a string.
func (opt Option) String() string {
	return "{" + string(opt.Name) + " " + opt.Parameters.String() + "}"
}

// NewOption creates named option with given parameters.
func NewOption(name string, params map[string]string) Option {
	p := Parameters{}
	for k, v := range params {
		p.Set([]byte(k), []byte(v))
	}
	return Option{
		Name:       []byte(name),
		Parameters: p,
	}
}

// Equal reports whether option is equal to b.
func (opt Option) Equal(b Option) bool {
	if bytes.Equal(opt.Name, b.Name) {
		return opt.Parameters.Equal(b.Parameters)
	}
	return false
}

// Parameters represents option's parameters.
type Parameters struct {
	pos   int
	bytes int
	arr   [8]pair
	dyn   []pair
}

// Equal reports whether a equal to b.
func (p Parameters) Equal(b Parameters) bool {
	switch {
	case p.dyn == nil && b.dyn == nil:
	case p.dyn != nil && b.dyn != nil:
	default:
		return false
	}

	ad, bd := p.data(), b.data()
	if len(ad) != len(bd) {
		return false
	}

	sort.Sort(pairs(ad))
	sort.Sort(pairs(bd))

	for i := 0; i < len(ad); i++ {
		av, bv := ad[i], bd[i]
		if !bytes.Equal(av.key, bv.key) || !bytes.Equal(av.value, bv.value) {
			return false
		}
	}
	return true
}

// Size returns number of bytes that needed to copy p.
func (p *Parameters) Size() int {
	return p.bytes
}

// Copy copies all underlying []byte slices into dst and returns new
// Parameters.
// Note that dst must be at least of p.Size() length.
func (p *Parameters) Copy(dst []byte) (Parameters, []byte) {
	ret := Parameters{
		pos:   p.pos,
		bytes: p.bytes,
	}
	if p.dyn != nil {
		ret.dyn = make([]pair, len(p.dyn))
		for i, v := range p.dyn {
			ret.dyn[i], dst = v.copy(dst)
		}
	} else {
		for i, p := range p.arr {
			ret.arr[i], dst = p.copy(dst)
		}
	}
	return ret, dst
}

// Get returns value by key and flag about existence such value.
func (p *Parameters) Get(key string) (value []byte, ok bool) {
	for _, v := range p.data() {
		if string(v.key) == key {
			return v.value, true
		}
	}
	return nil, false
}

// Set sets value by key.
func (p *Parameters) Set(key, value []byte) {
	p.bytes += len(key) + len(value)

	if p.pos < len(p.arr) {
		p.arr[p.pos] = pair{key, value}
		p.pos++
		return
	}

	if p.dyn == nil {
		p.dyn = make([]pair, len(p.arr), len(p.arr)+1)
		copy(p.dyn, p.arr[:])
	}
	p.dyn = append(p.dyn, pair{key, value})
}

// ForEach iterates over parameters key-value pairs and calls cb for each one.
func (p *Parameters) ForEach(cb func(k, v []byte) bool) {
	for _, v := range p.data() {
		if !cb(v.key, v.value) {
			break
		}
	}
}

// String represents parameters as a string.
func (p *Parameters) String() (ret string) {
	ret = "["
	for i, v := range p.data() {
		if i > 0 {
			ret += " "
		}
		ret += string(v.key) + ":" + string(v.value)
	}
	return ret + "]"
}

func (p *Parameters) data() []pair {
	if p.dyn != nil {
		return p.dyn
	}
	return p.arr[:p.pos]
}

type pair struct {
	key, value []byte
}

func (p pair) copy(dst []byte) (pair, []byte) {
	n := copy(dst, p.key)
	p.key = dst[:n]
	m := n + copy(dst[n:], p.value)
	p.value = dst[n:m]

	dst = dst[m:]

	return p, dst
}

type pairs []pair

func (p pairs) Len() int           { return len(p) }
func (p pairs) Less(a, b int) bool { return bytes.Compare(p[a].key, p[b].key) == -1 }
func (p pairs) Swap(a, b int)      { p[a], p[b] = p[b], p[a] }
<|endoftext|>


<commit_before>package cli

import (
	flag "github.com/docker/docker/pkg/mflag"
	"github.com/docker/go-connections/tlsconfig"
)

// CommonFlags represents flags that are common to both the client and the daemon.
type CommonFlags struct {
	FlagSet   *flag.FlagSet
	PostParse func()

	Debug      bool
	Hosts      []string
	LogLevel   string
	TLS        bool
	TLSVerify  bool
	TLSOptions *tlsconfig.Options
	TrustKey   string
}

// Command is the struct containing the command name and description
type Command struct {
	Name        string
	Description string
}

var dockerCommands = []Command{
	{"attach", "Attach to a running container"},
	{"build", "Build an image from a Dockerfile"},
	{"commit", "Create a new image from a container's changes"},
	{"cp", "Copy files/folders between a container and the local filesystem"},
	{"create", "Create a new container"},
	{"diff", "Inspect changes on a container's filesystem"},
	{"events", "Get real time events from the server"},
	{"exec", "Run a command in a running container"},
	{"export", "Export a container's filesystem as a tar archive"},
	{"history", "Show the history of an image"},
	{"images", "List images"},
	{"import", "Import the contents from a tarball to create a filesystem image"},
	{"info", "Display system-wide information"},
	{"inspect", "Return low-level information on a container or image"},
	{"kill", "Kill a running container"},
	{"load", "Load an image from a tar archive or STDIN"},
	{"login", "Register or log in to a Docker registry"},
	{"logout", "Log out from a Docker registry"},
	{"logs", "Fetch the logs of a container"},
	{"network", "Manage Docker networks"},
	{"pause", "Pause all processes within a container"},
	{"port", "List port mappings or a specific mapping for the CONTAINER"},
	{"ps", "List containers"},
	{"pull", "Pull an image or a repository from a registry"},
	{"push", "Push an image or a repository to a registry"},
	{"rename", "Rename a container"},
	{"restart", "Restart a container"},
	{"rm", "Remove one or more containers"},
	{"rmi", "Remove one or more images"},
	{"run", "Run a command in a new container"},
	{"save", "Save an image(s) to a tar archive"},
	{"search", "Search the Docker Hub for images"},
	{"start", "Start one or more stopped containers"},
	{"stats", "Display a live stream of container(s) resource usage statistics"},
	{"stop", "Stop a running container"},
	{"tag", "Tag an image into a repository"},
	{"top", "Display the running processes of a container"},
	{"unpause", "Unpause all processes within a container"},
	{"update", "Update configuration of one or more containers"},
	{"version", "Show the Docker version information"},
	{"volume", "Manage Docker volumes"},
	{"wait", "Block until a container stops, then print its exit code"},
}

// DockerCommands stores all the docker command
var DockerCommands = make(map[string]Command)

func init() {
	for _, cmd := range dockerCommands {
		DockerCommands[cmd.Name] = cmd
	}
}
<commit_msg>Remove some references to "register" through login<commit_after>package cli

import (
	flag "github.com/docker/docker/pkg/mflag"
	"github.com/docker/go-connections/tlsconfig"
)

// CommonFlags represents flags that are common to both the client and the daemon.
type CommonFlags struct {
	FlagSet   *flag.FlagSet
	PostParse func()

	Debug      bool
	Hosts      []string
	LogLevel   string
	TLS        bool
	TLSVerify  bool
	TLSOptions *tlsconfig.Options
	TrustKey   string
}

// Command is the struct containing the command name and description
type Command struct {
	Name        string
	Description string
}

var dockerCommands = []Command{
	{"attach", "Attach to a running container"},
	{"build", "Build an image from a Dockerfile"},
	{"commit", "Create a new image from a container's changes"},
	{"cp", "Copy files/folders between a container and the local filesystem"},
	{"create", "Create a new container"},
	{"diff", "Inspect changes on a container's filesystem"},
	{"events", "Get real time events from the server"},
	{"exec", "Run a command in a running container"},
	{"export", "Export a container's filesystem as a tar archive"},
	{"history", "Show the history of an image"},
	{"images", "List images"},
	{"import", "Import the contents from a tarball to create a filesystem image"},
	{"info", "Display system-wide information"},
	{"inspect", "Return low-level information on a container or image"},
	{"kill", "Kill a running container"},
	{"load", "Load an image from a tar archive or STDIN"},
	{"login", "Log in to a Docker registry"},
	{"logout", "Log out from a Docker registry"},
	{"logs", "Fetch the logs of a container"},
	{"network", "Manage Docker networks"},
	{"pause", "Pause all processes within a container"},
	{"port", "List port mappings or a specific mapping for the CONTAINER"},
	{"ps", "List containers"},
	{"pull", "Pull an image or a repository from a registry"},
	{"push", "Push an image or a repository to a registry"},
	{"rename", "Rename a container"},
	{"restart", "Restart a container"},
	{"rm", "Remove one or more containers"},
	{"rmi", "Remove one or more images"},
	{"run", "Run a command in a new container"},
	{"save", "Save an image(s) to a tar archive"},
	{"search", "Search the Docker Hub for images"},
	{"start", "Start one or more stopped containers"},
	{"stats", "Display a live stream of container(s) resource usage statistics"},
	{"stop", "Stop a running container"},
	{"tag", "Tag an image into a repository"},
	{"top", "Display the running processes of a container"},
	{"unpause", "Unpause all processes within a container"},
	{"update", "Update configuration of one or more containers"},
	{"version", "Show the Docker version information"},
	{"volume", "Manage Docker volumes"},
	{"wait", "Block until a container stops, then print its exit code"},
}

// DockerCommands stores all the docker command
var DockerCommands = make(map[string]Command)

func init() {
	for _, cmd := range dockerCommands {
		DockerCommands[cmd.Name] = cmd
	}
}
<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"encoding/json"
	"fmt"
	"github.com/kless/validate"
	c "github.com/wsxiaoys/terminal/color"
	"io/ioutil"
	"os"
	"path/filepath"
	"regexp"
	"strings"
)

// Global variables used in the whole application
var (
	GOPATH      = os.Getenv("GOPATH")
	SRCPATH     = filepath.Join(GOPATH, "src")
	HOME        = os.Getenv("HOME")
	GOBI_CONFIG = filepath.Join(HOME, ".gobi.json")
	GITHUB      = "github.com"
	BITBUCKET   = "bitbucket.org"
	GOOGLE      = "code.google.com"
	GOBIPATH    = filepath.Join(SRCPATH, GITHUB, "fern4lvarez", "gobi")
)

// UserConfig contains all information about the current user
type UserConfig struct {
	Name    string `json:"name"`
	Id      string `json:"id"`
	Host    string `json:"host"`
	Email   string `json:"email"`
	License string `json:"license"`
}

// NewConfig promps a form and returns a UserConfig object
// based on the answers
// A JSON file is stored at $HOME/.gobi.json with this content
func NewConfig() *UserConfig {
	var name, username, host, email, license string
	reader := bufio.NewReader(os.Stdin)

	c.Println("@{!y}No configuration found! @bI'd like to know more about you.")
	// Name
	c.Print("@{!b}Name: ")
	name, _ = reader.ReadString('\n')
	name = strings.TrimSpace(name)
	for !validateName(name) {
		c.Println("@{!y}Please insert your name.")
		c.Print("@{!b}Name: ")
		name, _ = reader.ReadString('\n')
		name = strings.TrimSpace(name)
	}
	// Username
	c.Print("@{!b}Username: ")
	username, _ = reader.ReadString('\n')
	username = strings.TrimSpace(username)
	for !validateUserName(username) {
		reader = bufio.NewReader(os.Stdin)
		c.Println("@{!y}Wrong username, try again.")
		c.Print("@{!b}Username: ")
		username, _ = reader.ReadString('\n')
		username = strings.TrimSpace(username)
	}
	// Host
	c.Print("@{!b}Host @b(github.com, bitbucket.org or code.google.com)@{!b}: ")
	host, _ = reader.ReadString('\n')
	host = strings.TrimSpace(host)
	for !validateHost(strings.TrimSpace(host)) {
		reader = bufio.NewReader(os.Stdin)
		c.Println("@{!y}Invalid host, try again. @yOptions: github.com, bitbucket.org or code.google.com")
		c.Print("@{!b}Host: ")
		host, _ = reader.ReadString('\n')
		host = strings.TrimSpace(host)
	}
	// Email
	c.Print("@{!b}Email: ")
	email, _ = reader.ReadString('\n')
	email = strings.TrimSpace(email)
	for !validateEmail(email) {
		reader = bufio.NewReader(os.Stdin)
		c.Println("@{!y}Invalid Email address, try again.")
		c.Print("@{!b}Email: ")
		email, _ = reader.ReadString('\n')
		email = strings.TrimSpace(email)
	}
	// License
	c.Print("@{!b}License: ")
	license, _ = reader.ReadString('\n')
	license = strings.TrimSpace(license)
	for !validateLicense(license) {
		reader = bufio.NewReader(os.Stdin)
		c.Println("@{!y}Invalid license, try again. @yOptions: AGPL, Apache, BSD, BSD3-Clause, Eclipse, GPLv2, GPLv3, LGPLv2.1, LGPLv3, MIT, Mozilla, PublicDomain, no-license.")
		c.Print("@{!b}License: ")
		license, _ = reader.ReadString('\n')
		license = strings.TrimSpace(license)
	}
	// User config creation
	conf := &UserConfig{name, username, host, email, license}
	b, _ := json.Marshal(conf)
	ioutil.WriteFile(GOBI_CONFIG, []byte(b), 0744)
	return conf
}

// WhoAreYou pretty prints the UserConfig
func (uc UserConfig) WhoAreYou() {
	c.Printf("@bYou are @{!g}%s @b(@{!g}%s@b)@b. Creating projects on @{!g}%s/%s @bunder @{!g}%s @blicense.\n",
		uc.Name, uc.Email, uc.Host, uc.Id, uc.License)
}

// checkConfig if JSON config file exists and returns the UserConfig if so
func checkConfig() UserConfig {
	var user UserConfig
	b, errRead := ioutil.ReadFile(GOBI_CONFIG)
	if errUnmarshal := json.Unmarshal(b, &user); errRead != nil || errUnmarshal != nil {
		return *NewConfig()
	} else {
		return user
	}
	return UserConfig{}
}

// validateName: Cannot be empty
func validateName(name string) bool {
	return name != ""
}

// validateUserName: Cannot be empty, only one path level
func validateUserName(username string) bool {
	return username != "" && !strings.Contains(username, "/") && !strings.Contains(username, " ")
}

// validateHost: Can only be GITHUB, BITBUCKET OR GOOGLE
func validateHost(host string) bool {
	hosts := []string{GITHUB, BITBUCKET, GOOGLE}
	for i := range hosts {
		if strings.EqualFold(host, hosts[i]) {
			return true
		}
	}
	return false
}

// validateEmail: Must have a correct email format
func validateEmail(email string) bool {
	exp, err := regexp.Compile(validate.RE_BASIC_EMAIL)
	if err != nil {
		fmt.Println(err)
	}
	return exp.MatchString(email)
}

// validateLicense: Must be one of the supported licenses
func validateLicense(license string) bool {
	licenses := []string{"AGPL", "Apache", "BSD", "BSD3-Clause", "Eclipse",
		"GPLv2", "GPLv3", "LGPLv2.1", "LGPLv3",
		"MIT", "Mozilla", "PublicDomain", "no-license"}
	for i := range licenses {
		if strings.EqualFold(license, licenses[i]) {
			return true
		}
	}
	return false
}
<commit_msg>Refactor config creation<commit_after>package main

import (
	"bufio"
	"encoding/json"
	"fmt"
	"github.com/kless/validate"
	c "github.com/wsxiaoys/terminal/color"
	"io/ioutil"
	"os"
	"path/filepath"
	"regexp"
	"strings"
)

// Global variables used in the whole application
var (
	GOPATH      = os.Getenv("GOPATH")
	SRCPATH     = filepath.Join(GOPATH, "src")
	HOME        = os.Getenv("HOME")
	GOBI_CONFIG = filepath.Join(HOME, ".gobi.json")
	GITHUB      = "github.com"
	BITBUCKET   = "bitbucket.org"
	GOOGLE      = "code.google.com"
	GOBIPATH    = filepath.Join(SRCPATH, GITHUB, "fern4lvarez", "gobi")
)

// UserConfig contains all information about the current user
type UserConfig struct {
	Name    string `json:"name"`
	Id      string `json:"id"`
	Host    string `json:"host"`
	Email   string `json:"email"`
	License string `json:"license"`
}

// NewConfig promps a form and returns a UserConfig object
// based on the answers
// A JSON file is stored at $HOME/.gobi.json with this content
func NewConfig() *UserConfig {
	c.Println("@{!y}No configuration found! @bI'd like to know more about you.")

	// Prompted user configuration form
	var name, userName, host, email, license string
	name = promptField(validateName,
		"@{!b}Name: ",
		"@{!y}Please insert your name.",
		"@{!b}Name: ")
	userName = promptField(validateUserName,
		"@{!b}Username: ",
		"@{!y}Wrong username, try again.",
		"@{!b}Username: ")
	host = promptField(validateHost,
		"@{!b}Host @b(github.com, bitbucket.org or code.google.com)@{!b}: ",
		"@{!y}Invalid host, try again. @yOptions: github.com, bitbucket.org or code.google.com",
		"@{!b}Host: ")
	email = promptField(validateEmail,
		"@{!b}Email: ",
		"@{!y}Invalid Email address, try again.",
		"@{!b}Email: ")
	license = promptField(validateLicense,
		"@{!b}License: ",
		"@{!y}Invalid license, try again. @yOptions: AGPL, Apache, BSD, BSD3-Clause, Eclipse, GPLv2, GPLv3, LGPLv2.1, LGPLv3, MIT, Mozilla, PublicDomain, no-license.",
		"@{!b}License: ")

	// User config creation
	conf := &UserConfig{name, userName, host, email, license}
	b, _ := json.Marshal(conf)
	ioutil.WriteFile(GOBI_CONFIG, []byte(b), 0744)
	return conf
}

// WhoAreYou pretty prints the UserConfig
func (uc UserConfig) WhoAreYou() {
	c.Printf("@bYou are @{!g}%s @b(@{!g}%s@b)@b. Creating projects on @{!g}%s/%s @bunder @{!g}%s @blicense.\n",
		uc.Name, uc.Email, uc.Host, uc.Id, uc.License)
}

// checkConfig if JSON config file exists and returns the UserConfig if so
func checkConfig() UserConfig {
	var user UserConfig
	b, errRead := ioutil.ReadFile(GOBI_CONFIG)
	if errUnmarshal := json.Unmarshal(b, &user); errRead != nil || errUnmarshal != nil {
		return *NewConfig()
	} else {
		return user
	}
	return UserConfig{}
}

// promptField to validate and save input value
func promptField(validateFunc func(string) bool, welcomeMsg, errorMsg, welcome2Msg string) (resp string) {
	reader := bufio.NewReader(os.Stdin)
	c.Print(welcomeMsg)
	resp, _ = reader.ReadString('\n')
	resp = strings.TrimSpace(resp)
	for !validateFunc(resp) {
		c.Println(errorMsg)
		c.Print(welcome2Msg)
		resp, _ = reader.ReadString('\n')
		resp = strings.TrimSpace(resp)
	}
	return
}

// validateName: Cannot be empty
func validateName(name string) bool {
	return name != ""
}

// validateUserName: Cannot be empty, only one path level
func validateUserName(username string) bool {
	return username != "" && !strings.Contains(username, "/") && !strings.Contains(username, " ")
}

// validateHost: Can only be GITHUB, BITBUCKET OR GOOGLE
func validateHost(host string) bool {
	hosts := []string{GITHUB, BITBUCKET, GOOGLE}
	for i := range hosts {
		if strings.EqualFold(host, hosts[i]) {
			return true
		}
	}
	return false
}

// validateEmail: Must have a correct email format
func validateEmail(email string) bool {
	exp, err := regexp.Compile(validate.RE_BASIC_EMAIL)
	if err != nil {
		fmt.Println(err)
	}
	return exp.MatchString(email)
}

// validateLicense: Must be one of the supported licenses
func validateLicense(license string) bool {
	licenses := []string{"AGPL", "Apache", "BSD", "BSD3-Clause", "Eclipse",
		"GPLv2", "GPLv3", "LGPLv2.1", "LGPLv3",
		"MIT", "Mozilla", "PublicDomain", "no-license"}
	for i := range licenses {
		if strings.EqualFold(license, licenses[i]) {
			return true
		}
	}
	return false
}
<|endoftext|>


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

package main

import (
	"os"
	"os/user"
	"path"
	"runtime"

	"github.com/minio/mc/pkg/quick"
	"github.com/minio/minio/pkg/iodine"
)

type configV1 struct {
	Version string
	Aliases map[string]string
	Hosts   map[string]*hostConfig
}

// cached variables should *NEVER* be accessed directly from outside this file.
var cache struct {
	config       quick.Config
	configLoaded bool // set to true if cache is valid.
}

// customConfigDir used internally only by test functions
var customConfigDir string

// getMcConfigDir - construct minio client config folder
func getMcConfigDir() (string, error) {
	if customConfigDir != "" {
		// For windows the path is slightly different
		switch runtime.GOOS {
		case "windows":
			return path.Join(customConfigDir, mcConfigWindowsDir), nil
		default:
			return path.Join(customConfigDir, mcConfigDir), nil
		}
	}
	u, err := user.Current()
	if err != nil {
		return "", iodine.New(err, nil)
	}
	// For windows the path is slightly different
	switch runtime.GOOS {
	case "windows":
		return path.Join(u.HomeDir, mcConfigWindowsDir), nil
	default:
		return path.Join(u.HomeDir, mcConfigDir), nil
	}
}

// createMcConfigDir - create minio client config folder
func createMcConfigDir() error {
	p, err := getMcConfigDir()
	if err != nil {
		return iodine.New(err, nil)
	}
	err = os.MkdirAll(p, 0700)
	if err != nil {
		return iodine.New(err, nil)
	}
	return nil
}

// getMcConfigPath - construct minio client configuration path
func getMcConfigPath() (string, error) {
	dir, err := getMcConfigDir()
	if err != nil {
		return "", iodine.New(err, nil)
	}
	return path.Join(dir, mcConfigFile), nil
}

// mustGetMcConfigPath - similar to getMcConfigPath, ignores errors
func mustGetMcConfigPath() string {
	p, _ := getMcConfigPath()
	return p
}

// getMcConfig - reads configuration file and returns config
func getMcConfig() (*configV1, error) {
	if !isMcConfigExist() {
		return nil, iodine.New(errInvalidArgument{}, nil)
	}

	configFile, err := getMcConfigPath()
	if err != nil {
		return nil, iodine.New(err, nil)
	}

	// Cached in private global variable.
	if cache.configLoaded { // Use previously cached config.
		return cache.config.Data().(*configV1), nil
	}

	conf := newConfigV1()
	cache.config, err = quick.New(conf)
	if err != nil {
		return nil, iodine.New(err, nil)
	}

	err = cache.config.Load(configFile)
	if err != nil {
		return nil, iodine.New(err, nil)
	}
	cache.configLoaded = true

	return cache.config.Data().(*configV1), nil

}

// isMcConfigExist returns err if config doesn't exist
func isMcConfigExist() bool {
	configFile, err := getMcConfigPath()
	if err != nil {
		return false
	}
	_, err = os.Stat(configFile)
	if err != nil {
		return false
	}
	return true
}

// writeConfig - write configuration file
func writeConfig(config quick.Config) error {
	err := createMcConfigDir()
	if err != nil {
		return iodine.New(err, nil)
	}
	configPath, err := getMcConfigPath()
	if err != nil {
		return iodine.New(err, nil)
	}
	if err := config.Save(configPath); err != nil {
		return iodine.New(err, nil)
	}
	return nil
}

// newConfigV1() - get new config version 1.0
func newConfigV1() *configV1 {
	conf := new(configV1)
	conf.Version = mcCurrentConfigVersion
	// make sure to allocate map's otherwise Golang
	// exists silently without providing any errors
	conf.Hosts = make(map[string]*hostConfig)
	conf.Aliases = make(map[string]string)
	return conf
}

// newConfig - get new config interface
func newConfig() (config quick.Config, err error) {
	conf := newConfigV1()
	s3HostConf := new(hostConfig)
	s3HostConf.AccessKeyID = ""
	s3HostConf.SecretAccessKey = ""

	playHostConfig := new(hostConfig)
	playHostConfig.AccessKeyID = ""
	playHostConfig.SecretAccessKey = ""

	dlHostConfig := new(hostConfig)
	dlHostConfig.AccessKeyID = ""
	dlHostConfig.SecretAccessKey = ""

	// Your example host config
	exampleHostConf := new(hostConfig)
	exampleHostConf.AccessKeyID = globalAccessKeyID
	exampleHostConf.SecretAccessKey = globalSecretAccessKey

	conf.Hosts[exampleHostURL] = exampleHostConf
	conf.Hosts["s3*.amazonaws.com"] = s3HostConf
	conf.Hosts["play.minio.io:9000"] = playHostConfig
	conf.Hosts["dl.minio.io:9000"] = dlHostConfig

	aliases := make(map[string]string)
	aliases["s3"] = "https://s3.amazonaws.com"
	aliases["play"] = "http://play.minio.io:9000"
	aliases["dl"] = "http://dl.minio.io:9000"
	aliases["localhost"] = "http://localhost:9000"
	conf.Aliases = aliases
	config, err = quick.New(conf)
	if err != nil {
		return nil, iodine.New(err, nil)
	}

	return config, nil
}
<commit_msg>default access and secret key notice<commit_after>/*
 * Minio Client (C) 2015 Minio, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package main

import (
	"os"
	"os/user"
	"path"
	"runtime"

	"github.com/minio/mc/pkg/quick"
	"github.com/minio/minio/pkg/iodine"
)

type configV1 struct {
	Version string
	Aliases map[string]string
	Hosts   map[string]*hostConfig
}

// cached variables should *NEVER* be accessed directly from outside this file.
var cache struct {
	config       quick.Config
	configLoaded bool // set to true if cache is valid.
}

// customConfigDir used internally only by test functions
var customConfigDir string

// getMcConfigDir - construct minio client config folder
func getMcConfigDir() (string, error) {
	if customConfigDir != "" {
		// For windows the path is slightly different
		switch runtime.GOOS {
		case "windows":
			return path.Join(customConfigDir, mcConfigWindowsDir), nil
		default:
			return path.Join(customConfigDir, mcConfigDir), nil
		}
	}
	u, err := user.Current()
	if err != nil {
		return "", iodine.New(err, nil)
	}
	// For windows the path is slightly different
	switch runtime.GOOS {
	case "windows":
		return path.Join(u.HomeDir, mcConfigWindowsDir), nil
	default:
		return path.Join(u.HomeDir, mcConfigDir), nil
	}
}

// createMcConfigDir - create minio client config folder
func createMcConfigDir() error {
	p, err := getMcConfigDir()
	if err != nil {
		return iodine.New(err, nil)
	}
	err = os.MkdirAll(p, 0700)
	if err != nil {
		return iodine.New(err, nil)
	}
	return nil
}

// getMcConfigPath - construct minio client configuration path
func getMcConfigPath() (string, error) {
	dir, err := getMcConfigDir()
	if err != nil {
		return "", iodine.New(err, nil)
	}
	return path.Join(dir, mcConfigFile), nil
}

// mustGetMcConfigPath - similar to getMcConfigPath, ignores errors
func mustGetMcConfigPath() string {
	p, _ := getMcConfigPath()
	return p
}

// getMcConfig - reads configuration file and returns config
func getMcConfig() (*configV1, error) {
	if !isMcConfigExist() {
		return nil, iodine.New(errInvalidArgument{}, nil)
	}

	configFile, err := getMcConfigPath()
	if err != nil {
		return nil, iodine.New(err, nil)
	}

	// Cached in private global variable.
	if cache.configLoaded { // Use previously cached config.
		return cache.config.Data().(*configV1), nil
	}

	conf := newConfigV1()
	cache.config, err = quick.New(conf)
	if err != nil {
		return nil, iodine.New(err, nil)
	}

	err = cache.config.Load(configFile)
	if err != nil {
		return nil, iodine.New(err, nil)
	}
	cache.configLoaded = true

	return cache.config.Data().(*configV1), nil

}

// isMcConfigExist returns err if config doesn't exist
func isMcConfigExist() bool {
	configFile, err := getMcConfigPath()
	if err != nil {
		return false
	}
	_, err = os.Stat(configFile)
	if err != nil {
		return false
	}
	return true
}

// writeConfig - write configuration file
func writeConfig(config quick.Config) error {
	err := createMcConfigDir()
	if err != nil {
		return iodine.New(err, nil)
	}
	configPath, err := getMcConfigPath()
	if err != nil {
		return iodine.New(err, nil)
	}
	if err := config.Save(configPath); err != nil {
		return iodine.New(err, nil)
	}
	return nil
}

// newConfigV1() - get new config version 1.0
func newConfigV1() *configV1 {
	conf := new(configV1)
	conf.Version = mcCurrentConfigVersion
	// make sure to allocate map's otherwise Golang
	// exists silently without providing any errors
	conf.Hosts = make(map[string]*hostConfig)
	conf.Aliases = make(map[string]string)
	return conf
}

// newConfig - get new config interface
func newConfig() (config quick.Config, err error) {
	conf := newConfigV1()
	s3HostConf := new(hostConfig)
	s3HostConf.AccessKeyID = globalAccessKeyID
	s3HostConf.SecretAccessKey = globalSecretAccessKey

	playHostConfig := new(hostConfig)
	playHostConfig.AccessKeyID = ""
	playHostConfig.SecretAccessKey = ""

	dlHostConfig := new(hostConfig)
	dlHostConfig.AccessKeyID = ""
	dlHostConfig.SecretAccessKey = ""

	// Your example host config
	exampleHostConf := new(hostConfig)
	exampleHostConf.AccessKeyID = globalAccessKeyID
	exampleHostConf.SecretAccessKey = globalSecretAccessKey

	conf.Hosts[exampleHostURL] = exampleHostConf
	conf.Hosts["s3*.amazonaws.com"] = s3HostConf
	conf.Hosts["play.minio.io:9000"] = playHostConfig
	conf.Hosts["dl.minio.io:9000"] = dlHostConfig

	aliases := make(map[string]string)
	aliases["s3"] = "https://s3.amazonaws.com"
	aliases["play"] = "http://play.minio.io:9000"
	aliases["dl"] = "http://dl.minio.io:9000"
	aliases["localhost"] = "http://localhost:9000"
	conf.Aliases = aliases
	config, err = quick.New(conf)
	if err != nil {
		return nil, iodine.New(err, nil)
	}

	return config, nil
}
<|endoftext|>


<commit_before>package perfM

//Config define the Config about perfM
type Config struct {
	Frequency  int //set for the sampling frequency
	BufferSize int //set for the global time channel buffer size
	BinsNumber int //set the histogram bins number
}
<commit_msg>change into the with style for config<commit_after>package perfM

//Config define the Config about perfM
type Config struct {
	Frequency  int //set for the sampling frequency
	BufferSize int //set for the global time channel buffer size
	BinsNumber int //set the histogram bins number
}

//new gen the config
func NewConfig(options ...Options) Config {
	c := Config{}
	for _, o := range options {
		o(&c)
	}
	return c
}

//Options define the options of congif
type Options func(*Config)

//WithFrequency set the frequency
func WithFrequency(i int) Options {
	return func(o *Config) {
		o.Frequency = i
	}
}

//WithBuffersize set the buffer size of config
func WithBufferSize(i int) Options {
	return func(o *Config) {
		o.BufferSize = i
	}
}

//Withbinsnumber set the bins number of config
func WithBinsNumber(i int) Options {
	return func(o *Config) {
		o.BinsNumber = i
	}
}
<|endoftext|>