<commit_before>package client_test

import (
	. "github.com/emc-advanced-dev/unik/pkg/client"

	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
	"github.com/emc-advanced-dev/unik/test/helpers"
	"github.com/Sirupsen/logrus"
	"github.com/emc-advanced-dev/unik/pkg/types"
	"strings"
	"github.com/onsi/ginkgo/extensions/table"
	"fmt"
)

const (
	example_cpp_includeos = "example-cpp-includeos"
	example_go_httpd = "example_go_httpd"
	example_godeps_go_app = "example_godeps_go_app"
	example_go_nontrivial = "example-go-nontrivial"
	example_nodejs_app = "example-nodejs-app"
	example_java_project = "example_java_project"
	example_python_project = "example-python3-httpd"
)

var _ = Describe("Instances", func() {
	daemonUrl := "127.0.0.1:3000"
	var c = UnikClient(daemonUrl)
	Describe("instances", func() {
		Describe("All()", func() {
			var image *types.Image
			var volume *types.Volume
			AfterEach(func() {
				if image != nil {
					c.Images().Delete(image.Id, true)
				}
				if volume != nil {
					c.Volumes().Delete(volume.Id, true)
				}
			})
			Context("no instances exist", func() {
				if len(cfg.Providers.Virtualbox) > 0 && len(cfg.Providers.Vsphere) < 1 ||
				len(cfg.Providers.Virtualbox) < 1 && len(cfg.Providers.Vsphere) > 0 {
					Context("on virtualbox or vsphere provider", func() {
						It("returns a list with only the Instance Listener VM", func() {
							instances, err := c.Instances().All()
							Expect(err).NotTo(HaveOccurred())
							Expect(instances).To(HaveLen(1))
							Expect(instances[0].Name).To(ContainSubstring("Listener"))
						})

					})
				} else if len(cfg.Providers.Virtualbox) > 0 && len(cfg.Providers.Vsphere) > 0 {
					Context("on virtualbox and vsphere providers", func() {
						It("returns a list with only the Instance Listener VMs", func() {
							instances, err := c.Instances().All()
							Expect(err).NotTo(HaveOccurred())
							Expect(instances).To(HaveLen(2))
							Expect(instances[0].Name).To(ContainSubstring("Listener"))
							Expect(instances[1].Name).To(ContainSubstring("Listener"))
						})

					})
				} else if len(cfg.Providers.Virtualbox) < 1 && len(cfg.Providers.Vsphere) < 1 {
					It("returns an empty list", func() {
						instances, err := c.Instances().All()
						Expect(err).NotTo(HaveOccurred())
						Expect(instances).To(BeEmpty())
					})
				}
			})
			Context("instances exist", func() {
				Describe("Run()", func() {
					imageNames := []string{
						example_nodejs_app,
						example_go_httpd,
						example_godeps_go_app,
						example_java_project,
						example_python_project,
						example_go_nontrivial,
					}
					providers := []string{}
					entries := []table.TableEntry{}
					if len(cfg.Providers.Virtualbox) > 0 {
						providers = append(providers, "virtualbox")
						entries = append(entries, table.Entry(example_cpp_includeos, example_cpp_includeos, false, "virtualbox"))
					}
					if len(cfg.Providers.Aws) > 0 {
						providers = append(providers, "aws")
					}
					if len(cfg.Providers.Vsphere) > 0 {
						providers = append(providers, "vsphere")
					}
					if len(cfg.Providers.Qemu) > 0 {
						entries = append(entries, table.Entry(example_go_httpd, example_go_httpd, true, "qemu"))
						entries = append(entries, table.Entry(example_godeps_go_app, example_godeps_go_app, true, "qemu"))
					}
					for _, imageName := range imageNames {
						for _, provider := range providers {
							entries = append(entries, table.Entry(imageName, imageName, false, provider))
							entries = append(entries, table.Entry(imageName, imageName, true, provider))
						}
					}
					logrus.WithField("entries", entries).WithField("imageNames", imageNames).WithField("providers", providers).Infof("ENTRIES TO TEST")
					Context("Build() then Run()", func() {
						table.DescribeTable("running images", func(imageName string, withVolume bool, provider string) {
							compiler := ""
							switch {
							case strings.Contains(imageName, "includeos"):
								logrus.Infof("found image type IncludeOS: %s", imageName)
								compiler = fmt.Sprintf("includeos-cpp-%s", provider)
								break
							case strings.Contains(imageName, "go"):
								logrus.Infof("found image type GO: %s", imageName)
								compiler = fmt.Sprintf("rump-go-%s", provider)
								break
							case strings.Contains(imageName, "nodejs"):
								logrus.Infof("found image type NODE: %s", imageName)
								compiler = fmt.Sprintf("rump-nodejs-%s", provider)
								break
							case strings.Contains(imageName, "java"):
								logrus.Infof("found image type JAVA: %s", imageName)
								compiler = fmt.Sprintf("osv-java-%s", provider)
								break
							default:
								logrus.Panic("unknown image name " + imageName)
							}
							//vsphere -> vmware for compilers
							compiler = strings.Replace(compiler, "vsphere", "vmware", -1)
							if !withVolume {
								Context("with no volume", func() {
									mounts := []string{}
									var err error
									image, err = helpers.BuildExampleImage(daemonUrl, imageName, compiler, provider, mounts)
									Expect(err).ToNot(HaveOccurred())
									instanceName := imageName
									volsToMounts := map[string]string{}
									instance, err := helpers.RunExampleInstance(daemonUrl, instanceName, image.Name, volsToMounts)
									Expect(err).ToNot(HaveOccurred())
									instances, err := c.Instances().All()
									Expect(err).NotTo(HaveOccurred())
									//instance state shoule be Running
									instance.State = types.InstanceState_Running
									//ip may not have been set at Run() call, ignore it on assert
									if instance.IpAddress == "" {
										for _, instance := range instances {
											instance.IpAddress = ""
											if instance.State != types.InstanceState_Running && provider == "aws" {
												logrus.Warnf("instance state is %s, not running. setting to running so tests pass", instance.State)
												instance.State = types.InstanceState_Running
											}
										}
									}
									Expect(instances).To(ContainElement(instance))
								})
							} else {
								Context("with volume", func() {
									mounts := []string{"/volume"}
									var err error
									image, err = helpers.BuildExampleImage(daemonUrl, imageName, compiler, provider, mounts)
									Expect(err).ToNot(HaveOccurred())
									volume, err = helpers.CreateExampleVolume(daemonUrl, "test_volume_" + imageName, provider, 15)
									Expect(err).ToNot(HaveOccurred())
									instanceName := imageName
									noCleanup := false
									env := map[string]string{"FOO": "BAR"}
									memoryMb := 128
									mountPointsToVols := map[string]string{"/volume": volume.Id}
									instance, err := c.Instances().Run(instanceName, image.Name, mountPointsToVols, env, memoryMb, noCleanup, false)
									Expect(err).ToNot(HaveOccurred())
									instances, err := c.Instances().All()
									Expect(err).NotTo(HaveOccurred())
									//instance state shoule be Running
									instance.State = types.InstanceState_Running
									//ip may not have been set at Run() call, ignore it on assert
									if instance.IpAddress == "" {
										for _, instance := range instances {
											instance.IpAddress = ""
											if instance.State != types.InstanceState_Running && provider == "aws" {
												logrus.Warnf("instance state is %s, not running. setting to running so tests pass", instance.State)
												instance.State = types.InstanceState_Running
											}
										}
									}
									Expect(instances).To(ContainElement(instance))
								})
							}
						}, entries...)
					})
				})
			})
		})
	})
})
<commit_msg>qemu test for includeos<commit_after>package client_test

import (
	. "github.com/emc-advanced-dev/unik/pkg/client"

	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
	"github.com/emc-advanced-dev/unik/test/helpers"
	"github.com/Sirupsen/logrus"
	"github.com/emc-advanced-dev/unik/pkg/types"
	"strings"
	"github.com/onsi/ginkgo/extensions/table"
	"fmt"
)

const (
	example_cpp_includeos = "example-cpp-includeos"
	example_go_httpd = "example_go_httpd"
	example_godeps_go_app = "example_godeps_go_app"
	example_go_nontrivial = "example-go-nontrivial"
	example_nodejs_app = "example-nodejs-app"
	example_java_project = "example_java_project"
	example_python_project = "example-python3-httpd"
)

var _ = Describe("Instances", func() {
	daemonUrl := "127.0.0.1:3000"
	var c = UnikClient(daemonUrl)
	Describe("instances", func() {
		Describe("All()", func() {
			var image *types.Image
			var volume *types.Volume
			AfterEach(func() {
				if image != nil {
					c.Images().Delete(image.Id, true)
				}
				if volume != nil {
					c.Volumes().Delete(volume.Id, true)
				}
			})
			Context("no instances exist", func() {
				if len(cfg.Providers.Virtualbox) > 0 && len(cfg.Providers.Vsphere) < 1 ||
				len(cfg.Providers.Virtualbox) < 1 && len(cfg.Providers.Vsphere) > 0 {
					Context("on virtualbox or vsphere provider", func() {
						It("returns a list with only the Instance Listener VM", func() {
							instances, err := c.Instances().All()
							Expect(err).NotTo(HaveOccurred())
							Expect(instances).To(HaveLen(1))
							Expect(instances[0].Name).To(ContainSubstring("Listener"))
						})

					})
				} else if len(cfg.Providers.Virtualbox) > 0 && len(cfg.Providers.Vsphere) > 0 {
					Context("on virtualbox and vsphere providers", func() {
						It("returns a list with only the Instance Listener VMs", func() {
							instances, err := c.Instances().All()
							Expect(err).NotTo(HaveOccurred())
							Expect(instances).To(HaveLen(2))
							Expect(instances[0].Name).To(ContainSubstring("Listener"))
							Expect(instances[1].Name).To(ContainSubstring("Listener"))
						})

					})
				} else if len(cfg.Providers.Virtualbox) < 1 && len(cfg.Providers.Vsphere) < 1 {
					It("returns an empty list", func() {
						instances, err := c.Instances().All()
						Expect(err).NotTo(HaveOccurred())
						Expect(instances).To(BeEmpty())
					})
				}
			})
			Context("instances exist", func() {
				Describe("Run()", func() {
					imageNames := []string{
						example_nodejs_app,
						example_go_httpd,
						example_godeps_go_app,
						example_java_project,
						example_python_project,
						example_go_nontrivial,
					}
					providers := []string{}
					entries := []table.TableEntry{}
					if len(cfg.Providers.Virtualbox) > 0 {
						providers = append(providers, "virtualbox")
						entries = append(entries, table.Entry(example_cpp_includeos, example_cpp_includeos, false, "virtualbox"))
					}
					if len(cfg.Providers.Aws) > 0 {
						providers = append(providers, "aws")
					}
					if len(cfg.Providers.Vsphere) > 0 {
						providers = append(providers, "vsphere")
					}
					if len(cfg.Providers.Qemu) > 0 {
						entries = append(entries, table.Entry(example_go_httpd, example_go_httpd, true, "qemu"))
						entries = append(entries, table.Entry(example_godeps_go_app, example_godeps_go_app, true, "qemu"))
						entries = append(entries, table.Entry(example_cpp_includeos, example_cpp_includeos, false, "qemu"))
					}
					for _, imageName := range imageNames {
						for _, provider := range providers {
							entries = append(entries, table.Entry(imageName, imageName, false, provider))
							entries = append(entries, table.Entry(imageName, imageName, true, provider))
						}
					}
					logrus.WithField("entries", entries).WithField("imageNames", imageNames).WithField("providers", providers).Infof("ENTRIES TO TEST")
					Context("Build() then Run()", func() {
						table.DescribeTable("running images", func(imageName string, withVolume bool, provider string) {
							compiler := ""
							switch {
							case strings.Contains(imageName, "includeos"):
								logrus.Infof("found image type IncludeOS: %s", imageName)
								compiler = fmt.Sprintf("includeos-cpp-%s", provider)
								break
							case strings.Contains(imageName, "go"):
								logrus.Infof("found image type GO: %s", imageName)
								compiler = fmt.Sprintf("rump-go-%s", provider)
								break
							case strings.Contains(imageName, "nodejs"):
								logrus.Infof("found image type NODE: %s", imageName)
								compiler = fmt.Sprintf("rump-nodejs-%s", provider)
								break
							case strings.Contains(imageName, "java"):
								logrus.Infof("found image type JAVA: %s", imageName)
								compiler = fmt.Sprintf("osv-java-%s", provider)
								break
							default:
								logrus.Panic("unknown image name " + imageName)
							}
							//vsphere -> vmware for compilers
							compiler = strings.Replace(compiler, "vsphere", "vmware", -1)
							if !withVolume {
								Context("with no volume", func() {
									mounts := []string{}
									var err error
									image, err = helpers.BuildExampleImage(daemonUrl, imageName, compiler, provider, mounts)
									Expect(err).ToNot(HaveOccurred())
									instanceName := imageName
									volsToMounts := map[string]string{}
									instance, err := helpers.RunExampleInstance(daemonUrl, instanceName, image.Name, volsToMounts)
									Expect(err).ToNot(HaveOccurred())
									instances, err := c.Instances().All()
									Expect(err).NotTo(HaveOccurred())
									//instance state shoule be Running
									instance.State = types.InstanceState_Running
									//ip may not have been set at Run() call, ignore it on assert
									if instance.IpAddress == "" {
										for _, instance := range instances {
											instance.IpAddress = ""
											if instance.State != types.InstanceState_Running && provider == "aws" {
												logrus.Warnf("instance state is %s, not running. setting to running so tests pass", instance.State)
												instance.State = types.InstanceState_Running
											}
										}
									}
									Expect(instances).To(ContainElement(instance))
								})
							} else {
								Context("with volume", func() {
									mounts := []string{"/volume"}
									var err error
									image, err = helpers.BuildExampleImage(daemonUrl, imageName, compiler, provider, mounts)
									Expect(err).ToNot(HaveOccurred())
									volume, err = helpers.CreateExampleVolume(daemonUrl, "test_volume_" + imageName, provider, 15)
									Expect(err).ToNot(HaveOccurred())
									instanceName := imageName
									noCleanup := false
									env := map[string]string{"FOO": "BAR"}
									memoryMb := 128
									mountPointsToVols := map[string]string{"/volume": volume.Id}
									instance, err := c.Instances().Run(instanceName, image.Name, mountPointsToVols, env, memoryMb, noCleanup, false)
									Expect(err).ToNot(HaveOccurred())
									instances, err := c.Instances().All()
									Expect(err).NotTo(HaveOccurred())
									//instance state shoule be Running
									instance.State = types.InstanceState_Running
									//ip may not have been set at Run() call, ignore it on assert
									if instance.IpAddress == "" {
										for _, instance := range instances {
											instance.IpAddress = ""
											if instance.State != types.InstanceState_Running && provider == "aws" {
												logrus.Warnf("instance state is %s, not running. setting to running so tests pass", instance.State)
												instance.State = types.InstanceState_Running
											}
										}
									}
									Expect(instances).To(ContainElement(instance))
								})
							}
						}, entries...)
					})
				})
			})
		})
	})
})
<|endoftext|>


<commit_before>package scout

import (
	"context"
	"os"
	"path/filepath"
	"runtime"
	"strings"
	"time"

	"github.com/google/uuid"

	"github.com/datawire/ambassador/v2/pkg/metriton"
	"github.com/datawire/dlib/dcontext"
	"github.com/datawire/dlib/dlog"
	"github.com/telepresenceio/telepresence/v2/pkg/client"
	"github.com/telepresenceio/telepresence/v2/pkg/client/userd/auth/authdata"
	"github.com/telepresenceio/telepresence/v2/pkg/filelocation"
)

// Environment variable prefix for additional metadata to be reported
const environmentMetadataPrefix = "TELEPRESENCE_REPORT_"

type bufEntry struct {
	action  string
	entries []Entry
}

// Reporter is a Metriton reported
type Reporter struct {
	index    int
	buffer   chan bufEntry
	done     chan struct{}
	reporter *metriton.Reporter
}

// Entry is a key/value association used when reporting
type Entry struct {
	Key   string
	Value interface{}
}

// getInstallIDFromFilesystem returns the telepresence install ID, and also sets the reporter base
// metadata to include any conflicting install IDs written by old versions of the product.
func getInstallIDFromFilesystem(ctx context.Context, reporter *metriton.Reporter) (string, error) {
	type filecacheEntry struct {
		Body string
		Err  error
	}
	filecache := make(map[string]filecacheEntry)
	readFile := func(filename string) (string, error) {
		if _, isCached := filecache[filename]; !isCached {
			bs, err := os.ReadFile(filename)
			filecache[filename] = filecacheEntry{
				Body: string(bs),
				Err:  err,
			}
		}
		return filecache[filename].Body, filecache[filename].Err
	}

	// Do these in order of precedence when there are multiple install IDs.
	var retID string
	allIDs := make(map[string]string)

	if runtime.GOOS != "windows" { // won't find any legacy on Windows
		// We'll use this (and justify overriding GOOS=linux) below.
		xdgConfigHome, err := filelocation.UserConfigDir(filelocation.WithGOOS(ctx, "linux"))
		if err == nil {
			// Similarly to Telepresence-1 (below), edgectl always used the XDG filepath, but unlike
			// Telepresence-1 it did obey $XDG_CONFIG_HOME.
			if id, err := readFile(filepath.Join(xdgConfigHome, "edgectl", "id")); err == nil {
				allIDs["edgectl"] = id
				retID = id
			}
		}

		// Telepresence-1 used "$HOME/.config/telepresence/id" always, even on macOS (where ~/.config
		// isn't a thing) or when $XDG_CONFIG_HOME is something different than "$HOME/.config".
		if homeDir, err := filelocation.UserHomeDir(ctx); err == nil {
			if id, err := readFile(filepath.Join(homeDir, ".config", "telepresence", "id")); err == nil {
				allIDs["telepresence-1"] = id
				retID = id
			}
		}

		// Telepresence-2 prior to 2.1.0 did the exact same thing as edgectl, but with
		// "telepresence2" in the path instead of "edgectl".
		if id, err := readFile(filepath.Join(xdgConfigHome, "telepresence2", "id")); err == nil {
			allIDs["telepresence-2<2.1"] = id
			retID = id
		}
	}

	// Current.  Telepresence-2 now uses the most appropriate directory for the platform, and
	// uses "telepresence" instead of "telepresence2".  On GOOS=linux this is probably
	// (depending on how $XDG_CONFIG_HOME is set) the same as the Telepresence 1 location.
	telConfigDir, err := filelocation.AppUserConfigDir(ctx)
	if err != nil {
		return "", err
	}
	idFilename := filepath.Join(telConfigDir, "id")
	if id, err := readFile(idFilename); err != nil {
		if !os.IsNotExist(err) {
			return "", err
		}
	} else {
		allIDs["telepresence-2"] = id
		retID = id
	}

	// OK, now process all of that.

	if len(allIDs) == 0 {
		retID = uuid.New().String()
	}

	if allIDs["telepresence-2"] != retID {
		if err := os.MkdirAll(filepath.Dir(idFilename), 0755); err != nil {
			return "", err
		}
		if err := os.WriteFile(idFilename, []byte(retID), 0644); err != nil {
			return "", err
		}
	}

	reporter.BaseMetadata["new_install"] = len(allIDs) == 0
	for product, id := range allIDs {
		if id != retID {
			reporter.BaseMetadata["install_id_"+product] = id
		}
	}
	return retID, nil
}

// bufferSize is the max number of entries that can be stored in the buffer
// before entries are discarded.
const bufferSize = 40

// NewReporter creates a new initialized Reporter instance that can be used to
// send telepresence reports to Metriton
func NewReporter(ctx context.Context, mode string) *Reporter {
	r := &Reporter{
		reporter: &metriton.Reporter{
			Application: "telepresence2",
			Version:     client.Version(),
			GetInstallID: func(r *metriton.Reporter) (string, error) {
				id, err := getInstallIDFromFilesystem(ctx, r)
				if err != nil {
					id = "00000000-0000-0000-0000-000000000000"
					r.BaseMetadata["new_install"] = true
					r.BaseMetadata["install_id_error"] = err.Error()
				}
				return id, nil
			},
		},
	}
	r.initialize(ctx, mode, runtime.GOOS, runtime.GOARCH)
	return r
}

// initialization broken out or constructor for the benefit of testing
func (r *Reporter) initialize(ctx context.Context, mode, goos, goarch string) {
	r.buffer = make(chan bufEntry, bufferSize)
	r.done = make(chan struct{})

	// Fixed (growing) metadata passed with every report
	baseMeta := getOsMetadata(ctx)
	baseMeta["mode"] = mode
	baseMeta["trace_id"] = uuid.New()
	baseMeta["goos"] = goos
	baseMeta["goarch"] = goarch

	// Discover how Telepresence was installed based on the binary's location
	installMethod, err := client.GetInstallMechanism()
	if err != nil {
		dlog.Errorf(ctx, "scout error getting executable: %s", err)
	}
	baseMeta["install_method"] = installMethod
	for k, v := range getDefaultEnvironmentMetadata() {
		baseMeta[k] = v
	}
	r.reporter.BaseMetadata = baseMeta
}

func (r *Reporter) InstallID() string {
	return r.reporter.InstallID()
}

const setMetadatumAction = "__set_metadatum__"

// SetMetadatum associates the given key with the given value in the metadata
// of this instance.
func (r *Reporter) SetMetadatum(ctx context.Context, key string, value interface{}) {
	r.Report(ctx, setMetadatumAction, Entry{Key: key, Value: value})
}

// Start starts the instance in a goroutine
func (r *Reporter) Start(ctx context.Context) {
	go func() {
		if err := r.Run(ctx); err != nil {
			dlog.Error(ctx, err)
		}
	}()
}

func (r *Reporter) Close() {
	// Send a zeroed bufEntry here instead of closing the buffer so that
	// any stray Reports that arrive after the context is cancelled aren't
	// sent on a closed channel
	select {
	case r.buffer <- bufEntry{}:
	default:
	}
	// Wait for the done channel to close. Give up after 3 seconds (that
	// should be plenty)
	select {
	case <-r.done:
	case <-time.After(3 * time.Second):
	}
}

// Run ensures that all reports on the send queue are sent to the endpoint
func (r *Reporter) Run(ctx context.Context) error {
	go func() {
		// Close buffer and let it drain when ctx is done.
		<-ctx.Done()

		// Send a zeroed bufEntry here instead of closing the buffer so that
		// any stray Reports that arrive after the context is cancelled aren't
		// sent on a closed channel
		select {
		case r.buffer <- bufEntry{}:
		default:
		}
	}()

	defer close(r.done)

	hc := dcontext.HardContext(ctx)
	for be := range r.buffer {
		switch be.action {
		case "":
			return nil
		case setMetadatumAction:
			entry := be.entries[0]
			if entry.Value == "" {
				delete(r.reporter.BaseMetadata, entry.Key)
			} else {
				r.reporter.BaseMetadata[entry.Key] = entry.Value
			}
		default:
			r.doReport(hc, &be)
		}
	}
	return nil
}

// Report constructs and buffers a report on the send queue. It includes the fixed (growing)
// set of metadata in the Reporter structure and the entries passed as arguments to this
// call. It also includes and increments the index, which can be used to
// determine the correct order of reported events for this installation
// attempt (correlated by the trace_id set at the start).
func (r *Reporter) Report(ctx context.Context, action string, entries ...Entry) {
	select {
	case r.buffer <- bufEntry{action, entries}:
	default:
		dlog.Infof(ctx, "scout report %q discarded. Output buffer is full (or closed)", action)
	}
}

func (r *Reporter) doReport(ctx context.Context, be *bufEntry) {
	r.index++
	metadata := make(map[string]interface{}, 4+len(be.entries))
	metadata["action"] = be.action
	metadata["index"] = r.index
	userInfo, err := authdata.LoadUserInfoFromUserCache(ctx)
	if err == nil && userInfo.Id != "" {
		metadata["user_id"] = userInfo.Id
		metadata["account_id"] = userInfo.AccountId
	}
	for _, metaItem := range be.entries {
		metadata[metaItem.Key] = metaItem.Value
	}

	_, err = r.reporter.Report(ctx, metadata)
	if err != nil && ctx.Err() == nil {
		dlog.Infof(ctx, "scout report %q failed: %v", be.action, err)
	}
}

// Returns a metadata map containing all the additional environment variables to be reported
func getDefaultEnvironmentMetadata() map[string]string {
	metadata := map[string]string{}
	for _, e := range os.Environ() {
		pair := strings.SplitN(e, "=", 2)
		if strings.HasPrefix(pair[0], environmentMetadataPrefix) {
			key := strings.ToLower(strings.TrimPrefix(pair[0], environmentMetadataPrefix))
			metadata[key] = pair[1]
		}
	}
	return metadata
}
<commit_msg>Allow scout reporters for multiple install ids<commit_after>package scout

import (
	"context"
	"os"
	"path/filepath"
	"runtime"
	"strings"
	"time"

	"github.com/google/uuid"

	"github.com/datawire/ambassador/v2/pkg/metriton"
	"github.com/datawire/dlib/dcontext"
	"github.com/datawire/dlib/dlog"
	"github.com/telepresenceio/telepresence/v2/pkg/client"
	"github.com/telepresenceio/telepresence/v2/pkg/client/userd/auth/authdata"
	"github.com/telepresenceio/telepresence/v2/pkg/filelocation"
)

// Environment variable prefix for additional metadata to be reported
const environmentMetadataPrefix = "TELEPRESENCE_REPORT_"

type bufEntry struct {
	action  string
	entries []Entry
}

// Reporter is a Metriton reported
type Reporter struct {
	index    int
	buffer   chan bufEntry
	done     chan struct{}
	reporter *metriton.Reporter
}

// Entry is a key/value association used when reporting
type Entry struct {
	Key   string
	Value interface{}
}

type InstallType string

const (
	CLI    InstallType = "cli"
	Docker InstallType = "docker"
)

var idFiles = map[InstallType]string{
	CLI:    "id",
	Docker: "docker_id",
}

// getInstallIDFromFilesystem returns the telepresence install ID, and also sets the reporter base
// metadata to include any conflicting install IDs written by old versions of the product.
func getInstallIDFromFilesystem(ctx context.Context, reporter *metriton.Reporter, installType InstallType) (string, error) {
	type filecacheEntry struct {
		Body string
		Err  error
	}
	filecache := make(map[string]filecacheEntry)
	readFile := func(filename string) (string, error) {
		if _, isCached := filecache[filename]; !isCached {
			bs, err := os.ReadFile(filename)
			filecache[filename] = filecacheEntry{
				Body: string(bs),
				Err:  err,
			}
		}
		return filecache[filename].Body, filecache[filename].Err
	}

	// Do these in order of precedence when there are multiple install IDs.
	var retID string
	allIDs := make(map[string]string)

	if runtime.GOOS != "windows" { // won't find any legacy on Windows
		// We'll use this (and justify overriding GOOS=linux) below.
		xdgConfigHome, err := filelocation.UserConfigDir(filelocation.WithGOOS(ctx, "linux"))
		if err == nil {
			// Similarly to Telepresence-1 (below), edgectl always used the XDG filepath, but unlike
			// Telepresence-1 it did obey $XDG_CONFIG_HOME.
			if id, err := readFile(filepath.Join(xdgConfigHome, "edgectl", "id")); err == nil {
				allIDs["edgectl"] = id
				retID = id
			}
		}

		// Telepresence-1 used "$HOME/.config/telepresence/id" always, even on macOS (where ~/.config
		// isn't a thing) or when $XDG_CONFIG_HOME is something different than "$HOME/.config".
		if homeDir, err := filelocation.UserHomeDir(ctx); err == nil {
			if id, err := readFile(filepath.Join(homeDir, ".config", "telepresence", "id")); err == nil {
				allIDs["telepresence-1"] = id
				retID = id
			}
		}

		// Telepresence-2 prior to 2.1.0 did the exact same thing as edgectl, but with
		// "telepresence2" in the path instead of "edgectl".
		if id, err := readFile(filepath.Join(xdgConfigHome, "telepresence2", "id")); err == nil {
			allIDs["telepresence-2<2.1"] = id
			retID = id
		}
	}

	// Current.  Telepresence-2 now uses the most appropriate directory for the platform, and
	// uses "telepresence" instead of "telepresence2".  On GOOS=linux this is probably
	// (depending on how $XDG_CONFIG_HOME is set) the same as the Telepresence 1 location.
	telConfigDir, err := filelocation.AppUserConfigDir(ctx)
	if err != nil {
		return "", err
	}
	idFilename := filepath.Join(telConfigDir, idFiles[installType])
	if id, err := readFile(idFilename); err != nil {
		if !os.IsNotExist(err) {
			return "", err
		}
	} else {
		allIDs["telepresence-2"] = id
		retID = id
	}

	// OK, now process all of that.

	if len(allIDs) == 0 {
		retID = uuid.New().String()
	}

	if allIDs["telepresence-2"] != retID {
		if err := os.MkdirAll(filepath.Dir(idFilename), 0755); err != nil {
			return "", err
		}
		if err := os.WriteFile(idFilename, []byte(retID), 0644); err != nil {
			return "", err
		}
	}

	reporter.BaseMetadata["new_install"] = len(allIDs) == 0

	// We don't want to add the extra ids until we've decided if it's a new install or not
	// this is because we'd like a new install of type A to be reported even if there's already
	// an existing install of type B
	for otherType, fileName := range idFiles {
		if otherType == installType {
			continue
		}
		idFilename := filepath.Join(telConfigDir, fileName)
		if id, err := readFile(idFilename); err != nil {
			if !os.IsNotExist(err) {
				return "", err
			}
		} else {
			allIDs["telepresence-2-"+string(otherType)] = id
		}
	}

	for product, id := range allIDs {
		if id != retID {
			reporter.BaseMetadata["install_id_"+product] = id
		}
	}
	return retID, nil
}

// bufferSize is the max number of entries that can be stored in the buffer
// before entries are discarded.
const bufferSize = 40

func NewReporterForInstallType(ctx context.Context, mode string, installType InstallType) *Reporter {
	r := &Reporter{
		reporter: &metriton.Reporter{
			Application: "telepresence2",
			Version:     client.Version(),
			GetInstallID: func(r *metriton.Reporter) (string, error) {
				id, err := getInstallIDFromFilesystem(ctx, r, installType)
				if err != nil {
					id = "00000000-0000-0000-0000-000000000000"
					r.BaseMetadata["new_install"] = true
					r.BaseMetadata["install_id_error"] = err.Error()
				}
				return id, nil
			},
		},
	}
	r.initialize(ctx, mode, runtime.GOOS, runtime.GOARCH)
	return r
}

// NewReporter creates a new initialized Reporter instance that can be used to
// send telepresence reports to Metriton
func NewReporter(ctx context.Context, mode string) *Reporter {
	return NewReporterForInstallType(ctx, mode, CLI)
}

// initialization broken out or constructor for the benefit of testing
func (r *Reporter) initialize(ctx context.Context, mode, goos, goarch string) {
	r.buffer = make(chan bufEntry, bufferSize)
	r.done = make(chan struct{})

	// Fixed (growing) metadata passed with every report
	baseMeta := getOsMetadata(ctx)
	baseMeta["mode"] = mode
	baseMeta["trace_id"] = uuid.New()
	baseMeta["goos"] = goos
	baseMeta["goarch"] = goarch

	// Discover how Telepresence was installed based on the binary's location
	installMethod, err := client.GetInstallMechanism()
	if err != nil {
		dlog.Errorf(ctx, "scout error getting executable: %s", err)
	}
	baseMeta["install_method"] = installMethod
	for k, v := range getDefaultEnvironmentMetadata() {
		baseMeta[k] = v
	}
	r.reporter.BaseMetadata = baseMeta
}

func (r *Reporter) InstallID() string {
	return r.reporter.InstallID()
}

const setMetadatumAction = "__set_metadatum__"

// SetMetadatum associates the given key with the given value in the metadata
// of this instance.
func (r *Reporter) SetMetadatum(ctx context.Context, key string, value interface{}) {
	r.Report(ctx, setMetadatumAction, Entry{Key: key, Value: value})
}

// Start starts the instance in a goroutine
func (r *Reporter) Start(ctx context.Context) {
	go func() {
		if err := r.Run(ctx); err != nil {
			dlog.Error(ctx, err)
		}
	}()
}

func (r *Reporter) Close() {
	// Send a zeroed bufEntry here instead of closing the buffer so that
	// any stray Reports that arrive after the context is cancelled aren't
	// sent on a closed channel
	select {
	case r.buffer <- bufEntry{}:
	default:
	}
	// Wait for the done channel to close. Give up after 3 seconds (that
	// should be plenty)
	select {
	case <-r.done:
	case <-time.After(3 * time.Second):
	}
}

// Run ensures that all reports on the send queue are sent to the endpoint
func (r *Reporter) Run(ctx context.Context) error {
	go func() {
		// Close buffer and let it drain when ctx is done.
		<-ctx.Done()

		// Send a zeroed bufEntry here instead of closing the buffer so that
		// any stray Reports that arrive after the context is cancelled aren't
		// sent on a closed channel
		select {
		case r.buffer <- bufEntry{}:
		default:
		}
	}()

	defer close(r.done)

	hc := dcontext.HardContext(ctx)
	for be := range r.buffer {
		switch be.action {
		case "":
			return nil
		case setMetadatumAction:
			entry := be.entries[0]
			if entry.Value == "" {
				delete(r.reporter.BaseMetadata, entry.Key)
			} else {
				r.reporter.BaseMetadata[entry.Key] = entry.Value
			}
		default:
			r.doReport(hc, &be)
		}
	}
	return nil
}

// Report constructs and buffers a report on the send queue. It includes the fixed (growing)
// set of metadata in the Reporter structure and the entries passed as arguments to this
// call. It also includes and increments the index, which can be used to
// determine the correct order of reported events for this installation
// attempt (correlated by the trace_id set at the start).
func (r *Reporter) Report(ctx context.Context, action string, entries ...Entry) {
	select {
	case r.buffer <- bufEntry{action, entries}:
	default:
		dlog.Infof(ctx, "scout report %q discarded. Output buffer is full (or closed)", action)
	}
}

func (r *Reporter) doReport(ctx context.Context, be *bufEntry) {
	r.index++
	metadata := make(map[string]interface{}, 4+len(be.entries))
	metadata["action"] = be.action
	metadata["index"] = r.index
	userInfo, err := authdata.LoadUserInfoFromUserCache(ctx)
	if err == nil && userInfo.Id != "" {
		metadata["user_id"] = userInfo.Id
		metadata["account_id"] = userInfo.AccountId
	}
	for _, metaItem := range be.entries {
		metadata[metaItem.Key] = metaItem.Value
	}

	_, err = r.reporter.Report(ctx, metadata)
	if err != nil && ctx.Err() == nil {
		dlog.Infof(ctx, "scout report %q failed: %v", be.action, err)
	}
}

// Returns a metadata map containing all the additional environment variables to be reported
func getDefaultEnvironmentMetadata() map[string]string {
	metadata := map[string]string{}
	for _, e := range os.Environ() {
		pair := strings.SplitN(e, "=", 2)
		if strings.HasPrefix(pair[0], environmentMetadataPrefix) {
			key := strings.ToLower(strings.TrimPrefix(pair[0], environmentMetadataPrefix))
			metadata[key] = pair[1]
		}
	}
	return metadata
}
<|endoftext|>


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

package config

import "github.com/google/exposure-notifications-verification-server/pkg/controller"

// FeatureConfig represents features that are introduced as off by default allowing
// for server operators to control their release.
type FeatureConfig struct {
	// EnableAuthenticatedSMS allows for realms to managed SMS specific signing keys,
	// and enable/disable this feature. There is no launch timeline for GA.
	// This should not be used without prior coordination with Apple/Google.
	EnableAuthenticatedSMS bool `env:"ENABLE_AUTHENTICATED_SMS, default=true"`

	// EnableUserReport allows for realms to enable user initiated diagnosis reporting,
	// via API + SMS.
	EnableUserReport bool `env:"ENABLE_USER_REPORT, default=false"`

	// EnableUserReportWeb will host a web page on the enx-redirect service
	// under a realm's domain IFF that realm has enabled user report and admin user report
	EnableUserReportWeb bool `env:"ENABLE_USER_REPORT_WEB, default=false"`

	// EnableAPIKeyLastUsedAt controls whether the UI will show the last_used_at
	// value for an API key.
	//
	// TODO(sethvargo): Move to true in 0.28.0+.
	EnableAPIKeyLastUsedAt bool `env:"ENABLE_API_KEY_LAST_USED_AT, default=false"`
}

// AddToTemplate takes TemplateMap and writes the status of all known
// feature flags for use in HTML templates.
func (f *FeatureConfig) AddToTemplate(m controller.TemplateMap) controller.TemplateMap {
	m["features"] = f
	return m
}
<commit_msg>Enable viewing API key usage by default (#2060)<commit_after>// Copyright 2021 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package config

import "github.com/google/exposure-notifications-verification-server/pkg/controller"

// FeatureConfig represents features that are introduced as off by default allowing
// for server operators to control their release.
type FeatureConfig struct {
	// EnableAuthenticatedSMS allows for realms to managed SMS specific signing keys,
	// and enable/disable this feature. There is no launch timeline for GA.
	// This should not be used without prior coordination with Apple/Google.
	EnableAuthenticatedSMS bool `env:"ENABLE_AUTHENTICATED_SMS, default=true"`

	// EnableUserReport allows for realms to enable user initiated diagnosis reporting,
	// via API + SMS.
	EnableUserReport bool `env:"ENABLE_USER_REPORT, default=false"`

	// EnableUserReportWeb will host a web page on the enx-redirect service
	// under a realm's domain IFF that realm has enabled user report and admin user report
	EnableUserReportWeb bool `env:"ENABLE_USER_REPORT_WEB, default=false"`

	// EnableAPIKeyLastUsedAt controls whether the UI will show the last_used_at
	// value for an API key.
	EnableAPIKeyLastUsedAt bool `env:"ENABLE_API_KEY_LAST_USED_AT, default=true"`
}

// AddToTemplate takes TemplateMap and writes the status of all known
// feature flags for use in HTML templates.
func (f *FeatureConfig) AddToTemplate(m controller.TemplateMap) controller.TemplateMap {
	m["features"] = f
	return m
}
<|endoftext|>


<commit_before>/*
Copyright 2017 The Kubernetes Authors.

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

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

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

package ipvs

import (
	"testing"

	utilipset "k8s.io/kubernetes/pkg/util/ipset"
	fakeipset "k8s.io/kubernetes/pkg/util/ipset/testing"
)

func TestCheckIPSetVersion(t *testing.T) {
	testCases := []struct {
		vstring string
		valid   bool
	}{
		// version less than "6.0" is not valid.
		{"4.0", false},
		{"5.1", false},
		{"5.1.2", false},
		// "7" is not a valid version string.
		{"7", false},
		{"6.0", true},
		{"6.1", true},
		{"6.19", true},
		{"7.0", true},
		{"8.1.2", true},
		{"9.3.4.0", true},
		{"total junk", false},
	}

	for i := range testCases {
		valid := checkMinVersion(testCases[i].vstring)
		if testCases[i].valid != valid {
			t.Errorf("Expected result: %v, Got result: %v", testCases[i].valid, valid)
		}
	}
}

const testIPSetVersion = "v6.19"

func TestSyncIPSetEntries(t *testing.T) {
	testCases := []struct {
		set             *utilipset.IPSet
		setType         utilipset.Type
		ipv6            bool
		activeEntries   []string
		currentEntries  []string
		expectedEntries []string
	}{
		{ // case 0
			set: &utilipset.IPSet{
				Name: "foo",
			},
			setType:         utilipset.HashIPPort,
			ipv6:            false,
			activeEntries:   []string{"172.17.0.4,tcp:80"},
			currentEntries:  nil,
			expectedEntries: []string{"172.17.0.4,tcp:80"},
		},
		{ // case 1
			set: &utilipset.IPSet{
				Name: "abz",
			},
			setType:         utilipset.HashIPPort,
			ipv6:            true,
			activeEntries:   []string{"FE80::0202:B3FF:FE1E:8329,tcp:80"},
			currentEntries:  []string{"FE80::0202:B3FF:FE1E:8329,tcp:80"},
			expectedEntries: []string{"FE80::0202:B3FF:FE1E:8329,tcp:80"},
		},
		{ // case 2
			set: &utilipset.IPSet{
				Name: "bca",
			},
			setType:         utilipset.HashIPPort,
			ipv6:            false,
			activeEntries:   []string{"172.17.0.4,tcp:80", "172.17.0.5,tcp:80"},
			currentEntries:  []string{"172.17.0.5,udp:53"},
			expectedEntries: []string{"172.17.0.4,tcp:80", "172.17.0.5,tcp:80"},
		},
		{ // case 3
			set: &utilipset.IPSet{
				Name: "bar",
			},
			setType:         utilipset.HashIPPortIP,
			ipv6:            false,
			activeEntries:   []string{"172.17.0.4,tcp:80:172.17.0.4"},
			currentEntries:  []string{"172.17.0.4,tcp:80:172.17.0.4"},
			expectedEntries: []string{"172.17.0.4,tcp:80:172.17.0.4"},
		},
		{ // case 4
			set: &utilipset.IPSet{
				Name: "baz",
			},
			setType:         utilipset.HashIPPortIP,
			ipv6:            true,
			activeEntries:   []string{"FE80:0000:0000:0000:0202:B3FF:FE1E:8329,tcp:8080:FE80:0000:0000:0000:0202:B3FF:FE1E:8329"},
			currentEntries:  []string{"1111:0000:0000:0000:0202:B3FF:FE1E:8329,tcp:8081:1111:0000:0000:0000:0202:B3FF:FE1E:8329:8081"},
			expectedEntries: []string{"FE80:0000:0000:0000:0202:B3FF:FE1E:8329,tcp:8080:FE80:0000:0000:0000:0202:B3FF:FE1E:8329"},
		},
		{ // case 5
			set: &utilipset.IPSet{
				Name: "NOPE",
			},
			setType:         utilipset.HashIPPortIP,
			ipv6:            false,
			activeEntries:   []string{"172.17.0.4,tcp:80,172.17.0.9", "172.17.0.5,tcp:80,172.17.0.10"},
			currentEntries:  nil,
			expectedEntries: []string{"172.17.0.4,tcp:80,172.17.0.9", "172.17.0.5,tcp:80,172.17.0.10"},
		},
		{ // case 6
			set: &utilipset.IPSet{
				Name: "ABC-DEF",
			},
			setType:         utilipset.HashIPPortNet,
			ipv6:            false,
			activeEntries:   []string{"172.17.0.4,tcp:80,172.17.0.0/16", "172.17.0.5,tcp:80,172.17.0.0/16"},
			currentEntries:  nil,
			expectedEntries: []string{"172.17.0.4,tcp:80,172.17.0.0/16", "172.17.0.5,tcp:80,172.17.0.0/16"},
		},
		{ // case 7
			set: &utilipset.IPSet{
				Name: "zar",
			},
			setType:         utilipset.HashIPPortNet,
			ipv6:            true,
			activeEntries:   []string{"FE80::8329,tcp:8800,2001:db8::/32"},
			currentEntries:  []string{"FE80::8329,tcp:8800,2001:db8::/32"},
			expectedEntries: []string{"FE80::8329,tcp:8800,2001:db8::/32"},
		},
		{ // case 8
			set: &utilipset.IPSet{
				Name: "bbb",
			},
			setType:         utilipset.HashIPPortNet,
			ipv6:            true,
			activeEntries:   nil,
			currentEntries:  []string{"FE80::8329,udp:8801,2001:db8::/32"},
			expectedEntries: nil,
		},
		{ // case 9
			set: &utilipset.IPSet{
				Name: "AAA",
			},
			setType:         utilipset.BitmapPort,
			activeEntries:   nil,
			currentEntries:  []string{"80"},
			expectedEntries: nil,
		},
		{ // case 10
			set: &utilipset.IPSet{
				Name: "c-c-c",
			},
			setType:         utilipset.BitmapPort,
			activeEntries:   []string{"8080", "9090"},
			currentEntries:  []string{"80"},
			expectedEntries: []string{"8080", "9090"},
		},
		{ // case 11
			set: &utilipset.IPSet{
				Name: "NODE-PORT",
			},
			setType:         utilipset.BitmapPort,
			activeEntries:   []string{"8080"},
			currentEntries:  []string{"80", "9090", "8081", "8082"},
			expectedEntries: []string{"8080"},
		},
		{ // case 12
			set: &utilipset.IPSet{
				Name: "sctp-1",
			},
			setType:         utilipset.HashIPPort,
			ipv6:            false,
			activeEntries:   []string{"172.17.0.4,sctp:80"},
			currentEntries:  nil,
			expectedEntries: []string{"172.17.0.4,sctp:80"},
		},
		{ // case 1
			set: &utilipset.IPSet{
				Name: "sctp-2",
			},
			setType:         utilipset.HashIPPort,
			ipv6:            true,
			activeEntries:   []string{"FE80::0202:B3FF:FE1E:8329,sctp:80"},
			currentEntries:  []string{"FE80::0202:B3FF:FE1E:8329,sctp:80"},
			expectedEntries: []string{"FE80::0202:B3FF:FE1E:8329,sctp:80"},
		},
	}

	for i := range testCases {
		set := NewIPSet(fakeipset.NewFake(testIPSetVersion), testCases[i].set.Name, testCases[i].setType, testCases[i].ipv6, "comment-"+testCases[i].set.Name)

		if err := set.handle.CreateSet(&set.IPSet, true); err != nil {
			t.Errorf("Unexpected error: %v", err)
		}
		for _, entry := range testCases[i].expectedEntries {
			set.handle.AddEntry(entry, testCases[i].set, true)
		}

		set.activeEntries.Insert(testCases[i].activeEntries...)
		set.syncIPSetEntries()
		for _, entry := range testCases[i].expectedEntries {
			found, err := set.handle.TestEntry(entry, testCases[i].set.Name)
			if err != nil {
				t.Errorf("Unexpected error: %v", err)
			}
			if !found {
				t.Errorf("Unexpected entry 172.17.0.4,tcp:80 not found in set foo")
			}
		}
	}
}
<commit_msg>ipvs: add descriptions to ipset unit tests<commit_after>/*
Copyright 2017 The Kubernetes Authors.

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

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

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

package ipvs

import (
	"testing"

	utilipset "k8s.io/kubernetes/pkg/util/ipset"
	fakeipset "k8s.io/kubernetes/pkg/util/ipset/testing"
)

func TestCheckIPSetVersion(t *testing.T) {
	testCases := []struct {
		name    string
		vstring string
		valid   bool
	}{
		{"4.0 < 6.0 is invalid", "4.0", false},
		{"5.1 < 6.0 is invalid", "5.1", false},
		{"5.2 < 6.0 is invalid", "5.1.2", false},
		{"7 is not a valid version", "7", false},
		{"6.0 is valid since >= 6.0", "6.0", true},
		{"6.1 is valid since >= 6.0", "6.1", true},
		{"6.19 is valid since >= 6.0", "6.19", true},
		{"7.0 is valid since >= 6.0", "7.0", true},
		{"8.1.2 is valid since >= 6.0", "8.1.2", true},
		{"9.3.4.0 is valid since >= 6.0", "9.3.4.0", true},
		{"not a valid semantic version", "total junk", false},
	}

	for _, testCase := range testCases {
		t.Run(testCase.name, func(t *testing.T) {
			valid := checkMinVersion(testCase.vstring)
			if testCase.valid != valid {
				t.Errorf("Expected result: %v, Got result: %v", testCase.valid, valid)
			}
		})
	}
}

const testIPSetVersion = "v6.19"

func TestSyncIPSetEntries(t *testing.T) {
	testCases := []struct {
		name            string
		set             *utilipset.IPSet
		setType         utilipset.Type
		ipv6            bool
		activeEntries   []string
		currentEntries  []string
		expectedEntries []string
	}{
		{
			name: "normal ipset sync",
			set: &utilipset.IPSet{
				Name: "foo",
			},
			setType:         utilipset.HashIPPort,
			ipv6:            false,
			activeEntries:   []string{"172.17.0.4,tcp:80"},
			currentEntries:  nil,
			expectedEntries: []string{"172.17.0.4,tcp:80"},
		},
		{
			name: "ipset IPv6 sync with no new entries",
			set: &utilipset.IPSet{
				Name: "abz",
			},
			setType:         utilipset.HashIPPort,
			ipv6:            true,
			activeEntries:   []string{"FE80::0202:B3FF:FE1E:8329,tcp:80"},
			currentEntries:  []string{"FE80::0202:B3FF:FE1E:8329,tcp:80"},
			expectedEntries: []string{"FE80::0202:B3FF:FE1E:8329,tcp:80"},
		},
		{
			name: "ipset sync with updated udp->tcp in hash",
			set: &utilipset.IPSet{
				Name: "bca",
			},
			setType:         utilipset.HashIPPort,
			ipv6:            false,
			activeEntries:   []string{"172.17.0.4,tcp:80", "172.17.0.5,tcp:80"},
			currentEntries:  []string{"172.17.0.5,udp:53"},
			expectedEntries: []string{"172.17.0.4,tcp:80", "172.17.0.5,tcp:80"},
		},
		{
			name: "ipset sync no updates required",
			set: &utilipset.IPSet{
				Name: "bar",
			},
			setType:         utilipset.HashIPPortIP,
			ipv6:            false,
			activeEntries:   []string{"172.17.0.4,tcp:80:172.17.0.4"},
			currentEntries:  []string{"172.17.0.4,tcp:80:172.17.0.4"},
			expectedEntries: []string{"172.17.0.4,tcp:80:172.17.0.4"},
		},
		{
			name: "ipset IPv6 sync, delete and add new entry",
			set: &utilipset.IPSet{
				Name: "baz",
			},
			setType:         utilipset.HashIPPortIP,
			ipv6:            true,
			activeEntries:   []string{"FE80:0000:0000:0000:0202:B3FF:FE1E:8329,tcp:8080:FE80:0000:0000:0000:0202:B3FF:FE1E:8329"},
			currentEntries:  []string{"1111:0000:0000:0000:0202:B3FF:FE1E:8329,tcp:8081:1111:0000:0000:0000:0202:B3FF:FE1E:8329:8081"},
			expectedEntries: []string{"FE80:0000:0000:0000:0202:B3FF:FE1E:8329,tcp:8080:FE80:0000:0000:0000:0202:B3FF:FE1E:8329"},
		},
		{
			name: "ipset sync, no current entries",
			set: &utilipset.IPSet{
				Name: "NOPE",
			},
			setType:         utilipset.HashIPPortIP,
			ipv6:            false,
			activeEntries:   []string{"172.17.0.4,tcp:80,172.17.0.9", "172.17.0.5,tcp:80,172.17.0.10"},
			currentEntries:  nil,
			expectedEntries: []string{"172.17.0.4,tcp:80,172.17.0.9", "172.17.0.5,tcp:80,172.17.0.10"},
		},
		{
			name: "ipset sync, no current entries with /16 subnet",
			set: &utilipset.IPSet{
				Name: "ABC-DEF",
			},
			setType:         utilipset.HashIPPortNet,
			ipv6:            false,
			activeEntries:   []string{"172.17.0.4,tcp:80,172.17.0.0/16", "172.17.0.5,tcp:80,172.17.0.0/16"},
			currentEntries:  nil,
			expectedEntries: []string{"172.17.0.4,tcp:80,172.17.0.0/16", "172.17.0.5,tcp:80,172.17.0.0/16"},
		},
		{
			name: "ipset IPv6 sync, no updates required with /32 subnet",
			set: &utilipset.IPSet{
				Name: "zar",
			},
			setType:         utilipset.HashIPPortNet,
			ipv6:            true,
			activeEntries:   []string{"FE80::8329,tcp:8800,2001:db8::/32"},
			currentEntries:  []string{"FE80::8329,tcp:8800,2001:db8::/32"},
			expectedEntries: []string{"FE80::8329,tcp:8800,2001:db8::/32"},
		},
		{
			name: "ipset IPv6 sync, current entries removed",
			set: &utilipset.IPSet{
				Name: "bbb",
			},
			setType:         utilipset.HashIPPortNet,
			ipv6:            true,
			activeEntries:   nil,
			currentEntries:  []string{"FE80::8329,udp:8801,2001:db8::/32"},
			expectedEntries: nil,
		},
		{
			name: "ipset sync, port entry removed",
			set: &utilipset.IPSet{
				Name: "AAA",
			},
			setType:         utilipset.BitmapPort,
			activeEntries:   nil,
			currentEntries:  []string{"80"},
			expectedEntries: nil,
		},
		{
			name: "ipset sync, remove old and add new",
			set: &utilipset.IPSet{
				Name: "c-c-c",
			},
			setType:         utilipset.BitmapPort,
			activeEntries:   []string{"8080", "9090"},
			currentEntries:  []string{"80"},
			expectedEntries: []string{"8080", "9090"},
		},
		{
			name: "ipset sync, remove many stale ports",
			set: &utilipset.IPSet{
				Name: "NODE-PORT",
			},
			setType:         utilipset.BitmapPort,
			activeEntries:   []string{"8080"},
			currentEntries:  []string{"80", "9090", "8081", "8082"},
			expectedEntries: []string{"8080"},
		},
		{
			name: "ipset sync, add sctp entry",
			set: &utilipset.IPSet{
				Name: "sctp-1",
			},
			setType:         utilipset.HashIPPort,
			ipv6:            false,
			activeEntries:   []string{"172.17.0.4,sctp:80"},
			currentEntries:  nil,
			expectedEntries: []string{"172.17.0.4,sctp:80"},
		},
		{
			name: "ipset sync, add IPV6 sctp entry",
			set: &utilipset.IPSet{
				Name: "sctp-2",
			},
			setType:         utilipset.HashIPPort,
			ipv6:            true,
			activeEntries:   []string{"FE80::0202:B3FF:FE1E:8329,sctp:80"},
			currentEntries:  []string{"FE80::0202:B3FF:FE1E:8329,sctp:80"},
			expectedEntries: []string{"FE80::0202:B3FF:FE1E:8329,sctp:80"},
		},
	}

	for _, testCase := range testCases {
		t.Run(testCase.name, func(t *testing.T) {
			set := NewIPSet(fakeipset.NewFake(testIPSetVersion), testCase.set.Name, testCase.setType, testCase.ipv6, "comment-"+testCase.set.Name)

			if err := set.handle.CreateSet(&set.IPSet, true); err != nil {
				t.Errorf("Unexpected error: %v", err)
			}

			for _, entry := range testCase.currentEntries {
				set.handle.AddEntry(entry, testCase.set, true)
			}

			set.activeEntries.Insert(testCase.activeEntries...)
			set.syncIPSetEntries()
			for _, entry := range testCase.expectedEntries {
				found, err := set.handle.TestEntry(entry, testCase.set.Name)
				if err != nil {
					t.Errorf("Unexpected error: %v", err)
				}
				if !found {
					t.Errorf("Unexpected entry %q not found in set foo", entry)
				}
			}
		})
	}
}
<|endoftext|>


<commit_before>package pubsub

import (
	"testing"
	"time"
)

func TestSendToOneSub(t *testing.T) {
	p := NewPublisher(100*time.Millisecond, 10)
	c := p.Subscribe()

	p.Publish("hi")

	msg := <-c
	if msg.(string) != "hi" {
		t.Fatalf("expected message hi but received %v", msg)
	}
}

func TestSendToMultipleSubs(t *testing.T) {
	p := NewPublisher(100*time.Millisecond, 10)
	subs := []chan interface{}{}
	subs = append(subs, p.Subscribe(), p.Subscribe(), p.Subscribe())

	p.Publish("hi")

	for _, c := range subs {
		msg := <-c
		if msg.(string) != "hi" {
			t.Fatalf("expected message hi but received %v", msg)
		}
	}
}

func TestEvictOneSub(t *testing.T) {
	p := NewPublisher(100*time.Millisecond, 10)
	s1 := p.Subscribe()
	s2 := p.Subscribe()

	p.Evict(s1)
	p.Publish("hi")
	if _, ok := <-s1; ok {
		t.Fatal("expected s1 to not receive the published message")
	}

	msg := <-s2
	if msg.(string) != "hi" {
		t.Fatalf("expected message hi but received %v", msg)
	}
}

func TestClosePublisher(t *testing.T) {
	p := NewPublisher(100*time.Millisecond, 10)
	subs := []chan interface{}{}
	subs = append(subs, p.Subscribe(), p.Subscribe(), p.Subscribe())
	p.Close()

	for _, c := range subs {
		if _, ok := <-c; ok {
			t.Fatal("expected all subscriber channels to be closed")
		}
	}
}
<commit_msg>Benchmark for pkg/pubsub package<commit_after>package pubsub

import (
	"fmt"
	"testing"
	"time"
)

func TestSendToOneSub(t *testing.T) {
	p := NewPublisher(100*time.Millisecond, 10)
	c := p.Subscribe()

	p.Publish("hi")

	msg := <-c
	if msg.(string) != "hi" {
		t.Fatalf("expected message hi but received %v", msg)
	}
}

func TestSendToMultipleSubs(t *testing.T) {
	p := NewPublisher(100*time.Millisecond, 10)
	subs := []chan interface{}{}
	subs = append(subs, p.Subscribe(), p.Subscribe(), p.Subscribe())

	p.Publish("hi")

	for _, c := range subs {
		msg := <-c
		if msg.(string) != "hi" {
			t.Fatalf("expected message hi but received %v", msg)
		}
	}
}

func TestEvictOneSub(t *testing.T) {
	p := NewPublisher(100*time.Millisecond, 10)
	s1 := p.Subscribe()
	s2 := p.Subscribe()

	p.Evict(s1)
	p.Publish("hi")
	if _, ok := <-s1; ok {
		t.Fatal("expected s1 to not receive the published message")
	}

	msg := <-s2
	if msg.(string) != "hi" {
		t.Fatalf("expected message hi but received %v", msg)
	}
}

func TestClosePublisher(t *testing.T) {
	p := NewPublisher(100*time.Millisecond, 10)
	subs := []chan interface{}{}
	subs = append(subs, p.Subscribe(), p.Subscribe(), p.Subscribe())
	p.Close()

	for _, c := range subs {
		if _, ok := <-c; ok {
			t.Fatal("expected all subscriber channels to be closed")
		}
	}
}

const sampleText = "test"

type testSubscriber struct {
	dataCh chan interface{}
	ch     chan error
}

func (s *testSubscriber) Wait() error {
	return <-s.ch
}

func newTestSubscriber(p *Publisher) *testSubscriber {
	ts := &testSubscriber{
		dataCh: p.Subscribe(),
		ch:     make(chan error),
	}
	go func() {
		for data := range ts.dataCh {
			s, ok := data.(string)
			if !ok {
				ts.ch <- fmt.Errorf("Unexpected type %T", data)
				break
			}
			if s != sampleText {
				ts.ch <- fmt.Errorf("Unexpected text %s", s)
				break
			}
		}
		close(ts.ch)
	}()
	return ts
}

func BenchmarkPubSub(b *testing.B) {
	for i := 0; i < b.N; i++ {
		b.StopTimer()
		p := NewPublisher(0, 1024)
		var subs [](*testSubscriber)
		for j := 0; j < 50; j++ {
			subs = append(subs, newTestSubscriber(p))
		}
		b.StartTimer()
		for j := 0; j < 1000; j++ {
			p.Publish(sampleText)
		}
		time.AfterFunc(1*time.Second, func() {
			for _, s := range subs {
				p.Evict(s.dataCh)
			}
		})
		for _, s := range subs {
			if err := s.Wait(); err != nil {
				b.Fatal(err)
			}
		}
	}
}
<|endoftext|>


<commit_before>/*
Copyright 2015 The Kubernetes Authors All rights reserved.

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

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

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

package flocker

import (
	"fmt"
	"path"
	"time"

	flockerclient "github.com/ClusterHQ/flocker-go"
	"k8s.io/kubernetes/pkg/api"
	"k8s.io/kubernetes/pkg/types"
	"k8s.io/kubernetes/pkg/util"
	"k8s.io/kubernetes/pkg/util/exec"
	"k8s.io/kubernetes/pkg/util/mount"
	"k8s.io/kubernetes/pkg/util/strings"
	"k8s.io/kubernetes/pkg/volume"
	volumeutil "k8s.io/kubernetes/pkg/volume/util"
)

const (
	flockerPluginName = "kubernetes.io/flocker"

	defaultHost           = "localhost"
	defaultPort           = 4523
	defaultCACertFile     = "/etc/flocker/cluster.crt"
	defaultClientKeyFile  = "/etc/flocker/apiuser.key"
	defaultClientCertFile = "/etc/flocker/apiuser.crt"

	timeoutWaitingForVolume = 2 * time.Minute
	tickerWaitingForVolume  = 5 * time.Second
)

func ProbeVolumePlugins() []volume.VolumePlugin {
	return []volume.VolumePlugin{&flockerPlugin{}}
}

type flockerPlugin struct {
	host volume.VolumeHost
}

type flocker struct {
	datasetName string
	path        string
	pod         *api.Pod
	mounter     mount.Interface
	plugin      *flockerPlugin
}

func (p *flockerPlugin) Init(host volume.VolumeHost) error {
	p.host = host
	return nil
}

func (p flockerPlugin) Name() string {
	return flockerPluginName
}

func (p flockerPlugin) CanSupport(spec *volume.Spec) bool {
	return (spec.PersistentVolume != nil && spec.PersistentVolume.Spec.Flocker != nil) ||
		(spec.Volume != nil && spec.Volume.Flocker != nil)
}

func (p *flockerPlugin) getFlockerVolumeSource(spec *volume.Spec) (*api.FlockerVolumeSource, bool) {
	// AFAIK this will always be r/w, but perhaps for the future it will be needed
	readOnly := false

	if spec.Volume != nil && spec.Volume.Flocker != nil {
		return spec.Volume.Flocker, readOnly
	}
	return spec.PersistentVolume.Spec.Flocker, readOnly
}

func (p *flockerPlugin) NewBuilder(spec *volume.Spec, pod *api.Pod, opts volume.VolumeOptions) (volume.Builder, error) {
	source, readOnly := p.getFlockerVolumeSource(spec)
	builder := flockerBuilder{
		flocker: &flocker{
			datasetName: source.DatasetName,
			pod:         pod,
			mounter:     p.host.GetMounter(),
			plugin:      p,
		},
		exe:      exec.New(),
		opts:     opts,
		readOnly: readOnly,
	}
	return &builder, nil
}

func (p *flockerPlugin) NewCleaner(datasetName string, podUID types.UID) (volume.Cleaner, error) {
	// Flocker agent will take care of this, there is nothing we can do here
	return nil, nil
}

type flockerBuilder struct {
	*flocker
	client   flockerclient.Clientable
	exe      exec.Interface
	opts     volume.VolumeOptions
	readOnly bool
	volume.MetricsNil
}

func (b flockerBuilder) GetAttributes() volume.Attributes {
	return volume.Attributes{
		ReadOnly:        b.readOnly,
		Managed:         false,
		SupportsSELinux: false,
	}
}
func (b flockerBuilder) GetPath() string {
	return b.flocker.path
}

func (b flockerBuilder) SetUp(fsGroup *int64) error {
	return b.SetUpAt(b.flocker.datasetName, fsGroup)
}

// newFlockerClient uses environment variables and pod attributes to return a
// flocker client capable of talking with the Flocker control service.
func (b flockerBuilder) newFlockerClient() (*flockerclient.Client, error) {
	host := util.GetEnvAsStringOrFallback("FLOCKER_CONTROL_SERVICE_HOST", defaultHost)
	port, err := util.GetEnvAsIntOrFallback("FLOCKER_CONTROL_SERVICE_PORT", defaultPort)

	if err != nil {
		return nil, err
	}
	caCertPath := util.GetEnvAsStringOrFallback("FLOCKER_CONTROL_SERVICE_CA_FILE", defaultCACertFile)
	keyPath := util.GetEnvAsStringOrFallback("FLOCKER_CONTROL_SERVICE_CLIENT_KEY_FILE", defaultClientKeyFile)
	certPath := util.GetEnvAsStringOrFallback("FLOCKER_CONTROL_SERVICE_CLIENT_CERT_FILE", defaultClientCertFile)

	c, err := flockerclient.NewClient(host, port, b.flocker.pod.Status.HostIP, caCertPath, keyPath, certPath)
	return c, err
}

func (b *flockerBuilder) getMetaDir() string {
	return path.Join(
		b.plugin.host.GetPodPluginDir(
			b.flocker.pod.UID, strings.EscapeQualifiedNameForDisk(flockerPluginName),
		),
		b.datasetName,
	)
}

/*
SetUpAt will setup a Flocker volume following this flow of calls to the Flocker
control service:

1. Get the dataset id for the given volume name/dir
2. It should already be there, if it's not the user needs to manually create it
3. Check the current Primary UUID
4. If it doesn't match with the Primary UUID that we got on 2, then we will
   need to update the Primary UUID for this volume.
5. Wait until the Primary UUID was updated or timeout.
*/
func (b flockerBuilder) SetUpAt(dir string, fsGroup *int64) error {
	if volumeutil.IsReady(b.getMetaDir()) {
		return nil
	}

	if b.client == nil {
		c, err := b.newFlockerClient()
		if err != nil {
			return err
		}
		b.client = c
	}

	datasetID, err := b.client.GetDatasetID(dir)
	if err != nil {
		return err
	}

	s, err := b.client.GetDatasetState(datasetID)
	if err != nil {
		return fmt.Errorf("The volume '%s' is not available in Flocker. You need to create this manually with Flocker CLI before using it.", dir)
	}

	primaryUUID, err := b.client.GetPrimaryUUID()
	if err != nil {
		return err
	}

	if s.Primary != primaryUUID {
		if err := b.updateDatasetPrimary(datasetID, primaryUUID); err != nil {
			return err
		}
	}

	b.flocker.path = s.Path
	volumeutil.SetReady(b.getMetaDir())
	return nil
}

// updateDatasetPrimary will update the primary in Flocker and wait for it to
// be ready. If it never gets to ready state it will timeout and error.
func (b flockerBuilder) updateDatasetPrimary(datasetID, primaryUUID string) error {
	// We need to update the primary and wait for it to be ready
	_, err := b.client.UpdatePrimaryForDataset(primaryUUID, datasetID)
	if err != nil {
		return err
	}

	timeoutChan := time.NewTimer(timeoutWaitingForVolume).C
	tickChan := time.NewTicker(tickerWaitingForVolume).C

	for {
		if s, err := b.client.GetDatasetState(datasetID); err == nil && s.Primary == primaryUUID {
			return nil
		}

		select {
		case <-timeoutChan:
			return fmt.Errorf(
				"Timed out waiting for the dataset_id: '%s' to be moved to the primary: '%s'\n%v",
				datasetID, primaryUUID, err,
			)
		case <-tickChan:
			break
		}
	}

}
<commit_msg>update mountpath of volume after migration<commit_after>/*
Copyright 2015 The Kubernetes Authors All rights reserved.

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

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

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

package flocker

import (
	"fmt"
	"path"
	"time"

	flockerclient "github.com/ClusterHQ/flocker-go"
	"k8s.io/kubernetes/pkg/api"
	"k8s.io/kubernetes/pkg/types"
	"k8s.io/kubernetes/pkg/util"
	"k8s.io/kubernetes/pkg/util/exec"
	"k8s.io/kubernetes/pkg/util/mount"
	"k8s.io/kubernetes/pkg/util/strings"
	"k8s.io/kubernetes/pkg/volume"
	volumeutil "k8s.io/kubernetes/pkg/volume/util"
)

const (
	flockerPluginName = "kubernetes.io/flocker"

	defaultHost           = "localhost"
	defaultPort           = 4523
	defaultCACertFile     = "/etc/flocker/cluster.crt"
	defaultClientKeyFile  = "/etc/flocker/apiuser.key"
	defaultClientCertFile = "/etc/flocker/apiuser.crt"

	timeoutWaitingForVolume = 2 * time.Minute
	tickerWaitingForVolume  = 5 * time.Second
)

func ProbeVolumePlugins() []volume.VolumePlugin {
	return []volume.VolumePlugin{&flockerPlugin{}}
}

type flockerPlugin struct {
	host volume.VolumeHost
}

type flocker struct {
	datasetName string
	path        string
	pod         *api.Pod
	mounter     mount.Interface
	plugin      *flockerPlugin
}

func (p *flockerPlugin) Init(host volume.VolumeHost) error {
	p.host = host
	return nil
}

func (p flockerPlugin) Name() string {
	return flockerPluginName
}

func (p flockerPlugin) CanSupport(spec *volume.Spec) bool {
	return (spec.PersistentVolume != nil && spec.PersistentVolume.Spec.Flocker != nil) ||
		(spec.Volume != nil && spec.Volume.Flocker != nil)
}

func (p *flockerPlugin) getFlockerVolumeSource(spec *volume.Spec) (*api.FlockerVolumeSource, bool) {
	// AFAIK this will always be r/w, but perhaps for the future it will be needed
	readOnly := false

	if spec.Volume != nil && spec.Volume.Flocker != nil {
		return spec.Volume.Flocker, readOnly
	}
	return spec.PersistentVolume.Spec.Flocker, readOnly
}

func (p *flockerPlugin) NewBuilder(spec *volume.Spec, pod *api.Pod, opts volume.VolumeOptions) (volume.Builder, error) {
	source, readOnly := p.getFlockerVolumeSource(spec)
	builder := flockerBuilder{
		flocker: &flocker{
			datasetName: source.DatasetName,
			pod:         pod,
			mounter:     p.host.GetMounter(),
			plugin:      p,
		},
		exe:      exec.New(),
		opts:     opts,
		readOnly: readOnly,
	}
	return &builder, nil
}

func (p *flockerPlugin) NewCleaner(datasetName string, podUID types.UID) (volume.Cleaner, error) {
	// Flocker agent will take care of this, there is nothing we can do here
	return nil, nil
}

type flockerBuilder struct {
	*flocker
	client   flockerclient.Clientable
	exe      exec.Interface
	opts     volume.VolumeOptions
	readOnly bool
	volume.MetricsNil
}

func (b flockerBuilder) GetAttributes() volume.Attributes {
	return volume.Attributes{
		ReadOnly:        b.readOnly,
		Managed:         false,
		SupportsSELinux: false,
	}
}
func (b flockerBuilder) GetPath() string {
	return b.flocker.path
}

func (b flockerBuilder) SetUp(fsGroup *int64) error {
	return b.SetUpAt(b.flocker.datasetName, fsGroup)
}

// newFlockerClient uses environment variables and pod attributes to return a
// flocker client capable of talking with the Flocker control service.
func (b flockerBuilder) newFlockerClient() (*flockerclient.Client, error) {
	host := util.GetEnvAsStringOrFallback("FLOCKER_CONTROL_SERVICE_HOST", defaultHost)
	port, err := util.GetEnvAsIntOrFallback("FLOCKER_CONTROL_SERVICE_PORT", defaultPort)

	if err != nil {
		return nil, err
	}
	caCertPath := util.GetEnvAsStringOrFallback("FLOCKER_CONTROL_SERVICE_CA_FILE", defaultCACertFile)
	keyPath := util.GetEnvAsStringOrFallback("FLOCKER_CONTROL_SERVICE_CLIENT_KEY_FILE", defaultClientKeyFile)
	certPath := util.GetEnvAsStringOrFallback("FLOCKER_CONTROL_SERVICE_CLIENT_CERT_FILE", defaultClientCertFile)

	c, err := flockerclient.NewClient(host, port, b.flocker.pod.Status.HostIP, caCertPath, keyPath, certPath)
	return c, err
}

func (b *flockerBuilder) getMetaDir() string {
	return path.Join(
		b.plugin.host.GetPodPluginDir(
			b.flocker.pod.UID, strings.EscapeQualifiedNameForDisk(flockerPluginName),
		),
		b.datasetName,
	)
}

/*
SetUpAt will setup a Flocker volume following this flow of calls to the Flocker
control service:

1. Get the dataset id for the given volume name/dir
2. It should already be there, if it's not the user needs to manually create it
3. Check the current Primary UUID
4. If it doesn't match with the Primary UUID that we got on 2, then we will
   need to update the Primary UUID for this volume.
5. Wait until the Primary UUID was updated or timeout.
*/
func (b flockerBuilder) SetUpAt(dir string, fsGroup *int64) error {
	if volumeutil.IsReady(b.getMetaDir()) {
		return nil
	}

	if b.client == nil {
		c, err := b.newFlockerClient()
		if err != nil {
			return err
		}
		b.client = c
	}

	datasetID, err := b.client.GetDatasetID(dir)
	if err != nil {
		return err
	}

	s, err := b.client.GetDatasetState(datasetID)
	if err != nil {
		return fmt.Errorf("The volume '%s' is not available in Flocker. You need to create this manually with Flocker CLI before using it.", dir)
	}

	primaryUUID, err := b.client.GetPrimaryUUID()
	if err != nil {
		return err
	}

	if s.Primary != primaryUUID {
		if err := b.updateDatasetPrimary(datasetID, primaryUUID); err != nil {
			return err
		}
		newState, err := b.client.GetDatasetState(datasetID)
		if err != nil {
			return fmt.Errorf("The volume '%s' migrated unsuccessfully.", datasetID)
		}
		b.flocker.path = newState.Path
	} else {
		b.flocker.path = s.Path
	}

	volumeutil.SetReady(b.getMetaDir())
	return nil
}

// updateDatasetPrimary will update the primary in Flocker and wait for it to
// be ready. If it never gets to ready state it will timeout and error.
func (b flockerBuilder) updateDatasetPrimary(datasetID, primaryUUID string) error {
	// We need to update the primary and wait for it to be ready
	_, err := b.client.UpdatePrimaryForDataset(primaryUUID, datasetID)
	if err != nil {
		return err
	}

	timeoutChan := time.NewTimer(timeoutWaitingForVolume).C
	tickChan := time.NewTicker(tickerWaitingForVolume).C

	for {
		if s, err := b.client.GetDatasetState(datasetID); err == nil && s.Primary == primaryUUID {
			return nil
		}

		select {
		case <-timeoutChan:
			return fmt.Errorf(
				"Timed out waiting for the dataset_id: '%s' to be moved to the primary: '%s'\n%v",
				datasetID, primaryUUID, err,
			)
		case <-tickChan:
			break
		}
	}

}
<|endoftext|>


<commit_before>// Copyright 2014 Hajime Hoshi
//
// 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 ebiten_test

import (
	. "github.com/hajimehoshi/ebiten"
	"testing"
)

func TestGeometryInit(t *testing.T) {
	var m GeoM
	for i := 0; i < GeoMDim-1; i++ {
		for j := 0; j < GeoMDim; j++ {
			got := m.Element(i, j)
			want := 0.0
			if i == j {
				want = 1
			}
			if want != got {
				t.Errorf("m.Element(%d, %d) = %f, want %f", i, j, got, want)
			}
		}
	}
}

func TestGeometryAssign(t *testing.T) {
	m := GeoM{}
	m.SetElement(0, 0, 1)
	m2 := m
	m.SetElement(0, 0, 0)
	got := m2.Element(0, 0)
	want := 1.0
	if want != got {
		t.Errorf("m2.Element(%d, %d) = %f, want %f", 0, 0, got, want)
	}
}

func TestGeometryConcat(t *testing.T) {
	matrix1 := GeoM{}
	matrix1.Scale(2, 2)
	matrix2 := GeoM{}
	matrix2.Translate(1, 1)

	matrix3 := matrix1
	matrix3.Concat(matrix2)
	expected := [][]float64{
		{2, 0, 1},
		{0, 2, 1},
	}
	for i := 0; i < 2; i++ {
		for j := 0; j < 3; j++ {
			got := matrix3.Element(i, j)
			want := expected[i][j]
			if want != got {
				t.Errorf("matrix3.Element(%d, %d) = %f,"+
					" want %f",
					i, j, got, want)
			}
		}
	}

	matrix4 := matrix2
	matrix4.Concat(matrix1)
	expected = [][]float64{
		{2, 0, 2},
		{0, 2, 2},
	}
	for i := 0; i < 2; i++ {
		for j := 0; j < 3; j++ {
			got := matrix4.Element(i, j)
			want := expected[i][j]
			if want != got {
				t.Errorf("matrix4.Element(%d, %d) = %f, want %f",
					i, j, got, want)
			}
		}
	}
}
<commit_msg>graphics: Add BenchmarkGeoM<commit_after>// Copyright 2014 Hajime Hoshi
//
// 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 ebiten_test

import (
	"math"
	"testing"

	. "github.com/hajimehoshi/ebiten"
)

func TestGeometryInit(t *testing.T) {
	var m GeoM
	for i := 0; i < GeoMDim-1; i++ {
		for j := 0; j < GeoMDim; j++ {
			got := m.Element(i, j)
			want := 0.0
			if i == j {
				want = 1
			}
			if want != got {
				t.Errorf("m.Element(%d, %d) = %f, want %f", i, j, got, want)
			}
		}
	}
}

func TestGeometryAssign(t *testing.T) {
	m := GeoM{}
	m.SetElement(0, 0, 1)
	m2 := m
	m.SetElement(0, 0, 0)
	got := m2.Element(0, 0)
	want := 1.0
	if want != got {
		t.Errorf("m2.Element(%d, %d) = %f, want %f", 0, 0, got, want)
	}
}

func TestGeometryConcat(t *testing.T) {
	matrix1 := GeoM{}
	matrix1.Scale(2, 2)
	matrix2 := GeoM{}
	matrix2.Translate(1, 1)

	matrix3 := matrix1
	matrix3.Concat(matrix2)
	expected := [][]float64{
		{2, 0, 1},
		{0, 2, 1},
	}
	for i := 0; i < 2; i++ {
		for j := 0; j < 3; j++ {
			got := matrix3.Element(i, j)
			want := expected[i][j]
			if want != got {
				t.Errorf("matrix3.Element(%d, %d) = %f,"+
					" want %f",
					i, j, got, want)
			}
		}
	}

	matrix4 := matrix2
	matrix4.Concat(matrix1)
	expected = [][]float64{
		{2, 0, 2},
		{0, 2, 2},
	}
	for i := 0; i < 2; i++ {
		for j := 0; j < 3; j++ {
			got := matrix4.Element(i, j)
			want := expected[i][j]
			if want != got {
				t.Errorf("matrix4.Element(%d, %d) = %f, want %f",
					i, j, got, want)
			}
		}
	}
}

func BenchmarkGeoM(b *testing.B) {
	var m GeoM
	for i := 0; i < b.N; i++ {
		m = GeoM{}
		m.Translate(10, 20)
		m.Scale(2, 3)
		m.Rotate(math.Pi / 2)
	}
}
<|endoftext|>


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

package main

import (
	"context"
	"flag"
	"fmt"
	"io/ioutil"
	"net"
	"net/http"
	"os"
	"os/signal"
	"time"

	"github.com/GoogleContainerTools/kpt/porch/repository/pkg/git"
	gogit "github.com/go-git/go-git/v5"
	"github.com/go-git/go-git/v5/plumbing"
	"github.com/go-git/go-git/v5/plumbing/object"
	"k8s.io/klog/v2"
)

var (
	port = flag.Int("port", 9446, "Server port")
)

func main() {
	flag.Parse()

	if err := run(flag.Args()); err != nil {
		fmt.Fprintf(os.Stderr, "unexpected error: %v", err)
	}
}

func run(dirs []string) error {
	var dir string

	switch len(dirs) {
	case 0:
		var err error
		dir, err = ioutil.TempDir("", "repo-*")
		if err != nil {
			return fmt.Errorf("failed to create temporary directory for git repository: %w", err)
		}

	case 1:
		dir = dirs[0]

	default:
		return fmt.Errorf("can server only one git repository, not %d", len(dirs))
	}

	var repo *gogit.Repository
	var err error

	if repo, err = gogit.PlainOpen(dir); err != nil {
		if err != gogit.ErrRepositoryNotExists {
			return fmt.Errorf("failed to open git repository %q: %w", dir, err)
		}
		isBare := true
		repo, err = gogit.PlainInit(dir, isBare)
		if err != nil {
			return fmt.Errorf("failed to initialize git repository %q: %w", dir, err)
		}
		if err := createEmptyCommit(repo); err != nil {
			return err
		}

		// Delete go-git default branch
		_ = repo.Storer.RemoveReference(plumbing.Master)
	}

	server, err := git.NewGitServer(repo)
	if err != nil {
		return fmt.Errorf("filed to initialize git server: %w", err)
	}

	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()

	addressChannel := make(chan net.Addr)

	go func() {
		if err := server.ListenAndServe(ctx, fmt.Sprintf(":%d", *port), addressChannel); err != nil && err != http.ErrServerClosed {
			klog.Fatalf("Listen failed: %v", err)
		}
	}()

	address := <-addressChannel
	fmt.Fprintf(os.Stderr, "Listening on %s\n", address)

	wait := make(chan os.Signal, 1)
	signal.Notify(wait, os.Interrupt)

	<-wait

	return nil
}

func createEmptyCommit(repo *gogit.Repository) error {
	store := repo.Storer
	// Create first commit using empty tree.
	emptyTree := object.Tree{}
	encodedTree := store.NewEncodedObject()
	if err := emptyTree.Encode(encodedTree); err != nil {
		return fmt.Errorf("failed to encode initial empty commit tree: %w", err)
	}

	treeHash, err := store.SetEncodedObject(encodedTree)
	if err != nil {
		return fmt.Errorf("failed to create initial empty commit tree: %w", err)
	}

	sig := object.Signature{
		Name:  "Git Server",
		Email: "git-server@kpt.dev",
		When:  time.Now(),
	}

	commit := object.Commit{
		Author:       sig,
		Committer:    sig,
		Message:      "Empty Commit",
		TreeHash:     treeHash,
		ParentHashes: []plumbing.Hash{}, // No parents
	}

	encodedCommit := store.NewEncodedObject()
	if err := commit.Encode(encodedCommit); err != nil {
		return fmt.Errorf("failed to encode initial empty commit: %w", err)
	}

	commitHash, err := store.SetEncodedObject(encodedCommit)
	if err != nil {
		return fmt.Errorf("failed to create initial empty commit: %w", err)
	}

	main := plumbing.NewHashReference(plumbing.ReferenceName("refs/heads/main"), commitHash)
	if err := repo.Storer.SetReference(main); err != nil {
		return fmt.Errorf("failed to set refs/heads/main to commit sha %s: %w", commitHash, err)
	}

	head := plumbing.NewSymbolicReference(plumbing.HEAD, "refs/heads/main")
	if err := repo.Storer.SetReference(head); err != nil {
		return fmt.Errorf("failed to set HEAD to refs/heads/main: %w", err)
	}

	return nil
}
<commit_msg>test-git-server: Support klog flags (#2985)<commit_after>// Copyright 2022 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package main

import (
	"context"
	"flag"
	"fmt"
	"io/ioutil"
	"net"
	"net/http"
	"os"
	"os/signal"
	"time"

	"github.com/GoogleContainerTools/kpt/porch/repository/pkg/git"
	gogit "github.com/go-git/go-git/v5"
	"github.com/go-git/go-git/v5/plumbing"
	"github.com/go-git/go-git/v5/plumbing/object"
	"k8s.io/klog/v2"
)

var (
	port = flag.Int("port", 9446, "Server port")
)

func main() {
	klog.InitFlags(nil)

	flag.Parse()

	if err := run(flag.Args()); err != nil {
		fmt.Fprintf(os.Stderr, "unexpected error: %v", err)
	}
}

func run(dirs []string) error {
	var dir string

	switch len(dirs) {
	case 0:
		var err error
		dir, err = ioutil.TempDir("", "repo-*")
		if err != nil {
			return fmt.Errorf("failed to create temporary directory for git repository: %w", err)
		}

	case 1:
		dir = dirs[0]

	default:
		return fmt.Errorf("can server only one git repository, not %d", len(dirs))
	}

	var repo *gogit.Repository
	var err error

	if repo, err = gogit.PlainOpen(dir); err != nil {
		if err != gogit.ErrRepositoryNotExists {
			return fmt.Errorf("failed to open git repository %q: %w", dir, err)
		}
		isBare := true
		repo, err = gogit.PlainInit(dir, isBare)
		if err != nil {
			return fmt.Errorf("failed to initialize git repository %q: %w", dir, err)
		}
		if err := createEmptyCommit(repo); err != nil {
			return err
		}

		// Delete go-git default branch
		_ = repo.Storer.RemoveReference(plumbing.Master)
	}

	server, err := git.NewGitServer(repo)
	if err != nil {
		return fmt.Errorf("filed to initialize git server: %w", err)
	}

	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()

	addressChannel := make(chan net.Addr)

	go func() {
		if err := server.ListenAndServe(ctx, fmt.Sprintf(":%d", *port), addressChannel); err != nil && err != http.ErrServerClosed {
			klog.Fatalf("Listen failed: %v", err)
		}
	}()

	address := <-addressChannel
	fmt.Fprintf(os.Stderr, "Listening on %s\n", address)

	wait := make(chan os.Signal, 1)
	signal.Notify(wait, os.Interrupt)

	<-wait

	return nil
}

func createEmptyCommit(repo *gogit.Repository) error {
	store := repo.Storer
	// Create first commit using empty tree.
	emptyTree := object.Tree{}
	encodedTree := store.NewEncodedObject()
	if err := emptyTree.Encode(encodedTree); err != nil {
		return fmt.Errorf("failed to encode initial empty commit tree: %w", err)
	}

	treeHash, err := store.SetEncodedObject(encodedTree)
	if err != nil {
		return fmt.Errorf("failed to create initial empty commit tree: %w", err)
	}

	sig := object.Signature{
		Name:  "Git Server",
		Email: "git-server@kpt.dev",
		When:  time.Now(),
	}

	commit := object.Commit{
		Author:       sig,
		Committer:    sig,
		Message:      "Empty Commit",
		TreeHash:     treeHash,
		ParentHashes: []plumbing.Hash{}, // No parents
	}

	encodedCommit := store.NewEncodedObject()
	if err := commit.Encode(encodedCommit); err != nil {
		return fmt.Errorf("failed to encode initial empty commit: %w", err)
	}

	commitHash, err := store.SetEncodedObject(encodedCommit)
	if err != nil {
		return fmt.Errorf("failed to create initial empty commit: %w", err)
	}

	main := plumbing.NewHashReference(plumbing.ReferenceName("refs/heads/main"), commitHash)
	if err := repo.Storer.SetReference(main); err != nil {
		return fmt.Errorf("failed to set refs/heads/main to commit sha %s: %w", commitHash, err)
	}

	head := plumbing.NewSymbolicReference(plumbing.HEAD, "refs/heads/main")
	if err := repo.Storer.SetReference(head); err != nil {
		return fmt.Errorf("failed to set HEAD to refs/heads/main: %w", err)
	}

	return nil
}
<|endoftext|>


<commit_before>package discovery

import (
	"errors"
	"fmt"
	"net"
	"net/url"
	"os"
	"strings"

	kv "gopkg.in/Clever/kayvee-go.v2"
)

// m is a convenience type for using kv.
type m map[string]interface{}

const (
	templateVar = "SERVICE_%s_%s_%%s"
)

func getVar(envVar string) (string, error) {
	envVar = strings.ToUpper(envVar)
	envVar = strings.Replace(envVar, "-", "_", -1)
	val := os.Getenv(envVar)
	if val == "" {
		return "", errors.New(kv.FormatLog("discovery-go", kv.Error, "missing.env.var", m{
			"var": envVar,
		}))
	}
	return val, nil
}

// URL finds the specified URL for a service based off of the service's name and which
// interface you are accessing. Values are found in environment variables fitting the scheme:
// SERVICE_{SERVICE NAME}_{INTERFACE NAME}_{PROTO,HOST,PORT}.
func URL(service, name string) (string, error) {
	proto, err := Proto(service, name)
	if err != nil {
		return "", err
	}
	host, err := Host(service, name)
	if err != nil {
		return "", err
	}
	port, err := Port(service, name)
	if err != nil {
		return "", err
	}

	rawURL := fmt.Sprintf("%s://%s:%s", proto, host, port)
	u, err := url.Parse(rawURL)
	if err != nil {
		return "", errors.New(kv.FormatLog("discovery-go", kv.Error, "missing env var", m{
			"url":   rawURL,
			"error": fmt.Errorf("Failed to parse URL: %s", err.Error()),
		}))
	}
	return u.String(), nil
}

// HostPort finds the specified host:port combo for a service based off of the service's name and
// which interface you are accessing. Values are found in environment variables fitting the scheme:
// SERVICE_{SERVICE NAME}_{INTERFACE NAME}_{PROTO,HOST,PORT}.
func HostPort(service, name string) (string, error) {
	host, err := Host(service, name)
	if err != nil {
		return "", err
	}
	port, err := Port(service, name)
	if err != nil {
		return "", err
	}

	return net.JoinHostPort(host, port), nil
}

// Proto finds the specified protocol for a service based off of the service's name and which
// interface you are accessing. Values are found in environment variables fitting the scheme:
// SERVICE_{SERVICE NAME}_{INTERFACE NAME}_PROTO.
func Proto(service, name string) (string, error) {
	template := fmt.Sprintf(templateVar, service, name)
	return getVar(fmt.Sprintf(template, "PROTO"))
}

// Host finds the specified host for a service based off of the service's name and which
// interface you are accessing. Values are found in environment variables fitting the scheme:
// SERVICE_{SERVICE NAME}_{INTERFACE NAME}_HOST.
func Host(service, name string) (string, error) {
	template := fmt.Sprintf(templateVar, service, name)
	return getVar(fmt.Sprintf(template, "HOST"))
}

// Port finds the specified port for a service based off of the service's name and which
// interface you are accessing. Values are found in environment variables fitting the scheme:
// SERVICE_{SERVICE NAME}_{INTERFACE NAME}_PORT.
func Port(service, name string) (string, error) {
	template := fmt.Sprintf(templateVar, service, name)
	return getVar(fmt.Sprintf(template, "PORT"))
}
<commit_msg>kayvee v2 --> v3<commit_after>package discovery

import (
	"errors"
	"fmt"
	"net"
	"net/url"
	"os"
	"strings"

	"gopkg.in/Clever/kayvee-go.v3"
	"gopkg.in/Clever/kayvee-go.v3/logger"
)

const (
	templateVar = "SERVICE_%s_%s_%%s"
)

func getVar(envVar string) (string, error) {
	envVar = strings.ToUpper(envVar)
	envVar = strings.Replace(envVar, "-", "_", -1)
	val := os.Getenv(envVar)
	if val == "" {
		return "", errors.New(kayvee.FormatLog("discovery-go", kayvee.Error, "missing.env.var", logger.M{
			"var": envVar,
		}))
	}
	return val, nil
}

// URL finds the specified URL for a service based off of the service's name and which
// interface you are accessing. Values are found in environment variables fitting the scheme:
// SERVICE_{SERVICE NAME}_{INTERFACE NAME}_{PROTO,HOST,PORT}.
func URL(service, name string) (string, error) {
	proto, err := Proto(service, name)
	if err != nil {
		return "", err
	}
	host, err := Host(service, name)
	if err != nil {
		return "", err
	}
	port, err := Port(service, name)
	if err != nil {
		return "", err
	}

	rawURL := fmt.Sprintf("%s://%s:%s", proto, host, port)
	u, err := url.Parse(rawURL)
	if err != nil {
		return "", errors.New(kayvee.FormatLog("discovery-go", kayvee.Error, "missing env var", logger.M{
			"url":   rawURL,
			"error": fmt.Errorf("Failed to parse URL: %s", err.Error()),
		}))
	}
	return u.String(), nil
}

// HostPort finds the specified host:port combo for a service based off of the service's name and
// which interface you are accessing. Values are found in environment variables fitting the scheme:
// SERVICE_{SERVICE NAME}_{INTERFACE NAME}_{PROTO,HOST,PORT}.
func HostPort(service, name string) (string, error) {
	host, err := Host(service, name)
	if err != nil {
		return "", err
	}
	port, err := Port(service, name)
	if err != nil {
		return "", err
	}

	return net.JoinHostPort(host, port), nil
}

// Proto finds the specified protocol for a service based off of the service's name and which
// interface you are accessing. Values are found in environment variables fitting the scheme:
// SERVICE_{SERVICE NAME}_{INTERFACE NAME}_PROTO.
func Proto(service, name string) (string, error) {
	template := fmt.Sprintf(templateVar, service, name)
	return getVar(fmt.Sprintf(template, "PROTO"))
}

// Host finds the specified host for a service based off of the service's name and which
// interface you are accessing. Values are found in environment variables fitting the scheme:
// SERVICE_{SERVICE NAME}_{INTERFACE NAME}_HOST.
func Host(service, name string) (string, error) {
	template := fmt.Sprintf(templateVar, service, name)
	return getVar(fmt.Sprintf(template, "HOST"))
}

// Port finds the specified port for a service based off of the service's name and which
// interface you are accessing. Values are found in environment variables fitting the scheme:
// SERVICE_{SERVICE NAME}_{INTERFACE NAME}_PORT.
func Port(service, name string) (string, error) {
	template := fmt.Sprintf(templateVar, service, name)
	return getVar(fmt.Sprintf(template, "PORT"))
}
<|endoftext|>


<commit_before>package disk

import "io"

func ReadBlock(file io.ReadSeeker, index, bs int64) ([]byte, error) {

}

func WriteBlock(file io.WriteSeeker, index, bs int64, data []byte) error {

}
<commit_msg>nil return<commit_after>package disk

import "io"

func ReadBlock(file io.ReadSeeker, index, bs int64) ([]byte, error) {
	return nil, nil
}

func WriteBlock(file io.WriteSeeker, index, bs int64, data []byte) error {
	return nil
}
<|endoftext|>