<commit_before>package fs

import (
	"bufio"
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	log "github.com/Sirupsen/logrus"
	"io"
	"os"
	"os/exec"
	"path/filepath"
	"reflect"
	"sort"
	"syscall"

	"github.com/oursky/ourd/oddb"
)

const userDBKey = "_user"

const publicDBKey = "_public"
const privateDBKey = "_private"

var dbHookFuncs []oddb.DBHookFunc

// fileConn implements oddb.Conn interface
type fileConn struct {
	Dir      string
	AppName  string
	userDB   userDatabase
	deviceDB *deviceDatabase
	publicDB oddb.Database
}

// Open returns a new connection to fs implementation
func Open(appName, dir string) (oddb.Conn, error) {
	containerPath := filepath.Join(dir, appName)
	userDBPath := filepath.Join(containerPath, userDBKey)
	deviceDBPath := filepath.Join(containerPath, "_device")
	publicDBPath := filepath.Join(containerPath, publicDBKey)

	conn := &fileConn{
		Dir:      containerPath,
		AppName:  appName,
		userDB:   newUserDatabase(userDBPath),
		deviceDB: newDeviceDatabase(deviceDBPath),
	}
	conn.publicDB = newDatabase(conn, publicDBPath, publicDBKey)

	return conn, nil
}

func (conn *fileConn) Close() error {
	return nil
}

func (conn *fileConn) CreateUser(info *oddb.UserInfo) error {
	return conn.userDB.Create(info)
}

func (conn *fileConn) GetUser(id string, info *oddb.UserInfo) error {
	return conn.userDB.Get(id, info)
}

func (conn *fileConn) UpdateUser(info *oddb.UserInfo) error {
	return conn.userDB.Update(info)
}

func (conn *fileConn) DeleteUser(id string) error {
	return conn.userDB.Delete(id)
}

func (conn *fileConn) GetDevice(id string, device *oddb.Device) error {
	return conn.deviceDB.Get(id, device)
}

func (conn *fileConn) SaveDevice(device *oddb.Device) error {
	return conn.deviceDB.Save(device)
}

func (conn *fileConn) DeleteDevice(id string) error {
	return conn.deviceDB.Delete(id)
}

func (conn *fileConn) PublicDB() oddb.Database {
	return conn.publicDB
}

func (conn *fileConn) PrivateDB(userKey string) oddb.Database {
	dbPath := filepath.Join(conn.Dir, userKey)
	return newDatabase(conn, dbPath, privateDBKey)
}

func (conn *fileConn) AddDBRecordHook(hookFunc oddb.DBHookFunc) {
	dbHookFuncs = append(dbHookFuncs, hookFunc)
}

type fileDatabase struct {
	conn      *fileConn
	Dir       string
	Key       string
	subscriDB subscriptionDB
}

func newDatabase(conn *fileConn, dir string, key string) *fileDatabase {
	return &fileDatabase{
		conn:      conn,
		Dir:       dir,
		Key:       key,
		subscriDB: newSubscriptionDB(filepath.Join(dir, "_subscription")),
	}
}

// convenient method to execute hooks if err is nil
func (db fileDatabase) executeHook(record *oddb.Record, event oddb.RecordHookEvent, err error) error {
	if err != nil {
		return err
	}

	for _, hookFunc := range dbHookFuncs {
		go hookFunc(db, record, event)
	}

	return nil
}

func (db fileDatabase) Conn() oddb.Conn {
	return db.conn
}

func (db fileDatabase) ID() string {
	return db.Key
}

func (db fileDatabase) Get(key string, record *oddb.Record) error {
	file, err := os.Open(filepath.Join(db.Dir, key))
	if err != nil {
		if os.IsNotExist(err) {
			return oddb.ErrRecordNotFound
		}
		return err
	}

	jsonDecoder := json.NewDecoder(file)
	return jsonDecoder.Decode(record)
}

func (db fileDatabase) Save(record *oddb.Record) error {
	filePath := db.recordPath(record)
	if err := os.MkdirAll(db.Dir, 0755); err != nil {
		return err
	}

	event := recordEventByPath(filePath)

	file, err := os.Create(filePath)
	if err != nil {
		return err
	}

	jsonEncoder := json.NewEncoder(file)
	err = jsonEncoder.Encode(record)

	return db.executeHook(record, event, err)
}

func (db fileDatabase) Delete(key string) error {
	record := oddb.Record{}
	if err := db.Get(key, &record); err != nil {
		return err
	}

	err := os.Remove(filepath.Join(db.Dir, key))
	return db.executeHook(&record, oddb.RecordDeleted, err)
}

type recordSorter struct {
	records []oddb.Record
	by      func(r1, r2 *oddb.Record) bool
}

func (s *recordSorter) Len() int {
	return len(s.records)
}

func (s *recordSorter) Swap(i, j int) {
	s.records[i], s.records[j] = s.records[j], s.records[i]
}

func (s *recordSorter) Less(i, j int) bool {
	less := s.by(&s.records[i], &s.records[j])
	return less
}

func (s *recordSorter) Sort() {
	sort.Sort(s)
}

func newRecordSorter(records []oddb.Record, sortinfo oddb.Sort) *recordSorter {
	var by func(r1, r2 *oddb.Record) bool

	field := sortinfo.KeyPath

	switch sortinfo.Order {
	default:
		by = func(r1, r2 *oddb.Record) bool {
			return reflectLess(r1.Get(field), r2.Get(field))
		}
	case oddb.Desc:
		by = func(r1, r2 *oddb.Record) bool {
			return !reflectLess(r1.Get(field), r2.Get(field))
		}
	}

	return &recordSorter{
		records: records,
		by:      by,
	}
}

// reflectLess determines whether i1 should have order less than i2.
// This func doesn't deal with pointers
func reflectLess(i1, i2 interface{}) bool {
	if i1 == nil && i2 == nil {
		return true
	}
	if i1 == nil {
		return true
	}
	if i2 == nil {
		return false
	}

	v1 := reflect.ValueOf(i1)
	v2 := reflect.ValueOf(i2)

	if v1.Kind() != v2.Kind() {
		return fmt.Sprint(i1) < fmt.Sprint(i2)
	}

	switch v1.Kind() {
	case reflect.Bool:
		b1, b2 := i1.(bool), i2.(bool)
		if b1 && !b2 { // treating bool as number, then only [1, 0] returns false
			return false
		}
		return true
	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
		return v1.Int() < v2.Int()
	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
		return v1.Uint() < v2.Uint()
	case reflect.Float32, reflect.Float64:
		return v1.Float() < v2.Float()
	case reflect.String:
		return v1.String() < v2.String()
	default:
		return fmt.Sprint(i1) < fmt.Sprint(i2)
	}
}

// Query performs a query on the current Database.
//
// FIXME: Curent implementation is not complete. It assumes the first
// argument being the type of Record and always returns a Rows that
// iterates over all records of that type.
func (db fileDatabase) Query(query *oddb.Query) (*oddb.Rows, error) {
	const grepFmt = "grep -he \"{\\\"_type\\\":\\\"%v\\\"\" %v"

	if err := os.MkdirAll(db.Dir, 0755); err != nil {
		return oddb.NewRows(&memoryRows{0, []oddb.Record{}}), err
	}
	grep := fmt.Sprintf(grepFmt, query.Type, filepath.Join(db.Dir, "*"))

	var outbuf bytes.Buffer
	var errbuf bytes.Buffer

	cmd := exec.Command("sh", "-c", grep)
	cmd.Stdout = &outbuf
	cmd.Stdin = &errbuf

	if err := cmd.Run(); err != nil {
		if exitErr, ok := err.(*exec.ExitError); ok {
			// NOTE: this cast is platform depedent and is only tested
			// on UNIX-like system
			if status, ok := exitErr.Sys().(syscall.WaitStatus); ok {
				// grep has a exit status of 1 if it finds nothing
				// See: http://www.gnu.org/software/grep/manual/html_node/Exit-Status.html
				log.WithFields(log.Fields{
					"ExitStatus": status.ExitStatus(),
				}).Infoln("grep returns non-zero exit status")
			}
		}

		log.WithFields(log.Fields{
			"err":    err.Error(),
			"stderr": errbuf.String(),
			"path":   db.Dir,
		}).Infoln("Failed to grep")

		return oddb.NewRows(&memoryRows{0, []oddb.Record{}}), nil
	}

	records := []oddb.Record{}
	scanner := bufio.NewScanner(&outbuf)
	for scanner.Scan() {
		record := oddb.Record{}
		if err := json.Unmarshal(scanner.Bytes(), &record); err != nil {
			return nil, err
		}
		records = append(records, record)
	}

	if len(query.Sorts) > 0 {
		if len(query.Sorts) > 1 {
			return nil, errors.New("multiple sort order is not supported")
		}

		newRecordSorter(records, query.Sorts[0]).Sort()
	}

	return oddb.NewRows(&memoryRows{0, records}), nil
}

func (db fileDatabase) GetSubscription(key string, subscription *oddb.Subscription) error {
	return db.subscriDB.Get(key, subscription)
}

func (db fileDatabase) SaveSubscription(subscription *oddb.Subscription) error {
	return db.subscriDB.Save(subscription)
}

func (db fileDatabase) DeleteSubscription(key string) error {
	return db.subscriDB.Delete(key)
}

func (db fileDatabase) GetMatchingSubscription(record *oddb.Record) []oddb.Subscription {
	return db.subscriDB.GetMatchingSubscription(record)
}

func (db fileDatabase) recordPath(record *oddb.Record) string {
	return filepath.Join(db.Dir, record.Key)
}

type memoryRows struct {
	currentRowIndex int
	records         []oddb.Record
}

func (rs *memoryRows) Close() error {
	return nil
}

func (rs *memoryRows) Next(record *oddb.Record) error {
	if rs.currentRowIndex >= len(rs.records) {
		return io.EOF
	}

	*record = rs.records[rs.currentRowIndex]
	rs.currentRowIndex = rs.currentRowIndex + 1
	return nil
}

func init() {
	oddb.Register("fs", oddb.DriverFunc(Open))
}
<commit_msg>Check for empty app name in fs implementation<commit_after>package fs

import (
	"bufio"
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	log "github.com/Sirupsen/logrus"
	"io"
	"os"
	"os/exec"
	"path/filepath"
	"reflect"
	"sort"
	"syscall"

	"github.com/oursky/ourd/oddb"
)

const userDBKey = "_user"

const publicDBKey = "_public"
const privateDBKey = "_private"

var dbHookFuncs []oddb.DBHookFunc

// fileConn implements oddb.Conn interface
type fileConn struct {
	Dir      string
	AppName  string
	userDB   userDatabase
	deviceDB *deviceDatabase
	publicDB oddb.Database
}

// Open returns a new connection to fs implementation
func Open(appName, dir string) (oddb.Conn, error) {
	if appName == "" {
		return nil, errors.New("fs: appName cannot be empty")
	}

	containerPath := filepath.Join(dir, appName)
	userDBPath := filepath.Join(containerPath, userDBKey)
	deviceDBPath := filepath.Join(containerPath, "_device")
	publicDBPath := filepath.Join(containerPath, publicDBKey)

	conn := &fileConn{
		Dir:      containerPath,
		AppName:  appName,
		userDB:   newUserDatabase(userDBPath),
		deviceDB: newDeviceDatabase(deviceDBPath),
	}
	conn.publicDB = newDatabase(conn, publicDBPath, publicDBKey)

	return conn, nil
}

func (conn *fileConn) Close() error {
	return nil
}

func (conn *fileConn) CreateUser(info *oddb.UserInfo) error {
	return conn.userDB.Create(info)
}

func (conn *fileConn) GetUser(id string, info *oddb.UserInfo) error {
	return conn.userDB.Get(id, info)
}

func (conn *fileConn) UpdateUser(info *oddb.UserInfo) error {
	return conn.userDB.Update(info)
}

func (conn *fileConn) DeleteUser(id string) error {
	return conn.userDB.Delete(id)
}

func (conn *fileConn) GetDevice(id string, device *oddb.Device) error {
	return conn.deviceDB.Get(id, device)
}

func (conn *fileConn) SaveDevice(device *oddb.Device) error {
	return conn.deviceDB.Save(device)
}

func (conn *fileConn) DeleteDevice(id string) error {
	return conn.deviceDB.Delete(id)
}

func (conn *fileConn) PublicDB() oddb.Database {
	return conn.publicDB
}

func (conn *fileConn) PrivateDB(userKey string) oddb.Database {
	dbPath := filepath.Join(conn.Dir, userKey)
	return newDatabase(conn, dbPath, privateDBKey)
}

func (conn *fileConn) AddDBRecordHook(hookFunc oddb.DBHookFunc) {
	dbHookFuncs = append(dbHookFuncs, hookFunc)
}

type fileDatabase struct {
	conn      *fileConn
	Dir       string
	Key       string
	subscriDB subscriptionDB
}

func newDatabase(conn *fileConn, dir string, key string) *fileDatabase {
	return &fileDatabase{
		conn:      conn,
		Dir:       dir,
		Key:       key,
		subscriDB: newSubscriptionDB(filepath.Join(dir, "_subscription")),
	}
}

// convenient method to execute hooks if err is nil
func (db fileDatabase) executeHook(record *oddb.Record, event oddb.RecordHookEvent, err error) error {
	if err != nil {
		return err
	}

	for _, hookFunc := range dbHookFuncs {
		go hookFunc(db, record, event)
	}

	return nil
}

func (db fileDatabase) Conn() oddb.Conn {
	return db.conn
}

func (db fileDatabase) ID() string {
	return db.Key
}

func (db fileDatabase) Get(key string, record *oddb.Record) error {
	file, err := os.Open(filepath.Join(db.Dir, key))
	if err != nil {
		if os.IsNotExist(err) {
			return oddb.ErrRecordNotFound
		}
		return err
	}

	jsonDecoder := json.NewDecoder(file)
	return jsonDecoder.Decode(record)
}

func (db fileDatabase) Save(record *oddb.Record) error {
	filePath := db.recordPath(record)
	if err := os.MkdirAll(db.Dir, 0755); err != nil {
		return err
	}

	event := recordEventByPath(filePath)

	file, err := os.Create(filePath)
	if err != nil {
		return err
	}

	jsonEncoder := json.NewEncoder(file)
	err = jsonEncoder.Encode(record)

	return db.executeHook(record, event, err)
}

func (db fileDatabase) Delete(key string) error {
	record := oddb.Record{}
	if err := db.Get(key, &record); err != nil {
		return err
	}

	err := os.Remove(filepath.Join(db.Dir, key))
	return db.executeHook(&record, oddb.RecordDeleted, err)
}

type recordSorter struct {
	records []oddb.Record
	by      func(r1, r2 *oddb.Record) bool
}

func (s *recordSorter) Len() int {
	return len(s.records)
}

func (s *recordSorter) Swap(i, j int) {
	s.records[i], s.records[j] = s.records[j], s.records[i]
}

func (s *recordSorter) Less(i, j int) bool {
	less := s.by(&s.records[i], &s.records[j])
	return less
}

func (s *recordSorter) Sort() {
	sort.Sort(s)
}

func newRecordSorter(records []oddb.Record, sortinfo oddb.Sort) *recordSorter {
	var by func(r1, r2 *oddb.Record) bool

	field := sortinfo.KeyPath

	switch sortinfo.Order {
	default:
		by = func(r1, r2 *oddb.Record) bool {
			return reflectLess(r1.Get(field), r2.Get(field))
		}
	case oddb.Desc:
		by = func(r1, r2 *oddb.Record) bool {
			return !reflectLess(r1.Get(field), r2.Get(field))
		}
	}

	return &recordSorter{
		records: records,
		by:      by,
	}
}

// reflectLess determines whether i1 should have order less than i2.
// This func doesn't deal with pointers
func reflectLess(i1, i2 interface{}) bool {
	if i1 == nil && i2 == nil {
		return true
	}
	if i1 == nil {
		return true
	}
	if i2 == nil {
		return false
	}

	v1 := reflect.ValueOf(i1)
	v2 := reflect.ValueOf(i2)

	if v1.Kind() != v2.Kind() {
		return fmt.Sprint(i1) < fmt.Sprint(i2)
	}

	switch v1.Kind() {
	case reflect.Bool:
		b1, b2 := i1.(bool), i2.(bool)
		if b1 && !b2 { // treating bool as number, then only [1, 0] returns false
			return false
		}
		return true
	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
		return v1.Int() < v2.Int()
	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
		return v1.Uint() < v2.Uint()
	case reflect.Float32, reflect.Float64:
		return v1.Float() < v2.Float()
	case reflect.String:
		return v1.String() < v2.String()
	default:
		return fmt.Sprint(i1) < fmt.Sprint(i2)
	}
}

// Query performs a query on the current Database.
//
// FIXME: Curent implementation is not complete. It assumes the first
// argument being the type of Record and always returns a Rows that
// iterates over all records of that type.
func (db fileDatabase) Query(query *oddb.Query) (*oddb.Rows, error) {
	const grepFmt = "grep -he \"{\\\"_type\\\":\\\"%v\\\"\" %v"

	if err := os.MkdirAll(db.Dir, 0755); err != nil {
		return oddb.NewRows(&memoryRows{0, []oddb.Record{}}), err
	}
	grep := fmt.Sprintf(grepFmt, query.Type, filepath.Join(db.Dir, "*"))

	var outbuf bytes.Buffer
	var errbuf bytes.Buffer

	cmd := exec.Command("sh", "-c", grep)
	cmd.Stdout = &outbuf
	cmd.Stdin = &errbuf

	if err := cmd.Run(); err != nil {
		if exitErr, ok := err.(*exec.ExitError); ok {
			// NOTE: this cast is platform depedent and is only tested
			// on UNIX-like system
			if status, ok := exitErr.Sys().(syscall.WaitStatus); ok {
				// grep has a exit status of 1 if it finds nothing
				// See: http://www.gnu.org/software/grep/manual/html_node/Exit-Status.html
				log.WithFields(log.Fields{
					"ExitStatus": status.ExitStatus(),
				}).Infoln("grep returns non-zero exit status")
			}
		}

		log.WithFields(log.Fields{
			"err":    err.Error(),
			"stderr": errbuf.String(),
			"path":   db.Dir,
		}).Infoln("Failed to grep")

		return oddb.NewRows(&memoryRows{0, []oddb.Record{}}), nil
	}

	records := []oddb.Record{}
	scanner := bufio.NewScanner(&outbuf)
	for scanner.Scan() {
		record := oddb.Record{}
		if err := json.Unmarshal(scanner.Bytes(), &record); err != nil {
			return nil, err
		}
		records = append(records, record)
	}

	if len(query.Sorts) > 0 {
		if len(query.Sorts) > 1 {
			return nil, errors.New("multiple sort order is not supported")
		}

		newRecordSorter(records, query.Sorts[0]).Sort()
	}

	return oddb.NewRows(&memoryRows{0, records}), nil
}

func (db fileDatabase) GetSubscription(key string, subscription *oddb.Subscription) error {
	return db.subscriDB.Get(key, subscription)
}

func (db fileDatabase) SaveSubscription(subscription *oddb.Subscription) error {
	return db.subscriDB.Save(subscription)
}

func (db fileDatabase) DeleteSubscription(key string) error {
	return db.subscriDB.Delete(key)
}

func (db fileDatabase) GetMatchingSubscription(record *oddb.Record) []oddb.Subscription {
	return db.subscriDB.GetMatchingSubscription(record)
}

func (db fileDatabase) recordPath(record *oddb.Record) string {
	return filepath.Join(db.Dir, record.Key)
}

type memoryRows struct {
	currentRowIndex int
	records         []oddb.Record
}

func (rs *memoryRows) Close() error {
	return nil
}

func (rs *memoryRows) Next(record *oddb.Record) error {
	if rs.currentRowIndex >= len(rs.records) {
		return io.EOF
	}

	*record = rs.records[rs.currentRowIndex]
	rs.currentRowIndex = rs.currentRowIndex + 1
	return nil
}

func init() {
	oddb.Register("fs", oddb.DriverFunc(Open))
}
<|endoftext|>


<commit_before><commit_msg>Also check stdout<commit_after><|endoftext|>


<commit_before>// Copyright 2014 CoreOS, Inc.
//
// 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.

//+build linux

package stage0

//
// Rocket is a reference implementation of the app container specification.
//
// Execution on Rocket is divided into a number of stages, and the `rkt`
// binary implements the first stage (stage 0)
//

import (
	"archive/tar"
	"bytes"
	"compress/bzip2"
	"compress/gzip"
	"crypto/sha512"
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"os"
	"path/filepath"
	"syscall"

	"github.com/appc/spec/aci"
	"github.com/appc/spec/schema"
	"github.com/appc/spec/schema/types"
	"github.com/coreos/rocket/Godeps/_workspace/src/code.google.com/p/go-uuid/uuid"
	"github.com/coreos/rocket/cas"
	rktpath "github.com/coreos/rocket/path"
	"github.com/coreos/rocket/pkg/lock"
	ptar "github.com/coreos/rocket/pkg/tar"
	"github.com/coreos/rocket/version"

	"github.com/coreos/rocket/stage0/stage1_init"
	"github.com/coreos/rocket/stage0/stage1_rootfs"
)

const (
	initPath  = "stage1/init"
	envLockFd = "RKT_LOCK_FD"
)

type Config struct {
	Store         *cas.Store // store containing all of the configured application images
	ContainersDir string     // root directory for rocket containers
	Stage1Init    string     // binary to be execed as stage1
	Stage1Rootfs  string     // compressed bundle containing a rootfs for stage1
	Debug         bool
	// TODO(jonboulle): These images are partially-populated hashes, this should be clarified.
	Images           []types.Hash      // application images
	Volumes          map[string]string // map of volumes that rocket can provide to applications
	PrivateNet       bool              // container should have its own network stack
	SpawnMetadataSvc bool              // launch metadata service
}

func init() {
	log.SetOutput(ioutil.Discard)
}

// Setup sets up a filesystem for a container based on the given config.
// The directory containing the filesystem is returned, and any error encountered.
func Setup(cfg Config) (string, error) {
	if cfg.Debug {
		log.SetOutput(os.Stderr)
	}

	// Create a unique directory for this container
	cuuid, dir, err := makeUniqueContainer(cfg.ContainersDir)
	if err != nil {
		return "", fmt.Errorf("error creating directory: %v", err)
	}

	// Set up the container lock
	if err := lockDir(dir); err != nil {
		return "", err
	}

	log.Printf("Unpacking stage1 rootfs")
	if cfg.Stage1Rootfs != "" {
		err = unpackRootfs(cfg.Stage1Rootfs, rktpath.Stage1RootfsPath(dir))
	} else {
		err = unpackBuiltinRootfs(rktpath.Stage1RootfsPath(dir))
	}
	if err != nil {
		return "", fmt.Errorf("error unpacking rootfs: %v", err)
	}

	log.Printf("Writing stage1 init")
	var in io.Reader
	if cfg.Stage1Init != "" {
		in, err = os.Open(cfg.Stage1Init)
		if err != nil {
			return "", fmt.Errorf("error loading stage1 init binary: %v", err)
		}
	} else {
		init_bin, err := stage1_init.Asset("s1init")
		if err != nil {
			return "", fmt.Errorf("error accessing stage1 init bindata: %v", err)
		}
		in = bytes.NewBuffer(init_bin)
	}
	fn := filepath.Join(dir, initPath)
	out, err := os.OpenFile(fn, os.O_CREATE|os.O_WRONLY, 0555)
	if err != nil {
		return "", fmt.Errorf("error opening stage1 init for writing: %v", err)
	}
	if _, err := io.Copy(out, in); err != nil {
		return "", fmt.Errorf("error writing stage1 init: %v", err)
	}
	if err := out.Close(); err != nil {
		return "", fmt.Errorf("error closing stage1 init: %v", err)
	}

	log.Printf("Wrote filesystem to %s\n", dir)

	cm := schema.ContainerRuntimeManifest{
		ACKind: "ContainerRuntimeManifest",
		UUID:   *cuuid,
		Apps:   make(schema.AppList, 0),
	}

	v, err := types.NewSemVer(version.Version)
	if err != nil {
		return "", fmt.Errorf("error creating version: %v", err)
	}
	cm.ACVersion = *v

	for _, img := range cfg.Images {
		am, err := setupImage(cfg, img, dir)
		if err != nil {
			return "", fmt.Errorf("error setting up image %s: %v", img, err)
		}
		if cm.Apps.Get(am.Name) != nil {
			return "", fmt.Errorf("error: multiple apps with name %s", am.Name)
		}
		a := schema.RuntimeApp{
			Name:        am.Name,
			ImageID:     img,
			Isolators:   am.App.Isolators,
			Annotations: am.Annotations,
		}
		cm.Apps = append(cm.Apps, a)
	}

	var sVols []types.Volume
	for key, path := range cfg.Volumes {
		v := types.Volume{
			Kind:     "host",
			Source:   path,
			ReadOnly: true,
			Fulfills: []types.ACName{
				types.ACName(key),
			},
		}
		sVols = append(sVols, v)
	}
	// TODO(jonboulle): check that app mountpoint expectations are
	// satisfied here, rather than waiting for stage1
	cm.Volumes = sVols

	cdoc, err := json.Marshal(cm)
	if err != nil {
		return "", fmt.Errorf("error marshalling container manifest: %v", err)
	}

	log.Printf("Writing container manifest")
	fn = rktpath.ContainerManifestPath(dir)
	if err := ioutil.WriteFile(fn, cdoc, 0700); err != nil {
		return "", fmt.Errorf("error writing container manifest: %v", err)
	}
	return dir, nil
}

// Run actually runs the container by exec()ing the stage1 init inside
// the container filesystem.
func Run(cfg Config, dir string) {
	log.Printf("Pivoting to filesystem %s", dir)
	if err := os.Chdir(dir); err != nil {
		log.Fatalf("failed changing to dir: %v", err)
	}

	log.Printf("Execing %s", initPath)
	args := []string{initPath}
	if cfg.Debug {
		args = append(args, "--debug")
	}
	if cfg.SpawnMetadataSvc {
		rktExe, err := os.Readlink("/proc/self/exe")
		if err != nil {
			log.Fatalf("failed to readlink /proc/self/exe: %v", err)
		}
		dbgFlag := ""
		if cfg.Debug {
			dbgFlag = " --debug"
		}
		args = append(args, fmt.Sprintf("--metadata-svc=%s%s metadatasvc --no-idle", rktExe, dbgFlag))
	}
	if cfg.PrivateNet {
		args = append(args, "--private-net")
	}
	if err := syscall.Exec(initPath, args, os.Environ()); err != nil {
		log.Fatalf("error execing init: %v", err)
	}
}

func makeUniqueContainer(pdir string) (*types.UUID, string, error) {
	// Arbitrary limit so we don't spin forever
	for i := 0; i <= 100; i++ {
		cuuid, err := types.NewUUID(uuid.New())
		if err != nil {
			// Should never happen
			return nil, "", fmt.Errorf("error creating UUID: %v", err)
		}

		dir := filepath.Join(pdir, cuuid.String())
		err = os.Mkdir(dir, 0700)
		switch {
		case err == nil:
			return cuuid, dir, nil
		case os.IsExist(err):
			continue
		case err != nil:
			return nil, "", err
		}
	}
	return nil, "", fmt.Errorf("couldn't find unique directory!")
}

func lockDir(dir string) error {
	l, err := lock.TryExclusiveLock(dir)
	if err != nil {
		return fmt.Errorf("error acquiring lock on dir %q: %v", dir, err)
	}
	// We need the fd number for stage1 and leave the file open / lock held til process exit
	fd, err := l.Fd()
	if err != nil {
		panic(err)
	}
	return os.Setenv(envLockFd, fmt.Sprintf("%v", fd))
}

func untarRootfs(r io.Reader, dir string) error {
	tr := tar.NewReader(r)
	if err := os.MkdirAll(dir, 0776); err != nil {
		return fmt.Errorf("error creating stage1 rootfs directory: %v", err)
	}

	if err := ptar.ExtractTar(tr, dir, false, nil); err != nil {
		return fmt.Errorf("error extracting rootfs: %v", err)
	}
	return nil
}

// unpackRootfs unpacks a stage1 rootfs (compressed file, pointed to by rfs)
// into dir, returning any error encountered
func unpackRootfs(rfs string, dir string) error {
	fh, err := os.Open(rfs)
	if err != nil {
		return fmt.Errorf("error opening stage1 rootfs: %v", err)
	}
	typ, err := aci.DetectFileType(fh)
	if err != nil {
		return fmt.Errorf("error detecting image type: %v", err)
	}
	if _, err := fh.Seek(0, 0); err != nil {
		return fmt.Errorf("error seeking image: %v", err)
	}
	var r io.Reader
	switch typ {
	case aci.TypeGzip:
		r, err = gzip.NewReader(fh)
		if err != nil {
			return fmt.Errorf("error reading gzip: %v", err)
		}
	case aci.TypeBzip2:
		r = bzip2.NewReader(fh)
	case aci.TypeXz:
		r = aci.XzReader(fh)
	case aci.TypeTar:
		r = fh
	case aci.TypeUnknown:
		return fmt.Errorf("error: unknown image filetype")
	default:
		// should never happen
		panic("no type returned from DetectFileType?")
	}
	return untarRootfs(r, dir)
}

// unpackBuiltinRootfs unpacks the included stage1 rootfs into dir
func unpackBuiltinRootfs(dir string) error {
	b, err := stage1_rootfs.Asset("s1rootfs.tar")
	if err != nil {
		return fmt.Errorf("error accessing rootfs asset: %v", err)
	}
	buf := bytes.NewBuffer(b)
	return untarRootfs(buf, dir)
}

// setupImage attempts to load the image by the given hash from the store,
// verifies that the image matches the hash, and extracts the image into a
// directory in the given dir.
// It returns the ImageManifest that the image contains.
// TODO(jonboulle): tighten up the Hash type here; currently it is partially-populated (i.e. half-length sha512)
func setupImage(cfg Config, img types.Hash, dir string) (*schema.ImageManifest, error) {
	log.Println("Loading image", img.String())

	rs, err := cfg.Store.ReadStream(img.String())
	if err != nil {
		return nil, fmt.Errorf("error reading stream: %v", err)
	}

	ad := rktpath.AppImagePath(dir, img)
	err = os.MkdirAll(ad, 0776)
	if err != nil {
		return nil, fmt.Errorf("error creating image directory: %v", err)
	}

	hash := sha512.New()
	r := io.TeeReader(rs, hash)

	if err := ptar.ExtractTar(tar.NewReader(r), ad, false, nil); err != nil {
		return nil, fmt.Errorf("error extracting ACI: %v", err)
	}

	// Tar does not necessarily read the complete file, so ensure we read the entirety into the hash
	if _, err := io.Copy(ioutil.Discard, r); err != nil {
		return nil, fmt.Errorf("error reading ACI: %v", err)
	}

	// TODO(jonboulle): clean this up, leaky abstraction with the store.
	if g := cas.HashToKey(hash); g != img.String() {
		if err := os.RemoveAll(ad); err != nil {
			fmt.Fprintf(os.Stderr, "error cleaning up directory: %v\n", err)
		}
		return nil, fmt.Errorf("image hash does not match expected (%v != %v)", g, img.String())
	}

	err = os.MkdirAll(filepath.Join(ad, "rootfs/tmp"), 0777)
	if err != nil {
		return nil, fmt.Errorf("error creating tmp directory: %v", err)
	}

	mpath := rktpath.ImageManifestPath(dir, img)
	f, err := os.Open(mpath)
	if err != nil {
		return nil, fmt.Errorf("error opening app manifest: %v", err)
	}
	b, err := ioutil.ReadAll(f)
	if err != nil {
		return nil, fmt.Errorf("error reading app manifest: %v", err)
	}
	var am schema.ImageManifest
	if err := json.Unmarshal(b, &am); err != nil {
		return nil, fmt.Errorf("error unmarshaling app manifest: %v", err)
	}
	return &am, nil
}
<commit_msg>stage0: add docstring + create parent dir<commit_after>// Copyright 2014 CoreOS, Inc.
//
// 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.

//+build linux

package stage0

//
// Rocket is a reference implementation of the app container specification.
//
// Execution on Rocket is divided into a number of stages, and the `rkt`
// binary implements the first stage (stage 0)
//

import (
	"archive/tar"
	"bytes"
	"compress/bzip2"
	"compress/gzip"
	"crypto/sha512"
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"os"
	"path/filepath"
	"syscall"

	"github.com/appc/spec/aci"
	"github.com/appc/spec/schema"
	"github.com/appc/spec/schema/types"
	"github.com/coreos/rocket/Godeps/_workspace/src/code.google.com/p/go-uuid/uuid"
	"github.com/coreos/rocket/cas"
	rktpath "github.com/coreos/rocket/path"
	"github.com/coreos/rocket/pkg/lock"
	ptar "github.com/coreos/rocket/pkg/tar"
	"github.com/coreos/rocket/version"

	"github.com/coreos/rocket/stage0/stage1_init"
	"github.com/coreos/rocket/stage0/stage1_rootfs"
)

const (
	initPath  = "stage1/init"
	envLockFd = "RKT_LOCK_FD"
)

type Config struct {
	Store         *cas.Store // store containing all of the configured application images
	ContainersDir string     // root directory for rocket containers
	Stage1Init    string     // binary to be execed as stage1
	Stage1Rootfs  string     // compressed bundle containing a rootfs for stage1
	Debug         bool
	// TODO(jonboulle): These images are partially-populated hashes, this should be clarified.
	Images           []types.Hash      // application images
	Volumes          map[string]string // map of volumes that rocket can provide to applications
	PrivateNet       bool              // container should have its own network stack
	SpawnMetadataSvc bool              // launch metadata service
}

func init() {
	log.SetOutput(ioutil.Discard)
}

// Setup sets up a filesystem for a container based on the given config.
// The directory containing the filesystem is returned, and any error encountered.
func Setup(cfg Config) (string, error) {
	if cfg.Debug {
		log.SetOutput(os.Stderr)
	}

	if err := os.MkdirAll(cfg.ContainersDir, 0700); err != nil {
		return "", fmt.Errorf("error creating containers directory: %v", err)
	}

	// Create a unique directory for this container
	cuuid, dir, err := makeUniqueContainer(cfg.ContainersDir)
	if err != nil {
		return "", fmt.Errorf("error creating directory: %v", err)
	}

	// Set up the container lock
	if err := lockDir(dir); err != nil {
		return "", err
	}

	log.Printf("Unpacking stage1 rootfs")
	if cfg.Stage1Rootfs != "" {
		err = unpackRootfs(cfg.Stage1Rootfs, rktpath.Stage1RootfsPath(dir))
	} else {
		err = unpackBuiltinRootfs(rktpath.Stage1RootfsPath(dir))
	}
	if err != nil {
		return "", fmt.Errorf("error unpacking rootfs: %v", err)
	}

	log.Printf("Writing stage1 init")
	var in io.Reader
	if cfg.Stage1Init != "" {
		in, err = os.Open(cfg.Stage1Init)
		if err != nil {
			return "", fmt.Errorf("error loading stage1 init binary: %v", err)
		}
	} else {
		init_bin, err := stage1_init.Asset("s1init")
		if err != nil {
			return "", fmt.Errorf("error accessing stage1 init bindata: %v", err)
		}
		in = bytes.NewBuffer(init_bin)
	}
	fn := filepath.Join(dir, initPath)
	out, err := os.OpenFile(fn, os.O_CREATE|os.O_WRONLY, 0555)
	if err != nil {
		return "", fmt.Errorf("error opening stage1 init for writing: %v", err)
	}
	if _, err := io.Copy(out, in); err != nil {
		return "", fmt.Errorf("error writing stage1 init: %v", err)
	}
	if err := out.Close(); err != nil {
		return "", fmt.Errorf("error closing stage1 init: %v", err)
	}

	log.Printf("Wrote filesystem to %s\n", dir)

	cm := schema.ContainerRuntimeManifest{
		ACKind: "ContainerRuntimeManifest",
		UUID:   *cuuid,
		Apps:   make(schema.AppList, 0),
	}

	v, err := types.NewSemVer(version.Version)
	if err != nil {
		return "", fmt.Errorf("error creating version: %v", err)
	}
	cm.ACVersion = *v

	for _, img := range cfg.Images {
		am, err := setupImage(cfg, img, dir)
		if err != nil {
			return "", fmt.Errorf("error setting up image %s: %v", img, err)
		}
		if cm.Apps.Get(am.Name) != nil {
			return "", fmt.Errorf("error: multiple apps with name %s", am.Name)
		}
		a := schema.RuntimeApp{
			Name:        am.Name,
			ImageID:     img,
			Isolators:   am.App.Isolators,
			Annotations: am.Annotations,
		}
		cm.Apps = append(cm.Apps, a)
	}

	var sVols []types.Volume
	for key, path := range cfg.Volumes {
		v := types.Volume{
			Kind:     "host",
			Source:   path,
			ReadOnly: true,
			Fulfills: []types.ACName{
				types.ACName(key),
			},
		}
		sVols = append(sVols, v)
	}
	// TODO(jonboulle): check that app mountpoint expectations are
	// satisfied here, rather than waiting for stage1
	cm.Volumes = sVols

	cdoc, err := json.Marshal(cm)
	if err != nil {
		return "", fmt.Errorf("error marshalling container manifest: %v", err)
	}

	log.Printf("Writing container manifest")
	fn = rktpath.ContainerManifestPath(dir)
	if err := ioutil.WriteFile(fn, cdoc, 0700); err != nil {
		return "", fmt.Errorf("error writing container manifest: %v", err)
	}
	return dir, nil
}

// Run actually runs the container by exec()ing the stage1 init inside
// the container filesystem.
func Run(cfg Config, dir string) {
	log.Printf("Pivoting to filesystem %s", dir)
	if err := os.Chdir(dir); err != nil {
		log.Fatalf("failed changing to dir: %v", err)
	}

	log.Printf("Execing %s", initPath)
	args := []string{initPath}
	if cfg.Debug {
		args = append(args, "--debug")
	}
	if cfg.SpawnMetadataSvc {
		rktExe, err := os.Readlink("/proc/self/exe")
		if err != nil {
			log.Fatalf("failed to readlink /proc/self/exe: %v", err)
		}
		dbgFlag := ""
		if cfg.Debug {
			dbgFlag = " --debug"
		}
		args = append(args, fmt.Sprintf("--metadata-svc=%s%s metadatasvc --no-idle", rktExe, dbgFlag))
	}
	if cfg.PrivateNet {
		args = append(args, "--private-net")
	}
	if err := syscall.Exec(initPath, args, os.Environ()); err != nil {
		log.Fatalf("error execing init: %v", err)
	}
}

// makeUniqueContainer creates a subdirectory (representing a container)
// within the given parent directory. On success, it returns a UUID
// representing the created container and the full path to the new directory.
// The UUID is guaranteed to be unique within the parent directory.
// The parent directory MUST exist and be writeable.
func makeUniqueContainer(pdir string) (*types.UUID, string, error) {
	// Arbitrary limit so we don't spin forever
	for i := 0; i <= 100; i++ {
		cuuid, err := types.NewUUID(uuid.New())
		if err != nil {
			// Should never happen
			return nil, "", fmt.Errorf("error creating UUID: %v", err)
		}

		dir := filepath.Join(pdir, cuuid.String())
		err = os.Mkdir(dir, 0700)
		switch {
		case err == nil:
			return cuuid, dir, nil
		case os.IsExist(err):
			continue
		case err != nil:
			return nil, "", err
		}
	}
	return nil, "", fmt.Errorf("couldn't find unique directory!")
}

func lockDir(dir string) error {
	l, err := lock.TryExclusiveLock(dir)
	if err != nil {
		return fmt.Errorf("error acquiring lock on dir %q: %v", dir, err)
	}
	// We need the fd number for stage1 and leave the file open / lock held til process exit
	fd, err := l.Fd()
	if err != nil {
		panic(err)
	}
	return os.Setenv(envLockFd, fmt.Sprintf("%v", fd))
}

func untarRootfs(r io.Reader, dir string) error {
	tr := tar.NewReader(r)
	if err := os.MkdirAll(dir, 0776); err != nil {
		return fmt.Errorf("error creating stage1 rootfs directory: %v", err)
	}

	if err := ptar.ExtractTar(tr, dir, false, nil); err != nil {
		return fmt.Errorf("error extracting rootfs: %v", err)
	}
	return nil
}

// unpackRootfs unpacks a stage1 rootfs (compressed file, pointed to by rfs)
// into dir, returning any error encountered
func unpackRootfs(rfs string, dir string) error {
	fh, err := os.Open(rfs)
	if err != nil {
		return fmt.Errorf("error opening stage1 rootfs: %v", err)
	}
	typ, err := aci.DetectFileType(fh)
	if err != nil {
		return fmt.Errorf("error detecting image type: %v", err)
	}
	if _, err := fh.Seek(0, 0); err != nil {
		return fmt.Errorf("error seeking image: %v", err)
	}
	var r io.Reader
	switch typ {
	case aci.TypeGzip:
		r, err = gzip.NewReader(fh)
		if err != nil {
			return fmt.Errorf("error reading gzip: %v", err)
		}
	case aci.TypeBzip2:
		r = bzip2.NewReader(fh)
	case aci.TypeXz:
		r = aci.XzReader(fh)
	case aci.TypeTar:
		r = fh
	case aci.TypeUnknown:
		return fmt.Errorf("error: unknown image filetype")
	default:
		// should never happen
		panic("no type returned from DetectFileType?")
	}
	return untarRootfs(r, dir)
}

// unpackBuiltinRootfs unpacks the included stage1 rootfs into dir
func unpackBuiltinRootfs(dir string) error {
	b, err := stage1_rootfs.Asset("s1rootfs.tar")
	if err != nil {
		return fmt.Errorf("error accessing rootfs asset: %v", err)
	}
	buf := bytes.NewBuffer(b)
	return untarRootfs(buf, dir)
}

// setupImage attempts to load the image by the given hash from the store,
// verifies that the image matches the hash, and extracts the image into a
// directory in the given dir.
// It returns the ImageManifest that the image contains.
// TODO(jonboulle): tighten up the Hash type here; currently it is partially-populated (i.e. half-length sha512)
func setupImage(cfg Config, img types.Hash, dir string) (*schema.ImageManifest, error) {
	log.Println("Loading image", img.String())

	rs, err := cfg.Store.ReadStream(img.String())
	if err != nil {
		return nil, fmt.Errorf("error reading stream: %v", err)
	}

	ad := rktpath.AppImagePath(dir, img)
	err = os.MkdirAll(ad, 0776)
	if err != nil {
		return nil, fmt.Errorf("error creating image directory: %v", err)
	}

	hash := sha512.New()
	r := io.TeeReader(rs, hash)

	if err := ptar.ExtractTar(tar.NewReader(r), ad, false, nil); err != nil {
		return nil, fmt.Errorf("error extracting ACI: %v", err)
	}

	// Tar does not necessarily read the complete file, so ensure we read the entirety into the hash
	if _, err := io.Copy(ioutil.Discard, r); err != nil {
		return nil, fmt.Errorf("error reading ACI: %v", err)
	}

	// TODO(jonboulle): clean this up, leaky abstraction with the store.
	if g := cas.HashToKey(hash); g != img.String() {
		if err := os.RemoveAll(ad); err != nil {
			fmt.Fprintf(os.Stderr, "error cleaning up directory: %v\n", err)
		}
		return nil, fmt.Errorf("image hash does not match expected (%v != %v)", g, img.String())
	}

	err = os.MkdirAll(filepath.Join(ad, "rootfs/tmp"), 0777)
	if err != nil {
		return nil, fmt.Errorf("error creating tmp directory: %v", err)
	}

	mpath := rktpath.ImageManifestPath(dir, img)
	f, err := os.Open(mpath)
	if err != nil {
		return nil, fmt.Errorf("error opening app manifest: %v", err)
	}
	b, err := ioutil.ReadAll(f)
	if err != nil {
		return nil, fmt.Errorf("error reading app manifest: %v", err)
	}
	var am schema.ImageManifest
	if err := json.Unmarshal(b, &am); err != nil {
		return nil, fmt.Errorf("error unmarshaling app manifest: %v", err)
	}
	return &am, nil
}
<|endoftext|>


<commit_before>// Copyright 2016 The Gosl Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package ode

import (
	"math"

	"github.com/cpmech/gosl/chk"
	"github.com/cpmech/gosl/la"
	"github.com/cpmech/gosl/utl"
)

// Config holds configuration parameters for the ODE solver
type Config struct {

	// parameters
	Hmin       float64 // minimum H allowed
	IniH       float64 // initial H
	NmaxIt     int     // max num iterations (allowed)
	NmaxSS     int     // max num substeps
	Mmin       float64 // min step multiplier
	Mmax       float64 // max step multiplier
	Mfac       float64 // step multiplier factor
	MfirstRej  float64 // coefficient to multiply stepsize if first step is rejected [0 ⇒ use dxnew]
	PredCtrl   bool    // use Gustafsson's predictive controller
	Eps        float64 // smallest number satisfying 1.0 + ϵ > 1.0
	ThetaMax   float64 // max theta to decide whether the Jacobian should be recomputed or not
	C1h        float64 // c1 of HW-VII p124 => min ratio to retain previous h
	C2h        float64 // c2 of HW-VII p124 => max ratio to retain previous h
	LerrStrat  int     // strategy to select local error computation method
	GoChan     bool    // allow use of go channels (threaded); e.g. to solve R and C systems concurrently
	CteTg      bool    // use constant tangent (Jacobian) in BwEuler
	UseRmsNorm bool    // use RMS norm instead of Euclidean in BwEuler
	Verbose    bool    // show messages, e.g. during iterations
	ZeroTrial  bool    // always start iterations with zero trial values (instead of collocation interpolation)
	StabBeta   float64 // Lund stabilisation coefficient β

	// stiffness detection
	StiffNstp  int     // number of steps to check stiff situation. 0 ⇒ no check. [default = 1]
	StiffRsMax float64 // maximum value of ρs [default = 0.5]
	StiffNyes  int     // number of "yes" stiff steps allowed [default = 15]
	StiffNnot  int     // number of "not" stiff steps to disregard stiffness [default = 6]

	// configurations for linear solver
	LinSolConfig *la.SparseConfig // configurations for sparse linear solver

	// output
	stepF     StepOutF  // function to process step output (of accepted steps) [may be nil]
	denseF    DenseOutF // function to process dense output [may be nil]
	denseDx   float64   // step size for dense output
	stepOut   bool      // perform output of (variable) steps
	denseOut  bool      // perform dense output is active
	denseNstp int       // number of dense steps

	// internal data
	method    string  // the ODE method
	stabBetaM float64 // factor to multiply stabilisation coefficient β

	// linear solver control
	lsKind string // linear solver kind

	// tolerances
	atol  float64 // absolute tolerance
	rtol  float64 // relative tolerance
	fnewt float64 // Newton's iterations tolerance

	// coefficients
	rerrPrevMin float64 // min value of rerrPrev

	// fixed steps
	fixed       bool    // use fixed steps
	fixedH      float64 // value of fixed stepsize
	fixedNsteps int     // number of fixed steps
}

// NewConfig returns a new [default] set of configuration parameters
//   method -- the ODE method: e.g. fweuler, bweuler, radau5, moeuler, dopri5
//   lsKind -- kind of linear solver: "umfpack" or "mumps" [may be empty]
func NewConfig(method string, lsKind string) (o *Config) {

	// check kind of linear solver
	if lsKind != "" && lsKind != "umfpack" && lsKind != "mumps" {
		chk.Panic("lsKind must be empty or \"umfpack\" or \"mumps\"")
	}
	if lsKind == "" {
		lsKind = "umfpack"
	}

	// parameters
	o = new(Config)
	o.ZeroTrial = false
	o.Hmin = 1.0e-10
	o.IniH = 1.0e-4
	o.NmaxIt = 7
	o.NmaxSS = 1000
	o.Mmin = 0.125
	o.Mmax = 5.0
	o.Mfac = 0.9
	o.MfirstRej = 0.1
	o.PredCtrl = true
	o.Eps = 1.0e-16
	o.ThetaMax = 1.0e-3
	o.C1h = 1.0
	o.C2h = 1.2
	o.LerrStrat = 3
	o.GoChan = true
	o.CteTg = false
	o.UseRmsNorm = true
	o.Verbose = false

	// stiffness detection
	o.StiffNstp = 0
	o.StiffRsMax = 0.5
	o.StiffNyes = 15
	o.StiffNnot = 6

	// configurations for linear solver
	o.LinSolConfig = la.NewSparseConfig()

	// internal data
	o.method = method

	// linear solver control
	o.lsKind = lsKind

	// set tolerances
	o.SetTols(1e-4, 1e-4)

	// coefficients
	o.rerrPrevMin = 1e-4
	switch method {
	case "radau5":
		o.rerrPrevMin = 1e-2
	case "dopri5":
		o.StabBeta = 0.04
		o.stabBetaM = 0.75
	case "dopri8":
		o.stabBetaM = 0.2
	}
	return
}

// SetTols sets tolerances according to Hairer and Wanner' suggestions
//   atol   -- absolute tolerance; use 0 for default [default = 1e-4]
//   rtol   -- relative tolerance; use 0 for default [default = 1e-4]
func (o *Config) SetTols(atol, rtol float64) {

	// check
	if atol <= 0.0 || atol <= 10.0*o.Eps {
		chk.Panic("tolerances are too small: Atol=%v, Rtol=%v", atol, atol)
	}

	// set
	o.atol, o.rtol = atol, rtol

	// check and change the tolerances [radau5 only]
	if o.method == "radau5" {
		β := 2.0 / 3.0
		quot := o.atol / o.rtol
		o.rtol = 0.1 * math.Pow(o.rtol, β)
		o.atol = o.rtol * quot
	}

	// tolerance for iterations
	o.fnewt = utl.Max(10.0*o.Eps/o.rtol, utl.Min(0.03, math.Sqrt(o.rtol)))
}

// SetTol sets both tolerances: Atol and Rtol
func (o *Config) SetTol(atolAndRtol float64) {
	o.SetTols(atolAndRtol, atolAndRtol)
}

// SetFixedH calculates the number of steps, the exact stepsize h, and set to use fixed stepsize
func (o *Config) SetFixedH(dxApprox, xf float64) {
	o.fixed = true
	o.fixedNsteps = int(math.Ceil(xf / dxApprox))
	o.fixedH = xf / float64(o.fixedNsteps)
	xfinal := float64(o.fixedNsteps) * o.fixedH
	if math.Abs(xfinal-xf) > 1e-14 {
		chk.Panic("_internal_: xfinal should be equal to xf. xfinal-xf=%25.18e\n", xfinal-xf)
	}
}

// SetStepOut activates output of (variable) steps
//  save -- save all values
//  out  -- function to be during step output [may be nil]
func (o *Config) SetStepOut(save bool, out StepOutF) {
	o.stepOut = save
	o.stepF = out
}

// SetDenseOut activates dense output
//  save -- save all values
//  out  -- function to be during dense output [may be nil]
func (o *Config) SetDenseOut(save bool, dxOut, xf float64, out DenseOutF) {
	if dxOut > 0 {
		o.denseOut = save
		o.denseF = out
		o.denseNstp = int(math.Ceil(xf / dxOut))
		o.denseDx = dxOut
	}
}
<commit_msg>Fix typos<commit_after>// Copyright 2016 The Gosl Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package ode

import (
	"math"

	"github.com/cpmech/gosl/chk"
	"github.com/cpmech/gosl/la"
	"github.com/cpmech/gosl/utl"
)

// Config holds configuration parameters for the ODE solver
type Config struct {

	// parameters
	Hmin       float64 // minimum H allowed
	IniH       float64 // initial H
	NmaxIt     int     // max num iterations (allowed)
	NmaxSS     int     // max num substeps
	Mmin       float64 // min step multiplier
	Mmax       float64 // max step multiplier
	Mfac       float64 // step multiplier factor
	MfirstRej  float64 // coefficient to multiply stepsize if first step is rejected [0 ⇒ use dxnew]
	PredCtrl   bool    // use Gustafsson's predictive controller
	Eps        float64 // smallest number satisfying 1.0 + ϵ > 1.0
	ThetaMax   float64 // max theta to decide whether the Jacobian should be recomputed or not
	C1h        float64 // c1 of HW-VII p124 => min ratio to retain previous h
	C2h        float64 // c2 of HW-VII p124 => max ratio to retain previous h
	LerrStrat  int     // strategy to select local error computation method
	GoChan     bool    // allow use of go channels (threaded); e.g. to solve R and C systems concurrently
	CteTg      bool    // use constant tangent (Jacobian) in BwEuler
	UseRmsNorm bool    // use RMS norm instead of Euclidean in BwEuler
	Verbose    bool    // show messages, e.g. during iterations
	ZeroTrial  bool    // always start iterations with zero trial values (instead of collocation interpolation)
	StabBeta   float64 // Lund stabilization coefficient β

	// stiffness detection
	StiffNstp  int     // number of steps to check stiff situation. 0 ⇒ no check. [default = 1]
	StiffRsMax float64 // maximum value of ρs [default = 0.5]
	StiffNyes  int     // number of "yes" stiff steps allowed [default = 15]
	StiffNnot  int     // number of "not" stiff steps to disregard stiffness [default = 6]

	// configurations for linear solver
	LinSolConfig *la.SparseConfig // configurations for sparse linear solver

	// output
	stepF     StepOutF  // function to process step output (of accepted steps) [may be nil]
	denseF    DenseOutF // function to process dense output [may be nil]
	denseDx   float64   // step size for dense output
	stepOut   bool      // perform output of (variable) steps
	denseOut  bool      // perform dense output is active
	denseNstp int       // number of dense steps

	// internal data
	method    string  // the ODE method
	stabBetaM float64 // factor to multiply stabilization coefficient β

	// linear solver control
	lsKind string // linear solver kind

	// tolerances
	atol  float64 // absolute tolerance
	rtol  float64 // relative tolerance
	fnewt float64 // Newton's iterations tolerance

	// coefficients
	rerrPrevMin float64 // min value of rerrPrev

	// fixed steps
	fixed       bool    // use fixed steps
	fixedH      float64 // value of fixed stepsize
	fixedNsteps int     // number of fixed steps
}

// NewConfig returns a new [default] set of configuration parameters
//   method -- the ODE method: e.g. fweuler, bweuler, radau5, moeuler, dopri5
//   lsKind -- kind of linear solver: "umfpack" or "mumps" [may be empty]
func NewConfig(method string, lsKind string) (o *Config) {

	// check kind of linear solver
	if lsKind != "" && lsKind != "umfpack" && lsKind != "mumps" {
		chk.Panic("lsKind must be empty or \"umfpack\" or \"mumps\"")
	}
	if lsKind == "" {
		lsKind = "umfpack"
	}

	// parameters
	o = new(Config)
	o.ZeroTrial = false
	o.Hmin = 1.0e-10
	o.IniH = 1.0e-4
	o.NmaxIt = 7
	o.NmaxSS = 1000
	o.Mmin = 0.125
	o.Mmax = 5.0
	o.Mfac = 0.9
	o.MfirstRej = 0.1
	o.PredCtrl = true
	o.Eps = 1.0e-16
	o.ThetaMax = 1.0e-3
	o.C1h = 1.0
	o.C2h = 1.2
	o.LerrStrat = 3
	o.GoChan = true
	o.CteTg = false
	o.UseRmsNorm = true
	o.Verbose = false

	// stiffness detection
	o.StiffNstp = 0
	o.StiffRsMax = 0.5
	o.StiffNyes = 15
	o.StiffNnot = 6

	// configurations for linear solver
	o.LinSolConfig = la.NewSparseConfig()

	// internal data
	o.method = method

	// linear solver control
	o.lsKind = lsKind

	// set tolerances
	o.SetTols(1e-4, 1e-4)

	// coefficients
	o.rerrPrevMin = 1e-4
	switch method {
	case "radau5":
		o.rerrPrevMin = 1e-2
	case "dopri5":
		o.StabBeta = 0.04
		o.stabBetaM = 0.75
	case "dopri8":
		o.stabBetaM = 0.2
	}
	return
}

// SetTols sets tolerances according to Hairer and Wanner' suggestions
//   atol   -- absolute tolerance; use 0 for default [default = 1e-4]
//   rtol   -- relative tolerance; use 0 for default [default = 1e-4]
func (o *Config) SetTols(atol, rtol float64) {

	// check
	if atol <= 0.0 || atol <= 10.0*o.Eps {
		chk.Panic("tolerances are too small: Atol=%v, Rtol=%v", atol, atol)
	}

	// set
	o.atol, o.rtol = atol, rtol

	// check and change the tolerances [radau5 only]
	if o.method == "radau5" {
		β := 2.0 / 3.0
		quot := o.atol / o.rtol
		o.rtol = 0.1 * math.Pow(o.rtol, β)
		o.atol = o.rtol * quot
	}

	// tolerance for iterations
	o.fnewt = utl.Max(10.0*o.Eps/o.rtol, utl.Min(0.03, math.Sqrt(o.rtol)))
}

// SetTol sets both tolerances: Atol and Rtol
func (o *Config) SetTol(atolAndRtol float64) {
	o.SetTols(atolAndRtol, atolAndRtol)
}

// SetFixedH calculates the number of steps, the exact stepsize h, and set to use fixed stepsize
func (o *Config) SetFixedH(dxApprox, xf float64) {
	o.fixed = true
	o.fixedNsteps = int(math.Ceil(xf / dxApprox))
	o.fixedH = xf / float64(o.fixedNsteps)
	xfinal := float64(o.fixedNsteps) * o.fixedH
	if math.Abs(xfinal-xf) > 1e-14 {
		chk.Panic("_internal_: xfinal should be equal to xf. xfinal-xf=%25.18e\n", xfinal-xf)
	}
}

// SetStepOut activates output of (variable) steps
//  save -- save all values
//  out  -- function to be during step output [may be nil]
func (o *Config) SetStepOut(save bool, out StepOutF) {
	o.stepOut = save
	o.stepF = out
}

// SetDenseOut activates dense output
//  save -- save all values
//  out  -- function to be during dense output [may be nil]
func (o *Config) SetDenseOut(save bool, dxOut, xf float64, out DenseOutF) {
	if dxOut > 0 {
		o.denseOut = save
		o.denseF = out
		o.denseNstp = int(math.Ceil(xf / dxOut))
		o.denseDx = dxOut
	}
}
<|endoftext|>


<commit_before>package snowball

import "testing"

// Stuct for holding tests where a word is transformed
// into another by the function to be tested.
//
type simpleStringTestCase struct {
	wordIn  string
	wordOut string
}

// A type representing all functions that take one
// string and return another.
type simpleStringFunction func(string) string

// Runs a series of test cases for functions that just
// transform one string into another.
//
func runSimpleStringTests(t *testing.T, f simpleStringFunction, tcs []simpleStringTestCase) {
	for _, testCase := range tcs {
		output := f(testCase.wordIn)
		if output != testCase.wordOut {
			t.Errorf("Expected \"%v\", but got \"%v\"", testCase.wordOut, output)
		}
	}
}

func Test_constants(t *testing.T) {
	expectedVowels := "aeiouy"
	if vowels != "aeiouy" {
		t.Errorf("Expected %v, got %v", expectedVowels, vowels)
	}
}

// Test stopWords for things we know should be true
// or false.
//
func Test_stopWords(t *testing.T) {

	// Test true
	knownTrueStopwords := [...]string{
		"a",
		"for",
		"be",
		"was",
	}
	for _, word := range knownTrueStopwords {
		if stopWords[word] == false {
			t.Errorf("Expected %v, to be in stopWords", word)
		}
	}

	// Test false
	knownFalseStopwords := [...]string{
		"truck",
		"deoxyribonucleic",
		"farse",
		"bullschnizzle",
	}
	for _, word := range knownFalseStopwords {
		if stopWords[word] == true {
			t.Errorf("Expected %v, to be in stopWords", word)
		}
	}
}

// Test specialWords for things we know should be present
// and not present.
//
func Test_specialWords(t *testing.T) {

	// Test true
	knownTrueSpecialwords := [...]string{
		"exceeding",
		"early",
		"outing",
	}
	for _, word := range knownTrueSpecialwords {
		if _, ok := specialWords[word]; !ok {
			t.Errorf("Expected %v, to be in specialWords", word)
		}
	}

	// Test false
	knownFalseSpecialwords := [...]string{
		"truck",
		"deoxyribonucleic",
		"farse",
		"bullschnizzle",
	}
	for _, word := range knownFalseSpecialwords {
		if _, ok := specialWords[word]; ok {
			t.Errorf("Expected %v, to NOT be in specialWords", word)
		}
	}
}

func Test_normalizeApostrophes(t *testing.T) {
	variants := [...]string{
		"\u2019xxx\u2019",
		"\u2018xxx\u2018",
		"\u201Bxxx\u201B",
		"’xxx’",
		"‘xxx‘",
		"‛xxx‛",
	}
	for _, v := range variants {
		normalizedVersion := normalizeApostrophes(v)
		if normalizedVersion != "'xxx'" {
			t.Errorf("Expected \"'xxx'\", not \"%v\"", normalizedVersion)
		}
	}
}

func Test_isLowerVowel(t *testing.T) {
	for _, r := range vowels {
		if isLowerVowel(r) == false {
			t.Errorf("Expected \"%v\" to be a vowel", r)
		}
	}

	consonant := "bcdfghjklmnpqrstvwxz"
	for _, r := range consonant {
		if isLowerVowel(r) == true {
			t.Errorf("Expected \"%v\" to NOT be a vowel", r)
		}
	}
}

func Test_capitalizeYs(t *testing.T) {
	var wordTests = []simpleStringTestCase{
		{"ysdcsdeysdfsysdfsdiyoyyyxyxayxey", "YsdcsdeYsdfsysdfsdiYoYyYxyxaYxeY"},
	}
	runSimpleStringTests(t, capitalizeYs, wordTests)
}

func Test_preprocessWord(t *testing.T) {
	var wordTests = []simpleStringTestCase{
		{"arguing", "arguing"},
		{"Arguing", "arguing"},
		{"'catty", "catty"},
		{"Kyle’s", "kyle's"},
		{"toy", "toY"},
	}
	runSimpleStringTests(t, preprocessWord, wordTests)
}

func Test_vnvSuffix(t *testing.T) {
	var wordTests = []simpleStringTestCase{
		{"crepuscular", "uscular"},
		{"uscular", "cular"},
	}
	runSimpleStringTests(t, vnvSuffix, wordTests)
}

func Test_r1r2(t *testing.T) {
	var wordTests = []struct {
		word string
		r1   string
		r2   string
	}{
		{"crepuscular", "uscular", "cular"},
		{"beautiful", "iful", "ul"},
		{"beauty", "y", ""},
		{"eucharist", "harist", "ist"},
		{"animadversion", "imadversion", "adversion"},
		{"mistresses", "tresses", "ses"},
		{"sprinkled", "kled", ""},
		// Special cases below
		{"communism", "ism", "m"},
		{"arsenal", "al", ""},
		{"generalities", "alities", "ities"},
		{"embed", "bed", ""},
	}
	for _, testCase := range wordTests {
		r1, r2 := r1r2(testCase.word)
		if r1 != testCase.r1 || r2 != testCase.r2 {
			t.Errorf("Expected \"{%v, %v}\", but got \"{%v, %v}\"", testCase.r1, testCase.r2, r1, r2)
		}
	}
}

func Test_replaceWordR1R2Suffix(t *testing.T) {
	var wordTests = []struct {
		wordIn   string
		r1in     string
		r2in     string
		suffix   string
		repl     string
		known    bool
		wordOut  string
		r1out    string
		r2out    string
		replaced bool
	}{
		{"animadversion", "imadversion", "adversion", "version", "", false, "animad", "imad", "ad", true},
		{"animadversion", "imadversion", "adversion", "version", "", true, "animad", "imad", "ad", true},
		{"animadversion", "imadversion", "", "version", "", true, "animad", "imad", "", true},
		{"animadversion", "imadversion", "adversion", "version", "xx", false, "animadxx", "imadxx", "adxx", true},
		{"animadversion", "imadversion", "adversion", "versionXX", "yy", false, "animadversion", "imadversion", "adversion", false},
		{"animadversion", "imadversion", "adversion", "versionXX", "yy", false, "animadversion", "imadversion", "adversion", false},
	}
	for _, testCase := range wordTests {
		wordOut, r1out, r2out, replaced := replaceWordR1R2Suffix(testCase.wordIn, testCase.r1in, testCase.r2in, testCase.suffix, testCase.repl, testCase.known)
		if wordOut != testCase.wordOut || r1out != testCase.r1out || r2out != testCase.r2out || replaced != testCase.replaced {
			t.Errorf("Expected \"{%v, %v, %v, %v}\", but got \"{%v, %v, %v, %v}\"", testCase.wordOut, testCase.r1out, testCase.r2out, testCase.replaced, wordOut, r1out, r2out, replaced)
		}
	}
}

type stepFunc func(string, string, string) (string, string, string)

func runStepTest(t *testing.T, f stepFunc, tcs []testCases) {

}

func Test_step0(t *testing.T) {
	var wordTests = []struct {
		wordIn  string
		r1in    string
		r2in    string
		wordOut string
		r1out   string
		r2out   string
	}{
		{"general's", "al's", "", "general", "al", ""},
		{"general's'", "al's'", "", "general", "al", ""},
		{"spices'", "es'", "", "spices", "es", ""},
	}
	for _, testCase := range wordTests {
		wordOut, r1out, r2out := step0(testCase.wordIn, testCase.r1in, testCase.r2in)
		if wordOut != testCase.wordOut || r1out != testCase.r1out || r2out != testCase.r2out {
			t.Errorf("Expected \"{%v, %v, %v}\", but got \"{%v, %v, %v}\"", testCase.wordOut, testCase.r1out, testCase.r2out, wordOut, r1out, r2out)
		}
	}
}

func Test_step1a(t *testing.T) {
	var wordTests = []struct {
		wordIn  string
		r1in    string
		r2in    string
		wordOut string
		r1out   string
		r2out   string
	}{
		{"ties", "ties", "ties", "tie", "tie", "tie"},
		{"cries", "cries", "cries", "cri", "cri", "cri"},
		{"mistresses", "tresses", "ses", "mistress", "tress", ""},
	}
	for _, testCase := range wordTests {
		wordOut, r1out, r2out := step1a(testCase.wordIn, testCase.r1in, testCase.r2in)
		if wordOut != testCase.wordOut || r1out != testCase.r1out || r2out != testCase.r2out {
			t.Errorf("Expected \"{%v, %v, %v}\", but got \"{%v, %v, %v}\"", testCase.wordOut, testCase.r1out, testCase.r2out, wordOut, r1out, r2out)
		}
	}
}

func Test_hasVowel(t *testing.T) {
	var wordTests = []struct {
		word      string
		leftSkip  int
		rightSkip int
		result    bool
	}{
		{"television", 0, 0, true},
		{"xxxaxxx", 0, 0, true},
		{"xxxaxxx", 1, 1, true},
		{"xxxaxxx", 1, 4, false},
		{"xxxaxxx", 4, 1, false},
		{"xxxaxxx", 4, 10, false},
		{"xxxaxxx", 4, 5, false},
		{"axxxxxx", 1, 0, false},
		{"xxxxxxa", 0, 1, false},
	}
	for _, testCase := range wordTests {
		result := hasVowel(testCase.word, testCase.leftSkip, testCase.rightSkip)
		if result != testCase.result {
			t.Errorf("Expected %v, but got %v for \"{%v, %v, %v}\"", testCase.result, result, testCase.word, testCase.leftSkip, testCase.rightSkip)
		}
	}
}

func Test_isShortWord(t *testing.T) {
	// bed, shed and shred are short words, bead, embed, beds are not short words.
	var wordTests = []struct {
		word    string
		isShort bool
	}{
		{"bed", true},
		{"shed", true},
		{"shred", true},
		{"bead", false},
		{"embed", false},
		{"beds", false},
	}
	for _, testCase := range wordTests {
		r1, _ := r1r2(testCase.word)
		isShort := isShortWord(testCase.word, r1)
		if isShort != testCase.isShort {
			t.Errorf("Expected %v, but got %v for \"{%v, %v}\"", testCase.isShort, isShort, testCase.word, r1)
		}
	}
}
func Test_firstSuffix(t *testing.T) {
	var wordTests = []struct {
		word      string
		sufficies []string
		suffix    string
		found     bool
	}{
		{"mistresses", []string{"tresses", "ses"}, "tresses", true},
	}
	for _, testCase := range wordTests {
		suffix, found := firstSuffix(testCase.word, testCase.sufficies...)
		if suffix != testCase.suffix || found != testCase.found {
			t.Errorf("Expected \"{%v, %v, %v}\", but got \"{%v, %v, %v}\"", testCase.suffix, testCase.found, suffix, found)
		}
	}
}
<commit_msg>Refactor tests<commit_after>package snowball

import "testing"

// Stuct for holding tests where a word is transformed
// into another by the function to be tested.
//
type simpleStringTestCase struct {
	wordIn  string
	wordOut string
}

// A type representing all functions that take one
// string and return another.
type simpleStringFunction func(string) string

// Runs a series of test cases for functions that just
// transform one string into another.
//
func runSimpleStringTests(t *testing.T, f simpleStringFunction, tcs []simpleStringTestCase) {
	for _, testCase := range tcs {
		output := f(testCase.wordIn)
		if output != testCase.wordOut {
			t.Errorf("Expected \"%v\", but got \"%v\"", testCase.wordOut, output)
		}
	}
}

func Test_constants(t *testing.T) {
	expectedVowels := "aeiouy"
	if vowels != "aeiouy" {
		t.Errorf("Expected %v, got %v", expectedVowels, vowels)
	}
}

// Test stopWords for things we know should be true
// or false.
//
func Test_stopWords(t *testing.T) {

	// Test true
	knownTrueStopwords := [...]string{
		"a",
		"for",
		"be",
		"was",
	}
	for _, word := range knownTrueStopwords {
		if stopWords[word] == false {
			t.Errorf("Expected %v, to be in stopWords", word)
		}
	}

	// Test false
	knownFalseStopwords := [...]string{
		"truck",
		"deoxyribonucleic",
		"farse",
		"bullschnizzle",
	}
	for _, word := range knownFalseStopwords {
		if stopWords[word] == true {
			t.Errorf("Expected %v, to be in stopWords", word)
		}
	}
}

// Test specialWords for things we know should be present
// and not present.
//
func Test_specialWords(t *testing.T) {

	// Test true
	knownTrueSpecialwords := [...]string{
		"exceeding",
		"early",
		"outing",
	}
	for _, word := range knownTrueSpecialwords {
		if _, ok := specialWords[word]; !ok {
			t.Errorf("Expected %v, to be in specialWords", word)
		}
	}

	// Test false
	knownFalseSpecialwords := [...]string{
		"truck",
		"deoxyribonucleic",
		"farse",
		"bullschnizzle",
	}
	for _, word := range knownFalseSpecialwords {
		if _, ok := specialWords[word]; ok {
			t.Errorf("Expected %v, to NOT be in specialWords", word)
		}
	}
}

func Test_normalizeApostrophes(t *testing.T) {
	variants := [...]string{
		"\u2019xxx\u2019",
		"\u2018xxx\u2018",
		"\u201Bxxx\u201B",
		"’xxx’",
		"‘xxx‘",
		"‛xxx‛",
	}
	for _, v := range variants {
		normalizedVersion := normalizeApostrophes(v)
		if normalizedVersion != "'xxx'" {
			t.Errorf("Expected \"'xxx'\", not \"%v\"", normalizedVersion)
		}
	}
}

func Test_isLowerVowel(t *testing.T) {
	for _, r := range vowels {
		if isLowerVowel(r) == false {
			t.Errorf("Expected \"%v\" to be a vowel", r)
		}
	}

	consonant := "bcdfghjklmnpqrstvwxz"
	for _, r := range consonant {
		if isLowerVowel(r) == true {
			t.Errorf("Expected \"%v\" to NOT be a vowel", r)
		}
	}
}

func Test_capitalizeYs(t *testing.T) {
	var wordTests = []simpleStringTestCase{
		{"ysdcsdeysdfsysdfsdiyoyyyxyxayxey", "YsdcsdeYsdfsysdfsdiYoYyYxyxaYxeY"},
	}
	runSimpleStringTests(t, capitalizeYs, wordTests)
}

func Test_preprocessWord(t *testing.T) {
	var wordTests = []simpleStringTestCase{
		{"arguing", "arguing"},
		{"Arguing", "arguing"},
		{"'catty", "catty"},
		{"Kyle’s", "kyle's"},
		{"toy", "toY"},
	}
	runSimpleStringTests(t, preprocessWord, wordTests)
}

func Test_vnvSuffix(t *testing.T) {
	var wordTests = []simpleStringTestCase{
		{"crepuscular", "uscular"},
		{"uscular", "cular"},
	}
	runSimpleStringTests(t, vnvSuffix, wordTests)
}

func Test_r1r2(t *testing.T) {
	var wordTests = []struct {
		word string
		r1   string
		r2   string
	}{
		{"crepuscular", "uscular", "cular"},
		{"beautiful", "iful", "ul"},
		{"beauty", "y", ""},
		{"eucharist", "harist", "ist"},
		{"animadversion", "imadversion", "adversion"},
		{"mistresses", "tresses", "ses"},
		{"sprinkled", "kled", ""},
		// Special cases below
		{"communism", "ism", "m"},
		{"arsenal", "al", ""},
		{"generalities", "alities", "ities"},
		{"embed", "bed", ""},
	}
	for _, testCase := range wordTests {
		r1, r2 := r1r2(testCase.word)
		if r1 != testCase.r1 || r2 != testCase.r2 {
			t.Errorf("Expected \"{%v, %v}\", but got \"{%v, %v}\"", testCase.r1, testCase.r2, r1, r2)
		}
	}
}

func Test_replaceWordR1R2Suffix(t *testing.T) {
	var wordTests = []struct {
		wordIn   string
		r1in     string
		r2in     string
		suffix   string
		repl     string
		known    bool
		wordOut  string
		r1out    string
		r2out    string
		replaced bool
	}{
		{"animadversion", "imadversion", "adversion", "version", "", false, "animad", "imad", "ad", true},
		{"animadversion", "imadversion", "adversion", "version", "", true, "animad", "imad", "ad", true},
		{"animadversion", "imadversion", "", "version", "", true, "animad", "imad", "", true},
		{"animadversion", "imadversion", "adversion", "version", "xx", false, "animadxx", "imadxx", "adxx", true},
		{"animadversion", "imadversion", "adversion", "versionXX", "yy", false, "animadversion", "imadversion", "adversion", false},
		{"animadversion", "imadversion", "adversion", "versionXX", "yy", false, "animadversion", "imadversion", "adversion", false},
	}
	for _, testCase := range wordTests {
		wordOut, r1out, r2out, replaced := replaceWordR1R2Suffix(testCase.wordIn, testCase.r1in, testCase.r2in, testCase.suffix, testCase.repl, testCase.known)
		if wordOut != testCase.wordOut || r1out != testCase.r1out || r2out != testCase.r2out || replaced != testCase.replaced {
			t.Errorf("Expected \"{%v, %v, %v, %v}\", but got \"{%v, %v, %v, %v}\"", testCase.wordOut, testCase.r1out, testCase.r2out, testCase.replaced, wordOut, r1out, r2out, replaced)
		}
	}
}

type stepFunc func(string, string, string) (string, string, string)
type stepTest struct {
	wordIn  string
	r1in    string
	r2in    string
	wordOut string
	r1out   string
	r2out   string
}

func runStepTest(t *testing.T, f stepFunc, tcs []stepTest) {
	for _, testCase := range tcs {
		wordOut, r1out, r2out := f(testCase.wordIn, testCase.r1in, testCase.r2in)
		if wordOut != testCase.wordOut || r1out != testCase.r1out || r2out != testCase.r2out {
			t.Errorf("Expected \"{%v, %v, %v}\", but got \"{%v, %v, %v}\"", testCase.wordOut, testCase.r1out, testCase.r2out, wordOut, r1out, r2out)
		}
	}
}

func Test_step0(t *testing.T) {
	var testCases = []stepTest{
		{"general's", "al's", "", "general", "al", ""},
		{"general's'", "al's'", "", "general", "al", ""},
		{"spices'", "es'", "", "spices", "es", ""},
	}
	runStepTest(t, step0, testCases)
}

func Test_step1a(t *testing.T) {
	var testCases = []stepTest{
		{"ties", "ties", "ties", "tie", "tie", "tie"},
		{"cries", "cries", "cries", "cri", "cri", "cri"},
		{"mistresses", "tresses", "ses", "mistress", "tress", ""},
	}
	runStepTest(t, step1a, testCases)
}

func Test_hasVowel(t *testing.T) {
	var testCases = []struct {
		word      string
		leftSkip  int
		rightSkip int
		result    bool
	}{
		{"television", 0, 0, true},
		{"xxxaxxx", 0, 0, true},
		{"xxxaxxx", 1, 1, true},
		{"xxxaxxx", 1, 4, false},
		{"xxxaxxx", 4, 1, false},
		{"xxxaxxx", 4, 10, false},
		{"xxxaxxx", 4, 5, false},
		{"axxxxxx", 1, 0, false},
		{"xxxxxxa", 0, 1, false},
	}
	for _, testCase := range testCases {
		result := hasVowel(testCase.word, testCase.leftSkip, testCase.rightSkip)
		if result != testCase.result {
			t.Errorf("Expected %v, but got %v for \"{%v, %v, %v}\"", testCase.result, result, testCase.word, testCase.leftSkip, testCase.rightSkip)
		}
	}
}

func Test_isShortWord(t *testing.T) {
	// bed, shed and shred are short words, bead, embed, beds are not short words.
	var testCases = []struct {
		word    string
		isShort bool
	}{
		{"bed", true},
		{"shed", true},
		{"shred", true},
		{"bead", false},
		{"embed", false},
		{"beds", false},
	}
	for _, testCase := range testCases {
		r1, _ := r1r2(testCase.word)
		isShort := isShortWord(testCase.word, r1)
		if isShort != testCase.isShort {
			t.Errorf("Expected %v, but got %v for \"{%v, %v}\"", testCase.isShort, isShort, testCase.word, r1)
		}
	}
}
func Test_firstSuffix(t *testing.T) {
	var testCases = []struct {
		word      string
		sufficies []string
		suffix    string
		found     bool
	}{
		{"mistresses", []string{"tresses", "ses"}, "tresses", true},
	}
	for _, testCase := range testCases {
		suffix, found := firstSuffix(testCase.word, testCase.sufficies...)
		if suffix != testCase.suffix || found != testCase.found {
			t.Errorf("Expected \"{%v, %v, %v}\", but got \"{%v, %v, %v}\"", testCase.suffix, testCase.found, suffix, found)
		}
	}
}
<|endoftext|>


<commit_before>// Copyright 2016 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 storage

import (
	"errors"
	"fmt"

	"golang.org/x/net/context"
	raw "google.golang.org/api/storage/v1"
)

// CopierFrom creates a Copier that can copy src to dst.
// You can immediately call Run on the returned Copier, or
// you can configure it first.
func (dst *ObjectHandle) CopierFrom(src *ObjectHandle) *Copier {
	return &Copier{dst: dst, src: src}
}

// A Copier copies a source object to a destination.
type Copier struct {
	// ObjectAttrs are optional attributes to set on the destination object.
	// Any attributes must be initialized before any calls on the Copier. Nil
	// or zero-valued attributes are ignored.
	ObjectAttrs

	// RewriteToken can be set before calling Run to resume a copy
	// operation. After Run returns a non-nil error, RewriteToken will
	// have been updated to contain the value needed to resume the copy.
	RewriteToken string

	// ProgressFunc can be used to monitor the progress of a multi-RPC copy
	// operation. If ProgressFunc is not nil and CopyFrom requires multiple
	// calls to the underlying service (see
	// https://cloud.google.com/storage/docs/json_api/v1/objects/rewrite), then
	// ProgressFunc will be invoked after each call with the number of bytes of
	// content copied so far and the total size in bytes of the source object.
	//
	// ProgressFunc is intended to make upload progress available to the
	// application. For example, the implementation of ProgressFunc may update
	// a progress bar in the application's UI, or log the result of
	// float64(copiedBytes)/float64(totalBytes).
	//
	// ProgressFunc should return quickly without blocking.
	ProgressFunc func(copiedBytes, totalBytes uint64)

	dst, src *ObjectHandle
}

// Run performs the copy.
func (c *Copier) Run(ctx context.Context) (*ObjectAttrs, error) {
	if err := c.src.validate(); err != nil {
		return nil, err
	}
	if err := c.dst.validate(); err != nil {
		return nil, err
	}
	// Convert destination attributes to raw form, omitting the bucket.
	// If the bucket is included but name or content-type aren't, the service
	// returns a 400 with "Required" as the only message. Omitting the bucket
	// does not cause any problems.
	rawObject := c.ObjectAttrs.toRawObject("")
	for {
		res, err := c.callRewrite(ctx, c.src, rawObject)
		if err != nil {
			return nil, err
		}
		if c.ProgressFunc != nil {
			c.ProgressFunc(uint64(res.TotalBytesRewritten), uint64(res.ObjectSize))
		}
		if res.Done { // Finished successfully.
			return newObject(res.Resource), nil
		}
	}
}

func (c *Copier) callRewrite(ctx context.Context, src *ObjectHandle, rawObj *raw.Object) (*raw.RewriteResponse, error) {
	call := c.dst.c.raw.Objects.Rewrite(src.bucket, src.object, c.dst.bucket, c.dst.object, rawObj)

	call.Context(ctx).Projection("full")
	if c.RewriteToken != "" {
		call.RewriteToken(c.RewriteToken)
	}
	if err := applyConds("Copy destination", c.dst.gen, c.dst.conds, call); err != nil {
		return nil, err
	}
	if err := applySourceConds(c.src.gen, c.src.conds, call); err != nil {
		return nil, err
	}
	if err := setEncryptionHeaders(call.Header(), c.dst.encryptionKey, false); err != nil {
		return nil, err
	}
	if err := setEncryptionHeaders(call.Header(), c.src.encryptionKey, true); err != nil {
		return nil, err
	}
	var res *raw.RewriteResponse
	var err error
	setClientHeader(call.Header())
	err = runWithRetry(ctx, func() error { res, err = call.Do(); return err })
	if err != nil {
		return nil, err
	}
	c.RewriteToken = res.RewriteToken
	return res, nil
}

// ComposerFrom creates a Composer that can compose srcs into dst.
// You can immediately call Run on the returned Composer, or you can
// configure it first.
//
// The encryption key for the destination object will be used to decrypt all
// source objects and encrypt the destination object. It is an error
// to specify an encryption key for any of the source objects.
func (dst *ObjectHandle) ComposerFrom(srcs ...*ObjectHandle) *Composer {
	return &Composer{dst: dst, srcs: srcs}
}

// A Composer composes source objects into a destination object.
type Composer struct {
	// ObjectAttrs are optional attributes to set on the destination object.
	// Any attributes must be initialized before any calls on the Composer. Nil
	// or zero-valued attributes are ignored.
	ObjectAttrs

	dst  *ObjectHandle
	srcs []*ObjectHandle
}

// Run performs the compose operation.
func (c *Composer) Run(ctx context.Context) (*ObjectAttrs, error) {
	if err := c.dst.validate(); err != nil {
		return nil, err
	}
	if len(c.srcs) == 0 {
		return nil, errors.New("storage: at least one source object must be specified")
	}

	req := &raw.ComposeRequest{}
	// Compose requires a non-empty Destination, so we always set it,
	// even if the caller-provided ObjectAttrs is the zero value.
	req.Destination = c.ObjectAttrs.toRawObject(c.dst.bucket)
	for _, src := range c.srcs {
		if err := src.validate(); err != nil {
			return nil, err
		}
		if src.bucket != c.dst.bucket {
			return nil, fmt.Errorf("storage: all source objects must be in bucket %q, found %q", c.dst.bucket, src.bucket)
		}
		if src.encryptionKey != nil {
			return nil, fmt.Errorf("storage: compose source %s.%s must not have encryption key", src.bucket, src.object)
		}
		srcObj := &raw.ComposeRequestSourceObjects{
			Name: src.object,
		}
		if err := applyConds("ComposeFrom source", src.gen, src.conds, composeSourceObj{srcObj}); err != nil {
			return nil, err
		}
		req.SourceObjects = append(req.SourceObjects, srcObj)
	}

	call := c.dst.c.raw.Objects.Compose(c.dst.bucket, c.dst.object, req).Context(ctx)
	if err := applyConds("ComposeFrom destination", c.dst.gen, c.dst.conds, call); err != nil {
		return nil, err
	}
	if err := setEncryptionHeaders(call.Header(), c.dst.encryptionKey, false); err != nil {
		return nil, err
	}
	var obj *raw.Object
	var err error
	setClientHeader(call.Header())
	err = runWithRetry(ctx, func() error { obj, err = call.Do(); return err })
	if err != nil {
		return nil, err
	}
	return newObject(obj), nil
}
<commit_msg>storage: fix reference to CopyFrom in doc comment<commit_after>// Copyright 2016 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 storage

import (
	"errors"
	"fmt"

	"golang.org/x/net/context"
	raw "google.golang.org/api/storage/v1"
)

// CopierFrom creates a Copier that can copy src to dst.
// You can immediately call Run on the returned Copier, or
// you can configure it first.
func (dst *ObjectHandle) CopierFrom(src *ObjectHandle) *Copier {
	return &Copier{dst: dst, src: src}
}

// A Copier copies a source object to a destination.
type Copier struct {
	// ObjectAttrs are optional attributes to set on the destination object.
	// Any attributes must be initialized before any calls on the Copier. Nil
	// or zero-valued attributes are ignored.
	ObjectAttrs

	// RewriteToken can be set before calling Run to resume a copy
	// operation. After Run returns a non-nil error, RewriteToken will
	// have been updated to contain the value needed to resume the copy.
	RewriteToken string

	// ProgressFunc can be used to monitor the progress of a multi-RPC copy
	// operation. If ProgressFunc is not nil and copying requires multiple
	// calls to the underlying service (see
	// https://cloud.google.com/storage/docs/json_api/v1/objects/rewrite), then
	// ProgressFunc will be invoked after each call with the number of bytes of
	// content copied so far and the total size in bytes of the source object.
	//
	// ProgressFunc is intended to make upload progress available to the
	// application. For example, the implementation of ProgressFunc may update
	// a progress bar in the application's UI, or log the result of
	// float64(copiedBytes)/float64(totalBytes).
	//
	// ProgressFunc should return quickly without blocking.
	ProgressFunc func(copiedBytes, totalBytes uint64)

	dst, src *ObjectHandle
}

// Run performs the copy.
func (c *Copier) Run(ctx context.Context) (*ObjectAttrs, error) {
	if err := c.src.validate(); err != nil {
		return nil, err
	}
	if err := c.dst.validate(); err != nil {
		return nil, err
	}
	// Convert destination attributes to raw form, omitting the bucket.
	// If the bucket is included but name or content-type aren't, the service
	// returns a 400 with "Required" as the only message. Omitting the bucket
	// does not cause any problems.
	rawObject := c.ObjectAttrs.toRawObject("")
	for {
		res, err := c.callRewrite(ctx, c.src, rawObject)
		if err != nil {
			return nil, err
		}
		if c.ProgressFunc != nil {
			c.ProgressFunc(uint64(res.TotalBytesRewritten), uint64(res.ObjectSize))
		}
		if res.Done { // Finished successfully.
			return newObject(res.Resource), nil
		}
	}
}

func (c *Copier) callRewrite(ctx context.Context, src *ObjectHandle, rawObj *raw.Object) (*raw.RewriteResponse, error) {
	call := c.dst.c.raw.Objects.Rewrite(src.bucket, src.object, c.dst.bucket, c.dst.object, rawObj)

	call.Context(ctx).Projection("full")
	if c.RewriteToken != "" {
		call.RewriteToken(c.RewriteToken)
	}
	if err := applyConds("Copy destination", c.dst.gen, c.dst.conds, call); err != nil {
		return nil, err
	}
	if err := applySourceConds(c.src.gen, c.src.conds, call); err != nil {
		return nil, err
	}
	if err := setEncryptionHeaders(call.Header(), c.dst.encryptionKey, false); err != nil {
		return nil, err
	}
	if err := setEncryptionHeaders(call.Header(), c.src.encryptionKey, true); err != nil {
		return nil, err
	}
	var res *raw.RewriteResponse
	var err error
	setClientHeader(call.Header())
	err = runWithRetry(ctx, func() error { res, err = call.Do(); return err })
	if err != nil {
		return nil, err
	}
	c.RewriteToken = res.RewriteToken
	return res, nil
}

// ComposerFrom creates a Composer that can compose srcs into dst.
// You can immediately call Run on the returned Composer, or you can
// configure it first.
//
// The encryption key for the destination object will be used to decrypt all
// source objects and encrypt the destination object. It is an error
// to specify an encryption key for any of the source objects.
func (dst *ObjectHandle) ComposerFrom(srcs ...*ObjectHandle) *Composer {
	return &Composer{dst: dst, srcs: srcs}
}

// A Composer composes source objects into a destination object.
type Composer struct {
	// ObjectAttrs are optional attributes to set on the destination object.
	// Any attributes must be initialized before any calls on the Composer. Nil
	// or zero-valued attributes are ignored.
	ObjectAttrs

	dst  *ObjectHandle
	srcs []*ObjectHandle
}

// Run performs the compose operation.
func (c *Composer) Run(ctx context.Context) (*ObjectAttrs, error) {
	if err := c.dst.validate(); err != nil {
		return nil, err
	}
	if len(c.srcs) == 0 {
		return nil, errors.New("storage: at least one source object must be specified")
	}

	req := &raw.ComposeRequest{}
	// Compose requires a non-empty Destination, so we always set it,
	// even if the caller-provided ObjectAttrs is the zero value.
	req.Destination = c.ObjectAttrs.toRawObject(c.dst.bucket)
	for _, src := range c.srcs {
		if err := src.validate(); err != nil {
			return nil, err
		}
		if src.bucket != c.dst.bucket {
			return nil, fmt.Errorf("storage: all source objects must be in bucket %q, found %q", c.dst.bucket, src.bucket)
		}
		if src.encryptionKey != nil {
			return nil, fmt.Errorf("storage: compose source %s.%s must not have encryption key", src.bucket, src.object)
		}
		srcObj := &raw.ComposeRequestSourceObjects{
			Name: src.object,
		}
		if err := applyConds("ComposeFrom source", src.gen, src.conds, composeSourceObj{srcObj}); err != nil {
			return nil, err
		}
		req.SourceObjects = append(req.SourceObjects, srcObj)
	}

	call := c.dst.c.raw.Objects.Compose(c.dst.bucket, c.dst.object, req).Context(ctx)
	if err := applyConds("ComposeFrom destination", c.dst.gen, c.dst.conds, call); err != nil {
		return nil, err
	}
	if err := setEncryptionHeaders(call.Header(), c.dst.encryptionKey, false); err != nil {
		return nil, err
	}
	var obj *raw.Object
	var err error
	setClientHeader(call.Header())
	err = runWithRetry(ctx, func() error { obj, err = call.Do(); return err })
	if err != nil {
		return nil, err
	}
	return newObject(obj), nil
}
<|endoftext|>


<commit_before>/*
   Hockeypuck - OpenPGP key server
   Copyright (C) 2012-2014  Casey Marshall

   This program is free software: you can redistribute it and/or modify
   it under the terms of the GNU Affero General Public License as published by
   the Free Software Foundation, version 3.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU Affero General Public License for more details.

   You should have received a copy of the GNU Affero General Public License
   along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

package mgohkp

import (
	"bytes"
	"io/ioutil"
	"net/http"
	"net/http/httptest"
	"net/url"
	stdtesting "testing"

	"github.com/facebookgo/mgotest"
	"github.com/hockeypuck/testing"
	"github.com/julienschmidt/httprouter"
	gc "gopkg.in/check.v1"
	"gopkg.in/mgo.v2"
	"gopkg.in/mgo.v2/bson"

	"gopkg.in/hockeypuck/hkp.v0"
	"gopkg.in/hockeypuck/openpgp.v0"
)

func Test(t *stdtesting.T) { gc.TestingT(t) }

type MgoSuite struct {
	storage *storage
	mgoSrv  *mgotest.Server
	session *mgo.Session
	srv     *httptest.Server
}

var _ = gc.Suite(&MgoSuite{})

func (s *MgoSuite) SetUpTest(c *gc.C) {
	s.mgoSrv = mgotest.NewStartedServer(c)
	s.session = s.mgoSrv.Session()
	st, err := NewStorage(s.session)
	c.Assert(err, gc.IsNil)
	s.storage = st.(*storage)

	r := httprouter.New()
	handler := hkp.NewHandler(s.storage)
	handler.Register(r)
	s.srv = httptest.NewServer(r)
}

func (s *MgoSuite) TearDownTest(c *gc.C) {
	s.srv.Close()
	s.session.Close()
	s.mgoSrv.Stop()
}

func (s *MgoSuite) addKey(c *gc.C, keyname string) {
	keytext, err := ioutil.ReadAll(testing.MustInput(keyname))
	c.Assert(err, gc.IsNil)
	res, err := http.PostForm(s.srv.URL+"/pks/add", url.Values{
		"keytext": []string{string(keytext)},
	})
	c.Assert(err, gc.IsNil)
	c.Assert(res.StatusCode, gc.Equals, http.StatusOK)
	defer res.Body.Close()
	_, err = ioutil.ReadAll(res.Body)
	c.Assert(err, gc.IsNil)
}

func (s *MgoSuite) TestMD5(c *gc.C) {
	res, err := http.Get(s.srv.URL + "/pks/lookup?op=hget&search=da84f40d830a7be2a3c0b7f2e146bfaa")
	c.Assert(err, gc.IsNil)
	res.Body.Close()
	c.Assert(err, gc.IsNil)
	c.Assert(res.StatusCode, gc.Equals, http.StatusNotFound)

	s.addKey(c, "sksdigest.asc")
	session, coll := s.storage.c()
	defer session.Close()
	var doc keyDoc
	err = coll.Find(bson.D{{"md5", "da84f40d830a7be2a3c0b7f2e146bfaa"}}).One(&doc)
	c.Assert(err, gc.IsNil)

	res, err = http.Get(s.srv.URL + "/pks/lookup?op=hget&search=da84f40d830a7be2a3c0b7f2e146bfaa")
	c.Assert(err, gc.IsNil)
	armor, err := ioutil.ReadAll(res.Body)
	res.Body.Close()
	c.Assert(err, gc.IsNil)
	c.Assert(res.StatusCode, gc.Equals, http.StatusOK)

	keys := openpgp.MustReadArmorKeys(bytes.NewBuffer(armor)).MustParse()
	c.Assert(keys, gc.HasLen, 1)
	c.Assert(keys[0].ShortID(), gc.Equals, "ce353cf4")
	c.Assert(keys[0].UserIDs, gc.HasLen, 1)
	c.Assert(keys[0].UserIDs[0].Keywords, gc.Equals, "Jenny Ondioline <jennyo@transient.net>")
}

func (s *MgoSuite) TestAddDuplicates(c *gc.C) {
	res, err := http.Get(s.srv.URL + "/pks/lookup?op=hget&search=da84f40d830a7be2a3c0b7f2e146bfaa")
	c.Assert(err, gc.IsNil)
	res.Body.Close()
	c.Assert(err, gc.IsNil)
	c.Assert(res.StatusCode, gc.Equals, http.StatusNotFound)

	for i := 0; i < 10; i++ {
		s.addKey(c, "sksdigest.asc")
	}

	session, coll := s.storage.c()
	defer session.Close()
	n, err := coll.Find(bson.D{{"md5", "da84f40d830a7be2a3c0b7f2e146bfaa"}}).Count()
	c.Assert(err, gc.IsNil)
	c.Assert(n, gc.Equals, 1)
}

func (s *MgoSuite) TestResolve(c *gc.C) {
	res, err := http.Get(s.srv.URL + "/pks/lookup?op=get&search=0x44a2d1db")
	c.Assert(err, gc.IsNil)
	res.Body.Close()
	c.Assert(err, gc.IsNil)
	c.Assert(res.StatusCode, gc.Equals, http.StatusNotFound)

	s.addKey(c, "uat.asc")
	var doc keyDoc
	session, coll := s.storage.c()
	defer session.Close()
	err = coll.Find(nil).One(&doc)
	c.Assert(err, gc.IsNil)

	// Should match
	for _, search := range []string{
		// short, long and full fingerprint key IDs match
		"0x44a2d1db", "0xf79362da44a2d1db", "0x81279eee7ec89fb781702adaf79362da44a2d1db",
		// contiguous words and email addresses match
		"casey", "marshall", "casey+marshall", "cAseY+MArSHaLL",
		"casey.marshall@gmail.com", "casey.marshall@gazzang.com"} {
		res, err = http.Get(s.srv.URL + "/pks/lookup?op=get&search=" + search)
		c.Assert(err, gc.IsNil)
		armor, err := ioutil.ReadAll(res.Body)
		res.Body.Close()
		c.Assert(err, gc.IsNil)
		c.Assert(res.StatusCode, gc.Equals, http.StatusOK)

		keys := openpgp.MustReadArmorKeys(bytes.NewBuffer(armor)).MustParse()
		c.Assert(keys, gc.HasLen, 1)
		c.Assert(keys[0].ShortID(), gc.Equals, "44a2d1db")
		c.Assert(keys[0].UserIDs, gc.HasLen, 2)
		c.Assert(keys[0].UserAttributes, gc.HasLen, 1)
		c.Assert(keys[0].UserIDs[0].Keywords, gc.Equals, "Casey Marshall <casey.marshall@gazzang.com>")
	}

	// Shouldn't match any of these
	for _, search := range []string{
		"0xdeadbeef", "0xce353cf4", "0xadaf79362da44a2d1db",
		"alice@example.com", "bob@example.com", "marshal", "com"} {
		res, err = http.Get(s.srv.URL + "/pks/lookup?op=get&search=" + search)
		c.Assert(err, gc.IsNil)
		res.Body.Close()
		c.Assert(res.StatusCode, gc.Equals, http.StatusNotFound)
	}
}
<commit_msg>More searches that shouldn't match.<commit_after>/*
   Hockeypuck - OpenPGP key server
   Copyright (C) 2012-2014  Casey Marshall

   This program is free software: you can redistribute it and/or modify
   it under the terms of the GNU Affero General Public License as published by
   the Free Software Foundation, version 3.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU Affero General Public License for more details.

   You should have received a copy of the GNU Affero General Public License
   along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

package mgohkp

import (
	"bytes"
	"io/ioutil"
	"net/http"
	"net/http/httptest"
	"net/url"
	stdtesting "testing"

	"github.com/facebookgo/mgotest"
	"github.com/hockeypuck/testing"
	"github.com/julienschmidt/httprouter"
	gc "gopkg.in/check.v1"
	"gopkg.in/mgo.v2"
	"gopkg.in/mgo.v2/bson"

	"gopkg.in/hockeypuck/hkp.v0"
	"gopkg.in/hockeypuck/openpgp.v0"
)

func Test(t *stdtesting.T) { gc.TestingT(t) }

type MgoSuite struct {
	storage *storage
	mgoSrv  *mgotest.Server
	session *mgo.Session
	srv     *httptest.Server
}

var _ = gc.Suite(&MgoSuite{})

func (s *MgoSuite) SetUpTest(c *gc.C) {
	s.mgoSrv = mgotest.NewStartedServer(c)
	s.session = s.mgoSrv.Session()
	st, err := NewStorage(s.session)
	c.Assert(err, gc.IsNil)
	s.storage = st.(*storage)

	r := httprouter.New()
	handler := hkp.NewHandler(s.storage)
	handler.Register(r)
	s.srv = httptest.NewServer(r)
}

func (s *MgoSuite) TearDownTest(c *gc.C) {
	s.srv.Close()
	s.session.Close()
	s.mgoSrv.Stop()
}

func (s *MgoSuite) addKey(c *gc.C, keyname string) {
	keytext, err := ioutil.ReadAll(testing.MustInput(keyname))
	c.Assert(err, gc.IsNil)
	res, err := http.PostForm(s.srv.URL+"/pks/add", url.Values{
		"keytext": []string{string(keytext)},
	})
	c.Assert(err, gc.IsNil)
	c.Assert(res.StatusCode, gc.Equals, http.StatusOK)
	defer res.Body.Close()
	_, err = ioutil.ReadAll(res.Body)
	c.Assert(err, gc.IsNil)
}

func (s *MgoSuite) TestMD5(c *gc.C) {
	res, err := http.Get(s.srv.URL + "/pks/lookup?op=hget&search=da84f40d830a7be2a3c0b7f2e146bfaa")
	c.Assert(err, gc.IsNil)
	res.Body.Close()
	c.Assert(err, gc.IsNil)
	c.Assert(res.StatusCode, gc.Equals, http.StatusNotFound)

	s.addKey(c, "sksdigest.asc")
	session, coll := s.storage.c()
	defer session.Close()
	var doc keyDoc
	err = coll.Find(bson.D{{"md5", "da84f40d830a7be2a3c0b7f2e146bfaa"}}).One(&doc)
	c.Assert(err, gc.IsNil)

	res, err = http.Get(s.srv.URL + "/pks/lookup?op=hget&search=da84f40d830a7be2a3c0b7f2e146bfaa")
	c.Assert(err, gc.IsNil)
	armor, err := ioutil.ReadAll(res.Body)
	res.Body.Close()
	c.Assert(err, gc.IsNil)
	c.Assert(res.StatusCode, gc.Equals, http.StatusOK)

	keys := openpgp.MustReadArmorKeys(bytes.NewBuffer(armor)).MustParse()
	c.Assert(keys, gc.HasLen, 1)
	c.Assert(keys[0].ShortID(), gc.Equals, "ce353cf4")
	c.Assert(keys[0].UserIDs, gc.HasLen, 1)
	c.Assert(keys[0].UserIDs[0].Keywords, gc.Equals, "Jenny Ondioline <jennyo@transient.net>")
}

func (s *MgoSuite) TestAddDuplicates(c *gc.C) {
	res, err := http.Get(s.srv.URL + "/pks/lookup?op=hget&search=da84f40d830a7be2a3c0b7f2e146bfaa")
	c.Assert(err, gc.IsNil)
	res.Body.Close()
	c.Assert(err, gc.IsNil)
	c.Assert(res.StatusCode, gc.Equals, http.StatusNotFound)

	for i := 0; i < 10; i++ {
		s.addKey(c, "sksdigest.asc")
	}

	session, coll := s.storage.c()
	defer session.Close()
	n, err := coll.Find(bson.D{{"md5", "da84f40d830a7be2a3c0b7f2e146bfaa"}}).Count()
	c.Assert(err, gc.IsNil)
	c.Assert(n, gc.Equals, 1)
}

func (s *MgoSuite) TestResolve(c *gc.C) {
	res, err := http.Get(s.srv.URL + "/pks/lookup?op=get&search=0x44a2d1db")
	c.Assert(err, gc.IsNil)
	res.Body.Close()
	c.Assert(err, gc.IsNil)
	c.Assert(res.StatusCode, gc.Equals, http.StatusNotFound)

	s.addKey(c, "uat.asc")
	var doc keyDoc
	session, coll := s.storage.c()
	defer session.Close()
	err = coll.Find(nil).One(&doc)
	c.Assert(err, gc.IsNil)

	// Should match
	for _, search := range []string{
		// short, long and full fingerprint key IDs match
		"0x44a2d1db", "0xf79362da44a2d1db", "0x81279eee7ec89fb781702adaf79362da44a2d1db",
		// contiguous words and email addresses match
		"casey", "marshall", "casey+marshall", "cAseY+MArSHaLL",
		"casey.marshall@gmail.com", "casey.marshall@gazzang.com"} {
		res, err = http.Get(s.srv.URL + "/pks/lookup?op=get&search=" + search)
		c.Assert(err, gc.IsNil)
		armor, err := ioutil.ReadAll(res.Body)
		res.Body.Close()
		c.Assert(err, gc.IsNil)
		c.Assert(res.StatusCode, gc.Equals, http.StatusOK)

		keys := openpgp.MustReadArmorKeys(bytes.NewBuffer(armor)).MustParse()
		c.Assert(keys, gc.HasLen, 1)
		c.Assert(keys[0].ShortID(), gc.Equals, "44a2d1db")
		c.Assert(keys[0].UserIDs, gc.HasLen, 2)
		c.Assert(keys[0].UserAttributes, gc.HasLen, 1)
		c.Assert(keys[0].UserIDs[0].Keywords, gc.Equals, "Casey Marshall <casey.marshall@gazzang.com>")
	}

	// Shouldn't match any of these
	for _, search := range []string{
		"0xdeadbeef", "0xce353cf4", "0xd1db", "44a2d1db", "0xadaf79362da44a2d1db",
		"alice@example.com", "bob@example.com", "marshal", "com"} {
		res, err = http.Get(s.srv.URL + "/pks/lookup?op=get&search=" + search)
		c.Assert(err, gc.IsNil)
		res.Body.Close()
		c.Assert(res.StatusCode, gc.Equals, http.StatusNotFound)
	}
}
<|endoftext|>