<commit_before>package client

import (
	"errors"
	"io/ioutil"
	"os"
	"path/filepath"
	"testing"

	"github.com/keybase/client/go/libkb"
	"github.com/keybase/client/go/protocol/keybase1"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
	"golang.org/x/net/context"
)

/*
 file source cases:
 1. file
 2. dir

 file source/dest types:
 1. kbfs
 2. local

 file dest cases:
 1. file
 2. dir
 3. not found
*/

func TestSimpleFSPathRemote(t *testing.T) {
	tc := libkb.SetupTest(t, "simplefs_path", 0)

	testPath := makeSimpleFSPath(tc.G, "/keybase/private/foobar")
	pathType, err := testPath.PathType()
	require.NoError(tc.T, err, "bad path type")
	assert.Equal(tc.T, keybase1.PathType_KBFS, pathType, "Expected remote path, got local")
	assert.Equal(tc.T, "/private/foobar", testPath.Kbfs())

	testPath = makeSimpleFSPath(tc.G, "/keybase/private/")
	pathType, err = testPath.PathType()
	require.NoError(tc.T, err, "bad path type")
	assert.Equal(tc.T, keybase1.PathType_KBFS, pathType, "Expected remote path, got local")
	assert.Equal(tc.T, "/private", testPath.Kbfs())

}

func TestSimpleFSPathLocal(t *testing.T) {
	tc := libkb.SetupTest(t, "simplefs_path", 0)

	testPath := makeSimpleFSPath(tc.G, "./foobar")
	pathType, err := testPath.PathType()
	require.NoError(tc.T, err, "bad path type")
	assert.Equal(tc.T, keybase1.PathType_LOCAL, pathType, "Expected local path, got remote")
}

// CmdSimpleFSGetStatus is the 'fs get-status' command.
type SimpleFSTestStat struct {
	remoteExists bool
	localExists  bool
}

func (s SimpleFSTestStat) SimpleFSStat(_ context.Context, path keybase1.Path) (keybase1.Dirent, error) {
	pathString := pathToString(path)
	entType := keybase1.DirentType_DIR
	// For a quick test, assume it's a file if there is a dot and 3 chars at the end
	if len(filepath.Ext(filepath.Base(pathString))) == 4 {
		entType = keybase1.DirentType_FILE
	}
	pathType, _ := path.PathType()
	if (pathType == keybase1.PathType_KBFS && s.remoteExists == true) ||
		(pathType == keybase1.PathType_LOCAL && s.localExists == true) {
		return keybase1.Dirent{
			Name:       pathString,
			DirentType: entType,
		}, nil
	}
	return keybase1.Dirent{}, errors.New(pathString + " does not exist")
}

func TestSimpleFSLocalSrcFile(t *testing.T) {
	tc := libkb.SetupTest(t, "simplefs_path", 0)

	// make a temp local dest directory + files we will clean up later
	tempdir, err := ioutil.TempDir("", "simpleFStest")
	defer os.RemoveAll(tempdir)
	require.NoError(t, err)
	path1 := keybase1.NewPathWithLocal(filepath.Join(tempdir, "test1.txt"))
	err = ioutil.WriteFile(path1.Local(), []byte("foo"), 0644)
	require.NoError(t, err)

	isSrcDir, srcPathString, err := checkPathIsDir(context.TODO(), SimpleFSTestStat{localExists: true}, path1)
	require.NoError(tc.T, err, "bad path type")
	require.False(tc.T, isSrcDir)
	require.Equal(tc.T, path1.Local(), srcPathString)

	destPath := makeSimpleFSPath(tc.G, "/keybase/public/foobar")

	isDestDir, destPathString, err := checkPathIsDir(context.TODO(), SimpleFSTestStat{remoteExists: true}, destPath)
	require.NoError(tc.T, err, "bad path type")
	require.True(tc.T, isDestDir)
	require.Equal(tc.T, "/public/foobar", destPathString)

	destPath, err = makeDestPath(tc.G,
		context.TODO(),
		SimpleFSTestStat{},
		path1,
		destPath,
		true,
		"/public/foobar")
	assert.Equal(tc.T, "/public/foobar/test1.txt", destPath.Kbfs())
	require.NoError(tc.T, err, "bad path type")

	pathType, err := destPath.PathType()
	require.NoError(tc.T, err, "bad path type")
	assert.Equal(tc.T, keybase1.PathType_KBFS, pathType, "Expected remote path, got local")

	assert.Equal(tc.T, "/public/foobar/test1.txt", destPath.Kbfs())
}

func TestSimpleFSRemoteSrcFile(t *testing.T) {
	tc := libkb.SetupTest(t, "simplefs_path", 0)

	// make a temp local dest directory + files we will clean up later
	tempdir, err := ioutil.TempDir("", "simpleFstest")
	defer os.RemoveAll(tempdir)
	require.NoError(t, err)

	tempdir = filepath.ToSlash(tempdir)

	destPath := makeSimpleFSPath(tc.G, tempdir)
	srcPath := makeSimpleFSPath(tc.G, "/keybase/public/foobar/test1.txt")

	destPath, err = makeDestPath(tc.G,
		context.TODO(),
		SimpleFSTestStat{remoteExists: true},
		srcPath,
		destPath,
		true,
		tempdir)
	exists, err2 := libkb.FileExists(destPath.Local())
	tc.G.Log.Debug("makeDestPath fileExists %s: %v, %v", destPath.Local(), exists, err2)

	require.NoError(tc.T, err, "makeDestPath returns %s", pathToString(destPath))

	isSrcDir, srcPathString, err := checkPathIsDir(context.TODO(), SimpleFSTestStat{remoteExists: true}, srcPath)
	require.Equal(tc.T, "/public/foobar/test1.txt", srcPath.Kbfs())
	require.False(tc.T, isSrcDir)
	require.Equal(tc.T, "/public/foobar/test1.txt", srcPathString)
	require.Equal(tc.T, "test1.txt", filepath.Base(srcPathString))

	pathType, err := destPath.PathType()
	require.NoError(tc.T, err, "bad path type")
	assert.Equal(tc.T, keybase1.PathType_LOCAL, pathType, "Expected local path, got remote")

	assert.Equal(tc.T, filepath.ToSlash(filepath.Join(tempdir, "test1.txt")), destPath.Local())
}

func TestSimpleFSLocalSrcDir(t *testing.T) {
	tc := libkb.SetupTest(t, "simplefs_path", 0)

	// make a temp local dest directory + files we will clean up later
	tempdir, err := ioutil.TempDir("", "simpleFStest")
	defer os.RemoveAll(tempdir)
	require.NoError(t, err)
	path1 := keybase1.NewPathWithLocal(tempdir)
	require.NoError(t, err)
	testStatter := SimpleFSTestStat{localExists: true}

	isSrcDir, srcPathString, err := checkPathIsDir(context.TODO(), testStatter, path1)
	require.NoError(tc.T, err, "bad path type")
	require.True(tc.T, isSrcDir)
	require.Equal(tc.T, path1.Local(), srcPathString)

	destPathInitial := makeSimpleFSPath(tc.G, "/keybase/public/foobar")

	// Test when dest. exists.
	// We append the last element of the source in that case.
	testStatter.remoteExists = true
	isDestDir, destPathString, err := checkPathIsDir(context.TODO(), testStatter, destPathInitial)
	require.NoError(tc.T, err, "bad path type")
	require.True(tc.T, isDestDir)
	require.Equal(tc.T, "/public/foobar", destPathString)

	destPath, err := makeDestPath(tc.G,
		context.TODO(),
		testStatter,
		path1,
		destPathInitial,
		true,
		"/public/foobar")
	assert.Equal(tc.T, filepath.ToSlash(filepath.Join("/public/foobar", filepath.Base(tempdir))), destPath.Kbfs())
	assert.Equal(tc.T, err, TargetFileExistsError, "Expected that remote target path exists because of SimpleFSTestStat")
	//	require.NoError(tc.T, err, "bad path type")

	pathType, err := destPath.PathType()
	require.NoError(tc.T, err, "bad path type")
	assert.Equal(tc.T, keybase1.PathType_KBFS, pathType, "Expected remote path, got local")

	// Test when dest. does not exist.
	// Dest name should remain as-is in that case
	testStatter.localExists = false
	isDestDir, destPathString, err = checkPathIsDir(context.TODO(), testStatter, destPath)
	require.NoError(tc.T, err, "bad path type")
	require.True(tc.T, isDestDir)

	destPath, err = makeDestPath(tc.G,
		context.TODO(),
		SimpleFSTestStat{},
		path1,
		destPathInitial,
		true,
		"/public/foobar")
	assert.Equal(tc.T, "/public/foobar", destPath.Kbfs())
	require.NoError(tc.T, err, "bad path type")

	pathType, err = destPath.PathType()
	require.NoError(tc.T, err, "bad path type")
	assert.Equal(tc.T, keybase1.PathType_KBFS, pathType, "Expected remote path, got local")

}

func TestSimpleFSRemoteSrcDir(t *testing.T) {
	tc := libkb.SetupTest(t, "simplefs_path", 0)

	// make a temp local dest directory + files we will clean up later
	tempdir, err := ioutil.TempDir("", "simpleFStest")
	defer os.RemoveAll(tempdir)
	require.NoError(t, err)
	destPathInitial := keybase1.NewPathWithLocal(tempdir)
	require.NoError(t, err)
	testStatter := SimpleFSTestStat{remoteExists: true}

	srcPathInitial := makeSimpleFSPath(tc.G, "/keybase/public/foobar")

	isSrcDir, srcPathString, err := checkPathIsDir(context.TODO(), testStatter, srcPathInitial)
	require.NoError(tc.T, err, "bad path type")
	require.True(tc.T, isSrcDir)
	require.Equal(tc.T, srcPathInitial.Kbfs(), srcPathString)

	// Test when dest. exists.
	// We append the last element of the source in that case.
	testStatter.localExists = true
	isDestDir, destPathString, err := checkPathIsDir(context.TODO(), testStatter, destPathInitial)
	require.NoError(tc.T, err, "bad path type")
	require.True(tc.T, isDestDir)
	require.Equal(tc.T, tempdir, destPathString)

	destPath, err := makeDestPath(tc.G,
		context.TODO(),
		testStatter,
		srcPathInitial,
		destPathInitial,
		true,
		tempdir)
	assert.Equal(tc.T, filepath.ToSlash(filepath.Join(tempdir, "foobar")), destPath.Local())
	require.NoError(tc.T, err, "Expected that remote target path does not exist")

	pathType, err := destPath.PathType()
	require.NoError(tc.T, err, "bad path type")
	assert.Equal(tc.T, keybase1.PathType_LOCAL, pathType, "Expected local path, got remote")

	// Test when dest. does not exist by adding "foobar" on the end of the source.
	// Dest name should remain as-is in that case
	testStatter.localExists = false
	tempdir = filepath.Join(tempdir, "foobar")
	destPathInitial = keybase1.NewPathWithLocal(tempdir)
	isDestDir, destPathString, err = checkPathIsDir(context.TODO(), testStatter, destPathInitial)
	assert.Equal(tc.T, tempdir, destPathString, "should use dest dir as-is")

	destPath, err = makeDestPath(tc.G,
		context.TODO(),
		SimpleFSTestStat{},
		srcPathInitial,
		destPathInitial,
		true,
		tempdir)
	assert.Equal(tc.T, filepath.ToSlash(tempdir), destPath.Local())
	require.NoError(tc.T, err, "bad path type")

	pathType, err = destPath.PathType()
	require.NoError(tc.T, err, "bad path type")
	assert.Equal(tc.T, keybase1.PathType_LOCAL, pathType, "Expected remote path, got local")

}

func TestSimpleFSLocalExists(t *testing.T) {
	tc := libkb.SetupTest(t, "simplefs_path", 0)

	// make a temp local dest directory + files we will clean up later
	tempdir, err := ioutil.TempDir("", "simpleFstest")
	defer os.RemoveAll(tempdir)
	require.NoError(t, err)
	tempFile := filepath.Join(tempdir, "test1.txt")
	err = ioutil.WriteFile(tempFile, []byte("foo"), 0644)
	require.NoError(t, err)

	testPath := makeSimpleFSPath(tc.G, tempdir)
	pathType, err := testPath.PathType()
	require.NoError(tc.T, err, "bad path type")
	assert.Equal(tc.T, keybase1.PathType_LOCAL, pathType, "Expected local path, got remote")

	// check directory
	err = checkElementExists(context.TODO(), SimpleFSTestStat{}, testPath)
	require.Error(tc.T, err, "Should get an element exists error")

	// check file
	testPath = makeSimpleFSPath(tc.G, tempFile)
	err = checkElementExists(context.TODO(), SimpleFSTestStat{}, testPath)
	require.Error(tc.T, err, "Should get an element exists error")
}
<commit_msg>more SimpleFS CLI tests (#6169)<commit_after>package client

import (
	"errors"
	"io/ioutil"
	"os"
	"path/filepath"
	"testing"

	"github.com/keybase/client/go/libkb"
	"github.com/keybase/client/go/protocol/keybase1"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
	"golang.org/x/net/context"
)

/*
 file source cases:
 1. file
 2. dir

 file source/dest types:
 1. kbfs
 2. local

 file dest cases:
 1. file
 2. dir
 3. not found
*/

func TestSimpleFSPathRemote(t *testing.T) {
	tc := libkb.SetupTest(t, "simplefs_path", 0)

	testPath := makeSimpleFSPath(tc.G, "/keybase/private/foobar")
	pathType, err := testPath.PathType()
	require.NoError(tc.T, err, "bad path type")
	assert.Equal(tc.T, keybase1.PathType_KBFS, pathType, "Expected remote path, got local")
	assert.Equal(tc.T, "/private/foobar", testPath.Kbfs())

	testPath = makeSimpleFSPath(tc.G, "/keybase/private/")
	pathType, err = testPath.PathType()
	require.NoError(tc.T, err, "bad path type")
	assert.Equal(tc.T, keybase1.PathType_KBFS, pathType, "Expected remote path, got local")
	assert.Equal(tc.T, "/private", testPath.Kbfs())

}

func TestSimpleFSPathLocal(t *testing.T) {
	tc := libkb.SetupTest(t, "simplefs_path", 0)

	testPath := makeSimpleFSPath(tc.G, "./foobar")
	pathType, err := testPath.PathType()
	require.NoError(tc.T, err, "bad path type")
	assert.Equal(tc.T, keybase1.PathType_LOCAL, pathType, "Expected local path, got remote")
}

// CmdSimpleFSGetStatus is the 'fs get-status' command.
type SimpleFSTestStat struct {
	remoteExists bool
	localExists  bool
}

func (s SimpleFSTestStat) SimpleFSStat(_ context.Context, path keybase1.Path) (keybase1.Dirent, error) {
	pathString := pathToString(path)
	entType := keybase1.DirentType_DIR
	// For a quick test, assume it's a file if there is a dot and 3 chars at the end
	if len(filepath.Ext(filepath.Base(pathString))) == 4 {
		entType = keybase1.DirentType_FILE
	}
	pathType, _ := path.PathType()
	if (pathType == keybase1.PathType_KBFS && s.remoteExists == true) ||
		(pathType == keybase1.PathType_LOCAL && s.localExists == true) {
		return keybase1.Dirent{
			Name:       pathString,
			DirentType: entType,
		}, nil
	}
	return keybase1.Dirent{}, errors.New(pathString + " does not exist")
}

func TestSimpleFSLocalSrcFile(t *testing.T) {
	tc := libkb.SetupTest(t, "simplefs_path", 0)

	// make a temp local dest directory + files we will clean up later
	tempdir, err := ioutil.TempDir("", "simpleFStest")
	defer os.RemoveAll(tempdir)
	require.NoError(t, err)
	path1 := keybase1.NewPathWithLocal(filepath.Join(tempdir, "test1.txt"))
	err = ioutil.WriteFile(path1.Local(), []byte("foo"), 0644)
	require.NoError(t, err)

	isSrcDir, srcPathString, err := checkPathIsDir(context.TODO(), SimpleFSTestStat{localExists: true}, path1)
	require.NoError(tc.T, err, "bad path type")
	require.False(tc.T, isSrcDir)
	require.Equal(tc.T, path1.Local(), srcPathString)

	// Destination file not given
	destPath := makeSimpleFSPath(tc.G, "/keybase/public/foobar")

	isDestDir, destPathString, err := checkPathIsDir(context.TODO(), SimpleFSTestStat{remoteExists: true}, destPath)
	require.NoError(tc.T, err, "bad path type")
	require.True(tc.T, isDestDir)
	require.Equal(tc.T, "/public/foobar", destPathString)

	destPath, err = makeDestPath(tc.G,
		context.TODO(),
		SimpleFSTestStat{},
		path1,
		destPath,
		true,
		"/public/foobar")
	assert.Equal(tc.T, "/public/foobar/test1.txt", destPath.Kbfs())
	require.NoError(tc.T, err, "bad path type")

	pathType, err := destPath.PathType()
	require.NoError(tc.T, err, "bad path type")
	assert.Equal(tc.T, keybase1.PathType_KBFS, pathType, "Expected remote path, got local")

	assert.Equal(tc.T, "/public/foobar/test1.txt", destPath.Kbfs())

	// Destination file given
	destPath, err = makeDestPath(tc.G,
		context.TODO(),
		SimpleFSTestStat{},
		path1,
		destPath,
		true,
		"/public/foobar")
	assert.Equal(tc.T, "/public/foobar/test1.txt", destPath.Kbfs())
	require.NoError(tc.T, err, "bad path type")

	pathType, err = destPath.PathType()
	require.NoError(tc.T, err, "bad path type")
	assert.Equal(tc.T, keybase1.PathType_KBFS, pathType, "Expected remote path, got local")

	assert.Equal(tc.T, "/public/foobar/test1.txt", destPath.Kbfs())

}

func TestSimpleFSRemoteSrcFile(t *testing.T) {
	tc := libkb.SetupTest(t, "simplefs_path", 0)

	// make a temp local dest directory + files we will clean up later
	tempdir, err := ioutil.TempDir("", "simpleFstest")
	defer os.RemoveAll(tempdir)
	require.NoError(t, err)

	tempdir = filepath.ToSlash(tempdir)

	destPath := makeSimpleFSPath(tc.G, tempdir)
	srcPath := makeSimpleFSPath(tc.G, "/keybase/public/foobar/test1.txt")

	// Destination file not included in path
	destPath, err = makeDestPath(tc.G,
		context.TODO(),
		SimpleFSTestStat{remoteExists: true},
		srcPath,
		destPath,
		true,
		tempdir)
	exists, err2 := libkb.FileExists(destPath.Local())
	tc.G.Log.Debug("makeDestPath fileExists %s: %v, %v", destPath.Local(), exists, err2)

	require.NoError(tc.T, err, "makeDestPath returns %s", pathToString(destPath))

	isSrcDir, srcPathString, err := checkPathIsDir(context.TODO(), SimpleFSTestStat{remoteExists: true}, srcPath)
	require.Equal(tc.T, "/public/foobar/test1.txt", srcPath.Kbfs())
	require.False(tc.T, isSrcDir)
	require.Equal(tc.T, "/public/foobar/test1.txt", srcPathString)
	require.Equal(tc.T, "test1.txt", filepath.Base(srcPathString))

	pathType, err := destPath.PathType()
	require.NoError(tc.T, err, "bad path type")
	assert.Equal(tc.T, keybase1.PathType_LOCAL, pathType, "Expected local path, got remote")

	assert.Equal(tc.T, filepath.ToSlash(filepath.Join(tempdir, "test1.txt")), destPath.Local())

	// Dest file included in path
	destPath, err = makeDestPath(tc.G,
		context.TODO(),
		SimpleFSTestStat{remoteExists: true},
		srcPath,
		destPath,
		true,
		tempdir)

	require.NoError(tc.T, err, "makeDestPath returns %s", pathToString(destPath))

	pathType, err = destPath.PathType()
	require.NoError(tc.T, err, "bad path type")
	assert.Equal(tc.T, keybase1.PathType_LOCAL, pathType, "Expected local path, got remote")

	assert.Equal(tc.T, filepath.ToSlash(filepath.Join(tempdir, "test1.txt")), destPath.Local())
}

func TestSimpleFSLocalSrcDir(t *testing.T) {
	tc := libkb.SetupTest(t, "simplefs_path", 0)

	// make a temp local dest directory + files we will clean up later
	tempdir, err := ioutil.TempDir("", "simpleFStest")
	defer os.RemoveAll(tempdir)
	require.NoError(t, err)
	path1 := keybase1.NewPathWithLocal(tempdir)
	require.NoError(t, err)
	testStatter := SimpleFSTestStat{localExists: true}

	isSrcDir, srcPathString, err := checkPathIsDir(context.TODO(), testStatter, path1)
	require.NoError(tc.T, err, "bad path type")
	require.True(tc.T, isSrcDir)
	require.Equal(tc.T, path1.Local(), srcPathString)

	destPathInitial := makeSimpleFSPath(tc.G, "/keybase/public/foobar")

	// Test when dest. exists.
	// We append the last element of the source in that case.
	testStatter.remoteExists = true
	isDestDir, destPathString, err := checkPathIsDir(context.TODO(), testStatter, destPathInitial)
	require.NoError(tc.T, err, "bad path type")
	require.True(tc.T, isDestDir)
	require.Equal(tc.T, "/public/foobar", destPathString)

	destPath, err := makeDestPath(tc.G,
		context.TODO(),
		testStatter,
		path1,
		destPathInitial,
		true,
		"/public/foobar")
	assert.Equal(tc.T, filepath.ToSlash(filepath.Join("/public/foobar", filepath.Base(tempdir))), destPath.Kbfs())
	assert.Equal(tc.T, err, TargetFileExistsError, "Expected that remote target path exists because of SimpleFSTestStat")
	//	require.NoError(tc.T, err, "bad path type")

	pathType, err := destPath.PathType()
	require.NoError(tc.T, err, "bad path type")
	assert.Equal(tc.T, keybase1.PathType_KBFS, pathType, "Expected remote path, got local")

	// Test when dest. does not exist.
	// Dest name should remain as-is in that case
	testStatter.localExists = false
	isDestDir, destPathString, err = checkPathIsDir(context.TODO(), testStatter, destPath)
	require.NoError(tc.T, err, "bad path type")
	require.True(tc.T, isDestDir)

	destPath, err = makeDestPath(tc.G,
		context.TODO(),
		SimpleFSTestStat{},
		path1,
		destPathInitial,
		true,
		"/public/foobar")
	assert.Equal(tc.T, "/public/foobar", destPath.Kbfs())
	require.NoError(tc.T, err, "bad path type")

	pathType, err = destPath.PathType()
	require.NoError(tc.T, err, "bad path type")
	assert.Equal(tc.T, keybase1.PathType_KBFS, pathType, "Expected remote path, got local")

}

func TestSimpleFSRemoteSrcDir(t *testing.T) {
	tc := libkb.SetupTest(t, "simplefs_path", 0)

	// make a temp local dest directory + files we will clean up later
	tempdir, err := ioutil.TempDir("", "simpleFStest")
	defer os.RemoveAll(tempdir)
	require.NoError(t, err)
	destPathInitial := keybase1.NewPathWithLocal(tempdir)
	require.NoError(t, err)
	testStatter := SimpleFSTestStat{remoteExists: true}

	srcPathInitial := makeSimpleFSPath(tc.G, "/keybase/public/foobar")

	isSrcDir, srcPathString, err := checkPathIsDir(context.TODO(), testStatter, srcPathInitial)
	require.NoError(tc.T, err, "bad path type")
	require.True(tc.T, isSrcDir)
	require.Equal(tc.T, srcPathInitial.Kbfs(), srcPathString)

	// Test when dest. exists.
	// We append the last element of the source in that case.
	testStatter.localExists = true
	isDestDir, destPathString, err := checkPathIsDir(context.TODO(), testStatter, destPathInitial)
	require.NoError(tc.T, err, "bad path type")
	require.True(tc.T, isDestDir)
	require.Equal(tc.T, tempdir, destPathString)

	destPath, err := makeDestPath(tc.G,
		context.TODO(),
		testStatter,
		srcPathInitial,
		destPathInitial,
		true,
		tempdir)
	assert.Equal(tc.T, filepath.ToSlash(filepath.Join(tempdir, "foobar")), destPath.Local())
	require.NoError(tc.T, err, "Expected that remote target path does not exist")

	pathType, err := destPath.PathType()
	require.NoError(tc.T, err, "bad path type")
	assert.Equal(tc.T, keybase1.PathType_LOCAL, pathType, "Expected local path, got remote")

	// Test when dest. does not exist by adding "foobar" on the end of the source.
	// Dest name should remain as-is in that case
	testStatter.localExists = false
	tempdir = filepath.Join(tempdir, "foobar")
	destPathInitial = keybase1.NewPathWithLocal(tempdir)
	isDestDir, destPathString, err = checkPathIsDir(context.TODO(), testStatter, destPathInitial)
	assert.Equal(tc.T, tempdir, destPathString, "should use dest dir as-is")

	destPath, err = makeDestPath(tc.G,
		context.TODO(),
		SimpleFSTestStat{},
		srcPathInitial,
		destPathInitial,
		true,
		tempdir)
	assert.Equal(tc.T, filepath.ToSlash(tempdir), destPath.Local())
	require.NoError(tc.T, err, "bad path type")

	pathType, err = destPath.PathType()
	require.NoError(tc.T, err, "bad path type")
	assert.Equal(tc.T, keybase1.PathType_LOCAL, pathType, "Expected remote path, got local")

}

func TestSimpleFSLocalExists(t *testing.T) {
	tc := libkb.SetupTest(t, "simplefs_path", 0)

	// make a temp local dest directory + files we will clean up later
	tempdir, err := ioutil.TempDir("", "simpleFstest")
	defer os.RemoveAll(tempdir)
	require.NoError(t, err)
	tempFile := filepath.Join(tempdir, "test1.txt")
	err = ioutil.WriteFile(tempFile, []byte("foo"), 0644)
	require.NoError(t, err)

	testPath := makeSimpleFSPath(tc.G, tempdir)
	pathType, err := testPath.PathType()
	require.NoError(tc.T, err, "bad path type")
	assert.Equal(tc.T, keybase1.PathType_LOCAL, pathType, "Expected local path, got remote")

	// check directory
	err = checkElementExists(context.TODO(), SimpleFSTestStat{}, testPath)
	require.Error(tc.T, err, "Should get an element exists error")

	// check file
	testPath = makeSimpleFSPath(tc.G, tempFile)
	err = checkElementExists(context.TODO(), SimpleFSTestStat{}, testPath)
	require.Error(tc.T, err, "Should get an element exists error")
}
<|endoftext|>


<commit_before>/*
Copyright 2013 The Camlistore Authors

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 syncutil

import (
	"bytes"
	"fmt"
	"log"
	"runtime"
	"sync"
	"sync/atomic"
	"time"

	"camlistore.org/pkg/strutil"
)

// RWMutexTracker is a sync.RWMutex that tracks who owns the current
// exclusive lock.  It's used for debugging deadlocks.
type RWMutexTracker struct {
	mu sync.RWMutex

	// Atomic counters for number waiting and having read and write locks.
	nwaitr int32
	nwaitw int32
	nhaver int32
	nhavew int32 // should always be 0 or 1

	logOnce sync.Once

	hmu    sync.Mutex
	holder []byte
	holdr  map[int64]bool // goroutines holding read lock
}

const stackBufSize = 16 << 20

var stackBuf = make(chan []byte, 8)

func getBuf() []byte {
	select {
	case b := <-stackBuf:
		return b[:stackBufSize]
	default:
		return make([]byte, stackBufSize)
	}
}

func putBuf(b []byte) {
	select {
	case stackBuf <- b:
	default:
	}
}

var goroutineSpace = []byte("goroutine ")

func GoroutineID() int64 {
	b := getBuf()
	defer putBuf(b)
	b = b[:runtime.Stack(b, false)]
	// Parse the 4707 otu of "goroutine 4707 ["
	b = bytes.TrimPrefix(b, goroutineSpace)
	i := bytes.IndexByte(b, ' ')
	if i < 0 {
		panic(fmt.Sprintf("No space found in %q", b))
	}
	b = b[:i]
	n, err := strutil.ParseUintBytes(b, 10, 64)
	if err != nil {
		panic(fmt.Sprintf("Failed to parse goroutine ID out of %q: %v", b, err))
	}
	return int64(n)
}

func (m *RWMutexTracker) startLogger() {
	go func() {
		var buf bytes.Buffer
		for {
			time.Sleep(1 * time.Second)
			buf.Reset()
			m.hmu.Lock()
			for gid := range m.holdr {
				fmt.Fprintf(&buf, " [%d]", gid)
			}
			m.hmu.Unlock()
			log.Printf("Mutex %p: waitW %d haveW %d   waitR %d haveR %d %s",
				m,
				atomic.LoadInt32(&m.nwaitw),
				atomic.LoadInt32(&m.nhavew),
				atomic.LoadInt32(&m.nwaitr),
				atomic.LoadInt32(&m.nhaver), buf.Bytes())
		}
	}()
}

func (m *RWMutexTracker) Lock() {
	m.logOnce.Do(m.startLogger)
	atomic.AddInt32(&m.nwaitw, 1)
	m.mu.Lock()
	atomic.AddInt32(&m.nwaitw, -1)
	atomic.AddInt32(&m.nhavew, 1)

	m.hmu.Lock()
	if len(m.holder) == 0 {
		m.holder = make([]byte, stackBufSize)
	}
	m.holder = m.holder[:runtime.Stack(m.holder[:stackBufSize], false)]
	log.Printf("Lock at %s", string(m.holder))
	m.hmu.Unlock()
}

func (m *RWMutexTracker) Unlock() {
	m.hmu.Lock()
	m.holder = nil
	m.hmu.Unlock()

	atomic.AddInt32(&m.nhavew, -1)
	m.mu.Unlock()
}

func (m *RWMutexTracker) RLock() {
	m.logOnce.Do(m.startLogger)
	atomic.AddInt32(&m.nwaitr, 1)
	m.mu.RLock()
	atomic.AddInt32(&m.nwaitr, -1)
	atomic.AddInt32(&m.nhaver, 1)

	gid := GoroutineID()
	m.hmu.Lock()
	if m.holdr == nil {
		m.holdr = make(map[int64]bool)
	}
	if m.holdr[gid] {
		panic("Recursive call to RLock")
	}
	m.holdr[gid] = true
	m.hmu.Unlock()
}

func stack() []byte {
	buf := make([]byte, 1024)
	return buf[:runtime.Stack(buf, false)]
}

func (m *RWMutexTracker) RUnlock() {
	atomic.AddInt32(&m.nhaver, -1)

	gid := GoroutineID()
	m.hmu.Lock()
	delete(m.holdr, gid)
	m.hmu.Unlock()

	m.mu.RUnlock()
}

// Holder returns the stack trace of the current exclusive lock holder's stack
// when it acquired the lock (with Lock). It returns the empty string if the lock
// is not currently held.
func (m *RWMutexTracker) Holder() string {
	m.hmu.Lock()
	defer m.hmu.Unlock()
	return string(m.holder)
}
<commit_msg>syncutil: in RWMutexTracker, log.Fatal instead of panic on double-RLock<commit_after>/*
Copyright 2013 The Camlistore Authors

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 syncutil

import (
	"bytes"
	"fmt"
	"log"
	"runtime"
	"sync"
	"sync/atomic"
	"time"

	"camlistore.org/pkg/strutil"
)

// RWMutexTracker is a sync.RWMutex that tracks who owns the current
// exclusive lock.  It's used for debugging deadlocks.
type RWMutexTracker struct {
	mu sync.RWMutex

	// Atomic counters for number waiting and having read and write locks.
	nwaitr int32
	nwaitw int32
	nhaver int32
	nhavew int32 // should always be 0 or 1

	logOnce sync.Once

	hmu    sync.Mutex
	holder []byte
	holdr  map[int64]bool // goroutines holding read lock
}

const stackBufSize = 16 << 20

var stackBuf = make(chan []byte, 8)

func getBuf() []byte {
	select {
	case b := <-stackBuf:
		return b[:stackBufSize]
	default:
		return make([]byte, stackBufSize)
	}
}

func putBuf(b []byte) {
	select {
	case stackBuf <- b:
	default:
	}
}

var goroutineSpace = []byte("goroutine ")

func GoroutineID() int64 {
	b := getBuf()
	defer putBuf(b)
	b = b[:runtime.Stack(b, false)]
	// Parse the 4707 otu of "goroutine 4707 ["
	b = bytes.TrimPrefix(b, goroutineSpace)
	i := bytes.IndexByte(b, ' ')
	if i < 0 {
		panic(fmt.Sprintf("No space found in %q", b))
	}
	b = b[:i]
	n, err := strutil.ParseUintBytes(b, 10, 64)
	if err != nil {
		panic(fmt.Sprintf("Failed to parse goroutine ID out of %q: %v", b, err))
	}
	return int64(n)
}

func (m *RWMutexTracker) startLogger() {
	go func() {
		var buf bytes.Buffer
		for {
			time.Sleep(1 * time.Second)
			buf.Reset()
			m.hmu.Lock()
			for gid := range m.holdr {
				fmt.Fprintf(&buf, " [%d]", gid)
			}
			m.hmu.Unlock()
			log.Printf("Mutex %p: waitW %d haveW %d   waitR %d haveR %d %s",
				m,
				atomic.LoadInt32(&m.nwaitw),
				atomic.LoadInt32(&m.nhavew),
				atomic.LoadInt32(&m.nwaitr),
				atomic.LoadInt32(&m.nhaver), buf.Bytes())
		}
	}()
}

func (m *RWMutexTracker) Lock() {
	m.logOnce.Do(m.startLogger)
	atomic.AddInt32(&m.nwaitw, 1)
	m.mu.Lock()
	atomic.AddInt32(&m.nwaitw, -1)
	atomic.AddInt32(&m.nhavew, 1)

	m.hmu.Lock()
	defer m.hmu.Unlock()
	if len(m.holder) == 0 {
		m.holder = make([]byte, stackBufSize)
	}
	m.holder = m.holder[:runtime.Stack(m.holder[:stackBufSize], false)]
	log.Printf("Lock at %s", string(m.holder))
}

func (m *RWMutexTracker) Unlock() {
	m.hmu.Lock()
	m.holder = nil
	m.hmu.Unlock()

	atomic.AddInt32(&m.nhavew, -1)
	m.mu.Unlock()
}

func (m *RWMutexTracker) RLock() {
	m.logOnce.Do(m.startLogger)
	atomic.AddInt32(&m.nwaitr, 1)

	// Catch read-write-read lock. See if somebody (us? via
	// another goroutine?) already has a read lock, and then
	// somebody else is waiting to write, meaning our second read
	// will deadlock.
	if atomic.LoadInt32(&m.nhaver) > 0 && atomic.LoadInt32(&m.nwaitw) > 0 {
		buf := getBuf()
		buf = buf[:runtime.Stack(buf, false)]
		log.Printf("Potential R-W-R deadlock at: %s", buf)
		putBuf(buf)
	}

	m.mu.RLock()
	atomic.AddInt32(&m.nwaitr, -1)
	atomic.AddInt32(&m.nhaver, 1)

	gid := GoroutineID()
	m.hmu.Lock()
	defer m.hmu.Unlock()
	if m.holdr == nil {
		m.holdr = make(map[int64]bool)
	}
	if m.holdr[gid] {
		buf := getBuf()
		buf = buf[:runtime.Stack(buf, false)]
		log.Fatalf("Recursive call to RLock: %s", buf)
	}
	m.holdr[gid] = true
}

func stack() []byte {
	buf := make([]byte, 1024)
	return buf[:runtime.Stack(buf, false)]
}

func (m *RWMutexTracker) RUnlock() {
	atomic.AddInt32(&m.nhaver, -1)

	gid := GoroutineID()
	m.hmu.Lock()
	delete(m.holdr, gid)
	m.hmu.Unlock()

	m.mu.RUnlock()
}

// Holder returns the stack trace of the current exclusive lock holder's stack
// when it acquired the lock (with Lock). It returns the empty string if the lock
// is not currently held.
func (m *RWMutexTracker) Holder() string {
	m.hmu.Lock()
	defer m.hmu.Unlock()
	return string(m.holder)
}
<|endoftext|>


<commit_before>package design

import (
	"fmt"
	"net/url"
	"path"
	"sort"
	"strings"

	"github.com/dimfeld/httppath"
	"goa.design/goa/design"
	"goa.design/goa/eval"
)

type (
	// ServiceExpr describes a HTTP service. It defines both a result
	// type and a set of endpoints that can be executed through HTTP
	// requests. ServiceExpr embeds a ServiceExpr and adds HTTP specific
	// properties.
	ServiceExpr struct {
		eval.DSLFunc
		// ServiceExpr is the service expression that backs this
		// service.
		ServiceExpr *design.ServiceExpr
		// Common URL prefixes to all service endpoint HTTP requests
		Paths []string
		// Name of parent service if any
		ParentName string
		// Endpoint with canonical service path
		CanonicalEndpointName string
		// HTTPEndpoints is the list of service endpoints.
		HTTPEndpoints []*EndpointExpr
		// HTTPErrors lists HTTP errors that apply to all endpoints.
		HTTPErrors []*ErrorExpr
		// FileServers is the list of static asset serving endpoints
		FileServers []*FileServerExpr
		// Metadata is a set of key/value pairs with semantic that is
		// specific to each generator.
		Metadata design.MetadataExpr
		// params defines common request parameters to all the service
		// HTTP endpoints. The keys may use the "attribute:param" syntax
		// where "attribute" is the name of the attribute and "param"
		// the name of the HTTP parameter.
		params *design.AttributeExpr
		// headers defines common headers to all the service HTTP
		// endpoints. The keys may use the "attribute:header" syntax
		// where "attribute" is the name of the attribute and "header"
		// the name of the HTTP header.
		headers *design.AttributeExpr
	}
)

// Name of service (service)
func (svc *ServiceExpr) Name() string {
	return svc.ServiceExpr.Name
}

// Description of service (service)
func (svc *ServiceExpr) Description() string {
	return svc.ServiceExpr.Description
}

// Schemes returns the service endpoint HTTP schemes.
func (svc *ServiceExpr) Schemes() []string {
	schemes := make(map[string]bool)
	for _, s := range svc.ServiceExpr.Servers {
		if u, err := url.Parse(s.URL); err != nil {
			schemes[u.Scheme] = true
		}
	}
	if len(schemes) == 0 {
		return nil
	}
	ss := make([]string, len(schemes))
	i := 0
	for s := range schemes {
		ss[i] = s
		i++
	}
	sort.Strings(ss)
	return ss
}

// Error returns the error with the given name.
func (svc *ServiceExpr) Error(name string) *design.ErrorExpr {
	for _, erro := range svc.ServiceExpr.Errors {
		if erro.Name == name {
			return erro
		}
	}
	return Root.Design.Error(name)
}

// Endpoint returns the service endpoint with the given name or nil if there
// isn't one.
func (svc *ServiceExpr) Endpoint(name string) *EndpointExpr {
	for _, a := range svc.HTTPEndpoints {
		if a.Name() == name {
			return a
		}
	}
	return nil
}

// EndpointFor builds the endpoint for the given method.
func (svc *ServiceExpr) EndpointFor(name string, m *design.MethodExpr) *EndpointExpr {
	if a := svc.Endpoint(name); a != nil {
		return a
	}
	a := &EndpointExpr{
		MethodExpr: m,
		Service:    svc,
	}
	svc.HTTPEndpoints = append(svc.HTTPEndpoints, a)
	return a
}

// CanonicalEndpoint returns the canonical endpoint of the service if any.
// The canonical endpoint is used to compute hrefs to services.
func (svc *ServiceExpr) CanonicalEndpoint() *EndpointExpr {
	name := svc.CanonicalEndpointName
	if name == "" {
		name = "show"
	}
	return svc.Endpoint(name)
}

// URITemplate returns a URI template to this service.
// The result is the empty string if the service does not have a "show" endpoint
// and does not define a different canonical endpoint.
func (svc *ServiceExpr) URITemplate() string {
	ca := svc.CanonicalEndpoint()
	if ca == nil || len(ca.Routes) == 0 {
		return ""
	}
	return ca.Routes[0].FullPaths()[0]
}

// FullPaths computes the base paths to the service endpoints concatenating the
// API and parent service base paths as needed.
func (svc *ServiceExpr) FullPaths() []string {
	if len(svc.Paths) == 0 {
		return []string{path.Join(Root.Path)}
	}
	var paths []string
	for _, p := range svc.Paths {
		if strings.HasPrefix(p, "//") {
			paths = append(paths, httppath.Clean(p))
			continue
		}
		var basePaths []string
		if p := svc.Parent(); p != nil {
			if ca := p.CanonicalEndpoint(); ca != nil {
				if routes := ca.Routes; len(routes) > 0 {
					// Note: all these tests should be true at code
					// generation time as DSL validation makes sure
					// that parent services have a canonical path.
					fullPaths := routes[0].FullPaths()
					basePaths = make([]string, len(fullPaths))
					for i, p := range fullPaths {
						basePaths[i] = path.Join(p)
					}
				}
			}
		} else {
			basePaths = []string{Root.Path}
		}
		for _, base := range basePaths {
			paths = append(paths, httppath.Clean(path.Join(base, p)))
		}
	}
	return paths
}

// Parent returns the parent service if any, nil otherwise.
func (svc *ServiceExpr) Parent() *ServiceExpr {
	if svc.ParentName != "" {
		if parent := Root.Service(svc.ParentName); parent != nil {
			return parent
		}
	}
	return nil
}

// HTTPError returns the service HTTP error with given name if any.
func (svc *ServiceExpr) HTTPError(name string) *ErrorExpr {
	for _, erro := range svc.HTTPErrors {
		if erro.Name == name {
			return erro
		}
	}
	return nil
}

// Headers initializes and returns the attribute holding the API headers.
func (svc *ServiceExpr) Headers() *design.AttributeExpr {
	if svc.headers == nil {
		svc.headers = &design.AttributeExpr{Type: &design.Object{}}
	}
	return svc.headers
}

// MappedHeaders computes the mapped attribute expression from Headers.
func (svc *ServiceExpr) MappedHeaders() *design.MappedAttributeExpr {
	return design.NewMappedAttributeExpr(svc.headers)
}

// Params initializes and returns the attribute holding the API parameters.
func (svc *ServiceExpr) Params() *design.AttributeExpr {
	if svc.params == nil {
		svc.params = &design.AttributeExpr{Type: &design.Object{}}
	}
	return svc.params
}

// MappedParams computes the mapped attribute expression from Params.
func (svc *ServiceExpr) MappedParams() *design.MappedAttributeExpr {
	return design.NewMappedAttributeExpr(svc.params)
}

// EvalName returns the generic definition name used in error messages.
func (svc *ServiceExpr) EvalName() string {
	if svc.Name() == "" {
		return "unnamed service"
	}
	return fmt.Sprintf("service %#v", svc.Name())
}

// Validate makes sure the service is valid.
func (svc *ServiceExpr) Validate() error {
	verr := new(eval.ValidationErrors)
	if svc.params != nil {
		verr.Merge(svc.params.Validate("parameters", svc))
	}
	if svc.headers != nil {
		verr.Merge(svc.headers.Validate("headers", svc))
	}
	if n := svc.ParentName; n != "" {
		if p := Root.Service(n); p == nil {
			verr.Add(svc, "Parent service %s not found", n)
		} else {
			if p.CanonicalEndpoint() == nil {
				verr.Add(svc, "Parent service %s has no canonical endpoint", n)
			}
			if p.ParentName == svc.Name() {
				verr.Add(svc, "Parent service %s is also child", n)
			}
		}
	}
	if n := svc.CanonicalEndpointName; n != "" {
		if a := svc.Endpoint(n); a == nil {
			verr.Add(svc, "Unknown canonical endpoint %s", n)
		}
	}
	return verr
}
<commit_msg>Properly validate error responses defined in service and root. (#1688)<commit_after>package design

import (
	"fmt"
	"net/url"
	"path"
	"sort"
	"strings"

	"github.com/dimfeld/httppath"
	"goa.design/goa/design"
	"goa.design/goa/eval"
)

type (
	// ServiceExpr describes a HTTP service. It defines both a result
	// type and a set of endpoints that can be executed through HTTP
	// requests. ServiceExpr embeds a ServiceExpr and adds HTTP specific
	// properties.
	ServiceExpr struct {
		eval.DSLFunc
		// ServiceExpr is the service expression that backs this
		// service.
		ServiceExpr *design.ServiceExpr
		// Common URL prefixes to all service endpoint HTTP requests
		Paths []string
		// Name of parent service if any
		ParentName string
		// Endpoint with canonical service path
		CanonicalEndpointName string
		// HTTPEndpoints is the list of service endpoints.
		HTTPEndpoints []*EndpointExpr
		// HTTPErrors lists HTTP errors that apply to all endpoints.
		HTTPErrors []*ErrorExpr
		// FileServers is the list of static asset serving endpoints
		FileServers []*FileServerExpr
		// Metadata is a set of key/value pairs with semantic that is
		// specific to each generator.
		Metadata design.MetadataExpr
		// params defines common request parameters to all the service
		// HTTP endpoints. The keys may use the "attribute:param" syntax
		// where "attribute" is the name of the attribute and "param"
		// the name of the HTTP parameter.
		params *design.AttributeExpr
		// headers defines common headers to all the service HTTP
		// endpoints. The keys may use the "attribute:header" syntax
		// where "attribute" is the name of the attribute and "header"
		// the name of the HTTP header.
		headers *design.AttributeExpr
	}
)

// Name of service (service)
func (svc *ServiceExpr) Name() string {
	return svc.ServiceExpr.Name
}

// Description of service (service)
func (svc *ServiceExpr) Description() string {
	return svc.ServiceExpr.Description
}

// Schemes returns the service endpoint HTTP schemes.
func (svc *ServiceExpr) Schemes() []string {
	schemes := make(map[string]bool)
	for _, s := range svc.ServiceExpr.Servers {
		if u, err := url.Parse(s.URL); err != nil {
			schemes[u.Scheme] = true
		}
	}
	if len(schemes) == 0 {
		return nil
	}
	ss := make([]string, len(schemes))
	i := 0
	for s := range schemes {
		ss[i] = s
		i++
	}
	sort.Strings(ss)
	return ss
}

// Error returns the error with the given name.
func (svc *ServiceExpr) Error(name string) *design.ErrorExpr {
	for _, erro := range svc.ServiceExpr.Errors {
		if erro.Name == name {
			return erro
		}
	}
	return Root.Design.Error(name)
}

// Endpoint returns the service endpoint with the given name or nil if there
// isn't one.
func (svc *ServiceExpr) Endpoint(name string) *EndpointExpr {
	for _, a := range svc.HTTPEndpoints {
		if a.Name() == name {
			return a
		}
	}
	return nil
}

// EndpointFor builds the endpoint for the given method.
func (svc *ServiceExpr) EndpointFor(name string, m *design.MethodExpr) *EndpointExpr {
	if a := svc.Endpoint(name); a != nil {
		return a
	}
	a := &EndpointExpr{
		MethodExpr: m,
		Service:    svc,
	}
	svc.HTTPEndpoints = append(svc.HTTPEndpoints, a)
	return a
}

// CanonicalEndpoint returns the canonical endpoint of the service if any.
// The canonical endpoint is used to compute hrefs to services.
func (svc *ServiceExpr) CanonicalEndpoint() *EndpointExpr {
	name := svc.CanonicalEndpointName
	if name == "" {
		name = "show"
	}
	return svc.Endpoint(name)
}

// URITemplate returns a URI template to this service.
// The result is the empty string if the service does not have a "show" endpoint
// and does not define a different canonical endpoint.
func (svc *ServiceExpr) URITemplate() string {
	ca := svc.CanonicalEndpoint()
	if ca == nil || len(ca.Routes) == 0 {
		return ""
	}
	return ca.Routes[0].FullPaths()[0]
}

// FullPaths computes the base paths to the service endpoints concatenating the
// API and parent service base paths as needed.
func (svc *ServiceExpr) FullPaths() []string {
	if len(svc.Paths) == 0 {
		return []string{path.Join(Root.Path)}
	}
	var paths []string
	for _, p := range svc.Paths {
		if strings.HasPrefix(p, "//") {
			paths = append(paths, httppath.Clean(p))
			continue
		}
		var basePaths []string
		if p := svc.Parent(); p != nil {
			if ca := p.CanonicalEndpoint(); ca != nil {
				if routes := ca.Routes; len(routes) > 0 {
					// Note: all these tests should be true at code
					// generation time as DSL validation makes sure
					// that parent services have a canonical path.
					fullPaths := routes[0].FullPaths()
					basePaths = make([]string, len(fullPaths))
					for i, p := range fullPaths {
						basePaths[i] = path.Join(p)
					}
				}
			}
		} else {
			basePaths = []string{Root.Path}
		}
		for _, base := range basePaths {
			paths = append(paths, httppath.Clean(path.Join(base, p)))
		}
	}
	return paths
}

// Parent returns the parent service if any, nil otherwise.
func (svc *ServiceExpr) Parent() *ServiceExpr {
	if svc.ParentName != "" {
		if parent := Root.Service(svc.ParentName); parent != nil {
			return parent
		}
	}
	return nil
}

// HTTPError returns the service HTTP error with given name if any.
func (svc *ServiceExpr) HTTPError(name string) *ErrorExpr {
	for _, erro := range svc.HTTPErrors {
		if erro.Name == name {
			return erro
		}
	}
	return nil
}

// Headers initializes and returns the attribute holding the API headers.
func (svc *ServiceExpr) Headers() *design.AttributeExpr {
	if svc.headers == nil {
		svc.headers = &design.AttributeExpr{Type: &design.Object{}}
	}
	return svc.headers
}

// MappedHeaders computes the mapped attribute expression from Headers.
func (svc *ServiceExpr) MappedHeaders() *design.MappedAttributeExpr {
	return design.NewMappedAttributeExpr(svc.headers)
}

// Params initializes and returns the attribute holding the API parameters.
func (svc *ServiceExpr) Params() *design.AttributeExpr {
	if svc.params == nil {
		svc.params = &design.AttributeExpr{Type: &design.Object{}}
	}
	return svc.params
}

// MappedParams computes the mapped attribute expression from Params.
func (svc *ServiceExpr) MappedParams() *design.MappedAttributeExpr {
	return design.NewMappedAttributeExpr(svc.params)
}

// EvalName returns the generic definition name used in error messages.
func (svc *ServiceExpr) EvalName() string {
	if svc.Name() == "" {
		return "unnamed service"
	}
	return fmt.Sprintf("service %#v", svc.Name())
}

// Validate makes sure the service is valid.
func (svc *ServiceExpr) Validate() error {
	verr := new(eval.ValidationErrors)
	if svc.params != nil {
		verr.Merge(svc.params.Validate("parameters", svc))
	}
	if svc.headers != nil {
		verr.Merge(svc.headers.Validate("headers", svc))
	}
	if n := svc.ParentName; n != "" {
		if p := Root.Service(n); p == nil {
			verr.Add(svc, "Parent service %s not found", n)
		} else {
			if p.CanonicalEndpoint() == nil {
				verr.Add(svc, "Parent service %s has no canonical endpoint", n)
			}
			if p.ParentName == svc.Name() {
				verr.Add(svc, "Parent service %s is also child", n)
			}
		}
	}
	if n := svc.CanonicalEndpointName; n != "" {
		if a := svc.Endpoint(n); a == nil {
			verr.Add(svc, "Unknown canonical endpoint %s", n)
		}
	}

	// Validate errors (have status codes and bodies are valid)
	for _, er := range svc.HTTPErrors {
		verr.Merge(er.Validate())
	}
	for _, er := range Root.HTTPErrors {
		// This may result in the same error being validated multiple
		// times however service is the top level expression being
		// walked and errors cannot be walked until all expressions have
		// run. Another solution could be to append a new dynamically
		// generated root that the eval engine would process after. Keep
		// things simple for now.
		verr.Merge(er.Validate())
	}

	return verr
}
<|endoftext|>


<commit_before>// +build cit

package imagevalidation

import (
	"fmt"
	"io/ioutil"
	"path/filepath"
	"regexp"
	"testing"
)

var licenseNames = []string{
	"Apache License",
	"Artistic/GPL",
	"Artistic",
	"Autoconf",
	"\"Bitstream Vera\"",
	"BSD",
	"BZIP",
	"COMMON PUBLIC LICENSE VERSION 1.0",
	"config-h",
	"curl",
	"Expat",
	"GAP",
	"GD",
	"GFDL-1.3+",
	"GNU General Public License",
	"GNU GPL",
	"GNU LGPL",
	"GNU Lesser Public License",
	"GPL",
	"HPND",
	"IBM PUBLIC LICENSE VERSION 1.0",
	"ISC",
	"JPEG",
	"LGPL",
	"MIT",
	"MIT license",
	"MIT/X11 (BSD like)",
	"no notice",
	"noderivs",
	"none",
	"PD",
	"PD-debian",
	"PERLDOCS",
	"permissive-fsf",
	"permissive-nowarranty",
	"probably-PD",
	"Paul Vixie\"s license",
	"public-domain",
	"REGCOMP",
	"S2P",
	"SDBM-PUBLIC-DOMAIN",
	"SIL Open Font License",
	"TEXT-SOUNDEX",
	"TEXT-TABS",
	"The OpenLDAP Public License",
	"Unicode",
	"X11",
	"X11-2",
	"ZLIB",
}

var licenses = []string{
	`Permission to use, copy, modify, distribute, and sell this software and its documentation for any purpose is hereby granted without fee, provided that the above copyright notice appear in all copies and that both that copyright notice and this permission notice appear in supporting documentation, and that the name of the authors not be used in advertising or publicity pertaining to distribution of the software without specific, written prior permission. The authors makes no representations about the suitability of this software for any purpose. It is provided "as is" without express or implied warranty.`,
	`free software; you can redistribute it and/or modify it under the terms of the GNU.*General Public License.*as published by the Free Software Foundation`,
	`The main library is licensed under GNU Lesser General Public License (LGPL) version 2.1+, Gnutls Extra (i.e. openssl wrapper library, and library for code for "GnuTLS Inner Application" support) build system, testsuite and commandline utilities are licenced under the GNU General Public License version 3+. The Guile bindings use the same license as the respective underlying library, i.e. LGPLv2.1+ for the main library and GPLv3+ for Gnutls extra.`,
	`Permission is granted to anyone to use this.*for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions`,
	`This software is released under the terms of the GNU.*General Public License.*`,
	`All files in this package can be freely distributed and used according to the terms of the GNU.*General Public License, either version 2 or (at your opinion) any newer version. This is the same distribution policy as for the Linux kernel itself -- see /usr/src/linux/COPYING for details.`,
	`You are free to distribute this.*under the terms of the BSD License`,
	`All files in this.*can be freely distributed and used according to the terms of the GNU General Public License`,
	`all of the code is covered under the terms of the GPL.`,
	`is free software`,
	`You are free to distribute this software under the terms of the BSD License.`,
	`is licensed under the BSD license`,
	`(is|are|be) free to distribute`,
	`may freely distribute`,
	`(is|are|be) freely distributed`,
	`.*is available under the terms of the GNU.*Public License`,
	`This data is licenced under 2 different licenses 1\) GNU General Public License, version 2 or later 2\) XFree86 1.0 license This data can be used freely under either license.`,
	`.*is in the public domain.`,
	`is covered under the terms of the GNU Public License.`,
	`redistribute it freely`,
	`the complete text of the GNU General Public License can be found in`,
	`free for commercial and non-commercial use as long as the following conditions are aheared to`,
	`Permission to.*use.*distribute.*this.*for any purpose.*is.*granted`,
	`There are no restrictions on distributing unmodified copies of Vim except that they must include this license text.`,
	`Redistribution and use.*(is|are) permitted`,
	`Permission is.*granted.*deal.*without restriction, including without limitation the rights to use`,
	`All its programs.*may be redistributed under the terms of the GNU GPL, Version 2 or later`,
	`is distributed under the GNU.*General Public License`,
	`This software is distributed under the GNU General Public License`,
	`This package is dual-licensed under the Academic Free License version 2.1, and the GPL version 2.`,
	`may be used, modified and redistributed only under the terms of the GNU General Public License`,
	`has been placed in the public domain`,
	`And licensed under the terms of the GPL license`,
	`are distributed under the terms of the GNU.*General Public License`,
	`The keys in the keyrings don\'t fall under any copyright. Everything else in the package is covered by the GNU GPL.`,
	`the complete text of the GNU General Public License and of the GNU Lesser Public License can be found in`,
	`THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS IBM PUBLIC LICENSE`,
	`THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS COMMON PUBLIC LICENSE`,
	`GNU LESSER GENERAL PUBLIC LICENSE`,
	`Permission is hereby granted.*to any person obtaining a copy of.*and associated documentation files.*to deal in.*without restriction`,
	`Redistribution and use of this software and associated documentation ("Software"), with or without modification, are permitted`,
	`This code is multi Licensed under all/any one of.*LGPLv2.*New Style BSD.*MIT`,
	`LICENSE. You may copy and use the Software, subject to these conditions: 1. This Software is licensed for use only in conjunction with Intel component products. Use of the Software in conjunction with non-Intel component products is not licensed hereunder.`,
	`Brocade Linux Fibre Channel HBA Firmware`,
	`QLogic Linux Fibre Channel HBA Firmware`,
	`Unlimited distribution and/or modification is allowed as long as this copyright notice remains intact.`,
	`Permission is hereby granted to use.*this.*for any purpose`,
	`are in the public domain`,
	`is (available|distributed) under the terms of the GNU.*Public License`,
	`(libudev|libgudev|udev) is licensed under the GNU (L|)GPL`,
	`The Linux Console Tools are covered by the GPL`,
	`Some portions of os-prober`,
	`Netcat and the associated package is a product of Avian Research, and is freely available in full source form with no restrictions save an obligation to give credit where due.`,
	`Permission is hereby granted, without written agreement and without licence or royalty fees, to use, copy, modify, and distribute this software`,
	`Open Market permits you to use, copy, modify, distribute, and license this Software and the Documentation for any purpose, provided that existing copyright notices are retained in all copies and that this notice is included verbatim in any distributions. No written agreement, license, or royalty fee is required for any of the authorized uses.`,
	`This software is made available under the terms of *either* of the licenses found in LICENSE.APACHE or LICENSE.BSD.`,
	`This library.*is public domain software`,
	`Apache License`,
	`The Artistic License 2.0`,
	`DO WHAT THE HELL YOU WANT TO`, // Yes, this is a real license.
}

const (
	licenseNameRegex = `(?i)(((License|Copyright)\s*:\s*%[1]s)|((covered )?under (the )?%[1]s)|(under (the terms of )?the %[1]s))`
)

var licenseGlobs = []string{
	"/usr/share/doc/*/copyright",
	"/usr/share/doc/*/LICENSE",
	"/usr/share/licenses/*/LICENSE",
}

func isValidLicenseName(licenseCheck string) bool {
	for _, name := range licenseNames {
		var regexString = fmt.Sprintf(licenseNameRegex, name)
		re, err := regexp.Compile(regexString)
		if err != nil {
			return false
		}
		if re.MatchString(licenseCheck) {
			return true
		}
	}
	return false
}

func isValidLicenseText(licenseCheck string) bool {
	for _, licenseText := range licenses {
		// (?i) case insensitive
		re, err := regexp.Compile(`(?i)` + licenseText)
		if err != nil {
			return false
		}

		if re.MatchString(licenseCheck) {
			return true
		}
	}
	return false
}

func TestArePackagesLegal(t *testing.T) {
	for _, pathGlob := range licenseGlobs {
		filenames, err := filepath.Glob(pathGlob)
		if err != nil || len(filenames) == 0 {
			t.Logf("couldnt resolve glob %s", pathGlob)
			continue
		}

		for _, filename := range filenames {
			isLegal, err := isPackageLegal(filename)
			if err != nil {
				t.Fatalf(err.Error())
			}
			if !isLegal {
				t.Errorf("Found illegal package: %v", filename)
			}
		}
	}
}

func isPackageLegal(filepath string) (bool, error) {
	bytes, err := ioutil.ReadFile(filepath)
	if err != nil {
		return false, fmt.Errorf("error reading file: %v", err)
	}

	var licenseCheck string = string(bytes)

	// Remove comments.
	commentRegex, err := regexp.Compile(`\*|#`)
	if err != nil {
		return false, fmt.Errorf("invalid regular expression: %v", err)
	}
	licenseCheck = commentRegex.ReplaceAllString(licenseCheck, "")

	// Replace repeated whitespace and newlines with one space.
	whitespaceRegex, err := regexp.Compile(`\s+`)
	if err != nil {
		return false, fmt.Errorf("invalid regular expression: %v", err)
	}
	licenseCheck = whitespaceRegex.ReplaceAllString(licenseCheck, " ")
	if isValidLicenseName(licenseCheck) || isValidLicenseText(licenseCheck) {
		return true, nil
	}

	return false, nil
}
<commit_msg>Some packages have license info in their README file. (#321)<commit_after>//go:build cit
// +build cit

package imagevalidation

import (
	"fmt"
	"io/ioutil"
	"path/filepath"
	"regexp"
	"testing"
)

var licenseNames = []string{
	"Apache License",
	"Artistic/GPL",
	"Artistic",
	"Autoconf",
	"\"Bitstream Vera\"",
	"BSD",
	"BZIP",
	"COMMON PUBLIC LICENSE VERSION 1.0",
	"config-h",
	"curl",
	"Expat",
	"GAP",
	"GD",
	"GFDL-1.3+",
	"GNU General Public License",
	"GNU GPL",
	"GNU LGPL",
	"GNU Lesser Public License",
	"GPL",
	"HPND",
	"IBM PUBLIC LICENSE VERSION 1.0",
	"ISC",
	"JPEG",
	"LGPL",
	"MIT",
	"MIT license",
	"MIT/X11 (BSD like)",
	"no notice",
	"noderivs",
	"none",
	"PD",
	"PD-debian",
	"PERLDOCS",
	"permissive-fsf",
	"permissive-nowarranty",
	"probably-PD",
	"Paul Vixie\"s license",
	"public-domain",
	"REGCOMP",
	"S2P",
	"SDBM-PUBLIC-DOMAIN",
	"SIL Open Font License",
	"TEXT-SOUNDEX",
	"TEXT-TABS",
	"The OpenLDAP Public License",
	"Unicode",
	"X11",
	"X11-2",
	"ZLIB",
}

var licenses = []string{
	`Permission to use, copy, modify, distribute, and sell this software and its documentation for any purpose is hereby granted without fee, provided that the above copyright notice appear in all copies and that both that copyright notice and this permission notice appear in supporting documentation, and that the name of the authors not be used in advertising or publicity pertaining to distribution of the software without specific, written prior permission. The authors makes no representations about the suitability of this software for any purpose. It is provided "as is" without express or implied warranty.`,
	`free software; you can redistribute it and/or modify it under the terms of the GNU.*General Public License.*as published by the Free Software Foundation`,
	`The main library is licensed under GNU Lesser General Public License (LGPL) version 2.1+, Gnutls Extra (i.e. openssl wrapper library, and library for code for "GnuTLS Inner Application" support) build system, testsuite and commandline utilities are licenced under the GNU General Public License version 3+. The Guile bindings use the same license as the respective underlying library, i.e. LGPLv2.1+ for the main library and GPLv3+ for Gnutls extra.`,
	`Permission is granted to anyone to use this.*for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions`,
	`This software is released under the terms of the GNU.*General Public License.*`,
	`All files in this package can be freely distributed and used according to the terms of the GNU.*General Public License, either version 2 or (at your opinion) any newer version. This is the same distribution policy as for the Linux kernel itself -- see /usr/src/linux/COPYING for details.`,
	`You are free to distribute this.*under the terms of the BSD License`,
	`All files in this.*can be freely distributed and used according to the terms of the GNU General Public License`,
	`all of the code is covered under the terms of the GPL.`,
	`is free software`,
	`You are free to distribute this software under the terms of the BSD License.`,
	`is licensed under the BSD license`,
	`(is|are|be) free to distribute`,
	`may freely distribute`,
	`(is|are|be) freely distributed`,
	`.*is available under the terms of the GNU.*Public License`,
	`This data is licenced under 2 different licenses 1\) GNU General Public License, version 2 or later 2\) XFree86 1.0 license This data can be used freely under either license.`,
	`.*is in the public domain.`,
	`is covered under the terms of the GNU Public License.`,
	`redistribute it freely`,
	`the complete text of the GNU General Public License can be found in`,
	`free for commercial and non-commercial use as long as the following conditions are aheared to`,
	`Permission to.*use.*distribute.*this.*for any purpose.*is.*granted`,
	`There are no restrictions on distributing unmodified copies of Vim except that they must include this license text.`,
	`Redistribution and use.*(is|are) permitted`,
	`Permission is.*granted.*deal.*without restriction, including without limitation the rights to use`,
	`All its programs.*may be redistributed under the terms of the GNU GPL, Version 2 or later`,
	`is distributed under the GNU.*General Public License`,
	`This software is distributed under the GNU General Public License`,
	`This package is dual-licensed under the Academic Free License version 2.1, and the GPL version 2.`,
	`may be used, modified and redistributed only under the terms of the GNU General Public License`,
	`has been placed in the public domain`,
	`And licensed under the terms of the GPL license`,
	`are distributed under the terms of the GNU.*General Public License`,
	`The keys in the keyrings don\'t fall under any copyright. Everything else in the package is covered by the GNU GPL.`,
	`the complete text of the GNU General Public License and of the GNU Lesser Public License can be found in`,
	`THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS IBM PUBLIC LICENSE`,
	`THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS COMMON PUBLIC LICENSE`,
	`GNU LESSER GENERAL PUBLIC LICENSE`,
	`Permission is hereby granted.*to any person obtaining a copy of.*and associated documentation files.*to deal in.*without restriction`,
	`Redistribution and use of this software and associated documentation ("Software"), with or without modification, are permitted`,
	`This code is multi Licensed under all/any one of.*LGPLv2.*New Style BSD.*MIT`,
	`LICENSE. You may copy and use the Software, subject to these conditions: 1. This Software is licensed for use only in conjunction with Intel component products. Use of the Software in conjunction with non-Intel component products is not licensed hereunder.`,
	`Brocade Linux Fibre Channel HBA Firmware`,
	`QLogic Linux Fibre Channel HBA Firmware`,
	`Unlimited distribution and/or modification is allowed as long as this copyright notice remains intact.`,
	`Permission is hereby granted to use.*this.*for any purpose`,
	`are in the public domain`,
	`is (available|distributed) under the terms of the GNU.*Public License`,
	`(libudev|libgudev|udev) is licensed under the GNU (L|)GPL`,
	`The Linux Console Tools are covered by the GPL`,
	`Some portions of os-prober`,
	`Netcat and the associated package is a product of Avian Research, and is freely available in full source form with no restrictions save an obligation to give credit where due.`,
	`Permission is hereby granted, without written agreement and without licence or royalty fees, to use, copy, modify, and distribute this software`,
	`Open Market permits you to use, copy, modify, distribute, and license this Software and the Documentation for any purpose, provided that existing copyright notices are retained in all copies and that this notice is included verbatim in any distributions. No written agreement, license, or royalty fee is required for any of the authorized uses.`,
	`This software is made available under the terms of *either* of the licenses found in LICENSE.APACHE or LICENSE.BSD.`,
	`This library.*is public domain software`,
	`Apache License`,
	`The Artistic License 2.0`,
	`DO WHAT THE HELL YOU WANT TO`, // Yes, this is a real license.
}

const (
	licenseNameRegex = `(?i)(((License|Copyright)\s*:\s*%[1]s)|((covered )?under (the )?%[1]s)|(under (the terms of )?the %[1]s))`
)

var licenseGlobs = []string{
	"/usr/share/doc/*/copyright",
	"/usr/share/doc/*/LICENSE",
	"/usr/share/doc/*/README",
	"/usr/share/licenses/*/LICENSE",
}

func isValidLicenseName(licenseCheck string) bool {
	for _, name := range licenseNames {
		var regexString = fmt.Sprintf(licenseNameRegex, name)
		re, err := regexp.Compile(regexString)
		if err != nil {
			return false
		}
		if re.MatchString(licenseCheck) {
			return true
		}
	}
	return false
}

func isValidLicenseText(licenseCheck string) bool {
	for _, licenseText := range licenses {
		// (?i) case insensitive
		re, err := regexp.Compile(`(?i)` + licenseText)
		if err != nil {
			return false
		}

		if re.MatchString(licenseCheck) {
			return true
		}
	}
	return false
}

func TestArePackagesLegal(t *testing.T) {
	for _, pathGlob := range licenseGlobs {
		filenames, err := filepath.Glob(pathGlob)
		if err != nil || len(filenames) == 0 {
			t.Logf("couldnt resolve glob %s", pathGlob)
			continue
		}

		for _, filename := range filenames {
			isLegal, err := isPackageLegal(filename)
			if err != nil {
				t.Fatalf(err.Error())
			}
			if !isLegal {
				t.Errorf("Found illegal package: %v", filename)
			}
		}
	}
}

func isPackageLegal(filepath string) (bool, error) {
	bytes, err := ioutil.ReadFile(filepath)
	if err != nil {
		return false, fmt.Errorf("error reading file: %v", err)
	}

	var licenseCheck string = string(bytes)

	// Remove comments.
	commentRegex, err := regexp.Compile(`\*|#`)
	if err != nil {
		return false, fmt.Errorf("invalid regular expression: %v", err)
	}
	licenseCheck = commentRegex.ReplaceAllString(licenseCheck, "")

	// Replace repeated whitespace and newlines with one space.
	whitespaceRegex, err := regexp.Compile(`\s+`)
	if err != nil {
		return false, fmt.Errorf("invalid regular expression: %v", err)
	}
	licenseCheck = whitespaceRegex.ReplaceAllString(licenseCheck, " ")
	if isValidLicenseName(licenseCheck) || isValidLicenseText(licenseCheck) {
		return true, nil
	}

	return false, nil
}
<|endoftext|>


<commit_before>package scaling_test

import (
	"encoding/json"
	"io/ioutil"
	"math/rand"
	"os/exec"
	"time"

	pusherConfig "cf-pusher/config"

	"github.com/cloudfoundry-incubator/cf-test-helpers/cf"
	"github.com/cloudfoundry-incubator/cf-test-helpers/helpers"
	. "github.com/onsi/ginkgo"
	ginkgoConfig "github.com/onsi/ginkgo/config"
	. "github.com/onsi/gomega"
	"github.com/onsi/gomega/gexec"

	"testing"
)

const Timeout_Short = 20 * time.Second

var (
	config     helpers.Config
	pushConfig pusherConfig.Config
)

func TestScaling(t *testing.T) {
	rand.Seed(ginkgoConfig.GinkgoConfig.RandomSeed + int64(GinkgoParallelNode()))

	RegisterFailHandler(Fail)
	RunSpecs(t, "Scaling Suite")
}

var _ = BeforeSuite(func() {
	config = helpers.LoadConfig()

	configPath := helpers.ConfigPath()
	configBytes, err := ioutil.ReadFile(configPath)
	Expect(err).NotTo(HaveOccurred())

	err = json.Unmarshal(configBytes, &pushConfig)
	Expect(err).NotTo(HaveOccurred())

	if pushConfig.Applications <= 0 {
		Fail("Applications count needs to be greater than 0")
	}

	if pushConfig.AppInstances <= 0 {
		Fail("AppInstances count needs to be greater than 0")
	}

	if pushConfig.ProxyApplications <= 0 {
		Fail("ProxyApplications count needs to be greater than 0")
	}

	if pushConfig.ProxyInstances <= 0 {
		Fail("ProxyInstances count needs to be greater than 0")
	}

	Expect(cf.Cf("api", "--skip-ssl-validation", config.ApiEndpoint).Wait(Timeout_Short)).To(gexec.Exit(0))
	AuthAsAdmin()
	Expect(cf.Cf("target", "-o", pushConfig.Prefix+"org", "-s", pushConfig.Prefix+"space").Wait(Timeout_Short)).To(gexec.Exit(0))
})

func AuthAsAdmin() {
	Auth(config.AdminUser, config.AdminPassword)
}

func Auth(username, password string) {
	By("authenticating as " + username)
	cmd := exec.Command("cf", "auth", username, password)
	sess, err := gexec.Start(cmd, nil, nil)
	Expect(err).NotTo(HaveOccurred())
	Eventually(sess.Wait(Timeout_Short)).Should(gexec.Exit(0))
}
<commit_msg>fix cf-test-helpers import<commit_after>package scaling_test

import (
	"encoding/json"
	"io/ioutil"
	"math/rand"
	"os/exec"
	"time"

	pusherConfig "cf-pusher/config"

	"github.com/cloudfoundry-incubator/cf-test-helpers/cf"
	helpers "github.com/cloudfoundry-incubator/cf-test-helpers/config"
	. "github.com/onsi/ginkgo"
	ginkgoConfig "github.com/onsi/ginkgo/config"
	. "github.com/onsi/gomega"
	"github.com/onsi/gomega/gexec"

	"testing"
)

const Timeout_Short = 20 * time.Second

var (
	config     *helpers.Config
	pushConfig pusherConfig.Config
)

func TestScaling(t *testing.T) {
	rand.Seed(ginkgoConfig.GinkgoConfig.RandomSeed + int64(GinkgoParallelNode()))

	RegisterFailHandler(Fail)
	RunSpecs(t, "Scaling Suite")
}

var _ = BeforeSuite(func() {
	config = helpers.LoadConfig()

	configPath := helpers.ConfigPath()
	configBytes, err := ioutil.ReadFile(configPath)
	Expect(err).NotTo(HaveOccurred())

	err = json.Unmarshal(configBytes, &pushConfig)
	Expect(err).NotTo(HaveOccurred())

	if pushConfig.Applications <= 0 {
		Fail("Applications count needs to be greater than 0")
	}

	if pushConfig.AppInstances <= 0 {
		Fail("AppInstances count needs to be greater than 0")
	}

	if pushConfig.ProxyApplications <= 0 {
		Fail("ProxyApplications count needs to be greater than 0")
	}

	if pushConfig.ProxyInstances <= 0 {
		Fail("ProxyInstances count needs to be greater than 0")
	}

	Expect(cf.Cf("api", "--skip-ssl-validation", config.ApiEndpoint).Wait(Timeout_Short)).To(gexec.Exit(0))
	AuthAsAdmin()
	Expect(cf.Cf("target", "-o", pushConfig.Prefix+"org", "-s", pushConfig.Prefix+"space").Wait(Timeout_Short)).To(gexec.Exit(0))
})

func AuthAsAdmin() {
	Auth(config.AdminUser, config.AdminPassword)
}

func Auth(username, password string) {
	By("authenticating as " + username)
	cmd := exec.Command("cf", "auth", username, password)
	sess, err := gexec.Start(cmd, nil, nil)
	Expect(err).NotTo(HaveOccurred())
	Eventually(sess.Wait(Timeout_Short)).Should(gexec.Exit(0))
}
<|endoftext|>


<commit_before>package vsphere

import (
	"fmt"
	"log"

	"github.com/hashicorp/terraform/helper/schema"
	"github.com/vmware/govmomi"
	"github.com/vmware/govmomi/find"
	"github.com/vmware/govmomi/object"
	"github.com/vmware/govmomi/property"
	"github.com/vmware/govmomi/vim25/mo"
	"golang.org/x/net/context"
)

func resourceVSphereVirtualMachine() *schema.Resource {
	return &schema.Resource{
		Create: resourceVSphereVirtualMachineCreate,
		Read:   resourceVSphereVirtualMachineRead,
		Update: resourceVSphereVirtualMachineUpdate,
		Delete: resourceVSphereVirtualMachineDelete,

		Schema: map[string]*schema.Schema{
			"name": &schema.Schema{
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},

			"vcpu": &schema.Schema{
				Type:     schema.TypeInt,
				Required: true,
				ForceNew: false,
			},

			"memory": &schema.Schema{
				Type:     schema.TypeInt,
				Required: true,
				ForceNew: false,
			},

			"datacenter": &schema.Schema{
				Type:     schema.TypeString,
				Optional: true,
				ForceNew: false,
			},

			"cluster": &schema.Schema{
				Type:     schema.TypeString,
				Optional: true,
				ForceNew: false,
			},

			"resource_pool": &schema.Schema{
				Type:     schema.TypeString,
				Optional: true,
				ForceNew: false,
			},

			"gateway": &schema.Schema{
				Type:     schema.TypeString,
				Optional: true,
				ForceNew: false,
			},

			"domain": &schema.Schema{
				Type:     schema.TypeString,
				Optional: true,
				ForceNew: false,
				Default:  "vsphere.local",
			},

			"time_zone": &schema.Schema{
				Type:     schema.TypeString,
				Optional: true,
				ForceNew: false,
				Default:  "Etc/UTC",
			},

			"dns_suffix": &schema.Schema{
				Type:     schema.TypeList,
				Optional: true,
				Elem:     &schema.Schema{Type: schema.TypeString},
				ForceNew: false,
			},

			"dns_server": &schema.Schema{
				Type:     schema.TypeList,
				Optional: true,
				Elem:     &schema.Schema{Type: schema.TypeString},
				ForceNew: false,
			},

			"network_interface": &schema.Schema{
				Type:     schema.TypeList,
				Required: true,
				Elem: &schema.Resource{
					Schema: map[string]*schema.Schema{
						"label": &schema.Schema{
							Type:     schema.TypeString,
							Required: true,
							ForceNew: false,
						},

						"ip_address": &schema.Schema{
							Type:     schema.TypeString,
							Optional: true,
							ForceNew: false,
						},

						"subnet_mask": &schema.Schema{
							Type:     schema.TypeString,
							Optional: true,
							ForceNew: false,
						},

						"adapter_type": &schema.Schema{
							Type:     schema.TypeString,
							Optional: true,
							ForceNew: false,
						},
					},
				},
			},

			"disk": &schema.Schema{
				Type:     schema.TypeList,
				Required: true,
				Elem: &schema.Resource{
					Schema: map[string]*schema.Schema{
						"template": &schema.Schema{
							Type:     schema.TypeString,
							Optional: true,
							ForceNew: false,
						},

						"datastore": &schema.Schema{
							Type:     schema.TypeString,
							Optional: true,
							ForceNew: false,
						},

						"size": &schema.Schema{
							Type:     schema.TypeInt,
							Optional: true,
							ForceNew: false,
						},

						"iops": &schema.Schema{
							Type:     schema.TypeInt,
							Optional: true,
							ForceNew: false,
						},
					},
				},
			},
		},
	}
}

func resourceVSphereVirtualMachineCreate(d *schema.ResourceData, meta interface{}) error {
	client := meta.(*govmomi.Client)

	vm := virtualMachine{
		name:     d.Get("name").(string),
		vcpu:     d.Get("vcpu").(int),
		memoryMb: int64(d.Get("memory").(int)),
	}

	if v := d.Get("datacenter"); v != nil {
		vm.datacenter = d.Get("datacenter").(string)
	}

	if v := d.Get("cluster"); v != nil {
		vm.cluster = d.Get("cluster").(string)
	}

	if v := d.Get("resource_pool"); v != nil {
		vm.resourcePool = d.Get("resource_pool").(string)
	}

	if v := d.Get("gateway"); v != nil {
		vm.gateway = d.Get("gateway").(string)
	}

	if v := d.Get("domain"); v != nil {
		vm.domain = d.Get("domain").(string)
	}

	if v := d.Get("time_zone"); v != nil {
		vm.timeZone = d.Get("time_zone").(string)
	}

	dns_suffix := d.Get("dns_suffix.#").(int)
	if dns_suffix > 0 {
		vm.dnsSuffixes = make([]string, 0, dns_suffix)
		for i := 0; i < dns_suffix; i++ {
			s := fmt.Sprintf("dns_suffix.%d", i)
			vm.dnsSuffixes = append(vm.dnsSuffixes, d.Get(s).(string))
		}
	} else {
		vm.dnsSuffixes = []string{"vsphere.local"}
	}

	dns_server := d.Get("dns_server.#").(int)
	if dns_server > 0 {
		vm.dnsServers = make([]string, 0, dns_server)
		for i := 0; i < dns_server; i++ {
			s := fmt.Sprintf("dns_server.%d", i)
			vm.dnsServers = append(vm.dnsServers, d.Get(s).(string))
		}
	} else {
		vm.dnsServers = []string{
			"8.8.8.8",
			"8.8.4.4",
		}
	}

	networksCount := d.Get("network_interface.#").(int)
	networks := make([]networkInterface, networksCount)
	for i := 0; i < networksCount; i++ {
		prefix := fmt.Sprintf("network_interface.%d", i)
		networks[i].label = d.Get(prefix + ".label").(string)
		if v := d.Get(prefix + ".ip_address"); v != nil {
			networks[i].ipAddress = d.Get(prefix + ".ip_address").(string)
			networks[i].subnetMask = d.Get(prefix + ".subnet_mask").(string)
		}
	}
	vm.networkInterfaces = networks
	log.Printf("[DEBUG] network_interface init: %v", networks)

	diskCount := d.Get("disk.#").(int)
	disks := make([]hardDisk, diskCount)
	for i := 0; i < diskCount; i++ {
		prefix := fmt.Sprintf("disk.%d", i)
		if i == 0 {
			if v := d.Get(prefix + ".template"); v != "" {
				vm.template = d.Get(prefix + ".template").(string)
			} else {
				if v := d.Get(prefix + ".size"); v != "" {
					disks[i].size = int64(d.Get(prefix + ".size").(int))
				} else {
					return fmt.Errorf("If template argument is not specified, size argument is required.")
				}
			}
			if v := d.Get(prefix + ".datastore"); v != "" {
				vm.datastore = d.Get(prefix + ".datastore").(string)
			}
		} else {
			if v := d.Get(prefix + ".size"); v != "" {
				disks[i].size = int64(d.Get(prefix + ".size").(int))
			} else {
				return fmt.Errorf("Size argument is required.")
			}
		}
		if v := d.Get(prefix + ".iops"); v != "" {
			disks[i].iops = int64(d.Get(prefix + ".iops").(int))
		}
	}
	vm.hardDisks = disks
	log.Printf("[DEBUG] disk init: %v", disks)

	if vm.template != "" {
		err := vm.deployVirtualMachine(client)
		if err != nil {
			return fmt.Errorf("error: %s", err)
		}
	} else {
		err := vm.createVirtualMachine(client)
		if err != nil {
			return fmt.Errorf("error: %s", err)
		}
	}
	d.SetId(vm.name)
	log.Printf("[INFO] Created virtual machine: %s", d.Id())

	return resourceVSphereVirtualMachineRead(d, meta)
}

func resourceVSphereVirtualMachineRead(d *schema.ResourceData, meta interface{}) error {
	var dc *object.Datacenter
	var err error

	client := meta.(*govmomi.Client)
	finder := find.NewFinder(client.Client, true)

	if d.Get("datacenter").(string) != "" {
		dc, err = finder.Datacenter(context.TODO(), d.Get("datacenter").(string))
		if err != nil {
			return err
		}
	} else {
		dc, err = finder.DefaultDatacenter(context.TODO())
		if err != nil {
			return err
		}
	}

	finder = finder.SetDatacenter(dc)
	vm, err := finder.VirtualMachine(context.TODO(), d.Get("name").(string))
	if err != nil {
		log.Printf("[ERROR] Virtual machine not found: %s", d.Get("name").(string))
		d.SetId("")
		return nil
	}

	var mvm mo.VirtualMachine

	collector := property.DefaultCollector(client.Client)
	err = collector.RetrieveOne(context.TODO(), vm.Reference(), []string{"summary"}, &mvm)

	d.Set("datacenter", dc)
	d.Set("memory", mvm.Summary.Config.MemorySizeMB)
	d.Set("cpu", mvm.Summary.Config.NumCpu)

	return nil
}

func resourceVSphereVirtualMachineUpdate(d *schema.ResourceData, meta interface{}) error {
	return nil
}

func resourceVSphereVirtualMachineDelete(d *schema.ResourceData, meta interface{}) error {
	var dc *object.Datacenter
	var err error

	client := meta.(*govmomi.Client)
	finder := find.NewFinder(client.Client, true)

	if d.Get("datacenter").(string) != "" {
		dc, err = finder.Datacenter(context.TODO(), d.Get("datacenter").(string))
		if err != nil {
			return err
		}
	} else {
		dc, err = finder.DefaultDatacenter(context.TODO())
		if err != nil {
			return err
		}
	}

	finder = finder.SetDatacenter(dc)
	vm, err := finder.VirtualMachine(context.TODO(), d.Get("name").(string))
	if err != nil {
		return err
	}

	log.Printf("[INFO] Deleting virtual machine: %s", d.Id())

	_, err = vm.PowerOff(context.TODO())
	if err != nil {
		return err
	}

	_, err = vm.Destroy(context.TODO())
	if err != nil {
		return err
	}

	d.SetId("")
	return nil
}
<commit_msg>Use GetOk method<commit_after>package vsphere

import (
	"fmt"
	"log"

	"github.com/hashicorp/terraform/helper/schema"
	"github.com/vmware/govmomi"
	"github.com/vmware/govmomi/find"
	"github.com/vmware/govmomi/object"
	"github.com/vmware/govmomi/property"
	"github.com/vmware/govmomi/vim25/mo"
	"golang.org/x/net/context"
)

func resourceVSphereVirtualMachine() *schema.Resource {
	return &schema.Resource{
		Create: resourceVSphereVirtualMachineCreate,
		Read:   resourceVSphereVirtualMachineRead,
		Update: resourceVSphereVirtualMachineUpdate,
		Delete: resourceVSphereVirtualMachineDelete,

		Schema: map[string]*schema.Schema{
			"name": &schema.Schema{
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},

			"vcpu": &schema.Schema{
				Type:     schema.TypeInt,
				Required: true,
				ForceNew: false,
			},

			"memory": &schema.Schema{
				Type:     schema.TypeInt,
				Required: true,
				ForceNew: false,
			},

			"datacenter": &schema.Schema{
				Type:     schema.TypeString,
				Optional: true,
				ForceNew: false,
			},

			"cluster": &schema.Schema{
				Type:     schema.TypeString,
				Optional: true,
				ForceNew: false,
			},

			"resource_pool": &schema.Schema{
				Type:     schema.TypeString,
				Optional: true,
				ForceNew: false,
			},

			"gateway": &schema.Schema{
				Type:     schema.TypeString,
				Optional: true,
				ForceNew: false,
			},

			"domain": &schema.Schema{
				Type:     schema.TypeString,
				Optional: true,
				ForceNew: false,
				Default:  "vsphere.local",
			},

			"time_zone": &schema.Schema{
				Type:     schema.TypeString,
				Optional: true,
				ForceNew: false,
				Default:  "Etc/UTC",
			},

			"dns_suffix": &schema.Schema{
				Type:     schema.TypeList,
				Optional: true,
				Elem:     &schema.Schema{Type: schema.TypeString},
				ForceNew: false,
			},

			"dns_server": &schema.Schema{
				Type:     schema.TypeList,
				Optional: true,
				Elem:     &schema.Schema{Type: schema.TypeString},
				ForceNew: false,
			},

			"network_interface": &schema.Schema{
				Type:     schema.TypeList,
				Required: true,
				Elem: &schema.Resource{
					Schema: map[string]*schema.Schema{
						"label": &schema.Schema{
							Type:     schema.TypeString,
							Required: true,
							ForceNew: false,
						},

						"ip_address": &schema.Schema{
							Type:     schema.TypeString,
							Optional: true,
							ForceNew: false,
						},

						"subnet_mask": &schema.Schema{
							Type:     schema.TypeString,
							Optional: true,
							ForceNew: false,
						},

						"adapter_type": &schema.Schema{
							Type:     schema.TypeString,
							Optional: true,
							ForceNew: false,
						},
					},
				},
			},

			"disk": &schema.Schema{
				Type:     schema.TypeList,
				Required: true,
				Elem: &schema.Resource{
					Schema: map[string]*schema.Schema{
						"template": &schema.Schema{
							Type:     schema.TypeString,
							Optional: true,
							ForceNew: false,
						},

						"datastore": &schema.Schema{
							Type:     schema.TypeString,
							Optional: true,
							ForceNew: false,
						},

						"size": &schema.Schema{
							Type:     schema.TypeInt,
							Optional: true,
							ForceNew: false,
						},

						"iops": &schema.Schema{
							Type:     schema.TypeInt,
							Optional: true,
							ForceNew: false,
						},
					},
				},
			},
		},
	}
}

func resourceVSphereVirtualMachineCreate(d *schema.ResourceData, meta interface{}) error {
	client := meta.(*govmomi.Client)

	vm := virtualMachine{
		name:     d.Get("name").(string),
		vcpu:     d.Get("vcpu").(int),
		memoryMb: int64(d.Get("memory").(int)),
	}

	if v, ok := d.GetOk("datacenter"); ok {
		vm.datacenter = v.(string)
	}

	if v, ok := d.GetOk("cluster"); ok {
		vm.cluster = v.(string)
	}

	if v, ok := d.GetOk("resource_pool"); ok {
		vm.resourcePool = v.(string)
	}

	if v, ok := d.GetOk("gateway"); ok {
		vm.gateway = v.(string)
	}

	if v, ok := d.GetOk("domain"); ok {
		vm.gateway = v.(string)
	}

	if v, ok := d.GetOk("time_zone"); ok {
		vm.gateway = v.(string)
	}

	dns_suffix := d.Get("dns_suffix.#").(int)
	if dns_suffix > 0 {
		vm.dnsSuffixes = make([]string, 0, dns_suffix)
		for i := 0; i < dns_suffix; i++ {
			s := fmt.Sprintf("dns_suffix.%d", i)
			vm.dnsSuffixes = append(vm.dnsSuffixes, d.Get(s).(string))
		}
	} else {
		vm.dnsSuffixes = []string{"vsphere.local"}
	}

	dns_server := d.Get("dns_server.#").(int)
	if dns_server > 0 {
		vm.dnsServers = make([]string, 0, dns_server)
		for i := 0; i < dns_server; i++ {
			s := fmt.Sprintf("dns_server.%d", i)
			vm.dnsServers = append(vm.dnsServers, d.Get(s).(string))
		}
	} else {
		vm.dnsServers = []string{
			"8.8.8.8",
			"8.8.4.4",
		}
	}

	networksCount := d.Get("network_interface.#").(int)
	networks := make([]networkInterface, networksCount)
	for i := 0; i < networksCount; i++ {
		prefix := fmt.Sprintf("network_interface.%d", i)
		networks[i].label = d.Get(prefix + ".label").(string)
		if v := d.Get(prefix + ".ip_address"); v != nil {
			networks[i].ipAddress = d.Get(prefix + ".ip_address").(string)
			networks[i].subnetMask = d.Get(prefix + ".subnet_mask").(string)
		}
	}
	vm.networkInterfaces = networks
	log.Printf("[DEBUG] network_interface init: %v", networks)

	diskCount := d.Get("disk.#").(int)
	disks := make([]hardDisk, diskCount)
	for i := 0; i < diskCount; i++ {
		prefix := fmt.Sprintf("disk.%d", i)
		if i == 0 {
			if v := d.Get(prefix + ".template"); v != "" {
				vm.template = d.Get(prefix + ".template").(string)
			} else {
				if v := d.Get(prefix + ".size"); v != "" {
					disks[i].size = int64(d.Get(prefix + ".size").(int))
				} else {
					return fmt.Errorf("If template argument is not specified, size argument is required.")
				}
			}
			if v := d.Get(prefix + ".datastore"); v != "" {
				vm.datastore = d.Get(prefix + ".datastore").(string)
			}
		} else {
			if v := d.Get(prefix + ".size"); v != "" {
				disks[i].size = int64(d.Get(prefix + ".size").(int))
			} else {
				return fmt.Errorf("Size argument is required.")
			}
		}
		if v := d.Get(prefix + ".iops"); v != "" {
			disks[i].iops = int64(d.Get(prefix + ".iops").(int))
		}
	}
	vm.hardDisks = disks
	log.Printf("[DEBUG] disk init: %v", disks)

	if vm.template != "" {
		err := vm.deployVirtualMachine(client)
		if err != nil {
			return fmt.Errorf("error: %s", err)
		}
	} else {
		err := vm.createVirtualMachine(client)
		if err != nil {
			return fmt.Errorf("error: %s", err)
		}
	}
	d.SetId(vm.name)
	log.Printf("[INFO] Created virtual machine: %s", d.Id())

	return resourceVSphereVirtualMachineRead(d, meta)
}

func resourceVSphereVirtualMachineRead(d *schema.ResourceData, meta interface{}) error {
	var dc *object.Datacenter
	var err error

	client := meta.(*govmomi.Client)
	finder := find.NewFinder(client.Client, true)

	if v, ok := d.GetOk("datacenter"); ok {
		dc, err = finder.Datacenter(context.TODO(), v.(string))
		if err != nil {
			return err
		}
	} else {
		dc, err = finder.DefaultDatacenter(context.TODO())
		if err != nil {
			return err
		}
	}

	finder = finder.SetDatacenter(dc)
	vm, err := finder.VirtualMachine(context.TODO(), d.Get("name").(string))
	if err != nil {
		log.Printf("[ERROR] Virtual machine not found: %s", d.Get("name").(string))
		d.SetId("")
		return nil
	}

	var mvm mo.VirtualMachine

	collector := property.DefaultCollector(client.Client)
	err = collector.RetrieveOne(context.TODO(), vm.Reference(), []string{"summary"}, &mvm)

	d.Set("datacenter", dc)
	d.Set("memory", mvm.Summary.Config.MemorySizeMB)
	d.Set("cpu", mvm.Summary.Config.NumCpu)

	return nil
}

func resourceVSphereVirtualMachineUpdate(d *schema.ResourceData, meta interface{}) error {
	return nil
}

func resourceVSphereVirtualMachineDelete(d *schema.ResourceData, meta interface{}) error {
	var dc *object.Datacenter
	var err error

	client := meta.(*govmomi.Client)
	finder := find.NewFinder(client.Client, true)

	if v, ok := d.GetOk("datacenter"); ok {
		dc, err = finder.Datacenter(context.TODO(), v.(string))
		if err != nil {
			return err
		}
	} else {
		dc, err = finder.DefaultDatacenter(context.TODO())
		if err != nil {
			return err
		}
	}

	finder = finder.SetDatacenter(dc)
	vm, err := finder.VirtualMachine(context.TODO(), d.Get("name").(string))
	if err != nil {
		return err
	}

	log.Printf("[INFO] Deleting virtual machine: %s", d.Id())

	_, err = vm.PowerOff(context.TODO())
	if err != nil {
		return err
	}

	_, err = vm.Destroy(context.TODO())
	if err != nil {
		return err
	}

	d.SetId("")
	return nil
}
<|endoftext|>


<commit_before>/*
 * Copyright 2017 The Kythe Authors. All rights reserved.
 *
 * 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 cli

import (
	"context"
	"flag"
	"fmt"
	"strings"

	cpb "kythe.io/kythe/proto/common_go_proto"
	gpb "kythe.io/kythe/proto/graph_go_proto"
)

type nodesCommand struct {
	nodeFilters       string
	factSizeThreshold int
}

func (nodesCommand) Name() string     { return "nodes" }
func (nodesCommand) Synopsis() string { return "retrieve a node's facts" }
func (nodesCommand) Usage() string    { return "" }
func (c *nodesCommand) SetFlags(flag *flag.FlagSet) {
	flag.StringVar(&c.nodeFilters, "filters", "", "Comma-separated list of node fact filters (default returns all)")
	flag.IntVar(&c.factSizeThreshold, "max_fact_size", 64,
		"Maximum size of fact values to display.  Facts with byte lengths longer than this value will only have their fact names displayed.")
}
func (c nodesCommand) Run(ctx context.Context, flag *flag.FlagSet, api API) error {
	if c.factSizeThreshold < 0 {
		return fmt.Errorf("invalid --max_fact_size value (must be non-negative): %d", c.factSizeThreshold)
	}

	req := &gpb.NodesRequest{
		Ticket: flag.Args(),
	}
	if c.nodeFilters != "" {
		req.Filter = strings.Split(c.nodeFilters, ",")
	}
	LogRequest(req)
	reply, err := api.GraphService.Nodes(ctx, req)
	if err != nil {
		return err
	}
	return c.displayNodes(reply.Nodes)
}

func (c *nodesCommand) displayNodes(nodes map[string]*cpb.NodeInfo) error {
	if DisplayJSON {
		return PrintJSON(nodes)
	}

	for ticket, n := range nodes {
		if _, err := fmt.Fprintln(out, ticket); err != nil {
			return err
		}
		for name, value := range n.Facts {
			if len(value) <= c.factSizeThreshold {
				if _, err := fmt.Fprintf(out, "  %s\t%s\n", name, value); err != nil {
					return err
				}
			} else {
				if _, err := fmt.Fprintf(out, "  %s\n", name); err != nil {
					return err
				}
			}
		}
	}
	return nil
}
<commit_msg>Improve CLI tool's display for truncated values (#4198)<commit_after>/*
 * Copyright 2017 The Kythe Authors. All rights reserved.
 *
 * 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 cli

import (
	"context"
	"flag"
	"fmt"
	"strings"

	cpb "kythe.io/kythe/proto/common_go_proto"
	gpb "kythe.io/kythe/proto/graph_go_proto"
)

type nodesCommand struct {
	nodeFilters       string
	factSizeThreshold int
}

func (nodesCommand) Name() string     { return "nodes" }
func (nodesCommand) Synopsis() string { return "retrieve a node's facts" }
func (nodesCommand) Usage() string    { return "" }
func (c *nodesCommand) SetFlags(flag *flag.FlagSet) {
	flag.StringVar(&c.nodeFilters, "filters", "", "Comma-separated list of node fact filters (default returns all)")
	flag.IntVar(&c.factSizeThreshold, "max_fact_size", 64,
		"Maximum size of fact values to display.  Facts with byte lengths longer than this value will only have their fact names displayed.")
}
func (c nodesCommand) Run(ctx context.Context, flag *flag.FlagSet, api API) error {
	if c.factSizeThreshold < 0 {
		return fmt.Errorf("invalid --max_fact_size value (must be non-negative): %d", c.factSizeThreshold)
	}

	req := &gpb.NodesRequest{
		Ticket: flag.Args(),
	}
	if c.nodeFilters != "" {
		req.Filter = strings.Split(c.nodeFilters, ",")
	}
	LogRequest(req)
	reply, err := api.GraphService.Nodes(ctx, req)
	if err != nil {
		return err
	}
	return c.displayNodes(reply.Nodes)
}

func (c *nodesCommand) displayNodes(nodes map[string]*cpb.NodeInfo) error {
	if DisplayJSON {
		return PrintJSON(nodes)
	}

	for ticket, n := range nodes {
		if _, err := fmt.Fprintln(out, ticket); err != nil {
			return err
		}
		for name, value := range n.Facts {
			if len(value) <= c.factSizeThreshold {
				if _, err := fmt.Fprintf(out, "  %s\t%s\n", name, value); err != nil {
					return err
				}
			} else {
				if _, err := fmt.Fprintf(out, "  %s\t%s<truncated>\n", name, value[:c.factSizeThreshold]); err != nil {
					return err
				}
			}
		}
	}
	return nil
}
<|endoftext|>


<commit_before>package lib

import (
	"os"
)

// all supported options of ossutil
const (
	OptionConfigFile       string = "configFile"
	OptionEndpoint                = "endpoint"
	OptionAccessKeyID             = "accessKeyID"
	OptionAccessKeySecret         = "accessKeySecret"
	OptionSTSToken                = "stsToken"
	OptionACL                     = "acl"
	OptionShortFormat             = "shortFormat"
	OptionDirectory               = "directory"
	OptionMultipart               = "multipart"
	OptionAllType                 = "allType"
	OptionRecursion               = "recursive"
	OptionBucket                  = "bucket"
	OptionForce                   = "force"
	OptionUpdate                  = "update"
	OptionDelete                  = "delete"
	OptionContinue                = "continue"
	OptionOutputDir               = "outputDir"
	OptionBigFileThreshold        = "bigfileThreshold"
	OptionCheckpointDir           = "checkpointDir"
	OptionSnapshotPath            = "snapshotPath"
	OptionRetryTimes              = "retryTimes"
	OptionRoutines                = "routines"
	OptionParallel                = "parallel"
	OptionLanguage                = "language"
	OptionHashType                = "hashType"
	OptionVersion                 = "version"
)

// the elements show in stat object
const (
	StatName             string = "Name"
	StatLocation                = "Location"
	StatCreationDate            = "CreationDate"
	StatExtranetEndpoint        = "ExtranetEndpoint"
	StatIntranetEndpoint        = "IntranetEndpoint"
	StatACL                     = "ACL"
	StatOwner                   = "Owner"
	StatLastModified            = "Last-Modified"
	StatContentMD5              = "Content-Md5"
	StatCRC64                   = "X-Oss-Hash-Crc64ecma"
)

// the elements show in hash file
const (
	HashCRC64      = "CRC64-ECMA"
	HashMD5        = "MD5"
	HashContentMD5 = "Content-MD5"
)

const (
	updateEndpoint       string = "oss-cn-hangzhou.aliyuncs.com"
	updateBucket                = "ossutil-version-update"
	updateVersionObject         = "ossutilversion"
	updateBinaryLinux           = "ossutil"
	updateBinaryWindow32        = "ossutil32.exe"
	updateBinaryWindow64        = "ossutil64.exe"
	updateBinaryMac64           = "ossutilmac64"
	updateTmpVersionFile        = ".ossutil_tmp_vsersion"
)

// global public variable
const (
	Package                 string = "ossutil"
	ChannelBuf              int    = 1000
	Version                 string = "1.0.0.Beta1"
	DefaultEndpoint         string = "oss.aliyuncs.com"
	DefaultLanguage                = "CH"
	EnglishLanguage                = "EN"
	Scheme                  string = "oss"
	DefaultConfigFile              = "~" + string(os.PathSeparator) + ".ossutilconfig"
	MaxUint                        = ^uint(0)
	MaxInt                         = int(MaxUint >> 1)
	MaxUint64                      = ^uint64(0)
	MaxInt64                       = int64(MaxUint64 >> 1)
	ReportPrefix                   = "ossutil_report_"
	ReportSuffix                   = ".report"
	DefaultOutputDir               = "ossutil_output"
	CheckpointDir                  = ".ossutil_checkpoint"
	CheckpointSep                  = "---"
	SnapshotConnector              = "==>"
	SnapshotSep                    = "#"
	MaxPartNum                     = 10000
	MaxIdealPartNum                = MaxPartNum / 10
	MinIdealPartNum                = MaxPartNum / 500
	MaxIdealPartSize               = 524288000
	MinIdealPartSize               = 1048576
	DefaultBigFileThreshold        = 104857600
	MaxBigFileThreshold            = MaxInt64
	MinBigFileThreshold            = 0
	RetryTimes              int    = 3
	MaxRetryTimes           int64  = 500
	MinRetryTimes           int64  = 1
	Routines                int    = 5
	MaxRoutines             int64  = 10000
	MinRoutines             int64  = 1
	MaxParallel             int64  = 10000
	MinParallel             int64  = 1
	DefaultHashType         string = "crc64"
	MD5HashType             string = "md5"
	LogFilePrefix                  = "ossutil_log_"
)

const (
	objectType    = 0x00000001
	multipartType = 0x00000010
	allType       = objectType | multipartType // marker for objects
	bucketType    = 0x10000000
)
<commit_msg>modify version<commit_after>package lib

import (
	"os"
)

// all supported options of ossutil
const (
	OptionConfigFile       string = "configFile"
	OptionEndpoint                = "endpoint"
	OptionAccessKeyID             = "accessKeyID"
	OptionAccessKeySecret         = "accessKeySecret"
	OptionSTSToken                = "stsToken"
	OptionACL                     = "acl"
	OptionShortFormat             = "shortFormat"
	OptionDirectory               = "directory"
	OptionMultipart               = "multipart"
	OptionAllType                 = "allType"
	OptionRecursion               = "recursive"
	OptionBucket                  = "bucket"
	OptionForce                   = "force"
	OptionUpdate                  = "update"
	OptionDelete                  = "delete"
	OptionContinue                = "continue"
	OptionOutputDir               = "outputDir"
	OptionBigFileThreshold        = "bigfileThreshold"
	OptionCheckpointDir           = "checkpointDir"
	OptionSnapshotPath            = "snapshotPath"
	OptionRetryTimes              = "retryTimes"
	OptionRoutines                = "routines"
	OptionParallel                = "parallel"
	OptionLanguage                = "language"
	OptionHashType                = "hashType"
	OptionVersion                 = "version"
)

// the elements show in stat object
const (
	StatName             string = "Name"
	StatLocation                = "Location"
	StatCreationDate            = "CreationDate"
	StatExtranetEndpoint        = "ExtranetEndpoint"
	StatIntranetEndpoint        = "IntranetEndpoint"
	StatACL                     = "ACL"
	StatOwner                   = "Owner"
	StatLastModified            = "Last-Modified"
	StatContentMD5              = "Content-Md5"
	StatCRC64                   = "X-Oss-Hash-Crc64ecma"
)

// the elements show in hash file
const (
	HashCRC64      = "CRC64-ECMA"
	HashMD5        = "MD5"
	HashContentMD5 = "Content-MD5"
)

const (
	updateEndpoint       string = "oss-cn-hangzhou.aliyuncs.com"
	updateBucket                = "ossutil-version-update"
	updateVersionObject         = "ossutilversion"
	updateBinaryLinux           = "ossutil"
	updateBinaryWindow32        = "ossutil32.exe"
	updateBinaryWindow64        = "ossutil64.exe"
	updateBinaryMac64           = "ossutilmac64"
	updateTmpVersionFile        = ".ossutil_tmp_vsersion"
)

// global public variable
const (
	Package                 string = "ossutil"
	ChannelBuf              int    = 1000
	Version                 string = "1.0.0.Beta2"
	DefaultEndpoint         string = "oss.aliyuncs.com"
	DefaultLanguage                = "CH"
	EnglishLanguage                = "EN"
	Scheme                  string = "oss"
	DefaultConfigFile              = "~" + string(os.PathSeparator) + ".ossutilconfig"
	MaxUint                        = ^uint(0)
	MaxInt                         = int(MaxUint >> 1)
	MaxUint64                      = ^uint64(0)
	MaxInt64                       = int64(MaxUint64 >> 1)
	ReportPrefix                   = "ossutil_report_"
	ReportSuffix                   = ".report"
	DefaultOutputDir               = "ossutil_output"
	CheckpointDir                  = ".ossutil_checkpoint"
	CheckpointSep                  = "---"
	SnapshotConnector              = "==>"
	SnapshotSep                    = "#"
	MaxPartNum                     = 10000
	MaxIdealPartNum                = MaxPartNum / 10
	MinIdealPartNum                = MaxPartNum / 500
	MaxIdealPartSize               = 524288000
	MinIdealPartSize               = 1048576
	DefaultBigFileThreshold        = 104857600
	MaxBigFileThreshold            = MaxInt64
	MinBigFileThreshold            = 0
	RetryTimes              int    = 3
	MaxRetryTimes           int64  = 500
	MinRetryTimes           int64  = 1
	Routines                int    = 5
	MaxRoutines             int64  = 10000
	MinRoutines             int64  = 1
	MaxParallel             int64  = 10000
	MinParallel             int64  = 1
	DefaultHashType         string = "crc64"
	MD5HashType             string = "md5"
	LogFilePrefix                  = "ossutil_log_"
)

const (
	objectType    = 0x00000001
	multipartType = 0x00000010
	allType       = objectType | multipartType // marker for objects
	bucketType    = 0x10000000
)
<|endoftext|>