<commit_before>package bolt

import (
	"context"

	"github.com/boltdb/bolt"
	"github.com/influxdata/chronograf"
	"github.com/influxdata/chronograf/bolt/internal"
)

// Ensure SourcesStore implements chronograf.SourcesStore.
var _ chronograf.SourcesStore = &SourcesStore{}

var SourcesBucket = []byte("Sources")

type SourcesStore struct {
	client *Client
}

// All returns all known sources
func (s *SourcesStore) All(ctx context.Context) ([]chronograf.Source, error) {
	var srcs []chronograf.Source
	if err := s.client.db.View(func(tx *bolt.Tx) error {
		var err error
		srcs, err = s.all(ctx, tx)
		if err != nil {
			return err
		}
		return nil
	}); err != nil {
		return nil, err
	}

	return srcs, nil

}

// Add creates a new Source in the SourceStore.
func (s *SourcesStore) Add(ctx context.Context, src chronograf.Source) (chronograf.Source, error) {

	// force first source added to be default
	if srcs, err := s.All(ctx); err != nil {
		return chronograf.Source{}, err
	} else if len(srcs) == 0 {
		src.Default = true
	}

	if err := s.client.db.Update(func(tx *bolt.Tx) error {
		return s.add(ctx, &src, tx)
	}); err != nil {
		return chronograf.Source{}, err
	}

	return src, nil
}

// Delete removes the Source from the SourcesStore
func (s *SourcesStore) Delete(ctx context.Context, src chronograf.Source) error {
	if err := s.client.db.Update(func(tx *bolt.Tx) error {
		if err := s.setRandomDefault(ctx, src, tx); err != nil {
			return err
		}
		return s.delete(ctx, src, tx)
	}); err != nil {
		return err
	}

	return nil
}

// Get returns a Source if the id exists.
func (s *SourcesStore) Get(ctx context.Context, id int) (chronograf.Source, error) {
	var src chronograf.Source
	if err := s.client.db.View(func(tx *bolt.Tx) error {
		var err error
		src, err = s.get(ctx, id, tx)
		if err != nil {
			return err
		}
		return nil
	}); err != nil {
		return chronograf.Source{}, err
	}

	return src, nil
}

// Update a Source
func (s *SourcesStore) Update(ctx context.Context, src chronograf.Source) error {
	if err := s.client.db.Update(func(tx *bolt.Tx) error {
		return s.update(ctx, src, tx)
	}); err != nil {
		return err
	}

	return nil
}

func (s *SourcesStore) all(ctx context.Context, tx *bolt.Tx) ([]chronograf.Source, error) {
	var srcs []chronograf.Source
	if err := tx.Bucket(SourcesBucket).ForEach(func(k, v []byte) error {
		var src chronograf.Source
		if err := internal.UnmarshalSource(v, &src); err != nil {
			return err
		}
		srcs = append(srcs, src)
		return nil
	}); err != nil {
		return srcs, err
	}
	return srcs, nil
}

func (s *SourcesStore) add(ctx context.Context, src *chronograf.Source, tx *bolt.Tx) error {
	b := tx.Bucket(SourcesBucket)
	seq, err := b.NextSequence()
	if err != nil {
		return err
	}
	src.ID = int(seq)

	if src.Default {
		if err := s.resetDefaultSource(ctx, tx); err != nil {
			return err
		}
	}

	if v, err := internal.MarshalSource(*src); err != nil {
		return err
	} else if err := b.Put(itob(src.ID), v); err != nil {
		return err
	}
	return nil
}

func (s *SourcesStore) delete(ctx context.Context, src chronograf.Source, tx *bolt.Tx) error {
	if err := tx.Bucket(SourcesBucket).Delete(itob(src.ID)); err != nil {
		return err
	}
	return nil
}

func (s *SourcesStore) get(ctx context.Context, id int, tx *bolt.Tx) (chronograf.Source, error) {
	var src chronograf.Source
	if v := tx.Bucket(SourcesBucket).Get(itob(id)); v == nil {
		return src, chronograf.ErrSourceNotFound
	} else if err := internal.UnmarshalSource(v, &src); err != nil {
		return src, err
	}
	return src, nil
}

func (s *SourcesStore) update(ctx context.Context, src chronograf.Source, tx *bolt.Tx) error {
	// Get an existing soource with the same ID.
	b := tx.Bucket(SourcesBucket)
	if v := b.Get(itob(src.ID)); v == nil {
		return chronograf.ErrSourceNotFound
	}

	if src.Default {
		if err := s.resetDefaultSource(ctx, tx); err != nil {
			return err
		}
	}

	if v, err := internal.MarshalSource(src); err != nil {
		return err
	} else if err := b.Put(itob(src.ID), v); err != nil {
		return err
	}
	return nil
}

// resetDefaultSource unsets the Default flag on all sources
func (s *SourcesStore) resetDefaultSource(ctx context.Context, tx *bolt.Tx) error {
	b := tx.Bucket(SourcesBucket)
	srcs, err := s.all(ctx, tx)
	if err != nil {
		return err
	}

	for _, other := range srcs {
		if other.Default {
			other.Default = false
			if v, err := internal.MarshalSource(other); err != nil {
				return err
			} else if err := b.Put(itob(other.ID), v); err != nil {
				return err
			}
		}
	}
	return nil
}

// setRandomDefault will locate a source other than the provided
// chronograf.Source and set it as the default source. If no other sources are
// available, the provided source will be set to the default source if is not
// already. It assumes that the provided chronograf.Source has been persisted.
func (s *SourcesStore) setRandomDefault(ctx context.Context, src chronograf.Source, tx *bolt.Tx) error {
	// Check if requested source is the current default
	if target, err := s.get(ctx, src.ID, tx); err != nil {
		return err
	} else if target.Default {
		// Locate another source to be the new default
		if srcs, err := s.all(ctx, tx); err != nil {
			return err
		} else {
			var other *chronograf.Source
			for idx, _ := range srcs {
				other = &srcs[idx]
				// avoid selecting the source we're about to delete as the new default
				if other.ID != target.ID {
					break
				}
			}

			// set the other to be the default
			other.Default = true
			if err := s.update(ctx, *other, tx); err != nil {
				return err
			}
		}
	}
	return nil
}
<commit_msg>Fix bolt sources go vet<commit_after>package bolt

import (
	"context"

	"github.com/boltdb/bolt"
	"github.com/influxdata/chronograf"
	"github.com/influxdata/chronograf/bolt/internal"
)

// Ensure SourcesStore implements chronograf.SourcesStore.
var _ chronograf.SourcesStore = &SourcesStore{}

var SourcesBucket = []byte("Sources")

type SourcesStore struct {
	client *Client
}

// All returns all known sources
func (s *SourcesStore) All(ctx context.Context) ([]chronograf.Source, error) {
	var srcs []chronograf.Source
	if err := s.client.db.View(func(tx *bolt.Tx) error {
		var err error
		srcs, err = s.all(ctx, tx)
		if err != nil {
			return err
		}
		return nil
	}); err != nil {
		return nil, err
	}

	return srcs, nil

}

// Add creates a new Source in the SourceStore.
func (s *SourcesStore) Add(ctx context.Context, src chronograf.Source) (chronograf.Source, error) {

	// force first source added to be default
	if srcs, err := s.All(ctx); err != nil {
		return chronograf.Source{}, err
	} else if len(srcs) == 0 {
		src.Default = true
	}

	if err := s.client.db.Update(func(tx *bolt.Tx) error {
		return s.add(ctx, &src, tx)
	}); err != nil {
		return chronograf.Source{}, err
	}

	return src, nil
}

// Delete removes the Source from the SourcesStore
func (s *SourcesStore) Delete(ctx context.Context, src chronograf.Source) error {
	if err := s.client.db.Update(func(tx *bolt.Tx) error {
		if err := s.setRandomDefault(ctx, src, tx); err != nil {
			return err
		}
		return s.delete(ctx, src, tx)
	}); err != nil {
		return err
	}

	return nil
}

// Get returns a Source if the id exists.
func (s *SourcesStore) Get(ctx context.Context, id int) (chronograf.Source, error) {
	var src chronograf.Source
	if err := s.client.db.View(func(tx *bolt.Tx) error {
		var err error
		src, err = s.get(ctx, id, tx)
		if err != nil {
			return err
		}
		return nil
	}); err != nil {
		return chronograf.Source{}, err
	}

	return src, nil
}

// Update a Source
func (s *SourcesStore) Update(ctx context.Context, src chronograf.Source) error {
	if err := s.client.db.Update(func(tx *bolt.Tx) error {
		return s.update(ctx, src, tx)
	}); err != nil {
		return err
	}

	return nil
}

func (s *SourcesStore) all(ctx context.Context, tx *bolt.Tx) ([]chronograf.Source, error) {
	var srcs []chronograf.Source
	if err := tx.Bucket(SourcesBucket).ForEach(func(k, v []byte) error {
		var src chronograf.Source
		if err := internal.UnmarshalSource(v, &src); err != nil {
			return err
		}
		srcs = append(srcs, src)
		return nil
	}); err != nil {
		return srcs, err
	}
	return srcs, nil
}

func (s *SourcesStore) add(ctx context.Context, src *chronograf.Source, tx *bolt.Tx) error {
	b := tx.Bucket(SourcesBucket)
	seq, err := b.NextSequence()
	if err != nil {
		return err
	}
	src.ID = int(seq)

	if src.Default {
		if err := s.resetDefaultSource(ctx, tx); err != nil {
			return err
		}
	}

	if v, err := internal.MarshalSource(*src); err != nil {
		return err
	} else if err := b.Put(itob(src.ID), v); err != nil {
		return err
	}
	return nil
}

func (s *SourcesStore) delete(ctx context.Context, src chronograf.Source, tx *bolt.Tx) error {
	if err := tx.Bucket(SourcesBucket).Delete(itob(src.ID)); err != nil {
		return err
	}
	return nil
}

func (s *SourcesStore) get(ctx context.Context, id int, tx *bolt.Tx) (chronograf.Source, error) {
	var src chronograf.Source
	if v := tx.Bucket(SourcesBucket).Get(itob(id)); v == nil {
		return src, chronograf.ErrSourceNotFound
	} else if err := internal.UnmarshalSource(v, &src); err != nil {
		return src, err
	}
	return src, nil
}

func (s *SourcesStore) update(ctx context.Context, src chronograf.Source, tx *bolt.Tx) error {
	// Get an existing soource with the same ID.
	b := tx.Bucket(SourcesBucket)
	if v := b.Get(itob(src.ID)); v == nil {
		return chronograf.ErrSourceNotFound
	}

	if src.Default {
		if err := s.resetDefaultSource(ctx, tx); err != nil {
			return err
		}
	}

	if v, err := internal.MarshalSource(src); err != nil {
		return err
	} else if err := b.Put(itob(src.ID), v); err != nil {
		return err
	}
	return nil
}

// resetDefaultSource unsets the Default flag on all sources
func (s *SourcesStore) resetDefaultSource(ctx context.Context, tx *bolt.Tx) error {
	b := tx.Bucket(SourcesBucket)
	srcs, err := s.all(ctx, tx)
	if err != nil {
		return err
	}

	for _, other := range srcs {
		if other.Default {
			other.Default = false
			if v, err := internal.MarshalSource(other); err != nil {
				return err
			} else if err := b.Put(itob(other.ID), v); err != nil {
				return err
			}
		}
	}
	return nil
}

// setRandomDefault will locate a source other than the provided
// chronograf.Source and set it as the default source. If no other sources are
// available, the provided source will be set to the default source if is not
// already. It assumes that the provided chronograf.Source has been persisted.
func (s *SourcesStore) setRandomDefault(ctx context.Context, src chronograf.Source, tx *bolt.Tx) error {
	// Check if requested source is the current default
	if target, err := s.get(ctx, src.ID, tx); err != nil {
		return err
	} else if target.Default {
		// Locate another source to be the new default
		srcs, err := s.all(ctx, tx)
		if err != nil {
			return err
		}
		var other *chronograf.Source
		for idx := range srcs {
			other = &srcs[idx]
			// avoid selecting the source we're about to delete as the new default
			if other.ID != target.ID {
				break
			}
		}

		// set the other to be the default
		other.Default = true
		if err := s.update(ctx, *other, tx); err != nil {
			return err
		}
	}
	return nil
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"path/filepath"
	"strings"
	"syscall"

	"github.com/Sirupsen/logrus"
	"github.com/opencontainers/ocitools/cmd/runtimetest/mount"
	rspec "github.com/opencontainers/runtime-spec/specs-go"
	"github.com/syndtr/gocapability/capability"
	"github.com/urfave/cli"
)

// PR_GET_NO_NEW_PRIVS isn't exposed in Golang so we define it ourselves copying the value from
// the kernel
const PR_GET_NO_NEW_PRIVS = 39

var (
	defaultFS = map[string]string{
		"/proc":    "proc",
		"/sys":     "sysfs",
		"/dev/pts": "devpts",
		"/dev/shm": "tmpfs",
	}
)

type validation func(*rspec.Spec) error

func loadSpecConfig() (spec *rspec.Spec, err error) {
	cPath := "config.json"
	cf, err := os.Open(cPath)
	if err != nil {
		if os.IsNotExist(err) {
			return nil, fmt.Errorf("config.json not found")
		}
	}
	defer cf.Close()

	if err = json.NewDecoder(cf).Decode(&spec); err != nil {
		return
	}
	return spec, nil
}

func validateProcess(spec *rspec.Spec) error {
	logrus.Debugf("validating container process")
	uid := os.Getuid()
	if uint32(uid) != spec.Process.User.UID {
		return fmt.Errorf("UID expected: %v, actual: %v", spec.Process.User.UID, uid)
	}
	gid := os.Getgid()
	if uint32(gid) != spec.Process.User.GID {
		return fmt.Errorf("GID expected: %v, actual: %v", spec.Process.User.GID, gid)
	}

	groups, err := os.Getgroups()
	if err != nil {
		return err
	}

	groupsMap := make(map[int]bool)
	for _, g := range groups {
		groupsMap[g] = true
	}

	for _, g := range spec.Process.User.AdditionalGids {
		if !groupsMap[int(g)] {
			return fmt.Errorf("Groups expected: %v, actual (should be superset): %v", spec.Process.User.AdditionalGids, groups)
		}
	}

	if spec.Process.Cwd != "" {
		cwd, err := os.Getwd()
		if err != nil {
			return err
		}
		if cwd != spec.Process.Cwd {
			return fmt.Errorf("Cwd expected: %v, actual: %v", spec.Process.Cwd, cwd)
		}
	}

	cmdlineBytes, err := ioutil.ReadFile("/proc/1/cmdline")
	if err != nil {
		return err
	}

	args := bytes.Split(bytes.Trim(cmdlineBytes, "\x00"), []byte("\x00"))
	if len(args) != len(spec.Process.Args) {
		return fmt.Errorf("Process arguments expected: %v, actual: %v", len(spec.Process.Args), len(args))
	}
	for i, a := range args {
		if string(a) != spec.Process.Args[i] {
			return fmt.Errorf("Process arguments expected: %v, actual: %v", string(a), spec.Process.Args[i])
		}
	}

	for _, env := range spec.Process.Env {
		parts := strings.Split(env, "=")
		key := parts[0]
		expectedValue := parts[1]
		actualValue := os.Getenv(key)
		if actualValue != expectedValue {
			return fmt.Errorf("Env %v expected: %v, actual: %v", key, expectedValue, actualValue)
		}
	}

	ret, _, errno := syscall.Syscall6(syscall.SYS_PRCTL, PR_GET_NO_NEW_PRIVS, 0, 0, 0, 0, 0)
	if errno != 0 {
		return errno
	}
	if spec.Process.NoNewPrivileges && ret != 1 {
		return fmt.Errorf("NoNewPrivileges expected: true, actual: false")
	}
	if !spec.Process.NoNewPrivileges && ret != 0 {
		return fmt.Errorf("NoNewPrivileges expected: false, actual: true")
	}

	return nil
}

func validateCapabilities(spec *rspec.Spec) error {
	logrus.Debugf("validating capabilities")

	last := capability.CAP_LAST_CAP
	// workaround for RHEL6 which has no /proc/sys/kernel/cap_last_cap
	if last == capability.Cap(63) {
		last = capability.CAP_BLOCK_SUSPEND
	}

	processCaps, err := capability.NewPid(1)
	if err != nil {
		return err
	}

	expectedCaps := make(map[string]bool)
	for _, ec := range spec.Process.Capabilities {
		expectedCaps[ec] = true
	}

	for _, cap := range capability.List() {
		if cap > last {
			continue
		}

		capKey := fmt.Sprintf("CAP_%s", strings.ToUpper(cap.String()))
		expectedSet := expectedCaps[capKey]
		actuallySet := processCaps.Get(capability.EFFECTIVE, cap)
		if expectedSet != actuallySet {
			if expectedSet {
				return fmt.Errorf("Expected Capability %v not set for process", cap.String())
			}
			return fmt.Errorf("Unexpected Capability %v set for process", cap.String())
		}
	}

	return nil
}

func validateHostname(spec *rspec.Spec) error {
	logrus.Debugf("validating hostname")
	hostname, err := os.Hostname()
	if err != nil {
		return err
	}
	if spec.Hostname != "" && hostname != spec.Hostname {
		return fmt.Errorf("Hostname expected: %v, actual: %v", spec.Hostname, hostname)
	}
	return nil
}

func validateRlimits(spec *rspec.Spec) error {
	logrus.Debugf("validating rlimits")
	for _, r := range spec.Process.Rlimits {
		rl, err := strToRlimit(r.Type)
		if err != nil {
			return err
		}

		var rlimit syscall.Rlimit
		if err := syscall.Getrlimit(rl, &rlimit); err != nil {
			return err
		}

		if rlimit.Cur != r.Soft {
			return fmt.Errorf("%v rlimit soft expected: %v, actual: %v", r.Type, r.Soft, rlimit.Cur)
		}
		if rlimit.Max != r.Hard {
			return fmt.Errorf("%v rlimit hard expected: %v, actual: %v", r.Type, r.Hard, rlimit.Max)
		}
	}
	return nil
}

func validateSysctls(spec *rspec.Spec) error {
	logrus.Debugf("validating sysctls")
	for k, v := range spec.Linux.Sysctl {
		keyPath := filepath.Join("/proc/sys", strings.Replace(k, ".", "/", -1))
		vBytes, err := ioutil.ReadFile(keyPath)
		if err != nil {
			return err
		}
		value := strings.TrimSpace(string(bytes.Trim(vBytes, "\x00")))
		if value != v {
			return fmt.Errorf("Sysctl %v value expected: %v, actual: %v", k, v, value)
		}
	}
	return nil
}

func testWriteAccess(path string) error {
	tmpfile, err := ioutil.TempFile(path, "Test")
	if err != nil {
		return err
	}

	tmpfile.Close()
	os.RemoveAll(filepath.Join(path, tmpfile.Name()))

	return nil
}

func validateRootFS(spec *rspec.Spec) error {
	logrus.Debugf("validating root filesystem")
	if spec.Root.Readonly {
		err := testWriteAccess("/")
		if err == nil {
			return fmt.Errorf("Rootfs should be readonly")
		}
	}

	return nil
}

func validateDefaultFS(spec *rspec.Spec) error {
	logrus.Debugf("validating linux default filesystem")

	mountInfos, err := mount.GetMounts()
	if err != nil {
		return err
	}

	mountsMap := make(map[string]string)
	for _, mountInfo := range mountInfos {
		mountsMap[mountInfo.Mountpoint] = mountInfo.Fstype
	}

	for fs, fstype := range defaultFS {
		if !(mountsMap[fs] == fstype) {
			return fmt.Errorf("%v must exist and expected type is %v", fs, fstype)
		}
	}

	return nil
}

func validateMaskedPaths(spec *rspec.Spec) error {
	logrus.Debugf("validating maskedPaths")
	for _, maskedPath := range spec.Linux.MaskedPaths {
		f, err := os.Open(maskedPath)
		if err != nil {
			return err
		}
		defer f.Close()
		b := make([]byte, 1)
		_, err = f.Read(b)
		if err != io.EOF {
			return fmt.Errorf("%v should not be readable", maskedPath)
		}
	}
	return nil
}

func validateROPaths(spec *rspec.Spec) error {
	logrus.Debugf("validating readonlyPaths")
	for _, v := range spec.Linux.ReadonlyPaths {
		err := testWriteAccess(v)
		if err == nil {
			return fmt.Errorf("%v should be readonly", v)
		}
	}

	return nil
}

func mountMatch(specMount rspec.Mount, sysMount rspec.Mount) error {
	if specMount.Destination != sysMount.Destination {
		return fmt.Errorf("mount destination expected: %v, actual: %v", specMount.Destination, sysMount.Destination)
	}

	if specMount.Type != sysMount.Type {
		return fmt.Errorf("mount %v type expected: %v, actual: %v", specMount.Destination, specMount.Type, sysMount.Type)
	}

	if specMount.Source != sysMount.Source {
		return fmt.Errorf("mount %v source expected: %v, actual: %v", specMount.Destination, specMount.Source, sysMount.Source)
	}

	return nil
}

func validateMountsExist(spec *rspec.Spec) error {
	logrus.Debugf("validating mounts exist")

	mountInfos, err := mount.GetMounts()
	if err != nil {
		return err
	}

	mountsMap := make(map[string][]rspec.Mount)
	for _, mountInfo := range mountInfos {
		m := rspec.Mount{
			Destination: mountInfo.Mountpoint,
			Type:        mountInfo.Fstype,
			Source:      mountInfo.Source,
		}
		mountsMap[mountInfo.Mountpoint] = append(mountsMap[mountInfo.Mountpoint], m)
	}

	for _, specMount := range spec.Mounts {
		found := false
		for _, sysMount := range mountsMap[specMount.Destination] {
			if err := mountMatch(specMount, sysMount); err == nil {
				found = true
				break
			}
		}
		if !found {
			return fmt.Errorf("Expected mount %v does not exist", specMount)
		}
	}

	return nil
}

func validate(context *cli.Context) error {
	logLevelString := context.String("log-level")
	logLevel, err := logrus.ParseLevel(logLevelString)
	if err != nil {
		return err
	}
	logrus.SetLevel(logLevel)

	spec, err := loadSpecConfig()
	if err != nil {
		return err
	}

	defaultValidations := []validation{
		validateRootFS,
		validateProcess,
		validateCapabilities,
		validateHostname,
		validateRlimits,
		validateMountsExist,
	}

	linuxValidations := []validation{
		validateDefaultFS,
		validateSysctls,
		validateMaskedPaths,
		validateROPaths,
	}

	for _, v := range defaultValidations {
		if err := v(spec); err != nil {
			return err
		}
	}

	if spec.Platform.OS == "linux" {
		for _, v := range linuxValidations {
			if err := v(spec); err != nil {
				return err
			}
		}
	}

	return nil
}

func main() {
	app := cli.NewApp()
	app.Name = "runtimetest"
	app.Version = "0.0.1"
	app.Usage = "Compare the environment with an OCI configuration"
	app.UsageText = "runtimetest [options]"
	app.Flags = []cli.Flag{
		cli.StringFlag{
			Name:  "log-level",
			Value: "error",
			Usage: "Log level (panic, fatal, error, warn, info, or debug)",
		},
	}

	app.Action = validate
	app.Run(os.Args)
}
<commit_msg>validation: add linux default devices validation<commit_after>package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"path/filepath"
	"strings"
	"syscall"

	"github.com/Sirupsen/logrus"
	"github.com/opencontainers/ocitools/cmd/runtimetest/mount"
	rspec "github.com/opencontainers/runtime-spec/specs-go"
	"github.com/syndtr/gocapability/capability"
	"github.com/urfave/cli"
)

// PR_GET_NO_NEW_PRIVS isn't exposed in Golang so we define it ourselves copying the value from
// the kernel
const PR_GET_NO_NEW_PRIVS = 39

var (
	defaultFS = map[string]string{
		"/proc":    "proc",
		"/sys":     "sysfs",
		"/dev/pts": "devpts",
		"/dev/shm": "tmpfs",
	}

	defaultDevices = []string{
		"/dev/null",
		"/dev/zero",
		"/dev/full",
		"/dev/random",
		"/dev/urandom",
		"/dev/tty",
		"/dev/console",
		"/dev/ptmx",
	}
)

type validation func(*rspec.Spec) error

func loadSpecConfig() (spec *rspec.Spec, err error) {
	cPath := "config.json"
	cf, err := os.Open(cPath)
	if err != nil {
		if os.IsNotExist(err) {
			return nil, fmt.Errorf("config.json not found")
		}
	}
	defer cf.Close()

	if err = json.NewDecoder(cf).Decode(&spec); err != nil {
		return
	}
	return spec, nil
}

func validateProcess(spec *rspec.Spec) error {
	logrus.Debugf("validating container process")
	uid := os.Getuid()
	if uint32(uid) != spec.Process.User.UID {
		return fmt.Errorf("UID expected: %v, actual: %v", spec.Process.User.UID, uid)
	}
	gid := os.Getgid()
	if uint32(gid) != spec.Process.User.GID {
		return fmt.Errorf("GID expected: %v, actual: %v", spec.Process.User.GID, gid)
	}

	groups, err := os.Getgroups()
	if err != nil {
		return err
	}

	groupsMap := make(map[int]bool)
	for _, g := range groups {
		groupsMap[g] = true
	}

	for _, g := range spec.Process.User.AdditionalGids {
		if !groupsMap[int(g)] {
			return fmt.Errorf("Groups expected: %v, actual (should be superset): %v", spec.Process.User.AdditionalGids, groups)
		}
	}

	if spec.Process.Cwd != "" {
		cwd, err := os.Getwd()
		if err != nil {
			return err
		}
		if cwd != spec.Process.Cwd {
			return fmt.Errorf("Cwd expected: %v, actual: %v", spec.Process.Cwd, cwd)
		}
	}

	cmdlineBytes, err := ioutil.ReadFile("/proc/1/cmdline")
	if err != nil {
		return err
	}

	args := bytes.Split(bytes.Trim(cmdlineBytes, "\x00"), []byte("\x00"))
	if len(args) != len(spec.Process.Args) {
		return fmt.Errorf("Process arguments expected: %v, actual: %v", len(spec.Process.Args), len(args))
	}
	for i, a := range args {
		if string(a) != spec.Process.Args[i] {
			return fmt.Errorf("Process arguments expected: %v, actual: %v", string(a), spec.Process.Args[i])
		}
	}

	for _, env := range spec.Process.Env {
		parts := strings.Split(env, "=")
		key := parts[0]
		expectedValue := parts[1]
		actualValue := os.Getenv(key)
		if actualValue != expectedValue {
			return fmt.Errorf("Env %v expected: %v, actual: %v", key, expectedValue, actualValue)
		}
	}

	ret, _, errno := syscall.Syscall6(syscall.SYS_PRCTL, PR_GET_NO_NEW_PRIVS, 0, 0, 0, 0, 0)
	if errno != 0 {
		return errno
	}
	if spec.Process.NoNewPrivileges && ret != 1 {
		return fmt.Errorf("NoNewPrivileges expected: true, actual: false")
	}
	if !spec.Process.NoNewPrivileges && ret != 0 {
		return fmt.Errorf("NoNewPrivileges expected: false, actual: true")
	}

	return nil
}

func validateCapabilities(spec *rspec.Spec) error {
	logrus.Debugf("validating capabilities")

	last := capability.CAP_LAST_CAP
	// workaround for RHEL6 which has no /proc/sys/kernel/cap_last_cap
	if last == capability.Cap(63) {
		last = capability.CAP_BLOCK_SUSPEND
	}

	processCaps, err := capability.NewPid(1)
	if err != nil {
		return err
	}

	expectedCaps := make(map[string]bool)
	for _, ec := range spec.Process.Capabilities {
		expectedCaps[ec] = true
	}

	for _, cap := range capability.List() {
		if cap > last {
			continue
		}

		capKey := fmt.Sprintf("CAP_%s", strings.ToUpper(cap.String()))
		expectedSet := expectedCaps[capKey]
		actuallySet := processCaps.Get(capability.EFFECTIVE, cap)
		if expectedSet != actuallySet {
			if expectedSet {
				return fmt.Errorf("Expected Capability %v not set for process", cap.String())
			}
			return fmt.Errorf("Unexpected Capability %v set for process", cap.String())
		}
	}

	return nil
}

func validateHostname(spec *rspec.Spec) error {
	logrus.Debugf("validating hostname")
	hostname, err := os.Hostname()
	if err != nil {
		return err
	}
	if spec.Hostname != "" && hostname != spec.Hostname {
		return fmt.Errorf("Hostname expected: %v, actual: %v", spec.Hostname, hostname)
	}
	return nil
}

func validateRlimits(spec *rspec.Spec) error {
	logrus.Debugf("validating rlimits")
	for _, r := range spec.Process.Rlimits {
		rl, err := strToRlimit(r.Type)
		if err != nil {
			return err
		}

		var rlimit syscall.Rlimit
		if err := syscall.Getrlimit(rl, &rlimit); err != nil {
			return err
		}

		if rlimit.Cur != r.Soft {
			return fmt.Errorf("%v rlimit soft expected: %v, actual: %v", r.Type, r.Soft, rlimit.Cur)
		}
		if rlimit.Max != r.Hard {
			return fmt.Errorf("%v rlimit hard expected: %v, actual: %v", r.Type, r.Hard, rlimit.Max)
		}
	}
	return nil
}

func validateSysctls(spec *rspec.Spec) error {
	logrus.Debugf("validating sysctls")
	for k, v := range spec.Linux.Sysctl {
		keyPath := filepath.Join("/proc/sys", strings.Replace(k, ".", "/", -1))
		vBytes, err := ioutil.ReadFile(keyPath)
		if err != nil {
			return err
		}
		value := strings.TrimSpace(string(bytes.Trim(vBytes, "\x00")))
		if value != v {
			return fmt.Errorf("Sysctl %v value expected: %v, actual: %v", k, v, value)
		}
	}
	return nil
}

func testWriteAccess(path string) error {
	tmpfile, err := ioutil.TempFile(path, "Test")
	if err != nil {
		return err
	}

	tmpfile.Close()
	os.RemoveAll(filepath.Join(path, tmpfile.Name()))

	return nil
}

func validateRootFS(spec *rspec.Spec) error {
	logrus.Debugf("validating root filesystem")
	if spec.Root.Readonly {
		err := testWriteAccess("/")
		if err == nil {
			return fmt.Errorf("Rootfs should be readonly")
		}
	}

	return nil
}

func validateDefaultFS(spec *rspec.Spec) error {
	logrus.Debugf("validating linux default filesystem")

	mountInfos, err := mount.GetMounts()
	if err != nil {
		return err
	}

	mountsMap := make(map[string]string)
	for _, mountInfo := range mountInfos {
		mountsMap[mountInfo.Mountpoint] = mountInfo.Fstype
	}

	for fs, fstype := range defaultFS {
		if !(mountsMap[fs] == fstype) {
			return fmt.Errorf("%v must exist and expected type is %v", fs, fstype)
		}
	}

	return nil
}

func validateDefaultDevices(spec *rspec.Spec) error {
	logrus.Debugf("validating linux default devices")

	for _, device := range defaultDevices {
		fi, err := os.Stat(device)
		if err != nil {
			if os.IsNotExist(err) {
				return fmt.Errorf("device node %v not found", device)
			} else {
				return err
			}
		} else {
			if fi.Mode() != os.ModeDevice {
				return fmt.Errorf("file %v is not a device as expected", device)
			}
		}
	}

	return nil
}

func validateMaskedPaths(spec *rspec.Spec) error {
	logrus.Debugf("validating maskedPaths")
	for _, maskedPath := range spec.Linux.MaskedPaths {
		f, err := os.Open(maskedPath)
		if err != nil {
			return err
		}
		defer f.Close()
		b := make([]byte, 1)
		_, err = f.Read(b)
		if err != io.EOF {
			return fmt.Errorf("%v should not be readable", maskedPath)
		}
	}
	return nil
}

func validateROPaths(spec *rspec.Spec) error {
	logrus.Debugf("validating readonlyPaths")
	for _, v := range spec.Linux.ReadonlyPaths {
		err := testWriteAccess(v)
		if err == nil {
			return fmt.Errorf("%v should be readonly", v)
		}
	}

	return nil
}

func mountMatch(specMount rspec.Mount, sysMount rspec.Mount) error {
	if specMount.Destination != sysMount.Destination {
		return fmt.Errorf("mount destination expected: %v, actual: %v", specMount.Destination, sysMount.Destination)
	}

	if specMount.Type != sysMount.Type {
		return fmt.Errorf("mount %v type expected: %v, actual: %v", specMount.Destination, specMount.Type, sysMount.Type)
	}

	if specMount.Source != sysMount.Source {
		return fmt.Errorf("mount %v source expected: %v, actual: %v", specMount.Destination, specMount.Source, sysMount.Source)
	}

	return nil
}

func validateMountsExist(spec *rspec.Spec) error {
	logrus.Debugf("validating mounts exist")

	mountInfos, err := mount.GetMounts()
	if err != nil {
		return err
	}

	mountsMap := make(map[string][]rspec.Mount)
	for _, mountInfo := range mountInfos {
		m := rspec.Mount{
			Destination: mountInfo.Mountpoint,
			Type:        mountInfo.Fstype,
			Source:      mountInfo.Source,
		}
		mountsMap[mountInfo.Mountpoint] = append(mountsMap[mountInfo.Mountpoint], m)
	}

	for _, specMount := range spec.Mounts {
		found := false
		for _, sysMount := range mountsMap[specMount.Destination] {
			if err := mountMatch(specMount, sysMount); err == nil {
				found = true
				break
			}
		}
		if !found {
			return fmt.Errorf("Expected mount %v does not exist", specMount)
		}
	}

	return nil
}

func validate(context *cli.Context) error {
	logLevelString := context.String("log-level")
	logLevel, err := logrus.ParseLevel(logLevelString)
	if err != nil {
		return err
	}
	logrus.SetLevel(logLevel)

	spec, err := loadSpecConfig()
	if err != nil {
		return err
	}

	defaultValidations := []validation{
		validateRootFS,
		validateProcess,
		validateCapabilities,
		validateHostname,
		validateRlimits,
		validateMountsExist,
	}

	linuxValidations := []validation{
		validateDefaultFS,
		validateDefaultDevices,
		validateSysctls,
		validateMaskedPaths,
		validateROPaths,
	}

	for _, v := range defaultValidations {
		if err := v(spec); err != nil {
			return err
		}
	}

	if spec.Platform.OS == "linux" {
		for _, v := range linuxValidations {
			if err := v(spec); err != nil {
				return err
			}
		}
	}

	return nil
}

func main() {
	app := cli.NewApp()
	app.Name = "runtimetest"
	app.Version = "0.0.1"
	app.Usage = "Compare the environment with an OCI configuration"
	app.UsageText = "runtimetest [options]"
	app.Flags = []cli.Flag{
		cli.StringFlag{
			Name:  "log-level",
			Value: "error",
			Usage: "Log level (panic, fatal, error, warn, info, or debug)",
		},
	}

	app.Action = validate
	app.Run(os.Args)
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"os"
	"strings"

	"github.com/CTSRD-SOAAP/gosoaap"
)

type Analyses []string

func (a *Analyses) Set(value string) error {
	*a = strings.Split(value, ",")
	return nil
}

func (a Analyses) String() string {
	return strings.Join(a, ", ")
}

var (
	intersectionDepth = flag.Int("intersection-depth", 3,
		"how many calls to trace back from a leaf node when looking"+
			" for call graph intersections")
)

func main() {
	//
	// Command-line arguments:
	//
	legend := flag.Bool("legend", false,
		"emit a Dot legend rather than an actual graph")

	analyses := Analyses{"vuln"}
	flag.Var(&analyses, "analyses",
		"SOAAP analysis results to graph (options: "+
			strings.Join(soaap.GraphAnalyses(), ", ")+
			")")

	groupBy := flag.String("group-by", "", "group nodes by file, sandbox, etc.")

	output := flag.String("output", "-", "output file")

	binout := flag.Bool("binary", false, "write binary output")
	simplify := flag.Bool("simplify", false, "simplify callgraph")

	flag.Parse()

	var input string
	switch len(flag.Args()) {
	case 0:
		input = "-"

	case 1:
		input = flag.Args()[0]

	default:
		printUsage()
		return
	}

	//
	// Load input file:
	//
	var f *os.File
	var err error

	if input == "-" {
		f = os.Stdin
	} else {
		f, err = os.Open(input)
		if err != nil {
			fmt.Fprintf(os.Stderr, "error: %s\n", err)
			return
		}
	}

	//
	// Open output file:
	//
	var out *os.File
	if *output == "-" {
		out = os.Stdout
	} else {
		out, err = os.Create(*output)
		if err != nil {
			fmt.Fprintf(os.Stderr, "error: %s\n", err)
			return
		}
	}

	//
	// The call/dataflow graph to transform and output.
	//
	var graph soaap.CallGraph

	//
	// Special case: legend of possible node types.
	//
	if *legend {
		graph = soaap.Legend()

	} else if strings.HasSuffix(f.Name(), ".graph") {

		// Load binary graph file.
		graph, err = soaap.LoadGraph(f, report)

	} else {

		// Load SOAAP results.
		graph, err = analyzeResultsFile(f, analyses)
	}

	if err != nil {
		fmt.Fprintf(os.Stderr, "\nerror: %s\n", err)
		os.Exit(1)
	}

	nodes, edges, flows := graph.Size()
	fmt.Printf("Result: %d nodes, %d calls and %d flows\n",
		nodes, edges, flows)

	//
	// Apply any requested transformations:
	//
	if *simplify {
		graph = graph.Simplified()
		nodes, edges, flows = graph.Size()
		fmt.Printf("Simplified: %d nodes, %d calls and %d flows\n",
			nodes, edges, flows)
	}

	//
	// Output the results:
	//
	if *binout {
		err = graph.Save(out)
	} else {
		err = graph.WriteDot(out, *groupBy)
	}

	if err != nil {
		fmt.Fprintf(os.Stderr, "error writing output: %s\n", err)
		os.Exit(1)
	}
}

func analyzeResultsFile(f *os.File, analyses []string) (soaap.CallGraph, error) {
	//
	// Combine callgraphs of the requested analyses:
	//
	results, err := soaap.LoadResults(f, report)
	if err != nil {
		return soaap.CallGraph{}, err
	}

	fmt.Println("Initializing empty call graph")
	graph := soaap.NewCallGraph()

	for _, analysis := range analyses {
		graph, err = soaap.ApplyAnalysis(
			analysis, &graph, &results, *intersectionDepth, report)
	}

	return graph, err
}

func printUsage() {
	fmt.Fprintf(os.Stderr,
		"Usage:  soaap-graph [options] <input file>\n\n")

	fmt.Fprintf(os.Stderr, "Options:\n")
	flag.PrintDefaults()
}

func report(progress string) {
	fmt.Println(progress)
	os.Stdout.Sync()
}
<commit_msg>Report on starting to load potentially-big files.<commit_after>package main

import (
	"flag"
	"fmt"
	"os"
	"strings"

	"github.com/CTSRD-SOAAP/gosoaap"
)

type Analyses []string

func (a *Analyses) Set(value string) error {
	*a = strings.Split(value, ",")
	return nil
}

func (a Analyses) String() string {
	return strings.Join(a, ", ")
}

var (
	intersectionDepth = flag.Int("intersection-depth", 3,
		"how many calls to trace back from a leaf node when looking"+
			" for call graph intersections")
)

func main() {
	//
	// Command-line arguments:
	//
	legend := flag.Bool("legend", false,
		"emit a Dot legend rather than an actual graph")

	analyses := Analyses{"vuln"}
	flag.Var(&analyses, "analyses",
		"SOAAP analysis results to graph (options: "+
			strings.Join(soaap.GraphAnalyses(), ", ")+
			")")

	groupBy := flag.String("group-by", "", "group nodes by file, sandbox, etc.")

	output := flag.String("output", "-", "output file")

	binout := flag.Bool("binary", false, "write binary output")
	simplify := flag.Bool("simplify", false, "simplify callgraph")

	flag.Parse()

	var input string
	switch len(flag.Args()) {
	case 0:
		input = "-"

	case 1:
		input = flag.Args()[0]

	default:
		printUsage()
		return
	}

	//
	// Load input file:
	//
	var f *os.File
	var err error

	if input == "-" {
		f = os.Stdin
	} else {
		f, err = os.Open(input)
		if err != nil {
			fmt.Fprintf(os.Stderr, "error: %s\n", err)
			return
		}
	}

	//
	// Open output file:
	//
	var out *os.File
	if *output == "-" {
		out = os.Stdout
	} else {
		out, err = os.Create(*output)
		if err != nil {
			fmt.Fprintf(os.Stderr, "error: %s\n", err)
			return
		}
	}

	//
	// The call/dataflow graph to transform and output.
	//
	var graph soaap.CallGraph

	//
	// Special case: legend of possible node types.
	//
	if *legend {
		graph = soaap.Legend()

	} else if strings.HasSuffix(f.Name(), ".graph") {

		// Load binary graph file.
		report(fmt.Sprintf("Loading binary graph data from '%s'...", f.Name()))
		graph, err = soaap.LoadGraph(f, report)

	} else {

		// Load SOAAP results.
		report(fmt.Sprintf("Loading SOAAP results from '%s'...", f.Name()))
		graph, err = analyzeResultsFile(f, analyses)
	}

	if err != nil {
		fmt.Fprintf(os.Stderr, "\nerror: %s\n", err)
		os.Exit(1)
	}

	nodes, edges, flows := graph.Size()
	fmt.Printf("Result: %d nodes, %d calls and %d flows\n",
		nodes, edges, flows)

	//
	// Apply any requested transformations:
	//
	if *simplify {
		graph = graph.Simplified()
		nodes, edges, flows = graph.Size()
		fmt.Printf("Simplified: %d nodes, %d calls and %d flows\n",
			nodes, edges, flows)
	}

	//
	// Output the results:
	//
	if *binout {
		err = graph.Save(out)
	} else {
		err = graph.WriteDot(out, *groupBy)
	}

	if err != nil {
		fmt.Fprintf(os.Stderr, "error writing output: %s\n", err)
		os.Exit(1)
	}
}

func analyzeResultsFile(f *os.File, analyses []string) (soaap.CallGraph, error) {
	//
	// Combine callgraphs of the requested analyses:
	//
	results, err := soaap.LoadResults(f, report)
	if err != nil {
		return soaap.CallGraph{}, err
	}

	fmt.Println("Initializing empty call graph")
	graph := soaap.NewCallGraph()

	for _, analysis := range analyses {
		graph, err = soaap.ApplyAnalysis(
			analysis, &graph, &results, *intersectionDepth, report)
	}

	return graph, err
}

func printUsage() {
	fmt.Fprintf(os.Stderr,
		"Usage:  soaap-graph [options] <input file>\n\n")

	fmt.Fprintf(os.Stderr, "Options:\n")
	flag.PrintDefaults()
}

func report(progress string) {
	fmt.Println(progress)
	os.Stdout.Sync()
}
<|endoftext|>


<commit_before>package commands

import (
	"bytes"
	"fmt"
	"io"
	"os"
	"path/filepath"
	"strings"

	bstore "github.com/ipfs/go-ipfs/blocks/blockstore"
	cmds "github.com/ipfs/go-ipfs/commands"
	corerepo "github.com/ipfs/go-ipfs/core/corerepo"
	config "github.com/ipfs/go-ipfs/repo/config"
	fsrepo "github.com/ipfs/go-ipfs/repo/fsrepo"
	lockfile "github.com/ipfs/go-ipfs/repo/fsrepo/lock"

	cid "gx/ipfs/QmV5gPoRsjN1Gid3LMdNZTyfCtP2DsvqEbMAmz82RmmiGk/go-cid"
	u "gx/ipfs/QmZuY8aV7zbNXVy6DyN9SmnuH3o9nG852F4aTiSBpts8d1/go-ipfs-util"
)

type RepoVersion struct {
	Version string
}

var RepoCmd = &cmds.Command{
	Helptext: cmds.HelpText{
		Tagline: "Manipulate the IPFS repo.",
		ShortDescription: `
'ipfs repo' is a plumbing command used to manipulate the repo.
`,
	},

	Subcommands: map[string]*cmds.Command{
		"gc":      repoGcCmd,
		"stat":    repoStatCmd,
		"fsck":    RepoFsckCmd,
		"version": repoVersionCmd,
		"verify":  repoVerifyCmd,
	},
}

type GcResult struct {
	Key   *cid.Cid
	Error string `json:",omitempty"`
}

var repoGcCmd = &cmds.Command{
	Helptext: cmds.HelpText{
		Tagline: "Perform a garbage collection sweep on the repo.",
		ShortDescription: `
'ipfs repo gc' is a plumbing command that will sweep the local
set of stored objects and remove ones that are not pinned in
order to reclaim hard disk space.
`,
	},
	Options: []cmds.Option{
		cmds.BoolOption("quiet", "q", "Write minimal output.").Default(false),
		cmds.BoolOption("stream-errors", "Stream errors.").Default(false),
	},
	Run: func(req cmds.Request, res cmds.Response) {
		n, err := req.InvocContext().GetNode()
		if err != nil {
			res.SetError(err, cmds.ErrNormal)
			return
		}

		streamErrors, _, _ := res.Request().Option("stream-errors").Bool()

		gcOutChan := corerepo.GarbageCollectAsync(n, req.Context())

		outChan := make(chan interface{}, cap(gcOutChan))
		res.SetOutput((<-chan interface{})(outChan))

		go func() {
			defer close(outChan)
			unreportedError := false
			var lastErr error
			if streamErrors {
				for res := range gcOutChan {
					if unreportedError {
						outChan <- &GcResult{Error: lastErr.Error()}
						unreportedError = false
					}
					if res.Error != nil {
						lastErr = res.Error
						unreportedError = true
					} else {
						outChan <- &GcResult{Key: res.KeyRemoved}
					}
				}
			} else {
				lastErr = corerepo.CollectResult(req.Context(), gcOutChan, func(k *cid.Cid) {
					outChan <- &GcResult{Key: k}
				})
			}
			if lastErr != nil {
				res.SetError(lastErr, cmds.ErrNormal)
			}
		}()
	},
	Type: GcResult{},
	Marshalers: cmds.MarshalerMap{
		cmds.Text: func(res cmds.Response) (io.Reader, error) {
			outChan, ok := res.Output().(<-chan interface{})
			if !ok {
				return nil, u.ErrCast()
			}

			quiet, _, err := res.Request().Option("quiet").Bool()
			if err != nil {
				return nil, err
			}

			for v := range outChan {
				obj, ok := v.(*GcResult)
				if !ok {
					return nil, u.ErrCast()
				}

				if obj.Error != "" {
					fmt.Fprintf(res.Stderr(), "Error: %s\n", obj.Error)
					continue
				}

				if quiet {
					fmt.Fprintf(res.Stdout(), "%s\n", obj.Key.String())
				} else {
					fmt.Fprintf(res.Stdout(), "removed %s\n", obj.Key.String())
				}
			}

			if res.Error() != nil {
				return nil, res.Error()
			}

			return nil, nil
		},
	},
}

var repoStatCmd = &cmds.Command{
	Helptext: cmds.HelpText{
		Tagline: "Get stats for the currently used repo.",
		ShortDescription: `
'ipfs repo stat' is a plumbing command that will scan the local
set of stored objects and print repo statistics. It outputs to stdout:
NumObjects      int Number of objects in the local repo.
RepoPath        string The path to the repo being currently used.
RepoSize        int Size in bytes that the repo is currently taking.
Version         string The repo version.
`,
	},
	Run: func(req cmds.Request, res cmds.Response) {
		n, err := req.InvocContext().GetNode()
		if err != nil {
			res.SetError(err, cmds.ErrNormal)
			return
		}

		stat, err := corerepo.RepoStat(n, req.Context())
		if err != nil {
			res.SetError(err, cmds.ErrNormal)
			return
		}

		res.SetOutput(stat)
	},
	Options: []cmds.Option{
		cmds.BoolOption("human", "Output RepoSize in MiB.").Default(false),
	},
	Type: corerepo.Stat{},
	Marshalers: cmds.MarshalerMap{
		cmds.Text: func(res cmds.Response) (io.Reader, error) {
			stat, ok := res.Output().(*corerepo.Stat)
			if !ok {
				return nil, u.ErrCast()
			}

			human, _, err := res.Request().Option("human").Bool()
			if err != nil {
				return nil, err
			}

			buf := new(bytes.Buffer)
			fmt.Fprintf(buf, "NumObjects \t %d\n", stat.NumObjects)
			sizeInMiB := stat.RepoSize / (1024 * 1024)
			if human && sizeInMiB > 0 {
				fmt.Fprintf(buf, "RepoSize (MiB) \t %d\n", sizeInMiB)
			} else {
				fmt.Fprintf(buf, "RepoSize \t %d\n", stat.RepoSize)
			}
			fmt.Fprintf(buf, "RepoPath \t %s\n", stat.RepoPath)
			fmt.Fprintf(buf, "Version \t %s\n", stat.Version)

			return buf, nil
		},
	},
}

var RepoFsckCmd = &cmds.Command{
	Helptext: cmds.HelpText{
		Tagline: "Remove repo lockfiles.",
		ShortDescription: `
'ipfs repo fsck' is a plumbing command that will remove repo and level db
lockfiles, as well as the api file. This command can only run when no ipfs
daemons are running.
`,
	},
	Run: func(req cmds.Request, res cmds.Response) {
		configRoot := req.InvocContext().ConfigRoot

		dsPath, err := config.DataStorePath(configRoot)
		if err != nil {
			res.SetError(err, cmds.ErrNormal)
			return
		}

		dsLockFile := filepath.Join(dsPath, "LOCK") // TODO: get this lockfile programmatically
		repoLockFile := filepath.Join(configRoot, lockfile.LockFile)
		apiFile := filepath.Join(configRoot, "api") // TODO: get this programmatically

		log.Infof("Removing repo lockfile: %s", repoLockFile)
		log.Infof("Removing datastore lockfile: %s", dsLockFile)
		log.Infof("Removing api file: %s", apiFile)

		err = os.Remove(repoLockFile)
		if err != nil && !os.IsNotExist(err) {
			res.SetError(err, cmds.ErrNormal)
			return
		}
		err = os.Remove(dsLockFile)
		if err != nil && !os.IsNotExist(err) {
			res.SetError(err, cmds.ErrNormal)
			return
		}
		err = os.Remove(apiFile)
		if err != nil && !os.IsNotExist(err) {
			res.SetError(err, cmds.ErrNormal)
			return
		}

		res.SetOutput(&MessageOutput{"Lockfiles have been removed.\n"})
	},
	Type: MessageOutput{},
	Marshalers: cmds.MarshalerMap{
		cmds.Text: MessageTextMarshaler,
	},
}

type VerifyProgress struct {
	Message  string
	Progress int
}

var repoVerifyCmd = &cmds.Command{
	Helptext: cmds.HelpText{
		Tagline: "Verify all blocks in repo are not corrupted.",
	},
	Run: func(req cmds.Request, res cmds.Response) {
		nd, err := req.InvocContext().GetNode()
		if err != nil {
			res.SetError(err, cmds.ErrNormal)
			return
		}

		out := make(chan interface{})
		go func() {
			defer close(out)
			bs := bstore.NewBlockstore(nd.Repo.Datastore())

			bs.HashOnRead(true)

			keys, err := bs.AllKeysChan(req.Context())
			if err != nil {
				log.Error(err)
				return
			}

			var fails int
			var i int
			for k := range keys {
				_, err := bs.Get(k)
				if err != nil {
					out <- &VerifyProgress{
						Message: fmt.Sprintf("block %s was corrupt (%s)", k, err),
					}
					fails++
				}
				i++
				out <- &VerifyProgress{Progress: i}
			}
			if fails == 0 {
				out <- &VerifyProgress{Message: "verify complete, all blocks validated."}
			} else {
				out <- &VerifyProgress{Message: "verify complete, some blocks were corrupt."}
			}
		}()

		res.SetOutput((<-chan interface{})(out))
	},
	Type: VerifyProgress{},
	Marshalers: cmds.MarshalerMap{
		cmds.Text: func(res cmds.Response) (io.Reader, error) {
			out := res.Output().(<-chan interface{})

			marshal := func(v interface{}) (io.Reader, error) {
				obj, ok := v.(*VerifyProgress)
				if !ok {
					return nil, u.ErrCast()
				}

				buf := new(bytes.Buffer)
				if obj.Message != "" {
					if strings.Contains(obj.Message, "blocks were corrupt") {
						return nil, fmt.Errorf(obj.Message)
					}
					if len(obj.Message) < 20 {
						obj.Message += "             "
					}
					fmt.Fprintln(buf, obj.Message)
					return buf, nil
				}

				fmt.Fprintf(buf, "%d blocks processed.\r", obj.Progress)
				return buf, nil
			}

			return &cmds.ChannelMarshaler{
				Channel:   out,
				Marshaler: marshal,
				Res:       res,
			}, nil
		},
	},
}

var repoVersionCmd = &cmds.Command{
	Helptext: cmds.HelpText{
		Tagline: "Show the repo version.",
		ShortDescription: `
'ipfs repo version' returns the current repo version.
`,
	},

	Options: []cmds.Option{
		cmds.BoolOption("quiet", "q", "Write minimal output."),
	},
	Run: func(req cmds.Request, res cmds.Response) {
		res.SetOutput(&RepoVersion{
			Version: fmt.Sprint(fsrepo.RepoVersion),
		})
	},
	Type: RepoVersion{},
	Marshalers: cmds.MarshalerMap{
		cmds.Text: func(res cmds.Response) (io.Reader, error) {
			response := res.Output().(*RepoVersion)

			quiet, _, err := res.Request().Option("quiet").Bool()
			if err != nil {
				return nil, err
			}

			buf := new(bytes.Buffer)
			if quiet {
				buf = bytes.NewBufferString(fmt.Sprintf("fs-repo@%s\n", response.Version))
			} else {
				buf = bytes.NewBufferString(fmt.Sprintf("ipfs repo version fs-repo@%s\n", response.Version))
			}
			return buf, nil

		},
	},
}
<commit_msg>gc: stream all errors including the last<commit_after>package commands

import (
	"bytes"
	"fmt"
	"io"
	"os"
	"path/filepath"
	"strings"

	bstore "github.com/ipfs/go-ipfs/blocks/blockstore"
	cmds "github.com/ipfs/go-ipfs/commands"
	corerepo "github.com/ipfs/go-ipfs/core/corerepo"
	config "github.com/ipfs/go-ipfs/repo/config"
	fsrepo "github.com/ipfs/go-ipfs/repo/fsrepo"
	lockfile "github.com/ipfs/go-ipfs/repo/fsrepo/lock"

	cid "gx/ipfs/QmV5gPoRsjN1Gid3LMdNZTyfCtP2DsvqEbMAmz82RmmiGk/go-cid"
	u "gx/ipfs/QmZuY8aV7zbNXVy6DyN9SmnuH3o9nG852F4aTiSBpts8d1/go-ipfs-util"
)

type RepoVersion struct {
	Version string
}

var RepoCmd = &cmds.Command{
	Helptext: cmds.HelpText{
		Tagline: "Manipulate the IPFS repo.",
		ShortDescription: `
'ipfs repo' is a plumbing command used to manipulate the repo.
`,
	},

	Subcommands: map[string]*cmds.Command{
		"gc":      repoGcCmd,
		"stat":    repoStatCmd,
		"fsck":    RepoFsckCmd,
		"version": repoVersionCmd,
		"verify":  repoVerifyCmd,
	},
}

type GcResult struct {
	Key   *cid.Cid
	Error string `json:",omitempty"`
}

var repoGcCmd = &cmds.Command{
	Helptext: cmds.HelpText{
		Tagline: "Perform a garbage collection sweep on the repo.",
		ShortDescription: `
'ipfs repo gc' is a plumbing command that will sweep the local
set of stored objects and remove ones that are not pinned in
order to reclaim hard disk space.
`,
	},
	Options: []cmds.Option{
		cmds.BoolOption("quiet", "q", "Write minimal output.").Default(false),
		cmds.BoolOption("stream-errors", "Stream errors.").Default(false),
	},
	Run: func(req cmds.Request, res cmds.Response) {
		n, err := req.InvocContext().GetNode()
		if err != nil {
			res.SetError(err, cmds.ErrNormal)
			return
		}

		streamErrors, _, _ := res.Request().Option("stream-errors").Bool()

		gcOutChan := corerepo.GarbageCollectAsync(n, req.Context())

		outChan := make(chan interface{}, cap(gcOutChan))
		res.SetOutput((<-chan interface{})(outChan))

		go func() {
			defer close(outChan)
			if streamErrors {
				errs := false
				for res := range gcOutChan {
					if res.Error != nil {
						outChan <- &GcResult{Error: res.Error.Error()}
						errs = true
					} else {
						outChan <- &GcResult{Key: res.KeyRemoved}
					}
				}
				if errs {
					res.SetError(fmt.Errorf("encountered errors during gc run"), cmds.ErrNormal)
				}
			} else {
				err := corerepo.CollectResult(req.Context(), gcOutChan, func(k *cid.Cid) {
					outChan <- &GcResult{Key: k}
				})
				if err != nil {
					res.SetError(err, cmds.ErrNormal)
				}
			}
		}()
	},
	Type: GcResult{},
	Marshalers: cmds.MarshalerMap{
		cmds.Text: func(res cmds.Response) (io.Reader, error) {
			outChan, ok := res.Output().(<-chan interface{})
			if !ok {
				return nil, u.ErrCast()
			}

			quiet, _, err := res.Request().Option("quiet").Bool()
			if err != nil {
				return nil, err
			}

			for v := range outChan {
				obj, ok := v.(*GcResult)
				if !ok {
					return nil, u.ErrCast()
				}

				if obj.Error != "" {
					fmt.Fprintf(res.Stderr(), "Error: %s\n", obj.Error)
					continue
				}

				if quiet {
					fmt.Fprintf(res.Stdout(), "%s\n", obj.Key.String())
				} else {
					fmt.Fprintf(res.Stdout(), "removed %s\n", obj.Key.String())
				}
			}

			if res.Error() != nil {
				return nil, res.Error()
			}

			return nil, nil
		},
	},
}

var repoStatCmd = &cmds.Command{
	Helptext: cmds.HelpText{
		Tagline: "Get stats for the currently used repo.",
		ShortDescription: `
'ipfs repo stat' is a plumbing command that will scan the local
set of stored objects and print repo statistics. It outputs to stdout:
NumObjects      int Number of objects in the local repo.
RepoPath        string The path to the repo being currently used.
RepoSize        int Size in bytes that the repo is currently taking.
Version         string The repo version.
`,
	},
	Run: func(req cmds.Request, res cmds.Response) {
		n, err := req.InvocContext().GetNode()
		if err != nil {
			res.SetError(err, cmds.ErrNormal)
			return
		}

		stat, err := corerepo.RepoStat(n, req.Context())
		if err != nil {
			res.SetError(err, cmds.ErrNormal)
			return
		}

		res.SetOutput(stat)
	},
	Options: []cmds.Option{
		cmds.BoolOption("human", "Output RepoSize in MiB.").Default(false),
	},
	Type: corerepo.Stat{},
	Marshalers: cmds.MarshalerMap{
		cmds.Text: func(res cmds.Response) (io.Reader, error) {
			stat, ok := res.Output().(*corerepo.Stat)
			if !ok {
				return nil, u.ErrCast()
			}

			human, _, err := res.Request().Option("human").Bool()
			if err != nil {
				return nil, err
			}

			buf := new(bytes.Buffer)
			fmt.Fprintf(buf, "NumObjects \t %d\n", stat.NumObjects)
			sizeInMiB := stat.RepoSize / (1024 * 1024)
			if human && sizeInMiB > 0 {
				fmt.Fprintf(buf, "RepoSize (MiB) \t %d\n", sizeInMiB)
			} else {
				fmt.Fprintf(buf, "RepoSize \t %d\n", stat.RepoSize)
			}
			fmt.Fprintf(buf, "RepoPath \t %s\n", stat.RepoPath)
			fmt.Fprintf(buf, "Version \t %s\n", stat.Version)

			return buf, nil
		},
	},
}

var RepoFsckCmd = &cmds.Command{
	Helptext: cmds.HelpText{
		Tagline: "Remove repo lockfiles.",
		ShortDescription: `
'ipfs repo fsck' is a plumbing command that will remove repo and level db
lockfiles, as well as the api file. This command can only run when no ipfs
daemons are running.
`,
	},
	Run: func(req cmds.Request, res cmds.Response) {
		configRoot := req.InvocContext().ConfigRoot

		dsPath, err := config.DataStorePath(configRoot)
		if err != nil {
			res.SetError(err, cmds.ErrNormal)
			return
		}

		dsLockFile := filepath.Join(dsPath, "LOCK") // TODO: get this lockfile programmatically
		repoLockFile := filepath.Join(configRoot, lockfile.LockFile)
		apiFile := filepath.Join(configRoot, "api") // TODO: get this programmatically

		log.Infof("Removing repo lockfile: %s", repoLockFile)
		log.Infof("Removing datastore lockfile: %s", dsLockFile)
		log.Infof("Removing api file: %s", apiFile)

		err = os.Remove(repoLockFile)
		if err != nil && !os.IsNotExist(err) {
			res.SetError(err, cmds.ErrNormal)
			return
		}
		err = os.Remove(dsLockFile)
		if err != nil && !os.IsNotExist(err) {
			res.SetError(err, cmds.ErrNormal)
			return
		}
		err = os.Remove(apiFile)
		if err != nil && !os.IsNotExist(err) {
			res.SetError(err, cmds.ErrNormal)
			return
		}

		res.SetOutput(&MessageOutput{"Lockfiles have been removed.\n"})
	},
	Type: MessageOutput{},
	Marshalers: cmds.MarshalerMap{
		cmds.Text: MessageTextMarshaler,
	},
}

type VerifyProgress struct {
	Message  string
	Progress int
}

var repoVerifyCmd = &cmds.Command{
	Helptext: cmds.HelpText{
		Tagline: "Verify all blocks in repo are not corrupted.",
	},
	Run: func(req cmds.Request, res cmds.Response) {
		nd, err := req.InvocContext().GetNode()
		if err != nil {
			res.SetError(err, cmds.ErrNormal)
			return
		}

		out := make(chan interface{})
		go func() {
			defer close(out)
			bs := bstore.NewBlockstore(nd.Repo.Datastore())

			bs.HashOnRead(true)

			keys, err := bs.AllKeysChan(req.Context())
			if err != nil {
				log.Error(err)
				return
			}

			var fails int
			var i int
			for k := range keys {
				_, err := bs.Get(k)
				if err != nil {
					out <- &VerifyProgress{
						Message: fmt.Sprintf("block %s was corrupt (%s)", k, err),
					}
					fails++
				}
				i++
				out <- &VerifyProgress{Progress: i}
			}
			if fails == 0 {
				out <- &VerifyProgress{Message: "verify complete, all blocks validated."}
			} else {
				out <- &VerifyProgress{Message: "verify complete, some blocks were corrupt."}
			}
		}()

		res.SetOutput((<-chan interface{})(out))
	},
	Type: VerifyProgress{},
	Marshalers: cmds.MarshalerMap{
		cmds.Text: func(res cmds.Response) (io.Reader, error) {
			out := res.Output().(<-chan interface{})

			marshal := func(v interface{}) (io.Reader, error) {
				obj, ok := v.(*VerifyProgress)
				if !ok {
					return nil, u.ErrCast()
				}

				buf := new(bytes.Buffer)
				if obj.Message != "" {
					if strings.Contains(obj.Message, "blocks were corrupt") {
						return nil, fmt.Errorf(obj.Message)
					}
					if len(obj.Message) < 20 {
						obj.Message += "             "
					}
					fmt.Fprintln(buf, obj.Message)
					return buf, nil
				}

				fmt.Fprintf(buf, "%d blocks processed.\r", obj.Progress)
				return buf, nil
			}

			return &cmds.ChannelMarshaler{
				Channel:   out,
				Marshaler: marshal,
				Res:       res,
			}, nil
		},
	},
}

var repoVersionCmd = &cmds.Command{
	Helptext: cmds.HelpText{
		Tagline: "Show the repo version.",
		ShortDescription: `
'ipfs repo version' returns the current repo version.
`,
	},

	Options: []cmds.Option{
		cmds.BoolOption("quiet", "q", "Write minimal output."),
	},
	Run: func(req cmds.Request, res cmds.Response) {
		res.SetOutput(&RepoVersion{
			Version: fmt.Sprint(fsrepo.RepoVersion),
		})
	},
	Type: RepoVersion{},
	Marshalers: cmds.MarshalerMap{
		cmds.Text: func(res cmds.Response) (io.Reader, error) {
			response := res.Output().(*RepoVersion)

			quiet, _, err := res.Request().Option("quiet").Bool()
			if err != nil {
				return nil, err
			}

			buf := new(bytes.Buffer)
			if quiet {
				buf = bytes.NewBufferString(fmt.Sprintf("fs-repo@%s\n", response.Version))
			} else {
				buf = bytes.NewBufferString(fmt.Sprintf("ipfs repo version fs-repo@%s\n", response.Version))
			}
			return buf, nil

		},
	},
}
<|endoftext|>


<commit_before>//
// DISCLAIMER
//
// Copyright 2017 ArangoDB GmbH, Cologne, Germany
//
// 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.
//
// Copyright holder is ArangoDB GmbH, Cologne, Germany
//
// Author Ewout Prangsma
//

package driver

import "context"

// CollectionDocuments provides access to the documents in a single collection.
type CollectionDocuments interface {
	// DocumentExists checks if a document with given key exists in the collection.
	DocumentExists(ctx context.Context, key string) (bool, error)

	// ReadDocument reads a single document with given key from the collection.
	// The document data is stored into result, the document meta data is returned.
	// If no document exists with given key, a NotFoundError is returned.
	ReadDocument(ctx context.Context, key string, result interface{}) (DocumentMeta, error)

	// ReadDocuments reads multiple documents with given keys from the collection.
	// The documents data is stored into elements of the given results slice,
	// the documents meta data is returned.
	// If no document exists with a given key, a NotFoundError is returned at its errors index.
	ReadDocuments(ctx context.Context, keys []string, results interface{}) (DocumentMetaSlice, ErrorSlice, error)

	// CreateDocument creates a single document in the collection.
	// The document data is loaded from the given document, the document meta data is returned.
	// If the document data already contains a `_key` field, this will be used as key of the new document,
	// otherwise a unique key is created.
	// A ConflictError is returned when a `_key` field contains a duplicate key, other any other field violates an index constraint.
	// To return the NEW document, prepare a context with `WithReturnNew`.
	// To wait until document has been synced to disk, prepare a context with `WithWaitForSync`.
	CreateDocument(ctx context.Context, document interface{}) (DocumentMeta, error)

	// CreateDocuments creates multiple documents in the collection.
	// The document data is loaded from the given documents slice, the documents meta data is returned.
	// If a documents element already contains a `_key` field, this will be used as key of the new document,
	// otherwise a unique key is created.
	// If a documents element contains a `_key` field with a duplicate key, other any other field violates an index constraint,
	// a ConflictError is returned in its inded in the errors slice.
	// To return the NEW documents, prepare a context with `WithReturnNew`. The data argument passed to `WithReturnNew` must be
	// a slice with the same number of entries as the `documents` slice.
	// To wait until document has been synced to disk, prepare a context with `WithWaitForSync`.
	// If the create request itself fails or one of the arguments is invalid, an error is returned.
	CreateDocuments(ctx context.Context, documents interface{}) (DocumentMetaSlice, ErrorSlice, error)

	// UpdateDocument updates a single document with given key in the collection.
	// The document meta data is returned.
	// To return the NEW document, prepare a context with `WithReturnNew`.
	// To return the OLD document, prepare a context with `WithReturnOld`.
	// To wait until document has been synced to disk, prepare a context with `WithWaitForSync`.
	// If no document exists with given key, a NotFoundError is returned.
	UpdateDocument(ctx context.Context, key string, update interface{}) (DocumentMeta, error)

	// UpdateDocuments updates multiple document with given keys in the collection.
	// The updates are loaded from the given updates slice, the documents meta data are returned.
	// To return the NEW documents, prepare a context with `WithReturnNew` with a slice of documents.
	// To return the OLD documents, prepare a context with `WithReturnOld` with a slice of documents.
	// To wait until documents has been synced to disk, prepare a context with `WithWaitForSync`.
	// If no document exists with a given key, a NotFoundError is returned at its errors index.
	// If keys is nil, each element in the updates slice must contain a `_key` field.
	UpdateDocuments(ctx context.Context, keys []string, updates interface{}) (DocumentMetaSlice, ErrorSlice, error)

	// ReplaceDocument replaces a single document with given key in the collection with the document given in the document argument.
	// The document meta data is returned.
	// To return the NEW document, prepare a context with `WithReturnNew`.
	// To return the OLD document, prepare a context with `WithReturnOld`.
	// To wait until document has been synced to disk, prepare a context with `WithWaitForSync`.
	// If no document exists with given key, a NotFoundError is returned.
	ReplaceDocument(ctx context.Context, key string, document interface{}) (DocumentMeta, error)

	// ReplaceDocuments replaces multiple documents with given keys in the collection with the documents given in the documents argument.
	// The replacements are loaded from the given documents slice, the documents meta data are returned.
	// To return the NEW documents, prepare a context with `WithReturnNew` with a slice of documents.
	// To return the OLD documents, prepare a context with `WithReturnOld` with a slice of documents.
	// To wait until documents has been synced to disk, prepare a context with `WithWaitForSync`.
	// If no document exists with a given key, a NotFoundError is returned at its errors index.
	// If keys is nil, each element in the documents slice must contain a `_key` field.
	ReplaceDocuments(ctx context.Context, keys []string, documents interface{}) (DocumentMetaSlice, ErrorSlice, error)

	// RemoveDocument removes a single document with given key from the collection.
	// The document meta data is returned.
	// To return the OLD document, prepare a context with `WithReturnOld`.
	// To wait until removal has been synced to disk, prepare a context with `WithWaitForSync`.
	// If no document exists with given key, a NotFoundError is returned.
	RemoveDocument(ctx context.Context, key string) (DocumentMeta, error)

	// RemoveDocuments removes multiple documents with given keys from the collection.
	// The document meta data are returned.
	// To return the OLD documents, prepare a context with `WithReturnOld` with a slice of documents.
	// To wait until removal has been synced to disk, prepare a context with `WithWaitForSync`.
	// If no document exists with a given key, a NotFoundError is returned at its errors index.
	RemoveDocuments(ctx context.Context, keys []string) (DocumentMetaSlice, ErrorSlice, error)

	// ImportDocuments imports one or more documents into the collection.
	// The document data is loaded from the given documents argument, statistics are returned.
	// The documents argument can be one of the following:
	// - An array of structs: All structs will be imported as individual documents.
	// - An array of maps: All maps will be imported as individual documents.
	// To wait until all documents have been synced to disk, prepare a context with `WithWaitForSync`.
	// To return details about documents that could not be imported, prepare a context with `WithImportDetails`.
	ImportDocuments(ctx context.Context, documents interface{}, options *ImportDocumentOptions) (ImportDocumentStatistics, error)
}

// ImportDocumentOptions holds optional options that control the import document process.
type ImportDocumentOptions struct {
	// FromPrefix is an optional prefix for the values in _from attributes. If specified, the value is automatically
	// prepended to each _from input value. This allows specifying just the keys for _from.
	FromPrefix string `json:"fromPrefix,omitempty"`
	// ToPrefix is an optional prefix for the values in _to attributes. If specified, the value is automatically
	// prepended to each _to input value. This allows specifying just the keys for _to.
	ToPrefix string `json:"toPrefix,omitempty"`
	// Overwrite is a flag that if set, then all data in the collection will be removed prior to the import.
	// Note that any existing index definitions will be preseved.
	Overwrite bool `json:"overwrite,omitempty"`
	// OnDuplicate controls what action is carried out in case of a unique key constraint violation.
	// Possible values are:
	// - ImportOnDuplicateError
	// - ImportOnDuplicateUpdate
	// - ImportOnDuplicateReplace
	// - ImportOnDuplicateIgnore
	OnDuplicate ImportOnDuplicate `json:"onDuplicate,omitempty"`
	// Complete is a flag that if set, will make the whole import fail if any error occurs.
	// Otherwise the import will continue even if some documents cannot be imported.
	Complete bool `json:"complete,omitempty"`
}

// ImportOnDuplicate is a type to control what action is carried out in case of a unique key constraint violation.
type ImportOnDuplicate string

const (
	// ImportOnDuplicateError will not import the current document because of the unique key constraint violation.
	// This is the default setting.
	ImportOnDuplicateError = ImportOnDuplicate("error")
	// ImportOnDuplicateUpdate will update an existing document in the database with the data specified in the request.
	// Attributes of the existing document that are not present in the request will be preseved.
	ImportOnDuplicateUpdate = ImportOnDuplicate("update")
	// ImportOnDuplicateReplace will replace an existing document in the database with the data specified in the request.
	ImportOnDuplicateReplace = ImportOnDuplicate("replace")
	// ImportOnDuplicateIgnore will not update an existing document and simply ignore the error caused by a unique key constraint violation.
	ImportOnDuplicateIgnore = ImportOnDuplicate("ignore")
)

// ImportDocumentStatistics holds statistics of an import action.
type ImportDocumentStatistics struct {
	// Created holds the number of documents imported.
	Created int64 `json:"created,omitempty"`
	// Errors holds the number of documents that were not imported due to an error.
	Errors int64 `json:"errors,omitempty"`
	// Empty holds the number of empty lines found in the input (will only contain a value greater zero for types documents or auto).
	Empty int64 `json:"empty,omitempty"`
	// Updated holds the number of updated/replaced documents (in case onDuplicate was set to either update or replace).
	Updated int64 `json:"updated,omitempty"`
	// Ignored holds the number of failed but ignored insert operations (in case onDuplicate was set to ignore).
	Ignored int64 `json:"ignored,omitempty"`
	// if query parameter details is set to true, the result will contain a details attribute which is an array
	// with more detailed information about which documents could not be inserted.
	Details []string

	ArangoError
}
<commit_msg>fix the typo (#307)<commit_after>//
// DISCLAIMER
//
// Copyright 2017 ArangoDB GmbH, Cologne, Germany
//
// 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.
//
// Copyright holder is ArangoDB GmbH, Cologne, Germany
//
// Author Ewout Prangsma
//

package driver

import "context"

// CollectionDocuments provides access to the documents in a single collection.
type CollectionDocuments interface {
	// DocumentExists checks if a document with given key exists in the collection.
	DocumentExists(ctx context.Context, key string) (bool, error)

	// ReadDocument reads a single document with given key from the collection.
	// The document data is stored into result, the document meta data is returned.
	// If no document exists with given key, a NotFoundError is returned.
	ReadDocument(ctx context.Context, key string, result interface{}) (DocumentMeta, error)

	// ReadDocuments reads multiple documents with given keys from the collection.
	// The documents data is stored into elements of the given results slice,
	// the documents meta data is returned.
	// If no document exists with a given key, a NotFoundError is returned at its errors index.
	ReadDocuments(ctx context.Context, keys []string, results interface{}) (DocumentMetaSlice, ErrorSlice, error)

	// CreateDocument creates a single document in the collection.
	// The document data is loaded from the given document, the document meta data is returned.
	// If the document data already contains a `_key` field, this will be used as key of the new document,
	// otherwise a unique key is created.
	// A ConflictError is returned when a `_key` field contains a duplicate key, other any other field violates an index constraint.
	// To return the NEW document, prepare a context with `WithReturnNew`.
	// To wait until document has been synced to disk, prepare a context with `WithWaitForSync`.
	CreateDocument(ctx context.Context, document interface{}) (DocumentMeta, error)

	// CreateDocuments creates multiple documents in the collection.
	// The document data is loaded from the given documents slice, the documents meta data is returned.
	// If a documents element already contains a `_key` field, this will be used as key of the new document,
	// otherwise a unique key is created.
	// If a documents element contains a `_key` field with a duplicate key, other any other field violates an index constraint,
	// a ConflictError is returned in its index in the errors slice.
	// To return the NEW documents, prepare a context with `WithReturnNew`. The data argument passed to `WithReturnNew` must be
	// a slice with the same number of entries as the `documents` slice.
	// To wait until document has been synced to disk, prepare a context with `WithWaitForSync`.
	// If the create request itself fails or one of the arguments is invalid, an error is returned.
	CreateDocuments(ctx context.Context, documents interface{}) (DocumentMetaSlice, ErrorSlice, error)

	// UpdateDocument updates a single document with given key in the collection.
	// The document meta data is returned.
	// To return the NEW document, prepare a context with `WithReturnNew`.
	// To return the OLD document, prepare a context with `WithReturnOld`.
	// To wait until document has been synced to disk, prepare a context with `WithWaitForSync`.
	// If no document exists with given key, a NotFoundError is returned.
	UpdateDocument(ctx context.Context, key string, update interface{}) (DocumentMeta, error)

	// UpdateDocuments updates multiple document with given keys in the collection.
	// The updates are loaded from the given updates slice, the documents meta data are returned.
	// To return the NEW documents, prepare a context with `WithReturnNew` with a slice of documents.
	// To return the OLD documents, prepare a context with `WithReturnOld` with a slice of documents.
	// To wait until documents has been synced to disk, prepare a context with `WithWaitForSync`.
	// If no document exists with a given key, a NotFoundError is returned at its errors index.
	// If keys is nil, each element in the updates slice must contain a `_key` field.
	UpdateDocuments(ctx context.Context, keys []string, updates interface{}) (DocumentMetaSlice, ErrorSlice, error)

	// ReplaceDocument replaces a single document with given key in the collection with the document given in the document argument.
	// The document meta data is returned.
	// To return the NEW document, prepare a context with `WithReturnNew`.
	// To return the OLD document, prepare a context with `WithReturnOld`.
	// To wait until document has been synced to disk, prepare a context with `WithWaitForSync`.
	// If no document exists with given key, a NotFoundError is returned.
	ReplaceDocument(ctx context.Context, key string, document interface{}) (DocumentMeta, error)

	// ReplaceDocuments replaces multiple documents with given keys in the collection with the documents given in the documents argument.
	// The replacements are loaded from the given documents slice, the documents meta data are returned.
	// To return the NEW documents, prepare a context with `WithReturnNew` with a slice of documents.
	// To return the OLD documents, prepare a context with `WithReturnOld` with a slice of documents.
	// To wait until documents has been synced to disk, prepare a context with `WithWaitForSync`.
	// If no document exists with a given key, a NotFoundError is returned at its errors index.
	// If keys is nil, each element in the documents slice must contain a `_key` field.
	ReplaceDocuments(ctx context.Context, keys []string, documents interface{}) (DocumentMetaSlice, ErrorSlice, error)

	// RemoveDocument removes a single document with given key from the collection.
	// The document meta data is returned.
	// To return the OLD document, prepare a context with `WithReturnOld`.
	// To wait until removal has been synced to disk, prepare a context with `WithWaitForSync`.
	// If no document exists with given key, a NotFoundError is returned.
	RemoveDocument(ctx context.Context, key string) (DocumentMeta, error)

	// RemoveDocuments removes multiple documents with given keys from the collection.
	// The document meta data are returned.
	// To return the OLD documents, prepare a context with `WithReturnOld` with a slice of documents.
	// To wait until removal has been synced to disk, prepare a context with `WithWaitForSync`.
	// If no document exists with a given key, a NotFoundError is returned at its errors index.
	RemoveDocuments(ctx context.Context, keys []string) (DocumentMetaSlice, ErrorSlice, error)

	// ImportDocuments imports one or more documents into the collection.
	// The document data is loaded from the given documents argument, statistics are returned.
	// The documents argument can be one of the following:
	// - An array of structs: All structs will be imported as individual documents.
	// - An array of maps: All maps will be imported as individual documents.
	// To wait until all documents have been synced to disk, prepare a context with `WithWaitForSync`.
	// To return details about documents that could not be imported, prepare a context with `WithImportDetails`.
	ImportDocuments(ctx context.Context, documents interface{}, options *ImportDocumentOptions) (ImportDocumentStatistics, error)
}

// ImportDocumentOptions holds optional options that control the import document process.
type ImportDocumentOptions struct {
	// FromPrefix is an optional prefix for the values in _from attributes. If specified, the value is automatically
	// prepended to each _from input value. This allows specifying just the keys for _from.
	FromPrefix string `json:"fromPrefix,omitempty"`
	// ToPrefix is an optional prefix for the values in _to attributes. If specified, the value is automatically
	// prepended to each _to input value. This allows specifying just the keys for _to.
	ToPrefix string `json:"toPrefix,omitempty"`
	// Overwrite is a flag that if set, then all data in the collection will be removed prior to the import.
	// Note that any existing index definitions will be preseved.
	Overwrite bool `json:"overwrite,omitempty"`
	// OnDuplicate controls what action is carried out in case of a unique key constraint violation.
	// Possible values are:
	// - ImportOnDuplicateError
	// - ImportOnDuplicateUpdate
	// - ImportOnDuplicateReplace
	// - ImportOnDuplicateIgnore
	OnDuplicate ImportOnDuplicate `json:"onDuplicate,omitempty"`
	// Complete is a flag that if set, will make the whole import fail if any error occurs.
	// Otherwise the import will continue even if some documents cannot be imported.
	Complete bool `json:"complete,omitempty"`
}

// ImportOnDuplicate is a type to control what action is carried out in case of a unique key constraint violation.
type ImportOnDuplicate string

const (
	// ImportOnDuplicateError will not import the current document because of the unique key constraint violation.
	// This is the default setting.
	ImportOnDuplicateError = ImportOnDuplicate("error")
	// ImportOnDuplicateUpdate will update an existing document in the database with the data specified in the request.
	// Attributes of the existing document that are not present in the request will be preserved.
	ImportOnDuplicateUpdate = ImportOnDuplicate("update")
	// ImportOnDuplicateReplace will replace an existing document in the database with the data specified in the request.
	ImportOnDuplicateReplace = ImportOnDuplicate("replace")
	// ImportOnDuplicateIgnore will not update an existing document and simply ignore the error caused by a unique key constraint violation.
	ImportOnDuplicateIgnore = ImportOnDuplicate("ignore")
)

// ImportDocumentStatistics holds statistics of an import action.
type ImportDocumentStatistics struct {
	// Created holds the number of documents imported.
	Created int64 `json:"created,omitempty"`
	// Errors holds the number of documents that were not imported due to an error.
	Errors int64 `json:"errors,omitempty"`
	// Empty holds the number of empty lines found in the input (will only contain a value greater zero for types documents or auto).
	Empty int64 `json:"empty,omitempty"`
	// Updated holds the number of updated/replaced documents (in case onDuplicate was set to either update or replace).
	Updated int64 `json:"updated,omitempty"`
	// Ignored holds the number of failed but ignored insert operations (in case onDuplicate was set to ignore).
	Ignored int64 `json:"ignored,omitempty"`
	// if query parameter details is set to true, the result will contain a details attribute which is an array
	// with more detailed information about which documents could not be inserted.
	Details []string

	ArangoError
}
<|endoftext|>


<commit_before>//go:build linux && cgo && !agent

package db

import (
	"database/sql"
	"fmt"

	"github.com/lxc/lxd/lxd/db/query"
	"github.com/lxc/lxd/shared"
	"github.com/lxc/lxd/shared/api"
)

// Code generation directives.
//
//go:generate -command mapper lxd-generate db mapper -t projects.mapper.go
//go:generate mapper reset
//
//go:generate mapper stmt -p db -e project objects
//go:generate mapper stmt -p db -e project objects-by-Name
//go:generate mapper stmt -p db -e project objects-by-ID
//go:generate mapper stmt -p db -e project create struct=Project
//go:generate mapper stmt -p db -e project id
//go:generate mapper stmt -p db -e project rename
//go:generate mapper stmt -p db -e project update struct=Project
//go:generate mapper stmt -p db -e project delete-by-Name
//
//go:generate mapper method -p db -e project GetMany references=Config version=2
//go:generate mapper method -p db -e project GetOne struct=Project version=2
//go:generate mapper method -p db -e project Exists struct=Project version=2
//go:generate mapper method -p db -e project Create references=Config version=2
//go:generate mapper method -p db -e project ID struct=Project version=2
//go:generate mapper method -p db -e project Rename version=2
//go:generate mapper method -p db -e project DeleteOne-by-Name version=2

// Project represents a LXD project
type Project struct {
	ID          int
	Description string
	Name        string `db:"omit=update"`
}

// ProjectFilter specifies potential query parameter fields.
type ProjectFilter struct {
	ID   *int
	Name *string `db:"omit=update"` // If non-empty, return only the project with this name.
}

// ToAPI converts the database Project struct to an api.Project entry.
func (p *Project) ToAPI(tx *ClusterTx) (*api.Project, error) {
	apiProject := &api.Project{
		ProjectPut: api.ProjectPut{
			Description: p.Description,
		},
		Name: p.Name,
	}

	var err error
	apiProject.Config, err = tx.GetProjectConfig(p.ID)
	if err != nil {
		return nil, err
	}

	return apiProject, nil
}

// ProjectHasProfiles is a helper to check if a project has the profiles
// feature enabled.
func (c *ClusterTx) ProjectHasProfiles(name string) (bool, error) {
	return projectHasProfiles(c.tx, name)
}

// GetProjectNames returns the names of all available projects.
func (c *ClusterTx) GetProjectNames() ([]string, error) {
	stmt := "SELECT name FROM projects"

	names, err := query.SelectStrings(c.tx, stmt)
	if err != nil {
		return nil, fmt.Errorf("Fetch project names: %w", err)
	}

	return names, nil
}

// GetProjectIDsToNames returns a map associating each project ID to its
// project name.
func (c *ClusterTx) GetProjectIDsToNames() (map[int64]string, error) {
	stmt := "SELECT id, name FROM projects"

	rows, err := c.tx.Query(stmt)
	if err != nil {
		return nil, err
	}
	defer rows.Close()

	result := map[int64]string{}
	for i := 0; rows.Next(); i++ {
		var id int64
		var name string

		err := rows.Scan(&id, &name)
		if err != nil {
			return nil, err
		}

		result[id] = name
	}

	err = rows.Err()
	if err != nil {
		return nil, err
	}

	return result, nil
}

func projectHasProfiles(tx *sql.Tx, name string) (bool, error) {
	stmt := `
SELECT projects_config.value
  FROM projects_config
  JOIN projects ON projects.id=projects_config.project_id
 WHERE projects.name=? AND projects_config.key='features.profiles'
`
	values, err := query.SelectStrings(tx, stmt, name)
	if err != nil {
		return false, fmt.Errorf("Fetch project config: %w", err)
	}

	if len(values) == 0 {
		return false, nil
	}

	return shared.IsTrue(values[0]), nil
}

// ProjectHasImages is a helper to check if a project has the images
// feature enabled.
func (c *ClusterTx) ProjectHasImages(name string) (bool, error) {
	project, err := c.GetProject(name)
	if err != nil {
		return false, fmt.Errorf("fetch project: %w", err)
	}

	config, err := c.GetProjectConfig(project.ID)
	if err != nil {
		return false, err
	}

	enabled := shared.IsTrue(config["features.images"])

	return enabled, nil
}

// UpdateProject updates the project matching the given key parameters.
func (c *ClusterTx) UpdateProject(name string, object api.ProjectPut) error {
	id, err := c.GetProjectID(name)
	if err != nil {
		return fmt.Errorf("Fetch project ID: %w", err)
	}

	stmt := c.stmt(projectUpdate)
	result, err := stmt.Exec(object.Description, id)
	if err != nil {
		return fmt.Errorf("Update project: %w", err)
	}

	n, err := result.RowsAffected()
	if err != nil {
		return fmt.Errorf("Fetch affected rows: %w", err)
	}
	if n != 1 {
		return fmt.Errorf("Query updated %d rows instead of 1", n)
	}

	// Clear config.
	_, err = c.tx.Exec(`
DELETE FROM projects_config WHERE projects_config.project_id = ?
`, id)
	if err != nil {
		return fmt.Errorf("Delete project config: %w", err)
	}

	err = c.UpdateConfig("project", int(id), object.Config)
	if err != nil {
		return fmt.Errorf("Insert config for project: %w", err)
	}

	return nil
}

// InitProjectWithoutImages updates populates the images_profiles table with
// all images from the default project when a project is created with
// features.images=false.
func (c *ClusterTx) InitProjectWithoutImages(project string) error {
	defaultProfileID, err := c.GetProfileID(project, "default")
	if err != nil {
		return fmt.Errorf("Fetch project ID: %w", err)
	}
	stmt := `INSERT INTO images_profiles (image_id, profile_id)
	SELECT images.id, ? FROM images WHERE project_id=1`
	_, err = c.tx.Exec(stmt, defaultProfileID)
	return err
}

// GetProjectUsedBy returns all the instances, images, profiles, storage
// volumes, networks, and acls that use the given project.
func (c *ClusterTx) GetProjectUsedBy(project Project) ([]string, error) {
	instances, err := c.GetInstanceURIs(InstanceFilter{Project: &project.Name})
	if err != nil {
		return nil, err
	}

	images, err := c.GetImageURIs(ImageFilter{Project: &project.Name})
	if err != nil {
		return nil, err
	}

	profiles, err := c.GetProfileURIs(ProfileFilter{Project: &project.Name})
	if err != nil {
		return nil, err
	}

	volumes, err := c.GetStorageVolumeURIs(project.Name)
	if err != nil {
		return nil, err
	}

	networks, err := c.GetNetworkURIs(project.ID, project.Name)
	if err != nil {
		return nil, err
	}

	networkACLs, err := c.GetNetworkACLURIs(project.ID, project.Name)
	if err != nil {
		return nil, err
	}

	usedBy := instances
	usedBy = append(usedBy, images...)
	usedBy = append(usedBy, profiles...)
	usedBy = append(usedBy, volumes...)
	usedBy = append(usedBy, networks...)
	usedBy = append(usedBy, networkACLs...)

	return usedBy, nil
}

// GetProject returns the project with the given key.
func (c *Cluster) GetProject(projectName string) (*Project, error) {
	var err error
	var p *Project
	err = c.Transaction(func(tx *ClusterTx) error {
		p, err = tx.GetProject(projectName)
		if err != nil {
			return err
		}

		return nil
	})
	if err != nil {
		return nil, err
	}

	return p, nil
}
<commit_msg>lxd/db/projects: Removed UsedBy handling from db package<commit_after>//go:build linux && cgo && !agent

package db

import (
	"database/sql"
	"fmt"

	"github.com/lxc/lxd/lxd/db/query"
	"github.com/lxc/lxd/shared"
	"github.com/lxc/lxd/shared/api"
)

// Code generation directives.
//
//go:generate -command mapper lxd-generate db mapper -t projects.mapper.go
//go:generate mapper reset
//
//go:generate mapper stmt -p db -e project objects
//go:generate mapper stmt -p db -e project objects-by-Name
//go:generate mapper stmt -p db -e project objects-by-ID
//go:generate mapper stmt -p db -e project create struct=Project
//go:generate mapper stmt -p db -e project id
//go:generate mapper stmt -p db -e project rename
//go:generate mapper stmt -p db -e project update struct=Project
//go:generate mapper stmt -p db -e project delete-by-Name
//
//go:generate mapper method -p db -e project GetMany references=Config version=2
//go:generate mapper method -p db -e project GetOne struct=Project version=2
//go:generate mapper method -p db -e project Exists struct=Project version=2
//go:generate mapper method -p db -e project Create references=Config version=2
//go:generate mapper method -p db -e project ID struct=Project version=2
//go:generate mapper method -p db -e project Rename version=2
//go:generate mapper method -p db -e project DeleteOne-by-Name version=2

// Project represents a LXD project
type Project struct {
	ID          int
	Description string
	Name        string `db:"omit=update"`
}

// ProjectFilter specifies potential query parameter fields.
type ProjectFilter struct {
	ID   *int
	Name *string `db:"omit=update"` // If non-empty, return only the project with this name.
}

// ToAPI converts the database Project struct to an api.Project entry.
func (p *Project) ToAPI(tx *ClusterTx) (*api.Project, error) {
	apiProject := &api.Project{
		ProjectPut: api.ProjectPut{
			Description: p.Description,
		},
		Name: p.Name,
	}

	var err error
	apiProject.Config, err = tx.GetProjectConfig(p.ID)
	if err != nil {
		return nil, err
	}

	return apiProject, nil
}

// ProjectHasProfiles is a helper to check if a project has the profiles
// feature enabled.
func (c *ClusterTx) ProjectHasProfiles(name string) (bool, error) {
	return projectHasProfiles(c.tx, name)
}

// GetProjectNames returns the names of all available projects.
func (c *ClusterTx) GetProjectNames() ([]string, error) {
	stmt := "SELECT name FROM projects"

	names, err := query.SelectStrings(c.tx, stmt)
	if err != nil {
		return nil, fmt.Errorf("Fetch project names: %w", err)
	}

	return names, nil
}

// GetProjectIDsToNames returns a map associating each project ID to its
// project name.
func (c *ClusterTx) GetProjectIDsToNames() (map[int64]string, error) {
	stmt := "SELECT id, name FROM projects"

	rows, err := c.tx.Query(stmt)
	if err != nil {
		return nil, err
	}
	defer rows.Close()

	result := map[int64]string{}
	for i := 0; rows.Next(); i++ {
		var id int64
		var name string

		err := rows.Scan(&id, &name)
		if err != nil {
			return nil, err
		}

		result[id] = name
	}

	err = rows.Err()
	if err != nil {
		return nil, err
	}

	return result, nil
}

func projectHasProfiles(tx *sql.Tx, name string) (bool, error) {
	stmt := `
SELECT projects_config.value
  FROM projects_config
  JOIN projects ON projects.id=projects_config.project_id
 WHERE projects.name=? AND projects_config.key='features.profiles'
`
	values, err := query.SelectStrings(tx, stmt, name)
	if err != nil {
		return false, fmt.Errorf("Fetch project config: %w", err)
	}

	if len(values) == 0 {
		return false, nil
	}

	return shared.IsTrue(values[0]), nil
}

// ProjectHasImages is a helper to check if a project has the images
// feature enabled.
func (c *ClusterTx) ProjectHasImages(name string) (bool, error) {
	project, err := c.GetProject(name)
	if err != nil {
		return false, fmt.Errorf("fetch project: %w", err)
	}

	config, err := c.GetProjectConfig(project.ID)
	if err != nil {
		return false, err
	}

	enabled := shared.IsTrue(config["features.images"])

	return enabled, nil
}

// UpdateProject updates the project matching the given key parameters.
func (c *ClusterTx) UpdateProject(name string, object api.ProjectPut) error {
	id, err := c.GetProjectID(name)
	if err != nil {
		return fmt.Errorf("Fetch project ID: %w", err)
	}

	stmt := c.stmt(projectUpdate)
	result, err := stmt.Exec(object.Description, id)
	if err != nil {
		return fmt.Errorf("Update project: %w", err)
	}

	n, err := result.RowsAffected()
	if err != nil {
		return fmt.Errorf("Fetch affected rows: %w", err)
	}
	if n != 1 {
		return fmt.Errorf("Query updated %d rows instead of 1", n)
	}

	// Clear config.
	_, err = c.tx.Exec(`
DELETE FROM projects_config WHERE projects_config.project_id = ?
`, id)
	if err != nil {
		return fmt.Errorf("Delete project config: %w", err)
	}

	err = c.UpdateConfig("project", int(id), object.Config)
	if err != nil {
		return fmt.Errorf("Insert config for project: %w", err)
	}

	return nil
}

// InitProjectWithoutImages updates populates the images_profiles table with
// all images from the default project when a project is created with
// features.images=false.
func (c *ClusterTx) InitProjectWithoutImages(project string) error {
	defaultProfileID, err := c.GetProfileID(project, "default")
	if err != nil {
		return fmt.Errorf("Fetch project ID: %w", err)
	}
	stmt := `INSERT INTO images_profiles (image_id, profile_id)
	SELECT images.id, ? FROM images WHERE project_id=1`
	_, err = c.tx.Exec(stmt, defaultProfileID)
	return err
}

// GetProject returns the project with the given key.
func (c *Cluster) GetProject(projectName string) (*Project, error) {
	var err error
	var p *Project
	err = c.Transaction(func(tx *ClusterTx) error {
		p, err = tx.GetProject(projectName)
		if err != nil {
			return err
		}

		return nil
	})
	if err != nil {
		return nil, err
	}

	return p, nil
}
<|endoftext|>


<commit_before>package apollostats

import (
	"fmt"

	_ "github.com/go-sql-driver/mysql"
	"github.com/jinzhu/gorm"
)

const MAX_ROWS = 200

// NOTE: DON'T USE ANY WRITE OPERATIONS ON THE DATABASE!
// We're interfacing with an external, live game database!

type DB struct {
	*gorm.DB
}

func OpenDB(DSN string) (*DB, error) {
	tmp := fmt.Sprintf("%s?parseTime=True", DSN)
	db, e := gorm.Open("mysql", tmp)
	if e != nil {
		return nil, e
	}
	return &DB{&db}, nil
}

func (db *DB) AllBans() []*Ban {
	var tmp []*Ban
	db.Order("id desc").Limit(MAX_ROWS).Find(&tmp)
	return tmp
}

func (db *DB) AllAccountItems() []*AccountItem {
	var tmp []*AccountItem
	db.Order("id desc").Limit(MAX_ROWS).Find(&tmp)
	return tmp
}

func (db *DB) AllDeaths() []*Death {
	var tmp []*Death
	db.Order("id desc").Limit(MAX_ROWS).Find(&tmp)
	return tmp
}

func (db *DB) AllRounds() []*RoundStats {
	var tmp []*RoundStats
	db.Order("id desc").Limit(MAX_ROWS).Find(&tmp)
	return tmp
}

func (db *DB) GetRound(id int64) (*RoundStats, error) {
	var tmp RoundStats
	if db.First(&tmp, id).RecordNotFound() {
		return nil, fmt.Errorf("Round not found")
	}
	return &tmp, nil
}

func (db *DB) GetAntags(id int64) []*RoundAntags {
	var tmp []*RoundAntags
	db.Order("round_id desc, name asc").Where("round_id = ?", id).Find(&tmp)
	return tmp
}

func (db *DB) GetAILaws(id int64) []*RoundAILaws {
	var tmp []*RoundAILaws
	db.Order("round_id desc, law asc").Where("round_id = ?", id).Find(&tmp)
	return tmp
}

func (db *DB) GetDeaths(id int64) []*Death {
	var tmp []*Death
	db.Order("round_id desc, name asc").Where("round_id = ?", id).Find(&tmp)
	return tmp
}
<commit_msg>Adds a db timeout, not sure it's doing anything though...<commit_after>package apollostats

import (
	"fmt"

	_ "github.com/go-sql-driver/mysql"
	"github.com/jinzhu/gorm"
)

const MAX_ROWS = 200

// NOTE: DON'T USE ANY WRITE OPERATIONS ON THE DATABASE!
// We're interfacing with an external, live game database!

type DB struct {
	*gorm.DB
}

func OpenDB(DSN string) (*DB, error) {
	tmp := fmt.Sprintf("%s?parseTime=True&timeout=30s", DSN)
	db, e := gorm.Open("mysql", tmp)
	if e != nil {
		return nil, e
	}
	return &DB{&db}, nil
}

func (db *DB) AllBans() []*Ban {
	var tmp []*Ban
	db.Order("id desc").Limit(MAX_ROWS).Find(&tmp)
	return tmp
}

func (db *DB) AllAccountItems() []*AccountItem {
	var tmp []*AccountItem
	db.Order("id desc").Limit(MAX_ROWS).Find(&tmp)
	return tmp
}

func (db *DB) AllDeaths() []*Death {
	var tmp []*Death
	db.Order("id desc").Limit(MAX_ROWS).Find(&tmp)
	return tmp
}

func (db *DB) AllRounds() []*RoundStats {
	var tmp []*RoundStats
	db.Order("id desc").Limit(MAX_ROWS).Find(&tmp)
	return tmp
}

func (db *DB) GetRound(id int64) (*RoundStats, error) {
	var tmp RoundStats
	if db.First(&tmp, id).RecordNotFound() {
		return nil, fmt.Errorf("Round not found")
	}
	return &tmp, nil
}

func (db *DB) GetAntags(id int64) []*RoundAntags {
	var tmp []*RoundAntags
	db.Order("round_id desc, name asc").Where("round_id = ?", id).Find(&tmp)
	return tmp
}

func (db *DB) GetAILaws(id int64) []*RoundAILaws {
	var tmp []*RoundAILaws
	db.Order("round_id desc, law asc").Where("round_id = ?", id).Find(&tmp)
	return tmp
}

func (db *DB) GetDeaths(id int64) []*Death {
	var tmp []*Death
	db.Order("round_id desc, name asc").Where("round_id = ?", id).Find(&tmp)
	return tmp
}
<|endoftext|>