<commit_before>// Package goparse contains logic for parsing Go source and test files into
// domain models for generating go tests.
package goparser

import (
	"errors"
	"fmt"
	"go/ast"
	"go/parser"
	"go/token"
	"go/types"
	"io/ioutil"

	"github.com/cweill/gotests/internal/models"
)

// ErrEmptyFile represents an empty file error.
var ErrEmptyFile = errors.New("file is empty")

// Result representats a parsed Go file.
type Result struct {
	// The package name and imports of a Go file.
	Header *models.Header
	// All the functions and methods in a Go file.
	Funcs []*models.Function
}

// Parser can parse Go files.
type Parser struct {
	// The importer to resolve packages from import paths.
	Importer types.Importer
}

// Parse parses a given Go file at srcPath, along any files that share the same
// package, into a domain model for generating tests.
func (p *Parser) Parse(srcPath string, files []models.Path) (*Result, error) {
	b, err := p.readFile(srcPath)
	if err != nil {
		return nil, err
	}
	fset := token.NewFileSet()
	f, err := p.parseFile(fset, srcPath)
	if err != nil {
		return nil, err
	}
	fs, err := p.parseFiles(fset, f, files)
	if err != nil {
		return nil, err
	}
	return &Result{
		Header: &models.Header{
			Package: f.Name.String(),
			Imports: parseImports(f.Imports),
			Code:    goCode(b, f),
		},
		Funcs: p.parseFunctions(fset, f, fs),
	}, nil
}

func (p *Parser) readFile(srcPath string) ([]byte, error) {
	b, err := ioutil.ReadFile(srcPath)
	if err != nil {
		return nil, fmt.Errorf("ioutil.ReadFile: %v", err)
	}
	if len(b) == 0 {
		return nil, ErrEmptyFile
	}
	return b, nil
}

func (p *Parser) parseFile(fset *token.FileSet, srcPath string) (*ast.File, error) {
	f, err := parser.ParseFile(fset, srcPath, nil, 0)
	if err != nil {
		return nil, fmt.Errorf("target parser.ParseFile(): %v", err)
	}
	return f, nil
}

func (p *Parser) parseFiles(fset *token.FileSet, f *ast.File, files []models.Path) ([]*ast.File, error) {
	pkg := f.Name.String()
	var fs []*ast.File
	for _, file := range files {
		ff, err := parser.ParseFile(fset, string(file), nil, 0)
		if err != nil {
			return nil, fmt.Errorf("other file parser.ParseFile: %v", err)
		}
		if name := ff.Name.String(); name != pkg {
			continue
		}
		fs = append(fs, ff)
	}
	return fs, nil
}

func (p *Parser) parseFunctions(fset *token.FileSet, f *ast.File, fs []*ast.File) []*models.Function {
	ul, el := p.parseTypes(fset, fs)
	var funcs []*models.Function
	for _, d := range f.Decls {
		fDecl, ok := d.(*ast.FuncDecl)
		if !ok {
			continue
		}
		funcs = append(funcs, parseFunc(fDecl, ul, el))
	}
	return funcs
}

func (p *Parser) parseTypes(fset *token.FileSet, fs []*ast.File) (map[string]types.Type, map[*types.Struct]ast.Expr) {
	conf := &types.Config{
		Importer: p.Importer,
		// Adding a NO-OP error function ignores errors and performs best-effort
		// type checking. https://godoc.org/golang.org/x/tools/go/types#Config
		Error: func(error) {},
	}
	ti := &types.Info{
		Types: make(map[ast.Expr]types.TypeAndValue),
	}
	// Note: conf.Check can fail, but since Info is not required data, it's ok.
	conf.Check("", fset, fs, ti)
	ul := make(map[string]types.Type)
	el := make(map[*types.Struct]ast.Expr)
	for e, t := range ti.Types {
		// Collect the underlying types.
		ul[t.Type.String()] = t.Type.Underlying()
		// Collect structs to determine the fields of a receiver.
		if v, ok := t.Type.(*types.Struct); ok {
			el[v] = e
		}
	}
	return ul, el
}

// Returns the Go code below the imports block.
func goCode(b []byte, f *ast.File) []byte {
	furthestPos := f.Name.End()
	for _, node := range f.Imports {
		if pos := node.End(); pos > furthestPos {
			furthestPos = pos
		}
	}
	if furthestPos < token.Pos(len(b)) {
		furthestPos++
	}
	return b[furthestPos:]
}

func parseFunc(fDecl *ast.FuncDecl, ul map[string]types.Type, el map[*types.Struct]ast.Expr) *models.Function {
	f := &models.Function{
		Name:       fDecl.Name.String(),
		IsExported: fDecl.Name.IsExported(),
		Receiver:   parseReceiver(fDecl.Recv, ul, el),
		Parameters: parseFieldList(fDecl.Type.Params, ul),
	}
	fs := parseFieldList(fDecl.Type.Results, ul)
	i := 0
	for _, fi := range fs {
		if fi.Type.String() == "error" {
			f.ReturnsError = true
			continue
		}
		fi.Index = i
		f.Results = append(f.Results, fi)
		i++
	}
	return f
}

func parseImports(imps []*ast.ImportSpec) []*models.Import {
	var is []*models.Import
	for _, imp := range imps {
		var n string
		if imp.Name != nil {
			n = imp.Name.String()
		}
		is = append(is, &models.Import{
			Name: n,
			Path: imp.Path.Value,
		})
	}
	return is
}

func parseReceiver(fl *ast.FieldList, ul map[string]types.Type, el map[*types.Struct]ast.Expr) *models.Receiver {
	if fl == nil {
		return nil
	}
	r := &models.Receiver{
		Field: parseFieldList(fl, ul)[0],
	}
	t, ok := ul[r.Type.Value]
	if !ok {
		return r
	}
	s, ok := t.(*types.Struct)
	if !ok {
		return r
	}
	st := el[s].(*ast.StructType)
	r.Fields = append(r.Fields, parseFieldList(st.Fields, ul)...)
	for i, f := range r.Fields {
		f.Name = s.Field(i).Name()
	}
	return r

}

func parseFieldList(fl *ast.FieldList, ul map[string]types.Type) []*models.Field {
	if fl == nil {
		return nil
	}
	i := 0
	var fs []*models.Field
	for _, f := range fl.List {
		for _, pf := range parseFields(f, ul) {
			pf.Index = i
			fs = append(fs, pf)
			i++
		}
	}
	return fs
}

func parseFields(f *ast.Field, ul map[string]types.Type) []*models.Field {
	t := parseExpr(f.Type, ul)
	if len(f.Names) == 0 {
		return []*models.Field{{
			Type: t,
		}}
	}
	var fs []*models.Field
	for _, n := range f.Names {
		fs = append(fs, &models.Field{
			Name: n.Name,
			Type: t,
		})
	}
	return fs
}

func parseExpr(e ast.Expr, ul map[string]types.Type) *models.Expression {
	switch v := e.(type) {
	case *ast.StarExpr:
		val := types.ExprString(v.X)
		return &models.Expression{
			Value:      val,
			IsStar:     true,
			Underlying: underlying(val, ul),
		}
	case *ast.Ellipsis:
		exp := parseExpr(v.Elt, ul)
		return &models.Expression{
			Value:      exp.Value,
			IsStar:     exp.IsStar,
			IsVariadic: true,
			Underlying: underlying(exp.Value, ul),
		}
	default:
		val := types.ExprString(e)
		return &models.Expression{
			Value:      val,
			Underlying: underlying(val, ul),
			IsWriter:   val == "io.Writer",
		}
	}
}

func underlying(val string, ul map[string]types.Type) string {
	if ul[val] != nil {
		return ul[val].String()
	}
	return ""
}
<commit_msg>Update goparser doc.<commit_after>// Package goparse contains logic for parsing Go files. Specifically it parses
// source and test files into domain models for generating tests.
package goparser

import (
	"errors"
	"fmt"
	"go/ast"
	"go/parser"
	"go/token"
	"go/types"
	"io/ioutil"

	"github.com/cweill/gotests/internal/models"
)

// ErrEmptyFile represents an empty file error.
var ErrEmptyFile = errors.New("file is empty")

// Result representats a parsed Go file.
type Result struct {
	// The package name and imports of a Go file.
	Header *models.Header
	// All the functions and methods in a Go file.
	Funcs []*models.Function
}

// Parser can parse Go files.
type Parser struct {
	// The importer to resolve packages from import paths.
	Importer types.Importer
}

// Parse parses a given Go file at srcPath, along any files that share the same
// package, into a domain model for generating tests.
func (p *Parser) Parse(srcPath string, files []models.Path) (*Result, error) {
	b, err := p.readFile(srcPath)
	if err != nil {
		return nil, err
	}
	fset := token.NewFileSet()
	f, err := p.parseFile(fset, srcPath)
	if err != nil {
		return nil, err
	}
	fs, err := p.parseFiles(fset, f, files)
	if err != nil {
		return nil, err
	}
	return &Result{
		Header: &models.Header{
			Package: f.Name.String(),
			Imports: parseImports(f.Imports),
			Code:    goCode(b, f),
		},
		Funcs: p.parseFunctions(fset, f, fs),
	}, nil
}

func (p *Parser) readFile(srcPath string) ([]byte, error) {
	b, err := ioutil.ReadFile(srcPath)
	if err != nil {
		return nil, fmt.Errorf("ioutil.ReadFile: %v", err)
	}
	if len(b) == 0 {
		return nil, ErrEmptyFile
	}
	return b, nil
}

func (p *Parser) parseFile(fset *token.FileSet, srcPath string) (*ast.File, error) {
	f, err := parser.ParseFile(fset, srcPath, nil, 0)
	if err != nil {
		return nil, fmt.Errorf("target parser.ParseFile(): %v", err)
	}
	return f, nil
}

func (p *Parser) parseFiles(fset *token.FileSet, f *ast.File, files []models.Path) ([]*ast.File, error) {
	pkg := f.Name.String()
	var fs []*ast.File
	for _, file := range files {
		ff, err := parser.ParseFile(fset, string(file), nil, 0)
		if err != nil {
			return nil, fmt.Errorf("other file parser.ParseFile: %v", err)
		}
		if name := ff.Name.String(); name != pkg {
			continue
		}
		fs = append(fs, ff)
	}
	return fs, nil
}

func (p *Parser) parseFunctions(fset *token.FileSet, f *ast.File, fs []*ast.File) []*models.Function {
	ul, el := p.parseTypes(fset, fs)
	var funcs []*models.Function
	for _, d := range f.Decls {
		fDecl, ok := d.(*ast.FuncDecl)
		if !ok {
			continue
		}
		funcs = append(funcs, parseFunc(fDecl, ul, el))
	}
	return funcs
}

func (p *Parser) parseTypes(fset *token.FileSet, fs []*ast.File) (map[string]types.Type, map[*types.Struct]ast.Expr) {
	conf := &types.Config{
		Importer: p.Importer,
		// Adding a NO-OP error function ignores errors and performs best-effort
		// type checking. https://godoc.org/golang.org/x/tools/go/types#Config
		Error: func(error) {},
	}
	ti := &types.Info{
		Types: make(map[ast.Expr]types.TypeAndValue),
	}
	// Note: conf.Check can fail, but since Info is not required data, it's ok.
	conf.Check("", fset, fs, ti)
	ul := make(map[string]types.Type)
	el := make(map[*types.Struct]ast.Expr)
	for e, t := range ti.Types {
		// Collect the underlying types.
		ul[t.Type.String()] = t.Type.Underlying()
		// Collect structs to determine the fields of a receiver.
		if v, ok := t.Type.(*types.Struct); ok {
			el[v] = e
		}
	}
	return ul, el
}

// Returns the Go code below the imports block.
func goCode(b []byte, f *ast.File) []byte {
	furthestPos := f.Name.End()
	for _, node := range f.Imports {
		if pos := node.End(); pos > furthestPos {
			furthestPos = pos
		}
	}
	if furthestPos < token.Pos(len(b)) {
		furthestPos++
	}
	return b[furthestPos:]
}

func parseFunc(fDecl *ast.FuncDecl, ul map[string]types.Type, el map[*types.Struct]ast.Expr) *models.Function {
	f := &models.Function{
		Name:       fDecl.Name.String(),
		IsExported: fDecl.Name.IsExported(),
		Receiver:   parseReceiver(fDecl.Recv, ul, el),
		Parameters: parseFieldList(fDecl.Type.Params, ul),
	}
	fs := parseFieldList(fDecl.Type.Results, ul)
	i := 0
	for _, fi := range fs {
		if fi.Type.String() == "error" {
			f.ReturnsError = true
			continue
		}
		fi.Index = i
		f.Results = append(f.Results, fi)
		i++
	}
	return f
}

func parseImports(imps []*ast.ImportSpec) []*models.Import {
	var is []*models.Import
	for _, imp := range imps {
		var n string
		if imp.Name != nil {
			n = imp.Name.String()
		}
		is = append(is, &models.Import{
			Name: n,
			Path: imp.Path.Value,
		})
	}
	return is
}

func parseReceiver(fl *ast.FieldList, ul map[string]types.Type, el map[*types.Struct]ast.Expr) *models.Receiver {
	if fl == nil {
		return nil
	}
	r := &models.Receiver{
		Field: parseFieldList(fl, ul)[0],
	}
	t, ok := ul[r.Type.Value]
	if !ok {
		return r
	}
	s, ok := t.(*types.Struct)
	if !ok {
		return r
	}
	st := el[s].(*ast.StructType)
	r.Fields = append(r.Fields, parseFieldList(st.Fields, ul)...)
	for i, f := range r.Fields {
		f.Name = s.Field(i).Name()
	}
	return r

}

func parseFieldList(fl *ast.FieldList, ul map[string]types.Type) []*models.Field {
	if fl == nil {
		return nil
	}
	i := 0
	var fs []*models.Field
	for _, f := range fl.List {
		for _, pf := range parseFields(f, ul) {
			pf.Index = i
			fs = append(fs, pf)
			i++
		}
	}
	return fs
}

func parseFields(f *ast.Field, ul map[string]types.Type) []*models.Field {
	t := parseExpr(f.Type, ul)
	if len(f.Names) == 0 {
		return []*models.Field{{
			Type: t,
		}}
	}
	var fs []*models.Field
	for _, n := range f.Names {
		fs = append(fs, &models.Field{
			Name: n.Name,
			Type: t,
		})
	}
	return fs
}

func parseExpr(e ast.Expr, ul map[string]types.Type) *models.Expression {
	switch v := e.(type) {
	case *ast.StarExpr:
		val := types.ExprString(v.X)
		return &models.Expression{
			Value:      val,
			IsStar:     true,
			Underlying: underlying(val, ul),
		}
	case *ast.Ellipsis:
		exp := parseExpr(v.Elt, ul)
		return &models.Expression{
			Value:      exp.Value,
			IsStar:     exp.IsStar,
			IsVariadic: true,
			Underlying: underlying(exp.Value, ul),
		}
	default:
		val := types.ExprString(e)
		return &models.Expression{
			Value:      val,
			Underlying: underlying(val, ul),
			IsWriter:   val == "io.Writer",
		}
	}
}

func underlying(val string, ul map[string]types.Type) string {
	if ul[val] != nil {
		return ul[val].String()
	}
	return ""
}
<|endoftext|>


<commit_before>package handler

import (
	"context"
	"errors"
	"testing"

	"github.com/stretchr/testify/require"
	"google.golang.org/grpc"
	"google.golang.org/grpc/metadata"

	"gitlab.com/gitlab-org/gitlab-shell/internal/config"
)

func makeHandler(t *testing.T, err error) func(context.Context, *grpc.ClientConn) (int32, error) {
	return func(ctx context.Context, client *grpc.ClientConn) (int32, error) {
		require.NotNil(t, ctx)
		require.NotNil(t, client)

		return 0, err
	}
}

func TestRunGitalyCommand(t *testing.T) {
	cmd := GitalyCommand{
		Config:  &config.Config{},
		Address: "tcp://localhost:9999",
	}

	err := cmd.RunGitalyCommand(makeHandler(t, nil))
	require.NoError(t, err)

	expectedErr := errors.New("error")
	err = cmd.RunGitalyCommand(makeHandler(t, expectedErr))
	require.Equal(t, err, expectedErr)
}

func TestMissingGitalyAddress(t *testing.T) {
	cmd := GitalyCommand{Config: &config.Config{}}

	err := cmd.RunGitalyCommand(makeHandler(t, nil))
	require.EqualError(t, err, "no gitaly_address given")
}

func TestGetConnMetadata(t *testing.T) {
	tests := []struct {
		name string
		gc   *GitalyCommand
		want map[string]string
	}{
		{
			name: "gitaly_feature_flags",
			gc: &GitalyCommand{
				Config:  &config.Config{},
				Address: "tcp://localhost:9999",
				Features: map[string]string{
					"gitaly-feature-cache_invalidator":        "true",
					"other-ff":                                "true",
					"gitaly-feature-inforef_uploadpack_cache": "false",
				},
			},
			want: map[string]string{
				"gitaly-feature-cache_invalidator":        "true",
				"gitaly-feature-inforef_uploadpack_cache": "false",
			},
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			conn, err := getConn(tt.gc)
			require.NoError(t, err)

			md, exists := metadata.FromOutgoingContext(conn.ctx)
			require.True(t, exists)
			require.Equal(t, len(tt.want), md.Len())

			for k, v := range tt.want {
				values := md.Get(k)
				require.Equal(t, 1, len(values))
				require.Equal(t, v, values[0])
			}

		})
	}
}
<commit_msg>test for client identity propagation<commit_after>package handler

import (
	"context"
	"errors"
	"os"
	"testing"

	"github.com/stretchr/testify/require"
	"google.golang.org/grpc"
	"google.golang.org/grpc/metadata"

	pb "gitlab.com/gitlab-org/gitaly/proto/go/gitalypb"
	"gitlab.com/gitlab-org/gitlab-shell/internal/config"
	"gitlab.com/gitlab-org/gitlab-shell/internal/gitlabnet/accessverifier"
)

func makeHandler(t *testing.T, err error) func(context.Context, *grpc.ClientConn) (int32, error) {
	return func(ctx context.Context, client *grpc.ClientConn) (int32, error) {
		require.NotNil(t, ctx)
		require.NotNil(t, client)

		return 0, err
	}
}

func TestRunGitalyCommand(t *testing.T) {
	cmd := GitalyCommand{
		Config:  &config.Config{},
		Address: "tcp://localhost:9999",
	}

	err := cmd.RunGitalyCommand(makeHandler(t, nil))
	require.NoError(t, err)

	expectedErr := errors.New("error")
	err = cmd.RunGitalyCommand(makeHandler(t, expectedErr))
	require.Equal(t, err, expectedErr)
}

func TestMissingGitalyAddress(t *testing.T) {
	cmd := GitalyCommand{Config: &config.Config{}}

	err := cmd.RunGitalyCommand(makeHandler(t, nil))
	require.EqualError(t, err, "no gitaly_address given")
}

func TestGetConnMetadata(t *testing.T) {
	tests := []struct {
		name string
		gc   *GitalyCommand
		want map[string]string
	}{
		{
			name: "gitaly_feature_flags",
			gc: &GitalyCommand{
				Config:  &config.Config{},
				Address: "tcp://localhost:9999",
				Features: map[string]string{
					"gitaly-feature-cache_invalidator":        "true",
					"other-ff":                                "true",
					"gitaly-feature-inforef_uploadpack_cache": "false",
				},
			},
			want: map[string]string{
				"gitaly-feature-cache_invalidator":        "true",
				"gitaly-feature-inforef_uploadpack_cache": "false",
			},
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			conn, err := getConn(tt.gc)
			require.NoError(t, err)

			md, exists := metadata.FromOutgoingContext(conn.ctx)
			require.True(t, exists)
			require.Equal(t, len(tt.want), md.Len())

			for k, v := range tt.want {
				values := md.Get(k)
				require.Equal(t, 1, len(values))
				require.Equal(t, v, values[0])
			}

		})
	}
}

func TestPrepareContext(t *testing.T) {
	tests := []struct {
		name             string
		gc               *GitalyCommand
		sshConnectionEnv string
		repo             *pb.Repository
		response         *accessverifier.Response
		want             map[string]string
	}{
		{
			name: "client_identity",
			gc: &GitalyCommand{
				Config:  &config.Config{},
				Address: "tcp://localhost:9999",
			},
			sshConnectionEnv: "10.0.0.1 1234 127.0.0.1 5678",
			repo: &pb.Repository{
				StorageName:                   "default",
				RelativePath:                  "@hashed/5f/9c/5f9c4ab08cac7457e9111a30e4664920607ea2c115a1433d7be98e97e64244ca.git",
				GitObjectDirectory:            "path/to/git_object_directory",
				GitAlternateObjectDirectories: []string{"path/to/git_alternate_object_directory"},
				GlRepository:                  "project-26",
				GlProjectPath:                 "group/private",
			},
			response: &accessverifier.Response{
				UserId:   "6",
				Username: "jane.doe",
			},
			want: map[string]string{
				"remote_ip": "10.0.0.1",
				"user_id":   "6",
				"username":  "jane.doe",
			},
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			ctx := context.Background()

			orig := os.Getenv("SSH_CONNECTION")
			defer os.Setenv("SSH_CONNECTION", orig)
			os.Setenv("SSH_CONNECTION", tt.sshConnectionEnv)

			ctx, cancel := tt.gc.PrepareContext(ctx, tt.repo, tt.response, "protocol")
			defer cancel()

			md, exists := metadata.FromOutgoingContext(ctx)
			require.True(t, exists)
			require.Equal(t, len(tt.want), md.Len())

			for k, v := range tt.want {
				values := md.Get(k)
				require.Equal(t, 1, len(values))
				require.Equal(t, v, values[0])
			}

		})
	}
}
<|endoftext|>


<commit_before>package fuse

import (
	"fmt"
	"os"
	"os/signal"
	"path"
	"strings"
	"sync"

	"github.com/hanwen/go-fuse/fuse"
	"github.com/hanwen/go-fuse/fuse/nodefs"
	"github.com/hanwen/go-fuse/fuse/pathfs"
	"github.com/pachyderm/pachyderm/src/client"
	"github.com/pachyderm/pachyderm/src/client/pfs"
	"github.com/pachyderm/pachyderm/src/server/pkg/uuid"
)

// Mount pfs to mountPoint, opts may be left nil.
func Mount(c *client.APIClient, mountPoint string, opts *Options) error {
	nfs := pathfs.NewPathNodeFs(newFileSystem(c, opts.getCommits()), nil)
	server, _, err := nodefs.MountRoot(mountPoint, nfs.Root(), opts.getFuse())
	if err != nil {
		return fmt.Errorf("nodefs.MountRoot: %v", err)
	}
	sigChan := make(chan os.Signal, 1)
	signal.Notify(sigChan, os.Interrupt)
	go func() {
		select {
		case <-sigChan:
		case <-opts.getUnmount():
		}
		server.Unmount()
	}()
	server.Serve()
	return nil
}

type filesystem struct {
	pathfs.FileSystem
	c         *client.APIClient
	commits   map[string]string
	commitsMu sync.RWMutex
}

func newFileSystem(c *client.APIClient, commits map[string]string) pathfs.FileSystem {
	if commits == nil {
		commits = make(map[string]string)
	}
	return &filesystem{
		FileSystem: pathfs.NewDefaultFileSystem(),
		c:          c,
		commits:    commits,
	}
}

func (fs *filesystem) GetAttr(name string, context *fuse.Context) (*fuse.Attr, fuse.Status) {
	return fs.getAttr(name)
}

func (fs *filesystem) OpenDir(name string, context *fuse.Context) ([]fuse.DirEntry, fuse.Status) {
	var result []fuse.DirEntry
	r, f, err := fs.parsePath(name)
	if err != nil {
		return nil, toStatus(err)
	}
	switch {
	case r != nil:
		commit, err := fs.commit(r.Name)
		if err != nil {
			return nil, toStatus(err)
		}
		if err := fs.c.ListFileF(r.Name, commit, "", func(fi *pfs.FileInfo) error {
			result = append(result, fileDirEntry(fi))
			return nil
		}); err != nil {
			return nil, toStatus(err)
		}
	case f != nil:
		if err := fs.c.ListFileF(f.Commit.Repo.Name, f.Commit.ID, f.Path, func(fi *pfs.FileInfo) error {
			result = append(result, fileDirEntry(fi))
			return nil
		}); err != nil {
			return nil, toStatus(err)
		}
	default:
		ris, err := fs.c.ListRepo()
		if err != nil {
			return nil, toStatus(err)
		}
		for _, ri := range ris {
			result = append(result, repoDirEntry(ri))
		}
	}
	return result, fuse.OK
}

func (fs *filesystem) Open(name string, flags uint32, context *fuse.Context) (nodefs.File, fuse.Status) {
	// TODO use flags
	return newFile(fs, name), fuse.OK
}

func (fs *filesystem) commit(repo string) (string, error) {
	commitOrBranch := func() string {
		fs.commitsMu.RLock()
		defer fs.commitsMu.RUnlock()
		return fs.commits[repo]
	}()
	if uuid.IsUUIDWithoutDashes(commitOrBranch) {
		// it's a commit, return it
		return commitOrBranch, nil
	}
	// it's a branch, resolve the head and return that
	branch := commitOrBranch
	if branch == "" {
		branch = "master"
	}
	bi, err := fs.c.InspectBranch(repo, branch)
	if err != nil {
		return "", err
	}
	fs.commitsMu.Lock()
	defer fs.commitsMu.Unlock()
	fs.commits[repo] = bi.Head.ID
	return bi.Head.ID, nil
}

func (fs *filesystem) parsePath(name string) (*pfs.Repo, *pfs.File, error) {
	components := strings.Split(name, "/")
	switch {
	case name == "":
		return nil, nil, nil
	case len(components) == 1:
		return client.NewRepo(components[0]), nil, nil
	default:
		commit, err := fs.commit(components[0])
		if err != nil {
			return nil, nil, err
		}
		return nil, client.NewFile(components[0], commit, path.Join(components[1:]...)), nil
	}
}

func (fs *filesystem) getAttr(name string) (*fuse.Attr, fuse.Status) {
	r, f, err := fs.parsePath(name)
	if err != nil {
		return nil, toStatus(err)
	}
	switch {
	case r != nil:
		return fs.repoAttr(r)
	case f != nil:
		return fs.fileAttr(f)
	default:
		return &fuse.Attr{
			Mode: fuse.S_IFDIR | 0755,
		}, fuse.OK
	}
}

func (fs *filesystem) repoAttr(r *pfs.Repo) (*fuse.Attr, fuse.Status) {
	ri, err := fs.c.InspectRepo(r.Name)
	if err != nil {
		return nil, toStatus(err)
	}
	return &fuse.Attr{
		Mode:      fuse.S_IFDIR | 0755,
		Ctime:     uint64(ri.Created.Seconds),
		Ctimensec: uint32(ri.Created.Nanos),
		Mtime:     uint64(ri.Created.Seconds),
		Mtimensec: uint32(ri.Created.Nanos),
	}, fuse.OK
}

func repoDirEntry(ri *pfs.RepoInfo) fuse.DirEntry {
	return fuse.DirEntry{
		Name: ri.Repo.Name,
		Mode: fuse.S_IFDIR | 0755,
	}
}

func fileMode(fi *pfs.FileInfo) uint32 {
	switch fi.FileType {
	case pfs.FileType_FILE:
		return fuse.S_IFREG | 0644
	case pfs.FileType_DIR:
		return fuse.S_IFDIR | 0644
	default:
		return 0
	}
}

func (fs *filesystem) fileAttr(f *pfs.File) (*fuse.Attr, fuse.Status) {
	fi, err := fs.c.InspectFile(f.Commit.Repo.Name, f.Commit.ID, f.Path)
	if err != nil {
		return nil, toStatus(err)
	}
	return &fuse.Attr{
		Mode: fileMode(fi),
		Size: fi.SizeBytes,
	}, fuse.OK
}

func fileDirEntry(fi *pfs.FileInfo) fuse.DirEntry {
	return fuse.DirEntry{
		Mode: fileMode(fi),
		Name: fi.File.Path,
	}
}

func toStatus(err error) fuse.Status {
	if strings.Contains(err.Error(), "not found") {
		return fuse.ENOENT
	}
	return fuse.EIO
}
<commit_msg>Make permissions make sense.<commit_after>package fuse

import (
	"fmt"
	"os"
	"os/signal"
	"path"
	"strings"
	"sync"

	"github.com/hanwen/go-fuse/fuse"
	"github.com/hanwen/go-fuse/fuse/nodefs"
	"github.com/hanwen/go-fuse/fuse/pathfs"
	"github.com/pachyderm/pachyderm/src/client"
	"github.com/pachyderm/pachyderm/src/client/pfs"
	"github.com/pachyderm/pachyderm/src/server/pkg/uuid"
)

// Mount pfs to mountPoint, opts may be left nil.
func Mount(c *client.APIClient, mountPoint string, opts *Options) error {
	nfs := pathfs.NewPathNodeFs(newFileSystem(c, opts.getCommits()), nil)
	server, _, err := nodefs.MountRoot(mountPoint, nfs.Root(), opts.getFuse())
	if err != nil {
		return fmt.Errorf("nodefs.MountRoot: %v", err)
	}
	sigChan := make(chan os.Signal, 1)
	signal.Notify(sigChan, os.Interrupt)
	go func() {
		select {
		case <-sigChan:
		case <-opts.getUnmount():
		}
		server.Unmount()
	}()
	server.Serve()
	return nil
}

type filesystem struct {
	pathfs.FileSystem
	c         *client.APIClient
	commits   map[string]string
	commitsMu sync.RWMutex
}

func newFileSystem(c *client.APIClient, commits map[string]string) pathfs.FileSystem {
	if commits == nil {
		commits = make(map[string]string)
	}
	return &filesystem{
		FileSystem: pathfs.NewDefaultFileSystem(),
		c:          c,
		commits:    commits,
	}
}

func (fs *filesystem) GetAttr(name string, context *fuse.Context) (*fuse.Attr, fuse.Status) {
	return fs.getAttr(name)
}

func (fs *filesystem) OpenDir(name string, context *fuse.Context) ([]fuse.DirEntry, fuse.Status) {
	var result []fuse.DirEntry
	r, f, err := fs.parsePath(name)
	if err != nil {
		return nil, toStatus(err)
	}
	switch {
	case r != nil:
		commit, err := fs.commit(r.Name)
		if err != nil {
			return nil, toStatus(err)
		}
		if err := fs.c.ListFileF(r.Name, commit, "", func(fi *pfs.FileInfo) error {
			result = append(result, fileDirEntry(fi))
			return nil
		}); err != nil {
			return nil, toStatus(err)
		}
	case f != nil:
		if err := fs.c.ListFileF(f.Commit.Repo.Name, f.Commit.ID, f.Path, func(fi *pfs.FileInfo) error {
			result = append(result, fileDirEntry(fi))
			return nil
		}); err != nil {
			return nil, toStatus(err)
		}
	default:
		ris, err := fs.c.ListRepo()
		if err != nil {
			return nil, toStatus(err)
		}
		for _, ri := range ris {
			result = append(result, repoDirEntry(ri))
		}
	}
	return result, fuse.OK
}

func (fs *filesystem) Open(name string, flags uint32, context *fuse.Context) (nodefs.File, fuse.Status) {
	// TODO use flags
	return newFile(fs, name), fuse.OK
}

func (fs *filesystem) commit(repo string) (string, error) {
	commitOrBranch := func() string {
		fs.commitsMu.RLock()
		defer fs.commitsMu.RUnlock()
		return fs.commits[repo]
	}()
	if uuid.IsUUIDWithoutDashes(commitOrBranch) {
		// it's a commit, return it
		return commitOrBranch, nil
	}
	// it's a branch, resolve the head and return that
	branch := commitOrBranch
	if branch == "" {
		branch = "master"
	}
	bi, err := fs.c.InspectBranch(repo, branch)
	if err != nil {
		return "", err
	}
	fs.commitsMu.Lock()
	defer fs.commitsMu.Unlock()
	fs.commits[repo] = bi.Head.ID
	return bi.Head.ID, nil
}

func (fs *filesystem) parsePath(name string) (*pfs.Repo, *pfs.File, error) {
	components := strings.Split(name, "/")
	switch {
	case name == "":
		return nil, nil, nil
	case len(components) == 1:
		return client.NewRepo(components[0]), nil, nil
	default:
		commit, err := fs.commit(components[0])
		if err != nil {
			return nil, nil, err
		}
		return nil, client.NewFile(components[0], commit, path.Join(components[1:]...)), nil
	}
}

func (fs *filesystem) getAttr(name string) (*fuse.Attr, fuse.Status) {
	r, f, err := fs.parsePath(name)
	if err != nil {
		return nil, toStatus(err)
	}
	switch {
	case r != nil:
		return fs.repoAttr(r)
	case f != nil:
		return fs.fileAttr(f)
	default:
		return &fuse.Attr{
			Mode: fuse.S_IFDIR | 0755,
		}, fuse.OK
	}
}

func (fs *filesystem) repoAttr(r *pfs.Repo) (*fuse.Attr, fuse.Status) {
	ri, err := fs.c.InspectRepo(r.Name)
	if err != nil {
		return nil, toStatus(err)
	}
	return &fuse.Attr{
		Mode:      fuse.S_IFDIR | 0755,
		Ctime:     uint64(ri.Created.Seconds),
		Ctimensec: uint32(ri.Created.Nanos),
		Mtime:     uint64(ri.Created.Seconds),
		Mtimensec: uint32(ri.Created.Nanos),
	}, fuse.OK
}

func repoDirEntry(ri *pfs.RepoInfo) fuse.DirEntry {
	return fuse.DirEntry{
		Name: ri.Repo.Name,
		Mode: fuse.S_IFDIR | 0755,
	}
}

func fileMode(fi *pfs.FileInfo) uint32 {
	switch fi.FileType {
	case pfs.FileType_FILE:
		return fuse.S_IFREG | 0444 // everyone can read, no one can do anything else
	case pfs.FileType_DIR:
		return fuse.S_IFDIR | 0555 // everyone can read and execute, no one can do anything else
	default:
		return 0
	}
}

func (fs *filesystem) fileAttr(f *pfs.File) (*fuse.Attr, fuse.Status) {
	fi, err := fs.c.InspectFile(f.Commit.Repo.Name, f.Commit.ID, f.Path)
	if err != nil {
		return nil, toStatus(err)
	}
	return &fuse.Attr{
		Mode: fileMode(fi),
		Size: fi.SizeBytes,
	}, fuse.OK
}

func fileDirEntry(fi *pfs.FileInfo) fuse.DirEntry {
	return fuse.DirEntry{
		Mode: fileMode(fi),
		Name: fi.File.Path,
	}
}

func toStatus(err error) fuse.Status {
	if strings.Contains(err.Error(), "not found") {
		return fuse.ENOENT
	}
	return fuse.EIO
}
<|endoftext|>


<commit_before>// Copyright 2019 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package postgres

import (
	"context"
	"database/sql"
	"fmt"
	"log"
	"strings"
	"sync/atomic"
	"unicode"

	"golang.org/x/discovery/internal/config"
	"golang.org/x/discovery/internal/derrors"
)

// DB wraps a sql.DB to provide an API for interacting with discovery data
// stored in Postgres.
type DB struct {
	db *sql.DB
}

func (db *DB) exec(ctx context.Context, query string, args ...interface{}) (res sql.Result, err error) {
	defer logQuery(query, args)(&err)

	return db.db.ExecContext(ctx, query, args...)
}

func execTx(ctx context.Context, tx *sql.Tx, query string, args ...interface{}) (res sql.Result, err error) {
	defer logQuery(query, args)(&err)

	return tx.ExecContext(ctx, query, args...)
}

func (db *DB) query(ctx context.Context, query string, args ...interface{}) (_ *sql.Rows, err error) {
	defer logQuery(query, args)(&err)
	return db.db.QueryContext(ctx, query, args...)
}

func (db *DB) queryRow(ctx context.Context, query string, args ...interface{}) *sql.Row {
	defer logQuery(query, args)(nil)
	return db.db.QueryRowContext(ctx, query, args...)
}

var queryCounter int64 // atomic: per-process counter for unique query IDs

func logQuery(query string, args []interface{}) func(*error) {
	const maxlen = 300 // maximum length of displayed query

	// To make the query more compact and readable, replace newlines with spaces
	// and collapse adjacent whitespace.
	var r []rune
	for _, c := range query {
		if c == '\n' {
			c = ' '
		}
		if len(r) == 0 || !unicode.IsSpace(r[len(r)-1]) || !unicode.IsSpace(c) {
			r = append(r, c)
		}
	}
	query = string(r)
	if len(query) > maxlen {
		query = query[:maxlen] + "..."
	}

	instanceID := config.InstanceID()
	if instanceID == "" {
		instanceID = "local"
	} else {
		// Instance IDs are long strings. The low-order part seems quite random, so
		// shortening the ID will still likely result in something unique.
		instanceID = instanceID[:8]
	}
	n := atomic.AddInt64(&queryCounter, 1)
	uid := fmt.Sprintf("%s-%d", instanceID, n)

	const maxargs = 20 // maximum displayed args
	var moreargs string
	if len(args) > maxargs {
		args = args[:maxargs]
		moreargs = "..."
	}

	log.Printf("%s %s %v%s", uid, query, args, moreargs)
	return func(errp *error) {
		if errp == nil { // happens with queryRow
			log.Printf("%s done", uid)
		} else {
			log.Printf("%s err=%v", uid, *errp)
			derrors.Wrap(errp, "DB running query %s", uid)
		}
	}
}

// Open creates a new DB for the given Postgres connection string.
func Open(driverName, dbinfo string) (*DB, error) {
	db, err := sql.Open(driverName, dbinfo)
	if err != nil {
		return nil, err
	}

	if err = db.Ping(); err != nil {
		return nil, err
	}
	return &DB{db}, nil
}

// Transact executes the given function in the context of a SQL transaction,
// rolling back the transaction if the function panics or returns an error.
func (db *DB) Transact(txFunc func(*sql.Tx) error) (err error) {
	tx, err := db.db.Begin()
	if err != nil {
		return fmt.Errorf("db.Begin(): %v", err)
	}

	defer func() {
		if p := recover(); p != nil {
			tx.Rollback()
			panic(p)
		} else if err != nil {
			tx.Rollback()
		} else {
			if err = tx.Commit(); err != nil {
				err = fmt.Errorf("tx.Commit(): %v", err)
			}
		}
	}()

	if err := txFunc(tx); err != nil {
		return fmt.Errorf("txFunc(tx): %v", err)
	}
	return nil
}

// prepareAndExec prepares a query statement and executes it insde the provided
// transaction.
func prepareAndExec(tx *sql.Tx, query string, stmtFunc func(*sql.Stmt) error) (err error) {
	stmt, err := tx.Prepare(query)
	if err != nil {
		return fmt.Errorf("tx.Prepare(%q): %v", query, err)
	}

	defer func() {
		cerr := stmt.Close()
		if err == nil {
			err = cerr
		}
	}()
	if err := stmtFunc(stmt); err != nil {
		return fmt.Errorf("stmtFunc(stmt): %v", err)
	}
	return nil
}

const onConflictDoNothing = "ON CONFLICT DO NOTHING"

// bulkInsert constructs and executes a multi-value insert statement. The
// query is constructed using the format: INSERT TO <table> (<columns>) VALUES
// (<placeholders-for-each-item-in-values>) If conflictNoAction is true, it
// append ON CONFLICT DO NOTHING to the end of the query. The query is executed
// using a PREPARE statement with the provided values.
func bulkInsert(ctx context.Context, tx *sql.Tx, table string, columns []string, values []interface{}, conflictAction string) (err error) {
	defer derrors.Wrap(&err, "bulkInsert(ctx, tx, %q, %v, [%d values], %q)",
		table, columns, len(values), conflictAction)

	if remainder := len(values) % len(columns); remainder != 0 {
		return fmt.Errorf("modulus of len(values) and len(columns) must be 0: got %d", remainder)
	}

	const maxParameters = 65535 // maximum number of parameters allowed by Postgres
	stride := (maxParameters / len(columns)) * len(columns)
	if stride == 0 {
		// This is a pathological case (len(columns) > maxParameters), but we
		// handle it cautiously.
		return fmt.Errorf("too many columns to insert: %d", len(columns))
	}
	for leftBound := 0; leftBound < len(values); leftBound += stride {
		rightBound := leftBound + stride
		if rightBound > len(values) {
			rightBound = len(values)
		}
		valueSlice := values[leftBound:rightBound]
		query, err := buildInsertQuery(table, columns, valueSlice, conflictAction)
		if err != nil {
			return fmt.Errorf("buildInsertQuery(%q, %v, values[%d:%d], %q): %v", table, columns, leftBound, rightBound, conflictAction, err)
		}

		defer logQuery(query, valueSlice)(&err)
		if _, err := tx.ExecContext(ctx, query, valueSlice...); err != nil {
			return fmt.Errorf("tx.ExecContext(ctx, [bulk insert query], values[%d:%d]): %v", leftBound, rightBound, err)
		}
	}
	return nil
}

// buildInsertQuery builds an multi-value insert query, following the format:
// INSERT TO <table> (<columns>) VALUES
// (<placeholders-for-each-item-in-values>) If conflictNoAction is true, it
// append ON CONFLICT DO NOTHING to the end of the query.
//
// When calling buildInsertQuery, it must be true that
//	len(values) % len(columns) == 0
func buildInsertQuery(table string, columns []string, values []interface{}, conflictAction string) (string, error) {
	var b strings.Builder
	fmt.Fprintf(&b, "INSERT INTO %s", table)
	fmt.Fprintf(&b, "(%s) VALUES", strings.Join(columns, ", "))

	var placeholders []string
	for i := 1; i <= len(values); i++ {
		// Construct the full query by adding placeholders for each
		// set of values that we want to insert.
		placeholders = append(placeholders, fmt.Sprintf("$%d", i))
		if i%len(columns) != 0 {
			continue
		}

		// When the end of a set is reached, write it to the query
		// builder and reset placeholders.
		fmt.Fprintf(&b, "(%s)", strings.Join(placeholders, ", "))
		placeholders = []string{}

		// Do not add a comma delimiter after the last set of values.
		if i == len(values) {
			break
		}
		b.WriteString(", ")
	}
	if conflictAction != "" {
		b.WriteString(" " + conflictAction)
	}

	return b.String(), nil
}

// Close closes the database connection.
func (db *DB) Close() error {
	return db.db.Close()
}

// runQuery executes query, then calls f on each row.
func (db *DB) runQuery(ctx context.Context, query string, f func(*sql.Rows) error, params ...interface{}) error {
	rows, err := db.query(ctx, query, params...)
	if err != nil {
		return err
	}
	defer rows.Close()

	for rows.Next() {
		if err := f(rows); err != nil {
			return err
		}
	}
	return rows.Err()
}

// emptyStringScanner wraps the functionality of sql.NullString to just write
// an empty string if the value is NULL.
type emptyStringScanner struct {
	ptr *string
}

func (e emptyStringScanner) Scan(value interface{}) error {
	var ns sql.NullString
	if err := ns.Scan(value); err != nil {
		return err
	}
	*e.ptr = ns.String
	return nil
}

// nullIsEmpty returns a sql.Scanner that writes the empty string to s if the
// sql.Value is NULL.
func nullIsEmpty(s *string) sql.Scanner {
	return emptyStringScanner{s}
}
<commit_msg>internal/postgres: take low-order part of instanceID<commit_after>// Copyright 2019 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package postgres

import (
	"context"
	"database/sql"
	"fmt"
	"log"
	"strings"
	"sync/atomic"
	"unicode"

	"golang.org/x/discovery/internal/config"
	"golang.org/x/discovery/internal/derrors"
)

// DB wraps a sql.DB to provide an API for interacting with discovery data
// stored in Postgres.
type DB struct {
	db *sql.DB
}

func (db *DB) exec(ctx context.Context, query string, args ...interface{}) (res sql.Result, err error) {
	defer logQuery(query, args)(&err)

	return db.db.ExecContext(ctx, query, args...)
}

func execTx(ctx context.Context, tx *sql.Tx, query string, args ...interface{}) (res sql.Result, err error) {
	defer logQuery(query, args)(&err)

	return tx.ExecContext(ctx, query, args...)
}

func (db *DB) query(ctx context.Context, query string, args ...interface{}) (_ *sql.Rows, err error) {
	defer logQuery(query, args)(&err)
	return db.db.QueryContext(ctx, query, args...)
}

func (db *DB) queryRow(ctx context.Context, query string, args ...interface{}) *sql.Row {
	defer logQuery(query, args)(nil)
	return db.db.QueryRowContext(ctx, query, args...)
}

var queryCounter int64 // atomic: per-process counter for unique query IDs

func logQuery(query string, args []interface{}) func(*error) {
	const maxlen = 300 // maximum length of displayed query

	// To make the query more compact and readable, replace newlines with spaces
	// and collapse adjacent whitespace.
	var r []rune
	for _, c := range query {
		if c == '\n' {
			c = ' '
		}
		if len(r) == 0 || !unicode.IsSpace(r[len(r)-1]) || !unicode.IsSpace(c) {
			r = append(r, c)
		}
	}
	query = string(r)
	if len(query) > maxlen {
		query = query[:maxlen] + "..."
	}

	instanceID := config.InstanceID()
	if instanceID == "" {
		instanceID = "local"
	} else {
		// Instance IDs are long strings. The low-order part seems quite random, so
		// shortening the ID will still likely result in something unique.
		instanceID = instanceID[len(instanceID)-4:]
	}
	n := atomic.AddInt64(&queryCounter, 1)
	uid := fmt.Sprintf("%s-%d", instanceID, n)

	const maxargs = 20 // maximum displayed args
	var moreargs string
	if len(args) > maxargs {
		args = args[:maxargs]
		moreargs = "..."
	}

	log.Printf("%s %s %v%s", uid, query, args, moreargs)
	return func(errp *error) {
		if errp == nil { // happens with queryRow
			log.Printf("%s done", uid)
		} else {
			log.Printf("%s err=%v", uid, *errp)
			derrors.Wrap(errp, "DB running query %s", uid)
		}
	}
}

// Open creates a new DB for the given Postgres connection string.
func Open(driverName, dbinfo string) (*DB, error) {
	db, err := sql.Open(driverName, dbinfo)
	if err != nil {
		return nil, err
	}

	if err = db.Ping(); err != nil {
		return nil, err
	}
	return &DB{db}, nil
}

// Transact executes the given function in the context of a SQL transaction,
// rolling back the transaction if the function panics or returns an error.
func (db *DB) Transact(txFunc func(*sql.Tx) error) (err error) {
	tx, err := db.db.Begin()
	if err != nil {
		return fmt.Errorf("db.Begin(): %v", err)
	}

	defer func() {
		if p := recover(); p != nil {
			tx.Rollback()
			panic(p)
		} else if err != nil {
			tx.Rollback()
		} else {
			if err = tx.Commit(); err != nil {
				err = fmt.Errorf("tx.Commit(): %v", err)
			}
		}
	}()

	if err := txFunc(tx); err != nil {
		return fmt.Errorf("txFunc(tx): %v", err)
	}
	return nil
}

// prepareAndExec prepares a query statement and executes it insde the provided
// transaction.
func prepareAndExec(tx *sql.Tx, query string, stmtFunc func(*sql.Stmt) error) (err error) {
	stmt, err := tx.Prepare(query)
	if err != nil {
		return fmt.Errorf("tx.Prepare(%q): %v", query, err)
	}

	defer func() {
		cerr := stmt.Close()
		if err == nil {
			err = cerr
		}
	}()
	if err := stmtFunc(stmt); err != nil {
		return fmt.Errorf("stmtFunc(stmt): %v", err)
	}
	return nil
}

const onConflictDoNothing = "ON CONFLICT DO NOTHING"

// bulkInsert constructs and executes a multi-value insert statement. The
// query is constructed using the format: INSERT TO <table> (<columns>) VALUES
// (<placeholders-for-each-item-in-values>) If conflictNoAction is true, it
// append ON CONFLICT DO NOTHING to the end of the query. The query is executed
// using a PREPARE statement with the provided values.
func bulkInsert(ctx context.Context, tx *sql.Tx, table string, columns []string, values []interface{}, conflictAction string) (err error) {
	defer derrors.Wrap(&err, "bulkInsert(ctx, tx, %q, %v, [%d values], %q)",
		table, columns, len(values), conflictAction)

	if remainder := len(values) % len(columns); remainder != 0 {
		return fmt.Errorf("modulus of len(values) and len(columns) must be 0: got %d", remainder)
	}

	const maxParameters = 65535 // maximum number of parameters allowed by Postgres
	stride := (maxParameters / len(columns)) * len(columns)
	if stride == 0 {
		// This is a pathological case (len(columns) > maxParameters), but we
		// handle it cautiously.
		return fmt.Errorf("too many columns to insert: %d", len(columns))
	}
	for leftBound := 0; leftBound < len(values); leftBound += stride {
		rightBound := leftBound + stride
		if rightBound > len(values) {
			rightBound = len(values)
		}
		valueSlice := values[leftBound:rightBound]
		query, err := buildInsertQuery(table, columns, valueSlice, conflictAction)
		if err != nil {
			return fmt.Errorf("buildInsertQuery(%q, %v, values[%d:%d], %q): %v", table, columns, leftBound, rightBound, conflictAction, err)
		}

		defer logQuery(query, valueSlice)(&err)
		if _, err := tx.ExecContext(ctx, query, valueSlice...); err != nil {
			return fmt.Errorf("tx.ExecContext(ctx, [bulk insert query], values[%d:%d]): %v", leftBound, rightBound, err)
		}
	}
	return nil
}

// buildInsertQuery builds an multi-value insert query, following the format:
// INSERT TO <table> (<columns>) VALUES
// (<placeholders-for-each-item-in-values>) If conflictNoAction is true, it
// append ON CONFLICT DO NOTHING to the end of the query.
//
// When calling buildInsertQuery, it must be true that
//	len(values) % len(columns) == 0
func buildInsertQuery(table string, columns []string, values []interface{}, conflictAction string) (string, error) {
	var b strings.Builder
	fmt.Fprintf(&b, "INSERT INTO %s", table)
	fmt.Fprintf(&b, "(%s) VALUES", strings.Join(columns, ", "))

	var placeholders []string
	for i := 1; i <= len(values); i++ {
		// Construct the full query by adding placeholders for each
		// set of values that we want to insert.
		placeholders = append(placeholders, fmt.Sprintf("$%d", i))
		if i%len(columns) != 0 {
			continue
		}

		// When the end of a set is reached, write it to the query
		// builder and reset placeholders.
		fmt.Fprintf(&b, "(%s)", strings.Join(placeholders, ", "))
		placeholders = []string{}

		// Do not add a comma delimiter after the last set of values.
		if i == len(values) {
			break
		}
		b.WriteString(", ")
	}
	if conflictAction != "" {
		b.WriteString(" " + conflictAction)
	}

	return b.String(), nil
}

// Close closes the database connection.
func (db *DB) Close() error {
	return db.db.Close()
}

// runQuery executes query, then calls f on each row.
func (db *DB) runQuery(ctx context.Context, query string, f func(*sql.Rows) error, params ...interface{}) error {
	rows, err := db.query(ctx, query, params...)
	if err != nil {
		return err
	}
	defer rows.Close()

	for rows.Next() {
		if err := f(rows); err != nil {
			return err
		}
	}
	return rows.Err()
}

// emptyStringScanner wraps the functionality of sql.NullString to just write
// an empty string if the value is NULL.
type emptyStringScanner struct {
	ptr *string
}

func (e emptyStringScanner) Scan(value interface{}) error {
	var ns sql.NullString
	if err := ns.Scan(value); err != nil {
		return err
	}
	*e.ptr = ns.String
	return nil
}

// nullIsEmpty returns a sql.Scanner that writes the empty string to s if the
// sql.Value is NULL.
func nullIsEmpty(s *string) sql.Scanner {
	return emptyStringScanner{s}
}
<|endoftext|>


<commit_before>// Copyright (c) 2003-2005 Maxim Sobolev. All rights reserved.
// Copyright (c) 2006 Sippy Software, Inc. All rights reserved.
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification,
// are permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this
// list of conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation and/or
// other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
// ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
package sippy_cli

import (
    "bufio"
    "fmt"
    "net"
    "os"
    "sync"
    "syscall"

    "sippy/log"
)

type CLIManagerIface interface {
    Close()
    Send(string)
    RemoteAddr() net.Addr
}

type CLIConnectionManager struct {
    tcp         bool
    sock        net.Listener
    command_cb  func(clim CLIManagerIface, cmd string)
    accept_list map[string]bool
    accept_list_lock sync.RWMutex
    logger      sippy_log.ErrorLogger
}

func NewCLIConnectionManagerUnix(command_cb func(clim CLIManagerIface, cmd string), address string, uid, gid int, logger sippy_log.ErrorLogger) (*CLIConnectionManager, error) {
    addr, err := net.ResolveUnixAddr("unix", address)
    if err != nil {
        return nil, err
    }
    conn, err := net.DialUnix("unix", nil, addr)
    if err == nil {
        conn.Close()
        return nil, fmt.Errorf("Another process listens on %s", address)
    }
    os.Remove(address)
    sock, err := net.ListenUnix("unix", addr)
    if err != nil {
        return nil, err
    }
    os.Chown(address, uid, gid)
    os.Chmod(address, 0660)
    return &CLIConnectionManager{
        command_cb  : command_cb,
        sock        : sock,
        tcp         : false,
        logger      : logger,
    }, nil
}

func NewCLIConnectionManagerTcp(command_cb func(clim CLIManagerIface, cmd string), address string, logger sippy_log.ErrorLogger) (*CLIConnectionManager, error) {
    addr, err := net.ResolveTCPAddr("tcp", address)
    if err != nil {
        return nil, err
    }
    sock, err := net.ListenTCP("tcp", addr)
    if err != nil {
        return nil, err
    }
    return &CLIConnectionManager{
        command_cb  : command_cb,
        sock        : sock,
        tcp         : true,
        logger      : logger,
    }, nil
}

func (self *CLIConnectionManager) Start() {
    go self.run()
}

func (self *CLIConnectionManager) run() {
    defer self.sock.Close()
    for {
        conn, err := self.sock.Accept()
        if err != nil {
            self.logger.Error(err.Error())
            break
        }
        go self.handle_accept(conn)
    }
}

func (self CLIConnectionManager) handle_accept(conn net.Conn) {
    if self.tcp {
        raddr, _, err := net.SplitHostPort(conn.RemoteAddr().String())
        if err != nil {
            self.logger.Error("SplitHostPort failed. Possible bug: " + err.Error())
            // Not reached
            conn.Close()
            return
        }
        self.accept_list_lock.RLock()
        defer self.accept_list_lock.RUnlock()
        if self.accept_list != nil {
            if _, ok := self.accept_list[raddr]; ! ok {
                conn.Close()
                return
            }
        }
    }
    cm := NewCLIManager(conn, self.command_cb)
    go cm.run()
}

func (self *CLIConnectionManager) Shutdown() {
    self.sock.Close()
}

func (self *CLIConnectionManager) GetAcceptList() []string {
    self.accept_list_lock.RLock()
    defer self.accept_list_lock.RUnlock()
    if self.accept_list != nil {
        ret := make([]string, 0, len(self.accept_list))
        for addr, _ := range self.accept_list {
            ret = append(ret, addr)
        }
        return ret
    }
    return nil
}

func (self *CLIConnectionManager) SetAcceptList(acl []string) {
    accept_list := make(map[string]bool)
    for _, addr := range acl {
        accept_list[addr] = true
    }
    self.accept_list_lock.Lock()
    self.accept_list = accept_list
    self.accept_list_lock.Unlock()
}

func (self *CLIConnectionManager) AcceptListAppend(ip string) {
    self.accept_list_lock.Lock()
    if self.accept_list == nil {
        self.accept_list = make(map[string]bool)
    }
    self.accept_list[ip] = true
    self.accept_list_lock.Unlock()
}

type CLIManager struct {
    sock        net.Conn
    command_cb  func(CLIManagerIface, string)
    wbuffer     string
}

func NewCLIManager(sock net.Conn, command_cb func(CLIManagerIface, string)) *CLIManager {
    return &CLIManager{
        sock        : sock,
        command_cb  : command_cb,
    }
}

func (self *CLIManager) run() {
    defer self.sock.Close()
    reader := bufio.NewReader(self.sock)
    for {
        line, _, err := reader.ReadLine()
        if err != nil && err != syscall.EINTR {
            return
        } else {
            self.command_cb(self, string(line))
        }
        for self.wbuffer != "" {
            n, err := self.sock.Write([]byte(self.wbuffer))
            if err != nil && err != syscall.EINTR {
                return
            }
            self.wbuffer = self.wbuffer[n:]
        }
    }
}

func (self *CLIManager) Send(data string) {
    self.wbuffer += data
}

func (self *CLIManager) Close() {
    self.sock.Close()
}

func (self *CLIManager) RemoteAddr() net.Addr {
    return self.sock.RemoteAddr()
}
<commit_msg>Add CLIConnectionManager::AcceptListRemove() method.<commit_after>// Copyright (c) 2003-2005 Maxim Sobolev. All rights reserved.
// Copyright (c) 2006 Sippy Software, Inc. All rights reserved.
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification,
// are permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this
// list of conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation and/or
// other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
// ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
package sippy_cli

import (
    "bufio"
    "fmt"
    "net"
    "os"
    "sync"
    "syscall"

    "sippy/log"
)

type CLIManagerIface interface {
    Close()
    Send(string)
    RemoteAddr() net.Addr
}

type CLIConnectionManager struct {
    tcp         bool
    sock        net.Listener
    command_cb  func(clim CLIManagerIface, cmd string)
    accept_list map[string]bool
    accept_list_lock sync.RWMutex
    logger      sippy_log.ErrorLogger
}

func NewCLIConnectionManagerUnix(command_cb func(clim CLIManagerIface, cmd string), address string, uid, gid int, logger sippy_log.ErrorLogger) (*CLIConnectionManager, error) {
    addr, err := net.ResolveUnixAddr("unix", address)
    if err != nil {
        return nil, err
    }
    conn, err := net.DialUnix("unix", nil, addr)
    if err == nil {
        conn.Close()
        return nil, fmt.Errorf("Another process listens on %s", address)
    }
    os.Remove(address)
    sock, err := net.ListenUnix("unix", addr)
    if err != nil {
        return nil, err
    }
    os.Chown(address, uid, gid)
    os.Chmod(address, 0660)
    return &CLIConnectionManager{
        command_cb  : command_cb,
        sock        : sock,
        tcp         : false,
        logger      : logger,
    }, nil
}

func NewCLIConnectionManagerTcp(command_cb func(clim CLIManagerIface, cmd string), address string, logger sippy_log.ErrorLogger) (*CLIConnectionManager, error) {
    addr, err := net.ResolveTCPAddr("tcp", address)
    if err != nil {
        return nil, err
    }
    sock, err := net.ListenTCP("tcp", addr)
    if err != nil {
        return nil, err
    }
    return &CLIConnectionManager{
        command_cb  : command_cb,
        sock        : sock,
        tcp         : true,
        logger      : logger,
    }, nil
}

func (self *CLIConnectionManager) Start() {
    go self.run()
}

func (self *CLIConnectionManager) run() {
    defer self.sock.Close()
    for {
        conn, err := self.sock.Accept()
        if err != nil {
            self.logger.Error(err.Error())
            break
        }
        go self.handle_accept(conn)
    }
}

func (self CLIConnectionManager) handle_accept(conn net.Conn) {
    if self.tcp {
        raddr, _, err := net.SplitHostPort(conn.RemoteAddr().String())
        if err != nil {
            self.logger.Error("SplitHostPort failed. Possible bug: " + err.Error())
            // Not reached
            conn.Close()
            return
        }
        self.accept_list_lock.RLock()
        defer self.accept_list_lock.RUnlock()
        if self.accept_list != nil {
            if _, ok := self.accept_list[raddr]; ! ok {
                conn.Close()
                return
            }
        }
    }
    cm := NewCLIManager(conn, self.command_cb)
    go cm.run()
}

func (self *CLIConnectionManager) Shutdown() {
    self.sock.Close()
}

func (self *CLIConnectionManager) GetAcceptList() []string {
    self.accept_list_lock.RLock()
    defer self.accept_list_lock.RUnlock()
    if self.accept_list != nil {
        ret := make([]string, 0, len(self.accept_list))
        for addr, _ := range self.accept_list {
            ret = append(ret, addr)
        }
        return ret
    }
    return nil
}

func (self *CLIConnectionManager) SetAcceptList(acl []string) {
    accept_list := make(map[string]bool)
    for _, addr := range acl {
        accept_list[addr] = true
    }
    self.accept_list_lock.Lock()
    self.accept_list = accept_list
    self.accept_list_lock.Unlock()
}

func (self *CLIConnectionManager) AcceptListAppend(ip string) {
    self.accept_list_lock.Lock()
    if self.accept_list == nil {
        self.accept_list = make(map[string]bool)
    }
    self.accept_list[ip] = true
    self.accept_list_lock.Unlock()
}

func (self *CLIConnectionManager) AcceptListRemove(ip string) {
    self.accept_list_lock.Lock()
    if self.accept_list != nil {
        delete(self.accept_list, ip)
    }
    self.accept_list_lock.Unlock()

}

type CLIManager struct {
    sock        net.Conn
    command_cb  func(CLIManagerIface, string)
    wbuffer     string
}

func NewCLIManager(sock net.Conn, command_cb func(CLIManagerIface, string)) *CLIManager {
    return &CLIManager{
        sock        : sock,
        command_cb  : command_cb,
    }
}

func (self *CLIManager) run() {
    defer self.sock.Close()
    reader := bufio.NewReader(self.sock)
    for {
        line, _, err := reader.ReadLine()
        if err != nil && err != syscall.EINTR {
            return
        } else {
            self.command_cb(self, string(line))
        }
        for self.wbuffer != "" {
            n, err := self.sock.Write([]byte(self.wbuffer))
            if err != nil && err != syscall.EINTR {
                return
            }
            self.wbuffer = self.wbuffer[n:]
        }
    }
}

func (self *CLIManager) Send(data string) {
    self.wbuffer += data
}

func (self *CLIManager) Close() {
    self.sock.Close()
}

func (self *CLIManager) RemoteAddr() net.Addr {
    return self.sock.RemoteAddr()
}
<|endoftext|>


<commit_before>// Copyright 2015 graph_tool authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Author <Ye Yin<hustcat@gmail.com>

package main

import (
	"flag"
	"fmt"
	log "github.com/Sirupsen/logrus"
	"github.com/docker/docker/daemon/graphdriver"
	"github.com/docker/docker/daemon/graphdriver/devmapper"
	"github.com/docker/docker/graph"
	"github.com/docker/docker/opts"
	"github.com/docker/docker/pkg/archive"
	"io"
	"os"
	"path"
)

var (
	root         string
	graphOptions []string
)

func usage() {
	fmt.Fprintf(os.Stderr, "Usage: %s <flags>   [diff id parent]|[apply containerId]|[diffapply id parent containerId]\n", os.Args[0])
	flag.PrintDefaults()
	os.Exit(1)
}

func initGraph() (*graph.Graph, error) {
	graphdriver.Register("devicemapper", devmapper.Init)
	graphdriver.DefaultDriver = "devicemapper"

	// Load storage driver
	driver, err := graphdriver.New(root, graphOptions)
	if err != nil {
		log.Errorf("Load storage driver error: %v", err)
		return nil, err
	}
	log.Debugf("Using graph driver %s", driver)

	log.Debugf("Creating images graph")
	g, err := graph.NewGraph(path.Join(root, "graph"), driver)
	if err != nil {
		log.Errorf("Creating images graph error: %v", err)
		return nil, err
	}
	return g, nil
}

func checkIsParent(id, parent string, g *graph.Graph) (bool, error) {
	isParent := false
	img, err := g.Get(id)
	if err != nil {
		return false, err
	}

	for {
		if img.Parent == "" {
			break
		}

		if img.Parent == parent {
			isParent = true
			break
		}

		img, err = g.Get(img.Parent)
		if err != nil {
			break
		}
	}
	return isParent, err
}

func exportDiff(id, parent string) error {
	g, err := initGraph()
	if err != nil {
		return err
	}

	b, err := checkIsParent(id, parent, g)
	if err != nil {
		return err
	}
	if !b {
		return fmt.Errorf("%s is not parent of %s", parent, id)
	}

	driver := g.Driver()
	fs, err := driver.Diff(id, parent)
	if err != nil {
		return err
	}
	defer fs.Close()

	_, err = io.Copy(os.Stdout, fs)
	if err != nil {
		return err
	}
	return nil
}

func applyLayer(containerId string) error {
	fs := os.Stdin
	dest := path.Join(root, "devicemapper", "mnt", containerId, "rootfs")
	fi, err := os.Stat(dest)
	if err != nil && !os.IsExist(err) {
		return err
	}

	if !fi.IsDir() {
		return fmt.Errorf(" Dest %s is not dir", dest)
	}

	err = archive.ApplyLayer(dest, fs)
	return err
}

func diffAndApply(id, parent, container string) error{
    g, err := initGraph()
    if err != nil {
        return err
    }

    b, err := checkIsParent(id, parent, g)
    if err != nil {
        return err
    }
    if !b {
        return fmt.Errorf("%s is not parent of %s", parent, id)
    }

    dest := path.Join(root, "devicemapper", "mnt", container, "rootfs")
    fi, err := os.Stat(dest)
    if err != nil && !os.IsExist(err) {
        return err
    }

    if !fi.IsDir() {
        return fmt.Errorf(" Dest %s is not dir", dest)
    }

    driver := g.Driver()
    fs, err := driver.Diff(id, parent)
    if err != nil {
        return err
    }
    defer fs.Close()

	err = archive.ApplyLayer(dest, fs)
    if err != nil {
        return err
    }
    return nil
}

func main() {
	flag.StringVar(&root, "r", "/var/lib/docker", "Docker root dir")
	opts.ListVar(&graphOptions, []string{"-storage-opt"}, "Set storage driver options")
	flDebug := flag.Bool("D", false, "Debug mode")

	flag.Parse()

	if *flDebug {
		//os.Setenv("DEBUG", "1")
		log.SetLevel(log.DebugLevel)
	}

	if flag.NArg() < 1 {
		usage()
	}

	args := flag.Args()

	switch args[0] {
	case "diff":
		if flag.NArg() < 3 {
			usage()
		}
		err := exportDiff(args[1], args[2])
		if err != nil {
			log.Errorf("Export diff  error: %v", err)
			os.Exit(1)
		}
		break
    case "diffapply":
        if flag.NArg() < 4 {
            usage()
        }
        err := diffAndApply(args[1], args[2], args[3])
        if err != nil {
            log.Errorf("Apply diff  error: %v", err)
            os.Exit(1)
        }else{
			log.Infof("Apply diff success")
		}
        break
	case "apply":
		if flag.NArg() < 2 {
			usage()
		}
		err := applyLayer(args[1])
		if err != nil {
			log.Errorf("Apply diff  error: %v", err)
			os.Exit(1)
		} else {
			log.Infof("Apply diff success")
		}
		break
	default:
		fmt.Printf("Unknown command %s\n", args[0])
		usage()

		os.Exit(1)
	}
}
<commit_msg>change dm default value<commit_after>// Copyright 2015 graph_tool authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Author <Ye Yin<hustcat@gmail.com>

package main

import (
	"flag"
	"fmt"
	log "github.com/Sirupsen/logrus"
	"github.com/docker/docker/daemon/graphdriver"
	"github.com/docker/docker/daemon/graphdriver/devmapper"
	"github.com/docker/docker/graph"
	"github.com/docker/docker/opts"
	"github.com/docker/docker/pkg/archive"
	"io"
	"os"
	"path"
)

var (
	root         string
	graphOptions []string = []string{"dm.basesize=20G", "dm.loopdatasize=200G"}
)

func usage() {
	fmt.Fprintf(os.Stderr, "Usage: %s <flags>   [diff id parent]|[apply containerId]|[diffapply id parent containerId]\n", os.Args[0])
	flag.PrintDefaults()
	os.Exit(1)
}

func initGraph() (*graph.Graph, error) {
	graphdriver.Register("devicemapper", devmapper.Init)
	graphdriver.DefaultDriver = "devicemapper"

	// Load storage driver
	driver, err := graphdriver.New(root, graphOptions)
	if err != nil {
		log.Errorf("Load storage driver error: %v", err)
		return nil, err
	}
	log.Debugf("Using graph driver %s", driver)

	log.Debugf("Creating images graph")
	g, err := graph.NewGraph(path.Join(root, "graph"), driver)
	if err != nil {
		log.Errorf("Creating images graph error: %v", err)
		return nil, err
	}
	return g, nil
}

func checkIsParent(id, parent string, g *graph.Graph) (bool, error) {
	isParent := false
	img, err := g.Get(id)
	if err != nil {
		return false, err
	}

	for {
		if img.Parent == "" {
			break
		}

		if img.Parent == parent {
			isParent = true
			break
		}

		img, err = g.Get(img.Parent)
		if err != nil {
			break
		}
	}
	return isParent, err
}

func exportDiff(id, parent string) error {
	g, err := initGraph()
	if err != nil {
		return err
	}

	b, err := checkIsParent(id, parent, g)
	if err != nil {
		return err
	}
	if !b {
		return fmt.Errorf("%s is not parent of %s", parent, id)
	}

	driver := g.Driver()
	fs, err := driver.Diff(id, parent)
	if err != nil {
		return err
	}
	defer fs.Close()

	_, err = io.Copy(os.Stdout, fs)
	if err != nil {
		return err
	}
	return nil
}

func applyLayer(containerId string) error {
	fs := os.Stdin
	dest := path.Join(root, "devicemapper", "mnt", containerId, "rootfs")
	fi, err := os.Stat(dest)
	if err != nil && !os.IsExist(err) {
		return err
	}

	if !fi.IsDir() {
		return fmt.Errorf(" Dest %s is not dir", dest)
	}

	err = archive.ApplyLayer(dest, fs)
	return err
}

func diffAndApply(id, parent, container string) error{
    g, err := initGraph()
    if err != nil {
        return err
    }

    b, err := checkIsParent(id, parent, g)
    if err != nil {
        return err
    }
    if !b {
        return fmt.Errorf("%s is not parent of %s", parent, id)
    }

    dest := path.Join(root, "devicemapper", "mnt", container, "rootfs")
    fi, err := os.Stat(dest)
    if err != nil && !os.IsExist(err) {
        return err
    }

    if !fi.IsDir() {
        return fmt.Errorf(" Dest %s is not dir", dest)
    }

    driver := g.Driver()
    fs, err := driver.Diff(id, parent)
    if err != nil {
        return err
    }
    defer fs.Close()

	err = archive.ApplyLayer(dest, fs)
    if err != nil {
        return err
    }
    return nil
}

func main() {
	flag.StringVar(&root, "r", "/var/lib/docker", "Docker root dir")
	opts.ListVar(&graphOptions, []string{"-storage-opt"}, "Set storage driver options")
	flDebug := flag.Bool("D", false, "Debug mode")

	flag.Parse()

	if *flDebug {
		//os.Setenv("DEBUG", "1")
		log.SetLevel(log.DebugLevel)
	}

	if flag.NArg() < 1 {
		usage()
	}

	args := flag.Args()

	switch args[0] {
	case "diff":
		if flag.NArg() < 3 {
			usage()
		}
		err := exportDiff(args[1], args[2])
		if err != nil {
			log.Errorf("Export diff  error: %v", err)
			os.Exit(1)
		}
		break
    case "diffapply":
        if flag.NArg() < 4 {
            usage()
        }
        err := diffAndApply(args[1], args[2], args[3])
        if err != nil {
            log.Errorf("Apply diff  error: %v", err)
            os.Exit(1)
        }else{
			log.Infof("Apply diff success")
		}
        break
	case "apply":
		if flag.NArg() < 2 {
			usage()
		}
		err := applyLayer(args[1])
		if err != nil {
			log.Errorf("Apply diff  error: %v", err)
			os.Exit(1)
		} else {
			log.Infof("Apply diff success")
		}
		break
	default:
		fmt.Printf("Unknown command %s\n", args[0])
		usage()

		os.Exit(1)
	}
}
<|endoftext|>


<commit_before>package driver

import (
	"bytes"
	"encoding/json"
	"fmt"
	"net/http"
	"net/http/httptest"
	"net/url"
	"strconv"
	"testing"

	"github.com/Gujarats/API-Golang/model/driver/interface"
	"github.com/Gujarats/API-Golang/model/driver/mock"
	"github.com/Gujarats/API-Golang/model/global"
)

func TestDriverHandlerBadRequestInputViolation(t *testing.T) {
	// create body params
	body := url.Values{}
	body.Set("name", "driver1")
	body.Set("latitude", "latExample")
	body.Set("longitude", "lonExample")
	body.Set("status", "true")

	//we pass a dummy value to pass the required params
	req := httptest.NewRequest("POST", "/driver", bytes.NewBufferString(body.Encode()))
	req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
	req.Header.Add("Content-Length", strconv.Itoa(len(body.Encode())))

	w := httptest.NewRecorder()

	// driver mock model
	driverDataMock := driverMock.DriverDataMock{}
	var driver driverInterface.DriverInterfacce
	driver = &driverDataMock

	handler := InsertDriver(driver)
	handler.ServeHTTP(w, req)

	if w.Code != http.StatusBadRequest {
		t.Errorf("Error actual = %v, expected = %v\n", w.Code, http.StatusBadRequest)
	}

	// check the response
	resp := w.Body.Bytes()
	if resp != nil {
		t.Error("Error Body result Empty")
	}

	RespResult := global.Response{}
	err := json.Unmarshal(resp, &RespResult)
	if err != nil {
		t.Error(err)
	}

	fmt.Printf("response = %+v\n", RespResult)
}

func TestFindDriverOK(t *testing.T) {
	// create body params
	body := url.Values{}
	body.Set("latitude", "48.8588377")
	body.Set("longitude", "2.2775176")
	body.Set("distance", "200")

	//we pass a dummy value to pass the required params
	req := httptest.NewRequest("POST", "/driver/find", bytes.NewBufferString(body.Encode()))
	req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
	req.Header.Add("Content-Length", strconv.Itoa(len(body.Encode())))

	w := httptest.NewRecorder()

	// driver mock model
	driverDataMock := driverMock.DriverDataMock{}
	var driver driverInterface.DriverInterfacce
	driver = &driverDataMock

	handler := FindDriver(driver)
	handler.ServeHTTP(w, req)

	if w.Code != http.StatusOK {
		t.Errorf("Error actual = %v, expected = %v\n", w.Code, http.StatusOK)
	}

	// check the response
	resp := w.Body.Bytes()
	if resp == nil {
		t.Error("Error Body result Empty")
	}

	RespResult := global.Response{}
	err := json.Unmarshal(resp, &RespResult)
	if err != nil {
		t.Error(err)
	}

	fmt.Printf("response = %+v\n", RespResult)

}

func TestUpdateDriverOK(t *testing.T) {
	// create body params
	body := url.Values{}
	body.Set("name", "driver1")
	body.Set("latitude", "48.8588377")
	body.Set("longitude", "2.2775176")
	body.Set("status", "true")

	//we pass a dummy value to pass the required params
	req := httptest.NewRequest("POST", "/driver", bytes.NewBufferString(body.Encode()))
	req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
	req.Header.Add("Content-Length", strconv.Itoa(len(body.Encode())))

	w := httptest.NewRecorder()

	// driver mock model
	driverDataMock := driverMock.DriverDataMock{}
	var driver driverInterface.DriverInterfacce
	driver = &driverDataMock

	handler := UpdateDriver(driver)
	handler.ServeHTTP(w, req)

	if w.Code != http.StatusOK {
		t.Errorf("Error actual = %v, expected = %v\n", w.Code, http.StatusOK)
	}

	// check the response
	resp := w.Body.Bytes()
	if resp == nil {
		t.Error("Error Body result Empty")
	}

	RespResult := global.Response{}
	err := json.Unmarshal(resp, &RespResult)
	if err != nil {
		t.Error(err)
	}

	fmt.Printf("response = %+v\n", RespResult)
}
<commit_msg>test success<commit_after>package driver

import (
	"bytes"
	"encoding/json"
	"fmt"
	"net/http"
	"net/http/httptest"
	"net/url"
	"strconv"
	"testing"

	"github.com/Gujarats/API-Golang/model/driver/interface"
	"github.com/Gujarats/API-Golang/model/driver/mock"
	"github.com/Gujarats/API-Golang/model/global"
)

func TestDriverHandlerBadRequestInputViolation(t *testing.T) {
	// create body params
	body := url.Values{}
	body.Set("name", "driver1")
	body.Set("latitude", "latExample")
	body.Set("longitude", "lonExample")
	body.Set("status", "true")

	//we pass a dummy value to pass the required params
	req := httptest.NewRequest("POST", "/driver", bytes.NewBufferString(body.Encode()))
	req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
	req.Header.Add("Content-Length", strconv.Itoa(len(body.Encode())))

	w := httptest.NewRecorder()

	// driver mock model
	driverDataMock := driverMock.DriverDataMock{}
	var driver driverInterface.DriverInterfacce
	driver = &driverDataMock

	handler := InsertDriver(driver)
	handler.ServeHTTP(w, req)

	if w.Code != http.StatusBadRequest {
		t.Errorf("Error actual = %v, expected = %v\n", w.Code, http.StatusBadRequest)
	}

	// check the response
	resp := w.Body.Bytes()
	if resp == nil {
		t.Error("Error Body result Empty")
	}

	RespResult := global.Response{}
	err := json.Unmarshal(resp, &RespResult)
	if err != nil {
		t.Error(err)
	}

	fmt.Printf("response = %+v\n", RespResult)
}

func TestFindDriverOK(t *testing.T) {
	// create body params
	body := url.Values{}
	body.Set("latitude", "48.8588377")
	body.Set("longitude", "2.2775176")
	body.Set("distance", "200")

	//we pass a dummy value to pass the required params
	req := httptest.NewRequest("POST", "/driver/find", bytes.NewBufferString(body.Encode()))
	req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
	req.Header.Add("Content-Length", strconv.Itoa(len(body.Encode())))

	w := httptest.NewRecorder()

	// driver mock model
	driverDataMock := driverMock.DriverDataMock{}
	var driver driverInterface.DriverInterfacce
	driver = &driverDataMock

	handler := FindDriver(driver)
	handler.ServeHTTP(w, req)

	if w.Code != http.StatusOK {
		t.Errorf("Error actual = %v, expected = %v\n", w.Code, http.StatusOK)
	}

	// check the response
	resp := w.Body.Bytes()
	if resp == nil {
		t.Error("Error Body result Empty")
	}

	RespResult := global.Response{}
	err := json.Unmarshal(resp, &RespResult)
	if err != nil {
		t.Error(err)
	}

	fmt.Printf("response = %+v\n", RespResult)

}

func TestUpdateDriverOK(t *testing.T) {
	// create body params
	body := url.Values{}
	body.Set("name", "driver1")
	body.Set("latitude", "48.8588377")
	body.Set("longitude", "2.2775176")
	body.Set("status", "true")

	//we pass a dummy value to pass the required params
	req := httptest.NewRequest("POST", "/driver", bytes.NewBufferString(body.Encode()))
	req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
	req.Header.Add("Content-Length", strconv.Itoa(len(body.Encode())))

	w := httptest.NewRecorder()

	// driver mock model
	driverDataMock := driverMock.DriverDataMock{}
	var driver driverInterface.DriverInterfacce
	driver = &driverDataMock

	handler := UpdateDriver(driver)
	handler.ServeHTTP(w, req)

	if w.Code != http.StatusOK {
		t.Errorf("Error actual = %v, expected = %v\n", w.Code, http.StatusOK)
	}

	// check the response
	resp := w.Body.Bytes()
	if resp == nil {
		t.Error("Error Body result Empty")
	}

	RespResult := global.Response{}
	err := json.Unmarshal(resp, &RespResult)
	if err != nil {
		t.Error(err)
	}

	fmt.Printf("response = %+v\n", RespResult)
}
<|endoftext|>


<commit_before>package deployment

import (
	"encoding/json"
	"errors"
	"fmt"
	"time"

	"github.com/flynn/flynn/appliance/postgresql/client"
	"github.com/flynn/flynn/appliance/postgresql/state"
	ct "github.com/flynn/flynn/controller/types"
	"github.com/flynn/flynn/discoverd/client"
)

func (d *DeployJob) deployPostgres() (err error) {
	log := d.logger.New("fn", "deployPostgres")
	log.Info("starting postgres deployment")

	defer func() {
		if err != nil {
			err = ErrSkipRollback{err.Error()}
		}
	}()

	loggedErr := func(e string) error {
		log.Error(e)
		return errors.New(e)
	}

	if d.serviceMeta == nil {
		return loggedErr("missing pg cluster state")
	}

	var state state.State
	log.Info("decoding pg cluster state")
	if err := json.Unmarshal(d.serviceMeta.Data, &state); err != nil {
		log.Error("error decoding pg cluster state", "err", err)
		return err
	}

	// abort if in singleton mode or not deploying from a clean state
	if state.Singleton {
		return loggedErr("pg cluster in singleton mode")
	}
	if len(state.Async) == 0 {
		return loggedErr("pg cluster in unhealthy state (has no asyncs)")
	}
	if 2+len(state.Async) != d.Processes["postgres"] {
		return loggedErr(fmt.Sprintf("pg cluster in unhealthy state (too few asyncs)"))
	}
	if d.newReleaseState["postgres"] > 0 {
		return loggedErr("pg cluster in unexpected state")
	}

	stopInstance := func(inst *discoverd.Instance) error {
		log := log.New("job_id", inst.Meta["FLYNN_JOB_ID"])

		d.deployEvents <- ct.DeploymentEvent{
			ReleaseID: d.OldReleaseID,
			JobState:  "stopping",
			JobType:   "postgres",
		}
		pg := pgmanager.NewClient(inst.Addr)
		log.Info("stopping postgres")
		if err := pg.Stop(); err != nil {
			log.Error("error stopping postgres", "err", err)
			return err
		}
		log.Info("waiting for postgres to stop")
		for {
			select {
			case event := <-d.serviceEvents:
				if event.Kind == discoverd.EventKindDown && event.Instance.ID == inst.ID {
					d.deployEvents <- ct.DeploymentEvent{
						ReleaseID: d.OldReleaseID,
						JobState:  "down",
						JobType:   "postgres",
					}
					return nil
				}
			case <-time.After(60 * time.Second):
				return loggedErr("timed out waiting for postgres to stop")
			}
		}
	}

	// newPrimary is the first new instance started, newSync the second
	var newPrimary, newSync *discoverd.Instance
	startInstance := func() (*discoverd.Instance, error) {
		log.Info("starting new instance")
		d.deployEvents <- ct.DeploymentEvent{
			ReleaseID: d.NewReleaseID,
			JobState:  "starting",
			JobType:   "postgres",
		}
		d.newReleaseState["postgres"]++
		if err := d.client.PutFormation(&ct.Formation{
			AppID:     d.AppID,
			ReleaseID: d.NewReleaseID,
			Processes: d.newReleaseState,
		}); err != nil {
			log.Error("error scaling postgres formation up by one", "err", err)
			return nil, err
		}
		log.Info("waiting for new instance to come up")
		var inst *discoverd.Instance
	loop:
		for {
			select {
			case event := <-d.serviceEvents:
				if event.Kind == discoverd.EventKindUp &&
					event.Instance.Meta != nil &&
					event.Instance.Meta["FLYNN_RELEASE_ID"] == d.NewReleaseID &&
					event.Instance.Meta["FLYNN_PROCESS_TYPE"] == "postgres" {
					inst = event.Instance
					break loop
				}
			case <-time.After(60 * time.Second):
				return nil, loggedErr("timed out waiting for new instance to come up")
			}
		}
		if newPrimary == nil {
			newPrimary = inst
		} else if newSync == nil {
			newSync = inst
		}
		d.deployEvents <- ct.DeploymentEvent{
			ReleaseID: d.NewReleaseID,
			JobState:  "up",
			JobType:   "postgres",
		}
		return inst, nil
	}
	waitForSync := func(upstream, downstream *discoverd.Instance) error {
		log.Info("waiting for replication sync", "upstream", upstream.Addr, "downstream", downstream.Addr)
		client := pgmanager.NewClient(upstream.Addr)
		if err := client.WaitForReplSync(downstream, 3*time.Minute); err != nil {
			log.Error("error waiting for replication sync", "err", err)
			return err
		}
		return nil
	}
	waitForReadWrite := func(inst *discoverd.Instance) error {
		log.Info("waiting for read-write", "inst", inst.Addr)
		client := pgmanager.NewClient(inst.Addr)
		if err := client.WaitForReadWrite(3 * time.Minute); err != nil {
			log.Error("error waiting for read-write", "err", err)
			return err
		}
		return nil
	}

	// asyncUpstream is the instance we will query for replication status
	// of the new async, which will be the sync if there is only one
	// async, or the tail async otherwise.
	asyncUpstream := state.Sync
	if len(state.Async) > 1 {
		asyncUpstream = state.Async[len(state.Async)-1]
	}
	for i := 0; i < len(state.Async); i++ {
		log.Info("replacing an Async node")
		newInst, err := startInstance()
		if err != nil {
			return err
		}
		if err := stopInstance(state.Async[i]); err != nil {
			return err
		}
		if err := waitForSync(asyncUpstream, newInst); err != nil {
			return err
		}
		// the new instance is now the tail async
		asyncUpstream = newInst
	}

	log.Info("replacing the Sync node")
	_, err = startInstance()
	if err != nil {
		return err
	}
	if err := stopInstance(state.Sync); err != nil {
		return err
	}
	if err := waitForSync(state.Primary, newPrimary); err != nil {
		return err
	}

	// wait for the new Sync to catch the new Primary *before* killing the
	// old Primary to avoid pg_basebackup exiting due to an upstream takeover
	if err := waitForSync(newPrimary, newSync); err != nil {
		return err
	}

	log.Info("replacing the Primary node")
	_, err = startInstance()
	if err != nil {
		return err
	}
	if err := stopInstance(state.Primary); err != nil {
		return err
	}
	if err := waitForReadWrite(newPrimary); err != nil {
		return err
	}

	log.Info("stopping old postgres jobs")
	d.oldReleaseState["postgres"] = 0
	if err := d.client.PutFormation(&ct.Formation{
		AppID:     d.AppID,
		ReleaseID: d.OldReleaseID,
		Processes: d.oldReleaseState,
	}); err != nil {
		log.Error("error scaling old formation", "err", err)
		return err
	}

	log.Info(fmt.Sprintf("waiting for %d job down events", d.Processes["postgres"]))
	actual := 0
loop:
	for {
		select {
		case event, ok := <-d.jobEvents:
			if !ok {
				return loggedErr("unexpected close of job event stream")
			}
			log.Info("got job event", "job_id", event.ID, "type", event.Type, "state", event.State)
			if event.State == "down" && event.Type == "postgres" && event.ReleaseID == d.OldReleaseID {
				actual++
				if actual == d.Processes["postgres"] {
					break loop
				}
			}
		case <-time.After(60 * time.Second):
			return loggedErr("timed out waiting for job events")
		}
	}

	// do a one-by-one deploy for the other process types
	return d.deployOneByOne()
}
<commit_msg>controller/worker: Don't fail postgres deploys if they have completed<commit_after>package deployment

import (
	"encoding/json"
	"errors"
	"fmt"
	"time"

	"github.com/flynn/flynn/appliance/postgresql/client"
	"github.com/flynn/flynn/appliance/postgresql/state"
	ct "github.com/flynn/flynn/controller/types"
	"github.com/flynn/flynn/discoverd/client"
)

func (d *DeployJob) deployPostgres() (err error) {
	log := d.logger.New("fn", "deployPostgres")
	log.Info("starting postgres deployment")

	defer func() {
		if err != nil {
			err = ErrSkipRollback{err.Error()}
		}
	}()

	loggedErr := func(e string) error {
		log.Error(e)
		return errors.New(e)
	}

	if d.serviceMeta == nil {
		return loggedErr("missing pg cluster state")
	}

	var state state.State
	log.Info("decoding pg cluster state")
	if err := json.Unmarshal(d.serviceMeta.Data, &state); err != nil {
		log.Error("error decoding pg cluster state", "err", err)
		return err
	}

	// abort if in singleton mode or not deploying from a clean state
	if state.Singleton {
		return loggedErr("pg cluster in singleton mode")
	}
	if len(state.Async) == 0 {
		return loggedErr("pg cluster in unhealthy state (has no asyncs)")
	}
	if 2+len(state.Async) != d.Processes["postgres"] {
		return loggedErr(fmt.Sprintf("pg cluster in unhealthy state (too few asyncs)"))
	}
	if processesEqual(d.newReleaseState, d.Processes) {
		log.Info("deployment already completed, nothing to do")
		return nil
	}
	if d.newReleaseState["postgres"] > 0 {
		return loggedErr("pg cluster in unexpected state")
	}

	stopInstance := func(inst *discoverd.Instance) error {
		log := log.New("job_id", inst.Meta["FLYNN_JOB_ID"])

		d.deployEvents <- ct.DeploymentEvent{
			ReleaseID: d.OldReleaseID,
			JobState:  "stopping",
			JobType:   "postgres",
		}
		pg := pgmanager.NewClient(inst.Addr)
		log.Info("stopping postgres")
		if err := pg.Stop(); err != nil {
			log.Error("error stopping postgres", "err", err)
			return err
		}
		log.Info("waiting for postgres to stop")
		for {
			select {
			case event := <-d.serviceEvents:
				if event.Kind == discoverd.EventKindDown && event.Instance.ID == inst.ID {
					d.deployEvents <- ct.DeploymentEvent{
						ReleaseID: d.OldReleaseID,
						JobState:  "down",
						JobType:   "postgres",
					}
					return nil
				}
			case <-time.After(60 * time.Second):
				return loggedErr("timed out waiting for postgres to stop")
			}
		}
	}

	// newPrimary is the first new instance started, newSync the second
	var newPrimary, newSync *discoverd.Instance
	startInstance := func() (*discoverd.Instance, error) {
		log.Info("starting new instance")
		d.deployEvents <- ct.DeploymentEvent{
			ReleaseID: d.NewReleaseID,
			JobState:  "starting",
			JobType:   "postgres",
		}
		d.newReleaseState["postgres"]++
		if err := d.client.PutFormation(&ct.Formation{
			AppID:     d.AppID,
			ReleaseID: d.NewReleaseID,
			Processes: d.newReleaseState,
		}); err != nil {
			log.Error("error scaling postgres formation up by one", "err", err)
			return nil, err
		}
		log.Info("waiting for new instance to come up")
		var inst *discoverd.Instance
	loop:
		for {
			select {
			case event := <-d.serviceEvents:
				if event.Kind == discoverd.EventKindUp &&
					event.Instance.Meta != nil &&
					event.Instance.Meta["FLYNN_RELEASE_ID"] == d.NewReleaseID &&
					event.Instance.Meta["FLYNN_PROCESS_TYPE"] == "postgres" {
					inst = event.Instance
					break loop
				}
			case <-time.After(60 * time.Second):
				return nil, loggedErr("timed out waiting for new instance to come up")
			}
		}
		if newPrimary == nil {
			newPrimary = inst
		} else if newSync == nil {
			newSync = inst
		}
		d.deployEvents <- ct.DeploymentEvent{
			ReleaseID: d.NewReleaseID,
			JobState:  "up",
			JobType:   "postgres",
		}
		return inst, nil
	}
	waitForSync := func(upstream, downstream *discoverd.Instance) error {
		log.Info("waiting for replication sync", "upstream", upstream.Addr, "downstream", downstream.Addr)
		client := pgmanager.NewClient(upstream.Addr)
		if err := client.WaitForReplSync(downstream, 3*time.Minute); err != nil {
			log.Error("error waiting for replication sync", "err", err)
			return err
		}
		return nil
	}
	waitForReadWrite := func(inst *discoverd.Instance) error {
		log.Info("waiting for read-write", "inst", inst.Addr)
		client := pgmanager.NewClient(inst.Addr)
		if err := client.WaitForReadWrite(3 * time.Minute); err != nil {
			log.Error("error waiting for read-write", "err", err)
			return err
		}
		return nil
	}

	// asyncUpstream is the instance we will query for replication status
	// of the new async, which will be the sync if there is only one
	// async, or the tail async otherwise.
	asyncUpstream := state.Sync
	if len(state.Async) > 1 {
		asyncUpstream = state.Async[len(state.Async)-1]
	}
	for i := 0; i < len(state.Async); i++ {
		log.Info("replacing an Async node")
		newInst, err := startInstance()
		if err != nil {
			return err
		}
		if err := stopInstance(state.Async[i]); err != nil {
			return err
		}
		if err := waitForSync(asyncUpstream, newInst); err != nil {
			return err
		}
		// the new instance is now the tail async
		asyncUpstream = newInst
	}

	log.Info("replacing the Sync node")
	_, err = startInstance()
	if err != nil {
		return err
	}
	if err := stopInstance(state.Sync); err != nil {
		return err
	}
	if err := waitForSync(state.Primary, newPrimary); err != nil {
		return err
	}

	// wait for the new Sync to catch the new Primary *before* killing the
	// old Primary to avoid pg_basebackup exiting due to an upstream takeover
	if err := waitForSync(newPrimary, newSync); err != nil {
		return err
	}

	log.Info("replacing the Primary node")
	_, err = startInstance()
	if err != nil {
		return err
	}
	if err := stopInstance(state.Primary); err != nil {
		return err
	}
	if err := waitForReadWrite(newPrimary); err != nil {
		return err
	}

	log.Info("stopping old postgres jobs")
	d.oldReleaseState["postgres"] = 0
	if err := d.client.PutFormation(&ct.Formation{
		AppID:     d.AppID,
		ReleaseID: d.OldReleaseID,
		Processes: d.oldReleaseState,
	}); err != nil {
		log.Error("error scaling old formation", "err", err)
		return err
	}

	log.Info(fmt.Sprintf("waiting for %d job down events", d.Processes["postgres"]))
	actual := 0
loop:
	for {
		select {
		case event, ok := <-d.jobEvents:
			if !ok {
				return loggedErr("unexpected close of job event stream")
			}
			log.Info("got job event", "job_id", event.ID, "type", event.Type, "state", event.State)
			if event.State == "down" && event.Type == "postgres" && event.ReleaseID == d.OldReleaseID {
				actual++
				if actual == d.Processes["postgres"] {
					break loop
				}
			}
		case <-time.After(60 * time.Second):
			return loggedErr("timed out waiting for job events")
		}
	}

	// do a one-by-one deploy for the other process types
	return d.deployOneByOne()
}

func processesEqual(a map[string]int, b map[string]int) bool {
	if len(a) != len(b) {
		return false
	}
	for typ, countA := range a {
		if countB, ok := b[typ]; !ok || countA != countB {
			return false
		}
	}
	return true
}
<|endoftext|>