<commit_before>package main

import (
	"fmt"
	"net/http"
	"strconv"

	"github.com/Sirupsen/logrus"
	"github.com/gorilla/context"

	pg "github.com/mozilla/tls-observatory/database"
	"github.com/mozilla/tls-observatory/logger"
)

func ScanHandler(w http.ResponseWriter, r *http.Request) {

	log := logger.GetLogger()

	log.WithFields(logrus.Fields{
		"form values": r.Form,
		"headers":     r.Header,
	}).Debug("Received request")

	var (
		status int
		err    error
	)

	defer func() {
		if nil != err {
			http.Error(w, err.Error(), status)
		}
	}()

	val, ok := context.GetOk(r, dbKey)

	if !ok {
		log.Error("Could not find db in request context")
		status = http.StatusInternalServerError
		return
	}

	db := val.(*pg.DB)

	db.Ping()

	domain := r.FormValue("target")

	if validateDomain(domain) {

		scan, err := db.NewScan(domain, -1) //no replay

		sID := strconv.FormatInt(scan.ID, 10)

		if err != nil {
			log.WithFields(logrus.Fields{
				"domain": domain,
				"error":  err.Error(),
			}).Error("Could not create new scan")
			status = http.StatusInternalServerError
			return
		}

		resp := fmt.Sprintf(`{"scan_id":"%d"}`, scan.ID)

		_, err = w.Write([]byte(resp))

		if err != nil {
			log.WithFields(logrus.Fields{
				"domain":  domain,
				"error":   err.Error(),
				"scan_id": scan.ID,
			}).Error("Could not write scan id to response")
			status = http.StatusInternalServerError
			return
		}

		status = http.StatusOK
	} else {
		status = http.StatusBadRequest
	}

}

func ResultHandler(w http.ResponseWriter, r *http.Request) {

	var (
		status int
		err    error
	)

	defer func() {
		if nil != err {
			http.Error(w, err.Error(), status)
		}
	}()

	domain := r.FormValue("id")

	if validateDomain(domain) {

		status = http.StatusOK

	} else {
		status = http.StatusBadRequest
		return
	}

}

func CertificateHandler(w http.ResponseWriter, r *http.Request) {

	var (
		status int
		err    error
	)

	defer func() {
		if nil != err {
			http.Error(w, err.Error(), status)
		}
	}()

	domain := r.FormValue("target")

	if validateDomain(domain) {

		//		raw := r.FormValue("raw")

		//		rawCert := false

		//		if raw == "true" {
		//			rawCert = true
		//		}

		status = http.StatusOK

	} else {
		status = http.StatusBadRequest
		return
	}

}

func validateDomain(domain string) bool {

	// TODO
	// Need to validate the domain, in a way,
	// before passing it to the retriever queue

	return true
}
<commit_msg>Fix unused sID value in API handler<commit_after>package main

import (
	"fmt"
	"net/http"

	"github.com/Sirupsen/logrus"
	"github.com/gorilla/context"

	pg "github.com/mozilla/tls-observatory/database"
	"github.com/mozilla/tls-observatory/logger"
)

func ScanHandler(w http.ResponseWriter, r *http.Request) {
	var (
		status int
		err    error
	)
	defer func() {
		if nil != err {
			http.Error(w, err.Error(), status)
		}
	}()
	log := logger.GetLogger()
	log.WithFields(logrus.Fields{
		"form values": r.Form,
		"headers":     r.Header,
	}).Debug("Received request")

	val, ok := context.GetOk(r, dbKey)
	if !ok {
		log.Error("Could not find db in request context")
		status = http.StatusInternalServerError
		return
	}

	db := val.(*pg.DB)
	db.Ping()

	domain := r.FormValue("target")
	if validateDomain(domain) {

		scan, err := db.NewScan(domain, -1) //no replay
		if err != nil {
			log.WithFields(logrus.Fields{
				"domain": domain,
				"error":  err.Error(),
			}).Error("Could not create new scan")
			status = http.StatusInternalServerError
			return
		}

		resp := fmt.Sprintf(`{"scan_id":"%d"}`, scan.ID)

		_, err = w.Write([]byte(resp))
		if err != nil {
			log.WithFields(logrus.Fields{
				"domain":  domain,
				"error":   err.Error(),
				"scan_id": scan.ID,
			}).Error("Could not write scan id to response")
			status = http.StatusInternalServerError
			return
		}
		status = http.StatusOK
	} else {
		status = http.StatusBadRequest
	}

}

func ResultHandler(w http.ResponseWriter, r *http.Request) {

	var (
		status int
		err    error
	)

	defer func() {
		if nil != err {
			http.Error(w, err.Error(), status)
		}
	}()

	domain := r.FormValue("id")

	if validateDomain(domain) {

		status = http.StatusOK

	} else {
		status = http.StatusBadRequest
		return
	}

}

func CertificateHandler(w http.ResponseWriter, r *http.Request) {

	var (
		status int
		err    error
	)

	defer func() {
		if nil != err {
			http.Error(w, err.Error(), status)
		}
	}()

	domain := r.FormValue("target")

	if validateDomain(domain) {

		//		raw := r.FormValue("raw")

		//		rawCert := false

		//		if raw == "true" {
		//			rawCert = true
		//		}

		status = http.StatusOK

	} else {
		status = http.StatusBadRequest
		return
	}

}

func validateDomain(domain string) bool {

	// TODO
	// Need to validate the domain, in a way,
	// before passing it to the retriever queue

	return true
}
<|endoftext|>


<commit_before>// This file was auto-generated by the veyron vdl tool.
// Source: channel.vdl

package channel

import (
	// The non-user imports are prefixed with "__" to prevent collisions.
	__vdl "v.io/core/veyron2/vdl"
	__vdlutil "v.io/core/veyron2/vdl/vdlutil"
)

type Request struct {
	Type string
	Seq  uint32
	Body __vdlutil.Any
}

func (Request) __VDLReflect(struct {
	Name string "v.io/wspr/veyron/services/wsprd/channel.Request"
}) {
}

type Response struct {
	ReqSeq uint32
	Err    string // TODO(bprosnitz) change this back to error when it is possible to do so. (issue 368)
	Body   __vdlutil.Any
}

func (Response) __VDLReflect(struct {
	Name string "v.io/wspr/veyron/services/wsprd/channel.Response"
}) {
}

type (
	// Message represents any single field of the Message union type.
	Message interface {
		// Index returns the field index.
		Index() int
		// Interface returns the field value as an interface.
		Interface() interface{}
		// Name returns the field name.
		Name() string
		// __VDLReflect describes the Message union type.
		__VDLReflect(__MessageReflect)
	}
	// MessageRequest represents field Request of the Message union type.
	MessageRequest struct{ Value Request }
	// MessageResponse represents field Response of the Message union type.
	MessageResponse struct{ Value Response }
	// __MessageReflect describes the Message union type.
	__MessageReflect struct {
		Name  string "v.io/wspr/veyron/services/wsprd/channel.Message"
		Type  Message
		Union struct {
			Request  MessageRequest
			Response MessageResponse
		}
	}
)

func (x MessageRequest) Index() int                    { return 0 }
func (x MessageRequest) Interface() interface{}        { return x.Value }
func (x MessageRequest) Name() string                  { return "Request" }
func (x MessageRequest) __VDLReflect(__MessageReflect) {}

func (x MessageResponse) Index() int                    { return 1 }
func (x MessageResponse) Interface() interface{}        { return x.Value }
func (x MessageResponse) Name() string                  { return "Response" }
func (x MessageResponse) __VDLReflect(__MessageReflect) {}

func init() {
	__vdl.Register(Request{})
	__vdl.Register(Response{})
	__vdl.Register(Message(MessageRequest{Request{}}))
}
<commit_msg>wspr: Rename vdlutil.Any to vdl.AnyRep.<commit_after>// This file was auto-generated by the veyron vdl tool.
// Source: channel.vdl

package channel

import (
	// The non-user imports are prefixed with "__" to prevent collisions.
	__vdl "v.io/core/veyron2/vdl"
)

type Request struct {
	Type string
	Seq  uint32
	Body __vdl.AnyRep
}

func (Request) __VDLReflect(struct {
	Name string "v.io/wspr/veyron/services/wsprd/channel.Request"
}) {
}

type Response struct {
	ReqSeq uint32
	Err    string // TODO(bprosnitz) change this back to error when it is possible to do so. (issue 368)
	Body   __vdl.AnyRep
}

func (Response) __VDLReflect(struct {
	Name string "v.io/wspr/veyron/services/wsprd/channel.Response"
}) {
}

type (
	// Message represents any single field of the Message union type.
	Message interface {
		// Index returns the field index.
		Index() int
		// Interface returns the field value as an interface.
		Interface() interface{}
		// Name returns the field name.
		Name() string
		// __VDLReflect describes the Message union type.
		__VDLReflect(__MessageReflect)
	}
	// MessageRequest represents field Request of the Message union type.
	MessageRequest struct{ Value Request }
	// MessageResponse represents field Response of the Message union type.
	MessageResponse struct{ Value Response }
	// __MessageReflect describes the Message union type.
	__MessageReflect struct {
		Name  string "v.io/wspr/veyron/services/wsprd/channel.Message"
		Type  Message
		Union struct {
			Request  MessageRequest
			Response MessageResponse
		}
	}
)

func (x MessageRequest) Index() int                    { return 0 }
func (x MessageRequest) Interface() interface{}        { return x.Value }
func (x MessageRequest) Name() string                  { return "Request" }
func (x MessageRequest) __VDLReflect(__MessageReflect) {}

func (x MessageResponse) Index() int                    { return 1 }
func (x MessageResponse) Interface() interface{}        { return x.Value }
func (x MessageResponse) Name() string                  { return "Response" }
func (x MessageResponse) __VDLReflect(__MessageReflect) {}

func init() {
	__vdl.Register(Request{})
	__vdl.Register(Response{})
	__vdl.Register(Message(MessageRequest{Request{}}))
}
<|endoftext|>


<commit_before>package gsmake

import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"net/url"
	"path/filepath"
	"strings"
	"time"

	"github.com/gsdocker/gserrors"
	"github.com/gsdocker/gslogger"
	"github.com/gsdocker/gsos"
	"github.com/gsmake/gsmake/fs"
	"github.com/gsmake/gsmake/vfs"
)

// gsmake predeined environment variables
const (
	EnvHome = "GSMAKE_HOME"
)

// Errors .
var (
	ErrLoad = errors.New("load package error")
)

// Properties .
type Properties map[string]interface{}

// Expand rewrites content to replace ${k} with properties[k] for each key k in match.
func Expand(content string, properties Properties) string {
	for k, v := range properties {

		if stringer, ok := v.(fmt.Stringer); ok {
			fmt.Println(stringer.String())
			content = strings.Replace(content, "${"+k+"}", stringer.String(), -1)
		} else {
			content = strings.Replace(content, "${"+k+"}", fmt.Sprintf("%v", v), -1)
		}

	}
	return content
}

// Import the gsmake import instruction description
type Import struct {
	Name    string // import package name
	Version string // import package version
	Domain  string `json:"scope"` // runtimes import flag, default is AOT import
	SCM     string // the source control manager type
	URL     string // remote url
}

// Task package defined task description
type Task struct {
	Prev        string // depend task name
	Description string // task description
	Package     string `json:"-"` // package name which defined this task
}

// Package describe a gsmake package object
type Package struct {
	Name       string          // package name string
	Import     []Import        // package import field
	Task       map[string]Task // package defined task
	Properties Properties      // properties
	vfspath    string          // vfs path
}

// Loader package loader
type Loader struct {
	gslogger.Log                     // mixin Log APIs
	packages     map[string]*Package // loaded packages
	checkerOfDCG []*Package          // DCG check stack
	rootfs       vfs.RootFS          // vfs object
	targetpath   string              // the loading package path
}

func load(rootpath string, target string) (*Loader, error) {

	fullpath, err := filepath.Abs(target)

	if err != nil {
		return nil, err
	}

	rootfs, err := vfs.New(rootpath, fullpath)

	if err != nil {
		return nil, err
	}

	loader := &Loader{
		Log:        gslogger.Get("loader"),
		targetpath: fullpath,
		packages:   make(map[string]*Package),
		rootfs:     rootfs,
	}

	loader.I("load package ...")

	start := time.Now()

	err = loader.load()

	loader.I("load package -- success %s", time.Now().Sub(start))

	if err != nil {
		return nil, err
	}

	return loader, nil
}

func (loader *Loader) load() error {

	jsonfile := filepath.Join(loader.targetpath, ".gsmake.json")

	if !fs.Exists(jsonfile) {

		return gserrors.Newf(
			ErrLoad,
			"target package not exists or is not a gsmake package\n\t%s",
			loader.targetpath,
		)
	}

	pkg, err := loadjson(jsonfile)

	if err != nil {
		return err
	}

	pkg, err = loader.loadpackagev2("", pkg.Name, loader.targetpath)

	if err != nil {
		return err
	}

	target := fmt.Sprintf("gsmake://%s?domain=task", pkg.Name)

	src := fmt.Sprintf("file://%s?version=current", loader.targetpath)

	pkg.vfspath = target

	loader.packages[pkg.vfspath] = pkg

	if !loader.rootfs.Mounted(src, target) {
		if err := loader.rootfs.Mount(src, target); err != nil {
			return err
		}
	}

	// try load gsmake
	if _, ok := loader.packages["gsmake://github.com/gsmake/gsmake?domain=task"]; !ok {

		pkg, err := loader.loadpackage(Import{
			Name:    "github.com/gsmake/gsmake",
			Version: "v2.0",
			SCM:     "git",
			Domain:  "task",
		})

		if err != nil {
			return gserrors.Newf(err, "load package github.com/gsmake/gsmake error")
		}

		loader.packages[pkg.vfspath] = pkg
	}

	// dismount not loaded packages

	entries, err := loader.rootfs.List()

	if err != nil {
		return gserrors.Newf(err, "list vfs nodes error")
	}

	for k := range loader.packages {
		loader.D("loaded package :%s", k)
	}

	for target := range entries {
		if _, ok := loader.packages[target]; !ok {
			loader.I("dismount :%s", target)
			loader.rootfs.Dismount(target)
		}
	}

	return nil
}

func (loader *Loader) checkDCG(name string) error {

	var stream bytes.Buffer

	for _, pkg := range loader.checkerOfDCG {
		if pkg.Name == name || stream.Len() != 0 {
			stream.WriteString(fmt.Sprintf("\t%s import\n", pkg.Name))
		}
	}

	if stream.Len() != 0 {
		return gserrors.Newf(ErrLoad, "circular package import :\n%s\t%s", stream.String(), name)
	}

	return nil
}

func (loader *Loader) loadpackage(i Import) (*Package, error) {

	target := fmt.Sprintf("gsmake://%s?domain=%s", i.Name, i.Domain)

	src := fmt.Sprintf("%s://%s?version=%s", i.SCM, i.Name, i.Version)

	if !loader.rootfs.Mounted(src, target) {
		if err := loader.rootfs.Mount(src, target); err != nil {
			return nil, err
		}
	}

	_, entry, err := loader.rootfs.Open(target)

	if err != nil {
		return nil, err
	}

	if pkg, ok := loader.packages[target]; ok {
		return pkg, nil
	}

	// DCG check
	if err := loader.checkDCG(target); err != nil {
		return nil, err
	}

	importpkg, err := loader.loadpackagev2(i.Domain, i.Name, entry.Mapping)

	importpkg.vfspath = target

	return importpkg, err
}

func (loader *Loader) loadpackagev2(domain, name, fullpath string) (*Package, error) {

	jsonfile := filepath.Join(fullpath, ".gsmake.json")

	if !gsos.IsExist(jsonfile) {
		// this package is a traditional golang package
		return &Package{
			Name: name,
		}, nil
	}

	pkg, err := loadjson(jsonfile)

	if err != nil {
		return nil, err
	}

	loader.checkerOfDCG = append(loader.checkerOfDCG, pkg)

	for _, v := range pkg.Import {

		if _, ok := loader.packages[v.Name]; ok {
			continue
		}

		if v.Version == "" {
			v.Version = "current"
		}

		if v.SCM == "" {

			u, err := url.Parse(fmt.Sprintf("https://%s", v.Name))

			if err != nil {
				return nil, gserrors.Newf(err, "%s invalid import package :%s", name, v.Name)
			}

			v.SCM = loader.rootfs.Protocol(u.Host)
		}

		domains := strings.Split(v.Domain, "|")

		if len(domains) == 1 && domains[0] == "" {
			domains = []string{"task"}
		}

		for _, d := range domains {

			if domain != d && domain != "" {

				continue
			}

			v.Domain = d

			importpkg, err := loader.loadpackage(v)

			if err != nil {
				return nil, err
			}

			loader.packages[importpkg.vfspath] = importpkg
		}
	}

	loader.checkerOfDCG = loader.checkerOfDCG[:len(loader.checkerOfDCG)-1]

	return pkg, nil
}

func loadjson(file string) (*Package, error) {

	content, err := ioutil.ReadFile(file)

	if err != nil {
		return nil, gserrors.Newf(err, "load config file err\n\t%s", file)
	}

	var config *Package

	err = json.Unmarshal(content, &config)

	if err != nil {
		return nil, gserrors.Newf(err, "unmarshal .gsmake.json file error\n\tfile:%s", file)
	}

	return config, nil
}
<commit_msg>update<commit_after>package gsmake

import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"net/url"
	"path/filepath"
	"strings"
	"time"

	"github.com/gsdocker/gserrors"
	"github.com/gsdocker/gslogger"
	"github.com/gsdocker/gsos"
	"github.com/gsmake/gsmake/fs"
	"github.com/gsmake/gsmake/vfs"
)

// gsmake predeined environment variables
const (
	EnvHome = "GSMAKE_HOME"
)

// Errors .
var (
	ErrLoad = errors.New("load package error")
)

// Properties .
type Properties map[string]interface{}

// Expand rewrites content to replace ${k} with properties[k] for each key k in match.
func Expand(content string, properties Properties) string {
	for k, v := range properties {

		if stringer, ok := v.(fmt.Stringer); ok {
			fmt.Println(stringer.String())
			content = strings.Replace(content, "${"+k+"}", stringer.String(), -1)
		} else {
			content = strings.Replace(content, "${"+k+"}", fmt.Sprintf("%v", v), -1)
		}

	}
	return content
}

// Import the gsmake import instruction description
type Import struct {
	Name    string // import package name
	Version string // import package version
	Domain  string `json:"scope"` // runtimes import flag, default is AOT import
	SCM     string // the source control manager type
	URL     string // remote url
}

// Task package defined task description
type Task struct {
	Prev        string // depend task name
	Description string // task description
	Package     string `json:"-"` // package name which defined this task
}

// Package describe a gsmake package object
type Package struct {
	Name       string          // package name string
	Import     []Import        // package import field
	Task       map[string]Task // package defined task
	Properties Properties      // properties
	vfspath    string          // vfs path
}

// Loader package loader
type Loader struct {
	gslogger.Log                     // mixin Log APIs
	packages     map[string]*Package // loaded packages
	checkerOfDCG []*Package          // DCG check stack
	rootfs       vfs.RootFS          // vfs object
	targetpath   string              // the loading package path
}

func load(rootpath string, target string) (*Loader, error) {

	fullpath, err := filepath.Abs(target)

	if err != nil {
		return nil, err
	}

	rootfs, err := vfs.New(rootpath, fullpath)

	if err != nil {
		return nil, err
	}

	loader := &Loader{
		Log:        gslogger.Get("loader"),
		targetpath: fullpath,
		packages:   make(map[string]*Package),
		rootfs:     rootfs,
	}

	loader.I("load package ...")

	start := time.Now()

	err = loader.load()

	loader.I("load package -- success %s", time.Now().Sub(start))

	if err != nil {
		return nil, err
	}

	return loader, nil
}

func (loader *Loader) load() error {

	jsonfile := filepath.Join(loader.targetpath, ".gsmake.json")

	if !fs.Exists(jsonfile) {

		return gserrors.Newf(
			ErrLoad,
			"target package not exists or is not a gsmake package\n\t%s",
			loader.targetpath,
		)
	}

	pkg, err := loadjson(jsonfile)

	if err != nil {
		return err
	}

	pkg, err = loader.loadpackagev2("", pkg.Name, loader.targetpath)

	if err != nil {
		return err
	}

	target := fmt.Sprintf("gsmake://%s?domain=task", pkg.Name)

	src := fmt.Sprintf("file://%s?version=current", loader.targetpath)

	pkg.vfspath = target

	loader.packages[pkg.vfspath] = pkg

	if !loader.rootfs.Mounted(src, target) {
		if err := loader.rootfs.Mount(src, target); err != nil {
			return err
		}
	}

	// try load gsmake
	if _, ok := loader.packages["gsmake://github.com/gsmake/gsmake?domain=task"]; !ok {

		pkg, err := loader.loadpackage(Import{
			Name:    "github.com/gsmake/gsmake",
			Version: "release/v2.0",
			SCM:     "git",
			Domain:  "task",
		})

		if err != nil {
			return gserrors.Newf(err, "load package github.com/gsmake/gsmake error")
		}

		loader.packages[pkg.vfspath] = pkg
	}

	// dismount not loaded packages

	entries, err := loader.rootfs.List()

	if err != nil {
		return gserrors.Newf(err, "list vfs nodes error")
	}

	for k := range loader.packages {
		loader.D("loaded package :%s", k)
	}

	for target := range entries {
		if _, ok := loader.packages[target]; !ok {
			loader.I("dismount :%s", target)
			loader.rootfs.Dismount(target)
		}
	}

	return nil
}

func (loader *Loader) checkDCG(name string) error {

	var stream bytes.Buffer

	for _, pkg := range loader.checkerOfDCG {
		if pkg.Name == name || stream.Len() != 0 {
			stream.WriteString(fmt.Sprintf("\t%s import\n", pkg.Name))
		}
	}

	if stream.Len() != 0 {
		return gserrors.Newf(ErrLoad, "circular package import :\n%s\t%s", stream.String(), name)
	}

	return nil
}

func (loader *Loader) loadpackage(i Import) (*Package, error) {

	target := fmt.Sprintf("gsmake://%s?domain=%s", i.Name, i.Domain)

	src := fmt.Sprintf("%s://%s?version=%s", i.SCM, i.Name, i.Version)

	if !loader.rootfs.Mounted(src, target) {
		if err := loader.rootfs.Mount(src, target); err != nil {
			return nil, err
		}
	}

	_, entry, err := loader.rootfs.Open(target)

	if err != nil {
		return nil, err
	}

	if pkg, ok := loader.packages[target]; ok {
		return pkg, nil
	}

	// DCG check
	if err := loader.checkDCG(target); err != nil {
		return nil, err
	}

	importpkg, err := loader.loadpackagev2(i.Domain, i.Name, entry.Mapping)

	importpkg.vfspath = target

	return importpkg, err
}

func (loader *Loader) loadpackagev2(domain, name, fullpath string) (*Package, error) {

	jsonfile := filepath.Join(fullpath, ".gsmake.json")

	if !gsos.IsExist(jsonfile) {
		// this package is a traditional golang package
		return &Package{
			Name: name,
		}, nil
	}

	pkg, err := loadjson(jsonfile)

	if err != nil {
		return nil, err
	}

	loader.checkerOfDCG = append(loader.checkerOfDCG, pkg)

	for _, v := range pkg.Import {

		if _, ok := loader.packages[v.Name]; ok {
			continue
		}

		if v.Version == "" {
			v.Version = "current"
		}

		if v.SCM == "" {

			u, err := url.Parse(fmt.Sprintf("https://%s", v.Name))

			if err != nil {
				return nil, gserrors.Newf(err, "%s invalid import package :%s", name, v.Name)
			}

			v.SCM = loader.rootfs.Protocol(u.Host)
		}

		domains := strings.Split(v.Domain, "|")

		if len(domains) == 1 && domains[0] == "" {
			domains = []string{"task"}
		}

		for _, d := range domains {

			if domain != d && domain != "" {

				continue
			}

			v.Domain = d

			importpkg, err := loader.loadpackage(v)

			if err != nil {
				return nil, err
			}

			loader.packages[importpkg.vfspath] = importpkg
		}
	}

	loader.checkerOfDCG = loader.checkerOfDCG[:len(loader.checkerOfDCG)-1]

	return pkg, nil
}

func loadjson(file string) (*Package, error) {

	content, err := ioutil.ReadFile(file)

	if err != nil {
		return nil, gserrors.Newf(err, "load config file err\n\t%s", file)
	}

	var config *Package

	err = json.Unmarshal(content, &config)

	if err != nil {
		return nil, gserrors.Newf(err, "unmarshal .gsmake.json file error\n\tfile:%s", file)
	}

	return config, nil
}
<|endoftext|>


<commit_before>package boltqueue

import (
	"encoding/binary"
	"errors"
	"time"

	"github.com/boltdb/bolt"
)

var (
	levelName = map[int][]byte{
		1: []byte("1"),
		2: []byte("2"),
		3: []byte("3"),
		4: []byte("4"),
		5: []byte("5"),
	}
)

// TODO: Interfacification of messages

// Message represents a message in the priority queue
type Message struct {
	key      int64
	value    []byte
	priority int
}

// NewMessage generates a new priority queue message with a priority range of
// 1-5
func NewMessage(priority int, value string) *Message {
	return &Message{getKey(), []byte(value), priority}
}

// ToString outputs the string representation of the message's value
func (m *Message) ToString() string {
	return string(m.value)
}

// PQueue is a priority queue backed by a Bolt database on disk
type PQueue struct {
	conn *bolt.DB
}

// NewPQueue loads or creates a new PQueue with the given filename
func NewPQueue(filename string) (*PQueue, error) {
	db, err := bolt.Open(filename, 0600, nil)
	if err != nil {
		return nil, err
	}

	// Ensure the priority buckets exist before using the queue
	err = db.Update(func(tx *bolt.Tx) error {
		for i := 1; i <= 5; i++ {
			_, err = tx.CreateBucketIfNotExists(levelName[int(i)])
			if err != nil {
				return err
			}
		}
		return nil
	})

	if err != nil {
		return nil, err
	}

	return &PQueue{db}, nil
}

// Enqueue adds a message to the queue at its appropriate priority level
func (b *PQueue) Enqueue(m *Message) error {
	if _, ok := levelName[m.priority]; !ok {
		return errors.New("invalid priority")
	}

	return b.conn.Update(func(tx *bolt.Tx) error {
		dbucket := tx.Bucket(levelName[m.priority])
		if dbucket == nil {
			return errors.New("data bucket does not exist")
		}

		// Push
		kb := make([]byte, 8)
		binary.BigEndian.PutUint64(kb, uint64(m.key))
		if err := dbucket.Put(kb, m.value); err != nil {
			return err
		}

		return nil
	})
}

// Dequeue removes the oldest, highest priority message from the queue and
// returns it
func (b *PQueue) Dequeue() (*Message, error) {
	var m *Message
	err := b.conn.Update(func(tx *bolt.Tx) error {

		for i := 1; i <= 5; i++ {
			dbucket := tx.Bucket(levelName[int(i)])
			if dbucket == nil {
				return errors.New("data bucket does not exist")
			}
			// Skip empty queues
			if dbucket.Stats().KeyN == 0 {
				continue
			}
			cur := dbucket.Cursor()
			k, v := cur.First()
			ki, _ := binary.Varint(k)

			m = &Message{ki, cloneBytes(v), i}
			if err := cur.Delete(); err != nil {
				return err
			}
			break
		}
		return nil
	})
	if err != nil {
		return nil, err
	}

	return m, nil
}

// Size returns the number of entries of a given priority from 1 to 5
func (b *PQueue) Size(priority int) (int, error) {
	if _, ok := levelName[priority]; !ok {
		return 0, errors.New("invalid priority")
	}

	var size int
	err := b.conn.View(func(tx *bolt.Tx) error {
		dbucket := tx.Bucket(levelName[priority])
		if dbucket == nil {
			return errors.New("data bucket does not exist")
		}
		size = dbucket.Stats().KeyN
		return nil
	})
	if err != nil {
		return 0, err
	}

	return size, nil
}

var lastTime int64

// Inspired by DavidHui/httpq/boltqueue
func getKey() int64 {
	t := time.Now().UnixNano()
	if t <= lastTime {
		t = lastTime + 1
	}
	lastTime = t
	return t
}

// taken from boltDB. Avoids corruption when re-queueing
func cloneBytes(v []byte) []byte {
	var clone = make([]byte, len(v))
	copy(clone, v)
	return clone
}
<commit_msg>incorrect attribution<commit_after>package boltqueue

import (
	"encoding/binary"
	"errors"
	"time"

	"github.com/boltdb/bolt"
)

var (
	levelName = map[int][]byte{
		1: []byte("1"),
		2: []byte("2"),
		3: []byte("3"),
		4: []byte("4"),
		5: []byte("5"),
	}
)

// TODO: Interfacification of messages

// Message represents a message in the priority queue
type Message struct {
	key      int64
	value    []byte
	priority int
}

// NewMessage generates a new priority queue message with a priority range of
// 1-5
func NewMessage(priority int, value string) *Message {
	return &Message{getKey(), []byte(value), priority}
}

// ToString outputs the string representation of the message's value
func (m *Message) ToString() string {
	return string(m.value)
}

// PQueue is a priority queue backed by a Bolt database on disk
type PQueue struct {
	conn *bolt.DB
}

// NewPQueue loads or creates a new PQueue with the given filename
func NewPQueue(filename string) (*PQueue, error) {
	db, err := bolt.Open(filename, 0600, nil)
	if err != nil {
		return nil, err
	}

	// Ensure the priority buckets exist before using the queue
	err = db.Update(func(tx *bolt.Tx) error {
		for i := 1; i <= 5; i++ {
			_, err = tx.CreateBucketIfNotExists(levelName[int(i)])
			if err != nil {
				return err
			}
		}
		return nil
	})

	if err != nil {
		return nil, err
	}

	return &PQueue{db}, nil
}

// Enqueue adds a message to the queue at its appropriate priority level
func (b *PQueue) Enqueue(m *Message) error {
	if _, ok := levelName[m.priority]; !ok {
		return errors.New("invalid priority")
	}

	return b.conn.Update(func(tx *bolt.Tx) error {
		dbucket := tx.Bucket(levelName[m.priority])
		if dbucket == nil {
			return errors.New("data bucket does not exist")
		}

		// Push
		kb := make([]byte, 8)
		binary.BigEndian.PutUint64(kb, uint64(m.key))
		if err := dbucket.Put(kb, m.value); err != nil {
			return err
		}

		return nil
	})
}

// Dequeue removes the oldest, highest priority message from the queue and
// returns it
func (b *PQueue) Dequeue() (*Message, error) {
	var m *Message
	err := b.conn.Update(func(tx *bolt.Tx) error {

		for i := 1; i <= 5; i++ {
			dbucket := tx.Bucket(levelName[int(i)])
			if dbucket == nil {
				return errors.New("data bucket does not exist")
			}
			// Skip empty queues
			if dbucket.Stats().KeyN == 0 {
				continue
			}
			cur := dbucket.Cursor()
			k, v := cur.First()
			ki, _ := binary.Varint(k)

			m = &Message{ki, cloneBytes(v), i}
			if err := cur.Delete(); err != nil {
				return err
			}
			break
		}
		return nil
	})
	if err != nil {
		return nil, err
	}

	return m, nil
}

// Size returns the number of entries of a given priority from 1 to 5
func (b *PQueue) Size(priority int) (int, error) {
	if _, ok := levelName[priority]; !ok {
		return 0, errors.New("invalid priority")
	}

	var size int
	err := b.conn.View(func(tx *bolt.Tx) error {
		dbucket := tx.Bucket(levelName[priority])
		if dbucket == nil {
			return errors.New("data bucket does not exist")
		}
		size = dbucket.Stats().KeyN
		return nil
	})
	if err != nil {
		return 0, err
	}

	return size, nil
}

var lastTime int64

// inspired by https://gist.github.com/burke/5833358
func getKey() int64 {
	t := time.Now().UnixNano()
	if t <= lastTime {
		t = lastTime + 1
	}
	lastTime = t
	return t
}

// taken from boltDB. Avoids corruption when re-queueing
func cloneBytes(v []byte) []byte {
	var clone = make([]byte, len(v))
	copy(clone, v)
	return clone
}
<|endoftext|>


<commit_before>package com

import "sync"

// GetUserDefaultLocaleIDFunc implements Kernel32 GetUserDefaultLCID().
type GetUserDefaultLocaleIDFunc func() uint32

// defaultLocale stores default locale callback.
var defaultLocale locale

// locale holds a RW mutex that should prevent multiple threads or goroutines
// from clashing.
//
// It is possible that the design removes the need for this, as this should be
// set within the init function of the library package. However, should also be
// possible to override the library init and go back and forth between
// implementations.
type locale struct {
	mutex    sync.RWMutex
	callback GetUserDefaultLocaleIDFunc
}

// SetGetUserDefaultLocaleIDCallback mutates the callback for retrieving locale ID.
//
// This is to be set by the library that accesses the Windows API.
func SetGetUserDefaultLocaleIDCallback(c GetUserDefaultLocaleIDFunc) {
	defaultLocale.mutex.Lock()
	defer defaultLocale.mutex.Unlock()
	defaultLocale.callback = c
}

// UserDefaultLocaleIDCallback accesses the callback for retrieving locale ID.
func UserDefaultLocaleIDCallback() GetUserDefaultLocaleIDFunc {
	defaultLocale.mutex.RLock()
	defer defaultLocale.mutex.RUnlock()
	return defaultLocale.callback
}

// GetUserDefaultLocaleID accesses the callback to retrieve default user locale.
func GetUserDefaultLocaleID() uint32 {
	defaultLocale.mutex.RLock()
	defer defaultLocale.mutex.RUnlock()
	return defaultLocale.callback()
}
<commit_msg>No longer need this file or technique.<commit_after><|endoftext|>


<commit_before>/*
 * Presdo. An static markdown web server.
 *
 * Original basead on Lanyon, by Marcus Kazmierczak (http://github.com/mkaz/lanyon)
 *
 * Author: Gustavo Paes <gustavo.paes@gmail.com>
 * Source: http://github.com/gustavopaees/
 */

package presdo

import (
    "log"
    "os"
    "io/ioutil"
    "encoding/json"
    "time"
    "net/http"
    "html/template"
)

const VERSION = "0.0.1"

// File info struct with full relative path and os.Stat()
type FileInfo struct {
    FullPath string
    Stat     os.FileInfo
}

// Time, in days, for each content type expire. It's sent to cache-control http response.
type cacheTimes struct {
    Html       int
    Css        int
    Javascript int
    Image      int
    Index      int
}

// Page struct.
type Page struct {
  Title, Category, Layout, Url string
  Content                      template.HTML
  Date                         time.Time
  Updated                      time.Time
  Params                       map[string]string
}

// Basic website information
var websiteConfig struct {
    Domain      string
    PortNumber  int
    PublicDir   string
    TemplateDir string
    DateFormat  string
    Ext         string
    Encode      string
    NotFound    string
    CacheConfig cacheTimes
}

// Read config file and start server
func Run() {
    if code, err := readConfigFile("./Presdofile.json"); code != 200 {
        log.Fatalln("Error", code, err)
    }

    if code, err := setDefaultValues(); code != 200 {
        log.Fatalln("Error", code, err)
    }

    view.Init()
    server.Run()
}

// Read file config Presdofile.json
func readConfigFile(configFile string) (int, error) {
    if _, err := os.Stat(configFile); os.IsNotExist(err) {
        //log.Fatalln("Config file not found. Please, create 'Presdofile.json' and try again.")
        return 404, err
    }

    file, err := ioutil.ReadFile(configFile)

    if err != nil {
        //log.Fatalln("Error on reading config file: ", err)
        return 403, err
    }

    if err := json.Unmarshal(file, &websiteConfig); err != nil {
        //log.Fatalln("Error on parsing json. Please, review your 'Presdofile.json': ", err)
        return 500, err
    }

    return 200, nil
}

// set default values for each website config.
func setDefaultValues() (int, error) {
    if websiteConfig.Domain == "" {
        websiteConfig.Domain = "localhost"
    }

    if websiteConfig.PortNumber == 0 {
        websiteConfig.PortNumber = 8080
    }

    if websiteConfig.PublicDir == "" {
        websiteConfig.PublicDir = "public/"
    }

    if websiteConfig.TemplateDir == "" {
        websiteConfig.TemplateDir = "view/default/"
    }

    if websiteConfig.DateFormat == "" {
        websiteConfig.DateFormat = "2006-01-02 03:04pm"
    }

    if websiteConfig.Ext == "" {
        websiteConfig.Ext = ".htm"
    }

    if websiteConfig.Encode == "" {
        websiteConfig.Encode = "utf-8"
    }

    if websiteConfig.NotFound == "" {
        websiteConfig.NotFound = "404.html"
    }

    //if _, err := os.Stat(websiteConfig.PublicDir); os.IsNotExist(err) {
    //    //log.Fatalln(err)
    //    return 404, err
    //}

    //if _, err := os.Stat(websiteConfig.TemplateDir); os.IsNotExist(err) {
    //    //log.Fatalln(err)
    //    return 404, err
    //}

    return 200, nil
}

// Check if request is to a static file or not.
// If request is a directory, check if there is an index file
func getRequestURIStaticFile(requestURI string) (interface{}, error) {
    publicPathRequest := paths.Public(paths.Request(requestURI))
    fileStat, err := os.Stat(publicPathRequest)

    if err != nil {
        return nil, err
    }

    return FileInfo{
        FullPath: requestURI,
        Stat    : fileStat,
    }, nil
}

// Recreate cached file when markdown was changed
func recreateCachedFileIfChanged(requestFilePath string, markdownPath string, cachedPath string) {
    markdownStat, err1 := os.Stat(markdownPath)
    cachedStat, err2   := os.Stat(cachedPath)

    if err1 == nil && err2 == nil {
        if cachedStat.ModTime() != markdownStat.ModTime() {
            markdown.Parse(requestFilePath, markdownPath)
        }
    }
}

// Look for an markdown file
func responseMarkdownFile(w http.ResponseWriter, r *http.Request) error {
    requestFilePath := paths.Request(r.RequestURI)
    cachedPath      := paths.Cache(requestFilePath)
    markdownPath    := paths.Markdown(requestFilePath)

    // if markdown not exist
    if _, err := os.Stat(markdownPath); err != nil {
        return err
    }

    var cachedStat os.FileInfo
    var errStat error

    // if not exist cached file, parse markdown content and create HTML file synchronous
    if cachedStat, errStat = os.Stat(cachedPath); errStat != nil {
        markdown.Parse(requestFilePath, markdownPath)
        cachedStat, _ = os.Stat(cachedPath)
    }

    server.Content(w, r, cachedPath, cachedStat.ModTime())

    go recreateCachedFileIfChanged(requestFilePath, markdownPath, cachedPath)

    return nil
}

<commit_msg>Always render markdown file in development mode<commit_after>/*
 * Presdo. An static markdown web server.
 *
 * Original basead on Lanyon, by Marcus Kazmierczak (http://github.com/mkaz/lanyon)
 *
 * Author: Gustavo Paes <gustavo.paes@gmail.com>
 * Source: http://github.com/gustavopaees/
 */

package presdo

import (
    "log"
    "os"
    "io/ioutil"
    "encoding/json"
    "time"
    "net/http"
    "html/template"
)

const VERSION = "0.0.1"

// File info struct with full relative path and os.Stat()
type FileInfo struct {
    FullPath string
    Stat     os.FileInfo
}

// Time, in days, for each content type expire. It's sent to cache-control http response.
type cacheTimes struct {
    Html       int
    Css        int
    Javascript int
    Image      int
    Index      int
}

// Page struct.
type Page struct {
  Title, Category, Layout, Url string
  Content                      template.HTML
  Date                         time.Time
  Updated                      time.Time
  Params                       map[string]string
}

// Basic website information
var websiteConfig struct {
    Domain       string
    PortNumber   int
    PublicDir    string
    TemplateDir  string
    DateFormat   string
    Ext          string
    Encode       string
    NotFound     string
    IsProduction bool
}

// Read config file and start server
func Run() {
    if code, err := readConfigFile("./Presdofile.json"); code != 200 {
        log.Fatalln("Error", code, err)
    }

    if code, err := setDefaultValues(); code != 200 {
        log.Fatalln("Error", code, err)
    }

    view.Init()
    server.Run()
}

// Read file config Presdofile.json
func readConfigFile(configFile string) (int, error) {
    if _, err := os.Stat(configFile); os.IsNotExist(err) {
        //log.Fatalln("Config file not found. Please, create 'Presdofile.json' and try again.")
        return 404, err
    }

    file, err := ioutil.ReadFile(configFile)

    if err != nil {
        //log.Fatalln("Error on reading config file: ", err)
        return 403, err
    }

    if err := json.Unmarshal(file, &websiteConfig); err != nil {
        //log.Fatalln("Error on parsing json. Please, review your 'Presdofile.json': ", err)
        return 500, err
    }

    return 200, nil
}

// set default values for each website config.
func setDefaultValues() (int, error) {
    if websiteConfig.Domain == "" {
        websiteConfig.Domain = "localhost"
    }

    if websiteConfig.PortNumber == 0 {
        websiteConfig.PortNumber = 8080
    }

    if websiteConfig.PublicDir == "" {
        websiteConfig.PublicDir = "public/"
    }

    if websiteConfig.TemplateDir == "" {
        websiteConfig.TemplateDir = "view/default/"
    }

    if websiteConfig.DateFormat == "" {
        websiteConfig.DateFormat = "2006-01-02 03:04pm"
    }

    if websiteConfig.Ext == "" {
        websiteConfig.Ext = ".htm"
    }

    if websiteConfig.Encode == "" {
        websiteConfig.Encode = "utf-8"
    }

    if websiteConfig.NotFound == "" {
        websiteConfig.NotFound = "404.html"
    }

    //if _, err := os.Stat(websiteConfig.PublicDir); os.IsNotExist(err) {
    //    //log.Fatalln(err)
    //    return 404, err
    //}

    //if _, err := os.Stat(websiteConfig.TemplateDir); os.IsNotExist(err) {
    //    //log.Fatalln(err)
    //    return 404, err
    //}

    return 200, nil
}

// Check if request is to a static file or not.
// If request is a directory, check if there is an index file
func getRequestURIStaticFile(requestURI string) (interface{}, error) {
    publicPathRequest := paths.Public(paths.Request(requestURI))
    fileStat, err := os.Stat(publicPathRequest)

    if err != nil {
        return nil, err
    }

    return FileInfo{
        FullPath: requestURI,
        Stat    : fileStat,
    }, nil
}

// Recreate cached file when markdown was changed
func recreateCachedFileIfChanged(requestFilePath string, markdownPath string, cachedPath string) {
    markdownStat, err1 := os.Stat(markdownPath)
    cachedStat, err2   := os.Stat(cachedPath)

    if err1 == nil && err2 == nil {
        if cachedStat.ModTime() != markdownStat.ModTime() {
            markdown.Parse(requestFilePath, markdownPath)
        }
    }
}

// Look for an markdown file
func responseMarkdownFile(w http.ResponseWriter, r *http.Request) error {
    requestFilePath := paths.Request(r.RequestURI)
    cachedPath      := paths.Cache(requestFilePath)
    markdownPath    := paths.Markdown(requestFilePath)

    // if markdown not exist
    if _, err := os.Stat(markdownPath); err != nil {
        return err
    }

    cachedStat, errStat := os.Stat(cachedPath)

    // Development mode always parse markdown file.
    // if not exist cached file, parse markdown content and create HTML file synchronous.
    if websiteConfig.IsProduction == false || errStat != nil {
        markdown.Parse(requestFilePath, markdownPath)
        cachedStat, _ = os.Stat(cachedPath)
    }

    server.Content(w, r, cachedPath, cachedStat.ModTime())

    go recreateCachedFileIfChanged(requestFilePath, markdownPath, cachedPath)

    return nil
}

<|endoftext|>


<commit_before>package ircbot

import (
	"fmt"
	"regexp"
	"strings"
	"time"

	irc "github.com/fluffle/goirc/client"
	logging "github.com/op/go-logging"
	"github.com/pteichman/go.cobe"
)

type Options struct {
	Server   string
	Nick     string
	Channels []string
	Ignore   []string
}

var clog = logging.MustGetLogger("cobe.ircbot")

// Backoff policy, milliseconds per attempt. End up with 30s attempts.
var backoff = []int{0, 0, 10, 30, 100, 300, 1000, 3000, 10000, 30000}

func backoffDuration(i int) time.Duration {
	if i < len(backoff) {
		return time.Duration(backoff[i]) * time.Millisecond
	}

	return time.Duration(backoff[len(backoff)-1]) * time.Millisecond
}

func backoffConnect(conn *irc.Conn, o *Options) {
	for i := 0; true; i++ {
		wait := backoffDuration(i)
		time.Sleep(wait)

		err := conn.Connect(o.Server)
		if err == nil {
			// The connection was successful.
			break
		}

		clog.Warning("Connection to %s failed: %s [%dms]", o.Server, err,
			int64(wait/time.Millisecond))
	}
}

func RunForever(b *cobe.Brain, o *Options) {
	stop := make(chan bool)
	conn := irc.SimpleClient(o.Nick)
	conn.Me.Ident = o.Nick
	conn.Me.Name = o.Nick

	conn.AddHandler("connected", func(conn *irc.Conn, line *irc.Line) {
		clog.Notice("Connected to %s. Joining %s.", o.Server,
			strings.Join(o.Channels, ", "))
		for _, channel := range o.Channels {
			conn.Join(channel)
		}
	})

	conn.AddHandler("disconnected", func(conn *irc.Conn, line *irc.Line) {
		clog.Warning("Disconnected from %s.", o.Server)
		backoffConnect(conn, o)
	})

	// The space after comma/colon is needed so we won't treat
	// urls as messages spoken to http.
	userMsg := regexp.MustCompile(`^(\S+)[,:]\s(.*?)$`)

	conn.AddHandler("privmsg", func(conn *irc.Conn, line *irc.Line) {
		user := line.Nick
		if in(o.Ignore, user) {
			clog.Debug("Ignoring privmsg from %s", user)
			return
		}

		target := line.Args[0]
		if !in(o.Channels, target) {
			clog.Debug("Ignoring privmsg on %s", target)
			return
		}

		var to, msg string

		groups := userMsg.FindStringSubmatch(line.Args[1])
		if len(groups) > 0 {
			to = groups[1]
			msg = groups[2]
		} else {
			msg = line.Args[1]
		}

		msg = strings.TrimSpace(msg)

		clog.Debug("Learn: %s", msg)
		b.Learn(msg)

		if to == o.Nick {
			reply := b.Reply(msg)
			clog.Debug("Reply: %s", reply)
			conn.Privmsg(target, fmt.Sprintf("%s: %s", user, reply))
		}
	})

	backoffConnect(conn, o)
	<-stop
}

func in(haystack []string, needle string) bool {
	for _, h := range haystack {
		if h == needle {
			return true
		}
	}

	return false
}
<commit_msg>Reconnect on kick.<commit_after>package ircbot

import (
	"fmt"
	"regexp"
	"strings"
	"time"

	irc "github.com/fluffle/goirc/client"
	logging "github.com/op/go-logging"
	"github.com/pteichman/go.cobe"
)

type Options struct {
	Server   string
	Nick     string
	Channels []string
	Ignore   []string
}

var clog = logging.MustGetLogger("cobe.ircbot")

// Backoff policy, milliseconds per attempt. End up with 30s attempts.
var backoff = []int{0, 0, 10, 30, 100, 300, 1000, 3000, 10000, 30000}

func backoffDuration(i int) time.Duration {
	if i < len(backoff) {
		return time.Duration(backoff[i]) * time.Millisecond
	}

	return time.Duration(backoff[len(backoff)-1]) * time.Millisecond
}

func backoffConnect(conn *irc.Conn, o *Options) {
	for i := 0; true; i++ {
		wait := backoffDuration(i)
		time.Sleep(wait)

		err := conn.Connect(o.Server)
		if err == nil {
			// The connection was successful.
			break
		}

		clog.Warning("Connection to %s failed: %s [%dms]", o.Server, err,
			int64(wait/time.Millisecond))
	}
}

func RunForever(b *cobe.Brain, o *Options) {
	stop := make(chan bool)
	conn := irc.SimpleClient(o.Nick)
	conn.Me.Ident = o.Nick
	conn.Me.Name = o.Nick

	conn.AddHandler("connected", func(conn *irc.Conn, line *irc.Line) {
		clog.Notice("Connected to %s. Joining %s.", o.Server,
			strings.Join(o.Channels, ", "))
		for _, channel := range o.Channels {
			conn.Join(channel)
		}
	})

	conn.AddHandler("disconnected", func(conn *irc.Conn, line *irc.Line) {
		clog.Warning("Disconnected from %s.", o.Server)
		backoffConnect(conn, o)
	})

	conn.AddHandler("kick", func(conn *irc.Conn, line *irc.Line) {
		if line.Args[1] == o.Nick {
			var channel = line.Args[0]
			clog.Notice("Kicked from %s. Rejoining.", channel)
			conn.Join(channel)
		}
	})

	// The space after comma/colon is needed so we won't treat
	// urls as messages spoken to http.
	userMsg := regexp.MustCompile(`^(\S+)[,:]\s(.*?)$`)

	conn.AddHandler("privmsg", func(conn *irc.Conn, line *irc.Line) {
		user := line.Nick
		if in(o.Ignore, user) {
			clog.Debug("Ignoring privmsg from %s", user)
			return
		}

		target := line.Args[0]
		if !in(o.Channels, target) {
			clog.Debug("Ignoring privmsg on %s", target)
			return
		}

		var to, msg string

		groups := userMsg.FindStringSubmatch(line.Args[1])
		if len(groups) > 0 {
			to = groups[1]
			msg = groups[2]
		} else {
			msg = line.Args[1]
		}

		msg = strings.TrimSpace(msg)

		clog.Debug("Learn: %s", msg)
		b.Learn(msg)

		if to == o.Nick {
			reply := b.Reply(msg)
			clog.Debug("Reply: %s", reply)
			conn.Privmsg(target, fmt.Sprintf("%s: %s", user, reply))
		}
	})

	backoffConnect(conn, o)
	<-stop
}

func in(haystack []string, needle string) bool {
	for _, h := range haystack {
		if h == needle {
			return true
		}
	}

	return false
}
<|endoftext|>


<commit_before>package httpclient

import (
	"crypto/tls"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"net/url"
	"reflect"
	"strconv"
	"strings"
	"time"

	"github.com/gobs/pretty"
	"github.com/gobs/simplejson"
)

var (
	DefaultClient = &http.Client{} // we use our own default client, so we can change the TLS configuration

	NoRedirect       = errors.New("No redirect")
	TooManyRedirects = errors.New("stopped after 10 redirects")
)

//
// Allow connections via HTTPS even if something is wrong with the certificate
// (self-signed or expired)
//
func AllowInsecure(insecure bool) {
	if insecure {
		tr := &http.Transport{
			TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
		}

		DefaultClient.Transport = tr
	} else {
		DefaultClient.Transport = nil
	}
}

//
// Set connection timeout
//
func SetTimeout(t time.Duration) {
	DefaultClient.Timeout = t
}

type HttpResponse struct {
	http.Response
}

func (r *HttpResponse) ContentType() string {
	content_type := r.Header.Get("Content-Type")
	if len(content_type) == 0 {
		return content_type
	}

	return strings.TrimSpace(strings.Split(content_type, ";")[0])
}

//
// Check if the input value is a "primitive" that can be safely stringified
//
func canStringify(v reflect.Value) bool {
	switch v.Kind() {
	case reflect.Bool,
		reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
		reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64,
		reflect.Float32, reflect.Float64,
		reflect.String:
		return true
	default:
		return false
	}
}

//
// Given a base URL and a bag of parameteters returns the URL with the encoded parameters
//
func URLWithPathParams(base string, path string, params map[string]interface{}) (u *url.URL) {

	u, err := url.Parse(base)
	if err != nil {
		log.Fatal(err)
	}

	if len(path) > 0 {
		u, err = u.Parse(path)
		if err != nil {
			log.Fatal(err)
		}
	}

	q := u.Query()

	for k, v := range params {
		val := reflect.ValueOf(v)

		switch val.Kind() {
		case reflect.Slice:
			if val.IsNil() { // TODO: add an option to ignore empty values
				q.Set(k, "")
				continue
			}
			fallthrough

		case reflect.Array:
			for i := 0; i < val.Len(); i++ {
				av := val.Index(i)

				if canStringify(av) {
					q.Add(k, fmt.Sprintf("%v", av))
				}
			}

		default:
			if canStringify(val) {
				q.Set(k, fmt.Sprintf("%v", v))
			} else {
				log.Fatal("Invalid type ", val)
			}
		}
	}

	u.RawQuery = q.Encode()
	return u
}

func URLWithParams(base string, params map[string]interface{}) (u *url.URL) {
	return URLWithPathParams(base, "", params)
}

//
// http.Get with params
//
func Get(urlStr string, params map[string]interface{}) (*HttpResponse, error) {
	resp, err := DefaultClient.Get(URLWithParams(urlStr, params).String())
	if err == nil {
		return &HttpResponse{*resp}, nil
	} else {
		return nil, err
	}
}

//
// http.Post with params
//
func Post(urlStr string, params map[string]interface{}) (*HttpResponse, error) {
	resp, err := DefaultClient.PostForm(urlStr, URLWithParams(urlStr, params).Query())
	if err == nil {
		return &HttpResponse{*resp}, nil
	} else {
		return nil, err
	}
}

//
//  Read the body
//
func (resp *HttpResponse) Content() []byte {
	body, _ := ioutil.ReadAll(resp.Body)
	resp.Body.Close()
	return body
}

//
//  Try to parse the response body as JSON
//
func (resp *HttpResponse) Json() (json *simplejson.Json) {
	json, _ = simplejson.LoadBytes(resp.Content())
	return
}

////////////////////////////////////////////////////////////////////////

//
// http.Client with some defaults and stuff
//
type HttpClient struct {
	// the http.Client
	client *http.Client

	// the base URL for this client
	BaseURL *url.URL

	// the client UserAgent string
	UserAgent string

	// Common headers to be passed on each request
	Headers map[string]string

	// if Verbose, log request and response info
	Verbose bool
}

//
// Create a new HttpClient
//
func NewHttpClient(base string) (httpClient *HttpClient) {
	httpClient = new(HttpClient)
	httpClient.client = &http.Client{CheckRedirect: httpClient.checkRedirect}
	httpClient.Headers = make(map[string]string)

	if u, err := url.Parse(base); err != nil {
		log.Fatal(err)
	} else {
		httpClient.BaseURL = u
	}

	return
}

//
// Allow connections via HTTPS even if something is wrong with the certificate
// (self-signed or expired)
//
func (self *HttpClient) AllowInsecure(insecure bool) {
	if insecure {
		tr := &http.Transport{
			TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
		}

		self.client.Transport = tr
	} else {
		self.client.Transport = nil
	}
}

//
// Set connection timeout
//
func (self *HttpClient) SetTimeout(t time.Duration) {
	self.client.Timeout = t
}

//
// add default headers plus extra headers
//
func (self *HttpClient) addHeaders(req *http.Request, headers map[string]string) {

	if len(self.UserAgent) > 0 {
		req.Header.Set("User-Agent", self.UserAgent)
	}

	for k, v := range self.Headers {
		req.Header.Set(k, v)
	}

	for k, v := range headers {
		if strings.ToLower(k) == "content-length" {
			if len, err := strconv.Atoi(v); err == nil && req.ContentLength <= 0 {
				req.ContentLength = int64(len)
			}
		} else {
			req.Header.Set(k, v)
		}
	}

}

//
// the callback for CheckRedirect, used to pass along the headers in case of redirection
//
func (self *HttpClient) checkRedirect(req *http.Request, via []*http.Request) error {
	if req.Method == "HEAD" {
		// don't follow redirects on a HEAD request
		return NoRedirect
	}

	if self.Verbose {
		log.Println("REDIRECT:", len(via), req.URL)
	}

	if len(via) >= 10 {
		return TooManyRedirects
	}

	// TODO: check for same host before adding headers
	self.addHeaders(req, nil)
	return nil
}

//
// Create a request object given the method, path, body and extra headers
//
func (self *HttpClient) Request(method string, urlpath string, body io.Reader, headers map[string]string) (req *http.Request) {
	if u, err := self.BaseURL.Parse(urlpath); err != nil {
		log.Fatal(err)
	} else {
		urlpath = u.String()
	}

	req, err := http.NewRequest(method, urlpath, body)
	if err != nil {
		log.Fatal(err)
	}

	self.addHeaders(req, headers)
	return
}

//
// Execute request
//
func (self *HttpClient) Do(req *http.Request) (*HttpResponse, error) {
	if self.Verbose {
		log.Println("REQUEST:", req.Method, req.URL, pretty.PrettyFormat(req.Header))
	}

	resp, err := self.client.Do(req)
	if urlerr, ok := err.(*url.Error); ok && urlerr.Err == NoRedirect {
		err = nil // redirect on HEAD is not an error
	}
	if err == nil {
		if self.Verbose {
			log.Println("RESPONSE:", resp.Status, pretty.PrettyFormat(resp.Header))
		}

		return &HttpResponse{*resp}, nil
	} else {
		if self.Verbose {
			log.Println("ERROR:", err, "REQUEST:", req.Method, req.URL, pretty.PrettyFormat(req.Header))
		}

		return nil, err
	}
}

//
// Execute a DELETE request
//
func (self *HttpClient) Delete(path string, headers map[string]string) (*HttpResponse, error) {
	req := self.Request("DELETE", path, nil, headers)
	return self.Do(req)
}

//
// Execute a HEAD request
//
func (self *HttpClient) Head(path string, params map[string]interface{}, headers map[string]string) (*HttpResponse, error) {
	req := self.Request("HEAD", URLWithParams(path, params).String(), nil, headers)
	return self.Do(req)
}

//
// Execute a GET request
//
func (self *HttpClient) Get(path string, params map[string]interface{}, headers map[string]string) (*HttpResponse, error) {
	req := self.Request("GET", URLWithParams(path, params).String(), nil, headers)
	return self.Do(req)
}

//
// Execute a POST request
//
func (self *HttpClient) Post(path string, content io.Reader, headers map[string]string) (*HttpResponse, error) {
	req := self.Request("POST", path, content, headers)
	return self.Do(req)
}

//
// Execute a PUT request
//
func (self *HttpClient) Put(path string, content io.Reader, headers map[string]string) (*HttpResponse, error) {
	req := self.Request("PUT", path, content, headers)
	return self.Do(req)
}
<commit_msg>Added HttpResponse.Close() that makes sure r.Body is drained before closing it<commit_after>package httpclient

import (
	"crypto/tls"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"net/url"
	"reflect"
	"strconv"
	"strings"
	"time"

	"github.com/gobs/pretty"
	"github.com/gobs/simplejson"
)

var (
	DefaultClient = &http.Client{} // we use our own default client, so we can change the TLS configuration

	NoRedirect       = errors.New("No redirect")
	TooManyRedirects = errors.New("stopped after 10 redirects")
)

//
// Allow connections via HTTPS even if something is wrong with the certificate
// (self-signed or expired)
//
func AllowInsecure(insecure bool) {
	if insecure {
		tr := &http.Transport{
			TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
		}

		DefaultClient.Transport = tr
	} else {
		DefaultClient.Transport = nil
	}
}

//
// Set connection timeout
//
func SetTimeout(t time.Duration) {
	DefaultClient.Timeout = t
}

type HttpResponse struct {
	http.Response
}

func (r *HttpResponse) ContentType() string {
	content_type := r.Header.Get("Content-Type")
	if len(content_type) == 0 {
		return content_type
	}

	return strings.TrimSpace(strings.Split(content_type, ";")[0])
}

//
// Close makes sure that all data from the body is read
// before closing the reader.
//
// If that is not the desider behaviour, just call HttpResponse.Body.Close()
//
func (r *HttpResponse) Close() {
	if r != nil && r.Body != nil {
		io.Copy(ioutil.Discard, r.Body)
		r.Body.Close()
	}
}

//
// Check if the input value is a "primitive" that can be safely stringified
//
func canStringify(v reflect.Value) bool {
	switch v.Kind() {
	case reflect.Bool,
		reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
		reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64,
		reflect.Float32, reflect.Float64,
		reflect.String:
		return true
	default:
		return false
	}
}

//
// Given a base URL and a bag of parameteters returns the URL with the encoded parameters
//
func URLWithPathParams(base string, path string, params map[string]interface{}) (u *url.URL) {

	u, err := url.Parse(base)
	if err != nil {
		log.Fatal(err)
	}

	if len(path) > 0 {
		u, err = u.Parse(path)
		if err != nil {
			log.Fatal(err)
		}
	}

	q := u.Query()

	for k, v := range params {
		val := reflect.ValueOf(v)

		switch val.Kind() {
		case reflect.Slice:
			if val.IsNil() { // TODO: add an option to ignore empty values
				q.Set(k, "")
				continue
			}
			fallthrough

		case reflect.Array:
			for i := 0; i < val.Len(); i++ {
				av := val.Index(i)

				if canStringify(av) {
					q.Add(k, fmt.Sprintf("%v", av))
				}
			}

		default:
			if canStringify(val) {
				q.Set(k, fmt.Sprintf("%v", v))
			} else {
				log.Fatal("Invalid type ", val)
			}
		}
	}

	u.RawQuery = q.Encode()
	return u
}

func URLWithParams(base string, params map[string]interface{}) (u *url.URL) {
	return URLWithPathParams(base, "", params)
}

//
// http.Get with params
//
func Get(urlStr string, params map[string]interface{}) (*HttpResponse, error) {
	resp, err := DefaultClient.Get(URLWithParams(urlStr, params).String())
	if err == nil {
		return &HttpResponse{*resp}, nil
	} else {
		return nil, err
	}
}

//
// http.Post with params
//
func Post(urlStr string, params map[string]interface{}) (*HttpResponse, error) {
	resp, err := DefaultClient.PostForm(urlStr, URLWithParams(urlStr, params).Query())
	if err == nil {
		return &HttpResponse{*resp}, nil
	} else {
		return nil, err
	}
}

//
//  Read the body
//
func (resp *HttpResponse) Content() []byte {
	body, _ := ioutil.ReadAll(resp.Body)
	resp.Body.Close()
	return body
}

//
//  Try to parse the response body as JSON
//
func (resp *HttpResponse) Json() (json *simplejson.Json) {
	json, _ = simplejson.LoadBytes(resp.Content())
	return
}

////////////////////////////////////////////////////////////////////////

//
// http.Client with some defaults and stuff
//
type HttpClient struct {
	// the http.Client
	client *http.Client

	// the base URL for this client
	BaseURL *url.URL

	// the client UserAgent string
	UserAgent string

	// Common headers to be passed on each request
	Headers map[string]string

	// if Verbose, log request and response info
	Verbose bool
}

//
// Create a new HttpClient
//
func NewHttpClient(base string) (httpClient *HttpClient) {
	httpClient = new(HttpClient)
	httpClient.client = &http.Client{CheckRedirect: httpClient.checkRedirect}
	httpClient.Headers = make(map[string]string)

	if u, err := url.Parse(base); err != nil {
		log.Fatal(err)
	} else {
		httpClient.BaseURL = u
	}

	return
}

//
// Allow connections via HTTPS even if something is wrong with the certificate
// (self-signed or expired)
//
func (self *HttpClient) AllowInsecure(insecure bool) {
	if insecure {
		tr := &http.Transport{
			TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
		}

		self.client.Transport = tr
	} else {
		self.client.Transport = nil
	}
}

//
// Set connection timeout
//
func (self *HttpClient) SetTimeout(t time.Duration) {
	self.client.Timeout = t
}

//
// add default headers plus extra headers
//
func (self *HttpClient) addHeaders(req *http.Request, headers map[string]string) {

	if len(self.UserAgent) > 0 {
		req.Header.Set("User-Agent", self.UserAgent)
	}

	for k, v := range self.Headers {
		req.Header.Set(k, v)
	}

	for k, v := range headers {
		if strings.ToLower(k) == "content-length" {
			if len, err := strconv.Atoi(v); err == nil && req.ContentLength <= 0 {
				req.ContentLength = int64(len)
			}
		} else {
			req.Header.Set(k, v)
		}
	}

}

//
// the callback for CheckRedirect, used to pass along the headers in case of redirection
//
func (self *HttpClient) checkRedirect(req *http.Request, via []*http.Request) error {
	if req.Method == "HEAD" {
		// don't follow redirects on a HEAD request
		return NoRedirect
	}

	if self.Verbose {
		log.Println("REDIRECT:", len(via), req.URL)
	}

	if len(via) >= 10 {
		return TooManyRedirects
	}

	// TODO: check for same host before adding headers
	self.addHeaders(req, nil)
	return nil
}

//
// Create a request object given the method, path, body and extra headers
//
func (self *HttpClient) Request(method string, urlpath string, body io.Reader, headers map[string]string) (req *http.Request) {
	if u, err := self.BaseURL.Parse(urlpath); err != nil {
		log.Fatal(err)
	} else {
		urlpath = u.String()
	}

	req, err := http.NewRequest(method, urlpath, body)
	if err != nil {
		log.Fatal(err)
	}

	self.addHeaders(req, headers)
	return
}

//
// Execute request
//
func (self *HttpClient) Do(req *http.Request) (*HttpResponse, error) {
	if self.Verbose {
		log.Println("REQUEST:", req.Method, req.URL, pretty.PrettyFormat(req.Header))
	}

	resp, err := self.client.Do(req)
	if urlerr, ok := err.(*url.Error); ok && urlerr.Err == NoRedirect {
		err = nil // redirect on HEAD is not an error
	}
	if err == nil {
		if self.Verbose {
			log.Println("RESPONSE:", resp.Status, pretty.PrettyFormat(resp.Header))
		}

		return &HttpResponse{*resp}, nil
	} else {
		if self.Verbose {
			log.Println("ERROR:", err, "REQUEST:", req.Method, req.URL, pretty.PrettyFormat(req.Header))
		}

		return nil, err
	}
}

//
// Execute a DELETE request
//
func (self *HttpClient) Delete(path string, headers map[string]string) (*HttpResponse, error) {
	req := self.Request("DELETE", path, nil, headers)
	return self.Do(req)
}

//
// Execute a HEAD request
//
func (self *HttpClient) Head(path string, params map[string]interface{}, headers map[string]string) (*HttpResponse, error) {
	req := self.Request("HEAD", URLWithParams(path, params).String(), nil, headers)
	return self.Do(req)
}

//
// Execute a GET request
//
func (self *HttpClient) Get(path string, params map[string]interface{}, headers map[string]string) (*HttpResponse, error) {
	req := self.Request("GET", URLWithParams(path, params).String(), nil, headers)
	return self.Do(req)
}

//
// Execute a POST request
//
func (self *HttpClient) Post(path string, content io.Reader, headers map[string]string) (*HttpResponse, error) {
	req := self.Request("POST", path, content, headers)
	return self.Do(req)
}

//
// Execute a PUT request
//
func (self *HttpClient) Put(path string, content io.Reader, headers map[string]string) (*HttpResponse, error) {
	req := self.Request("PUT", path, content, headers)
	return self.Do(req)
}
<|endoftext|>


<commit_before>package ndp

import (
    "fmt"
    "net"
    "encoding/binary"
)

type ICMPOptionType int

const (
    ICMPOptionTypeSourceLinkLayerAddress ICMPOptionType = 1
    ICMPOptionTypeTargetLinkLayerAddress ICMPOptionType = 2
    ICMPOptionTypePrefixInformation      ICMPOptionType = 3
    ICMPOptionTypeRedirectedHeader       ICMPOptionType = 4
    ICMPOptionTypeMTU                    ICMPOptionType = 5
    ICMPOptionTypeRecursiveDNSServer     ICMPOptionType = 25
    ICMPOptionTypeDNSSearchList          ICMPOptionType = 31
)

var icmpOptionTypes = map[ICMPOptionType]string {
    // https://tools.ietf.org/html/rfc4861#section-4.6
    1:  "Source Link Layer Address",
    2:  "Target Link Layer Address",
    3:  "Prefix Information",
    4:  "Redirected Header",
    5:  "MTU",
    // https://tools.ietf.org/html/rfc6106#section-5
    25: "Recursive DNS Server",
    31: "DNS Search List",
}

// https://tools.ietf.org/html/rfc4861#section-4.6
type ICMPOption interface {
    Len()        uint8
    Marshal()    ([]byte, error)
}

// https://tools.ietf.org/html/rfc4861#section-4.6.1
type ICMPOptionSourceLinkLayerAddress struct {
    Type             ICMPOptionType
    Length           uint8
    LinkLayerAddress net.HardwareAddr
}

// Len implements the Len method of ICMPOption interface.
func (o *ICMPOptionSourceLinkLayerAddress) Len() uint8 {
    if o == nil {
        return 0
    }

    return 1
}

type ICMPOptionTargetLinkLayerAddress struct {
    Type             ICMPOptionType
    Length           uint8
    LinkLayerAddress net.HardwareAddr
}

// Len implements the Len method of ICMPOption interface.
func (o *ICMPOptionTargetLinkLayerAddress) Len() uint8 {
    if o == nil {
        return 0
    }

    return 1
}

// https://tools.ietf.org/html/rfc4861#section-4.6.2
type ICMPOptionPrefixInformation struct {
    Type              ICMPOptionType
    Length            uint8
    PrefixLength      uint8
    OnLink            bool
    Auto              bool
    ValidLifetime     uint32
    PreferredLifetime uint32
    Prefix            net.IP
}

// Len implements the Len method of ICMPOption interface.
func (o *ICMPOptionPrefixInformation) Len() uint8 {
    if o == nil {
        return 0
    }

    return 4
}

// https://tools.ietf.org/html/rfc4861#section-4.6.4
type ICMPOptionMTU struct {
    Type   ICMPOptionType
    Length uint8
    MTU    uint32
}

// Len implements the Len method of ICMPOption interface.
func (o *ICMPOptionMTU) Len() uint8 {
    if o == nil {
        return 0
    }

    return 1
}

// https://tools.ietf.org/html/rfc6106#section-5.1
type ICMPOptionRecursiveDNSServer struct {
    Type     ICMPOptionType
    Length   uint8
    Lifetime uint32
    Servers  []net.IP
}

// Len implements the Len method of ICMPOption interface.
func (o *ICMPOptionRecursiveDNSServer) Len() uint8 {
    if o == nil {
        return 0
    }

    return 2 + uint8(len(o.Servers))
}

// https://tools.ietf.org/html/rfc6106#section-5.2
type ICMPOptionDNSSearchList struct {
    Type        ICMPOptionType
    Length      uint8
    Lifetime    uint32
    DomainNames []string
}

// Len implements the Len method of ICMPOption interface.
func (o *ICMPOptionDNSSearchList) Len() uint8 {
    if o == nil {
        return 0
    }

    return 2 + uint8(len(o.DomainNames))
}

func parseOptions(b []byte) ([]ICMPOption, error) {
    // empty container
    var icmpOptions = []ICMPOption {}

    for {
        // left over bytes are less than minimum option length
        if len(b) < 8 {
            break
        }

        optionType := ICMPOptionType(b[0])
        optionLength := uint8(b[1])
        var currentOption ICMPOption

        switch optionType {
            case ICMPOptionTypeSourceLinkLayerAddress:
                if optionLength != 1 {
                    fmt.Printf("incorrect length of %d\n", optionLength)
                    goto cleanup
                }

                currentOption := &ICMPOptionSourceLinkLayerAddress{
                    Type:             optionType,
                    Length:           optionLength,
                    LinkLayerAddress: b[2:8],
                }

                fmt.Printf("source address: %s\n", currentOption.LinkLayerAddress.String())

            case ICMPOptionTypeTargetLinkLayerAddress:
                if optionLength != 1 {
                    fmt.Printf("incorrect length of %d\n", optionLength)
                    goto cleanup
                }

                currentOption := &ICMPOptionTargetLinkLayerAddress{
                    Type:             optionType,
                    Length:           optionLength,
                    LinkLayerAddress: b[2:8],
                }

                fmt.Printf("target address: %s\n", currentOption.LinkLayerAddress.String())

            case ICMPOptionTypePrefixInformation:
                if optionLength != 4 {
                    fmt.Printf("incorrect length of %d\n", optionLength)
                    goto cleanup
                }

                currentOption := &ICMPOptionPrefixInformation{
                    Type:              optionType,
                    Length:            optionLength,
                    PrefixLength:      uint8(b[2]),
                    OnLink:            (b[3] & 0x80 > 0),
                    Auto:              (b[3] & 0x40 > 0),
                    ValidLifetime:     binary.BigEndian.Uint32(b[4:8]),
                    PreferredLifetime: binary.BigEndian.Uint32(b[8:12]),
                    Prefix:            net.IP(b[16:32]),
                }

                fmt.Printf("prefix: %s/%d, onlink: %t, auto: %t, valid: %d, preferred: %d\n", currentOption.Prefix.String(), currentOption.PrefixLength, currentOption.OnLink, currentOption.Auto, currentOption.ValidLifetime, currentOption.PreferredLifetime)

            case ICMPOptionTypeMTU:
                if optionLength != 1 {
                    fmt.Printf("incorrect length of %d for option %d\n", optionLength, optionType)
                    goto cleanup
                }

                currentOption := &ICMPOptionMTU{
                    Type:   optionType,
                    Length: optionLength,
                    MTU:    binary.BigEndian.Uint32(b[4:8]),
                }

                fmt.Printf("MTU: %d\n", currentOption.MTU)

            case ICMPOptionTypeRecursiveDNSServer:
                if optionLength < 3 {
                    fmt.Printf("incorrect length of %d for option 25\n", optionLength)
                    goto cleanup
                }

                currentOption := &ICMPOptionRecursiveDNSServer{
                    Type:       optionType,
                    Length:     optionLength,
                    Lifetime:   binary.BigEndian.Uint32(b[4:8]),
                }

                for i := 8; i < (int(optionLength) * 8); i += 16 {
                    currentOption.Servers = append(currentOption.Servers, net.IP(b[i:(i+16)]))
                }

                fmt.Printf("lifetime: %d, servers: %s\n", currentOption.Lifetime, currentOption.Servers)

            case ICMPOptionTypeDNSSearchList:
                if optionLength < 4 {
                    fmt.Printf("incorrect length of %d for option %d\n", optionLength, optionType)
                    goto cleanup
                }

                currentOption := &ICMPOptionDNSSearchList{
                    Type:     optionType,
                    Length:   optionLength,
                    Lifetime: binary.BigEndian.Uint32(b[4:8]),
                }

                for i := 8; i <(int(optionLength) * 8); i += 24 {
                    currentOption.DomainNames = append(currentOption.DomainNames, absDomainName(b[i:(i+24)]))
                }

                fmt.Printf("lifetime: %d, domains: %s\n", currentOption.Lifetime, currentOption.DomainNames)

            default:
                fmt.Printf("unhandled icmp option: %s (%d) (len: %d)\n", icmpOptionTypes[optionType], optionType, optionLength)
                goto cleanup
        }

        // add new option to array of options
        icmpOptions = append(icmpOptions, currentOption)

cleanup:
        // chop off bytes for this option
        b = b[(optionLength * 8):]
    }

    return icmpOptions, nil
}

// inspired by golang.org/net/dnsclient.go's absDomainName
func absDomainName(b []byte) string {
    name := ""
    start := 0
    for {
        length := int(b[start])
        if length > 0 {
            name += string(b[start:(start+length+1)]) + "."
        }

        start += (length + 1)
        if start >= len(b) {
            break
        }
    }

    return name
}
<commit_msg>fixed length calculation<commit_after>package ndp

import (
    "fmt"
    "net"
    "encoding/binary"
)

type ICMPOptionType int

const (
    ICMPOptionTypeSourceLinkLayerAddress ICMPOptionType = 1
    ICMPOptionTypeTargetLinkLayerAddress ICMPOptionType = 2
    ICMPOptionTypePrefixInformation      ICMPOptionType = 3
    ICMPOptionTypeRedirectedHeader       ICMPOptionType = 4
    ICMPOptionTypeMTU                    ICMPOptionType = 5
    ICMPOptionTypeRecursiveDNSServer     ICMPOptionType = 25
    ICMPOptionTypeDNSSearchList          ICMPOptionType = 31
)

var icmpOptionTypes = map[ICMPOptionType]string {
    // https://tools.ietf.org/html/rfc4861#section-4.6
    1:  "Source Link Layer Address",
    2:  "Target Link Layer Address",
    3:  "Prefix Information",
    4:  "Redirected Header",
    5:  "MTU",
    // https://tools.ietf.org/html/rfc6106#section-5
    25: "Recursive DNS Server",
    31: "DNS Search List",
}

// https://tools.ietf.org/html/rfc4861#section-4.6
type ICMPOption interface {
    Len()        uint8
    Marshal()    ([]byte, error)
}

// https://tools.ietf.org/html/rfc4861#section-4.6.1
type ICMPOptionSourceLinkLayerAddress struct {
    Type             ICMPOptionType
    Length           uint8
    LinkLayerAddress net.HardwareAddr
}

// Len implements the Len method of ICMPOption interface.
func (o *ICMPOptionSourceLinkLayerAddress) Len() uint8 {
    if o == nil {
        return 0
    }

    return 1
}

type ICMPOptionTargetLinkLayerAddress struct {
    Type             ICMPOptionType
    Length           uint8
    LinkLayerAddress net.HardwareAddr
}

// Len implements the Len method of ICMPOption interface.
func (o *ICMPOptionTargetLinkLayerAddress) Len() uint8 {
    if o == nil {
        return 0
    }

    return 1
}

// https://tools.ietf.org/html/rfc4861#section-4.6.2
type ICMPOptionPrefixInformation struct {
    Type              ICMPOptionType
    Length            uint8
    PrefixLength      uint8
    OnLink            bool
    Auto              bool
    ValidLifetime     uint32
    PreferredLifetime uint32
    Prefix            net.IP
}

// Len implements the Len method of ICMPOption interface.
func (o *ICMPOptionPrefixInformation) Len() uint8 {
    if o == nil {
        return 0
    }

    return 4
}

// https://tools.ietf.org/html/rfc4861#section-4.6.4
type ICMPOptionMTU struct {
    Type   ICMPOptionType
    Length uint8
    MTU    uint32
}

// Len implements the Len method of ICMPOption interface.
func (o *ICMPOptionMTU) Len() uint8 {
    if o == nil {
        return 0
    }

    return 1
}

// https://tools.ietf.org/html/rfc6106#section-5.1
type ICMPOptionRecursiveDNSServer struct {
    Type     ICMPOptionType
    Length   uint8
    Lifetime uint32
    Servers  []net.IP
}

// Len implements the Len method of ICMPOption interface.
func (o *ICMPOptionRecursiveDNSServer) Len() uint8 {
    if o == nil {
        return 0
    }

    return 1 + uint8(len(o.Servers) * 2)
}

// https://tools.ietf.org/html/rfc6106#section-5.2
type ICMPOptionDNSSearchList struct {
    Type        ICMPOptionType
    Length      uint8
    Lifetime    uint32
    DomainNames []string
}

// Len implements the Len method of ICMPOption interface.
func (o *ICMPOptionDNSSearchList) Len() uint8 {
    if o == nil {
        return 0
    }

    return 1 + uint8(len(o.DomainNames) * 3)
}

func parseOptions(b []byte) ([]ICMPOption, error) {
    // empty container
    var icmpOptions = []ICMPOption {}

    for {
        // left over bytes are less than minimum option length
        if len(b) < 8 {
            break
        }

        optionType := ICMPOptionType(b[0])
        optionLength := uint8(b[1])
        var currentOption ICMPOption

        switch optionType {
            case ICMPOptionTypeSourceLinkLayerAddress:
                if optionLength != 1 {
                    fmt.Printf("incorrect length of %d\n", optionLength)
                    goto cleanup
                }

                currentOption := &ICMPOptionSourceLinkLayerAddress{
                    Type:             optionType,
                    Length:           optionLength,
                    LinkLayerAddress: b[2:8],
                }

                fmt.Printf("source address: %s\n", currentOption.LinkLayerAddress.String())

            case ICMPOptionTypeTargetLinkLayerAddress:
                if optionLength != 1 {
                    fmt.Printf("incorrect length of %d\n", optionLength)
                    goto cleanup
                }

                currentOption := &ICMPOptionTargetLinkLayerAddress{
                    Type:             optionType,
                    Length:           optionLength,
                    LinkLayerAddress: b[2:8],
                }

                fmt.Printf("target address: %s\n", currentOption.LinkLayerAddress.String())

            case ICMPOptionTypePrefixInformation:
                if optionLength != 4 {
                    fmt.Printf("incorrect length of %d\n", optionLength)
                    goto cleanup
                }

                currentOption := &ICMPOptionPrefixInformation{
                    Type:              optionType,
                    Length:            optionLength,
                    PrefixLength:      uint8(b[2]),
                    OnLink:            (b[3] & 0x80 > 0),
                    Auto:              (b[3] & 0x40 > 0),
                    ValidLifetime:     binary.BigEndian.Uint32(b[4:8]),
                    PreferredLifetime: binary.BigEndian.Uint32(b[8:12]),
                    Prefix:            net.IP(b[16:32]),
                }

                fmt.Printf("prefix: %s/%d, onlink: %t, auto: %t, valid: %d, preferred: %d\n", currentOption.Prefix.String(), currentOption.PrefixLength, currentOption.OnLink, currentOption.Auto, currentOption.ValidLifetime, currentOption.PreferredLifetime)

            case ICMPOptionTypeMTU:
                if optionLength != 1 {
                    fmt.Printf("incorrect length of %d for option %d\n", optionLength, optionType)
                    goto cleanup
                }

                currentOption := &ICMPOptionMTU{
                    Type:   optionType,
                    Length: optionLength,
                    MTU:    binary.BigEndian.Uint32(b[4:8]),
                }

                fmt.Printf("MTU: %d\n", currentOption.MTU)

            case ICMPOptionTypeRecursiveDNSServer:
                if optionLength < 3 {
                    fmt.Printf("incorrect length of %d for option 25\n", optionLength)
                    goto cleanup
                }

                currentOption := &ICMPOptionRecursiveDNSServer{
                    Type:       optionType,
                    Length:     optionLength,
                    Lifetime:   binary.BigEndian.Uint32(b[4:8]),
                }

                for i := 8; i < (int(optionLength) * 8); i += 16 {
                    currentOption.Servers = append(currentOption.Servers, net.IP(b[i:(i+16)]))
                }

                fmt.Printf("lifetime: %d, servers: %s\n", currentOption.Lifetime, currentOption.Servers)

            case ICMPOptionTypeDNSSearchList:
                if optionLength < 4 {
                    fmt.Printf("incorrect length of %d for option %d\n", optionLength, optionType)
                    goto cleanup
                }

                currentOption := &ICMPOptionDNSSearchList{
                    Type:     optionType,
                    Length:   optionLength,
                    Lifetime: binary.BigEndian.Uint32(b[4:8]),
                }

                for i := 8; i <(int(optionLength) * 8); i += 24 {
                    currentOption.DomainNames = append(currentOption.DomainNames, absDomainName(b[i:(i+24)]))
                }

                fmt.Printf("lifetime: %d, domains: %s\n", currentOption.Lifetime, currentOption.DomainNames)

            default:
                fmt.Printf("unhandled icmp option: %s (%d) (len: %d)\n", icmpOptionTypes[optionType], optionType, optionLength)
                goto cleanup
        }

        // add new option to array of options
        icmpOptions = append(icmpOptions, currentOption)

cleanup:
        // chop off bytes for this option
        b = b[(optionLength * 8):]
    }

    return icmpOptions, nil
}

// inspired by golang.org/net/dnsclient.go's absDomainName
func absDomainName(b []byte) string {
    name := ""
    start := 0
    for {
        length := int(b[start])
        if length > 0 {
            name += string(b[start:(start+length+1)]) + "."
        }

        start += (length + 1)
        if start >= len(b) {
            break
        }
    }

    return name
}
<|endoftext|>


<commit_before>// Copyright 2013 tsuru 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 docker

import (
	"bytes"
	"github.com/dotcloud/docker"
	dcli "github.com/fsouza/go-dockerclient"
	"github.com/fsouza/go-dockerclient/testing"
	"github.com/globocom/config"
	"github.com/globocom/docker-cluster/cluster"
	"github.com/globocom/tsuru/app"
	"github.com/globocom/tsuru/db"
	"labix.org/v2/mgo/bson"
	"launchpad.net/gocheck"
)

type SchedulerSuite struct {
	storage *db.Storage
}

var _ = gocheck.Suite(&SchedulerSuite{})

func (s *SchedulerSuite) SetUpSuite(c *gocheck.C) {
	var err error
	config.Set("database:url", "127.0.0.1:27017")
	config.Set("database:name", "docker_scheduler_tests")
	config.Set("docker:repository-namespace", "tsuru")
	s.storage, err = db.Conn()
	c.Assert(err, gocheck.IsNil)
}

func (s *SchedulerSuite) TearDownSuite(c *gocheck.C) {
	s.storage.Apps().Database.DropDatabase()
	s.storage.Close()
}

func (s *SchedulerSuite) TestSchedulerSchedule(c *gocheck.C) {
	server0, err := testing.NewServer(nil)
	c.Assert(err, gocheck.IsNil)
	defer server0.Stop()
	server1, err := testing.NewServer(nil)
	c.Assert(err, gocheck.IsNil)
	defer server1.Stop()
	server2, err := testing.NewServer(nil)
	c.Assert(err, gocheck.IsNil)
	defer server2.Stop()
	var buf bytes.Buffer
	client, _ := dcli.NewClient(server0.URL())
	client.PullImage(dcli.PullImageOptions{Repository: "tsuru/python"}, &buf)
	client.PullImage(dcli.PullImageOptions{Repository: "tsuru/impius"}, &buf)
	client.PullImage(dcli.PullImageOptions{Repository: "tsuru/mirror"}, &buf)
	client.PullImage(dcli.PullImageOptions{Repository: "tsuru/dedication"}, &buf)
	client, _ = dcli.NewClient(server1.URL())
	client.PullImage(dcli.PullImageOptions{Repository: "tsuru/python"}, &buf)
	client.PullImage(dcli.PullImageOptions{Repository: "tsuru/impius"}, &buf)
	client.PullImage(dcli.PullImageOptions{Repository: "tsuru/mirror"}, &buf)
	client.PullImage(dcli.PullImageOptions{Repository: "tsuru/dedication"}, &buf)
	client, _ = dcli.NewClient(server2.URL())
	client.PullImage(dcli.PullImageOptions{Repository: "tsuru/python"}, &buf)
	client.PullImage(dcli.PullImageOptions{Repository: "tsuru/impius"}, &buf)
	client.PullImage(dcli.PullImageOptions{Repository: "tsuru/mirror"}, &buf)
	client.PullImage(dcli.PullImageOptions{Repository: "tsuru/dedication"}, &buf)
	a1 := app.App{Name: "impius", Teams: []string{"tsuruteam", "nodockerforme"}}
	a2 := app.App{Name: "mirror", Teams: []string{"tsuruteam"}}
	a3 := app.App{Name: "dedication", Teams: []string{"nodockerforme"}}
	err = s.storage.Apps().Insert(a1, a2, a3)
	c.Assert(err, gocheck.IsNil)
	defer s.storage.Apps().Remove(bson.M{"name": bson.M{"$in": []string{a1.Name, a2.Name, a3.Name}}})
	coll := s.storage.Collection(schedulerCollection)
	err = coll.Insert(
		node{ID: "server0", Address: server0.URL(), Team: "tsuruteam"},
		node{ID: "server1", Address: server1.URL(), Team: "tsuruteam"},
		node{ID: "server2", Address: server2.URL()},
	)
	c.Assert(err, gocheck.IsNil)
	defer coll.Remove(bson.M{"_id": bson.M{"$in": []string{"server0", "server1", "server2"}}})
	var scheduler segregatedScheduler
	config := docker.Config{Cmd: []string{"/usr/sbin/sshd", "-D"}, Image: "tsuru/impius"}
	node, _, err := scheduler.Schedule(&config)
	c.Assert(err, gocheck.IsNil)
	c.Check(node, gocheck.Equals, "server2")
	config = docker.Config{Cmd: []string{"/usr/sbin/sshd", "-D"}, Image: "tsuru/mirror"}
	node, _, err = scheduler.Schedule(&config)
	c.Assert(err, gocheck.IsNil)
	c.Check(node == "server0" || node == "server1", gocheck.Equals, true)
	config = docker.Config{Cmd: []string{"/usr/sbin/sshd", "-D"}, Image: "tsuru/python"}
	node, _, err = scheduler.Schedule(&config)
	c.Assert(err, gocheck.IsNil)
	c.Check(node, gocheck.Equals, "server2")
	config = docker.Config{Cmd: []string{"/usr/sbin/sshd", "-D"}, Image: "tsuru/dedication"}
	node, _, err = scheduler.Schedule(&config)
	c.Assert(err, gocheck.IsNil)
	c.Check(node, gocheck.Equals, "server2")
	config = docker.Config{Cmd: []string{"/usr/sbin/sshd", "-D"}}
	node, _, _ = scheduler.Schedule(&config)
	c.Check(node, gocheck.Equals, "server2")
}

func (s *SchedulerSuite) TestSchedulerNoFallback(c *gocheck.C) {
	app := app.App{Name: "bill", Teams: []string{"jean"}}
	err := s.storage.Apps().Insert(app)
	c.Assert(err, gocheck.IsNil)
	defer s.storage.Apps().Remove(bson.M{"name": app.Name})
	config := docker.Config{Cmd: []string{"/usr/sbin/sshd", "-D"}, Image: "tsuru/python"}
	var scheduler segregatedScheduler
	node, container, err := scheduler.Schedule(&config)
	c.Assert(node, gocheck.Equals, "")
	c.Assert(container, gocheck.IsNil)
	c.Assert(err, gocheck.Equals, errNoFallback)
}

func (s *SchedulerSuite) TestSchedulerNoNamespace(c *gocheck.C) {
	old, _ := config.Get("docker:repository-namespace")
	defer config.Set("docker:repository-namespace", old)
	config.Unset("docker:repository-name")
	config := docker.Config{Cmd: []string{"/usr/sbin/sshd", "-D"}, Image: "tsuru/python"}
	var scheduler segregatedScheduler
	node, container, err := scheduler.Schedule(&config)
	c.Assert(node, gocheck.Equals, "")
	c.Assert(container, gocheck.IsNil)
	c.Assert(err, gocheck.NotNil)
}

func (s *SchedulerSuite) TestSchedulerInvalidEndpoint(c *gocheck.C) {
	app := app.App{Name: "bill", Teams: []string{"jean"}}
	err := s.storage.Apps().Insert(app)
	c.Assert(err, gocheck.IsNil)
	defer s.storage.Apps().Remove(bson.M{"name": app.Name})
	coll := s.storage.Collection(schedulerCollection)
	err = coll.Insert(node{ID: "server0", Address: "", Team: "jean"})
	c.Assert(err, gocheck.IsNil)
	defer coll.Remove(bson.M{"_id": "server0"})
	config := docker.Config{Cmd: []string{"/usr/sbin/sshd", "-D"}, Image: "tsuru/python"}
	var scheduler segregatedScheduler
	node, container, err := scheduler.Schedule(&config)
	c.Assert(node, gocheck.Equals, "")
	c.Assert(container, gocheck.IsNil)
	c.Assert(err, gocheck.NotNil)
}

func (s *SchedulerSuite) TestSchedulerNodes(c *gocheck.C) {
	coll := s.storage.Collection(schedulerCollection)
	err := coll.Insert(
		node{ID: "server0", Address: "http://localhost:8080", Team: "tsuru"},
		node{ID: "server1", Address: "http://localhost:8081", Team: "tsuru"},
		node{ID: "server2", Address: "http://localhost:8082", Team: "tsuru"},
	)
	c.Assert(err, gocheck.IsNil)
	defer coll.RemoveAll(bson.M{"_id": bson.M{"$in": []string{"server0", "server1", "server2"}}})
	expected := []cluster.Node{
		{ID: "server0", Address: "http://localhost:8080"},
		{ID: "server1", Address: "http://localhost:8081"},
		{ID: "server2", Address: "http://localhost:8082"},
	}
	var scheduler segregatedScheduler
	nodes, err := scheduler.Nodes()
	c.Assert(err, gocheck.IsNil)
	c.Assert(nodes, gocheck.DeepEquals, expected)
}
<commit_msg>provision/docker: fix TestSchedulerNoNamespace<commit_after>// Copyright 2013 tsuru 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 docker

import (
	"bytes"
	"github.com/dotcloud/docker"
	dcli "github.com/fsouza/go-dockerclient"
	"github.com/fsouza/go-dockerclient/testing"
	"github.com/globocom/config"
	"github.com/globocom/docker-cluster/cluster"
	"github.com/globocom/tsuru/app"
	"github.com/globocom/tsuru/db"
	"labix.org/v2/mgo/bson"
	"launchpad.net/gocheck"
)

type SchedulerSuite struct {
	storage *db.Storage
}

var _ = gocheck.Suite(&SchedulerSuite{})

func (s *SchedulerSuite) SetUpSuite(c *gocheck.C) {
	var err error
	config.Set("database:url", "127.0.0.1:27017")
	config.Set("database:name", "docker_scheduler_tests")
	config.Set("docker:repository-namespace", "tsuru")
	s.storage, err = db.Conn()
	c.Assert(err, gocheck.IsNil)
}

func (s *SchedulerSuite) TearDownSuite(c *gocheck.C) {
	s.storage.Apps().Database.DropDatabase()
	s.storage.Close()
}

func (s *SchedulerSuite) TestSchedulerSchedule(c *gocheck.C) {
	server0, err := testing.NewServer(nil)
	c.Assert(err, gocheck.IsNil)
	defer server0.Stop()
	server1, err := testing.NewServer(nil)
	c.Assert(err, gocheck.IsNil)
	defer server1.Stop()
	server2, err := testing.NewServer(nil)
	c.Assert(err, gocheck.IsNil)
	defer server2.Stop()
	var buf bytes.Buffer
	client, _ := dcli.NewClient(server0.URL())
	client.PullImage(dcli.PullImageOptions{Repository: "tsuru/python"}, &buf)
	client.PullImage(dcli.PullImageOptions{Repository: "tsuru/impius"}, &buf)
	client.PullImage(dcli.PullImageOptions{Repository: "tsuru/mirror"}, &buf)
	client.PullImage(dcli.PullImageOptions{Repository: "tsuru/dedication"}, &buf)
	client, _ = dcli.NewClient(server1.URL())
	client.PullImage(dcli.PullImageOptions{Repository: "tsuru/python"}, &buf)
	client.PullImage(dcli.PullImageOptions{Repository: "tsuru/impius"}, &buf)
	client.PullImage(dcli.PullImageOptions{Repository: "tsuru/mirror"}, &buf)
	client.PullImage(dcli.PullImageOptions{Repository: "tsuru/dedication"}, &buf)
	client, _ = dcli.NewClient(server2.URL())
	client.PullImage(dcli.PullImageOptions{Repository: "tsuru/python"}, &buf)
	client.PullImage(dcli.PullImageOptions{Repository: "tsuru/impius"}, &buf)
	client.PullImage(dcli.PullImageOptions{Repository: "tsuru/mirror"}, &buf)
	client.PullImage(dcli.PullImageOptions{Repository: "tsuru/dedication"}, &buf)
	a1 := app.App{Name: "impius", Teams: []string{"tsuruteam", "nodockerforme"}}
	a2 := app.App{Name: "mirror", Teams: []string{"tsuruteam"}}
	a3 := app.App{Name: "dedication", Teams: []string{"nodockerforme"}}
	err = s.storage.Apps().Insert(a1, a2, a3)
	c.Assert(err, gocheck.IsNil)
	defer s.storage.Apps().Remove(bson.M{"name": bson.M{"$in": []string{a1.Name, a2.Name, a3.Name}}})
	coll := s.storage.Collection(schedulerCollection)
	err = coll.Insert(
		node{ID: "server0", Address: server0.URL(), Team: "tsuruteam"},
		node{ID: "server1", Address: server1.URL(), Team: "tsuruteam"},
		node{ID: "server2", Address: server2.URL()},
	)
	c.Assert(err, gocheck.IsNil)
	defer coll.Remove(bson.M{"_id": bson.M{"$in": []string{"server0", "server1", "server2"}}})
	var scheduler segregatedScheduler
	config := docker.Config{Cmd: []string{"/usr/sbin/sshd", "-D"}, Image: "tsuru/impius"}
	node, _, err := scheduler.Schedule(&config)
	c.Assert(err, gocheck.IsNil)
	c.Check(node, gocheck.Equals, "server2")
	config = docker.Config{Cmd: []string{"/usr/sbin/sshd", "-D"}, Image: "tsuru/mirror"}
	node, _, err = scheduler.Schedule(&config)
	c.Assert(err, gocheck.IsNil)
	c.Check(node == "server0" || node == "server1", gocheck.Equals, true)
	config = docker.Config{Cmd: []string{"/usr/sbin/sshd", "-D"}, Image: "tsuru/python"}
	node, _, err = scheduler.Schedule(&config)
	c.Assert(err, gocheck.IsNil)
	c.Check(node, gocheck.Equals, "server2")
	config = docker.Config{Cmd: []string{"/usr/sbin/sshd", "-D"}, Image: "tsuru/dedication"}
	node, _, err = scheduler.Schedule(&config)
	c.Assert(err, gocheck.IsNil)
	c.Check(node, gocheck.Equals, "server2")
	config = docker.Config{Cmd: []string{"/usr/sbin/sshd", "-D"}}
	node, _, _ = scheduler.Schedule(&config)
	c.Check(node, gocheck.Equals, "server2")
}

func (s *SchedulerSuite) TestSchedulerNoFallback(c *gocheck.C) {
	app := app.App{Name: "bill", Teams: []string{"jean"}}
	err := s.storage.Apps().Insert(app)
	c.Assert(err, gocheck.IsNil)
	defer s.storage.Apps().Remove(bson.M{"name": app.Name})
	config := docker.Config{Cmd: []string{"/usr/sbin/sshd", "-D"}, Image: "tsuru/python"}
	var scheduler segregatedScheduler
	node, container, err := scheduler.Schedule(&config)
	c.Assert(node, gocheck.Equals, "")
	c.Assert(container, gocheck.IsNil)
	c.Assert(err, gocheck.Equals, errNoFallback)
}

func (s *SchedulerSuite) TestSchedulerNoNamespace(c *gocheck.C) {
	old, _ := config.Get("docker:repository-namespace")
	defer config.Set("docker:repository-namespace", old)
	config.Unset("docker:repository-namespace")
	config := docker.Config{Cmd: []string{"/usr/sbin/sshd", "-D"}, Image: "tsuru/python"}
	var scheduler segregatedScheduler
	node, container, err := scheduler.Schedule(&config)
	c.Assert(node, gocheck.Equals, "")
	c.Assert(container, gocheck.IsNil)
	c.Assert(err, gocheck.NotNil)
}

func (s *SchedulerSuite) TestSchedulerInvalidEndpoint(c *gocheck.C) {
	app := app.App{Name: "bill", Teams: []string{"jean"}}
	err := s.storage.Apps().Insert(app)
	c.Assert(err, gocheck.IsNil)
	defer s.storage.Apps().Remove(bson.M{"name": app.Name})
	coll := s.storage.Collection(schedulerCollection)
	err = coll.Insert(node{ID: "server0", Address: "", Team: "jean"})
	c.Assert(err, gocheck.IsNil)
	defer coll.Remove(bson.M{"_id": "server0"})
	config := docker.Config{Cmd: []string{"/usr/sbin/sshd", "-D"}, Image: "tsuru/python"}
	var scheduler segregatedScheduler
	node, container, err := scheduler.Schedule(&config)
	c.Assert(node, gocheck.Equals, "")
	c.Assert(container, gocheck.IsNil)
	c.Assert(err, gocheck.NotNil)
}

func (s *SchedulerSuite) TestSchedulerNodes(c *gocheck.C) {
	coll := s.storage.Collection(schedulerCollection)
	err := coll.Insert(
		node{ID: "server0", Address: "http://localhost:8080", Team: "tsuru"},
		node{ID: "server1", Address: "http://localhost:8081", Team: "tsuru"},
		node{ID: "server2", Address: "http://localhost:8082", Team: "tsuru"},
	)
	c.Assert(err, gocheck.IsNil)
	defer coll.RemoveAll(bson.M{"_id": bson.M{"$in": []string{"server0", "server1", "server2"}}})
	expected := []cluster.Node{
		{ID: "server0", Address: "http://localhost:8080"},
		{ID: "server1", Address: "http://localhost:8081"},
		{ID: "server2", Address: "http://localhost:8082"},
	}
	var scheduler segregatedScheduler
	nodes, err := scheduler.Nodes()
	c.Assert(err, gocheck.IsNil)
	c.Assert(nodes, gocheck.DeepEquals, expected)
}
<|endoftext|>