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

package schema_test

import (
	"strings"
	"testing"

	"github.com/opencontainers/image-spec/schema"
)

func TestImageIndex(t *testing.T) {
	for i, tt := range []struct {
		imageIndex string
		fail       bool
	}{
		// expected failure: mediaType does not match pattern
		{
			imageIndex: `
{
  "schemaVersion": 2,
  "manifests": [
    {
      "mediaType": "invalid",
      "size": 7143,
      "digest": "sha256:e692418e4cbaf90ca69d05a66403747baa33ee08806650b51fab815ad7fc331f",
      "platform": {
        "architecture": "ppc64le",
        "os": "linux"
      }
    }
  ]
}
`,
			fail: true,
		},

		// expected failure: manifest.size is string, expected integer
		{
			imageIndex: `
{
  "schemaVersion": 2,
  "manifests": [
    {
      "mediaType": "application/vnd.oci.image.manifest.v1+json",
      "size": "7682",
      "digest": "sha256:5b0bcabd1ed22e9fb1310cf6c2dec7cdef19f0ad69efa1f392e94a4333501270",
      "platform": {
        "architecture": "amd64",
        "os": "linux"
	  }
    }
  ]
}
`,
			fail: true,
		},

		// expected failure: manifest.digest is missing, expected required
		{
			imageIndex: `
{
  "schemaVersion": 2,
  "manifests": [
    {
      "mediaType": "application/vnd.oci.image.manifest.v1+json",
      "size": 7682,
      "platform": {
        "architecture": "amd64",
        "os": "linux"
      }
    }
  ]
}
`,
			fail: true,
		},

		// expected failure: in the optional field platform platform.architecture is missing, expected required
		{
			imageIndex: `
{
  "schemaVersion": 2,
  "manifests": [
    {
      "mediaType": "application/vnd.oci.image.manifest.v1+json",
      "size": 7682,
      "digest": "sha256:5b0bcabd1ed22e9fb1310cf6c2dec7cdef19f0ad69efa1f392e94a4333501270",
      "platform": {
	"os": "linux",
      }
    }
  ]
}
`,
			fail: true,
		},

		// expected failure: invalid referenced manifest media type
		{
			imageIndex: `
{
  "schemaVersion": 2,
  "manifests": [
    {
      "mediaType": "invalid",
      "size": 7682,
      "digest": "sha256:5b0bcabd1ed22e9fb1310cf6c2dec7cdef19f0ad69efa1f392e94a4333501270",
      "platform": {
        "architecture": "amd64",
        "os": "linux"
      }
    }
  ]
}
`,
			fail: true,
		},

		// expected failure: empty referenced manifest media type
		{
			imageIndex: `
{
  "schemaVersion": 2,
  "manifests": [
    {
      "mediaType": "",
      "size": 7682,
      "digest": "sha256:5b0bcabd1ed22e9fb1310cf6c2dec7cdef19f0ad69efa1f392e94a4333501270",
      "platform": {
        "architecture": "amd64",
        "os": "linux"
      }
    }
  ]
}
`,
			fail: true,
		},

		// valid image index, with optional fields
		{
			imageIndex: `
{
  "schemaVersion": 2,
  "manifests": [
    {
      "mediaType": "application/vnd.oci.image.manifest.v1+json",
      "size": 7143,
      "digest": "sha256:e692418e4cbaf90ca69d05a66403747baa33ee08806650b51fab815ad7fc331f",
      "platform": {
        "architecture": "ppc64le",
        "os": "linux"
      }
    },
    {
      "mediaType": "application/vnd.oci.image.manifest.v1+json",
      "size": 7682,
      "digest": "sha256:5b0bcabd1ed22e9fb1310cf6c2dec7cdef19f0ad69efa1f392e94a4333501270",
      "platform": {
        "architecture": "amd64",
        "os": "linux"
      }
    }
  ],
  "annotations": {
    "com.example.key1": "value1",
    "com.example.key2": "value2"
  }
}
`,
			fail: false,
		},

		// valid image index, with required fields only
		{
			imageIndex: `
{
  "schemaVersion": 2,
  "manifests": [
    {
      "mediaType": "application/vnd.oci.image.manifest.v1+json",
      "size": 7143,
      "digest": "sha256:e692418e4cbaf90ca69d05a66403747baa33ee08806650b51fab815ad7fc331f"
    }
  ]
}
`,
			fail: false,
		},

		// valid image index, with customized media type of referenced manifest
		{
			imageIndex: `
{
  "schemaVersion": 2,
  "manifests": [
    {
      "mediaType": "application/customized.manifest+json",
      "size": 7143,
      "digest": "sha256:e692418e4cbaf90ca69d05a66403747baa33ee08806650b51fab815ad7fc331f",
      "platform": {
        "architecture": "ppc64le",
        "os": "linux"
      }
    }
  ]
}
`,
			fail: false,
		},
	} {
		r := strings.NewReader(tt.imageIndex)
		err := schema.ValidatorMediaTypeImageIndex.Validate(r)

		if got := err != nil; tt.fail != got {
			t.Errorf("test %d: expected validation failure %t but got %t, err %v", i, tt.fail, got, err)
		}
	}
}
<commit_msg>schema: minor formatting fix in tests<commit_after>// Copyright 2016 The Linux Foundation
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package schema_test

import (
	"strings"
	"testing"

	"github.com/opencontainers/image-spec/schema"
)

func TestImageIndex(t *testing.T) {
	for i, tt := range []struct {
		imageIndex string
		fail       bool
	}{
		// expected failure: mediaType does not match pattern
		{
			imageIndex: `
{
  "schemaVersion": 2,
  "manifests": [
    {
      "mediaType": "invalid",
      "size": 7143,
      "digest": "sha256:e692418e4cbaf90ca69d05a66403747baa33ee08806650b51fab815ad7fc331f",
      "platform": {
        "architecture": "ppc64le",
        "os": "linux"
      }
    }
  ]
}
`,
			fail: true,
		},

		// expected failure: manifest.size is string, expected integer
		{
			imageIndex: `
{
  "schemaVersion": 2,
  "manifests": [
    {
      "mediaType": "application/vnd.oci.image.manifest.v1+json",
      "size": "7682",
      "digest": "sha256:5b0bcabd1ed22e9fb1310cf6c2dec7cdef19f0ad69efa1f392e94a4333501270",
      "platform": {
        "architecture": "amd64",
        "os": "linux"
      }
    }
  ]
}
`,
			fail: true,
		},

		// expected failure: manifest.digest is missing, expected required
		{
			imageIndex: `
{
  "schemaVersion": 2,
  "manifests": [
    {
      "mediaType": "application/vnd.oci.image.manifest.v1+json",
      "size": 7682,
      "platform": {
        "architecture": "amd64",
        "os": "linux"
      }
    }
  ]
}
`,
			fail: true,
		},

		// expected failure: in the optional field platform platform.architecture is missing, expected required
		{
			imageIndex: `
{
  "schemaVersion": 2,
  "manifests": [
    {
      "mediaType": "application/vnd.oci.image.manifest.v1+json",
      "size": 7682,
      "digest": "sha256:5b0bcabd1ed22e9fb1310cf6c2dec7cdef19f0ad69efa1f392e94a4333501270",
      "platform": {
	"os": "linux",
      }
    }
  ]
}
`,
			fail: true,
		},

		// expected failure: invalid referenced manifest media type
		{
			imageIndex: `
{
  "schemaVersion": 2,
  "manifests": [
    {
      "mediaType": "invalid",
      "size": 7682,
      "digest": "sha256:5b0bcabd1ed22e9fb1310cf6c2dec7cdef19f0ad69efa1f392e94a4333501270",
      "platform": {
        "architecture": "amd64",
        "os": "linux"
      }
    }
  ]
}
`,
			fail: true,
		},

		// expected failure: empty referenced manifest media type
		{
			imageIndex: `
{
  "schemaVersion": 2,
  "manifests": [
    {
      "mediaType": "",
      "size": 7682,
      "digest": "sha256:5b0bcabd1ed22e9fb1310cf6c2dec7cdef19f0ad69efa1f392e94a4333501270",
      "platform": {
        "architecture": "amd64",
        "os": "linux"
      }
    }
  ]
}
`,
			fail: true,
		},

		// valid image index, with optional fields
		{
			imageIndex: `
{
  "schemaVersion": 2,
  "manifests": [
    {
      "mediaType": "application/vnd.oci.image.manifest.v1+json",
      "size": 7143,
      "digest": "sha256:e692418e4cbaf90ca69d05a66403747baa33ee08806650b51fab815ad7fc331f",
      "platform": {
        "architecture": "ppc64le",
        "os": "linux"
      }
    },
    {
      "mediaType": "application/vnd.oci.image.manifest.v1+json",
      "size": 7682,
      "digest": "sha256:5b0bcabd1ed22e9fb1310cf6c2dec7cdef19f0ad69efa1f392e94a4333501270",
      "platform": {
        "architecture": "amd64",
        "os": "linux"
      }
    }
  ],
  "annotations": {
    "com.example.key1": "value1",
    "com.example.key2": "value2"
  }
}
`,
			fail: false,
		},

		// valid image index, with required fields only
		{
			imageIndex: `
{
  "schemaVersion": 2,
  "manifests": [
    {
      "mediaType": "application/vnd.oci.image.manifest.v1+json",
      "size": 7143,
      "digest": "sha256:e692418e4cbaf90ca69d05a66403747baa33ee08806650b51fab815ad7fc331f"
    }
  ]
}
`,
			fail: false,
		},

		// valid image index, with customized media type of referenced manifest
		{
			imageIndex: `
{
  "schemaVersion": 2,
  "manifests": [
    {
      "mediaType": "application/customized.manifest+json",
      "size": 7143,
      "digest": "sha256:e692418e4cbaf90ca69d05a66403747baa33ee08806650b51fab815ad7fc331f",
      "platform": {
        "architecture": "ppc64le",
        "os": "linux"
      }
    }
  ]
}
`,
			fail: false,
		},
	} {
		r := strings.NewReader(tt.imageIndex)
		err := schema.ValidatorMediaTypeImageIndex.Validate(r)

		if got := err != nil; tt.fail != got {
			t.Errorf("test %d: expected validation failure %t but got %t, err %v", i, tt.fail, got, err)
		}
	}
}
<|endoftext|>


<commit_before>package kepub

import (
	"crypto/sha256"
	"fmt"
	"strings"
	"testing"

	"golang.org/x/net/html"

	"github.com/PuerkitoBio/goquery"
	"github.com/stretchr/testify/assert"
)

func TestCleanHTML(t *testing.T) {
	h := `<meta  content="urn:uuid:asd--asdasd-asdasdas-dasdasd234234"   name="Adept.expected.resource"   />��<st1:asd></st1:asd><o:p></o:p><h1></h1><h3></h3><h2>test</h2><style></style>`
	cleanHTML(&h)
	assert.Equal(t, "
test
", h, "should be equal if cleaned correctly")
}

func TestSmartenPunctuation(t *testing.T) {
	h := `-- --- <!--test-->`
	smartenPunctuation(&h)
	assert.Equal(t, " —   –  ", h, "should be equal if smartened correctly")
}

func TestOpenSelfClosingPs(t *testing.T) {
	h := `<p>test</p><p /><p  /><p>test</p>`
	openSelfClosingPs(&h)
	assert.Equal(t, "
test

test
", h, "should be equal if reopened correctly")
}

func TestAddSpans(t *testing.T) {
	h := `<!DOCTYPE html>

    <title>Test Book 1</title>
    <meta content="http://www.w3.org/1999/xhtml; charset=utf-8" http-equiv="Content-Type"/>

This is the first sentence. This is the second sentence. This is the third sentence.

This is the first sentence. This is the second sentence? This is the third sentence!

This is the first sentence. This is the second sentence? This is the third sentence!

This is the first sentence. This is the second sentence? This is the third sentence!

This is the first sentence here. This is the second sentence? This is the third sentence!

test

test

`

	doc, err := goquery.NewDocumentFromReader(strings.NewReader(h))
	assert.Nil(t, err, "err should be nil")

	addSpans(doc)

	nh, err := doc.Html()
	assert.Nil(t, err, "err should be nil")

	nh = html.UnescapeString(nh)

	hs := sha256.New()
	hs.Write([]byte(nh))

	hxs := fmt.Sprintf("%x", hs.Sum(nil))

	assert.Equal(t, "ae78fe3c38c263e2ad43879cb6c2eaf3c0e8dafdf1fdf877bff449f2c2c44eee", hxs, "hash of content should be equal if processed correctly")
}

func TestAddDivs(t *testing.T) {
	h := `<!DOCTYPE html>

    <title>Test Book 1</title>
    <meta content="http://www.w3.org/1999/xhtml; charset=utf-8" http-equiv="Content-Type"/>

This is the first sentence. This is the second sentence. This is the third sentence.

This is the first sentence. This is the second sentence? This is the third sentence!

This is the first sentence. This is the second sentence? This is the third sentence!

This is the first sentence. This is the second sentence? This is the third sentence!

This is the first sentence here. This is the second sentence? This is the third sentence!

test

test

`

	doc, err := goquery.NewDocumentFromReader(strings.NewReader(h))
	assert.Nil(t, err, "err should be nil")

	addDivs(doc)

	nh, err := doc.Html()
	assert.Nil(t, err, "err should be nil")

	hs := sha256.New()
	hs.Write([]byte(nh))

	hxs := fmt.Sprintf("%x", hs.Sum(nil))

	assert.Equal(t, "d24ae5a8f438358828d50b036007fe06c9e24b55d6aa238f4628a24d77a15485", hxs, "hash of content should be equal if processed correctly")
}
<commit_msg>Added another test<commit_after>package kepub

import (
	"crypto/sha256"
	"fmt"
	"strings"
	"testing"

	"golang.org/x/net/html"

	"github.com/PuerkitoBio/goquery"
	"github.com/stretchr/testify/assert"
)

func TestCleanHTML(t *testing.T) {
	h := `<meta  content="urn:uuid:asd--asdasd-asdasdas-dasdasd234234"   name="Adept.expected.resource"   />��<st1:asd></st1:asd><o:p></o:p><h1></h1><h3></h3><h2>test</h2><style></style>`
	cleanHTML(&h)
	assert.Equal(t, "
test
", h, "should be equal if cleaned correctly")
}

func TestSmartenPunctuation(t *testing.T) {
	h := `-- --- <!--test-->`
	smartenPunctuation(&h)
	assert.Equal(t, " —   –  ", h, "should be equal if smartened correctly")
}

func TestOpenSelfClosingPs(t *testing.T) {
	h := `<p>test</p><p /><p  /><p>test</p>`
	openSelfClosingPs(&h)
	assert.Equal(t, "
test

test
", h, "should be equal if reopened correctly")
}

func TestAddSpans(t *testing.T) {
	h := `<!DOCTYPE html>

    <title>Test Book 1</title>
    <meta content="http://www.w3.org/1999/xhtml; charset=utf-8" http-equiv="Content-Type"/>

This is the first sentence. This is the second sentence. This is the third sentence.

This is the first sentence. This is the second sentence? This is the third sentence!

This is the first sentence. This is the second sentence? This is the third sentence!

This is the first sentence. This is the second sentence? This is the third sentence!

This is the first sentence here. This is the second sentence? This is the third sentence!

test

test

`

	doc, err := goquery.NewDocumentFromReader(strings.NewReader(h))
	assert.Nil(t, err, "err should be nil")

	addSpans(doc)

	nh, err := doc.Html()
	assert.Nil(t, err, "err should be nil")

	nh = html.UnescapeString(nh)

	hs := sha256.New()
	hs.Write([]byte(nh))

	hxs := fmt.Sprintf("%x", hs.Sum(nil))

	assert.Equal(t, "ae78fe3c38c263e2ad43879cb6c2eaf3c0e8dafdf1fdf877bff449f2c2c44eee", hxs, "hash of content should be equal if processed correctly")
}

func TestAddDivs(t *testing.T) {
	h := `<!DOCTYPE html>

    <title>Test Book 1</title>
    <meta content="http://www.w3.org/1999/xhtml; charset=utf-8" http-equiv="Content-Type"/>

This is the first sentence. This is the second sentence. This is the third sentence.

This is the first sentence. This is the second sentence? This is the third sentence!

This is the first sentence. This is the second sentence? This is the third sentence!

This is the first sentence. This is the second sentence? This is the third sentence!

This is the first sentence here. This is the second sentence? This is the third sentence!

test

test

`

	doc, err := goquery.NewDocumentFromReader(strings.NewReader(h))
	assert.Nil(t, err, "err should be nil")

	addDivs(doc)

	nh, err := doc.Html()
	assert.Nil(t, err, "err should be nil")

	hs := sha256.New()
	hs.Write([]byte(nh))

	hxs := fmt.Sprintf("%x", hs.Sum(nil))

	assert.Equal(t, "d24ae5a8f438358828d50b036007fe06c9e24b55d6aa238f4628a24d77a15485", hxs, "hash of content should be equal if processed correctly")
}

func TestProcess(t *testing.T) {
	h := `<!DOCTYPE html>

    <title>Test Book 1</title>
    <meta content="http://www.w3.org/1999/xhtml; charset=utf-8" http-equiv="Content-Type"/>

This is the first sentence. This is the second sentence. This is the third sentence.

This is the first sentence. This is the second sentence? This is the third sentence!

This is the first sentence. This is the second sentence? This is the third sentence!

This is the first sentence. This is the second sentence? This is the third sentence!

This is the first sentence here. This is the second sentence? This is the third sentence!

test

test

`

	process(&h)

	hs := sha256.New()
	hs.Write([]byte(h))

	hxs := fmt.Sprintf("%x", hs.Sum(nil))

	assert.Equal(t, "3abc0810906b322e3860b3d7fd1bafd5133a4a66ced286497eaafb40c94612fd", hxs, "hash of content should be equal if processed correctly")
}
<|endoftext|>


<commit_before>package commands

import (
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"strconv"
	"strings"

	log "github.com/sirupsen/logrus"
	"github.com/spf13/cobra"
	"gopkg.in/yaml.v2"

	"github.com/ailispaw/talk2docker/api"
	"github.com/ailispaw/talk2docker/client"
)

var cmdCompose = &cobra.Command{
	Use:     "compose <PATH/TO/YAML> [NAME...]",
	Aliases: []string{"fig", "create"},
	Short:   "Compose containers",
	Long:    APP_NAME + " compose - Compose containers",
	Run:     composeContainers,
}

var cmdComposeContainers = &cobra.Command{
	Use:     "compose <PATH/TO/YAML> [NAME...]",
	Aliases: []string{"fig", "create"},
	Short:   "Compose containers",
	Long:    APP_NAME + " container compose - Compose containers",
	Run:     composeContainers,
}

func init() {
	cmdContainer.AddCommand(cmdComposeContainers)
}

type Composer struct {
	Build string `yaml:"build"`

	Ports   []string `yaml:"ports"`
	Volumes []string `yaml:"volumes"`

	// api.Config
	Hostname     string   `yaml:"hostname"`
	Domainname   string   `yaml:"domainname"`
	User         string   `yaml:"user"`
	Memory       int64    `yaml:"mem_limit"`
	MemorySwap   int64    `yaml:"mem_swap"`
	CpuShares    int64    `yaml:"cpu_shares"`
	Cpuset       string   `yaml:"cpuset"`
	ExposedPorts []string `yaml:"expose"`
	Tty          bool     `yaml:"tty"`
	OpenStdin    bool     `yaml:"stdin_open"`
	Env          []string `yaml:"environment"`
	Cmd          []string `yaml:"command"`
	Image        string   `yaml:"image"`
	WorkingDir   string   `yaml:"working_dir"`
	Entrypoint   string   `yaml:"entrypoint"`
	MacAddress   string   `yaml:"mac_address"`

	// api.HostConfig
	Privileged      bool     `yaml:"privileged"`
	Links           []string `yaml:"links"`
	ExternalLinks   []string `yaml:"external_links"`
	PublishAllPorts bool     `yaml:"publish_all"`
	Dns             []string `yaml:"dns"`
	DnsSearch       []string `yaml:"dns_search"`
	ExtraHosts      []string `yaml:"add_host"`
	VolumesFrom     []string `yaml:"volumes_from"`
	Devices         []string `yaml:"device"`
	NetworkMode     string   `yaml:"net"`
	IpcMode         string   `yaml:"ipc"`
	PidMode         string   `yaml:"pid"`
	CapAdd          []string `yaml:"cap_add"`
	CapDrop         []string `yaml:"cap_drop"`
	RestartPolicy   string   `yaml:"restart"`
	SecurityOpt     []string `yaml:"security_opt"`
	ReadonlyRootfs  bool     `yaml:"read_only"`
}

func composeContainers(ctx *cobra.Command, args []string) {
	if len(args) < 1 {
		ErrorExit(ctx, "Needs an argument <PATH/TO/YAML> to compose containers")
	}

	path := filepath.Clean(args[0])
	root := filepath.Dir(path)

	var names []string
	if len(args) > 1 {
		names = args[1:]
	}

	data, err := ioutil.ReadFile(path)
	if err != nil {
		log.Fatal(err)
	}

	os.Chdir(root)

	var composers map[string]Composer
	if err := yaml.Unmarshal(data, &composers); err != nil {
		log.Fatal(err)
	}

	inArray := func(a string, list []string) bool {
		for _, b := range list {
			if a == b {
				return true
			}
		}
		return false
	}

	var gotError = false
	for name, composer := range composers {
		if (len(names) == 0) || inArray(name, names) {
			if cid, err := composeContainer(ctx, name, composer); err != nil {
				log.Error(err)
				gotError = true
			} else {
				ctx.Println(cid)
			}
		}
	}
	if gotError {
		log.Fatal("Error: failed to compose one or more containers")
	}
}

func composeContainer(ctx *cobra.Command, name string, composer Composer) (string, error) {
	var (
		config     api.Config
		hostConfig api.HostConfig

		localVolumes   = make(map[string]struct{})
		bindVolumes    []string
		exposedPorts   = make(map[string]struct{})
		portBindings   = make(map[string][]api.PortBinding)
		links          []string
		deviceMappings []api.DeviceMapping
	)

	if composer.Image != "" {
		r, n, t, err := client.ParseRepositoryName(composer.Image)
		if err != nil {
			return "", err
		}
		composer.Image = n + ":" + t
		if r != "" {
			composer.Image = r + "/" + composer.Image
		}
	}

	docker, err := client.NewDockerClient(configPath, hostName, ctx.Out())
	if err != nil {
		return "", err
	}

	if composer.Build != "" {
		message, err := docker.BuildImage(composer.Build, composer.Image, false)
		if err != nil {
			return "", err
		}
		if composer.Image == "" {
			if _, err := fmt.Sscanf(message, "Successfully built %s", &composer.Image); err != nil {
				return "", err
			}
		}
	}

	for _, rawPort := range composer.Ports {
		var (
			hostPort, containerPort string
		)

		if !strings.Contains(rawPort, ":") {
			hostPort = ""
			containerPort = rawPort
		} else {
			parts := strings.Split(rawPort, ":")
			hostPort = parts[0]
			containerPort = parts[1]
		}

		port := fmt.Sprintf("%s/%s", containerPort, "tcp")
		if _, exists := exposedPorts[port]; !exists {
			exposedPorts[port] = struct{}{}
		}

		portBinding := api.PortBinding{
			HostPort: hostPort,
		}
		bslice, exists := portBindings[port]
		if !exists {
			bslice = []api.PortBinding{}
		}
		portBindings[port] = append(bslice, portBinding)
	}

	for _, containerPort := range composer.ExposedPorts {
		port := fmt.Sprintf("%s/%s", containerPort, "tcp")
		if _, exists := exposedPorts[port]; !exists {
			exposedPorts[port] = struct{}{}
		}
	}

	for _, volume := range composer.Volumes {
		if arr := strings.Split(volume, ":"); len(arr) > 1 {
			if arr[1] == "/" {
				return "", fmt.Errorf("Invalid bind mount: destination can't be '/'")
			}
			bindVolumes = append(bindVolumes, volume)
		} else if volume == "/" {
			return "", fmt.Errorf("Invalid volume: path can't be '/'")
		} else {
			localVolumes[volume] = struct{}{}
		}
	}

	for _, link := range append(composer.Links, composer.ExternalLinks...) {
		arr := strings.Split(link, ":")
		if len(arr) < 2 {
			links = append(links, arr[0]+":"+arr[0])
		} else {
			links = append(links, link)
		}
	}

	for _, device := range composer.Devices {
		src := ""
		dst := ""
		permissions := "rwm"
		arr := strings.Split(device, ":")
		switch len(arr) {
		case 3:
			permissions = arr[2]
			fallthrough
		case 2:
			dst = arr[1]
			fallthrough
		case 1:
			src = arr[0]
		default:
			return "", fmt.Errorf("Invalid device specification: %s", device)
		}

		if dst == "" {
			dst = src
		}

		deviceMapping := api.DeviceMapping{
			PathOnHost:        src,
			PathInContainer:   dst,
			CgroupPermissions: permissions,
		}
		deviceMappings = append(deviceMappings, deviceMapping)
	}

	parts := strings.Split(composer.RestartPolicy, ":")
	restartPolicy := api.RestartPolicy{}
	restartPolicy.Name = parts[0]
	if (restartPolicy.Name == "on-failure") && (len(parts) == 2) {
		count, err := strconv.Atoi(parts[1])
		if err != nil {
			return "", err
		}
		restartPolicy.MaximumRetryCount = count
	}

	config.Hostname = composer.Hostname
	config.Domainname = composer.Domainname
	config.User = composer.User
	config.Memory = composer.Memory
	config.MemorySwap = composer.MemorySwap
	config.CpuShares = composer.CpuShares
	config.Cpuset = composer.Cpuset
	config.ExposedPorts = exposedPorts
	config.Tty = composer.Tty
	config.OpenStdin = composer.OpenStdin
	config.Env = composer.Env
	config.Cmd = composer.Cmd
	config.Image = composer.Image
	config.Volumes = localVolumes
	config.WorkingDir = composer.WorkingDir
	if composer.Entrypoint != "" {
		config.Entrypoint = []string{composer.Entrypoint}
	}
	config.MacAddress = composer.MacAddress

	hostConfig.Binds = bindVolumes
	hostConfig.Privileged = composer.Privileged
	hostConfig.PortBindings = portBindings
	hostConfig.Links = links
	hostConfig.PublishAllPorts = composer.PublishAllPorts
	hostConfig.Dns = composer.Dns
	hostConfig.DnsSearch = composer.DnsSearch
	hostConfig.ExtraHosts = composer.ExtraHosts
	hostConfig.VolumesFrom = composer.VolumesFrom
	hostConfig.Devices = deviceMappings
	hostConfig.NetworkMode = composer.NetworkMode
	hostConfig.IpcMode = composer.IpcMode
	hostConfig.PidMode = composer.PidMode
	hostConfig.CapAdd = composer.CapAdd
	hostConfig.CapDrop = composer.CapDrop
	hostConfig.RestartPolicy = restartPolicy
	hostConfig.SecurityOpt = composer.SecurityOpt
	hostConfig.ReadonlyRootfs = composer.ReadonlyRootfs

	var cid string
	cid, err = docker.CreateContainer(name, config, hostConfig)
	if err != nil {
		if apiErr, ok := err.(api.Error); ok && (apiErr.StatusCode == 404) {
			if _, err := docker.PullImage(config.Image); err != nil {
				return "", err
			}

			cid, err = docker.CreateContainer(name, config, hostConfig)
			if err != nil {
				return "", err
			}
		} else {
			return "", err
		}
	}

	return cid, nil
}
<commit_msg>Add absolute path error checking<commit_after>package commands

import (
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"strconv"
	"strings"

	log "github.com/sirupsen/logrus"
	"github.com/spf13/cobra"
	"gopkg.in/yaml.v2"

	"github.com/ailispaw/talk2docker/api"
	"github.com/ailispaw/talk2docker/client"
)

var cmdCompose = &cobra.Command{
	Use:     "compose <PATH/TO/YAML> [NAME...]",
	Aliases: []string{"fig", "create"},
	Short:   "Compose containers",
	Long:    APP_NAME + " compose - Compose containers",
	Run:     composeContainers,
}

var cmdComposeContainers = &cobra.Command{
	Use:     "compose <PATH/TO/YAML> [NAME...]",
	Aliases: []string{"fig", "create"},
	Short:   "Compose containers",
	Long:    APP_NAME + " container compose - Compose containers",
	Run:     composeContainers,
}

func init() {
	cmdContainer.AddCommand(cmdComposeContainers)
}

type Composer struct {
	Build string `yaml:"build"`

	Ports   []string `yaml:"ports"`
	Volumes []string `yaml:"volumes"`

	// api.Config
	Hostname     string   `yaml:"hostname"`
	Domainname   string   `yaml:"domainname"`
	User         string   `yaml:"user"`
	Memory       int64    `yaml:"mem_limit"`
	MemorySwap   int64    `yaml:"mem_swap"`
	CpuShares    int64    `yaml:"cpu_shares"`
	Cpuset       string   `yaml:"cpuset"`
	ExposedPorts []string `yaml:"expose"`
	Tty          bool     `yaml:"tty"`
	OpenStdin    bool     `yaml:"stdin_open"`
	Env          []string `yaml:"environment"`
	Cmd          []string `yaml:"command"`
	Image        string   `yaml:"image"`
	WorkingDir   string   `yaml:"working_dir"`
	Entrypoint   string   `yaml:"entrypoint"`
	MacAddress   string   `yaml:"mac_address"`

	// api.HostConfig
	Privileged      bool     `yaml:"privileged"`
	Links           []string `yaml:"links"`
	ExternalLinks   []string `yaml:"external_links"`
	PublishAllPorts bool     `yaml:"publish_all"`
	Dns             []string `yaml:"dns"`
	DnsSearch       []string `yaml:"dns_search"`
	ExtraHosts      []string `yaml:"add_host"`
	VolumesFrom     []string `yaml:"volumes_from"`
	Devices         []string `yaml:"device"`
	NetworkMode     string   `yaml:"net"`
	IpcMode         string   `yaml:"ipc"`
	PidMode         string   `yaml:"pid"`
	CapAdd          []string `yaml:"cap_add"`
	CapDrop         []string `yaml:"cap_drop"`
	RestartPolicy   string   `yaml:"restart"`
	SecurityOpt     []string `yaml:"security_opt"`
	ReadonlyRootfs  bool     `yaml:"read_only"`
}

func composeContainers(ctx *cobra.Command, args []string) {
	if len(args) < 1 {
		ErrorExit(ctx, "Needs an argument <PATH/TO/YAML> to compose containers")
	}

	path := filepath.Clean(args[0])
	root := filepath.Dir(path)

	var names []string
	if len(args) > 1 {
		names = args[1:]
	}

	data, err := ioutil.ReadFile(path)
	if err != nil {
		log.Fatal(err)
	}

	os.Chdir(root)

	var composers map[string]Composer
	if err := yaml.Unmarshal(data, &composers); err != nil {
		log.Fatal(err)
	}

	inArray := func(a string, list []string) bool {
		for _, b := range list {
			if a == b {
				return true
			}
		}
		return false
	}

	var gotError = false
	for name, composer := range composers {
		if (len(names) == 0) || inArray(name, names) {
			if cid, err := composeContainer(ctx, name, composer); err != nil {
				log.Error(err)
				gotError = true
			} else {
				ctx.Println(cid)
			}
		}
	}
	if gotError {
		log.Fatal("Error: failed to compose one or more containers")
	}
}

func composeContainer(ctx *cobra.Command, name string, composer Composer) (string, error) {
	var (
		config     api.Config
		hostConfig api.HostConfig

		localVolumes   = make(map[string]struct{})
		bindVolumes    []string
		exposedPorts   = make(map[string]struct{})
		portBindings   = make(map[string][]api.PortBinding)
		links          []string
		deviceMappings []api.DeviceMapping
	)

	if composer.Image != "" {
		r, n, t, err := client.ParseRepositoryName(composer.Image)
		if err != nil {
			return "", err
		}
		composer.Image = n + ":" + t
		if r != "" {
			composer.Image = r + "/" + composer.Image
		}
	}

	if (composer.WorkingDir != "") && !filepath.IsAbs(composer.WorkingDir) {
		return "", fmt.Errorf("Invalid working directory: it must be absolute.")
	}

	docker, err := client.NewDockerClient(configPath, hostName, ctx.Out())
	if err != nil {
		return "", err
	}

	if composer.Build != "" {
		message, err := docker.BuildImage(composer.Build, composer.Image, false)
		if err != nil {
			return "", err
		}
		if composer.Image == "" {
			if _, err := fmt.Sscanf(message, "Successfully built %s", &composer.Image); err != nil {
				return "", err
			}
		}
	}

	for _, rawPort := range composer.Ports {
		var (
			hostPort, containerPort string
		)

		if !strings.Contains(rawPort, ":") {
			hostPort = ""
			containerPort = rawPort
		} else {
			parts := strings.Split(rawPort, ":")
			hostPort = parts[0]
			containerPort = parts[1]
		}

		port := fmt.Sprintf("%s/%s", containerPort, "tcp")
		if _, exists := exposedPorts[port]; !exists {
			exposedPorts[port] = struct{}{}
		}

		portBinding := api.PortBinding{
			HostPort: hostPort,
		}
		bslice, exists := portBindings[port]
		if !exists {
			bslice = []api.PortBinding{}
		}
		portBindings[port] = append(bslice, portBinding)
	}

	for _, containerPort := range composer.ExposedPorts {
		port := fmt.Sprintf("%s/%s", containerPort, "tcp")
		if _, exists := exposedPorts[port]; !exists {
			exposedPorts[port] = struct{}{}
		}
	}

	for _, volume := range composer.Volumes {
		if arr := strings.Split(volume, ":"); len(arr) > 1 {
			if arr[1] == "/" {
				return "", fmt.Errorf("Invalid bind mount: destination can't be '/'")
			}
			if !filepath.IsAbs(arr[0]) {
				return "", fmt.Errorf("Invalid bind mount: the host path must be absolute.")
			}
			bindVolumes = append(bindVolumes, volume)
		} else if volume == "/" {
			return "", fmt.Errorf("Invalid volume: path can't be '/'")
		} else {
			localVolumes[volume] = struct{}{}
		}
	}

	for _, link := range append(composer.Links, composer.ExternalLinks...) {
		arr := strings.Split(link, ":")
		if len(arr) < 2 {
			links = append(links, arr[0]+":"+arr[0])
		} else {
			links = append(links, link)
		}
	}

	for _, device := range composer.Devices {
		src := ""
		dst := ""
		permissions := "rwm"
		arr := strings.Split(device, ":")
		switch len(arr) {
		case 3:
			permissions = arr[2]
			fallthrough
		case 2:
			dst = arr[1]
			fallthrough
		case 1:
			src = arr[0]
		default:
			return "", fmt.Errorf("Invalid device specification: %s", device)
		}

		if dst == "" {
			dst = src
		}

		deviceMapping := api.DeviceMapping{
			PathOnHost:        src,
			PathInContainer:   dst,
			CgroupPermissions: permissions,
		}
		deviceMappings = append(deviceMappings, deviceMapping)
	}

	parts := strings.Split(composer.RestartPolicy, ":")
	restartPolicy := api.RestartPolicy{}
	restartPolicy.Name = parts[0]
	if (restartPolicy.Name == "on-failure") && (len(parts) == 2) {
		count, err := strconv.Atoi(parts[1])
		if err != nil {
			return "", err
		}
		restartPolicy.MaximumRetryCount = count
	}

	config.Hostname = composer.Hostname
	config.Domainname = composer.Domainname
	config.User = composer.User
	config.Memory = composer.Memory
	config.MemorySwap = composer.MemorySwap
	config.CpuShares = composer.CpuShares
	config.Cpuset = composer.Cpuset
	config.ExposedPorts = exposedPorts
	config.Tty = composer.Tty
	config.OpenStdin = composer.OpenStdin
	config.Env = composer.Env
	config.Cmd = composer.Cmd
	config.Image = composer.Image
	config.Volumes = localVolumes
	config.WorkingDir = composer.WorkingDir
	if composer.Entrypoint != "" {
		config.Entrypoint = []string{composer.Entrypoint}
	}
	config.MacAddress = composer.MacAddress

	hostConfig.Binds = bindVolumes
	hostConfig.Privileged = composer.Privileged
	hostConfig.PortBindings = portBindings
	hostConfig.Links = links
	hostConfig.PublishAllPorts = composer.PublishAllPorts
	hostConfig.Dns = composer.Dns
	hostConfig.DnsSearch = composer.DnsSearch
	hostConfig.ExtraHosts = composer.ExtraHosts
	hostConfig.VolumesFrom = composer.VolumesFrom
	hostConfig.Devices = deviceMappings
	hostConfig.NetworkMode = composer.NetworkMode
	hostConfig.IpcMode = composer.IpcMode
	hostConfig.PidMode = composer.PidMode
	hostConfig.CapAdd = composer.CapAdd
	hostConfig.CapDrop = composer.CapDrop
	hostConfig.RestartPolicy = restartPolicy
	hostConfig.SecurityOpt = composer.SecurityOpt
	hostConfig.ReadonlyRootfs = composer.ReadonlyRootfs

	var cid string
	cid, err = docker.CreateContainer(name, config, hostConfig)
	if err != nil {
		if apiErr, ok := err.(api.Error); ok && (apiErr.StatusCode == 404) {
			if _, err := docker.PullImage(config.Image); err != nil {
				return "", err
			}

			cid, err = docker.CreateContainer(name, config, hostConfig)
			if err != nil {
				return "", err
			}
		} else {
			return "", err
		}
	}

	return cid, nil
}
<|endoftext|>


<commit_before>package commands

import (
	"errors"
	"fmt"
	"os"

	"github.com/EngineerBetter/concourse-up/bosh"
	"github.com/EngineerBetter/concourse-up/certs"
	"github.com/EngineerBetter/concourse-up/concourse"
	"github.com/EngineerBetter/concourse-up/config"
	"github.com/EngineerBetter/concourse-up/fly"
	"github.com/EngineerBetter/concourse-up/iaas"
	"github.com/EngineerBetter/concourse-up/terraform"
	"github.com/EngineerBetter/concourse-up/util"

	"gopkg.in/urfave/cli.v1"
)

var destroyArgs config.DestroyArgs

var destroyFlags = []cli.Flag{
	cli.StringFlag{
		Name:        "region",
		Value:       "eu-west-1",
		Usage:       "(optional) AWS region",
		EnvVar:      "AWS_REGION",
		Destination: &destroyArgs.AWSRegion,
	},
	cli.StringFlag{
		Name:        "iaas",
		Usage:       "(optional) IAAS, can be AWS or GCP",
		EnvVar:      "IAAS",
		Value:       "AWS",
		Hidden:      true,
		Destination: &destroyArgs.IAAS,
	},
	cli.StringFlag{
		Name:        "namespace",
		Usage:       "(optional) Specify a namespace for deployments in order to group them in a meaningful way",
		EnvVar:      "NAMESPACE",
		Destination: &destroyArgs.Namespace,
	},
}

var destroy = cli.Command{
	Name:      "destroy",
	Aliases:   []string{"x"},
	Usage:     "Destroys a Concourse",
	ArgsUsage: "<name>",
	Flags:     destroyFlags,
	Action: func(c *cli.Context) error {
		name := c.Args().Get(0)
		if name == "" {
			return errors.New("Usage is `concourse-up destroy <name>`")
		}

		if !NonInteractiveModeEnabled() {
			confirm, err := util.CheckConfirmation(os.Stdin, os.Stdout, name)
			if err != nil {
				return err
			}

			if !confirm {
				fmt.Println("Bailing out...")
				return nil
			}
		}

		iaasClient, err := iaas.New(destroyArgs.IAAS, destroyArgs.AWSRegion)
		if err != nil {
			return err
		}

		client := concourse.NewClient(
			iaasClient,
			terraform.NewClient,
			bosh.NewClient,
			fly.New,
			certs.Generate,
			config.New(iaasClient, name, ""),
			nil,
			os.Stdout,
			os.Stderr,
			util.FindUserIP,
			certs.NewAcmeClient,
		)

		return client.Destroy()
	},
}
<commit_msg>namespace was provided but not used<commit_after>package commands

import (
	"errors"
	"fmt"
	"os"

	"github.com/EngineerBetter/concourse-up/bosh"
	"github.com/EngineerBetter/concourse-up/certs"
	"github.com/EngineerBetter/concourse-up/concourse"
	"github.com/EngineerBetter/concourse-up/config"
	"github.com/EngineerBetter/concourse-up/fly"
	"github.com/EngineerBetter/concourse-up/iaas"
	"github.com/EngineerBetter/concourse-up/terraform"
	"github.com/EngineerBetter/concourse-up/util"

	"gopkg.in/urfave/cli.v1"
)

var destroyArgs config.DestroyArgs

var destroyFlags = []cli.Flag{
	cli.StringFlag{
		Name:        "region",
		Value:       "eu-west-1",
		Usage:       "(optional) AWS region",
		EnvVar:      "AWS_REGION",
		Destination: &destroyArgs.AWSRegion,
	},
	cli.StringFlag{
		Name:        "iaas",
		Usage:       "(optional) IAAS, can be AWS or GCP",
		EnvVar:      "IAAS",
		Value:       "AWS",
		Hidden:      true,
		Destination: &destroyArgs.IAAS,
	},
	cli.StringFlag{
		Name:        "namespace",
		Usage:       "(optional) Specify a namespace for deployments in order to group them in a meaningful way",
		EnvVar:      "NAMESPACE",
		Destination: &destroyArgs.Namespace,
	},
}

var destroy = cli.Command{
	Name:      "destroy",
	Aliases:   []string{"x"},
	Usage:     "Destroys a Concourse",
	ArgsUsage: "<name>",
	Flags:     destroyFlags,
	Action: func(c *cli.Context) error {
		name := c.Args().Get(0)
		if name == "" {
			return errors.New("Usage is `concourse-up destroy <name>`")
		}

		if !NonInteractiveModeEnabled() {
			confirm, err := util.CheckConfirmation(os.Stdin, os.Stdout, name)
			if err != nil {
				return err
			}

			if !confirm {
				fmt.Println("Bailing out...")
				return nil
			}
		}

		iaasClient, err := iaas.New(destroyArgs.IAAS, destroyArgs.AWSRegion)
		if err != nil {
			return err
		}

		client := concourse.NewClient(
			iaasClient,
			terraform.NewClient,
			bosh.NewClient,
			fly.New,
			certs.Generate,
			config.New(iaasClient, name, destroyArgs.Namespace),
			nil,
			os.Stdout,
			os.Stderr,
			util.FindUserIP,
			certs.NewAcmeClient,
		)

		return client.Destroy()
	},
}
<|endoftext|>


<commit_before>package commands

import (
	"bufio"
	"bytes"
	"encoding/json"
	"io"
	"io/ioutil"

	"crypto/tls"
	"log"
	"net/http"
	"os"
	"os/exec"
	"os/signal"
	"path/filepath"
	"strconv"
	"strings"
	"syscall"

	"github.com/codegangsta/cli"
	"github.com/concourse/atc"
	"github.com/concourse/fly/config"
	"github.com/concourse/fly/eventstream"
	"github.com/pivotal-golang/archiver/compressor"
	"github.com/tedsuo/rata"
	"github.com/vito/go-sse/sse"
)

type Input struct {
	Name string
	Path string
	Pipe atc.Pipe
}

func Execute(c *cli.Context) {
	atcURL := c.GlobalString("atcURL")
	buildConfig := c.String("config")
	insecure := c.GlobalBool("insecure")
	excludeIgnored := c.GlobalBool("exclude-ignored")

	atcRequester := newAtcRequester(atcURL, insecure)

	inputMappings := c.StringSlice("input")
	if len(inputMappings) == 0 {
		wd, err := os.Getwd()
		if err != nil {
			log.Fatalln(err)
		}

		inputMappings = append(inputMappings, filepath.Base(wd)+"="+wd)
	}

	inputs := []Input{}
	for _, i := range inputMappings {
		segs := strings.SplitN(i, "=", 2)
		if len(segs) < 2 {
			log.Println("malformed input:", i)
			os.Exit(1)
		}

		inputName := segs[0]

		absPath, err := filepath.Abs(segs[1])
		if err != nil {
			log.Printf("could not locate input %s: %s\n", inputName, err)
			os.Exit(1)
		}

		pipe := createPipe(atcRequester)

		inputs = append(inputs, Input{
			Name: inputName,
			Path: absPath,
			Pipe: pipe,
		})
	}

	absConfig, err := filepath.Abs(buildConfig)
	if err != nil {
		log.Println("could not locate config file:", err)
		os.Exit(1)
	}

	build := createBuild(
		atcRequester,
		c.Bool("privileged"),
		inputs,
		config.LoadBuildConfig(absConfig, c.Args()),
	)

	terminate := make(chan os.Signal, 1)

	go abortOnSignal(atcRequester, terminate, build)

	signal.Notify(terminate, syscall.SIGINT, syscall.SIGTERM)

	eventSource := &sse.EventSource{
		Client: atcRequester.httpClient,
		CreateRequest: func() *http.Request {
			logOutput, err := atcRequester.CreateRequest(
				atc.BuildEvents,
				rata.Params{"build_id": strconv.Itoa(build.ID)},
				nil,
			)
			if err != nil {
				log.Fatalln(err)
			}

			return logOutput
		},
	}

	go func() {
		for _, i := range inputs {
			upload(i, excludeIgnored, atcRequester)
		}
	}()

	exitCode, err := eventstream.RenderStream(eventSource)
	if err != nil {
		log.Println("failed to render stream:", err)
		os.Exit(1)
	}

	eventSource.Close()

	os.Exit(exitCode)
}

func createPipe(atcRequester *atcRequester) atc.Pipe {
	cPipe, err := atcRequester.CreateRequest(atc.CreatePipe, nil, nil)
	if err != nil {
		log.Fatalln(err)
	}

	response, err := atcRequester.httpClient.Do(cPipe)
	if err != nil {
		log.Fatalln("request failed:", err)
	}

	defer response.Body.Close()

	if response.StatusCode != http.StatusCreated {
		log.Println("bad response when creating pipe:", response)
		response.Write(os.Stderr)
		os.Exit(1)
	}

	var pipe atc.Pipe
	err = json.NewDecoder(response.Body).Decode(&pipe)
	if err != nil {
		log.Println("malformed response when creating pipe:", err)
		os.Exit(1)
	}

	return pipe
}

func createBuild(
	atcRequester *atcRequester,
	privileged bool,
	inputs []Input,
	config atc.BuildConfig,
) atc.Build {
	buffer := &bytes.Buffer{}

	buildInputs := make([]atc.InputPlan, len(inputs))
	for idx, i := range inputs {
		readPipe, err := atcRequester.CreateHTTPRequest(
			atc.ReadPipe,
			rata.Params{"pipe_id": i.Pipe.ID},
			nil,
		)
		if err != nil {
			log.Fatalln(err)
		}

		readPipe.URL.Host = i.Pipe.PeerAddr

		buildInputs[idx] = atc.InputPlan{
			Name: i.Name,
			Type: "archive",
			Source: atc.Source{
				"uri": readPipe.URL.String(),
			},
		}
	}

	buildPlan := atc.BuildPlan{
		Privileged: privileged,
		Config:     &config,
		Inputs:     buildInputs,
	}

	err := json.NewEncoder(buffer).Encode(buildPlan)
	if err != nil {
		log.Fatalln("encoding build failed:", err)
	}

	createBuild, err := atcRequester.CreateRequest(atc.CreateBuild, nil, buffer)
	if err != nil {
		log.Fatalln(err)
	}

	createBuild.Header.Set("Content-Type", "application/json")

	response, err := atcRequester.httpClient.Do(createBuild)
	if err != nil {
		log.Fatalln("request failed:", err)
	}

	defer response.Body.Close()

	if response.StatusCode != http.StatusCreated {
		log.Println("bad response when creating build:", response)
		response.Write(os.Stderr)
		os.Exit(1)
	}

	var build atc.Build
	err = json.NewDecoder(response.Body).Decode(&build)
	if err != nil {
		log.Fatalln("response decoding failed:", err)
	}

	return build
}

func abortOnSignal(
	atcRequester *atcRequester,
	terminate <-chan os.Signal,
	build atc.Build,
) {
	<-terminate

	println("\naborting...")

	abortReq, err := atcRequester.CreateRequest(
		atc.AbortBuild,
		rata.Params{"build_id": strconv.Itoa(build.ID)},
		nil,
	)
	if err != nil {
		log.Fatalln(err)
	}

	resp, err := atcRequester.httpClient.Do(abortReq)
	if err != nil {
		log.Println("failed to abort:", err)
		os.Exit(255)
	}

	resp.Body.Close()

	// if told to terminate again, exit immediately
	<-terminate
	println("exiting immediately")
	os.Exit(2)
}

func upload(input Input, excludeIgnored bool, atcRequester *atcRequester) {
	path := input.Path
	pipe := input.Pipe

	var archive io.ReadCloser
	if tarPath, err := exec.LookPath("tar"); err != nil {
		compressor := compressor.NewTgz()

		tmpfile, err := ioutil.TempFile("", "fly")
		if err != nil {
			log.Fatalln("creating tempfile failed:", err)
		}

		tmpfile.Close()

		defer os.Remove(tmpfile.Name())

		err = compressor.Compress(path+"/", tmpfile.Name())
		if err != nil {
			log.Fatalln("creating archive failed:", err)
		}

		archive, err = os.Open(tmpfile.Name())
		if err != nil {
			log.Fatalln("could not open archive:", err)
		}
	} else {
		var files []string

		if excludeIgnored {
			files, err = getGitFiles(path)
			if err != nil {
				log.Fatalln("could not determine ignored files:", err)
			}
		} else {
			files = []string{"."}
		}

		tarCmd := exec.Command(tarPath, append([]string{"-czf", "-"}, files...)...)
		tarCmd.Dir = path
		tarCmd.Stderr = os.Stderr

		archive, err = tarCmd.StdoutPipe()
		if err != nil {
			log.Fatalln("could not create tar pipe:", err)
		}

		err = tarCmd.Start()
		if err != nil {
			log.Fatalln("could not run tar:", err)
		}

		defer tarCmd.Wait()
	}

	defer archive.Close()

	uploadBits, err := atcRequester.CreateRequest(
		atc.WritePipe,
		rata.Params{"pipe_id": pipe.ID},
		archive,
	)
	if err != nil {
		log.Fatalln(err)
	}

	response, err := atcRequester.httpClient.Do(uploadBits)
	if err != nil {
		log.Fatalln("request failed:", err)
	}

	defer response.Body.Close()

	if response.StatusCode != http.StatusOK {
		log.Println("bad response when uploading bits:", response)
		response.Write(os.Stderr)
		os.Exit(1)
	}
}

type atcRequester struct {
	*rata.RequestGenerator
	httpClient *http.Client
}

func newAtcRequester(atcUrl string, insecure bool) *atcRequester {
	tlsClientConfig := &tls.Config{InsecureSkipVerify: insecure}

	return &atcRequester{
		rata.NewRequestGenerator(atcUrl, atc.Routes),
		&http.Client{Transport: &http.Transport{TLSClientConfig: tlsClientConfig}},
	}
}

func (ar *atcRequester) CreateHTTPRequest(
	name string,
	params rata.Params,
	body io.Reader,
) (*http.Request, error) {
	request, err := ar.CreateRequest(name, params, body)
	if err != nil {
		return nil, err
	}

	url := request.URL
	url.Scheme = "http"
	request.URL = url
	return request, nil
}

func getGitFiles(dir string) ([]string, error) {
	tracked, err := gitLS(dir)
	if err != nil {
		return nil, err
	}

	untracked, err := gitLS(dir, "--others", "--exclude-standard")
	if err != nil {
		return nil, err
	}

	return append(tracked, untracked...), nil
}

func gitLS(dir string, flags ...string) ([]string, error) {
	files := []string{}

	gitLS := exec.Command("git", append([]string{"ls-files", "-z"}, flags...)...)
	gitLS.Dir = dir

	gitOut, err := gitLS.StdoutPipe()
	if err != nil {
		return nil, err
	}

	outScan := bufio.NewScanner(gitOut)
	outScan.Split(scanNull)

	err = gitLS.Start()
	if err != nil {
		return nil, err
	}

	for outScan.Scan() {
		files = append(files, outScan.Text())
	}

	err = gitLS.Wait()
	if err != nil {
		return nil, err
	}

	return files, nil
}

func scanNull(data []byte, atEOF bool) (int, []byte, error) {
	// eof, no more data; terminate
	if atEOF && len(data) == 0 {
		return 0, nil, nil
	}

	// look for terminating null byte
	if i := bytes.IndexByte(data, 0); i >= 0 {
		return i + 1, data[0:i], nil
	}

	// no final terminator; return what's left
	if atEOF {
		return len(data), data, nil
	}

	// request more data
	return 0, nil, nil
}
<commit_msg>connect to event source before uploading<commit_after>package commands

import (
	"bufio"
	"bytes"
	"encoding/json"
	"io"
	"io/ioutil"

	"crypto/tls"
	"log"
	"net/http"
	"os"
	"os/exec"
	"os/signal"
	"path/filepath"
	"strconv"
	"strings"
	"syscall"

	"github.com/codegangsta/cli"
	"github.com/concourse/atc"
	"github.com/concourse/fly/config"
	"github.com/concourse/fly/eventstream"
	"github.com/pivotal-golang/archiver/compressor"
	"github.com/tedsuo/rata"
	"github.com/vito/go-sse/sse"
)

type Input struct {
	Name string
	Path string
	Pipe atc.Pipe
}

func Execute(c *cli.Context) {
	atcURL := c.GlobalString("atcURL")
	buildConfig := c.String("config")
	insecure := c.GlobalBool("insecure")
	excludeIgnored := c.GlobalBool("exclude-ignored")

	atcRequester := newAtcRequester(atcURL, insecure)

	inputMappings := c.StringSlice("input")
	if len(inputMappings) == 0 {
		wd, err := os.Getwd()
		if err != nil {
			log.Fatalln(err)
		}

		inputMappings = append(inputMappings, filepath.Base(wd)+"="+wd)
	}

	inputs := []Input{}
	for _, i := range inputMappings {
		segs := strings.SplitN(i, "=", 2)
		if len(segs) < 2 {
			log.Println("malformed input:", i)
			os.Exit(1)
		}

		inputName := segs[0]

		absPath, err := filepath.Abs(segs[1])
		if err != nil {
			log.Printf("could not locate input %s: %s\n", inputName, err)
			os.Exit(1)
		}

		pipe := createPipe(atcRequester)

		inputs = append(inputs, Input{
			Name: inputName,
			Path: absPath,
			Pipe: pipe,
		})
	}

	absConfig, err := filepath.Abs(buildConfig)
	if err != nil {
		log.Println("could not locate config file:", err)
		os.Exit(1)
	}

	build := createBuild(
		atcRequester,
		c.Bool("privileged"),
		inputs,
		config.LoadBuildConfig(absConfig, c.Args()),
	)

	terminate := make(chan os.Signal, 1)

	go abortOnSignal(atcRequester, terminate, build)

	signal.Notify(terminate, syscall.SIGINT, syscall.SIGTERM)

	eventSource := &sse.EventSource{
		Client: atcRequester.httpClient,
		CreateRequest: func() *http.Request {
			logOutput, err := atcRequester.CreateRequest(
				atc.BuildEvents,
				rata.Params{"build_id": strconv.Itoa(build.ID)},
				nil,
			)
			if err != nil {
				log.Fatalln(err)
			}

			return logOutput
		},
	}

	err = eventSource.Connect()
	if err != nil {
		log.Println("failed to connect to event stream:", err)
		os.Exit(1)
	}

	go func() {
		for _, i := range inputs {
			upload(i, excludeIgnored, atcRequester)
		}
	}()

	exitCode, err := eventstream.RenderStream(eventSource)
	if err != nil {
		log.Println("failed to render stream:", err)
		os.Exit(1)
	}

	eventSource.Close()

	os.Exit(exitCode)
}

func createPipe(atcRequester *atcRequester) atc.Pipe {
	cPipe, err := atcRequester.CreateRequest(atc.CreatePipe, nil, nil)
	if err != nil {
		log.Fatalln(err)
	}

	response, err := atcRequester.httpClient.Do(cPipe)
	if err != nil {
		log.Fatalln("request failed:", err)
	}

	defer response.Body.Close()

	if response.StatusCode != http.StatusCreated {
		log.Println("bad response when creating pipe:", response)
		response.Write(os.Stderr)
		os.Exit(1)
	}

	var pipe atc.Pipe
	err = json.NewDecoder(response.Body).Decode(&pipe)
	if err != nil {
		log.Println("malformed response when creating pipe:", err)
		os.Exit(1)
	}

	return pipe
}

func createBuild(
	atcRequester *atcRequester,
	privileged bool,
	inputs []Input,
	config atc.BuildConfig,
) atc.Build {
	buffer := &bytes.Buffer{}

	buildInputs := make([]atc.InputPlan, len(inputs))
	for idx, i := range inputs {
		readPipe, err := atcRequester.CreateHTTPRequest(
			atc.ReadPipe,
			rata.Params{"pipe_id": i.Pipe.ID},
			nil,
		)
		if err != nil {
			log.Fatalln(err)
		}

		readPipe.URL.Host = i.Pipe.PeerAddr

		buildInputs[idx] = atc.InputPlan{
			Name: i.Name,
			Type: "archive",
			Source: atc.Source{
				"uri": readPipe.URL.String(),
			},
		}
	}

	buildPlan := atc.BuildPlan{
		Privileged: privileged,
		Config:     &config,
		Inputs:     buildInputs,
	}

	err := json.NewEncoder(buffer).Encode(buildPlan)
	if err != nil {
		log.Fatalln("encoding build failed:", err)
	}

	createBuild, err := atcRequester.CreateRequest(atc.CreateBuild, nil, buffer)
	if err != nil {
		log.Fatalln(err)
	}

	createBuild.Header.Set("Content-Type", "application/json")

	response, err := atcRequester.httpClient.Do(createBuild)
	if err != nil {
		log.Fatalln("request failed:", err)
	}

	defer response.Body.Close()

	if response.StatusCode != http.StatusCreated {
		log.Println("bad response when creating build:", response)
		response.Write(os.Stderr)
		os.Exit(1)
	}

	var build atc.Build
	err = json.NewDecoder(response.Body).Decode(&build)
	if err != nil {
		log.Fatalln("response decoding failed:", err)
	}

	return build
}

func abortOnSignal(
	atcRequester *atcRequester,
	terminate <-chan os.Signal,
	build atc.Build,
) {
	<-terminate

	println("\naborting...")

	abortReq, err := atcRequester.CreateRequest(
		atc.AbortBuild,
		rata.Params{"build_id": strconv.Itoa(build.ID)},
		nil,
	)
	if err != nil {
		log.Fatalln(err)
	}

	resp, err := atcRequester.httpClient.Do(abortReq)
	if err != nil {
		log.Println("failed to abort:", err)
		os.Exit(255)
	}

	resp.Body.Close()

	// if told to terminate again, exit immediately
	<-terminate
	println("exiting immediately")
	os.Exit(2)
}

func upload(input Input, excludeIgnored bool, atcRequester *atcRequester) {
	path := input.Path
	pipe := input.Pipe

	var archive io.ReadCloser
	if tarPath, err := exec.LookPath("tar"); err != nil {
		compressor := compressor.NewTgz()

		tmpfile, err := ioutil.TempFile("", "fly")
		if err != nil {
			log.Fatalln("creating tempfile failed:", err)
		}

		tmpfile.Close()

		defer os.Remove(tmpfile.Name())

		err = compressor.Compress(path+"/", tmpfile.Name())
		if err != nil {
			log.Fatalln("creating archive failed:", err)
		}

		archive, err = os.Open(tmpfile.Name())
		if err != nil {
			log.Fatalln("could not open archive:", err)
		}
	} else {
		var files []string

		if excludeIgnored {
			files, err = getGitFiles(path)
			if err != nil {
				log.Fatalln("could not determine ignored files:", err)
			}
		} else {
			files = []string{"."}
		}

		tarCmd := exec.Command(tarPath, append([]string{"-czf", "-"}, files...)...)
		tarCmd.Dir = path
		tarCmd.Stderr = os.Stderr

		archive, err = tarCmd.StdoutPipe()
		if err != nil {
			log.Fatalln("could not create tar pipe:", err)
		}

		err = tarCmd.Start()
		if err != nil {
			log.Fatalln("could not run tar:", err)
		}

		defer tarCmd.Wait()
	}

	defer archive.Close()

	uploadBits, err := atcRequester.CreateRequest(
		atc.WritePipe,
		rata.Params{"pipe_id": pipe.ID},
		archive,
	)
	if err != nil {
		log.Fatalln(err)
	}

	response, err := atcRequester.httpClient.Do(uploadBits)
	if err != nil {
		log.Fatalln("request failed:", err)
	}

	defer response.Body.Close()

	if response.StatusCode != http.StatusOK {
		log.Println("bad response when uploading bits:", response)
		response.Write(os.Stderr)
		os.Exit(1)
	}
}

type atcRequester struct {
	*rata.RequestGenerator
	httpClient *http.Client
}

func newAtcRequester(atcUrl string, insecure bool) *atcRequester {
	tlsClientConfig := &tls.Config{InsecureSkipVerify: insecure}

	return &atcRequester{
		rata.NewRequestGenerator(atcUrl, atc.Routes),
		&http.Client{Transport: &http.Transport{TLSClientConfig: tlsClientConfig}},
	}
}

func (ar *atcRequester) CreateHTTPRequest(
	name string,
	params rata.Params,
	body io.Reader,
) (*http.Request, error) {
	request, err := ar.CreateRequest(name, params, body)
	if err != nil {
		return nil, err
	}

	url := request.URL
	url.Scheme = "http"
	request.URL = url
	return request, nil
}

func getGitFiles(dir string) ([]string, error) {
	tracked, err := gitLS(dir)
	if err != nil {
		return nil, err
	}

	untracked, err := gitLS(dir, "--others", "--exclude-standard")
	if err != nil {
		return nil, err
	}

	return append(tracked, untracked...), nil
}

func gitLS(dir string, flags ...string) ([]string, error) {
	files := []string{}

	gitLS := exec.Command("git", append([]string{"ls-files", "-z"}, flags...)...)
	gitLS.Dir = dir

	gitOut, err := gitLS.StdoutPipe()
	if err != nil {
		return nil, err
	}

	outScan := bufio.NewScanner(gitOut)
	outScan.Split(scanNull)

	err = gitLS.Start()
	if err != nil {
		return nil, err
	}

	for outScan.Scan() {
		files = append(files, outScan.Text())
	}

	err = gitLS.Wait()
	if err != nil {
		return nil, err
	}

	return files, nil
}

func scanNull(data []byte, atEOF bool) (int, []byte, error) {
	// eof, no more data; terminate
	if atEOF && len(data) == 0 {
		return 0, nil, nil
	}

	// look for terminating null byte
	if i := bytes.IndexByte(data, 0); i >= 0 {
		return i + 1, data[0:i], nil
	}

	// no final terminator; return what's left
	if atEOF {
		return len(data), data, nil
	}

	// request more data
	return 0, nil, nil
}
<|endoftext|>


<commit_before>package commands

import (
	"context"
	"path"

	cli "github.com/urfave/cli"

	"github.com/ipfs/iptb/testbed"
)

var TestbedCmd = cli.Command{
	Name:  "testbed",
	Usage: "manage testbeds",
	Subcommands: []cli.Command{
		TestbedCreateCmd,
	},
}

var TestbedCreateCmd = cli.Command{
	Name:      "create",
	Usage:     "create testbed",
	ArgsUsage: "--type <type>",
	Flags: []cli.Flag{
		cli.IntFlag{
			Name:  "count",
			Usage: "number of nodes to initialize",
			Value: 1,
		},
		cli.BoolFlag{
			Name:  "force",
			Usage: "force overwrite of existing testbed",
		},
		cli.StringFlag{
			Name:   "type",
			Usage:  "kind of nodes to initialize",
			EnvVar: "IPTB_PLUGIN",
		},
		cli.StringSliceFlag{
			Name:  "attr",
			Usage: "specify addition attributes for nodes",
		},
		cli.BoolFlag{
			Name:  "init",
			Usage: "initialize after creation (like calling `init` after create)",
		},
	},
	Action: func(c *cli.Context) error {
		flagRoot := c.GlobalString("IPTB_ROOT")
		flagTestbed := c.GlobalString("testbed")
		flagType := c.String("type")
		flagInit := c.Bool("init")
		flagCount := c.Int("count")
		flagForce := c.Bool("force")
		flagAttrs := c.StringSlice("attr")

		attrs := parseAttrSlice(flagAttrs)
		tb := testbed.NewTestbed(path.Join(flagRoot, "testbeds", flagTestbed))

		if err := testbed.AlreadyInitCheck(tb.Dir(), flagForce); err != nil {
			return err
		}

		specs, err := testbed.BuildSpecs(tb.Dir(), flagCount, flagType, attrs)
		if err != nil {
			return err
		}

		if err := testbed.WriteNodeSpecs(tb.Dir(), specs); err != nil {
			return err
		}

		if flagInit {
			nodes, err := tb.Nodes()
			if err != nil {
				return err
			}

			for _, n := range nodes {
				if _, err := n.Init(context.Background()); err != nil {
					return err
				}
			}
		}

		return nil
	},
}
<commit_msg>fix: handle create with no flags<commit_after>package commands

import (
	"context"
	"fmt"
	"path"

	cli "github.com/urfave/cli"

	"github.com/ipfs/iptb/testbed"
)

var TestbedCmd = cli.Command{
	Name:  "testbed",
	Usage: "manage testbeds",
	Subcommands: []cli.Command{
		TestbedCreateCmd,
	},
}

var TestbedCreateCmd = cli.Command{
	Name:      "create",
	Usage:     "create testbed",
	ArgsUsage: "--type <type>",
	Flags: []cli.Flag{
		cli.IntFlag{
			Name:  "count",
			Usage: "number of nodes to initialize",
			Value: 0,
		},
		cli.BoolFlag{
			Name:  "force",
			Usage: "force overwrite of existing testbed",
		},
		cli.StringFlag{
			Name:   "type",
			Usage:  "kind of nodes to initialize",
			EnvVar: "IPTB_PLUGIN",
		},
		cli.StringSliceFlag{
			Name:  "attr",
			Usage: "specify addition attributes for nodes",
		},
		cli.BoolFlag{
			Name:  "init",
			Usage: "initialize after creation (like calling `init` after create)",
		},
	},
	Action: func(c *cli.Context) error {
		flagRoot := c.GlobalString("IPTB_ROOT")
		flagTestbed := c.GlobalString("testbed")
		flagType := c.String("type")
		flagInit := c.Bool("init")
		flagCount := c.Int("count")
		flagForce := c.Bool("force")
		flagAttrs := c.StringSlice("attr")

		attrs := parseAttrSlice(flagAttrs)
		tb := testbed.NewTestbed(path.Join(flagRoot, "testbeds", flagTestbed))

		if err := testbed.AlreadyInitCheck(tb.Dir(), flagForce); err != nil {
			return err
		}

		var specs []*testbed.NodeSpec
		if flagCount > 0 {
			if flagType == "" {
				return fmt.Errorf("must specify a type to create testbed nodes")
			}

			var err error
			specs, err = testbed.BuildSpecs(tb.Dir(), flagCount, flagType, attrs)
			if err != nil {
				return err
			}
		}

		if err := testbed.WriteNodeSpecs(tb.Dir(), specs); err != nil {
			return err
		}

		if flagInit {
			nodes, err := tb.Nodes()
			if err != nil {
				return err
			}

			for _, n := range nodes {
				if _, err := n.Init(context.Background()); err != nil {
					return err
				}
			}
		}

		return nil
	},
}
<|endoftext|>


<commit_before>package common

import (
	"fmt"
	"strings"
	"testing"

	"github.com/stretchr/testify/assert"
)

func TestIterateKeysWithValues(t *testing.T) {
	cmap := NewCMap()

	for i := 1; i <= 10; i++ {
		cmap.Set(fmt.Sprintf("key%d", i), fmt.Sprintf("value%d", i))
	}

	// Testing size
	assert.Equal(t, cmap.Size(), 10, "overall size should be 10")
	assert.Equal(t, len(cmap.Keys()), 10, "should be 10 keys")
	assert.Equal(t, len(cmap.Values()), 10, "should be 10 values")

	// Iterating Keys, checking for matching Value
	for _, key := range cmap.Keys() {
		val := strings.Replace(key, "key", "value", -1)
		assert.Equal(t, cmap.Get(key), val)
	}

	// Test if all keys are within []Keys()
	keys := cmap.Keys()
	for i := 1; i <= 10; i++ {
		assert.True(t, contains(keys, fmt.Sprintf("key%d", i)), "cmap.Keys() should contain key")
	}

	// Delete 1 Key
	cmap.Delete("key1")

	assert.NotEqual(t, len(keys), len(cmap.Keys()), "[]keys and []Keys() should not be equal, they are copies, one item was removed")

}

func TestContains(t *testing.T) {
	cmap := NewCMap()

	cmap.Set("key1", "value1")

	// Test for known values
	assert.True(t, cmap.Has("key1"), "should contain key1")
	assert.Equal(t, cmap.Get("key1"), "value1", "key1.value() should be value1")

	// Test for unknown values
	assert.False(t, cmap.Has("key2"), "should not contain key2")
	assert.Nil(t, cmap.Get("key2"), "does not contain key2")
}

func contains(array []string, value string) (bool) {
	for _, val := range array {
		if val == value {
			return true
		}
	}
	return false
}
<commit_msg>use assert.Contains in cmap_test<commit_after>package common

import (
	"fmt"
	"strings"
	"testing"

	"github.com/stretchr/testify/assert"
)

func TestIterateKeysWithValues(t *testing.T) {
	cmap := NewCMap()

	for i := 1; i <= 10; i++ {
		cmap.Set(fmt.Sprintf("key%d", i), fmt.Sprintf("value%d", i))
	}

	// Testing size
	assert.Equal(t, 10, cmap.Size())
	assert.Equal(t, 10, len(cmap.Keys()))
	assert.Equal(t, 10, len(cmap.Values()))

	// Iterating Keys, checking for matching Value
	for _, key := range cmap.Keys() {
		val := strings.Replace(key, "key", "value", -1)
		assert.Equal(t, val, cmap.Get(key))
	}

	// Test if all keys are within []Keys()
	keys := cmap.Keys()
	for i := 1; i <= 10; i++ {
		assert.Contains(t, keys, fmt.Sprintf("key%d", i), "cmap.Keys() should contain key")
	}

	// Delete 1 Key
	cmap.Delete("key1")

	assert.NotEqual(t, len(keys), len(cmap.Keys()), "[]keys and []Keys() should not be equal, they are copies, one item was removed")
}

func TestContains(t *testing.T) {
	cmap := NewCMap()

	cmap.Set("key1", "value1")

	// Test for known values
	assert.True(t, cmap.Has("key1"))
	assert.Equal(t, "value1", cmap.Get("key1"))

	// Test for unknown values
	assert.False(t, cmap.Has("key2"))
	assert.Nil(t, cmap.Get("key2"))
}
<|endoftext|>


<commit_before>package binny

import (
	"bufio"
	"encoding"
	"encoding/gob"
	"io"
	"math"
	"reflect"
	"unsafe"
)

const DefaultEncoderBufferSize = 4096

// Marshaler is the interface implemented by objects that can marshal themselves into a binary representation.
// Implementing this bypasses reflection and is generally faster but not nessecery optimized.
type Marshaler interface {
	MarshalBinny(enc *Encoder) error
}

type Encoder struct {
	w *bufio.Writer
}

// NewEncoder returns a new encoder with the DefaultEncoderBufferSize
func NewEncoder(w io.Writer) *Encoder {
	return NewEncoderSize(w, DefaultEncoderBufferSize)
}

// NewEncoder returns a new encoder with the specific buffer size, minimum is 24 bytes.
func NewEncoderSize(w io.Writer, sz int) *Encoder {
	if sz < 24 {
		sz = 24
	}
	return &Encoder{
		w: bufio.NewWriterSize(w, sz),
	}
}

// Reset discards any unflushed buffered data, clears any error, and
// resets b to write its output to w.
func (enc *Encoder) Reset(w io.Writer) {
	enc.w.Reset(w)
}

func (enc *Encoder) writeType(t Type) error {
	return enc.w.WriteByte(byte(t))
}

func (enc *Encoder) WriteVarUint(x uint64) (err error) {
	enc.writeType(VarUint)
	return enc.writeVarUint(x)
}

func (enc *Encoder) writeVarUint(x uint64) (err error) {
	for x >= 0x80 {
		enc.w.WriteByte(byte(x) | 0x80)
		x >>= 7
	}
	err = enc.w.WriteByte(byte(x))
	return
}

func (enc *Encoder) WriteVarInt(x int64) (err error) {
	enc.writeType(VarInt)
	return enc.writeVarInt(x)
}

func (enc *Encoder) writeVarInt(x int64) error {
	ux := uint64(x) << 1
	if x < 0 {
		ux = ^ux
	}
	return enc.writeVarUint(ux)
}

func (enc *Encoder) WriteString(v string) error {
	enc.writeType(String)
	enc.writeLen(len(v))
	_, err := enc.w.WriteString(v)
	return err
}

func (enc *Encoder) WriteBytes(v []byte) error {
	enc.writeType(ByteSlice)
	enc.writeLen(len(v))
	_, err := enc.Write(v)
	return err
}

func (enc *Encoder) Encode(v interface{}) error {
	switch v := v.(type) {
	case Marshaler:
		return v.MarshalBinny(enc)
	case encoding.BinaryMarshaler:
		return enc.WriteBinary(v)
	case gob.GobEncoder:
		return enc.WriteGob(v)
	case string:
		return enc.WriteString(v)
	case []byte:
		return enc.WriteBytes(v)
	case int64:
		return enc.WriteInt(int64(v))
	case int32:
		return enc.WriteInt(int64(v))
	case int16:
		return enc.WriteInt(int64(v))
	case int8:
		return enc.WriteInt8(v)
	case int:
		return enc.WriteInt(int64(v))
	case uint64:
		return enc.WriteUint(uint64(v))
	case uint32:
		return enc.WriteUint(uint64(v))
	case uint16:
		return enc.WriteUint(uint64(v))
	case uint8:
		return enc.WriteUint8(v)
	case uint:
		return enc.WriteUint(uint64(v))
	case float32:
		return enc.WriteFloat32(v)
	case float64:
		return enc.WriteFloat64(v)
	case complex64:
		return enc.WriteComplex64(v)
	case complex128:
		return enc.WriteComplex128(v)
	case bool:
		return enc.WriteBool(v)
	}
	return enc.encodeValue(reflect.ValueOf(v))
}

func (enc *Encoder) encodeValue(v reflect.Value) error {
	fn := typeEncoder(v.Type())
	return fn(enc, v)
}

// WriteUint writes v in the smallest possible native size
func (enc *Encoder) WriteUint(v uint64) error {
	if v <= math.MaxUint8 {
		return enc.WriteUint8(uint8(v))
	}
	if v <= math.MaxUint16 {
		return enc.WriteUint16(uint16(v))
	}
	if v <= math.MaxUint32 {
		return enc.WriteUint32(uint32(v))
	}
	return enc.WriteUint64(v)
}

// WriteInt writes u in the smallest possible native size
func (enc *Encoder) WriteInt(v int64) error {
	u := v
	if u < 0 {
		u = -u
	}
	if u <= math.MaxInt8 {
		return enc.WriteInt8(int8(v))
	}
	if u <= math.MaxInt16 {
		return enc.WriteInt16(int16(v))
	}
	if u <= math.MaxInt32 {
		return enc.WriteInt32(int32(v))
	}
	return enc.WriteInt64(v)
}

func (enc *Encoder) WriteBool(v bool) error {
	if v {
		return enc.writeType(BoolTrue)
	}
	return enc.writeType(BoolFalse)

}

func (enc *Encoder) WriteInt8(v int8) (err error) {
	enc.writeType(Int8)
	return enc.w.WriteByte(byte(v))
}

func (enc *Encoder) WriteUint8(v uint8) (err error) {
	enc.writeType(Uint8)
	return enc.w.WriteByte(v)
}

func (enc *Encoder) WriteByte(v byte) (err error) {
	return enc.WriteUint8(v)
}

func (enc *Encoder) WriteUint16(v uint16) error {
	enc.writeType(Uint16)
	_, err := enc.Write((*[2]byte)(unsafe.Pointer(&v))[:2:2])
	return err
}

func (enc *Encoder) WriteInt16(v int16) error {
	enc.writeType(Int16)
	_, err := enc.Write((*[2]byte)(unsafe.Pointer(&v))[:2:2])
	return err
}

func (enc *Encoder) WriteUint32(v uint32) error {
	enc.writeType(Uint32)
	_, err := enc.Write((*[4]byte)(unsafe.Pointer(&v))[:4:4])
	return err
}

func (enc *Encoder) WriteInt32(v int32) error {
	enc.writeType(Int32)
	_, err := enc.Write((*[4]byte)(unsafe.Pointer(&v))[:4:4])
	return err
}

func (enc *Encoder) WriteUint64(v uint64) error {
	enc.writeType(Uint64)
	_, err := enc.Write((*[8]byte)(unsafe.Pointer(&v))[:8:8])
	return err
}

func (enc *Encoder) WriteInt64(v int64) error {
	enc.writeType(Int64)
	_, err := enc.Write((*[8]byte)(unsafe.Pointer(&v))[:8:8])
	return err
}

func (enc *Encoder) WriteFloat32(v float32) error {
	enc.writeType(Float32)
	_, err := enc.Write((*[4]byte)(unsafe.Pointer(&v))[:4:4])
	return err
}

func (enc *Encoder) WriteFloat64(v float64) error {
	enc.writeType(Float64)
	_, err := enc.Write((*[8]byte)(unsafe.Pointer(&v))[:8:8])
	return err
}

func (enc *Encoder) WriteComplex64(v complex64) error {
	enc.writeType(Complex64)
	_, err := enc.Write((*[8]byte)(unsafe.Pointer(&v))[:8:8])
	return err
}

func (enc *Encoder) WriteComplex128(v complex128) error {
	enc.writeType(Complex128)
	_, err := enc.Write((*[16]byte)(unsafe.Pointer(&v))[:16:16])
	return err
}

func (enc *Encoder) WriteBinary(v encoding.BinaryMarshaler) error {
	b, err := v.MarshalBinary()
	if err != nil {
		return err
	}
	enc.writeType(Binary)
	enc.writeLen(len(b))
	_, err = enc.Write(b)
	return err
}

func (enc *Encoder) WriteGob(v gob.GobEncoder) error {
	b, err := v.GobEncode()
	if err != nil {
		return err
	}
	enc.writeType(Gob)
	enc.writeLen(len(b))
	_, err = enc.Write(b)
	return err
}

// Write writes the contents of p into the buffer.
// It allows the Encoder to be used as a regular io.Writer since it takes control of the original w.
func (enc *Encoder) Write(p []byte) (n int, err error) {
	return enc.w.Write(p)
}

// Flush writes any buffered data to the underlying io.Writer.
func (enc *Encoder) Flush() error {
	return enc.w.Flush()
}

func (enc *Encoder) writeLen(ln int) error {
	return enc.WriteUint(uint64(ln))
}

// Marshal is an alias for (sync.Pool'ed) NewEncoder(bytes.NewBuffer()).Encode(v)
func Marshal(v interface{}) ([]byte, error) {
	enc := getEncBuffer()
	err := enc.e.Encode(v)
	enc.e.Flush()
	b := []byte(enc.b.String()) // have to create a copy sadly :(
	putEncBuffer(enc)
	return b, err
}
<commit_msg>auto flush on encode<commit_after>package binny

import (
	"bufio"
	"encoding"
	"encoding/gob"
	"io"
	"math"
	"reflect"
	"unsafe"
)

const DefaultEncoderBufferSize = 4096

// Marshaler is the interface implemented by objects that can marshal themselves into a binary representation.
// Implementing this bypasses reflection and is generally faster but not nessecery optimized.
type Marshaler interface {
	MarshalBinny(enc *Encoder) error
}

type Encoder struct {
	w *bufio.Writer
}

// NewEncoder returns a new encoder with the DefaultEncoderBufferSize
func NewEncoder(w io.Writer) *Encoder {
	return NewEncoderSize(w, DefaultEncoderBufferSize)
}

// NewEncoder returns a new encoder with the specific buffer size, minimum is 24 bytes.
func NewEncoderSize(w io.Writer, sz int) *Encoder {
	if sz < 24 {
		sz = 24
	}
	return &Encoder{
		w: bufio.NewWriterSize(w, sz),
	}
}

// Reset discards any unflushed buffered data, clears any error, and
// resets b to write its output to w.
func (enc *Encoder) Reset(w io.Writer) {
	enc.w.Reset(w)
}

func (enc *Encoder) writeType(t Type) error {
	return enc.w.WriteByte(byte(t))
}

func (enc *Encoder) WriteVarUint(x uint64) (err error) {
	enc.writeType(VarUint)
	return enc.writeVarUint(x)
}

func (enc *Encoder) writeVarUint(x uint64) (err error) {
	for x >= 0x80 {
		enc.w.WriteByte(byte(x) | 0x80)
		x >>= 7
	}
	err = enc.w.WriteByte(byte(x))
	return
}

func (enc *Encoder) WriteVarInt(x int64) (err error) {
	enc.writeType(VarInt)
	return enc.writeVarInt(x)
}

func (enc *Encoder) writeVarInt(x int64) error {
	ux := uint64(x) << 1
	if x < 0 {
		ux = ^ux
	}
	return enc.writeVarUint(ux)
}

func (enc *Encoder) WriteString(v string) error {
	enc.writeType(String)
	enc.writeLen(len(v))
	_, err := enc.w.WriteString(v)
	return err
}

func (enc *Encoder) WriteBytes(v []byte) error {
	enc.writeType(ByteSlice)
	enc.writeLen(len(v))
	_, err := enc.Write(v)
	return err
}

func (enc *Encoder) Encode(v interface{}) error {
	defer enc.Flush()
	switch v := v.(type) {
	case Marshaler:
		return v.MarshalBinny(enc)
	case encoding.BinaryMarshaler:
		return enc.WriteBinary(v)
	case gob.GobEncoder:
		return enc.WriteGob(v)
	case string:
		return enc.WriteString(v)
	case []byte:
		return enc.WriteBytes(v)
	case int64:
		return enc.WriteInt(int64(v))
	case int32:
		return enc.WriteInt(int64(v))
	case int16:
		return enc.WriteInt(int64(v))
	case int8:
		return enc.WriteInt8(v)
	case int:
		return enc.WriteInt(int64(v))
	case uint64:
		return enc.WriteUint(uint64(v))
	case uint32:
		return enc.WriteUint(uint64(v))
	case uint16:
		return enc.WriteUint(uint64(v))
	case uint8:
		return enc.WriteUint8(v)
	case uint:
		return enc.WriteUint(uint64(v))
	case float32:
		return enc.WriteFloat32(v)
	case float64:
		return enc.WriteFloat64(v)
	case complex64:
		return enc.WriteComplex64(v)
	case complex128:
		return enc.WriteComplex128(v)
	case bool:
		return enc.WriteBool(v)
	}
	return enc.encodeValue(reflect.ValueOf(v))
}

func (enc *Encoder) encodeValue(v reflect.Value) error {
	fn := typeEncoder(v.Type())
	return fn(enc, v)
}

// WriteUint writes v in the smallest possible native size
func (enc *Encoder) WriteUint(v uint64) error {
	if v <= math.MaxUint8 {
		return enc.WriteUint8(uint8(v))
	}
	if v <= math.MaxUint16 {
		return enc.WriteUint16(uint16(v))
	}
	if v <= math.MaxUint32 {
		return enc.WriteUint32(uint32(v))
	}
	return enc.WriteUint64(v)
}

// WriteInt writes u in the smallest possible native size
func (enc *Encoder) WriteInt(v int64) error {
	u := v
	if u < 0 {
		u = -u
	}
	if u <= math.MaxInt8 {
		return enc.WriteInt8(int8(v))
	}
	if u <= math.MaxInt16 {
		return enc.WriteInt16(int16(v))
	}
	if u <= math.MaxInt32 {
		return enc.WriteInt32(int32(v))
	}
	return enc.WriteInt64(v)
}

func (enc *Encoder) WriteBool(v bool) error {
	if v {
		return enc.writeType(BoolTrue)
	}
	return enc.writeType(BoolFalse)

}

func (enc *Encoder) WriteInt8(v int8) (err error) {
	enc.writeType(Int8)
	return enc.w.WriteByte(byte(v))
}

func (enc *Encoder) WriteUint8(v uint8) (err error) {
	enc.writeType(Uint8)
	return enc.w.WriteByte(v)
}

func (enc *Encoder) WriteByte(v byte) (err error) {
	return enc.WriteUint8(v)
}

func (enc *Encoder) WriteUint16(v uint16) error {
	enc.writeType(Uint16)
	_, err := enc.Write((*[2]byte)(unsafe.Pointer(&v))[:2:2])
	return err
}

func (enc *Encoder) WriteInt16(v int16) error {
	enc.writeType(Int16)
	_, err := enc.Write((*[2]byte)(unsafe.Pointer(&v))[:2:2])
	return err
}

func (enc *Encoder) WriteUint32(v uint32) error {
	enc.writeType(Uint32)
	_, err := enc.Write((*[4]byte)(unsafe.Pointer(&v))[:4:4])
	return err
}

func (enc *Encoder) WriteInt32(v int32) error {
	enc.writeType(Int32)
	_, err := enc.Write((*[4]byte)(unsafe.Pointer(&v))[:4:4])
	return err
}

func (enc *Encoder) WriteUint64(v uint64) error {
	enc.writeType(Uint64)
	_, err := enc.Write((*[8]byte)(unsafe.Pointer(&v))[:8:8])
	return err
}

func (enc *Encoder) WriteInt64(v int64) error {
	enc.writeType(Int64)
	_, err := enc.Write((*[8]byte)(unsafe.Pointer(&v))[:8:8])
	return err
}

func (enc *Encoder) WriteFloat32(v float32) error {
	enc.writeType(Float32)
	_, err := enc.Write((*[4]byte)(unsafe.Pointer(&v))[:4:4])
	return err
}

func (enc *Encoder) WriteFloat64(v float64) error {
	enc.writeType(Float64)
	_, err := enc.Write((*[8]byte)(unsafe.Pointer(&v))[:8:8])
	return err
}

func (enc *Encoder) WriteComplex64(v complex64) error {
	enc.writeType(Complex64)
	_, err := enc.Write((*[8]byte)(unsafe.Pointer(&v))[:8:8])
	return err
}

func (enc *Encoder) WriteComplex128(v complex128) error {
	enc.writeType(Complex128)
	_, err := enc.Write((*[16]byte)(unsafe.Pointer(&v))[:16:16])
	return err
}

func (enc *Encoder) WriteBinary(v encoding.BinaryMarshaler) error {
	b, err := v.MarshalBinary()
	if err != nil {
		return err
	}
	enc.writeType(Binary)
	enc.writeLen(len(b))
	_, err = enc.Write(b)
	return err
}

func (enc *Encoder) WriteGob(v gob.GobEncoder) error {
	b, err := v.GobEncode()
	if err != nil {
		return err
	}
	enc.writeType(Gob)
	enc.writeLen(len(b))
	_, err = enc.Write(b)
	return err
}

// Write writes the contents of p into the buffer.
// It allows the Encoder to be used as a regular io.Writer since it takes control of the original w.
func (enc *Encoder) Write(p []byte) (n int, err error) {
	return enc.w.Write(p)
}

// Flush writes any buffered data to the underlying io.Writer.
func (enc *Encoder) Flush() error {
	return enc.w.Flush()
}

func (enc *Encoder) writeLen(ln int) error {
	return enc.WriteUint(uint64(ln))
}

// Marshal is an alias for (sync.Pool'ed) NewEncoder(bytes.NewBuffer()).Encode(v)
func Marshal(v interface{}) ([]byte, error) {
	enc := getEncBuffer()
	err := enc.e.Encode(v)
	enc.e.Flush()
	b := []byte(enc.b.String()) // have to create a copy sadly :(
	putEncBuffer(enc)
	return b, err
}
<|endoftext|>


<commit_before>package fastpass

import (
	"sort"

	"github.com/renstrom/fuzzysearch/fuzzy"
)

//Entries is a set of entries
type Entries []*Entry

//FuzzyMatch returns all entries with names fuzzy matching search
func (es Entries) FuzzyMatch(search string) (ret Entries) {
	for _, entry := range es {
		if fuzzy.Match(search, entry.Name) {
			ret = append(ret, entry)
		}
	}
	return
}

//DeleteByName deletes an entry from es and returns a new slice
func (es Entries) DeleteByName(name string) (cleaned Entries) {
	for _, e := range es {
		if e.Name == name {
			continue
		}
		cleaned = append(cleaned, e)
	}
	return
}

//FindByName finds an entry by it's name.
//It returns nil if no entry was found.
func (es Entries) FindByName(name string) *Entry {
	for _, e := range es {
		if e.Name == name {
			return e
		}
	}
	return nil
}

//SortByName sorts es by name
func (es Entries) SortByName() Entries {
	sort.Slice(es, func(i, j int) bool {
		return es[i].Name < es[j].Name
	})
	return es
}

//SortByHits sorts es by hits
func (es Entries) SortByHits() Entries {
	sort.Slice(es, func(i, j int) bool {
		return es[i].Stats.Hits > es[j].Stats.Hits
	})
	return es
}

//SortByBestMatch tries to sort entries by best match
func (es Entries) SortByBestMatch(search string) Entries {
	distances := make([]int, len(es))
	for i, e := range es {
		distances[i] = fuzzy.RankMatch(search, e.Name)
	}
	sort.Slice(es, func(i, j int) bool {
		iDistance, jDistance := distances[i], distances[j]

		if iDistance < 0 {
			return false
		}
		if iDistance == 0 {
			return true
		}
		if jDistance == 0 {
			return false
		}

		iScore := (float64(es[i].Stats.Hits) / (float64(iDistance) / float64(len(es[i].Name))))
		jScore := (float64(es[j].Stats.Hits) / (float64(jDistance) / float64(len(es[j].Name))))
		return iScore > jScore
	})
	return es
}

// //FilterByTag returns all entries with a certain tag
// func (entries Entries) FilterByTag(tag string) Entries {
// 	var matches Entries
// 	for _, e := range entries {
// 		if e.HasTag(tag) {
// 			matches = append(matches, e)
// 		}
// 	}
// 	return matches
// }
<commit_msg>dont allocate if nothing to delete from entries<commit_after>package fastpass

import (
	"sort"

	"github.com/renstrom/fuzzysearch/fuzzy"
)

//Entries is a set of entries
type Entries []*Entry

//FuzzyMatch returns all entries with names fuzzy matching search
func (es Entries) FuzzyMatch(search string) (ret Entries) {
	for _, entry := range es {
		if fuzzy.Match(search, entry.Name) {
			ret = append(ret, entry)
		}
	}
	return
}

//DeleteByName deletes an entry from es and returns the new slice
func (es Entries) DeleteByName(name string) (cleaned Entries) {
	for i, e := range es {
		if e.Name == name {
			cleaned = append(cleaned, es[:i]...)
			if i != (len(es) - 1) {
				cleaned = append(cleaned, es[i+i:]...)
			}
			return
		}
	}
	return es
}

//FindByName finds an entry by it's name.
//It returns nil if no entry was found.
func (es Entries) FindByName(name string) *Entry {
	for _, e := range es {
		if e.Name == name {
			return e
		}
	}
	return nil
}

//SortByName sorts es by name
func (es Entries) SortByName() Entries {
	sort.Slice(es, func(i, j int) bool {
		return es[i].Name < es[j].Name
	})
	return es
}

//SortByHits sorts es by hits
func (es Entries) SortByHits() Entries {
	sort.Slice(es, func(i, j int) bool {
		return es[i].Stats.Hits > es[j].Stats.Hits
	})
	return es
}

//SortByBestMatch tries to sort entries by best match
func (es Entries) SortByBestMatch(search string) Entries {
	distances := make([]int, len(es))
	for i, e := range es {
		distances[i] = fuzzy.RankMatch(search, e.Name)
	}
	sort.Slice(es, func(i, j int) bool {
		iDistance, jDistance := distances[i], distances[j]

		if iDistance < 0 {
			return false
		}
		if iDistance == 0 {
			return true
		}
		if jDistance == 0 {
			return false
		}

		iScore := (float64(es[i].Stats.Hits) / (float64(iDistance) / float64(len(es[i].Name))))
		jScore := (float64(es[j].Stats.Hits) / (float64(jDistance) / float64(len(es[j].Name))))
		return iScore > jScore
	})
	return es
}

// //FilterByTag returns all entries with a certain tag
// func (entries Entries) FilterByTag(tag string) Entries {
// 	var matches Entries
// 	for _, e := range entries {
// 		if e.HasTag(tag) {
// 			matches = append(matches, e)
// 		}
// 	}
// 	return matches
// }
<|endoftext|>


<commit_before>// Copyright 2016 Keybase, Inc. All rights reserved. Use of
// this source code is governed by the included BSD license.

package client

import (
	"errors"
	"fmt"
	"os"
	"strings"

	"github.com/keybase/cli"
	"github.com/keybase/client/go/libcmdline"
	"github.com/keybase/client/go/libkb"
	"github.com/keybase/client/go/protocol/chat1"
	"github.com/keybase/client/go/protocol/keybase1"
	"golang.org/x/net/context"
)

type CmdChatAPI struct {
	libkb.Contextified
	indent bool
}

func newCmdChatAPI(cl *libcmdline.CommandLine, g *libkb.GlobalContext) cli.Command {
	return cli.Command{
		Name:  "api",
		Usage: "JSON api",
		Action: func(c *cli.Context) {
			cmd := &CmdChatAPI{Contextified: libkb.NewContextified(g)}
			cl.ChooseCommand(cmd, "api", c)
		},
		Flags: []cli.Flag{
			cli.BoolFlag{
				Name:  "p, pretty",
				Usage: "Output pretty (indented) JSON.",
			},
		},
	}
}

func (c *CmdChatAPI) ParseArgv(ctx *cli.Context) error {
	if len(ctx.Args()) != 0 {
		return errors.New("api takes no arguments")
	}
	c.indent = ctx.Bool("pretty")
	return nil
}

func (c *CmdChatAPI) Run() error {
	d := NewChatAPIDecoder(&ChatAPI{svcHandler: c, indent: c.indent})

	// TODO: flags for not using stdin, stdout

	if err := d.Decode(context.Background(), os.Stdin, os.Stdout); err != nil {
		return err
	}

	return nil
}

func (c *CmdChatAPI) GetUsage() libkb.Usage {
	return libkb.Usage{
		API:       true,
		KbKeyring: true,
		Config:    true,
	}
}

type ConvSummary struct {
	ID      chat1.ConversationID `json:"id"`
	Channel ChatChannel          `json:"channel"`
}

type ChatList struct {
	Conversations []ConvSummary `json:"conversations"`
}

func (c *CmdChatAPI) ListV1(ctx context.Context) Reply {
	client, err := GetChatLocalClient(c.G())
	if err != nil {
		return c.errReply(err)
	}

	inbox, err := client.GetInboxLocal(ctx, nil)
	if err != nil {
		return c.errReply(err)
	}

	var cl ChatList
	cl.Conversations = make([]ConvSummary, len(inbox.Conversations))
	for i, conv := range inbox.Conversations {
		if len(conv.MaxMsgs) == 0 {
			return c.errReply(fmt.Errorf("conversation %d had no max msgs", conv.Metadata.ConversationID))
		}
		tlf := conv.MaxMsgs[0].ClientHeader.TlfName
		cl.Conversations[i] = ConvSummary{
			ID: conv.Metadata.ConversationID,
			Channel: ChatChannel{
				Name:      tlf,
				TopicType: strings.ToLower(conv.Metadata.IdTriple.TopicType.String()),
			},
		}
	}

	return Reply{Result: cl}
}

type MsgSender struct {
	UID        string `json:"uid"`
	Username   string `json:"username,omitempty"`
	DeviceID   string `json:"device_id"`
	DeviceName string `json:"device_name,omitempty"`
}

// unfortunately need this...
type MsgContent struct {
	TypeName   string                                `json:"type"`
	Text       *keybase1.MessageText                 `json:"text,omitempty"`
	Attachment *keybase1.MessageAttachment           `json:"attachment,omitempty"`
	Edit       *keybase1.MessageEdit                 `json:"edit,omitempty"`
	Delete     *keybase1.MessageDelete               `json:"delete,omitempty"`
	Metadata   *keybase1.MessageConversationMetadata `json:"metadata,omitempty"`
}

type MsgSummary struct {
	ID       chat1.MessageID `json:"id"`
	Channel  ChatChannel     `json:"channel"`
	Sender   MsgSender       `json:"sender"`
	SentAt   int64           `json:"sent_at"`
	SentAtMs int64           `json:"sent_at_ms"`
	Content  MsgContent      `json:"content"`
}

type Thread struct {
	Messages []MsgSummary `json:"messages"`
}

func (c *CmdChatAPI) ReadV1(ctx context.Context, opts readOptionsV1) Reply {
	client, err := GetChatLocalClient(c.G())
	if err != nil {
		return c.errReply(err)
	}

	if opts.ConversationID == 0 {
		// resolve conversation id
		cinfo := keybase1.ConversationInfoLocal{
			TlfName:   opts.Channel.Name,
			TopicType: opts.Channel.TopicTypeEnum(),
			TopicName: opts.Channel.TopicName,
		}
		existing, err := client.ResolveConversationLocal(ctx, cinfo)
		if err != nil {
			return c.errReply(err)
		}
		if len(existing) > 1 {
			return c.errReply(fmt.Errorf("multiple conversations matched %q", opts.Channel.Name))
		}
		if len(existing) == 0 {
			return c.errReply(fmt.Errorf("no conversations matched %q", opts.Channel.Name))
		}
		opts.ConversationID = existing[0].Id
	}

	arg := keybase1.GetThreadLocalArg{
		ConversationID: opts.ConversationID,
		MarkAsRead:     true,
	}
	threadView, err := client.GetThreadLocal(ctx, arg)
	if err != nil {
		return c.errReply(err)
	}

	var thread Thread
	thread.Messages = make([]MsgSummary, len(threadView.Messages))
	for i, m := range threadView.Messages {
		thread.Messages[i] = MsgSummary{
			ID: m.ServerHeader.MessageID,
			Channel: ChatChannel{
				Name:      m.MessagePlaintext.ClientHeader.TlfName,
				TopicType: strings.ToLower(m.MessagePlaintext.ClientHeader.Conv.TopicType.String()),
			},
			Sender: MsgSender{
				UID:      m.MessagePlaintext.ClientHeader.Sender.String(),
				DeviceID: m.MessagePlaintext.ClientHeader.SenderDevice.String(),
			},
			SentAt:   int64(m.ServerHeader.Ctime / 1000),
			SentAtMs: int64(m.ServerHeader.Ctime),
		}
		if len(m.MessagePlaintext.MessageBodies) > 0 {
			thread.Messages[i].Content = c.convertMsgBody(m.MessagePlaintext.MessageBodies[0])
		}
		if len(m.MessagePlaintext.MessageBodies) > 1 {
			c.G().Log.Warning("message %v had multiple bodies", m.ServerHeader.MessageID)
		}
	}

	return Reply{Result: thread}
}

func (c *CmdChatAPI) SendV1(ctx context.Context, opts sendOptionsV1) Reply {
	client, err := GetChatLocalClient(c.G())
	if err != nil {
		return c.errReply(err)
	}

	// XXX support conversation id
	cinfo := keybase1.ConversationInfoLocal{
		TlfName:   opts.Channel.Name,
		TopicType: opts.Channel.TopicTypeEnum(),
		TopicName: opts.Channel.TopicName,
	}

	// find the conversation
	existing, err := client.ResolveConversationLocal(ctx, cinfo)
	if err != nil {
		return c.errReply(err)
	}
	var conversation keybase1.ConversationInfoLocal
	switch len(existing) {
	case 0:
		conversation, err = client.NewConversationLocal(ctx, cinfo)
		if err != nil {
			return c.errReply(err)
		}
	case 1:
		conversation = existing[0]
	default:
		return c.errReply(fmt.Errorf("multiple conversations matched"))
	}

	// XXX ResolveConversationLocal, NewConversationLocal need to return
	// topic id and tlf id.  In order to fill in conversation id triple
	// below.

	postArg := keybase1.PostLocalArg{
		ConversationID: conversation.Id,
		MessagePlaintext: keybase1.MessagePlaintext{
			ClientHeader: chat1.MessageClientHeader{
				Conv: chat1.ConversationIDTriple{
					TopicType: conversation.TopicType,
				},
				TlfName:     conversation.TlfName,
				MessageType: chat1.MessageType_TEXT,
			},
			MessageBodies: []keybase1.MessageBody{
				keybase1.NewMessageBodyWithText(keybase1.MessageText{Body: opts.Message.Body}),
			},
		},
	}
	if err := client.PostLocal(ctx, postArg); err != nil {
		return c.errReply(err)
	}

	return Reply{Result: "message sent"}
}

func (c *CmdChatAPI) errReply(err error) Reply {
	return Reply{Error: &CallError{Message: err.Error()}}
}

// need this to get message type name
func (c *CmdChatAPI) convertMsgBody(mb keybase1.MessageBody) MsgContent {
	return MsgContent{
		TypeName:   strings.ToLower(chat1.MessageTypeRevMap[mb.MessageType__]),
		Text:       mb.Text__,
		Attachment: mb.Attachment__,
		Edit:       mb.Edit__,
		Delete:     mb.Delete__,
		Metadata:   mb.Metadata__,
	}
}
<commit_msg>Add flags for input, output files<commit_after>// Copyright 2016 Keybase, Inc. All rights reserved. Use of
// this source code is governed by the included BSD license.

package client

import (
	"errors"
	"fmt"
	"io"
	"os"
	"strings"

	"github.com/keybase/cli"
	"github.com/keybase/client/go/libcmdline"
	"github.com/keybase/client/go/libkb"
	"github.com/keybase/client/go/protocol/chat1"
	"github.com/keybase/client/go/protocol/keybase1"
	"golang.org/x/net/context"
)

type CmdChatAPI struct {
	libkb.Contextified
	indent     bool
	inputFile  string
	outputFile string
	message    string
}

func newCmdChatAPI(cl *libcmdline.CommandLine, g *libkb.GlobalContext) cli.Command {
	return cli.Command{
		Name:  "api",
		Usage: "JSON api",
		Action: func(c *cli.Context) {
			cmd := &CmdChatAPI{Contextified: libkb.NewContextified(g)}
			cl.ChooseCommand(cmd, "api", c)
		},
		Flags: []cli.Flag{
			cli.BoolFlag{
				Name:  "p, pretty",
				Usage: "Output pretty (indented) JSON.",
			},
			cli.StringFlag{
				Name:  "m",
				Usage: "Specify JSON as string instead of stdin",
			},
			cli.StringFlag{
				Name:  "i, infile",
				Usage: "Specify JSON input file (stdin default)",
			},
			cli.StringFlag{
				Name:  "o, outfile",
				Usage: "Specify output file (stdout default)",
			},
		},
	}
}

func (c *CmdChatAPI) ParseArgv(ctx *cli.Context) error {
	if len(ctx.Args()) != 0 {
		return errors.New("api takes no arguments")
	}
	c.indent = ctx.Bool("pretty")
	c.inputFile = ctx.String("infile")
	c.outputFile = ctx.String("outfile")
	c.message = ctx.String("message")

	if len(c.message) > 0 && len(c.inputFile) > 0 {
		return errors.New("specify -m or -i, but not both")
	}

	return nil
}

func (c *CmdChatAPI) Run() error {
	d := NewChatAPIDecoder(&ChatAPI{svcHandler: c, indent: c.indent})

	var r io.Reader
	r = os.Stdin
	if len(c.message) > 0 {
		r = strings.NewReader(c.message)
	} else if len(c.inputFile) > 0 {
		f, err := os.Open(c.inputFile)
		if err != nil {
			return err
		}
		defer f.Close()
		r = f
	}

	var w io.Writer
	w = os.Stdout
	if len(c.outputFile) > 0 {
		f, err := os.Create(c.outputFile)
		if err != nil {
			return err
		}
		defer f.Close()
		w = f
	}

	if err := d.Decode(context.Background(), r, w); err != nil {
		return err
	}

	return nil
}

func (c *CmdChatAPI) GetUsage() libkb.Usage {
	return libkb.Usage{
		API:       true,
		KbKeyring: true,
		Config:    true,
	}
}

type ConvSummary struct {
	ID      chat1.ConversationID `json:"id"`
	Channel ChatChannel          `json:"channel"`
}

type ChatList struct {
	Conversations []ConvSummary `json:"conversations"`
}

func (c *CmdChatAPI) ListV1(ctx context.Context) Reply {
	client, err := GetChatLocalClient(c.G())
	if err != nil {
		return c.errReply(err)
	}

	inbox, err := client.GetInboxLocal(ctx, nil)
	if err != nil {
		return c.errReply(err)
	}

	var cl ChatList
	cl.Conversations = make([]ConvSummary, len(inbox.Conversations))
	for i, conv := range inbox.Conversations {
		if len(conv.MaxMsgs) == 0 {
			return c.errReply(fmt.Errorf("conversation %d had no max msgs", conv.Metadata.ConversationID))
		}
		tlf := conv.MaxMsgs[0].ClientHeader.TlfName
		cl.Conversations[i] = ConvSummary{
			ID: conv.Metadata.ConversationID,
			Channel: ChatChannel{
				Name:      tlf,
				TopicType: strings.ToLower(conv.Metadata.IdTriple.TopicType.String()),
			},
		}
	}

	return Reply{Result: cl}
}

type MsgSender struct {
	UID        string `json:"uid"`
	Username   string `json:"username,omitempty"`
	DeviceID   string `json:"device_id"`
	DeviceName string `json:"device_name,omitempty"`
}

// unfortunately need this...
type MsgContent struct {
	TypeName   string                                `json:"type"`
	Text       *keybase1.MessageText                 `json:"text,omitempty"`
	Attachment *keybase1.MessageAttachment           `json:"attachment,omitempty"`
	Edit       *keybase1.MessageEdit                 `json:"edit,omitempty"`
	Delete     *keybase1.MessageDelete               `json:"delete,omitempty"`
	Metadata   *keybase1.MessageConversationMetadata `json:"metadata,omitempty"`
}

type MsgSummary struct {
	ID       chat1.MessageID `json:"id"`
	Channel  ChatChannel     `json:"channel"`
	Sender   MsgSender       `json:"sender"`
	SentAt   int64           `json:"sent_at"`
	SentAtMs int64           `json:"sent_at_ms"`
	Content  MsgContent      `json:"content"`
}

type Thread struct {
	Messages []MsgSummary `json:"messages"`
}

func (c *CmdChatAPI) ReadV1(ctx context.Context, opts readOptionsV1) Reply {
	client, err := GetChatLocalClient(c.G())
	if err != nil {
		return c.errReply(err)
	}

	if opts.ConversationID == 0 {
		// resolve conversation id
		cinfo := keybase1.ConversationInfoLocal{
			TlfName:   opts.Channel.Name,
			TopicType: opts.Channel.TopicTypeEnum(),
			TopicName: opts.Channel.TopicName,
		}
		existing, err := client.ResolveConversationLocal(ctx, cinfo)
		if err != nil {
			return c.errReply(err)
		}
		if len(existing) > 1 {
			return c.errReply(fmt.Errorf("multiple conversations matched %q", opts.Channel.Name))
		}
		if len(existing) == 0 {
			return c.errReply(fmt.Errorf("no conversations matched %q", opts.Channel.Name))
		}
		opts.ConversationID = existing[0].Id
	}

	arg := keybase1.GetThreadLocalArg{
		ConversationID: opts.ConversationID,
		MarkAsRead:     true,
	}
	threadView, err := client.GetThreadLocal(ctx, arg)
	if err != nil {
		return c.errReply(err)
	}

	var thread Thread
	thread.Messages = make([]MsgSummary, len(threadView.Messages))
	for i, m := range threadView.Messages {
		thread.Messages[i] = MsgSummary{
			ID: m.ServerHeader.MessageID,
			Channel: ChatChannel{
				Name:      m.MessagePlaintext.ClientHeader.TlfName,
				TopicType: strings.ToLower(m.MessagePlaintext.ClientHeader.Conv.TopicType.String()),
			},
			Sender: MsgSender{
				UID:      m.MessagePlaintext.ClientHeader.Sender.String(),
				DeviceID: m.MessagePlaintext.ClientHeader.SenderDevice.String(),
			},
			SentAt:   int64(m.ServerHeader.Ctime / 1000),
			SentAtMs: int64(m.ServerHeader.Ctime),
		}
		if len(m.MessagePlaintext.MessageBodies) > 0 {
			thread.Messages[i].Content = c.convertMsgBody(m.MessagePlaintext.MessageBodies[0])
		}
		if len(m.MessagePlaintext.MessageBodies) > 1 {
			c.G().Log.Warning("message %v had multiple bodies", m.ServerHeader.MessageID)
		}
	}

	return Reply{Result: thread}
}

func (c *CmdChatAPI) SendV1(ctx context.Context, opts sendOptionsV1) Reply {
	client, err := GetChatLocalClient(c.G())
	if err != nil {
		return c.errReply(err)
	}

	// XXX support conversation id
	cinfo := keybase1.ConversationInfoLocal{
		TlfName:   opts.Channel.Name,
		TopicType: opts.Channel.TopicTypeEnum(),
		TopicName: opts.Channel.TopicName,
	}

	// find the conversation
	existing, err := client.ResolveConversationLocal(ctx, cinfo)
	if err != nil {
		return c.errReply(err)
	}
	var conversation keybase1.ConversationInfoLocal
	switch len(existing) {
	case 0:
		conversation, err = client.NewConversationLocal(ctx, cinfo)
		if err != nil {
			return c.errReply(err)
		}
	case 1:
		conversation = existing[0]
	default:
		return c.errReply(fmt.Errorf("multiple conversations matched"))
	}

	// XXX ResolveConversationLocal, NewConversationLocal need to return
	// topic id and tlf id.  In order to fill in conversation id triple
	// below.
	// ticket CORE-3746

	postArg := keybase1.PostLocalArg{
		ConversationID: conversation.Id,
		MessagePlaintext: keybase1.MessagePlaintext{
			ClientHeader: chat1.MessageClientHeader{
				Conv: chat1.ConversationIDTriple{
					TopicType: conversation.TopicType,
				},
				TlfName:     conversation.TlfName,
				MessageType: chat1.MessageType_TEXT,
			},
			MessageBodies: []keybase1.MessageBody{
				keybase1.NewMessageBodyWithText(keybase1.MessageText{Body: opts.Message.Body}),
			},
		},
	}
	if err := client.PostLocal(ctx, postArg); err != nil {
		return c.errReply(err)
	}

	return Reply{Result: "message sent"}
}

func (c *CmdChatAPI) errReply(err error) Reply {
	return Reply{Error: &CallError{Message: err.Error()}}
}

// need this to get message type name
func (c *CmdChatAPI) convertMsgBody(mb keybase1.MessageBody) MsgContent {
	return MsgContent{
		TypeName:   strings.ToLower(chat1.MessageTypeRevMap[mb.MessageType__]),
		Text:       mb.Text__,
		Attachment: mb.Attachment__,
		Edit:       mb.Edit__,
		Delete:     mb.Delete__,
		Metadata:   mb.Metadata__,
	}
}
<|endoftext|>