<commit_before>// Copyright 2016-2017 Authors of Cilium
//
// 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 labels

import (
	"encoding/json"
	"strings"
	"testing"

	. "gopkg.in/check.v1"
)

// Hook up gocheck into the "go test" runner.
func Test(t *testing.T) {
	TestingT(t)
}

type LabelsSuite struct{}

var _ = Suite(&LabelsSuite{})

var (
	lblsArray = []string{`unspec:%=%ed`, `unspec://=/=`, `unspec:foo=bar`, `unspec:foo2==bar2`, `unspec:foo=====`, `unspec:foo\\==\=`, `unspec:key=`}
	lbls      = Labels{
		"foo":    NewLabel("foo", "bar", LabelSourceUnspec),
		"foo2":   NewLabel("foo2", "=bar2", LabelSourceUnspec),
		"key":    NewLabel("key", "", LabelSourceUnspec),
		"foo==":  NewLabel("foo==", "==", LabelSourceUnspec),
		`foo\\=`: NewLabel(`foo\\=`, `\=`, LabelSourceUnspec),
		`//=/`:   NewLabel(`//=/`, "", LabelSourceUnspec),
		`%`:      NewLabel(`%`, `%ed`, LabelSourceUnspec),
	}

	DefaultLabelSourceKeyPrefix = LabelSourceAny + "."
)

func (s *LabelsSuite) TestSHA256Sum(c *C) {
	str := lbls.SHA256Sum()
	c.Assert(str, Equals, "756e737065633a253d2565643b756e737065633a2f2f3d2f3d3b756e737065633a666f6f3d6261723b756e737065633a666f6f323d3d626172323b756e737065633a666f6f3d3d3d3d3d3b756e737065633a666f6f5c5c3d3d5c3d3b756e737065633a6b65793d3bc672b8d1ef56ed28ab87c3622c5114069bdd3ad7b8f9737498d0c01ecef0967a")
}

func (s *LabelsSuite) TestSortMap(c *C) {
	lblsString := strings.Join(lblsArray, ";")
	lblsString += ";"
	sortedMap := lbls.sortedList()
	c.Assert(sortedMap, DeepEquals, []byte(lblsString))
}

type lblTest struct {
	label  string
	result *Label
}

func (s *LabelsSuite) TestMap2Labels(c *C) {
	m := Map2Labels(map[string]string{
		"k8s:foo":  "bar",
		"k8s:foo2": "=bar2",
		"key":      "",
		"foo==":    "==",
		`foo\\=`:   `\=`,
		`//=/`:     "",
		`%`:        `%ed`,
	}, LabelSourceUnspec)
	c.Assert(m, DeepEquals, lbls)
}

func (s *LabelsSuite) TestMergeLabels(c *C) {
	to := Labels{
		"key1": NewLabel("key1", "value1", "source1"),
		"key2": NewLabel("key2", "value3", "source4"),
	}
	from := Labels{
		"key1": NewLabel("key1", "value3", "source4"),
	}
	want := Labels{
		"key1": NewLabel("key1", "value3", "source4"),
		"key2": NewLabel("key2", "value3", "source4"),
	}
	to.MergeLabels(from)
	from["key1"].Value = "changed"
	c.Assert(to, DeepEquals, want)
}

func (s *LabelsSuite) TestParseLabel(c *C) {
	tests := []struct {
		str string
		out *Label
	}{
		{"source1:key1=value1", NewLabel("key1", "value1", "source1")},
		{"key1=value1", NewLabel("key1", "value1", LabelSourceUnspec)},
		{"value1", NewLabel("value1", "", LabelSourceUnspec)},
		{"source1:key1", NewLabel("key1", "", "source1")},
		{"source1:key1==value1", NewLabel("key1", "=value1", "source1")},
		{"source::key1=value1", NewLabel("::key1", "value1", "source")},
		{"$key1=value1", NewLabel("key1", "value1", LabelSourceReserved)},
		{"1foo", NewLabel("1foo", "", LabelSourceUnspec)},
		{":2foo", NewLabel("2foo", "", LabelSourceUnspec)},
		{":3foo=", NewLabel("3foo", "", LabelSourceUnspec)},
		{"4blah=:foo=", NewLabel("foo", "", "4blah=")},
		{"5blah::foo=", NewLabel("::foo", "", "5blah")},
		{"6foo==", NewLabel("6foo", "=", LabelSourceUnspec)},
		{"7foo=bar", NewLabel("7foo", "bar", LabelSourceUnspec)},
		{"k8s:foo=bar:", NewLabel("foo", "bar:", "k8s")},
		{LabelSourceReservedKeyPrefix + "host", NewLabel("host", "", LabelSourceReserved)},
	}
	for _, test := range tests {
		lbl := ParseLabel(test.str)
		c.Assert(lbl, DeepEquals, test.out)
	}
}

func (s *LabelsSuite) TestParseSelectLabel(c *C) {
	tests := []struct {
		str string
		out *Label
	}{
		{"source1:key1=value1", NewLabel("key1", "value1", "source1")},
		{"key1=value1", NewLabel("key1", "value1", LabelSourceAny)},
		{"value1", NewLabel("value1", "", LabelSourceAny)},
		{"source1:key1", NewLabel("key1", "", "source1")},
		{"source1:key1==value1", NewLabel("key1", "=value1", "source1")},
		{"source::key1=value1", NewLabel("::key1", "value1", "source")},
		{"$key1=value1", NewLabel("key1", "value1", LabelSourceReserved)},
		{"1foo", NewLabel("1foo", "", LabelSourceAny)},
		{":2foo", NewLabel("2foo", "", LabelSourceAny)},
		{":3foo=", NewLabel("3foo", "", LabelSourceAny)},
		{"4blah=:foo=", NewLabel("foo", "", "4blah=")},
		{"5blah::foo=", NewLabel("::foo", "", "5blah")},
		{"6foo==", NewLabel("6foo", "=", LabelSourceAny)},
		{"7foo=bar", NewLabel("7foo", "bar", LabelSourceAny)},
		{"k8s:foo=bar:", NewLabel("foo", "bar:", "k8s")},
		{LabelSourceReservedKeyPrefix + "host", NewLabel("host", "", LabelSourceReserved)},
	}
	for _, test := range tests {
		lbl := ParseSelectLabel(test.str)
		c.Assert(lbl, DeepEquals, test.out)
	}
}

func (s *LabelsSuite) TestLabel(c *C) {
	var label Label

	longLabel := `{"source": "kubernetes", "key": "io.kubernetes.pod.name", "value": "foo"}`
	invLabel := `{"source": "kubernetes", "value": "foo"}`
	shortLabel := `"web"`

	err := json.Unmarshal([]byte(longLabel), &label)
	c.Assert(err, Equals, nil)
	c.Assert(label.Source, Equals, "kubernetes")
	c.Assert(label.Value, Equals, "foo")

	label = Label{}
	err = json.Unmarshal([]byte(invLabel), &label)
	c.Assert(err, Not(Equals), nil)

	label = Label{}
	err = json.Unmarshal([]byte(shortLabel), &label)
	c.Assert(err, Equals, nil)
	c.Assert(label.Source, Equals, LabelSourceUnspec)
	c.Assert(label.Value, Equals, "")

	label = Label{}
	err = json.Unmarshal([]byte(""), &label)
	c.Assert(err, Not(Equals), nil)
}

func (s *LabelsSuite) TestLabelCompare(c *C) {
	a1 := NewLabel(".", "", "")
	a2 := NewLabel(".", "", "")
	b1 := NewLabel("bar", "", LabelSourceUnspec)
	c1 := NewLabel("bar", "", "kubernetes")
	d1 := NewLabel("", "", "")

	c.Assert(a1.Equals(a2), Equals, true)
	c.Assert(a2.Equals(a1), Equals, true)
	c.Assert(a1.Equals(b1), Equals, false)
	c.Assert(a1.Equals(c1), Equals, false)
	c.Assert(a1.Equals(d1), Equals, false)
	c.Assert(b1.Equals(c1), Equals, false)
}

func (s *LabelsSuite) TestLabelParseKey(c *C) {
	tests := []struct {
		str string
		out string
	}{
		{"source0:key0=value1", "source0.key0"},
		{"source3:key1", "source3.key1"},
		{"source4:key1==value1", "source4.key1"},
		{"source::key1=value1", "source.:key1"},
		{"4blah=:foo=", "4blah=.foo"},
		{"5blah::foo=", "5blah.:foo"},
		{"source2.key1=value1", DefaultLabelSourceKeyPrefix + "source2.key1"},
		{"1foo", DefaultLabelSourceKeyPrefix + "1foo"},
		{":2foo", DefaultLabelSourceKeyPrefix + "2foo"},
		{":3foo=", DefaultLabelSourceKeyPrefix + "3foo"},
		{"6foo==", DefaultLabelSourceKeyPrefix + "6foo"},
		{"7foo=bar", DefaultLabelSourceKeyPrefix + "7foo"},
		{"cilium.key1=value1", DefaultLabelSourceKeyPrefix + "cilium.key1"},
		{"key1=value1", DefaultLabelSourceKeyPrefix + "key1"},
		{"value1", DefaultLabelSourceKeyPrefix + "value1"},
		{"$world=value1", LabelSourceReservedKeyPrefix + "world"},
		{"k8s:foo=bar:", LabelSourceK8sKeyPrefix + "foo"},
	}
	for _, test := range tests {
		lbl := GetExtendedKeyFrom(test.str)
		c.Assert(lbl, DeepEquals, test.out)
	}
}
<commit_msg> Issue 1453 :Fix another test TestSHA256Sum to test only 32bytes SHA and not the big incorrect length SHA  Signed-off by: Manali Bhutiyani manali@covalent.io<commit_after>// Copyright 2016-2017 Authors of Cilium
//
// 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 labels

import (
	"encoding/json"
	"strings"
	"testing"

	. "gopkg.in/check.v1"
)

// Hook up gocheck into the "go test" runner.
func Test(t *testing.T) {
	TestingT(t)
}

type LabelsSuite struct{}

var _ = Suite(&LabelsSuite{})

var (
	lblsArray = []string{`unspec:%=%ed`, `unspec://=/=`, `unspec:foo=bar`, `unspec:foo2==bar2`, `unspec:foo=====`, `unspec:foo\\==\=`, `unspec:key=`}
	lbls      = Labels{
		"foo":    NewLabel("foo", "bar", LabelSourceUnspec),
		"foo2":   NewLabel("foo2", "=bar2", LabelSourceUnspec),
		"key":    NewLabel("key", "", LabelSourceUnspec),
		"foo==":  NewLabel("foo==", "==", LabelSourceUnspec),
		`foo\\=`: NewLabel(`foo\\=`, `\=`, LabelSourceUnspec),
		`//=/`:   NewLabel(`//=/`, "", LabelSourceUnspec),
		`%`:      NewLabel(`%`, `%ed`, LabelSourceUnspec),
	}

	DefaultLabelSourceKeyPrefix = LabelSourceAny + "."
)

func (s *LabelsSuite) TestSHA256Sum(c *C) {
	str := lbls.SHA256Sum()
	c.Assert(str, Equals, "cf51cc7e153a09e82b242f2f0fb2f0f3923d2742a9d84de8bb0de669e5e558e3")
}

func (s *LabelsSuite) TestSortMap(c *C) {
	lblsString := strings.Join(lblsArray, ";")
	lblsString += ";"
	sortedMap := lbls.sortedList()
	c.Assert(sortedMap, DeepEquals, []byte(lblsString))
}

type lblTest struct {
	label  string
	result *Label
}

func (s *LabelsSuite) TestMap2Labels(c *C) {
	m := Map2Labels(map[string]string{
		"k8s:foo":  "bar",
		"k8s:foo2": "=bar2",
		"key":      "",
		"foo==":    "==",
		`foo\\=`:   `\=`,
		`//=/`:     "",
		`%`:        `%ed`,
	}, LabelSourceUnspec)
	c.Assert(m, DeepEquals, lbls)
}

func (s *LabelsSuite) TestMergeLabels(c *C) {
	to := Labels{
		"key1": NewLabel("key1", "value1", "source1"),
		"key2": NewLabel("key2", "value3", "source4"),
	}
	from := Labels{
		"key1": NewLabel("key1", "value3", "source4"),
	}
	want := Labels{
		"key1": NewLabel("key1", "value3", "source4"),
		"key2": NewLabel("key2", "value3", "source4"),
	}
	to.MergeLabels(from)
	from["key1"].Value = "changed"
	c.Assert(to, DeepEquals, want)
}

func (s *LabelsSuite) TestParseLabel(c *C) {
	tests := []struct {
		str string
		out *Label
	}{
		{"source1:key1=value1", NewLabel("key1", "value1", "source1")},
		{"key1=value1", NewLabel("key1", "value1", LabelSourceUnspec)},
		{"value1", NewLabel("value1", "", LabelSourceUnspec)},
		{"source1:key1", NewLabel("key1", "", "source1")},
		{"source1:key1==value1", NewLabel("key1", "=value1", "source1")},
		{"source::key1=value1", NewLabel("::key1", "value1", "source")},
		{"$key1=value1", NewLabel("key1", "value1", LabelSourceReserved)},
		{"1foo", NewLabel("1foo", "", LabelSourceUnspec)},
		{":2foo", NewLabel("2foo", "", LabelSourceUnspec)},
		{":3foo=", NewLabel("3foo", "", LabelSourceUnspec)},
		{"4blah=:foo=", NewLabel("foo", "", "4blah=")},
		{"5blah::foo=", NewLabel("::foo", "", "5blah")},
		{"6foo==", NewLabel("6foo", "=", LabelSourceUnspec)},
		{"7foo=bar", NewLabel("7foo", "bar", LabelSourceUnspec)},
		{"k8s:foo=bar:", NewLabel("foo", "bar:", "k8s")},
		{LabelSourceReservedKeyPrefix + "host", NewLabel("host", "", LabelSourceReserved)},
	}
	for _, test := range tests {
		lbl := ParseLabel(test.str)
		c.Assert(lbl, DeepEquals, test.out)
	}
}

func (s *LabelsSuite) TestParseSelectLabel(c *C) {
	tests := []struct {
		str string
		out *Label
	}{
		{"source1:key1=value1", NewLabel("key1", "value1", "source1")},
		{"key1=value1", NewLabel("key1", "value1", LabelSourceAny)},
		{"value1", NewLabel("value1", "", LabelSourceAny)},
		{"source1:key1", NewLabel("key1", "", "source1")},
		{"source1:key1==value1", NewLabel("key1", "=value1", "source1")},
		{"source::key1=value1", NewLabel("::key1", "value1", "source")},
		{"$key1=value1", NewLabel("key1", "value1", LabelSourceReserved)},
		{"1foo", NewLabel("1foo", "", LabelSourceAny)},
		{":2foo", NewLabel("2foo", "", LabelSourceAny)},
		{":3foo=", NewLabel("3foo", "", LabelSourceAny)},
		{"4blah=:foo=", NewLabel("foo", "", "4blah=")},
		{"5blah::foo=", NewLabel("::foo", "", "5blah")},
		{"6foo==", NewLabel("6foo", "=", LabelSourceAny)},
		{"7foo=bar", NewLabel("7foo", "bar", LabelSourceAny)},
		{"k8s:foo=bar:", NewLabel("foo", "bar:", "k8s")},
		{LabelSourceReservedKeyPrefix + "host", NewLabel("host", "", LabelSourceReserved)},
	}
	for _, test := range tests {
		lbl := ParseSelectLabel(test.str)
		c.Assert(lbl, DeepEquals, test.out)
	}
}

func (s *LabelsSuite) TestLabel(c *C) {
	var label Label

	longLabel := `{"source": "kubernetes", "key": "io.kubernetes.pod.name", "value": "foo"}`
	invLabel := `{"source": "kubernetes", "value": "foo"}`
	shortLabel := `"web"`

	err := json.Unmarshal([]byte(longLabel), &label)
	c.Assert(err, Equals, nil)
	c.Assert(label.Source, Equals, "kubernetes")
	c.Assert(label.Value, Equals, "foo")

	label = Label{}
	err = json.Unmarshal([]byte(invLabel), &label)
	c.Assert(err, Not(Equals), nil)

	label = Label{}
	err = json.Unmarshal([]byte(shortLabel), &label)
	c.Assert(err, Equals, nil)
	c.Assert(label.Source, Equals, LabelSourceUnspec)
	c.Assert(label.Value, Equals, "")

	label = Label{}
	err = json.Unmarshal([]byte(""), &label)
	c.Assert(err, Not(Equals), nil)
}

func (s *LabelsSuite) TestLabelCompare(c *C) {
	a1 := NewLabel(".", "", "")
	a2 := NewLabel(".", "", "")
	b1 := NewLabel("bar", "", LabelSourceUnspec)
	c1 := NewLabel("bar", "", "kubernetes")
	d1 := NewLabel("", "", "")

	c.Assert(a1.Equals(a2), Equals, true)
	c.Assert(a2.Equals(a1), Equals, true)
	c.Assert(a1.Equals(b1), Equals, false)
	c.Assert(a1.Equals(c1), Equals, false)
	c.Assert(a1.Equals(d1), Equals, false)
	c.Assert(b1.Equals(c1), Equals, false)
}

func (s *LabelsSuite) TestLabelParseKey(c *C) {
	tests := []struct {
		str string
		out string
	}{
		{"source0:key0=value1", "source0.key0"},
		{"source3:key1", "source3.key1"},
		{"source4:key1==value1", "source4.key1"},
		{"source::key1=value1", "source.:key1"},
		{"4blah=:foo=", "4blah=.foo"},
		{"5blah::foo=", "5blah.:foo"},
		{"source2.key1=value1", DefaultLabelSourceKeyPrefix + "source2.key1"},
		{"1foo", DefaultLabelSourceKeyPrefix + "1foo"},
		{":2foo", DefaultLabelSourceKeyPrefix + "2foo"},
		{":3foo=", DefaultLabelSourceKeyPrefix + "3foo"},
		{"6foo==", DefaultLabelSourceKeyPrefix + "6foo"},
		{"7foo=bar", DefaultLabelSourceKeyPrefix + "7foo"},
		{"cilium.key1=value1", DefaultLabelSourceKeyPrefix + "cilium.key1"},
		{"key1=value1", DefaultLabelSourceKeyPrefix + "key1"},
		{"value1", DefaultLabelSourceKeyPrefix + "value1"},
		{"$world=value1", LabelSourceReservedKeyPrefix + "world"},
		{"k8s:foo=bar:", LabelSourceK8sKeyPrefix + "foo"},
	}
	for _, test := range tests {
		lbl := GetExtendedKeyFrom(test.str)
		c.Assert(lbl, DeepEquals, test.out)
	}
}
<|endoftext|>


<commit_before>/*
Copyright 2019 The Kubernetes Authors 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 logs are convenience methods for fetching logs from a minikube cluster
package logs

import (
	"bufio"
	"bytes"
	"fmt"
	"io"
	"os"
	"os/exec"
	"regexp"
	"sort"
	"strings"

	"github.com/pkg/errors"
	"k8s.io/klog/v2"
	"k8s.io/minikube/pkg/minikube/audit"
	"k8s.io/minikube/pkg/minikube/bootstrapper"
	"k8s.io/minikube/pkg/minikube/command"
	"k8s.io/minikube/pkg/minikube/config"
	"k8s.io/minikube/pkg/minikube/cruntime"
	"k8s.io/minikube/pkg/minikube/localpath"
	"k8s.io/minikube/pkg/minikube/out"
	"k8s.io/minikube/pkg/minikube/style"
)

// rootCauses are regular expressions that match known failures
var rootCauses = []string{
	`^error: `,
	`eviction manager: pods.* evicted`,
	`unknown flag: --`,
	`forbidden.*no providers available`,
	`eviction manager:.*evicted`,
	`tls: bad certificate`,
	`kubelet.*no API client`,
	`kubelet.*No api server`,
	`STDIN.*127.0.0.1:8080`,
	`failed to create listener`,
	`address already in use`,
	`unable to evict any pods`,
	`eviction manager: unexpected error`,
	`Resetting AnonymousAuth to false`,
	`Unable to register node.*forbidden`,
	`Failed to initialize CSINodeInfo.*forbidden`,
	`Failed to admit pod`,
	`failed to "StartContainer"`,
	`Failed to start ContainerManager`,
	`kubelet.*forbidden.*cannot \w+ resource`,
	`leases.*forbidden.*cannot \w+ resource`,
	`failed to start daemon`,
}

// rootCauseRe combines rootCauses into a single regex
var rootCauseRe = regexp.MustCompile(strings.Join(rootCauses, "|"))

// ignoreCauseRe is a regular expression that matches spurious errors to not surface
var ignoreCauseRe = regexp.MustCompile("error: no objects passed to apply")

// importantPods are a list of pods to retrieve logs for, in addition to the bootstrapper logs.
var importantPods = []string{
	"kube-apiserver",
	"etcd",
	"coredns",
	"kube-scheduler",
	"kube-proxy",
	"kubernetes-dashboard",
	"storage-provisioner",
	"kube-controller-manager",
}

// logRunner is the subset of CommandRunner used for logging
type logRunner interface {
	RunCmd(*exec.Cmd) (*command.RunResult, error)
}

// lookbackwardsCount is how far back to look in a log for problems. This should be large enough to
// include usage messages from a failed binary, but small enough to not include irrelevant problems.
const lookBackwardsCount = 400

// Follow follows logs from multiple files in tail(1) format
func Follow(r cruntime.Manager, bs bootstrapper.Bootstrapper, cfg config.ClusterConfig, cr logRunner, logOutput io.Writer) error {
	cs := []string{}
	for _, v := range logCommands(r, bs, cfg, 0, true) {
		cs = append(cs, v+" &")
	}
	cs = append(cs, "wait")

	cmd := exec.Command("/bin/bash", "-c", strings.Join(cs, " "))
	cmd.Stdout = logOutput
	cmd.Stderr = logOutput
	if _, err := cr.RunCmd(cmd); err != nil {
		return errors.Wrapf(err, "log follow")
	}
	return nil
}

// IsProblem returns whether this line matches a known problem
func IsProblem(line string) bool {
	return rootCauseRe.MatchString(line) && !ignoreCauseRe.MatchString(line)
}

// FindProblems finds possible root causes among the logs
func FindProblems(r cruntime.Manager, bs bootstrapper.Bootstrapper, cfg config.ClusterConfig, cr logRunner) map[string][]string {
	pMap := map[string][]string{}
	cmds := logCommands(r, bs, cfg, lookBackwardsCount, false)
	for name := range cmds {
		klog.Infof("Gathering logs for %s ...", name)
		var b bytes.Buffer
		c := exec.Command("/bin/bash", "-c", cmds[name])
		c.Stderr = &b
		c.Stdout = &b

		if rr, err := cr.RunCmd(c); err != nil {
			klog.Warningf("failed %s: command: %s %v output: %s", name, rr.Command(), err, rr.Output())
			continue
		}
		scanner := bufio.NewScanner(&b)
		problems := []string{}
		for scanner.Scan() {
			l := scanner.Text()
			if IsProblem(l) {
				klog.Warningf("Found %s problem: %s", name, l)
				problems = append(problems, l)
			}
		}
		if len(problems) > 0 {
			pMap[name] = problems
		}
	}
	return pMap
}

// OutputProblems outputs discovered problems.
func OutputProblems(problems map[string][]string, maxLines int, logOutput *os.File) {
	out.SetErrFile(logOutput)
	defer out.SetErrFile(os.Stderr)

	for name, lines := range problems {
		out.FailureT("Problems detected in {{.name}}:", out.V{"name": name})
		if len(lines) > maxLines {
			lines = lines[len(lines)-maxLines:]
		}
		for _, l := range lines {
			out.ErrT(style.LogEntry, l)
		}
	}
}

// Output displays logs from multiple sources in tail(1) format
func Output(r cruntime.Manager, bs bootstrapper.Bootstrapper, cfg config.ClusterConfig, runner command.Runner, lines int, logOutput *os.File) error {
	cmds := logCommands(r, bs, cfg, lines, false)
	cmds["kernel"] = "uptime && uname -a && grep PRETTY /etc/os-release"

	names := []string{}
	for k := range cmds {
		names = append(names, k)
	}

	out.SetOutFile(logOutput)
	defer out.SetOutFile(os.Stdout)

	sort.Strings(names)
	failed := []string{}
	for i, name := range names {
		if i > 0 {
			out.Styled(style.Empty, "")
		}
		out.Styled(style.Empty, "==> {{.name}} <==", out.V{"name": name})
		var b bytes.Buffer
		c := exec.Command("/bin/bash", "-c", cmds[name])
		c.Stdout = &b
		c.Stderr = &b
		if rr, err := runner.RunCmd(c); err != nil {
			klog.Errorf("command %s failed with error: %v output: %q", rr.Command(), err, rr.Output())
			failed = append(failed, name)
			continue
		}
		l := ""
		scanner := bufio.NewScanner(&b)
		for scanner.Scan() {
			l += scanner.Text() + "\n"
		}
		out.Styled(style.Empty, l)
	}

	if len(failed) > 0 {
		return fmt.Errorf("unable to fetch logs for: %s", strings.Join(failed, ", "))
	}
	return nil
}

// outputAudit displays the audit logs.
func outputAudit(lines int) error {
	out.Styled(style.Empty, "")
	out.Styled(style.Empty, "==> Audit <==")
	r, err := audit.Report(lines)
	if err != nil {
		return fmt.Errorf("failed to create audit report: %v", err)
	}
	out.Styled(style.Empty, r.ASCIITable())
	return nil
}

// outputLastStart outputs the last start logs.
func outputLastStart() error {
	out.Styled(style.Empty, "")
	out.Styled(style.Empty, "==> Last Start <==")
	fp := localpath.LastStartLog()
	f, err := os.Open(fp)
	if os.IsNotExist(err) {
		msg := fmt.Sprintf("Last start log file not found at %s", fp)
		out.Styled(style.Empty, msg)
		return nil
	}
	if err != nil {
		return fmt.Errorf("failed to open file %s: %v", fp, err)
	}
	defer f.Close()
	l := ""
	s := bufio.NewScanner(f)
	for s.Scan() {
		l += s.Text() + "\n"
	}
	out.Styled(style.Empty, l)
	if err := s.Err(); err != nil {
		return fmt.Errorf("failed to read file %s: %v", fp, err)
	}
	return nil
}

// OutputOffline outputs logs that don't need a running cluster.
func OutputOffline(lines int, logOutput *os.File) {
	out.SetOutFile(logOutput)
	defer out.SetOutFile(os.Stdout)
	if err := outputAudit(lines); err != nil {
		klog.Errorf("failed to output audit logs: %v", err)
	}
	if err := outputLastStart(); err != nil {
		klog.Errorf("failed to output last start logs: %v", err)
	}

	out.Styled(style.Empty, "")
}

// logCommands returns a list of commands that would be run to receive the anticipated logs
func logCommands(r cruntime.Manager, bs bootstrapper.Bootstrapper, cfg config.ClusterConfig, length int, follow bool) map[string]string {
	cmds := bs.LogCommands(cfg, bootstrapper.LogOptions{Lines: length, Follow: follow})
	for _, pod := range importantPods {
		ids, err := r.ListContainers(cruntime.ListContainersOptions{Name: pod})
		if err != nil {
			klog.Errorf("Failed to list containers for %q: %v", pod, err)
			continue
		}
		klog.Infof("%d containers: %s", len(ids), ids)
		if len(ids) == 0 {
			klog.Warningf("No container was found matching %q", pod)
			continue
		}
		for _, i := range ids {
			key := fmt.Sprintf("%s [%s]", pod, i)
			cmds[key] = r.ContainerLogCmd(i, length, follow)
		}
	}
	cmds[r.Name()] = r.SystemLogCmd(length)
	cmds["container status"] = cruntime.ContainerStatusCommand()

	return cmds
}
<commit_msg>fix TestFunctional/serial/LogsFileCmd test failure<commit_after>/*
Copyright 2019 The Kubernetes Authors 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 logs are convenience methods for fetching logs from a minikube cluster
package logs

import (
	"bufio"
	"bytes"
	"fmt"
	"io"
	"os"
	"os/exec"
	"regexp"
	"sort"
	"strings"

	"github.com/pkg/errors"
	"k8s.io/klog/v2"
	"k8s.io/minikube/pkg/minikube/audit"
	"k8s.io/minikube/pkg/minikube/bootstrapper"
	"k8s.io/minikube/pkg/minikube/command"
	"k8s.io/minikube/pkg/minikube/config"
	"k8s.io/minikube/pkg/minikube/cruntime"
	"k8s.io/minikube/pkg/minikube/localpath"
	"k8s.io/minikube/pkg/minikube/out"
	"k8s.io/minikube/pkg/minikube/style"
)

// rootCauses are regular expressions that match known failures
var rootCauses = []string{
	`^error: `,
	`eviction manager: pods.* evicted`,
	`unknown flag: --`,
	`forbidden.*no providers available`,
	`eviction manager:.*evicted`,
	`tls: bad certificate`,
	`kubelet.*no API client`,
	`kubelet.*No api server`,
	`STDIN.*127.0.0.1:8080`,
	`failed to create listener`,
	`address already in use`,
	`unable to evict any pods`,
	`eviction manager: unexpected error`,
	`Resetting AnonymousAuth to false`,
	`Unable to register node.*forbidden`,
	`Failed to initialize CSINodeInfo.*forbidden`,
	`Failed to admit pod`,
	`failed to "StartContainer"`,
	`Failed to start ContainerManager`,
	`kubelet.*forbidden.*cannot \w+ resource`,
	`leases.*forbidden.*cannot \w+ resource`,
	`failed to start daemon`,
}

// rootCauseRe combines rootCauses into a single regex
var rootCauseRe = regexp.MustCompile(strings.Join(rootCauses, "|"))

// ignoreCauseRe is a regular expression that matches spurious errors to not surface
var ignoreCauseRe = regexp.MustCompile("error: no objects passed to apply")

// importantPods are a list of pods to retrieve logs for, in addition to the bootstrapper logs.
var importantPods = []string{
	"kube-apiserver",
	"etcd",
	"coredns",
	"kube-scheduler",
	"kube-proxy",
	"kubernetes-dashboard",
	"storage-provisioner",
	"kube-controller-manager",
}

// logRunner is the subset of CommandRunner used for logging
type logRunner interface {
	RunCmd(*exec.Cmd) (*command.RunResult, error)
}

// lookbackwardsCount is how far back to look in a log for problems. This should be large enough to
// include usage messages from a failed binary, but small enough to not include irrelevant problems.
const lookBackwardsCount = 400

// Follow follows logs from multiple files in tail(1) format
func Follow(r cruntime.Manager, bs bootstrapper.Bootstrapper, cfg config.ClusterConfig, cr logRunner, logOutput io.Writer) error {
	cs := []string{}
	for _, v := range logCommands(r, bs, cfg, 0, true) {
		cs = append(cs, v+" &")
	}
	cs = append(cs, "wait")

	cmd := exec.Command("/bin/bash", "-c", strings.Join(cs, " "))
	cmd.Stdout = logOutput
	cmd.Stderr = logOutput
	if _, err := cr.RunCmd(cmd); err != nil {
		return errors.Wrapf(err, "log follow")
	}
	return nil
}

// IsProblem returns whether this line matches a known problem
func IsProblem(line string) bool {
	return rootCauseRe.MatchString(line) && !ignoreCauseRe.MatchString(line)
}

// FindProblems finds possible root causes among the logs
func FindProblems(r cruntime.Manager, bs bootstrapper.Bootstrapper, cfg config.ClusterConfig, cr logRunner) map[string][]string {
	pMap := map[string][]string{}
	cmds := logCommands(r, bs, cfg, lookBackwardsCount, false)
	for name := range cmds {
		klog.Infof("Gathering logs for %s ...", name)
		var b bytes.Buffer
		c := exec.Command("/bin/bash", "-c", cmds[name])
		c.Stderr = &b
		c.Stdout = &b

		if rr, err := cr.RunCmd(c); err != nil {
			klog.Warningf("failed %s: command: %s %v output: %s", name, rr.Command(), err, rr.Output())
			continue
		}
		scanner := bufio.NewScanner(&b)
		problems := []string{}
		for scanner.Scan() {
			l := scanner.Text()
			if IsProblem(l) {
				klog.Warningf("Found %s problem: %s", name, l)
				problems = append(problems, l)
			}
		}
		if len(problems) > 0 {
			pMap[name] = problems
		}
	}
	return pMap
}

// OutputProblems outputs discovered problems.
func OutputProblems(problems map[string][]string, maxLines int, logOutput *os.File) {
	out.SetErrFile(logOutput)
	defer out.SetErrFile(os.Stderr)

	for name, lines := range problems {
		out.FailureT("Problems detected in {{.name}}:", out.V{"name": name})
		if len(lines) > maxLines {
			lines = lines[len(lines)-maxLines:]
		}
		for _, l := range lines {
			out.ErrT(style.LogEntry, l)
		}
	}
}

// Output displays logs from multiple sources in tail(1) format
func Output(r cruntime.Manager, bs bootstrapper.Bootstrapper, cfg config.ClusterConfig, runner command.Runner, lines int, logOutput *os.File) error {
	cmds := logCommands(r, bs, cfg, lines, false)
	cmds["kernel"] = "uptime && uname -a && grep PRETTY /etc/os-release"

	names := []string{}
	for k := range cmds {
		names = append(names, k)
	}

	out.SetOutFile(logOutput)
	defer out.SetOutFile(os.Stdout)
	out.SetErrFile(logOutput)
	defer out.SetErrFile(os.Stderr)

	sort.Strings(names)
	failed := []string{}
	for i, name := range names {
		if i > 0 {
			out.Styled(style.Empty, "")
		}
		out.Styled(style.Empty, "==> {{.name}} <==", out.V{"name": name})
		var b bytes.Buffer
		c := exec.Command("/bin/bash", "-c", cmds[name])
		c.Stdout = &b
		c.Stderr = &b
		if rr, err := runner.RunCmd(c); err != nil {
			klog.Errorf("command %s failed with error: %v output: %q", rr.Command(), err, rr.Output())
			failed = append(failed, name)
			continue
		}
		l := ""
		scanner := bufio.NewScanner(&b)
		for scanner.Scan() {
			l += scanner.Text() + "\n"
		}
		out.Styled(style.Empty, l)
	}

	if len(failed) > 0 {
		return fmt.Errorf("unable to fetch logs for: %s", strings.Join(failed, ", "))
	}
	return nil
}

// outputAudit displays the audit logs.
func outputAudit(lines int) error {
	out.Styled(style.Empty, "")
	out.Styled(style.Empty, "==> Audit <==")
	r, err := audit.Report(lines)
	if err != nil {
		return fmt.Errorf("failed to create audit report: %v", err)
	}
	out.Styled(style.Empty, r.ASCIITable())
	return nil
}

// outputLastStart outputs the last start logs.
func outputLastStart() error {
	out.Styled(style.Empty, "")
	out.Styled(style.Empty, "==> Last Start <==")
	fp := localpath.LastStartLog()
	f, err := os.Open(fp)
	if os.IsNotExist(err) {
		msg := fmt.Sprintf("Last start log file not found at %s", fp)
		out.Styled(style.Empty, msg)
		return nil
	}
	if err != nil {
		return fmt.Errorf("failed to open file %s: %v", fp, err)
	}
	defer f.Close()
	l := ""
	s := bufio.NewScanner(f)
	for s.Scan() {
		l += s.Text() + "\n"
	}
	out.Styled(style.Empty, l)
	if err := s.Err(); err != nil {
		return fmt.Errorf("failed to read file %s: %v", fp, err)
	}
	return nil
}

// OutputOffline outputs logs that don't need a running cluster.
func OutputOffline(lines int, logOutput *os.File) {
	out.SetOutFile(logOutput)
	defer out.SetOutFile(os.Stdout)
	out.SetErrFile(logOutput)
	defer out.SetErrFile(os.Stderr)
	if err := outputAudit(lines); err != nil {
		klog.Errorf("failed to output audit logs: %v", err)
	}
	if err := outputLastStart(); err != nil {
		klog.Errorf("failed to output last start logs: %v", err)
	}

	out.Styled(style.Empty, "")
}

// logCommands returns a list of commands that would be run to receive the anticipated logs
func logCommands(r cruntime.Manager, bs bootstrapper.Bootstrapper, cfg config.ClusterConfig, length int, follow bool) map[string]string {
	cmds := bs.LogCommands(cfg, bootstrapper.LogOptions{Lines: length, Follow: follow})
	for _, pod := range importantPods {
		ids, err := r.ListContainers(cruntime.ListContainersOptions{Name: pod})
		if err != nil {
			klog.Errorf("Failed to list containers for %q: %v", pod, err)
			continue
		}
		klog.Infof("%d containers: %s", len(ids), ids)
		if len(ids) == 0 {
			klog.Warningf("No container was found matching %q", pod)
			continue
		}
		for _, i := range ids {
			key := fmt.Sprintf("%s [%s]", pod, i)
			cmds[key] = r.ContainerLogCmd(i, length, follow)
		}
	}
	cmds[r.Name()] = r.SystemLogCmd(length)
	cmds["container status"] = cruntime.ContainerStatusCommand()

	return cmds
}
<|endoftext|>


<commit_before>package order

import (
	"fmt"

	"github.com/bitfinexcom/bitfinex-api-go/pkg/convert"
)

type Order struct {
	ID            int64
	GID           int64
	CID           int64
	Symbol        string
	MTSCreated    int64
	MTSUpdated    int64
	Amount        float64
	AmountOrig    float64
	Type          string
	TypePrev      string
	MTSTif        int64
	Flags         int64
	Status        string
	Price         float64
	PriceAvg      float64
	PriceTrailing float64
	PriceAuxLimit float64
	Notify        bool
	Hidden        bool
	PlacedID      int64
	Meta          map[string]interface{}
}

// Snapshot is a collection of Orders that would usually be sent on
// inital connection.
type Snapshot struct {
	Snapshot []*Order
}

// Update is an Order that gets sent out after every change to an order.
type Update Order

// New gets sent out after an Order was created successfully.
type New Order

// Cancel gets sent out after an Order was cancelled successfully.
type Cancel Order

// FromRaw takes the raw list of values as returned from the websocket
// service and tries to convert it into an Order.
func FromRaw(raw []interface{}) (o *Order, err error) {
	if len(raw) == 12 {
		o = &Order{
			ID:         convert.I64ValOrZero(raw[0]),
			Symbol:     convert.SValOrEmpty(raw[1]),
			Amount:     convert.F64ValOrZero(raw[2]),
			AmountOrig: convert.F64ValOrZero(raw[3]),
			Type:       convert.SValOrEmpty(raw[4]),
			Status:     convert.SValOrEmpty(raw[5]),
			Price:      convert.F64ValOrZero(raw[6]),
			PriceAvg:   convert.F64ValOrZero(raw[7]),
			MTSUpdated: convert.I64ValOrZero(raw[8]),
		}
		return
	}

	if len(raw) < 26 {
		return o, fmt.Errorf("data slice too short for order: %#v", raw)
	}

	o = &Order{
		ID:            convert.I64ValOrZero(raw[0]),
		GID:           convert.I64ValOrZero(raw[1]),
		CID:           convert.I64ValOrZero(raw[2]),
		Symbol:        convert.SValOrEmpty(raw[3]),
		MTSCreated:    convert.I64ValOrZero(raw[4]),
		MTSUpdated:    convert.I64ValOrZero(raw[5]),
		Amount:        convert.F64ValOrZero(raw[6]),
		AmountOrig:    convert.F64ValOrZero(raw[7]),
		Type:          convert.SValOrEmpty(raw[8]),
		TypePrev:      convert.SValOrEmpty(raw[9]),
		MTSTif:        convert.I64ValOrZero(raw[10]),
		Flags:         convert.I64ValOrZero(raw[12]),
		Status:        convert.SValOrEmpty(raw[13]),
		Price:         convert.F64ValOrZero(raw[16]),
		PriceAvg:      convert.F64ValOrZero(raw[17]),
		PriceTrailing: convert.F64ValOrZero(raw[18]),
		PriceAuxLimit: convert.F64ValOrZero(raw[19]),
		Notify:        convert.BValOrFalse(raw[23]),
		Hidden:        convert.BValOrFalse(raw[24]),
		PlacedID:      convert.I64ValOrZero(raw[25]),
	}

	if len(raw) >= 31 {
		o.Meta = convert.SiMapOrEmpty(raw[31])
	}

	return
}

// SnapshotFromRaw takes a raw list of values as returned from the websocket
// service and tries to convert it into an Snapshot.
func SnapshotFromRaw(raw []interface{}) (s *Snapshot, err error) {
	if len(raw) == 0 {
		return s, fmt.Errorf("data slice too short for order: %#v", raw)
	}

	os := make([]*Order, 0)
	switch raw[0].(type) {
	case []interface{}:
		for _, v := range raw {
			if l, ok := v.([]interface{}); ok {
				o, err := FromRaw(l)
				if err != nil {
					return s, err
				}
				os = append(os, o)
			}
		}
	default:
		return s, fmt.Errorf("not an order snapshot")
	}
	s = &Snapshot{Snapshot: os}

	return
}

func FromWSRaw(raw []interface{}, op string) (interface{}, error) {
	if op == "os" {
		return SnapshotFromRaw(raw)
	}

	o, err := FromRaw(raw)
	if err != nil {
		return nil, err
	}

	switch op {
	case "on":
		return New(*o), nil
	}

	return nil, fmt.Errorf("unrecognized order operation: %s", op)
}
<commit_msg>enriching order model with functionality to explicitly get order new, cancel, snapshot and update types<commit_after>package order

import (
	"fmt"

	"github.com/bitfinexcom/bitfinex-api-go/pkg/convert"
)

type Order struct {
	ID            int64
	GID           int64
	CID           int64
	Symbol        string
	MTSCreated    int64
	MTSUpdated    int64
	Amount        float64
	AmountOrig    float64
	Type          string
	TypePrev      string
	MTSTif        int64
	Flags         int64
	Status        string
	Price         float64
	PriceAvg      float64
	PriceTrailing float64
	PriceAuxLimit float64
	Notify        bool
	Hidden        bool
	PlacedID      int64
	Meta          map[string]interface{}
}

// Snapshot is a collection of Orders that would usually be sent on
// inital connection.
type Snapshot struct {
	Snapshot []*Order
}

// Update is an Order that gets sent out after every change to an order.
type Update Order

// New gets sent out after an Order was created successfully.
type New Order

// Cancel gets sent out after an Order was cancelled successfully.
type Cancel Order

// FromRaw takes the raw list of values as returned from the websocket
// service and tries to convert it into an Order.
func FromRaw(raw []interface{}) (o *Order, err error) {
	if len(raw) == 12 {
		o = &Order{
			ID:         convert.I64ValOrZero(raw[0]),
			Symbol:     convert.SValOrEmpty(raw[1]),
			Amount:     convert.F64ValOrZero(raw[2]),
			AmountOrig: convert.F64ValOrZero(raw[3]),
			Type:       convert.SValOrEmpty(raw[4]),
			Status:     convert.SValOrEmpty(raw[5]),
			Price:      convert.F64ValOrZero(raw[6]),
			PriceAvg:   convert.F64ValOrZero(raw[7]),
			MTSUpdated: convert.I64ValOrZero(raw[8]),
		}
		return
	}

	if len(raw) < 26 {
		return o, fmt.Errorf("data slice too short for order: %#v", raw)
	}

	o = &Order{
		ID:            convert.I64ValOrZero(raw[0]),
		GID:           convert.I64ValOrZero(raw[1]),
		CID:           convert.I64ValOrZero(raw[2]),
		Symbol:        convert.SValOrEmpty(raw[3]),
		MTSCreated:    convert.I64ValOrZero(raw[4]),
		MTSUpdated:    convert.I64ValOrZero(raw[5]),
		Amount:        convert.F64ValOrZero(raw[6]),
		AmountOrig:    convert.F64ValOrZero(raw[7]),
		Type:          convert.SValOrEmpty(raw[8]),
		TypePrev:      convert.SValOrEmpty(raw[9]),
		MTSTif:        convert.I64ValOrZero(raw[10]),
		Flags:         convert.I64ValOrZero(raw[12]),
		Status:        convert.SValOrEmpty(raw[13]),
		Price:         convert.F64ValOrZero(raw[16]),
		PriceAvg:      convert.F64ValOrZero(raw[17]),
		PriceTrailing: convert.F64ValOrZero(raw[18]),
		PriceAuxLimit: convert.F64ValOrZero(raw[19]),
		Notify:        convert.BValOrFalse(raw[23]),
		Hidden:        convert.BValOrFalse(raw[24]),
		PlacedID:      convert.I64ValOrZero(raw[25]),
	}

	if len(raw) >= 31 {
		o.Meta = convert.SiMapOrEmpty(raw[31])
	}

	return
}

// NewFromRaw reds "on" type message from data sream and
// maps it to order.New data structure
func NewFromRaw(raw []interface{}) (New, error) {
	o, err := FromRaw(raw)
	if err != nil {
		return New{}, err
	}

	return New(*o), nil
}

// UpdateFromRaw reds "ou" type message from data sream and
// maps it to order.Update data structure
func UpdateFromRaw(raw []interface{}) (Update, error) {
	o, err := FromRaw(raw)
	if err != nil {
		return Update{}, err
	}

	return Update(*o), nil
}

// CancelFromRaw reds "oc" type message from data sream and
// maps it to order.Cancel data structure
func CancelFromRaw(raw []interface{}) (Cancel, error) {
	o, err := FromRaw(raw)
	if err != nil {
		return Cancel{}, err
	}

	return Cancel(*o), nil
}

// SnapshotFromRaw takes a raw list of values as returned from the websocket
// service and tries to convert it into an Snapshot.
func SnapshotFromRaw(raw []interface{}) (s *Snapshot, err error) {
	if len(raw) == 0 {
		return s, fmt.Errorf("data slice too short for order: %#v", raw)
	}

	os := make([]*Order, 0)
	switch raw[0].(type) {
	case []interface{}:
		for _, v := range raw {
			if l, ok := v.([]interface{}); ok {
				o, err := FromRaw(l)
				if err != nil {
					return s, err
				}
				os = append(os, o)
			}
		}
	default:
		return s, fmt.Errorf("not an order snapshot")
	}
	s = &Snapshot{Snapshot: os}

	return
}
<|endoftext|>


<commit_before>package common

import (
	"fmt"
	"net"
	"sync"
	"time"

	"github.com/miekg/dns"

	kerrors "k8s.io/apimachinery/pkg/util/errors"
	utilruntime "k8s.io/apimachinery/pkg/util/runtime"
	"k8s.io/apimachinery/pkg/util/sets"
)

const (
	defaultTTL = 30 * time.Minute
)

type dnsValue struct {
	// All IPv4 addresses for a given domain name
	ips []net.IP
	// Time-to-live value from non-authoritative/cached name server for the domain
	ttl time.Duration
	// Holds (last dns lookup time + ttl), tells when to refresh IPs next time
	nextQueryTime time.Time
}

type DNS struct {
	// Protects dnsMap operations
	lock sync.Mutex
	// Holds dns name and its corresponding information
	dnsMap map[string]dnsValue

	// DNS resolvers
	nameservers []string
	// DNS port
	port string
}

func NewDNS(resolverConfigFile string) (*DNS, error) {
	config, err := dns.ClientConfigFromFile(resolverConfigFile)
	if err != nil || config == nil {
		return nil, fmt.Errorf("cannot initialize the resolver: %v", err)
	}

	return &DNS{
		dnsMap:      map[string]dnsValue{},
		nameservers: filterIPv4Servers(config.Servers),
		port:        config.Port,
	}, nil
}

func (d *DNS) Size() int {
	d.lock.Lock()
	defer d.lock.Unlock()

	return len(d.dnsMap)
}

func (d *DNS) Get(dns string) dnsValue {
	d.lock.Lock()
	defer d.lock.Unlock()

	data := dnsValue{}
	if res, ok := d.dnsMap[dns]; ok {
		data.ips = make([]net.IP, len(res.ips))
		copy(data.ips, res.ips)
		data.ttl = res.ttl
		data.nextQueryTime = res.nextQueryTime
	}
	return data
}

func (d *DNS) Add(dns string) error {
	d.lock.Lock()
	defer d.lock.Unlock()

	d.dnsMap[dns] = dnsValue{}
	err, _ := d.updateOne(dns)
	if err != nil {
		delete(d.dnsMap, dns)
	}
	return err
}

func (d *DNS) Update() (error, bool) {
	d.lock.Lock()
	defer d.lock.Unlock()

	errList := []error{}
	changed := false
	for dns := range d.dnsMap {
		err, updated := d.updateOne(dns)
		if err != nil {
			errList = append(errList, err)
			continue
		}
		if updated {
			changed = true
		}
	}
	return kerrors.NewAggregate(errList), changed
}

func (d *DNS) updateOne(dns string) (error, bool) {
	res, ok := d.dnsMap[dns]
	if !ok {
		// Should not happen, all operations on dnsMap are synchronized by d.lock
		return fmt.Errorf("DNS value not found in dnsMap for domain: %q", dns), false
	}

	ips, minTTL, err := d.getIPsAndMinTTL(dns)
	if err != nil {
		res.nextQueryTime = time.Now().Add(defaultTTL)
		d.dnsMap[dns] = res
		return err, false
	}

	changed := false
	if !ipsEqual(res.ips, ips) {
		changed = true
	}
	res.ips = ips
	res.ttl = minTTL
	res.nextQueryTime = time.Now().Add(res.ttl)
	d.dnsMap[dns] = res
	return nil, changed
}

func (d *DNS) getIPsAndMinTTL(domain string) ([]net.IP, time.Duration, error) {
	ips := []net.IP{}
	ttlSet := false
	var minTTL uint32

	for _, server := range d.nameservers {
		msg := new(dns.Msg)
		msg.SetQuestion(dns.Fqdn(domain), dns.TypeA)

		dialServer := server
		if _, _, err := net.SplitHostPort(server); err != nil {
			dialServer = net.JoinHostPort(server, d.port)
		}
		c := new(dns.Client)
		c.Timeout = 2 * time.Second
		in, _, err := c.Exchange(msg, dialServer)
		if err != nil {
			return nil, defaultTTL, err
		}
		if in != nil && in.Rcode != dns.RcodeSuccess {
			return nil, defaultTTL, fmt.Errorf("failed to get a valid answer: %v", in)
		}

		if in != nil && len(in.Answer) > 0 {
			for _, a := range in.Answer {
				if !ttlSet || a.Header().Ttl < minTTL {
					minTTL = a.Header().Ttl
					ttlSet = true
				}

				switch t := a.(type) {
				case *dns.A:
					ips = append(ips, t.A)
				}
			}
		}
	}

	if !ttlSet || (len(ips) == 0) {
		return nil, defaultTTL, fmt.Errorf("IPv4 addr not found for domain: %q, nameservers: %v", domain, d.nameservers)
	}

	ttl, err := time.ParseDuration(fmt.Sprintf("%ds", minTTL))
	if err != nil {
		utilruntime.HandleError(fmt.Errorf("Invalid TTL value for domain: %q, err: %v, defaulting ttl=%s", domain, err, defaultTTL.String()))
		ttl = defaultTTL
	}

	return removeDuplicateIPs(ips), ttl, nil
}

func (d *DNS) GetMinQueryTime() (time.Time, bool) {
	d.lock.Lock()
	defer d.lock.Unlock()

	timeSet := false
	var minTime time.Time
	for _, res := range d.dnsMap {
		if (timeSet == false) || res.nextQueryTime.Before(minTime) {
			timeSet = true
			minTime = res.nextQueryTime
		}
	}

	return minTime, timeSet
}

func ipsEqual(oldips, newips []net.IP) bool {
	if len(oldips) != len(newips) {
		return false
	}

	for _, oldip := range oldips {
		found := false
		for _, newip := range newips {
			if oldip.Equal(newip) {
				found = true
				break
			}
		}
		if !found {
			return false
		}
	}
	return true
}

func filterIPv4Servers(servers []string) []string {
	ipv4Servers := []string{}
	for _, server := range servers {
		ipString := server
		if host, _, err := net.SplitHostPort(server); err == nil {
			ipString = host
		}

		if ip := net.ParseIP(ipString); ip != nil {
			if ip.To4() != nil {
				ipv4Servers = append(ipv4Servers, server)
			}
		}
	}

	return ipv4Servers
}

func removeDuplicateIPs(ips []net.IP) []net.IP {
	ipSet := sets.NewString()
	for _, ip := range ips {
		ipSet.Insert(ip.String())
	}

	uniqueIPs := []net.IP{}
	for _, str := range ipSet.List() {
		ip := net.ParseIP(str)
		if ip != nil {
			uniqueIPs = append(uniqueIPs, ip)
		}
	}

	return uniqueIPs
}
<commit_msg>Increased DNS request timeout from 2 secs to 5 secs<commit_after>package common

import (
	"fmt"
	"net"
	"sync"
	"time"

	"github.com/miekg/dns"

	kerrors "k8s.io/apimachinery/pkg/util/errors"
	utilruntime "k8s.io/apimachinery/pkg/util/runtime"
	"k8s.io/apimachinery/pkg/util/sets"
)

const (
	defaultTTL = 30 * time.Minute
)

type dnsValue struct {
	// All IPv4 addresses for a given domain name
	ips []net.IP
	// Time-to-live value from non-authoritative/cached name server for the domain
	ttl time.Duration
	// Holds (last dns lookup time + ttl), tells when to refresh IPs next time
	nextQueryTime time.Time
}

type DNS struct {
	// Protects dnsMap operations
	lock sync.Mutex
	// Holds dns name and its corresponding information
	dnsMap map[string]dnsValue

	// DNS resolvers
	nameservers []string
	// DNS port
	port string
}

func NewDNS(resolverConfigFile string) (*DNS, error) {
	config, err := dns.ClientConfigFromFile(resolverConfigFile)
	if err != nil || config == nil {
		return nil, fmt.Errorf("cannot initialize the resolver: %v", err)
	}

	return &DNS{
		dnsMap:      map[string]dnsValue{},
		nameservers: filterIPv4Servers(config.Servers),
		port:        config.Port,
	}, nil
}

func (d *DNS) Size() int {
	d.lock.Lock()
	defer d.lock.Unlock()

	return len(d.dnsMap)
}

func (d *DNS) Get(dns string) dnsValue {
	d.lock.Lock()
	defer d.lock.Unlock()

	data := dnsValue{}
	if res, ok := d.dnsMap[dns]; ok {
		data.ips = make([]net.IP, len(res.ips))
		copy(data.ips, res.ips)
		data.ttl = res.ttl
		data.nextQueryTime = res.nextQueryTime
	}
	return data
}

func (d *DNS) Add(dns string) error {
	d.lock.Lock()
	defer d.lock.Unlock()

	d.dnsMap[dns] = dnsValue{}
	err, _ := d.updateOne(dns)
	if err != nil {
		delete(d.dnsMap, dns)
	}
	return err
}

func (d *DNS) Update() (error, bool) {
	d.lock.Lock()
	defer d.lock.Unlock()

	errList := []error{}
	changed := false
	for dns := range d.dnsMap {
		err, updated := d.updateOne(dns)
		if err != nil {
			errList = append(errList, err)
			continue
		}
		if updated {
			changed = true
		}
	}
	return kerrors.NewAggregate(errList), changed
}

func (d *DNS) updateOne(dns string) (error, bool) {
	res, ok := d.dnsMap[dns]
	if !ok {
		// Should not happen, all operations on dnsMap are synchronized by d.lock
		return fmt.Errorf("DNS value not found in dnsMap for domain: %q", dns), false
	}

	ips, minTTL, err := d.getIPsAndMinTTL(dns)
	if err != nil {
		res.nextQueryTime = time.Now().Add(defaultTTL)
		d.dnsMap[dns] = res
		return err, false
	}

	changed := false
	if !ipsEqual(res.ips, ips) {
		changed = true
	}
	res.ips = ips
	res.ttl = minTTL
	res.nextQueryTime = time.Now().Add(res.ttl)
	d.dnsMap[dns] = res
	return nil, changed
}

func (d *DNS) getIPsAndMinTTL(domain string) ([]net.IP, time.Duration, error) {
	ips := []net.IP{}
	ttlSet := false
	var minTTL uint32

	for _, server := range d.nameservers {
		msg := new(dns.Msg)
		msg.SetQuestion(dns.Fqdn(domain), dns.TypeA)

		dialServer := server
		if _, _, err := net.SplitHostPort(server); err != nil {
			dialServer = net.JoinHostPort(server, d.port)
		}
		c := new(dns.Client)
		c.Timeout = 5 * time.Second
		in, _, err := c.Exchange(msg, dialServer)
		if err != nil {
			return nil, defaultTTL, err
		}
		if in != nil && in.Rcode != dns.RcodeSuccess {
			return nil, defaultTTL, fmt.Errorf("failed to get a valid answer: %v", in)
		}

		if in != nil && len(in.Answer) > 0 {
			for _, a := range in.Answer {
				if !ttlSet || a.Header().Ttl < minTTL {
					minTTL = a.Header().Ttl
					ttlSet = true
				}

				switch t := a.(type) {
				case *dns.A:
					ips = append(ips, t.A)
				}
			}
		}
	}

	if !ttlSet || (len(ips) == 0) {
		return nil, defaultTTL, fmt.Errorf("IPv4 addr not found for domain: %q, nameservers: %v", domain, d.nameservers)
	}

	ttl, err := time.ParseDuration(fmt.Sprintf("%ds", minTTL))
	if err != nil {
		utilruntime.HandleError(fmt.Errorf("Invalid TTL value for domain: %q, err: %v, defaulting ttl=%s", domain, err, defaultTTL.String()))
		ttl = defaultTTL
	}

	return removeDuplicateIPs(ips), ttl, nil
}

func (d *DNS) GetMinQueryTime() (time.Time, bool) {
	d.lock.Lock()
	defer d.lock.Unlock()

	timeSet := false
	var minTime time.Time
	for _, res := range d.dnsMap {
		if (timeSet == false) || res.nextQueryTime.Before(minTime) {
			timeSet = true
			minTime = res.nextQueryTime
		}
	}

	return minTime, timeSet
}

func ipsEqual(oldips, newips []net.IP) bool {
	if len(oldips) != len(newips) {
		return false
	}

	for _, oldip := range oldips {
		found := false
		for _, newip := range newips {
			if oldip.Equal(newip) {
				found = true
				break
			}
		}
		if !found {
			return false
		}
	}
	return true
}

func filterIPv4Servers(servers []string) []string {
	ipv4Servers := []string{}
	for _, server := range servers {
		ipString := server
		if host, _, err := net.SplitHostPort(server); err == nil {
			ipString = host
		}

		if ip := net.ParseIP(ipString); ip != nil {
			if ip.To4() != nil {
				ipv4Servers = append(ipv4Servers, server)
			}
		}
	}

	return ipv4Servers
}

func removeDuplicateIPs(ips []net.IP) []net.IP {
	ipSet := sets.NewString()
	for _, ip := range ips {
		ipSet.Insert(ip.String())
	}

	uniqueIPs := []net.IP{}
	for _, str := range ipSet.List() {
		ip := net.ParseIP(str)
		if ip != nil {
			uniqueIPs = append(uniqueIPs, ip)
		}
	}

	return uniqueIPs
}
<|endoftext|>


<commit_before>// +build linux

package node

import (
	"fmt"
	"sync"
	"time"

	"github.com/golang/glog"

	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/util/sets"
	utilwait "k8s.io/apimachinery/pkg/util/wait"
	"k8s.io/client-go/tools/cache"

	networkapi "github.com/openshift/origin/pkg/network/apis/network"
	"github.com/openshift/origin/pkg/network/common"
	networkclient "github.com/openshift/origin/pkg/network/generated/internalclientset"
)

type nodeVNIDMap struct {
	policy        osdnPolicy
	networkClient networkclient.Interface

	// Synchronizes add or remove ids/namespaces
	lock       sync.Mutex
	ids        map[string]uint32
	mcEnabled  map[string]bool
	namespaces map[uint32]sets.String
}

func newNodeVNIDMap(policy osdnPolicy, networkClient networkclient.Interface) *nodeVNIDMap {
	return &nodeVNIDMap{
		policy:        policy,
		networkClient: networkClient,
		ids:           make(map[string]uint32),
		mcEnabled:     make(map[string]bool),
		namespaces:    make(map[uint32]sets.String),
	}
}

func (vmap *nodeVNIDMap) addNamespaceToSet(name string, vnid uint32) {
	set, found := vmap.namespaces[vnid]
	if !found {
		set = sets.NewString()
		vmap.namespaces[vnid] = set
	}
	set.Insert(name)
}

func (vmap *nodeVNIDMap) removeNamespaceFromSet(name string, vnid uint32) {
	if set, found := vmap.namespaces[vnid]; found {
		set.Delete(name)
		if set.Len() == 0 {
			delete(vmap.namespaces, vnid)
		}
	}
}

func (vmap *nodeVNIDMap) GetNamespaces(id uint32) []string {
	vmap.lock.Lock()
	defer vmap.lock.Unlock()

	if set, ok := vmap.namespaces[id]; ok {
		return set.List()
	} else {
		return nil
	}
}

func (vmap *nodeVNIDMap) GetMulticastEnabled(id uint32) bool {
	vmap.lock.Lock()
	defer vmap.lock.Unlock()

	set, exists := vmap.namespaces[id]
	if !exists || set.Len() == 0 {
		return false
	}
	for _, ns := range set.List() {
		if !vmap.mcEnabled[ns] {
			return false
		}
	}
	return true
}

// Nodes asynchronously watch for both NetNamespaces and services
// NetNamespaces populates vnid map and services/pod-setup depend on vnid map
// If for some reason, vnid map propagation from master to node is slow
// and if service/pod-setup tries to lookup vnid map then it may fail.
// So, use this method to alleviate this problem. This method will
// retry vnid lookup before giving up.
func (vmap *nodeVNIDMap) WaitAndGetVNID(name string) (uint32, error) {
	var id uint32
	backoff := utilwait.Backoff{
		Duration: 100 * time.Millisecond,
		Factor:   1.5,
		Steps:    5,
	}
	err := utilwait.ExponentialBackoff(backoff, func() (bool, error) {
		var err error
		id, err = vmap.getVNID(name)
		return err == nil, nil
	})
	if err == nil {
		return id, nil
	} else {
		VnidNotFoundErrors.Inc()
		return 0, fmt.Errorf("failed to find netid for namespace: %s in vnid map", name)
	}
}

func (vmap *nodeVNIDMap) getVNID(name string) (uint32, error) {
	vmap.lock.Lock()
	defer vmap.lock.Unlock()

	if id, ok := vmap.ids[name]; ok {
		return id, nil
	}
	return 0, fmt.Errorf("failed to find netid for namespace: %s in vnid map", name)
}

func (vmap *nodeVNIDMap) setVNID(name string, id uint32, mcEnabled bool) {
	vmap.lock.Lock()
	defer vmap.lock.Unlock()

	if oldId, found := vmap.ids[name]; found {
		vmap.removeNamespaceFromSet(name, oldId)
	}
	vmap.ids[name] = id
	vmap.mcEnabled[name] = mcEnabled
	vmap.addNamespaceToSet(name, id)

	glog.Infof("Associate netid %d to namespace %q with mcEnabled %v", id, name, mcEnabled)
}

func (vmap *nodeVNIDMap) unsetVNID(name string) (id uint32, err error) {
	vmap.lock.Lock()
	defer vmap.lock.Unlock()

	id, found := vmap.ids[name]
	if !found {
		return 0, fmt.Errorf("failed to find netid for namespace: %s in vnid map", name)
	}
	vmap.removeNamespaceFromSet(name, id)
	delete(vmap.ids, name)
	delete(vmap.mcEnabled, name)
	glog.Infof("Dissociate netid %d from namespace %q", id, name)
	return id, nil
}

func netnsIsMulticastEnabled(netns *networkapi.NetNamespace) bool {
	enabled, ok := netns.Annotations[networkapi.MulticastEnabledAnnotation]
	return enabled == "true" && ok
}

func (vmap *nodeVNIDMap) populateVNIDs() error {
	nets, err := vmap.networkClient.Network().NetNamespaces().List(metav1.ListOptions{})
	if err != nil {
		return err
	}

	for _, net := range nets.Items {
		vmap.setVNID(net.Name, net.NetID, netnsIsMulticastEnabled(&net))
	}
	return nil
}

func (vmap *nodeVNIDMap) Start() error {
	// Populate vnid map synchronously so that existing services can fetch vnid
	err := vmap.populateVNIDs()
	if err != nil {
		return err
	}

	go utilwait.Forever(vmap.watchNetNamespaces, 0)
	return nil
}

func (vmap *nodeVNIDMap) watchNetNamespaces() {
	common.RunEventQueue(vmap.networkClient.Network().RESTClient(), common.NetNamespaces, func(delta cache.Delta) error {
		netns := delta.Object.(*networkapi.NetNamespace)

		glog.V(5).Infof("Watch %s event for NetNamespace %q", delta.Type, netns.ObjectMeta.Name)
		switch delta.Type {
		case cache.Sync, cache.Added, cache.Updated:
			// Skip this event if nothing has changed
			oldNetID, err := vmap.getVNID(netns.NetName)
			oldMCEnabled := vmap.mcEnabled[netns.NetName]
			mcEnabled := netnsIsMulticastEnabled(netns)
			if err == nil && oldNetID == netns.NetID && oldMCEnabled == mcEnabled {
				break
			}
			vmap.setVNID(netns.NetName, netns.NetID, mcEnabled)

			if delta.Type == cache.Added {
				vmap.policy.AddNetNamespace(netns)
			} else {
				vmap.policy.UpdateNetNamespace(netns, oldNetID)
			}
		case cache.Deleted:
			// Unset VNID first so further operations don't see the deleted VNID
			vmap.unsetVNID(netns.NetName)
			vmap.policy.DeleteNetNamespace(netns)
		}
		return nil
	})
}
<commit_msg>Bug 1509799 - Fix WaitAndGetVNID() in sdn node<commit_after>// +build linux

package node

import (
	"fmt"
	"sync"
	"time"

	"github.com/golang/glog"

	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/util/sets"
	utilwait "k8s.io/apimachinery/pkg/util/wait"
	"k8s.io/client-go/tools/cache"

	networkapi "github.com/openshift/origin/pkg/network/apis/network"
	"github.com/openshift/origin/pkg/network/common"
	networkclient "github.com/openshift/origin/pkg/network/generated/internalclientset"
)

type nodeVNIDMap struct {
	policy        osdnPolicy
	networkClient networkclient.Interface

	// Synchronizes add or remove ids/namespaces
	lock       sync.Mutex
	ids        map[string]uint32
	mcEnabled  map[string]bool
	namespaces map[uint32]sets.String
}

func newNodeVNIDMap(policy osdnPolicy, networkClient networkclient.Interface) *nodeVNIDMap {
	return &nodeVNIDMap{
		policy:        policy,
		networkClient: networkClient,
		ids:           make(map[string]uint32),
		mcEnabled:     make(map[string]bool),
		namespaces:    make(map[uint32]sets.String),
	}
}

func (vmap *nodeVNIDMap) addNamespaceToSet(name string, vnid uint32) {
	set, found := vmap.namespaces[vnid]
	if !found {
		set = sets.NewString()
		vmap.namespaces[vnid] = set
	}
	set.Insert(name)
}

func (vmap *nodeVNIDMap) removeNamespaceFromSet(name string, vnid uint32) {
	if set, found := vmap.namespaces[vnid]; found {
		set.Delete(name)
		if set.Len() == 0 {
			delete(vmap.namespaces, vnid)
		}
	}
}

func (vmap *nodeVNIDMap) GetNamespaces(id uint32) []string {
	vmap.lock.Lock()
	defer vmap.lock.Unlock()

	if set, ok := vmap.namespaces[id]; ok {
		return set.List()
	} else {
		return nil
	}
}

func (vmap *nodeVNIDMap) GetMulticastEnabled(id uint32) bool {
	vmap.lock.Lock()
	defer vmap.lock.Unlock()

	set, exists := vmap.namespaces[id]
	if !exists || set.Len() == 0 {
		return false
	}
	for _, ns := range set.List() {
		if !vmap.mcEnabled[ns] {
			return false
		}
	}
	return true
}

// Nodes asynchronously watch for both NetNamespaces and services
// NetNamespaces populates vnid map and services/pod-setup depend on vnid map
// If for some reason, vnid map propagation from master to node is slow
// and if service/pod-setup tries to lookup vnid map then it may fail.
// So, use this method to alleviate this problem. This method will
// retry vnid lookup before giving up.
func (vmap *nodeVNIDMap) WaitAndGetVNID(name string) (uint32, error) {
	var id uint32
	// ~5 sec timeout
	backoff := utilwait.Backoff{
		Duration: 400 * time.Millisecond,
		Factor:   1.5,
		Steps:    6,
	}
	err := utilwait.ExponentialBackoff(backoff, func() (bool, error) {
		var err error
		id, err = vmap.getVNID(name)
		return err == nil, nil
	})
	if err == nil {
		return id, nil
	} else {
		// We may find netid when we check with api server but we will
		// still treat this as an error if we don't find it in vnid map.
		// So that we can imply insufficient timeout if we see many VnidNotFoundErrors.
		VnidNotFoundErrors.Inc()

		netns, err := vmap.networkClient.Network().NetNamespaces().Get(name, metav1.GetOptions{})
		if err != nil {
			return 0, fmt.Errorf("failed to find netid for namespace: %s, %v", name, err)
		}
		glog.Warningf("Netid for namespace: %s exists but not found in vnid map", name)
		vmap.setVNID(netns.Name, netns.NetID, netnsIsMulticastEnabled(netns))
		return netns.NetID, nil
	}
}

func (vmap *nodeVNIDMap) getVNID(name string) (uint32, error) {
	vmap.lock.Lock()
	defer vmap.lock.Unlock()

	if id, ok := vmap.ids[name]; ok {
		return id, nil
	}
	return 0, fmt.Errorf("failed to find netid for namespace: %s in vnid map", name)
}

func (vmap *nodeVNIDMap) setVNID(name string, id uint32, mcEnabled bool) {
	vmap.lock.Lock()
	defer vmap.lock.Unlock()

	if oldId, found := vmap.ids[name]; found {
		vmap.removeNamespaceFromSet(name, oldId)
	}
	vmap.ids[name] = id
	vmap.mcEnabled[name] = mcEnabled
	vmap.addNamespaceToSet(name, id)

	glog.Infof("Associate netid %d to namespace %q with mcEnabled %v", id, name, mcEnabled)
}

func (vmap *nodeVNIDMap) unsetVNID(name string) (id uint32, err error) {
	vmap.lock.Lock()
	defer vmap.lock.Unlock()

	id, found := vmap.ids[name]
	if !found {
		return 0, fmt.Errorf("failed to find netid for namespace: %s in vnid map", name)
	}
	vmap.removeNamespaceFromSet(name, id)
	delete(vmap.ids, name)
	delete(vmap.mcEnabled, name)
	glog.Infof("Dissociate netid %d from namespace %q", id, name)
	return id, nil
}

func netnsIsMulticastEnabled(netns *networkapi.NetNamespace) bool {
	enabled, ok := netns.Annotations[networkapi.MulticastEnabledAnnotation]
	return enabled == "true" && ok
}

func (vmap *nodeVNIDMap) populateVNIDs() error {
	nets, err := vmap.networkClient.Network().NetNamespaces().List(metav1.ListOptions{})
	if err != nil {
		return err
	}

	for _, net := range nets.Items {
		vmap.setVNID(net.Name, net.NetID, netnsIsMulticastEnabled(&net))
	}
	return nil
}

func (vmap *nodeVNIDMap) Start() error {
	// Populate vnid map synchronously so that existing services can fetch vnid
	err := vmap.populateVNIDs()
	if err != nil {
		return err
	}

	go utilwait.Forever(vmap.watchNetNamespaces, 0)
	return nil
}

func (vmap *nodeVNIDMap) watchNetNamespaces() {
	common.RunEventQueue(vmap.networkClient.Network().RESTClient(), common.NetNamespaces, func(delta cache.Delta) error {
		netns := delta.Object.(*networkapi.NetNamespace)

		glog.V(5).Infof("Watch %s event for NetNamespace %q", delta.Type, netns.ObjectMeta.Name)
		switch delta.Type {
		case cache.Sync, cache.Added, cache.Updated:
			// Skip this event if nothing has changed
			oldNetID, err := vmap.getVNID(netns.NetName)
			oldMCEnabled := vmap.mcEnabled[netns.NetName]
			mcEnabled := netnsIsMulticastEnabled(netns)
			if err == nil && oldNetID == netns.NetID && oldMCEnabled == mcEnabled {
				break
			}
			vmap.setVNID(netns.NetName, netns.NetID, mcEnabled)

			if delta.Type == cache.Added {
				vmap.policy.AddNetNamespace(netns)
			} else {
				vmap.policy.UpdateNetNamespace(netns, oldNetID)
			}
		case cache.Deleted:
			// Unset VNID first so further operations don't see the deleted VNID
			vmap.unsetVNID(netns.NetName)
			vmap.policy.DeleteNetNamespace(netns)
		}
		return nil
	})
}
<|endoftext|>


<commit_before>package paxos

import (
    "borg/assert"
    "testing"
)

// TODO: test wire format
// "x"            // too few separators
// "x:x"          // too few separators
// "x:x:x"        // too few separators
// "x:x:x:x:x"    // too many separators
// "1:x:INVITE:1" // invalid to address
// "X:*:INVITE:1" // invalid from address

//func TestIgnoresMalformedMessages(t *testing.T) {
//	unknownCommand := newInviteFrom(1, 1)
//	unknownCommand.(*Msg).cmd = "x"
//	invalidBody := newNominateFrom(1, 1, "foo")
//	invalidBody.(*Msg).body = "x"
//
//	totest := []Message{
//		newInviteFrom(1, 0), // invalid round number
//		unknownCommand,      // unknown command
//
//		invalidBody,                  // too few separators in nominate body
//		newNominateFrom(1, 0, "foo"), // invalid round number
//	}
//
//	for _, test := range totest {
//		ins := make(chan Message)
//		outs := SyncPutter(make(chan Message))
//
//		go acceptor(ins, PutWrapper{1, 2, outs})
//		ins <- test
//
//		// We want to check that it didn't try to send a response.
//		// If it didn't, it will continue to read the next input message and
//		// this will work fine. If it did, this will deadlock.
//		ins <- test
//
//		close(ins)
//	}
//}

//func TestIgnoresMalformedMessageBadCommand(t *testing.T) {
//    msgs := make(chan Message)
//    taught := make(chan string)
//
//    go func() {
//        taught <- learner(1, msgs)
//    }()
//
//    m := newVoteFrom(1, 1, "foo")
//    m.(*Msg).cmd = "foo"
//    msgs <- m
//    msgs <- newVoteFrom(1, 1, "foo")
//
//    assert.Equal(t, "foo", <-taught, "")
//}
//
//func TestIgnoresMessageWithIncorrectArityInBody(t *testing.T) {
//    msgs := make(chan Message)
//    taught := make(chan string)
//
//    go func() {
//        taught <- learner(1, msgs)
//    }()
//
//    m := newVoteFrom(1, 1, "foo")
//    m.(*Msg).body = ""
//    msgs <- m
//    msgs <- newVoteFrom(1, 1, "foo")
//
//    assert.Equal(t, "foo", <-taught, "")
//}

// For testing convenience
func newVoteFrom(from byte, i uint64, vval string) Message {
    m := NewVote(i, vval)
    m.SetSeqn(1)
    m.SetFrom(from)
    return m
}

// For testing convenience
func newNominateFrom(from byte, crnd uint64, v string) Message {
    m := NewNominate(crnd, v)
    m.SetSeqn(1)
    m.SetFrom(from)
    return m
}

// For testing convenience
func newRsvpFrom(from byte, i, vrnd uint64, vval string) Message {
    m := NewRsvp(i, vrnd, vval)
    m.SetSeqn(1)
    m.SetFrom(from)
    return m
}

// For testing convenience
func newInviteFrom(from byte, rnd uint64) Message {
    m := NewInvite(rnd)
    m.SetSeqn(1)
    m.SetFrom(from)
    return m
}

func TestMessageNewInvite(t *testing.T) {
    m := NewInvite(1)
    assert.Equal(t, Invite, m.Cmd(), "")
    assert.Equal(t, "1", m.Body(), "")
}

func TestMessageNewInviteAlt(t *testing.T) {
    m := NewInvite(2)
    assert.Equal(t, Invite, m.Cmd(), "")
    assert.Equal(t, "2", m.Body(), "")
}

func TestMessageNewNominate(t *testing.T) {
    m := NewNominate(1, "foo")
    assert.Equal(t, Nominate, m.Cmd(), "")
    assert.Equal(t, "1:foo", m.Body(), "")
}

func TestMessageNewNominateAlt(t *testing.T) {
    m := NewNominate(2, "bar")
    assert.Equal(t, Nominate, m.Cmd(), "")
    assert.Equal(t, "2:bar", m.Body(), "")
}

func TestMessageNewRsvp(t *testing.T) {
    m := NewRsvp(1, 0, "")
    assert.Equal(t, Rsvp, m.Cmd(), "")
    assert.Equal(t, "1:0:", m.Body(), "")
}

func TestMessageNewRsvpAlt(t *testing.T) {
    m := NewRsvp(2, 1, "foo")
    assert.Equal(t, Rsvp, m.Cmd(), "")
    assert.Equal(t, "2:1:foo", m.Body(), "")
}

func TestMessageNewVote(t *testing.T) {
    m := NewVote(1, "foo")
    assert.Equal(t, Vote, m.Cmd(), "")
    assert.Equal(t, "1:foo", m.Body(), "")
}

func TestMessageNewVoteAlt(t *testing.T) {
    m := NewVote(2, "bar")
    assert.Equal(t, Vote, m.Cmd(), "")
    assert.Equal(t, "2:bar", m.Body(), "")
}

func TestMessageSetFrom(t *testing.T) {
    m := NewInvite(1)
    m.SetFrom(1)
    assert.Equal(t, 1, m.From(), "")
    m.SetFrom(2)
    assert.Equal(t, 2, m.From(), "")
}

func TestMessageSetSeqn(t *testing.T) {
    m := NewInvite(1)
    m.SetSeqn(1)
    assert.Equal(t, uint64(1), m.Seqn(), "")
    m.SetSeqn(2)
    assert.Equal(t, uint64(2), m.Seqn(), "")
}

func TestMessageInviteParts(t *testing.T) {
    m := NewInvite(1)
    crnd := InviteParts(m)
    assert.Equal(t, uint64(1), crnd, "")
}

func TestMessageNominateParts(t *testing.T) {
    m := NewNominate(1, "foo")
    crnd, v := NominateParts(m)
    assert.Equal(t, uint64(1), crnd, "")
    assert.Equal(t, "foo", v, "")
}

func TestMessageRsvpParts(t *testing.T) {
    m := NewRsvp(1, 0, "")
    i, vrnd, vval := RsvpParts(m)
    assert.Equal(t, uint64(1), i, "")
    assert.Equal(t, uint64(0), vrnd, "")
    assert.Equal(t, "", vval, "")
}

func TestMessageVoteParts(t *testing.T) {
    m := NewVote(1, "foo")
    i, vval := VoteParts(m)
    assert.Equal(t, uint64(1), i, "")
    assert.Equal(t, "foo", vval, "")
}

func TestMessageInvitePartsAlt(t *testing.T) {
    m := NewInvite(2)
    crnd := InviteParts(m)
    assert.Equal(t, uint64(2), crnd, "")
}

func TestMessageNominatePartsAlt(t *testing.T) {
    m := NewNominate(2, "bar")
    crnd, v := NominateParts(m)
    assert.Equal(t, uint64(2), crnd, "")
    assert.Equal(t, "bar", v, "")
}

func TestMessageRsvpPartsAlt(t *testing.T) {
    m := NewRsvp(2, 1, "foo")
    i, vrnd, vval := RsvpParts(m)
    assert.Equal(t, uint64(2), i, "")
    assert.Equal(t, uint64(1), vrnd, "")
    assert.Equal(t, "foo", vval, "")
}

func TestMessageVotePartsAlt(t *testing.T) {
    m := NewVote(2, "bar")
    i, vval := VoteParts(m)
    assert.Equal(t, uint64(2), i, "")
    assert.Equal(t, "bar", vval, "")
}
<commit_msg>consolidate tests<commit_after>package paxos

import (
    "borg/assert"
    "testing"
)

// TODO: test wire format
// "x"            // too few separators
// "x:x"          // too few separators
// "x:x:x"        // too few separators
// "x:x:x:x:x"    // too many separators
// "1:x:INVITE:1" // invalid to address
// "X:*:INVITE:1" // invalid from address

//func TestIgnoresMalformedMessages(t *testing.T) {
//	unknownCommand := newInviteFrom(1, 1)
//	unknownCommand.(*Msg).cmd = "x"
//	invalidBody := newNominateFrom(1, 1, "foo")
//	invalidBody.(*Msg).body = "x"
//
//	totest := []Message{
//		newInviteFrom(1, 0), // invalid round number
//		unknownCommand,      // unknown command
//
//		invalidBody,                  // too few separators in nominate body
//		newNominateFrom(1, 0, "foo"), // invalid round number
//	}
//
//	for _, test := range totest {
//		ins := make(chan Message)
//		outs := SyncPutter(make(chan Message))
//
//		go acceptor(ins, PutWrapper{1, 2, outs})
//		ins <- test
//
//		// We want to check that it didn't try to send a response.
//		// If it didn't, it will continue to read the next input message and
//		// this will work fine. If it did, this will deadlock.
//		ins <- test
//
//		close(ins)
//	}
//}

//func TestIgnoresMalformedMessageBadCommand(t *testing.T) {
//    msgs := make(chan Message)
//    taught := make(chan string)
//
//    go func() {
//        taught <- learner(1, msgs)
//    }()
//
//    m := newVoteFrom(1, 1, "foo")
//    m.(*Msg).cmd = "foo"
//    msgs <- m
//    msgs <- newVoteFrom(1, 1, "foo")
//
//    assert.Equal(t, "foo", <-taught, "")
//}
//
//func TestIgnoresMessageWithIncorrectArityInBody(t *testing.T) {
//    msgs := make(chan Message)
//    taught := make(chan string)
//
//    go func() {
//        taught <- learner(1, msgs)
//    }()
//
//    m := newVoteFrom(1, 1, "foo")
//    m.(*Msg).body = ""
//    msgs <- m
//    msgs <- newVoteFrom(1, 1, "foo")
//
//    assert.Equal(t, "foo", <-taught, "")
//}

// For testing convenience
func newVoteFrom(from byte, i uint64, vval string) Message {
    m := NewVote(i, vval)
    m.SetSeqn(1)
    m.SetFrom(from)
    return m
}

// For testing convenience
func newNominateFrom(from byte, crnd uint64, v string) Message {
    m := NewNominate(crnd, v)
    m.SetSeqn(1)
    m.SetFrom(from)
    return m
}

// For testing convenience
func newRsvpFrom(from byte, i, vrnd uint64, vval string) Message {
    m := NewRsvp(i, vrnd, vval)
    m.SetSeqn(1)
    m.SetFrom(from)
    return m
}

// For testing convenience
func newInviteFrom(from byte, rnd uint64) Message {
    m := NewInvite(rnd)
    m.SetSeqn(1)
    m.SetFrom(from)
    return m
}

func TestMessageNewInvite(t *testing.T) {
    m := NewInvite(1)
    assert.Equal(t, Invite, m.Cmd(), "")
    crnd := InviteParts(m)
    assert.Equal(t, uint64(1), crnd, "")
}

func TestMessageNewInviteAlt(t *testing.T) {
    m := NewInvite(2)
    assert.Equal(t, Invite, m.Cmd(), "")
    crnd := InviteParts(m)
    assert.Equal(t, uint64(2), crnd, "")
}

func TestMessageNewNominate(t *testing.T) {
    m := NewNominate(1, "foo")
    assert.Equal(t, Nominate, m.Cmd(), "")
    crnd, v := NominateParts(m)
    assert.Equal(t, uint64(1), crnd, "")
    assert.Equal(t, "foo", v, "")
}

func TestMessageNewNominateAlt(t *testing.T) {
    m := NewNominate(2, "bar")
    assert.Equal(t, Nominate, m.Cmd(), "")
    crnd, v := NominateParts(m)
    assert.Equal(t, uint64(2), crnd, "")
    assert.Equal(t, "bar", v, "")
}

func TestMessageNewRsvp(t *testing.T) {
    m := NewRsvp(1, 0, "")
    assert.Equal(t, Rsvp, m.Cmd(), "")
    i, vrnd, vval := RsvpParts(m)
    assert.Equal(t, uint64(1), i, "")
    assert.Equal(t, uint64(0), vrnd, "")
    assert.Equal(t, "", vval, "")
}

func TestMessageNewRsvpAlt(t *testing.T) {
    m := NewRsvp(2, 1, "foo")
    assert.Equal(t, Rsvp, m.Cmd(), "")
    i, vrnd, vval := RsvpParts(m)
    assert.Equal(t, uint64(2), i, "")
    assert.Equal(t, uint64(1), vrnd, "")
    assert.Equal(t, "foo", vval, "")
}

func TestMessageNewVote(t *testing.T) {
    m := NewVote(1, "foo")
    assert.Equal(t, Vote, m.Cmd(), "")
    i, vval := VoteParts(m)
    assert.Equal(t, uint64(1), i, "")
    assert.Equal(t, "foo", vval, "")
}

func TestMessageNewVoteAlt(t *testing.T) {
    m := NewVote(2, "bar")
    assert.Equal(t, Vote, m.Cmd(), "")
    i, vval := VoteParts(m)
    assert.Equal(t, uint64(2), i, "")
    assert.Equal(t, "bar", vval, "")
}

func TestMessageSetFrom(t *testing.T) {
    m := NewInvite(1)
    m.SetFrom(1)
    assert.Equal(t, 1, m.From(), "")
    m.SetFrom(2)
    assert.Equal(t, 2, m.From(), "")
}

func TestMessageSetSeqn(t *testing.T) {
    m := NewInvite(1)
    m.SetSeqn(1)
    assert.Equal(t, uint64(1), m.Seqn(), "")
    m.SetSeqn(2)
    assert.Equal(t, uint64(2), m.Seqn(), "")
}
<|endoftext|>


<commit_before>// Copyright 2021 Google 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 runner

import (
	"fmt"
	"io/ioutil"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
	"testing"
)

// Runner runs an e2e test
type Runner struct {
	pkgName       string
	testCase      TestCase
	cmd           string
	t             *testing.T
	initialCommit string
	kptBin        string
}

func getKptBin() (string, error) {
	if p := os.Getenv(kptBinEnv); p != "" {
		return p, nil
	}
	return "", fmt.Errorf("must specify env '%s' for kpt binary path", kptBinEnv)
}

const (
	// If this env is set to "true", this e2e test framework will update the
	// expected diff and results if they already exist. If will not change
	// config.yaml.
	updateExpectedEnv string = "KPT_E2E_UPDATE_EXPECTED"
	kptBinEnv         string = "KPT_E2E_BIN"

	expectedDir         string = ".expected"
	expectedResultsFile string = "results.yaml"
	expectedDiffFile    string = "diff.patch"
	expectedConfigFile  string = "config.yaml"
	CommandFnEval       string = "eval"
	CommandFnRender     string = "render"
)

// NewRunner returns a new runner for pkg
func NewRunner(t *testing.T, testCase TestCase, c string) (*Runner, error) {
	info, err := os.Stat(testCase.Path)
	if err != nil {
		return nil, fmt.Errorf("cannot open path %s: %w", testCase.Path, err)
	}
	if !info.IsDir() {
		return nil, fmt.Errorf("path %s is not a directory", testCase.Path)
	}
	kptBin, err := getKptBin()
	if err != nil {
		return nil, fmt.Errorf("failed to find kpt binary: %w", err)
	}
	t.Logf("Using kpt binary: %s", kptBin)
	return &Runner{
		pkgName:  filepath.Base(testCase.Path),
		testCase: testCase,
		cmd:      c,
		t:        t,
		kptBin:   kptBin,
	}, nil
}

// Run runs the test.
func (r *Runner) Run() error {
	switch r.cmd {
	case CommandFnEval:
		return r.runFnEval()
	case CommandFnRender:
		return r.runFnRender()
	default:
		return fmt.Errorf("invalid command %s", r.cmd)
	}
}

func (r *Runner) runFnEval() error {
	r.t.Logf("Running test against package %s\n", r.pkgName)
	tmpDir, err := ioutil.TempDir("", "kpt-fn-e2e-*")
	if err != nil {
		return fmt.Errorf("failed to create temporary dir: %w", err)
	}
	defer os.RemoveAll(tmpDir)
	pkgPath := filepath.Join(tmpDir, r.pkgName)
	// create result dir
	resultsPath := filepath.Join(tmpDir, "results")
	err = os.Mkdir(resultsPath, 0755)
	if err != nil {
		return fmt.Errorf("failed to create results dir %s: %w", resultsPath, err)
	}

	// copy package to temp directory
	err = copyDir(r.testCase.Path, pkgPath)
	if err != nil {
		return fmt.Errorf("failed to copy package: %w", err)
	}

	// init and commit package files
	err = r.preparePackage(pkgPath)
	if err != nil {
		return fmt.Errorf("failed to prepare package: %w", err)
	}

	// run function
	kptArgs := []string{"fn", "eval", pkgPath, "--results-dir", resultsPath}
	if r.testCase.Config.EvalConfig.Network {
		kptArgs = append(kptArgs, "--network")
	}
	if r.testCase.Config.EvalConfig.Image != "" {
		kptArgs = append(kptArgs, "--image", r.testCase.Config.EvalConfig.Image)
	} else if !r.testCase.Config.EvalConfig.execUniquePath.Empty() {
		kptArgs = append(kptArgs, "--exec-path", string(r.testCase.Config.EvalConfig.execUniquePath))
	}
	if !r.testCase.Config.EvalConfig.fnConfigUniquePath.Empty() {
		kptArgs = append(kptArgs, "--fn-config", string(r.testCase.Config.EvalConfig.fnConfigUniquePath))
	}
	if r.testCase.Config.EvalConfig.IncludeMetaResources {
		kptArgs = append(kptArgs, "--include-meta-resources")
	}
	// args must be appended last
	if len(r.testCase.Config.EvalConfig.Args) > 0 {
		kptArgs = append(kptArgs, "--")
		for k, v := range r.testCase.Config.EvalConfig.Args {
			kptArgs = append(kptArgs, fmt.Sprintf("%s=%s", k, v))
		}
	}
	for i := 0; i < r.testCase.Config.RunCount(); i++ {
		output, fnErr := runCommand("", r.kptBin, kptArgs)
		if fnErr != nil {
			r.t.Logf("kpt error output: %s", output)
		}
		// Update the diff file or results file if updateExpectedEnv is set.
		if strings.ToLower(os.Getenv(updateExpectedEnv)) == "true" {
			return r.updateExpected(pkgPath, resultsPath, filepath.Join(r.testCase.Path, expectedDir))
		}

		// compare results
		err = r.compareResult(fnErr, pkgPath, resultsPath)
		if err != nil {
			return err
		}
		// we passed result check, now we should break if the command error
		// is expected
		if fnErr != nil {
			break
		}
	}

	return nil
}

func (r *Runner) runFnRender() error {
	r.t.Logf("Running test against package %s\n", r.pkgName)
	tmpDir, err := ioutil.TempDir("", "kpt-pipeline-e2e-*")
	if err != nil {
		return fmt.Errorf("failed to create temporary dir: %w", err)
	}
	if r.testCase.Config.Debug {
		fmt.Printf("Running test against package %s in dir %s \n", r.pkgName, tmpDir)
	}
	if !r.testCase.Config.Debug {
		// if debug is true, keep the test directory around for debugging
		defer os.RemoveAll(tmpDir)
	}
	pkgPath := filepath.Join(tmpDir, r.pkgName)
	// create dir to store untouched pkg to compare against
	origPkgPath := filepath.Join(tmpDir, "original")
	err = os.Mkdir(origPkgPath, 0755)
	if err != nil {
		return fmt.Errorf("failed to create original dir %s: %w", origPkgPath, err)
	}

	// copy package to temp directory
	err = copyDir(r.testCase.Path, pkgPath)
	if err != nil {
		return fmt.Errorf("failed to copy package: %w", err)
	}
	err = copyDir(r.testCase.Path, origPkgPath)
	if err != nil {
		return fmt.Errorf("failed to copy package: %w", err)
	}

	// init and commit package files
	err = r.preparePackage(pkgPath)
	if err != nil {
		return fmt.Errorf("failed to prepare package: %w", err)
	}

	// run function
	kptArgs := []string{"fn", "render", pkgPath}
	for i := 0; i < r.testCase.Config.RunCount(); i++ {
		output, fnErr := runCommand("", r.kptBin, kptArgs)
		// Update the diff file or results file if updateExpectedEnv is set.
		if strings.ToLower(os.Getenv(updateExpectedEnv)) == "true" {
			// TODO: `fn render` doesn't support result file now
			// use empty string to skip update results
			return r.updateExpected(pkgPath, "", filepath.Join(r.testCase.Path, expectedDir))
		}
		if fnErr != nil {
			r.t.Logf("kpt error output: %s", output)
		}
		// compare results
		// TODO: `fn render` doesn't support result file now
		// use empty string for results dir
		err = r.compareResult(fnErr, pkgPath, "")
		if err != nil {
			return err
		}
		// we passed result check, now we should break if the command error
		// is expected
		if fnErr != nil {
			break
		}
	}
	return nil
}

func (r *Runner) preparePackage(pkgPath string) error {
	err := gitInit(pkgPath)
	if err != nil {
		return err
	}

	err = gitAddAll(pkgPath)
	if err != nil {
		return err
	}

	err = gitCommit(pkgPath, "first")
	if err != nil {
		return err
	}

	r.initialCommit, err = getCommitHash(pkgPath)
	return err
}

func (r *Runner) compareResult(exitErr error, tmpPkgPath, resultsPath string) error {
	expected, err := newExpected(tmpPkgPath)
	if err != nil {
		return err
	}
	// get exit code
	exitCode := 0
	if e, ok := exitErr.(*exec.ExitError); ok {
		exitCode = e.ExitCode()
	} else if exitErr != nil {
		return fmt.Errorf("cannot get exit code, received error '%w'", exitErr)
	}

	if exitCode != r.testCase.Config.ExitCode {
		return fmt.Errorf("actual exit code %d doesn't match expected %d", exitCode, r.testCase.Config.ExitCode)
	}

	// compare results
	actual, err := readActualResults(resultsPath)
	if err != nil {
		return fmt.Errorf("failed to read actual results: %w", err)
	}
	diffOfResult, err := diffStrings(actual, expected.Results)
	if err != nil {
		return fmt.Errorf("error when run diff of results: %w: %s", err, diffOfResult)
	}
	if actual != expected.Results {
		return fmt.Errorf("actual results doesn't match expected\nActual\n===\n%s\nDiff of Results\n===\n%s",
			actual, diffOfResult)
	}

	// compare diff
	actual, err = readActualDiff(tmpPkgPath, r.initialCommit)
	if err != nil {
		return fmt.Errorf("failed to read actual diff: %w", err)
	}
	if actual != expected.Diff {
		diffOfDiff, err := diffStrings(actual, expected.Diff)
		if err != nil {
			return fmt.Errorf("error when run diff of diff: %w: %s", err, diffOfDiff)
		}
		return fmt.Errorf("actual diff doesn't match expected\nActual\n===\n%s\nDiff of Diff\n===\n%s",
			actual, diffOfDiff)
	}
	return nil
}

func (r *Runner) Skip() bool {
	return r.testCase.Config.Skip
}

func readActualResults(resultsPath string) (string, error) {
	// no results
	if resultsPath == "" {
		return "", nil
	}
	l, err := ioutil.ReadDir(resultsPath)
	if err != nil {
		return "", fmt.Errorf("failed to get files in results dir: %w", err)
	}
	if len(l) > 1 {
		return "", fmt.Errorf("unexpected results files number %d, should be 0 or 1", len(l))
	}
	if len(l) == 0 {
		// no result file
		return "", nil
	}
	resultsFile := l[0].Name()
	actualResults, err := ioutil.ReadFile(filepath.Join(resultsPath, resultsFile))
	if err != nil {
		return "", fmt.Errorf("failed to read actual results: %w", err)
	}
	return strings.TrimSpace(string(actualResults)), nil
}

func readActualDiff(path, origHash string) (string, error) {
	err := gitAddAll(path)
	if err != nil {
		return "", err
	}
	err = gitCommit(path, "second")
	if err != nil {
		return "", err
	}
	// diff with first commit
	actualDiff, err := gitDiff(path, origHash, "HEAD")
	if err != nil {
		return "", err
	}
	return strings.TrimSpace(actualDiff), nil
}

// expected contains the expected result for the function running
type expected struct {
	Results string
	Diff    string
}

func newExpected(path string) (expected, error) {
	e := expected{}
	// get expected results
	expectedResults, err := ioutil.ReadFile(filepath.Join(path, expectedDir, expectedResultsFile))
	switch {
	case os.IsNotExist(err):
		e.Results = ""
	case err != nil:
		return e, fmt.Errorf("failed to read expected results: %w", err)
	default:
		e.Results = strings.TrimSpace(string(expectedResults))
	}

	// get expected diff
	expectedDiff, err := ioutil.ReadFile(filepath.Join(path, expectedDir, expectedDiffFile))
	switch {
	case os.IsNotExist(err):
		e.Diff = ""
	case err != nil:
		return e, fmt.Errorf("failed to read expected diff: %w", err)
	default:
		e.Diff = strings.TrimSpace(string(expectedDiff))
	}

	return e, nil
}

func (r *Runner) updateExpected(tmpPkgPath, resultsPath, sourceOfTruthPath string) error {
	// We update results directory only when a result file already exists.
	l, err := ioutil.ReadDir(resultsPath)
	if err != nil {
		return err
	}
	if len(l) == 0 {
		actualDiff, err := readActualDiff(tmpPkgPath, r.initialCommit)
		if err != nil {
			return err
		}
		if actualDiff != "" {
			if err := ioutil.WriteFile(filepath.Join(sourceOfTruthPath, expectedDiffFile), []byte(actualDiff+"\n"), 0666); err != nil {
				return err
			}
		}
	} else {
		actualResults, err := readActualResults(resultsPath)
		if err != nil {
			return err
		}
		if actualResults != "" {
			if err := ioutil.WriteFile(filepath.Join(sourceOfTruthPath, expectedResultsFile), []byte(actualResults+"\n"), 0666); err != nil {
				return err
			}
		}
	}

	return nil
}
<commit_msg>fix updating e2e results (#1757)<commit_after>// Copyright 2021 Google 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 runner

import (
	"fmt"
	"io/ioutil"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
	"testing"
)

// Runner runs an e2e test
type Runner struct {
	pkgName       string
	testCase      TestCase
	cmd           string
	t             *testing.T
	initialCommit string
	kptBin        string
}

func getKptBin() (string, error) {
	if p := os.Getenv(kptBinEnv); p != "" {
		return p, nil
	}
	return "", fmt.Errorf("must specify env '%s' for kpt binary path", kptBinEnv)
}

const (
	// If this env is set to "true", this e2e test framework will update the
	// expected diff and results if they already exist. If will not change
	// config.yaml.
	updateExpectedEnv string = "KPT_E2E_UPDATE_EXPECTED"
	kptBinEnv         string = "KPT_E2E_BIN"

	expectedDir         string = ".expected"
	expectedResultsFile string = "results.yaml"
	expectedDiffFile    string = "diff.patch"
	expectedConfigFile  string = "config.yaml"
	CommandFnEval       string = "eval"
	CommandFnRender     string = "render"
)

// NewRunner returns a new runner for pkg
func NewRunner(t *testing.T, testCase TestCase, c string) (*Runner, error) {
	info, err := os.Stat(testCase.Path)
	if err != nil {
		return nil, fmt.Errorf("cannot open path %s: %w", testCase.Path, err)
	}
	if !info.IsDir() {
		return nil, fmt.Errorf("path %s is not a directory", testCase.Path)
	}
	kptBin, err := getKptBin()
	if err != nil {
		return nil, fmt.Errorf("failed to find kpt binary: %w", err)
	}
	t.Logf("Using kpt binary: %s", kptBin)
	return &Runner{
		pkgName:  filepath.Base(testCase.Path),
		testCase: testCase,
		cmd:      c,
		t:        t,
		kptBin:   kptBin,
	}, nil
}

// Run runs the test.
func (r *Runner) Run() error {
	switch r.cmd {
	case CommandFnEval:
		return r.runFnEval()
	case CommandFnRender:
		return r.runFnRender()
	default:
		return fmt.Errorf("invalid command %s", r.cmd)
	}
}

func (r *Runner) runFnEval() error {
	r.t.Logf("Running test against package %s\n", r.pkgName)
	tmpDir, err := ioutil.TempDir("", "kpt-fn-e2e-*")
	if err != nil {
		return fmt.Errorf("failed to create temporary dir: %w", err)
	}
	defer os.RemoveAll(tmpDir)
	pkgPath := filepath.Join(tmpDir, r.pkgName)
	// create result dir
	resultsPath := filepath.Join(tmpDir, "results")
	err = os.Mkdir(resultsPath, 0755)
	if err != nil {
		return fmt.Errorf("failed to create results dir %s: %w", resultsPath, err)
	}

	// copy package to temp directory
	err = copyDir(r.testCase.Path, pkgPath)
	if err != nil {
		return fmt.Errorf("failed to copy package: %w", err)
	}

	// init and commit package files
	err = r.preparePackage(pkgPath)
	if err != nil {
		return fmt.Errorf("failed to prepare package: %w", err)
	}

	// run function
	kptArgs := []string{"fn", "eval", pkgPath, "--results-dir", resultsPath}
	if r.testCase.Config.EvalConfig.Network {
		kptArgs = append(kptArgs, "--network")
	}
	if r.testCase.Config.EvalConfig.Image != "" {
		kptArgs = append(kptArgs, "--image", r.testCase.Config.EvalConfig.Image)
	} else if !r.testCase.Config.EvalConfig.execUniquePath.Empty() {
		kptArgs = append(kptArgs, "--exec-path", string(r.testCase.Config.EvalConfig.execUniquePath))
	}
	if !r.testCase.Config.EvalConfig.fnConfigUniquePath.Empty() {
		kptArgs = append(kptArgs, "--fn-config", string(r.testCase.Config.EvalConfig.fnConfigUniquePath))
	}
	if r.testCase.Config.EvalConfig.IncludeMetaResources {
		kptArgs = append(kptArgs, "--include-meta-resources")
	}
	// args must be appended last
	if len(r.testCase.Config.EvalConfig.Args) > 0 {
		kptArgs = append(kptArgs, "--")
		for k, v := range r.testCase.Config.EvalConfig.Args {
			kptArgs = append(kptArgs, fmt.Sprintf("%s=%s", k, v))
		}
	}
	for i := 0; i < r.testCase.Config.RunCount(); i++ {
		output, fnErr := runCommand("", r.kptBin, kptArgs)
		if fnErr != nil {
			r.t.Logf("kpt error output: %s", output)
		}
		// Update the diff file or results file if updateExpectedEnv is set.
		if strings.ToLower(os.Getenv(updateExpectedEnv)) == "true" {
			return r.updateExpected(pkgPath, resultsPath, filepath.Join(r.testCase.Path, expectedDir))
		}

		// compare results
		err = r.compareResult(fnErr, pkgPath, resultsPath)
		if err != nil {
			return err
		}
		// we passed result check, now we should break if the command error
		// is expected
		if fnErr != nil {
			break
		}
	}

	return nil
}

func (r *Runner) runFnRender() error {
	r.t.Logf("Running test against package %s\n", r.pkgName)
	tmpDir, err := ioutil.TempDir("", "kpt-pipeline-e2e-*")
	if err != nil {
		return fmt.Errorf("failed to create temporary dir: %w", err)
	}
	if r.testCase.Config.Debug {
		fmt.Printf("Running test against package %s in dir %s \n", r.pkgName, tmpDir)
	}
	if !r.testCase.Config.Debug {
		// if debug is true, keep the test directory around for debugging
		defer os.RemoveAll(tmpDir)
	}
	pkgPath := filepath.Join(tmpDir, r.pkgName)
	// create dir to store untouched pkg to compare against
	origPkgPath := filepath.Join(tmpDir, "original")
	err = os.Mkdir(origPkgPath, 0755)
	if err != nil {
		return fmt.Errorf("failed to create original dir %s: %w", origPkgPath, err)
	}

	// copy package to temp directory
	err = copyDir(r.testCase.Path, pkgPath)
	if err != nil {
		return fmt.Errorf("failed to copy package: %w", err)
	}
	err = copyDir(r.testCase.Path, origPkgPath)
	if err != nil {
		return fmt.Errorf("failed to copy package: %w", err)
	}

	// init and commit package files
	err = r.preparePackage(pkgPath)
	if err != nil {
		return fmt.Errorf("failed to prepare package: %w", err)
	}

	// run function
	kptArgs := []string{"fn", "render", pkgPath}
	for i := 0; i < r.testCase.Config.RunCount(); i++ {
		output, fnErr := runCommand("", r.kptBin, kptArgs)
		// Update the diff file or results file if updateExpectedEnv is set.
		if strings.ToLower(os.Getenv(updateExpectedEnv)) == "true" {
			// TODO: `fn render` doesn't support result file now
			// use empty string to skip update results
			return r.updateExpected(pkgPath, "", filepath.Join(r.testCase.Path, expectedDir))
		}
		if fnErr != nil {
			r.t.Logf("kpt error output: %s", output)
		}
		// compare results
		// TODO: `fn render` doesn't support result file now
		// use empty string for results dir
		err = r.compareResult(fnErr, pkgPath, "")
		if err != nil {
			return err
		}
		// we passed result check, now we should break if the command error
		// is expected
		if fnErr != nil {
			break
		}
	}
	return nil
}

func (r *Runner) preparePackage(pkgPath string) error {
	err := gitInit(pkgPath)
	if err != nil {
		return err
	}

	err = gitAddAll(pkgPath)
	if err != nil {
		return err
	}

	err = gitCommit(pkgPath, "first")
	if err != nil {
		return err
	}

	r.initialCommit, err = getCommitHash(pkgPath)
	return err
}

func (r *Runner) compareResult(exitErr error, tmpPkgPath, resultsPath string) error {
	expected, err := newExpected(tmpPkgPath)
	if err != nil {
		return err
	}
	// get exit code
	exitCode := 0
	if e, ok := exitErr.(*exec.ExitError); ok {
		exitCode = e.ExitCode()
	} else if exitErr != nil {
		return fmt.Errorf("cannot get exit code, received error '%w'", exitErr)
	}

	if exitCode != r.testCase.Config.ExitCode {
		return fmt.Errorf("actual exit code %d doesn't match expected %d", exitCode, r.testCase.Config.ExitCode)
	}

	// compare results
	actual, err := readActualResults(resultsPath)
	if err != nil {
		return fmt.Errorf("failed to read actual results: %w", err)
	}
	diffOfResult, err := diffStrings(actual, expected.Results)
	if err != nil {
		return fmt.Errorf("error when run diff of results: %w: %s", err, diffOfResult)
	}
	if actual != expected.Results {
		return fmt.Errorf("actual results doesn't match expected\nActual\n===\n%s\nDiff of Results\n===\n%s",
			actual, diffOfResult)
	}

	// compare diff
	actual, err = readActualDiff(tmpPkgPath, r.initialCommit)
	if err != nil {
		return fmt.Errorf("failed to read actual diff: %w", err)
	}
	if actual != expected.Diff {
		diffOfDiff, err := diffStrings(actual, expected.Diff)
		if err != nil {
			return fmt.Errorf("error when run diff of diff: %w: %s", err, diffOfDiff)
		}
		return fmt.Errorf("actual diff doesn't match expected\nActual\n===\n%s\nDiff of Diff\n===\n%s",
			actual, diffOfDiff)
	}
	return nil
}

func (r *Runner) Skip() bool {
	return r.testCase.Config.Skip
}

func readActualResults(resultsPath string) (string, error) {
	// no results
	if resultsPath == "" {
		return "", nil
	}
	l, err := ioutil.ReadDir(resultsPath)
	if err != nil {
		return "", fmt.Errorf("failed to get files in results dir: %w", err)
	}
	if len(l) > 1 {
		return "", fmt.Errorf("unexpected results files number %d, should be 0 or 1", len(l))
	}
	if len(l) == 0 {
		// no result file
		return "", nil
	}
	resultsFile := l[0].Name()
	actualResults, err := ioutil.ReadFile(filepath.Join(resultsPath, resultsFile))
	if err != nil {
		return "", fmt.Errorf("failed to read actual results: %w", err)
	}
	return strings.TrimSpace(string(actualResults)), nil
}

func readActualDiff(path, origHash string) (string, error) {
	err := gitAddAll(path)
	if err != nil {
		return "", err
	}
	err = gitCommit(path, "second")
	if err != nil {
		return "", err
	}
	// diff with first commit
	actualDiff, err := gitDiff(path, origHash, "HEAD")
	if err != nil {
		return "", err
	}
	return strings.TrimSpace(actualDiff), nil
}

// expected contains the expected result for the function running
type expected struct {
	Results string
	Diff    string
}

func newExpected(path string) (expected, error) {
	e := expected{}
	// get expected results
	expectedResults, err := ioutil.ReadFile(filepath.Join(path, expectedDir, expectedResultsFile))
	switch {
	case os.IsNotExist(err):
		e.Results = ""
	case err != nil:
		return e, fmt.Errorf("failed to read expected results: %w", err)
	default:
		e.Results = strings.TrimSpace(string(expectedResults))
	}

	// get expected diff
	expectedDiff, err := ioutil.ReadFile(filepath.Join(path, expectedDir, expectedDiffFile))
	switch {
	case os.IsNotExist(err):
		e.Diff = ""
	case err != nil:
		return e, fmt.Errorf("failed to read expected diff: %w", err)
	default:
		e.Diff = strings.TrimSpace(string(expectedDiff))
	}

	return e, nil
}

func (r *Runner) updateExpected(tmpPkgPath, resultsPath, sourceOfTruthPath string) error {
	if resultsPath != "" {
		// We update results directory only when a result file already exists.
		l, err := ioutil.ReadDir(resultsPath)
		if err != nil {
			return err
		}
		if len(l) > 0 {
			actualResults, err := readActualResults(resultsPath)
			if err != nil {
				return err
			}
			if actualResults != "" {
				if err := ioutil.WriteFile(filepath.Join(sourceOfTruthPath, expectedResultsFile), []byte(actualResults+"\n"), 0666); err != nil {
					return err
				}
			}
		}
	}
	actualDiff, err := readActualDiff(tmpPkgPath, r.initialCommit)
	if err != nil {
		return err
	}
	if actualDiff != "" {
		if err := ioutil.WriteFile(filepath.Join(sourceOfTruthPath, expectedDiffFile), []byte(actualDiff+"\n"), 0666); err != nil {
			return err
		}
	}

	return nil
}
<|endoftext|>


<commit_before>package main

import "testing"

func TestLookupValid(t *testing.T) {
	domain := Domain{"marshland.ovh", ClientConfig{}}
	proto, service := "tcp", "imaps"

	got_addr, got_port, err := domain.lookup(service, proto)
	want_addr, want_port := "hermes.marshland.ovh", uint16(993)

	if err != nil {
		t.Errorf("Service %q://%q returned error: %v", proto, service, err)
	}
	if got_addr != want_addr {
		t.Errorf("Service %q://%q returned address %q, expected %q", proto, service, got_addr, want_addr)
	}
	if got_port != want_port {
		t.Errorf("Service %q://%q returned port %d, expected %d", proto, service, got_port, want_port)
	}
}

func TestLookupEmpty(t *testing.T) {
	domain := Domain{"marshland.ovh", ClientConfig{}}
	proto, service := "udp", "imaps"

	_, _, err := domain.lookup(service, proto)

	if err == nil {
		t.Errorf("Service %q://%q should have errored", proto, service)
	}
}

func TestLookupError(t *testing.T) {
	domain := Domain{"marshland.ovh", ClientConfig{}}
	proto, service := "udp", "imaps"

	_, _, err := domain.lookup(service, proto)

	if err == nil {
		t.Errorf("Service %q://%q should have errored", proto, service)
	}
}
<commit_msg>Test config generation logic.<commit_after>package main

import "testing"

func TestLookupValid(t *testing.T) {
	domain := Domain{"marshland.ovh", ClientConfig{}}
	proto, service := "tcp", "imaps"

	got_addr, got_port, err := domain.lookup(service, proto)
	want_addr, want_port := "hermes.marshland.ovh", uint16(993)

	if err != nil {
		t.Errorf("Service %q://%q returned error: %v", proto, service, err)
	}
	if got_addr != want_addr {
		t.Errorf("Service %q://%q returned address %q, expected %q", proto, service, got_addr, want_addr)
	}
	if got_port != want_port {
		t.Errorf("Service %q://%q returned port %d, expected %d", proto, service, got_port, want_port)
	}
}

func TestLookupEmpty(t *testing.T) {
	domain := Domain{"marshland.ovh", ClientConfig{}}
	proto, service := "udp", "imaps"

	_, _, err := domain.lookup(service, proto)

	if err == nil {
		t.Errorf("Service %q://%q should have errored", proto, service)
	}
}

func TestLookupError(t *testing.T) {
	domain := Domain{"marshland.ovh", ClientConfig{}}
	proto, service := "udp", "imaps"

	_, _, err := domain.lookup(service, proto)

	if err == nil {
		t.Errorf("Service %q://%q should have errored", proto, service)
	}
}

func TestConfigIncoming(t *testing.T) {
	domain := Domain{"marshland.ovh", ClientConfig{}}
	domain.generate_xml()

	got := domain.config.Providers[0].IncomingServers[0]
	want := IncomingServer{}
	want.Type = "imap"
	want.Hostname = "hermes.marshland.ovh"
	want.Port = 993
	want.SocketType = "SSL"
	want.Authentication = "password-cleartext"
	want.Username = "%EMAILLOCALPART%"

	if got != want {
		t.Errorf("Incoming server doesn't match expected value")
	}
}

func TestConfigOutgoing(t *testing.T) {
	domain := Domain{"marshland.ovh", ClientConfig{}}
	domain.generate_xml()

	got := domain.config.Providers[0].OutgoingServers[0]
	want := OutgoingServer{}
	want.Type = "smtp"
	want.Hostname = "hermes.marshland.ovh"
	want.Port = 465
	want.SocketType = "SSL"
	want.Authentication = "password-cleartext"
	want.Username = "%EMAILLOCALPART%"

	if got != want {
		t.Errorf("Incoming server doesn't match expected value")
	}
}
<|endoftext|>