<commit_before>package progressui

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

	"github.com/containerd/console"
	"github.com/docker/go-units"
	"github.com/moby/buildkit/client"
	"github.com/morikuni/aec"
	digest "github.com/opencontainers/go-digest"
	"golang.org/x/time/rate"
)

func DisplaySolveStatus(ctx context.Context, ch chan *client.SolveStatus) error {
	c, err := console.ConsoleFromFile(os.Stdout)
	if err != nil {
		return err // TODO: switch to log mode
	}
	disp := &display{c: c}

	t := newTrace()
	ticker := time.NewTicker(100 * time.Millisecond)
	defer ticker.Stop()

	displayLimiter := rate.NewLimiter(rate.Every(70*time.Millisecond), 1)

	var done bool

	for {
		select {
		case <-ctx.Done():
			return ctx.Err()
		case <-ticker.C:
		case ss, ok := <-ch:
			if ok {
				t.update(ss)
			} else {
				done = true
			}
		}

		if done {
			disp.print(t.displayInfo(), true)
			t.printErrorLogs()
			return nil
		} else if displayLimiter.Allow() {
			disp.print(t.displayInfo(), false)
		}
	}
}

type displayInfo struct {
	startTime      time.Time
	jobs           []job
	countTotal     int
	countCompleted int
}

type job struct {
	startTime     *time.Time
	completedTime *time.Time
	name          string
	status        string
	hasError      bool
	isCanceled    bool
}

type trace struct {
	localTimeDiff time.Duration
	vertexes      []*vertex
	byDigest      map[digest.Digest]*vertex
}

type vertex struct {
	*client.Vertex
	statuses []*status
	byID     map[string]*status
	logs     []*client.VertexLog
}

type status struct {
	*client.VertexStatus
}

func newTrace() *trace {
	return &trace{
		byDigest: make(map[digest.Digest]*vertex),
	}
}

func (t *trace) update(s *client.SolveStatus) {
	for _, v := range s.Vertexes {
		prev, ok := t.byDigest[v.Digest]
		if !ok {
			t.byDigest[v.Digest] = &vertex{
				byID: make(map[string]*status),
			}
		}
		if v.Started != nil && (prev == nil || prev.Started == nil) {
			if t.localTimeDiff == 0 {
				t.localTimeDiff = time.Since(*v.Started)
			}
			t.vertexes = append(t.vertexes, t.byDigest[v.Digest])
		}
		t.byDigest[v.Digest].Vertex = v
	}
	for _, s := range s.Statuses {
		v, ok := t.byDigest[s.Vertex]
		if !ok {
			continue // shouldn't happen
		}
		prev, ok := v.byID[s.ID]
		if !ok {
			v.byID[s.ID] = &status{VertexStatus: s}
		}
		if s.Started != nil && (prev == nil || prev.Started == nil) {
			v.statuses = append(v.statuses, v.byID[s.ID])
		}
		v.byID[s.ID].VertexStatus = s
	}
	for _, l := range s.Logs {
		v, ok := t.byDigest[l.Vertex]
		if !ok {
			continue // shouldn't happen
		}
		v.logs = append(v.logs, l)
	}
}

func (t *trace) printErrorLogs() {
	for _, v := range t.vertexes {
		if v.Error != "" && !strings.HasSuffix(v.Error, context.Canceled.Error()) {
			fmt.Println("------")
			fmt.Printf(" > %s:\n", v.Name)
			for _, l := range v.logs {
				switch l.Stream {
				case 1:
					os.Stdout.Write(l.Data)
				case 2:
					os.Stderr.Write(l.Data)
				}
			}
			fmt.Println("------")
		}
	}
}

func (t *trace) displayInfo() (d displayInfo) {
	d.startTime = time.Now()
	if t.localTimeDiff != 0 {
		d.startTime = (*t.vertexes[0].Started).Add(t.localTimeDiff)
	}
	d.countTotal = len(t.byDigest)
	for _, v := range t.byDigest {
		if v.Completed != nil {
			d.countCompleted++
		}
	}

	for _, v := range t.vertexes {
		j := job{
			startTime:     addTime(v.Started, t.localTimeDiff),
			completedTime: addTime(v.Completed, t.localTimeDiff),
			name:          v.Name,
		}
		if v.Error != "" {
			if strings.HasSuffix(v.Error, context.Canceled.Error()) {
				j.isCanceled = true
				j.name = "CANCELED " + j.name
			} else {
				j.hasError = true
				j.name = "ERROR " + j.name
			}
		}
		d.jobs = append(d.jobs, j)
		for _, s := range v.statuses {
			j := job{
				startTime:     addTime(s.Started, t.localTimeDiff),
				completedTime: addTime(s.Completed, t.localTimeDiff),
				name:          "=> " + s.ID,
			}
			if s.Total != 0 {
				j.status = units.HumanSize(float64(s.Current)) + " / " + units.HumanSize(float64(s.Total))
			}
			d.jobs = append(d.jobs, j)
		}
	}

	return d
}

func addTime(tm *time.Time, d time.Duration) *time.Time {
	if tm == nil {
		return nil
	}
	t := (*tm).Add(d)
	return &t
}

type display struct {
	c         console.Console
	lineCount int
	repeated  bool
}

func (disp *display) print(d displayInfo, all bool) {
	// this output is inspired by Buck
	width := 80
	height := 10
	size, err := disp.c.Size()
	if err == nil {
		width = int(size.Width)
		height = int(size.Height)
	}

	if !all {
		d.jobs = wrapHeight(d.jobs, height-2)
	}

	b := aec.EmptyBuilder
	for i := 0; i <= disp.lineCount; i++ {
		b = b.EraseLine(aec.EraseModes.All).Up(1)
	}
	if !disp.repeated {
		b = b.Down(1)
	}
	b = b.EraseLine(aec.EraseModes.All)
	disp.repeated = true
	fmt.Print(b.Column(0).ANSI)

	statusStr := ""
	if d.countCompleted > 0 && d.countCompleted == d.countTotal {
		statusStr = "FINISHED"
	}

	fmt.Printf("[+] Building %.1fs (%d/%d) %s\n", time.Since(d.startTime).Seconds(), d.countCompleted, d.countTotal, statusStr)
	lineCount := 0
	for _, j := range d.jobs {
		endTime := time.Now()
		if j.completedTime != nil {
			endTime = *j.completedTime
		}
		if j.startTime == nil {
			continue
		}
		dt := endTime.Sub(*j.startTime).Seconds()
		if dt < 0.05 {
			dt = 0
		}
		pfx := " => "
		timer := fmt.Sprintf(" %.1fs\n", dt)
		status := j.status
		showStatus := false

		left := width - len(pfx) - len(timer) - 1
		if status != "" {
			if left+len(status) > 20 {
				showStatus = true
				left -= len(status) + 1
			}
		}
		if left < 12 { // too small screen to show progress
			continue
		}
		if len(j.name) > left {
			j.name = j.name[:left]
		}

		out := pfx + j.name
		if showStatus {
			out += " " + status
		}

		out = fmt.Sprintf("%-[2]*[1]s %[3]s", out, width-len(timer)-1, timer)
		if j.completedTime != nil {
			color := aec.BlueF
			if j.isCanceled {
				color = aec.YellowF
			} else if j.hasError {
				color = aec.RedF
			}
			out = aec.Apply(out, color)
		}
		fmt.Print(out)
		lineCount++
	}
	disp.lineCount = lineCount
}

func wrapHeight(j []job, limit int) []job {
	if len(j) > limit {
		j = j[len(j)-limit:]
	}
	return j
}
<commit_msg>progress: better protection against flickering<commit_after>package progressui

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

	"github.com/containerd/console"
	"github.com/docker/go-units"
	"github.com/moby/buildkit/client"
	"github.com/morikuni/aec"
	digest "github.com/opencontainers/go-digest"
	"golang.org/x/time/rate"
)

func DisplaySolveStatus(ctx context.Context, ch chan *client.SolveStatus) error {
	c, err := console.ConsoleFromFile(os.Stdout)
	if err != nil {
		return err // TODO: switch to log mode
	}
	disp := &display{c: c}

	t := newTrace()
	ticker := time.NewTicker(100 * time.Millisecond)
	defer ticker.Stop()

	displayLimiter := rate.NewLimiter(rate.Every(70*time.Millisecond), 1)

	var done bool

	for {
		select {
		case <-ctx.Done():
			return ctx.Err()
		case <-ticker.C:
		case ss, ok := <-ch:
			if ok {
				t.update(ss)
			} else {
				done = true
			}
		}

		if done {
			disp.print(t.displayInfo(), true)
			t.printErrorLogs()
			return nil
		} else if displayLimiter.Allow() {
			disp.print(t.displayInfo(), false)
		}
	}
}

type displayInfo struct {
	startTime      time.Time
	jobs           []job
	countTotal     int
	countCompleted int
}

type job struct {
	startTime     *time.Time
	completedTime *time.Time
	name          string
	status        string
	hasError      bool
	isCanceled    bool
}

type trace struct {
	localTimeDiff time.Duration
	vertexes      []*vertex
	byDigest      map[digest.Digest]*vertex
}

type vertex struct {
	*client.Vertex
	statuses []*status
	byID     map[string]*status
	logs     []*client.VertexLog
}

type status struct {
	*client.VertexStatus
}

func newTrace() *trace {
	return &trace{
		byDigest: make(map[digest.Digest]*vertex),
	}
}

func (t *trace) update(s *client.SolveStatus) {
	for _, v := range s.Vertexes {
		prev, ok := t.byDigest[v.Digest]
		if !ok {
			t.byDigest[v.Digest] = &vertex{
				byID: make(map[string]*status),
			}
		}
		if v.Started != nil && (prev == nil || prev.Started == nil) {
			if t.localTimeDiff == 0 {
				t.localTimeDiff = time.Since(*v.Started)
			}
			t.vertexes = append(t.vertexes, t.byDigest[v.Digest])
		}
		t.byDigest[v.Digest].Vertex = v
	}
	for _, s := range s.Statuses {
		v, ok := t.byDigest[s.Vertex]
		if !ok {
			continue // shouldn't happen
		}
		prev, ok := v.byID[s.ID]
		if !ok {
			v.byID[s.ID] = &status{VertexStatus: s}
		}
		if s.Started != nil && (prev == nil || prev.Started == nil) {
			v.statuses = append(v.statuses, v.byID[s.ID])
		}
		v.byID[s.ID].VertexStatus = s
	}
	for _, l := range s.Logs {
		v, ok := t.byDigest[l.Vertex]
		if !ok {
			continue // shouldn't happen
		}
		v.logs = append(v.logs, l)
	}
}

func (t *trace) printErrorLogs() {
	for _, v := range t.vertexes {
		if v.Error != "" && !strings.HasSuffix(v.Error, context.Canceled.Error()) {
			fmt.Println("------")
			fmt.Printf(" > %s:\n", v.Name)
			for _, l := range v.logs {
				switch l.Stream {
				case 1:
					os.Stdout.Write(l.Data)
				case 2:
					os.Stderr.Write(l.Data)
				}
			}
			fmt.Println("------")
		}
	}
}

func (t *trace) displayInfo() (d displayInfo) {
	d.startTime = time.Now()
	if t.localTimeDiff != 0 {
		d.startTime = (*t.vertexes[0].Started).Add(t.localTimeDiff)
	}
	d.countTotal = len(t.byDigest)
	for _, v := range t.byDigest {
		if v.Completed != nil {
			d.countCompleted++
		}
	}

	for _, v := range t.vertexes {
		j := job{
			startTime:     addTime(v.Started, t.localTimeDiff),
			completedTime: addTime(v.Completed, t.localTimeDiff),
			name:          v.Name,
		}
		if v.Error != "" {
			if strings.HasSuffix(v.Error, context.Canceled.Error()) {
				j.isCanceled = true
				j.name = "CANCELED " + j.name
			} else {
				j.hasError = true
				j.name = "ERROR " + j.name
			}
		}
		d.jobs = append(d.jobs, j)
		for _, s := range v.statuses {
			j := job{
				startTime:     addTime(s.Started, t.localTimeDiff),
				completedTime: addTime(s.Completed, t.localTimeDiff),
				name:          "=> " + s.ID,
			}
			if s.Total != 0 {
				j.status = units.HumanSize(float64(s.Current)) + " / " + units.HumanSize(float64(s.Total))
			}
			d.jobs = append(d.jobs, j)
		}
	}

	return d
}

func addTime(tm *time.Time, d time.Duration) *time.Time {
	if tm == nil {
		return nil
	}
	t := (*tm).Add(d)
	return &t
}

type display struct {
	c         console.Console
	lineCount int
	repeated  bool
}

func (disp *display) print(d displayInfo, all bool) {
	// this output is inspired by Buck
	width := 80
	height := 10
	size, err := disp.c.Size()
	if err == nil {
		width = int(size.Width)
		height = int(size.Height)
	}

	if !all {
		d.jobs = wrapHeight(d.jobs, height-2)
	}

	b := aec.EmptyBuilder
	for i := 0; i <= disp.lineCount; i++ {
		b = b.Up(1)
	}
	if !disp.repeated {
		b = b.Down(1)
	}
	disp.repeated = true
	fmt.Print(b.Column(0).ANSI)

	statusStr := ""
	if d.countCompleted > 0 && d.countCompleted == d.countTotal {
		statusStr = "FINISHED"
	}

	fmt.Print(aec.Hide)
	defer fmt.Print(aec.Show)

	out := fmt.Sprintf("[+] Building %.1fs (%d/%d) %s", time.Since(d.startTime).Seconds(), d.countCompleted, d.countTotal, statusStr)
	out = align(out, "", width)
	fmt.Println(out)
	lineCount := 0
	for _, j := range d.jobs {
		endTime := time.Now()
		if j.completedTime != nil {
			endTime = *j.completedTime
		}
		if j.startTime == nil {
			continue
		}
		dt := endTime.Sub(*j.startTime).Seconds()
		if dt < 0.05 {
			dt = 0
		}
		pfx := " => "
		timer := fmt.Sprintf(" %.1fs\n", dt)
		status := j.status
		showStatus := false

		left := width - len(pfx) - len(timer) - 1
		if status != "" {
			if left+len(status) > 20 {
				showStatus = true
				left -= len(status) + 1
			}
		}
		if left < 12 { // too small screen to show progress
			continue
		}
		if len(j.name) > left {
			j.name = j.name[:left]
		}

		out := pfx + j.name
		if showStatus {
			out += " " + status
		}

		out = align(out, timer, width)
		if j.completedTime != nil {
			color := aec.BlueF
			if j.isCanceled {
				color = aec.YellowF
			} else if j.hasError {
				color = aec.RedF
			}
			out = aec.Apply(out, color)
		}
		fmt.Print(out)
		lineCount++
	}
	disp.lineCount = lineCount
}

func align(l, r string, w int) string {
	return fmt.Sprintf("%-[2]*[1]s %[3]s", l, w-len(r)-1, r)
}

func wrapHeight(j []job, limit int) []job {
	if len(j) > limit {
		j = j[len(j)-limit:]
	}
	return j
}
<|endoftext|>


<commit_before>// +build fvtests

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

package fv

import (
	"context"
	"io/ioutil"
	"net"
	"os"
	"strconv"
	"time"

	"github.com/colabsaumoh/proto-udsuspver/nodeagentmgmt"
	"github.com/colabsaumoh/proto-udsuspver/protos/mgmtintf_v1"
	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
	log "github.com/sirupsen/logrus"
	"google.golang.org/grpc"

	"github.com/projectcalico/felix/proto"

	"github.com/projectcalico/felix/fv/containers"
	"github.com/projectcalico/felix/fv/utils"
	"github.com/projectcalico/felix/fv/workload"
	api "github.com/projectcalico/libcalico-go/lib/apis/v3"
	client "github.com/projectcalico/libcalico-go/lib/clientv3"
)

var _ = Context("policy sync API tests", func() {

	var (
		etcd               *containers.Container
		felix              *containers.Felix
		client             client.Interface
		w                  [3]*workload.Workload
		tempDir            string
		hostMgmtSocketPath string
	)

	BeforeEach(func() {
		// Create a temporary directory to map into the container as /var/run/calico, which
		// is where we tell Felix to put the policy sync socket.
		var err error
		tempDir, err = ioutil.TempDir("", "felixfv")
		Expect(err).NotTo(HaveOccurred())

		// Configure felix to enable the policy sync API.
		options := containers.DefaultTopologyOptions()
		options.ExtraEnvVars["FELIX_PolicySyncManagementSocketPath"] = "/var/run/calico/policy-mgmt.sock"
		options.ExtraEnvVars["FELIX_PolicySyncWorkloadSocketPathPrefix"] = "/var/run/calico"
		options.ExtraVolumes[tempDir] = "/var/run/calico"
		felix, etcd, client = containers.StartSingleNodeEtcdTopology(options)

		// Install a default profile that allows workloads with this profile to talk to each
		// other, in the absence of any Policy.
		defaultProfile := api.NewProfile()
		defaultProfile.Name = "default"
		defaultProfile.Spec.LabelsToApply = map[string]string{"default": ""}
		defaultProfile.Spec.Egress = []api.Rule{{Action: api.Allow}}
		defaultProfile.Spec.Ingress = []api.Rule{{
			Action: api.Allow,
			Source: api.EntityRule{Selector: "default == ''"},
		}}
		_, err = client.Profiles().Create(utils.Ctx, defaultProfile, utils.NoOptions)
		Expect(err).NotTo(HaveOccurred())

		// Create three workloads, using that profile.
		for ii := range w {
			iiStr := strconv.Itoa(ii)
			w[ii] = workload.Run(felix, "w"+iiStr, "cali1"+iiStr, "10.65.0.1"+iiStr, "8055", "tcp")
			w[ii].WorkloadEndpoint.Spec.Endpoint = "eth0"
			w[ii].WorkloadEndpoint.Spec.Orchestrator = "k8s"
			w[ii].WorkloadEndpoint.Spec.Pod = "fv-pod-" + iiStr
			w[ii].Configure(client)
		}

		hostMgmtSocketPath = tempDir + "/policy-mgmt.sock"
	})

	AfterEach(func() {
		for ii := range w {
			w[ii].Stop()
		}
		felix.Stop()

		if CurrentGinkgoTestDescription().Failed {
			etcd.Exec("etcdctl", "ls", "--recursive", "/")
		}
		etcd.Stop()
	})

	AfterEach(func() {
		if tempDir != "" {
			err := os.RemoveAll(tempDir)
			Expect(err).NotTo(HaveOccurred(), "Failed to clean up temp dir")
		}
	})

	Context("with an open management socket", func() {
		var (
			mgmtClient *nodeagentmgmt.Client
		)

		BeforeEach(func() {
			Eventually(hostMgmtSocketPath).Should(BeAnExistingFile())

			// Use the fact that anything we exec inside the Felix container runs as root to fix the
			// permissions on the socket so the test process can connect.
			felix.Exec("chmod", "a+rw", "/var/run/calico/policy-mgmt.sock")
			mgmtClient = nodeagentmgmt.ClientUds(hostMgmtSocketPath)
		})

		Context("after sending a workload creation", func() {
			var (
				hostWlSocketPath string
			)
			BeforeEach(func() {
				// Create the workload directory, this would normally be the responsibility of the
				// flex volume driver.
				hostWlDir := tempDir + "/wl0"
				os.MkdirAll(hostWlDir, 0777)
				Eventually(func() error {
					resp, err := mgmtClient.WorkloadAdded(&mgmtintf_v1.WorkloadInfo{
						Attrs: &mgmtintf_v1.WorkloadInfo_WorkloadAttributes{
							Uid:       "fv-pod-0",
							Namespace: "fv",
							Workload:  "fv-pod-0",
						},
						Workloadpath: "wl0",
					})
					log.WithField("response", resp).Info("WorkloadAdded response")
					return err
				}).ShouldNot(HaveOccurred())

				By("Creating the per-workload socket")
				hostWlSocketPath = hostWlDir + "/policysync.sock"
			})

			It("should create the workload socket", func() {
				Eventually(hostWlSocketPath).Should(BeAnExistingFile())
			})

			Context("with an open workload connection", func() {

				// Then connect to it.
				var (
					wlClient proto.PolicySyncClient
					err      error
					cancel   context.CancelFunc
					ctx      context.Context
				)

				BeforeEach(func() {
					// Use the fact that anything we exec inside the Felix container runs as root to fix the
					// permissions on the socket so the test process can connect.
					Eventually(hostWlSocketPath).Should(BeAnExistingFile())
					felix.Exec("chmod", "a+rw", "/var/run/calico/wl0/policysync.sock")
					var opts []grpc.DialOption
					opts = append(opts, grpc.WithInsecure())
					opts = append(opts, grpc.WithDialer(unixDialer))
					var conn *grpc.ClientConn
					conn, err = grpc.Dial(hostWlSocketPath, opts...)
					Expect(err).NotTo(HaveOccurred())

					wlClient = proto.NewPolicySyncClient(conn)
					ctx, cancel = context.WithTimeout(context.Background(), 10*time.Second)
				})

				AfterEach(func() {
					if cancel != nil {
						cancel()
					}
				})

				It("should receive something", func() {
					syncClient, err := wlClient.Sync(ctx, &proto.SyncRequest{})
					Expect(err).NotTo(HaveOccurred())
					msg, err := syncClient.Recv()
					Expect(err).NotTo(HaveOccurred())
					log.WithField("message", msg).Info("Received message")
				})
			})
		})
	})
})

func unixDialer(target string, timeout time.Duration) (net.Conn, error) {
	return net.DialTimeout("unix", target, timeout)
}
<commit_msg>Update policy sync FV to check the correct state is reached.<commit_after>// +build fvtests

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

package fv

import (
	"context"
	"io/ioutil"
	"net"
	"os"
	"reflect"
	"strconv"
	"time"

	"github.com/colabsaumoh/proto-udsuspver/nodeagentmgmt"
	"github.com/colabsaumoh/proto-udsuspver/protos/mgmtintf_v1"
	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
	log "github.com/sirupsen/logrus"
	"google.golang.org/grpc"

	"github.com/projectcalico/felix/dataplane/mock"
	"github.com/projectcalico/libcalico-go/lib/set"

	"github.com/projectcalico/felix/proto"

	"github.com/projectcalico/felix/fv/containers"
	"github.com/projectcalico/felix/fv/utils"
	"github.com/projectcalico/felix/fv/workload"
	api "github.com/projectcalico/libcalico-go/lib/apis/v3"
	client "github.com/projectcalico/libcalico-go/lib/clientv3"
)

var _ = Context("policy sync API tests", func() {

	var (
		etcd               *containers.Container
		felix              *containers.Felix
		client             client.Interface
		w                  [3]*workload.Workload
		tempDir            string
		hostMgmtSocketPath string
	)

	BeforeEach(func() {
		// Create a temporary directory to map into the container as /var/run/calico, which
		// is where we tell Felix to put the policy sync socket.
		var err error
		tempDir, err = ioutil.TempDir("", "felixfv")
		Expect(err).NotTo(HaveOccurred())

		// Configure felix to enable the policy sync API.
		options := containers.DefaultTopologyOptions()
		options.ExtraEnvVars["FELIX_PolicySyncManagementSocketPath"] = "/var/run/calico/policy-mgmt.sock"
		options.ExtraEnvVars["FELIX_PolicySyncWorkloadSocketPathPrefix"] = "/var/run/calico"
		options.ExtraVolumes[tempDir] = "/var/run/calico"
		felix, etcd, client = containers.StartSingleNodeEtcdTopology(options)

		// Install a default profile that allows workloads with this profile to talk to each
		// other, in the absence of any Policy.
		defaultProfile := api.NewProfile()
		defaultProfile.Name = "default"
		defaultProfile.Spec.LabelsToApply = map[string]string{"default": ""}
		defaultProfile.Spec.Egress = []api.Rule{{Action: api.Allow}}
		defaultProfile.Spec.Ingress = []api.Rule{{
			Action: api.Allow,
			Source: api.EntityRule{Selector: "default == ''"},
		}}
		_, err = client.Profiles().Create(utils.Ctx, defaultProfile, utils.NoOptions)
		Expect(err).NotTo(HaveOccurred())

		// Create three workloads, using that profile.
		for ii := range w {
			iiStr := strconv.Itoa(ii)
			w[ii] = workload.Run(felix, "w"+iiStr, "cali1"+iiStr, "10.65.0.1"+iiStr, "8055", "tcp")
			w[ii].WorkloadEndpoint.Spec.Endpoint = "eth0"
			w[ii].WorkloadEndpoint.Spec.Orchestrator = "k8s"
			w[ii].WorkloadEndpoint.Spec.Pod = "fv-pod-" + iiStr
			w[ii].Configure(client)
		}

		hostMgmtSocketPath = tempDir + "/policy-mgmt.sock"
	})

	AfterEach(func() {
		for ii := range w {
			w[ii].Stop()
		}
		felix.Stop()

		if CurrentGinkgoTestDescription().Failed {
			etcd.Exec("etcdctl", "ls", "--recursive", "/")
		}
		etcd.Stop()
	})

	AfterEach(func() {
		if tempDir != "" {
			err := os.RemoveAll(tempDir)
			Expect(err).NotTo(HaveOccurred(), "Failed to clean up temp dir")
		}
	})

	Context("with an open management socket", func() {
		var (
			mgmtClient *nodeagentmgmt.Client
		)

		BeforeEach(func() {
			Eventually(hostMgmtSocketPath).Should(BeAnExistingFile())

			// Use the fact that anything we exec inside the Felix container runs as root to fix the
			// permissions on the socket so the test process can connect.
			felix.Exec("chmod", "a+rw", "/var/run/calico/policy-mgmt.sock")
			mgmtClient = nodeagentmgmt.ClientUds(hostMgmtSocketPath)
		})

		Context("after sending a workload creation", func() {
			var (
				hostWlSocketPath string
			)
			BeforeEach(func() {
				// Create the workload directory, this would normally be the responsibility of the
				// flex volume driver.
				hostWlDir := tempDir + "/wl0"
				os.MkdirAll(hostWlDir, 0777)
				Eventually(func() error {
					resp, err := mgmtClient.WorkloadAdded(&mgmtintf_v1.WorkloadInfo{
						Attrs: &mgmtintf_v1.WorkloadInfo_WorkloadAttributes{
							Uid:       "fv-pod-0",
							Namespace: "fv",
							Workload:  "fv-pod-0",
						},
						Workloadpath: "wl0",
					})
					log.WithField("response", resp).Info("WorkloadAdded response")
					return err
				}).ShouldNot(HaveOccurred())

				By("Creating the per-workload socket")
				hostWlSocketPath = hostWlDir + "/policysync.sock"
			})

			It("should create the workload socket", func() {
				Eventually(hostWlSocketPath).Should(BeAnExistingFile())
			})

			Context("with an open workload connection", func() {

				// Then connect to it.
				var (
					wlClient proto.PolicySyncClient
					err      error
					cancel   context.CancelFunc
					ctx      context.Context
				)

				BeforeEach(func() {
					// Use the fact that anything we exec inside the Felix container runs as root to fix the
					// permissions on the socket so the test process can connect.
					Eventually(hostWlSocketPath).Should(BeAnExistingFile())
					felix.Exec("chmod", "a+rw", "/var/run/calico/wl0/policysync.sock")
					var opts []grpc.DialOption
					opts = append(opts, grpc.WithInsecure())
					opts = append(opts, grpc.WithDialer(unixDialer))
					var conn *grpc.ClientConn
					conn, err = grpc.Dial(hostWlSocketPath, opts...)
					Expect(err).NotTo(HaveOccurred())

					wlClient = proto.NewPolicySyncClient(conn)
					ctx, cancel = context.WithTimeout(context.Background(), 10*time.Second)
				})

				AfterEach(func() {
					if cancel != nil {
						cancel()
					}
				})

				It("should reach the expected state", func() {
					syncClient, err := wlClient.Sync(ctx, &proto.SyncRequest{})
					Expect(err).NotTo(HaveOccurred())

					mockDataplane := mock.NewMockDataplane()
					done := make(chan struct{})
					go func() {
						defer GinkgoRecover()
						defer close(done)

						for {
							msg, err := syncClient.Recv()
							if err != nil {
								log.WithError(err).Warn("Recv failed.")
								return
							}
							log.WithField("msg", msg).Info("Received workload message")
							mockDataplane.OnEvent(reflect.ValueOf(msg.Payload).Elem().Field(0).Interface())
						}
					}()

					Eventually(mockDataplane.ActiveProfiles).Should(Equal(set.From(proto.ProfileID{Name: "default"})))
					Eventually(mockDataplane.EndpointToPolicyOrder).Should(Equal(map[string][]mock.TierInfo{"k8s/fv/fv-pod-0/eth0": {}}))

					cancel()
					Eventually(done).Should(BeClosed())
				})
			})
		})
	})
})

func unixDialer(target string, timeout time.Duration) (net.Conn, error) {
	return net.DialTimeout("unix", target, timeout)
}
<|endoftext|>


<commit_before>package geodelta

import "math"

const DEG2RAD float64 = math.Pi / 180.0 // 度をラジアンに変換するための係数
const RAD2DEG float64 = 180.0 / math.Pi // ラジアンを度に変換するための係数

// 緯度をメルカトルY座標に変換する
//   -90.0 <= lat <= +90.0
//    -1.0 <= my  <=  +1.0
func LatToMy(lat float64) float64 {
	return math.Atanh(math.Sin(lat*DEG2RAD)) / math.Pi
}

// 経度をメルカトルX座標に変換する
//   -180.0 <= lng <= +180.0
//     -1.0 <= mx  <=   +1.0
func LngToMx(lng float64) float64 {
	return lng / 180.0
}

// メルカトルX座標/Y座標を正規化する
func NormalizeM(m float64) float64 {
	if m > +1.0 {
		return m - math.Ceil((m-1)/+2.0)*2.0
	} else if m < -1.0 {
		return m + math.Ceil((m+1)/-2.0)*2.0
	} else {
		return m
	}
}

// メルカトルY座標を緯度に変換する
//    -1.0 <= my  <=  +1.0
//   -90.0 <= lat <= +90.0
func MyToLat(my float64) float64 {
	return math.Asin(math.Tanh(my*math.Pi)) * RAD2DEG
}

// メルカトルX座標を経度に変換する
//     -1.0 <= mx  <=   +1.0
//   -180.0 <= lng <= +180.0
func MxToLng(mx float64) float64 {
	if mx < -1.0 {
		mx += 2.0
	}
	if mx > +1.0 {
		mx -= 2.0
	}
	return mx * 180.0
}

/* Ruby
module GeoDelta
  module Projector
    DELTA_HEIGHT = Math.sqrt(0.75)  # 一辺を1.0とする正三角形の高さ

    # メルカトルY座標から正規化Y座標に変換する
    #    -1.0 <= my <=  +1.0
    #   -12.0 <= ny <= +12.0
    def self.my_to_ny(my)
      return my / DELTA_HEIGHT * 12.0
    end

    # メルカトルX座標から正規化X座標に変換する
    #    -1.0 <= my <=  +1.0
    #   -12.0 <= ny <= +12.0
    def self.mx_to_nx(mx)
      return mx * 12.0
    end

    # 正規化Y座標からメルカトルY座標に変換する
    #   -12.0 <= ny <= +12.0
    #    -1.0 <= my <=  +1.0
    def self.ny_to_my(my)
      return my / 12.0 * DELTA_HEIGHT
    end

    # 正規化X座標からメルカトルX座標に変換する
    #   -12.0 <= ny <= +12.0
    #    -1.0 <= my <=  +1.0
    def self.nx_to_mx(ny)
      return ny / 12.0
    end

    def self.lat_to_ny(lat)
      return self.my_to_ny(self.lat_to_my(lat))
    end

    def self.lng_to_nx(lng)
      return self.mx_to_nx(self.lng_to_mx(lng))
    end

    def self.ny_to_lat(ny)
      return self.my_to_lat(self.ny_to_my(ny))
    end

    def self.nx_to_lng(nx)
      return self.mx_to_lng(self.nx_to_mx(nx))
    end

    def self.latlng_to_nxy(lat, lng)
      return [
        self.lng_to_nx(lng),
        self.lat_to_ny(lat),
      ]
    end

    def self.nxy_to_latlng(nx, ny)
      return [
        self.ny_to_lat(ny),
        self.nx_to_lng(nx),
      ]
    end
  end
end
*/
<commit_msg>リファクタリング<commit_after>package geodelta

import "math"

const DEG2RAD float64 = math.Pi / 180.0 // 度をラジアンに変換するための係数
const RAD2DEG float64 = 180.0 / math.Pi // ラジアンを度に変換するための係数

// 緯度をメルカトルY座標に変換する
//   -90.0 <= lat <= +90.0
//    -1.0 <= my  <=  +1.0
func LatToMy(lat float64) float64 {
	return math.Atanh(math.Sin(lat*DEG2RAD)) / math.Pi
}

// 経度をメルカトルX座標に変換する
//   -180.0 <= lng <= +180.0
//     -1.0 <= mx  <=   +1.0
func LngToMx(lng float64) float64 {
	return lng / 180.0
}

// メルカトルX座標/Y座標を正規化する
func NormalizeM(m float64) float64 {
	if m > +1.0 {
		return m - math.Ceil((m-1)/+2.0)*2.0
	} else if m < -1.0 {
		return m + math.Ceil((m+1)/-2.0)*2.0
	} else {
		return m
	}
}

// メルカトルY座標を緯度に変換する
//    -1.0 <= my  <=  +1.0
//   -90.0 <= lat <= +90.0
func MyToLat(my float64) float64 {
	return math.Asin(math.Tanh(NormalizeM(my)*math.Pi)) * RAD2DEG
}

// メルカトルX座標を経度に変換する
//     -1.0 <= mx  <=   +1.0
//   -180.0 <= lng <= +180.0
func MxToLng(mx float64) float64 {
	return NormalizeM(mx) * 180.0
}

/* Ruby
module GeoDelta
  module Projector
    DELTA_HEIGHT = Math.sqrt(0.75)  # 一辺を1.0とする正三角形の高さ

    # メルカトルY座標から正規化Y座標に変換する
    #    -1.0 <= my <=  +1.0
    #   -12.0 <= ny <= +12.0
    def self.my_to_ny(my)
      return my / DELTA_HEIGHT * 12.0
    end

    # メルカトルX座標から正規化X座標に変換する
    #    -1.0 <= my <=  +1.0
    #   -12.0 <= ny <= +12.0
    def self.mx_to_nx(mx)
      return mx * 12.0
    end

    # 正規化Y座標からメルカトルY座標に変換する
    #   -12.0 <= ny <= +12.0
    #    -1.0 <= my <=  +1.0
    def self.ny_to_my(my)
      return my / 12.0 * DELTA_HEIGHT
    end

    # 正規化X座標からメルカトルX座標に変換する
    #   -12.0 <= ny <= +12.0
    #    -1.0 <= my <=  +1.0
    def self.nx_to_mx(ny)
      return ny / 12.0
    end

    def self.lat_to_ny(lat)
      return self.my_to_ny(self.lat_to_my(lat))
    end

    def self.lng_to_nx(lng)
      return self.mx_to_nx(self.lng_to_mx(lng))
    end

    def self.ny_to_lat(ny)
      return self.my_to_lat(self.ny_to_my(ny))
    end

    def self.nx_to_lng(nx)
      return self.mx_to_lng(self.nx_to_mx(nx))
    end

    def self.latlng_to_nxy(lat, lng)
      return [
        self.lng_to_nx(lng),
        self.lat_to_ny(lat),
      ]
    end

    def self.nxy_to_latlng(nx, ny)
      return [
        self.ny_to_lat(ny),
        self.nx_to_lng(nx),
      ]
    end
  end
end
*/
<|endoftext|>


<commit_before>// +build !windows

package metrics

import (
	"regexp"
	"strings"

	"github.com/mackerelio/mackerel-agent/logging"
	"github.com/mackerelio/mackerel-agent/util"
)

// FilesystemGenerator is common filesystem metrics generator on unix os.
type FilesystemGenerator struct {
	IgnoreRegexp *regexp.Regexp
}

var logger = logging.GetLogger("metrics")

var dfColumnSpecs = []util.DfColumnSpec{
	util.DfColumnSpec{Name: "size", IsInt: true},
	util.DfColumnSpec{Name: "used", IsInt: true},
}

var (
	devDirReg    = regexp.MustCompile(`^/dev/(.*)$`)
	sanitizerReg = regexp.MustCompile(`[^A-Za-z0-9_-]`)
)

// Generate the metrics of filesystems
func (g *FilesystemGenerator) Generate() (Values, error) {
	filesystems, err := util.CollectDfValues(dfColumnSpecs)
	if err != nil {
		return nil, err
	}

	ret := make(map[string]float64)
	for name, values := range filesystems {
		// https://github.com/docker/docker/blob/v1.5.0/daemon/graphdriver/devmapper/deviceset.go#L981
		if strings.HasPrefix(name, "/dev/mapper/docker-") ||
			(g.IgnoreRegexp != nil && g.IgnoreRegexp.MatchString(name)) {
			continue
		}
		if matches := devdirReg.FindStringSubmatch(name); matches != nil {
			device := sanitizerReg.ReplaceAllString(matches[1], "_")
			for key, value := range values {
				intValue, valueTypeOk := value.(int64)
				if valueTypeOk {
					// kilo bytes -> bytes
					ret["filesystem."+device+"."+key] = float64(intValue) * 1024
				}
			}
		}
	}

	return Values(ret), nil
}
<commit_msg>use strings.TrimPrefix instead of regexp capture in metrics.filesystem<commit_after>// +build !windows

package metrics

import (
	"regexp"
	"strings"

	"github.com/mackerelio/mackerel-agent/logging"
	"github.com/mackerelio/mackerel-agent/util"
)

// FilesystemGenerator is common filesystem metrics generator on unix os.
type FilesystemGenerator struct {
	IgnoreRegexp *regexp.Regexp
}

var logger = logging.GetLogger("metrics")

var dfColumnSpecs = []util.DfColumnSpec{
	util.DfColumnSpec{Name: "size", IsInt: true},
	util.DfColumnSpec{Name: "used", IsInt: true},
}

var sanitizerReg = regexp.MustCompile(`[^A-Za-z0-9_-]`)

// Generate the metrics of filesystems
func (g *FilesystemGenerator) Generate() (Values, error) {
	filesystems, err := util.CollectDfValues(dfColumnSpecs)
	if err != nil {
		return nil, err
	}

	ret := make(map[string]float64)
	for name, values := range filesystems {
		// https://github.com/docker/docker/blob/v1.5.0/daemon/graphdriver/devmapper/deviceset.go#L981
		if strings.HasPrefix(name, "/dev/mapper/docker-") ||
			(g.IgnoreRegexp != nil && g.IgnoreRegexp.MatchString(name)) {
			continue
		}
		if device := strings.TrimPrefix(name, "/dev/"); name != device {
			device = sanitizerReg.ReplaceAllString(device, "_")
			for key, value := range values {
				intValue, valueTypeOk := value.(int64)
				if valueTypeOk {
					// kilo bytes -> bytes
					ret["filesystem."+device+"."+key] = float64(intValue) * 1024
				}
			}
		}
	}

	return Values(ret), nil
}
<|endoftext|>


<commit_before>// Copyright 2014 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

/*
Git-codereview manages the code review process for Git changes using a Gerrit
server.

The git-codereview tool manages ``change branches'' in the local git repository.
Each such branch tracks a single commit, or ``pending change,''
that is reviewed using a Gerrit server; the Gerrit remote must be
named ``origin'' in the local git repo.

Modifications to the pending change are applied by amending the commit.
This process implements the ``single-commit feature branch'' model.
Creating multiple-commit feature branches, for example to break a large
change into a reviewable sequence, is also supported; see the discussion below.

Once installed as git-codereview, the tool's commands are available through git
either by running

	git codereview <command>

or, if aliases are installed, as

	git <command>

The review tool's command names do not conflict with any extant git commands.
This document uses the first form for clarity but most users install these
aliases in their .gitconfig file:

	[alias]
		change = codereview change
		gofmt = codereview gofmt
		mail = codereview mail
		pending = codereview pending
		rebase-work = codereview rebase-work
		submit = codereview submit
		sync = codereview sync

Single-Commit Work Branches

For simple, unrelated changes, the typical usage of the git-codereview tool
is to place each pending change in its own Git branch.
In this workflow, the work branch contains
either no pending change beyond origin/master (when there's no local work)
or exactly one pending change beyond origin/master (the change being developed).

When there is no pending change on the work branch,
``git codereview change'' creates one by running ``git commit.''
Otherwise, when there is already a pending change,
``git codereview change'' revises it by running ``git commit --amend.''

The ``git codereview mail'' and ``git codereview submit'' commands
implicitly operate on the lone pending change.

Multiple-Commit Work Branches

Of course, it is not always feasible to put each pending change in a separate branch.
A sequence of changes that build on one another is more easily
managed as multiple commits on a single branch, and the git-codereview tool
supports this workflow as well.
To add a new pending change, invoke ``git commit'' directly,
instead of ``git codereview change''.
The git-codereview tool adjusts its behavior when there are
multiple pending changes.

The ``git codereview change'' command amends the top commit in the stack (HEAD).
To amend a commit further down the stack, use Git's rebase support,
for example by using ``git commit --fixup'' followed by ``git codereview rebase-work.''

The ``git codereview mail'' command requires an explicit revision argument,
but note that since ``git codereview mail'' is implemented as a ``git push,''
any commits earlier in the stack are necessarily also mailed.

The ``git codereview submit'' command also requires an explicit revision argument,
and while earlier commits are necessarily still uploaded and mailed,
only the named revision or revisions are submitted (merged into origin/master).
In a single-commit work branch, a successful ``git codereview submit''
effectively runs ``git codereview sync'' automatically.
In a multiple-commit work branch, it does not, because
the implied ``git rebase'' may conflict with the remaining pending commits.
Instead it is necessary to run ``git codereview sync'' explicitly
(when ready) after ``git codereview submit.''

Reusing Work Branches

Although one common practice is to create a new branch for each pending change,
running ``git codereview submit'' (and possibly ``git codereview sync'')
leaves the current branch ready for reuse with a future change.
Some developers find it helpful to create a single work branch
(``git change work'') and then do all work in that branch,
possibly in the multiple-commit mode, never changing between branches.

Command Details

All commands accept these global flags:

The -v flag prints all commands that make changes. Multiple occurrences
trigger more verbosity in some commands, including sync.

The -n flag prints all commands that would be run, but does not run them.

Descriptions of each command follow.

Branchpoint

	git codereview branchpoint

The branchpoint command prints the commit hash of the most recent commit
on the current branch that is shared with the Gerrit server. This is the point
where local work branched from the published tree. The command is intended
mainly for use in scripts. For example, ``git diff $(git codereview branchpoint)''
or ``git log $(git codereview branchpoint)..HEAD''.

Change

The change command creates and moves between Git branches and maintains the
pending changes on work branches.

	git codereview change [-a] [-q] [branchname]

Given a branch name as an argument, the change command switches to the named
branch, creating it if necessary. If the branch is created and there are staged
changes, it will commit the changes to the branch, creating a new pending
change.

With no argument, the change command creates a new pending change from the
staged changes in the current branch or, if there is already a pending change,
amends that change.

The -q option skips the editing of an extant pending change's commit message.

The -a option automatically adds any unstaged edits in tracked files during
commit; it is equivalent to the 'git commit' -a option.

Gofmt

The gofmt command applies the gofmt program to all files modified in the
current work branch, both in the staging area (index) and the working tree
(local directory).

	git codereview gofmt [-l]

The -l option causes the command to list the files that need reformatting but
not reformat them. Otherwise, the gofmt command reformats modified files in
place. That is, files in the staging area are reformatted in the staging area,
and files in the working tree are reformatted in the working tree.

Help

The help command displays basic usage instructions.

	git codereview help

Hooks

The hooks command installs the Git hooks to enforce code review conventions.

	git codereview hooks

The pre-commit hook checks that all Go code is formatted with gofmt and that
the commit is not being made directly to the master branch.

The commit-msg hook adds the Gerrit ``Change-Id'' line to the commit message if
not present. It also checks that the message uses the convention established by
the Go project that the first line has the form, pkg/path: summary.

The hooks command will not overwrite an existing hook.
If it is not installing hooks, use ``git codereview hooks -v'' for details.
This hook installation is also done at startup by all other git codereview
commands, except ``git codereview help''.

Hook-Invoke

The hook-invoke command is an internal command that invokes the named Git hook.

	git codereview hook-invoke <hook> [args]

It is run by the shell scripts installed by the ``git codereview hooks'' command.

Mail

The mail command starts the code review process for the pending change.

	git codereview mail [-f] [-r email] [-cc email] [-trybot] [revision]

It pushes the pending change commit in the current branch to the Gerrit code
review server and prints the URL for the change on the server.
If the change already exists on the server, the mail command updates that
change with a new changeset.

The -r and -cc flags identify the email addresses of people to do the code
review and to be CC'ed about the code review.
Multiple addresses are given as a comma-separated list.

An email address passed to -r or -cc can be shortened from name@domain to name.
The mail command resolves such shortenings by reading the list of past reviewers
from the git repository log to find email addresses of the form name@somedomain
and then, in case of ambiguity, using the reviewer who appears most often.

The -trybot flag runs the trybots on all new or updated changes. It is
equivalent to setting the Run-Trybot+1 label from Gerrit.

The mail command fails if there are staged edits that are not committed.
The -f flag overrides this behavior.

The mail command updates the tag <branchname>.mailed to refer to the
commit that was most recently mailed, so running ``git diff <branchname>.mailed''
shows diffs between what is on the Gerrit server and the current directory.

If there are multiple pending commits, the revision argument is mandatory.
If no revision is specified, the mail command prints a short summary of
the pending commits for use in deciding which to mail.

If any commit that would be pushed to the server contains the text
"DO NOT MAIL" (case insensitive) in its commit message, the mail command
will refuse to send the commit to the server.

Pending

The pending command prints to standard output the status of all pending changes
and staged, unstaged, and untracked files in the local repository.

	git codereview pending [-c] [-l] [-s]

The -c flag causes the command to show pending changes only on the current branch.

The -l flag causes the command to use only locally available information.
By default, it fetches recent commits and code review information from the
Gerrit server.

The -s flag causes the command to print abbreviated (short) output.

Common shorter aliases include ``git p'' for ``git pending''
and ``git pl'' for ``git pending -l'' (notably faster but without Gerrit information).

Rebase-work

The rebase-work command runs git rebase in interactive mode over pending changes.
It is shorthand for ``git rebase -i $(git codereview branchpoint)''.
It differs from plain ``git rebase -i'' in that the latter will try to incorporate
new commits from the origin branch during the rebase;
``git codereview rebase-work'' does not.

In multiple-commit workflows, rebase-work is used so often
that it can be helpful to alias it to ``git rw''.

Submit

The submit command pushes the pending change to the Gerrit server and tells
Gerrit to submit it to the master branch.

	git codereview submit [-i | revision...]

The command fails if there are modified files (staged or unstaged) that are not
part of the pending change.

The -i option causes the submit command to open a list of commits to submit
in the configured text editor, similar to ``git rebase -i''.

If multiple revisions are specified, the submit command submits each one in turn,
stopping at the first failure.

When run in a multiple-commit work branch,
either the -i option or the revision argument is mandatory.
If both are omitted, the submit command prints a short summary of
the pending commits for use in deciding which to submit.

After submitting the pending changes, the submit command tries to synchronize the
current branch to the submitted commit, if it can do so cleanly.
If not, it will prompt the user to run ``git codereview sync'' manually.

After a successful sync, the branch can be used to prepare a new change.

Sync

The sync command updates the local repository.

	git codereview sync

It fetches commits from the remote repository and merges them from the
upstream branch to the current branch, rebasing any pending changes.

Configuration

If a file named codereview.cfg is present in the repository root,
git-codereview will use it for configuration. It should contain lines
of this format:

	key: value

The ``gerrit'' key sets the Gerrit URL for this project. Git-codereview
automatically derives the Gerrit URL from repositories hosted in
*.googlesource.com. If not set or derived, the repository is assumed to
not have Gerrit, and certain features won't work.

The ``issuerepo'' key specifies the GitHub repository to use for issues, if
different from the source repository. If set to ``golang/go'', for example,
lines such as ``Fixes #123'' in a commit message will be rewritten to ``Fixes
golang/go#123''.

*/
package main
<commit_msg>git-codereview: pull punctuation marks out of quotation marks<commit_after>// Copyright 2014 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

/*
Git-codereview manages the code review process for Git changes using a Gerrit
server.

The git-codereview tool manages ``change branches'' in the local git repository.
Each such branch tracks a single commit, or ``pending change'',
that is reviewed using a Gerrit server; the Gerrit remote must be
named ``origin'' in the local git repo.

Modifications to the pending change are applied by amending the commit.
This process implements the ``single-commit feature branch'' model.
Creating multiple-commit feature branches, for example to break a large
change into a reviewable sequence, is also supported; see the discussion below.

Once installed as git-codereview, the tool's commands are available through git
either by running

	git codereview <command>

or, if aliases are installed, as

	git <command>

The review tool's command names do not conflict with any extant git commands.
This document uses the first form for clarity but most users install these
aliases in their .gitconfig file:

	[alias]
		change = codereview change
		gofmt = codereview gofmt
		mail = codereview mail
		pending = codereview pending
		rebase-work = codereview rebase-work
		submit = codereview submit
		sync = codereview sync

Single-Commit Work Branches

For simple, unrelated changes, the typical usage of the git-codereview tool
is to place each pending change in its own Git branch.
In this workflow, the work branch contains
either no pending change beyond origin/master (when there's no local work)
or exactly one pending change beyond origin/master (the change being developed).

When there is no pending change on the work branch,
``git codereview change'' creates one by running ``git commit''.
Otherwise, when there is already a pending change,
``git codereview change'' revises it by running ``git commit --amend''.

The ``git codereview mail'' and ``git codereview submit'' commands
implicitly operate on the lone pending change.

Multiple-Commit Work Branches

Of course, it is not always feasible to put each pending change in a separate branch.
A sequence of changes that build on one another is more easily
managed as multiple commits on a single branch, and the git-codereview tool
supports this workflow as well.
To add a new pending change, invoke ``git commit'' directly,
instead of ``git codereview change''.
The git-codereview tool adjusts its behavior when there are
multiple pending changes.

The ``git codereview change'' command amends the top commit in the stack (HEAD).
To amend a commit further down the stack, use Git's rebase support,
for example by using ``git commit --fixup'' followed by ``git codereview rebase-work''.

The ``git codereview mail'' command requires an explicit revision argument,
but note that since ``git codereview mail'' is implemented as a ``git push'',
any commits earlier in the stack are necessarily also mailed.

The ``git codereview submit'' command also requires an explicit revision argument,
and while earlier commits are necessarily still uploaded and mailed,
only the named revision or revisions are submitted (merged into origin/master).
In a single-commit work branch, a successful ``git codereview submit''
effectively runs ``git codereview sync'' automatically.
In a multiple-commit work branch, it does not, because
the implied ``git rebase'' may conflict with the remaining pending commits.
Instead it is necessary to run ``git codereview sync'' explicitly
(when ready) after ``git codereview submit''.

Reusing Work Branches

Although one common practice is to create a new branch for each pending change,
running ``git codereview submit'' (and possibly ``git codereview sync'')
leaves the current branch ready for reuse with a future change.
Some developers find it helpful to create a single work branch
(``git change work'') and then do all work in that branch,
possibly in the multiple-commit mode, never changing between branches.

Command Details

All commands accept these global flags:

The -v flag prints all commands that make changes. Multiple occurrences
trigger more verbosity in some commands, including sync.

The -n flag prints all commands that would be run, but does not run them.

Descriptions of each command follow.

Branchpoint

	git codereview branchpoint

The branchpoint command prints the commit hash of the most recent commit
on the current branch that is shared with the Gerrit server. This is the point
where local work branched from the published tree. The command is intended
mainly for use in scripts. For example, ``git diff $(git codereview branchpoint)''
or ``git log $(git codereview branchpoint)..HEAD''.

Change

The change command creates and moves between Git branches and maintains the
pending changes on work branches.

	git codereview change [-a] [-q] [branchname]

Given a branch name as an argument, the change command switches to the named
branch, creating it if necessary. If the branch is created and there are staged
changes, it will commit the changes to the branch, creating a new pending
change.

With no argument, the change command creates a new pending change from the
staged changes in the current branch or, if there is already a pending change,
amends that change.

The -q option skips the editing of an extant pending change's commit message.

The -a option automatically adds any unstaged edits in tracked files during
commit; it is equivalent to the 'git commit' -a option.

Gofmt

The gofmt command applies the gofmt program to all files modified in the
current work branch, both in the staging area (index) and the working tree
(local directory).

	git codereview gofmt [-l]

The -l option causes the command to list the files that need reformatting but
not reformat them. Otherwise, the gofmt command reformats modified files in
place. That is, files in the staging area are reformatted in the staging area,
and files in the working tree are reformatted in the working tree.

Help

The help command displays basic usage instructions.

	git codereview help

Hooks

The hooks command installs the Git hooks to enforce code review conventions.

	git codereview hooks

The pre-commit hook checks that all Go code is formatted with gofmt and that
the commit is not being made directly to the master branch.

The commit-msg hook adds the Gerrit ``Change-Id'' line to the commit message if
not present. It also checks that the message uses the convention established by
the Go project that the first line has the form, pkg/path: summary.

The hooks command will not overwrite an existing hook.
If it is not installing hooks, use ``git codereview hooks -v'' for details.
This hook installation is also done at startup by all other git codereview
commands, except ``git codereview help''.

Hook-Invoke

The hook-invoke command is an internal command that invokes the named Git hook.

	git codereview hook-invoke <hook> [args]

It is run by the shell scripts installed by the ``git codereview hooks'' command.

Mail

The mail command starts the code review process for the pending change.

	git codereview mail [-f] [-r email] [-cc email] [-trybot] [revision]

It pushes the pending change commit in the current branch to the Gerrit code
review server and prints the URL for the change on the server.
If the change already exists on the server, the mail command updates that
change with a new changeset.

The -r and -cc flags identify the email addresses of people to do the code
review and to be CC'ed about the code review.
Multiple addresses are given as a comma-separated list.

An email address passed to -r or -cc can be shortened from name@domain to name.
The mail command resolves such shortenings by reading the list of past reviewers
from the git repository log to find email addresses of the form name@somedomain
and then, in case of ambiguity, using the reviewer who appears most often.

The -trybot flag runs the trybots on all new or updated changes. It is
equivalent to setting the Run-Trybot+1 label from Gerrit.

The mail command fails if there are staged edits that are not committed.
The -f flag overrides this behavior.

The mail command updates the tag <branchname>.mailed to refer to the
commit that was most recently mailed, so running ``git diff <branchname>.mailed''
shows diffs between what is on the Gerrit server and the current directory.

If there are multiple pending commits, the revision argument is mandatory.
If no revision is specified, the mail command prints a short summary of
the pending commits for use in deciding which to mail.

If any commit that would be pushed to the server contains the text
"DO NOT MAIL" (case insensitive) in its commit message, the mail command
will refuse to send the commit to the server.

Pending

The pending command prints to standard output the status of all pending changes
and staged, unstaged, and untracked files in the local repository.

	git codereview pending [-c] [-l] [-s]

The -c flag causes the command to show pending changes only on the current branch.

The -l flag causes the command to use only locally available information.
By default, it fetches recent commits and code review information from the
Gerrit server.

The -s flag causes the command to print abbreviated (short) output.

Common shorter aliases include ``git p'' for ``git pending''
and ``git pl'' for ``git pending -l'' (notably faster but without Gerrit information).

Rebase-work

The rebase-work command runs git rebase in interactive mode over pending changes.
It is shorthand for ``git rebase -i $(git codereview branchpoint)''.
It differs from plain ``git rebase -i'' in that the latter will try to incorporate
new commits from the origin branch during the rebase;
``git codereview rebase-work'' does not.

In multiple-commit workflows, rebase-work is used so often
that it can be helpful to alias it to ``git rw''.

Submit

The submit command pushes the pending change to the Gerrit server and tells
Gerrit to submit it to the master branch.

	git codereview submit [-i | revision...]

The command fails if there are modified files (staged or unstaged) that are not
part of the pending change.

The -i option causes the submit command to open a list of commits to submit
in the configured text editor, similar to ``git rebase -i''.

If multiple revisions are specified, the submit command submits each one in turn,
stopping at the first failure.

When run in a multiple-commit work branch,
either the -i option or the revision argument is mandatory.
If both are omitted, the submit command prints a short summary of
the pending commits for use in deciding which to submit.

After submitting the pending changes, the submit command tries to synchronize the
current branch to the submitted commit, if it can do so cleanly.
If not, it will prompt the user to run ``git codereview sync'' manually.

After a successful sync, the branch can be used to prepare a new change.

Sync

The sync command updates the local repository.

	git codereview sync

It fetches commits from the remote repository and merges them from the
upstream branch to the current branch, rebasing any pending changes.

Configuration

If a file named codereview.cfg is present in the repository root,
git-codereview will use it for configuration. It should contain lines
of this format:

	key: value

The ``gerrit'' key sets the Gerrit URL for this project. Git-codereview
automatically derives the Gerrit URL from repositories hosted in
*.googlesource.com. If not set or derived, the repository is assumed to
not have Gerrit, and certain features won't work.

The ``issuerepo'' key specifies the GitHub repository to use for issues, if
different from the source repository. If set to ``golang/go'', for example,
lines such as ``Fixes #123'' in a commit message will be rewritten to ``Fixes
golang/go#123''.

*/
package main
<|endoftext|>


<commit_before>package scanner

import (
	"encoding/json"
	"net/http"
	"os"

	"github.com/kevintavog/findaphoto/common"

	"github.com/ian-kent/go-log/log"
	"golang.org/x/net/context"
	"gopkg.in/olivere/elastic.v5"
)

var MediaScanned int64
var MediaRemoved int64

// Walk through the index, removing any items no longer on the file system
func RemoveFiles() {
	client := common.CreateClient()

	scrollResponse, err := client.Scroll(common.MediaIndexName).
		Size(100).
		Type(common.MediaTypeName).
		Do(context.TODO())
	if err != nil {
		log.Error("Failed starting scan: %s", err.Error())
		return
	}

	scrollId := scrollResponse.ScrollId
	for {
		results, err := client.Scroll(common.MediaIndexName).
			Size(100).
			ScrollId(scrollId).
			Do(context.TODO())
		if err != nil {
			if el, ok := err.(*elastic.Error); ok {
				if el.Status == http.StatusNotFound {
					break
				}
			}

			log.Error("Failed scanning index: %s", err.Error())
			break
		}

		for _, hit := range results.Hits.Hits {
			var media common.Media
			err := json.Unmarshal(*hit.Source, &media)
			if err != nil {
				log.Error("Failed deserializing search result: %s", err.Error())
				continue
			}

			MediaScanned += 1
			removeDocument := false
			if !common.IsValidAliasedPath(media.Path) {
				removeDocument = true
			} else {
				fullPath, err := common.FullPathForAliasedPath(media.Path)
				if err != nil {
					log.Error("Unable to convert %s to a path: %s", media.Path, err.Error())
					continue
				}

				if _, err = os.Stat(fullPath); os.IsNotExist(err) {
					removeDocument = true
					log.Info("File doesn't exist: %s", fullPath)
				}
			}

			if removeDocument {
				MediaRemoved += 1
				deleteResponse, err := client.Delete().
					Index(common.MediaIndexName).
					Type(common.MediaTypeName).
					Id(media.Path).
					Do(context.TODO())
				if err != nil {
					log.Error("Failed removing document '%s' from index: %s", media.Path, err.Error())
				} else if deleteResponse.Found != true {
					log.Error("Delete of document '%s' failed", media.Path)
				}
			}
		}
	}
}
<commit_msg>Properly scan for removed documents<commit_after>package scanner

import (
	"encoding/json"
	"io"
	"net/http"
	"os"

	"github.com/kevintavog/findaphoto/common"

	"github.com/ian-kent/go-log/log"
	"golang.org/x/net/context"
	"gopkg.in/olivere/elastic.v5"
)

var MediaScanned int64
var MediaRemoved int64

// Walk through the index, removing any items no longer on the file system
func RemoveFiles() {
	client := common.CreateClient()

	scrollService := client.Scroll(common.MediaIndexName).Type(common.MediaTypeName).Size(100)

	_, err := scrollService.Do(context.TODO())
	if err != nil {
		log.Error("Failed starting scan: %s", err.Error())
		return
	}

	checked := 0
	removed := 0
	for {
		results, err := scrollService.Do(context.TODO())
		if err == io.EOF {
			break
		}
		if err != nil {
			if el, ok := err.(*elastic.Error); ok {
				if el.Status == http.StatusNotFound {
					break
				}
			}

			log.Error("Failed scanning index: %s", err.Error())
			break
		}

		for _, hit := range results.Hits.Hits {
			checked += 1

			var media common.Media
			err := json.Unmarshal(*hit.Source, &media)
			if err != nil {
				log.Error("Failed deserializing search result: %s", err.Error())
				continue
			}

			MediaScanned += 1
			removeDocument := false
			if !common.IsValidAliasedPath(media.Path) {
				removeDocument = true
			} else {
				fullPath, err := common.FullPathForAliasedPath(media.Path)
				if err != nil {
					log.Error("Unable to convert %s to a path: %s", media.Path, err.Error())
					continue
				}

				if _, err = os.Stat(fullPath); os.IsNotExist(err) {
					removeDocument = true
					log.Info("File doesn't exist: %s", fullPath)
				}
			}

			if removeDocument {
				removed += 1
				MediaRemoved += 1
				deleteResponse, err := client.Delete().
					Index(common.MediaIndexName).
					Type(common.MediaTypeName).
					Id(media.Path).
					Do(context.TODO())
				if err != nil {
					log.Error("Failed removing document '%s' from index: %s", media.Path, err.Error())
				} else if deleteResponse.Found != true {
					log.Error("Delete of document '%s' failed", media.Path)
				}
			}
		}
	}

	log.Info("Remover checked %d files and removed %d of them", checked, removed)
}
<|endoftext|>


<commit_before>// This file was auto-generated by the veyron vdl tool.
// Source: nativedep.vdl

package nativedep

import (
	// VDL system imports
	"v.io/v23/vdl"

	// VDL user imports
	"time"
	_ "v.io/x/ref/lib/vdl/testdata/nativetest"
	"v.io/v23/vdl/testdata/nativetest"
)

type All struct {
	A string
	B map[string]int
	C time.Time
	D nativetest.NativeSamePkg
	E map[nativetest.NativeSamePkg]time.Time
}

func (All) __VDLReflect(struct {
	Name string "v.io/x/ref/lib/vdl/testdata/nativedep.All"
}) {
}

func init() {
	vdl.Register((*All)(nil))
}
<commit_msg>lib/vdl: fixing out-of-date test data<commit_after>// This file was auto-generated by the veyron vdl tool.
// Source: nativedep.vdl

package nativedep

import (
	// VDL system imports
	"v.io/v23/vdl"

	// VDL user imports
	"time"
	"v.io/v23/vdl/testdata/nativetest"
	_ "v.io/x/ref/lib/vdl/testdata/nativetest"
)

type All struct {
	A string
	B map[string]int
	C time.Time
	D nativetest.NativeSamePkg
	E map[nativetest.NativeSamePkg]time.Time
}

func (All) __VDLReflect(struct {
	Name string "v.io/x/ref/lib/vdl/testdata/nativedep.All"
}) {
}

func init() {
	vdl.Register((*All)(nil))
}
<|endoftext|>


<commit_before>package ipvlan

import (
	"fmt"

	"github.com/docker/docker/pkg/parsers/kernel"
	"github.com/docker/docker/pkg/stringid"
	"github.com/docker/libnetwork/driverapi"
	"github.com/docker/libnetwork/netlabel"
	"github.com/docker/libnetwork/ns"
	"github.com/docker/libnetwork/options"
	"github.com/docker/libnetwork/osl"
	"github.com/docker/libnetwork/types"
	"github.com/sirupsen/logrus"
)

// CreateNetwork the network for the specified driver type
func (d *driver) CreateNetwork(nid string, option map[string]interface{}, nInfo driverapi.NetworkInfo, ipV4Data, ipV6Data []driverapi.IPAMData) error {
	defer osl.InitOSContext()()
	kv, err := kernel.GetKernelVersion()
	if err != nil {
		return fmt.Errorf("Failed to check kernel version for %s driver support: %v", ipvlanType, err)
	}
	// ensure Kernel version is >= v4.2 for ipvlan support
	if kv.Kernel < ipvlanKernelVer || (kv.Kernel == ipvlanKernelVer && kv.Major < ipvlanMajorVer) {
		return fmt.Errorf("kernel version failed to meet the minimum ipvlan kernel requirement of %d.%d, found %d.%d.%d",
			ipvlanKernelVer, ipvlanMajorVer, kv.Kernel, kv.Major, kv.Minor)
	}
	// reject a null v4 network
	if len(ipV4Data) == 0 || ipV4Data[0].Pool.String() == "0.0.0.0/0" {
		return fmt.Errorf("ipv4 pool is empty")
	}
	// parse and validate the config and bind to networkConfiguration
	config, err := parseNetworkOptions(nid, option)
	if err != nil {
		return err
	}
	config.ID = nid
	err = config.processIPAM(nid, ipV4Data, ipV6Data)
	if err != nil {
		return err
	}
	// verify the ipvlan mode from -o ipvlan_mode option
	switch config.IpvlanMode {
	case "", modeL2:
		// default to ipvlan L2 mode if -o ipvlan_mode is empty
		config.IpvlanMode = modeL2
	case modeL3:
		config.IpvlanMode = modeL3
	default:
		return fmt.Errorf("requested ipvlan mode '%s' is not valid, 'l2' mode is the ipvlan driver default", config.IpvlanMode)
	}
	// loopback is not a valid parent link
	if config.Parent == "lo" {
		return fmt.Errorf("loopback interface is not a valid %s parent link", ipvlanType)
	}
	// if parent interface not specified, create a dummy type link to use named dummy+net_id
	if config.Parent == "" {
		config.Parent = getDummyName(stringid.TruncateID(config.ID))
		// empty parent and --internal are handled the same. Set here to update k/v
		config.Internal = true
	}
	err = d.createNetwork(config)
	if err != nil {
		return err
	}
	// update persistent db, rollback on fail
	err = d.storeUpdate(config)
	if err != nil {
		d.deleteNetwork(config.ID)
		logrus.Debugf("encountered an error rolling back a network create for %s : %v", config.ID, err)
		return err
	}

	return nil
}

// createNetwork is used by new network callbacks and persistent network cache
func (d *driver) createNetwork(config *configuration) error {
	networkList := d.getNetworks()
	for _, nw := range networkList {
		if config.Parent == nw.config.Parent {
			return fmt.Errorf("network %s is already using parent interface %s",
				getDummyName(stringid.TruncateID(nw.config.ID)), config.Parent)
		}
	}
	if !parentExists(config.Parent) {
		// if the --internal flag is set, create a dummy link
		if config.Internal {
			err := createDummyLink(config.Parent, getDummyName(stringid.TruncateID(config.ID)))
			if err != nil {
				return err
			}
			config.CreatedSlaveLink = true
			// notify the user in logs they have limited communications
			if config.Parent == getDummyName(stringid.TruncateID(config.ID)) {
				logrus.Debugf("Empty -o parent= and --internal flags limit communications to other containers inside of network: %s",
					config.Parent)
			}
		} else {
			// if the subinterface parent_iface.vlan_id checks do not pass, return err.
			//  a valid example is 'eth0.10' for a parent iface 'eth0' with a vlan id '10'
			err := createVlanLink(config.Parent)
			if err != nil {
				return err
			}
			// if driver created the networks slave link, record it for future deletion
			config.CreatedSlaveLink = true
		}
	}
	n := &network{
		id:        config.ID,
		driver:    d,
		endpoints: endpointTable{},
		config:    config,
	}
	// add the *network
	d.addNetwork(n)

	return nil
}

// DeleteNetwork the network for the specified driver type
func (d *driver) DeleteNetwork(nid string) error {
	defer osl.InitOSContext()()
	n := d.network(nid)
	if n == nil {
		return fmt.Errorf("network id %s not found", nid)
	}
	// if the driver created the slave interface, delete it, otherwise leave it
	if ok := n.config.CreatedSlaveLink; ok {
		// if the interface exists, only delete if it matches iface.vlan or dummy.net_id naming
		if ok := parentExists(n.config.Parent); ok {
			// only delete the link if it is named the net_id
			if n.config.Parent == getDummyName(stringid.TruncateID(nid)) {
				err := delDummyLink(n.config.Parent)
				if err != nil {
					logrus.Debugf("link %s was not deleted, continuing the delete network operation: %v",
						n.config.Parent, err)
				}
			} else {
				// only delete the link if it matches iface.vlan naming
				err := delVlanLink(n.config.Parent)
				if err != nil {
					logrus.Debugf("link %s was not deleted, continuing the delete network operation: %v",
						n.config.Parent, err)
				}
			}
		}
	}
	for _, ep := range n.endpoints {
		if link, err := ns.NlHandle().LinkByName(ep.srcName); err == nil {
			if err := ns.NlHandle().LinkDel(link); err != nil {
				logrus.WithError(err).Warnf("Failed to delete interface (%s)'s link on endpoint (%s) delete", ep.srcName, ep.id)
			}
		}

		if err := d.storeDelete(ep); err != nil {
			logrus.Warnf("Failed to remove ipvlan endpoint %.7s from store: %v", ep.id, err)
		}
	}
	// delete the *network
	d.deleteNetwork(nid)
	// delete the network record from persistent cache
	err := d.storeDelete(n.config)
	if err != nil {
		return fmt.Errorf("error deleting deleting id %s from datastore: %v", nid, err)
	}
	return nil
}

// parseNetworkOptions parse docker network options
func parseNetworkOptions(id string, option options.Generic) (*configuration, error) {
	var (
		err    error
		config = &configuration{}
	)
	// parse generic labels first
	if genData, ok := option[netlabel.GenericData]; ok && genData != nil {
		if config, err = parseNetworkGenericOptions(genData); err != nil {
			return nil, err
		}
	}
	// setting the parent to "" will trigger an isolated network dummy parent link
	if _, ok := option[netlabel.Internal]; ok {
		config.Internal = true
		// empty --parent= and --internal are handled the same.
		config.Parent = ""
	}
	return config, nil
}

// parseNetworkGenericOptions parse generic driver docker network options
func parseNetworkGenericOptions(data interface{}) (*configuration, error) {
	var (
		err    error
		config *configuration
	)
	switch opt := data.(type) {
	case *configuration:
		config = opt
	case map[string]string:
		config = &configuration{}
		err = config.fromOptions(opt)
	case options.Generic:
		var opaqueConfig interface{}
		if opaqueConfig, err = options.GenerateFromModel(opt, config); err == nil {
			config = opaqueConfig.(*configuration)
		}
	default:
		err = types.BadRequestErrorf("unrecognized network configuration format: %v", opt)
	}
	return config, err
}

// fromOptions binds the generic options to networkConfiguration to cache
func (config *configuration) fromOptions(labels map[string]string) error {
	for label, value := range labels {
		switch label {
		case parentOpt:
			// parse driver option '-o parent'
			config.Parent = value
		case driverModeOpt:
			// parse driver option '-o ipvlan_mode'
			config.IpvlanMode = value
		}
	}
	return nil
}

// processIPAM parses v4 and v6 IP information and binds it to the network configuration
func (config *configuration) processIPAM(id string, ipamV4Data, ipamV6Data []driverapi.IPAMData) error {
	if len(ipamV4Data) > 0 {
		for _, ipd := range ipamV4Data {
			s := &ipv4Subnet{
				SubnetIP: ipd.Pool.String(),
				GwIP:     ipd.Gateway.String(),
			}
			config.Ipv4Subnets = append(config.Ipv4Subnets, s)
		}
	}
	if len(ipamV6Data) > 0 {
		for _, ipd := range ipamV6Data {
			s := &ipv6Subnet{
				SubnetIP: ipd.Pool.String(),
				GwIP:     ipd.Gateway.String(),
			}
			config.Ipv6Subnets = append(config.Ipv6Subnets, s)
		}
	}
	return nil
}
<commit_msg>Ipvlan network handles netlabel.Internal wrong<commit_after>package ipvlan

import (
	"fmt"

	"github.com/docker/docker/pkg/parsers/kernel"
	"github.com/docker/docker/pkg/stringid"
	"github.com/docker/libnetwork/driverapi"
	"github.com/docker/libnetwork/netlabel"
	"github.com/docker/libnetwork/ns"
	"github.com/docker/libnetwork/options"
	"github.com/docker/libnetwork/osl"
	"github.com/docker/libnetwork/types"
	"github.com/sirupsen/logrus"
)

// CreateNetwork the network for the specified driver type
func (d *driver) CreateNetwork(nid string, option map[string]interface{}, nInfo driverapi.NetworkInfo, ipV4Data, ipV6Data []driverapi.IPAMData) error {
	defer osl.InitOSContext()()
	kv, err := kernel.GetKernelVersion()
	if err != nil {
		return fmt.Errorf("Failed to check kernel version for %s driver support: %v", ipvlanType, err)
	}
	// ensure Kernel version is >= v4.2 for ipvlan support
	if kv.Kernel < ipvlanKernelVer || (kv.Kernel == ipvlanKernelVer && kv.Major < ipvlanMajorVer) {
		return fmt.Errorf("kernel version failed to meet the minimum ipvlan kernel requirement of %d.%d, found %d.%d.%d",
			ipvlanKernelVer, ipvlanMajorVer, kv.Kernel, kv.Major, kv.Minor)
	}
	// reject a null v4 network
	if len(ipV4Data) == 0 || ipV4Data[0].Pool.String() == "0.0.0.0/0" {
		return fmt.Errorf("ipv4 pool is empty")
	}
	// parse and validate the config and bind to networkConfiguration
	config, err := parseNetworkOptions(nid, option)
	if err != nil {
		return err
	}
	config.ID = nid
	err = config.processIPAM(nid, ipV4Data, ipV6Data)
	if err != nil {
		return err
	}
	// verify the ipvlan mode from -o ipvlan_mode option
	switch config.IpvlanMode {
	case "", modeL2:
		// default to ipvlan L2 mode if -o ipvlan_mode is empty
		config.IpvlanMode = modeL2
	case modeL3:
		config.IpvlanMode = modeL3
	default:
		return fmt.Errorf("requested ipvlan mode '%s' is not valid, 'l2' mode is the ipvlan driver default", config.IpvlanMode)
	}
	// loopback is not a valid parent link
	if config.Parent == "lo" {
		return fmt.Errorf("loopback interface is not a valid %s parent link", ipvlanType)
	}
	// if parent interface not specified, create a dummy type link to use named dummy+net_id
	if config.Parent == "" {
		config.Parent = getDummyName(stringid.TruncateID(config.ID))
		// empty parent and --internal are handled the same. Set here to update k/v
		config.Internal = true
	}
	err = d.createNetwork(config)
	if err != nil {
		return err
	}
	// update persistent db, rollback on fail
	err = d.storeUpdate(config)
	if err != nil {
		d.deleteNetwork(config.ID)
		logrus.Debugf("encountered an error rolling back a network create for %s : %v", config.ID, err)
		return err
	}

	return nil
}

// createNetwork is used by new network callbacks and persistent network cache
func (d *driver) createNetwork(config *configuration) error {
	networkList := d.getNetworks()
	for _, nw := range networkList {
		if config.Parent == nw.config.Parent {
			return fmt.Errorf("network %s is already using parent interface %s",
				getDummyName(stringid.TruncateID(nw.config.ID)), config.Parent)
		}
	}
	if !parentExists(config.Parent) {
		// if the --internal flag is set, create a dummy link
		if config.Internal {
			err := createDummyLink(config.Parent, getDummyName(stringid.TruncateID(config.ID)))
			if err != nil {
				return err
			}
			config.CreatedSlaveLink = true
			// notify the user in logs they have limited communications
			if config.Parent == getDummyName(stringid.TruncateID(config.ID)) {
				logrus.Debugf("Empty -o parent= and --internal flags limit communications to other containers inside of network: %s",
					config.Parent)
			}
		} else {
			// if the subinterface parent_iface.vlan_id checks do not pass, return err.
			//  a valid example is 'eth0.10' for a parent iface 'eth0' with a vlan id '10'
			err := createVlanLink(config.Parent)
			if err != nil {
				return err
			}
			// if driver created the networks slave link, record it for future deletion
			config.CreatedSlaveLink = true
		}
	}
	n := &network{
		id:        config.ID,
		driver:    d,
		endpoints: endpointTable{},
		config:    config,
	}
	// add the *network
	d.addNetwork(n)

	return nil
}

// DeleteNetwork the network for the specified driver type
func (d *driver) DeleteNetwork(nid string) error {
	defer osl.InitOSContext()()
	n := d.network(nid)
	if n == nil {
		return fmt.Errorf("network id %s not found", nid)
	}
	// if the driver created the slave interface, delete it, otherwise leave it
	if ok := n.config.CreatedSlaveLink; ok {
		// if the interface exists, only delete if it matches iface.vlan or dummy.net_id naming
		if ok := parentExists(n.config.Parent); ok {
			// only delete the link if it is named the net_id
			if n.config.Parent == getDummyName(stringid.TruncateID(nid)) {
				err := delDummyLink(n.config.Parent)
				if err != nil {
					logrus.Debugf("link %s was not deleted, continuing the delete network operation: %v",
						n.config.Parent, err)
				}
			} else {
				// only delete the link if it matches iface.vlan naming
				err := delVlanLink(n.config.Parent)
				if err != nil {
					logrus.Debugf("link %s was not deleted, continuing the delete network operation: %v",
						n.config.Parent, err)
				}
			}
		}
	}
	for _, ep := range n.endpoints {
		if link, err := ns.NlHandle().LinkByName(ep.srcName); err == nil {
			if err := ns.NlHandle().LinkDel(link); err != nil {
				logrus.WithError(err).Warnf("Failed to delete interface (%s)'s link on endpoint (%s) delete", ep.srcName, ep.id)
			}
		}

		if err := d.storeDelete(ep); err != nil {
			logrus.Warnf("Failed to remove ipvlan endpoint %.7s from store: %v", ep.id, err)
		}
	}
	// delete the *network
	d.deleteNetwork(nid)
	// delete the network record from persistent cache
	err := d.storeDelete(n.config)
	if err != nil {
		return fmt.Errorf("error deleting deleting id %s from datastore: %v", nid, err)
	}
	return nil
}

// parseNetworkOptions parse docker network options
func parseNetworkOptions(id string, option options.Generic) (*configuration, error) {
	var (
		err    error
		config = &configuration{}
	)
	// parse generic labels first
	if genData, ok := option[netlabel.GenericData]; ok && genData != nil {
		if config, err = parseNetworkGenericOptions(genData); err != nil {
			return nil, err
		}
	}
	// setting the parent to "" will trigger an isolated network dummy parent link
	if val, ok := option[netlabel.Internal]; ok {
		if internal, ok := val.(bool); ok && internal {
			config.Internal = true
			// empty --parent= and --internal are handled the same.
			config.Parent = ""
		}
	}
	return config, nil
}

// parseNetworkGenericOptions parse generic driver docker network options
func parseNetworkGenericOptions(data interface{}) (*configuration, error) {
	var (
		err    error
		config *configuration
	)
	switch opt := data.(type) {
	case *configuration:
		config = opt
	case map[string]string:
		config = &configuration{}
		err = config.fromOptions(opt)
	case options.Generic:
		var opaqueConfig interface{}
		if opaqueConfig, err = options.GenerateFromModel(opt, config); err == nil {
			config = opaqueConfig.(*configuration)
		}
	default:
		err = types.BadRequestErrorf("unrecognized network configuration format: %v", opt)
	}
	return config, err
}

// fromOptions binds the generic options to networkConfiguration to cache
func (config *configuration) fromOptions(labels map[string]string) error {
	for label, value := range labels {
		switch label {
		case parentOpt:
			// parse driver option '-o parent'
			config.Parent = value
		case driverModeOpt:
			// parse driver option '-o ipvlan_mode'
			config.IpvlanMode = value
		}
	}
	return nil
}

// processIPAM parses v4 and v6 IP information and binds it to the network configuration
func (config *configuration) processIPAM(id string, ipamV4Data, ipamV6Data []driverapi.IPAMData) error {
	if len(ipamV4Data) > 0 {
		for _, ipd := range ipamV4Data {
			s := &ipv4Subnet{
				SubnetIP: ipd.Pool.String(),
				GwIP:     ipd.Gateway.String(),
			}
			config.Ipv4Subnets = append(config.Ipv4Subnets, s)
		}
	}
	if len(ipamV6Data) > 0 {
		for _, ipd := range ipamV6Data {
			s := &ipv6Subnet{
				SubnetIP: ipd.Pool.String(),
				GwIP:     ipd.Gateway.String(),
			}
			config.Ipv6Subnets = append(config.Ipv6Subnets, s)
		}
	}
	return nil
}
<|endoftext|>


<commit_before>package libvirt

import (
	"fmt"
	"testing"

	libvirt "github.com/digitalocean/go-libvirt"
	"github.com/hashicorp/terraform-plugin-sdk/helper/acctest"
	"github.com/hashicorp/terraform-plugin-sdk/helper/resource"
	"github.com/hashicorp/terraform-plugin-sdk/terraform"
)

func TestAccLibvirtCloudInit_CreateCloudInitDiskAndUpdate(t *testing.T) {
	var volume libvirt.StorageVol
	randomResourceName := acctest.RandStringFromCharSet(10, acctest.CharSetAlpha)
	randomPoolName := acctest.RandStringFromCharSet(10, acctest.CharSetAlpha)
	randomPoolPath := "/tmp/terraform-provider-libvirt-pool-" + randomPoolName
	// this structs are contents values we expect.
	expectedContents := Expected{UserData: "#cloud-config", NetworkConfig: "network:", MetaData: "instance-id: bamboo"}
	expectedContents2 := Expected{UserData: "#cloud-config2", NetworkConfig: "network2:", MetaData: "instance-id: bamboo2"}
	expectedContentsEmpty := Expected{UserData: "#cloud-config2", NetworkConfig: "", MetaData: ""}
	randomIsoName := acctest.RandStringFromCharSet(10, acctest.CharSetAlpha) + ".iso"
	resource.Test(t, resource.TestCase{
		PreCheck:  func() { testAccPreCheck(t) },
		Providers: testAccProviders,
		CheckDestroy: func(s *terraform.State) error {
			return nil
		},
		Steps: []resource.TestStep{
			{
				Config: fmt.Sprintf(`
					resource "libvirt_pool" "%s" {
								name = "%s"
								type = "dir"
								path = "%s"
                            }
					resource "libvirt_cloudinit_disk" "%s" {
								name           = "%s"
								user_data      = "#cloud-config"
								meta_data      = "instance-id: bamboo"
								network_config = "network:"
                                pool           = "${libvirt_pool.%s.name}"
							}`, randomPoolName, randomPoolName, randomPoolPath, randomResourceName, randomIsoName, randomPoolName),

				Check: resource.ComposeTestCheckFunc(
					resource.TestCheckResourceAttr(
						"libvirt_cloudinit_disk."+randomResourceName, "name", randomIsoName),
					testAccCheckCloudInitVolumeExists("libvirt_cloudinit_disk."+randomResourceName, &volume),
					expectedContents.testAccCheckCloudInitDiskFilesContent("libvirt_cloudinit_disk."+randomResourceName, &volume),
				),
			},
			{
				Config: fmt.Sprintf(`
					resource "libvirt_pool" "%s" {
								name = "%s"
								type = "dir"
								path = "%s"
                            }
					resource "libvirt_cloudinit_disk" "%s" {
								name           = "%s"
								user_data      = "#cloud-config2"
								meta_data = "instance-id: bamboo2"
								network_config = "network2:"
                                pool           = "${libvirt_pool.%s.name}"
							}`, randomPoolName, randomPoolName, randomPoolPath, randomResourceName, randomIsoName, randomPoolName),

				Check: resource.ComposeTestCheckFunc(
					resource.TestCheckResourceAttr(
						"libvirt_cloudinit_disk."+randomResourceName, "name", randomIsoName),
					testAccCheckCloudInitVolumeExists("libvirt_cloudinit_disk."+randomResourceName, &volume),
					expectedContents2.testAccCheckCloudInitDiskFilesContent("libvirt_cloudinit_disk."+randomResourceName, &volume),
				),
			},
			{
				Config: fmt.Sprintf(`
					resource "libvirt_pool" "%s" {
								name = "%s"
								type = "dir"
								path = "%s"
                            }
					resource "libvirt_cloudinit_disk" "%s" {
								name           = "%s"
								user_data      = "#cloud-config2"
                                pool           = "${libvirt_pool.%s.name}"
							}`, randomPoolName, randomPoolName, randomPoolPath, randomResourceName, randomIsoName, randomPoolName),
				Check: resource.ComposeTestCheckFunc(
					resource.TestCheckResourceAttr(
						"libvirt_cloudinit_disk."+randomResourceName, "name", randomIsoName),
					testAccCheckCloudInitVolumeExists("libvirt_cloudinit_disk."+randomResourceName, &volume),
					expectedContentsEmpty.testAccCheckCloudInitDiskFilesContent("libvirt_cloudinit_disk."+randomResourceName, &volume),
				),
			},
			// when we apply 2 times with same conf, we should not have a diff. See bug:
			// https://github.com/dmacvicar/terraform-provider-libvirt/issues/313
			{
				Config: fmt.Sprintf(`
                        resource "libvirt_pool" "%s" {
								    name = "%s"
                                    type = "dir"
                                    path = "%s"
                                }
						resource "libvirt_cloudinit_disk" "%s" {
									name           = "%s"
									user_data      = "#cloud-config4"
                                    pool           = "${libvirt_pool.%s.name}"
								}`, randomPoolName, randomPoolName, randomPoolPath, randomResourceName, randomIsoName, randomPoolName),
				ExpectNonEmptyPlan: true,
				PlanOnly:           true,
				Check: resource.ComposeTestCheckFunc(
					resource.TestCheckResourceAttr(
						"libvirt_cloudinit_disk."+randomResourceName, "name", randomIsoName),
					testAccCheckCloudInitVolumeExists("libvirt_cloudinit_disk."+randomResourceName, &volume),
					expectedContentsEmpty.testAccCheckCloudInitDiskFilesContent("libvirt_cloudinit_disk."+randomResourceName, &volume),
				),
			},
		},
	})
}

// The destroy function should always handle the case where the resource might already be destroyed
// (manually, for example). If the resource is already destroyed, this should not return an error.
// This allows Terraform users to manually delete resources without breaking Terraform.
// This test should fail without a proper "Exists" implementation
func TestAccLibvirtCloudInit_ManuallyDestroyed(t *testing.T) {
	var volume libvirt.StorageVol
	randomResourceName := acctest.RandStringFromCharSet(10, acctest.CharSetAlpha)
	randomPoolName := acctest.RandStringFromCharSet(10, acctest.CharSetAlpha)
	randomPoolPath := "/tmp/terraform-provider-libvirt-pool-" + randomPoolName

	testAccCheckLibvirtCloudInitConfigBasic := fmt.Sprintf(`
        resource "libvirt_pool" "%s" {
            name = "%s"
            type = "dir"
            path = "%s"
        }
        resource "libvirt_cloudinit_disk" "%s" {
            name           = "%s"
            pool           = "${libvirt_pool.%s.name}"
			user_data      = "#cloud-config\nssh_authorized_keys: []\n"
		}`, randomPoolName, randomPoolName, randomPoolPath, randomResourceName, randomResourceName, randomPoolName)

	resource.Test(t, resource.TestCase{
		PreCheck:  func() { testAccPreCheck(t) },
		Providers: testAccProviders,
		CheckDestroy: func(s *terraform.State) error {
			return nil
		},
		Steps: []resource.TestStep{
			{
				Config: testAccCheckLibvirtCloudInitConfigBasic,
				Check: resource.ComposeTestCheckFunc(
					testAccCheckCloudInitVolumeExists("libvirt_cloudinit_disk."+randomResourceName, &volume),
				),
			},
			{
				Config:  testAccCheckLibvirtCloudInitConfigBasic,
				Destroy: true,
				PreConfig: func() {
					client := testAccProvider.Meta().(*Client)
					if volume.Key == "" {
						t.Fatalf("Key is blank")
					}
					if err := volumeDelete(client, volume.Key); err != nil {
						t.Fatal(err)
					}
				},
			},
		},
	})
}

func testAccCheckCloudInitVolumeExists(volumeName string, volume *libvirt.StorageVol) resource.TestCheckFunc {
	return func(state *terraform.State) error {
		virConn := testAccProvider.Meta().(*Client).libvirt

		rs, err := getResourceFromTerraformState(volumeName, state)
		if err != nil {
			return err
		}
		cikey, err := getCloudInitVolumeKeyFromTerraformID(rs.Primary.ID)
		if err != nil {
			return err
		}
		retrievedVol, err := virConn.StorageVolLookupByKey(cikey)
		if err != nil {
			return err
		}
		if retrievedVol.Key == "" {
			return fmt.Errorf("UUID is blank")
		}

		if retrievedVol.Key != cikey {
			fmt.Printf("retrievedVol.Key is: %s \ncloudinit key is %s", retrievedVol.Key, cikey)
			return fmt.Errorf("Resource ID and cloudinit volume key does not match")
		}

		*volume = retrievedVol

		return nil
	}
}

// this is helper method for test expected values
type Expected struct {
	UserData, NetworkConfig, MetaData string
}

func (expected *Expected) testAccCheckCloudInitDiskFilesContent(volumeName string, volume *libvirt.StorageVol) resource.TestCheckFunc {
	return func(state *terraform.State) error {
		virConn := testAccProvider.Meta().(*Client).libvirt

		rs, err := getResourceFromTerraformState(volumeName, state)
		if err != nil {
			return err
		}

		cloudInitDiskDef, err := newCloudInitDefFromRemoteISO(virConn, rs.Primary.ID)

		if cloudInitDiskDef.MetaData != expected.MetaData {
			return fmt.Errorf("metadata '%s' content differs from expected Metadata %s", cloudInitDiskDef.MetaData, expected.MetaData)
		}
		if cloudInitDiskDef.UserData != expected.UserData {
			return fmt.Errorf("userdata '%s' content differs from expected UserData  %s", cloudInitDiskDef.UserData, expected.UserData)
		}
		if cloudInitDiskDef.NetworkConfig != expected.NetworkConfig {
			return fmt.Errorf("networkconfig '%s' content differs from expected NetworkConfigData %s", cloudInitDiskDef.NetworkConfig, expected.NetworkConfig)
		}
		return nil
	}
}
<commit_msg>missing error check<commit_after>package libvirt

import (
	"fmt"
	"testing"

	libvirt "github.com/digitalocean/go-libvirt"
	"github.com/hashicorp/terraform-plugin-sdk/helper/acctest"
	"github.com/hashicorp/terraform-plugin-sdk/helper/resource"
	"github.com/hashicorp/terraform-plugin-sdk/terraform"
)

func TestAccLibvirtCloudInit_CreateCloudInitDiskAndUpdate(t *testing.T) {
	var volume libvirt.StorageVol
	randomResourceName := acctest.RandStringFromCharSet(10, acctest.CharSetAlpha)
	randomPoolName := acctest.RandStringFromCharSet(10, acctest.CharSetAlpha)
	randomPoolPath := "/tmp/terraform-provider-libvirt-pool-" + randomPoolName
	// this structs are contents values we expect.
	expectedContents := Expected{UserData: "#cloud-config", NetworkConfig: "network:", MetaData: "instance-id: bamboo"}
	expectedContents2 := Expected{UserData: "#cloud-config2", NetworkConfig: "network2:", MetaData: "instance-id: bamboo2"}
	expectedContentsEmpty := Expected{UserData: "#cloud-config2", NetworkConfig: "", MetaData: ""}
	randomIsoName := acctest.RandStringFromCharSet(10, acctest.CharSetAlpha) + ".iso"
	resource.Test(t, resource.TestCase{
		PreCheck:  func() { testAccPreCheck(t) },
		Providers: testAccProviders,
		CheckDestroy: func(s *terraform.State) error {
			return nil
		},
		Steps: []resource.TestStep{
			{
				Config: fmt.Sprintf(`
					resource "libvirt_pool" "%s" {
								name = "%s"
								type = "dir"
								path = "%s"
                            }
					resource "libvirt_cloudinit_disk" "%s" {
								name           = "%s"
								user_data      = "#cloud-config"
								meta_data      = "instance-id: bamboo"
								network_config = "network:"
                                pool           = "${libvirt_pool.%s.name}"
							}`, randomPoolName, randomPoolName, randomPoolPath, randomResourceName, randomIsoName, randomPoolName),

				Check: resource.ComposeTestCheckFunc(
					resource.TestCheckResourceAttr(
						"libvirt_cloudinit_disk."+randomResourceName, "name", randomIsoName),
					testAccCheckCloudInitVolumeExists("libvirt_cloudinit_disk."+randomResourceName, &volume),
					expectedContents.testAccCheckCloudInitDiskFilesContent("libvirt_cloudinit_disk."+randomResourceName, &volume),
				),
			},
			{
				Config: fmt.Sprintf(`
					resource "libvirt_pool" "%s" {
								name = "%s"
								type = "dir"
								path = "%s"
                            }
					resource "libvirt_cloudinit_disk" "%s" {
								name           = "%s"
								user_data      = "#cloud-config2"
								meta_data = "instance-id: bamboo2"
								network_config = "network2:"
                                pool           = "${libvirt_pool.%s.name}"
							}`, randomPoolName, randomPoolName, randomPoolPath, randomResourceName, randomIsoName, randomPoolName),

				Check: resource.ComposeTestCheckFunc(
					resource.TestCheckResourceAttr(
						"libvirt_cloudinit_disk."+randomResourceName, "name", randomIsoName),
					testAccCheckCloudInitVolumeExists("libvirt_cloudinit_disk."+randomResourceName, &volume),
					expectedContents2.testAccCheckCloudInitDiskFilesContent("libvirt_cloudinit_disk."+randomResourceName, &volume),
				),
			},
			{
				Config: fmt.Sprintf(`
					resource "libvirt_pool" "%s" {
								name = "%s"
								type = "dir"
								path = "%s"
                            }
					resource "libvirt_cloudinit_disk" "%s" {
								name           = "%s"
								user_data      = "#cloud-config2"
                                pool           = "${libvirt_pool.%s.name}"
							}`, randomPoolName, randomPoolName, randomPoolPath, randomResourceName, randomIsoName, randomPoolName),
				Check: resource.ComposeTestCheckFunc(
					resource.TestCheckResourceAttr(
						"libvirt_cloudinit_disk."+randomResourceName, "name", randomIsoName),
					testAccCheckCloudInitVolumeExists("libvirt_cloudinit_disk."+randomResourceName, &volume),
					expectedContentsEmpty.testAccCheckCloudInitDiskFilesContent("libvirt_cloudinit_disk."+randomResourceName, &volume),
				),
			},
			// when we apply 2 times with same conf, we should not have a diff. See bug:
			// https://github.com/dmacvicar/terraform-provider-libvirt/issues/313
			{
				Config: fmt.Sprintf(`
                        resource "libvirt_pool" "%s" {
								    name = "%s"
                                    type = "dir"
                                    path = "%s"
                                }
						resource "libvirt_cloudinit_disk" "%s" {
									name           = "%s"
									user_data      = "#cloud-config4"
                                    pool           = "${libvirt_pool.%s.name}"
								}`, randomPoolName, randomPoolName, randomPoolPath, randomResourceName, randomIsoName, randomPoolName),
				ExpectNonEmptyPlan: true,
				PlanOnly:           true,
				Check: resource.ComposeTestCheckFunc(
					resource.TestCheckResourceAttr(
						"libvirt_cloudinit_disk."+randomResourceName, "name", randomIsoName),
					testAccCheckCloudInitVolumeExists("libvirt_cloudinit_disk."+randomResourceName, &volume),
					expectedContentsEmpty.testAccCheckCloudInitDiskFilesContent("libvirt_cloudinit_disk."+randomResourceName, &volume),
				),
			},
		},
	})
}

// The destroy function should always handle the case where the resource might already be destroyed
// (manually, for example). If the resource is already destroyed, this should not return an error.
// This allows Terraform users to manually delete resources without breaking Terraform.
// This test should fail without a proper "Exists" implementation
func TestAccLibvirtCloudInit_ManuallyDestroyed(t *testing.T) {
	var volume libvirt.StorageVol
	randomResourceName := acctest.RandStringFromCharSet(10, acctest.CharSetAlpha)
	randomPoolName := acctest.RandStringFromCharSet(10, acctest.CharSetAlpha)
	randomPoolPath := "/tmp/terraform-provider-libvirt-pool-" + randomPoolName

	testAccCheckLibvirtCloudInitConfigBasic := fmt.Sprintf(`
        resource "libvirt_pool" "%s" {
            name = "%s"
            type = "dir"
            path = "%s"
        }
        resource "libvirt_cloudinit_disk" "%s" {
            name           = "%s"
            pool           = "${libvirt_pool.%s.name}"
			user_data      = "#cloud-config\nssh_authorized_keys: []\n"
		}`, randomPoolName, randomPoolName, randomPoolPath, randomResourceName, randomResourceName, randomPoolName)

	resource.Test(t, resource.TestCase{
		PreCheck:  func() { testAccPreCheck(t) },
		Providers: testAccProviders,
		CheckDestroy: func(s *terraform.State) error {
			return nil
		},
		Steps: []resource.TestStep{
			{
				Config: testAccCheckLibvirtCloudInitConfigBasic,
				Check: resource.ComposeTestCheckFunc(
					testAccCheckCloudInitVolumeExists("libvirt_cloudinit_disk."+randomResourceName, &volume),
				),
			},
			{
				Config:  testAccCheckLibvirtCloudInitConfigBasic,
				Destroy: true,
				PreConfig: func() {
					client := testAccProvider.Meta().(*Client)
					if volume.Key == "" {
						t.Fatalf("Key is blank")
					}
					if err := volumeDelete(client, volume.Key); err != nil {
						t.Fatal(err)
					}
				},
			},
		},
	})
}

func testAccCheckCloudInitVolumeExists(volumeName string, volume *libvirt.StorageVol) resource.TestCheckFunc {
	return func(state *terraform.State) error {
		virConn := testAccProvider.Meta().(*Client).libvirt

		rs, err := getResourceFromTerraformState(volumeName, state)
		if err != nil {
			return err
		}

		cikey, err := getCloudInitVolumeKeyFromTerraformID(rs.Primary.ID)
		if err != nil {
			return err
		}

		retrievedVol, err := virConn.StorageVolLookupByKey(cikey)
		if err != nil {
			return err
		}

		if retrievedVol.Key == "" {
			return fmt.Errorf("UUID is blank")
		}

		if retrievedVol.Key != cikey {
			fmt.Printf("retrievedVol.Key is: %s \ncloudinit key is %s", retrievedVol.Key, cikey)
			return fmt.Errorf("Resource ID and cloudinit volume key does not match")
		}

		*volume = retrievedVol

		return nil
	}
}

// this is helper method for test expected values
type Expected struct {
	UserData, NetworkConfig, MetaData string
}

func (expected *Expected) testAccCheckCloudInitDiskFilesContent(volumeName string, volume *libvirt.StorageVol) resource.TestCheckFunc {
	return func(state *terraform.State) error {
		virConn := testAccProvider.Meta().(*Client).libvirt

		rs, err := getResourceFromTerraformState(volumeName, state)
		if err != nil {
			return err
		}

		cloudInitDiskDef, err := newCloudInitDefFromRemoteISO(virConn, rs.Primary.ID)
		if err != nil {
			return err
		}

		if cloudInitDiskDef.MetaData != expected.MetaData {
			return fmt.Errorf("metadata '%s' content differs from expected Metadata %s", cloudInitDiskDef.MetaData, expected.MetaData)
		}
		if cloudInitDiskDef.UserData != expected.UserData {
			return fmt.Errorf("userdata '%s' content differs from expected UserData  %s", cloudInitDiskDef.UserData, expected.UserData)
		}
		if cloudInitDiskDef.NetworkConfig != expected.NetworkConfig {
			return fmt.Errorf("networkconfig '%s' content differs from expected NetworkConfigData %s", cloudInitDiskDef.NetworkConfig, expected.NetworkConfig)
		}
		return nil
	}
}
<|endoftext|>