<commit_before>package readline

import (
	"bufio"
	"container/list"
	"fmt"
	"os"
	"strings"
)

type hisItem struct {
	Source  []rune
	Version int64
	Tmp     []rune
}

func (h *hisItem) Clean() {
	h.Source = nil
	h.Tmp = nil
}

type opHistory struct {
	cfg        *Config
	history    *list.List
	historyVer int64
	current    *list.Element
	fd         *os.File
}

func newOpHistory(cfg *Config) (o *opHistory) {
	o = &opHistory{
		cfg:     cfg,
		history: list.New(),
	}
	return o
}

func (o *opHistory) Reset() {
	o.history = list.New()
	o.current = nil
}

func (o *opHistory) IsHistoryClosed() bool {
	return o.fd.Fd() == ^(uintptr(0))
}

func (o *opHistory) Init() {
	if o.IsHistoryClosed() {
		o.initHistory()
	}
}

func (o *opHistory) initHistory() {
	if o.cfg.HistoryFile != "" {
		o.historyUpdatePath(o.cfg.HistoryFile)
	}
}

// only called by newOpHistory
func (o *opHistory) historyUpdatePath(path string) {
	f, err := os.OpenFile(path, os.O_APPEND|os.O_CREATE|os.O_RDWR, 0666)
	if err != nil {
		return
	}
	o.fd = f
	r := bufio.NewReader(o.fd)
	total := 0
	for ; ; total++ {
		line, err := r.ReadString('\n')
		if err != nil {
			break
		}
		// ignore the empty line
		line = strings.TrimSpace(line)
		if len(line) == 0 {
			continue
		}
		o.Push([]rune(line))
		o.Compact()
	}
	if total > o.cfg.HistoryLimit {
		o.Rewrite()
	}
	o.historyVer++
	o.Push(nil)
	return
}

func (o *opHistory) Compact() {
	for o.history.Len() > o.cfg.HistoryLimit && o.history.Len() > 0 {
		o.history.Remove(o.history.Front())
	}
}

func (o *opHistory) Rewrite() {
	if o.cfg.HistoryFile == "" {
		return
	}

	tmpFile := o.cfg.HistoryFile + ".tmp"
	fd, err := os.OpenFile(tmpFile, os.O_CREATE|os.O_WRONLY|os.O_TRUNC|os.O_APPEND, 0666)
	if err != nil {
		return
	}

	buf := bufio.NewWriter(fd)
	for elem := o.history.Front(); elem != nil; elem = elem.Next() {
		buf.WriteString(string(elem.Value.(*hisItem).Source))
	}
	buf.Flush()

	// replace history file
	if err = os.Rename(tmpFile, o.cfg.HistoryFile); err != nil {
		fd.Close()
		return
	}

	if o.fd != nil {
		o.fd.Close()
	}
	// fd is write only, just satisfy what we need.
	o.fd = fd
}

func (o *opHistory) Close() {
	if o.fd != nil {
		o.fd.Close()
	}
}

func (o *opHistory) FindBck(isNewSearch bool, rs []rune, start int) (int, *list.Element) {
	for elem := o.current; elem != nil; elem = elem.Prev() {
		item := o.showItem(elem.Value)
		if isNewSearch {
			start += len(rs)
		}
		if elem == o.current {
			if len(item) >= start {
				item = item[:start]
			}
		}
		idx := runes.IndexAllBck(item, rs)
		if idx < 0 {
			continue
		}
		return idx, elem
	}
	return -1, nil
}

func (o *opHistory) FindFwd(isNewSearch bool, rs []rune, start int) (int, *list.Element) {
	for elem := o.current; elem != nil; elem = elem.Next() {
		item := o.showItem(elem.Value)
		if isNewSearch {
			start -= len(rs)
			if start < 0 {
				start = 0
			}
		}
		if elem == o.current {
			if len(item)-1 >= start {
				item = item[start:]
			} else {
				continue
			}
		}
		idx := runes.IndexAll(item, rs)
		if idx < 0 {
			continue
		}
		if elem == o.current {
			idx += start
		}
		return idx, elem
	}
	return -1, nil
}

func (o *opHistory) showItem(obj interface{}) []rune {
	item := obj.(*hisItem)
	if item.Version == o.historyVer {
		return item.Tmp
	}
	return item.Source
}

func (o *opHistory) Prev() []rune {
	if o.current == nil {
		return nil
	}
	current := o.current.Prev()
	if current == nil {
		return nil
	}
	o.current = current
	return runes.Copy(o.showItem(current.Value))
}

func (o *opHistory) Next() ([]rune, bool) {
	if o.current == nil {
		return nil, false
	}
	current := o.current.Next()
	if current == nil {
		return nil, false
	}

	o.current = current
	return runes.Copy(o.showItem(current.Value)), true
}

func (o *opHistory) debug() {
	Debug("-------")
	for item := o.history.Front(); item != nil; item = item.Next() {
		Debug(fmt.Sprintf("%+v", item.Value))
	}
}

// save history
func (o *opHistory) New(current []rune) (err error) {
	current = runes.Copy(current)

	// if just use last command without modify
	// just clean lastest history
	if back := o.history.Back(); back != nil {
		prev := back.Prev()
		if prev != nil {
			if runes.Equal(current, prev.Value.(*hisItem).Source) {
				o.current = o.history.Back()
				o.current.Value.(*hisItem).Clean()
				o.historyVer++
				return nil
			}
		}
	}

	if len(current) == 0 {
		o.current = o.history.Back()
		if o.current != nil {
			o.current.Value.(*hisItem).Clean()
			o.historyVer++
			return nil
		}
	}

	if o.current != o.history.Back() {
		// move history item to current command
		currentItem := o.current.Value.(*hisItem)
		// set current to last item
		o.current = o.history.Back()

		current = runes.Copy(currentItem.Tmp)
	}

	// err only can be a IO error, just report
	err = o.Update(current, true)

	// push a new one to commit current command
	o.historyVer++
	o.Push(nil)
	return
}

func (o *opHistory) Revert() {
	o.historyVer++
	o.current = o.history.Back()
}

func (o *opHistory) Update(s []rune, commit bool) (err error) {
	s = runes.Copy(s)
	if o.current == nil {
		o.Push(s)
		o.Compact()
		return
	}
	r := o.current.Value.(*hisItem)
	r.Version = o.historyVer
	if commit {
		r.Source = s
		if o.fd != nil {
			// just report the error
			_, err = o.fd.Write([]byte(string(r.Source) + "\n"))
		}
	} else {
		r.Tmp = append(r.Tmp[:0], s...)
	}
	o.current.Value = r
	o.Compact()
	return
}

func (o *opHistory) Push(s []rune) {
	s = runes.Copy(s)
	elem := o.history.PushBack(&hisItem{Source: s})
	o.current = elem
}
<commit_msg>added fd mutex to prevent data race on history updates with closing the history file (#66)<commit_after>package readline

import (
	"bufio"
	"container/list"
	"fmt"
	"os"
	"sync"
	"strings"
)

type hisItem struct {
	Source  []rune
	Version int64
	Tmp     []rune
}

func (h *hisItem) Clean() {
	h.Source = nil
	h.Tmp = nil
}

type opHistory struct {
	cfg        *Config
	history    *list.List
	historyVer int64
	current    *list.Element
	fd         *os.File
	fdLock	   sync.Mutex
}

func newOpHistory(cfg *Config) (o *opHistory) {
	o = &opHistory{
		cfg:     cfg,
		history: list.New(),
	}
	return o
}

func (o *opHistory) Reset() {
	o.history = list.New()
	o.current = nil
}

func (o *opHistory) IsHistoryClosed() bool {
	o.fdLock.Lock()
	defer o.fdLock.Unlock()
	return o.fd.Fd() == ^(uintptr(0))
}

func (o *opHistory) Init() {
	if o.IsHistoryClosed() {
		o.initHistory()
	}
}

func (o *opHistory) initHistory() {
	if o.cfg.HistoryFile != "" {
		o.historyUpdatePath(o.cfg.HistoryFile)
	}
}

// only called by newOpHistory
func (o *opHistory) historyUpdatePath(path string) {
	o.fdLock.Lock()
	defer o.fdLock.Unlock()
	f, err := os.OpenFile(path, os.O_APPEND|os.O_CREATE|os.O_RDWR, 0666)
	if err != nil {
		return
	}
	o.fd = f
	r := bufio.NewReader(o.fd)
	total := 0
	for ; ; total++ {
		line, err := r.ReadString('\n')
		if err != nil {
			break
		}
		// ignore the empty line
		line = strings.TrimSpace(line)
		if len(line) == 0 {
			continue
		}
		o.Push([]rune(line))
		o.Compact()
	}
	if total > o.cfg.HistoryLimit {
		o.Rewrite()
	}
	o.historyVer++
	o.Push(nil)
	return
}

func (o *opHistory) Compact() {
	for o.history.Len() > o.cfg.HistoryLimit && o.history.Len() > 0 {
		o.history.Remove(o.history.Front())
	}
}

func (o *opHistory) Rewrite() {
	o.fdLock.Lock()
	defer o.fdLock.Unlock()
	if o.cfg.HistoryFile == "" {
		return
	}

	tmpFile := o.cfg.HistoryFile + ".tmp"
	fd, err := os.OpenFile(tmpFile, os.O_CREATE|os.O_WRONLY|os.O_TRUNC|os.O_APPEND, 0666)
	if err != nil {
		return
	}

	buf := bufio.NewWriter(fd)
	for elem := o.history.Front(); elem != nil; elem = elem.Next() {
		buf.WriteString(string(elem.Value.(*hisItem).Source))
	}
	buf.Flush()

	// replace history file
	if err = os.Rename(tmpFile, o.cfg.HistoryFile); err != nil {
		fd.Close()
		return
	}

	if o.fd != nil {
		o.fd.Close()
	}
	// fd is write only, just satisfy what we need.
	o.fd = fd
}

func (o *opHistory) Close() {
	o.fdLock.Lock()
	defer o.fdLock.Unlock()
	if o.fd != nil {
		o.fd.Close()
	}
}

func (o *opHistory) FindBck(isNewSearch bool, rs []rune, start int) (int, *list.Element) {
	for elem := o.current; elem != nil; elem = elem.Prev() {
		item := o.showItem(elem.Value)
		if isNewSearch {
			start += len(rs)
		}
		if elem == o.current {
			if len(item) >= start {
				item = item[:start]
			}
		}
		idx := runes.IndexAllBck(item, rs)
		if idx < 0 {
			continue
		}
		return idx, elem
	}
	return -1, nil
}

func (o *opHistory) FindFwd(isNewSearch bool, rs []rune, start int) (int, *list.Element) {
	for elem := o.current; elem != nil; elem = elem.Next() {
		item := o.showItem(elem.Value)
		if isNewSearch {
			start -= len(rs)
			if start < 0 {
				start = 0
			}
		}
		if elem == o.current {
			if len(item)-1 >= start {
				item = item[start:]
			} else {
				continue
			}
		}
		idx := runes.IndexAll(item, rs)
		if idx < 0 {
			continue
		}
		if elem == o.current {
			idx += start
		}
		return idx, elem
	}
	return -1, nil
}

func (o *opHistory) showItem(obj interface{}) []rune {
	item := obj.(*hisItem)
	if item.Version == o.historyVer {
		return item.Tmp
	}
	return item.Source
}

func (o *opHistory) Prev() []rune {
	if o.current == nil {
		return nil
	}
	current := o.current.Prev()
	if current == nil {
		return nil
	}
	o.current = current
	return runes.Copy(o.showItem(current.Value))
}

func (o *opHistory) Next() ([]rune, bool) {
	if o.current == nil {
		return nil, false
	}
	current := o.current.Next()
	if current == nil {
		return nil, false
	}

	o.current = current
	return runes.Copy(o.showItem(current.Value)), true
}

func (o *opHistory) debug() {
	Debug("-------")
	for item := o.history.Front(); item != nil; item = item.Next() {
		Debug(fmt.Sprintf("%+v", item.Value))
	}
}

// save history
func (o *opHistory) New(current []rune) (err error) {
	current = runes.Copy(current)

	// if just use last command without modify
	// just clean lastest history
	if back := o.history.Back(); back != nil {
		prev := back.Prev()
		if prev != nil {
			if runes.Equal(current, prev.Value.(*hisItem).Source) {
				o.current = o.history.Back()
				o.current.Value.(*hisItem).Clean()
				o.historyVer++
				return nil
			}
		}
	}

	if len(current) == 0 {
		o.current = o.history.Back()
		if o.current != nil {
			o.current.Value.(*hisItem).Clean()
			o.historyVer++
			return nil
		}
	}

	if o.current != o.history.Back() {
		// move history item to current command
		currentItem := o.current.Value.(*hisItem)
		// set current to last item
		o.current = o.history.Back()

		current = runes.Copy(currentItem.Tmp)
	}

	// err only can be a IO error, just report
	err = o.Update(current, true)

	// push a new one to commit current command
	o.historyVer++
	o.Push(nil)
	return
}

func (o *opHistory) Revert() {
	o.historyVer++
	o.current = o.history.Back()
}

func (o *opHistory) Update(s []rune, commit bool) (err error) {
	o.fdLock.Lock()
	defer o.fdLock.Unlock()
	s = runes.Copy(s)
	if o.current == nil {
		o.Push(s)
		o.Compact()
		return
	}
	r := o.current.Value.(*hisItem)
	r.Version = o.historyVer
	if commit {
		r.Source = s
		if o.fd != nil {
			// just report the error
			_, err = o.fd.Write([]byte(string(r.Source) + "\n"))
		}
	} else {
		r.Tmp = append(r.Tmp[:0], s...)
	}
	o.current.Value = r
	o.Compact()
	return
}

func (o *opHistory) Push(s []rune) {
	s = runes.Copy(s)
	elem := o.history.PushBack(&hisItem{Source: s})
	o.current = elem
}
<|endoftext|>


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

package openweathermap

import (
	"encoding/json"
	"errors"
	"fmt"
	"net/http"
	"net/url"
	"strings"
)

// HistoricalParameters struct holds the (optional) fields to be
// supplied for historical data requests.
type HistoricalParameters struct {
	Start int64 // Data start (unix time, UTC time zone)
	End   int64 // Data end (unix time, UTC time zone)
	Cnt   int   // Amount of returned data (one per hour, can be used instead of Data end)
}

type Rain struct {
	ThreeH int `json:"3h"`
}

// WeatherHistory struct contains aggregate fields from the above
// structs.
type WeatherHistory struct {
	Main    Main      `json:"main"`
	Wind    Wind      `json:"wind"`
	Clouds  Clouds    `json:"clouds"`
	Weather []Weather `json:"weather"`
	Rain    Rain      `json:"rain"`
	Dt      int       `json:"dt"`
}

// HistoricalWeatherData struct is where the JSON is unmarshaled to
// when receiving data for a historical request.
type HistoricalWeatherData struct {
	Message  string           `json:"message"`
	Cod      int              `json:"cod"`
	CityData int              `json:"city_data"`
	CalcTime float64          `json:"calctime"`
	Cnt      int              `json:"cnt"`
	List     []WeatherHistory `json:"list"`
	Unit     string
}

// NewHistorical returns a new HistoricalWeatherData pointer with
//the supplied arguments.
func NewHistorical(unit string) (*HistoricalWeatherData, error) {
	unitChoice := strings.ToUpper(unit)
	if ValidDataUnit(unitChoice) {
		return &HistoricalWeatherData{Unit: unitChoice}, nil
	}
	return nil, errors.New("unit of measure not available")
}

// HistoryByName will return the history for the provided location
func (h *HistoricalWeatherData) HistoryByName(location string) error {
	var err error
	var response *http.Response
	response, err = http.Get(fmt.Sprintf(fmt.Sprintf(historyURL, "city?q=%s"), url.QueryEscape(location)))
	if err != nil {
		return err
	}
	defer response.Body.Close()
	if err = json.NewDecoder(response.Body).Decode(&h); err != nil {
		return err
	}
	return nil
}

// HistoryByID will return the history for the provided location ID
func (h *HistoricalWeatherData) HistoryByID(id int, hp ...*HistoricalParameters) error {
	if len(hp) > 0 {
		response, err := http.Get(fmt.Sprintf(fmt.Sprintf(historyURL, "city?id=%d&type=hour&start%d&end=%d&cnt=%d"), id, hp[0].Start, hp[0].End, hp[0].Cnt))
		if err != nil {
			return err
		}
		defer response.Body.Close()
		if err = json.NewDecoder(response.Body).Decode(&h); err != nil {
			return err
		}
	}
	response, err := http.Get(fmt.Sprintf(fmt.Sprintf(historyURL, "city?id=%d"), id))
	if err != nil {
		return err
	}
	defer response.Body.Close()
	if err = json.NewDecoder(response.Body).Decode(&h); err != nil {
		return err
	}
	return nil
}
<commit_msg>comment on exported struct for linter<commit_after>// Copyright 2015 Brian J. Downs
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package openweathermap

import (
	"encoding/json"
	"errors"
	"fmt"
	"net/http"
	"net/url"
	"strings"
)

// HistoricalParameters struct holds the (optional) fields to be
// supplied for historical data requests.
type HistoricalParameters struct {
	Start int64 // Data start (unix time, UTC time zone)
	End   int64 // Data end (unix time, UTC time zone)
	Cnt   int   // Amount of returned data (one per hour, can be used instead of Data end)
}

// Rain struct contains 3 hour data
type Rain struct {
	ThreeH int `json:"3h"`
}

// WeatherHistory struct contains aggregate fields from the above
// structs.
type WeatherHistory struct {
	Main    Main      `json:"main"`
	Wind    Wind      `json:"wind"`
	Clouds  Clouds    `json:"clouds"`
	Weather []Weather `json:"weather"`
	Rain    Rain      `json:"rain"`
	Dt      int       `json:"dt"`
}

// HistoricalWeatherData struct is where the JSON is unmarshaled to
// when receiving data for a historical request.
type HistoricalWeatherData struct {
	Message  string           `json:"message"`
	Cod      int              `json:"cod"`
	CityData int              `json:"city_data"`
	CalcTime float64          `json:"calctime"`
	Cnt      int              `json:"cnt"`
	List     []WeatherHistory `json:"list"`
	Unit     string
}

// NewHistorical returns a new HistoricalWeatherData pointer with
//the supplied arguments.
func NewHistorical(unit string) (*HistoricalWeatherData, error) {
	unitChoice := strings.ToUpper(unit)
	if ValidDataUnit(unitChoice) {
		return &HistoricalWeatherData{Unit: unitChoice}, nil
	}
	return nil, errors.New("unit of measure not available")
}

// HistoryByName will return the history for the provided location
func (h *HistoricalWeatherData) HistoryByName(location string) error {
	var err error
	var response *http.Response
	response, err = http.Get(fmt.Sprintf(fmt.Sprintf(historyURL, "city?q=%s"), url.QueryEscape(location)))
	if err != nil {
		return err
	}
	defer response.Body.Close()
	if err = json.NewDecoder(response.Body).Decode(&h); err != nil {
		return err
	}
	return nil
}

// HistoryByID will return the history for the provided location ID
func (h *HistoricalWeatherData) HistoryByID(id int, hp ...*HistoricalParameters) error {
	if len(hp) > 0 {
		response, err := http.Get(fmt.Sprintf(fmt.Sprintf(historyURL, "city?id=%d&type=hour&start%d&end=%d&cnt=%d"), id, hp[0].Start, hp[0].End, hp[0].Cnt))
		if err != nil {
			return err
		}
		defer response.Body.Close()
		if err = json.NewDecoder(response.Body).Decode(&h); err != nil {
			return err
		}
	}
	response, err := http.Get(fmt.Sprintf(fmt.Sprintf(historyURL, "city?id=%d"), id))
	if err != nil {
		return err
	}
	defer response.Body.Close()
	if err = json.NewDecoder(response.Body).Decode(&h); err != nil {
		return err
	}
	return nil
}
<|endoftext|>


<commit_before>package mego

import (
	"net/http"
	"strconv"
	"strings"
	"net/url"
	"mime/multipart"
	"mime"
	"github.com/google/uuid"
)

type sizer interface {
	Size() int64
}

// HttpCtx the mego context struct
type HttpCtx struct {
	req       *http.Request
	res       http.ResponseWriter
	routeData map[string]string
	ended     bool
	items     map[string]interface{}
	server    *Server
	area      *Area
}

// Request get the mego request
func (ctx *HttpCtx) Request() *http.Request {
	return ctx.req
}

// Response get the mego response
func (ctx *HttpCtx) Response() http.ResponseWriter {
	return ctx.res
}

// RouteString get the route parameter value as string by key
func (ctx *HttpCtx) RouteString(key string) string {
	if ctx.routeData == nil {
		return ""
	}
	return ctx.routeData[key]
}

// RouteInt get the route parameter value as int64 by key
func (ctx *HttpCtx) RouteInt(key string) int64 {
	var rawValue = ctx.RouteString(key)
	if len(rawValue) == 0 {
		return 0
	}
	value,err := strconv.ParseInt(rawValue, 0, 64)
	if err != nil {
		return 0
	}
	return value
}

// RouteUint get the route parameter value as uint64 by key
func (ctx *HttpCtx) RouteUint(key string) uint64 {
	var rawValue = ctx.RouteString(key)
	if len(rawValue) == 0 {
		return 0
	}
	value,err := strconv.ParseUint(rawValue, 0, 64)
	if err != nil {
		return 0
	}
	return value
}

// RouteFloat get the route parameter value as float by key
func (ctx *HttpCtx) RouteFloat(key string) float64 {
	var rawValue = ctx.RouteString(key)
	if len(rawValue) == 0 {
		return 0
	}
	value,err := strconv.ParseFloat(rawValue, 64)
	if err != nil {
		return 0
	}
	return value
}

// RouteBool get the route parameter value as boolean by key
func (ctx *HttpCtx) RouteBool(key string) bool {
	var rawValue = ctx.RouteString(key)
	if len(rawValue) == 0 || strings.ToLower(rawValue) == "false" || rawValue == "0"  {
		return false
	}
	return true
}

func (ctx *HttpCtx) PostFile(formName string) *UploadFile {
	f, h, err := ctx.Request().FormFile(formName)
	if err != nil {
		return &UploadFile{Error: err}
	}
	if f == nil {
		return nil
	}
	var size int64
	if s, ok := f.(sizer); ok {
		size = s.Size()
	} else {
		size = 0
	}
	return &UploadFile{FileName: h.Filename, Size: size, File: f, Header: h}
}

// SetItem add context data to mego context
func (ctx *HttpCtx) SetItem(key string, data interface{}) {
	if len(key) == 0 {
		return
	}
	if ctx.items == nil {
		ctx.items = make(map[string]interface{})
	}
	ctx.items[key] = data
}

// GetItem get the context data from mego context by key
func (ctx *HttpCtx) GetItem(key string) interface{} {
	if ctx.items == nil {
		return nil
	}
	return ctx.items[key]
}

// RemoveItem delete context item from mego context by key
func (ctx *HttpCtx) RemoveItem(key string) interface{} {
	if ctx.items == nil {
		return nil
	}
	data := ctx.items[key]
	delete(ctx.items, key)
	return data
}
func (ctx *HttpCtx) MapPath(path string) string {
	return ctx.server.MapRootPath(path)
}

// End end the mego context and stop the rest request function
func (ctx *HttpCtx) End() {
	ctx.ended = true
}

// parseForm parse the post form (both multipart and normal form)
func (ctx *HttpCtx) parseForm() error {
	isMultipart, reader, err := ctx.multipart()
	if err != nil {
		return err
	}
	if isMultipart {
		if ctx.req.MultipartForm != nil {
			return nil
		}
		if ctx.req.Form == nil {
			if err = ctx.req.ParseForm(); err != nil {
				return err
			}
		}
		f,err := reader.ReadForm(ctx.server.maxFormSize)
		if err != nil {
			return err
		}
		if ctx.req.PostForm == nil {
			ctx.req.PostForm = make(url.Values)
		}
		for k, v := range f.Value {
			ctx.req.Form[k] = append(ctx.req.Form[k], v...)
			// r.PostForm should also be populated. See Issue 9305.
			ctx.req.PostForm[k] = append(ctx.req.PostForm[k], v...)
		}
		ctx.req.MultipartForm = f
	} else {
		return ctx.req.ParseForm()
	}
	return nil
}

func (ctx *HttpCtx) multipart() (bool,*multipart.Reader,error) {
	v := ctx.req.Header.Get("Content-Type")
	if v == "" {
		return false, nil, nil
	}
	d, params, err := mime.ParseMediaType(v)
	if err != nil || d != "multipart/form-data" {
		return false, nil, nil
	}
	boundary, ok := params["boundary"]
	if !ok {
		return true, nil, http.ErrMissingBoundary
	}
	return true, multipart.NewReader(ctx.req.Body, boundary), nil
}<commit_msg>add PathPrefix to route parameter<commit_after>package mego

import (
	"net/http"
	"strconv"
	"strings"
	"net/url"
	"mime/multipart"
	"mime"
	"github.com/google/uuid"
)

type sizer interface {
	Size() int64
}

// HttpCtx the mego context struct
type HttpCtx struct {
	req       *http.Request
	res       http.ResponseWriter
	routeData map[string]string
	ended     bool
	items     map[string]interface{}
	server    *Server
	area      *Area
}

// Request get the mego request
func (ctx *HttpCtx) Request() *http.Request {
	return ctx.req
}

// Response get the mego response
func (ctx *HttpCtx) Response() http.ResponseWriter {
	return ctx.res
}

// RouteString get the route parameter value as string by key
func (ctx *HttpCtx) RouteString(key string) string {
	if ctx.routeData == nil {
		return ""
	}
	return ctx.routeData[key]
}

// RoutePathInfo get the route parameter "*pathInfo". the route url is like "/path/prefix/*pathInfo"
func (ctx *HttpCtx) RoutePathInfo() string {
	return ctx.RouteString("pathInfo")
}

// RouteInt get the route parameter value as int64 by key
func (ctx *HttpCtx) RouteInt(key string) int64 {
	var rawValue = ctx.RouteString(key)
	if len(rawValue) == 0 {
		return 0
	}
	value,err := strconv.ParseInt(rawValue, 0, 64)
	if err != nil {
		return 0
	}
	return value
}

// RouteUint get the route parameter value as uint64 by key
func (ctx *HttpCtx) RouteUint(key string) uint64 {
	var rawValue = ctx.RouteString(key)
	if len(rawValue) == 0 {
		return 0
	}
	value,err := strconv.ParseUint(rawValue, 0, 64)
	if err != nil {
		return 0
	}
	return value
}

// RouteFloat get the route parameter value as float by key
func (ctx *HttpCtx) RouteFloat(key string) float64 {
	var rawValue = ctx.RouteString(key)
	if len(rawValue) == 0 {
		return 0
	}
	value,err := strconv.ParseFloat(rawValue, 64)
	if err != nil {
		return 0
	}
	return value
}

// RouteBool get the route parameter value as boolean by key
func (ctx *HttpCtx) RouteBool(key string) bool {
	var rawValue = ctx.RouteString(key)
	if len(rawValue) == 0 || strings.ToLower(rawValue) == "false" || rawValue == "0"  {
		return false
	}
	return true
}

func (ctx *HttpCtx) PostFile(formName string) *UploadFile {
	f, h, err := ctx.Request().FormFile(formName)
	if err != nil {
		return &UploadFile{Error: err}
	}
	if f == nil {
		return nil
	}
	var size int64
	if s, ok := f.(sizer); ok {
		size = s.Size()
	} else {
		size = 0
	}
	return &UploadFile{FileName: h.Filename, Size: size, File: f, Header: h}
}

// SetItem add context data to mego context
func (ctx *HttpCtx) SetItem(key string, data interface{}) {
	if len(key) == 0 {
		return
	}
	if ctx.items == nil {
		ctx.items = make(map[string]interface{})
	}
	ctx.items[key] = data
}

// GetItem get the context data from mego context by key
func (ctx *HttpCtx) GetItem(key string) interface{} {
	if ctx.items == nil {
		return nil
	}
	return ctx.items[key]
}

// RemoveItem delete context item from mego context by key
func (ctx *HttpCtx) RemoveItem(key string) interface{} {
	if ctx.items == nil {
		return nil
	}
	data := ctx.items[key]
	delete(ctx.items, key)
	return data
}
func (ctx *HttpCtx) MapPath(path string) string {
	return ctx.server.MapRootPath(path)
}

// End end the mego context and stop the rest request function
func (ctx *HttpCtx) End() {
	ctx.ended = true
}

// parseForm parse the post form (both multipart and normal form)
func (ctx *HttpCtx) parseForm() error {
	isMultipart, reader, err := ctx.multipart()
	if err != nil {
		return err
	}
	if isMultipart {
		if ctx.req.MultipartForm != nil {
			return nil
		}
		if ctx.req.Form == nil {
			if err = ctx.req.ParseForm(); err != nil {
				return err
			}
		}
		f,err := reader.ReadForm(ctx.server.maxFormSize)
		if err != nil {
			return err
		}
		if ctx.req.PostForm == nil {
			ctx.req.PostForm = make(url.Values)
		}
		for k, v := range f.Value {
			ctx.req.Form[k] = append(ctx.req.Form[k], v...)
			// r.PostForm should also be populated. See Issue 9305.
			ctx.req.PostForm[k] = append(ctx.req.PostForm[k], v...)
		}
		ctx.req.MultipartForm = f
	} else {
		return ctx.req.ParseForm()
	}
	return nil
}

func (ctx *HttpCtx) multipart() (bool,*multipart.Reader,error) {
	v := ctx.req.Header.Get("Content-Type")
	if v == "" {
		return false, nil, nil
	}
	d, params, err := mime.ParseMediaType(v)
	if err != nil || d != "multipart/form-data" {
		return false, nil, nil
	}
	boundary, ok := params["boundary"]
	if !ok {
		return true, nil, http.ErrMissingBoundary
	}
	return true, multipart.NewReader(ctx.req.Body, boundary), nil
}<|endoftext|>


<commit_before>package main

import (
	"archive/tar"
	"bytes"
	"fmt"
	"io"
	"log"
	"os"
	"path"
	"regexp"
	"strings"
	"time"

	"github.com/flynn/flynn/controller/client"
	ct "github.com/flynn/flynn/controller/types"
	"github.com/flynn/flynn/host/types"
	"github.com/flynn/flynn/pkg/cluster"
	"github.com/flynn/flynn/pkg/exec"
	"github.com/flynn/flynn/pkg/random"
)

var clusterc = cluster.NewClient()

func init() {
	log.SetFlags(0)
}

var typesPattern = regexp.MustCompile("types.* -> (.+)\n")

const blobstoreURL = "http://blobstore.discoverd"
const scaleTimeout = 20 * time.Second

func main() {
	client, err := controller.NewClient("", os.Getenv("CONTROLLER_KEY"))
	if err != nil {
		log.Fatalln("Unable to connect to controller:", err)
	}

	appName := os.Args[1]

	app, err := client.GetApp(appName)
	if err == controller.ErrNotFound {
		log.Fatalf("Unknown app %q", appName)
	} else if err != nil {
		log.Fatalln("Error retrieving app:", err)
	}
	prevRelease, err := client.GetAppRelease(app.Name)
	if err == controller.ErrNotFound {
		prevRelease = &ct.Release{}
	} else if err != nil {
		log.Fatalln("Error getting current app release:", err)
	}

	fmt.Printf("-----> Building %s...\n", app.Name)

	var output bytes.Buffer
	slugURL := fmt.Sprintf("%s/%s.tgz", blobstoreURL, random.UUID())
	cmd := exec.Command(exec.DockerImage(os.Getenv("SLUGBUILDER_IMAGE_URI")), slugURL)
	cmd.Stdout = io.MultiWriter(os.Stdout, &output)
	cmd.Stderr = os.Stderr
	cmd.Meta = map[string]string{
		"flynn-controller.app":      app.ID,
		"flynn-controller.app_name": app.Name,
		"flynn-controller.release":  prevRelease.ID,
		"flynn-controller.type":     "slugbuilder",
	}
	if len(prevRelease.Env) > 0 {
		stdin, err := cmd.StdinPipe()
		if err != nil {
			log.Fatalln(err)
		}
		go appendEnvDir(os.Stdin, stdin, prevRelease.Env)
	} else {
		cmd.Stdin = os.Stdin
	}
	cmd.Env = make(map[string]string)
	cmd.Env["BUILD_CACHE_URL"] = fmt.Sprintf("%s/%s-cache.tgz", blobstoreURL, app.ID)
	if buildpackURL, ok := prevRelease.Env["BUILDPACK_URL"]; ok {
		cmd.Env["BUILDPACK_URL"] = buildpackURL
	}
	for _, k := range []string{"SSH_CLIENT_KEY", "SSH_CLIENT_HOSTS"} {
		if v := os.Getenv(k); v != "" {
			cmd.Env[k] = v
		}
	}

	if err := cmd.Run(); err != nil {
		log.Fatalln("Build failed:", err)
	}

	var types []string
	if match := typesPattern.FindSubmatch(output.Bytes()); match != nil {
		types = strings.Split(string(match[1]), ", ")
	}

	fmt.Printf("-----> Creating release...\n")

	artifact := &ct.Artifact{Type: "docker", URI: os.Getenv("SLUGRUNNER_IMAGE_URI")}
	if err := client.CreateArtifact(artifact); err != nil {
		log.Fatalln("Error creating artifact:", err)
	}

	release := &ct.Release{
		ArtifactID: artifact.ID,
		Env:        prevRelease.Env,
	}
	procs := make(map[string]ct.ProcessType)
	for _, t := range types {
		proc := prevRelease.Processes[t]
		proc.Cmd = []string{"start", t}
		if t == "web" {
			proc.Ports = []ct.Port{{
				Port:  8080,
				Proto: "tcp",
				Service: &host.Service{
					Name:   app.Name + "-web",
					Create: true,
					Check:  &host.HealthCheck{Type: "tcp"},
				},
			}}
		}
		procs[t] = proc
	}
	release.Processes = procs
	if release.Env == nil {
		release.Env = make(map[string]string)
	}
	release.Env["SLUG_URL"] = slugURL

	if err := client.CreateRelease(release); err != nil {
		log.Fatalln("Error creating release:", err)
	}
	if err := client.DeployAppRelease(app.Name, release.ID); err != nil {
		log.Fatalln("Error deploying app release:", err)
	}

	fmt.Println("=====> Application deployed")

	if needsDefaultScale(app.ID, prevRelease.ID, procs, client) {
		formation := &ct.Formation{
			AppID:     app.ID,
			ReleaseID: release.ID,
			Processes: map[string]int{"web": 1},
		}

		watcher, err := client.WatchJobEvents(app.ID, release.ID)
		if err != nil {
			log.Fatalln("Error streaming job events", err)
			return
		}
		defer watcher.Close()

		if err := client.PutFormation(formation); err != nil {
			log.Fatalln("Error putting formation:", err)
		}
		fmt.Println("=====> Waiting for web job to start...")

		err = watcher.WaitFor(ct.JobEvents{"web": {"up": 1}}, scaleTimeout, func(e *ct.Job) error {
			switch e.State {
			case "up":
				fmt.Println("=====> Default web formation scaled to 1")
			case "down", "crashed":
				return fmt.Errorf("Failed to scale web process type")
			}
			return nil
		})
		if err != nil {
			log.Fatalln(err.Error())
		}
	}
}

// needsDefaultScale indicates whether a release needs a default scale based on
// whether it has a web process type and either has no previous release or no
// previous scale.
func needsDefaultScale(appID, prevReleaseID string, procs map[string]ct.ProcessType, client *controller.Client) bool {
	if _, ok := procs["web"]; !ok {
		return false
	}
	if prevReleaseID == "" {
		return true
	}
	_, err := client.GetFormation(appID, prevReleaseID)
	return err == controller.ErrNotFound
}

func appendEnvDir(stdin io.Reader, pipe io.WriteCloser, env map[string]string) {
	defer pipe.Close()
	tr := tar.NewReader(stdin)
	tw := tar.NewWriter(pipe)
	for {
		hdr, err := tr.Next()
		if err == io.EOF {
			// end of tar archive
			break
		}
		if err != nil {
			log.Fatalln(err)
		}
		hdr.Name = path.Join("app", hdr.Name)
		if err := tw.WriteHeader(hdr); err != nil {
			log.Fatalln(err)
		}
		if _, err := io.Copy(tw, tr); err != nil {
			log.Fatalln(err)
		}
	}
	// append env dir
	for key, value := range env {
		hdr := &tar.Header{
			Name:    path.Join("env", key),
			Mode:    0644,
			ModTime: time.Now(),
			Size:    int64(len(value)),
		}

		if err := tw.WriteHeader(hdr); err != nil {
			log.Fatalln(err)
		}
		if _, err := tw.Write([]byte(value)); err != nil {
			log.Fatalln(err)
		}
	}
	hdr := &tar.Header{
		Name:    ".ENV_DIR_bdca46b87df0537eaefe79bb632d37709ff1df18",
		Mode:    0400,
		ModTime: time.Now(),
		Size:    0,
	}
	if err := tw.WriteHeader(hdr); err != nil {
		log.Fatalln(err)
	}
}
<commit_msg>gitreceive: Allow setting release metadata and env via cli arguments<commit_after>package main

import (
	"archive/tar"
	"bytes"
	"fmt"
	"io"
	"log"
	"os"
	"path"
	"regexp"
	"strings"
	"time"

	"github.com/flynn/flynn/Godeps/_workspace/src/github.com/flynn/go-docopt"
	"github.com/flynn/flynn/controller/client"
	ct "github.com/flynn/flynn/controller/types"
	"github.com/flynn/flynn/host/types"
	"github.com/flynn/flynn/pkg/cluster"
	"github.com/flynn/flynn/pkg/exec"
	"github.com/flynn/flynn/pkg/random"
	"github.com/flynn/flynn/pkg/version"
)

var clusterc = cluster.NewClient()

func init() {
	log.SetFlags(0)
}

var typesPattern = regexp.MustCompile("types.* -> (.+)\n")

const blobstoreURL = "http://blobstore.discoverd"
const scaleTimeout = 20 * time.Second

func parsePairs(args *docopt.Args, str string) (map[string]string, error) {
	pairs := args.All[str].([]string)
	item := make(map[string]string, len(pairs))
	for _, s := range pairs {
		v := strings.SplitN(s, "=", 2)
		if len(v) != 2 {
			return nil, fmt.Errorf("invalid var format: %q", s)
		}
		item[v[0]] = v[1]
	}
	return item, nil
}

func main() {
	client, err := controller.NewClient("", os.Getenv("CONTROLLER_KEY"))
	if err != nil {
		log.Fatalln("Unable to connect to controller:", err)
	}

	usage := `
Usage: flynn-receiver <app> <rev> [-e <var>=<val>]... [-m <key>=<val>]...

Options:
	-e,--env <var>=<val>
	-m,--meta <key>=<val>
`[1:]
	args, _ := docopt.Parse(usage, nil, true, version.String(), false)

	appName := args.String["<app>"]
	env, err := parsePairs(args, "--env")
	if err != nil {
		log.Fatal(err)
	}
	meta, err := parsePairs(args, "--meta")
	if err != nil {
		log.Fatal(err)
	}

	app, err := client.GetApp(appName)
	if err == controller.ErrNotFound {
		log.Fatalf("Unknown app %q", appName)
	} else if err != nil {
		log.Fatalln("Error retrieving app:", err)
	}
	prevRelease, err := client.GetAppRelease(app.Name)
	if err == controller.ErrNotFound {
		prevRelease = &ct.Release{}
	} else if err != nil {
		log.Fatalln("Error getting current app release:", err)
	}

	fmt.Printf("-----> Building %s...\n", app.Name)

	var output bytes.Buffer
	slugURL := fmt.Sprintf("%s/%s.tgz", blobstoreURL, random.UUID())
	cmd := exec.Command(exec.DockerImage(os.Getenv("SLUGBUILDER_IMAGE_URI")), slugURL)
	cmd.Stdout = io.MultiWriter(os.Stdout, &output)
	cmd.Stderr = os.Stderr
	cmd.Meta = map[string]string{
		"flynn-controller.app":      app.ID,
		"flynn-controller.app_name": app.Name,
		"flynn-controller.release":  prevRelease.ID,
		"flynn-controller.type":     "slugbuilder",
	}
	if len(prevRelease.Env) > 0 {
		stdin, err := cmd.StdinPipe()
		if err != nil {
			log.Fatalln(err)
		}
		go appendEnvDir(os.Stdin, stdin, prevRelease.Env)
	} else {
		cmd.Stdin = os.Stdin
	}
	cmd.Env = make(map[string]string)
	cmd.Env["BUILD_CACHE_URL"] = fmt.Sprintf("%s/%s-cache.tgz", blobstoreURL, app.ID)
	if buildpackURL, ok := prevRelease.Env["BUILDPACK_URL"]; ok {
		cmd.Env["BUILDPACK_URL"] = buildpackURL
	}
	for _, k := range []string{"SSH_CLIENT_KEY", "SSH_CLIENT_HOSTS"} {
		if v := os.Getenv(k); v != "" {
			cmd.Env[k] = v
		}
	}

	if err := cmd.Run(); err != nil {
		log.Fatalln("Build failed:", err)
	}

	var types []string
	if match := typesPattern.FindSubmatch(output.Bytes()); match != nil {
		types = strings.Split(string(match[1]), ", ")
	}

	fmt.Printf("-----> Creating release...\n")

	artifact := &ct.Artifact{Type: "docker", URI: os.Getenv("SLUGRUNNER_IMAGE_URI")}
	if err := client.CreateArtifact(artifact); err != nil {
		log.Fatalln("Error creating artifact:", err)
	}

	release := &ct.Release{
		ArtifactID: artifact.ID,
		Env:        prevRelease.Env,
		Meta:       prevRelease.Meta,
	}
	if release.Meta == nil {
		release.Meta = make(map[string]string, len(meta))
	}
	for k, v := range env {
		release.Env[k] = v
	}
	for k, v := range meta {
		release.Meta[k] = v
	}
	procs := make(map[string]ct.ProcessType)
	for _, t := range types {
		proc := prevRelease.Processes[t]
		proc.Cmd = []string{"start", t}
		if t == "web" {
			proc.Ports = []ct.Port{{
				Port:  8080,
				Proto: "tcp",
				Service: &host.Service{
					Name:   app.Name + "-web",
					Create: true,
					Check:  &host.HealthCheck{Type: "tcp"},
				},
			}}
		}
		procs[t] = proc
	}
	release.Processes = procs
	if release.Env == nil {
		release.Env = make(map[string]string)
	}
	release.Env["SLUG_URL"] = slugURL

	if err := client.CreateRelease(release); err != nil {
		log.Fatalln("Error creating release:", err)
	}
	if err := client.DeployAppRelease(app.Name, release.ID); err != nil {
		log.Fatalln("Error deploying app release:", err)
	}

	fmt.Println("=====> Application deployed")

	if needsDefaultScale(app.ID, prevRelease.ID, procs, client) {
		formation := &ct.Formation{
			AppID:     app.ID,
			ReleaseID: release.ID,
			Processes: map[string]int{"web": 1},
		}

		watcher, err := client.WatchJobEvents(app.ID, release.ID)
		if err != nil {
			log.Fatalln("Error streaming job events", err)
			return
		}
		defer watcher.Close()

		if err := client.PutFormation(formation); err != nil {
			log.Fatalln("Error putting formation:", err)
		}
		fmt.Println("=====> Waiting for web job to start...")

		err = watcher.WaitFor(ct.JobEvents{"web": {"up": 1}}, scaleTimeout, func(e *ct.Job) error {
			switch e.State {
			case "up":
				fmt.Println("=====> Default web formation scaled to 1")
			case "down", "crashed":
				return fmt.Errorf("Failed to scale web process type")
			}
			return nil
		})
		if err != nil {
			log.Fatalln(err.Error())
		}
	}
}

// needsDefaultScale indicates whether a release needs a default scale based on
// whether it has a web process type and either has no previous release or no
// previous scale.
func needsDefaultScale(appID, prevReleaseID string, procs map[string]ct.ProcessType, client *controller.Client) bool {
	if _, ok := procs["web"]; !ok {
		return false
	}
	if prevReleaseID == "" {
		return true
	}
	_, err := client.GetFormation(appID, prevReleaseID)
	return err == controller.ErrNotFound
}

func appendEnvDir(stdin io.Reader, pipe io.WriteCloser, env map[string]string) {
	defer pipe.Close()
	tr := tar.NewReader(stdin)
	tw := tar.NewWriter(pipe)
	for {
		hdr, err := tr.Next()
		if err == io.EOF {
			// end of tar archive
			break
		}
		if err != nil {
			log.Fatalln(err)
		}
		hdr.Name = path.Join("app", hdr.Name)
		if err := tw.WriteHeader(hdr); err != nil {
			log.Fatalln(err)
		}
		if _, err := io.Copy(tw, tr); err != nil {
			log.Fatalln(err)
		}
	}
	// append env dir
	for key, value := range env {
		hdr := &tar.Header{
			Name:    path.Join("env", key),
			Mode:    0644,
			ModTime: time.Now(),
			Size:    int64(len(value)),
		}

		if err := tw.WriteHeader(hdr); err != nil {
			log.Fatalln(err)
		}
		if _, err := tw.Write([]byte(value)); err != nil {
			log.Fatalln(err)
		}
	}
	hdr := &tar.Header{
		Name:    ".ENV_DIR_bdca46b87df0537eaefe79bb632d37709ff1df18",
		Mode:    0400,
		ModTime: time.Now(),
		Size:    0,
	}
	if err := tw.WriteHeader(hdr); err != nil {
		log.Fatalln(err)
	}
}
<|endoftext|>


<commit_before>package types

import (
	"fmt"
	"reflect"
	"time"

	abci "github.com/tendermint/abci/types"
	crypto "github.com/tendermint/go-crypto"
)

// TM2PB is used for converting Tendermint abci to protobuf abci.
// UNSTABLE
var TM2PB = tm2pb{}

type tm2pb struct{}

func (tm2pb) Header(header *Header) abci.Header {
	return abci.Header{
		ChainID:       header.ChainID,
		Height:        header.Height,
		Time:          header.Time.Unix(),
		NumTxs:        int32(header.NumTxs), // XXX: overflow
		LastBlockHash: header.LastBlockID.Hash,
		AppHash:       header.AppHash,
	}
}

func (tm2pb) Validator(val *Validator) abci.Validator {
	return abci.Validator{
		PubKey: TM2PB.PubKey(val.PubKey),
		Power:  val.VotingPower,
	}
}

func (tm2pb) PubKey(pubKey crypto.PubKey) abci.PubKey {
	switch pk := pubKey.(type) {
	case crypto.PubKeyEd25519:
		return abci.PubKey{
			Type: "ed25519",
			Data: pk[:],
		}
	case crypto.PubKeySecp256k1:
		return abci.PubKey{
			Type: "secp256k1",
			Data: pk[:],
		}
	default:
		panic(fmt.Sprintf("unknown pubkey type: %v %v", pubKey, reflect.TypeOf(pubKey)))
	}
}

func (tm2pb) Validators(vals *ValidatorSet) []abci.Validator {
	validators := make([]abci.Validator, len(vals.Validators))
	for i, val := range vals.Validators {
		validators[i] = TM2PB.Validator(val)
	}
	return validators
}

func (tm2pb) ConsensusParams(params *ConsensusParams) *abci.ConsensusParams {
	return &abci.ConsensusParams{
		BlockSize: &abci.BlockSize{

			MaxBytes: int32(params.BlockSize.MaxBytes),
			MaxTxs:   int32(params.BlockSize.MaxTxs),
			MaxGas:   params.BlockSize.MaxGas,
		},
		TxSize: &abci.TxSize{
			MaxBytes: int32(params.TxSize.MaxBytes),
			MaxGas:   params.TxSize.MaxGas,
		},
		BlockGossip: &abci.BlockGossip{
			BlockPartSizeBytes: int32(params.BlockGossip.BlockPartSizeBytes),
		},
	}
}

// ABCI Evidence includes information from the past that's not included in the evidence itself
// so Evidence types stays compact.
func (tm2pb) Evidence(ev Evidence, valSet *ValidatorSet, evTime time.Time) abci.Evidence {
	_, val := valSet.GetByAddress(ev.Address())
	if val == nil {
		// should already have checked this
		panic(val)
	}

	abciEvidence := abci.Evidence{
		Validator: abci.Validator{
			Address: ev.Address(),
			PubKey:  TM2PB.PubKey(val.PubKey),
			Power:   val.VotingPower,
		},
		Height:           ev.Height(),
		Time:             evTime.Unix(),
		TotalVotingPower: valSet.TotalVotingPower(),
	}

	// set type
	switch ev.(type) {
	case *DuplicateVoteEvidence:
		abciEvidence.Type = "duplicate/vote"
	case *MockGoodEvidence, MockGoodEvidence:
		abciEvidence.Type = "mock/good"
	default:
		panic(fmt.Sprintf("Unknown evidence type: %v %v", ev, reflect.TypeOf(ev)))
	}

	return abciEvidence
}

func (tm2pb) ValidatorFromPubKeyAndPower(pubkey crypto.PubKey, power int64) abci.Validator {
	pubkeyABCI := TM2PB.PubKey(pubkey)
	return abci.Validator{
		Address: pubkey.Address(),
		PubKey:  pubkeyABCI,
		Power:   power,
	}
}

//----------------------------------------------------------------------------

// PB2TM is used for converting protobuf abci to Tendermint abci.
// UNSTABLE
var PB2TM = pb2tm{}

type pb2tm struct{}

// TODO: validate key lengths ...
func (pb2tm) PubKey(pubKey abci.PubKey) (crypto.PubKey, error) {
	switch pubKey.Type {
	case "ed25519":
		var pk crypto.PubKeyEd25519
		copy(pk[:], pubKey.Data)
		return pk, nil
	case "secp256k1":
		var pk crypto.PubKeySecp256k1
		copy(pk[:], pubKey.Data)
		return pk, nil
	default:
		return nil, fmt.Errorf("Unknown pubkey type %v", pubKey.Type)
	}
}
<commit_msg>use const for abci type strings<commit_after>package types

import (
	"fmt"
	"reflect"
	"time"

	abci "github.com/tendermint/abci/types"
	crypto "github.com/tendermint/go-crypto"
)

//-------------------------------------------------------
// Use strings to distinguish types in ABCI messages

const (
	ABCIEvidenceTypeDuplicateVote = "duplicate/vote"
	ABCIEvidenceTypeMockGood      = "mock/good"
)

const (
	ABCIPubKeyTypeEd25519   = "ed25519"
	ABCIPubKeyTypeSecp256k1 = "secp256k1"
)

//-------------------------------------------------------

// TM2PB is used for converting Tendermint ABCI to protobuf ABCI.
// UNSTABLE
var TM2PB = tm2pb{}

type tm2pb struct{}

func (tm2pb) Header(header *Header) abci.Header {
	return abci.Header{
		ChainID:       header.ChainID,
		Height:        header.Height,
		Time:          header.Time.Unix(),
		NumTxs:        int32(header.NumTxs), // XXX: overflow
		LastBlockHash: header.LastBlockID.Hash,
		AppHash:       header.AppHash,
	}
}

func (tm2pb) Validator(val *Validator) abci.Validator {
	return abci.Validator{
		PubKey: TM2PB.PubKey(val.PubKey),
		Power:  val.VotingPower,
	}
}

func (tm2pb) PubKey(pubKey crypto.PubKey) abci.PubKey {
	switch pk := pubKey.(type) {
	case crypto.PubKeyEd25519:
		return abci.PubKey{
			Type: "ed25519",
			Data: pk[:],
		}
	case crypto.PubKeySecp256k1:
		return abci.PubKey{
			Type: "secp256k1",
			Data: pk[:],
		}
	default:
		panic(fmt.Sprintf("unknown pubkey type: %v %v", pubKey, reflect.TypeOf(pubKey)))
	}
}

func (tm2pb) Validators(vals *ValidatorSet) []abci.Validator {
	validators := make([]abci.Validator, len(vals.Validators))
	for i, val := range vals.Validators {
		validators[i] = TM2PB.Validator(val)
	}
	return validators
}

func (tm2pb) ConsensusParams(params *ConsensusParams) *abci.ConsensusParams {
	return &abci.ConsensusParams{
		BlockSize: &abci.BlockSize{

			MaxBytes: int32(params.BlockSize.MaxBytes),
			MaxTxs:   int32(params.BlockSize.MaxTxs),
			MaxGas:   params.BlockSize.MaxGas,
		},
		TxSize: &abci.TxSize{
			MaxBytes: int32(params.TxSize.MaxBytes),
			MaxGas:   params.TxSize.MaxGas,
		},
		BlockGossip: &abci.BlockGossip{
			BlockPartSizeBytes: int32(params.BlockGossip.BlockPartSizeBytes),
		},
	}
}

// ABCI Evidence includes information from the past that's not included in the evidence itself
// so Evidence types stays compact.
func (tm2pb) Evidence(ev Evidence, valSet *ValidatorSet, evTime time.Time) abci.Evidence {
	_, val := valSet.GetByAddress(ev.Address())
	if val == nil {
		// should already have checked this
		panic(val)
	}

	abciEvidence := abci.Evidence{
		Validator: abci.Validator{
			Address: ev.Address(),
			PubKey:  TM2PB.PubKey(val.PubKey),
			Power:   val.VotingPower,
		},
		Height:           ev.Height(),
		Time:             evTime.Unix(),
		TotalVotingPower: valSet.TotalVotingPower(),
	}

	// set type
	switch ev.(type) {
	case *DuplicateVoteEvidence:
		abciEvidence.Type = ABCIEvidenceTypeDuplicateVote
	case *MockGoodEvidence, MockGoodEvidence:
		abciEvidence.Type = ABCIEvidenceTypeMockGood
	default:
		panic(fmt.Sprintf("Unknown evidence type: %v %v", ev, reflect.TypeOf(ev)))
	}

	return abciEvidence
}

func (tm2pb) ValidatorFromPubKeyAndPower(pubkey crypto.PubKey, power int64) abci.Validator {
	pubkeyABCI := TM2PB.PubKey(pubkey)
	return abci.Validator{
		Address: pubkey.Address(),
		PubKey:  pubkeyABCI,
		Power:   power,
	}
}

//----------------------------------------------------------------------------

// PB2TM is used for converting protobuf ABCI to Tendermint ABCI.
// UNSTABLE
var PB2TM = pb2tm{}

type pb2tm struct{}

func (pb2tm) PubKey(pubKey abci.PubKey) (crypto.PubKey, error) {
	// TODO: define these in go-crypto and use them
	sizeEd := 32
	sizeSecp := 33
	switch pubKey.Type {
	case ABCIPubKeyTypeEd25519:
		if len(pubKey.Data) != sizeEd {
			return nil, fmt.Errorf("Invalid size for PubKeyEd25519. Got %d, expected %d", len(pubKey.Data), sizeEd)
		}
		var pk crypto.PubKeyEd25519
		copy(pk[:], pubKey.Data)
		return pk, nil
	case ABCIPubKeyTypeSecp256k1:
		if len(pubKey.Data) != sizeSecp {
			return nil, fmt.Errorf("Invalid size for PubKeyEd25519. Got %d, expected %d", len(pubKey.Data), sizeSecp)
		}
		var pk crypto.PubKeySecp256k1
		copy(pk[:], pubKey.Data)
		return pk, nil
	default:
		return nil, fmt.Errorf("Unknown pubkey type %v", pubKey.Type)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"go/build"
	"log"
	"os"
	"path/filepath"
	"sort"
	"strings"
)

func recursiveParseImports(
	imports map[string]bool, path string, cwd string,
) error {
	if path == "C" {
		return nil
	}

	pkg, err := build.Import(path, cwd, build.IgnoreVendor)
	if err != nil {
		return err
	}

	if path != "." {
		standard := false

		if strings.HasPrefix(pkg.ImportPath, "golang.org/") ||
			(pkg.Goroot && pkg.ImportPath != "") {
			standard = true
		}

		imports[pkg.ImportPath] = standard
	}

	for _, importing := range pkg.Imports {
		_, ok := imports[importing]
		if !ok {
			err = recursiveParseImports(imports, importing, cwd)
			if err != nil {
				return err
			}
		}
	}

	return nil
}

func parseImports(recursive bool) ([]string, error) {
	cwd, err := os.Getwd()
	if err != nil {
		return nil, err
	}

	var (
		allImports = map[string]bool{}
		imports    = []string{}
	)

	filepath.Walk(
		cwd, func(path string, info os.FileInfo, err error) error {
			if err != nil {
				return err
			}

			if filepath.Base(path) == ".git" ||
				filepath.Dir(path) == filepath.Join(cwd, "vendor") {
				return filepath.SkipDir
			}

			if path == filepath.Join(cwd, "vendor") {
				return nil
			}

			if !info.IsDir() {
				return nil
			}

			err = recursiveParseImports(
				allImports,
				".",
				path,
			)
			if _, ok := err.(*build.NoGoError); ok {
				return nil
			}

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

			return nil
		},
	)

	for importing, standard := range allImports {
		if !standard {
			importpath, err := getRootImportpath(importing)
			if err != nil {
				continue
			}

			found := false
			for _, imported := range imports {
				if importpath == imported {
					found = true
					break
				}
			}

			if found {
				continue
			}

			imports = append(imports, importpath)
		}
	}

	sort.Strings(imports)

	return imports, nil
}
<commit_msg>Don't import own package<commit_after>package main

import (
	"go/build"
	"log"
	"os"
	"path/filepath"
	"sort"
	"strings"
)

func recursiveParseImports(
	imports map[string]bool, path string, cwd string,
) error {
	if path == "C" {
		return nil
	}

	pkg, err := build.Import(path, cwd, build.IgnoreVendor)
	if err != nil {
		return err
	}

	if path != "." {
		standard := false

		if strings.HasPrefix(pkg.ImportPath, "golang.org/") ||
			(pkg.Goroot && pkg.ImportPath != "") {
			standard = true
		}

		imports[pkg.ImportPath] = standard
	}

	for _, importing := range pkg.Imports {
		_, ok := imports[importing]
		if !ok {
			err = recursiveParseImports(imports, importing, cwd)
			if err != nil {
				return err
			}
		}
	}

	return nil
}

func parseImports(recursive bool) ([]string, error) {
	cwd, err := os.Getwd()
	if err != nil {
		return nil, err
	}

	var (
		allImports = map[string]bool{}
		imports    = []string{}
	)

	filepath.Walk(
		cwd, func(path string, info os.FileInfo, err error) error {
			if err != nil {
				return err
			}

			if filepath.Base(path) == ".git" ||
				filepath.Dir(path) == filepath.Join(cwd, "vendor") {
				return filepath.SkipDir
			}

			if path == filepath.Join(cwd, "vendor") {
				return nil
			}

			if !info.IsDir() {
				return nil
			}

			err = recursiveParseImports(
				allImports,
				".",
				path,
			)
			if _, ok := err.(*build.NoGoError); ok {
				return nil
			}

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

			return nil
		},
	)

	for importing, standard := range allImports {
		if !standard {
			importpath, err := getRootImportpath(importing)
			if err != nil {
				continue
			}

			if isOwnPackage(importpath, cwd) {
				continue
			}

			found := false
			for _, imported := range imports {
				if importpath == imported {
					found = true
					break
				}
			}

			if found {
				continue
			}

			imports = append(imports, importpath)
		}
	}

	sort.Strings(imports)

	return imports, nil
}

func isOwnPackage(path, cwd string) bool {
	for _, gopath := range filepath.SplitList(os.Getenv("GOPATH")) {
		if strings.HasPrefix(filepath.Join(gopath, "src", path), cwd) {
			return true
		}
	}
	return false
}
<|endoftext|>


<commit_before>package softlayer

import (
	"errors"
	"fmt"
	"strconv"
	"testing"

	"strings"

	"github.com/hashicorp/terraform/helper/resource"
	"github.com/hashicorp/terraform/terraform"
	"github.ibm.com/riethm/gopherlayer.git/datatypes"
	"github.ibm.com/riethm/gopherlayer.git/services"
	"github.ibm.com/riethm/gopherlayer.git/session"
)

func TestAccSoftLayerScaleGroup_Basic(t *testing.T) {
	var scalegroup datatypes.Scale_Group

	resource.Test(t, resource.TestCase{
		PreCheck:     func() { testAccPreCheck(t) },
		Providers:    testAccProviders,
		CheckDestroy: testAccCheckSoftLayerScaleGroupDestroy,
		Steps: []resource.TestStep{
			resource.TestStep{
				Config: testAccCheckSoftLayerScaleGroupConfig_basic,
				Check: resource.ComposeTestCheckFunc(
					testAccCheckSoftLayerScaleGroupExists("softlayer_scale_group.sample-http-cluster", &scalegroup),
					resource.TestCheckResourceAttr(
						"softlayer_scale_group.sample-http-cluster", "name", "sample-http-cluster"),
					resource.TestCheckResourceAttr(
						"softlayer_scale_group.sample-http-cluster", "regional_group", "as-sgp-central-1"),
					resource.TestCheckResourceAttr(
						"softlayer_scale_group.sample-http-cluster", "cooldown", "30"),
					resource.TestCheckResourceAttr(
						"softlayer_scale_group.sample-http-cluster", "minimum_member_count", "1"),
					resource.TestCheckResourceAttr(
						"softlayer_scale_group.sample-http-cluster", "maximum_member_count", "10"),
					resource.TestCheckResourceAttr(
						"softlayer_scale_group.sample-http-cluster", "termination_policy", "CLOSEST_TO_NEXT_CHARGE"),
					resource.TestCheckResourceAttr(
						"softlayer_scale_group.sample-http-cluster", "virtual_server_id", "267513"),
					resource.TestCheckResourceAttr(
						"softlayer_scale_group.sample-http-cluster", "port", "8080"),
					resource.TestCheckResourceAttr(
						"softlayer_scale_group.sample-http-cluster", "health_check.type", "HTTP"),
					resource.TestCheckResourceAttr(
						"softlayer_scale_group.sample-http-cluster", "virtual_guest_member_template.0.name", "test-VM"),
					resource.TestCheckResourceAttr(
						"softlayer_scale_group.sample-http-cluster", "virtual_guest_member_template.0.domain", "example.com"),
					resource.TestCheckResourceAttr(
						"softlayer_scale_group.sample-http-cluster", "virtual_guest_member_template.0.cpu", "1"),
					resource.TestCheckResourceAttr(
						"softlayer_scale_group.sample-http-cluster", "virtual_guest_member_template.0.ram", "4096"),
					resource.TestCheckResourceAttr(
						"softlayer_scale_group.sample-http-cluster", "virtual_guest_member_template.0.public_network_speed", "1000"),
					resource.TestCheckResourceAttr(
						"softlayer_scale_group.sample-http-cluster", "virtual_guest_member_template.0.hourly_billing", "true"),
					resource.TestCheckResourceAttr(
						"softlayer_scale_group.sample-http-cluster", "virtual_guest_member_template.0.image", "DEBIAN_7_64"),
					resource.TestCheckResourceAttr(
						"softlayer_scale_group.sample-http-cluster", "virtual_guest_member_template.0.local_disk", "false"),
					resource.TestCheckResourceAttr(
						"softlayer_scale_group.sample-http-cluster", "virtual_guest_member_template.0.disks.0", "25"),
					resource.TestCheckResourceAttr(
						"softlayer_scale_group.sample-http-cluster", "virtual_guest_member_template.0.disks.1", "100"),
					resource.TestCheckResourceAttr(
						"softlayer_scale_group.sample-http-cluster", "virtual_guest_member_template.0.region", "sng01"),
					resource.TestCheckResourceAttr(
						"softlayer_scale_group.sample-http-cluster", "virtual_guest_member_template.0.post_install_script_uri", ""),
					resource.TestCheckResourceAttr(
						"softlayer_scale_group.sample-http-cluster", "virtual_guest_member_template.0.ssh_keys.0", "383111"),
					resource.TestCheckResourceAttr(
						"softlayer_scale_group.sample-http-cluster", "virtual_guest_member_template.0.user_data", "#!/bin/bash"),
					resource.TestCheckResourceAttr(
						"softlayer_scale_group.sample-http-cluster", "network_vlans.#", "1"),
					testAccCheckSoftLayerScaleGroupContainsNetworkVlan(&scalegroup, 1928, "bcr02a.sng01"),
				),
			},

			resource.TestStep{
				Config: testAccCheckSoftLayerScaleGroupConfig_updated,
				Check: resource.ComposeTestCheckFunc(
					testAccCheckSoftLayerScaleGroupExists("softlayer_scale_group.sample-http-cluster", &scalegroup),
					resource.TestCheckResourceAttr(
						"softlayer_scale_group.sample-http-cluster", "name", "changed_name"),
					resource.TestCheckResourceAttr(
						"softlayer_scale_group.sample-http-cluster", "regional_group", "as-sgp-central-1"),
					resource.TestCheckResourceAttr(
						"softlayer_scale_group.sample-http-cluster", "minimum_member_count", "2"),
					resource.TestCheckResourceAttr(
						"softlayer_scale_group.sample-http-cluster", "maximum_member_count", "12"),
					resource.TestCheckResourceAttr(
						"softlayer_scale_group.sample-http-cluster", "termination_policy", "NEWEST"),
					resource.TestCheckResourceAttr(
						"softlayer_scale_group.sample-http-cluster", "cooldown", "35"),
					resource.TestCheckResourceAttr(
						"softlayer_scale_group.sample-http-cluster", "port", "9090"),
					resource.TestCheckResourceAttr(
						"softlayer_scale_group.sample-http-cluster", "health_check.type", "HTTP-CUSTOM"),
					resource.TestCheckResourceAttr(
						"softlayer_scale_group.sample-http-cluster", "virtual_guest_member_template.0.name", "example-VM"),
					resource.TestCheckResourceAttr(
						"softlayer_scale_group.sample-http-cluster", "virtual_guest_member_template.0.domain", "test.com"),
					resource.TestCheckResourceAttr(
						"softlayer_scale_group.sample-http-cluster", "virtual_guest_member_template.0.cpu", "2"),
					resource.TestCheckResourceAttr(
						"softlayer_scale_group.sample-http-cluster", "virtual_guest_member_template.0.ram", "8192"),
					resource.TestCheckResourceAttr(
						"softlayer_scale_group.sample-http-cluster", "virtual_guest_member_template.0.public_network_speed", "100"),
					resource.TestCheckResourceAttr(
						"softlayer_scale_group.sample-http-cluster", "virtual_guest_member_template.0.image", "CENTOS_7_64"),
					resource.TestCheckResourceAttr(
						"softlayer_scale_group.sample-http-cluster", "virtual_guest_member_template.0.region", "sng01"),
					resource.TestCheckResourceAttr(
						"softlayer_scale_group.sample-http-cluster", "virtual_guest_member_template.0.post_install_script_uri", "http://localhost/index.html"),
				),
			},
		},
	})
}

func testAccCheckSoftLayerScaleGroupDestroy(s *terraform.State) error {
	service := services.GetScaleGroupService(testAccProvider.Meta().(*session.Session))

	for _, rs := range s.RootModule().Resources {
		if rs.Type != "softlayer_scale_group" {
			continue
		}

		scalegroupId, _ := strconv.Atoi(rs.Primary.ID)

		// Try to find the key
		_, err := service.Id(scalegroupId).Mask("id").GetObject()

		if err != nil && !strings.Contains(err.Error(), "404") {
			return fmt.Errorf("Error waiting for Auto Scale (%s) to be destroyed: %s", rs.Primary.ID, err)
		}
	}

	return nil
}

func testAccCheckSoftLayerScaleGroupContainsNetworkVlan(scaleGroup *datatypes.Scale_Group, vlanNumber int, primaryRouterHostname string) resource.TestCheckFunc {
	return func(s *terraform.State) error {
		found := false

		for _, scaleVlan := range scaleGroup.NetworkVlans {
			vlan := *scaleVlan.NetworkVlan

			if *vlan.VlanNumber == vlanNumber && *vlan.PrimaryRouter.Hostname == primaryRouterHostname {
				found = true
				break
			}
		}

		if !found {
			return fmt.Errorf(
				"Vlan number %d with router hostname %s not found in scale group",
				vlanNumber,
				primaryRouterHostname)
		}

		return nil
	}
}

func testAccCheckSoftLayerScaleGroupExists(n string, scalegroup *datatypes.Scale_Group) resource.TestCheckFunc {
	return func(s *terraform.State) error {
		rs, ok := s.RootModule().Resources[n]

		if !ok {
			return fmt.Errorf("Not found: %s", n)
		}

		if rs.Primary.ID == "" {
			return errors.New("No Record ID is set")
		}

		scalegroupId, _ := strconv.Atoi(rs.Primary.ID)

		service := services.GetScaleGroupService(testAccProvider.Meta().(*session.Session))
		foundScaleGroup, err := service.Id(scalegroupId).Mask(strings.Join(SoftLayerScaleGroupObjectMask, ";")).GetObject()

		if err != nil {
			return err
		}

		if strconv.Itoa(int(*foundScaleGroup.Id)) != rs.Primary.ID {
			return fmt.Errorf("Record %s not found", rs.Primary.ID)
		}

		*scalegroup = foundScaleGroup

		return nil
	}
}

const testAccCheckSoftLayerScaleGroupConfig_basic = `
resource "softlayer_scale_group" "sample-http-cluster" {
    name = "sample-http-cluster"
    regional_group = "as-sgp-central-1"
    cooldown = 30
    minimum_member_count = 1
    maximum_member_count = 10
    termination_policy = "CLOSEST_TO_NEXT_CHARGE"
    virtual_server_id = 267513
    port = 8080
    health_check = {
        type = "HTTP"
    }
    virtual_guest_member_template = {
        name = "test-VM"
        domain = "example.com"
        cpu = 1
        ram = 4096
        public_network_speed = 1000
        hourly_billing = true
        image = "DEBIAN_7_64"
        local_disk = false
        disks = [25,100]
        region = "sng01"
        post_install_script_uri = ""
        ssh_keys = [383111]
        user_data = "#!/bin/bash"
    }
    network_vlans = {
            vlan_number = "1928"
            primary_router_hostname = "bcr02a.sng01"
    }

}`

const testAccCheckSoftLayerScaleGroupConfig_updated = `
resource "softlayer_scale_group" "sample-http-cluster" {
    name = "changed_name"
    regional_group = "as-sgp-central-1"
    cooldown = 35
    minimum_member_count = 2
    maximum_member_count = 12
    termination_policy = "NEWEST"
    virtual_server_id = 267513
    port = 9090
    health_check = {
        type = "HTTP-CUSTOM"
        custom_method = "GET"
        custom_request = "/healthcheck"
        custom_response = 200
    }
    virtual_guest_member_template = {
        name = "example-VM"
        domain = "test.com"
        cpu = 2
        ram = 8192
        public_network_speed = 100
        hourly_billing = true
        image = "CENTOS_7_64"
        local_disk = false
        disks = [25,100]
        region = "sng01"
        post_install_script_uri = "http://localhost/index.html"
        ssh_keys = [383111]
        user_data = "#!/bin/bash"
    }
    network_vlans = {
        vlan_number = "1928"
        primary_router_hostname = "bcr02a.sng01"
    }
}`
<commit_msg>Changed region to datacenter in scale_group_test.<commit_after>package softlayer

import (
	"errors"
	"fmt"
	"strconv"
	"testing"

	"strings"

	"github.com/hashicorp/terraform/helper/resource"
	"github.com/hashicorp/terraform/terraform"
	"github.ibm.com/riethm/gopherlayer.git/datatypes"
	"github.ibm.com/riethm/gopherlayer.git/services"
	"github.ibm.com/riethm/gopherlayer.git/session"
)

func TestAccSoftLayerScaleGroup_Basic(t *testing.T) {
	var scalegroup datatypes.Scale_Group

	resource.Test(t, resource.TestCase{
		PreCheck:     func() { testAccPreCheck(t) },
		Providers:    testAccProviders,
		CheckDestroy: testAccCheckSoftLayerScaleGroupDestroy,
		Steps: []resource.TestStep{
			resource.TestStep{
				Config: testAccCheckSoftLayerScaleGroupConfig_basic,
				Check: resource.ComposeTestCheckFunc(
					testAccCheckSoftLayerScaleGroupExists("softlayer_scale_group.sample-http-cluster", &scalegroup),
					resource.TestCheckResourceAttr(
						"softlayer_scale_group.sample-http-cluster", "name", "sample-http-cluster"),
					resource.TestCheckResourceAttr(
						"softlayer_scale_group.sample-http-cluster", "regional_group", "as-sgp-central-1"),
					resource.TestCheckResourceAttr(
						"softlayer_scale_group.sample-http-cluster", "cooldown", "30"),
					resource.TestCheckResourceAttr(
						"softlayer_scale_group.sample-http-cluster", "minimum_member_count", "1"),
					resource.TestCheckResourceAttr(
						"softlayer_scale_group.sample-http-cluster", "maximum_member_count", "10"),
					resource.TestCheckResourceAttr(
						"softlayer_scale_group.sample-http-cluster", "termination_policy", "CLOSEST_TO_NEXT_CHARGE"),
					resource.TestCheckResourceAttr(
						"softlayer_scale_group.sample-http-cluster", "virtual_server_id", "267513"),
					resource.TestCheckResourceAttr(
						"softlayer_scale_group.sample-http-cluster", "port", "8080"),
					resource.TestCheckResourceAttr(
						"softlayer_scale_group.sample-http-cluster", "health_check.type", "HTTP"),
					resource.TestCheckResourceAttr(
						"softlayer_scale_group.sample-http-cluster", "virtual_guest_member_template.0.name", "test-VM"),
					resource.TestCheckResourceAttr(
						"softlayer_scale_group.sample-http-cluster", "virtual_guest_member_template.0.domain", "example.com"),
					resource.TestCheckResourceAttr(
						"softlayer_scale_group.sample-http-cluster", "virtual_guest_member_template.0.cpu", "1"),
					resource.TestCheckResourceAttr(
						"softlayer_scale_group.sample-http-cluster", "virtual_guest_member_template.0.ram", "4096"),
					resource.TestCheckResourceAttr(
						"softlayer_scale_group.sample-http-cluster", "virtual_guest_member_template.0.public_network_speed", "1000"),
					resource.TestCheckResourceAttr(
						"softlayer_scale_group.sample-http-cluster", "virtual_guest_member_template.0.hourly_billing", "true"),
					resource.TestCheckResourceAttr(
						"softlayer_scale_group.sample-http-cluster", "virtual_guest_member_template.0.image", "DEBIAN_7_64"),
					resource.TestCheckResourceAttr(
						"softlayer_scale_group.sample-http-cluster", "virtual_guest_member_template.0.local_disk", "false"),
					resource.TestCheckResourceAttr(
						"softlayer_scale_group.sample-http-cluster", "virtual_guest_member_template.0.disks.0", "25"),
					resource.TestCheckResourceAttr(
						"softlayer_scale_group.sample-http-cluster", "virtual_guest_member_template.0.disks.1", "100"),
					resource.TestCheckResourceAttr(
						"softlayer_scale_group.sample-http-cluster", "virtual_guest_member_template.0.region", "sng01"),
					resource.TestCheckResourceAttr(
						"softlayer_scale_group.sample-http-cluster", "virtual_guest_member_template.0.post_install_script_uri", ""),
					resource.TestCheckResourceAttr(
						"softlayer_scale_group.sample-http-cluster", "virtual_guest_member_template.0.ssh_keys.0", "383111"),
					resource.TestCheckResourceAttr(
						"softlayer_scale_group.sample-http-cluster", "virtual_guest_member_template.0.user_data", "#!/bin/bash"),
					resource.TestCheckResourceAttr(
						"softlayer_scale_group.sample-http-cluster", "network_vlans.#", "1"),
					testAccCheckSoftLayerScaleGroupContainsNetworkVlan(&scalegroup, 1928, "bcr02a.sng01"),
				),
			},

			resource.TestStep{
				Config: testAccCheckSoftLayerScaleGroupConfig_updated,
				Check: resource.ComposeTestCheckFunc(
					testAccCheckSoftLayerScaleGroupExists("softlayer_scale_group.sample-http-cluster", &scalegroup),
					resource.TestCheckResourceAttr(
						"softlayer_scale_group.sample-http-cluster", "name", "changed_name"),
					resource.TestCheckResourceAttr(
						"softlayer_scale_group.sample-http-cluster", "regional_group", "as-sgp-central-1"),
					resource.TestCheckResourceAttr(
						"softlayer_scale_group.sample-http-cluster", "minimum_member_count", "2"),
					resource.TestCheckResourceAttr(
						"softlayer_scale_group.sample-http-cluster", "maximum_member_count", "12"),
					resource.TestCheckResourceAttr(
						"softlayer_scale_group.sample-http-cluster", "termination_policy", "NEWEST"),
					resource.TestCheckResourceAttr(
						"softlayer_scale_group.sample-http-cluster", "cooldown", "35"),
					resource.TestCheckResourceAttr(
						"softlayer_scale_group.sample-http-cluster", "port", "9090"),
					resource.TestCheckResourceAttr(
						"softlayer_scale_group.sample-http-cluster", "health_check.type", "HTTP-CUSTOM"),
					resource.TestCheckResourceAttr(
						"softlayer_scale_group.sample-http-cluster", "virtual_guest_member_template.0.name", "example-VM"),
					resource.TestCheckResourceAttr(
						"softlayer_scale_group.sample-http-cluster", "virtual_guest_member_template.0.domain", "test.com"),
					resource.TestCheckResourceAttr(
						"softlayer_scale_group.sample-http-cluster", "virtual_guest_member_template.0.cpu", "2"),
					resource.TestCheckResourceAttr(
						"softlayer_scale_group.sample-http-cluster", "virtual_guest_member_template.0.ram", "8192"),
					resource.TestCheckResourceAttr(
						"softlayer_scale_group.sample-http-cluster", "virtual_guest_member_template.0.public_network_speed", "100"),
					resource.TestCheckResourceAttr(
						"softlayer_scale_group.sample-http-cluster", "virtual_guest_member_template.0.image", "CENTOS_7_64"),
					resource.TestCheckResourceAttr(
						"softlayer_scale_group.sample-http-cluster", "virtual_guest_member_template.0.region", "sng01"),
					resource.TestCheckResourceAttr(
						"softlayer_scale_group.sample-http-cluster", "virtual_guest_member_template.0.post_install_script_uri", "http://localhost/index.html"),
				),
			},
		},
	})
}

func testAccCheckSoftLayerScaleGroupDestroy(s *terraform.State) error {
	service := services.GetScaleGroupService(testAccProvider.Meta().(*session.Session))

	for _, rs := range s.RootModule().Resources {
		if rs.Type != "softlayer_scale_group" {
			continue
		}

		scalegroupId, _ := strconv.Atoi(rs.Primary.ID)

		// Try to find the key
		_, err := service.Id(scalegroupId).Mask("id").GetObject()

		if err != nil && !strings.Contains(err.Error(), "404") {
			return fmt.Errorf("Error waiting for Auto Scale (%s) to be destroyed: %s", rs.Primary.ID, err)
		}
	}

	return nil
}

func testAccCheckSoftLayerScaleGroupContainsNetworkVlan(scaleGroup *datatypes.Scale_Group, vlanNumber int, primaryRouterHostname string) resource.TestCheckFunc {
	return func(s *terraform.State) error {
		found := false

		for _, scaleVlan := range scaleGroup.NetworkVlans {
			vlan := *scaleVlan.NetworkVlan

			if *vlan.VlanNumber == vlanNumber && *vlan.PrimaryRouter.Hostname == primaryRouterHostname {
				found = true
				break
			}
		}

		if !found {
			return fmt.Errorf(
				"Vlan number %d with router hostname %s not found in scale group",
				vlanNumber,
				primaryRouterHostname)
		}

		return nil
	}
}

func testAccCheckSoftLayerScaleGroupExists(n string, scalegroup *datatypes.Scale_Group) resource.TestCheckFunc {
	return func(s *terraform.State) error {
		rs, ok := s.RootModule().Resources[n]

		if !ok {
			return fmt.Errorf("Not found: %s", n)
		}

		if rs.Primary.ID == "" {
			return errors.New("No Record ID is set")
		}

		scalegroupId, _ := strconv.Atoi(rs.Primary.ID)

		service := services.GetScaleGroupService(testAccProvider.Meta().(*session.Session))
		foundScaleGroup, err := service.Id(scalegroupId).Mask(strings.Join(SoftLayerScaleGroupObjectMask, ";")).GetObject()

		if err != nil {
			return err
		}

		if strconv.Itoa(int(*foundScaleGroup.Id)) != rs.Primary.ID {
			return fmt.Errorf("Record %s not found", rs.Primary.ID)
		}

		*scalegroup = foundScaleGroup

		return nil
	}
}

const testAccCheckSoftLayerScaleGroupConfig_basic = `
resource "softlayer_scale_group" "sample-http-cluster" {
    name = "sample-http-cluster"
    regional_group = "as-sgp-central-1"
    cooldown = 30
    minimum_member_count = 1
    maximum_member_count = 10
    termination_policy = "CLOSEST_TO_NEXT_CHARGE"
    virtual_server_id = 267513
    port = 8080
    health_check = {
        type = "HTTP"
    }
    virtual_guest_member_template = {
        name = "test-VM"
        domain = "example.com"
        cpu = 1
        ram = 4096
        public_network_speed = 1000
        hourly_billing = true
        image = "DEBIAN_7_64"
        local_disk = false
        disks = [25,100]
        datacenter = "sng01"
        post_install_script_uri = ""
        ssh_keys = [383111]
        user_data = "#!/bin/bash"
    }
    network_vlans = {
            vlan_number = "1928"
            primary_router_hostname = "bcr02a.sng01"
    }

}`

const testAccCheckSoftLayerScaleGroupConfig_updated = `
resource "softlayer_scale_group" "sample-http-cluster" {
    name = "changed_name"
    regional_group = "as-sgp-central-1"
    cooldown = 35
    minimum_member_count = 2
    maximum_member_count = 12
    termination_policy = "NEWEST"
    virtual_server_id = 267513
    port = 9090
    health_check = {
        type = "HTTP-CUSTOM"
        custom_method = "GET"
        custom_request = "/healthcheck"
        custom_response = 200
    }
    virtual_guest_member_template = {
        name = "example-VM"
        domain = "test.com"
        cpu = 2
        ram = 8192
        public_network_speed = 100
        hourly_billing = true
        image = "CENTOS_7_64"
        local_disk = false
        disks = [25,100]
        datacenter = "sng01"
        post_install_script_uri = "http://localhost/index.html"
        ssh_keys = [383111]
        user_data = "#!/bin/bash"
    }
    network_vlans = {
        vlan_number = "1928"
        primary_router_hostname = "bcr02a.sng01"
    }
}`
<|endoftext|>


<commit_before>// Copyright 2016 Documize Inc. <legal@documize.com>. All rights reserved.
//
// This software (Documize Community Edition) is licensed under
// GNU AGPL v3 http://www.gnu.org/licenses/agpl-3.0.en.html
//
// You can operate outside the AGPL restrictions by purchasing
// Documize Enterprise Edition and obtaining a commercial license
// by contacting <sales@documize.com>.
//
// https://documize.com

// Package mysql handles data persistence for both category definition
// and and document/category association.
package mysql

import (
	"database/sql"
	"fmt"
	"time"

	"github.com/documize/community/core/env"
	"github.com/documize/community/domain"
	"github.com/documize/community/domain/store/mysql"
	"github.com/documize/community/model/category"
	"github.com/pkg/errors"
)

// Scope provides data access to MySQL.
type Scope struct {
	Runtime *env.Runtime
}

// Add inserts the given record into the category table.
func (s Scope) Add(ctx domain.RequestContext, c category.Category) (err error) {
	c.Created = time.Now().UTC()
	c.Revised = time.Now().UTC()

	_, err = ctx.Transaction.Exec("INSERT INTO category (refid, orgid, labelid, category, created, revised) VALUES (?, ?, ?, ?, ?, ?)",
		c.RefID, c.OrgID, c.LabelID, c.Category, c.Created, c.Revised)

	if err != nil {
		err = errors.Wrap(err, "unable to execute insert category")
	}

	return
}

// GetBySpace returns space categories accessible by user.
// Context is used to for user ID.
func (s Scope) GetBySpace(ctx domain.RequestContext, spaceID string) (c []category.Category, err error) {
	err = s.Runtime.Db.Select(&c, `
		SELECT id, refid, orgid, labelid, category, created, revised FROM category
		WHERE orgid=? AND labelid=?
			  AND refid IN (SELECT refid FROM permission WHERE orgid=? AND location='category' AND refid IN (
				SELECT refid from permission WHERE orgid=? AND who='user' AND (whoid=? OR whoid='0') AND location='category' UNION ALL
				SELECT p.refid from permission p LEFT JOIN rolemember r ON p.whoid=r.roleid
					WHERE p.orgid=? AND p.who='role' AND p.location='category' AND (r.userid=? OR r.userid='0')
		))
	  ORDER BY category`, ctx.OrgID, spaceID, ctx.OrgID, ctx.OrgID, ctx.UserID, ctx.OrgID, ctx.UserID)

	if err == sql.ErrNoRows {
		err = nil
	}
	if err != nil {
		err = errors.Wrap(err, fmt.Sprintf("unable to execute select categories for space %s", spaceID))
	}

	return
}

// GetAllBySpace returns all space categories.
func (s Scope) GetAllBySpace(ctx domain.RequestContext, spaceID string) (c []category.Category, err error) {
	err = s.Runtime.Db.Select(&c, `
		SELECT id, refid, orgid, labelid, category, created, revised FROM category
		WHERE orgid=? AND labelid=?
			  AND labelid IN (SELECT refid FROM permission WHERE orgid=? AND location='space' AND refid IN (
				SELECT refid from permission WHERE orgid=? AND who='user' AND (whoid=? OR whoid='0') AND location='space' AND action='view' UNION ALL
				SELECT p.refid from permission p LEFT JOIN rolemember r ON p.whoid=r.roleid WHERE p.orgid=? AND p.who='role' AND p.location='space'
					AND p.action='view' AND (r.userid=? OR r.userid='0')
		))
	  ORDER BY category`, ctx.OrgID, spaceID, ctx.OrgID, ctx.OrgID, ctx.UserID, ctx.OrgID, ctx.UserID)

	if err == sql.ErrNoRows || len(c) == 0 {
		err = nil
		c = []category.Category{}
	}
	if err != nil {
		err = errors.Wrap(err, fmt.Sprintf("unable to execute select all categories for space %s", spaceID))
	}

	return
}

// Update saves category name change.
func (s Scope) Update(ctx domain.RequestContext, c category.Category) (err error) {
	c.Revised = time.Now().UTC()

	_, err = ctx.Transaction.NamedExec("UPDATE category SET category=:category, revised=:revised WHERE orgid=:orgid AND refid=:refid", c)

	if err != nil {
		err = errors.Wrap(err, fmt.Sprintf("unable to execute update for category %s", c.RefID))
	}

	return
}

// Get returns specified category
func (s Scope) Get(ctx domain.RequestContext, id string) (c category.Category, err error) {
	err = s.Runtime.Db.Get(&c, "SELECT id, refid, orgid, labelid, category, created, revised FROM category WHERE orgid=? AND refid=?",
		ctx.OrgID, id)

	if err != nil {
		err = errors.Wrap(err, fmt.Sprintf("unable to get category %s", id))
	}

	return
}

// Delete removes category from the store.
func (s Scope) Delete(ctx domain.RequestContext, id string) (rows int64, err error) {
	b := mysql.BaseQuery{}
	return b.DeleteConstrained(ctx.Transaction, "category", ctx.OrgID, id)
}

// AssociateDocument inserts category membership record into the category member table.
func (s Scope) AssociateDocument(ctx domain.RequestContext, m category.Member) (err error) {
	m.Created = time.Now().UTC()
	m.Revised = time.Now().UTC()

	_, err = ctx.Transaction.Exec("INSERT INTO categorymember (refid, orgid, categoryid, labelid, documentid, created, revised) VALUES (?, ?, ?, ?, ?, ?, ?)",
		m.RefID, m.OrgID, m.CategoryID, m.LabelID, m.DocumentID, m.Created, m.Revised)

	if err != nil {
		err = errors.Wrap(err, "unable to execute insert categorymember")
	}

	return
}

// DisassociateDocument removes document associatation from category.
func (s Scope) DisassociateDocument(ctx domain.RequestContext, categoryID, documentID string) (rows int64, err error) {
	b := mysql.BaseQuery{}

	sql := fmt.Sprintf("DELETE FROM categorymember WHERE orgid='%s' AND categoryid='%s' AND documentid='%s'",
		ctx.OrgID, categoryID, documentID)

	return b.DeleteWhere(ctx.Transaction, sql)
}

// RemoveCategoryMembership removes all category associations from the store.
func (s Scope) RemoveCategoryMembership(ctx domain.RequestContext, categoryID string) (rows int64, err error) {
	b := mysql.BaseQuery{}

	sql := fmt.Sprintf("DELETE FROM categorymember WHERE orgid='%s' AND categoryid='%s'",
		ctx.OrgID, categoryID)

	return b.DeleteWhere(ctx.Transaction, sql)
}

// RemoveSpaceCategoryMemberships removes all category associations from the store for the space.
func (s Scope) RemoveSpaceCategoryMemberships(ctx domain.RequestContext, spaceID string) (rows int64, err error) {
	b := mysql.BaseQuery{}

	sql := fmt.Sprintf("DELETE FROM categorymember WHERE orgid='%s' AND labelid='%s'",
		ctx.OrgID, spaceID)

	return b.DeleteWhere(ctx.Transaction, sql)
}

// RemoveDocumentCategories removes all document category associations from the store.
func (s Scope) RemoveDocumentCategories(ctx domain.RequestContext, documentID string) (rows int64, err error) {
	b := mysql.BaseQuery{}

	sql := fmt.Sprintf("DELETE FROM categorymember WHERE orgid='%s' AND documentid='%s'",
		ctx.OrgID, documentID)

	return b.DeleteWhere(ctx.Transaction, sql)
}

// DeleteBySpace removes all category and category associations for given space.
func (s Scope) DeleteBySpace(ctx domain.RequestContext, spaceID string) (rows int64, err error) {
	b := mysql.BaseQuery{}

	s1 := fmt.Sprintf("DELETE FROM categorymember WHERE orgid='%s' AND labelid='%s'", ctx.OrgID, spaceID)
	b.DeleteWhere(ctx.Transaction, s1)

	s2 := fmt.Sprintf("DELETE FROM category WHERE orgid='%s' AND labelid='%s'", ctx.OrgID, spaceID)
	return b.DeleteWhere(ctx.Transaction, s2)
}

// GetSpaceCategorySummary returns number of documents and users for space categories.
func (s Scope) GetSpaceCategorySummary(ctx domain.RequestContext, spaceID string) (c []category.SummaryModel, err error) {
	err = s.Runtime.Db.Select(&c, `
		SELECT 'documents' as type, categoryid, COUNT(*) as count
			FROM categorymember
			WHERE orgid=? AND labelid=? GROUP BY categoryid, type
		UNION ALL
		SELECT 'users' as type, refid AS categoryid, count(*) AS count
			FROM permission
			WHERE orgid=? AND location='category'
				AND refid IN (SELECT refid FROM category WHERE orgid=? AND labelid=?)
			GROUP BY refid, type`,
		ctx.OrgID, spaceID, ctx.OrgID, ctx.OrgID, spaceID /*, ctx.OrgID, ctx.OrgID, spaceID*/)

	if err == sql.ErrNoRows || len(c) == 0 {
		err = nil
		c = []category.SummaryModel{}
	}
	if err != nil {
		err = errors.Wrap(err, fmt.Sprintf("select category summary for space %s", spaceID))
	}

	return
}

// GetDocumentCategoryMembership returns all space categories associated with given document.
func (s Scope) GetDocumentCategoryMembership(ctx domain.RequestContext, documentID string) (c []category.Category, err error) {
	err = s.Runtime.Db.Select(&c, `
		SELECT id, refid, orgid, labelid, category, created, revised FROM category
		WHERE orgid=? AND refid IN (SELECT categoryid FROM categorymember WHERE orgid=? AND documentid=?)`, ctx.OrgID, ctx.OrgID, documentID)

	if err == sql.ErrNoRows {
		err = nil
	}
	if err != nil {
		err = errors.Wrap(err, fmt.Sprintf("unable to execute select categories for document %s", documentID))
	}

	return
}

// GetSpaceCategoryMembership returns category/document associations within space.
func (s Scope) GetSpaceCategoryMembership(ctx domain.RequestContext, spaceID string) (c []category.Member, err error) {
	err = s.Runtime.Db.Select(&c, `
		SELECT id, refid, orgid, labelid, categoryid, documentid, created, revised FROM categorymember
		WHERE orgid=? AND labelid=?
			  AND labelid IN (SELECT refid FROM permission WHERE orgid=? AND location='space' AND refid IN (
				SELECT refid from permission WHERE orgid=? AND who='user' AND (whoid=? OR whoid='0') AND location='space' AND action='view' UNION ALL
				SELECT p.refid from permission p LEFT JOIN rolemember r ON p.whoid=r.roleid WHERE p.orgid=? AND p.who='role' AND p.location='space'
					AND p.action='view' AND (r.userid=? OR r.userid='0')
		))
	  ORDER BY documentid`, ctx.OrgID, spaceID, ctx.OrgID, ctx.OrgID, ctx.UserID, ctx.OrgID, ctx.UserID)

	if err == sql.ErrNoRows {
		err = nil
	}
	if err != nil {
		err = errors.Wrap(err, fmt.Sprintf("select all category/document membership for space %s", spaceID))
	}

	return
}
<commit_msg>Clean up category code/formatting for MySQL store<commit_after>// Copyright 2016 Documize Inc. <legal@documize.com>. All rights reserved.
//
// This software (Documize Community Edition) is licensed under
// GNU AGPL v3 http://www.gnu.org/licenses/agpl-3.0.en.html
//
// You can operate outside the AGPL restrictions by purchasing
// Documize Enterprise Edition and obtaining a commercial license
// by contacting <sales@documize.com>.
//
// https://documize.com

// Package mysql handles data persistence for both category definition
// and and document/category association.
package mysql

import (
	"database/sql"
	"fmt"
	"time"

	"github.com/documize/community/core/env"
	"github.com/documize/community/domain"
	"github.com/documize/community/domain/store/mysql"
	"github.com/documize/community/model/category"
	"github.com/pkg/errors"
)

// Scope provides data access to MySQL.
type Scope struct {
	Runtime *env.Runtime
}

// Add inserts the given record into the category table.
func (s Scope) Add(ctx domain.RequestContext, c category.Category) (err error) {
	c.Created = time.Now().UTC()
	c.Revised = time.Now().UTC()

	_, err = ctx.Transaction.Exec("INSERT INTO category (refid, orgid, labelid, category, created, revised) VALUES (?, ?, ?, ?, ?, ?)",
		c.RefID, c.OrgID, c.LabelID, c.Category, c.Created, c.Revised)

	if err != nil {
		err = errors.Wrap(err, "unable to execute insert category")
	}

	return
}

// GetBySpace returns space categories accessible by user.
// Context is used to for user ID.
func (s Scope) GetBySpace(ctx domain.RequestContext, spaceID string) (c []category.Category, err error) {
	err = s.Runtime.Db.Select(&c, `
		SELECT id, refid, orgid, labelid, category, created, revised FROM category
		WHERE orgid=? AND labelid=?
			  AND refid IN (SELECT refid FROM permission WHERE orgid=? AND location='category' AND refid IN (
				SELECT refid from permission WHERE orgid=? AND who='user' AND (whoid=? OR whoid='0') AND location='category' UNION ALL
				SELECT p.refid from permission p LEFT JOIN rolemember r ON p.whoid=r.roleid
					WHERE p.orgid=? AND p.who='role' AND p.location='category' AND (r.userid=? OR r.userid='0')
		))
	  ORDER BY category`, ctx.OrgID, spaceID, ctx.OrgID, ctx.OrgID, ctx.UserID, ctx.OrgID, ctx.UserID)

	if err == sql.ErrNoRows {
		err = nil
	}
	if err != nil {
		err = errors.Wrap(err, fmt.Sprintf("unable to execute select categories for space %s", spaceID))
	}

	return
}

// GetAllBySpace returns all space categories.
func (s Scope) GetAllBySpace(ctx domain.RequestContext, spaceID string) (c []category.Category, err error) {
	err = s.Runtime.Db.Select(&c, `
		SELECT id, refid, orgid, labelid, category, created, revised FROM category
		WHERE orgid=? AND labelid=?
			  AND labelid IN (SELECT refid FROM permission WHERE orgid=? AND location='space' AND refid IN (
				SELECT refid from permission WHERE orgid=? AND who='user' AND (whoid=? OR whoid='0') AND location='space' AND action='view' UNION ALL
				SELECT p.refid from permission p LEFT JOIN rolemember r ON p.whoid=r.roleid WHERE p.orgid=? AND p.who='role' AND p.location='space'
					AND p.action='view' AND (r.userid=? OR r.userid='0')
		))
	  ORDER BY category`, ctx.OrgID, spaceID, ctx.OrgID, ctx.OrgID, ctx.UserID, ctx.OrgID, ctx.UserID)

	if err == sql.ErrNoRows || len(c) == 0 {
		err = nil
		c = []category.Category{}
	}
	if err != nil {
		err = errors.Wrap(err, fmt.Sprintf("unable to execute select all categories for space %s", spaceID))
	}

	return
}

// Update saves category name change.
func (s Scope) Update(ctx domain.RequestContext, c category.Category) (err error) {
	c.Revised = time.Now().UTC()

	_, err = ctx.Transaction.NamedExec("UPDATE category SET category=:category, revised=:revised WHERE orgid=:orgid AND refid=:refid", c)

	if err != nil {
		err = errors.Wrap(err, fmt.Sprintf("unable to execute update for category %s", c.RefID))
	}

	return
}

// Get returns specified category
func (s Scope) Get(ctx domain.RequestContext, id string) (c category.Category, err error) {
	err = s.Runtime.Db.Get(&c, "SELECT id, refid, orgid, labelid, category, created, revised FROM category WHERE orgid=? AND refid=?",
		ctx.OrgID, id)

	if err != nil {
		err = errors.Wrap(err, fmt.Sprintf("unable to get category %s", id))
	}

	return
}

// Delete removes category from the store.
func (s Scope) Delete(ctx domain.RequestContext, id string) (rows int64, err error) {
	b := mysql.BaseQuery{}
	return b.DeleteConstrained(ctx.Transaction, "category", ctx.OrgID, id)
}

// AssociateDocument inserts category membership record into the category member table.
func (s Scope) AssociateDocument(ctx domain.RequestContext, m category.Member) (err error) {
	m.Created = time.Now().UTC()
	m.Revised = time.Now().UTC()

	_, err = ctx.Transaction.Exec("INSERT INTO categorymember (refid, orgid, categoryid, labelid, documentid, created, revised) VALUES (?, ?, ?, ?, ?, ?, ?)",
		m.RefID, m.OrgID, m.CategoryID, m.LabelID, m.DocumentID, m.Created, m.Revised)

	if err != nil {
		err = errors.Wrap(err, "unable to execute insert categorymember")
	}

	return
}

// DisassociateDocument removes document associatation from category.
func (s Scope) DisassociateDocument(ctx domain.RequestContext, categoryID, documentID string) (rows int64, err error) {
	b := mysql.BaseQuery{}

	sql := fmt.Sprintf("DELETE FROM categorymember WHERE orgid='%s' AND categoryid='%s' AND documentid='%s'",
		ctx.OrgID, categoryID, documentID)

	return b.DeleteWhere(ctx.Transaction, sql)
}

// RemoveCategoryMembership removes all category associations from the store.
func (s Scope) RemoveCategoryMembership(ctx domain.RequestContext, categoryID string) (rows int64, err error) {
	b := mysql.BaseQuery{}

	sql := fmt.Sprintf("DELETE FROM categorymember WHERE orgid='%s' AND categoryid='%s'",
		ctx.OrgID, categoryID)

	return b.DeleteWhere(ctx.Transaction, sql)
}

// RemoveSpaceCategoryMemberships removes all category associations from the store for the space.
func (s Scope) RemoveSpaceCategoryMemberships(ctx domain.RequestContext, spaceID string) (rows int64, err error) {
	b := mysql.BaseQuery{}

	sql := fmt.Sprintf("DELETE FROM categorymember WHERE orgid='%s' AND labelid='%s'",
		ctx.OrgID, spaceID)

	return b.DeleteWhere(ctx.Transaction, sql)
}

// RemoveDocumentCategories removes all document category associations from the store.
func (s Scope) RemoveDocumentCategories(ctx domain.RequestContext, documentID string) (rows int64, err error) {
	b := mysql.BaseQuery{}

	sql := fmt.Sprintf("DELETE FROM categorymember WHERE orgid='%s' AND documentid='%s'",
		ctx.OrgID, documentID)

	return b.DeleteWhere(ctx.Transaction, sql)
}

// DeleteBySpace removes all category and category associations for given space.
func (s Scope) DeleteBySpace(ctx domain.RequestContext, spaceID string) (rows int64, err error) {
	b := mysql.BaseQuery{}

	s1 := fmt.Sprintf("DELETE FROM categorymember WHERE orgid='%s' AND labelid='%s'", ctx.OrgID, spaceID)
	b.DeleteWhere(ctx.Transaction, s1)

	s2 := fmt.Sprintf("DELETE FROM category WHERE orgid='%s' AND labelid='%s'", ctx.OrgID, spaceID)
	return b.DeleteWhere(ctx.Transaction, s2)
}

// GetSpaceCategorySummary returns number of documents and users for space categories.
func (s Scope) GetSpaceCategorySummary(ctx domain.RequestContext, spaceID string) (c []category.SummaryModel, err error) {
	err = s.Runtime.Db.Select(&c, `
		SELECT 'documents' as type, categoryid, COUNT(*) as count
			FROM categorymember
			WHERE orgid=? AND labelid=? GROUP BY categoryid, type
		UNION ALL
		SELECT 'users' as type, refid AS categoryid, count(*) AS count
			FROM permission
			WHERE orgid=? AND location='category'
				AND refid IN (SELECT refid FROM category WHERE orgid=? AND labelid=?)
			GROUP BY refid, type`,
		ctx.OrgID, spaceID, ctx.OrgID, ctx.OrgID, spaceID /*, ctx.OrgID, ctx.OrgID, spaceID*/)

	if err == sql.ErrNoRows || len(c) == 0 {
		err = nil
		c = []category.SummaryModel{}
	}
	if err != nil {
		err = errors.Wrap(err, fmt.Sprintf("select category summary for space %s", spaceID))
	}

	return
}

// GetDocumentCategoryMembership returns all space categories associated with given document.
func (s Scope) GetDocumentCategoryMembership(ctx domain.RequestContext, documentID string) (c []category.Category, err error) {
	err = s.Runtime.Db.Select(&c, `
		SELECT id, refid, orgid, labelid, category, created, revised FROM category
		WHERE orgid=? AND refid IN (SELECT categoryid FROM categorymember WHERE orgid=? AND documentid=?)`, ctx.OrgID, ctx.OrgID, documentID)

	if err == sql.ErrNoRows || len(c) == 0 {
		c = []category.Category{}
		err = nil
	}
	if err != nil {
		err = errors.Wrap(err, fmt.Sprintf("unable to execute select categories for document %s", documentID))
	}

	return
}

// GetSpaceCategoryMembership returns category/document associations within space.
func (s Scope) GetSpaceCategoryMembership(ctx domain.RequestContext, spaceID string) (c []category.Member, err error) {
	err = s.Runtime.Db.Select(&c, `
		SELECT id, refid, orgid, labelid, categoryid, documentid, created, revised FROM categorymember
		WHERE orgid=? AND labelid=?
			  AND labelid IN (SELECT refid FROM permission WHERE orgid=? AND location='space' AND refid IN (
				SELECT refid from permission WHERE orgid=? AND who='user' AND (whoid=? OR whoid='0') AND location='space' AND action='view' UNION ALL
				SELECT p.refid from permission p LEFT JOIN rolemember r ON p.whoid=r.roleid WHERE p.orgid=? AND p.who='role' AND p.location='space'
					AND p.action='view' AND (r.userid=? OR r.userid='0')
		))
	  ORDER BY documentid`, ctx.OrgID, spaceID, ctx.OrgID, ctx.OrgID, ctx.UserID, ctx.OrgID, ctx.UserID)

	if err == sql.ErrNoRows {
		err = nil
	}
	if err != nil {
		err = errors.Wrap(err, fmt.Sprintf("select all category/document membership for space %s", spaceID))
	}

	return
}
<|endoftext|>


<commit_before>package download

import (
	"errors"
	"log"
	"os"
	"path/filepath"
	"strings"

	"golang.org/x/tools/go/vcs"
)

// Download takes a user-provided string that represents a remote
// Go repository, and attempts to download it in a way similar to go get.
// It is forgiving in terms of the exact path given: the path may have
// a scheme or username, which will be trimmed.
func Download(path, dest string) (root *vcs.RepoRoot, err error) {
	return download(path, dest, false)
}

func download(path, dest string, firstAttempt bool) (root *vcs.RepoRoot, err error) {
	vcs.ShowCmd = true

	path, err = Clean(path)
	if err != nil {
		return root, err
	}

	root, err = vcs.RepoRootForImportPath(path, true)
	if err != nil {
		return root, err
	}

	localDirPath := filepath.Join(dest, root.Root, "..")

	err = os.MkdirAll(localDirPath, 0777)
	if err != nil {
		return root, err
	}

	fullLocalPath := filepath.Join(dest, root.Root)
	ex, err := exists(fullLocalPath)
	if err != nil {
		return root, err
	}
	if ex {
		log.Println("Update", root.Repo)
		err = root.VCS.Download(fullLocalPath)
		if err != nil && firstAttempt {
			// may have been rebased; we delete the directory, then try one more time:
			log.Printf("Failed to download %q (%v), trying again...", root.Repo, err.Error())
			err = os.Remove(fullLocalPath)
			return download(path, dest, false)
		} else if err != nil {
			return root, err
		}
	} else {
		log.Println("Create", root.Repo)

		err = root.VCS.Create(fullLocalPath, root.Repo)
		if err != nil {
			return root, err
		}
	}
	err = root.VCS.TagSync(fullLocalPath, "")
	if err != nil && firstAttempt {
		// may have been rebased; we delete the directory, then try one more time:
		log.Printf("Failed to update %q (%v), trying again...", root.Repo, err.Error())
		err = os.Remove(fullLocalPath)
		return download(path, dest, false)
	}
	return root, err
}

// Clean trims any URL parts, like the scheme or username, that might be present
// in a user-submitted URL
func Clean(path string) (string, error) {
	importPath := trimUsername(trimScheme(path))
	root, err := vcs.RepoRootForImportPath(importPath, true)
	if err != nil {
		return "", err
	}
	if root != nil && (root.Root == "" || root.Repo == "") {
		return root.Root, errors.New("empty repo root")
	}
	return root.Root, err
}

// trimScheme removes a scheme (e.g. https://) from the URL for more
// convenient pasting from browsers.
func trimScheme(repo string) string {
	schemeSep := "://"
	schemeSepIdx := strings.Index(repo, schemeSep)
	if schemeSepIdx > -1 {
		return repo[schemeSepIdx+len(schemeSep):]
	}

	return repo
}

// trimUsername removes the username for a URL, if it is present
func trimUsername(repo string) string {
	usernameSep := "@"
	usernameSepIdx := strings.Index(repo, usernameSep)
	if usernameSepIdx > -1 {
		return repo[usernameSepIdx+len(usernameSep):]
	}

	return repo
}

// exists returns whether the given file or directory exists or not
// from http://stackoverflow.com/a/10510783
func exists(path string) (bool, error) {
	_, err := os.Stat(path)
	if err == nil {
		return true, nil
	}
	if os.IsNotExist(err) {
		return false, nil
	}
	return true, err
}
<commit_msg>Closes #150 First attempt should be true, not false<commit_after>package download

import (
	"errors"
	"log"
	"os"
	"path/filepath"
	"strings"

	"golang.org/x/tools/go/vcs"
)

// Download takes a user-provided string that represents a remote
// Go repository, and attempts to download it in a way similar to go get.
// It is forgiving in terms of the exact path given: the path may have
// a scheme or username, which will be trimmed.
func Download(path, dest string) (root *vcs.RepoRoot, err error) {
	return download(path, dest, true)
}

func download(path, dest string, firstAttempt bool) (root *vcs.RepoRoot, err error) {
	vcs.ShowCmd = true

	path, err = Clean(path)
	if err != nil {
		return root, err
	}

	root, err = vcs.RepoRootForImportPath(path, true)
	if err != nil {
		return root, err
	}

	localDirPath := filepath.Join(dest, root.Root, "..")

	err = os.MkdirAll(localDirPath, 0777)
	if err != nil {
		return root, err
	}

	fullLocalPath := filepath.Join(dest, root.Root)
	ex, err := exists(fullLocalPath)
	if err != nil {
		return root, err
	}
	if ex {
		log.Println("Update", root.Repo)
		err = root.VCS.Download(fullLocalPath)
		if err != nil && firstAttempt {
			// may have been rebased; we delete the directory, then try one more time:
			log.Printf("Failed to download %q (%v), trying again...", root.Repo, err.Error())
			err = os.Remove(fullLocalPath)
			return download(path, dest, false)
		} else if err != nil {
			return root, err
		}
	} else {
		log.Println("Create", root.Repo)

		err = root.VCS.Create(fullLocalPath, root.Repo)
		if err != nil {
			return root, err
		}
	}
	err = root.VCS.TagSync(fullLocalPath, "")
	if err != nil && firstAttempt {
		// may have been rebased; we delete the directory, then try one more time:
		log.Printf("Failed to update %q (%v), trying again...", root.Repo, err.Error())
		err = os.Remove(fullLocalPath)
		return download(path, dest, false)
	}
	return root, err
}

// Clean trims any URL parts, like the scheme or username, that might be present
// in a user-submitted URL
func Clean(path string) (string, error) {
	importPath := trimUsername(trimScheme(path))
	root, err := vcs.RepoRootForImportPath(importPath, true)
	if err != nil {
		return "", err
	}
	if root != nil && (root.Root == "" || root.Repo == "") {
		return root.Root, errors.New("empty repo root")
	}
	return root.Root, err
}

// trimScheme removes a scheme (e.g. https://) from the URL for more
// convenient pasting from browsers.
func trimScheme(repo string) string {
	schemeSep := "://"
	schemeSepIdx := strings.Index(repo, schemeSep)
	if schemeSepIdx > -1 {
		return repo[schemeSepIdx+len(schemeSep):]
	}

	return repo
}

// trimUsername removes the username for a URL, if it is present
func trimUsername(repo string) string {
	usernameSep := "@"
	usernameSepIdx := strings.Index(repo, usernameSep)
	if usernameSepIdx > -1 {
		return repo[usernameSepIdx+len(usernameSep):]
	}

	return repo
}

// exists returns whether the given file or directory exists or not
// from http://stackoverflow.com/a/10510783
func exists(path string) (bool, error) {
	_, err := os.Stat(path)
	if err == nil {
		return true, nil
	}
	if os.IsNotExist(err) {
		return false, nil
	}
	return true, err
}
<|endoftext|>