<commit_before>// Copyright 2016 the Go-FUSE 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 main

import (
	"fmt"
	"github.com/hanwen/go-fuse/fuse"
	"github.com/hanwen/go-fuse/fuse/nodefs"
)

type MemFile interface {
	Stat(out *fuse.Attr)
	Data() []byte
}

type memNode struct {
	nodefs.Node
	file MemFile
	fs   *FileSystem
}

func (n *memNode) OnMount(c *nodefs.FileSystemConnector) {
	n.fs.onMount()
}

func (n *memNode) Print(indent int) {
	s := ""
	for i := 0; i < indent; i++ {
		s = s + " "
	}

	children := n.Inode().Children()
	for k, v := range children {
		if v.IsDir() {
			fmt.Println(s + k + ":")
			mn, ok := v.Node().(*memNode)
			if ok {
				mn.Print(indent + 2)
			}
		} else {
			fmt.Println(s + k)
		}
	}
}

func (n *memNode) OpenDir(context *fuse.Context) (stream []fuse.DirEntry, code fuse.Status) {
	children := n.Inode().Children()
	stream = make([]fuse.DirEntry, 0, len(children))
	for k, v := range children {
		mode := fuse.S_IFREG | 0666
		if v.IsDir() {
			mode = fuse.S_IFDIR | 0777
		}
		stream = append(stream, fuse.DirEntry{
			Name: k,
			Mode: uint32(mode),
		})
	}
	return stream, fuse.OK
}

func (n *memNode) Open(flags uint32, context *fuse.Context) (fuseFile nodefs.File, code fuse.Status) {
	if flags&fuse.O_ANYWRITE != 0 {
		return nil, fuse.EPERM
	}

	return nodefs.NewDataFile(n.file.Data()), fuse.OK
}

func (n *memNode) Deletable() bool {
	return false
}

func (n *memNode) GetAttr(out *fuse.Attr, file nodefs.File, context *fuse.Context) fuse.Status {
	if n.Inode().IsDir() {
		out.Mode = fuse.S_IFDIR | 0777
		return fuse.OK
	}
	n.file.Stat(out)
	return fuse.OK
}
<commit_msg>Delete node.go<commit_after><|endoftext|>


<commit_before>package restic

import (
	"encoding/json"
	"fmt"
	"os"
	"os/user"
	"strconv"
	"sync"
	"syscall"
	"time"

	"github.com/juju/errors"
	"github.com/restic/restic/backend"
	"github.com/restic/restic/debug"
	"github.com/restic/restic/pack"
	"github.com/restic/restic/server"
)

type Node struct {
	Name       string       `json:"name"`
	Type       string       `json:"type"`
	Mode       os.FileMode  `json:"mode,omitempty"`
	ModTime    time.Time    `json:"mtime,omitempty"`
	AccessTime time.Time    `json:"atime,omitempty"`
	ChangeTime time.Time    `json:"ctime,omitempty"`
	UID        uint32       `json:"uid"`
	GID        uint32       `json:"gid"`
	User       string       `json:"user,omitempty"`
	Group      string       `json:"group,omitempty"`
	Inode      uint64       `json:"inode,omitempty"`
	Size       uint64       `json:"size,omitempty"`
	Links      uint64       `json:"links,omitempty"`
	LinkTarget string       `json:"linktarget,omitempty"`
	Device     uint64       `json:"device,omitempty"`
	Content    []backend.ID `json:"content"`
	Subtree    backend.ID   `json:"subtree,omitempty"`

	Error string `json:"error,omitempty"`

	tree *Tree

	path  string
	err   error
	blobs server.Blobs
}

func (node Node) String() string {
	switch node.Type {
	case "file":
		return fmt.Sprintf("%s %5d %5d %6d %s %s",
			node.Mode, node.UID, node.GID, node.Size, node.ModTime, node.Name)
	case "dir":
		return fmt.Sprintf("%s %5d %5d %6d %s %s",
			node.Mode|os.ModeDir, node.UID, node.GID, node.Size, node.ModTime, node.Name)
	}

	return fmt.Sprintf("<Node(%s) %s>", node.Type, node.Name)
}

func (node Node) Tree() *Tree {
	return node.tree
}

func NodeFromFileInfo(path string, fi os.FileInfo) (*Node, error) {
	node := &Node{
		path:    path,
		Name:    fi.Name(),
		Mode:    fi.Mode() & (os.ModePerm | os.ModeType),
		ModTime: fi.ModTime(),
	}

	node.Type = nodeTypeFromFileInfo(fi)
	if node.Type == "file" {
		node.Size = uint64(fi.Size())
	}

	err := node.fillExtra(path, fi)
	return node, err
}

func nodeTypeFromFileInfo(fi os.FileInfo) string {
	switch fi.Mode() & (os.ModeType | os.ModeCharDevice) {
	case 0:
		return "file"
	case os.ModeDir:
		return "dir"
	case os.ModeSymlink:
		return "symlink"
	case os.ModeDevice | os.ModeCharDevice:
		return "chardev"
	case os.ModeDevice:
		return "dev"
	case os.ModeNamedPipe:
		return "fifo"
	case os.ModeSocket:
		return "socket"
	}

	return ""
}

func (node *Node) CreateAt(path string, s *server.Server) error {
	switch node.Type {
	case "dir":
		if err := node.createDirAt(path); err != nil {
			return errors.Annotate(err, "createDirAt")
		}
	case "file":
		if err := node.createFileAt(path, s); err != nil {
			return errors.Annotate(err, "createFileAt")
		}
	case "symlink":
		if err := node.createSymlinkAt(path); err != nil {
			return errors.Annotate(err, "createSymlinkAt")
		}
	case "dev":
		if err := node.createDevAt(path); err != nil {
			return errors.Annotate(err, "createDevAt")
		}
	case "chardev":
		if err := node.createCharDevAt(path); err != nil {
			return errors.Annotate(err, "createCharDevAt")
		}
	case "fifo":
		if err := node.createFifoAt(path); err != nil {
			return errors.Annotate(err, "createFifoAt")
		}
	case "socket":
		return nil
	default:
		return fmt.Errorf("filetype %q not implemented!\n", node.Type)
	}

	return node.restoreMetadata(path)
}

func (node Node) restoreMetadata(path string) error {
	var err error

	err = os.Lchown(path, int(node.UID), int(node.GID))
	if err != nil {
		return errors.Annotate(err, "Lchown")
	}

	if node.Type == "symlink" {
		return nil
	}

	err = os.Chmod(path, node.Mode)
	if err != nil {
		return errors.Annotate(err, "Chmod")
	}

	var utimes = []syscall.Timespec{
		syscall.NsecToTimespec(node.AccessTime.UnixNano()),
		syscall.NsecToTimespec(node.ModTime.UnixNano()),
	}
	err = syscall.UtimesNano(path, utimes)
	if err != nil {
		return errors.Annotate(err, "UtimesNano")
	}

	return nil
}

func (node Node) createDirAt(path string) error {
	err := os.Mkdir(path, node.Mode)
	if err != nil {
		return errors.Annotate(err, "Mkdir")
	}

	return nil
}

func (node Node) createFileAt(path string, s *server.Server) error {
	f, err := os.OpenFile(path, os.O_CREATE|os.O_WRONLY, 0600)
	defer f.Close()

	if err != nil {
		return errors.Annotate(err, "OpenFile")
	}

	for _, id := range node.Content {
		buf, err := s.LoadBlob(pack.Data, id)
		if err != nil {
			return errors.Annotate(err, "Load")
		}

		_, err = f.Write(buf)
		if err != nil {
			return errors.Annotate(err, "Write")
		}
	}

	return nil
}

func (node Node) createSymlinkAt(path string) error {
	err := os.Symlink(node.LinkTarget, path)
	if err != nil {
		return errors.Annotate(err, "Symlink")
	}

	return nil
}

func (node Node) MarshalJSON() ([]byte, error) {
	type nodeJSON Node
	nj := nodeJSON(node)
	name := strconv.Quote(node.Name)
	nj.Name = name[1 : len(name)-1]

	return json.Marshal(nj)
}

func (node *Node) UnmarshalJSON(data []byte) error {
	type nodeJSON Node
	nj := (*nodeJSON)(node)

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

	nj.Name, err = strconv.Unquote(`"` + nj.Name + `"`)
	return err
}

func (node Node) Equals(other Node) bool {
	if node.Name != other.Name {
		return false
	}
	if node.Type != other.Type {
		return false
	}
	if node.Mode != other.Mode {
		return false
	}
	if node.ModTime != other.ModTime {
		return false
	}
	if node.AccessTime != other.AccessTime {
		return false
	}
	if node.ChangeTime != other.ChangeTime {
		return false
	}
	if node.UID != other.UID {
		return false
	}
	if node.GID != other.GID {
		return false
	}
	if node.User != other.User {
		return false
	}
	if node.Group != other.Group {
		return false
	}
	if node.Inode != other.Inode {
		return false
	}
	if node.Size != other.Size {
		return false
	}
	if node.Links != other.Links {
		return false
	}
	if node.LinkTarget != other.LinkTarget {
		return false
	}
	if node.Device != other.Device {
		return false
	}
	if !node.sameContent(other) {
		return false
	}
	if !node.Subtree.Equal(other.Subtree) {
		return false
	}
	if node.Error != other.Error {
		return false
	}

	return true
}

func (node Node) sameContent(other Node) bool {
	if node.Content == nil {
		return other.Content == nil
	}

	if other.Content == nil {
		return false
	}

	if len(node.Content) != len(other.Content) {
		return false
	}

	for i := 0; i < len(node.Content); i++ {
		if !node.Content[i].Equal(other.Content[i]) {
			return false
		}
	}

	return true
}

func (node *Node) isNewer(path string, fi os.FileInfo) bool {
	if node.Type != "file" {
		debug.Log("node.isNewer", "node %v is newer: not file", path)
		return true
	}

	tpe := nodeTypeFromFileInfo(fi)
	if node.Name != fi.Name() || node.Type != tpe {
		debug.Log("node.isNewer", "node %v is newer: name or type changed", path)
		return true
	}

	extendedStat := fi.Sys().(*syscall.Stat_t)
	inode := extendedStat.Ino
	size := uint64(extendedStat.Size)

	if node.ModTime != fi.ModTime() ||
		node.ChangeTime != changeTime(extendedStat) ||
		node.Inode != inode ||
		node.Size != size {
		debug.Log("node.isNewer", "node %v is newer: timestamp, size or inode changed", path)
		return true
	}

	debug.Log("node.isNewer", "node %v is not newer", path)
	return false
}

func (node *Node) fillUser(stat *syscall.Stat_t) error {
	node.UID = stat.Uid
	node.GID = stat.Gid

	username, err := lookupUsername(strconv.Itoa(int(stat.Uid)))
	if err != nil {
		return errors.Annotate(err, "fillUser")
	}

	node.User = username
	return nil
}

var (
	uidLookupCache      = make(map[string]string)
	uidLookupCacheMutex = sync.RWMutex{}
)

func lookupUsername(uid string) (string, error) {
	uidLookupCacheMutex.RLock()
	value, ok := uidLookupCache[uid]
	uidLookupCacheMutex.RUnlock()

	if ok {
		return value, nil
	}

	u, err := user.LookupId(uid)
	if err != nil {
		return "", err
	}

	uidLookupCacheMutex.Lock()
	uidLookupCache[uid] = u.Username
	uidLookupCacheMutex.Unlock()

	return u.Username, nil
}

func (node *Node) fillExtra(path string, fi os.FileInfo) error {
	stat, ok := fi.Sys().(*syscall.Stat_t)
	if !ok {
		return nil
	}

	node.Inode = stat.Ino

	node.fillTimes(stat)

	var err error

	if err = node.fillUser(stat); err != nil {
		return errors.Annotate(err, "fillExtra")
	}

	switch node.Type {
	case "file":
		node.Size = uint64(stat.Size)
		node.Links = uint64(stat.Nlink)
	case "dir":
	case "symlink":
		node.LinkTarget, err = os.Readlink(path)
	case "dev":
		node.Device = uint64(stat.Rdev)
	case "chardev":
		node.Device = uint64(stat.Rdev)
	case "fifo":
	case "socket":
	default:
		err = fmt.Errorf("invalid node type %q", node.Type)
	}

	return err
}
<commit_msg>node: add comments<commit_after>package restic

import (
	"encoding/json"
	"fmt"
	"os"
	"os/user"
	"strconv"
	"sync"
	"syscall"
	"time"

	"github.com/juju/errors"
	"github.com/restic/restic/backend"
	"github.com/restic/restic/debug"
	"github.com/restic/restic/pack"
	"github.com/restic/restic/server"
)

// Node is a file, directory or other item in a backup.
type Node struct {
	Name       string       `json:"name"`
	Type       string       `json:"type"`
	Mode       os.FileMode  `json:"mode,omitempty"`
	ModTime    time.Time    `json:"mtime,omitempty"`
	AccessTime time.Time    `json:"atime,omitempty"`
	ChangeTime time.Time    `json:"ctime,omitempty"`
	UID        uint32       `json:"uid"`
	GID        uint32       `json:"gid"`
	User       string       `json:"user,omitempty"`
	Group      string       `json:"group,omitempty"`
	Inode      uint64       `json:"inode,omitempty"`
	Size       uint64       `json:"size,omitempty"`
	Links      uint64       `json:"links,omitempty"`
	LinkTarget string       `json:"linktarget,omitempty"`
	Device     uint64       `json:"device,omitempty"`
	Content    []backend.ID `json:"content"`
	Subtree    backend.ID   `json:"subtree,omitempty"`

	Error string `json:"error,omitempty"`

	tree *Tree

	path  string
	err   error
	blobs server.Blobs
}

func (node Node) String() string {
	switch node.Type {
	case "file":
		return fmt.Sprintf("%s %5d %5d %6d %s %s",
			node.Mode, node.UID, node.GID, node.Size, node.ModTime, node.Name)
	case "dir":
		return fmt.Sprintf("%s %5d %5d %6d %s %s",
			node.Mode|os.ModeDir, node.UID, node.GID, node.Size, node.ModTime, node.Name)
	}

	return fmt.Sprintf("<Node(%s) %s>", node.Type, node.Name)
}

func (node Node) Tree() *Tree {
	return node.tree
}

// NodeFromFileInfo returns a new node from the given path and FileInfo.
func NodeFromFileInfo(path string, fi os.FileInfo) (*Node, error) {
	node := &Node{
		path:    path,
		Name:    fi.Name(),
		Mode:    fi.Mode() & (os.ModePerm | os.ModeType),
		ModTime: fi.ModTime(),
	}

	node.Type = nodeTypeFromFileInfo(fi)
	if node.Type == "file" {
		node.Size = uint64(fi.Size())
	}

	err := node.fillExtra(path, fi)
	return node, err
}

func nodeTypeFromFileInfo(fi os.FileInfo) string {
	switch fi.Mode() & (os.ModeType | os.ModeCharDevice) {
	case 0:
		return "file"
	case os.ModeDir:
		return "dir"
	case os.ModeSymlink:
		return "symlink"
	case os.ModeDevice | os.ModeCharDevice:
		return "chardev"
	case os.ModeDevice:
		return "dev"
	case os.ModeNamedPipe:
		return "fifo"
	case os.ModeSocket:
		return "socket"
	}

	return ""
}

// CreatAt creates the node at the given path and restores all the meta data.
func (node *Node) CreateAt(path string, s *server.Server) error {
	switch node.Type {
	case "dir":
		if err := node.createDirAt(path); err != nil {
			return errors.Annotate(err, "createDirAt")
		}
	case "file":
		if err := node.createFileAt(path, s); err != nil {
			return errors.Annotate(err, "createFileAt")
		}
	case "symlink":
		if err := node.createSymlinkAt(path); err != nil {
			return errors.Annotate(err, "createSymlinkAt")
		}
	case "dev":
		if err := node.createDevAt(path); err != nil {
			return errors.Annotate(err, "createDevAt")
		}
	case "chardev":
		if err := node.createCharDevAt(path); err != nil {
			return errors.Annotate(err, "createCharDevAt")
		}
	case "fifo":
		if err := node.createFifoAt(path); err != nil {
			return errors.Annotate(err, "createFifoAt")
		}
	case "socket":
		return nil
	default:
		return fmt.Errorf("filetype %q not implemented!\n", node.Type)
	}

	return node.restoreMetadata(path)
}

func (node Node) restoreMetadata(path string) error {
	var err error

	err = os.Lchown(path, int(node.UID), int(node.GID))
	if err != nil {
		return errors.Annotate(err, "Lchown")
	}

	if node.Type == "symlink" {
		return nil
	}

	err = os.Chmod(path, node.Mode)
	if err != nil {
		return errors.Annotate(err, "Chmod")
	}

	var utimes = []syscall.Timespec{
		syscall.NsecToTimespec(node.AccessTime.UnixNano()),
		syscall.NsecToTimespec(node.ModTime.UnixNano()),
	}
	err = syscall.UtimesNano(path, utimes)
	if err != nil {
		return errors.Annotate(err, "UtimesNano")
	}

	return nil
}

func (node Node) createDirAt(path string) error {
	err := os.Mkdir(path, node.Mode)
	if err != nil {
		return errors.Annotate(err, "Mkdir")
	}

	return nil
}

func (node Node) createFileAt(path string, s *server.Server) error {
	f, err := os.OpenFile(path, os.O_CREATE|os.O_WRONLY, 0600)
	defer f.Close()

	if err != nil {
		return errors.Annotate(err, "OpenFile")
	}

	for _, id := range node.Content {
		buf, err := s.LoadBlob(pack.Data, id)
		if err != nil {
			return errors.Annotate(err, "Load")
		}

		_, err = f.Write(buf)
		if err != nil {
			return errors.Annotate(err, "Write")
		}
	}

	return nil
}

func (node Node) createSymlinkAt(path string) error {
	err := os.Symlink(node.LinkTarget, path)
	if err != nil {
		return errors.Annotate(err, "Symlink")
	}

	return nil
}

func (node Node) MarshalJSON() ([]byte, error) {
	type nodeJSON Node
	nj := nodeJSON(node)
	name := strconv.Quote(node.Name)
	nj.Name = name[1 : len(name)-1]

	return json.Marshal(nj)
}

func (node *Node) UnmarshalJSON(data []byte) error {
	type nodeJSON Node
	nj := (*nodeJSON)(node)

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

	nj.Name, err = strconv.Unquote(`"` + nj.Name + `"`)
	return err
}

func (node Node) Equals(other Node) bool {
	if node.Name != other.Name {
		return false
	}
	if node.Type != other.Type {
		return false
	}
	if node.Mode != other.Mode {
		return false
	}
	if node.ModTime != other.ModTime {
		return false
	}
	if node.AccessTime != other.AccessTime {
		return false
	}
	if node.ChangeTime != other.ChangeTime {
		return false
	}
	if node.UID != other.UID {
		return false
	}
	if node.GID != other.GID {
		return false
	}
	if node.User != other.User {
		return false
	}
	if node.Group != other.Group {
		return false
	}
	if node.Inode != other.Inode {
		return false
	}
	if node.Size != other.Size {
		return false
	}
	if node.Links != other.Links {
		return false
	}
	if node.LinkTarget != other.LinkTarget {
		return false
	}
	if node.Device != other.Device {
		return false
	}
	if !node.sameContent(other) {
		return false
	}
	if !node.Subtree.Equal(other.Subtree) {
		return false
	}
	if node.Error != other.Error {
		return false
	}

	return true
}

func (node Node) sameContent(other Node) bool {
	if node.Content == nil {
		return other.Content == nil
	}

	if other.Content == nil {
		return false
	}

	if len(node.Content) != len(other.Content) {
		return false
	}

	for i := 0; i < len(node.Content); i++ {
		if !node.Content[i].Equal(other.Content[i]) {
			return false
		}
	}

	return true
}

func (node *Node) isNewer(path string, fi os.FileInfo) bool {
	if node.Type != "file" {
		debug.Log("node.isNewer", "node %v is newer: not file", path)
		return true
	}

	tpe := nodeTypeFromFileInfo(fi)
	if node.Name != fi.Name() || node.Type != tpe {
		debug.Log("node.isNewer", "node %v is newer: name or type changed", path)
		return true
	}

	extendedStat := fi.Sys().(*syscall.Stat_t)
	inode := extendedStat.Ino
	size := uint64(extendedStat.Size)

	if node.ModTime != fi.ModTime() ||
		node.ChangeTime != changeTime(extendedStat) ||
		node.Inode != inode ||
		node.Size != size {
		debug.Log("node.isNewer", "node %v is newer: timestamp, size or inode changed", path)
		return true
	}

	debug.Log("node.isNewer", "node %v is not newer", path)
	return false
}

func (node *Node) fillUser(stat *syscall.Stat_t) error {
	node.UID = stat.Uid
	node.GID = stat.Gid

	username, err := lookupUsername(strconv.Itoa(int(stat.Uid)))
	if err != nil {
		return errors.Annotate(err, "fillUser")
	}

	node.User = username
	return nil
}

var (
	uidLookupCache      = make(map[string]string)
	uidLookupCacheMutex = sync.RWMutex{}
)

func lookupUsername(uid string) (string, error) {
	uidLookupCacheMutex.RLock()
	value, ok := uidLookupCache[uid]
	uidLookupCacheMutex.RUnlock()

	if ok {
		return value, nil
	}

	u, err := user.LookupId(uid)
	if err != nil {
		return "", err
	}

	uidLookupCacheMutex.Lock()
	uidLookupCache[uid] = u.Username
	uidLookupCacheMutex.Unlock()

	return u.Username, nil
}

func (node *Node) fillExtra(path string, fi os.FileInfo) error {
	stat, ok := fi.Sys().(*syscall.Stat_t)
	if !ok {
		return nil
	}

	node.Inode = stat.Ino

	node.fillTimes(stat)

	var err error

	if err = node.fillUser(stat); err != nil {
		return errors.Annotate(err, "fillExtra")
	}

	switch node.Type {
	case "file":
		node.Size = uint64(stat.Size)
		node.Links = uint64(stat.Nlink)
	case "dir":
	case "symlink":
		node.LinkTarget, err = os.Readlink(path)
	case "dev":
		node.Device = uint64(stat.Rdev)
	case "chardev":
		node.Device = uint64(stat.Rdev)
	case "fifo":
	case "socket":
	default:
		err = fmt.Errorf("invalid node type %q", node.Type)
	}

	return err
}
<|endoftext|>


<commit_before>// Copyright 2012, 2013 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package provisioner

import (
	"fmt"

	"launchpad.net/juju-core/constraints"
	"launchpad.net/juju-core/environs"
	"launchpad.net/juju-core/instance"
	"launchpad.net/juju-core/names"
	"launchpad.net/juju-core/state"
	"launchpad.net/juju-core/state/api/params"
	"launchpad.net/juju-core/state/apiserver/common"
	"launchpad.net/juju-core/state/watcher"
)

// ProvisionerAPI provides access to the Provisioner API facade.
type ProvisionerAPI struct {
	*common.Remover
	*common.StatusSetter
	*common.DeadEnsurer
	*common.PasswordChanger
	*common.LifeGetter
	*common.Addresser
	*common.ToolsGetter

	st          *state.State
	resources   *common.Resources
	authorizer  common.Authorizer
	getAuthFunc common.GetAuthFunc
}

// NewProvisionerAPI creates a new server-side ProvisionerAPI facade.
func NewProvisionerAPI(
	st *state.State,
	resources *common.Resources,
	authorizer common.Authorizer,
) (*ProvisionerAPI, error) {
	if !authorizer.AuthMachineAgent() && !authorizer.AuthEnvironManager() {
		return nil, common.ErrPerm
	}
	getAuthFunc := func() (common.AuthFunc, error) {
		isEnvironManager := authorizer.AuthEnvironManager()
		isMachineAgent := authorizer.AuthMachineAgent()
		authEntityTag := authorizer.GetAuthTag()

		return func(tag string) bool {
			if isMachineAgent && tag == authEntityTag {
				// A machine agent can always access its own machine.
				return true
			}
			_, id, err := names.ParseTag(tag, names.MachineTagKind)
			if err != nil {
				return false
			}
			parentId := state.ParentId(id)
			if parentId == "" {
				// All top-level machines are accessible by the
				// environment manager.
				return isEnvironManager
			}
			// All containers with the authenticated machine as a
			// parent are accessible by it.
			return isMachineAgent && names.MachineTag(parentId) == authEntityTag
		}, nil
	}
	return &ProvisionerAPI{
		Remover:         common.NewRemover(st, false, getAuthFunc),
		StatusSetter:    common.NewStatusSetter(st, getAuthFunc),
		DeadEnsurer:     common.NewDeadEnsurer(st, getAuthFunc),
		PasswordChanger: common.NewPasswordChanger(st, getAuthFunc),
		LifeGetter:      common.NewLifeGetter(st, getAuthFunc),
		Addresser:       common.NewAddresser(st),
		ToolsGetter:     common.NewToolsGetter(st, getAuthFunc),
		st:              st,
		resources:       resources,
		authorizer:      authorizer,
		getAuthFunc:     getAuthFunc,
	}, nil
}

func (p *ProvisionerAPI) getMachine(canAccess common.AuthFunc, tag string) (*state.Machine, error) {
	if !canAccess(tag) {
		return nil, common.ErrPerm
	}
	entity, err := p.st.FindEntity(tag)
	if err != nil {
		return nil, err
	}
	// The authorization function guarantees that the tag represents a
	// machine.
	return entity.(*state.Machine), nil
}

func (p *ProvisionerAPI) watchOneMachineContainers(arg params.WatchContainer) (params.StringsWatchResult, error) {
	nothing := params.StringsWatchResult{}
	canAccess, err := p.getAuthFunc()
	if err != nil {
		return nothing, err
	}
	if !canAccess(arg.MachineTag) {
		return nothing, common.ErrPerm
	}
	_, id, err := names.ParseTag(arg.MachineTag, names.MachineTagKind)
	if err != nil {
		return nothing, err
	}
	machine, err := p.st.Machine(id)
	if err != nil {
		return nothing, err
	}
	watch := machine.WatchContainers(instance.ContainerType(arg.ContainerType))
	// Consume the initial event and forward it to the result.
	if changes, ok := <-watch.Changes(); ok {
		return params.StringsWatchResult{
			StringsWatcherId: p.resources.Register(watch),
			Changes:          changes,
		}, nil
	}
	return nothing, watcher.MustErr(watch)
}

// WatchContainers starts a StringsWatcher to watch all containers deployed to
// any machine passed in args.
func (p *ProvisionerAPI) WatchContainers(args params.WatchContainers) (params.StringsWatchResults, error) {
	result := params.StringsWatchResults{
		Results: make([]params.StringsWatchResult, len(args.Params)),
	}
	for i, arg := range args.Params {
		watcherResult, err := p.watchOneMachineContainers(arg)
		result.Results[i] = watcherResult
		result.Results[i].Error = common.ServerError(err)
	}
	return result, nil
}

// WatchForEnvironConfigChanges returns a NotifyWatcher to observe
// changes to the environment configuration.
func (p *ProvisionerAPI) WatchForEnvironConfigChanges() (params.NotifyWatchResult, error) {
	result := params.NotifyWatchResult{}
	watch := p.st.WatchForEnvironConfigChanges()
	// Consume the initial event. Technically, API
	// calls to Watch 'transmit' the initial event
	// in the Watch response. But NotifyWatchers
	// have no state to transmit.
	if _, ok := <-watch.Changes(); ok {
		result.NotifyWatcherId = p.resources.Register(watch)
	} else {
		return result, watcher.MustErr(watch)
	}
	return result, nil
}

// EnvironConfig returns the current environment's configuration.
func (p *ProvisionerAPI) EnvironConfig() (params.EnvironConfigResult, error) {
	result := params.EnvironConfigResult{}
	config, err := p.st.EnvironConfig()
	if err != nil {
		return result, err
	}
	allAttrs := config.AllAttrs()
	if !p.authorizer.AuthEnvironManager() {
		// Mask out any secrets in the environment configuration
		// with values of the same type, so it'll pass validation.
		//
		// TODO(dimitern) 201309-26 bug #1231384
		// This needs to change so we won't return anything to
		// entities other than the environment manager, but the
		// provisioner code should be refactored first.
		env, err := environs.New(config)
		if err != nil {
			return result, err
		}
		secretAttrs, err := env.Provider().SecretAttrs(config)
		for k := range secretAttrs {
			allAttrs[k] = "not available"
		}
	}
	result.Config = allAttrs
	return result, nil
}

// ContainerConfig returns the current environment's configuration.
func (p *ProvisionerAPI) ContainerConfig() (params.ContainerConfig, error) {
	result := params.ContainerConfig{}
	config, err := p.st.EnvironConfig()
	if err != nil {
		return result, err
	}
	result.ProviderType = config.Type()
	result.AuthorizedKeys = config.AuthorizedKeys()
	result.SSLHostnameVerification = config.SSLHostnameVerification()
	return result, nil
}

// Status returns the status of each given machine entity.
func (p *ProvisionerAPI) Status(args params.Entities) (params.StatusResults, error) {
	result := params.StatusResults{
		Results: make([]params.StatusResult, len(args.Entities)),
	}
	canAccess, err := p.getAuthFunc()
	if err != nil {
		return result, err
	}
	for i, entity := range args.Entities {
		machine, err := p.getMachine(canAccess, entity.Tag)
		if err == nil {
			r := &result.Results[i]
			r.Status, r.Info, _, err = machine.Status()
		}
		result.Results[i].Error = common.ServerError(err)
	}
	return result, nil
}

// Series returns the deployed series for each given machine entity.
func (p *ProvisionerAPI) Series(args params.Entities) (params.StringResults, error) {
	result := params.StringResults{
		Results: make([]params.StringResult, len(args.Entities)),
	}
	canAccess, err := p.getAuthFunc()
	if err != nil {
		return result, err
	}
	for i, entity := range args.Entities {
		machine, err := p.getMachine(canAccess, entity.Tag)
		if err == nil {
			result.Results[i].Result = machine.Series()
		}
		result.Results[i].Error = common.ServerError(err)
	}
	return result, nil
}

// Constraints returns the constraints for each given machine entity.
func (p *ProvisionerAPI) Constraints(args params.Entities) (params.ConstraintsResults, error) {
	result := params.ConstraintsResults{
		Results: make([]params.ConstraintsResult, len(args.Entities)),
	}
	canAccess, err := p.getAuthFunc()
	if err != nil {
		return result, err
	}
	for i, entity := range args.Entities {
		machine, err := p.getMachine(canAccess, entity.Tag)
		if err == nil {
			var cons constraints.Value
			cons, err = machine.Constraints()
			if err == nil {
				result.Results[i].Constraints = cons
			}
		}
		result.Results[i].Error = common.ServerError(err)
	}
	return result, nil
}

// SetProvisioned sets the provider specific machine id, nonce and
// metadata for each given machine. Once set, the instance id cannot
// be changed.
func (p *ProvisionerAPI) SetProvisioned(args params.SetProvisioned) (params.ErrorResults, error) {
	result := params.ErrorResults{
		Results: make([]params.ErrorResult, len(args.Machines)),
	}
	canAccess, err := p.getAuthFunc()
	if err != nil {
		return result, err
	}
	for i, arg := range args.Machines {
		machine, err := p.getMachine(canAccess, arg.Tag)
		if err == nil {
			err = machine.SetProvisioned(arg.InstanceId, arg.Nonce, arg.Characteristics)
		}
		result.Results[i].Error = common.ServerError(err)
	}
	return result, nil
}

// InstanceId returns the provider specific instance id for each given
// machine or an CodeNotProvisioned error, if not set.
func (p *ProvisionerAPI) InstanceId(args params.Entities) (params.StringResults, error) {
	result := params.StringResults{
		Results: make([]params.StringResult, len(args.Entities)),
	}
	canAccess, err := p.getAuthFunc()
	if err != nil {
		return result, err
	}
	for i, entity := range args.Entities {
		machine, err := p.getMachine(canAccess, entity.Tag)
		if err == nil {
			var instanceId instance.Id
			instanceId, err = machine.InstanceId()
			if err == nil {
				result.Results[i].Result = string(instanceId)
			}
		}
		result.Results[i].Error = common.ServerError(err)
	}
	return result, nil
}

// WatchEnvironMachines returns a StringsWatcher that notifies of
// changes to the lifecycles of the machines (but not containers) in
// the current environment.
func (p *ProvisionerAPI) WatchEnvironMachines() (params.StringsWatchResult, error) {
	result := params.StringsWatchResult{}
	if !p.authorizer.AuthEnvironManager() {
		return result, common.ErrPerm
	}
	watch := p.st.WatchEnvironMachines()
	// Consume the initial event and forward it to the result.
	if changes, ok := <-watch.Changes(); ok {
		result.StringsWatcherId = p.resources.Register(watch)
		result.Changes = changes
	} else {
		err := watcher.MustErr(watch)
		return result, fmt.Errorf("cannot obtain initial environment machines: %v", err)
	}
	return result, nil
}
<commit_msg>Fix the docstring.<commit_after>// Copyright 2012, 2013 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package provisioner

import (
	"fmt"

	"launchpad.net/juju-core/constraints"
	"launchpad.net/juju-core/environs"
	"launchpad.net/juju-core/instance"
	"launchpad.net/juju-core/names"
	"launchpad.net/juju-core/state"
	"launchpad.net/juju-core/state/api/params"
	"launchpad.net/juju-core/state/apiserver/common"
	"launchpad.net/juju-core/state/watcher"
)

// ProvisionerAPI provides access to the Provisioner API facade.
type ProvisionerAPI struct {
	*common.Remover
	*common.StatusSetter
	*common.DeadEnsurer
	*common.PasswordChanger
	*common.LifeGetter
	*common.Addresser
	*common.ToolsGetter

	st          *state.State
	resources   *common.Resources
	authorizer  common.Authorizer
	getAuthFunc common.GetAuthFunc
}

// NewProvisionerAPI creates a new server-side ProvisionerAPI facade.
func NewProvisionerAPI(
	st *state.State,
	resources *common.Resources,
	authorizer common.Authorizer,
) (*ProvisionerAPI, error) {
	if !authorizer.AuthMachineAgent() && !authorizer.AuthEnvironManager() {
		return nil, common.ErrPerm
	}
	getAuthFunc := func() (common.AuthFunc, error) {
		isEnvironManager := authorizer.AuthEnvironManager()
		isMachineAgent := authorizer.AuthMachineAgent()
		authEntityTag := authorizer.GetAuthTag()

		return func(tag string) bool {
			if isMachineAgent && tag == authEntityTag {
				// A machine agent can always access its own machine.
				return true
			}
			_, id, err := names.ParseTag(tag, names.MachineTagKind)
			if err != nil {
				return false
			}
			parentId := state.ParentId(id)
			if parentId == "" {
				// All top-level machines are accessible by the
				// environment manager.
				return isEnvironManager
			}
			// All containers with the authenticated machine as a
			// parent are accessible by it.
			return isMachineAgent && names.MachineTag(parentId) == authEntityTag
		}, nil
	}
	return &ProvisionerAPI{
		Remover:         common.NewRemover(st, false, getAuthFunc),
		StatusSetter:    common.NewStatusSetter(st, getAuthFunc),
		DeadEnsurer:     common.NewDeadEnsurer(st, getAuthFunc),
		PasswordChanger: common.NewPasswordChanger(st, getAuthFunc),
		LifeGetter:      common.NewLifeGetter(st, getAuthFunc),
		Addresser:       common.NewAddresser(st),
		ToolsGetter:     common.NewToolsGetter(st, getAuthFunc),
		st:              st,
		resources:       resources,
		authorizer:      authorizer,
		getAuthFunc:     getAuthFunc,
	}, nil
}

func (p *ProvisionerAPI) getMachine(canAccess common.AuthFunc, tag string) (*state.Machine, error) {
	if !canAccess(tag) {
		return nil, common.ErrPerm
	}
	entity, err := p.st.FindEntity(tag)
	if err != nil {
		return nil, err
	}
	// The authorization function guarantees that the tag represents a
	// machine.
	return entity.(*state.Machine), nil
}

func (p *ProvisionerAPI) watchOneMachineContainers(arg params.WatchContainer) (params.StringsWatchResult, error) {
	nothing := params.StringsWatchResult{}
	canAccess, err := p.getAuthFunc()
	if err != nil {
		return nothing, err
	}
	if !canAccess(arg.MachineTag) {
		return nothing, common.ErrPerm
	}
	_, id, err := names.ParseTag(arg.MachineTag, names.MachineTagKind)
	if err != nil {
		return nothing, err
	}
	machine, err := p.st.Machine(id)
	if err != nil {
		return nothing, err
	}
	watch := machine.WatchContainers(instance.ContainerType(arg.ContainerType))
	// Consume the initial event and forward it to the result.
	if changes, ok := <-watch.Changes(); ok {
		return params.StringsWatchResult{
			StringsWatcherId: p.resources.Register(watch),
			Changes:          changes,
		}, nil
	}
	return nothing, watcher.MustErr(watch)
}

// WatchContainers starts a StringsWatcher to watch all containers deployed to
// any machine passed in args.
func (p *ProvisionerAPI) WatchContainers(args params.WatchContainers) (params.StringsWatchResults, error) {
	result := params.StringsWatchResults{
		Results: make([]params.StringsWatchResult, len(args.Params)),
	}
	for i, arg := range args.Params {
		watcherResult, err := p.watchOneMachineContainers(arg)
		result.Results[i] = watcherResult
		result.Results[i].Error = common.ServerError(err)
	}
	return result, nil
}

// WatchForEnvironConfigChanges returns a NotifyWatcher to observe
// changes to the environment configuration.
func (p *ProvisionerAPI) WatchForEnvironConfigChanges() (params.NotifyWatchResult, error) {
	result := params.NotifyWatchResult{}
	watch := p.st.WatchForEnvironConfigChanges()
	// Consume the initial event. Technically, API
	// calls to Watch 'transmit' the initial event
	// in the Watch response. But NotifyWatchers
	// have no state to transmit.
	if _, ok := <-watch.Changes(); ok {
		result.NotifyWatcherId = p.resources.Register(watch)
	} else {
		return result, watcher.MustErr(watch)
	}
	return result, nil
}

// EnvironConfig returns the current environment's configuration.
func (p *ProvisionerAPI) EnvironConfig() (params.EnvironConfigResult, error) {
	result := params.EnvironConfigResult{}
	config, err := p.st.EnvironConfig()
	if err != nil {
		return result, err
	}
	allAttrs := config.AllAttrs()
	if !p.authorizer.AuthEnvironManager() {
		// Mask out any secrets in the environment configuration
		// with values of the same type, so it'll pass validation.
		//
		// TODO(dimitern) 201309-26 bug #1231384
		// This needs to change so we won't return anything to
		// entities other than the environment manager, but the
		// provisioner code should be refactored first.
		env, err := environs.New(config)
		if err != nil {
			return result, err
		}
		secretAttrs, err := env.Provider().SecretAttrs(config)
		for k := range secretAttrs {
			allAttrs[k] = "not available"
		}
	}
	result.Config = allAttrs
	return result, nil
}

// ContainerConfig returns information from the environment config that are
// needed for container cloud-init.
func (p *ProvisionerAPI) ContainerConfig() (params.ContainerConfig, error) {
	result := params.ContainerConfig{}
	config, err := p.st.EnvironConfig()
	if err != nil {
		return result, err
	}
	result.ProviderType = config.Type()
	result.AuthorizedKeys = config.AuthorizedKeys()
	result.SSLHostnameVerification = config.SSLHostnameVerification()
	return result, nil
}

// Status returns the status of each given machine entity.
func (p *ProvisionerAPI) Status(args params.Entities) (params.StatusResults, error) {
	result := params.StatusResults{
		Results: make([]params.StatusResult, len(args.Entities)),
	}
	canAccess, err := p.getAuthFunc()
	if err != nil {
		return result, err
	}
	for i, entity := range args.Entities {
		machine, err := p.getMachine(canAccess, entity.Tag)
		if err == nil {
			r := &result.Results[i]
			r.Status, r.Info, _, err = machine.Status()
		}
		result.Results[i].Error = common.ServerError(err)
	}
	return result, nil
}

// Series returns the deployed series for each given machine entity.
func (p *ProvisionerAPI) Series(args params.Entities) (params.StringResults, error) {
	result := params.StringResults{
		Results: make([]params.StringResult, len(args.Entities)),
	}
	canAccess, err := p.getAuthFunc()
	if err != nil {
		return result, err
	}
	for i, entity := range args.Entities {
		machine, err := p.getMachine(canAccess, entity.Tag)
		if err == nil {
			result.Results[i].Result = machine.Series()
		}
		result.Results[i].Error = common.ServerError(err)
	}
	return result, nil
}

// Constraints returns the constraints for each given machine entity.
func (p *ProvisionerAPI) Constraints(args params.Entities) (params.ConstraintsResults, error) {
	result := params.ConstraintsResults{
		Results: make([]params.ConstraintsResult, len(args.Entities)),
	}
	canAccess, err := p.getAuthFunc()
	if err != nil {
		return result, err
	}
	for i, entity := range args.Entities {
		machine, err := p.getMachine(canAccess, entity.Tag)
		if err == nil {
			var cons constraints.Value
			cons, err = machine.Constraints()
			if err == nil {
				result.Results[i].Constraints = cons
			}
		}
		result.Results[i].Error = common.ServerError(err)
	}
	return result, nil
}

// SetProvisioned sets the provider specific machine id, nonce and
// metadata for each given machine. Once set, the instance id cannot
// be changed.
func (p *ProvisionerAPI) SetProvisioned(args params.SetProvisioned) (params.ErrorResults, error) {
	result := params.ErrorResults{
		Results: make([]params.ErrorResult, len(args.Machines)),
	}
	canAccess, err := p.getAuthFunc()
	if err != nil {
		return result, err
	}
	for i, arg := range args.Machines {
		machine, err := p.getMachine(canAccess, arg.Tag)
		if err == nil {
			err = machine.SetProvisioned(arg.InstanceId, arg.Nonce, arg.Characteristics)
		}
		result.Results[i].Error = common.ServerError(err)
	}
	return result, nil
}

// InstanceId returns the provider specific instance id for each given
// machine or an CodeNotProvisioned error, if not set.
func (p *ProvisionerAPI) InstanceId(args params.Entities) (params.StringResults, error) {
	result := params.StringResults{
		Results: make([]params.StringResult, len(args.Entities)),
	}
	canAccess, err := p.getAuthFunc()
	if err != nil {
		return result, err
	}
	for i, entity := range args.Entities {
		machine, err := p.getMachine(canAccess, entity.Tag)
		if err == nil {
			var instanceId instance.Id
			instanceId, err = machine.InstanceId()
			if err == nil {
				result.Results[i].Result = string(instanceId)
			}
		}
		result.Results[i].Error = common.ServerError(err)
	}
	return result, nil
}

// WatchEnvironMachines returns a StringsWatcher that notifies of
// changes to the lifecycles of the machines (but not containers) in
// the current environment.
func (p *ProvisionerAPI) WatchEnvironMachines() (params.StringsWatchResult, error) {
	result := params.StringsWatchResult{}
	if !p.authorizer.AuthEnvironManager() {
		return result, common.ErrPerm
	}
	watch := p.st.WatchEnvironMachines()
	// Consume the initial event and forward it to the result.
	if changes, ok := <-watch.Changes(); ok {
		result.StringsWatcherId = p.resources.Register(watch)
		result.Changes = changes
	} else {
		err := watcher.MustErr(watch)
		return result, fmt.Errorf("cannot obtain initial environment machines: %v", err)
	}
	return result, nil
}
<|endoftext|>


<commit_before>// Copyright (c) 2017, 2019, Oracle and/or its affiliates. All rights reserved.

package provider

import (
	"context"
	"fmt"
	"testing"
	"time"

	"github.com/hashicorp/terraform/helper/resource"
	"github.com/hashicorp/terraform/helper/schema"
	"github.com/hashicorp/terraform/terraform"
	"github.com/oracle/oci-go-sdk/common"
	oci_core "github.com/oracle/oci-go-sdk/core"

	"github.com/terraform-providers/terraform-provider-oci/httpreplay"
)

var (
	ImageRequiredOnlyResource = ImageResourceDependencies +
		generateResourceFromRepresentationMap("oci_core_image", "test_image", Required, Create, imageRepresentation)

	imageDataSourceRepresentation = map[string]interface{}{
		"compartment_id": Representation{repType: Required, create: `${var.compartment_id}`},
		"display_name":   Representation{repType: Optional, create: `MyCustomImage`, update: `displayName2`},
		"state":          Representation{repType: Optional, create: `AVAILABLE`},
		"filter":         RepresentationGroup{Required, imageDataSourceFilterRepresentation}}
	imageDataSourceFilterRepresentation = map[string]interface{}{
		"name":   Representation{repType: Required, create: `id`},
		"values": Representation{repType: Required, create: []string{`${oci_core_image.test_image.id}`}},
	}

	imageRepresentation = map[string]interface{}{
		"compartment_id": Representation{repType: Required, create: `${var.compartment_id}`},
		"defined_tags":   Representation{repType: Optional, create: `${map("${oci_identity_tag_namespace.tag-namespace1.name}.${oci_identity_tag.tag1.name}", "value")}`, update: `${map("${oci_identity_tag_namespace.tag-namespace1.name}.${oci_identity_tag.tag1.name}", "updatedValue")}`},
		"display_name":   Representation{repType: Optional, create: `MyCustomImage`, update: `displayName2`},
		"freeform_tags":  Representation{repType: Optional, create: map[string]string{"Department": "Finance"}, update: map[string]string{"Department": "Accounting"}},
		"instance_id":    Representation{repType: Required, create: `${oci_core_instance.test_instance.id}`},
		"launch_mode":    Representation{repType: Optional, create: `NATIVE`},
		"timeouts":       RepresentationGroup{Required, timeoutsRepresentation},
	}

	timeoutsRepresentation = map[string]interface{}{
		"create": Representation{repType: Required, create: `30m`},
	}

	ImageResourceDependencies = InstanceRequiredOnlyResource
)

func TestCoreImageResource_basic(t *testing.T) {
	httpreplay.SetScenario("TestCoreImageResource_basic")
	defer httpreplay.SaveScenario()

	provider := testAccProvider
	config := testProviderConfig()

	compartmentId := getEnvSettingWithBlankDefault("compartment_ocid")
	compartmentIdVariableStr := fmt.Sprintf("variable \"compartment_id\" { default = \"%s\" }\n", compartmentId)

	resourceName := "oci_core_image.test_image"
	datasourceName := "data.oci_core_images.test_images"

	var resId, resId2 string

	resource.Test(t, resource.TestCase{
		PreCheck: func() { testAccPreCheck(t) },
		Providers: map[string]terraform.ResourceProvider{
			"oci": provider,
		},
		CheckDestroy: testAccCheckCoreImageDestroy,
		Steps: []resource.TestStep{
			// verify create
			{
				Config: config + compartmentIdVariableStr + ImageResourceDependencies +
					generateResourceFromRepresentationMap("oci_core_image", "test_image", Required, Create, imageRepresentation),
				Check: resource.ComposeAggregateTestCheckFunc(
					resource.TestCheckResourceAttr(resourceName, "compartment_id", compartmentId),
					resource.TestCheckResourceAttrSet(resourceName, "instance_id"),

					func(s *terraform.State) (err error) {
						resId, err = fromInstanceState(s, resourceName, "id")
						return err
					},
				),
			},

			// delete before next create
			{
				Config: config + compartmentIdVariableStr + ImageResourceDependencies,
			},
			// verify create with optionals
			{
				Config: config + compartmentIdVariableStr + ImageResourceDependencies +
					generateResourceFromRepresentationMap("oci_core_image", "test_image", Optional, Create, imageRepresentation),
				Check: resource.ComposeAggregateTestCheckFunc(
					resource.TestCheckResourceAttr(resourceName, "compartment_id", compartmentId),
					resource.TestCheckResourceAttrSet(resourceName, "create_image_allowed"),
					resource.TestCheckResourceAttr(resourceName, "defined_tags.%", "1"),
					resource.TestCheckResourceAttr(resourceName, "display_name", "MyCustomImage"),
					resource.TestCheckResourceAttr(resourceName, "freeform_tags.%", "1"),
					resource.TestCheckResourceAttrSet(resourceName, "id"),
					resource.TestCheckResourceAttrSet(resourceName, "instance_id"),
					resource.TestCheckResourceAttr(resourceName, "launch_mode", "NATIVE"),
					resource.TestCheckResourceAttrSet(resourceName, "operating_system"),
					resource.TestCheckResourceAttrSet(resourceName, "operating_system_version"),
					resource.TestCheckResourceAttrSet(resourceName, "state"),
					resource.TestCheckResourceAttrSet(resourceName, "time_created"),

					func(s *terraform.State) (err error) {
						resId, err = fromInstanceState(s, resourceName, "id")
						return err
					},
				),
			},

			// verify updates to updatable parameters
			{
				Config: config + compartmentIdVariableStr + ImageResourceDependencies +
					generateResourceFromRepresentationMap("oci_core_image", "test_image", Optional, Update, imageRepresentation),
				Check: resource.ComposeAggregateTestCheckFunc(
					resource.TestCheckResourceAttr(resourceName, "compartment_id", compartmentId),
					resource.TestCheckResourceAttrSet(resourceName, "create_image_allowed"),
					resource.TestCheckResourceAttr(resourceName, "defined_tags.%", "1"),
					resource.TestCheckResourceAttr(resourceName, "display_name", "displayName2"),
					resource.TestCheckResourceAttr(resourceName, "freeform_tags.%", "1"),
					resource.TestCheckResourceAttrSet(resourceName, "id"),
					resource.TestCheckResourceAttrSet(resourceName, "instance_id"),
					resource.TestCheckResourceAttr(resourceName, "launch_mode", "NATIVE"),
					resource.TestCheckResourceAttrSet(resourceName, "operating_system"),
					resource.TestCheckResourceAttrSet(resourceName, "operating_system_version"),
					resource.TestCheckResourceAttrSet(resourceName, "state"),
					resource.TestCheckResourceAttrSet(resourceName, "time_created"),

					func(s *terraform.State) (err error) {
						resId2, err = fromInstanceState(s, resourceName, "id")
						if resId != resId2 {
							return fmt.Errorf("Resource recreated when it was supposed to be updated.")
						}
						return err
					},
				),
			},
			// verify datasource
			{
				Config: config +
					generateDataSourceFromRepresentationMap("oci_core_images", "test_images", Optional, Update, imageDataSourceRepresentation) +
					compartmentIdVariableStr + ImageResourceDependencies +
					generateResourceFromRepresentationMap("oci_core_image", "test_image", Optional, Update, imageRepresentation),
				Check: resource.ComposeAggregateTestCheckFunc(
					resource.TestCheckResourceAttr(datasourceName, "compartment_id", compartmentId),
					resource.TestCheckResourceAttr(datasourceName, "display_name", "displayName2"),
					resource.TestCheckResourceAttr(datasourceName, "state", "AVAILABLE"),

					resource.TestCheckResourceAttr(datasourceName, "images.#", "1"),
					resource.TestCheckResourceAttr(datasourceName, "images.0.agent_features.#", "0"),
					resource.TestCheckResourceAttrSet(datasourceName, "images.0.base_image_id"),
					resource.TestCheckResourceAttr(datasourceName, "images.0.compartment_id", compartmentId),
					resource.TestCheckResourceAttrSet(datasourceName, "images.0.create_image_allowed"),
					resource.TestCheckResourceAttr(datasourceName, "images.0.defined_tags.%", "1"),
					resource.TestCheckResourceAttr(datasourceName, "images.0.display_name", "displayName2"),
					resource.TestCheckResourceAttr(datasourceName, "images.0.freeform_tags.%", "1"),
					resource.TestCheckResourceAttrSet(datasourceName, "images.0.id"),
					resource.TestCheckResourceAttr(datasourceName, "images.0.launch_mode", "NATIVE"),
					resource.TestCheckResourceAttr(datasourceName, "images.0.launch_options.#", "1"),
					resource.TestCheckResourceAttrSet(datasourceName, "images.0.operating_system"),
					resource.TestCheckResourceAttrSet(datasourceName, "images.0.operating_system_version"),
					resource.TestCheckResourceAttrSet(datasourceName, "images.0.size_in_mbs"),
					resource.TestCheckResourceAttrSet(datasourceName, "images.0.state"),
					resource.TestCheckResourceAttrSet(datasourceName, "images.0.time_created"),
				),
			},
			// verify resource import
			{
				Config:            config,
				ImportState:       true,
				ImportStateVerify: true,
				ImportStateVerifyIgnore: []string{
					"image_source_details",
					"instance_id",
				},
				ResourceName: resourceName,
			},
		},
	})
}

func testAccCheckCoreImageDestroy(s *terraform.State) error {
	noResourceFound := true
	client := testAccProvider.Meta().(*OracleClients).computeClient
	for _, rs := range s.RootModule().Resources {
		if rs.Type == "oci_core_image" {
			noResourceFound = false
			request := oci_core.GetImageRequest{}

			tmp := rs.Primary.ID
			request.ImageId = &tmp

			request.RequestMetadata.RetryPolicy = getRetryPolicy(true, "core")

			response, err := client.GetImage(context.Background(), request)

			if err == nil {
				deletedLifecycleStates := map[string]bool{
					string(oci_core.ImageLifecycleStateDeleted): true,
				}
				if _, ok := deletedLifecycleStates[string(response.LifecycleState)]; !ok {
					//resource lifecycle state is not in expected deleted lifecycle states.
					return fmt.Errorf("resource lifecycle state: %s is not in expected deleted lifecycle states", response.LifecycleState)
				}
				//resource lifecycle state is in expected deleted lifecycle states. continue with next one.
				continue
			}

			//Verify that exception is for '404 not found'.
			if failure, isServiceError := common.IsServiceError(err); !isServiceError || failure.GetHTTPStatusCode() != 404 {
				return err
			}
		}
	}
	if noResourceFound {
		return fmt.Errorf("at least one resource was expected from the state file, but could not be found")
	}

	return nil
}

func init() {
	if DependencyGraph == nil {
		initDependencyGraph()
	}
	resource.AddTestSweepers("CoreImage", &resource.Sweeper{
		Name:         "CoreImage",
		Dependencies: DependencyGraph["image"],
		F:            sweepCoreImageResource,
	})
}

func sweepCoreImageResource(compartment string) error {
	computeClient := GetTestClients(&schema.ResourceData{}).computeClient
	imageIds, err := getImageIds(compartment)
	if err != nil {
		return err
	}
	for _, imageId := range imageIds {
		if ok := SweeperDefaultResourceId[imageId]; !ok {
			deleteImageRequest := oci_core.DeleteImageRequest{}

			deleteImageRequest.ImageId = &imageId

			deleteImageRequest.RequestMetadata.RetryPolicy = getRetryPolicy(true, "core")
			_, error := computeClient.DeleteImage(context.Background(), deleteImageRequest)
			if error != nil {
				fmt.Printf("Error deleting Image %s %s, It is possible that the resource is already deleted. Please verify manually \n", imageId, error)
				continue
			}
			waitTillCondition(testAccProvider, &imageId, imageSweepWaitCondition, time.Duration(3*time.Minute),
				imageSweepResponseFetchOperation, "core", true)
		}
	}
	return nil
}

func getImageIds(compartment string) ([]string, error) {
	ids := getResourceIdsToSweep(compartment, "ImageId")
	if ids != nil {
		return ids, nil
	}
	var resourceIds []string
	compartmentId := compartment
	computeClient := GetTestClients(&schema.ResourceData{}).computeClient

	listImagesRequest := oci_core.ListImagesRequest{}
	listImagesRequest.CompartmentId = &compartmentId
	listImagesRequest.LifecycleState = oci_core.ImageLifecycleStateAvailable
	listImagesResponse, err := computeClient.ListImages(context.Background(), listImagesRequest)

	if err != nil {
		return resourceIds, fmt.Errorf("Error getting Image list for compartment id : %s , %s \n", compartmentId, err)
	}
	for _, image := range listImagesResponse.Items {
		id := *image.Id
		resourceIds = append(resourceIds, id)
		addResourceIdToSweeperResourceIdMap(compartmentId, "ImageId", id)
	}
	return resourceIds, nil
}

func imageSweepWaitCondition(response common.OCIOperationResponse) bool {
	// Only stop if the resource is available beyond 3 mins. As there could be an issue for the sweeper to delete the resource and manual intervention required.
	if imageResponse, ok := response.Response.(oci_core.GetImageResponse); ok {
		return imageResponse.LifecycleState != oci_core.ImageLifecycleStateDeleted
	}
	return false
}

func imageSweepResponseFetchOperation(client *OracleClients, resourceId *string, retryPolicy *common.RetryPolicy) error {
	_, err := client.computeClient.GetImage(context.Background(), oci_core.GetImageRequest{
		ImageId: resourceId,
		RequestMetadata: common.RequestMetadata{
			RetryPolicy: retryPolicy,
		},
	})
	return err
}
<commit_msg>Fix sweeper clean up CoreImage<commit_after>// Copyright (c) 2017, 2019, Oracle and/or its affiliates. All rights reserved.

package provider

import (
	"context"
	"fmt"
	"testing"
	"time"

	"github.com/hashicorp/terraform/helper/resource"
	"github.com/hashicorp/terraform/helper/schema"
	"github.com/hashicorp/terraform/terraform"
	"github.com/oracle/oci-go-sdk/common"
	oci_core "github.com/oracle/oci-go-sdk/core"

	"github.com/terraform-providers/terraform-provider-oci/httpreplay"
)

var (
	ImageRequiredOnlyResource = ImageResourceDependencies +
		generateResourceFromRepresentationMap("oci_core_image", "test_image", Required, Create, imageRepresentation)

	imageDataSourceRepresentation = map[string]interface{}{
		"compartment_id": Representation{repType: Required, create: `${var.compartment_id}`},
		"display_name":   Representation{repType: Optional, create: `MyCustomImage`, update: `displayName2`},
		"state":          Representation{repType: Optional, create: `AVAILABLE`},
		"filter":         RepresentationGroup{Required, imageDataSourceFilterRepresentation}}
	imageDataSourceFilterRepresentation = map[string]interface{}{
		"name":   Representation{repType: Required, create: `id`},
		"values": Representation{repType: Required, create: []string{`${oci_core_image.test_image.id}`}},
	}

	imageRepresentation = map[string]interface{}{
		"compartment_id": Representation{repType: Required, create: `${var.compartment_id}`},
		"defined_tags":   Representation{repType: Optional, create: `${map("${oci_identity_tag_namespace.tag-namespace1.name}.${oci_identity_tag.tag1.name}", "value")}`, update: `${map("${oci_identity_tag_namespace.tag-namespace1.name}.${oci_identity_tag.tag1.name}", "updatedValue")}`},
		"display_name":   Representation{repType: Optional, create: `MyCustomImage`, update: `displayName2`},
		"freeform_tags":  Representation{repType: Optional, create: map[string]string{"Department": "Finance"}, update: map[string]string{"Department": "Accounting"}},
		"instance_id":    Representation{repType: Required, create: `${oci_core_instance.test_instance.id}`},
		"launch_mode":    Representation{repType: Optional, create: `NATIVE`},
		"timeouts":       RepresentationGroup{Required, timeoutsRepresentation},
	}

	timeoutsRepresentation = map[string]interface{}{
		"create": Representation{repType: Required, create: `30m`},
	}

	ImageResourceDependencies = InstanceRequiredOnlyResource
)

func TestCoreImageResource_basic(t *testing.T) {
	httpreplay.SetScenario("TestCoreImageResource_basic")
	defer httpreplay.SaveScenario()

	provider := testAccProvider
	config := testProviderConfig()

	compartmentId := getEnvSettingWithBlankDefault("compartment_ocid")
	compartmentIdVariableStr := fmt.Sprintf("variable \"compartment_id\" { default = \"%s\" }\n", compartmentId)

	resourceName := "oci_core_image.test_image"
	datasourceName := "data.oci_core_images.test_images"

	var resId, resId2 string

	resource.Test(t, resource.TestCase{
		PreCheck: func() { testAccPreCheck(t) },
		Providers: map[string]terraform.ResourceProvider{
			"oci": provider,
		},
		CheckDestroy: testAccCheckCoreImageDestroy,
		Steps: []resource.TestStep{
			// verify create
			{
				Config: config + compartmentIdVariableStr + ImageResourceDependencies +
					generateResourceFromRepresentationMap("oci_core_image", "test_image", Required, Create, imageRepresentation),
				Check: resource.ComposeAggregateTestCheckFunc(
					resource.TestCheckResourceAttr(resourceName, "compartment_id", compartmentId),
					resource.TestCheckResourceAttrSet(resourceName, "instance_id"),

					func(s *terraform.State) (err error) {
						resId, err = fromInstanceState(s, resourceName, "id")
						return err
					},
				),
			},

			// delete before next create
			{
				Config: config + compartmentIdVariableStr + ImageResourceDependencies,
			},
			// verify create with optionals
			{
				Config: config + compartmentIdVariableStr + ImageResourceDependencies +
					generateResourceFromRepresentationMap("oci_core_image", "test_image", Optional, Create, imageRepresentation),
				Check: resource.ComposeAggregateTestCheckFunc(
					resource.TestCheckResourceAttr(resourceName, "compartment_id", compartmentId),
					resource.TestCheckResourceAttrSet(resourceName, "create_image_allowed"),
					resource.TestCheckResourceAttr(resourceName, "defined_tags.%", "1"),
					resource.TestCheckResourceAttr(resourceName, "display_name", "MyCustomImage"),
					resource.TestCheckResourceAttr(resourceName, "freeform_tags.%", "1"),
					resource.TestCheckResourceAttrSet(resourceName, "id"),
					resource.TestCheckResourceAttrSet(resourceName, "instance_id"),
					resource.TestCheckResourceAttr(resourceName, "launch_mode", "NATIVE"),
					resource.TestCheckResourceAttrSet(resourceName, "operating_system"),
					resource.TestCheckResourceAttrSet(resourceName, "operating_system_version"),
					resource.TestCheckResourceAttrSet(resourceName, "state"),
					resource.TestCheckResourceAttrSet(resourceName, "time_created"),

					func(s *terraform.State) (err error) {
						resId, err = fromInstanceState(s, resourceName, "id")
						return err
					},
				),
			},

			// verify updates to updatable parameters
			{
				Config: config + compartmentIdVariableStr + ImageResourceDependencies +
					generateResourceFromRepresentationMap("oci_core_image", "test_image", Optional, Update, imageRepresentation),
				Check: resource.ComposeAggregateTestCheckFunc(
					resource.TestCheckResourceAttr(resourceName, "compartment_id", compartmentId),
					resource.TestCheckResourceAttrSet(resourceName, "create_image_allowed"),
					resource.TestCheckResourceAttr(resourceName, "defined_tags.%", "1"),
					resource.TestCheckResourceAttr(resourceName, "display_name", "displayName2"),
					resource.TestCheckResourceAttr(resourceName, "freeform_tags.%", "1"),
					resource.TestCheckResourceAttrSet(resourceName, "id"),
					resource.TestCheckResourceAttrSet(resourceName, "instance_id"),
					resource.TestCheckResourceAttr(resourceName, "launch_mode", "NATIVE"),
					resource.TestCheckResourceAttrSet(resourceName, "operating_system"),
					resource.TestCheckResourceAttrSet(resourceName, "operating_system_version"),
					resource.TestCheckResourceAttrSet(resourceName, "state"),
					resource.TestCheckResourceAttrSet(resourceName, "time_created"),

					func(s *terraform.State) (err error) {
						resId2, err = fromInstanceState(s, resourceName, "id")
						if resId != resId2 {
							return fmt.Errorf("Resource recreated when it was supposed to be updated.")
						}
						return err
					},
				),
			},
			// verify datasource
			{
				Config: config +
					generateDataSourceFromRepresentationMap("oci_core_images", "test_images", Optional, Update, imageDataSourceRepresentation) +
					compartmentIdVariableStr + ImageResourceDependencies +
					generateResourceFromRepresentationMap("oci_core_image", "test_image", Optional, Update, imageRepresentation),
				Check: resource.ComposeAggregateTestCheckFunc(
					resource.TestCheckResourceAttr(datasourceName, "compartment_id", compartmentId),
					resource.TestCheckResourceAttr(datasourceName, "display_name", "displayName2"),
					resource.TestCheckResourceAttr(datasourceName, "state", "AVAILABLE"),

					resource.TestCheckResourceAttr(datasourceName, "images.#", "1"),
					resource.TestCheckResourceAttr(datasourceName, "images.0.agent_features.#", "0"),
					resource.TestCheckResourceAttrSet(datasourceName, "images.0.base_image_id"),
					resource.TestCheckResourceAttr(datasourceName, "images.0.compartment_id", compartmentId),
					resource.TestCheckResourceAttrSet(datasourceName, "images.0.create_image_allowed"),
					resource.TestCheckResourceAttr(datasourceName, "images.0.defined_tags.%", "1"),
					resource.TestCheckResourceAttr(datasourceName, "images.0.display_name", "displayName2"),
					resource.TestCheckResourceAttr(datasourceName, "images.0.freeform_tags.%", "1"),
					resource.TestCheckResourceAttrSet(datasourceName, "images.0.id"),
					resource.TestCheckResourceAttr(datasourceName, "images.0.launch_mode", "NATIVE"),
					resource.TestCheckResourceAttr(datasourceName, "images.0.launch_options.#", "1"),
					resource.TestCheckResourceAttrSet(datasourceName, "images.0.operating_system"),
					resource.TestCheckResourceAttrSet(datasourceName, "images.0.operating_system_version"),
					resource.TestCheckResourceAttrSet(datasourceName, "images.0.size_in_mbs"),
					resource.TestCheckResourceAttrSet(datasourceName, "images.0.state"),
					resource.TestCheckResourceAttrSet(datasourceName, "images.0.time_created"),
				),
			},
			// verify resource import
			{
				Config:            config,
				ImportState:       true,
				ImportStateVerify: true,
				ImportStateVerifyIgnore: []string{
					"image_source_details",
					"instance_id",
				},
				ResourceName: resourceName,
			},
		},
	})
}

func testAccCheckCoreImageDestroy(s *terraform.State) error {
	noResourceFound := true
	client := testAccProvider.Meta().(*OracleClients).computeClient
	for _, rs := range s.RootModule().Resources {
		if rs.Type == "oci_core_image" {
			noResourceFound = false
			request := oci_core.GetImageRequest{}

			tmp := rs.Primary.ID
			request.ImageId = &tmp

			request.RequestMetadata.RetryPolicy = getRetryPolicy(true, "core")

			response, err := client.GetImage(context.Background(), request)

			if err == nil {
				deletedLifecycleStates := map[string]bool{
					string(oci_core.ImageLifecycleStateDeleted): true,
				}
				if _, ok := deletedLifecycleStates[string(response.LifecycleState)]; !ok {
					//resource lifecycle state is not in expected deleted lifecycle states.
					return fmt.Errorf("resource lifecycle state: %s is not in expected deleted lifecycle states", response.LifecycleState)
				}
				//resource lifecycle state is in expected deleted lifecycle states. continue with next one.
				continue
			}

			//Verify that exception is for '404 not found'.
			if failure, isServiceError := common.IsServiceError(err); !isServiceError || failure.GetHTTPStatusCode() != 404 {
				return err
			}
		}
	}
	if noResourceFound {
		return fmt.Errorf("at least one resource was expected from the state file, but could not be found")
	}

	return nil
}

func init() {
	if DependencyGraph == nil {
		initDependencyGraph()
	}
	resource.AddTestSweepers("CoreImage", &resource.Sweeper{
		Name:         "CoreImage",
		Dependencies: DependencyGraph["image"],
		F:            sweepCoreImageResource,
	})
}

func sweepCoreImageResource(compartment string) error {
	computeClient := GetTestClients(&schema.ResourceData{}).computeClient
	imageIds, err := getImageIds(compartment)
	if err != nil {
		return err
	}
	for _, imageId := range imageIds {
		if ok := SweeperDefaultResourceId[imageId]; !ok {
			deleteImageRequest := oci_core.DeleteImageRequest{}

			deleteImageRequest.ImageId = &imageId

			deleteImageRequest.RequestMetadata.RetryPolicy = getRetryPolicy(true, "core")
			_, error := computeClient.DeleteImage(context.Background(), deleteImageRequest)
			if error != nil {
				fmt.Printf("Error deleting Image %s %s, It is possible that the resource is already deleted. Please verify manually \n", imageId, error)
				continue
			}
			waitTillCondition(testAccProvider, &imageId, imageSweepWaitCondition, time.Duration(3*time.Minute),
				imageSweepResponseFetchOperation, "core", true)
		}
	}
	return nil
}

func getImageIds(compartment string) ([]string, error) {
	ids := getResourceIdsToSweep(compartment, "ImageId")
	if ids != nil {
		return ids, nil
	}
	var resourceIds []string
	compartmentId := compartment
	computeClient := GetTestClients(&schema.ResourceData{}).computeClient

	listImagesRequest := oci_core.ListImagesRequest{}
	listImagesRequest.CompartmentId = &compartmentId
	listImagesRequest.LifecycleState = oci_core.ImageLifecycleStateAvailable
	listImagesResponse, err := computeClient.ListImages(context.Background(), listImagesRequest)

	if err != nil {
		return resourceIds, fmt.Errorf("Error getting Image list for compartment id : %s , %s \n", compartmentId, err)
	}
	for _, image := range listImagesResponse.Items {
		if image.CompartmentId != nil && *image.CompartmentId == compartment && image.BaseImageId != nil {
			id := *image.Id
			resourceIds = append(resourceIds, id)
			addResourceIdToSweeperResourceIdMap(compartmentId, "ImageId", id)
		}
	}
	return resourceIds, nil
}

func imageSweepWaitCondition(response common.OCIOperationResponse) bool {
	// Only stop if the resource is available beyond 3 mins. As there could be an issue for the sweeper to delete the resource and manual intervention required.
	if imageResponse, ok := response.Response.(oci_core.GetImageResponse); ok {
		return imageResponse.LifecycleState != oci_core.ImageLifecycleStateDeleted
	}
	return false
}

func imageSweepResponseFetchOperation(client *OracleClients, resourceId *string, retryPolicy *common.RetryPolicy) error {
	_, err := client.computeClient.GetImage(context.Background(), oci_core.GetImageRequest{
		ImageId: resourceId,
		RequestMetadata: common.RequestMetadata{
			RetryPolicy: retryPolicy,
		},
	})
	return err
}
<|endoftext|>


<commit_before>package db

import (
	"fmt"
	"io"
	"time"

	"github.com/lestrrat/go-pdebug"
	"github.com/pkg/errors"
)

func compileRangeWhere(dst io.Writer, args *[]interface{}, since int64, rangeStart, rangeEnd time.Time) error {
	where := getStmtBuf()
	defer releaseStmtBuf(where)

	where.WriteString(ConferenceTable)
	where.WriteString(".oid > ?")
	*args = append(*args, since)

	if !rangeStart.IsZero() {
		if where.Len() > 0 {
			where.WriteString(" AND ")
		}
		where.WriteString(ConferenceDateTable)
		where.WriteString(".date >= ?")
		*args = append(*args, rangeStart)
	}

	if !rangeEnd.IsZero() {
		if where.Len() > 0 {
			where.WriteString(" AND ")
		}
		where.WriteString(ConferenceDateTable)
		where.WriteString(".date <= ?")
		*args = append(*args, rangeEnd)
	}

	toWrite := where.Len()
	n, err := where.WriteTo(dst)
	if n != int64(toWrite) {
		if err != nil {
			return errors.Wrap(err, "failed to write where clause to destination")
		}
		return errors.New("failed to write entire where clause to destination")
	}
	return nil
}

func (v *ConferenceList) LoadByStatusAndRange(tx *Tx, status string, since string, rangeStart, rangeEnd time.Time, limit int) error {
	// We need the oid of "since"
	var sinceOID int64
	if since != "" {
		var vdb Conference
		if err := vdb.LoadByEID(tx, since); err != nil {
			return errors.Wrap(err, "failed to load reference row")
		}
		sinceOID = vdb.OID
	}

	qbuf := getStmtBuf()
	defer releaseStmtBuf(qbuf)

	var hasDate bool
	if !rangeStart.IsZero() || !rangeEnd.IsZero() {
		hasDate = true
	}

	qbuf.WriteString(`SELECT `)
	qbuf.WriteString(ConferenceStdSelectColumns)
	qbuf.WriteString(` FROM `)
	qbuf.WriteString(ConferenceTable)
	if hasDate {
		qbuf.WriteString(` JOIN `)
		qbuf.WriteString(ConferenceDateTable)
		qbuf.WriteString(` ON `)
		qbuf.WriteString(ConferenceTable)
		qbuf.WriteString(`.eid = `)
		qbuf.WriteString(ConferenceDateTable)
		qbuf.WriteString(`.conference_id `)
	}

	qbuf.WriteString(` WHERE `)

	var args []interface{}
	if err := compileRangeWhere(qbuf, &args, sinceOID, rangeStart, rangeEnd); err != nil {
		return errors.Wrap(err, "failed to compile range where clause")
	}
	qbuf.WriteString(` AND `)
	qbuf.WriteString(ConferenceTable)
	qbuf.WriteString(`.status = ?`)
	args = append(args, status)

	qbuf.WriteString(` ORDER BY oid DESC`)
	fmt.Fprintf(qbuf, " LIMIT %d", limit)

pdebug.Printf("%s", qbuf.String())
pdebug.Printf("%#v", args)

	return v.execSQLAndExtract(tx, qbuf.String(), limit, args...)
}

func (v *ConferenceList) LoadByRange(tx *Tx, since string, rangeStart, rangeEnd time.Time, limit int) error {
	// We need the oid of "since"
	var sinceOID int64
	if since != "" {
		var vdb Conference
		if err := vdb.LoadByEID(tx, since); err != nil {
			return errors.Wrap(err, "failed to load reference row")
		}
		sinceOID = vdb.OID
	}

	// Use JOIN later
	var args []interface{}
	qbuf := getStmtBuf()
	defer releaseStmtBuf(qbuf)

	var hasDate bool
	if !rangeStart.IsZero() || !rangeEnd.IsZero() {
		hasDate = true
	}

	qbuf.WriteString(`SELECT `)
	qbuf.WriteString(ConferenceStdSelectColumns)
	qbuf.WriteString(` FROM `)
	qbuf.WriteString(ConferenceTable)
	if hasDate {
		qbuf.WriteString(` JOIN `)
		qbuf.WriteString(ConferenceDateTable)
		qbuf.WriteString(` ON `)
		qbuf.WriteString(ConferenceTable)
		qbuf.WriteString(`.eid = `)
		qbuf.WriteString(ConferenceDateTable)
		qbuf.WriteString(`.conference_id `)
	}

	qbuf.WriteString(` WHERE `)
	if err := compileRangeWhere(qbuf, &args, sinceOID, rangeStart, rangeEnd); err != nil {
		return errors.Wrap(err, "failed to compile range where clause")
	}

	if hasDate {
		qbuf.WriteString(` ORDER BY `)
		qbuf.WriteString(ConferenceDateTable)
		qbuf.WriteString(`.date DESC`)
	} else {
		qbuf.WriteString(` ORDER BY oid DESC`)
	}

	fmt.Fprintf(qbuf, " LIMIT %d", limit)
	return v.execSQLAndExtract(tx, qbuf.String(), limit, args...)
}

func (v *ConferenceList) execSQLAndExtract(tx *Tx, sql string, limit int, args ...interface{}) error {
	rows, err := tx.Query(sql, args...)
	if err != nil {
		return err
	}

	res := make(ConferenceList, 0, limit)
	for rows.Next() {
		row := Conference{}
		if err := row.Scan(rows); err != nil {
			return err
		}
		res = append(res, row)
	}
	*v = res
	return nil
}

func ListConferencesByOrganizer(tx *Tx, l *ConferenceList, orgID, since string, limit int) error {
	stmt := getStmtBuf()
	defer releaseStmtBuf(stmt)

	stmt.WriteString(`SELECT `)
	stmt.WriteString(ConferenceStdSelectColumns)
	stmt.WriteString(` FROM `)
	stmt.WriteString(ConferenceTable)
	stmt.WriteString(` JOIN `)
	stmt.WriteString(ConferenceAdministratorTable)
	stmt.WriteString(` ON `)
	stmt.WriteString(ConferenceTable)
	stmt.WriteString(`.eid = `)
	stmt.WriteString(ConferenceAdministratorTable)
	stmt.WriteString(`.conference_id WHERE `)
	stmt.WriteString(ConferenceAdministratorTable)
	stmt.WriteString(`.user_id = ?`)
	if since != "" {
		// Unimplemented
	}
	if limit > 0 {
		// Unimplemented
	}

	rows, err := tx.Query(stmt.String(), orgID)
	if err != nil {
		return errors.Wrap(err, "failed to execute query")
	}
	res := make(ConferenceList, 0, limit)
	for rows.Next() {
		row := Conference{}
		if err := row.Scan(rows); err != nil {
			return err
		}
		res = append(res, row)
	}
	*l = res
	return nil
}
<commit_msg>take status into consideration<commit_after>package db

import (
	"fmt"
	"io"
	"time"

	"github.com/lestrrat/go-pdebug"
	"github.com/pkg/errors"
)

func compileRangeWhere(dst io.Writer, args *[]interface{}, since int64, rangeStart, rangeEnd time.Time) error {
	where := getStmtBuf()
	defer releaseStmtBuf(where)

	where.WriteString(ConferenceTable)
	where.WriteString(".oid > ?")
	*args = append(*args, since)

	if !rangeStart.IsZero() {
		if where.Len() > 0 {
			where.WriteString(" AND ")
		}
		where.WriteString(ConferenceDateTable)
		where.WriteString(".date >= ?")
		*args = append(*args, rangeStart)
	}

	if !rangeEnd.IsZero() {
		if where.Len() > 0 {
			where.WriteString(" AND ")
		}
		where.WriteString(ConferenceDateTable)
		where.WriteString(".date <= ?")
		*args = append(*args, rangeEnd)
	}

	toWrite := where.Len()
	n, err := where.WriteTo(dst)
	if n != int64(toWrite) {
		if err != nil {
			return errors.Wrap(err, "failed to write where clause to destination")
		}
		return errors.New("failed to write entire where clause to destination")
	}
	return nil
}

func (v *ConferenceList) LoadByStatusAndRange(tx *Tx, status string, since string, rangeStart, rangeEnd time.Time, limit int) error {
	// We need the oid of "since"
	var sinceOID int64
	if since != "" {
		var vdb Conference
		if err := vdb.LoadByEID(tx, since); err != nil {
			return errors.Wrap(err, "failed to load reference row")
		}
		sinceOID = vdb.OID
	}

	qbuf := getStmtBuf()
	defer releaseStmtBuf(qbuf)

	var hasDate bool
	if !rangeStart.IsZero() || !rangeEnd.IsZero() {
		hasDate = true
	}

	qbuf.WriteString(`SELECT `)
	qbuf.WriteString(ConferenceStdSelectColumns)
	qbuf.WriteString(` FROM `)
	qbuf.WriteString(ConferenceTable)
	if hasDate {
		qbuf.WriteString(` JOIN `)
		qbuf.WriteString(ConferenceDateTable)
		qbuf.WriteString(` ON `)
		qbuf.WriteString(ConferenceTable)
		qbuf.WriteString(`.eid = `)
		qbuf.WriteString(ConferenceDateTable)
		qbuf.WriteString(`.conference_id `)
	}

	qbuf.WriteString(` WHERE `)

	var args []interface{}
	if err := compileRangeWhere(qbuf, &args, sinceOID, rangeStart, rangeEnd); err != nil {
		return errors.Wrap(err, "failed to compile range where clause")
	}
	qbuf.WriteString(` AND `)
	qbuf.WriteString(ConferenceTable)
	qbuf.WriteString(`.status = ?`)
	args = append(args, status)

	qbuf.WriteString(` ORDER BY oid DESC`)
	fmt.Fprintf(qbuf, " LIMIT %d", limit)

pdebug.Printf("%s", qbuf.String())
pdebug.Printf("%#v", args)

	return v.execSQLAndExtract(tx, qbuf.String(), limit, args...)
}

func (v *ConferenceList) LoadByRange(tx *Tx, since string, rangeStart, rangeEnd time.Time, limit int) error {
	// We need the oid of "since"
	var sinceOID int64
	if since != "" {
		var vdb Conference
		if err := vdb.LoadByEID(tx, since); err != nil {
			return errors.Wrap(err, "failed to load reference row")
		}
		sinceOID = vdb.OID
	}

	// Use JOIN later
	var args []interface{}
	qbuf := getStmtBuf()
	defer releaseStmtBuf(qbuf)

	var hasDate bool
	if !rangeStart.IsZero() || !rangeEnd.IsZero() {
		hasDate = true
	}

	qbuf.WriteString(`SELECT `)
	qbuf.WriteString(ConferenceStdSelectColumns)
	qbuf.WriteString(` FROM `)
	qbuf.WriteString(ConferenceTable)
	if hasDate {
		qbuf.WriteString(` JOIN `)
		qbuf.WriteString(ConferenceDateTable)
		qbuf.WriteString(` ON `)
		qbuf.WriteString(ConferenceTable)
		qbuf.WriteString(`.eid = `)
		qbuf.WriteString(ConferenceDateTable)
		qbuf.WriteString(`.conference_id `)
	}

	qbuf.WriteString(` WHERE `)
	if err := compileRangeWhere(qbuf, &args, sinceOID, rangeStart, rangeEnd); err != nil {
		return errors.Wrap(err, "failed to compile range where clause")
	}

	if hasDate {
		qbuf.WriteString(` ORDER BY `)
		qbuf.WriteString(ConferenceDateTable)
		qbuf.WriteString(`.date DESC`)
	} else {
		qbuf.WriteString(` ORDER BY oid DESC`)
	}

	fmt.Fprintf(qbuf, " LIMIT %d", limit)
	return v.execSQLAndExtract(tx, qbuf.String(), limit, args...)
}

func (v *ConferenceList) execSQLAndExtract(tx *Tx, sql string, limit int, args ...interface{}) error {
	rows, err := tx.Query(sql, args...)
	if err != nil {
		return err
	}

	res := make(ConferenceList, 0, limit)
	for rows.Next() {
		row := Conference{}
		if err := row.Scan(rows); err != nil {
			return err
		}
		res = append(res, row)
	}
	*v = res
	return nil
}

func ListConferencesByOrganizer(tx *Tx, l *ConferenceList, orgID, since string, limit int) error {
	stmt := getStmtBuf()
	defer releaseStmtBuf(stmt)

	stmt.WriteString(`SELECT `)
	stmt.WriteString(ConferenceStdSelectColumns)
	stmt.WriteString(` FROM `)
	stmt.WriteString(ConferenceTable)
	stmt.WriteString(` JOIN `)
	stmt.WriteString(ConferenceAdministratorTable)
	stmt.WriteString(` ON `)
	stmt.WriteString(ConferenceTable)
	stmt.WriteString(`.eid = `)
	stmt.WriteString(ConferenceAdministratorTable)
	stmt.WriteString(`.conference_id WHERE `)
	stmt.WriteString(ConferenceTable)
	stmt.WriteString(`.status != "private" AND `)
	stmt.WriteString(ConferenceAdministratorTable)
	stmt.WriteString(`.user_id = ? `)
	if since != "" {
		// Unimplemented
	}
	if limit > 0 {
		// Unimplemented
	}

	rows, err := tx.Query(stmt.String(), orgID)
	if err != nil {
		return errors.Wrap(err, "failed to execute query")
	}
	res := make(ConferenceList, 0, limit)
	for rows.Next() {
		row := Conference{}
		if err := row.Scan(rows); err != nil {
			return err
		}
		res = append(res, row)
	}
	*l = res
	return nil
}
<|endoftext|>


<commit_before>// Package tasks provides functions for scheduling
// periodic tasks (e.g. background jobs).
package tasks

import (
	"bytes"
	"errors"
	"fmt"
	"reflect"
	"sync"
	"time"

	"gnd.la/app"
	"gnd.la/internal/runtimeutil"
	"gnd.la/log"
	"gnd.la/signal"
)

var running struct {
	sync.Mutex
	tasks map[*Task]int
}

var registered struct {
	sync.RWMutex
	tasks map[string]*Task
}

var onListenTasks struct {
	sync.RWMutex
	tasks []*Task
}

// Task represent a scheduled task.
type Task struct {
	App      *app.App
	Handler  app.Handler
	Interval time.Duration
	Options  *Options
	ticker   *time.Ticker
	stop     chan struct{}
	stopped  chan struct{}
}

// Stop de-schedules the task. After stopping the task, it
// won't be started again but if it's currently running, it will
// be completed.
func (t *Task) Stop() {
	if t.stop != nil {
		t.stop <- struct{}{}
		<-t.stopped
		close(t.stopped)
		t.stopped = nil
	}
}

func (t *Task) Resume(now bool) {
	t.Stop()
	t.ticker = time.NewTicker(t.Interval)
	t.stop = make(chan struct{}, 1)
	t.stopped = make(chan struct{}, 1)
	go t.execute(now)
}

// Name returns the task name.
func (t *Task) Name() string {
	if t.Options != nil && t.Options.Name != "" {
		return t.Options.Name
	}
	return runtimeutil.FuncName(t.Handler)
}

// Delete stops the task by calling t.Stop() and then removes
// it from the internal task register.
func (t *Task) Delete() {
	registered.Lock()
	defer registered.Unlock()
	t.deleteLocked()
}

func (t *Task) deleteLocked() {
	t.Stop()
	delete(registered.tasks, t.Name())
}

func (t *Task) execute(now bool) {
	if now {
		t.executeTask()
	}
	for {
		c := t.ticker.C
		select {
		case <-c:
			t.executeTask()
		case <-t.stop:
			close(t.stop)
			t.stop = nil
			t.ticker.Stop()
			t.ticker = nil
			t.stopped <- struct{}{}
			return
		}
	}
}

func (t *Task) executeTask() {
	if err := startTask(t); err != nil {
		log.Error(err)
	}
}

// Options are used to specify task options when registering them.
type Options struct {
	// Name indicates the task name, used for checking the number
	// of instances running. If the task name is not provided, it's
	// derived from the function. Two tasks with the same name are
	// considered as equal, even if their functions are different.
	Name string
	// MaxInstances indicates the maximum number of instances of
	// this function that can be simultaneously running. If zero,
	// there is no limit.
	MaxInstances int
}

func afterTask(task *Task, started time.Time, terr *error) {
	name := task.Name()
	if err := recover(); err != nil {
		skip, stackSkip, _, _ := runtimeutil.GetPanic()
		var buf bytes.Buffer
		fmt.Fprintf(&buf, "Panic executing task %s: %v\n", name, err)
		stack := runtimeutil.FormatStack(stackSkip)
		location, code := runtimeutil.FormatCaller(skip, 5, true, true)
		if location != "" {
			buf.WriteString("\n At ")
			buf.WriteString(location)
			if code != "" {
				buf.WriteByte('\n')
				buf.WriteString(code)
				buf.WriteByte('\n')
			}
		}
		if stack != "" {
			buf.WriteString("\nStack:\n")
			buf.WriteString(stack)
		}
		*terr = errors.New(buf.String())
	}
	end := time.Now()
	running.Lock()
	defer running.Unlock()
	c := running.tasks[task] - 1
	if c > 0 {
		running.tasks[task] = c
	} else {
		delete(running.tasks, task)
	}
	log.Debugf("Finished task %s (%d instances now running) at %v (took %v)", name, c, end, end.Sub(started))
}

func numberOfInstances(task *Task) (int, error) {
	running.Lock()
	defer running.Unlock()
	c := running.tasks[task]
	if task.Options != nil && task.Options.MaxInstances > 0 {
		if c >= task.Options.MaxInstances {
			return 0, fmt.Errorf("not starting task %s because it's already running %d instances", task.Name(), c)
		}
	}
	if running.tasks == nil {
		running.tasks = make(map[*Task]int)
	}
	c++
	running.tasks[task] = c
	return c, nil
}

func executeTask(ctx *app.Context, task *Task) (ran bool, err error) {
	var n int
	if n, err = numberOfInstances(task); err != nil {
		return
	}
	started := time.Now()
	log.Debugf("Starting task %s (%d instances now running) at %v", task.Name(), n, started)
	ran = true
	defer afterTask(task, started, &err)
	task.Handler(ctx)
	return
}

// Register registers a new task that might be run with Run, but
// without scheduling it. If there was previously another task
// registered with the same name, it will be deleted.
func Register(m *app.App, task app.Handler, opts *Options) *Task {
	t := &Task{App: m, Handler: task, Options: opts}
	registered.Lock()
	defer registered.Unlock()
	if registered.tasks == nil {
		registered.tasks = make(map[string]*Task)
	}
	name := t.Name()
	if prev := registered.tasks[name]; prev != nil {
		log.Debugf("There's already a task registered as %s, deleting it", name)
		prev.deleteLocked()
	}
	registered.tasks[name] = t
	return t
}

// Schedule registers and schedules a task to be run at the given
// interval. If interval is 0, the task is only registered, but not
// scheduled. The onListen argument indicates if the task should also run
// (in its own goroutine) as soon as the app starts listening rather than
// waiting until interval for the first run. Note that on App Engine, the task
// will be started when the first cron request comes in (these are usually scheduled
// to run once a minute).
//
// Schedule returns a Task instance, which might be used to stop, resume or delete a it.
func Schedule(m *app.App, task app.Handler, opts *Options, interval time.Duration, onListen bool) *Task {
	t := Register(m, task, opts)
	t.Interval = interval
	go t.Resume(false)
	if onListen {
		onListenTasks.Lock()
		onListenTasks.tasks = append(onListenTasks.tasks, t)
		onListenTasks.Unlock()
	}
	return t
}

// Run starts the given task identifier by it's name, unless
// it has been previously registered with Options which
// prevent from running it right now (e.g. it was registered
// with MaxInstances = 2 and there are already 2 instances running).
// The first return argument indicates if the task was executed, while
// the second includes any errors which happened while running the task.
func Run(ctx *app.Context, name string) (bool, error) {
	registered.RLock()
	task := registered.tasks[name]
	registered.RUnlock()
	if task == nil {
		return false, fmt.Errorf("there's no task registered with the name %q", name)
	}
	return executeTask(ctx, task)
}

// RunHandler starts the given task identifier by it's handler. The same
// restrictions in Run() apply to this function.
// Return values are the same as Run().
func RunHandler(ctx *app.Context, handler app.Handler) (bool, error) {
	var task *Task
	p := reflect.ValueOf(handler).Pointer()
	registered.RLock()
	for _, v := range registered.tasks {
		if reflect.ValueOf(v.Handler).Pointer() == p {
			task = v
			break
		}
	}
	registered.RUnlock()
	if task == nil {
		return false, fmt.Errorf("there's no task registered with the handler %s", runtimeutil.FuncName(handler))
	}
	return executeTask(ctx, task)
}

// Execute runs the given handler in a task context. If the handler fails
// with a panic, it will be returned in the error return value.
func Execute(ctx *app.Context, handler app.Handler) error {
	t := &Task{App: ctx.App(), Handler: handler}
	_, err := executeTask(ctx, t)
	return err
}

func init() {
	// Admin commands are executed on WILL_PREPARE so we
	// won't reach this point if there's an admin command
	// provided in the cmdline.
	signal.Listen(app.DID_PREPARE, func(_ string, obj interface{}) {
		a := obj.(*app.App)
		onListenTasks.Lock()
		var pending []*Task
		for _, v := range onListenTasks.tasks {
			if v.App == a {
				go v.executeTask()
			} else {
				pending = append(pending, v)
			}
		}
		onListenTasks.tasks = pending
		onListenTasks.Unlock()
	})
}
<commit_msg>Call Task.executeTask in a goroutine<commit_after>// Package tasks provides functions for scheduling
// periodic tasks (e.g. background jobs).
package tasks

import (
	"bytes"
	"errors"
	"fmt"
	"reflect"
	"sync"
	"time"

	"gnd.la/app"
	"gnd.la/internal/runtimeutil"
	"gnd.la/log"
	"gnd.la/signal"
)

var running struct {
	sync.Mutex
	tasks map[*Task]int
}

var registered struct {
	sync.RWMutex
	tasks map[string]*Task
}

var onListenTasks struct {
	sync.RWMutex
	tasks []*Task
}

// Task represent a scheduled task.
type Task struct {
	App      *app.App
	Handler  app.Handler
	Interval time.Duration
	Options  *Options
	ticker   *time.Ticker
	stop     chan struct{}
	stopped  chan struct{}
}

// Stop de-schedules the task. After stopping the task, it
// won't be started again but if it's currently running, it will
// be completed.
func (t *Task) Stop() {
	if t.stop != nil {
		t.stop <- struct{}{}
		<-t.stopped
		close(t.stopped)
		t.stopped = nil
	}
}

func (t *Task) Resume(now bool) {
	t.Stop()
	t.ticker = time.NewTicker(t.Interval)
	t.stop = make(chan struct{}, 1)
	t.stopped = make(chan struct{}, 1)
	go t.execute(now)
}

// Name returns the task name.
func (t *Task) Name() string {
	if t.Options != nil && t.Options.Name != "" {
		return t.Options.Name
	}
	return runtimeutil.FuncName(t.Handler)
}

// Delete stops the task by calling t.Stop() and then removes
// it from the internal task register.
func (t *Task) Delete() {
	registered.Lock()
	defer registered.Unlock()
	t.deleteLocked()
}

func (t *Task) deleteLocked() {
	t.Stop()
	delete(registered.tasks, t.Name())
}

func (t *Task) execute(now bool) {
	if now {
		t.executeTask()
	}
	for {
		c := t.ticker.C
		select {
		case <-c:
			go t.executeTask()
		case <-t.stop:
			close(t.stop)
			t.stop = nil
			t.ticker.Stop()
			t.ticker = nil
			t.stopped <- struct{}{}
			return
		}
	}
}

func (t *Task) executeTask() {
	if err := startTask(t); err != nil {
		log.Error(err)
	}
}

// Options are used to specify task options when registering them.
type Options struct {
	// Name indicates the task name, used for checking the number
	// of instances running. If the task name is not provided, it's
	// derived from the function. Two tasks with the same name are
	// considered as equal, even if their functions are different.
	Name string
	// MaxInstances indicates the maximum number of instances of
	// this function that can be simultaneously running. If zero,
	// there is no limit.
	MaxInstances int
}

func afterTask(task *Task, started time.Time, terr *error) {
	name := task.Name()
	if err := recover(); err != nil {
		skip, stackSkip, _, _ := runtimeutil.GetPanic()
		var buf bytes.Buffer
		fmt.Fprintf(&buf, "Panic executing task %s: %v\n", name, err)
		stack := runtimeutil.FormatStack(stackSkip)
		location, code := runtimeutil.FormatCaller(skip, 5, true, true)
		if location != "" {
			buf.WriteString("\n At ")
			buf.WriteString(location)
			if code != "" {
				buf.WriteByte('\n')
				buf.WriteString(code)
				buf.WriteByte('\n')
			}
		}
		if stack != "" {
			buf.WriteString("\nStack:\n")
			buf.WriteString(stack)
		}
		*terr = errors.New(buf.String())
	}
	end := time.Now()
	running.Lock()
	defer running.Unlock()
	c := running.tasks[task] - 1
	if c > 0 {
		running.tasks[task] = c
	} else {
		delete(running.tasks, task)
	}
	log.Debugf("Finished task %s (%d instances now running) at %v (took %v)", name, c, end, end.Sub(started))
}

func numberOfInstances(task *Task) (int, error) {
	running.Lock()
	defer running.Unlock()
	c := running.tasks[task]
	if task.Options != nil && task.Options.MaxInstances > 0 {
		if c >= task.Options.MaxInstances {
			return 0, fmt.Errorf("not starting task %s because it's already running %d instances", task.Name(), c)
		}
	}
	if running.tasks == nil {
		running.tasks = make(map[*Task]int)
	}
	c++
	running.tasks[task] = c
	return c, nil
}

func executeTask(ctx *app.Context, task *Task) (ran bool, err error) {
	var n int
	if n, err = numberOfInstances(task); err != nil {
		return
	}
	started := time.Now()
	log.Debugf("Starting task %s (%d instances now running) at %v", task.Name(), n, started)
	ran = true
	defer afterTask(task, started, &err)
	task.Handler(ctx)
	return
}

// Register registers a new task that might be run with Run, but
// without scheduling it. If there was previously another task
// registered with the same name, it will be deleted.
func Register(m *app.App, task app.Handler, opts *Options) *Task {
	t := &Task{App: m, Handler: task, Options: opts}
	registered.Lock()
	defer registered.Unlock()
	if registered.tasks == nil {
		registered.tasks = make(map[string]*Task)
	}
	name := t.Name()
	if prev := registered.tasks[name]; prev != nil {
		log.Debugf("There's already a task registered as %s, deleting it", name)
		prev.deleteLocked()
	}
	registered.tasks[name] = t
	return t
}

// Schedule registers and schedules a task to be run at the given
// interval. If interval is 0, the task is only registered, but not
// scheduled. The onListen argument indicates if the task should also run
// (in its own goroutine) as soon as the app starts listening rather than
// waiting until interval for the first run. Note that on App Engine, the task
// will be started when the first cron request comes in (these are usually scheduled
// to run once a minute).
//
// Schedule returns a Task instance, which might be used to stop, resume or delete a it.
func Schedule(m *app.App, task app.Handler, opts *Options, interval time.Duration, onListen bool) *Task {
	t := Register(m, task, opts)
	t.Interval = interval
	go t.Resume(false)
	if onListen {
		onListenTasks.Lock()
		onListenTasks.tasks = append(onListenTasks.tasks, t)
		onListenTasks.Unlock()
	}
	return t
}

// Run starts the given task identifier by it's name, unless
// it has been previously registered with Options which
// prevent from running it right now (e.g. it was registered
// with MaxInstances = 2 and there are already 2 instances running).
// The first return argument indicates if the task was executed, while
// the second includes any errors which happened while running the task.
func Run(ctx *app.Context, name string) (bool, error) {
	registered.RLock()
	task := registered.tasks[name]
	registered.RUnlock()
	if task == nil {
		return false, fmt.Errorf("there's no task registered with the name %q", name)
	}
	return executeTask(ctx, task)
}

// RunHandler starts the given task identifier by it's handler. The same
// restrictions in Run() apply to this function.
// Return values are the same as Run().
func RunHandler(ctx *app.Context, handler app.Handler) (bool, error) {
	var task *Task
	p := reflect.ValueOf(handler).Pointer()
	registered.RLock()
	for _, v := range registered.tasks {
		if reflect.ValueOf(v.Handler).Pointer() == p {
			task = v
			break
		}
	}
	registered.RUnlock()
	if task == nil {
		return false, fmt.Errorf("there's no task registered with the handler %s", runtimeutil.FuncName(handler))
	}
	return executeTask(ctx, task)
}

// Execute runs the given handler in a task context. If the handler fails
// with a panic, it will be returned in the error return value.
func Execute(ctx *app.Context, handler app.Handler) error {
	t := &Task{App: ctx.App(), Handler: handler}
	_, err := executeTask(ctx, t)
	return err
}

func init() {
	// Admin commands are executed on WILL_PREPARE so we
	// won't reach this point if there's an admin command
	// provided in the cmdline.
	signal.Listen(app.DID_PREPARE, func(_ string, obj interface{}) {
		a := obj.(*app.App)
		onListenTasks.Lock()
		var pending []*Task
		for _, v := range onListenTasks.tasks {
			if v.App == a {
				go v.executeTask()
			} else {
				pending = append(pending, v)
			}
		}
		onListenTasks.tasks = pending
		onListenTasks.Unlock()
	})
}
<|endoftext|>