<commit_before>package main

import (
	"container/heap"
	"fmt"
	"github.com/jessevdk/go-flags"
	"github.com/tj/go-dropbox"
	"golang.org/x/text/unicode/norm"
	"os"
	"path/filepath"
	"strings"
)

/* TODO
- Clean up output formatting
- Parallelize local/remote file listing
  - Maybe find additional ways to speed up? Generating local hashes is probably
    largest bottleneck, would this benefit at all from parallelization?
- Add progress printing - maybe collect progress from remote/local listing through channels
- Test for more case issues - already handling when root folder is lowercased
  by Dropbox, but maybe other path components could be as well?
- Ignore more file names in skipLocalFile - see https://www.dropbox.com/help/syncing-uploads/files-not-syncing
*/

// File stores the result of either Dropbox API or local file listing
type File struct {
	Path        string
	ContentHash string
}

// FileHeap is a list of Files sorted by path
type FileHeap []*File

func (h FileHeap) Len() int           { return len(h) }
func (h FileHeap) Less(i, j int) bool { return h[i].Path < h[j].Path }
func (h FileHeap) Swap(i, j int)      { h[i], h[j] = h[j], h[i] }

// Push a File onto the heap
func (h *FileHeap) Push(x interface{}) {
	// Push and Pop use pointer receivers because they modify the slice's length,
	// not just its contents.
	*h = append(*h, x.(*File))
}

// Pop a File off the heap
func (h *FileHeap) Pop() interface{} {
	old := *h
	n := len(old)
	x := old[n-1]
	*h = old[0 : n-1]
	return x
}

// PopOrNil pops a File off the heap or returns nil if there's nothing left
func (h *FileHeap) PopOrNil() *File {
	if h.Len() > 0 {
		return heap.Pop(h).(*File)
	}
	return nil
}

// ManifestComparison records the relative paths that differ between remote and
// local versions of a directory
type ManifestComparison struct {
	OnlyRemote      []string
	OnlyLocal       []string
	ContentMismatch []string
	Matches         int
	Misses          int
}

func main() {
	token := os.Getenv("DROPBOX_ACCESS_TOKEN")
	if token == "" {
		fmt.Fprintln(os.Stderr, "Missing Dropbox OAuth token! Please set the DROPBOX_ACCESS_TOKEN environment variable.")
		os.Exit(1)
	}

	var opts struct {
		Verbose          bool   `short:"v" long:"verbose" description:"Show verbose debug information"`
		RemoteRoot       string `short:"r" long:"remote" description:"Directory in Dropbox to verify" default:"/"`
		LocalRoot        string `short:"l" long:"local" description:"Local directory to compare to Dropbox contents" default:"."`
		CheckContentHash bool   `long:"check" description:"Check content hash of local files"`
	}

	_, err := flags.Parse(&opts)
	if err != nil {
		fmt.Fprintln(os.Stderr, err.Error())
		os.Exit(1)
	}

	// Dropbox API uses empty string for root, but for figuring out relative
	// paths of the returned entries it's easier to use "/". Conversion is
	// handled before the API call.
	if opts.RemoteRoot == "" {
		opts.RemoteRoot = "/"
	}
	if opts.RemoteRoot[0] != '/' {
		opts.RemoteRoot = "/" + opts.RemoteRoot
	}

	localRoot, _ := filepath.Abs(opts.LocalRoot)

	dbxClient := dropbox.New(dropbox.NewConfig(token))

	fmt.Printf("Comparing Dropbox directory \"%v\" to local directory \"%v\"\n", opts.RemoteRoot, localRoot)
	if opts.CheckContentHash {
		fmt.Println("Checking content hashes.")
	}
	fmt.Println("")

	dropboxManifest, err := getDropboxManifest(dbxClient, opts.RemoteRoot)
	if err != nil {
		panic(err)
	}

	localManifest, err := getLocalManifest(localRoot, opts.CheckContentHash)
	if err != nil {
		panic(err)
	}

	manifestComparison := compareManifests(dropboxManifest, localManifest)

	fmt.Println("")
	printFileList(manifestComparison.OnlyRemote, "Files only in remote")
	printFileList(manifestComparison.OnlyLocal, "Files only in local")
	printFileList(manifestComparison.ContentMismatch, "Files whose contents don't match")
	total := manifestComparison.Matches + manifestComparison.Misses
	fmt.Println("SUMMARY:")
	fmt.Printf("Files matched: %d/%d\n", manifestComparison.Matches, total)
	fmt.Printf("Files not matched: %d/%d\n", manifestComparison.Misses, total)
}

func getDropboxManifest(dbxClient *dropbox.Client, rootPath string) (manifest *FileHeap, err error) {
	manifest = &FileHeap{}
	heap.Init(manifest)
	cursor := ""
	keepGoing := true

	for keepGoing {
		var resp *dropbox.ListFolderOutput
		if cursor != "" {
			arg := &dropbox.ListFolderContinueInput{Cursor: cursor}
			resp, err = dbxClient.Files.ListFolderContinue(arg)
		} else {
			apiPath := rootPath
			if apiPath == "/" {
				apiPath = ""
			}
			arg := &dropbox.ListFolderInput{
				Path:             apiPath,
				Recursive:        true,
				IncludeMediaInfo: false,
				IncludeDeleted:   false,
			}
			resp, err = dbxClient.Files.ListFolder(arg)
		}
		if err != nil {
			return
		}
		for _, entry := range resp.Entries {
			if entry.Tag == "file" {

				var relPath string
				relPath, err = normalizePath(rootPath, entry.PathDisplay)
				if err != nil {
					return
				}
				heap.Push(manifest, &File{
					Path:        relPath,
					ContentHash: entry.ContentHash,
				})
			}
		}

		cursor = resp.Cursor
		keepGoing = resp.HasMore

		// DEBUG info
		fmt.Printf("%d dropbox files listed          \r", manifest.Len())
	}

	return
}

func getLocalManifest(localRoot string, contentHash bool) (manifest *FileHeap, err error) {
	manifest = &FileHeap{}
	heap.Init(manifest)

	err = filepath.Walk(localRoot, func(entryPath string, info os.FileInfo, err error) error {
		if err != nil {
			return err
		}

		if info.Mode().IsRegular() && !skipLocalFile(entryPath) {
			relPath, err := normalizePath(localRoot, entryPath)
			if err != nil {
				return err
			}

			hash := ""
			if contentHash {
				hash, err = dropbox.FileContentHash(entryPath)
				if err != nil {
					return err
				}
			}

			heap.Push(manifest, &File{
				Path:        relPath,
				ContentHash: hash,
			})

			// DEBUG info
			fmt.Printf("%d local files listed          \r", manifest.Len())
		}

		return nil
	})

	return
}

func normalizePath(root string, entryPath string) (string, error) {
	relPath, err := filepath.Rel(root, entryPath)
	if err != nil {
		return "", err
	}
	if relPath[0:3] == "../" {
		// try lowercase root instead
		relPath, err = filepath.Rel(strings.ToLower(root), entryPath)
		if err != nil {
			return "", err
		}
	}

	// Normalize Unicode combining characters
	relPath = norm.NFC.String(relPath)
	return relPath, nil
}

func skipLocalFile(path string) bool {
	// TODO handle more ignored file names
	if filepath.Base(path) == ".DS_Store" {
		return true
	}
	return false
}

func compareManifests(remoteManifest, localManifest *FileHeap) *ManifestComparison {
	// 1. Pop a path off both remote and local manifests.
	// 2. While remote & local are both not nil:
	//    Compare remote & local:
	//    a. If local is nil or local > remote, this file is only in remote. Record and pop remote again.
	//    b. If remote is nil or local < remote, this file is only in local. Record and pop local again.
	//    c. If local == remote, check for content mismatch. Record if necessary and pop both again.
	comparison := &ManifestComparison{}
	local := localManifest.PopOrNil()
	remote := remoteManifest.PopOrNil()
	for local != nil || remote != nil {
		if local == nil || local.Path > remote.Path {
			comparison.OnlyRemote = append(comparison.OnlyRemote, remote.Path)
			comparison.Misses++
			remote = remoteManifest.PopOrNil()
		} else if remote == nil || local.Path < remote.Path {
			comparison.OnlyLocal = append(comparison.OnlyLocal, local.Path)
			comparison.Misses++
			local = localManifest.PopOrNil()
		} else if remote.Path == local.Path {
			if !compareFileContents(remote, local) {
				comparison.ContentMismatch = append(comparison.ContentMismatch, local.Path)
				comparison.Misses++
			} else {
				comparison.Matches++
			}
			local = localManifest.PopOrNil()
			remote = remoteManifest.PopOrNil()
		} else {
			// This should be unreachable...
			panic(fmt.Sprintf("Unexpected condition! Local: %v, remote: %v", local, remote))
		}
	}
	return comparison
}

func compareFileContents(remote, local *File) bool {
	if remote.ContentHash == "" || local.ContentHash == "" {
		// Missing content hash for one of the files, possibly intentionally,
		// so can't compare. Assume that presence of both is enough to
		// validate.
		return true
	}
	return remote.ContentHash == local.ContentHash
}

func printFileList(files []string, description string) {
	fmt.Printf("%s: %d\n\n", description, len(files))
	for _, path := range files {
		fmt.Println(path)
	}
	if len(files) > 0 {
		fmt.Print("\n\n")
	}
}
<commit_msg>Fix dumb logic bug in manifest comparison<commit_after>package main

import (
	"container/heap"
	"fmt"
	"github.com/jessevdk/go-flags"
	"github.com/tj/go-dropbox"
	"golang.org/x/text/unicode/norm"
	"os"
	"path/filepath"
	"strings"
)

/* TODO
- Clean up output formatting
- Parallelize local/remote file listing
  - Maybe find additional ways to speed up? Generating local hashes is probably
    largest bottleneck, would this benefit at all from parallelization?
- Add progress printing - maybe collect progress from remote/local listing through channels
- Test for more case issues - already handling when root folder is lowercased
  by Dropbox, but maybe other path components could be as well?
- Ignore more file names in skipLocalFile - see https://www.dropbox.com/help/syncing-uploads/files-not-syncing
*/

// File stores the result of either Dropbox API or local file listing
type File struct {
	Path        string
	ContentHash string
}

// FileHeap is a list of Files sorted by path
type FileHeap []*File

func (h FileHeap) Len() int           { return len(h) }
func (h FileHeap) Less(i, j int) bool { return h[i].Path < h[j].Path }
func (h FileHeap) Swap(i, j int)      { h[i], h[j] = h[j], h[i] }

// Push a File onto the heap
func (h *FileHeap) Push(x interface{}) {
	// Push and Pop use pointer receivers because they modify the slice's length,
	// not just its contents.
	*h = append(*h, x.(*File))
}

// Pop a File off the heap
func (h *FileHeap) Pop() interface{} {
	old := *h
	n := len(old)
	x := old[n-1]
	*h = old[0 : n-1]
	return x
}

// PopOrNil pops a File off the heap or returns nil if there's nothing left
func (h *FileHeap) PopOrNil() *File {
	if h.Len() > 0 {
		return heap.Pop(h).(*File)
	}
	return nil
}

// ManifestComparison records the relative paths that differ between remote and
// local versions of a directory
type ManifestComparison struct {
	OnlyRemote      []string
	OnlyLocal       []string
	ContentMismatch []string
	Matches         int
	Misses          int
}

func main() {
	token := os.Getenv("DROPBOX_ACCESS_TOKEN")
	if token == "" {
		fmt.Fprintln(os.Stderr, "Missing Dropbox OAuth token! Please set the DROPBOX_ACCESS_TOKEN environment variable.")
		os.Exit(1)
	}

	var opts struct {
		Verbose          bool   `short:"v" long:"verbose" description:"Show verbose debug information"`
		RemoteRoot       string `short:"r" long:"remote" description:"Directory in Dropbox to verify" default:"/"`
		LocalRoot        string `short:"l" long:"local" description:"Local directory to compare to Dropbox contents" default:"."`
		CheckContentHash bool   `long:"check" description:"Check content hash of local files"`
	}

	_, err := flags.Parse(&opts)
	if err != nil {
		fmt.Fprintln(os.Stderr, err.Error())
		os.Exit(1)
	}

	// Dropbox API uses empty string for root, but for figuring out relative
	// paths of the returned entries it's easier to use "/". Conversion is
	// handled before the API call.
	if opts.RemoteRoot == "" {
		opts.RemoteRoot = "/"
	}
	if opts.RemoteRoot[0] != '/' {
		opts.RemoteRoot = "/" + opts.RemoteRoot
	}

	localRoot, _ := filepath.Abs(opts.LocalRoot)

	dbxClient := dropbox.New(dropbox.NewConfig(token))

	fmt.Printf("Comparing Dropbox directory \"%v\" to local directory \"%v\"\n", opts.RemoteRoot, localRoot)
	if opts.CheckContentHash {
		fmt.Println("Checking content hashes.")
	}
	fmt.Println("")

	dropboxManifest, err := getDropboxManifest(dbxClient, opts.RemoteRoot)
	if err != nil {
		panic(err)
	}

	localManifest, err := getLocalManifest(localRoot, opts.CheckContentHash)
	if err != nil {
		panic(err)
	}

	manifestComparison := compareManifests(dropboxManifest, localManifest)

	fmt.Println("")
	printFileList(manifestComparison.OnlyRemote, "Files only in remote")
	printFileList(manifestComparison.OnlyLocal, "Files only in local")
	printFileList(manifestComparison.ContentMismatch, "Files whose contents don't match")
	total := manifestComparison.Matches + manifestComparison.Misses
	fmt.Println("SUMMARY:")
	fmt.Printf("Files matched: %d/%d\n", manifestComparison.Matches, total)
	fmt.Printf("Files not matched: %d/%d\n", manifestComparison.Misses, total)
}

func getDropboxManifest(dbxClient *dropbox.Client, rootPath string) (manifest *FileHeap, err error) {
	manifest = &FileHeap{}
	heap.Init(manifest)
	cursor := ""
	keepGoing := true

	for keepGoing {
		var resp *dropbox.ListFolderOutput
		if cursor != "" {
			arg := &dropbox.ListFolderContinueInput{Cursor: cursor}
			resp, err = dbxClient.Files.ListFolderContinue(arg)
		} else {
			apiPath := rootPath
			if apiPath == "/" {
				apiPath = ""
			}
			arg := &dropbox.ListFolderInput{
				Path:             apiPath,
				Recursive:        true,
				IncludeMediaInfo: false,
				IncludeDeleted:   false,
			}
			resp, err = dbxClient.Files.ListFolder(arg)
		}
		if err != nil {
			return
		}
		for _, entry := range resp.Entries {
			if entry.Tag == "file" {

				var relPath string
				relPath, err = normalizePath(rootPath, entry.PathDisplay)
				if err != nil {
					return
				}
				heap.Push(manifest, &File{
					Path:        relPath,
					ContentHash: entry.ContentHash,
				})
			}
		}

		cursor = resp.Cursor
		keepGoing = resp.HasMore

		// DEBUG info
		fmt.Printf("%d dropbox files listed          \r", manifest.Len())
	}

	return
}

func getLocalManifest(localRoot string, contentHash bool) (manifest *FileHeap, err error) {
	manifest = &FileHeap{}
	heap.Init(manifest)

	err = filepath.Walk(localRoot, func(entryPath string, info os.FileInfo, err error) error {
		if err != nil {
			return err
		}

		if info.Mode().IsRegular() && !skipLocalFile(entryPath) {
			relPath, err := normalizePath(localRoot, entryPath)
			if err != nil {
				return err
			}

			hash := ""
			if contentHash {
				hash, err = dropbox.FileContentHash(entryPath)
				if err != nil {
					return err
				}
			}

			heap.Push(manifest, &File{
				Path:        relPath,
				ContentHash: hash,
			})

			// DEBUG info
			fmt.Printf("%d local files listed          \r", manifest.Len())
		}

		return nil
	})

	return
}

func normalizePath(root string, entryPath string) (string, error) {
	relPath, err := filepath.Rel(root, entryPath)
	if err != nil {
		return "", err
	}
	if relPath[0:3] == "../" {
		// try lowercase root instead
		relPath, err = filepath.Rel(strings.ToLower(root), entryPath)
		if err != nil {
			return "", err
		}
	}

	// Normalize Unicode combining characters
	relPath = norm.NFC.String(relPath)
	return relPath, nil
}

func skipLocalFile(path string) bool {
	// TODO handle more ignored file names
	if filepath.Base(path) == ".DS_Store" {
		return true
	}
	return false
}

func compareManifests(remoteManifest, localManifest *FileHeap) *ManifestComparison {
	// 1. Pop a path off both remote and local manifests.
	// 2. While remote & local are both not nil:
	//    Compare remote & local:
	//    a. If local is nil or local > remote, this file is only in remote. Record and pop remote again.
	//    b. If remote is nil or local < remote, this file is only in local. Record and pop local again.
	//    c. If local == remote, check for content mismatch. Record if necessary and pop both again.
	comparison := &ManifestComparison{}
	local := localManifest.PopOrNil()
	remote := remoteManifest.PopOrNil()
	for local != nil || remote != nil {
		if local == nil {
			comparison.OnlyRemote = append(comparison.OnlyRemote, remote.Path)
			comparison.Misses++
			remote = remoteManifest.PopOrNil()
		} else if remote == nil {
			comparison.OnlyLocal = append(comparison.OnlyLocal, local.Path)
			comparison.Misses++
			local = localManifest.PopOrNil()
		} else if local.Path > remote.Path {
			comparison.OnlyRemote = append(comparison.OnlyRemote, remote.Path)
			comparison.Misses++
			remote = remoteManifest.PopOrNil()
		} else if local.Path < remote.Path {
			comparison.OnlyLocal = append(comparison.OnlyLocal, local.Path)
			comparison.Misses++
			local = localManifest.PopOrNil()
		} else {
			// this must mean that remote.Path == local.Path
			if compareFileContents(remote, local) {
				comparison.Matches++
			} else {
				comparison.ContentMismatch = append(comparison.ContentMismatch, local.Path)
				comparison.Misses++
			}
			local = localManifest.PopOrNil()
			remote = remoteManifest.PopOrNil()
		}
	}
	return comparison
}

func compareFileContents(remote, local *File) bool {
	if remote.ContentHash == "" || local.ContentHash == "" {
		// Missing content hash for one of the files, possibly intentionally,
		// so can't compare. Assume that presence of both is enough to
		// validate.
		return true
	}
	return remote.ContentHash == local.ContentHash
}

func printFileList(files []string, description string) {
	fmt.Printf("%s: %d\n\n", description, len(files))
	for _, path := range files {
		fmt.Println(path)
	}
	if len(files) > 0 {
		fmt.Print("\n\n")
	}
}
<|endoftext|>


<commit_before>package rtnetlink

import (
	"encoding"
	"fmt"
	"net"
	"os"
	"reflect"
	"testing"

	"github.com/mdlayher/netlink"
)

func TestConnExecute(t *testing.T) {
	req := &LinkMessage{}

	wantnl := netlink.Message{
		Header: netlink.Header{
			Type:  rtmGetLink,
			Flags: netlink.HeaderFlagsRequest,
			// Sequence and PID not set because we are mocking the underlying
			// netlink connection.
		},
		Data: mustMarshal(req),
	}
	wantrt := []LinkMessage{
		LinkMessage{
			Family: 0x101,
			Type:   0x0,
			Index:  0x4030201,
			Flags:  0x101,
			Change: 0x4030201,
			Attributes: LinkAttributes{
				Address:   net.HardwareAddr(nil),
				Broadcast: net.HardwareAddr(nil),
				Name:      "",
				MTU:       0x0,
				Type:      0x0,
				QueueDisc: "",
				Stats:     nil,
			},
		},
	}

	c, tc := testConn(t)
	tc.receive = []netlink.Message{{
		Header: netlink.Header{
			Length: 16,
			// Sequence and PID not set because we are mocking the underlying
			// netlink connection.
		},
		Data: []byte{
			0x01, 0x01, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04,
			0x01, 0x01, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04,
		},
	}}

	msgs, err := c.Execute(req, rtmGetLink, netlink.HeaderFlagsRequest)
	if err != nil {
		t.Fatalf("failed to execute: %v", err)
	}

	links := make([]LinkMessage, 0, len(msgs))
	for _, m := range msgs {
		link := (m).(*LinkMessage)
		links = append(links, *link)
	}

	if want, got := wantnl, tc.send; !reflect.DeepEqual(want, got) {
		t.Fatalf("unexpected request:\n- want: %#v\n-  got: %#v",
			want, got)
	}
	if want, got := wantrt, links; !reflect.DeepEqual(want, got) {
		t.Fatalf("unexpected replies:\n- want: %#v\n-  got: %#v",
			want, got)
	}
}

func TestConnSend(t *testing.T) {
	req := &LinkMessage{}

	c, tc := testConn(t)

	nlreq, err := c.Send(req, rtmGetLink, netlink.HeaderFlagsRequest)
	if err != nil {
		t.Fatalf("failed to send: %v", err)
	}

	reqb, err := req.MarshalBinary()
	if err != nil {
		t.Fatalf("failed to marshal request: %v", err)
	}

	want := netlink.Message{
		Header: netlink.Header{
			Type:  rtmGetLink,
			Flags: netlink.HeaderFlagsRequest,
		},
		Data: reqb,
	}

	if got := tc.send; !reflect.DeepEqual(want, got) {
		t.Fatalf("unexpected output message from Conn.Send:\n- want: %#v\n-  got: %#v",
			want, got)
	}
	if got := nlreq; !reflect.DeepEqual(want, got) {
		t.Fatalf("unexpected modified message:\n- want: %#v\n-  got: %#v",
			want, got)
	}
}

func TestConnReceive(t *testing.T) {
	c, tc := testConn(t)
	tc.receive = []netlink.Message{
		{
			Header: netlink.Header{
				Length:   16,
				Sequence: 1,
				PID:      uint32(os.Getpid()),
			},
			Data: []byte{
				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
			},
		},
		{
			Header: netlink.Header{
				Length:   16,
				Sequence: 1,
				PID:      uint32(os.Getpid()),
			},
			Data: []byte{
				0x02, 0x01, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04,
				0x02, 0x01, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04,
			},
		},
	}

	wantnl := tc.receive
	wantrt := []LinkMessage{
		LinkMessage{
			Family: 0x0,
			Type:   0x0,
			Index:  0x0,
			Flags:  0x0,
			Change: 0x0,
			Attributes: LinkAttributes{
				Address:   net.HardwareAddr(nil),
				Broadcast: net.HardwareAddr(nil),
				Name:      "",
				MTU:       0x0,
				Type:      0x0,
				QueueDisc: "",
				Stats:     nil,
			},
		},
		LinkMessage{
			Family: 0x102,
			Type:   0x0,
			Index:  0x4030201,
			Flags:  0x102,
			Change: 0x4030201,
			Attributes: LinkAttributes{
				Address:   net.HardwareAddr(nil),
				Broadcast: net.HardwareAddr(nil),
				Name:      "",
				MTU:       0x0,
				Type:      0x0,
				QueueDisc: "",
				Stats:     nil,
			},
		},
	}

	rtmsgs, nlmsgs, err := c.Receive()
	if err != nil {
		t.Fatalf("failed to receive messages: %v", err)
	}

	links := make([]LinkMessage, 0, len(rtmsgs))
	for _, m := range rtmsgs {
		link := (m).(*LinkMessage)
		links = append(links, *link)
	}

	if want, got := wantnl, nlmsgs; !reflect.DeepEqual(want, got) {
		t.Fatalf("unexpected netlink.Messages from Conn.Receive:\n- want: %#v\n-  got: %#v",
			want, got)
	}

	if want, got := wantrt, links; !reflect.DeepEqual(want, got) {
		t.Fatalf("unexpected Messages from Conn.Receive:\n- want: %#v\n-  got: %#v",
			want, got)
	}
}

func testConn(t *testing.T) (*Conn, *testNetlinkConn) {
	c := &testNetlinkConn{}
	return newConn(c), c
}

type testNetlinkConn struct {
	send    netlink.Message
	receive []netlink.Message

	noopConn
}

func (c *testNetlinkConn) Send(m netlink.Message) (netlink.Message, error) {
	c.send = m
	return m, nil
}

func (c *testNetlinkConn) Receive() ([]netlink.Message, error) {
	return c.receive, nil
}

type noopConn struct{}

func (c *noopConn) Close() error                                    { return nil }
func (c *noopConn) Send(m netlink.Message) (netlink.Message, error) { return netlink.Message{}, nil }
func (c *noopConn) Receive() ([]netlink.Message, error)             { return nil, nil }

func mustMarshal(m encoding.BinaryMarshaler) []byte {
	b, err := m.MarshalBinary()
	if err != nil {
		panic(fmt.Sprintf("failed to marshal binary: %v", err))
	}

	return b
}
<commit_msg>test: gofmt -s<commit_after>package rtnetlink

import (
	"encoding"
	"fmt"
	"net"
	"os"
	"reflect"
	"testing"

	"github.com/mdlayher/netlink"
)

func TestConnExecute(t *testing.T) {
	req := &LinkMessage{}

	wantnl := netlink.Message{
		Header: netlink.Header{
			Type:  rtmGetLink,
			Flags: netlink.HeaderFlagsRequest,
			// Sequence and PID not set because we are mocking the underlying
			// netlink connection.
		},
		Data: mustMarshal(req),
	}
	wantrt := []LinkMessage{
		{
			Family: 0x101,
			Type:   0x0,
			Index:  0x4030201,
			Flags:  0x101,
			Change: 0x4030201,
			Attributes: LinkAttributes{
				Address:   net.HardwareAddr(nil),
				Broadcast: net.HardwareAddr(nil),
				Name:      "",
				MTU:       0x0,
				Type:      0x0,
				QueueDisc: "",
				Stats:     nil,
			},
		},
	}

	c, tc := testConn(t)
	tc.receive = []netlink.Message{{
		Header: netlink.Header{
			Length: 16,
			// Sequence and PID not set because we are mocking the underlying
			// netlink connection.
		},
		Data: []byte{
			0x01, 0x01, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04,
			0x01, 0x01, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04,
		},
	}}

	msgs, err := c.Execute(req, rtmGetLink, netlink.HeaderFlagsRequest)
	if err != nil {
		t.Fatalf("failed to execute: %v", err)
	}

	links := make([]LinkMessage, 0, len(msgs))
	for _, m := range msgs {
		link := (m).(*LinkMessage)
		links = append(links, *link)
	}

	if want, got := wantnl, tc.send; !reflect.DeepEqual(want, got) {
		t.Fatalf("unexpected request:\n- want: %#v\n-  got: %#v",
			want, got)
	}
	if want, got := wantrt, links; !reflect.DeepEqual(want, got) {
		t.Fatalf("unexpected replies:\n- want: %#v\n-  got: %#v",
			want, got)
	}
}

func TestConnSend(t *testing.T) {
	req := &LinkMessage{}

	c, tc := testConn(t)

	nlreq, err := c.Send(req, rtmGetLink, netlink.HeaderFlagsRequest)
	if err != nil {
		t.Fatalf("failed to send: %v", err)
	}

	reqb, err := req.MarshalBinary()
	if err != nil {
		t.Fatalf("failed to marshal request: %v", err)
	}

	want := netlink.Message{
		Header: netlink.Header{
			Type:  rtmGetLink,
			Flags: netlink.HeaderFlagsRequest,
		},
		Data: reqb,
	}

	if got := tc.send; !reflect.DeepEqual(want, got) {
		t.Fatalf("unexpected output message from Conn.Send:\n- want: %#v\n-  got: %#v",
			want, got)
	}
	if got := nlreq; !reflect.DeepEqual(want, got) {
		t.Fatalf("unexpected modified message:\n- want: %#v\n-  got: %#v",
			want, got)
	}
}

func TestConnReceive(t *testing.T) {
	c, tc := testConn(t)
	tc.receive = []netlink.Message{
		{
			Header: netlink.Header{
				Length:   16,
				Sequence: 1,
				PID:      uint32(os.Getpid()),
			},
			Data: []byte{
				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
			},
		},
		{
			Header: netlink.Header{
				Length:   16,
				Sequence: 1,
				PID:      uint32(os.Getpid()),
			},
			Data: []byte{
				0x02, 0x01, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04,
				0x02, 0x01, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04,
			},
		},
	}

	wantnl := tc.receive
	wantrt := []LinkMessage{
		{
			Family: 0x0,
			Type:   0x0,
			Index:  0x0,
			Flags:  0x0,
			Change: 0x0,
			Attributes: LinkAttributes{
				Address:   net.HardwareAddr(nil),
				Broadcast: net.HardwareAddr(nil),
				Name:      "",
				MTU:       0x0,
				Type:      0x0,
				QueueDisc: "",
				Stats:     nil,
			},
		},
		{
			Family: 0x102,
			Type:   0x0,
			Index:  0x4030201,
			Flags:  0x102,
			Change: 0x4030201,
			Attributes: LinkAttributes{
				Address:   net.HardwareAddr(nil),
				Broadcast: net.HardwareAddr(nil),
				Name:      "",
				MTU:       0x0,
				Type:      0x0,
				QueueDisc: "",
				Stats:     nil,
			},
		},
	}

	rtmsgs, nlmsgs, err := c.Receive()
	if err != nil {
		t.Fatalf("failed to receive messages: %v", err)
	}

	links := make([]LinkMessage, 0, len(rtmsgs))
	for _, m := range rtmsgs {
		link := (m).(*LinkMessage)
		links = append(links, *link)
	}

	if want, got := wantnl, nlmsgs; !reflect.DeepEqual(want, got) {
		t.Fatalf("unexpected netlink.Messages from Conn.Receive:\n- want: %#v\n-  got: %#v",
			want, got)
	}

	if want, got := wantrt, links; !reflect.DeepEqual(want, got) {
		t.Fatalf("unexpected Messages from Conn.Receive:\n- want: %#v\n-  got: %#v",
			want, got)
	}
}

func testConn(t *testing.T) (*Conn, *testNetlinkConn) {
	c := &testNetlinkConn{}
	return newConn(c), c
}

type testNetlinkConn struct {
	send    netlink.Message
	receive []netlink.Message

	noopConn
}

func (c *testNetlinkConn) Send(m netlink.Message) (netlink.Message, error) {
	c.send = m
	return m, nil
}

func (c *testNetlinkConn) Receive() ([]netlink.Message, error) {
	return c.receive, nil
}

type noopConn struct{}

func (c *noopConn) Close() error                                    { return nil }
func (c *noopConn) Send(m netlink.Message) (netlink.Message, error) { return netlink.Message{}, nil }
func (c *noopConn) Receive() ([]netlink.Message, error)             { return nil, nil }

func mustMarshal(m encoding.BinaryMarshaler) []byte {
	b, err := m.MarshalBinary()
	if err != nil {
		panic(fmt.Sprintf("failed to marshal binary: %v", err))
	}

	return b
}
<|endoftext|>


<commit_before>package gocb

import (
	"gopkg.in/couchbase/gocbcore.v7"
)

const (
	goCbVersionStr = "v1.3.3"
)

const (
	// Legacy flag format for JSON data.
	lfJson = 0

	// Common flags mask
	cfMask = 0xFF000000
	// Common flags mask for data format
	cfFmtMask = 0x0F000000
	// Common flags mask for compression mode.
	cfCmprMask = 0xE0000000

	// Common flag format for sdk-private data.
	cfFmtPrivate = 1 << 24
	// Common flag format for JSON data.
	cfFmtJson = 2 << 24
	// Common flag format for binary data.
	cfFmtBinary = 3 << 24
	// Common flag format for string data.
	cfFmtString = 4 << 24

	// Common flags compression for disabled compression.
	cfCmprNone = 0 << 29
)

// IndexType provides information on the type of indexer used for an index.
type IndexType string

const (
	// IndexTypeN1ql indicates that GSI was used to build the index.
	IndexTypeN1ql = IndexType("gsi")

	// IndexTypeView indicates that views were used to build the index.
	IndexTypeView = IndexType("views")
)

// QueryProfileType specifies the profiling mode to use during a query.
type QueryProfileType string

const (
	// QueryProfileNone disables query profiling
	QueryProfileNone = QueryProfileType("off")

	// QueryProfilePhases includes phase profiling information in the query response
	QueryProfilePhases = QueryProfileType("phases")

	// QueryProfileTimings includes timing profiling information in the query response
	QueryProfileTimings = QueryProfileType("timings")
)

// SubdocFlag provides special handling flags for sub-document operations
type SubdocFlag gocbcore.SubdocFlag

const (
	// SubdocFlagNone indicates no special behaviours
	SubdocFlagNone = SubdocFlag(gocbcore.SubdocFlagNone)

	// SubdocFlagCreatePath indicates you wish to recursively create the tree of paths
	// if it does not already exist within the document.
	SubdocFlagCreatePath = SubdocFlag(gocbcore.SubdocFlagMkDirP)

	// SubdocFlagXattr indicates your path refers to an extended attribute rather than the document.
	SubdocFlagXattr = SubdocFlag(gocbcore.SubdocFlagXattrPath)

	// SubdocFlagUseMacros indicates that you wish macro substitution to occur on the value
	SubdocFlagUseMacros = SubdocFlag(gocbcore.SubdocFlagExpandMacros)
)

// SubdocDocFlag specifies document-level flags for a sub-document operation.
type SubdocDocFlag gocbcore.SubdocDocFlag

const (
	// SubdocDocFlagNone indicates no special behaviours
	SubdocDocFlagNone = SubdocDocFlag(gocbcore.SubdocDocFlagNone)

	// SubdocDocFlagMkDoc indicates that the document should be created if it does not already exist.
	SubdocDocFlagMkDoc = SubdocDocFlag(gocbcore.SubdocDocFlagMkDoc)

	// SubdocDocFlagReplaceDoc indices that this operation should be a replace rather than upsert.
	SubdocDocFlagReplaceDoc = SubdocDocFlag(gocbcore.SubdocDocFlagReplaceDoc)

	// SubdocDocFlagAccessDeleted indicates that you wish to receive soft-deleted documents.
	SubdocDocFlagAccessDeleted = SubdocDocFlag(gocbcore.SubdocDocFlagAccessDeleted)
)

// ServiceType specifies a particular Couchbase service type.
type ServiceType gocbcore.ServiceType

const (
	// MemdService represents a memcached service.
	MemdService = ServiceType(gocbcore.MemdService)

	// MgmtService represents a management service (typically ns_server).
	MgmtService = ServiceType(gocbcore.MgmtService)

	// CapiService represents a CouchAPI service (typically for views).
	CapiService = ServiceType(gocbcore.CapiService)

	// N1qlService represents a N1QL service (typically for query).
	N1qlService = ServiceType(gocbcore.N1qlService)

	// FtsService represents a full-text-search service.
	FtsService = ServiceType(gocbcore.FtsService)

	// CbasService represents an analytics service.
	CbasService = ServiceType(gocbcore.CbasService)
)
<commit_msg>Bump version number to v1.3.4<commit_after>package gocb

import (
	"gopkg.in/couchbase/gocbcore.v7"
)

const (
	goCbVersionStr = "v1.3.4"
)

const (
	// Legacy flag format for JSON data.
	lfJson = 0

	// Common flags mask
	cfMask = 0xFF000000
	// Common flags mask for data format
	cfFmtMask = 0x0F000000
	// Common flags mask for compression mode.
	cfCmprMask = 0xE0000000

	// Common flag format for sdk-private data.
	cfFmtPrivate = 1 << 24
	// Common flag format for JSON data.
	cfFmtJson = 2 << 24
	// Common flag format for binary data.
	cfFmtBinary = 3 << 24
	// Common flag format for string data.
	cfFmtString = 4 << 24

	// Common flags compression for disabled compression.
	cfCmprNone = 0 << 29
)

// IndexType provides information on the type of indexer used for an index.
type IndexType string

const (
	// IndexTypeN1ql indicates that GSI was used to build the index.
	IndexTypeN1ql = IndexType("gsi")

	// IndexTypeView indicates that views were used to build the index.
	IndexTypeView = IndexType("views")
)

// QueryProfileType specifies the profiling mode to use during a query.
type QueryProfileType string

const (
	// QueryProfileNone disables query profiling
	QueryProfileNone = QueryProfileType("off")

	// QueryProfilePhases includes phase profiling information in the query response
	QueryProfilePhases = QueryProfileType("phases")

	// QueryProfileTimings includes timing profiling information in the query response
	QueryProfileTimings = QueryProfileType("timings")
)

// SubdocFlag provides special handling flags for sub-document operations
type SubdocFlag gocbcore.SubdocFlag

const (
	// SubdocFlagNone indicates no special behaviours
	SubdocFlagNone = SubdocFlag(gocbcore.SubdocFlagNone)

	// SubdocFlagCreatePath indicates you wish to recursively create the tree of paths
	// if it does not already exist within the document.
	SubdocFlagCreatePath = SubdocFlag(gocbcore.SubdocFlagMkDirP)

	// SubdocFlagXattr indicates your path refers to an extended attribute rather than the document.
	SubdocFlagXattr = SubdocFlag(gocbcore.SubdocFlagXattrPath)

	// SubdocFlagUseMacros indicates that you wish macro substitution to occur on the value
	SubdocFlagUseMacros = SubdocFlag(gocbcore.SubdocFlagExpandMacros)
)

// SubdocDocFlag specifies document-level flags for a sub-document operation.
type SubdocDocFlag gocbcore.SubdocDocFlag

const (
	// SubdocDocFlagNone indicates no special behaviours
	SubdocDocFlagNone = SubdocDocFlag(gocbcore.SubdocDocFlagNone)

	// SubdocDocFlagMkDoc indicates that the document should be created if it does not already exist.
	SubdocDocFlagMkDoc = SubdocDocFlag(gocbcore.SubdocDocFlagMkDoc)

	// SubdocDocFlagReplaceDoc indices that this operation should be a replace rather than upsert.
	SubdocDocFlagReplaceDoc = SubdocDocFlag(gocbcore.SubdocDocFlagReplaceDoc)

	// SubdocDocFlagAccessDeleted indicates that you wish to receive soft-deleted documents.
	SubdocDocFlagAccessDeleted = SubdocDocFlag(gocbcore.SubdocDocFlagAccessDeleted)
)

// ServiceType specifies a particular Couchbase service type.
type ServiceType gocbcore.ServiceType

const (
	// MemdService represents a memcached service.
	MemdService = ServiceType(gocbcore.MemdService)

	// MgmtService represents a management service (typically ns_server).
	MgmtService = ServiceType(gocbcore.MgmtService)

	// CapiService represents a CouchAPI service (typically for views).
	CapiService = ServiceType(gocbcore.CapiService)

	// N1qlService represents a N1QL service (typically for query).
	N1qlService = ServiceType(gocbcore.N1qlService)

	// FtsService represents a full-text-search service.
	FtsService = ServiceType(gocbcore.FtsService)

	// CbasService represents an analytics service.
	CbasService = ServiceType(gocbcore.CbasService)
)
<|endoftext|>


<commit_before>// Package aphgrpc provides various interfaces, functions, types
// for building and working with gRPC services.
package aphgrpc

import (
	"encoding/json"
	"net/http"
	"strconv"

	context "golang.org/x/net/context"
	dat "gopkg.in/mgutz/dat.v1"

	"github.com/grpc-ecosystem/grpc-gateway/runtime"
	"github.com/manyminds/api2go"
	"github.com/pkg/errors"
	"google.golang.org/grpc"
	"google.golang.org/grpc/codes"
	"google.golang.org/grpc/metadata"
	"google.golang.org/grpc/status"
)

const (
	// MetaKey is the key used for storing all metadata
	MetaKey = "error"
)

var (
	//ErrDatabaseQuery represents database query related errors
	ErrDatabaseQuery = newError("Database query error")
	//ErrDatabaseInsert represents database insert related errors
	ErrDatabaseInsert = newError("Database insert error")
	//ErrDatabaseUpdate represents database update related errors
	ErrDatabaseUpdate = newError("Database update error")
	//ErrDatabaseDelete represents database update delete errors
	ErrDatabaseDelete = newError("Database delete error")
	//ErrNotFound represents the absence of an HTTP resource
	ErrNotFound = newError("Resource not found")
	//ErrJSONEncoding represents any json encoding error
	ErrJSONEncoding = newError("Json encoding error")
	//ErrStructMarshal represents any error with marshalling structure
	ErrStructMarshal = newError("Structure marshalling error")
	//ErrIncludeParam represents any error with invalid include query parameter
	ErrIncludeParam = newErrorWithParam("Invalid include query parameter", "include")
	//ErrSparseFieldSets represents any error with invalid sparse fieldsets query parameter
	ErrFields = newErrorWithParam("Invalid field query parameter", "field")
	//ErrFilterParam represents any error with invalid filter query paramter
	ErrFilterParam = newErrorWithParam("Invalid filter query parameter", "filter")
	//ErrNotAcceptable represents any error with wrong or inappropriate http Accept header
	ErrNotAcceptable = newError("Accept header is not acceptable")
	//ErrUnsupportedMedia represents any error with unsupported media type in http header
	ErrUnsupportedMedia = newError("Media type is not supported")
	//ErrRetrieveMetadata represents any error to retrieve grpc metadata from the running context
	ErrRetrieveMetadata = errors.New("unable to retrieve metadata")
	//ErrXForwardedHost represents any failure or absence of x-forwarded-host HTTP header in the grpc context
	ErrXForwardedHost = errors.New("x-forwarded-host header is absent")
)

func newErrorWithParam(msg, param string) metadata.MD {
	return metadata.Pairs(MetaKey, msg, param)
}

func newError(msg string) metadata.MD {
	return metadata.Pairs(MetaKey, msg)
}

// CustomHTTPError is a custom error handler for grpc-gateway to generate
// JSONAPI formatted HTTP response.
func CustomHTTPError(ctx context.Context, _ *runtime.ServeMux, marshaler runtime.Marshaler, w http.ResponseWriter, _ *http.Request, err error) {
	md, ok := runtime.ServerMetadataFromContext(ctx)
	if !ok {
		fallbackError(w, getgRPCStatus(errors.Wrap(err, "unable to retrieve metadata")))
		return
	}
	JSONAPIError(w, md.TrailerMD, getgRPCStatus(err))
}

func getgRPCStatus(err error) *status.Status {
	s, ok := status.FromError(err)
	if !ok {
		return status.New(codes.Unknown, err.Error())
	}
	return s
}

// JSONAPIError generates JSONAPI formatted error message
func JSONAPIError(w http.ResponseWriter, md metadata.MD, s *status.Status) {
	status := runtime.HTTPStatusFromCode(s.Code())
	jsnErr := api2go.Error{
		Status: strconv.Itoa(status),
		Title:  md["error"][0],
		Detail: s.Message(),
		Meta: map[string]interface{}{
			"creator": "api error helper",
		},
	}
	w.Header().Set("Content-Type", "application/vnd.api+json")
	w.WriteHeader(status)
	encErr := json.NewEncoder(w).Encode(api2go.HTTPError{Errors: []api2go.Error{jsnErr}})
	if encErr != nil {
		http.Error(w, encErr.Error(), http.StatusInternalServerError)
	}
}

func fallbackError(w http.ResponseWriter, s *status.Status) {
	status := runtime.HTTPStatusFromCode(s.Code())
	jsnErr := api2go.Error{
		Status: strconv.Itoa(status),
		Title:  "gRPC error",
		Detail: s.Message(),
		Meta: map[string]interface{}{
			"creator": "api error helper",
		},
	}
	w.Header().Set("Content-Type", "application/vnd.api+json")
	w.WriteHeader(status)
	encErr := json.NewEncoder(w).Encode(api2go.HTTPError{Errors: []api2go.Error{jsnErr}})
	if encErr != nil {
		http.Error(w, encErr.Error(), http.StatusInternalServerError)
	}
}

func HandleError(ctx context.Context, err error) error {
	switch err {
	case dat.ErrNotFound:
		grpc.SetTrailer(ctx, ErrNotFound)
		return status.Error(codes.NotFound, err.Error())
	case ErrRetrieveMetadata:
		grpc.SetTrailer(ctx, newError(err.Error()))
		return status.Error(codes.Internal, err.Error())
	case ErrXForwardedHost:
		grpc.SetTrailer(ctx, newError(err.Error()))
		return status.Error(codes.Internal, err.Error())
	default:
		grpc.SetTrailer(ctx, ErrDatabaseQuery)
		return status.Error(codes.Internal, err.Error())
	}
}
<commit_msg>Added correct key to Pair method to pass even no of key-pair values<commit_after>// Package aphgrpc provides various interfaces, functions, types
// for building and working with gRPC services.
package aphgrpc

import (
	"encoding/json"
	"net/http"
	"strconv"
	"strings"

	context "golang.org/x/net/context"
	dat "gopkg.in/mgutz/dat.v1"

	"github.com/grpc-ecosystem/grpc-gateway/runtime"
	"github.com/manyminds/api2go"
	"github.com/pkg/errors"
	"google.golang.org/grpc"
	"google.golang.org/grpc/codes"
	"google.golang.org/grpc/metadata"
	"google.golang.org/grpc/status"
)

const (
	// MetaKey is the key used for storing all metadata
	MetaKey = "error"
)

var (
	//ErrDatabaseQuery represents database query related errors
	ErrDatabaseQuery = newError("Database query error")
	//ErrDatabaseInsert represents database insert related errors
	ErrDatabaseInsert = newError("Database insert error")
	//ErrDatabaseUpdate represents database update related errors
	ErrDatabaseUpdate = newError("Database update error")
	//ErrDatabaseDelete represents database update delete errors
	ErrDatabaseDelete = newError("Database delete error")
	//ErrNotFound represents the absence of an HTTP resource
	ErrNotFound = newError("Resource not found")
	//ErrJSONEncoding represents any json encoding error
	ErrJSONEncoding = newError("Json encoding error")
	//ErrStructMarshal represents any error with marshalling structure
	ErrStructMarshal = newError("Structure marshalling error")
	//ErrIncludeParam represents any error with invalid include query parameter
	ErrIncludeParam = newErrorWithParam("Invalid include query parameter", "include")
	//ErrSparseFieldSets represents any error with invalid sparse fieldsets query parameter
	ErrFields = newErrorWithParam("Invalid field query parameter", "field")
	//ErrFilterParam represents any error with invalid filter query paramter
	ErrFilterParam = newErrorWithParam("Invalid filter query parameter", "filter")
	//ErrNotAcceptable represents any error with wrong or inappropriate http Accept header
	ErrNotAcceptable = newError("Accept header is not acceptable")
	//ErrUnsupportedMedia represents any error with unsupported media type in http header
	ErrUnsupportedMedia = newError("Media type is not supported")
	//ErrRetrieveMetadata represents any error to retrieve grpc metadata from the running context
	ErrRetrieveMetadata = errors.New("unable to retrieve metadata")
	//ErrXForwardedHost represents any failure or absence of x-forwarded-host HTTP header in the grpc context
	ErrXForwardedHost = errors.New("x-forwarded-host header is absent")
)

func newErrorWithParam(msg, param string) metadata.MD {
	return metadata.Pairs(MetaKey, msg, MetaKey, param)
}

func newError(msg string) metadata.MD {
	return metadata.Pairs(MetaKey, msg)
}

// CustomHTTPError is a custom error handler for grpc-gateway to generate
// JSONAPI formatted HTTP response.
func CustomHTTPError(ctx context.Context, _ *runtime.ServeMux, marshaler runtime.Marshaler, w http.ResponseWriter, _ *http.Request, err error) {
	md, ok := runtime.ServerMetadataFromContext(ctx)
	if !ok {
		fallbackError(w, getgRPCStatus(errors.Wrap(err, "unable to retrieve metadata")))
		return
	}
	JSONAPIError(w, md.TrailerMD, getgRPCStatus(err))
}

func getgRPCStatus(err error) *status.Status {
	s, ok := status.FromError(err)
	if !ok {
		return status.New(codes.Unknown, err.Error())
	}
	return s
}

// JSONAPIError generates JSONAPI formatted error message
func JSONAPIError(w http.ResponseWriter, md metadata.MD, s *status.Status) {
	status := runtime.HTTPStatusFromCode(s.Code())
	jsnErr := api2go.Error{
		Status: strconv.Itoa(status),
		Title:  strings.Join(md["error"], "-"),
		Detail: s.Message(),
		Meta: map[string]interface{}{
			"creator": "api error helper",
		},
	}
	w.Header().Set("Content-Type", "application/vnd.api+json")
	w.WriteHeader(status)
	encErr := json.NewEncoder(w).Encode(api2go.HTTPError{Errors: []api2go.Error{jsnErr}})
	if encErr != nil {
		http.Error(w, encErr.Error(), http.StatusInternalServerError)
	}
}

func fallbackError(w http.ResponseWriter, s *status.Status) {
	status := runtime.HTTPStatusFromCode(s.Code())
	jsnErr := api2go.Error{
		Status: strconv.Itoa(status),
		Title:  "gRPC error",
		Detail: s.Message(),
		Meta: map[string]interface{}{
			"creator": "api error helper",
		},
	}
	w.Header().Set("Content-Type", "application/vnd.api+json")
	w.WriteHeader(status)
	encErr := json.NewEncoder(w).Encode(api2go.HTTPError{Errors: []api2go.Error{jsnErr}})
	if encErr != nil {
		http.Error(w, encErr.Error(), http.StatusInternalServerError)
	}
}

func HandleError(ctx context.Context, err error) error {
	switch err {
	case dat.ErrNotFound:
		grpc.SetTrailer(ctx, ErrNotFound)
		return status.Error(codes.NotFound, err.Error())
	case ErrRetrieveMetadata:
		grpc.SetTrailer(ctx, newError(err.Error()))
		return status.Error(codes.Internal, err.Error())
	case ErrXForwardedHost:
		grpc.SetTrailer(ctx, newError(err.Error()))
		return status.Error(codes.Internal, err.Error())
	default:
		grpc.SetTrailer(ctx, ErrDatabaseQuery)
		return status.Error(codes.Internal, err.Error())
	}
}
<|endoftext|>


<commit_before>// BaruwaAPI Golang bindings for Baruwa REST API
// Copyright (C) 2019 Andrew Colin Kissa <andrew@topdog.za.net>

// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this file,
// You can obtain one at http://mozilla.org/MPL/2.0/.

// Package api Golang bindings for Baruwa REST API
package api

const (
	// APIVersion of Baruwa API
	APIVersion = "v1"
	// Version of this libary
	Version = "0.0.1"
)
<commit_msg>FIX: Typo<commit_after>// BaruwaAPI Golang bindings for Baruwa REST API
// Copyright (C) 2019 Andrew Colin Kissa <andrew@topdog.za.net>

// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this file,
// You can obtain one at http://mozilla.org/MPL/2.0/.

// Package api Golang bindings for Baruwa REST API
package api

const (
	// APIVersion of Baruwa API
	APIVersion = "v1"
	// Version of this library
	Version = "0.0.1"
)
<|endoftext|>


<commit_before>// Copyright 2018 New Vector Ltd
// Copyright 2019-2020 The Matrix.org Foundation C.I.C.
//
// 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 sqlite3

import (
	"context"
	"database/sql"

	"github.com/matrix-org/dendrite/internal/sqlutil"
)

const txnIDSchema = `
-- Keeps a count of the current transaction ID
CREATE TABLE IF NOT EXISTS appservice_counters (
  name TEXT PRIMARY KEY NOT NULL,
  last_id INTEGER DEFAULT 1
);
INSERT OR IGNORE INTO appservice_counters (name, last_id) VALUES('txn_id', 1);
`

const selectTxnIDSQL = `
  SELECT last_id FROM appservice_counters WHERE name='txn_id';
  UPDATE appservice_counters SET last_id=last_id+1 WHERE name='txn_id';
`

type txnStatements struct {
	db              *sql.DB
	writer          sqlutil.Writer
	selectTxnIDStmt *sql.Stmt
}

func (s *txnStatements) prepare(db *sql.DB, writer sqlutil.Writer) (err error) {
	s.db = db
	s.writer = writer
	_, err = db.Exec(txnIDSchema)
	if err != nil {
		return
	}

	if s.selectTxnIDStmt, err = db.Prepare(selectTxnIDSQL); err != nil {
		return
	}

	return
}

// selectTxnID selects the latest ascending transaction ID
func (s *txnStatements) selectTxnID(
	ctx context.Context,
) (txnID int, err error) {
	err = s.writer.Do(s.db, nil, func(txn *sql.Tx) error {
		err := s.selectTxnIDStmt.QueryRowContext(ctx).Scan(&txnID)
		return err
	})
	return
}
<commit_msg>Split the select+update query for txn_id counter (#1855)<commit_after>// Copyright 2018 New Vector Ltd
// Copyright 2019-2020 The Matrix.org Foundation C.I.C.
//
// 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 sqlite3

import (
	"context"
	"database/sql"

	"github.com/matrix-org/dendrite/internal/sqlutil"
)

const txnIDSchema = `
-- Keeps a count of the current transaction ID
CREATE TABLE IF NOT EXISTS appservice_counters (
  name TEXT PRIMARY KEY NOT NULL,
  last_id INTEGER DEFAULT 1
);
INSERT OR IGNORE INTO appservice_counters (name, last_id) VALUES('txn_id', 1);
`

const selectTxnIDSQL = `
  SELECT last_id FROM appservice_counters WHERE name='txn_id'
`

const updateTxnIDSQL = `
  UPDATE appservice_counters SET last_id=last_id+1 WHERE name='txn_id'
`

type txnStatements struct {
	db              *sql.DB
	writer          sqlutil.Writer
	selectTxnIDStmt *sql.Stmt
	updateTxnIDStmt *sql.Stmt
}

func (s *txnStatements) prepare(db *sql.DB, writer sqlutil.Writer) (err error) {
	s.db = db
	s.writer = writer
	_, err = db.Exec(txnIDSchema)
	if err != nil {
		return
	}

	if s.selectTxnIDStmt, err = db.Prepare(selectTxnIDSQL); err != nil {
		return
	}

	if s.updateTxnIDStmt, err = db.Prepare(updateTxnIDSQL); err != nil {
		return
	}

	return
}

// selectTxnID selects the latest ascending transaction ID
func (s *txnStatements) selectTxnID(
	ctx context.Context,
) (txnID int, err error) {
	err = s.writer.Do(s.db, nil, func(txn *sql.Tx) error {
		err := s.selectTxnIDStmt.QueryRowContext(ctx).Scan(&txnID)
		if err != nil {
			return err
		}

		_, err = s.updateTxnIDStmt.ExecContext(ctx)
		return err
	})
	return
}
<|endoftext|>


<commit_before>// naprrqlhp.go
package main

import (
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"os/signal"
	"path/filepath"
	"syscall"

	"github.com/nsip/nias2/naprrql"
	"github.com/shomali11/parallelizer"
	"github.com/tidwall/gjson"
)

// simple test of worker pool to run all pipelines
func main() {

	// shutdown handler
	c := make(chan os.Signal, 2)
	signal.Notify(c, os.Interrupt, syscall.SIGTERM)
	go func() {
		<-c
		closeDB()
		os.Exit(1)
	}()

	startWebServer(true)
	runReports()

	// close down
	closeDB()
	os.Exit(1)

}

//
// run all report pipelines as parallel jobs
//
func runReports() {

	schools, err := getSchoolsList()
	if err != nil {
		log.Fatalln("Cannot connect to naprrql server: ", err)
	}

	schoolTemplates := getTemplates("./reporting_templates/school/")
	systemTemplates := getTemplates("./reporting_templates/system/")

	log.Println(schools)
	log.Println(len(schoolTemplates))
	log.Println(len(systemTemplates))

	group := parallelizer.NewGroup(parallelizer.WithPoolSize(1000), parallelizer.WithJobQueueSize(500))
	defer group.Close()

	// school reports
	for _, school := range schools {
		for filename, query := range schoolTemplates {
			f := filename
			q := query
			sch := school
			group.Add(func() {
				naprrql.RunSchoolReportPipeline(f, q, sch)
			})
		}
	}

	// system reports
	for filename, query := range systemTemplates {
		f := filename
		q := query
		group.Add(func() {
			naprrql.RunSystemReportPipeline(f, q, schools)
		})
	}

	// item print reports
	group.Add(func() {
		naprrql.RunItemPipeline(schools)
	})

	// writing extract reports
	group.Add(func() {
		var pipelineError error
		naprrql.RunWritingExtractPipeline(schools, pipelineError)
	})

	// xml pipeline
	// system-level
	group.Add(func() {
		naprrql.RunXMLPipeline(schools)
	})
	// per school
	for _, school := range schools {
		sc := school
		group.Add(func() { naprrql.RunXMLPipelinePerSchool(sc) })
	}

	// qa reports
	group.Add(func() { naprrql.RunQAErdsReportPipeline(schools) })
	group.Add(func() { naprrql.RunQAItemRespReportPipeline(schools) })
	group.Add(func() { naprrql.RunQAMiscReportPipeline(schools) })
	group.Add(func() { naprrql.RunQAOrphanPipeline(schools) })
	group.Add(func() {
		naprrql.RunQASchoolSummaryPipeline(schools, "./out/qa", "./reporting_templates/qa/qaSchools_map.csv")
	})

	err = group.Wait()

	fmt.Println()
	fmt.Println("Done")
	fmt.Printf("Error: %v\n\n", err)
}

//
// connect to the server & retrieve list of known acara-ids for schools
//
func getSchoolsList() ([]string, error) {

	schoolsList := make([]string, 0)
	gqlc := naprrql.NewGqlClient()

	// create gql query parameters
	query := `query schoolDetails{school_details{ACARAId}}`
	variables := make(map[string]interface{})

	json, err := gqlc.DoQuery(naprrql.DEF_GQL_URL, query, variables)
	if err != nil {
		return schoolsList, err
	}
	acaraids := json.Get("#.ACARAId")
	acaraids.ForEach(func(key, value gjson.Result) bool {
		schoolsList = append(schoolsList, value.String())
		return true // keep iterating
	})

	return schoolsList, nil

}

//
// ensure clean shutdown of data store
//
func closeDB() {
	log.Println("Closing datastore...")
	naprrql.GetDB().Close()
	log.Println("Datastore closed.")
}

//
// launch the webserver
//
func startWebServer(silent bool) {
	go naprrql.RunQLServer()
	if !silent {
		fmt.Printf("\n\nBrowse to following locations:\n\n")
		fmt.Printf("\n\tFor qa reporting user interface:\n\n\t\thttp://localhost:1329/ui\n")
		fmt.Printf("\n\tFor data query explorer:\n\n\t\thttp://localhost:1329/sifql\n")
		fmt.Printf("\n\tFor data model viewer:\n\n\t\thttp://localhost:1329/datamodel\n\n ")
	}

}

//
//
// retrieves templates for reporting queries
// in effect these are just the graphQL queries saved to files
//
func getTemplates(templatesPath string) map[string]string {

	files := make([]string, 0)
	templates := make(map[string]string)

	// get the list of query templates
	gqlFiles, err := filepath.Glob(templatesPath + "*.gql")
	if err != nil {
		log.Fatalln("Unable to read system report template files: ", err)
	}
	files = append(files, gqlFiles...)
	if len(files) == 0 {
		log.Fatalln("No template (*.gql) files found in input folder " + templatesPath)
	}

	// store template against filename
	for _, file := range files {
		query, err := ioutil.ReadFile(file)
		if err != nil {
			log.Fatalln("Unable to load template query from file: ", err)
		}
		templates[file] = string(query)
	}

	return templates

}
<commit_msg>export Pipeline function and pass in variable from HP version<commit_after>// naprrqlhp.go
package main

import (
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"os/signal"
	"path/filepath"
	"syscall"

	"github.com/nsip/nias2/naprrql"
	"github.com/shomali11/parallelizer"
	"github.com/tidwall/gjson"
)

// simple test of worker pool to run all pipelines
func main() {

	// shutdown handler
	c := make(chan os.Signal, 2)
	signal.Notify(c, os.Interrupt, syscall.SIGTERM)
	go func() {
		<-c
		closeDB()
		os.Exit(1)
	}()

	startWebServer(true)
	runReports()

	// close down
	closeDB()
	os.Exit(1)

}

//
// run all report pipelines as parallel jobs
//
func runReports() {

	schools, err := getSchoolsList()
	if err != nil {
		log.Fatalln("Cannot connect to naprrql server: ", err)
	}

	schoolTemplates := getTemplates("./reporting_templates/school/")
	systemTemplates := getTemplates("./reporting_templates/system/")

	log.Println(schools)
	log.Println(len(schoolTemplates))
	log.Println(len(systemTemplates))

	group := parallelizer.NewGroup(parallelizer.WithPoolSize(1000), parallelizer.WithJobQueueSize(500))
	defer group.Close()

	// school reports
	for _, school := range schools {
		for filename, query := range schoolTemplates {
			f := filename
			q := query
			sch := school
			group.Add(func() {
				naprrql.RunSchoolReportPipeline(f, q, sch)
			})
		}
	}

	// system reports
	for filename, query := range systemTemplates {
		f := filename
		q := query
		group.Add(func() {
			naprrql.RunSystemReportPipeline(f, q, schools)
		})
	}

	// item print reports
	group.Add(func() {
		naprrql.RunItemPipeline(schools)
	})

	// writing extract reports
	group.Add(func() {
		var pipelineError error
		naprrql.Run	WritingExtractPipeline(schools, pipelineError)
	})

	// xml pipeline
	// system-level
	group.Add(func() {
		naprrql.RunXMLPipeline(schools)
	})
	// per school
	for _, school := range schools {
		sc := school
		group.Add(func() { naprrql.RunXMLPipelinePerSchool(sc) })
	}

	// qa reports
	group.Add(func() { naprrql.RunQAErdsReportPipeline(schools) })
	group.Add(func() { naprrql.RunQAItemRespReportPipeline(schools) })
	group.Add(func() { naprrql.RunQAMiscReportPipeline(schools) })
	group.Add(func() { naprrql.RunQAOrphanPipeline(schools) })
	group.Add(func() {
		naprrql.RunQASchoolSummaryPipeline(schools, "./out/qa", "./reporting_templates/qa/qaSchools_map.csv")
	})

	err = group.Wait()

	fmt.Println()
	fmt.Println("Done")
	fmt.Printf("Error: %v\n\n", err)
}

//
// connect to the server & retrieve list of known acara-ids for schools
//
func getSchoolsList() ([]string, error) {

	schoolsList := make([]string, 0)
	gqlc := naprrql.NewGqlClient()

	// create gql query parameters
	query := `query schoolDetails{school_details{ACARAId}}`
	variables := make(map[string]interface{})

	json, err := gqlc.DoQuery(naprrql.DEF_GQL_URL, query, variables)
	if err != nil {
		return schoolsList, err
	}
	acaraids := json.Get("#.ACARAId")
	acaraids.ForEach(func(key, value gjson.Result) bool {
		schoolsList = append(schoolsList, value.String())
		return true // keep iterating
	})

	return schoolsList, nil

}

//
// ensure clean shutdown of data store
//
func closeDB() {
	log.Println("Closing datastore...")
	naprrql.GetDB().Close()
	log.Println("Datastore closed.")
}

//
// launch the webserver
//
func startWebServer(silent bool) {
	go naprrql.RunQLServer()
	if !silent {
		fmt.Printf("\n\nBrowse to following locations:\n\n")
		fmt.Printf("\n\tFor qa reporting user interface:\n\n\t\thttp://localhost:1329/ui\n")
		fmt.Printf("\n\tFor data query explorer:\n\n\t\thttp://localhost:1329/sifql\n")
		fmt.Printf("\n\tFor data model viewer:\n\n\t\thttp://localhost:1329/datamodel\n\n ")
	}

}

//
//
// retrieves templates for reporting queries
// in effect these are just the graphQL queries saved to files
//
func getTemplates(templatesPath string) map[string]string {

	files := make([]string, 0)
	templates := make(map[string]string)

	// get the list of query templates
	gqlFiles, err := filepath.Glob(templatesPath + "*.gql")
	if err != nil {
		log.Fatalln("Unable to read system report template files: ", err)
	}
	files = append(files, gqlFiles...)
	if len(files) == 0 {
		log.Fatalln("No template (*.gql) files found in input folder " + templatesPath)
	}

	// store template against filename
	for _, file := range files {
		query, err := ioutil.ReadFile(file)
		if err != nil {
			log.Fatalln("Unable to load template query from file: ", err)
		}
		templates[file] = string(query)
	}

	return templates

}
<|endoftext|>


<commit_before>package textgui

/*
 * Various functions for using curses/termbox
 *
 */

import (
	"github.com/nsf/termbox-go"
)

var (
	BG          = termbox.ColorBlue
	TITLECOLOR  = termbox.ColorCyan | termbox.AttrBold
	TEXTCOLOR   = termbox.ColorBlack
	BOXBG       = termbox.ColorWhite
	BOXLIGHT    = termbox.ColorWhite | termbox.AttrBold
	BOXDARK     = termbox.ColorBlack
	BUTTONFOCUS = termbox.ColorYellow | termbox.AttrBold
	BUTTONTEXT  = termbox.ColorWhite | termbox.AttrBold
	LISTFOCUS   = termbox.ColorRed
	LISTTEXT    = termbox.ColorBlack
)

func Write(x int, y int, text string, fg termbox.Attribute, bg termbox.Attribute) {
	pos := 0
	for _, r := range text {
		termbox.SetCell(x+pos, y, r, fg, bg)
		pos++
	}
}

func Say(x int, y int, text string) {
	pos := 0
	for _, r := range text {
		termbox.SetCell(x+pos, y, r, TEXTCOLOR, BG)
		pos++
	}
}

func Clear() {
	termbox.Clear(TEXTCOLOR, BG)
}

func ScreenWidth() int {
	return First(termbox.Size)
}

func ScreenHeight() int {
	return Second(termbox.Size)
}

func Flush() {
	termbox.Flush()
}

func Init() error {
	return termbox.Init()
}

func Close() {
	termbox.Close()
}

func PollEvent() *termbox.Event {
	e := termbox.PollEvent()
	return &e
}

// Wait for Esc or Enter to be pressed
func WaitForKey() {
	for {
		e := PollEvent()
		switch e.Type {
		case termbox.EventKey:
			switch e.Key {
			case termbox.KeyEsc:
				return
			case termbox.KeyEnter:
				return
			}
		}
	}
}

func SetFg(fg termbox.Attribute) {
	TEXTCOLOR = fg
}

func SetBg(bg termbox.Attribute) {
	BG = bg
}
<commit_msg>Added Space to the list of keys to wait for when waiting for a key<commit_after>package textgui

/*
 * Various functions for using curses/termbox
 *
 */

import (
	"github.com/nsf/termbox-go"
)

var (
	BG          = termbox.ColorBlue
	TITLECOLOR  = termbox.ColorCyan | termbox.AttrBold
	TEXTCOLOR   = termbox.ColorBlack
	BOXBG       = termbox.ColorWhite
	BOXLIGHT    = termbox.ColorWhite | termbox.AttrBold
	BOXDARK     = termbox.ColorBlack
	BUTTONFOCUS = termbox.ColorYellow | termbox.AttrBold
	BUTTONTEXT  = termbox.ColorWhite | termbox.AttrBold
	LISTFOCUS   = termbox.ColorRed
	LISTTEXT    = termbox.ColorBlack
)

func Write(x int, y int, text string, fg termbox.Attribute, bg termbox.Attribute) {
	pos := 0
	for _, r := range text {
		termbox.SetCell(x+pos, y, r, fg, bg)
		pos++
	}
}

func Say(x int, y int, text string) {
	pos := 0
	for _, r := range text {
		termbox.SetCell(x+pos, y, r, TEXTCOLOR, BG)
		pos++
	}
}

func Clear() {
	termbox.Clear(TEXTCOLOR, BG)
}

func ScreenWidth() int {
	return First(termbox.Size)
}

func ScreenHeight() int {
	return Second(termbox.Size)
}

func Flush() {
	termbox.Flush()
}

func Init() error {
	return termbox.Init()
}

func Close() {
	termbox.Close()
}

func PollEvent() *termbox.Event {
	e := termbox.PollEvent()
	return &e
}

// Wait for Esc, Enter or Space to be pressed
func WaitForKey() {
	for {
		e := PollEvent()
		switch e.Type {
		case termbox.EventKey:
			switch e.Key {
			case termbox.KeyEsc, termbox.KeyEnter, termbox.KeySpace:
				return
			}
		}
	}
}

func SetFg(fg termbox.Attribute) {
	TEXTCOLOR = fg
}

func SetBg(bg termbox.Attribute) {
	BG = bg
}
<|endoftext|>


<commit_before>package lint

import (
	"errors"
	"fmt"
	"strings"
)

// Runtime stores a set of rules.
type Runtime struct {
	rules  Rules
	config RuleConfig
}

// NewRuntime creates a new Runtime.
func NewRuntime(config RuleConfig) *Runtime {
	return &Runtime{
		rules:  make(Rules),
		config: config,
	}
}

// AddRules adds rules, of which the name will be added a prefix to reduce collisions
func (r *Runtime) AddRules(prefix string, rules ...Rule) error {
	for _, rl := range rules {
		if _, found := r.rules[rl.Info().Name.WithPrefix(prefix)]; found {
			return fmt.Errorf("duplicate repository entry with name %q", rl.Info().Name.WithPrefix(prefix))
		}
		r.rules[rl.Info().Name.WithPrefix(prefix)] = rl
	}
	return nil
}

// Run executes rules on the request when a config is found for the file path
// of the request.
//
// If the found config contains rule configs for some rules, the status and
// category of the affected rules will be updated accordingly. In other words,
// rule configs can be used to turn on/off certain rules and change the category
// of the returned problems.
func (r *Runtime) Run(req Request, configs Configs) (Response, error) {
	cfg, err := configs.Search(req.ProtoFile().Path())
	if err != nil {
		return Response{}, err
	}
	return r.run(req, cfg.RuleConfigs)
}

func (r *Runtime) run(req Request, ruleCfgMap map[string]RuleConfig) (Response, error) {
	finalResp := Response{}
	var errMessages []string
	for name, rl := range r.rules {
		config := r.config
		for prefix, c := range ruleCfgMap {
			if name.HasPrefix(prefix) {
				config = config.WithOverride(c)
				break
			}
		}

		if config.Status == Enabled {
			if resp, err := rl.Lint(req); err == nil {
				for _, p := range resp.Problems {
					p.category = rl.Info().Category

					if config.Category != "" {
						p.category = config.Category
					}

					finalResp.Problems = append(finalResp.Problems, p)
				}
			} else {
				errMessages = append(errMessages, err.Error())
			}
		}
	}

	var err error
	if len(errMessages) != 0 {
		err = errors.New(strings.Join(errMessages, "; "))
	}

	return finalResp, err
}
<commit_msg>Consolidated two functions<commit_after>package lint

import (
	"errors"
	"fmt"
	"strings"
)

// Runtime stores a set of rules.
type Runtime struct {
	rules  Rules
	config RuleConfig
}

// NewRuntime creates a new Runtime.
func NewRuntime(config RuleConfig) *Runtime {
	return &Runtime{
		rules:  make(Rules),
		config: config,
	}
}

// AddRules adds rules, of which the name will be added a prefix to reduce collisions
func (r *Runtime) AddRules(prefix string, rules ...Rule) error {
	for _, rl := range rules {
		if _, found := r.rules[rl.Info().Name.WithPrefix(prefix)]; found {
			return fmt.Errorf("duplicate repository entry with name %q", rl.Info().Name.WithPrefix(prefix))
		}
		r.rules[rl.Info().Name.WithPrefix(prefix)] = rl
	}
	return nil
}

// Run executes rules on the request when a config is found for the file path
// of the request.
//
// If the found config contains rule configs for some rules, the status and
// category of the affected rules will be updated accordingly. In other words,
// rule configs can be used to turn on/off certain rules and change the category
// of the returned problems.
func (r *Runtime) Run(req Request, configs Configs) (Response, error) {
	cfg, err := configs.Search(req.ProtoFile().Path())

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

	finalResp := Response{}
	var errMessages []string
	for name, rl := range r.rules {
		config := r.config
		for prefix, c := range cfg.RuleConfigs {
			if name.HasPrefix(prefix) {
				config = config.WithOverride(c)
				break
			}
		}

		if config.Status == Enabled {
			if resp, err := rl.Lint(req); err == nil {
				for _, p := range resp.Problems {
					p.category = rl.Info().Category

					if config.Category != "" {
						p.category = config.Category
					}

					finalResp.Problems = append(finalResp.Problems, p)
				}
			} else {
				errMessages = append(errMessages, err.Error())
			}
		}
	}

	if len(errMessages) != 0 {
		err = errors.New(strings.Join(errMessages, "; "))
	}

	return finalResp, err
}
<|endoftext|>


<commit_before>package main

import (
	"io/ioutil"
	"os"
	"os/exec"
	"path/filepath"
	"reflect"
	"testing"

	"github.com/motemen/ghq/cmdutil"
)

var remoteDummyURL = mustParseURL("https://example.com/git/repo")

func TestVCSBackend(t *testing.T) {
	tempDir, err := ioutil.TempDir("", "ghq-test")
	if err != nil {
		t.Fatal(err)
	}
	defer os.RemoveAll(tempDir)
	localDir := filepath.Join(tempDir, "repo")
	_commands := []*exec.Cmd{}
	lastCommand := func() *exec.Cmd { return _commands[len(_commands)-1] }
	defer func(orig func(cmd *exec.Cmd) error) {
		cmdutil.CommandRunner = orig
	}(cmdutil.CommandRunner)
	cmdutil.CommandRunner = func(cmd *exec.Cmd) error {
		_commands = append(_commands, cmd)
		return nil
	}

	testCases := []struct {
		name   string
		f      func() error
		expect []string
		dir    string
	}{{
		name: "[git] clone",
		f: func() error {
			return GitBackend.Clone(remoteDummyURL, localDir, false, false)
		},
		expect: []string{"git", "clone", remoteDummyURL.String(), localDir},
	}, {
		name: "[git] shallow clone",
		f: func() error {
			return GitBackend.Clone(remoteDummyURL, localDir, true, true)
		},
		expect: []string{"git", "clone", "--depth", "1", remoteDummyURL.String(), localDir},
	}, {
		name: "[git] update",
		f: func() error {
			return GitBackend.Update(localDir, false)
		},
		expect: []string{"git", "pull", "--ff-only"},
		dir:    localDir,
	}, {
		name: "[svn] checkout",
		f: func() error {
			return SubversionBackend.Clone(remoteDummyURL, localDir, false, false)
		},
		expect: []string{"svn", "checkout", remoteDummyURL.String(), localDir},
	}, {
		name: "[svn] checkout shallow",
		f: func() error {
			return SubversionBackend.Clone(remoteDummyURL, localDir, true, false)
		},
		expect: []string{"svn", "checkout", "--depth", "1", remoteDummyURL.String(), localDir},
	}, {
		name: "[svn] update",
		f: func() error {
			return SubversionBackend.Update(localDir, true)
		},
		expect: []string{"svn", "update"},
		dir:    localDir,
	}, {
		name: "[git-svn] clone",
		f: func() error {
			return GitsvnBackend.Clone(remoteDummyURL, localDir, false, false)
		},
		expect: []string{"git", "svn", "clone", remoteDummyURL.String(), localDir},
	}, {
		name: "[git-svn] update",
		f: func() error {
			return GitsvnBackend.Update(localDir, false)
		},
		expect: []string{"git", "svn", "rebase"},
		dir:    localDir,
	}, {
		name: "[git-svn] clone shallow",
		f: func() error {
			return GitsvnBackend.Clone(remoteDummyURL, localDir, true, false)
		},
		expect: []string{"git", "svn", "clone", remoteDummyURL.String(), localDir},
	}, {
		name: "[hg] clone",
		f: func() error {
			return MercurialBackend.Clone(remoteDummyURL, localDir, false, false)
		},
		expect: []string{"hg", "clone", remoteDummyURL.String(), localDir},
	}, {
		name: "[hg] update",
		f: func() error {
			return MercurialBackend.Update(localDir, false)
		},
		expect: []string{"hg", "pull", "--update"},
		dir:    localDir,
	}, {
		name: "[hg] clone shallow",
		f: func() error {
			return MercurialBackend.Clone(remoteDummyURL, localDir, true, false)
		},
		expect: []string{"hg", "clone", remoteDummyURL.String(), localDir},
	}, {
		name: "[darcs] clone",
		f: func() error {
			return DarcsBackend.Clone(remoteDummyURL, localDir, false, false)
		},
		expect: []string{"darcs", "get", remoteDummyURL.String(), localDir},
	}, {
		name: "[darcs] clone shallow",
		f: func() error {
			return DarcsBackend.Clone(remoteDummyURL, localDir, true, false)
		},
		expect: []string{"darcs", "get", "--lazy", remoteDummyURL.String(), localDir},
	}, {
		name: "[darcs] update",
		f: func() error {
			return DarcsBackend.Update(localDir, false)
		},
		expect: []string{"darcs", "pull"},
		dir:    localDir,
	}, {
		name: "[bzr] clone",
		f: func() error {
			return BazaarBackend.Clone(remoteDummyURL, localDir, false, false)
		},
		expect: []string{"bzr", "branch", remoteDummyURL.String(), localDir},
	}, {
		name: "[bzr] update",
		f: func() error {
			return BazaarBackend.Update(localDir, false)
		},
		expect: []string{"bzr", "pull"},
		dir:    localDir,
	}, {
		name: "[bzr] clone shallow",
		f: func() error {
			return BazaarBackend.Clone(remoteDummyURL, localDir, true, false)
		},
		expect: []string{"bzr", "branch", remoteDummyURL.String(), localDir},
	}, {
		name: "[fossil] clone",
		f: func() error {
			return FossilBackend.Clone(remoteDummyURL, localDir, false, false)
		},
		expect: []string{"fossil", "open", fossilRepoName},
		dir:    localDir,
	}, {
		name: "[fossil] update",
		f: func() error {
			return FossilBackend.Update(localDir, false)
		},
		expect: []string{"fossil", "update"},
		dir:    localDir,
	}}

	for _, tc := range testCases {
		t.Run(tc.name, func(t *testing.T) {
			if err := tc.f(); err != nil {
				t.Errorf("error should be nil, but: %s", err)
			}
			c := lastCommand()
			if !reflect.DeepEqual(c.Args, tc.expect) {
				t.Errorf("\ngot:  %+v\nexpect: %+v", c.Args, tc.expect)
			}
			if c.Dir != tc.dir {
				t.Errorf("got: %s, expect: %s", c.Dir, tc.dir)
			}
		})
	}
}

func TestCvsDummyBackend(t *testing.T) {
	tempDir, err := ioutil.TempDir("", "ghq-test")
	if err != nil {
		t.Fatal(err)
	}
	defer os.RemoveAll(tempDir)
	localDir := filepath.Join(tempDir, "repo")

	if err := cvsDummyBackend.Clone(remoteDummyURL, localDir, false, false); err == nil {
		t.Error("error should be occurred, but nil")
	}

	if err := cvsDummyBackend.Clone(remoteDummyURL, localDir, true, false); err == nil {
		t.Error("error should be occurred, but nil")
	}

	if err := cvsDummyBackend.Update(localDir, false); err == nil {
		t.Error("error should be occurred, but nil")
	}
}
<commit_msg>fix test<commit_after>package main

import (
	"io/ioutil"
	"os"
	"os/exec"
	"path/filepath"
	"reflect"
	"testing"

	"github.com/motemen/ghq/cmdutil"
)

var remoteDummyURL = mustParseURL("https://example.com/git/repo")

func TestVCSBackend(t *testing.T) {
	tempDir, err := ioutil.TempDir("", "ghq-test")
	if err != nil {
		t.Fatal(err)
	}
	defer os.RemoveAll(tempDir)
	localDir := filepath.Join(tempDir, "repo")
	_commands := []*exec.Cmd{}
	lastCommand := func() *exec.Cmd { return _commands[len(_commands)-1] }
	defer func(orig func(cmd *exec.Cmd) error) {
		cmdutil.CommandRunner = orig
	}(cmdutil.CommandRunner)
	cmdutil.CommandRunner = func(cmd *exec.Cmd) error {
		_commands = append(_commands, cmd)
		return nil
	}

	testCases := []struct {
		name   string
		f      func() error
		expect []string
		dir    string
	}{{
		name: "[git] clone",
		f: func() error {
			return GitBackend.Clone(remoteDummyURL, localDir, false, false)
		},
		expect: []string{"git", "clone", remoteDummyURL.String(), localDir},
	}, {
		name: "[git] shallow clone",
		f: func() error {
			return GitBackend.Clone(remoteDummyURL, localDir, true, true)
		},
		expect: []string{"git", "clone", "--depth", "1", remoteDummyURL.String(), localDir},
	}, {
		name: "[git] update",
		f: func() error {
			return GitBackend.Update(localDir, false)
		},
		expect: []string{"git", "pull", "--ff-only"},
		dir:    localDir,
	}, {
		name: "[svn] checkout",
		f: func() error {
			return SubversionBackend.Clone(remoteDummyURL, localDir, false, false)
		},
		expect: []string{"svn", "checkout", remoteDummyURL.String(), localDir},
	}, {
		name: "[svn] checkout shallow",
		f: func() error {
			return SubversionBackend.Clone(remoteDummyURL, localDir, true, false)
		},
		expect: []string{"svn", "checkout", "--depth", "1", remoteDummyURL.String(), localDir},
	}, {
		name: "[svn] update",
		f: func() error {
			return SubversionBackend.Update(localDir, true)
		},
		expect: []string{"svn", "update"},
		dir:    localDir,
	}, {
		name: "[git-svn] clone",
		f: func() error {
			return GitsvnBackend.Clone(remoteDummyURL, localDir, false, false)
		},
		expect: []string{"git", "svn", "clone", remoteDummyURL.String(), localDir},
	}, {
		name: "[git-svn] update",
		f: func() error {
			return GitsvnBackend.Update(localDir, false)
		},
		expect: []string{"git", "svn", "rebase"},
		dir:    localDir,
	}, {
		name: "[git-svn] clone shallow",
		f: func() error {
			return GitsvnBackend.Clone(remoteDummyURL, localDir, true, false)
		},
		expect: []string{"git", "svn", "clone", remoteDummyURL.String(), localDir},
	}, {
		name: "[hg] clone",
		f: func() error {
			return MercurialBackend.Clone(remoteDummyURL, localDir, false, false)
		},
		expect: []string{"hg", "clone", remoteDummyURL.String(), localDir},
	}, {
		name: "[hg] update",
		f: func() error {
			return MercurialBackend.Update(localDir, false)
		},
		expect: []string{"hg", "pull", "--update"},
		dir:    localDir,
	}, {
		name: "[hg] clone shallow",
		f: func() error {
			return MercurialBackend.Clone(remoteDummyURL, localDir, true, false)
		},
		expect: []string{"hg", "clone", remoteDummyURL.String(), localDir},
	}, {
		name: "[darcs] clone",
		f: func() error {
			return DarcsBackend.Clone(remoteDummyURL, localDir, false, false)
		},
		expect: []string{"darcs", "get", remoteDummyURL.String(), localDir},
	}, {
		name: "[darcs] clone shallow",
		f: func() error {
			return DarcsBackend.Clone(remoteDummyURL, localDir, true, false)
		},
		expect: []string{"darcs", "get", "--lazy", remoteDummyURL.String(), localDir},
	}, {
		name: "[darcs] update",
		f: func() error {
			return DarcsBackend.Update(localDir, false)
		},
		expect: []string{"darcs", "pull"},
		dir:    localDir,
	}, {
		name: "[bzr] clone",
		f: func() error {
			return BazaarBackend.Clone(remoteDummyURL, localDir, false, false)
		},
		expect: []string{"bzr", "branch", remoteDummyURL.String(), localDir},
	}, {
		name: "[bzr] update",
		f: func() error {
			return BazaarBackend.Update(localDir, false)
		},
		expect: []string{"bzr", "pull", "--overwrite"},
		dir:    localDir,
	}, {
		name: "[bzr] clone shallow",
		f: func() error {
			return BazaarBackend.Clone(remoteDummyURL, localDir, true, false)
		},
		expect: []string{"bzr", "branch", remoteDummyURL.String(), localDir},
	}, {
		name: "[fossil] clone",
		f: func() error {
			return FossilBackend.Clone(remoteDummyURL, localDir, false, false)
		},
		expect: []string{"fossil", "open", fossilRepoName},
		dir:    localDir,
	}, {
		name: "[fossil] update",
		f: func() error {
			return FossilBackend.Update(localDir, false)
		},
		expect: []string{"fossil", "update"},
		dir:    localDir,
	}}

	for _, tc := range testCases {
		t.Run(tc.name, func(t *testing.T) {
			if err := tc.f(); err != nil {
				t.Errorf("error should be nil, but: %s", err)
			}
			c := lastCommand()
			if !reflect.DeepEqual(c.Args, tc.expect) {
				t.Errorf("\ngot:  %+v\nexpect: %+v", c.Args, tc.expect)
			}
			if c.Dir != tc.dir {
				t.Errorf("got: %s, expect: %s", c.Dir, tc.dir)
			}
		})
	}
}

func TestCvsDummyBackend(t *testing.T) {
	tempDir, err := ioutil.TempDir("", "ghq-test")
	if err != nil {
		t.Fatal(err)
	}
	defer os.RemoveAll(tempDir)
	localDir := filepath.Join(tempDir, "repo")

	if err := cvsDummyBackend.Clone(remoteDummyURL, localDir, false, false); err == nil {
		t.Error("error should be occurred, but nil")
	}

	if err := cvsDummyBackend.Clone(remoteDummyURL, localDir, true, false); err == nil {
		t.Error("error should be occurred, but nil")
	}

	if err := cvsDummyBackend.Update(localDir, false); err == nil {
		t.Error("error should be occurred, but nil")
	}
}
<|endoftext|>


<commit_before>package mdocker

import (
	"errors"
	"net/http"

	"github.com/fsouza/go-dockerclient"
	"github.com/mistifyio/mistify-agent/rpc"
)

var cStateRunning = "running"
var cStatePaused = "paused"
var cStateStopped = "stopped"

func (md *MDocker) containersFromAPIContainers(acs []docker.APIContainers) ([]*docker.Container, error) {
	containers := make([]*docker.Container, 0, len(acs))
	for _, ac := range acs {
		container, err := md.client.InspectContainer(ac.ID)
		if err != nil {
			return nil, err
		}
		containers = append(containers, container)
	}
	return containers, nil
}

func (md *MDocker) fetchContainerState(containerID string) (string, error) {
	container, err := md.client.InspectContainer(containerID)
	if err != nil {
		return "", err
	}
	if container.State.Paused {
		return cStatePaused, nil
	}
	if container.State.Running {
		return cStateRunning, nil
	}
	return cStateStopped, nil
}

func assertContainerState(expected, actual string) error {
	if actual != expected {
		return errors.New("unexpected container state")
	}
	return nil
}

func requestContainerName(request *rpc.GuestRequest) (string, error) {
	if request.Guest == nil || request.Guest.Id == "" {
		return "", errors.New("missing guest with id")
	}
	return request.Guest.Id, nil
}

// ListContainers retrieves a list of Docker containers
func (md *MDocker) ListContainers(h *http.Request, request *rpc.ContainerRequest, response *rpc.ContainerResponse) error {
	var opts docker.ListContainersOptions
	if err := md.RequestOpts(request, &opts); err != nil {
		return err
	}

	apiContainers, err := md.client.ListContainers(opts)
	if err != nil {
		return err
	}
	containers, err := md.containersFromAPIContainers(apiContainers)
	if err != nil {
		return err
	}

	response.Containers = containers
	return nil
}

// GetContainer retrieves information about a specific Docker container
func (md *MDocker) GetContainer(h *http.Request, request *rpc.ContainerRequest, response *rpc.ContainerResponse) error {
	container, err := md.client.InspectContainer(request.ID)
	if err != nil {
		return err
	}

	response.Containers = []*docker.Container{
		container,
	}
	return nil
}

// DeleteContainer deletes a Docker container
func (md *MDocker) DeleteContainer(h *http.Request, request *rpc.GuestRequest, response *rpc.GuestResponse) error {
	containerName, err := requestContainerName(request)
	if err != nil {
		return err
	}

	opts := docker.RemoveContainerOptions{
		ID: containerName,
	}
	if err := md.client.RemoveContainer(opts); err != nil {
		return err
	}

	response.Guest = request.Guest
	response.Guest.State = "deleted"
	return nil
}

// SaveContainer saves a Docker container
func (md *MDocker) SaveContainer(h *http.Request, request *rpc.ContainerRequest, response *rpc.ContainerImageResponse) error {
	var opts docker.CommitContainerOptions
	if err := md.RequestOpts(request, &opts); err != nil {
		return err
	}
	if request.ID != "" {
		opts.Container = request.ID
	}
	image, err := md.client.CommitContainer(opts)
	if err != nil {
		return err
	}
	response.Images = []*docker.Image{
		image,
	}
	return nil
}

// CreateContainer creates a new Docker container
func (md *MDocker) CreateContainer(h *http.Request, request *rpc.GuestRequest, response *rpc.GuestResponse) error {
	containerName, err := requestContainerName(request)
	if err != nil {
		return err
	}
	guest := request.Guest
	opts := docker.CreateContainerOptions{
		Name: containerName,
		Config: &docker.Config{
			Hostname: guest.Id,
			Image:    guest.Image,
			Memory:   int64(guest.Memory) * 1024 * 1024, // Convert MB to bytes
		},
		HostConfig: &docker.HostConfig{
			PublishAllPorts: true,
		},
	}
	container, err := md.client.CreateContainer(opts)
	if err != nil {
		return err
	}

	state, err := md.fetchContainerState(container.ID)
	if err != nil {
		return err
	}

	guest.State = state
	response.Guest = guest
	return nil
}

// StartContainer starts a Docker container
func (md *MDocker) StartContainer(h *http.Request, request *rpc.GuestRequest, response *rpc.GuestResponse) error {
	containerName, err := requestContainerName(request)
	if err != nil {
		return err
	}
	hostConfig := &docker.HostConfig{
		PublishAllPorts: true,
	}
	if err := md.client.StartContainer(containerName, hostConfig); err != nil {
		return err
	}
	state, err := md.fetchContainerState(containerName)
	if err != nil {
		return err
	}
	if err := assertContainerState(cStateRunning, state); err != nil {
		return err
	}

	response.Guest = request.Guest
	response.Guest.State = state
	return nil
}

// StopContainer stops a Docker container or kills it after a timeout
func (md *MDocker) StopContainer(h *http.Request, request *rpc.GuestRequest, response *rpc.GuestRequest) error {
	containerName, err := requestContainerName(request)
	if err != nil {
		return err
	}
	if err := md.client.StopContainer(containerName, 10); err != nil {
		return err
	}
	state, err := md.fetchContainerState(containerName)
	if err != nil {
		return err
	}
	if err := assertContainerState(cStateStopped, state); err != nil {
		return err
	}

	response.Guest = request.Guest
	response.Guest.State = state
	return nil
}

// RestartContainer restarts a Docker container
func (md *MDocker) RestartContainer(h *http.Request, request *rpc.GuestRequest, response *rpc.GuestRequest) error {
	containerName, err := requestContainerName(request)
	if err != nil {
		return err
	}
	if err := md.client.RestartContainer(containerName, 60); err != nil {
		return err
	}
	state, err := md.fetchContainerState(containerName)
	if err != nil {
		return err
	}
	if err := assertContainerState(cStateRunning, state); err != nil {
		return err
	}

	response.Guest = request.Guest
	response.Guest.State = state
	return nil
}

// RebootContainer restarts a Docker container
func (md *MDocker) RebootContainer(h *http.Request, request *rpc.GuestRequest, response *rpc.GuestRequest) error {
	return md.RestartContainer(h, request, response)
}

// PauseContainer pauses a Docker container
func (md *MDocker) PauseContainer(h *http.Request, request *rpc.GuestRequest, response *rpc.GuestRequest) error {
	containerName, err := requestContainerName(request)
	if err != nil {
		return err
	}
	if err := md.client.PauseContainer(containerName); err != nil {
		return err
	}
	state, err := md.fetchContainerState(containerName)
	if err != nil {
		return err
	}
	if err := assertContainerState(cStatePaused, state); err != nil {
		return err
	}

	response.Guest = request.Guest
	response.Guest.State = state
	return nil
}

// UnpauseContainer restarts a Docker container
func (md *MDocker) UnpauseContainer(h *http.Request, request *rpc.GuestRequest, response *rpc.GuestRequest) error {
	containerName, err := requestContainerName(request)
	if err != nil {
		return err
	}
	if err := md.client.UnpauseContainer(containerName); err != nil {
		return err
	}
	state, err := md.fetchContainerState(containerName)
	if err != nil {
		return err
	}
	if err := assertContainerState(cStateRunning, state); err != nil {
		return err
	}

	response.Guest = request.Guest
	response.Guest.State = state
	return nil
}
<commit_msg>MIST-376 Starting running or stopping stopped container shouldn't fail<commit_after>package mdocker

import (
	"errors"
	"net/http"

	"github.com/fsouza/go-dockerclient"
	"github.com/mistifyio/mistify-agent/rpc"
)

var cStateRunning = "running"
var cStatePaused = "paused"
var cStateStopped = "stopped"

func (md *MDocker) containersFromAPIContainers(acs []docker.APIContainers) ([]*docker.Container, error) {
	containers := make([]*docker.Container, 0, len(acs))
	for _, ac := range acs {
		container, err := md.client.InspectContainer(ac.ID)
		if err != nil {
			return nil, err
		}
		containers = append(containers, container)
	}
	return containers, nil
}

func (md *MDocker) fetchContainerState(containerID string) (string, error) {
	container, err := md.client.InspectContainer(containerID)
	if err != nil {
		return "", err
	}
	if container.State.Paused {
		return cStatePaused, nil
	}
	if container.State.Running {
		return cStateRunning, nil
	}
	return cStateStopped, nil
}

func assertContainerState(expected, actual string) error {
	if actual != expected {
		return errors.New("unexpected container state")
	}
	return nil
}

func requestContainerName(request *rpc.GuestRequest) (string, error) {
	if request.Guest == nil || request.Guest.Id == "" {
		return "", errors.New("missing guest with id")
	}
	return request.Guest.Id, nil
}

// ListContainers retrieves a list of Docker containers
func (md *MDocker) ListContainers(h *http.Request, request *rpc.ContainerRequest, response *rpc.ContainerResponse) error {
	var opts docker.ListContainersOptions
	if err := md.RequestOpts(request, &opts); err != nil {
		return err
	}

	apiContainers, err := md.client.ListContainers(opts)
	if err != nil {
		return err
	}
	containers, err := md.containersFromAPIContainers(apiContainers)
	if err != nil {
		return err
	}

	response.Containers = containers
	return nil
}

// GetContainer retrieves information about a specific Docker container
func (md *MDocker) GetContainer(h *http.Request, request *rpc.ContainerRequest, response *rpc.ContainerResponse) error {
	container, err := md.client.InspectContainer(request.ID)
	if err != nil {
		return err
	}

	response.Containers = []*docker.Container{
		container,
	}
	return nil
}

// DeleteContainer deletes a Docker container
func (md *MDocker) DeleteContainer(h *http.Request, request *rpc.GuestRequest, response *rpc.GuestResponse) error {
	containerName, err := requestContainerName(request)
	if err != nil {
		return err
	}

	opts := docker.RemoveContainerOptions{
		ID: containerName,
	}
	if err := md.client.RemoveContainer(opts); err != nil {
		return err
	}

	response.Guest = request.Guest
	response.Guest.State = "deleted"
	return nil
}

// SaveContainer saves a Docker container
func (md *MDocker) SaveContainer(h *http.Request, request *rpc.ContainerRequest, response *rpc.ContainerImageResponse) error {
	var opts docker.CommitContainerOptions
	if err := md.RequestOpts(request, &opts); err != nil {
		return err
	}
	if request.ID != "" {
		opts.Container = request.ID
	}
	image, err := md.client.CommitContainer(opts)
	if err != nil {
		return err
	}
	response.Images = []*docker.Image{
		image,
	}
	return nil
}

// CreateContainer creates a new Docker container
func (md *MDocker) CreateContainer(h *http.Request, request *rpc.GuestRequest, response *rpc.GuestResponse) error {
	containerName, err := requestContainerName(request)
	if err != nil {
		return err
	}
	guest := request.Guest
	opts := docker.CreateContainerOptions{
		Name: containerName,
		Config: &docker.Config{
			Hostname: guest.Id,
			Image:    guest.Image,
			Memory:   int64(guest.Memory) * 1024 * 1024, // Convert MB to bytes
		},
		HostConfig: &docker.HostConfig{
			PublishAllPorts: true,
		},
	}
	container, err := md.client.CreateContainer(opts)
	if err != nil {
		return err
	}

	state, err := md.fetchContainerState(container.ID)
	if err != nil {
		return err
	}

	guest.State = state
	response.Guest = guest
	return nil
}

// StartContainer starts a Docker container
func (md *MDocker) StartContainer(h *http.Request, request *rpc.GuestRequest, response *rpc.GuestResponse) error {
	containerName, err := requestContainerName(request)
	if err != nil {
		return err
	}
	hostConfig := &docker.HostConfig{
		PublishAllPorts: true,
	}
	err = md.client.StartContainer(containerName, hostConfig)
	if err, ok := err.(*docker.ContainerAlreadyRunning); err != nil && !ok {
		return err
	}
	state, err := md.fetchContainerState(containerName)
	if err != nil {
		return err
	}
	if err := assertContainerState(cStateRunning, state); err != nil {
		return err
	}

	response.Guest = request.Guest
	response.Guest.State = state
	return nil
}

// StopContainer stops a Docker container or kills it after a timeout
func (md *MDocker) StopContainer(h *http.Request, request *rpc.GuestRequest, response *rpc.GuestRequest) error {
	containerName, err := requestContainerName(request)
	if err != nil {
		return err
	}
	err = md.client.StopContainer(containerName, 10)
	if err, ok := err.(*docker.ContainerNotRunning); err != nil && !ok {
		return err
	}
	state, err := md.fetchContainerState(containerName)
	if err != nil {
		return err
	}
	if err := assertContainerState(cStateStopped, state); err != nil {
		return err
	}

	response.Guest = request.Guest
	response.Guest.State = state
	return nil
}

// RestartContainer restarts a Docker container
func (md *MDocker) RestartContainer(h *http.Request, request *rpc.GuestRequest, response *rpc.GuestRequest) error {
	containerName, err := requestContainerName(request)
	if err != nil {
		return err
	}
	if err := md.client.RestartContainer(containerName, 60); err != nil {
		return err
	}
	state, err := md.fetchContainerState(containerName)
	if err != nil {
		return err
	}
	if err := assertContainerState(cStateRunning, state); err != nil {
		return err
	}

	response.Guest = request.Guest
	response.Guest.State = state
	return nil
}

// RebootContainer restarts a Docker container
func (md *MDocker) RebootContainer(h *http.Request, request *rpc.GuestRequest, response *rpc.GuestRequest) error {
	return md.RestartContainer(h, request, response)
}

// PauseContainer pauses a Docker container
func (md *MDocker) PauseContainer(h *http.Request, request *rpc.GuestRequest, response *rpc.GuestRequest) error {
	containerName, err := requestContainerName(request)
	if err != nil {
		return err
	}
	if err := md.client.PauseContainer(containerName); err != nil {
		return err
	}
	state, err := md.fetchContainerState(containerName)
	if err != nil {
		return err
	}
	if err := assertContainerState(cStatePaused, state); err != nil {
		return err
	}

	response.Guest = request.Guest
	response.Guest.State = state
	return nil
}

// UnpauseContainer restarts a Docker container
func (md *MDocker) UnpauseContainer(h *http.Request, request *rpc.GuestRequest, response *rpc.GuestRequest) error {
	containerName, err := requestContainerName(request)
	if err != nil {
		return err
	}
	if err := md.client.UnpauseContainer(containerName); err != nil {
		return err
	}
	state, err := md.fetchContainerState(containerName)
	if err != nil {
		return err
	}
	if err := assertContainerState(cStateRunning, state); err != nil {
		return err
	}

	response.Guest = request.Guest
	response.Guest.State = state
	return nil
}
<|endoftext|>


<commit_before>package mux

import (
	"runtime"

	"v2ray.com/core/common/buf"
	"v2ray.com/core/common/net"
	"v2ray.com/core/common/protocol"
	"v2ray.com/core/common/serial"
)

type Writer struct {
	dest         net.Destination
	writer       buf.Writer
	id           uint16
	followup     bool
	transferType protocol.TransferType
}

func NewWriter(id uint16, dest net.Destination, writer buf.Writer, transferType protocol.TransferType) *Writer {
	return &Writer{
		id:           id,
		dest:         dest,
		writer:       writer,
		followup:     false,
		transferType: transferType,
	}
}

func NewResponseWriter(id uint16, writer buf.Writer, transferType protocol.TransferType) *Writer {
	return &Writer{
		id:           id,
		writer:       writer,
		followup:     true,
		transferType: transferType,
	}
}

func (w *Writer) getNextFrameMeta() FrameMetadata {
	meta := FrameMetadata{
		SessionID: w.id,
		Target:    w.dest,
	}
	if w.followup {
		meta.SessionStatus = SessionStatusKeep
	} else {
		w.followup = true
		meta.SessionStatus = SessionStatusNew
	}

	return meta
}

func (w *Writer) writeMetaOnly() error {
	meta := w.getNextFrameMeta()
	b := buf.New()
	if err := b.AppendSupplier(meta.AsSupplier()); err != nil {
		return err
	}
	runtime.KeepAlive(meta)
	return w.writer.Write(buf.NewMultiBufferValue(b))
}

func (w *Writer) writeData(mb buf.MultiBuffer) error {
	meta := w.getNextFrameMeta()
	meta.Option.Set(OptionData)

	frame := buf.New()
	if err := frame.AppendSupplier(meta.AsSupplier()); err != nil {
		return err
	}
	runtime.KeepAlive(meta)
	if err := frame.AppendSupplier(serial.WriteUint16(uint16(mb.Len()))); err != nil {
		return err
	}

	mb2 := buf.NewMultiBuffer()
	mb2.Append(frame)
	mb2.AppendMulti(mb)
	return w.writer.Write(mb2)
}

// Write implements buf.MultiBufferWriter.
func (w *Writer) Write(mb buf.MultiBuffer) error {
	if mb.IsEmpty() {
		return w.writeMetaOnly()
	}

	if w.transferType == protocol.TransferTypeStream {
		const chunkSize = 8 * 1024
		for !mb.IsEmpty() {
			slice := mb.SliceBySize(chunkSize)
			if err := w.writeData(slice); err != nil {
				return err
			}
		}
	} else {
		for _, b := range mb {
			if err := w.writeData(buf.NewMultiBufferValue(b)); err != nil {
				return err
			}
		}
	}

	return nil
}

func (w *Writer) Close() {
	meta := FrameMetadata{
		SessionID:     w.id,
		SessionStatus: SessionStatusEnd,
	}

	frame := buf.New()
	frame.AppendSupplier(meta.AsSupplier())
	runtime.KeepAlive(meta)

	w.writer.Write(buf.NewMultiBufferValue(frame))
}
<commit_msg>safe release buffer<commit_after>package mux

import (
	"runtime"

	"v2ray.com/core/common/buf"
	"v2ray.com/core/common/net"
	"v2ray.com/core/common/protocol"
	"v2ray.com/core/common/serial"
)

type Writer struct {
	dest         net.Destination
	writer       buf.Writer
	id           uint16
	followup     bool
	transferType protocol.TransferType
}

func NewWriter(id uint16, dest net.Destination, writer buf.Writer, transferType protocol.TransferType) *Writer {
	return &Writer{
		id:           id,
		dest:         dest,
		writer:       writer,
		followup:     false,
		transferType: transferType,
	}
}

func NewResponseWriter(id uint16, writer buf.Writer, transferType protocol.TransferType) *Writer {
	return &Writer{
		id:           id,
		writer:       writer,
		followup:     true,
		transferType: transferType,
	}
}

func (w *Writer) getNextFrameMeta() FrameMetadata {
	meta := FrameMetadata{
		SessionID: w.id,
		Target:    w.dest,
	}
	if w.followup {
		meta.SessionStatus = SessionStatusKeep
	} else {
		w.followup = true
		meta.SessionStatus = SessionStatusNew
	}

	return meta
}

func (w *Writer) writeMetaOnly() error {
	meta := w.getNextFrameMeta()
	b := buf.New()
	if err := b.AppendSupplier(meta.AsSupplier()); err != nil {
		return err
	}
	runtime.KeepAlive(meta)
	return w.writer.Write(buf.NewMultiBufferValue(b))
}

func (w *Writer) writeData(mb buf.MultiBuffer) error {
	meta := w.getNextFrameMeta()
	meta.Option.Set(OptionData)

	frame := buf.New()
	if err := frame.AppendSupplier(meta.AsSupplier()); err != nil {
		return err
	}
	runtime.KeepAlive(meta)
	if err := frame.AppendSupplier(serial.WriteUint16(uint16(mb.Len()))); err != nil {
		return err
	}

	mb2 := buf.NewMultiBuffer()
	mb2.Append(frame)
	mb2.AppendMulti(mb)
	return w.writer.Write(mb2)
}

// Write implements buf.MultiBufferWriter.
func (w *Writer) Write(mb buf.MultiBuffer) error {
	defer mb.Release()

	if mb.IsEmpty() {
		return w.writeMetaOnly()
	}

	for !mb.IsEmpty() {
		var chunk buf.MultiBuffer
		if w.transferType == protocol.TransferTypeStream {
			chunk = mb.SliceBySize(8 * 1024)
		} else {
			chunk = buf.NewMultiBufferValue(mb.SplitFirst())
		}
		if err := w.writeData(chunk); err != nil {
			return err
		}
	}

	return nil
}

func (w *Writer) Close() {
	meta := FrameMetadata{
		SessionID:     w.id,
		SessionStatus: SessionStatusEnd,
	}

	frame := buf.New()
	frame.AppendSupplier(meta.AsSupplier())
	runtime.KeepAlive(meta)

	w.writer.Write(buf.NewMultiBufferValue(frame))
}
<|endoftext|>