<commit_before>// Copyright 2015 CoreOS, 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 client

import (
	"errors"
	"fmt"
	"io/ioutil"
	"net"
	"net/http"
	"net/url"
	"sync"
	"time"

	"github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/net/context"
)

var (
	ErrNoEndpoints           = errors.New("client: no endpoints available")
	ErrTooManyRedirects      = errors.New("client: too many redirects")
	errTooManyRedirectChecks = errors.New("client: too many redirect checks")
)

var DefaultRequestTimeout = 5 * time.Second

var DefaultTransport CancelableTransport = &http.Transport{
	Proxy: http.ProxyFromEnvironment,
	Dial: (&net.Dialer{
		Timeout:   30 * time.Second,
		KeepAlive: 30 * time.Second,
	}).Dial,
	TLSHandshakeTimeout: 10 * time.Second,
}

type Config struct {
	// Endpoints defines a set of URLs (schemes, hosts and ports only)
	// that can be used to communicate with a logical etcd cluster. For
	// example, a three-node cluster could be provided like so:
	//
	// 	Endpoints: []string{
	//		"http://node1.example.com:4001",
	//		"http://node2.example.com:2379",
	//		"http://node3.example.com:4001",
	//	}
	//
	// If multiple endpoints are provided, the Client will attempt to
	// use them all in the event that one or more of them are unusable.
	//
	// If Client.Sync is ever called, the Client may cache an alternate
	// set of endpoints to continue operation.
	Endpoints []string

	// Transport is used by the Client to drive HTTP requests. If not
	// provided, DefaultTransport will be used.
	Transport CancelableTransport

	// CheckRedirect specifies the policy for handling HTTP redirects.
	// If CheckRedirect is not nil, the Client calls it before
	// following an HTTP redirect. The sole argument is the number of
	// requests that have alrady been made. If CheckRedirect returns
	// an error, Client.Do will not make any further requests and return
	// the error back it to the caller.
	//
	// If CheckRedirect is nil, the Client uses its default policy,
	// which is to stop after 10 consecutive requests.
	CheckRedirect CheckRedirectFunc
}

func (cfg *Config) transport() CancelableTransport {
	if cfg.Transport == nil {
		return DefaultTransport
	}
	return cfg.Transport
}

func (cfg *Config) checkRedirect() CheckRedirectFunc {
	if cfg.CheckRedirect == nil {
		return DefaultCheckRedirect
	}
	return cfg.CheckRedirect
}

// CancelableTransport mimics net/http.Transport, but requires that
// the object also support request cancellation.
type CancelableTransport interface {
	http.RoundTripper
	CancelRequest(req *http.Request)
}

type CheckRedirectFunc func(via int) error

// DefaultCheckRedirect follows up to 10 redirects, but no more.
var DefaultCheckRedirect CheckRedirectFunc = func(via int) error {
	if via > 10 {
		return ErrTooManyRedirects
	}
	return nil
}

type Client interface {
	// Sync updates the internal cache of the etcd cluster's membership.
	Sync(context.Context) error

	// Endpoints returns a copy of the current set of API endpoints used
	// by Client to resolve HTTP requests. If Sync has ever been called,
	// this may differ from the initial Endpoints provided in the Config.
	Endpoints() []string

	httpClient
}

func New(cfg Config) (Client, error) {
	c := &httpClusterClient{clientFactory: newHTTPClientFactory(cfg.transport(), cfg.checkRedirect())}
	if err := c.reset(cfg.Endpoints); err != nil {
		return nil, err
	}
	return c, nil
}

type httpClient interface {
	Do(context.Context, httpAction) (*http.Response, []byte, error)
}

func newHTTPClientFactory(tr CancelableTransport, cr CheckRedirectFunc) httpClientFactory {
	return func(ep url.URL) httpClient {
		return &redirectFollowingHTTPClient{
			checkRedirect: cr,
			client: &simpleHTTPClient{
				transport: tr,
				endpoint:  ep,
			},
		}
	}
}

type httpClientFactory func(url.URL) httpClient

type httpAction interface {
	HTTPRequest(url.URL) *http.Request
}

type httpClusterClient struct {
	clientFactory httpClientFactory
	endpoints     []url.URL
	sync.RWMutex
}

func (c *httpClusterClient) reset(eps []string) error {
	if len(eps) == 0 {
		return ErrNoEndpoints
	}

	neps := make([]url.URL, len(eps))
	for i, ep := range eps {
		u, err := url.Parse(ep)
		if err != nil {
			return err
		}
		neps[i] = *u
	}

	c.endpoints = neps

	return nil
}

func (c *httpClusterClient) Do(ctx context.Context, act httpAction) (*http.Response, []byte, error) {
	c.RLock()
	leps := len(c.endpoints)
	eps := make([]url.URL, leps)
	n := copy(eps, c.endpoints)
	c.RUnlock()

	if leps == 0 {
		return nil, nil, ErrNoEndpoints
	}

	if leps != n {
		return nil, nil, errors.New("unable to pick endpoint: copy failed")
	}

	var resp *http.Response
	var body []byte
	var err error

	for _, ep := range eps {
		hc := c.clientFactory(ep)
		resp, body, err = hc.Do(ctx, act)
		if err != nil {
			if err == context.DeadlineExceeded || err == context.Canceled {
				return nil, nil, err
			}
			continue
		}
		if resp.StatusCode/100 == 5 {
			continue
		}
		break
	}

	return resp, body, err
}

func (c *httpClusterClient) Endpoints() []string {
	c.RLock()
	defer c.RUnlock()

	eps := make([]string, len(c.endpoints))
	for i, ep := range c.endpoints {
		eps[i] = ep.String()
	}

	return eps
}

func (c *httpClusterClient) Sync(ctx context.Context) error {
	mAPI := NewMembersAPI(c)
	ms, err := mAPI.List(ctx)
	if err != nil {
		return err
	}

	c.Lock()
	defer c.Unlock()

	eps := make([]string, 0)
	for _, m := range ms {
		eps = append(eps, m.ClientURLs...)
	}

	return c.reset(eps)
}

type roundTripResponse struct {
	resp *http.Response
	err  error
}

type simpleHTTPClient struct {
	transport CancelableTransport
	endpoint  url.URL
}

func (c *simpleHTTPClient) Do(ctx context.Context, act httpAction) (*http.Response, []byte, error) {
	req := act.HTTPRequest(c.endpoint)

	rtchan := make(chan roundTripResponse, 1)
	go func() {
		resp, err := c.transport.RoundTrip(req)
		rtchan <- roundTripResponse{resp: resp, err: err}
		close(rtchan)
	}()

	var resp *http.Response
	var err error

	select {
	case rtresp := <-rtchan:
		resp, err = rtresp.resp, rtresp.err
	case <-ctx.Done():
		// cancel and wait for request to actually exit before continuing
		c.transport.CancelRequest(req)
		rtresp := <-rtchan
		resp = rtresp.resp
		err = ctx.Err()
	}

	// always check for resp nil-ness to deal with possible
	// race conditions between channels above
	defer func() {
		if resp != nil {
			resp.Body.Close()
		}
	}()

	if err != nil {
		return nil, nil, err
	}

	body, err := ioutil.ReadAll(resp.Body)
	return resp, body, err
}

type redirectFollowingHTTPClient struct {
	client        httpClient
	checkRedirect CheckRedirectFunc
}

func (r *redirectFollowingHTTPClient) Do(ctx context.Context, act httpAction) (*http.Response, []byte, error) {
	for i := 0; i < 100; i++ {
		if i > 0 {
			if err := r.checkRedirect(i); err != nil {
				return nil, nil, err
			}
		}
		resp, body, err := r.client.Do(ctx, act)
		if err != nil {
			return nil, nil, err
		}
		if resp.StatusCode/100 == 3 {
			hdr := resp.Header.Get("Location")
			if hdr == "" {
				return nil, nil, fmt.Errorf("Location header not set")
			}
			loc, err := url.Parse(hdr)
			if err != nil {
				return nil, nil, fmt.Errorf("Location header not valid URL: %s", hdr)
			}
			act = &redirectedHTTPAction{
				action:   act,
				location: *loc,
			}
			continue
		}
		return resp, body, nil
	}

	return nil, nil, errTooManyRedirectChecks
}

type redirectedHTTPAction struct {
	action   httpAction
	location url.URL
}

func (r *redirectedHTTPAction) HTTPRequest(ep url.URL) *http.Request {
	orig := r.action.HTTPRequest(ep)
	orig.URL = &r.location
	return orig
}
<commit_msg>client: don't use nested actions<commit_after>// Copyright 2015 CoreOS, 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 client

import (
	"errors"
	"fmt"
	"io/ioutil"
	"net"
	"net/http"
	"net/url"
	"sync"
	"time"

	"github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/net/context"
)

var (
	ErrNoEndpoints           = errors.New("client: no endpoints available")
	ErrTooManyRedirects      = errors.New("client: too many redirects")
	errTooManyRedirectChecks = errors.New("client: too many redirect checks")
)

var DefaultRequestTimeout = 5 * time.Second

var DefaultTransport CancelableTransport = &http.Transport{
	Proxy: http.ProxyFromEnvironment,
	Dial: (&net.Dialer{
		Timeout:   30 * time.Second,
		KeepAlive: 30 * time.Second,
	}).Dial,
	TLSHandshakeTimeout: 10 * time.Second,
}

type Config struct {
	// Endpoints defines a set of URLs (schemes, hosts and ports only)
	// that can be used to communicate with a logical etcd cluster. For
	// example, a three-node cluster could be provided like so:
	//
	// 	Endpoints: []string{
	//		"http://node1.example.com:4001",
	//		"http://node2.example.com:2379",
	//		"http://node3.example.com:4001",
	//	}
	//
	// If multiple endpoints are provided, the Client will attempt to
	// use them all in the event that one or more of them are unusable.
	//
	// If Client.Sync is ever called, the Client may cache an alternate
	// set of endpoints to continue operation.
	Endpoints []string

	// Transport is used by the Client to drive HTTP requests. If not
	// provided, DefaultTransport will be used.
	Transport CancelableTransport

	// CheckRedirect specifies the policy for handling HTTP redirects.
	// If CheckRedirect is not nil, the Client calls it before
	// following an HTTP redirect. The sole argument is the number of
	// requests that have alrady been made. If CheckRedirect returns
	// an error, Client.Do will not make any further requests and return
	// the error back it to the caller.
	//
	// If CheckRedirect is nil, the Client uses its default policy,
	// which is to stop after 10 consecutive requests.
	CheckRedirect CheckRedirectFunc
}

func (cfg *Config) transport() CancelableTransport {
	if cfg.Transport == nil {
		return DefaultTransport
	}
	return cfg.Transport
}

func (cfg *Config) checkRedirect() CheckRedirectFunc {
	if cfg.CheckRedirect == nil {
		return DefaultCheckRedirect
	}
	return cfg.CheckRedirect
}

// CancelableTransport mimics net/http.Transport, but requires that
// the object also support request cancellation.
type CancelableTransport interface {
	http.RoundTripper
	CancelRequest(req *http.Request)
}

type CheckRedirectFunc func(via int) error

// DefaultCheckRedirect follows up to 10 redirects, but no more.
var DefaultCheckRedirect CheckRedirectFunc = func(via int) error {
	if via > 10 {
		return ErrTooManyRedirects
	}
	return nil
}

type Client interface {
	// Sync updates the internal cache of the etcd cluster's membership.
	Sync(context.Context) error

	// Endpoints returns a copy of the current set of API endpoints used
	// by Client to resolve HTTP requests. If Sync has ever been called,
	// this may differ from the initial Endpoints provided in the Config.
	Endpoints() []string

	httpClient
}

func New(cfg Config) (Client, error) {
	c := &httpClusterClient{clientFactory: newHTTPClientFactory(cfg.transport(), cfg.checkRedirect())}
	if err := c.reset(cfg.Endpoints); err != nil {
		return nil, err
	}
	return c, nil
}

type httpClient interface {
	Do(context.Context, httpAction) (*http.Response, []byte, error)
}

func newHTTPClientFactory(tr CancelableTransport, cr CheckRedirectFunc) httpClientFactory {
	return func(ep url.URL) httpClient {
		return &redirectFollowingHTTPClient{
			checkRedirect: cr,
			client: &simpleHTTPClient{
				transport: tr,
				endpoint:  ep,
			},
		}
	}
}

type httpClientFactory func(url.URL) httpClient

type httpAction interface {
	HTTPRequest(url.URL) *http.Request
}

type httpClusterClient struct {
	clientFactory httpClientFactory
	endpoints     []url.URL
	sync.RWMutex
}

func (c *httpClusterClient) reset(eps []string) error {
	if len(eps) == 0 {
		return ErrNoEndpoints
	}

	neps := make([]url.URL, len(eps))
	for i, ep := range eps {
		u, err := url.Parse(ep)
		if err != nil {
			return err
		}
		neps[i] = *u
	}

	c.endpoints = neps

	return nil
}

func (c *httpClusterClient) Do(ctx context.Context, act httpAction) (*http.Response, []byte, error) {
	c.RLock()
	leps := len(c.endpoints)
	eps := make([]url.URL, leps)
	n := copy(eps, c.endpoints)
	c.RUnlock()

	if leps == 0 {
		return nil, nil, ErrNoEndpoints
	}

	if leps != n {
		return nil, nil, errors.New("unable to pick endpoint: copy failed")
	}

	var resp *http.Response
	var body []byte
	var err error

	for _, ep := range eps {
		hc := c.clientFactory(ep)
		resp, body, err = hc.Do(ctx, act)
		if err != nil {
			if err == context.DeadlineExceeded || err == context.Canceled {
				return nil, nil, err
			}
			continue
		}
		if resp.StatusCode/100 == 5 {
			continue
		}
		break
	}

	return resp, body, err
}

func (c *httpClusterClient) Endpoints() []string {
	c.RLock()
	defer c.RUnlock()

	eps := make([]string, len(c.endpoints))
	for i, ep := range c.endpoints {
		eps[i] = ep.String()
	}

	return eps
}

func (c *httpClusterClient) Sync(ctx context.Context) error {
	mAPI := NewMembersAPI(c)
	ms, err := mAPI.List(ctx)
	if err != nil {
		return err
	}

	c.Lock()
	defer c.Unlock()

	eps := make([]string, 0)
	for _, m := range ms {
		eps = append(eps, m.ClientURLs...)
	}

	return c.reset(eps)
}

type roundTripResponse struct {
	resp *http.Response
	err  error
}

type simpleHTTPClient struct {
	transport CancelableTransport
	endpoint  url.URL
}

func (c *simpleHTTPClient) Do(ctx context.Context, act httpAction) (*http.Response, []byte, error) {
	req := act.HTTPRequest(c.endpoint)

	rtchan := make(chan roundTripResponse, 1)
	go func() {
		resp, err := c.transport.RoundTrip(req)
		rtchan <- roundTripResponse{resp: resp, err: err}
		close(rtchan)
	}()

	var resp *http.Response
	var err error

	select {
	case rtresp := <-rtchan:
		resp, err = rtresp.resp, rtresp.err
	case <-ctx.Done():
		// cancel and wait for request to actually exit before continuing
		c.transport.CancelRequest(req)
		rtresp := <-rtchan
		resp = rtresp.resp
		err = ctx.Err()
	}

	// always check for resp nil-ness to deal with possible
	// race conditions between channels above
	defer func() {
		if resp != nil {
			resp.Body.Close()
		}
	}()

	if err != nil {
		return nil, nil, err
	}

	body, err := ioutil.ReadAll(resp.Body)
	return resp, body, err
}

type redirectFollowingHTTPClient struct {
	client        httpClient
	checkRedirect CheckRedirectFunc
}

func (r *redirectFollowingHTTPClient) Do(ctx context.Context, act httpAction) (*http.Response, []byte, error) {
	next := act
	for i := 0; i < 100; i++ {
		if i > 0 {
			if err := r.checkRedirect(i); err != nil {
				return nil, nil, err
			}
		}
		resp, body, err := r.client.Do(ctx, next)
		if err != nil {
			return nil, nil, err
		}
		if resp.StatusCode/100 == 3 {
			hdr := resp.Header.Get("Location")
			if hdr == "" {
				return nil, nil, fmt.Errorf("Location header not set")
			}
			loc, err := url.Parse(hdr)
			if err != nil {
				return nil, nil, fmt.Errorf("Location header not valid URL: %s", hdr)
			}
			next = &redirectedHTTPAction{
				action:   act,
				location: *loc,
			}
			continue
		}
		return resp, body, nil
	}

	return nil, nil, errTooManyRedirectChecks
}

type redirectedHTTPAction struct {
	action   httpAction
	location url.URL
}

func (r *redirectedHTTPAction) HTTPRequest(ep url.URL) *http.Request {
	orig := r.action.HTTPRequest(ep)
	orig.URL = &r.location
	return orig
}
<|endoftext|>


<commit_before>// Copyright (C) 2017 Google 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 task

import (
	"context"
	"sync"
)

// Task is the unit of work used in the task system.
// Tasks should generally be reentrant, they may be run more than once in more than one executor, and should generally
// be agnostic as to whether they are run in parallel.
type Task func(context.Context) error

// Once wraps a task so that only the first invocation of the outer task invokes the inner task.
func Once(task Task) Task {
	once := sync.Once{}
	var err error
	return func(ctx context.Context) error {
		once.Do(func() { err = task(ctx) })
		return err
	}
}
<commit_msg>core/event/task: Add Retry() method.<commit_after>// Copyright (C) 2017 Google 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 task

import (
	"context"
	"sync"
	"time"
)

// Task is the unit of work used in the task system.
// Tasks should generally be reentrant, they may be run more than once in more than one executor, and should generally
// be agnostic as to whether they are run in parallel.
type Task func(context.Context) error

// Once wraps a task so that only the first invocation of the outer task invokes the inner task.
func Once(task Task) Task {
	once := sync.Once{}
	var err error
	return func(ctx context.Context) error {
		once.Do(func() { err = task(ctx) })
		return err
	}
}

// Retry repeatedly calls task until task returns a nil error, the number
// attempts reaches maxAttempts or the context is cancelled. Retry will sleep
// for retryDelay between retry attempts.
// if maxAttempts <= 0, then there is no maximum limit to the number of times
// task will be called.
func Retry(ctx context.Context, maxAttempts int, retryDelay time.Duration, task Task) error {
	var count int
	for {
		err := task(ctx)
		if err == nil {
			return nil
		}
		count++
		if maxAttempts > 0 && count >= maxAttempts {
			return err
		}
		select {
		case <-ShouldStop(ctx):
			return StopReason(ctx)
		case <-time.After(retryDelay):
		}
	}
}
<|endoftext|>


<commit_before>// Package client provides Ios client side code for connecting to an Ios cluster
package client

import (
	"bufio"
	"errors"
	"github.com/golang/glog"
	"github.com/heidi-ann/ios/config"
	"github.com/heidi-ann/ios/msgs"
	"io"
	"math/rand"
	"net"
	"time"
)

type client struct {
	id        int
	requestID int //TODO: write this value to disk
	stats     *statsFile
	servers   []string
	conn      net.Conn
	rd        *bufio.Reader
	timeout   time.Duration
	master    int
}

func connect(addrs []string, tries int, hint int) (net.Conn, int, error) {
	var conn net.Conn
	var err error

	// reset invalid hint
	if len(addrs) >= hint {
		hint = 0
	}

	// first, try on to connect to the most likely leader
	glog.V(1).Info("Trying to connect to ", addrs[hint])
	conn, err = net.Dial("tcp", addrs[hint])
	// if successful
	if err == nil {
		glog.V(1).Infof("Connect established to %s", addrs[hint])
		return conn, hint, err
	}
	//if unsuccessful
	glog.Warning(err)

	// if fails, try everyone else
	for i := range addrs {
		for t := tries; t > 0; t-- {
			glog.V(1).Info("Trying to connect to ", addrs[i])
			conn, err = net.Dial("tcp", addrs[i])

			// if successful
			if err == nil {
				glog.V(1).Infof("Connect established to %s", addrs[i])
				return conn, i, err
			}

			//if unsuccessful
			glog.Warning(err)
			time.Sleep(100 * time.Millisecond)
		}
	}

	// calc most likely next leader
	hint += 1
	if len(addrs) == hint {
		hint = 0
	}
	return conn, hint, err
}

// send bytes and wait for reply, return bytes returned if succussful or error otherwise
func dispatcher(b []byte, conn net.Conn, r *bufio.Reader, timeout time.Duration) ([]byte, error) {
	// setup channels for timeout implementation
	errCh := make(chan error, 1)
	replyCh := make(chan []byte, 1)

	go func() {
		// send request
		_, err := conn.Write(b)
		_, err = conn.Write([]byte("\n"))
		if err != nil && err != io.EOF {
			glog.Warning(err)
			errCh <- err
		}
		glog.V(1).Info("Sent")
		// read response
		reply, err := r.ReadBytes('\n')
		if err != nil && err != io.EOF {
			glog.Warning(err)
			errCh <- err
		}
		// success, return reply
		replyCh <- reply
	}()

	//handling outcomes
	select {
	case reply := <-replyCh:
		return reply, nil
	case err := <-errCh:
		return nil, err
	case <-time.After(timeout):
		return nil, errors.New("Timeout")
	}
}

// StartClient creates an Ios client and tries to connect to an Ios cluster
// If ID is -1 then a random one will be generated
func StartClient(id int, statFile string, addrs []string, timeout time.Duration) *client {
	// TODO: find a better way to handle required flags
	if id == -1 {
		rand.Seed(time.Now().UTC().UnixNano())
		id = rand.Int()
		glog.V(1).Info("ID was not provided, ID ", id, " has been assigned")
	}

	glog.Info("Starting up client ", id)

	// set up stats collection
	stats := openStatsFile(statFile)

	// connecting to server
	conn, master, err := connect(addrs, 10, 0)
	if err != nil {
		glog.Fatal(err)
	}
	rd := bufio.NewReader(conn)

	glog.Info("Client is ready to start processing incoming requests")
	return &client{id, 1, stats, addrs, conn, rd, timeout, master}
}

func (c *client) SubmitRequest(text string) (bool, string) {
	glog.V(1).Info("Request ", c.requestID, " is: ", text)

	// prepare request
	req := msgs.ClientRequest{c.id, c.requestID, false, text}
	b, err := msgs.Marshal(req)
	if err != nil {
		glog.Fatal(err)
	}
	glog.V(1).Info(string(b))

	c.stats.startRequest(c.requestID)
	tries := 0
	var reply *msgs.ClientResponse

	// dispatch request until successful
	for {
		tries++
		if tries > len(c.servers) {
			req.ForceViewChange = true
			b, err = msgs.Marshal(req)
			if err != nil {
				glog.Fatal(err)
			}
		}

		replyBytes, err := dispatcher(b, c.conn, c.rd, c.timeout)
		if err == nil {
			//handle reply
			reply = new(msgs.ClientResponse)
			err = msgs.Unmarshal(replyBytes, reply)

			if err == nil && !reply.Success {
				err = errors.New("request marked by server as unsuccessful")
			}
			if err == nil && reply.Success {
				glog.V(1).Info("request was Successful", reply)
				break
			}
		}
		glog.Warning("Request ", c.requestID, " failed due to: ", err)

		// try to establish a new connection
		for {
			if c.conn != nil {
				err = c.conn.Close()
				if err != nil {
					glog.Warning(err)
				}
			}
			c.conn, c.master, err = connect(c.servers, 10, c.master+1)
			if err == nil {
				break
			}
			glog.Warning("Serious connectivity issues")
			time.Sleep(time.Second)
		}

		c.rd = bufio.NewReader(c.conn)

	}

	//check reply is not nil
	if *reply == (msgs.ClientResponse{}) {
		glog.Fatal("Response is nil")
	}

	//check reply is as expected
	if reply.ClientID != c.id {
		glog.Fatal("Response received has wrong ClientID: expected ",
			c.id, " ,received ", reply.ClientID)
	}
	if reply.RequestID != c.requestID {
		glog.Fatal("Response received has wrong RequestID: expected ",
			c.requestID, " ,received ", reply.RequestID)
	}
	if !reply.Success {
		glog.Fatal("Response marked as unsuccessful but not retried")
	}

	// write to latency to log
	c.stats.stopRequest(tries)
	c.requestID++
	return true, reply.Response
}

// StartClientFromConfigFile creates an Ios client
// If ID is -1 then a random one will be generated
func StartClientFromConfigFile(id int, statFile string, configFile string) *client {
	conf := config.ParseClientConfig(configFile)
	timeout := time.Millisecond * time.Duration(conf.Parameters.Timeout)
	return StartClient(id, statFile, conf.Addresses.Address, timeout)
}

func (c *client) StopClient() {
	glog.V(1).Info("Shutting down client ", c.id)
	// close stats file
	c.stats.closeStatsFile()
	// close connection
	if c.conn != nil {
		c.conn.Close()
	}
}
<commit_msg>adding nil test to try to fix travis build<commit_after>// Package client provides Ios client side code for connecting to an Ios cluster
package client

import (
	"bufio"
	"errors"
	"github.com/golang/glog"
	"github.com/heidi-ann/ios/config"
	"github.com/heidi-ann/ios/msgs"
	"io"
	"math/rand"
	"net"
	"time"
)

type client struct {
	id        int
	requestID int //TODO: write this value to disk
	stats     *statsFile
	servers   []string
	conn      net.Conn
	rd        *bufio.Reader
	timeout   time.Duration
	master    int
}

func connect(addrs []string, tries int, hint int) (net.Conn, int, error) {
	var conn net.Conn
	var err error

	// reset invalid hint
	if len(addrs) >= hint {
		hint = 0
	}

	// first, try on to connect to the most likely leader
	glog.V(1).Info("Trying to connect to ", addrs[hint])
	conn, err = net.Dial("tcp", addrs[hint])
	// if successful
	if err == nil {
		glog.V(1).Infof("Connect established to %s", addrs[hint])
		return conn, hint, err
	}
	//if unsuccessful
	glog.Warning(err)

	// if fails, try everyone else
	for i := range addrs {
		for t := tries; t > 0; t-- {
			glog.V(1).Info("Trying to connect to ", addrs[i])
			conn, err = net.Dial("tcp", addrs[i])

			// if successful
			if err == nil {
				glog.V(1).Infof("Connect established to %s", addrs[i])
				return conn, i, err
			}

			//if unsuccessful
			glog.Warning(err)
			time.Sleep(100 * time.Millisecond)
		}
	}

	// calc most likely next leader
	hint += 1
	if len(addrs) == hint {
		hint = 0
	}
	return conn, hint, err
}

// send bytes and wait for reply, return bytes returned if succussful or error otherwise
func dispatcher(b []byte, conn net.Conn, r *bufio.Reader, timeout time.Duration) ([]byte, error) {
	// check for nil connection
	if conn == nil {
		glog.Warning("connection missing")
		return nil, errors.New("Connection closed")
	}

	// setup channels for timeout implementation
	errCh := make(chan error, 1)
	replyCh := make(chan []byte, 1)

	go func() {
		// send request
		_, err := conn.Write(b)
		_, err = conn.Write([]byte("\n"))
		if err != nil && err != io.EOF {
			glog.Warning(err)
			errCh <- err
		}
		glog.V(1).Info("Sent")
		// read response
		reply, err := r.ReadBytes('\n')
		if err != nil && err != io.EOF {
			glog.Warning(err)
			errCh <- err
		}
		// success, return reply
		replyCh <- reply
	}()

	//handling outcomes
	select {
	case reply := <-replyCh:
		return reply, nil
	case err := <-errCh:
		return nil, err
	case <-time.After(timeout):
		return nil, errors.New("Timeout")
	}
}

// StartClient creates an Ios client and tries to connect to an Ios cluster
// If ID is -1 then a random one will be generated
func StartClient(id int, statFile string, addrs []string, timeout time.Duration) *client {
	// TODO: find a better way to handle required flags
	if id == -1 {
		rand.Seed(time.Now().UTC().UnixNano())
		id = rand.Int()
		glog.V(1).Info("ID was not provided, ID ", id, " has been assigned")
	}

	glog.Info("Starting up client ", id)

	// set up stats collection
	stats := openStatsFile(statFile)

	// connecting to server
	conn, master, err := connect(addrs, 10, 0)
	if err != nil {
		glog.Fatal(err)
	}
	rd := bufio.NewReader(conn)

	glog.Info("Client is ready to start processing incoming requests")
	return &client{id, 1, stats, addrs, conn, rd, timeout, master}
}

func (c *client) SubmitRequest(text string) (bool, string) {
	glog.V(1).Info("Request ", c.requestID, " is: ", text)

	// prepare request
	req := msgs.ClientRequest{c.id, c.requestID, false, text}
	b, err := msgs.Marshal(req)
	if err != nil {
		glog.Fatal(err)
	}
	glog.V(1).Info(string(b))

	c.stats.startRequest(c.requestID)
	tries := 0
	var reply *msgs.ClientResponse

	// dispatch request until successful
	for {
		tries++
		if tries > len(c.servers) {
			req.ForceViewChange = true
			b, err = msgs.Marshal(req)
			if err != nil {
				glog.Fatal(err)
			}
		}

		replyBytes, err := dispatcher(b, c.conn, c.rd, c.timeout)
		if err == nil {
			//handle reply
			reply = new(msgs.ClientResponse)
			err = msgs.Unmarshal(replyBytes, reply)

			if err == nil && !reply.Success {
				err = errors.New("request marked by server as unsuccessful")
			}
			if err == nil && reply.Success {
				glog.V(1).Info("request was Successful", reply)
				break
			}
		}
		glog.Warning("Request ", c.requestID, " failed due to: ", err)

		// try to establish a new connection
		for {
			if c.conn != nil {
				err = c.conn.Close()
				if err != nil {
					glog.Warning(err)
				}
			}
			c.conn, c.master, err = connect(c.servers, 10, c.master+1)
			if err == nil {
				break
			}
			glog.Warning("Serious connectivity issues")
			time.Sleep(time.Second)
		}

		c.rd = bufio.NewReader(c.conn)

	}

	//check reply is not nil
	if *reply == (msgs.ClientResponse{}) {
		glog.Fatal("Response is nil")
	}

	//check reply is as expected
	if reply.ClientID != c.id {
		glog.Fatal("Response received has wrong ClientID: expected ",
			c.id, " ,received ", reply.ClientID)
	}
	if reply.RequestID != c.requestID {
		glog.Fatal("Response received has wrong RequestID: expected ",
			c.requestID, " ,received ", reply.RequestID)
	}
	if !reply.Success {
		glog.Fatal("Response marked as unsuccessful but not retried")
	}

	// write to latency to log
	c.stats.stopRequest(tries)
	c.requestID++
	return true, reply.Response
}

// StartClientFromConfigFile creates an Ios client
// If ID is -1 then a random one will be generated
func StartClientFromConfigFile(id int, statFile string, configFile string) *client {
	conf := config.ParseClientConfig(configFile)
	timeout := time.Millisecond * time.Duration(conf.Parameters.Timeout)
	return StartClient(id, statFile, conf.Addresses.Address, timeout)
}

func (c *client) StopClient() {
	glog.V(1).Info("Shutting down client ", c.id)
	// close stats file
	c.stats.closeStatsFile()
	// close connection
	if c.conn != nil {
		c.conn.Close()
	}
}
<|endoftext|>


<commit_before>package client

import (
	"fmt"
	"github.com/gogather/com/log"
	"net"
)

type JClient struct {
	ip   string
	port int
}

func (this *JClient) Start(ip string, port int) net.Conn {
	addr := fmt.Sprintf("%s:%d", ip, port)
	conn, err := net.Dial("tcp", addr)
	if err != nil {
		conn.Close()
		log.Warnln("connect judge server failed in port:", port)
		return nil
	} else {
		return conn
	}
}
<commit_msg>finished client api<commit_after>package client

import (
	"fmt"
	"github.com/gogather/com/log"
	"net"
)

type JClient struct {
	ip   string
	port int
	conn net.Conn
}

func (this *JClient) Start(ip string, port int) error {
	addr := fmt.Sprintf("%s:%d", ip, port)
	conn, err := net.Dial("tcp", addr)
	if err != nil {
		conn.Close()
		log.Warnln("connect judge server failed in port:", port)
	} else {
		this.conn = conn
	}

	return err
}

func (this *JClient) Request(msg string) string {
	var buf [10240]byte

	this.conn.Write(msg)

	n, err := this.conn.Read(buf[0:])
	if err != nil {
		if err == io.EOF {
			return
		}
	}

	return string(buf[0:n])
}
<|endoftext|>


<commit_before>package client

import (
	"bufio"
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"net"
	"net/http"
	"net/http/httputil"

	"github.com/flynn/go-discoverd"
	"github.com/flynn/lorne/types"
	"github.com/flynn/rpcplus"
)

var ErrNoServers = errors.New("lorne: no servers found")

func New(id string) (*Client, error) {
	disc, err := discoverd.NewClient()
	if err != nil {
		return nil, err
	}
	services, err := disc.ServiceSet("flynn-lorne." + id)
	if err != nil {
		return nil, err
	}
	addrs := services.Addrs()
	if len(addrs) == 0 {
		return nil, ErrNoServers
	}
	c, err := rpcplus.DialHTTP("tcp", addrs[0])
	return &Client{c, services}, err
}

type Client struct {
	c *rpcplus.Client

	service *discoverd.ServiceSet
}

func (c *Client) JobList() (map[string]lorne.Job, error) {
	var jobs map[string]lorne.Job
	err := c.c.Call("Host.JobList", struct{}{}, &jobs)
	return jobs, err
}

func (c *Client) GetJob(id string) (*lorne.Job, error) {
	var res lorne.Job
	err := c.c.Call("Host.GetJob", id, &res)
	return &res, err
}

func (c *Client) StopJob(id string) error {
	return c.c.Call("Host.StopJob", id, &struct{}{})
}

var ErrWouldWait = errors.New("lorne: attach would wait")

func (c *Client) Attach(req *lorne.AttachReq, wait bool) (ReadWriteCloser, func() error, error) {
	data, err := json.Marshal(req)
	if err != nil {
		return nil, nil, err
	}
	httpReq, err := http.NewRequest("POST", "/attach", bytes.NewBuffer(data))
	if err != nil {
		return nil, nil, err
	}
	addrs := c.service.Addrs()
	if len(addrs) == 0 {
		return nil, nil, ErrNoServers
	}
	conn, err := net.Dial("tcp", addrs[0])
	if err != nil {
		return nil, nil, err
	}
	clientconn := httputil.NewClientConn(conn, nil)
	res, err := clientconn.Do(httpReq)
	if err != nil && err != httputil.ErrPersistEOF {
		return nil, nil, err
	}
	if res.StatusCode != 200 {
		return nil, nil, fmt.Errorf("lorne: unexpected status %d", res.StatusCode)
	}
	var rwc io.ReadWriteCloser
	var buf *bufio.Reader
	rwc, buf = clientconn.Hijack()
	if buf.Buffered() > 0 {
		rwc = struct {
			io.Reader
			writeCloser
		}{
			io.MultiReader(io.LimitReader(buf, int64(buf.Buffered())), rwc),
			rwc.(writeCloser),
		}
	}

	attachState := make([]byte, 1)
	if _, err := rwc.Read(attachState); err != nil {
		rwc.Close()
		return nil, nil, err
	}

	handleState := func() error {
		switch attachState[0] {
		case lorne.AttachSuccess:
			return nil
		case lorne.AttachError:
			errBytes, err := ioutil.ReadAll(rwc)
			rwc.Close()
			if err != nil {
				return err
			}
			return errors.New(string(errBytes))
		default:
			rwc.Close()
			return fmt.Errorf("lorne: unknown attach state: %d", attachState)
		}
	}

	if attachState[0] == lorne.AttachWaiting {
		if !wait {
			rwc.Close()
			return nil, nil, ErrWouldWait
		}
		return rwc.(ReadWriteCloser), func() error {
			if _, err := rwc.Read(attachState); err != nil {
				rwc.Close()
				return err
			}
			return handleState()
		}, nil
	}

	return rwc.(ReadWriteCloser), nil, handleState()
}

type ReadWriteCloser interface {
	io.ReadWriteCloser
	CloseWrite() error
}

type writeCloser interface {
	io.WriteCloser
	CloseWrite() error
}
<commit_msg>host/client: Update discoverd usage<commit_after>package client

import (
	"bufio"
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"net"
	"net/http"
	"net/http/httputil"

	"github.com/flynn/go-discoverd"
	"github.com/flynn/lorne/types"
	"github.com/flynn/rpcplus"
)

var ErrNoServers = errors.New("lorne: no servers found")

func New(id string) (*Client, error) {
	services, err := discoverd.NewServiceSet("flynn-lorne." + id)
	if err != nil {
		return nil, err
	}
	addrs := services.Addrs()
	if len(addrs) == 0 {
		return nil, ErrNoServers
	}
	c, err := rpcplus.DialHTTP("tcp", addrs[0])
	return &Client{c, services}, err
}

type Client struct {
	c *rpcplus.Client

	service *discoverd.ServiceSet
}

func (c *Client) JobList() (map[string]lorne.Job, error) {
	var jobs map[string]lorne.Job
	err := c.c.Call("Host.JobList", struct{}{}, &jobs)
	return jobs, err
}

func (c *Client) GetJob(id string) (*lorne.Job, error) {
	var res lorne.Job
	err := c.c.Call("Host.GetJob", id, &res)
	return &res, err
}

func (c *Client) StopJob(id string) error {
	return c.c.Call("Host.StopJob", id, &struct{}{})
}

var ErrWouldWait = errors.New("lorne: attach would wait")

func (c *Client) Attach(req *lorne.AttachReq, wait bool) (ReadWriteCloser, func() error, error) {
	data, err := json.Marshal(req)
	if err != nil {
		return nil, nil, err
	}
	httpReq, err := http.NewRequest("POST", "/attach", bytes.NewBuffer(data))
	if err != nil {
		return nil, nil, err
	}
	addrs := c.service.Addrs()
	if len(addrs) == 0 {
		return nil, nil, ErrNoServers
	}
	conn, err := net.Dial("tcp", addrs[0])
	if err != nil {
		return nil, nil, err
	}
	clientconn := httputil.NewClientConn(conn, nil)
	res, err := clientconn.Do(httpReq)
	if err != nil && err != httputil.ErrPersistEOF {
		return nil, nil, err
	}
	if res.StatusCode != 200 {
		return nil, nil, fmt.Errorf("lorne: unexpected status %d", res.StatusCode)
	}
	var rwc io.ReadWriteCloser
	var buf *bufio.Reader
	rwc, buf = clientconn.Hijack()
	if buf.Buffered() > 0 {
		rwc = struct {
			io.Reader
			writeCloser
		}{
			io.MultiReader(io.LimitReader(buf, int64(buf.Buffered())), rwc),
			rwc.(writeCloser),
		}
	}

	attachState := make([]byte, 1)
	if _, err := rwc.Read(attachState); err != nil {
		rwc.Close()
		return nil, nil, err
	}

	handleState := func() error {
		switch attachState[0] {
		case lorne.AttachSuccess:
			return nil
		case lorne.AttachError:
			errBytes, err := ioutil.ReadAll(rwc)
			rwc.Close()
			if err != nil {
				return err
			}
			return errors.New(string(errBytes))
		default:
			rwc.Close()
			return fmt.Errorf("lorne: unknown attach state: %d", attachState)
		}
	}

	if attachState[0] == lorne.AttachWaiting {
		if !wait {
			rwc.Close()
			return nil, nil, ErrWouldWait
		}
		return rwc.(ReadWriteCloser), func() error {
			if _, err := rwc.Read(attachState); err != nil {
				rwc.Close()
				return err
			}
			return handleState()
		}, nil
	}

	return rwc.(ReadWriteCloser), nil, handleState()
}

type ReadWriteCloser interface {
	io.ReadWriteCloser
	CloseWrite() error
}

type writeCloser interface {
	io.WriteCloser
	CloseWrite() error
}
<|endoftext|>


<commit_before>// Copyright 2015, 2016 Eris Industries (UK) Ltd.
// This file is part of Eris-RT

// Eris-RT is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.

// Eris-RT is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with Eris-RT.  If not, see <http://www.gnu.org/licenses/>.

package client

import (
	"fmt"
	// "strings"

	"github.com/tendermint/go-rpc/client"

	log "github.com/eris-ltd/eris-logger"

	acc "github.com/eris-ltd/eris-db/account"
	consensus_types "github.com/eris-ltd/eris-db/consensus/types"
	core_types "github.com/eris-ltd/eris-db/core/types"
	tendermint_client "github.com/eris-ltd/eris-db/rpc/tendermint/client"
	tendermint_types "github.com/eris-ltd/eris-db/rpc/tendermint/core/types"
	"github.com/eris-ltd/eris-db/txs"
)

type NodeClient interface {
	Broadcast(transaction txs.Tx) (*txs.Receipt, error)
	DeriveWebsocketClient() (nodeWsClient NodeWebsocketClient, err error)

	Status() (ChainId []byte, ValidatorPublicKey []byte, LatestBlockHash []byte,
		LatestBlockHeight int, LatestBlockTime int64, err error)
	GetAccount(address []byte) (*acc.Account, error)
	QueryContract(callerAddress, calleeAddress, data []byte) (ret []byte, gasUsed int64, err error)
	QueryContractCode(address, code, data []byte) (ret []byte, gasUsed int64, err error)

	DumpStorage(address []byte) (storage *core_types.Storage, err error)
	GetName(name string) (owner []byte, data string, expirationBlock int, err error)
	ListValidators() (blockHeight int, bondedValidators, unbondingValidators []consensus_types.Validator, err error)
}

type NodeWebsocketClient interface {
	Subscribe(eventId string) error
	Unsubscribe(eventId string) error

	WaitForConfirmation(tx txs.Tx, chainId string, inputAddr []byte) (chan Confirmation, error)
	Close()
}

// NOTE [ben] Compiler check to ensure ErisNodeClient successfully implements
// eris-db/client.NodeClient
var _ NodeClient = (*ErisNodeClient)(nil)

// Eris-Client is a simple struct exposing the client rpc methods

type ErisNodeClient struct {
	broadcastRPC string
}

// ErisKeyClient.New returns a new eris-keys client for provided rpc location
// Eris-keys connects over http request-responses
func NewErisNodeClient(rpcString string) *ErisNodeClient {
	return &ErisNodeClient{
		broadcastRPC: rpcString,
	}
}

//------------------------------------------------------------------------------------
// broadcast to blockchain node
// NOTE: [ben] Eris Client first continues from tendermint rpc, but will have handshake to negotiate
// protocol version for moving towards rpc/v1

func (erisNodeClient *ErisNodeClient) Broadcast(tx txs.Tx) (*txs.Receipt, error) {
	client := rpcclient.NewClientURI(erisNodeClient.broadcastRPC)
	receipt, err := tendermint_client.BroadcastTx(client, tx)
	if err != nil {
		return nil, err
	}
	return &receipt, nil
}

func (erisNodeClient *ErisNodeClient) DeriveWebsocketClient() (nodeWsClient NodeWebsocketClient, err error) {
	var wsAddr string
	// TODO: clean up this inherited mess on dealing with the address prefixes.
	nodeAddr := erisNodeClient.broadcastRPC
	// if strings.HasPrefix(nodeAddr, "http://") {
	// 	wsAddr = strings.TrimPrefix(nodeAddr, "http://")
	// }
	// if strings.HasPrefix(nodeAddr, "tcp://") {
	// 	wsAddr = strings.TrimPrefix(nodeAddr, "tcp://")
	// }
	// if strings.HasPrefix(nodeAddr, "unix://") {
	// 	log.WithFields(log.Fields{
	// 		"node address": nodeAddr,
	// 	}).Error("Unable to subscribe to websocket from unix socket.")
	// 	return nil, fmt.Errorf("Unable to construct websocket from unix socket: %s", nodeAddr)
	// }
	// wsAddr = "ws://" + wsAddr
	wsAddr = nodeAddr
	log.WithFields(log.Fields{
		"websocket address": wsAddr,
		"endpoint":          "/websocket",
	}).Debug("Subscribing to websocket address")
	wsClient := rpcclient.NewWSClient(wsAddr, "/websocket")
	if _, err = wsClient.Start(); err != nil {
		return nil, err
	}
	derivedErisNodeWebsocketClient := &ErisNodeWebsocketClient{
		tendermintWebsocket: wsClient,
	}
	return derivedErisNodeWebsocketClient, nil
}

//------------------------------------------------------------------------------------
// RPC requests other than transaction related

// Status returns the ChainId (GenesisHash), validator's PublicKey, latest block hash
// the block height and the latest block time.
func (erisNodeClient *ErisNodeClient) Status() (GenesisHash []byte, ValidatorPublicKey []byte, LatestBlockHash []byte, LatestBlockHeight int, LatestBlockTime int64, err error) {
	client := rpcclient.NewClientJSONRPC(erisNodeClient.broadcastRPC)
	res, err := tendermint_client.Status(client)
	if err != nil {
		err = fmt.Errorf("Error connecting to node (%s) to get status: %s",
			erisNodeClient.broadcastRPC, err.Error())
		return nil, nil, nil, int(0), int64(0), err
	}

	// unwrap return results
	GenesisHash = res.GenesisHash
	ValidatorPublicKey = res.PubKey.Bytes()
	LatestBlockHash = res.LatestBlockHash
	LatestBlockHeight = res.LatestBlockHeight
	LatestBlockTime = res.LatestBlockTime
	return
}

func (erisNodeClient *ErisNodeClient) ChainId() (ChainName, ChainId string, GenesisHash []byte, err error) {
	client := rpcclient.NewClientJSONRPC(erisNodeClient.broadcastRPC)
	chainIdResult, err := tendermint_client.ChainId(client)
	if err != nil {
		err = fmt.Errorf("Error connecting to node (%s) to get chain id: %s",
			erisNodeClient.broadcastRPC, err.Error())
		return "", "", nil, err
	}
	// unwrap results
	ChainName = chainIdResult.ChainName
	ChainId = chainIdResult.ChainId
	GenesisHash = make([]byte, len(chainIdResult.GenesisHash))
	copy(GenesisHash[:], chainIdResult.GenesisHash)
	return
}

// QueryContract executes the contract code at address with the given data
// NOTE: there is no check on the caller;
func (erisNodeClient *ErisNodeClient) QueryContract(callerAddress, calleeAddress, data []byte) (ret []byte, gasUsed int64, err error) {
	client := rpcclient.NewClientJSONRPC(erisNodeClient.broadcastRPC)
	callResult, err := tendermint_client.Call(client, callerAddress, calleeAddress, data)
	if err != nil {
		err = fmt.Errorf("Error connnecting to node (%s) to query contract at (%X) with data (%X)",
			erisNodeClient.broadcastRPC, calleeAddress, data, err.Error())
		return nil, int64(0), err
	}
	return callResult.Return, callResult.GasUsed, nil
}

// QueryContractCode executes the contract code at address with the given data but with provided code
func (erisNodeClient *ErisNodeClient) QueryContractCode(address, code, data []byte) (ret []byte, gasUsed int64, err error) {
	client := rpcclient.NewClientJSONRPC(erisNodeClient.broadcastRPC)
	// TODO: [ben] Call and CallCode have an inconsistent signature; it makes sense for both to only
	// have a single address that is the contract to query.
	callResult, err := tendermint_client.CallCode(client, address, code, data)
	if err != nil {
		err = fmt.Errorf("Error connnecting to node (%s) to query contract code at (%X) with data (%X) and code (%X)",
			erisNodeClient.broadcastRPC, address, data, code, err.Error())
		return nil, int64(0), err
	}
	return callResult.Return, callResult.GasUsed, nil
}

// GetAccount returns a copy of the account
func (erisNodeClient *ErisNodeClient) GetAccount(address []byte) (*acc.Account, error) {
	client := rpcclient.NewClientJSONRPC(erisNodeClient.broadcastRPC)
	account, err := tendermint_client.GetAccount(client, address)
	if err != nil {
		err = fmt.Errorf("Error connecting to node (%s) to fetch account (%X): %s",
			erisNodeClient.broadcastRPC, address, err.Error())
		return nil, err
	}
	if account == nil {
		err = fmt.Errorf("Unknown account %X at node (%s)", address, erisNodeClient.broadcastRPC)
		return nil, err
	}

	return account.Copy(), nil
}

// DumpStorage returns the full storage for an account.
func (erisNodeClient *ErisNodeClient) DumpStorage(address []byte) (storage *core_types.Storage, err error) {
	client := rpcclient.NewClientJSONRPC(erisNodeClient.broadcastRPC)
	resultStorage, err := tendermint_client.DumpStorage(client, address)
	if err != nil {
		err = fmt.Errorf("Error connecting to node (%s) to get storage for account (%X): %s",
			erisNodeClient.broadcastRPC, address, err.Error())
		return nil, err
	}
	// UnwrapResultDumpStorage is an inefficient full deep copy,
	// to transform the type to /core/types.Storage
	// TODO: removing go-wire and go-rpc allows us to collapse these types
	storage = tendermint_types.UnwrapResultDumpStorage(resultStorage)
	return
}

//--------------------------------------------------------------------------------------------
// Name registry

func (erisNodeClient *ErisNodeClient) GetName(name string) (owner []byte, data string, expirationBlock int, err error) {
	client := rpcclient.NewClientJSONRPC(erisNodeClient.broadcastRPC)
	entryResult, err := tendermint_client.GetName(client, name)
	if err != nil {
		err = fmt.Errorf("Error connecting to node (%s) to get name registrar entry for name (%s)",
			erisNodeClient.broadcastRPC, name)
		return nil, "", 0, err
	}
	// unwrap return results
	owner = entryResult.Owner
	data = entryResult.Data
	expirationBlock = entryResult.Expires
	return
}

//--------------------------------------------------------------------------------------------

func (erisNodeClient *ErisNodeClient) ListValidators() (blockHeight int,
	bondedValidators []consensus_types.Validator, unbondingValidators []consensus_types.Validator, err error) {
	client := rpcclient.NewClientJSONRPC(erisNodeClient.broadcastRPC)
	validatorsResult, err := tendermint_client.ListValidators(client)
	if err != nil {
		err = fmt.Errorf("Error connecting to node (%s) to get validators",
			erisNodeClient.broadcastRPC)
		return 0, nil, nil, err
	}
	// unwrap return results
	blockHeight = validatorsResult.BlockHeight
	bondedValidators = validatorsResult.BondedValidators
	unbondingValidators = validatorsResult.UnbondingValidators
	return
}
<commit_msg>client: silence log output from go-rpc<commit_after>// Copyright 2015, 2016 Eris Industries (UK) Ltd.
// This file is part of Eris-RT

// Eris-RT is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.

// Eris-RT is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with Eris-RT.  If not, see <http://www.gnu.org/licenses/>.

package client

import (
	"fmt"
	// "strings"

	"github.com/tendermint/go-rpc/client"
	// Note [ben]: this is included to silence the logger from tendermint/go-rpc/client
	// see func init()
	tendermint_log "github.com/tendermint/log15"

	log "github.com/eris-ltd/eris-logger"

	acc "github.com/eris-ltd/eris-db/account"
	consensus_types "github.com/eris-ltd/eris-db/consensus/types"
	core_types "github.com/eris-ltd/eris-db/core/types"
	tendermint_client "github.com/eris-ltd/eris-db/rpc/tendermint/client"
	tendermint_types "github.com/eris-ltd/eris-db/rpc/tendermint/core/types"
	"github.com/eris-ltd/eris-db/txs"
)

type NodeClient interface {
	Broadcast(transaction txs.Tx) (*txs.Receipt, error)
	DeriveWebsocketClient() (nodeWsClient NodeWebsocketClient, err error)

	Status() (ChainId []byte, ValidatorPublicKey []byte, LatestBlockHash []byte,
		LatestBlockHeight int, LatestBlockTime int64, err error)
	GetAccount(address []byte) (*acc.Account, error)
	QueryContract(callerAddress, calleeAddress, data []byte) (ret []byte, gasUsed int64, err error)
	QueryContractCode(address, code, data []byte) (ret []byte, gasUsed int64, err error)

	DumpStorage(address []byte) (storage *core_types.Storage, err error)
	GetName(name string) (owner []byte, data string, expirationBlock int, err error)
	ListValidators() (blockHeight int, bondedValidators, unbondingValidators []consensus_types.Validator, err error)
}

type NodeWebsocketClient interface {
	Subscribe(eventId string) error
	Unsubscribe(eventId string) error

	WaitForConfirmation(tx txs.Tx, chainId string, inputAddr []byte) (chan Confirmation, error)
	Close()
}

// NOTE [ben] Compiler check to ensure ErisNodeClient successfully implements
// eris-db/client.NodeClient
var _ NodeClient = (*ErisNodeClient)(nil)

// Eris-Client is a simple struct exposing the client rpc methods

type ErisNodeClient struct {
	broadcastRPC string
}

// ErisKeyClient.New returns a new eris-keys client for provided rpc location
// Eris-keys connects over http request-responses
func NewErisNodeClient(rpcString string) *ErisNodeClient {
	return &ErisNodeClient{
		broadcastRPC: rpcString,
	}
}

// Note [Ben]: This is a hack to silence Tendermint logger from tendermint/go-rpc
// it needs to be initialised before go-rpc, hence it's placement here.
func init() {
	h := tendermint_log.LvlFilterHandler(tendermint_log.LvlWarn, tendermint_log.StdoutHandler)
    tendermint_log.Root().SetHandler(h)
}

//------------------------------------------------------------------------------------
// broadcast to blockchain node
// NOTE: [ben] Eris Client first continues from tendermint rpc, but will have handshake to negotiate
// protocol version for moving towards rpc/v1

func (erisNodeClient *ErisNodeClient) Broadcast(tx txs.Tx) (*txs.Receipt, error) {
	client := rpcclient.NewClientURI(erisNodeClient.broadcastRPC)
	receipt, err := tendermint_client.BroadcastTx(client, tx)
	if err != nil {
		return nil, err
	}
	return &receipt, nil
}

func (erisNodeClient *ErisNodeClient) DeriveWebsocketClient() (nodeWsClient NodeWebsocketClient, err error) {
	var wsAddr string
	// TODO: clean up this inherited mess on dealing with the address prefixes.
	nodeAddr := erisNodeClient.broadcastRPC
	// if strings.HasPrefix(nodeAddr, "http://") {
	// 	wsAddr = strings.TrimPrefix(nodeAddr, "http://")
	// }
	// if strings.HasPrefix(nodeAddr, "tcp://") {
	// 	wsAddr = strings.TrimPrefix(nodeAddr, "tcp://")
	// }
	// if strings.HasPrefix(nodeAddr, "unix://") {
	// 	log.WithFields(log.Fields{
	// 		"node address": nodeAddr,
	// 	}).Error("Unable to subscribe to websocket from unix socket.")
	// 	return nil, fmt.Errorf("Unable to construct websocket from unix socket: %s", nodeAddr)
	// }
	// wsAddr = "ws://" + wsAddr
	wsAddr = nodeAddr
	log.WithFields(log.Fields{
		"websocket address": wsAddr,
		"endpoint":          "/websocket",
	}).Debug("Subscribing to websocket address")
	wsClient := rpcclient.NewWSClient(wsAddr, "/websocket")
	if _, err = wsClient.Start(); err != nil {
		return nil, err
	}
	derivedErisNodeWebsocketClient := &ErisNodeWebsocketClient{
		tendermintWebsocket: wsClient,
	}
	return derivedErisNodeWebsocketClient, nil
}

//------------------------------------------------------------------------------------
// RPC requests other than transaction related

// Status returns the ChainId (GenesisHash), validator's PublicKey, latest block hash
// the block height and the latest block time.
func (erisNodeClient *ErisNodeClient) Status() (GenesisHash []byte, ValidatorPublicKey []byte, LatestBlockHash []byte, LatestBlockHeight int, LatestBlockTime int64, err error) {
	client := rpcclient.NewClientJSONRPC(erisNodeClient.broadcastRPC)
	res, err := tendermint_client.Status(client)
	if err != nil {
		err = fmt.Errorf("Error connecting to node (%s) to get status: %s",
			erisNodeClient.broadcastRPC, err.Error())
		return nil, nil, nil, int(0), int64(0), err
	}

	// unwrap return results
	GenesisHash = res.GenesisHash
	ValidatorPublicKey = res.PubKey.Bytes()
	LatestBlockHash = res.LatestBlockHash
	LatestBlockHeight = res.LatestBlockHeight
	LatestBlockTime = res.LatestBlockTime
	return
}

func (erisNodeClient *ErisNodeClient) ChainId() (ChainName, ChainId string, GenesisHash []byte, err error) {
	client := rpcclient.NewClientJSONRPC(erisNodeClient.broadcastRPC)
	chainIdResult, err := tendermint_client.ChainId(client)
	if err != nil {
		err = fmt.Errorf("Error connecting to node (%s) to get chain id: %s",
			erisNodeClient.broadcastRPC, err.Error())
		return "", "", nil, err
	}
	// unwrap results
	ChainName = chainIdResult.ChainName
	ChainId = chainIdResult.ChainId
	GenesisHash = make([]byte, len(chainIdResult.GenesisHash))
	copy(GenesisHash[:], chainIdResult.GenesisHash)
	return
}

// QueryContract executes the contract code at address with the given data
// NOTE: there is no check on the caller;
func (erisNodeClient *ErisNodeClient) QueryContract(callerAddress, calleeAddress, data []byte) (ret []byte, gasUsed int64, err error) {
	client := rpcclient.NewClientJSONRPC(erisNodeClient.broadcastRPC)
	callResult, err := tendermint_client.Call(client, callerAddress, calleeAddress, data)
	if err != nil {
		err = fmt.Errorf("Error connnecting to node (%s) to query contract at (%X) with data (%X)",
			erisNodeClient.broadcastRPC, calleeAddress, data, err.Error())
		return nil, int64(0), err
	}
	return callResult.Return, callResult.GasUsed, nil
}

// QueryContractCode executes the contract code at address with the given data but with provided code
func (erisNodeClient *ErisNodeClient) QueryContractCode(address, code, data []byte) (ret []byte, gasUsed int64, err error) {
	client := rpcclient.NewClientJSONRPC(erisNodeClient.broadcastRPC)
	// TODO: [ben] Call and CallCode have an inconsistent signature; it makes sense for both to only
	// have a single address that is the contract to query.
	callResult, err := tendermint_client.CallCode(client, address, code, data)
	if err != nil {
		err = fmt.Errorf("Error connnecting to node (%s) to query contract code at (%X) with data (%X) and code (%X)",
			erisNodeClient.broadcastRPC, address, data, code, err.Error())
		return nil, int64(0), err
	}
	return callResult.Return, callResult.GasUsed, nil
}

// GetAccount returns a copy of the account
func (erisNodeClient *ErisNodeClient) GetAccount(address []byte) (*acc.Account, error) {
	client := rpcclient.NewClientJSONRPC(erisNodeClient.broadcastRPC)
	account, err := tendermint_client.GetAccount(client, address)
	if err != nil {
		err = fmt.Errorf("Error connecting to node (%s) to fetch account (%X): %s",
			erisNodeClient.broadcastRPC, address, err.Error())
		return nil, err
	}
	if account == nil {
		err = fmt.Errorf("Unknown account %X at node (%s)", address, erisNodeClient.broadcastRPC)
		return nil, err
	}

	return account.Copy(), nil
}

// DumpStorage returns the full storage for an account.
func (erisNodeClient *ErisNodeClient) DumpStorage(address []byte) (storage *core_types.Storage, err error) {
	client := rpcclient.NewClientJSONRPC(erisNodeClient.broadcastRPC)
	resultStorage, err := tendermint_client.DumpStorage(client, address)
	if err != nil {
		err = fmt.Errorf("Error connecting to node (%s) to get storage for account (%X): %s",
			erisNodeClient.broadcastRPC, address, err.Error())
		return nil, err
	}
	// UnwrapResultDumpStorage is an inefficient full deep copy,
	// to transform the type to /core/types.Storage
	// TODO: removing go-wire and go-rpc allows us to collapse these types
	storage = tendermint_types.UnwrapResultDumpStorage(resultStorage)
	return
}

//--------------------------------------------------------------------------------------------
// Name registry

func (erisNodeClient *ErisNodeClient) GetName(name string) (owner []byte, data string, expirationBlock int, err error) {
	client := rpcclient.NewClientJSONRPC(erisNodeClient.broadcastRPC)
	entryResult, err := tendermint_client.GetName(client, name)
	if err != nil {
		err = fmt.Errorf("Error connecting to node (%s) to get name registrar entry for name (%s)",
			erisNodeClient.broadcastRPC, name)
		return nil, "", 0, err
	}
	// unwrap return results
	owner = entryResult.Owner
	data = entryResult.Data
	expirationBlock = entryResult.Expires
	return
}

//--------------------------------------------------------------------------------------------

func (erisNodeClient *ErisNodeClient) ListValidators() (blockHeight int,
	bondedValidators []consensus_types.Validator, unbondingValidators []consensus_types.Validator, err error) {
	client := rpcclient.NewClientJSONRPC(erisNodeClient.broadcastRPC)
	validatorsResult, err := tendermint_client.ListValidators(client)
	if err != nil {
		err = fmt.Errorf("Error connecting to node (%s) to get validators",
			erisNodeClient.broadcastRPC)
		return 0, nil, nil, err
	}
	// unwrap return results
	blockHeight = validatorsResult.BlockHeight
	bondedValidators = validatorsResult.BondedValidators
	unbondingValidators = validatorsResult.UnbondingValidators
	return
}
<|endoftext|>


<commit_before><commit_msg>To not install if already installed<commit_after><|endoftext|>


<commit_before>package client

import (
	"encoding/binary"
	"errors"
	"io"
	"net"
	"path"
	"strings"

	"github.com/joushou/qp"
)

const (
	DefaultMaxSize = 128 * 1024
)

var (
	ErrUnknownProtocol        = errors.New("unknown protocol")
	ErrSimpleClientNotStarted = errors.New("client not started")
	ErrNoSuchFile             = errors.New("no such file")
	ErrNotADirectory          = errors.New("not a directory")
	ErrWeirdResponse          = errors.New("weird response")
)

func toError(m qp.Message) error {
	if eresp, ok := m.(*qp.ErrorResponse); ok {
		return errors.New(eresp.Error)
	}
	return nil
}

func emptyStat() qp.Stat {
	return qp.Stat{
		Type:   ^uint16(0),
		Dev:    ^uint32(0),
		Mode:   ^qp.FileMode(0),
		Atime:  ^uint32(0),
		Mtime:  ^uint32(0),
		Length: ^uint64(0),
	}
}

// SimpleClient provides a simple API for working with 9P servers. It is not
// the most efficient way to use 9P, but allows using such servers with little
// to no clue about what exactly 9P is.
type SimpleClient struct {
	c       *Client
	maxSize uint32
	root    qp.Fid
	nextFid qp.Fid
}

func (c *SimpleClient) getFid() qp.Fid {
	// We need to skip NOFID (highest value) and 0 (our root)
	if c.nextFid == qp.NOFID {
		c.nextFid++
	}
	if c.nextFid == 0 {
		c.nextFid++
	}
	f := c.nextFid
	c.nextFid++
	return f
}

func (c *SimpleClient) setup(username, servicename string) error {
	if c.c == nil {
		return ErrSimpleClientNotStarted
	}

	resp, err := c.c.Send(&qp.VersionRequest{
		Tag:     qp.NOTAG,
		MaxSize: DefaultMaxSize,
		Version: qp.Version,
	})
	if err != nil {
		c.c.Stop()
		c.c = nil
		return err
	}
	if err = toError(resp); err != nil {
		c.c.Stop()
		c.c = nil
		return err
	}

	vresp, ok := resp.(*qp.VersionResponse)
	if !ok {
		return ErrWeirdResponse
	}

	if vresp.Version != qp.Version {
		return ErrUnknownProtocol
	}

	c.maxSize = vresp.MaxSize

	t, err := c.c.Tag()
	if err != nil {
		c.c.Stop()
		c.c = nil
		return err
	}

	resp, err = c.c.Send(&qp.AttachRequest{
		Tag:      t,
		Fid:      c.root,
		AuthFid:  qp.NOFID,
		Username: username,
		Service:  servicename,
	})

	if err != nil {
		c.c.Stop()
		c.c = nil
		return err
	}
	if err = toError(resp); err != nil {
		c.c.Stop()
		c.c = nil
		return err
	}

	return nil
}

func (c *SimpleClient) readAll(fid qp.Fid) ([]byte, error) {
	var b []byte

	for {
		t, err := c.c.Tag()
		if err != nil {
			return nil, err
		}
		resp, err := c.c.Send(&qp.ReadRequest{
			Tag:    t,
			Fid:    fid,
			Offset: uint64(len(b)),
			Count:  c.maxSize - 9, // The size of a response
		})
		if err != nil {
			return nil, err
		}
		if err = toError(resp); err != nil {
			return nil, err
		}
		rresp, ok := resp.(*qp.ReadResponse)
		if !ok {
			return nil, ErrWeirdResponse
		}

		if len(rresp.Data) == 0 {
			break
		}
		b = append(b, rresp.Data...)
	}

	return b, nil
}

func (c *SimpleClient) writeAll(fid qp.Fid, data []byte) error {
	var offset uint64
	for {
		count := int(c.maxSize - 20)
		if len(data[offset:]) < count {
			count = len(data[offset:])
		}

		if count == 0 {
			break
		}

		t, err := c.c.Tag()
		if err != nil {
			return err
		}
		resp, err := c.c.Send(&qp.WriteRequest{
			Tag:    t,
			Fid:    fid,
			Offset: offset,
			Data:   data[offset : offset+uint64(count)],
		})
		if err != nil {
			return err
		}
		if err = toError(resp); err != nil {
			return err
		}

		wresp, ok := resp.(*qp.WriteResponse)
		if !ok {
			return ErrWeirdResponse
		}
		offset += uint64(wresp.Count)
	}

	return nil
}

func (c *SimpleClient) walkTo(file string) (qp.Fid, qp.Qid, error) {
	s := strings.Split(file, "/")

	var strs []string
	for _, str := range s {
		if str != "" {
			strs = append(strs, str)
		}
	}
	s = strs

	f := c.getFid()
	t, err := c.c.Tag()
	if err != nil {
		return qp.NOFID, qp.Qid{}, err
	}
	resp, err := c.c.Send(&qp.WalkRequest{
		Tag:    t,
		Fid:    c.root,
		NewFid: f,
		Names:  s,
	})
	if err != nil {
		return qp.NOFID, qp.Qid{}, err
	}
	if err = toError(resp); err != nil {
		return qp.NOFID, qp.Qid{}, err
	}

	wresp, ok := resp.(*qp.WalkResponse)
	if !ok {
		return qp.NOFID, qp.Qid{}, ErrWeirdResponse
	}

	if len(wresp.Qids) != len(s) {
		return qp.NOFID, qp.Qid{}, ErrNoSuchFile
	}

	q := qp.Qid{}
	if len(wresp.Qids) > 0 {
		end := len(wresp.Qids) - 1
		for i, q := range wresp.Qids {
			if i == end {
				break
			}
			if q.Type&qp.QTDIR == 0 {
				return qp.NOFID, qp.Qid{}, ErrNotADirectory
			}
		}
		q = wresp.Qids[end]
	}

	return f, q, nil
}

func (c *SimpleClient) clunk(fid qp.Fid) {
	t, _ := c.c.Tag()
	c.c.Send(&qp.ClunkRequest{
		Tag: t,
		Fid: fid,
	})
}

func (c *SimpleClient) Stat(file string) (qp.Stat, error) {
	fid, _, err := c.walkTo(file)
	if err != nil {
		return qp.Stat{}, err
	}
	defer c.clunk(fid)

	t, err := c.c.Tag()
	if err != nil {
		return qp.Stat{}, err
	}

	resp, err := c.c.Send(&qp.StatRequest{
		Tag: t,
		Fid: fid,
	})

	if err != nil {
		return qp.Stat{}, err
	}
	if err = toError(resp); err != nil {
		return qp.Stat{}, err
	}

	sresp, ok := resp.(*qp.StatResponse)
	if !ok {
		return qp.Stat{}, ErrWeirdResponse
	}

	return sresp.Stat, nil
}

func (c *SimpleClient) ReadSome(file string, offset uint64) ([]byte, error) {
	fid, _, err := c.walkTo(file)
	if err != nil {
		return nil, err
	}
	defer c.clunk(fid)

	t, err := c.c.Tag()
	if err != nil {
		return nil, err
	}
	resp, err := c.c.Send(&qp.OpenRequest{
		Tag:  t,
		Fid:  fid,
		Mode: qp.OREAD,
	})
	if err != nil {
		return nil, err
	}
	if err = toError(resp); err != nil {
		return nil, err
	}

	t, err = c.c.Tag()
	if err != nil {
		return nil, err
	}
	resp, err = c.c.Send(&qp.ReadRequest{
		Tag:    t,
		Fid:    fid,
		Offset: offset,
		Count:  c.maxSize - 9, // The size of a response
	})
	if err != nil {
		return nil, err
	}
	if err = toError(resp); err != nil {
		return nil, err
	}
	rresp, ok := resp.(*qp.ReadResponse)
	if !ok {
		return nil, ErrWeirdResponse
	}

	return rresp.Data, nil
}

func (c *SimpleClient) Read(file string) ([]byte, error) {
	fid, _, err := c.walkTo(file)
	if err != nil {
		return nil, err
	}
	defer c.clunk(fid)

	t, err := c.c.Tag()
	if err != nil {
		return nil, err
	}
	resp, err := c.c.Send(&qp.OpenRequest{
		Tag:  t,
		Fid:  fid,
		Mode: qp.OREAD,
	})
	if err != nil {
		return nil, err
	}
	if err = toError(resp); err != nil {
		return nil, err
	}

	return c.readAll(fid)
}

func (c *SimpleClient) Write(content []byte, file string) error {
	fid, _, err := c.walkTo(file)
	if err != nil {
		return err
	}
	defer c.clunk(fid)

	t, err := c.c.Tag()
	if err != nil {
		return err
	}
	resp, err := c.c.Send(&qp.OpenRequest{
		Tag:  t,
		Fid:  fid,
		Mode: qp.OWRITE,
	})
	if err != nil {
		return err
	}
	if err = toError(resp); err != nil {
		return err
	}

	return c.writeAll(fid, content)
}

func (c *SimpleClient) List(file string) ([]string, error) {
	fid, _, err := c.walkTo(file)
	if err != nil {
		return nil, err
	}
	defer c.clunk(fid)

	t, err := c.c.Tag()
	if err != nil {
		return nil, err
	}
	resp, err := c.c.Send(&qp.OpenRequest{
		Tag:  t,
		Fid:  fid,
		Mode: qp.OREAD,
	})
	if err != nil {
		return nil, err
	}
	if err = toError(resp); err != nil {
		return nil, err
	}

	b, err := c.readAll(fid)
	if err != nil {
		return nil, err
	}

	var strs []string
	for len(b) > 0 {
		x := &qp.Stat{}
		l := binary.LittleEndian.Uint16(b[0:2])
		if err := x.UnmarshalBinary(b); err != nil {
			return nil, err
		}
		b = b[l+2:]
		if x.Mode&qp.DMDIR == 0 {
			strs = append(strs, x.Name)
		} else {
			strs = append(strs, x.Name+"/")
		}
	}

	return strs, nil
}

func (c *SimpleClient) Create(name string, directory bool) error {
	dir := path.Dir(name)
	file := path.Base(name)

	fid, _, err := c.walkTo(dir)
	if err != nil {
		return err
	}
	defer c.clunk(fid)

	perms := qp.FileMode(0755)
	if directory {
		perms |= qp.DMDIR
	}

	t, err := c.c.Tag()
	if err != nil {
		return err
	}
	resp, err := c.c.Send(&qp.CreateRequest{
		Tag:         t,
		Fid:         fid,
		Name:        file,
		Permissions: perms,
		Mode:        qp.OREAD,
	})
	if err != nil {
		return err
	}
	if err = toError(resp); err != nil {
		return err
	}
	return nil
}

func (c *SimpleClient) Rename(oldname, newname string) error {
	sold := strings.Split(oldname, "/")
	snew := strings.Split(newname, "/")
	if len(sold) != len(snew) {
		return errors.New("invalid rename")
	}

	for i := 0; i < len(sold)-1; i++ {
		if sold[i] != snew[i] {
			return errors.New("invalid rename")
		}
	}

	fid, _, err := c.walkTo(oldname)
	if err != nil {
		return err
	}
	defer c.clunk(fid)

	s := emptyStat()
	s.Name = snew[len(snew)-1]

	t, err := c.c.Tag()
	if err != nil {
		return err
	}
	resp, err := c.c.Send(&qp.WriteStatRequest{
		Tag:  t,
		Fid:  fid,
		Stat: s,
	})
	if err != nil {
		return err
	}
	if err = toError(resp); err != nil {
		return err
	}
	return nil
}

func (c *SimpleClient) Remove(name string) error {
	fid, _, err := c.walkTo(name)
	if err != nil {
		return err
	}

	t, err := c.c.Tag()
	if err != nil {
		c.clunk(fid)
		return err
	}
	resp, err := c.c.Send(&qp.RemoveRequest{
		Tag: t,
		Fid: fid,
	})
	if err != nil {
		c.clunk(fid)
		return err
	}
	if err = toError(resp); err != nil {
		c.clunk(fid)
		return err
	}
	return nil
}

func (c *SimpleClient) Dial(network, address, username, servicename string) error {
	conn, err := net.Dial(network, address)
	if err != nil {
		return err
	}

	c.c = New(conn)
	go c.c.Start()

	err = c.setup(username, servicename)
	if err != nil {
		return err
	}
	return nil
}

func (c *SimpleClient) Connect(rw io.ReadWriter, username, servicename string) error {
	c.c = New(rw)
	go c.c.Start()

	err := c.setup(username, servicename)
	if err != nil {
		return err
	}
	return nil
}
<commit_msg>Change List to return slice of stats<commit_after>package client

import (
	"encoding/binary"
	"errors"
	"io"
	"net"
	"path"
	"strings"

	"github.com/joushou/qp"
)

const (
	DefaultMaxSize = 128 * 1024
)

var (
	ErrUnknownProtocol        = errors.New("unknown protocol")
	ErrSimpleClientNotStarted = errors.New("client not started")
	ErrNoSuchFile             = errors.New("no such file")
	ErrNotADirectory          = errors.New("not a directory")
	ErrWeirdResponse          = errors.New("weird response")
)

func toError(m qp.Message) error {
	if eresp, ok := m.(*qp.ErrorResponse); ok {
		return errors.New(eresp.Error)
	}
	return nil
}

func emptyStat() qp.Stat {
	return qp.Stat{
		Type:   ^uint16(0),
		Dev:    ^uint32(0),
		Mode:   ^qp.FileMode(0),
		Atime:  ^uint32(0),
		Mtime:  ^uint32(0),
		Length: ^uint64(0),
	}
}

// SimpleClient provides a simple API for working with 9P servers. It is not
// the most efficient way to use 9P, but allows using such servers with little
// to no clue about what exactly 9P is.
type SimpleClient struct {
	c       *Client
	maxSize uint32
	root    qp.Fid
	nextFid qp.Fid
}

func (c *SimpleClient) getFid() qp.Fid {
	// We need to skip NOFID (highest value) and 0 (our root)
	if c.nextFid == qp.NOFID {
		c.nextFid++
	}
	if c.nextFid == 0 {
		c.nextFid++
	}
	f := c.nextFid
	c.nextFid++
	return f
}

func (c *SimpleClient) setup(username, servicename string) error {
	if c.c == nil {
		return ErrSimpleClientNotStarted
	}

	resp, err := c.c.Send(&qp.VersionRequest{
		Tag:     qp.NOTAG,
		MaxSize: DefaultMaxSize,
		Version: qp.Version,
	})
	if err != nil {
		c.c.Stop()
		c.c = nil
		return err
	}
	if err = toError(resp); err != nil {
		c.c.Stop()
		c.c = nil
		return err
	}

	vresp, ok := resp.(*qp.VersionResponse)
	if !ok {
		return ErrWeirdResponse
	}

	if vresp.Version != qp.Version {
		return ErrUnknownProtocol
	}

	c.maxSize = vresp.MaxSize

	t, err := c.c.Tag()
	if err != nil {
		c.c.Stop()
		c.c = nil
		return err
	}

	resp, err = c.c.Send(&qp.AttachRequest{
		Tag:      t,
		Fid:      c.root,
		AuthFid:  qp.NOFID,
		Username: username,
		Service:  servicename,
	})

	if err != nil {
		c.c.Stop()
		c.c = nil
		return err
	}
	if err = toError(resp); err != nil {
		c.c.Stop()
		c.c = nil
		return err
	}

	return nil
}

func (c *SimpleClient) readAll(fid qp.Fid) ([]byte, error) {
	var b []byte

	for {
		t, err := c.c.Tag()
		if err != nil {
			return nil, err
		}
		resp, err := c.c.Send(&qp.ReadRequest{
			Tag:    t,
			Fid:    fid,
			Offset: uint64(len(b)),
			Count:  c.maxSize - 9, // The size of a response
		})
		if err != nil {
			return nil, err
		}
		if err = toError(resp); err != nil {
			return nil, err
		}
		rresp, ok := resp.(*qp.ReadResponse)
		if !ok {
			return nil, ErrWeirdResponse
		}

		if len(rresp.Data) == 0 {
			break
		}
		b = append(b, rresp.Data...)
	}

	return b, nil
}

func (c *SimpleClient) writeAll(fid qp.Fid, data []byte) error {
	var offset uint64
	for {
		count := int(c.maxSize - 20)
		if len(data[offset:]) < count {
			count = len(data[offset:])
		}

		if count == 0 {
			break
		}

		t, err := c.c.Tag()
		if err != nil {
			return err
		}
		resp, err := c.c.Send(&qp.WriteRequest{
			Tag:    t,
			Fid:    fid,
			Offset: offset,
			Data:   data[offset : offset+uint64(count)],
		})
		if err != nil {
			return err
		}
		if err = toError(resp); err != nil {
			return err
		}

		wresp, ok := resp.(*qp.WriteResponse)
		if !ok {
			return ErrWeirdResponse
		}
		offset += uint64(wresp.Count)
	}

	return nil
}

func (c *SimpleClient) walkTo(file string) (qp.Fid, qp.Qid, error) {
	s := strings.Split(file, "/")

	var strs []string
	for _, str := range s {
		if str != "" {
			strs = append(strs, str)
		}
	}
	s = strs

	f := c.getFid()
	t, err := c.c.Tag()
	if err != nil {
		return qp.NOFID, qp.Qid{}, err
	}
	resp, err := c.c.Send(&qp.WalkRequest{
		Tag:    t,
		Fid:    c.root,
		NewFid: f,
		Names:  s,
	})
	if err != nil {
		return qp.NOFID, qp.Qid{}, err
	}
	if err = toError(resp); err != nil {
		return qp.NOFID, qp.Qid{}, err
	}

	wresp, ok := resp.(*qp.WalkResponse)
	if !ok {
		return qp.NOFID, qp.Qid{}, ErrWeirdResponse
	}

	if len(wresp.Qids) != len(s) {
		return qp.NOFID, qp.Qid{}, ErrNoSuchFile
	}

	q := qp.Qid{}
	if len(wresp.Qids) > 0 {
		end := len(wresp.Qids) - 1
		for i, q := range wresp.Qids {
			if i == end {
				break
			}
			if q.Type&qp.QTDIR == 0 {
				return qp.NOFID, qp.Qid{}, ErrNotADirectory
			}
		}
		q = wresp.Qids[end]
	}

	return f, q, nil
}

func (c *SimpleClient) clunk(fid qp.Fid) {
	t, _ := c.c.Tag()
	c.c.Send(&qp.ClunkRequest{
		Tag: t,
		Fid: fid,
	})
}

func (c *SimpleClient) Stat(file string) (qp.Stat, error) {
	fid, _, err := c.walkTo(file)
	if err != nil {
		return qp.Stat{}, err
	}
	defer c.clunk(fid)

	t, err := c.c.Tag()
	if err != nil {
		return qp.Stat{}, err
	}

	resp, err := c.c.Send(&qp.StatRequest{
		Tag: t,
		Fid: fid,
	})

	if err != nil {
		return qp.Stat{}, err
	}
	if err = toError(resp); err != nil {
		return qp.Stat{}, err
	}

	sresp, ok := resp.(*qp.StatResponse)
	if !ok {
		return qp.Stat{}, ErrWeirdResponse
	}

	return sresp.Stat, nil
}

func (c *SimpleClient) ReadSome(file string, offset uint64) ([]byte, error) {
	fid, _, err := c.walkTo(file)
	if err != nil {
		return nil, err
	}
	defer c.clunk(fid)

	t, err := c.c.Tag()
	if err != nil {
		return nil, err
	}
	resp, err := c.c.Send(&qp.OpenRequest{
		Tag:  t,
		Fid:  fid,
		Mode: qp.OREAD,
	})
	if err != nil {
		return nil, err
	}
	if err = toError(resp); err != nil {
		return nil, err
	}

	t, err = c.c.Tag()
	if err != nil {
		return nil, err
	}
	resp, err = c.c.Send(&qp.ReadRequest{
		Tag:    t,
		Fid:    fid,
		Offset: offset,
		Count:  c.maxSize - 9, // The size of a response
	})
	if err != nil {
		return nil, err
	}
	if err = toError(resp); err != nil {
		return nil, err
	}
	rresp, ok := resp.(*qp.ReadResponse)
	if !ok {
		return nil, ErrWeirdResponse
	}

	return rresp.Data, nil
}

func (c *SimpleClient) Read(file string) ([]byte, error) {
	fid, _, err := c.walkTo(file)
	if err != nil {
		return nil, err
	}
	defer c.clunk(fid)

	t, err := c.c.Tag()
	if err != nil {
		return nil, err
	}
	resp, err := c.c.Send(&qp.OpenRequest{
		Tag:  t,
		Fid:  fid,
		Mode: qp.OREAD,
	})
	if err != nil {
		return nil, err
	}
	if err = toError(resp); err != nil {
		return nil, err
	}

	return c.readAll(fid)
}

func (c *SimpleClient) Write(content []byte, file string) error {
	fid, _, err := c.walkTo(file)
	if err != nil {
		return err
	}
	defer c.clunk(fid)

	t, err := c.c.Tag()
	if err != nil {
		return err
	}
	resp, err := c.c.Send(&qp.OpenRequest{
		Tag:  t,
		Fid:  fid,
		Mode: qp.OWRITE,
	})
	if err != nil {
		return err
	}
	if err = toError(resp); err != nil {
		return err
	}

	return c.writeAll(fid, content)
}

func (c *SimpleClient) List(file string) ([]qp.Stat, error) {
	fid, _, err := c.walkTo(file)
	if err != nil {
		return nil, err
	}
	defer c.clunk(fid)

	t, err := c.c.Tag()
	if err != nil {
		return nil, err
	}
	resp, err := c.c.Send(&qp.OpenRequest{
		Tag:  t,
		Fid:  fid,
		Mode: qp.OREAD,
	})
	if err != nil {
		return nil, err
	}
	if err = toError(resp); err != nil {
		return nil, err
	}

	b, err := c.readAll(fid)
	if err != nil {
		return nil, err
	}

	var stats []qp.Stat
	for len(b) > 0 {
		x := qp.Stat{}
		l := binary.LittleEndian.Uint16(b[0:2])
		if err := x.UnmarshalBinary(b[0 : 2+l]); err != nil {
			return nil, err
		}
		b = b[2+l:]
		stats = append(stats, x)
	}

	return stats, nil
}

func (c *SimpleClient) Create(name string, directory bool) error {
	dir := path.Dir(name)
	file := path.Base(name)

	fid, _, err := c.walkTo(dir)
	if err != nil {
		return err
	}
	defer c.clunk(fid)

	perms := qp.FileMode(0755)
	if directory {
		perms |= qp.DMDIR
	}

	t, err := c.c.Tag()
	if err != nil {
		return err
	}
	resp, err := c.c.Send(&qp.CreateRequest{
		Tag:         t,
		Fid:         fid,
		Name:        file,
		Permissions: perms,
		Mode:        qp.OREAD,
	})
	if err != nil {
		return err
	}
	if err = toError(resp); err != nil {
		return err
	}
	return nil
}

func (c *SimpleClient) Rename(oldname, newname string) error {
	sold := strings.Split(oldname, "/")
	snew := strings.Split(newname, "/")
	if len(sold) != len(snew) {
		return errors.New("invalid rename")
	}

	for i := 0; i < len(sold)-1; i++ {
		if sold[i] != snew[i] {
			return errors.New("invalid rename")
		}
	}

	fid, _, err := c.walkTo(oldname)
	if err != nil {
		return err
	}
	defer c.clunk(fid)

	s := emptyStat()
	s.Name = snew[len(snew)-1]

	t, err := c.c.Tag()
	if err != nil {
		return err
	}
	resp, err := c.c.Send(&qp.WriteStatRequest{
		Tag:  t,
		Fid:  fid,
		Stat: s,
	})
	if err != nil {
		return err
	}
	if err = toError(resp); err != nil {
		return err
	}
	return nil
}

func (c *SimpleClient) Remove(name string) error {
	fid, _, err := c.walkTo(name)
	if err != nil {
		return err
	}

	t, err := c.c.Tag()
	if err != nil {
		c.clunk(fid)
		return err
	}
	resp, err := c.c.Send(&qp.RemoveRequest{
		Tag: t,
		Fid: fid,
	})
	if err != nil {
		c.clunk(fid)
		return err
	}
	if err = toError(resp); err != nil {
		c.clunk(fid)
		return err
	}
	return nil
}

func (c *SimpleClient) Dial(network, address, username, servicename string) error {
	conn, err := net.Dial(network, address)
	if err != nil {
		return err
	}

	c.c = New(conn)
	go c.c.Start()

	err = c.setup(username, servicename)
	if err != nil {
		return err
	}
	return nil
}

func (c *SimpleClient) Connect(rw io.ReadWriter, username, servicename string) error {
	c.c = New(rw)
	go c.c.Start()

	err := c.setup(username, servicename)
	if err != nil {
		return err
	}
	return nil
}
<|endoftext|>


<commit_before>/*
Copyright The Helm Authors.

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

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

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

package main // import "helm.sh/helm/v3/cmd/helm"

import (
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"strings"

	"github.com/spf13/cobra"
	"github.com/spf13/pflag"
	"k8s.io/klog"
	"sigs.k8s.io/yaml"

	// Import to initialize client auth plugins.
	_ "k8s.io/client-go/plugin/pkg/client/auth"

	"helm.sh/helm/v3/internal/completion"
	"helm.sh/helm/v3/pkg/action"
	"helm.sh/helm/v3/pkg/cli"
	"helm.sh/helm/v3/pkg/gates"
	kubefake "helm.sh/helm/v3/pkg/kube/fake"
	"helm.sh/helm/v3/pkg/release"
	"helm.sh/helm/v3/pkg/storage/driver"
)

// FeatureGateOCI is the feature gate for checking if `helm chart` and `helm registry` commands should work
const FeatureGateOCI = gates.Gate("HELM_EXPERIMENTAL_OCI")

var (
	settings = cli.New()
)

func init() {
	log.SetFlags(log.Lshortfile)
}

func debug(format string, v ...interface{}) {
	if settings.Debug {
		format = fmt.Sprintf("[debug] %s\n", format)
		log.Output(2, fmt.Sprintf(format, v...))
	}
}

func initKubeLogs() {
	pflag.CommandLine.SetNormalizeFunc(wordSepNormalizeFunc)
	gofs := flag.NewFlagSet("klog", flag.ExitOnError)
	klog.InitFlags(gofs)
	pflag.CommandLine.AddGoFlagSet(gofs)
	pflag.CommandLine.Set("logtostderr", "true")
}

func main() {
	initKubeLogs()

	actionConfig := new(action.Configuration)
	cmd := newRootCmd(actionConfig, os.Stdout, os.Args[1:])

	if calledCmd, _, err := cmd.Find(os.Args[1:]); err == nil && calledCmd.Name() == completion.CompRequestCmd {
		// If completion is being called, we have to check if the completion is for the "--kube-context"
		// value; if it is, we cannot call the action.Init() method with an incomplete kube-context value
		// or else it will fail immediately.  So, we simply unset the invalid kube-context value.
		if args := os.Args[1:]; len(args) > 2 && args[len(args)-2] == "--kube-context" {
			// We are completing the kube-context value!  Reset it as the current value is not valid.
			settings.KubeContext = ""
		}
	}

	helmDriver := os.Getenv("HELM_DRIVER")
	if err := actionConfig.Init(settings.RESTClientGetter(), settings.Namespace(), helmDriver, debug); err != nil {
		log.Fatal(err)
	}
	if helmDriver == "memory" {
		loadReleasesInMemory(actionConfig)
	}

	if err := cmd.Execute(); err != nil {
		debug("%+v", err)
		switch e := err.(type) {
		case pluginError:
			os.Exit(e.code)
		default:
			os.Exit(1)
		}
	}
}

// wordSepNormalizeFunc changes all flags that contain "_" separators
func wordSepNormalizeFunc(f *pflag.FlagSet, name string) pflag.NormalizedName {
	return pflag.NormalizedName(strings.ReplaceAll(name, "_", "-"))
}

func checkOCIFeatureGate() func(_ *cobra.Command, _ []string) error {
	return func(_ *cobra.Command, _ []string) error {
		if !FeatureGateOCI.IsEnabled() {
			return FeatureGateOCI.Error()
		}
		return nil
	}
}

// This function loads releases into the memory storage if the
// environment variable is properly set.
func loadReleasesInMemory(actionConfig *action.Configuration) {
	filePaths := strings.Split(os.Getenv("HELM_MEMORY_DRIVER_DATA"), ":")
	if len(filePaths) == 0 {
		return
	}

	store := actionConfig.Releases
	mem, ok := store.Driver.(*driver.Memory)
	if !ok {
		// For an unexpected reason we are not dealing with the memory storage driver.
		return
	}

	actionConfig.KubeClient = &kubefake.PrintingKubeClient{Out: ioutil.Discard}

	for _, path := range filePaths {
		b, err := ioutil.ReadFile(path)
		if err != nil {
			log.Fatal("Unable to read memory driver data", err)
		}

		releases := []*release.Release{}
		if err := yaml.Unmarshal(b, &releases); err != nil {
			log.Fatal("Unable to unmarshal memory driver data: ", err)
		}

		for _, rel := range releases {
			if err := store.Create(rel); err != nil {
				log.Fatal(err)
			}
		}
	}
	// Must reset namespace to the proper one
	mem.SetNamespace(settings.Namespace())
}
<commit_msg>chore(comp): Remove unnecessary code<commit_after>/*
Copyright The Helm Authors.

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

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

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

package main // import "helm.sh/helm/v3/cmd/helm"

import (
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"strings"

	"github.com/spf13/cobra"
	"github.com/spf13/pflag"
	"k8s.io/klog"
	"sigs.k8s.io/yaml"

	// Import to initialize client auth plugins.
	_ "k8s.io/client-go/plugin/pkg/client/auth"

	"helm.sh/helm/v3/pkg/action"
	"helm.sh/helm/v3/pkg/cli"
	"helm.sh/helm/v3/pkg/gates"
	kubefake "helm.sh/helm/v3/pkg/kube/fake"
	"helm.sh/helm/v3/pkg/release"
	"helm.sh/helm/v3/pkg/storage/driver"
)

// FeatureGateOCI is the feature gate for checking if `helm chart` and `helm registry` commands should work
const FeatureGateOCI = gates.Gate("HELM_EXPERIMENTAL_OCI")

var (
	settings = cli.New()
)

func init() {
	log.SetFlags(log.Lshortfile)
}

func debug(format string, v ...interface{}) {
	if settings.Debug {
		format = fmt.Sprintf("[debug] %s\n", format)
		log.Output(2, fmt.Sprintf(format, v...))
	}
}

func initKubeLogs() {
	pflag.CommandLine.SetNormalizeFunc(wordSepNormalizeFunc)
	gofs := flag.NewFlagSet("klog", flag.ExitOnError)
	klog.InitFlags(gofs)
	pflag.CommandLine.AddGoFlagSet(gofs)
	pflag.CommandLine.Set("logtostderr", "true")
}

func main() {
	initKubeLogs()

	actionConfig := new(action.Configuration)
	cmd := newRootCmd(actionConfig, os.Stdout, os.Args[1:])

	helmDriver := os.Getenv("HELM_DRIVER")
	if err := actionConfig.Init(settings.RESTClientGetter(), settings.Namespace(), helmDriver, debug); err != nil {
		log.Fatal(err)
	}
	if helmDriver == "memory" {
		loadReleasesInMemory(actionConfig)
	}

	if err := cmd.Execute(); err != nil {
		debug("%+v", err)
		switch e := err.(type) {
		case pluginError:
			os.Exit(e.code)
		default:
			os.Exit(1)
		}
	}
}

// wordSepNormalizeFunc changes all flags that contain "_" separators
func wordSepNormalizeFunc(f *pflag.FlagSet, name string) pflag.NormalizedName {
	return pflag.NormalizedName(strings.ReplaceAll(name, "_", "-"))
}

func checkOCIFeatureGate() func(_ *cobra.Command, _ []string) error {
	return func(_ *cobra.Command, _ []string) error {
		if !FeatureGateOCI.IsEnabled() {
			return FeatureGateOCI.Error()
		}
		return nil
	}
}

// This function loads releases into the memory storage if the
// environment variable is properly set.
func loadReleasesInMemory(actionConfig *action.Configuration) {
	filePaths := strings.Split(os.Getenv("HELM_MEMORY_DRIVER_DATA"), ":")
	if len(filePaths) == 0 {
		return
	}

	store := actionConfig.Releases
	mem, ok := store.Driver.(*driver.Memory)
	if !ok {
		// For an unexpected reason we are not dealing with the memory storage driver.
		return
	}

	actionConfig.KubeClient = &kubefake.PrintingKubeClient{Out: ioutil.Discard}

	for _, path := range filePaths {
		b, err := ioutil.ReadFile(path)
		if err != nil {
			log.Fatal("Unable to read memory driver data", err)
		}

		releases := []*release.Release{}
		if err := yaml.Unmarshal(b, &releases); err != nil {
			log.Fatal("Unable to unmarshal memory driver data: ", err)
		}

		for _, rel := range releases {
			if err := store.Create(rel); err != nil {
				log.Fatal(err)
			}
		}
	}
	// Must reset namespace to the proper one
	mem.SetNamespace(settings.Namespace())
}
<|endoftext|>


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

/*
IPOAM verifies IP-layer connectivity and discovers an IP-layer path
like ping and traceroute commands.

Usage:	ipoam command [flags] [arguments]

The commands are:
	cv|ping                 Verify IP-layer connectivity
	rt|pathdisc|traceroute  Discover an IP-layer path
	sh|show|list            Show network facility information

Verify IP-layer connectivity

CV (Connectivity Verification) uses both ICMP echo request and reply
messages for verifying IP-layer connectivity. The destination can be
unicast (including anycast), multicast or broadcast addresses.
Also it can be a single or multiple addresses.

Usage:	ipoam cv|ping [flags] destination

destination
	A hostname, DNS reg-name, IP address, IP address prefix, or
	comma-separated list of IP addresses and/or IP address prefixes.
	A combination of unicast and multicast addresses is prohibited.

Flags:
	-4	Run IPv4 test only
	-6	Run IPv6 test only
	-count int
		Iteration count, less than or equal to zero will run until interruped
	-hops int
		IPv4 TTL or IPv6 hop-limit on outgoing unicast packets (default 64)
	-if string
		Outbound interface name
	-mchops int
		IPv4 TTL or IPv6 hop-limit on outgoing multicast packets (default 5)
	-pldlen int
		ICMP echo payload length (default 56)
	-q	Quiet output except summary
	-src string
		Source IP address
	-tc int
		IPv4 TOS or IPv6 traffic-class on outgoing packets
	-v	Show verbose information
	-wait int
		Seconds between transmitting each echo (default 1)
	-x	Run transmission only

A sample output:

	% sudo ipoam cv -v -4 -count=3 golang.org
	Connectivity verification for golang.org [173.194.72.141]: 56 bytes payload
	56 bytes tc=0x0 hops=36 from=173.194.72.141 to=192.168.0.1 if=en1 echo.id=46383 echo.seq=1 rtt=162.508918ms
	56 bytes tc=0x0 hops=36 from=173.194.72.141 to=192.168.0.1 if=en1 echo.id=46383 echo.seq=2 rtt=157.233688ms
	56 bytes tc=0x0 hops=36 from=173.194.72.141 to=192.168.0.1 if=en1 echo.id=46383 echo.seq=3 rtt=160.959004ms

	Statistical information for golang.org:
	173.194.72.141: 0.0% loss, rcvd=3 sent=3 op.err=0 icmp.err=0 min=157.233688ms avg=160.23387ms max=162.508918ms stddev=2.213801ms

Discover an IP-layer path

RT (Route Trace) transmits probe packets and discovers a route to the
destination by determining recevived ICMP error messages from nodes
along the route. The probe packets can be carried by either UDP or
ICMP.

Usage:	ipoam rt|pathdisc|traceroute [flags] destination

destination
	A hostname, DNS reg-name or IP address.

Flags:
	-4	Run IPv4 test only
	-6	Run IPv6 test only
	-count int
		Per-hop probe count (default 3)
	-hops int
		Maximum IPv4 TTL or IPv6 hop-limit (default 30)
	-if string
		Outbound interface name
	-m	Use ICMP for probe packets instead of UDP
	-n	Don't use DNS reverse lookup
	-pldlen int
		Probe packet payload length (default 56)
	-port int
		Base destination port number, range will be [port, port+hops) (default 33434)
	-src string
		Source IP address
	-tc int
		IPv4 TOS or IPv6 traffic-class on probe packets
	-v	Show verbose information
	-wait int
		Seconds between transmitting each probe (default 1)

A sample output:

	% sudo ipoam rt -v golang.org
	Path discovery for golang.org: 30 hops max, 3 per-hop probes, 56 bytes payload
	Warning: golang.org has multiple addresses, using 173.194.72.141
	  1  192.168.0.254 tc=0x0 hops=255 to=192.168.0.1 if=en1  6.881108ms  1.259877ms  1.216255ms
	  2  *  1.002800195s  1.004971212s  1.001800509s
	[...]
	 15  72.14.204.58 tc=0x0 hops=241 to=192.168.0.1 if=en1  94.682646ms  94.999308ms  81.693418ms
	 16  72.14.239.202 tc=0x0 hops=240 to=192.168.0.1 if=en1  105.799675ms  94.859865ms  94.828963ms
	 17  66.249.94.80 tc=0x0 hops=233 to=192.168.0.1 if=en1 <label=28257 tc=0x4 s=true ttl=1>  149.924191ms
	     209.85.245.206 tc=0x0 hops=233 to=192.168.0.1 if=en1 <label=32722 tc=0x4 s=true ttl=1>  154.502803ms  150.42802ms
	 18  72.14.233.137 tc=0x0 hops=234 to=192.168.0.1 if=en1 <label=693726 tc=0x4 s=true ttl=1>  164.885999ms
	     72.14.235.147 tc=0x0 hops=234 to=192.168.0.1 if=en1 <label=453203 tc=0x4 s=true ttl=1>  154.552519ms
	     209.85.248.129 tc=0x0 hops=234 to=192.168.0.1 if=en1 <label=24431 tc=0x4 s=true ttl=1>  154.920485ms
	 19  72.14.237.171 tc=0x0 hops=235 to=192.168.0.1 if=en1  170.145454ms
	     209.85.243.21 tc=0x0 hops=235 to=192.168.0.1 if=en1  118.445733ms  154.649823ms
	 20  *  1.000688625s  1.000789355s  1.001540175s
	 21  tf-in-f141.1e100.net. (173.194.72.141) tc=0x0 hops=37 to=192.168.0.1 if=en1  161.714887ms  154.699088ms  139.969393ms

Show network facility information

Show displays network facility information.

Usage:	ipoam sh|show|list [flags] int|interfaces [interface name]

Flags:
	-4	Show IPv4 information only
	-6	Show IPv6 information only
	-b	Show brief information

A sample output:

	% ipoam sh -b int
	Name              Index  Status  Routed      MTU    Hardware address
	lo0               1      up      link-local  16384  <nil>
	gif0              2      down                1280   <nil>
	stf0              3      down                1280   <nil>
	en0               4      up                  1500   00:01:02:ab:cd:01
	en1               5      up      global      1500   00:01:02:ab:cd:02
	fw0               6      up                  4078   00:01:02:03:ab:cd:ef:01
	p2p0              7      up                  2304   00:01:02:ab:cd:03
*/
package main
<commit_msg>ipoam/cmd/ipoam: fix typo<commit_after>// Copyright 2015 Mikio Hara. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

/*
IPOAM verifies IP-layer connectivity and discovers an IP-layer path
like ping and traceroute commands.

Usage:	ipoam command [flags] [arguments]

The commands are:
	cv|ping                 Verify IP-layer connectivity
	rt|pathdisc|traceroute  Discover an IP-layer path
	sh|show|list            Show network facility information

Verify IP-layer connectivity

CV (Connectivity Verification) uses both ICMP echo request and reply
messages for verifying IP-layer connectivity. The destination can be
unicast (including anycast), multicast or broadcast addresses.
Also it can be a single or multiple addresses.

Usage:	ipoam cv|ping [flags] destination

destination
	A hostname, DNS reg-name, IP address, IP address prefix, or
	comma-separated list of IP addresses and/or IP address prefixes.
	A combination of unicast and multicast addresses is prohibited.

Flags:
	-4	Run IPv4 test only
	-6	Run IPv6 test only
	-count int
		Iteration count, less than or equal to zero will run until interruped
	-hops int
		IPv4 TTL or IPv6 hop-limit on outgoing unicast packets (default 64)
	-if string
		Outbound interface name
	-mchops int
		IPv4 TTL or IPv6 hop-limit on outgoing multicast packets (default 5)
	-pldlen int
		ICMP echo payload length (default 56)
	-q	Quiet output except summary
	-src string
		Source IP address
	-tc int
		IPv4 TOS or IPv6 traffic-class on outgoing packets
	-v	Show verbose information
	-wait int
		Seconds between transmitting each echo (default 1)
	-x	Run transmission only

A sample output:

	% sudo ipoam cv -v -4 -count=3 golang.org
	Connectivity verification for golang.org [173.194.72.141]: 56 bytes payload
	56 bytes tc=0x0 hops=36 from=173.194.72.141 to=192.168.0.1 if=en1 echo.id=46383 echo.seq=1 rtt=162.508918ms
	56 bytes tc=0x0 hops=36 from=173.194.72.141 to=192.168.0.1 if=en1 echo.id=46383 echo.seq=2 rtt=157.233688ms
	56 bytes tc=0x0 hops=36 from=173.194.72.141 to=192.168.0.1 if=en1 echo.id=46383 echo.seq=3 rtt=160.959004ms

	Statistical information for golang.org:
	173.194.72.141: 0.0% loss, rcvd=3 sent=3 op.err=0 icmp.err=0 min=157.233688ms avg=160.23387ms max=162.508918ms stddev=2.213801ms

Discover an IP-layer path

RT (Route Trace) transmits probe packets and discovers a route to the
destination by determining received ICMP error messages from nodes
along the route. The probe packets can be carried by either UDP or
ICMP.

Usage:	ipoam rt|pathdisc|traceroute [flags] destination

destination
	A hostname, DNS reg-name or IP address.

Flags:
	-4	Run IPv4 test only
	-6	Run IPv6 test only
	-count int
		Per-hop probe count (default 3)
	-hops int
		Maximum IPv4 TTL or IPv6 hop-limit (default 30)
	-if string
		Outbound interface name
	-m	Use ICMP for probe packets instead of UDP
	-n	Don't use DNS reverse lookup
	-pldlen int
		Probe packet payload length (default 56)
	-port int
		Base destination port number, range will be [port, port+hops) (default 33434)
	-src string
		Source IP address
	-tc int
		IPv4 TOS or IPv6 traffic-class on probe packets
	-v	Show verbose information
	-wait int
		Seconds between transmitting each probe (default 1)

A sample output:

	% sudo ipoam rt -v golang.org
	Path discovery for golang.org: 30 hops max, 3 per-hop probes, 56 bytes payload
	Warning: golang.org has multiple addresses, using 173.194.72.141
	  1  192.168.0.254 tc=0x0 hops=255 to=192.168.0.1 if=en1  6.881108ms  1.259877ms  1.216255ms
	  2  *  1.002800195s  1.004971212s  1.001800509s
	[...]
	 15  72.14.204.58 tc=0x0 hops=241 to=192.168.0.1 if=en1  94.682646ms  94.999308ms  81.693418ms
	 16  72.14.239.202 tc=0x0 hops=240 to=192.168.0.1 if=en1  105.799675ms  94.859865ms  94.828963ms
	 17  66.249.94.80 tc=0x0 hops=233 to=192.168.0.1 if=en1 <label=28257 tc=0x4 s=true ttl=1>  149.924191ms
	     209.85.245.206 tc=0x0 hops=233 to=192.168.0.1 if=en1 <label=32722 tc=0x4 s=true ttl=1>  154.502803ms  150.42802ms
	 18  72.14.233.137 tc=0x0 hops=234 to=192.168.0.1 if=en1 <label=693726 tc=0x4 s=true ttl=1>  164.885999ms
	     72.14.235.147 tc=0x0 hops=234 to=192.168.0.1 if=en1 <label=453203 tc=0x4 s=true ttl=1>  154.552519ms
	     209.85.248.129 tc=0x0 hops=234 to=192.168.0.1 if=en1 <label=24431 tc=0x4 s=true ttl=1>  154.920485ms
	 19  72.14.237.171 tc=0x0 hops=235 to=192.168.0.1 if=en1  170.145454ms
	     209.85.243.21 tc=0x0 hops=235 to=192.168.0.1 if=en1  118.445733ms  154.649823ms
	 20  *  1.000688625s  1.000789355s  1.001540175s
	 21  tf-in-f141.1e100.net. (173.194.72.141) tc=0x0 hops=37 to=192.168.0.1 if=en1  161.714887ms  154.699088ms  139.969393ms

Show network facility information

Show displays network facility information.

Usage:	ipoam sh|show|list [flags] int|interfaces [interface name]

Flags:
	-4	Show IPv4 information only
	-6	Show IPv6 information only
	-b	Show brief information

A sample output:

	% ipoam sh -b int
	Name              Index  Status  Routed      MTU    Hardware address
	lo0               1      up      link-local  16384  <nil>
	gif0              2      down                1280   <nil>
	stf0              3      down                1280   <nil>
	en0               4      up                  1500   00:01:02:ab:cd:01
	en1               5      up      global      1500   00:01:02:ab:cd:02
	fw0               6      up                  4078   00:01:02:03:ab:cd:ef:01
	p2p0              7      up                  2304   00:01:02:ab:cd:03
*/
package main
<|endoftext|>