<commit_before>// +build !windows

package containerd

import (
	"context"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"strconv"
	"strings"

	"golang.org/x/sys/unix"

	"github.com/containerd/containerd/containers"
	"github.com/containerd/containerd/content"
	"github.com/containerd/containerd/fs"
	"github.com/containerd/containerd/images"
	"github.com/containerd/containerd/namespaces"
	"github.com/opencontainers/image-spec/identity"
	"github.com/opencontainers/image-spec/specs-go/v1"
	"github.com/opencontainers/runc/libcontainer/user"
	specs "github.com/opencontainers/runtime-spec/specs-go"
	"github.com/pkg/errors"
)

// WithTTY sets the information on the spec as well as the environment variables for
// using a TTY
func WithTTY(_ context.Context, _ *Client, _ *containers.Container, s *specs.Spec) error {
	s.Process.Terminal = true
	s.Process.Env = append(s.Process.Env, "TERM=xterm")
	return nil
}

// WithHostNamespace allows a task to run inside the host's linux namespace
func WithHostNamespace(ns specs.LinuxNamespaceType) SpecOpts {
	return func(_ context.Context, _ *Client, _ *containers.Container, s *specs.Spec) error {
		for i, n := range s.Linux.Namespaces {
			if n.Type == ns {
				s.Linux.Namespaces = append(s.Linux.Namespaces[:i], s.Linux.Namespaces[i+1:]...)
				return nil
			}
		}
		return nil
	}
}

// WithLinuxNamespace uses the passed in namespace for the spec. If a namespace of the same type already exists in the
// spec, the existing namespace is replaced by the one provided.
func WithLinuxNamespace(ns specs.LinuxNamespace) SpecOpts {
	return func(_ context.Context, _ *Client, _ *containers.Container, s *specs.Spec) error {
		for i, n := range s.Linux.Namespaces {
			if n.Type == ns.Type {
				before := s.Linux.Namespaces[:i]
				after := s.Linux.Namespaces[i+1:]
				s.Linux.Namespaces = append(before, ns)
				s.Linux.Namespaces = append(s.Linux.Namespaces, after...)
				return nil
			}
		}
		s.Linux.Namespaces = append(s.Linux.Namespaces, ns)
		return nil
	}
}

// WithImageConfig configures the spec to from the configuration of an Image
func WithImageConfig(i Image) SpecOpts {
	return func(ctx context.Context, client *Client, c *containers.Container, s *specs.Spec) error {
		var (
			image = i.(*image)
			store = client.ContentStore()
		)
		ic, err := image.i.Config(ctx, store)
		if err != nil {
			return err
		}
		var (
			ociimage v1.Image
			config   v1.ImageConfig
		)
		switch ic.MediaType {
		case v1.MediaTypeImageConfig, images.MediaTypeDockerSchema2Config:
			p, err := content.ReadBlob(ctx, store, ic.Digest)
			if err != nil {
				return err
			}

			if err := json.Unmarshal(p, &ociimage); err != nil {
				return err
			}
			config = ociimage.Config
		default:
			return fmt.Errorf("unknown image config media type %s", ic.MediaType)
		}
		s.Process.Env = append(s.Process.Env, config.Env...)
		cmd := config.Cmd
		s.Process.Args = append(config.Entrypoint, cmd...)
		if config.User != "" {
			parts := strings.Split(config.User, ":")
			switch len(parts) {
			case 1:
				v, err := strconv.ParseUint(parts[0], 0, 10)
				if err != nil {
					// if we cannot parse as a uint they try to see if it is a username
					if err := WithUsername(config.User)(ctx, client, c, s); err != nil {
						return err
					}
					return err
				}
				if err := WithUserID(uint32(v))(ctx, client, c, s); err != nil {
					return err
				}
			case 2:
				v, err := strconv.ParseUint(parts[0], 0, 10)
				if err != nil {
					return err
				}
				uid := uint32(v)
				if v, err = strconv.ParseUint(parts[1], 0, 10); err != nil {
					return err
				}
				gid := uint32(v)
				s.Process.User.UID, s.Process.User.GID = uid, gid
			default:
				return fmt.Errorf("invalid USER value %s", config.User)
			}
		}
		cwd := config.WorkingDir
		if cwd == "" {
			cwd = "/"
		}
		s.Process.Cwd = cwd
		return nil
	}
}

// WithRootFSPath specifies unmanaged rootfs path.
func WithRootFSPath(path string, readonly bool) SpecOpts {
	return func(_ context.Context, _ *Client, _ *containers.Container, s *specs.Spec) error {
		s.Root = &specs.Root{
			Path:     path,
			Readonly: readonly,
		}
		// Entrypoint is not set here (it's up to caller)
		return nil
	}
}

// WithResources sets the provided resources on the spec for task updates
func WithResources(resources *specs.LinuxResources) UpdateTaskOpts {
	return func(ctx context.Context, client *Client, r *UpdateTaskInfo) error {
		r.Resources = resources
		return nil
	}
}

// WithNoNewPrivileges sets no_new_privileges on the process for the container
func WithNoNewPrivileges(_ context.Context, _ *Client, _ *containers.Container, s *specs.Spec) error {
	s.Process.NoNewPrivileges = true
	return nil
}

// WithHostHostsFile bind-mounts the host's /etc/hosts into the container as readonly
func WithHostHostsFile(_ context.Context, _ *Client, _ *containers.Container, s *specs.Spec) error {
	s.Mounts = append(s.Mounts, specs.Mount{
		Destination: "/etc/hosts",
		Type:        "bind",
		Source:      "/etc/hosts",
		Options:     []string{"rbind", "ro"},
	})
	return nil
}

// WithHostResolvconf bind-mounts the host's /etc/resolv.conf into the container as readonly
func WithHostResolvconf(_ context.Context, _ *Client, _ *containers.Container, s *specs.Spec) error {
	s.Mounts = append(s.Mounts, specs.Mount{
		Destination: "/etc/resolv.conf",
		Type:        "bind",
		Source:      "/etc/resolv.conf",
		Options:     []string{"rbind", "ro"},
	})
	return nil
}

// WithHostLocaltime bind-mounts the host's /etc/localtime into the container as readonly
func WithHostLocaltime(_ context.Context, _ *Client, _ *containers.Container, s *specs.Spec) error {
	s.Mounts = append(s.Mounts, specs.Mount{
		Destination: "/etc/localtime",
		Type:        "bind",
		Source:      "/etc/localtime",
		Options:     []string{"rbind", "ro"},
	})
	return nil
}

// WithUserNamespace sets the uid and gid mappings for the task
// this can be called multiple times to add more mappings to the generated spec
func WithUserNamespace(container, host, size uint32) SpecOpts {
	return func(_ context.Context, _ *Client, _ *containers.Container, s *specs.Spec) error {
		var hasUserns bool
		for _, ns := range s.Linux.Namespaces {
			if ns.Type == specs.UserNamespace {
				hasUserns = true
				break
			}
		}
		if !hasUserns {
			s.Linux.Namespaces = append(s.Linux.Namespaces, specs.LinuxNamespace{
				Type: specs.UserNamespace,
			})
		}
		mapping := specs.LinuxIDMapping{
			ContainerID: container,
			HostID:      host,
			Size:        size,
		}
		s.Linux.UIDMappings = append(s.Linux.UIDMappings, mapping)
		s.Linux.GIDMappings = append(s.Linux.GIDMappings, mapping)
		return nil
	}
}

// WithRemappedSnapshot creates a new snapshot and remaps the uid/gid for the
// filesystem to be used by a container with user namespaces
func WithRemappedSnapshot(id string, i Image, uid, gid uint32) NewContainerOpts {
	return func(ctx context.Context, client *Client, c *containers.Container) error {
		diffIDs, err := i.(*image).i.RootFS(ctx, client.ContentStore())
		if err != nil {
			return err
		}

		setSnapshotterIfEmpty(c)

		var (
			snapshotter = client.SnapshotService(c.Snapshotter)
			parent      = identity.ChainID(diffIDs).String()
			usernsID    = fmt.Sprintf("%s-%d-%d", parent, uid, gid)
		)
		if _, err := snapshotter.Stat(ctx, usernsID); err == nil {
			if _, err := snapshotter.Prepare(ctx, id, usernsID); err != nil {
				return err
			}
			c.RootFS = id
			c.Image = i.Name()
			return nil
		}
		mounts, err := snapshotter.Prepare(ctx, usernsID+"-remap", parent)
		if err != nil {
			return err
		}
		if err := remapRootFS(mounts, uid, gid); err != nil {
			snapshotter.Remove(ctx, usernsID)
			return err
		}
		if err := snapshotter.Commit(ctx, usernsID, usernsID+"-remap"); err != nil {
			return err
		}
		if _, err := snapshotter.Prepare(ctx, id, usernsID); err != nil {
			return err
		}
		c.RootFS = id
		c.Image = i.Name()
		return nil
	}
}

// WithCgroup sets the container's cgroup path
func WithCgroup(path string) SpecOpts {
	return func(_ context.Context, _ *Client, _ *containers.Container, s *specs.Spec) error {
		s.Linux.CgroupsPath = path
		return nil
	}
}

// WithNamespacedCgroup uses the namespace set on the context to create a
// root directory for containers in the cgroup with the id as the subcgroup
func WithNamespacedCgroup() SpecOpts {
	return func(ctx context.Context, _ *Client, c *containers.Container, s *specs.Spec) error {
		namespace, err := namespaces.NamespaceRequired(ctx)
		if err != nil {
			return err
		}
		s.Linux.CgroupsPath = filepath.Join("/", namespace, c.ID)
		return nil
	}
}

// WithUidGid allows the UID and GID for the Process to be set
func WithUidGid(uid, gid uint32) SpecOpts {
	return func(_ context.Context, _ *Client, _ *containers.Container, s *specs.Spec) error {
		s.Process.User.UID = uid
		s.Process.User.GID = gid
		return nil
	}
}

// WithUserID sets the correct UID and GID for the container based
// on the image's /etc/passwd contents. If uid is not found in
// /etc/passwd, it sets uid but leaves gid 0, and not returns error.
func WithUserID(uid uint32) SpecOpts {
	return func(ctx context.Context, client *Client, c *containers.Container, s *specs.Spec) error {
		if c.Snapshotter == "" {
			return errors.Errorf("no snapshotter set for container")
		}
		if c.RootFS == "" {
			return errors.Errorf("rootfs not created for container")
		}
		snapshotter := client.SnapshotService(c.Snapshotter)
		mounts, err := snapshotter.Mounts(ctx, c.RootFS)
		if err != nil {
			return err
		}
		root, err := ioutil.TempDir("", "ctd-username")
		if err != nil {
			return err
		}
		defer os.RemoveAll(root)
		for _, m := range mounts {
			if err := m.Mount(root); err != nil {
				return err
			}
		}
		defer unix.Unmount(root, 0)
		ppath, err := fs.RootPath(root, "/etc/passwd")
		if err != nil {
			return err
		}
		f, err := os.Open(ppath)
		if err != nil {
			return err
		}
		defer f.Close()
		users, err := user.ParsePasswdFilter(f, func(u user.User) bool {
			return u.Uid == int(uid)
		})
		if err != nil {
			return err
		}
		if len(users) == 0 {
			s.Process.User.UID = uid
			return nil
		}
		u := users[0]
		s.Process.User.UID, s.Process.User.GID = uint32(u.Uid), uint32(u.Gid)
		return nil
	}
}

// WithUsername sets the correct UID and GID for the container
// based on the the image's /etc/passwd contents. If the username
// is not found in /etc/passwd, it returns error.
func WithUsername(username string) SpecOpts {
	return func(ctx context.Context, client *Client, c *containers.Container, s *specs.Spec) error {
		if c.Snapshotter == "" {
			return errors.Errorf("no snapshotter set for container")
		}
		if c.RootFS == "" {
			return errors.Errorf("rootfs not created for container")
		}
		snapshotter := client.SnapshotService(c.Snapshotter)
		mounts, err := snapshotter.Mounts(ctx, c.RootFS)
		if err != nil {
			return err
		}
		root, err := ioutil.TempDir("", "ctd-username")
		if err != nil {
			return err
		}
		defer os.RemoveAll(root)
		for _, m := range mounts {
			if err := m.Mount(root); err != nil {
				return err
			}
		}
		defer unix.Unmount(root, 0)
		ppath, err := fs.RootPath(root, "/etc/passwd")
		if err != nil {
			return err
		}
		f, err := os.Open(ppath)
		if err != nil {
			return err
		}
		defer f.Close()
		users, err := user.ParsePasswdFilter(f, func(u user.User) bool {
			return u.Name == username
		})
		if err != nil {
			return err
		}
		if len(users) == 0 {
			return errors.Errorf("no users found for %s", username)
		}
		u := users[0]
		s.Process.User.UID, s.Process.User.GID = uint32(u.Uid), uint32(u.Gid)
		return nil
	}
}
<commit_msg>WithUserID should not return error when /etc/passwd doesn't exist.<commit_after>// +build !windows

package containerd

import (
	"context"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"strconv"
	"strings"

	"golang.org/x/sys/unix"

	"github.com/containerd/containerd/containers"
	"github.com/containerd/containerd/content"
	"github.com/containerd/containerd/fs"
	"github.com/containerd/containerd/images"
	"github.com/containerd/containerd/namespaces"
	"github.com/opencontainers/image-spec/identity"
	"github.com/opencontainers/image-spec/specs-go/v1"
	"github.com/opencontainers/runc/libcontainer/user"
	specs "github.com/opencontainers/runtime-spec/specs-go"
	"github.com/pkg/errors"
)

// WithTTY sets the information on the spec as well as the environment variables for
// using a TTY
func WithTTY(_ context.Context, _ *Client, _ *containers.Container, s *specs.Spec) error {
	s.Process.Terminal = true
	s.Process.Env = append(s.Process.Env, "TERM=xterm")
	return nil
}

// WithHostNamespace allows a task to run inside the host's linux namespace
func WithHostNamespace(ns specs.LinuxNamespaceType) SpecOpts {
	return func(_ context.Context, _ *Client, _ *containers.Container, s *specs.Spec) error {
		for i, n := range s.Linux.Namespaces {
			if n.Type == ns {
				s.Linux.Namespaces = append(s.Linux.Namespaces[:i], s.Linux.Namespaces[i+1:]...)
				return nil
			}
		}
		return nil
	}
}

// WithLinuxNamespace uses the passed in namespace for the spec. If a namespace of the same type already exists in the
// spec, the existing namespace is replaced by the one provided.
func WithLinuxNamespace(ns specs.LinuxNamespace) SpecOpts {
	return func(_ context.Context, _ *Client, _ *containers.Container, s *specs.Spec) error {
		for i, n := range s.Linux.Namespaces {
			if n.Type == ns.Type {
				before := s.Linux.Namespaces[:i]
				after := s.Linux.Namespaces[i+1:]
				s.Linux.Namespaces = append(before, ns)
				s.Linux.Namespaces = append(s.Linux.Namespaces, after...)
				return nil
			}
		}
		s.Linux.Namespaces = append(s.Linux.Namespaces, ns)
		return nil
	}
}

// WithImageConfig configures the spec to from the configuration of an Image
func WithImageConfig(i Image) SpecOpts {
	return func(ctx context.Context, client *Client, c *containers.Container, s *specs.Spec) error {
		var (
			image = i.(*image)
			store = client.ContentStore()
		)
		ic, err := image.i.Config(ctx, store)
		if err != nil {
			return err
		}
		var (
			ociimage v1.Image
			config   v1.ImageConfig
		)
		switch ic.MediaType {
		case v1.MediaTypeImageConfig, images.MediaTypeDockerSchema2Config:
			p, err := content.ReadBlob(ctx, store, ic.Digest)
			if err != nil {
				return err
			}

			if err := json.Unmarshal(p, &ociimage); err != nil {
				return err
			}
			config = ociimage.Config
		default:
			return fmt.Errorf("unknown image config media type %s", ic.MediaType)
		}
		s.Process.Env = append(s.Process.Env, config.Env...)
		cmd := config.Cmd
		s.Process.Args = append(config.Entrypoint, cmd...)
		if config.User != "" {
			parts := strings.Split(config.User, ":")
			switch len(parts) {
			case 1:
				v, err := strconv.ParseUint(parts[0], 0, 10)
				if err != nil {
					// if we cannot parse as a uint they try to see if it is a username
					if err := WithUsername(config.User)(ctx, client, c, s); err != nil {
						return err
					}
					return err
				}
				if err := WithUserID(uint32(v))(ctx, client, c, s); err != nil {
					return err
				}
			case 2:
				v, err := strconv.ParseUint(parts[0], 0, 10)
				if err != nil {
					return err
				}
				uid := uint32(v)
				if v, err = strconv.ParseUint(parts[1], 0, 10); err != nil {
					return err
				}
				gid := uint32(v)
				s.Process.User.UID, s.Process.User.GID = uid, gid
			default:
				return fmt.Errorf("invalid USER value %s", config.User)
			}
		}
		cwd := config.WorkingDir
		if cwd == "" {
			cwd = "/"
		}
		s.Process.Cwd = cwd
		return nil
	}
}

// WithRootFSPath specifies unmanaged rootfs path.
func WithRootFSPath(path string, readonly bool) SpecOpts {
	return func(_ context.Context, _ *Client, _ *containers.Container, s *specs.Spec) error {
		s.Root = &specs.Root{
			Path:     path,
			Readonly: readonly,
		}
		// Entrypoint is not set here (it's up to caller)
		return nil
	}
}

// WithResources sets the provided resources on the spec for task updates
func WithResources(resources *specs.LinuxResources) UpdateTaskOpts {
	return func(ctx context.Context, client *Client, r *UpdateTaskInfo) error {
		r.Resources = resources
		return nil
	}
}

// WithNoNewPrivileges sets no_new_privileges on the process for the container
func WithNoNewPrivileges(_ context.Context, _ *Client, _ *containers.Container, s *specs.Spec) error {
	s.Process.NoNewPrivileges = true
	return nil
}

// WithHostHostsFile bind-mounts the host's /etc/hosts into the container as readonly
func WithHostHostsFile(_ context.Context, _ *Client, _ *containers.Container, s *specs.Spec) error {
	s.Mounts = append(s.Mounts, specs.Mount{
		Destination: "/etc/hosts",
		Type:        "bind",
		Source:      "/etc/hosts",
		Options:     []string{"rbind", "ro"},
	})
	return nil
}

// WithHostResolvconf bind-mounts the host's /etc/resolv.conf into the container as readonly
func WithHostResolvconf(_ context.Context, _ *Client, _ *containers.Container, s *specs.Spec) error {
	s.Mounts = append(s.Mounts, specs.Mount{
		Destination: "/etc/resolv.conf",
		Type:        "bind",
		Source:      "/etc/resolv.conf",
		Options:     []string{"rbind", "ro"},
	})
	return nil
}

// WithHostLocaltime bind-mounts the host's /etc/localtime into the container as readonly
func WithHostLocaltime(_ context.Context, _ *Client, _ *containers.Container, s *specs.Spec) error {
	s.Mounts = append(s.Mounts, specs.Mount{
		Destination: "/etc/localtime",
		Type:        "bind",
		Source:      "/etc/localtime",
		Options:     []string{"rbind", "ro"},
	})
	return nil
}

// WithUserNamespace sets the uid and gid mappings for the task
// this can be called multiple times to add more mappings to the generated spec
func WithUserNamespace(container, host, size uint32) SpecOpts {
	return func(_ context.Context, _ *Client, _ *containers.Container, s *specs.Spec) error {
		var hasUserns bool
		for _, ns := range s.Linux.Namespaces {
			if ns.Type == specs.UserNamespace {
				hasUserns = true
				break
			}
		}
		if !hasUserns {
			s.Linux.Namespaces = append(s.Linux.Namespaces, specs.LinuxNamespace{
				Type: specs.UserNamespace,
			})
		}
		mapping := specs.LinuxIDMapping{
			ContainerID: container,
			HostID:      host,
			Size:        size,
		}
		s.Linux.UIDMappings = append(s.Linux.UIDMappings, mapping)
		s.Linux.GIDMappings = append(s.Linux.GIDMappings, mapping)
		return nil
	}
}

// WithRemappedSnapshot creates a new snapshot and remaps the uid/gid for the
// filesystem to be used by a container with user namespaces
func WithRemappedSnapshot(id string, i Image, uid, gid uint32) NewContainerOpts {
	return func(ctx context.Context, client *Client, c *containers.Container) error {
		diffIDs, err := i.(*image).i.RootFS(ctx, client.ContentStore())
		if err != nil {
			return err
		}

		setSnapshotterIfEmpty(c)

		var (
			snapshotter = client.SnapshotService(c.Snapshotter)
			parent      = identity.ChainID(diffIDs).String()
			usernsID    = fmt.Sprintf("%s-%d-%d", parent, uid, gid)
		)
		if _, err := snapshotter.Stat(ctx, usernsID); err == nil {
			if _, err := snapshotter.Prepare(ctx, id, usernsID); err != nil {
				return err
			}
			c.RootFS = id
			c.Image = i.Name()
			return nil
		}
		mounts, err := snapshotter.Prepare(ctx, usernsID+"-remap", parent)
		if err != nil {
			return err
		}
		if err := remapRootFS(mounts, uid, gid); err != nil {
			snapshotter.Remove(ctx, usernsID)
			return err
		}
		if err := snapshotter.Commit(ctx, usernsID, usernsID+"-remap"); err != nil {
			return err
		}
		if _, err := snapshotter.Prepare(ctx, id, usernsID); err != nil {
			return err
		}
		c.RootFS = id
		c.Image = i.Name()
		return nil
	}
}

// WithCgroup sets the container's cgroup path
func WithCgroup(path string) SpecOpts {
	return func(_ context.Context, _ *Client, _ *containers.Container, s *specs.Spec) error {
		s.Linux.CgroupsPath = path
		return nil
	}
}

// WithNamespacedCgroup uses the namespace set on the context to create a
// root directory for containers in the cgroup with the id as the subcgroup
func WithNamespacedCgroup() SpecOpts {
	return func(ctx context.Context, _ *Client, c *containers.Container, s *specs.Spec) error {
		namespace, err := namespaces.NamespaceRequired(ctx)
		if err != nil {
			return err
		}
		s.Linux.CgroupsPath = filepath.Join("/", namespace, c.ID)
		return nil
	}
}

// WithUidGid allows the UID and GID for the Process to be set
func WithUidGid(uid, gid uint32) SpecOpts {
	return func(_ context.Context, _ *Client, _ *containers.Container, s *specs.Spec) error {
		s.Process.User.UID = uid
		s.Process.User.GID = gid
		return nil
	}
}

// WithUserID sets the correct UID and GID for the container based
// on the image's /etc/passwd contents. If /etc/passwd does not exist,
// or uid is not found in /etc/passwd, it sets gid to be the same with
// uid, and not returns error.
func WithUserID(uid uint32) SpecOpts {
	return func(ctx context.Context, client *Client, c *containers.Container, s *specs.Spec) error {
		if c.Snapshotter == "" {
			return errors.Errorf("no snapshotter set for container")
		}
		if c.RootFS == "" {
			return errors.Errorf("rootfs not created for container")
		}
		snapshotter := client.SnapshotService(c.Snapshotter)
		mounts, err := snapshotter.Mounts(ctx, c.RootFS)
		if err != nil {
			return err
		}
		root, err := ioutil.TempDir("", "ctd-username")
		if err != nil {
			return err
		}
		defer os.RemoveAll(root)
		for _, m := range mounts {
			if err := m.Mount(root); err != nil {
				return err
			}
		}
		defer unix.Unmount(root, 0)
		ppath, err := fs.RootPath(root, "/etc/passwd")
		if err != nil {
			return err
		}
		f, err := os.Open(ppath)
		if err != nil {
			if os.IsNotExist(err) {
				s.Process.User.UID, s.Process.User.GID = uid, uid
				return nil
			}
			return err
		}
		defer f.Close()
		users, err := user.ParsePasswdFilter(f, func(u user.User) bool {
			return u.Uid == int(uid)
		})
		if err != nil {
			return err
		}
		if len(users) == 0 {
			s.Process.User.UID, s.Process.User.GID = uid, uid
			return nil
		}
		u := users[0]
		s.Process.User.UID, s.Process.User.GID = uint32(u.Uid), uint32(u.Gid)
		return nil
	}
}

// WithUsername sets the correct UID and GID for the container
// based on the the image's /etc/passwd contents. If /etc/passwd
// does not exist, or the username is not found in /etc/passwd,
// it returns error.
func WithUsername(username string) SpecOpts {
	return func(ctx context.Context, client *Client, c *containers.Container, s *specs.Spec) error {
		if c.Snapshotter == "" {
			return errors.Errorf("no snapshotter set for container")
		}
		if c.RootFS == "" {
			return errors.Errorf("rootfs not created for container")
		}
		snapshotter := client.SnapshotService(c.Snapshotter)
		mounts, err := snapshotter.Mounts(ctx, c.RootFS)
		if err != nil {
			return err
		}
		root, err := ioutil.TempDir("", "ctd-username")
		if err != nil {
			return err
		}
		defer os.RemoveAll(root)
		for _, m := range mounts {
			if err := m.Mount(root); err != nil {
				return err
			}
		}
		defer unix.Unmount(root, 0)
		ppath, err := fs.RootPath(root, "/etc/passwd")
		if err != nil {
			return err
		}
		f, err := os.Open(ppath)
		if err != nil {
			return err
		}
		defer f.Close()
		users, err := user.ParsePasswdFilter(f, func(u user.User) bool {
			return u.Name == username
		})
		if err != nil {
			return err
		}
		if len(users) == 0 {
			return errors.Errorf("no users found for %s", username)
		}
		u := users[0]
		s.Process.User.UID, s.Process.User.GID = uint32(u.Uid), uint32(u.Gid)
		return nil
	}
}
<|endoftext|>


<commit_before>package main

import (
	"github.com/ymotongpoo/goltsv"
	"reflect"
	"strings"
	"testing"
)

var ParseKeysListTests = []struct {
	list string
	keys []string
}{
	// normal
	{`host`, []string{`host`}},
	{`host,status`, []string{`host`, `status`}},
	{`host,status,size`, []string{`host`, `status`, `size`}},

	// include empty keys
	{``, []string{``}},
	{`,`, []string{``, ``}},
	{`,,`, []string{``, ``, ``}},
	{`,host`, []string{``, `host`}},
	{`,,host`, []string{``, ``, `host`}},
	{`host,`, []string{`host`, ``}},
	{`host,,`, []string{`host`, ``, ``}},
	{`,,host,,status,,`, []string{``, ``, `host`, ``, `status`, ``, ``}},

	// include escaped comma
	{`a\,b`, []string{`a,b`}},
	{`a\,\,b`, []string{`a,,b`}},
	{`a\,,b\,`, []string{`a,`, `b,`}},
	{`\,a,\,b`, []string{`,a`, `,b`}},
	{`\,a\,,\,b\,`, []string{`,a,`, `,b,`}},
	{`a\,b,c\,d\,e`, []string{`a,b`, `c,d,e`}},
	{`a\,b,c\,d\,e,f\,g\,h\,i`, []string{`a,b`, `c,d,e`, `f,g,h,i`}},

	// include escaped backslash
	{`a\\b`, []string{`a\b`}},
	{`a\\\\b`, []string{`a\\b`}},
	{`a\\,b\\`, []string{`a\`, `b\`}},
	{`\\a,\\b`, []string{`\a`, `\b`}},
	{`\\a\\,\\b\\`, []string{`\a\`, `\b\`}},
	{`a\\b,c\\d\\e`, []string{`a\b`, `c\d\e`}},
	{`a\\b,c\\d\\e,f\\g\\h\\i`, []string{`a\b`, `c\d\e`, `f\g\h\i`}},
}

func TestParseKeysList(t *testing.T) {
	for _, test := range ParseKeysListTests {
		expect := test.keys
		actual := ParseKeysList(test.list)
		if !reflect.DeepEqual(actual, expect) {
			t.Errorf("ParseKeysList(%q) = %q, want %q",
				test.list, actual, expect)
		}
	}
}

func TestNewLTSVScanner(t *testing.T) {
	keys := []string{"host", "time"}
	reader := strings.NewReader(``)
	expect := &LTSVScanner{
		keys:   keys,
		reader: goltsv.NewReader(reader),
	}
	actual := NewLTSVScanner(keys, reader)
	if !reflect.DeepEqual(actual, expect) {
		t.Errorf("NewLTSVScanner(%q, %q) = %q, want %q",
			keys, reader, actual, expect)
	}
}

func TestScan(t *testing.T) {
	keys := []string{"host"}
	reader := strings.NewReader(`
host:192.168.0.1	status:200
host:172.16.0.12	status:404
`[1:])
	l := NewLTSVScanner(keys, reader)
	expects := []bool{true, true, false}
	for i := 0; i < len(expects); i++ {
		expect := expects[i]
		actual := l.Scan()
		if actual != expect {
			t.Errorf("Scan[%v]: got %v, want %v", i, actual, expect)
		}
	}
}

func TestScanError(t *testing.T) {
	keys := []string{"host"}
	reader := strings.NewReader(`
host:192.168.0.1	status:200
a	b	c
host:172.16.0.12	status:404
`[1:])
	l := NewLTSVScanner(keys, reader)
	expects := []bool{true, false, false}
	for i := 0; i < len(expects); i++ {
		expect := expects[i]
		actual := l.Scan()
		if actual != expect {
			t.Errorf("Scan[%v]: got %v, want %v", i, actual, expect)
		}
	}
}

func TestErr(t *testing.T) {
	keys := []string{"host"}
	reader := strings.NewReader(`
host:192.168.0.1	status:200
host:172.16.0.12	status:404
`[1:])
	l := NewLTSVScanner(keys, reader)
	expects := []error{nil, nil, nil}
	for i := 0; i < len(expects); i++ {
		l.Scan()
		expect := expects[i]
		actual := l.Err()
		if actual != expect {
			t.Errorf("Scan[%v]: got %v, want %v", i, actual, expect)
		}
	}
}
<commit_msg>Add test at error for LTSVScanner.Err<commit_after>package main

import (
	"github.com/ymotongpoo/goltsv"
	"reflect"
	"strings"
	"testing"
)

var ParseKeysListTests = []struct {
	list string
	keys []string
}{
	// normal
	{`host`, []string{`host`}},
	{`host,status`, []string{`host`, `status`}},
	{`host,status,size`, []string{`host`, `status`, `size`}},

	// include empty keys
	{``, []string{``}},
	{`,`, []string{``, ``}},
	{`,,`, []string{``, ``, ``}},
	{`,host`, []string{``, `host`}},
	{`,,host`, []string{``, ``, `host`}},
	{`host,`, []string{`host`, ``}},
	{`host,,`, []string{`host`, ``, ``}},
	{`,,host,,status,,`, []string{``, ``, `host`, ``, `status`, ``, ``}},

	// include escaped comma
	{`a\,b`, []string{`a,b`}},
	{`a\,\,b`, []string{`a,,b`}},
	{`a\,,b\,`, []string{`a,`, `b,`}},
	{`\,a,\,b`, []string{`,a`, `,b`}},
	{`\,a\,,\,b\,`, []string{`,a,`, `,b,`}},
	{`a\,b,c\,d\,e`, []string{`a,b`, `c,d,e`}},
	{`a\,b,c\,d\,e,f\,g\,h\,i`, []string{`a,b`, `c,d,e`, `f,g,h,i`}},

	// include escaped backslash
	{`a\\b`, []string{`a\b`}},
	{`a\\\\b`, []string{`a\\b`}},
	{`a\\,b\\`, []string{`a\`, `b\`}},
	{`\\a,\\b`, []string{`\a`, `\b`}},
	{`\\a\\,\\b\\`, []string{`\a\`, `\b\`}},
	{`a\\b,c\\d\\e`, []string{`a\b`, `c\d\e`}},
	{`a\\b,c\\d\\e,f\\g\\h\\i`, []string{`a\b`, `c\d\e`, `f\g\h\i`}},
}

func TestParseKeysList(t *testing.T) {
	for _, test := range ParseKeysListTests {
		expect := test.keys
		actual := ParseKeysList(test.list)
		if !reflect.DeepEqual(actual, expect) {
			t.Errorf("ParseKeysList(%q) = %q, want %q",
				test.list, actual, expect)
		}
	}
}

func TestNewLTSVScanner(t *testing.T) {
	keys := []string{"host", "time"}
	reader := strings.NewReader(``)
	expect := &LTSVScanner{
		keys:   keys,
		reader: goltsv.NewReader(reader),
	}
	actual := NewLTSVScanner(keys, reader)
	if !reflect.DeepEqual(actual, expect) {
		t.Errorf("NewLTSVScanner(%q, %q) = %q, want %q",
			keys, reader, actual, expect)
	}
}

func TestScan(t *testing.T) {
	keys := []string{"host"}
	reader := strings.NewReader(`
host:192.168.0.1	status:200
host:172.16.0.12	status:404
`[1:])
	l := NewLTSVScanner(keys, reader)
	expects := []bool{true, true, false}
	for i := 0; i < len(expects); i++ {
		expect := expects[i]
		actual := l.Scan()
		if actual != expect {
			t.Errorf("Scan[%v]: got %v, want %v", i, actual, expect)
		}
	}
}

func TestScanError(t *testing.T) {
	keys := []string{"host"}
	reader := strings.NewReader(`
host:192.168.0.1	status:200
a	b	c
host:172.16.0.12	status:404
`[1:])
	l := NewLTSVScanner(keys, reader)
	expects := []bool{true, false, false}
	for i := 0; i < len(expects); i++ {
		expect := expects[i]
		actual := l.Scan()
		if actual != expect {
			t.Errorf("Scan[%v]: got %v, want %v", i, actual, expect)
		}
	}
}

func TestErr(t *testing.T) {
	keys := []string{"host"}
	reader := strings.NewReader(`
host:192.168.0.1	status:200
host:172.16.0.12	status:404
`[1:])
	l := NewLTSVScanner(keys, reader)
	expects := []error{nil, nil, nil}
	for i := 0; i < len(expects); i++ {
		l.Scan()
		expect := expects[i]
		actual := l.Err()
		if actual != expect {
			t.Errorf("Scan[%v]: got %v, want %v", i, actual, expect)
		}
	}
}

func TestErrError(t *testing.T) {
	keys := []string{"host"}
	reader := strings.NewReader(`
host:192.168.0.1	status:200
a	b	c
host:172.16.0.12	status:404
`[1:])
	l := NewLTSVScanner(keys, reader)
	expects := []error{nil, goltsv.ErrLabelName, goltsv.ErrLabelName}
	for i := 0; i < len(expects); i++ {
		l.Scan()
		expect := expects[i]
		actual := l.Err()
		if actual != expect {
			t.Errorf("Scan[%v]: got %v, want %v", i, actual, expect)
		}
	}
}
<|endoftext|>


<commit_before>// +build linux,cgo,!agent

package sys

import (
	"os"
	"path/filepath"

	"github.com/pkg/errors"
)

// LocalDatabasePath returns the path of the local database file.
func (s *OS) LocalDatabasePath() string {
	return filepath.Join(s.VarDir, "database", "local.db")
}

// LegacyLocalDatabasePath returns the path of legacy local database file.
func (s *OS) LegacyLocalDatabasePath() string {
	return filepath.Join(s.VarDir, "lxd.db")
}

// GlobalDatabaseDir returns the path of the global database directory.
func (s *OS) GlobalDatabaseDir() string {
	return filepath.Join(s.VarDir, "database", "global")
}

// GlobalDatabasePath returns the path of the global database SQLite file
// managed by dqlite.
func (s *OS) GlobalDatabasePath() string {
	return filepath.Join(s.GlobalDatabaseDir(), "db.bin")
}

// LegacyGlobalDatabasePath returns the path of legacy global database file.
func (s *OS) LegacyGlobalDatabasePath() string {
	return filepath.Join(s.VarDir, "raft", "db.bin")
}

// Make sure all our directories are available.
func (s *OS) initDirs() error {
	dirs := []struct {
		path string
		mode os.FileMode
	}{
		{s.VarDir, 0711},
		{filepath.Join(s.VarDir, "backups"), 0700},
		{s.CacheDir, 0700},
		// containers is 0711 because liblxc needs to traverse dir to get to each container.
		{filepath.Join(s.VarDir, "containers"), 0711},
		{filepath.Join(s.VarDir, "virtual-machines"), 0711},
		{filepath.Join(s.VarDir, "database"), 0700},
		{filepath.Join(s.VarDir, "devices"), 0711},
		{filepath.Join(s.VarDir, "devlxd"), 0755},
		{filepath.Join(s.VarDir, "disks"), 0700},
		{filepath.Join(s.VarDir, "images"), 0700},
		{s.LogDir, 0700},
		{filepath.Join(s.VarDir, "networks"), 0711},
		{filepath.Join(s.VarDir, "security"), 0700},
		{filepath.Join(s.VarDir, "security", "apparmor"), 0700},
		{filepath.Join(s.VarDir, "security", "apparmor", "cache"), 0700},
		{filepath.Join(s.VarDir, "security", "apparmor", "profiles"), 0700},
		{filepath.Join(s.VarDir, "security", "seccomp"), 0700},
		{filepath.Join(s.VarDir, "shmounts"), 0711},
		// snapshots is 0700 as liblxc does not need to access this.
		{filepath.Join(s.VarDir, "snapshots"), 0700},
		{filepath.Join(s.VarDir, "virtual-machines-snapshots"), 0700},
		{filepath.Join(s.VarDir, "storage-pools"), 0711},
	}

	for _, dir := range dirs {
		err := os.Mkdir(dir.path, dir.mode)
		if err != nil {
			if !os.IsExist(err) {
				return errors.Wrapf(err, "Failed to init dir %s", dir.path)
			}

			err = os.Chmod(dir.path, dir.mode)
			if err != nil && !os.IsNotExist(err) {
				return errors.Wrapf(err, "Failed to chmod dir %s", dir.path)
			}
		}
	}

	return nil
}
<commit_msg>lxd/sys/fs: Adds backups/custom and backups/instances to initDirs()<commit_after>// +build linux,cgo,!agent

package sys

import (
	"os"
	"path/filepath"

	"github.com/pkg/errors"
)

// LocalDatabasePath returns the path of the local database file.
func (s *OS) LocalDatabasePath() string {
	return filepath.Join(s.VarDir, "database", "local.db")
}

// LegacyLocalDatabasePath returns the path of legacy local database file.
func (s *OS) LegacyLocalDatabasePath() string {
	return filepath.Join(s.VarDir, "lxd.db")
}

// GlobalDatabaseDir returns the path of the global database directory.
func (s *OS) GlobalDatabaseDir() string {
	return filepath.Join(s.VarDir, "database", "global")
}

// GlobalDatabasePath returns the path of the global database SQLite file
// managed by dqlite.
func (s *OS) GlobalDatabasePath() string {
	return filepath.Join(s.GlobalDatabaseDir(), "db.bin")
}

// LegacyGlobalDatabasePath returns the path of legacy global database file.
func (s *OS) LegacyGlobalDatabasePath() string {
	return filepath.Join(s.VarDir, "raft", "db.bin")
}

// Make sure all our directories are available.
func (s *OS) initDirs() error {
	dirs := []struct {
		path string
		mode os.FileMode
	}{
		{s.VarDir, 0711},
		{filepath.Join(s.VarDir, "backups"), 0700},
		{filepath.Join(s.VarDir, "backups", "custom"), 0700},
		{filepath.Join(s.VarDir, "backups", "instances"), 0700},
		{s.CacheDir, 0700},
		// containers is 0711 because liblxc needs to traverse dir to get to each container.
		{filepath.Join(s.VarDir, "containers"), 0711},
		{filepath.Join(s.VarDir, "virtual-machines"), 0711},
		{filepath.Join(s.VarDir, "database"), 0700},
		{filepath.Join(s.VarDir, "devices"), 0711},
		{filepath.Join(s.VarDir, "devlxd"), 0755},
		{filepath.Join(s.VarDir, "disks"), 0700},
		{filepath.Join(s.VarDir, "images"), 0700},
		{s.LogDir, 0700},
		{filepath.Join(s.VarDir, "networks"), 0711},
		{filepath.Join(s.VarDir, "security"), 0700},
		{filepath.Join(s.VarDir, "security", "apparmor"), 0700},
		{filepath.Join(s.VarDir, "security", "apparmor", "cache"), 0700},
		{filepath.Join(s.VarDir, "security", "apparmor", "profiles"), 0700},
		{filepath.Join(s.VarDir, "security", "seccomp"), 0700},
		{filepath.Join(s.VarDir, "shmounts"), 0711},
		// snapshots is 0700 as liblxc does not need to access this.
		{filepath.Join(s.VarDir, "snapshots"), 0700},
		{filepath.Join(s.VarDir, "virtual-machines-snapshots"), 0700},
		{filepath.Join(s.VarDir, "storage-pools"), 0711},
	}

	for _, dir := range dirs {
		err := os.Mkdir(dir.path, dir.mode)
		if err != nil {
			if !os.IsExist(err) {
				return errors.Wrapf(err, "Failed to init dir %s", dir.path)
			}

			err = os.Chmod(dir.path, dir.mode)
			if err != nil && !os.IsNotExist(err) {
				return errors.Wrapf(err, "Failed to chmod dir %s", dir.path)
			}
		}
	}

	return nil
}
<|endoftext|>


<commit_before>package sys

import (
	"path/filepath"
	"sync"

	log "github.com/lxc/lxd/shared/log15"

	"github.com/lxc/lxd/lxd/util"
	"github.com/lxc/lxd/shared"
	"github.com/lxc/lxd/shared/idmap"
	"github.com/lxc/lxd/shared/logger"
)

// InotifyTargetInfo records the inotify information associated with a given
// inotify target
type InotifyTargetInfo struct {
	Mask uint32
	Wd   int
	Path string
}

// InotifyInfo records the inotify information associated with a given
// inotify instance
type InotifyInfo struct {
	Fd int
	sync.RWMutex
	Targets map[string]*InotifyTargetInfo
}

// OS is a high-level facade for accessing all operating-system
// level functionality that LXD uses.
type OS struct {
	VarDir   string // Data directory (e.g. /var/lib/lxd/).
	CacheDir string // Cache directory (e.g. /var/cache/lxd/).
	LogDir   string // Log directory (e.g. /var/log/lxd).

	// Caches of system characteristics detected at Init() time.
	Architectures           []int           // Cache of detected system architectures
	LxcPath                 string          // Path to the $LXD_DIR/containers directory
	BackingFS               string          // Backing filesystem of $LXD_DIR/containers
	IdmapSet                *idmap.IdmapSet // Information about user/group ID mapping
	ExecPath                string          // Absolute path to the LXD executable
	RunningInUserNS         bool
	AppArmorAvailable       bool
	AppArmorStacking        bool
	AppArmorStacked         bool
	AppArmorAdmin           bool
	AppArmorConfined        bool
	CGroupBlkioController   bool
	CGroupCPUController     bool
	CGroupCPUacctController bool
	CGroupCPUsetController  bool
	CGroupDevicesController bool
	CGroupFreezerController bool
	CGroupMemoryController  bool
	CGroupNetPrioController bool
	CGroupPidsController    bool
	CGroupSwapAccounting    bool
	InotifyWatch            InotifyInfo
	NetnsGetifaddrs         bool
	UeventInjection         bool
	SeccompListener         bool
	VFS3Fscaps              bool
	Shiftfs                 bool

	MockMode bool // If true some APIs will be mocked (for testing)
}

// DefaultOS returns a fresh uninitialized OS instance with default values.
func DefaultOS() *OS {
	newOS := &OS{
		VarDir:   shared.VarPath(),
		CacheDir: shared.CachePath(),
		LogDir:   shared.LogPath(),
	}
	newOS.InotifyWatch.Fd = -1
	newOS.InotifyWatch.Targets = make(map[string]*InotifyTargetInfo)
	return newOS
}

// Init our internal data structures.
func (s *OS) Init() error {
	err := s.initDirs()
	if err != nil {
		return err
	}

	s.Architectures, err = util.GetArchitectures()
	if err != nil {
		return err
	}

	s.LxcPath = filepath.Join(s.VarDir, "containers")

	s.BackingFS, err = util.FilesystemDetect(s.LxcPath)
	if err != nil {
		logger.Error("Error detecting backing fs", log.Ctx{"err": err})
	}

	s.IdmapSet = util.GetIdmapSet()
	s.ExecPath = util.GetExecPath()
	s.RunningInUserNS = shared.RunningInUserNS()

	s.initAppArmor()
	s.initCGroup()

	return nil
}
<commit_msg>lxd/sys: Cleanup State struct<commit_after>package sys

import (
	"path/filepath"
	"sync"

	log "github.com/lxc/lxd/shared/log15"

	"github.com/lxc/lxd/lxd/util"
	"github.com/lxc/lxd/shared"
	"github.com/lxc/lxd/shared/idmap"
	"github.com/lxc/lxd/shared/logger"
)

// InotifyTargetInfo records the inotify information associated with a given
// inotify target
type InotifyTargetInfo struct {
	Mask uint32
	Wd   int
	Path string
}

// InotifyInfo records the inotify information associated with a given
// inotify instance
type InotifyInfo struct {
	Fd int
	sync.RWMutex
	Targets map[string]*InotifyTargetInfo
}

// OS is a high-level facade for accessing all operating-system
// level functionality that LXD uses.
type OS struct {
	// Directories
	CacheDir string // Cache directory (e.g. /var/cache/lxd/).
	LogDir   string // Log directory (e.g. /var/log/lxd).
	VarDir   string // Data directory (e.g. /var/lib/lxd/).

	// Daemon environment
	Architectures   []int           // Cache of detected system architectures
	BackingFS       string          // Backing filesystem of $LXD_DIR/containers
	ExecPath        string          // Absolute path to the LXD executable
	IdmapSet        *idmap.IdmapSet // Information about user/group ID mapping
	InotifyWatch    InotifyInfo
	LxcPath         string // Path to the $LXD_DIR/containers directory
	MockMode        bool   // If true some APIs will be mocked (for testing)
	RunningInUserNS bool

	// Apparmor features
	AppArmorAdmin     bool
	AppArmorAvailable bool
	AppArmorConfined  bool
	AppArmorStacked   bool
	AppArmorStacking  bool

	// Cgroup features
	CGroupBlkioController   bool
	CGroupCPUacctController bool
	CGroupCPUController     bool
	CGroupCPUsetController  bool
	CGroupDevicesController bool
	CGroupFreezerController bool
	CGroupMemoryController  bool
	CGroupNetPrioController bool
	CGroupPidsController    bool
	CGroupSwapAccounting    bool

	// Kernel features
	NetnsGetifaddrs bool
	SeccompListener bool
	Shiftfs         bool
	UeventInjection bool
	VFS3Fscaps      bool
}

// DefaultOS returns a fresh uninitialized OS instance with default values.
func DefaultOS() *OS {
	newOS := &OS{
		VarDir:   shared.VarPath(),
		CacheDir: shared.CachePath(),
		LogDir:   shared.LogPath(),
	}
	newOS.InotifyWatch.Fd = -1
	newOS.InotifyWatch.Targets = make(map[string]*InotifyTargetInfo)
	return newOS
}

// Init our internal data structures.
func (s *OS) Init() error {
	err := s.initDirs()
	if err != nil {
		return err
	}

	s.Architectures, err = util.GetArchitectures()
	if err != nil {
		return err
	}

	s.LxcPath = filepath.Join(s.VarDir, "containers")

	s.BackingFS, err = util.FilesystemDetect(s.LxcPath)
	if err != nil {
		logger.Error("Error detecting backing fs", log.Ctx{"err": err})
	}

	s.IdmapSet = util.GetIdmapSet()
	s.ExecPath = util.GetExecPath()
	s.RunningInUserNS = shared.RunningInUserNS()

	s.initAppArmor()
	s.initCGroup()

	return nil
}
<|endoftext|>


<commit_before><commit_msg>redraw frame after scrollbar position changes in text editors<commit_after><|endoftext|>


<commit_before>// Copyright 2012 The AEGo 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 ds/appengine/datastore provides App Engine datastore persistence.

This package is a wrapper for the appengine/datastore package.
*/
package datastore

import (
	"appengine"
	"appengine/datastore"
	dserrors "github.com/gaego/ds/errors"
)

type Store struct{}

// New creates a new datastore.Store
func New() *Store {
	return &Store{}
}

// Count returns the total number of items in the store.
// this method is for testing ONLY.
func (s *Store) Count(c appengine.Context) int64 {
	q := datastore.NewQuery("X")
	cnt, _ := q.Count(c)
	return int64(cnt)
}

// PutMulti given a []*datastore.Key and a struct pointer adds multiple entities
// to the store".
func (s *Store) PutMulti(c appengine.Context, key []*datastore.Key, src interface{}) ([]*datastore.Key, error) {
	// TODO(kylefinley) Error codes should be converted to ds errors.
	return datastore.PutMulti(c, key, src)
}

// Put given a *datastore.Key and a struct pointer adds a single entity
// to the store.
func (s *Store) Put(c appengine.Context, key *datastore.Key, src interface{}) (*datastore.Key, error) {
	// TODO(kylefinley) Error codes should be converted to ds errors.
	return datastore.Put(c, key, src)
}

// GetMulti given a []*datastore.Key returns multiple entities from the store
func (s *Store) GetMulti(c appengine.Context, key []*datastore.Key, dst interface{}) error {
	// TODO(kylefinley) Error codes should be converted to ds errors.
	// This needs to be optimized.
	dserr := datastore.GetMulti(c, key, dst)
	if dserr != nil {
		err := make(appengine.MultiError, len(key))
		for i, e := range dserr.(appengine.MultiError) {
			if e != nil {
				if e == datastore.ErrNoSuchEntity {
					err[i] = dserrors.ErrNoSuchEntity
				} else {
					err[i] = e
				}
			}
		}
		return err
	}
	return nil
}

// Get given a *datastore.Key returns a single entity from the store
func (s *Store) Get(c appengine.Context, key *datastore.Key, dst interface{}) (err error) {
	err = datastore.Get(c, key, dst)
	if err == datastore.ErrNoSuchEntity {
		err = dserrors.ErrNoSuchEntity
	}
	return
}

// DeleteMulti given a []*datastore.Key deletes multiple entities from the store
func (s *Store) DeleteMulti(c appengine.Context, key []*datastore.Key) (err error) {
	// TODO(google) if the supplied key does not exist, Datastore should
	// return datastore.ErrNoSuchEntity instead of panicing.
	// TODO(kylefinley) figure out a way to catch the panic here.
	defer func() {
		if r := recover(); r != nil {
			err = dserrors.ErrNoSuchEntity
			return
		}
	}()
	err = datastore.DeleteMulti(c, key)
	return
}

// Delete given a *datastore.Key deletes a single entity from the store
func (s *Store) Delete(c appengine.Context, key *datastore.Key) (err error) {
	// TODO(google) if the supplied key does not exist, Datastore should
	// return datastore.ErrNoSuchEntity instead of panicing.
	// TODO(kylefinley) figure out a way to catch the panic here.
	defer func() {
		if r := recover(); r != nil {
			err = dserrors.ErrNoSuchEntity
			return
		}
	}()
	err = datastore.Delete(c, key)
	return
}
<commit_msg>Fixed a type assertion which caused a panic<commit_after>// Copyright 2012 The AEGo 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 ds/appengine/datastore provides App Engine datastore persistence.

This package is a wrapper for the appengine/datastore package.
*/
package datastore

import (
	"appengine"
	"appengine/datastore"
	dserrors "github.com/gaego/ds/errors"
)

type Store struct{}

// New creates a new datastore.Store
func New() *Store {
	return &Store{}
}

// Count returns the total number of items in the store.
// this method is for testing ONLY.
func (s *Store) Count(c appengine.Context) int64 {
	q := datastore.NewQuery("X")
	cnt, _ := q.Count(c)
	return int64(cnt)
}

// PutMulti given a []*datastore.Key and a struct pointer adds multiple entities
// to the store".
func (s *Store) PutMulti(c appengine.Context, key []*datastore.Key, src interface{}) ([]*datastore.Key, error) {
	// TODO(kylefinley) Error codes should be converted to ds errors.
	return datastore.PutMulti(c, key, src)
}

// Put given a *datastore.Key and a struct pointer adds a single entity
// to the store.
func (s *Store) Put(c appengine.Context, key *datastore.Key, src interface{}) (*datastore.Key, error) {
	// TODO(kylefinley) Error codes should be converted to ds errors.
	return datastore.Put(c, key, src)
}

func convertError(err error) error {
	if err == datastore.ErrNoSuchEntity {
		return dserrors.ErrNoSuchEntity
	}
	return err
}

// GetMulti given a []*datastore.Key returns multiple entities from the store
func (s *Store) GetMulti(c appengine.Context, key []*datastore.Key, dst interface{}) error {
	// TODO(kylefinley) Error codes should be converted to ds errors.
	// This needs to be optimized.
	dserr := datastore.GetMulti(c, key, dst)
	if dserr != nil {
		if newDserr, ok := dserr.(appengine.MultiError); ok {
			err := make(appengine.MultiError, len(key))
			for i, e := range dserr.(appengine.MultiError) {
				if e != nil {
					err[i] = convertError(err)
				}
			}
			return err
		} else {
			return convertError(dserr)
		}
	}
	return nil
}

// Get given a *datastore.Key returns a single entity from the store
func (s *Store) Get(c appengine.Context, key *datastore.Key, dst interface{}) error {
	err = datastore.Get(c, key, dst)
	return convertError(err)
}

// DeleteMulti given a []*datastore.Key deletes multiple entities from the store
func (s *Store) DeleteMulti(c appengine.Context, key []*datastore.Key) (err error) {
	// TODO(google) if the supplied key does not exist, Datastore should
	// return datastore.ErrNoSuchEntity instead of panicing.
	// TODO(kylefinley) figure out a way to catch the panic here.
	defer func() {
		if r := recover(); r != nil {
			err = dserrors.ErrNoSuchEntity
			return
		}
	}()
	err = datastore.DeleteMulti(c, key)
	return
}

// Delete given a *datastore.Key deletes a single entity from the store
func (s *Store) Delete(c appengine.Context, key *datastore.Key) (err error) {
	// TODO(google) if the supplied key does not exist, Datastore should
	// return datastore.ErrNoSuchEntity instead of panicing.
	// TODO(kylefinley) figure out a way to catch the panic here.
	defer func() {
		if r := recover(); r != nil {
			err = dserrors.ErrNoSuchEntity
			return
		}
	}()
	err = datastore.Delete(c, key)
	return
}
<|endoftext|>


<commit_before>package u

import (
	"archive/zip"
	"crypto/sha1"
	"fmt"
	"io"
	"os"
	"path/filepath"
	"time"
)

// treats any error (e.g. lack of access due to permissions) as non-existence
func PathExists(path string) bool {
	_, err := os.Stat(path)
	return err == nil
}

// Returns true, nil if a path exists and is a directory
// Returns false, nil if a path exists and is not a directory (e.g. a file)
// Returns undefined, error if there was an error e.g. because a path doesn't exists
func PathIsDir(path string) (isDir bool, err error) {
	fi, err := os.Stat(path)
	if err != nil {
		return false, err
	}
	return fi.IsDir(), nil
}

func FileSha1(path string) (string, error) {
	f, err := os.Open(path)
	if err != nil {
		//fmt.Printf("os.Open(%s) failed with %s\n", path, err.Error())
		return "", err
	}
	defer f.Close()
	h := sha1.New()
	_, err = io.Copy(h, f)
	if err != nil {
		//fmt.Printf("io.Copy() failed with %s\n", err.Error())
		return "", err
	}
	return fmt.Sprintf("%x", h.Sum(nil)), nil
}

func DataSha1(data []byte) (string, error) {
	h := sha1.New()
	_, err := h.Write(data)
	if err != nil {
		return "", err
	}
	return fmt.Sprintf("%x", h.Sum(nil)), nil
}

func TimeSinceNowAsString(t time.Time) string {
	d := time.Now().Sub(t)
	minutes := int(d.Minutes()) % 60
	hours := int(d.Hours())
	days := hours / 24
	hours = hours % 24
	if days > 0 {
		return fmt.Sprintf("%dd %dhr", days, hours)
	}
	if hours > 0 {
		return fmt.Sprintf("%dhr %dm", hours, minutes)
	}
	return fmt.Sprintf("%dm", minutes)
}

// the names of files inside the zip file are relatitve to dirToZip e.g.
// if dirToZip is foo and there is a file foo/bar.txt, the name in the zip
// will be bar.txt
func CreateZipWithDirContent(zipFilePath, dirToZip string) error {
	if isDir, err := PathIsDir(dirToZip); err != nil || !isDir {
		// TODO: should return an error if err == nil && !isDir
		return err
	}
	zf, err := os.Create(zipFilePath)
	if err != nil {
		//fmt.Printf("Failed to os.Create() %s, %s\n", zipFilePath, err.Error())
		return err
	}
	defer zf.Close()
	zipWriter := zip.NewWriter(zf)
	// TODO: is the order of defer here can create problems?
	// TODO: need to check error code returned by Close()
	defer zipWriter.Close()

	//fmt.Printf("Walk root: %s\n", config.LocalDir)
	err = filepath.Walk(dirToZip, func(pathToZip string, info os.FileInfo, err error) error {
		if err != nil {
			//fmt.Printf("WalkFunc() received err %s from filepath.Wath()\n", err.Error())
			return err
		}
		//fmt.Printf("%s\n", path)
		isDir, err := PathIsDir(pathToZip)
		if err != nil {
			//fmt.Printf("PathIsDir() for %s failed with %s\n", pathToZip, err.Error())
			return err
		}
		if isDir {
			return nil
		}
		toZipReader, err := os.Open(pathToZip)
		if err != nil {
			//fmt.Printf("os.Open() %s failed with %s\n", pathToZip, err.Error())
			return err
		}
		defer toZipReader.Close()

		zipName := pathToZip[len(dirToZip)+1:] // +1 for '/' in the path
		inZipWriter, err := zipWriter.Create(zipName)
		if err != nil {
			//fmt.Printf("Error in zipWriter(): %s\n", err.Error())
			return err
		}
		_, err = io.Copy(inZipWriter, toZipReader)
		if err != nil {
			return err
		}
		//fmt.Printf("Added %s to zip file\n", pathToZip)
		return nil
	})
	return nil
}
<commit_msg>add more common functions<commit_after>package u

import (
	"archive/zip"
	"crypto/sha1"
	"fmt"
	"io"
	"os"
	"path/filepath"
	"time"
)

// treats any error (e.g. lack of access due to permissions) as non-existence
func PathExists(path string) bool {
	_, err := os.Stat(path)
	return err == nil
}

// Returns true, nil if a path exists and is a directory
// Returns false, nil if a path exists and is not a directory (e.g. a file)
// Returns undefined, error if there was an error e.g. because a path doesn't exists
func PathIsDir(path string) (isDir bool, err error) {
	fi, err := os.Stat(path)
	if err != nil {
		return false, err
	}
	return fi.IsDir(), nil
}

func CreateDirIfNotExists(path string) error {
	if !PathExists(path) {
		return os.MkdirAll(path, 0777)
	}
	return nil
}

func FileSha1(path string) (string, error) {
	f, err := os.Open(path)
	if err != nil {
		//fmt.Printf("os.Open(%s) failed with %s\n", path, err.Error())
		return "", err
	}
	defer f.Close()
	h := sha1.New()
	_, err = io.Copy(h, f)
	if err != nil {
		//fmt.Printf("io.Copy() failed with %s\n", err.Error())
		return "", err
	}
	return fmt.Sprintf("%x", h.Sum(nil)), nil
}

func Sha1StringOfBytes(data []byte) string {
	return fmt.Sprintf("%x", Sha1OfBytes(data))
}

func Sha1OfBytes(data []byte) []byte {
	h := sha1.New()
	h.Write(data)
	return h.Sum(nil)
}

// TODO: remove in favor of Sha1OfBytes
func DataSha1(data []byte) (string, error) {
	return Sha1StringOfBytes(data), nil
}

func TimeSinceNowAsString(t time.Time) string {
	d := time.Now().Sub(t)
	minutes := int(d.Minutes()) % 60
	hours := int(d.Hours())
	days := hours / 24
	hours = hours % 24
	if days > 0 {
		return fmt.Sprintf("%dd %dhr", days, hours)
	}
	if hours > 0 {
		return fmt.Sprintf("%dhr %dm", hours, minutes)
	}
	return fmt.Sprintf("%dm", minutes)
}

func CopyFile(dst, src string) error {
	fsrc, err := os.Open(src)
	if err != nil {
		return err
	}
	defer fsrc.Close()
	fdst, err := os.Create(dst)
	if err != nil {
		return err
	}
	defer fdst.Close()
	if _, err = io.Copy(fdst, fsrc); err != nil {
		return err
	}
	return nil
}

// the names of files inside the zip file are relatitve to dirToZip e.g.
// if dirToZip is foo and there is a file foo/bar.txt, the name in the zip
// will be bar.txt
func CreateZipWithDirContent(zipFilePath, dirToZip string) error {
	if isDir, err := PathIsDir(dirToZip); err != nil || !isDir {
		// TODO: should return an error if err == nil && !isDir
		return err
	}
	zf, err := os.Create(zipFilePath)
	if err != nil {
		//fmt.Printf("Failed to os.Create() %s, %s\n", zipFilePath, err.Error())
		return err
	}
	defer zf.Close()
	zipWriter := zip.NewWriter(zf)
	// TODO: is the order of defer here can create problems?
	// TODO: need to check error code returned by Close()
	defer zipWriter.Close()

	//fmt.Printf("Walk root: %s\n", config.LocalDir)
	err = filepath.Walk(dirToZip, func(pathToZip string, info os.FileInfo, err error) error {
		if err != nil {
			//fmt.Printf("WalkFunc() received err %s from filepath.Wath()\n", err.Error())
			return err
		}
		//fmt.Printf("%s\n", path)
		isDir, err := PathIsDir(pathToZip)
		if err != nil {
			//fmt.Printf("PathIsDir() for %s failed with %s\n", pathToZip, err.Error())
			return err
		}
		if isDir {
			return nil
		}
		toZipReader, err := os.Open(pathToZip)
		if err != nil {
			//fmt.Printf("os.Open() %s failed with %s\n", pathToZip, err.Error())
			return err
		}
		defer toZipReader.Close()

		zipName := pathToZip[len(dirToZip)+1:] // +1 for '/' in the path
		inZipWriter, err := zipWriter.Create(zipName)
		if err != nil {
			//fmt.Printf("Error in zipWriter(): %s\n", err.Error())
			return err
		}
		_, err = io.Copy(inZipWriter, toZipReader)
		if err != nil {
			return err
		}
		//fmt.Printf("Added %s to zip file\n", pathToZip)
		return nil
	})
	return nil
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"log"
	"net"
	"net/http"
	"os"
	"path"

	"github.com/joushou/serve2"
	"github.com/joushou/serve2/proto"
)

var (
	server    *serve2.Server
	conf      Config
	confReady bool
	logger    func(string, ...interface{})
)

// Config is the top-level config
type Config struct {
	Address   string
	LogStdout bool   `json:"logStdout,omitempty"`
	LogFile   string `json:"logFile,omitempty"`
	MaxRead   int    `json:"maxRead,omitempty"`
	Protocols []Protocol
}

// Protocol is the part of config defining individual protocols
type Protocol struct {
	Kind      string
	AsDefault bool                   `json:"default,omitempty"`
	Conf      map[string]interface{} `json:"conf,omitempty"`
}

func logit(format string, msg ...interface{}) {
	defer func() {
		if r := recover(); r != nil {
			println("Log failed: ", r)
			panic(r)
		}
	}()

	if logger != nil || !confReady {
		log.Printf(format, msg...)
	}
}

type httpHandler struct {
	path, defaultFile, notFoundMsg string
}

func (h httpHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	if r.Method == "OPTIONS" {
		return
	}

	// We add the "./" to make things relative
	p := "." + path.Clean(r.URL.Path)

	if p == "./" {
		p += h.defaultFile
	}
	// We then put the origin on there
	p = path.Join(h.path, p)

	content, err := ioutil.ReadFile(p)
	if err != nil {
		logit("http could not read file %s: %v", p, err)
		w.WriteHeader(404)
		fmt.Fprintf(w, "%s", h.notFoundMsg)
		return
	}

	fmt.Fprintf(w, "%s", content)
}

func main() {
	defer func() {
		if err := recover(); err != nil {
			logit("Panicked: %s", err)
		}
	}()

	if len(os.Args) <= 1 {
		panic("Missing configuration path")
	}

	path := os.Args[1]

	bytes, err := ioutil.ReadFile(path)
	if err != nil {
		logit("Reading configuration failed")
		panic(err)
	}

	err = json.Unmarshal(bytes, &conf)
	if err != nil {
		logit("Parsing configuration failed")
		panic(err)
	}

	confReady = true

	server = serve2.New()

	if conf.LogStdout && conf.LogFile != "" {
		panic("Unable to both log to stdout and to logfile")
	}

	if conf.LogStdout || conf.LogFile != "" {
		if conf.LogFile != "" {
			file, err := os.Create(conf.LogFile)
			if err != nil {
				logit("Failed to open logfile: %s", conf.LogFile)
				panic(err)
			}
			log.SetOutput(file)
		}

		logger = log.Printf
		server.Logger = log.Printf
	}

	if conf.MaxRead != 0 {
		server.BytesToCheck = conf.MaxRead
	}

	logit("Maximum buffer size: %d", server.BytesToCheck)

	l, err := net.Listen("tcp", conf.Address)
	if err != nil {
		logit("Listen on [%s] failed", conf.Address)
		panic(err)
	}

	logit("Listening on: %s", conf.Address)

	for _, v := range conf.Protocols {
		var (
			handler serve2.ProtocolHandler
			err     error
		)
		switch v.Kind {
		case "proxy":
			magic, mok := v.Conf["magic"].(string)
			magicSlice, sok := v.Conf["magic"].([]interface{})
			if !mok && !sok {
				panic("Proxy declaration is missing valid magic")
			}

			target, ok := v.Conf["target"].(string)
			if !ok {
				panic("Proxy declaration is missing valid target")
			}

			if mok {
				handler = proto.NewProxy([]byte(magic), "tcp", target)
			} else {
				magics := make([][]byte, len(magicSlice))
				for i := range magicSlice {
					magic, ok := magicSlice[i].(string)
					if !ok {
						panic("magic declaration invalid")
					}
					magics[i] = []byte(magic)
				}
				handler = proto.NewMultiProxy(magics, "tcp", target)
			}
		case "tls":
			cert, ok := v.Conf["cert"].(string)
			if !ok {
				panic("TLS declaration is missing valid certificate")
			}

			key, ok := v.Conf["key"].(string)
			if !ok {
				panic("TLS declaration is missing valid key")
			}

			var protos []string
			y, ok := v.Conf["protos"].([]interface{})
			if !ok {
				panic("TLS protos declaration invalid")
			}

			for _, x := range y {
				proto, ok := x.(string)
				if !ok {
					panic("TLS protos declaration invalid")
				}
				protos = append(protos, proto)
			}

			handler, err = proto.NewTLS(protos, cert, key)
			if err != nil {
				logit("TLS configuration failed")
				panic(err)
			}
		case "http":
			h := httpHandler{}
			msg, msgOk := v.Conf["notFoundMsg"]
			filename, fileOk := v.Conf["notFoundFile"]
			if fileOk && msgOk {
				panic("HTTP notFoundMsg and notFoundFile declared simultaneously")
			}

			if !msgOk && !fileOk {
				h.notFoundMsg = "<!DOCTYPE html>
404
"
			} else if msgOk {
				h.notFoundMsg, msgOk = msg.(string)
				if !msgOk {
					panic("HTTP notFoundMsg declaration invalid")
				}
			} else if fileOk {
				f, ok := filename.(string)
				if !ok {
					panic("HTTP notFoundFile declaration invalid")
				}

				x, err := ioutil.ReadFile(f)
				if err != nil {
					logit("HTTP unable to open notFoundFile")
					panic(err)
				}
				h.notFoundMsg = string(x)
			}

			c, ok := v.Conf["defaultFile"]
			if !ok {
				h.defaultFile = "index.html"
			} else {
				h.defaultFile, ok = c.(string)
				if !ok {
					panic("HTTP defaultFile declaration invalid")
				}
			}

			h.path, ok = v.Conf["path"].(string)
			if !ok {
				panic("HTTP path declaration invalid")
			}

			handler = proto.NewHTTP(h)
		case "echo":
			handler = proto.NewEcho()
		case "discard":
			handler = proto.NewDiscard()
		default:
			panic("Unknown kind: " + v.Kind)
		}

		if v.AsDefault {
			server.DefaultProtocol = handler
		} else {
			server.AddHandler(handler)
		}
	}

	if server.DefaultProtocol != nil {
		logit("Default protocol set to: %v", server.DefaultProtocol)
	}

	server.Serve(l)
}
<commit_msg>Experiments with tlsmatcher<commit_after>package main

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"log"
	"net"
	"net/http"
	"os"
	"path"

	"github.com/joushou/serve2"
	"github.com/joushou/serve2/proto"
	"github.com/joushou/serve2/utils"
)

var (
	server    *serve2.Server
	conf      Config
	confReady bool
	logger    func(string, ...interface{})
)

// Config is the top-level config
type Config struct {
	Address   string
	LogStdout bool   `json:"logStdout,omitempty"`
	LogFile   string `json:"logFile,omitempty"`
	MaxRead   int    `json:"maxRead,omitempty"`
	Protocols []Protocol
}

// Protocol is the part of config defining individual protocols
type Protocol struct {
	Kind      string
	AsDefault bool                   `json:"default,omitempty"`
	Conf      map[string]interface{} `json:"conf,omitempty"`
}

func logit(format string, msg ...interface{}) {
	defer func() {
		if r := recover(); r != nil {
			println("Log failed: ", r)
			panic(r)
		}
	}()

	if logger != nil || !confReady {
		log.Printf(format, msg...)
	}
}

type httpHandler struct {
	path, defaultFile, notFoundMsg string
}

func (h httpHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	if r.Method == "OPTIONS" {
		return
	}

	// We add the "./" to make things relative
	p := "." + path.Clean(r.URL.Path)

	if p == "./" {
		p += h.defaultFile
	}
	// We then put the origin on there
	p = path.Join(h.path, p)

	content, err := ioutil.ReadFile(p)
	if err != nil {
		logit("http could not read file %s: %v", p, err)
		w.WriteHeader(404)
		fmt.Fprintf(w, "%s", h.notFoundMsg)
		return
	}

	fmt.Fprintf(w, "%s", content)
}

func main() {
	defer func() {
		if err := recover(); err != nil {
			logit("Panicked: %s", err)
		}
	}()

	if len(os.Args) <= 1 {
		panic("Missing configuration path")
	}

	path := os.Args[1]

	bytes, err := ioutil.ReadFile(path)
	if err != nil {
		logit("Reading configuration failed")
		panic(err)
	}

	err = json.Unmarshal(bytes, &conf)
	if err != nil {
		logit("Parsing configuration failed")
		panic(err)
	}

	confReady = true

	server = serve2.New()

	if conf.LogStdout && conf.LogFile != "" {
		panic("Unable to both log to stdout and to logfile")
	}

	if conf.LogStdout || conf.LogFile != "" {
		if conf.LogFile != "" {
			file, err := os.Create(conf.LogFile)
			if err != nil {
				logit("Failed to open logfile: %s", conf.LogFile)
				panic(err)
			}
			log.SetOutput(file)
		}

		logger = log.Printf
		server.Logger = log.Printf
	}

	if conf.MaxRead != 0 {
		server.BytesToCheck = conf.MaxRead
	}

	logit("Maximum buffer size: %d", server.BytesToCheck)

	l, err := net.Listen("tcp", conf.Address)
	if err != nil {
		logit("Listen on [%s] failed", conf.Address)
		panic(err)
	}

	logit("Listening on: %s", conf.Address)

	for _, v := range conf.Protocols {
		var (
			handler serve2.ProtocolHandler
			err     error
		)
		switch v.Kind {
		case "proxy":
			magic, mok := v.Conf["magic"].(string)
			magicSlice, sok := v.Conf["magic"].([]interface{})
			if !mok && !sok {
				panic("Proxy declaration is missing valid magic")
			}

			target, ok := v.Conf["target"].(string)
			if !ok {
				panic("Proxy declaration is missing valid target")
			}

			if mok {
				handler = proto.NewProxy([]byte(magic), "tcp", target)
			} else {
				magics := make([][]byte, len(magicSlice))
				for i := range magicSlice {
					magic, ok := magicSlice[i].(string)
					if !ok {
						panic("magic declaration invalid")
					}
					magics[i] = []byte(magic)
				}
				handler = proto.NewMultiProxy(magics, "tcp", target)
			}
		case "tls":
			cert, ok := v.Conf["cert"].(string)
			if !ok {
				panic("TLS declaration is missing valid certificate")
			}

			key, ok := v.Conf["key"].(string)
			if !ok {
				panic("TLS declaration is missing valid key")
			}

			var protos []string
			y, ok := v.Conf["protos"].([]interface{})
			if !ok {
				panic("TLS protos declaration invalid")
			}

			for _, x := range y {
				proto, ok := x.(string)
				if !ok {
					panic("TLS protos declaration invalid")
				}
				protos = append(protos, proto)
			}

			handler, err = proto.NewTLS(protos, cert, key)
			if err != nil {
				logit("TLS configuration failed")
				panic(err)
			}
		case "tlsmatcher":
			target, ok := v.Conf["target"].(string)
			if !ok {
				panic("TLSMatcher declaration is missing valid target")
			}

			cb := func(c net.Conn) (net.Conn, error) {
				return nil, utils.DialAndProxy(c, "tcp", target)
			}

			t := proto.NewTLSMatcher(cb)

			var checks proto.TLSMatcherChecks
			if sn, ok := v.Conf["serverName"].(string); ok {
				checks |= proto.TLSCheckServerName
				t.ServerName = sn
			}

			if np, ok := v.Conf["negotiatedProtocol"].(string); ok {
				checks |= proto.TLSCheckNegotiatedProtocol
				t.NegotiatedProtocol = np
			}

			if npm, ok := v.Conf["negotiatedProtocolIsMutual"].(bool); ok {
				checks |= proto.TLSCheckNegotiatedProtocolIsMutual
				t.NegotiatedProtocolIsMutual = npm
			}

			t.Checks = checks
			t.Description = fmt.Sprintf("TLSMatcher [dest: %s]", target)
			handler = t
		case "http":
			h := httpHandler{}
			msg, msgOk := v.Conf["notFoundMsg"]
			filename, fileOk := v.Conf["notFoundFile"]
			if fileOk && msgOk {
				panic("HTTP notFoundMsg and notFoundFile declared simultaneously")
			}

			if !msgOk && !fileOk {
				h.notFoundMsg = "<!DOCTYPE html>
404
"
			} else if msgOk {
				h.notFoundMsg, msgOk = msg.(string)
				if !msgOk {
					panic("HTTP notFoundMsg declaration invalid")
				}
			} else if fileOk {
				f, ok := filename.(string)
				if !ok {
					panic("HTTP notFoundFile declaration invalid")
				}

				x, err := ioutil.ReadFile(f)
				if err != nil {
					logit("HTTP unable to open notFoundFile")
					panic(err)
				}
				h.notFoundMsg = string(x)
			}

			c, ok := v.Conf["defaultFile"]
			if !ok {
				h.defaultFile = "index.html"
			} else {
				h.defaultFile, ok = c.(string)
				if !ok {
					panic("HTTP defaultFile declaration invalid")
				}
			}

			h.path, ok = v.Conf["path"].(string)
			if !ok {
				panic("HTTP path declaration invalid")
			}

			handler = proto.NewHTTP(h)
		case "echo":
			handler = proto.NewEcho()
		case "discard":
			handler = proto.NewDiscard()
		default:
			panic("Unknown kind: " + v.Kind)
		}

		if v.AsDefault {
			server.DefaultProtocol = handler
		} else {
			server.AddHandler(handler)
		}
	}

	if server.DefaultProtocol != nil {
		logit("Default protocol set to: %v", server.DefaultProtocol)
	}

	server.Serve(l)
}
<|endoftext|>


<commit_before>package navitia

import (
	"github.com/pkg/errors"
	"net/http"
	"time"
)

const (
	// DefaultAPIProtocol is the protocol to be used
	DefaultAPIProtocol = "https"

	// DefaultAPIHostname is the known Navitia API hostname
	DefaultAPIHostname = "api.navitia.io"

	// DefaultAPIVersion is the used API Version
	DefaultAPIVersion = "v1"

	defaultAPIURL = DefaultAPIProtocol + "://" + DefaultAPIHostname + "/" + DefaultAPIVersion
)

var defaultClient = &http.Client{}

// Session holds a current session, it is thread-safe
type Session struct {
	APIKey string
	APIURL string

	client  *http.Client
	created time.Time
}

// New creates a new session given an API Key
// It acts as a convenience wrapper to NewCustom
func New(key string) (*Session, error) {
	return NewCustom(key, defaultAPIURL, defaultClient)
}

// NewCustom creates a custom new session given an API key, URL to api base & http client
func NewCustom(key string, url string, client *http.Client) (*Session, error) {
	return &Session{
		APIKey:  key,
		APIURL:  url,
		created: time.Now(),
		client:  client,
	}, nil
}

// UseClient sets a given *http.Client to be used for further queries
func (s *Session) UseClient(client *http.Client) {
	s.client = client
}

// newRequest creates a newRequest with the correct auth set
func (s *Session) newRequest(url string) (*http.Request, error) {
	// Create the request
	newReq, err := http.NewRequest("GET", url, nil)
	if err != nil {
		return newReq, errors.Wrapf(err, "couldn't create new request (for %s)", url)
	}

	// Add basic auth
	newReq.SetBasicAuth(s.APIKey, "")

	return newReq, err
}
<commit_msg>API-Breaking: remove (*Session).UseClient, it's useless<commit_after>package navitia

import (
	"github.com/pkg/errors"
	"net/http"
	"time"
)

const (
	// DefaultAPIProtocol is the protocol to be used
	DefaultAPIProtocol = "https"

	// DefaultAPIHostname is the known Navitia API hostname
	DefaultAPIHostname = "api.navitia.io"

	// DefaultAPIVersion is the used API Version
	DefaultAPIVersion = "v1"

	defaultAPIURL = DefaultAPIProtocol + "://" + DefaultAPIHostname + "/" + DefaultAPIVersion
)

var defaultClient = &http.Client{}

// Session holds a current session, it is thread-safe
type Session struct {
	APIKey string
	APIURL string

	client  *http.Client
	created time.Time
}

// New creates a new session given an API Key
// It acts as a convenience wrapper to NewCustom
func New(key string) (*Session, error) {
	return NewCustom(key, defaultAPIURL, defaultClient)
}

// NewCustom creates a custom new session given an API key, URL to api base & http client
func NewCustom(key string, url string, client *http.Client) (*Session, error) {
	return &Session{
		APIKey:  key,
		APIURL:  url,
		created: time.Now(),
		client:  client,
	}, nil
}

// newRequest creates a newRequest with the correct auth set
func (s *Session) newRequest(url string) (*http.Request, error) {
	// Create the request
	newReq, err := http.NewRequest("GET", url, nil)
	if err != nil {
		return newReq, errors.Wrapf(err, "couldn't create new request (for %s)", url)
	}

	// Add basic auth
	newReq.SetBasicAuth(s.APIKey, "")

	return newReq, err
}
<|endoftext|>


<commit_before>// +build !nacl,!plan9,!windows

package exec

import (
	"testing"
	"time"

	"github.com/cybozu-go/goma"
)

func TestConstruct(t *testing.T) {
	t.Parallel()

	_, err := construct(nil)
	if err != goma.ErrNoKey {
		t.Error(`err != goma.ErrNoKey`)
	}

	a, err := construct(map[string]interface{}{
		"command": "sh",
		"args": []interface{}{"-u", "-c", `
echo GOMA_MONITOR=$GOMA_MONITOR
echo GOMA_VERSION=$GOMA_VERSION
if [ "$GOMA_EVENT" != "init" ]; then exit 1; fi
`},
	})
	if err != nil {
		t.Fatal(err)
	}

	if err := a.Init("monitor1"); err != nil {
		t.Error(err)
	}
}

func TestFail(t *testing.T) {
	t.Parallel()

	a, err := construct(map[string]interface{}{
		"command": "sh",
		"args": []interface{}{"-u", "-c", `
echo GOMA_VALUE=$GOMA_VALUE
if [ "$GOMA_VALUE" != "0.1" ]; then exit 1; fi
`},
	})
	if err != nil {
		t.Fatal(err)
	}

	if err := a.Fail("monitor1", 0.1); err != nil {
		t.Error(err)
	}
}

func TestRecover(t *testing.T) {
	t.Parallel()

	a, err := construct(map[string]interface{}{
		"command": "sh",
		"args": []interface{}{"-u", "-c", `
echo GOMA_DURATION=$GOMA_DURATION
if [ "$GOMA_DURATION" != "39" ]; then exit 1; fi
`},
	})
	if err != nil {
		t.Fatal(err)
	}

	if err := a.Recover("monitor1", 39280*time.Millisecond); err != nil {
		t.Error(err)
	}
}

func TestEnv(t *testing.T) {
	t.Parallel()

	a, err := construct(map[string]interface{}{
		"command": "sh",
		"args": []interface{}{"-u", "-c", `
echo TEST_ENV1=$TEST_ENV1
if [ "$TEST_ENV1" != "test1" ]; then exit 1; fi
`},
		"env": []interface{}{"TEST_ENV1=test1"},
	})
	if err != nil {
		t.Fatal(err)
	}

	if err := a.Fail("monitor1", 0); err != nil {
		t.Error(err)
	}
}

func TestTimeout(t *testing.T) {
	t.Parallel()

	a, err := construct(map[string]interface{}{
		"command": "sleep",
		"args":    []interface{}{"10"},
		"timeout": 1,
	})
	if err != nil {
		t.Fatal(err)
	}

	if err := a.Init("monitor1"); err == nil {
		t.Error("err must not be nil")
	}
}
<commit_msg>[actions/exec] add tests.<commit_after>// +build !nacl,!plan9,!windows

package exec

import (
	"testing"
	"time"

	"github.com/cybozu-go/goma"
)

func TestConstruct(t *testing.T) {
	t.Parallel()

	_, err := construct(nil)
	if err != goma.ErrNoKey {
		t.Error(`err != goma.ErrNoKey`)
	}

	a, err := construct(map[string]interface{}{
		"command": "sh",
		"args": []interface{}{"-u", "-c", `
echo GOMA_MONITOR=$GOMA_MONITOR
echo GOMA_VERSION=$GOMA_VERSION
if [ "$GOMA_EVENT" != "init" ]; then exit 1; fi
`},
	})
	if err != nil {
		t.Fatal(err)
	}

	if err := a.Init("monitor1"); err != nil {
		t.Error(err)
	}
}

func TestFail(t *testing.T) {
	t.Parallel()

	a, err := construct(map[string]interface{}{
		"command": "sh",
		"args": []interface{}{"-u", "-c", `
echo GOMA_VALUE=$GOMA_VALUE
if [ "$GOMA_EVENT" != "fail" ]; then exit 1; fi
if [ "$GOMA_VALUE" != "0.1" ]; then exit 1; fi
`},
	})
	if err != nil {
		t.Fatal(err)
	}

	if err := a.Fail("monitor1", 0.1); err != nil {
		t.Error(err)
	}
}

func TestRecover(t *testing.T) {
	t.Parallel()

	a, err := construct(map[string]interface{}{
		"command": "sh",
		"args": []interface{}{"-u", "-c", `
echo GOMA_DURATION=$GOMA_DURATION
if [ "$GOMA_EVENT" != "recover" ]; then exit 1; fi
if [ "$GOMA_DURATION" != "39" ]; then exit 1; fi
`},
	})
	if err != nil {
		t.Fatal(err)
	}

	if err := a.Recover("monitor1", 39280*time.Millisecond); err != nil {
		t.Error(err)
	}
}

func TestEnv(t *testing.T) {
	t.Parallel()

	a, err := construct(map[string]interface{}{
		"command": "sh",
		"args": []interface{}{"-u", "-c", `
echo TEST_ENV1=$TEST_ENV1
if [ "$TEST_ENV1" != "test1" ]; then exit 1; fi
`},
		"env": []interface{}{"TEST_ENV1=test1"},
	})
	if err != nil {
		t.Fatal(err)
	}

	if err := a.Fail("monitor1", 0); err != nil {
		t.Error(err)
	}
}

func TestTimeout(t *testing.T) {
	t.Parallel()

	a, err := construct(map[string]interface{}{
		"command": "sleep",
		"args":    []interface{}{"10"},
		"timeout": 1,
	})
	if err != nil {
		t.Fatal(err)
	}

	if err := a.Init("monitor1"); err == nil {
		t.Error("err must not be nil")
	}
}
<|endoftext|>


<commit_before>// Copyright 2014-2015 Amazon.com, Inc. or its affiliates. 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. A copy of the
// License is located at
//
//	http://aws.amazon.com/apache2.0/
//
// or in the "license" file accompanying this file. This file 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 updater

import (
	"crypto/sha256"
	"errors"
	"fmt"
	"io"
	"net/http"
	"path/filepath"
	"strings"
	"sync"
	"time"

	"github.com/aws/amazon-ecs-agent/agent/acs/model/ecsacs"
	"github.com/aws/amazon-ecs-agent/agent/acs/update_handler/os"
	"github.com/aws/amazon-ecs-agent/agent/config"
	"github.com/aws/amazon-ecs-agent/agent/engine"
	"github.com/aws/amazon-ecs-agent/agent/httpclient"
	"github.com/aws/amazon-ecs-agent/agent/logger"
	"github.com/aws/amazon-ecs-agent/agent/sighandlers"
	"github.com/aws/amazon-ecs-agent/agent/sighandlers/exitcodes"
	"github.com/aws/amazon-ecs-agent/agent/statemanager"
	"github.com/aws/amazon-ecs-agent/agent/utils"
	"github.com/aws/amazon-ecs-agent/agent/utils/ttime"
	"github.com/aws/amazon-ecs-agent/agent/wsclient"
)

var log = logger.ForModule("updater")

const desiredImageFile = "desired-image"

// update describes metadata around an update 2-phase request
type updater struct {
	stage     updateStage
	stageTime time.Time
	// downloadMessageID is the most recent message id seen for this update id
	downloadMessageID string
	// updateID is a unique identifier for this update used to determine if a
	// new update request, even with a different message id, is a duplicate or
	// not
	updateID   string
	fs         os.FileSystem
	acs        wsclient.ClientServer
	config     *config.Config
	httpclient *http.Client

	sync.Mutex
}

type updateStage int8

const (
	updateNone updateStage = iota
	updateDownloading
	updateDownloaded
)

const (
	maxUpdateDuration     = 30 * time.Minute
	updateDownloadTimeout = 15 * time.Minute
)

// Singleton updater
var singleUpdater *updater

// AddAgentUpdateHandlers adds the needed update handlers to perform agent
// updates
func AddAgentUpdateHandlers(cs wsclient.ClientServer, cfg *config.Config, saver statemanager.Saver, taskEngine engine.TaskEngine) {
	if cfg.UpdatesEnabled {
		singleUpdater = &updater{
			acs:        cs,
			config:     cfg,
			fs:         os.Default,
			httpclient: httpclient.New(updateDownloadTimeout, false),
		}
		cs.AddRequestHandler(singleUpdater.stageUpdateHandler())
		cs.AddRequestHandler(singleUpdater.performUpdateHandler(saver, taskEngine))
		log.Debug("Added update handlers")
	} else {
		log.Debug("Updates disabled; no handlers added")
	}
}

func (u *updater) stageUpdateHandler() func(req *ecsacs.StageUpdateMessage) {
	return func(req *ecsacs.StageUpdateMessage) {
		u.Lock()
		defer u.Unlock()

		if req == nil || req.MessageId == nil {
			log.Error("Nil request to stage update or missing MessageID")
			return
		}
		nack := func(reason string) {
			log.Debug("Nacking update", "reason", reason)
			u.acs.MakeRequest(&ecsacs.NackRequest{
				Cluster:           req.ClusterArn,
				ContainerInstance: req.ContainerInstanceArn,
				MessageId:         req.MessageId,
				Reason:            &reason,
			})
			u.reset()
		}

		if err := validateUpdateInfo(req.UpdateInfo); err != nil {
			nack("Invalid update: " + err.Error())
			return
		}

		log.Debug("Staging update", "update", req)

		if u.stage != updateNone {
			if u.updateID != "" && u.updateID == *req.UpdateInfo.Signature {
				log.Debug("Update already in progress, acking duplicate message", "id", u.updateID)
				// Acking here is safe as any currently-downloading update will already be holding
				// the update lock.  A failed download will nack and clear state (while holding the
				// update lock) before this code is reached, meaning that the above conditional will
				// not evaluate true (no matching, in-progress update).
				u.acs.MakeRequest(&ecsacs.AckRequest{
					Cluster:           req.ClusterArn,
					ContainerInstance: req.ContainerInstanceArn,
					MessageId:         req.MessageId,
				})
				return
			} else {
				// Nack previous update
				reason := "New update arrived: " + *req.MessageId
				u.acs.MakeRequest(&ecsacs.NackRequest{
					Cluster:           req.ClusterArn,
					ContainerInstance: req.ContainerInstanceArn,
					MessageId:         &u.downloadMessageID,
					Reason:            &reason,
				})
			}
		}
		u.updateID = *req.UpdateInfo.Signature
		u.stage = updateDownloading
		u.stageTime = ttime.Now()
		u.downloadMessageID = *req.MessageId

		err := u.download(req.UpdateInfo)
		if err != nil {
			nack("Unable to download: " + err.Error())
			return
		}

		u.stage = updateDownloaded

		u.acs.MakeRequest(&ecsacs.AckRequest{
			Cluster:           req.ClusterArn,
			ContainerInstance: req.ContainerInstanceArn,
			MessageId:         req.MessageId,
		})
	}
}

func (u *updater) download(info *ecsacs.UpdateInfo) (err error) {
	if info == nil || info.Location == nil {
		return errors.New("No location given")
	}
	if info.Signature == nil {
		return errors.New("No signature given")
	}
	resp, err := u.httpclient.Get(*info.Location)
	if resp != nil && resp.Body != nil {
		defer resp.Body.Close()
	}
	if err != nil {
		return err
	}

	outFileBasename := utils.RandHex() + ".ecs-update.tar"
	outFilePath := filepath.Join(u.config.UpdateDownloadDir, outFileBasename)
	outFile, err := u.fs.Create(outFilePath)
	if err != nil {
		return err
	}
	defer func() {
		outFile.Close()
		if err != nil {
			u.fs.Remove(outFilePath)
		}
	}()

	hashsum := sha256.New()
	bodyHashReader := io.TeeReader(resp.Body, hashsum)
	_, err = io.Copy(outFile, bodyHashReader)
	if err != nil {
		return err
	}
	shasum := hashsum.Sum(nil)
	shasumString := fmt.Sprintf("%x", shasum)

	if shasumString != strings.TrimSpace(*info.Signature) {
		return errors.New("Hashsum validation failed")
	}

	err = u.fs.WriteFile(filepath.Join(u.config.UpdateDownloadDir, desiredImageFile), []byte(outFileBasename+"\n"), 0644)
	return err
}

func (u *updater) performUpdateHandler(saver statemanager.Saver, taskEngine engine.TaskEngine) func(req *ecsacs.PerformUpdateMessage) {
	return func(req *ecsacs.PerformUpdateMessage) {
		u.Lock()
		defer u.Unlock()

		log.Debug("Got perform update request")
		if u.stage != updateDownloaded {
			log.Debug("Nacking update; not downloaded")
			reason := "Cannot perform update; update not downloaded"
			u.acs.MakeRequest(&ecsacs.NackRequest{
				Cluster:           req.ClusterArn,
				ContainerInstance: req.ContainerInstanceArn,
				MessageId:         req.MessageId,
				Reason:            &reason,
			})
			return
		}
		u.acs.MakeRequest(&ecsacs.AckRequest{
			Cluster:           req.ClusterArn,
			ContainerInstance: req.ContainerInstanceArn,
			MessageId:         req.MessageId,
		})

		err := sighandlers.FinalSave(saver, taskEngine)
		if err != nil {
			log.Crit("Error saving before update exit", "err", err)
		} else {
			log.Debug("Saved state!")
		}
		u.fs.Exit(exitcodes.ExitUpdate)
	}
}

func (u *updater) reset() {
	u.updateID = ""
	u.downloadMessageID = ""
	u.stage = updateNone
	u.stageTime = time.Time{}
}
<commit_msg>Elevate Agent update failure logging from DEBUG to ERROR<commit_after>// Copyright 2014-2015 Amazon.com, Inc. or its affiliates. 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. A copy of the
// License is located at
//
//	http://aws.amazon.com/apache2.0/
//
// or in the "license" file accompanying this file. This file 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 updater

import (
	"crypto/sha256"
	"errors"
	"fmt"
	"io"
	"net/http"
	"path/filepath"
	"strings"
	"sync"
	"time"

	"github.com/aws/amazon-ecs-agent/agent/acs/model/ecsacs"
	"github.com/aws/amazon-ecs-agent/agent/acs/update_handler/os"
	"github.com/aws/amazon-ecs-agent/agent/config"
	"github.com/aws/amazon-ecs-agent/agent/engine"
	"github.com/aws/amazon-ecs-agent/agent/httpclient"
	"github.com/aws/amazon-ecs-agent/agent/logger"
	"github.com/aws/amazon-ecs-agent/agent/sighandlers"
	"github.com/aws/amazon-ecs-agent/agent/sighandlers/exitcodes"
	"github.com/aws/amazon-ecs-agent/agent/statemanager"
	"github.com/aws/amazon-ecs-agent/agent/utils"
	"github.com/aws/amazon-ecs-agent/agent/utils/ttime"
	"github.com/aws/amazon-ecs-agent/agent/wsclient"
)

var log = logger.ForModule("updater")

const desiredImageFile = "desired-image"

// update describes metadata around an update 2-phase request
type updater struct {
	stage     updateStage
	stageTime time.Time
	// downloadMessageID is the most recent message id seen for this update id
	downloadMessageID string
	// updateID is a unique identifier for this update used to determine if a
	// new update request, even with a different message id, is a duplicate or
	// not
	updateID   string
	fs         os.FileSystem
	acs        wsclient.ClientServer
	config     *config.Config
	httpclient *http.Client

	sync.Mutex
}

type updateStage int8

const (
	updateNone updateStage = iota
	updateDownloading
	updateDownloaded
)

const (
	maxUpdateDuration     = 30 * time.Minute
	updateDownloadTimeout = 15 * time.Minute
)

// Singleton updater
var singleUpdater *updater

// AddAgentUpdateHandlers adds the needed update handlers to perform agent
// updates
func AddAgentUpdateHandlers(cs wsclient.ClientServer, cfg *config.Config, saver statemanager.Saver, taskEngine engine.TaskEngine) {
	if cfg.UpdatesEnabled {
		singleUpdater = &updater{
			acs:        cs,
			config:     cfg,
			fs:         os.Default,
			httpclient: httpclient.New(updateDownloadTimeout, false),
		}
		cs.AddRequestHandler(singleUpdater.stageUpdateHandler())
		cs.AddRequestHandler(singleUpdater.performUpdateHandler(saver, taskEngine))
		log.Debug("Added update handlers")
	} else {
		log.Debug("Updates disabled; no handlers added")
	}
}

func (u *updater) stageUpdateHandler() func(req *ecsacs.StageUpdateMessage) {
	return func(req *ecsacs.StageUpdateMessage) {
		u.Lock()
		defer u.Unlock()

		if req == nil || req.MessageId == nil {
			log.Error("Nil request to stage update or missing MessageID")
			return
		}
		nack := func(reason string) {
			log.Error("Nacking update", "reason", reason)
			u.acs.MakeRequest(&ecsacs.NackRequest{
				Cluster:           req.ClusterArn,
				ContainerInstance: req.ContainerInstanceArn,
				MessageId:         req.MessageId,
				Reason:            &reason,
			})
			u.reset()
		}

		if err := validateUpdateInfo(req.UpdateInfo); err != nil {
			nack("Invalid update: " + err.Error())
			return
		}

		log.Debug("Staging update", "update", req)

		if u.stage != updateNone {
			if u.updateID != "" && u.updateID == *req.UpdateInfo.Signature {
				log.Debug("Update already in progress, acking duplicate message", "id", u.updateID)
				// Acking here is safe as any currently-downloading update will already be holding
				// the update lock.  A failed download will nack and clear state (while holding the
				// update lock) before this code is reached, meaning that the above conditional will
				// not evaluate true (no matching, in-progress update).
				u.acs.MakeRequest(&ecsacs.AckRequest{
					Cluster:           req.ClusterArn,
					ContainerInstance: req.ContainerInstanceArn,
					MessageId:         req.MessageId,
				})
				return
			} else {
				// Nack previous update
				reason := "New update arrived: " + *req.MessageId
				u.acs.MakeRequest(&ecsacs.NackRequest{
					Cluster:           req.ClusterArn,
					ContainerInstance: req.ContainerInstanceArn,
					MessageId:         &u.downloadMessageID,
					Reason:            &reason,
				})
			}
		}
		u.updateID = *req.UpdateInfo.Signature
		u.stage = updateDownloading
		u.stageTime = ttime.Now()
		u.downloadMessageID = *req.MessageId

		err := u.download(req.UpdateInfo)
		if err != nil {
			nack("Unable to download: " + err.Error())
			return
		}

		u.stage = updateDownloaded

		u.acs.MakeRequest(&ecsacs.AckRequest{
			Cluster:           req.ClusterArn,
			ContainerInstance: req.ContainerInstanceArn,
			MessageId:         req.MessageId,
		})
	}
}

func (u *updater) download(info *ecsacs.UpdateInfo) (err error) {
	if info == nil || info.Location == nil {
		return errors.New("No location given")
	}
	if info.Signature == nil {
		return errors.New("No signature given")
	}
	resp, err := u.httpclient.Get(*info.Location)
	if resp != nil && resp.Body != nil {
		defer resp.Body.Close()
	}
	if err != nil {
		return err
	}

	outFileBasename := utils.RandHex() + ".ecs-update.tar"
	outFilePath := filepath.Join(u.config.UpdateDownloadDir, outFileBasename)
	outFile, err := u.fs.Create(outFilePath)
	if err != nil {
		return err
	}
	defer func() {
		outFile.Close()
		if err != nil {
			u.fs.Remove(outFilePath)
		}
	}()

	hashsum := sha256.New()
	bodyHashReader := io.TeeReader(resp.Body, hashsum)
	_, err = io.Copy(outFile, bodyHashReader)
	if err != nil {
		return err
	}
	shasum := hashsum.Sum(nil)
	shasumString := fmt.Sprintf("%x", shasum)

	if shasumString != strings.TrimSpace(*info.Signature) {
		return errors.New("Hashsum validation failed")
	}

	err = u.fs.WriteFile(filepath.Join(u.config.UpdateDownloadDir, desiredImageFile), []byte(outFileBasename+"\n"), 0644)
	return err
}

func (u *updater) performUpdateHandler(saver statemanager.Saver, taskEngine engine.TaskEngine) func(req *ecsacs.PerformUpdateMessage) {
	return func(req *ecsacs.PerformUpdateMessage) {
		u.Lock()
		defer u.Unlock()

		log.Debug("Got perform update request")
		if u.stage != updateDownloaded {
			log.Error("Nacking update; not downloaded")
			reason := "Cannot perform update; update not downloaded"
			u.acs.MakeRequest(&ecsacs.NackRequest{
				Cluster:           req.ClusterArn,
				ContainerInstance: req.ContainerInstanceArn,
				MessageId:         req.MessageId,
				Reason:            &reason,
			})
			return
		}
		u.acs.MakeRequest(&ecsacs.AckRequest{
			Cluster:           req.ClusterArn,
			ContainerInstance: req.ContainerInstanceArn,
			MessageId:         req.MessageId,
		})

		err := sighandlers.FinalSave(saver, taskEngine)
		if err != nil {
			log.Crit("Error saving before update exit", "err", err)
		} else {
			log.Debug("Saved state!")
		}
		u.fs.Exit(exitcodes.ExitUpdate)
	}
}

func (u *updater) reset() {
	u.updateID = ""
	u.downloadMessageID = ""
	u.stage = updateNone
	u.stageTime = time.Time{}
}
<|endoftext|>


<commit_before>// Copyright 2010 The Walk 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 walk

import (
	"unsafe"
)

import (
	"github.com/kumakichi/win"
)

const mainWindowWindowClass = `\o/ Walk_MainWindow_Class \o/`

func init() {
	MustRegisterWindowClass(mainWindowWindowClass)
}

type MainWindow struct {
	FormBase
	windowPlacement *win.WINDOWPLACEMENT
	menu            *Menu
	toolBar         *ToolBar
	statusBar       *StatusBar
}

func NewMainWindow() (*MainWindow, error) {
	mw := new(MainWindow)

	if err := InitWindow(
		mw,
		nil,
		mainWindowWindowClass,
		win.WS_OVERLAPPEDWINDOW,
		win.WS_EX_CONTROLPARENT); err != nil {

		return nil, err
	}

	succeeded := false
	defer func() {
		if !succeeded {
			mw.Dispose()
		}
	}()

	mw.SetPersistent(true)

	var err error

	if mw.menu, err = newMenuBar(); err != nil {
		return nil, err
	}
	if !win.SetMenu(mw.hWnd, mw.menu.hMenu) {
		return nil, lastError("SetMenu")
	}

	if mw.toolBar, err = NewToolBar(mw); err != nil {
		return nil, err
	}
	mw.toolBar.parent = nil
	mw.Children().Remove(mw.toolBar)
	mw.toolBar.parent = mw
	win.SetParent(mw.toolBar.hWnd, mw.hWnd)

	if mw.statusBar, err = NewStatusBar(mw); err != nil {
		return nil, err
	}
	mw.statusBar.parent = nil
	mw.Children().Remove(mw.statusBar)
	mw.statusBar.parent = mw
	win.SetParent(mw.statusBar.hWnd, mw.hWnd)

	// This forces display of focus rectangles, as soon as the user starts to type.
	mw.SendMessage(win.WM_CHANGEUISTATE, win.UIS_INITIALIZE, 0)

	succeeded = true

	return mw, nil
}

func (mw *MainWindow) Menu() *Menu {
	return mw.menu
}

func (mw *MainWindow) ToolBar() *ToolBar {
	return mw.toolBar
}

func (mw *MainWindow) StatusBar() *StatusBar {
	return mw.statusBar
}

func (mw *MainWindow) ClientBounds() Rectangle {
	bounds := mw.FormBase.ClientBounds()

	if mw.toolBar.Actions().Len() > 0 {
		tlbBounds := mw.toolBar.Bounds()

		bounds.Y += tlbBounds.Height
		bounds.Height -= tlbBounds.Height
	}

	if mw.statusBar.Visible() {
		bounds.Height -= mw.statusBar.Height()
	}

	return bounds
}

func (mw *MainWindow) SetVisible(visible bool) {
	if visible {
		win.DrawMenuBar(mw.hWnd)

		if mw.clientComposite.layout != nil {
			mw.clientComposite.layout.Update(false)
		}
	}

	mw.FormBase.SetVisible(visible)
}

func (mw *MainWindow) Fullscreen() bool {
	return win.GetWindowLong(mw.hWnd, win.GWL_STYLE)&win.WS_OVERLAPPEDWINDOW == 0
}

func (mw *MainWindow) SetFullscreen(fullscreen bool) error {
	if fullscreen == mw.Fullscreen() {
		return nil
	}

	if fullscreen {
		var mi win.MONITORINFO
		mi.CbSize = uint32(unsafe.Sizeof(mi))

		if mw.windowPlacement == nil {
			mw.windowPlacement = new(win.WINDOWPLACEMENT)
		}

		if !win.GetWindowPlacement(mw.hWnd, mw.windowPlacement) {
			return lastError("GetWindowPlacement")
		}
		if !win.GetMonitorInfo(win.MonitorFromWindow(
			mw.hWnd, win.MONITOR_DEFAULTTOPRIMARY), &mi) {

			return newError("GetMonitorInfo")
		}

		if err := mw.ensureStyleBits(win.WS_OVERLAPPEDWINDOW, false); err != nil {
			return err
		}

		if r := mi.RcMonitor; !win.SetWindowPos(
			mw.hWnd, win.HWND_TOP,
			r.Left, r.Top, r.Right-r.Left, r.Bottom-r.Top,
			win.SWP_FRAMECHANGED|win.SWP_NOOWNERZORDER) {

			return lastError("SetWindowPos")
		}
	} else {
		if err := mw.ensureStyleBits(win.WS_OVERLAPPEDWINDOW, true); err != nil {
			return err
		}

		if !win.SetWindowPlacement(mw.hWnd, mw.windowPlacement) {
			return lastError("SetWindowPlacement")
		}

		if !win.SetWindowPos(mw.hWnd, 0, 0, 0, 0, 0, win.SWP_FRAMECHANGED|win.SWP_NOMOVE|
			win.SWP_NOOWNERZORDER|win.SWP_NOSIZE|win.SWP_NOZORDER) {

			return lastError("SetWindowPos")
		}
	}

	return nil
}

func (mw *MainWindow) WndProc(hwnd win.HWND, msg uint32, wParam, lParam uintptr) uintptr {
	switch msg {
	case win.WM_SIZE, win.WM_SIZING:
		cb := mw.ClientBounds()

		mw.toolBar.SetBounds(Rectangle{0, 0, cb.Width, mw.toolBar.Height()})
		mw.statusBar.SetBounds(Rectangle{0, cb.Height, cb.Width, mw.statusBar.Height()})
	}

	return mw.FormBase.WndProc(hwnd, msg, wParam, lParam)
}
<commit_msg>at last, statusbar works ok(not really) now when the application has both MENU and ToolBar, mainwindow.go line 107 ...<commit_after>// Copyright 2010 The Walk 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 walk

import (
	"unsafe"
)

import (
	"github.com/kumakichi/win"
)

const mainWindowWindowClass = `\o/ Walk_MainWindow_Class \o/`

func init() {
	MustRegisterWindowClass(mainWindowWindowClass)
}

type MainWindow struct {
	FormBase
	windowPlacement *win.WINDOWPLACEMENT
	menu            *Menu
	toolBar         *ToolBar
	statusBar       *StatusBar
}

func NewMainWindow() (*MainWindow, error) {
	mw := new(MainWindow)

	if err := InitWindow(
		mw,
		nil,
		mainWindowWindowClass,
		win.WS_OVERLAPPEDWINDOW,
		win.WS_EX_CONTROLPARENT); err != nil {

		return nil, err
	}

	succeeded := false
	defer func() {
		if !succeeded {
			mw.Dispose()
		}
	}()

	mw.SetPersistent(true)

	var err error

	if mw.menu, err = newMenuBar(); err != nil {
		return nil, err
	}
	if !win.SetMenu(mw.hWnd, mw.menu.hMenu) {
		return nil, lastError("SetMenu")
	}

	if mw.toolBar, err = NewToolBar(mw); err != nil {
		return nil, err
	}
	mw.toolBar.parent = nil
	mw.Children().Remove(mw.toolBar)
	mw.toolBar.parent = mw
	win.SetParent(mw.toolBar.hWnd, mw.hWnd)

	if mw.statusBar, err = NewStatusBar(mw); err != nil {
		return nil, err
	}
	mw.statusBar.parent = nil
	mw.Children().Remove(mw.statusBar)
	mw.statusBar.parent = mw
	win.SetParent(mw.statusBar.hWnd, mw.hWnd)

	// This forces display of focus rectangles, as soon as the user starts to type.
	mw.SendMessage(win.WM_CHANGEUISTATE, win.UIS_INITIALIZE, 0)

	succeeded = true

	return mw, nil
}

func (mw *MainWindow) Menu() *Menu {
	return mw.menu
}

func (mw *MainWindow) ToolBar() *ToolBar {
	return mw.toolBar
}

func (mw *MainWindow) StatusBar() *StatusBar {
	return mw.statusBar
}

func (mw *MainWindow) ClientBounds() Rectangle {
	bounds := mw.FormBase.ClientBounds()

	if mw.toolBar.Actions().Len() > 0 {
		tlbBounds := mw.toolBar.Bounds()

		bounds.Y += tlbBounds.Height
		bounds.Height -= tlbBounds.Height
	}

	if mw.statusBar.Visible() {
		bounds.Height -= mw.statusBar.Height()
	}

	return bounds
}

func (mw *MainWindow) SetVisible(visible bool) {
	if visible {
		win.DrawMenuBar(mw.hWnd)

		if mw.clientComposite.layout != nil {
			mw.clientComposite.layout.Update(false)
		}
	}

	mw.FormBase.SetVisible(visible)
}

func (mw *MainWindow) Fullscreen() bool {
	return win.GetWindowLong(mw.hWnd, win.GWL_STYLE)&win.WS_OVERLAPPEDWINDOW == 0
}

func (mw *MainWindow) SetFullscreen(fullscreen bool) error {
	if fullscreen == mw.Fullscreen() {
		return nil
	}

	if fullscreen {
		var mi win.MONITORINFO
		mi.CbSize = uint32(unsafe.Sizeof(mi))

		if mw.windowPlacement == nil {
			mw.windowPlacement = new(win.WINDOWPLACEMENT)
		}

		if !win.GetWindowPlacement(mw.hWnd, mw.windowPlacement) {
			return lastError("GetWindowPlacement")
		}
		if !win.GetMonitorInfo(win.MonitorFromWindow(
			mw.hWnd, win.MONITOR_DEFAULTTOPRIMARY), &mi) {

			return newError("GetMonitorInfo")
		}

		if err := mw.ensureStyleBits(win.WS_OVERLAPPEDWINDOW, false); err != nil {
			return err
		}

		if r := mi.RcMonitor; !win.SetWindowPos(
			mw.hWnd, win.HWND_TOP,
			r.Left, r.Top, r.Right-r.Left, r.Bottom-r.Top,
			win.SWP_FRAMECHANGED|win.SWP_NOOWNERZORDER) {

			return lastError("SetWindowPos")
		}
	} else {
		if err := mw.ensureStyleBits(win.WS_OVERLAPPEDWINDOW, true); err != nil {
			return err
		}

		if !win.SetWindowPlacement(mw.hWnd, mw.windowPlacement) {
			return lastError("SetWindowPlacement")
		}

		if !win.SetWindowPos(mw.hWnd, 0, 0, 0, 0, 0, win.SWP_FRAMECHANGED|win.SWP_NOMOVE|
			win.SWP_NOOWNERZORDER|win.SWP_NOSIZE|win.SWP_NOZORDER) {

			return lastError("SetWindowPos")
		}
	}

	return nil
}

func (mw *MainWindow) WndProc(hwnd win.HWND, msg uint32, wParam, lParam uintptr) uintptr {
	switch msg {
	case win.WM_SIZE, win.WM_SIZING:
		cb := mw.ClientBounds()

		mw.toolBar.SetBounds(Rectangle{0, 0, cb.Width, mw.toolBar.Height()})
		mw.statusBar.SetBounds(Rectangle{0, cb.Height + mw.statusBar.Height(), cb.Width, mw.statusBar.Height()})
	}

	return mw.FormBase.WndProc(hwnd, msg, wParam, lParam)
}
<|endoftext|>