<commit_before>/*
Copyright 2014 Workiva, LLC

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 augmentedtree

func intervalOverlaps(n *node, low, high int64, interval Interval, maxDimension uint64) bool {
	if !overlaps(n.interval.HighAtDimension(1), high, n.interval.LowAtDimension(1), low) {
		return false
	}

	if interval == nil {
		return true
	}

	for i := uint64(2); i <= maxDimension; i++ {
		if !n.interval.OverlapsAtDimension(interval, i) {
			return false
		}
	}

	return true
}

func overlaps(high, otherHigh, low, otherLow int64) bool {
	return high >= otherLow && low <= otherHigh
}

// compare returns an int indicating which direction the node
// should go.
func compare(nodeLow, ivLow int64, nodeID, ivID uint64) int {
	if ivLow > nodeLow {
		return 1
	}

	if ivLow < nodeLow {
		return 0
	}

	return intFromBool(ivID > nodeID)
}

type node struct {
	interval Interval
	max, min int64    // max value held by children
	children [2]*node // array to hold left/right
	red      bool     // indicates if this node is red
	id       uint64   // we store the id locally to reduce the number of calls to the method on the interface
}

func (n *node) query(low, high int64, interval Interval, maxDimension uint64, fn func(node *node)) {
	if n.children[0] != nil && overlaps(n.children[0].max, high, n.children[0].min, low) {
		n.children[0].query(low, high, interval, maxDimension, fn)
	}

	if intervalOverlaps(n, low, high, interval, maxDimension) {
		fn(n)
	}

	if n.children[1] != nil && overlaps(n.children[1].max, high, n.children[1].min, low) {
		n.children[1].query(low, high, interval, maxDimension, fn)
	}
}

func (n *node) adjustRanges() {
	for i := 0; i <= 1; i++ {
		if n.children[i] != nil {
			n.children[i].adjustRanges()
		}
	}

	n.adjustRange()
}

func (n *node) adjustRange() {
	setMin(n)
	setMax(n)
}

func newDummy() node {
	return node{
		children: [2]*node{},
	}
}

func newNode(interval Interval, min, max int64, dimension uint64) *node {
	itn := &node{
		interval: interval,
		min:      min,
		max:      max,
		red:      true,
		children: [2]*node{},
	}
	if interval != nil {
		itn.id = interval.ID()
	}

	return itn
}

type tree struct {
	root                 *node
	maxDimension, number uint64
	dummy                node
}

func (t *tree) Traverse(fn func(id Interval)) {
	nodes := []*node{t.root}

	for len(nodes) != 0 {
		c := nodes[len(nodes)-1]
		fn(c.interval)
		nodes = nodes[:len(nodes)-1]
		if c.children[0] != nil {
			nodes = append(nodes, c.children[0])
		}
		if c.children[1] != nil {
			nodes = append(nodes, c.children[1])
		}
	}
}

func (tree *tree) resetDummy() {
	tree.dummy.children[0], tree.dummy.children[1] = nil, nil
	tree.dummy.red = false
}

// Len returns the number of items in this tree.
func (tree *tree) Len() uint64 {
	return tree.number
}

// add will add the provided interval to the tree.
func (tree *tree) add(iv Interval) {
	if tree.root == nil {
		tree.root = newNode(
			iv, iv.LowAtDimension(1),
			iv.HighAtDimension(1),
			1,
		)
		tree.root.red = false
		tree.number++
		return
	}

	tree.resetDummy()
	var (
		dummy               = tree.dummy
		parent, grandParent *node
		node                = tree.root
		dir, last           int
		otherLast           = 1
		id                  = iv.ID()
		max                 = iv.HighAtDimension(1)
		ivLow               = iv.LowAtDimension(1)
		helper              = &dummy
	)

	// set this AFTER clearing dummy
	helper.children[1] = tree.root
	for {
		if node == nil {
			node = newNode(iv, ivLow, max, 1)
			parent.children[dir] = node
			tree.number++
		} else if isRed(node.children[0]) && isRed(node.children[1]) {
			node.red = true
			node.children[0].red = false
			node.children[1].red = false
		}
		if max > node.max {
			node.max = max
		}

		if ivLow < node.min {
			node.min = ivLow
		}

		if isRed(parent) && isRed(node) {
			localDir := intFromBool(helper.children[1] == grandParent)

			if node == parent.children[last] {
				helper.children[localDir] = rotate(grandParent, otherLast)
			} else {
				helper.children[localDir] = doubleRotate(grandParent, otherLast)
			}
		}

		if node.id == id {
			break
		}

		last = dir
		otherLast = takeOpposite(last)
		dir = compare(node.interval.LowAtDimension(1), ivLow, node.id, id)

		if grandParent != nil {
			helper = grandParent
		}
		grandParent, parent, node = parent, node, node.children[dir]
	}

	tree.root = dummy.children[1]
	tree.root.red = false
}

// Add will add the provided intervals to this tree.
func (tree *tree) Add(intervals ...Interval) {
	for _, iv := range intervals {
		tree.add(iv)
	}
}

// delete will remove the provided interval from the tree.
func (tree *tree) delete(iv Interval) {
	if tree.root == nil {
		return
	}

	tree.resetDummy()
	var (
		dummy                      = tree.dummy
		found, parent, grandParent *node
		last, otherDir, otherLast  int // keeping track of last direction
		id                         = iv.ID()
		dir                        = 1
		node                       = &dummy
		ivLow                      = iv.LowAtDimension(1)
	)

	node.children[1] = tree.root
	for node.children[dir] != nil {
		last = dir
		otherLast = takeOpposite(last)

		grandParent, parent, node = parent, node, node.children[dir]

		dir = compare(node.interval.LowAtDimension(1), ivLow, node.id, id)
		otherDir = takeOpposite(dir)

		if node.id == id {
			found = node
		}

		if !isRed(node) && !isRed(node.children[dir]) {
			if isRed(node.children[otherDir]) {
				parent.children[last] = rotate(node, dir)
				parent = parent.children[last]
			} else if !isRed(node.children[otherDir]) {
				t := parent.children[otherLast]

				if t != nil {
					if !isRed(t.children[otherLast]) && !isRed(t.children[last]) {
						parent.red = false
						node.red = true
						t.red = true
					} else {
						localDir := intFromBool(grandParent.children[1] == parent)

						if isRed(t.children[last]) {
							grandParent.children[localDir] = doubleRotate(
								parent, last,
							)
						} else if isRed(t.children[otherLast]) {
							grandParent.children[localDir] = rotate(
								parent, last,
							)
						}

						node.red = true
						grandParent.children[localDir].red = true
						grandParent.children[localDir].children[0].red = false
						grandParent.children[localDir].children[1].red = false
					}
				}
			}
		}
	}

	if found != nil {
		tree.number--
		found.interval, found.max, found.min, found.id = node.interval, node.max, node.min, node.id
		parentDir := intFromBool(parent.children[1] == node)
		childDir := intFromBool(node.children[0] == nil)

		parent.children[parentDir] = node.children[childDir]
	}

	tree.root = dummy.children[1]
	if tree.root != nil {
		tree.root.red = false
	}
}

// Delete will remove the provided intervals from this tree.
func (tree *tree) Delete(intervals ...Interval) {
	for _, iv := range intervals {
		tree.delete(iv)
	}
	if tree.root != nil {
		tree.root.adjustRanges()
	}
}

// Query will return a list of intervals that intersect the provided
// interval.  The provided interval's ID method is ignored so the
// provided ID is irrelevant.
func (tree *tree) Query(interval Interval) Intervals {
	if tree.root == nil {
		return nil
	}

	var (
		Intervals = intervalsPool.Get().(Intervals)
		ivLow     = interval.LowAtDimension(1)
		ivHigh    = interval.HighAtDimension(1)
	)

	tree.root.query(ivLow, ivHigh, interval, tree.maxDimension, func(node *node) {
		Intervals = append(Intervals, node.interval)
	})

	return Intervals
}

func isRed(node *node) bool {
	return node != nil && node.red
}

func setMax(parent *node) {
	parent.max = parent.interval.HighAtDimension(1)

	if parent.children[0] != nil && parent.children[0].max > parent.max {
		parent.max = parent.children[0].max
	}

	if parent.children[1] != nil && parent.children[1].max > parent.max {
		parent.max = parent.children[1].max
	}
}

func setMin(parent *node) {
	parent.min = parent.interval.LowAtDimension(1)
	if parent.children[0] != nil && parent.children[0].min < parent.min {
		parent.min = parent.children[0].min
	}

	if parent.children[1] != nil && parent.children[1].min < parent.min {
		parent.min = parent.children[1].min
	}

	if parent.interval.LowAtDimension(1) < parent.min {
		parent.min = parent.interval.LowAtDimension(1)
	}
}

func rotate(parent *node, dir int) *node {
	otherDir := takeOpposite(dir)

	child := parent.children[otherDir]
	parent.children[otherDir] = child.children[dir]
	child.children[dir] = parent
	parent.red = true
	child.red = false
	child.max = parent.max
	setMax(child)
	setMax(parent)
	setMin(child)
	setMin(parent)

	return child
}

func doubleRotate(parent *node, dir int) *node {
	otherDir := takeOpposite(dir)

	parent.children[otherDir] = rotate(parent.children[otherDir], otherDir)
	return rotate(parent, dir)
}

func intFromBool(value bool) int {
	if value {
		return 1
	}

	return 0
}

func takeOpposite(value int) int {
	return 1 - value
}

func newTree(maxDimension uint64) *tree {
	return &tree{
		maxDimension: maxDimension,
		dummy:        newDummy(),
	}
}

// New constructs and returns a new interval tree with the max
// dimensions provided.
func New(dimensions uint64) Tree {
	return newTree(dimensions)
}
<commit_msg>Don't panic on traversing an empty tree<commit_after>/*
Copyright 2014 Workiva, LLC

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 augmentedtree

func intervalOverlaps(n *node, low, high int64, interval Interval, maxDimension uint64) bool {
	if !overlaps(n.interval.HighAtDimension(1), high, n.interval.LowAtDimension(1), low) {
		return false
	}

	if interval == nil {
		return true
	}

	for i := uint64(2); i <= maxDimension; i++ {
		if !n.interval.OverlapsAtDimension(interval, i) {
			return false
		}
	}

	return true
}

func overlaps(high, otherHigh, low, otherLow int64) bool {
	return high >= otherLow && low <= otherHigh
}

// compare returns an int indicating which direction the node
// should go.
func compare(nodeLow, ivLow int64, nodeID, ivID uint64) int {
	if ivLow > nodeLow {
		return 1
	}

	if ivLow < nodeLow {
		return 0
	}

	return intFromBool(ivID > nodeID)
}

type node struct {
	interval Interval
	max, min int64    // max value held by children
	children [2]*node // array to hold left/right
	red      bool     // indicates if this node is red
	id       uint64   // we store the id locally to reduce the number of calls to the method on the interface
}

func (n *node) query(low, high int64, interval Interval, maxDimension uint64, fn func(node *node)) {
	if n.children[0] != nil && overlaps(n.children[0].max, high, n.children[0].min, low) {
		n.children[0].query(low, high, interval, maxDimension, fn)
	}

	if intervalOverlaps(n, low, high, interval, maxDimension) {
		fn(n)
	}

	if n.children[1] != nil && overlaps(n.children[1].max, high, n.children[1].min, low) {
		n.children[1].query(low, high, interval, maxDimension, fn)
	}
}

func (n *node) adjustRanges() {
	for i := 0; i <= 1; i++ {
		if n.children[i] != nil {
			n.children[i].adjustRanges()
		}
	}

	n.adjustRange()
}

func (n *node) adjustRange() {
	setMin(n)
	setMax(n)
}

func newDummy() node {
	return node{
		children: [2]*node{},
	}
}

func newNode(interval Interval, min, max int64, dimension uint64) *node {
	itn := &node{
		interval: interval,
		min:      min,
		max:      max,
		red:      true,
		children: [2]*node{},
	}
	if interval != nil {
		itn.id = interval.ID()
	}

	return itn
}

type tree struct {
	root                 *node
	maxDimension, number uint64
	dummy                node
}

func (t *tree) Traverse(fn func(id Interval)) {
	nodes := []*node{t.root}

	for len(nodes) != 0 {
		c := nodes[len(nodes)-1]
		nodes = nodes[:len(nodes)-1]
		if c != nil {
			fn(c.interval)
			if c.children[0] != nil {
				nodes = append(nodes, c.children[0])
			}
			if c.children[1] != nil {
				nodes = append(nodes, c.children[1])
			}
		}
	}
}

func (tree *tree) resetDummy() {
	tree.dummy.children[0], tree.dummy.children[1] = nil, nil
	tree.dummy.red = false
}

// Len returns the number of items in this tree.
func (tree *tree) Len() uint64 {
	return tree.number
}

// add will add the provided interval to the tree.
func (tree *tree) add(iv Interval) {
	if tree.root == nil {
		tree.root = newNode(
			iv, iv.LowAtDimension(1),
			iv.HighAtDimension(1),
			1,
		)
		tree.root.red = false
		tree.number++
		return
	}

	tree.resetDummy()
	var (
		dummy               = tree.dummy
		parent, grandParent *node
		node                = tree.root
		dir, last           int
		otherLast           = 1
		id                  = iv.ID()
		max                 = iv.HighAtDimension(1)
		ivLow               = iv.LowAtDimension(1)
		helper              = &dummy
	)

	// set this AFTER clearing dummy
	helper.children[1] = tree.root
	for {
		if node == nil {
			node = newNode(iv, ivLow, max, 1)
			parent.children[dir] = node
			tree.number++
		} else if isRed(node.children[0]) && isRed(node.children[1]) {
			node.red = true
			node.children[0].red = false
			node.children[1].red = false
		}
		if max > node.max {
			node.max = max
		}

		if ivLow < node.min {
			node.min = ivLow
		}

		if isRed(parent) && isRed(node) {
			localDir := intFromBool(helper.children[1] == grandParent)

			if node == parent.children[last] {
				helper.children[localDir] = rotate(grandParent, otherLast)
			} else {
				helper.children[localDir] = doubleRotate(grandParent, otherLast)
			}
		}

		if node.id == id {
			break
		}

		last = dir
		otherLast = takeOpposite(last)
		dir = compare(node.interval.LowAtDimension(1), ivLow, node.id, id)

		if grandParent != nil {
			helper = grandParent
		}
		grandParent, parent, node = parent, node, node.children[dir]
	}

	tree.root = dummy.children[1]
	tree.root.red = false
}

// Add will add the provided intervals to this tree.
func (tree *tree) Add(intervals ...Interval) {
	for _, iv := range intervals {
		tree.add(iv)
	}
}

// delete will remove the provided interval from the tree.
func (tree *tree) delete(iv Interval) {
	if tree.root == nil {
		return
	}

	tree.resetDummy()
	var (
		dummy                      = tree.dummy
		found, parent, grandParent *node
		last, otherDir, otherLast  int // keeping track of last direction
		id                         = iv.ID()
		dir                        = 1
		node                       = &dummy
		ivLow                      = iv.LowAtDimension(1)
	)

	node.children[1] = tree.root
	for node.children[dir] != nil {
		last = dir
		otherLast = takeOpposite(last)

		grandParent, parent, node = parent, node, node.children[dir]

		dir = compare(node.interval.LowAtDimension(1), ivLow, node.id, id)
		otherDir = takeOpposite(dir)

		if node.id == id {
			found = node
		}

		if !isRed(node) && !isRed(node.children[dir]) {
			if isRed(node.children[otherDir]) {
				parent.children[last] = rotate(node, dir)
				parent = parent.children[last]
			} else if !isRed(node.children[otherDir]) {
				t := parent.children[otherLast]

				if t != nil {
					if !isRed(t.children[otherLast]) && !isRed(t.children[last]) {
						parent.red = false
						node.red = true
						t.red = true
					} else {
						localDir := intFromBool(grandParent.children[1] == parent)

						if isRed(t.children[last]) {
							grandParent.children[localDir] = doubleRotate(
								parent, last,
							)
						} else if isRed(t.children[otherLast]) {
							grandParent.children[localDir] = rotate(
								parent, last,
							)
						}

						node.red = true
						grandParent.children[localDir].red = true
						grandParent.children[localDir].children[0].red = false
						grandParent.children[localDir].children[1].red = false
					}
				}
			}
		}
	}

	if found != nil {
		tree.number--
		found.interval, found.max, found.min, found.id = node.interval, node.max, node.min, node.id
		parentDir := intFromBool(parent.children[1] == node)
		childDir := intFromBool(node.children[0] == nil)

		parent.children[parentDir] = node.children[childDir]
	}

	tree.root = dummy.children[1]
	if tree.root != nil {
		tree.root.red = false
	}
}

// Delete will remove the provided intervals from this tree.
func (tree *tree) Delete(intervals ...Interval) {
	for _, iv := range intervals {
		tree.delete(iv)
	}
	if tree.root != nil {
		tree.root.adjustRanges()
	}
}

// Query will return a list of intervals that intersect the provided
// interval.  The provided interval's ID method is ignored so the
// provided ID is irrelevant.
func (tree *tree) Query(interval Interval) Intervals {
	if tree.root == nil {
		return nil
	}

	var (
		Intervals = intervalsPool.Get().(Intervals)
		ivLow     = interval.LowAtDimension(1)
		ivHigh    = interval.HighAtDimension(1)
	)

	tree.root.query(ivLow, ivHigh, interval, tree.maxDimension, func(node *node) {
		Intervals = append(Intervals, node.interval)
	})

	return Intervals
}

func isRed(node *node) bool {
	return node != nil && node.red
}

func setMax(parent *node) {
	parent.max = parent.interval.HighAtDimension(1)

	if parent.children[0] != nil && parent.children[0].max > parent.max {
		parent.max = parent.children[0].max
	}

	if parent.children[1] != nil && parent.children[1].max > parent.max {
		parent.max = parent.children[1].max
	}
}

func setMin(parent *node) {
	parent.min = parent.interval.LowAtDimension(1)
	if parent.children[0] != nil && parent.children[0].min < parent.min {
		parent.min = parent.children[0].min
	}

	if parent.children[1] != nil && parent.children[1].min < parent.min {
		parent.min = parent.children[1].min
	}

	if parent.interval.LowAtDimension(1) < parent.min {
		parent.min = parent.interval.LowAtDimension(1)
	}
}

func rotate(parent *node, dir int) *node {
	otherDir := takeOpposite(dir)

	child := parent.children[otherDir]
	parent.children[otherDir] = child.children[dir]
	child.children[dir] = parent
	parent.red = true
	child.red = false
	child.max = parent.max
	setMax(child)
	setMax(parent)
	setMin(child)
	setMin(parent)

	return child
}

func doubleRotate(parent *node, dir int) *node {
	otherDir := takeOpposite(dir)

	parent.children[otherDir] = rotate(parent.children[otherDir], otherDir)
	return rotate(parent, dir)
}

func intFromBool(value bool) int {
	if value {
		return 1
	}

	return 0
}

func takeOpposite(value int) int {
	return 1 - value
}

func newTree(maxDimension uint64) *tree {
	return &tree{
		maxDimension: maxDimension,
		dummy:        newDummy(),
	}
}

// New constructs and returns a new interval tree with the max
// dimensions provided.
func New(dimensions uint64) Tree {
	return newTree(dimensions)
}
<|endoftext|>


<commit_before>//
// DISCLAIMER
//
// Copyright 2018 ArangoDB GmbH, Cologne, Germany
//
// 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.
//
// Copyright holder is ArangoDB GmbH, Cologne, Germany
//
// Author Ewout Prangsma
//

package test

import (
	"context"
	"fmt"
	"os"
	"strings"
	"testing"
	"time"
)

// TestDockerClusterRecovery starts a master starter in docker, followed by 2 slave starters.
// Once started, it destroys one of the starters and attempts a recovery.
func TestDockerClusterRecovery(t *testing.T) {
	needTestMode(t, testModeDocker)
	needStarterMode(t, starterModeCluster)
	if os.Getenv("IP") == "" {
		t.Fatal("IP envvar must be set to IP address of this machine")
	}
	/*
		docker volume create arangodb1
		docker run -i --name=adb1 --rm -p 8528:8528 \
			-v arangodb1:/data \
			-v /var/run/docker.sock:/var/run/docker.sock \
			arangodb/arangodb-starter \
			--docker.container=adb1 \
			--starter.address=$IP
	*/
	volID1 := createDockerID("vol-starter-test-cluster-recovery1-")
	createDockerVolume(t, volID1)
	defer removeDockerVolume(t, volID1)

	volID2 := createDockerID("vol-starter-test-cluster-recovery2-")
	createDockerVolume(t, volID2)
	defer removeDockerVolume(t, volID2)

	volID3 := createDockerID("vol-starter-test-cluster-recovery3-")
	createDockerVolume(t, volID3)
	defer removeDockerVolume(t, volID3)

	// Cleanup of left over tests
	removeDockerContainersByLabel(t, "starter-test=true")
	removeStarterCreatedDockerContainers(t)

	start := time.Now()

	cID1 := createDockerID("starter-test-cluster-recovery1-")
	dockerRun1 := Spawn(t, strings.Join([]string{
		"docker run -i",
		"--label starter-test=true",
		"--name=" + cID1,
		"--rm",
		fmt.Sprintf("-p %d:%d", basePort, basePort),
		fmt.Sprintf("-v %s:/data", volID1),
		"-v /var/run/docker.sock:/var/run/docker.sock",
		"arangodb/arangodb-starter",
		"--docker.container=" + cID1,
		fmt.Sprintf("--starter.port=%d", basePort),
		"--starter.address=$IP",
		createEnvironmentStarterOptions(),
	}, " "))
	defer dockerRun1.Close()
	defer removeDockerContainer(t, cID1)

	cID2 := createDockerID("starter-test-cluster-recovery2-")
	dockerRun2 := Spawn(t, strings.Join([]string{
		"docker run -i",
		"--label starter-test=true",
		"--name=" + cID2,
		"--rm",
		fmt.Sprintf("-p %d:%d", basePort+100, basePort+100),
		fmt.Sprintf("-v %s:/data", volID2),
		"-v /var/run/docker.sock:/var/run/docker.sock",
		"arangodb/arangodb-starter",
		"--docker.container=" + cID2,
		fmt.Sprintf("--starter.port=%d", basePort+100),
		"--starter.address=$IP",
		createEnvironmentStarterOptions(),
		fmt.Sprintf("--starter.join=$IP:%d", basePort),
	}, " "))
	defer dockerRun2.Close()
	defer removeDockerContainer(t, cID2)

	cID3 := createDockerID("starter-test-cluster-recovery3-")
	dockerRun3 := Spawn(t, strings.Join([]string{
		"docker run -i",
		"--label starter-test=true",
		"--name=" + cID3,
		"--rm",
		fmt.Sprintf("-p %d:%d", basePort+200, basePort+200),
		fmt.Sprintf("-v %s:/data", volID3),
		"-v /var/run/docker.sock:/var/run/docker.sock",
		"arangodb/arangodb-starter",
		"--docker.container=" + cID3,
		fmt.Sprintf("--starter.port=%d", basePort+200),
		"--starter.address=$IP",
		createEnvironmentStarterOptions(),
		fmt.Sprintf("--starter.join=$IP:%d", basePort),
	}, " "))
	defer dockerRun3.Close()
	defer removeDockerContainer(t, cID3)

	if ok := WaitUntilStarterReady(t, whatCluster, dockerRun1, dockerRun2, dockerRun3); ok {
		t.Logf("Cluster start took %s", time.Since(start))
		testCluster(t, insecureStarterEndpoint(0), false)
		testCluster(t, insecureStarterEndpoint(100), false)
		testCluster(t, insecureStarterEndpoint(200), false)
	}

	if isVerbose {
		t.Log("Start killing slave1 and its servers")
	}

	// Cluster is up.
	// Kill starter slave-1 and all its processes
	ctx := context.Background()
	c := NewStarterClient(t, insecureStarterEndpoint(100))
	plist, err := c.Processes(ctx)
	if err != nil {
		t.Errorf("Processes failed: %s", describe(err))
		return
	}
	// Kill starter & servers
	containersToKill := []string{cID2}
	for _, s := range plist.Servers {
		containersToKill = append(containersToKill, s.ContainerID)
	}
	killDockerRun2 := Spawn(t, strings.Join([]string{
		"docker rm -vf " + cID2,
	}, " "))
	killDockerRun2.Wait()

	// Remove entire docker volume
	removeDockerVolume(t, volID2)

	if isVerbose {
		t.Log("Starting recovery...")
	}

	// Create new volume
	recVolID2 := createDockerID("vol-starter-test-cluster-recovery2-recovery-")
	createDockerVolume(t, recVolID2)
	defer removeDockerVolume(t, recVolID2)

	// Create RECOVERY file in new volume
	recoveryContent := fmt.Sprintf("%s:%d", os.Getenv("IP"), basePort+(100))
	dockerBuildRecoveryRun := Spawn(t, strings.Join([]string{
		"docker run -i",
		"--label starter-test=true",
		"--name=" + cID2 + "recovery-builder",
		"--rm",
		fmt.Sprintf("-v %s:/data", recVolID2),
		"alpine",
		fmt.Sprintf("sh -c \"echo %s > /data/RECOVERY\"", recoveryContent),
	}, " "))
	dockerBuildRecoveryRun.Wait()

	// Restart dockerRun2
	recCID2 := createDockerID("starter-test-cluster-recovery2-recovery-")
	recDockerRun2 := Spawn(t, strings.Join([]string{
		"docker run -i",
		"--label starter-test=true",
		"--name=" + recCID2,
		"--rm",
		fmt.Sprintf("-p %d:%d", basePort+100, basePort+100),
		fmt.Sprintf("-v %s:/data", recVolID2),
		"-v /var/run/docker.sock:/var/run/docker.sock",
		"arangodb/arangodb-starter",
		"--docker.container=" + recCID2,
		fmt.Sprintf("--starter.port=%d", basePort+100),
		"--starter.address=$IP",
		createEnvironmentStarterOptions(),
		fmt.Sprintf("--starter.join=$IP:%d", basePort),
	}, " "))
	defer recDockerRun2.Close()
	defer removeDockerContainer(t, recCID2)

	// Wait until recovered
	if ok := WaitUntilStarterReady(t, whatCluster, recDockerRun2); ok {
		t.Logf("Cluster start (with recovery) took %s", time.Since(start))
		testCluster(t, insecureStarterEndpoint(0), false)
		testCluster(t, insecureStarterEndpoint(100), false)
		testCluster(t, insecureStarterEndpoint(200), false)
	}

	// RECOVERY file must now be gone
	/*	if _, err := os.Stat(filepath.Join(dataDirSlave1, "RECOVERY")); !os.IsNotExist(err) {
		t.Errorf("Expected RECOVERY file to not-exist, got: %s", describe(err))
	}*/

	if isVerbose {
		t.Log("Waiting for termination")
	}
	ShutdownStarter(t, insecureStarterEndpoint(0*portIncrement))
	ShutdownStarter(t, insecureStarterEndpoint(1*portIncrement))
	ShutdownStarter(t, insecureStarterEndpoint(2*portIncrement))
}
<commit_msg>Test fixes<commit_after>//
// DISCLAIMER
//
// Copyright 2018 ArangoDB GmbH, Cologne, Germany
//
// 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.
//
// Copyright holder is ArangoDB GmbH, Cologne, Germany
//
// Author Ewout Prangsma
//

package test

import (
	"context"
	"fmt"
	"os"
	"strings"
	"testing"
	"time"
)

// TestDockerClusterRecovery starts a master starter in docker, followed by 2 slave starters.
// Once started, it destroys one of the starters and attempts a recovery.
func TestDockerClusterRecovery(t *testing.T) {
	needTestMode(t, testModeDocker)
	needStarterMode(t, starterModeCluster)
	if os.Getenv("IP") == "" {
		t.Fatal("IP envvar must be set to IP address of this machine")
	}
	/*
		docker volume create arangodb1
		docker run -i --name=adb1 --rm -p 8528:8528 \
			-v arangodb1:/data \
			-v /var/run/docker.sock:/var/run/docker.sock \
			arangodb/arangodb-starter \
			--docker.container=adb1 \
			--starter.address=$IP
	*/
	volID1 := createDockerID("vol-starter-test-cluster-recovery1-")
	createDockerVolume(t, volID1)
	defer removeDockerVolume(t, volID1)

	volID2 := createDockerID("vol-starter-test-cluster-recovery2-")
	createDockerVolume(t, volID2)
	defer removeDockerVolume(t, volID2)

	volID3 := createDockerID("vol-starter-test-cluster-recovery3-")
	createDockerVolume(t, volID3)
	defer removeDockerVolume(t, volID3)

	// Cleanup of left over tests
	removeDockerContainersByLabel(t, "starter-test=true")
	removeStarterCreatedDockerContainers(t)

	start := time.Now()

	cID1 := createDockerID("starter-test-cluster-recovery1-")
	dockerRun1 := Spawn(t, strings.Join([]string{
		"docker run -i",
		"--label starter-test=true",
		"--name=" + cID1,
		"--rm",
		fmt.Sprintf("-p %d:%d", basePort, basePort),
		fmt.Sprintf("-v %s:/data", volID1),
		"-v /var/run/docker.sock:/var/run/docker.sock",
		"arangodb/arangodb-starter",
		"--docker.container=" + cID1,
		fmt.Sprintf("--starter.port=%d", basePort),
		"--starter.address=$IP",
		createEnvironmentStarterOptions(),
	}, " "))
	defer dockerRun1.Close()
	defer removeDockerContainer(t, cID1)

	cID2 := createDockerID("starter-test-cluster-recovery2-")
	dockerRun2 := Spawn(t, strings.Join([]string{
		"docker run -i",
		"--label starter-test=true",
		"--name=" + cID2,
		"--rm",
		fmt.Sprintf("-p %d:%d", basePort+100, basePort+100),
		fmt.Sprintf("-v %s:/data", volID2),
		"-v /var/run/docker.sock:/var/run/docker.sock",
		"arangodb/arangodb-starter",
		"--docker.container=" + cID2,
		fmt.Sprintf("--starter.port=%d", basePort+100),
		"--starter.address=$IP",
		createEnvironmentStarterOptions(),
		fmt.Sprintf("--starter.join=$IP:%d", basePort),
	}, " "))
	defer dockerRun2.Close()
	defer removeDockerContainer(t, cID2)

	cID3 := createDockerID("starter-test-cluster-recovery3-")
	dockerRun3 := Spawn(t, strings.Join([]string{
		"docker run -i",
		"--label starter-test=true",
		"--name=" + cID3,
		"--rm",
		fmt.Sprintf("-p %d:%d", basePort+200, basePort+200),
		fmt.Sprintf("-v %s:/data", volID3),
		"-v /var/run/docker.sock:/var/run/docker.sock",
		"arangodb/arangodb-starter",
		"--docker.container=" + cID3,
		fmt.Sprintf("--starter.port=%d", basePort+200),
		"--starter.address=$IP",
		createEnvironmentStarterOptions(),
		fmt.Sprintf("--starter.join=$IP:%d", basePort),
	}, " "))
	defer dockerRun3.Close()
	defer removeDockerContainer(t, cID3)

	if ok := WaitUntilStarterReady(t, whatCluster, dockerRun1, dockerRun2, dockerRun3); ok {
		t.Logf("Cluster start took %s", time.Since(start))
		testCluster(t, insecureStarterEndpoint(0), false)
		testCluster(t, insecureStarterEndpoint(100), false)
		testCluster(t, insecureStarterEndpoint(200), false)
	}

	if isVerbose {
		t.Log("Start killing slave1 and its servers")
	}

	// Cluster is up.
	// Kill starter slave-1 and all its processes
	ctx := context.Background()
	c := NewStarterClient(t, insecureStarterEndpoint(100))
	plist, err := c.Processes(ctx)
	if err != nil {
		t.Errorf("Processes failed: %s", describe(err))
		return
	}
	// Kill starter & servers
	containersToKill := []string{cID2}
	for _, s := range plist.Servers {
		containersToKill = append(containersToKill, s.ContainerID)
	}
	killDockerRun2 := Spawn(t, "docker rm -vf "+strings.Join(containersToKill, " "))
	killDockerRun2.Wait()

	// Remove entire docker volume
	removeDockerVolume(t, volID2)

	if isVerbose {
		t.Log("Starting recovery...")
	}

	// Create new volume
	recVolID2 := createDockerID("vol-starter-test-cluster-recovery2-recovery-")
	createDockerVolume(t, recVolID2)
	defer removeDockerVolume(t, recVolID2)

	// Create RECOVERY file in new volume
	recoveryContent := fmt.Sprintf("%s:%d", os.Getenv("IP"), basePort+(100))
	dockerBuildRecoveryRun := Spawn(t, strings.Join([]string{
		"docker run -i",
		"--label starter-test=true",
		"--name=" + cID2 + "recovery-builder",
		"--rm",
		fmt.Sprintf("-v %s:/data", recVolID2),
		"alpine",
		fmt.Sprintf("sh -c \"echo %s > /data/RECOVERY\"", recoveryContent),
	}, " "))
	dockerBuildRecoveryRun.Wait()

	// Restart dockerRun2
	recCID2 := createDockerID("starter-test-cluster-recovery2-recovery-")
	recDockerRun2 := Spawn(t, strings.Join([]string{
		"docker run -i",
		"--label starter-test=true",
		"--name=" + recCID2,
		"--rm",
		fmt.Sprintf("-p %d:%d", basePort+100, basePort+100),
		fmt.Sprintf("-v %s:/data", recVolID2),
		"-v /var/run/docker.sock:/var/run/docker.sock",
		"arangodb/arangodb-starter",
		"--docker.container=" + recCID2,
		fmt.Sprintf("--starter.port=%d", basePort+100),
		"--starter.address=$IP",
		createEnvironmentStarterOptions(),
		fmt.Sprintf("--starter.join=$IP:%d", basePort),
	}, " "))
	defer recDockerRun2.Close()
	defer removeDockerContainer(t, recCID2)

	// Wait until recovered
	if ok := WaitUntilStarterReady(t, whatCluster, recDockerRun2); ok {
		t.Logf("Cluster start (with recovery) took %s", time.Since(start))
		testCluster(t, insecureStarterEndpoint(0), false)
		testCluster(t, insecureStarterEndpoint(100), false)
		testCluster(t, insecureStarterEndpoint(200), false)
	}

	// RECOVERY file must now be gone
	/*	if _, err := os.Stat(filepath.Join(dataDirSlave1, "RECOVERY")); !os.IsNotExist(err) {
		t.Errorf("Expected RECOVERY file to not-exist, got: %s", describe(err))
	}*/

	if isVerbose {
		t.Log("Waiting for termination")
	}
	ShutdownStarter(t, insecureStarterEndpoint(0))
	ShutdownStarter(t, insecureStarterEndpoint(100))
	ShutdownStarter(t, insecureStarterEndpoint(200))
}
<|endoftext|>


<commit_before>// +build linux

/*
Copyright 2016 The Kubernetes Authors.

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

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

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

// To run tests in this suite
// NOTE: This test suite requires password-less sudo capabilities to run the kubelet and kube-apiserver.
package e2e_node

import (
	"bytes"
	"encoding/json"
	"flag"
	"fmt"
	"io/ioutil"
	"math/rand"
	"os"
	"os/exec"
	"path"
	"syscall"
	"testing"
	"time"

	"k8s.io/api/core/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	utilyaml "k8s.io/apimachinery/pkg/util/yaml"
	clientset "k8s.io/client-go/kubernetes"
	"k8s.io/kubernetes/cmd/kubeadm/app/util/system"
	commontest "k8s.io/kubernetes/test/e2e/common"
	"k8s.io/kubernetes/test/e2e/framework"
	"k8s.io/kubernetes/test/e2e_node/services"

	"github.com/kardianos/osext"
	. "github.com/onsi/ginkgo"
	"github.com/onsi/ginkgo/config"
	morereporters "github.com/onsi/ginkgo/reporters"
	. "github.com/onsi/gomega"
	"github.com/spf13/pflag"
	"k8s.io/klog"
)

var e2es *services.E2EServices

// TODO(random-liu): Change the following modes to sub-command.
var runServicesMode = flag.Bool("run-services-mode", false, "If true, only run services (etcd, apiserver) in current process, and not run test.")
var runKubeletMode = flag.Bool("run-kubelet-mode", false, "If true, only start kubelet, and not run test.")
var systemValidateMode = flag.Bool("system-validate-mode", false, "If true, only run system validation in current process, and not run test.")
var systemSpecFile = flag.String("system-spec-file", "", "The name of the system spec file that will be used for node conformance test. If it's unspecified or empty, the default system spec (system.DefaultSysSpec) will be used.")

func init() {
	framework.RegisterCommonFlags()
	framework.RegisterNodeFlags()
	pflag.CommandLine.AddGoFlagSet(flag.CommandLine)
	// Mark the run-services-mode flag as hidden to prevent user from using it.
	pflag.CommandLine.MarkHidden("run-services-mode")
	// It's weird that if I directly use pflag in TestContext, it will report error.
	// It seems that someone is using flag.Parse() after init() and TestMain().
	// TODO(random-liu): Find who is using flag.Parse() and cause errors and move the following logic
	// into TestContext.
	// TODO(pohly): remove RegisterNodeFlags from test_context.go enable Viper config support here?
}

func TestMain(m *testing.M) {
	rand.Seed(time.Now().UnixNano())
	pflag.Parse()
	framework.AfterReadingAllFlags(&framework.TestContext)
	setExtraEnvs()
	os.Exit(m.Run())
}

// When running the containerized conformance test, we'll mount the
// host root filesystem as readonly to /rootfs.
const rootfs = "/rootfs"

func TestE2eNode(t *testing.T) {
	if *runServicesMode {
		// If run-services-mode is specified, only run services in current process.
		services.RunE2EServices(t)
		return
	}
	if *runKubeletMode {
		// If run-kubelet-mode is specified, only start kubelet.
		services.RunKubelet()
		return
	}
	if *systemValidateMode {
		// If system-validate-mode is specified, only run system validation in current process.
		spec := &system.DefaultSysSpec
		if *systemSpecFile != "" {
			var err error
			spec, err = loadSystemSpecFromFile(*systemSpecFile)
			if err != nil {
				klog.Exitf("Failed to load system spec: %v", err)
			}
		}
		if framework.TestContext.NodeConformance {
			// Chroot to /rootfs to make system validation can check system
			// as in the root filesystem.
			// TODO(random-liu): Consider to chroot the whole test process to make writing
			// test easier.
			if err := syscall.Chroot(rootfs); err != nil {
				klog.Exitf("chroot %q failed: %v", rootfs, err)
			}
		}
		if _, err := system.ValidateSpec(*spec, framework.TestContext.ContainerRuntime); err != nil {
			klog.Exitf("system validation failed: %v", err)
		}
		return
	}
	// If run-services-mode is not specified, run test.
	RegisterFailHandler(Fail)
	reporters := []Reporter{}
	reportDir := framework.TestContext.ReportDir
	if reportDir != "" {
		// Create the directory if it doesn't already exists
		if err := os.MkdirAll(reportDir, 0755); err != nil {
			klog.Errorf("Failed creating report directory: %v", err)
		} else {
			// Configure a junit reporter to write to the directory
			junitFile := fmt.Sprintf("junit_%s_%02d.xml", framework.TestContext.ReportPrefix, config.GinkgoConfig.ParallelNode)
			junitPath := path.Join(reportDir, junitFile)
			reporters = append(reporters, morereporters.NewJUnitReporter(junitPath))
		}
	}
	RunSpecsWithDefaultAndCustomReporters(t, "E2eNode Suite", reporters)
}

// Setup the kubelet on the node
var _ = SynchronizedBeforeSuite(func() []byte {
	// Run system validation test.
	Expect(validateSystem()).To(Succeed(), "system validation")

	// Pre-pull the images tests depend on so we can fail immediately if there is an image pull issue
	// This helps with debugging test flakes since it is hard to tell when a test failure is due to image pulling.
	if framework.TestContext.PrepullImages {
		klog.Infof("Pre-pulling images so that they are cached for the tests.")
		updateImageWhiteList()
		err := PrePullAllImages()
		Expect(err).ShouldNot(HaveOccurred())
	}

	// TODO(yifan): Temporary workaround to disable coreos from auto restart
	// by masking the locksmithd.
	// We should mask locksmithd when provisioning the machine.
	maskLocksmithdOnCoreos()

	if *startServices {
		// If the services are expected to stop after test, they should monitor the test process.
		// If the services are expected to keep running after test, they should not monitor the test process.
		e2es = services.NewE2EServices(*stopServices)
		Expect(e2es.Start()).To(Succeed(), "should be able to start node services.")
		klog.Infof("Node services started.  Running tests...")
	} else {
		klog.Infof("Running tests without starting services.")
	}

	klog.Infof("Wait for the node to be ready")
	waitForNodeReady()

	// Reference common test to make the import valid.
	commontest.CurrentSuite = commontest.NodeE2E

	return nil
}, func([]byte) {
	// update test context with node configuration.
	Expect(updateTestContext()).To(Succeed(), "update test context with node config.")
})

// Tear down the kubelet on the node
var _ = SynchronizedAfterSuite(func() {}, func() {
	if e2es != nil {
		if *startServices && *stopServices {
			klog.Infof("Stopping node services...")
			e2es.Stop()
		}
	}

	klog.Infof("Tests Finished")
})

// validateSystem runs system validation in a separate process and returns error if validation fails.
func validateSystem() error {
	testBin, err := osext.Executable()
	if err != nil {
		return fmt.Errorf("can't get current binary: %v", err)
	}
	// Pass all flags into the child process, so that it will see the same flag set.
	output, err := exec.Command(testBin, append([]string{"--system-validate-mode"}, os.Args[1:]...)...).CombinedOutput()
	// The output of system validation should have been formatted, directly print here.
	fmt.Print(string(output))
	if err != nil {
		return fmt.Errorf("system validation failed: %v", err)
	}
	return nil
}

func maskLocksmithdOnCoreos() {
	data, err := ioutil.ReadFile("/etc/os-release")
	if err != nil {
		// Not all distros contain this file.
		klog.Infof("Could not read /etc/os-release: %v", err)
		return
	}
	if bytes.Contains(data, []byte("ID=coreos")) {
		output, err := exec.Command("systemctl", "mask", "--now", "locksmithd").CombinedOutput()
		Expect(err).NotTo(HaveOccurred(), fmt.Sprintf("should be able to mask locksmithd - output: %q", string(output)))
		klog.Infof("Locksmithd is masked successfully")
	}
}

func waitForNodeReady() {
	const (
		// nodeReadyTimeout is the time to wait for node to become ready.
		nodeReadyTimeout = 2 * time.Minute
		// nodeReadyPollInterval is the interval to check node ready.
		nodeReadyPollInterval = 1 * time.Second
	)
	client, err := getAPIServerClient()
	Expect(err).NotTo(HaveOccurred(), "should be able to get apiserver client.")
	Eventually(func() error {
		node, err := getNode(client)
		if err != nil {
			return fmt.Errorf("failed to get node: %v", err)
		}
		if !isNodeReady(node) {
			return fmt.Errorf("node is not ready: %+v", node)
		}
		return nil
	}, nodeReadyTimeout, nodeReadyPollInterval).Should(Succeed())
}

// updateTestContext updates the test context with the node name.
// TODO(random-liu): Using dynamic kubelet configuration feature to
// update test context with node configuration.
func updateTestContext() error {
	setExtraEnvs()
	updateImageWhiteList()

	client, err := getAPIServerClient()
	if err != nil {
		return fmt.Errorf("failed to get apiserver client: %v", err)
	}
	// Update test context with current node object.
	node, err := getNode(client)
	if err != nil {
		return fmt.Errorf("failed to get node: %v", err)
	}
	framework.TestContext.NodeName = node.Name // Set node name.
	// Update test context with current kubelet configuration.
	// This assumes all tests which dynamically change kubelet configuration
	// must: 1) run in serial; 2) restore kubelet configuration after test.
	kubeletCfg, err := getCurrentKubeletConfig()
	if err != nil {
		return fmt.Errorf("failed to get kubelet configuration: %v", err)
	}
	framework.TestContext.KubeletConfig = *kubeletCfg // Set kubelet config
	return nil
}

// getNode gets node object from the apiserver.
func getNode(c *clientset.Clientset) (*v1.Node, error) {
	nodes, err := c.CoreV1().Nodes().List(metav1.ListOptions{})
	Expect(err).NotTo(HaveOccurred(), "should be able to list nodes.")
	if nodes == nil {
		return nil, fmt.Errorf("the node list is nil.")
	}
	Expect(len(nodes.Items) > 1).NotTo(BeTrue(), "should not be more than 1 nodes.")
	if len(nodes.Items) == 0 {
		return nil, fmt.Errorf("empty node list: %+v", nodes)
	}
	return &nodes.Items[0], nil
}

// getAPIServerClient gets a apiserver client.
func getAPIServerClient() (*clientset.Clientset, error) {
	config, err := framework.LoadConfig()
	if err != nil {
		return nil, fmt.Errorf("failed to load config: %v", err)
	}
	client, err := clientset.NewForConfig(config)
	if err != nil {
		return nil, fmt.Errorf("failed to create client: %v", err)
	}
	return client, nil
}

// loadSystemSpecFromFile returns the system spec from the file with the
// filename.
func loadSystemSpecFromFile(filename string) (*system.SysSpec, error) {
	b, err := ioutil.ReadFile(filename)
	if err != nil {
		return nil, err
	}
	data, err := utilyaml.ToJSON(b)
	if err != nil {
		return nil, err
	}
	spec := new(system.SysSpec)
	if err := json.Unmarshal(data, spec); err != nil {
		return nil, err
	}
	return spec, nil
}

// isNodeReady returns true if a node is ready; false otherwise.
func isNodeReady(node *v1.Node) bool {
	for _, c := range node.Status.Conditions {
		if c.Type == v1.NodeReady {
			return c.Status == v1.ConditionTrue
		}
	}
	return false
}

func setExtraEnvs() {
	for name, value := range framework.TestContext.ExtraEnvs {
		os.Setenv(name, value)
	}
}
<commit_msg>fix a mistake-2<commit_after>// +build linux

/*
Copyright 2016 The Kubernetes Authors.

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

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

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

// To run tests in this suite
// NOTE: This test suite requires password-less sudo capabilities to run the kubelet and kube-apiserver.
package e2e_node

import (
	"bytes"
	"encoding/json"
	"flag"
	"fmt"
	"io/ioutil"
	"math/rand"
	"os"
	"os/exec"
	"path"
	"syscall"
	"testing"
	"time"

	"k8s.io/api/core/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	utilyaml "k8s.io/apimachinery/pkg/util/yaml"
	clientset "k8s.io/client-go/kubernetes"
	"k8s.io/kubernetes/cmd/kubeadm/app/util/system"
	commontest "k8s.io/kubernetes/test/e2e/common"
	"k8s.io/kubernetes/test/e2e/framework"
	"k8s.io/kubernetes/test/e2e_node/services"

	"github.com/kardianos/osext"
	. "github.com/onsi/ginkgo"
	"github.com/onsi/ginkgo/config"
	morereporters "github.com/onsi/ginkgo/reporters"
	. "github.com/onsi/gomega"
	"github.com/spf13/pflag"
	"k8s.io/klog"
)

var e2es *services.E2EServices

// TODO(random-liu): Change the following modes to sub-command.
var runServicesMode = flag.Bool("run-services-mode", false, "If true, only run services (etcd, apiserver) in current process, and not run test.")
var runKubeletMode = flag.Bool("run-kubelet-mode", false, "If true, only start kubelet, and not run test.")
var systemValidateMode = flag.Bool("system-validate-mode", false, "If true, only run system validation in current process, and not run test.")
var systemSpecFile = flag.String("system-spec-file", "", "The name of the system spec file that will be used for node conformance test. If it's unspecified or empty, the default system spec (system.DefaultSysSpec) will be used.")

func init() {
	framework.RegisterCommonFlags()
	framework.RegisterNodeFlags()
	pflag.CommandLine.AddGoFlagSet(flag.CommandLine)
	// Mark the run-services-mode flag as hidden to prevent user from using it.
	pflag.CommandLine.MarkHidden("run-services-mode")
	// It's weird that if I directly use pflag in TestContext, it will report error.
	// It seems that someone is using flag.Parse() after init() and TestMain().
	// TODO(random-liu): Find who is using flag.Parse() and cause errors and move the following logic
	// into TestContext.
	// TODO(pohly): remove RegisterNodeFlags from test_context.go enable Viper config support here?
}

func TestMain(m *testing.M) {
	rand.Seed(time.Now().UnixNano())
	pflag.Parse()
	framework.AfterReadingAllFlags(&framework.TestContext)
	setExtraEnvs()
	os.Exit(m.Run())
}

// When running the containerized conformance test, we'll mount the
// host root filesystem as readonly to /rootfs.
const rootfs = "/rootfs"

func TestE2eNode(t *testing.T) {
	if *runServicesMode {
		// If run-services-mode is specified, only run services in current process.
		services.RunE2EServices(t)
		return
	}
	if *runKubeletMode {
		// If run-kubelet-mode is specified, only start kubelet.
		services.RunKubelet()
		return
	}
	if *systemValidateMode {
		// If system-validate-mode is specified, only run system validation in current process.
		spec := &system.DefaultSysSpec
		if *systemSpecFile != "" {
			var err error
			spec, err = loadSystemSpecFromFile(*systemSpecFile)
			if err != nil {
				klog.Exitf("Failed to load system spec: %v", err)
			}
		}
		if framework.TestContext.NodeConformance {
			// Chroot to /rootfs to make system validation can check system
			// as in the root filesystem.
			// TODO(random-liu): Consider to chroot the whole test process to make writing
			// test easier.
			if err := syscall.Chroot(rootfs); err != nil {
				klog.Exitf("chroot %q failed: %v", rootfs, err)
			}
		}
		if _, err := system.ValidateSpec(*spec, framework.TestContext.ContainerRuntime); err != nil {
			klog.Exitf("system validation failed: %v", err)
		}
		return
	}
	// If run-services-mode is not specified, run test.
	RegisterFailHandler(Fail)
	reporters := []Reporter{}
	reportDir := framework.TestContext.ReportDir
	if reportDir != "" {
		// Create the directory if it doesn't already exists
		if err := os.MkdirAll(reportDir, 0755); err != nil {
			klog.Errorf("Failed creating report directory: %v", err)
		} else {
			// Configure a junit reporter to write to the directory
			junitFile := fmt.Sprintf("junit_%s_%02d.xml", framework.TestContext.ReportPrefix, config.GinkgoConfig.ParallelNode)
			junitPath := path.Join(reportDir, junitFile)
			reporters = append(reporters, morereporters.NewJUnitReporter(junitPath))
		}
	}
	RunSpecsWithDefaultAndCustomReporters(t, "E2eNode Suite", reporters)
}

// Setup the kubelet on the node
var _ = SynchronizedBeforeSuite(func() []byte {
	// Run system validation test.
	Expect(validateSystem()).To(Succeed(), "system validation")

	// Pre-pull the images tests depend on so we can fail immediately if there is an image pull issue
	// This helps with debugging test flakes since it is hard to tell when a test failure is due to image pulling.
	if framework.TestContext.PrepullImages {
		klog.Infof("Pre-pulling images so that they are cached for the tests.")
		updateImageWhiteList()
		err := PrePullAllImages()
		Expect(err).ShouldNot(HaveOccurred())
	}

	// TODO(yifan): Temporary workaround to disable coreos from auto restart
	// by masking the locksmithd.
	// We should mask locksmithd when provisioning the machine.
	maskLocksmithdOnCoreos()

	if *startServices {
		// If the services are expected to stop after test, they should monitor the test process.
		// If the services are expected to keep running after test, they should not monitor the test process.
		e2es = services.NewE2EServices(*stopServices)
		Expect(e2es.Start()).To(Succeed(), "should be able to start node services.")
		klog.Infof("Node services started.  Running tests...")
	} else {
		klog.Infof("Running tests without starting services.")
	}

	klog.Infof("Wait for the node to be ready")
	waitForNodeReady()

	// Reference common test to make the import valid.
	commontest.CurrentSuite = commontest.NodeE2E

	return nil
}, func([]byte) {
	// update test context with node configuration.
	Expect(updateTestContext()).To(Succeed(), "update test context with node config.")
})

// Tear down the kubelet on the node
var _ = SynchronizedAfterSuite(func() {}, func() {
	if e2es != nil {
		if *startServices && *stopServices {
			klog.Infof("Stopping node services...")
			e2es.Stop()
		}
	}

	klog.Infof("Tests Finished")
})

// validateSystem runs system validation in a separate process and returns error if validation fails.
func validateSystem() error {
	testBin, err := osext.Executable()
	if err != nil {
		return fmt.Errorf("can't get current binary: %v", err)
	}
	// Pass all flags into the child process, so that it will see the same flag set.
	output, err := exec.Command(testBin, append([]string{"--system-validate-mode"}, os.Args[1:]...)...).CombinedOutput()
	// The output of system validation should have been formatted, directly print here.
	fmt.Print(string(output))
	if err != nil {
		return fmt.Errorf("system validation failed: %v", err)
	}
	return nil
}

func maskLocksmithdOnCoreos() {
	data, err := ioutil.ReadFile("/etc/os-release")
	if err != nil {
		// Not all distros contain this file.
		klog.Infof("Could not read /etc/os-release: %v", err)
		return
	}
	if bytes.Contains(data, []byte("ID=coreos")) {
		output, err := exec.Command("systemctl", "mask", "--now", "locksmithd").CombinedOutput()
		Expect(err).NotTo(HaveOccurred(), fmt.Sprintf("should be able to mask locksmithd - output: %q", string(output)))
		klog.Infof("Locksmithd is masked successfully")
	}
}

func waitForNodeReady() {
	const (
		// nodeReadyTimeout is the time to wait for node to become ready.
		nodeReadyTimeout = 2 * time.Minute
		// nodeReadyPollInterval is the interval to check node ready.
		nodeReadyPollInterval = 1 * time.Second
	)
	client, err := getAPIServerClient()
	Expect(err).NotTo(HaveOccurred(), "should be able to get apiserver client.")
	Eventually(func() error {
		node, err := getNode(client)
		if err != nil {
			return fmt.Errorf("failed to get node: %v", err)
		}
		if !isNodeReady(node) {
			return fmt.Errorf("node is not ready: %+v", node)
		}
		return nil
	}, nodeReadyTimeout, nodeReadyPollInterval).Should(Succeed())
}

// updateTestContext updates the test context with the node name.
// TODO(random-liu): Using dynamic kubelet configuration feature to
// update test context with node configuration.
func updateTestContext() error {
	setExtraEnvs()
	updateImageWhiteList()

	client, err := getAPIServerClient()
	if err != nil {
		return fmt.Errorf("failed to get apiserver client: %v", err)
	}
	// Update test context with current node object.
	node, err := getNode(client)
	if err != nil {
		return fmt.Errorf("failed to get node: %v", err)
	}
	framework.TestContext.NodeName = node.Name // Set node name.
	// Update test context with current kubelet configuration.
	// This assumes all tests which dynamically change kubelet configuration
	// must: 1) run in serial; 2) restore kubelet configuration after test.
	kubeletCfg, err := getCurrentKubeletConfig()
	if err != nil {
		return fmt.Errorf("failed to get kubelet configuration: %v", err)
	}
	framework.TestContext.KubeletConfig = *kubeletCfg // Set kubelet config
	return nil
}

// getNode gets node object from the apiserver.
func getNode(c *clientset.Clientset) (*v1.Node, error) {
	nodes, err := c.CoreV1().Nodes().List(metav1.ListOptions{})
	Expect(err).NotTo(HaveOccurred(), "should be able to list nodes.")
	if nodes == nil {
		return nil, fmt.Errorf("the node list is nil.")
	}
	Expect(len(nodes.Items) > 1).NotTo(BeTrue(), "the number of nodes is more than 1.")
	if len(nodes.Items) == 0 {
		return nil, fmt.Errorf("empty node list: %+v", nodes)
	}
	return &nodes.Items[0], nil
}

// getAPIServerClient gets a apiserver client.
func getAPIServerClient() (*clientset.Clientset, error) {
	config, err := framework.LoadConfig()
	if err != nil {
		return nil, fmt.Errorf("failed to load config: %v", err)
	}
	client, err := clientset.NewForConfig(config)
	if err != nil {
		return nil, fmt.Errorf("failed to create client: %v", err)
	}
	return client, nil
}

// loadSystemSpecFromFile returns the system spec from the file with the
// filename.
func loadSystemSpecFromFile(filename string) (*system.SysSpec, error) {
	b, err := ioutil.ReadFile(filename)
	if err != nil {
		return nil, err
	}
	data, err := utilyaml.ToJSON(b)
	if err != nil {
		return nil, err
	}
	spec := new(system.SysSpec)
	if err := json.Unmarshal(data, spec); err != nil {
		return nil, err
	}
	return spec, nil
}

// isNodeReady returns true if a node is ready; false otherwise.
func isNodeReady(node *v1.Node) bool {
	for _, c := range node.Status.Conditions {
		if c.Type == v1.NodeReady {
			return c.Status == v1.ConditionTrue
		}
	}
	return false
}

func setExtraEnvs() {
	for name, value := range framework.TestContext.ExtraEnvs {
		os.Setenv(name, value)
	}
}
<|endoftext|>


<commit_before>package pipelineexecution

import (
	"context"
	"github.com/rancher/norman/controller"
	"github.com/rancher/rancher/pkg/pipeline/engine"
	"github.com/rancher/rancher/pkg/pipeline/utils"
	"github.com/rancher/rancher/pkg/ref"
	"github.com/rancher/rancher/pkg/ticker"
	"github.com/rancher/types/apis/project.cattle.io/v3"
	"github.com/sirupsen/logrus"
	apierrors "k8s.io/apimachinery/pkg/api/errors"
	"k8s.io/apimachinery/pkg/labels"
	"time"
)

// This controller is responsible for updating pipeline execution states
// by syncing with the pipeline engine. It also detect executors' status
// and do the actual run pipeline when they are ready

const (
	syncStateInterval = 5 * time.Second
)

type ExecutionStateSyncer struct {
	clusterName string

	pipelineLister          v3.PipelineLister
	pipelines               v3.PipelineInterface
	pipelineExecutionLister v3.PipelineExecutionLister
	pipelineExecutions      v3.PipelineExecutionInterface
	pipelineEngine          engine.PipelineEngine
}

func (s *ExecutionStateSyncer) sync(ctx context.Context, syncInterval time.Duration) {
	for range ticker.Context(ctx, syncInterval) {
		s.syncState()
	}

}

func (s *ExecutionStateSyncer) syncState() {
	set := labels.Set(map[string]string{utils.PipelineFinishLabel: "false"})
	allExecutions, err := s.pipelineExecutionLister.List("", set.AsSelector())
	if err != nil {
		logrus.Errorf("Error listing PipelineExecutions - %v", err)
		return
	}
	executions := []*v3.PipelineExecution{}
	for _, e := range allExecutions {
		if controller.ObjectInCluster(s.clusterName, e) {
			executions = append(executions, e)
		}
	}
	if len(executions) < 1 {
		return
	}

	for _, execution := range executions {
		if v3.PipelineExecutionConditionInitialized.IsUnknown(execution) {
			s.checkAndRun(execution)
		} else if v3.PipelineExecutionConditionInitialized.IsTrue(execution) {
			e := execution.DeepCopy()
			updated, err := s.pipelineEngine.SyncExecution(e)
			if err != nil {
				logrus.Errorf("got error in syncExecution: %v", err)
				v3.PipelineExecutionConditionBuilt.False(e)
				v3.PipelineExecutionConditionBuilt.ReasonAndMessageFromError(e, err)
				e.Status.ExecutionState = utils.StateFailed
				updated = true
			}
			if updated {
				if err := s.updateExecutionAndLastRunState(e); err != nil {
					logrus.Error(err)
					continue
				}
			}
		} else {
			if err := s.updateExecutionAndLastRunState(execution); err != nil {
				logrus.Errorf("Error update pipeline execution - %v", err)
			}
		}
	}

	logrus.Debugf("Sync pipeline execution state complete")
}

func (s *ExecutionStateSyncer) checkAndRun(execution *v3.PipelineExecution) {
	ready, err := s.pipelineEngine.PreCheck(execution)
	if err != nil {
		e := execution.DeepCopy()
		v3.PipelineExecutionConditionBuilt.False(e)
		v3.PipelineExecutionConditionBuilt.ReasonAndMessageFromError(e, err)
		e.Status.ExecutionState = utils.StateFailed
		if err := s.updateExecutionAndLastRunState(e); err != nil {
			logrus.Error(err)
		}
	}
	if ready {
		e := execution.DeepCopy()
		if err := s.pipelineEngine.RunPipelineExecution(e); err != nil {
			v3.PipelineExecutionConditionProvisioned.False(e)
			v3.PipelineExecutionConditionProvisioned.ReasonAndMessageFromError(e, err)
			e.Status.ExecutionState = utils.StateFailed
			if err := s.updateExecutionAndLastRunState(e); err != nil {
				logrus.Error(err)
			}
			return
		}
		v3.PipelineExecutionConditionInitialized.True(e)
		v3.PipelineExecutionConditionProvisioned.CreateUnknownIfNotExists(e)
		v3.PipelineExecutionConditionProvisioned.Message(e, "Assigning jobs to pipeline engine")
		if err := s.updateExecutionAndLastRunState(e); err != nil {
			logrus.Error(err)
		}
	}
	if v3.PipelineExecutionConditionInitialized.GetMessage(execution) == "" {
		e := execution.DeepCopy()
		v3.PipelineExecutionConditionInitialized.Message(e, "setting up jenkins")
		if err := s.updateExecutionAndLastRunState(e); err != nil {
			logrus.Error(err)
		}
	}

}

func (s *ExecutionStateSyncer) updateExecutionAndLastRunState(execution *v3.PipelineExecution) error {
	if v3.PipelineExecutionConditionInitialized.IsFalse(execution) || v3.PipelineExecutionConditionProvisioned.IsFalse(execution) ||
		v3.PipelineExecutionConditionBuilt.IsFalse(execution) {
		execution.Labels[utils.PipelineFinishLabel] = "true"

		if execution.Status.Ended == "" {
			execution.Status.Ended = time.Now().Format(time.RFC3339)
		}
	}

	if _, err := s.pipelineExecutions.Update(execution); err != nil {
		return err
	}

	//check and update lastrunstate of the pipeline when necessary
	ns, name := ref.Parse(execution.Spec.PipelineName)
	p, err := s.pipelineLister.Get(ns, name)
	if apierrors.IsNotFound(err) {
		logrus.Warningf("pipeline of execution '%s' is not found", execution.Name)
		return nil
	} else if err != nil {
		return err
	}

	if p.Status.LastExecutionID == execution.Namespace+":"+execution.Name &&
		p.Status.LastRunState != execution.Status.ExecutionState {
		p.Status.LastRunState = execution.Status.ExecutionState
		if _, err := s.pipelines.Update(p); err != nil {
			return err
		}
	}
	return nil
}
<commit_msg>update pipeline set up message<commit_after>package pipelineexecution

import (
	"context"
	"github.com/rancher/norman/controller"
	"github.com/rancher/rancher/pkg/pipeline/engine"
	"github.com/rancher/rancher/pkg/pipeline/utils"
	"github.com/rancher/rancher/pkg/ref"
	"github.com/rancher/rancher/pkg/ticker"
	"github.com/rancher/types/apis/project.cattle.io/v3"
	"github.com/sirupsen/logrus"
	apierrors "k8s.io/apimachinery/pkg/api/errors"
	"k8s.io/apimachinery/pkg/labels"
	"time"
)

// This controller is responsible for updating pipeline execution states
// by syncing with the pipeline engine. It also detect executors' status
// and do the actual run pipeline when they are ready

const (
	syncStateInterval = 5 * time.Second
)

type ExecutionStateSyncer struct {
	clusterName string

	pipelineLister          v3.PipelineLister
	pipelines               v3.PipelineInterface
	pipelineExecutionLister v3.PipelineExecutionLister
	pipelineExecutions      v3.PipelineExecutionInterface
	pipelineEngine          engine.PipelineEngine
}

func (s *ExecutionStateSyncer) sync(ctx context.Context, syncInterval time.Duration) {
	for range ticker.Context(ctx, syncInterval) {
		s.syncState()
	}

}

func (s *ExecutionStateSyncer) syncState() {
	set := labels.Set(map[string]string{utils.PipelineFinishLabel: "false"})
	allExecutions, err := s.pipelineExecutionLister.List("", set.AsSelector())
	if err != nil {
		logrus.Errorf("Error listing PipelineExecutions - %v", err)
		return
	}
	executions := []*v3.PipelineExecution{}
	for _, e := range allExecutions {
		if controller.ObjectInCluster(s.clusterName, e) {
			executions = append(executions, e)
		}
	}
	if len(executions) < 1 {
		return
	}

	for _, execution := range executions {
		if v3.PipelineExecutionConditionInitialized.IsUnknown(execution) {
			s.checkAndRun(execution)
		} else if v3.PipelineExecutionConditionInitialized.IsTrue(execution) {
			e := execution.DeepCopy()
			updated, err := s.pipelineEngine.SyncExecution(e)
			if err != nil {
				logrus.Errorf("got error in syncExecution: %v", err)
				v3.PipelineExecutionConditionBuilt.False(e)
				v3.PipelineExecutionConditionBuilt.ReasonAndMessageFromError(e, err)
				e.Status.ExecutionState = utils.StateFailed
				updated = true
			}
			if updated {
				if err := s.updateExecutionAndLastRunState(e); err != nil {
					logrus.Error(err)
					continue
				}
			}
		} else {
			if err := s.updateExecutionAndLastRunState(execution); err != nil {
				logrus.Errorf("Error update pipeline execution - %v", err)
			}
		}
	}

	logrus.Debugf("Sync pipeline execution state complete")
}

func (s *ExecutionStateSyncer) checkAndRun(execution *v3.PipelineExecution) {
	ready, err := s.pipelineEngine.PreCheck(execution)
	if err != nil {
		e := execution.DeepCopy()
		v3.PipelineExecutionConditionBuilt.False(e)
		v3.PipelineExecutionConditionBuilt.ReasonAndMessageFromError(e, err)
		e.Status.ExecutionState = utils.StateFailed
		if err := s.updateExecutionAndLastRunState(e); err != nil {
			logrus.Error(err)
		}
	}
	if ready {
		e := execution.DeepCopy()
		if err := s.pipelineEngine.RunPipelineExecution(e); err != nil {
			v3.PipelineExecutionConditionProvisioned.False(e)
			v3.PipelineExecutionConditionProvisioned.ReasonAndMessageFromError(e, err)
			e.Status.ExecutionState = utils.StateFailed
			if err := s.updateExecutionAndLastRunState(e); err != nil {
				logrus.Error(err)
			}
			return
		}
		v3.PipelineExecutionConditionInitialized.True(e)
		v3.PipelineExecutionConditionProvisioned.CreateUnknownIfNotExists(e)
		v3.PipelineExecutionConditionProvisioned.Message(e, "Assigning jobs to pipeline engine")
		if err := s.updateExecutionAndLastRunState(e); err != nil {
			logrus.Error(err)
		}
	}
	if v3.PipelineExecutionConditionInitialized.GetMessage(execution) == "" {
		e := execution.DeepCopy()
		v3.PipelineExecutionConditionInitialized.Message(e, "Setting up jenkins. If it is not deployed, this can take a few minutes.")
		if err := s.updateExecutionAndLastRunState(e); err != nil {
			logrus.Error(err)
		}
	}

}

func (s *ExecutionStateSyncer) updateExecutionAndLastRunState(execution *v3.PipelineExecution) error {
	if v3.PipelineExecutionConditionInitialized.IsFalse(execution) || v3.PipelineExecutionConditionProvisioned.IsFalse(execution) ||
		v3.PipelineExecutionConditionBuilt.IsFalse(execution) {
		execution.Labels[utils.PipelineFinishLabel] = "true"

		if execution.Status.Ended == "" {
			execution.Status.Ended = time.Now().Format(time.RFC3339)
		}
	}

	if _, err := s.pipelineExecutions.Update(execution); err != nil {
		return err
	}

	//check and update lastrunstate of the pipeline when necessary
	ns, name := ref.Parse(execution.Spec.PipelineName)
	p, err := s.pipelineLister.Get(ns, name)
	if apierrors.IsNotFound(err) {
		logrus.Warningf("pipeline of execution '%s' is not found", execution.Name)
		return nil
	} else if err != nil {
		return err
	}

	if p.Status.LastExecutionID == execution.Namespace+":"+execution.Name &&
		p.Status.LastRunState != execution.Status.ExecutionState {
		p.Status.LastRunState = execution.Status.ExecutionState
		if _, err := s.pipelines.Update(p); err != nil {
			return err
		}
	}
	return nil
}
<|endoftext|>


<commit_before>package backend

import (
	"testing"
)

func TestConfigLoading(t *testing.T) {
	editor := GetEditor()
	editor.loadSetting("testdata/Default.sublime-settings")

	if editor.Settings().Has("tab_size") != true {
		t.Error("Expected editor settings to have tab_size")
	}

	tab_size := editor.Settings().Get("tab_size").(float64)
	if tab_size != 4 {
		t.Errorf("Expected tab_size to equal 4, got: %v", tab_size)
	}
}
<commit_msg>Add copyright information to editor_test.go<commit_after>// Copyright 2013 The lime Authors.
// Use of this source code is governed by a 2-clause
// BSD-style license that can be found in the LICENSE file.

package backend

import (
	"testing"
)

func TestConfigLoading(t *testing.T) {
	editor := GetEditor()
	editor.loadSetting("testdata/Default.sublime-settings")

	if editor.Settings().Has("tab_size") != true {
		t.Error("Expected editor settings to have tab_size")
	}

	tab_size := editor.Settings().Get("tab_size").(float64)
	if tab_size != 4 {
		t.Errorf("Expected tab_size to equal 4, got: %v", tab_size)
	}
}
<|endoftext|>


<commit_before>// Package backoffutil provides a wrapper above github.com/cenk/backoff.Retry
// that checks the error returned and only retries retryable errors.
//
// For the sake of simplicity, the backoff strategy is the default exponential backoff.
package backoffutil

import (
	"github.com/cenk/backoff"
	"github.com/objenious/errorutil"
)

// Retry does exponential backoff.
// Backoff will trigger if an error is returned, implements Retryabler AND the error is retryable.
func Retry(fn func() error) error {
	var finalerr error
	err := backoff.Retry(func() error {
		finalerr = fn()
		if errorutil.IsRetryable(finalerr) {
			return finalerr
		}
		return nil
	}, backoff.NewExponentialBackOff())

	if err != nil {
		return err
	}
	return finalerr
}
<commit_msg>backoffutil: fix import path<commit_after>// Package backoffutil provides a wrapper above github.com/cenk/backoff.Retry
// that checks the error returned and only retries retryable errors.
//
// For the sake of simplicity, the backoff strategy is the default exponential backoff.
package backoffutil

import (
	"github.com/cenkalti/backoff"
	"github.com/objenious/errorutil"
)

// Retry does exponential backoff.
// Backoff will trigger if an error is returned, implements Retryabler AND the error is retryable.
func Retry(fn func() error) error {
	var finalerr error
	err := backoff.Retry(func() error {
		finalerr = fn()
		if errorutil.IsRetryable(finalerr) {
			return finalerr
		}
		return nil
	}, backoff.NewExponentialBackOff())

	if err != nil {
		return err
	}
	return finalerr
}
<|endoftext|>


<commit_before>/*
Copyright 2021 The Kubernetes Authors.

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

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

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

package do

import (
	"errors"
	"math/rand"
	"time"
)

var allZones = []string{
	"nyc1",
	"nyc3",
	"sfo3",
	"tor1",
	"lon1",
	"sgp1",
	"blr1",
	"sfo3",
}

// ErrNoEligibleRegion indicates the requested number of zones is not available in any region
var ErrNoEligibleRegion = errors.New("No eligible DO region found with enough zones")

// ErrMoreThanOneZone indicates the requested number of zones was more than one
var ErrMoreThanOneZone = errors.New("More than 1 zone is chosen. DO only works with 1 zone")

// RandomZones returns a random set of availability zones within a region
func RandomZones(count int) ([]string, error) {
	if count > 1 {
		return nil, ErrMoreThanOneZone
	}

	rand.Seed(time.Now().UnixNano())
	n := rand.Intn(1000) % len(allZones)
	chosenZone := allZones[n]

	chosenZones := make([]string, 0)
	chosenZones = append(chosenZones, chosenZone)

	return chosenZones, nil
}
<commit_msg>Add more supported regions for e2e tests<commit_after>/*
Copyright 2021 The Kubernetes Authors.

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

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

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

package do

import (
	"errors"
	"math/rand"
	"time"
)

var allZones = []string{
	"nyc1",
	"nyc2",
	"nyc3",
	"tor1",
	"lon1",
	"sgp1",
	"blr1",
	"sfo1",
	"sfo3",
	"ams2",
	"ams3",
	"fra1",
}

// ErrNoEligibleRegion indicates the requested number of zones is not available in any region
var ErrNoEligibleRegion = errors.New("No eligible DO region found with enough zones")

// ErrMoreThanOneZone indicates the requested number of zones was more than one
var ErrMoreThanOneZone = errors.New("More than 1 zone is chosen. DO only works with 1 zone")

// RandomZones returns a random set of availability zones within a region
func RandomZones(count int) ([]string, error) {
	if count > 1 {
		return nil, ErrMoreThanOneZone
	}

	rand.Seed(time.Now().UnixNano())
	n := rand.Intn(1000) % len(allZones)
	chosenZone := allZones[n]

	chosenZones := make([]string, 0)
	chosenZones = append(chosenZones, chosenZone)

	return chosenZones, nil
}
<|endoftext|>


<commit_before>package x86_16

import (
	"fmt"
	"syscall"

	co "github.com/lunixbochs/usercorn/go/kernel/common"
	"github.com/lunixbochs/usercorn/go/models"
	uc "github.com/unicorn-engine/unicorn/bindings/go/unicorn"
)

const (
	STACK_BASE = 0x8000
	STACK_SIZE = 0x1000
)

var dosSysNum = map[int]string{
	0x00: "terminate",
	0x01: "char_in",
	0x02: "char_out",
	0x09: "display",
	0x4C: "terminate_with_code",
}

// TODO: Create a reverse map of this for conciseness
var abiMap = map[int][]int{
	0x00: {},
	0x01: {uc.X86_REG_DX},
	0x02: {uc.X86_REG_DX}, // Actually DL
	0x09: {uc.X86_REG_DX, uc.X86_REG_DS},
	0x30: {},
	0x3C: {uc.X86_REG_DX, uc.X86_REG_DS, uc.X86_REG_AL},
	0x3D: {uc.X86_REG_DX, uc.X86_REG_DS, uc.X86_REG_AL},
	0x4C: {uc.X86_REG_AL},
}

type PSP struct {
	CPMExit                     [2]uint8
	FirstFreeSegment            uint16
	Reserved1                   uint8
	CPMCall5Compat              [5]uint8
	OldTSRAddress               uint32
	OldBreakAddress             uint32
	CriticalErrorHandlerAddress uint32
	CallerPSPSegment            uint16
	JobFileTable                [20]uint8
	EnvironmentSegment          uint16
	INT21SSSP                   uint32
	JobFileTableSize            uint16
	JobFileTablePointer         uint32
	PreviousPSP                 uint32
	Reserved2                   uint32
	DOSVersion                  uint16
	Reserved3                   [14]uint8
	DOSFarCall                  [3]uint8
	Reserved4                   uint16
	ExtendedFCB1                [7]uint8
	FCB1                        [16]uint8
	FCB2                        [20]uint8
	CommandLineLength           uint8
	CommandLine                 [127]byte
}

type DosKernel struct {
	*co.KernelBase
}

func (k *DosKernel) Terminate() {
	k.U.Exit(models.ExitStatus(0))
}

func (k *DosKernel) CharIn(buf co.Buf) {
}

func (k *DosKernel) CharOut(char uint16) {
	fmt.Printf("%c", uint8(char&0xFF))
}

func (k *DosKernel) Display(buf co.Buf) {
	// TODO: Read ahead? This'll be slow
	var i uint64
	var mem []uint8
	char := uint8(0)

	for i = 1; char != '$'; i++ {
		mem, _ = k.U.MemRead(buf.Addr, i)
		char = mem[i-1]
	}

	syscall.Write(1, mem[:i-2])
}

func (k *DosKernel) GetDosVersion() {
	k.U.RegWrite(uc.X86_REG_AX, 0x7)
}

func (k *DosKernel) TerminateWithCode(code int) {
	k.U.Exit(models.ExitStatus(code))
}

func NewKernel() *DosKernel {
	return &DosKernel{&co.KernelBase{}}
}

var regNames = []string{
	"ip", "sp", "bp", "ax", "bx", "cx", "dx",
	"si", "di", "flags", "cs", "ds", "es", "ss",
}

func DosInit(u models.Usercorn, args, env []string) error {
	u.RegWrite(u.Arch().SP, STACK_BASE+STACK_SIZE)
	u.SetStackBase(STACK_BASE)
	u.SetStackSize(STACK_SIZE)
	u.SetEntry(0x100)
	return nil
}

func DosSyscall(u models.Usercorn) {
	num, _ := u.RegRead(uc.X86_REG_AH)
	name, _ := dosSysNum[int(num)]
	// TODO: How are registers numbered from here?
	u.Syscall(int(num), name, dosArgs(u, int(num)))
	// TODO: Set error
}

func dosArgs(u models.Usercorn, num int) func(n int) ([]uint64, error) {
	return co.RegArgs(u, abiMap[num])
}

func DosInterrupt(u models.Usercorn, cause uint32) {
	intno := cause & 0xFF
	if intno == 0x21 {
		DosSyscall(u)
	} else if intno == 0x20 {
		u.Syscall(0, "terminate", func(int) ([]uint64, error) { return []uint64{}, nil })
	} else {
		panic(fmt.Sprintf("unhandled X86 interrupt %d", intno))
	}
}
func DosKernels(u models.Usercorn) []interface{} {
	kernel := &DosKernel{&co.KernelBase{}}
	return []interface{}{kernel}
}

func init() {
	Arch.RegisterOS(&models.OS{
		Name:      "DOS",
		Init:      DosInit,
		Interrupt: DosInterrupt,
		Kernels:   DosKernels,
	})
}
<commit_msg>DOS: Add more syscalls + FD table<commit_after>package x86_16

import (
	"errors"
	"fmt"
	"syscall"

	co "github.com/lunixbochs/usercorn/go/kernel/common"
	"github.com/lunixbochs/usercorn/go/models"
	uc "github.com/unicorn-engine/unicorn/bindings/go/unicorn"
)

const (
	STACK_BASE = 0x8000
	STACK_SIZE = 0x1000
	NUM_FDS    = 256

	// Registers
	AH    = uc.X86_REG_AH
	AL    = uc.X86_REG_AL
	AX    = uc.X86_REG_AX
	BH    = uc.X86_REG_BH
	BL    = uc.X86_REG_BL
	BP    = uc.X86_REG_BP
	BX    = uc.X86_REG_BX
	CH    = uc.X86_REG_CH
	CL    = uc.X86_REG_CL
	CS    = uc.X86_REG_CS
	CX    = uc.X86_REG_CX
	DH    = uc.X86_REG_DH
	DI    = uc.X86_REG_DI
	DL    = uc.X86_REG_DL
	DS    = uc.X86_REG_DS
	DX    = uc.X86_REG_DX
	ES    = uc.X86_REG_ES
	FS    = uc.X86_REG_FS
	GS    = uc.X86_REG_GS
	IP    = uc.X86_REG_IP
	SI    = uc.X86_REG_SI
	SP    = uc.X86_REG_SP
	SS    = uc.X86_REG_SS
	FLAGS = uc.X86_REG_EFLAGS
)

func (k *DosKernel) reg16(enum int) uint16 {
	r, _ := k.U.RegRead(enum)
	return uint16(r)
}
func (k *DosKernel) reg8(enum int) uint8 {
	r, _ := k.U.RegRead(enum)
	return uint8(r)
}
func (k *DosKernel) wreg16(enum int, val uint16) {
	k.U.RegWrite(enum, uint64(val))
}
func (k *DosKernel) wreg8(enum int, val uint8) {
	k.U.RegWrite(enum, uint64(val))
}
func (k *DosKernel) setFlagC(set bool) {
	// TODO: Write setFlagX with enum for each flag
	// Unicorn doesn't have the non-extended FLAGS register, so we're
	// dealing with 32 bits here
	flags, _ := k.U.RegRead(FLAGS)
	if set {
		flags |= 1 // CF = 1
	} else {
		flags &= 0xfffffffe // CF = 0
	}
	k.U.RegWrite(FLAGS, flags)
}

var dosSysNum = map[int]string{
	0x00: "terminate",
	0x01: "char_in",
	0x02: "char_out",
	0x09: "display",
	0x30: "get_dos_version",
	0x3C: "create_or_truncate",
	0x3D: "open",
	0x3E: "close",
	0x3F: "read",
	0x40: "write",
	0x4C: "terminate_with_code",
}

// TODO: Create a reverse map of this for conciseness
var abiMap = map[int][]int{
	0x00: {},
	0x01: {DX},
	0x02: {DX}, // Actually DL
	0x09: {DX, DS},
	0x30: {},
	0x3C: {DX, DS, CX},
	0x3D: {DX, DS, AL},
	0x3E: {BX},
	0x3F: {BX, CX, DX, DS},
	0x40: {BX, CX, DX, DS},
	0x4C: {AL},
}

type PSP struct {
	CPMExit                     [2]uint8
	FirstFreeSegment            uint16
	Reserved1                   uint8
	CPMCall5Compat              [5]uint8
	OldTSRAddress               uint32
	OldBreakAddress             uint32
	CriticalErrorHandlerAddress uint32
	CallerPSPSegment            uint16
	JobFileTable                [20]uint8
	EnvironmentSegment          uint16
	INT21SSSP                   uint32
	JobFileTableSize            uint16
	JobFileTablePointer         uint32
	PreviousPSP                 uint32
	Reserved2                   uint32
	DOSVersion                  uint16
	Reserved3                   [14]uint8
	DOSFarCall                  [3]uint8
	Reserved4                   uint16
	ExtendedFCB1                [7]uint8
	FCB1                        [16]uint8
	FCB2                        [20]uint8
	CommandLineLength           uint8
	CommandLine                 [127]byte
}

type DosKernel struct {
	*co.KernelBase
	fds [NUM_FDS]int
}

func (k *DosKernel) readUntilChar(addr uint64, c byte) []byte {
	var mem []byte
	var i uint64
	var char byte = 0

	// TODO: Read ahead? This'll be slow
	for i = 1; char != c || i == 1; i++ {
		mem, _ = k.U.MemRead(addr, i)
		char = mem[i-1]
	}
	return mem[:i-2]
}

func (k *DosKernel) getFd(fd int) (uint16, error) {
	for i := uint16(0); i < NUM_FDS; i++ {
		if k.fds[i] == -1 {
			k.fds[i] = fd
			return i, nil
		}
	}
	return 0xFFFF, errors.New("DOS FD table exhausted")
}

func (k *DosKernel) freeFd(fd int) (int, error) {
	realfd := k.fds[fd]
	if realfd == -1 {
		return 0xFFFF, errors.New("FD not found in FD table")
	}
	k.fds[fd] = -1
	return realfd, nil
}

func (k *DosKernel) Terminate() {
	k.U.Exit(models.ExitStatus(0))
}

func (k *DosKernel) CharIn(buf co.Buf) {
	var char byte
	fmt.Scanf("%c", &char)
	k.U.MemWrite(buf.Addr, []byte{char})
}

func (k *DosKernel) CharOut(char uint16) {
	fmt.Printf("%c", uint8(char&0xFF))
}

func (k *DosKernel) Display(buf co.Buf) {
	mem := k.readUntilChar(buf.Addr, '$')

	syscall.Write(1, mem)
	k.wreg8(AL, 0x24)
}

func (k *DosKernel) GetDosVersion() {
	k.wreg16(AX, 0x7)
}

func (k *DosKernel) openFile(filename string, mode int) {
	realfd, err := syscall.Open(filename, mode, 0)
	if err != nil {
		k.wreg16(AX, 0xFFFF)
		k.setFlagC(true)
		return
	}

	// Find an internal fd number
	dosfd, err := k.getFd(realfd)
	if err != nil {
		k.wreg16(AX, dosfd)
		k.setFlagC(true)
		return
	}
	k.setFlagC(false)
	k.wreg16(AX, dosfd)
}

func (k *DosKernel) CreateOrTruncate(buf co.Buf) {
	filename := string(k.readUntilChar(buf.Addr, '$'))
	k.openFile(filename, syscall.O_CREAT|syscall.O_TRUNC|syscall.O_RDWR)
}

func (k *DosKernel) Open(buf co.Buf, mode int) {
	filename := string(k.readUntilChar(buf.Addr, '\x00'))
	k.openFile(filename, mode)
}

func (k *DosKernel) Close(fd int) {
	// Find and free the internal fd
	realfd, _ := k.freeFd(fd)
	err := syscall.Close(realfd)
	if err != nil {
		k.setFlagC(true)
		// TODO: Set AX to error code
		k.wreg16(AX, 0xFFFF)
	}
	k.setFlagC(false)
	k.wreg16(AX, 0)
}

func (k *DosKernel) Read(fd int, len int, buf co.Buf) {
	mem := make([]byte, len)
	n, err := syscall.Read(fd, mem)
	if err != nil {
		k.setFlagC(true)
		// TODO: Set AX to error code
		k.wreg16(AX, 0xFFFF)
	}
	k.U.MemWrite(buf.Addr, mem)
	k.setFlagC(false)
	k.wreg16(AX, uint16(n))
}

func (k *DosKernel) Write(fd uint, n uint, buf co.Buf, _ int) {
	mem, _ := k.U.MemRead(buf.Addr, uint64(n))
	written, err := syscall.Write(k.fds[fd], mem)
	if err != nil {
		k.setFlagC(true)
		// TODO: Set AX to error code
		k.wreg16(AX, 0xFFFF)
	}
	k.setFlagC(false)
	k.wreg16(AX, uint16(written))
}

func (k *DosKernel) TerminateWithCode(code int) {
	k.U.Exit(models.ExitStatus(code))
}

func NewKernel() *DosKernel {
	k := &DosKernel{
		KernelBase: &co.KernelBase{},
	}

	// Init FDs
	for i := 0; i < NUM_FDS; i++ {
		k.fds[i] = -1
	}
	k.fds[0] = 0
	k.fds[1] = 1
	k.fds[2] = 2
	return k
}

var regNames = []string{
	"ip", "sp", "bp", "ax", "bx", "cx", "dx",
	"si", "di", "flags", "cs", "ds", "es", "ss",
}

func DosInit(u models.Usercorn, args, env []string) error {
	u.RegWrite(u.Arch().SP, STACK_BASE+STACK_SIZE)
	u.SetStackBase(STACK_BASE)
	u.SetStackSize(STACK_SIZE)
	u.SetEntry(0x100)
	return nil
}

func DosSyscall(u models.Usercorn) {
	num, _ := u.RegRead(AH)
	name, _ := dosSysNum[int(num)]
	// TODO: How are registers numbered from here?
	u.Syscall(int(num), name, dosArgs(u, int(num)))
	// TODO: Set error
}

func dosArgs(u models.Usercorn, num int) func(n int) ([]uint64, error) {
	return co.RegArgs(u, abiMap[num])
}

func DosInterrupt(u models.Usercorn, cause uint32) {
	intno := cause & 0xFF
	if intno == 0x21 {
		DosSyscall(u)
	} else if intno == 0x20 {
		u.Syscall(0, "terminate", func(int) ([]uint64, error) { return []uint64{}, nil })
	} else {
		panic(fmt.Sprintf("unhandled X86 interrupt %d", intno))
	}
}
func DosKernels(u models.Usercorn) []interface{} {
	return []interface{}{NewKernel()}
}

func init() {
	Arch.RegisterOS(&models.OS{
		Name:      "DOS",
		Init:      DosInit,
		Interrupt: DosInterrupt,
		Kernels:   DosKernels,
	})
}
<|endoftext|>