<commit_before>package fusefrontend_reverse

import (
	"context"
	"fmt"
	"os"
	"path/filepath"
	"syscall"

	"golang.org/x/sys/unix"

	"github.com/hanwen/go-fuse/v2/fs"
	"github.com/hanwen/go-fuse/v2/fuse"

	"github.com/rfjakob/gocryptfs/internal/contentenc"
	"github.com/rfjakob/gocryptfs/internal/pathiv"
	"github.com/rfjakob/gocryptfs/internal/syscallcompat"
	"github.com/rfjakob/gocryptfs/internal/tlog"
)

// Node is a file or directory in the filesystem tree
// in a `gocryptfs -reverse` mount.
type Node struct {
	fs.Inode
}

// Lookup - FUSE call for discovering a file.
func (n *Node) Lookup(ctx context.Context, cName string, out *fuse.EntryOut) (ch *fs.Inode, errno syscall.Errno) {
	var d *dirfdPlus
	t := n.lookupFileType(cName)
	if t == typeDiriv {
		// gocryptfs.diriv
		return n.lookupDiriv(ctx, out)
	} else if t == typeName {
		// gocryptfs.longname.*.name
		return n.lookupLongnameName(ctx, cName, out)
	} else if t == typeConfig {
		// gocryptfs.conf
		return n.lookupConf(ctx, out)
	} else if t == typeReal {
		// real file
		d, errno = n.prepareAtSyscall(cName)
		//fmt.Printf("Lookup: prepareAtSyscall -> d=%#v, errno=%d\n", d, errno)
		if errno != 0 {
			return
		}
		defer syscall.Close(d.dirfd)
	}
	// Get device number and inode number into `st`
	st, err := syscallcompat.Fstatat2(d.dirfd, d.pName, unix.AT_SYMLINK_NOFOLLOW)
	if err != nil {
		return nil, fs.ToErrno(err)
	}
	// Create new inode and fill `out`
	ch = n.newChild(ctx, st, out)
	// Translate ciphertext size in `out.Attr.Size` to plaintext size
	if t == typeReal {
		n.translateSize(d.dirfd, cName, d.pName, &out.Attr)
	}
	return ch, 0
}

// GetAttr - FUSE call for stat()ing a file.
//
// GetAttr is symlink-safe through use of openBackingDir() and Fstatat().
func (n *Node) Getattr(ctx context.Context, f fs.FileHandle, out *fuse.AttrOut) (errno syscall.Errno) {
	// If the kernel gives us a file handle, use it.
	if f != nil {
		return f.(fs.FileGetattrer).Getattr(ctx, out)
	}

	d, errno := n.prepareAtSyscall("")
	if errno != 0 {
		return
	}
	defer syscall.Close(d.dirfd)

	st, err := syscallcompat.Fstatat2(d.dirfd, d.pName, unix.AT_SYMLINK_NOFOLLOW)
	if err != nil {
		return fs.ToErrno(err)
	}

	// Fix inode number
	rn := n.rootNode()
	rn.inoMap.TranslateStat(st)
	out.Attr.FromStat(st)

	// Translate ciphertext size in `out.Attr.Size` to plaintext size
	cName := filepath.Base(n.Path())
	n.translateSize(d.dirfd, cName, d.pName, &out.Attr)

	if rn.args.ForceOwner != nil {
		out.Owner = *rn.args.ForceOwner
	}
	return 0
}

// Readlink - FUSE call.
//
// Symlink-safe through openBackingDir() + Readlinkat().
func (n *Node) Readlink(ctx context.Context) (out []byte, errno syscall.Errno) {
	d, errno := n.prepareAtSyscall("")
	if errno != 0 {
		return
	}
	defer syscall.Close(d.dirfd)

	return n.readlink(d.dirfd, d.cName, d.pName)
}

// Open - FUSE call. Open already-existing file.
//
// Symlink-safe through Openat().
func (n *Node) Open(ctx context.Context, flags uint32) (fh fs.FileHandle, fuseFlags uint32, errno syscall.Errno) {
	d, errno := n.prepareAtSyscall("")
	if errno != 0 {
		return
	}
	defer syscall.Close(d.dirfd)

	fd, err := syscallcompat.Openat(d.dirfd, d.pName, syscall.O_RDONLY|syscall.O_NOFOLLOW, 0)
	if err != nil {
		errno = fs.ToErrno(err)
		return
	}

	// Reject access if the file descriptor does not refer to a regular file.
	var st syscall.Stat_t
	err = syscall.Fstat(fd, &st)
	if err != nil {
		tlog.Warn.Printf("Open: Fstat error: %v", err)
		syscall.Close(fd)
		errno = fs.ToErrno(err)
		return
	}
	var a fuse.Attr
	a.FromStat(&st)
	if !a.IsRegular() {
		tlog.Warn.Printf("ino%d: newFile: not a regular file", st.Ino)
		syscall.Close(fd)
		errno = syscall.EACCES
		return
	}
	// See if we have that inode number already in the table
	// (even if Nlink has dropped to 1)
	var derivedIVs pathiv.FileIVs
	v, found := inodeTable.Load(st.Ino)
	if found {
		tlog.Debug.Printf("ino%d: newFile: found in the inode table", st.Ino)
		derivedIVs = v.(pathiv.FileIVs)
	} else {
		p := n.Path()
		derivedIVs = pathiv.DeriveFile(p)
		// Nlink > 1 means there is more than one path to this file.
		// Store the derived values so we always return the same data,
		// regardless of the path that is used to access the file.
		// This means that the first path wins.
		if st.Nlink > 1 {
			v, found = inodeTable.LoadOrStore(st.Ino, derivedIVs)
			if found {
				// Another thread has stored a different value before we could.
				derivedIVs = v.(pathiv.FileIVs)
			} else {
				tlog.Debug.Printf("ino%d: newFile: Nlink=%d, stored in the inode table", st.Ino, st.Nlink)
			}
		}
	}
	header := contentenc.FileHeader{
		Version: contentenc.CurrentVersion,
		ID:      derivedIVs.ID,
	}
	fh = &File{
		fd:         os.NewFile(uintptr(fd), fmt.Sprintf("fd%d", fd)),
		header:     header,
		block0IV:   derivedIVs.Block0IV,
		contentEnc: n.rootNode().contentEnc,
	}
	return
}

// StatFs - FUSE call. Returns information about the filesystem.
//
// Symlink-safe because the path is ignored.
func (n *Node) Statfs(ctx context.Context, out *fuse.StatfsOut) syscall.Errno {
	p := n.rootNode().args.Cipherdir
	var st syscall.Statfs_t
	err := syscall.Statfs(p, &st)
	if err != nil {
		return fs.ToErrno(err)
	}
	out.FromStatfsT(&st)
	return 0
}
<commit_msg>fusefrontend_reverse: fix GETATTR panic<commit_after>package fusefrontend_reverse

import (
	"context"
	"fmt"
	"os"
	"path/filepath"
	"syscall"

	"golang.org/x/sys/unix"

	"github.com/hanwen/go-fuse/v2/fs"
	"github.com/hanwen/go-fuse/v2/fuse"

	"github.com/rfjakob/gocryptfs/internal/contentenc"
	"github.com/rfjakob/gocryptfs/internal/pathiv"
	"github.com/rfjakob/gocryptfs/internal/syscallcompat"
	"github.com/rfjakob/gocryptfs/internal/tlog"
)

// Node is a file or directory in the filesystem tree
// in a `gocryptfs -reverse` mount.
type Node struct {
	fs.Inode
}

// Lookup - FUSE call for discovering a file.
func (n *Node) Lookup(ctx context.Context, cName string, out *fuse.EntryOut) (ch *fs.Inode, errno syscall.Errno) {
	var d *dirfdPlus
	t := n.lookupFileType(cName)
	if t == typeDiriv {
		// gocryptfs.diriv
		return n.lookupDiriv(ctx, out)
	} else if t == typeName {
		// gocryptfs.longname.*.name
		return n.lookupLongnameName(ctx, cName, out)
	} else if t == typeConfig {
		// gocryptfs.conf
		return n.lookupConf(ctx, out)
	} else if t == typeReal {
		// real file
		d, errno = n.prepareAtSyscall(cName)
		//fmt.Printf("Lookup: prepareAtSyscall -> d=%#v, errno=%d\n", d, errno)
		if errno != 0 {
			return
		}
		defer syscall.Close(d.dirfd)
	}
	// Get device number and inode number into `st`
	st, err := syscallcompat.Fstatat2(d.dirfd, d.pName, unix.AT_SYMLINK_NOFOLLOW)
	if err != nil {
		return nil, fs.ToErrno(err)
	}
	// Create new inode and fill `out`
	ch = n.newChild(ctx, st, out)
	// Translate ciphertext size in `out.Attr.Size` to plaintext size
	if t == typeReal {
		n.translateSize(d.dirfd, cName, d.pName, &out.Attr)
	}
	return ch, 0
}

// GetAttr - FUSE call for stat()ing a file.
//
// GetAttr is symlink-safe through use of openBackingDir() and Fstatat().
func (n *Node) Getattr(ctx context.Context, f fs.FileHandle, out *fuse.AttrOut) (errno syscall.Errno) {
	d, errno := n.prepareAtSyscall("")
	if errno != 0 {
		return
	}
	defer syscall.Close(d.dirfd)

	st, err := syscallcompat.Fstatat2(d.dirfd, d.pName, unix.AT_SYMLINK_NOFOLLOW)
	if err != nil {
		return fs.ToErrno(err)
	}

	// Fix inode number
	rn := n.rootNode()
	rn.inoMap.TranslateStat(st)
	out.Attr.FromStat(st)

	// Translate ciphertext size in `out.Attr.Size` to plaintext size
	cName := filepath.Base(n.Path())
	n.translateSize(d.dirfd, cName, d.pName, &out.Attr)

	if rn.args.ForceOwner != nil {
		out.Owner = *rn.args.ForceOwner
	}
	return 0
}

// Readlink - FUSE call.
//
// Symlink-safe through openBackingDir() + Readlinkat().
func (n *Node) Readlink(ctx context.Context) (out []byte, errno syscall.Errno) {
	d, errno := n.prepareAtSyscall("")
	if errno != 0 {
		return
	}
	defer syscall.Close(d.dirfd)

	return n.readlink(d.dirfd, d.cName, d.pName)
}

// Open - FUSE call. Open already-existing file.
//
// Symlink-safe through Openat().
func (n *Node) Open(ctx context.Context, flags uint32) (fh fs.FileHandle, fuseFlags uint32, errno syscall.Errno) {
	d, errno := n.prepareAtSyscall("")
	if errno != 0 {
		return
	}
	defer syscall.Close(d.dirfd)

	fd, err := syscallcompat.Openat(d.dirfd, d.pName, syscall.O_RDONLY|syscall.O_NOFOLLOW, 0)
	if err != nil {
		errno = fs.ToErrno(err)
		return
	}

	// Reject access if the file descriptor does not refer to a regular file.
	var st syscall.Stat_t
	err = syscall.Fstat(fd, &st)
	if err != nil {
		tlog.Warn.Printf("Open: Fstat error: %v", err)
		syscall.Close(fd)
		errno = fs.ToErrno(err)
		return
	}
	var a fuse.Attr
	a.FromStat(&st)
	if !a.IsRegular() {
		tlog.Warn.Printf("ino%d: newFile: not a regular file", st.Ino)
		syscall.Close(fd)
		errno = syscall.EACCES
		return
	}
	// See if we have that inode number already in the table
	// (even if Nlink has dropped to 1)
	var derivedIVs pathiv.FileIVs
	v, found := inodeTable.Load(st.Ino)
	if found {
		tlog.Debug.Printf("ino%d: newFile: found in the inode table", st.Ino)
		derivedIVs = v.(pathiv.FileIVs)
	} else {
		p := n.Path()
		derivedIVs = pathiv.DeriveFile(p)
		// Nlink > 1 means there is more than one path to this file.
		// Store the derived values so we always return the same data,
		// regardless of the path that is used to access the file.
		// This means that the first path wins.
		if st.Nlink > 1 {
			v, found = inodeTable.LoadOrStore(st.Ino, derivedIVs)
			if found {
				// Another thread has stored a different value before we could.
				derivedIVs = v.(pathiv.FileIVs)
			} else {
				tlog.Debug.Printf("ino%d: newFile: Nlink=%d, stored in the inode table", st.Ino, st.Nlink)
			}
		}
	}
	header := contentenc.FileHeader{
		Version: contentenc.CurrentVersion,
		ID:      derivedIVs.ID,
	}
	fh = &File{
		fd:         os.NewFile(uintptr(fd), fmt.Sprintf("fd%d", fd)),
		header:     header,
		block0IV:   derivedIVs.Block0IV,
		contentEnc: n.rootNode().contentEnc,
	}
	return
}

// StatFs - FUSE call. Returns information about the filesystem.
//
// Symlink-safe because the path is ignored.
func (n *Node) Statfs(ctx context.Context, out *fuse.StatfsOut) syscall.Errno {
	p := n.rootNode().args.Cipherdir
	var st syscall.Statfs_t
	err := syscall.Statfs(p, &st)
	if err != nil {
		return fs.ToErrno(err)
	}
	out.FromStatfsT(&st)
	return 0
}
<|endoftext|>


<commit_before>package cache

import (
	"time"
)

//DummyCache DummyCache dont store any data.
//Usually used in develop environment or testing
type DummyCache struct {
	DriverUtil
}

//SetBytesValue Set bytes data to cache by given key.
//Return any error raised.
func (c *DummyCache) SetBytesValue(key string, bytes []byte, ttl time.Duration) error {
	return nil
}

//UpdateBytesValue Update bytes data to cache by given key only if the cache exist.
//Return any error raised.
func (c *DummyCache) UpdateBytesValue(key string, bytes []byte, ttl time.Duration) error {
	return nil
}

//GetBytesValue Get bytes data from cache by given key.
//Return data bytes and any error raised.
func (c *DummyCache) GetBytesValue(key string) ([]byte, error) {
	return nil, ErrNotFound
}

//MGetBytesValue get multiple bytes data from cache by given keys.
//Return data bytes map and any error if raised.
func (c *DummyCache) MGetBytesValue(keys ...string) (map[string][]byte, error) {
	return map[string][]byte{}, nil
}

//MSetBytesValue set multiple bytes data to cache with given key-value map.
//Return  any error if raised.
func (c *DummyCache) MSetBytesValue(data map[string][]byte, ttl time.Duration) error {
	return nil
}

//Del Delete data in cache by given key.
//Return any error raised.
func (c *DummyCache) Del(key string) error {
	return nil
}

//SetGCErrHandler Set callback to handler error raised when gc.
func (c *DummyCache) SetGCErrHandler(f func(err error)) {
	return
}

//Close Close cache.
//Return any error if raised
func (c *DummyCache) Close() error {
	return nil
}

//Flush Delete all data in cache.
//Return any error if raised
func (c *DummyCache) Flush() error {
	return nil
}

//IncrCounter Increase int val in cache by given key.Count cache and data cache are in two independent namespace.
//Return int data value and any error raised.
func (c *DummyCache) IncrCounter(key string, increment int64, ttl time.Duration) (int64, error) {
	return 0, nil
}

//SetCounter Set int val in cache by given key.Count cache and data cache are in two independent namespace.
//Return any error raised.
func (c *DummyCache) SetCounter(key string, v int64, ttl time.Duration) error {
	return nil
}

//GetCounter Get int val from cache by given key.Count cache and data cache are in two independent namespace.
//Return int data value and any error raised.
func (c *DummyCache) GetCounter(key string) (int64, error) {
	return 0, ErrNotFound
}

//DelCounter Delete int val in cache by given key.Count cache and data cache are in two independent namespace.
//Return any error raised.
func (c *DummyCache) DelCounter(key string) error {
	return nil
}

//Expire set cache value expire duration by given key and ttl
func (c *DummyCache) Expire(key string, ttl time.Duration) error {
	return nil
}

//ExpireCounter set cache counter  expire duration by given key and ttl
func (c *DummyCache) ExpireCounter(key string, ttl time.Duration) error {
	return nil
}

func init() {
	Register("dummycache", func(loader func(interface{}) error) (Driver, error) {
		return &DummyCache{}, nil
	})
}
<commit_msg>update<commit_after>package cache

import (
	"sync"
	"time"
)

//DummyCache DummyCache dont store any data.
//Usually used in develop environment or testing
type DummyCache struct {
	DriverUtil
}

//SetBytesValue Set bytes data to cache by given key.
//Return any error raised.
func (c *DummyCache) SetBytesValue(key string, bytes []byte, ttl time.Duration) error {
	return nil
}

//UpdateBytesValue Update bytes data to cache by given key only if the cache exist.
//Return any error raised.
func (c *DummyCache) UpdateBytesValue(key string, bytes []byte, ttl time.Duration) error {
	return nil
}

//GetBytesValue Get bytes data from cache by given key.
//Return data bytes and any error raised.
func (c *DummyCache) GetBytesValue(key string) ([]byte, error) {
	return nil, ErrNotFound
}

//MGetBytesValue get multiple bytes data from cache by given keys.
//Return data bytes map and any error if raised.
func (c *DummyCache) MGetBytesValue(keys ...string) (map[string][]byte, error) {
	return map[string][]byte{}, nil
}

//MSetBytesValue set multiple bytes data to cache with given key-value map.
//Return  any error if raised.
func (c *DummyCache) MSetBytesValue(data map[string][]byte, ttl time.Duration) error {
	return nil
}

//Del Delete data in cache by given key.
//Return any error raised.
func (c *DummyCache) Del(key string) error {
	return nil
}

//SetGCErrHandler Set callback to handler error raised when gc.
func (c *DummyCache) SetGCErrHandler(f func(err error)) {
	return
}

//Close Close cache.
//Return any error if raised
func (c *DummyCache) Close() error {
	return nil
}

//Flush Delete all data in cache.
//Return any error if raised
func (c *DummyCache) Flush() error {
	return nil
}

//IncrCounter Increase int val in cache by given key.Count cache and data cache are in two independent namespace.
//Return int data value and any error raised.
func (c *DummyCache) IncrCounter(key string, increment int64, ttl time.Duration) (int64, error) {
	return 0, nil
}

//SetCounter Set int val in cache by given key.Count cache and data cache are in two independent namespace.
//Return any error raised.
func (c *DummyCache) SetCounter(key string, v int64, ttl time.Duration) error {
	return nil
}

//GetCounter Get int val from cache by given key.Count cache and data cache are in two independent namespace.
//Return int data value and any error raised.
func (c *DummyCache) GetCounter(key string) (int64, error) {
	return 0, ErrNotFound
}

//DelCounter Delete int val in cache by given key.Count cache and data cache are in two independent namespace.
//Return any error raised.
func (c *DummyCache) DelCounter(key string) error {
	return nil
}

//Expire set cache value expire duration by given key and ttl
func (c *DummyCache) Expire(key string, ttl time.Duration) error {
	return nil
}

//ExpireCounter set cache counter  expire duration by given key and ttl
func (c *DummyCache) ExpireCounter(key string, ttl time.Duration) error {
	return nil
}

var dummy *Cache
var dummylock sync.Mutex

func Dummy() Cacheable {
	dummylock.Lock()
	defer dummylock.Unlock()
	if dummy == nil {
		dummy = New()
	}
	c := OptionConfig{
		Driver:    "dummycache",
		Marshaler: "json",
	}
	err := c.ApplyTo(dummy)
	if err != nil {
		panic(err)
	}
	return dummy
}

func init() {
	Register("dummycache", func(loader func(interface{}) error) (Driver, error) {
		return &DummyCache{}, nil
	})

}
<|endoftext|>


<commit_before>// Copyright 2012, 2013 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package testing

import (
	"fmt"
	"go/build"
	"os"
	"path/filepath"
	"sync"

	"github.com/juju/utils/fs"

	"github.com/juju/juju/charm"
)

func check(err error) {
	if err != nil {
		panic(err)
	}
}

// Repo represents a charm repository used for testing.
type Repo struct {
	once sync.Once
	path string
}

func (r *Repo) Path() string {
	r.once.Do(r.init)
	return r.path
}

// init is called once when r.Path() is called for the first time, and
// it initializes r.path to the location of the local testing
// repository.
func (r *Repo) init() {
	p, err := build.Import("github.com/juju/juju/testing", "", build.FindOnly)
	check(err)
	r.path = filepath.Join(p.Dir, "repo")
}

// Charms represents the specific charm repository stored in this package and
// used by the Juju unit tests. The series name is "quantal".
var Charms = &Repo{}

func clone(dst, src string) string {
	dst = filepath.Join(dst, filepath.Base(src))
	check(fs.Copy(src, dst))
	return dst
}

// DirPath returns the path to a charm directory with the given name in the
// default series
func (r *Repo) DirPath(name string) string {
	return filepath.Join(r.Path(), "quantal", name)
}

// Dir returns the actual charm.Dir named name.
func (r *Repo) Dir(name string) *charm.Dir {
	ch, err := charm.ReadDir(r.DirPath(name))
	check(err)
	return ch
}

// ClonedDirPath returns the path to a new copy of the default charm directory
// named name.
func (r *Repo) ClonedDirPath(dst, name string) string {
	return clone(dst, r.DirPath(name))
}

// RenamedClonedDirPath returns the path to a new copy of the default
// charm directory named name, but renames it to newName.
func (r *Repo) RenamedClonedDirPath(dst, name, newName string) string {
	newDst := clone(dst, r.DirPath(name))
	renamedDst := filepath.Join(filepath.Dir(newDst), newName)
	check(os.Rename(newDst, renamedDst))
	return renamedDst
}

// ClonedDir returns an actual charm.Dir based on a new copy of the charm directory
// named name, in the directory dst.
func (r *Repo) ClonedDir(dst, name string) *charm.Dir {
	ch, err := charm.ReadDir(r.ClonedDirPath(dst, name))
	check(err)
	return ch
}

// ClonedURL makes a copy of the charm directory. It will create a directory
// with the series name if it does not exist, and then clone the charm named
// name into that directory. The return value is a URL pointing at the local
// charm.
func (r *Repo) ClonedURL(dst, series, name string) *charm.URL {
	dst = filepath.Join(dst, series)
	if err := os.MkdirAll(dst, os.FileMode(0777)); err != nil {
		panic(fmt.Errorf("cannot make destination directory: %v", err))
	}
	clone(dst, r.DirPath(name))
	return &charm.URL{
		Reference: charm.Reference{
			Schema:   "local",
			Name:     name,
			Revision: -1,
		},
		Series: series,
	}
}

// BundlePath returns the path to a new charm bundle file created from the
// charm directory named name, in the directory dst.
func (r *Repo) BundlePath(dst, name string) string {
	dir := r.Dir(name)
	path := filepath.Join(dst, "bundle.charm")
	file, err := os.Create(path)
	check(err)
	defer file.Close()
	check(dir.BundleTo(file))
	return path
}

// Bundle returns an actual charm.Bundle created from a new charm bundle file
// created from the charm directory named name, in the directory dst.
func (r *Repo) Bundle(dst, name string) *charm.Bundle {
	ch, err := charm.ReadBundle(r.BundlePath(dst, name))
	check(err)
	return ch
}

// MockCharmStore implements charm.Repository and is used to isolate tests
// that would otherwise need to hit the real charm store.
type MockCharmStore struct {
	charms        map[string]map[int]*charm.Bundle
	AuthAttrs     string
	TestMode      bool
	DefaultSeries string
}

func NewMockCharmStore() *MockCharmStore {
	return &MockCharmStore{charms: map[string]map[int]*charm.Bundle{}}
}

func (s *MockCharmStore) WithAuthAttrs(auth string) charm.Repository {
	s.AuthAttrs = auth
	return s
}

func (s *MockCharmStore) WithTestMode(testMode bool) charm.Repository {
	s.TestMode = testMode
	return s
}

func (s *MockCharmStore) WithDefaultSeries(series string) charm.Repository {
	s.DefaultSeries = series
	return s
}

func (s *MockCharmStore) Resolve(ref charm.Reference) (*charm.URL, error) {
	if s.DefaultSeries == "" {
		return nil, fmt.Errorf("missing default series, cannot resolve charm url: %q", ref)
	}
	return &charm.URL{Reference: ref, Series: s.DefaultSeries}, nil
}

// SetCharm adds and removes charms in s. The affected charm is identified by
// charmURL, which must be revisioned. If bundle is nil, the charm will be
// removed; otherwise, it will be stored. It is an error to store a bundle
// under a charmURL that does not share its name and revision.
func (s *MockCharmStore) SetCharm(charmURL *charm.URL, bundle *charm.Bundle) error {
	base := charmURL.WithRevision(-1).String()
	if charmURL.Revision < 0 {
		return fmt.Errorf("bad charm url revision")
	}
	if bundle == nil {
		delete(s.charms[base], charmURL.Revision)
		return nil
	}
	bundleRev := bundle.Revision()
	bundleName := bundle.Meta().Name
	if bundleName != charmURL.Name || bundleRev != charmURL.Revision {
		return fmt.Errorf("charm url %s mismatch with bundle %s-%d", charmURL, bundleName, bundleRev)
	}
	if _, found := s.charms[base]; !found {
		s.charms[base] = map[int]*charm.Bundle{}
	}
	s.charms[base][charmURL.Revision] = bundle
	return nil
}

// interpret extracts from charmURL information relevant to both Latest and
// Get. The returned "base" is always the string representation of the
// unrevisioned part of charmURL; the "rev" wil be taken from the charmURL if
// available, and will otherwise be the revision of the latest charm in the
// store with the same "base".
func (s *MockCharmStore) interpret(charmURL *charm.URL) (base string, rev int) {
	base, rev = charmURL.WithRevision(-1).String(), charmURL.Revision
	if rev == -1 {
		for candidate := range s.charms[base] {
			if candidate > rev {
				rev = candidate
			}
		}
	}
	return
}

// Get implements charm.Repository.Get.
func (s *MockCharmStore) Get(charmURL *charm.URL) (charm.Charm, error) {
	base, rev := s.interpret(charmURL)
	charm, found := s.charms[base][rev]
	if !found {
		return nil, fmt.Errorf("charm not found in mock store: %s", charmURL)
	}
	return charm, nil
}

// Latest implements charm.Repository.Latest.
func (s *MockCharmStore) Latest(charmURLs ...*charm.URL) ([]charm.CharmRevision, error) {
	result := make([]charm.CharmRevision, len(charmURLs))
	for i, curl := range charmURLs {
		charmURL := curl.WithRevision(-1)
		base, rev := s.interpret(charmURL)
		if _, found := s.charms[base][rev]; !found {
			result[i].Err = fmt.Errorf("charm not found in mock store: %s", charmURL)
		} else {
			result[i].Revision = rev
		}
	}
	return result, nil
}
<commit_msg>charm/testing: do not rely on copy overwriting<commit_after>// Copyright 2012, 2013 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package testing

import (
	"fmt"
	"go/build"
	"os"
	"path/filepath"
	"sync"

	"github.com/juju/utils/fs"

	"github.com/juju/juju/charm"
)

func check(err error) {
	if err != nil {
		panic(err)
	}
}

// Repo represents a charm repository used for testing.
type Repo struct {
	once sync.Once
	path string
}

func (r *Repo) Path() string {
	r.once.Do(r.init)
	return r.path
}

// init is called once when r.Path() is called for the first time, and
// it initializes r.path to the location of the local testing
// repository.
func (r *Repo) init() {
	p, err := build.Import("github.com/juju/juju/testing", "", build.FindOnly)
	check(err)
	r.path = filepath.Join(p.Dir, "repo")
}

// Charms represents the specific charm repository stored in this package and
// used by the Juju unit tests. The series name is "quantal".
var Charms = &Repo{}

func clone(dst, src string) string {
	dst = filepath.Join(dst, filepath.Base(src))
	check(fs.Copy(src, dst))
	return dst
}

// DirPath returns the path to a charm directory with the given name in the
// default series
func (r *Repo) DirPath(name string) string {
	return filepath.Join(r.Path(), "quantal", name)
}

// Dir returns the actual charm.Dir named name.
func (r *Repo) Dir(name string) *charm.Dir {
	ch, err := charm.ReadDir(r.DirPath(name))
	check(err)
	return ch
}

// ClonedDirPath returns the path to a new copy of the default charm directory
// named name.
func (r *Repo) ClonedDirPath(dst, name string) string {
	return clone(dst, r.DirPath(name))
}

// RenamedClonedDirPath returns the path to a new copy of the default
// charm directory named name, renamed to newName.
func (r *Repo) RenamedClonedDirPath(dst, name, newName string) string {
	dstPath := filepath.Join(dst, newName)
	err := fs.Copy(r.DirPath(name), dstPath)
	check(err)
	return dstPath
}

// ClonedDir returns an actual charm.Dir based on a new copy of the charm directory
// named name, in the directory dst.
func (r *Repo) ClonedDir(dst, name string) *charm.Dir {
	ch, err := charm.ReadDir(r.ClonedDirPath(dst, name))
	check(err)
	return ch
}

// ClonedURL makes a copy of the charm directory. It will create a directory
// with the series name if it does not exist, and then clone the charm named
// name into that directory. The return value is a URL pointing at the local
// charm.
func (r *Repo) ClonedURL(dst, series, name string) *charm.URL {
	dst = filepath.Join(dst, series)
	if err := os.MkdirAll(dst, os.FileMode(0777)); err != nil {
		panic(fmt.Errorf("cannot make destination directory: %v", err))
	}
	clone(dst, r.DirPath(name))
	return &charm.URL{
		Reference: charm.Reference{
			Schema:   "local",
			Name:     name,
			Revision: -1,
		},
		Series: series,
	}
}

// BundlePath returns the path to a new charm bundle file created from the
// charm directory named name, in the directory dst.
func (r *Repo) BundlePath(dst, name string) string {
	dir := r.Dir(name)
	path := filepath.Join(dst, "bundle.charm")
	file, err := os.Create(path)
	check(err)
	defer file.Close()
	check(dir.BundleTo(file))
	return path
}

// Bundle returns an actual charm.Bundle created from a new charm bundle file
// created from the charm directory named name, in the directory dst.
func (r *Repo) Bundle(dst, name string) *charm.Bundle {
	ch, err := charm.ReadBundle(r.BundlePath(dst, name))
	check(err)
	return ch
}

// MockCharmStore implements charm.Repository and is used to isolate tests
// that would otherwise need to hit the real charm store.
type MockCharmStore struct {
	charms        map[string]map[int]*charm.Bundle
	AuthAttrs     string
	TestMode      bool
	DefaultSeries string
}

func NewMockCharmStore() *MockCharmStore {
	return &MockCharmStore{charms: map[string]map[int]*charm.Bundle{}}
}

func (s *MockCharmStore) WithAuthAttrs(auth string) charm.Repository {
	s.AuthAttrs = auth
	return s
}

func (s *MockCharmStore) WithTestMode(testMode bool) charm.Repository {
	s.TestMode = testMode
	return s
}

func (s *MockCharmStore) WithDefaultSeries(series string) charm.Repository {
	s.DefaultSeries = series
	return s
}

func (s *MockCharmStore) Resolve(ref charm.Reference) (*charm.URL, error) {
	if s.DefaultSeries == "" {
		return nil, fmt.Errorf("missing default series, cannot resolve charm url: %q", ref)
	}
	return &charm.URL{Reference: ref, Series: s.DefaultSeries}, nil
}

// SetCharm adds and removes charms in s. The affected charm is identified by
// charmURL, which must be revisioned. If bundle is nil, the charm will be
// removed; otherwise, it will be stored. It is an error to store a bundle
// under a charmURL that does not share its name and revision.
func (s *MockCharmStore) SetCharm(charmURL *charm.URL, bundle *charm.Bundle) error {
	base := charmURL.WithRevision(-1).String()
	if charmURL.Revision < 0 {
		return fmt.Errorf("bad charm url revision")
	}
	if bundle == nil {
		delete(s.charms[base], charmURL.Revision)
		return nil
	}
	bundleRev := bundle.Revision()
	bundleName := bundle.Meta().Name
	if bundleName != charmURL.Name || bundleRev != charmURL.Revision {
		return fmt.Errorf("charm url %s mismatch with bundle %s-%d", charmURL, bundleName, bundleRev)
	}
	if _, found := s.charms[base]; !found {
		s.charms[base] = map[int]*charm.Bundle{}
	}
	s.charms[base][charmURL.Revision] = bundle
	return nil
}

// interpret extracts from charmURL information relevant to both Latest and
// Get. The returned "base" is always the string representation of the
// unrevisioned part of charmURL; the "rev" wil be taken from the charmURL if
// available, and will otherwise be the revision of the latest charm in the
// store with the same "base".
func (s *MockCharmStore) interpret(charmURL *charm.URL) (base string, rev int) {
	base, rev = charmURL.WithRevision(-1).String(), charmURL.Revision
	if rev == -1 {
		for candidate := range s.charms[base] {
			if candidate > rev {
				rev = candidate
			}
		}
	}
	return
}

// Get implements charm.Repository.Get.
func (s *MockCharmStore) Get(charmURL *charm.URL) (charm.Charm, error) {
	base, rev := s.interpret(charmURL)
	charm, found := s.charms[base][rev]
	if !found {
		return nil, fmt.Errorf("charm not found in mock store: %s", charmURL)
	}
	return charm, nil
}

// Latest implements charm.Repository.Latest.
func (s *MockCharmStore) Latest(charmURLs ...*charm.URL) ([]charm.CharmRevision, error) {
	result := make([]charm.CharmRevision, len(charmURLs))
	for i, curl := range charmURLs {
		charmURL := curl.WithRevision(-1)
		base, rev := s.interpret(charmURL)
		if _, found := s.charms[base][rev]; !found {
			result[i].Err = fmt.Errorf("charm not found in mock store: %s", charmURL)
		} else {
			result[i].Revision = rev
		}
	}
	return result, nil
}
<|endoftext|>


<commit_before>package main

import (
	"crypto/tls"
	"fmt"
	"net"
	"os"
	"regexp"
	"strings"
	"time"

	"github.com/jessevdk/go-flags"
	"github.com/mackerelio/checkers"
)

type tcpOpts struct {
	exchange
	Service  string  `long:"service"`
	Hostname string  `short:"H" long:"hostname" description:"Host name or IP Address"`
	Timeout  float64 `short:"t" long:"timeout" default:"10" description:"Seconds before connection times out"`
	MaxBytes int     `short:"m" long:"maxbytes"`
	Delay    float64 `short:"d" long:"delay" description:"Seconds to wait between sending string and polling for response"`
	Warning  float64 `short:"w" long:"warning" description:"Response time to result in warning status (seconds)"`
	Critical float64 `short:"c" long:"critical" description:"Response time to result in critical status (seconds)"`
	Escape   bool    `short:"E" long:"escape" description:"Can use \\n, \\r, \\t or \\ in send or quit string. Must come before send or quit option. By default, nothing added to send, \\r\\n added to end of quit"`
}

type exchange struct {
	Send          string `short:"s" long:"send" description:"String to send to the server"`
	ExpectPattern string `short:"e" long:"expect-pattern" description:"Regexp pattern to expect in server response"`
	Quit          string `short:"q" long:"quit" description:"String to send server to initiate a clean close of the connection"`
	Port          int    `short:"p" long:"port" description:"Port number"`
	SSL           bool   `short:"S" long:"ssl" description:"Use SSL for the connection."`
	expectReg     *regexp.Regexp
}

func main() {
	opts, err := parseArgs(os.Args[1:])
	if err != nil {
		os.Exit(1)
	}
	ckr := opts.run()
	ckr.Name = "TCP"
	if opts.Service != "" {
		ckr.Name = opts.Service
	}
	ckr.Exit()
}

func parseArgs(args []string) (*tcpOpts, error) {
	opts := &tcpOpts{}
	_, err := flags.ParseArgs(opts, args)
	return opts, err
}

var defaultExchangeMap = map[string]exchange{
	"FTP": exchange{
		Port:          21,
		ExpectPattern: `^220`,
		Quit:          "QUIT",
	},
	"POP": exchange{
		Port:          110,
		ExpectPattern: `^\+OK`,
		Quit:          "QUIT",
	},
	"SPOP": exchange{
		Port:          995,
		ExpectPattern: `^\+OK`,
		Quit:          "QUIT",
		SSL:           true,
	},
	"IMAP": exchange{
		Port:          143,
		ExpectPattern: `^\* OK`,
		Quit:          "a1 LOGOUT",
	},
	"SIMAP": exchange{
		Port:          993,
		ExpectPattern: `^\* OK`,
		Quit:          "a1 LOGOUT",
		SSL:           true,
	},
	"SMTP": exchange{
		Port:          25,
		ExpectPattern: `^220`,
		Quit:          "QUIT",
	},
	"SSMTP": exchange{
		Port:          465,
		ExpectPattern: `^220`,
		Quit:          "QUIT",
		SSL:           true,
	},
}

func (opts *tcpOpts) prepare() error {
	opts.Service = strings.ToUpper(opts.Service)

	if opts.Service != "" {
		defaultEx, ok := defaultExchangeMap[opts.Service]
		if !ok {
			return fmt.Errorf("check-tcp called with unknown service: %s", opts.Service)
		}
		opts.merge(defaultEx)
	}

	if opts.Escape {
		opts.Quit = escapedString(opts.Quit)
		opts.Send = escapedString(opts.Send)
	} else if opts.Quit != "" {
		opts.Quit += "\r\n"
	}
	var err error
	if opts.ExpectPattern != "" {
		opts.expectReg, err = regexp.Compile(opts.ExpectPattern)
	}
	return err
}

func (opts *tcpOpts) merge(ex exchange) {
	if opts.Port == 0 {
		opts.Port = ex.Port
	}
	if opts.Send == "" {
		opts.Send = ex.Send
	}
	if opts.ExpectPattern == "" {
		opts.ExpectPattern = ex.ExpectPattern
	}
	if opts.Quit == "" {
		opts.Quit = ex.Quit
	}
	if !opts.SSL {
		opts.SSL = ex.SSL
	}
}

func dial(address string, ssl bool) (net.Conn, error) {
	if ssl {
		return tls.Dial("tcp", address, &tls.Config{})
	}
	return net.Dial("tcp", address)
}

func (opts *tcpOpts) run() *checkers.Checker {
	err := opts.prepare()
	if err != nil {
		return checkers.Unknown(err.Error())
	}
	// prevent changing output of some commands
	os.Setenv("LANG", "C")
	os.Setenv("LC_ALL", "C")

	address := fmt.Sprintf("%s:%d", opts.Hostname, opts.Port)
	start := time.Now()
	if opts.Delay > 0 {
		time.Sleep(time.Duration(opts.Delay) * time.Second)
	}
	conn, err := dial(address, opts.SSL)
	if err != nil {
		return checkers.Critical(err.Error())
	}
	defer conn.Close()

	if opts.Send != "" {
		err := write(conn, []byte(opts.Send), opts.Timeout)
		if err != nil {
			return checkers.Critical(err.Error())
		}
	}

	res := ""
	if opts.expectReg != nil {
		buf, err := slurp(conn, opts.MaxBytes, opts.Timeout)
		if err != nil {
			return checkers.Critical(err.Error())
		}
		res = string(buf)
		if !opts.expectReg.MatchString(res) {
			return checkers.Critical("Unexpected response from host/socket: " + res)
		}
	}

	if opts.Quit != "" {
		err := write(conn, []byte(opts.Quit), opts.Timeout)
		if err != nil {
			return checkers.Critical(err.Error())
		}
	}
	elapsed := time.Now().Sub(start)

	chkSt := checkers.OK
	if opts.Warning > 0 && elapsed > time.Duration(opts.Warning)*time.Second {
		chkSt = checkers.WARNING
	}
	if opts.Critical > 0 && elapsed > time.Duration(opts.Critical)*time.Second {
		chkSt = checkers.CRITICAL
	}
	msg := fmt.Sprintf("%.3f seconds response time on", float64(elapsed)/float64(time.Second))
	if opts.Hostname != "" {
		msg += " " + opts.Hostname
	}
	if opts.Port > 0 {
		msg += fmt.Sprintf(" port %d", opts.Port)
	}
	if res != "" {
		msg += fmt.Sprintf(" [%s]", strings.Trim(res, "\r\n"))
	}
	return checkers.NewChecker(chkSt, msg)
}

func write(conn net.Conn, content []byte, timeout float64) error {
	if timeout > 0 {
		conn.SetWriteDeadline(time.Now().Add(time.Duration(timeout) * time.Second))
	}
	_, err := conn.Write(content)
	return err
}

func slurp(conn net.Conn, maxbytes int, timeout float64) ([]byte, error) {
	buf := []byte{}
	readLimit := 32 * 1024
	if maxbytes > 0 {
		readLimit = maxbytes
	}
	readBytes := 0
	if timeout > 0 {
		conn.SetReadDeadline(time.Now().Add(time.Duration(timeout) * time.Second))
	}
	for {
		tmpBuf := make([]byte, readLimit)
		i, err := conn.Read(tmpBuf)
		if err != nil {
			return buf, err
		}
		buf = append(buf, tmpBuf[:i]...)
		readBytes += i
		if i < readLimit || (maxbytes > 0 && maxbytes <= readBytes) {
			break
		}
	}
	return buf, nil
}

func escapedString(str string) (escaped string) {
	l := len(str)
	for i := 0; i < l; i++ {
		c := str[i]
		if c == '\\' && i+1 < l {
			i++
			c := str[i]
			switch c {
			case 'n':
				escaped += "\n"
			case 'r':
				escaped += "\r"
			case 't':
				escaped += "\t"
			case '\\':
				escaped += `\`
			default:
				escaped += `\` + string(c)
			}
		} else {
			escaped += string(c)
		}
	}
	return escaped
}
<commit_msg>documentation<commit_after>package main

import (
	"crypto/tls"
	"fmt"
	"net"
	"os"
	"regexp"
	"strings"
	"time"

	"github.com/jessevdk/go-flags"
	"github.com/mackerelio/checkers"
)

type tcpOpts struct {
	Service  string  `long:"service" description:"Service name. e.g. ftp, smtp, pop, imap and so on"`
	Hostname string  `short:"H" long:"hostname" description:"Host name or IP Address"`
	exchange
	Timeout  float64 `short:"t" long:"timeout" default:"10" description:"Seconds before connection times out"`
	MaxBytes int     `short:"m" long:"maxbytes"`
	Delay    float64 `short:"d" long:"delay" description:"Seconds to wait between sending string and polling for response"`
	Warning  float64 `short:"w" long:"warning" description:"Response time to result in warning status (seconds)"`
	Critical float64 `short:"c" long:"critical" description:"Response time to result in critical status (seconds)"`
	Escape   bool    `short:"E" long:"escape" description:"Can use \\n, \\r, \\t or \\ in send or quit string. Must come before send or quit option. By default, nothing added to send, \\r\\n added to end of quit"`
}

type exchange struct {
	Port          int    `short:"p" long:"port" description:"Port number"`
	Send          string `short:"s" long:"send" description:"String to send to the server"`
	ExpectPattern string `short:"e" long:"expect-pattern" description:"Regexp pattern to expect in server response"`
	Quit          string `short:"q" long:"quit" description:"String to send server to initiate a clean close of the connection"`
	SSL           bool   `short:"S" long:"ssl" description:"Use SSL for the connection."`
	expectReg     *regexp.Regexp
}

func main() {
	opts, err := parseArgs(os.Args[1:])
	if err != nil {
		os.Exit(1)
	}
	ckr := opts.run()
	ckr.Name = "TCP"
	if opts.Service != "" {
		ckr.Name = opts.Service
	}
	ckr.Exit()
}

func parseArgs(args []string) (*tcpOpts, error) {
	opts := &tcpOpts{}
	_, err := flags.ParseArgs(opts, args)
	return opts, err
}

var defaultExchangeMap = map[string]exchange{
	"FTP": exchange{
		Port:          21,
		ExpectPattern: `^220`,
		Quit:          "QUIT",
	},
	"POP": exchange{
		Port:          110,
		ExpectPattern: `^\+OK`,
		Quit:          "QUIT",
	},
	"SPOP": exchange{
		Port:          995,
		ExpectPattern: `^\+OK`,
		Quit:          "QUIT",
		SSL:           true,
	},
	"IMAP": exchange{
		Port:          143,
		ExpectPattern: `^\* OK`,
		Quit:          "a1 LOGOUT",
	},
	"SIMAP": exchange{
		Port:          993,
		ExpectPattern: `^\* OK`,
		Quit:          "a1 LOGOUT",
		SSL:           true,
	},
	"SMTP": exchange{
		Port:          25,
		ExpectPattern: `^220`,
		Quit:          "QUIT",
	},
	"SSMTP": exchange{
		Port:          465,
		ExpectPattern: `^220`,
		Quit:          "QUIT",
		SSL:           true,
	},
}

func (opts *tcpOpts) prepare() error {
	opts.Service = strings.ToUpper(opts.Service)

	if opts.Service != "" {
		defaultEx, ok := defaultExchangeMap[opts.Service]
		if !ok {
			return fmt.Errorf("check-tcp called with unknown service: %s", opts.Service)
		}
		opts.merge(defaultEx)
	}

	if opts.Escape {
		opts.Quit = escapedString(opts.Quit)
		opts.Send = escapedString(opts.Send)
	} else if opts.Quit != "" {
		opts.Quit += "\r\n"
	}
	var err error
	if opts.ExpectPattern != "" {
		opts.expectReg, err = regexp.Compile(opts.ExpectPattern)
	}
	return err
}

func (opts *tcpOpts) merge(ex exchange) {
	if opts.Port == 0 {
		opts.Port = ex.Port
	}
	if opts.Send == "" {
		opts.Send = ex.Send
	}
	if opts.ExpectPattern == "" {
		opts.ExpectPattern = ex.ExpectPattern
	}
	if opts.Quit == "" {
		opts.Quit = ex.Quit
	}
	if !opts.SSL {
		opts.SSL = ex.SSL
	}
}

func dial(address string, ssl bool) (net.Conn, error) {
	if ssl {
		return tls.Dial("tcp", address, &tls.Config{})
	}
	return net.Dial("tcp", address)
}

func (opts *tcpOpts) run() *checkers.Checker {
	err := opts.prepare()
	if err != nil {
		return checkers.Unknown(err.Error())
	}
	// prevent changing output of some commands
	os.Setenv("LANG", "C")
	os.Setenv("LC_ALL", "C")

	address := fmt.Sprintf("%s:%d", opts.Hostname, opts.Port)
	start := time.Now()
	if opts.Delay > 0 {
		time.Sleep(time.Duration(opts.Delay) * time.Second)
	}
	conn, err := dial(address, opts.SSL)
	if err != nil {
		return checkers.Critical(err.Error())
	}
	defer conn.Close()

	if opts.Send != "" {
		err := write(conn, []byte(opts.Send), opts.Timeout)
		if err != nil {
			return checkers.Critical(err.Error())
		}
	}

	res := ""
	if opts.expectReg != nil {
		buf, err := slurp(conn, opts.MaxBytes, opts.Timeout)
		if err != nil {
			return checkers.Critical(err.Error())
		}
		res = string(buf)
		if !opts.expectReg.MatchString(res) {
			return checkers.Critical("Unexpected response from host/socket: " + res)
		}
	}

	if opts.Quit != "" {
		err := write(conn, []byte(opts.Quit), opts.Timeout)
		if err != nil {
			return checkers.Critical(err.Error())
		}
	}
	elapsed := time.Now().Sub(start)

	chkSt := checkers.OK
	if opts.Warning > 0 && elapsed > time.Duration(opts.Warning)*time.Second {
		chkSt = checkers.WARNING
	}
	if opts.Critical > 0 && elapsed > time.Duration(opts.Critical)*time.Second {
		chkSt = checkers.CRITICAL
	}
	msg := fmt.Sprintf("%.3f seconds response time on", float64(elapsed)/float64(time.Second))
	if opts.Hostname != "" {
		msg += " " + opts.Hostname
	}
	if opts.Port > 0 {
		msg += fmt.Sprintf(" port %d", opts.Port)
	}
	if res != "" {
		msg += fmt.Sprintf(" [%s]", strings.Trim(res, "\r\n"))
	}
	return checkers.NewChecker(chkSt, msg)
}

func write(conn net.Conn, content []byte, timeout float64) error {
	if timeout > 0 {
		conn.SetWriteDeadline(time.Now().Add(time.Duration(timeout) * time.Second))
	}
	_, err := conn.Write(content)
	return err
}

func slurp(conn net.Conn, maxbytes int, timeout float64) ([]byte, error) {
	buf := []byte{}
	readLimit := 32 * 1024
	if maxbytes > 0 {
		readLimit = maxbytes
	}
	readBytes := 0
	if timeout > 0 {
		conn.SetReadDeadline(time.Now().Add(time.Duration(timeout) * time.Second))
	}
	for {
		tmpBuf := make([]byte, readLimit)
		i, err := conn.Read(tmpBuf)
		if err != nil {
			return buf, err
		}
		buf = append(buf, tmpBuf[:i]...)
		readBytes += i
		if i < readLimit || (maxbytes > 0 && maxbytes <= readBytes) {
			break
		}
	}
	return buf, nil
}

func escapedString(str string) (escaped string) {
	l := len(str)
	for i := 0; i < l; i++ {
		c := str[i]
		if c == '\\' && i+1 < l {
			i++
			c := str[i]
			switch c {
			case 'n':
				escaped += "\n"
			case 'r':
				escaped += "\r"
			case 't':
				escaped += "\t"
			case '\\':
				escaped += `\`
			default:
				escaped += `\` + string(c)
			}
		} else {
			escaped += string(c)
		}
	}
	return escaped
}
<|endoftext|>


<commit_before>package client

import (
	"bytes"
	"errors"
	"github.com/oleiade/lane"
	. "github.com/smartystreets/goconvey/convey"
	"github.com/stratospark/torro/structure"
	"io"
	"log"
	"net"
	"testing"
	"time"
)

type MockConnection struct {
	ReadQueue         *lane.Queue
	SendHandshakeChan chan *structure.Handshake
	SendMessageChan   chan structure.Message
	RestOfMessageChan chan bool
	ReceiveBytesChan  chan []byte
}

func (c *MockConnection) SendHandshake(hs *structure.Handshake) {
	c.SendHandshakeChan <- hs
}

func (c *MockConnection) SendMessage(m structure.Message) {
	c.SendMessageChan <- m
}

func (c *MockConnection) Read(b []byte) (n int, err error) {
	log.Println("Trying to read, len(b): ", len(b))
	readBytes := func() {
		log.Printf("ReadBytes, len(queue): %d", c.ReadQueue.Size())
		h := c.ReadQueue.Dequeue()
		read, _ := h.([]byte)
		log.Printf("MockConnection Read: %q, len(read): %d, len(b): %d\n", read, len(read), len(b))
		for i := 0; i < len(b); i++ {
			b[i] = read[i]
		}
		if len(b) < len(read) {
			c.ReadQueue.Prepend(read[len(b):])
			c.RestOfMessageChan <- true
		}
	}
	select {
	case <-c.RestOfMessageChan:
		readBytes()
	case hs := <-c.SendHandshakeChan:
		log.Println("SendHandshakeChan")
		read := hs.Bytes()
		c.ReadQueue.Enqueue(read)
		readBytes()
	case m := <-c.SendMessageChan:
		log.Println("SendMessageChan")
		read := m.Bytes()
		c.ReadQueue.Enqueue(read)
		readBytes()
	}

	return len(b), err
}

func (c *MockConnection) Write(b []byte) (n int, err error) {
	log.Printf("MockConnection Write: %q\n", b)
	c.ReceiveBytesChan <- b
	return len(b), nil
}

func (c *MockConnection) Close() error {
	return nil
}

type MockConnectionFetcher struct {
	Conns map[string]*BTConn
}

func NewMockConnectionFetcher() *MockConnectionFetcher {
	return &MockConnectionFetcher{
		Conns: make(map[string]*BTConn),
	}
}

func (t *MockConnectionFetcher) Dial(addr string) (*BTConn, error) {
	conn := &MockConnection{
		ReadQueue:         lane.NewQueue(),
		SendHandshakeChan: make(chan *structure.Handshake, 1),
		SendMessageChan:   make(chan structure.Message, 1),
		RestOfMessageChan: make(chan bool, 1),
		ReceiveBytesChan:  make(chan []byte, 1),
	}
	btc := &BTConn{Conn: conn}
	t.Conns[addr] = btc

	return btc, nil
}

var (
	port         int    = 55555
	peerIdRemote string = "-TR2840-nj5ovtREMOTE"
	peerIdClient string = "-TR2840-nj5ovtCLIENT"
	hash         []byte = []byte("\x6f\xda\xb6\xc1\x9f\x72\x14\x76\xfa\xca\xab\x36\x60\x8a\x87\x7a\x2a\xac\xbf\xc9")
)

func TestListen(t *testing.T) {
	Convey("Listens to incoming connections on a given port", t, func() {
		s := NewBTService(port, []byte(peerIdRemote))
		s.StartListening()

		time.Sleep(time.Millisecond)
		So(s.Listener, ShouldNotBeNil)
		So(s.Listening, ShouldBeTrue)

		_ = s.StopListening()
		So(s.Listening, ShouldBeFalse)
	})

}

func TestAcceptHandshake(t *testing.T) {
	Convey("Accepts a handshake and adds to the connection list", t, func() {
		s := NewBTService(port, []byte(peerIdRemote))
		s.AddHash(hash)
		s.StartListening()

		time.Sleep(time.Millisecond)
		So(s.Listener, ShouldNotBeNil)
		So(s.Listening, ShouldBeTrue)

		addr, _ := net.ResolveTCPAddr("tcp", "localhost:55555")
		conn, err := net.DialTCP("tcp", nil, addr)
		So(err, ShouldBeNil)

		handshake := "\x13\x42\x69\x74\x54\x6f\x72\x72\x65\x6e\x74\x20\x70\x72\x6f\x74\x6f\x63\x6f\x6c\x00\x00\x00\x00\x00\x10\x00\x05\x6f\xda\xb6\xc1\x9f\x72\x14\x76\xfa\xca\xab\x36\x60\x8a\x87\x7a\x2a\xac\xbf\xc9\x2d\x55\x54\x33\x34\x34\x30\x2d\xcf\x9f\x51\x2b\xce\x01\x31\xf9\x38\x6f\xb6\x98"
		conn.Write([]byte(handshake))
		time.Sleep(time.Millisecond)
		respHandshake, err := structure.ReadHandshake(conn)
		So(err, ShouldBeNil)
		So(respHandshake, ShouldNotBeNil)
		So(len(s.Peers), ShouldEqual, 1)

		_ = s.StopListening()
		So(s.Listening, ShouldBeFalse)
	})
}

func TestRejectHandshake(t *testing.T) {
	Convey("Rejects a malformed handshake request", t, func() {
		s := NewBTService(port, []byte(peerIdRemote))
		s.StartListening()

		time.Sleep(time.Millisecond * 50)
		So(s.Listener, ShouldNotBeNil)
		So(s.Listening, ShouldBeTrue)

		addr, _ := net.ResolveTCPAddr("tcp", "localhost:55555")
		conn, err := net.DialTCP("tcp", nil, addr)
		So(err, ShouldBeNil)

		handshake := "\x13\x43\x69\x74\x54\x6f\x72\x72\x65\x6e\x74\x20\x70\x72\x6f\x74\x6f\x63\x6f\x6c\x00\x00\x00\x00\x00\x10\x00\x05\x6f\xda\xb6\xc1\x9f\x72\x14\x76\xfa\xca\xab\x36\x60\x8a\x87\x7a\x2a\xac\xbf\xc9\x2d\x55\x54\x33\x34\x34\x30\x2d\xcf\x9f\x51\x2b\xce\x01\x31\xf9\x38\x6f\xb6\x98"
		conn.Write([]byte(handshake))
		time.Sleep(time.Millisecond * 50)
		So(len(s.Peers), ShouldEqual, 0)
		buf := make([]byte, 4)
		_, err = io.ReadFull(conn, buf)
		So(err, ShouldNotBeNil)

		_ = s.StopListening()
		So(s.Listening, ShouldBeFalse)
	})
}

func TestInitiateHandshakes(t *testing.T) {
	Convey("Sends out handshake request to every IP in list", t, func() {
		s := NewBTService(port, []byte(peerIdRemote))
		mc := NewMockConnectionFetcher()
		s.ConnectionFetcher = mc
		s.AddHash(hash)
		s.StartListening()

		time.Sleep(time.Millisecond * 50)

		// TODO: check that peer data is saved within service data structure
		peers := make([]structure.Peer, 2)
		peers[0] = structure.Peer{IP: net.IPv4(192, 168, 1, 1), Port: 55556}
		peers[1] = structure.Peer{IP: net.IPv4(192, 168, 1, 2), Port: 55557}
		s.InitiateHandshakes(hash, peers)

		for _, p := range peers {
			c0 := mc.Conns[p.AddrString()].Conn.(*MockConnection)
			hs, err := structure.ReadHandshake(bytes.NewReader(<-c0.ReceiveBytesChan))
			So(hs, ShouldNotBeNil)
			So(err, ShouldBeNil)
			hs, _ = structure.NewHandshake(hash, []byte(peerIdClient))
			c0.SendHandshake(hs)
		}

		time.Sleep(time.Millisecond * 50)
		So(len(s.Peers), ShouldEqual, len(peers))

		_ = s.StopListening()
		So(s.Listening, ShouldBeFalse)
	})
}

func ReadMessageOrTimeout(c *MockConnection) (*structure.BasicMessage, error) {
	select {
	case b := <-c.ReceiveBytesChan:
		m, err := structure.ReadMessage(bytes.NewReader(b))
		bm, _ := m.(*structure.BasicMessage)
		So(bm, ShouldNotBeNil)
		So(err, ShouldBeNil)
		return bm, err
	case <-time.After(time.Millisecond * 100):
		return nil, errors.New("Timeout")
	}
}

func TestConversation(t *testing.T) {
	Convey("Receives Bitfield message and sends Interested message", t, func() {
		s := NewBTService(port, []byte(peerIdRemote))
		mc := NewMockConnectionFetcher()
		s.ConnectionFetcher = mc
		s.AddHash(hash)
		s.StartListening()

		time.Sleep(time.Millisecond * 50)

		// TODO: check that peer data is saved within service data structure
		peers := make([]structure.Peer, 1)
		peers[0] = structure.Peer{IP: net.IPv4(192, 168, 1, 1), Port: 55556}
		s.InitiateHandshakes(hash, peers)

		for _, p := range peers {
			c0 := mc.Conns[p.AddrString()].Conn.(*MockConnection)
			hs, err := structure.ReadHandshake(bytes.NewReader(<-c0.ReceiveBytesChan))
			So(hs, ShouldNotBeNil)
			So(err, ShouldBeNil)

			hs, _ = structure.NewHandshake(hash, []byte(peerIdClient))
			c0.SendHandshake(hs)

			bf := structure.BitFieldFromHexString("\xff\xff\xff\x01")
			msg := &structure.BitFieldMessage{BasicMessage: structure.BasicMessage{Type: structure.MessageTypeBitField, Length: 5, Payload: []byte("\xff\xff\xff\x01")}, BitField: bf}
			c0.SendMessage(msg)

			m, err := ReadMessageOrTimeout(c0)
			So(err, ShouldBeNil)
			So(m.Type, ShouldEqual, structure.MessageTypeInterested)
		}

		time.Sleep(time.Millisecond * 50)

		_ = s.StopListening()
	})
}
<commit_msg>Increase stability of handler tests<commit_after>package client

import (
	"bytes"
	"errors"
	"github.com/oleiade/lane"
	. "github.com/smartystreets/goconvey/convey"
	"github.com/stratospark/torro/structure"
	"io"
	"log"
	"net"
	"testing"
	"time"
)

type MockConnection struct {
	ReadQueue         *lane.Queue
	SendMessageChan   chan structure.Message
	RestOfMessageChan chan bool
	ReceiveBytesChan  chan []byte
}

func (c *MockConnection) SendMessage(m structure.Message) {
	c.SendMessageChan <- m
}

func (c *MockConnection) Read(b []byte) (n int, err error) {
	log.Println("Trying to read, len(b): ", len(b))
	readBytes := func() {
		log.Printf("ReadBytes, len(queue): %d", c.ReadQueue.Size())
		h := c.ReadQueue.Dequeue()
		read, _ := h.([]byte)
		log.Printf("MockConnection Read: %q, len(read): %d, len(b): %d\n", read, len(read), len(b))
		for i := 0; i < len(b); i++ {
			b[i] = read[i]
		}
		log.Printf("After REad")
		if len(b) < len(read) {
			c.ReadQueue.Prepend(read[len(b):])
			c.RestOfMessageChan <- true
		}
	}
	select {
	case <-c.RestOfMessageChan:
		readBytes()
	case m := <-c.SendMessageChan:
		log.Println("SendMessageChan")
		read := m.Bytes()
		c.ReadQueue.Enqueue(read)
		readBytes()
	}

	return len(b), err
}

func (c *MockConnection) Write(b []byte) (n int, err error) {
	log.Printf("MockConnection Write: %q\n", b)
	c.ReceiveBytesChan <- b
	return len(b), nil
}

func (c *MockConnection) Close() error {
	return nil
}

type MockConnectionFetcher struct {
	Conns map[string]*BTConn
}

func NewMockConnectionFetcher() *MockConnectionFetcher {
	return &MockConnectionFetcher{
		Conns: make(map[string]*BTConn),
	}
}

func (t *MockConnectionFetcher) Dial(addr string) (*BTConn, error) {
	conn := &MockConnection{
		ReadQueue:         lane.NewQueue(),
		SendMessageChan:   make(chan structure.Message, 1),
		RestOfMessageChan: make(chan bool, 1),
		ReceiveBytesChan:  make(chan []byte, 1),
	}
	btc := &BTConn{Conn: conn}
	t.Conns[addr] = btc

	return btc, nil
}

var (
	port         int    = 55555
	peerIdRemote string = "-TR2840-nj5ovtREMOTE"
	peerIdClient string = "-TR2840-nj5ovtCLIENT"
	hash         []byte = []byte("\x6f\xda\xb6\xc1\x9f\x72\x14\x76\xfa\xca\xab\x36\x60\x8a\x87\x7a\x2a\xac\xbf\xc9")
)

func TestListen(t *testing.T) {
	Convey("Listens to incoming connections on a given port", t, func() {
		s := NewBTService(port, []byte(peerIdRemote))
		s.StartListening()

		So(s.Listener, ShouldNotBeNil)
		So(s.Listening, ShouldBeTrue)

		_ = s.StopListening()
		So(s.Listening, ShouldBeFalse)
	})

}

func TestAcceptHandshake(t *testing.T) {
	Convey("Accepts a handshake and adds to the connection list", t, func() {
		s := NewBTService(port, []byte(peerIdRemote))
		s.AddHash(hash)
		s.StartListening()

		So(s.Listener, ShouldNotBeNil)
		So(s.Listening, ShouldBeTrue)

		addr, _ := net.ResolveTCPAddr("tcp", "localhost:55555")
		conn, err := net.DialTCP("tcp", nil, addr)
		So(err, ShouldBeNil)

		handshake := "\x13\x42\x69\x74\x54\x6f\x72\x72\x65\x6e\x74\x20\x70\x72\x6f\x74\x6f\x63\x6f\x6c\x00\x00\x00\x00\x00\x10\x00\x05\x6f\xda\xb6\xc1\x9f\x72\x14\x76\xfa\xca\xab\x36\x60\x8a\x87\x7a\x2a\xac\xbf\xc9\x2d\x55\x54\x33\x34\x34\x30\x2d\xcf\x9f\x51\x2b\xce\x01\x31\xf9\x38\x6f\xb6\x98"
		conn.Write([]byte(handshake))
		respHandshake, err := structure.ReadHandshake(conn)
		So(err, ShouldBeNil)
		So(respHandshake, ShouldNotBeNil)
		So(len(s.Peers), ShouldEqual, 1)

		_ = s.StopListening()
		So(s.Listening, ShouldBeFalse)
	})
}

func TestRejectHandshake(t *testing.T) {
	Convey("Rejects a malformed handshake request", t, func() {
		s := NewBTService(port, []byte(peerIdRemote))
		s.StartListening()

		So(s.Listener, ShouldNotBeNil)
		So(s.Listening, ShouldBeTrue)

		addr, _ := net.ResolveTCPAddr("tcp", "localhost:55555")
		conn, err := net.DialTCP("tcp", nil, addr)
		So(err, ShouldBeNil)

		handshake := "\x13\x43\x69\x74\x54\x6f\x72\x72\x65\x6e\x74\x20\x70\x72\x6f\x74\x6f\x63\x6f\x6c\x00\x00\x00\x00\x00\x10\x00\x05\x6f\xda\xb6\xc1\x9f\x72\x14\x76\xfa\xca\xab\x36\x60\x8a\x87\x7a\x2a\xac\xbf\xc9\x2d\x55\x54\x33\x34\x34\x30\x2d\xcf\x9f\x51\x2b\xce\x01\x31\xf9\x38\x6f\xb6\x98"
		conn.Write([]byte(handshake))
		So(len(s.Peers), ShouldEqual, 0)
		buf := make([]byte, 4)
		_, err = io.ReadFull(conn, buf)
		So(err, ShouldNotBeNil)

		_ = s.StopListening()
		So(s.Listening, ShouldBeFalse)
	})
}

func TestInitiateHandshakes(t *testing.T) {
	Convey("Sends out handshake request to every IP in list", t, func() {
		s := NewBTService(port, []byte(peerIdRemote))
		mc := NewMockConnectionFetcher()
		s.ConnectionFetcher = mc
		s.AddHash(hash)
		s.StartListening()

		// TODO: check that peer data is saved within service data structure
		peers := make([]structure.Peer, 2)
		peers[0] = structure.Peer{IP: net.IPv4(192, 168, 1, 1), Port: 55556}
		peers[1] = structure.Peer{IP: net.IPv4(192, 168, 1, 2), Port: 55557}
		s.InitiateHandshakes(hash, peers)

		for _, p := range peers {
			c0 := mc.Conns[p.AddrString()].Conn.(*MockConnection)
			hs, err := structure.ReadHandshake(bytes.NewReader(<-c0.ReceiveBytesChan))
			So(hs, ShouldNotBeNil)
			So(err, ShouldBeNil)
			hs, _ = structure.NewHandshake(hash, []byte(peerIdClient))
			c0.SendMessage(hs)
		}

		time.Sleep(time.Millisecond)
		So(len(s.Peers), ShouldEqual, len(peers))

		_ = s.StopListening()
		So(s.Listening, ShouldBeFalse)
	})
}

func ReadMessageOrTimeout(c *MockConnection) (*structure.BasicMessage, error) {
	select {
	case b := <-c.ReceiveBytesChan:
		m, err := structure.ReadMessage(bytes.NewReader(b))
		bm, _ := m.(*structure.BasicMessage)
		So(bm, ShouldNotBeNil)
		So(err, ShouldBeNil)
		return bm, err
	case <-time.After(time.Millisecond * 10):
		return nil, errors.New("Timeout")
	}
}

func TestConversation(t *testing.T) {
	Convey("Receives Bitfield message and sends Interested message", t, func() {
		s := NewBTService(port, []byte(peerIdRemote))
		mc := NewMockConnectionFetcher()
		s.ConnectionFetcher = mc
		s.AddHash(hash)
		s.StartListening()

		// TODO: check that peer data is saved within service data structure
		peers := make([]structure.Peer, 1)
		peers[0] = structure.Peer{IP: net.IPv4(192, 168, 1, 1), Port: 55556}
		s.InitiateHandshakes(hash, peers)

		for _, p := range peers {
			c0 := mc.Conns[p.AddrString()].Conn.(*MockConnection)
			hs, err := structure.ReadHandshake(bytes.NewReader(<-c0.ReceiveBytesChan))
			So(hs, ShouldNotBeNil)
			So(err, ShouldBeNil)

			hs, _ = structure.NewHandshake(hash, []byte(peerIdClient))
			c0.SendMessage(hs)

			bf := structure.BitFieldFromHexString("\xff\xff\xff\x01")
			msg := &structure.BitFieldMessage{BasicMessage: structure.BasicMessage{Type: structure.MessageTypeBitField, Length: 5, Payload: []byte("\xff\xff\xff\x01")}, BitField: bf}
			c0.SendMessage(msg)

			m, err := ReadMessageOrTimeout(c0)
			So(err, ShouldBeNil)
			So(m.Type, ShouldEqual, structure.MessageTypeInterested)
		}

		_ = s.StopListening()
	})
}
<|endoftext|>


<commit_before>package cloud

import (
	"bytes"
	"context"
	"encoding/json"
	"strings"
	"text/template"

	"github.com/appscode/pharmer/api"
	"github.com/ghodss/yaml"
	"github.com/hashicorp/go-version"
	"gopkg.in/ini.v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/client-go/util/cert"
	kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1alpha1"
)

type TemplateData struct {
	IsPreReleaseVersion bool
	KubernetesVersion   string
	KubeadmVersion      string
	KubeadmToken        string
	CAKey               string
	FrontProxyKey       string
	APIServerAddress    string
	APIBindPort         int32
	ExtraDomains        string
	NetworkProvider     string
	MasterConfiguration string
	CloudConfigPath     string
	CloudConfig         string
	NodeGroupName       string
	Provider            string
}

func GetTemplateData(ctx context.Context, cluster *api.Cluster, nodeGroup string) TemplateData {
	td := TemplateData{
		KubernetesVersion: cluster.Spec.KubernetesVersion,
		KubeadmVersion:    cluster.Spec.KubeadmVersion,
		KubeadmToken:      cluster.Spec.Token,
		CAKey:             string(cert.EncodePrivateKeyPEM(CAKey(ctx))),
		FrontProxyKey:     string(cert.EncodePrivateKeyPEM(FrontProxyCAKey(ctx))),
		APIServerAddress:  cluster.APIServerAddress(),
		APIBindPort:       6443,
		ExtraDomains:      cluster.Spec.ClusterExternalDomain,
		NetworkProvider:   cluster.Spec.Networking.NetworkProvider,
		NodeGroupName:     nodeGroup,
		Provider:          cluster.Spec.Cloud.CloudProvider,
	}
	if cluster.Spec.KubeadmVersion != "" {
		if v, err := version.NewVersion(cluster.Spec.KubeadmVersion); err == nil && v.Prerelease() != "" {
			td.IsPreReleaseVersion = true
		}
	}

	cfg := kubeadmapi.MasterConfiguration{
		TypeMeta: metav1.TypeMeta{
			APIVersion: "kubeadm.k8s.io/v1alpha1",
			Kind:       "MasterConfiguration",
		},
		API:  cluster.Spec.API,
		Etcd: cluster.Spec.Etcd,
		Networking: kubeadmapi.Networking{
			ServiceSubnet: cluster.Spec.Networking.ServiceSubnet,
			PodSubnet:     cluster.Spec.Networking.PodSubnet,
			DNSDomain:     cluster.Spec.Networking.DNSDomain,
		},
		KubernetesVersion: cluster.Spec.KubernetesVersion,
		CloudProvider:     cluster.Spec.Cloud.CloudProvider,
		// AuthorizationModes:
		Token: cluster.Spec.Token,
		//	TokenTTL:                   cluster.Spec.TokenTTL,
		APIServerExtraArgs:         map[string]string{},
		ControllerManagerExtraArgs: map[string]string{},
		SchedulerExtraArgs:         map[string]string{},
		APIServerCertSANs:          []string{},
	}
	{
		if cluster.Spec.Cloud.GCE != nil {
			cfg.APIServerExtraArgs["cloud-config"] = cluster.Spec.Cloud.CloudConfigPath
			td.CloudConfigPath = cluster.Spec.Cloud.CloudConfigPath
			// ref: https://github.com/kubernetes/kubernetes/blob/release-1.5/cluster/gce/configure-vm.sh#L846
			cfg := ini.Empty()
			err := cfg.Section("global").ReflectFrom(cluster.Spec.Cloud.GCE.CloudConfig)
			if err != nil {
				panic(err)
			}
			var buf bytes.Buffer
			_, err = cfg.WriteTo(&buf)
			if err != nil {
				panic(err)
			}
			td.CloudConfig = buf.String()
		}
	}
	{
		if cluster.Spec.Cloud.Azure != nil {
			cfg.APIServerExtraArgs["cloud-config"] = cluster.Spec.Cloud.CloudConfigPath
			td.CloudConfigPath = cluster.Spec.Cloud.CloudConfigPath

			data, err := json.MarshalIndent(cluster.Spec.Cloud.Azure.CloudConfig, "", "  ")
			if err != nil {
				panic(err)
			}
			td.CloudConfig = string(data)
		}
	}
	{
		extraDomains := []string{}
		if domain := Extra(ctx).ExternalDomain(cluster.Name); domain != "" {
			extraDomains = append(extraDomains, domain)
		}
		if domain := Extra(ctx).InternalDomain(cluster.Name); domain != "" {
			extraDomains = append(extraDomains, domain)
		}
		td.ExtraDomains = strings.Join(extraDomains, ",")
	}
	cb, err := yaml.Marshal(&cfg)
	if err != nil {
		panic(err)
	}
	td.MasterConfiguration = string(cb)
	return td
}

func RenderStartupScript(ctx context.Context, cluster *api.Cluster, role, nodeGroup string) (string, error) {
	var buf bytes.Buffer
	if err := StartupScriptTemplate.ExecuteTemplate(&buf, role, GetTemplateData(ctx, cluster, nodeGroup)); err != nil {
		return "", err
	}
	return buf.String(), nil
}

var (
	StartupScriptTemplate = template.Must(template.New(api.RoleMaster).Parse(`#!/bin/bash
set -x
set -o errexit
set -o nounset
set -o pipefail

# log to /var/log/startup-script.log
exec > >(tee -a /var/log/startup-script.log)
exec 2>&1

# kill apt processes (E: Unable to lock directory /var/lib/apt/lists/)
kill $(ps aux | grep '[a]pt' | awk '{print $2}') || true

{{ template "prepare-host" . }}

apt-get update -y
apt-get install -y apt-transport-https curl ca-certificates

curl -fSsL https://packages.cloud.google.com/apt/doc/apt-key.gpg | apt-key add -
echo 'deb http://apt.kubernetes.io/ kubernetes-xenial main' > /etc/apt/sources.list.d/kubernetes.list

add-apt-repository -y ppa:gluster/glusterfs-3.10

apt-get update -y
apt-get install -y \
	socat \
	ebtables \
	git \
	haveged \
	nfs-common \
	cron \
	glusterfs-client \
	kubelet \
	{{ if not .IsPreReleaseVersion }}kubeadm{{ if .KubeadmVersion }}={{ .KubeadmVersion }}{{ end }}{{ end }} \
	cloud-utils \
	docker.io || true

{{ if .IsPreReleaseVersion }}
curl -Lo kubeadm https://dl.k8s.io/release/{{ .KubeadmVersion }}/bin/linux/amd64/kubeadm \
    && chmod +x kubeadm \
	&& mv kubeadm /usr/bin/
{{ end }}

curl -Lo pre-k https://cdn.appscode.com/binaries/pre-k/0.1.0-alpha.5/pre-k-linux-amd64 \
	&& chmod +x pre-k \
	&& mv pre-k /usr/bin/

systemctl enable docker
systemctl start docker

cat > /etc/systemd/system/kubelet.service.d/20-pharmer.conf <<EOF
[Service]
Environment="KUBELET_EXTRA_ARGS=--node-labels=cloud.appscode.com/pool={{ .NodeGroupName }} {{ if  .CloudConfigPath }} --cloud-provider={{ .Provider }} --cloud-config={{ .CloudConfigPath }} {{ end }}"
EOF

systemctl daemon-reload
systemctl restart kubelet

kubeadm reset

{{ template "setup-certs" . }}

{{ if .CloudConfigPath }}
cat > {{ .CloudConfigPath }} <<EOF
{{ .CloudConfig }}
EOF
{{ end }}

mkdir -p /etc/kubernetes/kubeadm

{{ if .MasterConfiguration }}
cat > /etc/kubernetes/kubeadm/config.yaml <<EOF
{{ .MasterConfiguration }}
EOF
{{ end }}

pre-k merge master-config \
	--config=/etc/kubernetes/kubeadm/config.yaml \
	--apiserver-bind-port={{ .APIBindPort }} \
	--token={{ .KubeadmToken }} \
	--apiserver-advertise-address=$(pre-k get public-ips --all=false) \
	--apiserver-cert-extra-sans=$(pre-k get public-ips --routable) \
	--apiserver-cert-extra-sans=$(pre-k get private-ips) \
	--apiserver-cert-extra-sans={{ .ExtraDomains }} \
	--kubernetes-version={{ .KubernetesVersion }} \
	> /etc/kubernetes/kubeadm/config.yaml
kubeadm init --config=/etc/kubernetes/kubeadm/config.yaml --skip-token-print

{{ if eq .NetworkProvider "flannel" }}
{{ template "flannel" . }}
{{ else if eq .NetworkProvider "calico" }}
{{ template "calico" . }}
{{ end }}

mkdir -p ~/.kube
sudo cp -i /etc/kubernetes/admin.conf ~/.kube/config
sudo chown $(id -u):$(id -g) ~/.kube/config

{{ if eq .Provider "external" }}
{{ template "master-ccm" . }}
{{end}}
`))

	_ = template.Must(StartupScriptTemplate.New(api.RoleNode).Parse(`#!/bin/bash
set -x
set -o errexit
set -o nounset
set -o pipefail

# log to /var/log/startup-script.log
exec > >(tee -a /var/log/startup-script.log)
exec 2>&1

# kill apt processes (E: Unable to lock directory /var/lib/apt/lists/)
kill $(ps aux | grep '[a]pt' | awk '{print $2}') || true

{{ template "prepare-host" . }}

apt-get update -y
apt-get install -y apt-transport-https curl ca-certificates

curl -fSsL https://packages.cloud.google.com/apt/doc/apt-key.gpg | apt-key add -
echo 'deb http://apt.kubernetes.io/ kubernetes-xenial main' > /etc/apt/sources.list.d/kubernetes.list

add-apt-repository -y ppa:gluster/glusterfs-3.10

apt-get update -y
apt-get install -y \
	socat \
	ebtables \
	git \
	haveged \
	nfs-common \
	cron \
	glusterfs-client \
	kubelet \
	{{ if not .IsPreReleaseVersion }}kubeadm{{ if .KubeadmVersion }}={{ .KubeadmVersion }}{{ end }}{{ end }} \
	docker.io || true

{{ if .IsPreReleaseVersion }}
curl -Lo kubeadm https://dl.k8s.io/release/{{ .KubeadmVersion }}/bin/linux/amd64/kubeadm \
    && chmod +x kubeadm \
	&& mv kubeadm /usr/bin/
{{ end }}

systemctl enable docker
systemctl start docker

{{ if .CloudConfigPath }}
cat > {{ .CloudConfigPath }} <<EOF
{{ .CloudConfig }}
EOF
{{ end }}

{{ if eq .Provider "external" }}
cat > /etc/systemd/system/kubelet.service.d/20-pharmer.conf <<EOF
[Service]
Environment="KUBELET_EXTRA_ARGS=--node-labels=cloud.appscode.com/pool={{ .NodeGroupName }},node-role.kubernetes.io/node= --cloud-provider=external"
EOF
{{ else }}
cat > /etc/systemd/system/kubelet.service.d/20-pharmer.conf <<EOF
[Service]
Environment="KUBELET_EXTRA_ARGS=--node-labels=cloud.appscode.com/pool={{ .NodeGroupName }},node-role.kubernetes.io/node= {{ if  .CloudConfigPath }} --cloud-provider={{ .Provider }} --cloud-config={{ .CloudConfigPath }} {{ end }}"
EOF
{{end}}

systemctl daemon-reload
systemctl restart kubelet

kubeadm reset
kubeadm join --token={{ .KubeadmToken }} {{ .APIServerAddress }}
`))

	_ = template.Must(StartupScriptTemplate.New("prepare-host").Parse(``))

	_ = template.Must(StartupScriptTemplate.New("setup-certs").Parse(`
mkdir -p /etc/kubernetes/pki

cat > /etc/kubernetes/pki/ca.key <<EOF
{{ .CAKey }}
EOF
pre-k get cacert --common-name=ca < /etc/kubernetes/pki/ca.key > /etc/kubernetes/pki/ca.crt

cat > /etc/kubernetes/pki/front-proxy-ca.key <<EOF
{{ .FrontProxyKey }}
EOF
pre-k get cacert --common-name=front-proxy-ca < /etc/kubernetes/pki/front-proxy-ca.key > /etc/kubernetes/pki/front-proxy-ca.crt

chmod 600 /etc/kubernetes/pki/ca.key /etc/kubernetes/pki/front-proxy-ca.key
`))

	_ = template.Must(StartupScriptTemplate.New("calico").Parse(`
kubectl apply \
  -f http://docs.projectcalico.org/v2.3/getting-started/kubernetes/installation/hosted/kubeadm/1.6/calico.yaml \
  --kubeconfig /etc/kubernetes/admin.conf
`))

	_ = template.Must(StartupScriptTemplate.New("flannel").Parse(`
kubectl apply \
  -f https://raw.githubusercontent.com/coreos/flannel/v0.8.0/Documentation/kube-flannel.yml \
  --kubeconfig /etc/kubernetes/admin.conf
kubectl apply \
  -f https://raw.githubusercontent.com/coreos/flannel/v0.8.0/Documentation/kube-flannel-rbac.yml \
  --kubeconfig /etc/kubernetes/admin.conf
`))

	_ = template.Must(StartupScriptTemplate.New("master-ccm").Parse(`
until [ $(kubectl get pods -n kube-system -l k8s-app=kube-dns -o jsonpath={.items[0].status.phase} --kubeconfig /etc/kubernetes/admin.conf) == "Running" ]
do
   echo '.'
   sleep 5
done

kubectl apply -f "https://raw.githubusercontent.com/appscode/pharmer/ccm/cloud/providers/{{ .Provider }}/cloud-control-manager.yaml" --kubeconfig /etc/kubernetes/admin.conf

until [ $(kubectl get pods -n kube-system -l app=cloud-controller-manager -o jsonpath={.items[0].status.phase} --kubeconfig /etc/kubernetes/admin.conf) == "Running" ]
do
   echo '.'
   sleep 5
done

cat > /etc/systemd/system/kubelet.service.d/20-pharmer.conf <<EOF
[Service]
Environment="KUBELET_EXTRA_ARGS=--node-labels=cloud.appscode.com/pool={{ .NodeGroupName }} --cloud-provider=external"
EOF

NODE_NAME=$(uname -n)
kubectl taint nodes ${NODE_NAME} node.cloudprovider.kubernetes.io/uninitialized=true:NoSchedule --kubeconfig /etc/kubernetes/admin.conf

systemctl daemon-reload
systemctl restart kubelet

sleep 10
reboot

`))
)
<commit_msg>Rename ccm template<commit_after>package cloud

import (
	"bytes"
	"context"
	"encoding/json"
	"strings"
	"text/template"

	"github.com/appscode/pharmer/api"
	"github.com/ghodss/yaml"
	"github.com/hashicorp/go-version"
	"gopkg.in/ini.v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/client-go/util/cert"
	kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1alpha1"
)

type TemplateData struct {
	IsPreReleaseVersion bool
	KubernetesVersion   string
	KubeadmVersion      string
	KubeadmToken        string
	CAKey               string
	FrontProxyKey       string
	APIServerAddress    string
	APIBindPort         int32
	ExtraDomains        string
	NetworkProvider     string
	MasterConfiguration string
	CloudConfigPath     string
	CloudConfig         string
	NodeGroupName       string
	Provider            string
}

func GetTemplateData(ctx context.Context, cluster *api.Cluster, nodeGroup string) TemplateData {
	td := TemplateData{
		KubernetesVersion: cluster.Spec.KubernetesVersion,
		KubeadmVersion:    cluster.Spec.KubeadmVersion,
		KubeadmToken:      cluster.Spec.Token,
		CAKey:             string(cert.EncodePrivateKeyPEM(CAKey(ctx))),
		FrontProxyKey:     string(cert.EncodePrivateKeyPEM(FrontProxyCAKey(ctx))),
		APIServerAddress:  cluster.APIServerAddress(),
		APIBindPort:       6443,
		ExtraDomains:      cluster.Spec.ClusterExternalDomain,
		NetworkProvider:   cluster.Spec.Networking.NetworkProvider,
		NodeGroupName:     nodeGroup,
		Provider:          cluster.Spec.Cloud.CloudProvider,
	}
	if cluster.Spec.KubeadmVersion != "" {
		if v, err := version.NewVersion(cluster.Spec.KubeadmVersion); err == nil && v.Prerelease() != "" {
			td.IsPreReleaseVersion = true
		}
	}

	cfg := kubeadmapi.MasterConfiguration{
		TypeMeta: metav1.TypeMeta{
			APIVersion: "kubeadm.k8s.io/v1alpha1",
			Kind:       "MasterConfiguration",
		},
		API:  cluster.Spec.API,
		Etcd: cluster.Spec.Etcd,
		Networking: kubeadmapi.Networking{
			ServiceSubnet: cluster.Spec.Networking.ServiceSubnet,
			PodSubnet:     cluster.Spec.Networking.PodSubnet,
			DNSDomain:     cluster.Spec.Networking.DNSDomain,
		},
		KubernetesVersion: cluster.Spec.KubernetesVersion,
		CloudProvider:     cluster.Spec.Cloud.CloudProvider,
		// AuthorizationModes:
		Token: cluster.Spec.Token,
		//	TokenTTL:                   cluster.Spec.TokenTTL,
		APIServerExtraArgs:         map[string]string{},
		ControllerManagerExtraArgs: map[string]string{},
		SchedulerExtraArgs:         map[string]string{},
		APIServerCertSANs:          []string{},
	}
	{
		if cluster.Spec.Cloud.GCE != nil {
			cfg.APIServerExtraArgs["cloud-config"] = cluster.Spec.Cloud.CloudConfigPath
			td.CloudConfigPath = cluster.Spec.Cloud.CloudConfigPath
			// ref: https://github.com/kubernetes/kubernetes/blob/release-1.5/cluster/gce/configure-vm.sh#L846
			cfg := ini.Empty()
			err := cfg.Section("global").ReflectFrom(cluster.Spec.Cloud.GCE.CloudConfig)
			if err != nil {
				panic(err)
			}
			var buf bytes.Buffer
			_, err = cfg.WriteTo(&buf)
			if err != nil {
				panic(err)
			}
			td.CloudConfig = buf.String()
		}
	}
	{
		if cluster.Spec.Cloud.Azure != nil {
			cfg.APIServerExtraArgs["cloud-config"] = cluster.Spec.Cloud.CloudConfigPath
			td.CloudConfigPath = cluster.Spec.Cloud.CloudConfigPath

			data, err := json.MarshalIndent(cluster.Spec.Cloud.Azure.CloudConfig, "", "  ")
			if err != nil {
				panic(err)
			}
			td.CloudConfig = string(data)
		}
	}
	{
		extraDomains := []string{}
		if domain := Extra(ctx).ExternalDomain(cluster.Name); domain != "" {
			extraDomains = append(extraDomains, domain)
		}
		if domain := Extra(ctx).InternalDomain(cluster.Name); domain != "" {
			extraDomains = append(extraDomains, domain)
		}
		td.ExtraDomains = strings.Join(extraDomains, ",")
	}
	cb, err := yaml.Marshal(&cfg)
	if err != nil {
		panic(err)
	}
	td.MasterConfiguration = string(cb)
	return td
}

func RenderStartupScript(ctx context.Context, cluster *api.Cluster, role, nodeGroup string) (string, error) {
	var buf bytes.Buffer
	if err := StartupScriptTemplate.ExecuteTemplate(&buf, role, GetTemplateData(ctx, cluster, nodeGroup)); err != nil {
		return "", err
	}
	return buf.String(), nil
}

var (
	StartupScriptTemplate = template.Must(template.New(api.RoleMaster).Parse(`#!/bin/bash
set -x
set -o errexit
set -o nounset
set -o pipefail

# log to /var/log/startup-script.log
exec > >(tee -a /var/log/startup-script.log)
exec 2>&1

# kill apt processes (E: Unable to lock directory /var/lib/apt/lists/)
kill $(ps aux | grep '[a]pt' | awk '{print $2}') || true

{{ template "prepare-host" . }}

apt-get update -y
apt-get install -y apt-transport-https curl ca-certificates

curl -fSsL https://packages.cloud.google.com/apt/doc/apt-key.gpg | apt-key add -
echo 'deb http://apt.kubernetes.io/ kubernetes-xenial main' > /etc/apt/sources.list.d/kubernetes.list

add-apt-repository -y ppa:gluster/glusterfs-3.10

apt-get update -y
apt-get install -y \
	socat \
	ebtables \
	git \
	haveged \
	nfs-common \
	cron \
	glusterfs-client \
	kubelet \
	{{ if not .IsPreReleaseVersion }}kubeadm{{ if .KubeadmVersion }}={{ .KubeadmVersion }}{{ end }}{{ end }} \
	cloud-utils \
	docker.io || true

{{ if .IsPreReleaseVersion }}
curl -Lo kubeadm https://dl.k8s.io/release/{{ .KubeadmVersion }}/bin/linux/amd64/kubeadm \
    && chmod +x kubeadm \
	&& mv kubeadm /usr/bin/
{{ end }}

curl -Lo pre-k https://cdn.appscode.com/binaries/pre-k/0.1.0-alpha.5/pre-k-linux-amd64 \
	&& chmod +x pre-k \
	&& mv pre-k /usr/bin/

systemctl enable docker
systemctl start docker

cat > /etc/systemd/system/kubelet.service.d/20-pharmer.conf <<EOF
[Service]
Environment="KUBELET_EXTRA_ARGS=--node-labels=cloud.appscode.com/pool={{ .NodeGroupName }} {{ if  .CloudConfigPath }} --cloud-provider={{ .Provider }} --cloud-config={{ .CloudConfigPath }} {{ end }}"
EOF

systemctl daemon-reload
systemctl restart kubelet

kubeadm reset

{{ template "setup-certs" . }}

{{ if .CloudConfigPath }}
cat > {{ .CloudConfigPath }} <<EOF
{{ .CloudConfig }}
EOF
{{ end }}

mkdir -p /etc/kubernetes/kubeadm

{{ if .MasterConfiguration }}
cat > /etc/kubernetes/kubeadm/config.yaml <<EOF
{{ .MasterConfiguration }}
EOF
{{ end }}

pre-k merge master-config \
	--config=/etc/kubernetes/kubeadm/config.yaml \
	--apiserver-bind-port={{ .APIBindPort }} \
	--token={{ .KubeadmToken }} \
	--apiserver-advertise-address=$(pre-k get public-ips --all=false) \
	--apiserver-cert-extra-sans=$(pre-k get public-ips --routable) \
	--apiserver-cert-extra-sans=$(pre-k get private-ips) \
	--apiserver-cert-extra-sans={{ .ExtraDomains }} \
	--kubernetes-version={{ .KubernetesVersion }} \
	> /etc/kubernetes/kubeadm/config.yaml
kubeadm init --config=/etc/kubernetes/kubeadm/config.yaml --skip-token-print

{{ if eq .NetworkProvider "flannel" }}
{{ template "flannel" . }}
{{ else if eq .NetworkProvider "calico" }}
{{ template "calico" . }}
{{ end }}

mkdir -p ~/.kube
sudo cp -i /etc/kubernetes/admin.conf ~/.kube/config
sudo chown $(id -u):$(id -g) ~/.kube/config

{{ if eq .Provider "external" }}
{{ template "ccm" . }}
{{end}}
`))

	_ = template.Must(StartupScriptTemplate.New(api.RoleNode).Parse(`#!/bin/bash
set -x
set -o errexit
set -o nounset
set -o pipefail

# log to /var/log/startup-script.log
exec > >(tee -a /var/log/startup-script.log)
exec 2>&1

# kill apt processes (E: Unable to lock directory /var/lib/apt/lists/)
kill $(ps aux | grep '[a]pt' | awk '{print $2}') || true

{{ template "prepare-host" . }}

apt-get update -y
apt-get install -y apt-transport-https curl ca-certificates

curl -fSsL https://packages.cloud.google.com/apt/doc/apt-key.gpg | apt-key add -
echo 'deb http://apt.kubernetes.io/ kubernetes-xenial main' > /etc/apt/sources.list.d/kubernetes.list

add-apt-repository -y ppa:gluster/glusterfs-3.10

apt-get update -y
apt-get install -y \
	socat \
	ebtables \
	git \
	haveged \
	nfs-common \
	cron \
	glusterfs-client \
	kubelet \
	{{ if not .IsPreReleaseVersion }}kubeadm{{ if .KubeadmVersion }}={{ .KubeadmVersion }}{{ end }}{{ end }} \
	docker.io || true

{{ if .IsPreReleaseVersion }}
curl -Lo kubeadm https://dl.k8s.io/release/{{ .KubeadmVersion }}/bin/linux/amd64/kubeadm \
    && chmod +x kubeadm \
	&& mv kubeadm /usr/bin/
{{ end }}

systemctl enable docker
systemctl start docker

{{ if .CloudConfigPath }}
cat > {{ .CloudConfigPath }} <<EOF
{{ .CloudConfig }}
EOF
{{ end }}

{{ if eq .Provider "external" }}
cat > /etc/systemd/system/kubelet.service.d/20-pharmer.conf <<EOF
[Service]
Environment="KUBELET_EXTRA_ARGS=--node-labels=cloud.appscode.com/pool={{ .NodeGroupName }},node-role.kubernetes.io/node= --cloud-provider=external"
EOF
{{ else }}
cat > /etc/systemd/system/kubelet.service.d/20-pharmer.conf <<EOF
[Service]
Environment="KUBELET_EXTRA_ARGS=--node-labels=cloud.appscode.com/pool={{ .NodeGroupName }},node-role.kubernetes.io/node= {{ if  .CloudConfigPath }} --cloud-provider={{ .Provider }} --cloud-config={{ .CloudConfigPath }} {{ end }}"
EOF
{{end}}

systemctl daemon-reload
systemctl restart kubelet

kubeadm reset
kubeadm join --token={{ .KubeadmToken }} {{ .APIServerAddress }}
`))

	_ = template.Must(StartupScriptTemplate.New("prepare-host").Parse(``))

	_ = template.Must(StartupScriptTemplate.New("setup-certs").Parse(`
mkdir -p /etc/kubernetes/pki

cat > /etc/kubernetes/pki/ca.key <<EOF
{{ .CAKey }}
EOF
pre-k get cacert --common-name=ca < /etc/kubernetes/pki/ca.key > /etc/kubernetes/pki/ca.crt

cat > /etc/kubernetes/pki/front-proxy-ca.key <<EOF
{{ .FrontProxyKey }}
EOF
pre-k get cacert --common-name=front-proxy-ca < /etc/kubernetes/pki/front-proxy-ca.key > /etc/kubernetes/pki/front-proxy-ca.crt

chmod 600 /etc/kubernetes/pki/ca.key /etc/kubernetes/pki/front-proxy-ca.key
`))

	_ = template.Must(StartupScriptTemplate.New("ccm").Parse(`
until [ $(kubectl get pods -n kube-system -l k8s-app=kube-dns -o jsonpath={.items[0].status.phase} --kubeconfig /etc/kubernetes/admin.conf) == "Running" ]
do
   echo '.'
   sleep 5
done

kubectl apply -f "https://raw.githubusercontent.com/appscode/pharmer/ccm/cloud/providers/{{ .Provider }}/cloud-control-manager.yaml" --kubeconfig /etc/kubernetes/admin.conf

until [ $(kubectl get pods -n kube-system -l app=cloud-controller-manager -o jsonpath={.items[0].status.phase} --kubeconfig /etc/kubernetes/admin.conf) == "Running" ]
do
   echo '.'
   sleep 5
done

cat > /etc/systemd/system/kubelet.service.d/20-pharmer.conf <<EOF
[Service]
Environment="KUBELET_EXTRA_ARGS=--node-labels=cloud.appscode.com/pool={{ .NodeGroupName }} --cloud-provider=external"
EOF

NODE_NAME=$(uname -n)
kubectl taint nodes ${NODE_NAME} node.cloudprovider.kubernetes.io/uninitialized=true:NoSchedule --kubeconfig /etc/kubernetes/admin.conf

systemctl daemon-reload
systemctl restart kubelet

sleep 10
reboot
`))

	_ = template.Must(StartupScriptTemplate.New("calico").Parse(`
kubectl apply \
  -f http://docs.projectcalico.org/v2.3/getting-started/kubernetes/installation/hosted/kubeadm/1.6/calico.yaml \
  --kubeconfig /etc/kubernetes/admin.conf
`))

	_ = template.Must(StartupScriptTemplate.New("flannel").Parse(`
kubectl apply \
  -f https://raw.githubusercontent.com/coreos/flannel/v0.8.0/Documentation/kube-flannel.yml \
  --kubeconfig /etc/kubernetes/admin.conf
kubectl apply \
  -f https://raw.githubusercontent.com/coreos/flannel/v0.8.0/Documentation/kube-flannel-rbac.yml \
  --kubeconfig /etc/kubernetes/admin.conf
`))
)
<|endoftext|>


<commit_before>package cloudDNS

import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	rackspace "github.com/ghthor/gorackspace"
	"io/ioutil"
	"net/http"
)

type (
	// omitempty fields aren't needed when submiting a request to Add, Modify, Remove a Record
	Record struct {
		Name     string `json:"name"`
		Id       string `json:"id,omitempty"`
		Type     string `json:"type"`
		Data     string `json:"data"`
		Updated  string `json:"updated,omitempty"`
		Created  string `json:"created,omitempty"`
		TTL      int    `json:"ttl"`
		Comment  string `json:"comment,omitempty"`
		Priority int    `json:"priority,omitempty"`
	}

	RecordList struct {
		Records []Record `json:"records"`
	}

	RecordListResponse struct {
		Records      []Record `json:"records"`
		TotalEntries int      `json:"totalEntries"`
		rawJson      string
	}
)

func ListRecords(session rackspace.AuthSession, domain Domain) ([]Record, error) {
	// TODO: Inspect the Catalog to ensure this session has CloudDNS ability
	reqUrl := fmt.Sprintf("%s/domains/%d/records", session.ServiceCatalog().CloudDNS[0].PublicURL, domain.Id)
	req, _ := http.NewRequest("GET", reqUrl, nil)

	req.Header.Set("Accept", "application/json")
	req.Header.Set("X-Auth-Token", session.Id())

	resp, err := session.Client().Do(req)
	if err != nil {
		return nil, err
	}

	responseBody, _ := ioutil.ReadAll(resp.Body)

	switch resp.StatusCode {
	default:
		fallthrough
	case 401, 403, 400, 500, 503:
		return nil, errors.New(fmt.Sprintf("%s", responseBody))
	case 200, 203:
	}

	recordListResponse := &RecordListResponse{rawJson: string(responseBody)}

	// Parse Response Body
	err = json.Unmarshal(responseBody, recordListResponse)
	if err != nil {
		return nil, err
	}

	return recordListResponse.Records, nil
}

func AddRecords(session rackspace.AuthSession, domain Domain, records []Record) (*rackspace.JobStatus, error) {
	recordList := RecordList{records}
	recordListJson, err := json.Marshal(recordList)
	if err != nil {
		return nil, err
	}

	// TODO: Inspect the Catalog to ensure this session has CloudDNS ability
	reqUrl := fmt.Sprintf("%s/domains/%d/records", session.ServiceCatalog().CloudDNS[0].PublicURL, domain.Id)
	req, _ := http.NewRequest("POST", reqUrl, bytes.NewBuffer(recordListJson))

	req.Header.Set("Content-type", "application/json")
	req.Header.Set("Accept", "application/json")
	req.Header.Set("X-Auth-Token", session.Id())

	resp, err := session.Client().Do(req)
	if err != nil {
		return nil, err
	}

	responseBody, _ := ioutil.ReadAll(resp.Body)

	switch resp.StatusCode {
	default:
		fallthrough
	case 400, 401, 404, 413, 500, 503:
		return nil, errors.New(fmt.Sprintf("%s", responseBody))
	case 200, 202:
	}

	jobStatus := &rackspace.JobStatus{}
	err = json.Unmarshal(responseBody, jobStatus)
	if err != nil {
		return nil, err
	}

	return jobStatus, nil
}

func AddRecord(session rackspace.AuthSession, domain Domain, newRecord Record) (*rackspace.JobStatus, error) {
	return AddRecords(session, domain, []Record{newRecord})
}
<commit_msg>Added the ability to Update Records<commit_after>package cloudDNS

import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	rackspace "github.com/ghthor/gorackspace"
	"io/ioutil"
	"net/http"
)

type (
	// omitempty fields aren't needed when submiting a request to Add, Modify, Remove a Record
	Record struct {
		Name     string `json:"name"`
		Id       string `json:"id,omitempty"`
		Type     string `json:"type"`
		Data     string `json:"data"`
		Updated  string `json:"updated,omitempty"`
		Created  string `json:"created,omitempty"`
		TTL      int    `json:"ttl"`
		Comment  string `json:"comment,omitempty"`
		Priority int    `json:"priority,omitempty"`
	}

	RecordList struct {
		Records []Record `json:"records"`
	}

	RecordListResponse struct {
		Records      []Record `json:"records"`
		TotalEntries int      `json:"totalEntries"`
		rawJson      string
	}
)

func ListRecords(session rackspace.AuthSession, domain Domain) ([]Record, error) {
	// TODO: Inspect the Catalog to ensure this session has CloudDNS ability
	reqUrl := fmt.Sprintf("%s/domains/%d/records", session.ServiceCatalog().CloudDNS[0].PublicURL, domain.Id)
	req, _ := http.NewRequest("GET", reqUrl, nil)

	req.Header.Set("Accept", "application/json")
	req.Header.Set("X-Auth-Token", session.Id())

	resp, err := session.Client().Do(req)
	if err != nil {
		return nil, err
	}

	responseBody, _ := ioutil.ReadAll(resp.Body)

	switch resp.StatusCode {
	default:
		fallthrough
	case 401, 403, 400, 500, 503:
		return nil, errors.New(fmt.Sprintf("%s", responseBody))
	case 200, 203:
	}

	recordListResponse := &RecordListResponse{rawJson: string(responseBody)}

	// Parse Response Body
	err = json.Unmarshal(responseBody, recordListResponse)
	if err != nil {
		return nil, err
	}

	return recordListResponse.Records, nil
}

func AddRecords(session rackspace.AuthSession, domain Domain, records []Record) (*rackspace.JobStatus, error) {
	recordList := RecordList{records}
	recordListJson, err := json.Marshal(recordList)
	if err != nil {
		return nil, err
	}

	// TODO: Inspect the Catalog to ensure this session has CloudDNS ability
	reqUrl := fmt.Sprintf("%s/domains/%d/records", session.ServiceCatalog().CloudDNS[0].PublicURL, domain.Id)
	req, _ := http.NewRequest("POST", reqUrl, bytes.NewBuffer(recordListJson))

	req.Header.Set("Content-type", "application/json")
	req.Header.Set("Accept", "application/json")
	req.Header.Set("X-Auth-Token", session.Id())

	resp, err := session.Client().Do(req)
	if err != nil {
		return nil, err
	}

	responseBody, _ := ioutil.ReadAll(resp.Body)

	switch resp.StatusCode {
	default:
		fallthrough
	case 400, 401, 404, 413, 500, 503:
		return nil, errors.New(fmt.Sprintf("%s", responseBody))
	case 200, 202:
	}

	jobStatus := &rackspace.JobStatus{}
	err = json.Unmarshal(responseBody, jobStatus)
	if err != nil {
		return nil, err
	}

	return jobStatus, nil
}

func AddRecord(session rackspace.AuthSession, domain Domain, newRecord Record) (*rackspace.JobStatus, error) {
	return AddRecords(session, domain, []Record{newRecord})
}

func UpdateRecords(session rackspace.AuthSession, domain Domain, records []Record) (*rackspace.JobStatus, error) {
	recordList := RecordList{records}
	recordListJson, err := json.Marshal(recordList)
	if err != nil {
		return nil, err
	}

	// TODO: Inspect the Catalog to ensure this session has CloudDNS ability
	reqUrl := fmt.Sprintf("%s/domains/%d/records", session.ServiceCatalog().CloudDNS[0].PublicURL, domain.Id)
	req, _ := http.NewRequest("PUT", reqUrl, bytes.NewBuffer(recordListJson))

	req.Header.Set("Content-type", "application/json")
	req.Header.Set("Accept", "application/json")
	req.Header.Set("X-Auth-Token", session.Id())

	resp, err := session.Client().Do(req)
	if err != nil {
		return nil, err
	}

	responseBody, _ := ioutil.ReadAll(resp.Body)

	switch resp.StatusCode {
	default:
		fallthrough
	case 400, 401, 404, 413, 500, 503:
		return nil, errors.New(fmt.Sprintf("%s", responseBody))
	case 200, 202:
	}

	jobStatus := &rackspace.JobStatus{}
	err = json.Unmarshal(responseBody, jobStatus)
	if err != nil {
		return nil, err
	}

	return jobStatus, nil
}

func UpdateRecord(session rackspace.AuthSession, domain Domain, record Record) (*rackspace.JobStatus, error) {
	if record.Id == "" || record.Name == "" {
		return nil, errors.New(fmt.Sprintf("Invalid Record: %s", record))
	}

	return UpdateRecords(session, domain, []Record{record})
}
<|endoftext|>


<commit_before>// Copyright 2009 The Go 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 main

import (
	"bytes"
	"flag"
	"fmt"
	"go/ast"
	"go/parser"
	"go/printer"
	"go/scanner"
	"go/token"
	"io/ioutil"
	"os"
	"path/filepath"
	"strings"
)

var (
	// main operation modes
	list        = flag.Bool("l", false, "list files whose formatting differs from gofmt's")
	write       = flag.Bool("w", false, "write result to (source) file instead of stdout")
	rewriteRule = flag.String("r", "", "rewrite rule (e.g., 'α[β:len(α)] -> α[β:]')")
	simplifyAST = flag.Bool("s", false, "simplify code")

	// layout control
	comments  = flag.Bool("comments", true, "print comments")
	tabWidth  = flag.Int("tabwidth", 8, "tab width")
	tabIndent = flag.Bool("tabindent", true, "indent with tabs independent of -spaces")
	useSpaces = flag.Bool("spaces", true, "align with spaces instead of tabs")
)

var (
	fset        = token.NewFileSet()
	exitCode    = 0
	rewrite     func(*ast.File) *ast.File
	parserMode  uint
	printerMode uint
)

func report(err os.Error) {
	scanner.PrintError(os.Stderr, err)
	exitCode = 2
}

func usage() {
	fmt.Fprintf(os.Stderr, "usage: gofmt [flags] [path ...]\n")
	flag.PrintDefaults()
	os.Exit(2)
}

func initParserMode() {
	parserMode = uint(0)
	if *comments {
		parserMode |= parser.ParseComments
	}
}

func initPrinterMode() {
	printerMode = uint(0)
	if *tabIndent {
		printerMode |= printer.TabIndent
	}
	if *useSpaces {
		printerMode |= printer.UseSpaces
	}
}

func isGoFile(f *os.FileInfo) bool {
	// ignore non-Go files
	return f.IsRegular() && !strings.HasPrefix(f.Name, ".") && strings.HasSuffix(f.Name, ".go")
}

func processFile(f *os.File) os.Error {
	src, err := ioutil.ReadAll(f)
	if err != nil {
		return err
	}

	file, err := parser.ParseFile(fset, f.Name(), src, parserMode)

	if err != nil {
		return err
	}

	if rewrite != nil {
		file = rewrite(file)
	}

	if *simplifyAST {
		simplify(file)
	}

	var buf bytes.Buffer
	_, err = (&printer.Config{printerMode, *tabWidth}).Fprint(&buf, fset, file)
	if err != nil {
		return err
	}
	res := buf.Bytes()

	if !bytes.Equal(src, res) {
		// formatting has changed
		if *list {
			fmt.Fprintln(os.Stdout, f.Name())
		}
		if *write {
			err = ioutil.WriteFile(f.Name(), res, 0)
			if err != nil {
				return err
			}
		}
	}

	if !*list && !*write {
		_, err = os.Stdout.Write(res)
	}

	return err
}

func processFileByName(filename string) os.Error {
	file, err := os.Open(filename, os.O_RDONLY, 0)
	if err != nil {
		return err
	}
	defer file.Close()
	return processFile(file)
}

type fileVisitor chan os.Error

func (v fileVisitor) VisitDir(path string, f *os.FileInfo) bool {
	return true
}

func (v fileVisitor) VisitFile(path string, f *os.FileInfo) {
	if isGoFile(f) {
		v <- nil // synchronize error handler
		if err := processFileByName(path); err != nil {
			v <- err
		}
	}
}

func walkDir(path string) {
	v := make(fileVisitor)
	go func() {
		filepath.Walk(path, v, v)
		close(v)
	}()
	for err := range v {
		if err != nil {
			report(err)
		}
	}
}

func main() {
	flag.Usage = usage
	flag.Parse()
	if *tabWidth < 0 {
		fmt.Fprintf(os.Stderr, "negative tabwidth %d\n", *tabWidth)
		os.Exit(2)
	}

	initParserMode()
	initPrinterMode()
	initRewrite()

	if flag.NArg() == 0 {
		if err := processFile(os.Stdin); err != nil {
			report(err)
		}
	}

	for i := 0; i < flag.NArg(); i++ {
		path := flag.Arg(i)
		switch dir, err := os.Stat(path); {
		case err != nil:
			report(err)
		case dir.IsRegular():
			if err := processFileByName(path); err != nil {
				report(err)
			}
		case dir.IsDirectory():
			walkDir(path)
		}
	}

	os.Exit(exitCode)
}
<commit_msg>gofmt: add profiling flag<commit_after>// Copyright 2009 The Go 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 main

import (
	"bytes"
	"flag"
	"fmt"
	"go/ast"
	"go/parser"
	"go/printer"
	"go/scanner"
	"go/token"
	"io/ioutil"
	"os"
	"path/filepath"
	"runtime/pprof"
	"strings"
)

var (
	// main operation modes
	list        = flag.Bool("l", false, "list files whose formatting differs from gofmt's")
	write       = flag.Bool("w", false, "write result to (source) file instead of stdout")
	rewriteRule = flag.String("r", "", "rewrite rule (e.g., 'α[β:len(α)] -> α[β:]')")
	simplifyAST = flag.Bool("s", false, "simplify code")

	// layout control
	comments  = flag.Bool("comments", true, "print comments")
	tabWidth  = flag.Int("tabwidth", 8, "tab width")
	tabIndent = flag.Bool("tabindent", true, "indent with tabs independent of -spaces")
	useSpaces = flag.Bool("spaces", true, "align with spaces instead of tabs")

	// debugging
	cpuprofile = flag.String("cpuprofile", "", "write cpu profile to this file")
)

var (
	fset        = token.NewFileSet()
	exitCode    = 0
	rewrite     func(*ast.File) *ast.File
	parserMode  uint
	printerMode uint
)

func report(err os.Error) {
	scanner.PrintError(os.Stderr, err)
	exitCode = 2
}

func usage() {
	fmt.Fprintf(os.Stderr, "usage: gofmt [flags] [path ...]\n")
	flag.PrintDefaults()
	os.Exit(2)
}

func initParserMode() {
	parserMode = uint(0)
	if *comments {
		parserMode |= parser.ParseComments
	}
}

func initPrinterMode() {
	printerMode = uint(0)
	if *tabIndent {
		printerMode |= printer.TabIndent
	}
	if *useSpaces {
		printerMode |= printer.UseSpaces
	}
}

func isGoFile(f *os.FileInfo) bool {
	// ignore non-Go files
	return f.IsRegular() && !strings.HasPrefix(f.Name, ".") && strings.HasSuffix(f.Name, ".go")
}

func processFile(f *os.File) os.Error {
	src, err := ioutil.ReadAll(f)
	if err != nil {
		return err
	}

	file, err := parser.ParseFile(fset, f.Name(), src, parserMode)

	if err != nil {
		return err
	}

	if rewrite != nil {
		file = rewrite(file)
	}

	if *simplifyAST {
		simplify(file)
	}

	var buf bytes.Buffer
	_, err = (&printer.Config{printerMode, *tabWidth}).Fprint(&buf, fset, file)
	if err != nil {
		return err
	}
	res := buf.Bytes()

	if !bytes.Equal(src, res) {
		// formatting has changed
		if *list {
			fmt.Fprintln(os.Stdout, f.Name())
		}
		if *write {
			err = ioutil.WriteFile(f.Name(), res, 0)
			if err != nil {
				return err
			}
		}
	}

	if !*list && !*write {
		_, err = os.Stdout.Write(res)
	}

	return err
}

func processFileByName(filename string) os.Error {
	file, err := os.Open(filename, os.O_RDONLY, 0)
	if err != nil {
		return err
	}
	defer file.Close()
	return processFile(file)
}

type fileVisitor chan os.Error

func (v fileVisitor) VisitDir(path string, f *os.FileInfo) bool {
	return true
}

func (v fileVisitor) VisitFile(path string, f *os.FileInfo) {
	if isGoFile(f) {
		v <- nil // synchronize error handler
		if err := processFileByName(path); err != nil {
			v <- err
		}
	}
}

func walkDir(path string) {
	v := make(fileVisitor)
	go func() {
		filepath.Walk(path, v, v)
		close(v)
	}()
	for err := range v {
		if err != nil {
			report(err)
		}
	}
}

func main() {
	// call gofmtMain in a separate function
	// so that it can use defer and have them
	// run before the exit.
	gofmtMain()
	os.Exit(exitCode)
}

func gofmtMain() {
	flag.Usage = usage
	flag.Parse()
	if *tabWidth < 0 {
		fmt.Fprintf(os.Stderr, "negative tabwidth %d\n", *tabWidth)
		exitCode = 2
		return
	}

	if *cpuprofile != "" {
		f, err := os.Open(*cpuprofile, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0666)
		if err != nil {
			fmt.Fprintf(os.Stderr, "creating cpu profile: %s\n", err)
			exitCode = 2
			return
		}
		defer f.Close()
		pprof.StartCPUProfile(f)
		defer pprof.StopCPUProfile()
	}

	initParserMode()
	initPrinterMode()
	initRewrite()

	if flag.NArg() == 0 {
		if err := processFile(os.Stdin); err != nil {
			report(err)
		}
	}

	for i := 0; i < flag.NArg(); i++ {
		path := flag.Arg(i)
		switch dir, err := os.Stat(path); {
		case err != nil:
			report(err)
		case dir.IsRegular():
			if err := processFileByName(path); err != nil {
				report(err)
			}
		case dir.IsDirectory():
			walkDir(path)
		}
	}
}
<|endoftext|>