<commit_before>package main

import (
	"fmt"
	"strings"
	"unicode"
)

type tokenType int

const (
	tokenText tokenType = iota
	tokenName
	tokenTagOpen
	tokenTagName
	tokenTagClose
	tokenSpace
	tokenEof
)

var names = map[tokenType]string{
	tokenText:     "TEXT",
	tokenName:     "NAME",
	tokenTagOpen:  "TAG_OPEN",
	tokenTagName:  "TAG_NAME",
	tokenTagClose: "TAG_CLOSE",
	tokenEof:      "EOF",
}

const (
	delimOpenTag      = "{%"
	delimCloseTag     = "%}"
	delimOpenPrint    = "{{"
	delimClosePrint   = "}}"
	delimOpenComment  = "{#"
	delimCloseComment = "#}"
)

type lexerState int

const (
	stateData lexerState = iota
	stateBlock
	stateVar
	stateString
	stateInterpolation
)

type token struct {
	value     string
	pos       int
	tokenType tokenType
}

func (tok token) String() string {
	return fmt.Sprintf("{%s '%s' %d}\n", names[tok.tokenType], tok.value, tok.pos)
}

type tokenStream []token

type stateFn func(*lexer) stateFn

type lexer struct {
	pos    int // The position of the last emission
	cursor int // The position of the cursor
	input  string
	tokens tokenStream
	state  stateFn
}

func (lex *lexer) tokenize(code string) tokenStream {
	lex.pos = 0
	lex.cursor = 0
	lex.input = code
	lex.tokens = tokenStream{}

	for lex.state = lexData; lex.state != nil; {
		lex.state = lex.state(lex)
	}

	return lex.tokens
}

func (lex *lexer) next() string {
	fmt.Println(lex.cursor, len(lex.input))
	if lex.cursor+1 >= len(lex.input) {
		return ""
	}

	lex.cursor += 1

	return string(lex.input[lex.cursor])
}

func (lex *lexer) backup() {
	if lex.cursor <= lex.pos {
		return
	}

	fmt.Println("Backing up")
	lex.cursor -= 1
}

func (lex *lexer) peek() string {
	return lex.input[lex.cursor+1 : lex.cursor+2]
}

func (lex *lexer) current() string {
	return lex.input[lex.cursor : lex.cursor+1]
}

func (lex *lexer) ignore() {
	lex.pos = lex.cursor
}

func (lex *lexer) emit(t tokenType) {
	fmt.Println(lex.pos, lex.cursor, len(lex.input))
	val := lex.input[lex.pos:lex.cursor]
	tok := token{val, lex.pos, t}
	fmt.Println(tok)
	lex.tokens = append(lex.tokens, tok)
	lex.pos = lex.cursor
}

func (lex *lexer) consumeWhitespace() {
	if lex.pos != lex.cursor {
		panic("Whitespace may only be consumed directly after emission")
	}
	for {
		str := lex.input[lex.cursor : lex.cursor+1]
		if !isSpace(str) {
			break
		}
		lex.next()
	}

	lex.ignore()
}

func lexData(lex *lexer) stateFn {
	for {
		switch {
		case strings.HasPrefix(lex.input[lex.cursor:], delimOpenTag):
			if lex.cursor > lex.pos {
				lex.emit(tokenText)
			}
			return lexTagOpen
		}

		if lex.next() == "" {
			break
		}
	}

	if lex.cursor > lex.pos {
		lex.emit(tokenText)
	}

	lex.emit(tokenEof)

	return nil
}

func lexExpression(lex *lexer) stateFn {
	lex.consumeWhitespace()

	switch str := lex.current(); {
	case str[0] == delimCloseTag[0]:
		return lexTagClose

	case isAlphaNumeric(str):
		return lexName
	}

	panic("Unknown expression")
}

func lexName(lex *lexer) stateFn {
	for {
		str := lex.current()
		if isAlphaNumeric(str) {
			lex.next()
		} else {
			break
		}
	}

	lex.emit(tokenName)

	return lexExpression
}

func lexTagOpen(lex *lexer) stateFn {
	lex.cursor += len(delimOpenTag)
	lex.emit(tokenTagOpen)

	return lexTagName
}

func lexTagName(lex *lexer) stateFn {
	lex.consumeWhitespace()
	for {
		str := lex.next()
		if !isAlphaNumeric(str) {
			break
		}
	}

	lex.emit(tokenTagName)

	return lexExpression
}

func lexTagClose(lex *lexer) stateFn {
	lex.cursor += len(delimCloseTag)
	lex.emit(tokenTagClose)

	return lexData
}

func isSpace(str string) bool {
	return str == " " || str == "\t"
}

func isAlphaNumeric(str string) bool {
	for _, s := range str {
		if string(s) != "_" && !unicode.IsLetter(s) && !unicode.IsDigit(s) {
			return false
		}
	}

	return true
}

func main() {
	data := "<html><head><title>{% block title %}{% endblock %}"

	lex := lexer{}

	fmt.Println(lex.tokenize(data))
}
<commit_msg>Added basic print tag and parenthesis handling<commit_after>package main

import (
	"fmt"
	"strings"
	"unicode"
)

type tokenType int

const (
	tokenText tokenType = iota
	tokenName
	tokenTagOpen
	tokenTagName
	tokenTagClose
	tokenPrintOpen
	tokenPrintClose
	tokenParensOpen
	tokenParensClose
	tokenArrayOpen
	tokenArrayClose
	tokenHashOpen
	tokenHashClose
	tokenEof
)

var names = map[tokenType]string{
	tokenText:       "TEXT",
	tokenName:       "NAME",
	tokenTagOpen:    "TAG_OPEN",
	tokenTagName:    "TAG_NAME",
	tokenTagClose:   "TAG_CLOSE",
	tokenPrintOpen:  "PRINT_OPEN",
	tokenPrintClose: "PRINT_CLOSE",
	tokenParensOpen: "PARENS_OPEN",
	tokenParensClose: "PARENS_CLOSE",
	tokenArrayOpen: "ARRAY_OPEN",
	tokenArrayClose: "ARRAY_CLOSE",
	tokenHashOpen: "HASH_OPEN",
	tokenHashClose: "HASH_CLOSE",
	tokenEof:        "EOF",
}

const (
	delimOpenTag      = "{%"
	delimCloseTag     = "%}"
	delimOpenPrint    = "{{"
	delimClosePrint   = "}}"
	delimOpenComment  = "{#"
	delimCloseComment = "#}"
)

type lexerState int

const (
	stateData lexerState = iota
	stateBlock
	stateVar
	stateString
	stateInterpolation
)

type token struct {
	value     string
	pos       int
	tokenType tokenType
}

func (tok token) String() string {
	return fmt.Sprintf("{%s '%s' %d}\n", names[tok.tokenType], tok.value, tok.pos)
}

type tokenStream []token

type stateFn func(*lexer) stateFn

type lexer struct {
	pos    int // The position of the last emission
	cursor int // The position of the cursor
	parens int // Number of still-open parenthesis in the current expression
	input  string
	tokens tokenStream
	state  stateFn
}

func (lex *lexer) tokenize(code string) tokenStream {
	lex.pos = 0
	lex.cursor = 0
	lex.input = code
	lex.tokens = tokenStream{}

	for lex.state = lexData; lex.state != nil; {
		lex.state = lex.state(lex)
	}

	return lex.tokens
}

func (lex *lexer) next() string {
	fmt.Println(lex.cursor, len(lex.input))
	if lex.cursor+1 >= len(lex.input) {
		return ""
	}

	lex.cursor += 1

	return string(lex.input[lex.cursor])
}

func (lex *lexer) backup() {
	if lex.cursor <= lex.pos {
		return
	}

	fmt.Println("Backing up")
	lex.cursor -= 1
}

func (lex *lexer) peek() string {
	return lex.input[lex.cursor+1 : lex.cursor+2]
}

func (lex *lexer) current() string {
	return lex.input[lex.cursor : lex.cursor+1]
}

func (lex *lexer) ignore() {
	lex.pos = lex.cursor
}

func (lex *lexer) emit(t tokenType) {
	fmt.Println(lex.pos, lex.cursor, len(lex.input))
	val := lex.input[lex.pos:lex.cursor]
	tok := token{val, lex.pos, t}
	fmt.Println(tok)
	lex.tokens = append(lex.tokens, tok)
	lex.pos = lex.cursor
	if lex.pos < len(lex.input) {
		lex.consumeWhitespace()
	}
}

func (lex *lexer) consumeWhitespace() {
	if lex.pos != lex.cursor {
		panic("Whitespace may only be consumed directly after emission")
	}
	for {
		if isSpace(lex.current()) {
			lex.next()
		} else {
			break
		}
	}

	lex.ignore()
}

func lexData(lex *lexer) stateFn {
	for {
		switch {
		case strings.HasPrefix(lex.input[lex.cursor:], delimOpenTag):
			if lex.cursor > lex.pos {
				lex.emit(tokenText)
			}
			return lexTagOpen

		case strings.HasPrefix(lex.input[lex.cursor:], delimOpenPrint):
			if lex.cursor > lex.pos {
				lex.emit(tokenText)
			}
			return lexPrintOpen
		}

		if lex.next() == "" {
			break
		}
	}

	if lex.cursor > lex.pos {
		lex.emit(tokenText)
	}

	lex.emit(tokenEof)

	return nil
}

func lexExpression(lex *lexer) stateFn {
	switch str := lex.current(); {
	case strings.HasPrefix(lex.input[lex.cursor:], delimCloseTag):
		if lex.cursor > lex.pos {
			panic("Incomplete token?")
		}
		return lexTagClose

	case strings.HasPrefix(lex.input[lex.cursor:], delimClosePrint):
		if lex.cursor > lex.pos {
			panic("Incomplete token?")
		}
		return lexPrintClose

	case strings.ContainsAny(str, "([{"):
		return lexOpenParens

	case strings.ContainsAny(str, "}])"):
		return lexCloseParens

	case isAlphaNumeric(str):
		return lexName
	}

	panic("Unknown expression")
}

func lexOpenParens(lex *lexer) stateFn {
	switch str := lex.current(); {
	case str == "(":
		lex.next()
		lex.emit(tokenParensOpen)

	case str == "[":
		lex.next()
		lex.emit(tokenArrayOpen)

	case str == "{":
		lex.next()
		lex.emit(tokenHashOpen)

	default:
		fmt.Println(lex.current())
		panic("Unknown parens: ")
	}

	lex.parens += 1

	return lexExpression
}

func lexCloseParens(lex *lexer) stateFn {
	switch str := lex.current(); {
	case str == ")":
		lex.next()
		lex.emit(tokenParensClose)

	case str == "]":
		lex.next()
		lex.emit(tokenArrayClose)

	case str == "}":
		lex.next()
		lex.emit(tokenHashClose)

	default:
		panic("Unknown parens")
	}

	lex.parens -= 1

	return lexExpression
}

func lexName(lex *lexer) stateFn {
	for {
		str := lex.current()
		if isAlphaNumeric(str) {
			lex.next()
		} else {
			break
		}
	}

	lex.emit(tokenName)

	return lexExpression
}

func lexTagOpen(lex *lexer) stateFn {
	lex.cursor += len(delimOpenTag)
	lex.emit(tokenTagOpen)

	return lexTagName
}

func lexTagName(lex *lexer) stateFn {
	for {
		str := lex.next()
		if !isAlphaNumeric(str) {
			break
		}
	}

	lex.emit(tokenTagName)

	return lexExpression
}

func lexTagClose(lex *lexer) stateFn {
	lex.cursor += len(delimCloseTag)
	lex.emit(tokenTagClose)

	return lexData
}

func lexPrintOpen(lex *lexer) stateFn {
	lex.cursor += len(delimOpenPrint)
	lex.emit(tokenPrintOpen)

	return lexExpression
}

func lexPrintClose(lex *lexer) stateFn {
	lex.cursor += len(delimClosePrint)
	lex.emit(tokenPrintClose)

	return lexData
}

func isSpace(str string) bool {
	return str == " " || str == "\t"
}

func isAlphaNumeric(str string) bool {
	for _, s := range str {
		if string(s) != "_" && !unicode.IsLetter(s) && !unicode.IsDigit(s) {
			return false
		}
	}

	return true
}

func main() {
	data := "<html><head><title>{% block title %}{{ ([ test ]) }}{% endblock %}"

	lex := lexer{}

	fmt.Println(lex.tokenize(data))
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"flag"
	"fmt"
	"io/ioutil"
	"os"
	"path"

	"github.com/coreos/coreos-cloudinit/datasource"
	"github.com/coreos/coreos-cloudinit/initialize"
	"github.com/coreos/coreos-cloudinit/network"
	"github.com/coreos/coreos-cloudinit/system"
)

const version = "0.7.4"

func main() {
	var printVersion bool
	flag.BoolVar(&printVersion, "version", false, "Print the version and exit")

	var ignoreFailure bool
	flag.BoolVar(&ignoreFailure, "ignore-failure", false, "Exits with 0 status in the event of malformed input from user-data")

	var file string
	flag.StringVar(&file, "from-file", "", "Read user-data from provided file")

	var configdrive string
	flag.StringVar(&configdrive, "from-configdrive", "", "Read user-data from provided cloud-drive directory")

	var url string
	flag.StringVar(&url, "from-url", "", "Download user-data from provided url")

	var useProcCmdline bool
	flag.BoolVar(&useProcCmdline, "from-proc-cmdline", false, fmt.Sprintf("Parse %s for '%s=<url>', using the cloud-config served by an HTTP GET to <url>", datasource.ProcCmdlineLocation, datasource.ProcCmdlineCloudConfigFlag))

	var convertNetconf string
	flag.StringVar(&convertNetconf, "convert-netconf", "", "Read the network config provided in cloud-drive and translate it from the specified format into networkd unit files (requires the -from-configdrive flag)")

	var workspace string
	flag.StringVar(&workspace, "workspace", "/var/lib/coreos-cloudinit", "Base directory coreos-cloudinit should use to store data")

	var sshKeyName string
	flag.StringVar(&sshKeyName, "ssh-key-name", initialize.DefaultSSHKeyName, "Add SSH keys to the system with the given name")

	flag.Parse()

	if printVersion == true {
		fmt.Printf("coreos-cloudinit version %s\n", version)
		os.Exit(0)
	}

	var ds datasource.Datasource
	if file != "" {
		ds = datasource.NewLocalFile(file)
	} else if url != "" {
		ds = datasource.NewMetadataService(url)
	} else if configdrive != "" {
		ds = datasource.NewConfigDrive(configdrive)
	} else if useProcCmdline {
		ds = datasource.NewProcCmdline()
	} else {
		fmt.Println("Provide one of --from-file, --from-configdrive, --from-url or --from-proc-cmdline")
		os.Exit(1)
	}

	if convertNetconf != "" && configdrive == "" {
		fmt.Println("-convert-netconf flag requires -from-configdrive")
		os.Exit(1)
	}

	switch convertNetconf {
	case "":
	case "debian":
	default:
		fmt.Printf("Invalid option to -convert-netconf: '%s'. Supported options: 'debian'\n", convertNetconf)
		os.Exit(1)
	}

	fmt.Printf("Fetching user-data from datasource of type %q\n", ds.Type())
	userdataBytes, err := ds.Fetch()
	if err != nil {
		fmt.Printf("Failed fetching user-data from datasource: %v\n", err)
		if ignoreFailure {
			os.Exit(0)
		} else {
			os.Exit(1)
		}
	}

	env := initialize.NewEnvironment("/", workspace)
	if len(userdataBytes) > 0 {
		if err := processUserdata(string(userdataBytes), env); err != nil {
			fmt.Printf("Failed resolving user-data: %v\n", err)
			if !ignoreFailure {
				os.Exit(1)
			}
		}
	} else {
		fmt.Println("No user data to handle.")
	}

	if convertNetconf != "" {
		if err := processNetconf(convertNetconf, configdrive); err != nil {
			fmt.Printf("Failed to process network config: %v\n", err)
			if !ignoreFailure {
				os.Exit(1)
			}
		}
	}
}

func processUserdata(userdata string, env *initialize.Environment) error {
	userdata = env.Apply(userdata)

	parsed, err := initialize.ParseUserData(userdata)
	if err != nil {
		fmt.Printf("Failed parsing user-data: %v\n", err)
		return err
	}

	err = initialize.PrepWorkspace(env.Workspace())
	if err != nil {
		fmt.Printf("Failed preparing workspace: %v\n", err)
		return err
	}

	switch t := parsed.(type) {
	case initialize.CloudConfig:
		err = initialize.Apply(t, env)
	case system.Script:
		var path string
		path, err = initialize.PersistScriptInWorkspace(t, env.Workspace())
		if err == nil {
			var name string
			name, err = system.ExecuteScript(path)
			initialize.PersistUnitNameInWorkspace(name, env.Workspace())
		}
	}

	return err
}

func processNetconf(convertNetconf, configdrive string) error {
	openstackRoot := path.Join(configdrive, "openstack")
	metadataFilename := path.Join(openstackRoot, "latest", "meta_data.json")
	metadataBytes, err := ioutil.ReadFile(metadataFilename)
	if err != nil {
		return err
	}

	var metadata struct {
		NetworkConfig struct {
			ContentPath string `json:"content_path"`
		} `json:"network_config"`
	}
	if err := json.Unmarshal(metadataBytes, &metadata); err != nil {
		return err
	}
	configPath := metadata.NetworkConfig.ContentPath
	if configPath == "" {
		fmt.Printf("No network config specified in %q.\n", metadataFilename)
		return nil
	}

	netconfBytes, err := ioutil.ReadFile(path.Join(openstackRoot, configPath))
	if err != nil {
		return err
	}

	var interfaces []network.InterfaceGenerator
	switch convertNetconf {
	case "debian":
		interfaces, err = network.ProcessDebianNetconf(string(netconfBytes))
	default:
		return fmt.Errorf("Unsupported network config format %q", convertNetconf)
	}

	if err != nil {
		return err
	}

	if err := system.WriteNetworkdConfigs(interfaces); err != nil {
		return err
	}
	return system.RestartNetwork(interfaces)
}
<commit_msg>chore(coreos-cloudinit): bump to 0.7.4+git<commit_after>package main

import (
	"encoding/json"
	"flag"
	"fmt"
	"io/ioutil"
	"os"
	"path"

	"github.com/coreos/coreos-cloudinit/datasource"
	"github.com/coreos/coreos-cloudinit/initialize"
	"github.com/coreos/coreos-cloudinit/network"
	"github.com/coreos/coreos-cloudinit/system"
)

const version = "0.7.4+git"

func main() {
	var printVersion bool
	flag.BoolVar(&printVersion, "version", false, "Print the version and exit")

	var ignoreFailure bool
	flag.BoolVar(&ignoreFailure, "ignore-failure", false, "Exits with 0 status in the event of malformed input from user-data")

	var file string
	flag.StringVar(&file, "from-file", "", "Read user-data from provided file")

	var configdrive string
	flag.StringVar(&configdrive, "from-configdrive", "", "Read user-data from provided cloud-drive directory")

	var url string
	flag.StringVar(&url, "from-url", "", "Download user-data from provided url")

	var useProcCmdline bool
	flag.BoolVar(&useProcCmdline, "from-proc-cmdline", false, fmt.Sprintf("Parse %s for '%s=<url>', using the cloud-config served by an HTTP GET to <url>", datasource.ProcCmdlineLocation, datasource.ProcCmdlineCloudConfigFlag))

	var convertNetconf string
	flag.StringVar(&convertNetconf, "convert-netconf", "", "Read the network config provided in cloud-drive and translate it from the specified format into networkd unit files (requires the -from-configdrive flag)")

	var workspace string
	flag.StringVar(&workspace, "workspace", "/var/lib/coreos-cloudinit", "Base directory coreos-cloudinit should use to store data")

	var sshKeyName string
	flag.StringVar(&sshKeyName, "ssh-key-name", initialize.DefaultSSHKeyName, "Add SSH keys to the system with the given name")

	flag.Parse()

	if printVersion == true {
		fmt.Printf("coreos-cloudinit version %s\n", version)
		os.Exit(0)
	}

	var ds datasource.Datasource
	if file != "" {
		ds = datasource.NewLocalFile(file)
	} else if url != "" {
		ds = datasource.NewMetadataService(url)
	} else if configdrive != "" {
		ds = datasource.NewConfigDrive(configdrive)
	} else if useProcCmdline {
		ds = datasource.NewProcCmdline()
	} else {
		fmt.Println("Provide one of --from-file, --from-configdrive, --from-url or --from-proc-cmdline")
		os.Exit(1)
	}

	if convertNetconf != "" && configdrive == "" {
		fmt.Println("-convert-netconf flag requires -from-configdrive")
		os.Exit(1)
	}

	switch convertNetconf {
	case "":
	case "debian":
	default:
		fmt.Printf("Invalid option to -convert-netconf: '%s'. Supported options: 'debian'\n", convertNetconf)
		os.Exit(1)
	}

	fmt.Printf("Fetching user-data from datasource of type %q\n", ds.Type())
	userdataBytes, err := ds.Fetch()
	if err != nil {
		fmt.Printf("Failed fetching user-data from datasource: %v\n", err)
		if ignoreFailure {
			os.Exit(0)
		} else {
			os.Exit(1)
		}
	}

	env := initialize.NewEnvironment("/", workspace)
	if len(userdataBytes) > 0 {
		if err := processUserdata(string(userdataBytes), env); err != nil {
			fmt.Printf("Failed resolving user-data: %v\n", err)
			if !ignoreFailure {
				os.Exit(1)
			}
		}
	} else {
		fmt.Println("No user data to handle.")
	}

	if convertNetconf != "" {
		if err := processNetconf(convertNetconf, configdrive); err != nil {
			fmt.Printf("Failed to process network config: %v\n", err)
			if !ignoreFailure {
				os.Exit(1)
			}
		}
	}
}

func processUserdata(userdata string, env *initialize.Environment) error {
	userdata = env.Apply(userdata)

	parsed, err := initialize.ParseUserData(userdata)
	if err != nil {
		fmt.Printf("Failed parsing user-data: %v\n", err)
		return err
	}

	err = initialize.PrepWorkspace(env.Workspace())
	if err != nil {
		fmt.Printf("Failed preparing workspace: %v\n", err)
		return err
	}

	switch t := parsed.(type) {
	case initialize.CloudConfig:
		err = initialize.Apply(t, env)
	case system.Script:
		var path string
		path, err = initialize.PersistScriptInWorkspace(t, env.Workspace())
		if err == nil {
			var name string
			name, err = system.ExecuteScript(path)
			initialize.PersistUnitNameInWorkspace(name, env.Workspace())
		}
	}

	return err
}

func processNetconf(convertNetconf, configdrive string) error {
	openstackRoot := path.Join(configdrive, "openstack")
	metadataFilename := path.Join(openstackRoot, "latest", "meta_data.json")
	metadataBytes, err := ioutil.ReadFile(metadataFilename)
	if err != nil {
		return err
	}

	var metadata struct {
		NetworkConfig struct {
			ContentPath string `json:"content_path"`
		} `json:"network_config"`
	}
	if err := json.Unmarshal(metadataBytes, &metadata); err != nil {
		return err
	}
	configPath := metadata.NetworkConfig.ContentPath
	if configPath == "" {
		fmt.Printf("No network config specified in %q.\n", metadataFilename)
		return nil
	}

	netconfBytes, err := ioutil.ReadFile(path.Join(openstackRoot, configPath))
	if err != nil {
		return err
	}

	var interfaces []network.InterfaceGenerator
	switch convertNetconf {
	case "debian":
		interfaces, err = network.ProcessDebianNetconf(string(netconfBytes))
	default:
		return fmt.Errorf("Unsupported network config format %q", convertNetconf)
	}

	if err != nil {
		return err
	}

	if err := system.WriteNetworkdConfigs(interfaces); err != nil {
		return err
	}
	return system.RestartNetwork(interfaces)
}
<|endoftext|>


<commit_before>package main

import "errors"
import "fmt"
import "io/ioutil"
import "strings"
import "os"
import "path/filepath"
import "github.com/howeyc/fsnotify"
import "mk/Apollo/events"

var MainContext *Context

// modules by name
var modules map[string]*Module = make(map[string]*Module)

// modules by path
var modulesByPath map[string]*Module = make(map[string]*Module)
var moduleSearchPaths = []string{"modules"}
var watcher *fsnotify.Watcher
var watcherDone chan bool

type Context struct {
	symbols map[string]Data
	parent  *Context
	usages  []Usage
}

type Usage struct {
	context *Context
	prefix  string
}

type Module struct {
	name    string
	source  string
	context *Context
}

func (module *Module) Refresh() {
	module.Reload()

	// reimport this module into all usage contexts
	for _, usage := range module.context.usages {
		usage.context.Reimport(module.context, usage.prefix)
	}
}

func (c *Context) String() string {
	return "Context"
}

func (c *Context) Equals(other Data) bool {
	return false
}

func (c *Context) GetType() DataType {
	return ContextType
}

// Gets a module by name. Loads the module beforehand if necessary
func GetModule(name string, env *Context) *Module {
	module, ok := modules[name]
	if !ok {
		module = LoadModule(name, env)
		modules[name] = module
	}

	return module
}

func FindModuleFile(name string) string {
	for _, modulePath := range moduleSearchPaths {
		path := modulePath + "/" + strings.Replace(name, ".", "/", -1) + ".glisp"
		absPath, err := filepath.Abs(path)
		if err != nil {
			panic(err.Error())
		}
		if _, err := os.Stat(absPath); err == nil {
			return absPath
		}
	}

	return ""
}

func (module *Module) Reload() {
	bytes, err := ioutil.ReadFile(module.source)
	if err != nil {
		panic(fmt.Sprintf("Failed to reload module %s: %s", module.name, err.Error()))
	}

	text := "(do " + string(bytes) + ""
	_, err = EvaluateString(text, module.context)
	if err != nil {
		panic(err.Error())
	}
}

func LoadModule(name string, env *Context) *Module {
	path := FindModuleFile(name)
	if path == "" {
		panic(fmt.Sprintf("Module %s could not be found in search path", name))
	}
	bytes, err := ioutil.ReadFile(path)
	if err != nil {
		panic(fmt.Sprintf("Failed to load module %s: %s", name, err.Error()))
	}

	text := "(do " + string(bytes) + ""
	context := NewContext()
	context.parent = env

	_, err = EvaluateString(text, context)
	if err == nil {
		module := new(Module)
		module.name = name
		module.context = context
		module.source = path

		err := watcher.Watch(filepath.Dir(path))
		if err != nil {
			fmt.Print(err.Error())
		}

		modulesByPath[path] = module

		return module
	} else {
		panic(err.Error())
	}
}

func NewContext() *Context {
	return &Context{
		make(map[string]Data),
		nil,
		make([]Usage, 0),
	}
}

func (c *Context) Define(symbol Symbol, value Data) {
	c.symbols[symbol.Value] = value
}

func (c *Context) IsDefined(symbol Symbol) bool {
	_, defined := c.symbols[symbol.Value]
	if !defined && c.parent != nil {
		return c.parent.IsDefined(symbol)
	}
	return defined
}

func (c *Context) LookUp(symbol Symbol) Data {
	val, defined := c.symbols[symbol.Value]
	if defined {
		return val
	} else if c.parent != nil {
		return c.parent.LookUp(symbol)
	} else {
		return nil
	}
}

func (c *Context) Reimport(other *Context, prefix string) {
	for key, value := range other.symbols {
		c.symbols[prefix+key] = value
	}
}

func (c *Context) Import(other *Context, prefix string) {
	for key, value := range other.symbols {
		c.symbols[prefix+key] = value
	}

	other.usages = append(other.usages, Usage{c, prefix})
}

func EvaluateString(code string, context *Context) (Data, error) {
	ast, err := Parse(code)
	if err != nil {
		return nil, err
	}

	result, err := Evaluate(ast, context)
	if err != nil {
		return nil, err
	}

	return result, nil
}

func Evaluate(code Data, context *Context) (Data, error) {
	defer func() {
		if e := recover(); e != nil {
			fmt.Printf("%v in %v\n", e, code)
		}
	}()

	switch t := code.(type) {
	case List:
		// copy the list because we're going to mutate it
		t = t.SliceFrom(0)

		if t.evaluated {
			// if the list was already evaluated just return its contents as is
			return code, nil
		} else if t.Len() == 0 {
			return nil, errors.New("invalid function invocation")
		}

		// first expression must be a symbol
		symbol, ok := t.Front().Value.(Symbol)
		if ok {
			// look up the value for that symbol
			fn := context.LookUp(symbol)
			if fn != nil {
				// check if we can call it as a function
				fn, ok := fn.(Caller)
				if ok {
					t.Remove(t.Front())
					return fn.Call(t, context), nil
				} else {
					return nil, errors.New(fmt.Sprintf("%s is not a function", t.Get(0)))
				}
			} else {
				return nil, errors.New(fmt.Sprintf("%s is not defined", t.Get(0)))
			}
		}

		function, ok := t.Front().Value.(Function)
		if ok {
			t.Remove(t.Front()) // remove function name from list to get only arguments
			return function.Call(t, context), nil
		}

		return nil, errors.New(fmt.Sprintf("%s is neither a symbol nor a function and cannot be called as such", t.Get(0)))
	case Keyword:
		return t, nil
	case Symbol:
		// look up the symbol and returns its value
		result := context.LookUp(t)
		if result != nil {
			return context.LookUp(t), nil
		} else {
			return nil, errors.New(fmt.Sprintf("%s is not defined", t.Value))
		}
	}

	return code, nil
}

func initWatchdog() {
	w, err := fsnotify.NewWatcher()
	if err != nil {
		panic(err.Error())
	}

	watcher = w
	//watcherDone = make(chan bool)

	go func() {
		for {
			select {
			case ev := <-watcher.Event:
				if ev == nil {
					return
				}

				if ev.IsModify() && strings.HasSuffix(ev.Name, ".glisp") {
					if module, ok := modulesByPath[ev.Name]; ok {
						module.Refresh()
					}
				}
			case err := <-watcher.Error:
				if err == nil {
					return
				}
			}
		}
	}()
}

func InitRuntime() {
	initWatchdog()
	MainContext = CreateMainContext()
	ECS_init()
}

func shutdownWatchdog() {
	//<-watcherDone
	watcher.Close()
}

func ShutdownRuntime() {
	eventBus := MainContext.symbols["$events"].(NativeObject).Value.(*events.EventBus)
	eventBus.Shutdown()

	ECS_shutdown()
	shutdownWatchdog()
}

func CreateMainContext() *Context {
	context := NewContext()
	context.symbols["Int"] = IntType
	context.symbols["Float"] = FloatType
	context.symbols["Bool"] = BoolType
	context.symbols["String"] = StringType
	context.symbols["Symbol"] = SymbolType
	context.symbols["Keyword"] = KeywordType
	context.symbols["List"] = ListType
	context.symbols["Dict"] = DictType
	context.symbols["NativeFunction"] = NativeFunctionType
	context.symbols["NativeFunctionB"] = NativeFunctionBType

	context.symbols["Nothing"] = Nothing{}
	context.symbols["true"] = Bool{true}
	context.symbols["false"] = Bool{false}

	context.symbols["do"] = NativeFunction{_do}
	context.symbols["def"] = NativeFunctionB{_def}
	context.symbols["type"] = NativeFunction{_type}
	context.symbols["str"] = NativeFunction{_str}
	context.symbols["fn"] = NativeFunctionB{_fn}
	context.symbols["defn"] = NativeFunctionB{_defn}
	context.symbols["defn|"] = NativeFunctionB{_extend_function}
	context.symbols["lambda"] = NativeFunctionB{_lambda}

	context.symbols["symbol"] = NativeFunction{_symbol}
	context.symbols["keyword"] = NativeFunction{_keyword}
	context.symbols["list"] = NativeFunction{_list}
	context.symbols["dict"] = NativeFunction{_dict}

	context.symbols["print"] = NativeFunction{_print}

	context.symbols["do"] = NativeFunction{_do}
	context.symbols["let"] = NativeFunctionB{_let}
	context.symbols["foreach"] = NativeFunction{_foreach}
	context.symbols["map"] = NativeFunction{_map}
	context.symbols["filter"] = NativeFunction{_filter}
	context.symbols["apply"] = NativeFunction{_apply}

	// control flow
	context.symbols["if"] = NativeFunctionB{_if}
	context.symbols["="] = NativeFunction{_equals}

	context.symbols["get"] = NativeFunction{_get}
	context.symbols["put"] = NativeFunction{_put}
	context.symbols["slice"] = NativeFunction{_slice}
	context.symbols["len"] = NativeFunction{_len}
	context.symbols["append"] = NativeFunction{_append}
	context.symbols["prepend"] = NativeFunction{_prepend}
	context.symbols["first"] = NativeFunction{_first}
	context.symbols["last"] = NativeFunction{_last}

	context.symbols["+"] = NativeFunction{_plus}
	context.symbols["-"] = NativeFunction{_minus}
	context.symbols["*"] = NativeFunction{_multiply}
	context.symbols["/"] = NativeFunction{_divide}

	context.symbols["compare"] = NativeFunction{_compare}
	context.symbols["<"] = NativeFunction{_lesser_than}
	context.symbols[">"] = NativeFunction{_greater_than}
	context.symbols["<="] = NativeFunction{_lesser_than_or_equal}
	context.symbols[">="] = NativeFunction{_greater_than_or_equal}
	context.symbols["=="] = NativeFunction{_equals}

	context.symbols["range"] = NativeFunction{_range}

	context.symbols["import"] = NativeFunctionB{_import}
	context.symbols["$core"] = context
	context.symbols["code"] = NativeFunction{_code}

	context.symbols["entity"] = NativeFunction{_entity}

	context.symbols["defevent"] = NativeFunctionB{_defevent}
	context.symbols["subscribe"] = NativeFunction{_subscribe}
	context.symbols["unsubscribe"] = NativeFunction{_unsubscribe}
	context.symbols["trigger"] = NativeFunction{_trigger}

	// event system
	eventBus := new(events.EventBus)
	eventBus.Init()
	context.symbols["$events"] = NativeObject{eventBus}

	// import aux. functions defined in gamelisp itself
	coreModule := GetModule("$core", context)
	context.Import(coreModule.context, "")

	return context
}
<commit_msg>hot swapping function definitions<commit_after>package main

import "errors"
import "fmt"
import "io/ioutil"
import "strings"
import "os"
import "path/filepath"
import "github.com/howeyc/fsnotify"
import "mk/Apollo/events"

var MainContext *Context

// modules by name
var modules map[string]*Module = make(map[string]*Module)

// modules by path
var modulesByPath map[string]*Module = make(map[string]*Module)
var moduleSearchPaths = []string{"modules"}
var watcher *fsnotify.Watcher
var watcherDone chan bool

type Context struct {
	symbols map[string]Data
	parent  *Context
	usages  []Usage
}

type Usage struct {
	context *Context
	prefix  string
}

type Module struct {
	name    string
	source  string
	context *Context
}

func (module *Module) Refresh() {
	module.Reload()

	// reimport this module into all usage contexts
	for _, usage := range module.context.usages {
		usage.context.Reimport(module.context, usage.prefix)
	}
}

func (c *Context) String() string {
	return "Context"
}

func (c *Context) Equals(other Data) bool {
	return false
}

func (c *Context) GetType() DataType {
	return ContextType
}

// Gets a module by name. Loads the module beforehand if necessary
func GetModule(name string, env *Context) *Module {
	module, ok := modules[name]
	if !ok {
		module = LoadModule(name, env)
		modules[name] = module
	}

	return module
}

func FindModuleFile(name string) string {
	for _, modulePath := range moduleSearchPaths {
		path := modulePath + "/" + strings.Replace(name, ".", "/", -1) + ".glisp"
		absPath, err := filepath.Abs(path)
		if err != nil {
			panic(err.Error())
		}
		if _, err := os.Stat(absPath); err == nil {
			return absPath
		}
	}

	return ""
}

func (module *Module) Reload() {
	bytes, err := ioutil.ReadFile(module.source)
	if err != nil {
		panic(fmt.Sprintf("Failed to reload module %s: %s", module.name, err.Error()))
	}

	text := "(do " + string(bytes) + ""
	_, err = EvaluateString(text, module.context)
	if err != nil {
		panic(err.Error())
	}
}

func LoadModule(name string, env *Context) *Module {
	path := FindModuleFile(name)
	if path == "" {
		panic(fmt.Sprintf("Module %s could not be found in search path", name))
	}
	bytes, err := ioutil.ReadFile(path)
	if err != nil {
		panic(fmt.Sprintf("Failed to load module %s: %s", name, err.Error()))
	}

	text := "(do " + string(bytes) + ""
	context := NewContext()
	context.parent = env

	_, err = EvaluateString(text, context)
	if err == nil {
		module := new(Module)
		module.name = name
		module.context = context
		module.source = path

		err := watcher.Watch(filepath.Dir(path))
		if err != nil {
			fmt.Print(err.Error())
		}

		modulesByPath[path] = module

		return module
	} else {
		panic(err.Error())
	}
}

func NewContext() *Context {
	return &Context{
		make(map[string]Data),
		nil,
		make([]Usage, 0),
	}
}

func (c *Context) Define(symbol Symbol, value Data) {
	c.symbols[symbol.Value] = value
}

func (c *Context) IsDefined(symbol Symbol) bool {
	_, defined := c.symbols[symbol.Value]
	if !defined && c.parent != nil {
		return c.parent.IsDefined(symbol)
	}
	return defined
}

func (c *Context) LookUp(symbol Symbol) Data {
	val, defined := c.symbols[symbol.Value]
	if defined {
		return val
	} else if c.parent != nil {
		return c.parent.LookUp(symbol)
	} else {
		return nil
	}
}

func (c *Context) Reimport(other *Context, prefix string) {
	for key, value := range other.symbols {
		if newFunction, ok := value.(*Function); ok {
			if current, ok := c.symbols[prefix+key]; ok {
				if currentFunction, ok := current.(*Function); ok && currentFunction.Name == newFunction.Name {
					// replace function definition in place
					currentFunction.Dispatchers = newFunction.Dispatchers
				}

			}
		}

		c.symbols[prefix+key] = value
	}
}

func (c *Context) Import(other *Context, prefix string) {
	for key, value := range other.symbols {
		c.symbols[prefix+key] = value
	}

	other.usages = append(other.usages, Usage{c, prefix})
}

func EvaluateString(code string, context *Context) (Data, error) {
	ast, err := Parse(code)
	if err != nil {
		return nil, err
	}

	result, err := Evaluate(ast, context)
	if err != nil {
		return nil, err
	}

	return result, nil
}

func Evaluate(code Data, context *Context) (Data, error) {
	defer func() {
		if e := recover(); e != nil {
			fmt.Printf("%v in %v\n", e, code)
		}
	}()

	switch t := code.(type) {
	case List:
		// copy the list because we're going to mutate it
		t = t.SliceFrom(0)

		if t.evaluated {
			// if the list was already evaluated just return its contents as is
			return code, nil
		} else if t.Len() == 0 {
			return nil, errors.New("invalid function invocation")
		}

		// first expression must be a symbol
		symbol, ok := t.Front().Value.(Symbol)
		if ok {
			// look up the value for that symbol
			fn := context.LookUp(symbol)
			if fn != nil {
				// check if we can call it as a function
				fn, ok := fn.(Caller)
				if ok {
					t.Remove(t.Front())
					return fn.Call(t, context), nil
				} else {
					return nil, errors.New(fmt.Sprintf("%s is not a function", t.Get(0)))
				}
			} else {
				return nil, errors.New(fmt.Sprintf("%s is not defined", t.Get(0)))
			}
		}

		function, ok := t.Front().Value.(Function)
		if ok {
			t.Remove(t.Front()) // remove function name from list to get only arguments
			return function.Call(t, context), nil
		}

		return nil, errors.New(fmt.Sprintf("%s is neither a symbol nor a function and cannot be called as such", t.Get(0)))
	case Keyword:
		return t, nil
	case Symbol:
		// look up the symbol and returns its value
		result := context.LookUp(t)
		if result != nil {
			return context.LookUp(t), nil
		} else {
			return nil, errors.New(fmt.Sprintf("%s is not defined", t.Value))
		}
	}

	return code, nil
}

func initWatchdog() {
	w, err := fsnotify.NewWatcher()
	if err != nil {
		panic(err.Error())
	}

	watcher = w
	//watcherDone = make(chan bool)

	go func() {
		for {
			select {
			case ev := <-watcher.Event:
				if ev == nil {
					return
				}

				if ev.IsModify() && strings.HasSuffix(ev.Name, ".glisp") {
					if module, ok := modulesByPath[ev.Name]; ok {
						module.Refresh()
					}
				}
			case err := <-watcher.Error:
				if err == nil {
					return
				}
			}
		}
	}()
}

func InitRuntime() {
	initWatchdog()
	MainContext = CreateMainContext()
	ECS_init()
}

func shutdownWatchdog() {
	//<-watcherDone
	watcher.Close()
}

func ShutdownRuntime() {
	eventBus := MainContext.symbols["$events"].(NativeObject).Value.(*events.EventBus)
	eventBus.Shutdown()

	ECS_shutdown()
	shutdownWatchdog()
}

func CreateMainContext() *Context {
	context := NewContext()
	context.symbols["Int"] = IntType
	context.symbols["Float"] = FloatType
	context.symbols["Bool"] = BoolType
	context.symbols["String"] = StringType
	context.symbols["Symbol"] = SymbolType
	context.symbols["Keyword"] = KeywordType
	context.symbols["List"] = ListType
	context.symbols["Dict"] = DictType
	context.symbols["NativeFunction"] = NativeFunctionType
	context.symbols["NativeFunctionB"] = NativeFunctionBType

	context.symbols["Nothing"] = Nothing{}
	context.symbols["true"] = Bool{true}
	context.symbols["false"] = Bool{false}

	context.symbols["do"] = NativeFunction{_do}
	context.symbols["def"] = NativeFunctionB{_def}
	context.symbols["type"] = NativeFunction{_type}
	context.symbols["str"] = NativeFunction{_str}
	context.symbols["fn"] = NativeFunctionB{_fn}
	context.symbols["defn"] = NativeFunctionB{_defn}
	context.symbols["defn|"] = NativeFunctionB{_extend_function}
	context.symbols["lambda"] = NativeFunctionB{_lambda}

	context.symbols["symbol"] = NativeFunction{_symbol}
	context.symbols["keyword"] = NativeFunction{_keyword}
	context.symbols["list"] = NativeFunction{_list}
	context.symbols["dict"] = NativeFunction{_dict}

	context.symbols["print"] = NativeFunction{_print}

	context.symbols["do"] = NativeFunction{_do}
	context.symbols["let"] = NativeFunctionB{_let}
	context.symbols["foreach"] = NativeFunction{_foreach}
	context.symbols["map"] = NativeFunction{_map}
	context.symbols["filter"] = NativeFunction{_filter}
	context.symbols["apply"] = NativeFunction{_apply}

	// control flow
	context.symbols["if"] = NativeFunctionB{_if}
	context.symbols["="] = NativeFunction{_equals}

	context.symbols["get"] = NativeFunction{_get}
	context.symbols["put"] = NativeFunction{_put}
	context.symbols["slice"] = NativeFunction{_slice}
	context.symbols["len"] = NativeFunction{_len}
	context.symbols["append"] = NativeFunction{_append}
	context.symbols["prepend"] = NativeFunction{_prepend}
	context.symbols["first"] = NativeFunction{_first}
	context.symbols["last"] = NativeFunction{_last}

	context.symbols["+"] = NativeFunction{_plus}
	context.symbols["-"] = NativeFunction{_minus}
	context.symbols["*"] = NativeFunction{_multiply}
	context.symbols["/"] = NativeFunction{_divide}

	context.symbols["compare"] = NativeFunction{_compare}
	context.symbols["<"] = NativeFunction{_lesser_than}
	context.symbols[">"] = NativeFunction{_greater_than}
	context.symbols["<="] = NativeFunction{_lesser_than_or_equal}
	context.symbols[">="] = NativeFunction{_greater_than_or_equal}
	context.symbols["=="] = NativeFunction{_equals}

	context.symbols["range"] = NativeFunction{_range}

	context.symbols["import"] = NativeFunctionB{_import}
	context.symbols["$core"] = context
	context.symbols["code"] = NativeFunction{_code}

	context.symbols["entity"] = NativeFunction{_entity}

	context.symbols["defevent"] = NativeFunctionB{_defevent}
	context.symbols["subscribe"] = NativeFunction{_subscribe}
	context.symbols["unsubscribe"] = NativeFunction{_unsubscribe}
	context.symbols["trigger"] = NativeFunction{_trigger}

	// event system
	eventBus := new(events.EventBus)
	eventBus.Init()
	context.symbols["$events"] = NativeObject{eventBus}

	// import aux. functions defined in gamelisp itself
	coreModule := GetModule("$core", context)
	context.Import(coreModule.context, "")

	return context
}
<|endoftext|>


<commit_before>package main

import (
	goflag "flag"
	"fmt"
	"github.com/gesellix/couchdb-prometheus-exporter/glogadapt"
	"github.com/gesellix/couchdb-prometheus-exporter/lib"
	"github.com/golang/glog"
	"github.com/namsral/flag"
	"github.com/prometheus/client_golang/prometheus"
	"github.com/prometheus/client_golang/prometheus/promhttp"
	"net/http"
	"strconv"
	"strings"
)

type exporterConfigType struct {
	listenAddress   string
	metricsEndpoint string
	couchdbURI      string
	couchdbUsername string
	couchdbPassword string
	couchdbInsecure bool
	databases       string
}

var exporterConfig exporterConfigType

func init() {
	flag.String(flag.DefaultConfigFlagname, "", "path to config file")
	flag.StringVar(&exporterConfig.listenAddress, "telemetry.address", "localhost:9984", "Address on which to expose metrics.")
	flag.StringVar(&exporterConfig.metricsEndpoint, "telemetry.endpoint", "/metrics", "Path under which to expose metrics.")
	flag.StringVar(&exporterConfig.couchdbURI, "couchdb.uri", "http://localhost:5984", "URI to the CouchDB instance")
	flag.StringVar(&exporterConfig.couchdbUsername, "couchdb.username", "", "Basic auth username for the CouchDB instance")
	flag.StringVar(&exporterConfig.couchdbPassword, "couchdb.password", "", "Basic auth password for the CouchDB instance")
	flag.BoolVar(&exporterConfig.couchdbInsecure, "couchdb.insecure", true, "Ignore server certificate if using https")
	flag.StringVar(&exporterConfig.databases, "databases", "", fmt.Sprintf("Comma separated list of database names, or '%s'", lib.AllDbs))

	flag.BoolVar(&glogadapt.Logging.ToStderr, "logtostderr", false, "log to standard error instead of files")
	flag.BoolVar(&glogadapt.Logging.AlsoToStderr, "alsologtostderr", false, "log to standard error as well as files")
	flag.Var(&glogadapt.Logging.Verbosity, "v", "log level for V logs")
	flag.Var(&glogadapt.Logging.StderrThreshold, "stderrthreshold", "logs at or above this threshold go to stderr")
	flag.StringVar(&glogadapt.Logging.LogDir, "log_dir", "", "If non-empty, write log files in this directory")
}

func main() {
	flag.Parse()
	// Convinces goflags that we have called Parse() to avoid noisy logs.
	// Necessary due to https://github.com/golang/glog/commit/65d674618f712aa808a7d0104131b9206fc3d5ad
	// and us using another flags package.
	goflag.CommandLine.Parse([]string{})
	goflag.Lookup("logtostderr").Value.Set(strconv.FormatBool(*&glogadapt.Logging.ToStderr))
	goflag.Lookup("alsologtostderr").Value.Set(strconv.FormatBool(*&glogadapt.Logging.AlsoToStderr))
	goflag.Lookup("v").Value.Set(glogadapt.Logging.Verbosity.String())
	goflag.Lookup("stderrthreshold").Value.Set(glogadapt.Logging.StderrThreshold.String())
	goflag.Lookup("log_dir").Value.Set(glogadapt.Logging.LogDir)

	var databases []string
	if *&exporterConfig.databases != "" {
		databases = strings.Split(*&exporterConfig.databases, ",")
	}

	exporter := lib.NewExporter(
		*&exporterConfig.couchdbURI,
		lib.BasicAuth{
			Username: *&exporterConfig.couchdbUsername,
			Password: *&exporterConfig.couchdbPassword},
		lib.CollectorConfig{
			Databases:    databases,
			CollectViews: true,
		},
		*&exporterConfig.couchdbInsecure)
	prometheus.MustRegister(exporter)

	http.Handle(*&exporterConfig.metricsEndpoint, promhttp.Handler())
	http.HandleFunc("/status", func(w http.ResponseWriter, r *http.Request) {
		fmt.Fprint(w, "OK")
	})
	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		http.Error(w, fmt.Sprintf("Please GET %s", *&exporterConfig.metricsEndpoint), http.StatusNotFound)
	})

	glog.Infof("Starting exporter at '%s' to read from CouchDB at '%s'", *&exporterConfig.listenAddress, *&exporterConfig.couchdbURI)
	err := http.ListenAndServe(*&exporterConfig.listenAddress, nil)
	if err != nil {
		glog.Fatal(err)
	}
}
<commit_msg>make view metrics collection configurable (default: enabled)<commit_after>package main

import (
	goflag "flag"
	"fmt"
	"github.com/gesellix/couchdb-prometheus-exporter/glogadapt"
	"github.com/gesellix/couchdb-prometheus-exporter/lib"
	"github.com/golang/glog"
	"github.com/namsral/flag"
	"github.com/prometheus/client_golang/prometheus"
	"github.com/prometheus/client_golang/prometheus/promhttp"
	"net/http"
	"strconv"
	"strings"
)

type exporterConfigType struct {
	listenAddress   string
	metricsEndpoint string
	couchdbURI      string
	couchdbUsername string
	couchdbPassword string
	couchdbInsecure bool
	databases       string
	databaseViews   bool
}

var exporterConfig exporterConfigType

func init() {
	flag.String(flag.DefaultConfigFlagname, "", "path to config file")
	flag.StringVar(&exporterConfig.listenAddress, "telemetry.address", "localhost:9984", "Address on which to expose metrics.")
	flag.StringVar(&exporterConfig.metricsEndpoint, "telemetry.endpoint", "/metrics", "Path under which to expose metrics.")
	flag.StringVar(&exporterConfig.couchdbURI, "couchdb.uri", "http://localhost:5984", "URI to the CouchDB instance")
	flag.StringVar(&exporterConfig.couchdbUsername, "couchdb.username", "", "Basic auth username for the CouchDB instance")
	flag.StringVar(&exporterConfig.couchdbPassword, "couchdb.password", "", "Basic auth password for the CouchDB instance")
	flag.BoolVar(&exporterConfig.couchdbInsecure, "couchdb.insecure", true, "Ignore server certificate if using https")
	flag.StringVar(&exporterConfig.databases, "databases", "", fmt.Sprintf("Comma separated list of database names, or '%s'", lib.AllDbs))
	flag.BoolVar(&exporterConfig.databaseViews, "databases.views", true, "Collect view details of every observed database")

	flag.BoolVar(&glogadapt.Logging.ToStderr, "logtostderr", false, "log to standard error instead of files")
	flag.BoolVar(&glogadapt.Logging.AlsoToStderr, "alsologtostderr", false, "log to standard error as well as files")
	flag.Var(&glogadapt.Logging.Verbosity, "v", "log level for V logs")
	flag.Var(&glogadapt.Logging.StderrThreshold, "stderrthreshold", "logs at or above this threshold go to stderr")
	flag.StringVar(&glogadapt.Logging.LogDir, "log_dir", "", "If non-empty, write log files in this directory")
}

func main() {
	flag.Parse()
	// Convinces goflags that we have called Parse() to avoid noisy logs.
	// Necessary due to https://github.com/golang/glog/commit/65d674618f712aa808a7d0104131b9206fc3d5ad
	// and us using another flags package.
	goflag.CommandLine.Parse([]string{})
	goflag.Lookup("logtostderr").Value.Set(strconv.FormatBool(*&glogadapt.Logging.ToStderr))
	goflag.Lookup("alsologtostderr").Value.Set(strconv.FormatBool(*&glogadapt.Logging.AlsoToStderr))
	goflag.Lookup("v").Value.Set(glogadapt.Logging.Verbosity.String())
	goflag.Lookup("stderrthreshold").Value.Set(glogadapt.Logging.StderrThreshold.String())
	goflag.Lookup("log_dir").Value.Set(glogadapt.Logging.LogDir)

	var databases []string
	if *&exporterConfig.databases != "" {
		databases = strings.Split(*&exporterConfig.databases, ",")
	}

	exporter := lib.NewExporter(
		*&exporterConfig.couchdbURI,
		lib.BasicAuth{
			Username: *&exporterConfig.couchdbUsername,
			Password: *&exporterConfig.couchdbPassword},
		lib.CollectorConfig{
			Databases:    databases,
			CollectViews: *&exporterConfig.databaseViews,
		},
		*&exporterConfig.couchdbInsecure)
	prometheus.MustRegister(exporter)

	http.Handle(*&exporterConfig.metricsEndpoint, promhttp.Handler())
	http.HandleFunc("/status", func(w http.ResponseWriter, r *http.Request) {
		fmt.Fprint(w, "OK")
	})
	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		http.Error(w, fmt.Sprintf("Please GET %s", *&exporterConfig.metricsEndpoint), http.StatusNotFound)
	})

	glog.Infof("Starting exporter at '%s' to read from CouchDB at '%s'", *&exporterConfig.listenAddress, *&exporterConfig.couchdbURI)
	err := http.ListenAndServe(*&exporterConfig.listenAddress, nil)
	if err != nil {
		glog.Fatal(err)
	}
}
<|endoftext|>


<commit_before>// +build !windows

package image // import "github.com/docker/docker/integration/image"

import (
	"context"
	"io"
	"io/ioutil"
	"os"
	"strconv"
	"syscall"
	"testing"
	"unsafe"

	"github.com/docker/docker/api/types"
	"github.com/docker/docker/internal/test/daemon"
	"github.com/docker/docker/internal/test/fakecontext"
	"gotest.tools/assert"
	"gotest.tools/skip"
)

// This is a regression test for #38488
// It ensures that orphan layers can be found and cleaned up
// after unsuccessful image removal
func TestRemoveImageGarbageCollector(t *testing.T) {
	// This test uses very platform specific way to prevent
	// daemon for remove image layer.
	skip.If(t, testEnv.DaemonInfo.OSType != "linux")
	skip.If(t, os.Getenv("DOCKER_ENGINE_GOARCH") != "amd64")

	// Create daemon with overlay2 graphdriver because vfs uses disk differently
	// and this test case would not work with it.
	d := daemon.New(t, daemon.WithStorageDriver("overlay2"), daemon.WithImageService)
	d.Start(t)
	defer d.Stop(t)

	ctx := context.Background()
	client := d.NewClientT(t)
	i := d.ImageService()

	img := "test-garbage-collector"

	// Build a image with multiple layers
	dockerfile := `FROM busybox
	RUN echo echo Running... > /run.sh`
	source := fakecontext.New(t, "", fakecontext.WithDockerfile(dockerfile))
	defer source.Close()
	resp, err := client.ImageBuild(ctx,
		source.AsTarReader(t),
		types.ImageBuildOptions{
			Remove:      true,
			ForceRemove: true,
			Tags:        []string{img},
		})
	assert.NilError(t, err)
	_, err = io.Copy(ioutil.Discard, resp.Body)
	resp.Body.Close()
	assert.NilError(t, err)
	image, _, err := client.ImageInspectWithRaw(ctx, img)
	assert.NilError(t, err)

	// Mark latest image layer to immutable
	data := image.GraphDriver.Data
	file, _ := os.Open(data["UpperDir"])
	attr := 0x00000010
	fsflags := uintptr(0x40086602)
	argp := uintptr(unsafe.Pointer(&attr))
	_, _, errno := syscall.Syscall(syscall.SYS_IOCTL, file.Fd(), fsflags, argp)
	assert.Equal(t, "errno 0", errno.Error())

	// Try to remove the image, it should generate error
	// but marking layer back to mutable before checking errors (so we don't break CI server)
	_, err = client.ImageRemove(ctx, img, types.ImageRemoveOptions{})
	attr = 0x00000000
	argp = uintptr(unsafe.Pointer(&attr))
	_, _, errno = syscall.Syscall(syscall.SYS_IOCTL, file.Fd(), fsflags, argp)
	assert.Equal(t, "errno 0", errno.Error())
	assert.ErrorContains(t, err, "permission denied")

	// Verify that layer remaining on disk
	dir, _ := os.Stat(data["UpperDir"])
	assert.Equal(t, "true", strconv.FormatBool(dir.IsDir()))

	// Run imageService.Cleanup() and make sure that layer was removed from disk
	i.Cleanup()
	dir, err = os.Stat(data["UpperDir"])
	assert.ErrorContains(t, err, "no such file or directory")
}
<commit_msg>Add extra permission check in removal test<commit_after>// +build !windows

package image // import "github.com/docker/docker/integration/image"

import (
	"context"
	"io"
	"io/ioutil"
	"os"
	"strconv"
	"strings"
	"syscall"
	"testing"
	"unsafe"

	"github.com/docker/docker/api/types"
	"github.com/docker/docker/internal/test/daemon"
	"github.com/docker/docker/internal/test/fakecontext"
	"gotest.tools/assert"
	"gotest.tools/skip"
)

// This is a regression test for #38488
// It ensures that orphan layers can be found and cleaned up
// after unsuccessful image removal
func TestRemoveImageGarbageCollector(t *testing.T) {
	// This test uses very platform specific way to prevent
	// daemon for remove image layer.
	skip.If(t, testEnv.DaemonInfo.OSType != "linux")
	skip.If(t, os.Getenv("DOCKER_ENGINE_GOARCH") != "amd64")

	// Create daemon with overlay2 graphdriver because vfs uses disk differently
	// and this test case would not work with it.
	d := daemon.New(t, daemon.WithStorageDriver("overlay2"), daemon.WithImageService)
	d.Start(t)
	defer d.Stop(t)

	ctx := context.Background()
	client := d.NewClientT(t)
	i := d.ImageService()

	img := "test-garbage-collector"

	// Build a image with multiple layers
	dockerfile := `FROM busybox
	RUN echo echo Running... > /run.sh`
	source := fakecontext.New(t, "", fakecontext.WithDockerfile(dockerfile))
	defer source.Close()
	resp, err := client.ImageBuild(ctx,
		source.AsTarReader(t),
		types.ImageBuildOptions{
			Remove:      true,
			ForceRemove: true,
			Tags:        []string{img},
		})
	assert.NilError(t, err)
	_, err = io.Copy(ioutil.Discard, resp.Body)
	resp.Body.Close()
	assert.NilError(t, err)
	image, _, err := client.ImageInspectWithRaw(ctx, img)
	assert.NilError(t, err)

	// Mark latest image layer to immutable
	data := image.GraphDriver.Data
	file, _ := os.Open(data["UpperDir"])
	attr := 0x00000010
	fsflags := uintptr(0x40086602)
	argp := uintptr(unsafe.Pointer(&attr))
	_, _, errno := syscall.Syscall(syscall.SYS_IOCTL, file.Fd(), fsflags, argp)
	assert.Equal(t, "errno 0", errno.Error())

	// Try to remove the image, it should generate error
	// but marking layer back to mutable before checking errors (so we don't break CI server)
	_, err = client.ImageRemove(ctx, img, types.ImageRemoveOptions{})
	attr = 0x00000000
	argp = uintptr(unsafe.Pointer(&attr))
	_, _, errno = syscall.Syscall(syscall.SYS_IOCTL, file.Fd(), fsflags, argp)
	assert.Equal(t, "errno 0", errno.Error())
	assert.Assert(t, err != nil)
	errStr := err.Error()
	if !(strings.Contains(errStr, "permission denied") || strings.Contains(errStr, "operation not permitted")) {
		t.Errorf("ImageRemove error not an permission error %s", errStr)
	}

	// Verify that layer remaining on disk
	dir, _ := os.Stat(data["UpperDir"])
	assert.Equal(t, "true", strconv.FormatBool(dir.IsDir()))

	// Run imageService.Cleanup() and make sure that layer was removed from disk
	i.Cleanup()
	dir, err = os.Stat(data["UpperDir"])
	assert.ErrorContains(t, err, "no such file or directory")
}
<|endoftext|>


<commit_before>// Package gitfs implements a git cli based RCS backend.
package gitfs

import (
	"bytes"
	"context"
	"fmt"
	"io"
	"os"
	"os/exec"
	"path/filepath"
	"strconv"
	"strings"
	"time"

	"github.com/gopasspw/gopass/internal/backend"
	"github.com/gopasspw/gopass/internal/backend/storage/fs"
	"github.com/gopasspw/gopass/internal/out"
	"github.com/gopasspw/gopass/internal/store"
	"github.com/gopasspw/gopass/pkg/ctxutil"
	"github.com/gopasspw/gopass/pkg/debug"
	"github.com/gopasspw/gopass/pkg/fsutil"

	"github.com/blang/semver/v4"
)

type contextKey int

const (
	ctxKeyPathOverride contextKey = iota
)

func withPathOverride(ctx context.Context, path string) context.Context {
	return context.WithValue(ctx, ctxKeyPathOverride, path)
}

func getPathOverride(ctx context.Context, def string) string {
	if sv, ok := ctx.Value(ctxKeyPathOverride).(string); ok && sv != "" {
		return sv
	}
	return def
}

// Git is a cli based git backend
type Git struct {
	fs *fs.Store
}

// New creates a new git cli based git backend
func New(path string) (*Git, error) {
	if !fsutil.IsDir(filepath.Join(path, ".git")) {
		return nil, fmt.Errorf("git repo does not exist")
	}
	return &Git{
		fs: fs.New(path),
	}, nil
}

// Clone clones an existing git repo and returns a new cli based git backend
// configured for this clone repo
func Clone(ctx context.Context, repo, path string) (*Git, error) {
	g := &Git{
		fs: fs.New(path),
	}
	if err := g.Cmd(withPathOverride(ctx, filepath.Dir(path)), "Clone", "clone", repo, path); err != nil {
		return nil, err
	}
	return g, nil
}

// Init initializes this store's git repo
func Init(ctx context.Context, path, userName, userEmail string) (*Git, error) {
	g := &Git{
		fs: fs.New(path),
	}
	// the git repo may be empty (i.e. no branches, cloned from a fresh remote)
	// or already initialized. Only run git init if the folder is completely empty
	if !g.IsInitialized() {
		if err := g.Cmd(ctx, "Init", "init"); err != nil {
			return nil, fmt.Errorf("failed to initialize git: %s", err)
		}
		out.Printf(ctx, "git initialized at %s", g.fs.Path())
	}

	if !ctxutil.IsGitInit(ctx) {
		return g, nil
	}

	// initialize the local git config
	if err := g.InitConfig(ctx, userName, userEmail); err != nil {
		return g, fmt.Errorf("failed to configure git: %s", err)
	}
	out.Printf(ctx, "git configured at %s", g.fs.Path())

	// add current content of the store
	if err := g.Add(ctx, g.fs.Path()); err != nil {
		return g, fmt.Errorf("failed to add %q to git: %w", g.fs.Path(), err)
	}

	// commit if there is something to commit
	if !g.HasStagedChanges(ctx) {
		debug.Log("No staged changes")
		return g, nil
	}

	if err := g.Commit(ctx, "Add current content of password store"); err != nil {
		return g, fmt.Errorf("failed to commit changes to git: %w", err)
	}

	return g, nil
}

func (g *Git) captureCmd(ctx context.Context, name string, args ...string) ([]byte, []byte, error) {
	bufOut := &bytes.Buffer{}
	bufErr := &bytes.Buffer{}

	cmd := exec.CommandContext(ctx, "git", args[0:]...)
	cmd.Dir = getPathOverride(ctx, g.fs.Path())
	cmd.Stdout = bufOut
	cmd.Stderr = bufErr

	if debug.IsEnabled() && ctxutil.IsVerbose(ctx) {
		cmd.Stdout = io.MultiWriter(bufOut, os.Stdout)
		cmd.Stderr = io.MultiWriter(bufErr, os.Stderr)
	}

	debug.Log("store.%s: %s %+v (%s)", name, cmd.Path, cmd.Args, g.fs.Path())
	err := cmd.Run()
	return bufOut.Bytes(), bufErr.Bytes(), err
}

// Cmd runs an git command
func (g *Git) Cmd(ctx context.Context, name string, args ...string) error {
	stdout, stderr, err := g.captureCmd(ctx, name, args...)
	if err != nil {
		debug.Log("CMD: %s %+v\nError: %s\nOutput:\n  Stdout: %q\n  Stderr: %q", name, args, err, string(stdout), string(stderr))
		return fmt.Errorf("%s: %s", err, strings.TrimSpace(string(stderr)))
	}

	return nil
}

// Name returns git
func (g *Git) Name() string {
	return "git"
}

// Version returns the git version as major, minor and patch level
func (g *Git) Version(ctx context.Context) semver.Version {
	v := semver.Version{}

	cmd := exec.CommandContext(ctx, "git", "version")
	cmdout, err := cmd.Output()
	if err != nil {
		debug.Log("Failed to run 'git version': %s", err)
		return v
	}

	svStr := strings.TrimPrefix(string(cmdout), "git version ")
	if p := strings.Fields(svStr); len(p) > 0 {
		svStr = p[0]
	}

	sv, err := semver.ParseTolerant(svStr)
	if err != nil {
		debug.Log("Failed to parse %q as semver: %s", svStr, err)
		return v
	}
	return sv
}

// IsInitialized returns true if this stores has an (probably) initialized .git folder
func (g *Git) IsInitialized() bool {
	return fsutil.IsFile(filepath.Join(g.fs.Path(), ".git", "config"))
}

// Add adds the listed files to the git index
func (g *Git) Add(ctx context.Context, files ...string) error {
	if !g.IsInitialized() {
		return store.ErrGitNotInit
	}

	for i := range files {
		files[i] = strings.TrimPrefix(files[i], g.fs.Path()+"/")
	}

	args := []string{"add", "--all", "--force"}
	args = append(args, files...)

	return g.Cmd(ctx, "gitAdd", args...)
}

// HasStagedChanges returns true if there are any staged changes which can be committed
func (g *Git) HasStagedChanges(ctx context.Context) bool {
	if err := g.Cmd(ctx, "gitDiffIndex", "diff-index", "--quiet", "HEAD"); err != nil {
		return true
	}
	return false
}

// Commit creates a new git commit with the given commit message
func (g *Git) Commit(ctx context.Context, msg string) error {
	if !g.IsInitialized() {
		return store.ErrGitNotInit
	}

	if !g.HasStagedChanges(ctx) {
		return store.ErrGitNothingToCommit
	}

	return g.Cmd(ctx, "gitCommit", "commit", fmt.Sprintf("--date=%d +00:00", ctxutil.GetCommitTimestamp(ctx).UTC().Unix()), "-m", msg)
}

func (g *Git) defaultRemote(ctx context.Context, branch string) string {
	opts, err := g.ConfigList(ctx)
	if err != nil {
		return "origin"
	}

	remote := opts["branch."+branch+".remote"]
	if remote == "" {
		return "origin"
	}

	needle := "remote." + remote + ".url"
	for k := range opts {
		if k == needle {
			return remote
		}
	}
	return "origin"
}

func (g *Git) defaultBranch(ctx context.Context) string {
	out, _, err := g.captureCmd(ctx, "defaultBranch", "rev-parse", "--abbrev-ref", "HEAD")
	if err != nil || string(out) == "" {
		// see https://github.com/github/renaming
		return "main"
	}
	return strings.TrimSpace(string(out))
}

// PushPull pushes the repo to it's origin.
// optional arguments: remote and branch
func (g *Git) PushPull(ctx context.Context, op, remote, branch string) error {
	if ctxutil.IsNoNetwork(ctx) {
		debug.Log("Skipping network ops. NoNetwork=true")
		return nil
	}
	if !g.IsInitialized() {
		return store.ErrGitNotInit
	}

	if branch == "" {
		branch = g.defaultBranch(ctx)
	}
	if remote == "" {
		remote = g.defaultRemote(ctx, branch)
	}

	if v, err := g.ConfigGet(ctx, "remote."+remote+".url"); err != nil || v == "" {
		return store.ErrGitNoRemote
	}

	if err := g.Cmd(ctx, "gitPush", "pull", remote, branch); err != nil {
		if op == "pull" {
			return err
		}
		out.Warningf(ctx, "Failed to pull before git push: %s", err)
	}
	if op == "pull" {
		return nil
	}

	return g.Cmd(ctx, "gitPush", "push", remote, branch)
}

// Push pushes to the git remote
func (g *Git) Push(ctx context.Context, remote, branch string) error {
	if ctxutil.IsNoNetwork(ctx) {
		debug.Log("Skipping network ops. NoNetwork=true")
		return nil
	}
	return g.PushPull(ctx, "push", remote, branch)
}

// Pull pulls from the git remote
func (g *Git) Pull(ctx context.Context, remote, branch string) error {
	if ctxutil.IsNoNetwork(ctx) {
		debug.Log("Skipping network ops. NoNetwork=true")
		return nil
	}
	return g.PushPull(ctx, "pull", remote, branch)
}

// AddRemote adds a new remote
func (g *Git) AddRemote(ctx context.Context, remote, url string) error {
	return g.Cmd(ctx, "gitAddRemote", "remote", "add", remote, url)
}

// RemoveRemote removes a remote
func (g *Git) RemoveRemote(ctx context.Context, remote string) error {
	return g.Cmd(ctx, "gitRemoveRemote", "remote", "remove", remote)
}

// Revisions will list all available revisions of the named entity
// see http://blog.lost-theory.org/post/how-to-parse-git-log-output/
// and https://git-scm.com/docs/git-log#_pretty_formats
func (g *Git) Revisions(ctx context.Context, name string) ([]backend.Revision, error) {
	args := []string{
		"log",
		`--format=%H%x1f%an%x1f%ae%x1f%at%x1f%s%x1f%b%x1e`,
		"--",
		name,
	}
	stdout, stderr, err := g.captureCmd(ctx, "Revisions", args...)
	if err != nil {
		debug.Log("Command failed: %s", string(stderr))
		return nil, err
	}
	so := string(stdout)
	revs := make([]backend.Revision, 0, strings.Count(so, "\x1e"))
	for _, rev := range strings.Split(so, "\x1e") {
		rev = strings.TrimSpace(rev)
		if rev == "" {
			continue
		}

		p := strings.Split(rev, "\x1f")
		if len(p) < 1 {
			continue
		}

		r := backend.Revision{}
		r.Hash = p[0]
		if len(p) > 1 {
			r.AuthorName = p[1]
		}
		if len(p) > 2 {
			r.AuthorEmail = p[2]
		}
		if len(p) > 3 {
			if iv, err := strconv.ParseInt(p[3], 10, 64); err == nil {
				r.Date = time.Unix(iv, 0)
			}
		}
		if len(p) > 4 {
			r.Subject = p[4]
		}
		if len(p) > 5 {
			r.Body = p[5]
		}
		revs = append(revs, r)
	}
	return revs, nil
}

// GetRevision will return the content of any revision of the named entity
// see https://git-scm.com/docs/git-log#_pretty_formats
func (g *Git) GetRevision(ctx context.Context, name, revision string) ([]byte, error) {
	name = strings.TrimSpace(name)
	revision = strings.TrimSpace(revision)
	args := []string{
		"show",
		revision + ":" + name,
	}
	stdout, stderr, err := g.captureCmd(ctx, "GetRevision", args...)
	if err != nil {
		debug.Log("Command failed: %s", string(stderr))
		return nil, err
	}
	return stdout, nil
}

// Status return the git status output
func (g *Git) Status(ctx context.Context) ([]byte, error) {
	stdout, stderr, err := g.captureCmd(ctx, "GitStatus", "status")
	if err != nil {
		debug.Log("Command failed: %s\n%s", string(stdout), string(stderr))
		return nil, err
	}
	return stdout, nil
}

// Compact will run git gc
func (g *Git) Compact(ctx context.Context) error {
	return g.Cmd(ctx, "gitGC", "gc", "--aggressive")
}
<commit_msg>Emit a warning when untracked files are present in a git store (#1972)<commit_after>// Package gitfs implements a git cli based RCS backend.
package gitfs

import (
	"bytes"
	"context"
	"fmt"
	"io"
	"os"
	"os/exec"
	"path/filepath"
	"strconv"
	"strings"
	"time"

	"github.com/gopasspw/gopass/internal/backend"
	"github.com/gopasspw/gopass/internal/backend/storage/fs"
	"github.com/gopasspw/gopass/internal/out"
	"github.com/gopasspw/gopass/internal/store"
	"github.com/gopasspw/gopass/pkg/ctxutil"
	"github.com/gopasspw/gopass/pkg/debug"
	"github.com/gopasspw/gopass/pkg/fsutil"

	"github.com/blang/semver/v4"
)

type contextKey int

const (
	ctxKeyPathOverride contextKey = iota
)

func withPathOverride(ctx context.Context, path string) context.Context {
	return context.WithValue(ctx, ctxKeyPathOverride, path)
}

func getPathOverride(ctx context.Context, def string) string {
	if sv, ok := ctx.Value(ctxKeyPathOverride).(string); ok && sv != "" {
		return sv
	}
	return def
}

// Git is a cli based git backend
type Git struct {
	fs *fs.Store
}

// New creates a new git cli based git backend
func New(path string) (*Git, error) {
	if !fsutil.IsDir(filepath.Join(path, ".git")) {
		return nil, fmt.Errorf("git repo does not exist")
	}
	return &Git{
		fs: fs.New(path),
	}, nil
}

// Clone clones an existing git repo and returns a new cli based git backend
// configured for this clone repo
func Clone(ctx context.Context, repo, path string) (*Git, error) {
	g := &Git{
		fs: fs.New(path),
	}
	if err := g.Cmd(withPathOverride(ctx, filepath.Dir(path)), "Clone", "clone", repo, path); err != nil {
		return nil, err
	}
	return g, nil
}

// Init initializes this store's git repo
func Init(ctx context.Context, path, userName, userEmail string) (*Git, error) {
	g := &Git{
		fs: fs.New(path),
	}
	// the git repo may be empty (i.e. no branches, cloned from a fresh remote)
	// or already initialized. Only run git init if the folder is completely empty
	if !g.IsInitialized() {
		if err := g.Cmd(ctx, "Init", "init"); err != nil {
			return nil, fmt.Errorf("failed to initialize git: %s", err)
		}
		out.Printf(ctx, "git initialized at %s", g.fs.Path())
	}

	if !ctxutil.IsGitInit(ctx) {
		return g, nil
	}

	// initialize the local git config
	if err := g.InitConfig(ctx, userName, userEmail); err != nil {
		return g, fmt.Errorf("failed to configure git: %s", err)
	}
	out.Printf(ctx, "git configured at %s", g.fs.Path())

	// add current content of the store
	if err := g.Add(ctx, g.fs.Path()); err != nil {
		return g, fmt.Errorf("failed to add %q to git: %w", g.fs.Path(), err)
	}

	// commit if there is something to commit
	if !g.HasStagedChanges(ctx) {
		debug.Log("No staged changes")
		return g, nil
	}

	if err := g.Commit(ctx, "Add current content of password store"); err != nil {
		return g, fmt.Errorf("failed to commit changes to git: %w", err)
	}

	return g, nil
}

func (g *Git) captureCmd(ctx context.Context, name string, args ...string) ([]byte, []byte, error) {
	bufOut := &bytes.Buffer{}
	bufErr := &bytes.Buffer{}

	cmd := exec.CommandContext(ctx, "git", args[0:]...)
	cmd.Dir = getPathOverride(ctx, g.fs.Path())
	cmd.Stdout = bufOut
	cmd.Stderr = bufErr

	if debug.IsEnabled() && ctxutil.IsVerbose(ctx) {
		cmd.Stdout = io.MultiWriter(bufOut, os.Stdout)
		cmd.Stderr = io.MultiWriter(bufErr, os.Stderr)
	}

	debug.Log("store.%s: %s %+v (%s)", name, cmd.Path, cmd.Args, g.fs.Path())
	err := cmd.Run()
	return bufOut.Bytes(), bufErr.Bytes(), err
}

// Cmd runs an git command
func (g *Git) Cmd(ctx context.Context, name string, args ...string) error {
	stdout, stderr, err := g.captureCmd(ctx, name, args...)
	if err != nil {
		debug.Log("CMD: %s %+v\nError: %s\nOutput:\n  Stdout: %q\n  Stderr: %q", name, args, err, string(stdout), string(stderr))
		return fmt.Errorf("%s: %s", err, strings.TrimSpace(string(stderr)))
	}

	return nil
}

// Name returns git
func (g *Git) Name() string {
	return "git"
}

// Version returns the git version as major, minor and patch level
func (g *Git) Version(ctx context.Context) semver.Version {
	v := semver.Version{}

	cmd := exec.CommandContext(ctx, "git", "version")
	cmdout, err := cmd.Output()
	if err != nil {
		debug.Log("Failed to run 'git version': %s", err)
		return v
	}

	svStr := strings.TrimPrefix(string(cmdout), "git version ")
	if p := strings.Fields(svStr); len(p) > 0 {
		svStr = p[0]
	}

	sv, err := semver.ParseTolerant(svStr)
	if err != nil {
		debug.Log("Failed to parse %q as semver: %s", svStr, err)
		return v
	}
	return sv
}

// IsInitialized returns true if this stores has an (probably) initialized .git folder
func (g *Git) IsInitialized() bool {
	return fsutil.IsFile(filepath.Join(g.fs.Path(), ".git", "config"))
}

// Add adds the listed files to the git index
func (g *Git) Add(ctx context.Context, files ...string) error {
	if !g.IsInitialized() {
		return store.ErrGitNotInit
	}

	for i := range files {
		files[i] = strings.TrimPrefix(files[i], g.fs.Path()+"/")
	}

	args := []string{"add", "--all", "--force"}
	args = append(args, files...)

	return g.Cmd(ctx, "gitAdd", args...)
}

// HasStagedChanges returns true if there are any staged changes which can be committed
func (g *Git) HasStagedChanges(ctx context.Context) bool {
	if err := g.Cmd(ctx, "gitDiffIndex", "diff-index", "--quiet", "HEAD"); err != nil {
		return true
	}
	return false
}

// ListUntrackedFiles lists untracked files
func (g *Git) ListUntrackedFiles(ctx context.Context) []string {
	stdout, _, err := g.captureCmd(ctx, "gitLsFiles", "ls-files", ".", "--exclude-standard", "--others")
	if err != nil {
		return []string{fmt.Sprintf("ERROR: %s", err)}
	}
	uf := []string{}
	for _, f := range strings.Split(string(stdout), "\n") {
		if f == "" {
			continue
		}
		uf = append(uf, f)
	}
	return uf
}

// Commit creates a new git commit with the given commit message
func (g *Git) Commit(ctx context.Context, msg string) error {
	if !g.IsInitialized() {
		return store.ErrGitNotInit
	}

	if !g.HasStagedChanges(ctx) {
		return store.ErrGitNothingToCommit
	}

	return g.Cmd(ctx, "gitCommit", "commit", fmt.Sprintf("--date=%d +00:00", ctxutil.GetCommitTimestamp(ctx).UTC().Unix()), "-m", msg)
}

func (g *Git) defaultRemote(ctx context.Context, branch string) string {
	opts, err := g.ConfigList(ctx)
	if err != nil {
		return "origin"
	}

	remote := opts["branch."+branch+".remote"]
	if remote == "" {
		return "origin"
	}

	needle := "remote." + remote + ".url"
	for k := range opts {
		if k == needle {
			return remote
		}
	}
	return "origin"
}

func (g *Git) defaultBranch(ctx context.Context) string {
	out, _, err := g.captureCmd(ctx, "defaultBranch", "rev-parse", "--abbrev-ref", "HEAD")
	if err != nil || string(out) == "" {
		// see https://github.com/github/renaming
		return "main"
	}
	return strings.TrimSpace(string(out))
}

// PushPull pushes the repo to it's origin.
// optional arguments: remote and branch
func (g *Git) PushPull(ctx context.Context, op, remote, branch string) error {
	if ctxutil.IsNoNetwork(ctx) {
		debug.Log("Skipping network ops. NoNetwork=true")
		return nil
	}
	if !g.IsInitialized() {
		return store.ErrGitNotInit
	}

	if branch == "" {
		branch = g.defaultBranch(ctx)
	}
	if remote == "" {
		remote = g.defaultRemote(ctx, branch)
	}

	if v, err := g.ConfigGet(ctx, "remote."+remote+".url"); err != nil || v == "" {
		return store.ErrGitNoRemote
	}

	if err := g.Cmd(ctx, "gitPush", "pull", remote, branch); err != nil {
		if op == "pull" {
			return err
		}
		out.Warningf(ctx, "Failed to pull before git push: %s", err)
	}
	if op == "pull" {
		return nil
	}

	if uf := g.ListUntrackedFiles(ctx); len(uf) > 0 {
		out.Warningf(ctx, "Found untracked files: %+v", uf)
	}
	return g.Cmd(ctx, "gitPush", "push", remote, branch)
}

// Push pushes to the git remote
func (g *Git) Push(ctx context.Context, remote, branch string) error {
	if ctxutil.IsNoNetwork(ctx) {
		debug.Log("Skipping network ops. NoNetwork=true")
		return nil
	}
	return g.PushPull(ctx, "push", remote, branch)
}

// Pull pulls from the git remote
func (g *Git) Pull(ctx context.Context, remote, branch string) error {
	if ctxutil.IsNoNetwork(ctx) {
		debug.Log("Skipping network ops. NoNetwork=true")
		return nil
	}
	return g.PushPull(ctx, "pull", remote, branch)
}

// AddRemote adds a new remote
func (g *Git) AddRemote(ctx context.Context, remote, url string) error {
	return g.Cmd(ctx, "gitAddRemote", "remote", "add", remote, url)
}

// RemoveRemote removes a remote
func (g *Git) RemoveRemote(ctx context.Context, remote string) error {
	return g.Cmd(ctx, "gitRemoveRemote", "remote", "remove", remote)
}

// Revisions will list all available revisions of the named entity
// see http://blog.lost-theory.org/post/how-to-parse-git-log-output/
// and https://git-scm.com/docs/git-log#_pretty_formats
func (g *Git) Revisions(ctx context.Context, name string) ([]backend.Revision, error) {
	args := []string{
		"log",
		`--format=%H%x1f%an%x1f%ae%x1f%at%x1f%s%x1f%b%x1e`,
		"--",
		name,
	}
	stdout, stderr, err := g.captureCmd(ctx, "Revisions", args...)
	if err != nil {
		debug.Log("Command failed: %s", string(stderr))
		return nil, err
	}
	so := string(stdout)
	revs := make([]backend.Revision, 0, strings.Count(so, "\x1e"))
	for _, rev := range strings.Split(so, "\x1e") {
		rev = strings.TrimSpace(rev)
		if rev == "" {
			continue
		}

		p := strings.Split(rev, "\x1f")
		if len(p) < 1 {
			continue
		}

		r := backend.Revision{}
		r.Hash = p[0]
		if len(p) > 1 {
			r.AuthorName = p[1]
		}
		if len(p) > 2 {
			r.AuthorEmail = p[2]
		}
		if len(p) > 3 {
			if iv, err := strconv.ParseInt(p[3], 10, 64); err == nil {
				r.Date = time.Unix(iv, 0)
			}
		}
		if len(p) > 4 {
			r.Subject = p[4]
		}
		if len(p) > 5 {
			r.Body = p[5]
		}
		revs = append(revs, r)
	}
	return revs, nil
}

// GetRevision will return the content of any revision of the named entity
// see https://git-scm.com/docs/git-log#_pretty_formats
func (g *Git) GetRevision(ctx context.Context, name, revision string) ([]byte, error) {
	name = strings.TrimSpace(name)
	revision = strings.TrimSpace(revision)
	args := []string{
		"show",
		revision + ":" + name,
	}
	stdout, stderr, err := g.captureCmd(ctx, "GetRevision", args...)
	if err != nil {
		debug.Log("Command failed: %s", string(stderr))
		return nil, err
	}
	return stdout, nil
}

// Status return the git status output
func (g *Git) Status(ctx context.Context) ([]byte, error) {
	stdout, stderr, err := g.captureCmd(ctx, "GitStatus", "status")
	if err != nil {
		debug.Log("Command failed: %s\n%s", string(stdout), string(stderr))
		return nil, err
	}
	return stdout, nil
}

// Compact will run git gc
func (g *Git) Compact(ctx context.Context) error {
	return g.Cmd(ctx, "gitGC", "gc", "--aggressive")
}
<|endoftext|>


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

// Package contentcache stores GCS object contents locally.
// Note: The content cache is not concurrent safe and callers should ensure thread safety
package contentcache

import (
	"fmt"
	"io"
	"io/ioutil"
	"regexp"

	"github.com/googlecloudplatform/gcsfuse/internal/gcsx"
	"github.com/jacobsa/timeutil"
)

// CacheObjectKey uniquely identifies GCS objects by bucket name and object name
type CacheObjectKey struct {
	BucketName string
	ObjectName string
}

// ContentCache is a directory on local disk to store the object content.
type ContentCache struct {
	tempDir        string
	localFileCache bool
	fileMap        map[CacheObjectKey]gcsx.TempFile
	mtimeClock     timeutil.Clock
}

// RecoverCache recovers the cache with existing persisted files when gcsfuse starts
func (c *ContentCache) RecoverCache() error {
	if c.tempDir == "" {
		c.tempDir = "/tmp"
	}
	files, err := ioutil.ReadDir(c.tempDir)
	if err != nil {
		// if we fail to read the specified directory, log and return error
		return fmt.Errorf("recover cache: %w", err)
	}
	for _, file := range files {
		// validate not a directory and matches gcsfuse pattern
		if !file.IsDir() && matchPattern(file.Name()) {
			// TODO ezl: load the files from disk to the in memory map
		}
	}
	return nil
}

// Helper function that matches the format of a gcsfuse file
func matchPattern(fileName string) bool {
	// TODO ezl: replace with constant defined in gcsx.TempFile
	match, err := regexp.MatchString(fmt.Sprintf("gcsfuse[0-9]+[.]json"), fileName)
	if err != nil {
		return false
	}
	return match
}

// New creates a ContentCache.
func New(tempDir string, mtimeClock timeutil.Clock) *ContentCache {
	return &ContentCache{
		tempDir:    tempDir,
		fileMap:    make(map[CacheObjectKey]gcsx.TempFile),
		mtimeClock: mtimeClock,
	}
}

// NewTempFile returns a handle for a temporary file on the disk. The caller
// must call Destroy on the TempFile before releasing it.
func (c *ContentCache) NewTempFile(rc io.ReadCloser) (gcsx.TempFile, error) {
	return gcsx.NewTempFile(rc, c.tempDir, c.mtimeClock)
}

// Add or replace creates a new cache file or updates an existing cache file
func (c *ContentCache) AddOrReplace(cacheObjectKey *CacheObjectKey, generation int64, rc io.ReadCloser) (gcsx.TempFile, error) {
	if cacheObject, exists := c.fileMap[*cacheObjectKey]; exists {
		cacheObject.Destroy()
	}
	metadata := &gcsx.TempFileObjectMetadata{
		BucketName: cacheObjectKey.BucketName,
		ObjectName: cacheObjectKey.ObjectName,
		Generation: generation,
	}
	file, err := c.NewCacheFile(rc, metadata)
	if err != nil {
		return nil, err
	}
	c.fileMap[*cacheObjectKey] = file
	return file, err
}

// Get retrieves a file from the cache given the GCS object name and bucket name
func (c *ContentCache) Get(cacheObjectKey *CacheObjectKey) (gcsx.TempFile, bool) {
	file, exists := c.fileMap[*cacheObjectKey]
	return file, exists
}

// Remove removes and destroys the specfied cache file and metadata on disk
func (c *ContentCache) Remove(cacheObjectKey *CacheObjectKey) {
	if _, exists := c.fileMap[*cacheObjectKey]; exists {
		c.fileMap[*cacheObjectKey].Destroy()
	}
	delete(c.fileMap, *cacheObjectKey)
}

// NewCacheFile creates a cache file on the disk storing the object content
// TODO ezl we should refactor reading/writing cache files and metadata to a different package
func (c *ContentCache) NewCacheFile(rc io.ReadCloser, metadata *gcsx.TempFileObjectMetadata) (gcsx.TempFile, error) {
	return gcsx.NewCacheFile(rc, metadata, c.tempDir, c.mtimeClock)
}
<commit_msg>Fix cache methods after rebase<commit_after>// Copyright 2021 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// Package contentcache stores GCS object contents locally.
// Note: The content cache is not concurrent safe and callers should ensure thread safety
package contentcache

import (
	"fmt"
	"io"
	"io/ioutil"
	"regexp"

	"github.com/googlecloudplatform/gcsfuse/internal/gcsx"
	"github.com/jacobsa/timeutil"
)

// CacheObjectKey uniquely identifies GCS objects by bucket name and object name
type CacheObjectKey struct {
	BucketName string
	ObjectName string
}

// ContentCache is a directory on local disk to store the object content.
type ContentCache struct {
	tempDir        string
	localFileCache bool
	fileMap        map[CacheObjectKey]gcsx.TempFile
	mtimeClock     timeutil.Clock
}

// RecoverCache recovers the cache with existing persisted files when gcsfuse starts
func (c *ContentCache) RecoverCache() error {
	if c.tempDir == "" {
		c.tempDir = "/tmp"
	}
	files, err := ioutil.ReadDir(c.tempDir)
	if err != nil {
		// if we fail to read the specified directory, log and return error
		return fmt.Errorf("recover cache: %w", err)
	}
	for _, file := range files {
		// validate not a directory and matches gcsfuse pattern
		if !file.IsDir() && matchPattern(file.Name()) {
			// TODO ezl: load the files from disk to the in memory map
		}
	}
	return nil
}

// Helper function that matches the format of a gcsfuse file
func matchPattern(fileName string) bool {
	// TODO ezl: replace with constant defined in gcsx.TempFile
	match, err := regexp.MatchString(fmt.Sprintf("gcsfuse[0-9]+[.]json"), fileName)
	if err != nil {
		return false
	}
	return match
}

// New creates a ContentCache.
func New(tempDir string, mtimeClock timeutil.Clock) *ContentCache {
	return &ContentCache{
		tempDir:    tempDir,
		fileMap:    make(map[CacheObjectKey]gcsx.TempFile),
		mtimeClock: mtimeClock,
	}
}

// NewTempFile returns a handle for a temporary file on the disk. The caller
// must call Destroy on the TempFile before releasing it.
func (c *ContentCache) NewTempFile(rc io.ReadCloser) (gcsx.TempFile, error) {
	return gcsx.NewTempFile(rc, c.tempDir, c.mtimeClock)
}

// AddOrReplace creates a new cache file or updates an existing cache file
func (c *ContentCache) AddOrReplace(cacheObjectKey *CacheObjectKey, generation int64, rc io.ReadCloser) (gcsx.TempFile, error) {
	if cacheObject, exists := c.fileMap[*cacheObjectKey]; exists {
		cacheObject.Destroy()
	}
	metadata := &gcsx.TempFileObjectMetadata{
		BucketName: cacheObjectKey.BucketName,
		ObjectName: cacheObjectKey.ObjectName,
		Generation: generation,
	}
	file, err := c.NewCacheFile(rc, metadata)
	if err != nil {
		return nil, fmt.Errorf("Could not AddOrReplace cache file: %w", err)
	}
	c.fileMap[*cacheObjectKey] = file
	return file, err
}

// Get retrieves a file from the cache given the GCS object name and bucket name
func (c *ContentCache) Get(cacheObjectKey *CacheObjectKey) (gcsx.TempFile, bool) {
	file, exists := c.fileMap[*cacheObjectKey]
	return file, exists
}

// Remove removes and destroys the specfied cache file and metadata on disk
func (c *ContentCache) Remove(cacheObjectKey *CacheObjectKey) {
	if cacheObject, exists := c.fileMap[*cacheObjectKey]; exists {
		cacheObject.Destroy()
	}
	delete(c.fileMap, *cacheObjectKey)
}

// NewCacheFile creates a cache file on the disk storing the object content
// TODO ezl we should refactor reading/writing cache files and metadata to a different package
func (c *ContentCache) NewCacheFile(rc io.ReadCloser, metadata *gcsx.TempFileObjectMetadata) (gcsx.TempFile, error) {
	return gcsx.NewCacheFile(rc, metadata, c.tempDir, c.mtimeClock)
}
<|endoftext|>


<commit_before>package log

import (
	"fmt"
	"log"
	"os"
)

const (
	INFO_PREFIX  = "[INFO]"
	WARN_PREFIX  = "[WARN]"
	ERROR_PREFIX = "[ERROR]"
	FATAL_PREFIX = "[FATAL]"
	PANIC_PREFIX = "[PANIC]"
)

func Print(v ...interface{}) {
	log.Print(v)
}

func Printf(format string, v ...interface{}) {
	log.Printf(format, v)
}

func Info(v ...interface{}) {
	log.Printf("%v %v\n", INFO_PREFIX, fmt.Sprint(v...))
}

func Infof(format string, v ...interface{}) {
	log.Printf("%v %v\n", INFO_PREFIX, fmt.Sprintf(format, v...))
}

func Warn(v ...interface{}) {
	log.Printf("%v %v\n", WARN_PREFIX, fmt.Sprint(v...))
}

func Warnf(format string, v ...interface{}) {
	log.Printf("%v %v\n", WARN_PREFIX, fmt.Sprintf(format, v...))
}

func Error(v ...interface{}) {
	log.Printf("%v %v\n", ERROR_PREFIX, fmt.Sprint(v...))
}

func Errorf(format string, v ...interface{}) {
	log.Printf("%v %v\n", ERROR_PREFIX, fmt.Sprintf(format, v...))
}

func Fatal(v ...interface{}) {
	log.Printf("%v %v\n", FATAL_PREFIX, fmt.Sprint(v...))
	os.Exit(1)
}

func Fatalf(format string, v ...interface{}) {
	log.Printf("%v %v\n", FATAL_PREFIX, fmt.Sprintf(format, v...))
	os.Exit(1)
}

func Panic(v ...interface{}) {
	message := fmt.Sprint(v...)
	log.Printf("%v %v\n", PANIC_PREFIX, message)
	panic(message)
}

func Panicf(format string, v ...interface{}) {
	message := fmt.Sprintf(format, v...)
	log.Printf("%v %v\n", PANIC_PREFIX, message)
	panic(message)
}
<commit_msg>Fix log.Print and log.Printf<commit_after>package log

import (
	"fmt"
	"log"
	"os"
)

const (
	INFO_PREFIX  = "[INFO]"
	WARN_PREFIX  = "[WARN]"
	ERROR_PREFIX = "[ERROR]"
	FATAL_PREFIX = "[FATAL]"
	PANIC_PREFIX = "[PANIC]"
)

func Print(v ...interface{}) {
	log.Print(v...)
}

func Printf(format string, v ...interface{}) {
	log.Printf(format, v...)
}

func Info(v ...interface{}) {
	log.Printf("%v %v\n", INFO_PREFIX, fmt.Sprint(v...))
}

func Infof(format string, v ...interface{}) {
	log.Printf("%v %v\n", INFO_PREFIX, fmt.Sprintf(format, v...))
}

func Warn(v ...interface{}) {
	log.Printf("%v %v\n", WARN_PREFIX, fmt.Sprint(v...))
}

func Warnf(format string, v ...interface{}) {
	log.Printf("%v %v\n", WARN_PREFIX, fmt.Sprintf(format, v...))
}

func Error(v ...interface{}) {
	log.Printf("%v %v\n", ERROR_PREFIX, fmt.Sprint(v...))
}

func Errorf(format string, v ...interface{}) {
	log.Printf("%v %v\n", ERROR_PREFIX, fmt.Sprintf(format, v...))
}

func Fatal(v ...interface{}) {
	log.Printf("%v %v\n", FATAL_PREFIX, fmt.Sprint(v...))
	os.Exit(1)
}

func Fatalf(format string, v ...interface{}) {
	log.Printf("%v %v\n", FATAL_PREFIX, fmt.Sprintf(format, v...))
	os.Exit(1)
}

func Panic(v ...interface{}) {
	message := fmt.Sprint(v...)
	log.Printf("%v %v\n", PANIC_PREFIX, message)
	panic(message)
}

func Panicf(format string, v ...interface{}) {
	message := fmt.Sprintf(format, v...)
	log.Printf("%v %v\n", PANIC_PREFIX, message)
	panic(message)
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	logrus "github.com/sirupsen/logrus"
)

type LogConfig struct {
	Level       string         `json:"level,omitempty"`
	Stackdriver *LoggingConfig `json:"stackdriver,omitempty"`
}

var logger = logrus.New()
var log = logger.WithFields(logrus.Fields{})

func (c *LogConfig) setup() *ConfigError {
	setups := []ConfigSetup{
		c.setupLevel,
		c.setupStackdriver,
	}
	for _, setup := range setups {
		err := setup()
		if err != nil {
			return err
		}
	}
	return nil
}

func (c *LogConfig) setupLevel() *ConfigError {
	if c.Level == "" {
		c.Level = "info"
	}
	level, err := logrus.ParseLevel(c.Level)
	if err != nil {
		log.Warnf("Error on log.ParseLevel level: %q because of %v\n", c.Level, err)
		return &ConfigError{Name: "level", Message: fmt.Sprintf("is invalid because of %v", err)}
	}
	logger.SetLevel(level)
	return nil
}

func (c *LogConfig) setupStackdriver() *ConfigError {
	if c.Stackdriver != nil {
		err := c.Stackdriver.setup()
		if err != nil {
			err.Add("stackdriver")
			return err
		}
	}
	return nil
}
<commit_msg>:+1: Add CommandSeverity to LogConfig<commit_after>package main

import (
	"fmt"
	logrus "github.com/sirupsen/logrus"
)

type LogConfig struct {
	Level       string         `json:"level,omitempty"`
	CommandSeverity string     `json:"command_severity"`
	commandSeverityLevel logrus.Level
	Stackdriver *LoggingConfig `json:"stackdriver,omitempty"`
}

var logger = logrus.New()
var log = logger.WithFields(logrus.Fields{})

func (c *LogConfig) setup() *ConfigError {
	setups := []ConfigSetup{
		c.setupLevel,
		c.setupCommandSeverity,
		c.setupStackdriver,
	}
	for _, setup := range setups {
		err := setup()
		if err != nil {
			return err
		}
	}
	return nil
}

func (c *LogConfig) setupLevel() *ConfigError {
	if c.Level == "" {
		c.Level = "info"
	}
	level, err := logrus.ParseLevel(c.Level)
	if err != nil {
		log.Warnf("Error on log.ParseLevel level: %q because of %v\n", c.Level, err)
		return &ConfigError{Name: "level", Message: fmt.Sprintf("is invalid because of %v", err)}
	}
	logger.SetLevel(level)
	return nil
}

func (c *LogConfig) setupCommandSeverity() *ConfigError {
	if c.CommandSeverity == "" {
		c.CommandSeverity = "info"
	}
	level, err := logrus.ParseLevel(c.CommandSeverity)
	if err != nil {
		log.Warnf("Error on log.ParseLevel command_severity: %q because of %v\n", c.CommandSeverity, err)
		return &ConfigError{Name: "command_severity", Message: fmt.Sprintf("is invalid because of %v", err)}
	}
	c.commandSeverityLevel = level
	return nil
}

func (c *LogConfig) setupStackdriver() *ConfigError {
	if c.Stackdriver != nil {
		err := c.Stackdriver.setup()
		if err != nil {
			err.Add("stackdriver")
			return err
		}
	}
	return nil
}
<|endoftext|>


<commit_before>package raft

import (
	"fmt"
	"net"
)

type LogType uint8

const (
	// Commands are applied to a user FSM
	LogCommand LogType = iota

	// Noop is used to assert leadership
	LogNoop

	// Used to add a new peer
	LogAddPeer

	// Used to remove an existing peer
	LogRemovePeer

	// Barrier is used to ensure all preceeding
	// operations have been applied to the FSM. It is
	// similar to LogNoop, but instead of returning once committed,
	// it only returns once the FSM manager acks it. Otherwise it is
	// possible there are operations committed but not yet applied to
	// the FSM.
	LogBarrier
)

var (
	LogNotFound = fmt.Errorf("log not found")
)

// Log entries are replicated to all members of the Raft cluster
// and form the heart of the replicated state machine.
type Log struct {
	Index uint64
	Term  uint64
	Type  LogType
	Data  []byte

	// Peer is not exported since it is not transmitted, only used
	// internally to construct the Data field.
	peer net.Addr
}

// LogStore is used to provide an interface for storing
// and retrieving logs in a durable fashion
type LogStore interface {
	// Returns the first index written. 0 for no entries.
	FirstIndex() (uint64, error)

	// Returns the last index written. 0 for no entries.
	LastIndex() (uint64, error)

	// Gets a log entry at a given index
	GetLog(index uint64, log *Log) error

	// Stores a log entry
	StoreLog(log *Log) error

	// Stores multiple log entries
	StoreLogs(logs []*Log) error

	// Deletes a range of log entries. The range is inclusive.
	DeleteRange(min, max uint64) error
}
<commit_msg>Adding new VerifyLeader internal log type<commit_after>package raft

import (
	"fmt"
	"net"
)

type LogType uint8

const (
	// Commands are applied to a user FSM
	LogCommand LogType = iota

	// Noop is used to assert leadership
	LogNoop

	// Used to add a new peer
	LogAddPeer

	// Used to remove an existing peer
	LogRemovePeer

	// Barrier is used to ensure all preceeding
	// operations have been applied to the FSM. It is
	// similar to LogNoop, but instead of returning once committed,
	// it only returns once the FSM manager acks it. Otherwise it is
	// possible there are operations committed but not yet applied to
	// the FSM.
	LogBarrier

	// VerifyLeader is used to check that the current ndoe is still
	// the leader. This is never committed to the raft log, but is
	// used internally.
	LogVerifyLeader
)

var (
	LogNotFound = fmt.Errorf("log not found")
)

// Log entries are replicated to all members of the Raft cluster
// and form the heart of the replicated state machine.
type Log struct {
	Index uint64
	Term  uint64
	Type  LogType
	Data  []byte

	// Peer is not exported since it is not transmitted, only used
	// internally to construct the Data field.
	peer net.Addr
}

// LogStore is used to provide an interface for storing
// and retrieving logs in a durable fashion
type LogStore interface {
	// Returns the first index written. 0 for no entries.
	FirstIndex() (uint64, error)

	// Returns the last index written. 0 for no entries.
	LastIndex() (uint64, error)

	// Gets a log entry at a given index
	GetLog(index uint64, log *Log) error

	// Stores a log entry
	StoreLog(log *Log) error

	// Stores multiple log entries
	StoreLogs(logs []*Log) error

	// Deletes a range of log entries. The range is inclusive.
	DeleteRange(min, max uint64) error
}
<|endoftext|>