<commit_before>// Copyright (c) 2015-2016, NVIDIA CORPORATION. All rights reserved.

package nvidia

import (
	"bufio"
	"bytes"
	"debug/elf"
	"encoding/binary"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"os/exec"
	"path"
	"path/filepath"
	"regexp"
	"strings"

	"github.com/NVIDIA/nvidia-docker/tools/src/ldcache"
)

const (
	binDir   = "bin"
	lib32Dir = "lib"
	lib64Dir = "lib64"
)

type components map[string][]string

type volumeDir struct {
	name  string
	files []string
}

type VolumeInfo struct {
	Name       string
	Mountpoint string
	Components components
}

type Volume struct {
	*VolumeInfo

	Path    string
	Version string
	dirs    []volumeDir
}

type VolumeMap map[string]*Volume

type FileCloneStrategy interface {
	Clone(src, dst string) error
}

type LinkStrategy struct{}

func (s LinkStrategy) Clone(src, dst string) error {
	return os.Link(src, dst)
}

type LinkOrCopyStrategy struct{}

func (s LinkOrCopyStrategy) Clone(src, dst string) error {
	// Prefer hard link, fallback to copy
	err := os.Link(src, dst)
	if err != nil {
		err = Copy(src, dst)
	}
	return err
}

func Copy(src, dst string) error {
	s, err := os.Open(src)
	if err != nil {
		return err
	}
	defer s.Close()

	fi, err := s.Stat()
	if err != nil {
		return err
	}

	d, err := os.Create(dst)
	if err != nil {
		return err
	}

	if _, err := io.Copy(d, s); err != nil {
		d.Close()
		return err
	}

	if err := d.Chmod(fi.Mode()); err != nil {
		d.Close()
		return err
	}

	return d.Close()
}

var Volumes = []VolumeInfo{
	{
		"nvidia_driver",
		"/usr/local/nvidia",
		components{
			"binaries": {
				//"nvidia-modprobe",       // Kernel module loader
				//"nvidia-settings",       // X server settings
				//"nvidia-xconfig",        // X xorg.conf editor
				"nvidia-cuda-mps-control", // Multi process service CLI
				"nvidia-cuda-mps-server",  // Multi process service server
				"nvidia-debugdump",        // GPU coredump utility
				"nvidia-persistenced",     // Persistence mode utility
				"nvidia-smi",              // System management interface
			},
			"libraries": {
				// ------- X11 -------

				//"libnvidia-cfg.so",  // GPU configuration (used by nvidia-xconfig)
				//"libnvidia-gtk2.so", // GTK2 (used by nvidia-settings)
				//"libnvidia-gtk3.so", // GTK3 (used by nvidia-settings)
				//"libnvidia-wfb.so",  // Wrapped software rendering module for X server
				//"libglx.so",         // GLX extension module for X server

				// ----- Compute -----

				"libnvidia-ml.so",              // Management library
				"libcuda.so",                   // CUDA driver library
				"libnvidia-ptxjitcompiler.so",  // PTX-SASS JIT compiler (used by libcuda)
				"libnvidia-fatbinaryloader.so", // fatbin loader (used by libcuda)
				"libnvidia-opencl.so",          // NVIDIA OpenCL ICD
				"libnvidia-compiler.so",        // NVVM-PTX compiler for OpenCL (used by libnvidia-opencl)
				//"libOpenCL.so",               // OpenCL ICD loader

				// ------ Video ------

				"libvdpau_nvidia.so",  // NVIDIA VDPAU ICD
				"libnvidia-encode.so", // Video encoder
				"libnvcuvid.so",       // Video decoder
				"libnvidia-fbc.so",    // Framebuffer capture
				"libnvidia-ifr.so",    // OpenGL framebuffer capture

				// ----- Graphic -----

				// XXX In an ideal world we would only mount nvidia_* vendor specific libraries and
				// install ICD loaders inside the container. However, for backward compatibility reason
				// we need to mount everything. This will hopefully change once GLVND is well established.

				"libGL.so",         // OpenGL/GLX legacy _or_ compatibility wrapper (GLVND)
				"libGLX.so",        // GLX ICD loader (GLVND)
				"libOpenGL.so",     // OpenGL ICD loader (GLVND)
				"libGLESv1_CM.so",  // OpenGL ES v1 common profile legacy _or_ ICD loader (GLVND)
				"libGLESv2.so",     // OpenGL ES v2 legacy _or_ ICD loader (GLVND)
				"libEGL.so",        // EGL ICD loader
				"libGLdispatch.so", // OpenGL dispatch (GLVND) (used by libOpenGL, libEGL and libGLES*)

				"libGLX_nvidia.so",         // OpenGL/GLX ICD (GLVND)
				"libEGL_nvidia.so",         // EGL ICD (GLVND)
				"libGLESv2_nvidia.so",      // OpenGL ES v2 ICD (GLVND)
				"libGLESv1_CM_nvidia.so",   // OpenGL ES v1 common profile ICD (GLVND)
				"libnvidia-eglcore.so",     // EGL core (used by libGLES* or libGLES*_nvidia and libEGL_nvidia)
				"libnvidia-egl-wayland.so", // EGL wayland extensions (used by libEGL_nvidia)
				"libnvidia-glcore.so",      // OpenGL core (used by libGL or libGLX_nvidia)
				"libnvidia-tls.so",         // Thread local storage (used by libGL or libGLX_nvidia)
				"libnvidia-glsi.so",        // OpenGL system interaction (used by libEGL_nvidia)
			},
		},
	},
}

func blacklisted(file string, obj *elf.File) (bool, error) {
	lib := regexp.MustCompile(`^.*/lib([\w-]+)\.so[\d.]*$`)
	glcore := regexp.MustCompile(`libnvidia-e?glcore\.so`)
	gldispatch := regexp.MustCompile(`libGLdispatch\.so`)

	if m := lib.FindStringSubmatch(file); m != nil {
		switch m[1] {

		// Blacklist EGL/OpenGL libraries issued by other vendors
		case "EGL":
			fallthrough
		case "GLESv1_CM":
			fallthrough
		case "GLESv2":
			fallthrough
		case "GL":
			deps, err := obj.DynString(elf.DT_NEEDED)
			if err != nil {
				return false, err
			}
			for _, d := range deps {
				if glcore.MatchString(d) || gldispatch.MatchString(d) {
					return false, nil
				}
			}
			return true, nil

		// Blacklist TLS libraries using the old ABI (!= 2.3.99)
		case "nvidia-tls":
			const abi = 0x6300000003
			s, err := obj.Section(".note.ABI-tag").Data()
			if err != nil {
				return false, err
			}
			return binary.LittleEndian.Uint64(s[24:]) != abi, nil
		}
	}
	return false, nil
}

func (v *Volume) Create(s FileCloneStrategy) (err error) {
	if err = os.MkdirAll(v.Path, 0755); err != nil {
		return
	}
	defer func() {
		if err != nil {
			v.Remove()
		}
	}()

	for _, d := range v.dirs {
		vpath := path.Join(v.Path, v.Version, d.name)
		if err := os.MkdirAll(vpath, 0755); err != nil {
			return err
		}

		// For each file matching the volume components (blacklist excluded), create a hardlink/copy
		// of it inside the volume directory. We also need to create soname symlinks similar to what
		// ldconfig does since our volume will only show up at runtime.
		for _, f := range d.files {
			obj, err := elf.Open(f)
			if err != nil {
				return fmt.Errorf("%s: %v", f, err)
			}
			defer obj.Close()

			ok, err := blacklisted(f, obj)
			if err != nil {
				return fmt.Errorf("%s: %v", f, err)
			}
			if ok {
				continue
			}

			l := path.Join(vpath, path.Base(f))
			if err := s.Clone(f, l); err != nil {
				return err
			}
			soname, err := obj.DynString(elf.DT_SONAME)
			if err != nil {
				return fmt.Errorf("%s: %v", f, err)
			}
			if len(soname) > 0 {
				l = path.Join(vpath, soname[0])
				if err := os.Symlink(path.Base(f), l); err != nil && !os.IsExist(err) {
					return err
				}
				// XXX GLVND requires this symlink for indirect GLX support
				// It won't be needed once we have an indirect GLX vendor neutral library.
				if strings.HasPrefix(soname[0], "libGLX_nvidia") {
					l = strings.Replace(l, "GLX_nvidia", "GLX_indirect", 1)
					if err := os.Symlink(path.Base(f), l); err != nil && !os.IsExist(err) {
						return err
					}
				}
			}
		}
	}
	return nil
}

func (v *Volume) Remove(version ...string) error {
	vv := v.Version
	if len(version) == 1 {
		vv = version[0]
	}
	return os.RemoveAll(path.Join(v.Path, vv))
}

func (v *Volume) Exists(version ...string) (bool, error) {
	vv := v.Version
	if len(version) == 1 {
		vv = version[0]
	}
	_, err := os.Stat(path.Join(v.Path, vv))
	if os.IsNotExist(err) {
		return false, nil
	}
	return true, err
}

func (v *Volume) ListVersions() ([]string, error) {
	dirs, err := ioutil.ReadDir(v.Path)
	if os.IsNotExist(err) {
		return nil, nil
	}
	if err != nil {
		return nil, err
	}
	versions := make([]string, len(dirs))
	for i := range dirs {
		versions[i] = dirs[i].Name()
	}
	return versions, nil
}

func which(bins ...string) ([]string, error) {
	paths := make([]string, 0, len(bins))

	out, _ := exec.Command("which", bins...).Output()
	r := bufio.NewReader(bytes.NewBuffer(out))
	for {
		p, err := r.ReadString('\n')
		if err == io.EOF {
			break
		}
		if err != nil {
			return nil, err
		}
		if p = strings.TrimSpace(p); !path.IsAbs(p) {
			continue
		}
		path, err := filepath.EvalSymlinks(p)
		if err != nil {
			return nil, err
		}
		paths = append(paths, path)
	}
	return paths, nil
}

func LookupVolumes(prefix string) (vols VolumeMap, err error) {
	drv, err := GetDriverVersion()
	if err != nil {
		return nil, err
	}
	cache, err := ldcache.Open()
	if err != nil {
		return nil, err
	}
	defer func() {
		if e := cache.Close(); err == nil {
			err = e
		}
	}()

	vols = make(VolumeMap, len(Volumes))

	for i := range Volumes {
		vol := &Volume{
			VolumeInfo: &Volumes[i],
			Path:       path.Join(prefix, Volumes[i].Name),
			Version:    drv,
		}

		for t, c := range vol.Components {
			switch t {
			case "binaries":
				bins, err := which(c...)
				if err != nil {
					return nil, err
				}
				vol.dirs = append(vol.dirs, volumeDir{binDir, bins})
			case "libraries":
				libs32, libs64 := cache.Lookup(c...)
				vol.dirs = append(vol.dirs,
					volumeDir{lib32Dir, libs32},
					volumeDir{lib64Dir, libs64},
				)
			}
		}
		vols[vol.Name] = vol
	}
	return
}
<commit_msg>Hardcode libcuda.so symlink for compatibility reason<commit_after>// Copyright (c) 2015-2016, NVIDIA CORPORATION. All rights reserved.

package nvidia

import (
	"bufio"
	"bytes"
	"debug/elf"
	"encoding/binary"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"os/exec"
	"path"
	"path/filepath"
	"regexp"
	"strings"

	"github.com/NVIDIA/nvidia-docker/tools/src/ldcache"
)

const (
	binDir   = "bin"
	lib32Dir = "lib"
	lib64Dir = "lib64"
)

type components map[string][]string

type volumeDir struct {
	name  string
	files []string
}

type VolumeInfo struct {
	Name       string
	Mountpoint string
	Components components
}

type Volume struct {
	*VolumeInfo

	Path    string
	Version string
	dirs    []volumeDir
}

type VolumeMap map[string]*Volume

type FileCloneStrategy interface {
	Clone(src, dst string) error
}

type LinkStrategy struct{}

func (s LinkStrategy) Clone(src, dst string) error {
	return os.Link(src, dst)
}

type LinkOrCopyStrategy struct{}

func (s LinkOrCopyStrategy) Clone(src, dst string) error {
	// Prefer hard link, fallback to copy
	err := os.Link(src, dst)
	if err != nil {
		err = Copy(src, dst)
	}
	return err
}

func Copy(src, dst string) error {
	s, err := os.Open(src)
	if err != nil {
		return err
	}
	defer s.Close()

	fi, err := s.Stat()
	if err != nil {
		return err
	}

	d, err := os.Create(dst)
	if err != nil {
		return err
	}

	if _, err := io.Copy(d, s); err != nil {
		d.Close()
		return err
	}

	if err := d.Chmod(fi.Mode()); err != nil {
		d.Close()
		return err
	}

	return d.Close()
}

var Volumes = []VolumeInfo{
	{
		"nvidia_driver",
		"/usr/local/nvidia",
		components{
			"binaries": {
				//"nvidia-modprobe",       // Kernel module loader
				//"nvidia-settings",       // X server settings
				//"nvidia-xconfig",        // X xorg.conf editor
				"nvidia-cuda-mps-control", // Multi process service CLI
				"nvidia-cuda-mps-server",  // Multi process service server
				"nvidia-debugdump",        // GPU coredump utility
				"nvidia-persistenced",     // Persistence mode utility
				"nvidia-smi",              // System management interface
			},
			"libraries": {
				// ------- X11 -------

				//"libnvidia-cfg.so",  // GPU configuration (used by nvidia-xconfig)
				//"libnvidia-gtk2.so", // GTK2 (used by nvidia-settings)
				//"libnvidia-gtk3.so", // GTK3 (used by nvidia-settings)
				//"libnvidia-wfb.so",  // Wrapped software rendering module for X server
				//"libglx.so",         // GLX extension module for X server

				// ----- Compute -----

				"libnvidia-ml.so",              // Management library
				"libcuda.so",                   // CUDA driver library
				"libnvidia-ptxjitcompiler.so",  // PTX-SASS JIT compiler (used by libcuda)
				"libnvidia-fatbinaryloader.so", // fatbin loader (used by libcuda)
				"libnvidia-opencl.so",          // NVIDIA OpenCL ICD
				"libnvidia-compiler.so",        // NVVM-PTX compiler for OpenCL (used by libnvidia-opencl)
				//"libOpenCL.so",               // OpenCL ICD loader

				// ------ Video ------

				"libvdpau_nvidia.so",  // NVIDIA VDPAU ICD
				"libnvidia-encode.so", // Video encoder
				"libnvcuvid.so",       // Video decoder
				"libnvidia-fbc.so",    // Framebuffer capture
				"libnvidia-ifr.so",    // OpenGL framebuffer capture

				// ----- Graphic -----

				// XXX In an ideal world we would only mount nvidia_* vendor specific libraries and
				// install ICD loaders inside the container. However, for backward compatibility reason
				// we need to mount everything. This will hopefully change once GLVND is well established.

				"libGL.so",         // OpenGL/GLX legacy _or_ compatibility wrapper (GLVND)
				"libGLX.so",        // GLX ICD loader (GLVND)
				"libOpenGL.so",     // OpenGL ICD loader (GLVND)
				"libGLESv1_CM.so",  // OpenGL ES v1 common profile legacy _or_ ICD loader (GLVND)
				"libGLESv2.so",     // OpenGL ES v2 legacy _or_ ICD loader (GLVND)
				"libEGL.so",        // EGL ICD loader
				"libGLdispatch.so", // OpenGL dispatch (GLVND) (used by libOpenGL, libEGL and libGLES*)

				"libGLX_nvidia.so",         // OpenGL/GLX ICD (GLVND)
				"libEGL_nvidia.so",         // EGL ICD (GLVND)
				"libGLESv2_nvidia.so",      // OpenGL ES v2 ICD (GLVND)
				"libGLESv1_CM_nvidia.so",   // OpenGL ES v1 common profile ICD (GLVND)
				"libnvidia-eglcore.so",     // EGL core (used by libGLES* or libGLES*_nvidia and libEGL_nvidia)
				"libnvidia-egl-wayland.so", // EGL wayland extensions (used by libEGL_nvidia)
				"libnvidia-glcore.so",      // OpenGL core (used by libGL or libGLX_nvidia)
				"libnvidia-tls.so",         // Thread local storage (used by libGL or libGLX_nvidia)
				"libnvidia-glsi.so",        // OpenGL system interaction (used by libEGL_nvidia)
			},
		},
	},
}

func blacklisted(file string, obj *elf.File) (bool, error) {
	lib := regexp.MustCompile(`^.*/lib([\w-]+)\.so[\d.]*$`)
	glcore := regexp.MustCompile(`libnvidia-e?glcore\.so`)
	gldispatch := regexp.MustCompile(`libGLdispatch\.so`)

	if m := lib.FindStringSubmatch(file); m != nil {
		switch m[1] {

		// Blacklist EGL/OpenGL libraries issued by other vendors
		case "EGL":
			fallthrough
		case "GLESv1_CM":
			fallthrough
		case "GLESv2":
			fallthrough
		case "GL":
			deps, err := obj.DynString(elf.DT_NEEDED)
			if err != nil {
				return false, err
			}
			for _, d := range deps {
				if glcore.MatchString(d) || gldispatch.MatchString(d) {
					return false, nil
				}
			}
			return true, nil

		// Blacklist TLS libraries using the old ABI (!= 2.3.99)
		case "nvidia-tls":
			const abi = 0x6300000003
			s, err := obj.Section(".note.ABI-tag").Data()
			if err != nil {
				return false, err
			}
			return binary.LittleEndian.Uint64(s[24:]) != abi, nil
		}
	}
	return false, nil
}

func (v *Volume) Create(s FileCloneStrategy) (err error) {
	if err = os.MkdirAll(v.Path, 0755); err != nil {
		return
	}
	defer func() {
		if err != nil {
			v.Remove()
		}
	}()

	for _, d := range v.dirs {
		vpath := path.Join(v.Path, v.Version, d.name)
		if err := os.MkdirAll(vpath, 0755); err != nil {
			return err
		}

		// For each file matching the volume components (blacklist excluded), create a hardlink/copy
		// of it inside the volume directory. We also need to create soname symlinks similar to what
		// ldconfig does since our volume will only show up at runtime.
		for _, f := range d.files {
			obj, err := elf.Open(f)
			if err != nil {
				return fmt.Errorf("%s: %v", f, err)
			}
			defer obj.Close()

			ok, err := blacklisted(f, obj)
			if err != nil {
				return fmt.Errorf("%s: %v", f, err)
			}
			if ok {
				continue
			}

			l := path.Join(vpath, path.Base(f))
			if err := s.Clone(f, l); err != nil {
				return err
			}
			soname, err := obj.DynString(elf.DT_SONAME)
			if err != nil {
				return fmt.Errorf("%s: %v", f, err)
			}
			if len(soname) > 0 {
				l = path.Join(vpath, soname[0])
				if err := os.Symlink(path.Base(f), l); err != nil && !os.IsExist(err) {
					return err
				}
				// XXX Many applications (wrongly) assume that libcuda.so exists (e.g. with dlopen)
				// Hardcode the libcuda symlink for the time being.
				if strings.HasPrefix(soname[0], "libcuda") {
					l = strings.TrimRight(l, ".0123456789")
					if err := os.Symlink(path.Base(f), l); err != nil && !os.IsExist(err) {
						return err
					}
				}
				// XXX GLVND requires this symlink for indirect GLX support
				// It won't be needed once we have an indirect GLX vendor neutral library.
				if strings.HasPrefix(soname[0], "libGLX_nvidia") {
					l = strings.Replace(l, "GLX_nvidia", "GLX_indirect", 1)
					if err := os.Symlink(path.Base(f), l); err != nil && !os.IsExist(err) {
						return err
					}
				}
			}
		}
	}
	return nil
}

func (v *Volume) Remove(version ...string) error {
	vv := v.Version
	if len(version) == 1 {
		vv = version[0]
	}
	return os.RemoveAll(path.Join(v.Path, vv))
}

func (v *Volume) Exists(version ...string) (bool, error) {
	vv := v.Version
	if len(version) == 1 {
		vv = version[0]
	}
	_, err := os.Stat(path.Join(v.Path, vv))
	if os.IsNotExist(err) {
		return false, nil
	}
	return true, err
}

func (v *Volume) ListVersions() ([]string, error) {
	dirs, err := ioutil.ReadDir(v.Path)
	if os.IsNotExist(err) {
		return nil, nil
	}
	if err != nil {
		return nil, err
	}
	versions := make([]string, len(dirs))
	for i := range dirs {
		versions[i] = dirs[i].Name()
	}
	return versions, nil
}

func which(bins ...string) ([]string, error) {
	paths := make([]string, 0, len(bins))

	out, _ := exec.Command("which", bins...).Output()
	r := bufio.NewReader(bytes.NewBuffer(out))
	for {
		p, err := r.ReadString('\n')
		if err == io.EOF {
			break
		}
		if err != nil {
			return nil, err
		}
		if p = strings.TrimSpace(p); !path.IsAbs(p) {
			continue
		}
		path, err := filepath.EvalSymlinks(p)
		if err != nil {
			return nil, err
		}
		paths = append(paths, path)
	}
	return paths, nil
}

func LookupVolumes(prefix string) (vols VolumeMap, err error) {
	drv, err := GetDriverVersion()
	if err != nil {
		return nil, err
	}
	cache, err := ldcache.Open()
	if err != nil {
		return nil, err
	}
	defer func() {
		if e := cache.Close(); err == nil {
			err = e
		}
	}()

	vols = make(VolumeMap, len(Volumes))

	for i := range Volumes {
		vol := &Volume{
			VolumeInfo: &Volumes[i],
			Path:       path.Join(prefix, Volumes[i].Name),
			Version:    drv,
		}

		for t, c := range vol.Components {
			switch t {
			case "binaries":
				bins, err := which(c...)
				if err != nil {
					return nil, err
				}
				vol.dirs = append(vol.dirs, volumeDir{binDir, bins})
			case "libraries":
				libs32, libs64 := cache.Lookup(c...)
				vol.dirs = append(vol.dirs,
					volumeDir{lib32Dir, libs32},
					volumeDir{lib64Dir, libs64},
				)
			}
		}
		vols[vol.Name] = vol
	}
	return
}
<|endoftext|>


<commit_before>/**
 * Date: 8/02/14
 * Time: 11:22 AM
 */
package opal

import (
	"database/sql"
	"fmt"
	"github.com/twinj/version"
	"log"
	"reflect"
)

const (
	// Group magic number
	group = 0xAA7E5441

	// Magic number specific to Opal
	opalMagic = 0x88280BA1

	// Version information
	release   = 1
	iteration = 0
	revision  = 0
	api       = 1
)

var (
	// The current instance - instances can be swapped out
	// TODO create a package only handle set by the user
	currentGem *Gem
)

func init() {
	version.System(group, opalMagic, release, iteration, revision, api, "OPAL")
}

// Copy a Gem into the packages address to be used as the current service
func SwitchGem(pGem Gem) bool {
	*currentGem = pGem
	return true
}

// Retrieve a copy of Gem
func GetGem() Gem {
	return *currentGem
}

// ******************************************** Data Access

// ActiveRecordDAO acts as a data provider for a Model's Entity.
// It is a limited version of the full Domain access object.
type ActiveRecordDAO interface {

	// Takes a Model saves it as a new data-store entity and
	// returns a result
	Insert(pModel Model) Result

	// Takes a Model, updates an existing entity from the
	// data-store and returns a result
	Save(pModel Model) Result

	// Takes a Model and removes an existing entity from the
	// data-store
	Delete(pModel Model) Result
}

// ModelDAO acts a data provider for a Model's domain
// Methods are
type ModelDAO interface {

	OPAL
	ActiveRecordDAO

	// Find all models within the domain
	FindAllModels(pModelName ModelName) []Model

	// Find a specific Model using its keys
	FindModel(pModelName ModelName, pKeys ...interface{}) Model

	// Create a Sql Builder for the specified Model
	SqlBuilder(pModelName ModelName) *SqlBuilder
}

// ModelIDAO Implements ModelDAO
type ModelIDAO struct {
	gem *Gem
}

// Opal partially implements the opal OPAL interface
func (ModelIDAO) opal() OpalMagic {
	return opal
}

// Opal partially implements the opal OPAL interface
func (ModelIDAO) Kind() reflect.Kind {
	return DAO
}

// TODO betterway to handle DAO
func (o ModelIDAO) Gem() Gem {
	return *o.gem
}

func (o ModelIDAO) FindAllModels(pModelName ModelName) []Model {
	meta := o.gem.allModelsMetadata[pModelName]
	// TODO what if lose connection
	stmt := meta.preparedStatements[findAll]
	rows, err := stmt.Query()
	if err != nil {
		log.Print(err)
		return nil // TODO handle err
	}
	defer rows.Close()
	var models []Model
	for rows.Next() {
		model, args := meta.ScanInto()
		rows.Scan(args...)
		models = append(models, model)
	}
	return models
}

// TODO better key solution
func (o ModelIDAO) FindModel(pModelName ModelName, pKeys ...interface{}) Model {
	meta := o.gem.allModelsMetadata[pModelName]
	stmt := meta.preparedStatements[find]
	row := stmt.QueryRow(pKeys...)
	model, args := meta.ScanInto()
	err := row.Scan(args...)
	if err != nil {
		//TODO determine how errors should be handled
		fmt.Println(err)
		return nil
	}
	return model
}

func (o *ModelIDAO) SqlBuilder(pModelName ModelName) *SqlBuilder {
	meta := o.gem.allModelsMetadata[pModelName]
	builder := new(SqlBuilder)
	builder.ModelMetadata = &meta
	builder.Dialect = o.gem.Dialect
	return builder
}

// TODO find why insert prepared statement does not work
// TODO fix persis
// gets a primary key unique constraint error
func (o *ModelIDAO) Insert(pModel Model) Result {
	if o.gem.tx == nil {
		// TODO remove?
		builder := o.SqlBuilder(pModel.ModelName()).Insert().Values()
		fPre, fPost := insertHooks(pModel)
		if fPre != nil {
			err := fPre()
			if err != nil {
				return Result{nil, err}
			}
		}
		result, err := o.gem.Exec(builder, insertArgs(pModel)...)
		if err != nil {
			return Result{result, err}
		}
		// TODO dialect for Id
		if id, err := result.LastInsertId(); err == nil {
			// TODO compound key
			v, ok := pModel.Keys()[0].(*AutoIncrement)
			if ok {
				v.A(id)
			}
			v2, ok := pModel.Keys()[0].(*Int64)
			if ok {
				v2.A(id)
			}
		}
		if fPost != nil {
			err := fPost()
			if err != nil {
				return Result{nil, err}
			}
		}
		return Result{result, err}
	}
	return persist(o, pModel)
}

func (o *ModelIDAO) Save(pModel Model) Result {
	return merge(o, pModel)
}

func (o *ModelIDAO) Delete(pModel Model) Result {
	return remove(o, pModel)
}

func (o *ModelIDAO) ExecorStmt(pModelName ModelName, pNamedStmt string) *sql.Stmt {
	// TODO handle disconnections
	stmt := o.gem.allModelsMetadata[pModelName].preparedStatements[pNamedStmt]
	if o.gem.tx == nil {
		return stmt
	}
	return o.gem.tx.stmt(stmt)
}

// Future type for using when the opal sql has more of its own nuances
type OpalSql string

func (o OpalSql) String() string {
	return string(o)
}

type Rows struct {
	*sql.Rows // TODO explore embedded is wasteful?
}

type Sql interface {
	String() string
}

type StartArgs struct {
	BaseModel BaseModel
	DB *sql.DB
	Dialect   Dialect
	CreateEntity func(ModelName) Entity
	Id *OpalMagic
}

func GEM(o StartArgs) *Gem {
	// TODO panic on nil options
	gem := new(Gem)
	gem.Dialect = o.Dialect
	gem.dao = &ModelIDAO{gem: gem}
	gem.DB = o.DB
	gem.funcCreateDomainEntity = o.CreateEntity

	SetMagic(o.Id)
	if gem.funcCreateDomainEntity == nil {
		gem.funcCreateDomainEntity = NewEntity
	}
	models := o.BaseModel.Models()
	gem.allModelsMetadata = make(map[ModelName]ModelMetadata, len(models))
	gem.allModelsEntity = make(map[ModelName]*Entity, len(models))
	gem.txPreparedStatements = make(map[*sql.Stmt]*sql.Stmt)
	currentGem = gem
	for _, face := range models {
		model, ok := face.(Model)
		if !ok {
			panic("Opal.Start: You cannot pass a type which does not implement the Model interface.")
		}
		// TODO option for fuller path name
		t := reflect.TypeOf(model).Elem()

		// Create the ModelMetadata and gather the
		// table and column information
		meta := NewMetadata(model, t)

		// Gather the metadata and save into the ModelMetadata holder
		modelName, entity, modelDAO := model.Gather(meta) // TODO somehow detach Gather from model and initialise another way

		// Add the ModelName to the map for retrieving metadata
		gem.modelNames = append(gem.modelNames, modelName)
		gem.allModelsMetadata[modelName] = *meta
		gem.allModelsEntity[modelName] = entity

		// Inject OpalDAOs into Model DAOs
		// TODO report
		modelDAO(gem.dao)

		// Save an entity instance into the provided address
		*gem.allModelsEntity[modelName] = gem.funcCreateDomainEntity(modelName)

		// Generate prepared statements
		builder := gem.dao.SqlBuilder(modelName)

		// Create tables if necessary
		table := builder.Create().Sql()
		log.Printf("Opal.Start: Create table statement: %s", table.String())
		gem.Exec(table)

		meta.addStmt(gem.DB, findAll, builder.Select().Sql())
		meta.addStmt(gem.DB, find, builder.Select().WherePk().Sql())
		meta.addStmt(gem.DB, insert, builder.Insert().Values().Sql())
		meta.addStmt(gem.DB, update, builder.Update().WherePk().Sql())
		meta.addStmt(gem.DB, delete, builder.Delete().WherePk().Sql())
	}
	return currentGem
}

// Base Model statement names
const (
	find    = "find"
	findAll = "findAll"
	insert  = "insert"
	update  = "update"
	delete  = "delete"
)

type SqlBuilderDialectEncoder func(*SqlBuilder, DialectEncoder) *SqlBuilder

type ModifyDB (func(ModelName, Model) (Result, error))

// *********************************************  SPECIAL TYPES

type PreparedQuery interface {
}

type ModelQueries interface {
	NamedQueries() []PreparedQuery
	DerivedQueries() []PreparedQuery
}

type Validation interface {
}

<commit_msg>Version change<commit_after>/**
 * Date: 8/02/14
 * Time: 11:22 AM
 */
package opal

import (
	"database/sql"
	"fmt"
	"github.com/twinj/version"
	"log"
	"reflect"
)

const (
	// Group magic number
	group = 0xAA7E5441

	// Magic number specific to Opal
	opalMagic = 0x88280BA1

	// Version information
	release   = 1
	iteration = 0
	revision  = 0
	api       = 1
)

var (
	// The current instance - instances can be swapped out
	// TODO create a package only handle set by the user
	currentGem *Gem
)

func init() {
	version.Init(group, opalMagic, release, iteration, revision, api, "OPAL")
}

// Copy a Gem into the packages address to be used as the current service
func SwitchGem(pGem Gem) bool {
	*currentGem = pGem
	return true
}

// Retrieve a copy of Gem
func GetGem() Gem {
	return *currentGem
}

// ******************************************** Data Access

// ActiveRecordDAO acts as a data provider for a Model's Entity.
// It is a limited version of the full Domain access object.
type ActiveRecordDAO interface {

	// Takes a Model saves it as a new data-store entity and
	// returns a result
	Insert(pModel Model) Result

	// Takes a Model, updates an existing entity from the
	// data-store and returns a result
	Save(pModel Model) Result

	// Takes a Model and removes an existing entity from the
	// data-store
	Delete(pModel Model) Result
}

// ModelDAO acts a data provider for a Model's domain
// Methods are
type ModelDAO interface {

	OPAL
	ActiveRecordDAO

	// Find all models within the domain
	FindAllModels(pModelName ModelName) []Model

	// Find a specific Model using its keys
	FindModel(pModelName ModelName, pKeys ...interface{}) Model

	// Create a Sql Builder for the specified Model
	SqlBuilder(pModelName ModelName) *SqlBuilder
}

// ModelIDAO Implements ModelDAO
type ModelIDAO struct {
	gem *Gem
}

// Opal partially implements the opal OPAL interface
func (ModelIDAO) opal() OpalMagic {
	return opal
}

// Opal partially implements the opal OPAL interface
func (ModelIDAO) Kind() reflect.Kind {
	return DAO
}

// TODO betterway to handle DAO
func (o ModelIDAO) Gem() Gem {
	return *o.gem
}

func (o ModelIDAO) FindAllModels(pModelName ModelName) []Model {
	meta := o.gem.allModelsMetadata[pModelName]
	// TODO what if lose connection
	stmt := meta.preparedStatements[findAll]
	rows, err := stmt.Query()
	if err != nil {
		log.Print(err)
		return nil // TODO handle err
	}
	defer rows.Close()
	var models []Model
	for rows.Next() {
		model, args := meta.ScanInto()
		rows.Scan(args...)
		models = append(models, model)
	}
	return models
}

// TODO better key solution
func (o ModelIDAO) FindModel(pModelName ModelName, pKeys ...interface{}) Model {
	meta := o.gem.allModelsMetadata[pModelName]
	stmt := meta.preparedStatements[find]
	row := stmt.QueryRow(pKeys...)
	model, args := meta.ScanInto()
	err := row.Scan(args...)
	if err != nil {
		//TODO determine how errors should be handled
		fmt.Println(err)
		return nil
	}
	return model
}

func (o *ModelIDAO) SqlBuilder(pModelName ModelName) *SqlBuilder {
	meta := o.gem.allModelsMetadata[pModelName]
	builder := new(SqlBuilder)
	builder.ModelMetadata = &meta
	builder.Dialect = o.gem.Dialect
	return builder
}

// TODO find why insert prepared statement does not work
// TODO fix persis
// gets a primary key unique constraint error
func (o *ModelIDAO) Insert(pModel Model) Result {
	if o.gem.tx == nil {
		// TODO remove?
		builder := o.SqlBuilder(pModel.ModelName()).Insert().Values()
		fPre, fPost := insertHooks(pModel)
		if fPre != nil {
			err := fPre()
			if err != nil {
				return Result{nil, err}
			}
		}
		result, err := o.gem.Exec(builder, insertArgs(pModel)...)
		if err != nil {
			return Result{result, err}
		}
		// TODO dialect for Id
		if id, err := result.LastInsertId(); err == nil {
			// TODO compound key
			v, ok := pModel.Keys()[0].(*AutoIncrement)
			if ok {
				v.A(id)
			}
			v2, ok := pModel.Keys()[0].(*Int64)
			if ok {
				v2.A(id)
			}
		}
		if fPost != nil {
			err := fPost()
			if err != nil {
				return Result{nil, err}
			}
		}
		return Result{result, err}
	}
	return persist(o, pModel)
}

func (o *ModelIDAO) Save(pModel Model) Result {
	return merge(o, pModel)
}

func (o *ModelIDAO) Delete(pModel Model) Result {
	return remove(o, pModel)
}

func (o *ModelIDAO) ExecorStmt(pModelName ModelName, pNamedStmt string) *sql.Stmt {
	// TODO handle disconnections
	stmt := o.gem.allModelsMetadata[pModelName].preparedStatements[pNamedStmt]
	if o.gem.tx == nil {
		return stmt
	}
	return o.gem.tx.stmt(stmt)
}

// Future type for using when the opal sql has more of its own nuances
type OpalSql string

func (o OpalSql) String() string {
	return string(o)
}

type Rows struct {
	*sql.Rows // TODO explore embedded is wasteful?
}

type Sql interface {
	String() string
}

type StartArgs struct {
	BaseModel BaseModel
	DB *sql.DB
	Dialect   Dialect
	CreateEntity func(ModelName) Entity
	Id *OpalMagic
}

func GEM(o StartArgs) *Gem {
	// TODO panic on nil options
	gem := new(Gem)
	gem.Dialect = o.Dialect
	gem.dao = &ModelIDAO{gem: gem}
	gem.DB = o.DB
	gem.funcCreateDomainEntity = o.CreateEntity

	SetMagic(o.Id)
	if gem.funcCreateDomainEntity == nil {
		gem.funcCreateDomainEntity = NewEntity
	}
	models := o.BaseModel.Models()
	gem.allModelsMetadata = make(map[ModelName]ModelMetadata, len(models))
	gem.allModelsEntity = make(map[ModelName]*Entity, len(models))
	gem.txPreparedStatements = make(map[*sql.Stmt]*sql.Stmt)
	currentGem = gem
	for _, face := range models {
		model, ok := face.(Model)
		if !ok {
			panic("Opal.Start: You cannot pass a type which does not implement the Model interface.")
		}
		// TODO option for fuller path name
		t := reflect.TypeOf(model).Elem()

		// Create the ModelMetadata and gather the
		// table and column information
		meta := NewMetadata(model, t)

		// Gather the metadata and save into the ModelMetadata holder
		modelName, entity, modelDAO := model.Gather(meta) // TODO somehow detach Gather from model and initialise another way

		// Add the ModelName to the map for retrieving metadata
		gem.modelNames = append(gem.modelNames, modelName)
		gem.allModelsMetadata[modelName] = *meta
		gem.allModelsEntity[modelName] = entity

		// Inject OpalDAOs into Model DAOs
		// TODO report
		modelDAO(gem.dao)

		// Save an entity instance into the provided address
		*gem.allModelsEntity[modelName] = gem.funcCreateDomainEntity(modelName)

		// Generate prepared statements
		builder := gem.dao.SqlBuilder(modelName)

		// Create tables if necessary
		table := builder.Create().Sql()
		log.Printf("Opal.Start: Create table statement: %s", table.String())
		gem.Exec(table)

		meta.addStmt(gem.DB, findAll, builder.Select().Sql())
		meta.addStmt(gem.DB, find, builder.Select().WherePk().Sql())
		meta.addStmt(gem.DB, insert, builder.Insert().Values().Sql())
		meta.addStmt(gem.DB, update, builder.Update().WherePk().Sql())
		meta.addStmt(gem.DB, delete, builder.Delete().WherePk().Sql())
	}
	return currentGem
}

// Base Model statement names
const (
	find    = "find"
	findAll = "findAll"
	insert  = "insert"
	update  = "update"
	delete  = "delete"
)

type SqlBuilderDialectEncoder func(*SqlBuilder, DialectEncoder) *SqlBuilder

type ModifyDB (func(ModelName, Model) (Result, error))

// *********************************************  SPECIAL TYPES

type PreparedQuery interface {
}

type ModelQueries interface {
	NamedQueries() []PreparedQuery
	DerivedQueries() []PreparedQuery
}

type Validation interface {
}

<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
	"os"
	"strconv"
	"strings"
	"sync"

	"github.com/gorilla/mux"
	"github.com/gorilla/websocket"
	"github.com/lxc/lxd/shared"
	"gopkg.in/lxc/go-lxc.v2"
)

func runCommand(container *lxc.Container, command []string, options lxc.AttachOptions) shared.OperationResult {
	status, err := container.RunCommandStatus(command, options)
	if err != nil {
		shared.Debugf("Failed running command: %q", err.Error())
		return shared.OperationError(err)
	}

	metadata, err := json.Marshal(shared.Jmap{"return": status})
	if err != nil {
		return shared.OperationError(err)
	}

	return shared.OperationResult{Metadata: metadata, Error: nil}
}

func (s *execWs) Metadata() interface{} {
	fds := shared.Jmap{}
	for fd, secret := range s.fds {
		if fd == -1 {
			fds["control"] = secret
		} else {
			fds[strconv.Itoa(fd)] = secret
		}
	}

	return shared.Jmap{"fds": fds}
}

func (s *execWs) Connect(secret string, r *http.Request, w http.ResponseWriter) error {
	for fd, fdSecret := range s.fds {
		if secret == fdSecret {
			conn, err := shared.WebsocketUpgrader.Upgrade(w, r, nil)
			if err != nil {
				return err
			}

			s.conns[fd] = conn

			if fd == -1 {
				s.controlConnected <- true
				return nil
			}

			for i, c := range s.conns {
				if i != -1 && c == nil {
					return nil
				}
			}
			s.allConnected <- true
			return nil
		}
	}

	/* If we didn't find the right secret, the user provided a bad one,
	 * which 403, not 404, since this operation actually exists */
	return os.ErrPermission
}

func (s *execWs) Do(id string) shared.OperationResult {
	<-s.allConnected

	var err error
	var ttys []*os.File
	var ptys []*os.File

	if s.interactive {
		ttys = make([]*os.File, 1)
		ptys = make([]*os.File, 1)
		ptys[0], ttys[0], err = shared.OpenPty(s.rootUid, s.rootGid)
		s.options.StdinFd = ttys[0].Fd()
		s.options.StdoutFd = ttys[0].Fd()
		s.options.StderrFd = ttys[0].Fd()
	} else {
		ttys = make([]*os.File, 3)
		ptys = make([]*os.File, 3)
		for i := 0; i < len(ttys); i++ {
			ptys[i], ttys[i], err = shared.Pipe()
			if err != nil {
				return shared.OperationError(err)
			}
		}
		s.options.StdinFd = ptys[0].Fd()
		s.options.StdoutFd = ttys[1].Fd()
		s.options.StderrFd = ttys[2].Fd()
	}

	controlExit := make(chan bool)
	var wgEOF sync.WaitGroup

	if s.interactive {
		wgEOF.Add(1)
		go func() {
			select {
			case <-s.controlConnected:
				break

			case <-controlExit:
				return
			}

			for {
				mt, r, err := s.conns[-1].NextReader()
				if mt == websocket.CloseMessage {
					break
				}

				if err != nil {
					shared.Debugf("Got error getting next reader %s", err)
					break
				}

				buf, err := ioutil.ReadAll(r)
				if err != nil {
					shared.Debugf("Failed to read message %s", err)
					break
				}

				command := shared.ContainerExecControl{}

				if err := json.Unmarshal(buf, &command); err != nil {
					shared.Debugf("Failed to unmarshal control socket command: %s", err)
					continue
				}

				if command.Command == "window-resize" {
					winchWidth, err := strconv.Atoi(command.Args["width"])
					if err != nil {
						shared.Debugf("Unable to extract window width: %s", err)
						continue
					}

					winchHeight, err := strconv.Atoi(command.Args["height"])
					if err != nil {
						shared.Debugf("Unable to extract window height: %s", err)
						continue
					}

					err = shared.SetSize(int(ptys[0].Fd()), winchWidth, winchHeight)
					if err != nil {
						shared.Debugf("Failed to set window size to: %dx%d", winchWidth, winchHeight)
						continue
					}
				}

				if err != nil {
					shared.Debugf("Got error writing to writer %s", err)
					break
				}
			}
		}()
		go func() {
			<-shared.WebsocketMirror(s.conns[0], ptys[0], ptys[0])
			wgEOF.Done()
		}()
	} else {
		wgEOF.Add(len(ttys) - 1)
		for i := 0; i < len(ttys); i++ {
			go func(i int) {
				if i == 0 {
					<-shared.WebsocketRecvStream(ttys[i], s.conns[i])
					ttys[i].Close()
				} else {
					<-shared.WebsocketSendStream(s.conns[i], ptys[i])
					ptys[i].Close()
					wgEOF.Done()
				}
			}(i)
		}
	}

	result := runCommand(
		s.container,
		s.command,
		s.options,
	)

	for _, tty := range ttys {
		tty.Close()
	}

	if s.interactive && s.conns[-1] == nil {
		controlExit <- true
	}

	wgEOF.Wait()

	for _, pty := range ptys {
		pty.Close()
	}

	return result
}

func containerExecPost(d *Daemon, r *http.Request) Response {
	name := mux.Vars(r)["name"]
	c, err := containerLXDLoad(d, name)
	if err != nil {
		return SmartError(err)
	}

	if !c.IsRunning() {
		return BadRequest(fmt.Errorf("Container is not running."))
	}

	if c.IsFrozen() {
		return BadRequest(fmt.Errorf("Container is frozen."))
	}

	post := commandPostContent{}
	buf, err := ioutil.ReadAll(r.Body)
	if err != nil {
		return BadRequest(err)
	}

	if err := json.Unmarshal(buf, &post); err != nil {
		return BadRequest(err)
	}

	opts := lxc.DefaultAttachOptions
	opts.ClearEnv = true
	opts.Env = []string{}

	for k, v := range c.Config() {
		if strings.HasPrefix(k, "environment.") {
			opts.Env = append(opts.Env, fmt.Sprintf("%s=%s", strings.TrimPrefix(k, "environment."), v))
		}
	}

	if post.Environment != nil {
		for k, v := range post.Environment {
			if k == "HOME" {
				opts.Cwd = v
			}
			opts.Env = append(opts.Env, fmt.Sprintf("%s=%s", k, v))
		}
	}

	if post.WaitForWS {
		ws := &execWs{}
		ws.fds = map[int]string{}
		idmapset := c.IdmapSet()
		if idmapset != nil {
			ws.rootUid, ws.rootGid = idmapset.ShiftIntoNs(0, 0)
		}
		ws.conns = map[int]*websocket.Conn{}
		ws.conns[-1] = nil
		ws.conns[0] = nil
		if !post.Interactive {
			ws.conns[1] = nil
			ws.conns[2] = nil
		}
		ws.allConnected = make(chan bool, 1)
		ws.controlConnected = make(chan bool, 1)
		ws.interactive = post.Interactive
		ws.done = make(chan shared.OperationResult, 1)
		ws.options = opts
		for i := -1; i < len(ws.conns)-1; i++ {
			ws.fds[i], err = shared.RandomCryptoString()
			if err != nil {
				return InternalError(err)
			}
		}

		ws.command = post.Command
		ws.container = c.LXContainerGet()

		return AsyncResponseWithWs(ws, nil)
	}

	run := func(id string) shared.OperationResult {

		nullDev, err := os.OpenFile(os.DevNull, os.O_RDWR, 0666)
		if err != nil {
			return shared.OperationError(err)
		}
		defer nullDev.Close()
		nullfd := nullDev.Fd()

		opts.StdinFd = nullfd
		opts.StdoutFd = nullfd
		opts.StderrFd = nullfd

		return runCommand(c.LXContainerGet(), post.Command, opts)
	}

	return AsyncResponse(run, nil)
}
<commit_msg>close exec's control connection on cmd finish<commit_after>package main

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
	"os"
	"strconv"
	"strings"
	"sync"

	"github.com/gorilla/mux"
	"github.com/gorilla/websocket"
	"github.com/lxc/lxd/shared"
	"gopkg.in/lxc/go-lxc.v2"
)

func runCommand(container *lxc.Container, command []string, options lxc.AttachOptions) shared.OperationResult {
	status, err := container.RunCommandStatus(command, options)
	if err != nil {
		shared.Debugf("Failed running command: %q", err.Error())
		return shared.OperationError(err)
	}

	metadata, err := json.Marshal(shared.Jmap{"return": status})
	if err != nil {
		return shared.OperationError(err)
	}

	return shared.OperationResult{Metadata: metadata, Error: nil}
}

func (s *execWs) Metadata() interface{} {
	fds := shared.Jmap{}
	for fd, secret := range s.fds {
		if fd == -1 {
			fds["control"] = secret
		} else {
			fds[strconv.Itoa(fd)] = secret
		}
	}

	return shared.Jmap{"fds": fds}
}

func (s *execWs) Connect(secret string, r *http.Request, w http.ResponseWriter) error {
	for fd, fdSecret := range s.fds {
		if secret == fdSecret {
			conn, err := shared.WebsocketUpgrader.Upgrade(w, r, nil)
			if err != nil {
				return err
			}

			s.conns[fd] = conn

			if fd == -1 {
				s.controlConnected <- true
				return nil
			}

			for i, c := range s.conns {
				if i != -1 && c == nil {
					return nil
				}
			}
			s.allConnected <- true
			return nil
		}
	}

	/* If we didn't find the right secret, the user provided a bad one,
	 * which 403, not 404, since this operation actually exists */
	return os.ErrPermission
}

func (s *execWs) Do(id string) shared.OperationResult {
	<-s.allConnected

	var err error
	var ttys []*os.File
	var ptys []*os.File

	if s.interactive {
		ttys = make([]*os.File, 1)
		ptys = make([]*os.File, 1)
		ptys[0], ttys[0], err = shared.OpenPty(s.rootUid, s.rootGid)
		s.options.StdinFd = ttys[0].Fd()
		s.options.StdoutFd = ttys[0].Fd()
		s.options.StderrFd = ttys[0].Fd()
	} else {
		ttys = make([]*os.File, 3)
		ptys = make([]*os.File, 3)
		for i := 0; i < len(ttys); i++ {
			ptys[i], ttys[i], err = shared.Pipe()
			if err != nil {
				return shared.OperationError(err)
			}
		}
		s.options.StdinFd = ptys[0].Fd()
		s.options.StdoutFd = ttys[1].Fd()
		s.options.StderrFd = ttys[2].Fd()
	}

	controlExit := make(chan bool)
	var wgEOF sync.WaitGroup

	if s.interactive {
		wgEOF.Add(1)
		go func() {
			select {
			case <-s.controlConnected:
				break

			case <-controlExit:
				return
			}

			for {
				mt, r, err := s.conns[-1].NextReader()
				if mt == websocket.CloseMessage {
					break
				}

				if err != nil {
					shared.Debugf("Got error getting next reader %s", err)
					break
				}

				buf, err := ioutil.ReadAll(r)
				if err != nil {
					shared.Debugf("Failed to read message %s", err)
					break
				}

				command := shared.ContainerExecControl{}

				if err := json.Unmarshal(buf, &command); err != nil {
					shared.Debugf("Failed to unmarshal control socket command: %s", err)
					continue
				}

				if command.Command == "window-resize" {
					winchWidth, err := strconv.Atoi(command.Args["width"])
					if err != nil {
						shared.Debugf("Unable to extract window width: %s", err)
						continue
					}

					winchHeight, err := strconv.Atoi(command.Args["height"])
					if err != nil {
						shared.Debugf("Unable to extract window height: %s", err)
						continue
					}

					err = shared.SetSize(int(ptys[0].Fd()), winchWidth, winchHeight)
					if err != nil {
						shared.Debugf("Failed to set window size to: %dx%d", winchWidth, winchHeight)
						continue
					}
				}

				if err != nil {
					shared.Debugf("Got error writing to writer %s", err)
					break
				}
			}
		}()
		go func() {
			<-shared.WebsocketMirror(s.conns[0], ptys[0], ptys[0])
			wgEOF.Done()
		}()
	} else {
		wgEOF.Add(len(ttys) - 1)
		for i := 0; i < len(ttys); i++ {
			go func(i int) {
				if i == 0 {
					<-shared.WebsocketRecvStream(ttys[i], s.conns[i])
					ttys[i].Close()
				} else {
					<-shared.WebsocketSendStream(s.conns[i], ptys[i])
					ptys[i].Close()
					wgEOF.Done()
				}
			}(i)
		}
	}

	result := runCommand(
		s.container,
		s.command,
		s.options,
	)

	for _, tty := range ttys {
		tty.Close()
	}

	if s.conns[-1] == nil {
		if s.interactive {
			controlExit <- true
		}
	} else {
		s.conns[-1].Close()
	}

	wgEOF.Wait()

	for _, pty := range ptys {
		pty.Close()
	}

	return result
}

func containerExecPost(d *Daemon, r *http.Request) Response {
	name := mux.Vars(r)["name"]
	c, err := containerLXDLoad(d, name)
	if err != nil {
		return SmartError(err)
	}

	if !c.IsRunning() {
		return BadRequest(fmt.Errorf("Container is not running."))
	}

	if c.IsFrozen() {
		return BadRequest(fmt.Errorf("Container is frozen."))
	}

	post := commandPostContent{}
	buf, err := ioutil.ReadAll(r.Body)
	if err != nil {
		return BadRequest(err)
	}

	if err := json.Unmarshal(buf, &post); err != nil {
		return BadRequest(err)
	}

	opts := lxc.DefaultAttachOptions
	opts.ClearEnv = true
	opts.Env = []string{}

	for k, v := range c.Config() {
		if strings.HasPrefix(k, "environment.") {
			opts.Env = append(opts.Env, fmt.Sprintf("%s=%s", strings.TrimPrefix(k, "environment."), v))
		}
	}

	if post.Environment != nil {
		for k, v := range post.Environment {
			if k == "HOME" {
				opts.Cwd = v
			}
			opts.Env = append(opts.Env, fmt.Sprintf("%s=%s", k, v))
		}
	}

	if post.WaitForWS {
		ws := &execWs{}
		ws.fds = map[int]string{}
		idmapset := c.IdmapSet()
		if idmapset != nil {
			ws.rootUid, ws.rootGid = idmapset.ShiftIntoNs(0, 0)
		}
		ws.conns = map[int]*websocket.Conn{}
		ws.conns[-1] = nil
		ws.conns[0] = nil
		if !post.Interactive {
			ws.conns[1] = nil
			ws.conns[2] = nil
		}
		ws.allConnected = make(chan bool, 1)
		ws.controlConnected = make(chan bool, 1)
		ws.interactive = post.Interactive
		ws.done = make(chan shared.OperationResult, 1)
		ws.options = opts
		for i := -1; i < len(ws.conns)-1; i++ {
			ws.fds[i], err = shared.RandomCryptoString()
			if err != nil {
				return InternalError(err)
			}
		}

		ws.command = post.Command
		ws.container = c.LXContainerGet()

		return AsyncResponseWithWs(ws, nil)
	}

	run := func(id string) shared.OperationResult {

		nullDev, err := os.OpenFile(os.DevNull, os.O_RDWR, 0666)
		if err != nil {
			return shared.OperationError(err)
		}
		defer nullDev.Close()
		nullfd := nullDev.Fd()

		opts.StdinFd = nullfd
		opts.StdoutFd = nullfd
		opts.StderrFd = nullfd

		return runCommand(c.LXContainerGet(), post.Command, opts)
	}

	return AsyncResponse(run, nil)
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"testing"

	"github.com/stretchr/testify/suite"

	"github.com/lxc/lxd/lxd/db"
	"github.com/lxc/lxd/lxd/device/config"
	driver "github.com/lxc/lxd/lxd/storage"
	"github.com/lxc/lxd/shared"
	"github.com/lxc/lxd/shared/api"
	"github.com/lxc/lxd/shared/idmap"
)

type containerTestSuite struct {
	lxdTestSuite
}

func (suite *containerTestSuite) TestContainer_ProfilesDefault() {
	args := db.ContainerArgs{
		Ctype:     db.CTypeRegular,
		Ephemeral: false,
		Name:      "testFoo",
	}

	c, err := containerCreateInternal(suite.d.State(), args)
	suite.Req.Nil(err)
	defer c.Delete()

	profiles := c.Profiles()
	suite.Len(
		profiles,
		1,
		"No default profile created on containerCreateInternal.")

	suite.Equal(
		"default",
		profiles[0],
		"First profile should be the default profile.")
}

func (suite *containerTestSuite) TestContainer_ProfilesMulti() {
	// Create an unprivileged profile
	err := suite.d.cluster.Transaction(func(tx *db.ClusterTx) error {
		profile := db.Profile{
			Name:        "unprivileged",
			Description: "unprivileged",
			Config:      map[string]string{"security.privileged": "true"},
			Devices:     config.Devices{},
			Project:     "default",
		}
		_, err := tx.ProfileCreate(profile)
		return err
	})

	suite.Req.Nil(err, "Failed to create the unprivileged profile.")
	defer func() {
		suite.d.cluster.Transaction(func(tx *db.ClusterTx) error {
			return tx.ProfileDelete("default", "unpriviliged")
		})
	}()

	args := db.ContainerArgs{
		Ctype:     db.CTypeRegular,
		Ephemeral: false,
		Profiles:  []string{"default", "unprivileged"},
		Name:      "testFoo",
	}

	c, err := containerCreateInternal(suite.d.State(), args)
	suite.Req.Nil(err)
	defer c.Delete()

	profiles := c.Profiles()
	suite.Len(
		profiles,
		2,
		"Didn't get both profiles in containerCreateInternal.")

	suite.True(
		c.IsPrivileged(),
		"The container is not privileged (didn't apply the unprivileged profile?).")
}

func (suite *containerTestSuite) TestContainer_ProfilesOverwriteDefaultNic() {
	args := db.ContainerArgs{
		Ctype:     db.CTypeRegular,
		Ephemeral: false,
		Config:    map[string]string{"security.privileged": "true"},
		Devices: config.Devices{
			"eth0": config.Device{
				"type":    "nic",
				"nictype": "bridged",
				"parent":  "unknownbr0"}},
		Name: "testFoo",
	}

	c, err := containerCreateInternal(suite.d.State(), args)
	suite.Req.Nil(err)

	suite.True(c.IsPrivileged(), "This container should be privileged.")

	out, _, err := c.Render()
	suite.Req.Nil(err)

	state := out.(*api.Container)
	defer c.Delete()

	suite.Equal(
		"unknownbr0",
		state.Devices["eth0"]["parent"],
		"Container config doesn't overwrite profile config.")
}

func (suite *containerTestSuite) TestContainer_LoadFromDB() {
	args := db.ContainerArgs{
		Ctype:     db.CTypeRegular,
		Ephemeral: false,
		Config:    map[string]string{"security.privileged": "true"},
		Devices: config.Devices{
			"eth0": config.Device{
				"type":    "nic",
				"nictype": "bridged",
				"parent":  "unknownbr0"}},
		Name: "testFoo",
	}

	// Create the container
	c, err := containerCreateInternal(suite.d.State(), args)
	suite.Req.Nil(err)
	defer c.Delete()

	// Load the container and trigger initLXC()
	c2, err := containerLoadByProjectAndName(suite.d.State(), "default", "testFoo")
	c2.IsRunning()
	suite.Req.Nil(err)
	_, err = c2.StorageStart()
	suite.Req.Nil(err)

	// When loading from DB, we won't have a full LXC config
	c.(*containerLXC).c = nil
	c.(*containerLXC).cConfig = false
	c2.(*containerLXC).c = nil
	c2.(*containerLXC).cConfig = false

	suite.Exactly(
		c,
		c2,
		"The loaded container isn't excactly the same as the created one.")
}

func (suite *containerTestSuite) TestContainer_Path_Regular() {
	// Regular
	args := db.ContainerArgs{
		Ctype:     db.CTypeRegular,
		Ephemeral: false,
		Name:      "testFoo",
	}

	c, err := containerCreateInternal(suite.d.State(), args)
	suite.Req.Nil(err)
	defer c.Delete()

	suite.Req.False(c.IsSnapshot(), "Shouldn't be a snapshot.")
	suite.Req.Equal(shared.VarPath("containers", "testFoo"), c.Path())
	suite.Req.Equal(shared.VarPath("containers", "testFoo2"), driver.ContainerPath("testFoo2", false))
}

func (suite *containerTestSuite) TestContainer_Path_Snapshot() {
	// Snapshot
	args := db.ContainerArgs{
		Ctype:     db.CTypeSnapshot,
		Ephemeral: false,
		Name:      "test/snap0",
	}

	c, err := containerCreateInternal(suite.d.State(), args)
	suite.Req.Nil(err)
	defer c.Delete()

	suite.Req.True(c.IsSnapshot(), "Should be a snapshot.")
	suite.Req.Equal(
		shared.VarPath("snapshots", "test", "snap0"),
		c.Path())
	suite.Req.Equal(
		shared.VarPath("snapshots", "test", "snap1"),
		driver.ContainerPath("test/snap1", true))
}

func (suite *containerTestSuite) TestContainer_LogPath() {
	args := db.ContainerArgs{
		Ctype:     db.CTypeRegular,
		Ephemeral: false,
		Name:      "testFoo",
	}

	c, err := containerCreateInternal(suite.d.State(), args)
	suite.Req.Nil(err)
	defer c.Delete()

	suite.Req.Equal(shared.VarPath("logs", "testFoo"), c.LogPath())
}

func (suite *containerTestSuite) TestContainer_IsPrivileged_Privileged() {
	args := db.ContainerArgs{
		Ctype:     db.CTypeRegular,
		Ephemeral: false,
		Config:    map[string]string{"security.privileged": "true"},
		Name:      "testFoo",
	}

	c, err := containerCreateInternal(suite.d.State(), args)
	suite.Req.Nil(err)

	suite.Req.True(c.IsPrivileged(), "This container should be privileged.")
	suite.Req.Nil(c.Delete(), "Failed to delete the container.")
}

func (suite *containerTestSuite) TestContainer_IsPrivileged_Unprivileged() {
	args := db.ContainerArgs{
		Ctype:     db.CTypeRegular,
		Ephemeral: false,
		Config:    map[string]string{"security.privileged": "false"},
		Name:      "testFoo",
	}

	c, err := containerCreateInternal(suite.d.State(), args)
	suite.Req.Nil(err)

	suite.Req.False(c.IsPrivileged(), "This container should be unprivileged.")
	suite.Req.Nil(c.Delete(), "Failed to delete the container.")
}

func (suite *containerTestSuite) TestContainer_Rename() {
	args := db.ContainerArgs{
		Ctype:     db.CTypeRegular,
		Ephemeral: false,
		Name:      "testFoo",
	}

	c, err := containerCreateInternal(suite.d.State(), args)
	suite.Req.Nil(err)
	defer c.Delete()

	suite.Req.Nil(c.Rename("testFoo2"), "Failed to rename the container.")
	suite.Req.Equal(shared.VarPath("containers", "testFoo2"), c.Path())
}

func (suite *containerTestSuite) TestContainer_findIdmap_isolated() {
	c1, err := containerCreateInternal(suite.d.State(), db.ContainerArgs{
		Ctype: db.CTypeRegular,
		Name:  "isol-1",
		Config: map[string]string{
			"security.idmap.isolated": "true",
		},
	})
	suite.Req.Nil(err)
	defer c1.Delete()

	c2, err := containerCreateInternal(suite.d.State(), db.ContainerArgs{
		Ctype: db.CTypeRegular,
		Name:  "isol-2",
		Config: map[string]string{
			"security.idmap.isolated": "true",
		},
	})
	suite.Req.Nil(err)
	defer c2.Delete()

	map1, err := c1.(*containerLXC).NextIdmap()
	suite.Req.Nil(err)
	map2, err := c2.(*containerLXC).NextIdmap()
	suite.Req.Nil(err)

	host := suite.d.os.IdmapSet.Idmap[0]

	for i := 0; i < 2; i++ {
		suite.Req.Equal(host.Hostid+65536, map1.Idmap[i].Hostid, "hostids don't match %d", i)
		suite.Req.Equal(int64(0), map1.Idmap[i].Nsid, "nsid nonzero")
		suite.Req.Equal(int64(65536), map1.Idmap[i].Maprange, "incorrect maprange")
	}

	for i := 0; i < 2; i++ {
		suite.Req.Equal(host.Hostid+65536*2, map2.Idmap[i].Hostid, "hostids don't match")
		suite.Req.Equal(int64(0), map2.Idmap[i].Nsid, "nsid nonzero")
		suite.Req.Equal(int64(65536), map2.Idmap[i].Maprange, "incorrect maprange")
	}
}

func (suite *containerTestSuite) TestContainer_findIdmap_mixed() {
	c1, err := containerCreateInternal(suite.d.State(), db.ContainerArgs{
		Ctype: db.CTypeRegular,
		Name:  "isol-1",
		Config: map[string]string{
			"security.idmap.isolated": "false",
		},
	})
	suite.Req.Nil(err)
	defer c1.Delete()

	c2, err := containerCreateInternal(suite.d.State(), db.ContainerArgs{
		Ctype: db.CTypeRegular,
		Name:  "isol-2",
		Config: map[string]string{
			"security.idmap.isolated": "true",
		},
	})
	suite.Req.Nil(err)
	defer c2.Delete()

	map1, err := c1.(*containerLXC).NextIdmap()
	suite.Req.Nil(err)
	map2, err := c2.(*containerLXC).NextIdmap()
	suite.Req.Nil(err)

	host := suite.d.os.IdmapSet.Idmap[0]

	for i := 0; i < 2; i++ {
		suite.Req.Equal(host.Hostid, map1.Idmap[i].Hostid, "hostids don't match %d", i)
		suite.Req.Equal(int64(0), map1.Idmap[i].Nsid, "nsid nonzero")
		suite.Req.Equal(host.Maprange, map1.Idmap[i].Maprange, "incorrect maprange")
	}

	for i := 0; i < 2; i++ {
		suite.Req.Equal(host.Hostid+65536, map2.Idmap[i].Hostid, "hostids don't match")
		suite.Req.Equal(int64(0), map2.Idmap[i].Nsid, "nsid nonzero")
		suite.Req.Equal(int64(65536), map2.Idmap[i].Maprange, "incorrect maprange")
	}
}

func (suite *containerTestSuite) TestContainer_findIdmap_raw() {
	c1, err := containerCreateInternal(suite.d.State(), db.ContainerArgs{
		Ctype: db.CTypeRegular,
		Name:  "isol-1",
		Config: map[string]string{
			"security.idmap.isolated": "false",
			"raw.idmap":               "both 1000 1000",
		},
	})
	suite.Req.Nil(err)
	defer c1.Delete()

	map1, err := c1.(*containerLXC).NextIdmap()
	suite.Req.Nil(err)

	host := suite.d.os.IdmapSet.Idmap[0]

	for _, i := range []int{0, 3} {
		suite.Req.Equal(host.Hostid, map1.Idmap[i].Hostid, "hostids don't match")
		suite.Req.Equal(int64(0), map1.Idmap[i].Nsid, "nsid nonzero")
		suite.Req.Equal(int64(1000), map1.Idmap[i].Maprange, "incorrect maprange")
	}

	for _, i := range []int{1, 4} {
		suite.Req.Equal(int64(1000), map1.Idmap[i].Hostid, "hostids don't match")
		suite.Req.Equal(int64(1000), map1.Idmap[i].Nsid, "invalid nsid")
		suite.Req.Equal(int64(1), map1.Idmap[i].Maprange, "incorrect maprange")
	}

	for _, i := range []int{2, 5} {
		suite.Req.Equal(host.Hostid+1001, map1.Idmap[i].Hostid, "hostids don't match")
		suite.Req.Equal(int64(1001), map1.Idmap[i].Nsid, "invalid nsid")
		suite.Req.Equal(host.Maprange-1000-1, map1.Idmap[i].Maprange, "incorrect maprange")
	}
}

func (suite *containerTestSuite) TestContainer_findIdmap_maxed() {
	maps := []*idmap.IdmapSet{}

	for i := 0; i < 7; i++ {
		c, err := containerCreateInternal(suite.d.State(), db.ContainerArgs{
			Ctype: db.CTypeRegular,
			Name:  fmt.Sprintf("isol-%d", i),
			Config: map[string]string{
				"security.idmap.isolated": "true",
			},
		})

		/* we should fail if there are no ids left */
		if i != 6 {
			suite.Req.Nil(err)
		} else {
			suite.Req.NotNil(err)
			return
		}

		defer c.Delete()

		m, err := c.(*containerLXC).NextIdmap()
		suite.Req.Nil(err)

		maps = append(maps, m)
	}

	for i, m1 := range maps {
		for j, m2 := range maps {
			if m1 == m2 {
				continue
			}

			for _, e := range m2.Idmap {
				suite.Req.False(m1.HostidsIntersect(e), "%d and %d's idmaps intersect %v %v", i, j, m1, m2)
			}
		}
	}
}

func TestContainerTestSuite(t *testing.T) {
	suite.Run(t, new(containerTestSuite))
}
<commit_msg>Remove legacy unit test making use of old snapshot apis<commit_after>package main

import (
	"fmt"
	"testing"

	"github.com/stretchr/testify/suite"

	"github.com/lxc/lxd/lxd/db"
	"github.com/lxc/lxd/lxd/device/config"
	driver "github.com/lxc/lxd/lxd/storage"
	"github.com/lxc/lxd/shared"
	"github.com/lxc/lxd/shared/api"
	"github.com/lxc/lxd/shared/idmap"
)

type containerTestSuite struct {
	lxdTestSuite
}

func (suite *containerTestSuite) TestContainer_ProfilesDefault() {
	args := db.ContainerArgs{
		Ctype:     db.CTypeRegular,
		Ephemeral: false,
		Name:      "testFoo",
	}

	c, err := containerCreateInternal(suite.d.State(), args)
	suite.Req.Nil(err)
	defer c.Delete()

	profiles := c.Profiles()
	suite.Len(
		profiles,
		1,
		"No default profile created on containerCreateInternal.")

	suite.Equal(
		"default",
		profiles[0],
		"First profile should be the default profile.")
}

func (suite *containerTestSuite) TestContainer_ProfilesMulti() {
	// Create an unprivileged profile
	err := suite.d.cluster.Transaction(func(tx *db.ClusterTx) error {
		profile := db.Profile{
			Name:        "unprivileged",
			Description: "unprivileged",
			Config:      map[string]string{"security.privileged": "true"},
			Devices:     config.Devices{},
			Project:     "default",
		}
		_, err := tx.ProfileCreate(profile)
		return err
	})

	suite.Req.Nil(err, "Failed to create the unprivileged profile.")
	defer func() {
		suite.d.cluster.Transaction(func(tx *db.ClusterTx) error {
			return tx.ProfileDelete("default", "unpriviliged")
		})
	}()

	args := db.ContainerArgs{
		Ctype:     db.CTypeRegular,
		Ephemeral: false,
		Profiles:  []string{"default", "unprivileged"},
		Name:      "testFoo",
	}

	c, err := containerCreateInternal(suite.d.State(), args)
	suite.Req.Nil(err)
	defer c.Delete()

	profiles := c.Profiles()
	suite.Len(
		profiles,
		2,
		"Didn't get both profiles in containerCreateInternal.")

	suite.True(
		c.IsPrivileged(),
		"The container is not privileged (didn't apply the unprivileged profile?).")
}

func (suite *containerTestSuite) TestContainer_ProfilesOverwriteDefaultNic() {
	args := db.ContainerArgs{
		Ctype:     db.CTypeRegular,
		Ephemeral: false,
		Config:    map[string]string{"security.privileged": "true"},
		Devices: config.Devices{
			"eth0": config.Device{
				"type":    "nic",
				"nictype": "bridged",
				"parent":  "unknownbr0"}},
		Name: "testFoo",
	}

	c, err := containerCreateInternal(suite.d.State(), args)
	suite.Req.Nil(err)

	suite.True(c.IsPrivileged(), "This container should be privileged.")

	out, _, err := c.Render()
	suite.Req.Nil(err)

	state := out.(*api.Container)
	defer c.Delete()

	suite.Equal(
		"unknownbr0",
		state.Devices["eth0"]["parent"],
		"Container config doesn't overwrite profile config.")
}

func (suite *containerTestSuite) TestContainer_LoadFromDB() {
	args := db.ContainerArgs{
		Ctype:     db.CTypeRegular,
		Ephemeral: false,
		Config:    map[string]string{"security.privileged": "true"},
		Devices: config.Devices{
			"eth0": config.Device{
				"type":    "nic",
				"nictype": "bridged",
				"parent":  "unknownbr0"}},
		Name: "testFoo",
	}

	// Create the container
	c, err := containerCreateInternal(suite.d.State(), args)
	suite.Req.Nil(err)
	defer c.Delete()

	// Load the container and trigger initLXC()
	c2, err := containerLoadByProjectAndName(suite.d.State(), "default", "testFoo")
	c2.IsRunning()
	suite.Req.Nil(err)
	_, err = c2.StorageStart()
	suite.Req.Nil(err)

	// When loading from DB, we won't have a full LXC config
	c.(*containerLXC).c = nil
	c.(*containerLXC).cConfig = false
	c2.(*containerLXC).c = nil
	c2.(*containerLXC).cConfig = false

	suite.Exactly(
		c,
		c2,
		"The loaded container isn't excactly the same as the created one.")
}

func (suite *containerTestSuite) TestContainer_Path_Regular() {
	// Regular
	args := db.ContainerArgs{
		Ctype:     db.CTypeRegular,
		Ephemeral: false,
		Name:      "testFoo",
	}

	c, err := containerCreateInternal(suite.d.State(), args)
	suite.Req.Nil(err)
	defer c.Delete()

	suite.Req.False(c.IsSnapshot(), "Shouldn't be a snapshot.")
	suite.Req.Equal(shared.VarPath("containers", "testFoo"), c.Path())
	suite.Req.Equal(shared.VarPath("containers", "testFoo2"), driver.ContainerPath("testFoo2", false))
}

func (suite *containerTestSuite) TestContainer_LogPath() {
	args := db.ContainerArgs{
		Ctype:     db.CTypeRegular,
		Ephemeral: false,
		Name:      "testFoo",
	}

	c, err := containerCreateInternal(suite.d.State(), args)
	suite.Req.Nil(err)
	defer c.Delete()

	suite.Req.Equal(shared.VarPath("logs", "testFoo"), c.LogPath())
}

func (suite *containerTestSuite) TestContainer_IsPrivileged_Privileged() {
	args := db.ContainerArgs{
		Ctype:     db.CTypeRegular,
		Ephemeral: false,
		Config:    map[string]string{"security.privileged": "true"},
		Name:      "testFoo",
	}

	c, err := containerCreateInternal(suite.d.State(), args)
	suite.Req.Nil(err)

	suite.Req.True(c.IsPrivileged(), "This container should be privileged.")
	suite.Req.Nil(c.Delete(), "Failed to delete the container.")
}

func (suite *containerTestSuite) TestContainer_IsPrivileged_Unprivileged() {
	args := db.ContainerArgs{
		Ctype:     db.CTypeRegular,
		Ephemeral: false,
		Config:    map[string]string{"security.privileged": "false"},
		Name:      "testFoo",
	}

	c, err := containerCreateInternal(suite.d.State(), args)
	suite.Req.Nil(err)

	suite.Req.False(c.IsPrivileged(), "This container should be unprivileged.")
	suite.Req.Nil(c.Delete(), "Failed to delete the container.")
}

func (suite *containerTestSuite) TestContainer_Rename() {
	args := db.ContainerArgs{
		Ctype:     db.CTypeRegular,
		Ephemeral: false,
		Name:      "testFoo",
	}

	c, err := containerCreateInternal(suite.d.State(), args)
	suite.Req.Nil(err)
	defer c.Delete()

	suite.Req.Nil(c.Rename("testFoo2"), "Failed to rename the container.")
	suite.Req.Equal(shared.VarPath("containers", "testFoo2"), c.Path())
}

func (suite *containerTestSuite) TestContainer_findIdmap_isolated() {
	c1, err := containerCreateInternal(suite.d.State(), db.ContainerArgs{
		Ctype: db.CTypeRegular,
		Name:  "isol-1",
		Config: map[string]string{
			"security.idmap.isolated": "true",
		},
	})
	suite.Req.Nil(err)
	defer c1.Delete()

	c2, err := containerCreateInternal(suite.d.State(), db.ContainerArgs{
		Ctype: db.CTypeRegular,
		Name:  "isol-2",
		Config: map[string]string{
			"security.idmap.isolated": "true",
		},
	})
	suite.Req.Nil(err)
	defer c2.Delete()

	map1, err := c1.(*containerLXC).NextIdmap()
	suite.Req.Nil(err)
	map2, err := c2.(*containerLXC).NextIdmap()
	suite.Req.Nil(err)

	host := suite.d.os.IdmapSet.Idmap[0]

	for i := 0; i < 2; i++ {
		suite.Req.Equal(host.Hostid+65536, map1.Idmap[i].Hostid, "hostids don't match %d", i)
		suite.Req.Equal(int64(0), map1.Idmap[i].Nsid, "nsid nonzero")
		suite.Req.Equal(int64(65536), map1.Idmap[i].Maprange, "incorrect maprange")
	}

	for i := 0; i < 2; i++ {
		suite.Req.Equal(host.Hostid+65536*2, map2.Idmap[i].Hostid, "hostids don't match")
		suite.Req.Equal(int64(0), map2.Idmap[i].Nsid, "nsid nonzero")
		suite.Req.Equal(int64(65536), map2.Idmap[i].Maprange, "incorrect maprange")
	}
}

func (suite *containerTestSuite) TestContainer_findIdmap_mixed() {
	c1, err := containerCreateInternal(suite.d.State(), db.ContainerArgs{
		Ctype: db.CTypeRegular,
		Name:  "isol-1",
		Config: map[string]string{
			"security.idmap.isolated": "false",
		},
	})
	suite.Req.Nil(err)
	defer c1.Delete()

	c2, err := containerCreateInternal(suite.d.State(), db.ContainerArgs{
		Ctype: db.CTypeRegular,
		Name:  "isol-2",
		Config: map[string]string{
			"security.idmap.isolated": "true",
		},
	})
	suite.Req.Nil(err)
	defer c2.Delete()

	map1, err := c1.(*containerLXC).NextIdmap()
	suite.Req.Nil(err)
	map2, err := c2.(*containerLXC).NextIdmap()
	suite.Req.Nil(err)

	host := suite.d.os.IdmapSet.Idmap[0]

	for i := 0; i < 2; i++ {
		suite.Req.Equal(host.Hostid, map1.Idmap[i].Hostid, "hostids don't match %d", i)
		suite.Req.Equal(int64(0), map1.Idmap[i].Nsid, "nsid nonzero")
		suite.Req.Equal(host.Maprange, map1.Idmap[i].Maprange, "incorrect maprange")
	}

	for i := 0; i < 2; i++ {
		suite.Req.Equal(host.Hostid+65536, map2.Idmap[i].Hostid, "hostids don't match")
		suite.Req.Equal(int64(0), map2.Idmap[i].Nsid, "nsid nonzero")
		suite.Req.Equal(int64(65536), map2.Idmap[i].Maprange, "incorrect maprange")
	}
}

func (suite *containerTestSuite) TestContainer_findIdmap_raw() {
	c1, err := containerCreateInternal(suite.d.State(), db.ContainerArgs{
		Ctype: db.CTypeRegular,
		Name:  "isol-1",
		Config: map[string]string{
			"security.idmap.isolated": "false",
			"raw.idmap":               "both 1000 1000",
		},
	})
	suite.Req.Nil(err)
	defer c1.Delete()

	map1, err := c1.(*containerLXC).NextIdmap()
	suite.Req.Nil(err)

	host := suite.d.os.IdmapSet.Idmap[0]

	for _, i := range []int{0, 3} {
		suite.Req.Equal(host.Hostid, map1.Idmap[i].Hostid, "hostids don't match")
		suite.Req.Equal(int64(0), map1.Idmap[i].Nsid, "nsid nonzero")
		suite.Req.Equal(int64(1000), map1.Idmap[i].Maprange, "incorrect maprange")
	}

	for _, i := range []int{1, 4} {
		suite.Req.Equal(int64(1000), map1.Idmap[i].Hostid, "hostids don't match")
		suite.Req.Equal(int64(1000), map1.Idmap[i].Nsid, "invalid nsid")
		suite.Req.Equal(int64(1), map1.Idmap[i].Maprange, "incorrect maprange")
	}

	for _, i := range []int{2, 5} {
		suite.Req.Equal(host.Hostid+1001, map1.Idmap[i].Hostid, "hostids don't match")
		suite.Req.Equal(int64(1001), map1.Idmap[i].Nsid, "invalid nsid")
		suite.Req.Equal(host.Maprange-1000-1, map1.Idmap[i].Maprange, "incorrect maprange")
	}
}

func (suite *containerTestSuite) TestContainer_findIdmap_maxed() {
	maps := []*idmap.IdmapSet{}

	for i := 0; i < 7; i++ {
		c, err := containerCreateInternal(suite.d.State(), db.ContainerArgs{
			Ctype: db.CTypeRegular,
			Name:  fmt.Sprintf("isol-%d", i),
			Config: map[string]string{
				"security.idmap.isolated": "true",
			},
		})

		/* we should fail if there are no ids left */
		if i != 6 {
			suite.Req.Nil(err)
		} else {
			suite.Req.NotNil(err)
			return
		}

		defer c.Delete()

		m, err := c.(*containerLXC).NextIdmap()
		suite.Req.Nil(err)

		maps = append(maps, m)
	}

	for i, m1 := range maps {
		for j, m2 := range maps {
			if m1 == m2 {
				continue
			}

			for _, e := range m2.Idmap {
				suite.Req.False(m1.HostidsIntersect(e), "%d and %d's idmaps intersect %v %v", i, j, m1, m2)
			}
		}
	}
}

func TestContainerTestSuite(t *testing.T) {
	suite.Run(t, new(containerTestSuite))
}
<|endoftext|>


<commit_before>// blakesum command calculates BLAKE-256 and BLAKE-224 checksum of files.
package main

import (
	"flag"
	"fmt"
	"github.com/dchest/blake256"
	"hash"
	"io"
	"os"
)

var is224 = flag.Bool("224", false, "Use BLAKE-224")

func calcSum(f *os.File) (sum []byte, err os.Error) {
	var h hash.Hash
	if *is224 {
		h = blake256.New224()
	} else {
		h = blake256.New()
	}
	_, err = io.Copy(h, f)
	sum = h.Sum()
	return
}

func main() {
	flag.Parse()
	if flag.NArg() == 0 {
		// Read from stdin.
		sum, err := calcSum(os.Stdin)
		if err != nil {
			fmt.Println(os.Stderr, "*** error reading from stdin")
			os.Exit(1)
		}
		fmt.Printf("%x\n", sum)
		os.Exit(0)
	}
	var hashname string
	if *is224 {
		hashname = "BLAKE-224"
	} else {
		hashname = "BLAKE-256"
	}
	exitNo := 0
	for i := 0; i < flag.NArg(); i++ {
		filename := flag.Arg(i)
		f, err := os.Open(filename)
		if err != nil {
			fmt.Fprintf(os.Stderr, "*** error opening %q\n", filename)
			exitNo = 1
			continue
		}
		sum, err := calcSum(f)
		f.Close()
		if err != nil {
			fmt.Fprintf(os.Stderr, "*** error reading %q\n", filename)
			exitNo = 1
			continue
		}
		fmt.Printf("%s (%s) = %x\n", hashname, filename, sum)
	}
	os.Exit(exitNo)
}
<commit_msg>Fix for Go weekly.2012-01-15.<commit_after>// blakesum command calculates BLAKE-256 and BLAKE-224 checksum of files.
package main

import (
	"flag"
	"fmt"
	"github.com/dchest/blake256"
	"hash"
	"io"
	"os"
)

var is224 = flag.Bool("224", false, "Use BLAKE-224")

func calcSum(f *os.File) (sum []byte, err error) {
	var h hash.Hash
	if *is224 {
		h = blake256.New224()
	} else {
		h = blake256.New()
	}
	_, err = io.Copy(h, f)
	sum = h.Sum(nil)
	return
}

func main() {
	flag.Parse()
	if flag.NArg() == 0 {
		// Read from stdin.
		sum, err := calcSum(os.Stdin)
		if err != nil {
			fmt.Println(os.Stderr, "*** error reading from stdin")
			os.Exit(1)
		}
		fmt.Printf("%x\n", sum)
		os.Exit(0)
	}
	var hashname string
	if *is224 {
		hashname = "BLAKE-224"
	} else {
		hashname = "BLAKE-256"
	}
	exitNo := 0
	for i := 0; i < flag.NArg(); i++ {
		filename := flag.Arg(i)
		f, err := os.Open(filename)
		if err != nil {
			fmt.Fprintf(os.Stderr, "*** error opening %q\n", filename)
			exitNo = 1
			continue
		}
		sum, err := calcSum(f)
		f.Close()
		if err != nil {
			fmt.Fprintf(os.Stderr, "*** error reading %q\n", filename)
			exitNo = 1
			continue
		}
		fmt.Printf("%s (%s) = %x\n", hashname, filename, sum)
	}
	os.Exit(exitNo)
}
<|endoftext|>


<commit_before>package blog

import (
	"fmt"
	"time"

	"github.com/gorilla/feeds"
	"github.com/hoisie/web"
	"github.com/jmoiron/monet/app"
	"github.com/jmoiron/monet/db"
	"github.com/jmoiron/monet/template"
	"labix.org/v2/mgo/bson"
)

var base = template.Base{Path: "base.mandira"}
var RssHref string
var AtomHref string

// Attach the blog app frontend
func Attach(url string) {
	web.Get(url+"blog/page/(\\d+)", blogPage)
	web.Get(url+"blog/([^/]+)/?", blogDetail)
	web.Get(url+"blog/", blogIndex)
	web.Get(url+"stream/page/(\\d+)", streamPage)
	web.Get(url+"stream/", streamIndex)
	web.Get(url+"blog/rss", rss)
	web.Get(url+"blog/atom", atom)

	RssHref = url + "blog/rss"
	AtomHref = url + "blog/atom"
}

// Render the post, using the cached ContentRendered if available, or generating
// and re-saving it to the database if not
func RenderPost(post *Post) string {
	if len(post.ContentRendered) == 0 {
		db.Upsert(post)
	}
	return template.Render("blog/post.mandira", post)
}

// A Flatpage view.  Attach it via web.Get wherever you want flatpages to be available
func Flatpage(ctx *web.Context, url string) string {
	p := GetPage(url)
	fmt.Printf("Got page %v for url %s\n", p, url)
	if p == nil {
		ctx.Abort(404, "Page not found")
		return ""
	}

	return template.Render("base.mandira", M{
		"body":        p.ContentRendered,
		"title":       "jmoiron.net",
		"description": "Blog and assorted media from Jason Moiron.",
	})
}

func Index() string {
	var post *Post
	var entry *Entry
	var posts []Post
	var entries []*Entry

	err := db.Latest(post, M{"published": 1}).Limit(7).All(&posts)
	if err != nil {
		fmt.Println(err)
	}
	err = db.Latest(entry, nil).Limit(4).All(&entries)
	if err != nil {
		fmt.Println(err)
	}

	post = &posts[0]
	return base.Render("index.mandira", M{
		"Post":        RenderPost(post),
		"Posts":       posts[1:],
		"Entries":     entries,
		"title":       "jmoiron.net",
		"description": post.Summary})
}

func blogIndex(ctx *web.Context) string {
	return blogPage(ctx, "1")
}

func blogPage(ctx *web.Context, page string) string {
	pn := app.PageNumber(page)
	perPage := 15
	paginator := app.NewPaginator(pn, perPage)
	paginator.Link = "/blog/page/"

	var post *Post
	var posts []Post
	// do a search, if required, of title and content
	var err error
	var numObjects int

	if len(ctx.Params["Search"]) > 0 {
		term := M{"$regex": ctx.Params["Search"]}
		search := M{"published": 1, "$or": []M{M{"title": term}, M{"content": term}}}
		err = db.Latest(post, search).Skip(paginator.Skip).Limit(perPage).All(&posts)
		numObjects, _ = db.Latest(post, search).Count()
	} else {
		err = db.Latest(post, M{"published": 1}).Skip(paginator.Skip).
			Limit(perPage).Iter().All(&posts)
		numObjects, _ = db.Find(post, M{"published": 1}).Count()
	}

	if err != nil {
		fmt.Println(err)
	}

	return base.Render("blog/index.mandira", M{
		"Rss":        RssHref,
		"Atom":       AtomHref,
		"Posts":      posts,
		"Pagination": paginator.Render(numObjects)}, ctx.Params)
}

func _createFeed() *feeds.Feed {
	var posts []Post
	var post *Post

	err := db.Latest(post, M{"published": 1}).Limit(10).Iter().All(&posts)
	if err != nil {
		fmt.Println(err)
		return nil
	}

	feed := &feeds.Feed{
		Title:       "jmoiron.net blog",
		Link:        &feeds.Link{Href: "http://jmoiron.net"},
		Description: "the blog of Jason Moiron, all thoughts his own",
		Author:      &feeds.Author{"Jason Moiron", "jmoiron@jmoiron.net"},
		Updated:     time.Now(),
	}

	for _, post := range posts {
		feed.Add(&feeds.Item{
			Title:       post.Title,
			Link:        &feeds.Link{Href: "http://jmoiron.net/blog/" + post.Slug + "/"},
			Description: post.ContentRendered,
			Created:     time.Unix(int64(post.Timestamp), 0),
		})
	}
	return feed
}

func atom(ctx *web.Context) string {
	feed := _createFeed()
	ctx.Header().Set("Content-Type", "application/xml")
	if feed == nil {
		return "<!-- error -->"
	}
	text, err := feed.ToAtom()
	if err != nil {
		fmt.Println(err)
		return "<!-- error -->"
	}
	return text
}

func rss(ctx *web.Context) string {
	feed := _createFeed()
	ctx.Header().Set("Content-Type", "application/xml")
	if feed == nil {
		return "<!-- error -->"
	}
	text, err := feed.ToRss()
	if err != nil {
		fmt.Println(err)
		return "<!-- error -->"
	}
	return text
}

func blogDetail(ctx *web.Context, slug string) string {
	var post = new(Post)
	err := db.Find(post, M{"slug": slug}).One(&post)
	if err != nil {
		fmt.Println(err)
		ctx.Abort(404, "Page not found")
		return ""
	}

	return template.Render("base.mandira", M{
		"Rss":         RssHref,
		"Atom":        AtomHref,
		"body":        RenderPost(post),
		"title":       post.Title,
		"description": post.Summary})
}

func streamIndex(ctx *web.Context) string {
	return streamPage(ctx, "1")
}

func streamPage(ctx *web.Context, page string) string {
	num := app.PageNumber(page)
	perPage := 25
	paginator := app.NewPaginator(num, perPage)
	paginator.Link = "/stream/page/"

	var entry *Entry
	var entries []*Entry

	// do a search, if required, of title and content
	var err error
	var numObjects int

	if len(ctx.Params["Search"]) > 0 {
		re := new(bson.RegEx)
		re.Pattern = ctx.Params["Search"]
		re.Options = "i"
		term := M{"$regex": re}
		search := M{"summaryrendered": term}
		//search := M{"$or": []M{M{"title": term}, M{"summaryrendered": term}}}
		err = db.Latest(entry, search).Skip(paginator.Skip).Limit(perPage).All(&entries)
		numObjects, _ = db.Latest(entry, search).Count()
	} else {
		err = db.Latest(entry, nil).Skip(paginator.Skip).Limit(perPage).Iter().All(&entries)
		numObjects, _ = db.Cursor(entry).Count()
	}

	if err != nil {
		fmt.Println(err)
	}

	return base.Render("blog/stream/index.mandira", M{
		"Entries":    entries,
		"Pagination": paginator.Render(numObjects),
		"title":      "Lifestream"}, ctx.Params)
}
<commit_msg>fix blog & atom links<commit_after>package blog

import (
	"fmt"
	"time"

	"github.com/gorilla/feeds"
	"github.com/hoisie/web"
	"github.com/jmoiron/monet/app"
	"github.com/jmoiron/monet/db"
	"github.com/jmoiron/monet/template"
	"labix.org/v2/mgo/bson"
)

var base = template.Base{Path: "base.mandira"}
var RssHref string
var AtomHref string

// Attach the blog app frontend
func Attach(url string) {
	web.Get(url+"blog/rss", rss)
	web.Get(url+"blog/atom", atom)
	web.Get(url+"blog/page/(\\d+)", blogPage)
	web.Get(url+"blog/([^/]+)/?", blogDetail)
	web.Get(url+"blog/", blogIndex)
	web.Get(url+"stream/page/(\\d+)", streamPage)
	web.Get(url+"stream/", streamIndex)

	RssHref = url + "blog/rss"
	AtomHref = url + "blog/atom"
}

// Render the post, using the cached ContentRendered if available, or generating
// and re-saving it to the database if not
func RenderPost(post *Post) string {
	if len(post.ContentRendered) == 0 {
		db.Upsert(post)
	}
	return template.Render("blog/post.mandira", post)
}

// A Flatpage view.  Attach it via web.Get wherever you want flatpages to be available
func Flatpage(ctx *web.Context, url string) string {
	p := GetPage(url)
	fmt.Printf("Got page %v for url %s\n", p, url)
	if p == nil {
		ctx.Abort(404, "Page not found")
		return ""
	}

	return template.Render("base.mandira", M{
		"body":        p.ContentRendered,
		"title":       "jmoiron.net",
		"description": "Blog and assorted media from Jason Moiron.",
	})
}

func Index() string {
	var post *Post
	var entry *Entry
	var posts []Post
	var entries []*Entry

	err := db.Latest(post, M{"published": 1}).Limit(7).All(&posts)
	if err != nil {
		fmt.Println(err)
	}
	err = db.Latest(entry, nil).Limit(4).All(&entries)
	if err != nil {
		fmt.Println(err)
	}

	post = &posts[0]
	return base.Render("index.mandira", M{
		"Post":        RenderPost(post),
		"Posts":       posts[1:],
		"Entries":     entries,
		"title":       "jmoiron.net",
		"description": post.Summary})
}

func blogIndex(ctx *web.Context) string {
	return blogPage(ctx, "1")
}

func blogPage(ctx *web.Context, page string) string {
	pn := app.PageNumber(page)
	perPage := 15
	paginator := app.NewPaginator(pn, perPage)
	paginator.Link = "/blog/page/"

	var post *Post
	var posts []Post
	// do a search, if required, of title and content
	var err error
	var numObjects int

	if len(ctx.Params["Search"]) > 0 {
		term := M{"$regex": ctx.Params["Search"]}
		search := M{"published": 1, "$or": []M{M{"title": term}, M{"content": term}}}
		err = db.Latest(post, search).Skip(paginator.Skip).Limit(perPage).All(&posts)
		numObjects, _ = db.Latest(post, search).Count()
	} else {
		err = db.Latest(post, M{"published": 1}).Skip(paginator.Skip).
			Limit(perPage).Iter().All(&posts)
		numObjects, _ = db.Find(post, M{"published": 1}).Count()
	}

	if err != nil {
		fmt.Println(err)
	}

	return base.Render("blog/index.mandira", M{
		"Rss":        RssHref,
		"Atom":       AtomHref,
		"Posts":      posts,
		"Pagination": paginator.Render(numObjects)}, ctx.Params)
}

func _createFeed() *feeds.Feed {
	var posts []Post
	var post *Post

	err := db.Latest(post, M{"published": 1}).Limit(10).Iter().All(&posts)
	if err != nil {
		fmt.Println(err)
		return nil
	}

	feed := &feeds.Feed{
		Title:       "jmoiron.net blog",
		Link:        &feeds.Link{Href: "http://jmoiron.net"},
		Description: "the blog of Jason Moiron, all thoughts his own",
		Author:      &feeds.Author{"Jason Moiron", "jmoiron@jmoiron.net"},
		Updated:     time.Now(),
	}

	for _, post := range posts {
		feed.Add(&feeds.Item{
			Title:       post.Title,
			Link:        &feeds.Link{Href: "http://jmoiron.net/blog/" + post.Slug + "/"},
			Description: post.ContentRendered,
			Created:     time.Unix(int64(post.Timestamp), 0),
		})
	}
	return feed
}

func atom(ctx *web.Context) string {
	feed := _createFeed()
	ctx.Header().Set("Content-Type", "application/xml")
	if feed == nil {
		return "<!-- error -->"
	}
	text, err := feed.ToAtom()
	if err != nil {
		fmt.Println(err)
		return "<!-- error -->"
	}
	return text
}

func rss(ctx *web.Context) string {
	feed := _createFeed()
	ctx.Header().Set("Content-Type", "application/xml")
	if feed == nil {
		return "<!-- error -->"
	}
	text, err := feed.ToRss()
	if err != nil {
		fmt.Println(err)
		return "<!-- error -->"
	}
	return text
}

func blogDetail(ctx *web.Context, slug string) string {
	var post = new(Post)
	err := db.Find(post, M{"slug": slug}).One(&post)
	if err != nil {
		fmt.Println(err)
		ctx.Abort(404, "Page not found")
		return ""
	}

	return template.Render("base.mandira", M{
		"Rss":         RssHref,
		"Atom":        AtomHref,
		"body":        RenderPost(post),
		"title":       post.Title,
		"description": post.Summary})
}

func streamIndex(ctx *web.Context) string {
	return streamPage(ctx, "1")
}

func streamPage(ctx *web.Context, page string) string {
	num := app.PageNumber(page)
	perPage := 25
	paginator := app.NewPaginator(num, perPage)
	paginator.Link = "/stream/page/"

	var entry *Entry
	var entries []*Entry

	// do a search, if required, of title and content
	var err error
	var numObjects int

	if len(ctx.Params["Search"]) > 0 {
		re := new(bson.RegEx)
		re.Pattern = ctx.Params["Search"]
		re.Options = "i"
		term := M{"$regex": re}
		search := M{"summaryrendered": term}
		//search := M{"$or": []M{M{"title": term}, M{"summaryrendered": term}}}
		err = db.Latest(entry, search).Skip(paginator.Skip).Limit(perPage).All(&entries)
		numObjects, _ = db.Latest(entry, search).Count()
	} else {
		err = db.Latest(entry, nil).Skip(paginator.Skip).Limit(perPage).Iter().All(&entries)
		numObjects, _ = db.Cursor(entry).Count()
	}

	if err != nil {
		fmt.Println(err)
	}

	return base.Render("blog/stream/index.mandira", M{
		"Entries":    entries,
		"Pagination": paginator.Render(numObjects),
		"title":      "Lifestream"}, ctx.Params)
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"github.com/ant512/gobble/akismet"
	"html"
	"io/ioutil"
	"log"
	"os"
	"path/filepath"
	"strconv"
	"strings"
	"sync"
	"time"
)

type BlogPostMetadata struct {
	Title            string
	Id               int
	Date             time.Time
	Tags             []string
	DisallowComments bool
}

type BlogPost struct {
	Metadata     BlogPostMetadata
	Body         BlogItemBody
	Comments     Comments
	PostPath     string
	CommentPath  string
	Url          string
	Filename     string
	ModifiedDate time.Time
	mutex        sync.RWMutex
}

func LoadPost(filename, postPath, commentPath string) (*BlogPost, error) {

	b := &BlogPost{}
	b.PostPath = postPath
	b.Filename = filename
	b.CommentPath = commentPath

	fullPath := filepath.Join(postPath, filename)

	err := loadBlogFile(fullPath, func(fileInfo os.FileInfo) {
		b.ModifiedDate = fileInfo.ModTime()
	}, func(key, value string) {
		switch key {
		case "title":
			b.Metadata.Title = value
		case "id":
			b.Metadata.Id, _ = strconv.Atoi(value)
		case "tags":

			tags := strings.Split(value, ",")

			formattedTags := []string{}

			for j := range tags {
				tags[j] = strings.TrimSpace(tags[j])
				tags[j] = strings.Replace(tags[j], " ", "-", -1)
				tags[j] = strings.Replace(tags[j], "/", "-", -1)
				tags[j] = strings.Replace(tags[j], "#", "", -1)
				tags[j] = strings.ToLower(tags[j])

				if tags[j] != "" {
					formattedTags = append(formattedTags, tags[j])
				}
			}

			b.Metadata.Tags = formattedTags
		case "date":
			b.Metadata.Date = stringToTime(value)
		case "disallowcomments":
			b.Metadata.DisallowComments = value == "true"
		default:
		}
	}, func(value string) {
		bytes := []byte(value)

		b.Body.Markdown = value
		b.Body.HTML = convertMarkdownToHtml(&bytes)
	})

	if err == nil {
		b.Url = b.urlFromBlogPostProperties()
		b.loadComments()
	} else {
		log.Println(err)
	}

	return b, err
}

func (b *BlogPost) NonSpamComments() Comments {
	comments := Comments{}

	for _, comment := range b.Comments {
		if !comment.Metadata.IsSpam {
			comments = append(comments, comment)
		}
	}

	return comments
}

func (b *BlogPost) ContainsTag(tag string) bool {
	for _, t := range b.Metadata.Tags {
		if t == strings.ToLower(tag) {
			return true
		}
	}

	return false
}

func (b *BlogPost) ContainsTerm(term string) bool {

	term = strings.ToLower(term)

	if b.ContainsTag(term) {
		return true
	}

	if b.Comments.ContainsTerm(term) {
		return true
	}

	if b.Body.ContainsTerm(term) {
		return true
	}

	terms := strings.Split(term, " ")
	title := strings.ToLower(b.Metadata.Title)

	for _, item := range terms {
		if !strings.Contains(title, item) {
			return false
		}
	}

	return true
}

func (b *BlogPost) AllowsComments() bool {
	if b.Metadata.DisallowComments {
		return false
	}

	if SharedConfig.CommentsOpenForDays == 0 {
		return true
	}

	var closeDate = b.Metadata.Date.Add(time.Hour * 24 * time.Duration(SharedConfig.CommentsOpenForDays))

	return time.Now().Before(closeDate)
}

func (b *BlogPost) SaveComment(akismetAPIKey, serverAddress, remoteAddress, userAgent, referrer, author, email, body string) {

	// TODO: Ensure file name is unique
	isSpam, _ := akismet.IsSpamComment(body, serverAddress, remoteAddress, userAgent, referrer, author, email, akismetAPIKey)
	comment := NewComment(html.EscapeString(author), html.EscapeString(email), html.EscapeString(body), isSpam)

	b.mutex.Lock()
	b.Comments = append(b.Comments, comment)
	b.mutex.Unlock()

	commentPath := filepath.Join(b.CommentPath, b.Filename[:len(b.Filename)-3])
	filename := timeToFilename(comment.Metadata.Date)
	fullPath := filepath.Join(commentPath, filename)

	log.Println(commentPath)
	os.MkdirAll(commentPath, 0775)

	content := comment.String()

	err := ioutil.WriteFile(fullPath, []byte(content), 0644)

	if err != nil {
		log.Println(err)
	}
}

func (b *BlogPost) urlFromBlogPostProperties() string {
	title := strings.ToLower(b.Metadata.Title)
	title = strings.Replace(title, " ", "-", -1)

	replacer := strings.NewReplacer(",", "#", ":", "\"", "?", "/")
	title = replacer.Replace(title)

	return fmt.Sprintf("%04d/%02d/%02d/%s", b.Metadata.Date.Year(), b.Metadata.Date.Month(), b.Metadata.Date.Day(), title)
}

func (b *BlogPost) loadComments() {

	filename := b.Filename[:len(b.Filename)-3]
	dirname := b.CommentPath + string(filepath.Separator) + filename + string(filepath.Separator)

	b.Comments, _ = LoadComments(dirname)
}
<commit_msg>Fixed bad post URLs.<commit_after>package main

import (
	"fmt"
	"github.com/ant512/gobble/akismet"
	"html"
	"io/ioutil"
	"log"
	"os"
	"path/filepath"
	"strconv"
	"strings"
	"sync"
	"time"
)

type BlogPostMetadata struct {
	Title            string
	Id               int
	Date             time.Time
	Tags             []string
	DisallowComments bool
}

type BlogPost struct {
	Metadata     BlogPostMetadata
	Body         BlogItemBody
	Comments     Comments
	PostPath     string
	CommentPath  string
	Url          string
	Filename     string
	ModifiedDate time.Time
	mutex        sync.RWMutex
}

func LoadPost(filename, postPath, commentPath string) (*BlogPost, error) {

	b := &BlogPost{}
	b.PostPath = postPath
	b.Filename = filename
	b.CommentPath = commentPath

	fullPath := filepath.Join(postPath, filename)

	err := loadBlogFile(fullPath, func(fileInfo os.FileInfo) {
		b.ModifiedDate = fileInfo.ModTime()
	}, func(key, value string) {
		switch key {
		case "title":
			b.Metadata.Title = value
		case "id":
			b.Metadata.Id, _ = strconv.Atoi(value)
		case "tags":

			tags := strings.Split(value, ",")

			formattedTags := []string{}

			for j := range tags {
				tags[j] = strings.TrimSpace(tags[j])
				tags[j] = strings.Replace(tags[j], " ", "-", -1)
				tags[j] = strings.Replace(tags[j], "/", "-", -1)
				tags[j] = strings.Replace(tags[j], "#", "", -1)
				tags[j] = strings.ToLower(tags[j])

				if tags[j] != "" {
					formattedTags = append(formattedTags, tags[j])
				}
			}

			b.Metadata.Tags = formattedTags
		case "date":
			b.Metadata.Date = stringToTime(value)
		case "disallowcomments":
			b.Metadata.DisallowComments = value == "true"
		default:
		}
	}, func(value string) {
		bytes := []byte(value)

		b.Body.Markdown = value
		b.Body.HTML = convertMarkdownToHtml(&bytes)
	})

	if err == nil {
		b.Url = b.urlFromBlogPostProperties()
		b.loadComments()
	} else {
		log.Println(err)
	}

	return b, err
}

func (b *BlogPost) NonSpamComments() Comments {
	comments := Comments{}

	for _, comment := range b.Comments {
		if !comment.Metadata.IsSpam {
			comments = append(comments, comment)
		}
	}

	return comments
}

func (b *BlogPost) ContainsTag(tag string) bool {
	for _, t := range b.Metadata.Tags {
		if t == strings.ToLower(tag) {
			return true
		}
	}

	return false
}

func (b *BlogPost) ContainsTerm(term string) bool {

	term = strings.ToLower(term)

	if b.ContainsTag(term) {
		return true
	}

	if b.Comments.ContainsTerm(term) {
		return true
	}

	if b.Body.ContainsTerm(term) {
		return true
	}

	terms := strings.Split(term, " ")
	title := strings.ToLower(b.Metadata.Title)

	for _, item := range terms {
		if !strings.Contains(title, item) {
			return false
		}
	}

	return true
}

func (b *BlogPost) AllowsComments() bool {
	if b.Metadata.DisallowComments {
		return false
	}

	if SharedConfig.CommentsOpenForDays == 0 {
		return true
	}

	var closeDate = b.Metadata.Date.Add(time.Hour * 24 * time.Duration(SharedConfig.CommentsOpenForDays))

	return time.Now().Before(closeDate)
}

func (b *BlogPost) SaveComment(akismetAPIKey, serverAddress, remoteAddress, userAgent, referrer, author, email, body string) {

	// TODO: Ensure file name is unique
	isSpam, _ := akismet.IsSpamComment(body, serverAddress, remoteAddress, userAgent, referrer, author, email, akismetAPIKey)
	comment := NewComment(html.EscapeString(author), html.EscapeString(email), html.EscapeString(body), isSpam)

	b.mutex.Lock()
	b.Comments = append(b.Comments, comment)
	b.mutex.Unlock()

	commentPath := filepath.Join(b.CommentPath, b.Filename[:len(b.Filename)-3])
	filename := timeToFilename(comment.Metadata.Date)
	fullPath := filepath.Join(commentPath, filename)

	log.Println(commentPath)
	os.MkdirAll(commentPath, 0775)

	content := comment.String()

	err := ioutil.WriteFile(fullPath, []byte(content), 0644)

	if err != nil {
		log.Println(err)
	}
}

func (b *BlogPost) urlFromBlogPostProperties() string {
	title := strings.ToLower(b.Metadata.Title)
	title = strings.Replace(title, " ", "-", -1)
	title = strings.Replace(title, ",", "", -1)
	title = strings.Replace(title, "#", "", -1)
	title = strings.Replace(title, ":", "", -1)
	title = strings.Replace(title, "\\", "", -1)
	title = strings.Replace(title, "?", "", -1)
	title = strings.Replace(title, "/", "", -1)

	return fmt.Sprintf("%04d/%02d/%02d/%s", b.Metadata.Date.Year(), b.Metadata.Date.Month(), b.Metadata.Date.Day(), title)
}

func (b *BlogPost) loadComments() {

	filename := b.Filename[:len(b.Filename)-3]
	dirname := b.CommentPath + string(filepath.Separator) + filename + string(filepath.Separator)

	b.Comments, _ = LoadComments(dirname)
}
<|endoftext|>


<commit_before><commit_msg>Add hack to try to stop a.probe from being detected as leaked.<commit_after><|endoftext|>


<commit_before><commit_msg>feat: deduplicate tags (#884)<commit_after><|endoftext|>