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

package grumpy

import (
	"reflect"
)

// CodeType is the object representing the Python 'code' type.
var CodeType = newBasisType("code", reflect.TypeOf(Code{}), toCodeUnsafe, ObjectType)

// CodeFlag is a switch controlling the behavior of a Code object.
type CodeFlag int

const (
	// CodeFlagVarArg means a Code object accepts *arg parameters.
	CodeFlagVarArg CodeFlag = 4
	// CodeFlagKWArg means a Code object accepts **kwarg parameters.
	CodeFlagKWArg CodeFlag = 8
)

// Code represents Python 'code' objects.
type Code struct {
	Object
	name     string `attr:"co_name"`
	filename string `attr:"co_filename"`
	// argc is the number of positional arguments.
	argc      int      `attr:"co_argcount"`
	flags     CodeFlag `attr:"co_flags"`
	paramSpec *ParamSpec
	fn        func(*Frame, []*Object) (*Object, *BaseException)
}

// NewCode creates a new Code object that executes the given fn.
func NewCode(name, filename string, params []Param, flags CodeFlag, fn func(*Frame, []*Object) (*Object, *BaseException)) *Code {
	s := NewParamSpec(name, params, flags&CodeFlagVarArg != 0, flags&CodeFlagKWArg != 0)
	return &Code{Object{typ: CodeType}, name, filename, len(params), flags, s, fn}
}

func toCodeUnsafe(o *Object) *Code {
	return (*Code)(o.toPointer())
}

// Eval runs the code object c in the context of the given globals.
func (c *Code) Eval(f *Frame, globals *Dict, args Args, kwargs KWArgs) (*Object, *BaseException) {
	validated := f.MakeArgs(c.paramSpec.Count)
	if raised := c.paramSpec.Validate(f, validated, args, kwargs); raised != nil {
		return nil, raised
	}
	oldExc, oldTraceback := f.ExcInfo()
	next := newChildFrame(f)
	next.code = c
	next.globals = globals
	ret, raised := c.fn(next, validated)
	next.release()
	f.FreeArgs(validated)
	if raised == nil {
		// Restore exc_info to what it was when we left the previous
		// frame.
		f.RestoreExc(oldExc, oldTraceback)
		if ret == nil {
			ret = None
		}
	} else {
		_, tb := f.ExcInfo()
		tb = newTraceback(f, tb)
		f.RestoreExc(raised, tb)
	}
	return ret, raised
}
<commit_msg>Fix tracebacks (#316)<commit_after>// Copyright 2016 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package grumpy

import (
	"reflect"
)

// CodeType is the object representing the Python 'code' type.
var CodeType = newBasisType("code", reflect.TypeOf(Code{}), toCodeUnsafe, ObjectType)

// CodeFlag is a switch controlling the behavior of a Code object.
type CodeFlag int

const (
	// CodeFlagVarArg means a Code object accepts *arg parameters.
	CodeFlagVarArg CodeFlag = 4
	// CodeFlagKWArg means a Code object accepts **kwarg parameters.
	CodeFlagKWArg CodeFlag = 8
)

// Code represents Python 'code' objects.
type Code struct {
	Object
	name     string `attr:"co_name"`
	filename string `attr:"co_filename"`
	// argc is the number of positional arguments.
	argc      int      `attr:"co_argcount"`
	flags     CodeFlag `attr:"co_flags"`
	paramSpec *ParamSpec
	fn        func(*Frame, []*Object) (*Object, *BaseException)
}

// NewCode creates a new Code object that executes the given fn.
func NewCode(name, filename string, params []Param, flags CodeFlag, fn func(*Frame, []*Object) (*Object, *BaseException)) *Code {
	s := NewParamSpec(name, params, flags&CodeFlagVarArg != 0, flags&CodeFlagKWArg != 0)
	return &Code{Object{typ: CodeType}, name, filename, len(params), flags, s, fn}
}

func toCodeUnsafe(o *Object) *Code {
	return (*Code)(o.toPointer())
}

// Eval runs the code object c in the context of the given globals.
func (c *Code) Eval(f *Frame, globals *Dict, args Args, kwargs KWArgs) (*Object, *BaseException) {
	validated := f.MakeArgs(c.paramSpec.Count)
	if raised := c.paramSpec.Validate(f, validated, args, kwargs); raised != nil {
		return nil, raised
	}
	oldExc, oldTraceback := f.ExcInfo()
	next := newChildFrame(f)
	next.code = c
	next.globals = globals
	ret, raised := c.fn(next, validated)
	next.release()
	f.FreeArgs(validated)
	if raised == nil {
		// Restore exc_info to what it was when we left the previous
		// frame.
		f.RestoreExc(oldExc, oldTraceback)
		if ret == nil {
			ret = None
		}
	} else {
		_, tb := f.ExcInfo()
		if f.code != nil {
			// The root frame has no code object so don't include it
			// in the traceback.
			tb = newTraceback(f, tb)
		}
		f.RestoreExc(raised, tb)
	}
	return ret, raised
}
<|endoftext|>


<commit_before>package sentinel

import (
	"strconv"

	"github.com/mdevilliers/redishappy/services/logger"
	"github.com/mdevilliers/redishappy/services/redis"
	"github.com/mdevilliers/redishappy/types"
)

type SentinelClient struct {
	sentinel       types.Sentinel
	redisClient    redis.RedisClient
	sleepInSeconds int
}

func NewSentinelClient(sentinel types.Sentinel, redisConnection redis.RedisConnection) (*SentinelClient, error) {

	uri := sentinel.GetLocation()
	logger.Info.Printf("SentinelClient : connecting to %s", uri)

	redisclient, err := redisConnection.GetConnection("tcp", uri)

	if err != nil {
		logger.Info.Printf("SentinelClient : not connected to %s, %s", uri, err.Error())
		return nil, err
	}

	logger.Info.Printf("SentinelClient : connected to %s", uri)

	client := &SentinelClient{redisClient: redisclient,
		sentinel:       sentinel,
		sleepInSeconds: 1}
	return client, nil
}

func (m *SentinelClient) Close() {
	m.redisClient.Close()
}

func (m *SentinelClient) DiscoverMasterForCluster(clusterName string) (*types.MasterDetails, error) {

	r := m.redisClient.Cmd("SENTINEL", "get-master-addr-by-name", clusterName)

	bits, err := r.List()

	if err == nil {
		port, _ := strconv.Atoi(bits[1])
		return &types.MasterDetails{Name: clusterName, Ip: bits[0], Port: port}, nil
	}

	return &types.MasterDetails{}, err
}

func (client *SentinelClient) FindConnectedSentinels(clustername string) []types.Sentinel {

	r := client.redisClient.Cmd("SENTINEL", "SENTINELS", clustername)
	response := []types.Sentinel{}

	for _, e := range r.Elems() {

		t, _ := e.Hash()

		port, _ := strconv.Atoi(t["port"])
		response = append(response, types.Sentinel{Host: t["ip"], Port: port})
	}
	return response
}

func (client *SentinelClient) FindKnownClusters() []string {

	r := client.redisClient.Cmd("SENTINEL", "MASTERS")
	response := []string{}

	for _, e := range r.Elems() {

		t, _ := e.Hash()
		response = append(response, t["name"])
	}
	return response
}
<commit_msg>remove unused field<commit_after>package sentinel

import (
	"strconv"

	"github.com/mdevilliers/redishappy/services/logger"
	"github.com/mdevilliers/redishappy/services/redis"
	"github.com/mdevilliers/redishappy/types"
)

type SentinelClient struct {
	sentinel    types.Sentinel
	redisClient redis.RedisClient
}

func NewSentinelClient(sentinel types.Sentinel, redisConnection redis.RedisConnection) (*SentinelClient, error) {

	uri := sentinel.GetLocation()
	logger.Info.Printf("SentinelClient : connecting to %s", uri)

	redisclient, err := redisConnection.GetConnection("tcp", uri)

	if err != nil {
		logger.Info.Printf("SentinelClient : not connected to %s, %s", uri, err.Error())
		return nil, err
	}

	logger.Info.Printf("SentinelClient : connected to %s", uri)

	client := &SentinelClient{redisClient: redisclient,
		sentinel: sentinel}
	return client, nil
}

func (m *SentinelClient) Close() {
	m.redisClient.Close()
}

func (m *SentinelClient) DiscoverMasterForCluster(clusterName string) (*types.MasterDetails, error) {

	r := m.redisClient.Cmd("SENTINEL", "get-master-addr-by-name", clusterName)

	bits, err := r.List()

	if err == nil {
		port, _ := strconv.Atoi(bits[1])
		return &types.MasterDetails{Name: clusterName, Ip: bits[0], Port: port}, nil
	}

	return &types.MasterDetails{}, err
}

func (client *SentinelClient) FindConnectedSentinels(clustername string) []types.Sentinel {

	r := client.redisClient.Cmd("SENTINEL", "SENTINELS", clustername)
	response := []types.Sentinel{}

	for _, e := range r.Elems() {

		t, _ := e.Hash()

		port, _ := strconv.Atoi(t["port"])
		response = append(response, types.Sentinel{Host: t["ip"], Port: port})
	}
	return response
}

func (client *SentinelClient) FindKnownClusters() []string {

	r := client.redisClient.Cmd("SENTINEL", "MASTERS")
	response := []string{}

	for _, e := range r.Elems() {

		t, _ := e.Hash()
		response = append(response, t["name"])
	}
	return response
}
<|endoftext|>


<commit_before>// fsync benchmark
package main

import (
	"flag"
	"io/ioutil"
	"log"
	"os"
	"time"
)

var pN = flag.Int("n", 100, "Iterations to test - default 100")

func main() {
	flag.Parse()
	out, err := ioutil.TempFile("", "fsyncbench")
	if err != nil {
		log.Fatal(err)
	}
	err = os.Remove(out.Name())
	if err != nil {
		log.Fatal(err)
	}

	buf := []byte{'A'}
	N := *pN
	duration := time.Duration(0)
	for i := 0; i < N; i++ {
		_, err = out.Write(buf)
		if err != nil {
			log.Fatal(err)
		}
		start := time.Now()
		err = out.Sync()
		end := time.Now()
		duration += end.Sub(start)
		if err != nil {
			log.Fatal(err)
		}
	}

	log.Printf("That took %s for %d fsyncs", duration, N)
	log.Printf("That took %s per fsync", duration/time.Duration(N))

	err = out.Close()
	if err != nil {
		log.Fatal(err)
	}
}
<commit_msg>Fix running on Windows<commit_after>// fsync benchmark
package main

import (
	"flag"
	"io/ioutil"
	"log"
	"os"
	"time"
)

var pN = flag.Int("n", 100, "Iterations to test - default 100")

func main() {
	flag.Parse()
	out, err := ioutil.TempFile("", "fsyncbench")
	if err != nil {
		log.Fatal(err)
	}
	defer func() {
		err = out.Close()
		if err != nil {
			log.Print(err)
		}
		err = os.Remove(out.Name())
		if err != nil {
			log.Print(err)
		}
	}()

	buf := []byte{'A'}
	N := *pN
	duration := time.Duration(0)
	for i := 0; i < N; i++ {
		_, err = out.Write(buf)
		if err != nil {
			log.Fatal(err)
		}
		start := time.Now()
		err = out.Sync()
		end := time.Now()
		duration += end.Sub(start)
		if err != nil {
			log.Fatal(err)
		}
	}

	log.Printf("That took %s for %d fsyncs", duration, N)
	log.Printf("That took %s per fsync", duration/time.Duration(N))
}
<|endoftext|>


<commit_before>package pegomock_test

import (
	"fmt"
	"reflect"

	"github.com/onsi/gomega/format"
	"github.com/onsi/gomega/types"
	"github.com/petergtz/pegomock/internal/verify"
)

type PanicWithMatcher struct {
	expectedWith interface{}
	actualWith   interface{}
}

func PanicWith(object interface{}) types.GomegaMatcher {
	verify.NotNil(object, "You must provide a non-nil object to PanicWith")
	return &PanicWithMatcher{expectedWith: object}
}

func (matcher *PanicWithMatcher) Match(actual interface{}) (success bool, err error) {
	if actual == nil {
		return false, fmt.Errorf("PanicWithMatcher expects a non-nil actual.")
	}

	actualType := reflect.TypeOf(actual)
	if actualType.Kind() != reflect.Func {
		return false, fmt.Errorf("PanicWithMatcher expects a function.  Got:\n%s", format.Object(actual, 1))
	}
	if !(actualType.NumIn() == 0 && actualType.NumOut() == 0) {
		return false, fmt.Errorf("PanicWithMatcher expects a function with no arguments and no return value.  Got:\n%s", format.Object(actual, 1))
	}

	success = false
	defer func() {
		if object := recover(); object == matcher.expectedWith {
			success = true
		} else {
			matcher.actualWith = object
		}
	}()

	reflect.ValueOf(actual).Call([]reflect.Value{})

	return
}

func (matcher *PanicWithMatcher) FailureMessage(actual interface{}) (message string) {
	if matcher.actualWith == "" {
		return format.Message(actual, "to panic")
	} else {
		// TODO: can we reuse format.Message somehow?
		return fmt.Sprintf("Expected\n\t<func ()>: %v\n\tpanicking with <%T>: %v\n\nto panic with\n\t<%T>: %v",
			actual, matcher.actualWith, matcher.actualWith, matcher.expectedWith, matcher.expectedWith)
	}
}

func (matcher *PanicWithMatcher) NegatedFailureMessage(actual interface{}) (message string) {
	if matcher.actualWith == "" {
		return format.Message(actual, "not to panic")
	} else {
		return fmt.Sprintf("Expected\n\t<func ()>: %v\n\tpanicking with <%T>: %v\n\nnot to panic with\n\t<%T>: %v",
			actual, matcher.actualWith, matcher.actualWith, matcher.expectedWith, matcher.expectedWith)
	}

}
<commit_msg>Fix build: Forgot one NotNil in previous commit<commit_after>package pegomock_test

import (
	"fmt"
	"reflect"

	"github.com/onsi/gomega/format"
	"github.com/onsi/gomega/types"
	"github.com/petergtz/pegomock/internal/verify"
)

type PanicWithMatcher struct {
	expectedWith interface{}
	actualWith   interface{}
}

func PanicWith(object interface{}) types.GomegaMatcher {
	verify.Argument(object != nil, "You must provide a non-nil object to PanicWith")
	return &PanicWithMatcher{expectedWith: object}
}

func (matcher *PanicWithMatcher) Match(actual interface{}) (success bool, err error) {
	if actual == nil {
		return false, fmt.Errorf("PanicWithMatcher expects a non-nil actual.")
	}

	actualType := reflect.TypeOf(actual)
	if actualType.Kind() != reflect.Func {
		return false, fmt.Errorf("PanicWithMatcher expects a function.  Got:\n%s", format.Object(actual, 1))
	}
	if !(actualType.NumIn() == 0 && actualType.NumOut() == 0) {
		return false, fmt.Errorf("PanicWithMatcher expects a function with no arguments and no return value.  Got:\n%s", format.Object(actual, 1))
	}

	success = false
	defer func() {
		if object := recover(); object == matcher.expectedWith {
			success = true
		} else {
			matcher.actualWith = object
		}
	}()

	reflect.ValueOf(actual).Call([]reflect.Value{})

	return
}

func (matcher *PanicWithMatcher) FailureMessage(actual interface{}) (message string) {
	if matcher.actualWith == "" {
		return format.Message(actual, "to panic")
	} else {
		// TODO: can we reuse format.Message somehow?
		return fmt.Sprintf("Expected\n\t<func ()>: %v\n\tpanicking with <%T>: %v\n\nto panic with\n\t<%T>: %v",
			actual, matcher.actualWith, matcher.actualWith, matcher.expectedWith, matcher.expectedWith)
	}
}

func (matcher *PanicWithMatcher) NegatedFailureMessage(actual interface{}) (message string) {
	if matcher.actualWith == "" {
		return format.Message(actual, "not to panic")
	} else {
		return fmt.Sprintf("Expected\n\t<func ()>: %v\n\tpanicking with <%T>: %v\n\nnot to panic with\n\t<%T>: %v",
			actual, matcher.actualWith, matcher.actualWith, matcher.expectedWith, matcher.expectedWith)
	}

}
<|endoftext|>


<commit_before>// Brief: Scripture API
// Primary responsibility: Query calls for Scripture functionality

package main

import (
	"fmt"
	"log"
	"net/url"

	bmul "github.com/julwrites/BotMultiplexer"
)

var passageQuery string = "www.biblegateway.com/passage/?search=%s&version=%s"

func GetReference(ref string, env *bmul.SessionData) string {
	query := fmt.Sprintf(passageQuery, ref, GetUserConfig(&env.User).Version)

	log.Printf("Query String: %s", query)

	doc := GetHtml(query)

	if doc == nil {
		log.Fatalf("Error getting reference")
		return ""
	}

	foundRef, err := Find(doc, "passage-display-bcv")
	if err != nil {
		log.Fatalf("Error in Finding of Reference: %v", err)
	}

	return foundRef.Data
}

func GetPassage(ref string, env *bmul.SessionData) string {
	query := fmt.Sprintf(passageQuery, ref, GetUserConfig(&env.User).Version)
	query = url.QueryEscape(query)

	doc := GetHtml(query)

	if doc == nil {
		log.Fatalf("Error getting reference")
		return ""
	}

	foundRef, err := Find(doc, "passage-display-bcv")
	if err != nil {
		log.Fatalf("Error in Finding of Reference: %v", err)
	}

	return foundRef.Data
}

func GetBiblePassage(env *bmul.SessionData) bool {
	if len(env.Msg.Message) > 0 {

		ref := GetReference(env.Msg.Message, env)

		if len(ref) > 0 {
			env.Res.Message = GetPassage(ref, env)

			return true
		}
	}

	return false
}
<commit_msg>Adding http specifier<commit_after>// Brief: Scripture API
// Primary responsibility: Query calls for Scripture functionality

package main

import (
	"fmt"
	"log"
	"net/url"

	bmul "github.com/julwrites/BotMultiplexer"
)

var passageQuery string = "http://www.biblegateway.com/passage/?search=%s&version=%s"

func GetReference(ref string, env *bmul.SessionData) string {
	query := fmt.Sprintf(passageQuery, ref, GetUserConfig(&env.User).Version)

	log.Printf("Query String: %s", query)

	doc := GetHtml(query)

	if doc == nil {
		log.Fatalf("Error getting reference")
		return ""
	}

	foundRef, err := Find(doc, "passage-display-bcv")
	if err != nil {
		log.Fatalf("Error in Finding of Reference: %v", err)
	}

	return foundRef.Data
}

func GetPassage(ref string, env *bmul.SessionData) string {
	query := fmt.Sprintf(passageQuery, ref, GetUserConfig(&env.User).Version)
	query = url.QueryEscape(query)

	doc := GetHtml(query)

	if doc == nil {
		log.Fatalf("Error getting reference")
		return ""
	}

	foundRef, err := Find(doc, "passage-display-bcv")
	if err != nil {
		log.Fatalf("Error in Finding of Reference: %v", err)
	}

	return foundRef.Data
}

func GetBiblePassage(env *bmul.SessionData) bool {
	if len(env.Msg.Message) > 0 {

		ref := GetReference(env.Msg.Message, env)

		if len(ref) > 0 {
			env.Res.Message = GetPassage(ref, env)

			return true
		}
	}

	return false
}
<|endoftext|>


<commit_before>package main

import (
	"io/ioutil"
	"log"
	"net"
	"net/http"
	"os"
	"path/filepath"

	"github.com/zserge/webview"
)

var indexHTML = `
<!doctype html>

		<meta http-equiv="X-UA-Compatible" content="IE=edge">

Hello, world

`

func runLocalHTTP() {
	ln, err := net.Listen("tcp", "127.0.0.1:0")
	if err != nil {
		log.Fatal(err)
	}
	go func() {
		defer ln.Close()
		http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
			w.Write([]byte(indexHTML))
		})
		log.Fatal(http.Serve(ln, nil))
	}()
	url := "http://" + ln.Addr().String()
	w := webview.New(webview.Settings{
		Title: "Loaded: Local HTTP Server",
		URL:   url,
	})
	defer w.Exit()
	w.Run()
}

func runLocalFile() {
	dir, err := ioutil.TempDir("", "webview")
	if err != nil {
		log.Fatal(err)
	}
	defer os.RemoveAll(dir)
	tmpfn := filepath.Join(dir, "index.html")
	if err := ioutil.WriteFile(tmpfn, []byte(indexHTML), 0666); err != nil {
		log.Fatal(err)
	}
	abs, err := filepath.Abs(tmpfn)
	if err != nil {
		log.Fatal(err)
	}
	log.Println(abs)
	w := webview.New(webview.Settings{
		Title: "Loaded: Local file URL",
		URL:   "file://" + abs,
	})
	defer w.Exit()
	w.Run()
}

func runDataURL() {
	w := webview.New(webview.Settings{
		Title: "Loaded: Data URL",
		URL:   "data:text/html," + indexHTML,
	})
	defer w.Exit()
	w.Run()
}

func runInjectJS() {
	w := webview.New(webview.Settings{
		Title: "Loaded: Injected via JavaScript",
		URL:   "data:text/html,",
	})
	defer w.Exit()
	w.Dispatch(func() {
		w.Eval(`document.body.innerHTML = "<h1>Hello, world</h1>";`)
	})
	w.Run()
}

func main() {
	runLocalHTTP()
	//runLocalFile()
	//runDataURL()
	//runInjectJS()
}
<commit_msg>example: fix path url encoding<commit_after>package main

import (
	"io/ioutil"
	"log"
	"net"
	"net/url"
	"net/http"
	"os"
	"path/filepath"

	"github.com/zserge/webview"
)

var indexHTML = `
<!doctype html>

		<meta http-equiv="X-UA-Compatible" content="IE=edge">

Hello, world

`

func runLocalHTTP() {
	ln, err := net.Listen("tcp", "127.0.0.1:0")
	if err != nil {
		log.Fatal(err)
	}
	go func() {
		defer ln.Close()
		http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
			w.Write([]byte(indexHTML))
		})
		log.Fatal(http.Serve(ln, nil))
	}()
	url := "http://" + ln.Addr().String()
	w := webview.New(webview.Settings{
		Title: "Loaded: Local HTTP Server",
		URL:   url,
	})
	defer w.Exit()
	w.Run()
}

func runLocalFile() {
	dir, err := ioutil.TempDir("", "webview")
	if err != nil {
		log.Fatal(err)
	}
	defer os.RemoveAll(dir)
	tmpfn := filepath.Join(dir, "index.html")
	if err := ioutil.WriteFile(tmpfn, []byte(indexHTML), 0666); err != nil {
		log.Fatal(err)
	}
	abs, err := filepath.Abs(tmpfn)
	if err != nil {
		log.Fatal(err)
	}
	log.Println("local tmp file: ", abs)
	w := webview.New(webview.Settings{
		Title: "Loaded: Local file URL",
		URL:   "file://" + abs,
	})
	defer w.Exit()
	w.Run()
}

func runDataURL() {
	w := webview.New(webview.Settings{
		Title: "Loaded: Data URL",
		URL:   "data:text/html," + url.PathEscape(indexHTML),
	})
	defer w.Exit()
	w.Run()
}

func runInjectJS() {
	w := webview.New(webview.Settings{
		Title: "Loaded: Injected via JavaScript",
		URL:   "data:text/html,",
	})
	defer w.Exit()
	w.Dispatch(func() {
		w.Eval(`document.body.innerHTML = "<h1>Hello, world</h1>";`)
	})
	w.Run()
}

func main() {
	runLocalHTTP()
	//runLocalFile()
	//runDataURL()
	//runInjectJS()
}
<|endoftext|>


<commit_before>package kloud

import (
	"errors"
	"fmt"
	"koding/db/models"
	"koding/db/mongodb"
	"koding/kites/kloud/contexthelper/session"
	"koding/kites/kloud/terraformer"
	"strings"

	"labix.org/v2/mgo"
	"labix.org/v2/mgo/bson"

	"golang.org/x/net/context"

	"github.com/hashicorp/terraform/terraform"
	"github.com/koding/kite"
	"github.com/mitchellh/mapstructure"
)

type PlanMachine struct {
	Provider   string            `json:"provider"`
	Attributes map[string]string `json:"attributes"`
}

type PlanOutput struct {
	Machines []PlanMachine `json:"machines"`
}

type PlanRequest struct {
	// Terraform template file
	TerraformContext string `json:"terraformContext"`

	// PublicKeys contains provider to publicKeys mapping
	PublicKeys map[string]string `json:"publicKeys"`
}

type terraformCredentials struct {
	Creds []*terraformCredential
}

type terraformCredential struct {
	Provider string
	Data     map[string]string `mapstructure:"data"`
}

func (k *Kloud) Plan(r *kite.Request) (interface{}, error) {
	if r.Args == nil {
		return nil, NewError(ErrNoArguments)
	}

	var args *PlanRequest
	if err := r.Args.One().Unmarshal(&args); err != nil {
		return nil, err
	}

	if args.TerraformContext == "" {
		return nil, NewError(ErrTerraformContextIsMissing)
	}

	if len(args.PublicKeys) == 0 {
		return nil, errors.New("credential ids are not passed")
	}

	ctx := k.ContextCreator(context.Background())

	sess, ok := session.FromContext(ctx)
	if !ok {
		return nil, errors.New("session context is not passed")
	}

	creds, err := fetchCredentials(r.Username, sess.DB, args.PublicKeys)
	if err != nil {
		return nil, err
	}

	// TODO(arslan): make one single persistent connection if needed, for now
	// this is ok.
	tfKite, err := terraformer.Connect(sess.Kite)
	if err != nil {
		return nil, err
	}
	defer tfKite.Close()

	args.TerraformContext = appendVariables(args.TerraformContext, creds)
	plan, err := tfKite.Plan(args.TerraformContext)
	if err != nil {
		return nil, err
	}

	return machineFromPlan(plan)
}

// appendVariables appends the given key/value credentials to the hclFile (terraform) file
func appendVariables(hclFile string, creds *terraformCredentials) string {
	// TODO: use hcl encoder, this is just for testing
	for _, cred := range creds.Creds {
		// we only support aws for now
		if cred.Provider != "aws" {
			continue
		}

		for k, v := range cred.Data {
			hclFile += "\n"
			varTemplate := `
variable "%s" {
	default = "%s"
}`
			hclFile += fmt.Sprintf(varTemplate, k, v)
		}
	}

	return hclFile
}

func fetchCredentials(username string, db *mongodb.MongoDB, keys map[string]string) (*terraformCredentials, error) {
	// 1- fetch jaccount from username
	var account *models.Account
	if err := db.Run("jAccounts", func(c *mgo.Collection) error {
		return c.Find(bson.M{"profile.nickname": username}).One(&account)
	}); err != nil {
		return nil, err
	}

	// 2- fetch credential from publickey via args
	publicKeys := make([]string, 0)
	for _, publicKey := range keys {
		publicKeys = append(publicKeys, publicKey)
	}

	var credentials []*models.Credential
	if err := db.Run("jCredentials", func(c *mgo.Collection) error {
		return c.Find(bson.M{"publicKey": bson.M{"$in": publicKeys}}).All(&credentials)
	}); err != nil {
		return nil, fmt.Errorf("credentials lookup error: %v", err)
	}

	// 3- count relationship with credential id and jaccount id as user or
	// owner. Any non valid credentials will be discarded
	validKeys := make(map[string]string, 0)

	for _, cred := range credentials {
		err := db.Run("relationships", func(c *mgo.Collection) error {
			_, err := c.Find(bson.M{
				"targetId": cred.Id,
				"sourceId": account.Id,
				"as": bson.M{
					"$in": []string{"owner", "user"},
				},
			}).Count()
			return err
		})

		if err != nil {
			// we return for any not validated public key.
			return nil, fmt.Errorf("credential with publicKey '%s' is not validated", cred.PublicKey)
		}

		validKeys[cred.PublicKey] = cred.Provider
	}

	// 4- fetch credentialdata with publickey
	validPublicKeys := make([]string, 0)
	for pKey := range validKeys {
		validPublicKeys = append(validPublicKeys, pKey)
	}

	var credentialData []*models.CredentialData
	if err := db.Run("jCredentialDatas", func(c *mgo.Collection) error {
		return c.Find(bson.M{"publicKey": bson.M{"$in": validPublicKeys}}).All(&credentialData)
	}); err != nil {
		return nil, fmt.Errorf("credential data lookup error: %v", err)
	}

	// 5- return list of keys. We only support aws for now
	creds := &terraformCredentials{
		Creds: make([]*terraformCredential, 0),
	}

	for _, data := range credentialData {
		provider, ok := validKeys[data.PublicKey]
		if !ok {
			return nil, fmt.Errorf("provider is not found for key: %s", data.PublicKey)
		}
		// for now we only support aws
		if provider != "aws" {
			continue
		}

		cred := &terraformCredential{
			Provider: provider,
		}

		if err := mapstructure.Decode(data.Meta, &cred.Data); err != nil {
			return nil, err
		}
		creds.Creds = append(creds.Creds, cred)

	}
	return creds, nil
}

func machineFromPlan(plan *terraform.Plan) (*PlanOutput, error) {
	out := &PlanOutput{
		Machines: make([]PlanMachine, 0),
	}

	attrs := make(map[string]string, 0)

	if plan.Diff == nil {
		return nil, errors.New("plan diff is empty")
	}

	if plan.Diff.Modules == nil {
		return nil, errors.New("plan diff module is empty")
	}

	for _, d := range plan.Diff.Modules {
		if d.Resources == nil {
			continue
		}

		for providerResource, r := range d.Resources {
			if r.Attributes == nil {
				continue
			}

			for name, a := range r.Attributes {
				attrs[name] = a.New
			}

			// providerResource is in the form of "aws_instance.foo"
			splitted := strings.Split(providerResource, "_")
			if len(splitted) == 0 {
				return nil, fmt.Errorf("provider resource is unknown: %v", splitted)
			}

			out.Machines = append(out.Machines, PlanMachine{
				Provider:   splitted[0],
				Attributes: attrs,
			})
		}
	}

	return out, nil
}
<commit_msg>kloud/plan: use modelhelper for account fetching<commit_after>package kloud

import (
	"errors"
	"fmt"
	"koding/db/models"
	"koding/db/mongodb"
	"koding/db/mongodb/modelhelper"
	"koding/kites/kloud/contexthelper/session"
	"koding/kites/kloud/terraformer"
	"strings"

	"labix.org/v2/mgo"
	"labix.org/v2/mgo/bson"

	"golang.org/x/net/context"

	"github.com/hashicorp/terraform/terraform"
	"github.com/koding/kite"
	"github.com/mitchellh/mapstructure"
)

type PlanMachine struct {
	Provider   string            `json:"provider"`
	Attributes map[string]string `json:"attributes"`
}

type PlanOutput struct {
	Machines []PlanMachine `json:"machines"`
}

type PlanRequest struct {
	// Terraform template file
	TerraformContext string `json:"terraformContext"`

	// PublicKeys contains provider to publicKeys mapping
	PublicKeys map[string]string `json:"publicKeys"`
}

type terraformCredentials struct {
	Creds []*terraformCredential
}

type terraformCredential struct {
	Provider string
	Data     map[string]string `mapstructure:"data"`
}

func (k *Kloud) Plan(r *kite.Request) (interface{}, error) {
	if r.Args == nil {
		return nil, NewError(ErrNoArguments)
	}

	var args *PlanRequest
	if err := r.Args.One().Unmarshal(&args); err != nil {
		return nil, err
	}

	if args.TerraformContext == "" {
		return nil, NewError(ErrTerraformContextIsMissing)
	}

	if len(args.PublicKeys) == 0 {
		return nil, errors.New("credential ids are not passed")
	}

	ctx := k.ContextCreator(context.Background())

	sess, ok := session.FromContext(ctx)
	if !ok {
		return nil, errors.New("session context is not passed")
	}

	creds, err := fetchCredentials(r.Username, sess.DB, args.PublicKeys)
	if err != nil {
		return nil, err
	}

	// TODO(arslan): make one single persistent connection if needed, for now
	// this is ok.
	tfKite, err := terraformer.Connect(sess.Kite)
	if err != nil {
		return nil, err
	}
	defer tfKite.Close()

	args.TerraformContext = appendVariables(args.TerraformContext, creds)
	plan, err := tfKite.Plan(args.TerraformContext)
	if err != nil {
		return nil, err
	}

	return machineFromPlan(plan)
}

// appendVariables appends the given key/value credentials to the hclFile (terraform) file
func appendVariables(hclFile string, creds *terraformCredentials) string {
	// TODO: use hcl encoder, this is just for testing
	for _, cred := range creds.Creds {
		// we only support aws for now
		if cred.Provider != "aws" {
			continue
		}

		for k, v := range cred.Data {
			hclFile += "\n"
			varTemplate := `
variable "%s" {
	default = "%s"
}`
			hclFile += fmt.Sprintf(varTemplate, k, v)
		}
	}

	return hclFile
}

func fetchCredentials(username string, db *mongodb.MongoDB, keys map[string]string) (*terraformCredentials, error) {
	// 1- fetch jaccount from username
	account, err := modelhelper.GetAccount(username)
	if err != nil {
		return nil, err
	}

	// 2- fetch credential from publickey via args
	publicKeys := make([]string, 0)
	for _, publicKey := range keys {
		publicKeys = append(publicKeys, publicKey)
	}

	var credentials []*models.Credential
	if err := db.Run("jCredentials", func(c *mgo.Collection) error {
		return c.Find(bson.M{"publicKey": bson.M{"$in": publicKeys}}).All(&credentials)
	}); err != nil {
		return nil, fmt.Errorf("credentials lookup error: %v", err)
	}

	// 3- count relationship with credential id and jaccount id as user or
	// owner. Any non valid credentials will be discarded
	validKeys := make(map[string]string, 0)

	for _, cred := range credentials {
		err := db.Run("relationships", func(c *mgo.Collection) error {
			_, err := c.Find(bson.M{
				"targetId": cred.Id,
				"sourceId": account.Id,
				"as": bson.M{
					"$in": []string{"owner", "user"},
				},
			}).Count()
			return err
		})

		if err != nil {
			// we return for any not validated public key.
			return nil, fmt.Errorf("credential with publicKey '%s' is not validated", cred.PublicKey)
		}

		validKeys[cred.PublicKey] = cred.Provider
	}

	// 4- fetch credentialdata with publickey
	validPublicKeys := make([]string, 0)
	for pKey := range validKeys {
		validPublicKeys = append(validPublicKeys, pKey)
	}

	var credentialData []*models.CredentialData
	if err := db.Run("jCredentialDatas", func(c *mgo.Collection) error {
		return c.Find(bson.M{"publicKey": bson.M{"$in": validPublicKeys}}).All(&credentialData)
	}); err != nil {
		return nil, fmt.Errorf("credential data lookup error: %v", err)
	}

	// 5- return list of keys. We only support aws for now
	creds := &terraformCredentials{
		Creds: make([]*terraformCredential, 0),
	}

	for _, data := range credentialData {
		provider, ok := validKeys[data.PublicKey]
		if !ok {
			return nil, fmt.Errorf("provider is not found for key: %s", data.PublicKey)
		}
		// for now we only support aws
		if provider != "aws" {
			continue
		}

		cred := &terraformCredential{
			Provider: provider,
		}

		if err := mapstructure.Decode(data.Meta, &cred.Data); err != nil {
			return nil, err
		}
		creds.Creds = append(creds.Creds, cred)

	}
	return creds, nil
}

func machineFromPlan(plan *terraform.Plan) (*PlanOutput, error) {
	out := &PlanOutput{
		Machines: make([]PlanMachine, 0),
	}

	attrs := make(map[string]string, 0)

	if plan.Diff == nil {
		return nil, errors.New("plan diff is empty")
	}

	if plan.Diff.Modules == nil {
		return nil, errors.New("plan diff module is empty")
	}

	for _, d := range plan.Diff.Modules {
		if d.Resources == nil {
			continue
		}

		for providerResource, r := range d.Resources {
			if r.Attributes == nil {
				continue
			}

			for name, a := range r.Attributes {
				attrs[name] = a.New
			}

			// providerResource is in the form of "aws_instance.foo"
			splitted := strings.Split(providerResource, "_")
			if len(splitted) == 0 {
				return nil, fmt.Errorf("provider resource is unknown: %v", splitted)
			}

			out.Machines = append(out.Machines, PlanMachine{
				Provider:   splitted[0],
				Attributes: attrs,
			})
		}
	}

	return out, nil
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"time"

	"github.com/kr/pretty"
)

type strToInf map[string]interface{}

type UserResult struct {
	Level         int
	Username      string
	ExemptReson   string
	LastLoginDate string
}

func NewUserResults() []*UserResult {
	return []*UserResult{}
}

type Result struct {
	Desc       string
	Successful []*UserResult
	Failure    []*UserResult
	Exempt     []*UserResult
	StartedAt  string
	EndedAt    string
}

func NewResult(desc string) *Result {
	return &Result{
		Desc:       desc,
		Successful: NewUserResults(),
		Failure:    NewUserResults(),
		Exempt:     NewUserResults(),
		StartedAt:  time.Now().String(),
	}
}

func (r *Result) String() string {
	pretty.Println(r)

	return fmt.Sprintf(
		"Started: %v, Ran %v. Success: %v, Exempt: %v Ended: %v",
		r.StartedAt, r.Desc, len(r.Successful), len(r.Exempt), r.EndedAt,
	)
}
<commit_msg>janitor: remove debug<commit_after>package main

import (
	"fmt"
	"time"
)

type strToInf map[string]interface{}

type UserResult struct {
	Level         int
	Username      string
	ExemptReson   string
	LastLoginDate string
}

func NewUserResults() []*UserResult {
	return []*UserResult{}
}

type Result struct {
	Desc       string
	Successful []*UserResult
	Failure    []*UserResult
	Exempt     []*UserResult
	StartedAt  string
	EndedAt    string
}

func NewResult(desc string) *Result {
	return &Result{
		Desc:       desc,
		Successful: NewUserResults(),
		Failure:    NewUserResults(),
		Exempt:     NewUserResults(),
		StartedAt:  time.Now().String(),
	}
}

func (r *Result) String() string {
	return fmt.Sprintf(
		"Started: %v, Ran %v. Success: %v, Exempt: %v Ended: %v",
		r.StartedAt, r.Desc, len(r.Successful), len(r.Exempt), r.EndedAt,
	)
}
<|endoftext|>


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

// Download remote packages.

package main

import (
	"http"
	"os"
	"path/filepath"
	"regexp"
	"strings"
)

const dashboardURL = "http://godashboard.appspot.com/package"

// maybeReportToDashboard reports path to dashboard unless
// -dashboard=false is on command line.  It ignores errors.
func maybeReportToDashboard(path string) {
	// if -dashboard=false was on command line, do nothing
	if !*reportToDashboard {
		return
	}

	// otherwise lob url to dashboard
	r, _ := http.Post(dashboardURL, "application/x-www-form-urlencoded", strings.NewReader("path="+path))
	if r != nil && r.Body != nil {
		r.Body.Close()
	}
}

var vcsPatterns = map[string]*regexp.Regexp{
	"googlecode": regexp.MustCompile(`^([a-z0-9\-]+\.googlecode\.com/(svn|hg))(/[a-z0-9A-Z_.\-/]*)?$`),
	"github":     regexp.MustCompile(`^(github\.com/[a-z0-9A-Z_.\-]+/[a-z0-9A-Z_.\-]+)(/[a-z0-9A-Z_.\-/]*)?$`),
	"bitbucket":  regexp.MustCompile(`^(bitbucket\.org/[a-z0-9A-Z_.\-]+/[a-z0-9A-Z_.\-]+)(/[a-z0-9A-Z_.\-/]*)?$`),
	"launchpad":  regexp.MustCompile(`^(launchpad\.net/([a-z0-9A-Z_.\-]+(/[a-z0-9A-Z_.\-]+)?|~[a-z0-9A-Z_.\-]+/(\+junk|[a-z0-9A-Z_.\-]+)/[a-z0-9A-Z_.\-]+))(/[a-z0-9A-Z_.\-/]+)?$`),
}

// isRemote returns true if the provided package path
// matches one of the supported remote repositories.
func isRemote(pkg string) bool {
	for _, r := range vcsPatterns {
		if r.MatchString(pkg) {
			return true
		}
	}
	return false
}

// download checks out or updates pkg from the remote server.
func download(pkg, srcDir string) os.Error {
	if strings.Contains(pkg, "..") {
		return os.ErrorString("invalid path (contains ..)")
	}
	if m := vcsPatterns["bitbucket"].FindStringSubmatch(pkg); m != nil {
		if err := vcsCheckout(&hg, srcDir, m[1], "http://"+m[1], m[1]); err != nil {
			return err
		}
		return nil
	}
	if m := vcsPatterns["googlecode"].FindStringSubmatch(pkg); m != nil {
		var v *vcs
		switch m[2] {
		case "hg":
			v = &hg
		case "svn":
			v = &svn
		default:
			// regexp only allows hg, svn to get through
			panic("missing case in download: " + pkg)
		}
		if err := vcsCheckout(v, srcDir, m[1], "https://"+m[1], m[1]); err != nil {
			return err
		}
		return nil
	}
	if m := vcsPatterns["github"].FindStringSubmatch(pkg); m != nil {
		if strings.HasSuffix(m[1], ".git") {
			return os.ErrorString("repository " + pkg + " should not have .git suffix")
		}
		if err := vcsCheckout(&git, srcDir, m[1], "http://"+m[1]+".git", m[1]); err != nil {
			return err
		}
		return nil
	}
	if m := vcsPatterns["launchpad"].FindStringSubmatch(pkg); m != nil {
		// Either lp.net/<project>[/<series>[/<path>]]
		//	 or lp.net/~<user or team>/<project>/<branch>[/<path>]
		if err := vcsCheckout(&bzr, srcDir, m[1], "https://"+m[1], m[1]); err != nil {
			return err
		}
		return nil
	}
	return os.ErrorString("unknown repository: " + pkg)
}

// a vcs represents a version control system
// like Mercurial, Git, or Subversion.
type vcs struct {
	cmd               string
	metadir           string
	checkout          string
	clone             string
	update            string
	updateReleaseFlag string
	pull              string
	pullForceFlag     string
	log               string
	logLimitFlag      string
	logReleaseFlag    string
}

var hg = vcs{
	cmd:               "hg",
	metadir:           ".hg",
	checkout:          "checkout",
	clone:             "clone",
	update:            "update",
	updateReleaseFlag: "release",
	pull:              "pull",
	log:               "log",
	logLimitFlag:      "-l1",
	logReleaseFlag:    "-rrelease",
}

var git = vcs{
	cmd:               "git",
	metadir:           ".git",
	checkout:          "checkout",
	clone:             "clone",
	update:            "pull",
	updateReleaseFlag: "release",
	pull:              "fetch",
	log:               "show-ref",
	logLimitFlag:      "",
	logReleaseFlag:    "release",
}

var svn = vcs{
	cmd:               "svn",
	metadir:           ".svn",
	checkout:          "checkout",
	clone:             "checkout",
	update:            "update",
	updateReleaseFlag: "release",
	log:               "log",
	logLimitFlag:      "-l1",
	logReleaseFlag:    "release",
}

var bzr = vcs{
	cmd:               "bzr",
	metadir:           ".bzr",
	checkout:          "update",
	clone:             "branch",
	update:            "update",
	updateReleaseFlag: "-rrelease",
	pull:              "pull",
	pullForceFlag:     "--overwrite",
	log:               "log",
	logLimitFlag:      "-l1",
	logReleaseFlag:    "-rrelease",
}

// Try to detect if a "release" tag exists.  If it does, update
// to the tagged version, otherwise just update the current branch.
// NOTE(_nil): svn will always fail because it is trying to get
// the revision history of a file named "release" instead of
// looking for a commit with a release tag
func (v *vcs) updateRepo(dst string) os.Error {
	if err := quietRun(dst, nil, v.cmd, v.log, v.logLimitFlag, v.logReleaseFlag); err == nil {
		if err := run(dst, nil, v.cmd, v.checkout, v.updateReleaseFlag); err != nil {
			return err
		}
	} else if err := run(dst, nil, v.cmd, v.update); err != nil {
		return err
	}
	return nil
}

// vcsCheckout checks out repo into dst using vcs.
// It tries to check out (or update, if the dst already
// exists and -u was specified on the command line)
// the repository at tag/branch "release".  If there is no
// such tag or branch, it falls back to the repository tip.
func vcsCheckout(vcs *vcs, srcDir, pkgprefix, repo, dashpath string) os.Error {
	dst := filepath.Join(srcDir, filepath.FromSlash(pkgprefix))
	dir, err := os.Stat(filepath.Join(dst, vcs.metadir))
	if err == nil && !dir.IsDirectory() {
		return os.ErrorString("not a directory: " + dst)
	}
	if err != nil {
		parent, _ := filepath.Split(dst)
		if err := os.MkdirAll(parent, 0777); err != nil {
			return err
		}
		if err := run(string(filepath.Separator), nil, vcs.cmd, vcs.clone, repo, dst); err != nil {
			return err
		}
		if err := vcs.updateRepo(dst); err != nil {
			return err
		}
		// success on first installation - report
		maybeReportToDashboard(dashpath)
	} else if *update {
		// Retrieve new revisions from the remote branch, if the VCS
		// supports this operation independently (e.g. svn doesn't)
		if vcs.pull != "" {
			if vcs.pullForceFlag != "" {
				if err := run(dst, nil, vcs.cmd, vcs.pull, vcs.pullForceFlag); err != nil {
					return err
				}
			} else if err := run(dst, nil, vcs.cmd, vcs.pull); err != nil {
				return err
			}
		}

		// Update to release or latest revision
		if err := vcs.updateRepo(dst); err != nil {
			return err
		}
	}
	return nil
}
<commit_msg>goinstall: Add support for arbitary code repositories<commit_after>// Copyright 2010 The Go Authors.  All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Download remote packages.

package main

import (
	"exec"
	"http"
	"os"
	"path"
	"path/filepath"
	"regexp"
	"strings"
	"sync"
	"time"
)

const dashboardURL = "http://godashboard.appspot.com/package"

// maybeReportToDashboard reports path to dashboard unless
// -dashboard=false is on command line.  It ignores errors.
func maybeReportToDashboard(path string) {
	// if -dashboard=false was on command line, do nothing
	if !*reportToDashboard {
		return
	}

	// otherwise lob url to dashboard
	r, _ := http.Post(dashboardURL, "application/x-www-form-urlencoded", strings.NewReader("path="+path))
	if r != nil && r.Body != nil {
		r.Body.Close()
	}
}

type host struct {
	pattern  *regexp.Regexp
	protocol string
}

// a vcs represents a version control system
// like Mercurial, Git, or Subversion.
type vcs struct {
	name              string
	cmd               string
	metadir           string
	checkout          string
	clone             string
	update            string
	updateReleaseFlag string
	pull              string
	pullForceFlag     string
	log               string
	logLimitFlag      string
	logReleaseFlag    string
	check             string
	protocols         []string
	suffix            string
	findRepos         bool
	defaultHosts      []host

	// Is this tool present? (set by findTools)
	available bool
}

type vcsMatch struct {
	*vcs
	prefix, repo string
}

var hg = vcs{
	name:              "Mercurial",
	cmd:               "hg",
	metadir:           ".hg",
	checkout:          "checkout",
	clone:             "clone",
	update:            "update",
	updateReleaseFlag: "release",
	pull:              "pull",
	log:               "log",
	logLimitFlag:      "-l1",
	logReleaseFlag:    "-rrelease",
	check:             "identify",
	protocols:         []string{"http"},
	findRepos:         true,
	defaultHosts: []host{
		{regexp.MustCompile(`^([a-z0-9\-]+\.googlecode\.com/hg)(/[a-z0-9A-Z_.\-/]*)?$`), "https"},
		{regexp.MustCompile(`^(bitbucket\.org/[a-z0-9A-Z_.\-]+/[a-z0-9A-Z_.\-]+)(/[a-z0-9A-Z_.\-/]*)?$`), "http"},
	},
}

var git = vcs{
	name:              "Git",
	cmd:               "git",
	metadir:           ".git",
	checkout:          "checkout",
	clone:             "clone",
	update:            "pull",
	updateReleaseFlag: "release",
	pull:              "fetch",
	log:               "show-ref",
	logLimitFlag:      "",
	logReleaseFlag:    "release",
	check:             "peek-remote",
	protocols:         []string{"git", "http"},
	suffix:            ".git",
	findRepos:         true,
	defaultHosts: []host{
		{regexp.MustCompile(`^(github\.com/[a-z0-9A-Z_.\-]+/[a-z0-9A-Z_.\-]+)(/[a-z0-9A-Z_.\-/]*)?$`), "http"},
	},
}

var svn = vcs{
	name:              "Subversion",
	cmd:               "svn",
	metadir:           ".svn",
	checkout:          "checkout",
	clone:             "checkout",
	update:            "update",
	updateReleaseFlag: "release",
	log:               "log",
	logLimitFlag:      "-l1",
	logReleaseFlag:    "release",
	check:             "info",
	protocols:         []string{"http", "svn"},
	findRepos:         false,
	defaultHosts: []host{
		{regexp.MustCompile(`^([a-z0-9\-]+\.googlecode\.com/svn)(/[a-z0-9A-Z_.\-/]*)?$`), "https"},
	},
}

var bzr = vcs{
	name:              "Bazaar",
	cmd:               "bzr",
	metadir:           ".bzr",
	checkout:          "update",
	clone:             "branch",
	update:            "update",
	updateReleaseFlag: "-rrelease",
	pull:              "pull",
	pullForceFlag:     "--overwrite",
	log:               "log",
	logLimitFlag:      "-l1",
	logReleaseFlag:    "-rrelease",
	check:             "info",
	protocols:         []string{"http", "bzr"},
	findRepos:         true,
	defaultHosts: []host{
		{regexp.MustCompile(`^(launchpad\.net/([a-z0-9A-Z_.\-]+(/[a-z0-9A-Z_.\-]+)?|~[a-z0-9A-Z_.\-]+/(\+junk|[a-z0-9A-Z_.\-]+)/[a-z0-9A-Z_.\-]+))(/[a-z0-9A-Z_.\-/]+)?$`), "https"},
	},
}

var vcsList = []*vcs{&git, &hg, &bzr, &svn}

func potentialPrefixes(pkg string) []string {
	prefixes := []string{}

	parts := strings.Split(pkg, "/", -1)
	elem := parts[0]
	for _, part := range parts[1:] {
		elem = path.Join(elem, part)
		prefixes = append(prefixes, elem)
	}

	return prefixes
}

func tryCommand(c chan *vcsMatch, v *vcs, prefixes []string) {
	for _, proto := range v.protocols {
		for _, prefix := range prefixes {
			repo := proto + "://" + prefix + v.suffix
			if exec.Command(v.cmd, v.check, repo).Run() == nil {
				c <- &vcsMatch{v, prefix, repo}
				return
			}
		}
	}
}

var findToolsOnce sync.Once

func findTools() {
	for _, v := range vcsList {
		v.available = exec.Command(v.cmd, "help").Run() == nil
	}
}

var logMissingToolsOnce sync.Once

func logMissingTools() {
	for _, v := range vcsList {
		if !v.available {
			logf("%s not found; %s packages will be ignored\n", v.cmd, v.name)
		}
	}
}

func findVcs(pkg string) *vcsMatch {
	c := make(chan *vcsMatch, len(vcsList))

	findToolsOnce.Do(findTools)

	// we don't know how much of the name constitutes the repository prefix, so
	// build a list of possibilities
	prefixes := potentialPrefixes(pkg)

	for _, v := range vcsList {
		if !v.available {
			continue
		}
		if v.findRepos {
			go tryCommand(c, v, prefixes)
		} else {
			go tryCommand(c, v, []string{pkg})
		}
	}

	select {
	case m := <-c:
		return m
	case <-time.After(20 * 1e9):
	}

	logMissingToolsOnce.Do(logMissingTools)

	return nil
}

// isRemote returns true if the first part of the package name looks like a
// hostname - i.e. contains at least one '.' and the last part is at least 2
// characters.
func isRemote(pkg string) bool {
	parts := strings.Split(pkg, "/", 2)
	if len(parts) != 2 {
		return false
	}
	parts = strings.Split(parts[0], ".", -1)
	if len(parts) < 2 || len(parts[len(parts)-1]) < 2 {
		return false
	}
	return true
}

// download checks out or updates pkg from the remote server.
func download(pkg, srcDir string) os.Error {
	if strings.Contains(pkg, "..") {
		return os.ErrorString("invalid path (contains ..)")
	}
	var m *vcsMatch
	for _, v := range vcsList {
		for _, host := range v.defaultHosts {
			if hm := host.pattern.FindStringSubmatch(pkg); hm != nil {
				if v.suffix != "" && strings.HasSuffix(hm[1], v.suffix) {
					return os.ErrorString("repository " + pkg + " should not have " + v.suffix + " suffix")
				}
				repo := host.protocol + "://" + hm[1] + v.suffix
				m = &vcsMatch{v, hm[1], repo}
			}
		}
	}
	if m == nil {
		m = findVcs(pkg)
	}
	if m == nil {
		return os.ErrorString("cannot download: " + pkg)
	}
	return vcsCheckout(m.vcs, srcDir, m.prefix, m.repo, pkg)
}

// Try to detect if a "release" tag exists.  If it does, update
// to the tagged version, otherwise just update the current branch.
// NOTE(_nil): svn will always fail because it is trying to get
// the revision history of a file named "release" instead of
// looking for a commit with a release tag
func (v *vcs) updateRepo(dst string) os.Error {
	if err := quietRun(dst, nil, v.cmd, v.log, v.logLimitFlag, v.logReleaseFlag); err == nil {
		if err := run(dst, nil, v.cmd, v.checkout, v.updateReleaseFlag); err != nil {
			return err
		}
	} else if err := run(dst, nil, v.cmd, v.update); err != nil {
		return err
	}
	return nil
}

// vcsCheckout checks out repo into dst using vcs.
// It tries to check out (or update, if the dst already
// exists and -u was specified on the command line)
// the repository at tag/branch "release".  If there is no
// such tag or branch, it falls back to the repository tip.
func vcsCheckout(vcs *vcs, srcDir, pkgprefix, repo, dashpath string) os.Error {
	dst := filepath.Join(srcDir, filepath.FromSlash(pkgprefix))
	dir, err := os.Stat(filepath.Join(dst, vcs.metadir))
	if err == nil && !dir.IsDirectory() {
		return os.ErrorString("not a directory: " + dst)
	}
	if err != nil {
		parent, _ := filepath.Split(dst)
		if err := os.MkdirAll(parent, 0777); err != nil {
			return err
		}
		if err := run(string(filepath.Separator), nil, vcs.cmd, vcs.clone, repo, dst); err != nil {
			return err
		}
		if err := vcs.updateRepo(dst); err != nil {
			return err
		}
		// success on first installation - report
		maybeReportToDashboard(dashpath)
	} else if *update {
		// Retrieve new revisions from the remote branch, if the VCS
		// supports this operation independently (e.g. svn doesn't)
		if vcs.pull != "" {
			if vcs.pullForceFlag != "" {
				if err := run(dst, nil, vcs.cmd, vcs.pull, vcs.pullForceFlag); err != nil {
					return err
				}
			} else if err := run(dst, nil, vcs.cmd, vcs.pull); err != nil {
				return err
			}
		}

		// Update to release or latest revision
		if err := vcs.updateRepo(dst); err != nil {
			return err
		}
	}
	return nil
}
<|endoftext|>


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

// Download remote packages.

package main

import (
	"http"
	"os"
	"path"
	"regexp"
	"strings"
)

const dashboardURL = "http://godashboard.appspot.com/package"

// maybeReportToDashboard reports path to dashboard unless
// -dashboard=false is on command line.  It ignores errors.
func maybeReportToDashboard(path string) {
	// if -dashboard=false was on command line, do nothing
	if !*reportToDashboard {
		return
	}

	// otherwise lob url to dashboard
	r, _ := http.Post(dashboardURL, "application/x-www-form-urlencoded", strings.NewReader("path="+path))
	if r != nil && r.Body != nil {
		r.Body.Close()
	}
}

var googlecode = regexp.MustCompile(`^([a-z0-9\-]+\.googlecode\.com/(svn|hg))(/[a-z0-9A-Z_.\-/]*)?$`)
var github = regexp.MustCompile(`^(github\.com/[a-z0-9A-Z_.\-]+/[a-z0-9A-Z_.\-]+)(/[a-z0-9A-Z_.\-/]*)?$`)
var bitbucket = regexp.MustCompile(`^(bitbucket\.org/[a-z0-9A-Z_.\-]+/[a-z0-9A-Z_.\-]+)(/[a-z0-9A-Z_.\-/]*)?$`)
var launchpad = regexp.MustCompile(`^(launchpad\.net/([a-z0-9A-Z_.\-]+(/[a-z0-9A-Z_.\-]+)?|~[a-z0-9A-Z_.\-]+/(\+junk|[a-z0-9A-Z_.\-]+)/[a-z0-9A-Z_.\-]+))(/[a-z0-9A-Z_.\-/]+)?$`)

// download checks out or updates pkg from the remote server.
func download(pkg string) (string, os.Error) {
	if strings.Contains(pkg, "..") {
		return "", os.ErrorString("invalid path (contains ..)")
	}
	if m := bitbucket.FindStringSubmatch(pkg); m != nil {
		if err := vcsCheckout(&hg, root+m[1], "http://"+m[1], m[1]); err != nil {
			return "", err
		}
		return root + pkg, nil
	}
	if m := googlecode.FindStringSubmatch(pkg); m != nil {
		var v *vcs
		switch m[2] {
		case "hg":
			v = &hg
		case "svn":
			v = &svn
		default:
			// regexp only allows hg, svn to get through
			panic("missing case in download: " + pkg)
		}
		if err := vcsCheckout(v, root+m[1], "https://"+m[1], m[1]); err != nil {
			return "", err
		}
		return root + pkg, nil
	}
	if m := github.FindStringSubmatch(pkg); m != nil {
		if strings.HasSuffix(m[1], ".git") {
			return "", os.ErrorString("repository " + pkg + " should not have .git suffix")
		}
		if err := vcsCheckout(&git, root+m[1], "http://"+m[1]+".git", m[1]); err != nil {
			return "", err
		}
		return root + pkg, nil
	}
	if m := launchpad.FindStringSubmatch(pkg); m != nil {
		// Either lp.net/<project>[/<series>[/<path>]]
		//	 or lp.net/~<user or team>/<project>/<branch>[/<path>]
		if err := vcsCheckout(&bzr, root+m[1], "https://"+m[1], m[1]); err != nil {
			return "", err
		}
		return root + pkg, nil
	}
	return "", os.ErrorString("unknown repository: " + pkg)
}

// a vcs represents a version control system
// like Mercurial, Git, or Subversion.
type vcs struct {
	cmd               string
	metadir           string
	checkout          string
	clone             string
	update            string
	updateReleaseFlag string
	pull              string
	pullForceFlag     string
	log               string
	logLimitFlag      string
	logReleaseFlag    string
}

var hg = vcs{
	cmd:               "hg",
	metadir:           ".hg",
	checkout:          "checkout",
	clone:             "clone",
	update:            "update",
	updateReleaseFlag: "release",
	pull:              "pull",
	log:               "log",
	logLimitFlag:      "-l1",
	logReleaseFlag:    "-rrelease",
}

var git = vcs{
	cmd:               "git",
	metadir:           ".git",
	checkout:          "checkout",
	clone:             "clone",
	update:            "pull",
	updateReleaseFlag: "release",
	pull:              "fetch",
	log:               "show-ref",
	logLimitFlag:      "",
	logReleaseFlag:    "release",
}

var svn = vcs{
	cmd:               "svn",
	metadir:           ".svn",
	checkout:          "checkout",
	clone:             "checkout",
	update:            "update",
	updateReleaseFlag: "release",
	log:               "log",
	logLimitFlag:      "-l1",
	logReleaseFlag:    "release",
}

var bzr = vcs{
	cmd:               "bzr",
	metadir:           ".bzr",
	checkout:          "checkout",
	clone:             "branch",
	update:            "update",
	updateReleaseFlag: "-rrelease",
	pull:              "pull",
	pullForceFlag:     "--overwrite",
	log:               "log",
	logLimitFlag:      "-l1",
	logReleaseFlag:    "-rrelease",
}

// Try to detect if a "release" tag exists.  If it does, update
// to the tagged version, otherwise just update the current branch.
// NOTE(_nil): svn will always fail because it is trying to get
// the revision history of a file named "release" instead of
// looking for a commit with a release tag
func (v *vcs) updateRepo(dst string) os.Error {
	if err := quietRun(dst, nil, v.cmd, v.log, v.logLimitFlag, v.logReleaseFlag); err == nil {
		if err := run(dst, nil, v.cmd, v.checkout, v.updateReleaseFlag); err != nil {
			return err
		}
	} else if err := run(dst, nil, v.cmd, v.update); err != nil {
		return err
	}
	return nil
}

// vcsCheckout checks out repo into dst using vcs.
// It tries to check out (or update, if the dst already
// exists and -u was specified on the command line)
// the repository at tag/branch "release".  If there is no
// such tag or branch, it falls back to the repository tip.
func vcsCheckout(vcs *vcs, dst, repo, dashpath string) os.Error {
	dir, err := os.Stat(dst + "/" + vcs.metadir)
	if err == nil && !dir.IsDirectory() {
		return os.ErrorString("not a directory: " + dst)
	}
	if err != nil {
		parent, _ := path.Split(dst)
		if err := os.MkdirAll(parent, 0777); err != nil {
			return err
		}
		if err := run("/", nil, vcs.cmd, vcs.clone, repo, dst); err != nil {
			return err
		}
		if err := vcs.updateRepo(dst); err != nil {
			return err
		}
		// success on first installation - report
		maybeReportToDashboard(dashpath)
	} else if *update {
		// Retrieve new revisions from the remote branch, if the VCS
		// supports this operation independently (e.g. svn doesn't)
		if vcs.pull != "" {
			if vcs.pullForceFlag != "" {
				if err := run(dst, nil, vcs.cmd, vcs.pull, vcs.pullForceFlag); err != nil {
					return err
				}
			} else if err := run(dst, nil, vcs.cmd, vcs.pull); err != nil {
				return err
			}
		}

		// Update to release or latest revision
		if err := vcs.updateRepo(dst); err != nil {
			return err
		}
	}
	return nil
}
<commit_msg>goinstall: fix -u for bzr<commit_after>// Copyright 2010 The Go Authors.  All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Download remote packages.

package main

import (
	"http"
	"os"
	"path"
	"regexp"
	"strings"
)

const dashboardURL = "http://godashboard.appspot.com/package"

// maybeReportToDashboard reports path to dashboard unless
// -dashboard=false is on command line.  It ignores errors.
func maybeReportToDashboard(path string) {
	// if -dashboard=false was on command line, do nothing
	if !*reportToDashboard {
		return
	}

	// otherwise lob url to dashboard
	r, _ := http.Post(dashboardURL, "application/x-www-form-urlencoded", strings.NewReader("path="+path))
	if r != nil && r.Body != nil {
		r.Body.Close()
	}
}

var googlecode = regexp.MustCompile(`^([a-z0-9\-]+\.googlecode\.com/(svn|hg))(/[a-z0-9A-Z_.\-/]*)?$`)
var github = regexp.MustCompile(`^(github\.com/[a-z0-9A-Z_.\-]+/[a-z0-9A-Z_.\-]+)(/[a-z0-9A-Z_.\-/]*)?$`)
var bitbucket = regexp.MustCompile(`^(bitbucket\.org/[a-z0-9A-Z_.\-]+/[a-z0-9A-Z_.\-]+)(/[a-z0-9A-Z_.\-/]*)?$`)
var launchpad = regexp.MustCompile(`^(launchpad\.net/([a-z0-9A-Z_.\-]+(/[a-z0-9A-Z_.\-]+)?|~[a-z0-9A-Z_.\-]+/(\+junk|[a-z0-9A-Z_.\-]+)/[a-z0-9A-Z_.\-]+))(/[a-z0-9A-Z_.\-/]+)?$`)

// download checks out or updates pkg from the remote server.
func download(pkg string) (string, os.Error) {
	if strings.Contains(pkg, "..") {
		return "", os.ErrorString("invalid path (contains ..)")
	}
	if m := bitbucket.FindStringSubmatch(pkg); m != nil {
		if err := vcsCheckout(&hg, root+m[1], "http://"+m[1], m[1]); err != nil {
			return "", err
		}
		return root + pkg, nil
	}
	if m := googlecode.FindStringSubmatch(pkg); m != nil {
		var v *vcs
		switch m[2] {
		case "hg":
			v = &hg
		case "svn":
			v = &svn
		default:
			// regexp only allows hg, svn to get through
			panic("missing case in download: " + pkg)
		}
		if err := vcsCheckout(v, root+m[1], "https://"+m[1], m[1]); err != nil {
			return "", err
		}
		return root + pkg, nil
	}
	if m := github.FindStringSubmatch(pkg); m != nil {
		if strings.HasSuffix(m[1], ".git") {
			return "", os.ErrorString("repository " + pkg + " should not have .git suffix")
		}
		if err := vcsCheckout(&git, root+m[1], "http://"+m[1]+".git", m[1]); err != nil {
			return "", err
		}
		return root + pkg, nil
	}
	if m := launchpad.FindStringSubmatch(pkg); m != nil {
		// Either lp.net/<project>[/<series>[/<path>]]
		//	 or lp.net/~<user or team>/<project>/<branch>[/<path>]
		if err := vcsCheckout(&bzr, root+m[1], "https://"+m[1], m[1]); err != nil {
			return "", err
		}
		return root + pkg, nil
	}
	return "", os.ErrorString("unknown repository: " + pkg)
}

// a vcs represents a version control system
// like Mercurial, Git, or Subversion.
type vcs struct {
	cmd               string
	metadir           string
	checkout          string
	clone             string
	update            string
	updateReleaseFlag string
	pull              string
	pullForceFlag     string
	log               string
	logLimitFlag      string
	logReleaseFlag    string
}

var hg = vcs{
	cmd:               "hg",
	metadir:           ".hg",
	checkout:          "checkout",
	clone:             "clone",
	update:            "update",
	updateReleaseFlag: "release",
	pull:              "pull",
	log:               "log",
	logLimitFlag:      "-l1",
	logReleaseFlag:    "-rrelease",
}

var git = vcs{
	cmd:               "git",
	metadir:           ".git",
	checkout:          "checkout",
	clone:             "clone",
	update:            "pull",
	updateReleaseFlag: "release",
	pull:              "fetch",
	log:               "show-ref",
	logLimitFlag:      "",
	logReleaseFlag:    "release",
}

var svn = vcs{
	cmd:               "svn",
	metadir:           ".svn",
	checkout:          "checkout",
	clone:             "checkout",
	update:            "update",
	updateReleaseFlag: "release",
	log:               "log",
	logLimitFlag:      "-l1",
	logReleaseFlag:    "release",
}

var bzr = vcs{
	cmd:               "bzr",
	metadir:           ".bzr",
	checkout:          "update",
	clone:             "branch",
	update:            "update",
	updateReleaseFlag: "-rrelease",
	pull:              "pull",
	pullForceFlag:     "--overwrite",
	log:               "log",
	logLimitFlag:      "-l1",
	logReleaseFlag:    "-rrelease",
}

// Try to detect if a "release" tag exists.  If it does, update
// to the tagged version, otherwise just update the current branch.
// NOTE(_nil): svn will always fail because it is trying to get
// the revision history of a file named "release" instead of
// looking for a commit with a release tag
func (v *vcs) updateRepo(dst string) os.Error {
	if err := quietRun(dst, nil, v.cmd, v.log, v.logLimitFlag, v.logReleaseFlag); err == nil {
		if err := run(dst, nil, v.cmd, v.checkout, v.updateReleaseFlag); err != nil {
			return err
		}
	} else if err := run(dst, nil, v.cmd, v.update); err != nil {
		return err
	}
	return nil
}

// vcsCheckout checks out repo into dst using vcs.
// It tries to check out (or update, if the dst already
// exists and -u was specified on the command line)
// the repository at tag/branch "release".  If there is no
// such tag or branch, it falls back to the repository tip.
func vcsCheckout(vcs *vcs, dst, repo, dashpath string) os.Error {
	dir, err := os.Stat(dst + "/" + vcs.metadir)
	if err == nil && !dir.IsDirectory() {
		return os.ErrorString("not a directory: " + dst)
	}
	if err != nil {
		parent, _ := path.Split(dst)
		if err := os.MkdirAll(parent, 0777); err != nil {
			return err
		}
		if err := run("/", nil, vcs.cmd, vcs.clone, repo, dst); err != nil {
			return err
		}
		if err := vcs.updateRepo(dst); err != nil {
			return err
		}
		// success on first installation - report
		maybeReportToDashboard(dashpath)
	} else if *update {
		// Retrieve new revisions from the remote branch, if the VCS
		// supports this operation independently (e.g. svn doesn't)
		if vcs.pull != "" {
			if vcs.pullForceFlag != "" {
				if err := run(dst, nil, vcs.cmd, vcs.pull, vcs.pullForceFlag); err != nil {
					return err
				}
			} else if err := run(dst, nil, vcs.cmd, vcs.pull); err != nil {
				return err
			}
		}

		// Update to release or latest revision
		if err := vcs.updateRepo(dst); err != nil {
			return err
		}
	}
	return nil
}
<|endoftext|>


<commit_before>package main
import (
	"log"
	"net/http"
	"html/template"
	"time"
	"strconv"
)

type RentRoll struct {
	Conf Configuration
	AsOfDateDay string
	AsOfDateMonth string
	AsOfDateYear  string
	DefaultLeaseStartDate string
	DefaultLeaseEndDate string

}

func rentRollHandler(w http.ResponseWriter, r *http.Request) {
	log.Print("rentrollhandler - begin")
	conf := configuration()
	conf.GPlusSigninCallback = "gRentRoll"
	conf.FacebookSigninCallback = "fbRentRoll"
	month := time.Now().Month()
	day := strconv.Itoa(time.Now().Day())
	year := time.Now().Year()
	start := strconv.Itoa(int(month)) + "/" + day + "/" + strconv.Itoa(year)
	end := strconv.Itoa(int(month)) + "/" + day + "/" + strconv.Itoa(year + 3)
	rentroll := RentRoll{
		Conf: conf,
		AsOfDateDay: day,
		AsOfDateMonth: month.String(),
		AsOfDateYear: strconv.Itoa(time.Now().Year()),
		DefaultLeaseStartDate: start,
		DefaultLeaseEndDate: end,
	}
	if r.FormValue("Name") != "" {
		sqFt, _ := strconv.Atoi(r.FormValue("SqFt"))
		addTenant(r.FormValue("DbName"),
			r.FormValue("Name"),
			r.FormValue("Address"),
			sqFt,
			r.FormValue("LeaseStartDate"),
			r.FormValue("LeaseEndDate"),
			r.FormValue("BaseRent"),
			r.FormValue("Electricity"),
			r.FormValue("Gas"),
			r.FormValue("Water"),
			r.FormValue("SewageTrashRecycle"),
			r.FormValue("Comments"))
	}
	t, _ := template.ParseFiles(
		"rentroll.html",
		"templates/header.html",
		"templates/topbar.html",
		"templates/bottombar.html")
	log.Print("rentrollhandler - execute")
	t.Execute(w, rentroll)
}

type TenantsTemplate struct {
	Conf Configuration
	Tenants []Tenant
}

type Tenant struct {
	Name string
	Address string
	SqFt int
	LeaseStartDate string
	LeaseEndDate string
	BaseRent string
	Electricity string
	Gas string
	Water string
	SewageTrashRecycle string
	Comments string
}

func tenantsHandler(w http.ResponseWriter, r *http.Request) {
	tenants := []Tenant{}
	log.Print("tenantshandler - begin")
	email := r.FormValue("email")
	if email == "" || email == "dummy@dummy.com" {
		log.Print("rentroll - NO EMAIL SET")
		tenants = []Tenant{Tenant{"#1", "", 0, "", "", "", "", "", "", "", ""}}
	} else {
		dbName := email
		tenants = dbReadTenants(dbName)
	}
	t, _ := template.ParseFiles("templates/tenants.html")
	log.Print("tenanthandler - execute")
	tenantsTemplate := TenantsTemplate{
		Conf: configuration(),
		Tenants: tenants,
	}
	t.ExecuteTemplate(w, "Tenants", tenantsTemplate)
}

func rentRollTemplateHandler(w http.ResponseWriter, r *http.Request) {
	log.Print("rentrolltemplate - begin")
	conf := configuration()
	conf.GPlusSigninCallback = "gRentRollTemplate"
	conf.FacebookSigninCallback = "fbRentRollTemplate"
	rentroll := RentRoll{Conf: conf}
	t, _ := template.ParseFiles(
		"rentrolltemplate.html",
		"templates/header.html",
		"templates/topbar.html",
		"templates/bottombar.html")
	log.Print("rentrollhandler - execute")
	t.Execute(w, rentroll)
}

func addTenant(dbName, name, address string, sqft int, start, end, baseRent, electricity, gas, water, sewageTrashRecycle, comments string) {
	if name == "" {
		log.Print("addtenant - NO NAME SET")
		return
	}
	log.Print("addtenant - name")
	log.Print(name)
	if dbName == "" {
		log.Print("addtenant - NO DBNAME SET")
		return
	}
	log.Print("addtenant - dbname")
	log.Print(dbName)

	/*address := r.FormValue("address")
	sqft := r.FormValue("sqft")
	start := r.FormValue("leasestartdate")
	end := r.FormValue("leaseenddate")
	base := r.FormValue("baserent")
	electricity := r.FormValue("electricity")
	gas := r.FormValue("gas")
	water := r.FormValue("water")
	sewagetrashrecycle := r.FormValue("sewagetrashrecycle")
	comments := r.FormValue("comments")*/
	log.Print("addtenant - execute")
	dbInsert(dbName, name, address, sqft, start, end, baseRent, electricity, gas, water, sewageTrashRecycle, comments)
}
<commit_msg>Format rent as currency instead of raw numbers<commit_after>package main
import (
	"log"
	"net/http"
	"html/template"
	"time"
	"strconv"
	"github.com/joiggama/money"
)

type RentRoll struct {
	Conf Configuration
	AsOfDateDay string
	AsOfDateMonth string
	AsOfDateYear  string
	DefaultLeaseStartDate string
	DefaultLeaseEndDate string

}

func rentRollHandler(w http.ResponseWriter, r *http.Request) {
	log.Print("rentrollhandler - begin")
	conf := configuration()
	conf.GPlusSigninCallback = "gRentRoll"
	conf.FacebookSigninCallback = "fbRentRoll"
	month := time.Now().Month()
	day := strconv.Itoa(time.Now().Day())
	year := time.Now().Year()
	start := strconv.Itoa(int(month)) + "/" + day + "/" + strconv.Itoa(year)
	end := strconv.Itoa(int(month)) + "/" + day + "/" + strconv.Itoa(year + 3)
	rentroll := RentRoll{
		Conf: conf,
		AsOfDateDay: day,
		AsOfDateMonth: month.String(),
		AsOfDateYear: strconv.Itoa(time.Now().Year()),
		DefaultLeaseStartDate: start,
		DefaultLeaseEndDate: end,
	}
	if r.FormValue("Name") != "" {
		sqFt, _ := strconv.Atoi(r.FormValue("SqFt"))
		addTenant(r.FormValue("DbName"),
			r.FormValue("Name"),
			r.FormValue("Address"),
			sqFt,
			r.FormValue("LeaseStartDate"),
			r.FormValue("LeaseEndDate"),
			r.FormValue("BaseRent"),
			r.FormValue("Electricity"),
			r.FormValue("Gas"),
			r.FormValue("Water"),
			r.FormValue("SewageTrashRecycle"),
			r.FormValue("Comments"))
	}
	t, _ := template.ParseFiles(
		"rentroll.html",
		"templates/header.html",
		"templates/topbar.html",
		"templates/bottombar.html")
	log.Print("rentrollhandler - execute")
	t.Execute(w, rentroll)
}

type TenantsTemplate struct {
	Conf Configuration
	Tenants []Tenant
}

type Tenant struct {
	Name string
	Address string
	SqFt int
	LeaseStartDate string
	LeaseEndDate string
	BaseRent string
	Electricity string
	Gas string
	Water string
	SewageTrashRecycle string
	Comments string
}

func tenantsHandler(w http.ResponseWriter, r *http.Request) {
	tenants := []Tenant{}
	log.Print("tenantshandler - begin")
	email := r.FormValue("email")
	if email == "" || email == "dummy@dummy.com" {
		log.Print("rentroll - NO EMAIL SET")
		tenants = []Tenant{Tenant{"#1", "", 0, "", "", "", "", "", "", "", ""}}
	} else {
		dbName := email
		tenants = dbReadTenants(dbName)
		formatCurrency(tenants)
	}
	t, _ := template.ParseFiles("templates/tenants.html")
	log.Print("tenanthandler - execute")
	tenantsTemplate := TenantsTemplate{
		Conf: configuration(),
		Tenants: tenants,
	}
	t.ExecuteTemplate(w, "Tenants", tenantsTemplate)
}

func formatCurrency(tenants []Tenant) {
	for i, _ := range tenants {
		tenants[i].BaseRent = formatMoney(tenants[i].BaseRent)
		tenants[i].Electricity = formatMoney(tenants[i].Electricity)
		tenants[i].Gas = formatMoney(tenants[i].Gas)
		tenants[i].Water = formatMoney(tenants[i].Water)
		tenants[i].SewageTrashRecycle = formatMoney(tenants[i].SewageTrashRecycle)
	}
}

func formatMoney(mon string) string {
	if mon == "" {
		mon = "0"
	}
	val, err := strconv.ParseFloat(mon, 32)
	if err != nil {
		log.Print("formatMoney - can't parse money: ")
		log.Print(mon)
		return ""
	}
	return money.New(val)
}

func rentRollTemplateHandler(w http.ResponseWriter, r *http.Request) {
	log.Print("rentrolltemplate - begin")
	conf := configuration()
	conf.GPlusSigninCallback = "gRentRollTemplate"
	conf.FacebookSigninCallback = "fbRentRollTemplate"
	rentroll := RentRoll{Conf: conf}
	t, _ := template.ParseFiles(
		"rentrolltemplate.html",
		"templates/header.html",
		"templates/topbar.html",
		"templates/bottombar.html")
	log.Print("rentrollhandler - execute")
	t.Execute(w, rentroll)
}

func addTenant(dbName, name, address string, sqft int, start, end, baseRent, electricity, gas, water, sewageTrashRecycle, comments string) {
	if name == "" {
		log.Print("addtenant - NO NAME SET")
		return
	}
	log.Print("addtenant - name")
	log.Print(name)
	if dbName == "" {
		log.Print("addtenant - NO DBNAME SET")
		return
	}
	log.Print("addtenant - dbname")
	log.Print(dbName)

	/*address := r.FormValue("address")
	sqft := r.FormValue("sqft")
	start := r.FormValue("leasestartdate")
	end := r.FormValue("leaseenddate")
	base := r.FormValue("baserent")
	electricity := r.FormValue("electricity")
	gas := r.FormValue("gas")
	water := r.FormValue("water")
	sewagetrashrecycle := r.FormValue("sewagetrashrecycle")
	comments := r.FormValue("comments")*/
	log.Print("addtenant - execute")
	dbInsert(dbName, name, address, sqft, start, end, baseRent, electricity, gas, water, sewageTrashRecycle, comments)
}
<|endoftext|>


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

package main

import (
	"flag"
	"fmt"
	"github.com/bmizerany/pat"
	"github.com/tsuru/config"
	"github.com/tsuru/gandalf/api"
	"log"
	"net/http"
)

const version = "0.4.1"

func main() {
	dry := flag.Bool("dry", false, "dry-run: does not start the server (for testing purpose)")
	configFile := flag.String("config", "/etc/gandalf.conf", "Gandalf configuration file")
	gVersion := flag.Bool("version", false, "Print version and exit")
	flag.Parse()

	if *gVersion {
		fmt.Printf("gandalf-webserver version %s\n", version)
		return
	}

	err := config.ReadAndWatchConfigFile(*configFile)
	if err != nil {
		msg := `Could not find gandalf config file. Searched on %s.
For an example conf check gandalf/etc/gandalf.conf file.\n %s`
		log.Panicf(msg, *configFile, err)
	}
	router := pat.New()
	router.Post("/user/:name/key", http.HandlerFunc(api.AddKey))
	router.Del("/user/:name/key/:keyname", http.HandlerFunc(api.RemoveKey))
	router.Get("/user/:name/keys", http.HandlerFunc(api.ListKeys))
	router.Post("/user", http.HandlerFunc(api.NewUser))
	router.Del("/user/:name", http.HandlerFunc(api.RemoveUser))
	router.Post("/repository", http.HandlerFunc(api.NewRepository))
	router.Post("/repository/grant", http.HandlerFunc(api.GrantAccess))
	router.Del("/repository/revoke", http.HandlerFunc(api.RevokeAccess))
	router.Del("/repository/:name", http.HandlerFunc(api.RemoveRepository))
	router.Get("/repository/:name", http.HandlerFunc(api.GetRepository))
	router.Put("/repository/:name", http.HandlerFunc(api.RenameRepository))
	router.Get("/repository/:name/archive", http.HandlerFunc(api.GetArchive))
	router.Get("/repository/:name/contents", http.HandlerFunc(api.GetFileContents))
	router.Get("/repository/:name/tree/:path", http.HandlerFunc(api.GetTree))
	router.Get("/repository/:name/tree", http.HandlerFunc(api.GetTree))
	router.Get("/repository/:name/branches", http.HandlerFunc(api.GetBranches))
	router.Get("/repository/:name/tags", http.HandlerFunc(api.GetTags))
	router.Get("/repository/:name/diff/commits", http.HandlerFunc(api.GetDiff))
	router.Post("/repository/:name/commit", http.HandlerFunc(api.Commit))
	router.Get("/repository/:name/logs", http.HandlerFunc(api.GetLog))
	router.Get("/healthcheck/", http.HandlerFunc(api.HealthCheck))
	router.Post("/hook/:name", http.HandlerFunc(api.AddHook))

	bind, err := config.GetString("bind")
	if err != nil {
		var perr error
		bind, perr = config.GetString("webserver:port")
		if perr != nil {
			panic(err)
		}
	}
	if !*dry {
		log.Fatal(http.ListenAndServe(bind, router))
	}
}
<commit_msg>Removing repository tree with path on url (path will be passed by querystring)<commit_after>// Copyright 2014 gandalf authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package main

import (
	"flag"
	"fmt"
	"github.com/bmizerany/pat"
	"github.com/tsuru/config"
	"github.com/tsuru/gandalf/api"
	"log"
	"net/http"
)

const version = "0.4.1"

func main() {
	dry := flag.Bool("dry", false, "dry-run: does not start the server (for testing purpose)")
	configFile := flag.String("config", "/etc/gandalf.conf", "Gandalf configuration file")
	gVersion := flag.Bool("version", false, "Print version and exit")
	flag.Parse()

	if *gVersion {
		fmt.Printf("gandalf-webserver version %s\n", version)
		return
	}

	err := config.ReadAndWatchConfigFile(*configFile)
	if err != nil {
		msg := `Could not find gandalf config file. Searched on %s.
For an example conf check gandalf/etc/gandalf.conf file.\n %s`
		log.Panicf(msg, *configFile, err)
	}
	router := pat.New()
	router.Post("/user/:name/key", http.HandlerFunc(api.AddKey))
	router.Del("/user/:name/key/:keyname", http.HandlerFunc(api.RemoveKey))
	router.Get("/user/:name/keys", http.HandlerFunc(api.ListKeys))
	router.Post("/user", http.HandlerFunc(api.NewUser))
	router.Del("/user/:name", http.HandlerFunc(api.RemoveUser))
	router.Post("/repository", http.HandlerFunc(api.NewRepository))
	router.Post("/repository/grant", http.HandlerFunc(api.GrantAccess))
	router.Del("/repository/revoke", http.HandlerFunc(api.RevokeAccess))
	router.Del("/repository/:name", http.HandlerFunc(api.RemoveRepository))
	router.Get("/repository/:name", http.HandlerFunc(api.GetRepository))
	router.Put("/repository/:name", http.HandlerFunc(api.RenameRepository))
	router.Get("/repository/:name/archive", http.HandlerFunc(api.GetArchive))
	router.Get("/repository/:name/contents", http.HandlerFunc(api.GetFileContents))
	router.Get("/repository/:name/tree", http.HandlerFunc(api.GetTree))
	router.Get("/repository/:name/branches", http.HandlerFunc(api.GetBranches))
	router.Get("/repository/:name/tags", http.HandlerFunc(api.GetTags))
	router.Get("/repository/:name/diff/commits", http.HandlerFunc(api.GetDiff))
	router.Post("/repository/:name/commit", http.HandlerFunc(api.Commit))
	router.Get("/repository/:name/logs", http.HandlerFunc(api.GetLog))
	router.Get("/healthcheck/", http.HandlerFunc(api.HealthCheck))
	router.Post("/hook/:name", http.HandlerFunc(api.AddHook))

	bind, err := config.GetString("bind")
	if err != nil {
		var perr error
		bind, perr = config.GetString("webserver:port")
		if perr != nil {
			panic(err)
		}
	}
	if !*dry {
		log.Fatal(http.ListenAndServe(bind, router))
	}
}
<|endoftext|>


<commit_before>// SPDX-License-Identifier: MIT
//
// Copyright © 2018 Kent Gibson <warthog618@gmail.com>.

//
// Test suite for GSM module.
//
// Note that these tests provide a mockModem which does not attempt to emulate
// a serial modem, but which provides responses required to exercise gsm.go So,
// while the commands may follow the structure of the AT protocol they most
// certainly are not AT commands - just patterns that elicit the behaviour
// required for the test.

package gsm_test

import (
	"fmt"
	"io"
	"testing"
	"time"

	"github.com/pkg/errors"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
	"github.com/warthog618/modem/at"
	"github.com/warthog618/modem/gsm"
	"github.com/warthog618/modem/trace"
	"github.com/warthog618/sms/encoding/pdumode"
	"github.com/warthog618/sms/encoding/semioctet"
	"github.com/warthog618/sms/encoding/tpdu"
)

var debug = false // set to true to enable tracing of the flow to the mockModem.

func TestNew(t *testing.T) {
	mm := mockModem{cmdSet: nil, echo: false, r: make(chan []byte, 10)}
	defer teardownModem(&mm)
	a := at.New(&mm)
	patterns := []struct {
		name    string
		options []gsm.Option
		success bool
	}{
		{
			"default",
			nil,
			true,
		},
		{
			"WithPDUMode",
			[]gsm.Option{gsm.WithPDUMode},
			true,
		},
	}
	for _, p := range patterns {
		f := func(t *testing.T) {
			g := gsm.New(a, p.options...)
			if p.success {
				assert.NotNil(t, g)
			} else {
				assert.Nil(t, g)
			}
		}
		t.Run(p.name, f)
	}
}

func TestInit(t *testing.T) {
	// mocked
	cmdSet := map[string][]string{
		// for init (AT)
		string(27) + "\r\n\r\n": {"\r\n"},
		"ATZ\r\n":               {"OK\r\n"},
		"AT^CURC=0\r\n":         {"OK\r\n"},
		// for init (GSM)
		"AT+CMEE=2\r\n": {"OK\r\n"},
		"AT+CMGF=1\r\n": {"OK\r\n"},
		"AT+GCAP\r\n":   {"+GCAP: +CGSM,+DS,+ES\r\n", "OK\r\n"},
	}
	patterns := []struct {
		name     string
		options  []at.InitOption
		residual []byte
		key      string
		value    []string
		pduMode  bool
		err      error
	}{
		{
			"vanilla",
			nil,
			nil,
			"",
			nil,
			false,
			nil,
		},
		{
			"residual OKs",
			nil,
			[]byte("\r\nOK\r\nOK\r\n"),
			"",
			nil,
			false,
			nil,
		},
		{
			"residual ERRORs",
			nil,
			[]byte("\r\nERROR\r\nERROR\r\n"),
			"",
			nil,
			false,
			nil,
		},
		{
			"cruft",
			nil,
			nil,
			"AT+GCAP\r\n",
			[]string{"cruft\r\n", "+GCAP: +CGSM,+DS,+ES\r\n", "OK\r\n"},
			false,
			nil,
		},
		{
			"CMEE error",
			nil,
			nil,
			"AT+CMEE=2\r\n",
			[]string{"ERROR\r\n"},
			false,
			at.ErrError,
		},
		{
			"GCAP error",
			nil,
			nil,
			"AT+GCAP\r\n",
			[]string{"ERROR\r\n"},
			false,
			at.ErrError,
		},
		{
			"not GSM capable",
			nil,
			nil,
			"AT+GCAP\r\n",
			[]string{"+GCAP: +DS,+ES\r\n", "OK\r\n"},
			false,
			gsm.ErrNotGSMCapable,
		},
		{
			"AT init failure",
			nil,
			nil,
			"ATZ\r\n",
			[]string{"ERROR\r\n"},
			false,
			fmt.Errorf("ATZ returned error: %w", at.ErrError),
		},
		{
			"timeout",
			[]at.InitOption{at.WithTimeout(0)},
			nil,
			"",
			nil,
			false,
			at.ErrDeadlineExceeded,
		},
		{
			"unsupported PDU mode",
			nil,
			nil,
			"",
			nil,
			true,
			at.ErrError,
		},
		{
			"PDU mode",
			nil,
			nil,
			"AT+CMGF=0\r\n",
			[]string{"OK\r\n"},
			true,
			nil,
		},
	}
	for _, p := range patterns {
		f := func(t *testing.T) {
			mm := mockModem{cmdSet: cmdSet, echo: false, r: make(chan []byte, 10)}
			defer teardownModem(&mm)
			a := at.New(&mm)
			gopts := []gsm.Option{}
			if p.pduMode {
				gopts = append(gopts, gsm.WithPDUMode)
			}
			g := gsm.New(a, gopts...)
			require.NotNil(t, g)
			var oldvalue []string
			if p.residual != nil {
				mm.r <- p.residual
			}
			if p.key != "" {
				oldvalue = cmdSet[p.key]
				cmdSet[p.key] = p.value
			}
			err := g.Init(p.options...)
			if oldvalue != nil {
				cmdSet[p.key] = oldvalue
			}
			assert.Equal(t, p.err, err)
		}
		t.Run(p.name, f)
	}
}

func TestSendSMS(t *testing.T) {
	// mocked
	cmdSet := map[string][]string{
		"AT+CMGS=\"+123456789\"\r":            {"\n>"},
		"test message" + string(26):           {"\r\n", "+CMGS: 42\r\n", "\r\nOK\r\n"},
		"cruft test message" + string(26):     {"\r\n", "pad\r\n", "+CMGS: 43\r\n", "\r\nOK\r\n"},
		"malformed test message" + string(26): {"\r\n", "pad\r\n", "\r\nOK\r\n"},
	}
	patterns := []struct {
		name    string
		options []at.CommandOption
		number  string
		message string
		err     error
		mr      string
	}{
		{
			"ok",
			nil,
			"+123456789",
			"test message",
			nil,
			"42",
		},
		{
			"error",
			nil,
			"+1234567890",
			"test message",
			at.ErrError,
			"",
		},
		{
			"cruft",
			nil,
			"+123456789",
			"cruft test message",
			nil,
			"43",
		},
		{
			"malformed",
			nil,
			"+123456789",
			"malformed test message",
			gsm.ErrMalformedResponse,
			"",
		},
		{
			"timeout",
			[]at.CommandOption{at.WithTimeout(0)},
			"+123456789",
			"test message",
			at.ErrDeadlineExceeded,
			"",
		},
	}
	g, mm := setupModem(t, cmdSet)
	require.NotNil(t, g)
	require.NotNil(t, mm)
	defer teardownModem(mm)

	for _, p := range patterns {
		f := func(t *testing.T) {
			mr, err := g.SendSMS(p.number, p.message, p.options...)
			assert.Equal(t, p.err, err)
			assert.Equal(t, p.mr, mr)
		}
		t.Run(p.name, f)
	}

	// wrong mode
	g, mm = setupModem(t, cmdSet, gsm.WithPDUMode)
	require.NotNil(t, g)
	require.NotNil(t, mm)
	defer teardownModem(mm)
	p := patterns[0]
	mr, err := g.SendSMS(p.number, p.message)
	assert.Equal(t, gsm.ErrWrongMode, err)
	assert.Equal(t, "", mr)
}

func TestSendSMSPDU(t *testing.T) {
	// mocked
	cmdSet := map[string][]string{
		"AT+CMGS=6\r":                 {"\n>"},
		"00010203040506" + string(26): {"\r\n", "+CMGS: 42\r\n", "\r\nOK\r\n"},
		"00110203040506" + string(26): {"\r\n", "pad\r\n", "+CMGS: 43\r\n", "\r\nOK\r\n"},
		"00210203040506" + string(26): {"\r\n", "pad\r\n", "\r\nOK\r\n"},
	}
	patterns := []struct {
		name    string
		options []at.CommandOption
		tpdu    []byte
		err     error
		mr      string
	}{
		{
			"ok",
			nil,
			[]byte{1, 2, 3, 4, 5, 6},
			nil,
			"42",
		},
		{
			"error",
			nil,
			[]byte{1},
			at.ErrError,
			"",
		},
		{
			"cruft",
			nil,
			[]byte{0x11, 2, 3, 4, 5, 6},
			nil,
			"43",
		},
		{
			"malformed",
			nil,
			[]byte{0x21, 2, 3, 4, 5, 6},
			gsm.ErrMalformedResponse,
			"",
		},
		{
			"timeout",
			[]at.CommandOption{at.WithTimeout(0)},
			[]byte{1, 2, 3, 4, 5, 6},
			at.ErrDeadlineExceeded,
			"",
		},
	}
	g, mm := setupModem(t, cmdSet, gsm.WithPDUMode)
	require.NotNil(t, g)
	require.NotNil(t, mm)
	defer teardownModem(mm)

	for _, p := range patterns {
		f := func(t *testing.T) {
			mr, err := g.SendSMSPDU(p.tpdu, p.options...)
			assert.Equal(t, p.err, err)
			assert.Equal(t, p.mr, mr)
		}
		t.Run(p.name, f)
	}

	// wrong mode
	g, mm = setupModem(t, cmdSet)
	require.NotNil(t, g)
	require.NotNil(t, mm)
	defer teardownModem(mm)
	p := patterns[0]
	omr, oerr := g.SendSMSPDU(p.tpdu)
	assert.Equal(t, gsm.ErrWrongMode, oerr)
	assert.Equal(t, "", omr)
}

func TestWithSCA(t *testing.T) {
	var sca pdumode.SMSCAddress
	sca.Addr = "text"
	gopts := []gsm.Option{gsm.WithSCA(sca)}
	g, mm := setupModem(t, nil, gopts...)
	defer teardownModem(mm)

	tp := []byte{1, 2, 3, 4, 5, 6}
	omr, oerr := g.SendSMSPDU(tp)
	assert.Equal(t, tpdu.EncodeError("addr", semioctet.ErrInvalidDigit(0x74)), oerr)
	assert.Equal(t, "", omr)
}

type mockModem struct {
	cmdSet    map[string][]string
	echo      bool
	closed    bool
	readDelay time.Duration
	// The buffer emulating characters emitted by the modem.
	r chan []byte
}

func (m *mockModem) Read(p []byte) (n int, err error) {
	data, ok := <-m.r
	if data == nil {
		return 0, fmt.Errorf("closed")
	}
	time.Sleep(m.readDelay)
	copy(p, data) // assumes p is empty
	if !ok {
		return len(data), fmt.Errorf("closed with data")
	}
	return len(data), nil
}

func (m *mockModem) Write(p []byte) (n int, err error) {
	if m.closed {
		return 0, errors.New("closed")
	}
	if m.echo {
		m.r <- p
	}
	v := m.cmdSet[string(p)]
	if len(v) == 0 {
		m.r <- []byte("\r\nERROR\r\n")
	} else {
		for _, l := range v {
			if len(l) == 0 {
				continue
			}
			m.r <- []byte(l)
		}
	}
	return len(p), nil
}

func (m *mockModem) Close() error {
	if m.closed == false {
		m.closed = true
		close(m.r)
	}
	return nil
}

func setupModem(t *testing.T, cmdSet map[string][]string, gopts ...gsm.Option) (*gsm.GSM, *mockModem) {
	mm := &mockModem{cmdSet: cmdSet, echo: true, r: make(chan []byte, 10), readDelay: time.Millisecond}
	var modem io.ReadWriter = mm
	if debug {
		modem = trace.New(modem)
	}
	g := gsm.New(at.New(modem), gopts...)
	require.NotNil(t, g)
	return g, mm
}

func teardownModem(m *mockModem) {
	m.Close()
}
<commit_msg>add delay for timeout test<commit_after>// SPDX-License-Identifier: MIT
//
// Copyright © 2018 Kent Gibson <warthog618@gmail.com>.

//
// Test suite for GSM module.
//
// Note that these tests provide a mockModem which does not attempt to emulate
// a serial modem, but which provides responses required to exercise gsm.go So,
// while the commands may follow the structure of the AT protocol they most
// certainly are not AT commands - just patterns that elicit the behaviour
// required for the test.

package gsm_test

import (
	"fmt"
	"io"
	"testing"
	"time"

	"github.com/pkg/errors"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
	"github.com/warthog618/modem/at"
	"github.com/warthog618/modem/gsm"
	"github.com/warthog618/modem/trace"
	"github.com/warthog618/sms/encoding/pdumode"
	"github.com/warthog618/sms/encoding/semioctet"
	"github.com/warthog618/sms/encoding/tpdu"
)

var debug = false // set to true to enable tracing of the flow to the mockModem.

func TestNew(t *testing.T) {
	mm := mockModem{cmdSet: nil, echo: false, r: make(chan []byte, 10)}
	defer teardownModem(&mm)
	a := at.New(&mm)
	patterns := []struct {
		name    string
		options []gsm.Option
		success bool
	}{
		{
			"default",
			nil,
			true,
		},
		{
			"WithPDUMode",
			[]gsm.Option{gsm.WithPDUMode},
			true,
		},
	}
	for _, p := range patterns {
		f := func(t *testing.T) {
			g := gsm.New(a, p.options...)
			if p.success {
				assert.NotNil(t, g)
			} else {
				assert.Nil(t, g)
			}
		}
		t.Run(p.name, f)
	}
}

func TestInit(t *testing.T) {
	// mocked
	cmdSet := map[string][]string{
		// for init (AT)
		string(27) + "\r\n\r\n": {"\r\n"},
		"ATZ\r\n":               {"OK\r\n"},
		"AT^CURC=0\r\n":         {"OK\r\n"},
		// for init (GSM)
		"AT+CMEE=2\r\n": {"OK\r\n"},
		"AT+CMGF=1\r\n": {"OK\r\n"},
		"AT+GCAP\r\n":   {"+GCAP: +CGSM,+DS,+ES\r\n", "OK\r\n"},
	}
	patterns := []struct {
		name     string
		options  []at.InitOption
		residual []byte
		key      string
		value    []string
		pduMode  bool
		err      error
	}{
		{
			"vanilla",
			nil,
			nil,
			"",
			nil,
			false,
			nil,
		},
		{
			"residual OKs",
			nil,
			[]byte("\r\nOK\r\nOK\r\n"),
			"",
			nil,
			false,
			nil,
		},
		{
			"residual ERRORs",
			nil,
			[]byte("\r\nERROR\r\nERROR\r\n"),
			"",
			nil,
			false,
			nil,
		},
		{
			"cruft",
			nil,
			nil,
			"AT+GCAP\r\n",
			[]string{"cruft\r\n", "+GCAP: +CGSM,+DS,+ES\r\n", "OK\r\n"},
			false,
			nil,
		},
		{
			"CMEE error",
			nil,
			nil,
			"AT+CMEE=2\r\n",
			[]string{"ERROR\r\n"},
			false,
			at.ErrError,
		},
		{
			"GCAP error",
			nil,
			nil,
			"AT+GCAP\r\n",
			[]string{"ERROR\r\n"},
			false,
			at.ErrError,
		},
		{
			"not GSM capable",
			nil,
			nil,
			"AT+GCAP\r\n",
			[]string{"+GCAP: +DS,+ES\r\n", "OK\r\n"},
			false,
			gsm.ErrNotGSMCapable,
		},
		{
			"AT init failure",
			nil,
			nil,
			"ATZ\r\n",
			[]string{"ERROR\r\n"},
			false,
			fmt.Errorf("ATZ returned error: %w", at.ErrError),
		},
		{
			"timeout",
			[]at.InitOption{at.WithTimeout(0)},
			nil,
			"",
			nil,
			false,
			at.ErrDeadlineExceeded,
		},
		{
			"unsupported PDU mode",
			nil,
			nil,
			"",
			nil,
			true,
			at.ErrError,
		},
		{
			"PDU mode",
			nil,
			nil,
			"AT+CMGF=0\r\n",
			[]string{"OK\r\n"},
			true,
			nil,
		},
	}
	for _, p := range patterns {
		f := func(t *testing.T) {
			mm := mockModem{
				cmdSet:    cmdSet,
				echo:      false,
				r:         make(chan []byte, 10),
				readDelay: time.Millisecond,
			}
			defer teardownModem(&mm)
			a := at.New(&mm)
			gopts := []gsm.Option{}
			if p.pduMode {
				gopts = append(gopts, gsm.WithPDUMode)
			}
			g := gsm.New(a, gopts...)
			require.NotNil(t, g)
			var oldvalue []string
			if p.residual != nil {
				mm.r <- p.residual
			}
			if p.key != "" {
				oldvalue = cmdSet[p.key]
				cmdSet[p.key] = p.value
			}
			err := g.Init(p.options...)
			if oldvalue != nil {
				cmdSet[p.key] = oldvalue
			}
			assert.Equal(t, p.err, err)
		}
		t.Run(p.name, f)
	}
}

func TestSendSMS(t *testing.T) {
	// mocked
	cmdSet := map[string][]string{
		"AT+CMGS=\"+123456789\"\r":            {"\n>"},
		"test message" + string(26):           {"\r\n", "+CMGS: 42\r\n", "\r\nOK\r\n"},
		"cruft test message" + string(26):     {"\r\n", "pad\r\n", "+CMGS: 43\r\n", "\r\nOK\r\n"},
		"malformed test message" + string(26): {"\r\n", "pad\r\n", "\r\nOK\r\n"},
	}
	patterns := []struct {
		name    string
		options []at.CommandOption
		number  string
		message string
		err     error
		mr      string
	}{
		{
			"ok",
			nil,
			"+123456789",
			"test message",
			nil,
			"42",
		},
		{
			"error",
			nil,
			"+1234567890",
			"test message",
			at.ErrError,
			"",
		},
		{
			"cruft",
			nil,
			"+123456789",
			"cruft test message",
			nil,
			"43",
		},
		{
			"malformed",
			nil,
			"+123456789",
			"malformed test message",
			gsm.ErrMalformedResponse,
			"",
		},
		{
			"timeout",
			[]at.CommandOption{at.WithTimeout(0)},
			"+123456789",
			"test message",
			at.ErrDeadlineExceeded,
			"",
		},
	}
	g, mm := setupModem(t, cmdSet)
	require.NotNil(t, g)
	require.NotNil(t, mm)
	defer teardownModem(mm)

	for _, p := range patterns {
		f := func(t *testing.T) {
			mr, err := g.SendSMS(p.number, p.message, p.options...)
			assert.Equal(t, p.err, err)
			assert.Equal(t, p.mr, mr)
		}
		t.Run(p.name, f)
	}

	// wrong mode
	g, mm = setupModem(t, cmdSet, gsm.WithPDUMode)
	require.NotNil(t, g)
	require.NotNil(t, mm)
	defer teardownModem(mm)
	p := patterns[0]
	mr, err := g.SendSMS(p.number, p.message)
	assert.Equal(t, gsm.ErrWrongMode, err)
	assert.Equal(t, "", mr)
}

func TestSendSMSPDU(t *testing.T) {
	// mocked
	cmdSet := map[string][]string{
		"AT+CMGS=6\r":                 {"\n>"},
		"00010203040506" + string(26): {"\r\n", "+CMGS: 42\r\n", "\r\nOK\r\n"},
		"00110203040506" + string(26): {"\r\n", "pad\r\n", "+CMGS: 43\r\n", "\r\nOK\r\n"},
		"00210203040506" + string(26): {"\r\n", "pad\r\n", "\r\nOK\r\n"},
	}
	patterns := []struct {
		name    string
		options []at.CommandOption
		tpdu    []byte
		err     error
		mr      string
	}{
		{
			"ok",
			nil,
			[]byte{1, 2, 3, 4, 5, 6},
			nil,
			"42",
		},
		{
			"error",
			nil,
			[]byte{1},
			at.ErrError,
			"",
		},
		{
			"cruft",
			nil,
			[]byte{0x11, 2, 3, 4, 5, 6},
			nil,
			"43",
		},
		{
			"malformed",
			nil,
			[]byte{0x21, 2, 3, 4, 5, 6},
			gsm.ErrMalformedResponse,
			"",
		},
		{
			"timeout",
			[]at.CommandOption{at.WithTimeout(0)},
			[]byte{1, 2, 3, 4, 5, 6},
			at.ErrDeadlineExceeded,
			"",
		},
	}
	g, mm := setupModem(t, cmdSet, gsm.WithPDUMode)
	require.NotNil(t, g)
	require.NotNil(t, mm)
	defer teardownModem(mm)

	for _, p := range patterns {
		f := func(t *testing.T) {
			mr, err := g.SendSMSPDU(p.tpdu, p.options...)
			assert.Equal(t, p.err, err)
			assert.Equal(t, p.mr, mr)
		}
		t.Run(p.name, f)
	}

	// wrong mode
	g, mm = setupModem(t, cmdSet)
	require.NotNil(t, g)
	require.NotNil(t, mm)
	defer teardownModem(mm)
	p := patterns[0]
	omr, oerr := g.SendSMSPDU(p.tpdu)
	assert.Equal(t, gsm.ErrWrongMode, oerr)
	assert.Equal(t, "", omr)
}

func TestWithSCA(t *testing.T) {
	var sca pdumode.SMSCAddress
	sca.Addr = "text"
	gopts := []gsm.Option{gsm.WithSCA(sca)}
	g, mm := setupModem(t, nil, gopts...)
	defer teardownModem(mm)

	tp := []byte{1, 2, 3, 4, 5, 6}
	omr, oerr := g.SendSMSPDU(tp)
	assert.Equal(t, tpdu.EncodeError("addr", semioctet.ErrInvalidDigit(0x74)), oerr)
	assert.Equal(t, "", omr)
}

type mockModem struct {
	cmdSet    map[string][]string
	echo      bool
	closed    bool
	readDelay time.Duration
	// The buffer emulating characters emitted by the modem.
	r chan []byte
}

func (m *mockModem) Read(p []byte) (n int, err error) {
	data, ok := <-m.r
	if data == nil {
		return 0, fmt.Errorf("closed")
	}
	time.Sleep(m.readDelay)
	copy(p, data) // assumes p is empty
	if !ok {
		return len(data), fmt.Errorf("closed with data")
	}
	return len(data), nil
}

func (m *mockModem) Write(p []byte) (n int, err error) {
	if m.closed {
		return 0, errors.New("closed")
	}
	if m.echo {
		m.r <- p
	}
	v := m.cmdSet[string(p)]
	if len(v) == 0 {
		m.r <- []byte("\r\nERROR\r\n")
	} else {
		for _, l := range v {
			if len(l) == 0 {
				continue
			}
			m.r <- []byte(l)
		}
	}
	return len(p), nil
}

func (m *mockModem) Close() error {
	if m.closed == false {
		m.closed = true
		close(m.r)
	}
	return nil
}

func setupModem(t *testing.T, cmdSet map[string][]string, gopts ...gsm.Option) (*gsm.GSM, *mockModem) {
	mm := &mockModem{
		cmdSet:    cmdSet,
		echo:      true,
		r:         make(chan []byte, 10),
		readDelay: time.Millisecond,
	}
	var modem io.ReadWriter = mm
	if debug {
		modem = trace.New(modem)
	}
	g := gsm.New(at.New(modem), gopts...)
	require.NotNil(t, g)
	return g, mm
}

func teardownModem(m *mockModem) {
	m.Close()
}
<|endoftext|>