<commit_before>package git

import (
	"fmt"
	"os"
	"os/exec"
	"testing"

	"github.com/stretchr/testify/assert"

	"github.com/Sirupsen/logrus"
	"srcd.works/go-git.v4"
	"srcd.works/go-git.v4/plumbing"
	"srcd.works/go-git.v4/plumbing/object"
)

var repo *git.Repository
var gitRepositoryPath = "testing-repository"

func setup() {
	path, err := os.Getwd()

	if err != nil {
		fmt.Println(err)
		os.Exit(1)
	}

	repo, err = git.PlainOpen(path + "/" + gitRepositoryPath)

	if err != nil {
		fmt.Println(err)
		os.Exit(1)
	}
}

func getCommitFromRef(ref string) *object.Commit {
	cmd := exec.Command("git", "rev-parse", ref)
	cmd.Dir = gitRepositoryPath

	ID, err := cmd.Output()
	ID = ID[:len(ID)-1]

	if err != nil {
		logrus.WithField("ID", string(ID)).Fatal(err)
	}

	c, err := repo.Commit(plumbing.NewHash(string(ID)))

	if err != nil {
		logrus.WithField("ID", ID).Fatal(err)
	}

	return c
}

func TestMain(m *testing.M) {
	setup()
	code := m.Run()
	os.Exit(code)
}

func TestResolveRef(t *testing.T) {
	type g struct {
		ref string
		f   func(*object.Commit, error)
	}

	tests := []g{
		{
			"HEAD",
			func(o *object.Commit, err error) {
				assert.NoError(t, err)
				assert.True(t, o.ID().String() == getCommitFromRef("HEAD").ID().String(), "Must resolve HEAD reference")
			},
		},
		{
			"test1",
			func(o *object.Commit, err error) {
				assert.NoError(t, err)
				assert.True(t, o.ID().String() == getCommitFromRef("test1").ID().String(), "Must resolve branch reference")
			},
		},
		{
			getCommitFromRef("test1").ID().String(),
			func(o *object.Commit, err error) {
				assert.NoError(t, err)
				assert.True(t, o.ID().String() == getCommitFromRef("test1").ID().String(), "Must resolve commit id")
			},
		},
		{
			"whatever",
			func(o *object.Commit, err error) {
				assert.Error(t, err)
				assert.EqualError(t, err, `reference "whatever" can't be found in git repository`)
			},
		},
	}

	for _, test := range tests {
		test.f(resolveRef(test.ref, repo))
	}
}

func TestResolveRefWithErrors(t *testing.T) {
	type g struct {
		ref  string
		repo *git.Repository
		f    func(*object.Commit, error)
	}

	tests := []g{
		{
			"whatever",
			repo,
			func(o *object.Commit, err error) {
				assert.Error(t, err)
				assert.EqualError(t, err, `reference "whatever" can't be found in git repository`)
			},
		},
	}

	for _, test := range tests {
		test.f(resolveRef(test.ref, test.repo))
	}
}

func TestFetchCommits(t *testing.T) {
	type g struct {
		path    string
		toRef   string
		fromRef string
		f       func(*[]object.Commit, error)
	}

	tests := []g{
		{
			gitRepositoryPath,
			getCommitFromRef("HEAD").ID().String(),
			getCommitFromRef("test").ID().String(),
			func(cs *[]object.Commit, err error) {
				assert.Error(t, err)
				assert.Regexp(t, `can't produce a diff between .*? and .*?, check your range is correct by running "git log .*?\.\..*?" command`, err.Error())
			},
		},
		{
			gitRepositoryPath,
			getCommitFromRef("HEAD~1").ID().String(),
			getCommitFromRef("HEAD~3").ID().String(),
			func(cs *[]object.Commit, err error) {
				assert.Error(t, err)
				assert.Regexp(t, `can't produce a diff between .*? and .*?, check your range is correct by running "git log .*?\.\..*?" command`, err.Error())
			},
		},
		{
			gitRepositoryPath,
			getCommitFromRef("HEAD~3").ID().String(),
			getCommitFromRef("test~2^2").ID().String(),
			func(cs *[]object.Commit, err error) {
				assert.NoError(t, err)
				assert.Len(t, *cs, 5)

				commitTests := []string{
					"Merge branch 'test2' into test1\n",
					"feat(file6) : new file 6\n\ncreate a new file 6\n",
					"feat(file5) : new file 5\n\ncreate a new file 5\n",
					"feat(file4) : new file 4\n\ncreate a new file 4\n",
					"feat(file3) : new file 3\n\ncreate a new file 3\n",
				}

				for i, c := range *cs {
					assert.Equal(t, commitTests[i], c.Message)
				}
			},
		},
		{
			gitRepositoryPath,
			getCommitFromRef("HEAD~4").ID().String(),
			getCommitFromRef("test~2^2^2").ID().String(),
			func(cs *[]object.Commit, err error) {
				assert.NoError(t, err, "Must return no errors")
				assert.Len(t, *cs, 5, "Must contains 3 commits")

				commitTests := []string{
					"feat(file6) : new file 6\n\ncreate a new file 6\n",
					"feat(file5) : new file 5\n\ncreate a new file 5\n",
					"feat(file4) : new file 4\n\ncreate a new file 4\n",
					"feat(file3) : new file 3\n\ncreate a new file 3\n",
					"feat(file2) : new file 2\n\ncreate a new file 2\n",
				}

				for i, c := range *cs {
					assert.Equal(t, commitTests[i], c.Message)
				}
			},
		},
		{
			"whatever",
			getCommitFromRef("HEAD").ID().String(),
			getCommitFromRef("HEAD~1").ID().String(),
			func(cs *[]object.Commit, err error) {
				assert.EqualError(t, err, `check "whatever" is an existing git repository path`)
			},
		},
		{
			gitRepositoryPath,
			"whatever",
			getCommitFromRef("HEAD~1").ID().String(),
			func(cs *[]object.Commit, err error) {
				assert.EqualError(t, err, `reference "whatever" can't be found in git repository`)
			},
		},
		{
			gitRepositoryPath,
			getCommitFromRef("HEAD~1").ID().String(),
			"whatever",
			func(cs *[]object.Commit, err error) {
				assert.EqualError(t, err, `reference "whatever" can't be found in git repository`)
			},
		},
		{
			gitRepositoryPath,
			"HEAD",
			"HEAD",
			func(cs *[]object.Commit, err error) {
				assert.EqualError(t, err, `can't produce a diff between HEAD and HEAD, check your range is correct by running "git log HEAD..HEAD" command`)
			},
		},
	}

	for _, test := range tests {
		test.f(FetchCommits(test.path, test.toRef, test.fromRef))
	}
}

func TestShallowCloneProducesNoErrors(t *testing.T) {
	path := "shallow-repository-test"
	cmd := exec.Command("rm", "-rf", path)
	_, err := cmd.Output()

	assert.NoError(t, err, "Must delete repository")

	cmd = exec.Command("git", "clone", "--depth", "2", "https://github.com/octocat/Spoon-Knife.git", path)
	_, err = cmd.Output()

	assert.NoError(t, err, "Must shallow clone repository")

	cmd = exec.Command("git", "rev-parse", "HEAD~1")
	cmd.Dir = path

	fromRef, err := cmd.Output()
	fromRef = fromRef[:len(fromRef)-1]

	assert.NoError(t, err, "Must extract HEAD~1")

	cmd = exec.Command("git", "rev-parse", "HEAD")
	cmd.Dir = path

	toRef, err := cmd.Output()
	toRef = toRef[:len(toRef)-1]

	assert.NoError(t, err, "Must extract HEAD")

	commits, err := FetchCommits("shallow-repository-test", string(fromRef), string(toRef))

	assert.NoError(t, err)
	assert.Len(t, *commits, 1, "Must fetch commits in shallow clone")
}
<commit_msg>doc(chyle/git) : remove useless comments<commit_after>package git

import (
	"fmt"
	"os"
	"os/exec"
	"testing"

	"github.com/stretchr/testify/assert"

	"github.com/Sirupsen/logrus"
	"srcd.works/go-git.v4"
	"srcd.works/go-git.v4/plumbing"
	"srcd.works/go-git.v4/plumbing/object"
)

var repo *git.Repository
var gitRepositoryPath = "testing-repository"

func setup() {
	path, err := os.Getwd()

	if err != nil {
		fmt.Println(err)
		os.Exit(1)
	}

	repo, err = git.PlainOpen(path + "/" + gitRepositoryPath)

	if err != nil {
		fmt.Println(err)
		os.Exit(1)
	}
}

func getCommitFromRef(ref string) *object.Commit {
	cmd := exec.Command("git", "rev-parse", ref)
	cmd.Dir = gitRepositoryPath

	ID, err := cmd.Output()
	ID = ID[:len(ID)-1]

	if err != nil {
		logrus.WithField("ID", string(ID)).Fatal(err)
	}

	c, err := repo.Commit(plumbing.NewHash(string(ID)))

	if err != nil {
		logrus.WithField("ID", ID).Fatal(err)
	}

	return c
}

func TestMain(m *testing.M) {
	setup()
	code := m.Run()
	os.Exit(code)
}

func TestResolveRef(t *testing.T) {
	type g struct {
		ref string
		f   func(*object.Commit, error)
	}

	tests := []g{
		{
			"HEAD",
			func(o *object.Commit, err error) {
				assert.NoError(t, err)
				assert.True(t, o.ID().String() == getCommitFromRef("HEAD").ID().String(), "Must resolve HEAD reference")
			},
		},
		{
			"test1",
			func(o *object.Commit, err error) {
				assert.NoError(t, err)
				assert.True(t, o.ID().String() == getCommitFromRef("test1").ID().String(), "Must resolve branch reference")
			},
		},
		{
			getCommitFromRef("test1").ID().String(),
			func(o *object.Commit, err error) {
				assert.NoError(t, err)
				assert.True(t, o.ID().String() == getCommitFromRef("test1").ID().String(), "Must resolve commit id")
			},
		},
		{
			"whatever",
			func(o *object.Commit, err error) {
				assert.Error(t, err)
				assert.EqualError(t, err, `reference "whatever" can't be found in git repository`)
			},
		},
	}

	for _, test := range tests {
		test.f(resolveRef(test.ref, repo))
	}
}

func TestResolveRefWithErrors(t *testing.T) {
	type g struct {
		ref  string
		repo *git.Repository
		f    func(*object.Commit, error)
	}

	tests := []g{
		{
			"whatever",
			repo,
			func(o *object.Commit, err error) {
				assert.Error(t, err)
				assert.EqualError(t, err, `reference "whatever" can't be found in git repository`)
			},
		},
	}

	for _, test := range tests {
		test.f(resolveRef(test.ref, test.repo))
	}
}

func TestFetchCommits(t *testing.T) {
	type g struct {
		path    string
		toRef   string
		fromRef string
		f       func(*[]object.Commit, error)
	}

	tests := []g{
		{
			gitRepositoryPath,
			getCommitFromRef("HEAD").ID().String(),
			getCommitFromRef("test").ID().String(),
			func(cs *[]object.Commit, err error) {
				assert.Error(t, err)
				assert.Regexp(t, `can't produce a diff between .*? and .*?, check your range is correct by running "git log .*?\.\..*?" command`, err.Error())
			},
		},
		{
			gitRepositoryPath,
			getCommitFromRef("HEAD~1").ID().String(),
			getCommitFromRef("HEAD~3").ID().String(),
			func(cs *[]object.Commit, err error) {
				assert.Error(t, err)
				assert.Regexp(t, `can't produce a diff between .*? and .*?, check your range is correct by running "git log .*?\.\..*?" command`, err.Error())
			},
		},
		{
			gitRepositoryPath,
			getCommitFromRef("HEAD~3").ID().String(),
			getCommitFromRef("test~2^2").ID().String(),
			func(cs *[]object.Commit, err error) {
				assert.NoError(t, err)
				assert.Len(t, *cs, 5)

				commitTests := []string{
					"Merge branch 'test2' into test1\n",
					"feat(file6) : new file 6\n\ncreate a new file 6\n",
					"feat(file5) : new file 5\n\ncreate a new file 5\n",
					"feat(file4) : new file 4\n\ncreate a new file 4\n",
					"feat(file3) : new file 3\n\ncreate a new file 3\n",
				}

				for i, c := range *cs {
					assert.Equal(t, commitTests[i], c.Message)
				}
			},
		},
		{
			gitRepositoryPath,
			getCommitFromRef("HEAD~4").ID().String(),
			getCommitFromRef("test~2^2^2").ID().String(),
			func(cs *[]object.Commit, err error) {
				assert.NoError(t, err)
				assert.Len(t, *cs, 5)

				commitTests := []string{
					"feat(file6) : new file 6\n\ncreate a new file 6\n",
					"feat(file5) : new file 5\n\ncreate a new file 5\n",
					"feat(file4) : new file 4\n\ncreate a new file 4\n",
					"feat(file3) : new file 3\n\ncreate a new file 3\n",
					"feat(file2) : new file 2\n\ncreate a new file 2\n",
				}

				for i, c := range *cs {
					assert.Equal(t, commitTests[i], c.Message)
				}
			},
		},
		{
			"whatever",
			getCommitFromRef("HEAD").ID().String(),
			getCommitFromRef("HEAD~1").ID().String(),
			func(cs *[]object.Commit, err error) {
				assert.EqualError(t, err, `check "whatever" is an existing git repository path`)
			},
		},
		{
			gitRepositoryPath,
			"whatever",
			getCommitFromRef("HEAD~1").ID().String(),
			func(cs *[]object.Commit, err error) {
				assert.EqualError(t, err, `reference "whatever" can't be found in git repository`)
			},
		},
		{
			gitRepositoryPath,
			getCommitFromRef("HEAD~1").ID().String(),
			"whatever",
			func(cs *[]object.Commit, err error) {
				assert.EqualError(t, err, `reference "whatever" can't be found in git repository`)
			},
		},
		{
			gitRepositoryPath,
			"HEAD",
			"HEAD",
			func(cs *[]object.Commit, err error) {
				assert.EqualError(t, err, `can't produce a diff between HEAD and HEAD, check your range is correct by running "git log HEAD..HEAD" command`)
			},
		},
	}

	for _, test := range tests {
		test.f(FetchCommits(test.path, test.toRef, test.fromRef))
	}
}

func TestShallowCloneProducesNoErrors(t *testing.T) {
	path := "shallow-repository-test"
	cmd := exec.Command("rm", "-rf", path)
	_, err := cmd.Output()

	assert.NoError(t, err)

	cmd = exec.Command("git", "clone", "--depth", "2", "https://github.com/octocat/Spoon-Knife.git", path)
	_, err = cmd.Output()

	assert.NoError(t, err)

	cmd = exec.Command("git", "rev-parse", "HEAD~1")
	cmd.Dir = path

	fromRef, err := cmd.Output()
	fromRef = fromRef[:len(fromRef)-1]

	assert.NoError(t, err, "Must extract HEAD~1")

	cmd = exec.Command("git", "rev-parse", "HEAD")
	cmd.Dir = path

	toRef, err := cmd.Output()
	toRef = toRef[:len(toRef)-1]

	assert.NoError(t, err)

	commits, err := FetchCommits("shallow-repository-test", string(fromRef), string(toRef))

	assert.NoError(t, err)
	assert.Len(t, *commits, 1, "Must fetch commits in shallow clone")
}
<|endoftext|>


<commit_before>// Copyright 2016 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 clientv3

import (
	"sync"
	"time"

	"github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc"
	"github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/codes"
	pb "github.com/coreos/etcd/etcdserver/etcdserverpb"
)

// Client provides and manages an etcd v3 client session.
type Client struct {
	// KV is the keyvalue API for the client's connection.
	KV pb.KVClient
	// Lease is the lease API for the client's connection.
	Lease pb.LeaseClient
	// Watch is the watch API for the client's connection.
	Watch pb.WatchClient
	// Cluster is the cluster API for the client's connection.
	Cluster pb.ClusterClient

	conn   *grpc.ClientConn
	cfg    Config
	mu     sync.RWMutex // protects connection selection and error list
	errors []error      // errors passed to retryConnection
}

// EndpointDialer is a policy for choosing which endpoint to dial next
type EndpointDialer func(*Client) (*grpc.ClientConn, error)

type Config struct {
	// Endpoints is a list of URLs
	Endpoints []string

	// RetryDialer chooses the next endpoint to use
	RetryDialer EndpointDialer

	// DialTimeout is the timeout for failing to establish a connection.
	DialTimeout time.Duration

	// TODO TLS options
}

// New creates a new etcdv3 client from a given configuration.
func New(cfg Config) (*Client, error) {
	if cfg.RetryDialer == nil {
		cfg.RetryDialer = dialEndpointList
	}
	// use a temporary skeleton client to bootstrap first connection
	conn, err := cfg.RetryDialer(&Client{cfg: cfg})
	if err != nil {
		return nil, err
	}
	return newClient(conn, &cfg), nil
}

// NewFromURL creates a new etcdv3 client from a URL.
func NewFromURL(url string) (*Client, error) {
	return New(Config{Endpoints: []string{url}})
}

// NewFromConn creates a new etcdv3 client from an established grpc Connection.
func NewFromConn(conn *grpc.ClientConn) *Client { return newClient(conn, nil) }

// Clone creates a copy of client with the old connection and new API clients.
func (c *Client) Clone() *Client { return newClient(c.conn, &c.cfg) }

// Close shuts down the client's etcd connections.
func (c *Client) Close() error {
	return c.conn.Close()
}

// Endpoints lists the registered endpoints for the client.
func (c *Client) Endpoints() []string { return c.cfg.Endpoints }

// Errors returns all errors that have been observed since called last.
func (c *Client) Errors() (errs []error) {
	c.mu.Lock()
	defer c.mu.Unlock()
	errs = c.errors
	c.errors = nil
	return errs
}

// Dial establishes a connection for a given endpoint using the client's config
func (c *Client) Dial(endpoint string) (*grpc.ClientConn, error) {
	// TODO: enable grpc.WithTransportCredentials(creds)
	conn, err := grpc.Dial(
		endpoint,
		grpc.WithBlock(),
		grpc.WithTimeout(c.cfg.DialTimeout),
		grpc.WithInsecure())
	if err != nil {
		return nil, err
	}
	return conn, nil
}

func newClient(conn *grpc.ClientConn, cfg *Config) *Client {
	if cfg == nil {
		cfg = &Config{RetryDialer: dialEndpointList}
	}
	return &Client{
		KV:      pb.NewKVClient(conn),
		Lease:   pb.NewLeaseClient(conn),
		Watch:   pb.NewWatchClient(conn),
		Cluster: pb.NewClusterClient(conn),
		conn:    conn,
		cfg:     *cfg,
	}
}

// activeConnection returns the current in-use connection
func (c *Client) activeConnection() *grpc.ClientConn {
	c.mu.RLock()
	defer c.mu.RUnlock()
	return c.conn
}

// refreshConnection establishes a new connection
func (c *Client) retryConnection(oldConn *grpc.ClientConn, err error) (*grpc.ClientConn, error) {
	c.mu.Lock()
	defer c.mu.Unlock()
	if err != nil {
		c.errors = append(c.errors, err)
	}
	if oldConn != c.conn {
		// conn has already been updated
		return c.conn, nil
	}
	conn, dialErr := c.cfg.RetryDialer(c)
	if dialErr != nil {
		c.errors = append(c.errors, dialErr)
		return nil, dialErr
	}
	c.conn = conn
	return c.conn, nil
}

// dialEndpoints attempts to connect to each endpoint in order until a
// connection is established.
func dialEndpointList(c *Client) (*grpc.ClientConn, error) {
	var err error
	for _, ep := range c.Endpoints() {
		conn, curErr := c.Dial(ep)
		if curErr != nil {
			err = curErr
		} else {
			return conn, nil
		}
	}
	return nil, err
}

func isRPCError(err error) bool {
	return grpc.Code(err) != codes.Unknown
}
<commit_msg>clientv3: support tls<commit_after>// Copyright 2016 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 clientv3

import (
	"sync"
	"time"

	"github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc"
	"github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/codes"
	"github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/credentials"
	pb "github.com/coreos/etcd/etcdserver/etcdserverpb"
	"github.com/coreos/etcd/pkg/transport"
)

// Client provides and manages an etcd v3 client session.
type Client struct {
	// KV is the keyvalue API for the client's connection.
	KV pb.KVClient
	// Lease is the lease API for the client's connection.
	Lease pb.LeaseClient
	// Watch is the watch API for the client's connection.
	Watch pb.WatchClient
	// Cluster is the cluster API for the client's connection.
	Cluster pb.ClusterClient

	conn   *grpc.ClientConn
	cfg    Config
	creds  *credentials.TransportAuthenticator
	mu     sync.RWMutex // protects connection selection and error list
	errors []error      // errors passed to retryConnection
}

// EndpointDialer is a policy for choosing which endpoint to dial next
type EndpointDialer func(*Client) (*grpc.ClientConn, error)

type Config struct {
	// Endpoints is a list of URLs
	Endpoints []string

	// RetryDialer chooses the next endpoint to use
	RetryDialer EndpointDialer

	// DialTimeout is the timeout for failing to establish a connection.
	DialTimeout time.Duration

	// TLS holds the client secure credentials, if any.
	TLS *transport.TLSInfo
}

// New creates a new etcdv3 client from a given configuration.
func New(cfg Config) (*Client, error) {
	if cfg.RetryDialer == nil {
		cfg.RetryDialer = dialEndpointList
	}
	// use a temporary skeleton client to bootstrap first connection
	conn, err := cfg.RetryDialer(&Client{cfg: cfg})
	if err != nil {
		return nil, err
	}
	return newClient(conn, &cfg)
}

// NewFromURL creates a new etcdv3 client from a URL.
func NewFromURL(url string) (*Client, error) {
	return New(Config{Endpoints: []string{url}})
}

// NewFromConn creates a new etcdv3 client from an established grpc Connection.
func NewFromConn(conn *grpc.ClientConn) *Client { return mustNewClient(conn, nil) }

// Clone creates a copy of client with the old connection and new API clients.
func (c *Client) Clone() *Client { return mustNewClient(c.conn, &c.cfg) }

// Close shuts down the client's etcd connections.
func (c *Client) Close() error {
	return c.conn.Close()
}

// Endpoints lists the registered endpoints for the client.
func (c *Client) Endpoints() []string { return c.cfg.Endpoints }

// Errors returns all errors that have been observed since called last.
func (c *Client) Errors() (errs []error) {
	c.mu.Lock()
	defer c.mu.Unlock()
	errs = c.errors
	c.errors = nil
	return errs
}

// Dial establishes a connection for a given endpoint using the client's config
func (c *Client) Dial(endpoint string) (*grpc.ClientConn, error) {
	opts := []grpc.DialOption{
		grpc.WithBlock(),
		grpc.WithTimeout(c.cfg.DialTimeout),
	}
	if c.creds != nil {
		opts = append(opts, grpc.WithTransportCredentials(*c.creds))
	} else {
		opts = append(opts, grpc.WithInsecure())
	}
	conn, err := grpc.Dial(endpoint, opts...)
	if err != nil {
		return nil, err
	}
	return conn, nil
}

func mustNewClient(conn *grpc.ClientConn, cfg *Config) *Client {
	c, err := newClient(conn, cfg)
	if err != nil {
		panic("expected no error")
	}
	return c
}

func newClient(conn *grpc.ClientConn, cfg *Config) (*Client, error) {
	if cfg == nil {
		cfg = &Config{RetryDialer: dialEndpointList}
	}
	var creds *credentials.TransportAuthenticator
	if cfg.TLS != nil {
		tlscfg, err := cfg.TLS.ClientConfig()
		if err != nil {
			return nil, err
		}
		c := credentials.NewTLS(tlscfg)
		creds = &c
	}
	return &Client{
		KV:      pb.NewKVClient(conn),
		Lease:   pb.NewLeaseClient(conn),
		Watch:   pb.NewWatchClient(conn),
		Cluster: pb.NewClusterClient(conn),
		conn:    conn,
		cfg:     *cfg,
		creds:   creds,
	}, nil
}

// activeConnection returns the current in-use connection
func (c *Client) ActiveConnection() *grpc.ClientConn {
	c.mu.RLock()
	defer c.mu.RUnlock()
	return c.conn
}

// refreshConnection establishes a new connection
func (c *Client) retryConnection(oldConn *grpc.ClientConn, err error) (*grpc.ClientConn, error) {
	c.mu.Lock()
	defer c.mu.Unlock()
	if err != nil {
		c.errors = append(c.errors, err)
	}
	if oldConn != c.conn {
		// conn has already been updated
		return c.conn, nil
	}
	conn, dialErr := c.cfg.RetryDialer(c)
	if dialErr != nil {
		c.errors = append(c.errors, dialErr)
		return nil, dialErr
	}
	c.conn = conn
	return c.conn, nil
}

// dialEndpoints attempts to connect to each endpoint in order until a
// connection is established.
func dialEndpointList(c *Client) (*grpc.ClientConn, error) {
	var err error
	for _, ep := range c.Endpoints() {
		conn, curErr := c.Dial(ep)
		if curErr != nil {
			err = curErr
		} else {
			return conn, nil
		}
	}
	return nil, err
}

func isRPCError(err error) bool {
	return grpc.Code(err) != codes.Unknown
}
<|endoftext|>


<commit_before>package bdm

// Commands
const (
	GetVersion byte = 0xA2

	GetPowerState byte = 0x19
	SetPowerState byte = 0x18

	GetUserInputControl byte = 0x1D
	SetUserInputControl byte = 0x1C
)

// Arguments for get version command.
const (
	SICPVersion     byte = 0x00
	PlatformVersion byte = 0x01
)

// Arguments and result for power state.
const (
	PowerStateOff byte = 0x01
	PowerStateOn  byte = 0x02
)

// Arguments and result for user input control
const (
	UserInputMaskLocked   byte = 0x00 // 0b00000000
	UserInputMaskUnlocked byte = 0xff // 0b11111111

	UserInputMaskLocalKeyboard byte = 0x02 // 0b00000010
	UserInputMaskRemoteControl byte = 0x01 // 0b00000001
)
<commit_msg>Define Commands.<commit_after>package bdm

// Commands
const (
	GetVersion byte = 0xA2

	GetPowerState byte = 0x19
	SetPowerState byte = 0x18

	GetUserInputControl      byte = 0x1D
	SetUserInputControl      byte = 0x1C
	GetUserInputControlState byte = 0x1B
	SetUserInputControlState byte = 0x1A

	GetPowerAtColdStart byte = 0xA4
	SetPowerAtColdStart byte = 0xA3

	// TODO: argument and result
	SetInputSource byte = 0xAC

	GetCurrentSource byte = 0xAD

	GetAutoSignalDetecting byte = 0xAF
	SetAutoSignalDetecting byte = 0xAE

	GetVideoParameters  byte = 0x33
	SetVideoParameters  byte = 0x32
	GetColorTemperature byte = 0x35
	SetColorTemperature byte = 0x34
	GetColorParameters  byte = 0x37
	SetColorParameters  byte = 0x36

	GetPictureFormat      byte = 0x3B
	SetPictureFormat      byte = 0x3A
	GetVGAVideoParameters byte = 0x39
	SetVGAVideoParameters byte = 0x38

	GetPictureInPicture byte = 0x3D
	SetPictureInPicture byte = 0x3C

	GetPIPSource byte = 0x85
	SetPIPSource byte = 0x84

	GetVolume byte = 0x45
	SetVolume byte = 0x44

	SetVolumeLimits byte = 0xB8

	GetAudioParameters byte = 0x43
	SetAudioParameters byte = 0x42

	GetMiscInfo byte = 0x0F

	GetSmartPower byte = 0xDE
	SetSmartPower byte = 0xDD

	SetVideoAlignment byte = 0x70

	GetTemperature byte = 0x2F

	GetSerialCode byte = 0x15

	GetTiling byte = 0x23
	SetTiling byte = 0x22

	GetlightSensor byte = 0x25
	SetlightSensor byte = 0x24

	GetOSDRotating byte = 0x27
	SetOSDRotating byte = 0x26

	GetInformationOSDFeature byte = 0x2D
	SetInformationOSDFeature byte = 0x2C

	GetMEMCEffect byte = 0x29
	SetMEMCEffect byte = 0x28

	GetTouchFeature byte = 0x1F
	SetTouchFeature byte = 0x1E

	GetNoiseReductionFeature byte = 0x2B
	SetNoiseReductionFeature byte = 0x2A

	GetScanModeFeature byte = 0x51
	SetScanModeFeature byte = 0x50

	GetScanConversionFeature byte = 0x53
	SetScanConversionFeature byte = 0x52

	GetSwitchOnDelayFeature byte = 0x55
	SetSwitchOnDelayFeature byte = 0x54

	SetFactoryReset byte = 0x56
)

// Arguments for get version command.
const (
	SICPVersion     byte = 0x00
	PlatformVersion byte = 0x01
)

// Arguments and result for power state.
const (
	PowerStateOff byte = 0x01
	PowerStateOn  byte = 0x02
)

// Arguments and result for user input control
const (
	UserInputMaskLocked   byte = 0x00 // 0b00000000
	UserInputMaskUnlocked byte = 0xff // 0b11111111

	UserInputMaskLocalKeyboard byte = 0x02 // 0b00000010
	UserInputMaskRemoteControl byte = 0x01 // 0b00000001

	UserInputLockAll          byte = 0x01
	UserInputLockAllButVolume byte = 0x02
	UserInputLockAllButPower  byte = 0x03
)

// Argumetns and result for power state at cold start.
const (
	PowerAtColdStartPowerOff   byte = 0x00
	PowerAtColdStartForcedOn   byte = 0x01
	PowerAtColdStartLastStatus byte = 0x02
)
<|endoftext|>


<commit_before>package gramework

const (
	// MethodDELETE is the HTTP DELETE method
	MethodDELETE = "DELETE"

	// MethodGET is the HTTP GET method
	MethodGET = "GET"

	// MethodHEAD is the HTTP HEAD method
	MethodHEAD = "HEAD"

	// MethodOPTIONS is the HTTP OPTIONS method
	MethodOPTIONS = "OPTIONS"

	// MethodPATCH is the HTTP PATCH method
	MethodPATCH = "PATCH"

	// MethodPOST is the HTTP POST method
	MethodPOST = "POST"

	// MethodPUT is the HTTP PUT method
	MethodPUT = "PUT"
)

const (
	emptyString = ""

	fmtV = "%v"

	htmlCT = "text/html"
)
<commit_msg>const: set charset in html CT<commit_after>package gramework

const (
	// MethodDELETE is the HTTP DELETE method
	MethodDELETE = "DELETE"

	// MethodGET is the HTTP GET method
	MethodGET = "GET"

	// MethodHEAD is the HTTP HEAD method
	MethodHEAD = "HEAD"

	// MethodOPTIONS is the HTTP OPTIONS method
	MethodOPTIONS = "OPTIONS"

	// MethodPATCH is the HTTP PATCH method
	MethodPATCH = "PATCH"

	// MethodPOST is the HTTP POST method
	MethodPOST = "POST"

	// MethodPUT is the HTTP PUT method
	MethodPUT = "PUT"
)

const (
	emptyString = ""

	fmtV = "%v"

	htmlCT = "text/html; charset=utf8"
)
<|endoftext|>


<commit_before>// Copyright 2016-2017 Authors of Cilium
//
// 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 cmd

import (
	"bytes"
	"encoding/json"
	"fmt"
	"os"
	"regexp"
	"sort"
	"strconv"
	"strings"
	"text/tabwriter"

	"github.com/cilium/cilium/common"
	"github.com/cilium/cilium/pkg/bpf"
	"github.com/cilium/cilium/pkg/endpoint"
	"github.com/cilium/cilium/pkg/identity"
	"github.com/cilium/cilium/pkg/maps/policymap"
	"github.com/cilium/cilium/pkg/option"
	"github.com/cilium/cilium/pkg/u8proto"

	"github.com/spf13/cobra"
)

// Fatalf prints the Printf formatted message to stderr and exits the program
// Note: os.Exit(1) is not recoverable
func Fatalf(msg string, args ...interface{}) {
	fmt.Fprintf(os.Stderr, "Error: %s\n", fmt.Sprintf(msg, args...))
	os.Exit(1)
}

// Usagef prints the Printf formatted message to stderr, prints usage help and
// exits the program
// Note: os.Exit(1) is not recoverable
func Usagef(cmd *cobra.Command, msg string, args ...interface{}) {
	txt := fmt.Sprintf(msg, args...)
	fmt.Fprintf(os.Stderr, "Error: %s\n\n", txt)
	cmd.Help()
	os.Exit(1)
}

func requireEndpointID(cmd *cobra.Command, args []string) {
	if len(args) < 1 {
		Usagef(cmd, "Missing endpoint id argument")
	}

	if id := identity.ReservedIdentities[args[0]]; id == identity.IdentityUnknown {
		_, _, err := endpoint.ValidateID(args[0])

		if err != nil {
			Fatalf("Cannot parse endpoint id \"%s\": %s", args[0], err)
		}
	}
}

func requireEndpointIDorGlobal(cmd *cobra.Command, args []string) {
	if len(args) < 1 {
		Usagef(cmd, "Missing endpoint id or 'global' argument")
	}

	if args[0] != "global" {
		requireEndpointID(cmd, args)
	}
}

func requirePath(cmd *cobra.Command, args []string) {
	if len(args) < 1 {
		Usagef(cmd, "Missing path argument")
	}

	if args[0] == "" {
		Usagef(cmd, "Empty path argument")
	}
}

func requireServiceID(cmd *cobra.Command, args []string) {
	if len(args) < 1 {
		Usagef(cmd, "Missing service id argument")
	}

	if args[0] == "" {
		Usagef(cmd, "Empty service id argument")
	}
}

// TablePrinter prints the map[string][]string, which is an usual representation
// of dumped BPF map, using tabwriter.
func TablePrinter(firstTitle, secondTitle string, data map[string][]string) {
	w := tabwriter.NewWriter(os.Stdout, 5, 0, 3, ' ', 0)

	fmt.Fprintf(w, "%s\t%s\t\n", firstTitle, secondTitle)

	for key, value := range data {
		for k, v := range value {
			if k == 0 {
				fmt.Fprintf(w, "%s\t%s\t\n", key, v)
			} else {
				fmt.Fprintf(w, "%s\t%s\t\n", "", v)
			}
		}
	}

	w.Flush()
}

// Search 'result' for strings with escaped JSON inside, and expand the JSON.
func expandNestedJSON(result bytes.Buffer) (bytes.Buffer, error) {
	reStringWithJSON := regexp.MustCompile(`"[^"\\{]*{.*[^\\]"`)
	reJSON := regexp.MustCompile(`{.*}`)
	for {
		var (
			loc    []int
			indent string
		)

		// Search for nested JSON; if we don't find any, then break.
		resBytes := result.Bytes()
		if loc = reStringWithJSON.FindIndex(resBytes); loc == nil {
			break
		}

		// Determine the current indentation
		for i := 0; i < loc[0]-1; i++ {
			idx := loc[0] - i - 1
			if resBytes[idx] != ' ' {
				break
			}
			indent = fmt.Sprintf("%s ", indent)
		}

		stringStart := loc[0]
		stringEnd := loc[1]

		// Unquote the string with the nested json.
		quotedBytes := resBytes[stringStart:stringEnd]
		unquoted, err := strconv.Unquote(string(quotedBytes))
		if err != nil {
			return bytes.Buffer{}, fmt.Errorf("Failed to Unquote string: %s\n%s", err.Error(), string(quotedBytes))
		}

		// Find the JSON within the quoted string.
		nestedStart := 0
		nestedEnd := 0
		if locs := reJSON.FindAllStringIndex(unquoted, -1); locs != nil {
			// The last match is the longest one.
			last := len(locs) - 1
			nestedStart = locs[last][0]
			nestedEnd = locs[last][1]
		} else if reJSON.Match(quotedBytes) {
			// The entire string is JSON
			nestedEnd = len(unquoted)
		}

		// Decode the nested JSON
		decoded := ""
		if nestedEnd != 0 {
			m := make(map[string]interface{})
			nested := bytes.NewBufferString(unquoted[nestedStart:nestedEnd])
			if err := json.NewDecoder(nested).Decode(&m); err != nil {
				return bytes.Buffer{}, fmt.Errorf("Failed to decode nested JSON: %s", err.Error())
			}
			decodedBytes, err := json.MarshalIndent(m, indent, "  ")
			if err != nil {
				return bytes.Buffer{}, fmt.Errorf("Cannot marshal nested JSON: %s", err.Error())
			}
			decoded = string(decodedBytes)
		}

		// Serialize
		nextResult := bytes.Buffer{}
		nextResult.Write(resBytes[0:stringStart])
		nextResult.WriteString(string(unquoted[:nestedStart]))
		nextResult.WriteString(string(decoded))
		nextResult.WriteString(string(unquoted[nestedEnd:]))
		nextResult.Write(resBytes[stringEnd:])
		result = nextResult
	}

	return result, nil
}

// parseTrafficString converts the provided string to its corresponding
// TrafficDirection. If the string does not correspond to a valid TrafficDirection
// type, returns Invalid and a corresponding error.
func parseTrafficString(td string) (policymap.TrafficDirection, error) {
	lowered := strings.ToLower(td)

	switch lowered {
	case "ingress":
		return policymap.Ingress, nil
	case "egress":
		return policymap.Egress, nil
	default:
		return policymap.Invalid, fmt.Errorf("invalid direction %q provided", td)
	}

}

// updatePolicyKey updates an entry to the PolicyMap for the endpoint ID, identity,
// traffic direction, and optional list of ports in the list of arguments for the
// given command. Adds the entry to the PolicyMap if add is true, deletes if fails.
// TODO: GH-3396.
func updatePolicyKey(cmd *cobra.Command, args []string, add bool) {
	if len(args) < 3 {
		Usagef(cmd, "<endpoint id>, <traffic-direction>, and <identity> required")
	}

	trafficDirection := args[1]
	parsedTd, err := parseTrafficString(trafficDirection)
	if err != nil {
		Fatalf("Failed to convert %s to a valid traffic direction: %s", args[1], err)
	}

	endpointID := args[0]
	if numericIdentity := identity.GetReservedID(endpointID); numericIdentity != identity.IdentityUnknown {
		endpointID = "reserved_" + strconv.FormatUint(uint64(numericIdentity), 10)
	}

	policyMapPath := bpf.MapPath(policymap.MapName + endpointID)
	policyMap, _, err := policymap.OpenMap(policyMapPath)
	if err != nil {
		Fatalf("Cannot open policymap '%s' : %s", policyMapPath, err)
	}

	peerLbl, err := strconv.ParseUint(args[2], 10, 32)
	if err != nil {
		Fatalf("Failed to convert %s", args[2])
	}

	port := uint16(0)
	protos := []uint8{}
	if len(args) > 3 {
		pp, err := parseL4PortsSlice([]string{args[3]})
		if err != nil {
			Fatalf("Failed to parse L4: %s", err)
		}
		port = pp[0].Port
		if port != 0 {
			proto, _ := u8proto.ParseProtocol(pp[0].Protocol)
			if proto == 0 {
				for _, proto := range u8proto.ProtoIDs {
					protos = append(protos, uint8(proto))
				}
			} else {
				protos = append(protos, uint8(proto))
			}
		}
	}
	if len(protos) == 0 {
		protos = append(protos, 0)
	}

	label := uint32(peerLbl)
	for _, proto := range protos {
		u8p := u8proto.U8proto(proto)
		entry := fmt.Sprintf("%d %d/%s", label, port, u8p.String())
		if add == true {
			if err := policyMap.Allow(label, port, u8p, parsedTd); err != nil {
				Fatalf("Cannot add policy key '%s': %s\n", entry, err)
			}
		} else {
			if err := policyMap.Delete(label, port, u8p, parsedTd); err != nil {
				Fatalf("Cannot delete policy key '%s': %s\n", entry, err)
			}
		}
	}
}

// dumpConfig pretty prints boolean options
func dumpConfig(Opts map[string]string) {
	opts := []string{}
	for k := range Opts {
		opts = append(opts, k)
	}
	sort.Strings(opts)

	for _, k := range opts {
		if enabled, err := option.NormalizeBool(Opts[k]); err != nil {
			Fatalf("Invalid option answer %s: %s", Opts[k], err)
		} else if enabled {
			fmt.Printf("%-24s %s\n", k, common.Green("Enabled"))
		} else {
			fmt.Printf("%-24s %s\n", k, common.Red("Disabled"))
		}
	}
}
<commit_msg>Fix weird indentation for rules<commit_after>// Copyright 2016-2017 Authors of Cilium
//
// 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 cmd

import (
	"bytes"
	"encoding/json"
	"fmt"
	"os"
	"regexp"
	"sort"
	"strconv"
	"strings"
	"text/tabwriter"

	"github.com/cilium/cilium/common"
	"github.com/cilium/cilium/pkg/bpf"
	"github.com/cilium/cilium/pkg/endpoint"
	"github.com/cilium/cilium/pkg/identity"
	"github.com/cilium/cilium/pkg/maps/policymap"
	"github.com/cilium/cilium/pkg/option"
	"github.com/cilium/cilium/pkg/u8proto"

	"github.com/spf13/cobra"
)

// Fatalf prints the Printf formatted message to stderr and exits the program
// Note: os.Exit(1) is not recoverable
func Fatalf(msg string, args ...interface{}) {
	fmt.Fprintf(os.Stderr, "Error: %s\n", fmt.Sprintf(msg, args...))
	os.Exit(1)
}

// Usagef prints the Printf formatted message to stderr, prints usage help and
// exits the program
// Note: os.Exit(1) is not recoverable
func Usagef(cmd *cobra.Command, msg string, args ...interface{}) {
	txt := fmt.Sprintf(msg, args...)
	fmt.Fprintf(os.Stderr, "Error: %s\n\n", txt)
	cmd.Help()
	os.Exit(1)
}

func requireEndpointID(cmd *cobra.Command, args []string) {
	if len(args) < 1 {
		Usagef(cmd, "Missing endpoint id argument")
	}

	if id := identity.ReservedIdentities[args[0]]; id == identity.IdentityUnknown {
		_, _, err := endpoint.ValidateID(args[0])

		if err != nil {
			Fatalf("Cannot parse endpoint id \"%s\": %s", args[0], err)
		}
	}
}

func requireEndpointIDorGlobal(cmd *cobra.Command, args []string) {
	if len(args) < 1 {
		Usagef(cmd, "Missing endpoint id or 'global' argument")
	}

	if args[0] != "global" {
		requireEndpointID(cmd, args)
	}
}

func requirePath(cmd *cobra.Command, args []string) {
	if len(args) < 1 {
		Usagef(cmd, "Missing path argument")
	}

	if args[0] == "" {
		Usagef(cmd, "Empty path argument")
	}
}

func requireServiceID(cmd *cobra.Command, args []string) {
	if len(args) < 1 {
		Usagef(cmd, "Missing service id argument")
	}

	if args[0] == "" {
		Usagef(cmd, "Empty service id argument")
	}
}

// TablePrinter prints the map[string][]string, which is an usual representation
// of dumped BPF map, using tabwriter.
func TablePrinter(firstTitle, secondTitle string, data map[string][]string) {
	w := tabwriter.NewWriter(os.Stdout, 5, 0, 3, ' ', 0)

	fmt.Fprintf(w, "%s\t%s\t\n", firstTitle, secondTitle)

	for key, value := range data {
		for k, v := range value {
			if k == 0 {
				fmt.Fprintf(w, "%s\t%s\t\n", key, v)
			} else {
				fmt.Fprintf(w, "%s\t%s\t\n", "", v)
			}
		}
	}

	w.Flush()
}

// Search 'result' for strings with escaped JSON inside, and expand the JSON.
func expandNestedJSON(result bytes.Buffer) (bytes.Buffer, error) {
	reStringWithJSON := regexp.MustCompile(`"[^"\\{]*{.*[^\\]"`)
	reJSON := regexp.MustCompile(`{.*}`)
	for {
		var (
			loc    []int
			indent string
		)

		// Search for nested JSON; if we don't find any, then break.
		resBytes := result.Bytes()
		if loc = reStringWithJSON.FindIndex(resBytes); loc == nil {
			break
		}

		// Determine the current indentation
		for i := 0; i < loc[0]-1; i++ {
			idx := loc[0] - i - 1
			if resBytes[idx] != ' ' {
				break
			}
			indent = fmt.Sprintf("\t%s\t", indent)
		}

		stringStart := loc[0]
		stringEnd := loc[1]

		// Unquote the string with the nested json.
		quotedBytes := resBytes[stringStart:stringEnd]
		unquoted, err := strconv.Unquote(string(quotedBytes))
		if err != nil {
			return bytes.Buffer{}, fmt.Errorf("Failed to Unquote string: %s\n%s", err.Error(), string(quotedBytes))
		}

		// Find the JSON within the quoted string.
		nestedStart := 0
		nestedEnd := 0
		if locs := reJSON.FindAllStringIndex(unquoted, -1); locs != nil {
			// The last match is the longest one.
			last := len(locs) - 1
			nestedStart = locs[last][0]
			nestedEnd = locs[last][1]
		} else if reJSON.Match(quotedBytes) {
			// The entire string is JSON
			nestedEnd = len(unquoted)
		}

		// Decode the nested JSON
		decoded := ""
		if nestedEnd != 0 {
			m := make(map[string]interface{})
			nested := bytes.NewBufferString(unquoted[nestedStart:nestedEnd])
			if err := json.NewDecoder(nested).Decode(&m); err != nil {
				return bytes.Buffer{}, fmt.Errorf("Failed to decode nested JSON: %s", err.Error())
			}
			decodedBytes, err := json.MarshalIndent(m, indent, "  ")
			if err != nil {
				return bytes.Buffer{}, fmt.Errorf("Cannot marshal nested JSON: %s", err.Error())
			}
			decoded = string(decodedBytes)
		}

		// Serialize
		nextResult := bytes.Buffer{}
		nextResult.Write(resBytes[0:stringStart])
		nextResult.WriteString(string(unquoted[:nestedStart]))
		nextResult.WriteString(string(decoded))
		nextResult.WriteString(string(unquoted[nestedEnd:]))
		nextResult.Write(resBytes[stringEnd:])
		result = nextResult
	}

	return result, nil
}

// parseTrafficString converts the provided string to its corresponding
// TrafficDirection. If the string does not correspond to a valid TrafficDirection
// type, returns Invalid and a corresponding error.
func parseTrafficString(td string) (policymap.TrafficDirection, error) {
	lowered := strings.ToLower(td)

	switch lowered {
	case "ingress":
		return policymap.Ingress, nil
	case "egress":
		return policymap.Egress, nil
	default:
		return policymap.Invalid, fmt.Errorf("invalid direction %q provided", td)
	}

}

// updatePolicyKey updates an entry to the PolicyMap for the endpoint ID, identity,
// traffic direction, and optional list of ports in the list of arguments for the
// given command. Adds the entry to the PolicyMap if add is true, deletes if fails.
// TODO: GH-3396.
func updatePolicyKey(cmd *cobra.Command, args []string, add bool) {
	if len(args) < 3 {
		Usagef(cmd, "<endpoint id>, <traffic-direction>, and <identity> required")
	}

	trafficDirection := args[1]
	parsedTd, err := parseTrafficString(trafficDirection)
	if err != nil {
		Fatalf("Failed to convert %s to a valid traffic direction: %s", args[1], err)
	}

	endpointID := args[0]
	if numericIdentity := identity.GetReservedID(endpointID); numericIdentity != identity.IdentityUnknown {
		endpointID = "reserved_" + strconv.FormatUint(uint64(numericIdentity), 10)
	}

	policyMapPath := bpf.MapPath(policymap.MapName + endpointID)
	policyMap, _, err := policymap.OpenMap(policyMapPath)
	if err != nil {
		Fatalf("Cannot open policymap '%s' : %s", policyMapPath, err)
	}

	peerLbl, err := strconv.ParseUint(args[2], 10, 32)
	if err != nil {
		Fatalf("Failed to convert %s", args[2])
	}

	port := uint16(0)
	protos := []uint8{}
	if len(args) > 3 {
		pp, err := parseL4PortsSlice([]string{args[3]})
		if err != nil {
			Fatalf("Failed to parse L4: %s", err)
		}
		port = pp[0].Port
		if port != 0 {
			proto, _ := u8proto.ParseProtocol(pp[0].Protocol)
			if proto == 0 {
				for _, proto := range u8proto.ProtoIDs {
					protos = append(protos, uint8(proto))
				}
			} else {
				protos = append(protos, uint8(proto))
			}
		}
	}
	if len(protos) == 0 {
		protos = append(protos, 0)
	}

	label := uint32(peerLbl)
	for _, proto := range protos {
		u8p := u8proto.U8proto(proto)
		entry := fmt.Sprintf("%d %d/%s", label, port, u8p.String())
		if add == true {
			if err := policyMap.Allow(label, port, u8p, parsedTd); err != nil {
				Fatalf("Cannot add policy key '%s': %s\n", entry, err)
			}
		} else {
			if err := policyMap.Delete(label, port, u8p, parsedTd); err != nil {
				Fatalf("Cannot delete policy key '%s': %s\n", entry, err)
			}
		}
	}
}

// dumpConfig pretty prints boolean options
func dumpConfig(Opts map[string]string) {
	opts := []string{}
	for k := range Opts {
		opts = append(opts, k)
	}
	sort.Strings(opts)

	for _, k := range opts {
		if enabled, err := option.NormalizeBool(Opts[k]); err != nil {
			Fatalf("Invalid option answer %s: %s", Opts[k], err)
		} else if enabled {
			fmt.Printf("%-24s %s\n", k, common.Green("Enabled"))
		} else {
			fmt.Printf("%-24s %s\n", k, common.Red("Disabled"))
		}
	}
}
<|endoftext|>


<commit_before>package drivers

import (
	"fmt"
	log "github.com/Sirupsen/logrus"
	"github.com/docker/go-plugins-helpers/volume"
	"os"
	"path/filepath"
)

const (
	NfsOptions   = "nfsopts"
	DefaultNfsV3 = "port=2049,nolock,proto=tcp"
)

type nfsDriver struct {
	volumeDriver
	version int
	nfsopts map[string]string
}

var (
	EmptyMap = map[string]string{}
)

func NewNFSDriver(root string, version int, nfsopts string) nfsDriver {
	d := nfsDriver{
		volumeDriver: newVolumeDriver(root),
		version:      version,
		nfsopts:      map[string]string{},
	}

	if len(nfsopts) > 0 {
		d.nfsopts[NfsOptions] = nfsopts
	}
	return d
}

func (n nfsDriver) Mount(r volume.MountRequest) volume.Response {
	log.Debugf("Entering Mount: %v", r)
	n.m.Lock()
	defer n.m.Unlock()

	resolvedName, resOpts := resolveName(r.Name)

	hostdir := mountpoint(n.root, resolvedName)
	source := n.fixSource(resolvedName)

	// Support adhoc mounts (outside of docker volume create)
	// need to adjust source for ShareOpt
	if resOpts != nil {
		if share, found := resOpts[ShareOpt]; found {
			source = n.fixSource(share)
		}
	}

	if n.mountm.HasMount(resolvedName) && n.mountm.Count(resolvedName) > 0 {
		log.Infof("Using existing NFS volume mount: %s", hostdir)
		n.mountm.Increment(resolvedName)
		if err := run(fmt.Sprintf("mountpoint -q %s", hostdir)); err != nil {
			log.Infof("Existing NFS volume not mounted, force remount.")
		} else {
			return volume.Response{Mountpoint: hostdir}
		}
	}

	log.Infof("Mounting NFS volume %s on %s", source, hostdir)

	if err := createDest(hostdir); err != nil {
		return volume.Response{Err: err.Error()}
	}

	if n.mountm.HasMount(resolvedName) == false {
		n.mountm.Create(resolvedName, hostdir, resOpts)
	}

	if err := n.mountVolume(resolvedName, source, hostdir, n.version); err != nil {
		return volume.Response{Err: err.Error()}
	}
	n.mountm.Add(resolvedName, hostdir)

	if n.mountm.GetOption(resolvedName, ShareOpt) != "" && n.mountm.GetOptionAsBool(resolvedName, CreateOpt) {
		log.Infof("Mount: Share and Create options enabled - using %s as sub-dir mount", resolvedName)
		datavol := filepath.Join(hostdir, resolvedName)
		if err := createDest(filepath.Join(hostdir, resolvedName)); err != nil {
			return volume.Response{Err: err.Error()}
		}
		hostdir = datavol
	}

	return volume.Response{Mountpoint: hostdir}
}

func (n nfsDriver) Unmount(r volume.UnmountRequest) volume.Response {
	log.Debugf("Entering Unmount: %v", r)

	n.m.Lock()
	defer n.m.Unlock()

	resolvedName, _ := resolveName(r.Name)

	hostdir := mountpoint(n.root, resolvedName)

	if n.mountm.HasMount(resolvedName) {
		if n.mountm.Count(resolvedName) > 1 {
			log.Printf("Skipping unmount for %s - in use by other containers", resolvedName)
			n.mountm.Decrement(resolvedName)
			return volume.Response{}
		}
		n.mountm.Decrement(resolvedName)
	}

	log.Infof("Unmounting volume name %s from %s", resolvedName, hostdir)

	if err := run(fmt.Sprintf("umount %s", hostdir)); err != nil {
		log.Errorf("Error unmounting volume from host: %s", err.Error())
		return volume.Response{Err: err.Error()}
	}

	n.mountm.DeleteIfNotManaged(resolvedName)

        // Check if directory is empty. This command will return "err" if empty
        if err := run(fmt.Sprintf("ls -1 %s | grep .", hostdir)); err == nil {
                log.Warnf("Directory %s not empty after unmount. Skipping RemoveAll call.", hostdir)
        } else {
                if err := os.RemoveAll(hostdir); err != nil {
                      return volume.Response{Err: err.Error()}
        }

	return volume.Response{}
}

func (n nfsDriver) fixSource(name string) string {
	if n.mountm.HasOption(name, ShareOpt) {
		return addShareColon(n.mountm.GetOption(name, ShareOpt))
	}
	return addShareColon(name)
}

func (n nfsDriver) mountVolume(name, source, dest string, version int) error {
	var cmd string

	options := merge(n.mountm.GetOptions(name), n.nfsopts)
	opts := ""
	if val, ok := options[NfsOptions]; ok {
		opts = val
	}

	mountCmd := "mount"

	if log.GetLevel() == log.DebugLevel {
		mountCmd = mountCmd + " -v"
	}

	switch version {
	case 3:
		log.Debugf("Mounting with NFSv3 - src: %s, dest: %s", source, dest)
		if len(opts) < 1 {
			opts = DefaultNfsV3
		}
		cmd = fmt.Sprintf("%s -t nfs -o %s %s %s", mountCmd, opts, source, dest)
	default:
		log.Debugf("Mounting with NFSv4 - src: %s, dest: %s", source, dest)
		if len(opts) > 0 {
			cmd = fmt.Sprintf("%s -t nfs4 -o %s %s %s", mountCmd, opts, source, dest)
		} else {
			cmd = fmt.Sprintf("%s -t nfs4 %s %s", mountCmd, source, dest)
		}
	}
	log.Debugf("exec: %s\n", cmd)
	return run(cmd)
}
<commit_msg>Added missing bracket due to merge issue.<commit_after>package drivers

import (
	"fmt"
	log "github.com/Sirupsen/logrus"
	"github.com/docker/go-plugins-helpers/volume"
	"os"
	"path/filepath"
)

const (
	NfsOptions   = "nfsopts"
	DefaultNfsV3 = "port=2049,nolock,proto=tcp"
)

type nfsDriver struct {
	volumeDriver
	version int
	nfsopts map[string]string
}

var (
	EmptyMap = map[string]string{}
)

func NewNFSDriver(root string, version int, nfsopts string) nfsDriver {
	d := nfsDriver{
		volumeDriver: newVolumeDriver(root),
		version:      version,
		nfsopts:      map[string]string{},
	}

	if len(nfsopts) > 0 {
		d.nfsopts[NfsOptions] = nfsopts
	}
	return d
}

func (n nfsDriver) Mount(r volume.MountRequest) volume.Response {
	log.Debugf("Entering Mount: %v", r)
	n.m.Lock()
	defer n.m.Unlock()

	resolvedName, resOpts := resolveName(r.Name)

	hostdir := mountpoint(n.root, resolvedName)
	source := n.fixSource(resolvedName)

	// Support adhoc mounts (outside of docker volume create)
	// need to adjust source for ShareOpt
	if resOpts != nil {
		if share, found := resOpts[ShareOpt]; found {
			source = n.fixSource(share)
		}
	}

	if n.mountm.HasMount(resolvedName) && n.mountm.Count(resolvedName) > 0 {
		log.Infof("Using existing NFS volume mount: %s", hostdir)
		n.mountm.Increment(resolvedName)
		if err := run(fmt.Sprintf("mountpoint -q %s", hostdir)); err != nil {
			log.Infof("Existing NFS volume not mounted, force remount.")
		} else {
			return volume.Response{Mountpoint: hostdir}
		}
	}

	log.Infof("Mounting NFS volume %s on %s", source, hostdir)

	if err := createDest(hostdir); err != nil {
		return volume.Response{Err: err.Error()}
	}

	if n.mountm.HasMount(resolvedName) == false {
		n.mountm.Create(resolvedName, hostdir, resOpts)
	}

	if err := n.mountVolume(resolvedName, source, hostdir, n.version); err != nil {
		return volume.Response{Err: err.Error()}
	}
	n.mountm.Add(resolvedName, hostdir)

	if n.mountm.GetOption(resolvedName, ShareOpt) != "" && n.mountm.GetOptionAsBool(resolvedName, CreateOpt) {
		log.Infof("Mount: Share and Create options enabled - using %s as sub-dir mount", resolvedName)
		datavol := filepath.Join(hostdir, resolvedName)
		if err := createDest(filepath.Join(hostdir, resolvedName)); err != nil {
			return volume.Response{Err: err.Error()}
		}
		hostdir = datavol
	}

	return volume.Response{Mountpoint: hostdir}
}

func (n nfsDriver) Unmount(r volume.UnmountRequest) volume.Response {
	log.Debugf("Entering Unmount: %v", r)

	n.m.Lock()
	defer n.m.Unlock()

	resolvedName, _ := resolveName(r.Name)

	hostdir := mountpoint(n.root, resolvedName)

	if n.mountm.HasMount(resolvedName) {
		if n.mountm.Count(resolvedName) > 1 {
			log.Printf("Skipping unmount for %s - in use by other containers", resolvedName)
			n.mountm.Decrement(resolvedName)
			return volume.Response{}
		}
		n.mountm.Decrement(resolvedName)
	}

	log.Infof("Unmounting volume name %s from %s", resolvedName, hostdir)

	if err := run(fmt.Sprintf("umount %s", hostdir)); err != nil {
		log.Errorf("Error unmounting volume from host: %s", err.Error())
		return volume.Response{Err: err.Error()}
	}

	n.mountm.DeleteIfNotManaged(resolvedName)

        // Check if directory is empty. This command will return "err" if empty
        if err := run(fmt.Sprintf("ls -1 %s | grep .", hostdir)); err == nil {
                log.Warnf("Directory %s not empty after unmount. Skipping RemoveAll call.", hostdir)
        } else {
                if err := os.RemoveAll(hostdir); err != nil {
                      return volume.Response{Err: err.Error()}
		}
        }

	return volume.Response{}
}

func (n nfsDriver) fixSource(name string) string {
	if n.mountm.HasOption(name, ShareOpt) {
		return addShareColon(n.mountm.GetOption(name, ShareOpt))
	}
	return addShareColon(name)
}

func (n nfsDriver) mountVolume(name, source, dest string, version int) error {
	var cmd string

	options := merge(n.mountm.GetOptions(name), n.nfsopts)
	opts := ""
	if val, ok := options[NfsOptions]; ok {
		opts = val
	}

	mountCmd := "mount"

	if log.GetLevel() == log.DebugLevel {
		mountCmd = mountCmd + " -v"
	}

	switch version {
	case 3:
		log.Debugf("Mounting with NFSv3 - src: %s, dest: %s", source, dest)
		if len(opts) < 1 {
			opts = DefaultNfsV3
		}
		cmd = fmt.Sprintf("%s -t nfs -o %s %s %s", mountCmd, opts, source, dest)
	default:
		log.Debugf("Mounting with NFSv4 - src: %s, dest: %s", source, dest)
		if len(opts) > 0 {
			cmd = fmt.Sprintf("%s -t nfs4 -o %s %s %s", mountCmd, opts, source, dest)
		} else {
			cmd = fmt.Sprintf("%s -t nfs4 %s %s", mountCmd, source, dest)
		}
	}
	log.Debugf("exec: %s\n", cmd)
	return run(cmd)
}
<|endoftext|>


<commit_before>/*
This package is just a collection of use-case for the various aspects of the RIPE API.
Consider this both as an example on how to use the API and a testing tool for the API wrapper.
*/
package main

import (
	"fmt"
	"github.com/keltia/ripe-atlas"
	"github.com/urfave/cli"
	"log"
	"os"
	"sort"
)

var (
	// CLI specific options
	fDebug   bool
	fLogfile string
	fVerbose bool

	// See flag.go for details

	// Global API options
	fFieldList string
	fFormat    string
	fInclude   string
	fOptFields string
	fPageNum   string
	fPageSize  string
	fSortOrder string
	fWantMine  bool

	// Probe-specific ones
	fAllProbes bool
	fIsAnchor  bool

	// Common measurement ones
	fAllMeasurements bool
	fAsn             string
	fAsnV4           string
	fAsnV6           string
	fCountry         string
	fProtocol        string
	fMeasureType     string
	fWant4           bool
	fWant6           bool

	// Create measurements
	fBillTo    string
	fIsOneOff  bool
	fStartTime string
	fStopTime  string

	// HTTP
	fHTTPMethod  string
	fUserAgent   string
	fHTTPVersion string

	// DNS
	fBitCD         bool
	fDisableDNSSEC bool

	// Traceroute
	fMaxHops    int
	fPacketSize int

	// ProbeSet parameters
	fPoolSize  int
	fAreaType  string
	fAreaValue string

	// Our configuration file
	cnf *Config

	// All possible commands
	cliCommands []cli.Command

	client *atlas.Client

	// Our tiple-valued synthesis of fWant4/fWant6
	wantAF string
)

const (
	atlasVersion = "0.25"
	// MyName is the application name
	MyName = "ripe-atlas"

	// WantBoth is the way to ask for both IPv4 & IPv6.
	WantBoth = "64"

	// Want4 only 4
	Want4 = "4"
	// Want6 only 6
	Want6 = "6"
)

func openlog(fn string) *log.Logger {
	fh, err := os.OpenFile(fn, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
	if err != nil {
		log.Fatalf("error: can not open logfile %s: %v", fn, err)
	}

	mylog := log.New(fh, "", log.LstdFlags)
	verbose("Logfile: %s %#v", fn, mylog)

	return mylog
}

// -4 & -6 are special, if neither is specified, then we turn both as true
// Check a few other things while we are here
func finalcheck(c *cli.Context) error {

	var (
		err   error
		mylog *log.Logger
	)

	// Load main configuration
	cnf, err = LoadConfig("")
	if err != nil {
		verbose("No configuration file found.")
	}

	// Logical
	if fDebug {
		fVerbose = true
		verbose("config: %#v", cnf)
	}

	// Various messages
	if cnf.APIKey != "" {
		verbose("Found API key!")
	} else {
		verbose("No API key!")
	}

	if cnf.DefaultProbe != 0 {
		verbose("Found default probe: %d\n", cnf.DefaultProbe)
	}

	// Allow overwrite of a few parameters
	if fPoolSize != 0 {
		cnf.ProbeSet.PoolSize = fPoolSize
	}
	if fAreaType != "" {
		cnf.ProbeSet.Type = fAreaType
	}
	if fAreaValue != "" {
		cnf.ProbeSet.Value = fAreaValue
	}

	// Check whether we have proxy authentication (from a separate config file)
	auth, err := setupProxyAuth()
	if err != nil {
		verbose("Invalid or no proxy auth credentials")
	}

	// If we want a logfile, open one for the API to log into
	if fLogfile != "" {
		mylog = openlog(fLogfile)
	}

	// Wondering whether to move to the Functional options pattern
	// cf. https://dave.cheney.net/2016/11/13/do-not-fear-first-class-functions
	client, err = atlas.NewClient(atlas.Config{
		APIKey:       cnf.APIKey,
		DefaultProbe: cnf.DefaultProbe,
		IsOneOff:     fIsOneOff,
		PoolSize:     cnf.ProbeSet.PoolSize,
		AreaType:     cnf.ProbeSet.Type,
		AreaValue:    cnf.ProbeSet.Value,
		ProxyAuth:    auth,
		Verbose:      fVerbose,
		Log:          mylog,
	})

	// No need to continue if this fails
	if err != nil {
		log.Fatalf("Error creating the Atlas client: %v", err)
	}

	if fWant4 {
		wantAF = Want4
	}

	if fWant6 {
		wantAF = Want6
	}

	// Both are fine
	if fWant4 && fWant6 {
		wantAF = WantBoth
	}

	// So is neither — common case
	if !fWant4 && !fWant6 {
		wantAF = WantBoth
	}

	return nil
}

// main is the starting point (and everything)
func main() {
	cli.VersionFlag = cli.BoolFlag{Name: "version, V"}

	cli.VersionPrinter = func(c *cli.Context) {
		fmt.Printf("API wrapper: %s Atlas API: %s\n", c.App.Version, atlas.GetVersion())
	}

	app := cli.NewApp()
	app.Name = "atlas"
	app.Usage = "RIPE Atlas CLI interface"
	app.Author = "Ollivier Robert <roberto@keltia.net>"
	app.Version = atlasVersion
	//app.HideVersion = true

	// General flags
	app.Flags = []cli.Flag{
		cli.StringFlag{
			Name:        "format,f",
			Usage:       "specify output format (NOT IMPLEMENTED)",
			Destination: &fFormat,
		},
		cli.BoolFlag{
			Name:        "debug,D",
			Usage:       "debug mode",
			Destination: &fDebug,
		},
		cli.BoolFlag{
			Name:        "verbose,v",
			Usage:       "verbose mode",
			Destination: &fVerbose,
		},
		cli.StringFlag{
			Name:        "fields,F",
			Usage:       "specify which fields are wanted",
			Destination: &fFieldList,
		},
		cli.StringFlag{
			Name:        "include,I",
			Usage:       "specify whether objects should be expanded",
			Destination: &fInclude,
		},
		cli.StringFlag{
			Name:        "logfile,L",
			Usage:       "specify a log file",
			Destination: &fLogfile,
		},
		cli.BoolFlag{
			Name:        "mine,M",
			Usage:       "limit output to my objects",
			Destination: &fWantMine,
		},
		cli.StringFlag{
			Name:        "opt-fields,O",
			Usage:       "specify which optional fields are wanted",
			Destination: &fOptFields,
		},
		cli.StringFlag{
			Name:        "page-size,P",
			Usage:       "page size for results",
			Destination: &fPageSize,
		},

		cli.StringFlag{
			Name:        "sort,S",
			Usage:       "sort results",
			Destination: &fSortOrder,
		},
		cli.BoolTFlag{
			Name:        "1,is-oneoff",
			Usage:       "one-time measurement",
			Destination: &fIsOneOff,
		},
		cli.BoolFlag{
			Name:        "6, ipv6",
			Usage:       "Only IPv6",
			Destination: &fWant6,
		},
		cli.BoolFlag{
			Name:        "4, ipv4",
			Usage:       "Only IPv4",
			Destination: &fWant4,
		},
		// These are not global parameters but it makes sense to define them only once
		// and not in every cmd_* files.
		cli.IntFlag{
			Name:        "pool-size,N",
			Usage:       "Number of probes to request",
			Destination: &fPoolSize,
		},
		cli.StringFlag{
			Name:        "area-type",
			Usage:       "Set type for probes (area, country, etc.)",
			Destination: &fAreaType,
		},
		cli.StringFlag{
			Name:        "area-value",
			Usage:       "Value for the probe set (WW, West, etc.)",
			Destination: &fAreaValue,
		},
	}

	// Ensure -4 & -6 are treated properly & initialization is done
	app.Before = finalcheck

	sort.Sort(ByAlphabet(cliCommands))
	app.Commands = cliCommands
	app.Run(os.Args)
}
<commit_msg>Add debug message.<commit_after>/*
This package is just a collection of use-case for the various aspects of the RIPE API.
Consider this both as an example on how to use the API and a testing tool for the API wrapper.
*/
package main

import (
	"fmt"
	"github.com/keltia/ripe-atlas"
	"github.com/urfave/cli"
	"log"
	"os"
	"sort"
)

var (
	// CLI specific options
	fDebug   bool
	fLogfile string
	fVerbose bool

	// See flag.go for details

	// Global API options
	fFieldList string
	fFormat    string
	fInclude   string
	fOptFields string
	fPageNum   string
	fPageSize  string
	fSortOrder string
	fWantMine  bool

	// Probe-specific ones
	fAllProbes bool
	fIsAnchor  bool

	// Common measurement ones
	fAllMeasurements bool
	fAsn             string
	fAsnV4           string
	fAsnV6           string
	fCountry         string
	fProtocol        string
	fMeasureType     string
	fWant4           bool
	fWant6           bool

	// Create measurements
	fBillTo    string
	fIsOneOff  bool
	fStartTime string
	fStopTime  string

	// HTTP
	fHTTPMethod  string
	fUserAgent   string
	fHTTPVersion string

	// DNS
	fBitCD         bool
	fDisableDNSSEC bool

	// Traceroute
	fMaxHops    int
	fPacketSize int

	// ProbeSet parameters
	fPoolSize  int
	fAreaType  string
	fAreaValue string

	// Our configuration file
	cnf *Config

	// All possible commands
	cliCommands []cli.Command

	client *atlas.Client

	// Our tiple-valued synthesis of fWant4/fWant6
	wantAF string
)

const (
	atlasVersion = "0.25"
	// MyName is the application name
	MyName = "ripe-atlas"

	// WantBoth is the way to ask for both IPv4 & IPv6.
	WantBoth = "64"

	// Want4 only 4
	Want4 = "4"
	// Want6 only 6
	Want6 = "6"
)

func openlog(fn string) *log.Logger {
	fh, err := os.OpenFile(fn, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
	if err != nil {
		log.Fatalf("error: can not open logfile %s: %v", fn, err)
	}

	mylog := log.New(fh, "", log.LstdFlags)
	verbose("Logfile: %s %#v", fn, mylog)

	return mylog
}

// -4 & -6 are special, if neither is specified, then we turn both as true
// Check a few other things while we are here
func finalcheck(c *cli.Context) error {

	var (
		err   error
		mylog *log.Logger
	)

	// Load main configuration
	cnf, err = LoadConfig("")
	if err != nil {
		verbose("No configuration file found.")
	}

	// Logical
	if fDebug {
		fVerbose = true
		verbose("config: %#v", cnf)
	}

	// Various messages
	if cnf.APIKey != "" {
		verbose("Found API key!")
	} else {
		verbose("No API key!")
	}

	if cnf.DefaultProbe != 0 {
		verbose("Found default probe: %d\n", cnf.DefaultProbe)
	}

	// Allow overwrite of a few parameters
	if fPoolSize != 0 {
		cnf.ProbeSet.PoolSize = fPoolSize
	}
	if fAreaType != "" {
		cnf.ProbeSet.Type = fAreaType
	}
	if fAreaValue != "" {
		cnf.ProbeSet.Value = fAreaValue
	}

	// Check whether we have proxy authentication (from a separate config file)
	auth, err := setupProxyAuth()
	if err != nil {
		verbose("Invalid or no proxy auth credentials")
	}

	// If we want a logfile, open one for the API to log into
	if fLogfile != "" {
		mylog = openlog(fLogfile)
	}

	// Wondering whether to move to the Functional options pattern
	// cf. https://dave.cheney.net/2016/11/13/do-not-fear-first-class-functions
	client, err = atlas.NewClient(atlas.Config{
		APIKey:       cnf.APIKey,
		DefaultProbe: cnf.DefaultProbe,
		IsOneOff:     fIsOneOff,
		PoolSize:     cnf.ProbeSet.PoolSize,
		AreaType:     cnf.ProbeSet.Type,
		AreaValue:    cnf.ProbeSet.Value,
		ProxyAuth:    auth,
		Verbose:      fVerbose,
		Log:          mylog,
	})

	// No need to continue if this fails
	if err != nil {
		log.Fatalf("Error creating the Atlas client: %v", err)
	}

	if fWant4 {
		wantAF = Want4
	}

	if fWant6 {
		wantAF = Want6
	}

	// Both are fine
	if fWant4 && fWant6 {
		wantAF = WantBoth
	}

	// So is neither — common case
	if !fWant4 && !fWant6 {
		wantAF = WantBoth
	}

	debug("wantAF=%s", wantAF)
	return nil
}

// main is the starting point (and everything)
func main() {
	cli.VersionFlag = cli.BoolFlag{Name: "version, V"}

	cli.VersionPrinter = func(c *cli.Context) {
		fmt.Printf("API wrapper: %s Atlas API: %s\n", c.App.Version, atlas.GetVersion())
	}

	app := cli.NewApp()
	app.Name = "atlas"
	app.Usage = "RIPE Atlas CLI interface"
	app.Author = "Ollivier Robert <roberto@keltia.net>"
	app.Version = atlasVersion
	//app.HideVersion = true

	// General flags
	app.Flags = []cli.Flag{
		cli.StringFlag{
			Name:        "format,f",
			Usage:       "specify output format (NOT IMPLEMENTED)",
			Destination: &fFormat,
		},
		cli.BoolFlag{
			Name:        "debug,D",
			Usage:       "debug mode",
			Destination: &fDebug,
		},
		cli.BoolFlag{
			Name:        "verbose,v",
			Usage:       "verbose mode",
			Destination: &fVerbose,
		},
		cli.StringFlag{
			Name:        "fields,F",
			Usage:       "specify which fields are wanted",
			Destination: &fFieldList,
		},
		cli.StringFlag{
			Name:        "include,I",
			Usage:       "specify whether objects should be expanded",
			Destination: &fInclude,
		},
		cli.StringFlag{
			Name:        "logfile,L",
			Usage:       "specify a log file",
			Destination: &fLogfile,
		},
		cli.BoolFlag{
			Name:        "mine,M",
			Usage:       "limit output to my objects",
			Destination: &fWantMine,
		},
		cli.StringFlag{
			Name:        "opt-fields,O",
			Usage:       "specify which optional fields are wanted",
			Destination: &fOptFields,
		},
		cli.StringFlag{
			Name:        "page-size,P",
			Usage:       "page size for results",
			Destination: &fPageSize,
		},

		cli.StringFlag{
			Name:        "sort,S",
			Usage:       "sort results",
			Destination: &fSortOrder,
		},
		cli.BoolTFlag{
			Name:        "1,is-oneoff",
			Usage:       "one-time measurement",
			Destination: &fIsOneOff,
		},
		cli.BoolFlag{
			Name:        "6, ipv6",
			Usage:       "Only IPv6",
			Destination: &fWant6,
		},
		cli.BoolFlag{
			Name:        "4, ipv4",
			Usage:       "Only IPv4",
			Destination: &fWant4,
		},
		// These are not global parameters but it makes sense to define them only once
		// and not in every cmd_* files.
		cli.IntFlag{
			Name:        "pool-size,N",
			Usage:       "Number of probes to request",
			Destination: &fPoolSize,
		},
		cli.StringFlag{
			Name:        "area-type",
			Usage:       "Set type for probes (area, country, etc.)",
			Destination: &fAreaType,
		},
		cli.StringFlag{
			Name:        "area-value",
			Usage:       "Value for the probe set (WW, West, etc.)",
			Destination: &fAreaValue,
		},
	}

	// Ensure -4 & -6 are treated properly & initialization is done
	app.Before = finalcheck

	sort.Sort(ByAlphabet(cliCommands))
	app.Commands = cliCommands
	app.Run(os.Args)
}
<|endoftext|>


<commit_before>/*
This package is just a collection of use-case for the various aspects of the RIPE API.
Consider this both as an example on how to use the API and a testing tool for the API wrapper.
*/
package main

import (
	"fmt"
	"github.com/keltia/proxy"
	"github.com/keltia/ripe-atlas"
	"github.com/urfave/cli"
	"log"
	"os"
	"sort"
)

var (
	// CLI specific options
	fDebug   bool
	fLogfile string
	fVerbose bool

	// See flag.go for details

	// Global API options
	fFieldList string
	fFormat    string
	fInclude   string
	fOptFields string
	fPageNum   string
	fPageSize  string
	fSortOrder string
	fWantMine  bool

	// Probe-specific ones
	fAllProbes bool
	fIsAnchor  bool
	fTags      string

	// Common measurement ones
	fAllMeasurements bool
	fAsn             string
	fAsnV4           string
	fAsnV6           string
	fCountry         string
	fProtocol        string
	fMeasureType     string
	fWant4           bool
	fWant6           bool
	fMTags           string

	// Create measurements
	fBillTo    string
	fIsOneOff  bool
	fStartTime string
	fStopTime  string

	// HTTP
	fHTTPMethod  string
	fUserAgent   string
	fHTTPVersion string

	// DNS
	fBitCD         bool
	fDisableDNSSEC bool

	// Traceroute
	fMaxHops    int
	fPacketSize int

	// ProbeSet parameters
	fPoolSize  int
	fAreaType  string
	fAreaValue string

	// Our configuration file
	cnf *Config

	// All possible commands
	cliCommands []cli.Command

	client *atlas.Client

	// Our tiple-valued synthesis of fWant4/fWant6
	wantAF string
)

const (
	atlasVersion = "0.40"
	// MyName is the application name
	MyName = "ripe-atlas"

	// WantBoth is the way to ask for both IPv4 & IPv6.
	WantBoth = "64"

	// Want4 only 4
	Want4 = "4"
	// Want6 only 6
	Want6 = "6"
)

func openlog(fn string) *log.Logger {
	fh, err := os.OpenFile(fn, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
	if err != nil {
		log.Fatalf("error: can not open logfile %s: %v", fn, err)
	}

	mylog := log.New(fh, "", log.LstdFlags)
	verbose("Logfile: %s %#v", fn, mylog)

	return mylog
}

// -4 & -6 are special, if neither is specified, then we turn both as true
// Check a few other things while we are here
func finalcheck(c *cli.Context) error {

	var (
		err   error
		mylog *log.Logger
	)

	// Load main configuration
	cnf, err = LoadConfig("")
	if err != nil {
		verbose("No configuration file found.")
	}

	// Logical
	if fDebug {
		fVerbose = true
		verbose("config: %#v", cnf)
	}

	// Various messages
	if cnf.APIKey != "" {
		verbose("Found API key!")
	} else {
		verbose("No API key!")
	}

	if cnf.DefaultProbe != 0 {
		verbose("Found default probe: %d\n", cnf.DefaultProbe)
	}

	// Allow overwrite of a few parameters
	if fPoolSize != 0 {
		cnf.ProbeSet.PoolSize = fPoolSize
	}
	if fAreaType != "" {
		cnf.ProbeSet.Type = fAreaType
	}
	if fAreaValue != "" {
		cnf.ProbeSet.Value = fAreaValue
	}

	// Check whether we have proxy authentication (from a separate config file)
	auth, err := proxy.SetupProxyAuth()
	if err != nil {
		verbose("Invalid or no proxy auth credentials")
	}

	// If we want a logfile, open one for the API to log into
	if fLogfile != "" {
		mylog = openlog(fLogfile)
	}

	// Check whether the -C <country> was specified then override configuration
	if fCountry != "" {
		cnf.ProbeSet.Type = "country"
		cnf.ProbeSet.Value = fCountry
	}

	// Check whether tags are specified
	if fTags != "" {
		cnf.ProbeSet.Tags = fTags
	}

	// Wondering whether to move to the Functional options pattern
	// cf. https://dave.cheney.net/2016/11/13/do-not-fear-first-class-functions
	client, err = atlas.NewClient(atlas.Config{
		APIKey:       cnf.APIKey,
		DefaultProbe: cnf.DefaultProbe,
		IsOneOff:     fIsOneOff,
		PoolSize:     cnf.ProbeSet.PoolSize,
		AreaType:     cnf.ProbeSet.Type,
		AreaValue:    cnf.ProbeSet.Value,
		Tags:         cnf.ProbeSet.Tags,
		ProxyAuth:    auth,
		Verbose:      fVerbose,
		Log:          mylog,
	})

	// No need to continue if this fails
	if err != nil {
		log.Fatalf("Error creating the Atlas client: %v", err)
	}

	if fWant4 {
		wantAF = Want4
	}

	if fWant6 {
		wantAF = Want6
	}

	// Both are fine
	if fWant4 && fWant6 {
		wantAF = WantBoth
	}

	// So is neither — common case
	if !fWant4 && !fWant6 {
		wantAF = WantBoth
	}

	debug("wantAF=%s", wantAF)
	return nil
}

// main is the starting point (and everything)
func main() {
	cli.VersionFlag = cli.BoolFlag{Name: "version, V"}

	cli.VersionPrinter = func(c *cli.Context) {
		fmt.Printf("API wrapper: %s Atlas API: %s\n", c.App.Version, atlas.GetVersion())
	}

	app := cli.NewApp()
	app.Name = "atlas"
	app.Usage = "RIPE Atlas CLI interface"
	app.Author = "Ollivier Robert <roberto@keltia.net>"
	app.Version = atlasVersion
	//app.HideVersion = true

	// General flags
	app.Flags = []cli.Flag{
		cli.StringFlag{
			Name:        "format,f",
			Usage:       "specify output format (NOT IMPLEMENTED)",
			Destination: &fFormat,
		},
		cli.BoolFlag{
			Name:        "debug,D",
			Usage:       "debug mode",
			Destination: &fDebug,
		},
		cli.BoolFlag{
			Name:        "verbose,v",
			Usage:       "verbose mode",
			Destination: &fVerbose,
		},
		cli.StringFlag{
			Name:        "fields,F",
			Usage:       "specify which fields are wanted",
			Destination: &fFieldList,
		},
		cli.StringFlag{
			Name:        "include,I",
			Usage:       "specify whether objects should be expanded",
			Destination: &fInclude,
		},
		cli.StringFlag{
			Name:        "logfile,L",
			Usage:       "specify a log file",
			Destination: &fLogfile,
		},
		cli.BoolFlag{
			Name:        "mine,M",
			Usage:       "limit output to my objects",
			Destination: &fWantMine,
		},
		cli.StringFlag{
			Name:        "opt-fields,O",
			Usage:       "specify which optional fields are wanted",
			Destination: &fOptFields,
		},
		cli.StringFlag{
			Name:        "page-size,P",
			Usage:       "page size for results",
			Destination: &fPageSize,
		},

		cli.StringFlag{
			Name:        "sort,S",
			Usage:       "sort results",
			Destination: &fSortOrder,
		},
		cli.BoolTFlag{
			Name:        "1,is-oneoff",
			Usage:       "one-time measurement",
			Destination: &fIsOneOff,
		},
		cli.BoolFlag{
			Name:        "6, ipv6",
			Usage:       "Only IPv6",
			Destination: &fWant6,
		},
		cli.BoolFlag{
			Name:        "4, ipv4",
			Usage:       "Only IPv4",
			Destination: &fWant4,
		},
		// These are not global parameters but it makes sense to define them only once
		// and not in every cmd_* files.
		cli.IntFlag{
			Name:        "pool-size,N",
			Usage:       "Number of probes to request",
			Destination: &fPoolSize,
		},
		cli.StringFlag{
			Name:        "area-type",
			Usage:       "Set type for probes (area, country, etc.)",
			Destination: &fAreaType,
		},
		cli.StringFlag{
			Name:        "area-value",
			Usage:       "Value for the probe set (WW, West, etc.)",
			Destination: &fAreaValue,
		},
		cli.StringFlag{
			Name:        "country,C",
			Usage:       "Short cut to specify a country",
			Destination: &fCountry,
		},
		cli.StringFlag{
			Name:        "tags,T",
			Usage:       "Include/exclude tags for probesets",
			Destination: &fTags,
		},
	}

	// Ensure -4 & -6 are treated properly & initialization is done
	app.Before = finalcheck

	sort.Sort(ByAlphabet(cliCommands))
	app.Commands = cliCommands
	app.Run(os.Args)
}
<commit_msg>Welcome to v0.41 with filtering on tags.<commit_after>/*
This package is just a collection of use-case for the various aspects of the RIPE API.
Consider this both as an example on how to use the API and a testing tool for the API wrapper.
*/
package main

import (
	"fmt"
	"github.com/keltia/proxy"
	"github.com/keltia/ripe-atlas"
	"github.com/urfave/cli"
	"log"
	"os"
	"sort"
)

var (
	// CLI specific options
	fDebug   bool
	fLogfile string
	fVerbose bool

	// See flag.go for details

	// Global API options
	fFieldList string
	fFormat    string
	fInclude   string
	fOptFields string
	fPageNum   string
	fPageSize  string
	fSortOrder string
	fWantMine  bool

	// Probe-specific ones
	fAllProbes bool
	fIsAnchor  bool
	fTags      string

	// Common measurement ones
	fAllMeasurements bool
	fAsn             string
	fAsnV4           string
	fAsnV6           string
	fCountry         string
	fProtocol        string
	fMeasureType     string
	fWant4           bool
	fWant6           bool
	fMTags           string

	// Create measurements
	fBillTo    string
	fIsOneOff  bool
	fStartTime string
	fStopTime  string

	// HTTP
	fHTTPMethod  string
	fUserAgent   string
	fHTTPVersion string

	// DNS
	fBitCD         bool
	fDisableDNSSEC bool

	// Traceroute
	fMaxHops    int
	fPacketSize int

	// ProbeSet parameters
	fPoolSize  int
	fAreaType  string
	fAreaValue string

	// Our configuration file
	cnf *Config

	// All possible commands
	cliCommands []cli.Command

	client *atlas.Client

	// Our tiple-valued synthesis of fWant4/fWant6
	wantAF string
)

const (
	atlasVersion = "0.41"
	// MyName is the application name
	MyName = "ripe-atlas"

	// WantBoth is the way to ask for both IPv4 & IPv6.
	WantBoth = "64"

	// Want4 only 4
	Want4 = "4"
	// Want6 only 6
	Want6 = "6"
)

func openlog(fn string) *log.Logger {
	fh, err := os.OpenFile(fn, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
	if err != nil {
		log.Fatalf("error: can not open logfile %s: %v", fn, err)
	}

	mylog := log.New(fh, "", log.LstdFlags)
	verbose("Logfile: %s %#v", fn, mylog)

	return mylog
}

// -4 & -6 are special, if neither is specified, then we turn both as true
// Check a few other things while we are here
func finalcheck(c *cli.Context) error {

	var (
		err   error
		mylog *log.Logger
	)

	// Load main configuration
	cnf, err = LoadConfig("")
	if err != nil {
		verbose("No configuration file found.")
	}

	// Logical
	if fDebug {
		fVerbose = true
		verbose("config: %#v", cnf)
	}

	// Various messages
	if cnf.APIKey != "" {
		verbose("Found API key!")
	} else {
		verbose("No API key!")
	}

	if cnf.DefaultProbe != 0 {
		verbose("Found default probe: %d\n", cnf.DefaultProbe)
	}

	// Allow overwrite of a few parameters
	if fPoolSize != 0 {
		cnf.ProbeSet.PoolSize = fPoolSize
	}
	if fAreaType != "" {
		cnf.ProbeSet.Type = fAreaType
	}
	if fAreaValue != "" {
		cnf.ProbeSet.Value = fAreaValue
	}

	// Check whether we have proxy authentication (from a separate config file)
	auth, err := proxy.SetupProxyAuth()
	if err != nil {
		verbose("Invalid or no proxy auth credentials")
	}

	// If we want a logfile, open one for the API to log into
	if fLogfile != "" {
		mylog = openlog(fLogfile)
	}

	// Check whether the -C <country> was specified then override configuration
	if fCountry != "" {
		cnf.ProbeSet.Type = "country"
		cnf.ProbeSet.Value = fCountry
	}

	// Check whether tags are specified
	if fTags != "" {
		cnf.ProbeSet.Tags = fTags
	}

	// Wondering whether to move to the Functional options pattern
	// cf. https://dave.cheney.net/2016/11/13/do-not-fear-first-class-functions
	client, err = atlas.NewClient(atlas.Config{
		APIKey:       cnf.APIKey,
		DefaultProbe: cnf.DefaultProbe,
		IsOneOff:     fIsOneOff,
		PoolSize:     cnf.ProbeSet.PoolSize,
		AreaType:     cnf.ProbeSet.Type,
		AreaValue:    cnf.ProbeSet.Value,
		Tags:         cnf.ProbeSet.Tags,
		ProxyAuth:    auth,
		Verbose:      fVerbose,
		Log:          mylog,
	})

	// No need to continue if this fails
	if err != nil {
		log.Fatalf("Error creating the Atlas client: %v", err)
	}

	if fWant4 {
		wantAF = Want4
	}

	if fWant6 {
		wantAF = Want6
	}

	// Both are fine
	if fWant4 && fWant6 {
		wantAF = WantBoth
	}

	// So is neither — common case
	if !fWant4 && !fWant6 {
		wantAF = WantBoth
	}

	debug("wantAF=%s", wantAF)
	return nil
}

// main is the starting point (and everything)
func main() {
	cli.VersionFlag = cli.BoolFlag{Name: "version, V"}

	cli.VersionPrinter = func(c *cli.Context) {
		fmt.Printf("API wrapper: %s Atlas API: %s\n", c.App.Version, atlas.GetVersion())
	}

	app := cli.NewApp()
	app.Name = "atlas"
	app.Usage = "RIPE Atlas CLI interface"
	app.Author = "Ollivier Robert <roberto@keltia.net>"
	app.Version = atlasVersion
	//app.HideVersion = true

	// General flags
	app.Flags = []cli.Flag{
		cli.StringFlag{
			Name:        "format,f",
			Usage:       "specify output format (NOT IMPLEMENTED)",
			Destination: &fFormat,
		},
		cli.BoolFlag{
			Name:        "debug,D",
			Usage:       "debug mode",
			Destination: &fDebug,
		},
		cli.BoolFlag{
			Name:        "verbose,v",
			Usage:       "verbose mode",
			Destination: &fVerbose,
		},
		cli.StringFlag{
			Name:        "fields,F",
			Usage:       "specify which fields are wanted",
			Destination: &fFieldList,
		},
		cli.StringFlag{
			Name:        "include,I",
			Usage:       "specify whether objects should be expanded",
			Destination: &fInclude,
		},
		cli.StringFlag{
			Name:        "logfile,L",
			Usage:       "specify a log file",
			Destination: &fLogfile,
		},
		cli.BoolFlag{
			Name:        "mine,M",
			Usage:       "limit output to my objects",
			Destination: &fWantMine,
		},
		cli.StringFlag{
			Name:        "opt-fields,O",
			Usage:       "specify which optional fields are wanted",
			Destination: &fOptFields,
		},
		cli.StringFlag{
			Name:        "page-size,P",
			Usage:       "page size for results",
			Destination: &fPageSize,
		},

		cli.StringFlag{
			Name:        "sort,S",
			Usage:       "sort results",
			Destination: &fSortOrder,
		},
		cli.BoolTFlag{
			Name:        "1,is-oneoff",
			Usage:       "one-time measurement",
			Destination: &fIsOneOff,
		},
		cli.BoolFlag{
			Name:        "6, ipv6",
			Usage:       "Only IPv6",
			Destination: &fWant6,
		},
		cli.BoolFlag{
			Name:        "4, ipv4",
			Usage:       "Only IPv4",
			Destination: &fWant4,
		},
		// These are not global parameters but it makes sense to define them only once
		// and not in every cmd_* files.
		cli.IntFlag{
			Name:        "pool-size,N",
			Usage:       "Number of probes to request",
			Destination: &fPoolSize,
		},
		cli.StringFlag{
			Name:        "area-type",
			Usage:       "Set type for probes (area, country, etc.)",
			Destination: &fAreaType,
		},
		cli.StringFlag{
			Name:        "area-value",
			Usage:       "Value for the probe set (WW, West, etc.)",
			Destination: &fAreaValue,
		},
		cli.StringFlag{
			Name:        "country,C",
			Usage:       "Short cut to specify a country",
			Destination: &fCountry,
		},
		cli.StringFlag{
			Name:        "tags,T",
			Usage:       "Include/exclude tags for probesets",
			Destination: &fTags,
		},
	}

	// Ensure -4 & -6 are treated properly & initialization is done
	app.Before = finalcheck

	sort.Sort(ByAlphabet(cliCommands))
	app.Commands = cliCommands
	app.Run(os.Args)
}
<|endoftext|>


<commit_before>/*
This package is just a collection of use-case for the various aspects of the RIPE API.
Consider this both as an example on how to use the API and a testing tool for the API wrapper.
*/
package main

import (
	"fmt"
	"log"
	"os"
	"sort"

	"github.com/keltia/proxy"
	"github.com/urfave/cli"

	atlas "github.com/keltia/ripe-atlas"
)

var (
	// CLI specific options
	fDebug   bool
	fLogfile string
	fVerbose bool

	// See flag.go for details

	// Global API options
	fFieldList string
	fFormat    string
	fInclude   string
	fOptFields string
	fPageNum   string
	fPageSize  string
	fSortOrder string
	fWantMine  bool

	// Probe-specific ones
	fAllProbes bool
	fIsAnchor  bool
	fTags      string

	// Common measurement ones
	fAllMeasurements bool
	fAsn             string
	fAsnV4           string
	fAsnV6           string
	fCountry         string
	fProtocol        string
	fMeasureType     string
	fWant4           bool
	fWant6           bool
	fMTags           string

	// Create measurements
	fBillTo    string
	fIsOneOff  bool
	fStartTime string
	fStopTime  string

	// HTTP
	fHTTPMethod  string
	fUserAgent   string
	fHTTPVersion string

	// DNS
	fBitCD         bool
	fDisableDNSSEC bool

	// Traceroute
	fMaxHops    int
	fPacketSize int

	// ProbeSet parameters
	fPoolSize  int
	fAreaType  string
	fAreaValue string

	// Our configuration file
	cnf *Config

	// All possible commands
	cliCommands []cli.Command

	client *atlas.Client

	// Our tiple-valued synthesis of fWant4/fWant6
	wantAF string
)

const (
	atlasVersion = "0.41"
	// MyName is the application name
	MyName = "ripe-atlas"

	// WantBoth is the way to ask for both IPv4 & IPv6.
	WantBoth = "64"

	// Want4 only 4
	Want4 = "4"
	// Want6 only 6
	Want6 = "6"
)

func openlog(fn string) *log.Logger {
	fh, err := os.OpenFile(fn, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
	if err != nil {
		log.Fatalf("error: can not open logfile %s: %v", fn, err)
	}

	mylog := log.New(fh, "", log.LstdFlags)
	verbose("Logfile: %s %#v", fn, mylog)

	return mylog
}

// -4 & -6 are special, if neither is specified, then we turn both as true
// Check a few other things while we are here
func finalcheck(c *cli.Context) error {

	var (
		err   error
		mylog *log.Logger
	)

	// Load main configuration
	cnf, err = LoadConfig("")
	if err != nil {
		verbose("No configuration file found.")
	}

	// Logical
	if fDebug {
		fVerbose = true
		verbose("config: %#v", cnf)
	}

	// Various messages
	if cnf.APIKey != "" {
		verbose("Found API key!")
	} else {
		verbose("No API key!")
	}

	if cnf.DefaultProbe != 0 {
		verbose("Found default probe: %d\n", cnf.DefaultProbe)
	}

	// Allow overwrite of a few parameters
	if fPoolSize != 0 {
		cnf.ProbeSet.PoolSize = fPoolSize
	}
	if fAreaType != "" {
		cnf.ProbeSet.Type = fAreaType
	}
	if fAreaValue != "" {
		cnf.ProbeSet.Value = fAreaValue
	}

	// Check whether we have proxy authentication (from a separate config file)
	auth, err := proxy.SetupProxyAuth()
	if err != nil {
		verbose("Invalid or no proxy auth credentials")
	}

	// If we want a logfile, open one for the API to log into
	if fLogfile != "" {
		mylog = openlog(fLogfile)
	}

	// Check whether the -C <country> was specified then override configuration
	if fCountry != "" {
		cnf.ProbeSet.Type = "country"
		cnf.ProbeSet.Value = fCountry
	}

	// Check whether tags are specified
	if fTags != "" {
		cnf.ProbeSet.Tags = fTags
	}

	// Wondering whether to move to the Functional options pattern
	// cf. https://dave.cheney.net/2016/11/13/do-not-fear-first-class-functions
	client, err = atlas.NewClient(atlas.Config{
		APIKey:       cnf.APIKey,
		DefaultProbe: cnf.DefaultProbe,
		IsOneOff:     fIsOneOff,
		PoolSize:     cnf.ProbeSet.PoolSize,
		AreaType:     cnf.ProbeSet.Type,
		AreaValue:    cnf.ProbeSet.Value,
		Tags:         cnf.ProbeSet.Tags,
		ProxyAuth:    auth,
		Verbose:      fVerbose,
		Log:          mylog,
	})

	// No need to continue if this fails
	if err != nil {
		log.Fatalf("Error creating the Atlas client: %v", err)
	}

	if fWant4 {
		wantAF = Want4
	}

	if fWant6 {
		wantAF = Want6
	}

	// Both are fine
	if fWant4 && fWant6 {
		wantAF = WantBoth
	}

	// So is neither — common case
	if !fWant4 && !fWant6 {
		wantAF = WantBoth
	}

	debug("wantAF=%s", wantAF)
	return nil
}

// main is the starting point (and everything)
func main() {
	cli.VersionFlag = cli.BoolFlag{Name: "version, V"}

	cli.VersionPrinter = func(c *cli.Context) {
		fmt.Printf("API wrapper: %s Atlas API: %s\n", c.App.Version, atlas.GetVersion())
	}

	app := cli.NewApp()
	app.Name = "atlas"
	app.Usage = "RIPE Atlas CLI interface"
	app.Author = "Ollivier Robert <roberto@keltia.net>"
	app.Version = atlasVersion
	//app.HideVersion = true

	// General flags
	app.Flags = []cli.Flag{
		cli.StringFlag{
			Name:        "format,f",
			Usage:       "specify output format (NOT IMPLEMENTED)",
			Destination: &fFormat,
		},
		cli.BoolFlag{
			Name:        "debug,D",
			Usage:       "debug mode",
			Destination: &fDebug,
		},
		cli.BoolFlag{
			Name:        "verbose,v",
			Usage:       "verbose mode",
			Destination: &fVerbose,
		},
		cli.StringFlag{
			Name:        "fields,F",
			Usage:       "specify which fields are wanted",
			Destination: &fFieldList,
		},
		cli.StringFlag{
			Name:        "include,I",
			Usage:       "specify whether objects should be expanded",
			Destination: &fInclude,
		},
		cli.StringFlag{
			Name:        "logfile,L",
			Usage:       "specify a log file",
			Destination: &fLogfile,
		},
		cli.BoolFlag{
			Name:        "mine,M",
			Usage:       "limit output to my objects",
			Destination: &fWantMine,
		},
		cli.StringFlag{
			Name:        "opt-fields,O",
			Usage:       "specify which optional fields are wanted",
			Destination: &fOptFields,
		},
		cli.StringFlag{
			Name:        "page-size,P",
			Usage:       "page size for results",
			Destination: &fPageSize,
		},

		cli.StringFlag{
			Name:        "sort,S",
			Usage:       "sort results",
			Destination: &fSortOrder,
		},
		cli.BoolTFlag{
			Name:        "1,is-oneoff",
			Usage:       "one-time measurement",
			Destination: &fIsOneOff,
		},
		cli.BoolFlag{
			Name:        "6, ipv6",
			Usage:       "Only IPv6",
			Destination: &fWant6,
		},
		cli.BoolFlag{
			Name:        "4, ipv4",
			Usage:       "Only IPv4",
			Destination: &fWant4,
		},
		// These are not global parameters but it makes sense to define them only once
		// and not in every cmd_* files.
		cli.IntFlag{
			Name:        "pool-size,N",
			Usage:       "Number of probes to request",
			Destination: &fPoolSize,
		},
		cli.StringFlag{
			Name:        "area-type",
			Usage:       "Set type for probes (area, country, etc.)",
			Destination: &fAreaType,
		},
		cli.StringFlag{
			Name:        "area-value",
			Usage:       "Value for the probe set (WW, West, etc.)",
			Destination: &fAreaValue,
		},
		cli.StringFlag{
			Name:        "country,C",
			Usage:       "Short cut to specify a country",
			Destination: &fCountry,
		},
		cli.StringFlag{
			Name:        "tags,T",
			Usage:       "Include/exclude tags for probesets",
			Destination: &fTags,
		},
	}

	// Ensure -4 & -6 are treated properly & initialization is done
	app.Before = finalcheck

	sort.Sort(ByAlphabet(cliCommands))
	app.Commands = cliCommands
	app.Run(os.Args)
}
<commit_msg>Welcome 0.42 with "-B,--bare" added to "ip".<commit_after>/*
This package is just a collection of use-case for the various aspects of the RIPE API.
Consider this both as an example on how to use the API and a testing tool for the API wrapper.
*/
package main

import (
	"fmt"
	"log"
	"os"
	"sort"

	"github.com/keltia/proxy"
	"github.com/urfave/cli"

	atlas "github.com/keltia/ripe-atlas"
)

var (
	// CLI specific options
	fDebug   bool
	fLogfile string
	fVerbose bool

	// See flag.go for details

	// Global API options
	fFieldList string
	fFormat    string
	fInclude   string
	fOptFields string
	fPageNum   string
	fPageSize  string
	fSortOrder string
	fWantMine  bool

	// Probe-specific ones
	fAllProbes bool
	fIsAnchor  bool
	fTags      string

	// Common measurement ones
	fAllMeasurements bool
	fAsn             string
	fAsnV4           string
	fAsnV6           string
	fCountry         string
	fProtocol        string
	fMeasureType     string
	fWant4           bool
	fWant6           bool
	fMTags           string

	// Create measurements
	fBillTo    string
	fIsOneOff  bool
	fStartTime string
	fStopTime  string

	// HTTP
	fHTTPMethod  string
	fUserAgent   string
	fHTTPVersion string

	// DNS
	fBitCD         bool
	fDisableDNSSEC bool

	// Traceroute
	fMaxHops    int
	fPacketSize int

	// ProbeSet parameters
	fPoolSize  int
	fAreaType  string
	fAreaValue string

	// Our configuration file
	cnf *Config

	// All possible commands
	cliCommands []cli.Command

	client *atlas.Client

	// Our tiple-valued synthesis of fWant4/fWant6
	wantAF string
)

const (
	atlasVersion = "0.42"
	// MyName is the application name
	MyName = "ripe-atlas"

	// WantBoth is the way to ask for both IPv4 & IPv6.
	WantBoth = "64"

	// Want4 only 4
	Want4 = "4"
	// Want6 only 6
	Want6 = "6"
)

func openlog(fn string) *log.Logger {
	fh, err := os.OpenFile(fn, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
	if err != nil {
		log.Fatalf("error: can not open logfile %s: %v", fn, err)
	}

	mylog := log.New(fh, "", log.LstdFlags)
	verbose("Logfile: %s %#v", fn, mylog)

	return mylog
}

// -4 & -6 are special, if neither is specified, then we turn both as true
// Check a few other things while we are here
func finalcheck(c *cli.Context) error {

	var (
		err   error
		mylog *log.Logger
	)

	// Load main configuration
	cnf, err = LoadConfig("")
	if err != nil {
		verbose("No configuration file found.")
	}

	// Logical
	if fDebug {
		fVerbose = true
		verbose("config: %#v", cnf)
	}

	// Various messages
	if cnf.APIKey != "" {
		verbose("Found API key!")
	} else {
		verbose("No API key!")
	}

	if cnf.DefaultProbe != 0 {
		verbose("Found default probe: %d\n", cnf.DefaultProbe)
	}

	// Allow overwrite of a few parameters
	if fPoolSize != 0 {
		cnf.ProbeSet.PoolSize = fPoolSize
	}
	if fAreaType != "" {
		cnf.ProbeSet.Type = fAreaType
	}
	if fAreaValue != "" {
		cnf.ProbeSet.Value = fAreaValue
	}

	// Check whether we have proxy authentication (from a separate config file)
	auth, err := proxy.SetupProxyAuth()
	if err != nil {
		verbose("Invalid or no proxy auth credentials")
	}

	// If we want a logfile, open one for the API to log into
	if fLogfile != "" {
		mylog = openlog(fLogfile)
	}

	// Check whether the -C <country> was specified then override configuration
	if fCountry != "" {
		cnf.ProbeSet.Type = "country"
		cnf.ProbeSet.Value = fCountry
	}

	// Check whether tags are specified
	if fTags != "" {
		cnf.ProbeSet.Tags = fTags
	}

	// Wondering whether to move to the Functional options pattern
	// cf. https://dave.cheney.net/2016/11/13/do-not-fear-first-class-functions
	client, err = atlas.NewClient(atlas.Config{
		APIKey:       cnf.APIKey,
		DefaultProbe: cnf.DefaultProbe,
		IsOneOff:     fIsOneOff,
		PoolSize:     cnf.ProbeSet.PoolSize,
		AreaType:     cnf.ProbeSet.Type,
		AreaValue:    cnf.ProbeSet.Value,
		Tags:         cnf.ProbeSet.Tags,
		ProxyAuth:    auth,
		Verbose:      fVerbose,
		Log:          mylog,
	})

	// No need to continue if this fails
	if err != nil {
		log.Fatalf("Error creating the Atlas client: %v", err)
	}

	if fWant4 {
		wantAF = Want4
	}

	if fWant6 {
		wantAF = Want6
	}

	// Both are fine
	if fWant4 && fWant6 {
		wantAF = WantBoth
	}

	// So is neither — common case
	if !fWant4 && !fWant6 {
		wantAF = WantBoth
	}

	debug("wantAF=%s", wantAF)
	return nil
}

// main is the starting point (and everything)
func main() {
	cli.VersionFlag = cli.BoolFlag{Name: "version, V"}

	cli.VersionPrinter = func(c *cli.Context) {
		fmt.Printf("API wrapper: %s Atlas API: %s\n", c.App.Version, atlas.GetVersion())
	}

	app := cli.NewApp()
	app.Name = "atlas"
	app.Usage = "RIPE Atlas CLI interface"
	app.Author = "Ollivier Robert <roberto@keltia.net>"
	app.Version = atlasVersion
	//app.HideVersion = true

	// General flags
	app.Flags = []cli.Flag{
		cli.StringFlag{
			Name:        "format,f",
			Usage:       "specify output format (NOT IMPLEMENTED)",
			Destination: &fFormat,
		},
		cli.BoolFlag{
			Name:        "debug,D",
			Usage:       "debug mode",
			Destination: &fDebug,
		},
		cli.BoolFlag{
			Name:        "verbose,v",
			Usage:       "verbose mode",
			Destination: &fVerbose,
		},
		cli.StringFlag{
			Name:        "fields,F",
			Usage:       "specify which fields are wanted",
			Destination: &fFieldList,
		},
		cli.StringFlag{
			Name:        "include,I",
			Usage:       "specify whether objects should be expanded",
			Destination: &fInclude,
		},
		cli.StringFlag{
			Name:        "logfile,L",
			Usage:       "specify a log file",
			Destination: &fLogfile,
		},
		cli.BoolFlag{
			Name:        "mine,M",
			Usage:       "limit output to my objects",
			Destination: &fWantMine,
		},
		cli.StringFlag{
			Name:        "opt-fields,O",
			Usage:       "specify which optional fields are wanted",
			Destination: &fOptFields,
		},
		cli.StringFlag{
			Name:        "page-size,P",
			Usage:       "page size for results",
			Destination: &fPageSize,
		},

		cli.StringFlag{
			Name:        "sort,S",
			Usage:       "sort results",
			Destination: &fSortOrder,
		},
		cli.BoolTFlag{
			Name:        "1,is-oneoff",
			Usage:       "one-time measurement",
			Destination: &fIsOneOff,
		},
		cli.BoolFlag{
			Name:        "6, ipv6",
			Usage:       "Only IPv6",
			Destination: &fWant6,
		},
		cli.BoolFlag{
			Name:        "4, ipv4",
			Usage:       "Only IPv4",
			Destination: &fWant4,
		},
		// These are not global parameters but it makes sense to define them only once
		// and not in every cmd_* files.
		cli.IntFlag{
			Name:        "pool-size,N",
			Usage:       "Number of probes to request",
			Destination: &fPoolSize,
		},
		cli.StringFlag{
			Name:        "area-type",
			Usage:       "Set type for probes (area, country, etc.)",
			Destination: &fAreaType,
		},
		cli.StringFlag{
			Name:        "area-value",
			Usage:       "Value for the probe set (WW, West, etc.)",
			Destination: &fAreaValue,
		},
		cli.StringFlag{
			Name:        "country,C",
			Usage:       "Short cut to specify a country",
			Destination: &fCountry,
		},
		cli.StringFlag{
			Name:        "tags,T",
			Usage:       "Include/exclude tags for probesets",
			Destination: &fTags,
		},
	}

	// Ensure -4 & -6 are treated properly & initialization is done
	app.Before = finalcheck

	sort.Sort(ByAlphabet(cliCommands))
	app.Commands = cliCommands
	app.Run(os.Args)
}
<|endoftext|>