<commit_before>package slack

// OutgoingMessage is used for the realtime API, and seems incomplete.
type OutgoingMessage struct {
	ID int `json:"id"`
	// channel ID
	Channel         string `json:"channel,omitempty"`
	Text            string `json:"text,omitempty"`
	Type            string `json:"type,omitempty"`
	ThreadTimestamp string `json:"thread_ts,omitempty"`
}

// Message is an auxiliary type to allow us to have a message containing sub messages
type Message struct {
	Msg
	SubMessage *Msg `json:"message,omitempty"`
}

// Msg contains information about a slack message
type Msg struct {
	// Basic Message
	Type            string       `json:"type,omitempty"`
	Channel         string       `json:"channel,omitempty"`
	User            string       `json:"user,omitempty"`
	Text            string       `json:"text,omitempty"`
	Timestamp       string       `json:"ts,omitempty"`
	ThreadTimestamp string       `json:"thread_ts,omitempty"`
	IsStarred       bool         `json:"is_starred,omitempty"`
	PinnedTo        []string     `json:"pinned_to, omitempty"`
	Attachments     []Attachment `json:"attachments,omitempty"`
	Edited          *Edited      `json:"edited,omitempty"`
	LastRead        string       `json:"last_read,omitempty"`
	Subscribed      bool         `json:"subscribed,omitempty"`
	UnreadCount     int          `json:"unread_count,omitempty"`

	// Message Subtypes
	SubType string `json:"subtype,omitempty"`

	// Hidden Subtypes
	Hidden           bool   `json:"hidden,omitempty"`     // message_changed, message_deleted, unpinned_item
	DeletedTimestamp string `json:"deleted_ts,omitempty"` // message_deleted
	EventTimestamp   string `json:"event_ts,omitempty"`

	// bot_message (https://api.slack.com/events/message/bot_message)
	BotID    string `json:"bot_id,omitempty"`
	Username string `json:"username,omitempty"`
	Icons    *Icon  `json:"icons,omitempty"`

	// channel_join, group_join
	Inviter string `json:"inviter,omitempty"`

	// channel_topic, group_topic
	Topic string `json:"topic,omitempty"`

	// channel_purpose, group_purpose
	Purpose string `json:"purpose,omitempty"`

	// channel_name, group_name
	Name    string `json:"name,omitempty"`
	OldName string `json:"old_name,omitempty"`

	// channel_archive, group_archive
	Members []string `json:"members,omitempty"`

	// channels.replies, groups.replies, im.replies, mpim.replies
	ReplyCount   int     `json:"reply_count,omitempty"`
	Replies      []Reply `json:"replies,omitempty"`
	ParentUserId string  `json:"parent_user_id,omitempty"`

	// file_share, file_comment, file_mention
	File *File `json:"file,omitempty"`

	// file_share
	Upload bool `json:"upload,omitempty"`

	// file_comment
	Comment *Comment `json:"comment,omitempty"`

	// pinned_item
	ItemType string `json:"item_type,omitempty"`

	// https://api.slack.com/rtm
	ReplyTo int    `json:"reply_to,omitempty"`
	Team    string `json:"team,omitempty"`

	// reactions
	Reactions []ItemReaction `json:"reactions,omitempty"`

	// slash commands and interactive messages
	ResponseType    string `json:"response_type,omitempty"`
	ReplaceOriginal bool   `json:"replace_original,omitempty"`
}

// Icon is used for bot messages
type Icon struct {
	IconURL   string `json:"icon_url,omitempty"`
	IconEmoji string `json:"icon_emoji,omitempty"`
}

// Edited indicates that a message has been edited.
type Edited struct {
	User      string `json:"user,omitempty"`
	Timestamp string `json:"ts,omitempty"`
}

// Reply contains information about a reply for a thread
type Reply struct {
	User      string `json:"user,omitempty"`
	Timestamp string `json:"ts,omitempty"`
}

// Event contains the event type
type Event struct {
	Type string `json:"type,omitempty"`
}

// Ping contains information about a Ping Event
type Ping struct {
	ID   int    `json:"id"`
	Type string `json:"type"`
}

// Pong contains information about a Pong Event
type Pong struct {
	Type    string `json:"type"`
	ReplyTo int    `json:"reply_to"`
}

// NewOutgoingMessage prepares an OutgoingMessage that the user can
// use to send a message. Use this function to properly set the
// messageID.
func (rtm *RTM) NewOutgoingMessage(text string, channelID string) *OutgoingMessage {
	id := rtm.idGen.Next()
	return &OutgoingMessage{
		ID:      id,
		Type:    "message",
		Channel: channelID,
		Text:    text,
	}
}

// NewTypingMessage prepares an OutgoingMessage that the user can
// use to send as a typing indicator. Use this function to properly set the
// messageID.
func (rtm *RTM) NewTypingMessage(channelID string) *OutgoingMessage {
	id := rtm.idGen.Next()
	return &OutgoingMessage{
		ID:      id,
		Type:    "typing",
		Channel: channelID,
	}
}
<commit_msg>add missing Msg.DeleteOriginal field<commit_after>package slack

// OutgoingMessage is used for the realtime API, and seems incomplete.
type OutgoingMessage struct {
	ID int `json:"id"`
	// channel ID
	Channel         string `json:"channel,omitempty"`
	Text            string `json:"text,omitempty"`
	Type            string `json:"type,omitempty"`
	ThreadTimestamp string `json:"thread_ts,omitempty"`
}

// Message is an auxiliary type to allow us to have a message containing sub messages
type Message struct {
	Msg
	SubMessage *Msg `json:"message,omitempty"`
}

// Msg contains information about a slack message
type Msg struct {
	// Basic Message
	Type            string       `json:"type,omitempty"`
	Channel         string       `json:"channel,omitempty"`
	User            string       `json:"user,omitempty"`
	Text            string       `json:"text,omitempty"`
	Timestamp       string       `json:"ts,omitempty"`
	ThreadTimestamp string       `json:"thread_ts,omitempty"`
	IsStarred       bool         `json:"is_starred,omitempty"`
	PinnedTo        []string     `json:"pinned_to, omitempty"`
	Attachments     []Attachment `json:"attachments,omitempty"`
	Edited          *Edited      `json:"edited,omitempty"`
	LastRead        string       `json:"last_read,omitempty"`
	Subscribed      bool         `json:"subscribed,omitempty"`
	UnreadCount     int          `json:"unread_count,omitempty"`

	// Message Subtypes
	SubType string `json:"subtype,omitempty"`

	// Hidden Subtypes
	Hidden           bool   `json:"hidden,omitempty"`     // message_changed, message_deleted, unpinned_item
	DeletedTimestamp string `json:"deleted_ts,omitempty"` // message_deleted
	EventTimestamp   string `json:"event_ts,omitempty"`

	// bot_message (https://api.slack.com/events/message/bot_message)
	BotID    string `json:"bot_id,omitempty"`
	Username string `json:"username,omitempty"`
	Icons    *Icon  `json:"icons,omitempty"`

	// channel_join, group_join
	Inviter string `json:"inviter,omitempty"`

	// channel_topic, group_topic
	Topic string `json:"topic,omitempty"`

	// channel_purpose, group_purpose
	Purpose string `json:"purpose,omitempty"`

	// channel_name, group_name
	Name    string `json:"name,omitempty"`
	OldName string `json:"old_name,omitempty"`

	// channel_archive, group_archive
	Members []string `json:"members,omitempty"`

	// channels.replies, groups.replies, im.replies, mpim.replies
	ReplyCount   int     `json:"reply_count,omitempty"`
	Replies      []Reply `json:"replies,omitempty"`
	ParentUserId string  `json:"parent_user_id,omitempty"`

	// file_share, file_comment, file_mention
	File *File `json:"file,omitempty"`

	// file_share
	Upload bool `json:"upload,omitempty"`

	// file_comment
	Comment *Comment `json:"comment,omitempty"`

	// pinned_item
	ItemType string `json:"item_type,omitempty"`

	// https://api.slack.com/rtm
	ReplyTo int    `json:"reply_to,omitempty"`
	Team    string `json:"team,omitempty"`

	// reactions
	Reactions []ItemReaction `json:"reactions,omitempty"`

	// slash commands and interactive messages
	ResponseType    string `json:"response_type,omitempty"`
	ReplaceOriginal bool   `json:"replace_original,omitempty"`
	DeleteOriginal  bool   `json:"delete_original,omitempty"`
}

// Icon is used for bot messages
type Icon struct {
	IconURL   string `json:"icon_url,omitempty"`
	IconEmoji string `json:"icon_emoji,omitempty"`
}

// Edited indicates that a message has been edited.
type Edited struct {
	User      string `json:"user,omitempty"`
	Timestamp string `json:"ts,omitempty"`
}

// Reply contains information about a reply for a thread
type Reply struct {
	User      string `json:"user,omitempty"`
	Timestamp string `json:"ts,omitempty"`
}

// Event contains the event type
type Event struct {
	Type string `json:"type,omitempty"`
}

// Ping contains information about a Ping Event
type Ping struct {
	ID   int    `json:"id"`
	Type string `json:"type"`
}

// Pong contains information about a Pong Event
type Pong struct {
	Type    string `json:"type"`
	ReplyTo int    `json:"reply_to"`
}

// NewOutgoingMessage prepares an OutgoingMessage that the user can
// use to send a message. Use this function to properly set the
// messageID.
func (rtm *RTM) NewOutgoingMessage(text string, channelID string) *OutgoingMessage {
	id := rtm.idGen.Next()
	return &OutgoingMessage{
		ID:      id,
		Type:    "message",
		Channel: channelID,
		Text:    text,
	}
}

// NewTypingMessage prepares an OutgoingMessage that the user can
// use to send as a typing indicator. Use this function to properly set the
// messageID.
func (rtm *RTM) NewTypingMessage(channelID string) *OutgoingMessage {
	id := rtm.idGen.Next()
	return &OutgoingMessage{
		ID:      id,
		Type:    "typing",
		Channel: channelID,
	}
}
<|endoftext|>


<commit_before>package main

import (
	"os"
	"os/user"
	"strconv"
	"strings"

	. "github.com/reujab/bronze/types"
)

func dirSegment(segment *Segment) {
	usr, err := user.Current()
	die(err)
	segment.Value, err = os.Getwd()
	die(err)
	length, _ := strconv.Atoi(os.Getenv("BRONZE_DIR_LENGTH"))

	if strings.HasPrefix(segment.Value, usr.HomeDir) {
		segment.Value = icons["home"] + segment.Value[len(usr.HomeDir):]
	}

	if length != 0 {
		dirs := strings.Split(segment.Value, string(os.PathSeparator))
		for i := len(dirs) - 2; i >= 0; i-- {
			runes := []rune(dirs[i])
			if len(runes) > length {
				dirs[i] = string(runes[:length])
			}
		}

		segment.Value = strings.Join(dirs, string(os.PathSeparator))
	}
}
<commit_msg>added BRONZE_DIR_ALIASES<commit_after>package main

import (
	"os"
	"os/user"
	"path/filepath"
	"strconv"
	"strings"

	. "github.com/reujab/bronze/types"
)

func dirSegment(segment *Segment) {
	var aliases []string
	if os.Getenv("BRONZE_DIR_ALIASES") != "" {
		aliases = strings.Split(os.Getenv("BRONZE_DIR_ALIASES"), ":")
	}
	if len(aliases)%2 != 0 {
		dief("dir: invalid BRONZE_DIR_ALIASES")
	}

	usr, err := user.Current()
	die(err)
	segment.Value, err = os.Getwd()
	die(err)
	aliases = append(aliases, usr.HomeDir, icons["home"])

	for i := 0; i < len(aliases); i += 2 {
		prefix := filepath.Clean(aliases[i])
		icon := aliases[i+1]
		if strings.HasPrefix(segment.Value, prefix) {
			segment.Value = icon + segment.Value[len(prefix):]
			break
		}
	}

	length, _ := strconv.Atoi(os.Getenv("BRONZE_DIR_LENGTH"))
	if length != 0 {
		dirs := strings.Split(segment.Value, string(os.PathSeparator))
		for i := len(dirs) - 2; i >= 0; i-- {
			runes := []rune(dirs[i])
			if len(runes) > length {
				dirs[i] = string(runes[:length])
			}
		}

		segment.Value = strings.Join(dirs, string(os.PathSeparator))
	}
}
<|endoftext|>


<commit_before>/*
Example use:

	import (
		"net/url"
	)

	base, err := url.Parse("http://example.com")
	c, err := New(base)

	var success string
	var otherSuccess string
	var fail string
	c.Get(&Params{
		Path: "path/to/resource",
		Headers: http.Header{"One-Off": []string{"one"}},
		Query: url.Values{"testquery": []string{"test-override"}},
		Body:         "hello",
		UnmarshalMap: UnmarshalMap{
			200: &success,
			201: &otherSuccess,
			202: &otherSuccess,
			400: &fail,
			404: &fail,
		},
	})

*/
package grestclient
<commit_msg>Alter doc.go<commit_after>/*
Example use:

	import (
		"net/url"
	)

	base, err := url.Parse("http://example.com")
	c, err := New(base)

	var success string
	var otherSuccess string
	var fail string
	c.Get(&Params{
		Path: "path/to/resource",
		Headers: http.Header{"One-Off": []string{"one"}},
		Query: url.Values{"testquery": []string{"test-override"}},
		UnmarshalMap: UnmarshalMap{
			200: &success,
			201: &otherSuccess,
			202: &otherSuccess,
			400: &fail,
			404: &fail,
		},
	})

	c.Post(&Params{
		Path: "path/to/resource",
		Headers: http.Header{"One-Off": []string{"one"}},
		Query: url.Values{"testquery": []string{"test-override"}},
		Body: "hello",
		UnmarshalMap: UnmarshalMap{
			200: &success,
			201: &otherSuccess,
			202: &otherSuccess,
			400: &fail,
			404: &fail,
		},
	})

*/
package grestclient
<|endoftext|>


<commit_before>// Package recovr is a HTTP middleware that catches any panics and serves a proper error response.
//
// 	package main
//
// 	import (
// 		"log"
// 		"net/http"
// 		"github.com/dre1080/recovr"
// 	)
//
// 	var myPanicHandler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// 		panic("you should not have a handler that just panics ;)")
// 	})
//
// 	func main() {
// 		recovery := recovr.New()
// 		app := recovery(myPanicHandler)
// 		http.ListenAndServe("0.0.0.0:3000", app)
// 	}
package recovr
<commit_msg>docs: remove unnecessary import<commit_after>// Package recovr is a HTTP middleware that catches any panics and serves a proper error response.
//
// 	package main
//
// 	import (
// 		"net/http"
//
// 		"github.com/dre1080/recovr"
// 	)
//
// 	var myPanicHandler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// 		panic("you should not have a handler that just panics ;)")
// 	})
//
// 	func main() {
// 		recovery := recovr.New()
// 		app := recovery(myPanicHandler)
// 		http.ListenAndServe("0.0.0.0:3000", app)
// 	}
package recovr
<|endoftext|>


<commit_before>// Copyright 2015, Joe Tsai. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE.md file.

package main

import "os"
import "fmt"
import "strings"
import "io/ioutil"
import "path"
import "path/filepath"
import "unicode"
import "unicode/utf8"
import "go/doc"
import "go/build"
import "go/token"
import "go/parser"

var args []string
var matchCase = false
var unexported = false
var showCmd = false

func handleDoc(t *tokenizer) {
	if tok, old := t.Next(); old {
		switch tok {
		case "-c", "--c":
			matchCase = true
		case "-u", "--u":
			unexported = true
		case "-cmd", "--cmd":
			showCmd = true
		default:
			args = append(args, tok)
		}
	} else {
		if strings.Index(tok, "-") == 0 {
			// go doc [-u] [-c] [-cmd]
			for _, s := range []string{"-u", "-c", "-cmd"} {
				if strings.Index(s, tok) == 0 {
					fmt.Println(s + " ")
				}
			}
		} else {
			switch len(args) {
			case 0:
				// go doc
				// go doc <sym>[.<method>]
				relPkg := false
				if cwd, err := os.Getwd(); err == nil {
					relPkg = suggestPackageContents(cwd, "", tok)
				}

				// go doc <pkg>
				if len(tok) > 0 || !relPkg {
					suggestPackages(tok)
				}

				// go doc [<pkg>].<sym>[.<method>]
				root, base := path.Split(tok)
				if i := strings.Index(base, "."); len(tok) > 0 && i > 0 {
					i += len(root)
					for _, pkgPath := range makePaths(tok[:i]) {
						tokRoot, tok := tok[:i+1], tok[i+1:]
						if suggestPackageContents(pkgPath, tokRoot, tok) {
							break
						}
					}
				}
			case 1:
				// go doc <pkg> <sym>[.<method>]
				for _, pkgPath := range makePaths(args[0]) {
					if suggestPackageContents(pkgPath, "", tok) {
						break
					}
				}
			}
		}
		return
	}
	handleDoc(t)
}

// Parse a package and suggest all symbols found in it.
func suggestPackageContents(pkgPath, tokRoot, tok string) bool {
	name, pkg := parsePackage(pkgPath)
	if pkg == nil || (name == "main" && !showCmd) {
		return false
	}

	for _, c := range pkg.Consts {
		for _, n := range c.Names {
			suggestSymbol(tokRoot, tok, n)
		}
	}
	for _, v := range pkg.Vars {
		for _, n := range v.Names {
			suggestSymbol(tokRoot, tok, n)
		}
	}
	for _, f := range pkg.Funcs {
		suggestSymbol(tokRoot, tok, f.Name)
	}
	for _, t := range pkg.Types {
		suggestSymbol(tokRoot, tok, t.Name)
		for _, m := range t.Methods {
			suggestSymbol(tokRoot, tok, t.Name+"."+m.Name)
		}
	}
	return true
}

// Print the symbol as a suggestion.
func suggestSymbol(tokRoot, tok, name string) {
	// Check if name is exported
	if !unexported {
		i := strings.LastIndex(name, ".")
		rune, _ := utf8.DecodeRuneInString(name[i+1:])
		if !unicode.IsUpper(rune) {
			return
		}
	}

	if strings.Index(name, tok) == 0 {
		fmt.Println(tokRoot + name + " ")
	} else if !matchCase {
		// Copied from cmd/doc/pkg.go.
		simpleFold := func(r rune) rune {
			for {
				r1 := unicode.SimpleFold(r)
				if r1 <= r {
					return r1
				}
				r = r1
			}
		}

		// Copied from cmd/doc/pkg.go.
		for _, u := range tok {
			p, w := utf8.DecodeRuneInString(name)
			name = name[w:]
			if u == p {
				continue
			}
			if unicode.IsLower(u) && simpleFold(u) == simpleFold(p) {
				continue
			}
			return
		}
		fmt.Println(tokRoot + tok + name + " ")
	}
}

// Suggest packages that tok could possibly be pointing toward.
func suggestPackages(tok string) {
	root, base := path.Split(tok)
	if isDir(tok) {
		root, base = tok, ""
	}

	for _, dirPath := range makePaths(root) {
		for _, dir := range getDirs(dirPath) {
			if strings.Index(dir, base) != 0 {
				// Skip paths don't match the partial token
				continue
			}
			if strings.Index(dir, ".") == 0 && len(base) == 0 {
				// Skip hidden folders unless there is a partial match
				continue
			}
			path := path.Join(dirPath, dir)
			if isPackage(path) {
				fmt.Println(root + dir + " ")
			}
			if hasPackages(path) {
				fmt.Println(root + dir + string(os.PathSeparator))
			}
		}
	}
}

// Expand GOROOT and GOPATH with respect to some dirPath.
func makePaths(dirPath string) (paths []string) {
	// TODO(jtsai): Can dirPath be an absolute path?
	for _, dir := range filepath.SplitList(build.Default.GOPATH) {
		if dir != "" {
			paths = append(paths, path.Join(dir, "src", dirPath))
		}
	}
	if dir := build.Default.GOROOT; dir != "" {
		paths = append(paths, path.Join(dir, "src", dirPath))
	}
	return
}

// Check if dirPath points to a directory.
func isDir(dirPath string) bool {
	return len(dirPath) > 0 && dirPath[len(dirPath)-1] == os.PathSeparator
}

// Get all directories within the directory at dirPath.
func getDirs(dirPath string) (dirs []string) {
	fis, _ := ioutil.ReadDir(dirPath)
	for _, fi := range fis {
		if fi.Mode()&os.ModeSymlink > 0 {
			_fi, err := os.Stat(path.Join(dirPath, fi.Name()))
			if err == nil {
				fi = _fi
			}
		}
		if fi.IsDir() {
			dirs = append(dirs, fi.Name())
		}
	}
	return
}

// Check if the path is a Go package. This function simply checks if at least
// one Go file exists in the directory and could be wrong.
func isPackage(path string) bool {
	fis, _ := ioutil.ReadDir(path)
	for _, fi := range fis {
		if strings.HasSuffix(fi.Name(), ".go") {
			return true
		}
	}
	return false
}

// Check if this directory contains other Go packages. This function simply
// checks if other directories exist within this directory and could be wrong.
func hasPackages(path string) bool {
	return len(getDirs(path)) > 0
}

// Parse a package at a given dir.
// This logic is a stripped down version of cmd/doc/pkg.go.
func parsePackage(dir string) (_ string, _ *doc.Package) {
	if dir == "" {
		return
	}

	pkg, err := build.ImportDir(dir, build.ImportComment)
	if err != nil {
		return
	}

	fs := token.NewFileSet()
	include := func(info os.FileInfo) bool {
		for _, name := range pkg.GoFiles {
			if name == info.Name() {
				return true
			}
		}
		for _, name := range pkg.CgoFiles {
			if name == info.Name() {
				return true
			}
		}
		return false
	}
	pkgs, err := parser.ParseDir(fs, pkg.Dir, include, parser.ParseComments)
	if err != nil {
		return
	}
	if len(pkgs) != 1 {
		return
	}
	astPkg := pkgs[pkg.Name]

	docPkg := doc.New(astPkg, pkg.ImportPath, doc.AllDecls)
	for _, typ := range docPkg.Types {
		docPkg.Consts = append(docPkg.Consts, typ.Consts...)
		docPkg.Vars = append(docPkg.Vars, typ.Vars...)
		docPkg.Funcs = append(docPkg.Funcs, typ.Funcs...)
	}

	return pkg.Name, docPkg
}
<commit_msg>Fix visibility bug where type is unexported<commit_after>// Copyright 2015, Joe Tsai. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE.md file.

package main

import "os"
import "fmt"
import "strings"
import "io/ioutil"
import "path"
import "path/filepath"
import "unicode"
import "unicode/utf8"
import "go/doc"
import "go/build"
import "go/token"
import "go/parser"

var args []string
var matchCase = false
var unexported = false
var showCmd = false

func handleDoc(t *tokenizer) {
	if tok, old := t.Next(); old {
		switch tok {
		case "-c", "--c":
			matchCase = true
		case "-u", "--u":
			unexported = true
		case "-cmd", "--cmd":
			showCmd = true
		default:
			args = append(args, tok)
		}
	} else {
		if strings.Index(tok, "-") == 0 {
			// go doc [-u] [-c] [-cmd]
			for _, s := range []string{"-u", "-c", "-cmd"} {
				if strings.Index(s, tok) == 0 {
					fmt.Println(s + " ")
				}
			}
		} else {
			switch len(args) {
			case 0:
				// go doc
				// go doc <sym>[.<method>]
				relPkg := false
				if cwd, err := os.Getwd(); err == nil {
					relPkg = suggestPackageContents(cwd, "", tok)
				}

				// go doc <pkg>
				if len(tok) > 0 || !relPkg {
					suggestPackages(tok)
				}

				// go doc [<pkg>].<sym>[.<method>]
				root, base := path.Split(tok)
				if i := strings.Index(base, "."); len(tok) > 0 && i > 0 {
					i += len(root)
					for _, pkgPath := range makePaths(tok[:i]) {
						tokRoot, tok := tok[:i+1], tok[i+1:]
						if suggestPackageContents(pkgPath, tokRoot, tok) {
							break
						}
					}
				}
			case 1:
				// go doc <pkg> <sym>[.<method>]
				for _, pkgPath := range makePaths(args[0]) {
					if suggestPackageContents(pkgPath, "", tok) {
						break
					}
				}
			}
		}
		return
	}
	handleDoc(t)
}

// Parse a package and suggest all symbols found in it.
func suggestPackageContents(pkgPath, tokRoot, tok string) bool {
	name, pkg := parsePackage(pkgPath)
	if pkg == nil || (name == "main" && !showCmd) {
		return false
	}

	for _, c := range pkg.Consts {
		for _, n := range c.Names {
			suggestSymbol(tokRoot, tok, n)
		}
	}
	for _, v := range pkg.Vars {
		for _, n := range v.Names {
			suggestSymbol(tokRoot, tok, n)
		}
	}
	for _, f := range pkg.Funcs {
		suggestSymbol(tokRoot, tok, f.Name)
	}
	for _, t := range pkg.Types {
		suggestSymbol(tokRoot, tok, t.Name)
		for _, m := range t.Methods {
			suggestSymbol(tokRoot, tok, t.Name+"."+m.Name)
		}
	}
	return true
}

// Print the symbol as a suggestion.
func suggestSymbol(tokRoot, tok, name string) {
	// Check if name is exported
	if !unexported {
		i := strings.LastIndex(name, ".")
		r1, _ := utf8.DecodeRuneInString(name)
		r2, _ := utf8.DecodeRuneInString(name[i+1:])
		if !unicode.IsUpper(r1) || !unicode.IsUpper(r2) {
			return
		}
	}

	if strings.Index(name, tok) == 0 {
		fmt.Println(tokRoot + name + " ")
	} else if !matchCase {
		// Copied from cmd/doc/pkg.go.
		simpleFold := func(r rune) rune {
			for {
				r1 := unicode.SimpleFold(r)
				if r1 <= r {
					return r1
				}
				r = r1
			}
		}

		// Copied from cmd/doc/pkg.go.
		for _, u := range tok {
			p, w := utf8.DecodeRuneInString(name)
			name = name[w:]
			if u == p {
				continue
			}
			if unicode.IsLower(u) && simpleFold(u) == simpleFold(p) {
				continue
			}
			return
		}
		fmt.Println(tokRoot + tok + name + " ")
	}
}

// Suggest packages that tok could possibly be pointing toward.
func suggestPackages(tok string) {
	root, base := path.Split(tok)
	if isDir(tok) {
		root, base = tok, ""
	}

	for _, dirPath := range makePaths(root) {
		for _, dir := range getDirs(dirPath) {
			if strings.Index(dir, base) != 0 {
				// Skip paths don't match the partial token
				continue
			}
			if strings.Index(dir, ".") == 0 && len(base) == 0 {
				// Skip hidden folders unless there is a partial match
				continue
			}
			path := path.Join(dirPath, dir)
			if isPackage(path) {
				fmt.Println(root + dir + " ")
			}
			if hasPackages(path) {
				fmt.Println(root + dir + string(os.PathSeparator))
			}
		}
	}
}

// Expand GOROOT and GOPATH with respect to some dirPath.
func makePaths(dirPath string) (paths []string) {
	// TODO(jtsai): Can dirPath be an absolute path?
	for _, dir := range filepath.SplitList(build.Default.GOPATH) {
		if dir != "" {
			paths = append(paths, path.Join(dir, "src", dirPath))
		}
	}
	if dir := build.Default.GOROOT; dir != "" {
		paths = append(paths, path.Join(dir, "src", dirPath))
	}
	return
}

// Check if dirPath points to a directory.
func isDir(dirPath string) bool {
	return len(dirPath) > 0 && dirPath[len(dirPath)-1] == os.PathSeparator
}

// Get all directories within the directory at dirPath.
func getDirs(dirPath string) (dirs []string) {
	fis, _ := ioutil.ReadDir(dirPath)
	for _, fi := range fis {
		if fi.Mode()&os.ModeSymlink > 0 {
			_fi, err := os.Stat(path.Join(dirPath, fi.Name()))
			if err == nil {
				fi = _fi
			}
		}
		if fi.IsDir() {
			dirs = append(dirs, fi.Name())
		}
	}
	return
}

// Check if the path is a Go package. This function simply checks if at least
// one Go file exists in the directory and could be wrong.
func isPackage(path string) bool {
	fis, _ := ioutil.ReadDir(path)
	for _, fi := range fis {
		if strings.HasSuffix(fi.Name(), ".go") {
			return true
		}
	}
	return false
}

// Check if this directory contains other Go packages. This function simply
// checks if other directories exist within this directory and could be wrong.
func hasPackages(path string) bool {
	return len(getDirs(path)) > 0
}

// Parse a package at a given dir.
// This logic is a stripped down version of cmd/doc/pkg.go.
func parsePackage(dir string) (_ string, _ *doc.Package) {
	if dir == "" {
		return
	}

	pkg, err := build.ImportDir(dir, build.ImportComment)
	if err != nil {
		return
	}

	fs := token.NewFileSet()
	include := func(info os.FileInfo) bool {
		for _, name := range pkg.GoFiles {
			if name == info.Name() {
				return true
			}
		}
		for _, name := range pkg.CgoFiles {
			if name == info.Name() {
				return true
			}
		}
		return false
	}
	pkgs, err := parser.ParseDir(fs, pkg.Dir, include, parser.ParseComments)
	if err != nil {
		return
	}
	if len(pkgs) != 1 {
		return
	}
	astPkg := pkgs[pkg.Name]

	docPkg := doc.New(astPkg, pkg.ImportPath, doc.AllDecls)
	for _, typ := range docPkg.Types {
		docPkg.Consts = append(docPkg.Consts, typ.Consts...)
		docPkg.Vars = append(docPkg.Vars, typ.Vars...)
		docPkg.Funcs = append(docPkg.Funcs, typ.Funcs...)
	}

	return pkg.Name, docPkg
}
<|endoftext|>


<commit_before>// Copyright (c) 2012, Suryandaru Triandana <syndtr@gmail.com>
// All rights reserved.
//
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

package leveldb

import (
	"fmt"
	"sync/atomic"
	"time"

	"github.com/syndtr/goleveldb/leveldb/journal"
	"github.com/syndtr/goleveldb/leveldb/storage"
)

// Logging.

type dropper struct {
	s  *session
	fd storage.FileDesc
}

func (d dropper) Drop(err error) {
	if e, ok := err.(*journal.ErrCorrupted); ok {
		d.s.logf("journal@drop %s-%d S·%s %q", d.fd.Type, d.fd.Num, shortenb(e.Size), e.Reason)
	} else {
		d.s.logf("journal@drop %s-%d %q", d.fd.Type, d.fd.Num, err)
	}
}

func (s *session) log(v ...interface{})                 { s.stor.Log(fmt.Sprint(v...)) }
func (s *session) logf(format string, v ...interface{}) { s.stor.Log(fmt.Sprintf(format, v...)) }

// File utils.

func (s *session) newTemp() storage.FileDesc {
	num := atomic.AddInt64(&s.stTempFileNum, 1) - 1
	return storage.FileDesc{Type: storage.TypeTemp, Num: num}
}

// Session state.

const (
	// maxCachedNumber represents the maximum number of version tasks
	// that can be cached in the ref loop.
	maxCachedNumber = 256

	// maxCachedTime represents the maximum time for ref loop to cache
	// a version task.
	maxCachedTime = 5 * time.Minute
)

// vDelta indicates the change information between the next version
// and the currently specified version
type vDelta struct {
	vid     int64
	added   []int64
	deleted []int64
}

// vTask defines a version task for either reference or release.
type vTask struct {
	vid     int64
	files   []tFiles
	created time.Time
}

func (s *session) refLoop() {
	var (
		fileRef    = make(map[int64]int)    // Table file reference counter
		ref        = make(map[int64]*vTask) // Current referencing version store
		deltas     = make(map[int64]*vDelta)
		referenced = make(map[int64]struct{})
		released   = make(map[int64]*vDelta)  // Released version that waiting for processing
		abandoned  = make(map[int64]struct{}) // Abandoned version id
		next, last int64
	)
	// addFileRef adds file reference counter with specified file number and
	// reference value
	addFileRef := func(fnum int64, ref int) int {
		ref += fileRef[fnum]
		if ref > 0 {
			fileRef[fnum] = ref
		} else if ref == 0 {
			delete(fileRef, fnum)
		} else {
			panic(fmt.Sprintf("negative ref: %v", fnum))
		}
		return ref
	}
	// skipAbandoned skips useless abandoned version id.
	skipAbandoned := func() bool {
		if _, exist := abandoned[next]; exist {
			delete(abandoned, next)
			return true
		}
		return false
	}
	// applyDelta applies version change to current file reference.
	applyDelta := func(d *vDelta) {
		for _, t := range d.added {
			addFileRef(t, 1)
		}
		for _, t := range d.deleted {
			if addFileRef(t, -1) == 0 {
				s.tops.remove(storage.FileDesc{Type: storage.TypeTable, Num: t})
			}
		}
	}

	timer := time.NewTimer(0)
	<-timer.C // discard the initial tick
	defer timer.Stop()

	// processTasks processes version tasks in strict order.
	//
	// If we want to use delta to reduce the cost of file references and dereferences,
	// we must strictly follow the id of the version, otherwise some files that are
	// being referenced will be deleted.
	//
	// In addition, some db operations (such as iterators) may cause a version to be
	// referenced for a long time. In order to prevent such operations from blocking
	// the entire processing queue, we will properly convert some of the version tasks
	// into full file references and releases.
	processTasks := func() {
		timer.Reset(maxCachedTime)
		// Make sure we don't cache too many version tasks.
		for {
			// Skip any abandoned version number to prevent blocking processing.
			if skipAbandoned() {
				next += 1
				continue
			}
			// Don't bother the version that has been released.
			if _, exist := released[next]; exist {
				break
			}
			// Ensure the specified version has been referenced.
			if _, exist := ref[next]; !exist {
				break
			}
			if last-next < maxCachedNumber && time.Since(ref[next].created) < maxCachedTime {
				break
			}
			// Convert version task into full file references and releases mode.
			// Reference version(i+1) first and wait version(i) to release.
			// FileRef(i+1) = FileRef(i) + Delta(i)
			for _, tt := range ref[next].files {
				for _, t := range tt {
					addFileRef(t.fd.Num, 1)
				}
			}
			// Note, if some compactions take a long time, even more than 5 minutes,
			// we may miss the corresponding delta information here.
			// Fortunately it will not affect the correctness of the file reference,
			// and we can apply the delta once we receive it.
			if d := deltas[next]; d != nil {
				applyDelta(d)
			}
			referenced[next] = struct{}{}
			delete(ref, next)
			delete(deltas, next)
			next += 1
		}

		// Use delta information to process all released versions.
		for {
			if skipAbandoned() {
				next += 1
				continue
			}
			if d, exist := released[next]; exist {
				if d != nil {
					applyDelta(d)
				}
				delete(released, next)
				next += 1
				continue
			}
			return
		}
	}

	for {
		processTasks()

		select {
		case t := <-s.refCh:
			if _, exist := ref[t.vid]; exist {
				panic("duplicate reference request")
			}
			ref[t.vid] = t
			if t.vid > last {
				last = t.vid
			}

		case d := <-s.deltaCh:
			if _, exist := ref[d.vid]; !exist {
				if _, exist2 := referenced[d.vid]; !exist2 {
					panic("invalid release request")
				}
				// The reference opt is already expired, apply
				// delta here.
				applyDelta(d)
				continue
			}
			deltas[d.vid] = d

		case t := <-s.relCh:
			if _, exist := referenced[t.vid]; exist {
				for _, tt := range t.files {
					for _, t := range tt {
						if addFileRef(t.fd.Num, -1) == 0 {
							s.tops.remove(t.fd)
						}
					}
				}
				delete(referenced, t.vid)
				continue
			}
			if _, exist := ref[t.vid]; !exist {
				panic("invalid release request")
			}
			released[t.vid] = deltas[t.vid]
			delete(deltas, t.vid)
			delete(ref, t.vid)

		case id := <-s.abandon:
			if id >= next {
				abandoned[id] = struct{}{}
			}

		case <-timer.C:

		case r := <-s.fileRefCh:
			ref := make(map[int64]int)
			for f, c := range fileRef {
				ref[f] = c
			}
			r <- ref

		case <-s.closeC:
			s.closeW.Done()
			return
		}
	}
}

// Get current version. This will incr version ref, must call
// version.release (exactly once) after use.
func (s *session) version() *version {
	s.vmu.Lock()
	defer s.vmu.Unlock()
	s.stVersion.incref()
	return s.stVersion
}

func (s *session) tLen(level int) int {
	s.vmu.Lock()
	defer s.vmu.Unlock()
	return s.stVersion.tLen(level)
}

// Set current version to v.
func (s *session) setVersion(r *sessionRecord, v *version) {
	s.vmu.Lock()
	defer s.vmu.Unlock()
	// Hold by session. It is important to call this first before releasing
	// current version, otherwise the still used files might get released.
	v.incref()
	if s.stVersion != nil {
		if r != nil {
			var (
				added   = make([]int64, 0, len(r.addedTables))
				deleted = make([]int64, 0, len(r.deletedTables))
			)
			for _, t := range r.addedTables {
				added = append(added, t.num)
			}
			for _, t := range r.deletedTables {
				deleted = append(deleted, t.num)
			}
			select {
			case s.deltaCh <- &vDelta{vid: s.stVersion.id, added: added, deleted: deleted}:
			case <-v.s.closeC:
				s.log("reference loop already exist")
			}
		}
		// Release current version.
		s.stVersion.releaseNB()
	}
	s.stVersion = v
}

// Get current unused file number.
func (s *session) nextFileNum() int64 {
	return atomic.LoadInt64(&s.stNextFileNum)
}

// Set current unused file number to num.
func (s *session) setNextFileNum(num int64) {
	atomic.StoreInt64(&s.stNextFileNum, num)
}

// Mark file number as used.
func (s *session) markFileNum(num int64) {
	nextFileNum := num + 1
	for {
		old, x := atomic.LoadInt64(&s.stNextFileNum), nextFileNum
		if old > x {
			x = old
		}
		if atomic.CompareAndSwapInt64(&s.stNextFileNum, old, x) {
			break
		}
	}
}

// Allocate a file number.
func (s *session) allocFileNum() int64 {
	return atomic.AddInt64(&s.stNextFileNum, 1) - 1
}

// Reuse given file number.
func (s *session) reuseFileNum(num int64) {
	for {
		old, x := atomic.LoadInt64(&s.stNextFileNum), num
		if old != x+1 {
			x = old
		}
		if atomic.CompareAndSwapInt64(&s.stNextFileNum, old, x) {
			break
		}
	}
}

// Set compaction ptr at given level; need external synchronization.
func (s *session) setCompPtr(level int, ik internalKey) {
	if level >= len(s.stCompPtrs) {
		newCompPtrs := make([]internalKey, level+1)
		copy(newCompPtrs, s.stCompPtrs)
		s.stCompPtrs = newCompPtrs
	}
	s.stCompPtrs[level] = append(internalKey{}, ik...)
}

// Get compaction ptr at given level; need external synchronization.
func (s *session) getCompPtr(level int) internalKey {
	if level >= len(s.stCompPtrs) {
		return nil
	}
	return s.stCompPtrs[level]
}

// Manifest related utils.

// Fill given session record obj with current states; need external
// synchronization.
func (s *session) fillRecord(r *sessionRecord, snapshot bool) {
	r.setNextFileNum(s.nextFileNum())

	if snapshot {
		if !r.has(recJournalNum) {
			r.setJournalNum(s.stJournalNum)
		}

		if !r.has(recSeqNum) {
			r.setSeqNum(s.stSeqNum)
		}

		for level, ik := range s.stCompPtrs {
			if ik != nil {
				r.addCompPtr(level, ik)
			}
		}

		r.setComparer(s.icmp.uName())
	}
}

// Mark if record has been committed, this will update session state;
// need external synchronization.
func (s *session) recordCommited(rec *sessionRecord) {
	if rec.has(recJournalNum) {
		s.stJournalNum = rec.journalNum
	}

	if rec.has(recPrevJournalNum) {
		s.stPrevJournalNum = rec.prevJournalNum
	}

	if rec.has(recSeqNum) {
		s.stSeqNum = rec.seqNum
	}

	for _, r := range rec.compPtrs {
		s.setCompPtr(r.level, internalKey(r.ikey))
	}
}

// Create a new manifest file; need external synchronization.
func (s *session) newManifest(rec *sessionRecord, v *version) (err error) {
	fd := storage.FileDesc{Type: storage.TypeManifest, Num: s.allocFileNum()}
	writer, err := s.stor.Create(fd)
	if err != nil {
		return
	}
	jw := journal.NewWriter(writer)

	if v == nil {
		v = s.version()
		defer v.release()
	}
	if rec == nil {
		rec = &sessionRecord{}
	}
	s.fillRecord(rec, true)
	v.fillRecord(rec)

	defer func() {
		if err == nil {
			s.recordCommited(rec)
			if s.manifest != nil {
				s.manifest.Close()
			}
			if s.manifestWriter != nil {
				s.manifestWriter.Close()
			}
			if !s.manifestFd.Zero() {
				s.stor.Remove(s.manifestFd)
			}
			s.manifestFd = fd
			s.manifestWriter = writer
			s.manifest = jw
		} else {
			writer.Close()
			s.stor.Remove(fd)
			s.reuseFileNum(fd.Num)
		}
	}()

	w, err := jw.Next()
	if err != nil {
		return
	}
	err = rec.encode(w)
	if err != nil {
		return
	}
	err = jw.Flush()
	if err != nil {
		return
	}
	err = s.stor.SetMeta(fd)
	return
}

// Flush record to disk.
func (s *session) flushManifest(rec *sessionRecord) (err error) {
	s.fillRecord(rec, false)
	w, err := s.manifest.Next()
	if err != nil {
		return
	}
	err = rec.encode(w)
	if err != nil {
		return
	}
	err = s.manifest.Flush()
	if err != nil {
		return
	}
	if !s.o.GetNoSync() {
		err = s.manifestWriter.Sync()
		if err != nil {
			return
		}
	}
	s.recordCommited(rec)
	return
}
<commit_msg>Add fsync after creating new manifest<commit_after>// Copyright (c) 2012, Suryandaru Triandana <syndtr@gmail.com>
// All rights reserved.
//
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

package leveldb

import (
	"fmt"
	"sync/atomic"
	"time"

	"github.com/syndtr/goleveldb/leveldb/journal"
	"github.com/syndtr/goleveldb/leveldb/storage"
)

// Logging.

type dropper struct {
	s  *session
	fd storage.FileDesc
}

func (d dropper) Drop(err error) {
	if e, ok := err.(*journal.ErrCorrupted); ok {
		d.s.logf("journal@drop %s-%d S·%s %q", d.fd.Type, d.fd.Num, shortenb(e.Size), e.Reason)
	} else {
		d.s.logf("journal@drop %s-%d %q", d.fd.Type, d.fd.Num, err)
	}
}

func (s *session) log(v ...interface{})                 { s.stor.Log(fmt.Sprint(v...)) }
func (s *session) logf(format string, v ...interface{}) { s.stor.Log(fmt.Sprintf(format, v...)) }

// File utils.

func (s *session) newTemp() storage.FileDesc {
	num := atomic.AddInt64(&s.stTempFileNum, 1) - 1
	return storage.FileDesc{Type: storage.TypeTemp, Num: num}
}

// Session state.

const (
	// maxCachedNumber represents the maximum number of version tasks
	// that can be cached in the ref loop.
	maxCachedNumber = 256

	// maxCachedTime represents the maximum time for ref loop to cache
	// a version task.
	maxCachedTime = 5 * time.Minute
)

// vDelta indicates the change information between the next version
// and the currently specified version
type vDelta struct {
	vid     int64
	added   []int64
	deleted []int64
}

// vTask defines a version task for either reference or release.
type vTask struct {
	vid     int64
	files   []tFiles
	created time.Time
}

func (s *session) refLoop() {
	var (
		fileRef    = make(map[int64]int)    // Table file reference counter
		ref        = make(map[int64]*vTask) // Current referencing version store
		deltas     = make(map[int64]*vDelta)
		referenced = make(map[int64]struct{})
		released   = make(map[int64]*vDelta)  // Released version that waiting for processing
		abandoned  = make(map[int64]struct{}) // Abandoned version id
		next, last int64
	)
	// addFileRef adds file reference counter with specified file number and
	// reference value
	addFileRef := func(fnum int64, ref int) int {
		ref += fileRef[fnum]
		if ref > 0 {
			fileRef[fnum] = ref
		} else if ref == 0 {
			delete(fileRef, fnum)
		} else {
			panic(fmt.Sprintf("negative ref: %v", fnum))
		}
		return ref
	}
	// skipAbandoned skips useless abandoned version id.
	skipAbandoned := func() bool {
		if _, exist := abandoned[next]; exist {
			delete(abandoned, next)
			return true
		}
		return false
	}
	// applyDelta applies version change to current file reference.
	applyDelta := func(d *vDelta) {
		for _, t := range d.added {
			addFileRef(t, 1)
		}
		for _, t := range d.deleted {
			if addFileRef(t, -1) == 0 {
				s.tops.remove(storage.FileDesc{Type: storage.TypeTable, Num: t})
			}
		}
	}

	timer := time.NewTimer(0)
	<-timer.C // discard the initial tick
	defer timer.Stop()

	// processTasks processes version tasks in strict order.
	//
	// If we want to use delta to reduce the cost of file references and dereferences,
	// we must strictly follow the id of the version, otherwise some files that are
	// being referenced will be deleted.
	//
	// In addition, some db operations (such as iterators) may cause a version to be
	// referenced for a long time. In order to prevent such operations from blocking
	// the entire processing queue, we will properly convert some of the version tasks
	// into full file references and releases.
	processTasks := func() {
		timer.Reset(maxCachedTime)
		// Make sure we don't cache too many version tasks.
		for {
			// Skip any abandoned version number to prevent blocking processing.
			if skipAbandoned() {
				next += 1
				continue
			}
			// Don't bother the version that has been released.
			if _, exist := released[next]; exist {
				break
			}
			// Ensure the specified version has been referenced.
			if _, exist := ref[next]; !exist {
				break
			}
			if last-next < maxCachedNumber && time.Since(ref[next].created) < maxCachedTime {
				break
			}
			// Convert version task into full file references and releases mode.
			// Reference version(i+1) first and wait version(i) to release.
			// FileRef(i+1) = FileRef(i) + Delta(i)
			for _, tt := range ref[next].files {
				for _, t := range tt {
					addFileRef(t.fd.Num, 1)
				}
			}
			// Note, if some compactions take a long time, even more than 5 minutes,
			// we may miss the corresponding delta information here.
			// Fortunately it will not affect the correctness of the file reference,
			// and we can apply the delta once we receive it.
			if d := deltas[next]; d != nil {
				applyDelta(d)
			}
			referenced[next] = struct{}{}
			delete(ref, next)
			delete(deltas, next)
			next += 1
		}

		// Use delta information to process all released versions.
		for {
			if skipAbandoned() {
				next += 1
				continue
			}
			if d, exist := released[next]; exist {
				if d != nil {
					applyDelta(d)
				}
				delete(released, next)
				next += 1
				continue
			}
			return
		}
	}

	for {
		processTasks()

		select {
		case t := <-s.refCh:
			if _, exist := ref[t.vid]; exist {
				panic("duplicate reference request")
			}
			ref[t.vid] = t
			if t.vid > last {
				last = t.vid
			}

		case d := <-s.deltaCh:
			if _, exist := ref[d.vid]; !exist {
				if _, exist2 := referenced[d.vid]; !exist2 {
					panic("invalid release request")
				}
				// The reference opt is already expired, apply
				// delta here.
				applyDelta(d)
				continue
			}
			deltas[d.vid] = d

		case t := <-s.relCh:
			if _, exist := referenced[t.vid]; exist {
				for _, tt := range t.files {
					for _, t := range tt {
						if addFileRef(t.fd.Num, -1) == 0 {
							s.tops.remove(t.fd)
						}
					}
				}
				delete(referenced, t.vid)
				continue
			}
			if _, exist := ref[t.vid]; !exist {
				panic("invalid release request")
			}
			released[t.vid] = deltas[t.vid]
			delete(deltas, t.vid)
			delete(ref, t.vid)

		case id := <-s.abandon:
			if id >= next {
				abandoned[id] = struct{}{}
			}

		case <-timer.C:

		case r := <-s.fileRefCh:
			ref := make(map[int64]int)
			for f, c := range fileRef {
				ref[f] = c
			}
			r <- ref

		case <-s.closeC:
			s.closeW.Done()
			return
		}
	}
}

// Get current version. This will incr version ref, must call
// version.release (exactly once) after use.
func (s *session) version() *version {
	s.vmu.Lock()
	defer s.vmu.Unlock()
	s.stVersion.incref()
	return s.stVersion
}

func (s *session) tLen(level int) int {
	s.vmu.Lock()
	defer s.vmu.Unlock()
	return s.stVersion.tLen(level)
}

// Set current version to v.
func (s *session) setVersion(r *sessionRecord, v *version) {
	s.vmu.Lock()
	defer s.vmu.Unlock()
	// Hold by session. It is important to call this first before releasing
	// current version, otherwise the still used files might get released.
	v.incref()
	if s.stVersion != nil {
		if r != nil {
			var (
				added   = make([]int64, 0, len(r.addedTables))
				deleted = make([]int64, 0, len(r.deletedTables))
			)
			for _, t := range r.addedTables {
				added = append(added, t.num)
			}
			for _, t := range r.deletedTables {
				deleted = append(deleted, t.num)
			}
			select {
			case s.deltaCh <- &vDelta{vid: s.stVersion.id, added: added, deleted: deleted}:
			case <-v.s.closeC:
				s.log("reference loop already exist")
			}
		}
		// Release current version.
		s.stVersion.releaseNB()
	}
	s.stVersion = v
}

// Get current unused file number.
func (s *session) nextFileNum() int64 {
	return atomic.LoadInt64(&s.stNextFileNum)
}

// Set current unused file number to num.
func (s *session) setNextFileNum(num int64) {
	atomic.StoreInt64(&s.stNextFileNum, num)
}

// Mark file number as used.
func (s *session) markFileNum(num int64) {
	nextFileNum := num + 1
	for {
		old, x := atomic.LoadInt64(&s.stNextFileNum), nextFileNum
		if old > x {
			x = old
		}
		if atomic.CompareAndSwapInt64(&s.stNextFileNum, old, x) {
			break
		}
	}
}

// Allocate a file number.
func (s *session) allocFileNum() int64 {
	return atomic.AddInt64(&s.stNextFileNum, 1) - 1
}

// Reuse given file number.
func (s *session) reuseFileNum(num int64) {
	for {
		old, x := atomic.LoadInt64(&s.stNextFileNum), num
		if old != x+1 {
			x = old
		}
		if atomic.CompareAndSwapInt64(&s.stNextFileNum, old, x) {
			break
		}
	}
}

// Set compaction ptr at given level; need external synchronization.
func (s *session) setCompPtr(level int, ik internalKey) {
	if level >= len(s.stCompPtrs) {
		newCompPtrs := make([]internalKey, level+1)
		copy(newCompPtrs, s.stCompPtrs)
		s.stCompPtrs = newCompPtrs
	}
	s.stCompPtrs[level] = append(internalKey{}, ik...)
}

// Get compaction ptr at given level; need external synchronization.
func (s *session) getCompPtr(level int) internalKey {
	if level >= len(s.stCompPtrs) {
		return nil
	}
	return s.stCompPtrs[level]
}

// Manifest related utils.

// Fill given session record obj with current states; need external
// synchronization.
func (s *session) fillRecord(r *sessionRecord, snapshot bool) {
	r.setNextFileNum(s.nextFileNum())

	if snapshot {
		if !r.has(recJournalNum) {
			r.setJournalNum(s.stJournalNum)
		}

		if !r.has(recSeqNum) {
			r.setSeqNum(s.stSeqNum)
		}

		for level, ik := range s.stCompPtrs {
			if ik != nil {
				r.addCompPtr(level, ik)
			}
		}

		r.setComparer(s.icmp.uName())
	}
}

// Mark if record has been committed, this will update session state;
// need external synchronization.
func (s *session) recordCommited(rec *sessionRecord) {
	if rec.has(recJournalNum) {
		s.stJournalNum = rec.journalNum
	}

	if rec.has(recPrevJournalNum) {
		s.stPrevJournalNum = rec.prevJournalNum
	}

	if rec.has(recSeqNum) {
		s.stSeqNum = rec.seqNum
	}

	for _, r := range rec.compPtrs {
		s.setCompPtr(r.level, internalKey(r.ikey))
	}
}

// Create a new manifest file; need external synchronization.
func (s *session) newManifest(rec *sessionRecord, v *version) (err error) {
	fd := storage.FileDesc{Type: storage.TypeManifest, Num: s.allocFileNum()}
	writer, err := s.stor.Create(fd)
	if err != nil {
		return
	}
	jw := journal.NewWriter(writer)

	if v == nil {
		v = s.version()
		defer v.release()
	}
	if rec == nil {
		rec = &sessionRecord{}
	}
	s.fillRecord(rec, true)
	v.fillRecord(rec)

	defer func() {
		if err == nil {
			s.recordCommited(rec)
			if s.manifest != nil {
				s.manifest.Close()
			}
			if s.manifestWriter != nil {
				s.manifestWriter.Close()
			}
			if !s.manifestFd.Zero() {
				s.stor.Remove(s.manifestFd)
			}
			s.manifestFd = fd
			s.manifestWriter = writer
			s.manifest = jw
		} else {
			writer.Close()
			s.stor.Remove(fd)
			s.reuseFileNum(fd.Num)
		}
	}()

	w, err := jw.Next()
	if err != nil {
		return
	}
	err = rec.encode(w)
	if err != nil {
		return
	}
	err = jw.Flush()
	if err != nil {
		return
	}
	if !s.o.GetNoSync() {
		err = writer.Sync()
		if err != nil {
			return
		}
	}
	err = s.stor.SetMeta(fd)
	return
}

// Flush record to disk.
func (s *session) flushManifest(rec *sessionRecord) (err error) {
	s.fillRecord(rec, false)
	w, err := s.manifest.Next()
	if err != nil {
		return
	}
	err = rec.encode(w)
	if err != nil {
		return
	}
	err = s.manifest.Flush()
	if err != nil {
		return
	}
	if !s.o.GetNoSync() {
		err = s.manifestWriter.Sync()
		if err != nil {
			return
		}
	}
	s.recordCommited(rec)
	return
}
<|endoftext|>


<commit_before>/*
Copyright 2017 Eliott Teissonniere

Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation
files (the "Software"), to deal in the Software without restriction,
including without limitation the rights to use, copy, modify, merge,
publish, distribute, sublicense, and/or sell copies of the Software,
and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:

The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/

package api

import (
	"context"
	"net/http"
	"net/http/httptest"
	"testing"
	"encoding/base64"
	"github.com/Bit-Nation/BITNATION-Panthalassa/repo"
	"github.com/Bit-Nation/BITNATION-Panthalassa/tracker"
	"github.com/gin-gonic/gin"
	"github.com/DeveloppSoft/go-ipfs-api"
)

type LedgerMock struct {
	Repo string

	sh *shell.Shell // IPFS api
}

func NewLedgerMock(repo_path string, ipfs_api string) *LedgerMock {
	return &LedgerMock{Repo: repo_path, sh: shell.NewShell(ipfs_api)}
}

/***************************/
/* Implement interface for mocking Ledger
/***************************/
func (l *LedgerMock) Sync() error {
	// First, add the repo to ipfs
	return nil
}

func (l *LedgerMock) GetMessage(peer_name string, sequence string) (string, error) {
	return "My Message", nil
}

func (l *LedgerMock) GetLastSeq(peer_name string) (string, error) {
	return "Message", nil
}

func (l *LedgerMock) GetFeed(peer_name string) ([]string, error) {
	return []string{"Message 1", "Message 2"}, nil
}

func (l *LedgerMock) Whoami() string {
	return "me"
}

func (l *LedgerMock) About(peer_name string) (string, error) {
	about := "About me"
	return about, nil
}

func (l *LedgerMock) SetAbout(about repo.About) error {
	return nil
}

func (l *LedgerMock) Publish(data string) error {
	return nil
}

func (l *LedgerMock) AddRessource(b64 string) (string, error) {
	return "hash", nil
}

func (l *LedgerMock) GetRessource(id string) (string, error) {
	return base64.StdEncoding.EncodeToString([]byte{}), nil
}

func (l *LedgerMock) Resolve(name string) (string, error) {
	return "name", nil
}

/***************************/

func TestSync(t *testing.T) {
	// Make the repo
	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()

	ipfsApi := "<host>:<port>"
	rep := NewLedgerMock("./", ipfsApi)

	// Load tracker and api
	trk, _ := tracker.NewTracker(ctx, "./", ipfsApi)
	api := API{Repo: rep, Tracker: trk}
	//Create a new request
	req, errRequest := http.NewRequest("GET", "/sync", nil)

	if errRequest != nil {
		t.Fatal(errRequest)
	}

	//Record the response
	w := httptest.NewRecorder()
	r := gin.Default()

	r.GET("/sync", api.sync)

	r.ServeHTTP(w, req)

	if w.Code != http.StatusOK {
		t.Errorf("Response code should be %d, was: %d", http.StatusOK, w.Code)
	}
}

func TestGetMessage(t *testing.T) {
	// Make the repo
	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()

	ipfsApi := "<host>:<port>"
	rep := NewLedgerMock("./", ipfsApi)

	// Load tracker and api
	trk, _ := tracker.NewTracker(ctx, "./", ipfsApi)
	api := API{Repo: rep, Tracker: trk}
	//Create a new request
	req, errRequest := http.NewRequest("GET", "/get_message/user1/1", nil)

	if errRequest != nil {
		t.Fatal(errRequest)
	}

	//Record the response
	w := httptest.NewRecorder()
	r := gin.Default()

	r.GET("/get_message/user1/1", api.getMessage)

	r.ServeHTTP(w, req)

	if w.Code != http.StatusOK {
		t.Errorf("Response code should be %d, was: %d", http.StatusOK, w.Code)
	}

	//TODO: Test the message content
}
<commit_msg>[api] Added test for getMessage, actually testing the message content * One thing I noticed is that the getMessage is returning a quoted string   I'm not sure if this is correct, we can fix it in future iterations if needed<commit_after>/*
Copyright 2017 Eliott Teissonniere

Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation
files (the "Software"), to deal in the Software without restriction,
including without limitation the rights to use, copy, modify, merge,
publish, distribute, sublicense, and/or sell copies of the Software,
and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:

The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/

package api

import (
	"fmt"
	"context"
	"net/http"
	"net/http/httptest"
	"testing"
	"encoding/base64"
	"github.com/Bit-Nation/BITNATION-Panthalassa/repo"
	"github.com/Bit-Nation/BITNATION-Panthalassa/tracker"
	"github.com/gin-gonic/gin"
	"github.com/DeveloppSoft/go-ipfs-api"
)

type LedgerMock struct {
	Repo string

	sh *shell.Shell // IPFS api
}

func NewLedgerMock(repo_path string, ipfs_api string) *LedgerMock {
	return &LedgerMock{Repo: repo_path, sh: shell.NewShell(ipfs_api)}
}

/***************************/
/* Implement interface for mocking Ledger
/***************************/
func (l *LedgerMock) Sync() error {
	// First, add the repo to ipfs
	return nil
}

func (l *LedgerMock) GetMessage(peer_name string, sequence string) (string, error) {
	return fmt.Sprintf("My Message %s", sequence), nil
}

func (l *LedgerMock) GetLastSeq(peer_name string) (string, error) {
	return "Message", nil
}

func (l *LedgerMock) GetFeed(peer_name string) ([]string, error) {
	return []string{"Message 1", "Message 2"}, nil
}

func (l *LedgerMock) Whoami() string {
	return "me"
}

func (l *LedgerMock) About(peer_name string) (string, error) {
	about := "About me"
	return about, nil
}

func (l *LedgerMock) SetAbout(about repo.About) error {
	return nil
}

func (l *LedgerMock) Publish(data string) error {
	return nil
}

func (l *LedgerMock) AddRessource(b64 string) (string, error) {
	return "hash", nil
}

func (l *LedgerMock) GetRessource(id string) (string, error) {
	return base64.StdEncoding.EncodeToString([]byte{}), nil
}

func (l *LedgerMock) Resolve(name string) (string, error) {
	return "name", nil
}

/***************************/

func TestSync(t *testing.T) {
	// Make the repo
	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()

	ipfsApi := "<host>:<port>"
	rep := NewLedgerMock("./", ipfsApi)

	// Load tracker and api
	trk, _ := tracker.NewTracker(ctx, "./", ipfsApi)
	api := API{Repo: rep, Tracker: trk}
	//Create a new request
	req, errRequest := http.NewRequest("GET", "/sync", nil)

	if errRequest != nil {
		t.Fatal(errRequest)
	}

	//Record the response
	w := httptest.NewRecorder()
	r := gin.Default()

	r.GET("/sync", api.sync)

	r.ServeHTTP(w, req)

	if w.Code != http.StatusOK {
		t.Errorf("Response code should be %d, was: %d", http.StatusOK, w.Code)
	}
}

func TestGetMessage(t *testing.T) {
	// Make the repo
	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()

	ipfsApi := "<host>:<port>"
	rep := NewLedgerMock("./", ipfsApi)

	// Load tracker and api
	trk, _ := tracker.NewTracker(ctx, "./", ipfsApi)
	api := API{Repo: rep, Tracker: trk}
	//Create a new request
	seq := "1"
	req, errRequest := http.NewRequest("GET", fmt.Sprintf("/get_message/user1/%s", seq), nil)

	if errRequest != nil {
		t.Fatal(errRequest)
	}

	//Record the response
	w := httptest.NewRecorder()
	r := gin.Default()

	expectedMessage := fmt.Sprintf("\"My Message %s\"", seq)

	r.GET("/get_message/:user/:seq", api.getMessage)

	r.ServeHTTP(w, req)

	if w.Code != http.StatusOK {
		t.Errorf("Response code should be %d, was: %d", http.StatusOK, w.Code)
	}

	if w.Body.String() != expectedMessage {
		t.Errorf("Message should be %s, was: %s", expectedMessage, w.Body)
	}
}
<|endoftext|>


<commit_before>// Comands runs all zfs functions
package api

import (
	"fmt"
	"strings"

	"github.com/nfrance-conseil/zeplic/config"
	"github.com/mistifyio/go-zfs"
)

func RealMain(j int) int {
	// Start syslog system service
	w, _ := config.LogBook()

	for i := 0; i < j; i++ {
		// We going to extract all data stored in JSON file
		pieces := config.Extract(i)

		// This value returns if the dataset is enable
		takedataset := pieces[0].(bool)

		// Execute the functions
		if takedataset == true {
			// Get clones dataset
			clone := pieces[1].(string)
			cl, err := zfs.GetDataset(clone)
			if err != nil {
				w.Info("[INFO] the clone '"+clone+"' does not exist.")
			} else {
				// Destroy clones dataset
				err := cl.Destroy(zfs.DestroyRecursiveClones)
				if err != nil {
					w.Err("[ERROR] it was not possible to destroy the clone '"+clone+"'.")
				} else {
					w.Info("[INFO] the clone '"+clone+"' has been destroyed.")
				}
			}

			// Get dataset (called in JSON file)
			dataset := pieces[2].(string)
			ds, err := zfs.GetDataset(dataset)
			if err != nil {
				w.Info("[INFO] the dataset '"+dataset+"' does not exist.")
			}
			// Destroy dataset (optional)
/*			err := ds.Destroy(zfs.DestroyRecursive)
			if err != nil {
				w.Err("[ERROR] it was not possible to destroy the dataset '"+dataset+"'.")
			} else {
				w.Info("[INFO] the dataset '"+dataset+"' has been destroyed.")
			}*/
			if ds == nil {
				// Create dataset if it does not exist
				_, err := zfs.CreateFilesystem(dataset, nil)
				if err != nil {
					w.Err("[ERROR] it was not possible to create the dataset '"+dataset+"'.")
				} else {
					w.Info("[INFO] the dataset '"+dataset+"' has been created.")
				}
			}

			// Create a new snapshot
			snap := pieces[3].(string)
			s, err := ds.Snapshot(SnapName(snap), false)
			if err != nil {
				w.Err("[ERROR] it was not possible to create the snapshot '"+dataset+"@"+SnapName(snap)+"'.")
			} else {
				w.Info("[INFO] the snapshot '"+dataset+"@"+SnapName(snap)+"' has been created.")
			}

			// Delete the backup snapshot
			list, err := zfs.Snapshots(dataset)
			if err != nil {
				w.Err("[ERROR] it was not possible to access of snapshots list.")
			}
			justList := fmt.Sprintf("%s", list)
			for strings.Contains(justList, "BACKUP") {
				take := Between(justList, "{", " ")
				if strings.Contains(take, "BACKUP") {
					snap, err := zfs.GetDataset(take)
					if err != nil {
						w.Err("[ERROR] it was not possible to get the snapshot '"+take+"'.")
					}
					err = snap.Destroy(zfs.DestroyDefault)
					if err != nil {
						w.Err("[ERROR] it was not possible to destroy the snapshot '"+take+"'.")
					} else {
						w.Info("[INFO] the snapshot '"+take+"' has been destroyed.")
					}
					remove := Before(justList, " %")
					r := int(len(remove)+1)
					justList = Chop(r, justList)
				} else {
					remove := Before(justList, " %")
					r := int(len(remove)+1)
					justList = Chop(r, justList)
				}
			}

			// Return the number of existing snapshots
			retain := pieces[4].(int)
			count, err := zfs.Snapshots(dataset)
			if err != nil {
				w.Err("[ERROR] it was not possible to access of snapshots list.")
			}
			k := len(count)
			if k > 0 {
				// Save the last #Retain(JSON file) snapshots
				for ; k > retain; k-- {
					list, err := zfs.Snapshots(dataset)
					if err != nil {
						w.Err("[ERROR] it was not possible to access of snapshots list.")
					}
					justList := fmt.Sprintf("%s", list)
					take := Between(justList, "{", " ")
					snap, err := zfs.GetDataset(take)
					if err != nil {
						w.Err("[ERROR] it was not possible to get the snapshot '"+take+"'.")
					}
					err = snap.Destroy(zfs.DestroyDefault)
					if err != nil {
						w.Err("[ERROR] it was not possible to destroy the snapshot '"+take+"'.")
					} else {
						w.Info("[INFO] the snapshot '"+take+"' has been destroyed.")
					}
				}
			}

			// Create a backup snapshot
			backup := pieces[5].(bool)
			if backup == true {
				_, err := ds.Snapshot(SnapBackup(), false)
				if err != nil {
					w.Err("[ERROR] it was not possible to create the backup snapshot '"+dataset+"@"+SnapBackup()+"'.")
				} else {
					w.Info("[INFO] the backup snapshot '"+dataset+"@"+SnapBackup()+"' has been created.")
				}
			}

			// Create a clone of last snapshot
			takeclone := pieces[6].(bool)
			if takeclone == true {
				_, err = s.Clone(clone, nil)
				if err != nil {
					w.Err("[ERROR] it was not possible to clone the snapshot '"+dataset+"@"+SnapName(snap)+"'.")
				} else {
					w.Info("[INFO] the snapshot '"+dataset+"@"+SnapName(snap)+"' has been clone.")
				}
			}

			// Rollback of last snaphot
			takerollback := pieces[7].(bool)
			if takerollback == true {
				s.Rollback(true)
				if err != nil {
					w.Err("[ERROR] it was not possible to rolling back the snapshot '"+SnapName(snap)+"'.")
				} else {
					w.Info("[INFO] the snapshot '"+dataset+"@"+SnapName(snap)+"' has been restored.")
				}
			}
		}
	}
	return 1
}
<commit_msg>Revised<commit_after>// Comands runs all zfs functions
package api

import (
	"fmt"
	"strings"

	"github.com/nfrance-conseil/zeplic/config"
	"github.com/mistifyio/go-zfs"
)

func RealMain(j int) int {
	// Start syslog system service
	w, _ := config.LogBook()

	for i := 0; i < j; i++ {
		// We going to extract all data stored in JSON file
		pieces := config.Extract(i)

		// This value returns if the dataset is enable
		takedataset := pieces[0].(bool)

		// Execute the functions
		if takedataset == true {
			// Get clones dataset
			clone := pieces[1].(string)
			cl, err := zfs.GetDataset(clone)
			if err != nil {
				w.Info("[INFO] the clone '"+clone+"' does not exist.")
			} else {
				// Destroy clones dataset
				err := cl.Destroy(zfs.DestroyRecursiveClones)
				if err != nil {
					w.Err("[ERROR] it was not possible to destroy the clone '"+clone+"'.")
				} else {
					w.Info("[INFO] the clone '"+clone+"' has been destroyed.")
				}
			}

			// Get dataset (called in JSON file)
			dataset := pieces[2].(string)
			ds, err := zfs.GetDataset(dataset)
			if err != nil {
				w.Info("[INFO] the dataset '"+dataset+"' does not exist.")
			}
			// Destroy dataset (optional)
/*			err := ds.Destroy(zfs.DestroyRecursive)
			if err != nil {
				w.Err("[ERROR] it was not possible to destroy the dataset '"+dataset+"'.")
			} else {
				w.Info("[INFO] the dataset '"+dataset+"' has been destroyed.")
			}*/
			if ds == nil {
				// Create dataset if it does not exist
				_, err := zfs.CreateFilesystem(dataset, nil)
				if err != nil {
					w.Err("[ERROR] it was not possible to create the dataset '"+dataset+"'.")
				} else {
					w.Info("[INFO] the dataset '"+dataset+"' has been created.")
				}
			}

			// Create a new snapshot
			snap := pieces[3].(string)
			s, err := ds.Snapshot(SnapName(snap), false)
			if err != nil {
				w.Err("[ERROR] it was not possible to create the snapshot '"+dataset+"@"+SnapName(snap)+"'.")
			} else {
				w.Info("[INFO] the snapshot '"+dataset+"@"+SnapName(snap)+"' has been created.")
			}

			// Delete the backup snapshot
			list, err := zfs.Snapshots(dataset)
			if err != nil {
				w.Err("[ERROR] it was not possible to access of snapshots list.")
			}
			justList := fmt.Sprintf("%v", list)
			for strings.Contains(justList, "BACKUP") {
				take := Between(justList, "{", " ")
				if strings.Contains(take, "BACKUP") {
					snap, err := zfs.GetDataset(take)
					if err != nil {
						w.Err("[ERROR] it was not possible to get the snapshot '"+take+"'.")
					}
					err = snap.Destroy(zfs.DestroyDefault)
					if err != nil {
						w.Err("[ERROR] it was not possible to destroy the snapshot '"+take+"'.")
					} else {
						w.Info("[INFO] the snapshot '"+take+"' has been destroyed.")
					}
					remove := Before(justList, " %")
					r := int(len(remove)+1)
					justList = Chop(r, justList)
				} else {
					remove := Before(justList, " %")
					r := int(len(remove)+1)
					justList = Chop(r, justList)
				}
			}

			// Return the number of existing snapshots
			retain := pieces[4].(int)
			count, err := zfs.Snapshots(dataset)
			if err != nil {
				w.Err("[ERROR] it was not possible to access of snapshots list.")
			}
			k := len(count)
			if k > 0 {
				// Save the last #Retain(JSON file) snapshots
				for ; k > retain; k-- {
					list, err := zfs.Snapshots(dataset)
					if err != nil {
						w.Err("[ERROR] it was not possible to access of snapshots list.")
					}
					justList := fmt.Sprintf("%v", list)
					take := Between(justList, "{", " ")
					snap, err := zfs.GetDataset(take)
					if err != nil {
						w.Err("[ERROR] it was not possible to get the snapshot '"+take+"'.")
					}
					err = snap.Destroy(zfs.DestroyDefault)
					if err != nil {
						w.Err("[ERROR] it was not possible to destroy the snapshot '"+take+"'.")
					} else {
						w.Info("[INFO] the snapshot '"+take+"' has been destroyed.")
					}
				}
			}

			// Create a backup snapshot
			backup := pieces[5].(bool)
			if backup == true {
				_, err := ds.Snapshot(SnapBackup(), false)
				if err != nil {
					w.Err("[ERROR] it was not possible to create the backup snapshot '"+dataset+"@"+SnapBackup()+"'.")
				} else {
					w.Info("[INFO] the backup snapshot '"+dataset+"@"+SnapBackup()+"' has been created.")
				}
			}

			// Create a clone of last snapshot
			takeclone := pieces[6].(bool)
			if takeclone == true {
				_, err = s.Clone(clone, nil)
				if err != nil {
					w.Err("[ERROR] it was not possible to clone the snapshot '"+dataset+"@"+SnapName(snap)+"'.")
				} else {
					w.Info("[INFO] the snapshot '"+dataset+"@"+SnapName(snap)+"' has been clone.")
				}
			}

			// Rollback of last snaphot
			takerollback := pieces[7].(bool)
			if takerollback == true {
				s.Rollback(true)
				if err != nil {
					w.Err("[ERROR] it was not possible to rolling back the snapshot '"+SnapName(snap)+"'.")
				} else {
					w.Info("[INFO] the snapshot '"+dataset+"@"+SnapName(snap)+"' has been restored.")
				}
			}
		}
	}
	return 1
}
<|endoftext|>


<commit_before>// +build windows

package goboots

import (
	"crypto/tls"
	"net/http"

	"golang.org/x/crypto/acme/autocert"
)

func listenAndServe(addr string, handler http.Handler) error {
	return http.ListenAndServe(addr, handler)
}

func listenAndServeTLS(addr, certFile, keyFile string, handler http.Handler) error {
	return http.ListenAndServe(addr, handler)
}

func listenAndServeGracefully(addr string, handler http.Handler) error {
	return endless.ListenAndServe(addr, handler)
}

func listenAndServeTLSGracefully(addr, certFile, keyFile string, handler http.Handler) error {
	return http.ListenAndServeTLS(addr, certFile, keyFile, handler)
}

func autocertServerTLS(addrtls string, whitelist []string, app *App) error {
	m := autocert.Manager{
		Prompt:     autocert.AcceptTOS,
		HostPolicy: autocert.HostWhitelist(whitelist...),
	}
	s := &http.Server{
		Addr:      addrtls,
		TLSConfig: &tls.Config{GetCertificate: m.GetCertificate},
	}
	return s.ListenAndServeTLS("", "")
}

func autocertServerTLSGracefully(addrtls string, whitelist []string, app *App) error {
	m := autocert.Manager{
		Prompt:     autocert.AcceptTOS,
		HostPolicy: autocert.HostWhitelist(whitelist...),
	}
	s := &http.Server{
		Addr:      addrtls,
		TLSConfig: &tls.Config{GetCertificate: m.GetCertificate},
	}
	return s.ListenAndServeTLS("", "")
}
<commit_msg>fixed typo<commit_after>// +build windows

package goboots

import (
	"crypto/tls"
	"net/http"

	"golang.org/x/crypto/acme/autocert"
)

func listenAndServe(addr string, handler http.Handler) error {
	return http.ListenAndServe(addr, handler)
}

func listenAndServeTLS(addr, certFile, keyFile string, handler http.Handler) error {
	return http.ListenAndServe(addr, handler)
}

func listenAndServeGracefully(addr string, handler http.Handler) error {
	return http.ListenAndServe(addr, handler)
}

func listenAndServeTLSGracefully(addr, certFile, keyFile string, handler http.Handler) error {
	return http.ListenAndServeTLS(addr, certFile, keyFile, handler)
}

func autocertServerTLS(addrtls string, whitelist []string, app *App) error {
	m := autocert.Manager{
		Prompt:     autocert.AcceptTOS,
		HostPolicy: autocert.HostWhitelist(whitelist...),
	}
	s := &http.Server{
		Addr:      addrtls,
		TLSConfig: &tls.Config{GetCertificate: m.GetCertificate},
	}
	return s.ListenAndServeTLS("", "")
}

func autocertServerTLSGracefully(addrtls string, whitelist []string, app *App) error {
	m := autocert.Manager{
		Prompt:     autocert.AcceptTOS,
		HostPolicy: autocert.HostWhitelist(whitelist...),
	}
	s := &http.Server{
		Addr:      addrtls,
		TLSConfig: &tls.Config{GetCertificate: m.GetCertificate},
	}
	return s.ListenAndServeTLS("", "")
}
<|endoftext|>


<commit_before>package pop

import (
	"bytes"
	"fmt"
	"io"
	"os"
	"os/exec"
	"strings"

	// Load MySQL Go driver
	_ "github.com/go-sql-driver/mysql"
	"github.com/gobuffalo/pop/columns"
	"github.com/gobuffalo/pop/fizz"
	"github.com/gobuffalo/pop/fizz/translators"
	"github.com/jmoiron/sqlx"
	"github.com/pkg/errors"
)

var _ dialect = &mysql{}

type mysql struct {
	ConnectionDetails *ConnectionDetails
}

func (m *mysql) Name() string {
	return "mysql"
}

func (m *mysql) Details() *ConnectionDetails {
	return m.ConnectionDetails
}

func (m *mysql) URL() string {
	c := m.ConnectionDetails
	if m.ConnectionDetails.URL != "" {
		return strings.TrimPrefix(m.ConnectionDetails.URL, "mysql://")
	}
	s := "%s:%s@(%s:%s)/%s?parseTime=true&multiStatements=true&readTimeout=1s"
	return fmt.Sprintf(s, c.User, c.Password, c.Host, c.Port, c.Database)
}

func (m *mysql) urlWithoutDb() string {
	c := m.ConnectionDetails
	if m.ConnectionDetails.URL != "" {
		// respect user's own URL definition (with options).
		url := strings.TrimPrefix(m.ConnectionDetails.URL, "mysql://")
		return strings.Replace(url, "/"+c.Database+"?", "/?", 1)
	}
	s := "%s:%s@(%s:%s)/?parseTime=true&multiStatements=true&readTimeout=1s"
	return fmt.Sprintf(s, c.User, c.Password, c.Host, c.Port)
}

func (m *mysql) MigrationURL() string {
	return m.URL()
}

func (m *mysql) Create(s store, model *Model, cols columns.Columns) error {
	return errors.Wrap(genericCreate(s, model, cols), "mysql create")
}

func (m *mysql) Update(s store, model *Model, cols columns.Columns) error {
	return errors.Wrap(genericUpdate(s, model, cols), "mysql update")
}

func (m *mysql) Destroy(s store, model *Model) error {
	return errors.Wrap(genericDestroy(s, model), "mysql destroy")
}

func (m *mysql) SelectOne(s store, model *Model, query Query) error {
	return errors.Wrap(genericSelectOne(s, model, query), "mysql select one")
}

func (m *mysql) SelectMany(s store, models *Model, query Query) error {
	return errors.Wrap(genericSelectMany(s, models, query), "mysql select many")
}

// CreateDB creates a new database, from the given connection credentials
func (m *mysql) CreateDB() error {
	deets := m.ConnectionDetails
	db, err := sqlx.Open(deets.Dialect, m.urlWithoutDb())
	if err != nil {
		return errors.Wrapf(err, "error creating MySQL database %s", deets.Database)
	}
	defer db.Close()
	query := fmt.Sprintf("CREATE DATABASE `%s` DEFAULT COLLATE `utf8_general_ci`", deets.Database)
	Log(query)

	_, err = db.Exec(query)
	if err != nil {
		return errors.Wrapf(err, "error creating MySQL database %s", deets.Database)
	}

	fmt.Printf("created database %s\n", deets.Database)
	return nil
}

// DropDB drops an existing database, from the given connection credentials
func (m *mysql) DropDB() error {
	deets := m.ConnectionDetails
	db, err := sqlx.Open(deets.Dialect, m.urlWithoutDb())
	if err != nil {
		return errors.Wrapf(err, "error dropping MySQL database %s", deets.Database)
	}
	defer db.Close()
	query := fmt.Sprintf("DROP DATABASE `%s`", deets.Database)
	Log(query)

	_, err = db.Exec(query)
	if err != nil {
		return errors.Wrapf(err, "error dropping MySQL database %s", deets.Database)
	}

	fmt.Printf("dropped database %s\n", deets.Database)
	return nil
}

func (m *mysql) TranslateSQL(sql string) string {
	return sql
}

func (m *mysql) FizzTranslator() fizz.Translator {
	t := translators.NewMySQL(m.URL(), m.Details().Database)
	return t
}

func (m *mysql) Lock(fn func() error) error {
	return fn()
}

func (m *mysql) DumpSchema(w io.Writer) error {
	deets := m.Details()
	cmd := exec.Command("mysqldump", "-d", "-h", deets.Host, "-P", deets.Port, "-u", deets.User, fmt.Sprintf("--password=%s", deets.Password), deets.Database)
	if deets.Port == "socket" {
		cmd = exec.Command("mysqldump", "-d", "-S", deets.Host, "-u", deets.User, fmt.Sprintf("--password=%s", deets.Password), deets.Database)
	}
	Log(strings.Join(cmd.Args, " "))
	cmd.Stdout = w
	cmd.Stderr = os.Stderr

	err := cmd.Run()
	if err != nil {
		return err
	}

	fmt.Printf("dumped schema for %s\n", m.Details().Database)
	return nil
}

func (m *mysql) LoadSchema(r io.Reader) error {
	deets := m.Details()
	cmd := exec.Command("mysql", "-u", deets.User, fmt.Sprintf("--password=%s", deets.Password), "-h", deets.Host, "-P", deets.Port, "-D", deets.Database)
	if deets.Port == "socket" {
		cmd = exec.Command("mysql", "-u", deets.User, fmt.Sprintf("--password=%s", deets.Password), "-S", deets.Host, "-D", deets.Database)
	}
	in, err := cmd.StdinPipe()
	if err != nil {
		return err
	}
	go func() {
		defer in.Close()
		io.Copy(in, r)
	}()
	Log(strings.Join(cmd.Args, " "))
	err = cmd.Start()
	if err != nil {
		return err
	}

	err = cmd.Wait()
	if err != nil {
		return err
	}

	fmt.Printf("loaded schema for %s\n", m.Details().Database)
	return nil
}

func (m *mysql) TruncateAll(tx *Connection) error {
	stmts := []string{}
	err := tx.RawQuery(mysqlTruncate, m.Details().Database).All(&stmts)
	if err != nil {
		return err
	}
	if len(stmts) == 0 {
		return nil
	}

	var qb bytes.Buffer
	// #49: Disable foreign keys before truncation
	qb.WriteString("SET SESSION FOREIGN_KEY_CHECKS = 0; ")
	qb.WriteString(strings.Join(stmts, " "))
	// #49: Re-enable foreign keys after truncation
	qb.WriteString(" SET SESSION FOREIGN_KEY_CHECKS = 1;")

	return tx.RawQuery(qb.String()).Exec()
}

func newMySQL(deets *ConnectionDetails) dialect {
	cd := &mysql{
		ConnectionDetails: deets,
	}

	return cd
}

const mysqlTruncate = "SELECT concat('TRUNCATE TABLE `', TABLE_NAME, '`;') as stmt FROM INFORMATION_SCHEMA.TABLES WHERE table_schema = ? AND table_type <> 'VIEW'"
<commit_msg>Migrate MySQL LoadSchema to not depend on native client (#125)<commit_after>package pop

import (
	"bytes"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"os/exec"
	"strings"

	// Load MySQL Go driver
	_ "github.com/go-sql-driver/mysql"
	"github.com/gobuffalo/pop/columns"
	"github.com/gobuffalo/pop/fizz"
	"github.com/gobuffalo/pop/fizz/translators"
	"github.com/jmoiron/sqlx"
	"github.com/pkg/errors"
)

var _ dialect = &mysql{}

type mysql struct {
	ConnectionDetails *ConnectionDetails
}

func (m *mysql) Name() string {
	return "mysql"
}

func (m *mysql) Details() *ConnectionDetails {
	return m.ConnectionDetails
}

func (m *mysql) URL() string {
	c := m.ConnectionDetails
	if m.ConnectionDetails.URL != "" {
		return strings.TrimPrefix(m.ConnectionDetails.URL, "mysql://")
	}
	s := "%s:%s@(%s:%s)/%s?parseTime=true&multiStatements=true&readTimeout=1s"
	return fmt.Sprintf(s, c.User, c.Password, c.Host, c.Port, c.Database)
}

func (m *mysql) urlWithoutDb() string {
	c := m.ConnectionDetails
	if m.ConnectionDetails.URL != "" {
		// respect user's own URL definition (with options).
		url := strings.TrimPrefix(m.ConnectionDetails.URL, "mysql://")
		return strings.Replace(url, "/"+c.Database+"?", "/?", 1)
	}
	s := "%s:%s@(%s:%s)/?parseTime=true&multiStatements=true&readTimeout=1s"
	return fmt.Sprintf(s, c.User, c.Password, c.Host, c.Port)
}

func (m *mysql) MigrationURL() string {
	return m.URL()
}

func (m *mysql) Create(s store, model *Model, cols columns.Columns) error {
	return errors.Wrap(genericCreate(s, model, cols), "mysql create")
}

func (m *mysql) Update(s store, model *Model, cols columns.Columns) error {
	return errors.Wrap(genericUpdate(s, model, cols), "mysql update")
}

func (m *mysql) Destroy(s store, model *Model) error {
	return errors.Wrap(genericDestroy(s, model), "mysql destroy")
}

func (m *mysql) SelectOne(s store, model *Model, query Query) error {
	return errors.Wrap(genericSelectOne(s, model, query), "mysql select one")
}

func (m *mysql) SelectMany(s store, models *Model, query Query) error {
	return errors.Wrap(genericSelectMany(s, models, query), "mysql select many")
}

// CreateDB creates a new database, from the given connection credentials
func (m *mysql) CreateDB() error {
	deets := m.ConnectionDetails
	db, err := sqlx.Open(deets.Dialect, m.urlWithoutDb())
	if err != nil {
		return errors.Wrapf(err, "error creating MySQL database %s", deets.Database)
	}
	defer db.Close()
	query := fmt.Sprintf("CREATE DATABASE `%s` DEFAULT COLLATE `utf8_general_ci`", deets.Database)
	Log(query)

	_, err = db.Exec(query)
	if err != nil {
		return errors.Wrapf(err, "error creating MySQL database %s", deets.Database)
	}

	fmt.Printf("created database %s\n", deets.Database)
	return nil
}

// DropDB drops an existing database, from the given connection credentials
func (m *mysql) DropDB() error {
	deets := m.ConnectionDetails
	db, err := sqlx.Open(deets.Dialect, m.urlWithoutDb())
	if err != nil {
		return errors.Wrapf(err, "error dropping MySQL database %s", deets.Database)
	}
	defer db.Close()
	query := fmt.Sprintf("DROP DATABASE `%s`", deets.Database)
	Log(query)

	_, err = db.Exec(query)
	if err != nil {
		return errors.Wrapf(err, "error dropping MySQL database %s", deets.Database)
	}

	fmt.Printf("dropped database %s\n", deets.Database)
	return nil
}

func (m *mysql) TranslateSQL(sql string) string {
	return sql
}

func (m *mysql) FizzTranslator() fizz.Translator {
	t := translators.NewMySQL(m.URL(), m.Details().Database)
	return t
}

func (m *mysql) Lock(fn func() error) error {
	return fn()
}

func (m *mysql) DumpSchema(w io.Writer) error {
	deets := m.Details()
	cmd := exec.Command("mysqldump", "-d", "-h", deets.Host, "-P", deets.Port, "-u", deets.User, fmt.Sprintf("--password=%s", deets.Password), deets.Database)
	if deets.Port == "socket" {
		cmd = exec.Command("mysqldump", "-d", "-S", deets.Host, "-u", deets.User, fmt.Sprintf("--password=%s", deets.Password), deets.Database)
	}
	Log(strings.Join(cmd.Args, " "))
	cmd.Stdout = w
	cmd.Stderr = os.Stderr

	err := cmd.Run()
	if err != nil {
		return err
	}

	fmt.Printf("dumped schema for %s\n", m.Details().Database)
	return nil
}

func (m *mysql) LoadSchema(r io.Reader) error {
	// Open DB connection on the target DB
	deets := m.ConnectionDetails
	db, err := sqlx.Open(deets.Dialect, m.MigrationURL())
	if err != nil {
		return errors.WithMessage(err, fmt.Sprintf("unable to load schema for %s", deets.Database))
	}
	defer db.Close()

	// Get reader contents
	contents, err := ioutil.ReadAll(r)
	if err != nil {
		return err
	}

	if len(contents) == 0 {
		fmt.Printf("schema is empty for %s, skipping\n", deets.Database)
		return nil
	}

	// From the sqlx package docs, this works with pq driver
	_, err = db.Exec(string(contents))
	if err != nil {
		return errors.WithMessage(err, fmt.Sprintf("unable to load schema for %s", deets.Database))
	}

	fmt.Printf("loaded schema for %s\n", deets.Database)
	return nil
}

func (m *mysql) TruncateAll(tx *Connection) error {
	stmts := []string{}
	err := tx.RawQuery(mysqlTruncate, m.Details().Database).All(&stmts)
	if err != nil {
		return err
	}
	if len(stmts) == 0 {
		return nil
	}

	var qb bytes.Buffer
	// #49: Disable foreign keys before truncation
	qb.WriteString("SET SESSION FOREIGN_KEY_CHECKS = 0; ")
	qb.WriteString(strings.Join(stmts, " "))
	// #49: Re-enable foreign keys after truncation
	qb.WriteString(" SET SESSION FOREIGN_KEY_CHECKS = 1;")

	return tx.RawQuery(qb.String()).Exec()
}

func newMySQL(deets *ConnectionDetails) dialect {
	cd := &mysql{
		ConnectionDetails: deets,
	}

	return cd
}

const mysqlTruncate = "SELECT concat('TRUNCATE TABLE `', TABLE_NAME, '`;') as stmt FROM INFORMATION_SCHEMA.TABLES WHERE table_schema = ? AND table_type <> 'VIEW'"
<|endoftext|>


<commit_before>package slack

import (
	"encoding/json"
	"fmt"
	"os"
	"strconv"
	"strings"
	"time"

	"github.com/nlopes/slack"

	"github.com/keel-hq/keel/constants"
	"github.com/keel-hq/keel/extension/notification"
	"github.com/keel-hq/keel/types"
	"github.com/keel-hq/keel/version"

	log "github.com/sirupsen/logrus"
)

const timeout = 5 * time.Second

type sender struct {
	slackClient *slack.Client
	channels    []string
	botName     string
}

func init() {
	notification.RegisterSender("slack", &sender{})
}

func (s *sender) Configure(config *notification.Config) (bool, error) {
	var token string
	// Get configuration
	if os.Getenv(constants.EnvSlackToken) != "" {
		token = os.Getenv(constants.EnvSlackToken)
	} else {
		return false, nil
	}
	if os.Getenv(constants.EnvSlackBotName) != "" {
		s.botName = os.Getenv(constants.EnvSlackBotName)
	} else {
		s.botName = "keel"
	}

	if os.Getenv(constants.EnvSlackChannels) != "" {
		channels := os.Getenv(constants.EnvSlackChannels)
		s.channels = strings.Split(channels, ",")
	} else {
		s.channels = []string{"general"}
	}

	s.slackClient = slack.New(token)

	log.WithFields(log.Fields{
		"name":     "slack",
		"channels": s.channels,
	}).Info("extension.notification.slack: sender configured")

	var msg string
	if version.GetKeelVersion().Version != "" {
		msg = fmt.Sprintf("Keel has started. Version: '%s'. Revision: %s", version.GetKeelVersion().Version, version.GetKeelVersion().Revision)
	} else {
		msg = fmt.Sprintf("Keel has started. Revision: %s", version.GetKeelVersion().Revision)
	}

	err := s.Send(types.EventNotification{
		Message:   msg,
		CreatedAt: time.Now(),
		Type:      types.NotificationSystemEvent,
		Level:     types.LevelInfo,
		Channels:  s.channels,
	})
	if err != nil {
		log.WithFields(log.Fields{
			"error":    err,
			"name":     "slack",
			"channels": s.channels,
		}).Error("extension.notification.slack: failed to set greeting message")
	}

	return true, nil
}

func (s *sender) Send(event types.EventNotification) error {
	params := slack.NewPostMessageParameters()
	params.Username = s.botName
	params.IconURL = constants.KeelLogoURL

	attachements := []slack.Attachment{
		slack.Attachment{
			Fallback: event.Message,
			Color:    event.Level.Color(),
			Fields: []slack.AttachmentField{
				slack.AttachmentField{
					Title: event.Type.String(),
					Value: event.Message,
					Short: false,
				},
			},
			Footer: fmt.Sprintf("https://keel.sh %s", version.GetKeelVersion().Version),
			Ts:     json.Number(strconv.Itoa(int(event.CreatedAt.Unix()))),
		},
	}

	chans := s.channels
	if len(event.Channels) > 0 {
		chans = event.Channels
	}

	var mgsOpts []slack.MsgOption

	mgsOpts = append(mgsOpts, slack.MsgOptionPostMessageParameters(params))
	mgsOpts = append(mgsOpts, slack.MsgOptionAttachments(attachements...))

	for _, channel := range chans {
		_, _, err := s.slackClient.PostMessage(channel, mgsOpts...)
		if err != nil {
			log.WithFields(log.Fields{
				"error":   err,
				"channel": channel,
			}).Error("extension.notification.slack: failed to send notification")
		}
	}
	return nil
}
<commit_msg>only send "started" message if debug is enabled<commit_after>package slack

import (
	"encoding/json"
	"fmt"
	"os"
	"strconv"
	"strings"
	"time"

	"github.com/nlopes/slack"

	"github.com/keel-hq/keel/constants"
	"github.com/keel-hq/keel/extension/notification"
	"github.com/keel-hq/keel/types"
	"github.com/keel-hq/keel/version"

	log "github.com/sirupsen/logrus"
)

const timeout = 5 * time.Second

type sender struct {
	slackClient *slack.Client
	channels    []string
	botName     string
}

func init() {
	notification.RegisterSender("slack", &sender{})
}

func (s *sender) Configure(config *notification.Config) (bool, error) {
	var token string
	// Get configuration
	if os.Getenv(constants.EnvSlackToken) != "" {
		token = os.Getenv(constants.EnvSlackToken)
	} else {
		return false, nil
	}
	if os.Getenv(constants.EnvSlackBotName) != "" {
		s.botName = os.Getenv(constants.EnvSlackBotName)
	} else {
		s.botName = "keel"
	}

	if os.Getenv(constants.EnvSlackChannels) != "" {
		channels := os.Getenv(constants.EnvSlackChannels)
		s.channels = strings.Split(channels, ",")
	} else {
		s.channels = []string{"general"}
	}

	s.slackClient = slack.New(token)

	log.WithFields(log.Fields{
		"name":     "slack",
		"channels": s.channels,
	}).Info("extension.notification.slack: sender configured")

	if os.Getenv("DEBUG") != "" {
		var msg string
		if version.GetKeelVersion().Version != "" {
			msg = fmt.Sprintf("Keel has started. Version: '%s'. Revision: %s", version.GetKeelVersion().Version, version.GetKeelVersion().Revision)
		} else {
			msg = fmt.Sprintf("Keel has started. Revision: %s", version.GetKeelVersion().Revision)
		}

		err := s.Send(types.EventNotification{
			Message:   msg,
			CreatedAt: time.Now(),
			Type:      types.NotificationSystemEvent,
			Level:     types.LevelInfo,
			Channels:  s.channels,
		})
		if err != nil {
			log.WithFields(log.Fields{
				"error":    err,
				"name":     "slack",
				"channels": s.channels,
			}).Error("extension.notification.slack: failed to set greeting message")
		}

	}

	return true, nil
}

func (s *sender) Send(event types.EventNotification) error {
	params := slack.NewPostMessageParameters()
	params.Username = s.botName
	params.IconURL = constants.KeelLogoURL

	attachements := []slack.Attachment{
		slack.Attachment{
			Fallback: event.Message,
			Color:    event.Level.Color(),
			Fields: []slack.AttachmentField{
				slack.AttachmentField{
					Title: event.Type.String(),
					Value: event.Message,
					Short: false,
				},
			},
			Footer: fmt.Sprintf("https://keel.sh %s", version.GetKeelVersion().Version),
			Ts:     json.Number(strconv.Itoa(int(event.CreatedAt.Unix()))),
		},
	}

	chans := s.channels
	if len(event.Channels) > 0 {
		chans = event.Channels
	}

	var mgsOpts []slack.MsgOption

	mgsOpts = append(mgsOpts, slack.MsgOptionPostMessageParameters(params))
	mgsOpts = append(mgsOpts, slack.MsgOptionAttachments(attachements...))

	for _, channel := range chans {
		_, _, err := s.slackClient.PostMessage(channel, mgsOpts...)
		if err != nil {
			log.WithFields(log.Fields{
				"error":   err,
				"channel": channel,
			}).Error("extension.notification.slack: failed to send notification")
		}
	}
	return nil
}
<|endoftext|>


<commit_before>package fix

import (
	"fmt"
	"strconv"
	"strings"

	"github.com/mitchellh/mapstructure"
)

// FixerAmazonPrivateIP is a Fixer that replaces instances of `"private_ip":
// true` with `"ssh_interface": "private_ip"`
type FixerAmazonPrivateIP struct{}

func (FixerAmazonPrivateIP) Fix(input map[string]interface{}) (map[string]interface{}, error) {
	type template struct {
		Builders []map[string]interface{}
	}

	// Decode the input into our structure, if we can
	var tpl template
	if err := mapstructure.Decode(input, &tpl); err != nil {
		return nil, err
	}

	// Go through each builder and replace the enhanced_networking if we can
	for _, builder := range tpl.Builders {
		builderTypeRaw, ok := builder["type"]
		if !ok {
			continue
		}

		builderType, ok := builderTypeRaw.(string)
		if !ok {
			continue
		}

		if !strings.HasPrefix(builderType, "amazon-") {
			continue
		}

		// if ssh_interface already set, do nothing
		if _, ok := builder["ssh_interface"]; ok {
			continue
		}

		privateIPi, ok := builder["ssh_private_ip"]
		if !ok {
			continue
		}
		privateIP, ok := privateIPi.(bool)
		if !ok {
			var err error
			privateIP, err = strconv.ParseBool(privateIPi.(string))
			if err != nil {
				return nil, fmt.Errorf("ssh_private_ip is not a boolean")
			}
		}

		delete(builder, "ssh_private_ip")
		if privateIP {
			builder["ssh_interface"] = "private_ip"
		} else {
			builder["ssh_interface"] = "public_ip"
		}
	}

	input["builders"] = tpl.Builders
	return input, nil
}

func (FixerAmazonPrivateIP) Synopsis() string {
	return "Replaces `\"ssh_private_ip\": true` in amazon builders with `\"ssh_interface\": \"private_ip\"`"
}
<commit_msg>Append underlying error to output when FixerAmazonPrivateIP cannot parse the value of ssh_private_ip<commit_after>package fix

import (
	"fmt"
	"strconv"
	"strings"

	"github.com/mitchellh/mapstructure"
)

// FixerAmazonPrivateIP is a Fixer that replaces instances of `"private_ip":
// true` with `"ssh_interface": "private_ip"`
type FixerAmazonPrivateIP struct{}

func (FixerAmazonPrivateIP) Fix(input map[string]interface{}) (map[string]interface{}, error) {
	type template struct {
		Builders []map[string]interface{}
	}

	// Decode the input into our structure, if we can
	var tpl template
	if err := mapstructure.Decode(input, &tpl); err != nil {
		return nil, err
	}

	// Go through each builder and replace the enhanced_networking if we can
	for _, builder := range tpl.Builders {
		builderTypeRaw, ok := builder["type"]
		if !ok {
			continue
		}

		builderType, ok := builderTypeRaw.(string)
		if !ok {
			continue
		}

		if !strings.HasPrefix(builderType, "amazon-") {
			continue
		}

		// if ssh_interface already set, do nothing
		if _, ok := builder["ssh_interface"]; ok {
			continue
		}

		privateIPi, ok := builder["ssh_private_ip"]
		if !ok {
			continue
		}
		privateIP, ok := privateIPi.(bool)
		if !ok {
			var err error
			privateIP, err = strconv.ParseBool(privateIPi.(string))
			if err != nil {
				return nil, fmt.Errorf("ssh_private_ip is not a boolean, %s", err)
			}
		}

		delete(builder, "ssh_private_ip")
		if privateIP {
			builder["ssh_interface"] = "private_ip"
		} else {
			builder["ssh_interface"] = "public_ip"
		}
	}

	input["builders"] = tpl.Builders
	return input, nil
}

func (FixerAmazonPrivateIP) Synopsis() string {
	return "Replaces `\"ssh_private_ip\": true` in amazon builders with `\"ssh_interface\": \"private_ip\"`"
}
<|endoftext|>