<commit_before>package admin

import (
	"fmt"
	"github.com/qor/qor"
	"os"
	"path"
	"path/filepath"
	"regexp"

	"text/template"
)

var layouts = map[string]*template.Template{}
var templates = map[string]*template.Template{}
var tmplSuffix = regexp.MustCompile(`(\.tmpl)?$`)
var viewDirs = []string{}

func isExistingDir(pth string) bool {
	fi, err := os.Stat(pth)
	if err != nil {
		return false
	}
	return fi.Mode().IsDir()
}

func init() {
	if root := os.Getenv("WEB_ROOT"); root != "" {
		if dir := path.Join(root, "templates/qor"); isExistingDir(dir) {
			viewDirs = append(viewDirs, dir)
		}
	}

	if dir, err := filepath.Abs("templates/qor"); err == nil && isExistingDir(dir) {
		viewDirs = append(viewDirs, dir)
	}

	if dir := path.Join(os.Getenv("GOROOT"), "site/src/github.com/qor/qor/admin/templates"); isExistingDir(dir) {
		viewDirs = append(viewDirs, dir)
	}
}

func (admin *Admin) Render(str string, context *qor.Context) {
	var tmpl *template.Template

	cacheKey := path.Join(context.ResourceName, str)
	if t, ok := templates[cacheKey]; !ok || true {
		str = tmplSuffix.ReplaceAllString(str, ".tmpl")

		// parse layout
		paths := []string{}
		for _, p := range []string{path.Join("resources", context.ResourceName), path.Join("themes", "default"), "."} {
			for _, d := range viewDirs {
				if isExistingDir(path.Join(d, p)) {
					paths = append(paths, path.Join(d, p))
				}
			}
		}

		for _, f := range []string{"layout.tmpl", str} {
			for _, p := range paths {
				if _, err := os.Stat(path.Join(p, f)); !os.IsNotExist(err) {
					if tmpl, err = tmpl.ParseFiles(path.Join(p, f)); err != nil {
						fmt.Println(err)
					}
					break
				}
			}
		}

		for _, name := range []string{"header", "footer"} {
			if tmpl.Lookup(name) == nil {
				for _, p := range paths {
					if _, err := os.Stat(path.Join(p, name+".tmpl")); !os.IsNotExist(err) {
						if tmpl, err = tmpl.ParseFiles(path.Join(p, name+".tmpl")); err != nil {
							fmt.Println(err)
						}
						break
					}
				}
			}
		}

		templates[cacheKey] = tmpl
	} else {
		tmpl = t
	}

	if tmpl != nil {
		tmpl.Execute(context.Writer, context)
	}
}
<commit_msg>Display error message if have<commit_after>package admin

import (
	"fmt"
	"github.com/qor/qor"
	"os"
	"path"
	"path/filepath"
	"regexp"

	"text/template"
)

var layouts = map[string]*template.Template{}
var templates = map[string]*template.Template{}
var tmplSuffix = regexp.MustCompile(`(\.tmpl)?$`)
var viewDirs = []string{}

func isExistingDir(pth string) bool {
	fi, err := os.Stat(pth)
	if err != nil {
		return false
	}
	return fi.Mode().IsDir()
}

func init() {
	if root := os.Getenv("WEB_ROOT"); root != "" {
		if dir := path.Join(root, "templates/qor"); isExistingDir(dir) {
			viewDirs = append(viewDirs, dir)
		}
	}

	if dir, err := filepath.Abs("templates/qor"); err == nil && isExistingDir(dir) {
		viewDirs = append(viewDirs, dir)
	}

	if dir := path.Join(os.Getenv("GOROOT"), "site/src/github.com/qor/qor/admin/templates"); isExistingDir(dir) {
		viewDirs = append(viewDirs, dir)
	}
}

func (admin *Admin) Render(str string, context *qor.Context) {
	var tmpl *template.Template

	cacheKey := path.Join(context.ResourceName, str)
	if t, ok := templates[cacheKey]; !ok || true {
		str = tmplSuffix.ReplaceAllString(str, ".tmpl")

		// parse layout
		paths := []string{}
		for _, p := range []string{path.Join("resources", context.ResourceName), path.Join("themes", "default"), "."} {
			for _, d := range viewDirs {
				if isExistingDir(path.Join(d, p)) {
					paths = append(paths, path.Join(d, p))
				}
			}
		}

		for _, f := range []string{"layout.tmpl", str} {
			for _, p := range paths {
				if _, err := os.Stat(path.Join(p, f)); !os.IsNotExist(err) {
					if tmpl, err = tmpl.ParseFiles(path.Join(p, f)); err != nil {
						fmt.Println(err)
					}
					break
				}
			}
		}

		for _, name := range []string{"header", "footer"} {
			if tmpl.Lookup(name) == nil {
				for _, p := range paths {
					if _, err := os.Stat(path.Join(p, name+".tmpl")); !os.IsNotExist(err) {
						if tmpl, err = tmpl.ParseFiles(path.Join(p, name+".tmpl")); err != nil {
							fmt.Println(err)
						}
						break
					}
				}
			}
		}

		templates[cacheKey] = tmpl
	} else {
		tmpl = t
	}

	if tmpl != nil {
		if err := tmpl.Execute(context.Writer, context); err != nil {
			fmt.Println(err)
		}
	}
}
<|endoftext|>


<commit_before>package api

import (
	"github.com/aws/aws-sdk-go/aws"
	"github.com/jobtalk/thor/lib"
	"github.com/jobtalk/thor/lib/setting"
)

// serviceが存在しない時はサービスを作る
// 存在するときはアップデートする
func Deploy(awsConfig *aws.Config, s *setting.Setting) (interface{}, error) {
	var result = []interface{}{}
	if s.ECS != nil {
		resultMkELB, err := MkELB(awsConfig, s.ELB)
		if err != nil {
			return nil, err
		}
		result = append(result, resultMkELB)
	}
	if s.ECS.TaskDefinition != nil {
		resultTaskDefinition, err := lib.RegisterTaskDefinition(awsConfig, s.ECS.TaskDefinition)
		if err != nil {
			return nil, err
		}
		result = append(result, resultTaskDefinition)
	}
	resultUpsert, err := lib.UpsertService(awsConfig, s.ECS.Service)
	if err != nil {
		return nil, err
	}

	return append(result, resultUpsert), nil
}
<commit_msg>elbの設定がnilの時mkelbを走らせない<commit_after>package api

import (
	"github.com/aws/aws-sdk-go/aws"
	"github.com/jobtalk/thor/lib"
	"github.com/jobtalk/thor/lib/setting"
)

// serviceが存在しない時はサービスを作る
// 存在するときはアップデートする
func Deploy(awsConfig *aws.Config, s *setting.Setting) (interface{}, error) {
	var result = []interface{}{}
	if s.ECS != nil && s.ELB != nil {
		resultMkELB, err := MkELB(awsConfig, s.ELB)
		if err != nil {
			return nil, err
		}
		result = append(result, resultMkELB)
	}
	if s.ECS.TaskDefinition != nil {
		resultTaskDefinition, err := lib.RegisterTaskDefinition(awsConfig, s.ECS.TaskDefinition)
		if err != nil {
			return nil, err
		}
		result = append(result, resultTaskDefinition)
	}
	resultUpsert, err := lib.UpsertService(awsConfig, s.ECS.Service)
	if err != nil {
		return nil, err
	}

	return append(result, resultUpsert), nil
}
<|endoftext|>


<commit_before>package api

import (
	"fmt"
	"bytes"
	"os/exec"
	pathlib "path"
)

func Pull(remote string) string {
	//pkg := pull(remote)
	//pkg = decrypt(pkg, passphrase)
	//thaw(pkg)
	//return pkg.dirs()
	return ""
}

func Push(remote string) string {
	//pkg := freeze()
	//pkg = encrypt(pkg, passphrase)
	//push(pkg, remote)
	//return remote
	return ""
}

func freeze(name string) (pkg string) {
	var stderr bytes.Buffer
	pkg = pathlib.Join(Settings.Freezer, name)
	tar_gpg := fmt.Sprintf("tar czvpfC - %s %s --exclude=\"\\./denv/\\.*\" | gpg --symmetric --cipher-algo aes256 -o %s", UserHome(), pathlib.Base(Settings.DenvHome), pkg)
	cmd := exec.Command("bash", "-c", tar_gpg)
	cmd.Stderr = &stderr
	err := cmd.Run()
	if err != nil {
		err = fmt.Errorf("%s\n%s\n", stderr.String(), tar_gpg)
	}
	fmt.Println(tar_gpg)
	check(err)
	return
}

func thaw(pkg string) {
	var stderr bytes.Buffer
	untar_gpg := fmt.Sprintf("gpg -d %s | tar xzvf - -C %s", pkg, UserHome())
	cmd := exec.Command("bash", "-c", untar_gpg)
	cmd.Stderr = &stderr
	err := cmd.Run()
	if err != nil {
		err = fmt.Errorf("%s\n%s\n", stderr.String(), untar_gpg)
	}
	fmt.Println(untar_gpg)
	check(err)
	//return thawed denvs
}

func compress(paths []string) string {
	return ""

}

func decompress(path string) []string {
	//cmd := fmt.Sprintf("gpg -d %s | tar xzvf -C %s", path, Settings.DenvHome)
	return []string{}

}

func encrypt(pkg string, passphrase string) string {
	return ""

}

func decrypt(pkg string, passphrase string) string {
	return ""

}
<commit_msg>working on git integration<commit_after>package api

import (
	"fmt"
	"bytes"
	"os/exec"
	pathlib "path"
)

func Pull(remote string) string {
	//pkg := pull(remote)
	//pkg = decrypt(pkg, passphrase)
	//thaw(pkg)
	//return pkg.dirs()
	return ""
}

func Push(remote string) string {
	Info.Repository.Add(".")
	Info.Repository.Commit("freeze")
	Info.Repository.SetRemote("denv", remote)
	Info.Repository.Push("denv", "master")
	//pkg := freeze()
	//pkg = encrypt(pkg, passphrase)
	//push(pkg, remote)
	//return remote
	return ""
}

func freeze(name string) (pkg string) {
	var stderr bytes.Buffer
	pkg = pathlib.Join(Settings.Freezer, name)
	tar_gpg := fmt.Sprintf("tar czvpfC - %s %s --exclude=\"\\./denv/\\.*\" | gpg --symmetric --cipher-algo aes256 -o %s", UserHome(), pathlib.Base(Settings.DenvHome), pkg)
	cmd := exec.Command("bash", "-c", tar_gpg)
	cmd.Stderr = &stderr
	err := cmd.Run()
	if err != nil {
		err = fmt.Errorf("%s\n%s\n", stderr.String(), tar_gpg)
	}
	fmt.Println(tar_gpg)
	check(err)
	return
}

func thaw(pkg string) {
	var stderr bytes.Buffer
	untar_gpg := fmt.Sprintf("gpg -d %s | tar xzvf - -C %s", pkg, UserHome())
	cmd := exec.Command("bash", "-c", untar_gpg)
	cmd.Stderr = &stderr
	err := cmd.Run()
	if err != nil {
		err = fmt.Errorf("%s\n%s\n", stderr.String(), untar_gpg)
	}
	fmt.Println(untar_gpg)
	check(err)
	//return thawed denvs
}

func compress(paths []string) string {
	return ""

}

func decompress(path string) []string {
	//cmd := fmt.Sprintf("gpg -d %s | tar xzvf -C %s", path, Settings.DenvHome)
	return []string{}

}

func encrypt(pkg string, passphrase string) string {
	return ""

}

func decrypt(pkg string, passphrase string) string {
	return ""

}
<|endoftext|>


<commit_before>package testdata

func l2() {
	for {
	}
}

func l3() {
	for {
		for {
		}
	}
}

func l2range() {
	for range []struct{}{} {
	}
}

func l4() {
	for {
	}
	for true {
	}
	for ;; {
	}
}
<commit_msg>testdata: use for loop with full ForClause to prevent gofmt from reformatting it<commit_after>package testdata

func l2() {
	for {
	}
}

func l3() {
	for {
		for {
		}
	}
}

func l2range() {
	for range []struct{}{} {
	}
}

func l4() {
	for {
	}
	for true {
	}
	for i := 0; i < 10; i++ {
	}
}
<|endoftext|>


<commit_before>package experimental

import (
	"fmt"

	"code.cloudfoundry.org/cli/api/cloudcontroller/ccv3/constant"
	"code.cloudfoundry.org/cli/integration/helpers"
	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
	. "github.com/onsi/gomega/gbytes"
	. "github.com/onsi/gomega/gexec"
	. "github.com/onsi/gomega/ghttp"
)

var _ = Describe("v3-restart-app-instance command", func() {
	var (
		orgName   string
		spaceName string
		appName   string
	)

	BeforeEach(func() {
		orgName = helpers.NewOrgName()
		spaceName = helpers.NewSpaceName()
		appName = helpers.PrefixedRandomName("app")
	})

	Context("when --help flag is set", func() {
		It("Displays command usage to output", func() {
			session := helpers.CF("v3-restart-app-instance", "--help")
			Eventually(session).Should(Say("NAME:"))
			Eventually(session).Should(Say("v3-restart-app-instance - Terminate, then instantiate an app instance"))
			Eventually(session).Should(Say("USAGE:"))
			Eventually(session).Should(Say(`cf v3-restart-app-instance APP_NAME INDEX [--process PROCESS]`))
			Eventually(session).Should(Say("SEE ALSO:"))
			Eventually(session).Should(Say("v3-restart"))
			Eventually(session).Should(Exit(0))
		})
	})

	Context("when the app name is not provided", func() {
		It("tells the user that the app name is required, prints help text, and exits 1", func() {
			session := helpers.CF("v3-restart-app-instance")

			Eventually(session.Err).Should(Say("Incorrect Usage: the required arguments `APP_NAME` and `INDEX` were not provided"))
			Eventually(session).Should(Say("NAME:"))
			Eventually(session).Should(Exit(1))
		})
	})

	Context("when the index is not provided", func() {
		It("tells the user that the index is required, prints help text, and exits 1", func() {
			session := helpers.CF("v3-restart-app-instance", appName)

			Eventually(session.Err).Should(Say("Incorrect Usage: the required argument `INDEX` was not provided"))
			Eventually(session).Should(Say("NAME:"))
			Eventually(session).Should(Exit(1))
		})
	})

	It("displays the experimental warning", func() {
		session := helpers.CF("v3-restart-app-instance", appName, "1")
		Eventually(session.Err).Should(Say("This command is in EXPERIMENTAL stage and may change without notice"))
		Eventually(session).Should(Exit())
	})

	Context("when the environment is not setup correctly", func() {
		Context("when no API endpoint is set", func() {
			BeforeEach(func() {
				helpers.UnsetAPI()
			})

			It("fails with no API endpoint set message", func() {
				session := helpers.CF("v3-restart-app-instance", appName, "1")
				Eventually(session).Should(Say("FAILED"))
				Eventually(session.Err).Should(Say("No API endpoint set. Use 'cf login' or 'cf api' to target an endpoint."))
				Eventually(session).Should(Exit(1))
			})
		})

		Context("when the v3 api does not exist", func() {
			var server *Server

			BeforeEach(func() {
				server = helpers.StartAndTargetServerWithoutV3API()
			})

			AfterEach(func() {
				server.Close()
			})

			It("fails with error message that the minimum version is not met", func() {
				session := helpers.CF("v3-restart-app-instance", appName, "1")
				Eventually(session).Should(Say("FAILED"))
				Eventually(session.Err).Should(Say("This command requires CF API version 3\\.27\\.0 or higher\\."))
				Eventually(session).Should(Exit(1))
			})
		})

		Context("when the v3 api version is lower than the minimum version", func() {
			var server *Server

			BeforeEach(func() {
				server = helpers.StartAndTargetServerWithAPIVersions(helpers.DefaultV2Version, "3.0.0")
			})

			AfterEach(func() {
				server.Close()
			})

			It("fails with error message that the minimum version is not met", func() {
				session := helpers.CF("v3-restart-app-instance", appName, "1")
				Eventually(session).Should(Say("FAILED"))
				Eventually(session.Err).Should(Say("This command requires CF API version 3\\.27\\.0 or higher\\."))
				Eventually(session).Should(Exit(1))
			})
		})

		Context("when not logged in", func() {
			BeforeEach(func() {
				helpers.LogoutCF()
			})

			It("fails with not logged in message", func() {
				session := helpers.CF("v3-restart-app-instance", appName, "1")
				Eventually(session).Should(Say("FAILED"))
				Eventually(session.Err).Should(Say("Not logged in. Use 'cf login' to log in."))
				Eventually(session).Should(Exit(1))
			})
		})

		Context("when there is no org set", func() {
			BeforeEach(func() {
				helpers.LogoutCF()
				helpers.LoginCF()
			})

			It("fails with no targeted org error message", func() {
				session := helpers.CF("v3-restart-app-instance", appName, "1")
				Eventually(session).Should(Say("FAILED"))
				Eventually(session.Err).Should(Say("No org targeted, use 'cf target -o ORG' to target an org."))
				Eventually(session).Should(Exit(1))
			})
		})

		Context("when there is no space set", func() {
			BeforeEach(func() {
				helpers.LogoutCF()
				helpers.LoginCF()
				helpers.TargetOrg(ReadOnlyOrg)
			})

			It("fails with no targeted space error message", func() {
				session := helpers.CF("v3-restart-app-instance", appName, "1")
				Eventually(session).Should(Say("FAILED"))
				Eventually(session.Err).Should(Say("No space targeted, use 'cf target -s SPACE' to target a space."))
				Eventually(session).Should(Exit(1))
			})
		})
	})

	Context("when the environment is setup correctly", func() {
		var userName string

		BeforeEach(func() {
			helpers.SetupCF(orgName, spaceName)
			userName, _ = helpers.GetCredentials()
		})

		AfterEach(func() {
			helpers.QuickDeleteOrg(orgName)
		})

		Context("when app does not exist", func() {
			It("fails with error", func() {
				session := helpers.CF("v3-restart-app-instance", appName, "0", "--process", "some-process")
				Eventually(session).Should(Say("Restarting instance 0 of process some-process of app %s in org %s / space %s as %s", appName, orgName, spaceName, userName))
				Eventually(session.Err).Should(Say("App %s not found", appName))
				Eventually(session).Should(Exit(1))
			})
		})

		Context("when app exists", func() {
			BeforeEach(func() {
				helpers.WithProcfileApp(func(appDir string) {
					Eventually(helpers.CustomCF(helpers.CFEnv{WorkingDirectory: appDir}, "v3-push", appName)).Should(Exit(0))
				})
			})

			Context("when process type is not provided", func() {
				It("defaults to web process", func() {
					appOutputSession := helpers.CF("v3-app", appName)
					Eventually(appOutputSession).Should(Exit(0))
					firstAppTable := helpers.ParseV3AppProcessTable(appOutputSession.Out.Contents())

					session := helpers.CF("v3-restart-app-instance", appName, "0")
					Eventually(session).Should(Say("Restarting instance 0 of process web of app %s in org %s / space %s as %s", appName, orgName, spaceName, userName))
					Eventually(session).Should(Say("OK"))
					Eventually(session).Should(Exit(0))

					Eventually(func() string {
						var restartedAppTable helpers.AppTable
						Eventually(func() string {
							appOutputSession := helpers.CF("v3-app", appName)
							Eventually(appOutputSession).Should(Exit(0))
							restartedAppTable = helpers.ParseV3AppProcessTable(appOutputSession.Out.Contents())

							if len(restartedAppTable.Processes) > 0 {
								return restartedAppTable.Processes[0].Type
							}

							return ""
						}).Should(Equal(`web`))
						Expect(restartedAppTable.Processes[0].Instances).ToNot(BeEmpty())
						return restartedAppTable.Processes[0].Instances[0].Since
					}).ShouldNot(Equal(firstAppTable.Processes[0].Instances[0].Since))
				})
			})

			Context("when a process type is provided", func() {
				Context("when the process type does not exist", func() {
					It("fails with error", func() {
						session := helpers.CF("v3-restart-app-instance", appName, "0", "--process", "unknown-process")
						Eventually(session).Should(Say("Restarting instance 0 of process unknown-process of app %s in org %s / space %s as %s", appName, orgName, spaceName, userName))
						Eventually(session.Err).Should(Say("Process unknown-process not found"))
						Eventually(session).Should(Exit(1))
					})
				})

				Context("when the process type exists", func() {
					Context("when instance index exists", func() {
						findConsoleProcess := func(appTable helpers.AppTable) (helpers.AppProcessTable, bool) {
							for _, process := range appTable.Processes {
								if process.Type == "console" {
									return process, true
								}
							}
							return helpers.AppProcessTable{}, false
						}

						It("defaults to requested process", func() {
							By("scaling worker process to 1 instance")
							session := helpers.CF("v3-scale", appName, "--process", "console", "-i", "1")
							Eventually(session).Should(Exit(0))

							By("waiting for worker process to come up")
							var firstAppTableConsoleProcess helpers.AppProcessTable
							Eventually(func() string {
								appOutputSession := helpers.CF("v3-app", appName)
								Eventually(appOutputSession).Should(Exit(0))
								firstAppTable := helpers.ParseV3AppProcessTable(appOutputSession.Out.Contents())

								var found bool
								firstAppTableConsoleProcess, found = findConsoleProcess(firstAppTable)
								Expect(found).To(BeTrue())
								return fmt.Sprintf("%s, %s", firstAppTableConsoleProcess.Type, firstAppTableConsoleProcess.InstanceCount)
							}).Should(MatchRegexp(`console, 1/1`))

							By("restarting worker process instance")
							session = helpers.CF("v3-restart-app-instance", appName, "0", "--process", "console")
							Eventually(session).Should(Say("Restarting instance 0 of process console of app %s in org %s / space %s as %s", appName, orgName, spaceName, userName))
							Eventually(session).Should(Say("OK"))
							Eventually(session).Should(Exit(0))

							By("waiting for restarted process instance to come up")
							Eventually(func() string {
								var restartedAppTableConsoleProcess helpers.AppProcessTable

								Eventually(func() string {
									appOutputSession := helpers.CF("v3-app", appName)
									Eventually(appOutputSession).Should(Exit(0))

									restartedAppTable := helpers.ParseV3AppProcessTable(appOutputSession.Out.Contents())
									var found bool
									restartedAppTableConsoleProcess, found = findConsoleProcess(restartedAppTable)
									Expect(found).To(BeTrue())

									return fmt.Sprintf("%s, %s", firstAppTableConsoleProcess.Type, firstAppTableConsoleProcess.InstanceCount)
								}).Should(MatchRegexp(`console, 1/1`))

								return restartedAppTableConsoleProcess.Instances[0].Since
							}).ShouldNot(Equal(firstAppTableConsoleProcess.Instances[0].Since))
						})
					})

					Context("when instance index does not exist", func() {
						It("fails with error", func() {
							session := helpers.CF("v3-restart-app-instance", appName, "42", "--process", constant.ProcessTypeWeb)
							Eventually(session).Should(Say("Restarting instance 42 of process web of app %s in org %s / space %s as %s", appName, orgName, spaceName, userName))
							Eventually(session.Err).Should(Say("Instance 42 of process web not found"))
							Eventually(session).Should(Exit(1))
						})
					})
				})
			})
		})
	})
})
<commit_msg>fix bad integration test assertion<commit_after>package experimental

import (
	"fmt"

	"code.cloudfoundry.org/cli/api/cloudcontroller/ccv3/constant"
	"code.cloudfoundry.org/cli/integration/helpers"
	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
	. "github.com/onsi/gomega/gbytes"
	. "github.com/onsi/gomega/gexec"
	. "github.com/onsi/gomega/ghttp"
)

var _ = Describe("v3-restart-app-instance command", func() {
	var (
		orgName   string
		spaceName string
		appName   string
	)

	BeforeEach(func() {
		orgName = helpers.NewOrgName()
		spaceName = helpers.NewSpaceName()
		appName = helpers.PrefixedRandomName("app")
	})

	Context("when --help flag is set", func() {
		It("Displays command usage to output", func() {
			session := helpers.CF("v3-restart-app-instance", "--help")
			Eventually(session).Should(Say("NAME:"))
			Eventually(session).Should(Say("v3-restart-app-instance - Terminate, then instantiate an app instance"))
			Eventually(session).Should(Say("USAGE:"))
			Eventually(session).Should(Say(`cf v3-restart-app-instance APP_NAME INDEX [--process PROCESS]`))
			Eventually(session).Should(Say("SEE ALSO:"))
			Eventually(session).Should(Say("v3-restart"))
			Eventually(session).Should(Exit(0))
		})
	})

	Context("when the app name is not provided", func() {
		It("tells the user that the app name is required, prints help text, and exits 1", func() {
			session := helpers.CF("v3-restart-app-instance")

			Eventually(session.Err).Should(Say("Incorrect Usage: the required arguments `APP_NAME` and `INDEX` were not provided"))
			Eventually(session).Should(Say("NAME:"))
			Eventually(session).Should(Exit(1))
		})
	})

	Context("when the index is not provided", func() {
		It("tells the user that the index is required, prints help text, and exits 1", func() {
			session := helpers.CF("v3-restart-app-instance", appName)

			Eventually(session.Err).Should(Say("Incorrect Usage: the required argument `INDEX` was not provided"))
			Eventually(session).Should(Say("NAME:"))
			Eventually(session).Should(Exit(1))
		})
	})

	It("displays the experimental warning", func() {
		session := helpers.CF("v3-restart-app-instance", appName, "1")
		Eventually(session.Err).Should(Say("This command is in EXPERIMENTAL stage and may change without notice"))
		Eventually(session).Should(Exit())
	})

	Context("when the environment is not setup correctly", func() {
		Context("when no API endpoint is set", func() {
			BeforeEach(func() {
				helpers.UnsetAPI()
			})

			It("fails with no API endpoint set message", func() {
				session := helpers.CF("v3-restart-app-instance", appName, "1")
				Eventually(session).Should(Say("FAILED"))
				Eventually(session.Err).Should(Say("No API endpoint set. Use 'cf login' or 'cf api' to target an endpoint."))
				Eventually(session).Should(Exit(1))
			})
		})

		Context("when the v3 api does not exist", func() {
			var server *Server

			BeforeEach(func() {
				server = helpers.StartAndTargetServerWithoutV3API()
			})

			AfterEach(func() {
				server.Close()
			})

			It("fails with error message that the minimum version is not met", func() {
				session := helpers.CF("v3-restart-app-instance", appName, "1")
				Eventually(session).Should(Say("FAILED"))
				Eventually(session.Err).Should(Say("This command requires CF API version 3\\.27\\.0 or higher\\."))
				Eventually(session).Should(Exit(1))
			})
		})

		Context("when the v3 api version is lower than the minimum version", func() {
			var server *Server

			BeforeEach(func() {
				server = helpers.StartAndTargetServerWithAPIVersions(helpers.DefaultV2Version, "3.0.0")
			})

			AfterEach(func() {
				server.Close()
			})

			It("fails with error message that the minimum version is not met", func() {
				session := helpers.CF("v3-restart-app-instance", appName, "1")
				Eventually(session).Should(Say("FAILED"))
				Eventually(session.Err).Should(Say("This command requires CF API version 3\\.27\\.0 or higher\\."))
				Eventually(session).Should(Exit(1))
			})
		})

		Context("when not logged in", func() {
			BeforeEach(func() {
				helpers.LogoutCF()
			})

			It("fails with not logged in message", func() {
				session := helpers.CF("v3-restart-app-instance", appName, "1")
				Eventually(session).Should(Say("FAILED"))
				Eventually(session.Err).Should(Say("Not logged in. Use 'cf login' to log in."))
				Eventually(session).Should(Exit(1))
			})
		})

		Context("when there is no org set", func() {
			BeforeEach(func() {
				helpers.LogoutCF()
				helpers.LoginCF()
			})

			It("fails with no targeted org error message", func() {
				session := helpers.CF("v3-restart-app-instance", appName, "1")
				Eventually(session).Should(Say("FAILED"))
				Eventually(session.Err).Should(Say("No org targeted, use 'cf target -o ORG' to target an org."))
				Eventually(session).Should(Exit(1))
			})
		})

		Context("when there is no space set", func() {
			BeforeEach(func() {
				helpers.LogoutCF()
				helpers.LoginCF()
				helpers.TargetOrg(ReadOnlyOrg)
			})

			It("fails with no targeted space error message", func() {
				session := helpers.CF("v3-restart-app-instance", appName, "1")
				Eventually(session).Should(Say("FAILED"))
				Eventually(session.Err).Should(Say("No space targeted, use 'cf target -s SPACE' to target a space."))
				Eventually(session).Should(Exit(1))
			})
		})
	})

	Context("when the environment is setup correctly", func() {
		var userName string

		BeforeEach(func() {
			helpers.SetupCF(orgName, spaceName)
			userName, _ = helpers.GetCredentials()
		})

		AfterEach(func() {
			helpers.QuickDeleteOrg(orgName)
		})

		Context("when app does not exist", func() {
			It("fails with error", func() {
				session := helpers.CF("v3-restart-app-instance", appName, "0", "--process", "some-process")
				Eventually(session).Should(Say("Restarting instance 0 of process some-process of app %s in org %s / space %s as %s", appName, orgName, spaceName, userName))
				Eventually(session.Err).Should(Say("App %s not found", appName))
				Eventually(session).Should(Exit(1))
			})
		})

		Context("when app exists", func() {
			BeforeEach(func() {
				helpers.WithProcfileApp(func(appDir string) {
					Eventually(helpers.CustomCF(helpers.CFEnv{WorkingDirectory: appDir}, "v3-push", appName)).Should(Exit(0))
				})
			})

			Context("when process type is not provided", func() {
				It("defaults to web process", func() {
					appOutputSession := helpers.CF("v3-app", appName)
					Eventually(appOutputSession).Should(Exit(0))
					firstAppTable := helpers.ParseV3AppProcessTable(appOutputSession.Out.Contents())

					session := helpers.CF("v3-restart-app-instance", appName, "0")
					Eventually(session).Should(Say("Restarting instance 0 of process web of app %s in org %s / space %s as %s", appName, orgName, spaceName, userName))
					Eventually(session).Should(Say("OK"))
					Eventually(session).Should(Exit(0))

					Eventually(func() string {
						var restartedAppTable helpers.AppTable
						Eventually(func() string {
							appOutputSession := helpers.CF("v3-app", appName)
							Eventually(appOutputSession).Should(Exit(0))
							restartedAppTable = helpers.ParseV3AppProcessTable(appOutputSession.Out.Contents())

							if len(restartedAppTable.Processes) > 0 {
								return restartedAppTable.Processes[0].Type
							}

							return ""
						}).Should(Equal(`web`))
						Expect(restartedAppTable.Processes[0].Instances).ToNot(BeEmpty())
						return restartedAppTable.Processes[0].Instances[0].Since
					}).ShouldNot(Equal(firstAppTable.Processes[0].Instances[0].Since))
				})
			})

			Context("when a process type is provided", func() {
				Context("when the process type does not exist", func() {
					It("fails with error", func() {
						session := helpers.CF("v3-restart-app-instance", appName, "0", "--process", "unknown-process")
						Eventually(session).Should(Say("Restarting instance 0 of process unknown-process of app %s in org %s / space %s as %s", appName, orgName, spaceName, userName))
						Eventually(session.Err).Should(Say("Process unknown-process not found"))
						Eventually(session).Should(Exit(1))
					})
				})

				Context("when the process type exists", func() {
					Context("when instance index exists", func() {
						findConsoleProcess := func(appTable helpers.AppTable) (helpers.AppProcessTable, bool) {
							for _, process := range appTable.Processes {
								if process.Type == "console" {
									return process, true
								}
							}
							return helpers.AppProcessTable{}, false
						}

						It("defaults to requested process", func() {
							By("scaling worker process to 1 instance")
							session := helpers.CF("v3-scale", appName, "--process", "console", "-i", "1")
							Eventually(session).Should(Exit(0))

							By("waiting for worker process to come up")
							var firstAppTableConsoleProcess helpers.AppProcessTable
							Eventually(func() string {
								appOutputSession := helpers.CF("v3-app", appName)
								Eventually(appOutputSession).Should(Exit(0))
								firstAppTable := helpers.ParseV3AppProcessTable(appOutputSession.Out.Contents())

								var found bool
								firstAppTableConsoleProcess, found = findConsoleProcess(firstAppTable)
								Expect(found).To(BeTrue())
								return fmt.Sprintf("%s, %s", firstAppTableConsoleProcess.Type, firstAppTableConsoleProcess.InstanceCount)
							}).Should(MatchRegexp(`console, 1/1`))

							By("restarting worker process instance")
							session = helpers.CF("v3-restart-app-instance", appName, "0", "--process", "console")
							Eventually(session).Should(Say("Restarting instance 0 of process console of app %s in org %s / space %s as %s", appName, orgName, spaceName, userName))
							Eventually(session).Should(Say("OK"))
							Eventually(session).Should(Exit(0))

							By("waiting for restarted process instance to come up")
							Eventually(func() string {
								var restartedAppTableConsoleProcess helpers.AppProcessTable

								Eventually(func() string {
									appOutputSession := helpers.CF("v3-app", appName)
									Eventually(appOutputSession).Should(Exit(0))

									restartedAppTable := helpers.ParseV3AppProcessTable(appOutputSession.Out.Contents())
									var found bool
									restartedAppTableConsoleProcess, found = findConsoleProcess(restartedAppTable)
									Expect(found).To(BeTrue())

									return fmt.Sprintf("%s, %s", restartedAppTableConsoleProcess.Type, restartedAppTableConsoleProcess.InstanceCount)
								}).Should(MatchRegexp(`console, 1/1`))

								return restartedAppTableConsoleProcess.Instances[0].Since
							}).ShouldNot(Equal(firstAppTableConsoleProcess.Instances[0].Since))
						})
					})

					Context("when instance index does not exist", func() {
						It("fails with error", func() {
							session := helpers.CF("v3-restart-app-instance", appName, "42", "--process", constant.ProcessTypeWeb)
							Eventually(session).Should(Say("Restarting instance 42 of process web of app %s in org %s / space %s as %s", appName, orgName, spaceName, userName))
							Eventually(session.Err).Should(Say("Instance 42 of process web not found"))
							Eventually(session).Should(Exit(1))
						})
					})
				})
			})
		})
	})
})
<|endoftext|>


<commit_before>// Copyright 2015-2016 trivago GmbH
//
// 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 format

import (
	"encoding/json"
	"github.com/trivago/gollum/core"
	"github.com/trivago/gollum/core/log"
	"github.com/trivago/gollum/shared"
	"strings"
	"time"
)

// ProcessJSON formatter plugin
// ProcessJSON is a formatter that allows modifications to fields of a given
// JSON message. The message is modified and returned again as JSON.
// Configuration example
//
//  - "stream.Broadcast":
//    Formatter: "format.ProcessJSON"
//    ProcessJSONDataFormatter: "format.Forward"
//    ProcessJSONDirectives:
//      - "host:split: :host:@timestamp"
//      - "@timestamp:time:20060102150405:2006-01-02 15\\:04\\:05"
//      - "error:replace:°:\n"
//      - "text:trim: \t"
//      - "foo:rename:bar"
//		- "foobar:remove"
//    ProcessJSONTrimValues: true
//
// ProcessJSONDataFormatter formatter that will be applied before
// ProcessJSONDirectives are processed.
//
// ProcessJSONDirectives defines the action to be applied to the json payload.
// Directives are processed in order of appearance.
// The directives have to be given in the form of key:operation:parameters, where
// operation can be one of the following:
// - split:<string>:{<key>:<key>:...} Split the value by a string and set the
// resulting array elements to the given fields in order of appearance.
// - replace:<old>:<new> replace a given string in the value with a new one
// - trim:<characters> remove the given characters (not string!) from the start
// and end of the value
// - rename:<old>:<new> rename a given field
// - remove remove a given field
// - timestamp:<read>:<write> read a timestamp and transform it into another
// format
//
// ProcessJSONTrimValues will trim whitspaces from all values if enabled.
// Enabled by default.
type ProcessJSON struct {
	base       core.Formatter
	directives []transformDirective
	trimValues bool
}

type transformDirective struct {
	key        string
	operation  string
	parameters []string
}

type valueMap map[string]string

func init() {
	shared.TypeRegistry.Register(ProcessJSON{})
}

// Configure initializes this formatter with values from a plugin config.
func (format *ProcessJSON) Configure(conf core.PluginConfig) error {
	plugin, err := core.NewPluginWithType(conf.GetString("ProcessJSONDataFormatter", "format.Forward"), conf)
	if err != nil {
		return err
	}
	directives := conf.GetStringArray("ProcessJSONDirectives", []string{})

	format.base = plugin.(core.Formatter)
	format.directives = make([]transformDirective, 0, len(directives))
	format.trimValues = conf.GetBool("ProcessJSONTrimValues", true)

	for _, directive := range directives {
		directive := strings.Replace(directive, "\\:", "\r", -1)
		parts := strings.Split(directive, ":")
		for i, value := range parts {
			parts[i] = strings.Replace(value, "\r", ":", -1)
		}

		if len(parts) >= 2 {
			newDirective := transformDirective{
				key:       parts[0],
				operation: strings.ToLower(parts[1]),
			}

			for i := 2; i < len(parts); i++ {
				newDirective.parameters = append(newDirective.parameters, shared.Unescape(parts[i]))
			}

			format.directives = append(format.directives, newDirective)
		}
	}

	return nil
}

func (values *valueMap) processDirective(directive transformDirective) {
	if value, keyExists := (*values)[directive.key]; keyExists {

		numParameters := len(directive.parameters)
		switch directive.operation {
		case "rename":
			if numParameters == 1 {
				(*values)[shared.Unescape(directive.parameters[0])] = value
				delete(*values, directive.key)
			}

		case "time":
			if numParameters == 2 {

				if timestamp, err := time.Parse(directive.parameters[0], value[:len(directive.parameters[0])]); err != nil {
					Log.Warning.Print("ProcessJSON failed to parse a timestamp: ", err)
				} else {
					(*values)[directive.key] = timestamp.Format(directive.parameters[1])
				}
			}

		case "split":
			if numParameters > 1 {
				token := shared.Unescape(directive.parameters[0])
				if strings.Contains(value, token) {
					elements := strings.Split(value, token)
					mapping := directive.parameters[1:]
					maxItems := shared.MinI(len(elements), len(mapping))

					for i := 0; i < maxItems; i++ {
						(*values)[mapping[i]] = elements[i]
					}
				}
			}

		case "remove":
			if numParameters == 0 {
				delete(*values, directive.key)
			}

		case "replace":
			if numParameters == 2 {
				(*values)[directive.key] = strings.Replace(value, shared.Unescape(directive.parameters[0]), shared.Unescape(directive.parameters[1]), -1)
			}

		case "trim":
			switch {
			case numParameters == 0:
				(*values)[directive.key] = strings.Trim(value, " \t")
			case numParameters == 1:
				(*values)[directive.key] = strings.Trim(value, shared.Unescape(directive.parameters[0]))
			}

		case "remove":
			delete(*values, directive.key)
		}
	}
}

// Format modifies the JSON payload of this message
func (format *ProcessJSON) Format(msg core.Message) ([]byte, core.MessageStreamID) {
	data, streamID := format.base.Format(msg)
	if len(format.directives) == 0 {
		return data, streamID // ### return, no directives ###
	}

	values := make(valueMap)
	err := json.Unmarshal(data, &values)
	if err != nil {
		Log.Warning.Print("ProcessJSON failed to unmarshal a message: ", err)
		return data, streamID // ### return, malformed data ###
	}

	for _, directive := range format.directives {
		values.processDirective(directive)
	}

	if format.trimValues {
		for key, value := range values {
			values[key] = strings.Trim(value, " ")
		}
	}

	if jsonData, err := json.Marshal(values); err == nil {
		return jsonData, streamID // ### return, ok ###
	}

	Log.Warning.Print("ProcessJSON failed to marshal a message: ", err)
	return data, streamID
}
<commit_msg>Removed duplicated case<commit_after>// Copyright 2015-2016 trivago GmbH
//
// 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 format

import (
	"encoding/json"
	"github.com/trivago/gollum/core"
	"github.com/trivago/gollum/core/log"
	"github.com/trivago/gollum/shared"
	"strings"
	"time"
)

// ProcessJSON formatter plugin
// ProcessJSON is a formatter that allows modifications to fields of a given
// JSON message. The message is modified and returned again as JSON.
// Configuration example
//
//  - "stream.Broadcast":
//    Formatter: "format.ProcessJSON"
//    ProcessJSONDataFormatter: "format.Forward"
//    ProcessJSONDirectives:
//      - "host:split: :host:@timestamp"
//      - "@timestamp:time:20060102150405:2006-01-02 15\\:04\\:05"
//      - "error:replace:°:\n"
//      - "text:trim: \t"
//      - "foo:rename:bar"
//		- "foobar:remove"
//    ProcessJSONTrimValues: true
//
// ProcessJSONDataFormatter formatter that will be applied before
// ProcessJSONDirectives are processed.
//
// ProcessJSONDirectives defines the action to be applied to the json payload.
// Directives are processed in order of appearance.
// The directives have to be given in the form of key:operation:parameters, where
// operation can be one of the following:
// - split:<string>:{<key>:<key>:...} Split the value by a string and set the
// resulting array elements to the given fields in order of appearance.
// - replace:<old>:<new> replace a given string in the value with a new one
// - trim:<characters> remove the given characters (not string!) from the start
// and end of the value
// - rename:<old>:<new> rename a given field
// - remove remove a given field
// - timestamp:<read>:<write> read a timestamp and transform it into another
// format
//
// ProcessJSONTrimValues will trim whitspaces from all values if enabled.
// Enabled by default.
type ProcessJSON struct {
	base       core.Formatter
	directives []transformDirective
	trimValues bool
}

type transformDirective struct {
	key        string
	operation  string
	parameters []string
}

type valueMap map[string]string

func init() {
	shared.TypeRegistry.Register(ProcessJSON{})
}

// Configure initializes this formatter with values from a plugin config.
func (format *ProcessJSON) Configure(conf core.PluginConfig) error {
	plugin, err := core.NewPluginWithType(conf.GetString("ProcessJSONDataFormatter", "format.Forward"), conf)
	if err != nil {
		return err
	}
	directives := conf.GetStringArray("ProcessJSONDirectives", []string{})

	format.base = plugin.(core.Formatter)
	format.directives = make([]transformDirective, 0, len(directives))
	format.trimValues = conf.GetBool("ProcessJSONTrimValues", true)

	for _, directive := range directives {
		directive := strings.Replace(directive, "\\:", "\r", -1)
		parts := strings.Split(directive, ":")
		for i, value := range parts {
			parts[i] = strings.Replace(value, "\r", ":", -1)
		}

		if len(parts) >= 2 {
			newDirective := transformDirective{
				key:       parts[0],
				operation: strings.ToLower(parts[1]),
			}

			for i := 2; i < len(parts); i++ {
				newDirective.parameters = append(newDirective.parameters, shared.Unescape(parts[i]))
			}

			format.directives = append(format.directives, newDirective)
		}
	}

	return nil
}

func (values *valueMap) processDirective(directive transformDirective) {
	if value, keyExists := (*values)[directive.key]; keyExists {

		numParameters := len(directive.parameters)
		switch directive.operation {
		case "rename":
			if numParameters == 1 {
				(*values)[shared.Unescape(directive.parameters[0])] = value
				delete(*values, directive.key)
			}

		case "time":
			if numParameters == 2 {

				if timestamp, err := time.Parse(directive.parameters[0], value[:len(directive.parameters[0])]); err != nil {
					Log.Warning.Print("ProcessJSON failed to parse a timestamp: ", err)
				} else {
					(*values)[directive.key] = timestamp.Format(directive.parameters[1])
				}
			}

		case "split":
			if numParameters > 1 {
				token := shared.Unescape(directive.parameters[0])
				if strings.Contains(value, token) {
					elements := strings.Split(value, token)
					mapping := directive.parameters[1:]
					maxItems := shared.MinI(len(elements), len(mapping))

					for i := 0; i < maxItems; i++ {
						(*values)[mapping[i]] = elements[i]
					}
				}
			}

		case "remove":
			if numParameters == 0 {
				delete(*values, directive.key)
			}

		case "replace":
			if numParameters == 2 {
				(*values)[directive.key] = strings.Replace(value, shared.Unescape(directive.parameters[0]), shared.Unescape(directive.parameters[1]), -1)
			}

		case "trim":
			switch {
			case numParameters == 0:
				(*values)[directive.key] = strings.Trim(value, " \t")
			case numParameters == 1:
				(*values)[directive.key] = strings.Trim(value, shared.Unescape(directive.parameters[0]))
			}
		}
	}
}

// Format modifies the JSON payload of this message
func (format *ProcessJSON) Format(msg core.Message) ([]byte, core.MessageStreamID) {
	data, streamID := format.base.Format(msg)
	if len(format.directives) == 0 {
		return data, streamID // ### return, no directives ###
	}

	values := make(valueMap)
	err := json.Unmarshal(data, &values)
	if err != nil {
		Log.Warning.Print("ProcessJSON failed to unmarshal a message: ", err)
		return data, streamID // ### return, malformed data ###
	}

	for _, directive := range format.directives {
		values.processDirective(directive)
	}

	if format.trimValues {
		for key, value := range values {
			values[key] = strings.Trim(value, " ")
		}
	}

	if jsonData, err := json.Marshal(values); err == nil {
		return jsonData, streamID // ### return, ok ###
	}

	Log.Warning.Print("ProcessJSON failed to marshal a message: ", err)
	return data, streamID
}
<|endoftext|>


<commit_before>package client

import (
	"fmt"
	"io"
	"io/ioutil"
	"math/rand"
	"net/http"
	"os"
	"path"
	"sync"

	"github.com/facebookgo/pidfile"
	log "github.com/sirupsen/logrus"

	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/client-go/kubernetes"
	corev1 "k8s.io/client-go/kubernetes/typed/core/v1"
	_ "k8s.io/client-go/plugin/pkg/client/auth" // enables support for configs with auth
	"k8s.io/client-go/rest"
	"k8s.io/client-go/tools/clientcmd"
	"k8s.io/client-go/tools/portforward"
	"k8s.io/client-go/transport/spdy"
)

const (
	pachdLocalPort         = 30650
	pachdRemotePort        = 650
	samlAcsLocalPort       = 30654
	dashUILocalPort        = 30080
	dashWebSocketLocalPort = 30081
	pfsLocalPort           = 30652
)

// PortForwarder handles proxying local traffic to a kubernetes pod
type PortForwarder struct {
	core          corev1.CoreV1Interface
	client        rest.Interface
	config        *rest.Config
	namespace     string
	logger        *io.PipeWriter
	stopChansLock *sync.Mutex
	stopChans     []chan struct{}
	shutdown      bool
}

// NewPortForwarder creates a new port forwarder
func NewPortForwarder(namespace string) (*PortForwarder, error) {
	if namespace == "" {
		namespace = "default"
	}

	rules := clientcmd.NewDefaultClientConfigLoadingRules()
	overrides := &clientcmd.ConfigOverrides{}
	kubeConfig := clientcmd.NewNonInteractiveDeferredLoadingClientConfig(rules, overrides)
	config, err := kubeConfig.ClientConfig()
	if err != nil {
		return nil, err
	}

	client, err := kubernetes.NewForConfig(config)
	if err != nil {
		return nil, err
	}

	core := client.CoreV1()
	logger := log.StandardLogger()

	return &PortForwarder{
		core:          core,
		client:        core.RESTClient(),
		config:        config,
		namespace:     namespace,
		logger:        logger.Writer(),
		stopChansLock: &sync.Mutex{},
		stopChans:     []chan struct{}{},
		shutdown:      false,
	}, nil
}

// Run starts the port forwarder. Returns after initialization is begun,
// returning any initialization errors.
func (f *PortForwarder) Run(appName string, localPort, remotePort uint16) error {
	podNameSelector := map[string]string{
		"suite": "pachyderm",
		"app":   appName,
	}

	podList, err := f.core.Pods(f.namespace).List(metav1.ListOptions{
		LabelSelector: metav1.FormatLabelSelector(metav1.SetAsLabelSelector(podNameSelector)),
		TypeMeta: metav1.TypeMeta{
			Kind:       "ListOptions",
			APIVersion: "v1",
		},
	})
	if err != nil {
		return err
	}
	if len(podList.Items) == 0 {
		return fmt.Errorf("No pods found for app %s", appName)
	}

	// Choose a random pod
	podName := podList.Items[rand.Intn(len(podList.Items))].Name

	url := f.client.Post().
		Resource("pods").
		Namespace(f.namespace).
		Name(podName).
		SubResource("portforward").
		URL()

	transport, upgrader, err := spdy.RoundTripperFor(f.config)
	if err != nil {
		return err
	}

	dialer := spdy.NewDialer(upgrader, &http.Client{Transport: transport}, "POST", url)
	ports := []string{fmt.Sprintf("%d:%d", localPort, remotePort)}
	readyChan := make(chan struct{}, 1)
	stopChan := make(chan struct{}, 1)

	// Ensure that the port forwarder isn't already shutdown, and append the
	// shutdown channel so this forwarder can be closed
	f.stopChansLock.Lock()
	if f.shutdown {
		f.stopChansLock.Unlock()
		return fmt.Errorf("port forwarder is shutdown")
	}
	f.stopChans = append(f.stopChans, stopChan)
	f.stopChansLock.Unlock()

	fw, err := portforward.New(dialer, ports, stopChan, readyChan, ioutil.Discard, f.logger)
	if err != nil {
		return err
	}

	errChan := make(chan error, 1)
	go func() { errChan <- fw.ForwardPorts() }()

	select {
	case err = <-errChan:
		return fmt.Errorf("port forwarding failed: %v", err)
	case <-fw.Ready:
		return nil
	}
}

// RunForDaemon creates a port forwarder for the pachd daemon.
func (f *PortForwarder) RunForDaemon(localPort, remotePort uint16) error {
	if localPort == 0 {
		localPort = pachdLocalPort
	}
	if remotePort == 0 {
		remotePort = pachdRemotePort
	}
	return f.Run("pachd", localPort, remotePort)
}

// RunForSAMLACS creates a port forwarder for SAML ACS.
func (f *PortForwarder) RunForSAMLACS(localPort uint16) error {
	if localPort == 0 {
		localPort = samlAcsLocalPort
	}
	return f.Run("pachd", localPort, 654)
}

// RunForDashUI creates a port forwarder for the dash UI.
func (f *PortForwarder) RunForDashUI(localPort uint16) error {
	if localPort == 0 {
		localPort = dashUILocalPort
	}
	return f.Run("dash", localPort, 8080)
}

// RunForDashWebSocket creates a port forwarder for the dash websocket.
func (f *PortForwarder) RunForDashWebSocket(localPort uint16) error {
	if localPort == 0 {
		localPort = dashWebSocketLocalPort
	}
	return f.Run("dash", localPort, 8081)
}

// RunForPFS creates a port forwarder for PFS over HTTP.
func (f *PortForwarder) RunForPFS(localPort uint16) error {
	if localPort == 0 {
		localPort = pfsLocalPort
	}
	return f.Run("pachd", localPort, 30652)
}

// Lock uses pidfiles to ensure that only one port forwarder is running across
// one or more `pachctl` instances
func (f *PortForwarder) Lock() error {
	pidfile.SetPidfilePath(path.Join(os.Getenv("HOME"), ".pachyderm/port-forward.pid"))
	return pidfile.Write()
}

// Close shuts down port forwarding.
func (f *PortForwarder) Close() {
	defer f.logger.Close()

	f.stopChansLock.Lock()
	defer f.stopChansLock.Unlock()

	if f.shutdown {
		panic("port forwarder already shutdown")
	}

	f.shutdown = true

	for _, stopChan := range f.stopChans {
		close(stopChan)
	}
}
<commit_msg>Slighter terser code<commit_after>package client

import (
	"fmt"
	"io"
	"io/ioutil"
	"math/rand"
	"net/http"
	"os"
	"path"
	"sync"

	"github.com/facebookgo/pidfile"
	log "github.com/sirupsen/logrus"

	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/client-go/kubernetes"
	corev1 "k8s.io/client-go/kubernetes/typed/core/v1"
	_ "k8s.io/client-go/plugin/pkg/client/auth" // enables support for configs with auth
	"k8s.io/client-go/rest"
	"k8s.io/client-go/tools/clientcmd"
	"k8s.io/client-go/tools/portforward"
	"k8s.io/client-go/transport/spdy"
)

const (
	pachdLocalPort         = 30650
	pachdRemotePort        = 650
	samlAcsLocalPort       = 30654
	dashUILocalPort        = 30080
	dashWebSocketLocalPort = 30081
	pfsLocalPort           = 30652
)

// PortForwarder handles proxying local traffic to a kubernetes pod
type PortForwarder struct {
	core          corev1.CoreV1Interface
	client        rest.Interface
	config        *rest.Config
	namespace     string
	logger        *io.PipeWriter
	stopChansLock *sync.Mutex
	stopChans     []chan struct{}
	shutdown      bool
}

// NewPortForwarder creates a new port forwarder
func NewPortForwarder(namespace string) (*PortForwarder, error) {
	if namespace == "" {
		namespace = "default"
	}

	rules := clientcmd.NewDefaultClientConfigLoadingRules()
	overrides := &clientcmd.ConfigOverrides{}
	kubeConfig := clientcmd.NewNonInteractiveDeferredLoadingClientConfig(rules, overrides)
	config, err := kubeConfig.ClientConfig()
	if err != nil {
		return nil, err
	}

	client, err := kubernetes.NewForConfig(config)
	if err != nil {
		return nil, err
	}

	core := client.CoreV1()

	return &PortForwarder{
		core:          core,
		client:        core.RESTClient(),
		config:        config,
		namespace:     namespace,
		logger:        log.StandardLogger().Writer(),
		stopChansLock: &sync.Mutex{},
		stopChans:     []chan struct{}{},
		shutdown:      false,
	}, nil
}

// Run starts the port forwarder. Returns after initialization is begun,
// returning any initialization errors.
func (f *PortForwarder) Run(appName string, localPort, remotePort uint16) error {
	podNameSelector := map[string]string{
		"suite": "pachyderm",
		"app":   appName,
	}

	podList, err := f.core.Pods(f.namespace).List(metav1.ListOptions{
		LabelSelector: metav1.FormatLabelSelector(metav1.SetAsLabelSelector(podNameSelector)),
		TypeMeta: metav1.TypeMeta{
			Kind:       "ListOptions",
			APIVersion: "v1",
		},
	})
	if err != nil {
		return err
	}
	if len(podList.Items) == 0 {
		return fmt.Errorf("No pods found for app %s", appName)
	}

	// Choose a random pod
	podName := podList.Items[rand.Intn(len(podList.Items))].Name

	url := f.client.Post().
		Resource("pods").
		Namespace(f.namespace).
		Name(podName).
		SubResource("portforward").
		URL()

	transport, upgrader, err := spdy.RoundTripperFor(f.config)
	if err != nil {
		return err
	}

	dialer := spdy.NewDialer(upgrader, &http.Client{Transport: transport}, "POST", url)
	ports := []string{fmt.Sprintf("%d:%d", localPort, remotePort)}
	readyChan := make(chan struct{}, 1)
	stopChan := make(chan struct{}, 1)

	// Ensure that the port forwarder isn't already shutdown, and append the
	// shutdown channel so this forwarder can be closed
	f.stopChansLock.Lock()
	if f.shutdown {
		f.stopChansLock.Unlock()
		return fmt.Errorf("port forwarder is shutdown")
	}
	f.stopChans = append(f.stopChans, stopChan)
	f.stopChansLock.Unlock()

	fw, err := portforward.New(dialer, ports, stopChan, readyChan, ioutil.Discard, f.logger)
	if err != nil {
		return err
	}

	errChan := make(chan error, 1)
	go func() { errChan <- fw.ForwardPorts() }()

	select {
	case err = <-errChan:
		return fmt.Errorf("port forwarding failed: %v", err)
	case <-fw.Ready:
		return nil
	}
}

// RunForDaemon creates a port forwarder for the pachd daemon.
func (f *PortForwarder) RunForDaemon(localPort, remotePort uint16) error {
	if localPort == 0 {
		localPort = pachdLocalPort
	}
	if remotePort == 0 {
		remotePort = pachdRemotePort
	}
	return f.Run("pachd", localPort, remotePort)
}

// RunForSAMLACS creates a port forwarder for SAML ACS.
func (f *PortForwarder) RunForSAMLACS(localPort uint16) error {
	if localPort == 0 {
		localPort = samlAcsLocalPort
	}
	return f.Run("pachd", localPort, 654)
}

// RunForDashUI creates a port forwarder for the dash UI.
func (f *PortForwarder) RunForDashUI(localPort uint16) error {
	if localPort == 0 {
		localPort = dashUILocalPort
	}
	return f.Run("dash", localPort, 8080)
}

// RunForDashWebSocket creates a port forwarder for the dash websocket.
func (f *PortForwarder) RunForDashWebSocket(localPort uint16) error {
	if localPort == 0 {
		localPort = dashWebSocketLocalPort
	}
	return f.Run("dash", localPort, 8081)
}

// RunForPFS creates a port forwarder for PFS over HTTP.
func (f *PortForwarder) RunForPFS(localPort uint16) error {
	if localPort == 0 {
		localPort = pfsLocalPort
	}
	return f.Run("pachd", localPort, 30652)
}

// Lock uses pidfiles to ensure that only one port forwarder is running across
// one or more `pachctl` instances
func (f *PortForwarder) Lock() error {
	pidfile.SetPidfilePath(path.Join(os.Getenv("HOME"), ".pachyderm/port-forward.pid"))
	return pidfile.Write()
}

// Close shuts down port forwarding.
func (f *PortForwarder) Close() {
	defer f.logger.Close()

	f.stopChansLock.Lock()
	defer f.stopChansLock.Unlock()

	if f.shutdown {
		panic("port forwarder already shutdown")
	}

	f.shutdown = true

	for _, stopChan := range f.stopChans {
		close(stopChan)
	}
}
<|endoftext|>


<commit_before>package tests

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

type cliTestCase struct {
	args           []string
	code           int
	stdoutContains string
	stdoutEmpty    bool
	stderrContains string
	stderrEmpty    bool
}

func paseMeminfoLine(l string) int64 {
	fields := strings.Split(l, " ")
	asciiVal := fields[len(fields)-2]
	val, err := strconv.ParseInt(asciiVal, 10, 64)
	if err != nil {
		panic(err)
	}
	return val
}

func parseMeminfo() (memTotal int64, swapTotal int64) {
	/*
		/proc/meminfo looks like this:

		MemTotal:        8024108 kB
		[...]
		SwapTotal:        102396 kB
		[...]
	*/
	content, err := ioutil.ReadFile("/proc/meminfo")
	if err != nil {
		panic(err)
	}
	lines := strings.Split(string(content), "\n")
	for _, l := range lines {
		if strings.HasPrefix(l, "MemTotal:") {
			memTotal = paseMeminfoLine(l)
		}
		if strings.HasPrefix(l, "SwapTotal:") {
			swapTotal = paseMeminfoLine(l)
		}
	}
	return
}

func TestCli(t *testing.T) {
	memTotal, swapTotal := parseMeminfo()
	mem1percent := fmt.Sprintf("%d", memTotal*2/101)   // slightly below 2 percent
	swap2percent := fmt.Sprintf("%d", swapTotal*3/101) // slightly below 3 percent
	// The periodic memory report looks like this:
	//   mem avail: 4998 MiB (63 %), swap free: 0 MiB (0 %)
	const memReport = "mem avail: "
	// earlyoom startup looks like this:
	//   earlyoom v1.1-5-g74a364b-dirty
	//   mem total: 7836 MiB, min: 783 MiB (10 %)
	//   swap total: 0 MiB, min: 0 MiB (10 %)
	// startupMsg matches the last line of the startup output.
	const startupMsg = "swap total: "
	testcases := []cliTestCase{
		{args: []string{"-h"}, code: 0, stderrContains: "this help text", stdoutEmpty: true},
		{args: []string{"-h"}, code: 0, stderrContains: "this help text", stdoutEmpty: true},
		{args: nil, code: -1, stderrContains: startupMsg, stdoutContains: memReport},
		{args: []string{"-p"}, code: -1, stdoutContains: memReport},
		{args: []string{"-v"}, code: 0, stderrContains: "earlyoom v", stdoutEmpty: true},
		{args: []string{"-d"}, code: -1, stdoutContains: "^ new victim (higher badness)"},
		{args: []string{"-m", "1"}, code: -1, stderrContains: "1 %", stdoutContains: memReport},
		{args: []string{"-m", "0"}, code: 15, stderrContains: "Invalid percentage", stdoutEmpty: true},
		{args: []string{"-s", "2"}, code: -1, stderrContains: "2 %", stdoutContains: memReport},
		{args: []string{"-s", "0"}, code: 16, stderrContains: "Invalid percentage", stdoutEmpty: true},
		{args: []string{"-M", mem1percent}, code: -1, stderrContains: "min:  1 %", stdoutContains: memReport},
		{args: []string{"-S", swap2percent}, code: -1, stderrContains: "min:  2 %", stdoutContains: memReport},
		{args: []string{"-r", "0"}, code: -1, stderrContains: startupMsg, stdoutEmpty: true},
	}

	for i, tc := range testcases {
		t.Logf("Testcase #%d: earlyoom %s", i, strings.Join(tc.args, " "))
		pass := true
		res := runEarlyoom(t, tc.args...)
		if res.code != tc.code {
			t.Errorf("wrong exit code: have=%d want=%d", res.code, tc.code)
			pass = false
		}
		if tc.stdoutEmpty && res.stdout != "" {
			t.Errorf("stdout should be empty but is not")
			pass = false
		}
		if !strings.Contains(res.stdout, tc.stdoutContains) {
			t.Errorf("stdout should contain %q, but does not", tc.stdoutContains)
			pass = false
		}
		if tc.stderrEmpty && res.stderr != "" {
			t.Errorf("stderr should be empty, but is not")
			pass = false
		}
		if !strings.Contains(res.stderr, tc.stderrContains) {
			t.Errorf("stderr should contain %q, but does not", tc.stderrContains)
			pass = false
		}
		if !pass {
			const empty = "(empty)"
			if res.stderr == "" {
				res.stderr = empty
			}
			if res.stdout == "" {
				res.stdout = empty
			}
			t.Logf("stderr:\n%s", res.stderr)
			t.Logf("stdout:\n%s", res.stdout)
		}
	}
}
<commit_msg>tests: skip "-S" test when there is now swap space<commit_after>package tests

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

type cliTestCase struct {
	args           []string
	code           int
	stdoutContains string
	stdoutEmpty    bool
	stderrContains string
	stderrEmpty    bool
}

func paseMeminfoLine(l string) int64 {
	fields := strings.Split(l, " ")
	asciiVal := fields[len(fields)-2]
	val, err := strconv.ParseInt(asciiVal, 10, 64)
	if err != nil {
		panic(err)
	}
	return val
}

func parseMeminfo() (memTotal int64, swapTotal int64) {
	/*
		/proc/meminfo looks like this:

		MemTotal:        8024108 kB
		[...]
		SwapTotal:        102396 kB
		[...]
	*/
	content, err := ioutil.ReadFile("/proc/meminfo")
	if err != nil {
		panic(err)
	}
	lines := strings.Split(string(content), "\n")
	for _, l := range lines {
		if strings.HasPrefix(l, "MemTotal:") {
			memTotal = paseMeminfoLine(l)
		}
		if strings.HasPrefix(l, "SwapTotal:") {
			swapTotal = paseMeminfoLine(l)
		}
	}
	return
}

func TestCli(t *testing.T) {
	memTotal, swapTotal := parseMeminfo()
	mem1percent := fmt.Sprintf("%d", memTotal*2/101)   // slightly below 2 percent
	swap2percent := fmt.Sprintf("%d", swapTotal*3/101) // slightly below 3 percent
	// The periodic memory report looks like this:
	//   mem avail: 4998 MiB (63 %), swap free: 0 MiB (0 %)
	const memReport = "mem avail: "
	// earlyoom startup looks like this:
	//   earlyoom v1.1-5-g74a364b-dirty
	//   mem total: 7836 MiB, min: 783 MiB (10 %)
	//   swap total: 0 MiB, min: 0 MiB (10 %)
	// startupMsg matches the last line of the startup output.
	const startupMsg = "swap total: "
	testcases := []cliTestCase{
		{args: []string{"-h"}, code: 0, stderrContains: "this help text", stdoutEmpty: true},
		{args: []string{"-h"}, code: 0, stderrContains: "this help text", stdoutEmpty: true},
		{args: nil, code: -1, stderrContains: startupMsg, stdoutContains: memReport},
		{args: []string{"-p"}, code: -1, stdoutContains: memReport},
		{args: []string{"-v"}, code: 0, stderrContains: "earlyoom v", stdoutEmpty: true},
		{args: []string{"-d"}, code: -1, stdoutContains: "^ new victim (higher badness)"},
		{args: []string{"-m", "1"}, code: -1, stderrContains: "1 %", stdoutContains: memReport},
		{args: []string{"-m", "0"}, code: 15, stderrContains: "Invalid percentage", stdoutEmpty: true},
		{args: []string{"-s", "2"}, code: -1, stderrContains: "2 %", stdoutContains: memReport},
		{args: []string{"-s", "0"}, code: 16, stderrContains: "Invalid percentage", stdoutEmpty: true},
		{args: []string{"-M", mem1percent}, code: -1, stderrContains: "min:  1 %", stdoutContains: memReport},
		{args: []string{"-r", "0"}, code: -1, stderrContains: startupMsg, stdoutEmpty: true},
	}
	if swapTotal > 0 {
		// Test cannot work when there is no swap enabled
		tc := cliTestCase{args: []string{"-S", swap2percent}, code: -1, stderrContains: "min:  2 %", stdoutContains: memReport}
		testcases = append(testcases, tc)
	}

	for i, tc := range testcases {
		t.Logf("Testcase #%d: earlyoom %s", i, strings.Join(tc.args, " "))
		pass := true
		res := runEarlyoom(t, tc.args...)
		if res.code != tc.code {
			t.Errorf("wrong exit code: have=%d want=%d", res.code, tc.code)
			pass = false
		}
		if tc.stdoutEmpty && res.stdout != "" {
			t.Errorf("stdout should be empty but is not")
			pass = false
		}
		if !strings.Contains(res.stdout, tc.stdoutContains) {
			t.Errorf("stdout should contain %q, but does not", tc.stdoutContains)
			pass = false
		}
		if tc.stderrEmpty && res.stderr != "" {
			t.Errorf("stderr should be empty, but is not")
			pass = false
		}
		if !strings.Contains(res.stderr, tc.stderrContains) {
			t.Errorf("stderr should contain %q, but does not", tc.stderrContains)
			pass = false
		}
		if !pass {
			const empty = "(empty)"
			if res.stderr == "" {
				res.stderr = empty
			}
			if res.stdout == "" {
				res.stdout = empty
			}
			t.Logf("stderr:\n%s", res.stderr)
			t.Logf("stdout:\n%s", res.stdout)
		}
	}
}
<|endoftext|>


<commit_before>package tests

import (
	"database/sql"
	"log"
	"os"
	"reflect"
	"testing"

	_ "github.com/mattn/go-sqlite3"
	"github.com/monochromegane/argen"
)

func TestMain(m *testing.M) {
	db, err := sql.Open("sqlite3", ":memory:")
	if err != nil {
		log.Fatal(err)
	}
	defer db.Close()

	Use(db)
	LogMode(true)
	sqlStmt := `
	create table users (id integer not null primary key, name text, age integer);
	create table posts (id integer not null primary key, user_id integer, name text);
	`
	_, err = db.Exec(sqlStmt)
	if err != nil {
		log.Fatal(err, sqlStmt)
	}

	os.Exit(m.Run())
}

func TestSelect(t *testing.T) {
	u := &User{Name: "test"}
	u.Save()
	defer User{}.DeleteAll()

	u, err := User{}.Select("id").First()
	assertError(t, err)

	if !ar.IsZero(u.Name) {
		t.Errorf("column value should be empty, but %s", u.Name)
	}
}

func TestFind(t *testing.T) {
	expect := &User{Name: "test"}
	expect.Save()
	defer User{}.DeleteAll()

	u, err := User{}.Find(1)
	assertError(t, err)
	assertEqualStruct(t, expect, u)
}

func TestFindBy(t *testing.T) {
	expect := &User{Name: "test"}
	expect.Save()
	defer User{}.DeleteAll()

	u, err := User{}.FindBy("name", "test")
	assertError(t, err)
	assertEqualStruct(t, expect, u)
}

func TestFirst(t *testing.T) {
	for _, name := range []string{"test1", "test2"} {
		u := &User{Name: name}
		u.Save()
	}
	defer User{}.DeleteAll()

	expect, _ := User{}.Where("name", "test1").QueryRow()

	u, err := User{}.First()
	assertError(t, err)
	assertEqualStruct(t, expect, u)
}

func TestLast(t *testing.T) {
	for _, name := range []string{"test1", "test2"} {
		u := &User{Name: name}
		u.Save()
	}
	defer User{}.DeleteAll()

	expect, _ := User{}.Where("name", "test2").QueryRow()

	u, err := User{}.Last()
	assertError(t, err)
	assertEqualStruct(t, expect, u)
}

func TestWhere(t *testing.T) {
	expect := &User{Name: "test"}
	expect.Save()
	defer User{}.DeleteAll()

	u, err := User{}.Where("name", "test").And("id", 1).QueryRow()

	assertError(t, err)
	assertEqualStruct(t, expect, u)
}

func TestOrder(t *testing.T) {
	expects := []string{"test1", "test2"}
	for _, name := range expects {
		u := &User{Name: name}
		u.Save()
	}
	defer User{}.DeleteAll()

	users, err := User{}.Order("name", "ASC").Query()

	assertError(t, err)
	for i, u := range users {
		if u.Name != expects[i] {
			t.Errorf("column value should be %v, but %v", expects[i], u.Name)
		}
	}
}

func TestLimitAndOffset(t *testing.T) {
	for _, name := range []string{"test1", "test2", "test3"} {
		u := &User{Name: name}
		u.Save()
	}
	defer User{}.DeleteAll()

	users, err := User{}.Limit(2).Offset(1).Order("name", "ASC").Query()

	assertError(t, err)
	expects := []string{"test2", "test3"}
	for i, u := range users {
		if u.Name != expects[i] {
			t.Errorf("column value should be %v, but %v", expects[i], u.Name)
		}
	}
}

func TestGroupByAndHaving(t *testing.T) {
	for _, name := range []string{"testA", "testB", "testB"} {
		u := &User{Name: name}
		u.Save()
	}
	defer User{}.DeleteAll()

	users, err := User{}.Group("name").Having("count(name)", 2).Query()

	assertError(t, err)
	expects := []string{"testB"}
	for i, u := range users {
		if u.Name != expects[i] {
			t.Errorf("column value should be %v, but %v", expects[i], u.Name)
		}
	}
}

func TestExplain(t *testing.T) {
	err := User{}.Where("name", "test").Explain()
	assertError(t, err)
}

func TestIsValid(t *testing.T) {
	p := &Post{Name: "abc"}
	_, errs := p.IsValid()

	if len(errs.Messages["name"]) != 1 {
		t.Errorf("errors count should be 1, but %d", len(errs.Messages["name"]))
	}
}

func TestBuild(t *testing.T) {
	defer User{}.DeleteAll()
	u, _ := User{}.Create(UserParams{Name: "TestCreate"})

	p := u.BuildPost(PostParams{Name: "name"})

	if p.UserId != u.Id {
		t.Errorf("column value should be %v, but %v", u.Id, p.UserId)
	}
}

func TestCreate(t *testing.T) {
	u, errs := User{}.Create(UserParams{
		Name: "TestCreate",
	})
	defer User{}.DeleteAll()

	assertErrors(t, errs)

	expect, _ := User{}.FindBy("name", "TestCreate")
	assertEqualStruct(t, expect, u)
}

func TestIsNewRecordAndIsPresistent(t *testing.T) {
	defer User{}.DeleteAll()

	u := &User{Name: "test"}
	if !u.IsNewRecord() {
		t.Errorf("struct is new record, but isn't new record.")
	}

	u.Save()
	if !u.IsPersistent() {
		t.Errorf("struct is persistent, but isn't persistent.")
	}
}

func TestSaveWithInvalidData(t *testing.T) {
	defer Post{}.DeleteAll()

	// OnCreate
	p := &Post{Name: "invalid"}
	_, errs := p.Save()

	if len(errs.Messages["name"]) != 1 {
		t.Errorf("errors count should be 1, but %d", len(errs.Messages["name"]))
	}

	p.Name = "name"
	_, errs = p.Save()
	assertErrors(t, errs)

	// OnUpdate
	p.Name = "invalid2"
	_, errs = p.Save()

	if len(errs.Messages["name"]) != 1 {
		t.Errorf("errors count should be 1, but %d", len(errs.Messages["name"]))
	}

}

func TestSave(t *testing.T) {
	defer User{}.DeleteAll()

	u := &User{Name: "test"}

	_, errs := u.Save()
	assertErrors(t, errs)

	if u.Id == 0 {
		t.Errorf("Id should be setted after save, but isn't setted")
	}

	expect, _ := User{}.FindBy("name", "test")
	assertEqualStruct(t, expect, u)

	u.Name = "test2"
	_, errs = u.Save()
	assertErrors(t, errs)

	expect, _ = User{}.Find(u.Id)
	assertEqualStruct(t, expect, u)
}

func TestUpdate(t *testing.T) {
	defer User{}.DeleteAll()

	u := &User{Name: "test"}
	_, errs := u.Save()

	expect := UserParams{Name: "test2"}
	_, errs = u.Update(expect)
	assertErrors(t, errs)

	actual, _ := User{}.Find(u.Id)
	if expect.Name != actual.Name {
		t.Errorf("column value should be equal to %v, but %v", expect.Name, actual.Name)
	}
}

func TestUpdateColumns(t *testing.T) {
	defer User{}.DeleteAll()

	u := &User{Name: "test"}
	_, errs := u.Save()

	expect := UserParams{Name: "test2"}
	_, errs = u.UpdateColumns(expect)
	assertErrors(t, errs)

	actual, _ := User{}.Find(u.Id)
	if expect.Name != actual.Name {
		t.Errorf("column value should be equal to %v, but %v", expect.Name, actual.Name)
	}
}

func TestDelete(t *testing.T) {
	defer User{}.DeleteAll()

	u, _ := User{}.Create(UserParams{Name: "test1"})

	_, errs := u.Delete()
	assertErrors(t, errs)

	actual, _ := User{}.Find(u.Id)
	if actual != nil {
		t.Errorf("record should be deleted, but isn't deleted %v", actual)
	}
}

func TestScope(t *testing.T) {
	defer User{}.DeleteAll()

	User{}.Create(UserParams{Name: "test1", Age: 20})
	expect, _ := User{}.Create(UserParams{Name: "test2", Age: 21})

	users, _ := User{}.OlderThan(20).Query()

	if len(users) != 1 {
		t.Errorf("record count should be 1, but %v", len(users))
	}
	assertEqualStruct(t, users[0], expect)
}

func TestHasMany(t *testing.T) {
	defer func() {
		User{}.DeleteAll()
		Post{}.DeleteAll()
	}()

	u, _ := User{}.Create(UserParams{Name: "test1"})
	expect, _ := Post{}.Create(PostParams{UserId: u.Id, Name: "name"})

	user, err := User{}.Find(u.Id)
	posts, err := user.Posts()
	assertError(t, err)
	if len(posts) != 1 {
		t.Errorf("record count should be 1, but %v", len(posts))
	}
	assertEqualStruct(t, posts[0], expect)
}

func TestBelongsTo(t *testing.T) {
	defer func() {
		User{}.DeleteAll()
		Post{}.DeleteAll()
	}()

	expect, _ := User{}.Create(UserParams{Name: "test1"})
	p, _ := Post{}.Create(PostParams{UserId: expect.Id, Name: "name"})

	user, err := p.User()
	assertError(t, err)
	assertEqualStruct(t, user, expect)
}

func TestJoins(t *testing.T) {
	defer func() {
		User{}.DeleteAll()
		Post{}.DeleteAll()
	}()

	// User joins posts
	u, _ := User{}.Create(UserParams{Name: "test1"})
	p1, _ := Post{}.Create(PostParams{UserId: u.Id, Name: "name"})
	p2 := Post{UserId: u.Id, Name: "invalid"}
	p2.Save(false)

	users, err := User{}.JoinsPosts().Where("posts.name", "name").Query()
	assertError(t, err)
	if len(users) != 1 {
		t.Errorf("record count should be 1, but %v", len(users))
	}
	assertEqualStruct(t, users[0], u)

	// Posts joins user
	posts, err := Post{}.JoinsUser().Where("posts.name", "name").Query()
	assertError(t, err)

	if len(posts) != 1 {
		t.Errorf("record count should be 1, but %v", len(posts))
	}
	assertEqualStruct(t, posts[0], p1)
}

func TestExists(t *testing.T) {
	defer User{}.DeleteAll()
	exist := User{}.Exists()
	if exist {
		t.Errorf("record shouldn't exist, but exist")
	}

	User{}.Create(UserParams{Name: "test"})
	exist = User{}.Where("name", "test").Exists()
	if !exist {
		t.Errorf("record should exist, but dosen't exist")
	}
}

func TestCount(t *testing.T) {
	defer User{}.DeleteAll()
	count := User{}.Count()
	if count != 0 {
		t.Errorf("record count should be 0, but %v", count)
	}

	User{}.Create(UserParams{Name: "test"})
	count = User{}.Where("name", "test").Count("id")
	if count != 1 {
		t.Errorf("record count should be 1, but %v", count)
	}
}

func TestAll(t *testing.T) {
	defer User{}.DeleteAll()
	users, _ := User{}.All().Query()
	if len(users) != 0 {
		t.Errorf("record count should be 0, but %v", len(users))
	}

	User{}.Create(UserParams{Name: "test"})
	users, _ = User{}.Where("name", "test").All().Query()
	if len(users) != 1 {
		t.Errorf("record count should be 1, but %v", len(users))
	}
}

func assertEqualStruct(t *testing.T, expect, actual interface{}) {
	if !reflect.DeepEqual(expect, actual) {
		t.Errorf("struct should be equal to %v, but %v", expect, actual)
	}
}

func assertErrors(t *testing.T, errs *ar.Errors) {
	if errs != nil {
		t.Errorf("errors should be nil, but %v", errs)
	}
}

func assertError(t *testing.T, err error) {
	if err != nil {
		t.Errorf("error should be nil, but %v", err)
	}
}
<commit_msg>Added test for mysql.<commit_after>package tests

import (
	"database/sql"
	"log"
	"os"
	"reflect"
	"testing"

	_ "github.com/go-sql-driver/mysql"
	_ "github.com/mattn/go-sqlite3"
	"github.com/monochromegane/argen"
)

func TestMain(m *testing.M) {
	db, err := testDb()
	if err != nil {
		log.Fatal(err)
	}
	defer db.Close()

	Use(db)
	LogMode(true)
	for _, q := range testTables() {
		_, err = db.Exec(q)
		if err != nil {
			log.Fatal(err, q)
		}
	}

	os.Exit(m.Run())
}

func TestSelect(t *testing.T) {
	u := &User{Name: "test"}
	u.Save()
	defer User{}.DeleteAll()

	u, err := User{}.Select("id").First()
	assertError(t, err)

	if !ar.IsZero(u.Name) {
		t.Errorf("column value should be empty, but %s", u.Name)
	}
}

func TestFind(t *testing.T) {
	expect := &User{Name: "test"}
	expect.Save()
	defer User{}.DeleteAll()

	u, err := User{}.Find(expect.Id)
	assertError(t, err)
	assertEqualStruct(t, expect, u)
}

func TestFindBy(t *testing.T) {
	expect := &User{Name: "test"}
	expect.Save()
	defer User{}.DeleteAll()

	u, err := User{}.FindBy("name", "test")
	assertError(t, err)
	assertEqualStruct(t, expect, u)
}

func TestFirst(t *testing.T) {
	for _, name := range []string{"test1", "test2"} {
		u := &User{Name: name}
		u.Save()
	}
	defer User{}.DeleteAll()

	expect, _ := User{}.Where("name", "test1").QueryRow()

	u, err := User{}.First()
	assertError(t, err)
	assertEqualStruct(t, expect, u)
}

func TestLast(t *testing.T) {
	for _, name := range []string{"test1", "test2"} {
		u := &User{Name: name}
		u.Save()
	}
	defer User{}.DeleteAll()

	expect, _ := User{}.Where("name", "test2").QueryRow()

	u, err := User{}.Last()
	assertError(t, err)
	assertEqualStruct(t, expect, u)
}

func TestWhere(t *testing.T) {
	expect := &User{Name: "test"}
	expect.Save()
	defer User{}.DeleteAll()

	u, err := User{}.Where("name", "test").And("id", expect.Id).QueryRow()

	assertError(t, err)
	assertEqualStruct(t, expect, u)
}

func TestOrder(t *testing.T) {
	expects := []string{"test1", "test2"}
	for _, name := range expects {
		u := &User{Name: name}
		u.Save()
	}
	defer User{}.DeleteAll()

	users, err := User{}.Order("name", "ASC").Query()

	assertError(t, err)
	for i, u := range users {
		if u.Name != expects[i] {
			t.Errorf("column value should be %v, but %v", expects[i], u.Name)
		}
	}
}

func TestLimitAndOffset(t *testing.T) {
	for _, name := range []string{"test1", "test2", "test3"} {
		u := &User{Name: name}
		u.Save()
	}
	defer User{}.DeleteAll()

	users, err := User{}.Limit(2).Offset(1).Order("name", "ASC").Query()

	assertError(t, err)
	expects := []string{"test2", "test3"}
	for i, u := range users {
		if u.Name != expects[i] {
			t.Errorf("column value should be %v, but %v", expects[i], u.Name)
		}
	}
}

func TestGroupByAndHaving(t *testing.T) {
	for _, name := range []string{"testA", "testB", "testB"} {
		u := &User{Name: name}
		u.Save()
	}
	defer User{}.DeleteAll()

	users, err := User{}.Group("name").Having("count(name)", 2).Query()

	assertError(t, err)
	expects := []string{"testB"}
	for i, u := range users {
		if u.Name != expects[i] {
			t.Errorf("column value should be %v, but %v", expects[i], u.Name)
		}
	}
}

func TestExplain(t *testing.T) {
	err := User{}.Where("name", "test").Explain()
	assertError(t, err)
}

func TestIsValid(t *testing.T) {
	p := &Post{Name: "abc"}
	_, errs := p.IsValid()

	if len(errs.Messages["name"]) != 1 {
		t.Errorf("errors count should be 1, but %d", len(errs.Messages["name"]))
	}
}

func TestBuild(t *testing.T) {
	defer User{}.DeleteAll()
	u, _ := User{}.Create(UserParams{Name: "TestCreate"})

	p := u.BuildPost(PostParams{Name: "name"})

	if p.UserId != u.Id {
		t.Errorf("column value should be %v, but %v", u.Id, p.UserId)
	}
}

func TestCreate(t *testing.T) {
	u, errs := User{}.Create(UserParams{
		Name: "TestCreate",
	})
	defer User{}.DeleteAll()

	assertErrors(t, errs)

	expect, _ := User{}.FindBy("name", "TestCreate")
	assertEqualStruct(t, expect, u)
}

func TestIsNewRecordAndIsPresistent(t *testing.T) {
	defer User{}.DeleteAll()

	u := &User{Name: "test"}
	if !u.IsNewRecord() {
		t.Errorf("struct is new record, but isn't new record.")
	}

	u.Save()
	if !u.IsPersistent() {
		t.Errorf("struct is persistent, but isn't persistent.")
	}
}

func TestSaveWithInvalidData(t *testing.T) {
	defer Post{}.DeleteAll()

	// OnCreate
	p := &Post{Name: "invalid"}
	_, errs := p.Save()

	if len(errs.Messages["name"]) != 1 {
		t.Errorf("errors count should be 1, but %d", len(errs.Messages["name"]))
	}

	p.Name = "name"
	_, errs = p.Save()
	assertErrors(t, errs)

	// OnUpdate
	p.Name = "invalid2"
	_, errs = p.Save()

	if len(errs.Messages["name"]) != 1 {
		t.Errorf("errors count should be 1, but %d", len(errs.Messages["name"]))
	}

}

func TestSave(t *testing.T) {
	defer User{}.DeleteAll()

	u := &User{Name: "test"}

	_, errs := u.Save()
	assertErrors(t, errs)

	if u.Id == 0 {
		t.Errorf("Id should be setted after save, but isn't setted")
	}

	expect, _ := User{}.FindBy("name", "test")
	assertEqualStruct(t, expect, u)

	u.Name = "test2"
	_, errs = u.Save()
	assertErrors(t, errs)

	expect, _ = User{}.Find(u.Id)
	assertEqualStruct(t, expect, u)
}

func TestUpdate(t *testing.T) {
	defer User{}.DeleteAll()

	u := &User{Name: "test"}
	_, errs := u.Save()

	expect := UserParams{Name: "test2"}
	_, errs = u.Update(expect)
	assertErrors(t, errs)

	actual, _ := User{}.Find(u.Id)
	if expect.Name != actual.Name {
		t.Errorf("column value should be equal to %v, but %v", expect.Name, actual.Name)
	}
}

func TestUpdateColumns(t *testing.T) {
	defer User{}.DeleteAll()

	u := &User{Name: "test"}
	_, errs := u.Save()

	expect := UserParams{Name: "test2"}
	_, errs = u.UpdateColumns(expect)
	assertErrors(t, errs)

	actual, _ := User{}.Find(u.Id)
	if expect.Name != actual.Name {
		t.Errorf("column value should be equal to %v, but %v", expect.Name, actual.Name)
	}
}

func TestDelete(t *testing.T) {
	defer User{}.DeleteAll()

	u, _ := User{}.Create(UserParams{Name: "test1"})

	_, errs := u.Delete()
	assertErrors(t, errs)

	actual, _ := User{}.Find(u.Id)
	if actual != nil {
		t.Errorf("record should be deleted, but isn't deleted %v", actual)
	}
}

func TestScope(t *testing.T) {
	defer User{}.DeleteAll()

	User{}.Create(UserParams{Name: "test1", Age: 20})
	expect, _ := User{}.Create(UserParams{Name: "test2", Age: 21})

	users, _ := User{}.OlderThan(20).Query()

	if len(users) != 1 {
		t.Errorf("record count should be 1, but %v", len(users))
	}
	assertEqualStruct(t, users[0], expect)
}

func TestHasMany(t *testing.T) {
	defer func() {
		User{}.DeleteAll()
		Post{}.DeleteAll()
	}()

	u, _ := User{}.Create(UserParams{Name: "test1"})
	expect, _ := Post{}.Create(PostParams{UserId: u.Id, Name: "name"})

	user, err := User{}.Find(u.Id)
	posts, err := user.Posts()
	assertError(t, err)
	if len(posts) != 1 {
		t.Errorf("record count should be 1, but %v", len(posts))
	}
	assertEqualStruct(t, posts[0], expect)
}

func TestBelongsTo(t *testing.T) {
	defer func() {
		User{}.DeleteAll()
		Post{}.DeleteAll()
	}()

	expect, _ := User{}.Create(UserParams{Name: "test1"})
	p, _ := Post{}.Create(PostParams{UserId: expect.Id, Name: "name"})

	user, err := p.User()
	assertError(t, err)
	assertEqualStruct(t, user, expect)
}

func TestJoins(t *testing.T) {
	defer func() {
		User{}.DeleteAll()
		Post{}.DeleteAll()
	}()

	// User joins posts
	u, _ := User{}.Create(UserParams{Name: "test1"})
	p1, _ := Post{}.Create(PostParams{UserId: u.Id, Name: "name"})
	p2 := Post{UserId: u.Id, Name: "invalid"}
	p2.Save(false)

	users, err := User{}.JoinsPosts().Where("posts.name", "name").Query()
	assertError(t, err)
	if len(users) != 1 {
		t.Errorf("record count should be 1, but %v", len(users))
	}
	assertEqualStruct(t, users[0], u)

	// Posts joins user
	posts, err := Post{}.JoinsUser().Where("posts.name", "name").Query()
	assertError(t, err)

	if len(posts) != 1 {
		t.Errorf("record count should be 1, but %v", len(posts))
	}
	assertEqualStruct(t, posts[0], p1)
}

func TestExists(t *testing.T) {
	defer User{}.DeleteAll()
	exist := User{}.Exists()
	if exist {
		t.Errorf("record shouldn't exist, but exist")
	}

	User{}.Create(UserParams{Name: "test"})
	exist = User{}.Where("name", "test").Exists()
	if !exist {
		t.Errorf("record should exist, but dosen't exist")
	}
}

func TestCount(t *testing.T) {
	defer User{}.DeleteAll()
	count := User{}.Count()
	if count != 0 {
		t.Errorf("record count should be 0, but %v", count)
	}

	User{}.Create(UserParams{Name: "test"})
	count = User{}.Where("name", "test").Count("id")
	if count != 1 {
		t.Errorf("record count should be 1, but %v", count)
	}
}

func TestAll(t *testing.T) {
	defer User{}.DeleteAll()
	users, _ := User{}.All().Query()
	if len(users) != 0 {
		t.Errorf("record count should be 0, but %v", len(users))
	}

	User{}.Create(UserParams{Name: "test"})
	users, _ = User{}.Where("name", "test").All().Query()
	if len(users) != 1 {
		t.Errorf("record count should be 1, but %v", len(users))
	}
}

func assertEqualStruct(t *testing.T, expect, actual interface{}) {
	if !reflect.DeepEqual(expect, actual) {
		t.Errorf("struct should be equal to %v, but %v", expect, actual)
	}
}

func assertErrors(t *testing.T, errs *ar.Errors) {
	if errs != nil {
		t.Errorf("errors should be nil, but %v", errs)
	}
}

func assertError(t *testing.T, err error) {
	if err != nil {
		t.Errorf("error should be nil, but %v", err)
	}
}

func testDb() (*sql.DB, error) {
	switch os.Getenv("DB") {
	case "mysql":
		return sql.Open("mysql", "travis@/argen_test")
	case "sqlite3", "":
		return sql.Open("sqlite3", ":memory:")
	}
	return nil, nil
}

func testTables() []string {
	switch os.Getenv("DB") {
	case "mysql":
		return []string{
			"drop table if exists users;",
			"drop table if exists posts;",
			"create table users (id INTEGER PRIMARY KEY AUTO_INCREMENT, name text, age integer);",
			"create table posts (id INTEGER PRIMARY KEY AUTO_INCREMENT, user_id integer not null, name text);",
		}
	case "sqlite3", "":
		return []string{
			"create table users (id integer PRIMARY KEY AUTOINCREMENT, name text, age integer);",
			"create table posts (id integer PRIMARY KEY AUTOINCREMENT, user_id integer not null, name text);",
		}
	}
	return []string{}
}
<|endoftext|>


<commit_before>package redis

import (
	"time"
	"errors"
	"log"
	"fmt"
	"github.com/fzzy/radix/redis"
	"github.com/mediocregopher/hyrax/server/storage-router/storage/command"
	"github.com/mediocregopher/hyrax/server/storage-router/storage/unit"
)

type RedisConn struct {
	conn *redis.Client
	cmdCh chan *command.CommandBundle
	closeCh chan chan error
}

func New() unit.StorageUnitConn {
	return &RedisConn{
		conn: nil,
		cmdCh: make(chan *command.CommandBundle),
		closeCh: make(chan chan error),
	}
}

// Implements Connect for StorageUnitConn. Connects to redis over tcp and spawns
// a handler go-routine
func (r *RedisConn) Connect(conntype, addr string, _ ... interface{}) error {
	log.Println("redis.Dial(", conntype, addr, ")")
	conn, err := redis.Dial(conntype, addr)
	if err != nil {
		return err
	}

	r.conn = conn
	go r.spin()
	return nil
}

func (r *RedisConn) spin() {
	for {
		select {

		case retCh := <- r.closeCh:
			retCh <- r.conn.Close()
			close(r.cmdCh)
			close(r.closeCh)
			return

		case cmdb := <- r.cmdCh:
			r, err := r.cmd(cmdb.Cmd)
			ret := command.CommandRet{r, err}
			select {
			case cmdb.RetCh <- &ret:
			case <-time.After(10 * time.Second):
				log.Printf("Timedout in redisconn replying to cmd %v", cmdb.Cmd)
			}
		}
	}
}

func (r *RedisConn) cmd(cmd command.Command) (interface{}, error) {
	if trans := cmd.ExpandTransaction(); trans != nil {
		r.conn.Append(string(MULTI))
		for i := range trans {
			r.conn.Append(string(trans[i].Cmd()), trans[i].Args()...)
		}
		r.conn.Append(string(EXEC))

		for i := 0; i < len(trans)+1; i++ {
			r.conn.GetReply()
		}
		return decodeReply(r.conn.GetReply())
	} else {
		reply := r.conn.Cmd(string(cmd.Cmd()), cmd.Args()...)
		return decodeReply(reply)
	}
}

// Decodes a reply into a generic interface object, or an error
func decodeReply(r *redis.Reply) (interface{}, error) {
	switch r.Type {
	case redis.StatusReply:
		return r.Bytes()

	case redis.ErrorReply:
		return nil, r.Err

	case redis.IntegerReply:
		return r.Int()

	case redis.NilReply:
		return nil, nil

	case redis.BulkReply:
		return r.Bytes()

	case redis.MultiReply:
		return r.ListBytes()
	}

	return nil, nil
}

// Implements Cmd for StorageUnitConn.
func (r *RedisConn) Cmd(cmdb *command.CommandBundle) {
	select {
	case r.cmdCh <- cmdb:
	case <-time.After(10 * time.Second):
		err := fmt.Errorf("Redis connection timedout receiving command")
		select {
		case cmdb.RetCh <- &command.CommandRet{nil, err}:
		case <-time.After(1 * time.Second):
		}
	}
}

// Implements Close for StorageUnitConn.
func (r *RedisConn) Close() error {
	retCh := make(chan error)
	r.closeCh <- retCh
	return <- retCh
}

// RedisListToMap is used for converting the return of a HGETALL to a hash
func RedisListToMap(l [][]byte) (map[string][]byte, error) {
	llen := len(l)
	if llen%2 != 0 {
		return nil, errors.New("list has uneven number of elements")
	}

	m := map[string][]byte{}

	halfllen := llen / 2
	for i := 0; i < halfllen; i++ {
		m[string(l[i*2])] = l[i*2+1]
	}

	return m, nil
}
<commit_msg>fixed bug in redis storage connection<commit_after>package redis

import (
	"time"
	"errors"
	"fmt"
	"log"
	"github.com/fzzy/radix/redis"
	"github.com/mediocregopher/hyrax/server/storage-router/storage/command"
	"github.com/mediocregopher/hyrax/server/storage-router/storage/unit"
)

type RedisConn struct {
	conn *redis.Client
	cmdCh chan *command.CommandBundle
	closeCh chan chan error
}

func New() unit.StorageUnitConn {
	return &RedisConn{
		conn: nil,
		cmdCh: make(chan *command.CommandBundle),
		closeCh: make(chan chan error),
	}
}

// Implements Connect for StorageUnitConn. Connects to redis over tcp and spawns
// a handler go-routine
func (r *RedisConn) Connect(conntype, addr string, _ ... interface{}) error {
	conn, err := redis.Dial(conntype, addr)
	if err != nil {
		return err
	}

	r.conn = conn
	go r.spin()
	return nil
}

func (r *RedisConn) spin() {
	for {
		select {

		case retCh := <- r.closeCh:
			retCh <- r.conn.Close()
			close(r.cmdCh)
			close(r.closeCh)
			return

		case cmdb := <- r.cmdCh:
			rawret, err := r.cmd(cmdb.Cmd)
			ret := command.CommandRet{rawret, err}
			select {
			case cmdb.RetCh <- &ret:
			case <-time.After(10 * time.Second):
				log.Printf("Timedout in redisconn replying to cmd %v", cmdb.Cmd)
			}
		}
	}
}

func (r *RedisConn) cmd(cmd command.Command) (interface{}, error) {
	if trans := cmd.ExpandTransaction(); trans != nil {
		r.conn.Append(string(MULTI))
		for i := range trans {
			r.conn.Append(string(trans[i].Cmd()), trans[i].Args()...)
		}
		r.conn.Append(string(EXEC))

		for i := 0; i < len(trans)+1; i++ {
			r.conn.GetReply()
		}
		return decodeReply(r.conn.GetReply())
	} else {
		reply := r.conn.Cmd(string(cmd.Cmd()), cmd.Args()...)
		return decodeReply(reply)
	}
}

// Decodes a reply into a generic interface object, or an error
func decodeReply(r *redis.Reply) (interface{}, error) {
	switch r.Type {
	case redis.StatusReply:
		return r.Bytes()

	case redis.ErrorReply:
		return nil, r.Err

	case redis.IntegerReply:
		return r.Int()

	case redis.NilReply:
		return nil, nil

	case redis.BulkReply:
		return r.Bytes()

	case redis.MultiReply:
		return r.ListBytes()
	}

	return nil, nil
}

// Implements Cmd for StorageUnitConn.
func (r *RedisConn) Cmd(cmdb *command.CommandBundle) {
	select {
	case r.cmdCh <- cmdb:
	case <-time.After(10 * time.Second):
		err := fmt.Errorf("Redis connection timedout receiving command")
		select {
		case cmdb.RetCh <- &command.CommandRet{nil, err}:
		case <-time.After(1 * time.Second):
		}
	}
}

// Implements Close for StorageUnitConn.
func (r *RedisConn) Close() error {
	retCh := make(chan error)
	r.closeCh <- retCh
	return <- retCh
}

// RedisListToMap is used for converting the return of a HGETALL to a hash
func RedisListToMap(l [][]byte) (map[string][]byte, error) {
	llen := len(l)
	if llen%2 != 0 {
		return nil, errors.New("list has uneven number of elements")
	}

	m := map[string][]byte{}

	halfllen := llen / 2
	for i := 0; i < halfllen; i++ {
		m[string(l[i*2])] = l[i*2+1]
	}

	return m, nil
}
<|endoftext|>


<commit_before>package buffer

import (
	"fmt"
)

type UpdatesService struct {
	client *Client
}

type Update struct {
	CreatedAt       int                    `json:"created_at,omitempty"`
	ID              string                 `json:"id,omitempty"`
	Day             string                 `json:"day,omitempty"`
	DueAt           int                    `json:"due_at,omitempty"`
	DueTime         string                 `json:"due_time,omitempty"`
	ProfileID       string                 `json:"profile_id,omitempty"`
	ProfileService  string                 `json:"profile_service,omitempty"`
	SentAt          int                    `json:"sent_at,omitempty"`
	ServiceUpdateID string                 `json:"service_update_id,omitempty"`
	Statistics      map[string]interface{} `json:"statistics,omitempty"`
	Status          string                 `json:"status,omitempty"`
	Text            string                 `json:"text,omitempty"`
	TextFormatted   string                 `json:"text_formatted,omitempty"`
	UserID          string                 `json:"user_id,omitempty"`
	Via             string                 `json:"via,omitempty"`
}

type Updates struct {
	Total   int      `json:"total,omitempty"`
	Updates []Update `json:"updates,omitempty"`
}

func (s *UpdatesService) Destroy(updateID string) (bool, error) {
	u := fmt.Sprintf("/1/updates/%v/destroy.json", updateID)

	req, err := s.client.NewRequest("POST", u, nil)
	if err != nil {
		return false, err
	}

	_, err = s.client.Do(req, nil)

	return true, err
}

func (s *UpdatesService) Get(updateID string) (*Update, error) {
	u := fmt.Sprintf("/1/updates/%v.json", updateID)

	req, err := s.client.NewRequest("GET", u, nil)
	if err != nil {
		return nil, err
	}

	update := new(Update)
	_, err = s.client.Do(req, update)

	return update, err
}

func (s *UpdatesService) Share(updateID string) (bool, error) {
	u := fmt.Sprintf("/1/updates/%v/share.json", updateID)

	req, err := s.client.NewRequest("POST", u, nil)
	if err != nil {
		return false, err
	}

	_, err = s.client.Do(req, nil)

	return true, err
}
<commit_msg>Add /updates/:id/move_to_top endpoint<commit_after>package buffer

import (
	"fmt"
)

type UpdatesService struct {
	client *Client
}

type Update struct {
	CreatedAt       int                    `json:"created_at,omitempty"`
	ID              string                 `json:"id,omitempty"`
	Day             string                 `json:"day,omitempty"`
	DueAt           int                    `json:"due_at,omitempty"`
	DueTime         string                 `json:"due_time,omitempty"`
	ProfileID       string                 `json:"profile_id,omitempty"`
	ProfileService  string                 `json:"profile_service,omitempty"`
	SentAt          int                    `json:"sent_at,omitempty"`
	ServiceUpdateID string                 `json:"service_update_id,omitempty"`
	Statistics      map[string]interface{} `json:"statistics,omitempty"`
	Status          string                 `json:"status,omitempty"`
	Text            string                 `json:"text,omitempty"`
	TextFormatted   string                 `json:"text_formatted,omitempty"`
	UserID          string                 `json:"user_id,omitempty"`
	Via             string                 `json:"via,omitempty"`
}

type Updates struct {
	Total   int      `json:"total,omitempty"`
	Updates []Update `json:"updates,omitempty"`
}

func (s *UpdatesService) Destroy(updateID string) (bool, error) {
	u := fmt.Sprintf("/1/updates/%v/destroy.json", updateID)

	req, err := s.client.NewRequest("POST", u, nil)
	if err != nil {
		return false, err
	}

	_, err = s.client.Do(req, nil)

	return true, err
}

func (s *UpdatesService) Get(updateID string) (*Update, error) {
	u := fmt.Sprintf("/1/updates/%v.json", updateID)

	req, err := s.client.NewRequest("GET", u, nil)
	if err != nil {
		return nil, err
	}

	update := new(Update)
	_, err = s.client.Do(req, update)

	return update, err
}

func (s *UpdatesService) MoveToTop(updateID string) (*Update, error) {
	u := fmt.Sprintf("/1/updates/%v/move_to_top.json", updateID)

	req, err := s.client.NewRequest("POST", u, nil)
	if err != nil {
		return nil, err
	}

	update := new(Update)
	_, err = s.client.Do(req, update)

	return update, err
}

func (s *UpdatesService) Share(updateID string) (bool, error) {
	u := fmt.Sprintf("/1/updates/%v/share.json", updateID)

	req, err := s.client.NewRequest("POST", u, nil)
	if err != nil {
		return false, err
	}

	_, err = s.client.Do(req, nil)

	return true, err
}
<|endoftext|>