<commit_before>package main

import (
	"fmt"
	"strings"
	"time"

	"github.com/docker/docker/pkg/integration/checker"
	"github.com/go-check/check"
)

// Regression test for https://github.com/docker/docker/issues/7843
func (s *DockerSuite) TestStartAttachReturnsOnError(c *check.C) {
	// Windows does not support link
	testRequires(c, DaemonIsLinux)
	dockerCmd(c, "run", "-d", "--name", "test", "busybox")
	dockerCmd(c, "wait", "test")

	// Expect this to fail because the above container is stopped, this is what we want
	out, _, err := dockerCmdWithError("run", "-d", "--name", "test2", "--link", "test:test", "busybox")
	// err shouldn't be nil because container test2 try to link to stopped container
	c.Assert(err, checker.NotNil, check.Commentf("out: %s", out))

	ch := make(chan error)
	go func() {
		// Attempt to start attached to the container that won't start
		// This should return an error immediately since the container can't be started
		if _, _, err := dockerCmdWithError("start", "-a", "test2"); err == nil {
			ch <- fmt.Errorf("Expected error but got none")
		}
		close(ch)
	}()

	select {
	case err := <-ch:
		c.Assert(err, check.IsNil)
	case <-time.After(5 * time.Second):
		c.Fatalf("Attach did not exit properly")
	}
}

// gh#8555: Exit code should be passed through when using start -a
func (s *DockerSuite) TestStartAttachCorrectExitCode(c *check.C) {
	testRequires(c, DaemonIsLinux)
	out, _, _ := dockerCmdWithStdoutStderr(c, "run", "-d", "busybox", "sh", "-c", "sleep 2; exit 1")
	out = strings.TrimSpace(out)

	// make sure the container has exited before trying the "start -a"
	dockerCmd(c, "wait", out)

	startOut, exitCode, err := dockerCmdWithError("start", "-a", out)
	// start command should fail
	c.Assert(err, checker.NotNil, check.Commentf("startOut: %s", startOut))
	// start -a did not respond with proper exit code
	c.Assert(exitCode, checker.Equals, 1, check.Commentf("startOut: %s", startOut))

}

func (s *DockerSuite) TestStartAttachSilent(c *check.C) {
	name := "teststartattachcorrectexitcode"
	dockerCmd(c, "run", "--name", name, "busybox", "echo", "test")

	// make sure the container has exited before trying the "start -a"
	dockerCmd(c, "wait", name)

	startOut, _ := dockerCmd(c, "start", "-a", name)
	// start -a produced unexpected output
	c.Assert(startOut, checker.Equals, "test\n")
}

func (s *DockerSuite) TestStartRecordError(c *check.C) {
	// TODO Windows CI: Requires further porting work. Should be possible.
	testRequires(c, DaemonIsLinux)
	// when container runs successfully, we should not have state.Error
	dockerCmd(c, "run", "-d", "-p", "9999:9999", "--name", "test", "busybox", "top")
	stateErr := inspectField(c, "test", "State.Error")
	// Expected to not have state error
	c.Assert(stateErr, checker.Equals, "")

	// Expect this to fail and records error because of ports conflict
	out, _, err := dockerCmdWithError("run", "-d", "--name", "test2", "-p", "9999:9999", "busybox", "top")
	// err shouldn't be nil because docker run will fail
	c.Assert(err, checker.NotNil, check.Commentf("out: %s", out))

	stateErr = inspectField(c, "test2", "State.Error")
	c.Assert(stateErr, checker.Contains, "port is already allocated")

	// Expect the conflict to be resolved when we stop the initial container
	dockerCmd(c, "stop", "test")
	dockerCmd(c, "start", "test2")
	stateErr = inspectField(c, "test2", "State.Error")
	// Expected to not have state error but got one
	c.Assert(stateErr, checker.Equals, "")
}

func (s *DockerSuite) TestStartPausedContainer(c *check.C) {
	// Windows does not support pausing containers
	testRequires(c, DaemonIsLinux)
	defer unpauseAllContainers()

	dockerCmd(c, "run", "-d", "--name", "testing", "busybox", "top")

	dockerCmd(c, "pause", "testing")

	out, _, err := dockerCmdWithError("start", "testing")
	// an error should have been shown that you cannot start paused container
	c.Assert(err, checker.NotNil, check.Commentf("out: %s", out))
	// an error should have been shown that you cannot start paused container
	c.Assert(out, checker.Contains, "Cannot start a paused container, try unpause instead.")
}

func (s *DockerSuite) TestStartMultipleContainers(c *check.C) {
	// Windows does not support --link
	testRequires(c, DaemonIsLinux)
	// run a container named 'parent' and create two container link to `parent`
	dockerCmd(c, "run", "-d", "--name", "parent", "busybox", "top")

	for _, container := range []string{"child_first", "child_second"} {
		dockerCmd(c, "create", "--name", container, "--link", "parent:parent", "busybox", "top")
	}

	// stop 'parent' container
	dockerCmd(c, "stop", "parent")

	out := inspectField(c, "parent", "State.Running")
	// Container should be stopped
	c.Assert(out, checker.Equals, "false")

	// start all the three containers, container `child_first` start first which should be failed
	// container 'parent' start second and then start container 'child_second'
	expOut := "Cannot link to a non running container"
	expErr := "failed to start containers: [child_first]"
	out, _, err := dockerCmdWithError("start", "child_first", "parent", "child_second")
	// err shouldn't be nil because start will fail
	c.Assert(err, checker.NotNil, check.Commentf("out: %s", out))
	// output does not correspond to what was expected
	if !(strings.Contains(out, expOut) || strings.Contains(err.Error(), expErr)) {
		c.Fatalf("Expected out: %v with err: %v  but got out: %v with err: %v", expOut, expErr, out, err)
	}

	for container, expected := range map[string]string{"parent": "true", "child_first": "false", "child_second": "true"} {
		out := inspectField(c, container, "State.Running")
		// Container running state wrong
		c.Assert(out, checker.Equals, expected)
	}
}

func (s *DockerSuite) TestStartAttachMultipleContainers(c *check.C) {
	// run  multiple containers to test
	for _, container := range []string{"test1", "test2", "test3"} {
		dockerCmd(c, "run", "-d", "--name", container, "busybox", "top")
	}

	// stop all the containers
	for _, container := range []string{"test1", "test2", "test3"} {
		dockerCmd(c, "stop", container)
	}

	// test start and attach multiple containers at once, expected error
	for _, option := range []string{"-a", "-i", "-ai"} {
		out, _, err := dockerCmdWithError("start", option, "test1", "test2", "test3")
		// err shouldn't be nil because start will fail
		c.Assert(err, checker.NotNil, check.Commentf("out: %s", out))
		// output does not correspond to what was expected
		c.Assert(out, checker.Contains, "You cannot start and attach multiple containers at once.")
	}

	// confirm the state of all the containers be stopped
	for container, expected := range map[string]string{"test1": "false", "test2": "false", "test3": "false"} {
		out := inspectField(c, container, "State.Running")
		// Container running state wrong
		c.Assert(out, checker.Equals, expected)
	}
}
<commit_msg>Windows CI: Fix TestStartAttachMultipleContainers<commit_after>package main

import (
	"fmt"
	"strings"
	"time"

	"github.com/docker/docker/pkg/integration/checker"
	"github.com/go-check/check"
)

// Regression test for https://github.com/docker/docker/issues/7843
func (s *DockerSuite) TestStartAttachReturnsOnError(c *check.C) {
	// Windows does not support link
	testRequires(c, DaemonIsLinux)
	dockerCmd(c, "run", "-d", "--name", "test", "busybox")
	dockerCmd(c, "wait", "test")

	// Expect this to fail because the above container is stopped, this is what we want
	out, _, err := dockerCmdWithError("run", "-d", "--name", "test2", "--link", "test:test", "busybox")
	// err shouldn't be nil because container test2 try to link to stopped container
	c.Assert(err, checker.NotNil, check.Commentf("out: %s", out))

	ch := make(chan error)
	go func() {
		// Attempt to start attached to the container that won't start
		// This should return an error immediately since the container can't be started
		if _, _, err := dockerCmdWithError("start", "-a", "test2"); err == nil {
			ch <- fmt.Errorf("Expected error but got none")
		}
		close(ch)
	}()

	select {
	case err := <-ch:
		c.Assert(err, check.IsNil)
	case <-time.After(5 * time.Second):
		c.Fatalf("Attach did not exit properly")
	}
}

// gh#8555: Exit code should be passed through when using start -a
func (s *DockerSuite) TestStartAttachCorrectExitCode(c *check.C) {
	testRequires(c, DaemonIsLinux)
	out, _, _ := dockerCmdWithStdoutStderr(c, "run", "-d", "busybox", "sh", "-c", "sleep 2; exit 1")
	out = strings.TrimSpace(out)

	// make sure the container has exited before trying the "start -a"
	dockerCmd(c, "wait", out)

	startOut, exitCode, err := dockerCmdWithError("start", "-a", out)
	// start command should fail
	c.Assert(err, checker.NotNil, check.Commentf("startOut: %s", startOut))
	// start -a did not respond with proper exit code
	c.Assert(exitCode, checker.Equals, 1, check.Commentf("startOut: %s", startOut))

}

func (s *DockerSuite) TestStartAttachSilent(c *check.C) {
	name := "teststartattachcorrectexitcode"
	dockerCmd(c, "run", "--name", name, "busybox", "echo", "test")

	// make sure the container has exited before trying the "start -a"
	dockerCmd(c, "wait", name)

	startOut, _ := dockerCmd(c, "start", "-a", name)
	// start -a produced unexpected output
	c.Assert(startOut, checker.Equals, "test\n")
}

func (s *DockerSuite) TestStartRecordError(c *check.C) {
	// TODO Windows CI: Requires further porting work. Should be possible.
	testRequires(c, DaemonIsLinux)
	// when container runs successfully, we should not have state.Error
	dockerCmd(c, "run", "-d", "-p", "9999:9999", "--name", "test", "busybox", "top")
	stateErr := inspectField(c, "test", "State.Error")
	// Expected to not have state error
	c.Assert(stateErr, checker.Equals, "")

	// Expect this to fail and records error because of ports conflict
	out, _, err := dockerCmdWithError("run", "-d", "--name", "test2", "-p", "9999:9999", "busybox", "top")
	// err shouldn't be nil because docker run will fail
	c.Assert(err, checker.NotNil, check.Commentf("out: %s", out))

	stateErr = inspectField(c, "test2", "State.Error")
	c.Assert(stateErr, checker.Contains, "port is already allocated")

	// Expect the conflict to be resolved when we stop the initial container
	dockerCmd(c, "stop", "test")
	dockerCmd(c, "start", "test2")
	stateErr = inspectField(c, "test2", "State.Error")
	// Expected to not have state error but got one
	c.Assert(stateErr, checker.Equals, "")
}

func (s *DockerSuite) TestStartPausedContainer(c *check.C) {
	// Windows does not support pausing containers
	testRequires(c, DaemonIsLinux)
	defer unpauseAllContainers()

	dockerCmd(c, "run", "-d", "--name", "testing", "busybox", "top")

	dockerCmd(c, "pause", "testing")

	out, _, err := dockerCmdWithError("start", "testing")
	// an error should have been shown that you cannot start paused container
	c.Assert(err, checker.NotNil, check.Commentf("out: %s", out))
	// an error should have been shown that you cannot start paused container
	c.Assert(out, checker.Contains, "Cannot start a paused container, try unpause instead.")
}

func (s *DockerSuite) TestStartMultipleContainers(c *check.C) {
	// Windows does not support --link
	testRequires(c, DaemonIsLinux)
	// run a container named 'parent' and create two container link to `parent`
	dockerCmd(c, "run", "-d", "--name", "parent", "busybox", "top")

	for _, container := range []string{"child_first", "child_second"} {
		dockerCmd(c, "create", "--name", container, "--link", "parent:parent", "busybox", "top")
	}

	// stop 'parent' container
	dockerCmd(c, "stop", "parent")

	out := inspectField(c, "parent", "State.Running")
	// Container should be stopped
	c.Assert(out, checker.Equals, "false")

	// start all the three containers, container `child_first` start first which should be failed
	// container 'parent' start second and then start container 'child_second'
	expOut := "Cannot link to a non running container"
	expErr := "failed to start containers: [child_first]"
	out, _, err := dockerCmdWithError("start", "child_first", "parent", "child_second")
	// err shouldn't be nil because start will fail
	c.Assert(err, checker.NotNil, check.Commentf("out: %s", out))
	// output does not correspond to what was expected
	if !(strings.Contains(out, expOut) || strings.Contains(err.Error(), expErr)) {
		c.Fatalf("Expected out: %v with err: %v  but got out: %v with err: %v", expOut, expErr, out, err)
	}

	for container, expected := range map[string]string{"parent": "true", "child_first": "false", "child_second": "true"} {
		out := inspectField(c, container, "State.Running")
		// Container running state wrong
		c.Assert(out, checker.Equals, expected)
	}
}

func (s *DockerSuite) TestStartAttachMultipleContainers(c *check.C) {
	// run  multiple containers to test
	for _, container := range []string{"test1", "test2", "test3"} {
		runSleepingContainer(c, "--name", container)
	}

	// stop all the containers
	for _, container := range []string{"test1", "test2", "test3"} {
		dockerCmd(c, "stop", container)
	}

	// test start and attach multiple containers at once, expected error
	for _, option := range []string{"-a", "-i", "-ai"} {
		out, _, err := dockerCmdWithError("start", option, "test1", "test2", "test3")
		// err shouldn't be nil because start will fail
		c.Assert(err, checker.NotNil, check.Commentf("out: %s", out))
		// output does not correspond to what was expected
		c.Assert(out, checker.Contains, "You cannot start and attach multiple containers at once.")
	}

	// confirm the state of all the containers be stopped
	for container, expected := range map[string]string{"test1": "false", "test2": "false", "test3": "false"} {
		out := inspectField(c, container, "State.Running")
		// Container running state wrong
		c.Assert(out, checker.Equals, expected)
	}
}
<|endoftext|>


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

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

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

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

package object

import (
	"fmt"
	"io"
	"math/rand"
	"os"
	"path"
	"strings"

	"context"
	"net/http"
	"net/url"

	"github.com/vmware/govmomi/property"
	"github.com/vmware/govmomi/session"
	"github.com/vmware/govmomi/vim25"
	"github.com/vmware/govmomi/vim25/mo"
	"github.com/vmware/govmomi/vim25/soap"
	"github.com/vmware/govmomi/vim25/types"
)

// DatastoreNoSuchDirectoryError is returned when a directory could not be found.
type DatastoreNoSuchDirectoryError struct {
	verb    string
	subject string
}

func (e DatastoreNoSuchDirectoryError) Error() string {
	return fmt.Sprintf("cannot %s '%s': No such directory", e.verb, e.subject)
}

// DatastoreNoSuchFileError is returned when a file could not be found.
type DatastoreNoSuchFileError struct {
	verb    string
	subject string
}

func (e DatastoreNoSuchFileError) Error() string {
	return fmt.Sprintf("cannot %s '%s': No such file", e.verb, e.subject)
}

type Datastore struct {
	Common

	DatacenterPath string
}

func NewDatastore(c *vim25.Client, ref types.ManagedObjectReference) *Datastore {
	return &Datastore{
		Common: NewCommon(c, ref),
	}
}

func (d Datastore) Path(path string) string {
	return (&DatastorePath{
		Datastore: d.Name(),
		Path:      path,
	}).String()
}

// NewURL constructs a url.URL with the given file path for datastore access over HTTP.
func (d Datastore) NewURL(path string) *url.URL {
	u := d.c.URL()

	return &url.URL{
		Scheme: u.Scheme,
		Host:   u.Host,
		Path:   fmt.Sprintf("/folder/%s", path),
		RawQuery: url.Values{
			"dcPath": []string{d.DatacenterPath},
			"dsName": []string{d.Name()},
		}.Encode(),
	}
}

// URL is deprecated, use NewURL instead.
func (d Datastore) URL(ctx context.Context, dc *Datacenter, path string) (*url.URL, error) {
	return d.NewURL(path), nil
}

func (d Datastore) Browser(ctx context.Context) (*HostDatastoreBrowser, error) {
	var do mo.Datastore

	err := d.Properties(ctx, d.Reference(), []string{"browser"}, &do)
	if err != nil {
		return nil, err
	}

	return NewHostDatastoreBrowser(d.c, do.Browser), nil
}

func (d Datastore) useServiceTicket() bool {
	// If connected to workstation, service ticketing not supported
	// If connected to ESX, service ticketing not needed
	if !d.c.IsVC() {
		return false
	}

	key := "GOVMOMI_USE_SERVICE_TICKET"

	val := d.c.URL().Query().Get(key)
	if val == "" {
		val = os.Getenv(key)
	}

	if val == "1" || val == "true" {
		return true
	}

	return false
}

func (d Datastore) useServiceTicketHostName(name string) bool {
	// No need if talking directly to ESX.
	if !d.c.IsVC() {
		return false
	}

	// If version happens to be < 5.1
	if name == "" {
		return false
	}

	// If the HostSystem is using DHCP on a network without dynamic DNS,
	// HostSystem.Config.Network.DnsConfig.HostName is set to "localhost" by default.
	// This resolves to "localhost.localdomain" by default via /etc/hosts on ESX.
	// In that case, we will stick with the HostSystem.Name which is the IP address that
	// was used to connect the host to VC.
	if name == "localhost.localdomain" {
		return false
	}

	// Still possible to have HostName that don't resolve via DNS,
	// so we default to false.
	key := "GOVMOMI_USE_SERVICE_TICKET_HOSTNAME"

	val := d.c.URL().Query().Get(key)
	if val == "" {
		val = os.Getenv(key)
	}

	if val == "1" || val == "true" {
		return true
	}

	return false
}

type datastoreServiceTicketHostKey struct{}

// HostContext returns a Context where the given host will be used for datastore HTTP access
// via the ServiceTicket method.
func (d Datastore) HostContext(ctx context.Context, host *HostSystem) context.Context {
	return context.WithValue(ctx, datastoreServiceTicketHostKey{}, host)
}

// ServiceTicket obtains a ticket via AcquireGenericServiceTicket and returns it an http.Cookie with the url.URL
// that can be used along with the ticket cookie to access the given path.  An host is chosen at random unless the
// the given Context was created with a specific host via the HostContext method.
func (d Datastore) ServiceTicket(ctx context.Context, path string, method string) (*url.URL, *http.Cookie, error) {
	u := d.NewURL(path)

	host, ok := ctx.Value(datastoreServiceTicketHostKey{}).(*HostSystem)

	if !ok {
		if !d.useServiceTicket() {
			return u, nil, nil
		}

		hosts, err := d.AttachedHosts(ctx)
		if err != nil {
			return nil, nil, err
		}

		if len(hosts) == 0 {
			// Fallback to letting vCenter choose a host
			return u, nil, nil
		}

		// Pick a random attached host
		host = hosts[rand.Intn(len(hosts))]
	}

	ips, err := host.ManagementIPs(ctx)
	if err != nil {
		return nil, nil, err
	}

	if len(ips) > 0 {
		// prefer a ManagementIP
		u.Host = ips[0].String()
	} else {
		// fallback to inventory name
		u.Host, err = host.ObjectName(ctx)
		if err != nil {
			return nil, nil, err
		}
	}

	// VC datacenter path will not be valid against ESX
	q := u.Query()
	delete(q, "dcPath")
	u.RawQuery = q.Encode()

	spec := types.SessionManagerHttpServiceRequestSpec{
		Url: u.String(),
		// See SessionManagerHttpServiceRequestSpecMethod enum
		Method: fmt.Sprintf("http%s%s", method[0:1], strings.ToLower(method[1:])),
	}

	sm := session.NewManager(d.Client())

	ticket, err := sm.AcquireGenericServiceTicket(ctx, &spec)
	if err != nil {
		return nil, nil, err
	}

	cookie := &http.Cookie{
		Name:  "vmware_cgi_ticket",
		Value: ticket.Id,
	}

	if d.useServiceTicketHostName(ticket.HostName) {
		u.Host = ticket.HostName
	}

	d.Client().SetThumbprint(u.Host, ticket.SslThumbprint)

	return u, cookie, nil
}

func (d Datastore) uploadTicket(ctx context.Context, path string, param *soap.Upload) (*url.URL, *soap.Upload, error) {
	p := soap.DefaultUpload
	if param != nil {
		p = *param // copy
	}

	u, ticket, err := d.ServiceTicket(ctx, path, p.Method)
	if err != nil {
		return nil, nil, err
	}

	p.Ticket = ticket

	return u, &p, nil
}

func (d Datastore) downloadTicket(ctx context.Context, path string, param *soap.Download) (*url.URL, *soap.Download, error) {
	p := soap.DefaultDownload
	if param != nil {
		p = *param // copy
	}

	u, ticket, err := d.ServiceTicket(ctx, path, p.Method)
	if err != nil {
		return nil, nil, err
	}

	p.Ticket = ticket

	return u, &p, nil
}

// Upload via soap.Upload with an http service ticket
func (d Datastore) Upload(ctx context.Context, f io.Reader, path string, param *soap.Upload) error {
	u, p, err := d.uploadTicket(ctx, path, param)
	if err != nil {
		return err
	}
	return d.Client().Upload(f, u, p)
}

// UploadFile via soap.Upload with an http service ticket
func (d Datastore) UploadFile(ctx context.Context, file string, path string, param *soap.Upload) error {
	u, p, err := d.uploadTicket(ctx, path, param)
	if err != nil {
		return err
	}
	return d.Client().UploadFile(file, u, p)
}

// Download via soap.Download with an http service ticket
func (d Datastore) Download(ctx context.Context, path string, param *soap.Download) (io.ReadCloser, int64, error) {
	u, p, err := d.downloadTicket(ctx, path, param)
	if err != nil {
		return nil, 0, err
	}
	return d.Client().Download(u, p)
}

// DownloadFile via soap.Download with an http service ticket
func (d Datastore) DownloadFile(ctx context.Context, path string, file string, param *soap.Download) error {
	u, p, err := d.downloadTicket(ctx, path, param)
	if err != nil {
		return err
	}
	return d.Client().DownloadFile(file, u, p)
}

// AttachedHosts returns hosts that have this Datastore attached, accessible and writable.
func (d Datastore) AttachedHosts(ctx context.Context) ([]*HostSystem, error) {
	var ds mo.Datastore
	var hosts []*HostSystem

	pc := property.DefaultCollector(d.Client())
	err := pc.RetrieveOne(ctx, d.Reference(), []string{"host"}, &ds)
	if err != nil {
		return nil, err
	}

	mounts := make(map[types.ManagedObjectReference]types.DatastoreHostMount)
	var refs []types.ManagedObjectReference
	for _, host := range ds.Host {
		refs = append(refs, host.Key)
		mounts[host.Key] = host
	}

	var hs []mo.HostSystem
	err = pc.Retrieve(ctx, refs, []string{"runtime.connectionState", "runtime.powerState"}, &hs)
	if err != nil {
		return nil, err
	}

	for _, host := range hs {
		if host.Runtime.ConnectionState == types.HostSystemConnectionStateConnected &&
			host.Runtime.PowerState == types.HostSystemPowerStatePoweredOn {

			mount := mounts[host.Reference()]
			info := mount.MountInfo

			if *info.Mounted && *info.Accessible && info.AccessMode == string(types.HostMountModeReadWrite) {
				hosts = append(hosts, NewHostSystem(d.Client(), mount.Key))
			}
		}
	}

	return hosts, nil
}

// AttachedClusterHosts returns hosts that have this Datastore attached, accessible and writable and are members of the given cluster.
func (d Datastore) AttachedClusterHosts(ctx context.Context, cluster *ComputeResource) ([]*HostSystem, error) {
	var hosts []*HostSystem

	clusterHosts, err := cluster.Hosts(ctx)
	if err != nil {
		return nil, err
	}

	attachedHosts, err := d.AttachedHosts(ctx)
	if err != nil {
		return nil, err
	}

	refs := make(map[types.ManagedObjectReference]bool)
	for _, host := range attachedHosts {
		refs[host.Reference()] = true
	}

	for _, host := range clusterHosts {
		if refs[host.Reference()] {
			hosts = append(hosts, host)
		}
	}

	return hosts, nil
}

func (d Datastore) Stat(ctx context.Context, file string) (types.BaseFileInfo, error) {
	b, err := d.Browser(ctx)
	if err != nil {
		return nil, err
	}

	spec := types.HostDatastoreBrowserSearchSpec{
		Details: &types.FileQueryFlags{
			FileType:     true,
			FileSize:     true,
			Modification: true,
			FileOwner:    types.NewBool(true),
		},
		MatchPattern: []string{path.Base(file)},
	}

	dsPath := d.Path(path.Dir(file))
	task, err := b.SearchDatastore(ctx, dsPath, &spec)
	if err != nil {
		return nil, err
	}

	info, err := task.WaitForResult(ctx, nil)
	if err != nil {
		if info == nil || info.Error != nil {
			_, ok := info.Error.Fault.(*types.FileNotFound)
			if ok {
				// FileNotFound means the base path doesn't exist.
				return nil, DatastoreNoSuchDirectoryError{"stat", dsPath}
			}
		}

		return nil, err
	}

	res := info.Result.(types.HostDatastoreBrowserSearchResults)
	if len(res.File) == 0 {
		// File doesn't exist
		return nil, DatastoreNoSuchFileError{"stat", d.Path(file)}
	}

	return res.File[0], nil

}

// Type returns the type of file system volume.
func (d Datastore) Type(ctx context.Context) (types.HostFileSystemVolumeFileSystemType, error) {
	var mds mo.Datastore

	if err := d.Properties(ctx, d.Reference(), []string{"summary.type"}, &mds); err != nil {
		return types.HostFileSystemVolumeFileSystemType(""), err
	}
	return types.HostFileSystemVolumeFileSystemType(mds.Summary.Type), nil
}
<commit_msg>Use IsFileNotFound helper in Datastore.Stat (#969)<commit_after>/*
Copyright (c) 2015-2016 VMware, Inc. All Rights Reserved.

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

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

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

package object

import (
	"fmt"
	"io"
	"math/rand"
	"os"
	"path"
	"strings"

	"context"
	"net/http"
	"net/url"

	"github.com/vmware/govmomi/property"
	"github.com/vmware/govmomi/session"
	"github.com/vmware/govmomi/vim25"
	"github.com/vmware/govmomi/vim25/mo"
	"github.com/vmware/govmomi/vim25/soap"
	"github.com/vmware/govmomi/vim25/types"
)

// DatastoreNoSuchDirectoryError is returned when a directory could not be found.
type DatastoreNoSuchDirectoryError struct {
	verb    string
	subject string
}

func (e DatastoreNoSuchDirectoryError) Error() string {
	return fmt.Sprintf("cannot %s '%s': No such directory", e.verb, e.subject)
}

// DatastoreNoSuchFileError is returned when a file could not be found.
type DatastoreNoSuchFileError struct {
	verb    string
	subject string
}

func (e DatastoreNoSuchFileError) Error() string {
	return fmt.Sprintf("cannot %s '%s': No such file", e.verb, e.subject)
}

type Datastore struct {
	Common

	DatacenterPath string
}

func NewDatastore(c *vim25.Client, ref types.ManagedObjectReference) *Datastore {
	return &Datastore{
		Common: NewCommon(c, ref),
	}
}

func (d Datastore) Path(path string) string {
	return (&DatastorePath{
		Datastore: d.Name(),
		Path:      path,
	}).String()
}

// NewURL constructs a url.URL with the given file path for datastore access over HTTP.
func (d Datastore) NewURL(path string) *url.URL {
	u := d.c.URL()

	return &url.URL{
		Scheme: u.Scheme,
		Host:   u.Host,
		Path:   fmt.Sprintf("/folder/%s", path),
		RawQuery: url.Values{
			"dcPath": []string{d.DatacenterPath},
			"dsName": []string{d.Name()},
		}.Encode(),
	}
}

// URL is deprecated, use NewURL instead.
func (d Datastore) URL(ctx context.Context, dc *Datacenter, path string) (*url.URL, error) {
	return d.NewURL(path), nil
}

func (d Datastore) Browser(ctx context.Context) (*HostDatastoreBrowser, error) {
	var do mo.Datastore

	err := d.Properties(ctx, d.Reference(), []string{"browser"}, &do)
	if err != nil {
		return nil, err
	}

	return NewHostDatastoreBrowser(d.c, do.Browser), nil
}

func (d Datastore) useServiceTicket() bool {
	// If connected to workstation, service ticketing not supported
	// If connected to ESX, service ticketing not needed
	if !d.c.IsVC() {
		return false
	}

	key := "GOVMOMI_USE_SERVICE_TICKET"

	val := d.c.URL().Query().Get(key)
	if val == "" {
		val = os.Getenv(key)
	}

	if val == "1" || val == "true" {
		return true
	}

	return false
}

func (d Datastore) useServiceTicketHostName(name string) bool {
	// No need if talking directly to ESX.
	if !d.c.IsVC() {
		return false
	}

	// If version happens to be < 5.1
	if name == "" {
		return false
	}

	// If the HostSystem is using DHCP on a network without dynamic DNS,
	// HostSystem.Config.Network.DnsConfig.HostName is set to "localhost" by default.
	// This resolves to "localhost.localdomain" by default via /etc/hosts on ESX.
	// In that case, we will stick with the HostSystem.Name which is the IP address that
	// was used to connect the host to VC.
	if name == "localhost.localdomain" {
		return false
	}

	// Still possible to have HostName that don't resolve via DNS,
	// so we default to false.
	key := "GOVMOMI_USE_SERVICE_TICKET_HOSTNAME"

	val := d.c.URL().Query().Get(key)
	if val == "" {
		val = os.Getenv(key)
	}

	if val == "1" || val == "true" {
		return true
	}

	return false
}

type datastoreServiceTicketHostKey struct{}

// HostContext returns a Context where the given host will be used for datastore HTTP access
// via the ServiceTicket method.
func (d Datastore) HostContext(ctx context.Context, host *HostSystem) context.Context {
	return context.WithValue(ctx, datastoreServiceTicketHostKey{}, host)
}

// ServiceTicket obtains a ticket via AcquireGenericServiceTicket and returns it an http.Cookie with the url.URL
// that can be used along with the ticket cookie to access the given path.  An host is chosen at random unless the
// the given Context was created with a specific host via the HostContext method.
func (d Datastore) ServiceTicket(ctx context.Context, path string, method string) (*url.URL, *http.Cookie, error) {
	u := d.NewURL(path)

	host, ok := ctx.Value(datastoreServiceTicketHostKey{}).(*HostSystem)

	if !ok {
		if !d.useServiceTicket() {
			return u, nil, nil
		}

		hosts, err := d.AttachedHosts(ctx)
		if err != nil {
			return nil, nil, err
		}

		if len(hosts) == 0 {
			// Fallback to letting vCenter choose a host
			return u, nil, nil
		}

		// Pick a random attached host
		host = hosts[rand.Intn(len(hosts))]
	}

	ips, err := host.ManagementIPs(ctx)
	if err != nil {
		return nil, nil, err
	}

	if len(ips) > 0 {
		// prefer a ManagementIP
		u.Host = ips[0].String()
	} else {
		// fallback to inventory name
		u.Host, err = host.ObjectName(ctx)
		if err != nil {
			return nil, nil, err
		}
	}

	// VC datacenter path will not be valid against ESX
	q := u.Query()
	delete(q, "dcPath")
	u.RawQuery = q.Encode()

	spec := types.SessionManagerHttpServiceRequestSpec{
		Url: u.String(),
		// See SessionManagerHttpServiceRequestSpecMethod enum
		Method: fmt.Sprintf("http%s%s", method[0:1], strings.ToLower(method[1:])),
	}

	sm := session.NewManager(d.Client())

	ticket, err := sm.AcquireGenericServiceTicket(ctx, &spec)
	if err != nil {
		return nil, nil, err
	}

	cookie := &http.Cookie{
		Name:  "vmware_cgi_ticket",
		Value: ticket.Id,
	}

	if d.useServiceTicketHostName(ticket.HostName) {
		u.Host = ticket.HostName
	}

	d.Client().SetThumbprint(u.Host, ticket.SslThumbprint)

	return u, cookie, nil
}

func (d Datastore) uploadTicket(ctx context.Context, path string, param *soap.Upload) (*url.URL, *soap.Upload, error) {
	p := soap.DefaultUpload
	if param != nil {
		p = *param // copy
	}

	u, ticket, err := d.ServiceTicket(ctx, path, p.Method)
	if err != nil {
		return nil, nil, err
	}

	p.Ticket = ticket

	return u, &p, nil
}

func (d Datastore) downloadTicket(ctx context.Context, path string, param *soap.Download) (*url.URL, *soap.Download, error) {
	p := soap.DefaultDownload
	if param != nil {
		p = *param // copy
	}

	u, ticket, err := d.ServiceTicket(ctx, path, p.Method)
	if err != nil {
		return nil, nil, err
	}

	p.Ticket = ticket

	return u, &p, nil
}

// Upload via soap.Upload with an http service ticket
func (d Datastore) Upload(ctx context.Context, f io.Reader, path string, param *soap.Upload) error {
	u, p, err := d.uploadTicket(ctx, path, param)
	if err != nil {
		return err
	}
	return d.Client().Upload(f, u, p)
}

// UploadFile via soap.Upload with an http service ticket
func (d Datastore) UploadFile(ctx context.Context, file string, path string, param *soap.Upload) error {
	u, p, err := d.uploadTicket(ctx, path, param)
	if err != nil {
		return err
	}
	return d.Client().UploadFile(file, u, p)
}

// Download via soap.Download with an http service ticket
func (d Datastore) Download(ctx context.Context, path string, param *soap.Download) (io.ReadCloser, int64, error) {
	u, p, err := d.downloadTicket(ctx, path, param)
	if err != nil {
		return nil, 0, err
	}
	return d.Client().Download(u, p)
}

// DownloadFile via soap.Download with an http service ticket
func (d Datastore) DownloadFile(ctx context.Context, path string, file string, param *soap.Download) error {
	u, p, err := d.downloadTicket(ctx, path, param)
	if err != nil {
		return err
	}
	return d.Client().DownloadFile(file, u, p)
}

// AttachedHosts returns hosts that have this Datastore attached, accessible and writable.
func (d Datastore) AttachedHosts(ctx context.Context) ([]*HostSystem, error) {
	var ds mo.Datastore
	var hosts []*HostSystem

	pc := property.DefaultCollector(d.Client())
	err := pc.RetrieveOne(ctx, d.Reference(), []string{"host"}, &ds)
	if err != nil {
		return nil, err
	}

	mounts := make(map[types.ManagedObjectReference]types.DatastoreHostMount)
	var refs []types.ManagedObjectReference
	for _, host := range ds.Host {
		refs = append(refs, host.Key)
		mounts[host.Key] = host
	}

	var hs []mo.HostSystem
	err = pc.Retrieve(ctx, refs, []string{"runtime.connectionState", "runtime.powerState"}, &hs)
	if err != nil {
		return nil, err
	}

	for _, host := range hs {
		if host.Runtime.ConnectionState == types.HostSystemConnectionStateConnected &&
			host.Runtime.PowerState == types.HostSystemPowerStatePoweredOn {

			mount := mounts[host.Reference()]
			info := mount.MountInfo

			if *info.Mounted && *info.Accessible && info.AccessMode == string(types.HostMountModeReadWrite) {
				hosts = append(hosts, NewHostSystem(d.Client(), mount.Key))
			}
		}
	}

	return hosts, nil
}

// AttachedClusterHosts returns hosts that have this Datastore attached, accessible and writable and are members of the given cluster.
func (d Datastore) AttachedClusterHosts(ctx context.Context, cluster *ComputeResource) ([]*HostSystem, error) {
	var hosts []*HostSystem

	clusterHosts, err := cluster.Hosts(ctx)
	if err != nil {
		return nil, err
	}

	attachedHosts, err := d.AttachedHosts(ctx)
	if err != nil {
		return nil, err
	}

	refs := make(map[types.ManagedObjectReference]bool)
	for _, host := range attachedHosts {
		refs[host.Reference()] = true
	}

	for _, host := range clusterHosts {
		if refs[host.Reference()] {
			hosts = append(hosts, host)
		}
	}

	return hosts, nil
}

func (d Datastore) Stat(ctx context.Context, file string) (types.BaseFileInfo, error) {
	b, err := d.Browser(ctx)
	if err != nil {
		return nil, err
	}

	spec := types.HostDatastoreBrowserSearchSpec{
		Details: &types.FileQueryFlags{
			FileType:     true,
			FileSize:     true,
			Modification: true,
			FileOwner:    types.NewBool(true),
		},
		MatchPattern: []string{path.Base(file)},
	}

	dsPath := d.Path(path.Dir(file))
	task, err := b.SearchDatastore(ctx, dsPath, &spec)
	if err != nil {
		return nil, err
	}

	info, err := task.WaitForResult(ctx, nil)
	if err != nil {
		if types.IsFileNotFound(err) {
			// FileNotFound means the base path doesn't exist.
			return nil, DatastoreNoSuchDirectoryError{"stat", dsPath}
		}

		return nil, err
	}

	res := info.Result.(types.HostDatastoreBrowserSearchResults)
	if len(res.File) == 0 {
		// File doesn't exist
		return nil, DatastoreNoSuchFileError{"stat", d.Path(file)}
	}

	return res.File[0], nil

}

// Type returns the type of file system volume.
func (d Datastore) Type(ctx context.Context) (types.HostFileSystemVolumeFileSystemType, error) {
	var mds mo.Datastore

	if err := d.Properties(ctx, d.Reference(), []string{"summary.type"}, &mds); err != nil {
		return types.HostFileSystemVolumeFileSystemType(""), err
	}
	return types.HostFileSystemVolumeFileSystemType(mds.Summary.Type), nil
}
<|endoftext|>


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

package cgotest

/*
#include <unistd.h>

unsigned int sleep(unsigned int seconds);

extern void BackgroundSleep(int);
void twoSleep(int);
*/
import "C"

import (
	"runtime"
	"testing"
	"time"
)

var sleepDone = make(chan bool)

func parallelSleep(n int) {
	C.twoSleep(C.int(n))
	<-sleepDone
}

//export BackgroundSleep
func BackgroundSleep(n int) {
	go func() {
		C.sleep(C.uint(n))
		sleepDone <- true
	}()
}

// wasteCPU starts a background goroutine to waste CPU
// to cause the power management to raise the CPU frequency.
// On ARM this has the side effect of making sleep more accurate.
func wasteCPU() chan struct{} {
	done := make(chan struct{})
	go func() {
		for {
			select {
			case <-done:
				return
			default:
			}
		}
	}()
	// pause for a short amount of time to allow the
	// power management to recognise load has risen.
	<-time.After(300 * time.Millisecond)
	return done
}

func testParallelSleep(t *testing.T) {
	defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(2))
	defer close(wasteCPU())

	sleepSec := 1
	start := time.Now()
	parallelSleep(sleepSec)
	dt := time.Since(start)
	t.Logf("sleep(%d) slept for %v", sleepSec, dt)
	// bug used to run sleeps in serial, producing a 2*sleepSec-second delay.
	if dt >= time.Duration(sleepSec)*1300*time.Millisecond {
		t.Fatalf("parallel %d-second sleeps slept for %f seconds", sleepSec, dt.Seconds())
	}
}
<commit_msg>misc/cgo/test: do not run wasteCPU during TestParallelSleep unless on arm<commit_after>// Copyright 2011 The Go Authors.  All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package cgotest

/*
#include <unistd.h>

unsigned int sleep(unsigned int seconds);

extern void BackgroundSleep(int);
void twoSleep(int);
*/
import "C"

import (
	"runtime"
	"testing"
	"time"
)

var sleepDone = make(chan bool)

func parallelSleep(n int) {
	C.twoSleep(C.int(n))
	<-sleepDone
}

//export BackgroundSleep
func BackgroundSleep(n int) {
	go func() {
		C.sleep(C.uint(n))
		sleepDone <- true
	}()
}

// wasteCPU starts a background goroutine to waste CPU
// to cause the power management to raise the CPU frequency.
// On ARM this has the side effect of making sleep more accurate.
func wasteCPU() chan struct{} {
	done := make(chan struct{})
	go func() {
		for {
			select {
			case <-done:
				return
			default:
			}
		}
	}()
	// pause for a short amount of time to allow the
	// power management to recognise load has risen.
	<-time.After(300 * time.Millisecond)
	return done
}

func testParallelSleep(t *testing.T) {
	if runtime.GOARCH == "arm" {
		// on ARM, the 1.3s deadline is frequently missed,
		// and burning cpu seems to help
		defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(2))
		defer close(wasteCPU())
	}

	sleepSec := 1
	start := time.Now()
	parallelSleep(sleepSec)
	dt := time.Since(start)
	t.Logf("sleep(%d) slept for %v", sleepSec, dt)
	// bug used to run sleeps in serial, producing a 2*sleepSec-second delay.
	if dt >= time.Duration(sleepSec)*1300*time.Millisecond {
		t.Fatalf("parallel %d-second sleeps slept for %f seconds", sleepSec, dt.Seconds())
	}
}
<|endoftext|>


<commit_before>package amipublisher

import (
	"github.com/Symantec/Dominator/lib/awsutil"
	"github.com/Symantec/Dominator/lib/log"
	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/service/ec2"
	"time"
)

const expiresAtFormat = "2006-01-02:15:04:05"

func expireResources(targets awsutil.TargetList, skipList awsutil.TargetList,
	logger log.Logger) error {
	currentTime := time.Now() // Need a common "now" time.
	waitChannel := make(chan struct{})
	numTargets, err := awsutil.ForEachTarget(targets, skipList,
		func(awsService *ec2.EC2, account, region string, logger log.Logger) {
			expireRegionResources(awsService, currentTime, logger)
			waitChannel <- struct{}{}
		},
		logger)
	for i := 0; i < numTargets; i++ {
		<-waitChannel
	}
	return err
}

func expireRegionResources(awsService *ec2.EC2, currentTime time.Time,
	logger log.Logger) {
	filters := make([]*ec2.Filter, 1)
	values := make([]string, 1)
	values[0] = "ExpiresAt"
	filters[0] = &ec2.Filter{
		Name:   aws.String("tag-key"),
		Values: aws.StringSlice(values),
	}
	images, err := awsService.DescribeImages(&ec2.DescribeImagesInput{
		Filters: filters,
	})
	if err == nil {
		for _, image := range images.Images {
			expireImage(awsService, image, currentTime, logger)
		}
	}
	instances, err := awsService.DescribeInstances(&ec2.DescribeInstancesInput{
		Filters: filters,
	})
	if err == nil {
		for _, reservation := range instances.Reservations {
			for _, instance := range reservation.Instances {
				expireInstance(awsService, instance, currentTime, logger)
			}
		}
	}
	snapshots, err := awsService.DescribeSnapshots(&ec2.DescribeSnapshotsInput{
		Filters: filters,
	})
	if err == nil {
		for _, snapshot := range snapshots.Snapshots {
			expireSnapshot(awsService, snapshot, currentTime, logger)
		}
	}
	volumes, err := awsService.DescribeVolumes(&ec2.DescribeVolumesInput{
		Filters: filters,
	})
	if err == nil {
		for _, volume := range volumes.Volumes {
			expireVolume(awsService, volume, currentTime, logger)
		}
	}
}

func expireImage(awsService *ec2.EC2, image *ec2.Image, currentTime time.Time,
	logger log.Logger) {
	if hasExpired(image.Tags, currentTime) {
		imageId := aws.StringValue(image.ImageId)
		if err := deregisterAmi(awsService, imageId); err != nil {
			logger.Printf("error deleting: %s: %s\n", imageId, err)
		} else {
			logger.Printf("deleted: %s\n", imageId)
		}
	}
}

func expireInstance(awsService *ec2.EC2, instance *ec2.Instance,
	currentTime time.Time, logger log.Logger) {
	if hasExpired(instance.Tags, currentTime) {
		instanceId := aws.StringValue(instance.InstanceId)
		if err := libTerminateInstances(awsService, instanceId); err != nil {
			logger.Printf("error terminating: %s: %s\n", instanceId, err)
		} else {
			logger.Printf("terminated: %s\n", instanceId)
		}
	}
}

func expireSnapshot(awsService *ec2.EC2, snapshot *ec2.Snapshot,
	currentTime time.Time, logger log.Logger) {
	if hasExpired(snapshot.Tags, currentTime) {
		snapshotId := aws.StringValue(snapshot.SnapshotId)
		if err := deleteSnapshot(awsService, snapshotId); err != nil {
			logger.Printf("error deleting: %s: %s\n", snapshotId, err)
		} else {
			logger.Printf("deleted: %s\n", snapshotId)
		}
	}
}

func expireVolume(awsService *ec2.EC2, volume *ec2.Volume,
	currentTime time.Time, logger log.Logger) {
	if hasExpired(volume.Tags, currentTime) {
		volumeId := aws.StringValue(volume.VolumeId)
		if err := deleteVolume(awsService, volumeId); err != nil {
			logger.Printf("error deleting: %s: %s\n", volumeId, err)
		} else {
			logger.Printf("deleted: %s\n", volumeId)
		}
	}
}

func hasExpired(tags []*ec2.Tag, currentTime time.Time) bool {
	for _, tag := range tags {
		if *tag.Key != "ExpiresAt" {
			continue
		}
		expirationTime, err := time.Parse(expiresAtFormat, *tag.Value)
		if err != nil {
			continue
		}
		return currentTime.After(expirationTime)
	}
	return false
}
<commit_msg>Ignore terminated instances when expiring AWS resources.<commit_after>package amipublisher

import (
	"github.com/Symantec/Dominator/lib/awsutil"
	"github.com/Symantec/Dominator/lib/log"
	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/service/ec2"
	"time"
)

const expiresAtFormat = "2006-01-02:15:04:05"

func expireResources(targets awsutil.TargetList, skipList awsutil.TargetList,
	logger log.Logger) error {
	currentTime := time.Now() // Need a common "now" time.
	waitChannel := make(chan struct{})
	numTargets, err := awsutil.ForEachTarget(targets, skipList,
		func(awsService *ec2.EC2, account, region string, logger log.Logger) {
			expireRegionResources(awsService, currentTime, logger)
			waitChannel <- struct{}{}
		},
		logger)
	for i := 0; i < numTargets; i++ {
		<-waitChannel
	}
	return err
}

func expireRegionResources(awsService *ec2.EC2, currentTime time.Time,
	logger log.Logger) {
	filters := make([]*ec2.Filter, 1)
	values := make([]string, 1)
	values[0] = "ExpiresAt"
	filters[0] = &ec2.Filter{
		Name:   aws.String("tag-key"),
		Values: aws.StringSlice(values),
	}
	images, err := awsService.DescribeImages(&ec2.DescribeImagesInput{
		Filters: filters,
	})
	if err == nil {
		for _, image := range images.Images {
			expireImage(awsService, image, currentTime, logger)
		}
	}
	instances, err := awsService.DescribeInstances(&ec2.DescribeInstancesInput{
		Filters: filters,
	})
	if err == nil {
		for _, reservation := range instances.Reservations {
			for _, instance := range reservation.Instances {
				expireInstance(awsService, instance, currentTime, logger)
			}
		}
	}
	snapshots, err := awsService.DescribeSnapshots(&ec2.DescribeSnapshotsInput{
		Filters: filters,
	})
	if err == nil {
		for _, snapshot := range snapshots.Snapshots {
			expireSnapshot(awsService, snapshot, currentTime, logger)
		}
	}
	volumes, err := awsService.DescribeVolumes(&ec2.DescribeVolumesInput{
		Filters: filters,
	})
	if err == nil {
		for _, volume := range volumes.Volumes {
			expireVolume(awsService, volume, currentTime, logger)
		}
	}
}

func expireImage(awsService *ec2.EC2, image *ec2.Image, currentTime time.Time,
	logger log.Logger) {
	if hasExpired(image.Tags, currentTime) {
		imageId := aws.StringValue(image.ImageId)
		if err := deregisterAmi(awsService, imageId); err != nil {
			logger.Printf("error deleting: %s: %s\n", imageId, err)
		} else {
			logger.Printf("deleted: %s\n", imageId)
		}
	}
}

func expireInstance(awsService *ec2.EC2, instance *ec2.Instance,
	currentTime time.Time, logger log.Logger) {
	if aws.StringValue(instance.State.Name) == ec2.InstanceStateNameTerminated {
		return
	}
	if hasExpired(instance.Tags, currentTime) {
		instanceId := aws.StringValue(instance.InstanceId)
		if err := libTerminateInstances(awsService, instanceId); err != nil {
			logger.Printf("error terminating: %s: %s\n", instanceId, err)
		} else {
			logger.Printf("terminated: %s\n", instanceId)
		}
	}
}

func expireSnapshot(awsService *ec2.EC2, snapshot *ec2.Snapshot,
	currentTime time.Time, logger log.Logger) {
	if hasExpired(snapshot.Tags, currentTime) {
		snapshotId := aws.StringValue(snapshot.SnapshotId)
		if err := deleteSnapshot(awsService, snapshotId); err != nil {
			logger.Printf("error deleting: %s: %s\n", snapshotId, err)
		} else {
			logger.Printf("deleted: %s\n", snapshotId)
		}
	}
}

func expireVolume(awsService *ec2.EC2, volume *ec2.Volume,
	currentTime time.Time, logger log.Logger) {
	if hasExpired(volume.Tags, currentTime) {
		volumeId := aws.StringValue(volume.VolumeId)
		if err := deleteVolume(awsService, volumeId); err != nil {
			logger.Printf("error deleting: %s: %s\n", volumeId, err)
		} else {
			logger.Printf("deleted: %s\n", volumeId)
		}
	}
}

func hasExpired(tags []*ec2.Tag, currentTime time.Time) bool {
	for _, tag := range tags {
		if *tag.Key != "ExpiresAt" {
			continue
		}
		expirationTime, err := time.Parse(expiresAtFormat, *tag.Value)
		if err != nil {
			continue
		}
		return currentTime.After(expirationTime)
	}
	return false
}
<|endoftext|>


<commit_before>// +build !windows

package commands

import (
	"io"
	"log"
	"os"
	"os/exec"
)

func tarStreamFrom(workDir string, paths []string) (io.ReadCloser, error) {
	var archive io.ReadCloser

	if tarPath, err := exec.LookPath("tar"); err == nil {
		tarCmd := exec.Command(tarPath, append([]string{"-czf", "-"}, paths...)...)
		tarCmd.Dir = workDir
		tarCmd.Stderr = os.Stderr

		archive, err = tarCmd.StdoutPipe()
		if err != nil {
			log.Fatalln("could not create tar pipe:", err)
		}

		err = tarCmd.Start()
		if err != nil {
			log.Fatalln("could not run tar:", err)
		}
	} else {
		return nativeTarGZStreamFrom(workDir, paths)
	}

	return archive, nil
}
<commit_msg>Fix issue where tar command would fail if there were too many command line arguments<commit_after>// +build !windows

package commands

import (
	"io"
	"log"
	"os"
	"os/exec"
)

func tarStreamFrom(workDir string, paths []string) (io.ReadCloser, error) {
	var archive io.ReadCloser
	var writer io.WriteCloser

	if tarPath, err := exec.LookPath("tar"); err == nil {
		tarCmd := exec.Command(tarPath, []string{"-czf", "-", "-T", "-"}...)
		tarCmd.Dir = workDir
		tarCmd.Stderr = os.Stderr

		archive, err = tarCmd.StdoutPipe()
		if err != nil {
			log.Fatalln("could not create tar pipe:", err)
		}

		writer, err = tarCmd.StdinPipe()
		if err != nil {
			log.Fatalln("could not create tar stdin pipe:", err)
		}

		err = tarCmd.Start()
		if err != nil {
			log.Fatalln("could not run tar:", err)
		}

		go func() {
			for _, s := range paths {
				io.WriteString(writer, s+"\n")
			}
			writer.Close()
		}()
	} else {
		return nativeTarGZStreamFrom(workDir, paths)
	}

	return archive, nil
}
<|endoftext|>


<commit_before>//@auther foolbread
//@time 2015-11-13
//@file aqua/common/proto/common.go
package proto

import (
	"encoding/binary"
)

//status
const (
	STATUS_OK     = 0
	ALREADY_LOGIN = 1
	SERVICE_ERROR = 2
	MESSAGE_FULL  = 3
)

//cmd
const (
	KEEPALIVE_CMD      = 0
	LOGINREQ_CMD       = 1
	LOGINRES_CMD       = 2
	REDIRECT_CMD       = 4
	CONREGISTERREQ_CMD = 5
	CONREGISTERRES_CMD = 6
	LOCREGISTERREQ_CMD = 7
	LOCREGISTERRES_CMD = 8

	LOGICSERVICEREQ_CMD = 9
	LOGICSERVICERES_CMD = 10
)

var KeepAlive []byte = []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00}

func FillHead(d []byte, cmd uint32) []byte {
	var buf []byte = make([]byte, HEAD_LEN, 1024)

	binary.BigEndian.PutUint32(buf[4:], uint32(len(d))+HEAD_LEN)
	binary.BigEndian.PutUint32(buf[8:], cmd)

	buf = append(buf, d...)

	return buf
}
<commit_msg>+新增新状态<commit_after>//@auther foolbread
//@time 2015-11-13
//@file aqua/common/proto/common.go
package proto

import (
	"encoding/binary"
)

//status
const (
	STATUS_OK         = 0
	ALREADY_LOGIN     = 1
	SERVICE_ERROR     = 2
	MESSAGE_FULL      = 3
	WAITTING_RESPONSE = 4
)

//cmd
const (
	KEEPALIVE_CMD      = 0
	LOGINREQ_CMD       = 1
	LOGINRES_CMD       = 2
	REDIRECT_CMD       = 4
	CONREGISTERREQ_CMD = 5
	CONREGISTERRES_CMD = 6
	LOCREGISTERREQ_CMD = 7
	LOCREGISTERRES_CMD = 8

	LOGICSERVICEREQ_CMD = 9
	LOGICSERVICERES_CMD = 10
)

var KeepAlive []byte = []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00}

func FillHead(d []byte, cmd uint32) []byte {
	var buf []byte = make([]byte, HEAD_LEN, 1024)

	binary.BigEndian.PutUint32(buf[4:], uint32(len(d))+HEAD_LEN)
	binary.BigEndian.PutUint32(buf[8:], cmd)

	buf = append(buf, d...)

	return buf
}
<|endoftext|>


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

// Package reboot_test verifies that the current GOROOT can be used to bootstrap
// itself.
package reboot_test

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

func TestRepeatBootstrap(t *testing.T) {
	goroot, err := os.MkdirTemp("", "reboot-goroot")
	if err != nil {
		t.Fatal(err)
	}
	defer os.RemoveAll(goroot)

	gorootSrc := filepath.Join(goroot, "src")
	if err := overlayDir(gorootSrc, filepath.Join(runtime.GOROOT(), "src")); err != nil {
		t.Fatal(err)
	}

	if err := os.WriteFile(filepath.Join(goroot, "VERSION"), []byte(runtime.Version()), 0666); err != nil {
		t.Fatal(err)
	}

	var makeScript string
	switch runtime.GOOS {
	case "windows":
		makeScript = "make.bat"
	case "plan9":
		makeScript = "make.rc"
	default:
		makeScript = "make.bash"
	}

	cmd := exec.Command(filepath.Join(runtime.GOROOT(), "src", makeScript))
	cmd.Dir = gorootSrc
	cmd.Env = append(os.Environ(), "GOROOT=", "GOROOT_BOOTSTRAP="+runtime.GOROOT())
	cmd.Stderr = os.Stderr
	cmd.Stdout = os.Stdout
	if err := cmd.Run(); err != nil {
		t.Fatal(err)
	}
}
<commit_msg>misc/reboot: skip TestRepeatBootstrap on short builders<commit_after>// Copyright 2019 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Package reboot_test verifies that the current GOROOT can be used to bootstrap
// itself.
package reboot_test

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

func TestRepeatBootstrap(t *testing.T) {
	if testing.Short() {
		t.Skipf("skipping test that rebuilds the entire toolchain")
	}

	goroot, err := os.MkdirTemp("", "reboot-goroot")
	if err != nil {
		t.Fatal(err)
	}
	defer os.RemoveAll(goroot)

	gorootSrc := filepath.Join(goroot, "src")
	if err := overlayDir(gorootSrc, filepath.Join(runtime.GOROOT(), "src")); err != nil {
		t.Fatal(err)
	}

	if err := os.WriteFile(filepath.Join(goroot, "VERSION"), []byte(runtime.Version()), 0666); err != nil {
		t.Fatal(err)
	}

	var makeScript string
	switch runtime.GOOS {
	case "windows":
		makeScript = "make.bat"
	case "plan9":
		makeScript = "make.rc"
	default:
		makeScript = "make.bash"
	}

	cmd := exec.Command(filepath.Join(runtime.GOROOT(), "src", makeScript))
	cmd.Dir = gorootSrc
	cmd.Env = append(os.Environ(), "GOROOT=", "GOROOT_BOOTSTRAP="+runtime.GOROOT())
	cmd.Stderr = os.Stderr
	cmd.Stdout = os.Stdout
	if err := cmd.Run(); err != nil {
		t.Fatal(err)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"context"
	"flag"
	"fmt"
	"log"
	"net"
	"time"

	"github.com/golang/glog"
	"github.com/kward/go-osc/osc"
	"github.com/kward/venue/ping"
	"github.com/kward/venue/router"
	"github.com/kward/venue/touchosc"
	"github.com/kward/venue/venue"
	"github.com/kward/venue/venuelib"
)

var (
	oscClientHost = flag.String("osc_client_host", "127.0.0.1", "OSC client hostname/IP.")
	oscClientPort = flag.Uint("osc_client_port", 9000, "OSC client port.")
	oscServerHost = flag.String("osc_server_host", "0.0.0.0", "OSC server hostname/IP.")
	oscServerPort = flag.Uint("osc_server_port", 8000, "OSC server port.")

	venueHost    = flag.String("venue_host", "", "Venue VNC host/IP.")
	venuePort    = flag.Uint("venue_port", 5900, "Venue VNC port.")
	venuePasswd  string
	venueTimeout = flag.Duration("venue_timeout", 15*time.Second, "Venue VNC timeout.")

	venueFbRefresh   = flag.Bool("enable_venue_fb_refresh", false, "Enable Venue framebuffer refresh.")
	checkpointPeriod = flag.Duration("checkpoint_period", 1*time.Minute, "Checkpoint period")
)

func flagInit() {
	flag.StringVar(&venuePasswd, "venue_passwd", "", "Venue VNC password.")
	flag.Parse()
}

type state struct {
	input      int
	inputBank  int
	output     int
	outputBank int
	router     *router.Router
}

func NewState(router *router.Router) *state {
	return &state{
		input:      1,
		inputBank:  1,
		outputBank: 1,
		router:     router,
	}
}

func (s *state) handleBundle(b *osc.Bundle) {
	if glog.V(2) {
		glog.Infof("Received OSC bundle from %v:", b.Addr())
	}
	for i, msg := range b.Messages {
		if glog.V(4) {
			glog.Infof("OSC message #%d: ", i+1, msg.Address)
		}
	}
	glog.Errorf("%s unimplemented", venuelib.FnName())
}

func (s *state) handleMessage(v *venue.Venue, msg *osc.Message) {
	if glog.V(3) {
		glog.Info(venuelib.FnName())
	}

	// The address is expected to be in this format:
	// /version/layout/page/control/command[/num1][/num2][/label]
	if glog.V(2) {
		glog.Infof("Received OSC message from %s: %q", msg.Addr(), msg)
	}

	pkt, err := touchosc.Parse(msg)
	if err != nil {
		glog.Errorf("Failed to parse OSC message %s; %s", msg, err)
		return
	}
	if glog.V(4) {
		glog.Infof("Parsed packet: %s", pkt)
	}

	router.Dispatch(s.router, pkt)
}

func main() {
	flagInit()

	log.SetFlags(log.Flags() | log.Lmicroseconds | log.Lshortfile)

	if *venueHost == "" {
		glog.Exitln("missing --venue_host flag")
	}
	passwd := venuePasswd
	if passwd == "" {
		pw, err := venuelib.GetPasswd()
		if err != nil {
			glog.Exitf("Failed to get password; %s\n", err)
		}
		passwd = pw
	}

	// Instantiate Venue client.
	v, err := venue.New()
	if err != nil {
		glog.Exitf("Failure instantiating Venue client; %s\n", err)
	}

	// Establish connection with the Venue VNC server.
	ctx, cancel := context.WithTimeout(context.Background(), *venueTimeout)
	defer cancel()
	if err := v.Connect(ctx, *venueHost, *venuePort, passwd); err != nil {
		glog.Exitf("Failed to connect to Venue VNC server; %s\n", err)
	}
	defer v.Close()
	glog.Info("Venue connection established.")
	if err := v.Initialize(); err != nil {
		glog.Exitf("Unable to initialize Venue properly; %s\n", err)
	}

	router := &router.Router{}
	router.RegisterEndpoint(v)
	router.RegisterEndpoint(&ping.Ping{})

	go v.ListenAndHandle()

	o := &osc.Server{}
	conn, err := net.ListenPacket("udp", fmt.Sprintf("%v:%v", *oscServerHost, *oscServerPort))
	if err != nil {
		glog.Exitf("Error starting OSC server; %s\n", err)
	}
	defer conn.Close()
	glog.Info("OSC server started.")

	go func() {
		s := NewState(router)

		for {
			p, err := o.ReceivePacket(context.Background(), conn)
			if err != nil {
				glog.Exitf("OSC error; %s\n", err)
			}
			if p == nil {
				continue
			}

			switch t := p.(type) {
			case *osc.Bundle:
				s.handleBundle(p.(*osc.Bundle))
			case *osc.Message:
				s.handleMessage(v, p.(*osc.Message))
			default:
				glog.Errorf("unrecognized packet type %v", t)
			}
		}
	}()

	for {
		if glog.V(5) {
			glog.Infof("--- checkpoint ---")
		}
		time.Sleep(*checkpointPeriod)
	}
}
<commit_msg>replaced flagInit() with init()<commit_after>package main

import (
	"context"
	"flag"
	"fmt"
	"log"
	"net"
	"time"

	"github.com/golang/glog"
	"github.com/kward/go-osc/osc"
	"github.com/kward/venue/ping"
	"github.com/kward/venue/router"
	"github.com/kward/venue/touchosc"
	"github.com/kward/venue/venue"
	"github.com/kward/venue/venuelib"
)

var (
	oscClientHost = flag.String("osc_client_host", "127.0.0.1", "OSC client hostname/IP.")
	oscClientPort = flag.Uint("osc_client_port", 9000, "OSC client port.")
	oscServerHost = flag.String("osc_server_host", "0.0.0.0", "OSC server hostname/IP.")
	oscServerPort = flag.Uint("osc_server_port", 8000, "OSC server port.")

	venueHost    = flag.String("venue_host", "", "Venue VNC host/IP.")
	venuePort    = flag.Uint("venue_port", 5900, "Venue VNC port.")
	venuePasswd  string
	venueTimeout = flag.Duration("venue_timeout", 15*time.Second, "Venue VNC timeout.")

	venueFbRefresh   = flag.Bool("enable_venue_fb_refresh", false, "Enable Venue framebuffer refresh.")
	checkpointPeriod = flag.Duration("checkpoint_period", 1*time.Minute, "Checkpoint period")
)

func init() {
	flag.StringVar(&venuePasswd, "venue_passwd", "", "Venue VNC password.")
	flag.Parse()
}

type state struct {
	input      int
	inputBank  int
	output     int
	outputBank int
	router     *router.Router
}

func NewState(router *router.Router) *state {
	return &state{
		input:      1,
		inputBank:  1,
		outputBank: 1,
		router:     router,
	}
}

func (s *state) handleBundle(b *osc.Bundle) {
	if glog.V(2) {
		glog.Infof("Received OSC bundle from %v:", b.Addr())
	}
	for i, msg := range b.Messages {
		if glog.V(4) {
			glog.Infof("OSC message #%d: ", i+1, msg.Address)
		}
	}
	glog.Errorf("%s unimplemented", venuelib.FnName())
}

func (s *state) handleMessage(v *venue.Venue, msg *osc.Message) {
	if glog.V(3) {
		glog.Info(venuelib.FnName())
	}

	// The address is expected to be in this format:
	// /version/layout/page/control/command[/num1][/num2][/label]
	if glog.V(2) {
		glog.Infof("Received OSC message from %s: %q", msg.Addr(), msg)
	}

	pkt, err := touchosc.Parse(msg)
	if err != nil {
		glog.Errorf("Failed to parse OSC message %s; %s", msg, err)
		return
	}
	if glog.V(4) {
		glog.Infof("Parsed packet: %s", pkt)
	}

	router.Dispatch(s.router, pkt)
}

func main() {
	log.SetFlags(log.Flags() | log.Lmicroseconds | log.Lshortfile)

	if *venueHost == "" {
		glog.Exitln("missing --venue_host flag")
	}
	passwd := venuePasswd
	if passwd == "" {
		pw, err := venuelib.GetPasswd()
		if err != nil {
			glog.Exitf("Failed to get password; %s\n", err)
		}
		passwd = pw
	}

	// Instantiate Venue client.
	v, err := venue.New()
	if err != nil {
		glog.Exitf("Failure instantiating Venue client; %s\n", err)
	}

	// Establish connection with the Venue VNC server.
	ctx, cancel := context.WithTimeout(context.Background(), *venueTimeout)
	defer cancel()
	if err := v.Connect(ctx, *venueHost, *venuePort, passwd); err != nil {
		glog.Exitf("Failed to connect to Venue VNC server; %s\n", err)
	}
	defer v.Close()
	glog.Info("Venue connection established.")
	if err := v.Initialize(); err != nil {
		glog.Exitf("Unable to initialize Venue properly; %s\n", err)
	}

	router := &router.Router{}
	router.RegisterEndpoint(v)
	router.RegisterEndpoint(&ping.Ping{})

	go v.ListenAndHandle()

	o := &osc.Server{}
	conn, err := net.ListenPacket("udp", fmt.Sprintf("%v:%v", *oscServerHost, *oscServerPort))
	if err != nil {
		glog.Exitf("Error starting OSC server; %s\n", err)
	}
	defer conn.Close()
	glog.Info("OSC server started.")

	go func() {
		s := NewState(router)

		for {
			p, err := o.ReceivePacket(context.Background(), conn)
			if err != nil {
				glog.Exitf("OSC error; %s\n", err)
			}
			if p == nil {
				continue
			}

			switch t := p.(type) {
			case *osc.Bundle:
				s.handleBundle(p.(*osc.Bundle))
			case *osc.Message:
				s.handleMessage(v, p.(*osc.Message))
			default:
				glog.Errorf("unrecognized packet type %v", t)
			}
		}
	}()

	for {
		if glog.V(5) {
			glog.Infof("--- checkpoint ---")
		}
		time.Sleep(*checkpointPeriod)
	}
}
<|endoftext|>


<commit_before>package logging

import (
	"fmt"
	"time"

	"github.com/caarlos0/log"
	"github.com/charmbracelet/lipgloss"
	"github.com/goreleaser/goreleaser/internal/middleware"
	"github.com/goreleaser/goreleaser/pkg/context"
)

var (
	bold  = lipgloss.NewStyle().Bold(true)
	faint = lipgloss.NewStyle().Italic(true).Faint(true)
)

// Log pretty prints the given action and its title.
func Log(title string, next middleware.Action) middleware.Action {
	return func(ctx *context.Context) error {
		start := time.Now()
		defer func() {
			if took := time.Since(start).Round(time.Second); took > 0 {
				log.Info(faint.Render(fmt.Sprintf("took: %s", took)))
			}
			log.ResetPadding()
		}()
		log.Infof(bold.Render(title))
		log.IncreasePadding()
		return next(ctx)
	}
}

// PadLog pretty prints the given action and its title with an increased padding.
func PadLog(title string, next middleware.Action) middleware.Action {
	return func(ctx *context.Context) error {
		defer log.ResetPadding()
		log.ResetPadding()
		log.IncreasePadding()
		log.Infof(bold.Render(title))
		log.IncreasePadding()
		return next(ctx)
	}
}
<commit_msg>fix: log duration of padlog steps (#3439)<commit_after>package logging

import (
	"fmt"
	"time"

	"github.com/caarlos0/log"
	"github.com/charmbracelet/lipgloss"
	"github.com/goreleaser/goreleaser/internal/middleware"
	"github.com/goreleaser/goreleaser/pkg/context"
)

var (
	bold  = lipgloss.NewStyle().Bold(true)
	faint = lipgloss.NewStyle().Italic(true).Faint(true)
)

// Log pretty prints the given action and its title.
func Log(title string, next middleware.Action) middleware.Action {
	return func(ctx *context.Context) error {
		start := time.Now()
		defer func() {
			logDuration(start)
			log.ResetPadding()
		}()
		log.Infof(bold.Render(title))
		log.IncreasePadding()
		return next(ctx)
	}
}

// PadLog pretty prints the given action and its title with an increased padding.
func PadLog(title string, next middleware.Action) middleware.Action {
	return func(ctx *context.Context) error {
		start := time.Now()
		defer func() {
			logDuration(start)
			log.ResetPadding()
		}()
		log.ResetPadding()
		log.IncreasePadding()
		log.Infof(bold.Render(title))
		log.IncreasePadding()
		return next(ctx)
	}
}

func logDuration(start time.Time) {
	if took := time.Since(start).Round(time.Second); took > 0 {
		log.Info(faint.Render(fmt.Sprintf("took: %s", took)))
	}
}
<|endoftext|>


<commit_before>package model

import (
	"encoding/json"
	"fmt"
	"sort"
	"sync"
	"time"
)

type StopVisitId string

type StopVisitAttributes struct {
	ObjectId         ObjectID
	StopAreaObjectId ObjectID

	VehicleJourneyObjectId ObjectID
	PassageOrder           int

	ArrivalStatus   StopVisitArrivalStatus
	DepartureStatus StopVisitDepartureStatus
	RecordedAt      time.Time
	Schedules       StopVisitSchedules
	VehicleAtStop   bool

	Attributes Attributes
	References References
}

type StopVisit struct {
	ObjectIDConsumer

	model  Model
	Origin string

	id          StopVisitId
	collected   bool
	collectedAt time.Time

	StopAreaId       StopAreaId       `json:",omitempty"`
	VehicleJourneyId VehicleJourneyId `json:",omitempty"`
	Attributes       Attributes
	References       References

	ArrivalStatus   StopVisitArrivalStatus   `json:",omitempty"`
	DepartureStatus StopVisitDepartureStatus `json:",omitempty"`
	RecordedAt      time.Time
	Schedules       StopVisitSchedules
	VehicleAtStop   bool
	PassageOrder    int `json:",omitempty"`
}

func NewStopVisit(model Model) *StopVisit {
	stopVisit := &StopVisit{
		model:      model,
		Schedules:  NewStopVisitSchedules(),
		Attributes: NewAttributes(),
		References: NewReferences(),
	}
	stopVisit.objectids = make(ObjectIDs)
	return stopVisit
}

func (stopVisit *StopVisit) IsCollected() bool {
	return stopVisit.collected
}

func (stopVisit *StopVisit) NotCollected() {
	stopVisit.collected = false
}

func (stopVisit *StopVisit) CollectedAt() time.Time {
	return stopVisit.collectedAt
}

func (stopVisit *StopVisit) Collected(t time.Time) {
	stopVisit.collected = true
	stopVisit.collectedAt = t
}

func (stopVisit *StopVisit) Id() StopVisitId {
	return stopVisit.id
}

func (stopVisit *StopVisit) StopArea() StopArea {
	stopArea, _ := stopVisit.model.StopAreas().Find(stopVisit.StopAreaId)
	return stopArea
}

func (stopVisit *StopVisit) VehicleJourney() *VehicleJourney {
	vehicleJourney, ok := stopVisit.model.VehicleJourneys().Find(stopVisit.VehicleJourneyId)
	if !ok {
		return nil
	}
	return &vehicleJourney
}

func (stopVisit *StopVisit) MarshalJSON() ([]byte, error) {
	type Alias StopVisit
	aux := struct {
		Id          StopVisitId
		ObjectIDs   ObjectIDs `json:",omitempty"`
		Collected   bool
		CollectedAt *time.Time           `json:",omitempty"`
		RecordedAt  *time.Time           `json:",omitempty"`
		Attributes  Attributes           `json:",omitempty"`
		References  map[string]Reference `json:",omitempty"`
		Schedules   []StopVisitSchedule  `json:",omitempty"`
		*Alias
	}{
		Id:        stopVisit.id,
		Collected: stopVisit.collected,
		Alias:     (*Alias)(stopVisit),
	}

	if !stopVisit.ObjectIDs().Empty() {
		aux.ObjectIDs = stopVisit.ObjectIDs()
	}
	if !stopVisit.Attributes.IsEmpty() {
		aux.Attributes = stopVisit.Attributes
	}
	if !stopVisit.References.IsEmpty() {
		aux.References = stopVisit.References.GetReferences()
	}
	if !stopVisit.RecordedAt.IsZero() {
		aux.RecordedAt = &stopVisit.RecordedAt
	}
	if !stopVisit.collectedAt.IsZero() {
		aux.CollectedAt = &stopVisit.collectedAt
	}

	scheduleSlice := stopVisit.Schedules.ToSlice()
	if len(scheduleSlice) != 0 {
		aux.Schedules = scheduleSlice
	}

	return json.Marshal(&aux)
}

func (stopVisit *StopVisit) UnmarshalJSON(data []byte) error {
	type Alias StopVisit
	aux := &struct {
		ObjectIDs   map[string]string
		References  map[string]Reference
		CollectedAt time.Time
		Schedules   []StopVisitSchedule
		*Alias
	}{
		Alias: (*Alias)(stopVisit),
	}

	err := json.Unmarshal(data, aux)
	if err != nil {
		return err
	}

	if aux.ObjectIDs != nil {
		stopVisit.ObjectIDConsumer.objectids = NewObjectIDsFromMap(aux.ObjectIDs)
	}

	if aux.References != nil {
		stopVisit.References.SetReferences(aux.References)
	}

	if aux.Schedules != nil {
		stopVisit.Schedules = NewStopVisitSchedules()
		for _, schedule := range aux.Schedules {
			stopVisit.Schedules.SetSchedule(schedule.Kind(), schedule.DepartureTime(), schedule.ArrivalTime())
		}
	}

	if !aux.CollectedAt.IsZero() {
		stopVisit.Collected(aux.CollectedAt)
	}
	return nil
}

func (stopVisit *StopVisit) Attribute(key string) (string, bool) {
	value, present := stopVisit.Attributes[key]
	return value, present
}

func (stopVisit *StopVisit) Save() (ok bool) {
	ok = stopVisit.model.StopVisits().Save(stopVisit)
	return
}

func (stopVisit *StopVisit) Reference(key string) (Reference, bool) {
	value, present := stopVisit.References.Get(key)
	return value, present
}

func (stopVisit *StopVisit) ReferenceTime() time.Time {
	orderMap := []StopVisitScheduleType{"actual", "expected", "aimed"}

	for _, kind := range orderMap {
		if schedule := stopVisit.Schedules.Schedule(kind); !schedule.ArrivalTime().IsZero() {
			return schedule.ArrivalTime()
		}
	}

	for _, kind := range orderMap {
		if schedule := stopVisit.Schedules.Schedule(kind); !schedule.DepartureTime().IsZero() {
			return schedule.DepartureTime()
		}
	}

	return time.Time{}
}

type ByTime []StopVisit

func (a ByTime) Len() int           { return len(a) }
func (a ByTime) Swap(i, j int)      { a[i], a[j] = a[j], a[i] }
func (a ByTime) Less(i, j int) bool { return !a[i].ReferenceTime().After(a[j].ReferenceTime()) }

type MemoryStopVisits struct {
	UUIDConsumer
	ClockConsumer

	model Model

	mutex          *sync.RWMutex
	byIdentifier   map[StopVisitId]*StopVisit
	broadcastEvent func(event StopMonitoringBroadcastEvent)
}

type StopVisits interface {
	UUIDInterface

	New() StopVisit
	Find(id StopVisitId) (StopVisit, bool)
	FindByObjectId(objectid ObjectID) (StopVisit, bool)
	FindByVehicleJourneyId(id VehicleJourneyId) []StopVisit
	FindFollowingByVehicleJourneyId(id VehicleJourneyId) []StopVisit
	FindByStopAreaId(id StopAreaId) []StopVisit
	FindFollowingByStopAreaId(id StopAreaId) []StopVisit
	FindFollowingByStopAreaIds(stopAreaIds []StopAreaId) []StopVisit
	FindAll() []StopVisit
	Save(stopVisit *StopVisit) bool
	Delete(stopVisit *StopVisit) bool
}

func NewMemoryStopVisits() *MemoryStopVisits {
	return &MemoryStopVisits{
		mutex:        &sync.RWMutex{},
		byIdentifier: make(map[StopVisitId]*StopVisit),
	}
}

func (manager *MemoryStopVisits) New() StopVisit {
	stopVisit := NewStopVisit(manager.model)
	return *stopVisit
}

func (manager *MemoryStopVisits) Find(id StopVisitId) (StopVisit, bool) {
	manager.mutex.RLock()
	defer manager.mutex.RUnlock()

	stopVisit, ok := manager.byIdentifier[id]

	if ok {
		return *stopVisit, true
	} else {
		return StopVisit{}, false
	}
}

func (manager *MemoryStopVisits) FindByObjectId(objectid ObjectID) (StopVisit, bool) {
	manager.mutex.RLock()
	defer manager.mutex.RUnlock()

	for _, stopVisit := range manager.byIdentifier {
		stopVisitObjectId, _ := stopVisit.ObjectID(objectid.Kind())
		if stopVisitObjectId.Value() == objectid.Value() {
			return *stopVisit, true
		}
	}
	return StopVisit{}, false
}

func (manager *MemoryStopVisits) FindByVehicleJourneyId(id VehicleJourneyId) (stopVisits []StopVisit) {
	manager.mutex.RLock()
	defer manager.mutex.RUnlock()

	for _, stopVisit := range manager.byIdentifier {
		if stopVisit.VehicleJourneyId == id {
			stopVisits = append(stopVisits, *stopVisit)
		}
	}
	return
}

func (manager *MemoryStopVisits) FindFollowingByVehicleJourneyId(id VehicleJourneyId) (stopVisits []StopVisit) {
	manager.mutex.RLock()
	defer manager.mutex.RUnlock()

	for _, stopVisit := range manager.byIdentifier {
		if stopVisit.VehicleJourneyId == id && stopVisit.ReferenceTime().After(manager.Clock().Now()) {
			stopVisits = append(stopVisits, *stopVisit)
		}
	}
	sort.Sort(ByTime(stopVisits))
	return
}

func (manager *MemoryStopVisits) FindByStopAreaId(id StopAreaId) (stopVisits []StopVisit) {
	manager.mutex.RLock()
	defer manager.mutex.RUnlock()

	for _, stopVisit := range manager.byIdentifier {
		if stopVisit.StopAreaId == id {
			stopVisits = append(stopVisits, *stopVisit)
		}
	}
	return
}

func (manager *MemoryStopVisits) FindFollowingByStopAreaId(id StopAreaId) (stopVisits []StopVisit) {
	manager.mutex.RLock()
	defer manager.mutex.RUnlock()

	for _, stopVisit := range manager.byIdentifier {
		if stopVisit.StopAreaId == id && stopVisit.ReferenceTime().After(manager.Clock().Now()) {
			stopVisits = append(stopVisits, *stopVisit)
		}
	}
	sort.Sort(ByTime(stopVisits))
	return
}

func (manager *MemoryStopVisits) FindFollowingByStopAreaIds(stopAreaIds []StopAreaId) (stopVisits []StopVisit) {
	manager.mutex.RLock()
	defer manager.mutex.RUnlock()

	for _, stopAreaId := range stopAreaIds {
		stopVisits = append(stopVisits, manager.FindFollowingByStopAreaId(stopAreaId)...)
	}
	sort.Sort(ByTime(stopVisits))
	return
}

func (manager *MemoryStopVisits) FindAll() (stopVisits []StopVisit) {
	manager.mutex.RLock()
	defer manager.mutex.RUnlock()

	if len(manager.byIdentifier) == 0 {
		return []StopVisit{}
	}
	for _, stopVisit := range manager.byIdentifier {
		stopVisits = append(stopVisits, *stopVisit)
	}
	return
}

func (manager *MemoryStopVisits) Save(stopVisit *StopVisit) bool {
	manager.mutex.Lock()
	defer manager.mutex.Unlock()

	if stopVisit.id == "" {
		stopVisit.id = StopVisitId(manager.NewUUID())
	}

	stopVisit.model = manager.model
	manager.byIdentifier[stopVisit.id] = stopVisit

	event := StopMonitoringBroadcastEvent{
		ModelId:   string(stopVisit.id),
		ModelType: "StopVisit",
	}

	if manager.broadcastEvent != nil {
		manager.broadcastEvent(event)
	}

	return true
}

func (manager *MemoryStopVisits) Delete(stopVisit *StopVisit) bool {
	manager.mutex.Lock()
	defer manager.mutex.Unlock()

	delete(manager.byIdentifier, stopVisit.Id())
	return true
}

func (manager *MemoryStopVisits) Load(referentialSlug string) error {
	var selectStopVisits []SelectStopVisit
	modelName := manager.model.Date()

	sqlQuery := fmt.Sprintf("select * from stop_visits where referential_slug = '%s' and model_name = '%s'", referentialSlug, modelName.String())
	_, err := Database.Select(&selectStopVisits, sqlQuery)
	if err != nil {
		return err
	}
	for _, sv := range selectStopVisits {
		stopVisit := manager.New()
		stopVisit.id = StopVisitId(sv.Id)
		if sv.StopAreaId.Valid {
			stopVisit.StopAreaId = StopAreaId(sv.StopAreaId.String)
		}
		if sv.VehicleJourneyId.Valid {
			stopVisit.VehicleJourneyId = VehicleJourneyId(sv.VehicleJourneyId.String)
		}
		if sv.PassageOrder.Valid {
			stopVisit.PassageOrder = int(sv.PassageOrder.Int64)
		}

		if sv.Attributes.Valid && len(sv.Attributes.String) > 0 {
			if err = json.Unmarshal([]byte(sv.Attributes.String), &stopVisit.Attributes); err != nil {
				return err
			}
		}

		if sv.References.Valid && len(sv.References.String) > 0 {
			if err = json.Unmarshal([]byte(sv.References.String), &stopVisit.References); err != nil {
				return err
			}
		}

		if sv.ObjectIDs.Valid && len(sv.ObjectIDs.String) > 0 {
			objectIdMap := make(map[string]string)
			if err = json.Unmarshal([]byte(sv.ObjectIDs.String), &objectIdMap); err != nil {
				return err
			}
			stopVisit.objectids = NewObjectIDsFromMap(objectIdMap)
		}

		if sv.Schedules.Valid && len(sv.Schedules.String) > 0 {
			scheduleSlice := []StopVisitSchedule{}
			if err = json.Unmarshal([]byte(sv.Schedules.String), &scheduleSlice); err != nil {
				return err
			}
			stopVisit.Schedules = NewStopVisitSchedules()
			for _, schedule := range scheduleSlice {
				stopVisit.Schedules.SetSchedule(schedule.Kind(), schedule.DepartureTime(), schedule.ArrivalTime())
			}
		}

		manager.Save(&stopVisit)
	}
	return nil
}
<commit_msg>StopVisitSchedule#Copy when returning a copy of a StopVisit in StopVisitManager#Find and #FindBy<commit_after>package model

import (
	"encoding/json"
	"fmt"
	"sort"
	"sync"
	"time"
)

type StopVisitId string

type StopVisitAttributes struct {
	ObjectId         ObjectID
	StopAreaObjectId ObjectID

	VehicleJourneyObjectId ObjectID
	PassageOrder           int

	ArrivalStatus   StopVisitArrivalStatus
	DepartureStatus StopVisitDepartureStatus
	RecordedAt      time.Time
	Schedules       StopVisitSchedules
	VehicleAtStop   bool

	Attributes Attributes
	References References
}

type StopVisit struct {
	ObjectIDConsumer

	model  Model
	Origin string

	id          StopVisitId
	collected   bool
	collectedAt time.Time

	StopAreaId       StopAreaId       `json:",omitempty"`
	VehicleJourneyId VehicleJourneyId `json:",omitempty"`
	Attributes       Attributes
	References       References

	ArrivalStatus   StopVisitArrivalStatus   `json:",omitempty"`
	DepartureStatus StopVisitDepartureStatus `json:",omitempty"`
	RecordedAt      time.Time
	Schedules       StopVisitSchedules
	VehicleAtStop   bool
	PassageOrder    int `json:",omitempty"`
}

func NewStopVisit(model Model) *StopVisit {
	stopVisit := &StopVisit{
		model:      model,
		Schedules:  NewStopVisitSchedules(),
		Attributes: NewAttributes(),
		References: NewReferences(),
	}
	stopVisit.objectids = make(ObjectIDs)
	return stopVisit
}

func (stopVisit *StopVisit) copy() *StopVisit {
	s := *stopVisit
	s.Schedules = *(stopVisit.Schedules.Copy())
	return &s
}

func (stopVisit *StopVisit) IsCollected() bool {
	return stopVisit.collected
}

func (stopVisit *StopVisit) NotCollected() {
	stopVisit.collected = false
}

func (stopVisit *StopVisit) CollectedAt() time.Time {
	return stopVisit.collectedAt
}

func (stopVisit *StopVisit) Collected(t time.Time) {
	stopVisit.collected = true
	stopVisit.collectedAt = t
}

func (stopVisit *StopVisit) Id() StopVisitId {
	return stopVisit.id
}

func (stopVisit *StopVisit) StopArea() StopArea {
	stopArea, _ := stopVisit.model.StopAreas().Find(stopVisit.StopAreaId)
	return stopArea
}

func (stopVisit *StopVisit) VehicleJourney() *VehicleJourney {
	vehicleJourney, ok := stopVisit.model.VehicleJourneys().Find(stopVisit.VehicleJourneyId)
	if !ok {
		return nil
	}
	return &vehicleJourney
}

func (stopVisit *StopVisit) MarshalJSON() ([]byte, error) {
	type Alias StopVisit
	aux := struct {
		Id          StopVisitId
		ObjectIDs   ObjectIDs `json:",omitempty"`
		Collected   bool
		CollectedAt *time.Time           `json:",omitempty"`
		RecordedAt  *time.Time           `json:",omitempty"`
		Attributes  Attributes           `json:",omitempty"`
		References  map[string]Reference `json:",omitempty"`
		Schedules   []StopVisitSchedule  `json:",omitempty"`
		*Alias
	}{
		Id:        stopVisit.id,
		Collected: stopVisit.collected,
		Alias:     (*Alias)(stopVisit),
	}

	if !stopVisit.ObjectIDs().Empty() {
		aux.ObjectIDs = stopVisit.ObjectIDs()
	}
	if !stopVisit.Attributes.IsEmpty() {
		aux.Attributes = stopVisit.Attributes
	}
	if !stopVisit.References.IsEmpty() {
		aux.References = stopVisit.References.GetReferences()
	}
	if !stopVisit.RecordedAt.IsZero() {
		aux.RecordedAt = &stopVisit.RecordedAt
	}
	if !stopVisit.collectedAt.IsZero() {
		aux.CollectedAt = &stopVisit.collectedAt
	}

	scheduleSlice := stopVisit.Schedules.ToSlice()
	if len(scheduleSlice) != 0 {
		aux.Schedules = scheduleSlice
	}

	return json.Marshal(&aux)
}

func (stopVisit *StopVisit) UnmarshalJSON(data []byte) error {
	type Alias StopVisit
	aux := &struct {
		ObjectIDs   map[string]string
		References  map[string]Reference
		CollectedAt time.Time
		Schedules   []StopVisitSchedule
		*Alias
	}{
		Alias: (*Alias)(stopVisit),
	}

	err := json.Unmarshal(data, aux)
	if err != nil {
		return err
	}

	if aux.ObjectIDs != nil {
		stopVisit.ObjectIDConsumer.objectids = NewObjectIDsFromMap(aux.ObjectIDs)
	}

	if aux.References != nil {
		stopVisit.References.SetReferences(aux.References)
	}

	if aux.Schedules != nil {
		stopVisit.Schedules = NewStopVisitSchedules()
		for _, schedule := range aux.Schedules {
			stopVisit.Schedules.SetSchedule(schedule.Kind(), schedule.DepartureTime(), schedule.ArrivalTime())
		}
	}

	if !aux.CollectedAt.IsZero() {
		stopVisit.Collected(aux.CollectedAt)
	}
	return nil
}

func (stopVisit *StopVisit) Attribute(key string) (string, bool) {
	value, present := stopVisit.Attributes[key]
	return value, present
}

func (stopVisit *StopVisit) Save() (ok bool) {
	ok = stopVisit.model.StopVisits().Save(stopVisit)
	return
}

func (stopVisit *StopVisit) Reference(key string) (Reference, bool) {
	value, present := stopVisit.References.Get(key)
	return value, present
}

func (stopVisit *StopVisit) ReferenceTime() time.Time {
	orderMap := []StopVisitScheduleType{"actual", "expected", "aimed"}

	for _, kind := range orderMap {
		if schedule := stopVisit.Schedules.Schedule(kind); !schedule.ArrivalTime().IsZero() {
			return schedule.ArrivalTime()
		}
	}

	for _, kind := range orderMap {
		if schedule := stopVisit.Schedules.Schedule(kind); !schedule.DepartureTime().IsZero() {
			return schedule.DepartureTime()
		}
	}

	return time.Time{}
}

type ByTime []StopVisit

func (a ByTime) Len() int           { return len(a) }
func (a ByTime) Swap(i, j int)      { a[i], a[j] = a[j], a[i] }
func (a ByTime) Less(i, j int) bool { return !a[i].ReferenceTime().After(a[j].ReferenceTime()) }

type MemoryStopVisits struct {
	UUIDConsumer
	ClockConsumer

	model Model

	mutex          *sync.RWMutex
	byIdentifier   map[StopVisitId]*StopVisit
	broadcastEvent func(event StopMonitoringBroadcastEvent)
}

type StopVisits interface {
	UUIDInterface

	New() StopVisit
	Find(id StopVisitId) (StopVisit, bool)
	FindByObjectId(objectid ObjectID) (StopVisit, bool)
	FindByVehicleJourneyId(id VehicleJourneyId) []StopVisit
	FindFollowingByVehicleJourneyId(id VehicleJourneyId) []StopVisit
	FindByStopAreaId(id StopAreaId) []StopVisit
	FindFollowingByStopAreaId(id StopAreaId) []StopVisit
	FindFollowingByStopAreaIds(stopAreaIds []StopAreaId) []StopVisit
	FindAll() []StopVisit
	Save(stopVisit *StopVisit) bool
	Delete(stopVisit *StopVisit) bool
}

func NewMemoryStopVisits() *MemoryStopVisits {
	return &MemoryStopVisits{
		mutex:        &sync.RWMutex{},
		byIdentifier: make(map[StopVisitId]*StopVisit),
	}
}

func (manager *MemoryStopVisits) New() StopVisit {
	stopVisit := NewStopVisit(manager.model)
	return *stopVisit
}

func (manager *MemoryStopVisits) Find(id StopVisitId) (StopVisit, bool) {
	manager.mutex.RLock()
	defer manager.mutex.RUnlock()

	stopVisit, ok := manager.byIdentifier[id]

	if ok {
		return *(stopVisit.copy()), true
	} else {
		return StopVisit{}, false
	}
}

func (manager *MemoryStopVisits) FindByObjectId(objectid ObjectID) (StopVisit, bool) {
	manager.mutex.RLock()
	defer manager.mutex.RUnlock()

	for _, stopVisit := range manager.byIdentifier {
		stopVisitObjectId, _ := stopVisit.ObjectID(objectid.Kind())
		if stopVisitObjectId.Value() == objectid.Value() {
			return *(stopVisit.copy()), true
		}
	}
	return StopVisit{}, false
}

func (manager *MemoryStopVisits) FindByVehicleJourneyId(id VehicleJourneyId) (stopVisits []StopVisit) {
	manager.mutex.RLock()
	defer manager.mutex.RUnlock()

	for _, stopVisit := range manager.byIdentifier {
		if stopVisit.VehicleJourneyId == id {
			stopVisits = append(stopVisits, *(stopVisit.copy()))
		}
	}
	return
}

func (manager *MemoryStopVisits) FindFollowingByVehicleJourneyId(id VehicleJourneyId) (stopVisits []StopVisit) {
	manager.mutex.RLock()
	defer manager.mutex.RUnlock()

	for _, stopVisit := range manager.byIdentifier {
		if stopVisit.VehicleJourneyId == id && stopVisit.ReferenceTime().After(manager.Clock().Now()) {
			stopVisits = append(stopVisits, *(stopVisit.copy()))
		}
	}
	sort.Sort(ByTime(stopVisits))
	return
}

func (manager *MemoryStopVisits) FindByStopAreaId(id StopAreaId) (stopVisits []StopVisit) {
	manager.mutex.RLock()
	defer manager.mutex.RUnlock()

	for _, stopVisit := range manager.byIdentifier {
		if stopVisit.StopAreaId == id {
			stopVisits = append(stopVisits, *(stopVisit.copy()))
		}
	}
	return
}

func (manager *MemoryStopVisits) FindFollowingByStopAreaId(id StopAreaId) (stopVisits []StopVisit) {
	manager.mutex.RLock()
	defer manager.mutex.RUnlock()

	for _, stopVisit := range manager.byIdentifier {
		if stopVisit.StopAreaId == id && stopVisit.ReferenceTime().After(manager.Clock().Now()) {
			stopVisits = append(stopVisits, *(stopVisit.copy()))
		}
	}
	sort.Sort(ByTime(stopVisits))
	return
}

func (manager *MemoryStopVisits) FindFollowingByStopAreaIds(stopAreaIds []StopAreaId) (stopVisits []StopVisit) {
	manager.mutex.RLock()
	defer manager.mutex.RUnlock()

	for _, stopAreaId := range stopAreaIds {
		stopVisits = append(stopVisits, manager.FindFollowingByStopAreaId(stopAreaId)...)
	}
	sort.Sort(ByTime(stopVisits))
	return
}

func (manager *MemoryStopVisits) FindAll() (stopVisits []StopVisit) {
	manager.mutex.RLock()
	defer manager.mutex.RUnlock()

	if len(manager.byIdentifier) == 0 {
		return []StopVisit{}
	}
	for _, stopVisit := range manager.byIdentifier {
		stopVisits = append(stopVisits, *(stopVisit.copy()))
	}
	return
}

func (manager *MemoryStopVisits) Save(stopVisit *StopVisit) bool {
	manager.mutex.Lock()
	defer manager.mutex.Unlock()

	if stopVisit.id == "" {
		stopVisit.id = StopVisitId(manager.NewUUID())
	}

	stopVisit.model = manager.model
	manager.byIdentifier[stopVisit.id] = stopVisit

	event := StopMonitoringBroadcastEvent{
		ModelId:   string(stopVisit.id),
		ModelType: "StopVisit",
	}

	if manager.broadcastEvent != nil {
		manager.broadcastEvent(event)
	}

	return true
}

func (manager *MemoryStopVisits) Delete(stopVisit *StopVisit) bool {
	manager.mutex.Lock()
	defer manager.mutex.Unlock()

	delete(manager.byIdentifier, stopVisit.Id())
	return true
}

func (manager *MemoryStopVisits) Load(referentialSlug string) error {
	var selectStopVisits []SelectStopVisit
	modelName := manager.model.Date()

	sqlQuery := fmt.Sprintf("select * from stop_visits where referential_slug = '%s' and model_name = '%s'", referentialSlug, modelName.String())
	_, err := Database.Select(&selectStopVisits, sqlQuery)
	if err != nil {
		return err
	}
	for _, sv := range selectStopVisits {
		stopVisit := manager.New()
		stopVisit.id = StopVisitId(sv.Id)
		if sv.StopAreaId.Valid {
			stopVisit.StopAreaId = StopAreaId(sv.StopAreaId.String)
		}
		if sv.VehicleJourneyId.Valid {
			stopVisit.VehicleJourneyId = VehicleJourneyId(sv.VehicleJourneyId.String)
		}
		if sv.PassageOrder.Valid {
			stopVisit.PassageOrder = int(sv.PassageOrder.Int64)
		}

		if sv.Attributes.Valid && len(sv.Attributes.String) > 0 {
			if err = json.Unmarshal([]byte(sv.Attributes.String), &stopVisit.Attributes); err != nil {
				return err
			}
		}

		if sv.References.Valid && len(sv.References.String) > 0 {
			if err = json.Unmarshal([]byte(sv.References.String), &stopVisit.References); err != nil {
				return err
			}
		}

		if sv.ObjectIDs.Valid && len(sv.ObjectIDs.String) > 0 {
			objectIdMap := make(map[string]string)
			if err = json.Unmarshal([]byte(sv.ObjectIDs.String), &objectIdMap); err != nil {
				return err
			}
			stopVisit.objectids = NewObjectIDsFromMap(objectIdMap)
		}

		if sv.Schedules.Valid && len(sv.Schedules.String) > 0 {
			scheduleSlice := []StopVisitSchedule{}
			if err = json.Unmarshal([]byte(sv.Schedules.String), &scheduleSlice); err != nil {
				return err
			}
			stopVisit.Schedules = NewStopVisitSchedules()
			for _, schedule := range scheduleSlice {
				stopVisit.Schedules.SetSchedule(schedule.Kind(), schedule.DepartureTime(), schedule.ArrivalTime())
			}
		}

		manager.Save(&stopVisit)
	}
	return nil
}
<|endoftext|>


<commit_before>package models

import "gopkg.in/mgo.v2/bson"

func (q *Query) ToPipeline() []bson.M {
	pipeline := []bson.M{{"$group": bson.M{"_id": "$targetid", "gender": bson.M{"$max": "$gender"}, "birthdate": bson.M{"$max": "$birthdate"}, "entries": bson.M{"$addToSet": bson.M{"startdate": "$startdate", "enddate": "$enddate", "codes": "$codes", "type": "$type"}}}}}
	for _, extension := range q.Parameter {
		switch extension.Url {
		case "http://interventionengine.org/patientgender":
			pipeline = append(pipeline, bson.M{"$match": bson.M{"gender": extension.ValueString}})
		}
	}

	pipeline = append(pipeline, bson.M{"$group": bson.M{"_id": nil, "total": bson.M{"$sum": 1}}})
	return pipeline
}
<commit_msg>Starting work on handling condition code filtering<commit_after>package models

import "gopkg.in/mgo.v2/bson"

func (q *Query) ToPipeline() []bson.M {
	pipeline := []bson.M{{"$group": bson.M{"_id": "$targetid", "gender": bson.M{"$max": "$gender"}, "birthdate": bson.M{"$max": "$birthdate"}, "entries": bson.M{"$addToSet": bson.M{"startdate": "$startdate", "enddate": "$enddate", "codes": "$codes", "type": "$type"}}}}}
	for _, extension := range q.Parameter {
		switch extension.Url {
		case "http://interventionengine.org/patientgender":
			pipeline = append(pipeline, bson.M{"$match": bson.M{"gender": extension.ValueString}})
		case "http://interventionengine.org/conditioncode":
			// Hack for now assuming that all codable concepts contain a single code
			conditionCode := extension.ValueCodeableConcept.Coding[0].Code
			conditionSystem := extension.ValueCodeableConcept.Coding[0].System
			pipeline = append(pipeline, bson.M{"$match": bson.M{"entries.type": "Condition", "entries.codes.coding.code": conditionCode, "entries.codes.coding.system": conditionSystem}})
		}
	}

	pipeline = append(pipeline, bson.M{"$group": bson.M{"_id": nil, "total": bson.M{"$sum": 1}}})
	return pipeline
}
<|endoftext|>