<commit_before>package gsim

import (
	"math/big"
	"runtime"
	"sync"
)

// The OptionGenerator is responsible for generating the next
// available possible paths from each permutation prefix. Two
// implementations of OptionGenerator are provided: simplePermutation
// and graphPermutation. If neither are sufficient for your needs then
// you'll want to implement OptionGenerator yourself.
type OptionGenerator interface {
	// Generate is provided with the previously-chosen option, and is
	// required to return the set of options now available as the next
	// element in the permutation. OptionGenerators are expected to be
	// stateful. Generate must return an empty list for permutation
	// generation to terminate.
	Generate(interface{}) []interface{}
	// Clone is used during permutation generation. If the
	// OptionGenerator is stateful then Clone must return a fresh
	// OptionGenerator which shares no mutable state with the receiver
	// of Clone.
	Clone() OptionGenerator
}

type node struct {
	n         *big.Int
	depth     int
	value     interface{}
	generator OptionGenerator
	cumuOpts  *big.Int
}

type permN struct {
	perm []interface{}
	n    *big.Int
}

// Permutations allows you to interate through the available
// permutations, and extract specific permutations.
type Permutations struct {
	*node
}

var (
	bigIntZero = big.NewInt(0)
	bigIntOne  = big.NewInt(1)
	bigIntTwo  = big.NewInt(2)
)

// Construct a Permutations from an OptionGenerator.
func BuildPermutations(gen OptionGenerator) *Permutations {
	cur := &node{
		n:         bigIntZero,
		depth:     0,
		generator: gen,
		cumuOpts:  bigIntOne,
	}
	return &Permutations{
		node: cur,
	}
}

// Iterate through every permutation and use concurrency. A number
// of go-routines will be spawned appropriate for the current
// value of GOMAXPROCS. These go-routines will be fed batches of
// permutations and then invoke the function passed for each
// permutation. It's your responsibility to make sure the function
// passed is safe to be run concurrently from multiple
// go-routines.
//
// The first argument is the batchsize. If the batchsize is very
// low, then the generation of permutations will thrash CPU due to
// contention for locks on channels. If your processing of each
// permutation is very quick, then high numbers (e.g. 8192) can
// help to keep your CPU busy. If your processing of each
// permutation is less quick then lower numbers will avoid memory
// ballooning. Some trial and error may be worthwhile to find a
// good number for your computer, but 2048 is a sensible place to
// start.
func (p *Permutations) ForEachPar(batchSize int, f func(*big.Int, []interface{})) {
	par := runtime.GOMAXPROCS(0) // 0 gets the current count
	var wg sync.WaitGroup
	wg.Add(par)
	ch := make(chan []*permN, par*par)
	for idx := 0; idx < par; idx += 1 {
		go func() {
			defer wg.Done()
			for {
				perms, ok := <-ch
				if !ok {
					return
				}
				for _, perm := range perms {
					f(perm.n, perm.perm)
				}
			}
		}()
	}

	batch := make([]*permN, batchSize)
	batchIdx := 0
	p.ForEach(func(n *big.Int, perm []interface{}) {
		permCopy := make([]interface{}, len(perm))
		copy(permCopy, perm)
		batch[batchIdx] = &permN{n: n, perm: permCopy}
		batchIdx += 1
		if batchIdx == batchSize {
			ch <- batch
			batch = make([]*permN, batchSize)
			batchIdx = 0
		}
	})
	ch <- batch[:batchIdx]
	close(ch)
	wg.Wait()
}

// Iterate through every permutation in the current go-routine. No
// parallelism occurs. The function passed is invoked once for
// every permutation. It is supplied with the permutation number,
// and the permutation itself. These arguments should be
// considered read-only. If you mutate the permutation number or
// permutation then behaviour is undefined.
func (p *Permutations) ForEach(f func(*big.Int, []interface{})) {
	perm := []interface{}{}

	worklist := []*node{&node{
		n:         p.node.n,
		depth:     p.node.depth,
		generator: p.node.generator.Clone(),
		cumuOpts:  p.node.cumuOpts,
	}}
	l := len(worklist)

	for l != 0 {
		l -= 1
		cur := worklist[l]
		worklist = worklist[:l]

		perm = append(perm[:cur.depth], cur.value)

		options := cur.generator.Generate(cur.value)
		optionCount := len(options)

		if optionCount == 0 {
			f(cur.n, perm[1:])

		} else {
			cumuOpts := big.NewInt(int64(optionCount))
			cumuOpts.Mul(cur.cumuOpts, cumuOpts)
			for idx, option := range options {
				var childN *big.Int
				if optionCount == 1 {
					childN = cur.n
				} else {
					childN = big.NewInt(int64(idx))
					childN.Mul(childN, cur.cumuOpts)
					childN.Add(childN, cur.n)
				}
				var gen OptionGenerator
				if idx == 0 {
					gen = cur.generator
				} else {
					gen = cur.generator.Clone()
				}
				child := &node{
					n:         childN,
					depth:     cur.depth + 1,
					value:     option,
					generator: gen,
					cumuOpts:  cumuOpts,
				}
				worklist = append(worklist, child)
			}
			l += optionCount
		}
	}
}

// Every permutation has a unique number, which is supplied to the
// function passed in both of the other functions in
// Permutations. If you need to generate specific permutations,
// those numbers can be provided to Permutation, which will
// generate the exact same permutation. Note that iterating
// through a range of permutation numbers and repeatedly calling
// Permutation is slower than using either of the iterator
// functions.
func (p *Permutations) Permutation(permNum *big.Int) []interface{} {
	n := big.NewInt(0).Set(permNum)
	perm := []interface{}{}
	choiceBig := big.NewInt(0)

	gen := p.node.generator.Clone()
	val := p.node.value
	for {
		options := gen.Generate(val)
		optionCount := len(options)
		if optionCount == 0 {
			return perm
		}
		choiceBig.SetInt64(int64(optionCount))
		n.QuoRem(n, choiceBig, choiceBig)
		val = options[int(choiceBig.Int64())]
		perm = append(perm, val)
		gen = gen.Clone()
	}
	return perm
}
<commit_msg>Improve docs. Add note about determinism.<commit_after>package gsim

import (
	"math/big"
	"runtime"
	"sync"
)

// The OptionGenerator is responsible for generating the next
// available possible paths from each permutation prefix. Two
// implementations of OptionGenerator are provided: simplePermutation
// and graphPermutation. If neither are sufficient for your needs then
// you'll want to implement OptionGenerator yourself.
//
// If you do implement OptionGenerator yourself, you must ensure it is
// entirely deterministic. So do not rely on iteration order of maps
// and so forth.
type OptionGenerator interface {
	// Generate is provided with the previously-chosen option, and is
	// required to return the set of options now available as the next
	// element in the permutation. OptionGenerators are expected to be
	// stateful. Generate must return an empty list for permutation
	// generation to terminate.
	Generate(interface{}) []interface{}
	// Clone is used during permutation generation. If the
	// OptionGenerator is stateful then Clone must return a fresh
	// OptionGenerator which shares no mutable state with the receiver
	// of Clone.
	Clone() OptionGenerator
}

type node struct {
	n         *big.Int
	depth     int
	value     interface{}
	generator OptionGenerator
	cumuOpts  *big.Int
}

type permN struct {
	perm []interface{}
	n    *big.Int
}

// Permutations allows you to interate through the available
// permutations, and extract specific permutations.
type Permutations struct {
	*node
}

var (
	bigIntZero = big.NewInt(0)
	bigIntOne  = big.NewInt(1)
	bigIntTwo  = big.NewInt(2)
)

// Construct a Permutations from an OptionGenerator.
func BuildPermutations(gen OptionGenerator) *Permutations {
	cur := &node{
		n:         bigIntZero,
		depth:     0,
		generator: gen,
		cumuOpts:  bigIntOne,
	}
	return &Permutations{
		node: cur,
	}
}

// Iterate through every permutation and use concurrency. A number of
// go-routines will be spawned appropriate for the current value of
// GOMAXPROCS. These go-routines will be fed batches of permutations
// and then invoke f for each permutation. It's your responsibility to
// make sure f is safe to be run concurrently from multiple
// go-routines.
//
// If the batchsize is very low, then the generation of permutations
// will thrash CPU due to contention for locks on channels. If your
// processing of each permutation is very quick, then high numbers
// (e.g. 8192) can help to keep your CPU busy. If your processing of
// each permutation is less quick then lower numbers will avoid memory
// ballooning. Some trial and error may be worthwhile to find a good
// number for your computer, but 2048 is a sensible place to start.
func (p *Permutations) ForEachPar(batchSize int, f func(*big.Int, []interface{})) {
	par := runtime.GOMAXPROCS(0) // 0 gets the current count
	var wg sync.WaitGroup
	wg.Add(par)
	ch := make(chan []*permN, par*par)
	for idx := 0; idx < par; idx += 1 {
		go func() {
			defer wg.Done()
			for {
				perms, ok := <-ch
				if !ok {
					return
				}
				for _, perm := range perms {
					f(perm.n, perm.perm)
				}
			}
		}()
	}

	batch := make([]*permN, batchSize)
	batchIdx := 0
	p.ForEach(func(n *big.Int, perm []interface{}) {
		permCopy := make([]interface{}, len(perm))
		copy(permCopy, perm)
		batch[batchIdx] = &permN{n: n, perm: permCopy}
		batchIdx += 1
		if batchIdx == batchSize {
			ch <- batch
			batch = make([]*permN, batchSize)
			batchIdx = 0
		}
	})
	ch <- batch[:batchIdx]
	close(ch)
	wg.Wait()
}

// Iterate through every permutation in the current go-routine. No
// parallelism occurs. The function f is invoked once for every
// permutation. It is supplied with the permutation number, and the
// permutation itself. These arguments should be considered
// read-only. If you mutate the permutation number or permutation then
// behaviour is undefined.
func (p *Permutations) ForEach(f func(*big.Int, []interface{})) {
	perm := []interface{}{}

	worklist := []*node{&node{
		n:         p.node.n,
		depth:     p.node.depth,
		generator: p.node.generator.Clone(),
		cumuOpts:  p.node.cumuOpts,
	}}
	l := len(worklist)

	for l != 0 {
		l -= 1
		cur := worklist[l]
		worklist = worklist[:l]

		perm = append(perm[:cur.depth], cur.value)

		options := cur.generator.Generate(cur.value)
		optionCount := len(options)

		if optionCount == 0 {
			f(cur.n, perm[1:])

		} else {
			cumuOpts := big.NewInt(int64(optionCount))
			cumuOpts.Mul(cur.cumuOpts, cumuOpts)
			for idx, option := range options {
				var childN *big.Int
				if optionCount == 1 {
					childN = cur.n
				} else {
					childN = big.NewInt(int64(idx))
					childN.Mul(childN, cur.cumuOpts)
					childN.Add(childN, cur.n)
				}
				var gen OptionGenerator
				if idx == 0 {
					gen = cur.generator
				} else {
					gen = cur.generator.Clone()
				}
				child := &node{
					n:         childN,
					depth:     cur.depth + 1,
					value:     option,
					generator: gen,
					cumuOpts:  cumuOpts,
				}
				worklist = append(worklist, child)
			}
			l += optionCount
		}
	}
}

// Every permutation has a unique number, which is supplied to the
// function passed to both of the iteration functions in
// Permutations. If you need to generate specific permutations, those
// numbers can be provided to Permutation, which will generate the
// exact same permutation. Note that iterating through a range of
// permutation numbers and repeatedly calling Permutation is slower
// than using either of the iterator functions.
func (p *Permutations) Permutation(permNum *big.Int) []interface{} {
	n := big.NewInt(0).Set(permNum)
	perm := []interface{}{}
	choiceBig := big.NewInt(0)

	gen := p.node.generator.Clone()
	val := p.node.value
	for {
		options := gen.Generate(val)
		optionCount := len(options)
		if optionCount == 0 {
			return perm
		}
		choiceBig.SetInt64(int64(optionCount))
		n.QuoRem(n, choiceBig, choiceBig)
		val = options[int(choiceBig.Int64())]
		perm = append(perm, val)
		gen = gen.Clone()
	}
	return perm
}
<|endoftext|>


<commit_before>package hamt

import (
	"fmt"

	"github.com/lleo/go-hamt/hamt32"
	"github.com/lleo/go-hamt/hamt_key"
)

type Hamt interface {
	Get(hamt_key.Key) (interface{}, bool)
	Put(hamt_key.Key, interface{}) bool
	Del(hamt_key.Key) (interface{}, bool)
	IsEmpty() bool
	String() string
	LongString(indent string) string
}

//type Key interface {
//	Equals(Key) bool
//	Hash30() uint32
//	Hash60() uint64
//	String() string
//}

type keyVal struct {
	key hamt_key.Key
	val interface{}
}

func (kv keyVal) String() string {
	return fmt.Sprintf("keyVal{%s, %v}", kv.key, kv.val)
}

func NewHamt32() Hamt {
	return hamt32.NewHamt()
}
<commit_msg>added NewHamt64()<commit_after>package hamt

import (
	"fmt"

	"github.com/lleo/go-hamt/hamt32"
	"github.com/lleo/go-hamt/hamt64"
	"github.com/lleo/go-hamt/hamt_key"
)

type Hamt interface {
	Get(hamt_key.Key) (interface{}, bool)
	Put(hamt_key.Key, interface{}) bool
	Del(hamt_key.Key) (interface{}, bool)
	IsEmpty() bool
	String() string
	LongString(indent string) string
}

//type Key interface {
//	Equals(Key) bool
//	Hash30() uint32
//	Hash60() uint64
//	String() string
//}

type keyVal struct {
	key hamt_key.Key
	val interface{}
}

func (kv keyVal) String() string {
	return fmt.Sprintf("keyVal{%s, %v}", kv.key, kv.val)
}

func NewHamt32() Hamt {
	return hamt32.NewHamt()
}

func NewHamt64() Hamt {
	return hamt64.NewHamt()
}
<|endoftext|>


<commit_before>package hdf5

// #include "hdf5.h"
// #include <stdlib.h>
// #include <string.h>
import "C"

import (
	"fmt"
	"runtime"
	"unsafe"
)

type CString struct {
	cstr *C.char
}

func NewCString(s string) *CString {
	c_s := &CString{cstr: C.CString(s)}
	runtime.SetFinalizer(c_s, (*CString).finalizer)
	return c_s
}

func (s *CString) finalizer() {
	C.free(unsafe.Pointer(s.cstr))
}

// initialize the hdf5 library
func init() {
	err := h5err(C.H5open())
	if err != nil {
		err_str := fmt.Sprintf("pb calling H5open(): %s", err)
		panic(err_str)
	}
}

// utils
type hdferror struct {
	code int
}

func (h *hdferror) Error() string {
	return fmt.Sprintf("**hdf5 error** code=%d", h.code)
}

func h5err(herr C.herr_t) error {
	if herr >= C.herr_t(0) {
		return nil
	}
	return &hdferror{code: int(herr)}
}

// Close flushes all data to disk, closes all open identifiers, and cleans up memory.
// It should generally be called before your application exits.
func Close() error {
	return h5err(C.H5close())
}

// Returns the HDF library release number.
func GetLibVersion() (majnum, minnum, relnum uint, err error) {
	err = nil
	majnum = 0
	minnum = 0
	relnum = 0

	c_majnum := C.uint(majnum)
	c_minnum := C.uint(minnum)
	c_relnum := C.uint(relnum)

	herr := C.H5get_libversion(&c_majnum, &c_minnum, &c_relnum)
	err = h5err(herr)
	if err == nil {
		majnum = uint(c_majnum)
		minnum = uint(c_minnum)
		relnum = uint(c_relnum)
	}
	return
}

// Garbage collects on all free-lists of all types.
func GarbageCollect() error {
	return h5err(C.H5garbage_collect())
}

type Object interface {
	Name() string
	Id() int
	File() *File
}
<commit_msg>Remove CString type. It was unused.<commit_after>package hdf5

// #include "hdf5.h"
// #include <stdlib.h>
// #include <string.h>
import "C"

import (
	"fmt"
)

// initialize the hdf5 library
func init() {
	err := h5err(C.H5open())
	if err != nil {
		err_str := fmt.Sprintf("pb calling H5open(): %s", err)
		panic(err_str)
	}
}

// utils
type hdferror struct {
	code int
}

func (h *hdferror) Error() string {
	return fmt.Sprintf("**hdf5 error** code=%d", h.code)
}

func h5err(herr C.herr_t) error {
	if herr >= C.herr_t(0) {
		return nil
	}
	return &hdferror{code: int(herr)}
}

// Close flushes all data to disk, closes all open identifiers, and cleans up memory.
// It should generally be called before your application exits.
func Close() error {
	return h5err(C.H5close())
}

// Returns the HDF library release number.
func GetLibVersion() (majnum, minnum, relnum uint, err error) {
	err = nil
	majnum = 0
	minnum = 0
	relnum = 0

	c_majnum := C.uint(majnum)
	c_minnum := C.uint(minnum)
	c_relnum := C.uint(relnum)

	herr := C.H5get_libversion(&c_majnum, &c_minnum, &c_relnum)
	err = h5err(herr)
	if err == nil {
		majnum = uint(c_majnum)
		minnum = uint(c_minnum)
		relnum = uint(c_relnum)
	}
	return
}

// Garbage collects on all free-lists of all types.
func GarbageCollect() error {
	return h5err(C.H5garbage_collect())
}

type Object interface {
	Name() string
	Id() int
	File() *File
}
<|endoftext|>


<commit_before>package storage

import (
	"gopkg.in/mgo.v2"
)

type storage struct {
	c *mgo.Collection
}

func (s *storage) Store(data interface{}) error {
	return s.c.Insert(data)
}

func NewStorage(
	addr string,
	database string,
	colletion string,
) (*storage, error) {
	session, err := mgo.Dial(addr)
	if err != nil {
		return &storage{}, err
	}
	c := session.DB(database).C(colletion)
	return &storage{
		c: c,
	}, nil
}
<commit_msg>get dada from mongo<commit_after>package storage

import (
	"gopkg.in/mgo.v2"
)

type storage struct {
	c *mgo.Collection
}

func (s *storage) Store(data interface{}) error {
	return s.c.Insert(data)
}

func (s *storage) Load() (interface{}, error) {
	var results interface{}
	err := s.c.Find(nil).All(&results)
	return results, err
}

func NewStorage(
	addr string,
	database string,
	colletion string,
) (*storage, error) {
	session, err := mgo.Dial(addr)
	if err != nil {
		return &storage{}, err
	}
	c := session.DB(database).C(colletion)
	return &storage{
		c: c,
	}, nil
}
<|endoftext|>


<commit_before>package setup

import (
	"context"
	"fmt"
	"io"
	"sync"

	"github.com/docker/docker/api/types"
	"github.com/docker/docker/api/types/container"
	"github.com/docker/docker/client"
	"github.com/paralin/skiff-core/config"
	"github.com/paralin/skiff-core/util/multiwriter"
	log "github.com/sirupsen/logrus"
)

// ContainerSetup sets up a container.
type ContainerSetup struct {
	config *config.ConfigContainer
	waiter ImageWaiter
	logger multiwriter.MultiWriter

	wg          sync.WaitGroup
	err         error
	containerId string
}

// NewContainerSetup creates a new ContainerSetup.
func NewContainerSetup(config *config.ConfigContainer, waiter ImageWaiter) *ContainerSetup {
	return &ContainerSetup{config: config, waiter: waiter}
}

// buildDockerContainer builds the Docker API container representation of this config.
func (cs *ContainerSetup) buildDockerContainer() *types.ContainerCreateConfig {
	res := &types.ContainerCreateConfig{Name: cs.config.Name()}

	config := cs.config
	containerConfig := &container.Config{
		Cmd:        config.Cmd,
		Entrypoint: config.Entrypoint,
		Image:      config.Image,
		Tty:        config.Tty,
		WorkingDir: config.WorkingDirectory,
		StopSignal: config.StopSignal,
	}
	res.Config = containerConfig
	for _, ev := range config.Env {
		if len(ev) != 0 {
			containerConfig.Env = append(
				containerConfig.Env,
				ev,
			)
		}
	}
	useInit := !config.DisableInit
	hostConfig := &container.HostConfig{
		CapAdd:      config.CapAdd,
		DNS:         config.DNS,
		DNSSearch:   config.DNSSearch,
		ExtraHosts:  config.Hosts,
		Init:        &useInit,
		SecurityOpt: config.SecurityOpt,
		Tmpfs:       config.TmpFs,
		Privileged:  config.Privileged,
	}
	if rp := config.RestartPolicy; rp != "" {
		hostConfig.RestartPolicy = container.RestartPolicy{
			Name: rp,
		}
	}
	res.HostConfig = hostConfig
	if len(config.Mounts) > 0 {
		hostConfig.Binds = make([]string, len(config.Mounts))
		copy(hostConfig.Binds, config.Mounts)
	}
	if useInit {
		hostConfig.Binds = append(hostConfig.Binds, "/usr/bin/tini:/dev/init")
	}
	if config.HostNetwork {
		hostConfig.NetworkMode = container.NetworkMode("host")
	}
	if config.HostIPC {
		hostConfig.IpcMode = container.IpcMode("host")
	}
	if config.HostPID {
		hostConfig.PidMode = container.PidMode("host")
	}
	if config.HostUTS {
		hostConfig.UTSMode = container.UTSMode("host")
	}

	return res
}

// Execute starts the container setup.
func (cs *ContainerSetup) Execute() (execError error) {
	cs.wg.Add(1)
	defer func() {
		cs.err = execError
		cs.wg.Done()
	}()

	config := cs.config
	if config.Image == "" {
		return fmt.Errorf("Container %s must have image specified.", config.Name())
	}

	dockerClient, err := client.NewEnvClient()
	if err != nil {
		return err
	}
	defer dockerClient.Close()

	// wait for the image to be ready
	if err := cs.waiter.WaitForImage(config.Image, &cs.logger); err != nil {
		return err
	}

	// check if the container exists
	/*
		filters := filters.NewArgs()
		filters.Add("name", config.Name())
	*/
	list, err := dockerClient.ContainerList(context.Background(), types.ContainerListOptions{
		All: true,
		// Filters: filters,
	})
	if err != nil {
		return err
	}

	le := log.WithField("name", config.Name())
	for _, ctr := range list {
		for _, name := range ctr.Names {
			if name == config.Name() {
				le.Debug("Container already exists")
				cs.containerId = ctr.ID
				return nil
			}
		}
	}

	// create the container
	cconf := cs.buildDockerContainer()
	res, err := dockerClient.ContainerCreate(
		context.Background(),
		cconf.Config,
		cconf.HostConfig,
		cconf.NetworkingConfig,
		cconf.Name,
	)
	if err != nil {
		return err
	}
	le.WithField("id", res.ID).Debug("Container created")
	for _, warning := range res.Warnings {
		le.Warnf("Docker issued warning: %s", warning)
	}
	cs.containerId = res.ID

	cs.logger.Write([]byte("Container created with ID: "))
	cs.logger.Write([]byte(res.ID))
	cs.logger.Write([]byte("\n"))

	if cs.config.StartAfterCreate {
		cs.logger.Write([]byte("Starting container" + res.ID + "...\n"))
		err = dockerClient.ContainerStart(context.Background(), res.ID, types.ContainerStartOptions{})
		if err != nil {
			cs.logger.Write([]byte("Could not start container, continuing: " + err.Error() + "\n"))
		}
	}

	return nil
}

// Wait waits for Execute() to finish.
func (i *ContainerSetup) Wait(log io.Writer) error {
	i.logger.AddWriter(log)
	defer i.logger.RmWriter(log)

	i.wg.Wait()
	return i.err
}

// WaitWithId waits for Execute() to finish and returns the container ID.
func (i *ContainerSetup) WaitWithId(outw io.Writer) (string, error) {
	i.logger.AddWriter(outw)
	defer i.logger.RmWriter(outw)

	i.wg.Wait()
	return i.containerId, i.err
}
<commit_msg>fix(setup): check if container exists before downloading image<commit_after>package setup

import (
	"context"
	"fmt"
	"io"
	"sync"

	"github.com/docker/docker/api/types"
	"github.com/docker/docker/api/types/container"
	"github.com/docker/docker/client"
	"github.com/paralin/skiff-core/config"
	"github.com/paralin/skiff-core/util/multiwriter"
	log "github.com/sirupsen/logrus"
)

// ContainerSetup sets up a container.
type ContainerSetup struct {
	config *config.ConfigContainer
	waiter ImageWaiter
	logger multiwriter.MultiWriter

	wg          sync.WaitGroup
	err         error
	containerId string
}

// NewContainerSetup creates a new ContainerSetup.
func NewContainerSetup(config *config.ConfigContainer, waiter ImageWaiter) *ContainerSetup {
	return &ContainerSetup{config: config, waiter: waiter}
}

// buildDockerContainer builds the Docker API container representation of this config.
func (cs *ContainerSetup) buildDockerContainer() *types.ContainerCreateConfig {
	res := &types.ContainerCreateConfig{Name: cs.config.Name()}

	config := cs.config
	containerConfig := &container.Config{
		Cmd:        config.Cmd,
		Entrypoint: config.Entrypoint,
		Image:      config.Image,
		Tty:        config.Tty,
		WorkingDir: config.WorkingDirectory,
		StopSignal: config.StopSignal,
	}
	res.Config = containerConfig
	for _, ev := range config.Env {
		if len(ev) != 0 {
			containerConfig.Env = append(
				containerConfig.Env,
				ev,
			)
		}
	}
	useInit := !config.DisableInit
	hostConfig := &container.HostConfig{
		CapAdd:      config.CapAdd,
		DNS:         config.DNS,
		DNSSearch:   config.DNSSearch,
		ExtraHosts:  config.Hosts,
		Init:        &useInit,
		SecurityOpt: config.SecurityOpt,
		Tmpfs:       config.TmpFs,
		Privileged:  config.Privileged,
	}
	if rp := config.RestartPolicy; rp != "" {
		hostConfig.RestartPolicy = container.RestartPolicy{
			Name: rp,
		}
	}
	res.HostConfig = hostConfig
	if len(config.Mounts) > 0 {
		hostConfig.Binds = make([]string, len(config.Mounts))
		copy(hostConfig.Binds, config.Mounts)
	}
	if useInit {
		hostConfig.Binds = append(hostConfig.Binds, "/usr/bin/tini:/dev/init")
	}
	if config.HostNetwork {
		hostConfig.NetworkMode = container.NetworkMode("host")
	}
	if config.HostIPC {
		hostConfig.IpcMode = container.IpcMode("host")
	}
	if config.HostPID {
		hostConfig.PidMode = container.PidMode("host")
	}
	if config.HostUTS {
		hostConfig.UTSMode = container.UTSMode("host")
	}

	return res
}

// Execute starts the container setup.
func (cs *ContainerSetup) Execute() (execError error) {
	cs.wg.Add(1)
	defer func() {
		cs.err = execError
		cs.wg.Done()
	}()

	config := cs.config
	if config.Image == "" {
		return fmt.Errorf("Container %s must have image specified.", config.Name())
	}

	dockerClient, err := client.NewEnvClient()
	if err != nil {
		return err
	}
	defer dockerClient.Close()

	// check if the container exists
	le := log.WithField("name", config.Name())
	checkContainerExists := func() (bool, error) {
		list, err := dockerClient.ContainerList(context.Background(), types.ContainerListOptions{
			All: true,
		})
		if err != nil {
			return false, err
		}

		for _, ctr := range list {
			for _, name := range ctr.Names {
				if name == config.Name() {
					le.Debug("Container already exists")
					cs.containerId = ctr.ID
					return true, nil
				}
			}
		}
		return false, nil
	}

	// createOrFindContainer returns nil only if cs.containerID contains the container ID.
	createOrFindContainer := func() error {
		if exists, err := checkContainerExists(); exists || err != nil {
			return err
		}

		// wait for the image to be ready
		if err := cs.waiter.WaitForImage(config.Image, &cs.logger); err != nil {
			return err
		}

		if exists, err := checkContainerExists(); exists || err != nil {
			return err
		}

		// create the container
		cconf := cs.buildDockerContainer()
		res, err := dockerClient.ContainerCreate(
			context.Background(),
			cconf.Config,
			cconf.HostConfig,
			cconf.NetworkingConfig,
			cconf.Name,
		)
		if err != nil {
			return err
		}
		le.WithField("id", res.ID).Debug("Container created")
		for _, warning := range res.Warnings {
			le.Warnf("Docker issued warning: %s", warning)
		}
		cs.containerId = res.ID
		return nil
	}

	if err := createOrFindContainer(); err != nil {
		return err
	}

	containerID := cs.containerId
	cs.logger.Write([]byte("Container created/found with ID: "))
	cs.logger.Write([]byte(containerID))
	cs.logger.Write([]byte("\n"))

	if cs.config.StartAfterCreate {
		cs.logger.Write([]byte("Starting container" + containerID + "...\n"))
		err = dockerClient.ContainerStart(context.Background(), containerID, types.ContainerStartOptions{})
		if err != nil {
			cs.logger.Write([]byte("Could not start container, continuing: " + err.Error() + "\n"))
		}
	}

	return nil
}

// Wait waits for Execute() to finish.
func (i *ContainerSetup) Wait(log io.Writer) error {
	i.logger.AddWriter(log)
	defer i.logger.RmWriter(log)

	i.wg.Wait()
	return i.err
}

// WaitWithId waits for Execute() to finish and returns the container ID.
func (i *ContainerSetup) WaitWithId(outw io.Writer) (string, error) {
	i.logger.AddWriter(outw)
	defer i.logger.RmWriter(outw)

	i.wg.Wait()
	return i.containerId, i.err
}
<|endoftext|>


<commit_before>package wire

import (
	"bytes"
	"crypto/ecdsa"
	"crypto/elliptic"
	"crypto/sha256"
	"encoding/binary"
	"errors"
	"fmt"
	"io"
	"math/big"
	"strings"

	"veyron2/security"
	"veyron2/vom"
)

// ErrNoIntegrity is the error returned when bytes of an object seem to have been tampered with.
var ErrNoIntegrity = errors.New("signature does not match bytes, possible tampering")

// errInvalidBlessingName returns an error specifying that the provided blessing name is invalid.
func errInvalidBlessingName(blessingName string) error {
	return fmt.Errorf("invalid blessing name:%q", blessingName)
}

// WriteString writes the length and contents of the provided string to the provided Writer.
func WriteString(w io.Writer, tmp []byte, s string) {
	w.Write(tmp[:binary.PutVarint(tmp, int64(len(s)))])
	io.WriteString(w, s)
}

// WriteBytes writes the length and contents of the provided byte slice to the provided Writer.
func WriteBytes(w io.Writer, tmp, b []byte) {
	w.Write(tmp[:binary.PutVarint(tmp, int64(len(b)))])
	w.Write(b)
}

func ellipticCurve(t KeyCurve) (elliptic.Curve, error) {
	switch t {
	case KeyCurveP256:
		return elliptic.P256(), nil
	default:
		return nil, fmt.Errorf("unrecognized elliptic curve %v", t)
	}
}

// Decode unmarshals the contents of the PublicKey object and returns
// and crypto.ecdsa.PublicKey object.
func (p *PublicKey) Decode() (*ecdsa.PublicKey, error) {
	curve, err := ellipticCurve(p.Curve)
	if err != nil {
		return nil, err
	}
	x, y := elliptic.Unmarshal(curve, p.XY)
	return &ecdsa.PublicKey{Curve: curve, X: x, Y: y}, nil
}

// Encode takes a crypto.ecdsa.PublicKey object, marshals its contents
// and populates the PublicKey object with them.
func (p *PublicKey) Encode(key *ecdsa.PublicKey) error {
	if key.Curve != elliptic.P256() {
		return fmt.Errorf("unrecognized elliptic curve %T", p.Curve)
	}
	p.Curve = KeyCurveP256
	p.XY = elliptic.Marshal(key.Curve, key.X, key.Y)
	return nil
}

// encode serializes a security.Caveat object and sets the resulting bytes on the Caveat object.
func (c *Caveat) encode(caveat security.Caveat) error {
	var b bytes.Buffer
	if err := vom.NewEncoder(&b).Encode(caveat); err != nil {
		return err
	}
	c.Bytes = b.Bytes()
	return nil
}

// EncodeCaveats encodes the provided set of security.ServiceCaveat objects into Caveat objects.
func EncodeCaveats(serviceCaveats []security.ServiceCaveat) ([]Caveat, error) {
	caveats := make([]Caveat, len(serviceCaveats))
	for i, c := range serviceCaveats {
		caveats[i].Service = c.Service
		if err := caveats[i].encode(c.Caveat); err != nil {
			return nil, err
		}
	}
	return caveats, nil
}

// Decode deserializes the contents of the Caveat object to obtain a security.Caveat object.
func (c *Caveat) Decode() (security.Caveat, error) {
	var caveat security.Caveat
	if err := vom.NewDecoder(bytes.NewReader(c.Bytes)).Decode(&caveat); err != nil {
		return nil, err
	}
	return caveat, nil
}

// DecodeThirdPartyCaveats decodes the provided Caveat objects into security.ThirdPartyCaveat
// objects. The resulting objects are wrapped in security.ServiceCaveat objects according
// to the services they are bound to.
func DecodeThirdPartyCaveats(caveats []Caveat) (thirdPartyCaveats []security.ServiceCaveat) {
	for _, wireCav := range caveats {
		cav, err := wireCav.Decode()
		if err != nil {
			continue
		}
		tpCav, ok := cav.(security.ThirdPartyCaveat)
		if !ok {
			continue
		}
		thirdPartyCaveats = append(thirdPartyCaveats, security.ServiceCaveat{Service: wireCav.Service, Caveat: tpCav})
	}
	return
}

// Validate verifies the restriction embedded inside the security.Caveat if the label
// is an empty string (indicating a universal caveat) or if the label matches the Name
// of the LocalID present in the provided context.
func (c *Caveat) Validate(ctx security.Context) error {
	// TODO(ataly): Is checking that the localID matches the caveat's Service pattern
	// the right choice here?
	if c.Service != security.AllPrincipals && (ctx.LocalID() == nil || !ctx.LocalID().Match(c.Service)) {
		return nil
	}
	cav, err := c.Decode()
	if err != nil {
		return err
	}
	return cav.Validate(ctx)
}

// -- Helper methods on the wire format for the chain implementation of Identity --

// contentHash returns a SHA256 hash of the contents of the certificate along with the
// provided signature.
func (c *Certificate) contentHash(issuerSignature security.Signature) []byte {
	h := sha256.New()
	tmp := make([]byte, binary.MaxVarintLen64)
	WriteBytes(h, tmp, issuerSignature.R)
	WriteBytes(h, tmp, issuerSignature.S)
	WriteString(h, tmp, c.Name)
	h.Write([]byte{byte(c.PublicKey.Curve)})
	WriteBytes(h, tmp, c.PublicKey.XY)
	binary.Write(h, binary.BigEndian, uint32(len(c.Caveats)))
	for _, cav := range c.Caveats {
		WriteString(h, tmp, string(cav.Service))
		WriteBytes(h, tmp, cav.Bytes)
	}
	return h.Sum(nil)
}

// Sign uses the given Signer to sign the signature of the last certificate in
// the provided PublicID, storing the new signature in the current certificate.
func (c *Certificate) Sign(signer security.Signer, pubID *ChainPublicID) error {
	numCerts := len(pubID.Certificates)
	if numCerts == 0 {
		return errors.New("cannot sign a ChainPublicID with no certificates")
	}
	var err error
	c.Signature, err = signer.Sign(c.contentHash(pubID.Certificates[numCerts-1].Signature))
	return err
}

func (c *Certificate) verify(issuerSignature security.Signature, key *ecdsa.PublicKey) bool {
	var r, s big.Int
	return ecdsa.Verify(key, c.contentHash(issuerSignature), r.SetBytes(c.Signature.R), s.SetBytes(c.Signature.S))
}

// ValidateCaveats verifies if all caveats present on the certificate validate with
// respect to the provided context.
func (c *Certificate) ValidateCaveats(ctx security.Context) error {
	for _, cav := range c.Caveats {
		if err := cav.Validate(ctx); err != nil {
			return err
		}
	}
	return nil
}

// Name returns the chained name obtained by joining all names along the ChainPublicID's
// certificate chain.
func (id *ChainPublicID) Name() string {
	var buf bytes.Buffer
	for i, c := range id.Certificates {
		if i > 0 {
			buf.WriteString(ChainSeparator)
		}
		buf.WriteString(c.Name)
	}
	return buf.String()
}

// VerifyIntegrity verifies that the ChainPublicID has a valid certificate chain, i.e,
// (1) each certificate on the chain has a signature that can be verified using the
// public key specified in the previous certificate, (2) the first certificate's
// signature can be verified using its own public key, and (3) all certificate names
// are valid blessing names.
func (id *ChainPublicID) VerifyIntegrity() error {
	nCerts := len(id.Certificates)
	if nCerts == 0 {
		return ErrNoIntegrity
	}
	verificationKey, err := id.Certificates[0].PublicKey.Decode()
	if err != nil {
		return ErrNoIntegrity
	}
	issuerSignature := security.Signature{}
	for _, c := range id.Certificates {
		if err := ValidateBlessingName(c.Name); err != nil {
			return err
		}
		// TODO(ashankar, ataly): Do we worry about timing attacks by
		// early exiting on invalid certificate?
		if !c.verify(issuerSignature, verificationKey) {
			return ErrNoIntegrity
		}
		if verificationKey, err = c.PublicKey.Decode(); err != nil {
			return ErrNoIntegrity
		}
		issuerSignature = c.Signature
	}
	return nil
}

// ValidateBlessingName verifies if the provided name is fit to be the name of a blessing.
func ValidateBlessingName(name string) error {
	// TODO(ataly, ashankar): Define the list of reserved characters (such as  "*", "#",
	// "/", "\", etc.) and ensure that the check below ensures absence of all of them.
	if name == "" || strings.ContainsAny(name, ChainSeparator) || strings.Contains(name, "@@") {
		return errInvalidBlessingName(name)
	}
	return nil
}
<commit_msg>veyron/runtimes/google/ipc: Revert <principal_pattern>@@<objectname><commit_after>package wire

import (
	"bytes"
	"crypto/ecdsa"
	"crypto/elliptic"
	"crypto/sha256"
	"encoding/binary"
	"errors"
	"fmt"
	"io"
	"math/big"
	"strings"

	"veyron2/security"
	"veyron2/vom"
)

// ErrNoIntegrity is the error returned when bytes of an object seem to have been tampered with.
var ErrNoIntegrity = errors.New("signature does not match bytes, possible tampering")

// errInvalidBlessingName returns an error specifying that the provided blessing name is invalid.
func errInvalidBlessingName(blessingName string) error {
	return fmt.Errorf("invalid blessing name:%q", blessingName)
}

// WriteString writes the length and contents of the provided string to the provided Writer.
func WriteString(w io.Writer, tmp []byte, s string) {
	w.Write(tmp[:binary.PutVarint(tmp, int64(len(s)))])
	io.WriteString(w, s)
}

// WriteBytes writes the length and contents of the provided byte slice to the provided Writer.
func WriteBytes(w io.Writer, tmp, b []byte) {
	w.Write(tmp[:binary.PutVarint(tmp, int64(len(b)))])
	w.Write(b)
}

func ellipticCurve(t KeyCurve) (elliptic.Curve, error) {
	switch t {
	case KeyCurveP256:
		return elliptic.P256(), nil
	default:
		return nil, fmt.Errorf("unrecognized elliptic curve %v", t)
	}
}

// Decode unmarshals the contents of the PublicKey object and returns
// and crypto.ecdsa.PublicKey object.
func (p *PublicKey) Decode() (*ecdsa.PublicKey, error) {
	curve, err := ellipticCurve(p.Curve)
	if err != nil {
		return nil, err
	}
	x, y := elliptic.Unmarshal(curve, p.XY)
	return &ecdsa.PublicKey{Curve: curve, X: x, Y: y}, nil
}

// Encode takes a crypto.ecdsa.PublicKey object, marshals its contents
// and populates the PublicKey object with them.
func (p *PublicKey) Encode(key *ecdsa.PublicKey) error {
	if key.Curve != elliptic.P256() {
		return fmt.Errorf("unrecognized elliptic curve %T", p.Curve)
	}
	p.Curve = KeyCurveP256
	p.XY = elliptic.Marshal(key.Curve, key.X, key.Y)
	return nil
}

// encode serializes a security.Caveat object and sets the resulting bytes on the Caveat object.
func (c *Caveat) encode(caveat security.Caveat) error {
	var b bytes.Buffer
	if err := vom.NewEncoder(&b).Encode(caveat); err != nil {
		return err
	}
	c.Bytes = b.Bytes()
	return nil
}

// EncodeCaveats encodes the provided set of security.ServiceCaveat objects into Caveat objects.
func EncodeCaveats(serviceCaveats []security.ServiceCaveat) ([]Caveat, error) {
	caveats := make([]Caveat, len(serviceCaveats))
	for i, c := range serviceCaveats {
		caveats[i].Service = c.Service
		if err := caveats[i].encode(c.Caveat); err != nil {
			return nil, err
		}
	}
	return caveats, nil
}

// Decode deserializes the contents of the Caveat object to obtain a security.Caveat object.
func (c *Caveat) Decode() (security.Caveat, error) {
	var caveat security.Caveat
	if err := vom.NewDecoder(bytes.NewReader(c.Bytes)).Decode(&caveat); err != nil {
		return nil, err
	}
	return caveat, nil
}

// DecodeThirdPartyCaveats decodes the provided Caveat objects into security.ThirdPartyCaveat
// objects. The resulting objects are wrapped in security.ServiceCaveat objects according
// to the services they are bound to.
func DecodeThirdPartyCaveats(caveats []Caveat) (thirdPartyCaveats []security.ServiceCaveat) {
	for _, wireCav := range caveats {
		cav, err := wireCav.Decode()
		if err != nil {
			continue
		}
		tpCav, ok := cav.(security.ThirdPartyCaveat)
		if !ok {
			continue
		}
		thirdPartyCaveats = append(thirdPartyCaveats, security.ServiceCaveat{Service: wireCav.Service, Caveat: tpCav})
	}
	return
}

// Validate verifies the restriction embedded inside the security.Caveat if the label
// is an empty string (indicating a universal caveat) or if the label matches the Name
// of the LocalID present in the provided context.
func (c *Caveat) Validate(ctx security.Context) error {
	// TODO(ataly): Is checking that the localID matches the caveat's Service pattern
	// the right choice here?
	if c.Service != security.AllPrincipals && (ctx.LocalID() == nil || !ctx.LocalID().Match(c.Service)) {
		return nil
	}
	cav, err := c.Decode()
	if err != nil {
		return err
	}
	return cav.Validate(ctx)
}

// -- Helper methods on the wire format for the chain implementation of Identity --

// contentHash returns a SHA256 hash of the contents of the certificate along with the
// provided signature.
func (c *Certificate) contentHash(issuerSignature security.Signature) []byte {
	h := sha256.New()
	tmp := make([]byte, binary.MaxVarintLen64)
	WriteBytes(h, tmp, issuerSignature.R)
	WriteBytes(h, tmp, issuerSignature.S)
	WriteString(h, tmp, c.Name)
	h.Write([]byte{byte(c.PublicKey.Curve)})
	WriteBytes(h, tmp, c.PublicKey.XY)
	binary.Write(h, binary.BigEndian, uint32(len(c.Caveats)))
	for _, cav := range c.Caveats {
		WriteString(h, tmp, string(cav.Service))
		WriteBytes(h, tmp, cav.Bytes)
	}
	return h.Sum(nil)
}

// Sign uses the given Signer to sign the signature of the last certificate in
// the provided PublicID, storing the new signature in the current certificate.
func (c *Certificate) Sign(signer security.Signer, pubID *ChainPublicID) error {
	numCerts := len(pubID.Certificates)
	if numCerts == 0 {
		return errors.New("cannot sign a ChainPublicID with no certificates")
	}
	var err error
	c.Signature, err = signer.Sign(c.contentHash(pubID.Certificates[numCerts-1].Signature))
	return err
}

func (c *Certificate) verify(issuerSignature security.Signature, key *ecdsa.PublicKey) bool {
	var r, s big.Int
	return ecdsa.Verify(key, c.contentHash(issuerSignature), r.SetBytes(c.Signature.R), s.SetBytes(c.Signature.S))
}

// ValidateCaveats verifies if all caveats present on the certificate validate with
// respect to the provided context.
func (c *Certificate) ValidateCaveats(ctx security.Context) error {
	for _, cav := range c.Caveats {
		if err := cav.Validate(ctx); err != nil {
			return err
		}
	}
	return nil
}

// Name returns the chained name obtained by joining all names along the ChainPublicID's
// certificate chain.
func (id *ChainPublicID) Name() string {
	var buf bytes.Buffer
	for i, c := range id.Certificates {
		if i > 0 {
			buf.WriteString(ChainSeparator)
		}
		buf.WriteString(c.Name)
	}
	return buf.String()
}

// VerifyIntegrity verifies that the ChainPublicID has a valid certificate chain, i.e,
// (1) each certificate on the chain has a signature that can be verified using the
// public key specified in the previous certificate, (2) the first certificate's
// signature can be verified using its own public key, and (3) all certificate names
// are valid blessing names.
func (id *ChainPublicID) VerifyIntegrity() error {
	nCerts := len(id.Certificates)
	if nCerts == 0 {
		return ErrNoIntegrity
	}
	verificationKey, err := id.Certificates[0].PublicKey.Decode()
	if err != nil {
		return ErrNoIntegrity
	}
	issuerSignature := security.Signature{}
	for _, c := range id.Certificates {
		if err := ValidateBlessingName(c.Name); err != nil {
			return err
		}
		// TODO(ashankar, ataly): Do we worry about timing attacks by
		// early exiting on invalid certificate?
		if !c.verify(issuerSignature, verificationKey) {
			return ErrNoIntegrity
		}
		if verificationKey, err = c.PublicKey.Decode(); err != nil {
			return ErrNoIntegrity
		}
		issuerSignature = c.Signature
	}
	return nil
}

// ValidateBlessingName verifies if the provided name is fit to be the name of a blessing.
func ValidateBlessingName(name string) error {
	// TODO(ataly, ashankar): Define the list of reserved characters (such as  "*", "#",
	// "/", "\", etc.) and ensure that the check below ensures absence of all of them.
	if name == "" || strings.ContainsAny(name, ChainSeparator) {
		return errInvalidBlessingName(name)
	}
	return nil
}
<|endoftext|>


<commit_before>package softlayer

import (
	"encoding/json"
	"fmt"
	"regexp"
	"strings"

	"github.com/softlayer/softlayer-go/datatypes"
	"github.com/softlayer/softlayer-go/filter"
	"github.com/softlayer/softlayer-go/services"
	"github.com/softlayer/softlayer-go/session"

	"github.com/StackExchange/dnscontrol/v2/models"
	"github.com/StackExchange/dnscontrol/v2/providers"
	"github.com/StackExchange/dnscontrol/v2/providers/diff"
)

// SoftLayer is the protocol handle for this provider.
type SoftLayer struct {
	Session *session.Session
}

var features = providers.DocumentationNotes{
	providers.CanUseSRV:   providers.Can(),
	providers.CanGetZones: providers.Unimplemented(),
}

func init() {
	providers.RegisterDomainServiceProviderType("SOFTLAYER", newReg, features)
}

func newReg(conf map[string]string, _ json.RawMessage) (providers.DNSServiceProvider, error) {
	s := session.New(conf["username"], conf["api_key"], conf["endpoint_url"], conf["timeout"])

	if len(s.UserName) == 0 || len(s.APIKey) == 0 {
		return nil, fmt.Errorf("SoftLayer UserName and APIKey must be provided")
	}

	// s.Debug = true

	api := &SoftLayer{
		Session: s,
	}

	return api, nil
}

// GetNameservers returns the nameservers for a domain.
func (s *SoftLayer) GetNameservers(domain string) ([]*models.Nameserver, error) {
	// Always use the same nameservers for softlayer
	nservers := []string{"ns1.softlayer.com", "ns2.softlayer.com"}
	return models.StringsToNameservers(nservers), nil
}

// GetZoneRecords gets the records of a zone and returns them in RecordConfig format.
func (client *SoftLayer) GetZoneRecords(domain string) (models.Records, error) {
	return nil, fmt.Errorf("not implemented")
	// This enables the get-zones subcommand.
	// Implement this by extracting the code from GetDomainCorrections into
	// a single function.  For most providers this should be relatively easy.
}

// GetDomainCorrections returns corrections to update a domain.
func (s *SoftLayer) GetDomainCorrections(dc *models.DomainConfig) ([]*models.Correction, error) {
	corrections := []*models.Correction{}

	domain, err := s.getDomain(&dc.Name)

	if err != nil {
		return nil, err
	}

	actual, err := s.getExistingRecords(domain)

	if err != nil {
		return nil, err
	}

	_, create, delete, modify := diff.New(dc).IncrementalDiff(actual)

	for _, del := range delete {
		existing := del.Existing.Original.(datatypes.Dns_Domain_ResourceRecord)
		corrections = append(corrections, &models.Correction{
			Msg: del.String(),
			F:   s.deleteRecordFunc(*existing.Id),
		})
	}

	for _, cre := range create {
		corrections = append(corrections, &models.Correction{
			Msg: cre.String(),
			F:   s.createRecordFunc(cre.Desired, domain),
		})
	}

	for _, mod := range modify {
		existing := mod.Existing.Original.(datatypes.Dns_Domain_ResourceRecord)
		corrections = append(corrections, &models.Correction{
			Msg: mod.String(),
			F:   s.updateRecordFunc(&existing, mod.Desired),
		})
	}

	return corrections, nil
}

func (s *SoftLayer) getDomain(name *string) (*datatypes.Dns_Domain, error) {
	domains, err := services.GetAccountService(s.Session).
		Filter(filter.Path("domains.name").Eq(name).Build()).
		Mask("resourceRecords").
		GetDomains()

	if err != nil {
		return nil, err
	}

	if len(domains) == 0 {
		return nil, fmt.Errorf("Didn't find a domain matching %s", *name)
	} else if len(domains) > 1 {
		return nil, fmt.Errorf("Found %d domains matching %s", len(domains), *name)
	}

	return &domains[0], nil
}

func (s *SoftLayer) getExistingRecords(domain *datatypes.Dns_Domain) ([]*models.RecordConfig, error) {
	actual := []*models.RecordConfig{}

	for _, record := range domain.ResourceRecords {
		recType := strings.ToUpper(*record.Type)

		if recType == "SOA" {
			continue
		}

		recConfig := &models.RecordConfig{
			Type:     recType,
			TTL:      uint32(*record.Ttl),
			Original: record,
		}
		recConfig.SetTarget(*record.Data)

		switch recType {
		case "SRV":
			var service, protocol string = "", "_tcp"

			if record.Weight != nil {
				recConfig.SrvWeight = uint16(*record.Weight)
			}
			if record.Port != nil {
				recConfig.SrvPort = uint16(*record.Port)
			}
			if record.Priority != nil {
				recConfig.SrvPriority = uint16(*record.Priority)
			}
			if record.Protocol != nil {
				protocol = *record.Protocol
			}
			if record.Service != nil {
				service = *record.Service
			}
			recConfig.SetLabel(fmt.Sprintf("%s.%s", service, strings.ToLower(protocol)), *domain.Name)
		case "MX":
			if record.MxPriority != nil {
				recConfig.MxPreference = uint16(*record.MxPriority)
			}
			fallthrough
		default:
			recConfig.SetLabel(*record.Host, *domain.Name)
		}

		actual = append(actual, recConfig)
	}

	// Normalize
	models.PostProcessRecords(actual)

	return actual, nil
}

func (s *SoftLayer) createRecordFunc(desired *models.RecordConfig, domain *datatypes.Dns_Domain) func() error {
	var ttl, preference, domainID int = int(desired.TTL), int(desired.MxPreference), *domain.Id
	var weight, priority, port int = int(desired.SrvWeight), int(desired.SrvPriority), int(desired.SrvPort)
	var host, data, newType string = desired.GetLabel(), desired.GetTargetField(), desired.Type
	var err error

	srvRegexp := regexp.MustCompile(`^_(?P<Service>\w+)\.\_(?P<Protocol>\w+)$`)

	return func() error {
		newRecord := datatypes.Dns_Domain_ResourceRecord{
			DomainId: &domainID,
			Ttl:      &ttl,
			Type:     &newType,
			Data:     &data,
			Host:     &host,
		}

		switch newType {
		case "MX":
			service := services.GetDnsDomainResourceRecordMxTypeService(s.Session)

			newRecord.MxPriority = &preference

			newMx := datatypes.Dns_Domain_ResourceRecord_MxType{
				Dns_Domain_ResourceRecord: newRecord,
			}

			_, err = service.CreateObject(&newMx)

		case "SRV":
			service := services.GetDnsDomainResourceRecordSrvTypeService(s.Session)
			result := srvRegexp.FindStringSubmatch(host)

			if len(result) != 3 {
				return fmt.Errorf("SRV Record must match format \"_service._protocol\" not %s", host)
			}

			var serviceName, protocol string = result[1], strings.ToLower(result[2])

			newSrv := datatypes.Dns_Domain_ResourceRecord_SrvType{
				Dns_Domain_ResourceRecord: newRecord,
				Service:                   &serviceName,
				Port:                      &port,
				Priority:                  &priority,
				Protocol:                  &protocol,
				Weight:                    &weight,
			}

			_, err = service.CreateObject(&newSrv)

		default:
			service := services.GetDnsDomainResourceRecordService(s.Session)
			_, err = service.CreateObject(&newRecord)
		}

		return err
	}
}

func (s *SoftLayer) deleteRecordFunc(resID int) func() error {
	// seems to be no problem deleting MX and SRV records via common interface
	return func() error {
		_, err := services.GetDnsDomainResourceRecordService(s.Session).
			Id(resID).
			DeleteObject()

		return err
	}
}

func (s *SoftLayer) updateRecordFunc(existing *datatypes.Dns_Domain_ResourceRecord, desired *models.RecordConfig) func() error {
	var ttl, preference int = int(desired.TTL), int(desired.MxPreference)
	var priority, weight, port int = int(desired.SrvPriority), int(desired.SrvWeight), int(desired.SrvPort)

	return func() error {
		var changes = false
		var err error

		switch desired.Type {
		case "MX":
			service := services.GetDnsDomainResourceRecordMxTypeService(s.Session)
			updated := datatypes.Dns_Domain_ResourceRecord_MxType{}

			label := desired.GetLabel()
			if label != *existing.Host {
				updated.Host = &label
				changes = true
			}

			target := desired.GetTargetField()
			if target != *existing.Data {
				updated.Data = &target
				changes = true
			}

			if ttl != *existing.Ttl {
				updated.Ttl = &ttl
				changes = true
			}

			if preference != *existing.MxPriority {
				updated.MxPriority = &preference
				changes = true
			}

			if !changes {
				return fmt.Errorf("didn't find changes when I expect some")
			}

			_, err = service.Id(*existing.Id).EditObject(&updated)

		case "SRV":
			service := services.GetDnsDomainResourceRecordSrvTypeService(s.Session)
			updated := datatypes.Dns_Domain_ResourceRecord_SrvType{}

			label := desired.GetLabel()
			if label != *existing.Host {
				updated.Host = &label
				changes = true
			}

			target := desired.GetTargetField()
			if target != *existing.Data {
				updated.Data = &target
				changes = true
			}

			if ttl != *existing.Ttl {
				updated.Ttl = &ttl
				changes = true
			}

			if priority != *existing.Priority {
				updated.Priority = &priority
				changes = true
			}

			if weight != *existing.Weight {
				updated.Weight = &weight
				changes = true
			}

			if port != *existing.Port {
				updated.Port = &port
				changes = true
			}

			// TODO: handle service & protocol - or does that just result in a
			// delete and recreate?

			if !changes {
				return fmt.Errorf("didn't find changes when I expect some")
			}

			_, err = service.Id(*existing.Id).EditObject(&updated)

		default:
			service := services.GetDnsDomainResourceRecordService(s.Session)
			updated := datatypes.Dns_Domain_ResourceRecord{}

			label := desired.GetLabel()
			if label != *existing.Host {
				updated.Host = &label
				changes = true
			}

			target := desired.GetTargetField()
			if target != *existing.Data {
				updated.Data = &target
				changes = true
			}

			if ttl != *existing.Ttl {
				updated.Ttl = &ttl
				changes = true
			}

			if !changes {
				return fmt.Errorf("didn't find changes when I expect some")
			}

			_, err = service.Id(*existing.Id).EditObject(&updated)
		}

		return err
	}
}
<commit_msg>SOFTLAYER: Fixed Softlayer TXT Record existence Issue #583 (#659)<commit_after>package softlayer

import (
	"encoding/json"
	"fmt"
	"regexp"
	"strings"

	"github.com/softlayer/softlayer-go/datatypes"
	"github.com/softlayer/softlayer-go/filter"
	"github.com/softlayer/softlayer-go/services"
	"github.com/softlayer/softlayer-go/session"

	"github.com/StackExchange/dnscontrol/v2/models"
	"github.com/StackExchange/dnscontrol/v2/providers"
	"github.com/StackExchange/dnscontrol/v2/providers/diff"
)

// SoftLayer is the protocol handle for this provider.
type SoftLayer struct {
	Session *session.Session
}

var features = providers.DocumentationNotes{
	providers.CanUseSRV:   providers.Can(),
	providers.CanGetZones: providers.Unimplemented(),
}

func init() {
	providers.RegisterDomainServiceProviderType("SOFTLAYER", newReg, features)
}

func newReg(conf map[string]string, _ json.RawMessage) (providers.DNSServiceProvider, error) {
	s := session.New(conf["username"], conf["api_key"], conf["endpoint_url"], conf["timeout"])

	if len(s.UserName) == 0 || len(s.APIKey) == 0 {
		return nil, fmt.Errorf("SoftLayer UserName and APIKey must be provided")
	}

	// s.Debug = true

	api := &SoftLayer{
		Session: s,
	}

	return api, nil
}

// GetNameservers returns the nameservers for a domain.
func (s *SoftLayer) GetNameservers(domain string) ([]*models.Nameserver, error) {
	// Always use the same nameservers for softlayer
	nservers := []string{"ns1.softlayer.com", "ns2.softlayer.com"}
	return models.StringsToNameservers(nservers), nil
}

// GetZoneRecords gets the records of a zone and returns them in RecordConfig format.
func (client *SoftLayer) GetZoneRecords(domain string) (models.Records, error) {
	return nil, fmt.Errorf("not implemented")
	// This enables the get-zones subcommand.
	// Implement this by extracting the code from GetDomainCorrections into
	// a single function.  For most providers this should be relatively easy.
}

// GetDomainCorrections returns corrections to update a domain.
func (s *SoftLayer) GetDomainCorrections(dc *models.DomainConfig) ([]*models.Correction, error) {
	corrections := []*models.Correction{}

	domain, err := s.getDomain(&dc.Name)

	if err != nil {
		return nil, err
	}

	actual, err := s.getExistingRecords(domain)

	if err != nil {
		return nil, err
	}

	_, create, delete, modify := diff.New(dc).IncrementalDiff(actual)

	for _, del := range delete {
		existing := del.Existing.Original.(datatypes.Dns_Domain_ResourceRecord)
		corrections = append(corrections, &models.Correction{
			Msg: del.String(),
			F:   s.deleteRecordFunc(*existing.Id),
		})
	}

	for _, cre := range create {
		corrections = append(corrections, &models.Correction{
			Msg: cre.String(),
			F:   s.createRecordFunc(cre.Desired, domain),
		})
	}

	for _, mod := range modify {
		existing := mod.Existing.Original.(datatypes.Dns_Domain_ResourceRecord)
		corrections = append(corrections, &models.Correction{
			Msg: mod.String(),
			F:   s.updateRecordFunc(&existing, mod.Desired),
		})
	}

	return corrections, nil
}

func (s *SoftLayer) getDomain(name *string) (*datatypes.Dns_Domain, error) {
	domains, err := services.GetAccountService(s.Session).
		Filter(filter.Path("domains.name").Eq(name).Build()).
		Mask("resourceRecords").
		GetDomains()

	if err != nil {
		return nil, err
	}

	if len(domains) == 0 {
		return nil, fmt.Errorf("Didn't find a domain matching %s", *name)
	} else if len(domains) > 1 {
		return nil, fmt.Errorf("Found %d domains matching %s", len(domains), *name)
	}

	return &domains[0], nil
}

func (s *SoftLayer) getExistingRecords(domain *datatypes.Dns_Domain) ([]*models.RecordConfig, error) {
	actual := []*models.RecordConfig{}

	for _, record := range domain.ResourceRecords {
		recType := strings.ToUpper(*record.Type)

		if recType == "SOA" {
			continue
		}

		recConfig := &models.RecordConfig{
			Type:     recType,
			TTL:      uint32(*record.Ttl),
			Original: record,
		}
		recConfig.SetTarget(*record.Data)

		switch recType {
		case "SRV":
			var service, protocol string = "", "_tcp"

			if record.Weight != nil {
				recConfig.SrvWeight = uint16(*record.Weight)
			}
			if record.Port != nil {
				recConfig.SrvPort = uint16(*record.Port)
			}
			if record.Priority != nil {
				recConfig.SrvPriority = uint16(*record.Priority)
			}
			if record.Protocol != nil {
				protocol = *record.Protocol
			}
			if record.Service != nil {
				service = *record.Service
			}
			recConfig.SetLabel(fmt.Sprintf("%s.%s", service, strings.ToLower(protocol)), *domain.Name)
		case "TXT":
			recConfig.TxtStrings = append(recConfig.TxtStrings, *record.Data)
			fallthrough
		case "MX":
			if record.MxPriority != nil {
				recConfig.MxPreference = uint16(*record.MxPriority)
			}
			fallthrough
		default:
			recConfig.SetLabel(*record.Host, *domain.Name)
		}

		actual = append(actual, recConfig)
	}

	// Normalize
	models.PostProcessRecords(actual)

	return actual, nil
}

func (s *SoftLayer) createRecordFunc(desired *models.RecordConfig, domain *datatypes.Dns_Domain) func() error {
	var ttl, preference, domainID int = int(desired.TTL), int(desired.MxPreference), *domain.Id
	var weight, priority, port int = int(desired.SrvWeight), int(desired.SrvPriority), int(desired.SrvPort)
	var host, data, newType string = desired.GetLabel(), desired.GetTargetField(), desired.Type
	var err error

	srvRegexp := regexp.MustCompile(`^_(?P<Service>\w+)\.\_(?P<Protocol>\w+)$`)

	return func() error {
		newRecord := datatypes.Dns_Domain_ResourceRecord{
			DomainId: &domainID,
			Ttl:      &ttl,
			Type:     &newType,
			Data:     &data,
			Host:     &host,
		}

		switch newType {
		case "MX":
			service := services.GetDnsDomainResourceRecordMxTypeService(s.Session)

			newRecord.MxPriority = &preference

			newMx := datatypes.Dns_Domain_ResourceRecord_MxType{
				Dns_Domain_ResourceRecord: newRecord,
			}

			_, err = service.CreateObject(&newMx)

		case "SRV":
			service := services.GetDnsDomainResourceRecordSrvTypeService(s.Session)
			result := srvRegexp.FindStringSubmatch(host)

			if len(result) != 3 {
				return fmt.Errorf("SRV Record must match format \"_service._protocol\" not %s", host)
			}

			var serviceName, protocol string = result[1], strings.ToLower(result[2])

			newSrv := datatypes.Dns_Domain_ResourceRecord_SrvType{
				Dns_Domain_ResourceRecord: newRecord,
				Service:                   &serviceName,
				Port:                      &port,
				Priority:                  &priority,
				Protocol:                  &protocol,
				Weight:                    &weight,
			}

			_, err = service.CreateObject(&newSrv)

		default:
			service := services.GetDnsDomainResourceRecordService(s.Session)
			_, err = service.CreateObject(&newRecord)
		}

		return err
	}
}

func (s *SoftLayer) deleteRecordFunc(resID int) func() error {
	// seems to be no problem deleting MX and SRV records via common interface
	return func() error {
		_, err := services.GetDnsDomainResourceRecordService(s.Session).
			Id(resID).
			DeleteObject()

		return err
	}
}

func (s *SoftLayer) updateRecordFunc(existing *datatypes.Dns_Domain_ResourceRecord, desired *models.RecordConfig) func() error {
	var ttl, preference int = int(desired.TTL), int(desired.MxPreference)
	var priority, weight, port int = int(desired.SrvPriority), int(desired.SrvWeight), int(desired.SrvPort)

	return func() error {
		var changes = false
		var err error

		switch desired.Type {
		case "MX":
			service := services.GetDnsDomainResourceRecordMxTypeService(s.Session)
			updated := datatypes.Dns_Domain_ResourceRecord_MxType{}

			label := desired.GetLabel()
			if label != *existing.Host {
				updated.Host = &label
				changes = true
			}

			target := desired.GetTargetField()
			if target != *existing.Data {
				updated.Data = &target
				changes = true
			}

			if ttl != *existing.Ttl {
				updated.Ttl = &ttl
				changes = true
			}

			if preference != *existing.MxPriority {
				updated.MxPriority = &preference
				changes = true
			}

			if !changes {
				return fmt.Errorf("didn't find changes when I expect some")
			}

			_, err = service.Id(*existing.Id).EditObject(&updated)

		case "SRV":
			service := services.GetDnsDomainResourceRecordSrvTypeService(s.Session)
			updated := datatypes.Dns_Domain_ResourceRecord_SrvType{}

			label := desired.GetLabel()
			if label != *existing.Host {
				updated.Host = &label
				changes = true
			}

			target := desired.GetTargetField()
			if target != *existing.Data {
				updated.Data = &target
				changes = true
			}

			if ttl != *existing.Ttl {
				updated.Ttl = &ttl
				changes = true
			}

			if priority != *existing.Priority {
				updated.Priority = &priority
				changes = true
			}

			if weight != *existing.Weight {
				updated.Weight = &weight
				changes = true
			}

			if port != *existing.Port {
				updated.Port = &port
				changes = true
			}

			// TODO: handle service & protocol - or does that just result in a
			// delete and recreate?

			if !changes {
				return fmt.Errorf("didn't find changes when I expect some")
			}

			_, err = service.Id(*existing.Id).EditObject(&updated)

		default:
			service := services.GetDnsDomainResourceRecordService(s.Session)
			updated := datatypes.Dns_Domain_ResourceRecord{}

			label := desired.GetLabel()
			if label != *existing.Host {
				updated.Host = &label
				changes = true
			}

			target := desired.GetTargetField()
			if target != *existing.Data {
				updated.Data = &target
				changes = true
			}

			if ttl != *existing.Ttl {
				updated.Ttl = &ttl
				changes = true
			}

			if !changes {
				return fmt.Errorf("didn't find changes when I expect some")
			}

			_, err = service.Id(*existing.Id).EditObject(&updated)
		}

		return err
	}
}
<|endoftext|>


<commit_before>// Copyright 2015 Google Inc. All Rights Reserved.
// Author: jacobsa@google.com (Aaron Jacobs)

package gcsproxy_test

import (
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"testing"

	"github.com/jacobsa/gcloud/gcs"
	"github.com/jacobsa/gcloud/gcs/mock_gcs"
	"github.com/jacobsa/gcsfuse/gcsproxy"
	. "github.com/jacobsa/oglematchers"
	"github.com/jacobsa/oglemock"
	. "github.com/jacobsa/ogletest"
	"golang.org/x/net/context"
	"google.golang.org/cloud/storage"
)

func TestOgletest(t *testing.T) { RunTests(t) }

////////////////////////////////////////////////////////////////////////
// Helpers
////////////////////////////////////////////////////////////////////////

// An oglemock.Matcher that accepts a predicate function and a description,
// making it easy to make anonymous matcher types.
type predicateMatcher struct {
	Desc      string
	Predicate func(interface{}) error
}

var _ Matcher = &predicateMatcher{}

func (m *predicateMatcher) Matches(candidate interface{}) error {
	return m.Predicate(candidate)
}

func (m *predicateMatcher) Description() string {
	return m.Desc
}

func nameIs(name string) Matcher {
	return &predicateMatcher{
		Desc: fmt.Sprintf("Name is: %s", name),
		Predicate: func(candidate interface{}) error {
			req := candidate.(*gcs.CreateObjectRequest)
			if req.Attrs.Name != name {
				return errors.New("")
			}

			return nil
		},
	}
}

func contentsAre(s string) Matcher {
	return &predicateMatcher{
		Desc: fmt.Sprintf("Object contents are: %s", s),
		Predicate: func(candidate interface{}) error {
			// Snarf the contents.
			req := candidate.(*gcs.CreateObjectRequest)
			contents, err := ioutil.ReadAll(req.Contents)
			if err != nil {
				panic(err)
			}

			// Compare
			if string(contents) != s {
				return errors.New("")
			}

			return nil
		},
	}
}

////////////////////////////////////////////////////////////////////////
// Invariant-checking object proxy
////////////////////////////////////////////////////////////////////////

// A wrapper around ObjectProxy that calls CheckInvariants whenever invariants
// should hold. For catching logic errors early in the test.
type checkingObjectProxy struct {
	wrapped *gcsproxy.ObjectProxy
}

func (op *checkingObjectProxy) NoteLatest(o *storage.Object) error {
	op.wrapped.CheckInvariants()
	defer op.wrapped.CheckInvariants()
	return op.wrapped.NoteLatest(o)
}

func (op *checkingObjectProxy) Size() (uint64, error) {
	op.wrapped.CheckInvariants()
	defer op.wrapped.CheckInvariants()
	return op.wrapped.Size()
}

func (op *checkingObjectProxy) ReadAt(b []byte, o int64) (int, error) {
	op.wrapped.CheckInvariants()
	defer op.wrapped.CheckInvariants()
	return op.wrapped.ReadAt(context.Background(), b, o)
}

func (op *checkingObjectProxy) WriteAt(b []byte, o int64) (int, error) {
	op.wrapped.CheckInvariants()
	defer op.wrapped.CheckInvariants()
	return op.wrapped.WriteAt(context.Background(), b, o)
}

func (op *checkingObjectProxy) Truncate(n uint64) error {
	op.wrapped.CheckInvariants()
	defer op.wrapped.CheckInvariants()
	return op.wrapped.Truncate(context.Background(), n)
}

func (op *checkingObjectProxy) Sync() (*storage.Object, error) {
	op.wrapped.CheckInvariants()
	defer op.wrapped.CheckInvariants()
	return op.wrapped.Sync(context.Background())
}

////////////////////////////////////////////////////////////////////////
// Boilerplate
////////////////////////////////////////////////////////////////////////

type ObjectProxyTest struct {
	objectName string
	bucket     mock_gcs.MockBucket
	op         checkingObjectProxy
}

var _ SetUpInterface = &ObjectProxyTest{}

func (t *ObjectProxyTest) SetUp(ti *TestInfo) {
	t.objectName = "some/object"
	t.bucket = mock_gcs.NewMockBucket(ti.MockController, "bucket")

	var err error
	t.op.wrapped, err = gcsproxy.NewObjectProxy(
		t.bucket,
		t.objectName)

	if err != nil {
		panic(err)
	}
}

////////////////////////////////////////////////////////////////////////
// No source object
////////////////////////////////////////////////////////////////////////

// A test whose initial conditions are a fresh object proxy without a source
// object set.
type NoSourceObjectTest struct {
	ObjectProxyTest
}

var _ SetUpInterface = &NoSourceObjectTest{}

func init() { RegisterTestSuite(&NoSourceObjectTest{}) }

func (t *NoSourceObjectTest) NoteLatest_NegativeSize() {
	o := &storage.Object{
		Name:       t.objectName,
		Generation: 1234,
		Size:       -1,
	}

	err := t.op.NoteLatest(o)

	AssertNe(nil, err)
	ExpectThat(err, Error(HasSubstr("size")))
	ExpectThat(err, Error(HasSubstr("-1")))
}

func (t *NoSourceObjectTest) NoteLatest_WrongName() {
	o := &storage.Object{
		Name:       t.objectName + "foo",
		Generation: 1234,
		Size:       0,
	}

	err := t.op.NoteLatest(o)

	AssertNe(nil, err)
	ExpectThat(err, Error(HasSubstr("name")))
	ExpectThat(err, Error(HasSubstr("foo")))
}

func (t *NoSourceObjectTest) Size_InitialState() {
	size, err := t.op.Size()

	AssertEq(nil, err)
	ExpectEq(0, size)
}

func (t *NoSourceObjectTest) Size_AfterTruncatingToZero() {
	var err error

	// Truncate
	err = t.op.Truncate(0)
	AssertEq(nil, err)

	// Size
	size, err := t.op.Size()

	AssertEq(nil, err)
	ExpectEq(0, size)
}

func (t *NoSourceObjectTest) Size_AfterTruncatingToNonZero() {
	var err error

	// Truncate
	err = t.op.Truncate(123)
	AssertEq(nil, err)

	// Size
	size, err := t.op.Size()

	AssertEq(nil, err)
	ExpectEq(123, size)
}

func (t *NoSourceObjectTest) Size_AfterReading() {
	var err error

	// Read
	buf := make([]byte, 0)
	n, err := t.op.ReadAt(buf, 0)

	AssertEq(nil, err)
	AssertEq(0, n)

	// Size
	size, err := t.op.Size()

	AssertEq(nil, err)
	ExpectEq(0, size)
}

func (t *NoSourceObjectTest) Read_InitialState() {
	type testCase struct {
		offset      int64
		size        int
		expectedErr error
		expectedN   int
	}

	testCases := []testCase{
		// Empty ranges
		testCase{0, 0, nil, 0},
		testCase{17, 0, nil, 0},

		// Non-empty ranges
		testCase{0, 10, io.EOF, 0},
		testCase{17, 10, io.EOF, 0},
	}

	for _, tc := range testCases {
		buf := make([]byte, tc.size)
		n, err := t.op.ReadAt(buf, tc.offset)

		AssertEq(tc.expectedErr, err, "Test case: %v", tc)
		AssertEq(tc.expectedN, n, "Test case: %v", tc)
	}
}

func (t *NoSourceObjectTest) WriteToEndOfObjectThenRead() {
	var n int
	var err error
	var buf []byte

	// Extend the object by writing twice.
	n, err = t.op.WriteAt([]byte("taco"), 0)
	AssertEq(nil, err)
	AssertEq(len("taco"), n)

	n, err = t.op.WriteAt([]byte("burrito"), int64(len("taco")))
	AssertEq(nil, err)
	AssertEq(len("burrito"), n)

	// Read the whole thing.
	buf = make([]byte, 1024)
	n, err = t.op.ReadAt(buf, 0)

	AssertEq(io.EOF, err)
	ExpectEq(len("tacoburrito"), n)
	ExpectEq("tacoburrito", string(buf[:n]))

	// Read a range in the middle.
	buf = make([]byte, 4)
	n, err = t.op.ReadAt(buf, 3)

	AssertEq(nil, err)
	ExpectEq(4, n)
	ExpectEq("obur", string(buf[:n]))
}

func (t *NoSourceObjectTest) WritePastEndOfObjectThenRead() {
	var n int
	var err error
	var buf []byte

	// Extend the object by writing past its end.
	n, err = t.op.WriteAt([]byte("taco"), 2)
	AssertEq(nil, err)
	AssertEq(len("taco"), n)

	// Check its size.
	size, err := t.op.Size()
	AssertEq(nil, err)
	ExpectEq(2+len("taco"), size)

	// Read the whole thing.
	buf = make([]byte, 1024)
	n, err = t.op.ReadAt(buf, 0)

	AssertEq(io.EOF, err)
	ExpectEq(2+len("taco"), n)
	ExpectEq("\x00\x00taco", string(buf[:n]))

	// Read a range in the middle.
	buf = make([]byte, 4)
	n, err = t.op.ReadAt(buf, 1)

	AssertEq(nil, err)
	ExpectEq(4, n)
	ExpectEq("\x00tac", string(buf[:n]))
}

func (t *NoSourceObjectTest) WriteWithinObjectThenRead() {
	var n int
	var err error
	var buf []byte

	// Write several bytes to extend the object.
	n, err = t.op.WriteAt([]byte("00000"), 0)
	AssertEq(nil, err)
	AssertEq(len("00000"), n)

	// Overwrite some in the middle.
	n, err = t.op.WriteAt([]byte("11"), 1)
	AssertEq(nil, err)
	AssertEq(len("11"), n)

	// Read the whole thing.
	buf = make([]byte, 1024)
	n, err = t.op.ReadAt(buf, 0)

	AssertEq(io.EOF, err)
	ExpectEq(len("01100"), n)
	ExpectEq("01100", string(buf[:n]))
}

func (t *NoSourceObjectTest) GrowByTruncating() {
	var n int
	var err error
	var buf []byte

	// Truncate
	err = t.op.Truncate(4)
	AssertEq(nil, err)

	// Size
	size, err := t.op.Size()
	AssertEq(nil, err)
	ExpectEq(4, size)

	// Read the whole thing.
	buf = make([]byte, 1024)
	n, err = t.op.ReadAt(buf, 0)

	AssertEq(io.EOF, err)
	ExpectEq(4, n)
	ExpectEq("\x00\x00\x00\x00", string(buf[:n]))
}

func (t *NoSourceObjectTest) Sync_NoInteractions() {
	// CreateObject -- should receive an empty string.
	ExpectCall(t.bucket, "CreateObject")(
		Any(),
		AllOf(nameIs(t.objectName), contentsAre(""))).
		WillOnce(oglemock.Return(nil, errors.New("")))

	// Sync
	t.op.Sync()
}

func (t *NoSourceObjectTest) Sync_ReadCallsOnly() {
	// Make a Read call
	buf := make([]byte, 0)
	n, err := t.op.ReadAt(buf, 0)

	AssertEq(nil, err)
	AssertEq(0, n)

	// CreateObject -- should receive an empty string.
	ExpectCall(t.bucket, "CreateObject")(
		Any(),
		AllOf(nameIs(t.objectName), contentsAre(""))).
		WillOnce(oglemock.Return(nil, errors.New("")))

	// Sync
	t.op.Sync()
}

func (t *NoSourceObjectTest) Sync_AfterWriting() {
	AssertTrue(false, "TODO")
}

func (t *NoSourceObjectTest) Sync_AfterTruncating() {
	AssertTrue(false, "TODO")
}

func (t *NoSourceObjectTest) Sync_CreateObjectFails() {
	AssertTrue(false, "TODO")
}

func (t *NoSourceObjectTest) Sync_Successful() {
	AssertTrue(false, "TODO")
}

func (t *NoSourceObjectTest) SyncTwice() {
	AssertTrue(false, "TODO")
}

////////////////////////////////////////////////////////////////////////
// Source object present
////////////////////////////////////////////////////////////////////////

// A test whose initial conditions are an object proxy branching from a source
// object in the bucket.
type SourceObjectPresentTest struct {
	ObjectProxyTest
	sourceObject storage.Object
}

var _ SetUpInterface = &SourceObjectPresentTest{}

func init() { RegisterTestSuite(&SourceObjectPresentTest{}) }

func (t *SourceObjectPresentTest) SetUp(ti *TestInfo) {
	t.ObjectProxyTest.SetUp(ti)

	// Set up the source object.
	t.sourceObject = storage.Object{
		Name:       t.objectName,
		Generation: 123,
		Size:       456,
	}

	if err := t.op.NoteLatest(&t.sourceObject); err != nil {
		panic(err)
	}
}

func (t *SourceObjectPresentTest) DoesFoo() {
	AssertTrue(false, "TODO")
}
<commit_msg>NoSourceObjectTest.Sync_AfterWriting<commit_after>// Copyright 2015 Google Inc. All Rights Reserved.
// Author: jacobsa@google.com (Aaron Jacobs)

package gcsproxy_test

import (
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"testing"

	"github.com/jacobsa/gcloud/gcs"
	"github.com/jacobsa/gcloud/gcs/mock_gcs"
	"github.com/jacobsa/gcsfuse/gcsproxy"
	. "github.com/jacobsa/oglematchers"
	"github.com/jacobsa/oglemock"
	. "github.com/jacobsa/ogletest"
	"golang.org/x/net/context"
	"google.golang.org/cloud/storage"
)

func TestOgletest(t *testing.T) { RunTests(t) }

////////////////////////////////////////////////////////////////////////
// Helpers
////////////////////////////////////////////////////////////////////////

// An oglemock.Matcher that accepts a predicate function and a description,
// making it easy to make anonymous matcher types.
type predicateMatcher struct {
	Desc      string
	Predicate func(interface{}) error
}

var _ Matcher = &predicateMatcher{}

func (m *predicateMatcher) Matches(candidate interface{}) error {
	return m.Predicate(candidate)
}

func (m *predicateMatcher) Description() string {
	return m.Desc
}

func nameIs(name string) Matcher {
	return &predicateMatcher{
		Desc: fmt.Sprintf("Name is: %s", name),
		Predicate: func(candidate interface{}) error {
			req := candidate.(*gcs.CreateObjectRequest)
			if req.Attrs.Name != name {
				return errors.New("")
			}

			return nil
		},
	}
}

func contentsAre(s string) Matcher {
	return &predicateMatcher{
		Desc: fmt.Sprintf("Object contents are: %s", s),
		Predicate: func(candidate interface{}) error {
			// Snarf the contents.
			req := candidate.(*gcs.CreateObjectRequest)
			contents, err := ioutil.ReadAll(req.Contents)
			if err != nil {
				panic(err)
			}

			// Compare
			if string(contents) != s {
				return errors.New("")
			}

			return nil
		},
	}
}

////////////////////////////////////////////////////////////////////////
// Invariant-checking object proxy
////////////////////////////////////////////////////////////////////////

// A wrapper around ObjectProxy that calls CheckInvariants whenever invariants
// should hold. For catching logic errors early in the test.
type checkingObjectProxy struct {
	wrapped *gcsproxy.ObjectProxy
}

func (op *checkingObjectProxy) NoteLatest(o *storage.Object) error {
	op.wrapped.CheckInvariants()
	defer op.wrapped.CheckInvariants()
	return op.wrapped.NoteLatest(o)
}

func (op *checkingObjectProxy) Size() (uint64, error) {
	op.wrapped.CheckInvariants()
	defer op.wrapped.CheckInvariants()
	return op.wrapped.Size()
}

func (op *checkingObjectProxy) ReadAt(b []byte, o int64) (int, error) {
	op.wrapped.CheckInvariants()
	defer op.wrapped.CheckInvariants()
	return op.wrapped.ReadAt(context.Background(), b, o)
}

func (op *checkingObjectProxy) WriteAt(b []byte, o int64) (int, error) {
	op.wrapped.CheckInvariants()
	defer op.wrapped.CheckInvariants()
	return op.wrapped.WriteAt(context.Background(), b, o)
}

func (op *checkingObjectProxy) Truncate(n uint64) error {
	op.wrapped.CheckInvariants()
	defer op.wrapped.CheckInvariants()
	return op.wrapped.Truncate(context.Background(), n)
}

func (op *checkingObjectProxy) Sync() (*storage.Object, error) {
	op.wrapped.CheckInvariants()
	defer op.wrapped.CheckInvariants()
	return op.wrapped.Sync(context.Background())
}

////////////////////////////////////////////////////////////////////////
// Boilerplate
////////////////////////////////////////////////////////////////////////

type ObjectProxyTest struct {
	objectName string
	bucket     mock_gcs.MockBucket
	op         checkingObjectProxy
}

var _ SetUpInterface = &ObjectProxyTest{}

func (t *ObjectProxyTest) SetUp(ti *TestInfo) {
	t.objectName = "some/object"
	t.bucket = mock_gcs.NewMockBucket(ti.MockController, "bucket")

	var err error
	t.op.wrapped, err = gcsproxy.NewObjectProxy(
		t.bucket,
		t.objectName)

	if err != nil {
		panic(err)
	}
}

////////////////////////////////////////////////////////////////////////
// No source object
////////////////////////////////////////////////////////////////////////

// A test whose initial conditions are a fresh object proxy without a source
// object set.
type NoSourceObjectTest struct {
	ObjectProxyTest
}

var _ SetUpInterface = &NoSourceObjectTest{}

func init() { RegisterTestSuite(&NoSourceObjectTest{}) }

func (t *NoSourceObjectTest) NoteLatest_NegativeSize() {
	o := &storage.Object{
		Name:       t.objectName,
		Generation: 1234,
		Size:       -1,
	}

	err := t.op.NoteLatest(o)

	AssertNe(nil, err)
	ExpectThat(err, Error(HasSubstr("size")))
	ExpectThat(err, Error(HasSubstr("-1")))
}

func (t *NoSourceObjectTest) NoteLatest_WrongName() {
	o := &storage.Object{
		Name:       t.objectName + "foo",
		Generation: 1234,
		Size:       0,
	}

	err := t.op.NoteLatest(o)

	AssertNe(nil, err)
	ExpectThat(err, Error(HasSubstr("name")))
	ExpectThat(err, Error(HasSubstr("foo")))
}

func (t *NoSourceObjectTest) Size_InitialState() {
	size, err := t.op.Size()

	AssertEq(nil, err)
	ExpectEq(0, size)
}

func (t *NoSourceObjectTest) Size_AfterTruncatingToZero() {
	var err error

	// Truncate
	err = t.op.Truncate(0)
	AssertEq(nil, err)

	// Size
	size, err := t.op.Size()

	AssertEq(nil, err)
	ExpectEq(0, size)
}

func (t *NoSourceObjectTest) Size_AfterTruncatingToNonZero() {
	var err error

	// Truncate
	err = t.op.Truncate(123)
	AssertEq(nil, err)

	// Size
	size, err := t.op.Size()

	AssertEq(nil, err)
	ExpectEq(123, size)
}

func (t *NoSourceObjectTest) Size_AfterReading() {
	var err error

	// Read
	buf := make([]byte, 0)
	n, err := t.op.ReadAt(buf, 0)

	AssertEq(nil, err)
	AssertEq(0, n)

	// Size
	size, err := t.op.Size()

	AssertEq(nil, err)
	ExpectEq(0, size)
}

func (t *NoSourceObjectTest) Read_InitialState() {
	type testCase struct {
		offset      int64
		size        int
		expectedErr error
		expectedN   int
	}

	testCases := []testCase{
		// Empty ranges
		testCase{0, 0, nil, 0},
		testCase{17, 0, nil, 0},

		// Non-empty ranges
		testCase{0, 10, io.EOF, 0},
		testCase{17, 10, io.EOF, 0},
	}

	for _, tc := range testCases {
		buf := make([]byte, tc.size)
		n, err := t.op.ReadAt(buf, tc.offset)

		AssertEq(tc.expectedErr, err, "Test case: %v", tc)
		AssertEq(tc.expectedN, n, "Test case: %v", tc)
	}
}

func (t *NoSourceObjectTest) WriteToEndOfObjectThenRead() {
	var n int
	var err error
	var buf []byte

	// Extend the object by writing twice.
	n, err = t.op.WriteAt([]byte("taco"), 0)
	AssertEq(nil, err)
	AssertEq(len("taco"), n)

	n, err = t.op.WriteAt([]byte("burrito"), int64(len("taco")))
	AssertEq(nil, err)
	AssertEq(len("burrito"), n)

	// Read the whole thing.
	buf = make([]byte, 1024)
	n, err = t.op.ReadAt(buf, 0)

	AssertEq(io.EOF, err)
	ExpectEq(len("tacoburrito"), n)
	ExpectEq("tacoburrito", string(buf[:n]))

	// Read a range in the middle.
	buf = make([]byte, 4)
	n, err = t.op.ReadAt(buf, 3)

	AssertEq(nil, err)
	ExpectEq(4, n)
	ExpectEq("obur", string(buf[:n]))
}

func (t *NoSourceObjectTest) WritePastEndOfObjectThenRead() {
	var n int
	var err error
	var buf []byte

	// Extend the object by writing past its end.
	n, err = t.op.WriteAt([]byte("taco"), 2)
	AssertEq(nil, err)
	AssertEq(len("taco"), n)

	// Check its size.
	size, err := t.op.Size()
	AssertEq(nil, err)
	ExpectEq(2+len("taco"), size)

	// Read the whole thing.
	buf = make([]byte, 1024)
	n, err = t.op.ReadAt(buf, 0)

	AssertEq(io.EOF, err)
	ExpectEq(2+len("taco"), n)
	ExpectEq("\x00\x00taco", string(buf[:n]))

	// Read a range in the middle.
	buf = make([]byte, 4)
	n, err = t.op.ReadAt(buf, 1)

	AssertEq(nil, err)
	ExpectEq(4, n)
	ExpectEq("\x00tac", string(buf[:n]))
}

func (t *NoSourceObjectTest) WriteWithinObjectThenRead() {
	var n int
	var err error
	var buf []byte

	// Write several bytes to extend the object.
	n, err = t.op.WriteAt([]byte("00000"), 0)
	AssertEq(nil, err)
	AssertEq(len("00000"), n)

	// Overwrite some in the middle.
	n, err = t.op.WriteAt([]byte("11"), 1)
	AssertEq(nil, err)
	AssertEq(len("11"), n)

	// Read the whole thing.
	buf = make([]byte, 1024)
	n, err = t.op.ReadAt(buf, 0)

	AssertEq(io.EOF, err)
	ExpectEq(len("01100"), n)
	ExpectEq("01100", string(buf[:n]))
}

func (t *NoSourceObjectTest) GrowByTruncating() {
	var n int
	var err error
	var buf []byte

	// Truncate
	err = t.op.Truncate(4)
	AssertEq(nil, err)

	// Size
	size, err := t.op.Size()
	AssertEq(nil, err)
	ExpectEq(4, size)

	// Read the whole thing.
	buf = make([]byte, 1024)
	n, err = t.op.ReadAt(buf, 0)

	AssertEq(io.EOF, err)
	ExpectEq(4, n)
	ExpectEq("\x00\x00\x00\x00", string(buf[:n]))
}

func (t *NoSourceObjectTest) Sync_NoInteractions() {
	// CreateObject -- should receive an empty string.
	ExpectCall(t.bucket, "CreateObject")(
		Any(),
		AllOf(nameIs(t.objectName), contentsAre(""))).
		WillOnce(oglemock.Return(nil, errors.New("")))

	// Sync
	t.op.Sync()
}

func (t *NoSourceObjectTest) Sync_ReadCallsOnly() {
	// Make a Read call
	buf := make([]byte, 0)
	n, err := t.op.ReadAt(buf, 0)

	AssertEq(nil, err)
	AssertEq(0, n)

	// CreateObject -- should receive an empty string.
	ExpectCall(t.bucket, "CreateObject")(
		Any(),
		AllOf(nameIs(t.objectName), contentsAre(""))).
		WillOnce(oglemock.Return(nil, errors.New("")))

	// Sync
	t.op.Sync()
}

func (t *NoSourceObjectTest) Sync_AfterWriting() {
	var n int
	var err error

	// Write some data.
	n, err = t.op.WriteAt([]byte("taco"), 0)
	AssertEq(nil, err)
	AssertEq(len("taco"), n)

	n, err = t.op.WriteAt([]byte("burrito"), int64(len("taco")))
	AssertEq(nil, err)
	AssertEq(len("burrito"), n)

	// CreateObject -- should receive the contents we wrote above.
	ExpectCall(t.bucket, "CreateObject")(
		Any(),
		AllOf(nameIs(t.objectName), contentsAre("tacoburrito"))).
		WillOnce(oglemock.Return(nil, errors.New("")))

	// Sync
	t.op.Sync()
}

func (t *NoSourceObjectTest) Sync_AfterTruncating() {
	AssertTrue(false, "TODO")
}

func (t *NoSourceObjectTest) Sync_CreateObjectFails() {
	AssertTrue(false, "TODO")
}

func (t *NoSourceObjectTest) Sync_Successful() {
	AssertTrue(false, "TODO")
}

func (t *NoSourceObjectTest) SyncTwice() {
	AssertTrue(false, "TODO")
}

////////////////////////////////////////////////////////////////////////
// Source object present
////////////////////////////////////////////////////////////////////////

// A test whose initial conditions are an object proxy branching from a source
// object in the bucket.
type SourceObjectPresentTest struct {
	ObjectProxyTest
	sourceObject storage.Object
}

var _ SetUpInterface = &SourceObjectPresentTest{}

func init() { RegisterTestSuite(&SourceObjectPresentTest{}) }

func (t *SourceObjectPresentTest) SetUp(ti *TestInfo) {
	t.ObjectProxyTest.SetUp(ti)

	// Set up the source object.
	t.sourceObject = storage.Object{
		Name:       t.objectName,
		Generation: 123,
		Size:       456,
	}

	if err := t.op.NoteLatest(&t.sourceObject); err != nil {
		panic(err)
	}
}

func (t *SourceObjectPresentTest) DoesFoo() {
	AssertTrue(false, "TODO")
}
<|endoftext|>


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

package integration_test

import (
	"fmt"
	"io/ioutil"
	"os"
	"os/exec"
	"path"
	"runtime"
	"testing"

	"github.com/googlecloudplatform/gcsfuse/internal/canned"
	. "github.com/jacobsa/oglematchers"
	. "github.com/jacobsa/ogletest"
)

func TestMountHelper(t *testing.T) { RunTests(t) }

////////////////////////////////////////////////////////////////////////
// Boilerplate
////////////////////////////////////////////////////////////////////////

type MountHelperTest struct {
	// Path to the mount(8) helper binary.
	helperPath string

	// A temporary directory into which a file system may be mounted. Removed in
	// TearDown.
	dir string
}

var _ SetUpInterface = &MountHelperTest{}
var _ TearDownInterface = &MountHelperTest{}

func init() { RegisterTestSuite(&MountHelperTest{}) }

func (t *MountHelperTest) SetUp(_ *TestInfo) {
	var err error

	// Set up the appropriate helper path.
	switch runtime.GOOS {
	case "darwin":
		t.helperPath = path.Join(gBuildDir, "sbin/mount_gcsfuse")

	case "linux":
		t.helperPath = path.Join(gBuildDir, "sbin/mount.gcsfuse")

	default:
		AddFailure("Don't know how to deal with OS: %q", runtime.GOOS)
		AbortTest()
	}

	// Set up the temporary directory.
	t.dir, err = ioutil.TempDir("", "mount_helper_test")
	AssertEq(nil, err)
}

func (t *MountHelperTest) TearDown() {
	err := os.Remove(t.dir)
	AssertEq(nil, err)
}

func (t *MountHelperTest) mountHelperCommand(args []string) (cmd *exec.Cmd) {
	cmd = exec.Command(t.helperPath)
	cmd.Args = append(cmd.Args, args...)
	cmd.Env = []string{
		fmt.Sprintf("PATH=%s", path.Join(gBuildDir, "bin")),
	}

	return
}

func (t *MountHelperTest) mount(args []string) (err error) {
	cmd := t.mountHelperCommand(args)

	output, err := cmd.CombinedOutput()
	if err != nil {
		err = fmt.Errorf("CombinedOutput: %v\nOutput:\n%s", err, output)
		return
	}

	return
}

////////////////////////////////////////////////////////////////////////
// Tests
////////////////////////////////////////////////////////////////////////

func (t *MountHelperTest) BadUsage() {
	testCases := []struct {
		args           []string
		expectedOutput string
	}{
		// Too few args
		0: {
			[]string{canned.FakeBucketName},
			"two positional arguments",
		},

		// Too many args
		1: {
			[]string{canned.FakeBucketName, "a", "b"},
			"Unexpected arg 3",
		},

		// Trailing -o
		2: {
			[]string{canned.FakeBucketName, "a", "-o"},
			"Unexpected -o",
		},
	}

	// Run each test case.
	for i, tc := range testCases {
		cmd := t.mountHelperCommand(tc.args)

		output, err := cmd.CombinedOutput()
		ExpectThat(err, Error(HasSubstr("exit status")), "case %d", i)
		ExpectThat(string(output), MatchesRegexp(tc.expectedOutput), "case %d", i)
	}
}

func (t *MountHelperTest) SuccessfulMount() {
	var err error
	var fi os.FileInfo

	// Mount.
	args := []string{canned.FakeBucketName, t.dir}

	err = t.mount(args)
	AssertEq(nil, err)
	defer unmount(t.dir)

	// Check that the file system is available.
	fi, err = os.Lstat(path.Join(t.dir, canned.TopLevelFile))
	AssertEq(nil, err)
	ExpectEq(os.FileMode(0644), fi.Mode())
	ExpectEq(len(canned.TopLevelFile_Contents), fi.Size())
}

func (t *MountHelperTest) RelativeMountPoint() {
	var err error
	var fi os.FileInfo

	// Mount with a relative mount point.
	cmd := t.mountHelperCommand([]string{
		canned.FakeBucketName,
		path.Base(t.dir),
	})

	cmd.Dir = path.Dir(t.dir)

	output, err := cmd.CombinedOutput()
	AssertEq(nil, err, "output:\n%s", output)

	defer unmount(t.dir)

	// The file system should be available.
	fi, err = os.Lstat(path.Join(t.dir, canned.TopLevelFile))
	AssertEq(nil, err)
	ExpectEq(os.FileMode(0644), fi.Mode())
	ExpectEq(len(canned.TopLevelFile_Contents), fi.Size())
}

func (t *MountHelperTest) ReadOnlyMode() {
	var err error

	// Mount.
	args := []string{"-o", "ro", canned.FakeBucketName, t.dir}

	err = t.mount(args)
	AssertEq(nil, err)
	defer unmount(t.dir)

	// Writing to the file system should fail.
	err = ioutil.WriteFile(path.Join(t.dir, "blah"), []byte{}, 0400)
	ExpectThat(err, Error(HasSubstr("read-only")))
}

func (t *MountHelperTest) ExtraneousOptions() {
	var err error
	var fi os.FileInfo

	// Mount with extra junk that shouldn't be passed on.
	args := []string{
		"-o", "noauto,nouser,auto,user",
		canned.FakeBucketName,
		t.dir,
	}

	err = t.mount(args)
	AssertEq(nil, err)
	defer unmount(t.dir)

	// Check that the file system is available.
	fi, err = os.Lstat(path.Join(t.dir, canned.TopLevelFile))
	AssertEq(nil, err)
	ExpectEq(os.FileMode(0644), fi.Mode())
	ExpectEq(len(canned.TopLevelFile_Contents), fi.Size())
}

func (t *MountHelperTest) LinuxArgumentOrder() {
	var err error

	// Linux places the options at the end.
	args := []string{canned.FakeBucketName, t.dir, "-o", "ro"}

	err = t.mount(args)
	AssertEq(nil, err)
	defer unmount(t.dir)

	// Writing to the file system should fail.
	err = ioutil.WriteFile(path.Join(t.dir, "blah"), []byte{}, 0400)
	ExpectThat(err, Error(HasSubstr("read-only")))
}

func (t *MountHelperTest) FuseSubtype() {
	var err error
	var fi os.FileInfo

	// This test isn't relevant except on Linux.
	if runtime.GOOS != "linux" {
		return
	}

	// Mount using the tool that would be invoked by ~mount -t fuse.gcsfuse`.
	t.helperPath = path.Join(gBuildDir, "sbin/mount.fuse.gcsfuse")
	args := []string{canned.FakeBucketName, t.dir}

	err = t.mount(args)
	AssertEq(nil, err)
	defer unmount(t.dir)

	// Check that the file system is available.
	fi, err = os.Lstat(path.Join(t.dir, canned.TopLevelFile))
	AssertEq(nil, err)
	ExpectEq(os.FileMode(0644), fi.Mode())
	ExpectEq(len(canned.TopLevelFile_Contents), fi.Size())
}

func (t *MountHelperTest) ModeOptions() {
	var err error
	var fi os.FileInfo

	// Mount.
	args := []string{
		"-o", "dir_mode=754",
		"-o", "file_mode=612",
		canned.FakeBucketName, t.dir,
	}

	err = t.mount(args)
	AssertEq(nil, err)
	defer unmount(t.dir)

	// Stat the directory.
	fi, err = os.Lstat(path.Join(t.dir, canned.TopLevelDir))
	AssertEq(nil, err)
	ExpectEq(os.FileMode(0754)|os.ModeDir, fi.Mode())

	// Stat the file.
	fi, err = os.Lstat(path.Join(t.dir, canned.TopLevelFile))
	AssertEq(nil, err)
	ExpectEq(os.FileMode(612)|os.ModeDir, fi.Mode())
}

func (t *MountHelperTest) ImplicitDirs() {
	var err error

	// Mount.
	args := []string{"-o", "implicit_dirs", canned.FakeBucketName, t.dir}

	err = t.mount(args)
	AssertEq(nil, err)
	defer unmount(t.dir)

	// The implicit directory should be visible.
	fi, err := os.Lstat(path.Join(t.dir, canned.ImplicitDirFile))
	AssertEq(nil, err)
	ExpectTrue(fi.IsDir())
}
<commit_msg>Fix some test bugs.<commit_after>// Copyright 2015 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package integration_test

import (
	"fmt"
	"io/ioutil"
	"os"
	"os/exec"
	"path"
	"runtime"
	"testing"

	"github.com/googlecloudplatform/gcsfuse/internal/canned"
	. "github.com/jacobsa/oglematchers"
	. "github.com/jacobsa/ogletest"
)

func TestMountHelper(t *testing.T) { RunTests(t) }

////////////////////////////////////////////////////////////////////////
// Boilerplate
////////////////////////////////////////////////////////////////////////

type MountHelperTest struct {
	// Path to the mount(8) helper binary.
	helperPath string

	// A temporary directory into which a file system may be mounted. Removed in
	// TearDown.
	dir string
}

var _ SetUpInterface = &MountHelperTest{}
var _ TearDownInterface = &MountHelperTest{}

func init() { RegisterTestSuite(&MountHelperTest{}) }

func (t *MountHelperTest) SetUp(_ *TestInfo) {
	var err error

	// Set up the appropriate helper path.
	switch runtime.GOOS {
	case "darwin":
		t.helperPath = path.Join(gBuildDir, "sbin/mount_gcsfuse")

	case "linux":
		t.helperPath = path.Join(gBuildDir, "sbin/mount.gcsfuse")

	default:
		AddFailure("Don't know how to deal with OS: %q", runtime.GOOS)
		AbortTest()
	}

	// Set up the temporary directory.
	t.dir, err = ioutil.TempDir("", "mount_helper_test")
	AssertEq(nil, err)
}

func (t *MountHelperTest) TearDown() {
	err := os.Remove(t.dir)
	AssertEq(nil, err)
}

func (t *MountHelperTest) mountHelperCommand(args []string) (cmd *exec.Cmd) {
	cmd = exec.Command(t.helperPath)
	cmd.Args = append(cmd.Args, args...)
	cmd.Env = []string{
		fmt.Sprintf("PATH=%s", path.Join(gBuildDir, "bin")),
	}

	return
}

func (t *MountHelperTest) mount(args []string) (err error) {
	cmd := t.mountHelperCommand(args)

	output, err := cmd.CombinedOutput()
	if err != nil {
		err = fmt.Errorf("CombinedOutput: %v\nOutput:\n%s", err, output)
		return
	}

	return
}

////////////////////////////////////////////////////////////////////////
// Tests
////////////////////////////////////////////////////////////////////////

func (t *MountHelperTest) BadUsage() {
	testCases := []struct {
		args           []string
		expectedOutput string
	}{
		// Too few args
		0: {
			[]string{canned.FakeBucketName},
			"two positional arguments",
		},

		// Too many args
		1: {
			[]string{canned.FakeBucketName, "a", "b"},
			"Unexpected arg 3",
		},

		// Trailing -o
		2: {
			[]string{canned.FakeBucketName, "a", "-o"},
			"Unexpected -o",
		},
	}

	// Run each test case.
	for i, tc := range testCases {
		cmd := t.mountHelperCommand(tc.args)

		output, err := cmd.CombinedOutput()
		ExpectThat(err, Error(HasSubstr("exit status")), "case %d", i)
		ExpectThat(string(output), MatchesRegexp(tc.expectedOutput), "case %d", i)
	}
}

func (t *MountHelperTest) SuccessfulMount() {
	var err error
	var fi os.FileInfo

	// Mount.
	args := []string{canned.FakeBucketName, t.dir}

	err = t.mount(args)
	AssertEq(nil, err)
	defer unmount(t.dir)

	// Check that the file system is available.
	fi, err = os.Lstat(path.Join(t.dir, canned.TopLevelFile))
	AssertEq(nil, err)
	ExpectEq(os.FileMode(0644), fi.Mode())
	ExpectEq(len(canned.TopLevelFile_Contents), fi.Size())
}

func (t *MountHelperTest) RelativeMountPoint() {
	var err error
	var fi os.FileInfo

	// Mount with a relative mount point.
	cmd := t.mountHelperCommand([]string{
		canned.FakeBucketName,
		path.Base(t.dir),
	})

	cmd.Dir = path.Dir(t.dir)

	output, err := cmd.CombinedOutput()
	AssertEq(nil, err, "output:\n%s", output)

	defer unmount(t.dir)

	// The file system should be available.
	fi, err = os.Lstat(path.Join(t.dir, canned.TopLevelFile))
	AssertEq(nil, err)
	ExpectEq(os.FileMode(0644), fi.Mode())
	ExpectEq(len(canned.TopLevelFile_Contents), fi.Size())
}

func (t *MountHelperTest) ReadOnlyMode() {
	var err error

	// Mount.
	args := []string{"-o", "ro", canned.FakeBucketName, t.dir}

	err = t.mount(args)
	AssertEq(nil, err)
	defer unmount(t.dir)

	// Writing to the file system should fail.
	err = ioutil.WriteFile(path.Join(t.dir, "blah"), []byte{}, 0400)
	ExpectThat(err, Error(HasSubstr("read-only")))
}

func (t *MountHelperTest) ExtraneousOptions() {
	var err error
	var fi os.FileInfo

	// Mount with extra junk that shouldn't be passed on.
	args := []string{
		"-o", "noauto,nouser,auto,user",
		canned.FakeBucketName,
		t.dir,
	}

	err = t.mount(args)
	AssertEq(nil, err)
	defer unmount(t.dir)

	// Check that the file system is available.
	fi, err = os.Lstat(path.Join(t.dir, canned.TopLevelFile))
	AssertEq(nil, err)
	ExpectEq(os.FileMode(0644), fi.Mode())
	ExpectEq(len(canned.TopLevelFile_Contents), fi.Size())
}

func (t *MountHelperTest) LinuxArgumentOrder() {
	var err error

	// Linux places the options at the end.
	args := []string{canned.FakeBucketName, t.dir, "-o", "ro"}

	err = t.mount(args)
	AssertEq(nil, err)
	defer unmount(t.dir)

	// Writing to the file system should fail.
	err = ioutil.WriteFile(path.Join(t.dir, "blah"), []byte{}, 0400)
	ExpectThat(err, Error(HasSubstr("read-only")))
}

func (t *MountHelperTest) FuseSubtype() {
	var err error
	var fi os.FileInfo

	// This test isn't relevant except on Linux.
	if runtime.GOOS != "linux" {
		return
	}

	// Mount using the tool that would be invoked by ~mount -t fuse.gcsfuse`.
	t.helperPath = path.Join(gBuildDir, "sbin/mount.fuse.gcsfuse")
	args := []string{canned.FakeBucketName, t.dir}

	err = t.mount(args)
	AssertEq(nil, err)
	defer unmount(t.dir)

	// Check that the file system is available.
	fi, err = os.Lstat(path.Join(t.dir, canned.TopLevelFile))
	AssertEq(nil, err)
	ExpectEq(os.FileMode(0644), fi.Mode())
	ExpectEq(len(canned.TopLevelFile_Contents), fi.Size())
}

func (t *MountHelperTest) ModeOptions() {
	var err error
	var fi os.FileInfo

	// Mount.
	args := []string{
		"-o", "dir_mode=754",
		"-o", "file_mode=612",
		canned.FakeBucketName, t.dir,
	}

	err = t.mount(args)
	AssertEq(nil, err)
	defer unmount(t.dir)

	// Stat the directory.
	fi, err = os.Lstat(path.Join(t.dir, canned.TopLevelDir))
	AssertEq(nil, err)
	ExpectEq(os.FileMode(0754)|os.ModeDir, fi.Mode())

	// Stat the file.
	fi, err = os.Lstat(path.Join(t.dir, canned.TopLevelFile))
	AssertEq(nil, err)
	ExpectEq(os.FileMode(0612), fi.Mode())
}

func (t *MountHelperTest) ImplicitDirs() {
	var err error

	// Mount.
	args := []string{"-o", "implicit_dirs", canned.FakeBucketName, t.dir}

	err = t.mount(args)
	AssertEq(nil, err)
	defer unmount(t.dir)

	// The implicit directory should be visible.
	fi, err := os.Lstat(path.Join(t.dir, path.Dir(canned.ImplicitDirFile)))
	AssertEq(nil, err)
	ExpectTrue(fi.IsDir())
}
<|endoftext|>