<commit_before>package models

import (
	"debug/dwarf"
	"io/ioutil"
	"os"
	"path"
	"path/filepath"
	"sort"
	"strings"
)

type SourceLine struct {
	dwarf.LineEntry
	Start  uint64
	End    uint64
	Source string
}

func (s *SourceLine) Contains(addr uint64) bool {
	return s.Start <= addr && s.End > addr
}

type DebugFile struct {
	Symbols   []Symbol
	DWARF     *dwarf.Data
	SourceMap []*SourceLine
	SymbolMap map[string]Symbol
}

type symByStart []Symbol
type srcByStart []*SourceLine

func (a symByStart) Len() int      { return len(a) }
func (a symByStart) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
func (a symByStart) Less(i, j int) bool {
	return a[i].Start < a[j].Start || a[i].Start == a[j].Start && a[i].End < a[j].End
}
func (a srcByStart) Len() int      { return len(a) }
func (a srcByStart) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
func (a srcByStart) Less(i, j int) bool {
	return a[i].Start < a[j].Start || a[i].Start == a[j].Start && a[i].End < a[j].End
}

// sorts symbols by starting addr for binary search during symbolication
// builds source and symbol maps
func (m *DebugFile) CacheSym() {
	m.SymbolMap = make(map[string]Symbol)
	for _, sym := range m.Symbols {
		m.SymbolMap[sym.Name] = sym
	}
	sort.Sort(symByStart(m.Symbols))
}

func (m *DebugFile) CacheSource(srcPaths []string) {
	m.SourceMap = m.buildSourceMap(srcPaths)
	sort.Sort(srcByStart(m.SourceMap))
}

func findFile(srcPaths []string, parent string, shortname string, fullname string) []string {
	// TODO: if path is absolute, try -prefix
	// TODO: relative path to the exectuable?
	basename := path.Base(shortname)
	parname := path.Join(parent, shortname)
	names := []string{shortname, fullname, basename, parname}
	candidates := names
	for _, src := range srcPaths {
		for _, end := range names {
			candidates = append(candidates, filepath.Join(src, end))
		}
	}
	for _, fname := range candidates {
		if _, err := os.Stat(fname); err == nil {
			if data, err := ioutil.ReadFile(fname); err == nil {
				return strings.Split(string(data), "\n")
			}
		}
	}
	return nil
}

func (m *DebugFile) buildSourceMap(srcPaths []string) []*SourceLine {
	var lines []*SourceLine
	if m.DWARF == nil {
		return nil
	}
	reader := m.DWARF.Reader()
	var compdirs []string
	for {
		entry, err := reader.Next()
		if err != nil || entry == nil {
			break
		}
		if entry.Tag == dwarf.TagCompileUnit {
			compdirs = append(compdirs, entry.AttrField(dwarf.AttrCompDir).Val.(string))
		}
	}
	var common string
	if len(compdirs) > 0 {
		common = path.Clean(compdirs[0])
		for _, dir := range compdirs[1:] {
			dir = path.Clean(dir)
			for i := 0; i < len(dir) && i < len(common); i++ {
				if common[i] != dir[i] {
					common = common[:i]
					break
				}
			}
		}
		common = path.Dir(common)
	}

	reader.Seek(0)
	for {
		entry, err := reader.Next()
		if err != nil || entry == nil {
			break
		}
		if entry.Tag == dwarf.TagCompileUnit {
			files := make(map[string][]string)
			if reader, err := m.DWARF.LineReader(entry); err == nil {
				var line dwarf.LineEntry
				var sl *SourceLine
				for {
					err := reader.Next(&line)
					if err != nil {
						break
					}
					source := ""
					shortname := line.File.Name
					fullname := line.File.Name
					if tmp, err := filepath.Rel(common, fullname); err == nil {
						shortname = tmp
					}
					var file []string
					var ok bool
					if file, ok = files[fullname]; !ok {
						file = findFile(srcPaths, path.Base(common), shortname, fullname)
						files[fullname] = file
					}
					if len(file) > 0 && line.Line-1 < len(file) {
						source = file[line.Line-1]
					}
					if sl != nil {
						sl.End = line.Address + 1
					}
					if line.EndSequence {
						break
					}
					sl = &SourceLine{
						LineEntry: line,
						Start:     line.Address,
						End:       line.Address + 1,
						Source:    strings.Replace(source, "\t", "    ", -1),
					}
					lines = append(lines, sl)
				}
			}
		}
		reader.SkipChildren()
	}
	return lines
}

// binary search on m.Symbols
func (m *DebugFile) Symbolicate(addr uint64) (result Symbol, distance uint64) {
	l := 0
	r := len(m.Symbols) - 1
	for l <= r {
		mid := (l + r) / 2
		e := m.Symbols[mid]
		if addr >= e.End {
			l = mid + 1
		} else if addr < e.Start {
			r = mid - 1
		} else {
			return e, addr - e.Start
		}
	}
	return
}

func (m *DebugFile) SymbolLookup(name string) Symbol {
	if s, ok := m.SymbolMap[name]; ok {
		return s
	}
	return Symbol{}
}

// performs a binary search on m.SourceMap for addr
func (m *DebugFile) FileLine(addr uint64) *SourceLine {
	l := 0
	r := len(m.SourceMap) - 1
	for l <= r {
		mid := (l + r) / 2
		e := m.SourceMap[mid]
		if addr >= e.End {
			l = mid + 1
		} else if addr < e.Start {
			r = mid - 1
		} else {
			return e
		}
	}
	return nil
}
<commit_msg>reuse source across a whole DebugFile<commit_after>package models

import (
	"debug/dwarf"
	"io/ioutil"
	"os"
	"path"
	"path/filepath"
	"sort"
	"strings"
)

type SourceLine struct {
	dwarf.LineEntry
	Start  uint64
	End    uint64
	Source string
}

func (s *SourceLine) Contains(addr uint64) bool {
	return s.Start <= addr && s.End > addr
}

type DebugFile struct {
	Symbols   []Symbol
	DWARF     *dwarf.Data
	SourceMap []*SourceLine
	SymbolMap map[string]Symbol
}

type symByStart []Symbol
type srcByStart []*SourceLine

func (a symByStart) Len() int      { return len(a) }
func (a symByStart) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
func (a symByStart) Less(i, j int) bool {
	return a[i].Start < a[j].Start || a[i].Start == a[j].Start && a[i].End < a[j].End
}
func (a srcByStart) Len() int      { return len(a) }
func (a srcByStart) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
func (a srcByStart) Less(i, j int) bool {
	return a[i].Start < a[j].Start || a[i].Start == a[j].Start && a[i].End < a[j].End
}

// sorts symbols by starting addr for binary search during symbolication
// builds source and symbol maps
func (m *DebugFile) CacheSym() {
	m.SymbolMap = make(map[string]Symbol)
	for _, sym := range m.Symbols {
		m.SymbolMap[sym.Name] = sym
	}
	sort.Sort(symByStart(m.Symbols))
}

func (m *DebugFile) CacheSource(srcPaths []string) {
	m.SourceMap = m.buildSourceMap(srcPaths)
	sort.Sort(srcByStart(m.SourceMap))
}

func findFile(srcPaths []string, parent string, shortname string, fullname string) []string {
	// TODO: if path is absolute, try -prefix
	// TODO: relative path to the exectuable?
	basename := path.Base(shortname)
	parname := path.Join(parent, shortname)
	names := []string{shortname, fullname, basename, parname}
	candidates := names
	for _, src := range srcPaths {
		for _, end := range names {
			candidates = append(candidates, filepath.Join(src, end))
		}
	}
	for _, fname := range candidates {
		if _, err := os.Stat(fname); err == nil {
			if data, err := ioutil.ReadFile(fname); err == nil {
				return strings.Split(string(data), "\n")
			}
		}
	}
	return nil
}

func (m *DebugFile) buildSourceMap(srcPaths []string) []*SourceLine {
	var lines []*SourceLine
	if m.DWARF == nil {
		return nil
	}
	reader := m.DWARF.Reader()
	var compdirs []string
	for {
		entry, err := reader.Next()
		if err != nil || entry == nil {
			break
		}
		if entry.Tag == dwarf.TagCompileUnit {
			compdirs = append(compdirs, entry.AttrField(dwarf.AttrCompDir).Val.(string))
		}
	}
	var common string
	if len(compdirs) > 0 {
		common = path.Clean(compdirs[0])
		for _, dir := range compdirs[1:] {
			dir = path.Clean(dir)
			for i := 0; i < len(dir) && i < len(common); i++ {
				if common[i] != dir[i] {
					common = common[:i]
					break
				}
			}
		}
		common = path.Dir(common)
	}

	files := make(map[string][]string)
	reader.Seek(0)
	for {
		entry, err := reader.Next()
		if err != nil || entry == nil {
			break
		}
		if entry.Tag == dwarf.TagCompileUnit {
			if reader, err := m.DWARF.LineReader(entry); err == nil {
				var line dwarf.LineEntry
				var sl *SourceLine
				for {
					err := reader.Next(&line)
					if err != nil {
						break
					}
					source := ""
					shortname := line.File.Name
					fullname := line.File.Name
					if tmp, err := filepath.Rel(common, fullname); err == nil {
						shortname = tmp
					}
					var file []string
					var ok bool
					if file, ok = files[fullname]; !ok {
						file = findFile(srcPaths, path.Base(common), shortname, fullname)
						files[fullname] = file
					}
					if len(file) > 0 && line.Line-1 < len(file) {
						source = file[line.Line-1]
					}
					if sl != nil {
						sl.End = line.Address + 1
					}
					if line.EndSequence {
						break
					}
					sl = &SourceLine{
						LineEntry: line,
						Start:     line.Address,
						End:       line.Address + 1,
						Source:    strings.Replace(source, "\t", "    ", -1),
					}
					lines = append(lines, sl)
				}
			}
		}
		reader.SkipChildren()
	}
	return lines
}

// binary search on m.Symbols
func (m *DebugFile) Symbolicate(addr uint64) (result Symbol, distance uint64) {
	l := 0
	r := len(m.Symbols) - 1
	for l <= r {
		mid := (l + r) / 2
		e := m.Symbols[mid]
		if addr >= e.End {
			l = mid + 1
		} else if addr < e.Start {
			r = mid - 1
		} else {
			return e, addr - e.Start
		}
	}
	return
}

func (m *DebugFile) SymbolLookup(name string) Symbol {
	if s, ok := m.SymbolMap[name]; ok {
		return s
	}
	return Symbol{}
}

// performs a binary search on m.SourceMap for addr
func (m *DebugFile) FileLine(addr uint64) *SourceLine {
	l := 0
	r := len(m.SourceMap) - 1
	for l <= r {
		mid := (l + r) / 2
		e := m.SourceMap[mid]
		if addr >= e.End {
			l = mid + 1
		} else if addr < e.Start {
			r = mid - 1
		} else {
			return e
		}
	}
	return nil
}
<|endoftext|>


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

package systests

// Test various RPCs that are used mainly in other clients but not by the CLI.

import (
	"testing"

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

func TestRPCs(t *testing.T) {
	tc := setupTest(t, "resolve2")
	tc2 := cloneContext(tc)

	libkb.G.LocalDb = nil

	defer tc.Cleanup()

	stopCh := make(chan error)
	svc := service.NewService(tc.G, false)
	startCh := svc.GetStartChannel()
	go func() {
		err := svc.Run()
		if err != nil {
			t.Logf("Running the service produced an error: %v", err)
		}
		stopCh <- err
	}()

	<-startCh

	// Add test RPC methods here.
	testIdentifyResolve2(t, tc2.G)
	testCheckInvitationCode(t, tc2.G)
	testLoadAllPublicKeysUnverified(t, tc2.G)
	testLoadUserWithNoKeys(t, tc2.G)
	testCheckDevicesForUser(t, tc2.G)

	if err := client.CtlServiceStop(tc2.G); err != nil {
		t.Fatal(err)
	}

	// If the server failed, it's also an error
	if err := <-stopCh; err != nil {
		t.Fatal(err)
	}
}

func testIdentifyResolve2(t *testing.T, g *libkb.GlobalContext) {

	cli, err := client.GetIdentifyClient(g)
	if err != nil {
		t.Fatalf("failed to get new identifyclient: %v", err)
	}

	if _, err := cli.Resolve(context.TODO(), "uid:eb72f49f2dde6429e5d78003dae0c919"); err != nil {
		t.Fatalf("Resolve failed: %v\n", err)
	}

	// We don't want to hit the cache, since the previous lookup never hit the
	// server.  For Resolve2, we have to, since we need a username.  So test that
	// here.
	if res, err := cli.Resolve2(context.TODO(), "uid:eb72f49f2dde6429e5d78003dae0c919"); err != nil {
		t.Fatalf("Resolve failed: %v\n", err)
	} else if res.Username != "t_tracy" {
		t.Fatalf("Wrong username: %s != 't_tracy", res.Username)
	}

	if res, err := cli.Resolve2(context.TODO(), "t_tracy@rooter"); err != nil {
		t.Fatalf("Resolve2 failed: %v\n", err)
	} else if res.Username != "t_tracy" {
		t.Fatalf("Wrong name: %s != 't_tracy", res.Username)
	} else if !res.Uid.Equal(keybase1.UID("eb72f49f2dde6429e5d78003dae0c919")) {
		t.Fatalf("Wrong uid for tracy: %s\n", res.Uid)
	}

	if _, err := cli.Resolve2(context.TODO(), "foobag@rooter"); err == nil {
		t.Fatalf("expected an error on a bad resolve, but got none")
	} else if _, ok := err.(libkb.ResolutionError); !ok {
		t.Fatalf("Wrong error: wanted type %T but got (%v, %T)", libkb.ResolutionError{}, err, err)
	}
}

func testCheckInvitationCode(t *testing.T, g *libkb.GlobalContext) {
	cli, err := client.GetSignupClient(g)
	if err != nil {
		t.Fatalf("failed to get a signup client: %v", err)
	}

	err = cli.CheckInvitationCode(context.TODO(), keybase1.CheckInvitationCodeArg{InvitationCode: libkb.TestInvitationCode})
	if err != nil {
		t.Fatalf("Did not expect an error code, but got: %v", err)
	}
	err = cli.CheckInvitationCode(context.TODO(), keybase1.CheckInvitationCodeArg{InvitationCode: "eeoeoeoe333o3"})
	if _, ok := err.(libkb.BadInvitationCodeError); !ok {
		t.Fatalf("Expected an error code, but got %T %v", err, err)
	}
}

func testLoadAllPublicKeysUnverified(t *testing.T, g *libkb.GlobalContext) {

	cli, err := client.GetUserClient(g)
	if err != nil {
		t.Fatalf("failed to get user client: %s", err)
	}

	// t_rosetta
	arg := keybase1.LoadAllPublicKeysUnverifiedArg{Uid: keybase1.UID("b8939251cb3d367e68587acb33a64d19")}
	res, err := cli.LoadAllPublicKeysUnverified(context.TODO(), arg)
	if err != nil {
		t.Fatalf("failed to make load keys call: %s", err)
	}

	if len(res) != 3 {
		t.Fatalf("wrong amount of keys loaded: %d != %d", len(res), 3)
	}

	keys := map[keybase1.KID]bool{
		keybase1.KID("0101fe1183765f256289427d6943cd8bab3b5fe095bcdd27f031ed298da523efd3120a"): true,
		keybase1.KID("0101b5839c4ccaa9d03b3016b9aa73a7e3eafb067f9c86c07a6f2f79cb8558b1c97f0a"): true,
		keybase1.KID("0101188ee7e63ccbd05af498772ab2975ee29df773240d17dde09aecf6c132a5a9a60a"): true,
	}

	for _, key := range res {
		if _, ok := keys[key.KID]; !ok {
			t.Fatalf("unknown key in response: %s", key.KID)
		}
	}
}

func testLoadUserWithNoKeys(t *testing.T, g *libkb.GlobalContext) {
	// The LoadUser class in libkb returns an error by default if the user in
	// question has no keys. The RPC methods that wrap it should suppress this
	// error, by setting the PublicKeyOptional flag.

	cli, err := client.GetUserClient(g)
	if err != nil {
		t.Fatalf("failed to get a user client: %v", err)
	}

	// Check the LoadUserByName RPC. t_ellen is a test user with no keys.
	loadUserByNameArg := keybase1.LoadUserByNameArg{Username: "t_ellen"}
	tEllen, err := cli.LoadUserByName(context.TODO(), loadUserByNameArg)
	if err != nil {
		t.Fatal(err)
	}
	if tEllen.Username != "t_ellen" {
		t.Fatalf("expected t_ellen, saw %s", tEllen.Username)
	}

	// Check the LoadUser RPC.
	loadUserArg := keybase1.LoadUserArg{Uid: tEllen.Uid}
	tEllen2, err := cli.LoadUser(context.TODO(), loadUserArg)
	if err != nil {
		t.Fatal(err)
	}
	if tEllen2.Username != "t_ellen" {
		t.Fatalf("expected t_ellen, saw %s", tEllen2.Username)
	}
}

func testCheckDevicesForUser(t *testing.T, g *libkb.GlobalContext) {
	cli, err := client.GetDeviceClient(g)
	if err != nil {
		t.Fatalf("failed to get a device client: %v", err)
	}
	err = cli.CheckDeviceNameForUser(context.TODO(), keybase1.CheckDeviceNameForUserArg{
		Username:   "t_frank",
		Devicename: "bad $ device $ name",
	})
	if _, ok := err.(libkb.DeviceBadNameError); !ok {
		t.Fatalf("wanted a bad device name error; got %v", err)
	}
	err = cli.CheckDeviceNameForUser(context.TODO(), keybase1.CheckDeviceNameForUserArg{
		Username:   "t_frank",
		Devicename: "go c lient",
	})
	if _, ok := err.(libkb.DeviceNameInUseError); !ok {
		t.Fatalf("wanted a name in use error; got %v", err)
	}
}
<commit_msg>test that found bug in #5320 (#5330)<commit_after>// Copyright 2015 Keybase, Inc. All rights reserved. Use of
// this source code is governed by the included BSD license.

package systests

// Test various RPCs that are used mainly in other clients but not by the CLI.

import (
	"testing"

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

func TestRPCs(t *testing.T) {
	tc := setupTest(t, "resolve2")
	tc2 := cloneContext(tc)

	libkb.G.LocalDb = nil

	defer tc.Cleanup()

	stopCh := make(chan error)
	svc := service.NewService(tc.G, false)
	startCh := svc.GetStartChannel()
	go func() {
		err := svc.Run()
		if err != nil {
			t.Logf("Running the service produced an error: %v", err)
		}
		stopCh <- err
	}()

	<-startCh

	// Add test RPC methods here.
	testIdentifyResolve2(t, tc2.G)
	testCheckInvitationCode(t, tc2.G)
	testLoadAllPublicKeysUnverified(t, tc2.G)
	testLoadUserWithNoKeys(t, tc2.G)
	testCheckDevicesForUser(t, tc2.G)
	testIdentify2(t, tc2.G)

	if err := client.CtlServiceStop(tc2.G); err != nil {
		t.Fatal(err)
	}

	// If the server failed, it's also an error
	if err := <-stopCh; err != nil {
		t.Fatal(err)
	}
}

func testIdentifyResolve2(t *testing.T, g *libkb.GlobalContext) {

	cli, err := client.GetIdentifyClient(g)
	if err != nil {
		t.Fatalf("failed to get new identifyclient: %v", err)
	}

	if _, err := cli.Resolve(context.TODO(), "uid:eb72f49f2dde6429e5d78003dae0c919"); err != nil {
		t.Fatalf("Resolve failed: %v\n", err)
	}

	// We don't want to hit the cache, since the previous lookup never hit the
	// server.  For Resolve2, we have to, since we need a username.  So test that
	// here.
	if res, err := cli.Resolve2(context.TODO(), "uid:eb72f49f2dde6429e5d78003dae0c919"); err != nil {
		t.Fatalf("Resolve failed: %v\n", err)
	} else if res.Username != "t_tracy" {
		t.Fatalf("Wrong username: %s != 't_tracy", res.Username)
	}

	if res, err := cli.Resolve2(context.TODO(), "t_tracy@rooter"); err != nil {
		t.Fatalf("Resolve2 failed: %v\n", err)
	} else if res.Username != "t_tracy" {
		t.Fatalf("Wrong name: %s != 't_tracy", res.Username)
	} else if !res.Uid.Equal(keybase1.UID("eb72f49f2dde6429e5d78003dae0c919")) {
		t.Fatalf("Wrong uid for tracy: %s\n", res.Uid)
	}

	if _, err := cli.Resolve2(context.TODO(), "foobag@rooter"); err == nil {
		t.Fatalf("expected an error on a bad resolve, but got none")
	} else if _, ok := err.(libkb.ResolutionError); !ok {
		t.Fatalf("Wrong error: wanted type %T but got (%v, %T)", libkb.ResolutionError{}, err, err)
	}
}

func testCheckInvitationCode(t *testing.T, g *libkb.GlobalContext) {
	cli, err := client.GetSignupClient(g)
	if err != nil {
		t.Fatalf("failed to get a signup client: %v", err)
	}

	err = cli.CheckInvitationCode(context.TODO(), keybase1.CheckInvitationCodeArg{InvitationCode: libkb.TestInvitationCode})
	if err != nil {
		t.Fatalf("Did not expect an error code, but got: %v", err)
	}
	err = cli.CheckInvitationCode(context.TODO(), keybase1.CheckInvitationCodeArg{InvitationCode: "eeoeoeoe333o3"})
	if _, ok := err.(libkb.BadInvitationCodeError); !ok {
		t.Fatalf("Expected an error code, but got %T %v", err, err)
	}
}

func testLoadAllPublicKeysUnverified(t *testing.T, g *libkb.GlobalContext) {

	cli, err := client.GetUserClient(g)
	if err != nil {
		t.Fatalf("failed to get user client: %s", err)
	}

	// t_rosetta
	arg := keybase1.LoadAllPublicKeysUnverifiedArg{Uid: keybase1.UID("b8939251cb3d367e68587acb33a64d19")}
	res, err := cli.LoadAllPublicKeysUnverified(context.TODO(), arg)
	if err != nil {
		t.Fatalf("failed to make load keys call: %s", err)
	}

	if len(res) != 3 {
		t.Fatalf("wrong amount of keys loaded: %d != %d", len(res), 3)
	}

	keys := map[keybase1.KID]bool{
		keybase1.KID("0101fe1183765f256289427d6943cd8bab3b5fe095bcdd27f031ed298da523efd3120a"): true,
		keybase1.KID("0101b5839c4ccaa9d03b3016b9aa73a7e3eafb067f9c86c07a6f2f79cb8558b1c97f0a"): true,
		keybase1.KID("0101188ee7e63ccbd05af498772ab2975ee29df773240d17dde09aecf6c132a5a9a60a"): true,
	}

	for _, key := range res {
		if _, ok := keys[key.KID]; !ok {
			t.Fatalf("unknown key in response: %s", key.KID)
		}
	}
}

func testLoadUserWithNoKeys(t *testing.T, g *libkb.GlobalContext) {
	// The LoadUser class in libkb returns an error by default if the user in
	// question has no keys. The RPC methods that wrap it should suppress this
	// error, by setting the PublicKeyOptional flag.

	cli, err := client.GetUserClient(g)
	if err != nil {
		t.Fatalf("failed to get a user client: %v", err)
	}

	// Check the LoadUserByName RPC. t_ellen is a test user with no keys.
	loadUserByNameArg := keybase1.LoadUserByNameArg{Username: "t_ellen"}
	tEllen, err := cli.LoadUserByName(context.TODO(), loadUserByNameArg)
	if err != nil {
		t.Fatal(err)
	}
	if tEllen.Username != "t_ellen" {
		t.Fatalf("expected t_ellen, saw %s", tEllen.Username)
	}

	// Check the LoadUser RPC.
	loadUserArg := keybase1.LoadUserArg{Uid: tEllen.Uid}
	tEllen2, err := cli.LoadUser(context.TODO(), loadUserArg)
	if err != nil {
		t.Fatal(err)
	}
	if tEllen2.Username != "t_ellen" {
		t.Fatalf("expected t_ellen, saw %s", tEllen2.Username)
	}
}

func testCheckDevicesForUser(t *testing.T, g *libkb.GlobalContext) {
	cli, err := client.GetDeviceClient(g)
	if err != nil {
		t.Fatalf("failed to get a device client: %v", err)
	}
	err = cli.CheckDeviceNameForUser(context.TODO(), keybase1.CheckDeviceNameForUserArg{
		Username:   "t_frank",
		Devicename: "bad $ device $ name",
	})
	if _, ok := err.(libkb.DeviceBadNameError); !ok {
		t.Fatalf("wanted a bad device name error; got %v", err)
	}
	err = cli.CheckDeviceNameForUser(context.TODO(), keybase1.CheckDeviceNameForUserArg{
		Username:   "t_frank",
		Devicename: "go c lient",
	})
	if _, ok := err.(libkb.DeviceNameInUseError); !ok {
		t.Fatalf("wanted a name in use error; got %v", err)
	}
}

func testIdentify2(t *testing.T, g *libkb.GlobalContext) {

	cli, err := client.GetIdentifyClient(g)
	if err != nil {
		t.Fatalf("failed to get new identifyclient: %v", err)
	}

	_, err = cli.Identify2(context.TODO(), keybase1.Identify2Arg{
		UserAssertion: "t_alice",
		ChatGUIMode:   true,
	})
	if err != nil {
		t.Fatalf("Identify2 failed: %v\n", err)
	}

	_, err = cli.Identify2(context.TODO(), keybase1.Identify2Arg{
		UserAssertion: "t_weriojweroi",
		ChatGUIMode:   true,
	})
	if _, ok := err.(libkb.NotFoundError); !ok {
		t.Fatalf("Expected a not-found error, but got: %v (%T)", err, err)
	}
}
<|endoftext|>


<commit_before>package shell

import (
	"fmt"
	"net/http"
	"os"
	"os/exec"
	"strings"
	"syscall"

	"github.com/googollee/go-socket.io"
	"github.com/zenoss/glog"

	"github.com/zenoss/serviced"
	"github.com/zenoss/serviced/dao"
)

var empty interface{}

const (
	PROCESSKEY string = "process"
	MAXBUFFER  int    = 8192
)

func NewProcessForwarderServer(addr string) *ProcessServer {
	server := &ProcessServer{
		sio:   socketio.NewSocketIOServer(&socketio.Config{}),
		actor: &Forwarder{addr: addr},
	}
	server.sio.On("connect", server.onConnect)
	server.sio.On("disconnect", onForwarderDisconnect)
	return server
}

func NewProcessExecutorServer(port string) *ProcessServer {
	server := &ProcessServer{
		sio:   socketio.NewSocketIOServer(&socketio.Config{}),
		actor: &Executor{port: port},
	}
	server.sio.On("connect", server.onConnect)
	server.sio.On("disconnect", onExecutorDisconnect)
	return server
}

func (p *ProcessServer) Handle(pattern string, handler http.Handler) error {
	p.sio.Handle(pattern, handler)
	return nil
}

func (p *ProcessServer) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	p.sio.ServeHTTP(w, r)
}

func (s *ProcessServer) onConnect(ns *socketio.NameSpace) {
	glog.Infof("===========================================")
	glog.Infof("===========================================")
	glog.Infof("===========================================")
	glog.Infof("Received connection")
	glog.Infof("===========================================")
	glog.Infof("===========================================")
	glog.Infof("===========================================")

	ns.On("process", func(ns *socketio.NameSpace, cfg *ProcessConfig) {
		// Kick it off
		glog.Infof("Received process packet")
		proc := s.actor.Exec(cfg)
		ns.Session.Values[PROCESSKEY] = proc

		// Wire up output
		go proc.ReadRequest(ns)
		go proc.WriteResponse(ns)
	})
	glog.Infof("Waiting for process packet")
}

func onForwarderDisconnect(ns *socketio.NameSpace) {
	// ns.Session.Values[PROCESSKEY].(*ProcessInstance)
}

func onExecutorDisconnect(ns *socketio.NameSpace) {
	inst := ns.Session.Values[PROCESSKEY].(*ProcessInstance)
	// Client disconnected, so kill the process
	inst.Signal <- int(syscall.SIGKILL)
	inst.closeIncoming()
}

func (p *ProcessInstance) Close() {
	p.closeIncoming()
	p.closeOutgoing()
}

func (p *ProcessInstance) closeIncoming() {
	glog.V(0).Infof("Closing incoming channels")
	if _, ok := <-p.Stdin; ok {
		close(p.Stdin)
	}
	if _, ok := <-p.Signal; ok {
		close(p.Signal)
	}
}

func (p *ProcessInstance) closeOutgoing() {
	glog.V(0).Infof("Closing outgoing channels")
	if _, ok := <-p.Stdout; ok {
		close(p.Stdout)
	}
	if _, ok := <-p.Stderr; ok {
		close(p.Stderr)
	}
	if _, ok := <-p.Result; ok {
		close(p.Result)
	}
}

func (p *ProcessInstance) ReadRequest(ns *socketio.NameSpace) {
	ns.On("signal", func(n *socketio.NameSpace, signal int) {
		p.Signal <- signal
	})

	ns.On("stdin", func(n *socketio.NameSpace, stdin string) {
		glog.Infof("Received stdin: %s", stdin)
		p.Stdin <- stdin
	})

	glog.V(0).Info("Hooked up incoming events!")
}

func (p *ProcessInstance) WriteRequest(ns *socketio.NameSpace) {
	glog.V(0).Info("Hooking up input channels!")
	for p.Stdin != nil || p.Signal != nil {
		select {
		case m, ok := <-p.Stdin:
			if !ok {
				glog.V(0).Infof("Setting stdin to nil")
				p.Stdin = nil
				continue
			} else {
				ns.Emit("stdin", m)
			}
		case m, ok := <-p.Signal:
			if !ok {
				p.Signal = nil
				continue
			} else {
				ns.Emit("signal", m)
			}
		}
	}
}

func (p *ProcessInstance) ReadResponse(ns *socketio.NameSpace) {
	ns.On("stdout", func(n *socketio.NameSpace, stdout string) {
		glog.Infof("Process received stdout: %s", stdout)
		p.Stdout <- stdout
	})

	ns.On("stderr", func(n *socketio.NameSpace, stderr string) {
		glog.Infof("Process received stderr: %s", stderr)
		p.Stderr <- stderr
	})

	ns.On("result", func(n *socketio.NameSpace, result Result) {
		glog.Infof("Process received stderr: %s", result)
		p.Result <- result
	})
	glog.V(0).Info("Hooked up outgoing events!")

}

func (p *ProcessInstance) WriteResponse(ns *socketio.NameSpace) {
	glog.V(0).Info("Hooking up output channels!")
	for p.Stdout != nil || p.Stderr != nil || p.Result != nil {
		select {
		case m, ok := <-p.Stdout:
			if !ok {
				p.Stdout = nil
				continue
			} else {
				glog.Infof("Emitting stdout: %s", m)
				ns.Emit("stdout", m)
			}
		case m, ok := <-p.Stderr:
			if !ok {
				p.Stderr = nil
				continue
			} else {
				glog.Infof("Emitting stderr: %s", m)
				ns.Emit("stderr", m)
			}
		case m, ok := <-p.Result:
			if !ok {
				p.Result = nil
				continue
			} else {
				glog.Infof("Emitting result: %s", m)
				ns.Emit("result", m)
			}
		}
	}
}

func (f *Forwarder) Exec(cfg *ProcessConfig) *ProcessInstance {

	// Dial the remote ProcessServer
	host := strings.Split(f.addr, ":")[0]
	client, err := socketio.Dial(fmt.Sprintf("http://%s:50000/", host))

	if err != nil {
		glog.Fatalf("Unable to contact remote process server: %v", err)
	}

	client.On("connect", func(ns *socketio.NameSpace) {
		ns.Emit("process", cfg)
	})

	ns := client.Of("")
	proc := &ProcessInstance{
		Stdin:  make(chan string),
		Stdout: make(chan string),
		Stderr: make(chan string),
		Signal: make(chan int),
		Result: make(chan Result),
	}

	client.On("disconnect", func(ns *socketio.NameSpace) {
		glog.Infof("Disconnected!")
		proc.Close()
	})

	go proc.ReadResponse(ns)
	go proc.WriteRequest(ns)

	go client.Run()

	return proc
}

func (e *Executor) Exec(cfg *ProcessConfig) *ProcessInstance {
	return StartDocker(cfg, e.port)
}

func StartDocker(cfg *ProcessConfig, port string) *ProcessInstance {
	var (
		runner   Runner
		service  *dao.Service
		services []*dao.Service
	)

	// Create a control plane client to look up the service
	cp, err := serviced.NewControlClient(port)
	if err != nil {
		glog.Fatalf("Could not create a control plane client %v", err)
	}
	glog.Infof("Connected to the control plane at port :%s", port)

	err = (*cp).GetServices(&empty, &services)
	for _, svc := range services {
		if svc.Id == cfg.ServiceId || svc.Name == cfg.ServiceId {
			service = svc
			break
		}
	}

	glog.Infof("Found service %s", cfg.ServiceId)

	// Bind mount on /serviced
	dir, bin, err := serviced.ExecPath()
	if err != nil {
		glog.Fatalf("Unable to find serviced binary: %v", err)
	}
	servicedVolume := fmt.Sprintf("%s:/serviced", dir)

	// Bind mount the pwd
	dir, err = os.Getwd()
	pwdVolume := fmt.Sprintf("%s:/mnt/pwd", dir)

	// Get the shell command
	shellcmd := cfg.Command
	if cfg.Command == "" {
		shellcmd = "su -"
	}

	// Get the serviced command
	svcdcmd := fmt.Sprintf("/serviced/%s", bin)

	// Get the proxy command
	proxycmd := []string{svcdcmd, "-logtostderr=false", "proxy", "-logstash=false", "-autorestart=false", service.Id, shellcmd}

	// Get the docker start command
	docker, err := exec.LookPath("docker")
	if err != nil {
		glog.Fatalf("Docker not found: %v", err)
	}
	argv := []string{"run", "-rm", "-v", servicedVolume, "-v", pwdVolume}
	argv = append(argv, cfg.Envv...)

	if cfg.IsTTY {
		argv = append(argv, "-i", "-t")
	}

	argv = append(argv, service.ImageId)
	argv = append(argv, proxycmd...)

	runner, err = CreateCommand(docker, argv)
	if err != nil {
		glog.Fatalf("Unable to run command: %v", err)
	}

	// Wire it up
	inst := &ProcessInstance{
		Stdout: runner.StdoutPipe(),
		Stderr: runner.StderrPipe(),
		Stdin:  make(chan string),
		Signal: make(chan int),
		Result: make(chan Result),
	}

	go func() {
		go func() {
			glog.Infof("Reading from stdin/signal channels")
			for inst.Stdin != nil || inst.Signal != nil {
				select {
				case m, ok := <-inst.Stdin:
					if ok {
						runner.Write([]byte(m))
					} else {
						inst.Stdin = nil
					}
				case s, ok := <-inst.Signal:
					if ok {
						runner.Signal(syscall.Signal(s))
					} else {
						inst.Signal = nil
					}
				}
			}
		}()

		if err := runner.Reader(MAXBUFFER); err != nil {
			inst.Result <- Result{1, err, NORMAL}
		} else {
			inst.Result <- Result{0, err, NORMAL}
		}
	}()

	return inst
}
<commit_msg>Better logging...need to fix echo on stdin for serviced shell<commit_after>package shell

import (
	"fmt"
	"net/http"
	"os"
	"os/exec"
	"strings"
	"syscall"

	"github.com/googollee/go-socket.io"
	"github.com/zenoss/glog"

	"github.com/zenoss/serviced"
	"github.com/zenoss/serviced/dao"
)

var empty interface{}

const (
	PROCESSKEY string = "process"
	MAXBUFFER  int    = 8192
)

func NewProcessForwarderServer(addr string) *ProcessServer {
	server := &ProcessServer{
		sio:   socketio.NewSocketIOServer(&socketio.Config{}),
		actor: &Forwarder{addr: addr},
	}
	server.sio.On("connect", server.onConnect)
	server.sio.On("disconnect", onForwarderDisconnect)
	return server
}

func NewProcessExecutorServer(port string) *ProcessServer {
	server := &ProcessServer{
		sio:   socketio.NewSocketIOServer(&socketio.Config{}),
		actor: &Executor{port: port},
	}
	server.sio.On("connect", server.onConnect)
	server.sio.On("disconnect", onExecutorDisconnect)
	return server
}

func (p *ProcessServer) Handle(pattern string, handler http.Handler) error {
	p.sio.Handle(pattern, handler)
	return nil
}

func (p *ProcessServer) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	p.sio.ServeHTTP(w, r)
}

func (s *ProcessServer) onConnect(ns *socketio.NameSpace) {
	glog.Infof("===========================================")
	glog.Infof("===========================================")
	glog.Infof("===========================================")
	glog.Infof("Received connection")
	glog.Infof("===========================================")
	glog.Infof("===========================================")
	glog.Infof("===========================================")

	ns.On("process", func(ns *socketio.NameSpace, cfg *ProcessConfig) {
		// Kick it off
		glog.Infof("Received process packet")
		proc := s.actor.Exec(cfg)
		ns.Session.Values[PROCESSKEY] = proc

		// Wire up output
		go proc.ReadRequest(ns)
		go proc.WriteResponse(ns)
	})
	glog.Infof("Waiting for process packet")
}

func onForwarderDisconnect(ns *socketio.NameSpace) {
	// ns.Session.Values[PROCESSKEY].(*ProcessInstance)
}

func onExecutorDisconnect(ns *socketio.NameSpace) {
	inst := ns.Session.Values[PROCESSKEY].(*ProcessInstance)
	// Client disconnected, so kill the process
	inst.Signal <- int(syscall.SIGKILL)
	inst.closeIncoming()
}

func (p *ProcessInstance) Close() {
	p.closeIncoming()
	p.closeOutgoing()
}

func (p *ProcessInstance) closeIncoming() {
	glog.V(0).Infof("Closing incoming channels")
	if _, ok := <-p.Stdin; ok {
		close(p.Stdin)
	}
	if _, ok := <-p.Signal; ok {
		close(p.Signal)
	}
}

func (p *ProcessInstance) closeOutgoing() {
	glog.V(0).Infof("Closing outgoing channels")
	if _, ok := <-p.Stdout; ok {
		close(p.Stdout)
	}
	if _, ok := <-p.Stderr; ok {
		close(p.Stderr)
	}
	if _, ok := <-p.Result; ok {
		close(p.Result)
	}
}

func (p *ProcessInstance) ReadRequest(ns *socketio.NameSpace) {
	ns.On("signal", func(n *socketio.NameSpace, signal int) {
		p.Signal <- signal
	})

	ns.On("stdin", func(n *socketio.NameSpace, stdin string) {
		glog.Infof("Received stdin: %s", stdin)
		p.Stdin <- stdin
	})

	glog.V(0).Info("Hooked up incoming events!")
}

func (p *ProcessInstance) WriteRequest(ns *socketio.NameSpace) {
	glog.V(0).Info("Hooking up input channels!")
	for p.Stdin != nil || p.Signal != nil {
		select {
		case m, ok := <-p.Stdin:
			if !ok {
				glog.V(0).Infof("Setting stdin to nil")
				p.Stdin = nil
				continue
			} else {
				ns.Emit("stdin", m)
			}
		case m, ok := <-p.Signal:
			if !ok {
				p.Signal = nil
				continue
			} else {
				ns.Emit("signal", m)
			}
		}
	}
}

func (p *ProcessInstance) ReadResponse(ns *socketio.NameSpace) {
	ns.On("stdout", func(n *socketio.NameSpace, stdout string) {
		glog.Infof("Process received stdout: %s", stdout)
		p.Stdout <- stdout
	})

	ns.On("stderr", func(n *socketio.NameSpace, stderr string) {
		glog.Infof("Process received stderr: %s", stderr)
		p.Stderr <- stderr
	})

	ns.On("result", func(n *socketio.NameSpace, result Result) {
		glog.Infof("Process received stderr: %s", result)
		p.Result <- result
	})
	glog.V(0).Info("Hooked up outgoing events!")

}

func (p *ProcessInstance) WriteResponse(ns *socketio.NameSpace) {
	glog.V(0).Info("Hooking up output channels!")
	for p.Stdout != nil || p.Stderr != nil || p.Result != nil {
		select {
		case m, ok := <-p.Stdout:
			if !ok {
				p.Stdout = nil
				continue
			} else {
				glog.Infof("Emitting stdout: %s", m)
				ns.Emit("stdout", m)
			}
		case m, ok := <-p.Stderr:
			if !ok {
				p.Stderr = nil
				continue
			} else {
				glog.Infof("Emitting stderr: %s", m)
				ns.Emit("stderr", m)
			}
		case m, ok := <-p.Result:
			if !ok {
				p.Result = nil
				continue
			} else {
				glog.Infof("Emitting result: %s", m)
				ns.Emit("result", m)
			}
		}
	}
}

func (f *Forwarder) Exec(cfg *ProcessConfig) *ProcessInstance {

	// Dial the remote ProcessServer
	host := strings.Split(f.addr, ":")[0]
	client, err := socketio.Dial(fmt.Sprintf("http://%s:50000/", host))

	if err != nil {
		glog.Fatalf("Unable to contact remote process server: %v", err)
	}

	client.On("connect", func(ns *socketio.NameSpace) {
		ns.Emit("process", cfg)
	})

	ns := client.Of("")
	proc := &ProcessInstance{
		Stdin:  make(chan string),
		Stdout: make(chan string),
		Stderr: make(chan string),
		Signal: make(chan int),
		Result: make(chan Result),
	}

	client.On("disconnect", func(ns *socketio.NameSpace) {
		glog.Infof("Disconnected!")
		proc.Close()
	})

	go proc.ReadResponse(ns)
	go proc.WriteRequest(ns)

	go client.Run()

	return proc
}

func (e *Executor) Exec(cfg *ProcessConfig) *ProcessInstance {
	return StartDocker(cfg, e.port)
}

func StartDocker(cfg *ProcessConfig, port string) *ProcessInstance {
	var (
		runner   Runner
		service  *dao.Service
		services []*dao.Service
	)

	// Create a control plane client to look up the service
	cp, err := serviced.NewControlClient(port)
	if err != nil {
		glog.Fatalf("Could not create a control plane client %v", err)
	}
	glog.Infof("Connected to the control plane at %s", port)

	err = (*cp).GetServices(&empty, &services)
	for _, svc := range services {
		if svc.Id == cfg.ServiceId || svc.Name == cfg.ServiceId {
			service = svc
			break
		}
	}

	if service == nil {
		glog.Fatalf("Unable to find service %s", cfg.ServiceId)
	}
	glog.Infof("Found service %s", cfg.ServiceId)

	// Bind mount on /serviced
	dir, bin, err := serviced.ExecPath()
	if err != nil {
		glog.Fatalf("Unable to find serviced binary: %v", err)
	}
	servicedVolume := fmt.Sprintf("%s:/serviced", dir)

	// Bind mount the pwd
	dir, err = os.Getwd()
	pwdVolume := fmt.Sprintf("%s:/mnt/pwd", dir)

	// Get the shell command
	shellcmd := cfg.Command
	if cfg.Command == "" {
		shellcmd = "su -"
	}

	// Get the serviced command
	svcdcmd := fmt.Sprintf("/serviced/%s", bin)

	// Get the proxy command
	proxycmd := []string{svcdcmd, "-logtostderr=false", "proxy", "-logstash=false", "-autorestart=false", service.Id, shellcmd}

	// Get the docker start command
	docker, err := exec.LookPath("docker")
	if err != nil {
		glog.Fatalf("Docker not found: %v", err)
	}
	argv := []string{"run", "-rm", "-v", servicedVolume, "-v", pwdVolume}
	argv = append(argv, cfg.Envv...)

	if cfg.IsTTY {
		argv = append(argv, "-i", "-t")
	}

	argv = append(argv, service.ImageId)
	argv = append(argv, proxycmd...)

	glog.Infof("%s %s", docker, argv)
	runner, err = CreateCommand(docker, argv)
	if err != nil {
		glog.Fatalf("Unable to run command {%s %s} %v", docker, argv, err)
	}

	// Wire it up
	inst := &ProcessInstance{
		Stdout: runner.StdoutPipe(),
		Stderr: runner.StderrPipe(),
		Stdin:  make(chan string),
		Signal: make(chan int),
		Result: make(chan Result),
	}

	go func() {
		go func() {
			glog.Infof("Reading from stdin/signal channels")
			for inst.Stdin != nil || inst.Signal != nil {
				select {
				case m, ok := <-inst.Stdin:
					if ok {
						runner.Write([]byte(m))
					} else {
						inst.Stdin = nil
					}
				case s, ok := <-inst.Signal:
					if ok {
						runner.Signal(syscall.Signal(s))
					} else {
						inst.Signal = nil
					}
				}
			}
		}()

		if err := runner.Reader(MAXBUFFER); err != nil {
			inst.Result <- Result{1, err, NORMAL}
		} else {
			inst.Result <- Result{0, err, NORMAL}
		}
	}()

	return inst
}
<|endoftext|>


<commit_before>package thrift

// Copyright (c) 2015 Uber Technologies, Inc.

// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

import (
	"io"
	"io/ioutil"
	"log"
	"strings"

	"github.com/apache/thrift/lib/go/thrift"
	tchannel "github.com/uber/tchannel/golang"
	"golang.org/x/net/context"
)

// Server handles incoming TChannel calls and forwards them to the matching TChanServer.
type Server struct {
	*tchannel.Channel

	handlers map[string]TChanServer
}

// NewServer returns a server that can serve thrift services over TChannel.
func NewServer(tchan *tchannel.Channel) *Server {
	return &Server{
		Channel:  tchan,
		handlers: make(map[string]TChanServer),
	}
}

// Register registers the given TChanServer to be called on any incoming call for its' services.
// TODO(prashant): Replace Register call with this call.
func (s *Server) Register(svr TChanServer) {
	service := svr.Service()
	s.handlers[service] = svr
	for _, m := range svr.Methods() {
		s.Channel.Register(s, service+"::"+m)
	}
}

func (s *Server) onError(err error) {
	// TODO(prashant): Expose incoming call errors through options for NewServer.
	log.Fatalf("Server hit error on incoming call: %v", err)
}

func (s *Server) handle(ctx context.Context, handler TChanServer, method string, call *tchannel.InboundCall) error {
	reader, err := call.Arg2Reader()
	if err != nil {
		return err
	}

	// TODO(prashant): Read application headers.
	io.Copy(ioutil.Discard, reader)
	if err := reader.Close(); err != nil {
		return err
	}

	reader, err = call.Arg3Reader()
	if err != nil {
		return err
	}

	protocol := thrift.NewTBinaryProtocolTransport(&readWriterTransport{Reader: reader})
	success, resp, err := handler.Handle(ctx, method, protocol)
	if err != nil {
		reader.Close()
		call.Response().SendSystemError(err)
		return nil
	}
	if err := reader.Close(); err != nil {
		return err
	}

	if !success {
		call.Response().SetApplicationError()
	}
	// TODO(prashant): Write application headers.
	if err := tchannel.NewArgWriter(call.Response().Arg2Writer()).Write([]byte{0, 0}); err != nil {
		return err
	}

	writer, err := call.Response().Arg3Writer()
	protocol = thrift.NewTBinaryProtocolTransport(&readWriterTransport{Writer: writer})
	resp.Write(protocol)
	if err := writer.Close(); err != nil {
		return err
	}

	return nil
}

// Handle handles an incoming TChannel call and forwards it to the correct handler.
func (s *Server) Handle(ctx context.Context, call *tchannel.InboundCall) {
	parts := strings.Split(string(call.Operation()), "::")
	if len(parts) != 2 {
		log.Fatalf("Handle got call for %v which does not match the expected call format", parts)
	}

	service, method := parts[0], parts[1]
	handler, ok := s.handlers[service]
	if !ok {
		log.Fatalf("Handle got call for service %v which is not registered", service)
	}

	if err := s.handle(ctx, handler, method, call); err != nil {
		s.onError(err)
	}
}
<commit_msg>Use sync.RWMutex when accessing Server.handlers<commit_after>package thrift

// Copyright (c) 2015 Uber Technologies, Inc.

// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

import (
	"io"
	"io/ioutil"
	"log"
	"strings"
	"sync"

	"github.com/apache/thrift/lib/go/thrift"
	tchannel "github.com/uber/tchannel/golang"
	"golang.org/x/net/context"
)

// Server handles incoming TChannel calls and forwards them to the matching TChanServer.
type Server struct {
	*tchannel.Channel

	mut      sync.RWMutex
	handlers map[string]TChanServer
}

// NewServer returns a server that can serve thrift services over TChannel.
func NewServer(tchan *tchannel.Channel) *Server {
	return &Server{
		Channel:  tchan,
		handlers: make(map[string]TChanServer),
	}
}

// Register registers the given TChanServer to be called on any incoming call for its' services.
// TODO(prashant): Replace Register call with this call.
func (s *Server) Register(svr TChanServer) {
	service := svr.Service()

	s.mut.Lock()
	s.handlers[service] = svr
	s.mut.Unlock()

	for _, m := range svr.Methods() {
		s.Channel.Register(s, service+"::"+m)
	}
}

func (s *Server) onError(err error) {
	// TODO(prashant): Expose incoming call errors through options for NewServer.
	log.Fatalf("Server hit error on incoming call: %v", err)
}

func (s *Server) handle(ctx context.Context, handler TChanServer, method string, call *tchannel.InboundCall) error {
	reader, err := call.Arg2Reader()
	if err != nil {
		return err
	}

	// TODO(prashant): Read application headers.
	io.Copy(ioutil.Discard, reader)
	if err := reader.Close(); err != nil {
		return err
	}

	reader, err = call.Arg3Reader()
	if err != nil {
		return err
	}

	protocol := thrift.NewTBinaryProtocolTransport(&readWriterTransport{Reader: reader})
	success, resp, err := handler.Handle(ctx, method, protocol)
	if err != nil {
		reader.Close()
		call.Response().SendSystemError(err)
		return nil
	}
	if err := reader.Close(); err != nil {
		return err
	}

	if !success {
		call.Response().SetApplicationError()
	}
	// TODO(prashant): Write application headers.
	if err := tchannel.NewArgWriter(call.Response().Arg2Writer()).Write([]byte{0, 0}); err != nil {
		return err
	}

	writer, err := call.Response().Arg3Writer()
	protocol = thrift.NewTBinaryProtocolTransport(&readWriterTransport{Writer: writer})
	resp.Write(protocol)
	if err := writer.Close(); err != nil {
		return err
	}

	return nil
}

// Handle handles an incoming TChannel call and forwards it to the correct handler.
func (s *Server) Handle(ctx context.Context, call *tchannel.InboundCall) {
	parts := strings.Split(string(call.Operation()), "::")
	if len(parts) != 2 {
		log.Fatalf("Handle got call for %v which does not match the expected call format", parts)
	}

	service, method := parts[0], parts[1]
	s.mut.RLock()
	handler, ok := s.handlers[service]
	s.mut.RUnlock()
	if !ok {
		log.Fatalf("Handle got call for service %v which is not registered", service)
	}

	if err := s.handle(ctx, handler, method, call); err != nil {
		s.onError(err)
	}
}
<|endoftext|>


<commit_before>// Copyright 2019 The Knative Authors

// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at

//     http://www.apache.org/licenses/LICENSE-2.0

// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or im
// See the License for the specific language governing permissions and
// limitations under the License.

// +build e2e
// +build !eventing

package e2e

import (
	"fmt"
	"testing"

	"gotest.tools/assert"
	"knative.dev/client/pkg/util"
)

func TestServiceOptions(t *testing.T) {
	t.Parallel()
	test := NewE2eTest(t)
	test.Setup(t)
	defer test.Teardown(t)

	t.Run("create and validate service with concurrency options", func(t *testing.T) {
		test.serviceCreateWithOptions(t, "svc1", []string{"--concurrency-limit", "250", "--concurrency-target", "300"})
		test.validateServiceConcurrencyTarget(t, "svc1", "300")
		test.validateServiceConcurrencyLimit(t, "svc1", "250")
	})

	t.Run("update and validate service with concurrency limit", func(t *testing.T) {
		test.serviceUpdate(t, "svc1", []string{"--concurrency-limit", "300"})
		test.validateServiceConcurrencyLimit(t, "svc1", "300")
	})

	t.Run("update concurrency options with invalid values for service", func(t *testing.T) {
		command := []string{"service", "update", "svc1", "--concurrency-limit", "-1", "--concurrency-target", "0"}
		_, err := test.kn.RunWithOpts(command, runOpts{NoNamespace: false, AllowError: true})
		assert.ErrorContains(t, err, "invalid")
	})

	t.Run("returns steady concurrency options for service", func(t *testing.T) {
		test.validateServiceConcurrencyLimit(t, "svc1", "300")
		test.validateServiceConcurrencyTarget(t, "svc1", "300")
	})

	t.Run("delete service", func(t *testing.T) {
		test.serviceDelete(t, "svc1")
	})

	t.Run("create and validate service with min/max scale options ", func(t *testing.T) {
		test.serviceCreateWithOptions(t, "svc2", []string{"--min-scale", "1", "--max-scale", "3"})
		test.validateServiceMinScale(t, "svc2", "1")
		test.validateServiceMaxScale(t, "svc2", "3")
	})

	t.Run("update and validate service with max scale option", func(t *testing.T) {
		test.serviceUpdate(t, "svc2", []string{"--max-scale", "2"})
		test.validateServiceMaxScale(t, "svc2", "2")
	})

	t.Run("delete service", func(t *testing.T) {
		test.serviceDelete(t, "svc2")
	})

	t.Run("create, update and validate service with annotations", func(t *testing.T) {
		test.serviceCreateWithOptions(t, "svc3", []string{"--annotation", "alpha=wolf", "--annotation", "brave=horse"})
		test.validateServiceAnnotations(t, "svc3", map[string]string{"alpha": "wolf", "brave": "horse"})
		test.serviceUpdate(t, "svc3", []string{"--annotation", "alpha=direwolf", "--annotation", "brave-"})
		test.validateServiceAnnotations(t, "svc3", map[string]string{"alpha": "direwolf", "brave": ""})
		test.serviceDelete(t, "svc3")
	})

	t.Run("create, update and validate service with autoscale window option", func(t *testing.T) {
		test.serviceCreateWithOptions(t, "svc4", []string{"--autoscale-window", "1m"})
		test.validateAutoscaleWindow(t, "svc4", "1m")
		test.serviceUpdate(t, "svc4", []string{"--autoscale-window", "15s"})
		test.validateAutoscaleWindow(t, "svc4", "15s")
		test.serviceDelete(t, "svc4")
	})
}

func (test *e2eTest) serviceCreateWithOptions(t *testing.T, serviceName string, options []string) {
	command := []string{"service", "create", serviceName, "--image", KnDefaultTestImage}
	command = append(command, options...)
	out, err := test.kn.RunWithOpts(command, runOpts{NoNamespace: false})
	assert.NilError(t, err)
	assert.Check(t, util.ContainsAll(out, "service", serviceName, "Creating", "namespace", test.kn.namespace, "Ready"))
}

func (test *e2eTest) validateServiceConcurrencyLimit(t *testing.T, serviceName, concurrencyLimit string) {
	jsonpath := "jsonpath={.items[0].spec.template.spec.containerConcurrency}"
	out, err := test.kn.RunWithOpts([]string{"service", "list", serviceName, "-o", jsonpath}, runOpts{})
	assert.NilError(t, err)
	if out != "" {
		assert.Equal(t, out, concurrencyLimit)
	} else {
		// case where server returns fields like spec.runLatest.configuration.revisionTemplate.spec.containerConcurrency
		// TODO: Remove this case when `runLatest` field is deprecated altogether / v1beta1
		jsonpath = "jsonpath={.items[0].spec.runLatest.configuration.revisionTemplate.spec.containerConcurrency}"
		out, err := test.kn.RunWithOpts([]string{"service", "list", serviceName, "-o", jsonpath}, runOpts{})
		assert.NilError(t, err)
		assert.Equal(t, out, concurrencyLimit)
	}
}

func (test *e2eTest) validateServiceConcurrencyTarget(t *testing.T, serviceName, concurrencyTarget string) {
	jsonpath := "jsonpath={.items[0].spec.template.metadata.annotations.autoscaling\\.knative\\.dev/target}"
	out, err := test.kn.RunWithOpts([]string{"service", "list", serviceName, "-o", jsonpath}, runOpts{})
	assert.NilError(t, err)
	if out != "" {
		assert.Equal(t, out, concurrencyTarget)
	} else {
		// case where server returns fields like  spec.runLatest.configuration.revisionTemplate.spec.containerConcurrency
		// TODO: Remove this case when `runLatest` field is deprecated altogether / v1beta1
		jsonpath = "jsonpath={.items[0].spec.runLatest.configuration.revisionTemplate.metadata.annotations.autoscaling\\.knative\\.dev/target}"
		out, err := test.kn.RunWithOpts([]string{"service", "list", serviceName, "-o", jsonpath}, runOpts{})
		assert.NilError(t, err)
		assert.Equal(t, out, concurrencyTarget)
	}
}

func (test *e2eTest) validateAutoscaleWindow(t *testing.T, serviceName, window string) {
	jsonpath := "jsonpath={.items[0].spec.template.metadata.annotations.autoscaling\\.knative\\.dev/window}"
	out, err := test.kn.RunWithOpts([]string{"service", "list", serviceName, "-o", jsonpath}, runOpts{})
	assert.NilError(t, err)
	assert.Equal(t, out, window)
}

func (test *e2eTest) validateServiceMinScale(t *testing.T, serviceName, minScale string) {
	jsonpath := "jsonpath={.items[0].spec.template.metadata.annotations.autoscaling\\.knative\\.dev/minScale}"
	out, err := test.kn.RunWithOpts([]string{"service", "list", serviceName, "-o", jsonpath}, runOpts{})
	assert.NilError(t, err)
	if out != "" {
		assert.Equal(t, minScale, out)
	} else {
		// case where server could return either old or new fields
		// #TODO: remove this when old fields are deprecated, v1beta1
		jsonpath = "jsonpath={.items[0].spec.runLatest.configuration.revisionTemplate.metadata.annotations.autoscaling\\.knative\\.dev/minScale}"
		out, err := test.kn.RunWithOpts([]string{"service", "list", serviceName, "-o", jsonpath}, runOpts{})
		assert.NilError(t, err)
		assert.Equal(t, minScale, out)
	}
}

func (test *e2eTest) validateServiceMaxScale(t *testing.T, serviceName, maxScale string) {
	jsonpath := "jsonpath={.items[0].spec.template.metadata.annotations.autoscaling\\.knative\\.dev/maxScale}"
	out, err := test.kn.RunWithOpts([]string{"service", "list", serviceName, "-o", jsonpath}, runOpts{})
	assert.NilError(t, err)
	if out != "" {
		assert.Equal(t, maxScale, out)
	} else {
		// case where server could return either old or new fields
		// #TODO: remove this when old fields are deprecated, v1beta1
		jsonpath = "jsonpath={.items[0].spec.runLatest.configuration.revisionTemplate.metadata.annotations.autoscaling\\.knative\\.dev/maxScale}"
		out, err := test.kn.RunWithOpts([]string{"service", "list", serviceName, "-o", jsonpath}, runOpts{})
		assert.NilError(t, err)
		assert.Equal(t, maxScale, out)
	}
}

func (test *e2eTest) validateServiceAnnotations(t *testing.T, serviceName string, annotations map[string]string) {
	metadataAnnotationsJsonpathFormat := "jsonpath={.metadata.annotations.%s}"
	templateAnnotationsJsonpathFormat := "jsonpath={.spec.template.metadata.annotations.%s}"
	oldTemplateAnnotationsJsonpathFormat := "jsonpath={.spec.runLatest.configuration.revisionTemplate.metadata.annotations.%s}"

	for k, v := range annotations {
		out, err := test.kn.RunWithOpts([]string{"service", "describe", serviceName, "-o", fmt.Sprintf(metadataAnnotationsJsonpathFormat, k)}, runOpts{})
		assert.NilError(t, err)
		assert.Equal(t, v, out)

		out, err = test.kn.RunWithOpts([]string{"service", "describe", serviceName, "-o", fmt.Sprintf(templateAnnotationsJsonpathFormat, k)}, runOpts{})
		assert.NilError(t, err)
		if out != "" || v == "" {
			assert.Equal(t, v, out)
		} else {
			// case where server returns fields like  spec.runLatest.configuration.revisionTemplate.metadata.annotations
			// TODO: Remove this case when `runLatest` field is deprecated altogether / v1beta1
			out, err := test.kn.RunWithOpts([]string{"service", "describe", serviceName, "-o", fmt.Sprintf(oldTemplateAnnotationsJsonpathFormat, k)}, runOpts{})
			assert.NilError(t, err)
			assert.Equal(t, v, out)
		}
	}
}
<commit_msg>Add e2e test for cmd and arg options (#637)<commit_after>// Copyright 2019 The Knative Authors

// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at

//     http://www.apache.org/licenses/LICENSE-2.0

// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or im
// See the License for the specific language governing permissions and
// limitations under the License.

// +build e2e
// +build !eventing

package e2e

import (
	"fmt"
	"testing"

	"gotest.tools/assert"
	"knative.dev/client/pkg/util"
)

func TestServiceOptions(t *testing.T) {
	t.Parallel()
	test := NewE2eTest(t)
	test.Setup(t)
	defer test.Teardown(t)

	t.Run("create and validate service with concurrency options", func(t *testing.T) {
		test.serviceCreateWithOptions(t, "svc1", []string{"--concurrency-limit", "250", "--concurrency-target", "300"})
		test.validateServiceConcurrencyTarget(t, "svc1", "300")
		test.validateServiceConcurrencyLimit(t, "svc1", "250")
	})

	t.Run("update and validate service with concurrency limit", func(t *testing.T) {
		test.serviceUpdate(t, "svc1", []string{"--concurrency-limit", "300"})
		test.validateServiceConcurrencyLimit(t, "svc1", "300")
	})

	t.Run("update concurrency options with invalid values for service", func(t *testing.T) {
		command := []string{"service", "update", "svc1", "--concurrency-limit", "-1", "--concurrency-target", "0"}
		_, err := test.kn.RunWithOpts(command, runOpts{NoNamespace: false, AllowError: true})
		assert.ErrorContains(t, err, "invalid")
	})

	t.Run("returns steady concurrency options for service", func(t *testing.T) {
		test.validateServiceConcurrencyLimit(t, "svc1", "300")
		test.validateServiceConcurrencyTarget(t, "svc1", "300")
	})

	t.Run("delete service", func(t *testing.T) {
		test.serviceDelete(t, "svc1")
	})

	t.Run("create and validate service with min/max scale options ", func(t *testing.T) {
		test.serviceCreateWithOptions(t, "svc2", []string{"--min-scale", "1", "--max-scale", "3"})
		test.validateServiceMinScale(t, "svc2", "1")
		test.validateServiceMaxScale(t, "svc2", "3")
	})

	t.Run("update and validate service with max scale option", func(t *testing.T) {
		test.serviceUpdate(t, "svc2", []string{"--max-scale", "2"})
		test.validateServiceMaxScale(t, "svc2", "2")
	})

	t.Run("delete service", func(t *testing.T) {
		test.serviceDelete(t, "svc2")
	})

	t.Run("create, update and validate service with annotations", func(t *testing.T) {
		test.serviceCreateWithOptions(t, "svc3", []string{"--annotation", "alpha=wolf", "--annotation", "brave=horse"})
		test.validateServiceAnnotations(t, "svc3", map[string]string{"alpha": "wolf", "brave": "horse"})
		test.serviceUpdate(t, "svc3", []string{"--annotation", "alpha=direwolf", "--annotation", "brave-"})
		test.validateServiceAnnotations(t, "svc3", map[string]string{"alpha": "direwolf", "brave": ""})
		test.serviceDelete(t, "svc3")
	})

	t.Run("create, update and validate service with autoscale window option", func(t *testing.T) {
		test.serviceCreateWithOptions(t, "svc4", []string{"--autoscale-window", "1m"})
		test.validateAutoscaleWindow(t, "svc4", "1m")
		test.serviceUpdate(t, "svc4", []string{"--autoscale-window", "15s"})
		test.validateAutoscaleWindow(t, "svc4", "15s")
		test.serviceDelete(t, "svc4")
	})

	t.Run("create, update and validate service with cmd and arg options", func(t *testing.T) {
		test.serviceCreateWithOptions(t, "svc5", []string{"--cmd", "/go/bin/helloworld"})
		test.validateContainerField(t, "svc5", "command", "[/go/bin/helloworld]")
		test.serviceUpdate(t, "svc5", []string{"--arg", "myArg1", "--arg", "--myArg2"})
		test.validateContainerField(t, "svc5", "args", "[myArg1 --myArg2]")
		test.serviceUpdate(t, "svc5", []string{"--arg", "myArg1"})
		test.validateContainerField(t, "svc5", "args", "[myArg1]")
	})
}

func (test *e2eTest) serviceCreateWithOptions(t *testing.T, serviceName string, options []string) {
	command := []string{"service", "create", serviceName, "--image", KnDefaultTestImage}
	command = append(command, options...)
	out, err := test.kn.RunWithOpts(command, runOpts{NoNamespace: false})
	assert.NilError(t, err)
	assert.Check(t, util.ContainsAll(out, "service", serviceName, "Creating", "namespace", test.kn.namespace, "Ready"))
}

func (test *e2eTest) validateServiceConcurrencyLimit(t *testing.T, serviceName, concurrencyLimit string) {
	jsonpath := "jsonpath={.items[0].spec.template.spec.containerConcurrency}"
	out, err := test.kn.RunWithOpts([]string{"service", "list", serviceName, "-o", jsonpath}, runOpts{})
	assert.NilError(t, err)
	if out != "" {
		assert.Equal(t, out, concurrencyLimit)
	} else {
		// case where server returns fields like spec.runLatest.configuration.revisionTemplate.spec.containerConcurrency
		// TODO: Remove this case when `runLatest` field is deprecated altogether / v1beta1
		jsonpath = "jsonpath={.items[0].spec.runLatest.configuration.revisionTemplate.spec.containerConcurrency}"
		out, err := test.kn.RunWithOpts([]string{"service", "list", serviceName, "-o", jsonpath}, runOpts{})
		assert.NilError(t, err)
		assert.Equal(t, out, concurrencyLimit)
	}
}

func (test *e2eTest) validateServiceConcurrencyTarget(t *testing.T, serviceName, concurrencyTarget string) {
	jsonpath := "jsonpath={.items[0].spec.template.metadata.annotations.autoscaling\\.knative\\.dev/target}"
	out, err := test.kn.RunWithOpts([]string{"service", "list", serviceName, "-o", jsonpath}, runOpts{})
	assert.NilError(t, err)
	if out != "" {
		assert.Equal(t, out, concurrencyTarget)
	} else {
		// case where server returns fields like  spec.runLatest.configuration.revisionTemplate.spec.containerConcurrency
		// TODO: Remove this case when `runLatest` field is deprecated altogether / v1beta1
		jsonpath = "jsonpath={.items[0].spec.runLatest.configuration.revisionTemplate.metadata.annotations.autoscaling\\.knative\\.dev/target}"
		out, err := test.kn.RunWithOpts([]string{"service", "list", serviceName, "-o", jsonpath}, runOpts{})
		assert.NilError(t, err)
		assert.Equal(t, out, concurrencyTarget)
	}
}

func (test *e2eTest) validateAutoscaleWindow(t *testing.T, serviceName, window string) {
	jsonpath := "jsonpath={.items[0].spec.template.metadata.annotations.autoscaling\\.knative\\.dev/window}"
	out, err := test.kn.RunWithOpts([]string{"service", "list", serviceName, "-o", jsonpath}, runOpts{})
	assert.NilError(t, err)
	assert.Equal(t, out, window)
}

func (test *e2eTest) validateServiceMinScale(t *testing.T, serviceName, minScale string) {
	jsonpath := "jsonpath={.items[0].spec.template.metadata.annotations.autoscaling\\.knative\\.dev/minScale}"
	out, err := test.kn.RunWithOpts([]string{"service", "list", serviceName, "-o", jsonpath}, runOpts{})
	assert.NilError(t, err)
	if out != "" {
		assert.Equal(t, minScale, out)
	} else {
		// case where server could return either old or new fields
		// #TODO: remove this when old fields are deprecated, v1beta1
		jsonpath = "jsonpath={.items[0].spec.runLatest.configuration.revisionTemplate.metadata.annotations.autoscaling\\.knative\\.dev/minScale}"
		out, err := test.kn.RunWithOpts([]string{"service", "list", serviceName, "-o", jsonpath}, runOpts{})
		assert.NilError(t, err)
		assert.Equal(t, minScale, out)
	}
}

func (test *e2eTest) validateServiceMaxScale(t *testing.T, serviceName, maxScale string) {
	jsonpath := "jsonpath={.items[0].spec.template.metadata.annotations.autoscaling\\.knative\\.dev/maxScale}"
	out, err := test.kn.RunWithOpts([]string{"service", "list", serviceName, "-o", jsonpath}, runOpts{})
	assert.NilError(t, err)
	if out != "" {
		assert.Equal(t, maxScale, out)
	} else {
		// case where server could return either old or new fields
		// #TODO: remove this when old fields are deprecated, v1beta1
		jsonpath = "jsonpath={.items[0].spec.runLatest.configuration.revisionTemplate.metadata.annotations.autoscaling\\.knative\\.dev/maxScale}"
		out, err := test.kn.RunWithOpts([]string{"service", "list", serviceName, "-o", jsonpath}, runOpts{})
		assert.NilError(t, err)
		assert.Equal(t, maxScale, out)
	}
}

func (test *e2eTest) validateServiceAnnotations(t *testing.T, serviceName string, annotations map[string]string) {
	metadataAnnotationsJsonpathFormat := "jsonpath={.metadata.annotations.%s}"
	templateAnnotationsJsonpathFormat := "jsonpath={.spec.template.metadata.annotations.%s}"
	oldTemplateAnnotationsJsonpathFormat := "jsonpath={.spec.runLatest.configuration.revisionTemplate.metadata.annotations.%s}"

	for k, v := range annotations {
		out, err := test.kn.RunWithOpts([]string{"service", "describe", serviceName, "-o", fmt.Sprintf(metadataAnnotationsJsonpathFormat, k)}, runOpts{})
		assert.NilError(t, err)
		assert.Equal(t, v, out)

		out, err = test.kn.RunWithOpts([]string{"service", "describe", serviceName, "-o", fmt.Sprintf(templateAnnotationsJsonpathFormat, k)}, runOpts{})
		assert.NilError(t, err)
		if out != "" || v == "" {
			assert.Equal(t, v, out)
		} else {
			// case where server returns fields like  spec.runLatest.configuration.revisionTemplate.metadata.annotations
			// TODO: Remove this case when `runLatest` field is deprecated altogether / v1beta1
			out, err := test.kn.RunWithOpts([]string{"service", "describe", serviceName, "-o", fmt.Sprintf(oldTemplateAnnotationsJsonpathFormat, k)}, runOpts{})
			assert.NilError(t, err)
			assert.Equal(t, v, out)
		}
	}
}

func (test *e2eTest) validateContainerField(t *testing.T, serviceName, field, expected string) {
	jsonpath := fmt.Sprintf("jsonpath={.items[0].spec.template.spec.containers[0].%s}", field)
	out, err := test.kn.RunWithOpts([]string{"service", "list", serviceName, "-o", jsonpath}, runOpts{})
	assert.NilError(t, err)
	assert.Equal(t, out, expected)
}
<|endoftext|>


<commit_before>package netscaler

import (
	"encoding/base64"
	"github.com/chiradeep/go-nitro/config/system"
	"github.com/chiradeep/go-nitro/netscaler"
	"io/ioutil"
	"path"
	"runtime"
	"strings"
	"testing"
)

func uploadTestdataFile(t *testing.T, filename, targetDir string) error {
	nsClient := testAccProvider.Meta().(*NetScalerNitroClient).client

	// Get here path
	_, here_filename, _, _ := runtime.Caller(1)
	b, err := ioutil.ReadFile(path.Join(path.Dir(here_filename), "testdata", filename))

	if err != nil {
		return err
	}

	sf := system.Systemfile{
		Filename:     filename,
		Filecontent:  base64.StdEncoding.EncodeToString(b),
		Filelocation: targetDir,
	}
	_, err = nsClient.AddResource(netscaler.Systemfile.Type(), filename, &sf)
	if err != nil && strings.Contains(err.Error(), "File already exists") {
		args := map[string]string{"filelocation": "%2Fvar%2Ftmp"}
		err := nsClient.DeleteResourceWithArgsMap(netscaler.Systemfile.Type(), filename, args)
		if err != nil {
			return err
		}
		_, err = nsClient.AddResource(netscaler.Systemfile.Type(), filename, &sf)
		if err != nil {
			return err
		}
	}
	return nil
}
<commit_msg>Fix upload file helper<commit_after>package netscaler

import (
	"encoding/base64"
	"github.com/chiradeep/go-nitro/config/system"
	"github.com/chiradeep/go-nitro/netscaler"
	"io/ioutil"
	"path"
	"runtime"
	"strings"
	"testing"
)

func uploadTestdataFile(t *testing.T, filename, targetDir string) error {
	nsClient := testAccProvider.Meta().(*NetScalerNitroClient).client

	// Get here path
	_, here_filename, _, _ := runtime.Caller(1)
	b, err := ioutil.ReadFile(path.Join(path.Dir(here_filename), "testdata", filename))

	if err != nil {
		return err
	}

	sf := system.Systemfile{
		Filename:     filename,
		Filecontent:  base64.StdEncoding.EncodeToString(b),
		Filelocation: targetDir,
	}
	_, err = nsClient.AddResource(netscaler.Systemfile.Type(), filename, &sf)
	if err != nil && strings.Contains(err.Error(), "File already exists") {
		url_args := map[string]string{"filelocation": strings.Replace(targetDir, "/", "%2F", -1)}
		err := nsClient.DeleteResourceWithArgsMap(netscaler.Systemfile.Type(), filename, url_args)
		if err != nil {
			return err
		}
		_, err = nsClient.AddResource(netscaler.Systemfile.Type(), filename, &sf)
		if err != nil {
			return err
		}
	}
	return nil
}
<|endoftext|>


<commit_before>package builds

import (
	g "github.com/onsi/ginkgo"
	o "github.com/onsi/gomega"

	corev1 "k8s.io/api/core/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/client-go/util/retry"

	buildv1 "github.com/openshift/api/build/v1"
	exutil "github.com/openshift/origin/test/extended/util"
)

func Before(oc *exutil.CLI) {
	exutil.PreTestDump()
}

func After(oc *exutil.CLI) {
	if g.CurrentGinkgoTestDescription().Failed {
		exutil.DumpPodStates(oc)
		exutil.DumpConfigMapStates(oc)
		exutil.DumpPodLogsStartingWith("", oc)
	}
}

var _ = g.Describe("[sig-builds][Feature:Builds] s2i build with a root user image", func() {
	defer g.GinkgoRecover()
	oc := exutil.NewCLI("s2i-build-root")

	g.It("should create a root build and fail without a privileged SCC", func() {
		g.Skip("TODO: figure out why we aren't properly denying this, also consider whether we still need to deny it")
		Before(oc)
		defer After(oc)

		err := oc.Run("new-app").Args("docker.io/openshift/test-build-roots2i~https://github.com/sclorg/nodejs-ex", "--name", "nodejsfail").Execute()
		o.Expect(err).NotTo(o.HaveOccurred())

		err = exutil.WaitForABuild(oc.BuildClient().BuildV1().Builds(oc.Namespace()), "nodejsfail-1", nil, nil, nil)
		o.Expect(err).To(o.HaveOccurred())

		build, err := oc.BuildClient().BuildV1().Builds(oc.Namespace()).Get("nodejsfail-1", metav1.GetOptions{})
		o.Expect(err).NotTo(o.HaveOccurred())
		o.Expect(build.Status.Phase).To(o.Equal(buildv1.BuildPhaseFailed))
		o.Expect(build.Status.Reason).To(o.BeEquivalentTo("PullBuilderImageFailed" /*s2istatus.ReasonPullBuilderImageFailed*/))
		o.Expect(build.Status.Message).To(o.BeEquivalentTo("Failed to pull builder image." /*s2istatus.ReasonMessagePullBuilderImageFailed*/))

		podname := build.Annotations[buildv1.BuildPodNameAnnotation]
		pod, err := oc.KubeClient().CoreV1().Pods(oc.Namespace()).Get(podname, metav1.GetOptions{})
		o.Expect(err).NotTo(o.HaveOccurred())

		containers := make([]corev1.Container, len(pod.Spec.Containers)+len(pod.Spec.InitContainers))
		copy(containers, pod.Spec.Containers)
		copy(containers[len(pod.Spec.Containers):], pod.Spec.InitContainers)

		for _, c := range containers {
			env := map[string]string{}
			for _, e := range c.Env {
				env[e.Name] = e.Value
			}
			o.Expect(env["DROP_CAPS"]).To(o.Equal("KILL,MKNOD,SETGID,SETUID"))
			o.Expect(env["ALLOWED_UIDS"]).To(o.Equal("1-"))
		}
	})

	g.It("should create a root build and pass with a privileged SCC", func() {
		Before(oc)
		defer After(oc)
		g.By("adding builder account to privileged SCC")
		err := retry.RetryOnConflict(retry.DefaultRetry, func() error {
			scc, err := oc.AdminSecurityClient().SecurityV1().SecurityContextConstraints().Get("privileged", metav1.GetOptions{})
			o.Expect(err).NotTo(o.HaveOccurred())

			scc.Users = append(scc.Users, "system:serviceaccount:"+oc.Namespace()+":builder")
			_, err = oc.AdminSecurityClient().SecurityV1().SecurityContextConstraints().Update(scc)
			return err
		})
		o.Expect(err).NotTo(o.HaveOccurred())

		err = oc.Run("new-build").Args("docker.io/openshift/test-build-roots2i~https://github.com/sclorg/nodejs-ex", "--name", "nodejspass").Execute()
		o.Expect(err).NotTo(o.HaveOccurred())

		err = exutil.WaitForABuild(oc.BuildClient().BuildV1().Builds(oc.Namespace()), "nodejspass-1", nil, nil, nil)
		o.Expect(err).NotTo(o.HaveOccurred())

		build, err := oc.BuildClient().BuildV1().Builds(oc.Namespace()).Get("nodejspass-1", metav1.GetOptions{})
		o.Expect(err).NotTo(o.HaveOccurred())

		podname := build.Annotations[buildv1.BuildPodNameAnnotation]
		pod, err := oc.KubeClient().CoreV1().Pods(oc.Namespace()).Get(podname, metav1.GetOptions{})
		o.Expect(err).NotTo(o.HaveOccurred())

		containers := make([]corev1.Container, len(pod.Spec.Containers)+len(pod.Spec.InitContainers))
		copy(containers, pod.Spec.Containers)
		copy(containers[len(pod.Spec.Containers):], pod.Spec.InitContainers)

		for _, c := range containers {
			env := map[string]string{}
			for _, e := range c.Env {
				env[e.Name] = e.Value
			}
			o.Expect(env).NotTo(o.HaveKey("DROP_CAPS"))
			o.Expect(env).NotTo(o.HaveKey("ALLOWED_UIDS"))
		}
	})
})
<commit_msg>use rbac vs direct scc edit for s2i root bld test<commit_after>package builds

import (
	g "github.com/onsi/ginkgo"
	o "github.com/onsi/gomega"

	corev1 "k8s.io/api/core/v1"
	rbacv1 "k8s.io/api/rbac/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"

	buildv1 "github.com/openshift/api/build/v1"
	exutil "github.com/openshift/origin/test/extended/util"
)

func Before(oc *exutil.CLI) {
	exutil.PreTestDump()
}

func After(oc *exutil.CLI) {
	if g.CurrentGinkgoTestDescription().Failed {
		exutil.DumpPodStates(oc)
		exutil.DumpConfigMapStates(oc)
		exutil.DumpPodLogsStartingWith("", oc)
	}
}

var _ = g.Describe("[sig-builds][Feature:Builds] s2i build with a root user image", func() {
	defer g.GinkgoRecover()
	oc := exutil.NewCLI("s2i-build-root")

	g.It("should create a root build and fail without a privileged SCC", func() {
		g.Skip("TODO: figure out why we aren't properly denying this, also consider whether we still need to deny it")
		Before(oc)
		defer After(oc)

		err := oc.Run("new-app").Args("docker.io/openshift/test-build-roots2i~https://github.com/sclorg/nodejs-ex", "--name", "nodejsfail").Execute()
		o.Expect(err).NotTo(o.HaveOccurred())

		err = exutil.WaitForABuild(oc.BuildClient().BuildV1().Builds(oc.Namespace()), "nodejsfail-1", nil, nil, nil)
		o.Expect(err).To(o.HaveOccurred())

		build, err := oc.BuildClient().BuildV1().Builds(oc.Namespace()).Get("nodejsfail-1", metav1.GetOptions{})
		o.Expect(err).NotTo(o.HaveOccurred())
		o.Expect(build.Status.Phase).To(o.Equal(buildv1.BuildPhaseFailed))
		o.Expect(build.Status.Reason).To(o.BeEquivalentTo("PullBuilderImageFailed" /*s2istatus.ReasonPullBuilderImageFailed*/))
		o.Expect(build.Status.Message).To(o.BeEquivalentTo("Failed to pull builder image." /*s2istatus.ReasonMessagePullBuilderImageFailed*/))

		podname := build.Annotations[buildv1.BuildPodNameAnnotation]
		pod, err := oc.KubeClient().CoreV1().Pods(oc.Namespace()).Get(podname, metav1.GetOptions{})
		o.Expect(err).NotTo(o.HaveOccurred())

		containers := make([]corev1.Container, len(pod.Spec.Containers)+len(pod.Spec.InitContainers))
		copy(containers, pod.Spec.Containers)
		copy(containers[len(pod.Spec.Containers):], pod.Spec.InitContainers)

		for _, c := range containers {
			env := map[string]string{}
			for _, e := range c.Env {
				env[e.Name] = e.Value
			}
			o.Expect(env["DROP_CAPS"]).To(o.Equal("KILL,MKNOD,SETGID,SETUID"))
			o.Expect(env["ALLOWED_UIDS"]).To(o.Equal("1-"))
		}
	})

	g.It("should create a root build and pass with a privileged SCC", func() {
		Before(oc)
		defer After(oc)
		g.By("adding builder account to privileged SCC")
		// create a namespace local role and role binding to the privileged SCC;
		// role and role binding will be deleted as part of test namespace clean up
		role := &rbacv1.Role{}
		role.Namespace = oc.Namespace()
		role.Name = "privileged-builder-role"
		role.Rules = []rbacv1.PolicyRule{
			{
				APIGroups: []string{
					"security.openshift.io",
				},
				ResourceNames: []string{
					"privileged",
				},
				Resources: []string{
					"securitycontextconstraints",
				},
				Verbs: []string{
					"use",
				},
			},
		}
		role, err := oc.AdminKubeClient().RbacV1().Roles(oc.Namespace()).Create(role)
		o.Expect(err).NotTo(o.HaveOccurred())
		roleBinding := &rbacv1.RoleBinding{}
		roleBinding.Namespace = oc.Namespace()
		roleBinding.Name = "privileged-builder-rolebinding"
		roleBinding.RoleRef = rbacv1.RoleRef{
			APIGroup: "rbac.authorization.k8s.io",
			Kind:     "Role",
			Name:     "privileged-builder-role",
		}
		roleBinding.Subjects = []rbacv1.Subject{
			{
				Kind:      "ServiceAccount",
				APIGroup:  "",
				Name:      "builder",
				Namespace: oc.Namespace(),
			},
		}
		roleBinding, err = oc.AdminKubeClient().RbacV1().RoleBindings(oc.Namespace()).Create(roleBinding)
		o.Expect(err).NotTo(o.HaveOccurred())

		err = oc.Run("new-build").Args("docker.io/openshift/test-build-roots2i~https://github.com/sclorg/nodejs-ex", "--name", "nodejspass").Execute()
		o.Expect(err).NotTo(o.HaveOccurred())

		err = exutil.WaitForABuild(oc.BuildClient().BuildV1().Builds(oc.Namespace()), "nodejspass-1", nil, nil, nil)
		o.Expect(err).NotTo(o.HaveOccurred())

		build, err := oc.BuildClient().BuildV1().Builds(oc.Namespace()).Get("nodejspass-1", metav1.GetOptions{})
		o.Expect(err).NotTo(o.HaveOccurred())

		podname := build.Annotations[buildv1.BuildPodNameAnnotation]
		pod, err := oc.KubeClient().CoreV1().Pods(oc.Namespace()).Get(podname, metav1.GetOptions{})
		o.Expect(err).NotTo(o.HaveOccurred())

		containers := make([]corev1.Container, len(pod.Spec.Containers)+len(pod.Spec.InitContainers))
		copy(containers, pod.Spec.Containers)
		copy(containers[len(pod.Spec.Containers):], pod.Spec.InitContainers)

		for _, c := range containers {
			env := map[string]string{}
			for _, e := range c.Env {
				env[e.Name] = e.Value
			}
			o.Expect(env).NotTo(o.HaveKey("DROP_CAPS"))
			o.Expect(env).NotTo(o.HaveKey("ALLOWED_UIDS"))
		}
	})
})
<|endoftext|>


<commit_before>// Copyright (C) 2017 ecofast(胡光耀). All rights reserved.
// Use of this source code is governed by a BSD-style license.

// Package tcpsock provides easy to use interfaces for TCP I/O.
// Thanks to darksword(gansidui) and AlexStocks for their valuable projects
// which are gotcp(https://github.com/gansidui/gotcp)
// and getty(https://github.com/AlexStocks/getty).
package tcpsock

const (
	RecvBufLenMax = 4 * 1024
	SendBufLenMax = 4 * 1024

	cSendBufCap = 10
	cRecvBufCap = 10
)

type tcpSock struct {
	sendBufCap uint32
	recvBufCap uint32
	proto      Protocol
	exitChan   chan struct{}
}

type Protocol interface {
	Parse(b []byte, recvChan chan<- Packet)
	Process(conn *TcpConn, p Packet)
}

type Packet interface {
	Marshal() []byte
}
<commit_msg>modify tcpSock to support the implementation of TcpClient<commit_after>// Copyright (C) 2017 ecofast(胡光耀). All rights reserved.
// Use of this source code is governed by a BSD-style license.

// Package tcpsock provides easy to use interfaces for TCP I/O.
// Thanks to darksword(gansidui) and AlexStocks for their valuable projects
// which are gotcp(https://github.com/gansidui/gotcp)
// and getty(https://github.com/AlexStocks/getty).
package tcpsock

import (
	"sync"
)

const (
	RecvBufLenMax = 4 * 1024
	SendBufLenMax = 4 * 1024

	SendBufCapMax = 10
	RecvBufCapMax = 10
)

type tcpSock struct {
	sendBufCap    uint32
	recvBufCap    uint32
	proto         Protocol
	exitChan      chan struct{}
	waitGroup     *sync.WaitGroup
	onConnConnect OnTcpConnCallback
	onConnClose   OnTcpConnCallback
}

type Protocol interface {
	Parse(b []byte, recvChan chan<- Packet)
	Process(conn *TcpConn, p Packet)
}

type Packet interface {
	Marshal() []byte
}
<|endoftext|>


<commit_before>package immortal

import (
	"bytes"
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"os/signal"
	"path/filepath"
	"strings"
	"sync/atomic"
	"syscall"
	"testing"
	"time"
)

func TestHelperProcessSignalsFiFo(*testing.T) {
	if os.Getenv("GO_WANT_HELPER_PROCESS") != "1" {
		return
	}
	tmpfile := os.Getenv("TEST_SIGNALS_FILE")
	c := make(chan os.Signal, 1)
	signal.Notify(c,
		syscall.SIGALRM,
		syscall.SIGCONT,
		syscall.SIGHUP,
		syscall.SIGINT,
		syscall.SIGQUIT,
		syscall.SIGTTIN,
		syscall.SIGTTOU,
		syscall.SIGUSR1,
		syscall.SIGUSR2,
		syscall.SIGWINCH,
	)
	for {
		signalType := <-c
		switch signalType {
		case syscall.SIGALRM:
			ioutil.WriteFile(tmpfile, []byte("--a"), 0644)
		case syscall.SIGCONT:
			ioutil.WriteFile(tmpfile, []byte("--c"), 0644)
		case syscall.SIGHUP:
			ioutil.WriteFile(tmpfile, []byte("--h"), 0644)
		case syscall.SIGINT:
			ioutil.WriteFile(tmpfile, []byte("--i"), 0644)
		case syscall.SIGQUIT:
			ioutil.WriteFile(tmpfile, []byte("--q"), 0644)
		case syscall.SIGTTIN:
			ioutil.WriteFile(tmpfile, []byte("--in"), 0644)
		case syscall.SIGTTOU:
			ioutil.WriteFile(tmpfile, []byte("--ou"), 0644)
		case syscall.SIGUSR1:
			ioutil.WriteFile(tmpfile, []byte("--1"), 0644)
		case syscall.SIGUSR2:
			ioutil.WriteFile(tmpfile, []byte("--2"), 0644)
		case syscall.SIGWINCH:
			ioutil.WriteFile(tmpfile, []byte("--w"), 0644)
		}
	}
}

func TestSignalsFiFo(t *testing.T) {
	os.RemoveAll("supervise")
	var mylog bytes.Buffer
	log.SetOutput(&mylog)
	log.SetFlags(0)
	base := filepath.Base(os.Args[0]) // "exec.test"
	dir := filepath.Dir(os.Args[0])   // "/tmp/go-buildNNNN/os/exec/_test"
	if dir == "." {
		t.Skip("skipping; running test at root somehow")
	}
	parentDir := filepath.Dir(dir) // "/tmp/go-buildNNNN/os/exec"
	dirBase := filepath.Base(dir)  // "_test"
	if dirBase == "." {
		t.Skipf("skipping; unexpected shallow dir of %q", dir)
	}
	// for writing the signals
	tmpfile, err := ioutil.TempFile("", "signals")
	if err != nil {
		t.Fatal(err)
	}
	defer os.Remove(tmpfile.Name()) // clean up

	cfg := &Config{
		Env:     map[string]string{"GO_WANT_HELPER_PROCESS": "1", "TEST_SIGNALS_FILE": tmpfile.Name()},
		command: []string{filepath.Join(dirBase, base), "-test.run=TestHelperProcessSignalsFiFo", "--"},
		Cwd:     parentDir,
		Pid: Pid{
			Parent: filepath.Join(parentDir, "parent.pid"),
			Child:  filepath.Join(parentDir, "child.pid"),
		},
		ctl: true,
	}
	d, err := New(cfg)
	if err != nil {
		t.Error(err)
	}

	p, err := d.Run(NewProcess(cfg))
	if err != nil {
		t.Error(err)
	}

	// create socket
	if err := d.Listen(); err != nil {
		t.Fatal(err)
	}

	// check pids
	if pid, err := d.ReadPidFile(filepath.Join(parentDir, "parent.pid")); err != nil {
		t.Error(err)
	} else {
		expect(t, os.Getpid(), pid)
	}
	if pid, err := d.ReadPidFile(filepath.Join(parentDir, "child.pid")); err != nil {
		t.Error(err)
	} else {
		expect(t, p.Pid(), pid)
	}

	// sync
	time.Sleep(time.Second)

	var testSignals = []struct {
		signal   string
		expected string
	}{
		{"a", "--a"},
		{"alrm", "--a"},
		{"c", "--c"},
		{"cont", "--c"},
		{"h", "--h"},
		{"hup", "--h"},
		{"i", "--i"},
		{"int", "--i"},
		{"q", "--q"},
		{"quit", "--q"},
		{"in", "--in"},
		{"TTIN", "--in"},
		{"ou", "--ou"},
		{"TTOU", "--ou"},
		{"1", "--1"},
		{"usr1", "--1"},
		{"2", "--2"},
		{"usr2", "--2"},
		{"w", "--w"},
		{"winch", "--w"},
	}

	status := &Status{}
	for _, s := range testSignals {
		if err := getJSON(fmt.Sprintf("/signal/%s", s.signal), status); err != nil {
			t.Fatal(err)
		}
		// need to improve this (fifo maybe)
		time.Sleep(time.Millisecond)
		data, err := ioutil.ReadFile(tmpfile.Name())
		if err != nil {
			t.Fatal(err)
		}
		expect(t, s.expected, string(data))
	}

	// test "d", (keep it down and don't restart)
	if err := getJSON("/signal/d", status); err != nil {
		t.Fatal(err)
	}
	// wait for process to finish
	err = <-p.errch
	atomic.StoreUint32(&d.lock, d.lockOnce)
	expect(t, "signal: terminated", err.Error())

	// create error os: process already finished
	mylog.Reset()
	for _, s := range testSignals {
		if err := getJSON(fmt.Sprintf("/signal/%s", s.signal), status); err != nil {
			t.Fatal(err)
		}
		expect(t, true, strings.HasSuffix(strings.TrimSpace(mylog.String()), "os: process already finished"))
		mylog.Reset()
	}

	if err := getJSON("/signal/d", status); err != nil {
		t.Fatal(err)
	}
	expect(t, true, strings.HasSuffix(strings.TrimSpace(mylog.String()), "os: process already finished"))

	if err := getJSON("/signal/t", status); err != nil {
		t.Fatal(err)
	}
	expect(t, true, strings.HasSuffix(strings.TrimSpace(mylog.String()), "os: process already finished"))

	if err := getJSON("/signal/p", status); err != nil {
		t.Fatal(err)
	}
	expect(t, true, strings.HasSuffix(strings.TrimSpace(mylog.String()), "os: process already finished"))

	if err := getJSON("/signal/x", status); err != nil {
		t.Fatal(err)
	}
}
<commit_msg>modified:   signals_test.go<commit_after>package immortal

import (
	"bufio"
	"bytes"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"os"
	"os/signal"
	"path/filepath"
	"strings"
	"sync/atomic"
	"syscall"
	"testing"
	"time"
)

func TestHelperProcessSignalsFiFo(*testing.T) {
	if os.Getenv("GO_WANT_HELPER_PROCESS") != "1" {
		return
	}
	tmpdir := os.Getenv("TEST_TEMP_DIR")
	c := make(chan os.Signal, 1)
	signal.Notify(c,
		syscall.SIGALRM,
		syscall.SIGCONT,
		syscall.SIGHUP,
		syscall.SIGINT,
		syscall.SIGQUIT,
		syscall.SIGTTIN,
		syscall.SIGTTOU,
		syscall.SIGUSR1,
		syscall.SIGUSR2,
		syscall.SIGWINCH,
	)
	fifo, err := OpenFifo(filepath.Join(tmpdir, "fifo"))
	if err != nil {
		panic(err)
	}
	defer fifo.Close()
	for {
		signalType := <-c
		switch signalType {
		case syscall.SIGALRM:
			fmt.Fprintln(fifo, "--a")
		case syscall.SIGCONT:
			fmt.Fprintln(fifo, "--c")
		case syscall.SIGHUP:
			fmt.Fprintln(fifo, "--h")
		case syscall.SIGINT:
			fmt.Fprintln(fifo, "--i")
		case syscall.SIGQUIT:
			fmt.Fprintln(fifo, "--q")
		case syscall.SIGTTIN:
			fmt.Fprintln(fifo, "--in")
		case syscall.SIGTTOU:
			fmt.Fprintln(fifo, "--ou")
		case syscall.SIGUSR1:
			fmt.Fprintln(fifo, "--1")
		case syscall.SIGUSR2:
			fmt.Fprintln(fifo, "--2")
		case syscall.SIGWINCH:
			fmt.Fprintln(fifo, "--w")
		}
	}
}

func TestSignalsFiFo(t *testing.T) {
	os.RemoveAll("supervise")
	var mylog bytes.Buffer
	log.SetOutput(&mylog)
	log.SetFlags(0)
	base := filepath.Base(os.Args[0]) // "exec.test"
	dir := filepath.Dir(os.Args[0])   // "/tmp/go-buildNNNN/os/exec/_test"
	if dir == "." {
		t.Skip("skipping; running test at root somehow")
	}
	parentDir := filepath.Dir(dir) // "/tmp/go-buildNNNN/os/exec"
	dirBase := filepath.Base(dir)  // "_test"
	if dirBase == "." {
		t.Skipf("skipping; unexpected shallow dir of %q", dir)
	}
	// for writing the signals
	tmpdir, err := ioutil.TempDir("", "signals")
	if err != nil {
		t.Fatal(err)
	}
	defer os.Remove(tmpdir) // clean up

	cfg := &Config{
		Env:     map[string]string{"GO_WANT_HELPER_PROCESS": "1", "TEST_TEMP_DIR": tmpdir},
		command: []string{filepath.Join(dirBase, base), "-test.run=TestHelperProcessSignalsFiFo", "--"},
		Cwd:     parentDir,
		Pid: Pid{
			Parent: filepath.Join(parentDir, "parent.pid"),
			Child:  filepath.Join(parentDir, "child.pid"),
		},
		ctl: true,
	}
	d, err := New(cfg)
	if err != nil {
		t.Error(err)
	}

	p, err := d.Run(NewProcess(cfg))
	if err != nil {
		t.Error(err)
	}

	// create socket
	if err := d.Listen(); err != nil {
		t.Fatal(err)
	}

	// check pids
	if pid, err := d.ReadPidFile(filepath.Join(parentDir, "parent.pid")); err != nil {
		t.Error(err)
	} else {
		expect(t, os.Getpid(), pid)
	}
	if pid, err := d.ReadPidFile(filepath.Join(parentDir, "child.pid")); err != nil {
		t.Error(err)
	} else {
		expect(t, p.Pid(), pid)
	}

	// Make fifo in/out
	if err := MakeFifo(filepath.Join(tmpdir, "fifo")); err != nil {
		t.Fatal(err)
	}

	// sync fifo
	time.Sleep(time.Second)

	// open fifo for reading
	fifo, err := OpenFifo(filepath.Join(tmpdir, "fifo"))
	if err != nil {
		t.Error(err)
	}

	var testSignals = []struct {
		signal   string
		expected string
	}{
		{"a", "--a"},
		{"alrm", "--a"},
		{"c", "--c"},
		{"cont", "--c"},
		{"h", "--h"},
		{"hup", "--h"},
		{"i", "--i"},
		{"int", "--i"},
		{"q", "--q"},
		{"quit", "--q"},
		{"in", "--in"},
		{"TTIN", "--in"},
		{"ou", "--ou"},
		{"TTOU", "--ou"},
		{"1", "--1"},
		{"usr1", "--1"},
		{"2", "--2"},
		{"usr2", "--2"},
		{"w", "--w"},
		{"winch", "--w"},
	}

	type Response struct {
		Err string
	}
	res := &Response{}

	for _, s := range testSignals {
		if err := getJSON(fmt.Sprintf("/signal/%s", s.signal), res); err != nil {
			t.Fatal(err)
		}
		expect(t, "", res.Err)
		waitSig(t, fifo, s.expected)
	}

	// test "d", (keep it down and don't restart)
	if err := getJSON("/signal/d", res); err != nil {
		t.Fatal(err)
	}
	// wait for process to finish
	err = <-p.errch
	atomic.StoreUint32(&d.lock, d.lockOnce)
	expect(t, "signal: terminated", err.Error())

	// create error os: process already finished
	mylog.Reset()
	for _, s := range testSignals {
		if err := getJSON(fmt.Sprintf("/signal/%s", s.signal), res); err != nil {
			t.Fatal(err)
		}
		expect(t, true, strings.HasSuffix(strings.TrimSpace(mylog.String()), "os: process already finished"))
		mylog.Reset()
	}

	if err := getJSON("/signal/d", res); err != nil {
		t.Fatal(err)
	}
	expect(t, true, strings.HasSuffix(strings.TrimSpace(mylog.String()), "os: process already finished"))

	if err := getJSON("/signal/t", res); err != nil {
		t.Fatal(err)
	}
	expect(t, true, strings.HasSuffix(strings.TrimSpace(mylog.String()), "os: process already finished"))

	if err := getJSON("/signal/p", res); err != nil {
		t.Fatal(err)
	}
	expect(t, true, strings.HasSuffix(strings.TrimSpace(mylog.String()), "os: process already finished"))

	if err := getJSON("/signal/unknown", res); err != nil {
		t.Fatal(err)
	}
	expect(t, "Unknown signal: unknown", res.Err)

	if err := getJSON("/signal/x", res); err != nil {
		t.Fatal(err)
	}
}

func waitSig(t *testing.T, fifo *os.File, sig string) {
	buf := make([]byte, 0, 8)
	r := bufio.NewReader(fifo)
	for {
		n, err := r.Read(buf[:cap(buf)])
		if n == 0 {
			if err == nil {
				continue
			}
			if err == io.EOF {
				continue
			}
			t.Fatal(err)
		}
		buf = buf[:n]
		msg := strings.TrimSpace(string(buf))
		if msg != sig {
			expect(t, sig, msg)
		}
		return
	}
}
<|endoftext|>


<commit_before>package tail

import (
	"bufio"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"path/filepath"
	"strings"
	"testing"
	"time"
)

const (
	RotateMarker   = "__ROTATE__\n"
	TruncateMarker = "__TRUNCATE__\n"
	EOFMarker      = "__EOF__\n"
)

var Logs = []string{
	"single line\n",
	"multi line 1\nmulti line 2\nmulti line 3\n",
	"continuous line 1", "continuous line 2", "continuous line 3\n",
	RotateMarker,
	"foo\n",
	"bar\n",
	"baz\n",
	TruncateMarker,
	"FOOOO\n",
	"BAAAR\n",
	"BAZZZZZZZ\n",
	EOFMarker,
}

func TestTailFile(t *testing.T) {
	tmpdir, err := ioutil.TempDir("", "go-tail.")
	if err != nil {
		t.Fatalf("unexpected error: %v", err)
	}
	defer os.RemoveAll(tmpdir)

	go writeFile(t, tmpdir)
	tail, err := NewTailFile(filepath.Join(tmpdir, "test.log"))
	if err != nil {
		t.Fatalf("unexpected error: %v", err)
	}
	defer tail.Close()

	expected := strings.Join(Logs, "")
	actual, err := receive(t, tail)
	if err != nil {
		t.Fatalf("unexpected error: %v", err)
	}
	if actual != expected {
		t.Errorf("got %s\nwant %s", actual, expected)
	}
}

func TestTailReader(t *testing.T) {
	reader, writer := io.Pipe()

	go writeWriter(t, writer)
	tail, err := NewTailReader(reader)
	if err != nil {
		t.Fatalf("unexpected error: %v", err)
	}

	expected := strings.Join(Logs, "")
	actual, err := receive(t, tail)
	if err != nil {
		t.Fatalf("unexpected error: %v", err)
	}
	if actual != expected {
		t.Errorf("got %s\nwant %s", actual, expected)
	}

	reader.Close()
	writer.Close()
	select {
	case _, ok := <-tail.Lines:
		if ok {
			t.Error("want closed, but not")
		}
	case <-time.After(time.Second):
		t.Error("want closed, but not")
	}
	select {
	case _, ok := <-tail.Errors:
		if ok {
			t.Error("want closed, but not")
		}
	case <-time.After(time.Second):
		t.Error("want closed, but not")
	}
	tail.Close()
}

func writeFile(t *testing.T, tmpdir string) error {
	filename := filepath.Join(tmpdir, "test.log")
	file, err := os.OpenFile(filename, os.O_CREATE|os.O_WRONLY, 0644)
	if err != nil {
		return err
	}

	// wait for starting to tail...
	time.Sleep(2 * time.Second)

	for _, line := range Logs {
		_, err := file.WriteString(line)
		if err != nil {
			return err
		}
		t.Logf("write: %s", line)
		switch line {
		case RotateMarker:
			file.Close()
			os.Rename(filename, filename+".old")
			file, _ = os.OpenFile(filename, os.O_CREATE|os.O_WRONLY, 0644)
		case TruncateMarker:
			time.Sleep(1 * time.Second)
			os.Truncate(filename, 0)
			file.Seek(int64(0), os.SEEK_SET)
		}
		time.Sleep(10 * time.Millisecond)
	}

	file.Close()
	return nil
}

func writeWriter(t *testing.T, writer io.Writer) error {
	w := bufio.NewWriter(writer)
	for _, line := range Logs {
		_, err := w.WriteString(line)
		if err != nil {
			return err
		}
		w.Flush()
		t.Logf("write: %s", line)
		time.Sleep(10 * time.Millisecond)
	}
	return nil
}

func receive(t *testing.T, tail *Tail) (string, error) {
	actual := ""
	for {
		select {
		case line := <-tail.Lines:
			t.Logf("received: %s", line.Text)
			actual += line.Text
			if line.Text == EOFMarker {
				return actual, nil
			}
		case err := <-tail.Errors:
			return "", err
		case <-time.After(5 * time.Second):
			return "", errors.New("timeout")
		}
	}
}

func TestTailFile_Rotate(t *testing.T) {
	tmpdir, err := ioutil.TempDir("", "go-tail.")
	if err != nil {
		t.Fatalf("unexpected error: %v", err)
	}
	defer os.RemoveAll(tmpdir)

	go func() {
		filename := filepath.Join(tmpdir, "test.log")
		for i := 0; i < 10; i++ {
			file, err := os.OpenFile(filename, os.O_CREATE|os.O_WRONLY, 0644)
			if err != nil {
				t.Error(err)
				return
			}
			if i == 0 {
				// wait for starting to tail...
				time.Sleep(2 * time.Second)
			}

			// start to write logs
			go writeFileAndClose(t, file, fmt.Sprintf("file: %d\n", i))
			time.Sleep(time.Second)

			// Rotate log file, and start writing logs into a new file.
			// While, some logs are still written into the old file.
			os.Rename(filename, fmt.Sprintf("%s.%d", filename, i))
		}
	}()

	tail, err := NewTailFile(filepath.Join(tmpdir, "test.log"))
	if err != nil {
		t.Fatalf("unexpected error: %v", err)
	}
	go func() {
		time.Sleep(25 * time.Second)
		tail.Close()
	}()
	go func() {
		for err := range tail.Errors {
			t.Log("error: ", err)
		}
	}()

	var cnt int
	for range tail.Lines {
		cnt++
	}
	if cnt != 1000 {
		t.Errorf("want 1000, got %d", cnt)
	}
}

func writeFileAndClose(t *testing.T, file *os.File, line string) {
	for i := 0; i < 100; i++ {
		_, err := file.WriteString(line)
		if err != nil {
			t.Error(err)
			return
		}
		time.Sleep(90 * time.Millisecond)
	}

	if err := file.Close(); err != nil {
		t.Error(err)
	}
}
<commit_msg>improve test of Rotate<commit_after>package tail

import (
	"bufio"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"path/filepath"
	"strings"
	"sync"
	"testing"
	"time"
)

const (
	RotateMarker   = "__ROTATE__\n"
	TruncateMarker = "__TRUNCATE__\n"
	EOFMarker      = "__EOF__\n"
)

var Logs = []string{
	"single line\n",
	"multi line 1\nmulti line 2\nmulti line 3\n",
	"continuous line 1", "continuous line 2", "continuous line 3\n",
	RotateMarker,
	"foo\n",
	"bar\n",
	"baz\n",
	TruncateMarker,
	"FOOOO\n",
	"BAAAR\n",
	"BAZZZZZZZ\n",
	EOFMarker,
}

func TestTailFile(t *testing.T) {
	tmpdir, err := ioutil.TempDir("", "go-tail.")
	if err != nil {
		t.Fatalf("unexpected error: %v", err)
	}
	defer os.RemoveAll(tmpdir)

	go writeFile(t, tmpdir)
	tail, err := NewTailFile(filepath.Join(tmpdir, "test.log"))
	if err != nil {
		t.Fatalf("unexpected error: %v", err)
	}
	defer tail.Close()

	expected := strings.Join(Logs, "")
	actual, err := receive(t, tail)
	if err != nil {
		t.Fatalf("unexpected error: %v", err)
	}
	if actual != expected {
		t.Errorf("got %s\nwant %s", actual, expected)
	}
}

func TestTailReader(t *testing.T) {
	reader, writer := io.Pipe()

	go writeWriter(t, writer)
	tail, err := NewTailReader(reader)
	if err != nil {
		t.Fatalf("unexpected error: %v", err)
	}

	expected := strings.Join(Logs, "")
	actual, err := receive(t, tail)
	if err != nil {
		t.Fatalf("unexpected error: %v", err)
	}
	if actual != expected {
		t.Errorf("got %s\nwant %s", actual, expected)
	}

	reader.Close()
	writer.Close()
	select {
	case _, ok := <-tail.Lines:
		if ok {
			t.Error("want closed, but not")
		}
	case <-time.After(time.Second):
		t.Error("want closed, but not")
	}
	select {
	case _, ok := <-tail.Errors:
		if ok {
			t.Error("want closed, but not")
		}
	case <-time.After(time.Second):
		t.Error("want closed, but not")
	}
	tail.Close()
}

func writeFile(t *testing.T, tmpdir string) error {
	filename := filepath.Join(tmpdir, "test.log")
	file, err := os.OpenFile(filename, os.O_CREATE|os.O_WRONLY, 0644)
	if err != nil {
		return err
	}

	// wait for starting to tail...
	time.Sleep(2 * time.Second)

	for _, line := range Logs {
		_, err := file.WriteString(line)
		if err != nil {
			return err
		}
		t.Logf("write: %s", line)
		switch line {
		case RotateMarker:
			file.Close()
			os.Rename(filename, filename+".old")
			file, _ = os.OpenFile(filename, os.O_CREATE|os.O_WRONLY, 0644)
		case TruncateMarker:
			time.Sleep(1 * time.Second)
			os.Truncate(filename, 0)
			file.Seek(int64(0), os.SEEK_SET)
		}
		time.Sleep(10 * time.Millisecond)
	}

	file.Close()
	return nil
}

func writeWriter(t *testing.T, writer io.Writer) error {
	w := bufio.NewWriter(writer)
	for _, line := range Logs {
		_, err := w.WriteString(line)
		if err != nil {
			return err
		}
		w.Flush()
		t.Logf("write: %s", line)
		time.Sleep(10 * time.Millisecond)
	}
	return nil
}

func receive(t *testing.T, tail *Tail) (string, error) {
	actual := ""
	for {
		select {
		case line := <-tail.Lines:
			t.Logf("received: %s", line.Text)
			actual += line.Text
			if line.Text == EOFMarker {
				return actual, nil
			}
		case err := <-tail.Errors:
			return "", err
		case <-time.After(5 * time.Second):
			return "", errors.New("timeout")
		}
	}
}

func TestTailFile_Rotate(t *testing.T) {
	tmpdir, err := ioutil.TempDir("", "go-tail.")
	if err != nil {
		t.Fatalf("unexpected error: %v", err)
	}
	defer os.RemoveAll(tmpdir)

	var wg sync.WaitGroup
	wg.Add(1)
	go func() {
		defer wg.Done()
		filename := filepath.Join(tmpdir, "test.log")
		for i := 0; i < 10; i++ {
			file, err := os.OpenFile(filename, os.O_CREATE|os.O_WRONLY, 0644)
			if err != nil {
				t.Error(err)
				return
			}
			if i == 0 {
				// wait for starting to tail...
				time.Sleep(2 * time.Second)
			}

			// start to write logs
			wg.Add(1)
			go func() {
				defer wg.Done()
				writeFileAndClose(t, file, fmt.Sprintf("file: %d\n", i))
			}()
			time.Sleep(time.Second)

			// Rotate log file, and start writing logs into a new file.
			// While, some logs are still written into the old file.
			os.Rename(filename, fmt.Sprintf("%s.%d", filename, i))
		}
	}()

	tail, err := NewTailFile(filepath.Join(tmpdir, "test.log"))
	if err != nil {
		t.Fatalf("unexpected error: %v", err)
	}
	go func() {
		wg.Wait()
		tail.Close()
	}()
	go func() {
		for err := range tail.Errors {
			t.Log("error: ", err)
		}
	}()

	var cnt int
	for range tail.Lines {
		cnt++
	}
	if cnt != 1000 {
		t.Errorf("want 1000, got %d", cnt)
	}
}

func writeFileAndClose(t *testing.T, file *os.File, line string) {
	for i := 0; i < 100; i++ {
		_, err := file.WriteString(line)
		if err != nil {
			_ = file.Close()
			t.Error(err)
			return
		}
		time.Sleep(90 * time.Millisecond)
	}

	if err := file.Close(); err != nil {
		t.Error(err)
	}
}
<|endoftext|>