<commit_before>package main

import (
	"io/ioutil"
	"os"
	"path/filepath"
	"regexp"
	"testing"
	"time"
)

func TestWatcherNoticesCreateFile(t *testing.T) {
	t.Parallel()

	tmpdir, err := ioutil.TempDir("", "watcher_")
	if err != nil {
		t.Fatal(err)
	}
	defer func() {
		os.RemoveAll(tmpdir)
	}()

	watcher, err := NewWatcher()
	if err != nil {
		t.Fatal(err)
	}
	defer watcher.Close()

	err = watcher.Add(tmpdir)
	if err != nil {
		t.Fatal(err)
	}

	f, err := ioutil.TempFile(tmpdir, "watcher_")
	if err != nil {
		t.Fatal(err)
	}
	defer f.Close()

	select {
	case <-watcher.Events:
	case err := <-watcher.Errors:
		t.Fatal(err)
	case <-time.After(time.Second):
		t.Fatal("Creating file did not generate event")
	}
}

func TestWatcherNoticesWriteFile(t *testing.T) {
	t.Parallel()

	tmpdir, err := ioutil.TempDir("", "watcher_")
	if err != nil {
		t.Fatal(err)
	}
	defer func() {
		os.RemoveAll(tmpdir)
	}()

	f, err := ioutil.TempFile(tmpdir, "watcher_")
	if err != nil {
		t.Fatal(err)
	}
	defer f.Close()
	defer os.Remove(f.Name())

	watcher, err := NewWatcher()
	if err != nil {
		t.Fatal(err)
	}
	defer watcher.Close()

	err = watcher.Add(tmpdir)
	if err != nil {
		t.Fatal(err)
	}

	_, err = f.WriteString("asdffdsakjkdsklsdakjaskjadklaskjlsd")
	if err != nil {
		t.Fatal(err)
	}
	f.Close()

	select {
	case <-watcher.Events:
	case err := <-watcher.Errors:
		t.Fatal(err)
	case <-time.After(time.Second):
		t.Fatal("Writing to file did not generate event")
	}
}

func TestWatcherNoticesRemoveFile(t *testing.T) {
	t.Parallel()

	tmpdir, err := ioutil.TempDir("", "watcher_")
	if err != nil {
		t.Fatal(err)
	}
	defer func() {
		os.RemoveAll(tmpdir)
	}()

	f, err := ioutil.TempFile(tmpdir, "watcher_")
	if err != nil {
		t.Fatal(err)
	}
	defer f.Close()

	watcher, err := NewWatcher()
	if err != nil {
		t.Fatal(err)
	}
	defer watcher.Close()

	err = watcher.Add(tmpdir)
	if err != nil {
		t.Fatal(err)
	}

	err = os.Remove(f.Name())
	if err != nil {
		t.Fatal(err)
	}

	select {
	case <-watcher.Events:
	case err := <-watcher.Errors:
		t.Fatal(err)
	case <-time.After(time.Second):
		t.Fatal("Deleting file did not generate event")
	}
}

func TestWatcherNoticesCreatedSubdirectoryAndChangesWithinIt(t *testing.T) {
	t.Parallel()

	tmpdir, err := ioutil.TempDir("", "watcher_")
	if err != nil {
		t.Fatal(err)
	}
	defer func() {
		os.RemoveAll(tmpdir)
	}()

	watcher, err := NewWatcher()
	if err != nil {
		t.Fatal(err)
	}
	defer watcher.Close()

	err = watcher.Add(tmpdir)
	if err != nil {
		t.Fatal(err)
	}

	subdir, err := ioutil.TempDir(tmpdir, "watcher_")
	if err != nil {
		t.Fatal(err)
	}

	select {
	case <-watcher.Events:
	case err := <-watcher.Errors:
		t.Fatal(err)
	case <-time.After(time.Second):
		t.Fatal("Creating subdirectory did not trigger event")
	}

	f, err := ioutil.TempFile(subdir, "watcher_")
	if err != nil {
		t.Fatal(err)
	}
	defer f.Close()

	select {
	case <-watcher.Events:
	case err := <-watcher.Errors:
		t.Fatal(err)
	case <-time.After(time.Second):
		t.Fatal("Creating file in subdirectory created while watching did not trigger event")
	}
}

func TestWatcherNoticesCreatedFileInSubdirectory(t *testing.T) {
	t.Parallel()

	tmpdir, err := ioutil.TempDir("", "watcher_")
	if err != nil {
		t.Fatal(err)
	}
	defer func() {
		os.RemoveAll(tmpdir)
	}()

	subdir, err := ioutil.TempDir(tmpdir, "watcher_")
	if err != nil {
		t.Fatal(err)
	}

	watcher, err := NewWatcher()
	if err != nil {
		t.Fatal(err)
	}
	defer watcher.Close()

	err = watcher.Add(tmpdir)
	if err != nil {
		t.Fatal(err)
	}

	f, err := ioutil.TempFile(subdir, "watcher_")
	if err != nil {
		t.Fatal(err)
	}
	defer f.Close()

	select {
	case <-watcher.Events:
	case err := <-watcher.Errors:
		t.Fatal(err)
	case <-time.After(time.Second):
		t.Fatal("Creating file in existing subdirectory did not trigger event")
	}
}

func TestWatcherIgnoresChangesThatDoNotMatchInclude(t *testing.T) {
	t.Parallel()

	tmpdir, err := ioutil.TempDir("", "watcher_")
	if err != nil {
		t.Fatal(err)
	}
	defer func() {
		os.RemoveAll(tmpdir)
	}()

	watcher, err := NewWatcher()
	if err != nil {
		t.Fatal(err)
	}
	defer watcher.Close()

	watcher.Include = regexp.MustCompile(`\.go$`)

	err = watcher.Add(tmpdir)
	if err != nil {
		t.Fatal(err)
	}

	f, err := os.Create(filepath.Join(tmpdir, "main.rb"))
	if err != nil {
		t.Fatal(err)
	}
	defer f.Close()

	select {
	case <-watcher.Events:
		t.Fatal("Pattern should have excluded event")
	case err := <-watcher.Errors:
		t.Fatal(err)
	case <-time.After(time.Second):
	}

	f2, err := os.Create(filepath.Join(tmpdir, "main.go"))
	if err != nil {
		t.Fatal(err)
	}
	defer f2.Close()

	select {
	case <-watcher.Events:
	case err := <-watcher.Errors:
		t.Fatal(err)
	case <-time.After(time.Second):
		t.Fatal("Creating file did not generate event")
	}
}

func TestWatcherIgnoresChangesThatMatchExclude(t *testing.T) {
	t.Parallel()

	tmpdir, err := ioutil.TempDir("", "watcher_")
	if err != nil {
		t.Fatal(err)
	}
	defer func() {
		os.RemoveAll(tmpdir)
	}()

	watcher, err := NewWatcher()
	if err != nil {
		t.Fatal(err)
	}
	defer watcher.Close()

	watcher.Exclude = regexp.MustCompile(`\.js$`)

	err = watcher.Add(tmpdir)
	if err != nil {
		t.Fatal(err)
	}

	f, err := os.Create(filepath.Join(tmpdir, "main.js"))
	if err != nil {
		t.Fatal(err)
	}
	defer f.Close()

	select {
	case <-watcher.Events:
		t.Fatal("Exclude should have excluded event")
	case err := <-watcher.Errors:
		t.Fatal(err)
	case <-time.After(time.Second):
	}

	f2, err := os.Create(filepath.Join(tmpdir, "main.go"))
	if err != nil {
		t.Fatal(err)
	}
	defer f2.Close()

	select {
	case <-watcher.Events:
	case err := <-watcher.Errors:
		t.Fatal(err)
	case <-time.After(time.Second):
		t.Fatal("Creating file did not generate event")
	}
}
<commit_msg>Fix test on Windows<commit_after>package main

import (
	"io/ioutil"
	"os"
	"path/filepath"
	"regexp"
	"testing"
	"time"
)

func TestWatcherNoticesCreateFile(t *testing.T) {
	t.Parallel()

	tmpdir, err := ioutil.TempDir("", "watcher_")
	if err != nil {
		t.Fatal(err)
	}
	defer func() {
		os.RemoveAll(tmpdir)
	}()

	watcher, err := NewWatcher()
	if err != nil {
		t.Fatal(err)
	}
	defer watcher.Close()

	err = watcher.Add(tmpdir)
	if err != nil {
		t.Fatal(err)
	}

	f, err := ioutil.TempFile(tmpdir, "watcher_")
	if err != nil {
		t.Fatal(err)
	}
	defer f.Close()

	select {
	case <-watcher.Events:
	case err := <-watcher.Errors:
		t.Fatal(err)
	case <-time.After(time.Second):
		t.Fatal("Creating file did not generate event")
	}
}

func TestWatcherNoticesWriteFile(t *testing.T) {
	t.Parallel()

	tmpdir, err := ioutil.TempDir("", "watcher_")
	if err != nil {
		t.Fatal(err)
	}
	defer func() {
		os.RemoveAll(tmpdir)
	}()

	f, err := ioutil.TempFile(tmpdir, "watcher_")
	if err != nil {
		t.Fatal(err)
	}
	defer f.Close()
	defer os.Remove(f.Name())

	watcher, err := NewWatcher()
	if err != nil {
		t.Fatal(err)
	}
	defer watcher.Close()

	err = watcher.Add(tmpdir)
	if err != nil {
		t.Fatal(err)
	}

	_, err = f.WriteString("asdffdsakjkdsklsdakjaskjadklaskjlsd")
	if err != nil {
		t.Fatal(err)
	}
	f.Close()

	select {
	case <-watcher.Events:
	case err := <-watcher.Errors:
		t.Fatal(err)
	case <-time.After(time.Second):
		t.Fatal("Writing to file did not generate event")
	}
}

func TestWatcherNoticesRemoveFile(t *testing.T) {
	t.Parallel()

	tmpdir, err := ioutil.TempDir("", "watcher_")
	if err != nil {
		t.Fatal(err)
	}
	defer func() {
		os.RemoveAll(tmpdir)
	}()

	f, err := ioutil.TempFile(tmpdir, "watcher_")
	if err != nil {
		t.Fatal(err)
	}
	f.Close()

	watcher, err := NewWatcher()
	if err != nil {
		t.Fatal(err)
	}
	defer watcher.Close()

	err = watcher.Add(tmpdir)
	if err != nil {
		t.Fatal(err)
	}

	err = os.Remove(f.Name())
	if err != nil {
		t.Fatal(err)
	}

	select {
	case <-watcher.Events:
	case err := <-watcher.Errors:
		t.Fatal(err)
	case <-time.After(time.Second):
		t.Fatal("Deleting file did not generate event")
	}
}

func TestWatcherNoticesCreatedSubdirectoryAndChangesWithinIt(t *testing.T) {
	t.Parallel()

	tmpdir, err := ioutil.TempDir("", "watcher_")
	if err != nil {
		t.Fatal(err)
	}
	defer func() {
		os.RemoveAll(tmpdir)
	}()

	watcher, err := NewWatcher()
	if err != nil {
		t.Fatal(err)
	}
	defer watcher.Close()

	err = watcher.Add(tmpdir)
	if err != nil {
		t.Fatal(err)
	}

	subdir, err := ioutil.TempDir(tmpdir, "watcher_")
	if err != nil {
		t.Fatal(err)
	}

	select {
	case <-watcher.Events:
	case err := <-watcher.Errors:
		t.Fatal(err)
	case <-time.After(time.Second):
		t.Fatal("Creating subdirectory did not trigger event")
	}

	f, err := ioutil.TempFile(subdir, "watcher_")
	if err != nil {
		t.Fatal(err)
	}
	defer f.Close()

	select {
	case <-watcher.Events:
	case err := <-watcher.Errors:
		t.Fatal(err)
	case <-time.After(time.Second):
		t.Fatal("Creating file in subdirectory created while watching did not trigger event")
	}
}

func TestWatcherNoticesCreatedFileInSubdirectory(t *testing.T) {
	t.Parallel()

	tmpdir, err := ioutil.TempDir("", "watcher_")
	if err != nil {
		t.Fatal(err)
	}
	defer func() {
		os.RemoveAll(tmpdir)
	}()

	subdir, err := ioutil.TempDir(tmpdir, "watcher_")
	if err != nil {
		t.Fatal(err)
	}

	watcher, err := NewWatcher()
	if err != nil {
		t.Fatal(err)
	}
	defer watcher.Close()

	err = watcher.Add(tmpdir)
	if err != nil {
		t.Fatal(err)
	}

	f, err := ioutil.TempFile(subdir, "watcher_")
	if err != nil {
		t.Fatal(err)
	}
	defer f.Close()

	select {
	case <-watcher.Events:
	case err := <-watcher.Errors:
		t.Fatal(err)
	case <-time.After(time.Second):
		t.Fatal("Creating file in existing subdirectory did not trigger event")
	}
}

func TestWatcherIgnoresChangesThatDoNotMatchInclude(t *testing.T) {
	t.Parallel()

	tmpdir, err := ioutil.TempDir("", "watcher_")
	if err != nil {
		t.Fatal(err)
	}
	defer func() {
		os.RemoveAll(tmpdir)
	}()

	watcher, err := NewWatcher()
	if err != nil {
		t.Fatal(err)
	}
	defer watcher.Close()

	watcher.Include = regexp.MustCompile(`\.go$`)

	err = watcher.Add(tmpdir)
	if err != nil {
		t.Fatal(err)
	}

	f, err := os.Create(filepath.Join(tmpdir, "main.rb"))
	if err != nil {
		t.Fatal(err)
	}
	defer f.Close()

	select {
	case <-watcher.Events:
		t.Fatal("Pattern should have excluded event")
	case err := <-watcher.Errors:
		t.Fatal(err)
	case <-time.After(time.Second):
	}

	f2, err := os.Create(filepath.Join(tmpdir, "main.go"))
	if err != nil {
		t.Fatal(err)
	}
	defer f2.Close()

	select {
	case <-watcher.Events:
	case err := <-watcher.Errors:
		t.Fatal(err)
	case <-time.After(time.Second):
		t.Fatal("Creating file did not generate event")
	}
}

func TestWatcherIgnoresChangesThatMatchExclude(t *testing.T) {
	t.Parallel()

	tmpdir, err := ioutil.TempDir("", "watcher_")
	if err != nil {
		t.Fatal(err)
	}
	defer func() {
		os.RemoveAll(tmpdir)
	}()

	watcher, err := NewWatcher()
	if err != nil {
		t.Fatal(err)
	}
	defer watcher.Close()

	watcher.Exclude = regexp.MustCompile(`\.js$`)

	err = watcher.Add(tmpdir)
	if err != nil {
		t.Fatal(err)
	}

	f, err := os.Create(filepath.Join(tmpdir, "main.js"))
	if err != nil {
		t.Fatal(err)
	}
	defer f.Close()

	select {
	case <-watcher.Events:
		t.Fatal("Exclude should have excluded event")
	case err := <-watcher.Errors:
		t.Fatal(err)
	case <-time.After(time.Second):
	}

	f2, err := os.Create(filepath.Join(tmpdir, "main.go"))
	if err != nil {
		t.Fatal(err)
	}
	defer f2.Close()

	select {
	case <-watcher.Events:
	case err := <-watcher.Errors:
		t.Fatal(err)
	case <-time.After(time.Second):
		t.Fatal("Creating file did not generate event")
	}
}
<|endoftext|>


<commit_before>package assertions

import (
	"fmt"
	"strconv"

	"ci.guzzler.io/guzzler/corcel/core"
	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
)

type AssertionTestCase struct {
	Actual               interface{}
	Instance             interface{}
	ActualStringNumber   bool
	InstanceStringNumber bool
}

func NewAsssertionTestCase(actual interface{}, instance interface{}) (newInstance AssertionTestCase) {
	newInstance.Actual = actual
	newInstance.Instance = instance
	switch actualType := actual.(type) {
	case string:
		_, err := strconv.ParseFloat(actualType, 64)
		if err == nil {
			newInstance.ActualStringNumber = true
		}
	}
	switch instanceType := instance.(type) {
	case string:
		_, err := strconv.ParseFloat(instanceType, 64)
		if err == nil {
			newInstance.InstanceStringNumber = true
		}
	}
	return
}

var _ = FDescribe("Assertions", func() {

	key := "some:key"

	assert := func(testCases []AssertionTestCase, test func(actual interface{}, instance interface{})) {

		for _, testCase := range testCases {
			actualValue := testCase.Actual
			instanceValue := testCase.Instance
			testName := fmt.Sprintf("When Actual is of type %T and Instance is of type %T", actualValue, instanceValue)
			if testCase.ActualStringNumber {
				testName = fmt.Sprintf("%s. Actual value is a STRING NUMBER in this case", testName)
			}
			if testCase.InstanceStringNumber {
				testName = fmt.Sprintf("%s. Instance value is a STRING NUMBER in this case", testName)
			}
			It(testName, func() {
				test(actualValue, instanceValue)
			})
		}
	}

	/*
	   Using this test function and the test cases we get a nice readable output.  If you run ginkgo -v here is an example of the output

	   Assertions Greater Than Succeeds
	     When Actual is of type int and Instance is of type string. Instance value is a STRING NUMBER
	*/

	var nilValue interface{}

	FContext("Greater Than", func() {

		Context("Succeeds", func() {
			var testCases = []AssertionTestCase{
				NewAsssertionTestCase(float64(1.1), nilValue),
				NewAsssertionTestCase(int(1), nilValue),
				NewAsssertionTestCase("1", nilValue),
				NewAsssertionTestCase("a", nilValue),
				NewAsssertionTestCase(float64(5), float64(1)),
				NewAsssertionTestCase(int(5), float64(1)),
				NewAsssertionTestCase("2.2", float64(1)),
				NewAsssertionTestCase(int(5), int(1)),
				NewAsssertionTestCase("5", int(1)),
				NewAsssertionTestCase("abc", "a"),
				NewAsssertionTestCase(float64(1.3), "1.2"),
				NewAsssertionTestCase(int(3), "1"),
				NewAsssertionTestCase("3.1", "2"),
			}

			assert(testCases, func(actualValue interface{}, instanceValue interface{}) {
				key := "some:key"
				executionResult := core.ExecutionResult{
					key: actualValue,
				}

				assertion := GreaterThanAssertion{
					Key:   key,
					Value: instanceValue,
				}

				result := assertion.Assert(executionResult)
				Expect(result["result"]).To(Equal(true))
				Expect(result["message"]).To(BeNil())
			})
		})

		FContext("Fails", func() {
			var testCases = []AssertionTestCase{
				NewAsssertionTestCase(nilValue, nilValue),
				NewAsssertionTestCase(nilValue, int(5)),
				NewAsssertionTestCase(nilValue, "5.1"),
				NewAsssertionTestCase(nilValue, "fubar"),
				NewAsssertionTestCase(nilValue, float64(6.1)),
			}

			assert(testCases, func(actualValue interface{}, instanceValue interface{}) {
				executionResult := core.ExecutionResult{
					key: actualValue,
				}

				assertion := GreaterThanAssertion{
					Key:   key,
					Value: instanceValue,
				}

				result := assertion.Assert(executionResult)
				Expect(result["result"]).To(Equal(false))
				Expect(result["message"]).To(Equal(fmt.Sprintf("FAIL: %v is not greater %v", actualValue, instanceValue)))
			})
			/*
				PIt("When Actual is nil and Instance is float64", func() {

				})

				PIt("When Actual is float64 and Instance is float64", func() {

				})

				PIt("When Actual is int and Instance is float64", func() {

				})

				PIt("When Actual is string-number and Instance is float64", func() {

				})

				PIt("When Actual is float64 and Instance is int", func() {

				})

				PIt("When Actual is float64 and Instance is int", func() {

				})

				PIt("When Actual is int and Instance is int", func() {

				})

				PIt("When Actual is string-number and Instance is int", func() {

				})

				PIt("When Actual is string and Instance is int", func() {

				})

				PIt("When Actual is string and Instance is float64", func() {

				})

				PIt("When Actual is string and Instance is string-number", func() {

				})

				PIt("When Actual is string and Instance is string", func() {

				})

				PIt("When Actual is float64 and Instance is string", func() {

				})

				PIt("When Actual is int and Instance is string", func() {

				})

				PIt("When Actual string-number int and Instance is string", func() {

				})
			*/
		})

	})

	Context("Not Equal Assertion", func() {

		It("Succeeds", func() {
			executionResult := core.ExecutionResult{
				key: 8,
			}

			assertion := NotEqualAssertion{
				Key:   key,
				Value: 7,
			}

			result := assertion.Assert(executionResult)
			Expect(result["result"]).To(Equal(true))
			Expect(result["message"]).To(BeNil())
		})

		It("Fails", func() {
			executionResult := core.ExecutionResult{
				key: 7,
			}

			assertion := NotEqualAssertion{
				Key:   key,
				Value: 7,
			}

			result := assertion.Assert(executionResult)
			Expect(result["result"]).To(Equal(false))
			Expect(result["message"]).To(Equal("FAIL: 7 does match 7"))
		})
	})

	Context("Exact Assertion", func() {
		It("Exact Assertion Succeeds", func() {
			expectedValue := 7

			executionResult := core.ExecutionResult{
				key: expectedValue,
			}

			assertion := ExactAssertion{
				Key:   key,
				Value: expectedValue,
			}

			result := assertion.Assert(executionResult)
			Expect(result["result"]).To(Equal(true))
			Expect(result["message"]).To(BeNil())
		})

		It("Exact Assertion Fails", func() {
			expectedValue := 7

			executionResult := core.ExecutionResult{
				key: 8,
			}

			assertion := ExactAssertion{
				Key:   key,
				Value: expectedValue,
			}

			result := assertion.Assert(executionResult)
			Expect(result["result"]).To(Equal(false))
			Expect(result["message"]).To(Equal("FAIL: 8 does not match 7"))
		})

		//NOTHING is currently using the message when an assertion fails but we will need
		//it for when we put the errors into the report.  One of the edge cases with the message
		//is that say the actual value was a string "7" and the expected is an int 7.  The message
		//will not include the quotes so the message would read 7 does not equal 7 as opposed
		//to "7" does not equal 7.  Notice this is a type mismatch
		PIt("Exact Assertion Fails when actual and expected are different types", func() {
			key := "some:key"
			expectedValue := 7

			executionResult := core.ExecutionResult{
				key: "7",
			}

			assertion := ExactAssertion{
				Key:   key,
				Value: expectedValue,
			}

			result := assertion.Assert(executionResult)
			Expect(result["result"]).To(Equal(false))
			Expect(result["message"]).To(Equal("FAIL: \"7\" does not match 7"))
		})
	})

})
<commit_msg>Fails When Actual is of type float64 and Instance is of type float64<commit_after>package assertions

import (
	"fmt"
	"strconv"

	"ci.guzzler.io/guzzler/corcel/core"
	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
)

type AssertionTestCase struct {
	Actual               interface{}
	Instance             interface{}
	ActualStringNumber   bool
	InstanceStringNumber bool
}

func NewAsssertionTestCase(actual interface{}, instance interface{}) (newInstance AssertionTestCase) {
	newInstance.Actual = actual
	newInstance.Instance = instance
	switch actualType := actual.(type) {
	case string:
		_, err := strconv.ParseFloat(actualType, 64)
		if err == nil {
			newInstance.ActualStringNumber = true
		}
	}
	switch instanceType := instance.(type) {
	case string:
		_, err := strconv.ParseFloat(instanceType, 64)
		if err == nil {
			newInstance.InstanceStringNumber = true
		}
	}
	return
}

var _ = FDescribe("Assertions", func() {

	key := "some:key"

	assert := func(testCases []AssertionTestCase, test func(actual interface{}, instance interface{})) {

		for _, testCase := range testCases {
			actualValue := testCase.Actual
			instanceValue := testCase.Instance
			testName := fmt.Sprintf("When Actual is of type %T and Instance is of type %T", actualValue, instanceValue)
			if testCase.ActualStringNumber {
				testName = fmt.Sprintf("%s. Actual value is a STRING NUMBER in this case", testName)
			}
			if testCase.InstanceStringNumber {
				testName = fmt.Sprintf("%s. Instance value is a STRING NUMBER in this case", testName)
			}
			It(testName, func() {
				test(actualValue, instanceValue)
			})
		}
	}

	/*
	   Using this test function and the test cases we get a nice readable output.  If you run ginkgo -v here is an example of the output

	   Assertions Greater Than Succeeds
	     When Actual is of type int and Instance is of type string. Instance value is a STRING NUMBER
	*/

	var nilValue interface{}

	FContext("Greater Than", func() {

		Context("Succeeds", func() {
			var testCases = []AssertionTestCase{
				NewAsssertionTestCase(float64(1.1), nilValue),
				NewAsssertionTestCase(int(1), nilValue),
				NewAsssertionTestCase("1", nilValue),
				NewAsssertionTestCase("a", nilValue),
				NewAsssertionTestCase(float64(5), float64(1)),
				NewAsssertionTestCase(int(5), float64(1)),
				NewAsssertionTestCase("2.2", float64(1)),
				NewAsssertionTestCase(int(5), int(1)),
				NewAsssertionTestCase("5", int(1)),
				NewAsssertionTestCase("abc", "a"),
				NewAsssertionTestCase(float64(1.3), "1.2"),
				NewAsssertionTestCase(int(3), "1"),
				NewAsssertionTestCase("3.1", "2"),
			}

			assert(testCases, func(actualValue interface{}, instanceValue interface{}) {
				key := "some:key"
				executionResult := core.ExecutionResult{
					key: actualValue,
				}

				assertion := GreaterThanAssertion{
					Key:   key,
					Value: instanceValue,
				}

				result := assertion.Assert(executionResult)
				Expect(result["result"]).To(Equal(true))
				Expect(result["message"]).To(BeNil())
			})
		})

		FContext("Fails", func() {
			var testCases = []AssertionTestCase{
				NewAsssertionTestCase(nilValue, nilValue),
				NewAsssertionTestCase(nilValue, int(5)),
				NewAsssertionTestCase(nilValue, "5.1"),
				NewAsssertionTestCase(nilValue, "fubar"),
				NewAsssertionTestCase(nilValue, float64(6.1)),
				NewAsssertionTestCase(float64(5.1), float64(6.1)),
			}

			assert(testCases, func(actualValue interface{}, instanceValue interface{}) {
				executionResult := core.ExecutionResult{
					key: actualValue,
				}

				assertion := GreaterThanAssertion{
					Key:   key,
					Value: instanceValue,
				}

				result := assertion.Assert(executionResult)
				Expect(result["result"]).To(Equal(false))
				Expect(result["message"]).To(Equal(fmt.Sprintf("FAIL: %v is not greater %v", actualValue, instanceValue)))
			})
			/*
				PIt("When Actual is float64 and Instance is float64", func() {

				})

				PIt("When Actual is int and Instance is float64", func() {

				})

				PIt("When Actual is string-number and Instance is float64", func() {

				})

				PIt("When Actual is float64 and Instance is int", func() {

				})

				PIt("When Actual is float64 and Instance is int", func() {

				})

				PIt("When Actual is int and Instance is int", func() {

				})

				PIt("When Actual is string-number and Instance is int", func() {

				})

				PIt("When Actual is string and Instance is int", func() {

				})

				PIt("When Actual is string and Instance is float64", func() {

				})

				PIt("When Actual is string and Instance is string-number", func() {

				})

				PIt("When Actual is string and Instance is string", func() {

				})

				PIt("When Actual is float64 and Instance is string", func() {

				})

				PIt("When Actual is int and Instance is string", func() {

				})

				PIt("When Actual string-number int and Instance is string", func() {

				})
			*/
		})

	})

	Context("Not Equal Assertion", func() {

		It("Succeeds", func() {
			executionResult := core.ExecutionResult{
				key: 8,
			}

			assertion := NotEqualAssertion{
				Key:   key,
				Value: 7,
			}

			result := assertion.Assert(executionResult)
			Expect(result["result"]).To(Equal(true))
			Expect(result["message"]).To(BeNil())
		})

		It("Fails", func() {
			executionResult := core.ExecutionResult{
				key: 7,
			}

			assertion := NotEqualAssertion{
				Key:   key,
				Value: 7,
			}

			result := assertion.Assert(executionResult)
			Expect(result["result"]).To(Equal(false))
			Expect(result["message"]).To(Equal("FAIL: 7 does match 7"))
		})
	})

	Context("Exact Assertion", func() {
		It("Exact Assertion Succeeds", func() {
			expectedValue := 7

			executionResult := core.ExecutionResult{
				key: expectedValue,
			}

			assertion := ExactAssertion{
				Key:   key,
				Value: expectedValue,
			}

			result := assertion.Assert(executionResult)
			Expect(result["result"]).To(Equal(true))
			Expect(result["message"]).To(BeNil())
		})

		It("Exact Assertion Fails", func() {
			expectedValue := 7

			executionResult := core.ExecutionResult{
				key: 8,
			}

			assertion := ExactAssertion{
				Key:   key,
				Value: expectedValue,
			}

			result := assertion.Assert(executionResult)
			Expect(result["result"]).To(Equal(false))
			Expect(result["message"]).To(Equal("FAIL: 8 does not match 7"))
		})

		//NOTHING is currently using the message when an assertion fails but we will need
		//it for when we put the errors into the report.  One of the edge cases with the message
		//is that say the actual value was a string "7" and the expected is an int 7.  The message
		//will not include the quotes so the message would read 7 does not equal 7 as opposed
		//to "7" does not equal 7.  Notice this is a type mismatch
		PIt("Exact Assertion Fails when actual and expected are different types", func() {
			key := "some:key"
			expectedValue := 7

			executionResult := core.ExecutionResult{
				key: "7",
			}

			assertion := ExactAssertion{
				Key:   key,
				Value: expectedValue,
			}

			result := assertion.Assert(executionResult)
			Expect(result["result"]).To(Equal(false))
			Expect(result["message"]).To(Equal("FAIL: \"7\" does not match 7"))
		})
	})

})
<|endoftext|>


<commit_before>package configdefault

import (
	openshiftcontrolplanev1 "github.com/openshift/api/openshiftcontrolplane/v1"
	"github.com/openshift/library-go/pkg/config/configdefaults"
	leaderelectionconverter "github.com/openshift/library-go/pkg/config/leaderelection"
)

func SetRecommendedOpenShiftControllerConfigDefaults(config *openshiftcontrolplanev1.OpenShiftControllerManagerConfig) {
	configdefaults.SetRecommendedHTTPServingInfoDefaults(config.ServingInfo)
	configdefaults.SetRecommendedKubeClientConfigDefaults(&config.KubeClientConfig)
	config.LeaderElection = leaderelectionconverter.LeaderElectionDefaulting(config.LeaderElection, "kube-system", "openshift-master-controllers")

	configdefaults.DefaultStringSlice(&config.Controllers, []string{"*"})

	configdefaults.DefaultString(&config.Network.ServiceNetworkCIDR, "10.0.0.0/24")

	if config.ImageImport.MaxScheduledImageImportsPerMinute == 0 {
		config.ImageImport.MaxScheduledImageImportsPerMinute = 60
	}
	if config.ImageImport.ScheduledImageImportMinimumIntervalSeconds == 0 {
		config.ImageImport.ScheduledImageImportMinimumIntervalSeconds = 15 * 60
	}

	configdefaults.DefaultString(&config.SecurityAllocator.UIDAllocatorRange, "1000000000-1999999999/10000")
	configdefaults.DefaultString(&config.SecurityAllocator.MCSAllocatorRange, "s0:/2")
	if config.SecurityAllocator.MCSLabelsPerProject == 0 {
		config.SecurityAllocator.MCSLabelsPerProject = 5
	}
}
<commit_msg>fix defaults for openshift-controller-manager<commit_after>package configdefault

import (
	"time"

	openshiftcontrolplanev1 "github.com/openshift/api/openshiftcontrolplane/v1"
	"github.com/openshift/library-go/pkg/config/configdefaults"
	leaderelectionconverter "github.com/openshift/library-go/pkg/config/leaderelection"
)

func SetRecommendedOpenShiftControllerConfigDefaults(config *openshiftcontrolplanev1.OpenShiftControllerManagerConfig) {
	configdefaults.SetRecommendedHTTPServingInfoDefaults(config.ServingInfo)
	configdefaults.SetRecommendedKubeClientConfigDefaults(&config.KubeClientConfig)
	config.LeaderElection = leaderelectionconverter.LeaderElectionDefaulting(config.LeaderElection, "kube-system", "openshift-master-controllers")

	configdefaults.DefaultStringSlice(&config.Controllers, []string{"*"})

	configdefaults.DefaultString(&config.Network.ServiceNetworkCIDR, "10.0.0.0/24")

	if config.ImageImport.MaxScheduledImageImportsPerMinute == 0 {
		config.ImageImport.MaxScheduledImageImportsPerMinute = 60
	}
	if config.ImageImport.ScheduledImageImportMinimumIntervalSeconds == 0 {
		config.ImageImport.ScheduledImageImportMinimumIntervalSeconds = 15 * 60
	}

	configdefaults.DefaultString(&config.SecurityAllocator.UIDAllocatorRange, "1000000000-1999999999/10000")
	configdefaults.DefaultString(&config.SecurityAllocator.MCSAllocatorRange, "s0:/2")
	if config.SecurityAllocator.MCSLabelsPerProject == 0 {
		config.SecurityAllocator.MCSLabelsPerProject = 5
	}

	if config.ResourceQuota.MinResyncPeriod.Duration == 0 {
		config.ResourceQuota.MinResyncPeriod.Duration = 5 * time.Minute
	}
	if config.ResourceQuota.SyncPeriod.Duration == 0 {
		config.ResourceQuota.SyncPeriod.Duration = 12 * time.Hour
	}
	if config.ResourceQuota.ConcurrentSyncs == 0 {
		config.ResourceQuota.ConcurrentSyncs = 5
	}

	if config.ImageImport.MaxScheduledImageImportsPerMinute == 0 {
		config.ImageImport.MaxScheduledImageImportsPerMinute = 60
	}
	if config.ImageImport.ScheduledImageImportMinimumIntervalSeconds == 0 {
		config.ImageImport.ScheduledImageImportMinimumIntervalSeconds = 15 * 60 // 15 minutes
	}
}
<|endoftext|>


<commit_before>package net

import (
	"bytes"
	"path/filepath"
	"strings"
	"text/template"

	bosherr "github.com/cloudfoundry/bosh-agent/errors"
	boshlog "github.com/cloudfoundry/bosh-agent/logger"
	bosharp "github.com/cloudfoundry/bosh-agent/platform/net/arp"
	boship "github.com/cloudfoundry/bosh-agent/platform/net/ip"
	boshsettings "github.com/cloudfoundry/bosh-agent/settings"
	boshsys "github.com/cloudfoundry/bosh-agent/system"
)

const UbuntuNetManagerLogTag = "UbuntuNetManager"

type UbuntuNetManager struct {
	cmdRunner                     boshsys.CmdRunner
	fs                            boshsys.FileSystem
	ipResolver                    boship.Resolver
	interfaceConfigurationCreator InterfaceConfigurationCreator
	addressBroadcaster            bosharp.AddressBroadcaster
	logger                        boshlog.Logger
}

func NewUbuntuNetManager(
	fs boshsys.FileSystem,
	cmdRunner boshsys.CmdRunner,
	ipResolver boship.Resolver,
	interfaceConfigurationCreator InterfaceConfigurationCreator,
	addressBroadcaster bosharp.AddressBroadcaster,
	logger boshlog.Logger,
) Manager {
	return UbuntuNetManager{
		cmdRunner:                     cmdRunner,
		fs:                            fs,
		ipResolver:                    ipResolver,
		interfaceConfigurationCreator: interfaceConfigurationCreator,
		addressBroadcaster:            addressBroadcaster,
		logger:                        logger,
	}
}

// DHCP Config file - /etc/dhcp/dhclient.conf
// Ubuntu 14.04 accepts several DNS as a list in a single prepend directive
const ubuntuDHCPConfigTemplate = `# Generated by bosh-agent

option rfc3442-classless-static-routes code 121 = array of unsigned integer 8;

send host-name "<hostname>";

request subnet-mask, broadcast-address, time-offset, routers,
	domain-name, domain-name-servers, domain-search, host-name,
	netbios-name-servers, netbios-scope, interface-mtu,
	rfc3442-classless-static-routes, ntp-servers;
{{ if . }}
prepend domain-name-servers {{ . }};{{ end }}
`

func (net UbuntuNetManager) ComputeNetworkConfig(networks boshsettings.Networks) ([]StaticInterfaceConfiguration, []DHCPInterfaceConfiguration, []string, error) {
	nonVipNetworks := boshsettings.Networks{}
	for networkName, networkSettings := range networks {
		if networkSettings.IsVIP() {
			continue
		}
		nonVipNetworks[networkName] = networkSettings
	}

	staticInterfaceConfigurations, dhcpInterfaceConfigurations, err := net.buildInterfaces(nonVipNetworks)
	if err != nil {
		return nil, nil, nil, err
	}

	dnsNetwork, _ := nonVipNetworks.DefaultNetworkFor("dns")
	dnsServers := dnsNetwork.DNS
	return staticInterfaceConfigurations, dhcpInterfaceConfigurations, dnsServers, nil
}

func (net UbuntuNetManager) SetupNetworking(networks boshsettings.Networks, errCh chan error) error {
	staticInterfaceConfigurations, dhcpInterfaceConfigurations, dnsServers, err := net.ComputeNetworkConfig(networks)
	if err != nil {
		return bosherr.WrapError(err, "Computing network configuration")
	}

	interfacesChanged, err := net.writeNetworkInterfaces(dhcpInterfaceConfigurations, staticInterfaceConfigurations, dnsServers)
	if err != nil {
		return bosherr.WrapError(err, "Writing network configuration")
	}

	dhcpChanged := false
	if len(dhcpInterfaceConfigurations) > 0 {
		dhcpChanged, err = net.writeDHCPConfiguration(dnsServers)
		if err != nil {
			return err
		}
	}

	if interfacesChanged || dhcpChanged {
		err = net.removeDhcpDNSConfiguration()
		if err != nil {
			return err
		}
		net.restartNetworkingInterfaces()
	}

	net.broadcastIps(staticInterfaceConfigurations, dhcpInterfaceConfigurations, errCh)

	return nil
}

func (net UbuntuNetManager) removeDhcpDNSConfiguration() error {
	_, _, _, err := net.cmdRunner.RunCommand("pkill", "dhclient")
	if err != nil {
		net.logger.Error(UbuntuNetManagerLogTag, "Ignoring failure calling 'pkill dhclient': %s", err)
	}

	interfacesByMacAddress, err := net.detectMacAddresses()
	if err != nil {
		return err
	}

	for _, ifaceName := range interfacesByMacAddress {
		_, _, _, err = net.cmdRunner.RunCommand("resolvconf", "-d", ifaceName+".dhclient")
		if err != nil {
			net.logger.Error(UbuntuNetManagerLogTag, "Ignoring failure calling 'resolvconf -d %s.dhclient': %s", ifaceName, err)
		}
	}

	return nil
}

func (net UbuntuNetManager) buildInterfaces(networks boshsettings.Networks) ([]StaticInterfaceConfiguration, []DHCPInterfaceConfiguration, error) {
	interfacesByMacAddress, err := net.detectMacAddresses()
	if err != nil {
		return nil, nil, bosherr.WrapError(err, "Getting network interfaces")
	}

	staticInterfaceConfigurations, dhcpInterfaceConfigurations, err := net.interfaceConfigurationCreator.CreateInterfaceConfigurations(networks, interfacesByMacAddress)
	if err != nil {
		return nil, nil, bosherr.WrapError(err, "Creating interface configurations")
	}

	return staticInterfaceConfigurations, dhcpInterfaceConfigurations, nil
}

func (net UbuntuNetManager) broadcastIps(staticInterfaceConfigurations []StaticInterfaceConfiguration, dhcpInterfaceConfigurations []DHCPInterfaceConfiguration, errCh chan error) {
	addresses := []boship.InterfaceAddress{}
	for _, iface := range staticInterfaceConfigurations {
		addresses = append(addresses, boship.NewSimpleInterfaceAddress(iface.Name, iface.Address))
	}
	for _, iface := range dhcpInterfaceConfigurations {
		addresses = append(addresses, boship.NewResolvingInterfaceAddress(iface.Name, net.ipResolver))
	}

	go func() {
		net.addressBroadcaster.BroadcastMACAddresses(addresses)
		if errCh != nil {
			errCh <- nil
		}
	}()
}

func (net UbuntuNetManager) restartNetworkingInterfaces() {
	net.logger.Debug(UbuntuNetManagerLogTag, "Restarting network interfaces")

	_, _, _, err := net.cmdRunner.RunCommand("ifdown", "-a", "--no-loopback")
	if err != nil {
		net.logger.Error(UbuntuNetManagerLogTag, "Ignoring ifdown failure: %s", err.Error())
	}

	_, _, _, err = net.cmdRunner.RunCommand("ifup", "-a", "--no-loopback")
	if err != nil {
		net.logger.Error(UbuntuNetManagerLogTag, "Ignoring ifup failure: %s", err.Error())
	}
}

func (net UbuntuNetManager) writeDHCPConfiguration(dnsServers []string) (bool, error) {
	buffer := bytes.NewBuffer([]byte{})
	t := template.Must(template.New("dhcp-config").Parse(ubuntuDHCPConfigTemplate))

	// Keep DNS servers in the order specified by the network
	// because they are added by a *single* DHCP's prepend command
	dnsServersList := strings.Join(dnsServers, ", ")
	err := t.Execute(buffer, dnsServersList)
	if err != nil {
		return false, bosherr.WrapError(err, "Generating config from template")
	}
	dhclientConfigFile := "/etc/dhcp/dhclient.conf"
	changed, err := net.fs.ConvergeFileContents(dhclientConfigFile, buffer.Bytes())

	if err != nil {
		return changed, bosherr.WrapErrorf(err, "Writing to %s", dhclientConfigFile)
	}

	return changed, nil
}

type networkInterfaceConfig struct {
	DNSServers                    []string
	StaticInterfaceConfigurations []StaticInterfaceConfiguration
	DHCPInterfaceConfigurations   []DHCPInterfaceConfiguration
	HasDNSNameServers             bool
}

func (net UbuntuNetManager) writeNetworkInterfaces(dhcpInterfaceConfigurations []DHCPInterfaceConfiguration, staticInterfaceConfigurations []StaticInterfaceConfiguration, dnsServers []string) (bool, error) {
	networkInterfaceValues := networkInterfaceConfig{
		StaticInterfaceConfigurations: staticInterfaceConfigurations,
		DHCPInterfaceConfigurations:   dhcpInterfaceConfigurations,
		HasDNSNameServers:             true,
		DNSServers:                    dnsServers,
	}

	buffer := bytes.NewBuffer([]byte{})

	t := template.Must(template.New("network-interfaces").Parse(networkInterfacesTemplate))

	err := t.Execute(buffer, networkInterfaceValues)
	if err != nil {
		return false, bosherr.WrapError(err, "Generating config from template")
	}

	changed, err := net.fs.ConvergeFileContents("/etc/network/interfaces", buffer.Bytes())
	if err != nil {
		return changed, bosherr.WrapError(err, "Writing to /etc/network/interfaces")
	}

	return changed, nil
}

const networkInterfacesTemplate = `# Generated by bosh-agent
auto lo
iface lo inet loopback
{{ range .DHCPInterfaceConfigurations }}auto {{ .Name }}
iface {{ .Name }} inet dhcp{{ end }}
{{ range .StaticInterfaceConfigurations }}auto {{ .Name }}
iface {{ .Name }} inet static
    address {{ .Address }}
    network {{ .Network }}
    netmask {{ .Netmask }}
    broadcast {{ .Broadcast }}
    gateway {{ .Gateway }}{{ end }}
{{ if .DNSServers }}dns-nameservers{{ range .DNSServers }} {{ . }}{{ end }}{{ end }}`

func (net UbuntuNetManager) detectMacAddresses() (map[string]string, error) {
	addresses := map[string]string{}

	filePaths, err := net.fs.Glob("/sys/class/net/*")
	if err != nil {
		return addresses, bosherr.WrapError(err, "Getting file list from /sys/class/net")
	}

	if len(filePaths) == 0 {
		return addresses, bosherr.Error("No network interfaces found")
	}

	var macAddress string
	for _, filePath := range filePaths {
		isPhysicalDevice := net.fs.FileExists(filepath.Join(filePath, "device"))

		if isPhysicalDevice {
			macAddress, err = net.fs.ReadFileString(filepath.Join(filePath, "address"))
			if err != nil {
				return addresses, bosherr.WrapError(err, "Reading mac address from file")
			}

			macAddress = strings.Trim(macAddress, "\n")

			interfaceName := filepath.Base(filePath)
			addresses[macAddress] = interfaceName
		}
	}

	return addresses, nil
}
<commit_msg>Add comments why dhclient is stopped<commit_after>package net

import (
	"bytes"
	"path/filepath"
	"strings"
	"text/template"

	bosherr "github.com/cloudfoundry/bosh-agent/errors"
	boshlog "github.com/cloudfoundry/bosh-agent/logger"
	bosharp "github.com/cloudfoundry/bosh-agent/platform/net/arp"
	boship "github.com/cloudfoundry/bosh-agent/platform/net/ip"
	boshsettings "github.com/cloudfoundry/bosh-agent/settings"
	boshsys "github.com/cloudfoundry/bosh-agent/system"
)

const UbuntuNetManagerLogTag = "UbuntuNetManager"

type UbuntuNetManager struct {
	cmdRunner                     boshsys.CmdRunner
	fs                            boshsys.FileSystem
	ipResolver                    boship.Resolver
	interfaceConfigurationCreator InterfaceConfigurationCreator
	addressBroadcaster            bosharp.AddressBroadcaster
	logger                        boshlog.Logger
}

func NewUbuntuNetManager(
	fs boshsys.FileSystem,
	cmdRunner boshsys.CmdRunner,
	ipResolver boship.Resolver,
	interfaceConfigurationCreator InterfaceConfigurationCreator,
	addressBroadcaster bosharp.AddressBroadcaster,
	logger boshlog.Logger,
) Manager {
	return UbuntuNetManager{
		cmdRunner:                     cmdRunner,
		fs:                            fs,
		ipResolver:                    ipResolver,
		interfaceConfigurationCreator: interfaceConfigurationCreator,
		addressBroadcaster:            addressBroadcaster,
		logger:                        logger,
	}
}

// DHCP Config file - /etc/dhcp/dhclient.conf
// Ubuntu 14.04 accepts several DNS as a list in a single prepend directive
const ubuntuDHCPConfigTemplate = `# Generated by bosh-agent

option rfc3442-classless-static-routes code 121 = array of unsigned integer 8;

send host-name "<hostname>";

request subnet-mask, broadcast-address, time-offset, routers,
	domain-name, domain-name-servers, domain-search, host-name,
	netbios-name-servers, netbios-scope, interface-mtu,
	rfc3442-classless-static-routes, ntp-servers;
{{ if . }}
prepend domain-name-servers {{ . }};{{ end }}
`

func (net UbuntuNetManager) ComputeNetworkConfig(networks boshsettings.Networks) ([]StaticInterfaceConfiguration, []DHCPInterfaceConfiguration, []string, error) {
	nonVipNetworks := boshsettings.Networks{}
	for networkName, networkSettings := range networks {
		if networkSettings.IsVIP() {
			continue
		}
		nonVipNetworks[networkName] = networkSettings
	}

	staticInterfaceConfigurations, dhcpInterfaceConfigurations, err := net.buildInterfaces(nonVipNetworks)
	if err != nil {
		return nil, nil, nil, err
	}

	dnsNetwork, _ := nonVipNetworks.DefaultNetworkFor("dns")
	dnsServers := dnsNetwork.DNS
	return staticInterfaceConfigurations, dhcpInterfaceConfigurations, dnsServers, nil
}

func (net UbuntuNetManager) SetupNetworking(networks boshsettings.Networks, errCh chan error) error {
	staticInterfaceConfigurations, dhcpInterfaceConfigurations, dnsServers, err := net.ComputeNetworkConfig(networks)
	if err != nil {
		return bosherr.WrapError(err, "Computing network configuration")
	}

	interfacesChanged, err := net.writeNetworkInterfaces(dhcpInterfaceConfigurations, staticInterfaceConfigurations, dnsServers)
	if err != nil {
		return bosherr.WrapError(err, "Writing network configuration")
	}

	dhcpChanged := false
	if len(dhcpInterfaceConfigurations) > 0 {
		dhcpChanged, err = net.writeDHCPConfiguration(dnsServers)
		if err != nil {
			return err
		}
	}

	if interfacesChanged || dhcpChanged {
		err = net.removeDhcpDNSConfiguration()
		if err != nil {
			return err
		}
		net.restartNetworkingInterfaces()
	}

	net.broadcastIps(staticInterfaceConfigurations, dhcpInterfaceConfigurations, errCh)

	return nil
}

func (net UbuntuNetManager) removeDhcpDNSConfiguration() error {
	// Removing dhcp configuration from /etc/network/interfaces
	// and restarting network does not stop dhclient if dhcp
	// is no longer needed. See https://bugs.launchpad.net/ubuntu/+source/dhcp3/+bug/38140
	_, _, _, err := net.cmdRunner.RunCommand("pkill", "dhclient")
	if err != nil {
		net.logger.Error(UbuntuNetManagerLogTag, "Ignoring failure calling 'pkill dhclient': %s", err)
	}

	interfacesByMacAddress, err := net.detectMacAddresses()
	if err != nil {
		return err
	}

	for _, ifaceName := range interfacesByMacAddress {
		// Explicitly delete the resolvconf record about given iface
		// It seems to hold on to old dhclient records after dhcp configuration
		// is removed from /etc/network/interfaces.
		_, _, _, err = net.cmdRunner.RunCommand("resolvconf", "-d", ifaceName+".dhclient")
		if err != nil {
			net.logger.Error(UbuntuNetManagerLogTag, "Ignoring failure calling 'resolvconf -d %s.dhclient': %s", ifaceName, err)
		}
	}

	return nil
}

func (net UbuntuNetManager) buildInterfaces(networks boshsettings.Networks) ([]StaticInterfaceConfiguration, []DHCPInterfaceConfiguration, error) {
	interfacesByMacAddress, err := net.detectMacAddresses()
	if err != nil {
		return nil, nil, bosherr.WrapError(err, "Getting network interfaces")
	}

	staticInterfaceConfigurations, dhcpInterfaceConfigurations, err := net.interfaceConfigurationCreator.CreateInterfaceConfigurations(networks, interfacesByMacAddress)
	if err != nil {
		return nil, nil, bosherr.WrapError(err, "Creating interface configurations")
	}

	return staticInterfaceConfigurations, dhcpInterfaceConfigurations, nil
}

func (net UbuntuNetManager) broadcastIps(staticInterfaceConfigurations []StaticInterfaceConfiguration, dhcpInterfaceConfigurations []DHCPInterfaceConfiguration, errCh chan error) {
	addresses := []boship.InterfaceAddress{}
	for _, iface := range staticInterfaceConfigurations {
		addresses = append(addresses, boship.NewSimpleInterfaceAddress(iface.Name, iface.Address))
	}
	for _, iface := range dhcpInterfaceConfigurations {
		addresses = append(addresses, boship.NewResolvingInterfaceAddress(iface.Name, net.ipResolver))
	}

	go func() {
		net.addressBroadcaster.BroadcastMACAddresses(addresses)
		if errCh != nil {
			errCh <- nil
		}
	}()
}

func (net UbuntuNetManager) restartNetworkingInterfaces() {
	net.logger.Debug(UbuntuNetManagerLogTag, "Restarting network interfaces")

	_, _, _, err := net.cmdRunner.RunCommand("ifdown", "-a", "--no-loopback")
	if err != nil {
		net.logger.Error(UbuntuNetManagerLogTag, "Ignoring ifdown failure: %s", err.Error())
	}

	_, _, _, err = net.cmdRunner.RunCommand("ifup", "-a", "--no-loopback")
	if err != nil {
		net.logger.Error(UbuntuNetManagerLogTag, "Ignoring ifup failure: %s", err.Error())
	}
}

func (net UbuntuNetManager) writeDHCPConfiguration(dnsServers []string) (bool, error) {
	buffer := bytes.NewBuffer([]byte{})
	t := template.Must(template.New("dhcp-config").Parse(ubuntuDHCPConfigTemplate))

	// Keep DNS servers in the order specified by the network
	// because they are added by a *single* DHCP's prepend command
	dnsServersList := strings.Join(dnsServers, ", ")
	err := t.Execute(buffer, dnsServersList)
	if err != nil {
		return false, bosherr.WrapError(err, "Generating config from template")
	}
	dhclientConfigFile := "/etc/dhcp/dhclient.conf"
	changed, err := net.fs.ConvergeFileContents(dhclientConfigFile, buffer.Bytes())

	if err != nil {
		return changed, bosherr.WrapErrorf(err, "Writing to %s", dhclientConfigFile)
	}

	return changed, nil
}

type networkInterfaceConfig struct {
	DNSServers                    []string
	StaticInterfaceConfigurations []StaticInterfaceConfiguration
	DHCPInterfaceConfigurations   []DHCPInterfaceConfiguration
	HasDNSNameServers             bool
}

func (net UbuntuNetManager) writeNetworkInterfaces(dhcpInterfaceConfigurations []DHCPInterfaceConfiguration, staticInterfaceConfigurations []StaticInterfaceConfiguration, dnsServers []string) (bool, error) {
	networkInterfaceValues := networkInterfaceConfig{
		StaticInterfaceConfigurations: staticInterfaceConfigurations,
		DHCPInterfaceConfigurations:   dhcpInterfaceConfigurations,
		HasDNSNameServers:             true,
		DNSServers:                    dnsServers,
	}

	buffer := bytes.NewBuffer([]byte{})

	t := template.Must(template.New("network-interfaces").Parse(networkInterfacesTemplate))

	err := t.Execute(buffer, networkInterfaceValues)
	if err != nil {
		return false, bosherr.WrapError(err, "Generating config from template")
	}

	changed, err := net.fs.ConvergeFileContents("/etc/network/interfaces", buffer.Bytes())
	if err != nil {
		return changed, bosherr.WrapError(err, "Writing to /etc/network/interfaces")
	}

	return changed, nil
}

const networkInterfacesTemplate = `# Generated by bosh-agent
auto lo
iface lo inet loopback
{{ range .DHCPInterfaceConfigurations }}auto {{ .Name }}
iface {{ .Name }} inet dhcp{{ end }}
{{ range .StaticInterfaceConfigurations }}auto {{ .Name }}
iface {{ .Name }} inet static
    address {{ .Address }}
    network {{ .Network }}
    netmask {{ .Netmask }}
    broadcast {{ .Broadcast }}
    gateway {{ .Gateway }}{{ end }}
{{ if .DNSServers }}dns-nameservers{{ range .DNSServers }} {{ . }}{{ end }}{{ end }}`

func (net UbuntuNetManager) detectMacAddresses() (map[string]string, error) {
	addresses := map[string]string{}

	filePaths, err := net.fs.Glob("/sys/class/net/*")
	if err != nil {
		return addresses, bosherr.WrapError(err, "Getting file list from /sys/class/net")
	}

	if len(filePaths) == 0 {
		return addresses, bosherr.Error("No network interfaces found")
	}

	var macAddress string
	for _, filePath := range filePaths {
		isPhysicalDevice := net.fs.FileExists(filepath.Join(filePath, "device"))

		if isPhysicalDevice {
			macAddress, err = net.fs.ReadFileString(filepath.Join(filePath, "address"))
			if err != nil {
				return addresses, bosherr.WrapError(err, "Reading mac address from file")
			}

			macAddress = strings.Trim(macAddress, "\n")

			interfaceName := filepath.Base(filePath)
			addresses[macAddress] = interfaceName
		}
	}

	return addresses, nil
}
<|endoftext|>


<commit_before>/*
Copyright (c) 2017 VMware, Inc. All Rights Reserved.

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

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

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

package toolbox

import (
	"bytes"
	"fmt"
	"net"

	"github.com/davecgh/go-xdr/xdr2"
)

// Defs from: open-vm-tools/lib/guestRpc/nicinfo.x

type TypedIPAddress struct {
	Type    int32
	Address []byte
}

type IPAddressEntry struct {
	Address      TypedIPAddress
	PrefixLength uint32
	Origin       *int32 `xdr:"optional"`
	Status       *int32 `xdr:"optional"`
}

type InetCidrRouteEntry struct {
	Dest         TypedIPAddress
	PrefixLength uint32
	NextHop      *TypedIPAddress `xdr:"optional"`
	IfIndex      uint32
	Type         int32
	Metric       uint32
}

type DNSConfigInfo struct {
	HostName   *string `xdr:"optional"`
	DomainName *string `xdr:"optional"`
	Servers    []TypedIPAddress
	Search     *string `xdr:"optional"`
}

type WinsConfigInfo struct {
	Primary   TypedIPAddress
	Secondary TypedIPAddress
}

type DhcpConfigInfo struct {
	Enabled  bool
	Settings string
}

type GuestNicV3 struct {
	MacAddress       string
	IPs              []IPAddressEntry
	DNSConfigInfo    *DNSConfigInfo  `xdr:"optional"`
	WinsConfigInfo   *WinsConfigInfo `xdr:"optional"`
	DhcpConfigInfov4 *DhcpConfigInfo `xdr:"optional"`
	DhcpConfigInfov6 *DhcpConfigInfo `xdr:"optional"`
}

type NicInfoV3 struct {
	Nics             []GuestNicV3
	Routes           []InetCidrRouteEntry
	DNSConfigInfo    *DNSConfigInfo  `xdr:"optional"`
	WinsConfigInfo   *WinsConfigInfo `xdr:"optional"`
	DhcpConfigInfov4 *DhcpConfigInfo `xdr:"optional"`
	DhcpConfigInfov6 *DhcpConfigInfo `xdr:"optional"`
}

type GuestNicInfo struct {
	Version int32
	V3      *NicInfoV3 `xdr:"optional"`
}

func EncodeXDR(val interface{}) ([]byte, error) {
	var buf bytes.Buffer

	_, err := xdr.Marshal(&buf, val)
	if err != nil {
		return nil, err
	}

	return buf.Bytes(), nil
}

func DecodeXDR(buf []byte, val interface{}) error {
	r := bytes.NewReader(buf)
	_, err := xdr.Unmarshal(r, val)
	return err
}

func NewGuestNicInfo() *GuestNicInfo {
	return &GuestNicInfo{
		Version: 3,
		V3:      &NicInfoV3{},
	}
}

func (nic *GuestNicV3) AddIP(addr net.Addr) {
	ip, ok := addr.(*net.IPNet)
	if !ok {
		return
	}

	kind := int32(1) // IAT_IPV4
	if ip.IP.To4() == nil {
		kind = 2 // IAT_IPV6
	} else {
		ip.IP = ip.IP.To4() // convert to 4-byte representation
	}

	size, _ := ip.Mask.Size()

	// nicinfo.x defines enum IpAddressStatus, but vmtoolsd only uses IAS_PREFERRED
	var status int32 = 1 // IAS_PREFERRED

	e := IPAddressEntry{
		Address: TypedIPAddress{
			Type:    kind,
			Address: []byte(ip.IP),
		},
		PrefixLength: uint32(size),
		Status:       &status,
	}

	nic.IPs = append(nic.IPs, e)
}

func GuestInfoCommand(kind int, req []byte) []byte {
	request := fmt.Sprintf("SetGuestInfo  %d ", kind)
	return append([]byte(request), req...)
}

var (
	netInterfaces = net.Interfaces
	maxNics       = 16 // guestRpc/nicinfo.x:NICINFO_MAX_NICS
)

//
func DefaultGuestNicInfo() *GuestNicInfo {
	proto := NewGuestNicInfo()
	info := proto.V3
	// #nosec: Errors unhandled
	ifs, _ := netInterfaces()

	for _, i := range ifs {
		if i.Flags&net.FlagLoopback == net.FlagLoopback {
			continue
		}

		if len(i.HardwareAddr) == 0 {
			continue // Not useful from outside the guest without a MAC
		}

		// #nosec: Errors unhandled
		addrs, _ := i.Addrs()

		if len(addrs) == 0 {
			continue // Not useful from outside the guest without an IP
		}

		nic := GuestNicV3{
			MacAddress: i.HardwareAddr.String(),
		}

		for _, addr := range addrs {
			nic.AddIP(addr)
		}

		info.Nics = append(info.Nics, nic)

		if len(info.Nics) >= maxNics {
			break
		}
	}

	return proto
}

func GuestInfoNicInfoRequest() ([]byte, error) {
	r, err := EncodeXDR(DefaultGuestNicInfo())
	if err != nil {
		return nil, err
	}

	return GuestInfoCommand(9 /*INFO_IPADDRESS_V3*/, r), nil
}
<commit_msg>Format with latest version of goimports<commit_after>/*
Copyright (c) 2017 VMware, Inc. All Rights Reserved.

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

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

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

package toolbox

import (
	"bytes"
	"fmt"
	"net"

	xdr "github.com/davecgh/go-xdr/xdr2"
)

// Defs from: open-vm-tools/lib/guestRpc/nicinfo.x

type TypedIPAddress struct {
	Type    int32
	Address []byte
}

type IPAddressEntry struct {
	Address      TypedIPAddress
	PrefixLength uint32
	Origin       *int32 `xdr:"optional"`
	Status       *int32 `xdr:"optional"`
}

type InetCidrRouteEntry struct {
	Dest         TypedIPAddress
	PrefixLength uint32
	NextHop      *TypedIPAddress `xdr:"optional"`
	IfIndex      uint32
	Type         int32
	Metric       uint32
}

type DNSConfigInfo struct {
	HostName   *string `xdr:"optional"`
	DomainName *string `xdr:"optional"`
	Servers    []TypedIPAddress
	Search     *string `xdr:"optional"`
}

type WinsConfigInfo struct {
	Primary   TypedIPAddress
	Secondary TypedIPAddress
}

type DhcpConfigInfo struct {
	Enabled  bool
	Settings string
}

type GuestNicV3 struct {
	MacAddress       string
	IPs              []IPAddressEntry
	DNSConfigInfo    *DNSConfigInfo  `xdr:"optional"`
	WinsConfigInfo   *WinsConfigInfo `xdr:"optional"`
	DhcpConfigInfov4 *DhcpConfigInfo `xdr:"optional"`
	DhcpConfigInfov6 *DhcpConfigInfo `xdr:"optional"`
}

type NicInfoV3 struct {
	Nics             []GuestNicV3
	Routes           []InetCidrRouteEntry
	DNSConfigInfo    *DNSConfigInfo  `xdr:"optional"`
	WinsConfigInfo   *WinsConfigInfo `xdr:"optional"`
	DhcpConfigInfov4 *DhcpConfigInfo `xdr:"optional"`
	DhcpConfigInfov6 *DhcpConfigInfo `xdr:"optional"`
}

type GuestNicInfo struct {
	Version int32
	V3      *NicInfoV3 `xdr:"optional"`
}

func EncodeXDR(val interface{}) ([]byte, error) {
	var buf bytes.Buffer

	_, err := xdr.Marshal(&buf, val)
	if err != nil {
		return nil, err
	}

	return buf.Bytes(), nil
}

func DecodeXDR(buf []byte, val interface{}) error {
	r := bytes.NewReader(buf)
	_, err := xdr.Unmarshal(r, val)
	return err
}

func NewGuestNicInfo() *GuestNicInfo {
	return &GuestNicInfo{
		Version: 3,
		V3:      &NicInfoV3{},
	}
}

func (nic *GuestNicV3) AddIP(addr net.Addr) {
	ip, ok := addr.(*net.IPNet)
	if !ok {
		return
	}

	kind := int32(1) // IAT_IPV4
	if ip.IP.To4() == nil {
		kind = 2 // IAT_IPV6
	} else {
		ip.IP = ip.IP.To4() // convert to 4-byte representation
	}

	size, _ := ip.Mask.Size()

	// nicinfo.x defines enum IpAddressStatus, but vmtoolsd only uses IAS_PREFERRED
	var status int32 = 1 // IAS_PREFERRED

	e := IPAddressEntry{
		Address: TypedIPAddress{
			Type:    kind,
			Address: []byte(ip.IP),
		},
		PrefixLength: uint32(size),
		Status:       &status,
	}

	nic.IPs = append(nic.IPs, e)
}

func GuestInfoCommand(kind int, req []byte) []byte {
	request := fmt.Sprintf("SetGuestInfo  %d ", kind)
	return append([]byte(request), req...)
}

var (
	netInterfaces = net.Interfaces
	maxNics       = 16 // guestRpc/nicinfo.x:NICINFO_MAX_NICS
)

//
func DefaultGuestNicInfo() *GuestNicInfo {
	proto := NewGuestNicInfo()
	info := proto.V3
	// #nosec: Errors unhandled
	ifs, _ := netInterfaces()

	for _, i := range ifs {
		if i.Flags&net.FlagLoopback == net.FlagLoopback {
			continue
		}

		if len(i.HardwareAddr) == 0 {
			continue // Not useful from outside the guest without a MAC
		}

		// #nosec: Errors unhandled
		addrs, _ := i.Addrs()

		if len(addrs) == 0 {
			continue // Not useful from outside the guest without an IP
		}

		nic := GuestNicV3{
			MacAddress: i.HardwareAddr.String(),
		}

		for _, addr := range addrs {
			nic.AddIP(addr)
		}

		info.Nics = append(info.Nics, nic)

		if len(info.Nics) >= maxNics {
			break
		}
	}

	return proto
}

func GuestInfoNicInfoRequest() ([]byte, error) {
	r, err := EncodeXDR(DefaultGuestNicInfo())
	if err != nil {
		return nil, err
	}

	return GuestInfoCommand(9 /*INFO_IPADDRESS_V3*/, r), nil
}
<|endoftext|>


<commit_before>// This package provides a simple health check server.
//
// The package lets you register arbitrary health check endpoints by
// providing a name (== URL path) and a callback function.
//
// The health check server listens for HTTP requests on a given port;
// probes are executed by issuing requests to the endpoints' named
// URL paths.
//
// GETing the "/" path provides a list of registered endpoints, one
// per line. GETing "/_ALL_" probes each registered endpoint sequentially,
// returning each endpoint's path, HTTP status code and body per line.
//
// The package works as a "singleton" with just one server in order to
// avoid cluttering the main program by passing handles around.
package healthcheck

import (
	"net/http"
	"fmt"
	"bytes"
)

// Code wishing to get probed by the health-checker needs to provide this callback
type CallbackFunc func () (code int, body string)

// The HTTP server
var server *http.Server

// The HTTP request multiplexer
var serveMux *http.ServeMux

// List of endpoints known by the server
var endpoints map[string]CallbackFunc

// Init
func init() {
	// Create the request multiplexer
	serveMux = http.NewServeMux()
	// Initialize the enpoint list
	endpoints = make(map[string]CallbackFunc)
}

// Configures the health check server
//
//  listenAddr: an address understood by http.ListenAndServe(), e.g. ":8008"
func Configure(listenAddr string) {
	// Create the HTTP server
	server = &http.Server{
		Addr:    listenAddr,
		Handler: serveMux,
	}

	// Add default wildcard handler
	serveMux.HandleFunc("/",
		func(responseWriter http.ResponseWriter, httpRequest *http.Request) {
			path := httpRequest.URL.Path

			// Handle "/": list all our registered endpoints
			if path == "/" {
				fmt.Fprintf(responseWriter, "/_ALL_\n")

				// TBD: Collate
				for endpointPath, _ := range endpoints {
					fmt.Fprintf(responseWriter, "%s\n", endpointPath)
				}
				return
			}

			// Default action: 404
			responseWriter.WriteHeader(http.StatusNotFound)
			fmt.Fprintf(responseWriter, "Path not found\n")
			return
		},
	)

	// Add magical "/_ALL" handler
	serveMux.HandleFunc("/_ALL_",
		func(responseWriter http.ResponseWriter, httpRequest *http.Request) {
			// Response code needs to be set before writing the response body,
			// so we need to pool the body temporarily into resultBody
			var resultCode = 200
			var resultBody bytes.Buffer

			// Call all endpoints sequentially
			// (TBD: if there were _a lot_ of these we could do them in parallel)
			// TBD: Collate (output at least)
			for endpointPath, callback := range endpoints {
				// Call the callback
				code, body := callback()
				// Append path, code, body to response body
				fmt.Fprintf(&resultBody,
					"%s %d %s\n",
					endpointPath,
					code,
					body,
				)
				// Naive assumption: the bigger the code the more serious it is
				if code > resultCode {
					resultCode = code
				}
			}

			// Set HTTP response code
			responseWriter.WriteHeader(resultCode)

			// Write HTTP response body
			// (TBD: more efficient way, buffer -> writer?)
			fmt.Fprintf(responseWriter, resultBody.String())
		},
	)

	// Add a static "ping" endpoint
	AddEndpoint("/ping", func()(code int, body string){
		return 200, "PONG"
	})

	// Debugging
	//AddEndpoint("/ping", func()(code int, body string){ return 400, "DUPLICATEPONG" })
	//AddEndpoint("/fail", func()(code int, body string){ return 500, "FAIL" })
	//AddEndpoint("", func()(code int, body string){ return 500, "EMPTYFAIL" })
	//AddEndpoint("noprecedingslash", func()(code int, body string){ return 500, "FAIL" })
	//AddEndpoint("/hasfinalslash/", func()(code int, body string){ return 500, "FAIL" })

}

// Registers an endpoint with the health checker.
//
// The urlPath must be unique. The callback must return an HTTP response code
// and body text.
//
// Boilerplate:
//
//  healthcheck.AddEndpoint("/my/arbitrary/path" func()(code int, body string) {
//      return 200, "Foobar Plugin is OK"
//  })
//
func AddEndpoint(urlPath string, callback CallbackFunc){
	// Check parameters
	// -syntax
	if len(urlPath) == 0 || urlPath[:1] != "/" || urlPath[len(urlPath)-1:] == "/" {
		panic(fmt.Sprintf(
			"ERROR: Health check endpoint must begin and may not end with a slash: \"%s\"",
			urlPath))
	}
	// - reserved paths
	for _, path := range []string{"/", "/_ALL_"} {
		if urlPath == path {
			panic(fmt.Sprintf(
				"ERROR: Health check path \"%s\" is reserved", path))
		}
	}
	// - registered paths
	_, exists := endpoints[urlPath]
	if exists {
		panic(fmt.Sprintf(
			"ERROR: Health check endpoint \"%s\" already registered", urlPath))
	}

	// Register the HTTP route & handler
	serveMux.HandleFunc(
		urlPath,
		func(responseWriter http.ResponseWriter, httpRequest *http.Request){
			// Call the callback
			code, body := callback()
			// Set HTTP response code
			responseWriter.WriteHeader(code)
			// Write HTTP response body
			fmt.Fprintf(responseWriter, body)
			fmt.Fprintf(responseWriter, "\n")
		},
	)

	// Store the endpoint
	endpoints[urlPath] = callback
}

// Registers an endpoint with the health checker.
//
// This is a convenience version of AddEndpoint() that takes
// the urlPath's components as a list of strings and catenates
// them.
func AddEndpointPathArray(urlPath []string, callback CallbackFunc) {
	// Catenate path
	var cat bytes.Buffer
	for _, pathComponent := range urlPath {
		fmt.Fprintf(&cat, "/%s", pathComponent)
	}
	// Call it
	AddEndpoint(cat.String(), callback)
}

// Starts the HTTP server
//
// Call this after Configure() and AddEndpoint() calls.
//
// TBD: is it possible to AddEndpoint() after Start()ing?
func Start(){
	err := server.ListenAndServe()

	if err != nil {
		panic(err.Error())
	}
}

// TBD: Cleanup?
func Stop(){

}
<commit_msg>Switch to HTTP 200/503 statuses<commit_after>// This package provides a simple health check server.
//
// The package lets you register arbitrary health check endpoints by
// providing a name (== URL path) and a callback function.
//
// The health check server listens for HTTP requests on a given port;
// probes are executed by issuing requests to the endpoints' named
// URL paths.
//
// GETing the "/" path provides a list of registered endpoints, one
// per line. GETing "/_ALL_" probes each registered endpoint sequentially,
// returning each endpoint's path, HTTP status code and body per line.
//
// The package works as a "singleton" with just one server in order to
// avoid cluttering the main program by passing handles around.
package healthcheck

import (
	"net/http"
	"fmt"
	"bytes"
)

//
const (
	StatusOK = http.StatusOK
	StatusServiceUnavailable = http.StatusServiceUnavailable
)

// Code wishing to get probed by the health-checker needs to provide this callback
type CallbackFunc func () (code int, body string)

// The HTTP server
var server *http.Server

// The HTTP request multiplexer
var serveMux *http.ServeMux

// List of endpoints known by the server
var endpoints map[string]CallbackFunc

// Init
func init() {
	// Create the request multiplexer
	serveMux = http.NewServeMux()
	// Initialize the enpoint list
	endpoints = make(map[string]CallbackFunc)
}

// Configures the health check server
//
//  listenAddr: an address understood by http.ListenAndServe(), e.g. ":8008"
func Configure(listenAddr string) {
	// Create the HTTP server
	server = &http.Server{
		Addr:    listenAddr,
		Handler: serveMux,
	}

	// Add default wildcard handler
	serveMux.HandleFunc("/",
		func(responseWriter http.ResponseWriter, httpRequest *http.Request) {
			path := httpRequest.URL.Path

			// Handle "/": list all our registered endpoints
			if path == "/" {
				fmt.Fprintf(responseWriter, "/_ALL_\n")

				// TBD: Collate
				for endpointPath, _ := range endpoints {
					fmt.Fprintf(responseWriter, "%s\n", endpointPath)
				}
				return
			}

			// Default action: 404
			responseWriter.WriteHeader(http.StatusNotFound)
			fmt.Fprintf(responseWriter, "Path not found\n")
			return
		},
	)

	// Add magical "/_ALL" handler
	serveMux.HandleFunc("/_ALL_",
		func(responseWriter http.ResponseWriter, httpRequest *http.Request) {
			// Response code needs to be set before writing the response body,
			// so we need to pool the body temporarily into resultBody
			var resultCode = StatusOK
			var resultBody bytes.Buffer

			// Call all endpoints sequentially
			// (TBD: if there were _a lot_ of these we could do them in parallel)
			// TBD: Collate (output at least)
			for endpointPath, callback := range endpoints {
				// Call the callback
				code, body := callback()
				// Append path, code, body to response body
				fmt.Fprintf(&resultBody,
					"%s %d %s\n",
					endpointPath,
					code,
					body,
				)

				if code != StatusOK {
					resultCode = StatusServiceUnavailable
				}
			}

			// Set HTTP response code
			responseWriter.WriteHeader(resultCode)

			// Write HTTP response body
			// (TBD: more efficient way, buffer -> writer?)
			fmt.Fprintf(responseWriter, resultBody.String())
		},
	)

	// Add a static "ping" endpoint
	AddEndpoint("/ping", func()(code int, body string){
		return StatusOK, "PONG"
	})

	// Debugging
	//AddEndpoint("/ping", func()(code int, body string){ return 400, "DUPLICATEPONG" })
	//AddEndpoint("/fail", func()(code int, body string){ return 500, "FAIL" })
	//AddEndpoint("", func()(code int, body string){ return 500, "EMPTYFAIL" })
	//AddEndpoint("noprecedingslash", func()(code int, body string){ return 500, "FAIL" })
	//AddEndpoint("/hasfinalslash/", func()(code int, body string){ return 500, "FAIL" })

}

// Registers an endpoint with the health checker.
//
// The urlPath must be unique. The callback must return an HTTP response code
// and body text.
//
// Boilerplate:
//
//  healthcheck.AddEndpoint("/my/arbitrary/path" func()(code int, body string) {
//      return 200, "Foobar Plugin is OK"
//  })
//
func AddEndpoint(urlPath string, callback CallbackFunc){
	// Check parameters
	// -syntax
	if len(urlPath) == 0 || urlPath[:1] != "/" || urlPath[len(urlPath)-1:] == "/" {
		panic(fmt.Sprintf(
			"ERROR: Health check endpoint must begin and may not end with a slash: \"%s\"",
			urlPath))
	}
	// - reserved paths
	for _, path := range []string{"/", "/_ALL_"} {
		if urlPath == path {
			panic(fmt.Sprintf(
				"ERROR: Health check path \"%s\" is reserved", path))
		}
	}
	// - registered paths
	_, exists := endpoints[urlPath]
	if exists {
		panic(fmt.Sprintf(
			"ERROR: Health check endpoint \"%s\" already registered", urlPath))
	}

	// Register the HTTP route & handler
	serveMux.HandleFunc(
		urlPath,
		func(responseWriter http.ResponseWriter, httpRequest *http.Request){
			// Call the callback
			code, body := callback()
			// Set HTTP response code
			responseWriter.WriteHeader(code)
			// Write HTTP response body
			fmt.Fprintf(responseWriter, body)
			fmt.Fprintf(responseWriter, "\n")
		},
	)

	// Store the endpoint
	endpoints[urlPath] = callback
}

// Registers an endpoint with the health checker.
//
// This is a convenience version of AddEndpoint() that takes
// the urlPath's components as a list of strings and catenates
// them.
func AddEndpointPathArray(urlPath []string, callback CallbackFunc) {
	// Catenate path
	var cat bytes.Buffer
	for _, pathComponent := range urlPath {
		fmt.Fprintf(&cat, "/%s", pathComponent)
	}
	// Call it
	AddEndpoint(cat.String(), callback)
}

// Starts the HTTP server
//
// Call this after Configure() and AddEndpoint() calls.
//
// TBD: is it possible to AddEndpoint() after Start()ing?
func Start(){
	err := server.ListenAndServe()

	if err != nil {
		panic(err.Error())
	}
}

// TBD: Cleanup?
func Stop(){

}
<|endoftext|>


<commit_before>package healthcheck

import (
	"bufio"
	"context"
	"fmt"
	"net"
	"net/http"
	"sync/atomic"
	"time"

	"github.com/Devatoria/go-mesos-executor/logger"
	"github.com/Devatoria/go-mesos-executor/namespace"
	"github.com/spf13/viper"

	"github.com/Devatoria/go-nsenter"
	"github.com/mesos/mesos-go/api/v1/lib"
	"go.uber.org/zap"
)

// Checker is a health checker in charge to run check and say
// it task is healthy or unhealthy
type Checker struct {
	ConsecutiveFailures uint32
	Done                chan struct{}
	Exited              chan struct{}
	GracePeriodExpired  *uint32
	Healthy             chan bool
	Pid                 int
	TaskInfo            *mesos.TaskInfo
	Quit                chan struct{}
}

// NewChecker instanciate a health checker with given health check
func NewChecker(pid int, taskInfo *mesos.TaskInfo) *Checker {
	var gracePeriodExpired uint32
	gracePeriodExpired = 0

	return &Checker{
		ConsecutiveFailures: 0,
		Done:                make(chan struct{}),
		Exited:              make(chan struct{}),
		GracePeriodExpired:  &gracePeriodExpired,
		Healthy:             make(chan bool),
		Pid:                 pid,
		TaskInfo:            taskInfo,
		Quit:                make(chan struct{}),
	}
}

// Run starts to run health check
func (c *Checker) Run() {
	// Prepare interval ticker
	ticker := time.NewTicker(time.Duration(c.TaskInfo.GetHealthCheck().GetIntervalSeconds()) * time.Second)
	defer ticker.Stop()

	// Sleep during the defined delay before starting to check
	time.Sleep(time.Duration(c.TaskInfo.GetHealthCheck().GetDelaySeconds()) * time.Second)

	// Grace period timer
	gracePeriodTimer := time.NewTimer(time.Duration(c.TaskInfo.GetHealthCheck().GetGracePeriodSeconds()) * time.Second)
	defer gracePeriodTimer.Stop()

	// Wait for grace period to end (in parallel with checks)
	go func() {
		<-gracePeriodTimer.C
		atomic.StoreUint32(c.GracePeriodExpired, 1)

		logger.GetInstance().Development.Debug("Grace period expired")
	}()

	// Define check function to use (HTTP/TCP/COMMAND)
	var checkFunc func(chan bool)
	switch c.TaskInfo.GetHealthCheck().GetType() {
	case mesos.HealthCheck_HTTP:
		checkFunc = c.checkHTTP
	case mesos.HealthCheck_TCP:
		checkFunc = c.checkTCP
	case mesos.HealthCheck_COMMAND:
		checkFunc = c.checkCommand
	default: // Unsupported health check type
		logger.GetInstance().Production.Error("Unknown or unsupported health check type... Mark task as unhealthy",
			zap.String("type", c.TaskInfo.GetHealthCheck().GetType().String()),
		)
		c.Healthy <- false

		return
	}

	// Start ticking
	var healthy bool
	for {
		select {
		// Ticket has ticked (we must launch the check)
		case <-ticker.C:
			// Prepare channel to receive result or timeout
			result := make(chan bool)
			go checkFunc(result)
			healthy = <-result

			logger.GetInstance().Development.Debug("Health check ticker has ticked",
				zap.Bool("healthy", healthy),
			)

			// Manage consecutive failures count
			if healthy {
				c.ConsecutiveFailures = 0

				// Manually expire grace period if check is healthy
				atomic.StoreUint32(c.GracePeriodExpired, 1)
			} else {
				// Do not take care about consecutive failures if we are in the grace period
				// We'll just change the task status but do not increase count
				if atomic.LoadUint32(c.GracePeriodExpired) == 1 {
					c.ConsecutiveFailures++

					// If we reached the max consecutive failures, we stop the checker,
					// signal the executor and wait for it to tell us to free resources (using quit chan)
					if c.ConsecutiveFailures == c.TaskInfo.GetHealthCheck().GetConsecutiveFailures() {
						ticker.Stop()
						c.Done <- struct{}{}

						break
					}
				}
			}

			c.Healthy <- healthy
		// Executor asked the checker to quit
		case <-c.Quit:
			logger.GetInstance().Development.Debug("Shutting down checker")
			c.Exited <- struct{}{}

			return
		}
	}
}

// checkHTTP enters the container network namespace and
// calls the check path, and returns true if the HTTP status
// is between 200 and 399
func (c *Checker) checkHTTP(result chan bool) {
	// Enter network namespace only if we are running in bridge mode
	if c.TaskInfo.GetContainer().GetDocker().GetNetwork() == mesos.ContainerInfo_DockerInfo_BRIDGE {
		defer namespace.ExitNetworkNamespace() // No matter what happen, we must return into executor namespace and unlock thread

		// Enter container network namespace
		err := namespace.EnterNetworkNamespace(c.Pid)
		if err != nil {
			logger.GetInstance().Development.Debug("Error while getting container namespace",
				zap.Error(err),
			)

			result <- false

			return
		}
	}

	// Prepare raw TCP request
	// We can't use net/http package here because transport RoundTripper
	// would make the call on another goroutine, making the network namespace
	// to be switched to another one that we don't care.
	port := c.TaskInfo.GetHealthCheck().GetHTTP().GetPort()
	path := c.TaskInfo.GetHealthCheck().GetHTTP().GetPath()
	conn, err := net.DialTimeout("tcp", fmt.Sprintf("localhost:%d", port), time.Duration(c.TaskInfo.GetHealthCheck().GetTimeoutSeconds())*time.Second)
	defer conn.Close()
	if err != nil {
		logger.GetInstance().Production.Error("Error while connecting to health check socket",
			zap.Error(err),
		)

		result <- false

		return
	}

	// Do raw HTTP request and get status
	// Then, parse it into an HTTP response in order
	// to be able to easily manipulate it
	fmt.Fprintf(conn, fmt.Sprintf("GET %s HTTP/1.0\r\n\r\n", path))
	status := bufio.NewReader(conn)
	response, err := http.ReadResponse(status, nil)
	if err != nil {
		logger.GetInstance().Production.Error("Error while reading HTTP response",
			zap.Error(err),
		)

		result <- false

		return
	}

	// Check status code: should be between 200 and 399
	if response.StatusCode < 200 || response.StatusCode > 399 {
		logger.GetInstance().Production.Error("Unexpected status code",
			zap.Int("code", response.StatusCode),
		)

		result <- false

		return
	}

	result <- true
}

// checkTCP enters the container network namespace and
// check that given port is accessible
func (c *Checker) checkTCP(result chan bool) {
	// Enter network namespace only if we are running in bridge mode
	if c.TaskInfo.GetContainer().GetDocker().GetNetwork() == mesos.ContainerInfo_DockerInfo_BRIDGE {
		defer namespace.ExitNetworkNamespace()

		// Enter container network namespace
		err := namespace.EnterNetworkNamespace(c.Pid)
		if err != nil {
			logger.GetInstance().Development.Debug("Error while getting container namespace",
				zap.Error(err),
			)

			result <- false

			return
		}
	}

	// Try to dial on health check port
	port := c.TaskInfo.GetHealthCheck().GetTCP().GetPort()
	conn, err := net.DialTimeout("tcp", fmt.Sprintf("localhost:%d", port), time.Duration(c.TaskInfo.GetHealthCheck().GetTimeoutSeconds())*time.Second)
	defer conn.Close()
	if err != nil {
		logger.GetInstance().Production.Error("Error while connecting to health check socket",
			zap.Error(err),
		)

		result <- false

		return
	}

	result <- true
}

// checkCommand enters the container mount namespace and
// executes the given command using nsenter
func (c *Checker) checkCommand(result chan bool) {
	var err error
	var args []string
	var program string
	command := c.TaskInfo.GetHealthCheck().GetCommand()
	// Must be wrapped into a shell process
	if command.GetShell() {
		// Wrap command into quotes
		// (otherwise, params would not be passed to the wrapped command)
		args = []string{"-c", fmt.Sprintf("\"%s\"", command.GetValue())}
		program = "/bin/sh"
	} else {
		program = command.GetValue()
	}

	// Prepare config to enter required namespaces
	// TODO: should we enter into container user namespace if it is actived in containerizer?
	nsPath := fmt.Sprintf("%s/%d/ns", viper.GetString("proc_path"), c.Pid)
	config := &nsenter.Config{
		Mount:     true,
		MountFile: fmt.Sprintf("%s/mnt", nsPath),
		Target:    c.Pid,
	}

	// Enter network namespace only if we are running in bridge mode
	if c.TaskInfo.GetContainer().GetDocker().GetNetwork() == mesos.ContainerInfo_DockerInfo_BRIDGE {
		config.Net = true
		config.NetFile = fmt.Sprintf("%s/net", nsPath)
	}

	ctx, cancel := context.WithTimeout(context.Background(), time.Duration(c.TaskInfo.GetHealthCheck().GetTimeoutSeconds())*time.Second)
	stdout, stderr, err := config.ExecuteContext(ctx, program, args...)
	cancel()

	if err != nil {
		logger.GetInstance().Production.Error("Error while executing health check command",
			zap.String("stderr", stderr),
			zap.Error(err),
		)

		result <- false

		return
	}

	logger.GetInstance().Development.Debug("Health check has been done",
		zap.String("stdout", stdout),
	)

	result <- true
}
<commit_msg>Add comments<commit_after>package healthcheck

import (
	"bufio"
	"context"
	"fmt"
	"net"
	"net/http"
	"sync/atomic"
	"time"

	"github.com/Devatoria/go-mesos-executor/logger"
	"github.com/Devatoria/go-mesos-executor/namespace"
	"github.com/spf13/viper"

	"github.com/Devatoria/go-nsenter"
	"github.com/mesos/mesos-go/api/v1/lib"
	"go.uber.org/zap"
)

// Checker is a health checker in charge to run check and say
// it task is healthy or unhealthy
type Checker struct {
	ConsecutiveFailures uint32          // Consecutive failures count
	Done                chan struct{}   // Done chan is triggered when the checker has finished its work (task is unhealthy)
	Exited              chan struct{}   // Exited chan is triggered when the checker has freed its resources
	GracePeriodExpired  *uint32         // Grace period boolean, using uint32 to be atomic
	Healthy             chan bool       // Result chan
	Pid                 int             // Pid in which we enter namespace
	TaskInfo            *mesos.TaskInfo // The info of the checked task
	Quit                chan struct{}   // Quit chan is triggered by the core to tell the checker to stop its work
}

// NewChecker instanciate a health checker with given health check
func NewChecker(pid int, taskInfo *mesos.TaskInfo) *Checker {
	var gracePeriodExpired uint32
	gracePeriodExpired = 0

	return &Checker{
		ConsecutiveFailures: 0,
		Done:                make(chan struct{}),
		Exited:              make(chan struct{}),
		GracePeriodExpired:  &gracePeriodExpired,
		Healthy:             make(chan bool),
		Pid:                 pid,
		TaskInfo:            taskInfo,
		Quit:                make(chan struct{}),
	}
}

// Run starts to run health check
func (c *Checker) Run() {
	// Prepare interval ticker
	ticker := time.NewTicker(time.Duration(c.TaskInfo.GetHealthCheck().GetIntervalSeconds()) * time.Second)
	defer ticker.Stop()

	// Sleep during the defined delay before starting to check
	time.Sleep(time.Duration(c.TaskInfo.GetHealthCheck().GetDelaySeconds()) * time.Second)

	// Grace period timer
	gracePeriodTimer := time.NewTimer(time.Duration(c.TaskInfo.GetHealthCheck().GetGracePeriodSeconds()) * time.Second)
	defer gracePeriodTimer.Stop()

	// Wait for grace period to end (in parallel with checks)
	go func() {
		<-gracePeriodTimer.C
		atomic.StoreUint32(c.GracePeriodExpired, 1)

		logger.GetInstance().Development.Debug("Grace period expired")
	}()

	// Define check function to use (HTTP/TCP/COMMAND)
	var checkFunc func(chan bool)
	switch c.TaskInfo.GetHealthCheck().GetType() {
	case mesos.HealthCheck_HTTP:
		checkFunc = c.checkHTTP
	case mesos.HealthCheck_TCP:
		checkFunc = c.checkTCP
	case mesos.HealthCheck_COMMAND:
		checkFunc = c.checkCommand
	default: // Unsupported health check type
		logger.GetInstance().Production.Error("Unknown or unsupported health check type... Mark task as unhealthy",
			zap.String("type", c.TaskInfo.GetHealthCheck().GetType().String()),
		)
		c.Healthy <- false

		return
	}

	// Start ticking
	var healthy bool
	for {
		select {
		// Ticket has ticked (we must launch the check)
		case <-ticker.C:
			// Prepare channel to receive result or timeout
			result := make(chan bool)
			go checkFunc(result)
			healthy = <-result

			logger.GetInstance().Development.Debug("Health check ticker has ticked",
				zap.Bool("healthy", healthy),
			)

			// Manage consecutive failures count
			if healthy {
				c.ConsecutiveFailures = 0

				// Manually expire grace period if check is healthy
				atomic.StoreUint32(c.GracePeriodExpired, 1)
			} else {
				// Do not take care about consecutive failures if we are in the grace period
				// We'll just change the task status but do not increase count
				if atomic.LoadUint32(c.GracePeriodExpired) == 1 {
					c.ConsecutiveFailures++

					// If we reached the max consecutive failures, we stop the checker,
					// signal the executor and wait for it to tell us to free resources (using quit chan)
					if c.ConsecutiveFailures == c.TaskInfo.GetHealthCheck().GetConsecutiveFailures() {
						ticker.Stop()
						c.Done <- struct{}{}

						break
					}
				}
			}

			c.Healthy <- healthy
		// Executor asked the checker to quit
		case <-c.Quit:
			logger.GetInstance().Development.Debug("Shutting down checker")
			c.Exited <- struct{}{}

			return
		}
	}
}

// checkHTTP enters the container network namespace and
// calls the check path, and returns true if the HTTP status
// is between 200 and 399
func (c *Checker) checkHTTP(result chan bool) {
	// Enter network namespace only if we are running in bridge mode
	if c.TaskInfo.GetContainer().GetDocker().GetNetwork() == mesos.ContainerInfo_DockerInfo_BRIDGE {
		defer namespace.ExitNetworkNamespace() // No matter what happen, we must return into executor namespace and unlock thread

		// Enter container network namespace
		err := namespace.EnterNetworkNamespace(c.Pid)
		if err != nil {
			logger.GetInstance().Development.Debug("Error while getting container namespace",
				zap.Error(err),
			)

			result <- false

			return
		}
	}

	// Prepare raw TCP request
	// We can't use net/http package here because transport RoundTripper
	// would make the call on another goroutine, making the network namespace
	// to be switched to another one that we don't care.
	port := c.TaskInfo.GetHealthCheck().GetHTTP().GetPort()
	path := c.TaskInfo.GetHealthCheck().GetHTTP().GetPath()
	conn, err := net.DialTimeout("tcp", fmt.Sprintf("localhost:%d", port), time.Duration(c.TaskInfo.GetHealthCheck().GetTimeoutSeconds())*time.Second)
	defer conn.Close()
	if err != nil {
		logger.GetInstance().Production.Error("Error while connecting to health check socket",
			zap.Error(err),
		)

		result <- false

		return
	}

	// Do raw HTTP request and get status
	// Then, parse it into an HTTP response in order
	// to be able to easily manipulate it
	fmt.Fprintf(conn, fmt.Sprintf("GET %s HTTP/1.0\r\n\r\n", path))
	status := bufio.NewReader(conn)
	response, err := http.ReadResponse(status, nil)
	if err != nil {
		logger.GetInstance().Production.Error("Error while reading HTTP response",
			zap.Error(err),
		)

		result <- false

		return
	}

	// Check status code: should be between 200 and 399
	if response.StatusCode < 200 || response.StatusCode > 399 {
		logger.GetInstance().Production.Error("Unexpected status code",
			zap.Int("code", response.StatusCode),
		)

		result <- false

		return
	}

	result <- true
}

// checkTCP enters the container network namespace and
// check that given port is accessible
func (c *Checker) checkTCP(result chan bool) {
	// Enter network namespace only if we are running in bridge mode
	if c.TaskInfo.GetContainer().GetDocker().GetNetwork() == mesos.ContainerInfo_DockerInfo_BRIDGE {
		defer namespace.ExitNetworkNamespace()

		// Enter container network namespace
		err := namespace.EnterNetworkNamespace(c.Pid)
		if err != nil {
			logger.GetInstance().Development.Debug("Error while getting container namespace",
				zap.Error(err),
			)

			result <- false

			return
		}
	}

	// Try to dial on health check port
	port := c.TaskInfo.GetHealthCheck().GetTCP().GetPort()
	conn, err := net.DialTimeout("tcp", fmt.Sprintf("localhost:%d", port), time.Duration(c.TaskInfo.GetHealthCheck().GetTimeoutSeconds())*time.Second)
	defer conn.Close()
	if err != nil {
		logger.GetInstance().Production.Error("Error while connecting to health check socket",
			zap.Error(err),
		)

		result <- false

		return
	}

	result <- true
}

// checkCommand enters the container mount namespace and
// executes the given command using nsenter
func (c *Checker) checkCommand(result chan bool) {
	var err error
	var args []string
	var program string
	command := c.TaskInfo.GetHealthCheck().GetCommand()
	// Must be wrapped into a shell process
	if command.GetShell() {
		// Wrap command into quotes
		// (otherwise, params would not be passed to the wrapped command)
		args = []string{"-c", fmt.Sprintf("\"%s\"", command.GetValue())}
		program = "/bin/sh"
	} else {
		program = command.GetValue()
	}

	// Prepare config to enter required namespaces
	// TODO: should we enter into container user namespace if it is actived in containerizer?
	nsPath := fmt.Sprintf("%s/%d/ns", viper.GetString("proc_path"), c.Pid)
	config := &nsenter.Config{
		Mount:     true,
		MountFile: fmt.Sprintf("%s/mnt", nsPath),
		Target:    c.Pid,
	}

	// Enter network namespace only if we are running in bridge mode
	if c.TaskInfo.GetContainer().GetDocker().GetNetwork() == mesos.ContainerInfo_DockerInfo_BRIDGE {
		config.Net = true
		config.NetFile = fmt.Sprintf("%s/net", nsPath)
	}

	ctx, cancel := context.WithTimeout(context.Background(), time.Duration(c.TaskInfo.GetHealthCheck().GetTimeoutSeconds())*time.Second)
	stdout, stderr, err := config.ExecuteContext(ctx, program, args...)
	cancel()

	if err != nil {
		logger.GetInstance().Production.Error("Error while executing health check command",
			zap.String("stderr", stderr),
			zap.Error(err),
		)

		result <- false

		return
	}

	logger.GetInstance().Development.Debug("Health check has been done",
		zap.String("stdout", stdout),
	)

	result <- true
}
<|endoftext|>


<commit_before>package styxproto

// Based on
// http://plan9.bell-labs.com/sources/plan9/sys/include/fcall.h
const (
	msgTversion = iota + 100 // size[4] Tversion tag[2] msize[4] version[s]
	msgRversion              // size[4] Rversion tag[2] msize[4] version[s]
	msgTauth                 // size[4] Tauth tag[2] afid[4] uname[s] aname[s]
	msgRauth                 // size[4] Rauth tag[2] aqid[13]
	msgTattach               // size[4] Tattach tag[2] fid[4] afid[4] uname[s] aname[s]
	msgRattach               // size[4] Rattach tag[2] qid[13]
	msgTerror                // illegal
	msgRerror                // size[4] Rerror tag[2] ename[s]
	msgTflush                // size[4] Tflush tag[2] oldtag[2]
	msgRflush                // size[4] Rflush tag[2]
	msgTwalk                 // size[4] Twalk tag[2] fid[4] newfid[4] nwname[2] nwname*(wname[s])
	msgRwalk                 // size[4] Rwalk tag[2] nwqid[2] nwqid*(wqid[13])
	msgTopen                 // size[4] Topen tag[2] fid[4] mode[1]
	msgRopen                 // size[4] Ropen tag[2] qid[13] iounit[4]
	msgTcreate               // size[4] Tcreate tag[2] fid[4] name[s] perm[4] mode[1]
	msgRcreate               // size[4] Rcreate tag[2] qid[13] iounit[4]
	msgTread                 // size[4] Tread tag[2] fid[4] offset[8] count[4]
	msgRread                 // size[4] Rread tag[2] count[4] data[count]
	msgTwrite                // size[4] Twrite tag[2] fid[4] offset[8] count[4] data[count]
	msgRwrite                // size[4] Rwrite tag[2] count[4]
	msgTclunk                // size[4] Tclunk tag[2] fid[4]
	msgRclunk                // size[4] Rclunk tag[2]
	msgTremove               // size[4] Tremove tag[2] fid[4]
	msgRremove               // size[4] Rremove tag[2]
	msgTstat                 // size[4] Tstat tag[2] fid[4]
	msgRstat                 // size[4] Rstat tag[2] stat[n]
	msgTwstat                // size[4] Twstat tag[2] fid[4] stat[n]
	msgRwstat                // size[4] Rwstat tag[2]
)

const NoTag uint16 = 0xFFFF
<commit_msg>add file mode bits to styxproto<commit_after>package styxproto

// Based on
// http://plan9.bell-labs.com/sources/plan9/sys/include/fcall.h
const (
	msgTversion = iota + 100 // size[4] Tversion tag[2] msize[4] version[s]
	msgRversion              // size[4] Rversion tag[2] msize[4] version[s]
	msgTauth                 // size[4] Tauth tag[2] afid[4] uname[s] aname[s]
	msgRauth                 // size[4] Rauth tag[2] aqid[13]
	msgTattach               // size[4] Tattach tag[2] fid[4] afid[4] uname[s] aname[s]
	msgRattach               // size[4] Rattach tag[2] qid[13]
	msgTerror                // illegal
	msgRerror                // size[4] Rerror tag[2] ename[s]
	msgTflush                // size[4] Tflush tag[2] oldtag[2]
	msgRflush                // size[4] Rflush tag[2]
	msgTwalk                 // size[4] Twalk tag[2] fid[4] newfid[4] nwname[2] nwname*(wname[s])
	msgRwalk                 // size[4] Rwalk tag[2] nwqid[2] nwqid*(wqid[13])
	msgTopen                 // size[4] Topen tag[2] fid[4] mode[1]
	msgRopen                 // size[4] Ropen tag[2] qid[13] iounit[4]
	msgTcreate               // size[4] Tcreate tag[2] fid[4] name[s] perm[4] mode[1]
	msgRcreate               // size[4] Rcreate tag[2] qid[13] iounit[4]
	msgTread                 // size[4] Tread tag[2] fid[4] offset[8] count[4]
	msgRread                 // size[4] Rread tag[2] count[4] data[count]
	msgTwrite                // size[4] Twrite tag[2] fid[4] offset[8] count[4] data[count]
	msgRwrite                // size[4] Rwrite tag[2] count[4]
	msgTclunk                // size[4] Tclunk tag[2] fid[4]
	msgRclunk                // size[4] Rclunk tag[2]
	msgTremove               // size[4] Tremove tag[2] fid[4]
	msgRremove               // size[4] Rremove tag[2]
	msgTstat                 // size[4] Tstat tag[2] fid[4]
	msgRstat                 // size[4] Rstat tag[2] stat[n]
	msgTwstat                // size[4] Twstat tag[2] fid[4] stat[n]
	msgRwstat                // size[4] Rwstat tag[2]
)

const NoTag uint16 = 0xFFFF

// Flags for the mode field in Topen and Tcreate messages
const (
	OREAD   = 0  // open read-only
	OWRITE  = 1  // open write-only
	ORDWR   = 2  // open read-write
	OEXEC   = 3  // execute (== read but check execute permission)
	OTRUNC  = 16 // or'ed in (except for exec), truncate file first
	OCEXEC  = 32 // or'ed in, close on exec
	ORCLOSE = 64 // or'ed in, remove on close
)

// File modes
const (
	DMDIR    = 0x80000000 // mode bit for directories
	DMAPPEND = 0x40000000 // mode bit for append only files
	DMEXCL   = 0x20000000 // mode bit for exclusive use files
	DMMOUNT  = 0x10000000 // mode bit for mounted channel
	DMAUTH   = 0x08000000 // mode bit for authentication file
	DMTMP    = 0x04000000 // mode bit for non-backed-up file
	DMREAD   = 0x4        // mode bit for read permission
	DMWRITE  = 0x2        // mode bit for write permission
	DMEXEC   = 0x1        // mode bit for execute permission
)
<|endoftext|>