<commit_before>package test

import (
	"fmt"
	"io/ioutil"
	"strings"
	"testing"

	. "gopkg.in/check.v1"
	. "gopkg.in/src-d/go-git.v4/utils/fs"
)

func Test(t *testing.T) { TestingT(t) }

type FilesystemSuite struct {
	Fs Filesystem
}

func (s *FilesystemSuite) TestCreate(c *C) {
	f, err := s.Fs.Create("foo")
	c.Assert(err, IsNil)
	c.Assert(f.Filename(), Equals, "foo")
}

func (s *FilesystemSuite) TestCreateDepth(c *C) {
	f, err := s.Fs.Create("bar/foo")
	c.Assert(err, IsNil)
	c.Assert(f.Filename(), Equals, "bar/foo")
}

func (s *FilesystemSuite) TestCreateDepthAbsolute(c *C) {
	f, err := s.Fs.Create("/bar/foo")
	c.Assert(err, IsNil)
	c.Assert(f.Filename(), Equals, "bar/foo")
}

func (s *FilesystemSuite) TestCreateOverwrite(c *C) {
	for i := 0; i < 2; i++ {
		f, err := s.Fs.Create("foo")
		c.Assert(err, IsNil)

		l, err := f.Write([]byte("foo"))
		c.Assert(err, IsNil)
		c.Assert(l, Equals, 3)

		err = f.Close()
		c.Assert(err, IsNil)
	}

	f, err := s.Fs.Open("foo")
	c.Assert(err, IsNil)

	wrote, err := ioutil.ReadAll(f)
	c.Assert(err, IsNil)
	c.Assert(wrote, DeepEquals, []byte("foo"))
}

func (s *FilesystemSuite) TestCreateClose(c *C) {
	f, err := s.Fs.Create("foo")
	c.Assert(err, IsNil)
	c.Assert(f.IsClosed(), Equals, false)

	f.Write([]byte("foo"))
	c.Assert(f.Close(), IsNil)

	file, err := s.Fs.Open(f.Filename())
	c.Assert(err, IsNil)

	wrote, err := ioutil.ReadAll(file)
	c.Assert(err, IsNil)
	c.Assert(wrote, DeepEquals, []byte("foo"))

	c.Assert(f.IsClosed(), Equals, true)
}

func (s *FilesystemSuite) TestReadDirAndDir(c *C) {
	files := []string{"foo", "bar", "qux/baz", "qux/qux"}
	for _, name := range files {
		f, err := s.Fs.Create(name)
		c.Assert(err, IsNil)
		c.Assert(f.Close(), IsNil)
	}

	info, err := s.Fs.ReadDir("/")
	c.Assert(err, IsNil)
	c.Assert(info, HasLen, 3)

	info, err = s.Fs.ReadDir("/qux")
	c.Assert(err, IsNil)
	c.Assert(info, HasLen, 2)

	qux := s.Fs.Dir("/qux")
	info, err = qux.ReadDir("/")
	c.Assert(err, IsNil)
	c.Assert(info, HasLen, 2)
}

func (s *FilesystemSuite) TestRename(c *C) {
	f, err := s.Fs.Create("foo")
	c.Assert(err, IsNil)
	c.Assert(f.Close(), IsNil)

	err = s.Fs.Rename("foo", "bar")
	c.Assert(err, IsNil)

	foo, err := s.Fs.Stat("foo")
	c.Assert(foo, IsNil)
	c.Assert(err, NotNil)

	bar, err := s.Fs.Stat("bar")
	c.Assert(bar, NotNil)
	c.Assert(err, IsNil)
}

func (s *FilesystemSuite) TestTempFile(c *C) {
	f, err := s.Fs.TempFile("", "bar")
	c.Assert(err, IsNil)

	c.Assert(strings.HasPrefix(f.Filename(), "bar"), Equals, true)
}

func (s *FilesystemSuite) TestTempFileWithPath(c *C) {
	f, err := s.Fs.TempFile("foo", "bar")
	c.Assert(err, IsNil)
	fmt.Printf("f: %s\n", f.Filename())
	c.Assert(strings.HasPrefix(f.Filename(), s.Fs.Join("foo", "bar")), Equals, true)
}

func (s *FilesystemSuite) TestTempFileFullWithPath(c *C) {
	f, err := s.Fs.TempFile("/foo", "bar")
	c.Assert(err, IsNil)
	fmt.Printf("f: %s\n", f.Filename())
	c.Assert(strings.HasPrefix(f.Filename(), s.Fs.Join("foo", "bar")), Equals, true)
}

func (s *FilesystemSuite) TestOpenAndStat(c *C) {
	f, err := s.Fs.Create("foo")
	c.Assert(err, IsNil)
	c.Assert(f.Close(), IsNil)

	foo, err := s.Fs.Open("foo")
	c.Assert(foo, NotNil)
	c.Assert(foo.Filename(), Equals, "foo")
	c.Assert(err, IsNil)

	stat, err := s.Fs.Stat("foo")
	c.Assert(stat, NotNil)
	c.Assert(err, IsNil)
	c.Assert(stat.Name(), Equals, "foo")
}

func (s *FilesystemSuite) TestRemove(c *C) {
	f, err := s.Fs.Create("foo")
	c.Assert(err, IsNil)
	c.Assert(f.Close(), IsNil)

	err = s.Fs.Remove("foo")
	c.Assert(err, IsNil)
}

func (s *FilesystemSuite) TestRemoveNonExisting(c *C) {
	c.Assert(s.Fs.Remove("NON-EXISTING"), NotNil)
}

func (s *FilesystemSuite) TestRemoveTempFile(c *C) {
	f, err := s.Fs.TempFile("test-dir", "test-prefix")
	fn := f.Filename()
	c.Assert(err, IsNil)
	c.Assert(f.Close(), IsNil)

	c.Assert(s.Fs.Remove(fn), IsNil)
}

func (s *FilesystemSuite) TestJoin(c *C) {
	c.Assert(s.Fs.Join("foo", "bar"), Equals, "foo/bar")
}

func (s *FilesystemSuite) TestBase(c *C) {
	c.Assert(s.Fs.Base(), Not(Equals), "")
}
<commit_msg>utils/fs: add test for create file in dir. (#95)<commit_after>package test

import (
	"fmt"
	"io/ioutil"
	"strings"
	"testing"

	. "gopkg.in/check.v1"
	. "gopkg.in/src-d/go-git.v4/utils/fs"
)

func Test(t *testing.T) { TestingT(t) }

type FilesystemSuite struct {
	Fs Filesystem
}

func (s *FilesystemSuite) TestCreate(c *C) {
	f, err := s.Fs.Create("foo")
	c.Assert(err, IsNil)
	c.Assert(f.Filename(), Equals, "foo")
}

func (s *FilesystemSuite) TestCreateDepth(c *C) {
	f, err := s.Fs.Create("bar/foo")
	c.Assert(err, IsNil)
	c.Assert(f.Filename(), Equals, "bar/foo")
}

func (s *FilesystemSuite) TestCreateDepthAbsolute(c *C) {
	f, err := s.Fs.Create("/bar/foo")
	c.Assert(err, IsNil)
	c.Assert(f.Filename(), Equals, "bar/foo")
}

func (s *FilesystemSuite) TestCreateOverwrite(c *C) {
	for i := 0; i < 2; i++ {
		f, err := s.Fs.Create("foo")
		c.Assert(err, IsNil)

		l, err := f.Write([]byte("foo"))
		c.Assert(err, IsNil)
		c.Assert(l, Equals, 3)

		err = f.Close()
		c.Assert(err, IsNil)
	}

	f, err := s.Fs.Open("foo")
	c.Assert(err, IsNil)

	wrote, err := ioutil.ReadAll(f)
	c.Assert(err, IsNil)
	c.Assert(wrote, DeepEquals, []byte("foo"))
}

func (s *FilesystemSuite) TestCreateClose(c *C) {
	f, err := s.Fs.Create("foo")
	c.Assert(err, IsNil)
	c.Assert(f.IsClosed(), Equals, false)

	f.Write([]byte("foo"))
	c.Assert(f.Close(), IsNil)

	file, err := s.Fs.Open(f.Filename())
	c.Assert(err, IsNil)

	wrote, err := ioutil.ReadAll(file)
	c.Assert(err, IsNil)
	c.Assert(wrote, DeepEquals, []byte("foo"))

	c.Assert(f.IsClosed(), Equals, true)
}

func (s *FilesystemSuite) TestReadDirAndDir(c *C) {
	files := []string{"foo", "bar", "qux/baz", "qux/qux"}
	for _, name := range files {
		f, err := s.Fs.Create(name)
		c.Assert(err, IsNil)
		c.Assert(f.Close(), IsNil)
	}

	info, err := s.Fs.ReadDir("/")
	c.Assert(err, IsNil)
	c.Assert(info, HasLen, 3)

	info, err = s.Fs.ReadDir("/qux")
	c.Assert(err, IsNil)
	c.Assert(info, HasLen, 2)

	qux := s.Fs.Dir("/qux")
	info, err = qux.ReadDir("/")
	c.Assert(err, IsNil)
	c.Assert(info, HasLen, 2)
}

func (s *FilesystemSuite) TestCreateInDir(c *C) {
	dir := s.Fs.Dir("foo")
	f, err := dir.Create("bar")
	c.Assert(err, IsNil)
	c.Assert(f.Close(), IsNil)
	c.Assert(f.Filename(), Equals, "bar")
}

func (s *FilesystemSuite) TestRename(c *C) {
	f, err := s.Fs.Create("foo")
	c.Assert(err, IsNil)
	c.Assert(f.Close(), IsNil)

	err = s.Fs.Rename("foo", "bar")
	c.Assert(err, IsNil)

	foo, err := s.Fs.Stat("foo")
	c.Assert(foo, IsNil)
	c.Assert(err, NotNil)

	bar, err := s.Fs.Stat("bar")
	c.Assert(bar, NotNil)
	c.Assert(err, IsNil)
}

func (s *FilesystemSuite) TestTempFile(c *C) {
	f, err := s.Fs.TempFile("", "bar")
	c.Assert(err, IsNil)

	c.Assert(strings.HasPrefix(f.Filename(), "bar"), Equals, true)
}

func (s *FilesystemSuite) TestTempFileWithPath(c *C) {
	f, err := s.Fs.TempFile("foo", "bar")
	c.Assert(err, IsNil)
	fmt.Printf("f: %s\n", f.Filename())
	c.Assert(strings.HasPrefix(f.Filename(), s.Fs.Join("foo", "bar")), Equals, true)
}

func (s *FilesystemSuite) TestTempFileFullWithPath(c *C) {
	f, err := s.Fs.TempFile("/foo", "bar")
	c.Assert(err, IsNil)
	fmt.Printf("f: %s\n", f.Filename())
	c.Assert(strings.HasPrefix(f.Filename(), s.Fs.Join("foo", "bar")), Equals, true)
}

func (s *FilesystemSuite) TestOpenAndStat(c *C) {
	f, err := s.Fs.Create("foo")
	c.Assert(err, IsNil)
	c.Assert(f.Close(), IsNil)

	foo, err := s.Fs.Open("foo")
	c.Assert(foo, NotNil)
	c.Assert(foo.Filename(), Equals, "foo")
	c.Assert(err, IsNil)

	stat, err := s.Fs.Stat("foo")
	c.Assert(stat, NotNil)
	c.Assert(err, IsNil)
	c.Assert(stat.Name(), Equals, "foo")
}

func (s *FilesystemSuite) TestRemove(c *C) {
	f, err := s.Fs.Create("foo")
	c.Assert(err, IsNil)
	c.Assert(f.Close(), IsNil)

	err = s.Fs.Remove("foo")
	c.Assert(err, IsNil)
}

func (s *FilesystemSuite) TestRemoveNonExisting(c *C) {
	c.Assert(s.Fs.Remove("NON-EXISTING"), NotNil)
}

func (s *FilesystemSuite) TestRemoveTempFile(c *C) {
	f, err := s.Fs.TempFile("test-dir", "test-prefix")
	fn := f.Filename()
	c.Assert(err, IsNil)
	c.Assert(f.Close(), IsNil)

	c.Assert(s.Fs.Remove(fn), IsNil)
}

func (s *FilesystemSuite) TestJoin(c *C) {
	c.Assert(s.Fs.Join("foo", "bar"), Equals, "foo/bar")
}

func (s *FilesystemSuite) TestBase(c *C) {
	c.Assert(s.Fs.Base(), Not(Equals), "")
}
<|endoftext|>


<commit_before>package concourse

import (
	"bytes"
	"fmt"
	"io/ioutil"
	"os"
	"os/exec"
	"regexp"
	"strings"
	"text/template"

	"github.com/EngineerBetter/concourse-up/bosh"
	"github.com/EngineerBetter/concourse-up/config"
	"github.com/EngineerBetter/concourse-up/util/yaml"
	"github.com/fatih/color"
)

// Info represents the compound fields for info templates
type Info struct {
	Terraform   TerraformInfo   `json:"terraform"`
	Config      config.Config   `json:"config"`
	Instances   []bosh.Instance `json:"instances"`
	CertExpiry  string          `json:"cert_expiry"`
	GatewayUser string
}

// TerraformInfo represents the terraform output fields needed for the info templates
type TerraformInfo struct {
	DirectorPublicIP string
	NatGatewayIP     string
}

// FetchInfo fetches and builds the info
func (client *Client) FetchInfo() (*Info, error) {
	var gatewayUser string
	config, err := client.configClient.Load()
	if err != nil {
		return nil, err
	}

	directorCredsBytes, err := loadDirectorCreds(client.configClient)
	if err != nil {
		return nil, err
	}

	var certExpiry string
	if len(directorCredsBytes) > 0 {
		natsCA, err := yaml.Path(directorCredsBytes, "nats_server_tls/ca")
		if err != nil {
			return nil, err
		}

		var re = regexp.MustCompile(`\n\s*`)

		openSSL := exec.Command("openssl", "x509", "-noout", "-dates")
		openSSL.Stdin = strings.NewReader(re.ReplaceAllString(natsCA, "\n"))
		var out bytes.Buffer
		openSSL.Stdout = &out
		err = openSSL.Run()
		if err != nil {
			return nil, err
		}
		if strings.Contains(out.String(), "notAfter=") {
			certExpiry = strings.Split(out.String(), "notAfter=")[1]
		} else {
			return nil, fmt.Errorf("openssl output is not as expected. got: %s", out.String())
		}
	}

	environment, metadata, err := client.tfCLI.IAAS(client.provider.IAAS())
	if err != nil {
		return nil, err
	}
	switch client.provider.IAAS() {
	case awsConst: // nolint
		err = environment.Build(map[string]interface{}{
			"AllowIPs":               config.AllowIPs,
			"AvailabilityZone":       config.AvailabilityZone,
			"ConfigBucket":           config.ConfigBucket,
			"Deployment":             config.Deployment,
			"HostedZoneID":           config.HostedZoneID,
			"HostedZoneRecordPrefix": config.HostedZoneRecordPrefix,
			"Namespace":              config.Namespace,
			"Project":                config.Project,
			"PublicKey":              config.PublicKey,
			"RDSDefaultDatabaseName": config.RDSDefaultDatabaseName,
			"RDSInstanceClass":       config.RDSInstanceClass,
			"RDSPassword":            config.RDSPassword,
			"RDSUsername":            config.RDSUsername,
			"Region":                 config.Region,
			"SourceAccessIP":         config.SourceAccessIP,
			"TFStatePath":            config.TFStatePath,
			"MultiAZRDS":             config.MultiAZRDS,
		})
		if err != nil {
			return nil, err
		}
		gatewayUser = "vcap"
	case gcpConst: // nolint
		project, err1 := client.provider.Attr("project")
		if err1 != nil {
			return nil, err1
		}
		credentialspath, err1 := client.provider.Attr("credentials_path")
		if err1 != nil {
			return nil, err1
		}
		err1 = environment.Build(map[string]interface{}{
			"Region":             client.provider.Region(),
			"Zone":               client.provider.Zone(""),
			"Tags":               "",
			"Project":            project,
			"GCPCredentialsJSON": credentialspath,
			"ExternalIP":         config.SourceAccessIP,
			"Deployment":         config.Deployment,
			"ConfigBucket":       config.ConfigBucket,
			"DBName":             config.RDSDefaultDatabaseName,
			"DBTier":             config.RDSInstanceClass,
			"DBPassword":         config.RDSPassword,
			"DBUsername":         config.RDSUsername,
			"AllowIPs":           config.AllowIPs,
			"DNSManagedZoneName": config.HostedZoneID,
			"DNSRecordSetPrefix": config.HostedZoneRecordPrefix,
		})
		if err1 != nil {
			return nil, err1
		}
		gatewayUser = "jumpbox"
	}
	err = client.tfCLI.BuildOutput(environment, metadata)
	if err != nil {
		return nil, err
	}

	directorPublicIP, err := metadata.Get("DirectorPublicIP")
	if err != nil {
		return nil, err
	}

	natGatewayIP, err := metadata.Get("NatGatewayIP")
	if err != nil {
		return nil, err
	}

	terraformInfo := TerraformInfo{
		DirectorPublicIP: directorPublicIP,
		NatGatewayIP:     natGatewayIP,
	}

	// temporary fix before GCP has whitelisted IPs implemented
	if client.provider.IAAS() == "AWS" { //nolint
		userIP, err1 := client.ipChecker()
		if err1 != nil {
			return nil, err1
		}

		directorSecurityGroupID, err1 := metadata.Get("DirectorSecurityGroupID")
		if err1 != nil {
			return nil, err1
		}
		whitelisted, err1 := client.provider.CheckForWhitelistedIP(userIP, directorSecurityGroupID)
		if err1 != nil {
			return nil, err1
		}

		if !whitelisted {
			err1 = fmt.Errorf("Do you need to add your IP %s to the %s-director security group (for ports 22, 6868, and 25555)?", userIP, config.Deployment)
			return nil, err1
		}
	}

	boshClient, err := client.buildBoshClient(config, metadata)
	if err != nil {
		return nil, err
	}
	defer boshClient.Cleanup()

	instances, err := boshClient.Instances()
	if err != nil {
		return nil, err
	}

	return &Info{
		Terraform:   terraformInfo,
		Config:      config,
		Instances:   instances,
		GatewayUser: gatewayUser,
		CertExpiry:  certExpiry,
	}, nil
}

const infoTemplate = `Deployment:
	Namespace: {{.Config.Namespace}}
	IAAS:      aws
	Region:    {{.Config.Region}}

Workers:
	Count:              {{.Config.ConcourseWorkerCount}}
	Size:               {{.Config.ConcourseWorkerSize}}
	Outbound Public IP: {{.Terraform.NatGatewayIP}}

Instances:
{{range .Instances}}
	{{.Name}} {{.IP | replace "\n" ","}} {{.State}}
{{end}}

Concourse credentials:
	username: {{.Config.ConcourseUsername}}
	password: {{.Config.ConcoursePassword}}
	URL:      https://{{.Config.Domain}}

Credhub credentials:
	username: {{.Config.CredhubUsername}}
	password: {{.Config.CredhubPassword}}
	URL:      {{.Config.CredhubURL}}
	CA Cert:
		{{ .Config.CredhubCACert | replace "\n" "\n\t\t"}}

Grafana credentials:
	username: {{.Config.ConcourseUsername}}
	password: {{.Config.ConcoursePassword}}
	URL:      https://{{.Config.Domain}}:3000

Bosh credentials:
	username: {{.Config.DirectorUsername}}
	password: {{.Config.DirectorPassword}}
	IP:       {{.Terraform.DirectorPublicIP}}
	CA Cert:
		{{ .Config.DirectorCACert | replace "\n" "\n\t\t"}}

BOSH-generated NAT certs will expire on: {{ .CertExpiry }}

Uses Concourse-Up version {{.Config.Version}}

Built by {{"EngineerBetter http://engineerbetter.com" | blue}}
`

func (info *Info) String() string {
	t := template.Must(template.New("info").Funcs(template.FuncMap{
		"replace": func(old, new, s string) string {
			return strings.Replace(s, old, new, -1)
		},
		"blue": color.New(color.FgCyan, color.Bold).Sprint,
	}).Parse(infoTemplate))
	var buf bytes.Buffer
	err := t.Execute(&buf, info)
	if err != nil {
		panic(err)
	}
	return buf.String()
}

func writeTempFile(data string) (name string, err error) {
	f, err := ioutil.TempFile("", "")
	if err != nil {
		return "", err
	}
	name = f.Name()
	_, err = f.WriteString(data)
	if err1 := f.Close(); err == nil {
		err = err1
	}
	if err != nil {
		os.Remove(name)
	}
	return name, err
}

var envTemplate = template.Must(template.New("env").Funcs(template.FuncMap{
	"to_file": writeTempFile,
}).Parse(`
export BOSH_ENVIRONMENT={{.Terraform.DirectorPublicIP}}
export BOSH_GW_HOST={{.Terraform.DirectorPublicIP}}
export BOSH_CA_CERT='{{.Config.DirectorCACert}}'
export BOSH_DEPLOYMENT=concourse
export BOSH_CLIENT={{.Config.DirectorUsername}}
export BOSH_CLIENT_SECRET={{.Config.DirectorPassword}}
export BOSH_GW_USER={{.GatewayUser}}
export BOSH_GW_PRIVATE_KEY={{.Config.PrivateKey | to_file}}
export CREDHUB_SERVER={{.Config.CredhubURL}}
export CREDHUB_CA_CERT='{{.Config.CredhubCACert}}'
export CREDHUB_CLIENT=credhub_admin
export CREDHUB_SECRET={{.Config.CredhubAdminClientSecret}}
export NAMESPACE={{.Config.Namespace}}
`))

// Env returns a string that is suitable for a shell to evaluate that sets environment
// varibles which are used to log into bosh and credhub
func (info *Info) Env() (string, error) {
	var buf bytes.Buffer
	var i Info
	i = *info
	err := envTemplate.Execute(&buf, i)
	if err != nil {
		return "", err
	}
	return buf.String(), nil
}
<commit_msg>unshadowing nested err<commit_after>package concourse

import (
	"bytes"
	"fmt"
	"io/ioutil"
	"os"
	"os/exec"
	"regexp"
	"strings"
	"text/template"

	"github.com/EngineerBetter/concourse-up/bosh"
	"github.com/EngineerBetter/concourse-up/config"
	"github.com/EngineerBetter/concourse-up/util/yaml"
	"github.com/fatih/color"
)

// Info represents the compound fields for info templates
type Info struct {
	Terraform   TerraformInfo   `json:"terraform"`
	Config      config.Config   `json:"config"`
	Instances   []bosh.Instance `json:"instances"`
	CertExpiry  string          `json:"cert_expiry"`
	GatewayUser string
}

// TerraformInfo represents the terraform output fields needed for the info templates
type TerraformInfo struct {
	DirectorPublicIP string
	NatGatewayIP     string
}

// FetchInfo fetches and builds the info
func (client *Client) FetchInfo() (*Info, error) {
	var gatewayUser string
	config, err := client.configClient.Load()
	if err != nil {
		return nil, err
	}

	directorCredsBytes, err := loadDirectorCreds(client.configClient)
	if err != nil {
		return nil, err
	}

	var certExpiry string
	if len(directorCredsBytes) > 0 {
		natsCA, err1 := yaml.Path(directorCredsBytes, "nats_server_tls/ca")
		if err1 != nil {
			return nil, err1
		}

		var re = regexp.MustCompile(`\n\s*`)

		openSSL := exec.Command("openssl", "x509", "-noout", "-dates")
		openSSL.Stdin = strings.NewReader(re.ReplaceAllString(natsCA, "\n"))
		var out bytes.Buffer
		openSSL.Stdout = &out
		err1 = openSSL.Run()
		if err1 != nil {
			return nil, err1
		}
		if strings.Contains(out.String(), "notAfter=") {
			certExpiry = strings.Split(out.String(), "notAfter=")[1]
		} else {
			return nil, fmt.Errorf("openssl output is not as expected. got: %s", out.String())
		}
	}

	environment, metadata, err := client.tfCLI.IAAS(client.provider.IAAS())
	if err != nil {
		return nil, err
	}
	switch client.provider.IAAS() {
	case awsConst: // nolint
		err = environment.Build(map[string]interface{}{
			"AllowIPs":               config.AllowIPs,
			"AvailabilityZone":       config.AvailabilityZone,
			"ConfigBucket":           config.ConfigBucket,
			"Deployment":             config.Deployment,
			"HostedZoneID":           config.HostedZoneID,
			"HostedZoneRecordPrefix": config.HostedZoneRecordPrefix,
			"Namespace":              config.Namespace,
			"Project":                config.Project,
			"PublicKey":              config.PublicKey,
			"RDSDefaultDatabaseName": config.RDSDefaultDatabaseName,
			"RDSInstanceClass":       config.RDSInstanceClass,
			"RDSPassword":            config.RDSPassword,
			"RDSUsername":            config.RDSUsername,
			"Region":                 config.Region,
			"SourceAccessIP":         config.SourceAccessIP,
			"TFStatePath":            config.TFStatePath,
			"MultiAZRDS":             config.MultiAZRDS,
		})
		if err != nil {
			return nil, err
		}
		gatewayUser = "vcap"
	case gcpConst: // nolint
		project, err1 := client.provider.Attr("project")
		if err1 != nil {
			return nil, err1
		}
		credentialspath, err1 := client.provider.Attr("credentials_path")
		if err1 != nil {
			return nil, err1
		}
		err1 = environment.Build(map[string]interface{}{
			"Region":             client.provider.Region(),
			"Zone":               client.provider.Zone(""),
			"Tags":               "",
			"Project":            project,
			"GCPCredentialsJSON": credentialspath,
			"ExternalIP":         config.SourceAccessIP,
			"Deployment":         config.Deployment,
			"ConfigBucket":       config.ConfigBucket,
			"DBName":             config.RDSDefaultDatabaseName,
			"DBTier":             config.RDSInstanceClass,
			"DBPassword":         config.RDSPassword,
			"DBUsername":         config.RDSUsername,
			"AllowIPs":           config.AllowIPs,
			"DNSManagedZoneName": config.HostedZoneID,
			"DNSRecordSetPrefix": config.HostedZoneRecordPrefix,
		})
		if err1 != nil {
			return nil, err1
		}
		gatewayUser = "jumpbox"
	}
	err = client.tfCLI.BuildOutput(environment, metadata)
	if err != nil {
		return nil, err
	}

	directorPublicIP, err := metadata.Get("DirectorPublicIP")
	if err != nil {
		return nil, err
	}

	natGatewayIP, err := metadata.Get("NatGatewayIP")
	if err != nil {
		return nil, err
	}

	terraformInfo := TerraformInfo{
		DirectorPublicIP: directorPublicIP,
		NatGatewayIP:     natGatewayIP,
	}

	// temporary fix before GCP has whitelisted IPs implemented
	if client.provider.IAAS() == "AWS" { //nolint
		userIP, err1 := client.ipChecker()
		if err1 != nil {
			return nil, err1
		}

		directorSecurityGroupID, err1 := metadata.Get("DirectorSecurityGroupID")
		if err1 != nil {
			return nil, err1
		}
		whitelisted, err1 := client.provider.CheckForWhitelistedIP(userIP, directorSecurityGroupID)
		if err1 != nil {
			return nil, err1
		}

		if !whitelisted {
			err1 = fmt.Errorf("Do you need to add your IP %s to the %s-director security group (for ports 22, 6868, and 25555)?", userIP, config.Deployment)
			return nil, err1
		}
	}

	boshClient, err := client.buildBoshClient(config, metadata)
	if err != nil {
		return nil, err
	}
	defer boshClient.Cleanup()

	instances, err := boshClient.Instances()
	if err != nil {
		return nil, err
	}

	return &Info{
		Terraform:   terraformInfo,
		Config:      config,
		Instances:   instances,
		GatewayUser: gatewayUser,
		CertExpiry:  certExpiry,
	}, nil
}

const infoTemplate = `Deployment:
	Namespace: {{.Config.Namespace}}
	IAAS:      aws
	Region:    {{.Config.Region}}

Workers:
	Count:              {{.Config.ConcourseWorkerCount}}
	Size:               {{.Config.ConcourseWorkerSize}}
	Outbound Public IP: {{.Terraform.NatGatewayIP}}

Instances:
{{range .Instances}}
	{{.Name}} {{.IP | replace "\n" ","}} {{.State}}
{{end}}

Concourse credentials:
	username: {{.Config.ConcourseUsername}}
	password: {{.Config.ConcoursePassword}}
	URL:      https://{{.Config.Domain}}

Credhub credentials:
	username: {{.Config.CredhubUsername}}
	password: {{.Config.CredhubPassword}}
	URL:      {{.Config.CredhubURL}}
	CA Cert:
		{{ .Config.CredhubCACert | replace "\n" "\n\t\t"}}

Grafana credentials:
	username: {{.Config.ConcourseUsername}}
	password: {{.Config.ConcoursePassword}}
	URL:      https://{{.Config.Domain}}:3000

Bosh credentials:
	username: {{.Config.DirectorUsername}}
	password: {{.Config.DirectorPassword}}
	IP:       {{.Terraform.DirectorPublicIP}}
	CA Cert:
		{{ .Config.DirectorCACert | replace "\n" "\n\t\t"}}

BOSH-generated NAT certs will expire on: {{ .CertExpiry }}

Uses Concourse-Up version {{.Config.Version}}

Built by {{"EngineerBetter http://engineerbetter.com" | blue}}
`

func (info *Info) String() string {
	t := template.Must(template.New("info").Funcs(template.FuncMap{
		"replace": func(old, new, s string) string {
			return strings.Replace(s, old, new, -1)
		},
		"blue": color.New(color.FgCyan, color.Bold).Sprint,
	}).Parse(infoTemplate))
	var buf bytes.Buffer
	err := t.Execute(&buf, info)
	if err != nil {
		panic(err)
	}
	return buf.String()
}

func writeTempFile(data string) (name string, err error) {
	f, err := ioutil.TempFile("", "")
	if err != nil {
		return "", err
	}
	name = f.Name()
	_, err = f.WriteString(data)
	if err1 := f.Close(); err == nil {
		err = err1
	}
	if err != nil {
		os.Remove(name)
	}
	return name, err
}

var envTemplate = template.Must(template.New("env").Funcs(template.FuncMap{
	"to_file": writeTempFile,
}).Parse(`
export BOSH_ENVIRONMENT={{.Terraform.DirectorPublicIP}}
export BOSH_GW_HOST={{.Terraform.DirectorPublicIP}}
export BOSH_CA_CERT='{{.Config.DirectorCACert}}'
export BOSH_DEPLOYMENT=concourse
export BOSH_CLIENT={{.Config.DirectorUsername}}
export BOSH_CLIENT_SECRET={{.Config.DirectorPassword}}
export BOSH_GW_USER={{.GatewayUser}}
export BOSH_GW_PRIVATE_KEY={{.Config.PrivateKey | to_file}}
export CREDHUB_SERVER={{.Config.CredhubURL}}
export CREDHUB_CA_CERT='{{.Config.CredhubCACert}}'
export CREDHUB_CLIENT=credhub_admin
export CREDHUB_SECRET={{.Config.CredhubAdminClientSecret}}
export NAMESPACE={{.Config.Namespace}}
`))

// Env returns a string that is suitable for a shell to evaluate that sets environment
// varibles which are used to log into bosh and credhub
func (info *Info) Env() (string, error) {
	var buf bytes.Buffer
	var i Info
	i = *info
	err := envTemplate.Execute(&buf, i)
	if err != nil {
		return "", err
	}
	return buf.String(), nil
}
<|endoftext|>


<commit_before>package vlc

// #cgo LDFLAGS: -lvlc
// #include <vlc/vlc.h>
// #include <stdlib.h>
import "C"
import (
	"unsafe"
)

// RendererDiscoveryCallback is used by renderer discovery services to
// report discovery events.
//
// The available events are:
//   - RendererDiscovererItemAdded
//   - RendererDiscovererItemDeleted
type RendererDiscoveryCallback func(Event, *Renderer)

// RendererDiscovererDescriptor contains information about a renderer
// discovery service. Pass the `Name` field to the NewRendererDiscoverer
// method in order to create a new discovery service instance.
type RendererDiscovererDescriptor struct {
	Name     string
	LongName string
}

// ListRendererDiscoverers returns a list of descriptors identifying the
// available renderer discovery services.
func ListRendererDiscoverers() ([]*RendererDiscovererDescriptor, error) {
	if err := inst.assertInit(); err != nil {
		return nil, err
	}

	// Get renderer discoverer descriptors.
	var cDescriptors **C.libvlc_rd_description_t

	count := int(C.libvlc_renderer_discoverer_list_get(inst.handle, &cDescriptors))
	if count <= 0 || cDescriptors == nil {
		return nil, nil
	}
	defer C.libvlc_renderer_discoverer_list_release(cDescriptors, C.size_t(count))

	// Parse renderer discoverer descriptors.
	descriptors := make([]*RendererDiscovererDescriptor, 0, count)
	for i := 0; i < count; i++ {
		// Get current renderer discoverer descriptor.
		cDescriptorPtr := unsafe.Pointer(uintptr(unsafe.Pointer(cDescriptors)) +
			uintptr(i)*unsafe.Sizeof(*cDescriptors))
		if cDescriptorPtr == nil {
			return nil, ErrRendererDiscovererParse
		}

		cDescriptor := *(**C.libvlc_rd_description_t)(cDescriptorPtr)
		if cDescriptor == nil {
			return nil, ErrRendererDiscovererParse
		}

		// Parse renderer discoverer descriptor.
		descriptors = append(descriptors, &RendererDiscovererDescriptor{
			Name:     C.GoString(cDescriptor.psz_name),
			LongName: C.GoString(cDescriptor.psz_longname),
		})
	}

	return descriptors, nil
}

// RendererDiscoverer represents a renderer discovery service.
// Discovery services use different discovery protocols (e.g. mDNS)
// in order to find available media renderers (e.g. Chromecast).
type RendererDiscoverer struct {
	discoverer *C.libvlc_renderer_discoverer_t
	renderers  map[*C.libvlc_renderer_item_t]*Renderer
	stopFunc   func()
}

// NewRendererDiscoverer instantiates the renderer discovery service
// identified by the specified name. Use the ListRendererDiscoverers
// method to obtain the list of available discovery service descriptors.
// NOTE: Call the Release method on the discovery service instance in
// order to free the allocated resources.
func NewRendererDiscoverer(name string) (*RendererDiscoverer, error) {
	if err := inst.assertInit(); err != nil {
		return nil, err
	}

	cName := C.CString(name)
	defer C.free(unsafe.Pointer(cName))

	discoverer := C.libvlc_renderer_discoverer_new(inst.handle, cName)
	if discoverer == nil {
		return nil, errOrDefault(getError(), ErrRendererDiscovererCreate)
	}

	return &RendererDiscoverer{
		discoverer: discoverer,
		renderers:  map[*C.libvlc_renderer_item_t]*Renderer{},
	}, nil
}

// Release stops and destroys the renderer discovery service along
// with all the renderers found by the instance.
func (rd *RendererDiscoverer) Release() error {
	if err := rd.assertInit(); err != nil {
		return nil
	}

	// Stop discovery service.
	rd.stop()

	// Release renderers.
	for _, renderer := range rd.renderers {
		renderer.release()
	}
	rd.renderers = nil

	// Release discovery service.
	C.libvlc_renderer_discoverer_release(rd.discoverer)
	rd.discoverer = nil

	return getError()
}

// Start starts the renderer discovery service and reports discovery
// events through the specified callback function.
// NOTE: the Stop and Release methods should not be called from the callback
// function. Doing so will result in undefined behavior.
func (rd *RendererDiscoverer) Start(cb RendererDiscoveryCallback) error {
	if cb == nil {
		return ErrInvalidEventCallback
	}

	// Stop discovery service, if started.
	if err := rd.Stop(); err != nil {
		return err
	}

	// Retrieve event manager.
	manager, err := rd.eventManager()
	if err != nil {
		return err
	}

	// Create event callback.
	eventCallback := func(event *C.libvlc_event_t, userData interface{}) {
		if err := rd.assertInit(); err != nil {
			return
		}
		if event == nil {
			return
		}

		cRenderer := *(**C.libvlc_renderer_item_t)(unsafe.Pointer(&event.u[0]))
		if cRenderer == nil {
			return
		}

		renderer, ok := rd.renderers[cRenderer]
		if !ok {
			renderer = &Renderer{renderer: cRenderer}
			renderer.hold()
			rd.renderers[cRenderer] = renderer
		}

		switch event := Event(event._type); event {
		case RendererDiscovererItemAdded:
			cb(event, renderer)
		case RendererDiscovererItemDeleted:
			cb(event, renderer)
			delete(rd.renderers, cRenderer)
			renderer.release()
		}
	}

	// Attach discovery service events.
	events := []Event{
		RendererDiscovererItemAdded, RendererDiscovererItemDeleted,
	}

	eventIDs := make([]EventID, 0, len(events))
	for _, event := range events {
		eventID, err := manager.attach(event, nil, eventCallback, nil)
		if err != nil {
			return err
		}

		eventIDs = append(eventIDs, eventID)
	}
	defer func() {
		if rd.stopFunc == nil {
			manager.Detach(eventIDs...)
		}
	}()

	// Start discovery service.
	if C.libvlc_renderer_discoverer_start(rd.discoverer) < 0 {
		return errOrDefault(getError(), ErrRendererDiscovererStart)
	}

	rd.stopFunc = func() {
		// Detach events.
		manager.Detach(eventIDs...)

		// Stop discovery service.
		C.libvlc_renderer_discoverer_stop(rd.discoverer)
	}

	return nil
}

// Stop stops the discovery service.
func (rd *RendererDiscoverer) Stop() error {
	if err := rd.assertInit(); err != nil {
		return err
	}

	rd.stop()
	return nil
}

func (rd *RendererDiscoverer) stop() {
	if rd.stopFunc != nil {
		rd.stopFunc()
		rd.stopFunc = nil
	}
}

// eventManager returns the event manager responsible for the renderer
// discovery service.
func (rd *RendererDiscoverer) eventManager() (*EventManager, error) {
	if err := rd.assertInit(); err != nil {
		return nil, err
	}

	manager := C.libvlc_renderer_discoverer_event_manager(rd.discoverer)
	if manager == nil {
		return nil, ErrMissingEventManager
	}

	return newEventManager(manager), nil
}

func (rd *RendererDiscoverer) assertInit() error {
	if rd == nil || rd.discoverer == nil {
		return ErrRendererDiscovererNotInitialized
	}

	return nil
}
<commit_msg>Improve renderer discovery code documentation<commit_after>package vlc

// #cgo LDFLAGS: -lvlc
// #include <vlc/vlc.h>
// #include <stdlib.h>
import "C"
import (
	"unsafe"
)

// RendererDiscoveryCallback is used by renderer discovery services to
// report discovery events.
//
// The available events are:
//   - RendererDiscovererItemAdded
//   - RendererDiscovererItemDeleted
type RendererDiscoveryCallback func(Event, *Renderer)

// RendererDiscovererDescriptor contains information about a renderer
// discovery service. Pass the `Name` field to the NewRendererDiscoverer
// method in order to create a new discovery service instance.
type RendererDiscovererDescriptor struct {
	Name     string
	LongName string
}

// ListRendererDiscoverers returns a list of descriptors identifying the
// available renderer discovery services.
func ListRendererDiscoverers() ([]*RendererDiscovererDescriptor, error) {
	if err := inst.assertInit(); err != nil {
		return nil, err
	}

	// Get renderer discoverer descriptors.
	var cDescriptors **C.libvlc_rd_description_t

	count := int(C.libvlc_renderer_discoverer_list_get(inst.handle, &cDescriptors))
	if count <= 0 || cDescriptors == nil {
		return nil, nil
	}
	defer C.libvlc_renderer_discoverer_list_release(cDescriptors, C.size_t(count))

	// Parse renderer discoverer descriptors.
	descriptors := make([]*RendererDiscovererDescriptor, 0, count)
	for i := 0; i < count; i++ {
		// Get current renderer discoverer descriptor.
		cDescriptorPtr := unsafe.Pointer(uintptr(unsafe.Pointer(cDescriptors)) +
			uintptr(i)*unsafe.Sizeof(*cDescriptors))
		if cDescriptorPtr == nil {
			return nil, ErrRendererDiscovererParse
		}

		cDescriptor := *(**C.libvlc_rd_description_t)(cDescriptorPtr)
		if cDescriptor == nil {
			return nil, ErrRendererDiscovererParse
		}

		// Parse renderer discoverer descriptor.
		descriptors = append(descriptors, &RendererDiscovererDescriptor{
			Name:     C.GoString(cDescriptor.psz_name),
			LongName: C.GoString(cDescriptor.psz_longname),
		})
	}

	return descriptors, nil
}

// RendererDiscoverer represents a renderer discovery service.
// Discovery services use different discovery protocols (e.g. mDNS)
// in order to find available media renderers (e.g. Chromecast).
type RendererDiscoverer struct {
	discoverer *C.libvlc_renderer_discoverer_t
	renderers  map[*C.libvlc_renderer_item_t]*Renderer
	stopFunc   func()
}

// NewRendererDiscoverer instantiates the renderer discovery service
// identified by the specified name. Use the ListRendererDiscoverers
// method to obtain the list of available discovery service descriptors.
//   NOTE: Call the Release method on the discovery service instance in
//   order to free the allocated resources.
func NewRendererDiscoverer(name string) (*RendererDiscoverer, error) {
	if err := inst.assertInit(); err != nil {
		return nil, err
	}

	cName := C.CString(name)
	defer C.free(unsafe.Pointer(cName))

	discoverer := C.libvlc_renderer_discoverer_new(inst.handle, cName)
	if discoverer == nil {
		return nil, errOrDefault(getError(), ErrRendererDiscovererCreate)
	}

	return &RendererDiscoverer{
		discoverer: discoverer,
		renderers:  map[*C.libvlc_renderer_item_t]*Renderer{},
	}, nil
}

// Release stops and destroys the renderer discovery service along
// with all the renderers found by the instance.
func (rd *RendererDiscoverer) Release() error {
	if err := rd.assertInit(); err != nil {
		return nil
	}

	// Stop discovery service.
	rd.stop()

	// Release renderers.
	for _, renderer := range rd.renderers {
		renderer.release()
	}
	rd.renderers = nil

	// Release discovery service.
	C.libvlc_renderer_discoverer_release(rd.discoverer)
	rd.discoverer = nil

	return getError()
}

// Start starts the renderer discovery service and reports discovery
// events through the specified callback function.
//   NOTE: the Stop and Release methods should not be called from the callback
//   function. Doing so will result in undefined behavior.
func (rd *RendererDiscoverer) Start(cb RendererDiscoveryCallback) error {
	if cb == nil {
		return ErrInvalidEventCallback
	}

	// Stop discovery service, if started.
	if err := rd.Stop(); err != nil {
		return err
	}

	// Retrieve event manager.
	manager, err := rd.eventManager()
	if err != nil {
		return err
	}

	// Create event callback.
	eventCallback := func(event *C.libvlc_event_t, userData interface{}) {
		if err := rd.assertInit(); err != nil {
			return
		}
		if event == nil {
			return
		}

		cRenderer := *(**C.libvlc_renderer_item_t)(unsafe.Pointer(&event.u[0]))
		if cRenderer == nil {
			return
		}

		renderer, ok := rd.renderers[cRenderer]
		if !ok {
			renderer = &Renderer{renderer: cRenderer}
			renderer.hold()
			rd.renderers[cRenderer] = renderer
		}

		switch event := Event(event._type); event {
		case RendererDiscovererItemAdded:
			cb(event, renderer)
		case RendererDiscovererItemDeleted:
			cb(event, renderer)
			delete(rd.renderers, cRenderer)
			renderer.release()
		}
	}

	// Attach discovery service events.
	events := []Event{
		RendererDiscovererItemAdded, RendererDiscovererItemDeleted,
	}

	eventIDs := make([]EventID, 0, len(events))
	for _, event := range events {
		eventID, err := manager.attach(event, nil, eventCallback, nil)
		if err != nil {
			return err
		}

		eventIDs = append(eventIDs, eventID)
	}
	defer func() {
		if rd.stopFunc == nil {
			manager.Detach(eventIDs...)
		}
	}()

	// Start discovery service.
	if C.libvlc_renderer_discoverer_start(rd.discoverer) < 0 {
		return errOrDefault(getError(), ErrRendererDiscovererStart)
	}

	rd.stopFunc = func() {
		// Detach events.
		manager.Detach(eventIDs...)

		// Stop discovery service.
		C.libvlc_renderer_discoverer_stop(rd.discoverer)
	}

	return nil
}

// Stop stops the discovery service.
func (rd *RendererDiscoverer) Stop() error {
	if err := rd.assertInit(); err != nil {
		return err
	}

	rd.stop()
	return nil
}

func (rd *RendererDiscoverer) stop() {
	if rd.stopFunc != nil {
		rd.stopFunc()
		rd.stopFunc = nil
	}
}

// eventManager returns the event manager responsible for the renderer
// discovery service.
func (rd *RendererDiscoverer) eventManager() (*EventManager, error) {
	if err := rd.assertInit(); err != nil {
		return nil, err
	}

	manager := C.libvlc_renderer_discoverer_event_manager(rd.discoverer)
	if manager == nil {
		return nil, ErrMissingEventManager
	}

	return newEventManager(manager), nil
}

func (rd *RendererDiscoverer) assertInit() error {
	if rd == nil || rd.discoverer == nil {
		return ErrRendererDiscovererNotInitialized
	}

	return nil
}
<|endoftext|>


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

package flate

import (
	"io"

	"github.com/dsnet/compress/internal"
	"github.com/dsnet/compress/internal/prefix"
)

type Reader struct {
	InputOffset  int64 // Total number of bytes read from underlying io.Reader
	OutputOffset int64 // Total number of bytes emitted from Read

	rd     prefixReader // Input source
	toRead []byte       // Uncompressed data ready to be emitted from Read
	dist   int          // The current distance
	blkLen int          // Uncompressed bytes left to read in meta-block
	cpyLen int          // Bytes left to backward dictionary copy
	last   bool         // Last block bit detected
	err    error        // Persistent error

	step      func(*Reader) // Single step of decompression work (can panic)
	stepState int           // The sub-step state for certain steps

	dict     dictDecoder    // Dynamic sliding dictionary
	litTree  prefix.Decoder // Literal and length symbol prefix decoder
	distTree prefix.Decoder // Backward distance symbol prefix decoder
}

func NewReader(r io.Reader) *Reader {
	zr := new(Reader)
	zr.Reset(r)
	return zr
}

func (zr *Reader) Reset(r io.Reader) {
	*zr = Reader{
		rd:   zr.rd,
		step: (*Reader).readBlockHeader,
		dict: zr.dict,
	}
	zr.rd.Init(r)
	zr.dict.Init(maxHistSize)
}

func (zr *Reader) Read(buf []byte) (int, error) {
	for {
		if len(zr.toRead) > 0 {
			cnt := copy(buf, zr.toRead)
			zr.toRead = zr.toRead[cnt:]
			zr.OutputOffset += int64(cnt)
			return cnt, nil
		}
		if zr.err != nil {
			return 0, zr.err
		}

		// Perform next step in decompression process.
		zr.rd.Offset = zr.InputOffset
		func() {
			defer errRecover(&zr.err)
			zr.step(zr)
		}()
		var err error
		if zr.InputOffset, err = zr.rd.Flush(); err != nil {
			zr.err = err
		}
		if zr.err != nil {
			if zr.err == internal.ErrInvalid {
				zr.err = ErrCorrupt
			}
		}
		if zr.err != nil {
			zr.toRead = zr.dict.ReadFlush() // Flush what's left in case of error
		}
	}
}

func (zr *Reader) Close() error {
	if zr.err == io.EOF || zr.err == ErrClosed {
		zr.toRead = nil // Make sure future reads fail
		zr.err = ErrClosed
		return nil
	}
	return zr.err // Return the persistent error
}

// readBlockHeader reads the block header according to RFC section 3.2.3.
func (zr *Reader) readBlockHeader() {
	if zr.last {
		zr.rd.ReadPads()
		panic(io.EOF)
	}

	zr.last = zr.rd.ReadBits(1) == 1
	switch zr.rd.ReadBits(2) {
	case 0:
		// Raw block (RFC section 3.2.4).
		zr.rd.ReadPads()

		n := uint16(zr.rd.ReadBits(16))
		nn := uint16(zr.rd.ReadBits(16))
		if n^nn != 0xffff {
			panic(ErrCorrupt)
		}
		zr.blkLen = int(n)

		// By convention, an empty block flushes the read buffer.
		if zr.blkLen == 0 {
			zr.toRead = zr.dict.ReadFlush()
			zr.step = (*Reader).readBlockHeader
			return
		}
		zr.step = (*Reader).readRawData
	case 1:
		// Fixed prefix block (RFC section 3.2.6).
		zr.litTree, zr.distTree = decLit, decDist
		zr.step = (*Reader).readBlock
	case 2:
		// Dynamic prefix block (RFC section 3.2.7).
		zr.rd.ReadPrefixCodes(&zr.litTree, &zr.distTree)
		zr.step = (*Reader).readBlock
	default:
		// Reserved block (RFC section 3.2.3).
		panic(ErrCorrupt)
	}
}

// readRawData reads raw data according to RFC section 3.2.4.
func (zr *Reader) readRawData() {
	buf := zr.dict.WriteSlice()
	if len(buf) > zr.blkLen {
		buf = buf[:zr.blkLen]
	}

	cnt, err := zr.rd.Read(buf)
	zr.blkLen -= cnt
	zr.dict.WriteMark(cnt)
	if err != nil {
		if err == io.EOF {
			err = io.ErrUnexpectedEOF
		}
		panic(err)
	}

	if zr.blkLen > 0 {
		zr.toRead = zr.dict.ReadFlush()
		zr.step = (*Reader).readRawData // We need to continue this work
		return
	}
	zr.step = (*Reader).readBlockHeader
}

// readCommands reads block commands according to RFC section 3.2.3.
func (zr *Reader) readBlock() {
	const (
		stateInit = iota // Zero value must be stateInit
		stateDict
	)

	switch zr.stepState {
	case stateInit:
		goto readLiteral
	case stateDict:
		goto copyDistance
	}

readLiteral:
	// Read literal and/or (length, distance) according to RFC section 3.2.3.
	{
		if zr.dict.AvailSize() == 0 {
			zr.toRead = zr.dict.ReadFlush()
			zr.step = (*Reader).readBlock
			zr.stepState = stateInit // Need to continue work here
			return
		}

		// Read the literal symbol.
		litSym, ok := zr.rd.TryReadSymbol(&zr.litTree)
		if !ok {
			litSym = zr.rd.ReadSymbol(&zr.litTree)
		}
		switch {
		case litSym < endBlockSym:
			zr.dict.WriteByte(byte(litSym))
			goto readLiteral
		case litSym == endBlockSym:
			zr.step = (*Reader).readBlockHeader
			zr.stepState = stateInit // Next call to readBlock must start here
			return
		case litSym < maxNumLitSyms:
			// Decode the copy length.
			rec := lenRanges[litSym-257]
			extra, ok := zr.rd.TryReadBits(uint(rec.Len))
			if !ok {
				extra = zr.rd.ReadBits(uint(rec.Len))
			}
			zr.cpyLen = int(rec.Base) + int(extra)

			// Read the distance symbol.
			distSym, ok := zr.rd.TryReadSymbol(&zr.distTree)
			if !ok {
				distSym = zr.rd.ReadSymbol(&zr.distTree)
			}
			if distSym >= maxNumDistSyms {
				panic(ErrCorrupt)
			}

			// Decode the copy distance.
			rec = distRanges[distSym]
			extra, ok = zr.rd.TryReadBits(uint(rec.Len))
			if !ok {
				extra = zr.rd.ReadBits(uint(rec.Len))
			}
			zr.dist = int(rec.Base) + int(extra)

			goto copyDistance
		default:
			panic(ErrCorrupt)
		}
	}

copyDistance:
	// Perform a backwards copy according to RFC section 3.2.3.
	{
		cnt := zr.dict.TryWriteCopy(zr.dist, zr.cpyLen)
		if cnt == 0 {
			cnt = zr.dict.WriteCopy(zr.dist, zr.cpyLen)
		}
		zr.cpyLen -= cnt

		if zr.cpyLen > 0 {
			zr.toRead = zr.dict.ReadFlush()
			zr.step = (*Reader).readBlock
			zr.stepState = stateDict // Need to continue work here
			return
		} else {
			goto readLiteral
		}
	}
}
<commit_msg>flate: make Close cause all future Read fail<commit_after>// Copyright 2015, Joe Tsai. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE.md file.

package flate

import (
	"io"

	"github.com/dsnet/compress/internal"
	"github.com/dsnet/compress/internal/prefix"
)

type Reader struct {
	InputOffset  int64 // Total number of bytes read from underlying io.Reader
	OutputOffset int64 // Total number of bytes emitted from Read

	rd     prefixReader // Input source
	toRead []byte       // Uncompressed data ready to be emitted from Read
	dist   int          // The current distance
	blkLen int          // Uncompressed bytes left to read in meta-block
	cpyLen int          // Bytes left to backward dictionary copy
	last   bool         // Last block bit detected
	err    error        // Persistent error

	step      func(*Reader) // Single step of decompression work (can panic)
	stepState int           // The sub-step state for certain steps

	dict     dictDecoder    // Dynamic sliding dictionary
	litTree  prefix.Decoder // Literal and length symbol prefix decoder
	distTree prefix.Decoder // Backward distance symbol prefix decoder
}

func NewReader(r io.Reader) *Reader {
	zr := new(Reader)
	zr.Reset(r)
	return zr
}

func (zr *Reader) Reset(r io.Reader) {
	*zr = Reader{
		rd:   zr.rd,
		step: (*Reader).readBlockHeader,
		dict: zr.dict,
	}
	zr.rd.Init(r)
	zr.dict.Init(maxHistSize)
}

func (zr *Reader) Read(buf []byte) (int, error) {
	for {
		if len(zr.toRead) > 0 {
			cnt := copy(buf, zr.toRead)
			zr.toRead = zr.toRead[cnt:]
			zr.OutputOffset += int64(cnt)
			return cnt, nil
		}
		if zr.err != nil {
			return 0, zr.err
		}

		// Perform next step in decompression process.
		zr.rd.Offset = zr.InputOffset
		func() {
			defer errRecover(&zr.err)
			zr.step(zr)
		}()
		var err error
		if zr.InputOffset, err = zr.rd.Flush(); err != nil {
			zr.err = err
		}
		if zr.err != nil {
			if zr.err == internal.ErrInvalid {
				zr.err = ErrCorrupt
			}
		}
		if zr.err != nil {
			zr.toRead = zr.dict.ReadFlush() // Flush what's left in case of error
		}
	}
}

func (zr *Reader) Close() error {
	zr.toRead = nil // Make sure future reads fail
	if zr.err == io.EOF || zr.err == ErrClosed {
		zr.err = ErrClosed
		return nil
	}
	return zr.err // Return the persistent error
}

// readBlockHeader reads the block header according to RFC section 3.2.3.
func (zr *Reader) readBlockHeader() {
	if zr.last {
		zr.rd.ReadPads()
		panic(io.EOF)
	}

	zr.last = zr.rd.ReadBits(1) == 1
	switch zr.rd.ReadBits(2) {
	case 0:
		// Raw block (RFC section 3.2.4).
		zr.rd.ReadPads()

		n := uint16(zr.rd.ReadBits(16))
		nn := uint16(zr.rd.ReadBits(16))
		if n^nn != 0xffff {
			panic(ErrCorrupt)
		}
		zr.blkLen = int(n)

		// By convention, an empty block flushes the read buffer.
		if zr.blkLen == 0 {
			zr.toRead = zr.dict.ReadFlush()
			zr.step = (*Reader).readBlockHeader
			return
		}
		zr.step = (*Reader).readRawData
	case 1:
		// Fixed prefix block (RFC section 3.2.6).
		zr.litTree, zr.distTree = decLit, decDist
		zr.step = (*Reader).readBlock
	case 2:
		// Dynamic prefix block (RFC section 3.2.7).
		zr.rd.ReadPrefixCodes(&zr.litTree, &zr.distTree)
		zr.step = (*Reader).readBlock
	default:
		// Reserved block (RFC section 3.2.3).
		panic(ErrCorrupt)
	}
}

// readRawData reads raw data according to RFC section 3.2.4.
func (zr *Reader) readRawData() {
	buf := zr.dict.WriteSlice()
	if len(buf) > zr.blkLen {
		buf = buf[:zr.blkLen]
	}

	cnt, err := zr.rd.Read(buf)
	zr.blkLen -= cnt
	zr.dict.WriteMark(cnt)
	if err != nil {
		if err == io.EOF {
			err = io.ErrUnexpectedEOF
		}
		panic(err)
	}

	if zr.blkLen > 0 {
		zr.toRead = zr.dict.ReadFlush()
		zr.step = (*Reader).readRawData // We need to continue this work
		return
	}
	zr.step = (*Reader).readBlockHeader
}

// readCommands reads block commands according to RFC section 3.2.3.
func (zr *Reader) readBlock() {
	const (
		stateInit = iota // Zero value must be stateInit
		stateDict
	)

	switch zr.stepState {
	case stateInit:
		goto readLiteral
	case stateDict:
		goto copyDistance
	}

readLiteral:
	// Read literal and/or (length, distance) according to RFC section 3.2.3.
	{
		if zr.dict.AvailSize() == 0 {
			zr.toRead = zr.dict.ReadFlush()
			zr.step = (*Reader).readBlock
			zr.stepState = stateInit // Need to continue work here
			return
		}

		// Read the literal symbol.
		litSym, ok := zr.rd.TryReadSymbol(&zr.litTree)
		if !ok {
			litSym = zr.rd.ReadSymbol(&zr.litTree)
		}
		switch {
		case litSym < endBlockSym:
			zr.dict.WriteByte(byte(litSym))
			goto readLiteral
		case litSym == endBlockSym:
			zr.step = (*Reader).readBlockHeader
			zr.stepState = stateInit // Next call to readBlock must start here
			return
		case litSym < maxNumLitSyms:
			// Decode the copy length.
			rec := lenRanges[litSym-257]
			extra, ok := zr.rd.TryReadBits(uint(rec.Len))
			if !ok {
				extra = zr.rd.ReadBits(uint(rec.Len))
			}
			zr.cpyLen = int(rec.Base) + int(extra)

			// Read the distance symbol.
			distSym, ok := zr.rd.TryReadSymbol(&zr.distTree)
			if !ok {
				distSym = zr.rd.ReadSymbol(&zr.distTree)
			}
			if distSym >= maxNumDistSyms {
				panic(ErrCorrupt)
			}

			// Decode the copy distance.
			rec = distRanges[distSym]
			extra, ok = zr.rd.TryReadBits(uint(rec.Len))
			if !ok {
				extra = zr.rd.ReadBits(uint(rec.Len))
			}
			zr.dist = int(rec.Base) + int(extra)

			goto copyDistance
		default:
			panic(ErrCorrupt)
		}
	}

copyDistance:
	// Perform a backwards copy according to RFC section 3.2.3.
	{
		cnt := zr.dict.TryWriteCopy(zr.dist, zr.cpyLen)
		if cnt == 0 {
			cnt = zr.dict.WriteCopy(zr.dist, zr.cpyLen)
		}
		zr.cpyLen -= cnt

		if zr.cpyLen > 0 {
			zr.toRead = zr.dict.ReadFlush()
			zr.step = (*Reader).readBlock
			zr.stepState = stateDict // Need to continue work here
			return
		} else {
			goto readLiteral
		}
	}
}
<|endoftext|>


<commit_before>// Copyright (c) 2016, Janoš Guljaš <janos@resenje.org>
// All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package httpClient

import (
	"fmt"
	"net"
	"net/http"
	"net/http/httptest"
	"strings"
	"testing"
	"time"
)

func TestDefaultClient(t *testing.T) {
	ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {}))
	defer ts.Close()

	r, err := Default.Get(ts.URL)
	if err != nil {
		t.Error(err)
	}
	if r == nil {
		t.Error("unexpected nil response")
	}
}

func TestClientRetry(t *testing.T) {
	l, err := net.Listen("tcp", "")
	if err != nil {
		t.Error(err)
	}
	port := l.Addr().(*net.TCPAddr).Port
	l.Close()

	go func() {
		time.Sleep(2 * time.Second)

		l, err = net.Listen("tcp", fmt.Sprintf("localhost:%d", port))
		if err != nil {
			t.Error(err)
		}
		defer l.Close()
		server := http.Server{
			Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {}),
		}
		if err := server.Serve(l); err != nil {
			t.Error(err)
		}
	}()

	r, err := New(&Options{RetryTimeMax: 10 * time.Second}).Get(fmt.Sprintf("http://localhost:%d", port))
	if err != nil {
		t.Error(err)
	}
	if r == nil {
		t.Error("unexpected nil response")
	}

	r, err = (&http.Client{Transport: Transport(&Options{RetryTimeMax: 10 * time.Second})}).Get(fmt.Sprintf("http://localhost:%d", port))
	if err != nil {
		t.Error(err)
	}
	if r == nil {
		t.Error("unexpected nil response")
	}
}

func TestClientRetryFailure(t *testing.T) {
	l, err := net.Listen("tcp", "")
	if err != nil {
		t.Error(err)
	}
	port := l.Addr().(*net.TCPAddr).Port
	l.Close()

	go func() {
		time.Sleep(2 * time.Second)

		l, err := net.Listen("tcp", fmt.Sprintf("localhost:%d", port))
		if err != nil {
			panic(err)
		}
		defer l.Close()
		server := http.Server{
			Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {}),
		}
		if err := server.Serve(l); err != nil {
			panic(err)
		}
	}()

	r, err := New(&Options{RetryTimeMax: 1 * time.Second}).Get(fmt.Sprintf("http://localhost:%d", port))
	if err == nil || !strings.Contains(err.Error(), "connection refused") {
		t.Errorf("expected connection refused error, got %#v", err)
	}
	if r != nil {
		t.Error("unexpected not-nil response")
	}

	r, err = (&http.Client{Transport: Transport(&Options{RetryTimeMax: 1 * time.Second})}).Get(fmt.Sprintf("http://localhost:%d", port))
	if err == nil || !strings.Contains(err.Error(), "connection refused") {
		t.Errorf("expected connection refused error, got %#v", err)
	}
	if r != nil {
		t.Error("unexpected not-nil response")
	}
}

func TestClientRedirectHeaders(t *testing.T) {
	want := "test web"
	ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		if r.URL.Path == "/redirect" {
			http.Redirect(w, r, "/", http.StatusFound)
		}
		got := r.Header.Get("X-Test")
		if got != want {
			t.Errorf("expected X-Test header %q, got %q", want, got)
		}
	}))
	defer ts.Close()

	req, err := http.NewRequest("GET", ts.URL+"/redirect", nil)
	if err != nil {
		t.Error(err)
	}
	req.Header.Set("X-Test", want)
	r, err := Default.Do(req)
	if err != nil {
		t.Error(err)
	}
	if r == nil {
		t.Error("unexpected nil response")
	}
}

func TestOptionsMarshalJSON(t *testing.T) {
	o := Options{
		Timeout:             30 * time.Second,
		KeepAlive:           3 * time.Minute,
		TLSHandshakeTimeout: 100 * time.Millisecond,
		TLSSkipVerify:       true,
		RetryTimeMax:        1 * time.Hour,
		RetrySleepMax:       10 * time.Second,
		RetrySleepBase:      10 * time.Microsecond,
	}

	b, err := o.MarshalJSON()
	if err != nil {
		t.Error(err.Error())
	}
	got := string(b)
	want := "{\"timeout\":\"30s\",\"keep-alive\":\"3m0s\",\"tls-handshake-timeout\":\"100ms\",\"tls-skip-verify\":true,\"retry-time-max\":\"1h0m0s\",\"retry-sleep-max\":\"10s\",\"retry-sleep-base\":\"10µs\"}"
	if got != want {
		t.Errorf("expected json %q, got %q", want, got)
	}
}

func TestOptionsUnmarshalJSON(t *testing.T) {
	o := &Options{}
	if err := o.UnmarshalJSON([]byte("{\"timeout\":\"30s\",\"keep-alive\":\"3m0s\",\"tls-handshake-timeout\":\"100ms\",\"tls-skip-verify\":true,\"retry-time-max\":\"1h0m0s\",\"retry-sleep-max\":\"10s\",\"retry-sleep-base\":\"10µs\"}")); err != nil {
		t.Error(err.Error())
	}

	Timeout := 30 * time.Second
	if o.Timeout != Timeout {
		t.Errorf("expected Timeout %v, got %v", Timeout, o.Timeout)
	}
	KeepAlive := 3 * time.Minute
	if o.KeepAlive != KeepAlive {
		t.Errorf("expected KeepAlive %v, got %v", KeepAlive, o.KeepAlive)
	}
	TLSHandshakeTimeout := 100 * time.Millisecond
	if o.TLSHandshakeTimeout != TLSHandshakeTimeout {
		t.Errorf("expected TLSHandshakeTimeout %v, got %v", TLSHandshakeTimeout, o.TLSHandshakeTimeout)
	}
	TLSSkipVerify := true
	if o.TLSSkipVerify != TLSSkipVerify {
		t.Errorf("expected TLSSkipVerify %v, got %v", TLSSkipVerify, o.TLSSkipVerify)
	}
	RetryTimeMax := 1 * time.Hour
	if o.RetryTimeMax != RetryTimeMax {
		t.Errorf("expected RetryTimeMax %v, got %v", RetryTimeMax, o.RetryTimeMax)
	}
	RetrySleepMax := 10 * time.Second
	if o.RetrySleepMax != RetrySleepMax {
		t.Errorf("expected RetrySleepMax %v, got %v", RetrySleepMax, o.RetrySleepMax)
	}
	RetrySleepBase := 10 * time.Microsecond
	if o.RetrySleepBase != RetrySleepBase {
		t.Errorf("expected RetrySleepBase %v, got %v", RetrySleepBase, o.RetrySleepBase)
	}
}

func TestOptionsUnmarshalJSONError(t *testing.T) {
	o := &Options{}
	e := "invalid character '1' looking for beginning of object key string"
	if err := o.UnmarshalJSON([]byte("{1}")); err == nil || err.Error() != e {
		t.Errorf("expected error %q, got %v", e, err)
	}
}
<commit_msg>Update client/http TestClientRetryFailure test error reporting<commit_after>// Copyright (c) 2016, Janoš Guljaš <janos@resenje.org>
// All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package httpClient

import (
	"fmt"
	"net"
	"net/http"
	"net/http/httptest"
	"strings"
	"testing"
	"time"
)

func TestDefaultClient(t *testing.T) {
	ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {}))
	defer ts.Close()

	r, err := Default.Get(ts.URL)
	if err != nil {
		t.Error(err)
	}
	if r == nil {
		t.Error("unexpected nil response")
	}
}

func TestClientRetry(t *testing.T) {
	l, err := net.Listen("tcp", "")
	if err != nil {
		t.Error(err)
	}
	port := l.Addr().(*net.TCPAddr).Port
	l.Close()

	go func() {
		time.Sleep(2 * time.Second)

		l, err = net.Listen("tcp", fmt.Sprintf("localhost:%d", port))
		if err != nil {
			t.Error(err)
		}
		defer l.Close()
		server := http.Server{
			Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {}),
		}
		if err := server.Serve(l); err != nil {
			t.Error(err)
		}
	}()

	r, err := New(&Options{RetryTimeMax: 10 * time.Second}).Get(fmt.Sprintf("http://localhost:%d", port))
	if err != nil {
		t.Error(err)
	}
	if r == nil {
		t.Error("unexpected nil response")
	}

	r, err = (&http.Client{Transport: Transport(&Options{RetryTimeMax: 10 * time.Second})}).Get(fmt.Sprintf("http://localhost:%d", port))
	if err != nil {
		t.Error(err)
	}
	if r == nil {
		t.Error("unexpected nil response")
	}
}

func TestClientRetryFailure(t *testing.T) {
	l, err := net.Listen("tcp", "")
	if err != nil {
		t.Error(err)
	}
	port := l.Addr().(*net.TCPAddr).Port
	l.Close()

	go func() {
		time.Sleep(2 * time.Second)

		l, err := net.Listen("tcp", fmt.Sprintf("localhost:%d", port))
		if err != nil {
			panic(err)
		}
		defer l.Close()
		server := http.Server{
			Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {}),
		}
		if err := server.Serve(l); err != nil {
			panic(err)
		}
	}()

	r, err := New(&Options{RetryTimeMax: 1 * time.Second}).Get(fmt.Sprintf("http://localhost:%d", port))
	if err == nil || !strings.Contains(err.Error(), "connection refused") {
		t.Errorf("expected connection refused error, got %q", err.Error())
	}
	if r != nil {
		t.Error("unexpected not-nil response")
	}

	r, err = (&http.Client{Transport: Transport(&Options{RetryTimeMax: 1 * time.Second})}).Get(fmt.Sprintf("http://localhost:%d", port))
	if err == nil || !strings.Contains(err.Error(), "connection refused") {
		t.Errorf("expected connection refused error, got %q", err.Error())
	}
	if r != nil {
		t.Error("unexpected not-nil response")
	}
}

func TestClientRedirectHeaders(t *testing.T) {
	want := "test web"
	ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		if r.URL.Path == "/redirect" {
			http.Redirect(w, r, "/", http.StatusFound)
		}
		got := r.Header.Get("X-Test")
		if got != want {
			t.Errorf("expected X-Test header %q, got %q", want, got)
		}
	}))
	defer ts.Close()

	req, err := http.NewRequest("GET", ts.URL+"/redirect", nil)
	if err != nil {
		t.Error(err)
	}
	req.Header.Set("X-Test", want)
	r, err := Default.Do(req)
	if err != nil {
		t.Error(err)
	}
	if r == nil {
		t.Error("unexpected nil response")
	}
}

func TestOptionsMarshalJSON(t *testing.T) {
	o := Options{
		Timeout:             30 * time.Second,
		KeepAlive:           3 * time.Minute,
		TLSHandshakeTimeout: 100 * time.Millisecond,
		TLSSkipVerify:       true,
		RetryTimeMax:        1 * time.Hour,
		RetrySleepMax:       10 * time.Second,
		RetrySleepBase:      10 * time.Microsecond,
	}

	b, err := o.MarshalJSON()
	if err != nil {
		t.Error(err.Error())
	}
	got := string(b)
	want := "{\"timeout\":\"30s\",\"keep-alive\":\"3m0s\",\"tls-handshake-timeout\":\"100ms\",\"tls-skip-verify\":true,\"retry-time-max\":\"1h0m0s\",\"retry-sleep-max\":\"10s\",\"retry-sleep-base\":\"10µs\"}"
	if got != want {
		t.Errorf("expected json %q, got %q", want, got)
	}
}

func TestOptionsUnmarshalJSON(t *testing.T) {
	o := &Options{}
	if err := o.UnmarshalJSON([]byte("{\"timeout\":\"30s\",\"keep-alive\":\"3m0s\",\"tls-handshake-timeout\":\"100ms\",\"tls-skip-verify\":true,\"retry-time-max\":\"1h0m0s\",\"retry-sleep-max\":\"10s\",\"retry-sleep-base\":\"10µs\"}")); err != nil {
		t.Error(err.Error())
	}

	Timeout := 30 * time.Second
	if o.Timeout != Timeout {
		t.Errorf("expected Timeout %v, got %v", Timeout, o.Timeout)
	}
	KeepAlive := 3 * time.Minute
	if o.KeepAlive != KeepAlive {
		t.Errorf("expected KeepAlive %v, got %v", KeepAlive, o.KeepAlive)
	}
	TLSHandshakeTimeout := 100 * time.Millisecond
	if o.TLSHandshakeTimeout != TLSHandshakeTimeout {
		t.Errorf("expected TLSHandshakeTimeout %v, got %v", TLSHandshakeTimeout, o.TLSHandshakeTimeout)
	}
	TLSSkipVerify := true
	if o.TLSSkipVerify != TLSSkipVerify {
		t.Errorf("expected TLSSkipVerify %v, got %v", TLSSkipVerify, o.TLSSkipVerify)
	}
	RetryTimeMax := 1 * time.Hour
	if o.RetryTimeMax != RetryTimeMax {
		t.Errorf("expected RetryTimeMax %v, got %v", RetryTimeMax, o.RetryTimeMax)
	}
	RetrySleepMax := 10 * time.Second
	if o.RetrySleepMax != RetrySleepMax {
		t.Errorf("expected RetrySleepMax %v, got %v", RetrySleepMax, o.RetrySleepMax)
	}
	RetrySleepBase := 10 * time.Microsecond
	if o.RetrySleepBase != RetrySleepBase {
		t.Errorf("expected RetrySleepBase %v, got %v", RetrySleepBase, o.RetrySleepBase)
	}
}

func TestOptionsUnmarshalJSONError(t *testing.T) {
	o := &Options{}
	e := "invalid character '1' looking for beginning of object key string"
	if err := o.UnmarshalJSON([]byte("{1}")); err == nil || err.Error() != e {
		t.Errorf("expected error %q, got %v", e, err)
	}
}
<|endoftext|>


<commit_before>// ----------------------------------------------------------------------------
//
//     ***     AUTO GENERATED CODE    ***    Type: MMv1     ***
//
// ----------------------------------------------------------------------------
//
//     This file is automatically generated by Magic Modules and manual
//     changes will be clobbered when the file is regenerated.
//
//     Please read more about how to change this file in
//     .github/CONTRIBUTING.md.
//
// ----------------------------------------------------------------------------

package google

import "reflect"

const ComputeBackendBucketAssetType string = "compute.googleapis.com/BackendBucket"

func resourceConverterComputeBackendBucket() ResourceConverter {
	return ResourceConverter{
		AssetType: ComputeBackendBucketAssetType,
		Convert:   GetComputeBackendBucketCaiObject,
	}
}

func GetComputeBackendBucketCaiObject(d TerraformResourceData, config *Config) ([]Asset, error) {
	name, err := assetName(d, config, "//compute.googleapis.com/projects/{{project}}/global/backendBuckets/{{name}}")
	if err != nil {
		return []Asset{}, err
	}
	if obj, err := GetComputeBackendBucketApiObject(d, config); err == nil {
		return []Asset{{
			Name: name,
			Type: ComputeBackendBucketAssetType,
			Resource: &AssetResource{
				Version:              "v1",
				DiscoveryDocumentURI: "https://www.googleapis.com/discovery/v1/apis/compute/v1/rest",
				DiscoveryName:        "BackendBucket",
				Data:                 obj,
			},
		}}, nil
	} else {
		return []Asset{}, err
	}
}

func GetComputeBackendBucketApiObject(d TerraformResourceData, config *Config) (map[string]interface{}, error) {
	obj := make(map[string]interface{})
	bucketNameProp, err := expandComputeBackendBucketBucketName(d.Get("bucket_name"), d, config)
	if err != nil {
		return nil, err
	} else if v, ok := d.GetOkExists("bucket_name"); !isEmptyValue(reflect.ValueOf(bucketNameProp)) && (ok || !reflect.DeepEqual(v, bucketNameProp)) {
		obj["bucketName"] = bucketNameProp
	}
	cdnPolicyProp, err := expandComputeBackendBucketCdnPolicy(d.Get("cdn_policy"), d, config)
	if err != nil {
		return nil, err
	} else if v, ok := d.GetOkExists("cdn_policy"); !isEmptyValue(reflect.ValueOf(cdnPolicyProp)) && (ok || !reflect.DeepEqual(v, cdnPolicyProp)) {
		obj["cdnPolicy"] = cdnPolicyProp
	}
	customResponseHeadersProp, err := expandComputeBackendBucketCustomResponseHeaders(d.Get("custom_response_headers"), d, config)
	if err != nil {
		return nil, err
	} else if v, ok := d.GetOkExists("custom_response_headers"); !isEmptyValue(reflect.ValueOf(customResponseHeadersProp)) && (ok || !reflect.DeepEqual(v, customResponseHeadersProp)) {
		obj["customResponseHeaders"] = customResponseHeadersProp
	}
	descriptionProp, err := expandComputeBackendBucketDescription(d.Get("description"), d, config)
	if err != nil {
		return nil, err
	} else if v, ok := d.GetOkExists("description"); !isEmptyValue(reflect.ValueOf(descriptionProp)) && (ok || !reflect.DeepEqual(v, descriptionProp)) {
		obj["description"] = descriptionProp
	}
	enableCdnProp, err := expandComputeBackendBucketEnableCdn(d.Get("enable_cdn"), d, config)
	if err != nil {
		return nil, err
	} else if v, ok := d.GetOkExists("enable_cdn"); !isEmptyValue(reflect.ValueOf(enableCdnProp)) && (ok || !reflect.DeepEqual(v, enableCdnProp)) {
		obj["enableCdn"] = enableCdnProp
	}
	nameProp, err := expandComputeBackendBucketName(d.Get("name"), d, config)
	if err != nil {
		return nil, err
	} else if v, ok := d.GetOkExists("name"); !isEmptyValue(reflect.ValueOf(nameProp)) && (ok || !reflect.DeepEqual(v, nameProp)) {
		obj["name"] = nameProp
	}

	return obj, nil
}

func expandComputeBackendBucketBucketName(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) {
	return v, nil
}

func expandComputeBackendBucketCdnPolicy(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) {
	l := v.([]interface{})
	if len(l) == 0 || l[0] == nil {
		return nil, nil
	}
	raw := l[0]
	original := raw.(map[string]interface{})
	transformed := make(map[string]interface{})

	transformedSignedUrlCacheMaxAgeSec, err := expandComputeBackendBucketCdnPolicySignedUrlCacheMaxAgeSec(original["signed_url_cache_max_age_sec"], d, config)
	if err != nil {
		return nil, err
	} else if val := reflect.ValueOf(transformedSignedUrlCacheMaxAgeSec); val.IsValid() && !isEmptyValue(val) {
		transformed["signedUrlCacheMaxAgeSec"] = transformedSignedUrlCacheMaxAgeSec
	}

	transformedDefaultTtl, err := expandComputeBackendBucketCdnPolicyDefaultTtl(original["default_ttl"], d, config)
	if err != nil {
		return nil, err
	} else if val := reflect.ValueOf(transformedDefaultTtl); val.IsValid() && !isEmptyValue(val) {
		transformed["defaultTtl"] = transformedDefaultTtl
	}

	transformedMaxTtl, err := expandComputeBackendBucketCdnPolicyMaxTtl(original["max_ttl"], d, config)
	if err != nil {
		return nil, err
	} else if val := reflect.ValueOf(transformedMaxTtl); val.IsValid() && !isEmptyValue(val) {
		transformed["maxTtl"] = transformedMaxTtl
	}

	transformedClientTtl, err := expandComputeBackendBucketCdnPolicyClientTtl(original["client_ttl"], d, config)
	if err != nil {
		return nil, err
	} else if val := reflect.ValueOf(transformedClientTtl); val.IsValid() && !isEmptyValue(val) {
		transformed["clientTtl"] = transformedClientTtl
	}

	transformedNegativeCaching, err := expandComputeBackendBucketCdnPolicyNegativeCaching(original["negative_caching"], d, config)
	if err != nil {
		return nil, err
	} else {
		transformed["negativeCaching"] = transformedNegativeCaching
	}

	transformedNegativeCachingPolicy, err := expandComputeBackendBucketCdnPolicyNegativeCachingPolicy(original["negative_caching_policy"], d, config)
	if err != nil {
		return nil, err
	} else if val := reflect.ValueOf(transformedNegativeCachingPolicy); val.IsValid() && !isEmptyValue(val) {
		transformed["negativeCachingPolicy"] = transformedNegativeCachingPolicy
	}

	transformedCacheMode, err := expandComputeBackendBucketCdnPolicyCacheMode(original["cache_mode"], d, config)
	if err != nil {
		return nil, err
	} else if val := reflect.ValueOf(transformedCacheMode); val.IsValid() && !isEmptyValue(val) {
		transformed["cacheMode"] = transformedCacheMode
	}

	transformedServeWhileStale, err := expandComputeBackendBucketCdnPolicyServeWhileStale(original["serve_while_stale"], d, config)
	if err != nil {
		return nil, err
	} else {
		transformed["serveWhileStale"] = transformedServeWhileStale
	}

	return transformed, nil
}

func expandComputeBackendBucketCdnPolicySignedUrlCacheMaxAgeSec(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) {
	return v, nil
}

func expandComputeBackendBucketCdnPolicyDefaultTtl(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) {
	return v, nil
}

func expandComputeBackendBucketCdnPolicyMaxTtl(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) {
	return v, nil
}

func expandComputeBackendBucketCdnPolicyClientTtl(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) {
	return v, nil
}

func expandComputeBackendBucketCdnPolicyNegativeCaching(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) {
	return v, nil
}

func expandComputeBackendBucketCdnPolicyNegativeCachingPolicy(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) {
	l := v.([]interface{})
	req := make([]interface{}, 0, len(l))
	for _, raw := range l {
		if raw == nil {
			continue
		}
		original := raw.(map[string]interface{})
		transformed := make(map[string]interface{})

		transformedCode, err := expandComputeBackendBucketCdnPolicyNegativeCachingPolicyCode(original["code"], d, config)
		if err != nil {
			return nil, err
		} else if val := reflect.ValueOf(transformedCode); val.IsValid() && !isEmptyValue(val) {
			transformed["code"] = transformedCode
		}

		transformedTtl, err := expandComputeBackendBucketCdnPolicyNegativeCachingPolicyTtl(original["ttl"], d, config)
		if err != nil {
			return nil, err
		} else if val := reflect.ValueOf(transformedTtl); val.IsValid() && !isEmptyValue(val) {
			transformed["ttl"] = transformedTtl
		}

		req = append(req, transformed)
	}
	return req, nil
}

func expandComputeBackendBucketCdnPolicyNegativeCachingPolicyCode(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) {
	return v, nil
}

func expandComputeBackendBucketCdnPolicyNegativeCachingPolicyTtl(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) {
	return v, nil
}

func expandComputeBackendBucketCdnPolicyCacheMode(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) {
	return v, nil
}

func expandComputeBackendBucketCdnPolicyServeWhileStale(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) {
	return v, nil
}

func expandComputeBackendBucketCustomResponseHeaders(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) {
	return v, nil
}

func expandComputeBackendBucketDescription(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) {
	return v, nil
}

func expandComputeBackendBucketEnableCdn(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) {
	return v, nil
}

func expandComputeBackendBucketName(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) {
	return v, nil
}
<commit_msg>fixed a bug in update (#5336) (#340)<commit_after>// ----------------------------------------------------------------------------
//
//     ***     AUTO GENERATED CODE    ***    Type: MMv1     ***
//
// ----------------------------------------------------------------------------
//
//     This file is automatically generated by Magic Modules and manual
//     changes will be clobbered when the file is regenerated.
//
//     Please read more about how to change this file in
//     .github/CONTRIBUTING.md.
//
// ----------------------------------------------------------------------------

package google

import "reflect"

const ComputeBackendBucketAssetType string = "compute.googleapis.com/BackendBucket"

func resourceConverterComputeBackendBucket() ResourceConverter {
	return ResourceConverter{
		AssetType: ComputeBackendBucketAssetType,
		Convert:   GetComputeBackendBucketCaiObject,
	}
}

func GetComputeBackendBucketCaiObject(d TerraformResourceData, config *Config) ([]Asset, error) {
	name, err := assetName(d, config, "//compute.googleapis.com/projects/{{project}}/global/backendBuckets/{{name}}")
	if err != nil {
		return []Asset{}, err
	}
	if obj, err := GetComputeBackendBucketApiObject(d, config); err == nil {
		return []Asset{{
			Name: name,
			Type: ComputeBackendBucketAssetType,
			Resource: &AssetResource{
				Version:              "v1",
				DiscoveryDocumentURI: "https://www.googleapis.com/discovery/v1/apis/compute/v1/rest",
				DiscoveryName:        "BackendBucket",
				Data:                 obj,
			},
		}}, nil
	} else {
		return []Asset{}, err
	}
}

func GetComputeBackendBucketApiObject(d TerraformResourceData, config *Config) (map[string]interface{}, error) {
	obj := make(map[string]interface{})
	bucketNameProp, err := expandComputeBackendBucketBucketName(d.Get("bucket_name"), d, config)
	if err != nil {
		return nil, err
	} else if v, ok := d.GetOkExists("bucket_name"); !isEmptyValue(reflect.ValueOf(bucketNameProp)) && (ok || !reflect.DeepEqual(v, bucketNameProp)) {
		obj["bucketName"] = bucketNameProp
	}
	cdnPolicyProp, err := expandComputeBackendBucketCdnPolicy(d.Get("cdn_policy"), d, config)
	if err != nil {
		return nil, err
	} else if v, ok := d.GetOkExists("cdn_policy"); !isEmptyValue(reflect.ValueOf(cdnPolicyProp)) && (ok || !reflect.DeepEqual(v, cdnPolicyProp)) {
		obj["cdnPolicy"] = cdnPolicyProp
	}
	customResponseHeadersProp, err := expandComputeBackendBucketCustomResponseHeaders(d.Get("custom_response_headers"), d, config)
	if err != nil {
		return nil, err
	} else if v, ok := d.GetOkExists("custom_response_headers"); !isEmptyValue(reflect.ValueOf(customResponseHeadersProp)) && (ok || !reflect.DeepEqual(v, customResponseHeadersProp)) {
		obj["customResponseHeaders"] = customResponseHeadersProp
	}
	descriptionProp, err := expandComputeBackendBucketDescription(d.Get("description"), d, config)
	if err != nil {
		return nil, err
	} else if v, ok := d.GetOkExists("description"); !isEmptyValue(reflect.ValueOf(descriptionProp)) && (ok || !reflect.DeepEqual(v, descriptionProp)) {
		obj["description"] = descriptionProp
	}
	enableCdnProp, err := expandComputeBackendBucketEnableCdn(d.Get("enable_cdn"), d, config)
	if err != nil {
		return nil, err
	} else if v, ok := d.GetOkExists("enable_cdn"); !isEmptyValue(reflect.ValueOf(enableCdnProp)) && (ok || !reflect.DeepEqual(v, enableCdnProp)) {
		obj["enableCdn"] = enableCdnProp
	}
	nameProp, err := expandComputeBackendBucketName(d.Get("name"), d, config)
	if err != nil {
		return nil, err
	} else if v, ok := d.GetOkExists("name"); !isEmptyValue(reflect.ValueOf(nameProp)) && (ok || !reflect.DeepEqual(v, nameProp)) {
		obj["name"] = nameProp
	}

	return obj, nil
}

func expandComputeBackendBucketBucketName(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) {
	return v, nil
}

func expandComputeBackendBucketCdnPolicy(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) {
	l := v.([]interface{})
	if len(l) == 0 || l[0] == nil {
		return nil, nil
	}
	raw := l[0]
	original := raw.(map[string]interface{})
	transformed := make(map[string]interface{})

	transformedSignedUrlCacheMaxAgeSec, err := expandComputeBackendBucketCdnPolicySignedUrlCacheMaxAgeSec(original["signed_url_cache_max_age_sec"], d, config)
	if err != nil {
		return nil, err
	} else {
		transformed["signedUrlCacheMaxAgeSec"] = transformedSignedUrlCacheMaxAgeSec
	}

	transformedDefaultTtl, err := expandComputeBackendBucketCdnPolicyDefaultTtl(original["default_ttl"], d, config)
	if err != nil {
		return nil, err
	} else {
		transformed["defaultTtl"] = transformedDefaultTtl
	}

	transformedMaxTtl, err := expandComputeBackendBucketCdnPolicyMaxTtl(original["max_ttl"], d, config)
	if err != nil {
		return nil, err
	} else {
		transformed["maxTtl"] = transformedMaxTtl
	}

	transformedClientTtl, err := expandComputeBackendBucketCdnPolicyClientTtl(original["client_ttl"], d, config)
	if err != nil {
		return nil, err
	} else {
		transformed["clientTtl"] = transformedClientTtl
	}

	transformedNegativeCaching, err := expandComputeBackendBucketCdnPolicyNegativeCaching(original["negative_caching"], d, config)
	if err != nil {
		return nil, err
	} else {
		transformed["negativeCaching"] = transformedNegativeCaching
	}

	transformedNegativeCachingPolicy, err := expandComputeBackendBucketCdnPolicyNegativeCachingPolicy(original["negative_caching_policy"], d, config)
	if err != nil {
		return nil, err
	} else if val := reflect.ValueOf(transformedNegativeCachingPolicy); val.IsValid() && !isEmptyValue(val) {
		transformed["negativeCachingPolicy"] = transformedNegativeCachingPolicy
	}

	transformedCacheMode, err := expandComputeBackendBucketCdnPolicyCacheMode(original["cache_mode"], d, config)
	if err != nil {
		return nil, err
	} else if val := reflect.ValueOf(transformedCacheMode); val.IsValid() && !isEmptyValue(val) {
		transformed["cacheMode"] = transformedCacheMode
	}

	transformedServeWhileStale, err := expandComputeBackendBucketCdnPolicyServeWhileStale(original["serve_while_stale"], d, config)
	if err != nil {
		return nil, err
	} else {
		transformed["serveWhileStale"] = transformedServeWhileStale
	}

	return transformed, nil
}

func expandComputeBackendBucketCdnPolicySignedUrlCacheMaxAgeSec(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) {
	return v, nil
}

func expandComputeBackendBucketCdnPolicyDefaultTtl(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) {
	return v, nil
}

func expandComputeBackendBucketCdnPolicyMaxTtl(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) {
	return v, nil
}

func expandComputeBackendBucketCdnPolicyClientTtl(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) {
	return v, nil
}

func expandComputeBackendBucketCdnPolicyNegativeCaching(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) {
	return v, nil
}

func expandComputeBackendBucketCdnPolicyNegativeCachingPolicy(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) {
	l := v.([]interface{})
	req := make([]interface{}, 0, len(l))
	for _, raw := range l {
		if raw == nil {
			continue
		}
		original := raw.(map[string]interface{})
		transformed := make(map[string]interface{})

		transformedCode, err := expandComputeBackendBucketCdnPolicyNegativeCachingPolicyCode(original["code"], d, config)
		if err != nil {
			return nil, err
		} else if val := reflect.ValueOf(transformedCode); val.IsValid() && !isEmptyValue(val) {
			transformed["code"] = transformedCode
		}

		transformedTtl, err := expandComputeBackendBucketCdnPolicyNegativeCachingPolicyTtl(original["ttl"], d, config)
		if err != nil {
			return nil, err
		} else {
			transformed["ttl"] = transformedTtl
		}

		req = append(req, transformed)
	}
	return req, nil
}

func expandComputeBackendBucketCdnPolicyNegativeCachingPolicyCode(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) {
	return v, nil
}

func expandComputeBackendBucketCdnPolicyNegativeCachingPolicyTtl(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) {
	return v, nil
}

func expandComputeBackendBucketCdnPolicyCacheMode(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) {
	return v, nil
}

func expandComputeBackendBucketCdnPolicyServeWhileStale(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) {
	return v, nil
}

func expandComputeBackendBucketCustomResponseHeaders(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) {
	return v, nil
}

func expandComputeBackendBucketDescription(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) {
	return v, nil
}

func expandComputeBackendBucketEnableCdn(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) {
	return v, nil
}

func expandComputeBackendBucketName(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) {
	return v, nil
}
<|endoftext|>


<commit_before>// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
//
// Contributor: Julien Vehent jvehent@mozilla.com [:ulfr]
// Contributor: Aaron Meihm ameihm@mozilla.com [:alm]

package main

import (
	"encoding/json"
	"flag"
	"fmt"
	"os"
	"strconv"
	"strings"
	"time"

	"mig.ninja/mig"
	"mig.ninja/mig/client"
	migdbsearch "mig.ninja/mig/database/search"
)

func usage() {
	fmt.Fprintf(os.Stderr, `%s <query> - Search for MIG Agents

Usage: %s [-V] [-c path] -p "console style query" | -t "target style query"

The -p or -t flag must be specified to run a search.

The -V flag can be used to display MIG version.

Use -c to specify an alternate path to .migrc (by default, $HOME/.migrc)

CONSOLE MODE QUERY
------------------

The console mode query allows specification of a query string as would be passed
in mig-console using "search agent". It returns all matching agents.

EXAMPLE CONSOLE MODE QUERIES
----------------------------

All online agents:
  $ mig-agent-search -p "status=online"

All agents regardless of status:
  $ mig-agent-search -p "status=%%"

See the output of "search help" in mig-console for additional information on
how to format these queries.

TARGET MODE QUERY
-----------------

The target mode query allows specification of an agent targeting string as would
be passed to the -t flag using MIG command line. This evaluates agents using the
targeting string as the command line would, returning matching agents.

A search query is a SQL WHERE condition. It can filter on any field present in
the MIG Agents table.
	     Column      |           Type
	-----------------+-------------------------
	 id              | numeric
	 name            | character varying(2048)
	 queueloc        | character varying(2048)
	 mode            | character varying(2048)
	 version         | character varying(2048)
	 pid             | integer
	 starttime       | timestamp with time zone
	 destructiontime | timestamp with time zone
	 heartbeattime   | timestamp with time zone
	 status          | character varying(255)
	 environment     | json
	 tags            | json

The "environment" and "tags" fields are free JSON fields and can be queried using
Postgresql's JSON querying syntax.

Below is an example of environment document:
	{
	    "addresses": [
		"172.21.0.3/20",
		"fe80::3602:86ff:fe2b:6fdd/64"
	    ],
	    "arch": "amd64",
	    "ident": "Debian testing-updates sid",
	    "init": "upstart",
	    "isproxied": false,
	    "os": "linux",
	    "publicip": "172.21.0.3"
	}

Below is an example of tags document:
	{"operator":"linuxwall"}

EXAMPLE TARGET MODE QUERIES
---------------------------

Agent name "myserver.example.net"
  $ mig-agent-search -t "name='myserver.example.net'"

All Linux agents:
  $ mig-agent-search -t "environment->>'os'='linux'"

Ubuntu agents running 32 bits
  $ mig-agent-search -t "environment->>'ident' LIKE 'Ubuntu%%' AND environment->>'arch'='386'"

MacOS agents in datacenter SCL3
  $ mig-agent-search -t "environment->>'os'='darwin' AND name LIKE '%%\.scl3\.%%'"

Agents with uptime greater than 30 days
  $ mig-agent-search -t "starttime < NOW() - INTERVAL '30 days'"

Linux agents in checkin mode that are currently idle but woke up in the last hour
  $ mig-agent-search -t "mode='checkin' AND environment->>'os'='linux' AND status='idle' AND starttime > NOW() - INTERVAL '1 hour'"

Agents operated by team "opsec"
  $ mig-agent-search -t "tags->>'operator'='opsec'"

Command line flags:`, os.Args[0], os.Args[0])
}

func main() {
	defer func() {
		if e := recover(); e != nil {
			fmt.Fprintf(os.Stderr, "error: %v\n", e)
			os.Exit(1)
		}
	}()

	flag.Usage = usage

	var err error
	homedir := client.FindHomedir()
	var config = flag.String("c", homedir+"/.migrc", "Load configuration from file")
	var showversion = flag.Bool("V", false, "Show build version and exit")
	var paramSearch = flag.String("p", "", "Search using mig-console search style query")
	var targetSearch = flag.String("t", "", "Search using agent targeting string")
	flag.Parse()

	if *showversion {
		fmt.Println(mig.Version)
		os.Exit(0)
	}

	// Instantiate an API client
	conf, err := client.ReadConfiguration(*config)
	if err != nil {
		panic(err)
	}
	conf, err = client.ReadEnvConfiguration(conf)
	if err != nil {
		panic(err)
	}
	cli, err := client.NewClient(conf, "agent-search-"+mig.Version)
	if err != nil {
		panic(err)
	}

	if *paramSearch != "" {
		// Search using mig-console style keywords
		p, err := parseSearchQuery(*paramSearch)
		if err != nil {
			panic(err)
		}
		resources, err := cli.GetAPIResource("search?" + p.String())
		if err != nil {
			panic(err)
		}
		fmt.Println("name; id; status; version; mode; os; arch; pid; starttime; heartbeattime; tags; environment")
		for _, item := range resources.Collection.Items {
			for _, data := range item.Data {
				if data.Name != "agent" {
					continue
				}
				agt, err := client.ValueToAgent(data.Value)
				if err != nil {
					panic(err)
				}
				err = printAgent(agt)
				if err != nil {
					panic(err)
				}
			}
		}
	} else if *targetSearch != "" {
		// Search using an agent targeting string
		agents, err := cli.EvaluateAgentTarget(*targetSearch)
		if err != nil {
			panic(err)
		}
		fmt.Println("name; id; status; version; mode; os; arch; pid; starttime; heartbeattime; tags; environment")
		for _, agt := range agents {
			err = printAgent(agt)
			if err != nil {
				panic(err)
			}
		}
	} else {
		panic("must specify -p or -t, see help")
	}
	os.Exit(0)
}

func printAgent(agt mig.Agent) error {
	tags, err := json.Marshal(agt.Tags)
	if err != nil {
		return err
	}
	env, err := json.Marshal(agt.Env)
	if err != nil {
		return err
	}
	fmt.Printf("%s; %.0f; %s; %s; %s; %s; %s; %d; %s; %s; %s; %s\n",
		agt.Name, agt.ID, agt.Status, agt.Version, agt.Mode, agt.Env.OS,
		agt.Env.Arch, agt.PID, agt.StartTime.Format(time.RFC3339),
		agt.HeartBeatTS.Format(time.RFC3339), tags, env)
	return nil
}

// Transform a mig-console style search query into a set of parameters to send to the API
//
// This function is similar to the function in mig-console, however we do not include
// parameters that are not relevant to agents.
func parseSearchQuery(querystring string) (p migdbsearch.Parameters, err error) {
	defer func() {
		if e := recover(); e != nil {
			err = fmt.Errorf("parseSearchQuery() -> %v", e)
		}
	}()

	p = migdbsearch.NewParameters()
	p.Type = "agent"

	orders := strings.Split(querystring, " ")
	if len(orders) == 0 {
		panic("no criteria specified")
	}

	for _, order := range orders {
		if order == "and" {
			continue
		}
		params := strings.Split(order, "=")
		if len(params) != 2 {
			panic(fmt.Sprintf("Invalid `key=value` in search parameter '%s'", order))
		}
		key := params[0]
		value := params[1]
		// if the string contains % characters, used in postgres's pattern matching,
		// escape them properly
		switch key {
		case "after":
			p.After, err = time.Parse(time.RFC3339, value)
			if err != nil {
				panic("after date not in RFC3339 format, ex: 2015-09-23T14:14:16Z")
			}
		case "agentid":
			p.AgentID = value
		case "agentname":
			p.AgentName = value
		case "agentversion":
			p.AgentVersion = value
		case "before":
			p.Before, err = time.Parse(time.RFC3339, value)
			if err != nil {
				panic("before date not in RFC3339 format, ex: 2015-09-23T14:14:16Z")
			}
		case "limit":
			p.Limit, err = strconv.ParseFloat(value, 64)
			if err != nil {
				panic("invalid limit parameter")
			}
		case "status":
			p.Status = value
		case "name":
			p.AgentName = value
		default:
			panic(fmt.Sprintf("Unknown search key '%s'", key))
		}
	}
	return
}
<commit_msg>[minor] reorganize variable definition in mig-agent-search main<commit_after>// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
//
// Contributor: Julien Vehent jvehent@mozilla.com [:ulfr]
// Contributor: Aaron Meihm ameihm@mozilla.com [:alm]

package main

import (
	"encoding/json"
	"flag"
	"fmt"
	"os"
	"strconv"
	"strings"
	"time"

	"mig.ninja/mig"
	"mig.ninja/mig/client"
	migdbsearch "mig.ninja/mig/database/search"
)

func usage() {
	fmt.Fprintf(os.Stderr, `%s <query> - Search for MIG Agents

Usage: %s [-V] [-c path] -p "console style query" | -t "target style query"

The -p or -t flag must be specified to run a search.

The -V flag can be used to display MIG version.

Use -c to specify an alternate path to .migrc (by default, $HOME/.migrc)

CONSOLE MODE QUERY
------------------

The console mode query allows specification of a query string as would be passed
in mig-console using "search agent". It returns all matching agents.

EXAMPLE CONSOLE MODE QUERIES
----------------------------

All online agents:
  $ mig-agent-search -p "status=online"

All agents regardless of status:
  $ mig-agent-search -p "status=%%"

See the output of "search help" in mig-console for additional information on
how to format these queries.

TARGET MODE QUERY
-----------------

The target mode query allows specification of an agent targeting string as would
be passed to the -t flag using MIG command line. This evaluates agents using the
targeting string as the command line would, returning matching agents.

A search query is a SQL WHERE condition. It can filter on any field present in
the MIG Agents table.
	     Column      |           Type
	-----------------+-------------------------
	 id              | numeric
	 name            | character varying(2048)
	 queueloc        | character varying(2048)
	 mode            | character varying(2048)
	 version         | character varying(2048)
	 pid             | integer
	 starttime       | timestamp with time zone
	 destructiontime | timestamp with time zone
	 heartbeattime   | timestamp with time zone
	 status          | character varying(255)
	 environment     | json
	 tags            | json

The "environment" and "tags" fields are free JSON fields and can be queried using
Postgresql's JSON querying syntax.

Below is an example of environment document:
	{
	    "addresses": [
		"172.21.0.3/20",
		"fe80::3602:86ff:fe2b:6fdd/64"
	    ],
	    "arch": "amd64",
	    "ident": "Debian testing-updates sid",
	    "init": "upstart",
	    "isproxied": false,
	    "os": "linux",
	    "publicip": "172.21.0.3"
	}

Below is an example of tags document:
	{"operator":"linuxwall"}

EXAMPLE TARGET MODE QUERIES
---------------------------

Agent name "myserver.example.net"
  $ mig-agent-search -t "name='myserver.example.net'"

All Linux agents:
  $ mig-agent-search -t "environment->>'os'='linux'"

Ubuntu agents running 32 bits
  $ mig-agent-search -t "environment->>'ident' LIKE 'Ubuntu%%' AND environment->>'arch'='386'"

MacOS agents in datacenter SCL3
  $ mig-agent-search -t "environment->>'os'='darwin' AND name LIKE '%%\.scl3\.%%'"

Agents with uptime greater than 30 days
  $ mig-agent-search -t "starttime < NOW() - INTERVAL '30 days'"

Linux agents in checkin mode that are currently idle but woke up in the last hour
  $ mig-agent-search -t "mode='checkin' AND environment->>'os'='linux' AND status='idle' AND starttime > NOW() - INTERVAL '1 hour'"

Agents operated by team "opsec"
  $ mig-agent-search -t "tags->>'operator'='opsec'"

Command line flags:`, os.Args[0], os.Args[0])
}

func main() {
	defer func() {
		if e := recover(); e != nil {
			fmt.Fprintf(os.Stderr, "error: %v\n", e)
			os.Exit(1)
		}
	}()

	flag.Usage = usage

	var err error
	homedir := client.FindHomedir()
	var (
		err          error
		config       = flag.String("c", homedir+"/.migrc", "Load configuration from file")
		showversion  = flag.Bool("V", false, "Show build version and exit")
		paramSearch  = flag.String("p", "", "Search using mig-console search style query")
		targetSearch = flag.String("t", "", "Search using agent targeting string")
	)
	flag.Usage = usage
	flag.Parse()

	if *showversion {
		fmt.Println(mig.Version)
		os.Exit(0)
	}

	// Instantiate an API client
	conf, err := client.ReadConfiguration(*config)
	if err != nil {
		panic(err)
	}
	conf, err = client.ReadEnvConfiguration(conf)
	if err != nil {
		panic(err)
	}
	cli, err := client.NewClient(conf, "agent-search-"+mig.Version)
	if err != nil {
		panic(err)
	}

	if *paramSearch != "" {
		// Search using mig-console style keywords
		p, err := parseSearchQuery(*paramSearch)
		if err != nil {
			panic(err)
		}
		resources, err := cli.GetAPIResource("search?" + p.String())
		if err != nil {
			panic(err)
		}
		fmt.Println("name; id; status; version; mode; os; arch; pid; starttime; heartbeattime; tags; environment")
		for _, item := range resources.Collection.Items {
			for _, data := range item.Data {
				if data.Name != "agent" {
					continue
				}
				agt, err := client.ValueToAgent(data.Value)
				if err != nil {
					panic(err)
				}
				err = printAgent(agt)
				if err != nil {
					panic(err)
				}
			}
		}
	} else if *targetSearch != "" {
		// Search using an agent targeting string
		agents, err := cli.EvaluateAgentTarget(*targetSearch)
		if err != nil {
			panic(err)
		}
		fmt.Println("name; id; status; version; mode; os; arch; pid; starttime; heartbeattime; tags; environment")
		for _, agt := range agents {
			err = printAgent(agt)
			if err != nil {
				panic(err)
			}
		}
	} else {
		panic("must specify -p or -t, see help")
	}
	os.Exit(0)
}

func printAgent(agt mig.Agent) error {
	tags, err := json.Marshal(agt.Tags)
	if err != nil {
		return err
	}
	env, err := json.Marshal(agt.Env)
	if err != nil {
		return err
	}
	fmt.Printf("%s; %.0f; %s; %s; %s; %s; %s; %d; %s; %s; %s; %s\n",
		agt.Name, agt.ID, agt.Status, agt.Version, agt.Mode, agt.Env.OS,
		agt.Env.Arch, agt.PID, agt.StartTime.Format(time.RFC3339),
		agt.HeartBeatTS.Format(time.RFC3339), tags, env)
	return nil
}

// Transform a mig-console style search query into a set of parameters to send to the API
//
// This function is similar to the function in mig-console, however we do not include
// parameters that are not relevant to agents.
func parseSearchQuery(querystring string) (p migdbsearch.Parameters, err error) {
	defer func() {
		if e := recover(); e != nil {
			err = fmt.Errorf("parseSearchQuery() -> %v", e)
		}
	}()

	p = migdbsearch.NewParameters()
	p.Type = "agent"

	orders := strings.Split(querystring, " ")
	if len(orders) == 0 {
		panic("no criteria specified")
	}

	for _, order := range orders {
		if order == "and" {
			continue
		}
		params := strings.Split(order, "=")
		if len(params) != 2 {
			panic(fmt.Sprintf("Invalid `key=value` in search parameter '%s'", order))
		}
		key := params[0]
		value := params[1]
		// if the string contains % characters, used in postgres's pattern matching,
		// escape them properly
		switch key {
		case "after":
			p.After, err = time.Parse(time.RFC3339, value)
			if err != nil {
				panic("after date not in RFC3339 format, ex: 2015-09-23T14:14:16Z")
			}
		case "agentid":
			p.AgentID = value
		case "agentname":
			p.AgentName = value
		case "agentversion":
			p.AgentVersion = value
		case "before":
			p.Before, err = time.Parse(time.RFC3339, value)
			if err != nil {
				panic("before date not in RFC3339 format, ex: 2015-09-23T14:14:16Z")
			}
		case "limit":
			p.Limit, err = strconv.ParseFloat(value, 64)
			if err != nil {
				panic("invalid limit parameter")
			}
		case "status":
			p.Status = value
		case "name":
			p.AgentName = value
		default:
			panic(fmt.Sprintf("Unknown search key '%s'", key))
		}
	}
	return
}
<|endoftext|>