<commit_before>package agent

import (
	"fmt"
	"io"
	"io/ioutil"
	"math/rand"
	"net/http"
	"net/http/httptest"
	"os"
	"path/filepath"
	"runtime"
	"strings"
	"time"

	"github.com/hashicorp/nomad/api"
	"github.com/hashicorp/nomad/nomad"
	"github.com/hashicorp/nomad/nomad/structs"
	sconfig "github.com/hashicorp/nomad/nomad/structs/config"
	"github.com/hashicorp/nomad/testutil"
)

func init() {
	rand.Seed(time.Now().UnixNano()) // seed random number generator
}

// TempDir defines the base dir for temporary directories.
var TempDir = os.TempDir()

// TestAgent encapsulates an Agent with a default configuration and
// startup procedure suitable for testing. It panics if there are errors
// during creation or startup instead of returning errors. It manages a
// temporary data directory which is removed after shutdown.
type TestAgent struct {
	// Name is an optional name of the agent.
	Name string

	// ConfigCallback is an optional callback that allows modification of the
	// configuration before the agent is started.
	ConfigCallback func(*Config)

	// Config is the agent configuration. If Config is nil then
	// TestConfig() is used. If Config.DataDir is set then it is
	// the callers responsibility to clean up the data directory.
	// Otherwise, a temporary data directory is created and removed
	// when Shutdown() is called.
	Config *Config

	// LogOutput is the sink for the logs. If nil, logs are written
	// to os.Stderr.
	LogOutput io.Writer

	// DataDir is the data directory which is used when Config.DataDir
	// is not set. It is created automatically and removed when
	// Shutdown() is called.
	DataDir string

	// Key is the optional encryption key for the keyring.
	Key string

	// Server is a reference to the started HTTP endpoint.
	// It is valid after Start().
	Server *HTTPServer

	// Agent is the embedded Nomad agent.
	// It is valid after Start().
	*Agent
}

// NewTestAgent returns a started agent with the given name and
// configuration. It panics if the agent could not be started. The
// caller should call Shutdown() to stop the agent and remove temporary
// directories.
func NewTestAgent(name string, configCallback func(*Config)) *TestAgent {
	a := &TestAgent{Name: name, ConfigCallback: configCallback}
	a.Start()
	return a
}

// Start starts a test agent. It panics if the agent could not be started.
func (a *TestAgent) Start() *TestAgent {
	if a.Agent != nil {
		panic("TestAgent already started")
	}
	if a.Config == nil {
		a.Config = a.config()
	}
	if a.Config.DataDir == "" {
		name := "agent"
		if a.Name != "" {
			name = a.Name + "-agent"
		}
		name = strings.Replace(name, "/", "_", -1)
		d, err := ioutil.TempDir(TempDir, name)
		if err != nil {
			panic(fmt.Sprintf("Error creating data dir %s: %s", filepath.Join(TempDir, name), err))
		}
		a.DataDir = d
		a.Config.DataDir = d
		a.Config.NomadConfig.DataDir = d
	}

	for i := 10; i >= 0; i-- {
		pickRandomPorts(a.Config)
		a.Config.NodeName = fmt.Sprintf("Node %d", a.Config.Ports.RPC)

		// write the keyring
		if a.Key != "" {
			writeKey := func(key, filename string) {
				path := filepath.Join(a.Config.DataDir, filename)
				if err := initKeyring(path, key); err != nil {
					panic(fmt.Sprintf("Error creating keyring %s: %s", path, err))
				}
			}
			writeKey(a.Key, serfKeyring)
		}

		// we need the err var in the next exit condition
		if agent, err := a.start(); err == nil {
			a.Agent = agent
			break
		} else if i == 0 {
			fmt.Println(a.Name, "Error starting agent:", err)
			runtime.Goexit()
		} else {
			if agent != nil {
				agent.Shutdown()
			}
			wait := time.Duration(rand.Int31n(2000)) * time.Millisecond
			fmt.Println(a.Name, "retrying in", wait)
			time.Sleep(wait)
		}

		// Clean out the data dir if we are responsible for it before we
		// try again, since the old ports may have gotten written to
		// the data dir, such as in the Raft configuration.
		if a.DataDir != "" {
			if err := os.RemoveAll(a.DataDir); err != nil {
				fmt.Println(a.Name, "Error resetting data dir:", err)
				runtime.Goexit()
			}
		}
	}

	if a.Config.Server.BootstrapExpect == 1 && a.Config.Server.Enabled {
		testutil.WaitForResult(func() (bool, error) {
			args := &structs.GenericRequest{}
			var leader string
			err := a.RPC("Status.Leader", args, &leader)
			return leader != "", err
		}, func(err error) {
			panic(fmt.Sprintf("failed to find leader: %v", err))
		})
	} else {
		testutil.WaitForResult(func() (bool, error) {
			req, _ := http.NewRequest("GET", "/v1/agent/self", nil)
			resp := httptest.NewRecorder()
			_, err := a.Server.AgentSelfRequest(resp, req)
			return err == nil && resp.Code == 200, err
		}, func(err error) {
			panic(fmt.Sprintf("failed OK response: %v", err))
		})
	}
	return a
}

func (a *TestAgent) start() (*Agent, error) {
	if a.LogOutput == nil {
		a.LogOutput = os.Stderr
	}

	agent, err := NewAgent(a.Config, a.LogOutput)
	if err != nil {
		return nil, err
	}

	// Setup the HTTP server
	http, err := NewHTTPServer(agent, a.Config)
	if err != nil {
		return agent, err
	}

	a.Server = http
	return agent, nil
}

// Shutdown stops the agent and removes the data directory if it is
// managed by the test agent.
func (a *TestAgent) Shutdown() error {
	defer func() {
		if a.DataDir != "" {
			os.RemoveAll(a.DataDir)
		}
	}()

	// shutdown agent before endpoints
	a.Server.Shutdown()
	return a.Agent.Shutdown()
}

func (a *TestAgent) HTTPAddr() string {
	if a.Server == nil {
		return ""
	}
	return a.Server.Addr
}

func (a *TestAgent) Client() *api.Client {
	conf := api.DefaultConfig()
	conf.Address = a.HTTPAddr()
	c, err := api.NewClient(conf)
	if err != nil {
		panic(fmt.Sprintf("Error creating consul API client: %s", err))
	}
	return c
}

// FivePorts returns the first port number of a block of
// five random ports.
func FivePorts() int {
	return 1030 + int(rand.Int31n(6440))*5
}

// pickRandomPorts selects random ports from fixed size random blocks of
// ports. This does not eliminate the chance for port conflict but
// reduces it significanltly with little overhead. Furthermore, asking
// the kernel for a random port by binding to port 0 prolongs the test
// execution (in our case +20sec) while also not fully eliminating the
// chance of port conflicts for concurrently executed test binaries.
// Instead of relying on one set of ports to be sufficient we retry
// starting the agent with different ports on port conflict.
func pickRandomPorts(c *Config) {
	port := FivePorts()
	c.Ports.HTTP = port + 1
	c.Ports.RPC = port + 2
	c.Ports.Serf = port + 3

	if err := c.normalizeAddrs(); err != nil {
		panic(fmt.Sprintf("error normalizing config: %v", err))
	}
}

// TestConfig returns a unique default configuration for testing an
// agent.
func (a *TestAgent) config() *Config {
	conf := DevConfig()

	// Customize the server configuration
	config := nomad.DefaultConfig()
	conf.NomadConfig = config

	// Bind and set ports
	conf.BindAddr = "127.0.0.1"

	conf.Consul = sconfig.DefaultConsulConfig()
	conf.Vault.Enabled = new(bool)

	// Tighten the Serf timing
	config.SerfConfig.MemberlistConfig.SuspicionMult = 2
	config.SerfConfig.MemberlistConfig.RetransmitMult = 2
	config.SerfConfig.MemberlistConfig.ProbeTimeout = 50 * time.Millisecond
	config.SerfConfig.MemberlistConfig.ProbeInterval = 100 * time.Millisecond
	config.SerfConfig.MemberlistConfig.GossipInterval = 100 * time.Millisecond

	// Tighten the Raft timing
	config.RaftConfig.LeaderLeaseTimeout = 20 * time.Millisecond
	config.RaftConfig.HeartbeatTimeout = 40 * time.Millisecond
	config.RaftConfig.ElectionTimeout = 40 * time.Millisecond
	config.RaftConfig.StartAsLeader = true
	config.RaftTimeout = 500 * time.Millisecond

	if a.ConfigCallback != nil {
		a.ConfigCallback(conf)
	}

	return conf
}
<commit_msg>Fix bootstrapping and waiting<commit_after>package agent

import (
	"fmt"
	"io"
	"io/ioutil"
	"math/rand"
	"net/http"
	"net/http/httptest"
	"os"
	"path/filepath"
	"runtime"
	"strings"
	"time"

	"github.com/hashicorp/nomad/api"
	"github.com/hashicorp/nomad/nomad"
	"github.com/hashicorp/nomad/nomad/structs"
	sconfig "github.com/hashicorp/nomad/nomad/structs/config"
	"github.com/hashicorp/nomad/testutil"
)

func init() {
	rand.Seed(time.Now().UnixNano()) // seed random number generator
}

// TempDir defines the base dir for temporary directories.
var TempDir = os.TempDir()

// TestAgent encapsulates an Agent with a default configuration and
// startup procedure suitable for testing. It panics if there are errors
// during creation or startup instead of returning errors. It manages a
// temporary data directory which is removed after shutdown.
type TestAgent struct {
	// Name is an optional name of the agent.
	Name string

	// ConfigCallback is an optional callback that allows modification of the
	// configuration before the agent is started.
	ConfigCallback func(*Config)

	// Config is the agent configuration. If Config is nil then
	// TestConfig() is used. If Config.DataDir is set then it is
	// the callers responsibility to clean up the data directory.
	// Otherwise, a temporary data directory is created and removed
	// when Shutdown() is called.
	Config *Config

	// LogOutput is the sink for the logs. If nil, logs are written
	// to os.Stderr.
	LogOutput io.Writer

	// DataDir is the data directory which is used when Config.DataDir
	// is not set. It is created automatically and removed when
	// Shutdown() is called.
	DataDir string

	// Key is the optional encryption key for the keyring.
	Key string

	// Server is a reference to the started HTTP endpoint.
	// It is valid after Start().
	Server *HTTPServer

	// Agent is the embedded Nomad agent.
	// It is valid after Start().
	*Agent
}

// NewTestAgent returns a started agent with the given name and
// configuration. It panics if the agent could not be started. The
// caller should call Shutdown() to stop the agent and remove temporary
// directories.
func NewTestAgent(name string, configCallback func(*Config)) *TestAgent {
	a := &TestAgent{Name: name, ConfigCallback: configCallback}
	a.Start()
	return a
}

// Start starts a test agent. It panics if the agent could not be started.
func (a *TestAgent) Start() *TestAgent {
	if a.Agent != nil {
		panic("TestAgent already started")
	}
	if a.Config == nil {
		a.Config = a.config()
	}
	if a.Config.DataDir == "" {
		name := "agent"
		if a.Name != "" {
			name = a.Name + "-agent"
		}
		name = strings.Replace(name, "/", "_", -1)
		d, err := ioutil.TempDir(TempDir, name)
		if err != nil {
			panic(fmt.Sprintf("Error creating data dir %s: %s", filepath.Join(TempDir, name), err))
		}
		a.DataDir = d
		a.Config.DataDir = d
		a.Config.NomadConfig.DataDir = d
	}

	for i := 10; i >= 0; i-- {
		pickRandomPorts(a.Config)
		a.Config.NodeName = fmt.Sprintf("Node %d", a.Config.Ports.RPC)

		// write the keyring
		if a.Key != "" {
			writeKey := func(key, filename string) {
				path := filepath.Join(a.Config.DataDir, filename)
				if err := initKeyring(path, key); err != nil {
					panic(fmt.Sprintf("Error creating keyring %s: %s", path, err))
				}
			}
			writeKey(a.Key, serfKeyring)
		}

		// we need the err var in the next exit condition
		if agent, err := a.start(); err == nil {
			a.Agent = agent
			break
		} else if i == 0 {
			fmt.Println(a.Name, "Error starting agent:", err)
			runtime.Goexit()
		} else {
			if agent != nil {
				agent.Shutdown()
			}
			wait := time.Duration(rand.Int31n(2000)) * time.Millisecond
			fmt.Println(a.Name, "retrying in", wait)
			time.Sleep(wait)
		}

		// Clean out the data dir if we are responsible for it before we
		// try again, since the old ports may have gotten written to
		// the data dir, such as in the Raft configuration.
		if a.DataDir != "" {
			if err := os.RemoveAll(a.DataDir); err != nil {
				fmt.Println(a.Name, "Error resetting data dir:", err)
				runtime.Goexit()
			}
		}
	}

	if a.Config.NomadConfig.Bootstrap && a.Config.Server.Enabled {
		testutil.WaitForResult(func() (bool, error) {
			args := &structs.GenericRequest{}
			var leader string
			err := a.RPC("Status.Leader", args, &leader)
			return leader != "", err
		}, func(err error) {
			panic(fmt.Sprintf("failed to find leader: %v", err))
		})
	} else {
		testutil.WaitForResult(func() (bool, error) {
			req, _ := http.NewRequest("GET", "/v1/agent/self", nil)
			resp := httptest.NewRecorder()
			_, err := a.Server.AgentSelfRequest(resp, req)
			return err == nil && resp.Code == 200, err
		}, func(err error) {
			panic(fmt.Sprintf("failed OK response: %v", err))
		})
	}
	return a
}

func (a *TestAgent) start() (*Agent, error) {
	if a.LogOutput == nil {
		a.LogOutput = os.Stderr
	}

	agent, err := NewAgent(a.Config, a.LogOutput)
	if err != nil {
		return nil, err
	}

	// Setup the HTTP server
	http, err := NewHTTPServer(agent, a.Config)
	if err != nil {
		return agent, err
	}

	a.Server = http
	return agent, nil
}

// Shutdown stops the agent and removes the data directory if it is
// managed by the test agent.
func (a *TestAgent) Shutdown() error {
	defer func() {
		if a.DataDir != "" {
			os.RemoveAll(a.DataDir)
		}
	}()

	// shutdown agent before endpoints
	a.Server.Shutdown()
	return a.Agent.Shutdown()
}

func (a *TestAgent) HTTPAddr() string {
	if a.Server == nil {
		return ""
	}
	return a.Server.Addr
}

func (a *TestAgent) Client() *api.Client {
	conf := api.DefaultConfig()
	conf.Address = a.HTTPAddr()
	c, err := api.NewClient(conf)
	if err != nil {
		panic(fmt.Sprintf("Error creating consul API client: %s", err))
	}
	return c
}

// FivePorts returns the first port number of a block of
// five random ports.
func FivePorts() int {
	return 1030 + int(rand.Int31n(6440))*5
}

// pickRandomPorts selects random ports from fixed size random blocks of
// ports. This does not eliminate the chance for port conflict but
// reduces it significanltly with little overhead. Furthermore, asking
// the kernel for a random port by binding to port 0 prolongs the test
// execution (in our case +20sec) while also not fully eliminating the
// chance of port conflicts for concurrently executed test binaries.
// Instead of relying on one set of ports to be sufficient we retry
// starting the agent with different ports on port conflict.
func pickRandomPorts(c *Config) {
	port := FivePorts()
	c.Ports.HTTP = port + 1
	c.Ports.RPC = port + 2
	c.Ports.Serf = port + 3

	if err := c.normalizeAddrs(); err != nil {
		panic(fmt.Sprintf("error normalizing config: %v", err))
	}
}

// TestConfig returns a unique default configuration for testing an
// agent.
func (a *TestAgent) config() *Config {
	conf := DevConfig()

	// Customize the server configuration
	config := nomad.DefaultConfig()
	conf.NomadConfig = config

	// Bind and set ports
	conf.BindAddr = "127.0.0.1"

	conf.Consul = sconfig.DefaultConsulConfig()
	conf.Vault.Enabled = new(bool)

	// Tighten the Serf timing
	config.SerfConfig.MemberlistConfig.SuspicionMult = 2
	config.SerfConfig.MemberlistConfig.RetransmitMult = 2
	config.SerfConfig.MemberlistConfig.ProbeTimeout = 50 * time.Millisecond
	config.SerfConfig.MemberlistConfig.ProbeInterval = 100 * time.Millisecond
	config.SerfConfig.MemberlistConfig.GossipInterval = 100 * time.Millisecond

	// Tighten the Raft timing
	config.RaftConfig.LeaderLeaseTimeout = 20 * time.Millisecond
	config.RaftConfig.HeartbeatTimeout = 40 * time.Millisecond
	config.RaftConfig.ElectionTimeout = 40 * time.Millisecond
	config.RaftConfig.StartAsLeader = true
	config.RaftTimeout = 500 * time.Millisecond

	// Bootstrap ourselves
	config.Bootstrap = true
	config.BootstrapExpect = 1

	if a.ConfigCallback != nil {
		a.ConfigCallback(conf)
	}

	return conf
}
<|endoftext|>


<commit_before>package agent

import (
	"fmt"
	"github.com/hashicorp/serf/serf"
	"github.com/hashicorp/serf/testutil"
	"io"
	"math/rand"
	"net"
	"os"
	"time"
)

func init() {
	// Seed the random number generator
	rand.Seed(time.Now().UnixNano())
}

func drainEventCh(ch <-chan string) {
	for {
		select {
		case <-ch:
		default:
			return
		}
	}
}

func getRPCAddr() string {
	for i := 0; i < 500; i++ {
		l, err := net.Listen("tcp", fmt.Sprintf(":%d", rand.Int31n(25000)+1024))
		if err == nil {
			l.Close()
			return l.Addr().String()
		}
	}

	panic("no listener")
}

func testAgent(logOutput io.Writer) *Agent {
	if logOutput == nil {
		logOutput = os.Stderr
	}
	config := serf.DefaultConfig()
	config.MemberlistConfig.ProbeInterval = 100 * time.Millisecond
	config.MemberlistConfig.BindAddr = testutil.GetBindAddr().String()
	config.NodeName = config.MemberlistConfig.BindAddr

	agent, err := Create(config, logOutput)
	if err != nil {
		panic(err)
	}
	return agent
}

func testAgentWithConfig(config *serf.Config, logOutput io.Writer) *Agent {
	if logOutput == nil {
		logOutput = os.Stderr
	}
	config.MemberlistConfig.ProbeInterval = 100 * time.Millisecond
	config.MemberlistConfig.BindAddr = testutil.GetBindAddr().String()
	config.NodeName = config.MemberlistConfig.BindAddr

	agent, err := Create(config, logOutput)
	if err != nil {
		panic(err)
	}
	return agent
}
<commit_msg>agent: deduplicated testAgent by making it a convenience function to testAgentWithConfig.<commit_after>package agent

import (
	"fmt"
	"github.com/hashicorp/serf/serf"
	"github.com/hashicorp/serf/testutil"
	"io"
	"math/rand"
	"net"
	"os"
	"time"
)

func init() {
	// Seed the random number generator
	rand.Seed(time.Now().UnixNano())
}

func drainEventCh(ch <-chan string) {
	for {
		select {
		case <-ch:
		default:
			return
		}
	}
}

func getRPCAddr() string {
	for i := 0; i < 500; i++ {
		l, err := net.Listen("tcp", fmt.Sprintf(":%d", rand.Int31n(25000)+1024))
		if err == nil {
			l.Close()
			return l.Addr().String()
		}
	}

	panic("no listener")
}

func testAgent(logOutput io.Writer) *Agent {
	return testAgentWithConfig(serf.DefaultConfig(), logOutput)
}

func testAgentWithConfig(config *serf.Config, logOutput io.Writer) *Agent {
	if logOutput == nil {
		logOutput = os.Stderr
	}
	config.MemberlistConfig.ProbeInterval = 100 * time.Millisecond
	config.MemberlistConfig.BindAddr = testutil.GetBindAddr().String()
	config.NodeName = config.MemberlistConfig.BindAddr

	agent, err := Create(config, logOutput)
	if err != nil {
		panic(err)
	}
	return agent
}
<|endoftext|>


<commit_before>package command

import (
	"context"
	"crypto/sha256"
	"fmt"
	"runtime"
	"strings"

	"github.com/hashicorp/go-version"
	pluginsdk "github.com/hashicorp/packer-plugin-sdk/plugin"
	"github.com/hashicorp/packer/hcl2template/addrs"
	"github.com/hashicorp/packer/packer"
	plugingetter "github.com/hashicorp/packer/packer/plugin-getter"
	"github.com/hashicorp/packer/packer/plugin-getter/github"
	pkrversion "github.com/hashicorp/packer/version"
	"github.com/mitchellh/cli"
)

type PluginsInstallCommand struct {
	Meta
}

func (c *PluginsInstallCommand) Synopsis() string {
	return "Install latest Packer plugin [matching version constraint]"
}

func (c *PluginsInstallCommand) Help() string {
	helpText := `
Usage: packer plugins install <plugin> [<version constraint>]

  This command will install the most recent compatible Packer plugin matching
  version constraint.
  When the version constraint is omitted, the most recent version will be
  installed.

  Ex: packer plugins install github.com/hashicorp/happycloud v1.2.3
`

	return strings.TrimSpace(helpText)
}

func (c *PluginsInstallCommand) Run(args []string) int {
	ctx, cleanup := handleTermInterrupt(c.Ui)
	defer cleanup()

	return c.RunContext(ctx, args)
}

func (c *PluginsInstallCommand) RunContext(buildCtx context.Context, args []string) int {
	if len(args) < 1 || len(args) > 2 {
		return cli.RunResultHelp
	}

	opts := plugingetter.ListInstallationsOptions{
		FromFolders: c.Meta.CoreConfig.Components.PluginConfig.KnownPluginFolders,
		BinaryInstallationOptions: plugingetter.BinaryInstallationOptions{
			OS:              runtime.GOOS,
			ARCH:            runtime.GOARCH,
			APIVersionMajor: pluginsdk.APIVersionMajor,
			APIVersionMinor: pluginsdk.APIVersionMinor,
			Checksummers: []plugingetter.Checksummer{
				{Type: "sha256", Hash: sha256.New()},
			},
		},
	}

	plugin, diags := addrs.ParsePluginSourceString(args[0])
	if diags.HasErrors() {
		c.Ui.Error(diags.Error())
		return 1
	}

	// a plugin requirement that matches them all
	pluginRequirement := plugingetter.Requirement{
		Identifier: plugin,
		Implicit:   false,
	}

	if len(args) > 1 {
		constraints, err := version.NewConstraint(args[1])
		if err != nil {
			c.Ui.Error(err.Error())
			return 1
		}
		pluginRequirement.VersionConstraints = constraints
	}

	getters := []plugingetter.Getter{
		&github.Getter{
			// In the past some terraform plugins downloads were blocked from a
			// specific aws region by s3. Changing the user agent unblocked the
			// downloads so having one user agent per version will help mitigate
			// that a little more. Especially in the case someone forks this
			// code to make it more aggressive or something.
			// TODO: allow to set this from the config file or an environment
			// variable.
			UserAgent: "packer-getter-github-" + pkrversion.String(),
		},
	}

	newInstall, err := pluginRequirement.InstallLatest(plugingetter.InstallOptions{
		InFolders:                 opts.FromFolders,
		BinaryInstallationOptions: opts.BinaryInstallationOptions,
		Getters:                   getters,
	})

	if err != nil {
		c.Ui.Error(err.Error())
		return 1
	}

	if newInstall != nil {
		msg := fmt.Sprintf("Installed plugin %s %s in %q", pluginRequirement.Identifier, newInstall.Version, newInstall.BinaryPath)
		ui := &packer.ColoredUi{
			Color: packer.UiColorCyan,
			Ui:    c.Ui,
		}
		ui.Say(msg)
		return 0
	}

	return 0
}
<commit_msg>Set default file extension for binary installations on Windows (#11625)<commit_after>package command

import (
	"context"
	"crypto/sha256"
	"fmt"
	"runtime"
	"strings"

	"github.com/hashicorp/go-version"
	pluginsdk "github.com/hashicorp/packer-plugin-sdk/plugin"
	"github.com/hashicorp/packer/hcl2template/addrs"
	"github.com/hashicorp/packer/packer"
	plugingetter "github.com/hashicorp/packer/packer/plugin-getter"
	"github.com/hashicorp/packer/packer/plugin-getter/github"
	pkrversion "github.com/hashicorp/packer/version"
	"github.com/mitchellh/cli"
)

type PluginsInstallCommand struct {
	Meta
}

func (c *PluginsInstallCommand) Synopsis() string {
	return "Install latest Packer plugin [matching version constraint]"
}

func (c *PluginsInstallCommand) Help() string {
	helpText := `
Usage: packer plugins install <plugin> [<version constraint>]

  This command will install the most recent compatible Packer plugin matching
  version constraint.
  When the version constraint is omitted, the most recent version will be
  installed.

  Ex: packer plugins install github.com/hashicorp/happycloud v1.2.3
`

	return strings.TrimSpace(helpText)
}

func (c *PluginsInstallCommand) Run(args []string) int {
	ctx, cleanup := handleTermInterrupt(c.Ui)
	defer cleanup()

	return c.RunContext(ctx, args)
}

func (c *PluginsInstallCommand) RunContext(buildCtx context.Context, args []string) int {
	if len(args) < 1 || len(args) > 2 {
		return cli.RunResultHelp
	}

	opts := plugingetter.ListInstallationsOptions{
		FromFolders: c.Meta.CoreConfig.Components.PluginConfig.KnownPluginFolders,
		BinaryInstallationOptions: plugingetter.BinaryInstallationOptions{
			OS:              runtime.GOOS,
			ARCH:            runtime.GOARCH,
			APIVersionMajor: pluginsdk.APIVersionMajor,
			APIVersionMinor: pluginsdk.APIVersionMinor,
			Checksummers: []plugingetter.Checksummer{
				{Type: "sha256", Hash: sha256.New()},
			},
		},
	}

	plugin, diags := addrs.ParsePluginSourceString(args[0])
	if diags.HasErrors() {
		c.Ui.Error(diags.Error())
		return 1
	}

	// a plugin requirement that matches them all
	pluginRequirement := plugingetter.Requirement{
		Identifier: plugin,
		Implicit:   false,
	}

	if len(args) > 1 {
		constraints, err := version.NewConstraint(args[1])
		if err != nil {
			c.Ui.Error(err.Error())
			return 1
		}
		pluginRequirement.VersionConstraints = constraints
	}

	if runtime.GOOS == "windows" && opts.Ext == "" {
		opts.BinaryInstallationOptions.Ext = ".exe"
	}

	getters := []plugingetter.Getter{
		&github.Getter{
			// In the past some terraform plugins downloads were blocked from a
			// specific aws region by s3. Changing the user agent unblocked the
			// downloads so having one user agent per version will help mitigate
			// that a little more. Especially in the case someone forks this
			// code to make it more aggressive or something.
			// TODO: allow to set this from the config file or an environment
			// variable.
			UserAgent: "packer-getter-github-" + pkrversion.String(),
		},
	}

	newInstall, err := pluginRequirement.InstallLatest(plugingetter.InstallOptions{
		InFolders:                 opts.FromFolders,
		BinaryInstallationOptions: opts.BinaryInstallationOptions,
		Getters:                   getters,
	})

	if err != nil {
		c.Ui.Error(err.Error())
		return 1
	}

	if newInstall != nil {
		msg := fmt.Sprintf("Installed plugin %s %s in %q", pluginRequirement.Identifier, newInstall.Version, newInstall.BinaryPath)
		ui := &packer.ColoredUi{
			Color: packer.UiColorCyan,
			Ui:    c.Ui,
		}
		ui.Say(msg)
		return 0
	}

	return 0
}
<|endoftext|>


<commit_before>package cony

import (
	"testing"

	"github.com/streadway/amqp"
)

func TestNewClient(t *testing.T) {
	c := NewClient(func(c *Client) {
		c.addr = "test1"
	})

	if c.addr != "test1" {
		t.Error("should call functional opts")
	}
}

func TestClient_Declare(t *testing.T) {
	c := &Client{}

	if len(c.declarations) != 0 {
		t.Error("declarations should be empty")
	}

	q := &Queue{}
	c.Declare([]Declaration{DeclareQueue(q)})

	if len(c.declarations) != 1 {
		t.Error("declarations should have 1 declaration")
	}
}

func TestClient_Consume(t *testing.T) {
	c := NewClient()
	cons := &Consumer{}
	c.Consume(cons)

	if _, ok := c.consumers[cons]; !ok {
		t.Error("should save consumer")
	}

	c.deleteConsumer(cons)

	if _, ok := c.consumers[cons]; ok {
		t.Error("should remove consumer")
	}
}

func TestClient_Publish(t *testing.T) {
	c := NewClient()
	pub := &Publisher{}
	c.Publish(pub)

	if _, ok := c.publishers[pub]; !ok {
		t.Error("should save publisher")
	}

	c.deletePublisher(pub)

	if _, ok := c.publishers[pub]; ok {
		t.Error("should remove publisher")
	}
}

func TestClient_Errors(t *testing.T) {
	c := NewClient()
	errs := c.Errors()

	if errs == nil {
		t.Error("should initialize Errors channel")
	}
}

func TestClient_Close(t *testing.T) {
	c := NewClient()
	c.Close()

	if c.run != noRun {
		t.Error("should stop running")
	}
}

func TestClient_Loop(t *testing.T) {
	c := NewClient()
	c.run = noRun

	if c.Loop() {
		t.Error("should not run if noRun")
	}

	// immitate connection
	c.conn.Store(&amqp.Connection{})
	c.run = run

	if !c.Loop() {
		t.Error("should tell loop to run")
	}
}

func TestURL(t *testing.T) {
	c := &Client{}
	URL("test1")(c)

	if c.addr != "test1" {
		t.Error("should set URL")
	}
}

func TestBackoff(t *testing.T) {
	c := &Client{}
	Backoff(DefaultBackoff)(c)

	if c.bo == nil {
		t.Error("should set backoff")
	}
}
<commit_msg>more tests for client<commit_after>package cony

import (
	"errors"
	"testing"

	"github.com/streadway/amqp"
)

func TestNewClient(t *testing.T) {
	c := NewClient(func(c *Client) {
		c.addr = "test1"
	})

	if c.addr != "test1" {
		t.Error("should call functional opts")
	}
}

func TestClient_Declare(t *testing.T) {
	c := &Client{}

	if len(c.declarations) != 0 {
		t.Error("declarations should be empty")
	}

	q := &Queue{}
	c.Declare([]Declaration{DeclareQueue(q)})

	if len(c.declarations) != 1 {
		t.Error("declarations should have 1 declaration")
	}
}

func TestClient_Consume(t *testing.T) {
	c := NewClient()
	cons := &Consumer{}
	c.Consume(cons)

	if _, ok := c.consumers[cons]; !ok {
		t.Error("should save consumer")
	}

	c.deleteConsumer(cons)

	if _, ok := c.consumers[cons]; ok {
		t.Error("should remove consumer")
	}
}

func TestClient_Publish(t *testing.T) {
	c := NewClient()
	pub := &Publisher{}
	c.Publish(pub)

	if _, ok := c.publishers[pub]; !ok {
		t.Error("should save publisher")
	}

	c.deletePublisher(pub)

	if _, ok := c.publishers[pub]; ok {
		t.Error("should remove publisher")
	}
}

func TestClient_Errors(t *testing.T) {
	c := NewClient()
	errs := c.Errors()

	if errs == nil {
		t.Error("should initialize Errors channel")
	}
}

func TestClient_Close(t *testing.T) {
	c := NewClient()
	c.Close()

	if c.run != noRun {
		t.Error("should stop running")
	}
}

func TestClient_Loop(t *testing.T) {
	c := NewClient()
	c.run = noRun

	if c.Loop() {
		t.Error("should not run if noRun")
	}

	// immitate connection
	c.conn.Store(&amqp.Connection{})
	c.run = run

	if !c.Loop() {
		t.Error("should tell loop to run")
	}
}

func TestClient_reportErr(t *testing.T) {
	c := NewClient()

	if c.reportErr(nil) {
		t.Error("should return false on no error")
	}

	// fill in errs buffer
	for i := 0; i < 100; i++ {
		c.reportErr(errors.New("test err"))
	}

	// should not block, error will be discarded
	if !c.reportErr(errors.New("test err")) {
		t.Error("should return true")
	}
}

func TestClient_channel(t *testing.T) {}

func TestClient_connection(t *testing.T) {
	c := NewClient()

	if _, err := c.connection(); err != ErrNoConnection {
		t.Error("error should be", ErrNoConnection)
	}

	c.conn.Store(&amqp.Connection{})

	con, err := c.connection()
	if con == nil {
		t.Error("should return existing connection")
	}

	if err != nil {
		t.Error("should be no errors")
	}
}

func TestURL(t *testing.T) {
	c := &Client{}
	URL("test1")(c)

	if c.addr != "test1" {
		t.Error("should set URL")
	}

	URL("")(c)
	if c.addr != "amqp://guest:guest@localhost/" {
		t.Error("should use default URL")
	}
}

func TestBackoff(t *testing.T) {
	c := &Client{}
	Backoff(DefaultBackoff)(c)

	if c.bo == nil {
		t.Error("should set backoff")
	}
}
<|endoftext|>


<commit_before>package chapi_test

import (
	"encoding/json"
	"log"
	"os"

	"github.com/jimsmart/chapi"

	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
	. "github.com/onsi/gomega/gstruct"
)

var _ = Describe("Client", func() {

	apiKey := os.Getenv("COMPANIES_HOUSE_API_KEY")
	It("needs an API key", func() {
		Expect(apiKey).ToNot(Equal(""))
	})

	Context("a new client with a specified API key", func() {

		ch := chapi.NewClientWithKey(apiKey)

		It("should have a non-nil RESTClient", func() {
			Expect(ch.RESTClient).ToNot(BeNil())
		})
		It("should have an API key in its RESTClient", func() {
			Expect(ch.RESTClient.APIKey).ToNot(Equal(""))
		})
		It("should be able to Search() without error", func() {
			_, err := ch.Search("Richard Branson", 1, -1)
			Expect(err).To(BeNil())
		})
	})

	Context("a new client using the default package-level APIKey", func() {

		chapi.APIKey = apiKey
		ch := chapi.NewClient()

		It("should have a non-nil RESTClient", func() {
			Expect(ch.RESTClient).ToNot(BeNil())
		})
		It("should be able to Search() without error", func() {
			_, err := ch.Search("Richard Branson", 1, -1)
			Expect(err).To(BeNil())
		})
	})

	ch := chapi.NewClientWithKey(apiKey)

	Context("when calling Search(), asking for 10 results", func() {

		res, err := ch.Search("Richard Branson", 10, -1)

		It("should not return an error", func() {
			Expect(err).To(BeNil())
		})
		It("should return 10 results", func() {
			Expect(res.ItemsPerPage).To(Equal(10))
			Expect(res.StartIndex).To(Equal(0))
		})
		It("should return an expected result", func() {
			Expect(res.Items).To(ContainElement(MatchFields(IgnoreExtras, Fields{
				"Kind":  Equal("searchresults#officer"),
				"Title": Equal("Sir Richard Charles Nicholas BRANSON"),
				"Links": MatchAllFields(Fields{
					"Self": Not(Equal("")),
				}),
			})))
		})
		// TODO(js) We seem to be missing the ID extractors ...?
	})

	Context("when calling SearchCompanies(), asking for 5 results", func() {

		res, err := ch.SearchCompanies("Facebook UK Ltd", 5, -1)

		It("should not return an error", func() {
			Expect(err).To(BeNil())
		})
		It("should return 5 results", func() {
			Expect(res.ItemsPerPage).To(Equal(5))
			Expect(res.StartIndex).To(Equal(0))
		})
		It("should return an expected result", func() {
			Expect(*res).To(MatchFields(IgnoreExtras, Fields{
				"Kind": Equal("search#companies"),
				"Items": ContainElement(MatchFields(IgnoreExtras, Fields{
					"Title":         Equal("FACEBOOK UK LTD"),
					"CompanyNumber": Equal("06331310"),
					"Links": MatchAllFields(Fields{
						"Self": Not(Equal("")),
					}),
				})),
			}))
		})
		// TODO(js) We seem to be missing the ID extractors ...?
	})

	Context("when calling SearchOfficers(), asking for 10 results", func() {

		res, err := ch.SearchOfficers("Richard Branson", 10, -1)

		It("should not return an error", func() {
			Expect(err).To(BeNil())
		})
		It("should return 10 results", func() {
			Expect(res.ItemsPerPage).To(Equal(10))
			Expect(res.StartIndex).To(Equal(0))
		})
		It("should return an expected result", func() {
			Expect(res.Items).To(ContainElement(MatchFields(IgnoreExtras, Fields{
				"Kind":  Equal("searchresults#officer"),
				"Title": Equal("Sir Richard Charles Nicholas BRANSON"),
				"DateOfBirth": MatchAllFields(Fields{
					"Month": Equal(7),
					"Year":  Equal(1950),
				}),
				"Links": MatchAllFields(Fields{
					"Self": Not(Equal("")),
				}),
			})))
		})

		// TODO(js) We seem to be missing the ID extractors ...?
	})

	Context("when calling SearchDisqualifiedOfficers(), asking for 10 results", func() {

		res, err := ch.SearchDisqualifiedOfficers("John Smith", 10, -1)

		It("should not return an error", func() {
			Expect(err).To(BeNil())
		})
		It("should return 10 results", func() {
			Expect(res.ItemsPerPage).To(Equal(10))
			Expect(res.StartIndex).To(Equal(0))
		})
		It("should return some results", func() {
			Expect(res.Items).To(ContainElement(MatchFields(IgnoreExtras, Fields{
				"Kind": Equal("searchresults#disqualified-officer"),
			})))
		})

		// TODO(js) We seem to be missing the ID extractors ...?
	})

	Context("when calling CompanyProfile()", func() {

		res, err := ch.CompanyProfile("06331310")

		It("should not return an error", func() {
			Expect(err).To(BeNil())
		})
		It("should return the expected result", func() {
			Expect(*res).To(MatchFields(IgnoreExtras, Fields{
				"CompanyName":   Equal("FACEBOOK UK LTD"),
				"CompanyNumber": Equal("06331310"),
				"Links": MatchFields(IgnoreExtras, Fields{
					"Self":          Not(Equal("")),
					"FilingHistory": Not(Equal("")),
					"Officers":      Not(Equal("")),
				}),
			}))
		})
	})

	Context("when calling CompanyRegisteredOfficeAddress()", func() {

		res, err := ch.CompanyRegisteredOfficeAddress("06331310")

		It("should not return an error", func() {
			Expect(err).To(BeNil())
		})
		It("should return the expected result", func() {
			Expect(*res).To(MatchFields(IgnoreExtras, Fields{
				"AddressLine1": Equal("10 Brock Street"),
				"AddressLine2": Equal("Regent's Place"),
				"Locality":     Equal("London"),
				"PostalCode":   Equal("NW1 3FG"),
				"Country":      Equal("England"),
			}))
		})
	})

	Context("when calling CompanyOfficers()", func() {

		res, err := ch.CompanyOfficers("02627406", "", "", -1, -1)

		It("should not return an error", func() {
			Expect(err).To(BeNil())
		})
		It("should return the expected result", func() {

			Expect(*res).To(MatchFields(IgnoreExtras, Fields{
				"Links": MatchAllFields(Fields{
					"Self": Not(Equal("")),
				}),
				"Items": ContainElement(MatchFields(IgnoreExtras, Fields{
					"Name": Equal("DYSON, James, Sir"),
					"DateOfBirth": MatchFields(IgnoreExtras, Fields{
						"Month": Equal(5),
						"Year":  Equal(1947),
					}),
					"Nationality": Equal("British"),
					"Occupation":  Equal("Designer"),
					"OfficerRole": Equal("director"),
					"ResignedOn":  Equal("2010-06-18"),
					"Links": MatchAllFields(Fields{
						"Officer": MatchAllFields(Fields{
							"Appointments": Not(Equal("")),
						}),
					}),
				})),
			}))
		})
	})

	Context("when calling CompanyFilingHistory(), asking for 10 results", func() {

		res, err := ch.CompanyFilingHistory("02627406", "", 10, -1)

		It("should not return an error", func() {
			Expect(err).To(BeNil())
		})
		It("should return 10 results", func() {
			Expect(res.ItemsPerPage).To(Equal(10))
			Expect(res.StartIndex).To(Equal(0))
		})
		It("should return the expected result", func() {
			Expect(res.Items).To(ContainElement(MatchFields(IgnoreExtras, Fields{
				"Type":          Equal("AA"),
				"Category":      Equal("accounts"),
				"Description":   Equal("accounts-with-accounts-type-full"),
				"PaperFiled":    Equal(true),
				"Date":          Not(Equal("")),
				"TransactionID": Not(Equal("")),
				"Links": MatchAllFields(Fields{
					"Self":             Not(Equal("")),
					"DocumentMetadata": Not(Equal("")),
				}),
			})))
		})
	})

	Context("when calling OfficerAppointments(), asking for 10 results", func() {

		res, err := ch.OfficerAppointments("fPsul1-gLgzfRlgRvGBL14iNV3c", -1, -1)

		It("should not return an error", func() {
			Expect(err).To(BeNil())
		})

		It("should return an expected result", func() {
			Expect(*res).To(MatchFields(IgnoreExtras, Fields{
				"Kind": Equal("personal-appointment"),
				"Name": Equal("Richard Charles Nicholas BRANSON"),
				"DateOfBirth": MatchAllFields(Fields{
					"Month": Equal(7),
					"Year":  Equal(1950),
				}),
				"Items": ContainElement(MatchFields(IgnoreExtras, Fields{
					"Occupation": Equal("Company Director"),
					"AppointedTo": MatchFields(IgnoreExtras, Fields{
						"CompanyName": ContainSubstring("VIRGIN"),
					}),
				})),
			}))
		})

		// TODO(js) We seem to be missing the ID extractors ...?
	})

})

func printJSON(v interface{}) {
	b, _ := json.MarshalIndent(v, "", "   ")
	log.Println(string(b))
}
<commit_msg>Edits for clarity<commit_after>package chapi_test

import (
	"encoding/json"
	"log"
	"os"

	"github.com/jimsmart/chapi"

	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
	. "github.com/onsi/gomega/gstruct"
)

var _ = Describe("Client", func() {

	apiKey := os.Getenv("COMPANIES_HOUSE_API_KEY")
	It("needs an API key", func() {
		Expect(apiKey).ToNot(Equal(""))
	})

	Context("a new client with a specified API key", func() {

		ch := chapi.NewClientWithKey(apiKey)

		It("should have a non-nil RESTClient", func() {
			Expect(ch.RESTClient).ToNot(BeNil())
		})
		It("should have an API key in its RESTClient", func() {
			Expect(ch.RESTClient.APIKey).ToNot(Equal(""))
		})
		It("should be able to Search() without error", func() {
			_, err := ch.Search("Richard Branson", 1, -1)
			Expect(err).To(BeNil())
		})
	})

	Context("a new client using the default package-level APIKey", func() {

		chapi.APIKey = apiKey
		ch := chapi.NewClient()

		It("should have a non-nil RESTClient", func() {
			Expect(ch.RESTClient).ToNot(BeNil())
		})
		It("should be able to Search() without error", func() {
			_, err := ch.Search("Richard Branson", 1, -1)
			Expect(err).To(BeNil())
		})
	})

	ch := chapi.NewClientWithKey(apiKey)

	Context("when calling Search(), asking for 10 results", func() {

		res, err := ch.Search("Richard Branson", 10, -1)

		It("should not return an error", func() {
			Expect(err).To(BeNil())
		})
		It("should return 10 results", func() {
			Expect(res.ItemsPerPage).To(Equal(10))
			Expect(res.StartIndex).To(Equal(0))
		})
		It("should return an expected result", func() {
			Expect(res.Items).To(ContainElement(MatchFields(IgnoreExtras, Fields{
				"Kind":  Equal("searchresults#officer"),
				"Title": Equal("Sir Richard Charles Nicholas BRANSON"),
				"Links": MatchAllFields(Fields{
					"Self": Not(Equal("")),
				}),
			})))
		})
		// TODO(js) We seem to be missing the ID extractors ...?
	})

	Context("when calling SearchCompanies(), asking for 5 results", func() {

		res, err := ch.SearchCompanies("Facebook UK Ltd", 5, -1)

		It("should not return an error", func() {
			Expect(err).To(BeNil())
		})
		It("should return 5 results", func() {
			Expect(res.ItemsPerPage).To(Equal(5))
			Expect(res.StartIndex).To(Equal(0))
		})
		It("should return an expected result", func() {
			Expect(*res).To(MatchFields(IgnoreExtras, Fields{
				"Kind": Equal("search#companies"),
				"Items": ContainElement(MatchFields(IgnoreExtras, Fields{
					"Title":         Equal("FACEBOOK UK LTD"),
					"CompanyNumber": Equal("06331310"),
					"Links": MatchAllFields(Fields{
						"Self": Not(Equal("")),
					}),
				})),
			}))
		})
		// TODO(js) We seem to be missing the ID extractors ...?
	})

	Context("when calling SearchOfficers(), asking for 10 results", func() {

		res, err := ch.SearchOfficers("Richard Branson", 10, -1)

		It("should not return an error", func() {
			Expect(err).To(BeNil())
		})
		It("should return 10 results", func() {
			Expect(res.ItemsPerPage).To(Equal(10))
			Expect(res.StartIndex).To(Equal(0))
		})
		It("should return an expected result", func() {
			Expect(res.Items).To(ContainElement(MatchFields(IgnoreExtras, Fields{
				"Kind":  Equal("searchresults#officer"),
				"Title": Equal("Sir Richard Charles Nicholas BRANSON"),
				"DateOfBirth": MatchAllFields(Fields{
					"Month": Equal(7),
					"Year":  Equal(1950),
				}),
				"Links": MatchAllFields(Fields{
					"Self": Not(Equal("")),
				}),
			})))
		})

		// TODO(js) We seem to be missing the ID extractors ...?
	})

	Context("when calling SearchDisqualifiedOfficers(), asking for 10 results", func() {

		res, err := ch.SearchDisqualifiedOfficers("John Smith", 10, -1)

		It("should not return an error", func() {
			Expect(err).To(BeNil())
		})
		It("should return 10 results", func() {
			Expect(res.ItemsPerPage).To(Equal(10))
			Expect(res.StartIndex).To(Equal(0))
		})
		It("should return some results", func() {
			Expect(res.Items).To(ContainElement(MatchFields(IgnoreExtras, Fields{
				"Kind": Equal("searchresults#disqualified-officer"),
			})))
		})

		// TODO(js) We seem to be missing the ID extractors ...?
	})

	Context("when calling CompanyProfile()", func() {

		res, err := ch.CompanyProfile("06331310")

		It("should not return an error", func() {
			Expect(err).To(BeNil())
		})
		It("should return the expected result", func() {
			Expect(*res).To(MatchFields(IgnoreExtras, Fields{
				"CompanyName":   Equal("FACEBOOK UK LTD"),
				"CompanyNumber": Equal("06331310"),
				"Links": MatchFields(IgnoreExtras, Fields{
					"Self":          Not(Equal("")),
					"FilingHistory": Not(Equal("")),
					"Officers":      Not(Equal("")),
				}),
			}))
		})
	})

	Context("when calling CompanyRegisteredOfficeAddress()", func() {

		res, err := ch.CompanyRegisteredOfficeAddress("06331310")

		It("should not return an error", func() {
			Expect(err).To(BeNil())
		})
		It("should return the expected result", func() {
			Expect(*res).To(MatchFields(IgnoreExtras, Fields{
				"AddressLine1": Equal("10 Brock Street"),
				"AddressLine2": Equal("Regent's Place"),
				"Locality":     Equal("London"),
				"PostalCode":   Equal("NW1 3FG"),
				"Country":      Equal("England"),
			}))
		})
	})

	Context("when calling CompanyOfficers()", func() {

		res, err := ch.CompanyOfficers("02627406", "", "", -1, -1)

		It("should not return an error", func() {
			Expect(err).To(BeNil())
		})
		It("should return the expected result", func() {

			Expect(*res).To(MatchFields(IgnoreExtras, Fields{
				"Links": MatchAllFields(Fields{
					"Self": Not(Equal("")),
				}),
				"Items": ContainElement(MatchFields(IgnoreExtras, Fields{
					"Name": Equal("DYSON, James, Sir"),
					"DateOfBirth": MatchFields(IgnoreExtras, Fields{
						"Month": Equal(5),
						"Year":  Equal(1947),
					}),
					"Nationality": Equal("British"),
					"Occupation":  Equal("Designer"),
					"OfficerRole": Equal("director"),
					"ResignedOn":  Equal("2010-06-18"),
					"Links": MatchAllFields(Fields{
						"Officer": MatchAllFields(Fields{
							"Appointments": Not(Equal("")),
						}),
					}),
				})),
			}))
		})
	})

	Context("when calling CompanyFilingHistory(), asking for 10 results", func() {

		res, err := ch.CompanyFilingHistory("02627406", "", 10, -1)

		printJSON(res)

		It("should not return an error", func() {
			Expect(err).To(BeNil())
		})
		It("should return 10 results", func() {
			Expect(res.ItemsPerPage).To(Equal(10))
			Expect(res.StartIndex).To(Equal(0))
		})
		It("should return the expected result", func() {
			Expect(res.Items).To(ContainElement(MatchFields(IgnoreExtras, Fields{
				"Type":          Equal("AA"),
				"Category":      Equal("accounts"),
				"Description":   Equal("accounts-with-accounts-type-full"),
				"PaperFiled":    Equal(true),
				"Date":          Not(Equal("")),
				"TransactionID": Not(Equal("")),
				"Links": MatchAllFields(Fields{
					"Self":             Not(Equal("")),
					"DocumentMetadata": Not(Equal("")),
				}),
			})))
		})
	})

	Context("when calling CompanyFilingHistoryTransaction()", func() {

		res, err := ch.CompanyFilingHistoryTransaction("02627406", "")

		It("should not return an error", func() {
			Expect(err).To(BeNil())
		})
		It("should return the expected result", func() {
			Expect(res.Items).To(ContainElement(MatchFields(IgnoreExtras, Fields{
				"Type":          Equal("AA"),
				"Category":      Equal("accounts"),
				"Description":   Equal("accounts-with-accounts-type-full"),
				"PaperFiled":    Equal(true),
				"Date":          Not(Equal("")),
				"TransactionID": Not(Equal("")),
				"Links": MatchAllFields(Fields{
					"Self":             Not(Equal("")),
					"DocumentMetadata": Not(Equal("")),
				}),
			})))
		})
	})

	Context("when calling OfficerAppointments(), asking for 10 results", func() {

		res, err := ch.OfficerAppointments("fPsul1-gLgzfRlgRvGBL14iNV3c", -1, -1)

		It("should not return an error", func() {
			Expect(err).To(BeNil())
		})

		It("should return an expected result", func() {
			Expect(*res).To(MatchFields(IgnoreExtras, Fields{
				"Kind": Equal("personal-appointment"),
				"Name": Equal("Richard Charles Nicholas BRANSON"),
				"DateOfBirth": MatchAllFields(Fields{
					"Month": Equal(7),
					"Year":  Equal(1950),
				}),
				"Items": ContainElement(MatchFields(IgnoreExtras, Fields{
					"Occupation": Equal("Company Director"),
					"AppointedTo": MatchFields(IgnoreExtras, Fields{
						"CompanyName": ContainSubstring("VIRGIN"),
					}),
				})),
			}))
		})

		// TODO(js) We seem to be missing the ID extractors ...?
	})

})

func printJSON(v interface{}) {
	b, _ := json.MarshalIndent(v, "", "   ")
	log.Println(string(b))
}
<|endoftext|>


<commit_before>// client_test.go - Katzenpost client library tests.
// Copyright (C) 2019  David Stainton.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

// Package client provides a Katzenpost client library.
package client

import (
	"testing"
	"time"

	"github.com/katzenpost/core/epochtime"
	"github.com/katzenpost/kimchi"
	"github.com/stretchr/testify/assert"
)

const basePort = 30000

// TestClientConnect tests that a client can connect and send a message to the loop service
func TestClientConnect(t *testing.T) {
	t.Parallel()
	assert := assert.New(t)
	voting := true
	nVoting := 3
	nProvider := 2
	nMix := 6
	k := kimchi.NewKimchi(basePort+400, "", nil, voting, nVoting, nProvider, nMix)
	t.Logf("Running TestClientConnect.")
	k.Run()

	go func() {
		defer k.Shutdown()
		_, _, till := epochtime.Now()
		till += epochtime.Period // wait for one vote round, aligned at start of epoch
		<-time.After(till)
		t.Logf("Time is up!")

		// create a client configuration
		cfg, username, linkKey, err := k.GetClientConfig()
		assert.NoError(err)

		// instantiate a client instance
		c, err := New(cfg)
		assert.NoError(err)

		// add client log output
		go k.LogTailer(username, cfg.Logging.File)

		// instantiate a session
		s, err := c.NewSession(username, linkKey)
		assert.NoError(err)

		// get a PKI document? needs client method...
		desc, err := s.GetService("loop") // XXX: returns nil and no error?!
		assert.NoError(err)

		// send a message
		t.Logf("desc.Provider: %s", desc.Provider)
		_, err = s.SendUnreliableMessage(desc.Name, desc.Provider, []byte("hello!"))
		assert.NoError(err)

		c.Shutdown()
		c.Wait()
	}()

	k.Wait()
	t.Logf("Terminated.")
}
<commit_msg>fix NewSession usage and use nonvoting authority for tests<commit_after>// client_test.go - Katzenpost client library tests.
// Copyright (C) 2019  David Stainton.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

// Package client provides a Katzenpost client library.
package client

import (
	"testing"
	"time"

	"github.com/katzenpost/kimchi"
	"github.com/stretchr/testify/require"
)

const basePort = 30000

// TestClientConnect tests that a client can connect and send a message to the loop service
func TestClientConnect(t *testing.T) {
	t.Parallel()
	require := require.New(t)
	voting := false
	nVoting := 0
	nProvider := 2
	nMix := 6
	k := kimchi.NewKimchi(basePort+400, "", nil, voting, nVoting, nProvider, nMix)
	t.Logf("Running TestClientConnect.")
	k.Run()

	go func() {
		defer k.Shutdown()

		// create a client configuration
		cfg, username, linkKey, err := k.GetClientConfig()
		require.NoError(err)
		require.NotNil(cfg)

		<-time.After(90 * time.Second) // must wait for provider to fetch pki document
		t.Logf("Time is up!")

		// instantiate a client instance
		c, err := New(cfg)
		require.NotNil(cfg)
		require.NoError(err)

		// add client log output
		go k.LogTailer(username, cfg.Logging.File)

		// instantiate a session
		s, err := c.NewSession(linkKey)
		require.NoError(err)

		// get a PKI document? needs client method...
		desc, err := s.GetService("loop") // XXX: returns nil and no error?!
		require.NoError(err)

		// send a message
		t.Logf("desc.Provider: %s", desc.Provider)
		_, err = s.SendUnreliableMessage(desc.Name, desc.Provider, []byte("hello!"))
		t.Logf("Sent unreliable message to loop service")
		require.NoError(err)

		c.Shutdown()
		t.Logf("Shutdown requested")
		c.Wait()
	}()

	k.Wait()
	t.Logf("Terminated.")
}
<|endoftext|>


<commit_before>// Copyright 2019 Google LLC
//
// 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
//
// 		https://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 lint

import (
	"regexp"
	"strings"

	dpb "github.com/golang/protobuf/protoc-gen-go/descriptor"
	"github.com/jhump/protoreflect/desc"
)

// ProtoRule defines a lint rule that checks Google Protobuf APIs.
//
// Anything that satisfies this interface can be used as a rule,
// but most rule authors will want to use the implementations provided.
type ProtoRule interface {
	// GetName returns the name of the rule.
	GetName() RuleName

	// Lint accepts a FileDescriptor and lints it,
	// returning a slice of Problem objects it finds.
	Lint(*desc.FileDescriptor) []Problem
}

// FileRule defines a lint rule that checks a file as a whole.
type FileRule struct {
	Name RuleName

	// LintFile accepts a FileDescriptor and lints it, returning a slice of
	// Problems it finds.
	LintFile func(*desc.FileDescriptor) []Problem

	// OnlyIf accepts a FileDescriptor and determines whether this rule
	// is applicable.
	OnlyIf func(*desc.FileDescriptor) bool

	// nolint:structcheck,unused
	noPositional struct{}
}

// GetName returns the name of the rule.
func (r *FileRule) GetName() RuleName {
	return r.Name
}

// Lint forwards the FileDescriptor to the LintFile method defined on the
// FileRule.
func (r *FileRule) Lint(fd *desc.FileDescriptor) []Problem {
	if r.OnlyIf == nil || r.OnlyIf(fd) {
		return r.LintFile(fd)
	}
	return nil
}

// MessageRule defines a lint rule that is run on each message in the file.
//
// Both top-level messages and nested messages are visited.
type MessageRule struct {
	Name RuleName

	// LintMessage accepts a MessageDescriptor and lints it, returning a slice
	// of Problems it finds.
	LintMessage func(*desc.MessageDescriptor) []Problem

	// OnlyIf accepts a MessageDescriptor and determines whether this rule
	// is applicable.
	OnlyIf func(*desc.MessageDescriptor) bool

	// nolint:structcheck,unused
	noPositional struct{}
}

// GetName returns the name of the rule.
func (r *MessageRule) GetName() RuleName {
	return r.Name
}

// Lint visits every message in the file, and runs `LintMessage`.
//
// If an `OnlyIf` function is provided on the rule, it is run against each
// message, and if it returns false, the `LintMessage` function is not called.
func (r *MessageRule) Lint(fd *desc.FileDescriptor) []Problem {
	problems := []Problem{}

	// Iterate over each message and process rules for each message.
	for _, message := range getAllMessages(fd) {
		if r.OnlyIf == nil || r.OnlyIf(message) {
			problems = append(problems, r.LintMessage(message)...)
		}
	}
	return problems
}

// FieldRule defines a lint rule that is run on each field within a file.
type FieldRule struct {
	Name RuleName

	// LintField accepts a FieldDescriptor and lints it, returning a slice of
	// Problems it finds.
	LintField func(*desc.FieldDescriptor) []Problem

	// OnlyIf accepts a FieldDescriptor and determines whether this rule
	// is applicable.
	OnlyIf func(*desc.FieldDescriptor) bool

	// nolint:structcheck,unused
	noPositional struct{}
}

// GetName returns the name of the rule.
func (r *FieldRule) GetName() RuleName {
	return r.Name
}

// Lint visits every field in the file and runs `LintField`.
//
// If an `OnlyIf` function is provided on the rule, it is run against each
// field, and if it returns false, the `LintField` function is not called.
func (r *FieldRule) Lint(fd *desc.FileDescriptor) []Problem {
	problems := []Problem{}

	// Iterate over each message and process rules for each field in that
	// message.
	for _, message := range getAllMessages(fd) {
		for _, field := range message.GetFields() {
			if r.OnlyIf == nil || r.OnlyIf(field) {
				problems = append(problems, r.LintField(field)...)
			}
		}
	}
	return problems
}

// ServiceRule defines a lint rule that is run on each service.
type ServiceRule struct {
	Name RuleName

	// LintService accepts a ServiceDescriptor and lints it.
	LintService func(*desc.ServiceDescriptor) []Problem

	// OnlyIf accepts a ServiceDescriptor and determines whether this rule
	// is applicable.
	OnlyIf func(*desc.ServiceDescriptor) bool

	// nolint:structcheck,unused
	noPositional struct{}
}

// GetName returns the name of the rule.
func (r *ServiceRule) GetName() RuleName {
	return r.Name
}

// Lint visits every service in the file and runs `LintService`.
//
// If an `OnlyIf` function is provided on the rule, it is run against each
// service, and if it returns false, the `LintService` function is not called.
func (r *ServiceRule) Lint(fd *desc.FileDescriptor) []Problem {
	problems := []Problem{}
	for _, service := range fd.GetServices() {
		if r.OnlyIf == nil || r.OnlyIf(service) {
			problems = append(problems, r.LintService(service)...)
		}
	}
	return problems
}

// MethodRule defines a lint rule that is run on each method.
type MethodRule struct {
	Name RuleName

	// LintMethod accepts a MethodDescriptor and lints it.
	LintMethod func(*desc.MethodDescriptor) []Problem

	// OnlyIf accepts a MethodDescriptor and determines whether this rule
	// is applicable.
	OnlyIf func(*desc.MethodDescriptor) bool

	// nolint:structcheck,unused
	noPositional struct{}
}

// GetName returns the name of the rule.
func (r *MethodRule) GetName() RuleName {
	return r.Name
}

// Lint visits every method in the file and runs `LintMethod`.
//
// If an `OnlyIf` function is provided on the rule, it is run against each
// method, and if it returns false, the `LintMethod` function is not called.
func (r *MethodRule) Lint(fd *desc.FileDescriptor) []Problem {
	problems := []Problem{}
	for _, service := range fd.GetServices() {
		for _, method := range service.GetMethods() {
			if r.OnlyIf == nil || r.OnlyIf(method) {
				problems = append(problems, r.LintMethod(method)...)
			}
		}
	}
	return problems
}

// EnumRule defines a lint rule that is run on each enum.
type EnumRule struct {
	Name RuleName

	// LintEnum accepts a EnumDescriptor and lints it.
	LintEnum func(*desc.EnumDescriptor) []Problem

	// OnlyIf accepts an EnumDescriptor and determines whether this rule
	// is applicable.
	OnlyIf func(*desc.EnumDescriptor) bool

	// nolint:structcheck,unused
	noPositional struct{}
}

// GetName returns the name of the rule.
func (r *EnumRule) GetName() RuleName {
	return r.Name
}

// Lint visits every enum in the file and runs `LintEnum`.
//
// If an `OnlyIf` function is provided on the rule, it is run against each
// enum, and if it returns false, the `LintEnum` function is not called.
func (r *EnumRule) Lint(fd *desc.FileDescriptor) []Problem {
	problems := []Problem{}

	// Lint all enums, either at the top of the file, or nested within messages.
	for _, enum := range getAllEnums(fd) {
		if r.OnlyIf == nil || r.OnlyIf(enum) {
			problems = append(problems, r.LintEnum(enum)...)
		}
	}
	return problems
}

// EnumValueRule defines a lint rule that is run on each enum value.
type EnumValueRule struct {
	Name RuleName

	// LintEnumValue accepts a EnumValueDescriptor and lints it.
	LintEnumValue func(*desc.EnumValueDescriptor) []Problem

	// OnlyIf accepts an EnumValueDescriptor and determines whether this rule
	// is applicable.
	OnlyIf func(*desc.EnumValueDescriptor) bool

	// nolint:structcheck,unused
	noPositional struct{}
}

// GetName returns the name of the rule.
func (r *EnumValueRule) GetName() RuleName {
	return r.Name
}

// Lint visits every enum value in the file and runs `LintEnum`.
//
// If an `OnlyIf` function is provided on the rule, it is run against each
// enum value, and if it returns false, the `LintEnum` function is not called.
func (r *EnumValueRule) Lint(fd *desc.FileDescriptor) []Problem {
	problems := []Problem{}

	// Lint all enums, either at the top of the file, or nested within messages.
	for _, enum := range getAllEnums(fd) {
		for _, value := range enum.GetValues() {
			if r.OnlyIf == nil || r.OnlyIf(value) {
				problems = append(problems, r.LintEnumValue(value)...)
			}
		}
	}
	return problems
}

// DescriptorRule defines a lint rule that is run on every descriptor
// in the file (but not the file itself).
type DescriptorRule struct {
	Name RuleName

	// LintDescriptor accepts a generic descriptor and lints it.
	//
	// Note: Unless the descriptor is typecast to a more specific type,
	// only a subset of methods are available to it.
	LintDescriptor func(desc.Descriptor) []Problem

	// OnlyIf accepts a Descriptor and determines whether this rule
	// is applicable.
	OnlyIf func(desc.Descriptor) bool

	// nolint:structcheck,unused
	noPositional struct{}
}

// GetName returns the name of the rule.
func (r *DescriptorRule) GetName() RuleName {
	return r.Name
}

// Lint visits every descriptor in the file and runs `LintDescriptor`.
//
// It visits every service, method, message, field, enum, and enum value.
// This order is not guaranteed. It does NOT visit the file itself.
func (r *DescriptorRule) Lint(fd *desc.FileDescriptor) []Problem {
	problems := []Problem{}

	// Iterate over all services and methods.
	for _, service := range fd.GetServices() {
		if r.OnlyIf == nil || r.OnlyIf(service) {
			problems = append(problems, r.LintDescriptor(service)...)
		}
		for _, method := range service.GetMethods() {
			if r.OnlyIf == nil || r.OnlyIf(method) {
				problems = append(problems, r.LintDescriptor(method)...)
			}
		}
	}

	// Iterate over all messages, and all fields within each message.
	for _, message := range getAllMessages(fd) {
		if r.OnlyIf == nil || r.OnlyIf(message) {
			problems = append(problems, r.LintDescriptor(message)...)
		}
		for _, field := range message.GetFields() {
			if r.OnlyIf == nil || r.OnlyIf(field) {
				problems = append(problems, r.LintDescriptor(field)...)
			}
		}
	}

	// Iterate over all enums and enum values.
	for _, enum := range getAllEnums(fd) {
		if r.OnlyIf == nil || r.OnlyIf(enum) {
			problems = append(problems, r.LintDescriptor(enum)...)
		}
		for _, value := range enum.GetValues() {
			if r.OnlyIf == nil || r.OnlyIf(value) {
				problems = append(problems, r.LintDescriptor(value)...)
			}
		}
	}

	// Done; return the full set of problems.
	return problems
}

var disableRuleNameRegex = regexp.MustCompile(`api-linter:\s*(.+)\s*=\s*disabled`)

func extractDisabledRuleName(commentLine string) string {
	match := disableRuleNameRegex.FindStringSubmatch(commentLine)
	if len(match) > 0 {
		return match[1]
	}
	return ""
}

func getLeadingComments(d desc.Descriptor) string {
	if sourceInfo := d.GetSourceInfo(); sourceInfo != nil {
		return sourceInfo.GetLeadingComments()
	}
	return ""
}

// getAllMessages returns a slice with every message (not just top-level
// messages) in the file.
func getAllMessages(f *desc.FileDescriptor) (messages []*desc.MessageDescriptor) {
	messages = append(messages, f.GetMessageTypes()...)
	for _, message := range f.GetMessageTypes() {
		messages = append(messages, getAllNestedMessages(message)...)
	}
	return messages
}

// getAllNestedMessages returns a slice with the given message descriptor as well
// as all nested message descriptors, traversing to arbitrary depth.
func getAllNestedMessages(m *desc.MessageDescriptor) (messages []*desc.MessageDescriptor) {
	for _, nested := range m.GetNestedMessageTypes() {
		if !nested.IsMapEntry() { // Don't include the synthetic message type that represents an entry in a map field.
			messages = append(messages, nested)
		}
		messages = append(messages, getAllNestedMessages(nested)...)
	}
	return messages
}

// getAllEnums returns a slice with every enum (not just top-level enums)
// in the file.
func getAllEnums(f *desc.FileDescriptor) (enums []*desc.EnumDescriptor) {
	// Append all enums at the top level.
	enums = append(enums, f.GetEnumTypes()...)

	// Append all enums nested within messages.
	for _, m := range getAllMessages(f) {
		enums = append(enums, m.GetNestedEnumTypes()...)
	}

	return
}

// fileHeader attempts to get the comment at the top of the file, but it
// is on a best effort basis because protobuf is inconsistent.
//
// Taken from https://github.com/jhump/protoreflect/issues/215
func fileHeader(fd *desc.FileDescriptor) string {
	var firstLoc *dpb.SourceCodeInfo_Location
	var firstSpan int64

	// File level comments should only be comments identified on either
	// syntax (12), package (2), option (8), or import (3) statements.
	allowedPaths := map[int32]struct{}{2: {}, 3: {}, 8: {}, 12: {}}

	// Iterate over locations in the file descriptor looking for
	// what we think is a file-level comment.
	for _, curr := range fd.AsFileDescriptorProto().GetSourceCodeInfo().GetLocation() {
		// Skip locations that have no comments.
		if curr.LeadingComments == nil && len(curr.LeadingDetachedComments) == 0 {
			continue
		}
		// Skip locations that are not allowed because they should never be
		// mistaken for file-level comments.
		if _, ok := allowedPaths[curr.GetPath()[0]]; !ok {
			continue
		}
		currSpan := asPos(curr.Span)
		if firstLoc == nil || currSpan < firstSpan {
			firstLoc = curr
			firstSpan = currSpan
		}
	}
	if firstLoc == nil {
		return ""
	}
	if len(firstLoc.LeadingDetachedComments) > 0 {
		return strings.Join(firstLoc.LeadingDetachedComments, "\n")
	}
	return firstLoc.GetLeadingComments()
}

func asPos(span []int32) int64 {
	return (int64(span[0]) << 32) + int64(span[1])
}
<commit_msg>docs: Linter can only report errors in the running file. (#568)<commit_after>// Copyright 2019 Google LLC
//
// 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
//
// 		https://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 lint

import (
	"regexp"
	"strings"

	dpb "github.com/golang/protobuf/protoc-gen-go/descriptor"
	"github.com/jhump/protoreflect/desc"
)

// ProtoRule defines a lint rule that checks Google Protobuf APIs.
//
// Anything that satisfies this interface can be used as a rule,
// but most rule authors will want to use the implementations provided.
//
// Rules must only report errors in the file under which they are being run
// (not imported files).
type ProtoRule interface {
	// GetName returns the name of the rule.
	GetName() RuleName

	// Lint accepts a FileDescriptor and lints it,
	// returning a slice of Problem objects it finds.
	Lint(*desc.FileDescriptor) []Problem
}

// FileRule defines a lint rule that checks a file as a whole.
type FileRule struct {
	Name RuleName

	// LintFile accepts a FileDescriptor and lints it, returning a slice of
	// Problems it finds.
	LintFile func(*desc.FileDescriptor) []Problem

	// OnlyIf accepts a FileDescriptor and determines whether this rule
	// is applicable.
	OnlyIf func(*desc.FileDescriptor) bool

	// nolint:structcheck,unused
	noPositional struct{}
}

// GetName returns the name of the rule.
func (r *FileRule) GetName() RuleName {
	return r.Name
}

// Lint forwards the FileDescriptor to the LintFile method defined on the
// FileRule.
func (r *FileRule) Lint(fd *desc.FileDescriptor) []Problem {
	if r.OnlyIf == nil || r.OnlyIf(fd) {
		return r.LintFile(fd)
	}
	return nil
}

// MessageRule defines a lint rule that is run on each message in the file.
//
// Both top-level messages and nested messages are visited.
type MessageRule struct {
	Name RuleName

	// LintMessage accepts a MessageDescriptor and lints it, returning a slice
	// of Problems it finds.
	LintMessage func(*desc.MessageDescriptor) []Problem

	// OnlyIf accepts a MessageDescriptor and determines whether this rule
	// is applicable.
	OnlyIf func(*desc.MessageDescriptor) bool

	// nolint:structcheck,unused
	noPositional struct{}
}

// GetName returns the name of the rule.
func (r *MessageRule) GetName() RuleName {
	return r.Name
}

// Lint visits every message in the file, and runs `LintMessage`.
//
// If an `OnlyIf` function is provided on the rule, it is run against each
// message, and if it returns false, the `LintMessage` function is not called.
func (r *MessageRule) Lint(fd *desc.FileDescriptor) []Problem {
	problems := []Problem{}

	// Iterate over each message and process rules for each message.
	for _, message := range getAllMessages(fd) {
		if r.OnlyIf == nil || r.OnlyIf(message) {
			problems = append(problems, r.LintMessage(message)...)
		}
	}
	return problems
}

// FieldRule defines a lint rule that is run on each field within a file.
type FieldRule struct {
	Name RuleName

	// LintField accepts a FieldDescriptor and lints it, returning a slice of
	// Problems it finds.
	LintField func(*desc.FieldDescriptor) []Problem

	// OnlyIf accepts a FieldDescriptor and determines whether this rule
	// is applicable.
	OnlyIf func(*desc.FieldDescriptor) bool

	// nolint:structcheck,unused
	noPositional struct{}
}

// GetName returns the name of the rule.
func (r *FieldRule) GetName() RuleName {
	return r.Name
}

// Lint visits every field in the file and runs `LintField`.
//
// If an `OnlyIf` function is provided on the rule, it is run against each
// field, and if it returns false, the `LintField` function is not called.
func (r *FieldRule) Lint(fd *desc.FileDescriptor) []Problem {
	problems := []Problem{}

	// Iterate over each message and process rules for each field in that
	// message.
	for _, message := range getAllMessages(fd) {
		for _, field := range message.GetFields() {
			if r.OnlyIf == nil || r.OnlyIf(field) {
				problems = append(problems, r.LintField(field)...)
			}
		}
	}
	return problems
}

// ServiceRule defines a lint rule that is run on each service.
type ServiceRule struct {
	Name RuleName

	// LintService accepts a ServiceDescriptor and lints it.
	LintService func(*desc.ServiceDescriptor) []Problem

	// OnlyIf accepts a ServiceDescriptor and determines whether this rule
	// is applicable.
	OnlyIf func(*desc.ServiceDescriptor) bool

	// nolint:structcheck,unused
	noPositional struct{}
}

// GetName returns the name of the rule.
func (r *ServiceRule) GetName() RuleName {
	return r.Name
}

// Lint visits every service in the file and runs `LintService`.
//
// If an `OnlyIf` function is provided on the rule, it is run against each
// service, and if it returns false, the `LintService` function is not called.
func (r *ServiceRule) Lint(fd *desc.FileDescriptor) []Problem {
	problems := []Problem{}
	for _, service := range fd.GetServices() {
		if r.OnlyIf == nil || r.OnlyIf(service) {
			problems = append(problems, r.LintService(service)...)
		}
	}
	return problems
}

// MethodRule defines a lint rule that is run on each method.
type MethodRule struct {
	Name RuleName

	// LintMethod accepts a MethodDescriptor and lints it.
	LintMethod func(*desc.MethodDescriptor) []Problem

	// OnlyIf accepts a MethodDescriptor and determines whether this rule
	// is applicable.
	OnlyIf func(*desc.MethodDescriptor) bool

	// nolint:structcheck,unused
	noPositional struct{}
}

// GetName returns the name of the rule.
func (r *MethodRule) GetName() RuleName {
	return r.Name
}

// Lint visits every method in the file and runs `LintMethod`.
//
// If an `OnlyIf` function is provided on the rule, it is run against each
// method, and if it returns false, the `LintMethod` function is not called.
func (r *MethodRule) Lint(fd *desc.FileDescriptor) []Problem {
	problems := []Problem{}
	for _, service := range fd.GetServices() {
		for _, method := range service.GetMethods() {
			if r.OnlyIf == nil || r.OnlyIf(method) {
				problems = append(problems, r.LintMethod(method)...)
			}
		}
	}
	return problems
}

// EnumRule defines a lint rule that is run on each enum.
type EnumRule struct {
	Name RuleName

	// LintEnum accepts a EnumDescriptor and lints it.
	LintEnum func(*desc.EnumDescriptor) []Problem

	// OnlyIf accepts an EnumDescriptor and determines whether this rule
	// is applicable.
	OnlyIf func(*desc.EnumDescriptor) bool

	// nolint:structcheck,unused
	noPositional struct{}
}

// GetName returns the name of the rule.
func (r *EnumRule) GetName() RuleName {
	return r.Name
}

// Lint visits every enum in the file and runs `LintEnum`.
//
// If an `OnlyIf` function is provided on the rule, it is run against each
// enum, and if it returns false, the `LintEnum` function is not called.
func (r *EnumRule) Lint(fd *desc.FileDescriptor) []Problem {
	problems := []Problem{}

	// Lint all enums, either at the top of the file, or nested within messages.
	for _, enum := range getAllEnums(fd) {
		if r.OnlyIf == nil || r.OnlyIf(enum) {
			problems = append(problems, r.LintEnum(enum)...)
		}
	}
	return problems
}

// EnumValueRule defines a lint rule that is run on each enum value.
type EnumValueRule struct {
	Name RuleName

	// LintEnumValue accepts a EnumValueDescriptor and lints it.
	LintEnumValue func(*desc.EnumValueDescriptor) []Problem

	// OnlyIf accepts an EnumValueDescriptor and determines whether this rule
	// is applicable.
	OnlyIf func(*desc.EnumValueDescriptor) bool

	// nolint:structcheck,unused
	noPositional struct{}
}

// GetName returns the name of the rule.
func (r *EnumValueRule) GetName() RuleName {
	return r.Name
}

// Lint visits every enum value in the file and runs `LintEnum`.
//
// If an `OnlyIf` function is provided on the rule, it is run against each
// enum value, and if it returns false, the `LintEnum` function is not called.
func (r *EnumValueRule) Lint(fd *desc.FileDescriptor) []Problem {
	problems := []Problem{}

	// Lint all enums, either at the top of the file, or nested within messages.
	for _, enum := range getAllEnums(fd) {
		for _, value := range enum.GetValues() {
			if r.OnlyIf == nil || r.OnlyIf(value) {
				problems = append(problems, r.LintEnumValue(value)...)
			}
		}
	}
	return problems
}

// DescriptorRule defines a lint rule that is run on every descriptor
// in the file (but not the file itself).
type DescriptorRule struct {
	Name RuleName

	// LintDescriptor accepts a generic descriptor and lints it.
	//
	// Note: Unless the descriptor is typecast to a more specific type,
	// only a subset of methods are available to it.
	LintDescriptor func(desc.Descriptor) []Problem

	// OnlyIf accepts a Descriptor and determines whether this rule
	// is applicable.
	OnlyIf func(desc.Descriptor) bool

	// nolint:structcheck,unused
	noPositional struct{}
}

// GetName returns the name of the rule.
func (r *DescriptorRule) GetName() RuleName {
	return r.Name
}

// Lint visits every descriptor in the file and runs `LintDescriptor`.
//
// It visits every service, method, message, field, enum, and enum value.
// This order is not guaranteed. It does NOT visit the file itself.
func (r *DescriptorRule) Lint(fd *desc.FileDescriptor) []Problem {
	problems := []Problem{}

	// Iterate over all services and methods.
	for _, service := range fd.GetServices() {
		if r.OnlyIf == nil || r.OnlyIf(service) {
			problems = append(problems, r.LintDescriptor(service)...)
		}
		for _, method := range service.GetMethods() {
			if r.OnlyIf == nil || r.OnlyIf(method) {
				problems = append(problems, r.LintDescriptor(method)...)
			}
		}
	}

	// Iterate over all messages, and all fields within each message.
	for _, message := range getAllMessages(fd) {
		if r.OnlyIf == nil || r.OnlyIf(message) {
			problems = append(problems, r.LintDescriptor(message)...)
		}
		for _, field := range message.GetFields() {
			if r.OnlyIf == nil || r.OnlyIf(field) {
				problems = append(problems, r.LintDescriptor(field)...)
			}
		}
	}

	// Iterate over all enums and enum values.
	for _, enum := range getAllEnums(fd) {
		if r.OnlyIf == nil || r.OnlyIf(enum) {
			problems = append(problems, r.LintDescriptor(enum)...)
		}
		for _, value := range enum.GetValues() {
			if r.OnlyIf == nil || r.OnlyIf(value) {
				problems = append(problems, r.LintDescriptor(value)...)
			}
		}
	}

	// Done; return the full set of problems.
	return problems
}

var disableRuleNameRegex = regexp.MustCompile(`api-linter:\s*(.+)\s*=\s*disabled`)

func extractDisabledRuleName(commentLine string) string {
	match := disableRuleNameRegex.FindStringSubmatch(commentLine)
	if len(match) > 0 {
		return match[1]
	}
	return ""
}

func getLeadingComments(d desc.Descriptor) string {
	if sourceInfo := d.GetSourceInfo(); sourceInfo != nil {
		return sourceInfo.GetLeadingComments()
	}
	return ""
}

// getAllMessages returns a slice with every message (not just top-level
// messages) in the file.
func getAllMessages(f *desc.FileDescriptor) (messages []*desc.MessageDescriptor) {
	messages = append(messages, f.GetMessageTypes()...)
	for _, message := range f.GetMessageTypes() {
		messages = append(messages, getAllNestedMessages(message)...)
	}
	return messages
}

// getAllNestedMessages returns a slice with the given message descriptor as well
// as all nested message descriptors, traversing to arbitrary depth.
func getAllNestedMessages(m *desc.MessageDescriptor) (messages []*desc.MessageDescriptor) {
	for _, nested := range m.GetNestedMessageTypes() {
		if !nested.IsMapEntry() { // Don't include the synthetic message type that represents an entry in a map field.
			messages = append(messages, nested)
		}
		messages = append(messages, getAllNestedMessages(nested)...)
	}
	return messages
}

// getAllEnums returns a slice with every enum (not just top-level enums)
// in the file.
func getAllEnums(f *desc.FileDescriptor) (enums []*desc.EnumDescriptor) {
	// Append all enums at the top level.
	enums = append(enums, f.GetEnumTypes()...)

	// Append all enums nested within messages.
	for _, m := range getAllMessages(f) {
		enums = append(enums, m.GetNestedEnumTypes()...)
	}

	return
}

// fileHeader attempts to get the comment at the top of the file, but it
// is on a best effort basis because protobuf is inconsistent.
//
// Taken from https://github.com/jhump/protoreflect/issues/215
func fileHeader(fd *desc.FileDescriptor) string {
	var firstLoc *dpb.SourceCodeInfo_Location
	var firstSpan int64

	// File level comments should only be comments identified on either
	// syntax (12), package (2), option (8), or import (3) statements.
	allowedPaths := map[int32]struct{}{2: {}, 3: {}, 8: {}, 12: {}}

	// Iterate over locations in the file descriptor looking for
	// what we think is a file-level comment.
	for _, curr := range fd.AsFileDescriptorProto().GetSourceCodeInfo().GetLocation() {
		// Skip locations that have no comments.
		if curr.LeadingComments == nil && len(curr.LeadingDetachedComments) == 0 {
			continue
		}
		// Skip locations that are not allowed because they should never be
		// mistaken for file-level comments.
		if _, ok := allowedPaths[curr.GetPath()[0]]; !ok {
			continue
		}
		currSpan := asPos(curr.Span)
		if firstLoc == nil || currSpan < firstSpan {
			firstLoc = curr
			firstSpan = currSpan
		}
	}
	if firstLoc == nil {
		return ""
	}
	if len(firstLoc.LeadingDetachedComments) > 0 {
		return strings.Join(firstLoc.LeadingDetachedComments, "\n")
	}
	return firstLoc.GetLeadingComments()
}

func asPos(span []int32) int64 {
	return (int64(span[0]) << 32) + int64(span[1])
}
<|endoftext|>


<commit_before>package announcer

import (
	"time"

	"github.com/cenkalti/rain/internal/logger"
)

// DHTAnnouncer runs a function periodically to announce the Torrent to DHT network.
type DHTAnnouncer struct {
	lastAnnounce   time.Time
	needMorePeers  bool
	needMorePeersC chan bool
	closeC         chan struct{}
	doneC          chan struct{}
}

// NewDHTAnnouncer returns a new DHTAnnouncer.
func NewDHTAnnouncer() *DHTAnnouncer {
	return &DHTAnnouncer{
		needMorePeersC: make(chan bool),
		closeC:         make(chan struct{}),
		doneC:          make(chan struct{}),
	}
}

// Close the announcer.
func (a *DHTAnnouncer) Close() {
	close(a.closeC)
	<-a.doneC
}

// NeedMorePeers signals the announcer goroutine to fetch more peers from DHT.
func (a *DHTAnnouncer) NeedMorePeers(val bool) {
	select {
	case a.needMorePeersC <- val:
	case <-a.doneC:
	}
}

// Run the announcer. Invoke with go statement.
func (a *DHTAnnouncer) Run(announceFunc func(), interval, minInterval time.Duration, l logger.Logger) {
	defer close(a.doneC)

	timer := time.NewTimer(interval)
	defer timer.Stop()

	resetTimer := func() {
		if a.needMorePeers {
			timer.Reset(time.Until(a.lastAnnounce.Add(minInterval)))
		} else {
			timer.Reset(time.Until(a.lastAnnounce.Add(interval)))
		}
	}

	announce := func() {
		announceFunc()
		a.lastAnnounce = time.Now()
		resetTimer()
	}

	announce()
	for {
		select {
		case <-timer.C:
			announce()
		case a.needMorePeers = <-a.needMorePeersC:
			resetTimer()
		case <-a.closeC:
			return
		}
	}
}
<commit_msg>ask for more peers on start<commit_after>package announcer

import (
	"time"

	"github.com/cenkalti/rain/internal/logger"
)

// DHTAnnouncer runs a function periodically to announce the Torrent to DHT network.
type DHTAnnouncer struct {
	lastAnnounce   time.Time
	needMorePeers  bool
	needMorePeersC chan bool
	closeC         chan struct{}
	doneC          chan struct{}
}

// NewDHTAnnouncer returns a new DHTAnnouncer.
func NewDHTAnnouncer() *DHTAnnouncer {
	return &DHTAnnouncer{
		needMorePeersC: make(chan bool),
		closeC:         make(chan struct{}),
		doneC:          make(chan struct{}),
		needMorePeers:  true,
	}
}

// Close the announcer.
func (a *DHTAnnouncer) Close() {
	close(a.closeC)
	<-a.doneC
}

// NeedMorePeers signals the announcer goroutine to fetch more peers from DHT.
func (a *DHTAnnouncer) NeedMorePeers(val bool) {
	select {
	case a.needMorePeersC <- val:
	case <-a.doneC:
	}
}

// Run the announcer. Invoke with go statement.
func (a *DHTAnnouncer) Run(announceFunc func(), interval, minInterval time.Duration, l logger.Logger) {
	defer close(a.doneC)

	timer := time.NewTimer(minInterval)
	defer timer.Stop()

	resetTimer := func() {
		if a.needMorePeers {
			timer.Reset(time.Until(a.lastAnnounce.Add(minInterval)))
		} else {
			timer.Reset(time.Until(a.lastAnnounce.Add(interval)))
		}
	}

	announce := func() {
		announceFunc()
		a.lastAnnounce = time.Now()
		resetTimer()
	}

	announce()
	for {
		select {
		case <-timer.C:
			announce()
		case a.needMorePeers = <-a.needMorePeersC:
			resetTimer()
		case <-a.closeC:
			return
		}
	}
}
<|endoftext|>