<commit_before>// Copyright 2018 by caixw, All rights reserved.
// Use of this source code is governed by a MIT
// license that can be found in the LICENSE file.

package sqlbuilder

import (
	"database/sql"
	"sort"
)

// UpdateStmt 更新语句
type UpdateStmt struct {
	*execStmt

	table  string
	where  *WhereStmt
	values []*updateSet

	occColumn string      // 乐观锁的列名
	occValue  interface{} // 乐观锁的当前值
}

// 表示一条 SET 语句。比如 set key=val
type updateSet struct {
	column string
	value  interface{}
	typ    byte // 类型，可以是 + 自增类型，- 自减类型，或是空值表示正常表达式
}

// Update 声明一条 UPDATE 的 SQL 语句
func Update(e Engine, d Dialect) *UpdateStmt {
	stmt := &UpdateStmt{values: []*updateSet{}}
	stmt.execStmt = newExecStmt(e, d, stmt)
	stmt.where = newWhere()

	return stmt
}

// Table 指定表名
func (stmt *UpdateStmt) Table(table string) *UpdateStmt {
	stmt.table = table
	return stmt
}

// Set 设置值，若 col 相同，则会覆盖
func (stmt *UpdateStmt) Set(col string, val interface{}) *UpdateStmt {
	stmt.values = append(stmt.values, &updateSet{
		column: col,
		value:  val,
		typ:    0,
	})
	return stmt
}

// Increase 给列增加值
func (stmt *UpdateStmt) Increase(col string, val interface{}) *UpdateStmt {
	stmt.values = append(stmt.values, &updateSet{
		column: col,
		value:  val,
		typ:    '+',
	})
	return stmt
}

// Decrease 给钱减少值
func (stmt *UpdateStmt) Decrease(col string, val interface{}) *UpdateStmt {
	stmt.values = append(stmt.values, &updateSet{
		column: col,
		value:  val,
		typ:    '-',
	})
	return stmt
}

// OCC 指定一个用于乐观锁的字段。
//
// val 表示乐观锁原始的值，更新时如果值不等于 val，将更新失败。
func (stmt *UpdateStmt) OCC(col string, val interface{}) *UpdateStmt {
	stmt.occColumn = col
	stmt.occValue = val
	stmt.Increase(col, 1)
	return stmt
}

// WhereStmt 实现 WhereStmter 接口
func (stmt *UpdateStmt) WhereStmt() *WhereStmt {
	return stmt.where
}

// Reset 重置语句
func (stmt *UpdateStmt) Reset() *UpdateStmt {
	stmt.table = ""
	stmt.where.Reset()
	stmt.values = stmt.values[:0]

	stmt.occColumn = ""
	stmt.occValue = nil

	return stmt
}

// SQL 获取 SQL 语句以及对应的参数
func (stmt *UpdateStmt) SQL() (string, []interface{}, error) {
	if err := stmt.checkErrors(); err != nil {
		return "", nil, err
	}

	buf := New("UPDATE ")
	buf.WriteString(stmt.table)
	buf.WriteString(" SET ")

	args := make([]interface{}, 0, len(stmt.values))

	for _, val := range stmt.values {
		buf.QuoteKey(val.column)
		buf.WriteBytes('=')

		if val.typ != 0 {
			buf.QuoteKey(val.column).WriteBytes(val.typ)
		}

		if named, ok := val.value.(sql.NamedArg); ok && named.Name != "" {
			buf.WriteBytes('@')
			buf.WriteString(named.Name)
		} else {
			buf.WriteBytes('?')
		}
		buf.WriteBytes(',')
		args = append(args, val.value)
	}
	buf.TruncateLast(1)

	wq, wa, err := stmt.getWhereSQL()
	if err != nil {
		return "", nil, err
	}

	if wq != "" {
		buf.WriteString(" WHERE ")
		buf.WriteString(wq)
		args = append(args, wa...)
	}

	return buf.String(), args, nil
}

func (stmt *UpdateStmt) getWhereSQL() (string, []interface{}, error) {
	if stmt.occColumn == "" {
		return stmt.where.SQL()
	}

	w := newWhere()
	w.appendGroup(true, stmt.where)

	occ := w.AndGroup()
	if named, ok := stmt.occValue.(sql.NamedArg); ok && named.Name != "" {
		occ.And(stmt.occColumn+"=@"+named.Name, stmt.occValue)
	} else {
		occ.And(stmt.occColumn+"=?", stmt.occValue)
	}

	q, a, err := w.SQL()

	return q, a, err
}

// 检测列名是否存在重复，先排序，再与后一元素比较。
func (stmt *UpdateStmt) checkErrors() error {
	if stmt.table == "" {
		return ErrTableIsEmpty
	}

	if len(stmt.values) == 0 {
		return ErrValueIsEmpty
	}

	if stmt.columnsHasDup() {
		return ErrDupColumn
	}

	return nil
}

// 检测列名是否存在重复，先排序，再与后一元素比较。
func (stmt *UpdateStmt) columnsHasDup() bool {
	sort.SliceStable(stmt.values, func(i, j int) bool {
		return stmt.values[i].column < stmt.values[j].column
	})

	for index, col := range stmt.values {
		if index+1 >= len(stmt.values) {
			return false
		}

		if col.column == stmt.values[index+1].column {
			return true
		}
	}

	return false
}

// Where UpdateStmt.And 的别名
func (stmt *UpdateStmt) Where(cond string, args ...interface{}) *UpdateStmt {
	return stmt.And(cond, args...)
}

// And 添加一条 and 语句
func (stmt *UpdateStmt) And(cond string, args ...interface{}) *UpdateStmt {
	stmt.where.And(cond, args...)
	return stmt
}

// Or 添加一条 OR 语句
func (stmt *UpdateStmt) Or(cond string, args ...interface{}) *UpdateStmt {
	stmt.where.Or(cond, args...)
	return stmt
}

// AndIsNull 指定 WHERE ... AND col IS NULL
func (stmt *UpdateStmt) AndIsNull(col string) *UpdateStmt {
	stmt.where.AndIsNull(col)
	return stmt
}

// OrIsNull 指定 WHERE ... OR col IS NULL
func (stmt *UpdateStmt) OrIsNull(col string) *UpdateStmt {
	stmt.where.OrIsNull(col)
	return stmt
}

// AndIsNotNull 指定 WHERE ... AND col IS NOT NULL
func (stmt *UpdateStmt) AndIsNotNull(col string) *UpdateStmt {
	stmt.where.AndIsNotNull(col)
	return stmt
}

// OrIsNotNull 指定 WHERE ... OR col IS NOT NULL
func (stmt *UpdateStmt) OrIsNotNull(col string) *UpdateStmt {
	stmt.where.OrIsNotNull(col)
	return stmt
}

// AndBetween 指定 WHERE ... AND col BETWEEN v1 AND v2
func (stmt *UpdateStmt) AndBetween(col string, v1, v2 interface{}) *UpdateStmt {
	stmt.where.AndBetween(col, v1, v2)
	return stmt
}

// OrBetween 指定 WHERE ... OR col BETWEEN v1 AND v2
func (stmt *UpdateStmt) OrBetween(col string, v1, v2 interface{}) *UpdateStmt {
	stmt.where.OrBetween(col, v1, v2)
	return stmt
}

// AndNotBetween 指定 WHERE ... AND col NOT BETWEEN v1 AND v2
func (stmt *UpdateStmt) AndNotBetween(col string, v1, v2 interface{}) *UpdateStmt {
	stmt.where.AndNotBetween(col, v1, v2)
	return stmt
}

// OrNotBetween 指定 WHERE ... OR col BETWEEN v1 AND v2
func (stmt *UpdateStmt) OrNotBetween(col string, v1, v2 interface{}) *UpdateStmt {
	stmt.where.OrNotBetween(col, v1, v2)
	return stmt
}

// AndLike 指定 WHERE ... AND col LIKE content
func (stmt *UpdateStmt) AndLike(col string, content interface{}) *UpdateStmt {
	stmt.where.AndLike(col, content)
	return stmt
}

// OrLike 指定 WHERE ... OR col LIKE content
func (stmt *UpdateStmt) OrLike(col string, content interface{}) *UpdateStmt {
	stmt.where.OrLike(col, content)
	return stmt
}

// AndNotLike 指定 WHERE ... AND col NOT LIKE content
func (stmt *UpdateStmt) AndNotLike(col string, content interface{}) *UpdateStmt {
	stmt.where.AndNotLike(col, content)
	return stmt
}

// OrNotLike 指定 WHERE ... OR col NOT LIKE content
func (stmt *UpdateStmt) OrNotLike(col string, content interface{}) *UpdateStmt {
	stmt.where.OrNotLike(col, content)
	return stmt
}

// AndIn 指定 WHERE ... AND col IN(v...)
func (stmt *UpdateStmt) AndIn(col string, v ...interface{}) *UpdateStmt {
	stmt.where.AndIn(col, v...)
	return stmt
}

// OrIn 指定 WHERE ... OR col IN(v...)
func (stmt *UpdateStmt) OrIn(col string, v ...interface{}) *UpdateStmt {
	stmt.where.OrIn(col, v...)
	return stmt
}

// AndNotIn 指定 WHERE ... AND col NOT IN(v...)
func (stmt *UpdateStmt) AndNotIn(col string, v ...interface{}) *UpdateStmt {
	stmt.where.AndNotIn(col, v...)
	return stmt
}

// OrNotIn 指定 WHERE ... OR col IN(v...)
func (stmt *UpdateStmt) OrNotIn(col string, v ...interface{}) *UpdateStmt {
	stmt.where.OrNotIn(col, v...)
	return stmt
}

// AndGroup 开始一个子条件语句
func (stmt *UpdateStmt) AndGroup() *WhereStmt {
	return stmt.where.AndGroup()
}

// OrGroup 开始一个子条件语句
func (stmt *UpdateStmt) OrGroup() *WhereStmt {
	return stmt.where.OrGroup()
}
<commit_msg>[sqlbuilder] 改进文档<commit_after>// Copyright 2018 by caixw, All rights reserved.
// Use of this source code is governed by a MIT
// license that can be found in the LICENSE file.

package sqlbuilder

import (
	"database/sql"
	"sort"
)

// UpdateStmt 更新语句
type UpdateStmt struct {
	*execStmt

	table  string
	where  *WhereStmt
	values []*updateSet

	occColumn string      // 乐观锁的列名
	occValue  interface{} // 乐观锁的当前值
}

// 表示一条 SET 语句。比如 set key=val
type updateSet struct {
	column string
	value  interface{}
	typ    byte // 类型，可以是 + 自增类型，- 自减类型，或是空值表示正常表达式
}

// Update 声明一条 UPDATE 的 SQL 语句
func Update(e Engine, d Dialect) *UpdateStmt {
	stmt := &UpdateStmt{values: []*updateSet{}}
	stmt.execStmt = newExecStmt(e, d, stmt)
	stmt.where = newWhere()

	return stmt
}

// Table 指定表名
func (stmt *UpdateStmt) Table(table string) *UpdateStmt {
	stmt.table = table
	return stmt
}

// Set 设置值，若 col 相同，则会覆盖
//
// val 可以是 sql.NamedArg 类型
func (stmt *UpdateStmt) Set(col string, val interface{}) *UpdateStmt {
	stmt.values = append(stmt.values, &updateSet{
		column: col,
		value:  val,
		typ:    0,
	})
	return stmt
}

// Increase 给列增加值
func (stmt *UpdateStmt) Increase(col string, val interface{}) *UpdateStmt {
	stmt.values = append(stmt.values, &updateSet{
		column: col,
		value:  val,
		typ:    '+',
	})
	return stmt
}

// Decrease 给钱减少值
func (stmt *UpdateStmt) Decrease(col string, val interface{}) *UpdateStmt {
	stmt.values = append(stmt.values, &updateSet{
		column: col,
		value:  val,
		typ:    '-',
	})
	return stmt
}

// OCC 指定一个用于乐观锁的字段。
//
// val 表示乐观锁原始的值，更新时如果值不等于 val，将更新失败。
func (stmt *UpdateStmt) OCC(col string, val interface{}) *UpdateStmt {
	stmt.occColumn = col
	stmt.occValue = val
	stmt.Increase(col, 1)
	return stmt
}

// WhereStmt 实现 WhereStmter 接口
func (stmt *UpdateStmt) WhereStmt() *WhereStmt {
	return stmt.where
}

// Reset 重置语句
func (stmt *UpdateStmt) Reset() *UpdateStmt {
	stmt.table = ""
	stmt.where.Reset()
	stmt.values = stmt.values[:0]

	stmt.occColumn = ""
	stmt.occValue = nil

	return stmt
}

// SQL 获取 SQL 语句以及对应的参数
func (stmt *UpdateStmt) SQL() (string, []interface{}, error) {
	if err := stmt.checkErrors(); err != nil {
		return "", nil, err
	}

	buf := New("UPDATE ")
	buf.WriteString(stmt.table)
	buf.WriteString(" SET ")

	args := make([]interface{}, 0, len(stmt.values))

	for _, val := range stmt.values {
		buf.QuoteKey(val.column).WriteBytes('=')

		if val.typ != 0 {
			buf.QuoteKey(val.column).WriteBytes(val.typ)
		}

		if named, ok := val.value.(sql.NamedArg); ok && named.Name != "" {
			buf.WriteBytes('@').WriteString(named.Name)
		} else {
			buf.WriteBytes('?')
		}
		buf.WriteBytes(',')
		args = append(args, val.value)
	}
	buf.TruncateLast(1)

	wq, wa, err := stmt.getWhereSQL()
	if err != nil {
		return "", nil, err
	}

	if wq != "" {
		buf.WriteString(" WHERE ").WriteString(wq)
		args = append(args, wa...)
	}

	return buf.String(), args, nil
}

func (stmt *UpdateStmt) getWhereSQL() (string, []interface{}, error) {
	if stmt.occColumn == "" {
		return stmt.where.SQL()
	}

	w := newWhere()
	w.appendGroup(true, stmt.where)

	occ := w.AndGroup()
	if named, ok := stmt.occValue.(sql.NamedArg); ok && named.Name != "" {
		occ.And(stmt.occColumn+"=@"+named.Name, stmt.occValue)
	} else {
		occ.And(stmt.occColumn+"=?", stmt.occValue)
	}

	q, a, err := w.SQL()

	return q, a, err
}

// 检测列名是否存在重复，先排序，再与后一元素比较。
func (stmt *UpdateStmt) checkErrors() error {
	if stmt.table == "" {
		return ErrTableIsEmpty
	}

	if len(stmt.values) == 0 {
		return ErrValueIsEmpty
	}

	if stmt.columnsHasDup() {
		return ErrDupColumn
	}

	return nil
}

// 检测列名是否存在重复，先排序，再与后一元素比较。
func (stmt *UpdateStmt) columnsHasDup() bool {
	sort.SliceStable(stmt.values, func(i, j int) bool {
		return stmt.values[i].column < stmt.values[j].column
	})

	for index, col := range stmt.values {
		if index+1 >= len(stmt.values) {
			return false
		}

		if col.column == stmt.values[index+1].column {
			return true
		}
	}

	return false
}

// Where UpdateStmt.And 的别名
func (stmt *UpdateStmt) Where(cond string, args ...interface{}) *UpdateStmt {
	return stmt.And(cond, args...)
}

// And 添加一条 and 语句
func (stmt *UpdateStmt) And(cond string, args ...interface{}) *UpdateStmt {
	stmt.where.And(cond, args...)
	return stmt
}

// Or 添加一条 OR 语句
func (stmt *UpdateStmt) Or(cond string, args ...interface{}) *UpdateStmt {
	stmt.where.Or(cond, args...)
	return stmt
}

// AndIsNull 指定 WHERE ... AND col IS NULL
func (stmt *UpdateStmt) AndIsNull(col string) *UpdateStmt {
	stmt.where.AndIsNull(col)
	return stmt
}

// OrIsNull 指定 WHERE ... OR col IS NULL
func (stmt *UpdateStmt) OrIsNull(col string) *UpdateStmt {
	stmt.where.OrIsNull(col)
	return stmt
}

// AndIsNotNull 指定 WHERE ... AND col IS NOT NULL
func (stmt *UpdateStmt) AndIsNotNull(col string) *UpdateStmt {
	stmt.where.AndIsNotNull(col)
	return stmt
}

// OrIsNotNull 指定 WHERE ... OR col IS NOT NULL
func (stmt *UpdateStmt) OrIsNotNull(col string) *UpdateStmt {
	stmt.where.OrIsNotNull(col)
	return stmt
}

// AndBetween 指定 WHERE ... AND col BETWEEN v1 AND v2
func (stmt *UpdateStmt) AndBetween(col string, v1, v2 interface{}) *UpdateStmt {
	stmt.where.AndBetween(col, v1, v2)
	return stmt
}

// OrBetween 指定 WHERE ... OR col BETWEEN v1 AND v2
func (stmt *UpdateStmt) OrBetween(col string, v1, v2 interface{}) *UpdateStmt {
	stmt.where.OrBetween(col, v1, v2)
	return stmt
}

// AndNotBetween 指定 WHERE ... AND col NOT BETWEEN v1 AND v2
func (stmt *UpdateStmt) AndNotBetween(col string, v1, v2 interface{}) *UpdateStmt {
	stmt.where.AndNotBetween(col, v1, v2)
	return stmt
}

// OrNotBetween 指定 WHERE ... OR col BETWEEN v1 AND v2
func (stmt *UpdateStmt) OrNotBetween(col string, v1, v2 interface{}) *UpdateStmt {
	stmt.where.OrNotBetween(col, v1, v2)
	return stmt
}

// AndLike 指定 WHERE ... AND col LIKE content
func (stmt *UpdateStmt) AndLike(col string, content interface{}) *UpdateStmt {
	stmt.where.AndLike(col, content)
	return stmt
}

// OrLike 指定 WHERE ... OR col LIKE content
func (stmt *UpdateStmt) OrLike(col string, content interface{}) *UpdateStmt {
	stmt.where.OrLike(col, content)
	return stmt
}

// AndNotLike 指定 WHERE ... AND col NOT LIKE content
func (stmt *UpdateStmt) AndNotLike(col string, content interface{}) *UpdateStmt {
	stmt.where.AndNotLike(col, content)
	return stmt
}

// OrNotLike 指定 WHERE ... OR col NOT LIKE content
func (stmt *UpdateStmt) OrNotLike(col string, content interface{}) *UpdateStmt {
	stmt.where.OrNotLike(col, content)
	return stmt
}

// AndIn 指定 WHERE ... AND col IN(v...)
func (stmt *UpdateStmt) AndIn(col string, v ...interface{}) *UpdateStmt {
	stmt.where.AndIn(col, v...)
	return stmt
}

// OrIn 指定 WHERE ... OR col IN(v...)
func (stmt *UpdateStmt) OrIn(col string, v ...interface{}) *UpdateStmt {
	stmt.where.OrIn(col, v...)
	return stmt
}

// AndNotIn 指定 WHERE ... AND col NOT IN(v...)
func (stmt *UpdateStmt) AndNotIn(col string, v ...interface{}) *UpdateStmt {
	stmt.where.AndNotIn(col, v...)
	return stmt
}

// OrNotIn 指定 WHERE ... OR col IN(v...)
func (stmt *UpdateStmt) OrNotIn(col string, v ...interface{}) *UpdateStmt {
	stmt.where.OrNotIn(col, v...)
	return stmt
}

// AndGroup 开始一个子条件语句
func (stmt *UpdateStmt) AndGroup() *WhereStmt {
	return stmt.where.AndGroup()
}

// OrGroup 开始一个子条件语句
func (stmt *UpdateStmt) OrGroup() *WhereStmt {
	return stmt.where.OrGroup()
}
<|endoftext|>


<commit_before>// Copyright 2017 Eduardo Pinheiro (edpin@edpin.com). All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package https

import (
	"log"
	"net/http"
	"crypto/tls"
	"time"
	"strings"
)

// StartSecureServer starts an HTTPS server with a mux and a getCertificate
// function, which may be nil. Typically getCertificate will come from
// autocert.Manager, from package acme/autocert. The HTTPS server started
// enables HTST by default to ensure maximum protection (see
// https://www.owasp.org/index.php/HTTP_Strict_Transport_Security_Cheat_Sheet).
// StartSecureServer also starts an HTTP server that redirects all requests to
// their HTTPS counterpart and immediately terminates all connections.
func StartSecureServer(mux *http.ServeMux, getCertificate func(clientHello *tls.ClientHelloInfo) (*tls.Certificate, error)) {
	s := &http.Server{
		Addr:         ":https",
		ReadTimeout:  5 * time.Second,
		WriteTimeout: 10 * time.Second,
		IdleTimeout:  120 * time.Second,
		Handler:      NewHSTS(mux),
		TLSConfig: &tls.Config{
			GetCertificate: getCertificate,
			MinVersion:     tls.VersionTLS12,
			CurvePreferences: []tls.CurveID{
				tls.X25519, // requires go 1.8
				tls.CurveP521,
				tls.CurveP384,
				tls.CurveP256,
			},
			// Prefer this order of ciphers.
			CipherSuites: []uint16{
				tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
				tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305,
				// required by HTTP-2.
				tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
			},
		},
	}
	// Redirect regular HTTP requests to HTTPS.
	insecure := &http.Server{
		ReadTimeout:  5 * time.Second,
		WriteTimeout: 5 * time.Second,
		Handler: http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
			w.Header().Set("Connection", "close")
			url := "https://" + req.Host + req.URL.String()
			http.Redirect(w, req, url, http.StatusMovedPermanently)
		}),
	}
	go func() { log.Fatal(insecure.ListenAndServe()) }()
	log.Fatal(s.ListenAndServeTLS("", ""))
}

type htstMux struct {
	*http.ServeMux
}

// NewHSTS returns an HTTP handler that sets HSTS headers on all requests.
func NewHSTS(mux *http.ServeMux) http.Handler {
	return htstMux{
		ServeMux: mux,
	}
}

// ServeHTTP implements http.Handler.
func (h htstMux) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Strict-Transport-Security", "max-age=31536000; includeSubDomains")
	h.ServeMux.ServeHTTP(w, r)
}
<commit_msg>fixed extra import<commit_after>// Copyright 2017 Eduardo Pinheiro (edpin@edpin.com). All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package https

import (
	"log"
	"net/http"
	"crypto/tls"
	"time"
)

// StartSecureServer starts an HTTPS server with a mux and a getCertificate
// function, which may be nil. Typically getCertificate will come from
// autocert.Manager, from package acme/autocert. The HTTPS server started
// enables HTST by default to ensure maximum protection (see
// https://www.owasp.org/index.php/HTTP_Strict_Transport_Security_Cheat_Sheet).
// StartSecureServer also starts an HTTP server that redirects all requests to
// their HTTPS counterpart and immediately terminates all connections.
func StartSecureServer(mux *http.ServeMux, getCertificate func(clientHello *tls.ClientHelloInfo) (*tls.Certificate, error)) {
	s := &http.Server{
		Addr:         ":https",
		ReadTimeout:  5 * time.Second,
		WriteTimeout: 10 * time.Second,
		IdleTimeout:  120 * time.Second,
		Handler:      NewHSTS(mux),
		TLSConfig: &tls.Config{
			GetCertificate: getCertificate,
			MinVersion:     tls.VersionTLS12,
			CurvePreferences: []tls.CurveID{
				tls.X25519, // requires go 1.8
				tls.CurveP521,
				tls.CurveP384,
				tls.CurveP256,
			},
			// Prefer this order of ciphers.
			CipherSuites: []uint16{
				tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
				tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305,
				// required by HTTP-2.
				tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
			},
		},
	}
	// Redirect regular HTTP requests to HTTPS.
	insecure := &http.Server{
		ReadTimeout:  5 * time.Second,
		WriteTimeout: 5 * time.Second,
		Handler: http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
			w.Header().Set("Connection", "close")
			url := "https://" + req.Host + req.URL.String()
			http.Redirect(w, req, url, http.StatusMovedPermanently)
		}),
	}
	go func() { log.Fatal(insecure.ListenAndServe()) }()
	log.Fatal(s.ListenAndServeTLS("", ""))
}

type htstMux struct {
	*http.ServeMux
}

// NewHSTS returns an HTTP handler that sets HSTS headers on all requests.
func NewHSTS(mux *http.ServeMux) http.Handler {
	return htstMux{
		ServeMux: mux,
	}
}

// ServeHTTP implements http.Handler.
func (h htstMux) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Strict-Transport-Security", "max-age=31536000; includeSubDomains")
	h.ServeMux.ServeHTTP(w, r)
}
<|endoftext|>


<commit_before>package pilosa

import (
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"net"
	"net/http"
	"net/url"
	"os"
	"strconv"
	"sync"
	"time"

	"github.com/gogo/protobuf/proto"
	"github.com/umbel/pilosa/internal"
)

// Default server settings.
const (
	DefaultAntiEntropyInterval = 10 * time.Minute
	//DefaultPollingInterval     = 60 * time.Second
	DefaultPollingInterval = 5 * time.Second
)

// Server represents an index wrapped by a running HTTP server.
type Server struct {
	ln net.Listener

	// Close management.
	wg      sync.WaitGroup
	closing chan struct{}

	// Data storage and HTTP interface.
	Index   *Index
	Handler *Handler

	// Cluster configuration.
	// Host is replaced with actual host after opening if port is ":0".
	Host    string
	Cluster *Cluster

	// Background monitoring intervals.
	AntiEntropyInterval time.Duration
	PollingInterval     time.Duration

	LogOutput io.Writer
}

// NewServer returns a new instance of Server.
func NewServer() *Server {
	s := &Server{
		closing: make(chan struct{}),

		Index:   NewIndex(),
		Handler: NewHandler(),

		AntiEntropyInterval: DefaultAntiEntropyInterval,
		PollingInterval:     DefaultPollingInterval,

		LogOutput: os.Stderr,
	}

	s.Handler.Index = s.Index

	return s
}

// Open opens and initializes the server.
func (s *Server) Open() error {
	// Require a port in the hostname.
	host, port, err := net.SplitHostPort(s.Host)
	if err != nil {
		return err
	} else if port == "" {
		return errors.New("port must be specified in config host")
	}

	// Open HTTP listener to determine port (if specified as :0).
	ln, err := net.Listen("tcp", ":"+port)
	if err != nil {
		return err
	}
	s.ln = ln

	// Determine hostname based on listening port.
	s.Host = net.JoinHostPort(host, strconv.Itoa(s.ln.Addr().(*net.TCPAddr).Port))

	// Create local node if no cluster is specified.
	if len(s.Cluster.Nodes) == 0 {
		s.Cluster.Nodes = []*Node{{Host: s.Host}}
	}

	// Open index.
	if err := s.Index.Open(); err != nil {
		return err
	}

	// Create executor for executing queries.
	e := NewExecutor()
	e.Index = s.Index
	e.Host = s.Host
	e.Cluster = s.Cluster

	// Initialize HTTP handler.
	s.Handler.Host = s.Host
	s.Handler.Cluster = s.Cluster
	s.Handler.Executor = e
	s.Handler.LogOutput = s.LogOutput

	// Serve HTTP.
	go func() { http.Serve(ln, s.Handler) }()

	// Start background monitoring.
	s.wg.Add(2)
	go func() { defer s.wg.Done(); s.monitorAntiEntropy() }()
	go func() { defer s.wg.Done(); s.monitorMaxSlices() }()

	return nil
}

// Close closes the server and waits for it to shutdown.
func (s *Server) Close() error {
	// Notify goroutines to stop.
	close(s.closing)
	s.wg.Wait()

	if s.ln != nil {
		s.ln.Close()
	}
	if s.Index != nil {
		s.Index.Close()
	}

	return nil
}

// Addr returns the address of the listener.
func (s *Server) Addr() net.Addr {
	if s.ln == nil {
		return nil
	}
	return s.ln.Addr()
}

func (s *Server) logger() *log.Logger { return log.New(s.LogOutput, "", log.LstdFlags) }

func (s *Server) monitorAntiEntropy() {
	ticker := time.NewTicker(s.AntiEntropyInterval)
	defer ticker.Stop()

	s.logger().Printf("index sync monitor initializing (%s interval)", s.AntiEntropyInterval)

	for {
		// Wait for tick or a close.
		select {
		case <-s.closing:
			return
		case <-ticker.C:
		}

		s.logger().Printf("index sync beginning")

		// Initialize syncer with local index and remote client.
		var syncer IndexSyncer
		syncer.Index = s.Index
		syncer.Host = s.Host
		syncer.Cluster = s.Cluster
		syncer.Closing = s.closing

		// Sync indexes.
		if err := syncer.SyncIndex(); err != nil {
			s.logger().Printf("index sync error: err=%s", err)
			continue
		}

		// Record successful sync in log.
		s.logger().Printf("index sync complete")
	}
}

// monitorMaxSlices periodically pulls the highest slice from each node in the cluster.
func (s *Server) monitorMaxSlices() {
	// Ignore if only one node in the cluster.
	if len(s.Cluster.Nodes) <= 1 {
		return
	}

	ticker := time.NewTicker(s.PollingInterval)
	defer ticker.Stop()

	for {
		select {
		case <-s.closing:
			return
		case <-ticker.C:
		}

		oldmaxslices := s.Index.SliceNs()
		for _, node := range s.Cluster.Nodes {
			if s.Host != node.Host {
				maxSlices, _ := checkMaxSlices(node.Host)
				for db, newmax := range maxSlices {
					// we're not going to create a db locally if we don't know about it already
					// TODO: consider changing this so we DO create a db locally
					// do we want/need nodes to have empty files structures?
					if localdb := s.Index.DB(db); localdb != nil {
						if newmax > oldmaxslices[db] {
							oldmaxslices[db] = newmax
							localdb.SetRemoteMaxSlice(newmax)
						}
					}
				}
			}
		}
	}
}

func checkMaxSlices(hostport string) (MaxSlices, error) {
	// Create HTTP request.
	req, err := http.NewRequest("GET", (&url.URL{
		Scheme: "http",
		Host:   hostport,
		Path:   "/slices/max",
	}).String(), nil)

	if err != nil {
		return nil, err
	}

	// Require protobuf encoding.
	req.Header.Set("Accept", "application/x-protobuf")
	req.Header.Set("Content-Type", "application/x-protobuf")

	// Send request to remote node.
	resp, err := http.DefaultClient.Do(req)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

	// Read response into buffer.
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return nil, err
	}

	// Check status code.
	if resp.StatusCode != http.StatusOK {
		return nil, fmt.Errorf("invalid status: code=%d, err=%s", resp.StatusCode, body)
	}

	// Decode response object.
	pb := internal.MaxSlicesResponse{}

	if err = proto.Unmarshal(body, &pb); err != nil {
		return nil, err
	}

	return pb.MaxSlices, nil

}
<commit_msg>set DefaultPollingInterval back to its original value<commit_after>package pilosa

import (
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"net"
	"net/http"
	"net/url"
	"os"
	"strconv"
	"sync"
	"time"

	"github.com/gogo/protobuf/proto"
	"github.com/umbel/pilosa/internal"
)

// Default server settings.
const (
	DefaultAntiEntropyInterval = 10 * time.Minute
	DefaultPollingInterval     = 60 * time.Second
)

// Server represents an index wrapped by a running HTTP server.
type Server struct {
	ln net.Listener

	// Close management.
	wg      sync.WaitGroup
	closing chan struct{}

	// Data storage and HTTP interface.
	Index   *Index
	Handler *Handler

	// Cluster configuration.
	// Host is replaced with actual host after opening if port is ":0".
	Host    string
	Cluster *Cluster

	// Background monitoring intervals.
	AntiEntropyInterval time.Duration
	PollingInterval     time.Duration

	LogOutput io.Writer
}

// NewServer returns a new instance of Server.
func NewServer() *Server {
	s := &Server{
		closing: make(chan struct{}),

		Index:   NewIndex(),
		Handler: NewHandler(),

		AntiEntropyInterval: DefaultAntiEntropyInterval,
		PollingInterval:     DefaultPollingInterval,

		LogOutput: os.Stderr,
	}

	s.Handler.Index = s.Index

	return s
}

// Open opens and initializes the server.
func (s *Server) Open() error {
	// Require a port in the hostname.
	host, port, err := net.SplitHostPort(s.Host)
	if err != nil {
		return err
	} else if port == "" {
		return errors.New("port must be specified in config host")
	}

	// Open HTTP listener to determine port (if specified as :0).
	ln, err := net.Listen("tcp", ":"+port)
	if err != nil {
		return err
	}
	s.ln = ln

	// Determine hostname based on listening port.
	s.Host = net.JoinHostPort(host, strconv.Itoa(s.ln.Addr().(*net.TCPAddr).Port))

	// Create local node if no cluster is specified.
	if len(s.Cluster.Nodes) == 0 {
		s.Cluster.Nodes = []*Node{{Host: s.Host}}
	}

	// Open index.
	if err := s.Index.Open(); err != nil {
		return err
	}

	// Create executor for executing queries.
	e := NewExecutor()
	e.Index = s.Index
	e.Host = s.Host
	e.Cluster = s.Cluster

	// Initialize HTTP handler.
	s.Handler.Host = s.Host
	s.Handler.Cluster = s.Cluster
	s.Handler.Executor = e
	s.Handler.LogOutput = s.LogOutput

	// Serve HTTP.
	go func() { http.Serve(ln, s.Handler) }()

	// Start background monitoring.
	s.wg.Add(2)
	go func() { defer s.wg.Done(); s.monitorAntiEntropy() }()
	go func() { defer s.wg.Done(); s.monitorMaxSlices() }()

	return nil
}

// Close closes the server and waits for it to shutdown.
func (s *Server) Close() error {
	// Notify goroutines to stop.
	close(s.closing)
	s.wg.Wait()

	if s.ln != nil {
		s.ln.Close()
	}
	if s.Index != nil {
		s.Index.Close()
	}

	return nil
}

// Addr returns the address of the listener.
func (s *Server) Addr() net.Addr {
	if s.ln == nil {
		return nil
	}
	return s.ln.Addr()
}

func (s *Server) logger() *log.Logger { return log.New(s.LogOutput, "", log.LstdFlags) }

func (s *Server) monitorAntiEntropy() {
	ticker := time.NewTicker(s.AntiEntropyInterval)
	defer ticker.Stop()

	s.logger().Printf("index sync monitor initializing (%s interval)", s.AntiEntropyInterval)

	for {
		// Wait for tick or a close.
		select {
		case <-s.closing:
			return
		case <-ticker.C:
		}

		s.logger().Printf("index sync beginning")

		// Initialize syncer with local index and remote client.
		var syncer IndexSyncer
		syncer.Index = s.Index
		syncer.Host = s.Host
		syncer.Cluster = s.Cluster
		syncer.Closing = s.closing

		// Sync indexes.
		if err := syncer.SyncIndex(); err != nil {
			s.logger().Printf("index sync error: err=%s", err)
			continue
		}

		// Record successful sync in log.
		s.logger().Printf("index sync complete")
	}
}

// monitorMaxSlices periodically pulls the highest slice from each node in the cluster.
func (s *Server) monitorMaxSlices() {
	// Ignore if only one node in the cluster.
	if len(s.Cluster.Nodes) <= 1 {
		return
	}

	ticker := time.NewTicker(s.PollingInterval)
	defer ticker.Stop()

	for {
		select {
		case <-s.closing:
			return
		case <-ticker.C:
		}

		oldmaxslices := s.Index.SliceNs()
		for _, node := range s.Cluster.Nodes {
			if s.Host != node.Host {
				maxSlices, _ := checkMaxSlices(node.Host)
				for db, newmax := range maxSlices {
					// we're not going to create a db locally if we don't know about it already
					// TODO: consider changing this so we DO create a db locally
					// do we want/need nodes to have empty files structures?
					if localdb := s.Index.DB(db); localdb != nil {
						if newmax > oldmaxslices[db] {
							oldmaxslices[db] = newmax
							localdb.SetRemoteMaxSlice(newmax)
						}
					}
				}
			}
		}
	}
}

func checkMaxSlices(hostport string) (MaxSlices, error) {
	// Create HTTP request.
	req, err := http.NewRequest("GET", (&url.URL{
		Scheme: "http",
		Host:   hostport,
		Path:   "/slices/max",
	}).String(), nil)

	if err != nil {
		return nil, err
	}

	// Require protobuf encoding.
	req.Header.Set("Accept", "application/x-protobuf")
	req.Header.Set("Content-Type", "application/x-protobuf")

	// Send request to remote node.
	resp, err := http.DefaultClient.Do(req)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

	// Read response into buffer.
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return nil, err
	}

	// Check status code.
	if resp.StatusCode != http.StatusOK {
		return nil, fmt.Errorf("invalid status: code=%d, err=%s", resp.StatusCode, body)
	}

	// Decode response object.
	pb := internal.MaxSlicesResponse{}

	if err = proto.Unmarshal(body, &pb); err != nil {
		return nil, err
	}

	return pb.MaxSlices, nil

}
<|endoftext|>


<commit_before>package rpc2

import (
	"io"
	"log"
	"net"
	"reflect"
	"unicode"
	"unicode/utf8"

	"github.com/cenkalti/hub"
)

// Precompute the reflect type for error.  Can't use error directly
// because Typeof takes an empty interface value.  This is annoying.
var typeOfError = reflect.TypeOf((*error)(nil)).Elem()
var typeOfClient = reflect.TypeOf((*Client)(nil))

const (
	clientConnected hub.Kind = iota
	clientDisconnected
)

type Server struct {
	handlers map[string]*handler
	eventHub *hub.Hub
}

type handler struct {
	fn        reflect.Value
	argType   reflect.Type
	replyType reflect.Type
}

type connectionEvent struct {
	Client *Client
}

type disconnectionEvent struct {
	Client *Client
}

func (connectionEvent) Kind() hub.Kind    { return clientConnected }
func (disconnectionEvent) Kind() hub.Kind { return clientDisconnected }

func NewServer() *Server {
	return &Server{
		handlers: make(map[string]*handler),
		eventHub: hub.New(),
	}
}

func (s *Server) Handle(method string, handlerFunc interface{}) {
	addHandler(s.handlers, method, handlerFunc)
}

func addHandler(handlers map[string]*handler, mname string, handlerFunc interface{}) {
	if _, ok := handlers[mname]; ok {
		panic("rpc2: multiple registrations for " + mname)
	}

	method := reflect.ValueOf(handlerFunc)
	mtype := method.Type()
	// Method needs three ins: *client, *args, *reply.
	if mtype.NumIn() != 3 {
		log.Panicln("method", mname, "has wrong number of ins:", mtype.NumIn())
	}
	// First arg must be a pointer to rpc2.Client.
	clientType := mtype.In(0)
	if clientType.Kind() != reflect.Ptr {
		log.Panicln("method", mname, "client type not a pointer:", clientType)
	}
	if clientType != typeOfClient {
		log.Panicln("method", mname, "first argument", clientType.String(), "not *rpc2.Client")
	}
	// Second arg need not be a pointer.
	argType := mtype.In(1)
	if !isExportedOrBuiltinType(argType) {
		log.Panicln(mname, "argument type not exported:", argType)
	}
	// Third arg must be a pointer.
	replyType := mtype.In(2)
	if replyType.Kind() != reflect.Ptr {
		log.Panicln("method", mname, "reply type not a pointer:", replyType)
	}
	// Reply type must be exported.
	if !isExportedOrBuiltinType(replyType) {
		log.Panicln("method", mname, "reply type not exported:", replyType)
	}
	// Method needs one out.
	if mtype.NumOut() != 1 {
		log.Panicln("method", mname, "has wrong number of outs:", mtype.NumOut())
	}
	// The return type of the method must be error.
	if returnType := mtype.Out(0); returnType != typeOfError {
		log.Panicln("method", mname, "returns", returnType.String(), "not error")
	}
	handlers[mname] = &handler{
		fn:        method,
		argType:   argType,
		replyType: replyType,
	}
}

// Is this type exported or a builtin?
func isExportedOrBuiltinType(t reflect.Type) bool {
	for t.Kind() == reflect.Ptr {
		t = t.Elem()
	}
	// PkgPath will be non-empty even for an exported type,
	// so we need to check the type name as well.
	return isExported(t.Name()) || t.PkgPath() == ""
}

// Is this an exported - upper case - name?
func isExported(name string) bool {
	rune, _ := utf8.DecodeRuneInString(name)
	return unicode.IsUpper(rune)
}

func (s *Server) OnConnect(f func(*Client)) {
	s.eventHub.Subscribe(clientConnected, func(e hub.Event) {
		go f(e.(connectionEvent).Client)
	})
}

func (s *Server) OnDisconnect(f func(*Client)) {
	s.eventHub.Subscribe(clientDisconnected, func(e hub.Event) {
		go f(e.(disconnectionEvent).Client)
	})
}

// Accept accepts connections on the listener and serves requests
// for each incoming connection.  Accept blocks; the caller typically
// invokes it in a go statement.
func (s *Server) Accept(lis net.Listener) {
	for {
		conn, err := lis.Accept()
		if err != nil {
			log.Fatal("rpc.Serve: accept:", err.Error())
		}
		go s.ServeConn(conn)
	}
}

// ServeConn runs the server on a single connection.
// ServeConn blocks, serving the connection until the client hangs up.
// The caller typically invokes ServeConn in a go statement.
// ServeConn uses the gob wire format (see package gob) on the
// connection.  To use an alternate codec, use ServeCodec.
func (s *Server) ServeConn(conn io.ReadWriteCloser) {
	s.ServeCodec(newGobCodec(conn))
}

// ServeCodec is like ServeConn but uses the specified codec to
// decode requests and encode responses.
func (s *Server) ServeCodec(codec Codec) {
	defer codec.Close()

	// Client also handles the incoming connections.
	c := NewClientWithCodec(codec)
	c.handlers = s.handlers

	s.eventHub.Publish(connectionEvent{c})
	c.Run()
	s.eventHub.Publish(disconnectionEvent{c})
}
<commit_msg>add comment<commit_after>package rpc2

import (
	"io"
	"log"
	"net"
	"reflect"
	"unicode"
	"unicode/utf8"

	"github.com/cenkalti/hub"
)

// Precompute the reflect type for error.  Can't use error directly
// because Typeof takes an empty interface value.  This is annoying.
var typeOfError = reflect.TypeOf((*error)(nil)).Elem()
var typeOfClient = reflect.TypeOf((*Client)(nil))

const (
	clientConnected hub.Kind = iota
	clientDisconnected
)

type Server struct {
	handlers map[string]*handler
	eventHub *hub.Hub
}

type handler struct {
	fn        reflect.Value
	argType   reflect.Type
	replyType reflect.Type
}

type connectionEvent struct {
	Client *Client
}

type disconnectionEvent struct {
	Client *Client
}

func (connectionEvent) Kind() hub.Kind    { return clientConnected }
func (disconnectionEvent) Kind() hub.Kind { return clientDisconnected }

func NewServer() *Server {
	return &Server{
		handlers: make(map[string]*handler),
		eventHub: hub.New(),
	}
}

// Handle registers the handler function for the given method. If a handler already exists for method, Handle panics.
func (s *Server) Handle(method string, handlerFunc interface{}) {
	addHandler(s.handlers, method, handlerFunc)
}

func addHandler(handlers map[string]*handler, mname string, handlerFunc interface{}) {
	if _, ok := handlers[mname]; ok {
		panic("rpc2: multiple registrations for " + mname)
	}

	method := reflect.ValueOf(handlerFunc)
	mtype := method.Type()
	// Method needs three ins: *client, *args, *reply.
	if mtype.NumIn() != 3 {
		log.Panicln("method", mname, "has wrong number of ins:", mtype.NumIn())
	}
	// First arg must be a pointer to rpc2.Client.
	clientType := mtype.In(0)
	if clientType.Kind() != reflect.Ptr {
		log.Panicln("method", mname, "client type not a pointer:", clientType)
	}
	if clientType != typeOfClient {
		log.Panicln("method", mname, "first argument", clientType.String(), "not *rpc2.Client")
	}
	// Second arg need not be a pointer.
	argType := mtype.In(1)
	if !isExportedOrBuiltinType(argType) {
		log.Panicln(mname, "argument type not exported:", argType)
	}
	// Third arg must be a pointer.
	replyType := mtype.In(2)
	if replyType.Kind() != reflect.Ptr {
		log.Panicln("method", mname, "reply type not a pointer:", replyType)
	}
	// Reply type must be exported.
	if !isExportedOrBuiltinType(replyType) {
		log.Panicln("method", mname, "reply type not exported:", replyType)
	}
	// Method needs one out.
	if mtype.NumOut() != 1 {
		log.Panicln("method", mname, "has wrong number of outs:", mtype.NumOut())
	}
	// The return type of the method must be error.
	if returnType := mtype.Out(0); returnType != typeOfError {
		log.Panicln("method", mname, "returns", returnType.String(), "not error")
	}
	handlers[mname] = &handler{
		fn:        method,
		argType:   argType,
		replyType: replyType,
	}
}

// Is this type exported or a builtin?
func isExportedOrBuiltinType(t reflect.Type) bool {
	for t.Kind() == reflect.Ptr {
		t = t.Elem()
	}
	// PkgPath will be non-empty even for an exported type,
	// so we need to check the type name as well.
	return isExported(t.Name()) || t.PkgPath() == ""
}

// Is this an exported - upper case - name?
func isExported(name string) bool {
	rune, _ := utf8.DecodeRuneInString(name)
	return unicode.IsUpper(rune)
}

// OnConnect registers a function to run when a client connects.
func (s *Server) OnConnect(f func(*Client)) {
	s.eventHub.Subscribe(clientConnected, func(e hub.Event) {
		go f(e.(connectionEvent).Client)
	})
}

// OnDisconnect registers a function to run when a client disconnects.
func (s *Server) OnDisconnect(f func(*Client)) {
	s.eventHub.Subscribe(clientDisconnected, func(e hub.Event) {
		go f(e.(disconnectionEvent).Client)
	})
}

// Accept accepts connections on the listener and serves requests
// for each incoming connection.  Accept blocks; the caller typically
// invokes it in a go statement.
func (s *Server) Accept(lis net.Listener) {
	for {
		conn, err := lis.Accept()
		if err != nil {
			log.Fatal("rpc.Serve: accept:", err.Error())
		}
		go s.ServeConn(conn)
	}
}

// ServeConn runs the server on a single connection.
// ServeConn blocks, serving the connection until the client hangs up.
// The caller typically invokes ServeConn in a go statement.
// ServeConn uses the gob wire format (see package gob) on the
// connection.  To use an alternate codec, use ServeCodec.
func (s *Server) ServeConn(conn io.ReadWriteCloser) {
	s.ServeCodec(newGobCodec(conn))
}

// ServeCodec is like ServeConn but uses the specified codec to
// decode requests and encode responses.
func (s *Server) ServeCodec(codec Codec) {
	defer codec.Close()

	// Client also handles the incoming connections.
	c := NewClientWithCodec(codec)
	c.handlers = s.handlers

	s.eventHub.Publish(connectionEvent{c})
	c.Run()
	s.eventHub.Publish(disconnectionEvent{c})
}
<|endoftext|>


<commit_before>package main

import (
	"crypto/rand"
	"flag"
	"log"
	"net/http"

	"github.com/go-martini/martini"
	"github.com/martini-contrib/binding"
	"github.com/martini-contrib/render"
	"labix.org/v2/mgo"
	"labix.org/v2/mgo/bson"
)

type LMVFile struct {
	Size      int64      `bson:"size"         json:"size"         binding:"required"`
	Name      string     `bson:"name"         json:"name"         binding:"required"`
	Algorithm string     `bson:"algorithm"    json:"algorithm"    binding:"required"`
	Chunks    []LMVChunk `bson:"chunks"       json:"chunks"       binding:"required"`
	Tar       bool       `bson:"tar"          json:"tar"`
	Token     string     `bson:"token"        json:"token"`
}

type LMVChunk struct {
	Hash  string `bson:"hash"         json:"hash"         binding:"required"`
	Size  int64  `bson:"size"         json:"size"         binding:"required"`
	Index int    `bson:"index"        json:"index"        binding:"required"`
}

const (
	token_length = 10
	mgo_host     = "localhost"
	mgo_db       = "Ludicrous-MV"
	mgo_col      = "Files"
)

func randstr(length int) string {

	const alphanum = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
	var bytes = make([]byte, length)

	rand.Read(bytes)

	for i, b := range bytes {
		bytes[i] = alphanum[b%byte(len(alphanum))]
	}

	return string(bytes)

}

func main() {

	host := flag.String("host", "127.0.0.1", "")
	port := flag.String("port", "5688", "")

	flag.Parse()

	session, err := mgo.Dial(mgo_host)

	if err != nil {
		panic(err)
	}

	defer session.Close()

	session.SetMode(mgo.Monotonic, true)

	c := session.DB(mgo_db).C(mgo_col)

	m := martini.Classic()
	m.Use(render.Renderer())

	m.Get("/files/", func(r render.Render) {

		var lmv_files []LMVFile

		err := c.Find(bson.M{}).All(&lmv_files)

		if err != nil {
			log.Fatal(err)
		}

		r.JSON(200, lmv_files)

	})

	m.Get("/files/:token/", func(params martini.Params, r render.Render) {

		n, err := c.Find(bson.M{"token": params["token"]}).Count()

		if err != nil {
			log.Fatal(err)
		}

		if n != 1 {
			r.JSON(404, "")
		} else {
			var lmv_file LMVFile

			err = c.Find(bson.M{"token": params["token"]}).One(&lmv_file)

			if err != nil {
				log.Fatal(err)
			}

			r.JSON(200, lmv_file)
		}

	})

	m.Post("/files/", binding.Bind(LMVFile{}), func(r render.Render, lmv_file LMVFile) {

		token := randstr(token_length)

		for {
			n, err := c.Find(bson.M{"token": token}).Count()

			if err != nil {
				log.Fatal(err)
			}

			if n > 0 {
				token = randstr(token_length)
			} else {
				break
			}
		}

		lmv_file.Token = token

		err := c.Insert(lmv_file)

		if err != nil {
			log.Fatal(err)
		}

		r.JSON(200, map[string]interface{}{"token": token})

	})

	log.Fatal(http.ListenAndServe(*host+":"+*port, m))
}
<commit_msg>Provide verbose, coloured logs with logrus<commit_after>package main

import (
	"crypto/rand"
	"flag"
	"net/http"

	"github.com/Sirupsen/logrus"
	"github.com/go-martini/martini"
	"github.com/martini-contrib/binding"
	"github.com/martini-contrib/render"
	"labix.org/v2/mgo"
	"labix.org/v2/mgo/bson"
)

type LMVFile struct {
	Size      int64      `bson:"size"         json:"size"         binding:"required"`
	Name      string     `bson:"name"         json:"name"         binding:"required"`
	Algorithm string     `bson:"algorithm"    json:"algorithm"    binding:"required"`
	Chunks    []LMVChunk `bson:"chunks"       json:"chunks"       binding:"required"`
	Tar       bool       `bson:"tar"          json:"tar"`
	Token     string     `bson:"token"        json:"token"`
}

type LMVChunk struct {
	Hash  string `bson:"hash"         json:"hash"         binding:"required"`
	Size  int64  `bson:"size"         json:"size"         binding:"required"`
	Index int    `bson:"index"        json:"index"        binding:"required"`
}

const (
	token_length = 10
	mgo_host     = "localhost"
	mgo_db       = "Ludicrous-MV"
	mgo_col      = "Files"
)

var log = logrus.New()

func randstr(length int) string {

	const alphanum = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
	var bytes = make([]byte, length)

	rand.Read(bytes)

	for i, b := range bytes {
		bytes[i] = alphanum[b%byte(len(alphanum))]
	}

	return string(bytes)

}

func init() {
	log.Formatter = new(logrus.TextFormatter)
}

func main() {

	host := flag.String("host", "127.0.0.1", "")
	port := flag.String("port", "5688", "")

	flag.Parse()

	session, err := mgo.Dial(mgo_host)

	if err != nil {
		panic(err)
	}

	defer session.Close()

	session.SetMode(mgo.Monotonic, true)

	c := session.DB(mgo_db).C(mgo_col)

	m := martini.Classic()
	m.Use(render.Renderer())

	m.Get("/files/", func(r render.Render) {

		var lmv_files []LMVFile

		err := c.Find(bson.M{}).All(&lmv_files)

		if err != nil {
			log.Fatal(err)
		}

		r.JSON(200, lmv_files)

	})

	m.Get("/files/:token/", func(params martini.Params, r render.Render) {

		n, err := c.Find(bson.M{"token": params["token"]}).Count()

		if err != nil {
			log.Fatal(err)
		}

		if n != 1 {
			log.WithFields(logrus.Fields{
				"token": params["token"],
			}).Error("Token not found.")

			r.JSON(404, "")
		} else {
			var lmv_file LMVFile

			err = c.Find(bson.M{"token": params["token"]}).One(&lmv_file)

			if err != nil {
				log.Fatal(err)
			}

			log.WithFields(logrus.Fields{
				"token": params["token"],
			}).Info("Retrieved existing token.")

			r.JSON(200, lmv_file)
		}

	})

	m.Post("/files/", binding.Bind(LMVFile{}), func(r render.Render, lmv_file LMVFile) {

		token := randstr(token_length)

		for {
			n, err := c.Find(bson.M{"token": token}).Count()

			if err != nil {
				log.Fatal(err)
			}

			if n > 0 {
				token = randstr(token_length)
			} else {
				break
			}
		}

		lmv_file.Token = token

		err := c.Insert(lmv_file)

		if err != nil {
			log.Fatal(err)
		}

		r.JSON(200, map[string]interface{}{"token": token})

		log.WithFields(logrus.Fields{
			"token": token,
		}).Info("Inserted new file.")

	})

	log.Fatal(http.ListenAndServe(*host+":"+*port, m))
}
<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"encoding/binary"
	"flag"
	"fmt"
	"github.com/marcelki/tftp/netascii"
	"io"
	"log"
	"net"
	"os"
	"time"
)

const (
	TRIES = 3
)

var (
	port = flag.String("port", ":69", "The port to listen on")
	dir  = flag.String("dir", "", "The directory to serve")
)

func main() {
	flag.Parse()

	log.Fatalln(ListenAndServe(*port))
}

type session struct {
	addr net.Addr
	req  *request
}

func ListenAndServe(port string) error {
	pconn, err := net.ListenPacket("udp", port)
	if err != nil {
		return err
	}
	session := &session{}
	return session.Serve(pconn)
}

func (s *session) Serve(pconn net.PacketConn) error {
	defer pconn.Close()
	buf := make([]byte, 516)
	for {
		n, addr, err := pconn.ReadFrom(buf)
		if err != nil {
			if nErr, ok := err.(net.Error); ok && nErr.Temporary() {
				continue
			}
			return err
		}
		request, err := parseRequest(buf[:n])
		if err != nil {
			log.Printf("Request is not valid: %v.", err)
			continue
		}
		s.req = request
		s.addr = addr

		switch s.req.opcode {
		case RRQ:
			if *dir != "" {
				s.req.filename = *dir + "/" + s.req.filename
			}
			go s.ReadRequest()
		case WRQ:
			if *dir != "" {
				s.req.filename = *dir + "/" + s.req.filename
			}
			go s.WriteRequest()
		default:
			// TODO: received packet is incorrect
		}
	}
}

func (s *session) ReadRequest() {
	addr := s.addr
	conn, err := net.Dial("udp", addr.String())
	if err != nil {
		log.Printf("RRQ: Could not connect to %s: %s\n", addr.String(), err)
		return
	}
	defer conn.Close()

	if exists := fileExists(s.req.filename); !exists {
		err := s.sendError(conn, uint16(1), "File not found")
		if err != nil {
			log.Printf("RRQ: Error sending error packet to %v.\n", addr)
		}
		log.Printf("RRQ: Requested file %v does not exist\n", s.req.filename)
		return
	}
	fd, err := os.Open(s.req.filename)
	if err != nil {
		err = s.sendError(conn, uint16(0), "Not defined error: Could not open the file descriptor")
		if err != nil {
			log.Printf("RRQ: Error sending error packet to %v.\n", addr)
		}
		log.Printf("RRQ: Could not open the %s file descriptor for reading: %s \n", s.req.filename, err)
		return

	}
	// initial ack id
	id := uint16(1)
	// check if we get a performance gain, when not reusing the slice/array
	data := make([]byte, 512)
	for {
		n, err := io.ReadFull(fd, data)
		if err != nil && err != io.ErrUnexpectedEOF {
			return
		}
		err = s.sendData(conn, id, data[:n])
		if err != nil {
			// TOOD: logging
			return
		}
		id++
	}
}

func (s *session) WriteRequest() {
	addr := s.addr
	conn, err := net.Dial("udp", addr.String())
	if err != nil {
		log.Printf("WRQ: Could not connect to %s: %s\n", addr.String(), err)
		return
	}
	defer conn.Close()

	if exists := fileExists(s.req.filename); exists {
		err := s.sendError(conn, uint16(6), "File already exists.")
		if err != nil {
			log.Printf("WRQ: Error sending error packet to %v.\n", addr)
		}
		log.Printf("WRQ: File %v does already exist\n", s.req.filename)
		return
	}

	// TODO: use different permission for the file
	fd, err := os.OpenFile(s.req.filename, os.O_CREATE|os.O_WRONLY, 0777)
	if err != nil {
		err = s.sendError(conn, uint16(0), "Not defined error: Could not open the file descriptor")
		if err != nil {
			log.Printf("WRQ: Error sending error packet to %v.\n", addr)
		}
		log.Printf("WRQ: Could not open the %s file descriptor: %s \n", s.req.filename, err)
		return
	}
	defer fd.Close()

	bw := bufio.NewWriter(fd)
	id := uint16(0)
	for {
		// TODO: what happens when the client sends an error
		// we will never flush the writer ?!
		data, err := s.sendAck(conn, id, false)
		if err != nil {
			log.Printf("WRQ: Error sending ack packet to %v.\n", addr)
			return
		}
		id++
		if s.req.mode == "octet" {
			_, err = bw.Write(data)
		} else if s.req.mode == "netascii" {
			_, err = netascii.WriteTo(data, bw)
		} else {
			// TODO: logging
			fmt.Println("Mode not implemented yet!")
			return
		}
		if err != nil {
			log.Printf("WRQ: %v", err)
			return
		}
		if len(data) < 512 {
			s.sendAck(conn, id, true)
			bw.Flush()
			return
		}
	}
}

// sendAck sends a ack packet and returns the next data packet
// TODO: write a new routine to handle the write request, it works for now, but it isnt simple and elegant
func (s *session) sendAck(conn net.Conn, id uint16, last bool) (data []byte, err error) {
	p := ackPacket(id)
Tx:
	for try := 0; try < TRIES; try++ {
		conn.Write(p)
		conn.SetReadDeadline(time.Now().Add(time.Second))

		// TODO: search for a better alternative handling the last packet / termination of the session
		if last {
			return nil, err
		}

		recv := make([]byte, 516)
		for {
			n, err := conn.Read(recv)
			if err != nil {
				if nErr, ok := err.(net.Error); ok && nErr.Timeout() {
					continue Tx
				}
				return nil, err
			}
			opcode, remaining, ok := parsePacket(recv[:n])
			if !ok {
				continue
			}
			//blockid := remaining[:2]
			data = remaining[2:]
			switch opcode {
			case DATA:
				return data, nil
			case ERR:
				// TOOD: do something when error send
			}
		}
	}
	return nil, fmt.Errorf("timed outw waiting for the next data packet\n")
}

func (s *session) sendData(conn net.Conn, id uint16, data []byte) error {
	p := dataPacket(id, data)
Tx:
	for try := 0; try < TRIES; try++ {
		conn.Write(p)
		conn.SetReadDeadline(time.Now().Add(time.Second))

		// TODO: consider another slice length?!
		recv := make([]byte, 516)
		for {
			n, err := conn.Read(recv)
			if err != nil {
				if nErr, ok := err.(net.Error); ok && nErr.Timeout() {
					continue Tx
				}
				return err
			}
			opcode, remaining, ok := parsePacket(recv[:n])
			if !ok {
				continue
			}
			switch opcode {
			case ACK:
				recvid := binary.BigEndian.Uint16(remaining)
				if recvid == id {
					return nil
				}
				// TODO: what should we do, if the client sends a wrong id?
				log.Printf("RRQ: Received wrong block id (%v) for packet", recvid)
			case ERR:
				// TODO: client aborted the session
			}
		}
	}
	return fmt.Errorf("timed out waiting for ack")
}

func (s *session) sendError(conn net.Conn, errCode uint16, errMsg string) error {
	p := errorPacket(errCode, errMsg)
	_, err := conn.Write(p)
	if err != nil {
		return err
	}
	return nil
}

func fileExists(fname string) bool {
	if _, err := os.Stat(fname); os.IsNotExist(err) {
		return false
	}
	return true
}

func Debug(msg string, v ...interface{}) {
	fmt.Printf("DEBUG: %v\nValues: %v\n", msg, v)
}
<commit_msg>Check if disk is full or allocation exceeded when performing a write request.<commit_after>package main

import (
	"bufio"
	"encoding/binary"
	"flag"
	"fmt"
	"github.com/marcelki/tftp/netascii"
	"io"
	"log"
	"net"
	"os"
	"syscall"
	"time"
)

const (
	TRIES = 3
)

var (
	port = flag.String("port", ":69", "The port to listen on")
	dir  = flag.String("dir", "", "The directory to serve")
)

func main() {
	flag.Parse()

	log.Fatalln(ListenAndServe(*port))
}

type session struct {
	addr net.Addr
	req  *request
}

func ListenAndServe(port string) error {
	pconn, err := net.ListenPacket("udp", port)
	if err != nil {
		return err
	}
	session := &session{}
	return session.Serve(pconn)
}

func (s *session) Serve(pconn net.PacketConn) error {
	defer pconn.Close()
	buf := make([]byte, 516)
	for {
		n, addr, err := pconn.ReadFrom(buf)
		if err != nil {
			if nErr, ok := err.(net.Error); ok && nErr.Temporary() {
				continue
			}
			return err
		}
		request, err := parseRequest(buf[:n])
		if err != nil {
			log.Printf("Request is not valid: %v.", err)
			continue
		}
		s.req = request
		s.addr = addr

		switch s.req.opcode {
		case RRQ:
			if *dir != "" {
				s.req.filename = *dir + "/" + s.req.filename
			}
			go s.ReadRequest()
		case WRQ:
			if *dir != "" {
				s.req.filename = *dir + "/" + s.req.filename
			}
			go s.WriteRequest()
		default:
			// TODO: received packet is incorrect
		}
	}
}

func (s *session) ReadRequest() {
	addr := s.addr
	conn, err := net.Dial("udp", addr.String())
	if err != nil {
		log.Printf("RRQ: Could not connect to %s: %s\n", addr.String(), err)
		return
	}
	defer conn.Close()

	if exists := fileExists(s.req.filename); !exists {
		err := s.sendError(conn, uint16(1), "File not found")
		if err != nil {
			log.Printf("RRQ: Error sending error packet to %v.\n", addr)
		}
		log.Printf("RRQ: Requested file %v does not exist\n", s.req.filename)
		return
	}
	fd, err := os.Open(s.req.filename)
	if err != nil {
		err = s.sendError(conn, uint16(0), "Not defined error: Could not open the file descriptor")
		if err != nil {
			log.Printf("RRQ: Error sending error packet to %v.\n", addr)
		}
		log.Printf("RRQ: Could not open the %s file descriptor for reading: %s \n", s.req.filename, err)
		return

	}
	// initial ack id
	id := uint16(1)
	// check if we get a performance gain, when not reusing the slice/array
	data := make([]byte, 512)
	for {
		n, err := io.ReadFull(fd, data)
		if err != nil && err != io.ErrUnexpectedEOF {
			return
		}
		err = s.sendData(conn, id, data[:n])
		if err != nil {
			// TOOD: logging
			return
		}
		id++
	}
}

func (s *session) WriteRequest() {
	addr := s.addr
	conn, err := net.Dial("udp", addr.String())
	if err != nil {
		log.Printf("WRQ: Could not connect to %s: %s\n", addr.String(), err)
		return
	}
	defer conn.Close()

	if exists := fileExists(s.req.filename); exists {
		err := s.sendError(conn, uint16(6), "File already exists.")
		if err != nil {
			log.Printf("WRQ: Error sending error packet to %v.\n", addr)
		}
		log.Printf("WRQ: File %v does already exist\n", s.req.filename)
		return
	}

	// TODO: use different permission for the file
	fd, err := os.OpenFile(s.req.filename, os.O_CREATE|os.O_WRONLY, 0777)
	if err != nil {
		if e, ok := err.(*os.PathError); ok && e.Err == syscall.ENOSPC {
			err = s.sendError(conn, uint16(3), "Disk full or allocation exceeded")
			if err != nil {
				log.Printf("WRQ: Error sending error packet to %v.\n", addr)
			}
			log.Printf("WRQ: Not enough space to open the %s file descriptor\n", s.req.filename)
			return
		}
		err = s.sendError(conn, uint16(0), "Not defined error: Could not open the file descriptor")
		if err != nil {
			log.Printf("WRQ: Error sending error packet to %v.\n", addr)
		}
		log.Printf("WRQ: Could not open the %s file descriptor: %s \n", s.req.filename, err)
		return
	}
	defer fd.Close()

	bw := bufio.NewWriter(fd)
	id := uint16(0)
	for {
		// TODO: what happens when the client sends an error
		// we will never flush the writer ?!
		data, err := s.sendAck(conn, id, false)
		if err != nil {
			log.Printf("WRQ: Error sending ack packet to %v.\n", addr)
			return
		}
		id++
		if s.req.mode == "octet" {
			_, err = bw.Write(data)
		} else if s.req.mode == "netascii" {
			_, err = netascii.WriteTo(data, bw)
		} else {
			// TODO: logging
			fmt.Println("Mode not implemented yet!")
			return
		}
		if err != nil {
			log.Printf("WRQ: %v", err)
			return
		}
		if len(data) < 512 {
			s.sendAck(conn, id, true)
			bw.Flush()
			return
		}
	}
}

// sendAck sends a ack packet and returns the next data packet
// TODO: write a new routine to handle the write request, it works for now, but it isnt simple and elegant
func (s *session) sendAck(conn net.Conn, id uint16, last bool) (data []byte, err error) {
	p := ackPacket(id)
Tx:
	for try := 0; try < TRIES; try++ {
		conn.Write(p)
		conn.SetReadDeadline(time.Now().Add(time.Second))

		// TODO: search for a better alternative handling the last packet / termination of the session
		if last {
			return nil, err
		}

		recv := make([]byte, 516)
		for {
			n, err := conn.Read(recv)
			if err != nil {
				if nErr, ok := err.(net.Error); ok && nErr.Timeout() {
					continue Tx
				}
				return nil, err
			}
			opcode, remaining, ok := parsePacket(recv[:n])
			if !ok {
				continue
			}
			//blockid := remaining[:2]
			data = remaining[2:]
			switch opcode {
			case DATA:
				return data, nil
			case ERR:
				// TOOD: do something when error send
			}
		}
	}
	return nil, fmt.Errorf("timed outw waiting for the next data packet\n")
}

func (s *session) sendData(conn net.Conn, id uint16, data []byte) error {
	p := dataPacket(id, data)
Tx:
	for try := 0; try < TRIES; try++ {
		conn.Write(p)
		conn.SetReadDeadline(time.Now().Add(time.Second))

		// TODO: consider another slice length?!
		recv := make([]byte, 516)
		for {
			n, err := conn.Read(recv)
			if err != nil {
				if nErr, ok := err.(net.Error); ok && nErr.Timeout() {
					continue Tx
				}
				return err
			}
			opcode, remaining, ok := parsePacket(recv[:n])
			if !ok {
				continue
			}
			switch opcode {
			case ACK:
				recvid := binary.BigEndian.Uint16(remaining)
				if recvid == id {
					return nil
				}
				// TODO: what should we do, if the client sends a wrong id?
				log.Printf("RRQ: Received wrong block id (%v) for packet", recvid)
			case ERR:
				// TODO: client aborted the session
			}
		}
	}
	return fmt.Errorf("timed out waiting for ack")
}

func (s *session) sendError(conn net.Conn, errCode uint16, errMsg string) error {
	p := errorPacket(errCode, errMsg)
	_, err := conn.Write(p)
	if err != nil {
		return err
	}
	return nil
}

func fileExists(fname string) bool {
	if _, err := os.Stat(fname); os.IsNotExist(err) {
		return false
	}
	return true
}

func Debug(msg string, v ...interface{}) {
	fmt.Printf("DEBUG: %v\nValues: %v\n", msg, v)
}
<|endoftext|>


<commit_before>package operation

import (
	"bufio"
	"fmt"
	"io/ioutil"
	"log"
	"os/exec"

	json "github.com/bitly/go-simplejson"
	"github.com/iris-contrib/middleware/cors"
	"github.com/iris-contrib/middleware/logger"
	"github.com/iris-contrib/middleware/recovery"
	"github.com/kataras/go-template/html"
	"github.com/kataras/iris"
	"github.com/spf13/viper"
	"gopkg.in/mgo.v2"
	"gopkg.in/mgo.v2/bson"
)

var gSession *mgo.Session

func initDB(dbURL string) (*mgo.Session, error) {
	session, err := mgo.Dial(dbURL)
	if err != nil {
		return nil, fmt.Errorf("Cannot connect to mongo, %v", err)
	}
	return session, err
}

func getArgs(task *Task, frontTag, backTag string) []string {
	fabPath := viper.GetString("fabPath")
	if frontTag == "" {
		frontTag = task.Project.Front.Branch
	}
	if backTag == "" {
		backTag = task.Project.Backend.Branch
	}
	tmp, err := ioutil.TempDir("/tmp/", "operation")
	if err != nil {
		log.Panic(err)
	}
	deploy := fmt.Sprintf("deploy:tmp_path=%s,backend_url=%s,backend_branch=%s,front_url=%s,front_branch=%s,remote_path=%s,venv_path=%s,program=%s,workers=%s,worker_class=%s,bind=%s,user_group=%s,ext=%s,path=%s,include=%s,local_user=%s,local_password=%s,config_name=%s,nginx=%v",
		tmp, task.Project.Backend.Address, backTag, task.Project.Front.Address, frontTag, task.RemoteServer.Path, task.VenvPath, task.Gunicorn.Program, task.Gunicorn.Workers,
		task.Gunicorn.WorkerClass, task.Gunicorn.Bind, task.RemoteServer.Group, task.Supervisor.Extension, task.Supervisor.Path, task.Supervisor.Include, task.LocalServer.User, task.LocalServer.Password, task.ConfigName, task.Nginx)
	cmd := []string{
		"-f", fabPath, "-u", task.RemoteServer.User, "-p", task.RemoteServer.Password, "-H", task.RemoteServer.Host, deploy}
	return cmd
}

func updateTag(t *Task, frontTag, backTag string) {
	session := gSession.Clone()
	defer session.Close()
	var group Group
	coll := session.DB("operation").C("groups")
	err := coll.FindId(t.Group).One(&group)
	if err != nil {
		log.Fatal(err)
	}
	switch t.Project.Name {
	case "kelvin":
		if err := coll.UpdateId(group.ID, bson.M{"$set": bson.M{"front_tag": frontTag, "back_tag": backTag}}); err != nil {
			log.Fatal(err)
		}

	case "cms_plm":
		if err := coll.UpdateId(group.ID, bson.M{"$set": bson.M{"cms_tag": backTag}}); err != nil {
			log.Fatal(err)
		}
	}
}

// RunCommand ...
func RunCommand(c iris.WebsocketConnection, taskID, frontTag, backTag string) {
	session := gSession.Clone()
	defer session.Close()
	var task Task
	coll := session.DB("operation").C("tasks")
	err := coll.FindId(bson.ObjectIdHex(taskID)).One(&task)
	if err != nil {
		log.Fatal(err)
	}

	cmdArgs := getArgs(&task, frontTag, backTag)
	cmd := exec.Command("fab", cmdArgs...)
	stdout, err := cmd.StdoutPipe()
	if err != nil {
		log.Fatal(err)
	}
	cmd.Stderr = cmd.Stdout
	scanner := bufio.NewScanner(stdout)
	ch := make(chan int)
	go func() {
		for scanner.Scan() {
			if err := c.EmitMessage(scanner.Bytes()); err != nil {
				log.Panic("Emit error.\n", err)
			}
		}
		ch <- 0
	}()
	if err := cmd.Start(); err != nil {
		log.Panic("Exec error.\n", err)
	}
	if err := cmd.Wait(); err != nil {
		log.Panic("Finish error.\n", err)
	}
	updateTag(&task, frontTag, backTag)
	<-ch
}

// CreateApp ...
func CreateApp() *iris.Framework {
	var err error
	viper.AddConfigPath("/Users/sunyu/workspace/goprojects/src/github.com/syfun/operation")
	viper.AddConfigPath("D:/Workspace/gowork/src/github.com/syfun/operation")
	viper.AddConfigPath("/opt/operation")
	viper.SetConfigName("config")
	viper.SetConfigType("json")
	if err = viper.ReadInConfig(); err != nil {
		log.Panic(fmt.Errorf("Fatal error config file: %s \n", err))
	}
	gSession, err = initDB(viper.GetString("mongoURL"))
	if err != nil {
		log.Fatal(err)
	}

	app := iris.New()
	fmt.Println("##########")
	fmt.Println("templtePath", viper.GetString("templatePath"))
	fmt.Println("fabPath", viper.GetString("fabPath"))
	app.UseTemplate(html.New()).Directory(viper.GetString("templatePath"), ".html")
	app.Static("/static", viper.GetString("staticPath"), 1)

	app.Use(recovery.Handler)

	crs := cors.New(cors.Options{
		AllowedOrigins: []string{"*"},
		AllowedMethods: []string{"GET", "PUT", "POST", "DELETE"},
	})
	app.Use(crs)

	errorLogger := logger.New(logger.Config{Status: true, IP: true, Method: true, Path: true})
	app.Use(errorLogger)

	app.Get("/", func(c *iris.Context) {
		c.MustRender("index.html", nil)
	})
	app.Post("/api/v1/tasks", createTask)
	app.Get("/api/v1/tasks", queryTask)
	//app.Put("/api/v1/tasks/:taskID", updateTask)
	//app.Delete("/api/v1/tasks/:taskID", deleteTask)
	app.Get("/api/v1/groups", getGroups)

	app.Config.Websocket.Endpoint = "/ws"
	app.Websocket.OnConnection(func(c iris.WebsocketConnection) {
		fmt.Println("Connected.")
		c.OnMessage(func(message []byte) {
			fmt.Println(string(message))
			js, _ := json.NewJson(message)
			msgType, err := js.Get("type").String()
			if err != nil {
				log.Panic(err)
			}
			if msgType == "deploy" {
				taskID, err := js.Get("taskID").String()
				if err != nil {
					log.Println(err)
				}
				frontTag, err := js.Get("frontTag").String()
				if err != nil {
					log.Println(err)
				}
				backTag, err := js.Get("backTag").String()
				if err != nil {
					log.Println(err)
				}
				RunCommand(c, taskID, frontTag, backTag)
				log.Println("Deploy Over.")
				if err := c.EmitMessage([]byte("Deploy Over.")); err != nil {
					log.Panic("Over error.\n", err)
				}
			}
		})
		if err := c.Disconnect(); err != nil {
			log.Panic("Disconnect error.\n", err)
		}

	})
	return app
}
<commit_msg>add  detail error info abount run command<commit_after>package operation

import (
	"bufio"
	"fmt"
	"io/ioutil"
	"log"
	"os/exec"

	json "github.com/bitly/go-simplejson"
	"github.com/iris-contrib/middleware/cors"
	"github.com/iris-contrib/middleware/logger"
	"github.com/iris-contrib/middleware/recovery"
	"github.com/kataras/go-template/html"
	"github.com/kataras/iris"
	"github.com/spf13/viper"
	"gopkg.in/mgo.v2"
	"gopkg.in/mgo.v2/bson"
)

var gSession *mgo.Session

func initDB(dbURL string) (*mgo.Session, error) {
	session, err := mgo.Dial(dbURL)
	if err != nil {
		return nil, fmt.Errorf("Cannot connect to mongo, %v", err)
	}
	return session, err
}

func getArgs(task *Task, frontTag, backTag string) []string {
	fabPath := viper.GetString("fabPath")
	if frontTag == "" {
		frontTag = task.Project.Front.Branch
	}
	if backTag == "" {
		backTag = task.Project.Backend.Branch
	}
	tmp, err := ioutil.TempDir("/tmp/", "operation")
	if err != nil {
		log.Panic(err)
	}
	deploy := fmt.Sprintf("deploy:tmp_path=%s,backend_url=%s,backend_branch=%s,front_url=%s,front_branch=%s,remote_path=%s,venv_path=%s,program=%s,workers=%s,worker_class=%s,bind=%s,user_group=%s,ext=%s,path=%s,include=%s,local_user=%s,local_password=%s,config_name=%s,nginx=%v",
		tmp, task.Project.Backend.Address, backTag, task.Project.Front.Address, frontTag, task.RemoteServer.Path, task.VenvPath, task.Gunicorn.Program, task.Gunicorn.Workers,
		task.Gunicorn.WorkerClass, task.Gunicorn.Bind, task.RemoteServer.Group, task.Supervisor.Extension, task.Supervisor.Path, task.Supervisor.Include, task.LocalServer.User, task.LocalServer.Password, task.ConfigName, task.Nginx)
	cmd := []string{
		"-f", fabPath, "-u", task.RemoteServer.User, "-p", task.RemoteServer.Password, "-H", task.RemoteServer.Host, deploy}
	return cmd
}

func updateTag(t *Task, frontTag, backTag string) {
	session := gSession.Clone()
	defer session.Close()
	var group Group
	coll := session.DB("operation").C("groups")
	err := coll.FindId(t.Group).One(&group)
	if err != nil {
		log.Fatal(err)
	}
	switch t.Project.Name {
	case "kelvin":
		if err := coll.UpdateId(group.ID, bson.M{"$set": bson.M{"front_tag": frontTag, "back_tag": backTag}}); err != nil {
			log.Fatal(err)
		}

	case "cms_plm":
		if err := coll.UpdateId(group.ID, bson.M{"$set": bson.M{"cms_tag": backTag}}); err != nil {
			log.Fatal(err)
		}
	}
}

// RunCommand ...
func RunCommand(c iris.WebsocketConnection, taskID, frontTag, backTag string) error {
	session := gSession.Clone()
	defer session.Close()
	var task Task
	coll := session.DB("operation").C("tasks")
	err := coll.FindId(bson.ObjectIdHex(taskID)).One(&task)
	if err != nil {
		return fmt.Errorf("Cannot get task from mongo, %v", err)
	}

	cmdArgs := getArgs(&task, frontTag, backTag)
	cmd := exec.Command("fab", cmdArgs...)
	stdout, err := cmd.StdoutPipe()
	if err != nil {
		return fmt.Errorf("Cannot set stdout pipe, %v", err)
	}
	cmd.Stderr = cmd.Stdout
	scanner := bufio.NewScanner(stdout)
	ch := make(chan int)
	go func() {
		for scanner.Scan() {
			if err := c.EmitMessage(scanner.Bytes()); err != nil {
				log.Panic("Emit error.\n", err)
			}
		}
		ch <- 0
	}()
	if err := cmd.Start(); err != nil {
		return fmt.Errorf("Cannot start cmd, %v", err)
	}
	if err := cmd.Wait(); err != nil {
		return fmt.Errorf("Wait error, %v", err)
	}
	updateTag(&task, frontTag, backTag)
	<-ch
	return nil
}

// CreateApp ...
func CreateApp() *iris.Framework {
	var err error
	viper.AddConfigPath("/Users/sunyu/workspace/goprojects/src/github.com/syfun/operation")
	viper.AddConfigPath("D:/Workspace/gowork/src/github.com/syfun/operation")
	viper.AddConfigPath("/opt/operation")
	viper.SetConfigName("config")
	viper.SetConfigType("json")
	if err = viper.ReadInConfig(); err != nil {
		log.Panic(fmt.Errorf("Fatal error config file: %s \n", err))
	}
	gSession, err = initDB(viper.GetString("mongoURL"))
	if err != nil {
		log.Fatal(err)
	}

	app := iris.New()
	fmt.Println("##########")
	fmt.Println("templtePath", viper.GetString("templatePath"))
	fmt.Println("fabPath", viper.GetString("fabPath"))
	app.UseTemplate(html.New()).Directory(viper.GetString("templatePath"), ".html")
	app.Static("/static", viper.GetString("staticPath"), 1)

	app.Use(recovery.Handler)

	crs := cors.New(cors.Options{
		AllowedOrigins: []string{"*"},
		AllowedMethods: []string{"GET", "PUT", "POST", "DELETE"},
	})
	app.Use(crs)

	errorLogger := logger.New(logger.Config{Status: true, IP: true, Method: true, Path: true})
	app.Use(errorLogger)

	app.Get("/", func(c *iris.Context) {
		c.MustRender("index.html", nil)
	})
	app.Post("/api/v1/tasks", createTask)
	app.Get("/api/v1/tasks", queryTask)
	//app.Put("/api/v1/tasks/:taskID", updateTask)
	//app.Delete("/api/v1/tasks/:taskID", deleteTask)
	app.Get("/api/v1/groups", getGroups)

	app.Config.Websocket.Endpoint = "/ws"
	app.Websocket.OnConnection(func(c iris.WebsocketConnection) {
		fmt.Println("Connected.")
		c.OnMessage(func(message []byte) {
			defer c.Disconnect()
			fmt.Println(string(message))
			js, _ := json.NewJson(message)
			msgType, err := js.Get("type").String()
			if err != nil {
				log.Panic(err)
			}
			if msgType == "deploy" {
				taskID, err := js.Get("taskID").String()
				if err != nil {
					log.Panic(err)
				}
				frontTag, err := js.Get("frontTag").String()
				if err != nil {
					log.Panic(err)
				}
				backTag, err := js.Get("backTag").String()
				if err != nil {
					log.Panic(err)
				}
				if err := RunCommand(c, taskID, frontTag, backTag); err != nil {
					log.Panic(err)
				} else {
					log.Println("Deploy Over.")
					if err := c.EmitMessage([]byte("Deploy Over.")); err != nil {
						log.Panic("Over error.\n", err)
					}
				}
			}
		})
	})
	return app
}
<|endoftext|>


<commit_before>package core

import (
	"regexp"
	"testing"

	. "github.com/smartystreets/goconvey/convey"

	"github.com/supergiant/guber"
	"github.com/supergiant/supergiant/common"
	"github.com/supergiant/supergiant/core/mock"
)

var fakeRelease = &common.Release{
	InstanceCount:          1,
	TerminationGracePeriod: 666,
	Volumes: []*common.VolumeBlueprint{
		&common.VolumeBlueprint{
			Name: common.IDString("data"),
			Type: "gp2",
			Size: 20,
		},
	},
	Containers: []*common.ContainerBlueprint{
		&common.ContainerBlueprint{
			Image: "mysql",
			Ports: []*common.Port{
				&common.Port{
					Protocol: "TCP",
					Number:   3306,
				},
			},
			Mounts: []*common.Mount{
				&common.Mount{
					Volume: common.IDString("data"),
					Path:   "/var/lib/mysql",
				},
			},
		},
	},
}
var fakeReleaseJSON = `{
  "instance_count": 1,
	"termination_grace_period": 666,
  "volumes": [
    {
      "name": "data",
      "type": "gp2",
      "size": 20
    }
  ],
  "containers": [
    {
      "image": "mysql",
      "ports": [
        {
          "protocol": "TCP",
          "number": 3306
        }
      ],
      "mounts": [
        {
          "volume": "data",
          "path": "/var/lib/mysql"
        }
      ]
    }
  ]
}`

func TestReleaseList(t *testing.T) {
	Convey("Given a ReleaseCollection with 1 Release", t, func() {
		fakeEtcd := new(mock.FakeEtcd).ReturnValuesOnGet([]string{fakeReleaseJSON}, nil)
		core := newMockCore(fakeEtcd)

		// Kube Services are fetched on decorate()
		core.k8s = new(mock.FakeGuber).ReturnOnServiceGet(nil, new(guber.Error404)) // service does not exist

		core.AppsInterface = &AppCollection{core}
		core.EntrypointsInterface = &EntrypointCollection{core}
		core.ImageRegistriesInterface = &ImageRegistryCollection{core}
		core.dockerhub = core.ImageRegistries().New()
		// core.dockerhub.ImageReposInterface = new(FakeImageRepoCollection).ReturnOnGet(nil, &etcd.Error{Code: etcd.ErrorCodeKeyNotFound})

		app := core.Apps().New()
		app.Name = common.IDString("test")

		component := app.Components().New()
		component.Name = common.IDString("test")

		releases := component.Releases()

		Convey("When List() is called", func() {
			list, err := releases.List()

			Convey("The return value should be a ReleaseList with 1 Release", func() {
				So(err, ShouldBeNil)
				So(list.Items, ShouldHaveLength, 1)
				So(list.Items[0].Release, ShouldResemble, fakeRelease)
			})
		})
	})
}

func TestReleaseCreate(t *testing.T) {
	Convey("Given a ReleaseCollection and a new ReleaseResource", t, func() {
		etcdKeyCreated := ""
		updatedTargetReleaseTimestamp := ""

		fakeEtcd := new(mock.FakeEtcd)

		fakeEtcd.OnCreate(func(key string, val string) error {
			etcdKeyCreated = key
			return nil
		})

		fakeEtcd.OnUpdate(func(key string, val string) error {
			re := regexp.MustCompile(`"target_release_id":"([0-9]+)"`)
			updatedTargetReleaseTimestamp = re.FindStringSubmatch(val)[1]
			return nil
		})

		core := newMockCore(fakeEtcd)

		// Kube Services are fetched on decorate()
		core.k8s = new(mock.FakeGuber).ReturnOnServiceGet(nil, new(guber.Error404)) // service does not exist

		core.AppsInterface = &AppCollection{core}
		core.EntrypointsInterface = &EntrypointCollection{core}
		core.ImageRegistriesInterface = &ImageRegistryCollection{core}
		// core.dockerhub = core.ImageRegistries().New()

		app := core.Apps().New()
		app.Name = common.IDString("test")

		component := app.Components().New()
		component.Name = common.IDString("component-test")

		releases := component.Releases()

		fakeCopy := *fakeRelease
		release := releases.New()
		release.Release = &fakeCopy
		release.Meta = common.NewMeta() // because fakeCopy has no Meta

		Convey("When Create() is called", func() {
			err := releases.Create(release)

			Convey("The Release should be created in etcd", func() {
				So(err, ShouldBeNil)
				So(etcdKeyCreated, ShouldStartWith, "/supergiant/releases/test/component-test/201")
				So(release.Created, ShouldHaveSameTypeAs, new(common.Timestamp))
			})

			Convey("The Release should have a new Release Timestamp", func() {
				So(*release.Timestamp, ShouldStartWith, "201") // e.g. 20160519152252
			})

			Convey("Component should be updated with new TargetReleaseTimestamp", func() {
				So(updatedTargetReleaseTimestamp, ShouldEqual, *release.Timestamp)
			})
		})

		Convey("When Component already has a TargetReleaseTimestamp, and Create() is called", func() {
			component.TargetReleaseTimestamp = common.IDString("something")
			err := releases.Create(release)

			Convey("An error is returned", func() {
				So(err.Error(), ShouldEqual, "Component already has a target Release")
			})
		})
	})
}

func TestReleaseMergeCreate(t *testing.T) {
	Convey("Given a ReleaseCollection and a new ReleaseResource", t, func() {
		etcdKeyCreated := ""
		updatedTargetReleaseTimestamp := ""

		fakeEtcd := new(mock.FakeEtcd)

		fakeEtcd.OnCreate(func(key string, val string) error {
			etcdKeyCreated = key
			return nil
		})

		fakeEtcd.OnUpdate(func(key string, val string) error {
			re := regexp.MustCompile(`"target_release_id":"([0-9]+)"`)
			updatedTargetReleaseTimestamp = re.FindStringSubmatch(val)[1]
			return nil
		})

		core := newMockCore(fakeEtcd)

		// Kube Services are fetched on decorate()
		core.k8s = new(mock.FakeGuber).ReturnOnServiceGet(nil, new(guber.Error404)) // service does not exist

		core.AppsInterface = &AppCollection{core}
		core.EntrypointsInterface = &EntrypointCollection{core}
		core.ImageRegistriesInterface = &ImageRegistryCollection{core}

		app := core.Apps().New()
		app.Name = common.IDString("test")

		component := app.Components().New()
		component.Name = common.IDString("component-test")

		// releases := component.Releases()

		fakeCopy := *fakeRelease

		fakeCurrentRelease := &fakeCopy
		fakeCurrentRelease.Meta = common.NewMeta()

		fakeCurrentRelease.Timestamp = common.IDString("20160519152252")
		fakeCurrentRelease.Meta.Created = common.TimestampFromString("Tue, 12 Apr 2016 03:54:56 UTC")
		fakeCurrentRelease.Committed = true

		component.ReleasesInterface = new(FakeReleaseCollection).ReturnOnGet(fakeCurrentRelease, nil)
		component.CurrentReleaseTimestamp = fakeCurrentRelease.Timestamp

		// we do this and not component.Releases() because we mock it above
		releases := &ReleaseCollection{core, component}

		release := releases.New()
		release.TerminationGracePeriod = 420

		Convey("When MergeCreate() is called", func() {
			err := releases.MergeCreate(release)

			Convey("The Release should be created in etcd", func() {
				So(err, ShouldBeNil)
				So(etcdKeyCreated, ShouldStartWith, "/supergiant/releases/test/component-test/201")
			})

			Convey("The Release should have certain different Timestamp, Meta.Created, and Committed values", func() {
				So(*release.Timestamp, ShouldStartWith, "201") // e.g. 20160519152252
				So(*release.Timestamp, ShouldNotEqual, *fakeCurrentRelease.Timestamp)

				So(release.Created, ShouldHaveSameTypeAs, new(common.Timestamp))
				So(*release.Created, ShouldNotEqual, *fakeCurrentRelease.Created)

				So(release.Committed, ShouldBeFalse)
			})

			// TODO this really tests that release.collection.Create is called
			Convey("Component should be updated with new TargetReleaseTimestamp", func() {
				So(updatedTargetReleaseTimestamp, ShouldEqual, *release.Timestamp)
			})

			Convey("The Release should have values merged with CurrentRelease", func() {
				So(release.Containers[0].Image, ShouldEqual, "mysql") // old
				So(release.TerminationGracePeriod, ShouldEqual, 420)  // new
			})
		})

		Convey("When Component has no CurrentReleaseTimestamp, and MergeCreate() is called", func() {
			component.CurrentReleaseTimestamp = nil
			err := releases.MergeCreate(release)

			Convey("An error is returned", func() {
				So(err.Error(), ShouldEqual, "Attempting MergeCreate with no current Release")
			})
		})
	})
}
<commit_msg>Add test for ReleaseCollection Get<commit_after>package core

import (
	"regexp"
	"testing"

	. "github.com/smartystreets/goconvey/convey"

	"github.com/supergiant/guber"
	"github.com/supergiant/supergiant/common"
	"github.com/supergiant/supergiant/core/mock"
)

func TestReleaseList(t *testing.T) {
	Convey("Given a ReleaseCollection with 1 Release", t, func() {
		fakeEtcd := new(mock.FakeEtcd).ReturnValuesOnGet([]string{fakeReleaseJSON}, nil)
		core := newMockCore(fakeEtcd)

		// Kube Services are fetched on decorate()
		core.k8s = new(mock.FakeGuber).ReturnOnServiceGet(nil, new(guber.Error404)) // service does not exist

		core.AppsInterface = &AppCollection{core}
		core.EntrypointsInterface = &EntrypointCollection{core}
		core.ImageRegistriesInterface = &ImageRegistryCollection{core}
		core.dockerhub = core.ImageRegistries().New()
		// core.dockerhub.ImageReposInterface = new(FakeImageRepoCollection).ReturnOnGet(nil, &etcd.Error{Code: etcd.ErrorCodeKeyNotFound})

		app := core.Apps().New()
		app.Name = common.IDString("test")

		component := app.Components().New()
		component.Name = common.IDString("test")

		releases := component.Releases()

		Convey("When List() is called", func() {
			list, err := releases.List()

			Convey("The return value should be a ReleaseList with 1 Release", func() {
				So(err, ShouldBeNil)
				So(list.Items, ShouldHaveLength, 1)
				So(list.Items[0].Release, ShouldResemble, fakeRelease())
			})
		})
	})
}

func TestReleaseCreate(t *testing.T) {
	Convey("Given a ReleaseCollection and a new ReleaseResource", t, func() {
		etcdKeyCreated := ""
		updatedTargetReleaseTimestamp := ""

		fakeEtcd := new(mock.FakeEtcd)

		fakeEtcd.OnCreate(func(key string, val string) error {
			etcdKeyCreated = key
			return nil
		})

		fakeEtcd.OnUpdate(func(key string, val string) error {
			re := regexp.MustCompile(`"target_release_id":"([0-9]+)"`)
			updatedTargetReleaseTimestamp = re.FindStringSubmatch(val)[1]
			return nil
		})

		core := newMockCore(fakeEtcd)

		// Kube Services are fetched on decorate()
		core.k8s = new(mock.FakeGuber).ReturnOnServiceGet(nil, new(guber.Error404)) // service does not exist

		core.AppsInterface = &AppCollection{core}
		core.EntrypointsInterface = &EntrypointCollection{core}
		core.ImageRegistriesInterface = &ImageRegistryCollection{core}
		// core.dockerhub = core.ImageRegistries().New()

		app := core.Apps().New()
		app.Name = common.IDString("test")

		component := app.Components().New()
		component.Name = common.IDString("component-test")

		releases := component.Releases()

		release := releases.New()
		release.Release = fakeRelease()

		Convey("When Create() is called", func() {
			err := releases.Create(release)

			Convey("The Release should be created in etcd", func() {
				So(err, ShouldBeNil)
				So(etcdKeyCreated, ShouldStartWith, "/supergiant/releases/test/component-test/201")
				So(release.Created, ShouldHaveSameTypeAs, new(common.Timestamp))
			})

			Convey("The Release should have a new Release Timestamp", func() {
				So(*release.Timestamp, ShouldStartWith, "201") // e.g. 20160519152252
			})

			Convey("Component should be updated with new TargetReleaseTimestamp", func() {
				So(updatedTargetReleaseTimestamp, ShouldEqual, *release.Timestamp)
			})
		})

		Convey("When Component already has a TargetReleaseTimestamp, and Create() is called", func() {
			component.TargetReleaseTimestamp = common.IDString("something")
			err := releases.Create(release)

			Convey("An error is returned", func() {
				So(err.Error(), ShouldEqual, "Component already has a target Release")
			})
		})
	})
}

func TestReleaseMergeCreate(t *testing.T) {
	Convey("Given a ReleaseCollection and a new ReleaseResource", t, func() {
		etcdKeyCreated := ""
		updatedTargetReleaseTimestamp := ""

		fakeEtcd := new(mock.FakeEtcd)

		fakeEtcd.OnCreate(func(key string, val string) error {
			etcdKeyCreated = key
			return nil
		})

		fakeEtcd.OnUpdate(func(key string, val string) error {
			re := regexp.MustCompile(`"target_release_id":"([0-9]+)"`)
			updatedTargetReleaseTimestamp = re.FindStringSubmatch(val)[1]
			return nil
		})

		core := newMockCore(fakeEtcd)

		// Kube Services are fetched on decorate()
		core.k8s = new(mock.FakeGuber).ReturnOnServiceGet(nil, new(guber.Error404)) // service does not exist

		core.AppsInterface = &AppCollection{core}
		core.EntrypointsInterface = &EntrypointCollection{core}
		core.ImageRegistriesInterface = &ImageRegistryCollection{core}

		app := core.Apps().New()
		app.Name = common.IDString("test")

		component := app.Components().New()
		component.Name = common.IDString("component-test")

		fakeCurrentRelease := fakeRelease()

		fakeCurrentRelease.Timestamp = common.IDString("20160519152252")
		fakeCurrentRelease.Meta.Created = common.TimestampFromString("Tue, 12 Apr 2016 03:54:56 UTC")
		fakeCurrentRelease.Committed = true

		component.ReleasesInterface = new(FakeReleaseCollection).ReturnOnGet(fakeCurrentRelease, nil)
		component.CurrentReleaseTimestamp = fakeCurrentRelease.Timestamp

		// we do this and not component.Releases() because we mock it above
		releases := &ReleaseCollection{core, component}

		release := releases.New()
		release.TerminationGracePeriod = 420

		Convey("When MergeCreate() is called", func() {
			err := releases.MergeCreate(release)

			Convey("The Release should be created in etcd", func() {
				So(err, ShouldBeNil)
				So(etcdKeyCreated, ShouldStartWith, "/supergiant/releases/test/component-test/201")
			})

			Convey("The Release should have certain different Timestamp, Meta.Created, and Committed values", func() {
				So(*release.Timestamp, ShouldStartWith, "201") // e.g. 20160519152252
				So(*release.Timestamp, ShouldNotEqual, *fakeCurrentRelease.Timestamp)

				So(release.Created, ShouldHaveSameTypeAs, new(common.Timestamp))
				So(*release.Created, ShouldNotEqual, *fakeCurrentRelease.Created)

				So(release.Committed, ShouldBeFalse)
			})

			// TODO this really tests that release.collection.Create is called
			Convey("Component should be updated with new TargetReleaseTimestamp", func() {
				So(updatedTargetReleaseTimestamp, ShouldEqual, *release.Timestamp)
			})

			Convey("The Release should have values merged with CurrentRelease", func() {
				So(release.Containers[0].Image, ShouldEqual, "mysql") // old
				So(release.TerminationGracePeriod, ShouldEqual, 420)  // new
			})
		})

		Convey("When Component has no CurrentReleaseTimestamp, and MergeCreate() is called", func() {
			component.CurrentReleaseTimestamp = nil
			err := releases.MergeCreate(release)

			Convey("An error is returned", func() {
				So(err.Error(), ShouldEqual, "Attempting MergeCreate with no current Release")
			})
		})
	})
}

func TestReleaseGet(t *testing.T) {
	Convey("Given a ReleaseCollection with 1 Release", t, func() {
		fakeEtcd := new(mock.FakeEtcd).ReturnValueOnGet(fakeReleaseJSON, nil)
		core := newMockCore(fakeEtcd)

		// Kube Services are fetched on decorate()
		core.k8s = new(mock.FakeGuber).ReturnOnServiceGet(nil, new(guber.Error404)) // service does not exist

		core.AppsInterface = &AppCollection{core}
		core.EntrypointsInterface = &EntrypointCollection{core}
		core.ImageRegistriesInterface = &ImageRegistryCollection{core}
		core.dockerhub = core.ImageRegistries().New()
		// core.dockerhub.ImageReposInterface = new(FakeImageRepoCollection).ReturnOnGet(nil, &etcd.Error{Code: etcd.ErrorCodeKeyNotFound})

		app := core.Apps().New()
		app.Name = common.IDString("test")

		component := app.Components().New()
		component.Name = common.IDString("test")

		releases := component.Releases()

		Convey("When Get() is called", func() {
			release, err := releases.Get(common.IDString("buttman"))

			Convey("The return value should be the Release", func() {
				So(err, ShouldBeNil)
				So(release.Release, ShouldResemble, fakeRelease())
			})
		})
	})
}

//------------------------------------------------------------------------------
var fakeReleaseJSON = `{
	"created": null,
	"tags": {
		"test": "tag"
	},
	"instance_count": 1,
	"termination_grace_period": 666,
	"volumes": [
		{
			"name": "data",
			"type": "gp2",
			"size": 20
		}
	],
	"containers": [
		{
			"image": "mysql",
			"ports": [
				{
					"protocol": "TCP",
					"number": 3306
				}
			],
			"mounts": [
				{
					"volume": "data",
					"path": "/var/lib/mysql"
				}
			]
		}
	]
}`

// method so we don't have to worry about deep copying
func fakeRelease() *common.Release {
	return &common.Release{
		Meta: &common.Meta{
			Tags: map[string]string{
				"test": "tag",
			},
		},
		InstanceCount:          1,
		TerminationGracePeriod: 666,
		Volumes: []*common.VolumeBlueprint{
			&common.VolumeBlueprint{
				Name: common.IDString("data"),
				Type: "gp2",
				Size: 20,
			},
		},
		Containers: []*common.ContainerBlueprint{
			&common.ContainerBlueprint{
				Image: "mysql",
				Ports: []*common.Port{
					&common.Port{
						Protocol: "TCP",
						Number:   3306,
					},
				},
				Mounts: []*common.Mount{
					&common.Mount{
						Volume: common.IDString("data"),
						Path:   "/var/lib/mysql",
					},
				},
			},
		},
	}
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"crypto/hmac"
	"crypto/sha1"
	"encoding/json"
	"github.com/dimfeld/glog"
	"github.com/dimfeld/httptreemux"
	"net"
	"net/http"
	"strings"
)

type HookHandler func(http.ResponseWriter, *http.Request, map[string]string, *Hook)

func hookHandler(w http.ResponseWriter, r *http.Request, params map[string]string, hook *Hook) {
	githubEventType := r.Header.Get("X-GitHub-Event")

	if r.ContentLength > 16384 {
		// We should never get a request this large.
		w.WriteHeader(http.StatusRequestEntityTooLarge)
		return
	}

	buffer := bytes.Buffer{}
	buffer.ReadFrom(r.Body)
	r.Body.Close()

	if glog.V(2) {
		niceBuffer := &bytes.Buffer{}
		json.Indent(niceBuffer, buffer.Bytes(), "", "  ")
		glog.Infof("Hook %s received data %s\n",
			r.URL.Path, string(niceBuffer.Bytes()))
	}

	if hook.Secret != "" {
		secret := r.Header.Get("X-Hub-Signature")
		if !strings.HasPrefix(secret, "sha1=") {
			glog.Warningf("Request with no secret for hook %s from %s\n",
				r.URL.Path, r.RemoteAddr)
			w.WriteHeader(http.StatusForbidden)
			return
		}

		hash := hmac.New(sha1.New, []byte(hook.Secret))
		expected := hash.Sum(buffer.Bytes())
		if !hmac.Equal(expected, []byte(secret[5:])) {
			glog.Warningf("Request with bad secret for hook %s from %s\n",
				r.URL.Path, r.RemoteAddr)
			w.WriteHeader(http.StatusForbidden)
			return
		}
	}

	event, err := NewEvent(buffer.Bytes(), githubEventType)
	if err != nil {
		glog.Errorf("Error parinsg JSON for %s: %s", r.URL.Path, err)
	}
	event["urlparams"] = params
	hook.Execute(event)
}

func handlerWrapper(handler HookHandler, hook *Hook) httptreemux.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request, params map[string]string) {
		glog.Infoln("Called", r.URL.Path)
		handler(w, r, params, hook)
	}
}

func SetupServer(config *Config) (net.Listener, http.Handler) {
	var listener net.Listener = nil

	listener, err := net.Listen("tcp", config.ListenAddress)
	if err != nil {
		glog.Fatal("Could not listen on", config.ListenAddress)
	}

	if len(config.AcceptIp) != 0 {
		listenFilter := NewListenFilter(listener, WhiteList)
		for _, a := range config.AcceptIp {
			listenFilter.FilterAddr[a] = true
		}
		listener = listenFilter
	}

	router := httptreemux.New()

	for _, hook := range config.Hook {
		router.POST(hook.Url, handlerWrapper(hookHandler, hook))
	}

	return listener, router
}

func RunServer(config *Config) {
	listener, router := SetupServer(config)
	glog.Fatal(http.Serve(listener, router))
}
<commit_msg>Fix secret matching<commit_after>package main

import (
	"bytes"
	"crypto/hmac"
	"crypto/sha1"
	"encoding/hex"
	"encoding/json"
	"github.com/dimfeld/glog"
	"github.com/dimfeld/httptreemux"
	"net"
	"net/http"
	"strings"
)

type HookHandler func(http.ResponseWriter, *http.Request, map[string]string, *Hook)

func hookHandler(w http.ResponseWriter, r *http.Request, params map[string]string, hook *Hook) {
	githubEventType := r.Header.Get("X-GitHub-Event")

	if r.ContentLength > 16384 {
		// We should never get a request this large.
		w.WriteHeader(http.StatusRequestEntityTooLarge)
		return
	}

	buffer := bytes.Buffer{}
	buffer.ReadFrom(r.Body)
	r.Body.Close()

	if glog.V(2) {
		niceBuffer := &bytes.Buffer{}
		json.Indent(niceBuffer, buffer.Bytes(), "", "  ")
		glog.Infof("Hook %s received data %s\n",
			r.URL.Path, string(niceBuffer.Bytes()))
	}

	if hook.Secret != "" {
		secret := r.Header.Get("X-Hub-Signature")
		if !strings.HasPrefix(secret, "sha1=") {
			glog.Warningf("Request with no secret for hook %s from %s\n",
				r.URL.Path, r.RemoteAddr)
			w.WriteHeader(http.StatusForbidden)
			return
		}

		hash := hmac.New(sha1.New, []byte(hook.Secret))
		hash.Write(buffer.Bytes())
		expected := hash.Sum(nil)
		seen, err := hex.DecodeString(secret[5:])
		if err != nil || !hmac.Equal(expected, seen) {
			glog.Warningf("Request with bad secret for hook %s from %s\nExpected %s, saw %s",
				r.URL.Path, r.RemoteAddr, hex.EncodeToString(expected), secret)
			w.WriteHeader(http.StatusForbidden)
			return
		}
	}

	event, err := NewEvent(buffer.Bytes(), githubEventType)
	if err != nil {
		glog.Errorf("Error parinsg JSON for %s: %s", r.URL.Path, err)
	}
	event["urlparams"] = params
	hook.Execute(event)
}

func handlerWrapper(handler HookHandler, hook *Hook) httptreemux.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request, params map[string]string) {
		glog.Infoln("Called", r.URL.Path)
		handler(w, r, params, hook)
	}
}

func SetupServer(config *Config) (net.Listener, http.Handler) {
	var listener net.Listener = nil

	listener, err := net.Listen("tcp", config.ListenAddress)
	if err != nil {
		glog.Fatal("Could not listen on", config.ListenAddress)
	}

	if len(config.AcceptIp) != 0 {
		listenFilter := NewListenFilter(listener, WhiteList)
		for _, a := range config.AcceptIp {
			listenFilter.FilterAddr[a] = true
		}
		listener = listenFilter
	}

	router := httptreemux.New()

	for _, hook := range config.Hook {
		router.POST(hook.Url, handlerWrapper(hookHandler, hook))
	}

	return listener, router
}

func RunServer(config *Config) {
	listener, router := SetupServer(config)
	glog.Fatal(http.Serve(listener, router))
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"html/template"
	"net/http"
	"time"

	log "github.com/Sirupsen/logrus"
)

func runServer() {
	http.HandleFunc("/", status)
	http.HandleFunc("/example", showExampleConfig)
	http.HandleFunc("/config", showConfig)
	http.HandleFunc("/execute", execute)

	tcpSocket := ":8080"
	log.Infof("listening on %s", tcpSocket)
	http.ListenAndServe(tcpSocket, nil)
}

func status(w http.ResponseWriter, r *http.Request) {
	log.WithFields(log.Fields{
		"host":   r.RemoteAddr,
		"method": r.Method,
		"path":   r.URL.Path,
	}).Info()

	const tpl = `
<!DOCTYPE html>

		<meta charset="UTF-8">
		<title>{{.AppName}}</title>

photobomb

request

{{ .Request.RemoteAddr }}

routes

Path	Action

			{{range $path, $helpText := .Routes}}

			  {{ $path }}
			  {{ $helpText }}

			{{end}}

stats

			  uptime
			  {{ .Foo }}

configuration

{{ .Config }}

`

	t, err := template.New("webpage").Parse(tpl)
	routes := make(map[string]string)
	routes["/"] = "display this message"
	routes["/example"] = "display an example config"
	routes["/config"] = "display the current config"
	routes["/execute"] = "execute the current config"

	configJSON, err := json.Marshal(config)
	check(err)

	var simpleConfig SimpleRaid
	err = json.Unmarshal(configJSON, &simpleConfig)
	check(err)

	output, err := json.MarshalIndent(simpleConfig, "", "    ")
	check(err)

	data := struct {
		AppName string
		Routes  map[string]string
		Config  string
		Foo     time.Duration
		Request *http.Request
	}{
		AppName: appID,
		Routes:  routes,
		Config:  string(output),
		Foo:     config.Duration(),
		Request: r,
	}

	err = t.Execute(w, data)
	check(err)
}

func showExampleConfig(w http.ResponseWriter, r *http.Request) {
	output, err := json.MarshalIndent(ExampleConfig(), "", "    ")
	check(err)
	w.Write(output)
}

func showConfig(w http.ResponseWriter, r *http.Request) {
	configJSON, err := json.Marshal(config)
	check(err)

	var simpleConfig SimpleRaid
	err = json.Unmarshal(configJSON, &simpleConfig)
	check(err)

	output, err := json.MarshalIndent(simpleConfig, "", "    ")
	check(err)
	w.Write(output)
}

func execute(w http.ResponseWriter, r *http.Request) {
	raid := config
	summary := raid.Conduct()
	w.Header().Set("Content-Type", "application/json; charset=utf-8")
	w.Write(summary)
}
<commit_msg>set JSON content type<commit_after>package main

import (
	"encoding/json"
	"html/template"
	"net/http"
	"time"

	log "github.com/Sirupsen/logrus"
)

func runServer() {
	http.HandleFunc("/", status)
	http.HandleFunc("/example", showExampleConfig)
	http.HandleFunc("/config", showConfig)
	http.HandleFunc("/execute", execute)

	tcpSocket := ":8080"
	log.Infof("listening on %s", tcpSocket)
	http.ListenAndServe(tcpSocket, nil)
}

func status(w http.ResponseWriter, r *http.Request) {
	log.WithFields(log.Fields{
		"host":   r.RemoteAddr,
		"method": r.Method,
		"path":   r.URL.Path,
	}).Info()

	t, err := template.ParseFiles("index.html")
	check(err)

	routes := make(map[string]string)
	routes["/"] = "display this message"
	routes["/example"] = "display an example config"
	routes["/config"] = "display the current config"
	routes["/execute"] = "execute the current config"

	configJSON, err := json.Marshal(config)
	check(err)

	var simpleConfig SimpleRaid
	err = json.Unmarshal(configJSON, &simpleConfig)
	check(err)

	output, err := json.MarshalIndent(simpleConfig, "", "    ")
	check(err)

	data := struct {
		AppName string
		Routes  map[string]string
		Config  string
		Foo     time.Duration
		Request *http.Request
	}{
		AppName: appID,
		Routes:  routes,
		Config:  string(output),
		Foo:     config.Duration(),
		Request: r,
	}

	err = t.Execute(w, data)
	check(err)
}

func showExampleConfig(w http.ResponseWriter, r *http.Request) {
	output, err := json.MarshalIndent(ExampleConfig(), "", "    ")
	check(err)
	w.Header().Set("Content-Type", "application/json; charset=utf-8")
	w.Write(output)
}

func showConfig(w http.ResponseWriter, r *http.Request) {
	configJSON, err := json.Marshal(config)
	check(err)

	var simpleConfig SimpleRaid
	err = json.Unmarshal(configJSON, &simpleConfig)
	check(err)

	output, err := json.MarshalIndent(simpleConfig, "", "    ")
	check(err)
	w.Header().Set("Content-Type", "application/json; charset=utf-8")
	w.Write(output)
}

func execute(w http.ResponseWriter, r *http.Request) {
	raid := config
	summary := raid.Conduct()
	w.Header().Set("Content-Type", "application/json; charset=utf-8")
	w.Write(summary)
}
<|endoftext|>