<commit_before>package main

import (
	"flag"
	"fmt"
	"os"
	"sync"
	"time"

	"github.com/coreos/coreos-cloudinit/datasource"
	"github.com/coreos/coreos-cloudinit/initialize"
	"github.com/coreos/coreos-cloudinit/pkg"
	"github.com/coreos/coreos-cloudinit/system"
)

const (
	version               = "0.8.7+git"
	datasourceInterval    = 100 * time.Millisecond
	datasourceMaxInterval = 30 * time.Second
	datasourceTimeout     = 5 * time.Minute
)

var (
	printVersion  bool
	ignoreFailure bool
	sources       struct {
		file            string
		configDrive     string
		metadataService bool
		url             string
		procCmdLine     bool
	}
	convertNetconf string
	workspace      string
	sshKeyName     string
)

func init() {
	flag.BoolVar(&printVersion, "version", false, "Print the version and exit")
	flag.BoolVar(&ignoreFailure, "ignore-failure", false, "Exits with 0 status in the event of malformed input from user-data")
	flag.StringVar(&sources.file, "from-file", "", "Read user-data from provided file")
	flag.StringVar(&sources.configDrive, "from-configdrive", "", "Read data from provided cloud-drive directory")
	flag.BoolVar(&sources.metadataService, "from-metadata-service", false, "Download data from metadata service")
	flag.StringVar(&sources.url, "from-url", "", "Download user-data from provided url")
	flag.BoolVar(&sources.procCmdLine, "from-proc-cmdline", false, fmt.Sprintf("Parse %s for '%s=<url>', using the cloud-config served by an HTTP GET to <url>", datasource.ProcCmdlineLocation, datasource.ProcCmdlineCloudConfigFlag))
	flag.StringVar(&convertNetconf, "convert-netconf", "", "Read the network config provided in cloud-drive and translate it from the specified format into networkd unit files (requires the -from-configdrive flag)")
	flag.StringVar(&workspace, "workspace", "/var/lib/coreos-cloudinit", "Base directory coreos-cloudinit should use to store data")
	flag.StringVar(&sshKeyName, "ssh-key-name", initialize.DefaultSSHKeyName, "Add SSH keys to the system with the given name")
}

func main() {
	flag.Parse()

	die := func() {
		if ignoreFailure {
			os.Exit(0)
		}
		os.Exit(1)
	}

	if printVersion == true {
		fmt.Printf("coreos-cloudinit version %s\n", version)
		os.Exit(0)
	}

	if convertNetconf != "" && sources.configDrive == "" {
		fmt.Println("-convert-netconf flag requires -from-configdrive")
		os.Exit(1)
	}

	switch convertNetconf {
	case "":
	case "debian":
	default:
		fmt.Printf("Invalid option to -convert-netconf: '%s'. Supported options: 'debian'\n", convertNetconf)
		os.Exit(1)
	}

	dss := getDatasources()
	if len(dss) == 0 {
		fmt.Println("Provide at least one of --from-file, --from-configdrive, --from-metadata-service, --from-url or --from-proc-cmdline")
		os.Exit(1)
	}

	ds := selectDatasource(dss)
	if ds == nil {
		fmt.Println("No datasources available in time")
		die()
	}

	fmt.Printf("Fetching user-data from datasource of type %q\n", ds.Type())
	userdataBytes, err := ds.FetchUserdata()
	if err != nil {
		fmt.Printf("Failed fetching user-data from datasource: %v\n", err)
		die()
	}

	fmt.Printf("Fetching meta-data from datasource of type %q\n", ds.Type())
	metadataBytes, err := ds.FetchMetadata()
	if err != nil {
		fmt.Printf("Failed fetching meta-data from datasource: %v\n", err)
		die()
	}

	// Extract IPv4 addresses from metadata if possible
	var subs map[string]string
	if len(metadataBytes) > 0 {
		subs, err = initialize.ExtractIPsFromMetadata(metadataBytes)
		if err != nil {
			fmt.Printf("Failed extracting IPs from meta-data: %v\n", err)
			die()
		}
	}

	// Apply environment to user-data
	env := initialize.NewEnvironment("/", ds.ConfigRoot(), workspace, convertNetconf, sshKeyName, subs)
	userdata := env.Apply(string(userdataBytes))

	var ccm, ccu *initialize.CloudConfig
	var script *system.Script
	if ccm, err = initialize.ParseMetaData(string(metadataBytes)); err != nil {
		fmt.Printf("Failed to parse meta-data: %v\n", err)
		die()
	}
	if ud, err := initialize.ParseUserData(userdata); err != nil {
		fmt.Printf("Failed to parse user-data: %v\n", err)
		die()
	} else {
		switch t := ud.(type) {
		case *initialize.CloudConfig:
			ccu = t
		case system.Script:
			script = &t
		}
	}

	var cc *initialize.CloudConfig
	if ccm != nil && ccu != nil {
		fmt.Println("Merging cloud-config from meta-data and user-data")
		merged := mergeCloudConfig(*ccm, *ccu)
		cc = &merged
	} else if ccm != nil && ccu == nil {
		fmt.Println("Processing cloud-config from meta-data")
		cc = ccm
	} else if ccm == nil && ccu != nil {
		fmt.Println("Processing cloud-config from user-data")
		cc = ccu
	} else {
		fmt.Println("No cloud-config data to handle.")
	}

	if cc != nil {
		if err = initialize.Apply(*cc, env); err != nil {
			fmt.Printf("Failed to apply cloud-config: %v\n", err)
			die()
		}
	}

	if script != nil {
		if err = runScript(*script, env); err != nil {
			fmt.Printf("Failed to run script: %v\n", err)
			die()
		}
	}
}

// mergeCloudConfig merges certain options from mdcc (a CloudConfig derived from
// meta-data) onto udcc (a CloudConfig derived from user-data), if they are
// not already set on udcc (i.e. user-data always takes precedence)
// NB: This needs to be kept in sync with ParseMetadata so that it tracks all
// elements of a CloudConfig which that function can populate.
func mergeCloudConfig(mdcc, udcc initialize.CloudConfig) (cc initialize.CloudConfig) {
	if mdcc.Hostname != "" {
		if udcc.Hostname != "" {
			fmt.Printf("Warning: user-data hostname (%s) overrides metadata hostname (%s)", udcc.Hostname, mdcc.Hostname)
		} else {
			udcc.Hostname = mdcc.Hostname
		}

	}
	for _, key := range mdcc.SSHAuthorizedKeys {
		udcc.SSHAuthorizedKeys = append(udcc.SSHAuthorizedKeys, key)
	}
	if mdcc.NetworkConfigPath != "" {
		if udcc.NetworkConfigPath != "" {
			fmt.Printf("Warning: user-data NetworkConfigPath %s overrides metadata NetworkConfigPath %s", udcc.NetworkConfigPath, mdcc.NetworkConfigPath)
		} else {
			udcc.NetworkConfigPath = mdcc.NetworkConfigPath
		}
	}
	return udcc
}

// getDatasources creates a slice of possible Datasources for cloudinit based
// on the different source command-line flags.
func getDatasources() []datasource.Datasource {
	dss := make([]datasource.Datasource, 0, 5)
	if sources.file != "" {
		dss = append(dss, datasource.NewLocalFile(sources.file))
	}
	if sources.url != "" {
		dss = append(dss, datasource.NewRemoteFile(sources.url))
	}
	if sources.configDrive != "" {
		dss = append(dss, datasource.NewConfigDrive(sources.configDrive))
	}
	if sources.metadataService {
		dss = append(dss, datasource.NewMetadataService())
	}
	if sources.procCmdLine {
		dss = append(dss, datasource.NewProcCmdline())
	}
	return dss
}

// selectDatasource attempts to choose a valid Datasource to use based on its
// current availability. The first Datasource to report to be available is
// returned. Datasources will be retried if possible if they are not
// immediately available. If all Datasources are permanently unavailable or
// datasourceTimeout is reached before one becomes available, nil is returned.
func selectDatasource(sources []datasource.Datasource) datasource.Datasource {
	ds := make(chan datasource.Datasource)
	stop := make(chan struct{})
	var wg sync.WaitGroup

	for _, s := range sources {
		wg.Add(1)
		go func(s datasource.Datasource) {
			defer wg.Done()

			duration := datasourceInterval
			for {
				fmt.Printf("Checking availability of %q\n", s.Type())
				if s.IsAvailable() {
					ds <- s
					return
				} else if !s.AvailabilityChanges() {
					return
				}
				select {
				case <-stop:
					return
				case <-time.Tick(duration):
					duration = pkg.ExpBackoff(duration, datasourceMaxInterval)
				}
			}
		}(s)
	}

	done := make(chan struct{})
	go func() {
		wg.Wait()
		close(done)
	}()

	var s datasource.Datasource
	select {
	case s = <-ds:
	case <-done:
	case <-time.Tick(datasourceTimeout):
	}

	close(stop)
	return s
}

// TODO(jonboulle): this should probably be refactored and moved into a different module
func runScript(script system.Script, env *initialize.Environment) error {
	err := initialize.PrepWorkspace(env.Workspace())
	if err != nil {
		fmt.Printf("Failed preparing workspace: %v\n", err)
		return err
	}
	path, err := initialize.PersistScriptInWorkspace(script, env.Workspace())
	if err == nil {
		var name string
		name, err = system.ExecuteScript(path)
		initialize.PersistUnitNameInWorkspace(name, env.Workspace())
	}
	return err
}
<commit_msg>coreos-cloudinit: bump to 0.8.8<commit_after>package main

import (
	"flag"
	"fmt"
	"os"
	"sync"
	"time"

	"github.com/coreos/coreos-cloudinit/datasource"
	"github.com/coreos/coreos-cloudinit/initialize"
	"github.com/coreos/coreos-cloudinit/pkg"
	"github.com/coreos/coreos-cloudinit/system"
)

const (
	version               = "0.8.8"
	datasourceInterval    = 100 * time.Millisecond
	datasourceMaxInterval = 30 * time.Second
	datasourceTimeout     = 5 * time.Minute
)

var (
	printVersion  bool
	ignoreFailure bool
	sources       struct {
		file            string
		configDrive     string
		metadataService bool
		url             string
		procCmdLine     bool
	}
	convertNetconf string
	workspace      string
	sshKeyName     string
)

func init() {
	flag.BoolVar(&printVersion, "version", false, "Print the version and exit")
	flag.BoolVar(&ignoreFailure, "ignore-failure", false, "Exits with 0 status in the event of malformed input from user-data")
	flag.StringVar(&sources.file, "from-file", "", "Read user-data from provided file")
	flag.StringVar(&sources.configDrive, "from-configdrive", "", "Read data from provided cloud-drive directory")
	flag.BoolVar(&sources.metadataService, "from-metadata-service", false, "Download data from metadata service")
	flag.StringVar(&sources.url, "from-url", "", "Download user-data from provided url")
	flag.BoolVar(&sources.procCmdLine, "from-proc-cmdline", false, fmt.Sprintf("Parse %s for '%s=<url>', using the cloud-config served by an HTTP GET to <url>", datasource.ProcCmdlineLocation, datasource.ProcCmdlineCloudConfigFlag))
	flag.StringVar(&convertNetconf, "convert-netconf", "", "Read the network config provided in cloud-drive and translate it from the specified format into networkd unit files (requires the -from-configdrive flag)")
	flag.StringVar(&workspace, "workspace", "/var/lib/coreos-cloudinit", "Base directory coreos-cloudinit should use to store data")
	flag.StringVar(&sshKeyName, "ssh-key-name", initialize.DefaultSSHKeyName, "Add SSH keys to the system with the given name")
}

func main() {
	flag.Parse()

	die := func() {
		if ignoreFailure {
			os.Exit(0)
		}
		os.Exit(1)
	}

	if printVersion == true {
		fmt.Printf("coreos-cloudinit version %s\n", version)
		os.Exit(0)
	}

	if convertNetconf != "" && sources.configDrive == "" {
		fmt.Println("-convert-netconf flag requires -from-configdrive")
		os.Exit(1)
	}

	switch convertNetconf {
	case "":
	case "debian":
	default:
		fmt.Printf("Invalid option to -convert-netconf: '%s'. Supported options: 'debian'\n", convertNetconf)
		os.Exit(1)
	}

	dss := getDatasources()
	if len(dss) == 0 {
		fmt.Println("Provide at least one of --from-file, --from-configdrive, --from-metadata-service, --from-url or --from-proc-cmdline")
		os.Exit(1)
	}

	ds := selectDatasource(dss)
	if ds == nil {
		fmt.Println("No datasources available in time")
		die()
	}

	fmt.Printf("Fetching user-data from datasource of type %q\n", ds.Type())
	userdataBytes, err := ds.FetchUserdata()
	if err != nil {
		fmt.Printf("Failed fetching user-data from datasource: %v\n", err)
		die()
	}

	fmt.Printf("Fetching meta-data from datasource of type %q\n", ds.Type())
	metadataBytes, err := ds.FetchMetadata()
	if err != nil {
		fmt.Printf("Failed fetching meta-data from datasource: %v\n", err)
		die()
	}

	// Extract IPv4 addresses from metadata if possible
	var subs map[string]string
	if len(metadataBytes) > 0 {
		subs, err = initialize.ExtractIPsFromMetadata(metadataBytes)
		if err != nil {
			fmt.Printf("Failed extracting IPs from meta-data: %v\n", err)
			die()
		}
	}

	// Apply environment to user-data
	env := initialize.NewEnvironment("/", ds.ConfigRoot(), workspace, convertNetconf, sshKeyName, subs)
	userdata := env.Apply(string(userdataBytes))

	var ccm, ccu *initialize.CloudConfig
	var script *system.Script
	if ccm, err = initialize.ParseMetaData(string(metadataBytes)); err != nil {
		fmt.Printf("Failed to parse meta-data: %v\n", err)
		die()
	}
	if ud, err := initialize.ParseUserData(userdata); err != nil {
		fmt.Printf("Failed to parse user-data: %v\n", err)
		die()
	} else {
		switch t := ud.(type) {
		case *initialize.CloudConfig:
			ccu = t
		case system.Script:
			script = &t
		}
	}

	var cc *initialize.CloudConfig
	if ccm != nil && ccu != nil {
		fmt.Println("Merging cloud-config from meta-data and user-data")
		merged := mergeCloudConfig(*ccm, *ccu)
		cc = &merged
	} else if ccm != nil && ccu == nil {
		fmt.Println("Processing cloud-config from meta-data")
		cc = ccm
	} else if ccm == nil && ccu != nil {
		fmt.Println("Processing cloud-config from user-data")
		cc = ccu
	} else {
		fmt.Println("No cloud-config data to handle.")
	}

	if cc != nil {
		if err = initialize.Apply(*cc, env); err != nil {
			fmt.Printf("Failed to apply cloud-config: %v\n", err)
			die()
		}
	}

	if script != nil {
		if err = runScript(*script, env); err != nil {
			fmt.Printf("Failed to run script: %v\n", err)
			die()
		}
	}
}

// mergeCloudConfig merges certain options from mdcc (a CloudConfig derived from
// meta-data) onto udcc (a CloudConfig derived from user-data), if they are
// not already set on udcc (i.e. user-data always takes precedence)
// NB: This needs to be kept in sync with ParseMetadata so that it tracks all
// elements of a CloudConfig which that function can populate.
func mergeCloudConfig(mdcc, udcc initialize.CloudConfig) (cc initialize.CloudConfig) {
	if mdcc.Hostname != "" {
		if udcc.Hostname != "" {
			fmt.Printf("Warning: user-data hostname (%s) overrides metadata hostname (%s)", udcc.Hostname, mdcc.Hostname)
		} else {
			udcc.Hostname = mdcc.Hostname
		}

	}
	for _, key := range mdcc.SSHAuthorizedKeys {
		udcc.SSHAuthorizedKeys = append(udcc.SSHAuthorizedKeys, key)
	}
	if mdcc.NetworkConfigPath != "" {
		if udcc.NetworkConfigPath != "" {
			fmt.Printf("Warning: user-data NetworkConfigPath %s overrides metadata NetworkConfigPath %s", udcc.NetworkConfigPath, mdcc.NetworkConfigPath)
		} else {
			udcc.NetworkConfigPath = mdcc.NetworkConfigPath
		}
	}
	return udcc
}

// getDatasources creates a slice of possible Datasources for cloudinit based
// on the different source command-line flags.
func getDatasources() []datasource.Datasource {
	dss := make([]datasource.Datasource, 0, 5)
	if sources.file != "" {
		dss = append(dss, datasource.NewLocalFile(sources.file))
	}
	if sources.url != "" {
		dss = append(dss, datasource.NewRemoteFile(sources.url))
	}
	if sources.configDrive != "" {
		dss = append(dss, datasource.NewConfigDrive(sources.configDrive))
	}
	if sources.metadataService {
		dss = append(dss, datasource.NewMetadataService())
	}
	if sources.procCmdLine {
		dss = append(dss, datasource.NewProcCmdline())
	}
	return dss
}

// selectDatasource attempts to choose a valid Datasource to use based on its
// current availability. The first Datasource to report to be available is
// returned. Datasources will be retried if possible if they are not
// immediately available. If all Datasources are permanently unavailable or
// datasourceTimeout is reached before one becomes available, nil is returned.
func selectDatasource(sources []datasource.Datasource) datasource.Datasource {
	ds := make(chan datasource.Datasource)
	stop := make(chan struct{})
	var wg sync.WaitGroup

	for _, s := range sources {
		wg.Add(1)
		go func(s datasource.Datasource) {
			defer wg.Done()

			duration := datasourceInterval
			for {
				fmt.Printf("Checking availability of %q\n", s.Type())
				if s.IsAvailable() {
					ds <- s
					return
				} else if !s.AvailabilityChanges() {
					return
				}
				select {
				case <-stop:
					return
				case <-time.Tick(duration):
					duration = pkg.ExpBackoff(duration, datasourceMaxInterval)
				}
			}
		}(s)
	}

	done := make(chan struct{})
	go func() {
		wg.Wait()
		close(done)
	}()

	var s datasource.Datasource
	select {
	case s = <-ds:
	case <-done:
	case <-time.Tick(datasourceTimeout):
	}

	close(stop)
	return s
}

// TODO(jonboulle): this should probably be refactored and moved into a different module
func runScript(script system.Script, env *initialize.Environment) error {
	err := initialize.PrepWorkspace(env.Workspace())
	if err != nil {
		fmt.Printf("Failed preparing workspace: %v\n", err)
		return err
	}
	path, err := initialize.PersistScriptInWorkspace(script, env.Workspace())
	if err == nil {
		var name string
		name, err = system.ExecuteScript(path)
		initialize.PersistUnitNameInWorkspace(name, env.Workspace())
	}
	return err
}
<|endoftext|>


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

package bb

import (
	"os/exec"
	"path/filepath"
	"testing"

	"github.com/u-root/u-root/pkg/golang"
)

// Turn this off until we are done moving to modules.
func testPackageRewriteFile(t *testing.T) {
	bin := filepath.Join(t.TempDir(), "foo")
	if err := BuildBusybox(golang.Default(), []string{"github.com/u-root/u-root/pkg/uroot/test/foo"}, false, bin); err != nil {
		t.Fatal(err)
	}

	cmd := exec.Command(bin)
	o, err := cmd.CombinedOutput()
	if err != nil {
		t.Fatalf("foo failed: %v %v", string(o), err)
	}
}
<commit_msg>Re-enable pkg/bb test.<commit_after>// Copyright 2018 the u-root Authors. All rights reserved
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package bb

import (
	"os/exec"
	"path/filepath"
	"testing"

	"github.com/u-root/u-root/pkg/golang"
)

func TestPackageRewriteFile(t *testing.T) {
	bin := filepath.Join(t.TempDir(), "foo")
	if err := BuildBusybox(golang.Default(), []string{"github.com/u-root/u-root/pkg/uroot/test/foo"}, false, bin); err != nil {
		t.Fatal(err)
	}

	cmd := exec.Command(bin)
	o, err := cmd.CombinedOutput()
	if err != nil {
		t.Fatalf("foo failed: %v %v", string(o), err)
	}
}
<|endoftext|>


<commit_before>package geo

import (
	"bufio"
	"bytes"
	"errors"
	"fmt"
	"math"
	"strconv"
	"strings"

	"github.com/golang/geo/s2"
	dspb "github.com/steeling/InterUSS-Platform/pkg/dssproto"
)

const (
	// DefaultMinimumCellLevel is the default minimum cell level, chosen such
	// that the minimum cell size is ~1km^2.
	DefaultMinimumCellLevel int = 13
	// DefaultMaximumCellLevel is the default minimum cell level, chosen such
	// that the maximum cell size is ~1km^2.
	DefaultMaximumCellLevel int = 13
	maxAllowedAreaKm2           = 2500.0
)

var (
	// defaultRegionCoverer is the default s2.RegionCoverer for mapping areas
	// and extents to s2.CellUnion instances.
	defaultRegionCoverer = &s2.RegionCoverer{
		MinLevel: DefaultMinimumCellLevel,
		MaxLevel: DefaultMaximumCellLevel,
	}
	// RegionCoverer provides an overridable interface to defaultRegionCoverer
	RegionCoverer = defaultRegionCoverer

	errOddNumberOfCoordinatesInAreaString = errors.New("odd number of coordinates in area string")
	errNotEnoughPointsInPolygon           = errors.New("not enough points in polygon")
	errBadCoordSet                        = errors.New("coordinates did not create a well formed area")
)

func splitAtComma(data []byte, atEOF bool) (int, []byte, error) {
	if atEOF && len(data) == 0 {
		return 0, nil, nil
	}

	if i := bytes.IndexByte(data, ','); i >= 0 {
		return i + 1, data[:i], nil
	}

	if atEOF {
		return len(data), data, nil
	}

	return 0, nil, nil
}

func Volume4DToCellIDs(v4 *dspb.Volume4D) (s2.CellUnion, error) {
	if v4 == nil {
		return nil, errBadCoordSet
	}
	return Volume3DToCellIDs(v4.SpatialVolume)
}

func Volume3DToCellIDs(v3 *dspb.Volume3D) (s2.CellUnion, error) {
	if v3 == nil {
		return nil, errBadCoordSet
	}
	return GeoPolygonToCellIDs(v3.Footprint)
}

func GeoPolygonToCellIDs(geopolygon *dspb.GeoPolygon) (s2.CellUnion, error) {
	var points []s2.Point
	if geopolygon == nil {
		return nil, errBadCoordSet
	}
	for _, ltlng := range geopolygon.Vertices {
		points = append(points, s2.PointFromLatLng(s2.LatLngFromDegrees(ltlng.Lat, ltlng.Lng)))
	}
	loop := s2.LoopFromPoints(points)

	return Covering(loop)
}

func loopAreaToKm2(loopArea float64) float64 {
	const earthAreaKm2 = 510072000.0 // rough area of the earth in KM².
	return (loopArea * earthAreaKm2) / 4.0 * math.Pi
}

func Covering(loop *s2.Loop) (s2.CellUnion, error) {
	// TODO(steeling): consider setting max number of vertices.
	loopArea := loop.Area()
	if loopArea <= 0 {
		return nil, errBadCoordSet
	}
	if loopAreaToKm2(loopArea) > maxAllowedAreaKm2 {
		return nil, fmt.Errorf("area is too large (%fkm² > %fkm²)", loopAreaToKm2(loopArea), maxAllowedAreaKm2)
	}
	return RegionCoverer.Covering(loop), nil
}

// AreaToCellIDs parses "area" in the format 'lat0,lon0,lat1,lon1,...'
// and returns the resulting s2.CellUnion.
//
// TODO(tvoss):
//   * Agree and implement a maximum number of points in area
func AreaToCellIDs(area string) (s2.CellUnion, error) {
	var (
		lat, lng = float64(0), float64(0)
		points   = []s2.Point{}
		counter  = 0
		scanner  = bufio.NewScanner(strings.NewReader(area))
	)
	numCoords := strings.Count(area, ",") + 1
	if numCoords%2 == 1 {
		return nil, errOddNumberOfCoordinatesInAreaString
	}
	if numCoords/2 < 3 {
		return nil, errNotEnoughPointsInPolygon
	}
	scanner.Split(splitAtComma)

	for scanner.Scan() {
		trimmed := strings.TrimSpace(scanner.Text())
		switch counter % 2 {
		case 0:
			f, err := strconv.ParseFloat(trimmed, 64)
			if err != nil {
				return nil, errBadCoordSet
			}
			lat = f
		case 1:
			f, err := strconv.ParseFloat(trimmed, 64)
			if err != nil {
				return nil, errBadCoordSet
			}
			lng = f
			points = append(points, s2.PointFromLatLng(s2.LatLngFromDegrees(lat, lng)))
		}

		counter++
	}
	loopAttempt := s2.LoopFromPoints(points)
	if loopAreaToKm2(loopAttempt.Area()) > maxAllowedAreaKm2 {
		// This probably happened because the vertices were not ordered counter-clockwise.
		// We can try reversing to see if that's the case.
		for i, j := 0, len(points)-1; i < j; i, j = i+1, j-1 {
			points[i], points[j] = points[j], points[i]
		}
		loopAttempt = s2.LoopFromPoints(points)
	}
	return Covering(loopAttempt)
}
<commit_msg>Do the polygon winding check in both places.<commit_after>package geo

import (
	"bufio"
	"bytes"
	"errors"
	"fmt"
	"math"
	"strconv"
	"strings"

	"github.com/golang/geo/s2"
	dspb "github.com/steeling/InterUSS-Platform/pkg/dssproto"
)

const (
	// DefaultMinimumCellLevel is the default minimum cell level, chosen such
	// that the minimum cell size is ~1km^2.
	DefaultMinimumCellLevel int = 13
	// DefaultMaximumCellLevel is the default minimum cell level, chosen such
	// that the maximum cell size is ~1km^2.
	DefaultMaximumCellLevel int = 13
	maxAllowedAreaKm2           = 2500.0
)

var (
	// defaultRegionCoverer is the default s2.RegionCoverer for mapping areas
	// and extents to s2.CellUnion instances.
	defaultRegionCoverer = &s2.RegionCoverer{
		MinLevel: DefaultMinimumCellLevel,
		MaxLevel: DefaultMaximumCellLevel,
	}
	// RegionCoverer provides an overridable interface to defaultRegionCoverer
	RegionCoverer = defaultRegionCoverer

	errOddNumberOfCoordinatesInAreaString = errors.New("odd number of coordinates in area string")
	errNotEnoughPointsInPolygon           = errors.New("not enough points in polygon")
	errBadCoordSet                        = errors.New("coordinates did not create a well formed area")
)

func splitAtComma(data []byte, atEOF bool) (int, []byte, error) {
	if atEOF && len(data) == 0 {
		return 0, nil, nil
	}

	if i := bytes.IndexByte(data, ','); i >= 0 {
		return i + 1, data[:i], nil
	}

	if atEOF {
		return len(data), data, nil
	}

	return 0, nil, nil
}

func Volume4DToCellIDs(v4 *dspb.Volume4D) (s2.CellUnion, error) {
	if v4 == nil {
		return nil, errBadCoordSet
	}
	return Volume3DToCellIDs(v4.SpatialVolume)
}

func Volume3DToCellIDs(v3 *dspb.Volume3D) (s2.CellUnion, error) {
	if v3 == nil {
		return nil, errBadCoordSet
	}
	return GeoPolygonToCellIDs(v3.Footprint)
}

func GeoPolygonToCellIDs(geopolygon *dspb.GeoPolygon) (s2.CellUnion, error) {
	var points []s2.Point
	if geopolygon == nil {
		return nil, errBadCoordSet
	}
	for _, ltlng := range geopolygon.Vertices {
		points = append(points, s2.PointFromLatLng(s2.LatLngFromDegrees(ltlng.Lat, ltlng.Lng)))
	}
	return Covering(points)
}

func loopAreaKm2(loop *s2.Loop) float64 {
	const earthAreaKm2 = 510072000.0 // rough area of the earth in KM².
	return (loop.Area() * earthAreaKm2) / 4.0 * math.Pi
}

func Covering(points []s2.Point) (s2.CellUnion, error) {
	loop := s2.LoopFromPoints(points)
	if loopAreaKm2(loop) <= maxAllowedAreaKm2 {
		return RegionCoverer.Covering(loop), nil
	}

	// This probably happened because the vertices were not ordered counter-clockwise.
	// We can try reversing to see if that's the case.
	for i, j := 0, len(points)-1; i < j; i, j = i+1, j-1 {
		points[i], points[j] = points[j], points[i]
	}
	loop = s2.LoopFromPoints(points)
	if loopAreaKm2(loop) <= maxAllowedAreaKm2 {
		return RegionCoverer.Covering(loop), nil
	}

	return nil, fmt.Errorf("area is too large (%fkm² > %fkm²)", loopAreaKm2(loop), maxAllowedAreaKm2)
}

// AreaToCellIDs parses "area" in the format 'lat0,lon0,lat1,lon1,...'
// and returns the resulting s2.CellUnion.
//
// TODO(tvoss):
//   * Agree and implement a maximum number of points in area
func AreaToCellIDs(area string) (s2.CellUnion, error) {
	var (
		lat, lng = float64(0), float64(0)
		points   = []s2.Point{}
		counter  = 0
		scanner  = bufio.NewScanner(strings.NewReader(area))
	)
	numCoords := strings.Count(area, ",") + 1
	if numCoords%2 == 1 {
		return nil, errOddNumberOfCoordinatesInAreaString
	}
	if numCoords/2 < 3 {
		return nil, errNotEnoughPointsInPolygon
	}
	scanner.Split(splitAtComma)

	for scanner.Scan() {
		trimmed := strings.TrimSpace(scanner.Text())
		switch counter % 2 {
		case 0:
			f, err := strconv.ParseFloat(trimmed, 64)
			if err != nil {
				return nil, errBadCoordSet
			}
			lat = f
		case 1:
			f, err := strconv.ParseFloat(trimmed, 64)
			if err != nil {
				return nil, errBadCoordSet
			}
			lng = f
			points = append(points, s2.PointFromLatLng(s2.LatLngFromDegrees(lat, lng)))
		}

		counter++
	}
	return Covering(points)
}
<|endoftext|>


<commit_before>// Copyright 2018 The nvim-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.

package nctx

import (
	"context"
	"fmt"

	"github.com/neovim/go-client/nvim"
	"go.uber.org/zap"

	"github.com/zchee/nvim-go/pkg/logger"
)

const (
	EventBufLines       = "nvim_buf_lines_event"
	EventBufChangedtick = "nvim_buf_changedtick_event"
)

func RegisterBufLinesEvent(ctx context.Context, n *nvim.Nvim) {
	n.RegisterHandler(EventBufLines, func(linesEvent ...interface{}) {
		logger.FromContext(ctx).Debug(fmt.Sprintf("handles %s", EventBufLines), zap.Any("linesEvent", linesEvent))
	})
}

func RegisterBufChangedtickEvent(ctx context.Context, n *nvim.Nvim) {
	n.RegisterHandler(EventBufChangedtick, func(changedtickEvent ...interface{}) {
		logger.FromContext(ctx).Debug(fmt.Sprintf("handles %s", EventBufChangedtick), zap.Any("changedtickEvent", changedtickEvent))
	})
}
<commit_msg>nctx: cleanup event.go<commit_after>// Copyright 2018 The nvim-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.

package nctx

import (
	"errors"

	"github.com/neovim/go-client/nvim"
)

const (
	EventBufLines       = "nvim_buf_lines_event"
	EventBufChangedtick = "nvim_buf_changedtick_event"
	EventBufAttach      = "nvim_buf_attach_event"
	EventBufDetach      = "nvim_buf_detach_event"
)

func RegisterEvent(n *nvim.Nvim, event string, fn func(...interface{})) {
	n.RegisterHandler(event, fn)
}

func RegisterBufLinesEvent(n *nvim.Nvim, fn func(...interface{})) {
	RegisterEvent(n, EventBufLines, fn)
}

func RegisterBufChangedtickEvent(n *nvim.Nvim, fn func(...interface{})) {
	RegisterEvent(n, EventBufChangedtick, fn)
}

func reAttachFunc(n *nvim.Nvim) (nvim.Buffer, error) {
	buf, err := n.CurrentBuffer()
	if err != nil {
		return 0, errors.New("failed to gets current buffer")
	}

	if _, err := n.AttachBuffer(buf, false, make(map[string]interface{})); err != nil {
		return 0, errors.New("failed to attach buffer")
	}

	return buf, nil
}

func RegisterBufAttachEvent(n *nvim.Nvim, fn func(...interface{})) nvim.Buffer {
	buf, err := reAttachFunc(n)
	if err != nil {
		n.WritelnErr("failed to gets current buffer")
	}

	fn()

	return buf
}

func RegisterBufDetachEvent(n *nvim.Nvim, fn func(...interface{})) {
	detachFn := func(...interface{}) {
		_, err := reAttachFunc(n)
		if err != nil {
			n.WritelnErr("failed to gets current buffer")
		}

		fn()
	}

	RegisterEvent(n, EventBufDetach, detachFn)
}
<|endoftext|>


<commit_before>package stackit

import (
	"fmt"
	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/service/cloudformation"
	"github.com/aws/aws-sdk-go/service/sts"
	"github.com/pkg/errors"
	"strings"
	"time"
)

type StackitUpInput struct {
	StackName        string
	RoleARN          string
	StackPolicyBody  string
	TemplateBody     string
	PreviousTemplate bool
	Parameters       []*cloudformation.Parameter
	Tags             map[string]string
	NotificationARNs []string
	PopulateMissing  bool
}

func (s *Stackit) populateMissing(input *StackitUpInput) error {
	stack, _ := s.Describe(input.StackName)

	maybeAddParam := func(name, defaultValue *string) {
		if defaultValue != nil {
			return
		}

		for _, param := range input.Parameters {
			if *param.ParameterKey == *name {
				return
			}
		}
		input.Parameters = append(input.Parameters, &cloudformation.Parameter{
			ParameterKey:     name,
			UsePreviousValue: aws.Bool(true),
		})
	}

	if len(input.TemplateBody) == 0 {
		input.PreviousTemplate = true

		for _, param := range stack.Parameters {
			maybeAddParam(param.ParameterKey, nil)
		}
	} else {
		resp, err := s.api.ValidateTemplate(&cloudformation.ValidateTemplateInput{TemplateBody: &input.TemplateBody})
		if err != nil {
			return err
		}

		for _, param := range resp.Parameters {
			maybeAddParam(param.ParameterKey, param.DefaultValue)
		}
	}

	return nil
}

func (s *Stackit) ensureStackReady(stackName string, events chan<- TailStackEvent) error {
	stack, err := s.Describe(stackName)
	if err != nil {
		return err
	}

	cleanup := func(stackId string) error {
		token := generateToken()
		_, err := s.api.DeleteStack(&cloudformation.DeleteStackInput{StackName: &stackId, ClientRequestToken: &token})
		if err != nil {
			close(events)
			return err
		}

		s.PollStackEvents(stackId, token, func(event TailStackEvent) {
			events <- event
		})

		return nil
	}

	if stack != nil { // stack already exists
		if !IsTerminalStatus(*stack.StackStatus) && *stack.StackStatus != "REVIEW_IN_PROGRESS" {
			s.PollStackEvents(*stack.StackId, "", func(event TailStackEvent) {
				events <- event
			})
		}

		stack, err = s.Describe(*stack.StackId)
		if err != nil {
			return err
		}

		if *stack.StackStatus == "CREATE_FAILED" || *stack.StackStatus == "ROLLBACK_COMPLETE" {
			return cleanup(*stack.StackId)
		} else if *stack.StackStatus == "REVIEW_IN_PROGRESS" {
			resp, err := s.api.ListStackResources(&cloudformation.ListStackResourcesInput{StackName: stack.StackId})
			if err != nil {
				return err
			}
			if len(resp.StackResourceSummaries) == 0 {
				return cleanup(*stack.StackId)
			}
		}
	}

	return nil
}

func (s *Stackit) awsAccountId() (string, error) {
	resp, err := s.stsApi.GetCallerIdentity(&sts.GetCallerIdentityInput{})
	if err != nil {
		return "", errors.Wrap(err, "getting aws account id")
	}
	return *resp.Account, nil
}

type PrepareOutput struct {
	Input        *cloudformation.CreateChangeSetInput
	Output       *cloudformation.CreateChangeSetOutput
	Changes      []*cloudformation.Change
	TemplateBody string
}

func (s *Stackit) Prepare(input StackitUpInput, events chan<- TailStackEvent) (*PrepareOutput, error) {
	err := s.ensureStackReady(input.StackName, events)
	if err != nil {
		return nil, errors.Wrap(err, "waiting for stack to be in a clean state")
	}

	stack, err := s.Describe(input.StackName)
	if err != nil {
		return nil, errors.Wrap(err, "describing stack")
	}

	if input.PopulateMissing && stack != nil {
		err := s.populateMissing(&input)
		if err != nil {
			return nil, errors.Wrap(err, "populating missing parameters")
		}
	}

	token := generateToken()

	createInput := &cloudformation.CreateChangeSetInput{
		ChangeSetName:       aws.String(fmt.Sprintf("%s-csid-%d", input.StackName, time.Now().Unix())),
		StackName:           &input.StackName,
		Capabilities:        aws.StringSlice([]string{"CAPABILITY_IAM", "CAPABILITY_NAMED_IAM"}),
		Parameters:          input.Parameters,
		Tags:                mapToTags(input.Tags),
		NotificationARNs:    aws.StringSlice(input.NotificationARNs),
		ClientToken:         &token,
		UsePreviousTemplate: &input.PreviousTemplate,
	}

	if len(input.TemplateBody) > 0 {
		createInput.TemplateBody = &input.TemplateBody
	}

	if roleArn := input.RoleARN; len(roleArn) > 0 {
		if !strings.HasPrefix(roleArn, "arn:aws:iam") {
			accountId, err := s.awsAccountId()
			if err != nil {
				return nil, errors.Wrap(err, "retrieving aws account id from sts")
			}
			roleArn = fmt.Sprintf("arn:aws:iam::%s:role/%s", accountId, roleArn)
		}
		createInput.RoleARN = &roleArn
	}

	if stack != nil { // stack already exists
		createInput.ChangeSetType = aws.String(cloudformation.ChangeSetTypeUpdate)
	} else {
		createInput.ChangeSetType = aws.String(cloudformation.ChangeSetTypeCreate)
	}

	resp, err := s.api.CreateChangeSet(createInput)
	if err != nil {
		return nil, errors.Wrap(err, "creating change set")
	}

	change, err := s.waitForChangeset(resp.Id)
	if err != nil {
		return nil, errors.Wrap(err, "waiting for changeset to stabilise")
	}

	isNoop := change != nil && len(change.Changes) == 0
	if isNoop { // update is a no-op, nothing to change
		_, err = s.api.DeleteChangeSet(&cloudformation.DeleteChangeSetInput{ChangeSetName: resp.Id})
		return nil, errors.Wrap(err, "waiting for no-op changeset to delete")
	}

	getResp, err := s.api.GetTemplate(&cloudformation.GetTemplateInput{
		ChangeSetName: resp.Id,
		StackName:     resp.StackId,
		TemplateStage: aws.String(cloudformation.TemplateStageProcessed),
	})
	if err != nil {
		return nil, errors.Wrap(err, "getting processed template body")
	}

	return &PrepareOutput{
		Input:        createInput,
		Output:       resp,
		Changes:      change.Changes,
		TemplateBody: *getResp.TemplateBody,
	}, nil
}

func (s *Stackit) Execute(prepared *PrepareOutput, events chan<- TailStackEvent) error {
	token := prepared.Input.ClientToken

	_, err := s.api.ExecuteChangeSet(&cloudformation.ExecuteChangeSetInput{
		ChangeSetName:      prepared.Output.Id,
		ClientRequestToken: token,
	})

	if err != nil {
		close(events)
		return errors.Wrap(err, "executing change set")
	}

	_, err = s.PollStackEvents(*prepared.Output.StackId, *token, func(event TailStackEvent) {
		events <- event
	})

	close(events)
	return nil
}
<commit_msg>Generate new token for execution of change sets<commit_after>package stackit

import (
	"fmt"
	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/service/cloudformation"
	"github.com/aws/aws-sdk-go/service/sts"
	"github.com/pkg/errors"
	"strings"
	"time"
)

type StackitUpInput struct {
	StackName        string
	RoleARN          string
	StackPolicyBody  string
	TemplateBody     string
	PreviousTemplate bool
	Parameters       []*cloudformation.Parameter
	Tags             map[string]string
	NotificationARNs []string
	PopulateMissing  bool
}

func (s *Stackit) populateMissing(input *StackitUpInput) error {
	stack, _ := s.Describe(input.StackName)

	maybeAddParam := func(name, defaultValue *string) {
		if defaultValue != nil {
			return
		}

		for _, param := range input.Parameters {
			if *param.ParameterKey == *name {
				return
			}
		}
		input.Parameters = append(input.Parameters, &cloudformation.Parameter{
			ParameterKey:     name,
			UsePreviousValue: aws.Bool(true),
		})
	}

	if len(input.TemplateBody) == 0 {
		input.PreviousTemplate = true

		for _, param := range stack.Parameters {
			maybeAddParam(param.ParameterKey, nil)
		}
	} else {
		resp, err := s.api.ValidateTemplate(&cloudformation.ValidateTemplateInput{TemplateBody: &input.TemplateBody})
		if err != nil {
			return err
		}

		for _, param := range resp.Parameters {
			maybeAddParam(param.ParameterKey, param.DefaultValue)
		}
	}

	return nil
}

func (s *Stackit) ensureStackReady(stackName string, events chan<- TailStackEvent) error {
	stack, err := s.Describe(stackName)
	if err != nil {
		return err
	}

	cleanup := func(stackId string) error {
		token := generateToken()
		_, err := s.api.DeleteStack(&cloudformation.DeleteStackInput{StackName: &stackId, ClientRequestToken: &token})
		if err != nil {
			close(events)
			return err
		}

		s.PollStackEvents(stackId, token, func(event TailStackEvent) {
			events <- event
		})

		return nil
	}

	if stack != nil { // stack already exists
		if !IsTerminalStatus(*stack.StackStatus) && *stack.StackStatus != "REVIEW_IN_PROGRESS" {
			s.PollStackEvents(*stack.StackId, "", func(event TailStackEvent) {
				events <- event
			})
		}

		stack, err = s.Describe(*stack.StackId)
		if err != nil {
			return err
		}

		if *stack.StackStatus == "CREATE_FAILED" || *stack.StackStatus == "ROLLBACK_COMPLETE" {
			return cleanup(*stack.StackId)
		} else if *stack.StackStatus == "REVIEW_IN_PROGRESS" {
			resp, err := s.api.ListStackResources(&cloudformation.ListStackResourcesInput{StackName: stack.StackId})
			if err != nil {
				return err
			}
			if len(resp.StackResourceSummaries) == 0 {
				return cleanup(*stack.StackId)
			}
		}
	}

	return nil
}

func (s *Stackit) awsAccountId() (string, error) {
	resp, err := s.stsApi.GetCallerIdentity(&sts.GetCallerIdentityInput{})
	if err != nil {
		return "", errors.Wrap(err, "getting aws account id")
	}
	return *resp.Account, nil
}

type PrepareOutput struct {
	Input        *cloudformation.CreateChangeSetInput
	Output       *cloudformation.CreateChangeSetOutput
	Changes      []*cloudformation.Change
	TemplateBody string
}

func (s *Stackit) Prepare(input StackitUpInput, events chan<- TailStackEvent) (*PrepareOutput, error) {
	err := s.ensureStackReady(input.StackName, events)
	if err != nil {
		return nil, errors.Wrap(err, "waiting for stack to be in a clean state")
	}

	stack, err := s.Describe(input.StackName)
	if err != nil {
		return nil, errors.Wrap(err, "describing stack")
	}

	if input.PopulateMissing && stack != nil {
		err := s.populateMissing(&input)
		if err != nil {
			return nil, errors.Wrap(err, "populating missing parameters")
		}
	}

	token := generateToken()

	createInput := &cloudformation.CreateChangeSetInput{
		ChangeSetName:       aws.String(fmt.Sprintf("%s-csid-%d", input.StackName, time.Now().Unix())),
		StackName:           &input.StackName,
		Capabilities:        aws.StringSlice([]string{"CAPABILITY_IAM", "CAPABILITY_NAMED_IAM"}),
		Parameters:          input.Parameters,
		Tags:                mapToTags(input.Tags),
		NotificationARNs:    aws.StringSlice(input.NotificationARNs),
		ClientToken:         &token,
		UsePreviousTemplate: &input.PreviousTemplate,
	}

	if len(input.TemplateBody) > 0 {
		createInput.TemplateBody = &input.TemplateBody
	}

	if roleArn := input.RoleARN; len(roleArn) > 0 {
		if !strings.HasPrefix(roleArn, "arn:aws:iam") {
			accountId, err := s.awsAccountId()
			if err != nil {
				return nil, errors.Wrap(err, "retrieving aws account id from sts")
			}
			roleArn = fmt.Sprintf("arn:aws:iam::%s:role/%s", accountId, roleArn)
		}
		createInput.RoleARN = &roleArn
	}

	if stack != nil { // stack already exists
		createInput.ChangeSetType = aws.String(cloudformation.ChangeSetTypeUpdate)
	} else {
		createInput.ChangeSetType = aws.String(cloudformation.ChangeSetTypeCreate)
	}

	resp, err := s.api.CreateChangeSet(createInput)
	if err != nil {
		return nil, errors.Wrap(err, "creating change set")
	}

	change, err := s.waitForChangeset(resp.Id)
	if err != nil {
		return nil, errors.Wrap(err, "waiting for changeset to stabilise")
	}

	isNoop := change != nil && len(change.Changes) == 0
	if isNoop { // update is a no-op, nothing to change
		_, err = s.api.DeleteChangeSet(&cloudformation.DeleteChangeSetInput{ChangeSetName: resp.Id})
		return nil, errors.Wrap(err, "waiting for no-op changeset to delete")
	}

	getResp, err := s.api.GetTemplate(&cloudformation.GetTemplateInput{
		ChangeSetName: resp.Id,
		StackName:     resp.StackId,
		TemplateStage: aws.String(cloudformation.TemplateStageProcessed),
	})
	if err != nil {
		return nil, errors.Wrap(err, "getting processed template body")
	}

	return &PrepareOutput{
		Input:        createInput,
		Output:       resp,
		Changes:      change.Changes,
		TemplateBody: *getResp.TemplateBody,
	}, nil
}

func (s *Stackit) Execute(prepared *PrepareOutput, events chan<- TailStackEvent) error {
	token := generateToken()

	_, err := s.api.ExecuteChangeSet(&cloudformation.ExecuteChangeSetInput{
		ChangeSetName:      prepared.Output.Id,
		ClientRequestToken: &token,
	})

	if err != nil {
		close(events)
		return errors.Wrap(err, "executing change set")
	}

	_, err = s.PollStackEvents(stackId, token, func(event TailStackEvent) {
		events <- event
	})

	close(events)
	return nil
}
<|endoftext|>


<commit_before>package ui

import (
	"strings"
)

// Styling specifies how to change a Style. It can also be applied to a Segment
// or Text.
type Styling interface{ transform(*Style) }

// StyleText returns a new Text with the given Styling's applied. It does not
// modify the given Text.
func StyleText(t Text, ts ...Styling) Text {
	newt := make(Text, len(t))
	for i, seg := range t {
		newt[i] = StyleSegment(seg, ts...)
	}
	return newt
}

// StyleSegment returns a new Segment with the given Styling's applied. It does
// not modify the given Segment.
func StyleSegment(seg *Segment, ts ...Styling) *Segment {
	return &Segment{Text: seg.Text, Style: ApplyStyling(seg.Style, ts...)}
}

// ApplyStyling returns a new Style with the given Styling's applied.
func ApplyStyling(s Style, ts ...Styling) Style {
	for _, t := range ts {
		if t != nil {
			t.transform(&s)
		}
	}
	return s
}

// Stylings joins several transformers into one.
func Stylings(ts ...Styling) Styling { return jointStyling(ts) }

// Common stylings.
var (
	FgDefault Styling = setForeground{nil}

	FgBlack   Styling = setForeground{Black}
	FgRed     Styling = setForeground{Red}
	FgGreen   Styling = setForeground{Green}
	FgYellow  Styling = setForeground{Yellow}
	FgBlue    Styling = setForeground{Blue}
	FgMagenta Styling = setForeground{Magenta}
	FgCyan    Styling = setForeground{Cyan}
	FgWhite   Styling = setForeground{White}

	FgBrightBlack   Styling = setForeground{BrightBlack}
	FgBrightRed     Styling = setForeground{BrightRed}
	FgBrightGreen   Styling = setForeground{BrightGreen}
	FgBrightYellow  Styling = setForeground{BrightYellow}
	FgBrightBlue    Styling = setForeground{BrightBlue}
	FgBrightMagenta Styling = setForeground{BrightMagenta}
	FgBrightCyan    Styling = setForeground{BrightCyan}
	FgBrightWhite   Styling = setForeground{BrightWhite}

	BgDefault Styling = setBackground{nil}

	BgBlack   Styling = setBackground{Black}
	BgRed     Styling = setBackground{Red}
	BgGreen   Styling = setBackground{Green}
	BgYellow  Styling = setBackground{Yellow}
	BgBlue    Styling = setBackground{Blue}
	BgMagenta Styling = setBackground{Magenta}
	BgCyan    Styling = setBackground{Cyan}
	BgWhite   Styling = setBackground{White}

	BgBrightBlack   Styling = setBackground{BrightBlack}
	BgBrightRed     Styling = setBackground{BrightRed}
	BgBrightGreen   Styling = setBackground{BrightGreen}
	BgBrightYellow  Styling = setBackground{BrightYellow}
	BgBrightBlue    Styling = setBackground{BrightBlue}
	BgBrightMagenta Styling = setBackground{BrightMagenta}
	BgBrightCyan    Styling = setBackground{BrightCyan}
	BgBrightWhite   Styling = setBackground{BrightWhite}

	Bold       Styling = boolOn(accessBold)
	Dim        Styling = boolOn(accessDim)
	Italic     Styling = boolOn(accessItalic)
	Underlined Styling = boolOn(accessUnderlined)
	Blink      Styling = boolOn(accessBlink)
	Inverse    Styling = boolOn(accessInverse)

	NoBold       Styling = boolOff(accessBold)
	NoDim        Styling = boolOff(accessDim)
	NoItalic     Styling = boolOff(accessItalic)
	NoUnderlined Styling = boolOff(accessUnderlined)
	NoBlink      Styling = boolOff(accessBlink)
	NoInverse    Styling = boolOff(accessInverse)

	ToggleBold       Styling = boolToggle(accessBold)
	ToggleDim        Styling = boolToggle(accessDim)
	ToggleItalic     Styling = boolToggle(accessItalic)
	ToggleUnderlined Styling = boolToggle(accessUnderlined)
	ToggleBlink      Styling = boolToggle(accessBlink)
	ToggleInverse    Styling = boolToggle(accessInverse)
)

// Fg returns a Styling that sets the foreground color.
func Fg(c Color) Styling { return setForeground{c} }

// Bg returns a Styling that sets the background color.
func Bg(c Color) Styling { return setBackground{c} }

type setForeground struct{ c Color }
type setBackground struct{ c Color }
type boolOn func(*Style) *bool
type boolOff func(*Style) *bool
type boolToggle func(*Style) *bool

func (t setForeground) transform(s *Style) { s.Foreground = t.c }
func (t setBackground) transform(s *Style) { s.Background = t.c }
func (t boolOn) transform(s *Style)        { *t(s) = true }
func (t boolOff) transform(s *Style)       { *t(s) = false }
func (t boolToggle) transform(s *Style)    { p := t(s); *p = !*p }

func accessBold(s *Style) *bool       { return &s.Bold }
func accessDim(s *Style) *bool        { return &s.Dim }
func accessItalic(s *Style) *bool     { return &s.Italic }
func accessUnderlined(s *Style) *bool { return &s.Underlined }
func accessBlink(s *Style) *bool      { return &s.Blink }
func accessInverse(s *Style) *bool    { return &s.Inverse }

type jointStyling []Styling

func (t jointStyling) transform(s *Style) {
	for _, t := range t {
		t.transform(s)
	}
}

// ParseStyling parses a text representation of Styling, which are kebab
// case counterparts to the names of the builtin Styling's. For example,
// ToggleInverse is expressed as "toggle-inverse".
//
// Multiple stylings can be joined by spaces, which is equivalent to calling
// Stylings.
//
// If the given string is invalid, ParseStyling returns nil.
func ParseStyling(s string) Styling {
	if !strings.ContainsRune(s, ' ') {
		return parseOneStyling(s)
	}
	var joint jointStyling
	for _, subs := range strings.Split(s, " ") {
		parsed := parseOneStyling(subs)
		if parsed == nil {
			return nil
		}
		joint = append(joint, parseOneStyling(subs))
	}
	return joint
}

var boolFieldAccessor = map[string]func(*Style) *bool{
	"bold":       accessBold,
	"dim":        accessDim,
	"italic":     accessItalic,
	"underlined": accessUnderlined,
	"blink":      accessBlink,
	"inverse":    accessInverse,
}

func parseOneStyling(name string) Styling {
	switch {
	case name == "default" || name == "fg-default":
		return FgDefault
	case strings.HasPrefix(name, "fg-"):
		if color := parseColor(name[len("fg-"):]); color != nil {
			return setForeground{color}
		}
	case name == "bg-default":
		return BgDefault
	case strings.HasPrefix(name, "bg-"):
		if color := parseColor(name[len("bg-"):]); color != nil {
			return setBackground{color}
		}
	case strings.HasPrefix(name, "no-"):
		if f, ok := boolFieldAccessor[name[len("no-"):]]; ok {
			return boolOff(f)
		}
	case strings.HasPrefix(name, "toggle-"):
		if f, ok := boolFieldAccessor[name[len("toggle-"):]]; ok {
			return boolToggle(f)
		}
	default:
		if f, ok := boolFieldAccessor[name]; ok {
			return boolOn(f)
		}
		if color := parseColor(name); color != nil {
			return setForeground{color}
		}
	}
	return nil
}
<commit_msg>pkg/ui: Make all Styling types comparable.<commit_after>package ui

import (
	"strings"
)

// Styling specifies how to change a Style. It can also be applied to a Segment
// or Text.
type Styling interface{ transform(*Style) }

// StyleText returns a new Text with the given Styling's applied. It does not
// modify the given Text.
func StyleText(t Text, ts ...Styling) Text {
	newt := make(Text, len(t))
	for i, seg := range t {
		newt[i] = StyleSegment(seg, ts...)
	}
	return newt
}

// StyleSegment returns a new Segment with the given Styling's applied. It does
// not modify the given Segment.
func StyleSegment(seg *Segment, ts ...Styling) *Segment {
	return &Segment{Text: seg.Text, Style: ApplyStyling(seg.Style, ts...)}
}

// ApplyStyling returns a new Style with the given Styling's applied.
func ApplyStyling(s Style, ts ...Styling) Style {
	for _, t := range ts {
		if t != nil {
			t.transform(&s)
		}
	}
	return s
}

// Stylings joins several transformers into one.
func Stylings(ts ...Styling) Styling { return jointStyling(ts) }

// Common stylings.
var (
	FgDefault Styling = setForeground{nil}

	FgBlack   Styling = setForeground{Black}
	FgRed     Styling = setForeground{Red}
	FgGreen   Styling = setForeground{Green}
	FgYellow  Styling = setForeground{Yellow}
	FgBlue    Styling = setForeground{Blue}
	FgMagenta Styling = setForeground{Magenta}
	FgCyan    Styling = setForeground{Cyan}
	FgWhite   Styling = setForeground{White}

	FgBrightBlack   Styling = setForeground{BrightBlack}
	FgBrightRed     Styling = setForeground{BrightRed}
	FgBrightGreen   Styling = setForeground{BrightGreen}
	FgBrightYellow  Styling = setForeground{BrightYellow}
	FgBrightBlue    Styling = setForeground{BrightBlue}
	FgBrightMagenta Styling = setForeground{BrightMagenta}
	FgBrightCyan    Styling = setForeground{BrightCyan}
	FgBrightWhite   Styling = setForeground{BrightWhite}

	BgDefault Styling = setBackground{nil}

	BgBlack   Styling = setBackground{Black}
	BgRed     Styling = setBackground{Red}
	BgGreen   Styling = setBackground{Green}
	BgYellow  Styling = setBackground{Yellow}
	BgBlue    Styling = setBackground{Blue}
	BgMagenta Styling = setBackground{Magenta}
	BgCyan    Styling = setBackground{Cyan}
	BgWhite   Styling = setBackground{White}

	BgBrightBlack   Styling = setBackground{BrightBlack}
	BgBrightRed     Styling = setBackground{BrightRed}
	BgBrightGreen   Styling = setBackground{BrightGreen}
	BgBrightYellow  Styling = setBackground{BrightYellow}
	BgBrightBlue    Styling = setBackground{BrightBlue}
	BgBrightMagenta Styling = setBackground{BrightMagenta}
	BgBrightCyan    Styling = setBackground{BrightCyan}
	BgBrightWhite   Styling = setBackground{BrightWhite}

	Bold       Styling = boolOn{boldField{}}
	Dim        Styling = boolOn{dimField{}}
	Italic     Styling = boolOn{italicField{}}
	Underlined Styling = boolOn{underlinedField{}}
	Blink      Styling = boolOn{blinkField{}}
	Inverse    Styling = boolOn{inverseField{}}

	NoBold       Styling = boolOff{boldField{}}
	NoDim        Styling = boolOff{dimField{}}
	NoItalic     Styling = boolOff{italicField{}}
	NoUnderlined Styling = boolOff{underlinedField{}}
	NoBlink      Styling = boolOff{blinkField{}}
	NoInverse    Styling = boolOff{inverseField{}}

	ToggleBold       Styling = boolToggle{boldField{}}
	ToggleDim        Styling = boolToggle{dimField{}}
	ToggleItalic     Styling = boolToggle{italicField{}}
	ToggleUnderlined Styling = boolToggle{underlinedField{}}
	ToggleBlink      Styling = boolToggle{blinkField{}}
	ToggleInverse    Styling = boolToggle{inverseField{}}
)

// Fg returns a Styling that sets the foreground color.
func Fg(c Color) Styling { return setForeground{c} }

// Bg returns a Styling that sets the background color.
func Bg(c Color) Styling { return setBackground{c} }

type setForeground struct{ c Color }
type setBackground struct{ c Color }
type boolOn struct{ f boolField }
type boolOff struct{ f boolField }
type boolToggle struct{ f boolField }

func (t setForeground) transform(s *Style) { s.Foreground = t.c }
func (t setBackground) transform(s *Style) { s.Background = t.c }
func (t boolOn) transform(s *Style)        { *t.f.get(s) = true }
func (t boolOff) transform(s *Style)       { *t.f.get(s) = false }
func (t boolToggle) transform(s *Style)    { p := t.f.get(s); *p = !*p }

type boolField interface{ get(*Style) *bool }

type boldField struct{}
type dimField struct{}
type italicField struct{}
type underlinedField struct{}
type blinkField struct{}
type inverseField struct{}

func (boldField) get(s *Style) *bool       { return &s.Bold }
func (dimField) get(s *Style) *bool        { return &s.Dim }
func (italicField) get(s *Style) *bool     { return &s.Italic }
func (underlinedField) get(s *Style) *bool { return &s.Underlined }
func (blinkField) get(s *Style) *bool      { return &s.Blink }
func (inverseField) get(s *Style) *bool    { return &s.Inverse }

type jointStyling []Styling

func (t jointStyling) transform(s *Style) {
	for _, t := range t {
		t.transform(s)
	}
}

// ParseStyling parses a text representation of Styling, which are kebab
// case counterparts to the names of the builtin Styling's. For example,
// ToggleInverse is expressed as "toggle-inverse".
//
// Multiple stylings can be joined by spaces, which is equivalent to calling
// Stylings.
//
// If the given string is invalid, ParseStyling returns nil.
func ParseStyling(s string) Styling {
	if !strings.ContainsRune(s, ' ') {
		return parseOneStyling(s)
	}
	var joint jointStyling
	for _, subs := range strings.Split(s, " ") {
		parsed := parseOneStyling(subs)
		if parsed == nil {
			return nil
		}
		joint = append(joint, parseOneStyling(subs))
	}
	return joint
}

var boolFields = map[string]boolField{
	"bold":       boldField{},
	"dim":        dimField{},
	"italic":     italicField{},
	"underlined": underlinedField{},
	"blink":      blinkField{},
	"inverse":    inverseField{},
}

func parseOneStyling(name string) Styling {
	switch {
	case name == "default" || name == "fg-default":
		return FgDefault
	case strings.HasPrefix(name, "fg-"):
		if color := parseColor(name[len("fg-"):]); color != nil {
			return setForeground{color}
		}
	case name == "bg-default":
		return BgDefault
	case strings.HasPrefix(name, "bg-"):
		if color := parseColor(name[len("bg-"):]); color != nil {
			return setBackground{color}
		}
	case strings.HasPrefix(name, "no-"):
		if f, ok := boolFields[name[len("no-"):]]; ok {
			return boolOff{f}
		}
	case strings.HasPrefix(name, "toggle-"):
		if f, ok := boolFields[name[len("toggle-"):]]; ok {
			return boolToggle{f}
		}
	default:
		if f, ok := boolFields[name]; ok {
			return boolOn{f}
		}
		if color := parseColor(name); color != nil {
			return setForeground{color}
		}
	}
	return nil
}
<|endoftext|>


<commit_before>package global

import (
	"github.com/ready-steady/adapt/algorithm/internal"
)

type tracker struct {
	internal.Tracker

	ni   uint
	rate float64

	norms  []uint64
	scores []float64

	initialized bool
}

func newTracker(ni uint, config *Config) *tracker {
	return &tracker{
		Tracker: *internal.NewTracker(ni, config.MaxLevel, config.MaxIndices),

		ni:   ni,
		rate: config.AdaptivityRate,

		norms:  make([]uint64, 1),
		scores: make([]float64, 0),
	}
}

func (self *tracker) pull() []uint64 {
	if !self.initialized {
		self.initialized = true
		return self.Forward(^uint(0))
	}

	k := internal.LocateMinUint64s(self.norms, self.Active)
	min, max := self.norms[k], internal.MaxUint64s(self.norms)
	if float64(min) > (1.0-self.rate)*float64(max) {
		k = internal.LocateMaxFloat64s(self.scores, self.Active)
	}

	indices := self.Forward(k)

	nn := uint(len(indices)) / self.ni
	norm := self.norms[k] + 1
	for i := uint(0); i < nn; i++ {
		self.norms = append(self.norms, norm)
	}

	return indices
}

func (self *tracker) push(score float64) {
	self.scores = append(self.scores, score)
}
<commit_msg>a/global: a bit of refactoring<commit_after>package global

import (
	"github.com/ready-steady/adapt/algorithm/internal"
)

type tracker struct {
	internal.Tracker

	ni   uint
	rate float64

	norms  []uint64
	scores []float64

	initialized bool
}

func newTracker(ni uint, config *Config) *tracker {
	return &tracker{
		Tracker: *internal.NewTracker(ni, config.MaxLevel, config.MaxIndices),

		ni:   ni,
		rate: config.AdaptivityRate,
	}
}

func (self *tracker) pull() (indices []uint64) {
	var norm uint64

	if !self.initialized {
		self.initialized = true
		norm, indices = 0, self.Forward(^uint(0))
	} else {
		k := internal.LocateMinUint64s(self.norms, self.Active)
		min, max := self.norms[k], internal.MaxUint64s(self.norms)
		if float64(min) > (1.0-self.rate)*float64(max) {
			k = internal.LocateMaxFloat64s(self.scores, self.Active)
		}
		norm, indices = self.norms[k]+1, self.Forward(k)
	}

	nn := uint(len(indices)) / self.ni
	for i := uint(0); i < nn; i++ {
		self.norms = append(self.norms, norm)
	}

	return
}

func (self *tracker) push(score float64) {
	self.scores = append(self.scores, score)
}
<|endoftext|>


<commit_before>package v3_test

import (
	"errors"
	"time"

	"code.cloudfoundry.org/cli/actor/sharedaction"
	"code.cloudfoundry.org/cli/actor/v3action"
	"code.cloudfoundry.org/cli/actor/v3action/v3actionfakes"
	"code.cloudfoundry.org/cli/api/cloudcontroller/ccv3"
	"code.cloudfoundry.org/cli/command"
	"code.cloudfoundry.org/cli/command/commandfakes"
	"code.cloudfoundry.org/cli/command/v3"
	"code.cloudfoundry.org/cli/command/v3/v3fakes"
	"code.cloudfoundry.org/cli/util/configv3"
	"code.cloudfoundry.org/cli/util/ui"
	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
	. "github.com/onsi/gomega/gbytes"
)

var _ = Describe("v3-stage Command", func() {
	var (
		cmd             v3.V3StageCommand
		testUI          *ui.UI
		fakeConfig      *commandfakes.FakeConfig
		fakeSharedActor *commandfakes.FakeSharedActor
		fakeActor       *v3fakes.FakeV3StageActor
		fakeNOAAClient  *v3actionfakes.FakeNOAAClient
		binaryName      string
		executeErr      error
		app             string
		packageGUID     string
	)

	BeforeEach(func() {
		testUI = ui.NewTestUI(nil, NewBuffer(), NewBuffer())
		fakeConfig = new(commandfakes.FakeConfig)
		fakeSharedActor = new(commandfakes.FakeSharedActor)
		fakeActor = new(v3fakes.FakeV3StageActor)
		fakeNOAAClient = new(v3actionfakes.FakeNOAAClient)

		binaryName = "faceman"
		fakeConfig.BinaryNameReturns(binaryName)
		app = "some-app"
		packageGUID = "some-package-guid"

		cmd = v3.V3StageCommand{
			AppName:     app,
			PackageGUID: packageGUID,

			UI:          testUI,
			Config:      fakeConfig,
			SharedActor: fakeSharedActor,
			Actor:       fakeActor,
			NOAAClient:  fakeNOAAClient,
		}

	})

	JustBeforeEach(func() {
		executeErr = cmd.Execute(nil)
	})

	Context("when checking target fails", func() {
		BeforeEach(func() {
			fakeSharedActor.CheckTargetReturns(sharedaction.NotLoggedInError{BinaryName: binaryName})
		})

		It("returns an error", func() {
			Expect(executeErr).To(MatchError(command.NotLoggedInError{BinaryName: binaryName}))

			Expect(fakeSharedActor.CheckTargetCallCount()).To(Equal(1))
			_, checkTargetedOrg, checkTargetedSpace := fakeSharedActor.CheckTargetArgsForCall(0)
			Expect(checkTargetedOrg).To(BeTrue())
			Expect(checkTargetedSpace).To(BeTrue())
		})
	})

	Context("when the user is logged in", func() {
		BeforeEach(func() {
			fakeConfig.HasTargetedOrganizationReturns(true)
			fakeConfig.TargetedOrganizationReturns(configv3.Organization{
				GUID: "some-org-guid",
				Name: "some-org",
			})
			fakeConfig.HasTargetedSpaceReturns(true)
			fakeConfig.TargetedSpaceReturns(configv3.Space{
				GUID: "some-space-guid",
				Name: "some-space",
			})
			fakeConfig.CurrentUserReturns(configv3.User{Name: "steve"}, nil)
		})

		Context("when the logging does not error", func() {
			BeforeEach(func() {
				fakeActor.GetStreamingLogsForApplicationByNameAndSpaceStub = func(appName string, spaceGUID string, client v3action.NOAAClient) (<-chan *v3action.LogMessage, <-chan error, v3action.Warnings, error) {
					logStream := make(chan *v3action.LogMessage)
					errorStream := make(chan error)

					go func() {
						// defer close(logStream)
						// defer close(errorStream)
						logStream <- v3action.NewLogMessage("Here are some staging logs!", 1, time.Now(), v3action.StagingLog, "sourceInstance")
						logStream <- v3action.NewLogMessage("Here are some other staging logs!", 1, time.Now(), v3action.StagingLog, "sourceInstance")
					}()

					return logStream, errorStream, v3action.Warnings{"steve for all I care"}, nil
				}
			})

			Context("when the staging is successful", func() {
				BeforeEach(func() {
					fakeActor.StagePackageStub = func(packageGUID string) (<-chan v3action.Build, <-chan v3action.Warnings, <-chan error) {
						buildStream := make(chan v3action.Build)
						warningsStream := make(chan v3action.Warnings)
						errorStream := make(chan error)

						go func() {
							defer close(buildStream)
							defer close(warningsStream)
							defer close(errorStream)
							warningsStream <- v3action.Warnings{"some-warning", "some-other-warning"}
							buildStream <- v3action.Build{Droplet: ccv3.Droplet{GUID: "some-droplet-guid"}}
						}()

						return buildStream, warningsStream, errorStream
					}
				})

				It("outputs the droplet GUID", func() {
					Expect(executeErr).ToNot(HaveOccurred())

					Expect(testUI.Out).To(Say("Staging package for %s in org some-org / space some-space as steve...", app))
					Expect(testUI.Out).To(Say("droplet: some-droplet-guid"))
					Expect(testUI.Out).To(Say("OK"))

					Expect(testUI.Err).To(Say("some-warning"))
					Expect(testUI.Err).To(Say("some-other-warning"))
				})

				It("stages the package", func() {
					Expect(executeErr).ToNot(HaveOccurred())
					Expect(fakeActor.StagePackageCallCount()).To(Equal(1))
					Expect(fakeActor.StagePackageArgsForCall(0)).To(Equal(packageGUID))
				})

				It("displays staging logs and their warnings", func() {
					Expect(testUI.Out).To(Say("Here are some staging logs!"))
					Expect(testUI.Out).To(Say("Here are some other staging logs!"))

					Expect(testUI.Err).To(Say("steve for all I care"))

					Expect(fakeActor.GetStreamingLogsForApplicationByNameAndSpaceCallCount()).To(Equal(1))
					appName, spaceGUID, noaaClient := fakeActor.GetStreamingLogsForApplicationByNameAndSpaceArgsForCall(0)
					Expect(appName).To(Equal(app))
					Expect(spaceGUID).To(Equal("some-space-guid"))
					Expect(noaaClient).To(Equal(fakeNOAAClient))

					Expect(fakeActor.StagePackageCallCount()).To(Equal(1))
					Expect(fakeActor.StagePackageArgsForCall(0)).To(Equal(packageGUID))
				})
			})

			Context("when the staging returns an error", func() {
				var expectedErr error

				BeforeEach(func() {
					expectedErr = errors.New("any gibberish")
					fakeActor.StagePackageStub = func(packageGUID string) (<-chan v3action.Build, <-chan v3action.Warnings, <-chan error) {
						buildStream := make(chan v3action.Build)
						warningsStream := make(chan v3action.Warnings)
						errorStream := make(chan error)

						go func() {
							defer close(buildStream)
							defer close(warningsStream)
							defer close(errorStream)
							warningsStream <- v3action.Warnings{"some-warning", "some-other-warning"}
							errorStream <- expectedErr
						}()

						return buildStream, warningsStream, errorStream
					}
				})

				It("returns the error and displays warnings", func() {
					Expect(executeErr).To(Equal(expectedErr))

					Expect(testUI.Err).To(Say("some-warning"))
					Expect(testUI.Err).To(Say("some-other-warning"))
				})
			})
		})

		Context("when the logging stream has errors", func() {
			var expectedErr error

			BeforeEach(func() {
				expectedErr = errors.New("banana")

				fakeActor.GetStreamingLogsForApplicationByNameAndSpaceStub = func(appName string, spaceGUID string, client v3action.NOAAClient) (<-chan *v3action.LogMessage, <-chan error, v3action.Warnings, error) {
					logStream := make(chan *v3action.LogMessage)
					errorStream := make(chan error)

					go func() {
						defer close(logStream)
						defer close(errorStream)
						logStream <- v3action.NewLogMessage("Here are some staging logs!", 1, time.Now(), v3action.StagingLog, "sourceInstance")
						errorStream <- expectedErr
					}()

					return logStream, errorStream, v3action.Warnings{"steve for all I care"}, nil
				}

				fakeActor.StagePackageStub = func(packageGUID string) (<-chan v3action.Build, <-chan v3action.Warnings, <-chan error) {
					buildStream := make(chan v3action.Build)
					warningsStream := make(chan v3action.Warnings)
					errorStream := make(chan error)

					go func() {
						defer close(buildStream)
						defer close(warningsStream)
						defer close(errorStream)
						warningsStream <- v3action.Warnings{"some-warning", "some-other-warning"}
						buildStream <- v3action.Build{Droplet: ccv3.Droplet{GUID: "some-droplet-guid"}}
					}()

					return buildStream, warningsStream, errorStream
				}
			})

			It("displays the error and continues staging", func() {
				Expect(executeErr).ToNot(HaveOccurred())

				stdErr, ok := testUI.Err.(*Buffer)
				Expect(ok).To(BeTrue())
				stdErrString := string(stdErr.Contents())

				Expect(stdErrString).To(ContainSubstring("banana"))
				Expect(stdErrString).To(ContainSubstring("some-warning"))
				Expect(stdErrString).To(ContainSubstring("some-other-warning"))
			})
		})

		Context("when the logging returns an error due to an API error", func() {
			var expectedErr error
			BeforeEach(func() {
				expectedErr = errors.New("something is wrong!")
				logStream := make(chan *v3action.LogMessage)
				errorStream := make(chan error)
				fakeActor.GetStreamingLogsForApplicationByNameAndSpaceReturns(logStream, errorStream, v3action.Warnings{"some-warning", "some-other-warning"}, expectedErr)
			})

			It("returns the error and displays warnings", func() {
				Expect(executeErr).To(Equal(expectedErr))

				Expect(testUI.Err).To(Say("some-warning"))
				Expect(testUI.Err).To(Say("some-other-warning"))
			})
		})
	})
})
<commit_msg>fix race condition in v3-stage command tests<commit_after>package v3_test

import (
	"errors"
	"time"

	"code.cloudfoundry.org/cli/actor/sharedaction"
	"code.cloudfoundry.org/cli/actor/v3action"
	"code.cloudfoundry.org/cli/actor/v3action/v3actionfakes"
	"code.cloudfoundry.org/cli/api/cloudcontroller/ccv3"
	"code.cloudfoundry.org/cli/command"
	"code.cloudfoundry.org/cli/command/commandfakes"
	"code.cloudfoundry.org/cli/command/v3"
	"code.cloudfoundry.org/cli/command/v3/v3fakes"
	"code.cloudfoundry.org/cli/util/configv3"
	"code.cloudfoundry.org/cli/util/ui"
	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
	. "github.com/onsi/gomega/gbytes"
)

var _ = Describe("v3-stage Command", func() {
	var (
		cmd             v3.V3StageCommand
		testUI          *ui.UI
		fakeConfig      *commandfakes.FakeConfig
		fakeSharedActor *commandfakes.FakeSharedActor
		fakeActor       *v3fakes.FakeV3StageActor
		fakeNOAAClient  *v3actionfakes.FakeNOAAClient
		binaryName      string
		executeErr      error
		app             string
		packageGUID     string
	)

	BeforeEach(func() {
		testUI = ui.NewTestUI(nil, NewBuffer(), NewBuffer())
		fakeConfig = new(commandfakes.FakeConfig)
		fakeSharedActor = new(commandfakes.FakeSharedActor)
		fakeActor = new(v3fakes.FakeV3StageActor)
		fakeNOAAClient = new(v3actionfakes.FakeNOAAClient)

		binaryName = "faceman"
		fakeConfig.BinaryNameReturns(binaryName)
		app = "some-app"
		packageGUID = "some-package-guid"

		cmd = v3.V3StageCommand{
			AppName:     app,
			PackageGUID: packageGUID,

			UI:          testUI,
			Config:      fakeConfig,
			SharedActor: fakeSharedActor,
			Actor:       fakeActor,
			NOAAClient:  fakeNOAAClient,
		}

	})

	JustBeforeEach(func() {
		executeErr = cmd.Execute(nil)
	})

	Context("when checking target fails", func() {
		BeforeEach(func() {
			fakeSharedActor.CheckTargetReturns(sharedaction.NotLoggedInError{BinaryName: binaryName})
		})

		It("returns an error", func() {
			Expect(executeErr).To(MatchError(command.NotLoggedInError{BinaryName: binaryName}))

			Expect(fakeSharedActor.CheckTargetCallCount()).To(Equal(1))
			_, checkTargetedOrg, checkTargetedSpace := fakeSharedActor.CheckTargetArgsForCall(0)
			Expect(checkTargetedOrg).To(BeTrue())
			Expect(checkTargetedSpace).To(BeTrue())
		})
	})

	Context("when the user is logged in", func() {
		BeforeEach(func() {
			fakeConfig.HasTargetedOrganizationReturns(true)
			fakeConfig.TargetedOrganizationReturns(configv3.Organization{
				GUID: "some-org-guid",
				Name: "some-org",
			})
			fakeConfig.HasTargetedSpaceReturns(true)
			fakeConfig.TargetedSpaceReturns(configv3.Space{
				GUID: "some-space-guid",
				Name: "some-space",
			})
			fakeConfig.CurrentUserReturns(configv3.User{Name: "steve"}, nil)
		})

		Context("when the logging does not error", func() {
			allLogsWritten := make(chan bool)

			BeforeEach(func() {
				fakeActor.GetStreamingLogsForApplicationByNameAndSpaceStub = func(appName string, spaceGUID string, client v3action.NOAAClient) (<-chan *v3action.LogMessage, <-chan error, v3action.Warnings, error) {
					logStream := make(chan *v3action.LogMessage)
					errorStream := make(chan error)

					go func() {
						logStream <- v3action.NewLogMessage("Here are some staging logs!", 1, time.Now(), v3action.StagingLog, "sourceInstance")
						logStream <- v3action.NewLogMessage("Here are some other staging logs!", 1, time.Now(), v3action.StagingLog, "sourceInstance")
						allLogsWritten <- true
					}()

					return logStream, errorStream, v3action.Warnings{"steve for all I care"}, nil
				}
			})

			Context("when the staging is successful", func() {
				BeforeEach(func() {
					fakeActor.StagePackageStub = func(packageGUID string) (<-chan v3action.Build, <-chan v3action.Warnings, <-chan error) {
						buildStream := make(chan v3action.Build)
						warningsStream := make(chan v3action.Warnings)
						errorStream := make(chan error)

						go func() {
							<-allLogsWritten
							defer close(buildStream)
							defer close(warningsStream)
							defer close(errorStream)
							warningsStream <- v3action.Warnings{"some-warning", "some-other-warning"}
							buildStream <- v3action.Build{Droplet: ccv3.Droplet{GUID: "some-droplet-guid"}}
						}()

						return buildStream, warningsStream, errorStream
					}
				})

				It("outputs the droplet GUID", func() {
					Expect(executeErr).ToNot(HaveOccurred())

					Expect(testUI.Out).To(Say("Staging package for %s in org some-org / space some-space as steve...", app))
					Expect(testUI.Out).To(Say("droplet: some-droplet-guid"))
					Expect(testUI.Out).To(Say("OK"))

					Expect(testUI.Err).To(Say("some-warning"))
					Expect(testUI.Err).To(Say("some-other-warning"))
				})

				It("stages the package", func() {
					Expect(executeErr).ToNot(HaveOccurred())
					Expect(fakeActor.StagePackageCallCount()).To(Equal(1))
					Expect(fakeActor.StagePackageArgsForCall(0)).To(Equal(packageGUID))
				})

				It("displays staging logs and their warnings", func() {
					Expect(testUI.Out).To(Say("Here are some staging logs!"))
					Expect(testUI.Out).To(Say("Here are some other staging logs!"))

					Expect(testUI.Err).To(Say("steve for all I care"))

					Expect(fakeActor.GetStreamingLogsForApplicationByNameAndSpaceCallCount()).To(Equal(1))
					appName, spaceGUID, noaaClient := fakeActor.GetStreamingLogsForApplicationByNameAndSpaceArgsForCall(0)
					Expect(appName).To(Equal(app))
					Expect(spaceGUID).To(Equal("some-space-guid"))
					Expect(noaaClient).To(Equal(fakeNOAAClient))

					Expect(fakeActor.StagePackageCallCount()).To(Equal(1))
					Expect(fakeActor.StagePackageArgsForCall(0)).To(Equal(packageGUID))
				})
			})

			Context("when the staging returns an error", func() {
				var expectedErr error

				BeforeEach(func() {
					expectedErr = errors.New("any gibberish")
					fakeActor.StagePackageStub = func(packageGUID string) (<-chan v3action.Build, <-chan v3action.Warnings, <-chan error) {
						buildStream := make(chan v3action.Build)
						warningsStream := make(chan v3action.Warnings)
						errorStream := make(chan error)

						go func() {
							defer close(buildStream)
							defer close(warningsStream)
							defer close(errorStream)
							warningsStream <- v3action.Warnings{"some-warning", "some-other-warning"}
							errorStream <- expectedErr
						}()

						return buildStream, warningsStream, errorStream
					}
				})

				It("returns the error and displays warnings", func() {
					Expect(executeErr).To(Equal(expectedErr))

					Expect(testUI.Err).To(Say("some-warning"))
					Expect(testUI.Err).To(Say("some-other-warning"))
				})
			})
		})

		Context("when the logging stream has errors", func() {
			var expectedErr error
			allLogsWritten := make(chan bool)

			BeforeEach(func() {
				expectedErr = errors.New("banana")

				fakeActor.GetStreamingLogsForApplicationByNameAndSpaceStub = func(appName string, spaceGUID string, client v3action.NOAAClient) (<-chan *v3action.LogMessage, <-chan error, v3action.Warnings, error) {
					logStream := make(chan *v3action.LogMessage)
					errorStream := make(chan error)

					go func() {
						defer close(logStream)
						defer close(errorStream)
						logStream <- v3action.NewLogMessage("Here are some staging logs!", 1, time.Now(), v3action.StagingLog, "sourceInstance")
						errorStream <- expectedErr
						allLogsWritten <- true
					}()

					return logStream, errorStream, v3action.Warnings{"steve for all I care"}, nil
				}

				fakeActor.StagePackageStub = func(packageGUID string) (<-chan v3action.Build, <-chan v3action.Warnings, <-chan error) {
					buildStream := make(chan v3action.Build)
					warningsStream := make(chan v3action.Warnings)
					errorStream := make(chan error)

					go func() {
						<-allLogsWritten
						defer close(buildStream)
						defer close(warningsStream)
						defer close(errorStream)
						warningsStream <- v3action.Warnings{"some-warning", "some-other-warning"}
						buildStream <- v3action.Build{Droplet: ccv3.Droplet{GUID: "some-droplet-guid"}}
					}()

					return buildStream, warningsStream, errorStream
				}
			})

			It("displays the error and continues staging", func() {
				Expect(executeErr).ToNot(HaveOccurred())

				Expect(testUI.Err).To(Say("banana"))
				Expect(testUI.Err).To(Say("some-warning"))
				Expect(testUI.Err).To(Say("some-other-warning"))
			})
		})

		Context("when the logging returns an error due to an API error", func() {
			var expectedErr error
			BeforeEach(func() {
				expectedErr = errors.New("something is wrong!")
				logStream := make(chan *v3action.LogMessage)
				errorStream := make(chan error)
				fakeActor.GetStreamingLogsForApplicationByNameAndSpaceReturns(logStream, errorStream, v3action.Warnings{"some-warning", "some-other-warning"}, expectedErr)
			})

			It("returns the error and displays warnings", func() {
				Expect(executeErr).To(Equal(expectedErr))

				Expect(testUI.Err).To(Say("some-warning"))
				Expect(testUI.Err).To(Say("some-other-warning"))
			})
		})
	})
})
<|endoftext|>


<commit_before>package commandevaluators

/**
ASSUMPTIONS

a) there is only 1 DSP in a given room

b) microphones only have one port configuration and the DSP is the destination device

c) room-wide requests do not affect microphones

**/

import (
	"errors"
	"fmt"
	"log"
	"strconv"

	"github.com/byuoitav/av-api/base"
	"github.com/byuoitav/av-api/dbo"
	"github.com/byuoitav/configuration-database-microservice/accessors"

	ei "github.com/byuoitav/event-router-microservice/eventinfrastructure"
)

type SetVolumeDSP struct{}

func (p *SetVolumeDSP) Evaluate(room base.PublicRoom) ([]base.ActionStructure, error) {

	log.Printf("Evaluating SetVolume command in DSP context...")

	eventInfo := ei.EventInfo{
		Type:         ei.CORESTATE,
		EventCause:   ei.USERINPUT,
		EventInfoKey: "volume",
	}

	var actions []base.ActionStructure

	if room.Volume != nil {

		log.Printf("Room-wide request detected")

		eventInfo.EventInfoValue = strconv.Itoa(*room.Volume)

		actions, err := GetGeneralVolumeRequestActionsDSP(room, eventInfo)
		if err != nil {
			errorMessage := "Could not generate actions for room-wide \"SetVolume\" request: " + err.Error()
			log.Printf(errorMessage)
			return []base.ActionStructure{}, errors.New(errorMessage)
		}

		actions = append(actions, actions...)
	}

	if len(room.AudioDevices) > 0 {

		for _, audioDevice := range room.AudioDevices {

			if audioDevice.Volume != nil {

				eventInfo.EventInfoValue = strconv.Itoa(*audioDevice.Volume)

				device, err := dbo.GetDeviceByName(room.Building, room.Room, audioDevice.Name)
				if err != nil {
					log.Printf("Error getting device %s from database: %s", audioDevice.Name, err.Error())
				}

				if device.HasRole("Microphone") {

					action, err := GetMicVolumeAction(device, room, eventInfo, *audioDevice.Volume)
					if err != nil {
						return []base.ActionStructure{}, err
					}

					actions = append(actions, action)

				} else if device.HasRole("DSP") {

					dspActions, err := GetDSPMediaVolumeAction(device, room, eventInfo, *audioDevice.Volume)
					if err != nil {
						return []base.ActionStructure{}, err
					}

					actions = append(actions, dspActions...)

				} else if device.HasRole("AudioOut") {

					action, err := GetDisplayVolumeAction(device, room, eventInfo, *audioDevice.Volume)
					if err != nil {
						return []base.ActionStructure{}, err
					}

					actions = append(actions, action)

				} else { //bad device
					errorMessage := "Cannot set volume of device: " + device.Name + " in given context"
					log.Printf(errorMessage)
					return []base.ActionStructure{}, errors.New(errorMessage)
				}
			}
		}
	}

	log.Printf("%v actions generated.", len(actions))

	for _, a := range actions {
		log.Printf("%v, %v", a.Action, a.Parameters)

	}

	log.Printf("Evaluation complete.")
	return actions, nil
}

func (p *SetVolumeDSP) Validate(action base.ActionStructure) (err error) {
	maximum := 100
	minimum := 0

	level, err := strconv.Atoi(action.Parameters["level"])
	if err != nil {
		return err
	}

	if level > maximum || level < minimum {
		log.Printf("ERROR. %v is an invalid volume level for %s", action.Parameters["level"], action.Device.Name)
		return errors.New(action.Action + " is an invalid command for " + action.Device.Name)
	}

	return
}

func (p *SetVolumeDSP) GetIncompatibleCommands() (incompatibleActions []string) {
	return nil
}

func GetGeneralVolumeRequestActionsDSP(room base.PublicRoom, eventInfo ei.EventInfo) ([]base.ActionStructure, error) {

	log.Printf("Generating actions for room-wide \"SetVolume\" request")

	var actions []base.ActionStructure

	dsp, err := dbo.GetDevicesByBuildingAndRoomAndRole(room.Building, room.Room, "DSP")
	if err != nil {
		log.Printf("Error getting devices %s", err.Error)
		return []base.ActionStructure{}, err
	}

	dspActions, err := GetDSPMediaVolumeAction(dsp[0], room, eventInfo, *room.Volume)
	if err != nil {
		errorMessage := "Could not generate action corresponding to general mute request in room " + room.Room + ", building " + room.Building + ": " + err.Error()
		log.Printf(errorMessage)
		return []base.ActionStructure{}, errors.New(errorMessage)
	}

	actions = append(actions, dspActions...)

	audioDevices, err := dbo.GetDevicesByBuildingAndRoomAndRole(room.Building, room.Room, "AudioOut")
	if err != nil {
		log.Printf("Error getting devices %s", err.Error())
		return []base.ActionStructure{}, err
	}

	for _, device := range audioDevices {
		if device.HasRole("DSP") {
			continue
		}

		action, err := GetDisplayVolumeAction(device, room, eventInfo, *room.Volume)
		if err != nil {
			errorMessage := "Could not generate mute action for display " + device.Name + " in room " + room.Room + ", building " + room.Building + ": " + err.Error()
			log.Printf(errorMessage)
			return []base.ActionStructure{}, errors.New(errorMessage)
		}

		actions = append(actions, action)
	}

	return actions, nil
}

//we assume microphones are only connected to a DSP
//commands regarding microphones are only issued to DSP
func GetMicVolumeAction(mic accessors.Device, room base.PublicRoom, eventInfo ei.EventInfo, volume int) (base.ActionStructure, error) {

	log.Printf("Identified microphone volume request")

	parameters := make(map[string]string)

	eventInfo.EventInfoValue = string(volume)
	parameters["level"] = string(volume)
	parameters["input"] = mic.Ports[0].Name

	dsp, err := dbo.GetDeviceByName(room.Building, room.Room, mic.Ports[0].Destination)
	if err != nil {
		errorMessage := "Could not get DSP corresponding to mic " + mic.Name + ": " + err.Error()
		log.Printf(errorMessage)
		return base.ActionStructure{}, errors.New(errorMessage)
	}

	return base.ActionStructure{
		Action:              "SetVolume",
		GeneratingEvaluator: "SetVolumeDSP",
		Device:              dsp,
		DeviceSpecific:      true,
		EventLog:            []ei.EventInfo{eventInfo},
		Parameters:          parameters,
	}, nil

}

func GetDSPMediaVolumeAction(device accessors.Device, room base.PublicRoom, eventInfo ei.EventInfo, volume int) ([]base.ActionStructure, error) { //commands are issued to whatever port doesn't have a mic connected
	log.Printf("%v", volume)

	log.Printf("Generating action for command SetVolume on media routed through DSP")

	var output []base.ActionStructure

	for _, port := range device.Ports {
		parameters := make(map[string]string)
		parameters["level"] = fmt.Sprintf("%v", volume)
		eventInfo.EventInfoValue = fmt.Sprintf("%v", volume)

		sourceDevice, err := dbo.GetDeviceByName(room.Building, room.Room, port.Source)
		if err != nil {
			errorMessage := "Could not get device " + port.Source + " from database: " + err.Error()
			log.Printf(errorMessage)
			return []base.ActionStructure{}, errors.New(errorMessage)
		}

		if !(sourceDevice.HasRole("Microphone")) {

			parameters["input"] = port.Name
			action := base.ActionStructure{
				Action:              "SetVolume",
				GeneratingEvaluator: "SetVolumeDSP",
				Device:              device,
				DeviceSpecific:      true,
				EventLog:            []ei.EventInfo{eventInfo},
				Parameters:          parameters,
			}

			output = append(output, action)
		}
	}

	return output, nil

}

func GetDisplayVolumeAction(device accessors.Device, room base.PublicRoom, eventInfo ei.EventInfo, volume int) (base.ActionStructure, error) { //commands are issued to devices, e.g. they aren't connected to the DSP

	log.Printf("Generating action for SetVolume on device %s external to DSP", device.Name)

	parameters := make(map[string]string)

	eventInfo.EventInfoValue = strconv.Itoa(volume)
	parameters["level"] = string(volume)

	action := base.ActionStructure{
		Action:              "SetVolume",
		GeneratingEvaluator: "SetVolumeDSP",
		Device:              device,
		DeviceSpecific:      true,
		EventLog:            []ei.EventInfo{eventInfo},
		Parameters:          parameters,
	}

	return action, nil
}
<commit_msg>gotcha<commit_after>package commandevaluators

/**
ASSUMPTIONS

a) there is only 1 DSP in a given room

b) microphones only have one port configuration and the DSP is the destination device

c) room-wide requests do not affect microphones

**/

import (
	"errors"
	"fmt"
	"log"
	"strconv"

	"github.com/byuoitav/av-api/base"
	"github.com/byuoitav/av-api/dbo"
	"github.com/byuoitav/configuration-database-microservice/accessors"

	ei "github.com/byuoitav/event-router-microservice/eventinfrastructure"
)

type SetVolumeDSP struct{}

func (p *SetVolumeDSP) Evaluate(room base.PublicRoom) ([]base.ActionStructure, error) {

	log.Printf("Evaluating SetVolume command in DSP context...")

	eventInfo := ei.EventInfo{
		Type:         ei.CORESTATE,
		EventCause:   ei.USERINPUT,
		EventInfoKey: "volume",
	}

	var actions []base.ActionStructure

	if room.Volume != nil {

		log.Printf("Room-wide request detected")

		eventInfo.EventInfoValue = strconv.Itoa(*room.Volume)

		actions, err := GetGeneralVolumeRequestActionsDSP(room, eventInfo)
		if err != nil {
			errorMessage := "Could not generate actions for room-wide \"SetVolume\" request: " + err.Error()
			log.Printf(errorMessage)
			return []base.ActionStructure{}, errors.New(errorMessage)
		}

		actions = append(actions, actions...)
	}

	if len(room.AudioDevices) > 0 {

		for _, audioDevice := range room.AudioDevices {

			if audioDevice.Volume != nil {

				eventInfo.EventInfoValue = strconv.Itoa(*audioDevice.Volume)

				device, err := dbo.GetDeviceByName(room.Building, room.Room, audioDevice.Name)
				if err != nil {
					log.Printf("Error getting device %s from database: %s", audioDevice.Name, err.Error())
				}

				if device.HasRole("Microphone") {

					action, err := GetMicVolumeAction(device, room, eventInfo, *audioDevice.Volume)
					if err != nil {
						return []base.ActionStructure{}, err
					}

					actions = append(actions, action)

				} else if device.HasRole("DSP") {

					dspActions, err := GetDSPMediaVolumeAction(device, room, eventInfo, *audioDevice.Volume)
					if err != nil {
						return []base.ActionStructure{}, err
					}

					actions = append(actions, dspActions...)

				} else if device.HasRole("AudioOut") {

					action, err := GetDisplayVolumeAction(device, room, eventInfo, *audioDevice.Volume)
					if err != nil {
						return []base.ActionStructure{}, err
					}

					actions = append(actions, action)

				} else { //bad device
					errorMessage := "Cannot set volume of device: " + device.Name + " in given context"
					log.Printf(errorMessage)
					return []base.ActionStructure{}, errors.New(errorMessage)
				}
			}
		}
	}

	log.Printf("%v actions generated.", len(actions))

	for _, a := range actions {
		log.Printf("%v, %v", a.Action, a.Parameters)

	}

	log.Printf("Evaluation complete.")
	return actions, nil
}

func (p *SetVolumeDSP) Validate(action base.ActionStructure) (err error) {
	maximum := 100
	minimum := 0

	level, err := strconv.Atoi(action.Parameters["level"])
	if err != nil {
		return err
	}

	if level > maximum || level < minimum {
		log.Printf("ERROR. %v is an invalid volume level for %s", action.Parameters["level"], action.Device.Name)
		return errors.New(action.Action + " is an invalid command for " + action.Device.Name)
	}

	return
}

func (p *SetVolumeDSP) GetIncompatibleCommands() (incompatibleActions []string) {
	return nil
}

func GetGeneralVolumeRequestActionsDSP(room base.PublicRoom, eventInfo ei.EventInfo) ([]base.ActionStructure, error) {

	log.Printf("Generating actions for room-wide \"SetVolume\" request")

	var actions []base.ActionStructure

	dsp, err := dbo.GetDevicesByBuildingAndRoomAndRole(room.Building, room.Room, "DSP")
	if err != nil {
		log.Printf("Error getting devices %s", err.Error)
		return []base.ActionStructure{}, err
	}

	dspActions, err := GetDSPMediaVolumeAction(dsp[0], room, eventInfo, *room.Volume)
	if err != nil {
		errorMessage := "Could not generate action corresponding to general mute request in room " + room.Room + ", building " + room.Building + ": " + err.Error()
		log.Printf(errorMessage)
		return []base.ActionStructure{}, errors.New(errorMessage)
	}

	actions = append(actions, dspActions...)

	audioDevices, err := dbo.GetDevicesByBuildingAndRoomAndRole(room.Building, room.Room, "AudioOut")
	if err != nil {
		log.Printf("Error getting devices %s", err.Error())
		return []base.ActionStructure{}, err
	}

	for _, device := range audioDevices {
		if device.HasRole("DSP") {
			continue
		}

		action, err := GetDisplayVolumeAction(device, room, eventInfo, *room.Volume)
		if err != nil {
			errorMessage := "Could not generate mute action for display " + device.Name + " in room " + room.Room + ", building " + room.Building + ": " + err.Error()
			log.Printf(errorMessage)
			return []base.ActionStructure{}, errors.New(errorMessage)
		}

		actions = append(actions, action)
	}

	return actions, nil
}

//we assume microphones are only connected to a DSP
//commands regarding microphones are only issued to DSP
func GetMicVolumeAction(mic accessors.Device, room base.PublicRoom, eventInfo ei.EventInfo, volume int) (base.ActionStructure, error) {

	log.Printf("Identified microphone volume request")

	parameters := make(map[string]string)

	eventInfo.EventInfoValue = string(volume)
	parameters["level"] = string(volume)
	parameters["input"] = mic.Ports[0].Name

	dsp, err := dbo.GetDeviceByName(room.Building, room.Room, mic.Ports[0].Destination)
	if err != nil {
		errorMessage := "Could not get DSP corresponding to mic " + mic.Name + ": " + err.Error()
		log.Printf(errorMessage)
		return base.ActionStructure{}, errors.New(errorMessage)
	}

	return base.ActionStructure{
		Action:              "SetVolume",
		GeneratingEvaluator: "SetVolumeDSP",
		Device:              dsp,
		DeviceSpecific:      true,
		EventLog:            []ei.EventInfo{eventInfo},
		Parameters:          parameters,
	}, nil

}

func GetDSPMediaVolumeAction(device accessors.Device, room base.PublicRoom, eventInfo ei.EventInfo, volume int) ([]base.ActionStructure, error) { //commands are issued to whatever port doesn't have a mic connected
	log.Printf("%v", volume)

	log.Printf("Generating action for command SetVolume on media routed through DSP")

	var output []base.ActionStructure

	for _, port := range device.Ports {
		parameters := make(map[string]string)
		parameters["level"] = fmt.Sprintf("%v", volume)
		eventInfo.EventInfoValue = fmt.Sprintf("%v", volume)

		sourceDevice, err := dbo.GetDeviceByName(room.Building, room.Room, port.Source)
		if err != nil {
			errorMessage := "Could not get device " + port.Source + " from database: " + err.Error()
			log.Printf(errorMessage)
			return []base.ActionStructure{}, errors.New(errorMessage)
		}

		if !(sourceDevice.HasRole("Microphone")) {

			parameters["input"] = port.Name
			action := base.ActionStructure{
				Action:              "SetVolume",
				GeneratingEvaluator: "SetVolumeDSP",
				Device:              device,
				DeviceSpecific:      true,
				EventLog:            []ei.EventInfo{eventInfo},
				Parameters:          parameters,
			}

			output = append(output, action)
		}
	}

	return output, nil

}

func GetDisplayVolumeAction(device accessors.Device, room base.PublicRoom, eventInfo ei.EventInfo, volume int) (base.ActionStructure, error) { //commands are issued to devices, e.g. they aren't connected to the DSP

	log.Printf("Generating action for SetVolume on device %s external to DSP", device.Name)

	parameters := make(map[string]string)

	eventInfo.EventInfoValue = strconv.Itoa(volume)
	parameters["level"] = strconv.Itoa(volume)

	action := base.ActionStructure{
		Action:              "SetVolume",
		GeneratingEvaluator: "SetVolumeDSP",
		Device:              device,
		DeviceSpecific:      true,
		EventLog:            []ei.EventInfo{eventInfo},
		Parameters:          parameters,
	}

	return action, nil
}
<|endoftext|>


<commit_before>package halo

import (
	"sort"

	"github.com/phil-mansfield/gotetra/render/io"
	"github.com/phil-mansfield/table"
)

// halos allows for arrays of halo properties to be sorted simultaneously.
type halos struct {
	rids []int
	xs, ys, zs, ms, rs []float64
}

func (hs *halos) Len() int { return len(hs.rs) }
func (hs *halos) Less(i, j int) bool { return hs.rs[i] < hs.rs[j] }
func (hs *halos) Swap(i, j int) {
	hs.rs[i], hs.rs[j] = hs.rs[j], hs.rs[i]
	hs.ms[i], hs.ms[j] = hs.ms[j], hs.ms[i]
	hs.xs[i], hs.xs[j] = hs.xs[j], hs.xs[i]
	hs.ys[i], hs.ys[j] = hs.ys[j], hs.ys[i]
	hs.zs[i], hs.zs[j] = hs.zs[j], hs.zs[i]
	hs.rids[i], hs.rids[j] = hs.rids[j], hs.rids[i]
}

// ReadRockstar reads halo information from the given Rockstar catalog, sorted
// from largest to smallest.
func ReadRockstar(
	file string, rType Radius, cosmo *io.CosmologyHeader,
) (rids []int, xs, ys, zs, ms, rs []float64, err error) {
	rCol := rType.RockstarColumn()
	idCol, xCol, yCol, zCol := 1, 17, 18, 19

	colIdxs := []int{ idCol, xCol, yCol, zCol, rCol }
	cols, err := table.ReadTable(file, colIdxs, nil)
	if err != nil { return nil, nil, nil, nil, nil, nil, err }

	ids := cols[0]
	xs, ys, zs = cols[1], cols[2], cols[3]
	if rType.RockstarMass() {
		ms = cols[4]
		rs = make([]float64, len(ms))
		rType.Radius(cosmo, ms, rs)
	} else {
		rs = cols[4]
		ms = make([]float64, len(rs))
		for i := range rs { rs[i] /= 1000 } // kpc -> Mpc
		rType.Mass(cosmo, rs, ms)
	}

	rids = make([]int, len(ids))
	for i := range rids { rids[i] = int(ids[i]) }

	sort.Sort(sort.Reverse(&halos{ rids, xs, ys, zs, ms, rs }))
	return rids, xs, ys, zs, ms, rs, nil
}

type Val int
const (
	Scale Val = iota
	ID
	DescScale
	DescID
	NumProg
	PID
	UPID
	DescPID
	Phantom
	SAMMVir
	MVir
	RVir
	Rs
	Vrms
	MMP
	ScaleOfLastMMP
	VMax
	X
	Y
	Z
	Vx
	Vy
	Vz
	Jx
	Jy
	Jz
	Spin
	BreadthFirstID
	DepthFirstID
	TreeRootID
	OrigHaloID
	SnapNum
	NextCoprogenitorDepthFirstID
	LastProgenitorDepthFirstID
	RsKylpin
	MVirAll
	M200b
	M200c
	M500c
	M2500c
	XOff
	Voff
	SpinBullock
	BToA
	CToA
	Ax
	Ay
	Az
	BToA500c
	CToA500c
	Ax500c
	Ay500c
	Az500c
	TU
	MAcc
	MPeak
	VAcc
	VPeak
	HalfmassScale
	AccRateInst
	AccRate100Myr
	AccRateTdyn
	valNum
)

func ReadRockstarVals(
	file string, cosmo *io.CosmologyHeader, valFlags ...Val,
) (ids []int, vals[][]float64, err error) {
	colIdxs := []int{ int(ID) }
	for _, val := range valFlags {
		colIdxs = append(colIdxs, int(val))
	}
	cols, err := table.ReadTable(file, colIdxs, nil)
	if err != nil { return nil, nil, err }

	for i := range cols[0] {
		ids[i] = int(cols[0][i])
	}
	return ids, cols[1:], nil
}

func init() {
	if valNum != 61 { panic("Internal gotetra setup error.") }
}
<commit_msg>bug fix in halo/io.go<commit_after>package halo

import (
	"sort"

	"github.com/phil-mansfield/gotetra/render/io"
	"github.com/phil-mansfield/table"
)

// halos allows for arrays of halo properties to be sorted simultaneously.
type halos struct {
	rids []int
	xs, ys, zs, ms, rs []float64
}

func (hs *halos) Len() int { return len(hs.rs) }
func (hs *halos) Less(i, j int) bool { return hs.rs[i] < hs.rs[j] }
func (hs *halos) Swap(i, j int) {
	hs.rs[i], hs.rs[j] = hs.rs[j], hs.rs[i]
	hs.ms[i], hs.ms[j] = hs.ms[j], hs.ms[i]
	hs.xs[i], hs.xs[j] = hs.xs[j], hs.xs[i]
	hs.ys[i], hs.ys[j] = hs.ys[j], hs.ys[i]
	hs.zs[i], hs.zs[j] = hs.zs[j], hs.zs[i]
	hs.rids[i], hs.rids[j] = hs.rids[j], hs.rids[i]
}

// ReadRockstar reads halo information from the given Rockstar catalog, sorted
// from largest to smallest.
func ReadRockstar(
	file string, rType Radius, cosmo *io.CosmologyHeader,
) (rids []int, xs, ys, zs, ms, rs []float64, err error) {
	rCol := rType.RockstarColumn()
	idCol, xCol, yCol, zCol := 1, 17, 18, 19

	colIdxs := []int{ idCol, xCol, yCol, zCol, rCol }
	cols, err := table.ReadTable(file, colIdxs, nil)
	if err != nil { return nil, nil, nil, nil, nil, nil, err }

	ids := cols[0]
	xs, ys, zs = cols[1], cols[2], cols[3]
	if rType.RockstarMass() {
		ms = cols[4]
		rs = make([]float64, len(ms))
		rType.Radius(cosmo, ms, rs)
	} else {
		rs = cols[4]
		ms = make([]float64, len(rs))
		for i := range rs { rs[i] /= 1000 } // kpc -> Mpc
		rType.Mass(cosmo, rs, ms)
	}

	rids = make([]int, len(ids))
	for i := range rids { rids[i] = int(ids[i]) }

	sort.Sort(sort.Reverse(&halos{ rids, xs, ys, zs, ms, rs }))
	return rids, xs, ys, zs, ms, rs, nil
}

type Val int
const (
	Scale Val = iota
	ID
	DescScale
	DescID
	NumProg
	PID
	UPID
	DescPID
	Phantom
	SAMMVir
	MVir
	RVir
	Rs
	Vrms
	MMP
	ScaleOfLastMMP
	VMax
	X
	Y
	Z
	Vx
	Vy
	Vz
	Jx
	Jy
	Jz
	Spin
	BreadthFirstID
	DepthFirstID
	TreeRootID
	OrigHaloID
	SnapNum
	NextCoprogenitorDepthFirstID
	LastProgenitorDepthFirstID
	RsKylpin
	MVirAll
	M200b
	M200c
	M500c
	M2500c
	XOff
	Voff
	SpinBullock
	BToA
	CToA
	Ax
	Ay
	Az
	BToA500c
	CToA500c
	Ax500c
	Ay500c
	Az500c
	TU
	MAcc
	MPeak
	VAcc
	VPeak
	HalfmassScale
	AccRateInst
	AccRate100Myr
	AccRateTdyn
	valNum
)

func ReadRockstarVals(
	file string, cosmo *io.CosmologyHeader, valFlags ...Val,
) (ids []int, vals[][]float64, err error) {
	colIdxs := []int{ int(ID) }
	for _, val := range valFlags {
		colIdxs = append(colIdxs, int(val))
	}
	cols, err := table.ReadTable(file, colIdxs, nil)
	if err != nil { return nil, nil, err }

	ids = make([]int, len(cols[0]))
	for i := range cols[0] {
		ids[i] = int(cols[0][i])
	}

	if len(cols) == 1 {
		return ids, [][]float64{}, nil
	} else {
		return ids, cols[1:], nil
	}
}

func init() {
	if valNum != 62 { panic("Internal gotetra setup error.") }
}
<|endoftext|>