<commit_before>/* Copyright (c) 2014-2015 Jason Ish
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
 * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

package main

import (
	"encoding/json"
	"fmt"
	"log"
	"net/http"
	"net/http/httputil"
	"net/url"
	"os"

	"github.com/GeertJohan/go.rice"
	"github.com/jessevdk/go-flags"
	"github.com/gorilla/mux"
)

var buildDate string
var buildVersion string
var buildRev string

const DEFAULT_ELASTICSEARCH_URI string = "http://localhost:9200"

var opts struct {
	// We don't provide a default for this one so we can easily
	// detect if its been set or not.
	ElasticSearchUri   string `long:"elasticsearch" short:"e" description:"Elastic Search URI (default: http://localhost:9200)"`
	ElasticSearchIndex string `long:"index" short:"i" description:"Elastic Search Index (default: logstash-*)"`
	Port               string `long:"port" short:"p" default:"5636" description:"Port to bind to"`
	Host               string `long:"host" default:"0.0.0.0" description:"Host to bind to"`
	DevServerUri       string `long:"dev" description:"Frontend development server URI"`
	Version            bool   `long:"version" description:"Show version"`
	Config             string `long:"config" short:"c" description:"Configuration filename"`
	NoCheckCertificate bool `long:"no-check-certificate" short:"k" description:"Disable certificate check for Elastic Search"`
}

var config = &Config{}

type VersionResponse struct {
	Version  string
	Revision string
	Date     string
}

func VersionHandler(w http.ResponseWriter, r *http.Request) {
	response := VersionResponse{
		buildVersion,
		buildRev,
		buildDate,
	}
	w.Header().Set("Content-Type", "application/json; charset=UTF-8")
	json.NewEncoder(w).Encode(response)
}

func ConfigHandler(w http.ResponseWriter, r *http.Request) {
	configJson, err := config.toJSON()
	if err != nil {
		// Return failure.
		log.Println("error: ", err)
		return
	}
	w.Header().Set("Content-Type", "application/json; charset=UTF-8")
	w.Write(configJson)
}

func setupElasticSearchProxy(router *mux.Router) {
	if opts.ElasticSearchUri == "" {
		if os.Getenv("ELASTICSEARCH_URL") != "" {
			opts.ElasticSearchUri = os.Getenv("ELASTICSEARCH_URL")
		} else {
			opts.ElasticSearchUri = DEFAULT_ELASTICSEARCH_URI
		}
	}
	log.Printf("Elastic Search URI: %v", opts.ElasticSearchUri)
	esProxy, err := NewElasticSearchProxy(opts.ElasticSearchUri,
		"/elasticsearch", opts.NoCheckCertificate)
	if err != nil {
		log.Fatal(err)
	}
	router.PathPrefix("/elasticsearch").Handler(esProxy)
}

// Setup the handler for static files.
func setupStatic(router *mux.Router) {
	if len(opts.DevServerUri) > 0 {
		log.Printf("Proxying static files to development server %v.",
			opts.DevServerUri)
		devServerProxyUrl, err := url.Parse(opts.DevServerUri)
		if err != nil {
			log.Fatal(err)
		}
		devServerProxy :=
			httputil.NewSingleHostReverseProxy(devServerProxyUrl)
		router.PathPrefix("/").Handler(devServerProxy)

	} else {
		public := http.FileServer(
			rice.MustFindBox("./public").HTTPBox())
		router.PathPrefix("/").Handler(public)
	}
}

func main() {

	log.SetFlags(log.Lshortfile)

	_, err := flags.Parse(&opts)
	if err != nil {
		// flags.Parse should have already presented an error message.
		os.Exit(1)
	}

	if opts.Version {
		fmt.Printf("EveBox Version %s (rev %s) [%s]\n",
			buildVersion, buildRev, buildDate)
		os.Exit(0)
	}

	// If no configuration was provided, see if evebox.yaml exists
	// in the current directory.
	if opts.Config == "" {
		_, err = os.Stat("./evebox.yaml")
		if err == nil {
			opts.Config = "./evebox.yaml"
		}
	}
	if opts.Config != "" {
		log.Printf("Loading configuration file %s.\n", opts.Config)
		config, err = LoadConfig(opts.Config)
		if err != nil {
			log.Fatal(err)
		}
	}

	if opts.ElasticSearchIndex != "" {
		log.Printf("Using ElasticSearch Index %s.", opts.ElasticSearchIndex)
		config.ElasticSearchIndex = opts.ElasticSearchIndex
	}

	router := mux.NewRouter()

	router.HandleFunc("/eve2pcap", Eve2PcapHandler)
	router.HandleFunc("/api/version", VersionHandler)
	router.HandleFunc("/api/config", ConfigHandler)

	setupElasticSearchProxy(router)
	setupStatic(router)

	log.Printf("Listening on %s:%s", opts.Host, opts.Port)
	err = http.ListenAndServe(opts.Host + ":" + opts.Port, router)
	if err != nil {
		log.Fatal(err)
	}
}
<commit_msg>rename config to conf for new package config<commit_after>/* Copyright (c) 2014-2015 Jason Ish
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
 * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

package main

import (
	"encoding/json"
	"fmt"
	"log"
	"net/http"
	"net/http/httputil"
	"net/url"
	"os"

	"github.com/GeertJohan/go.rice"
	"github.com/gorilla/mux"
	"github.com/jessevdk/go-flags"
)

var buildDate string
var buildVersion string
var buildRev string

const DEFAULT_ELASTICSEARCH_URI string = "http://localhost:9200"

var opts struct {
	// We don't provide a default for this one so we can easily
	// detect if its been set or not.
	ElasticSearchUri   string `long:"elasticsearch" short:"e" description:"Elastic Search URI (default: http://localhost:9200)"`
	ElasticSearchIndex string `long:"index" short:"i" description:"Elastic Search Index (default: logstash-*)"`
	Port               string `long:"port" short:"p" default:"5636" description:"Port to bind to"`
	Host               string `long:"host" default:"0.0.0.0" description:"Host to bind to"`
	DevServerUri       string `long:"dev" description:"Frontend development server URI"`
	Version            bool   `long:"version" description:"Show version"`
	Config             string `long:"config" short:"c" description:"Configuration filename"`
	NoCheckCertificate bool   `long:"no-check-certificate" short:"k" description:"Disable certificate check for Elastic Search"`
}

var conf = &Config{}

type VersionResponse struct {
	Version  string
	Revision string
	Date     string
}

func VersionHandler(w http.ResponseWriter, r *http.Request) {
	response := VersionResponse{
		buildVersion,
		buildRev,
		buildDate,
	}
	w.Header().Set("Content-Type", "application/json; charset=UTF-8")
	json.NewEncoder(w).Encode(response)
}

func ConfigHandler(w http.ResponseWriter, r *http.Request) {
	configJson, err := conf.toJSON()
	if err != nil {
		// Return failure.
		log.Println("error: ", err)
		return
	}
	w.Header().Set("Content-Type", "application/json; charset=UTF-8")
	w.Write(configJson)
}

func setupElasticSearchProxy(router *mux.Router) {
	if opts.ElasticSearchUri == "" {
		if os.Getenv("ELASTICSEARCH_URL") != "" {
			opts.ElasticSearchUri = os.Getenv("ELASTICSEARCH_URL")
		} else {
			opts.ElasticSearchUri = DEFAULT_ELASTICSEARCH_URI
		}
	}
	log.Printf("Elastic Search URI: %v", opts.ElasticSearchUri)
	esProxy, err := NewElasticSearchProxy(opts.ElasticSearchUri,
		"/elasticsearch", opts.NoCheckCertificate)
	if err != nil {
		log.Fatal(err)
	}
	router.PathPrefix("/elasticsearch").Handler(esProxy)
}

// Setup the handler for static files.
func setupStatic(router *mux.Router) {
	if len(opts.DevServerUri) > 0 {
		log.Printf("Proxying static files to development server %v.",
			opts.DevServerUri)
		devServerProxyUrl, err := url.Parse(opts.DevServerUri)
		if err != nil {
			log.Fatal(err)
		}
		devServerProxy :=
			httputil.NewSingleHostReverseProxy(devServerProxyUrl)
		router.PathPrefix("/").Handler(devServerProxy)

	} else {
		public := http.FileServer(
			rice.MustFindBox("./public").HTTPBox())
		router.PathPrefix("/").Handler(public)
	}
}

func main() {

	log.SetFlags(log.Lshortfile)

	_, err := flags.Parse(&opts)
	if err != nil {
		// flags.Parse should have already presented an error message.
		os.Exit(1)
	}

	if opts.Version {
		fmt.Printf("EveBox Version %s (rev %s) [%s]\n",
			buildVersion, buildRev, buildDate)
		os.Exit(0)
	}

	// If no configuration was provided, see if evebox.yaml exists
	// in the current directory.
	if opts.Config == "" {
		_, err = os.Stat("./evebox.yaml")
		if err == nil {
			opts.Config = "./evebox.yaml"
		}
	}
	if opts.Config != "" {
		log.Printf("Loading configuration file %s.\n", opts.Config)
		conf, err = LoadConfig(opts.Config)
		if err != nil {
			log.Fatal(err)
		}
	}

	if opts.ElasticSearchIndex != "" {
		log.Printf("Using ElasticSearch Index %s.", opts.ElasticSearchIndex)
		conf.ElasticSearchIndex = opts.ElasticSearchIndex
	}

	router := mux.NewRouter()

	router.HandleFunc("/eve2pcap", Eve2PcapHandler)
	router.HandleFunc("/api/version", VersionHandler)
	router.HandleFunc("/api/config", ConfigHandler)

	setupElasticSearchProxy(router)
	setupStatic(router)

	log.Printf("Listening on %s:%s", opts.Host, opts.Port)
	err = http.ListenAndServe(opts.Host+":"+opts.Port, router)
	if err != nil {
		log.Fatal(err)
	}
}
<|endoftext|>


<commit_before>package bootstrap

import (
	"fmt"

	ct "github.com/flynn/flynn/controller/types"
	"github.com/flynn/flynn/pkg/tlscert"
	"github.com/flynn/flynn/router/types"
)

type AddRouteAction struct {
	ID string `json:"id"`

	AppStep  string `json:"app_step"`
	CertStep string `json:"cert_step"`
	*router.Route
}

func init() {
	Register("add-route", &AddRouteAction{})
}

type AddRouteState struct {
	App   *ct.App       `json:"app"`
	Route *router.Route `json:"route"`
}

func (a *AddRouteAction) Run(s *State) error {
	client, err := s.ControllerClient()
	if err != nil {
		return err
	}
	data, err := getAppStep(s, a.AppStep)
	if err != nil {
		return err
	}
	if a.Route.Type == "http" {
		route := a.Route.HTTPRoute()
		route.Domain = interpolate(s, route.Domain)
		if a.CertStep != "" {
			cert, err := getCertStep(s, a.CertStep)
			if err != nil {
				return err
			}
			route.TLSCert = cert.Cert
			route.TLSKey = cert.PrivateKey
		}
		a.Route = route.ToRoute()
	}

	if err := client.CreateRoute(data.App.ID, a.Route); err != nil {
		return err
	}
	s.StepData[a.ID] = &AddRouteState{App: data.App, Route: a.Route}

	return nil
}

func getAppStep(s *State, step string) (*AppState, error) {
	data, ok := s.StepData[step].(*AppState)
	if !ok {
		return nil, fmt.Errorf("bootstrap: unable to find step %q", step)
	}
	return data, nil
}

func getCertStep(s *State, step string) (*tlscert.Cert, error) {
	data, ok := s.StepData[step].(*tlscert.Cert)
	if !ok {
		return nil, fmt.Errorf("bootstrap: unable to find step %q", step)
	}
	return data, nil
}
<commit_msg>bootstrap: Retry route creation<commit_after>package bootstrap

import (
	"fmt"
	"time"

	ct "github.com/flynn/flynn/controller/types"
	"github.com/flynn/flynn/pkg/attempt"
	"github.com/flynn/flynn/pkg/tlscert"
	"github.com/flynn/flynn/router/types"
)

type AddRouteAction struct {
	ID string `json:"id"`

	AppStep  string `json:"app_step"`
	CertStep string `json:"cert_step"`
	*router.Route
}

func init() {
	Register("add-route", &AddRouteAction{})
}

type AddRouteState struct {
	App   *ct.App       `json:"app"`
	Route *router.Route `json:"route"`
}

func (a *AddRouteAction) Run(s *State) error {
	client, err := s.ControllerClient()
	if err != nil {
		return err
	}
	data, err := getAppStep(s, a.AppStep)
	if err != nil {
		return err
	}
	if a.Route.Type == "http" {
		route := a.Route.HTTPRoute()
		route.Domain = interpolate(s, route.Domain)
		if a.CertStep != "" {
			cert, err := getCertStep(s, a.CertStep)
			if err != nil {
				return err
			}
			route.TLSCert = cert.Cert
			route.TLSKey = cert.PrivateKey
		}
		a.Route = route.ToRoute()
	}

	err = attempt.Strategy{
		Min:   5,
		Total: 10 * time.Second,
		Delay: 200 * time.Millisecond,
	}.Run(func() error {
		return client.CreateRoute(data.App.ID, a.Route)
	})
	if err != nil {
		return err
	}
	s.StepData[a.ID] = &AddRouteState{App: data.App, Route: a.Route}

	return nil
}

func getAppStep(s *State, step string) (*AppState, error) {
	data, ok := s.StepData[step].(*AppState)
	if !ok {
		return nil, fmt.Errorf("bootstrap: unable to find step %q", step)
	}
	return data, nil
}

func getCertStep(s *State, step string) (*tlscert.Cert, error) {
	data, ok := s.StepData[step].(*tlscert.Cert)
	if !ok {
		return nil, fmt.Errorf("bootstrap: unable to find step %q", step)
	}
	return data, nil
}
<|endoftext|>


<commit_before>package simgo

import (
	"fmt"

	"github.com/juju/errgo"
)

const (
	// Built-in event priorities
	PriorityUrgent = iota
	PriorityNormal
)

// EventValue holds the value state for an Event.  If the value is pending it
// means that the event
type EventValue struct {
	val       interface{}
	isPending bool
}

// NewEventValue returns a pending EventValue.
func NewEventValue() *EventValue {
	return &EventValue{nil, true}
}

// Set sets the underlying value (and sets isPending accordingly).
func (ev *EventValue) Set(value interface{}) {
	ev.val = value
	ev.isPending = false
}

// Get returns the underlying event value along with an error if the value is
// still pending.
func (ev *EventValue) Get() (interface{}, error) {
	var err error
	if ev.isPending {
		err = errgo.New("event value is still pending")
	}
	return ev.val, err
}

// An Event is an event that may happen at some point in time.
//
//    An event
//
//    - may happen (i.e., triggered is False),
//    - is going to happen (i.e., triggered True) or
//    - has happened (i.e., processed True).
//
// Every event is bound to an environment (env) and is initially not triggered.
// Events are scheduled for processing by the environment after they are
// triggered by either `succeed`, `fail` or `trigger`. These methods also set
// the `ok` flag and the value of the event.
//
// An event has a list of `callbacks`. Once an event gets processed, all
// callbacks will be called with the event as the single argument. Callbacks
// can check if the event was successful by examining `ok` and do further
// processing with the value it has produced.
//
// TODO: Talk about how events are finalized/defused (?) after being processed.
type Event struct {
	// The environment the event lives in
	env *Environment
	// List of functions that are called when the event is processed.
	callbacks []func(*Event)
	// Value holds the event's value
	Value *EventValue
}

// NewEvent returns a new Event object with default values.
func NewEvent(env *Environment) *Event {
	return &Event{
		env,
		make([]func(*Event), 0),
		NewEventValue(),
	}
}

// Timeout embeds an event and adds a delay
type Timeout struct {
	*Event
	delay uint64
}

// NewTimeout returns a new Timeout object given an environment, delay and an
// Event value.  The event is automatically triggered when this function is
// called.
func NewTimeout(env *Environment, delay uint64, value interface{}) Timeout {
	return Timeout{
		&Event{
			env,
			make([]func(*Event), 0),
			&EventValue{value, false},
		},
		delay,
	}
}

// Schedule schedules the timeout event for the provided environment.
func (to *Timeout) Schedule(env *Environment) {
	env.Schedule(to.Event, PriorityNormal, to.delay)
}

// A Process processes an event yielding process function.
//
// A user implements a process function which is a coroutine function that can
// suspend its execution by yielding an event (using ProcComm.Yield()).
// Process will take care of resuming the process function with the value of
// that event once it has happened.
type Process struct {
	*Event
	env *Environment
	pc  *ProcComm
}

// NewProcess returns a new Process given an Environment and a ProcComm
// (which is used to communicate between the process function coroutine and the
// Process).
func NewProcess(env *Environment, pc *ProcComm) *Process {
	return &Process{
		NewEvent(env),
		env,
		pc,
	}
}

// Init initializes the process.  The process's Event is automatically
// triggered and scheduled.
func (p *Process) Init() {
	initEvent := NewEvent(p.env)
	fmt.Println("Adding initEvent callback...")
	initEvent.callbacks = append(initEvent.callbacks, p.resume)
	initEvent.Value.Set(nil)
	p.env.Schedule(initEvent, PriorityUrgent, 0)
}

// resume takes care of resuming the process function with the value of the
// provided Event.
func (p *Process) resume(event *Event) {
	fmt.Println("in resume")
	for {
		// event value is already triggered, no need to check err
		eventVal, _ := event.Value.Get()
		if nextEvent, ok := p.pc.Resume(eventVal); !ok {
			fmt.Println("proc finished...")
			p.env.Schedule(p.Event, PriorityNormal, 0)
			break
		} else {
			event = nextEvent
		}

		if event.callbacks != nil {
			// The event has not yet been triggered. Register
			// callback to resume the process if that happens.
			fmt.Println("Adding callback from resume()...")
			event.callbacks = append(event.callbacks, p.resume)
			return
		}
	}
}

// ProcWrapper is function that turns a user process function into a coroutine
// that can suspend its execution by yielding an event (using
// ProcComm.Yield()).
//
// See the examples directory for example usage.
//
func ProcWrapper(env *Environment, procFn func(*Environment, *ProcComm)) *ProcComm {
	pc := NewProcComm()
	go func() {
		// An initial yield imitates coroutine behavior of not
		// executing the coroutine body upon creation.
		pc.Yield(nil)
		procFn(env, pc)
		pc.Finish()
	}()
	return pc
}
<commit_msg>Set proc value upon termination if still unset<commit_after>package simgo

import (
	"fmt"

	"github.com/juju/errgo"
)

const (
	// Built-in event priorities
	PriorityUrgent = iota
	PriorityNormal
)

// EventValue holds the value state for an Event.  If the value is pending it
// means that the event
type EventValue struct {
	val       interface{}
	isPending bool
}

// NewEventValue returns a pending EventValue.
func NewEventValue() *EventValue {
	return &EventValue{nil, true}
}

// Set sets the underlying value (and sets isPending accordingly).
func (ev *EventValue) Set(value interface{}) {
	ev.val = value
	ev.isPending = false
}

// Get returns the underlying event value along with an error if the value is
// still pending.
func (ev *EventValue) Get() (interface{}, error) {
	var err error
	if ev.isPending {
		err = errgo.New("event value is still pending")
	}
	return ev.val, err
}

// An Event is an event that may happen at some point in time.
//
//    An event
//
//    - may happen (i.e., triggered is False),
//    - is going to happen (i.e., triggered True) or
//    - has happened (i.e., processed True).
//
// Every event is bound to an environment (env) and is initially not triggered.
// Events are scheduled for processing by the environment after they are
// triggered by either `succeed`, `fail` or `trigger`. These methods also set
// the `ok` flag and the value of the event.
//
// An event has a list of `callbacks`. Once an event gets processed, all
// callbacks will be called with the event as the single argument. Callbacks
// can check if the event was successful by examining `ok` and do further
// processing with the value it has produced.
//
// TODO: Talk about how events are finalized/defused (?) after being processed.
type Event struct {
	// The environment the event lives in
	env *Environment
	// List of functions that are called when the event is processed.
	callbacks []func(*Event)
	// Value holds the event's value
	Value *EventValue
}

// NewEvent returns a new Event object with default values.
func NewEvent(env *Environment) *Event {
	return &Event{
		env,
		make([]func(*Event), 0),
		NewEventValue(),
	}
}

// Timeout embeds an event and adds a delay
type Timeout struct {
	*Event
	delay uint64
}

// NewTimeout returns a new Timeout object given an environment, delay and an
// Event value.  The event is automatically triggered when this function is
// called.
func NewTimeout(env *Environment, delay uint64, value interface{}) Timeout {
	return Timeout{
		&Event{
			env,
			make([]func(*Event), 0),
			&EventValue{value, false},
		},
		delay,
	}
}

// Schedule schedules the timeout event for the provided environment.
func (to *Timeout) Schedule(env *Environment) {
	env.Schedule(to.Event, PriorityNormal, to.delay)
}

// A Process processes an event yielding process function.
//
// A user implements a process function which is a coroutine function that can
// suspend its execution by yielding an event (using ProcComm.Yield()).
// Process will take care of resuming the process function with the value of
// that event once it has happened.
type Process struct {
	*Event
	env *Environment
	pc  *ProcComm
}

// NewProcess returns a new Process given an Environment and a ProcComm
// (which is used to communicate between the process function coroutine and the
// Process).
func NewProcess(env *Environment, pc *ProcComm) *Process {
	return &Process{
		NewEvent(env),
		env,
		pc,
	}
}

// Init initializes the process.  The process's Event is automatically
// triggered and scheduled.
func (p *Process) Init() {
	initEvent := NewEvent(p.env)
	fmt.Println("Adding initEvent callback...")
	initEvent.callbacks = append(initEvent.callbacks, p.resume)
	initEvent.Value.Set(nil)
	p.env.Schedule(initEvent, PriorityUrgent, 0)
}

// resume takes care of resuming the process function with the value of the
// provided Event.
func (p *Process) resume(event *Event) {
	fmt.Println("in resume")
	for {
		// event value is already triggered, no need to check err
		eventVal, _ := event.Value.Get()
		if nextEvent, ok := p.pc.Resume(eventVal); !ok {
			fmt.Println("proc finished...")
			// Set the process value to nil if it hasn't been set
			// already.
			if p.Event.Value.isPending {
				p.Event.Value.Set(nil)
			}
			p.env.Schedule(p.Event, PriorityNormal, 0)
			break
		} else {
			event = nextEvent
		}

		if event.callbacks != nil {
			// The event has not yet been triggered. Register
			// callback to resume the process if that happens.
			fmt.Println("Adding callback from resume()...")
			event.callbacks = append(event.callbacks, p.resume)
			return
		}
	}
}

// ProcWrapper is function that turns a user process function into a coroutine
// that can suspend its execution by yielding an event (using
// ProcComm.Yield()).
//
// See the examples directory for example usage.
//
func ProcWrapper(env *Environment, procFn func(*Environment, *ProcComm)) *ProcComm {
	pc := NewProcComm()
	go func() {
		// An initial yield imitates coroutine behavior of not
		// executing the coroutine body upon creation.
		pc.Yield(nil)
		procFn(env, pc)
		pc.Finish()
	}()
	return pc
}
<|endoftext|>


<commit_before>// The cloudstack package contains a packer.Builder implementation
// that builds Cloudstack images (templates).

package cloudstack

import (
	"errors"
	"fmt"
	"github.com/mindjiver/gopherstack"
	"github.com/mitchellh/multistep"
	"github.com/mitchellh/packer/common"
	"github.com/mitchellh/packer/packer"
	"log"
	"os"
	"time"
)

// The unique id for the builder
const BuilderId = "mindjiver.cloudstack"

// Configuration tells the builder the credentials to use while
// communicating with Cloudstack and describes the template you are
// creating
type config struct {
	common.PackerConfig `mapstructure:",squash"`

	APIURL string `mapstructure:"api_url"`
	APIKey string `mapstructure:"api_key"`
	Secret string `mapstructure:"secret"`

	RawSSHTimeout   string `mapstructure:"ssh_timeout"`
	RawStateTimeout string `mapstructure:"state_timeout"`

	// Time to wait before issuing the API call to detach the
	// bootstrap/installation ISO from the virtual machine.
	RawDetachISOWait string `mapstructure:"detach_iso_wait"`

	InsecureSkipVerify bool `mapstructure:"insecure_skip_verify"`

	SSHUsername string `mapstructure:"ssh_username"`
	SSHPort     uint   `mapstructure:"ssh_port"`
	SSHKeyPath  string `mapstructure:"ssh_key_path"`

	// These are unexported since they're set by other fields
	// being set.
	sshTimeout    time.Duration
	stateTimeout  time.Duration
	detachISOWait time.Duration

	HTTPDir     string `mapstructure:"http_directory"`
	HTTPPortMin uint   `mapstructure:"http_port_min"`
	HTTPPortMax uint   `mapstructure:"http_port_max"`

	// Neccessary settings for Cloudstack to be able to spin up
	// Virtual Machine with either template or a ISO.
	ServiceOfferingId string   `mapstructure:"service_offering_id"`
	TemplateId        string   `mapstructure:"template_id"`
	ZoneId            string   `mapstructure:"zone_id"`
	NetworkIds        []string `mapstructure:"network_ids"`
	DiskOfferingId    string   `mapstructure:"disk_offering_id"`
	UserData          string   `mapstructure:"user_data"`
	Hypervisor        string   `mapstructure:"hypervisor"`

	// Tell Cloudstack under which name, description to save the
	// template.
	TemplateName        string `mapstructure:"template_name"`
	TemplateDisplayText string `mapstructure:"template_display_text"`
	TemplateOSId        string `mapstructure:"template_os_id"`

	tpl *packer.ConfigTemplate
}

type Builder struct {
	config config
	runner multistep.Runner
}

func (b *Builder) Prepare(raws ...interface{}) ([]string, error) {
	md, err := common.DecodeConfig(&b.config, raws...)
	if err != nil {
		return nil, err
	}

	b.config.tpl, err = packer.NewConfigTemplate()
	if err != nil {
		return nil, err
	}
	b.config.tpl.UserVars = b.config.PackerUserVars

	// Accumulate any errors
	errs := common.CheckUnusedConfig(md)

	if b.config.APIURL == "" {
		// Default to environment variable for API URL
		b.config.APIURL = os.Getenv("CLOUDSTACK_API_URL")
	}

	if b.config.APIKey == "" {
		// Default to environment variable for API key
		b.config.APIKey = os.Getenv("CLOUDSTACK_API_KEY")
	}

	if b.config.Secret == "" {
		// Default to environment variable for API secret
		b.config.Secret = os.Getenv("CLOUDSTACK_SECRET")
	}

	if b.config.HTTPPortMin == 0 {
		b.config.HTTPPortMin = 8000
	}

	if b.config.HTTPPortMax == 0 {
		b.config.HTTPPortMax = 9000
	}

	if b.config.TemplateName == "" {
		// Default to packer-{{ unix timestamp (utc) }}
		b.config.TemplateName = "packer-{{timestamp}}"
	}

	if b.config.TemplateDisplayText == "" {
		b.config.TemplateDisplayText = "Packer_Generated_Template"
	}

	if b.config.TemplateOSId == "" {
		// Default to Other 64 bit OS
		b.config.TemplateOSId = "103"
	}

	if b.config.SSHUsername == "" {
		// Default to "root". You can override this if your
		// source template has a different user account.
		b.config.SSHUsername = "root"
	}

	if b.config.SSHPort == 0 {
		// Default to port 22
		b.config.SSHPort = 22
	}

	if b.config.RawSSHTimeout == "" {
		// Default to 10 minute timeouts
		b.config.RawSSHTimeout = "10m"
	}

	if b.config.RawStateTimeout == "" {
		// Default to 5 minute timeouts waiting for desired
		// state. i.e waiting for virtual machine to become
		// active
		b.config.RawStateTimeout = "5m"
	}

	if b.config.RawDetachISOWait == "" {
		// Default to wait 10 seconds before detaching the ISO
		// from the started virtual machine.
		b.config.RawDetachISOWait = "10s"
	}

	templates := map[string]*string{
		"api_url":               &b.config.APIURL,
		"api_key":               &b.config.APIKey,
		"secret":                &b.config.Secret,
		"template_name":         &b.config.TemplateName,
		"template_display_text": &b.config.TemplateDisplayText,
		"ssh_username":          &b.config.SSHUsername,
		"ssh_timeout":           &b.config.RawSSHTimeout,
		"ssh_key_path":          &b.config.SSHKeyPath,
		"state_timeout":         &b.config.RawStateTimeout,
		"http_directory":        &b.config.HTTPDir,
		"detach_iso_wait":       &b.config.RawDetachISOWait,
	}

	for n, ptr := range templates {
		var err error
		*ptr, err = b.config.tpl.Process(*ptr, nil)
		if err != nil {
			errs = packer.MultiErrorAppend(
				errs, fmt.Errorf("Error processing %s: %s", n, err))
		}
	}

	if b.config.HTTPPortMin > b.config.HTTPPortMax {
		errs = packer.MultiErrorAppend(
			errs, errors.New("http_port_min must be less than http_port_max"))
	}

	// Required configurations that will display errors if not set
	if b.config.APIURL == "" {
		errs = packer.MultiErrorAppend(
			errs, errors.New("CLOUDSTACK_API_URL in env (APIURL in json) must be specified"))
	}

	if b.config.APIKey == "" {
		errs = packer.MultiErrorAppend(
			errs, errors.New("CLOUDSTACK_API_KEY in env (APIKey in json) must be specified"))
	}

	if b.config.Secret == "" {
		errs = packer.MultiErrorAppend(
			errs, errors.New("CLOUDSTACK_SECRET in env (Secret in json) must be specified"))
	}

	if b.config.ServiceOfferingId == "" {
		errs = packer.MultiErrorAppend(
			errs, errors.New("service_offering_id must be specified"))
	}

	if b.config.TemplateId == "" {
		errs = packer.MultiErrorAppend(
			errs, errors.New("template_id must be specified"))
	}

	if b.config.ZoneId == "" {
		errs = packer.MultiErrorAppend(
			errs, errors.New("zone_id must be specified"))
	}

	if len(b.config.NetworkIds) < 1 {
		errs = packer.MultiErrorAppend(
			errs, errors.New("network_ids must be specified"))
	}

	sshTimeout, err := time.ParseDuration(b.config.RawSSHTimeout)
	if err != nil {
		errs = packer.MultiErrorAppend(
			errs, fmt.Errorf("Failed parsing ssh_timeout: %s", err))
	}
	b.config.sshTimeout = sshTimeout

	detachISOWait, err := time.ParseDuration(b.config.RawDetachISOWait)
	if err != nil {
		errs = packer.MultiErrorAppend(
			errs, fmt.Errorf("Failed parsing iso_detach_wait: %s", err))
	}
	b.config.detachISOWait = detachISOWait

	stateTimeout, err := time.ParseDuration(b.config.RawStateTimeout)
	if err != nil {
		errs = packer.MultiErrorAppend(
			errs, fmt.Errorf("Failed parsing state_timeout: %s", err))
	}
	b.config.stateTimeout = stateTimeout

	if errs != nil && len(errs.Errors) > 0 {
		return nil, errs
	}

	common.ScrubConfig(b.config, b.config.APIKey, b.config.Secret)
	return nil, nil
}

func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packer.Artifact, error) {
	// Initialize the Cloudstack API client
	client := gopherstack.CloudstackClient{}.New(b.config.APIURL, b.config.APIKey,
		b.config.Secret, b.config.InsecureSkipVerify)

	// Set up the state
	state := new(multistep.BasicStateBag)
	state.Put("config", b.config)
	state.Put("client", client)
	state.Put("hook", hook)
	state.Put("ui", ui)

	// Build the steps
	steps := []multistep.Step{
		new(stepHTTPServer),
		new(stepCreateSSHKeyPair),
		new(stepDeployVirtualMachine),
		new(stepVirtualMachineState),
		new(stepDetachIso),
		&common.StepConnectSSH{
			SSHAddress:     sshAddress,
			SSHConfig:      sshConfig,
			SSHWaitTimeout: b.config.sshTimeout,
		},
		new(common.StepProvision),
		new(stepStopVirtualMachine),
		new(stepCreateTemplate),
	}

	// Run the steps
	if b.config.PackerDebug {
		b.runner = &multistep.DebugRunner{
			Steps:   steps,
			PauseFn: common.MultistepDebugFn(ui),
		}
	} else {
		b.runner = &multistep.BasicRunner{Steps: steps}
	}

	b.runner.Run(state)

	// If there was an error, return that
	if rawErr, ok := state.GetOk("error"); ok {
		return nil, rawErr.(error)
	}

	// If we were interrupted or cancelled, then just exit.
	if _, ok := state.GetOk(multistep.StateCancelled); ok {
		return nil, errors.New("Build was cancelled.")
	}

	if _, ok := state.GetOk(multistep.StateHalted); ok {
		return nil, errors.New("Build was halted.")
	}

	if _, ok := state.GetOk("template_name"); !ok {
		log.Println("Failed to find template_name in state. Bug?")
		return nil, nil
	}

	artifact := &Artifact{
		templateName: state.Get("template_name").(string),
		templateId:   state.Get("template_id").(string),
		client:       client,
	}

	return artifact, nil
}

func (b *Builder) Cancel() {
	if b.runner != nil {
		log.Println("Cancelling the step runner...")
		b.runner.Cancel()
	}
}
<commit_msg>builder/cloudstack: Enable templating for more variables<commit_after>// The cloudstack package contains a packer.Builder implementation
// that builds Cloudstack images (templates).

package cloudstack

import (
	"errors"
	"fmt"
	"github.com/mindjiver/gopherstack"
	"github.com/mitchellh/multistep"
	"github.com/mitchellh/packer/common"
	"github.com/mitchellh/packer/packer"
	"log"
	"os"
	"time"
)

// The unique id for the builder
const BuilderId = "mindjiver.cloudstack"

// Configuration tells the builder the credentials to use while
// communicating with Cloudstack and describes the template you are
// creating
type config struct {
	common.PackerConfig `mapstructure:",squash"`

	APIURL string `mapstructure:"api_url"`
	APIKey string `mapstructure:"api_key"`
	Secret string `mapstructure:"secret"`

	RawSSHTimeout   string `mapstructure:"ssh_timeout"`
	RawStateTimeout string `mapstructure:"state_timeout"`

	// Time to wait before issuing the API call to detach the
	// bootstrap/installation ISO from the virtual machine.
	RawDetachISOWait string `mapstructure:"detach_iso_wait"`

	InsecureSkipVerify bool `mapstructure:"insecure_skip_verify"`

	SSHUsername string `mapstructure:"ssh_username"`
	SSHPort     uint   `mapstructure:"ssh_port"`
	SSHKeyPath  string `mapstructure:"ssh_key_path"`

	// These are unexported since they're set by other fields
	// being set.
	sshTimeout    time.Duration
	stateTimeout  time.Duration
	detachISOWait time.Duration

	HTTPDir     string `mapstructure:"http_directory"`
	HTTPPortMin uint   `mapstructure:"http_port_min"`
	HTTPPortMax uint   `mapstructure:"http_port_max"`

	// Neccessary settings for Cloudstack to be able to spin up
	// Virtual Machine with either template or a ISO.
	ServiceOfferingId string   `mapstructure:"service_offering_id"`
	TemplateId        string   `mapstructure:"template_id"`
	ZoneId            string   `mapstructure:"zone_id"`
	NetworkIds        []string `mapstructure:"network_ids"`
	DiskOfferingId    string   `mapstructure:"disk_offering_id"`
	UserData          string   `mapstructure:"user_data"`
	Hypervisor        string   `mapstructure:"hypervisor"`

	// Tell Cloudstack under which name, description to save the
	// template.
	TemplateName        string `mapstructure:"template_name"`
	TemplateDisplayText string `mapstructure:"template_display_text"`
	TemplateOSId        string `mapstructure:"template_os_id"`

	tpl *packer.ConfigTemplate
}

type Builder struct {
	config config
	runner multistep.Runner
}

func (b *Builder) Prepare(raws ...interface{}) ([]string, error) {
	md, err := common.DecodeConfig(&b.config, raws...)
	if err != nil {
		return nil, err
	}

	b.config.tpl, err = packer.NewConfigTemplate()
	if err != nil {
		return nil, err
	}
	b.config.tpl.UserVars = b.config.PackerUserVars

	// Accumulate any errors
	errs := common.CheckUnusedConfig(md)

	if b.config.APIURL == "" {
		// Default to environment variable for API URL
		b.config.APIURL = os.Getenv("CLOUDSTACK_API_URL")
	}

	if b.config.APIKey == "" {
		// Default to environment variable for API key
		b.config.APIKey = os.Getenv("CLOUDSTACK_API_KEY")
	}

	if b.config.Secret == "" {
		// Default to environment variable for API secret
		b.config.Secret = os.Getenv("CLOUDSTACK_SECRET")
	}

	if b.config.HTTPPortMin == 0 {
		b.config.HTTPPortMin = 8000
	}

	if b.config.HTTPPortMax == 0 {
		b.config.HTTPPortMax = 9000
	}

	if b.config.TemplateName == "" {
		// Default to packer-{{ unix timestamp (utc) }}
		b.config.TemplateName = "packer-{{timestamp}}"
	}

	if b.config.TemplateDisplayText == "" {
		b.config.TemplateDisplayText = "Packer_Generated_Template"
	}

	if b.config.TemplateOSId == "" {
		// Default to Other 64 bit OS
		b.config.TemplateOSId = "103"
	}

	if b.config.SSHUsername == "" {
		// Default to "root". You can override this if your
		// source template has a different user account.
		b.config.SSHUsername = "root"
	}

	if b.config.SSHPort == 0 {
		// Default to port 22
		b.config.SSHPort = 22
	}

	if b.config.RawSSHTimeout == "" {
		// Default to 10 minute timeouts
		b.config.RawSSHTimeout = "10m"
	}

	if b.config.RawStateTimeout == "" {
		// Default to 5 minute timeouts waiting for desired
		// state. i.e waiting for virtual machine to become
		// active
		b.config.RawStateTimeout = "5m"
	}

	if b.config.RawDetachISOWait == "" {
		// Default to wait 10 seconds before detaching the ISO
		// from the started virtual machine.
		b.config.RawDetachISOWait = "10s"
	}

	templates := map[string]*string{
		"api_url":               &b.config.APIURL,
		"api_key":               &b.config.APIKey,
		"secret":                &b.config.Secret,
		"ssh_timeout":           &b.config.RawSSHTimeout,
		"state_timeout":         &b.config.RawStateTimeout,
		"detach_iso_wait":       &b.config.RawDetachISOWait,
		"insecure_skip_verify":  &b.config.InsecureSkipVerify,
		"ssh_username":          &b.config.SSHUsername,
		"ssh_port":              &b.config.SSHPort,
		"ssh_key_path":          &b.config.SSHKeyPath,
		"http_directory":        &b.config.HTTPDir,
		"service_offering_id":   &b.config.ServiceOfferingId,
		"template_id":           &b.config.TemplateId,
		"zone_id":               &b.config.ZoneId,
//		"network_ids":           &b.config.NetworkIds,
		"disk_offering_id":      &b.config.DiskOfferingId,
		"user_data":             &b.config.UserData,
		"hypervisor":            &b.config.Hypervisor,
		"template_name":         &b.config.TemplateName,
		"template_display_text": &b.config.TemplateDisplayText,
		"template_os_id":        &b.config.TemplateOSId,
	}

	for n, ptr := range templates {
		var err error
		*ptr, err = b.config.tpl.Process(*ptr, nil)
		if err != nil {
			errs = packer.MultiErrorAppend(
				errs, fmt.Errorf("Error processing %s: %s", n, err))
		}
	}

	if b.config.HTTPPortMin > b.config.HTTPPortMax {
		errs = packer.MultiErrorAppend(
			errs, errors.New("http_port_min must be less than http_port_max"))
	}

	// Required configurations that will display errors if not set
	if b.config.APIURL == "" {
		errs = packer.MultiErrorAppend(
			errs, errors.New("CLOUDSTACK_API_URL in env (APIURL in json) must be specified"))
	}

	if b.config.APIKey == "" {
		errs = packer.MultiErrorAppend(
			errs, errors.New("CLOUDSTACK_API_KEY in env (APIKey in json) must be specified"))
	}

	if b.config.Secret == "" {
		errs = packer.MultiErrorAppend(
			errs, errors.New("CLOUDSTACK_SECRET in env (Secret in json) must be specified"))
	}

	if b.config.ServiceOfferingId == "" {
		errs = packer.MultiErrorAppend(
			errs, errors.New("service_offering_id must be specified"))
	}

	if b.config.TemplateId == "" {
		errs = packer.MultiErrorAppend(
			errs, errors.New("template_id must be specified"))
	}

	if b.config.ZoneId == "" {
		errs = packer.MultiErrorAppend(
			errs, errors.New("zone_id must be specified"))
	}

	if len(b.config.NetworkIds) < 1 {
		errs = packer.MultiErrorAppend(
			errs, errors.New("network_ids must be specified"))
	}

	sshTimeout, err := time.ParseDuration(b.config.RawSSHTimeout)
	if err != nil {
		errs = packer.MultiErrorAppend(
			errs, fmt.Errorf("Failed parsing ssh_timeout: %s", err))
	}
	b.config.sshTimeout = sshTimeout

	detachISOWait, err := time.ParseDuration(b.config.RawDetachISOWait)
	if err != nil {
		errs = packer.MultiErrorAppend(
			errs, fmt.Errorf("Failed parsing iso_detach_wait: %s", err))
	}
	b.config.detachISOWait = detachISOWait

	stateTimeout, err := time.ParseDuration(b.config.RawStateTimeout)
	if err != nil {
		errs = packer.MultiErrorAppend(
			errs, fmt.Errorf("Failed parsing state_timeout: %s", err))
	}
	b.config.stateTimeout = stateTimeout

	if errs != nil && len(errs.Errors) > 0 {
		return nil, errs
	}

	common.ScrubConfig(b.config, b.config.APIKey, b.config.Secret)
	return nil, nil
}

func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packer.Artifact, error) {
	// Initialize the Cloudstack API client
	client := gopherstack.CloudstackClient{}.New(b.config.APIURL, b.config.APIKey,
		b.config.Secret, b.config.InsecureSkipVerify)

	// Set up the state
	state := new(multistep.BasicStateBag)
	state.Put("config", b.config)
	state.Put("client", client)
	state.Put("hook", hook)
	state.Put("ui", ui)

	// Build the steps
	steps := []multistep.Step{
		new(stepHTTPServer),
		new(stepCreateSSHKeyPair),
		new(stepDeployVirtualMachine),
		new(stepVirtualMachineState),
		new(stepDetachIso),
		&common.StepConnectSSH{
			SSHAddress:     sshAddress,
			SSHConfig:      sshConfig,
			SSHWaitTimeout: b.config.sshTimeout,
		},
		new(common.StepProvision),
		new(stepStopVirtualMachine),
		new(stepCreateTemplate),
	}

	// Run the steps
	if b.config.PackerDebug {
		b.runner = &multistep.DebugRunner{
			Steps:   steps,
			PauseFn: common.MultistepDebugFn(ui),
		}
	} else {
		b.runner = &multistep.BasicRunner{Steps: steps}
	}

	b.runner.Run(state)

	// If there was an error, return that
	if rawErr, ok := state.GetOk("error"); ok {
		return nil, rawErr.(error)
	}

	// If we were interrupted or cancelled, then just exit.
	if _, ok := state.GetOk(multistep.StateCancelled); ok {
		return nil, errors.New("Build was cancelled.")
	}

	if _, ok := state.GetOk(multistep.StateHalted); ok {
		return nil, errors.New("Build was halted.")
	}

	if _, ok := state.GetOk("template_name"); !ok {
		log.Println("Failed to find template_name in state. Bug?")
		return nil, nil
	}

	artifact := &Artifact{
		templateName: state.Get("template_name").(string),
		templateId:   state.Get("template_id").(string),
		client:       client,
	}

	return artifact, nil
}

func (b *Builder) Cancel() {
	if b.runner != nil {
		log.Println("Cancelling the step runner...")
		b.runner.Cancel()
	}
}
<|endoftext|>


<commit_before>package database
<commit_msg>Started work on database unit testing<commit_after>package database

import (
	"runtime"
	"strconv"
	"sync"
	"testing"
)

import "fmt"

type TestSession struct {
}

func (ms TestSession) DB(dbName string) Database {
	return &TestDatabase{}
}

type TestDatabase struct {
}

func (md TestDatabase) C(collectionName string) Collection {
	return &TestCollection{}
}

type TestCollection struct {
}

func (mc TestCollection) Find(selector interface{}) Query {
	return &TestQuery{}
}

func (mc TestCollection) RemoveId(id interface{}) error {
	return nil
}

func (mc TestCollection) Remove(selector interface{}) error {
	return nil
}

func (mc TestCollection) DropCollection() error {
	return nil
}

func (mc TestCollection) UpdateId(id interface{}, change interface{}) error {
	return nil
}

func (mc TestCollection) UpsertId(id interface{}, change interface{}) error {
	return nil
}

type TestQuery struct {
}

func (mq TestQuery) Count() (int, error) {
	return 0, nil
}

func (mq TestQuery) One(result interface{}) error {
	return nil
}

func (mq TestQuery) Iter() Iterator {
	return &TestIterator{}
}

type TestIterator struct {
}

func (mi TestIterator) All(result interface{}) error {
	return nil
}

func Test_ThreadSafety(t *testing.T) {
	runtime.GOMAXPROCS(2)
	Init(&TestSession{})

	char := NewCharacter("test", "", "")

	var wg sync.WaitGroup
	wg.Add(2)

	go func() {
		for i := 0; i < 100; i++ {
			char.SetName(strconv.Itoa(i))
		}
		wg.Done()
	}()

	go func() {
		for i := 0; i < 100; i++ {
			fmt.Println(char.GetName())
		}
		wg.Done()
	}()

	wg.Wait()
}

// vim: nocindent
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"log"
	"os"
	"strconv"
	"time"

	"golang.org/x/net/context"
	"google.golang.org/grpc"

	pb "github.com/brotherlogic/beerserver/proto"
	pbdi "github.com/brotherlogic/discovery/proto"
)

func getIP(servername string) (string, int) {
	conn, _ := grpc.Dial("192.168.86.64:50055", grpc.WithInsecure())
	defer conn.Close()

	registry := pbdi.NewDiscoveryServiceClient(conn)
	entry := pbdi.RegistryEntry{Name: servername}
	r, err := registry.Discover(context.Background(), &entry)
	if err != nil {
		return "", -1
	}
	log.Printf("Found %v", r)
	return r.Ip, int(r.Port)
}

func main() {
	addFlags := flag.NewFlagSet("AddBeer", flag.ExitOnError)
	var addSize = addFlags.String("size", "bomber", "Size of the beer")
	var id = addFlags.Int64("id", -1, "Id of the beer")
	var date = addFlags.String("date", "", "Date of drinking")

	getFlags := flag.NewFlagSet("GetBeer", flag.ExitOnError)
	var size = getFlags.String("size", "bomber", "Size of the beer")

	getCellarFlags := flag.NewFlagSet("GetCellar", flag.ExitOnError)
	var cellar = getCellarFlags.Int("cellar", 1, "The number of the cellar")

	removeFlags := flag.NewFlagSet("Remove", flag.ExitOnError)
	var rID = removeFlags.Int64("id", -1, "The id to be removed")

	ip, port := getIP("beerserver")
	conn, _ := grpc.Dial(ip+":"+strconv.Itoa(port), grpc.WithInsecure())
	defer conn.Close()

	client := pb.NewBeerCellarServiceClient(conn)

	if len(os.Args) <= 1 {
		fmt.Printf("Commands: get\n")
	} else {
		switch os.Args[1] {
		case "get":
			if err := getFlags.Parse(os.Args[2:]); err == nil {
				beer, err := client.GetBeer(context.Background(), &pb.Beer{Size: *size})
				if err != nil {
					log.Fatalf("Error getting beer: %v", err)
				}
				fmt.Printf("%v\n", beer)
			}
		case "add":
			if err := addFlags.Parse(os.Args[2:]); err == nil {

				ts, err := time.Parse("02/01/06", *date)
				if err != nil {
					log.Fatalf("Error parsing date: %v", err)
				}
				_, err = client.AddBeer(context.Background(), &pb.Beer{Size: *addSize, Id: *id, DrinkDate: ts.Unix()})
				if err != nil {
					log.Fatalf("Error adding beer: %v", err)
				}
			}
		case "cellar":
			if err := getCellarFlags.Parse(os.Args[2:]); err == nil {
				cellar, err := client.GetCellar(context.Background(), &pb.Cellar{Name: "cellar" + strconv.Itoa(*cellar)})
				if err != nil {
					log.Fatalf("Error getting cellar: %v", err)
				}
				for i, beer := range cellar.Beers {
					fmt.Printf("%v. %v\n", i+1, beer)
				}
			}
		case "remove":
			if err := removeFlags.Parse(os.Args[2:]); err == nil {
				beer, err := client.RemoveBeer(context.Background(), &pb.Beer{Id: *rID})
				if err != nil {
					log.Fatalf("Error removing beer: %v", err)
				}
				fmt.Printf("REMOVED %v\n", beer)
			}
		case "drunk":
			list, err := client.GetDrunk(context.Background(), &pb.Empty{})
			if err != nil {
				log.Fatalf("Error getting drunk beer: %v", err)
			}

			for i, beer := range list.Beers {
				fmt.Printf("%v. %v\n", i+1, beer)
			}
		}
	}
}
<commit_msg>Removed link output. This closes #121<commit_after>package main

import (
	"flag"
	"fmt"
	"log"
	"os"
	"strconv"
	"time"

	"golang.org/x/net/context"
	"google.golang.org/grpc"

	pb "github.com/brotherlogic/beerserver/proto"
	pbdi "github.com/brotherlogic/discovery/proto"
)

func getIP(servername string) (string, int) {
	conn, _ := grpc.Dial("192.168.86.64:50055", grpc.WithInsecure())
	defer conn.Close()

	registry := pbdi.NewDiscoveryServiceClient(conn)
	entry := pbdi.RegistryEntry{Name: servername}
	r, err := registry.Discover(context.Background(), &entry)
	if err != nil {
		return "", -1
	}
	return r.Ip, int(r.Port)
}

func main() {
	addFlags := flag.NewFlagSet("AddBeer", flag.ExitOnError)
	var addSize = addFlags.String("size", "bomber", "Size of the beer")
	var id = addFlags.Int64("id", -1, "Id of the beer")
	var date = addFlags.String("date", "", "Date of drinking")

	getFlags := flag.NewFlagSet("GetBeer", flag.ExitOnError)
	var size = getFlags.String("size", "bomber", "Size of the beer")

	getCellarFlags := flag.NewFlagSet("GetCellar", flag.ExitOnError)
	var cellar = getCellarFlags.Int("cellar", 1, "The number of the cellar")

	removeFlags := flag.NewFlagSet("Remove", flag.ExitOnError)
	var rID = removeFlags.Int64("id", -1, "The id to be removed")

	ip, port := getIP("beerserver")
	conn, _ := grpc.Dial(ip+":"+strconv.Itoa(port), grpc.WithInsecure())
	defer conn.Close()

	client := pb.NewBeerCellarServiceClient(conn)

	if len(os.Args) <= 1 {
		fmt.Printf("Commands: get\n")
	} else {
		switch os.Args[1] {
		case "get":
			if err := getFlags.Parse(os.Args[2:]); err == nil {
				beer, err := client.GetBeer(context.Background(), &pb.Beer{Size: *size})
				if err != nil {
					log.Fatalf("Error getting beer: %v", err)
				}
				fmt.Printf("%v\n", beer)
			}
		case "add":
			if err := addFlags.Parse(os.Args[2:]); err == nil {

				ts, err := time.Parse("02/01/06", *date)
				if err != nil {
					log.Fatalf("Error parsing date: %v", err)
				}
				_, err = client.AddBeer(context.Background(), &pb.Beer{Size: *addSize, Id: *id, DrinkDate: ts.Unix()})
				if err != nil {
					log.Fatalf("Error adding beer: %v", err)
				}
			}
		case "cellar":
			if err := getCellarFlags.Parse(os.Args[2:]); err == nil {
				cellar, err := client.GetCellar(context.Background(), &pb.Cellar{Name: "cellar" + strconv.Itoa(*cellar)})
				if err != nil {
					log.Fatalf("Error getting cellar: %v", err)
				}
				for i, beer := range cellar.Beers {
					fmt.Printf("%v. %v\n", i+1, beer)
				}
			}
		case "remove":
			if err := removeFlags.Parse(os.Args[2:]); err == nil {
				beer, err := client.RemoveBeer(context.Background(), &pb.Beer{Id: *rID})
				if err != nil {
					log.Fatalf("Error removing beer: %v", err)
				}
				fmt.Printf("REMOVED %v\n", beer)
			}
		case "drunk":
			list, err := client.GetDrunk(context.Background(), &pb.Empty{})
			if err != nil {
				log.Fatalf("Error getting drunk beer: %v", err)
			}

			for i, beer := range list.Beers {
				fmt.Printf("%v. %v\n", i+1, beer)
			}
		}
	}
}
<|endoftext|>


<commit_before>package GoSDK

import (
	"fmt"
)

const (
	_LIB_PREAMBLE = "/codeadmin/v/2/library/"
)

func (d *DevClient) GetLibraries(systemKey string) ([]interface{}, error) {
	creds, err := d.credentials()
	if err != nil {
		return nil, err
	}
	resp, err := get(_LIB_PREAMBLE+systemKey, nil, creds)

	resp, err = mapResponse(resp, err)
	if err != nil {
		return nil, err
	}
	fmt.Printf("GOT BACK: %+v\n", resp.Body)
	return resp.Body.([]interface{}), nil
}

func (d *DevClient) CreateLibrary(systemKey, name string, data map[string]interface{}) (map[string]interface{}, error) {
	creds, err := d.credentials()
	if err != nil {
		return nil, err
	}
	resp, err := post(_LIB_PREAMBLE+systemKey+"/"+name, data, creds)
	resp, err = mapResponse(resp, err)
	if err != nil {
		return nil, err
	}

	return resp.Body.(map[string]interface{}), nil
}
<commit_msg>Finished library calls: v0.0001<commit_after>package GoSDK

import (
	"fmt"
	"strconv"
)

const (
	_LIB_PREAMBLE  = "/codeadmin/v/2/library"
	_HIST_PREAMBLE = "/codeadmin/v/2/history/library"
)

func (d *DevClient) GetAllLibraries() ([]interface{}, error) {
	creds, err := d.credentials()
	if err != nil {
		return nil, err
	}
	resp, err := get(_LIB_PREAMBLE, nil, creds)
	resp, err = mapResponse(resp, err)
	if err != nil {
		return nil, err
	}
	return resp.Body.([]interface{}), nil
}

func (d *DevClient) GetLibraries(systemKey string) ([]interface{}, error) {
	creds, err := d.credentials()
	if err != nil {
		return nil, err
	}
	resp, err := get(_LIB_PREAMBLE+"/"+systemKey, nil, creds)
	resp, err = mapResponse(resp, err)
	if err != nil {
		return nil, err
	}
	fmt.Printf("GOT BACK: %+v\n", resp.Body)
	return resp.Body.([]interface{}), nil
}

func (d *DevClient) GetLibrary(systemKey, name string) (map[string]interface{}, error) {
	creds, err := d.credentials()
	if err != nil {
		return nil, err
	}
	resp, err := get(_LIB_PREAMBLE+"/"+systemKey+"/"+name, nil, creds)
	resp, err = mapResponse(resp, err)
	if err != nil {
		return nil, err
	}
	return resp.Body.(map[string]interface{}), nil
}

func (d *DevClient) CreateLibrary(systemKey, name string, data map[string]interface{}) (map[string]interface{}, error) {
	creds, err := d.credentials()
	if err != nil {
		return nil, err
	}
	resp, err := post(_LIB_PREAMBLE+"/"+systemKey+"/"+name, data, creds)
	resp, err = mapResponse(resp, err)
	if err != nil {
		return nil, err
	}
	return resp.Body.(map[string]interface{}), nil
}

func (d *DevClient) UpdateLibrary(systemKey, name string, data map[string]interface{}) (map[string]interface{}, error) {
	creds, err := d.credentials()
	if err != nil {
		return nil, err
	}
	resp, err := put(_LIB_PREAMBLE+"/"+systemKey+"/"+name, data, creds)
	resp, err = mapResponse(resp, err)
	if err != nil {
		return nil, err
	}
	return resp.Body.(map[string]interface{}), nil
}

func (d *DevClient) DeleteLibrary(systemKey, name string) error {
	creds, err := d.credentials()
	if err != nil {
		return err
	}
	resp, err := delete(_LIB_PREAMBLE+"/"+systemKey+"/"+name, nil, creds)
	resp, err = mapResponse(resp, err)
	if err != nil {
		return err
	}
	return nil
}

func (d *DevClient) GetVersionHistory(systemKey, name string) ([]interface{}, error) {
	creds, err := d.credentials()
	if err != nil {
		return nil, err
	}
	resp, err := get(_HIST_PREAMBLE+"/"+systemKey+"/"+name, nil, creds)
	resp, err = mapResponse(resp, err)
	if err != nil {
		return nil, err
	}
	return resp.Body.([]interface{}), nil
}

func (d *DevClient) GetVersion(systemKey, name string, version int) (map[string]interface{}, error) {
	creds, err := d.credentials()
	if err != nil {
		return nil, err
	}
	resp, err := get(_HIST_PREAMBLE+"/"+systemKey+"/"+name+"/"+strconv.Itoa(version), nil, creds)
	resp, err = mapResponse(resp, err)
	if err != nil {
		return nil, err
	}
	return resp.Body.(map[string]interface{}), nil
}
<|endoftext|>


<commit_before>package finder

import (
	"errors"
	"os"
	"path/filepath"
	"regexp"

	"github.com/gobwas/glob"
)

type itemType uint8

const (
	typeAll itemType = iota
	typeFile
	typeDir
)

// Finder contains options for a file/directory search.
type Finder struct {
	dirs        []string
	names       []Matcher
	paths       []Matcher
	notPaths    []Matcher
	notNames    []Matcher
	minDepth    int
	maxDepth    int
	setupErrors []error
	itype       itemType
}

// Matcher checks whether an Item matches.
type Matcher func(Item) bool

// New returns a new finder.
//
// By default it will search for both files and directories.
func New() *Finder {
	return &Finder{maxDepth: -1}
}

// In searches in the given list of directories.
func (f *Finder) In(directories ...string) *Finder {
	f.dirs = append(f.dirs, directories...)
	return f
}

// Path narrows down the folders to be searched using gobwas/glob
//
// p is matched against the item's RelPath()
// See https://github.com/gobwas/glob
func (f *Finder) Path(p string) *Finder {
	matcher := f.path(p)
	if matcher != nil {
		f.paths = append(f.paths, matcher)
	}
	return f
}

// NotPath excludes folders from the search using gobwas/glob
//
// p is matched against the item's RelPath()
// See https://github.com/gobwas/glob
func (f *Finder) NotPath(p string) *Finder {
	matcher := f.path(p)
	if matcher != nil {
		f.notPaths = append(f.notPaths, matcher)
	}
	return f
}

func (f *Finder) path(p string) Matcher {
	g, err := glob.Compile(p, os.PathSeparator)
	if err != nil {
		f.setupErrors = append(f.setupErrors, err)
		return nil
	}
	return func(i Item) bool {
		if i.IsDir() {
			return g.Match(i.RelPath())
		}
		return g.Match(filepath.Dir(i.RelPath()))
	}
}

// Name matches a file or directory name using gobwas/glob
//
// See https://github.com/gobwas/glob
func (f *Finder) Name(n string) *Finder {
	matcher := f.name(n)
	if matcher != nil {
		f.names = append(f.names, matcher)
	}
	return f
}

func (f *Finder) name(n string) Matcher {
	g, err := glob.Compile(n, os.PathSeparator)
	if err != nil {
		f.setupErrors = append(f.setupErrors, err)
		return nil
	}
	return func(i Item) bool {
		return g.Match(i.Name())
	}
}

// NameRegex matches a file or directory name using package regexp.
func (f *Finder) NameRegex(n string) *Finder {
	matcher := f.nameRegex(n)
	if matcher != nil {
		f.names = append(f.names, matcher)
	}
	return f
}

func (f *Finder) nameRegex(n string) Matcher {
	re, err := regexp.Compile(n)
	if err != nil {
		f.setupErrors = append(f.setupErrors, err)
		return nil
	}
	return func(i Item) bool {
		return re.MatchString(i.Name())
	}
}

// NotName excludes a file or directory name using gobwas/glob
//
// See https://github.com/gobwas/glob
func (f *Finder) NotName(n string) *Finder {
	matcher := f.name(n)
	if matcher != nil {
		f.notNames = append(f.notNames, matcher)
	}
	return f
}

// NotNameRegex excludes a file or directory name using package regexp.
func (f *Finder) NotNameRegex(n string) *Finder {
	matcher := f.nameRegex(n)
	if matcher != nil {
		f.notNames = append(f.notNames, matcher)
	}
	return f
}

// Files makes the finder return files only.
func (f *Finder) Files() *Finder {
	f.itype = typeFile
	return f
}

// Dirs makes the finder return directories only.
func (f *Finder) Dirs() *Finder {
	f.itype = typeDir
	return f
}

func (f *Finder) MaxDepth(max int) *Finder {
	f.maxDepth = max
	return f
}

func (f *Finder) MinDepth(min int) *Finder {
	f.minDepth = min
	return f
}

var IsMatch error = nil
var ErrNoMatch = errors.New("Item did not match")
var ErrSkipDir = filepath.SkipDir

// fast excludes first, followed by more expensive path matching
func (f *Finder) match(i Item) error {
	if (f.itype == typeDir && !i.IsDir()) || (f.itype == typeFile && i.IsDir()) {
		return ErrNoMatch
	}
	if f.maxDepth != -1 || f.minDepth != 0 {
		depth := i.Depth()
		if f.minDepth != 0 && depth < f.minDepth {
			return ErrNoMatch
		}
		if f.maxDepth != -1 && depth > f.maxDepth {
			return ErrSkipDir
		}
	}
	var match error
	match = f.matchPaths(i)
	if match != IsMatch {
		return match
	}
	match = f.matchNotPaths(i)
	if match != IsMatch {
		return match
	}
	match = f.matchNames(i)
	if match != IsMatch {
		return match
	}
	return f.matchNotNames(i)
}

func (f *Finder) matchPaths(i Item) error {
	if len(f.paths) == 0 {
		return IsMatch
	}
	if i.IsDir() {
		for _, p := range f.paths {
			if !p(i) {
				return ErrSkipDir
			}
		}
		return IsMatch
	}
	match := ErrNoMatch
	for _, p := range f.paths {
		if p(i) {
			match = IsMatch
		}
	}
	return match
}

func (f *Finder) matchNotPaths(i Item) error {
	if len(f.notPaths) == 0 {
		return IsMatch
	}
	if i.IsDir() {
		for _, p := range f.notPaths {
			if p(i) {
				return ErrSkipDir
			}
		}
		return IsMatch
	}
	for _, p := range f.notPaths {
		if p(i) {
			return ErrNoMatch
		}
	}
	return IsMatch
}

func (f *Finder) matchNames(i Item) error {
	if len(f.names) == 0 {
		return IsMatch
	}
	for _, n := range f.names {
		if n(i) {
			return IsMatch
		}
	}
	return ErrNoMatch
}

func (f *Finder) matchNotNames(i Item) error {
	if len(f.notNames) == 0 {
		return IsMatch
	}
	for _, n := range f.notNames {
		if n(i) {
			return ErrNoMatch
		}
	}
	return IsMatch
}

// Each calls func fn with each found item.
func (f *Finder) Each(fn func(Item)) []error {
	var errs []error
	var dir string
	walker := func(path string, info os.FileInfo, err error) error {
		if dir == path {
			return nil
		}
		if err != nil {
			return err
		}
		relDir, err := filepath.Rel(dir, path)
		if err != nil {
			return err
		}
		item := newItem(info, dir, relDir)
		match := f.match(item)
		switch match {
		case IsMatch:
			fn(item)
			return nil
		case ErrNoMatch:
			return nil
		case ErrSkipDir:
			return ErrSkipDir
		}
		return nil
	}
	for _, dir = range f.dirs {
		err := filepath.Walk(dir, walker)
		if err != nil && err != ErrSkipDir {
			errs = append(errs, err)
		}
	}
	return append(f.setupErrors, errs...)
}

// ToSlice returns a slice of all found items.
func (f *Finder) ToSlice() (ItemSlice, []error) {
	var l []Item
	errs := f.Each(func(file Item) {
		l = append(l, file)
	})
	return l, errs
}
<commit_msg>Extract matchDepth<commit_after>package finder

import (
	"errors"
	"os"
	"path/filepath"
	"regexp"

	"github.com/gobwas/glob"
)

type itemType uint8

const (
	typeAll itemType = iota
	typeFile
	typeDir
)

// Finder contains options for a file/directory search.
type Finder struct {
	dirs        []string
	names       []Matcher
	paths       []Matcher
	notPaths    []Matcher
	notNames    []Matcher
	minDepth    int
	maxDepth    int
	setupErrors []error
	itype       itemType
}

// Matcher checks whether an Item matches.
type Matcher func(Item) bool

// New returns a new finder.
//
// By default it will search for both files and directories.
func New() *Finder {
	return &Finder{maxDepth: -1}
}

// In searches in the given list of directories.
func (f *Finder) In(directories ...string) *Finder {
	f.dirs = append(f.dirs, directories...)
	return f
}

// Path narrows down the folders to be searched using gobwas/glob
//
// p is matched against the item's RelPath()
// See https://github.com/gobwas/glob
func (f *Finder) Path(p string) *Finder {
	matcher := f.path(p)
	if matcher != nil {
		f.paths = append(f.paths, matcher)
	}
	return f
}

// NotPath excludes folders from the search using gobwas/glob
//
// p is matched against the item's RelPath()
// See https://github.com/gobwas/glob
func (f *Finder) NotPath(p string) *Finder {
	matcher := f.path(p)
	if matcher != nil {
		f.notPaths = append(f.notPaths, matcher)
	}
	return f
}

func (f *Finder) path(p string) Matcher {
	g, err := glob.Compile(p, os.PathSeparator)
	if err != nil {
		f.setupErrors = append(f.setupErrors, err)
		return nil
	}
	return func(i Item) bool {
		if i.IsDir() {
			return g.Match(i.RelPath())
		}
		return g.Match(filepath.Dir(i.RelPath()))
	}
}

// Name matches a file or directory name using gobwas/glob
//
// See https://github.com/gobwas/glob
func (f *Finder) Name(n string) *Finder {
	matcher := f.name(n)
	if matcher != nil {
		f.names = append(f.names, matcher)
	}
	return f
}

func (f *Finder) name(n string) Matcher {
	g, err := glob.Compile(n, os.PathSeparator)
	if err != nil {
		f.setupErrors = append(f.setupErrors, err)
		return nil
	}
	return func(i Item) bool {
		return g.Match(i.Name())
	}
}

// NameRegex matches a file or directory name using package regexp.
func (f *Finder) NameRegex(n string) *Finder {
	matcher := f.nameRegex(n)
	if matcher != nil {
		f.names = append(f.names, matcher)
	}
	return f
}

func (f *Finder) nameRegex(n string) Matcher {
	re, err := regexp.Compile(n)
	if err != nil {
		f.setupErrors = append(f.setupErrors, err)
		return nil
	}
	return func(i Item) bool {
		return re.MatchString(i.Name())
	}
}

// NotName excludes a file or directory name using gobwas/glob
//
// See https://github.com/gobwas/glob
func (f *Finder) NotName(n string) *Finder {
	matcher := f.name(n)
	if matcher != nil {
		f.notNames = append(f.notNames, matcher)
	}
	return f
}

// NotNameRegex excludes a file or directory name using package regexp.
func (f *Finder) NotNameRegex(n string) *Finder {
	matcher := f.nameRegex(n)
	if matcher != nil {
		f.notNames = append(f.notNames, matcher)
	}
	return f
}

// Files makes the finder return files only.
func (f *Finder) Files() *Finder {
	f.itype = typeFile
	return f
}

// Dirs makes the finder return directories only.
func (f *Finder) Dirs() *Finder {
	f.itype = typeDir
	return f
}

func (f *Finder) MaxDepth(max int) *Finder {
	f.maxDepth = max
	return f
}

func (f *Finder) MinDepth(min int) *Finder {
	f.minDepth = min
	return f
}

var IsMatch error = nil
var ErrNoMatch = errors.New("Item did not match")
var ErrSkipDir = filepath.SkipDir

// fast excludes first, followed by more expensive path matching
func (f *Finder) match(i Item) error {
	if (f.itype == typeDir && !i.IsDir()) || (f.itype == typeFile && i.IsDir()) {
		return ErrNoMatch
	}
	match := f.matchDepth(i)
	if match != IsMatch {
		return match
	}
	match = f.matchPaths(i)
	if match != IsMatch {
		return match
	}
	match = f.matchNotPaths(i)
	if match != IsMatch {
		return match
	}
	match = f.matchNames(i)
	if match != IsMatch {
		return match
	}
	return f.matchNotNames(i)
}

func (f *Finder) matchDepth(i Item) error {
	if f.maxDepth == -1 && f.minDepth == 0 {
		return IsMatch
	}
	depth := i.Depth()
	if f.minDepth != 0 && depth < f.minDepth {
		return ErrNoMatch
	}
	if f.maxDepth != -1 && depth > f.maxDepth {
		return ErrSkipDir
	}
	return IsMatch
}

func (f *Finder) matchPaths(i Item) error {
	if len(f.paths) == 0 {
		return IsMatch
	}
	if i.IsDir() {
		for _, p := range f.paths {
			if !p(i) {
				return ErrSkipDir
			}
		}
		return IsMatch
	}
	match := ErrNoMatch
	for _, p := range f.paths {
		if p(i) {
			match = IsMatch
		}
	}
	return match
}

func (f *Finder) matchNotPaths(i Item) error {
	if len(f.notPaths) == 0 {
		return IsMatch
	}
	if i.IsDir() {
		for _, p := range f.notPaths {
			if p(i) {
				return ErrSkipDir
			}
		}
		return IsMatch
	}
	for _, p := range f.notPaths {
		if p(i) {
			return ErrNoMatch
		}
	}
	return IsMatch
}

func (f *Finder) matchNames(i Item) error {
	if len(f.names) == 0 {
		return IsMatch
	}
	for _, n := range f.names {
		if n(i) {
			return IsMatch
		}
	}
	return ErrNoMatch
}

func (f *Finder) matchNotNames(i Item) error {
	if len(f.notNames) == 0 {
		return IsMatch
	}
	for _, n := range f.notNames {
		if n(i) {
			return ErrNoMatch
		}
	}
	return IsMatch
}

// Each calls func fn with each found item.
func (f *Finder) Each(fn func(Item)) []error {
	var errs []error
	var dir string
	walker := func(path string, info os.FileInfo, err error) error {
		if dir == path {
			return nil
		}
		if err != nil {
			return err
		}
		relDir, err := filepath.Rel(dir, path)
		if err != nil {
			return err
		}
		item := newItem(info, dir, relDir)
		match := f.match(item)
		switch match {
		case IsMatch:
			fn(item)
			return nil
		case ErrNoMatch:
			return nil
		case ErrSkipDir:
			return ErrSkipDir
		}
		return nil
	}
	for _, dir = range f.dirs {
		err := filepath.Walk(dir, walker)
		if err != nil && err != ErrSkipDir {
			errs = append(errs, err)
		}
	}
	return append(f.setupErrors, errs...)
}

// ToSlice returns a slice of all found items.
func (f *Finder) ToSlice() (ItemSlice, []error) {
	var l []Item
	errs := f.Each(func(file Item) {
		l = append(l, file)
	})
	return l, errs
}
<|endoftext|>


<commit_before>// Copyright 2022 Brian J. Downs
//
// 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 openweathermap

import (
	"errors"
	"net/http"
	"strings"
)

var errUnitUnavailable = errors.New("unit unavailable")
var errLangUnavailable = errors.New("language unavailable")
var errInvalidKey = errors.New("invalid api key")
var errInvalidOption = errors.New("invalid option")
var errInvalidHttpClient = errors.New("invalid http client")
var errForecastUnavailable = errors.New("forecast unavailable")
var errExcludesUnavailable = errors.New("onecall excludes unavailable")
var errCountOfCityIDs      = errors.New("count of ids should not be more than 20 per request")

// DataUnits represents the character chosen to represent the temperature notation
var DataUnits = map[string]string{"C": "metric", "F": "imperial", "K": "internal"}
var (
	baseURL        = "https://api.openweathermap.org/data/2.5/weather?%s"
	onecallURL     = "https://api.openweathermap.org/data/2.5/onecall?%s"
	iconURL        = "https://openweathermap.org/img/w/%s"
  groupURL       = "http://api.openweathermap.org/data/2.5/group?%s"
	stationURL     = "https://api.openweathermap.org/data/2.5/station?id=%d"
	forecast5Base  = "https://api.openweathermap.org/data/2.5/forecast?appid=%s&%s&mode=json&units=%s&lang=%s&cnt=%d"
	forecast16Base = "https://api.openweathermap.org/data/2.5/forecast/daily?appid=%s&%s&mode=json&units=%s&lang=%s&cnt=%d"
	historyURL     = "https://api.openweathermap.org/data/2.5/history/%s"
	pollutionURL   = "https://api.openweathermap.org/pollution/v1/co/"
	uvURL          = "https://api.openweathermap.org/data/2.5/"
	dataPostURL    = "https://openweathermap.org/data/post"
)

// LangCodes holds all supported languages to be used
// inspried and sourced from @bambocher (github.com/bambocher)
var LangCodes = map[string]string{
	"AF":    "Afrikaans",
	"AL":    "Albanian",
	"AR":    "Arabic",
	"AZ":    "Azerbaijani",
	"BG":    "Bulgarian",
	"CA":    "Catalan",
	"CZ":    "Czech",
	"DA":    "Danish",
	"DE":    "German",
	"EL":    "Greek",
	"EN":    "English",
	"ES":    "Spanish",
	"EU":    "Basque",
	"FA":    "Persian (Farsi)",
	"FI":    "Finnish",
	"FR":    "French",
	"GL":    "Galician",
	"HE":    "Hebrew",
	"HI":    "Hindi",
	"HR":    "Croatian",
	"HU":    "Hungarian",
	"ID":    "Indonesian",
	"IT":    "Italian",
	"JA":    "Japanese",
	"KR":    "Korean",
	"LA":    "Latvian",
	"LT":    "Lithuanian",
	"MK":    "Macedonian",
	"NL":    "Dutch",
	"NO":    "Norwegian",
	"PL":    "Polish",
	"PT":    "Portuguese",
	"PT_BR": "Português Brasil",
	"RO":    "Romanian",
	"RU":    "Russian",
	"SE":    "Swedish",
	"SK":    "Slovak",
	"SL":    "Slovenian",
	"SP":    "Spanish",
	"SR":    "Serbian",
	"SV":    "Swedish",
	"TH":    "Thai",
	"TR":    "Turkish",
	"UA":    "Ukrainian",
	"UK":    "Ukrainian",
	"VI":    "Vietnamese",
	"ZH_CN": "Chinese Simplified",
	"ZH_TW": "Chinese Traditional",
	"ZU":    "Zulu",
}

// Exclude holds all supported excludes option to be used
const (
	ExcludeCurrent  = "current"
	ExcludeMinutely = "minutely"
	ExcludeHourly   = "hourly"
	ExcludeDaily    = "daily"
	ExcludeAlerts   = "alerts"
)

var Excludes []string = []string{ExcludeCurrent, ExcludeMinutely, ExcludeHourly, ExcludeDaily, ExcludeAlerts}

// Config will hold default settings to be passed into the
// "NewCurrent, NewForecast, etc}" functions.
type Config struct {
	Mode     string // user choice of JSON or XML
	Unit     string // measurement for results to be displayed.  F, C, or K
	Lang     string // should reference a key in the LangCodes map
	APIKey   string // API Key for connecting to the OWM
	Username string // Username for posting data
	Password string // Pasword for posting data
}

// APIError returned on failed API calls.
type APIError struct {
	Message string `json:"message"`
	COD     string `json:"cod"`
}

// Coordinates struct holds longitude and latitude data in returned
// JSON or as parameter data for requests using longitude and latitude.
type Coordinates struct {
	Longitude float64 `json:"lon"`
	Latitude  float64 `json:"lat"`
}

// Sys struct contains general information about the request
// and the surrounding area for where the request was made.
type Sys struct {
	Type    int     `json:"type"`
	ID      int     `json:"id"`
	Message float64 `json:"message"`
	Country string  `json:"country"`
	Sunrise int     `json:"sunrise"`
	Sunset  int     `json:"sunset"`
}

// Wind struct contains the speed and degree of the wind.
type Wind struct {
	Speed float64 `json:"speed"`
	Deg   float64 `json:"deg"`
}

// Weather struct holds high-level, basic info on the returned
// data.
type Weather struct {
	ID          int    `json:"id"`
	Main        string `json:"main"`
	Description string `json:"description"`
	Icon        string `json:"icon"`
}

// Main struct contains the temperates, humidity, pressure for the request.
type Main struct {
	Temp      float64 `json:"temp"`
	TempMin   float64 `json:"temp_min"`
	TempMax   float64 `json:"temp_max"`
	FeelsLike float64 `json:"feels_like"`
	Pressure  float64 `json:"pressure"`
	SeaLevel  float64 `json:"sea_level"`
	GrndLevel float64 `json:"grnd_level"`
	Humidity  int     `json:"humidity"`
}

// Clouds struct holds data regarding cloud cover.
type Clouds struct {
	All int `json:"all"`
}

// 	return key
// }
func setKey(key string) (string, error) {
	if err := ValidAPIKey(key); err != nil {
		return "", err
	}
	return key, nil
}

// ValidDataUnit makes sure the string passed in is an accepted
// unit of measure to be used for the return data.
func ValidDataUnit(u string) bool {
	for d := range DataUnits {
		if u == d {
			return true
		}
	}
	return false
}

// ValidLangCode makes sure the string passed in is an
// acceptable lang code.
func ValidLangCode(c string) bool {
	for d := range LangCodes {
		if c == d {
			return true
		}
	}
	return false
}

// ValidDataUnitSymbol makes sure the string passed in is an
// acceptable data unit symbol.
func ValidDataUnitSymbol(u string) bool {
	for _, d := range DataUnits {
		if u == d {
			return true
		}
	}
	return false
}

// ValidExcludes makes sure the string passed in is an
// acceptable excludes options.
func ValidExcludes(e []string) (string, error) {
	list := make([]string, 0)
	for _, v := range e {
		vl := strings.ToLower(v)
		notFound := true

		for _, d := range Excludes {
			if d == vl {
				list = append(list, v)
				notFound = false
				break
			}
		}

		if notFound {
			return "", errExcludesUnavailable
		}
	}
	return strings.Join(list, ","), nil
}

// ValidAPIKey makes sure that the key given is a valid one
func ValidAPIKey(key string) error {
	if len(key) > 64 {
		return errors.New("invalid key")
	}
	return nil
}

// CheckAPIKeyExists will see if an API key has been set.
func (c *Config) CheckAPIKeyExists() bool { return len(c.APIKey) > 1 }

// Settings holds the client settings
type Settings struct {
	client *http.Client
}

// NewSettings returns a new Setting pointer with default http client.
func NewSettings() *Settings {
	return &Settings{
		client: http.DefaultClient,
	}
}

// Optional client settings
type Option func(s *Settings) error

// WithHttpClient sets custom http client when creating a new Client.
func WithHttpClient(c *http.Client) Option {
	return func(s *Settings) error {
		if c == nil {
			return errInvalidHttpClient
		}
		s.client = c
		return nil
	}
}

// setOptions sets Optional client settings to the Settings pointer
func setOptions(settings *Settings, options []Option) error {
	for _, option := range options {
		if option == nil {
			return errInvalidOption
		}
		err := option(settings)
		if err != nil {
			return err
		}
	}
	return nil
}
<commit_msg>error use, formatting<commit_after>// Copyright 2022 Brian J. Downs
//
// 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 openweathermap

import (
	"errors"
	"net/http"
	"strings"
)

var (
	errUnitUnavailable     = errors.New("unit unavailable")
	errLangUnavailable     = errors.New("language unavailable")
	errInvalidKey          = errors.New("invalid api key")
	errInvalidOption       = errors.New("invalid option")
	errInvalidHttpClient   = errors.New("invalid http client")
	errForecastUnavailable = errors.New("forecast unavailable")
	errExcludesUnavailable = errors.New("onecall excludes unavailable")
	errCountOfCityIDs      = errors.New("count of ids should not be more than 20 per request")
)

// DataUnits represents the character chosen to represent the temperature notation
var DataUnits = map[string]string{"C": "metric", "F": "imperial", "K": "internal"}
var (
	baseURL        = "https://api.openweathermap.org/data/2.5/weather?%s"
	onecallURL     = "https://api.openweathermap.org/data/2.5/onecall?%s"
	iconURL        = "https://openweathermap.org/img/w/%s"
	groupURL       = "http://api.openweathermap.org/data/2.5/group?%s"
	stationURL     = "https://api.openweathermap.org/data/2.5/station?id=%d"
	forecast5Base  = "https://api.openweathermap.org/data/2.5/forecast?appid=%s&%s&mode=json&units=%s&lang=%s&cnt=%d"
	forecast16Base = "https://api.openweathermap.org/data/2.5/forecast/daily?appid=%s&%s&mode=json&units=%s&lang=%s&cnt=%d"
	historyURL     = "https://api.openweathermap.org/data/2.5/history/%s"
	pollutionURL   = "https://api.openweathermap.org/pollution/v1/co/"
	uvURL          = "https://api.openweathermap.org/data/2.5/"
	dataPostURL    = "https://openweathermap.org/data/post"
)

// LangCodes holds all supported languages to be used
// inspried and sourced from @bambocher (github.com/bambocher)
var LangCodes = map[string]string{
	"AF":    "Afrikaans",
	"AL":    "Albanian",
	"AR":    "Arabic",
	"AZ":    "Azerbaijani",
	"BG":    "Bulgarian",
	"CA":    "Catalan",
	"CZ":    "Czech",
	"DA":    "Danish",
	"DE":    "German",
	"EL":    "Greek",
	"EN":    "English",
	"ES":    "Spanish",
	"EU":    "Basque",
	"FA":    "Persian (Farsi)",
	"FI":    "Finnish",
	"FR":    "French",
	"GL":    "Galician",
	"HE":    "Hebrew",
	"HI":    "Hindi",
	"HR":    "Croatian",
	"HU":    "Hungarian",
	"ID":    "Indonesian",
	"IT":    "Italian",
	"JA":    "Japanese",
	"KR":    "Korean",
	"LA":    "Latvian",
	"LT":    "Lithuanian",
	"MK":    "Macedonian",
	"NL":    "Dutch",
	"NO":    "Norwegian",
	"PL":    "Polish",
	"PT":    "Portuguese",
	"PT_BR": "Português Brasil",
	"RO":    "Romanian",
	"RU":    "Russian",
	"SE":    "Swedish",
	"SK":    "Slovak",
	"SL":    "Slovenian",
	"SP":    "Spanish",
	"SR":    "Serbian",
	"SV":    "Swedish",
	"TH":    "Thai",
	"TR":    "Turkish",
	"UA":    "Ukrainian",
	"UK":    "Ukrainian",
	"VI":    "Vietnamese",
	"ZH_CN": "Chinese Simplified",
	"ZH_TW": "Chinese Traditional",
	"ZU":    "Zulu",
}

// Exclude holds all supported excludes option to be used
const (
	ExcludeCurrent  = "current"
	ExcludeMinutely = "minutely"
	ExcludeHourly   = "hourly"
	ExcludeDaily    = "daily"
	ExcludeAlerts   = "alerts"
)

var Excludes []string = []string{
	ExcludeCurrent,
	ExcludeMinutely,
	ExcludeHourly,
	ExcludeDaily,
	ExcludeAlerts,
}

// Config will hold default settings to be passed into the
// "NewCurrent, NewForecast, etc}" functions.
type Config struct {
	Mode     string // user choice of JSON or XML
	Unit     string // measurement for results to be displayed.  F, C, or K
	Lang     string // should reference a key in the LangCodes map
	APIKey   string // API Key for connecting to the OWM
	Username string // Username for posting data
	Password string // Pasword for posting data
}

// APIError returned on failed API calls.
type APIError struct {
	Message string `json:"message"`
	COD     string `json:"cod"`
}

// Coordinates struct holds longitude and latitude data in returned
// JSON or as parameter data for requests using longitude and latitude.
type Coordinates struct {
	Longitude float64 `json:"lon"`
	Latitude  float64 `json:"lat"`
}

// Sys struct contains general information about the request
// and the surrounding area for where the request was made.
type Sys struct {
	Type    int     `json:"type"`
	ID      int     `json:"id"`
	Message float64 `json:"message"`
	Country string  `json:"country"`
	Sunrise int     `json:"sunrise"`
	Sunset  int     `json:"sunset"`
}

// Wind struct contains the speed and degree of the wind.
type Wind struct {
	Speed float64 `json:"speed"`
	Deg   float64 `json:"deg"`
}

// Weather struct holds high-level, basic info on the returned
// data.
type Weather struct {
	ID          int    `json:"id"`
	Main        string `json:"main"`
	Description string `json:"description"`
	Icon        string `json:"icon"`
}

// Main struct contains the temperates, humidity, pressure for the request.
type Main struct {
	Temp      float64 `json:"temp"`
	TempMin   float64 `json:"temp_min"`
	TempMax   float64 `json:"temp_max"`
	FeelsLike float64 `json:"feels_like"`
	Pressure  float64 `json:"pressure"`
	SeaLevel  float64 `json:"sea_level"`
	GrndLevel float64 `json:"grnd_level"`
	Humidity  int     `json:"humidity"`
}

// Clouds struct holds data regarding cloud cover.
type Clouds struct {
	All int `json:"all"`
}

// 	return key
// }
func setKey(key string) (string, error) {
	if err := ValidAPIKey(key); err != nil {
		return "", err
	}
	return key, nil
}

// ValidDataUnit makes sure the string passed in is an accepted
// unit of measure to be used for the return data.
func ValidDataUnit(u string) bool {
	for d := range DataUnits {
		if u == d {
			return true
		}
	}
	return false
}

// ValidLangCode makes sure the string passed in is an
// acceptable lang code.
func ValidLangCode(c string) bool {
	for d := range LangCodes {
		if c == d {
			return true
		}
	}
	return false
}

// ValidDataUnitSymbol makes sure the string passed in is an
// acceptable data unit symbol.
func ValidDataUnitSymbol(u string) bool {
	for _, d := range DataUnits {
		if u == d {
			return true
		}
	}
	return false
}

// ValidExcludes makes sure the string passed in is an
// acceptable excludes options.
func ValidExcludes(e []string) (string, error) {
	list := make([]string, 0)
	for _, v := range e {
		vl := strings.ToLower(v)
		notFound := true

		for _, d := range Excludes {
			if d == vl {
				list = append(list, v)
				notFound = false
				break
			}
		}

		if notFound {
			return "", errExcludesUnavailable
		}
	}
	return strings.Join(list, ","), nil
}

// ValidAPIKey makes sure that the key given is a valid one
func ValidAPIKey(key string) error {
	if len(key) > 64 {
		return errInvalidKey
	}
	return nil
}

// CheckAPIKeyExists will see if an API key has been set.
func (c *Config) CheckAPIKeyExists() bool { return len(c.APIKey) > 1 }

// Settings holds the client settings
type Settings struct {
	client *http.Client
}

// NewSettings returns a new Setting pointer with default http client.
func NewSettings() *Settings {
	return &Settings{
		client: http.DefaultClient,
	}
}

// Optional client settings
type Option func(s *Settings) error

// WithHttpClient sets custom http client when creating a new Client.
func WithHttpClient(c *http.Client) Option {
	return func(s *Settings) error {
		if c == nil {
			return errInvalidHttpClient
		}
		s.client = c
		return nil
	}
}

// setOptions sets Optional client settings to the Settings pointer
func setOptions(settings *Settings, options []Option) error {
	for _, option := range options {
		if option == nil {
			return errInvalidOption
		}
		err := option(settings)
		if err != nil {
			return err
		}
	}
	return nil
}
<|endoftext|>