<commit_before>package main

import (
	"errors"
	"fmt"
	"regexp"
	"strings"
	"testing"
)

func testcmd(cmd string) (string, error) {
	fmt.Println("(T) " + cmd)
	switch {
	case cmd == "sudo ls -a1F /mnt/sda1/var/lib/docker/vfs/dir":
		return currenttest.vs.ls(), nil
	case cmd == "docker ps -aq --no-trunc":
		return currenttest.cs.ps(), nil
	case strings.HasPrefix(cmd, "docker inspect -f '{{ .Name }},{{ range $key, $value := .Volumes }}{{ $key }},{{ $value }}##~#{{ end }}' "):
		return "", nil
	case strings.HasPrefix(cmd, "sudo rm /mnt/sda1/var/lib/docker/vfs/dir/"):
		deleted := cmd[len("sudo rm /mnt/sda1/var/lib/docker/vfs/dir/"):]
		deletions = append(deletions, deleted)
		return "", nil
	case strings.HasPrefix(cmd, "sudo readlink /mnt/sda1/var/lib/docker/vfs/dir/"):
		if strings.Contains(cmd, ",nonexistent") {
			return "", errors.New("non-existent linked folder")
		}
		r := regexp.MustCompile(`.*\$([^#]+)###.*`)
		ss := r.FindStringSubmatch(cmd)
		if len(ss) == 2 {
			folder := ss[1]
			folder = folder + strings.Repeat("1", 64-len(folder))
			return folder, nil
		}
		return "", nil
	case strings.HasPrefix(cmd, "sudo ls /mnt/sda1/var/lib/docker/vfs/dir/"):
		if cmd == "sudo ls /mnt/sda1/var/lib/docker/vfs/dir/" {
			return "", errors.New("non-ls linked folder")
		}
		return "", nil
	default:
		currentT.Fatalf("test '%s': unknown command!\n", cmd)
		return fmt.Sprintf("test '%s'", cmd), errors.New("unknown command")
	}
}

type volspecs []string
type contspecs []string
type Test struct {
	title string
	vs    volspecs
	cs    contspecs
	res   []int
	strs  []string
}

func newTest(title string) *Test {
	return &Test{title: title, res: []int{0, 0, 0, 0, 0}}
}
func (t *Test) setContainersPs(cs contspecs) *Test {
	t.cs = cs
	return t
}

func (vs volspecs) ls() string {
	if len(vs) == 0 {
		return ""
	}
	res := ""
	for i, spec := range vs {
		switch {
		case strings.HasSuffix(spec, "/"):
			spec = spec[:len(spec)-1]
			res = res + spec + strings.Repeat(fmt.Sprintf("%d", i), 64-len(spec)) + "/\n"
		case strings.HasSuffix(spec, "@"):
			mp := "." + strings.Replace(spec, ";", "###", -1)
			mp = strings.Replace(mp, "/", ",#,", -1)
			res = res + mp + "\n"

		default:
			res = res + spec + "\n"
		}
	}
	return res
}

func (cs contspecs) ps() string {
	if len(cs) == 0 {
		return ""
	}
	res := ""
	for _, spec := range cs {
		switch {
		default:
			res = res + spec + "\n"
		}
	}
	return res
}

var deletions = []string{}
var tests = []*Test{
	newTest("empty vfs"),
	newTest("2 valid containers").
		setContainersPs([]string{"contA", "contB"}),
	/*
		Test{"empty vfs", []string{}, []int{0, 0, 0, 0, 0}, []string{}},
		Test{"two volumes", []string{"fa/", "fb/"}, []int{0, 0, 2, 2, 0}, []string{"vol 'fa00000'<<nil>>", "vol 'fb11111'<<nil>>"}},
		Test{"Invalid (ill-formed) markers must be deleted", []string{"cainv/path/a@"}, []int{0, 0, 0, 0, -1}, []string{}},
		Test{"Invalid (no readlink) markers must be deleted", []string{"ca;/path/nonexistenta@", "cb;/path/nonexistentb@"}, []int{0, 0, 0, 0, -2}, []string{}},
		Test{"Invalid (no ls) markers must be deleted", []string{"ca;/path/nolsa@", "cb;/path/nolsb@"}, []int{0, 0, 0, 0, -2}, []string{}},
		Test{"Invalid (no vdir) markers must be deleted", []string{"ca$novdira;/path/nolsa@", "cb$novdirb;/path/nolsb@"}, []int{0, 0, 0, 0, -2}, []string{}},
		Test{"two valid markers", []string{"ca$fa;/path/vola@", "cb$fb;/path/volb@"}, []int{0, 0, 0, 0, 2}, []string{"marker 'fa11111'<ca$fa->/path/vola>", "marker 'fb11111'<cb$fb->/path/volb>"}},
		Test{"Invalid (bad name) volume", []string{"inva/"}, []int{0, 0, -1, 0, 0}, []string{}},
		Test{"Invalid file in volume vfs dir", []string{"invf"}, []int{0, 0, -1, 0, 0}, []string{}},
	*/
}
var currenttest *Test
var currentT *testing.T

// TestContainers test different vfs scenarios
func TestContainers(t *testing.T) {
	cmd = testcmd
	currentT = t
	for i, test := range tests {
		currenttest = test
		deletions = []string{}
		fmt.Println("------ vvv " + test.title + " vvv ------")
		main()
		tc := Containers()
		toc := OrphanedContainers()
		tv := Volumes()
		tov := OrphanedVolumes()
		tm := Markers()
		if len(tc) != test.res[0] {
			t.Errorf("Test %d: '%s' expected '%d' containers, got '%d'", i+1, test.title, test.res[0], len(tc))
		}
		if len(toc) != test.res[1] {
			t.Errorf("Test %d: '%s' expected '%d' orphaned containers, got '%d'", i+1, test.title, test.res[1], len(toc))
		}
		if nbvolumes(tv) != test.res[2] {
			t.Errorf("Test %d: '%s' expected '%d' volumes, got '%d'", i+1, test.title, test.res[2], nbvolumes(tv))
		}
		if len(tov) != test.res[3] {
			t.Errorf("Test %d: '%s' expected '%d' orphaned volumes, got '%d'", i+1, test.title, test.res[3], len(tov))
		}
		if nbmarkers(tm) != test.res[4] {
			t.Errorf("Test %d: '%s' expected '%d' markers, got '%d'", i+1, test.title, test.res[4], nbmarkers(tm))
		}

		for _, v := range tv {
			vs := v.String()
			check(vs, "volume", test, t, i)
		}
		for _, m := range tm {
			ms := m.String()
			check(ms, "marker", test, t, i)
		}
		fmt.Println("------ ^^^ " + test.title + " ^^^ ------")
		fmt.Println("----------")
	}
}

func check(s string, tmsg string, test *Test, t *testing.T, i int) {
	found := false
	for _, tms := range test.strs {
		if s == tms {
			found = true
			break
		}
	}
	if !found {
		t.Errorf("Test %d: '%s' expected %s '%s', not found", i+1, test.title, tmsg, s)
	}

}

func nbmarkers(tm markers) int {
	res := len(tm)
	for _, d := range deletions {
		if strings.HasPrefix(d, ".") {
			res = res - 1
		}
	}
	return res
}

func nbvolumes(vm volumes) int {
	res := len(vm)
	for _, d := range deletions {
		if !strings.HasPrefix(d, ".") {
			res = res - 1
		}
	}
	return res
}
<commit_msg>gcl_test.go: return right number of containers based on expect specs<commit_after>package main

import (
	"errors"
	"fmt"
	"regexp"
	"strings"
	"testing"
)

func testcmd(cmd string) (string, error) {
	fmt.Println("(T) " + cmd)
	switch {
	case cmd == "sudo ls -a1F /mnt/sda1/var/lib/docker/vfs/dir":
		return currenttest.vs.ls(), nil
	case cmd == "docker ps -aq --no-trunc":
		res := ""
		for _, _ = range currenttest.cs {
			res = res + "x\n"
		}
		return res, nil
	case strings.HasPrefix(cmd, "docker inspect -f '{{ .Name }},{{ range $key, $value := .Volumes }}{{ $key }},{{ $value }}##~#{{ end }}' "):
		return "", nil
	case strings.HasPrefix(cmd, "sudo rm /mnt/sda1/var/lib/docker/vfs/dir/"):
		deleted := cmd[len("sudo rm /mnt/sda1/var/lib/docker/vfs/dir/"):]
		deletions = append(deletions, deleted)
		return "", nil
	case strings.HasPrefix(cmd, "sudo readlink /mnt/sda1/var/lib/docker/vfs/dir/"):
		if strings.Contains(cmd, ",nonexistent") {
			return "", errors.New("non-existent linked folder")
		}
		r := regexp.MustCompile(`.*\$([^#]+)###.*`)
		ss := r.FindStringSubmatch(cmd)
		if len(ss) == 2 {
			folder := ss[1]
			folder = folder + strings.Repeat("1", 64-len(folder))
			return folder, nil
		}
		return "", nil
	case strings.HasPrefix(cmd, "sudo ls /mnt/sda1/var/lib/docker/vfs/dir/"):
		if cmd == "sudo ls /mnt/sda1/var/lib/docker/vfs/dir/" {
			return "", errors.New("non-ls linked folder")
		}
		return "", nil
	default:
		currentT.Fatalf("test '%s': unknown command!\n", cmd)
		return fmt.Sprintf("test '%s'", cmd), errors.New("unknown command")
	}
}

type volspecs []string
type contspecs []string
type Test struct {
	title string
	vs    volspecs
	cs    contspecs
	res   []int
	strs  []string
}

func newTest(title string) *Test {
	return &Test{title: title, res: []int{0, 0, 0, 0, 0}}
}
func (t *Test) setContainersPs(cs contspecs) *Test {
	t.cs = cs
	return t
}

func (vs volspecs) ls() string {
	if len(vs) == 0 {
		return ""
	}
	res := ""
	for i, spec := range vs {
		switch {
		case strings.HasSuffix(spec, "/"):
			spec = spec[:len(spec)-1]
			res = res + spec + strings.Repeat(fmt.Sprintf("%d", i), 64-len(spec)) + "/\n"
		case strings.HasSuffix(spec, "@"):
			mp := "." + strings.Replace(spec, ";", "###", -1)
			mp = strings.Replace(mp, "/", ",#,", -1)
			res = res + mp + "\n"

		default:
			res = res + spec + "\n"
		}
	}
	return res
}

func (cs contspecs) ps() string {
	if len(cs) == 0 {
		return ""
	}
	res := ""
	for _, spec := range cs {
		switch {
		default:
			res = res + spec + "\n"
		}
	}
	return res
}

var deletions = []string{}
var tests = []*Test{
	newTest("empty vfs"),
	newTest("2 valid containers").
		setContainersPs([]string{"contA", "contB"}),
	/*
		Test{"empty vfs", []string{}, []int{0, 0, 0, 0, 0}, []string{}},
		Test{"two volumes", []string{"fa/", "fb/"}, []int{0, 0, 2, 2, 0}, []string{"vol 'fa00000'<<nil>>", "vol 'fb11111'<<nil>>"}},
		Test{"Invalid (ill-formed) markers must be deleted", []string{"cainv/path/a@"}, []int{0, 0, 0, 0, -1}, []string{}},
		Test{"Invalid (no readlink) markers must be deleted", []string{"ca;/path/nonexistenta@", "cb;/path/nonexistentb@"}, []int{0, 0, 0, 0, -2}, []string{}},
		Test{"Invalid (no ls) markers must be deleted", []string{"ca;/path/nolsa@", "cb;/path/nolsb@"}, []int{0, 0, 0, 0, -2}, []string{}},
		Test{"Invalid (no vdir) markers must be deleted", []string{"ca$novdira;/path/nolsa@", "cb$novdirb;/path/nolsb@"}, []int{0, 0, 0, 0, -2}, []string{}},
		Test{"two valid markers", []string{"ca$fa;/path/vola@", "cb$fb;/path/volb@"}, []int{0, 0, 0, 0, 2}, []string{"marker 'fa11111'<ca$fa->/path/vola>", "marker 'fb11111'<cb$fb->/path/volb>"}},
		Test{"Invalid (bad name) volume", []string{"inva/"}, []int{0, 0, -1, 0, 0}, []string{}},
		Test{"Invalid file in volume vfs dir", []string{"invf"}, []int{0, 0, -1, 0, 0}, []string{}},
	*/
}
var currenttest *Test
var currentT *testing.T

// TestContainers test different vfs scenarios
func TestContainers(t *testing.T) {
	cmd = testcmd
	currentT = t
	for i, test := range tests {
		currenttest = test
		deletions = []string{}
		fmt.Println("------ vvv " + test.title + " vvv ------")
		main()
		tc := Containers()
		toc := OrphanedContainers()
		tv := Volumes()
		tov := OrphanedVolumes()
		tm := Markers()
		if len(tc) != test.res[0] {
			t.Errorf("Test %d: '%s' expected '%d' containers, got '%d'", i+1, test.title, test.res[0], len(tc))
		}
		if len(toc) != test.res[1] {
			t.Errorf("Test %d: '%s' expected '%d' orphaned containers, got '%d'", i+1, test.title, test.res[1], len(toc))
		}
		if nbvolumes(tv) != test.res[2] {
			t.Errorf("Test %d: '%s' expected '%d' volumes, got '%d'", i+1, test.title, test.res[2], nbvolumes(tv))
		}
		if len(tov) != test.res[3] {
			t.Errorf("Test %d: '%s' expected '%d' orphaned volumes, got '%d'", i+1, test.title, test.res[3], len(tov))
		}
		if nbmarkers(tm) != test.res[4] {
			t.Errorf("Test %d: '%s' expected '%d' markers, got '%d'", i+1, test.title, test.res[4], nbmarkers(tm))
		}

		for _, v := range tv {
			vs := v.String()
			check(vs, "volume", test, t, i)
		}
		for _, m := range tm {
			ms := m.String()
			check(ms, "marker", test, t, i)
		}
		fmt.Println("------ ^^^ " + test.title + " ^^^ ------")
		fmt.Println("----------")
	}
}

func check(s string, tmsg string, test *Test, t *testing.T, i int) {
	found := false
	for _, tms := range test.strs {
		if s == tms {
			found = true
			break
		}
	}
	if !found {
		t.Errorf("Test %d: '%s' expected %s '%s', not found", i+1, test.title, tmsg, s)
	}

}

func nbmarkers(tm markers) int {
	res := len(tm)
	for _, d := range deletions {
		if strings.HasPrefix(d, ".") {
			res = res - 1
		}
	}
	return res
}

func nbvolumes(vm volumes) int {
	res := len(vm)
	for _, d := range deletions {
		if !strings.HasPrefix(d, ".") {
			res = res - 1
		}
	}
	return res
}
<|endoftext|>


<commit_before>package vm

import (
	"fmt"
	"github.com/goby-lang/goby/bytecode"
	"strings"
)

type operation func(vm *VM, cf *callFrame, args ...interface{})

type operationType string

type label struct {
	name string
	Type labelType
}

type labelType string

type action struct {
	name      string
	operation operation
}

type instruction struct {
	action *action
	Params []interface{}
	Line   int
}

type instructionSet struct {
	label        *label
	instructions []*instruction
	filename     filename
}

func (is *instructionSet) define(line int, a *action, params ...interface{}) {
	i := &instruction{action: a, Params: params, Line: line}
	is.instructions = append(is.instructions, i)
}

var builtInActions = map[operationType]*action{
	bytecode.Pop: {
		name: bytecode.Pop,
		operation: func(vm *VM, cf *callFrame, args ...interface{}) {
			vm.stack.pop()
		},
	},
	bytecode.PutObject: {
		name: bytecode.PutObject,
		operation: func(vm *VM, cf *callFrame, args ...interface{}) {
			object := initializeObject(args[0])
			vm.stack.push(&Pointer{Target: object})
		},
	},
	bytecode.GetConstant: {
		name: bytecode.GetConstant,
		operation: func(vm *VM, cf *callFrame, args ...interface{}) {
			constName := args[0].(string)
			c := vm.lookupConstant(cf, constName)

			if c == nil {
				msg := "Can't find constant: " + constName
				vm.returnError(msg)
			}

			vm.stack.push(c)
		},
	},
	bytecode.GetLocal: {
		name: bytecode.GetLocal,
		operation: func(vm *VM, cf *callFrame, args ...interface{}) {
			index := args[0].(int)
			depth := 0

			if len(args) >= 2 {
				depth = args[1].(int)
			}

			p := cf.getLCL(index, depth)

			//if p == nil {
			//	vm.returnError(fmt.Sprintf("locals index: %d is nil. Callframe: %s", index, cf.instructionSet.label.name))
			//}

			vm.stack.push(p)
		},
	},
	bytecode.GetInstanceVariable: {
		name: bytecode.GetInstanceVariable,
		operation: func(vm *VM, cf *callFrame, args ...interface{}) {
			variableName := args[0].(string)
			v, ok := cf.self.(*RObject).InstanceVariables.get(variableName)
			if !ok {
				vm.stack.push(&Pointer{Target: NULL})
				return
			}

			p := &Pointer{Target: v}
			vm.stack.push(p)
		},
	},
	bytecode.SetInstanceVariable: {
		name: bytecode.SetInstanceVariable,
		operation: func(vm *VM, cf *callFrame, args ...interface{}) {
			variableName := args[0].(string)
			p := vm.stack.pop()
			cf.self.(*RObject).InstanceVariables.set(variableName, p.Target)
		},
	},
	bytecode.SetLocal: {
		name: bytecode.SetLocal,
		operation: func(vm *VM, cf *callFrame, args ...interface{}) {
			v := vm.stack.pop()
			depth := 0

			if len(args) >= 2 {
				depth = args[1].(int)
			}
			cf.insertLCL(args[0].(int), depth, v.Target)
		},
	},
	bytecode.SetConstant: {
		name: bytecode.SetConstant,
		operation: func(vm *VM, cf *callFrame, args ...interface{}) {
			constName := args[0].(string)
			v := vm.stack.pop()

			cf.storeConstant(constName, v)
		},
	},
	bytecode.NewArray: {
		name: bytecode.NewArray,
		operation: func(vm *VM, cf *callFrame, args ...interface{}) {
			argCount := args[0].(int)
			elems := []Object{}

			for i := 0; i < argCount; i++ {
				v := vm.stack.pop()
				elems = append([]Object{v.Target}, elems...)
			}

			arr := initializeArray(elems)
			vm.stack.push(&Pointer{arr})
		},
	},
	bytecode.NewHash: {
		name: bytecode.NewHash,
		operation: func(vm *VM, cf *callFrame, args ...interface{}) {
			argCount := args[0].(int)
			pairs := map[string]Object{}

			for i := 0; i < argCount/2; i++ {
				v := vm.stack.pop()
				k := vm.stack.pop()
				pairs[k.Target.(*StringObject).Value] = v.Target
			}

			hash := initializeHash(pairs)
			vm.stack.push(&Pointer{hash})
		},
	},
	bytecode.BranchUnless: {
		name: bytecode.BranchUnless,
		operation: func(vm *VM, cf *callFrame, args ...interface{}) {
			v := vm.stack.pop()
			bool, isBool := v.Target.(*BooleanObject)

			if isBool {
				if bool.Value {
					return
				}

				line := args[0].(int)
				cf.pc = line
				return
			}

			_, isNull := v.Target.(*NullObject)

			if isNull {
				line := args[0].(int)
				cf.pc = line
				return
			}
		},
	},
	bytecode.BranchIf: {
		name: bytecode.BranchIf,
		operation: func(vm *VM, cf *callFrame, args ...interface{}) {
			v := vm.stack.pop()
			bool, isBool := v.Target.(*BooleanObject)

			if isBool {
				if !bool.Value {
					return
				}

				line := args[0].(int)
				cf.pc = line
				return
			}
		},
	},
	bytecode.Jump: {
		name: bytecode.Jump,
		operation: func(vm *VM, cf *callFrame, args ...interface{}) {
			cf.pc = args[0].(int)
		},
	},
	bytecode.PutSelf: {
		name: bytecode.PutSelf,
		operation: func(vm *VM, cf *callFrame, args ...interface{}) {
			vm.stack.push(&Pointer{cf.self})
		},
	},
	bytecode.PutString: {
		name: bytecode.PutString,
		operation: func(vm *VM, cf *callFrame, args ...interface{}) {
			object := initializeObject(args[0])
			vm.stack.push(&Pointer{object})
		},
	},
	bytecode.PutNull: {
		name: bytecode.PutNull,
		operation: func(vm *VM, cf *callFrame, args ...interface{}) {
			vm.stack.push(&Pointer{NULL})
		},
	},
	bytecode.DefMethod: {
		name: bytecode.DefMethod,
		operation: func(vm *VM, cf *callFrame, args ...interface{}) {
			argCount := args[0].(int)
			methodName := vm.stack.pop().Target.(*StringObject).Value
			is, _ := vm.getMethodIS(methodName, cf.instructionSet.filename)
			method := &MethodObject{Name: methodName, argc: argCount, instructionSet: is, class: methodClass}

			v := vm.stack.pop().Target
			switch self := v.(type) {
			case *RClass:
				self.Methods.set(methodName, method)
			default:
				self.returnClass().(*RClass).Methods.set(methodName, method)
			}
		},
	},
	bytecode.DefSingletonMethod: {
		name: bytecode.DefSingletonMethod,
		operation: func(vm *VM, cf *callFrame, args ...interface{}) {
			argCount := args[0].(int)
			methodName := vm.stack.pop().Target.(*StringObject).Value
			is, _ := vm.getMethodIS(methodName, cf.instructionSet.filename)
			method := &MethodObject{Name: methodName, argc: argCount, instructionSet: is, class: methodClass}

			v := vm.stack.pop().Target

			switch self := v.(type) {
			case *RClass:
				self.setSingletonMethod(methodName, method)
			default:
				self.returnClass().setSingletonMethod(methodName, method)
			}
		},
	},
	bytecode.DefClass: {
		name: bytecode.DefClass,
		operation: func(vm *VM, cf *callFrame, args ...interface{}) {
			subject := strings.Split(args[0].(string), ":")
			subjectType, subjectName := subject[0], subject[1]
			class := initializeClass(subjectName, subjectType == "module")
			classPr := cf.storeConstant(class.Name, class)

			is := vm.getClassIS(class.Name, cf.instructionSet.filename)

			if len(args) >= 2 {
				superClassName := args[1].(string)
				superClass := vm.lookupConstant(cf, superClassName)
				inheritedClass, ok := superClass.Target.(*RClass)

				if !ok {
					vm.returnError("Constant " + superClassName + " is not a class. got=" + string(superClass.Target.returnClass().ReturnName()))
				}

				class.pseudoSuperClass = inheritedClass
				class.superClass = inheritedClass
			}

			vm.stack.pop()
			c := newCallFrame(is)
			c.self = class
			vm.callFrameStack.push(c)
			vm.startFromTopFrame()

			vm.stack.push(classPr)
		},
	},
	bytecode.Send: {
		name: bytecode.Send,
		operation: func(vm *VM, cf *callFrame, args ...interface{}) {
			var method Object

			methodName := args[0].(string)
			argCount := args[1].(int)
			argPr := vm.sp - argCount
			receiverPr := argPr - 1
			receiver := vm.stack.Data[receiverPr].Target

			switch r := receiver.(type) {
			case Class:
				method = r.lookupClassMethod(methodName)
			default:
				method = r.returnClass().lookupInstanceMethod(methodName)
			}

			if method == nil {
				vm.returnError("undefined method `" + methodName + "' for " + receiver.Inspect())
			}

			blockFrame := vm.retrieveBlock(cf, args)

			switch m := method.(type) {
			case *MethodObject:
				vm.evalMethodObject(receiver, m, receiverPr, argCount, argPr, blockFrame)
			case *BuiltInMethodObject:
				vm.evalBuiltInMethod(receiver, m, receiverPr, argCount, argPr, blockFrame)
			case *Error:
				vm.returnError(m.Inspect())
			default:
				vm.returnError(fmt.Sprintf("unknown instance method type: %T", m))
			}
		},
	},
	bytecode.InvokeBlock: {
		name: bytecode.InvokeBlock,
		operation: func(vm *VM, cf *callFrame, args ...interface{}) {
			argCount := args[0].(int)
			argPr := vm.sp - argCount
			receiverPr := argPr - 1
			receiver := vm.stack.Data[receiverPr].Target

			if cf.blockFrame == nil {
				vm.returnError("Can't yield without a block")
			}

			c := newCallFrame(cf.blockFrame.instructionSet)
			c.blockFrame = cf.blockFrame
			c.ep = cf.blockFrame.ep
			c.self = receiver

			for i := 0; i < argCount; i++ {
				c.locals[i] = vm.stack.Data[argPr+i]
			}

			vm.callFrameStack.push(c)
			vm.startFromTopFrame()

			vm.stack.Data[receiverPr] = vm.stack.top()
			vm.sp = receiverPr + 1
		},
	},
	bytecode.Leave: {
		name: bytecode.Leave,
		operation: func(vm *VM, cf *callFrame, args ...interface{}) {
			cf = vm.callFrameStack.pop()
			cf.pc = len(cf.instructionSet.instructions)
		},
	},
}

func (vm *VM) retrieveBlock(cf *callFrame, args []interface{}) (blockFrame *callFrame) {
	var blockName string
	var hasBlock bool

	if len(args) > 2 {
		hasBlock = true
		blockFlag := args[2].(string)
		blockName = strings.Split(blockFlag, ":")[1]
	} else {
		hasBlock = false
	}

	if hasBlock {
		block := vm.getBlock(blockName, cf.instructionSet.filename)

		c := newCallFrame(block)
		c.isBlock = true
		c.ep = cf
		c.self = cf.self

		vm.callFrameStack.push(c)
		blockFrame = c
	}

	return
}

func (vm *VM) evalBuiltInMethod(receiver Object, method *BuiltInMethodObject, receiverPr, argCount, argPr int, blockFrame *callFrame) {
	methodBody := method.Fn(receiver)
	args := []Object{}

	for i := 0; i < argCount; i++ {
		args = append(args, vm.stack.Data[argPr+i].Target)
	}

	evaluated := methodBody(vm, args, blockFrame)

	_, ok := receiver.(*RClass)
	if method.Name == "new" && ok {
		instance := evaluated.(*RObject)
		if instance.InitializeMethod != nil {
			vm.evalMethodObject(instance, instance.InitializeMethod, receiverPr, argCount, argPr, blockFrame)
		}
	}
	vm.stack.Data[receiverPr] = &Pointer{evaluated}
	vm.sp = receiverPr + 1
}

func (vm *VM) evalMethodObject(receiver Object, method *MethodObject, receiverPr, argC, argPr int, blockFrame *callFrame) {
	c := newCallFrame(method.instructionSet)
	c.self = receiver

	for i := 0; i < argC; i++ {
		c.insertLCL(i, 0, vm.stack.Data[argPr+i].Target)
	}

	c.blockFrame = blockFrame
	vm.callFrameStack.push(c)
	vm.startFromTopFrame()

	vm.stack.Data[receiverPr] = vm.stack.top()
	vm.sp = receiverPr + 1
}

func initializeObject(value interface{}) Object {
	switch v := value.(type) {
	case int:
		return initilaizeInteger(int(v))
	case int64:
		return initilaizeInteger(int(v))
	case string:
		switch v {
		case "true":
			return TRUE
		case "false":
			return FALSE
		case "nil":
			return NULL
		default:
			return initializeString(v)
		}
	default:
		panic(fmt.Sprintf("Unknown data type: %T", v))
	}
}
<commit_msg>Remove some impossible condition handling.<commit_after>package vm

import (
	"fmt"
	"github.com/goby-lang/goby/bytecode"
	"strings"
)

type operation func(vm *VM, cf *callFrame, args ...interface{})

type operationType string

type label struct {
	name string
	Type labelType
}

type labelType string

type action struct {
	name      string
	operation operation
}

type instruction struct {
	action *action
	Params []interface{}
	Line   int
}

type instructionSet struct {
	label        *label
	instructions []*instruction
	filename     filename
}

func (is *instructionSet) define(line int, a *action, params ...interface{}) {
	i := &instruction{action: a, Params: params, Line: line}
	is.instructions = append(is.instructions, i)
}

var builtInActions = map[operationType]*action{
	bytecode.Pop: {
		name: bytecode.Pop,
		operation: func(vm *VM, cf *callFrame, args ...interface{}) {
			vm.stack.pop()
		},
	},
	bytecode.PutObject: {
		name: bytecode.PutObject,
		operation: func(vm *VM, cf *callFrame, args ...interface{}) {
			object := initializeObject(args[0])
			vm.stack.push(&Pointer{Target: object})
		},
	},
	bytecode.GetConstant: {
		name: bytecode.GetConstant,
		operation: func(vm *VM, cf *callFrame, args ...interface{}) {
			constName := args[0].(string)
			c := vm.lookupConstant(cf, constName)

			if c == nil {
				msg := "Can't find constant: " + constName
				vm.returnError(msg)
			}

			vm.stack.push(c)
		},
	},
	bytecode.GetLocal: {
		name: bytecode.GetLocal,
		operation: func(vm *VM, cf *callFrame, args ...interface{}) {
			index := args[0].(int)
			depth := 0

			if len(args) >= 2 {
				depth = args[1].(int)
			}

			p := cf.getLCL(index, depth)

			//if p == nil {
			//	vm.returnError(fmt.Sprintf("locals index: %d is nil. Callframe: %s", index, cf.instructionSet.label.name))
			//}

			vm.stack.push(p)
		},
	},
	bytecode.GetInstanceVariable: {
		name: bytecode.GetInstanceVariable,
		operation: func(vm *VM, cf *callFrame, args ...interface{}) {
			variableName := args[0].(string)
			v, ok := cf.self.(*RObject).InstanceVariables.get(variableName)
			if !ok {
				vm.stack.push(&Pointer{Target: NULL})
				return
			}

			p := &Pointer{Target: v}
			vm.stack.push(p)
		},
	},
	bytecode.SetInstanceVariable: {
		name: bytecode.SetInstanceVariable,
		operation: func(vm *VM, cf *callFrame, args ...interface{}) {
			variableName := args[0].(string)
			p := vm.stack.pop()
			cf.self.(*RObject).InstanceVariables.set(variableName, p.Target)
		},
	},
	bytecode.SetLocal: {
		name: bytecode.SetLocal,
		operation: func(vm *VM, cf *callFrame, args ...interface{}) {
			v := vm.stack.pop()
			depth := 0

			if len(args) >= 2 {
				depth = args[1].(int)
			}
			cf.insertLCL(args[0].(int), depth, v.Target)
		},
	},
	bytecode.SetConstant: {
		name: bytecode.SetConstant,
		operation: func(vm *VM, cf *callFrame, args ...interface{}) {
			constName := args[0].(string)
			v := vm.stack.pop()

			cf.storeConstant(constName, v)
		},
	},
	bytecode.NewArray: {
		name: bytecode.NewArray,
		operation: func(vm *VM, cf *callFrame, args ...interface{}) {
			argCount := args[0].(int)
			elems := []Object{}

			for i := 0; i < argCount; i++ {
				v := vm.stack.pop()
				elems = append([]Object{v.Target}, elems...)
			}

			arr := initializeArray(elems)
			vm.stack.push(&Pointer{arr})
		},
	},
	bytecode.NewHash: {
		name: bytecode.NewHash,
		operation: func(vm *VM, cf *callFrame, args ...interface{}) {
			argCount := args[0].(int)
			pairs := map[string]Object{}

			for i := 0; i < argCount/2; i++ {
				v := vm.stack.pop()
				k := vm.stack.pop()
				pairs[k.Target.(*StringObject).Value] = v.Target
			}

			hash := initializeHash(pairs)
			vm.stack.push(&Pointer{hash})
		},
	},
	bytecode.BranchUnless: {
		name: bytecode.BranchUnless,
		operation: func(vm *VM, cf *callFrame, args ...interface{}) {
			v := vm.stack.pop()
			bool, isBool := v.Target.(*BooleanObject)

			if isBool {
				if bool.Value {
					return
				}

				line := args[0].(int)
				cf.pc = line
				return
			}

			_, isNull := v.Target.(*NullObject)

			if isNull {
				line := args[0].(int)
				cf.pc = line
				return
			}
		},
	},
	bytecode.BranchIf: {
		name: bytecode.BranchIf,
		operation: func(vm *VM, cf *callFrame, args ...interface{}) {
			v := vm.stack.pop()
			bool, isBool := v.Target.(*BooleanObject)

			if isBool {
				if !bool.Value {
					return
				}

				line := args[0].(int)
				cf.pc = line
				return
			}
		},
	},
	bytecode.Jump: {
		name: bytecode.Jump,
		operation: func(vm *VM, cf *callFrame, args ...interface{}) {
			cf.pc = args[0].(int)
		},
	},
	bytecode.PutSelf: {
		name: bytecode.PutSelf,
		operation: func(vm *VM, cf *callFrame, args ...interface{}) {
			vm.stack.push(&Pointer{cf.self})
		},
	},
	bytecode.PutString: {
		name: bytecode.PutString,
		operation: func(vm *VM, cf *callFrame, args ...interface{}) {
			object := initializeObject(args[0])
			vm.stack.push(&Pointer{object})
		},
	},
	bytecode.PutNull: {
		name: bytecode.PutNull,
		operation: func(vm *VM, cf *callFrame, args ...interface{}) {
			vm.stack.push(&Pointer{NULL})
		},
	},
	bytecode.DefMethod: {
		name: bytecode.DefMethod,
		operation: func(vm *VM, cf *callFrame, args ...interface{}) {
			argCount := args[0].(int)
			methodName := vm.stack.pop().Target.(*StringObject).Value
			is, _ := vm.getMethodIS(methodName, cf.instructionSet.filename)
			method := &MethodObject{Name: methodName, argc: argCount, instructionSet: is, class: methodClass}

			v := vm.stack.pop().Target
			switch self := v.(type) {
			case *RClass:
				self.Methods.set(methodName, method)
			default:
				self.returnClass().(*RClass).Methods.set(methodName, method)
			}
		},
	},
	bytecode.DefSingletonMethod: {
		name: bytecode.DefSingletonMethod,
		operation: func(vm *VM, cf *callFrame, args ...interface{}) {
			argCount := args[0].(int)
			methodName := vm.stack.pop().Target.(*StringObject).Value
			is, _ := vm.getMethodIS(methodName, cf.instructionSet.filename)
			method := &MethodObject{Name: methodName, argc: argCount, instructionSet: is, class: methodClass}

			v := vm.stack.pop().Target

			switch self := v.(type) {
			case *RClass:
				self.setSingletonMethod(methodName, method)
			default:
				self.returnClass().setSingletonMethod(methodName, method)
			}
		},
	},
	bytecode.DefClass: {
		name: bytecode.DefClass,
		operation: func(vm *VM, cf *callFrame, args ...interface{}) {
			subject := strings.Split(args[0].(string), ":")
			subjectType, subjectName := subject[0], subject[1]
			class := initializeClass(subjectName, subjectType == "module")
			classPr := cf.storeConstant(class.Name, class)

			is := vm.getClassIS(class.Name, cf.instructionSet.filename)

			if len(args) >= 2 {
				superClassName := args[1].(string)
				superClass := vm.lookupConstant(cf, superClassName)
				inheritedClass, ok := superClass.Target.(*RClass)

				if !ok {
					vm.returnError("Constant " + superClassName + " is not a class. got=" + string(superClass.Target.returnClass().ReturnName()))
				}

				class.pseudoSuperClass = inheritedClass
				class.superClass = inheritedClass
			}

			vm.stack.pop()
			c := newCallFrame(is)
			c.self = class
			vm.callFrameStack.push(c)
			vm.startFromTopFrame()

			vm.stack.push(classPr)
		},
	},
	bytecode.Send: {
		name: bytecode.Send,
		operation: func(vm *VM, cf *callFrame, args ...interface{}) {
			var method Object

			methodName := args[0].(string)
			argCount := args[1].(int)
			argPr := vm.sp - argCount
			receiverPr := argPr - 1
			receiver := vm.stack.Data[receiverPr].Target

			switch r := receiver.(type) {
			case Class:
				method = r.lookupClassMethod(methodName)
			default:
				method = r.returnClass().lookupInstanceMethod(methodName)
			}

			if method == nil {
				vm.returnError("undefined method `" + methodName + "' for " + receiver.Inspect())
			}

			blockFrame := vm.retrieveBlock(cf, args)

			switch m := method.(type) {
			case *MethodObject:
				vm.evalMethodObject(receiver, m, receiverPr, argCount, argPr, blockFrame)
			case *BuiltInMethodObject:
				vm.evalBuiltInMethod(receiver, m, receiverPr, argCount, argPr, blockFrame)
			case *Error:
				vm.returnError(m.Inspect())
			}
		},
	},
	bytecode.InvokeBlock: {
		name: bytecode.InvokeBlock,
		operation: func(vm *VM, cf *callFrame, args ...interface{}) {
			argCount := args[0].(int)
			argPr := vm.sp - argCount
			receiverPr := argPr - 1
			receiver := vm.stack.Data[receiverPr].Target

			if cf.blockFrame == nil {
				vm.returnError("Can't yield without a block")
			}

			c := newCallFrame(cf.blockFrame.instructionSet)
			c.blockFrame = cf.blockFrame
			c.ep = cf.blockFrame.ep
			c.self = receiver

			for i := 0; i < argCount; i++ {
				c.locals[i] = vm.stack.Data[argPr+i]
			}

			vm.callFrameStack.push(c)
			vm.startFromTopFrame()

			vm.stack.Data[receiverPr] = vm.stack.top()
			vm.sp = receiverPr + 1
		},
	},
	bytecode.Leave: {
		name: bytecode.Leave,
		operation: func(vm *VM, cf *callFrame, args ...interface{}) {
			cf = vm.callFrameStack.pop()
			cf.pc = len(cf.instructionSet.instructions)
		},
	},
}

func (vm *VM) retrieveBlock(cf *callFrame, args []interface{}) (blockFrame *callFrame) {
	var blockName string
	var hasBlock bool

	if len(args) > 2 {
		hasBlock = true
		blockFlag := args[2].(string)
		blockName = strings.Split(blockFlag, ":")[1]
	} else {
		hasBlock = false
	}

	if hasBlock {
		block := vm.getBlock(blockName, cf.instructionSet.filename)

		c := newCallFrame(block)
		c.isBlock = true
		c.ep = cf
		c.self = cf.self

		vm.callFrameStack.push(c)
		blockFrame = c
	}

	return
}

func (vm *VM) evalBuiltInMethod(receiver Object, method *BuiltInMethodObject, receiverPr, argCount, argPr int, blockFrame *callFrame) {
	methodBody := method.Fn(receiver)
	args := []Object{}

	for i := 0; i < argCount; i++ {
		args = append(args, vm.stack.Data[argPr+i].Target)
	}

	evaluated := methodBody(vm, args, blockFrame)

	_, ok := receiver.(*RClass)
	if method.Name == "new" && ok {
		instance := evaluated.(*RObject)
		if instance.InitializeMethod != nil {
			vm.evalMethodObject(instance, instance.InitializeMethod, receiverPr, argCount, argPr, blockFrame)
		}
	}
	vm.stack.Data[receiverPr] = &Pointer{evaluated}
	vm.sp = receiverPr + 1
}

func (vm *VM) evalMethodObject(receiver Object, method *MethodObject, receiverPr, argC, argPr int, blockFrame *callFrame) {
	c := newCallFrame(method.instructionSet)
	c.self = receiver

	for i := 0; i < argC; i++ {
		c.insertLCL(i, 0, vm.stack.Data[argPr+i].Target)
	}

	c.blockFrame = blockFrame
	vm.callFrameStack.push(c)
	vm.startFromTopFrame()

	vm.stack.Data[receiverPr] = vm.stack.top()
	vm.sp = receiverPr + 1
}

func initializeObject(value interface{}) Object {
	switch v := value.(type) {
	case int:
		return initilaizeInteger(int(v))
	case string:
		switch v {
		case "true":
			return TRUE
		case "false":
			return FALSE
		default:
			return initializeString(v)
		}
	}

	return newError(fmt.Sprintf("Unknow data type %T", value))
}
<|endoftext|>


<commit_before>package main

import (
	"reflect"
	"regexp"
	"testing"
)

func TestNewMatcher(t *testing.T) {
	expr := "a[bc](def)"
	m, err := NewMatcher(expr)
	if err != nil {
		t.Errorf("NewMatcher(%q) returns %q, want nil",
			expr, err)
	}

	expect, err := regexp.Compile(expr)
	if err != nil {
		t.Errorf("regexp.Compile(%q) returns %q, want nil",
			expr, err)
	}
	actual := m.re
	if !reflect.DeepEqual(actual, expect) {
		t.Errorf("got %q, want %q", actual, expect)
	}
}

func TestMatch(t *testing.T) {
	expr := `\d+`
	m, err := NewMatcher(expr)
	if err != nil {
		t.Errorf("NewMatcher(%q) returns %q, want nil",
			expr, err)
	}

	src1 := "xxx123"
	expect1 := true
	actual1 := m.MatchString(src1)
	if expect1 != actual1 {
		t.Errorf("MatchString(%q) = %v, want %v",
			expr, actual1, expect1)
	}

	src2 := "xxxabc"
	expect2 := false
	actual2 := m.MatchString(src2)
	if expect2 != actual2 {
		t.Errorf("MatchString(%q) = %v, want %v",
			expr, actual2, expect2)
	}
}
<commit_msg>Add test for NewProcessor<commit_after>package main

import (
	"os/exec"
	"reflect"
	"regexp"
	"testing"
)

func TestNewMatcher(t *testing.T) {
	expr := "a[bc](def)"
	m, err := NewMatcher(expr)
	if err != nil {
		t.Errorf("NewMatcher(%q) returns %q, want nil",
			expr, err)
	}

	expect, err := regexp.Compile(expr)
	if err != nil {
		t.Errorf("regexp.Compile(%q) returns %q, want nil",
			expr, err)
	}
	actual := m.re
	if !reflect.DeepEqual(actual, expect) {
		t.Errorf("got %q, want %q", actual, expect)
	}
}

func TestMatch(t *testing.T) {
	expr := `\d+`
	m, err := NewMatcher(expr)
	if err != nil {
		t.Errorf("NewMatcher(%q) returns %q, want nil",
			expr, err)
	}

	src1 := "xxx123"
	expect1 := true
	actual1 := m.MatchString(src1)
	if expect1 != actual1 {
		t.Errorf("MatchString(%q) = %v, want %v",
			expr, actual1, expect1)
	}

	src2 := "xxxabc"
	expect2 := false
	actual2 := m.MatchString(src2)
	if expect2 != actual2 {
		t.Errorf("MatchString(%q) = %v, want %v",
			expr, actual2, expect2)
	}
}

func TestNewProcessor(t *testing.T) {
	name := "mkdir"
	p, err := NewProcessor(name)
	if err != nil {
		t.Errorf("NewMatcher(%q) returns %q, want nil",
			name, err)
	}

	expect := exec.Command(name)
	actual := p.cmd
	if !reflect.DeepEqual(actual, expect) {
		t.Errorf("NewProcessor(%q).cmd = %v, want %v",
			name, actual, expect)
	}
}
<|endoftext|>


<commit_before>package hashtree

import (
	"github.com/pachyderm/pachyderm/src/client/pfs"
)

// ErrCode identifies different kinds of errors returned by methods in
// HashTree below. The ErrCode of any such error can be retrieved with Code().
type ErrCode uint8

const (
	// OK is returned on success
	OK ErrCode = iota

	// Unknown is returned by Code() when an error wasn't emitted by the HashTree
	// implementation.
	Unknown

	// Internal is returned when a HashTree encounters a bug (usually due to the
	// violation of an internal invariant).
	Internal

	// CannotDeserialize is returned when Deserialize(bytes) fails, perhaps due to
	// 'bytes' being corrupted.
	CannotDeserialize

	// Unsupported is returned when Deserialize(bytes) encounters an unsupported
	// (likely old) serialized HashTree.
	Unsupported

	// PathNotFound is returned when Get() or DeleteFile() is called with a path
	// that doesn't lead to a node.
	PathNotFound

	// MalformedGlob is returned when Glob() is called with an invalid glob
	// pattern.
	MalformedGlob

	// PathConflict is returned when a path that is expected to point to a
	// directory in fact points to a file, or the reverse. For example:
	// 1. PutFile is called with a path that points to a directory.
	// 2. PutFile is called with a path that contains a prefix that
	//    points to a file.
	// 3. Merge is forced to merge a directory into a file
	PathConflict
)

// HashTree is the signature of a hash tree provided by this library. To get a
// new HashTree, create an OpenHashTree with NewHashTree(), modify it, and then
// call Finish() on it.
type HashTree interface {
	// Open makes a deep copy of the HashTree and returns the copy
	Open() OpenHashTree

	// Get retrieves a file.
	Get(path string) (*NodeProto, error)

	// List retrieves the list of files and subdirectories of the directory at
	// 'path'.
	List(path string) ([]*NodeProto, error)

	// Glob returns a list of files and directories that match 'pattern'.
	Glob(pattern string) ([]*NodeProto, error)

	// FSSize gets the size of the file system that this tree represents.
	// It's essentially a helper around h.Get("/").SubtreeBytes
	FSSize() int64

	// Walk calls a given function against every node in the hash tree.
	// The order of traversal is not guaranteed.  If any invocation of the
	// function returns an error, the walk stops and returns the error.
	Walk(func(path string, node *NodeProto) error) error

	// Diff returns a the diff of 2 HashTrees at particular Paths. It takes a
	// callback function f, which will be called with paths that are not
	// identical to the same path in the other HashTree.
	Diff(oldHashTree HashTree, newPath string, oldPath string, recursiveDepth int64, f func(path string, node *NodeProto, new bool) error) error
}

// OpenNode is similar to NodeProto, except that it doesn't include the Hash
// field (which is not generally meaningful in an OpenHashTree)
type OpenNode struct {
	Name string
	Size int64

	FileNode *FileNodeProto
	DirNode  *DirectoryNodeProto
}

// OpenHashTree is like HashTree, except that it can be modified. Once an
// OpenHashTree is Finish()ed, the hash and size stored with each node will be
// updated (until then, the hashes and sizes stored in an OpenHashTree will be
// stale).
type OpenHashTree interface {
	HashTree
	// GetOpen retrieves a file.
	GetOpen(path string) (*OpenNode, error)

	// PutFile appends data to a file (and creates the file if it doesn't exist).
	PutFile(path string, objects []*pfs.Object, size int64) error

	// PutDir creates a directory (or does nothing if one exists).
	PutDir(path string) error

	// DeleteFile deletes a regular file or directory (along with its children).
	DeleteFile(path string) error

	// Merge adds all of the files and directories in each tree in 'trees' into
	// this tree. If it errors this tree will be left in a undefined state and
	// should be discarded. If you'd like to be able to revert to the previous
	// state of the tree you should Finish and then Open the tree.
	Merge(trees ...HashTree) error

	// Finish makes a deep copy of the OpenHashTree, updates all of the hashes and
	// node size metadata in the copy, and returns the copy
	Finish() (HashTree, error)
}
<commit_msg>Add comment for recursiveDepth usage<commit_after>package hashtree

import (
	"github.com/pachyderm/pachyderm/src/client/pfs"
)

// ErrCode identifies different kinds of errors returned by methods in
// HashTree below. The ErrCode of any such error can be retrieved with Code().
type ErrCode uint8

const (
	// OK is returned on success
	OK ErrCode = iota

	// Unknown is returned by Code() when an error wasn't emitted by the HashTree
	// implementation.
	Unknown

	// Internal is returned when a HashTree encounters a bug (usually due to the
	// violation of an internal invariant).
	Internal

	// CannotDeserialize is returned when Deserialize(bytes) fails, perhaps due to
	// 'bytes' being corrupted.
	CannotDeserialize

	// Unsupported is returned when Deserialize(bytes) encounters an unsupported
	// (likely old) serialized HashTree.
	Unsupported

	// PathNotFound is returned when Get() or DeleteFile() is called with a path
	// that doesn't lead to a node.
	PathNotFound

	// MalformedGlob is returned when Glob() is called with an invalid glob
	// pattern.
	MalformedGlob

	// PathConflict is returned when a path that is expected to point to a
	// directory in fact points to a file, or the reverse. For example:
	// 1. PutFile is called with a path that points to a directory.
	// 2. PutFile is called with a path that contains a prefix that
	//    points to a file.
	// 3. Merge is forced to merge a directory into a file
	PathConflict
)

// HashTree is the signature of a hash tree provided by this library. To get a
// new HashTree, create an OpenHashTree with NewHashTree(), modify it, and then
// call Finish() on it.
type HashTree interface {
	// Open makes a deep copy of the HashTree and returns the copy
	Open() OpenHashTree

	// Get retrieves a file.
	Get(path string) (*NodeProto, error)

	// List retrieves the list of files and subdirectories of the directory at
	// 'path'.
	List(path string) ([]*NodeProto, error)

	// Glob returns a list of files and directories that match 'pattern'.
	Glob(pattern string) ([]*NodeProto, error)

	// FSSize gets the size of the file system that this tree represents.
	// It's essentially a helper around h.Get("/").SubtreeBytes
	FSSize() int64

	// Walk calls a given function against every node in the hash tree.
	// The order of traversal is not guaranteed.  If any invocation of the
	// function returns an error, the walk stops and returns the error.
	Walk(func(path string, node *NodeProto) error) error

	// Diff returns a the diff of 2 HashTrees at particular Paths. It takes a
	// callback function f, which will be called with paths that are not
	// identical to the same path in the other HashTree.
	// Specify '-1' for fully recursive, or '1' for shallow diff
	Diff(oldHashTree HashTree, newPath string, oldPath string, recursiveDepth int64, f func(path string, node *NodeProto, new bool) error) error
}

// OpenNode is similar to NodeProto, except that it doesn't include the Hash
// field (which is not generally meaningful in an OpenHashTree)
type OpenNode struct {
	Name string
	Size int64

	FileNode *FileNodeProto
	DirNode  *DirectoryNodeProto
}

// OpenHashTree is like HashTree, except that it can be modified. Once an
// OpenHashTree is Finish()ed, the hash and size stored with each node will be
// updated (until then, the hashes and sizes stored in an OpenHashTree will be
// stale).
type OpenHashTree interface {
	HashTree
	// GetOpen retrieves a file.
	GetOpen(path string) (*OpenNode, error)

	// PutFile appends data to a file (and creates the file if it doesn't exist).
	PutFile(path string, objects []*pfs.Object, size int64) error

	// PutDir creates a directory (or does nothing if one exists).
	PutDir(path string) error

	// DeleteFile deletes a regular file or directory (along with its children).
	DeleteFile(path string) error

	// Merge adds all of the files and directories in each tree in 'trees' into
	// this tree. If it errors this tree will be left in a undefined state and
	// should be discarded. If you'd like to be able to revert to the previous
	// state of the tree you should Finish and then Open the tree.
	Merge(trees ...HashTree) error

	// Finish makes a deep copy of the OpenHashTree, updates all of the hashes and
	// node size metadata in the copy, and returns the copy
	Finish() (HashTree, error)
}
<|endoftext|>


<commit_before>/*
// Copyright (c) 2016 Intel Corporation
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
*/

// Package qemu provides methods and types for launching and managing QEMU
// instances.  Instances can be launched with the LaunchQemu function and
// managed thereafter via QMPStart and the QMP object that this function
// returns.  To manage a qemu instance after it has been launched you need
// to pass the -qmp option during launch requesting the qemu instance to create
// a QMP unix domain manageent socket, e.g.,
// -qmp unix:/tmp/qmp-socket,server,nowait.  For more information see the
// example below.
package qemu

import (
	"bytes"
	"fmt"
	"os"
	"os/exec"
	"strings"

	"context"
)

// Device describes a device to be created by qemu.
type Device struct {
	// Type is the qemu device type
	Type string

	// ID is the user defined device ID.
	ID string

	// MemDev is the device memory identifier.
	MemDev string

	// FSDev is the device filesystem identifier.
	FSDev string

	// MountTag is the device filesystem mount point tag.
	// It is only relevant when combined with FSDev.
	MountTag string

	// CharDev is the device character device identifier.
	CharDev string
}

// Object is a qemu object representation.
type Object struct {
	// Type is the qemu object type
	Type string

	// ID is the user defined object ID.
	ID string

	// MemPath is the object's memory path.
	// This is only relevant for memory objects
	MemPath string

	// Size is the object size in bytes
	Size uint64
}

// FSDevice represents a qemu filesystem configuration.
type FSDevice struct {
	// Type is the filesystem device type (e.g. "local")
	Type string

	// ID is the filesystem identifier.
	// It should match an existing Device ID.
	ID string

	// Path is the host root path for this filesystem.
	Path string

	// SecurityModel is the security model for this filesystem device.
	SecurityModel string
}

// QMPSocket represents a qemu QMP socket configuration.
type QMPSocket struct {
	// Type is the socket type (e.g. "unix").
	Type string

	// Name is the socket name.
	Name string

	// Server tells if this is a server socket.
	Server bool

	// NoWait tells if qemu should block waiting for a client to connect.
	NoWait bool
}

// Kernel is the guest kernel configuration structure.
type Kernel struct {
	// Path is the guest kernel path on the host filesystem.
	Path string

	// Params is the kernel parameters string.
	Params string
}

// Config is the qemu configuration structure.
// It allows for passing custom settings and parameters to the qemu API.
type Config struct {
	// Path is the qemu binary path.
	Path string

	// Ctx is not used at the moment.
	Ctx context.Context

	// Name is the qemu guest name
	Name string

	// MachineType is the machine type to be used by qemu.
	MachineType string

	// MachineTypeAcceleration are the machine acceleration option to be used by qemu.
	MachineTypeAcceleration string

	// CPUModel is the CPU model to be used by qemu.
	CPUModel string

	// QMPSocket is the QMP socket description.
	QMPSocket QMPSocket

	// Devices is a list of devices for qemu to create.
	Devices []Device

	// Objects is a list of objects for qemu to create.
	Objects []Object

	// FilesystemDevices is a list of filesystem devices.
	FilesystemDevices []FSDevice

	// Kernel is the guest kernel configuration.
	Kernel Kernel

	// ExtraParams is a slice of options to pass to qemu.
	ExtraParams []string

	// FDs is a list of open file descriptors to be passed to the spawned qemu process
	FDs []*os.File
}

func appendName(params []string, config Config) []string {
	if config.Name != "" {
		params = append(params, "-name")
		params = append(params, config.Name)
	}

	return params
}

func appendMachineParams(params []string, config Config) []string {
	if config.MachineType != "" && config.MachineTypeAcceleration != "" {
		params = append(params, "-machine")
		params = append(params, fmt.Sprintf("%s,accel=%s", config.MachineType, config.MachineTypeAcceleration))
	}

	return params
}

func appendCPUModel(params []string, config Config) []string {
	if config.CPUModel != "" {
		params = append(params, "-cpu")
		params = append(params, config.CPUModel)
	}

	return params
}

func appendQMPSocket(params []string, config Config) []string {
	if config.QMPSocket.Type != "" && config.QMPSocket.Name != "" {
		var qmpParams []string

		qmpParams = append(qmpParams, fmt.Sprintf("%s:", config.QMPSocket.Type))
		qmpParams = append(qmpParams, fmt.Sprintf("%s", config.QMPSocket.Name))
		if config.QMPSocket.Server == true {
			qmpParams = append(qmpParams, ",server")
			if config.QMPSocket.NoWait == true {
				qmpParams = append(qmpParams, ",nowait")
			}
		}

		params = append(params, "-qmp")
		params = append(params, strings.Join(qmpParams, ""))
	}

	return params
}

func appendDevices(params []string, config Config) []string {
	for _, d := range config.Devices {
		if d.Type != "" {
			var deviceParams []string

			deviceParams = append(deviceParams, fmt.Sprintf("%s", d.Type))

			if d.ID != "" {
				deviceParams = append(deviceParams, fmt.Sprintf(",id=%s", d.ID))
			}

			if d.MemDev != "" {
				deviceParams = append(deviceParams, fmt.Sprintf(",memdev=%s", d.MemDev))
			}

			if d.CharDev != "" {
				deviceParams = append(deviceParams, fmt.Sprintf(",chardev=%s", d.CharDev))
			}

			if d.FSDev != "" {
				deviceParams = append(deviceParams, fmt.Sprintf(",fsdev=%s", d.FSDev))

				if d.MountTag != "" {
					deviceParams = append(deviceParams, fmt.Sprintf(",mount_tag=%s", d.MountTag))
				}
			}

			params = append(params, "-device")
			params = append(params, strings.Join(deviceParams, ""))
		}
	}

	return params
}

func appendObjects(params []string, config Config) []string {
	for _, o := range config.Objects {
		if o.Type != "" {
			var objectParams []string

			objectParams = append(objectParams, o.Type)

			if o.ID != "" {
				objectParams = append(objectParams, fmt.Sprintf(",id=%s", o.ID))
			}

			if o.MemPath != "" {
				objectParams = append(objectParams, fmt.Sprintf(",mem-path=%s", o.MemPath))
			}

			if o.Size > 0 {
				objectParams = append(objectParams, fmt.Sprintf(",size=%d", o.Size))
			}

			params = append(params, "-object")
			params = append(params, strings.Join(objectParams, ""))
		}
	}

	return params
}

func appendFilesystemDevices(params []string, config Config) []string {
	for _, f := range config.FilesystemDevices {
		if f.Type != "" {
			var fsParams []string

			fsParams = append(fsParams, fmt.Sprintf("%s", f.Type))

			if f.ID != "" {
				fsParams = append(fsParams, fmt.Sprintf(",id=%s", f.ID))
			}

			if f.Path != "" {
				fsParams = append(fsParams, fmt.Sprintf(",path=%s", f.Path))
			}

			if f.SecurityModel != "" {
				fsParams = append(fsParams, fmt.Sprintf(",security-model=%s", f.SecurityModel))
			}

			params = append(params, "-fsdev")
			params = append(params, strings.Join(fsParams, ""))
		}
	}

	return params
}

func appendKernel(params []string, config Config) []string {
	if config.Kernel.Path != "" {
		params = append(params, "-kernel")
		params = append(params, config.Kernel.Path)

		if config.Kernel.Params != "" {
			params = append(params, "-append")
			params = append(params, config.Kernel.Params)
		}
	}

	return params
}

// LaunchQemu can be used to launch a new qemu instance.
//
// The Config parameter contains a set of qemu parameters and settings.
//
// This function writes its log output via logger parameter.
//
// The function will block until the launched qemu process exits.  "", nil
// will be returned if the launch succeeds.  Otherwise a string containing
// the contents of stderr + a Go error object will be returned.
func LaunchQemu(config Config, logger QMPLog) (string, error) {
	var params []string

	params = appendName(params, config)
	params = appendMachineParams(params, config)
	params = appendCPUModel(params, config)
	params = appendQMPSocket(params, config)
	params = appendDevices(params, config)
	params = appendFilesystemDevices(params, config)
	params = appendObjects(params, config)
	params = appendKernel(params, config)

	params = append(params, config.ExtraParams...)

	return LaunchCustomQemu(config.Ctx, config.Path, params, config.FDs, logger)
}

// LaunchCustomQemu can be used to launch a new qemu instance.
//
// The path parameter is used to pass the qemu executable path.
//
// The ctx parameter is not currently used but has been added so that the
// signature of this function will not need to change when launch cancellation
// is implemented.
//
// params is a slice of options to pass to qemu-system-x86_64 and fds is a
// list of open file descriptors that are to be passed to the spawned qemu
// process.
//
// This function writes its log output via logger parameter.
//
// The function will block until the launched qemu process exits.  "", nil
// will be returned if the launch succeeds.  Otherwise a string containing
// the contents of stderr + a Go error object will be returned.
func LaunchCustomQemu(ctx context.Context, path string, params []string, fds []*os.File, logger QMPLog) (string, error) {
	if logger == nil {
		logger = qmpNullLogger{}
	}

	errStr := ""

	if path == "" {
		path = "qemu-system-x86_64"
	}

	cmd := exec.Command(path, params...)
	if len(fds) > 0 {
		logger.Infof("Adding extra file %v", fds)
		cmd.ExtraFiles = fds
	}

	var stderr bytes.Buffer
	cmd.Stderr = &stderr
	logger.Infof("launching qemu with: %v", params)

	err := cmd.Run()
	if err != nil {
		logger.Errorf("Unable to launch qemu: %v", err)
		errStr = stderr.String()
		logger.Errorf("%s", errStr)
	}
	return errStr, err
}
<commit_msg>qemu: Add a Character Devices slice field to the Config structure<commit_after>/*
// Copyright (c) 2016 Intel Corporation
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
*/

// Package qemu provides methods and types for launching and managing QEMU
// instances.  Instances can be launched with the LaunchQemu function and
// managed thereafter via QMPStart and the QMP object that this function
// returns.  To manage a qemu instance after it has been launched you need
// to pass the -qmp option during launch requesting the qemu instance to create
// a QMP unix domain manageent socket, e.g.,
// -qmp unix:/tmp/qmp-socket,server,nowait.  For more information see the
// example below.
package qemu

import (
	"bytes"
	"fmt"
	"os"
	"os/exec"
	"strings"

	"context"
)

// Device describes a device to be created by qemu.
type Device struct {
	// Type is the qemu device type
	Type string

	// ID is the user defined device ID.
	ID string

	// MemDev is the device memory identifier.
	MemDev string

	// FSDev is the device filesystem identifier.
	FSDev string

	// MountTag is the device filesystem mount point tag.
	// It is only relevant when combined with FSDev.
	MountTag string

	// CharDev is the device character device identifier.
	CharDev string
}

// Object is a qemu object representation.
type Object struct {
	// Type is the qemu object type
	Type string

	// ID is the user defined object ID.
	ID string

	// MemPath is the object's memory path.
	// This is only relevant for memory objects
	MemPath string

	// Size is the object size in bytes
	Size uint64
}

// FSDevice represents a qemu filesystem configuration.
type FSDevice struct {
	// Type is the filesystem device type (e.g. "local")
	Type string

	// ID is the filesystem identifier.
	// It should match an existing Device ID.
	ID string

	// Path is the host root path for this filesystem.
	Path string

	// SecurityModel is the security model for this filesystem device.
	SecurityModel string
}

// QMPSocket represents a qemu QMP socket configuration.
type QMPSocket struct {
	// Type is the socket type (e.g. "unix").
	Type string

	// Name is the socket name.
	Name string

	// Server tells if this is a server socket.
	Server bool

	// NoWait tells if qemu should block waiting for a client to connect.
	NoWait bool
}

// Kernel is the guest kernel configuration structure.
type Kernel struct {
	// Path is the guest kernel path on the host filesystem.
	Path string

	// Params is the kernel parameters string.
	Params string
}

// Config is the qemu configuration structure.
// It allows for passing custom settings and parameters to the qemu API.
type Config struct {
	// Path is the qemu binary path.
	Path string

	// Ctx is not used at the moment.
	Ctx context.Context

	// Name is the qemu guest name
	Name string

	// MachineType is the machine type to be used by qemu.
	MachineType string

	// MachineTypeAcceleration are the machine acceleration option to be used by qemu.
	MachineTypeAcceleration string

	// CPUModel is the CPU model to be used by qemu.
	CPUModel string

	// QMPSocket is the QMP socket description.
	QMPSocket QMPSocket

	// Devices is a list of devices for qemu to create.
	Devices []Device

	// CharDevices is a list of character devices for qemu to export.
	CharDevices []string

	// Objects is a list of objects for qemu to create.
	Objects []Object

	// FilesystemDevices is a list of filesystem devices.
	FilesystemDevices []FSDevice

	// Kernel is the guest kernel configuration.
	Kernel Kernel

	// ExtraParams is a slice of options to pass to qemu.
	ExtraParams []string

	// FDs is a list of open file descriptors to be passed to the spawned qemu process
	FDs []*os.File
}

func appendName(params []string, config Config) []string {
	if config.Name != "" {
		params = append(params, "-name")
		params = append(params, config.Name)
	}

	return params
}

func appendMachineParams(params []string, config Config) []string {
	if config.MachineType != "" && config.MachineTypeAcceleration != "" {
		params = append(params, "-machine")
		params = append(params, fmt.Sprintf("%s,accel=%s", config.MachineType, config.MachineTypeAcceleration))
	}

	return params
}

func appendCPUModel(params []string, config Config) []string {
	if config.CPUModel != "" {
		params = append(params, "-cpu")
		params = append(params, config.CPUModel)
	}

	return params
}

func appendQMPSocket(params []string, config Config) []string {
	if config.QMPSocket.Type != "" && config.QMPSocket.Name != "" {
		var qmpParams []string

		qmpParams = append(qmpParams, fmt.Sprintf("%s:", config.QMPSocket.Type))
		qmpParams = append(qmpParams, fmt.Sprintf("%s", config.QMPSocket.Name))
		if config.QMPSocket.Server == true {
			qmpParams = append(qmpParams, ",server")
			if config.QMPSocket.NoWait == true {
				qmpParams = append(qmpParams, ",nowait")
			}
		}

		params = append(params, "-qmp")
		params = append(params, strings.Join(qmpParams, ""))
	}

	return params
}

func appendDevices(params []string, config Config) []string {
	for _, d := range config.Devices {
		if d.Type != "" {
			var deviceParams []string

			deviceParams = append(deviceParams, fmt.Sprintf("%s", d.Type))

			if d.ID != "" {
				deviceParams = append(deviceParams, fmt.Sprintf(",id=%s", d.ID))
			}

			if d.MemDev != "" {
				deviceParams = append(deviceParams, fmt.Sprintf(",memdev=%s", d.MemDev))
			}

			if d.CharDev != "" {
				deviceParams = append(deviceParams, fmt.Sprintf(",chardev=%s", d.CharDev))
			}

			if d.FSDev != "" {
				deviceParams = append(deviceParams, fmt.Sprintf(",fsdev=%s", d.FSDev))

				if d.MountTag != "" {
					deviceParams = append(deviceParams, fmt.Sprintf(",mount_tag=%s", d.MountTag))
				}
			}

			params = append(params, "-device")
			params = append(params, strings.Join(deviceParams, ""))
		}
	}

	return params
}

func appendCharDevices(params []string, config Config) []string {
	for _, c := range config.CharDevices {
		params = append(params, "-chardev")
		params = append(params, c)
	}

	return params
}

func appendObjects(params []string, config Config) []string {
	for _, o := range config.Objects {
		if o.Type != "" {
			var objectParams []string

			objectParams = append(objectParams, o.Type)

			if o.ID != "" {
				objectParams = append(objectParams, fmt.Sprintf(",id=%s", o.ID))
			}

			if o.MemPath != "" {
				objectParams = append(objectParams, fmt.Sprintf(",mem-path=%s", o.MemPath))
			}

			if o.Size > 0 {
				objectParams = append(objectParams, fmt.Sprintf(",size=%d", o.Size))
			}

			params = append(params, "-object")
			params = append(params, strings.Join(objectParams, ""))
		}
	}

	return params
}

func appendFilesystemDevices(params []string, config Config) []string {
	for _, f := range config.FilesystemDevices {
		if f.Type != "" {
			var fsParams []string

			fsParams = append(fsParams, fmt.Sprintf("%s", f.Type))

			if f.ID != "" {
				fsParams = append(fsParams, fmt.Sprintf(",id=%s", f.ID))
			}

			if f.Path != "" {
				fsParams = append(fsParams, fmt.Sprintf(",path=%s", f.Path))
			}

			if f.SecurityModel != "" {
				fsParams = append(fsParams, fmt.Sprintf(",security-model=%s", f.SecurityModel))
			}

			params = append(params, "-fsdev")
			params = append(params, strings.Join(fsParams, ""))
		}
	}

	return params
}

func appendKernel(params []string, config Config) []string {
	if config.Kernel.Path != "" {
		params = append(params, "-kernel")
		params = append(params, config.Kernel.Path)

		if config.Kernel.Params != "" {
			params = append(params, "-append")
			params = append(params, config.Kernel.Params)
		}
	}

	return params
}

// LaunchQemu can be used to launch a new qemu instance.
//
// The Config parameter contains a set of qemu parameters and settings.
//
// This function writes its log output via logger parameter.
//
// The function will block until the launched qemu process exits.  "", nil
// will be returned if the launch succeeds.  Otherwise a string containing
// the contents of stderr + a Go error object will be returned.
func LaunchQemu(config Config, logger QMPLog) (string, error) {
	var params []string

	params = appendName(params, config)
	params = appendMachineParams(params, config)
	params = appendCPUModel(params, config)
	params = appendQMPSocket(params, config)
	params = appendDevices(params, config)
	params = appendCharDevices(params, config)
	params = appendFilesystemDevices(params, config)
	params = appendObjects(params, config)
	params = appendKernel(params, config)

	params = append(params, config.ExtraParams...)

	return LaunchCustomQemu(config.Ctx, config.Path, params, config.FDs, logger)
}

// LaunchCustomQemu can be used to launch a new qemu instance.
//
// The path parameter is used to pass the qemu executable path.
//
// The ctx parameter is not currently used but has been added so that the
// signature of this function will not need to change when launch cancellation
// is implemented.
//
// params is a slice of options to pass to qemu-system-x86_64 and fds is a
// list of open file descriptors that are to be passed to the spawned qemu
// process.
//
// This function writes its log output via logger parameter.
//
// The function will block until the launched qemu process exits.  "", nil
// will be returned if the launch succeeds.  Otherwise a string containing
// the contents of stderr + a Go error object will be returned.
func LaunchCustomQemu(ctx context.Context, path string, params []string, fds []*os.File, logger QMPLog) (string, error) {
	if logger == nil {
		logger = qmpNullLogger{}
	}

	errStr := ""

	if path == "" {
		path = "qemu-system-x86_64"
	}

	cmd := exec.Command(path, params...)
	if len(fds) > 0 {
		logger.Infof("Adding extra file %v", fds)
		cmd.ExtraFiles = fds
	}

	var stderr bytes.Buffer
	cmd.Stderr = &stderr
	logger.Infof("launching qemu with: %v", params)

	err := cmd.Run()
	if err != nil {
		logger.Errorf("Unable to launch qemu: %v", err)
		errStr = stderr.String()
		logger.Errorf("%s", errStr)
	}
	return errStr, err
}
<|endoftext|>


<commit_before>package libkbfs

import (
	"crypto/tls"
	"crypto/x509"
	"encoding/base64"
	"errors"
	"fmt"
	libkb "github.com/keybase/client/go/libkb"
	"github.com/keybase/client/protocol/go"
	"github.com/maxtaco/go-framed-msgpack-rpc/rpc2"
	"io/ioutil"
	"net"
	"sync"
	"time"
)

var (
	ErrNoActiveBlockConn  = errors.New("Not connected to block server")
	ErrBlockConnTimeout   = errors.New("Repeatedly failed to connect to block server")
	ErrNoActiveBIndexConn = errors.New("Not connected to bindex server")
	ErrBIndexConnTimeout  = errors.New("Repeatedly failed to connect to bindex server")
	TIMEOUT               = 60 * time.Second
)

type Connectable struct {
	srv_addr   string
	conn       net.Conn
	connected  bool
	last_tried time.Time
	retry_mu   sync.Mutex
	kbpki      KBPKI
}

type BlockClt struct {
	clt keybase_1.BlockClient
	Connectable
}

type BIndexClt struct {
	clt keybase_1.BIndexClient
	Connectable
}

type BlockServerRemote struct {
	blockly  BlockClt
	bindexly BIndexClt
}

func TLSConnect(srv_addr string) (conn net.Conn, err error) {
	CA_Pool := x509.NewCertPool()
	var cacert []byte
	cacert, err = ioutil.ReadFile("./cacert.pem")
	if err != nil {
		return
	}
	CA_Pool.AppendCertsFromPEM(cacert)

	config := tls.Config{RootCAs: CA_Pool}
	conn, err = tls.Dial("tcp", srv_addr, &config)
	if err != nil {
		return
	}
	return
}

func TCPConnect(srvaddr string) (net.Conn, error) {
	return net.Dial("tcp", srvaddr)
}

func (c *Connectable) ConnectOnce() (err error) {
	c.conn, err = TLSConnect(c.srv_addr)
	return
}

func (c *BlockClt) ConnectOnce() error {
	err := c.Connectable.ConnectOnce()
	if err != nil {
		return err
	}
	c.clt = keybase_1.BlockClient{rpc2.NewClient(
		rpc2.NewTransport(c.conn, libkb.NewRpcLogFactory(), libkb.WrapError), libkb.UnwrapError)}

	session, err := c.kbpki.GetSession()
	if err == nil {
		err = c.clt.BlockSession(session.GetToken())
		if err == nil {
			c.connected = true
			return nil
		}
	}
	c.conn.Close() //failed to announce session, close the whole thing
	return err
}

func (c *BIndexClt) ConnectOnce() error {
	err := c.Connectable.ConnectOnce()
	if err != nil {
		return err
	}
	c.clt = keybase_1.BIndexClient{rpc2.NewClient(
		rpc2.NewTransport(c.conn, libkb.NewRpcLogFactory(), libkb.WrapError), libkb.UnwrapError)}

	session, err := c.kbpki.GetSession()
	if err == nil {
		err = c.clt.BIndexSession(session.GetToken())
		if err == nil {
			c.connected = true
			return nil
		}
	}
	c.conn.Close() //failed to announce session, close the whole thing
	return err
}

func NewBlockServerRemote(blk_srvaddr string, bind_srvaddr string, kbpki KBPKI) *BlockServerRemote {
	b := &BlockServerRemote{}

	var err error
	b.blockly.srv_addr = blk_srvaddr
	b.blockly.kbpki = kbpki
	if err = b.blockly.ConnectOnce(); err != nil {
		go b.blockly.Reconnect()
	}

	b.bindexly.srv_addr = bind_srvaddr
	b.bindexly.kbpki = kbpki
	if err = b.bindexly.ConnectOnce(); err != nil {
		go b.bindexly.Reconnect()
	}

	return b
}

func (b *Connectable) WaitForReconnect() error {
	timeout := time.Now().Add(TIMEOUT)

	b.retry_mu.Lock()
	defer b.retry_mu.Unlock()

	for !b.connected {
		b.retry_mu.Unlock()
		if time.Now().After(timeout) {
			return ErrBlockConnTimeout
		}
		time.Sleep(1 * time.Second)
		b.retry_mu.Lock()
	}
	return nil
}

func (b *BlockClt) Reconnect() {
	b.retry_mu.Lock()
	defer b.retry_mu.Unlock()

	for b.ConnectOnce() != nil {
		b.retry_mu.Unlock()
		time.Sleep(1 * time.Second)
		b.retry_mu.Lock()
	}
	return
}

func (b *BIndexClt) Reconnect() {
	b.retry_mu.Lock()
	defer b.retry_mu.Unlock()

	for b.ConnectOnce() != nil {
		b.retry_mu.Unlock()
		time.Sleep(1 * time.Second)
		b.retry_mu.Lock()
	}
	return
}

func (b *BlockServerRemote) Shutdown() {
	b.blockly.conn.Close()
	b.bindexly.conn.Close()
}

func (b *BlockServerRemote) Get(id BlockId, context BlockContext) ([]byte, error) {
	if !b.blockly.connected {
		if err := b.blockly.WaitForReconnect(); err != nil {
			return nil, err
		}
	}
	//XXX: if fails due to connection problem, should reconnect
	arg := keybase_1.GetBlockArg{
		BlockId: base64.StdEncoding.EncodeToString(id[:]),
		Size:    0,
	}
	if buf, err := b.blockly.clt.GetBlock(arg); err != nil {
		return nil, err
	} else {
		return buf, err
	}
	//XXX: need to fetch the block key
}

func (b *BlockServerRemote) Put(id BlockId, context BlockContext, buf []byte) error {
	if !b.blockly.connected {
		if err := b.blockly.WaitForReconnect(); err != nil {
			return err
		}
	}
	if !b.bindexly.connected {
		if err := b.bindexly.WaitForReconnect(); err != nil {
			return err
		}
	}

	arg := keybase_1.PutBlockArg{
		BlockId: base64.StdEncoding.EncodeToString(id[:]),
		Buf:     buf,
	}
	if err := b.blockly.clt.PutBlock(arg); err != nil {
		fmt.Printf("PUT err is %v\n", err)
		return err
	} else {
		return err
	}
	return nil

}

func (b *BlockServerRemote) Delete(id BlockId, context BlockContext) error {
	/*
		if err := b.blockly.clt.blockSession(); err != nil {
			return err
		}
			arg := keybase_1.DeleteArg{
				Blockid: id[:],
				Uid:     keybase_1.UID(context.GetWriter()),
			}
				if err := b.blockly.clt.Delete(arg); err != nil {
					fmt.Printf("DEL err %v\n", err)
					return err
				} else {
					return nil
				}
	*/
	return nil
}
<commit_msg>fix compilation error<commit_after>package libkbfs

import (
	"crypto/tls"
	"crypto/x509"
	"encoding/base64"
	"errors"
	"fmt"
	libkb "github.com/keybase/client/go/libkb"
	"github.com/keybase/client/protocol/go"
	"github.com/maxtaco/go-framed-msgpack-rpc/rpc2"
	"io/ioutil"
	"net"
	"sync"
	"time"
)

var (
	ErrNoActiveBlockConn  = errors.New("Not connected to block server")
	ErrBlockConnTimeout   = errors.New("Repeatedly failed to connect to block server")
	ErrNoActiveBIndexConn = errors.New("Not connected to bindex server")
	ErrBIndexConnTimeout  = errors.New("Repeatedly failed to connect to bindex server")
	TIMEOUT               = 60 * time.Second
)

type Connectable struct {
	srv_addr   string
	conn       net.Conn
	connected  bool
	last_tried time.Time
	retry_mu   sync.Mutex
	kbpki      KBPKI
}

type BlockClt struct {
	clt keybase_1.BlockClient
	Connectable
}

type BIndexClt struct {
	clt keybase_1.BIndexClient
	Connectable
}

type BlockServerRemote struct {
	blockly  BlockClt
	bindexly BIndexClt
}

func TLSConnect(srv_addr string) (conn net.Conn, err error) {
	CA_Pool := x509.NewCertPool()
	var cacert []byte
	cacert, err = ioutil.ReadFile("./cacert.pem")
	if err != nil {
		return
	}
	CA_Pool.AppendCertsFromPEM(cacert)

	config := tls.Config{RootCAs: CA_Pool}
	conn, err = tls.Dial("tcp", srv_addr, &config)
	if err != nil {
		return
	}
	return
}

func TCPConnect(srvaddr string) (net.Conn, error) {
	return net.Dial("tcp", srvaddr)
}

func (c *Connectable) ConnectOnce() (err error) {
	c.conn, err = TLSConnect(c.srv_addr)
	return
}

func (c *BlockClt) ConnectOnce() error {
	err := c.Connectable.ConnectOnce()
	if err != nil {
		return err
	}
	c.clt = keybase_1.BlockClient{rpc2.NewClient(
		rpc2.NewTransport(c.conn, libkb.NewRpcLogFactory(), libkb.WrapError), libkb.UnwrapError)}

	session, err := c.kbpki.GetSession()
	if err == nil {
		err = c.clt.BlockSession(session.GetToken())
		if err == nil {
			c.connected = true
			return nil
		}
	}
	c.conn.Close() //failed to announce session, close the whole thing
	return err
}

func (c *BIndexClt) ConnectOnce() error {
	err := c.Connectable.ConnectOnce()
	if err != nil {
		return err
	}
	c.clt = keybase_1.BIndexClient{rpc2.NewClient(
		rpc2.NewTransport(c.conn, libkb.NewRpcLogFactory(), libkb.WrapError), libkb.UnwrapError)}

	session, err := c.kbpki.GetSession()
	if err == nil {
		err = c.clt.BIndexSession(session.GetToken())
		if err == nil {
			c.connected = true
			return nil
		}
	}
	c.conn.Close() //failed to announce session, close the whole thing
	return err
}

func NewBlockServerRemote(blk_srvaddr string, bind_srvaddr string, kbpki KBPKI) *BlockServerRemote {
	b := &BlockServerRemote{}

	var err error
	b.blockly.srv_addr = blk_srvaddr
	b.blockly.kbpki = kbpki
	if err = b.blockly.ConnectOnce(); err != nil {
		go b.blockly.Reconnect()
	}

	b.bindexly.srv_addr = bind_srvaddr
	b.bindexly.kbpki = kbpki
	if err = b.bindexly.ConnectOnce(); err != nil {
		go b.bindexly.Reconnect()
	}

	return b
}

func (b *Connectable) WaitForReconnect() error {
	timeout := time.Now().Add(TIMEOUT)

	b.retry_mu.Lock()
	defer b.retry_mu.Unlock()

	for !b.connected {
		b.retry_mu.Unlock()
		if time.Now().After(timeout) {
			return ErrBlockConnTimeout
		}
		time.Sleep(1 * time.Second)
		b.retry_mu.Lock()
	}
	return nil
}

func (b *BlockClt) Reconnect() {
	b.retry_mu.Lock()
	defer b.retry_mu.Unlock()

	for b.ConnectOnce() != nil {
		b.retry_mu.Unlock()
		time.Sleep(1 * time.Second)
		b.retry_mu.Lock()
	}
	return
}

func (b *BIndexClt) Reconnect() {
	b.retry_mu.Lock()
	defer b.retry_mu.Unlock()

	for b.ConnectOnce() != nil {
		b.retry_mu.Unlock()
		time.Sleep(1 * time.Second)
		b.retry_mu.Lock()
	}
	return
}

func (b *BlockServerRemote) Shutdown() {
	b.blockly.conn.Close()
	b.bindexly.conn.Close()
}

func (b *BlockServerRemote) Get(id BlockId, context BlockContext) ([]byte, error) {
	if !b.blockly.connected {
		if err := b.blockly.WaitForReconnect(); err != nil {
			return nil, err
		}
	}
	//XXX: if fails due to connection problem, should reconnect
	bid := keybase_1.BlockIdCombo{
		BlockId: base64.StdEncoding.EncodeToString(id[:]),
		Size:    0,
	}

	if buf, err := b.blockly.clt.GetBlock(bid); err != nil {
		return nil, err
	} else {
		return buf, err
	}
	//XXX: need to fetch the block key
}

func (b *BlockServerRemote) Put(id BlockId, context BlockContext, buf []byte) error {
	if !b.blockly.connected {
		if err := b.blockly.WaitForReconnect(); err != nil {
			return err
		}
	}
	if !b.bindexly.connected {
		if err := b.bindexly.WaitForReconnect(); err != nil {
			return err
		}
	}

	arg := keybase_1.PutBlockArg{
		Bid: keybase_1.BlockIdCombo{BlockId: base64.StdEncoding.EncodeToString(id[:]), Size: len(buf)},
		Buf: buf,
	}
	if err := b.blockly.clt.PutBlock(arg); err != nil {
		fmt.Printf("PUT err is %v\n", err)
		return err
	} else {
		return err
	}
	return nil

}

func (b *BlockServerRemote) Delete(id BlockId, context BlockContext) error {
	/*
		if err := b.blockly.clt.blockSession(); err != nil {
			return err
		}
			arg := keybase_1.DeleteArg{
				Blockid: id[:],
				Uid:     keybase_1.UID(context.GetWriter()),
			}
				if err := b.blockly.clt.Delete(arg); err != nil {
					fmt.Printf("DEL err %v\n", err)
					return err
				} else {
					return nil
				}
	*/
	return nil
}
<|endoftext|>


<commit_before>package client

import (
	"bufio"
	"fmt"
	"regexp"
	"strconv"
	"time"

	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	kapi "k8s.io/kubernetes/pkg/api"
	kclientset "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset"

	osclientcmd "github.com/openshift/origin/pkg/cmd/util/clientcmd"
	"github.com/openshift/origin/pkg/cmd/util/variable"
	"github.com/openshift/origin/pkg/diagnostics/types"
)

const (
	DiagnosticPodName = "DiagnosticPod"
)

// DiagnosticPod is a diagnostic that runs a diagnostic pod and relays the results.
type DiagnosticPod struct {
	KubeClient          kclientset.Interface
	Namespace           string
	Level               int
	Factory             *osclientcmd.Factory
	PreventModification bool
	ImageTemplate       variable.ImageTemplate
}

// Name is part of the Diagnostic interface and just returns name.
func (d *DiagnosticPod) Name() string {
	return DiagnosticPodName
}

// Description is part of the Diagnostic interface and provides a user-focused description of what the diagnostic does.
func (d *DiagnosticPod) Description() string {
	return "Create a pod to run diagnostics from the application standpoint"
}

// CanRun is part of the Diagnostic interface; it determines if the conditions are right to run this diagnostic.
func (d *DiagnosticPod) CanRun() (bool, error) {
	if d.PreventModification {
		return false, fmt.Errorf("running the diagnostic pod is an API change, which is prevented as you indicated")
	}
	return true, nil
}

// Check is part of the Diagnostic interface; it runs the actual diagnostic logic
func (d *DiagnosticPod) Check() types.DiagnosticResult {
	r := types.NewDiagnosticResult("DiagnosticPod")
	d.runDiagnosticPod(nil, r)
	return r
}

func (d *DiagnosticPod) runDiagnosticPod(service *kapi.Service, r types.DiagnosticResult) {
	loglevel := d.Level
	if loglevel > 2 {
		loglevel = 2 // need to show summary at least
	}
	imageName := d.ImageTemplate.ExpandOrDie("deployer")
	pod, err := d.KubeClient.Core().Pods(d.Namespace).Create(&kapi.Pod{
		ObjectMeta: metav1.ObjectMeta{GenerateName: "pod-diagnostic-test-"},
		Spec: kapi.PodSpec{
			RestartPolicy: kapi.RestartPolicyNever,
			Containers: []kapi.Container{
				{
					Name:    "pod-diagnostics",
					Image:   imageName,
					Command: []string{"openshift", "infra", "diagnostic-pod", "-l", strconv.Itoa(loglevel)},
				},
			},
		},
	})
	if err != nil {
		r.Error("DCli2001", err, fmt.Sprintf("Creating diagnostic pod with image %s failed. Error: (%[2]T) %[2]v", imageName, err))
		return
	}
	defer func() { // delete what we created, or notify that we couldn't
		zero := int64(0)
		delOpts := metav1.DeleteOptions{TypeMeta: pod.TypeMeta, GracePeriodSeconds: &zero}
		if err := d.KubeClient.Core().Pods(d.Namespace).Delete(pod.ObjectMeta.Name, &delOpts); err != nil {
			r.Error("DCl2002", err, fmt.Sprintf("Deleting diagnostic pod '%s' failed. Error: %s", pod.ObjectMeta.Name, fmt.Sprintf("(%T) %[1]s", err)))
		}
	}()
	pod, err = d.KubeClient.Core().Pods(d.Namespace).Get(pod.ObjectMeta.Name, metav1.GetOptions{}) // status is filled in post-create
	if err != nil {
		r.Error("DCli2003", err, fmt.Sprintf("Retrieving the diagnostic pod definition failed. Error: (%T) %[1]v", err))
		return
	}
	r.Debug("DCli2004", fmt.Sprintf("Created diagnostic pod named %v running image %s.", pod.ObjectMeta.Name, imageName))

	bytelim := int64(1024000)
	podLogsOpts := &kapi.PodLogOptions{
		TypeMeta:   pod.TypeMeta,
		Container:  "pod-diagnostics",
		Follow:     true,
		LimitBytes: &bytelim,
	}
	req, err := d.Factory.LogsForObject(pod, podLogsOpts, 1*time.Minute)
	if err != nil {
		r.Error("DCli2005", err, fmt.Sprintf("The request for diagnostic pod logs failed unexpectedly. Error: (%T) %[1]v", err))
		return
	}

	// wait for pod to be started and logs available
	var scanner *bufio.Scanner
	var lastError error
	for times := 1; true; times++ {
		if times <= 50 {
			readCloser, err := req.Stream()
			if err != nil {
				lastError = err
				r.Debug("DCli2010", fmt.Sprintf("Could not get diagnostic pod logs (loop %d): (%T[2]) %[2]v", times, err))
				time.Sleep(time.Duration(times*100) * time.Millisecond)
				continue
			}
			defer readCloser.Close()
			// make sure we can actually get something from the stream before going on.
			// it seems the creation of docker logs can trail the container start a bit.
			lineScanner := bufio.NewScanner(readCloser)
			if lineScanner.Scan() {
				scanner = lineScanner
				break // success - drop down to reading the logs.
			}
			// no luck - try, try again
			lastError = fmt.Errorf("Diagnostics pod is ready but not its logs (loop %d). Retry.", times)
			r.Debug("DCli2010", lastError.Error())
			time.Sleep(time.Duration(times*100) * time.Millisecond)
			continue
		}
		// tries exhausted
		r.Warn("DCli2006", err, fmt.Sprintf("Timed out preparing diagnostic pod logs for streaming, so this diagnostic cannot run.\nIt is likely that the image '%s' was not pulled and running yet.\nLast error: (%T[2]) %[2]v", pod.Spec.Containers[0].Image, lastError))
		return
	}
	// then watch logs and wait until it exits
	podLogs, warnings, errors := "", 0, 0
	errorRegex := regexp.MustCompile(`^\[Note\]\s+Errors\s+seen:\s+(\d+)`)
	warnRegex := regexp.MustCompile(`^\[Note\]\s+Warnings\s+seen:\s+(\d+)`)
	// keep in mind one test line was already scanned, so scan after the loop runs once
	for scanned := true; scanned; scanned = scanner.Scan() {
		line := scanner.Text()
		podLogs += line + "\n"
		if matches := errorRegex.FindStringSubmatch(line); matches != nil {
			errors, _ = strconv.Atoi(matches[1])
		} else if matches := warnRegex.FindStringSubmatch(line); matches != nil {
			warnings, _ = strconv.Atoi(matches[1])
		}
	}
	if err := scanner.Err(); err != nil { // Scan terminated abnormally
		r.Error("DCli2009", err, fmt.Sprintf("Unexpected error reading diagnostic pod logs: (%T) %[1]v\nLogs are:\n%[2]s", err, podLogs))
	} else {
		if errors > 0 {
			r.Error("DCli2012", nil, "See the errors below in the output from the diagnostic pod:\n"+podLogs)
		} else if warnings > 0 {
			r.Warn("DCli2013", nil, "See the warnings below in the output from the diagnostic pod:\n"+podLogs)
		} else {
			r.Info("DCli2008", fmt.Sprintf("Output from the diagnostic pod (image %s):\n", imageName)+podLogs)
		}
	}
}
<commit_msg>DiagnosticPod: handle interrupt<commit_after>package client

import (
	"bufio"
	"fmt"
	"os"
	"os/signal"
	"regexp"
	"strconv"
	"syscall"
	"time"

	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	kapi "k8s.io/kubernetes/pkg/api"
	kclientset "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset"

	osclientcmd "github.com/openshift/origin/pkg/cmd/util/clientcmd"
	"github.com/openshift/origin/pkg/cmd/util/variable"
	"github.com/openshift/origin/pkg/diagnostics/types"
)

const (
	DiagnosticPodName = "DiagnosticPod"
)

// DiagnosticPod is a diagnostic that runs a diagnostic pod and relays the results.
type DiagnosticPod struct {
	KubeClient          kclientset.Interface
	Namespace           string
	Level               int
	Factory             *osclientcmd.Factory
	PreventModification bool
	ImageTemplate       variable.ImageTemplate
}

// Name is part of the Diagnostic interface and just returns name.
func (d *DiagnosticPod) Name() string {
	return DiagnosticPodName
}

// Description is part of the Diagnostic interface and provides a user-focused description of what the diagnostic does.
func (d *DiagnosticPod) Description() string {
	return "Create a pod to run diagnostics from the application standpoint"
}

// CanRun is part of the Diagnostic interface; it determines if the conditions are right to run this diagnostic.
func (d *DiagnosticPod) CanRun() (bool, error) {
	if d.PreventModification {
		return false, fmt.Errorf("running the diagnostic pod is an API change, which is prevented as you indicated")
	}
	return true, nil
}

// Check is part of the Diagnostic interface; it runs the actual diagnostic logic
func (d *DiagnosticPod) Check() types.DiagnosticResult {
	r := types.NewDiagnosticResult("DiagnosticPod")
	d.runDiagnosticPod(r)
	return r
}

func (d *DiagnosticPod) runDiagnosticPod(r types.DiagnosticResult) {
	loglevel := d.Level
	if loglevel > 2 {
		loglevel = 2 // need to show summary at least
	}
	imageName := d.ImageTemplate.ExpandOrDie("deployer")
	pod, err := d.KubeClient.Core().Pods(d.Namespace).Create(&kapi.Pod{
		ObjectMeta: metav1.ObjectMeta{GenerateName: "pod-diagnostic-test-"},
		Spec: kapi.PodSpec{
			RestartPolicy: kapi.RestartPolicyNever,
			Containers: []kapi.Container{
				{
					Name:    "pod-diagnostics",
					Image:   imageName,
					Command: []string{"openshift", "infra", "diagnostic-pod", "-l", strconv.Itoa(loglevel)},
				},
			},
		},
	})
	if err != nil {
		r.Error("DCli2001", err, fmt.Sprintf("Creating diagnostic pod with image %s failed. Error: (%[2]T) %[2]v", imageName, err))
		return
	}

	// Jump straight to clean up if there is an interrupt/terminate signal while running diagnostic
	done := make(chan bool, 1)
	sig := make(chan os.Signal, 1)
	signal.Notify(sig, os.Interrupt, syscall.SIGTERM)
	go func() {
		<-sig
		r.Warn("DCli2014", nil, "Interrupt received; aborting diagnostic.")
		done <- true
	}()
	go func() {
		d.processDiagnosticPodResults(pod, imageName, r)
		done <- true
	}()

	<-done
	signal.Stop(sig)
	// delete what we created, or notify that we couldn't
	zero := int64(0)
	delOpts := metav1.DeleteOptions{TypeMeta: pod.TypeMeta, GracePeriodSeconds: &zero}
	if err := d.KubeClient.Core().Pods(d.Namespace).Delete(pod.ObjectMeta.Name, &delOpts); err != nil {
		r.Error("DCl2002", err, fmt.Sprintf("Deleting diagnostic pod '%s' failed. Error: %s", pod.ObjectMeta.Name, fmt.Sprintf("(%T) %[1]s", err)))
	}
}

func (d *DiagnosticPod) processDiagnosticPodResults(protoPod *kapi.Pod, imageName string, r types.DiagnosticResult) {
	pod, err := d.KubeClient.Core().Pods(d.Namespace).Get(protoPod.ObjectMeta.Name, metav1.GetOptions{}) // status is filled in post-create
	if err != nil {
		r.Error("DCli2003", err, fmt.Sprintf("Retrieving the diagnostic pod definition failed. Error: (%T) %[1]v", err))
		return
	}
	r.Debug("DCli2004", fmt.Sprintf("Created diagnostic pod named %v running image %s.", pod.ObjectMeta.Name, imageName))

	bytelim := int64(1024000)
	podLogsOpts := &kapi.PodLogOptions{
		TypeMeta:   pod.TypeMeta,
		Container:  "pod-diagnostics",
		Follow:     true,
		LimitBytes: &bytelim,
	}
	req, err := d.Factory.LogsForObject(pod, podLogsOpts, 1*time.Minute)
	if err != nil {
		r.Error("DCli2005", err, fmt.Sprintf("The request for diagnostic pod logs failed unexpectedly. Error: (%T) %[1]v", err))
		return
	}

	// wait for pod to be started and logs available
	var scanner *bufio.Scanner
	var lastError error
	for times := 1; true; times++ {
		if times <= 50 {
			readCloser, err := req.Stream()
			if err != nil {
				lastError = err
				r.Debug("DCli2010", fmt.Sprintf("Could not get diagnostic pod logs (loop %d): (%T[2]) %[2]v", times, err))
				time.Sleep(time.Duration(times*100) * time.Millisecond)
				continue
			}
			defer readCloser.Close()
			// make sure we can actually get something from the stream before going on.
			// it seems the creation of docker logs can trail the container start a bit.
			lineScanner := bufio.NewScanner(readCloser)
			if lineScanner.Scan() {
				scanner = lineScanner
				break // success - drop down to reading the logs.
			}
			// no luck - try, try again
			lastError = fmt.Errorf("Diagnostics pod is ready but not its logs (loop %d). Retry.", times)
			r.Debug("DCli2010", lastError.Error())
			time.Sleep(time.Duration(times*100) * time.Millisecond)
			continue
		}
		// tries exhausted
		r.Warn("DCli2006", err, fmt.Sprintf("Timed out preparing diagnostic pod logs for streaming, so this diagnostic cannot run.\nIt is likely that the image '%s' was not pulled and running yet.\nLast error: (%T[2]) %[2]v", pod.Spec.Containers[0].Image, lastError))
		return
	}
	// then watch logs and wait until it exits
	podLogs, warnings, errors := "", 0, 0
	errorRegex := regexp.MustCompile(`^\[Note\]\s+Errors\s+seen:\s+(\d+)`)
	warnRegex := regexp.MustCompile(`^\[Note\]\s+Warnings\s+seen:\s+(\d+)`)
	// keep in mind one test line was already scanned, so scan after the loop runs once
	for scanned := true; scanned; scanned = scanner.Scan() {
		line := scanner.Text()
		podLogs += line + "\n"
		if matches := errorRegex.FindStringSubmatch(line); matches != nil {
			errors, _ = strconv.Atoi(matches[1])
		} else if matches := warnRegex.FindStringSubmatch(line); matches != nil {
			warnings, _ = strconv.Atoi(matches[1])
		}
	}
	if err := scanner.Err(); err != nil { // Scan terminated abnormally
		r.Error("DCli2009", err, fmt.Sprintf("Unexpected error reading diagnostic pod logs: (%T) %[1]v\nLogs are:\n%[2]s", err, podLogs))
	} else {
		if errors > 0 {
			r.Error("DCli2012", nil, "See the errors below in the output from the diagnostic pod:\n"+podLogs)
		} else if warnings > 0 {
			r.Warn("DCli2013", nil, "See the warnings below in the output from the diagnostic pod:\n"+podLogs)
		} else {
			r.Info("DCli2008", fmt.Sprintf("Output from the diagnostic pod (image %s):\n", imageName)+podLogs)
		}
	}
}
<|endoftext|>