<commit_before>/*

   Copyright 2016 Wenhui Shen <www.webx.top>

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.

*/

package manager

import (
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"strings"
	"sync"

	"github.com/admpub/fsnotify"
	"github.com/admpub/log"

	"github.com/webx-top/echo/logger"
	"github.com/webx-top/echo/middleware/render/driver"
)

var Default driver.Manager = New()

func New() *Manager {
	m := &Manager{
		caches: make(map[string][]byte),
		mutex:  &sync.RWMutex{},
		ignores: map[string]bool{
			"*.tmp": false,
			"*.TMP": false,
		},
		allows:   map[string]bool{},
		callback: map[string]func(string, string, string){},
		Logger:   log.GetLogger(`watcher`),
		done:     make(chan bool),
	}
	return m
}

// Manager Tempate manager
type Manager struct {
	caches   map[string][]byte
	firstDir string
	mutex    *sync.RWMutex
	ignores  map[string]bool
	allows   map[string]bool
	Logger   logger.Logger
	callback map[string]func(string, string, string) //参数为：目标名称，类型(file/dir)，事件名(create/delete/modify/rename)
	done     chan bool
	watcher  *fsnotify.Watcher
}

func (m *Manager) closeMoniter() {
	m.mutex.Lock()
	defer m.mutex.Unlock()
	m.firstDir = ``
	if m.done == nil {
		return
	}
	close(m.done)
	m.done = nil
	if m.watcher != nil {
		m.watcher.Close()
	}
}

func (m *Manager) getWatcher() *fsnotify.Watcher {
	if m.watcher == nil {
		var err error
		m.watcher, err = fsnotify.NewWatcher()
		if err != nil {
			m.Logger.Error(err)
		}
	}
	return m.watcher
}

func (m *Manager) AddCallback(rootDir string, callback func(name, typ, event string)) {
	m.mutex.Lock()
	m.callback[rootDir] = callback
	m.mutex.Unlock()
}

func (m *Manager) ClearCallback() {
	m.callback = map[string]func(string, string, string){}
}

func (m *Manager) DelCallback(rootDir string) {
	m.mutex.Lock()
	if _, ok := m.callback[rootDir]; ok {
		delete(m.callback, rootDir)
	}
	m.mutex.Unlock()
}

func (m *Manager) ClearAllows() {
	m.allows = map[string]bool{}
}

func (m *Manager) AddAllow(allows ...string) {
	for _, allow := range allows {
		m.allows[allow] = true
	}
}

func (m *Manager) DelAllow(allow string) {
	if _, ok := m.allows[allow]; ok {
		delete(m.allows, allow)
	}
}

func (m *Manager) ClearIgnores() {
	m.ignores = map[string]bool{}
}

func (m *Manager) AddIgnore(ignores ...string) {
	for _, ignore := range ignores {
		m.allows[ignore] = false
	}
}

func (m *Manager) DelIgnore(ignore string) {
	if _, ok := m.ignores[ignore]; ok {
		delete(m.ignores, ignore)
	}
}

func (m *Manager) SetLogger(logger logger.Logger) {
	m.Logger = logger
}

func (m *Manager) allowCached(name string) bool {
	ok := len(m.allows) == 0
	if !ok {
		_, ok = m.allows[`*`+filepath.Ext(name)]
		if !ok {
			ok = m.allows[filepath.Base(name)]
		}
	}
	return ok
}

func (m *Manager) AddWatchDir(ppath string) (err error) {
	m.mutex.Lock()
	defer m.mutex.Unlock()
	ppath, err = filepath.Abs(ppath)
	if err != nil {
		return
	}
	if len(m.firstDir) == 0 {
		m.firstDir = ppath
	}
	err = m.getWatcher().Add(ppath)
	if err != nil {
		m.Logger.Error(err.Error())
		return
	}

	err = filepath.Walk(ppath, func(f string, info os.FileInfo, err error) error {
		if info.IsDir() {
			return m.getWatcher().Add(f)
		}
		return nil
	})

	//err = m.cacheAll(ppath)
	return
}

func (m *Manager) CancelWatchDir(oldDir string) (err error) {
	m.mutex.Lock()
	defer m.mutex.Unlock()
	oldDir, err = filepath.Abs(oldDir)
	if err != nil {
		return
	}
	for tmpl := range m.caches {
		if strings.HasPrefix(tmpl, oldDir) {
			if err != nil {
				return
			}
			delete(m.caches, tmpl)
		}
	}
	filepath.Walk(oldDir, func(f string, info os.FileInfo, err error) error {
		if info.IsDir() {
			m.getWatcher().Remove(f)
			return nil
		}
		return nil
	})
	m.getWatcher().Remove(oldDir)
	return
}

func (m *Manager) ChangeWatchDir(oldDir string, newDir string) (err error) {
	err = m.CancelWatchDir(oldDir)
	if err != nil {
		return err
	}
	err = m.AddWatchDir(newDir)
	return
}

func (m *Manager) Start() error {
	go m.watch()
	return nil
}

func (m *Manager) watch() error {
	watcher := m.getWatcher()
	var logSuffix string
	if len(m.firstDir) > 0 {
		logSuffix = ": " + m.firstDir + " etc"
	}
	m.Logger.Debug("TemplateMgr watcher is start" + logSuffix + ".")
	defer func() {
		watcher.Close()
		m.watcher = nil
	}()
	go func() {
		for {
			select {
			case ev := <-watcher.Events:
				if _, ok := m.ignores[filepath.Base(ev.Name)]; ok {
					return
				}
				if _, ok := m.ignores[`*`+filepath.Ext(ev.Name)]; ok {
					return
				}
				d, err := os.Stat(ev.Name)
				if err != nil {
					return
				}
				if ev.Op&fsnotify.Create == fsnotify.Create {
					if d.IsDir() {
						watcher.Add(ev.Name)
						m.onChange(ev.Name, "dir", "create")
						return
					}
					m.onChange(ev.Name, "file", "create")
					if m.allowCached(ev.Name) {
						content, err := ioutil.ReadFile(ev.Name)
						if err != nil {
							m.Logger.Infof("loaded template %v failed: %v", ev.Name, err)
							return
						}
						m.Logger.Infof("loaded template file %v success", ev.Name)
						m.CacheTemplate(ev.Name, content)
					}
				} else if ev.Op&fsnotify.Remove == fsnotify.Remove {
					if d.IsDir() {
						watcher.Remove(ev.Name)
						m.onChange(ev.Name, "dir", "delete")
						return
					}
					m.onChange(ev.Name, "file", "delete")
					if m.allowCached(ev.Name) {
						m.CacheDelete(ev.Name)
					}
				} else if ev.Op&fsnotify.Write == fsnotify.Write {
					if d.IsDir() {
						m.onChange(ev.Name, "dir", "modify")
						return
					}
					m.onChange(ev.Name, "file", "modify")
					if m.allowCached(ev.Name) {
						content, err := ioutil.ReadFile(ev.Name)
						if err != nil {
							m.Logger.Errorf("reloaded template %v failed: %v", ev.Name, err)
							return
						}
						m.CacheTemplate(ev.Name, content)
						m.Logger.Infof("reloaded template %v success", ev.Name)
					}
				} else if ev.Op&fsnotify.Rename == fsnotify.Rename {
					if d.IsDir() {
						watcher.Remove(ev.Name)
						m.onChange(ev.Name, "dir", "rename")
						return
					}
					m.onChange(ev.Name, "file", "rename")
					if m.allowCached(ev.Name) {
						m.CacheDelete(ev.Name)
					}
				}
			case err := <-watcher.Errors:
				if err != nil {
					m.Logger.Error("error:", err)
				}
			}
		}
	}()

	<-m.done
	m.Logger.Debug("TemplateMgr watcher is closed" + logSuffix + ".")
	return nil
}

func (m *Manager) onChange(name, typ, event string) {
	for _, callback := range m.callback {
		callback(name, typ, event)
	}
}

func (m *Manager) cacheAll(rootDir string) error {
	fmt.Print(rootDir + ": Reading the contents of the template files, please wait... ")
	err := filepath.Walk(rootDir, func(f string, info os.FileInfo, err error) error {
		if info.IsDir() {
			return nil
		}
		if _, ok := m.ignores[filepath.Base(f)]; !ok {
			content, err := ioutil.ReadFile(f)
			if err != nil {
				m.Logger.Debugf("load template %s error: %v", f, err)
				return err
			}
			m.Logger.Debugf("loaded template", f)
			m.caches[f] = content
		}
		return nil
	})
	fmt.Println(rootDir + ": Complete.")
	return err
}

func (m *Manager) Close() {
	m.closeMoniter()
}

func (m *Manager) GetTemplate(tmpl string) ([]byte, error) {
	tmplPath, err := filepath.Abs(tmpl)
	if err != nil {
		return nil, err
	}
	m.mutex.Lock()
	defer m.mutex.Unlock()
	if content, ok := m.caches[tmplPath]; ok {
		m.Logger.Debugf("load template %v from cache", tmplPath)
		return content, nil
	}
	content, err := ioutil.ReadFile(tmplPath)
	if err != nil {
		return nil, err
	}
	m.Logger.Debugf("load template %v from the file", tmplPath)
	m.caches[tmplPath] = content
	return content, err
}

func (m *Manager) SetTemplate(tmpl string, content []byte) error {
	tmplPath, err := filepath.Abs(tmpl)
	if err != nil {
		return err
	}
	m.mutex.Lock()
	defer m.mutex.Unlock()
	err = ioutil.WriteFile(tmplPath, content, 0666)
	if err != nil {
		return err
	}
	m.Logger.Debugf("load template %v from the file", tmplPath)
	m.caches[tmplPath] = content
	return err
}

func (m *Manager) CacheTemplate(tmpl string, content []byte) {
	m.Logger.Debugf("update template %v on cache", tmpl)
	m.mutex.Lock()
	m.caches[tmpl] = content
	m.mutex.Unlock()
}

func (m *Manager) CacheDelete(tmpl string) {
	m.mutex.Lock()
	if _, ok := m.caches[tmpl]; ok {
		m.Logger.Infof("delete template %v from cache", tmpl)
		delete(m.caches, tmpl)
	}
	m.mutex.Unlock()
}

func (m *Manager) ClearCache() {
	m.caches = make(map[string][]byte)
}
<commit_msg>update<commit_after>/*

   Copyright 2016 Wenhui Shen <www.webx.top>

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.

*/

package manager

import (
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"strings"
	"sync"

	"github.com/admpub/fsnotify"
	"github.com/admpub/log"

	"github.com/webx-top/echo/logger"
	"github.com/webx-top/echo/middleware/render/driver"
)

var Default driver.Manager = New()

func New() *Manager {
	m := &Manager{
		caches: make(map[string][]byte),
		mutex:  &sync.RWMutex{},
		ignores: map[string]bool{
			"*.tmp": false,
			"*.TMP": false,
		},
		allows:   map[string]bool{},
		callback: map[string]func(string, string, string){},
		Logger:   log.GetLogger(`watcher`),
		done:     make(chan bool),
	}
	return m
}

// Manager Tempate manager
type Manager struct {
	caches   map[string][]byte
	firstDir string
	mutex    *sync.RWMutex
	ignores  map[string]bool
	allows   map[string]bool
	Logger   logger.Logger
	callback map[string]func(string, string, string) //参数为：目标名称，类型(file/dir)，事件名(create/delete/modify/rename)
	done     chan bool
	watcher  *fsnotify.Watcher
}

func (m *Manager) closeMoniter() {
	m.mutex.Lock()
	defer m.mutex.Unlock()
	m.firstDir = ``
	if m.done == nil {
		return
	}
	close(m.done)
	m.done = nil
	if m.watcher != nil {
		m.watcher.Close()
	}
}

func (m *Manager) getWatcher() *fsnotify.Watcher {
	if m.watcher == nil {
		var err error
		m.watcher, err = fsnotify.NewWatcher()
		if err != nil {
			m.Logger.Error(err)
		}
	}
	return m.watcher
}

func (m *Manager) AddCallback(rootDir string, callback func(name, typ, event string)) {
	m.mutex.Lock()
	m.callback[rootDir] = callback
	m.mutex.Unlock()
}

func (m *Manager) ClearCallback() {
	m.callback = map[string]func(string, string, string){}
}

func (m *Manager) DelCallback(rootDir string) {
	m.mutex.Lock()
	if _, ok := m.callback[rootDir]; ok {
		delete(m.callback, rootDir)
	}
	m.mutex.Unlock()
}

func (m *Manager) ClearAllows() {
	m.allows = map[string]bool{}
}

func (m *Manager) AddAllow(allows ...string) {
	for _, allow := range allows {
		m.allows[allow] = true
	}
}

func (m *Manager) DelAllow(allow string) {
	if _, ok := m.allows[allow]; ok {
		delete(m.allows, allow)
	}
}

func (m *Manager) ClearIgnores() {
	m.ignores = map[string]bool{}
}

func (m *Manager) AddIgnore(ignores ...string) {
	for _, ignore := range ignores {
		m.allows[ignore] = false
	}
}

func (m *Manager) DelIgnore(ignore string) {
	if _, ok := m.ignores[ignore]; ok {
		delete(m.ignores, ignore)
	}
}

func (m *Manager) SetLogger(logger logger.Logger) {
	m.Logger = logger
}

func (m *Manager) allowCached(name string) bool {
	ok := len(m.allows) == 0
	if !ok {
		_, ok = m.allows[`*`+filepath.Ext(name)]
		if !ok {
			ok = m.allows[filepath.Base(name)]
		}
	}
	return ok
}

func (m *Manager) AddWatchDir(ppath string) (err error) {
	m.mutex.Lock()
	defer m.mutex.Unlock()
	ppath, err = filepath.Abs(ppath)
	if err != nil {
		return
	}
	if len(m.firstDir) == 0 {
		m.firstDir = ppath
	}
	err = m.getWatcher().Add(ppath)
	if err != nil {
		m.Logger.Error(err.Error())
		return
	}

	err = filepath.Walk(ppath, func(f string, info os.FileInfo, err error) error {
		if info.IsDir() {
			return m.getWatcher().Add(f)
		}
		return nil
	})

	//err = m.cacheAll(ppath)
	return
}

func (m *Manager) CancelWatchDir(oldDir string) (err error) {
	m.mutex.Lock()
	defer m.mutex.Unlock()
	oldDir, err = filepath.Abs(oldDir)
	if err != nil {
		return
	}
	for tmpl := range m.caches {
		if strings.HasPrefix(tmpl, oldDir) {
			if err != nil {
				return
			}
			delete(m.caches, tmpl)
		}
	}
	filepath.Walk(oldDir, func(f string, info os.FileInfo, err error) error {
		if info.IsDir() {
			m.getWatcher().Remove(f)
			return nil
		}
		return nil
	})
	m.getWatcher().Remove(oldDir)
	return
}

func (m *Manager) ChangeWatchDir(oldDir string, newDir string) (err error) {
	err = m.CancelWatchDir(oldDir)
	if err != nil {
		return err
	}
	err = m.AddWatchDir(newDir)
	return
}

func (m *Manager) Start() error {
	go m.watch()
	return nil
}

func (m *Manager) watch() error {
	watcher := m.getWatcher()
	var logSuffix string
	if len(m.firstDir) > 0 {
		logSuffix = ": " + m.firstDir + " etc"
	}
	m.Logger.Debug("TemplateMgr watcher is start" + logSuffix + ".")
	defer func() {
		watcher.Close()
		m.watcher = nil
	}()
	go func() {
		for {
			select {
			case ev := <-watcher.Events:
				if _, ok := m.ignores[filepath.Base(ev.Name)]; ok {
					return
				}
				if _, ok := m.ignores[`*`+filepath.Ext(ev.Name)]; ok {
					return
				}
				d, err := os.Stat(ev.Name)
				if err != nil {
					return
				}
				if ev.Op&fsnotify.Create == fsnotify.Create {
					if d.IsDir() {
						watcher.Add(ev.Name)
						m.onChange(ev.Name, "dir", "create")
						return
					}
					m.onChange(ev.Name, "file", "create")
					if m.allowCached(ev.Name) {
						content, err := ioutil.ReadFile(ev.Name)
						if err != nil {
							m.Logger.Infof("loaded template %v failed: %v", ev.Name, err)
							return
						}
						m.Logger.Infof("loaded template file %v success", ev.Name)
						m.CacheTemplate(ev.Name, content)
					}
				} else if ev.Op&fsnotify.Remove == fsnotify.Remove {
					if d.IsDir() {
						watcher.Remove(ev.Name)
						m.onChange(ev.Name, "dir", "delete")
						return
					}
					m.onChange(ev.Name, "file", "delete")
					if m.allowCached(ev.Name) {
						m.CacheDelete(ev.Name)
					}
				} else if ev.Op&fsnotify.Write == fsnotify.Write {
					if d.IsDir() {
						m.onChange(ev.Name, "dir", "modify")
						return
					}
					m.onChange(ev.Name, "file", "modify")
					if m.allowCached(ev.Name) {
						content, err := ioutil.ReadFile(ev.Name)
						if err != nil {
							m.Logger.Errorf("reloaded template %v failed: %v", ev.Name, err)
							return
						}
						m.CacheTemplate(ev.Name, content)
						m.Logger.Infof("reloaded template %v success", ev.Name)
					}
				} else if ev.Op&fsnotify.Rename == fsnotify.Rename {
					if d.IsDir() {
						watcher.Remove(ev.Name)
						m.onChange(ev.Name, "dir", "rename")
						return
					}
					m.onChange(ev.Name, "file", "rename")
					if m.allowCached(ev.Name) {
						m.CacheDelete(ev.Name)
					}
				}
			case err := <-watcher.Errors:
				if err != nil {
					m.Logger.Error("error:", err)
				}
			}
		}
	}()

	<-m.done
	m.Logger.Debug("TemplateMgr watcher is closed" + logSuffix + ".")
	return nil
}

func (m *Manager) onChange(name, typ, event string) {
	for _, callback := range m.callback {
		callback(name, typ, event)
	}
}

func (m *Manager) cacheAll(rootDir string) error {
	fmt.Print(rootDir + ": Reading the contents of the template files, please wait... ")
	err := filepath.Walk(rootDir, func(f string, info os.FileInfo, err error) error {
		if info.IsDir() {
			return nil
		}
		if _, ok := m.ignores[filepath.Base(f)]; !ok {
			content, err := ioutil.ReadFile(f)
			if err != nil {
				m.Logger.Debugf("load template %s error: %v", f, err)
				return err
			}
			m.Logger.Debugf("loaded template", f)
			m.caches[f] = content
		}
		return nil
	})
	fmt.Println(rootDir + ": Complete.")
	return err
}

func (m *Manager) Close() {
	m.closeMoniter()
}

func (m *Manager) GetTemplate(tmpl string) ([]byte, error) {
	tmplPath, err := filepath.Abs(tmpl)
	if err != nil {
		return nil, err
	}
	if !m.allowCached(tmplPath) {
		return ioutil.ReadFile(tmplPath)
	}
	m.mutex.Lock()
	defer m.mutex.Unlock()
	if content, ok := m.caches[tmplPath]; ok {
		m.Logger.Debugf("load template %v from cache", tmplPath)
		return content, nil
	}
	content, err := ioutil.ReadFile(tmplPath)
	if err != nil {
		return nil, err
	}
	m.Logger.Debugf("load template %v from the file", tmplPath)
	m.caches[tmplPath] = content
	return content, err
}

func (m *Manager) SetTemplate(tmpl string, content []byte) error {
	tmplPath, err := filepath.Abs(tmpl)
	if err != nil {
		return err
	}
	m.mutex.Lock()
	defer m.mutex.Unlock()
	err = ioutil.WriteFile(tmplPath, content, 0666)
	if err != nil {
		return err
	}
	if m.allowCached(tmplPath) {
		m.Logger.Debugf("load template %v from the file", tmplPath)
		m.caches[tmplPath] = content
	}
	return err
}

func (m *Manager) CacheTemplate(tmpl string, content []byte) {
	m.Logger.Debugf("update template %v on cache", tmpl)
	m.mutex.Lock()
	m.caches[tmpl] = content
	m.mutex.Unlock()
}

func (m *Manager) CacheDelete(tmpl string) {
	m.mutex.Lock()
	if _, ok := m.caches[tmpl]; ok {
		m.Logger.Infof("delete template %v from cache", tmpl)
		delete(m.caches, tmpl)
	}
	m.mutex.Unlock()
}

func (m *Manager) ClearCache() {
	m.caches = make(map[string][]byte)
}
<|endoftext|>


<commit_before>package team

import (
	"fmt"

	"github.com/appcelerator/amp/api/rpc/account"
	"github.com/appcelerator/amp/cli"
	"github.com/appcelerator/amp/pkg/time"
	"github.com/spf13/cobra"
	"golang.org/x/net/context"
	"google.golang.org/grpc"
)

type getTeamOptions struct {
	org  string
	team string
}

// NewTeamGetCommand returns a new instance of the get team command.
func NewTeamGetCommand(c cli.Interface) *cobra.Command {
	opts := getTeamOptions{}
	cmd := &cobra.Command{
		Use:     "get [OPTIONS]",
		Short:   "Get team",
		PreRunE: cli.NoArgs,
		RunE: func(cmd *cobra.Command, args []string) error {
			return getTeam(c, cmd, opts)
		},
	}
	flags := cmd.Flags()
	flags.StringVar(&opts.org, "org", "", "Organization name")
	flags.StringVar(&opts.team, "team", "", "Team name")
	return cmd
}

func getTeam(c cli.Interface, cmd *cobra.Command, opts getTeamOptions) error {
	if !cmd.Flag("org").Changed {
		opts.org = c.Console().GetInput("organization name")
	}
	if !cmd.Flag("team").Changed {
		opts.team = c.Console().GetInput("team name")
	}

	conn := c.ClientConn()
	client := account.NewAccountClient(conn)
	request := &account.GetTeamRequest{
		OrganizationName: opts.org,
		TeamName:         opts.team,
	}
	reply, err := client.GetTeam(context.Background(), request)
	if err != nil {
		return fmt.Errorf("%s", grpc.ErrorDesc(err))
	}
	c.Console().Printf("Team: %s\n", reply.Team.Name)
	c.Console().Printf("Organization: %s\n", opts.org)
	c.Console().Printf("Created On: %s\n", time.ConvertTime(reply.Team.CreateDt))
	return nil
}
<commit_msg>team flag replaced as argument (#1419)<commit_after>package team

import (
	"fmt"

	"github.com/appcelerator/amp/api/rpc/account"
	"github.com/appcelerator/amp/cli"
	"github.com/appcelerator/amp/pkg/time"
	"github.com/spf13/cobra"
	"golang.org/x/net/context"
	"google.golang.org/grpc"
)

type getTeamOptions struct {
	org  string
}

// NewTeamGetCommand returns a new instance of the get team command.
func NewTeamGetCommand(c cli.Interface) *cobra.Command {
	opts := getTeamOptions{}
	cmd := &cobra.Command{
		Use:     "get [OPTIONS] TEAM",
		Short:   "Get team",
		PreRunE: cli.ExactArgs(1),
		RunE: func(cmd *cobra.Command, args []string) error {
			return getTeam(c, cmd, args, opts)
		},
	}
	flags := cmd.Flags()
	flags.StringVar(&opts.org, "org", "", "Organization name")
	return cmd
}

func getTeam(c cli.Interface, cmd *cobra.Command, args []string, opts getTeamOptions) error {
	if !cmd.Flag("org").Changed {
		opts.org = c.Console().GetInput("organization name")
	}

	conn := c.ClientConn()
	client := account.NewAccountClient(conn)
	request := &account.GetTeamRequest{
		OrganizationName: opts.org,
		TeamName:         args[0],
	}
	reply, err := client.GetTeam(context.Background(), request)
	if err != nil {
		return fmt.Errorf("%s", grpc.ErrorDesc(err))
	}
	c.Console().Printf("Team: %s\n", reply.Team.Name)
	c.Console().Printf("Organization: %s\n", opts.org)
	c.Console().Printf("Created On: %s\n", time.ConvertTime(reply.Team.CreateDt))
	return nil
}
<|endoftext|>


<commit_before>package version

// Version of Functions
var Version = "0.3.354"
<commit_msg>fnserver: 0.3.355 release [skip ci]<commit_after>package version

// Version of Functions
var Version = "0.3.355"
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"errors"
	"github.com/globocom/config"
	"github.com/globocom/gandalf/db"
	"github.com/globocom/gandalf/repository"
	"github.com/globocom/gandalf/user"
	"io"
	"labix.org/v2/mgo/bson"
	"log/syslog"
	"os"
	"os/exec"
	"path"
	"regexp"
	"strings"
)

var log *syslog.Writer

func hasWritePermission(u *user.User, r *repository.Repository) (allowed bool) {
	for _, userName := range r.Users {
		if u.Name == userName {
			return true
		}
	}
	return false
}

func hasReadPermission(u *user.User, r *repository.Repository) (allowed bool) {
	if r.IsPublic {
		return true
	}
	for _, userName := range r.Users {
		if u.Name == userName {
			return true
		}
	}
	return false
}

// Returns the command being executed by ssh.
// When a user runs `$ git push` from his/her machine, the server
// receives a ssh command, identified by this user (by the ssh key).
// The command and it's parameters are available through the SSH_ORIGINAL_COMMAND
// environment variable. In the git push example, it would have the following value:
// SSH_ORIGINAL_COMMAND=git-receive-pack 'foo.git'
// This function is responsible for retrieving the `git-receive-pack` part of SSH_ORIGINAL_COMMAND
func action() string {
	return strings.Split(os.Getenv("SSH_ORIGINAL_COMMAND"), " ")[0]
}

// Get the repository name requested in SSH_ORIGINAL_COMMAND and retrieves
// the related document on the database and returns it.
// this function does two distinct things (maybe it should'n), it
// parses the SSH_ORIGINAL_COMMAND and returns a "validation" error if it doesn't
// matches the expected format and gets the repository from the database based on the info
// obtained by the SSH_ORIGINAL_COMMAND parse.
func requestedRepository() (repository.Repository, error) {
	repoName, err := requestedRepositoryName()
	if err != nil {
		return repository.Repository{}, err
	}
	var repo repository.Repository
	if err := db.Session.Repository().Find(bson.M{"_id": repoName}).One(&repo); err != nil {
		return repository.Repository{}, errors.New("Repository not found")
	}
	return repo, nil
}

func requestedRepositoryName() (string, error) {
	r, err := regexp.Compile(`[\w-]+ '([\w-]+)\.git'`)
	if err != nil {
		panic(err)
	}
	m := r.FindStringSubmatch(os.Getenv("SSH_ORIGINAL_COMMAND"))
	if len(m) < 2 {
		return "", errors.New("Cannot deduce repository name from command. You are probably trying to do something nasty")
	}
	return m[1], nil
}

// Checks whether a command is a valid git command
// The following format is allowed:
//  git-([\w-]+) '([\w-]+)\.git'
func validateCmd() error {
	r, err := regexp.Compile(`git-([\w-]+) '([\w-]+)\.git'`)
	if err != nil {
		panic(err)
	}
	if m := r.FindStringSubmatch(os.Getenv("SSH_ORIGINAL_COMMAND")); len(m) < 3 {
		return errors.New("You've tried to execute some weird command, I'm deliberately denying you to do that, get over it.")
	}
	return nil
}

// Executes the SSH_ORIGINAL_COMMAND based on the condition
// defined by the `f` parameter.
// Also receives a custom error message to print to the end user and a
// stdout object, where the SSH_ORIGINAL_COMMAND output is going to be written
func executeAction(f func(*user.User, *repository.Repository) bool, errMsg string, stdout io.Writer) {
	var u user.User
	if err := db.Session.User().Find(bson.M{"_id": os.Args[1]}).One(&u); err != nil {
		log.Err("Error obtaining user. Gandalf database is probably in an inconsistent state.")
		return
	}
	repo, err := requestedRepository()
	if err != nil {
		log.Err(err.Error())
		return
	}
	if f(&u, &repo) {
		// split into a function (maybe executeCmd)
		c, err := formatCommand()
		if err != nil {
			log.Err(err.Error())
		}
		log.Info("Executing " + strings.Join(c, " "))
		cmd := exec.Command(c[0], c[1:]...)
		cmd.Stdin = os.Stdin
		cmd.Stdout = stdout
		stderr := &bytes.Buffer{}
		cmd.Stderr = stderr
		err = cmd.Run()
		if err != nil {
			log.Err("Got error while executing original command: " + err.Error())
			log.Err(stderr.String())
		}
		return
	}
	log.Err("Permission denied.")
	log.Err(errMsg)
}

func formatCommand() ([]string, error) {
	p, err := config.GetString("bare-location")
	if err != nil {
		log.Err(err.Error())
		return []string{}, err
	}
	repoName, err := requestedRepositoryName()
	if err != nil {
		log.Err(err.Error())
		return []string{}, err
	}
	repoName += ".git"
	cmdList := strings.Split(os.Getenv("SSH_ORIGINAL_COMMAND"), " ")
	for i, c := range cmdList {
		if c == "'"+repoName+"'" {
			cmdList[i] = path.Join(p, repoName)
			break
		}
	}
	return cmdList, nil
}

func main() {
	var err error
	log, err = syslog.New(syslog.LOG_INFO, "gandalf-listener")
	if err != nil {
		panic(err.Error())
	}
	err = config.ReadConfigFile("/etc/gandalf.conf")
	if err != nil {
		log.Err(err.Error())
		return
	}
	err = validateCmd()
	if err != nil {
		log.Err(err.Error())
		return
	}
	a := action()
	if a == "git-receive-pack" {
		executeAction(hasWritePermission, "You don't have access to write in this repository.", os.Stdout)
		return
	}
	if a == "git-upload-pack" {
		executeAction(hasReadPermission, "You don't have access to read this repository.", os.Stdout)
		return
	}
}
<commit_msg>bin: printing error in stderr<commit_after>package main

import (
	"bytes"
	"errors"
	"github.com/globocom/config"
	"github.com/globocom/gandalf/db"
	"github.com/globocom/gandalf/repository"
	"github.com/globocom/gandalf/user"
	"io"
	"labix.org/v2/mgo/bson"
	"log/syslog"
	"os"
	"os/exec"
	"path"
	"regexp"
	"strings"
)

var log *syslog.Writer

func hasWritePermission(u *user.User, r *repository.Repository) (allowed bool) {
	for _, userName := range r.Users {
		if u.Name == userName {
			return true
		}
	}
	return false
}

func hasReadPermission(u *user.User, r *repository.Repository) (allowed bool) {
	if r.IsPublic {
		return true
	}
	for _, userName := range r.Users {
		if u.Name == userName {
			return true
		}
	}
	return false
}

// Returns the command being executed by ssh.
// When a user runs `$ git push` from his/her machine, the server
// receives a ssh command, identified by this user (by the ssh key).
// The command and it's parameters are available through the SSH_ORIGINAL_COMMAND
// environment variable. In the git push example, it would have the following value:
// SSH_ORIGINAL_COMMAND=git-receive-pack 'foo.git'
// This function is responsible for retrieving the `git-receive-pack` part of SSH_ORIGINAL_COMMAND
func action() string {
	return strings.Split(os.Getenv("SSH_ORIGINAL_COMMAND"), " ")[0]
}

// Get the repository name requested in SSH_ORIGINAL_COMMAND and retrieves
// the related document on the database and returns it.
// this function does two distinct things (maybe it should'n), it
// parses the SSH_ORIGINAL_COMMAND and returns a "validation" error if it doesn't
// matches the expected format and gets the repository from the database based on the info
// obtained by the SSH_ORIGINAL_COMMAND parse.
func requestedRepository() (repository.Repository, error) {
	repoName, err := requestedRepositoryName()
	if err != nil {
		return repository.Repository{}, err
	}
	var repo repository.Repository
	if err := db.Session.Repository().Find(bson.M{"_id": repoName}).One(&repo); err != nil {
		return repository.Repository{}, errors.New("Repository not found")
	}
	return repo, nil
}

func requestedRepositoryName() (string, error) {
	r, err := regexp.Compile(`[\w-]+ '([\w-]+)\.git'`)
	if err != nil {
		panic(err)
	}
	m := r.FindStringSubmatch(os.Getenv("SSH_ORIGINAL_COMMAND"))
	if len(m) < 2 {
		return "", errors.New("Cannot deduce repository name from command. You are probably trying to do something nasty")
	}
	return m[1], nil
}

// Checks whether a command is a valid git command
// The following format is allowed:
//  git-([\w-]+) '([\w-]+)\.git'
func validateCmd() error {
	r, err := regexp.Compile(`git-([\w-]+) '([\w-]+)\.git'`)
	if err != nil {
		panic(err)
	}
	if m := r.FindStringSubmatch(os.Getenv("SSH_ORIGINAL_COMMAND")); len(m) < 3 {
		return errors.New("You've tried to execute some weird command, I'm deliberately denying you to do that, get over it.")
	}
	return nil
}

// Executes the SSH_ORIGINAL_COMMAND based on the condition
// defined by the `f` parameter.
// Also receives a custom error message to print to the end user and a
// stdout object, where the SSH_ORIGINAL_COMMAND output is going to be written
func executeAction(f func(*user.User, *repository.Repository) bool, errMsg string, stdout io.Writer) {
	var u user.User
	if err := db.Session.User().Find(bson.M{"_id": os.Args[1]}).One(&u); err != nil {
		log.Err("Error obtaining user. Gandalf database is probably in an inconsistent state.")
		fmt.Fprintln(os.Stderr, "Error obtaining user. Gandalf database is probably in an inconsistent state.")
		return
	}
	repo, err := requestedRepository()
	if err != nil {
		log.Err(err.Error())
		fmt.Fprintln(os.Stderr, err.Error())
		return
	}
	if f(&u, &repo) {
		// split into a function (maybe executeCmd)
		c, err := formatCommand()
		if err != nil {
			log.Err(err.Error())
			fmt.Fprintln(os.Stderr, err.Error())
		}
		log.Info("Executing " + strings.Join(c, " "))
		cmd := exec.Command(c[0], c[1:]...)
		cmd.Stdin = os.Stdin
		cmd.Stdout = stdout
		stderr := &bytes.Buffer{}
		cmd.Stderr = stderr
		err = cmd.Run()
		if err != nil {
			log.Err("Got error while executing original command: " + err.Error())
			log.Err(stderr.String())
			fmt.Fprintln(os.Stderr, "Got error while executing original command: "+err.Error())
			fmt.Fprintln(os.Stderr, stderr.Error())
		}
		return
	}
	log.Err("Permission denied.")
	log.Err(errMsg)
	fmt.Fprintln(os.Stderr, "Permission denied.")
	fmt.Fprintln(os.Stderr, errMsg)
}

func formatCommand() ([]string, error) {
	p, err := config.GetString("bare-location")
	if err != nil {
		log.Err(err.Error())
		return []string{}, err
	}
	repoName, err := requestedRepositoryName()
	if err != nil {
		log.Err(err.Error())
		return []string{}, err
	}
	repoName += ".git"
	cmdList := strings.Split(os.Getenv("SSH_ORIGINAL_COMMAND"), " ")
	for i, c := range cmdList {
		if c == "'"+repoName+"'" {
			cmdList[i] = path.Join(p, repoName)
			break
		}
	}
	return cmdList, nil
}

func main() {
	var err error
	log, err = syslog.New(syslog.LOG_INFO, "gandalf-listener")
	if err != nil {
		panic(err.Error())
	}
	err = config.ReadConfigFile("/etc/gandalf.conf")
	if err != nil {
		log.Err(err.Error())
		return
	}
	err = validateCmd()
	if err != nil {
		log.Err(err.Error())
		return
	}
	a := action()
	if a == "git-receive-pack" {
		executeAction(hasWritePermission, "You don't have access to write in this repository.", os.Stdout)
		return
	}
	if a == "git-upload-pack" {
		executeAction(hasReadPermission, "You don't have access to read this repository.", os.Stdout)
		return
	}
}
<|endoftext|>


<commit_before>package catalog

import (
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"os"
	"strings"

	"github.com/dnaeon/gru/graph"
	"github.com/dnaeon/gru/module"
	"github.com/dnaeon/gru/resource"
)

// ErrEmptyCatalog is returned when no resources were found from the
// loaded modules in the catalog
var ErrEmptyCatalog = errors.New("Catalog is empty")

type resourceMap map[string]resource.Resource

// Catalog type represents a collection of modules loaded from HCL or JSON
type Catalog struct {
	modules []*module.Module
}

// NewCatalog creates a new empty catalog
func NewCatalog() *Catalog {
	c := &Catalog{
		modules: make([]*module.Module, 0),
	}

	return c
}

// createResourceMap creates a map of the unique resource IDs and
// the actual resource instances
func (c *Catalog) createResourceMap() (resourceMap, error) {
	// A map containing the unique resource ID and the
	// module where the resource has been declared
	rModuleMap := make(map[string]string)

	rMap := make(resourceMap)
	for _, m := range c.modules {
		for _, r := range m.Resources {
			id := r.ID()
			if _, ok := rMap[id]; ok {
				return rMap, fmt.Errorf("Duplicate resource %s in %s, previous declaration was in %s", id, m.Name, rModuleMap[id])
			}
			rModuleMap[id] = m.Name
			rMap[id] = r
		}
	}

	if len(rMap) == 0 {
		return rMap, ErrEmptyCatalog
	}

	return rMap, nil
}

// resourceGraph creates a DAG graph for the resources in catalog
func (c *Catalog) resourceGraph() (*graph.Graph, error) {
	// Create a DAG graph of the resources in catalog
	// The generated graph can be topologically sorted in order to
	// determine the proper order of evaluating resources
	// If the graph cannot be sorted, it means we have a
	// circular dependency in our resources
	g := graph.NewGraph()

	resources, err := c.createResourceMap()
	if err != nil {
		return g, err
	}

	// A map containing the resource ids and their nodes in the graph
	// Create a graph nodes for each resource from the catalog
	nodes := make(map[string]*graph.Node)
	for name := range resources {
		node := graph.NewNode(name)
		nodes[name] = node
		g.AddNode(node)
	}

	// Connect the nodes in the graph
	for name, r := range resources {
		deps := r.Want()
		for _, dep := range deps {
			if _, ok := resources[dep]; !ok {
				e := fmt.Errorf("Resource %s wants %s, which is not in catalog", name, dep)
				return g, e
			}
			g.AddEdge(nodes[name], nodes[dep])
		}
	}

	return g, nil
}

// Run processes the catalog
func (c *Catalog) Run(w io.Writer) error {
	resourceErrors := c.Validate()
	if len(resourceErrors) > 0 {
		for _, e := range resourceErrors {
			fmt.Fprint(w, e)
		}
		return errors.New("Failed to validate catalog resources")
	}

	rMap, err := c.createResourceMap()
	if err != nil {
		return err
	}

	// Perform topological sort of the resources graph
	g, err := c.resourceGraph()
	if err != nil {
		return err
	}

	sorted, err := g.Sort()
	if err != nil {
		return err
	}

	for _, node := range sorted {
		r := rMap[node.Name]
		id := r.ID()

		state, err := r.Evaluate()
		if err != nil {
			fmt.Fprintf(w, "Failed to evaluate resource %s: %s\n", id, err)
			continue
		}

		if !resource.StateIsValid(state.Want) || !resource.StateIsValid(state.Current) {
			fmt.Fprintf(w, "Invalid state(s) for resource %s: want %s, current %s\n", id, state.Want, state.Current)
			continue
		}

		// If resource is in the desired state, but out of date
		if state.Want == state.Current {
			if state.Update {
				fmt.Fprintf(w, "%s is out of date\n", r.ID())
				err = r.Update(w)
				if err != nil {
					fmt.Fprintf(w, "%s error: %s\n", r.ID(), err)
				}
			}
			continue
		}

		fmt.Fprintf(w, "%s is %s, should be %s\n", id, state.Current, state.Want)
		var action func(w io.Writer) error
		if state.Want == resource.StatePresent || state.Want == resource.StateRunning {
			if state.Current == resource.StateAbsent || state.Current == resource.StateStopped {
				action = r.Create
			}
		} else {
			if state.Current == resource.StatePresent || state.Current == resource.StateRunning {
				action = r.Delete
			}
		}

		// Perform the operation
		err = action(w)
		if err != nil {
			fmt.Fprintf(w, "%s error: %s", r.ID(), err)
		}

		if state.Update {
			err = r.Update(w)
			if err != nil {
				fmt.Fprintf(w, "%s error %s\n", r.ID(), err)
			}
		}
	}

	return nil
}

// Validate validates the resources from catalog
func (c *Catalog) Validate() []error {
	foundErrors := make([]error, 0)

	rMap, err := c.createResourceMap()
	if err != nil {
		foundErrors = append(foundErrors, err)
	}

	// Validate resources
	for id, r := range rMap {
		err = r.Validate()
		if err != nil {
			foundErrors = append(foundErrors, fmt.Errorf("Failed to validate %s: %s\n", id, err))
		}
	}

	// Check for unknown keys
	for _, m := range c.modules {
		for _, key := range m.UnknownKeys {
			foundErrors = append(foundErrors, fmt.Errorf("Unknown key '%s' in module %s", key, m.Name))
		}
	}

	return foundErrors
}

// GenerateCatalogDOT generates a DOT file for the resources in catalog
func (c *Catalog) GenerateCatalogDOT(w io.Writer) error {
	g, err := c.resourceGraph()
	if err != nil {
		return err
	}
	g.GenerateDOT("resources", w)

	// Try a topological sort of the graph
	// In case of circular dependencies in the graph
	// generate a DOT file for the remaining nodes in the graph,
	// which would give us the resources causing circular dependencies
	if nodes, err := g.Sort(); err == graph.ErrCircularDependency {
		circularGraph := graph.NewGraph()
		circularGraph.AddNode(nodes...)
		circularGraph.GenerateDOT("resources_circular", w)
	}

	return nil
}

// Len returns the number of unique resources found in catalog
func (c *Catalog) Len() int {
	resources, err := c.createResourceMap()
	if err != nil {
		return 0
	}

	return len(resources)
}

// Load creates a catalog from the provided module name
func Load(main, path string) (*Catalog, error) {
	c := NewCatalog()

	// Discover all modules from the provided module path
	registry, err := module.Discover(path)
	if _, ok := registry[main]; !ok {
		return c, fmt.Errorf("Module %s was not found in the module path", main)
	}

	// A map containing the discovered module names and the actually loaded modules
	moduleNames := make(map[string]*module.Module)
	for n, p := range registry {
		f, err := os.Open(p)
		if err != nil {
			return c, err
		}

		m, err := module.Load(n, f)
		if err != nil {
			return c, err
		}
		moduleNames[n] = m

		f.Close()
	}

	// A map containing the modules as graph nodes
	// The graph is used to determine if we have
	// circular module imports and also to provide the
	// proper ordering of loading modules after a
	// topological sort of the graph nodes
	nodes := make(map[string]*graph.Node)
	for n := range moduleNames {
		node := graph.NewNode(n)
		nodes[n] = node
	}

	// Recursively find all imports that the main module has and
	// resolve the dependency graph
	g := graph.NewGraph()
	var createModuleGraph func(m *module.Module) error
	createModuleGraph = func(m *module.Module) error {
		if _, ok := g.GetNode(m.Name); !ok {
			g.AddNode(nodes[m.Name])
		} else {
			return nil
		}

		for _, mi := range m.Imports {
			if _, ok := moduleNames[mi.Name]; !ok {
				return fmt.Errorf("Module %s imports %s, which is not in the module path", m.Name, mi.Name)
			}

			// Build the dependencies of imported modules as well
			createModuleGraph(moduleNames[mi.Name])

			// Finally connect the nodes in the graph
			g.AddEdge(nodes[m.Name], nodes[mi.Name])
		}

		return nil
	}

	//	Build the dependency graph of the module imports
	err = createModuleGraph(moduleNames[main])
	if err != nil {
		return c, err
	}

	// Topologically sort the graph
	// In case of an error it means we have a circular import
	sorted, err := g.Sort()
	if err != nil {
		return c, err
	}

	// Finally add the sorted modules to the catalog
	for _, node := range sorted {
		c.modules = append(c.modules, moduleNames[node.Name])
	}

	return c, nil
}

// MarshalJSON creates a stripped down version of the catalog in JSON,
// which contains all resources from the catalog and is suitable for
// clients to consume in order to create a single-module catalog from it.
func (c *Catalog) MarshalJSON() ([]byte, error) {
	resources := make([]resource.Resource, 0)
	for _, m := range c.modules {
		resources = append(resources, m.Resources...)
	}

	toJson := make(map[string][]resourceMap, 0)
	for _, r := range resources {
		item := resourceMap{
			r.ResourceName(): r,
		}
		toJson[r.Type()] = append(toJson[r.Type()], item)
	}

	return json.Marshal(toJson)
}

// UnmarshalJSON loads a catalog from JSON input
// The loaded catalog is a catalog with a single
// module named "main"
func (c *Catalog) UnmarshalJSON(input []byte) error {
	r := strings.NewReader(string(input))
	main, err := module.Load("main", r)
	if err != nil {
		return err
	}

	c.modules = append(c.modules, main)

	return nil
}
<commit_msg>Connect the resource to the ones that happen before and after it<commit_after>package catalog

import (
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"os"
	"strings"

	"github.com/dnaeon/gru/graph"
	"github.com/dnaeon/gru/module"
	"github.com/dnaeon/gru/resource"
)

// ErrEmptyCatalog is returned when no resources were found from the
// loaded modules in the catalog
var ErrEmptyCatalog = errors.New("Catalog is empty")

type resourceMap map[string]resource.Resource

// Catalog type represents a collection of modules loaded from HCL or JSON
type Catalog struct {
	modules []*module.Module
}

// NewCatalog creates a new empty catalog
func NewCatalog() *Catalog {
	c := &Catalog{
		modules: make([]*module.Module, 0),
	}

	return c
}

// createResourceMap creates a map of the unique resource IDs and
// the actual resource instances
func (c *Catalog) createResourceMap() (resourceMap, error) {
	// A map containing the unique resource ID and the
	// module where the resource has been declared
	rModuleMap := make(map[string]string)

	rMap := make(resourceMap)
	for _, m := range c.modules {
		for _, r := range m.Resources {
			id := r.ResourceID()
			if _, ok := rMap[id]; ok {
				return rMap, fmt.Errorf("Duplicate resource %s in %s, previous declaration was in %s", id, m.Name, rModuleMap[id])
			}
			rModuleMap[id] = m.Name
			rMap[id] = r
		}
	}

	if len(rMap) == 0 {
		return rMap, ErrEmptyCatalog
	}

	return rMap, nil
}

// resourceGraph creates a DAG graph for the resources in catalog
func (c *Catalog) resourceGraph() (*graph.Graph, error) {
	// Create a DAG graph of the resources in catalog
	// The generated graph can be topologically sorted in order to
	// determine the proper order of evaluating resources
	// If the graph cannot be sorted, it means we have a
	// circular dependency in our resources
	g := graph.NewGraph()

	resources, err := c.createResourceMap()
	if err != nil {
		return g, err
	}

	// A map containing the resource ids and their nodes in the graph
	// Create a graph nodes for each resource from the catalog
	nodes := make(map[string]*graph.Node)
	for name := range resources {
		node := graph.NewNode(name)
		nodes[name] = node
		g.AddNode(node)
	}

	// Connect the nodes in the graph
	for name, r := range resources {
		before := r.WantBefore()
		after := r.WantAfter()

		// Connect current resource with the ones that happen after it
		for _, dep := range after {
			if _, ok := resources[dep]; !ok {
				e := fmt.Errorf("Resource %s wants %s, which is not in catalog", name, dep)
				return g, e
			}
			g.AddEdge(nodes[name], nodes[dep])
		}

		// Connect current resource with the ones that happen before it
		for _, dep := range before {
			if _, ok := resources[dep]; !ok {
				e := fmt.Errorf("Resource %s wants %s, which is not in catalog", name, dep)
				return g, e
			}
			g.AddEdge(nodes[dep], nodes[name])
		}
	}

	return g, nil
}

// Run processes the catalog
func (c *Catalog) Run(w io.Writer) error {
	resourceErrors := c.Validate()
	if len(resourceErrors) > 0 {
		for _, e := range resourceErrors {
			fmt.Fprint(w, e)
		}
		return errors.New("Failed to validate catalog resources")
	}

	rMap, err := c.createResourceMap()
	if err != nil {
		return err
	}

	// Perform topological sort of the resources graph
	g, err := c.resourceGraph()
	if err != nil {
		return err
	}

	sorted, err := g.Sort()
	if err != nil {
		return err
	}

	for _, node := range sorted {
		r := rMap[node.Name]
		id := r.ResourceID()

		state, err := r.Evaluate()
		if err != nil {
			fmt.Fprintf(w, "Failed to evaluate resource %s: %s\n", id, err)
			continue
		}

		if !resource.StateIsValid(state.Want) || !resource.StateIsValid(state.Current) {
			fmt.Fprintf(w, "Invalid state(s) for resource %s: want %s, current %s\n", id, state.Want, state.Current)
			continue
		}

		// If resource is in the desired state, but out of date
		if state.Want == state.Current {
			if state.Update {
				fmt.Fprintf(w, "%s is out of date\n", r.ResourceID())
				err = r.Update(w)
				if err != nil {
					fmt.Fprintf(w, "%s error: %s\n", r.ResourceID(), err)
				}
			}
			continue
		}

		fmt.Fprintf(w, "%s is %s, should be %s\n", id, state.Current, state.Want)
		var action func(w io.Writer) error
		if state.Want == resource.StatePresent || state.Want == resource.StateRunning {
			if state.Current == resource.StateAbsent || state.Current == resource.StateStopped {
				action = r.Create
			}
		} else {
			if state.Current == resource.StatePresent || state.Current == resource.StateRunning {
				action = r.Delete
			}
		}

		// Perform the operation
		err = action(w)
		if err != nil {
			fmt.Fprintf(w, "%s error: %s", r.ResourceID(), err)
		}

		if state.Update {
			err = r.Update(w)
			if err != nil {
				fmt.Fprintf(w, "%s error %s\n", r.ResourceID(), err)
			}
		}
	}

	return nil
}

// Validate validates the resources from catalog
func (c *Catalog) Validate() []error {
	foundErrors := make([]error, 0)

	rMap, err := c.createResourceMap()
	if err != nil {
		foundErrors = append(foundErrors, err)
	}

	// Validate resources
	for id, r := range rMap {
		err = r.Validate()
		if err != nil {
			foundErrors = append(foundErrors, fmt.Errorf("Failed to validate %s: %s\n", id, err))
		}
	}

	// Check for unknown keys
	for _, m := range c.modules {
		for _, key := range m.UnknownKeys {
			foundErrors = append(foundErrors, fmt.Errorf("Unknown key '%s' in module %s", key, m.Name))
		}
	}

	return foundErrors
}

// GenerateCatalogDOT generates a DOT file for the resources in catalog
func (c *Catalog) GenerateCatalogDOT(w io.Writer) error {
	g, err := c.resourceGraph()
	if err != nil {
		return err
	}
	g.GenerateDOT("resources", w)

	// Try a topological sort of the graph
	// In case of circular dependencies in the graph
	// generate a DOT file for the remaining nodes in the graph,
	// which would give us the resources causing circular dependencies
	if nodes, err := g.Sort(); err == graph.ErrCircularDependency {
		circularGraph := graph.NewGraph()
		circularGraph.AddNode(nodes...)
		circularGraph.GenerateDOT("resources_circular", w)
	}

	return nil
}

// Len returns the number of unique resources found in catalog
func (c *Catalog) Len() int {
	resources, err := c.createResourceMap()
	if err != nil {
		return 0
	}

	return len(resources)
}

// Load creates a catalog from the provided module name
func Load(main, path string) (*Catalog, error) {
	c := NewCatalog()

	// Discover all modules from the provided module path
	registry, err := module.Discover(path)
	if _, ok := registry[main]; !ok {
		return c, fmt.Errorf("Module %s was not found in the module path", main)
	}

	// A map containing the discovered module names and the actually loaded modules
	moduleNames := make(map[string]*module.Module)
	for n, p := range registry {
		f, err := os.Open(p)
		if err != nil {
			return c, err
		}

		m, err := module.Load(n, f)
		if err != nil {
			return c, err
		}
		moduleNames[n] = m

		f.Close()
	}

	// A map containing the modules as graph nodes
	// The graph is used to determine if we have
	// circular module imports and also to provide the
	// proper ordering of loading modules after a
	// topological sort of the graph nodes
	nodes := make(map[string]*graph.Node)
	for n := range moduleNames {
		node := graph.NewNode(n)
		nodes[n] = node
	}

	// Recursively find all imports that the main module has and
	// resolve the dependency graph
	g := graph.NewGraph()
	var createModuleGraph func(m *module.Module) error
	createModuleGraph = func(m *module.Module) error {
		if _, ok := g.GetNode(m.Name); !ok {
			g.AddNode(nodes[m.Name])
		} else {
			return nil
		}

		for _, mi := range m.Imports {
			if _, ok := moduleNames[mi.Name]; !ok {
				return fmt.Errorf("Module %s imports %s, which is not in the module path", m.Name, mi.Name)
			}

			// Build the dependencies of imported modules as well
			createModuleGraph(moduleNames[mi.Name])

			// Finally connect the nodes in the graph
			g.AddEdge(nodes[m.Name], nodes[mi.Name])
		}

		return nil
	}

	//	Build the dependency graph of the module imports
	err = createModuleGraph(moduleNames[main])
	if err != nil {
		return c, err
	}

	// Topologically sort the graph
	// In case of an error it means we have a circular import
	sorted, err := g.Sort()
	if err != nil {
		return c, err
	}

	// Finally add the sorted modules to the catalog
	for _, node := range sorted {
		c.modules = append(c.modules, moduleNames[node.Name])
	}

	return c, nil
}

// MarshalJSON creates a stripped down version of the catalog in JSON,
// which contains all resources from the catalog and is suitable for
// clients to consume in order to create a single-module catalog from it.
func (c *Catalog) MarshalJSON() ([]byte, error) {
	resources := make([]resource.Resource, 0)
	for _, m := range c.modules {
		resources = append(resources, m.Resources...)
	}

	toJson := make(map[string][]resourceMap, 0)
	for _, r := range resources {
		item := resourceMap{
			r.ResourceName(): r,
		}
		toJson[r.ResourceType()] = append(toJson[r.ResourceType()], item)
	}

	return json.Marshal(toJson)
}

// UnmarshalJSON loads a catalog from JSON input
// The loaded catalog is a catalog with a single
// module named "main"
func (c *Catalog) UnmarshalJSON(input []byte) error {
	r := strings.NewReader(string(input))
	main, err := module.Load("main", r)
	if err != nil {
		return err
	}

	c.modules = append(c.modules, main)

	return nil
}
<|endoftext|>


<commit_before>// Copyright 2014, Bryan Matsuo. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// launch.go [created: Thu, 20 Mar 2014]

/*
Launch instances

the "launch" command can be used to spin up one or more new ec2 instances.

	oti launch -h

*/
package main

import (
	"code.google.com/p/go-uuid/uuid"
	"github.com/bmatsuo/oti/otisub"
	"github.com/bmatsuo/oti/otitag"
	"github.com/crowdmob/goamz/aws"
	awsec2 "github.com/crowdmob/goamz/ec2"

	"flag"
	"fmt"
	"strconv"
	"strings"
)

var launch = otisub.Register("launch", func(args []string) {
	fs := otisub.FlagSet(flag.ExitOnError, "inspect", "imagename [directive ...] ...")
	sessionType := fs.String("s", "launch", "session type for management purposes")
	waitPending := fs.Bool("w", false, "wait while instances are 'pending'")
	fs.Parse(args)
	args = fs.Args()

	umfts, err := ParseUserLaunchManifest(args)
	if err != nil {
		Log.Fatal(err)
	}

	if len(umfts) == 0 {
		Log.Fatal("no manifests")
	}

	awsregion := aws.USEast
	awsauth, err := Config.AwsAuth()
	if err != nil {
		Log.Fatalln("error reading aws credentials: ", err)
	}

	ec2 := awsec2.New(awsauth, awsregion)

	for _, mft := range ManifestsNeedingImageLookup(umfts) { // mft points into mfts
		images, err := LookupImages(ec2, mft.Name)
		if err != nil {
			Log.Fatal("error locating image ids: ", err)
		}
		if len(images) > 0 {
			Log.Fatal("ambigous results: %v", images)
		}
		mft.Ec2ImageId = images[0].Id
	}

	if DEBUG {
		for _, m := range umfts {
			Log.Printf("%#v", m)
		}
	}

	sessionId, err := NewSessionId(*sessionType)
	if err != nil {
		Log.Fatal(err)
	}

	Log.Println("session id: ", sessionId)
	fmt.Println(sessionId) // to stdout

	// TODO find images based on manifest name

	mfts, err := BuildSystemLaunchManifests(ec2, sessionId, umfts)

	if DEBUG {
		for _, m := range mfts {
			Log.Printf("%#v", m)
		}
	}

	var haserrors bool
	for _, m := range mfts {
		runopts := &awsec2.RunInstancesOptions{
			ImageId:        m.Ec2.ImageId,
			MinCount:       m.Min,
			MaxCount:       m.Max,
			KeyName:        m.Ec2.KeyName,
			InstanceType:   m.Ec2.InstanceType,
			SecurityGroups: m.Ec2.SecurityGroups,
		}
		resp, err := ec2.RunInstances(runopts)
		if err != nil {
			haserrors = true
			Log.Printf("error running %q: %v", m.Name, err)
			continue
		}

		var instanceIds []string
		for _, inst := range resp.Instances {
			instanceIds = append(instanceIds, inst.InstanceId)
		}

		if DEBUG {
			Log.Printf("started instances: %v", instanceIds)
		}

		_, err = ec2.CreateTags(instanceIds, []awsec2.Tag{
			{Config.Ec2Tag(otitag.SessionId), string(m.SessionId)},
		})
		if err != nil {
			haserrors = true
			Log.Printf("error tagging instances: %v", err)
		}
	}
	if haserrors {
		Log.Fatal()
	}

	// wait for instances to boot
	if *waitPending {
		Log.Fatal("waiting not implemented")
	}
})

func BuildSystemLaunchManifests(ec2 *awsec2.EC2, sessionId SessionId, umfts []ULM) ([]LaunchManifest, error) {
	mfts := make([]LaunchManifest, len(umfts))
	secgroups, err := LookupSecurityGroups(ec2, umfts)
	if err != nil {
		return nil, fmt.Errorf("error locating up security groups: %v", err)
	}

	for i := range umfts {
		mfts[i].SessionId = sessionId
		mfts[i].Name = umfts[i].Name
		mfts[i].Min = umfts[i].Min
		mfts[i].Max = umfts[i].Max
		mfts[i].Ec2.InstanceType = umfts[i].Ec2InstanceType
		mfts[i].Ec2.ImageId = umfts[i].Ec2ImageId
		mfts[i].Ec2.KeyName = umfts[i].Ec2KeyName
		for _, group := range umfts[i].Ec2SecGroups {
			found := false
			for _, info := range secgroups {
				if info.Id == group {
					mfts[i].Ec2.SecurityGroups = append(mfts[i].Ec2.SecurityGroups, info.SecurityGroup)
					found = true
				} else if info.Name == group {
					mfts[i].Ec2.SecurityGroups = append(mfts[i].Ec2.SecurityGroups, info.SecurityGroup)
					found = true
				}
			}
			if !found {
				return nil, fmt.Errorf("unknown security group: %q", group)
			}
		}
	}

	return mfts, nil
}

func LookupSecurityGroups(ec2 *awsec2.EC2, mfts []ULM) ([]awsec2.SecurityGroupInfo, error) {
	var groups []awsec2.SecurityGroup
	for i := range mfts {
		for _, group := range mfts[i].Ec2SecGroups {
			if strings.HasPrefix(group, "sg-") {
				groups = append(groups, awsec2.SecurityGroup{Id: group})
			} else {
				groups = append(groups, awsec2.SecurityGroup{Name: group})
			}
		}
	}

	resp, err := ec2.SecurityGroups(groups, nil)
	if err != nil {
		return nil, err
	}

	return resp.Groups, nil
}

func ManifestsNeedingImageLookup(ulms []ULM) []*ULM {
	var _ulms []*ULM
	for i := range ulms {
		if ulms[i].Ec2ImageId == "" {
			_ulms = append(_ulms, &ulms[i])
		}
	}
	return _ulms
}

// use the packer config to locate images on ec2.
func LookupImages(ec2 *awsec2.EC2, name string) ([]awsec2.Image, error) {
	return nil, fmt.Errorf("unimplemented")
}

// User Launch Manifest -- information read from the command line
type ULM struct {
	Name            string   // OTI name that can be used to filter images
	Ec2ImageId      string   // AWS EC2 image id.
	Ec2InstanceType string   // AWS EC2 instance type.
	Ec2KeyName      string   // AWS EC2 key pair name
	Ec2SecGroups    []string // Security groups to assign the instances
	Min, Max        int      // may not be empty
}

type ArgumentError struct {
	i   int
	err error
}

func (err ArgumentError) Error() string {
	return fmt.Sprintf("argument %d: %v", err.i, err.err)
}

var ErrEndOfArgs = ArgumentError{-1, fmt.Errorf("no more arguments")}

// parses a set launch manifest. manifests have the form
//	name [ flag[=val] ... ] -- ...
// for reference use the following list of flags and the default values
//	flag      alias  default
//	min              1
//	max              1
//	ec2type          "t1.micro"
//	ami              ""
//	keyname          ""
//	secgroup         ""
func ParseUserLaunchManifest(args []string) ([]ULM, error) {
	ulms := make([]ULM, 0, len(args))
	sepseq := "--"

	parseUlm := func(args []string) (ulm ULM, rest []string, err error) {
		rest = args

		if len(rest) == 0 {
			return ULM{}, nil, ErrEndOfArgs
		}

		if rest[0] == sepseq {
			err := fmt.Errorf("unexpected separator sequence %v", sepseq)
			return ULM{}, nil, err
		}

		ulm.Name, rest = args[0], rest[1:]

		// set defaults
		ulm.Min, ulm.Max = 1, 1
		ulm.Ec2InstanceType = "t1.micro"

		retErr := func(err error) (ULM, []string, error) {
			return ULM{}, nil, err
		}
		ulmErr := func(err error) error { return fmt.Errorf("%v %v", ulm.Name, err) }
		ulmFlagErr := func(key string, err error) error {
			return ulmErr(fmt.Errorf("invalid flag %q: %v", key, err))
		}

		flags := make(map[string][]string)
		for len(rest) > 0 && rest[0] != sepseq {
			var head string
			head, rest = rest[0], rest[1:]
			key, value := head, ""

			pair := strings.SplitN(key, "=", 2)
			if len(pair) == 2 {
				key, value = pair[0], pair[1]
			}

			switch key {
			case "min", "max", "secgroup", "ami", "keyname", "ec2type":
			default:
				err := fmt.Errorf("unexpected flag %v", key)
				return retErr(ulmErr(err))
			}

			flags[key] = append(flags[key], value)
		}

		for k, vs := range flags {
			var err error
			numvs := len(vs)
			switch k {
			case "min":
				if numvs > 1 {
					err = fmt.Errorf("specified multiple times")
				} else {
					ulm.Min, err = strconv.Atoi(vs[0])
				}
			case "max":
				if numvs > 1 {
					err = fmt.Errorf("specified multiple times")
				} else {
					ulm.Max, err = strconv.Atoi(vs[0])
				}
			case "secgroup":
				ulm.Ec2SecGroups = vs
			case "ec2type":
				if numvs > 1 {
					err = fmt.Errorf("specified multiple times")
				} else {
					ulm.Ec2InstanceType = vs[0]
				}
			case "ami":
				if numvs > 1 {
					err = fmt.Errorf("specified multiple times")
				} else {
					ulm.Ec2ImageId = vs[0]
				}
			case "keyname":
				if numvs > 1 {
					err = fmt.Errorf("specified multiple times")
				} else {
					ulm.Ec2KeyName = vs[0]
				}
			}
			if err != nil {
				return retErr(ulmFlagErr(k, err))
			}
		}

		if len(ulm.Ec2SecGroups) == 0 {
			return retErr(ulmFlagErr("secgroup", fmt.Errorf("required for now")))
		}

		if ulm.Ec2ImageId == "" {
			return retErr(ulmFlagErr("ami", fmt.Errorf("required for now")))
		}

		if ulm.Min > ulm.Max {
			return retErr(ulmErr(err))
		}

		if len(rest) > 0 && rest[0] == sepseq {
			return ulm, rest[1:], nil
		} else {
			return ulm, nil, nil
		}
	}

	for len(args) > 0 {
		var ulm ULM
		var err error
		ulm, args, err = parseUlm(args)
		if err != nil {
			return nil, err
		}
		ulms = append(ulms, ulm)
	}

	return ulms, nil
}

type LaunchManifest struct {
	Name      string    // configured by the user
	Min, Max  int       // configured by the user
	SessionId SessionId // generated at runtime
	Ec2       struct {
		ImageId        string                 // located AWS image id
		InstanceType   string                 // configured by the user
		KeyName        string                 // configured by the user or generated at run-time
		SecurityGroups []awsec2.SecurityGroup // configured by the user or created at runtime
	}
}

type SessionId string

func NewSessionId(sessiontype string) (SessionId, error) {
	if strings.Contains(sessiontype, ":") {
		return "", fmt.Errorf("session type cannot contain ':'")
	}
	if sessiontype == "" {
		sessiontype = "session"
	}
	sid := SessionId(fmt.Sprintf("%s:%v", sessiontype, uuid.New()))
	return sid, nil
}

func (sid SessionId) Type() string {
	return strings.SplitN(string(sid), ":", 2)[0]
}
<commit_msg>clean<commit_after>// Copyright 2014, Bryan Matsuo. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// launch.go [created: Thu, 20 Mar 2014]

/*
Launch instances

the "launch" command can be used to spin up one or more new ec2 instances.

	oti launch -h

*/
package main

import (
	"code.google.com/p/go-uuid/uuid"
	"github.com/bmatsuo/oti/otisub"
	"github.com/bmatsuo/oti/otitag"
	"github.com/crowdmob/goamz/aws"
	awsec2 "github.com/crowdmob/goamz/ec2"

	"flag"
	"fmt"
	"strconv"
	"strings"
)

var launch = otisub.Register("launch", func(args []string) {
	fs := otisub.FlagSet(flag.ExitOnError, "inspect", "imagename [directive ...] ...")
	sessionType := fs.String("s", "launch", "session type for management purposes")
	keyname := fs.String("k", "", "default key pair used to run instances")
	waitPending := fs.Bool("w", false, "wait while instances are 'pending'")
	fs.Parse(args)
	args = fs.Args()

	umfts, err := ParseUserLaunchManifest(args)
	if err != nil {
		Log.Fatal(err)
	}

	if len(umfts) == 0 {
		Log.Fatal("no manifests")
	}

	awsregion := aws.USEast
	awsauth, err := Config.AwsAuth()
	if err != nil {
		Log.Fatalln("error reading aws credentials: ", err)
	}

	ec2 := awsec2.New(awsauth, awsregion)

	// find images based on manifest names (if no image is explicitly specified)
	for _, mft := range ManifestsNeedingImageLookup(umfts) { // mft points into mfts
		images, err := LookupImages(ec2, mft.Name)
		if err != nil {
			Log.Fatal("error locating image ids: ", err)
		}
		if len(images) > 0 {
			Log.Fatal("ambigous results: %v", images)
		}
		mft.Ec2ImageId = images[0].Id
	}

	if DEBUG {
		for _, m := range umfts {
			Log.Printf("%#v", m)
		}
	}

	sessionId, err := NewSessionId(*sessionType)
	if err != nil {
		Log.Fatal(err)
	}

	Log.Println("session id: ", sessionId)
	fmt.Println(sessionId) // to stdout

	mfts, err := BuildSystemLaunchManifests(ec2, sessionId, *keyname, umfts)
	if err != nil {
		Log.Fatalln(err)
	}

	if DEBUG {
		for _, m := range mfts {
			Log.Printf("%#v", m)
		}
	}

	var haserrors bool
	for _, m := range mfts {
		runopts := &awsec2.RunInstancesOptions{
			ImageId:        m.Ec2.ImageId,
			MinCount:       m.Min,
			MaxCount:       m.Max,
			KeyName:        m.Ec2.KeyName,
			InstanceType:   m.Ec2.InstanceType,
			SecurityGroups: m.Ec2.SecurityGroups,
		}
		resp, err := ec2.RunInstances(runopts)
		if err != nil {
			haserrors = true
			Log.Printf("error running %q: %v", m.Name, err)
			continue
		}

		var instanceIds []string
		for _, inst := range resp.Instances {
			instanceIds = append(instanceIds, inst.InstanceId)
		}

		if DEBUG {
			Log.Printf("started instances: %v", instanceIds)
		}

		_, err = ec2.CreateTags(instanceIds, []awsec2.Tag{
			{Config.Ec2Tag(otitag.SessionId), string(m.SessionId)},
		})
		if err != nil {
			haserrors = true
			Log.Printf("error tagging instances: %v", err)
		}
	}
	if haserrors {
		Log.Fatal()
	}

	// wait for instances to boot
	if *waitPending {
		Log.Fatal("waiting not implemented")
	}
})

// create LaunchManifests from the given ULMs. the manifests are given the
// provided session id and, if the ULM does not specify a ec2 key name, the
// provided keyname as well.
func BuildSystemLaunchManifests(ec2 *awsec2.EC2, sessionId SessionId, keyname string, umfts []ULM) ([]LaunchManifest, error) {
	mfts := make([]LaunchManifest, len(umfts))

	// get real security groups.
	secgroups, err := LookupSecurityGroups(ec2, umfts)
	if err != nil {
		return nil, fmt.Errorf("error locating up security groups: %v", err)
	}

	// get key pairs TODO

	// build each LaunchManifest
	for i := range umfts {
		m := &mfts[i]
		um := &umfts[i]
		m.SessionId = sessionId
		m.Name = um.Name
		m.Min = um.Min
		m.Max = um.Max
		m.Ec2.InstanceType = um.Ec2InstanceType
		m.Ec2.ImageId = um.Ec2ImageId
		m.Ec2.KeyName = um.Ec2KeyName
		if m.Ec2.KeyName == "" {
			m.Ec2.KeyName = keyname
		}
		for _, group := range um.Ec2SecGroups {
			found := false
			for _, info := range secgroups {
				if info.Id == group {
					m.Ec2.SecurityGroups = append(m.Ec2.SecurityGroups, info.SecurityGroup)
					found = true
				} else if info.Name == group {
					m.Ec2.SecurityGroups = append(m.Ec2.SecurityGroups, info.SecurityGroup)
					found = true
				}
			}
			if !found {
				return nil, fmt.Errorf("unknown security group: %q", group)
			}
		}
	}

	return mfts, nil
}

func LookupSecurityGroups(ec2 *awsec2.EC2, mfts []ULM) ([]awsec2.SecurityGroupInfo, error) {
	var groups []awsec2.SecurityGroup
	for i := range mfts {
		for _, group := range mfts[i].Ec2SecGroups {
			if strings.HasPrefix(group, "sg-") {
				groups = append(groups, awsec2.SecurityGroup{Id: group})
			} else {
				groups = append(groups, awsec2.SecurityGroup{Name: group})
			}
		}
	}

	resp, err := ec2.SecurityGroups(groups, nil)
	if err != nil {
		return nil, err
	}

	return resp.Groups, nil
}

func ManifestsNeedingImageLookup(ulms []ULM) []*ULM {
	var _ulms []*ULM
	for i := range ulms {
		if ulms[i].Ec2ImageId == "" {
			_ulms = append(_ulms, &ulms[i])
		}
	}
	return _ulms
}

// use the packer config to locate images on ec2.
func LookupImages(ec2 *awsec2.EC2, name string) ([]awsec2.Image, error) {
	return nil, fmt.Errorf("unimplemented")
}

// User Launch Manifest -- information read from the command line
type ULM struct {
	Name            string   // OTI name that can be used to filter images
	Ec2ImageId      string   // AWS EC2 image id.
	Ec2InstanceType string   // AWS EC2 instance type.
	Ec2KeyName      string   // AWS EC2 key pair name
	Ec2SecGroups    []string // Security groups to assign the instances
	Min, Max        int      // may not be empty
}

type ArgumentError struct {
	i   int
	err error
}

func (err ArgumentError) Error() string {
	return fmt.Sprintf("argument %d: %v", err.i, err.err)
}

var ErrEndOfArgs = ArgumentError{-1, fmt.Errorf("no more arguments")}

// parses a set launch manifest. manifests have the form
//	name [ flag[=val] ... ] -- ...
// for reference use the following list of flags and the default values
//	flag      alias  default
//	min              1
//	max              1
//	ec2type          "t1.micro"
//	ami              ""
//	keyname          ""
//	secgroup         ""
func ParseUserLaunchManifest(args []string) ([]ULM, error) {
	ulms := make([]ULM, 0, len(args))
	sepseq := "--"

	parseUlm := func(args []string) (ulm ULM, rest []string, err error) {
		rest = args

		if len(rest) == 0 {
			return ULM{}, nil, ErrEndOfArgs
		}

		if rest[0] == sepseq {
			err := fmt.Errorf("unexpected separator sequence %v", sepseq)
			return ULM{}, nil, err
		}

		ulm.Name, rest = args[0], rest[1:]

		// set defaults
		ulm.Min, ulm.Max = 1, 1
		ulm.Ec2InstanceType = "t1.micro"

		retErr := func(err error) (ULM, []string, error) {
			return ULM{}, nil, err
		}
		ulmErr := func(err error) error { return fmt.Errorf("%v %v", ulm.Name, err) }
		ulmFlagErr := func(key string, err error) error {
			return ulmErr(fmt.Errorf("invalid flag %q: %v", key, err))
		}

		flags := make(map[string][]string)
		for len(rest) > 0 && rest[0] != sepseq {
			var head string
			head, rest = rest[0], rest[1:]
			key, value := head, ""

			pair := strings.SplitN(key, "=", 2)
			if len(pair) == 2 {
				key, value = pair[0], pair[1]
			}

			switch key {
			case "min", "max", "secgroup", "ami", "keyname", "ec2type":
			default:
				err := fmt.Errorf("unexpected flag %v", key)
				return retErr(ulmErr(err))
			}

			flags[key] = append(flags[key], value)
		}

		for k, vs := range flags {
			var err error
			numvs := len(vs)
			switch k {
			case "min":
				if numvs > 1 {
					err = fmt.Errorf("specified multiple times")
				} else {
					ulm.Min, err = strconv.Atoi(vs[0])
				}
			case "max":
				if numvs > 1 {
					err = fmt.Errorf("specified multiple times")
				} else {
					ulm.Max, err = strconv.Atoi(vs[0])
				}
			case "secgroup":
				ulm.Ec2SecGroups = vs
			case "ec2type":
				if numvs > 1 {
					err = fmt.Errorf("specified multiple times")
				} else {
					ulm.Ec2InstanceType = vs[0]
				}
			case "ami":
				if numvs > 1 {
					err = fmt.Errorf("specified multiple times")
				} else {
					ulm.Ec2ImageId = vs[0]
				}
			case "keyname":
				if numvs > 1 {
					err = fmt.Errorf("specified multiple times")
				} else {
					ulm.Ec2KeyName = vs[0]
				}
			}
			if err != nil {
				return retErr(ulmFlagErr(k, err))
			}
		}

		if len(ulm.Ec2SecGroups) == 0 {
			return retErr(ulmFlagErr("secgroup", fmt.Errorf("required for now")))
		}

		if ulm.Ec2ImageId == "" {
			return retErr(ulmFlagErr("ami", fmt.Errorf("required for now")))
		}

		if ulm.Min > ulm.Max {
			return retErr(ulmErr(err))
		}

		if len(rest) > 0 && rest[0] == sepseq {
			return ulm, rest[1:], nil
		} else {
			return ulm, nil, nil
		}
	}

	for len(args) > 0 {
		var ulm ULM
		var err error
		ulm, args, err = parseUlm(args)
		if err != nil {
			return nil, err
		}
		ulms = append(ulms, ulm)
	}

	return ulms, nil
}

type LaunchManifest struct {
	Name      string    // configured by the user
	Min, Max  int       // configured by the user
	SessionId SessionId // generated at runtime
	Ec2       struct {
		ImageId        string                 // located AWS image id
		InstanceType   string                 // configured by the user
		KeyName        string                 // configured by the user or generated at run-time
		SecurityGroups []awsec2.SecurityGroup // configured by the user or created at runtime
	}
}

type SessionId string

func NewSessionId(sessiontype string) (SessionId, error) {
	if strings.Contains(sessiontype, ":") {
		return "", fmt.Errorf("session type cannot contain ':'")
	}
	if sessiontype == "" {
		sessiontype = "session"
	}
	sid := SessionId(fmt.Sprintf("%s:%v", sessiontype, uuid.New()))
	return sid, nil
}

func (sid SessionId) Type() string {
	return strings.SplitN(string(sid), ":", 2)[0]
}
<|endoftext|>


<commit_before>package maxminddb

import (
	"encoding/hex"
	"math/big"
	"os"
	"reflect"
	"strings"
	"testing"
)

func TestBool(t *testing.T) {
	bools := map[string]interface{}{
		"0007": false,
		"0107": true,
	}

	validateDecoding(t, bools)
}

func TestDouble(t *testing.T) {
	doubles := map[string]interface{}{
		"680000000000000000": 0.0,
		"683FE0000000000000": 0.5,
		"68400921FB54442EEA": 3.14159265359,
		"68405EC00000000000": 123.0,
		"6841D000000007F8F4": 1073741824.12457,
		"68BFE0000000000000": -0.5,
		"68C00921FB54442EEA": -3.14159265359,
		"68C1D000000007F8F4": -1073741824.12457,
	}
	validateDecoding(t, doubles)
}

func TestFloat(t *testing.T) {
	floats := map[string]interface{}{
		"040800000000": float32(0.0),
		"04083F800000": float32(1.0),
		"04083F8CCCCD": float32(1.1),
		"04084048F5C3": float32(3.14),
		"0408461C3FF6": float32(9999.99),
		"0408BF800000": float32(-1.0),
		"0408BF8CCCCD": float32(-1.1),
		"0408C048F5C3": -float32(3.14),
		"0408C61C3FF6": float32(-9999.99),
	}
	validateDecoding(t, floats)
}

func TestInt32(t *testing.T) {
	int32 := map[string]interface{}{
		"0001":         0,
		"0401ffffffff": -1,
		"0101ff":       255,
		"0401ffffff01": -255,
		"020101f4":     500,
		"0401fffffe0c": -500,
		"0201ffff":     65535,
		"0401ffff0001": -65535,
		"0301ffffff":   16777215,
		"0401ff000001": -16777215,
		"04017fffffff": 2147483647,
		"040180000001": -2147483647,
	}
	validateDecoding(t, int32)
}

func TestMap(t *testing.T) {
	maps := map[string]interface{}{
		"e0":                                         map[string]interface{}{},
		"e142656e43466f6f":                           map[string]interface{}{"en": "Foo"},
		"e242656e43466f6f427a6843e4baba":             map[string]interface{}{"en": "Foo", "zh": "人"},
		"e1446e616d65e242656e43466f6f427a6843e4baba": map[string]interface{}{"name": map[string]interface{}{"en": "Foo", "zh": "人"}},
		"e1496c616e677561676573020442656e427a68":     map[string]interface{}{"languages": []interface{}{"en", "zh"}},
	}
	validateDecoding(t, maps)
}

func TestSlice(t *testing.T) {
	slice := map[string]interface{}{
		"0004":                 []interface{}{},
		"010443466f6f":         []interface{}{"Foo"},
		"020443466f6f43e4baba": []interface{}{"Foo", "人"},
	}
	validateDecoding(t, slice)
}

var testStrings = makeTestStrings()

func makeTestStrings() map[string]interface{} {
	str := map[string]interface{}{
		"40":       "",
		"4131":     "1",
		"43E4BABA": "人",
		"5b313233343536373839303132333435363738393031323334353637":         "123456789012345678901234567",
		"5c31323334353637383930313233343536373839303132333435363738":       "1234567890123456789012345678",
		"5d003132333435363738393031323334353637383930313233343536373839":   "12345678901234567890123456789",
		"5d01313233343536373839303132333435363738393031323334353637383930": "123456789012345678901234567890",
	}

	for k, v := range map[string]int{"5e00d7": 500, "5e06b3": 2000, "5f001053": 70000} {
		key := k + strings.Repeat("78", v)
		str[key] = strings.Repeat("x", v)
	}

	return str
}

func TestString(t *testing.T) {
	validateDecoding(t, testStrings)
}

func TestByte(t *testing.T) {
	b := make(map[string]interface{})
	for key, val := range testStrings {
		oldCtrl, _ := hex.DecodeString(key[0:2])
		newCtrl := []byte{oldCtrl[0] ^ 0xc0}
		key = strings.Replace(key, hex.EncodeToString(oldCtrl), hex.EncodeToString(newCtrl), 1)
		b[key] = []byte(val.(string))
	}

	validateDecoding(t, b)
}

func TestUint16(t *testing.T) {
	uint16 := map[string]interface{}{
		"a0":     uint64(0),
		"a1ff":   uint64(255),
		"a201f4": uint64(500),
		"a22a78": uint64(10872),
		"a2ffff": uint64(65535),
	}
	validateDecoding(t, uint16)
}

func TestUint32(t *testing.T) {
	uint32 := map[string]interface{}{
		"c0":         uint64(0),
		"c1ff":       uint64(255),
		"c201f4":     uint64(500),
		"c22a78":     uint64(10872),
		"c2ffff":     uint64(65535),
		"c3ffffff":   uint64(16777215),
		"c4ffffffff": uint64(4294967295),
	}
	validateDecoding(t, uint32)
}

func TestUint64(t *testing.T) {
	ctrlByte := "02"
	bits := uint64(64)

	uints := map[string]interface{}{
		"00" + ctrlByte:          uint64(0),
		"02" + ctrlByte + "01f4": uint64(500),
		"02" + ctrlByte + "2a78": uint64(10872),
	}
	for i := uint64(0); i <= bits/8; i++ {
		expected := uint64((1 << (8 * i)) - 1)

		input := hex.EncodeToString([]byte{byte(i)}) + ctrlByte + strings.Repeat("ff", int(i))
		uints[input] = expected
	}

	validateDecoding(t, uints)
}

// Dedup with above somehow
func TestUint128(t *testing.T) {
	ctrlByte := "03"
	bits := uint(128)

	uints := map[string]interface{}{
		"00" + ctrlByte:          big.NewInt(0),
		"02" + ctrlByte + "01f4": big.NewInt(500),
		"02" + ctrlByte + "2a78": big.NewInt(10872),
	}
	for i := uint(1); i <= bits/8; i++ {
		expected := powBigInt(big.NewInt(2), 8*i)
		expected = expected.Sub(expected, big.NewInt(1))
		input := hex.EncodeToString([]byte{byte(i)}) + ctrlByte + strings.Repeat("ff", int(i))

		uints[input] = expected
	}

	validateDecoding(t, uints)
}

// No pow or bit shifting for big int, apparently :-(
// This is _not_ meant to be a comprehensive power function
func powBigInt(bi *big.Int, pow uint) *big.Int {
	newInt := big.NewInt(1)
	for i := uint(0); i < pow; i++ {
		newInt.Mul(newInt, bi)
	}
	return newInt
}

func validateDecoding(t *testing.T, tests map[string]interface{}) {
	for inputStr, expected := range tests {
		inputBytes, _ := hex.DecodeString(inputStr)
		d := decoder{inputBytes, 0}

		var result interface{}
		_, err := d.decode(0, reflect.ValueOf(&result))
		if err != nil {
			t.Error(err)
		}
		if !reflect.DeepEqual(result, expected) {
			// A big case statement would produce nicer errors
			t.Errorf("Output was incorrect: %s  %s", inputStr, expected)
		}
	}
}

func TestPointers(t *testing.T) {
	mapFile, err := os.Open("test-data/test-data/maps-with-pointers.raw")
	if err != nil {
		t.Error(err)
	}
    defer mapFile.Close()
	stats, err := mapFile.Stat()
	if err != nil {
		t.Error(err)
	}

	mmap, err := mmap(int(mapFile.Fd()), int(stats.Size()))
	if err != nil {
		t.Error(err)
	}
    defer munmap(mmap)
	d := decoder{mmap, 0}

	expected := map[uint]map[string]string{
		0:  map[string]string{"long_key": "long_value1"},
		22: map[string]string{"long_key": "long_value2"},
		37: map[string]string{"long_key2": "long_value1"},
		50: map[string]string{"long_key2": "long_value2"},
		55: map[string]string{"long_key": "long_value1"},
		57: map[string]string{"long_key2": "long_value2"},
	}

	for offset, expectedValue := range expected {
		var actual map[string]string
		_, err := d.decode(offset, reflect.ValueOf(&actual))
		if err != nil {
			t.Error(err)
		}
		if !reflect.DeepEqual(actual, expectedValue) {
			t.Errorf("Decode for pointer at %d failed", offset)
		}
	}

}
<commit_msg>Minor code cleanup<commit_after>package maxminddb

import (
	"encoding/hex"
	"math/big"
	"os"
	"reflect"
	"strings"
	"testing"
)

func TestBool(t *testing.T) {
	bools := map[string]interface{}{
		"0007": false,
		"0107": true,
	}

	validateDecoding(t, bools)
}

func TestDouble(t *testing.T) {
	doubles := map[string]interface{}{
		"680000000000000000": 0.0,
		"683FE0000000000000": 0.5,
		"68400921FB54442EEA": 3.14159265359,
		"68405EC00000000000": 123.0,
		"6841D000000007F8F4": 1073741824.12457,
		"68BFE0000000000000": -0.5,
		"68C00921FB54442EEA": -3.14159265359,
		"68C1D000000007F8F4": -1073741824.12457,
	}
	validateDecoding(t, doubles)
}

func TestFloat(t *testing.T) {
	floats := map[string]interface{}{
		"040800000000": float32(0.0),
		"04083F800000": float32(1.0),
		"04083F8CCCCD": float32(1.1),
		"04084048F5C3": float32(3.14),
		"0408461C3FF6": float32(9999.99),
		"0408BF800000": float32(-1.0),
		"0408BF8CCCCD": float32(-1.1),
		"0408C048F5C3": -float32(3.14),
		"0408C61C3FF6": float32(-9999.99),
	}
	validateDecoding(t, floats)
}

func TestInt32(t *testing.T) {
	int32 := map[string]interface{}{
		"0001":         0,
		"0401ffffffff": -1,
		"0101ff":       255,
		"0401ffffff01": -255,
		"020101f4":     500,
		"0401fffffe0c": -500,
		"0201ffff":     65535,
		"0401ffff0001": -65535,
		"0301ffffff":   16777215,
		"0401ff000001": -16777215,
		"04017fffffff": 2147483647,
		"040180000001": -2147483647,
	}
	validateDecoding(t, int32)
}

func TestMap(t *testing.T) {
	maps := map[string]interface{}{
		"e0":                                         map[string]interface{}{},
		"e142656e43466f6f":                           map[string]interface{}{"en": "Foo"},
		"e242656e43466f6f427a6843e4baba":             map[string]interface{}{"en": "Foo", "zh": "人"},
		"e1446e616d65e242656e43466f6f427a6843e4baba": map[string]interface{}{"name": map[string]interface{}{"en": "Foo", "zh": "人"}},
		"e1496c616e677561676573020442656e427a68":     map[string]interface{}{"languages": []interface{}{"en", "zh"}},
	}
	validateDecoding(t, maps)
}

func TestSlice(t *testing.T) {
	slice := map[string]interface{}{
		"0004":                 []interface{}{},
		"010443466f6f":         []interface{}{"Foo"},
		"020443466f6f43e4baba": []interface{}{"Foo", "人"},
	}
	validateDecoding(t, slice)
}

var testStrings = makeTestStrings()

func makeTestStrings() map[string]interface{} {
	str := map[string]interface{}{
		"40":       "",
		"4131":     "1",
		"43E4BABA": "人",
		"5b313233343536373839303132333435363738393031323334353637":         "123456789012345678901234567",
		"5c31323334353637383930313233343536373839303132333435363738":       "1234567890123456789012345678",
		"5d003132333435363738393031323334353637383930313233343536373839":   "12345678901234567890123456789",
		"5d01313233343536373839303132333435363738393031323334353637383930": "123456789012345678901234567890",
	}

	for k, v := range map[string]int{"5e00d7": 500, "5e06b3": 2000, "5f001053": 70000} {
		key := k + strings.Repeat("78", v)
		str[key] = strings.Repeat("x", v)
	}

	return str
}

func TestString(t *testing.T) {
	validateDecoding(t, testStrings)
}

func TestByte(t *testing.T) {
	b := make(map[string]interface{})
	for key, val := range testStrings {
		oldCtrl, _ := hex.DecodeString(key[0:2])
		newCtrl := []byte{oldCtrl[0] ^ 0xc0}
		key = strings.Replace(key, hex.EncodeToString(oldCtrl), hex.EncodeToString(newCtrl), 1)
		b[key] = []byte(val.(string))
	}

	validateDecoding(t, b)
}

func TestUint16(t *testing.T) {
	uint16 := map[string]interface{}{
		"a0":     uint64(0),
		"a1ff":   uint64(255),
		"a201f4": uint64(500),
		"a22a78": uint64(10872),
		"a2ffff": uint64(65535),
	}
	validateDecoding(t, uint16)
}

func TestUint32(t *testing.T) {
	uint32 := map[string]interface{}{
		"c0":         uint64(0),
		"c1ff":       uint64(255),
		"c201f4":     uint64(500),
		"c22a78":     uint64(10872),
		"c2ffff":     uint64(65535),
		"c3ffffff":   uint64(16777215),
		"c4ffffffff": uint64(4294967295),
	}
	validateDecoding(t, uint32)
}

func TestUint64(t *testing.T) {
	ctrlByte := "02"
	bits := uint64(64)

	uints := map[string]interface{}{
		"00" + ctrlByte:          uint64(0),
		"02" + ctrlByte + "01f4": uint64(500),
		"02" + ctrlByte + "2a78": uint64(10872),
	}
	for i := uint64(0); i <= bits/8; i++ {
		expected := uint64((1 << (8 * i)) - 1)

		input := hex.EncodeToString([]byte{byte(i)}) + ctrlByte + strings.Repeat("ff", int(i))
		uints[input] = expected
	}

	validateDecoding(t, uints)
}

// Dedup with above somehow
func TestUint128(t *testing.T) {
	ctrlByte := "03"
	bits := uint(128)

	uints := map[string]interface{}{
		"00" + ctrlByte:          big.NewInt(0),
		"02" + ctrlByte + "01f4": big.NewInt(500),
		"02" + ctrlByte + "2a78": big.NewInt(10872),
	}
	for i := uint(1); i <= bits/8; i++ {
		expected := powBigInt(big.NewInt(2), 8*i)
		expected = expected.Sub(expected, big.NewInt(1))
		input := hex.EncodeToString([]byte{byte(i)}) + ctrlByte + strings.Repeat("ff", int(i))

		uints[input] = expected
	}

	validateDecoding(t, uints)
}

// No pow or bit shifting for big int, apparently :-(
// This is _not_ meant to be a comprehensive power function
func powBigInt(bi *big.Int, pow uint) *big.Int {
	newInt := big.NewInt(1)
	for i := uint(0); i < pow; i++ {
		newInt.Mul(newInt, bi)
	}
	return newInt
}

func validateDecoding(t *testing.T, tests map[string]interface{}) {
	for inputStr, expected := range tests {
		inputBytes, _ := hex.DecodeString(inputStr)
		d := decoder{inputBytes, 0}

		var result interface{}
		_, err := d.decode(0, reflect.ValueOf(&result))
		if err != nil {
			t.Error(err)
		}
		if !reflect.DeepEqual(result, expected) {
			// A big case statement would produce nicer errors
			t.Errorf("Output was incorrect: %s  %s", inputStr, expected)
		}
	}
}

func TestPointers(t *testing.T) {
	mapFile, err := os.Open("test-data/test-data/maps-with-pointers.raw")
	if err != nil {
		t.Error(err)
	}
	defer mapFile.Close()
	stats, err := mapFile.Stat()
	if err != nil {
		t.Error(err)
	}

	mmap, err := mmap(int(mapFile.Fd()), int(stats.Size()))
	if err != nil {
		t.Error(err)
	}
	defer munmap(mmap)
	d := decoder{mmap, 0}

	expected := map[uint]map[string]string{
		0:  {"long_key": "long_value1"},
		22: {"long_key": "long_value2"},
		37: {"long_key2": "long_value1"},
		50: {"long_key2": "long_value2"},
		55: {"long_key": "long_value1"},
		57: {"long_key2": "long_value2"},
	}

	for offset, expectedValue := range expected {
		var actual map[string]string
		_, err := d.decode(offset, reflect.ValueOf(&actual))
		if err != nil {
			t.Error(err)
		}
		if !reflect.DeepEqual(actual, expectedValue) {
			t.Errorf("Decode for pointer at %d failed", offset)
		}
	}

}
<|endoftext|>


<commit_before>package version

// Version of Functions
var Version = "0.3.305"
<commit_msg>fnserver: 0.3.306 release [skip ci]<commit_after>package version

// Version of Functions
var Version = "0.3.306"
<|endoftext|>


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

/*

Package catalog provides a common catalog abstraction over all storage
engines, such as Couchbase server, cloud, mobile, file, 3rd-party
databases and storage engines, etc.

*/
package catalog

import (
	"github.com/couchbaselabs/dparval"
	"github.com/couchbaselabs/tuqtng/ast"
	"github.com/couchbaselabs/tuqtng/query"
)

// log channel for the catalog lifecycle
const CHANNEL = "CATALOG"

// Site represents a cluster or single-node server.
type Site interface {
	Id() string
	URL() string
	PoolIds() ([]string, query.Error)
	PoolNames() ([]string, query.Error)
	PoolById(id string) (Pool, query.Error)
	PoolByName(name string) (Pool, query.Error)
}

// Pool represents a logical authentication, query, and resource
// allocation boundary, as well as a grouping of buckets.
type Pool interface {
	SiteId() string
	Id() string
	Name() string
	BucketIds() ([]string, query.Error)
	BucketNames() ([]string, query.Error)
	BucketById(name string) (Bucket, query.Error)
	BucketByName(name string) (Bucket, query.Error)
}

// Bucket is a collection of key-value entries (typically
// key-document, but not always).
type Bucket interface {
	PoolId() string
	Id() string
	Name() string
	Count() (int64, query.Error)
	IndexIds() ([]string, query.Error)
	IndexNames() ([]string, query.Error)
	IndexById(id string) (Index, query.Error)
	IndexByName(name string) (Index, query.Error)
	IndexByPrimary() (PrimaryIndex, query.Error)
	Indexes() ([]Index, query.Error)
	Fetch(id string) (*dparval.Value, query.Error)
	BulkFetch([]string) (map[string]*dparval.Value, query.Error)
	Release()
	CreatePrimaryIndex() (PrimaryIndex, query.Error)
	CreateIndex(name string, key IndexKey, using IndexType) (Index, query.Error)
}

type IndexType string

const (
	PRIMARY IndexType = "primary"
	VIEW    IndexType = "view"
)

type IndexKey []ast.Expression

// Index is the base type for all indexes.
type Index interface {
	BucketId() string
	Id() string
	Name() string
	Type() IndexType
	Key() IndexKey
	Drop() query.Error // PrimaryIndexes cannot be dropped
}

// ScanIndex represents scanning indexes.
type ScanIndex interface {
	ScanEntries(ch dparval.ValueChannel, warnch, errch query.ErrorChannel)
}

// PrimaryIndex represents primary key indexes.
type PrimaryIndex interface {
	Index
	ScanIndex
}

type LookupValue []*dparval.Value

// ExistenceIndex represents existence indexes.
type ExistenceIndex interface {
	Check(value LookupValue, ch dparval.ValueChannel, warnch, errch query.ErrorChannel)
}

// LookupIndex represents lookup indexes.
type LookupIndex interface {
	ExistenceIndex
	Lookup(value LookupValue, ch dparval.ValueChannel, warnch, errch query.ErrorChannel)
}

// Direction represents ASC and DESC
// TODO: Is this needed?
type Direction int

const (
	ASC  Direction = 1
	DESC           = 2
)

// Inclusion controls how the boundaries values of a range are treated
type RangeInclusion int

const (
	Neither RangeInclusion = iota
	Low
	High
	Both
)

// RangeIndex represents range scan indexes.
type RangeIndex interface {
	Index
	ScanIndex
	LookupIndex
	Direction() Direction
	Statistics() (RangeStatistics, query.Error)
	ScanRange(low LookupValue, high LookupValue, RangeInclusion, ch dparval.ValueChannel, warnch, errch query.ErrorChannel)
}

// SearchIndex represents full text search indexes.
type SearchIndex interface {
	Index
	Search(value string, ch dparval.ValueChannel, warnch, errch query.ErrorChannel)
}

// RangeStatistics captures statistics for a range index.
type RangeStatistics interface {
	Count() (int64, query.Error)
	Min() (dparval.Value, query.Error)
	Max() (dparval.Value, query.Error)
	DistinctCount(int64, query.Error)
	Bins() ([]Bin, query.Error)
}

// Bin represents a range bin within IndexStatistics.
type Bin interface {
	Count() (int64, query.Error)
	Min() (dparval.Value, query.Error)
	Max() (dparval.Value, query.Error)
	DistinctCount(int64, query.Error)
}
<commit_msg>Fixed typo in ScanRange().<commit_after>//  Copyright (c) 2013 Couchbase, Inc.
//  Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
//  except in compliance with the License. You may obtain a copy of the License at
//    http://www.apache.org/licenses/LICENSE-2.0
//  Unless required by applicable law or agreed to in writing, software distributed under the
//  License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
//  either express or implied. See the License for the specific language governing permissions
//  and limitations under the License.

/*

Package catalog provides a common catalog abstraction over all storage
engines, such as Couchbase server, cloud, mobile, file, 3rd-party
databases and storage engines, etc.

*/
package catalog

import (
	"github.com/couchbaselabs/dparval"
	"github.com/couchbaselabs/tuqtng/ast"
	"github.com/couchbaselabs/tuqtng/query"
)

// log channel for the catalog lifecycle
const CHANNEL = "CATALOG"

// Site represents a cluster or single-node server.
type Site interface {
	Id() string
	URL() string
	PoolIds() ([]string, query.Error)
	PoolNames() ([]string, query.Error)
	PoolById(id string) (Pool, query.Error)
	PoolByName(name string) (Pool, query.Error)
}

// Pool represents a logical authentication, query, and resource
// allocation boundary, as well as a grouping of buckets.
type Pool interface {
	SiteId() string
	Id() string
	Name() string
	BucketIds() ([]string, query.Error)
	BucketNames() ([]string, query.Error)
	BucketById(name string) (Bucket, query.Error)
	BucketByName(name string) (Bucket, query.Error)
}

// Bucket is a collection of key-value entries (typically
// key-document, but not always).
type Bucket interface {
	PoolId() string
	Id() string
	Name() string
	Count() (int64, query.Error)
	IndexIds() ([]string, query.Error)
	IndexNames() ([]string, query.Error)
	IndexById(id string) (Index, query.Error)
	IndexByName(name string) (Index, query.Error)
	IndexByPrimary() (PrimaryIndex, query.Error)
	Indexes() ([]Index, query.Error)
	Fetch(id string) (*dparval.Value, query.Error)
	BulkFetch([]string) (map[string]*dparval.Value, query.Error)
	Release()
	CreatePrimaryIndex() (PrimaryIndex, query.Error)
	CreateIndex(name string, key IndexKey, using IndexType) (Index, query.Error)
}

type IndexType string

const (
	PRIMARY IndexType = "primary"
	VIEW    IndexType = "view"
)

type IndexKey []ast.Expression

// Index is the base type for all indexes.
type Index interface {
	BucketId() string
	Id() string
	Name() string
	Type() IndexType
	Key() IndexKey
	Drop() query.Error // PrimaryIndexes cannot be dropped
}

// ScanIndex represents scanning indexes.
type ScanIndex interface {
	ScanEntries(ch dparval.ValueChannel, warnch, errch query.ErrorChannel)
}

// PrimaryIndex represents primary key indexes.
type PrimaryIndex interface {
	Index
	ScanIndex
}

type LookupValue []*dparval.Value

// ExistenceIndex represents existence indexes.
type ExistenceIndex interface {
	Check(value LookupValue, ch dparval.ValueChannel, warnch, errch query.ErrorChannel)
}

// LookupIndex represents lookup indexes.
type LookupIndex interface {
	ExistenceIndex
	Lookup(value LookupValue, ch dparval.ValueChannel, warnch, errch query.ErrorChannel)
}

// Direction represents ASC and DESC
// TODO: Is this needed?
type Direction int

const (
	ASC  Direction = 1
	DESC           = 2
)

// Inclusion controls how the boundaries values of a range are treated
type RangeInclusion int

const (
	Neither RangeInclusion = iota
	Low
	High
	Both
)

// RangeIndex represents range scan indexes.
type RangeIndex interface {
	Index
	ScanIndex
	LookupIndex
	Direction() Direction
	Statistics() (RangeStatistics, query.Error)
	ScanRange(low LookupValue, high LookupValue, inclusion RangeInclusion, ch dparval.ValueChannel, warnch, errch query.ErrorChannel)
}

// SearchIndex represents full text search indexes.
type SearchIndex interface {
	Index
	Search(value string, ch dparval.ValueChannel, warnch, errch query.ErrorChannel)
}

// RangeStatistics captures statistics for a range index.
type RangeStatistics interface {
	Count() (int64, query.Error)
	Min() (dparval.Value, query.Error)
	Max() (dparval.Value, query.Error)
	DistinctCount(int64, query.Error)
	Bins() ([]Bin, query.Error)
}

// Bin represents a range bin within IndexStatistics.
type Bin interface {
	Count() (int64, query.Error)
	Min() (dparval.Value, query.Error)
	Max() (dparval.Value, query.Error)
	DistinctCount(int64, query.Error)
}
<|endoftext|>


<commit_before>package version

// Version of Functions
var Version = "0.3.677"
<commit_msg>fnserver: 0.3.678 release [skip ci]<commit_after>package version

// Version of Functions
var Version = "0.3.678"
<|endoftext|>