<commit_before>// +build linux

package linux

import (
	"context"
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"time"

	"github.com/boltdb/bolt"
	eventsapi "github.com/containerd/containerd/api/services/events/v1"
	"github.com/containerd/containerd/api/types"
	"github.com/containerd/containerd/errdefs"
	"github.com/containerd/containerd/events"
	"github.com/containerd/containerd/identifiers"
	client "github.com/containerd/containerd/linux/shim"
	shim "github.com/containerd/containerd/linux/shim/v1"
	"github.com/containerd/containerd/log"
	"github.com/containerd/containerd/metadata"
	"github.com/containerd/containerd/namespaces"
	"github.com/containerd/containerd/plugin"
	"github.com/containerd/containerd/reaper"
	"github.com/containerd/containerd/runtime"
	"github.com/containerd/containerd/sys"
	runc "github.com/containerd/go-runc"
	google_protobuf "github.com/golang/protobuf/ptypes/empty"
	"github.com/pkg/errors"
	"github.com/sirupsen/logrus"

	"golang.org/x/sys/unix"
)

var (
	pluginID = fmt.Sprintf("%s.%s", plugin.RuntimePlugin, "linux")
	empty    = &google_protobuf.Empty{}
)

const (
	configFilename = "config.json"
	defaultRuntime = "runc"
	defaultShim    = "containerd-shim"
)

func init() {
	plugin.Register(&plugin.Registration{
		Type: plugin.RuntimePlugin,
		ID:   "linux",
		Init: New,
		Requires: []plugin.PluginType{
			plugin.TaskMonitorPlugin,
			plugin.MetadataPlugin,
		},
		Config: &Config{
			Shim:    defaultShim,
			Runtime: defaultRuntime,
		},
	})
}

var _ = (runtime.Runtime)(&Runtime{})

type Config struct {
	// Shim is a path or name of binary implementing the Shim GRPC API
	Shim string `toml:"shim,omitempty"`
	// Runtime is a path or name of an OCI runtime used by the shim
	Runtime string `toml:"runtime,omitempty"`
	// NoShim calls runc directly from within the pkg
	NoShim bool `toml:"no_shim,omitempty"`
	// Debug enable debug on the shim
	ShimDebug bool `toml:"shim_debug,omitempty"`
}

func New(ic *plugin.InitContext) (interface{}, error) {
	if err := os.MkdirAll(ic.Root, 0711); err != nil {
		return nil, err
	}
	if err := os.MkdirAll(ic.State, 0711); err != nil {
		return nil, err
	}
	monitor, err := ic.Get(plugin.TaskMonitorPlugin)
	if err != nil {
		return nil, err
	}
	m, err := ic.Get(plugin.MetadataPlugin)
	if err != nil {
		return nil, err
	}
	cfg := ic.Config.(*Config)
	r := &Runtime{
		root:      ic.Root,
		state:     ic.State,
		remote:    !cfg.NoShim,
		shim:      cfg.Shim,
		shimDebug: cfg.ShimDebug,
		runtime:   cfg.Runtime,
		monitor:   monitor.(runtime.TaskMonitor),
		tasks:     runtime.NewTaskList(),
		db:        m.(*bolt.DB),
		address:   ic.Address,
		events:    ic.Events,
	}
	tasks, err := r.restoreTasks(ic.Context)
	if err != nil {
		return nil, err
	}
	// TODO: need to add the tasks to the monitor
	for _, t := range tasks {
		if err := r.tasks.AddWithNamespace(t.namespace, t); err != nil {
			return nil, err
		}
	}
	return r, nil
}

type Runtime struct {
	root      string
	state     string
	shim      string
	shimDebug bool
	runtime   string
	remote    bool
	address   string

	monitor runtime.TaskMonitor
	tasks   *runtime.TaskList
	db      *bolt.DB
	events  *events.Exchange
}

func (r *Runtime) ID() string {
	return pluginID
}

func (r *Runtime) Create(ctx context.Context, id string, opts runtime.CreateOpts) (_ runtime.Task, err error) {
	namespace, err := namespaces.NamespaceRequired(ctx)
	if err != nil {
		return nil, err
	}

	if err := identifiers.Validate(id); err != nil {
		return nil, errors.Wrapf(err, "invalid task id")
	}

	bundle, err := newBundle(
		namespace, id,
		filepath.Join(r.state, namespace),
		filepath.Join(r.root, namespace),
		opts.Spec.Value, r.events)
	if err != nil {
		return nil, err
	}
	defer func() {
		if err != nil {
			bundle.Delete()
		}
	}()
	s, err := bundle.NewShim(ctx, r.shim, r.address, r.remote, r.shimDebug, opts, func() {
		t, err := r.tasks.Get(ctx, id)
		if err != nil {
			// Task was never started or was already sucessfully deleted
			return
		}
		lc := t.(*Task)

		// Stop the monitor
		if err := r.monitor.Stop(lc); err != nil {
			log.G(ctx).WithError(err).WithFields(logrus.Fields{
				"id":        id,
				"namespace": namespace,
			}).Warn("failed to stop monitor")
		}

		log.G(ctx).WithFields(logrus.Fields{
			"id":        id,
			"namespace": namespace,
		}).Warn("cleaning up after killed shim")
		err = r.cleanupAfterDeadShim(context.Background(), bundle, namespace, id, lc.pid, true)
		if err == nil {
			r.tasks.Delete(ctx, lc)
		} else {
			log.G(ctx).WithError(err).WithFields(logrus.Fields{
				"id":        id,
				"namespace": namespace,
			}).Warn("failed to clen up after killed shim")
		}
	})
	if err != nil {
		return nil, err
	}
	defer func() {
		if err != nil {
			if kerr := s.KillShim(ctx); kerr != nil {
				log.G(ctx).WithError(err).Error("failed to kill shim")
			}
		}
	}()
	sopts := &shim.CreateTaskRequest{
		ID:         id,
		Bundle:     bundle.path,
		Runtime:    r.runtime,
		Stdin:      opts.IO.Stdin,
		Stdout:     opts.IO.Stdout,
		Stderr:     opts.IO.Stderr,
		Terminal:   opts.IO.Terminal,
		Checkpoint: opts.Checkpoint,
		Options:    opts.Options,
	}
	for _, m := range opts.Rootfs {
		sopts.Rootfs = append(sopts.Rootfs, &types.Mount{
			Type:    m.Type,
			Source:  m.Source,
			Options: m.Options,
		})
	}
	cr, err := s.Create(ctx, sopts)
	if err != nil {
		return nil, errdefs.FromGRPC(err)
	}
	t := newTask(id, namespace, int(cr.Pid), s)
	if err := r.tasks.Add(ctx, t); err != nil {
		return nil, err
	}
	// after the task is created, add it to the monitor
	if err = r.monitor.Monitor(t); err != nil {
		r.tasks.Delete(ctx, t)
		return nil, err
	}
	return t, nil
}

func (r *Runtime) Delete(ctx context.Context, c runtime.Task) (*runtime.Exit, error) {
	namespace, err := namespaces.NamespaceRequired(ctx)
	if err != nil {
		return nil, err
	}
	lc, ok := c.(*Task)
	if !ok {
		return nil, fmt.Errorf("task cannot be cast as *linux.Task")
	}
	if err := r.monitor.Stop(lc); err != nil {
		return nil, err
	}
	rsp, err := lc.shim.Delete(ctx, empty)
	if err != nil {
		return nil, errdefs.FromGRPC(err)
	}
	r.tasks.Delete(ctx, lc)
	if err := lc.shim.KillShim(ctx); err != nil {
		log.G(ctx).WithError(err).Error("failed to kill shim")
	}

	bundle := loadBundle(
		filepath.Join(r.state, namespace, lc.id),
		filepath.Join(r.root, namespace, lc.id),
		namespace,
		lc.id,
		r.events,
	)
	if err := bundle.Delete(); err != nil {
		log.G(ctx).WithError(err).Error("failed to delete bundle")
	}
	return &runtime.Exit{
		Status:    rsp.ExitStatus,
		Timestamp: rsp.ExitedAt,
		Pid:       rsp.Pid,
	}, nil
}

func (r *Runtime) Tasks(ctx context.Context) ([]runtime.Task, error) {
	return r.tasks.GetAll(ctx)
}

func (r *Runtime) restoreTasks(ctx context.Context) ([]*Task, error) {
	dir, err := ioutil.ReadDir(r.state)
	if err != nil {
		return nil, err
	}
	var o []*Task
	for _, namespace := range dir {
		if !namespace.IsDir() {
			continue
		}
		name := namespace.Name()
		log.G(ctx).WithField("namespace", name).Debug("loading tasks in namespace")
		tasks, err := r.loadTasks(ctx, name)
		if err != nil {
			return nil, err
		}
		o = append(o, tasks...)
	}
	return o, nil
}

func (r *Runtime) Get(ctx context.Context, id string) (runtime.Task, error) {
	return r.tasks.Get(ctx, id)
}

func (r *Runtime) loadTasks(ctx context.Context, ns string) ([]*Task, error) {
	dir, err := ioutil.ReadDir(filepath.Join(r.state, ns))
	if err != nil {
		return nil, err
	}
	var o []*Task
	for _, path := range dir {
		if !path.IsDir() {
			continue
		}
		id := path.Name()
		bundle := loadBundle(
			filepath.Join(r.state, ns, id),
			filepath.Join(r.root, ns, id),
			ns, id, r.events)
		s, err := bundle.Connect(ctx, r.remote)
		if err != nil {
			log.G(ctx).WithError(err).WithFields(logrus.Fields{
				"id":        id,
				"namespace": ns,
			}).Error("connecting to shim")
			pid, _ := runc.ReadPidFile(filepath.Join(bundle.path, client.InitPidFile))
			err := r.cleanupAfterDeadShim(ctx, bundle, ns, id, pid, false)
			if err != nil {
				log.G(ctx).WithError(err).WithField("bundle", bundle.path).
					Error("cleaning up after dead shim")
			}
			continue
		}
		o = append(o, &Task{
			id:        id,
			shim:      s,
			namespace: ns,
		})
	}
	return o, nil
}

func (r *Runtime) cleanupAfterDeadShim(ctx context.Context, bundle *bundle, ns, id string, pid int, reap bool) error {
	ctx = namespaces.WithNamespace(ctx, ns)
	if err := r.terminate(ctx, bundle, ns, id); err != nil {
		return errors.New("failed to terminate task, leaving bundle for debugging")
	}

	if reap {
		// if sub-reaper is set, reap our new child
		if v, err := sys.GetSubreaper(); err == nil && v == 1 {
			reaper.Default.Register(pid, &reaper.Cmd{ExitCh: make(chan struct{})})
			reaper.Default.WaitPid(pid)
			reaper.Default.Delete(pid)
		}
	}

	// Notify Client
	exitedAt := time.Now().UTC()
	r.events.Publish(ctx, runtime.TaskExitEventTopic, &eventsapi.TaskExit{
		ContainerID: id,
		ID:          id,
		Pid:         uint32(pid),
		ExitStatus:  128 + uint32(unix.SIGKILL),
		ExitedAt:    exitedAt,
	})

	if err := bundle.Delete(); err != nil {
		log.G(ctx).WithError(err).Error("delete bundle")
	}

	r.events.Publish(ctx, runtime.TaskDeleteEventTopic, &eventsapi.TaskDelete{
		ContainerID: id,
		Pid:         uint32(pid),
		ExitStatus:  128 + uint32(unix.SIGKILL),
		ExitedAt:    exitedAt,
	})

	return nil
}

func (r *Runtime) terminate(ctx context.Context, bundle *bundle, ns, id string) error {
	ctx = namespaces.WithNamespace(ctx, ns)
	rt, err := r.getRuntime(ctx, ns, id)
	if err != nil {
		return err
	}
	if err := rt.Delete(ctx, id, &runc.DeleteOpts{
		Force: true,
	}); err != nil {
		log.G(ctx).WithError(err).Warnf("delete runtime state %s", id)
	}
	if err := unix.Unmount(filepath.Join(bundle.path, "rootfs"), 0); err != nil {
		log.G(ctx).WithError(err).WithFields(logrus.Fields{
			"path": bundle.path,
			"id":   id,
		}).Warnf("unmount task rootfs")
	}
	return nil
}

func (r *Runtime) getRuntime(ctx context.Context, ns, id string) (*runc.Runc, error) {
	if err := r.db.View(func(tx *bolt.Tx) error {
		store := metadata.NewContainerStore(tx)
		var err error
		_, err = store.Get(ctx, id)
		return err
	}); err != nil {
		return nil, err
	}
	return &runc.Runc{
		// TODO: until we have a way to store/retrieve the original command
		// we can only rely on runc from the default $PATH
		Command:      runc.DefaultCommand,
		LogFormat:    runc.JSON,
		PdeathSignal: unix.SIGKILL,
		Root:         filepath.Join(client.RuncRoot, ns),
	}, nil
}
<commit_msg>Use configured runtime when cleaning up after dead shim<commit_after>// +build linux

package linux

import (
	"context"
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"time"

	"github.com/boltdb/bolt"
	eventsapi "github.com/containerd/containerd/api/services/events/v1"
	"github.com/containerd/containerd/api/types"
	"github.com/containerd/containerd/errdefs"
	"github.com/containerd/containerd/events"
	"github.com/containerd/containerd/identifiers"
	client "github.com/containerd/containerd/linux/shim"
	shim "github.com/containerd/containerd/linux/shim/v1"
	"github.com/containerd/containerd/log"
	"github.com/containerd/containerd/metadata"
	"github.com/containerd/containerd/namespaces"
	"github.com/containerd/containerd/plugin"
	"github.com/containerd/containerd/reaper"
	"github.com/containerd/containerd/runtime"
	"github.com/containerd/containerd/sys"
	runc "github.com/containerd/go-runc"
	google_protobuf "github.com/golang/protobuf/ptypes/empty"
	"github.com/pkg/errors"
	"github.com/sirupsen/logrus"

	"golang.org/x/sys/unix"
)

var (
	pluginID = fmt.Sprintf("%s.%s", plugin.RuntimePlugin, "linux")
	empty    = &google_protobuf.Empty{}
)

const (
	configFilename = "config.json"
	defaultRuntime = "runc"
	defaultShim    = "containerd-shim"
)

func init() {
	plugin.Register(&plugin.Registration{
		Type: plugin.RuntimePlugin,
		ID:   "linux",
		Init: New,
		Requires: []plugin.PluginType{
			plugin.TaskMonitorPlugin,
			plugin.MetadataPlugin,
		},
		Config: &Config{
			Shim:    defaultShim,
			Runtime: defaultRuntime,
		},
	})
}

var _ = (runtime.Runtime)(&Runtime{})

type Config struct {
	// Shim is a path or name of binary implementing the Shim GRPC API
	Shim string `toml:"shim,omitempty"`
	// Runtime is a path or name of an OCI runtime used by the shim
	Runtime string `toml:"runtime,omitempty"`
	// NoShim calls runc directly from within the pkg
	NoShim bool `toml:"no_shim,omitempty"`
	// Debug enable debug on the shim
	ShimDebug bool `toml:"shim_debug,omitempty"`
}

func New(ic *plugin.InitContext) (interface{}, error) {
	if err := os.MkdirAll(ic.Root, 0711); err != nil {
		return nil, err
	}
	if err := os.MkdirAll(ic.State, 0711); err != nil {
		return nil, err
	}
	monitor, err := ic.Get(plugin.TaskMonitorPlugin)
	if err != nil {
		return nil, err
	}
	m, err := ic.Get(plugin.MetadataPlugin)
	if err != nil {
		return nil, err
	}
	cfg := ic.Config.(*Config)
	r := &Runtime{
		root:      ic.Root,
		state:     ic.State,
		remote:    !cfg.NoShim,
		shim:      cfg.Shim,
		shimDebug: cfg.ShimDebug,
		runtime:   cfg.Runtime,
		monitor:   monitor.(runtime.TaskMonitor),
		tasks:     runtime.NewTaskList(),
		db:        m.(*bolt.DB),
		address:   ic.Address,
		events:    ic.Events,
	}
	tasks, err := r.restoreTasks(ic.Context)
	if err != nil {
		return nil, err
	}
	// TODO: need to add the tasks to the monitor
	for _, t := range tasks {
		if err := r.tasks.AddWithNamespace(t.namespace, t); err != nil {
			return nil, err
		}
	}
	return r, nil
}

type Runtime struct {
	root      string
	state     string
	shim      string
	shimDebug bool
	runtime   string
	remote    bool
	address   string

	monitor runtime.TaskMonitor
	tasks   *runtime.TaskList
	db      *bolt.DB
	events  *events.Exchange
}

func (r *Runtime) ID() string {
	return pluginID
}

func (r *Runtime) Create(ctx context.Context, id string, opts runtime.CreateOpts) (_ runtime.Task, err error) {
	namespace, err := namespaces.NamespaceRequired(ctx)
	if err != nil {
		return nil, err
	}

	if err := identifiers.Validate(id); err != nil {
		return nil, errors.Wrapf(err, "invalid task id")
	}

	bundle, err := newBundle(
		namespace, id,
		filepath.Join(r.state, namespace),
		filepath.Join(r.root, namespace),
		opts.Spec.Value, r.events)
	if err != nil {
		return nil, err
	}
	defer func() {
		if err != nil {
			bundle.Delete()
		}
	}()
	s, err := bundle.NewShim(ctx, r.shim, r.address, r.remote, r.shimDebug, opts, func() {
		t, err := r.tasks.Get(ctx, id)
		if err != nil {
			// Task was never started or was already sucessfully deleted
			return
		}
		lc := t.(*Task)

		// Stop the monitor
		if err := r.monitor.Stop(lc); err != nil {
			log.G(ctx).WithError(err).WithFields(logrus.Fields{
				"id":        id,
				"namespace": namespace,
			}).Warn("failed to stop monitor")
		}

		log.G(ctx).WithFields(logrus.Fields{
			"id":        id,
			"namespace": namespace,
		}).Warn("cleaning up after killed shim")
		err = r.cleanupAfterDeadShim(context.Background(), bundle, namespace, id, lc.pid, true)
		if err == nil {
			r.tasks.Delete(ctx, lc)
		} else {
			log.G(ctx).WithError(err).WithFields(logrus.Fields{
				"id":        id,
				"namespace": namespace,
			}).Warn("failed to clen up after killed shim")
		}
	})
	if err != nil {
		return nil, err
	}
	defer func() {
		if err != nil {
			if kerr := s.KillShim(ctx); kerr != nil {
				log.G(ctx).WithError(err).Error("failed to kill shim")
			}
		}
	}()
	sopts := &shim.CreateTaskRequest{
		ID:         id,
		Bundle:     bundle.path,
		Runtime:    r.runtime,
		Stdin:      opts.IO.Stdin,
		Stdout:     opts.IO.Stdout,
		Stderr:     opts.IO.Stderr,
		Terminal:   opts.IO.Terminal,
		Checkpoint: opts.Checkpoint,
		Options:    opts.Options,
	}
	for _, m := range opts.Rootfs {
		sopts.Rootfs = append(sopts.Rootfs, &types.Mount{
			Type:    m.Type,
			Source:  m.Source,
			Options: m.Options,
		})
	}
	cr, err := s.Create(ctx, sopts)
	if err != nil {
		return nil, errdefs.FromGRPC(err)
	}
	t := newTask(id, namespace, int(cr.Pid), s)
	if err := r.tasks.Add(ctx, t); err != nil {
		return nil, err
	}
	// after the task is created, add it to the monitor
	if err = r.monitor.Monitor(t); err != nil {
		r.tasks.Delete(ctx, t)
		return nil, err
	}
	return t, nil
}

func (r *Runtime) Delete(ctx context.Context, c runtime.Task) (*runtime.Exit, error) {
	namespace, err := namespaces.NamespaceRequired(ctx)
	if err != nil {
		return nil, err
	}
	lc, ok := c.(*Task)
	if !ok {
		return nil, fmt.Errorf("task cannot be cast as *linux.Task")
	}
	if err := r.monitor.Stop(lc); err != nil {
		return nil, err
	}
	rsp, err := lc.shim.Delete(ctx, empty)
	if err != nil {
		return nil, errdefs.FromGRPC(err)
	}
	r.tasks.Delete(ctx, lc)
	if err := lc.shim.KillShim(ctx); err != nil {
		log.G(ctx).WithError(err).Error("failed to kill shim")
	}

	bundle := loadBundle(
		filepath.Join(r.state, namespace, lc.id),
		filepath.Join(r.root, namespace, lc.id),
		namespace,
		lc.id,
		r.events,
	)
	if err := bundle.Delete(); err != nil {
		log.G(ctx).WithError(err).Error("failed to delete bundle")
	}
	return &runtime.Exit{
		Status:    rsp.ExitStatus,
		Timestamp: rsp.ExitedAt,
		Pid:       rsp.Pid,
	}, nil
}

func (r *Runtime) Tasks(ctx context.Context) ([]runtime.Task, error) {
	return r.tasks.GetAll(ctx)
}

func (r *Runtime) restoreTasks(ctx context.Context) ([]*Task, error) {
	dir, err := ioutil.ReadDir(r.state)
	if err != nil {
		return nil, err
	}
	var o []*Task
	for _, namespace := range dir {
		if !namespace.IsDir() {
			continue
		}
		name := namespace.Name()
		log.G(ctx).WithField("namespace", name).Debug("loading tasks in namespace")
		tasks, err := r.loadTasks(ctx, name)
		if err != nil {
			return nil, err
		}
		o = append(o, tasks...)
	}
	return o, nil
}

func (r *Runtime) Get(ctx context.Context, id string) (runtime.Task, error) {
	return r.tasks.Get(ctx, id)
}

func (r *Runtime) loadTasks(ctx context.Context, ns string) ([]*Task, error) {
	dir, err := ioutil.ReadDir(filepath.Join(r.state, ns))
	if err != nil {
		return nil, err
	}
	var o []*Task
	for _, path := range dir {
		if !path.IsDir() {
			continue
		}
		id := path.Name()
		bundle := loadBundle(
			filepath.Join(r.state, ns, id),
			filepath.Join(r.root, ns, id),
			ns, id, r.events)
		s, err := bundle.Connect(ctx, r.remote)
		if err != nil {
			log.G(ctx).WithError(err).WithFields(logrus.Fields{
				"id":        id,
				"namespace": ns,
			}).Error("connecting to shim")
			pid, _ := runc.ReadPidFile(filepath.Join(bundle.path, client.InitPidFile))
			err := r.cleanupAfterDeadShim(ctx, bundle, ns, id, pid, false)
			if err != nil {
				log.G(ctx).WithError(err).WithField("bundle", bundle.path).
					Error("cleaning up after dead shim")
			}
			continue
		}
		o = append(o, &Task{
			id:        id,
			shim:      s,
			namespace: ns,
		})
	}
	return o, nil
}

func (r *Runtime) cleanupAfterDeadShim(ctx context.Context, bundle *bundle, ns, id string, pid int, reap bool) error {
	ctx = namespaces.WithNamespace(ctx, ns)
	if err := r.terminate(ctx, bundle, ns, id); err != nil {
		return errors.New("failed to terminate task, leaving bundle for debugging")
	}

	if reap {
		// if sub-reaper is set, reap our new child
		if v, err := sys.GetSubreaper(); err == nil && v == 1 {
			reaper.Default.Register(pid, &reaper.Cmd{ExitCh: make(chan struct{})})
			reaper.Default.WaitPid(pid)
			reaper.Default.Delete(pid)
		}
	}

	// Notify Client
	exitedAt := time.Now().UTC()
	r.events.Publish(ctx, runtime.TaskExitEventTopic, &eventsapi.TaskExit{
		ContainerID: id,
		ID:          id,
		Pid:         uint32(pid),
		ExitStatus:  128 + uint32(unix.SIGKILL),
		ExitedAt:    exitedAt,
	})

	if err := bundle.Delete(); err != nil {
		log.G(ctx).WithError(err).Error("delete bundle")
	}

	r.events.Publish(ctx, runtime.TaskDeleteEventTopic, &eventsapi.TaskDelete{
		ContainerID: id,
		Pid:         uint32(pid),
		ExitStatus:  128 + uint32(unix.SIGKILL),
		ExitedAt:    exitedAt,
	})

	return nil
}

func (r *Runtime) terminate(ctx context.Context, bundle *bundle, ns, id string) error {
	ctx = namespaces.WithNamespace(ctx, ns)
	rt, err := r.getRuntime(ctx, ns, id)
	if err != nil {
		return err
	}
	if err := rt.Delete(ctx, id, &runc.DeleteOpts{
		Force: true,
	}); err != nil {
		log.G(ctx).WithError(err).Warnf("delete runtime state %s", id)
	}
	if err := unix.Unmount(filepath.Join(bundle.path, "rootfs"), 0); err != nil {
		log.G(ctx).WithError(err).WithFields(logrus.Fields{
			"path": bundle.path,
			"id":   id,
		}).Warnf("unmount task rootfs")
	}
	return nil
}

func (r *Runtime) getRuntime(ctx context.Context, ns, id string) (*runc.Runc, error) {
	if err := r.db.View(func(tx *bolt.Tx) error {
		store := metadata.NewContainerStore(tx)
		var err error
		_, err = store.Get(ctx, id)
		return err
	}); err != nil {
		return nil, err
	}
	return &runc.Runc{
		Command:      r.runtime,
		LogFormat:    runc.JSON,
		PdeathSignal: unix.SIGKILL,
		Root:         filepath.Join(client.RuncRoot, ns),
	}, nil
}
<|endoftext|>


<commit_before>/*  Copyright 2013, Raphael Estrada
    Author email:  <galaktor@gmx.de>
    Project home:  <https://github.com/galaktor/gorf24>
    Licensed under The MIT License (see README and LICENSE files)

    Updated to RF24 v.13
    2017, Marian Craciunescu, Marius Staicu
    Authors email:  <marian.craciunescu@esolutions.ro>, <marius.staicu@esolutions.ro>
    Project home:  <https://github.com/mariusstaicu/gorf24>
*/
package gorf24

/*
  #cgo LDFLAGS: -L./RF24_c
  #cgo LDFLAGS: -lrf24_c
  #cgo CFLAGS: -I./RF24_c
  #include "RF24_c.h"
  #include <stdio.h>
*/
import "C"
import (
	// "encoding/binary"
	// "fmt"
	"unsafe"
)

type LibRF24 interface {
	Begin()
	Delete()
	StartListening()
	StopListening()
	Write(data []byte, length uint8) bool
	StartWrite(data []byte, length uint8)
	WriteAckPayload(pipe uint8, data []byte, length uint8)
	Available() bool
	AvailablePipe() (bool, uint8)
	IsAckPayloadAvailable() bool
	Read(length uint8) ([]byte, bool)
	OpenWritingPipe(address uint64)
	OpenReadingPipe(pipe uint8, address uint64)
	SetRetries(delay uint8, count uint8)
	SetChannel(channel uint8)
	SetPayloadSize(size uint8)
	GetPayloadSize() uint8
	GetDynamicPayloadSize() uint8
	EnableAckPayload()
	EnableDynamicPayloads()
	IsPVariant() bool
	SetAutoAck(enable bool)
	SetAutoAckPipe(pipe uint8, enable bool)
	SetPALevel(level PA_DBM)
	GetPALevel() PA_DBM
	SetDataRate(rate DATARATE)
	GetDataRate() DATARATE
	SetCRCLength(length CRCLENGTH)
	GetCRCLength() CRCLENGTH
	DisableCRC()
	PrintDetails()
	PowerDown()
	PowerUp()
	WhatHappened() (tx_ok, tx_fail, rx_ready bool)
	TestCarrier() bool
	TestRPD() bool
}

// TODO: more idiomatic:
//   error handling
//   Read/Write interfaces
//   possibly more conventional byte slice passing
//   more type safety?
//   channel for available data, like time.Tick? r.Available() <-chan []byte  or so?
type RF24 struct {
	cptr        C.RF24Handle
	buffer_size uint8
	buffer      []byte
}

/*
func main() {
	var pipe uint64 = 0xF0F0F0F0E1
	r := New(25, 8, 32)
	defer r.Delete()
	r.Begin()
	r.SetRetries(15, 15)
	r.SetAutoAck(true)
	r.OpenReadingPipe(1, pipe)
	r.StartListening()
	r.PrintDetails()
	for {
		if r.Available() {
			data, _ := r.Read(4)
//			fmt.Printf("data: %v\n", data)
			payload := binary.LittleEndian.Uint32(data)
			fmt.Printf("Received %v\n", payload)
		} else {
			//
		}
	}
}
*/
func New(cepin uint8, cspin uint8, spispeed uint32) LibRF24 {
	var r RF24
	r.cptr = C.new_rf24(C.uint8_t(cepin), C.uint8_t(cspin), C.uint32_t(spispeed))
	r.buffer = make([]byte, 128) // max payload length according to nrf24 spec
	return &r
}

func (r *RF24) Delete() {
	C.rf24_delete(r.cptr)
	r.cptr = nil
}
func (r *RF24) Begin() {
	C.rf24_begin(r.cptr)
}

func (r *RF24) StartListening() {
	C.rf24_startListening(r.cptr)
}

func (r *RF24) StopListening() {
	C.rf24_stopListening(r.cptr)
}

// TODO: implement Reader/Writer compatible interfaces
func (r *RF24) Write(data []byte, length uint8) bool {
	return gobool(C.rf24_write(r.cptr, unsafe.Pointer(&data[0]), C.uint8_t(length)))
}
func (r *RF24) StartWrite(data []byte, length uint8) {
	C.rf24_startWrite(r.cptr, unsafe.Pointer(&data), C.uint8_t(length))
}
func (r *RF24) WriteAckPayload(pipe uint8, data []byte, length uint8) {
	C.rf24_writeAckPayload(r.cptr, C.uint8_t(pipe), unsafe.Pointer(&data), C.uint8_t(length))
}
func (r *RF24) Available() bool {
	return gobool(C.rf24_available(r.cptr))
}

// TODO: create Pipe type, make this a func on Pipe
func (r *RF24) AvailablePipe() (bool, uint8) {
	var pipe C.uint8_t
	available := gobool(C.rf24_available_pipe(r.cptr, &pipe))
	return available, uint8(pipe)
}
func (r *RF24) IsAckPayloadAvailable() bool {
	return gobool(C.rf24_isAckPayloadAvailable(r.cptr))
}
func (r *RF24) Read(length uint8) ([]byte, bool) {
	ok := gobool(C.rf24_read(r.cptr, unsafe.Pointer(&r.buffer[0]), C.uint8_t(length)))
	return r.buffer[:length], ok
}
func (r *RF24) OpenWritingPipe(address uint64) {
	C.rf24_openWritingPipe(r.cptr, C.uint64_t(address))
}
func (r *RF24) OpenReadingPipe(pipe uint8, address uint64) {
	C.rf24_openReadingPipe(r.cptr, C.uint8_t(pipe), C.uint64_t(address))
}
func (r *RF24) SetRetries(delay uint8, count uint8) {
	C.rf24_setRetries(r.cptr, C.uint8_t(delay), C.uint8_t(count))
}
func (r *RF24) SetChannel(channel uint8) {
	C.rf24_setChannel(r.cptr, C.uint8_t(channel))
}
func (r *RF24) SetPayloadSize(size uint8) {
	C.rf24_setPayloadSize(r.cptr, C.uint8_t(size))
}
func (r *RF24) GetPayloadSize() uint8 {
	return uint8(C.rf24_getPayloadSize(r.cptr))
}
func (r *RF24) GetDynamicPayloadSize() uint8 {
	return uint8(C.rf24_getDynamicPayloadSize(r.cptr))
}
func (r *RF24) EnableAckPayload() {
	C.rf24_enableAckPayload(r.cptr)
}
func (r *RF24) EnableDynamicPayloads() {
	C.rf24_enableDynamicPayloads(r.cptr)
}
func (r *RF24) IsPVariant() bool {
	return gobool(C.rf24_isPVariant(r.cptr))
}
func (r *RF24) SetAutoAck(enable bool) {
	C.rf24_setAutoAck(r.cptr, cbool(enable))
}
func (r *RF24) SetAutoAckPipe(pipe uint8, enable bool) {
	C.rf24_setAutoAck_pipe(r.cptr, C.uint8_t(pipe), cbool(enable))
}

// Is there any way to use the rf24_pa_dbm_e enum type directly
func (r *RF24) SetPALevel(level PA_DBM) {
	C.rf24_setPALevel(r.cptr, C.rf24_pa_dbm_val(level))
}
func (r *RF24) GetPALevel() PA_DBM {
	return PA_DBM(C.rf24_getPALevel(r.cptr))
}

func (r *RF24) SetDataRate(rate DATARATE) {
	C.rf24_setDataRate(r.cptr, C.rf24_datarate_val(rate))
}
func (r *RF24) GetDataRate() DATARATE {
	return DATARATE(C.rf24_getDataRate(r.cptr))
}

func (r *RF24) SetCRCLength(length CRCLENGTH) {
	C.rf24_setCRCLength(r.cptr, C.rf24_crclength_val(length))
}
func (r *RF24) GetCRCLength() CRCLENGTH {
	return CRCLENGTH(C.rf24_getCRCLength(r.cptr))
}
func (r *RF24) DisableCRC() {
	C.rf24_disableCRC(r.cptr)
}

// TODO: String() method would be great
func (r *RF24) PrintDetails() {
	C.rf24_printDetails(r.cptr)
}
func (r *RF24) PowerDown() {
	C.rf24_powerDown(r.cptr)
}
func (r *RF24) PowerUp() {
	C.rf24_powerUp(r.cptr)
}
func (r *RF24) WhatHappened() (tx_ok, tx_fail, rx_ready bool) {
	var out_tx_ok, out_tx_fail, out_rx_ready C.cbool
	C.rf24_whatHappened(r.cptr, &out_tx_ok, &out_tx_fail, &out_rx_ready)
	tx_ok, tx_fail, rx_ready = gobool(out_tx_ok), gobool(out_tx_fail), gobool(out_rx_ready)
	return
}

func (r *RF24) TestCarrier() bool {
	return gobool(C.rf24_testCarrier(r.cptr))
}
func (r *RF24) TestRPD() bool {
	return gobool(C.rf24_testRPD(r.cptr))
}

// TODO: @Marian Craciunescu add missing function implementation.
<commit_msg>Added missing function implementations.<commit_after>/*  Copyright 2013, Raphael Estrada
    Author email:  <galaktor@gmx.de>
    Project home:  <https://github.com/galaktor/gorf24>
    Licensed under The MIT License (see README and LICENSE files)

    Updated to RF24 v.13
    2017, Marian Craciunescu, Marius Staicu
    Authors email:  <marian.craciunescu@esolutions.ro>, <marius.staicu@esolutions.ro>
    Project home:  <https://github.com/mariusstaicu/gorf24>
*/
package gorf24

/*
  #cgo LDFLAGS: -L./RF24_c
  #cgo LDFLAGS: -lrf24_c
  #cgo CFLAGS: -I./RF24_c
  #include "RF24_c.h"
  #include <stdio.h>
*/
import "C"
import (
	// "encoding/binary"
	// "fmt"
	"unsafe"
)

type LibRF24 interface {
	Delete()
	Begin()
	IsChipConnected() bool
	StartListening()
	StopListening()
	Available() bool
	Read(length uint8) ([]byte, bool)
	OpenWritingPipe(address uint64)
	OpenReadingPipe(pipe uint8, address uint64)
	Write(data []byte, length uint8) bool
	PrintDetails()
	AvailablePipe() (bool, uint8)
	IsRxFifoFull() bool
	PowerDown()
	PowerUp()
	WriteMulticast(data []byte, length uint8, multicast bool) bool
	WriteFast(data []byte, length uint8) bool
	WriteFastMulticast(data []byte, length uint8, multicast bool) bool
	WriteBlocking(data []byte, length uint8, timeout uint32) bool
	IsTxStandBy() bool
	IsTxStandByExtended(timeout uint32, startTx bool) bool
	WriteAckPayload(pipe uint8, data []byte, length uint8)
	IsAckPayloadAvailable() bool
	WhatHappened() (tx_ok, tx_fail, rx_ready bool)
	StartWrite(data []byte, length uint8)
	StartFastWrite(data []byte, length uint8, multicast bool, startTx bool)
	ReUseTx()
	FlushTx() uint8
	TestCarrier() bool
	TestRPD() bool
	CloseReadingPipe(pipe uint8)
	SetAddressWidth(width uint8)
	SetRetries(delay uint8, count uint8)
	SetChannel(channel uint8)
	GetChannel(channel uint8) uint8
	SetPayloadSize(size uint8)
	GetPayloadSize() uint8
	GetDynamicPayloadSize() uint8
	EnableAckPayload()
	EnableDynamicPayloads()
	DisableDynamicPayloads()
	EnableDynamicAck()
	IsPVariant() bool
	SetAutoAck(enable bool)
	SetAutoAckPipe(pipe uint8, enable bool)
	SetPALevel(level PA_DBM)
	GetPALevel() PA_DBM
	SetDataRate(rate DATARATE)
	GetDataRate() DATARATE
	SetCRCLength(length CRCLENGTH)
	GetCRCLength() CRCLENGTH
	DisableCRC()
	MaskIRQ(txOk, txFail, rxReady bool)
}

// TODO: more idiomatic:
//   error handling
//   Read/Write interfaces
//   possibly more conventional byte slice passing
//   more type safety?
//   channel for available data, like time.Tick? r.Available() <-chan []byte  or so?
type RF24 struct {
	cptr        C.RF24Handle
	buffer_size uint8
	buffer      []byte
}

/*
func main() {
	var pipe uint64 = 0xF0F0F0F0E1
	r := New(25, 8, 32)
	defer r.Delete()
	r.Begin()
	r.SetRetries(15, 15)
	r.SetAutoAck(true)
	r.OpenReadingPipe(1, pipe)
	r.StartListening()
	r.PrintDetails()
	for {
		if r.Available() {
			data, _ := r.Read(4)
//			fmt.Printf("data: %v\n", data)
			payload := binary.LittleEndian.Uint32(data)
			fmt.Printf("Received %v\n", payload)
		} else {
			//
		}
	}
}
*/
func New(cepin uint8, cspin uint8, spispeed uint32) *RF24 {
	var r RF24
	r.cptr = C.new_rf24(C.uint8_t(cepin), C.uint8_t(cspin), C.uint32_t(spispeed))
	r.buffer = make([]byte, 128) // max payload length according to nrf24 spec
	return &r
}

func (r *RF24) Delete() {
	C.rf24_delete(r.cptr)
	r.cptr = nil
}
func (r *RF24) Begin() {
	C.rf24_begin(r.cptr)
}

func (r *RF24) IsChipConnected() bool {
	return gobool(C.rf24_isChipConnected(r.cptr))
}

func (r *RF24) StartListening() {
	C.rf24_startListening(r.cptr)
}

func (r *RF24) StopListening() {
	C.rf24_stopListening(r.cptr)
}

func (r *RF24) Available() bool {
	return gobool(C.rf24_available(r.cptr))
}

func (r *RF24) Read(length uint8) ([]byte, bool) {
	ok := gobool(C.rf24_read(r.cptr, unsafe.Pointer(&r.buffer[0]), C.uint8_t(length)))
	return r.buffer[:length], ok
}

func (r *RF24) OpenWritingPipe(address uint64) {
	C.rf24_openWritingPipe(r.cptr, C.uint64_t(address))
}

func (r *RF24) OpenReadingPipe(pipe uint8, address uint64) {
	C.rf24_openReadingPipe(r.cptr, C.uint8_t(pipe), C.uint64_t(address))
}

// TODO: implement Reader/Writer compatible interfaces
func (r *RF24) Write(data []byte, length uint8) bool {
	return gobool(C.rf24_write(r.cptr, unsafe.Pointer(&data[0]), C.uint8_t(length)))
}

// TODO: String() method would be great
func (r *RF24) PrintDetails() {
	C.rf24_printDetails(r.cptr)
}

// TODO: create Pipe type, make this a func on Pipe
func (r *RF24) AvailablePipe() (bool, uint8) {
	var pipe C.uint8_t
	available := gobool(C.rf24_available_pipe(r.cptr, &pipe))
	return available, uint8(pipe)
}

func (r *RF24) IsRxFifoFull() bool {
	return gobool(C.rf24_rxFifoFull(r.cptr))
}

func (r *RF24) PowerDown() {
	C.rf24_powerDown(r.cptr)
}

func (r *RF24) PowerUp() {
	C.rf24_powerUp(r.cptr)
}

func (r *RF24) WriteMulticast(data []byte, length uint8, multicast bool) bool {
	return gobool(C.rf24_writeMulticast(r.cptr, unsafe.Pointer(&data), C.uint8_t(len), cbool(multicast)))
}

func (r *RF24) WriteFast(data []byte, length uint8) bool {
	return gobool(C.rf24_writeFast(r.cptr, unsafe.Pointer(&data), C.uint8_t(length)))
}

func (r *RF24) WriteFastMulticast(data []byte, length uint8, multicast bool) bool {
	return gobool(C.rf24_writeFastMulticast(r.cptr, unsafe.Pointer(&data), C.uint8_t(length), cbool(multicast)))
}

func (r *RF24) WriteBlocking(data []byte, length uint8, timeout uint32) bool {
	return gobool(C.rf24_writeBlocking(r.cptr, unsafe.Pointer(&data), C.uint8_t(length), C.uint32_t(timeout)))
}

func (r *RF24) IsTxStandBy() bool {
	return gobool(C.rf24_txStandBy(r.cptr))
}

func (r *RF24) IsTxStandByExtended(timeout uint32, startTx bool) bool {
	return gobool(C.rf24_txStandByExtended(r.cptr, C.uint32_t(timeout), cbool(startTx)))
}

func (r *RF24) WriteAckPayload(pipe uint8, data []byte, length uint8) {
	C.rf24_writeAckPayload(r.cptr, C.uint8_t(pipe), unsafe.Pointer(&data), C.uint8_t(length))
}

func (r *RF24) IsAckPayloadAvailable() bool {
	return gobool(C.rf24_isAckPayloadAvailable(r.cptr))
}

func (r *RF24) WhatHappened() (tx_ok, tx_fail, rx_ready bool) {
	var out_tx_ok, out_tx_fail, out_rx_ready C.cbool
	C.rf24_whatHappened(r.cptr, &out_tx_ok, &out_tx_fail, &out_rx_ready)
	tx_ok, tx_fail, rx_ready = gobool(out_tx_ok), gobool(out_tx_fail), gobool(out_rx_ready)
	return
}

func (r *RF24) StartWrite(data []byte, length uint8) {
	C.rf24_startWrite(r.cptr, unsafe.Pointer(&data), C.uint8_t(length))
}

func (r *RF24) StartFastWrite(data []byte, length uint8, multicast bool, startTx bool) {
	C.rf24_startFastWrite(r.cptr, unsafe.Pointer(&data), C.uint8_t(length), cbool(multicast), cbool(startTx))
}

func (r *RF24) ReUseTx() {
	C.rf24_reUseTX(r.cptr)
}

func (r *RF24) FlushTx() uint8 {
	return uint8(C.rf24_flush_tx(r.cptr))
}

func (r *RF24) TestCarrier() bool {
	return gobool(C.rf24_testCarrier(r.cptr))
}

func (r *RF24) TestRPD() bool {
	return gobool(C.rf24_testRPD(r.cptr))
}

func (r *RF24) CloseReadingPipe(pipe uint8) {
	C.rf24_closeReadingPipe(r.cptr, C.uint8_t(pipe))
}

func (r *RF24) SetAddressWidth(width uint8) {
	C.rf24_setAddressWidth(r.cptr, C.uint8_t(width))
}

func (r *RF24) SetRetries(delay uint8, count uint8) {
	C.rf24_setRetries(r.cptr, C.uint8_t(delay), C.uint8_t(count))
}

func (r *RF24) SetChannel(channel uint8) {
	C.rf24_setChannel(r.cptr, C.uint8_t(channel))
}

func (r *RF24) GetChannel(channel uint8) uint8 {
	return uint8(C.rf24_getChannel(r.cptr))
}

func (r *RF24) SetPayloadSize(size uint8) {
	C.rf24_setPayloadSize(r.cptr, C.uint8_t(size))
}

func (r *RF24) GetPayloadSize() uint8 {
	return uint8(C.rf24_getPayloadSize(r.cptr))
}

func (r *RF24) GetDynamicPayloadSize() uint8 {
	return uint8(C.rf24_getDynamicPayloadSize(r.cptr))
}

func (r *RF24) EnableAckPayload() {
	C.rf24_enableAckPayload(r.cptr)
}

func (r *RF24) EnableDynamicPayloads() {
	C.rf24_enableDynamicPayloads(r.cptr)
}

func (r *RF24) DisableDynamicPayloads() {
	C.rf24_disableDynamicPayloads(r.cptr)
}

func (r *RF24) EnableDynamicAck() {
	C.rf24_enableDynamicAck(r.cptr)
}

func (r *RF24) IsPVariant() bool {
	return gobool(C.rf24_isPVariant(r.cptr))
}

func (r *RF24) SetAutoAck(enable bool) {
	C.rf24_setAutoAck(r.cptr, cbool(enable))
}

func (r *RF24) SetAutoAckPipe(pipe uint8, enable bool) {
	C.rf24_setAutoAck_pipe(r.cptr, C.uint8_t(pipe), cbool(enable))
}

// Is there any way to use the rf24_pa_dbm_e enum type directly
func (r *RF24) SetPALevel(level PA_DBM) {
	C.rf24_setPALevel(r.cptr, C.rf24_pa_dbm_val(level))
}

func (r *RF24) GetPALevel() PA_DBM {
	return PA_DBM(C.rf24_getPALevel(r.cptr))
}

func (r *RF24) SetDataRate(rate DATARATE) {
	C.rf24_setDataRate(r.cptr, C.rf24_datarate_val(rate))
}

func (r *RF24) GetDataRate() DATARATE {
	return DATARATE(C.rf24_getDataRate(r.cptr))
}

func (r *RF24) SetCRCLength(length CRCLENGTH) {
	C.rf24_setCRCLength(r.cptr, C.rf24_crclength_val(length))
}

func (r *RF24) GetCRCLength() CRCLENGTH {
	return CRCLENGTH(C.rf24_getCRCLength(r.cptr))
}

func (r *RF24) DisableCRC() {
	C.rf24_disableCRC(r.cptr)
}

func (r *RF24) MaskIRQ(txOk, txFail, rxReady bool) {
	C.rf24_maskIRQ(r.cptr, cbool(txOk), cbool(txFail), cbool(rxReady))
}
<|endoftext|>


<commit_before>package index

import (
	"encoding/xml"
	"errors"
	"fmt"
	"github.com/pboehm/series/renamer"
	"io/ioutil"
	"os"
	"regexp"
	"strconv"
	"strings"
)

var DefaultLanguage = "de"

type SeriesIndex struct {
	XMLName    xml.Name `xml:"seriesindex"`
	SeriesList []Series `xml:"series"`
	seriesMap  map[string]*Series
}

func (self *SeriesIndex) AddEpisode(episode *renamer.Episode) (bool, error) {
	series_name := self.SeriesNameInIndex(episode.Series)
	if series_name == "" {
		return false, errors.New("Series does not exist in index")
	}
	episode.Series = series_name
	series := self.seriesMap[episode.Series]

	// Handle episodes where no language is set
	if episode.Language == "" {
		self.GuessEpisodeLanguage(episode, series)
	}

	_, language_exist := series.languageMap[episode.Language]
	if !language_exist {
		return false, errors.New("Series is not watched in this language")
	}

	if self.IsEpisodeInIndex(*episode) {
		return false, errors.New("Episode already exists in Index")
	}

	episode_entry := Episode{Name: episode.CleanedFileName()}

	// find the right EpisodeSet so we can add our new episode to it
	set, exist := series.languageMap[episode.Language]
	if exist {
		set.EpisodeList = append(set.EpisodeList, episode_entry)

		// add it to the lookup cache
		key := GetIndexKey(episode.Season, episode.Episode)
		set.episodeMap[key] = episode_entry.Name

		return true, nil
	}

	return false,
		errors.New("Episode couldn't be added to index. This shouldn't occur!")
}

func (self *SeriesIndex) GuessEpisodeLanguage(episode *renamer.Episode, series *Series) {
	// This methods tries to find the right language for the supplied episode
	// based on several heuristics

	// When there is no language set and the series is only watched in one
	// language we can take this series
	if len(series.languageMap) == 1 {
		for k, _ := range series.languageMap {
			episode.Language = k
			break
		}
	}

	// Find the language which is most likely the right language
	if len(series.languageMap) > 1 {
		possible_languages := []string{}

		// when episode has not been watched in only one of the languages
		for lang, _ := range series.languageMap {
			episode.Language = lang
			if !self.IsEpisodeInIndex(*episode) {
				possible_languages = append(possible_languages, lang)
			}

			episode.Language = ""
		}

		if len(possible_languages) == 1 {
			episode.Language = possible_languages[0]

		} else if len(possible_languages) > 1 {
			// take the language where the previous episode exist
			previous_existing := []string{}

			for _, lang := range possible_languages {
				epi := *episode
				epi.Language = lang
				if (epi.Episode - 1) > 0 {
					epi.Episode -= 1
				}

				if self.IsEpisodeInIndex(epi) {
					previous_existing = append(previous_existing, lang)
				}
			}

			if len(previous_existing) == 1 {
				episode.Language = previous_existing[0]
			}
		}
	}
}

func (self *SeriesIndex) SeriesNameInIndex(series_name string) string {

	series_in_index, exist := self.seriesMap[series_name]
	if exist {
		return series_in_index.Name
	}

	// do a case insensitive search
	joined := series_name
	for {
		if joined == "" {
			break
		}

		pattern := regexp.MustCompile(fmt.Sprintf("^(?i)%s$", joined))
		for name, series := range self.seriesMap {
			if pattern.Match([]byte(name)) {
				return series.Name
			}
		}

		splitted := strings.Split(joined, " ")
		joined = strings.Join(splitted[1:], " ")
	}

	return ""
}

func (self *SeriesIndex) IsEpisodeInIndex(episode renamer.Episode) bool {

	series_name := self.SeriesNameInIndex(episode.Series)
	if series_name == "" {
		return false
	}

	series, series_exist := self.seriesMap[series_name]
	if !series_exist {
		return false
	}

	set, language_exist := series.languageMap[episode.Language]
	if !language_exist {
		return false
	}

	key := GetIndexKey(episode.Season, episode.Episode)
	_, episode_exist := set.episodeMap[key]

	if episode_exist {
		return true
	}

	// check if episode is before the lowest episode which sets all_before=true
	// takes place
	if set.allBefore {
		barrier := set.allBeforeSeason*100 + set.allBeforeEpisode
		actual := episode.Season*100 + episode.Episode

		if actual < barrier {
			return true
		}
	}

	return false
}

func ParseSeriesIndex(xmlpath string) (*SeriesIndex, error) {
	var index SeriesIndex

	xmlFile, err := os.Open(xmlpath)
	if err != nil {
		return &index, err
	}
	defer xmlFile.Close()

	content, err := ioutil.ReadAll(xmlFile)

	xml.Unmarshal([]byte(content), &index)

	index.BuildUpSeriesMap()
	return &index, nil
}

func (index *SeriesIndex) BuildUpSeriesMap() {
	// Build up the series map that holds references to series under the series
	// name and all aliases
	index.seriesMap = map[string]*Series{}

	for i := 0; i < len(index.SeriesList); i++ {
		series := &(index.SeriesList[i])
		series.BuildUpLanguageMap()

		index.seriesMap[series.Name] = series

		for _, alias := range series.Aliases {
			index.seriesMap[alias.To] = series
		}
	}
}

func (self *SeriesIndex) WriteToFile(xmlpath string) {

	marshaled, err := xml.MarshalIndent(*self, "", "  ")
	if err != nil {
		panic(err)
	}

	output := append([]byte(xml.Header), marshaled...)

	err = ioutil.WriteFile(xmlpath, output, 0644)
	if err != nil {
		panic(err)
	}
}

type Series struct {
	Name        string       `xml:"name,attr"`
	EpisodeSets []EpisodeSet `xml:"episodes"`
	Aliases     []Alias      `xml:"alias"`
	languageMap map[string]*EpisodeSet
}

func (self *Series) BuildUpLanguageMap() {
	self.languageMap = make(map[string]*EpisodeSet)

	for i := 0; i < len(self.EpisodeSets); i++ {
		set := &(self.EpisodeSets[i])
		set.BuildUpEpisodeMap()
		self.languageMap[set.GetLanguage()] = set
	}
}

type EpisodeSet struct {
	XMLName                           xml.Name  `xml:"episodes"`
	EpisodeList                       []Episode `xml:"episode"`
	Language                          string    `xml:"lang,attr,omitempty"`
	episodeMap                        map[string]string
	allBefore                         bool
	allBeforeSeason, allBeforeEpisode int
}

func (self *EpisodeSet) BuildUpEpisodeMap() {
	self.episodeMap = make(map[string]string)

	for _, episode := range self.EpisodeList {

		matched := renamer.ExtractEpisodeInformation(episode.Name)
		if matched != nil {
			nr_season, _ := strconv.Atoi(matched["season"])
			nr_episode, _ := strconv.Atoi(matched["episode"])
			key := GetIndexKey(nr_season, nr_episode)

			self.episodeMap[key] = episode.Name

			// handle all_before flag and set data for later usage
			if episode.AllBefore {
				self.allBefore = true
				self.allBeforeSeason = nr_season
				self.allBeforeEpisode = nr_episode
			}
		}
	}
}

func (self *EpisodeSet) GetLanguage() string {
	if self.Language != "" {
		return self.Language
	}

	return DefaultLanguage
}

type Episode struct {
	Name      string `xml:"name,attr"`
	AllBefore bool   `xml:"all_before,attr,omitempty"`
}

type Alias struct {
	To string `xml:"to,attr"`
}
<commit_msg>index now uses GetPossibleSeriesNames in AddEpisode<commit_after>package index

import (
	"encoding/xml"
	"errors"
	"fmt"
	"github.com/pboehm/series/renamer"
	"io/ioutil"
	"os"
	"regexp"
	"strconv"
	"strings"
)

var DefaultLanguage = "de"

type SeriesIndex struct {
	XMLName    xml.Name `xml:"seriesindex"`
	SeriesList []Series `xml:"series"`
	seriesMap  map[string]*Series
}

func (self *SeriesIndex) AddEpisode(episode *renamer.Episode) (bool, error) {

    // test for all possible series names if they exist in index and take the
    // first matching
    for _, possible_series := range episode.GetPossibleSeriesNames() {
        series_name := self.SeriesNameInIndex(possible_series)
        if series_name != "" {
            episode.Series = series_name
            break
        }
    }

	if episode.Series == "" {
		return false, errors.New("Series does not exist in index")
	}
	series := self.seriesMap[episode.Series]

	// Handle episodes where no language is set
	if episode.Language == "" {
		self.GuessEpisodeLanguage(episode, series)
	}

	_, language_exist := series.languageMap[episode.Language]
	if !language_exist {
		return false, errors.New("Series is not watched in this language")
	}

	if self.IsEpisodeInIndex(*episode) {
		return false, errors.New("Episode already exists in Index")
	}

	episode_entry := Episode{Name: episode.CleanedFileName()}

	// find the right EpisodeSet so we can add our new episode to it
	set, exist := series.languageMap[episode.Language]
	if exist {
		set.EpisodeList = append(set.EpisodeList, episode_entry)

		// add it to the lookup cache
		key := GetIndexKey(episode.Season, episode.Episode)
		set.episodeMap[key] = episode_entry.Name

		return true, nil
	}

	return false,
		errors.New("Episode couldn't be added to index. This shouldn't occur!")
}

func (self *SeriesIndex) GuessEpisodeLanguage(episode *renamer.Episode, series *Series) {
	// This methods tries to find the right language for the supplied episode
	// based on several heuristics

	// When there is no language set and the series is only watched in one
	// language we can take this series
	if len(series.languageMap) == 1 {
		for k, _ := range series.languageMap {
			episode.Language = k
			break
		}
	}

	// Find the language which is most likely the right language
	if len(series.languageMap) > 1 {
		possible_languages := []string{}

		// when episode has not been watched in only one of the languages
		for lang, _ := range series.languageMap {
			episode.Language = lang
			if !self.IsEpisodeInIndex(*episode) {
				possible_languages = append(possible_languages, lang)
			}

			episode.Language = ""
		}

		if len(possible_languages) == 1 {
			episode.Language = possible_languages[0]

		} else if len(possible_languages) > 1 {
			// take the language where the previous episode exist
			previous_existing := []string{}

			for _, lang := range possible_languages {
				epi := *episode
				epi.Language = lang
				if (epi.Episode - 1) > 0 {
					epi.Episode -= 1
				}

				if self.IsEpisodeInIndex(epi) {
					previous_existing = append(previous_existing, lang)
				}
			}

			if len(previous_existing) == 1 {
				episode.Language = previous_existing[0]
			}
		}
	}
}

func (self *SeriesIndex) SeriesNameInIndex(series_name string) string {

	series_in_index, exist := self.seriesMap[series_name]
	if exist {
		return series_in_index.Name
	}

	// do a case insensitive search
	joined := series_name
	for {
		if joined == "" {
			break
		}

		pattern := regexp.MustCompile(fmt.Sprintf("^(?i)%s$", joined))
		for name, series := range self.seriesMap {
			if pattern.Match([]byte(name)) {
				return series.Name
			}
		}

		splitted := strings.Split(joined, " ")
		joined = strings.Join(splitted[1:], " ")
	}

	return ""
}

func (self *SeriesIndex) IsEpisodeInIndex(episode renamer.Episode) bool {

	series_name := self.SeriesNameInIndex(episode.Series)
	if series_name == "" {
		return false
	}

	series, series_exist := self.seriesMap[series_name]
	if !series_exist {
		return false
	}

	set, language_exist := series.languageMap[episode.Language]
	if !language_exist {
		return false
	}

	key := GetIndexKey(episode.Season, episode.Episode)
	_, episode_exist := set.episodeMap[key]

	if episode_exist {
		return true
	}

	// check if episode is before the lowest episode which sets all_before=true
	// takes place
	if set.allBefore {
		barrier := set.allBeforeSeason*100 + set.allBeforeEpisode
		actual := episode.Season*100 + episode.Episode

		if actual < barrier {
			return true
		}
	}

	return false
}

func ParseSeriesIndex(xmlpath string) (*SeriesIndex, error) {
	var index SeriesIndex

	xmlFile, err := os.Open(xmlpath)
	if err != nil {
		return &index, err
	}
	defer xmlFile.Close()

	content, err := ioutil.ReadAll(xmlFile)

	xml.Unmarshal([]byte(content), &index)

	index.BuildUpSeriesMap()
	return &index, nil
}

func (index *SeriesIndex) BuildUpSeriesMap() {
	// Build up the series map that holds references to series under the series
	// name and all aliases
	index.seriesMap = map[string]*Series{}

	for i := 0; i < len(index.SeriesList); i++ {
		series := &(index.SeriesList[i])
		series.BuildUpLanguageMap()

		index.seriesMap[series.Name] = series

		for _, alias := range series.Aliases {
			index.seriesMap[alias.To] = series
		}
	}
}

func (self *SeriesIndex) WriteToFile(xmlpath string) {

	marshaled, err := xml.MarshalIndent(*self, "", "  ")
	if err != nil {
		panic(err)
	}

	output := append([]byte(xml.Header), marshaled...)

	err = ioutil.WriteFile(xmlpath, output, 0644)
	if err != nil {
		panic(err)
	}
}

type Series struct {
	Name        string       `xml:"name,attr"`
	EpisodeSets []EpisodeSet `xml:"episodes"`
	Aliases     []Alias      `xml:"alias"`
	languageMap map[string]*EpisodeSet
}

func (self *Series) BuildUpLanguageMap() {
	self.languageMap = make(map[string]*EpisodeSet)

	for i := 0; i < len(self.EpisodeSets); i++ {
		set := &(self.EpisodeSets[i])
		set.BuildUpEpisodeMap()
		self.languageMap[set.GetLanguage()] = set
	}
}

type EpisodeSet struct {
	XMLName                           xml.Name  `xml:"episodes"`
	EpisodeList                       []Episode `xml:"episode"`
	Language                          string    `xml:"lang,attr,omitempty"`
	episodeMap                        map[string]string
	allBefore                         bool
	allBeforeSeason, allBeforeEpisode int
}

func (self *EpisodeSet) BuildUpEpisodeMap() {
	self.episodeMap = make(map[string]string)

	for _, episode := range self.EpisodeList {

		matched := renamer.ExtractEpisodeInformation(episode.Name)
		if matched != nil {
			nr_season, _ := strconv.Atoi(matched["season"])
			nr_episode, _ := strconv.Atoi(matched["episode"])
			key := GetIndexKey(nr_season, nr_episode)

			self.episodeMap[key] = episode.Name

			// handle all_before flag and set data for later usage
			if episode.AllBefore {
				self.allBefore = true
				self.allBeforeSeason = nr_season
				self.allBeforeEpisode = nr_episode
			}
		}
	}
}

func (self *EpisodeSet) GetLanguage() string {
	if self.Language != "" {
		return self.Language
	}

	return DefaultLanguage
}

type Episode struct {
	Name      string `xml:"name,attr"`
	AllBefore bool   `xml:"all_before,attr,omitempty"`
}

type Alias struct {
	To string `xml:"to,attr"`
}
<|endoftext|>


<commit_before><commit_msg>Change wording for the documentation for the root pkg<commit_after><|endoftext|>


<commit_before><commit_msg>Fixed for golint<commit_after><|endoftext|>


<commit_before>package builtin

import (
	// "time"

	"github.com/wanliu/flow/builtin/config"
	// "github.com/wanliu/flow/builtin/confirm"
	"github.com/hysios/apiai-go"
	"github.com/wanliu/flow/builtin/resolves"
	"github.com/wanliu/flow/context"

	flow "github.com/wanliu/goflow"
)

func NewOrderNumber() interface{} {
	return new(OrderNumber)
}

type OrderNumber struct {
	flow.Component

	Ctx <-chan context.Context
	Out chan<- ReplyData
}

func (c *OrderNumber) OnCtx(ctx context.Context) {
	aiResult := ctx.Value("Result").(apiai.Result)

	if numInt, exist := aiResult.Params["order-numder"]; exist {
		orderNo := numInt.(string)

		resolveInt := ctx.Value(config.CtxKeyOrderNum)
		if resolveInt == nil {
			resolve := resolveInt.(resolves.OrderNumberResolve)
			reply := resolve.Resolve(orderNo, ctx)
			c.Out <- ReplyData{reply, ctx}
		} else {
			c.Out <- ReplyData{"接收到订单号输入，但是没有对应的操作哦", ctx}
		}

	} else {
		c.Out <- ReplyData{"无效的订单号输入", ctx}
	}
}
<commit_msg>order number intent<commit_after>package builtin

import (
	// "time"

	"github.com/wanliu/flow/builtin/config"
	// "github.com/wanliu/flow/builtin/confirm"
	"github.com/hysios/apiai-go"
	"github.com/wanliu/flow/builtin/resolves"
	"github.com/wanliu/flow/context"

	flow "github.com/wanliu/goflow"
)

func NewOrderNumber() interface{} {
	return new(OrderNumber)
}

type OrderNumber struct {
	flow.Component

	Ctx <-chan context.Context
	Out chan<- ReplyData
}

func (c *OrderNumber) OnCtx(ctx context.Context) {
	aiResult := ctx.Value("Result").(apiai.Result)

	if numInt, exist := aiResult.Params["order-numder"]; exist {
		orderNo := numInt.(string)

		resolveInt := ctx.Value(config.CtxKeyOrderNum)
		if resolveInt != nil {
			resolve := resolveInt.(resolves.OrderNumberResolve)
			reply := resolve.Resolve(orderNo, ctx)
			c.Out <- ReplyData{reply, ctx}
		} else {
			c.Out <- ReplyData{"接收到订单号输入，但是没有对应的操作哦", ctx}
		}

	} else {
		c.Out <- ReplyData{"无效的订单号输入", ctx}
	}
}
<|endoftext|>


<commit_before>package infra

import (
	"errors"
	"flag"
	"fmt"
	"github.com/cihub/seelog"
	"github.com/watermint/toolbox/dbx_api"
	"github.com/watermint/toolbox/dbx_api/dbx_auth"
	"github.com/watermint/toolbox/infra/diag"
	"github.com/watermint/toolbox/infra/util"
	"log"
	"os"
	"os/user"
	"path/filepath"
)

const (
	logConfig = `
	<seelog type="adaptive" mininterval="200000000" maxinterval="1000000000" critmsgcount="5">
	<formats>
    		<format id="detail" format="date:%Date(2006-01-02T15:04:05Z07:00)%tloc:%File:%FuncShort:%Line%tlevel:%Level%tmsg:%Msg%n" />
    		<format id="short" format="%Time [%LEVEL] %Msg%n" />
	</formats>
	<outputs formatid="detail">
		{{if .LogPath}}
    		<filter levels="{{.LogLevels}}">
        		<rollingfile formatid="detail" filename="{{.LogPath}}" type="size" maxsize="{{.LogMaxSize}}" maxrolls="{{.LogRolls}}" />
    		</filter>
		{{end}}
		<filter levels="info,warn,error,critical">
        		<console formatid="short" />
    		</filter>
    	</outputs>
	</seelog>
	`
)

const (
	DefaultLogMaxSize = 50 * 1024 * 1024
	DefaultLogRolls   = 7
)

var (
	logPath string
)

type ExecContext struct {
	Proxy        string
	WorkPath     string
	LogPath      string
	LogMaxSize   uint64
	LogRolls     int
	LogLevels    string
	CleanupToken bool
	TraceLog     bool
	issuedTokens []string
}

var (
	DropboxFullAppKey           string = ""
	DropboxFullAppSecret        string = ""
	BusinessInfoAppKey          string = "7hvc6534xiq9b1w"
	BusinessInfoAppSecret       string = "08w6ex5q3j76pqv"
	BusinessFileAppKey          string = "40tbehrieaha9w3"
	BusinessFileAppSecret       string = "jh1ylouwv9wbcu4"
	BusinessManagementAppKey    string = "9ihfmr6pu82045o"
	BusinessManagementAppSecret string = "ktvr8lltw5wqyxh"
	BusinessAuditAppKey         string = "6eieiasy6iqlqx1"
	BusinessAuditAppSecret      string = "za0n7k34ca10ows"
)

var (
	AppName    string = "toolbox"
	AppVersion string = "dev"
	AppHash    string = "XXXXXXX"
)

func (opts *ExecContext) FileOnWorkPath(name string) string {
	return filepath.Join(opts.WorkPath, name)
}

func (opts *ExecContext) AuthFile() string {
	return opts.FileOnWorkPath(AppName + ".secret")
}

func (opts *ExecContext) queueToken(a dbx_auth.DropboxAuthenticator, business bool) (ac *dbx_api.Context, err error) {
	token, err := a.LoadOrAuth(business, !opts.CleanupToken)
	if err != nil {
		return nil, err
	}

	seelog.Debugf("Issued token stored in ExecContext")
	opts.issuedTokens = append(opts.issuedTokens, token)

	ac = dbx_api.NewContext(token)

	return
}

func (opts *ExecContext) LoadOrAuthDropboxFull() (ac *dbx_api.Context, err error) {
	a := dbx_auth.DropboxAuthenticator{
		AuthFile:  opts.AuthFile(),
		AppKey:    DropboxFullAppKey,
		AppSecret: DropboxFullAppSecret,
		TokenType: dbx_auth.DropboxTokenFull,
	}
	return opts.queueToken(a, false)
}

func (opts *ExecContext) LoadOrAuthBusinessInfo() (ac *dbx_api.Context, err error) {
	a := dbx_auth.DropboxAuthenticator{
		AuthFile:  opts.AuthFile(),
		AppKey:    BusinessInfoAppKey,
		AppSecret: BusinessInfoAppSecret,
		TokenType: dbx_auth.DropboxTokenBusinessInfo,
	}
	return opts.queueToken(a, true)
}

func (opts *ExecContext) LoadOrAuthBusinessFile() (ac *dbx_api.Context, err error) {
	a := dbx_auth.DropboxAuthenticator{
		AuthFile:  opts.AuthFile(),
		AppKey:    BusinessFileAppKey,
		AppSecret: BusinessFileAppSecret,
		TokenType: dbx_auth.DropboxTokenBusinessFile,
	}
	return opts.queueToken(a, true)
}

func (opts *ExecContext) LoadOrAuthBusinessManagement() (ac *dbx_api.Context, err error) {
	a := dbx_auth.DropboxAuthenticator{
		AuthFile:  opts.AuthFile(),
		AppKey:    BusinessManagementAppKey,
		AppSecret: BusinessManagementAppSecret,
		TokenType: dbx_auth.DropboxTokenBusinessManagement,
	}
	return opts.queueToken(a, true)
}

func (opts *ExecContext) LoadOrAuthBusinessAudit() (ac *dbx_api.Context, err error) {
	a := dbx_auth.DropboxAuthenticator{
		AuthFile:  opts.AuthFile(),
		AppKey:    BusinessAuditAppKey,
		AppSecret: BusinessAuditAppSecret,
		TokenType: dbx_auth.DropboxTokenBusinessAudit,
	}
	return opts.queueToken(a, true)
}

func (opts *ExecContext) Startup() error {
	err := setupWorkPath(opts)
	if err != nil {
		return err
	}

	setupLogger(opts)

	seelog.Infof("[%s] version [%s] hash[%s]", AppName, AppVersion, AppHash)

	if opts.Proxy != "" {
		SetupHttpProxy(opts.Proxy)
	}

	diag.LogDiagnostics()
	diag.LogNetworkDiagnostics()

	err = diag.QuickNetworkDiagnostics()
	if err != nil {
		return errors.New("Unable to reach `www.dropbox.com`. Please check network connection and/or proxy configuration.")
	}

	return nil
}

func (opts *ExecContext) Shutdown() {
	seelog.Trace("Shutdown infrastructure")
	seelog.Infof("Log file is at [%s]", logPath)
	seelog.Flush()
}

func DefaultWorkPath() string {
	u, err := user.Current()
	if err != nil {
		log.Fatalf("Unable to determine current user: %v", err)
		panic(err)
	}
	return filepath.Join(u.HomeDir, "."+AppName)
}

func (ic *ExecContext) PrepareFlags(flagset *flag.FlagSet) {
	descProxy := "HTTP/HTTPS proxy (hostname:port)"
	flagset.StringVar(&ic.Proxy, "proxy", "", descProxy)

	descWork := fmt.Sprintf("Work directory (default: %s)", DefaultWorkPath())
	flagset.StringVar(&ic.WorkPath, "work", "", descWork)

	descCleanup := "Cleanup token on exit"
	flagset.BoolVar(&ic.CleanupToken, "cleanup-token", false, descCleanup)

	descTrace := "Enable trace level log"
	flagset.BoolVar(&ic.TraceLog, "trace", false, descTrace)
}

func setupWorkPath(opts *ExecContext) error {
	if opts.WorkPath == "" {
		opts.WorkPath = DefaultWorkPath()
		log.Printf("Setup using default work path: [%s]", opts.WorkPath)
	}

	st, err := os.Stat(opts.WorkPath)
	if err != nil {
		if os.IsNotExist(err) {
			err = os.MkdirAll(opts.WorkPath, 0701)
			if err != nil {
				log.Fatalf("Unable to create work directory: [%s]", opts.WorkPath)
				return err
			}
		} else {
			return err
		}
	} else {
		if !st.IsDir() {
			return errors.New(fmt.Sprintf("Unable to create work directory, it's not directory: [%s]. ", opts.WorkPath))
		}
		if st.Mode()&0700 == 0 {
			return errors.New(fmt.Sprintf("Unable to read/write work directory: %s", opts.WorkPath))
		}
	}

	return nil
}

func SetupHttpProxy(proxy string) {
	if proxy != "" {
		seelog.Debugf("Proxy configuration: HTTP_PROXY[%s]", proxy)
		seelog.Debugf("Proxy configuration: HTTPS_PROXY[%s]", proxy)
		os.Setenv("HTTP_PROXY", proxy)
		os.Setenv("HTTPS_PROXY", proxy)
	}
}

func setupLogger(opts *ExecContext) {
	if opts.LogMaxSize < 1 {
		opts.LogMaxSize = DefaultLogMaxSize
	}
	if opts.LogRolls < 1 {
		opts.LogRolls = DefaultLogRolls
	}
	if opts.LogPath == "" {
		opts.LogPath = filepath.Join(opts.WorkPath, AppName+".log")
	}

	logPath = opts.LogPath

	if opts.TraceLog {
		opts.LogLevels = "trace,debug,info,warn,error,critical"
	} else {
		opts.LogLevels = "debug,info,warn,error,critical"
	}

	conf, err := util.CompileTemplate(logConfig, opts)
	if err != nil {
		log.Fatalf("Unable to create log config template: %s", err)
		panic(err)
	}
	logger, err := seelog.LoggerFromConfigAsString(conf)
	if err != nil {
		log.Fatalln("Unable to configure seelog", err)
		panic(err)
	} else {
		seelog.ReplaceLogger(logger)
	}

	seelog.Debugf("Logging started: file[%s] maxSize[%d] rolls[%d]", opts.LogPath, opts.LogMaxSize, opts.LogRolls)
}
<commit_msg>Remove test token<commit_after>package infra

import (
	"errors"
	"flag"
	"fmt"
	"github.com/cihub/seelog"
	"github.com/watermint/toolbox/dbx_api"
	"github.com/watermint/toolbox/dbx_api/dbx_auth"
	"github.com/watermint/toolbox/infra/diag"
	"github.com/watermint/toolbox/infra/util"
	"log"
	"os"
	"os/user"
	"path/filepath"
)

const (
	logConfig = `
	<seelog type="adaptive" mininterval="200000000" maxinterval="1000000000" critmsgcount="5">
	<formats>
    		<format id="detail" format="date:%Date(2006-01-02T15:04:05Z07:00)%tloc:%File:%FuncShort:%Line%tlevel:%Level%tmsg:%Msg%n" />
    		<format id="short" format="%Time [%LEVEL] %Msg%n" />
	</formats>
	<outputs formatid="detail">
		{{if .LogPath}}
    		<filter levels="{{.LogLevels}}">
        		<rollingfile formatid="detail" filename="{{.LogPath}}" type="size" maxsize="{{.LogMaxSize}}" maxrolls="{{.LogRolls}}" />
    		</filter>
		{{end}}
		<filter levels="info,warn,error,critical">
        		<console formatid="short" />
    		</filter>
    	</outputs>
	</seelog>
	`
)

const (
	DefaultLogMaxSize = 50 * 1024 * 1024
	DefaultLogRolls   = 7
)

var (
	logPath string
)

type ExecContext struct {
	Proxy        string
	WorkPath     string
	LogPath      string
	LogMaxSize   uint64
	LogRolls     int
	LogLevels    string
	CleanupToken bool
	TraceLog     bool
	issuedTokens []string
}

var (
	DropboxFullAppKey           string = ""
	DropboxFullAppSecret        string = ""
	BusinessInfoAppKey          string = ""
	BusinessInfoAppSecret       string = ""
	BusinessFileAppKey          string = ""
	BusinessFileAppSecret       string = ""
	BusinessManagementAppKey    string = ""
	BusinessManagementAppSecret string = ""
	BusinessAuditAppKey         string = ""
	BusinessAuditAppSecret      string = ""
)

var (
	AppName    string = "toolbox"
	AppVersion string = "dev"
	AppHash    string = "XXXXXXX"
)

func (opts *ExecContext) FileOnWorkPath(name string) string {
	return filepath.Join(opts.WorkPath, name)
}

func (opts *ExecContext) AuthFile() string {
	return opts.FileOnWorkPath(AppName + ".secret")
}

func (opts *ExecContext) queueToken(a dbx_auth.DropboxAuthenticator, business bool) (ac *dbx_api.Context, err error) {
	token, err := a.LoadOrAuth(business, !opts.CleanupToken)
	if err != nil {
		return nil, err
	}

	seelog.Debugf("Issued token stored in ExecContext")
	opts.issuedTokens = append(opts.issuedTokens, token)

	ac = dbx_api.NewContext(token)

	return
}

func (opts *ExecContext) LoadOrAuthDropboxFull() (ac *dbx_api.Context, err error) {
	a := dbx_auth.DropboxAuthenticator{
		AuthFile:  opts.AuthFile(),
		AppKey:    DropboxFullAppKey,
		AppSecret: DropboxFullAppSecret,
		TokenType: dbx_auth.DropboxTokenFull,
	}
	return opts.queueToken(a, false)
}

func (opts *ExecContext) LoadOrAuthBusinessInfo() (ac *dbx_api.Context, err error) {
	a := dbx_auth.DropboxAuthenticator{
		AuthFile:  opts.AuthFile(),
		AppKey:    BusinessInfoAppKey,
		AppSecret: BusinessInfoAppSecret,
		TokenType: dbx_auth.DropboxTokenBusinessInfo,
	}
	return opts.queueToken(a, true)
}

func (opts *ExecContext) LoadOrAuthBusinessFile() (ac *dbx_api.Context, err error) {
	a := dbx_auth.DropboxAuthenticator{
		AuthFile:  opts.AuthFile(),
		AppKey:    BusinessFileAppKey,
		AppSecret: BusinessFileAppSecret,
		TokenType: dbx_auth.DropboxTokenBusinessFile,
	}
	return opts.queueToken(a, true)
}

func (opts *ExecContext) LoadOrAuthBusinessManagement() (ac *dbx_api.Context, err error) {
	a := dbx_auth.DropboxAuthenticator{
		AuthFile:  opts.AuthFile(),
		AppKey:    BusinessManagementAppKey,
		AppSecret: BusinessManagementAppSecret,
		TokenType: dbx_auth.DropboxTokenBusinessManagement,
	}
	return opts.queueToken(a, true)
}

func (opts *ExecContext) LoadOrAuthBusinessAudit() (ac *dbx_api.Context, err error) {
	a := dbx_auth.DropboxAuthenticator{
		AuthFile:  opts.AuthFile(),
		AppKey:    BusinessAuditAppKey,
		AppSecret: BusinessAuditAppSecret,
		TokenType: dbx_auth.DropboxTokenBusinessAudit,
	}
	return opts.queueToken(a, true)
}

func (opts *ExecContext) Startup() error {
	err := setupWorkPath(opts)
	if err != nil {
		return err
	}

	setupLogger(opts)

	seelog.Infof("[%s] version [%s] hash[%s]", AppName, AppVersion, AppHash)

	if opts.Proxy != "" {
		SetupHttpProxy(opts.Proxy)
	}

	diag.LogDiagnostics()
	diag.LogNetworkDiagnostics()

	err = diag.QuickNetworkDiagnostics()
	if err != nil {
		return errors.New("Unable to reach `www.dropbox.com`. Please check network connection and/or proxy configuration.")
	}

	return nil
}

func (opts *ExecContext) Shutdown() {
	seelog.Trace("Shutdown infrastructure")
	seelog.Infof("Log file is at [%s]", logPath)
	seelog.Flush()
}

func DefaultWorkPath() string {
	u, err := user.Current()
	if err != nil {
		log.Fatalf("Unable to determine current user: %v", err)
		panic(err)
	}
	return filepath.Join(u.HomeDir, "."+AppName)
}

func (ic *ExecContext) PrepareFlags(flagset *flag.FlagSet) {
	descProxy := "HTTP/HTTPS proxy (hostname:port)"
	flagset.StringVar(&ic.Proxy, "proxy", "", descProxy)

	descWork := fmt.Sprintf("Work directory (default: %s)", DefaultWorkPath())
	flagset.StringVar(&ic.WorkPath, "work", "", descWork)

	descCleanup := "Cleanup token on exit"
	flagset.BoolVar(&ic.CleanupToken, "cleanup-token", false, descCleanup)

	descTrace := "Enable trace level log"
	flagset.BoolVar(&ic.TraceLog, "trace", false, descTrace)
}

func setupWorkPath(opts *ExecContext) error {
	if opts.WorkPath == "" {
		opts.WorkPath = DefaultWorkPath()
		log.Printf("Setup using default work path: [%s]", opts.WorkPath)
	}

	st, err := os.Stat(opts.WorkPath)
	if err != nil {
		if os.IsNotExist(err) {
			err = os.MkdirAll(opts.WorkPath, 0701)
			if err != nil {
				log.Fatalf("Unable to create work directory: [%s]", opts.WorkPath)
				return err
			}
		} else {
			return err
		}
	} else {
		if !st.IsDir() {
			return errors.New(fmt.Sprintf("Unable to create work directory, it's not directory: [%s]. ", opts.WorkPath))
		}
		if st.Mode()&0700 == 0 {
			return errors.New(fmt.Sprintf("Unable to read/write work directory: %s", opts.WorkPath))
		}
	}

	return nil
}

func SetupHttpProxy(proxy string) {
	if proxy != "" {
		seelog.Debugf("Proxy configuration: HTTP_PROXY[%s]", proxy)
		seelog.Debugf("Proxy configuration: HTTPS_PROXY[%s]", proxy)
		os.Setenv("HTTP_PROXY", proxy)
		os.Setenv("HTTPS_PROXY", proxy)
	}
}

func setupLogger(opts *ExecContext) {
	if opts.LogMaxSize < 1 {
		opts.LogMaxSize = DefaultLogMaxSize
	}
	if opts.LogRolls < 1 {
		opts.LogRolls = DefaultLogRolls
	}
	if opts.LogPath == "" {
		opts.LogPath = filepath.Join(opts.WorkPath, AppName+".log")
	}

	logPath = opts.LogPath

	if opts.TraceLog {
		opts.LogLevels = "trace,debug,info,warn,error,critical"
	} else {
		opts.LogLevels = "debug,info,warn,error,critical"
	}

	conf, err := util.CompileTemplate(logConfig, opts)
	if err != nil {
		log.Fatalf("Unable to create log config template: %s", err)
		panic(err)
	}
	logger, err := seelog.LoggerFromConfigAsString(conf)
	if err != nil {
		log.Fatalln("Unable to configure seelog", err)
		panic(err)
	} else {
		seelog.ReplaceLogger(logger)
	}

	seelog.Debugf("Logging started: file[%s] maxSize[%d] rolls[%d]", opts.LogPath, opts.LogMaxSize, opts.LogRolls)
}
<|endoftext|>


<commit_before>package autocomplete_test

import (
	"github.com/centurylinkcloud/clc-go-cli/autocomplete"
	"github.com/centurylinkcloud/clc-go-cli/command_loader"
	"reflect"
	"sort"
	"strings"
	"testing"
)

func TestResourceAutocomplete(t *testing.T) {
	resources := command_loader.GetResources()
	sort.Strings(resources)

	args := []string{""}
	opts := strings.Split(autocomplete.Run(args), " ")
	sort.Strings(opts)
	if !reflect.DeepEqual(opts, resources) {
		t.Errorf("Invalid result.\n Expected: %s,\n obtained: %s", resources, opts)
	}

	args = []string{"serve"}
	opts = strings.Split(autocomplete.Run(args), " ")
	sort.Strings(opts)
	if !reflect.DeepEqual(opts, resources) {
		t.Errorf("Invalid result.\n Expected: %s,\n obtained: %s", resources, opts)
	}

	args = []string{"a"}
	opts = strings.Split(autocomplete.Run(args), " ")
	sort.Strings(opts)
	if !reflect.DeepEqual(opts, resources) {
		t.Errorf("Invalid result.\n Expected: %s,\n obtained: %s", resources, opts)
	}
}

func TestCommandAutocomplete(t *testing.T) {
	commands := command_loader.GetCommands("server")
	sort.Strings(commands)

	args := []string{"server"}
	opts := strings.Split(autocomplete.Run(args), " ")
	sort.Strings(opts)
	if !reflect.DeepEqual(opts, commands) {
		t.Errorf("Invalid result.\n Expected: %s,\n obtained: %s", commands, opts)
	}

	args = []string{"server", "cr"}
	opts = strings.Split(autocomplete.Run(args), " ")
	sort.Strings(opts)
	if !reflect.DeepEqual(opts, commands) {
		t.Errorf("Invalid result.\n Expected: %s,\n obtained: %s", commands, opts)
	}

	args = []string{"server", "create"}
	commands = []string{""}
	opts = strings.Split(autocomplete.Run(args), " ")
	sort.Strings(opts)
	if !reflect.DeepEqual(opts, commands) {
		t.Errorf("Invalid result.\n Expected: %s,\n obtained: %s", commands, opts)
	}

	args = []string{"serv", "create"}
	commands = []string{""}
	opts = strings.Split(autocomplete.Run(args), " ")
	sort.Strings(opts)
	if !reflect.DeepEqual(opts, commands) {
		t.Errorf("Invalid result.\n Expected: %s,\n obtained: %s", commands, opts)
	}
}
<commit_msg>Remove the wrong case from the autocomplete tests<commit_after>package autocomplete_test

import (
	"github.com/centurylinkcloud/clc-go-cli/autocomplete"
	"github.com/centurylinkcloud/clc-go-cli/command_loader"
	"reflect"
	"sort"
	"strings"
	"testing"
)

func TestResourceAutocomplete(t *testing.T) {
	resources := command_loader.GetResources()
	sort.Strings(resources)

	args := []string{""}
	opts := strings.Split(autocomplete.Run(args), " ")
	sort.Strings(opts)
	if !reflect.DeepEqual(opts, resources) {
		t.Errorf("Invalid result.\n Expected: %s,\n obtained: %s", resources, opts)
	}

	args = []string{"serve"}
	opts = strings.Split(autocomplete.Run(args), " ")
	sort.Strings(opts)
	if !reflect.DeepEqual(opts, resources) {
		t.Errorf("Invalid result.\n Expected: %s,\n obtained: %s", resources, opts)
	}

	args = []string{"a"}
	opts = strings.Split(autocomplete.Run(args), " ")
	sort.Strings(opts)
	if !reflect.DeepEqual(opts, resources) {
		t.Errorf("Invalid result.\n Expected: %s,\n obtained: %s", resources, opts)
	}
}

func TestCommandAutocomplete(t *testing.T) {
	commands := command_loader.GetCommands("server")
	sort.Strings(commands)

	args := []string{"server"}
	opts := strings.Split(autocomplete.Run(args), " ")
	sort.Strings(opts)
	if !reflect.DeepEqual(opts, commands) {
		t.Errorf("Invalid result.\n Expected: %s,\n obtained: %s", commands, opts)
	}

	args = []string{"server", "cr"}
	opts = strings.Split(autocomplete.Run(args), " ")
	sort.Strings(opts)
	if !reflect.DeepEqual(opts, commands) {
		t.Errorf("Invalid result.\n Expected: %s,\n obtained: %s", commands, opts)
	}

	args = []string{"serv", "create"}
	commands = []string{""}
	opts = strings.Split(autocomplete.Run(args), " ")
	sort.Strings(opts)
	if !reflect.DeepEqual(opts, commands) {
		t.Errorf("Invalid result.\n Expected: %s,\n obtained: %s", commands, opts)
	}
}
<|endoftext|>


<commit_before>// +build e2e

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

package e2e

import (
	"errors"
	"fmt"
	"path/filepath"
	"runtime"
	"testing"

	mf "github.com/jcrossley3/manifestival"
	apierrs "k8s.io/apimachinery/pkg/api/errors"
	"k8s.io/apimachinery/pkg/api/meta"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/util/wait"
	"knative.dev/pkg/test/logstream"
	"knative.dev/serving-operator/pkg/apis/serving/v1alpha1"
	"knative.dev/serving-operator/test"
	"knative.dev/serving-operator/test/resources"
)

// TestKnativeServingDeployment verifies the KnativeServing creation, deployment recreation, and KnativeServing deletion.
func TestKnativeServingDeployment(t *testing.T) {
	cancel := logstream.Start(t)
	defer cancel()
	clients := Setup(t)

	names := test.ResourceNames{
		KnativeServing: test.ServingOperatorName,
		Namespace:      test.ServingOperatorNamespace,
	}

	test.CleanupOnInterrupt(func() { test.TearDown(clients, names) })
	defer test.TearDown(clients, names)

	// Create a KnativeServing
	if _, err := resources.CreateKnativeServing(clients.KnativeServing(), names); err != nil {
		t.Fatalf("KnativeService %q failed to create: %v", names.KnativeServing, err)
	}

	// Test if KnativeServing can reach the READY status
	t.Run("create", func(t *testing.T) {
		knativeServingVerify(t, clients, names)
	})

	t.Run("configure", func(t *testing.T) {
		knativeServingVerify(t, clients, names)
		knativeServingConfigure(t, clients, names)
	})

	// Delete the deployments one by one to see if they will be recreated.
	t.Run("restore", func(t *testing.T) {
		knativeServingVerify(t, clients, names)
		deploymentRecreation(t, clients, names)
	})

	// Delete the KnativeServing to see if all resources will be removed
	t.Run("delete", func(t *testing.T) {
		knativeServingVerify(t, clients, names)
		knativeServingDelete(t, clients, names)
	})
}

// knativeServingVerify verifies if the KnativeServing can reach the READY status.
func knativeServingVerify(t *testing.T, clients *test.Clients, names test.ResourceNames) {
	if _, err := resources.WaitForKnativeServingState(clients.KnativeServing(), names.KnativeServing,
		resources.IsKnativeServingReady); err != nil {
		t.Fatalf("KnativeService %q failed to get to the READY status: %v", names.KnativeServing, err)
	}

}

// knativeServingConfigure verifies that KnativeServing config is set properly
func knativeServingConfigure(t *testing.T, clients *test.Clients, names test.ResourceNames) {
	// We'll arbitrarily choose logging and defaults config
	loggingConfigKey := "logging"
	loggingConfigMapName := fmt.Sprintf("%s/config-%s", names.Namespace, loggingConfigKey)
	defaultsConfigKey := "defaults"
	defaultsConfigMapName := fmt.Sprintf("%s/config-%s", names.Namespace, defaultsConfigKey)
	// Get the existing KS without any spec
	ks, err := clients.KnativeServing().Get(names.KnativeServing, metav1.GetOptions{})
	// Add config to its spec
	ks.Spec = v1alpha1.KnativeServingSpec{
		Config: map[string]map[string]string{
			defaultsConfigKey: {
				"revision-timeout-seconds": "200",
			},
			loggingConfigKey: {
				"loglevel.controller": "debug",
				"loglevel.autoscaler": "debug",
			},
		},
	}
	// Update it
	if ks, err = clients.KnativeServing().Update(ks); err != nil {
		t.Fatalf("KnativeServing %q failed to update: %v", names.KnativeServing, err)
	}
	// Verify the relevant configmaps have been updated
	err = resources.WaitForConfigMap(defaultsConfigMapName, clients.KubeClient.Kube, func(m map[string]string) bool {
		return m["revision-timeout-seconds"] == "200"
	})
	if err != nil {
		t.Fatalf("The operator failed to update %s configmap", defaultsConfigMapName)
	}
	err = resources.WaitForConfigMap(loggingConfigMapName, clients.KubeClient.Kube, func(m map[string]string) bool {
		return m["loglevel.controller"] == "debug" && m["loglevel.autoscaler"] == "debug"
	})
	if err != nil {
		t.Fatalf("The operator failed to update %s configmap", loggingConfigMapName)
	}

	// Delete a single key/value pair
	delete(ks.Spec.Config[loggingConfigKey], "loglevel.autoscaler")
	// Update it
	if ks, err = clients.KnativeServing().Update(ks); err != nil {
		t.Fatalf("KnativeServing %q failed to update: %v", names.KnativeServing, err)
	}
	// Verify the relevant configmap has been updated
	err = resources.WaitForConfigMap(loggingConfigMapName, clients.KubeClient.Kube, func(m map[string]string) bool {
		_, autoscalerKeyExists := m["loglevel.autoscaler"]
		// deleted key/value pair should be removed from the target config map
		return m["loglevel.controller"] == "debug" && !autoscalerKeyExists
	})
	if err != nil {
		t.Fatal("The operator failed to update the configmap")
	}

	// Use an empty map as the value
	ks.Spec.Config[defaultsConfigKey] = map[string]string{}
	// Update it
	if ks, err = clients.KnativeServing().Update(ks); err != nil {
		t.Fatalf("KnativeServing %q failed to update: %v", names.KnativeServing, err)
	}
	// Verify the relevant configmap has been updated and does not contain any keys except "_example"
	err = resources.WaitForConfigMap(defaultsConfigMapName, clients.KubeClient.Kube, func(m map[string]string) bool {
		_, exampleExists := m["_example"]
		return len(m) == 1 && exampleExists
	})
	if err != nil {
		t.Fatal("The operator failed to update the configmap")
	}

	// Now remove the config from the spec and update
	ks.Spec = v1alpha1.KnativeServingSpec{}
	if ks, err = clients.KnativeServing().Update(ks); err != nil {
		t.Fatalf("KnativeServing %q failed to update: %v", names.KnativeServing, err)
	}
	// And verify the configmap entry is gone
	err = resources.WaitForConfigMap(loggingConfigMapName, clients.KubeClient.Kube, func(m map[string]string) bool {
		_, exists := m["loglevel.controller"]
		return !exists
	})
	if err != nil {
		t.Fatal("The operator failed to revert the configmap")
	}
}

// deploymentRecreation verify whether all the deployments for knative serving are able to recreate, when they are deleted.
func deploymentRecreation(t *testing.T, clients *test.Clients, names test.ResourceNames) {
	dpList, err := clients.KubeClient.Kube.AppsV1().Deployments(names.Namespace).List(metav1.ListOptions{})
	if err != nil {
		t.Fatalf("Failed to get any deployment under the namespace %q: %v",
			test.ServingOperatorNamespace, err)
	}
	if len(dpList.Items) == 0 {
		t.Fatalf("No deployment under the namespace %q was found",
			test.ServingOperatorNamespace)
	}
	// Delete the first deployment and verify the operator recreates it
	deployment := dpList.Items[0]
	if err := clients.KubeClient.Kube.AppsV1().Deployments(deployment.Namespace).Delete(deployment.Name,
		&metav1.DeleteOptions{}); err != nil {
		t.Fatalf("Failed to delete deployment %s/%s: %v", deployment.Namespace, deployment.Name, err)
	}

	waitErr := wait.PollImmediate(resources.Interval, resources.Timeout, func() (bool, error) {
		dep, err := clients.KubeClient.Kube.AppsV1().Deployments(deployment.Namespace).Get(deployment.Name, metav1.GetOptions{})
		if err != nil {
			// If the deployment is not found, we continue to wait for the availability.
			if apierrs.IsNotFound(err) {
				return false, nil
			}
			return false, err
		}
		return resources.IsDeploymentAvailable(dep)
	})

	if waitErr != nil {
		t.Fatalf("The deployment %s/%s failed to reach the desired state: %v", deployment.Namespace, deployment.Name, err)
	}

	if _, err := resources.WaitForKnativeServingState(clients.KnativeServing(), test.ServingOperatorName,
		resources.IsKnativeServingReady); err != nil {
		t.Fatalf("KnativeService %q failed to reach the desired state: %v", test.ServingOperatorName, err)
	}
	t.Logf("The deployment %s/%s reached the desired state.", deployment.Namespace, deployment.Name)
}

// knativeServingDelete deletes tha KnativeServing to see if all resources will be deleted
func knativeServingDelete(t *testing.T, clients *test.Clients, names test.ResourceNames) {
	if err := clients.KnativeServing().Delete(names.KnativeServing, &metav1.DeleteOptions{}); err != nil {
		t.Fatalf("KnativeServing %q failed to delete: %v", names.KnativeServing, err)
	}
	err := wait.PollImmediate(resources.Interval, resources.Timeout, func() (bool, error) {
		_, err := clients.KnativeServing().Get(names.KnativeServing, metav1.GetOptions{})
		if apierrs.IsNotFound(err) {
			return true, nil
		}
		return false, err
	})
	if err != nil {
		t.Fatal("Timed out waiting on KnativeServing to delete", err)
	}
	_, b, _, _ := runtime.Caller(0)
	m, err := mf.NewManifest(filepath.Join((filepath.Dir(b)+"/.."), "config/"), false, clients.Config)
	if err != nil {
		t.Fatal("Failed to load manifest", err)
	}
	if err := verifyNoKnativeServings(clients); err != nil {
		t.Fatal(err)
	}
	for _, u := range m.Resources {
		if u.GetKind() == "Namespace" {
			// The namespace should be skipped, because when the CR is removed, the Manifest to be removed has
			// been modified, since the namespace can be injected.
			continue
		}
		gvrs, _ := meta.UnsafeGuessKindToResource(u.GroupVersionKind())
		if _, err := clients.Dynamic.Resource(gvrs).Get(u.GetName(), metav1.GetOptions{}); !apierrs.IsNotFound(err) {
			t.Fatalf("The %s %s failed to be deleted", u.GetKind(), u.GetName())
		}
	}
}

func verifyNoKnativeServings(clients *test.Clients) error {
	servings, err := clients.KnativeServingAll().List(metav1.ListOptions{})
	if err != nil {
		return err
	}
	if len(servings.Items) > 0 {
		return errors.New("Unable to verify cluster-scoped resources are deleted if any KnativeServing exists")
	}
	return nil
}
<commit_msg>Print error in knativeServingDelete (#268)<commit_after>// +build e2e

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

package e2e

import (
	"errors"
	"fmt"
	"path/filepath"
	"runtime"
	"testing"

	mf "github.com/jcrossley3/manifestival"
	apierrs "k8s.io/apimachinery/pkg/api/errors"
	"k8s.io/apimachinery/pkg/api/meta"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/util/wait"
	"knative.dev/pkg/test/logstream"
	"knative.dev/serving-operator/pkg/apis/serving/v1alpha1"
	"knative.dev/serving-operator/test"
	"knative.dev/serving-operator/test/resources"
)

// TestKnativeServingDeployment verifies the KnativeServing creation, deployment recreation, and KnativeServing deletion.
func TestKnativeServingDeployment(t *testing.T) {
	cancel := logstream.Start(t)
	defer cancel()
	clients := Setup(t)

	names := test.ResourceNames{
		KnativeServing: test.ServingOperatorName,
		Namespace:      test.ServingOperatorNamespace,
	}

	test.CleanupOnInterrupt(func() { test.TearDown(clients, names) })
	defer test.TearDown(clients, names)

	// Create a KnativeServing
	if _, err := resources.CreateKnativeServing(clients.KnativeServing(), names); err != nil {
		t.Fatalf("KnativeService %q failed to create: %v", names.KnativeServing, err)
	}

	// Test if KnativeServing can reach the READY status
	t.Run("create", func(t *testing.T) {
		knativeServingVerify(t, clients, names)
	})

	t.Run("configure", func(t *testing.T) {
		knativeServingVerify(t, clients, names)
		knativeServingConfigure(t, clients, names)
	})

	// Delete the deployments one by one to see if they will be recreated.
	t.Run("restore", func(t *testing.T) {
		knativeServingVerify(t, clients, names)
		deploymentRecreation(t, clients, names)
	})

	// Delete the KnativeServing to see if all resources will be removed
	t.Run("delete", func(t *testing.T) {
		knativeServingVerify(t, clients, names)
		knativeServingDelete(t, clients, names)
	})
}

// knativeServingVerify verifies if the KnativeServing can reach the READY status.
func knativeServingVerify(t *testing.T, clients *test.Clients, names test.ResourceNames) {
	if _, err := resources.WaitForKnativeServingState(clients.KnativeServing(), names.KnativeServing,
		resources.IsKnativeServingReady); err != nil {
		t.Fatalf("KnativeService %q failed to get to the READY status: %v", names.KnativeServing, err)
	}

}

// knativeServingConfigure verifies that KnativeServing config is set properly
func knativeServingConfigure(t *testing.T, clients *test.Clients, names test.ResourceNames) {
	// We'll arbitrarily choose logging and defaults config
	loggingConfigKey := "logging"
	loggingConfigMapName := fmt.Sprintf("%s/config-%s", names.Namespace, loggingConfigKey)
	defaultsConfigKey := "defaults"
	defaultsConfigMapName := fmt.Sprintf("%s/config-%s", names.Namespace, defaultsConfigKey)
	// Get the existing KS without any spec
	ks, err := clients.KnativeServing().Get(names.KnativeServing, metav1.GetOptions{})
	// Add config to its spec
	ks.Spec = v1alpha1.KnativeServingSpec{
		Config: map[string]map[string]string{
			defaultsConfigKey: {
				"revision-timeout-seconds": "200",
			},
			loggingConfigKey: {
				"loglevel.controller": "debug",
				"loglevel.autoscaler": "debug",
			},
		},
	}
	// Update it
	if ks, err = clients.KnativeServing().Update(ks); err != nil {
		t.Fatalf("KnativeServing %q failed to update: %v", names.KnativeServing, err)
	}
	// Verify the relevant configmaps have been updated
	err = resources.WaitForConfigMap(defaultsConfigMapName, clients.KubeClient.Kube, func(m map[string]string) bool {
		return m["revision-timeout-seconds"] == "200"
	})
	if err != nil {
		t.Fatalf("The operator failed to update %s configmap", defaultsConfigMapName)
	}
	err = resources.WaitForConfigMap(loggingConfigMapName, clients.KubeClient.Kube, func(m map[string]string) bool {
		return m["loglevel.controller"] == "debug" && m["loglevel.autoscaler"] == "debug"
	})
	if err != nil {
		t.Fatalf("The operator failed to update %s configmap", loggingConfigMapName)
	}

	// Delete a single key/value pair
	delete(ks.Spec.Config[loggingConfigKey], "loglevel.autoscaler")
	// Update it
	if ks, err = clients.KnativeServing().Update(ks); err != nil {
		t.Fatalf("KnativeServing %q failed to update: %v", names.KnativeServing, err)
	}
	// Verify the relevant configmap has been updated
	err = resources.WaitForConfigMap(loggingConfigMapName, clients.KubeClient.Kube, func(m map[string]string) bool {
		_, autoscalerKeyExists := m["loglevel.autoscaler"]
		// deleted key/value pair should be removed from the target config map
		return m["loglevel.controller"] == "debug" && !autoscalerKeyExists
	})
	if err != nil {
		t.Fatal("The operator failed to update the configmap")
	}

	// Use an empty map as the value
	ks.Spec.Config[defaultsConfigKey] = map[string]string{}
	// Update it
	if ks, err = clients.KnativeServing().Update(ks); err != nil {
		t.Fatalf("KnativeServing %q failed to update: %v", names.KnativeServing, err)
	}
	// Verify the relevant configmap has been updated and does not contain any keys except "_example"
	err = resources.WaitForConfigMap(defaultsConfigMapName, clients.KubeClient.Kube, func(m map[string]string) bool {
		_, exampleExists := m["_example"]
		return len(m) == 1 && exampleExists
	})
	if err != nil {
		t.Fatal("The operator failed to update the configmap")
	}

	// Now remove the config from the spec and update
	ks.Spec = v1alpha1.KnativeServingSpec{}
	if ks, err = clients.KnativeServing().Update(ks); err != nil {
		t.Fatalf("KnativeServing %q failed to update: %v", names.KnativeServing, err)
	}
	// And verify the configmap entry is gone
	err = resources.WaitForConfigMap(loggingConfigMapName, clients.KubeClient.Kube, func(m map[string]string) bool {
		_, exists := m["loglevel.controller"]
		return !exists
	})
	if err != nil {
		t.Fatal("The operator failed to revert the configmap")
	}
}

// deploymentRecreation verify whether all the deployments for knative serving are able to recreate, when they are deleted.
func deploymentRecreation(t *testing.T, clients *test.Clients, names test.ResourceNames) {
	dpList, err := clients.KubeClient.Kube.AppsV1().Deployments(names.Namespace).List(metav1.ListOptions{})
	if err != nil {
		t.Fatalf("Failed to get any deployment under the namespace %q: %v",
			test.ServingOperatorNamespace, err)
	}
	if len(dpList.Items) == 0 {
		t.Fatalf("No deployment under the namespace %q was found",
			test.ServingOperatorNamespace)
	}
	// Delete the first deployment and verify the operator recreates it
	deployment := dpList.Items[0]
	if err := clients.KubeClient.Kube.AppsV1().Deployments(deployment.Namespace).Delete(deployment.Name,
		&metav1.DeleteOptions{}); err != nil {
		t.Fatalf("Failed to delete deployment %s/%s: %v", deployment.Namespace, deployment.Name, err)
	}

	waitErr := wait.PollImmediate(resources.Interval, resources.Timeout, func() (bool, error) {
		dep, err := clients.KubeClient.Kube.AppsV1().Deployments(deployment.Namespace).Get(deployment.Name, metav1.GetOptions{})
		if err != nil {
			// If the deployment is not found, we continue to wait for the availability.
			if apierrs.IsNotFound(err) {
				return false, nil
			}
			return false, err
		}
		return resources.IsDeploymentAvailable(dep)
	})

	if waitErr != nil {
		t.Fatalf("The deployment %s/%s failed to reach the desired state: %v", deployment.Namespace, deployment.Name, err)
	}

	if _, err := resources.WaitForKnativeServingState(clients.KnativeServing(), test.ServingOperatorName,
		resources.IsKnativeServingReady); err != nil {
		t.Fatalf("KnativeService %q failed to reach the desired state: %v", test.ServingOperatorName, err)
	}
	t.Logf("The deployment %s/%s reached the desired state.", deployment.Namespace, deployment.Name)
}

// knativeServingDelete deletes tha KnativeServing to see if all resources will be deleted
func knativeServingDelete(t *testing.T, clients *test.Clients, names test.ResourceNames) {
	if err := clients.KnativeServing().Delete(names.KnativeServing, &metav1.DeleteOptions{}); err != nil {
		t.Fatalf("KnativeServing %q failed to delete: %v", names.KnativeServing, err)
	}
	err := wait.PollImmediate(resources.Interval, resources.Timeout, func() (bool, error) {
		_, err := clients.KnativeServing().Get(names.KnativeServing, metav1.GetOptions{})
		if apierrs.IsNotFound(err) {
			return true, nil
		}
		return false, err
	})
	if err != nil {
		t.Fatal("Timed out waiting on KnativeServing to delete", err)
	}
	_, b, _, _ := runtime.Caller(0)
	m, err := mf.NewManifest(filepath.Join((filepath.Dir(b)+"/.."), "config/"), false, clients.Config)
	if err != nil {
		t.Fatal("Failed to load manifest", err)
	}
	if err := verifyNoKnativeServings(clients); err != nil {
		t.Fatal(err)
	}
	for _, u := range m.Resources {
		if u.GetKind() == "Namespace" {
			// The namespace should be skipped, because when the CR is removed, the Manifest to be removed has
			// been modified, since the namespace can be injected.
			continue
		}
		gvrs, _ := meta.UnsafeGuessKindToResource(u.GroupVersionKind())
		if _, err := clients.Dynamic.Resource(gvrs).Get(u.GetName(), metav1.GetOptions{}); !apierrs.IsNotFound(err) {
			t.Fatalf("The %s %s failed to be deleted: %v", u.GetKind(), u.GetName(), err)
		}
	}
}

func verifyNoKnativeServings(clients *test.Clients) error {
	servings, err := clients.KnativeServingAll().List(metav1.ListOptions{})
	if err != nil {
		return err
	}
	if len(servings.Items) > 0 {
		return errors.New("Unable to verify cluster-scoped resources are deleted if any KnativeServing exists")
	}
	return nil
}
<|endoftext|>


<commit_before>package aws

import (
	"fmt"
	"testing"

	"github.com/hashicorp/terraform/helper/acctest"
	"github.com/hashicorp/terraform/helper/resource"
	"github.com/hashicorp/terraform/terraform"
)

func TestAccDataSourceAwsBatchJobQueue(t *testing.T) {
	rName := acctest.RandomWithPrefix("tf_acc_test_")
	resourceName := "aws_batch_job_queue.test"
	datasourceName := "data.aws_batch_job_queue.by_name"

	resource.Test(t, resource.TestCase{
		PreCheck:  func() { testAccPreCheck(t) },
		Providers: testAccProviders,
		Steps: []resource.TestStep{
			resource.TestStep{
				Config: testAccDataSourceAwsBatchJobQueueConfig(rName),
				Check: resource.ComposeTestCheckFunc(
					testAccDataSourceAwsBatchJobQueueCheck(datasourceName, resourceName),
				),
			},
		},
	})
}

func testAccDataSourceAwsBatchJobQueueCheck(datasourceName, resourceName string) resource.TestCheckFunc {
	return func(s *terraform.State) error {
		ds, ok := s.RootModule().Resources[datasourceName]
		if !ok {
			return fmt.Errorf("root module has no data source called %s", datasourceName)
		}

		jobQueueRs, ok := s.RootModule().Resources[resourceName]
		if !ok {
			return fmt.Errorf("root module has no resource called %s", resourceName)
		}

		attrNames := []string{
			"arn",
			"name",
			"state",
			"priority",
		}

		for _, attrName := range attrNames {
			if ds.Primary.Attributes[attrName] != jobQueueRs.Primary.Attributes[attrName] {
				return fmt.Errorf(
					"%s is %s; want %s",
					attrName,
					ds.Primary.Attributes[attrName],
					jobQueueRs.Primary.Attributes[attrName],
				)
			}
		}

		return nil
	}
}

func testAccDataSourceAwsBatchJobQueueConfig(rName string) string {
	return fmt.Sprintf(`
resource "aws_iam_role" "ecs_instance_role" {
  name = "ecs_%[1]s"
  assume_role_policy = <<EOF
{
    "Version": "2012-10-17",
    "Statement": [
    {
        "Action": "sts:AssumeRole",
        "Effect": "Allow",
        "Principal": {
        "Service": "ec2.amazonaws.com"
        }
    }
    ]
}
EOF
}

resource "aws_iam_role_policy_attachment" "ecs_instance_role" {
  role       = "${aws_iam_role.ecs_instance_role.name}"
  policy_arn = "arn:aws:iam::aws:policy/service-role/AmazonEC2ContainerServiceforEC2Role"
}

resource "aws_iam_instance_profile" "ecs_instance_role" {
  name  = "ecs_%[1]s"
  role = "${aws_iam_role.ecs_instance_role.name}"
}

resource "aws_iam_role" "aws_batch_service_role" {
  name = "batch_%[1]s"
  assume_role_policy = <<EOF
{
    "Version": "2012-10-17",
    "Statement": [
    {
        "Action": "sts:AssumeRole",
        "Effect": "Allow",
        "Principal": {
        "Service": "batch.amazonaws.com"
        }
    }
    ]
}
EOF
}

resource "aws_iam_role_policy_attachment" "aws_batch_service_role" {
  role       = "${aws_iam_role.aws_batch_service_role.name}"
  policy_arn = "arn:aws:iam::aws:policy/service-role/AWSBatchServiceRole"
}

resource "aws_security_group" "sample" {
  name = "%[1]s"
}

resource "aws_vpc" "sample" {
  cidr_block = "10.1.0.0/16"
}

resource "aws_subnet" "sample" {
  vpc_id = "${aws_vpc.sample.id}"
  cidr_block = "10.1.1.0/24"
}

resource "aws_batch_compute_environment" "sample" {
  compute_environment_name = "%[1]s"
  compute_resources {
    instance_role = "${aws_iam_instance_profile.ecs_instance_role.arn}"
    instance_type = [
      "c4.large",
    ]
    max_vcpus = 16
    min_vcpus = 0
    security_group_ids = [
      "${aws_security_group.sample.id}"
    ]
    subnets = [
      "${aws_subnet.sample.id}"
    ]
    type = "EC2"
  }
  service_role = "${aws_iam_role.aws_batch_service_role.arn}"
  type = "MANAGED"
  depends_on = ["aws_iam_role_policy_attachment.aws_batch_service_role"]
}

resource "aws_batch_job_queue" "test" {
  name = "%[1]s"
  state = "ENABLED"
  priority = 1
  compute_environments = ["${aws_batch_compute_environment.sample}"]
}

resource "aws_batch_job_queue" "wrong" {
  name = "%[1]s_wrong"
  state = "ENABLED"
  priority = 2
  compute_environments = ["${aws_batch_compute_environment.sample}"]
}

data "aws_batch_job_queue" "by_name" {
  name = "${aws_batch_job_queue.test.name}"
}
`, rName)
}
<commit_msg>Add missing arn to test compute environments<commit_after>package aws

import (
	"fmt"
	"testing"

	"github.com/hashicorp/terraform/helper/acctest"
	"github.com/hashicorp/terraform/helper/resource"
	"github.com/hashicorp/terraform/terraform"
)

func TestAccDataSourceAwsBatchJobQueue(t *testing.T) {
	rName := acctest.RandomWithPrefix("tf_acc_test_")
	resourceName := "aws_batch_job_queue.test"
	datasourceName := "data.aws_batch_job_queue.by_name"

	resource.Test(t, resource.TestCase{
		PreCheck:  func() { testAccPreCheck(t) },
		Providers: testAccProviders,
		Steps: []resource.TestStep{
			resource.TestStep{
				Config: testAccDataSourceAwsBatchJobQueueConfig(rName),
				Check: resource.ComposeTestCheckFunc(
					testAccDataSourceAwsBatchJobQueueCheck(datasourceName, resourceName),
				),
			},
		},
	})
}

func testAccDataSourceAwsBatchJobQueueCheck(datasourceName, resourceName string) resource.TestCheckFunc {
	return func(s *terraform.State) error {
		ds, ok := s.RootModule().Resources[datasourceName]
		if !ok {
			return fmt.Errorf("root module has no data source called %s", datasourceName)
		}

		jobQueueRs, ok := s.RootModule().Resources[resourceName]
		if !ok {
			return fmt.Errorf("root module has no resource called %s", resourceName)
		}

		attrNames := []string{
			"arn",
			"name",
			"state",
			"priority",
		}

		for _, attrName := range attrNames {
			if ds.Primary.Attributes[attrName] != jobQueueRs.Primary.Attributes[attrName] {
				return fmt.Errorf(
					"%s is %s; want %s",
					attrName,
					ds.Primary.Attributes[attrName],
					jobQueueRs.Primary.Attributes[attrName],
				)
			}
		}

		return nil
	}
}

func testAccDataSourceAwsBatchJobQueueConfig(rName string) string {
	return fmt.Sprintf(`
resource "aws_iam_role" "ecs_instance_role" {
  name = "ecs_%[1]s"
  assume_role_policy = <<EOF
{
    "Version": "2012-10-17",
    "Statement": [
    {
        "Action": "sts:AssumeRole",
        "Effect": "Allow",
        "Principal": {
        "Service": "ec2.amazonaws.com"
        }
    }
    ]
}
EOF
}

resource "aws_iam_role_policy_attachment" "ecs_instance_role" {
  role       = "${aws_iam_role.ecs_instance_role.name}"
  policy_arn = "arn:aws:iam::aws:policy/service-role/AmazonEC2ContainerServiceforEC2Role"
}

resource "aws_iam_instance_profile" "ecs_instance_role" {
  name  = "ecs_%[1]s"
  role = "${aws_iam_role.ecs_instance_role.name}"
}

resource "aws_iam_role" "aws_batch_service_role" {
  name = "batch_%[1]s"
  assume_role_policy = <<EOF
{
    "Version": "2012-10-17",
    "Statement": [
    {
        "Action": "sts:AssumeRole",
        "Effect": "Allow",
        "Principal": {
        "Service": "batch.amazonaws.com"
        }
    }
    ]
}
EOF
}

resource "aws_iam_role_policy_attachment" "aws_batch_service_role" {
  role       = "${aws_iam_role.aws_batch_service_role.name}"
  policy_arn = "arn:aws:iam::aws:policy/service-role/AWSBatchServiceRole"
}

resource "aws_security_group" "sample" {
  name = "%[1]s"
}

resource "aws_vpc" "sample" {
  cidr_block = "10.1.0.0/16"
}

resource "aws_subnet" "sample" {
  vpc_id = "${aws_vpc.sample.id}"
  cidr_block = "10.1.1.0/24"
}

resource "aws_batch_compute_environment" "sample" {
  compute_environment_name = "%[1]s"
  compute_resources {
    instance_role = "${aws_iam_instance_profile.ecs_instance_role.arn}"
    instance_type = [
      "c4.large",
    ]
    max_vcpus = 16
    min_vcpus = 0
    security_group_ids = [
      "${aws_security_group.sample.id}"
    ]
    subnets = [
      "${aws_subnet.sample.id}"
    ]
    type = "EC2"
  }
  service_role = "${aws_iam_role.aws_batch_service_role.arn}"
  type = "MANAGED"
  depends_on = ["aws_iam_role_policy_attachment.aws_batch_service_role"]
}

resource "aws_batch_job_queue" "test" {
  name = "%[1]s"
  state = "ENABLED"
  priority = 1
  compute_environments = ["${aws_batch_compute_environment.sample.arn}"]
}

resource "aws_batch_job_queue" "wrong" {
  name = "%[1]s_wrong"
  state = "ENABLED"
  priority = 2
  compute_environments = ["${aws_batch_compute_environment.sample.arn}"]
}

data "aws_batch_job_queue" "by_name" {
  name = "${aws_batch_job_queue.test.name}"
}
`, rName)
}
<|endoftext|>