<commit_before>package main

import (
	"flag"
	"fmt"
	"io/ioutil"
	"os"
	"runtime"

	"github.com/atotto/clipboard"
	"github.com/go-errors/errors"
	"github.com/layeh/gopher-luar"
	"github.com/mattn/go-isatty"
	"github.com/mitchellh/go-homedir"
	"github.com/yuin/gopher-lua"
	"github.com/zyedidia/tcell"
	"github.com/zyedidia/tcell/encoding"
)

var (
	// The main screen
	screen tcell.Screen

	// Object to send messages and prompts to the user
	messenger *Messenger

	// The default highlighting style
	// This simply defines the default foreground and background colors
	defStyle tcell.Style

	// Where the user's configuration is
	// This should be $XDG_CONFIG_HOME/micro
	// If $XDG_CONFIG_HOME is not set, it is ~/.config/micro
	configDir string

	// Version is the version number or commit hash
	// This should be set by the linker when compiling
	Version = "Unknown"

	// L is the lua state
	// This is the VM that runs the plugins
	L *lua.LState

	// The list of views
	tabs []*Tab
	// This is the currently open tab
	// It's just an index to the tab in the tabs array
	curTab int

	// Channel of jobs running in the background
	jobs chan JobFunction
	// Event channel
	events chan tcell.Event
)

// LoadInput determines which files should be loaded into buffers
// based on the input stored in os.Args
func LoadInput() []*Buffer {
	// There are a number of ways micro should start given its input

	// 1. If it is given a files in os.Args, it should open those

	// 2. If there is no input file and the input is not a terminal, that means
	// something is being piped in and the stdin should be opened in an
	// empty buffer

	// 3. If there is no input file and the input is a terminal, an empty buffer
	// should be opened

	var filename string
	var input []byte
	var err error
	var buffers []*Buffer

	if len(os.Args) > 1 {
		// Option 1
		// We go through each file and load it
		for i := 1; i < len(os.Args); i++ {
			filename = os.Args[i]
			// Check that the file exists
			if _, e := os.Stat(filename); e == nil {
				// If it exists we load it into a buffer
				input, err = ioutil.ReadFile(filename)
				if err != nil {
					TermMessage(err)
					continue
				}
			}
			// If the file didn't exist, input will be empty, and we'll open an empty buffer
			buffers = append(buffers, NewBuffer(input, filename))
		}
	} else if !isatty.IsTerminal(os.Stdin.Fd()) {
		// Option 2
		// The input is not a terminal, so something is being piped in
		// and we should read from stdin
		input, err = ioutil.ReadAll(os.Stdin)
		buffers = append(buffers, NewBuffer(input, filename))
	} else {
		// Option 3, just open an empty buffer
		buffers = append(buffers, NewBuffer(input, filename))
	}

	return buffers
}

// InitConfigDir finds the configuration directory for micro according to the XDG spec.
// If no directory is found, it creates one.
func InitConfigDir() {
	xdgHome := os.Getenv("XDG_CONFIG_HOME")
	if xdgHome == "" {
		// The user has not set $XDG_CONFIG_HOME so we should act like it was set to ~/.config
		home, err := homedir.Dir()
		if err != nil {
			TermMessage("Error finding your home directory\nCan't load config files")
			return
		}
		xdgHome = home + "/.config"
	}
	configDir = xdgHome + "/micro"

	if _, err := os.Stat(xdgHome); os.IsNotExist(err) {
		// If the xdgHome doesn't exist we should create it
		err = os.Mkdir(xdgHome, os.ModePerm)
		if err != nil {
			TermMessage("Error creating XDG_CONFIG_HOME directory: " + err.Error())
		}
	}

	if _, err := os.Stat(configDir); os.IsNotExist(err) {
		// If the micro specific config directory doesn't exist we should create that too
		err = os.Mkdir(configDir, os.ModePerm)
		if err != nil {
			TermMessage("Error creating configuration directory: " + err.Error())
		}
	}
}

// InitScreen creates and initializes the tcell screen
func InitScreen() {
	// Should we enable true color?
	truecolor := os.Getenv("MICRO_TRUECOLOR") == "1"

	// In order to enable true color, we have to set the TERM to `xterm-truecolor` when
	// initializing tcell, but after that, we can set the TERM back to whatever it was
	oldTerm := os.Getenv("TERM")
	if truecolor {
		os.Setenv("TERM", "xterm-truecolor")
	}

	// Initilize tcell
	var err error
	screen, err = tcell.NewScreen()
	if err != nil {
		fmt.Println(err)
		os.Exit(1)
	}
	if err = screen.Init(); err != nil {
		fmt.Println(err)
		os.Exit(1)
	}

	// Now we can put the TERM back to what it was before
	if truecolor {
		os.Setenv("TERM", oldTerm)
	}

	screen.SetStyle(defStyle)
	screen.EnableMouse()
}

// RedrawAll redraws everything -- all the views and the messenger
func RedrawAll() {
	messenger.Clear()
	DisplayTabs()
	messenger.Display()
	for _, v := range tabs[curTab].views {
		v.Display()
	}
	screen.Show()
}

// Passing -version as a flag will have micro print out the version number
var flagVersion = flag.Bool("version", false, "Show the version number")

func main() {
	flag.Parse()
	if *flagVersion {
		// If -version was passed
		fmt.Println("Micro version:", Version)
		os.Exit(0)
	}

	// Start the Lua VM for running plugins
	L = lua.NewState()
	defer L.Close()

	// Some encoding stuff in case the user isn't using UTF-8
	encoding.Register()
	tcell.SetEncodingFallback(tcell.EncodingFallbackASCII)

	// Find the user's configuration directory (probably $XDG_CONFIG_HOME/micro)
	InitConfigDir()

	// Load the user's settings
	InitSettings()
	InitCommands()
	InitBindings()

	// Load the syntax files, including the colorscheme
	LoadSyntaxFiles()

	// Load the help files
	LoadHelp()

	// Start the screen
	InitScreen()

	// This is just so if we have an error, we can exit cleanly and not completely
	// mess up the terminal being worked in
	// In other words we need to shut down tcell before the program crashes
	defer func() {
		if err := recover(); err != nil {
			screen.Fini()
			fmt.Println("Micro encountered an error:", err)
			// Print the stack trace too
			fmt.Print(errors.Wrap(err, 2).ErrorStack())
			os.Exit(1)
		}
	}()

	// Create a new messenger
	// This is used for sending the user messages in the bottom of the editor
	messenger = new(Messenger)
	messenger.history = make(map[string][]string)

	// Now we load the input
	buffers := LoadInput()
	for _, buf := range buffers {
		// For each buffer we create a new tab and place the view in that tab
		tab := NewTabFromView(NewView(buf))
		tab.SetNum(len(tabs))
		tabs = append(tabs, tab)
		for _, t := range tabs {
			for _, v := range t.views {
				v.Resize(screen.Size())
			}
		}
	}

	// Load all the plugin stuff
	// We give plugins access to a bunch of variables here which could be useful to them
	L.SetGlobal("OS", luar.New(L, runtime.GOOS))
	L.SetGlobal("tabs", luar.New(L, tabs))
	L.SetGlobal("curTab", luar.New(L, curTab))
	L.SetGlobal("messenger", luar.New(L, messenger))
	L.SetGlobal("GetOption", luar.New(L, GetOption))
	L.SetGlobal("AddOption", luar.New(L, AddOption))
	L.SetGlobal("BindKey", luar.New(L, BindKey))
	L.SetGlobal("MakeCommand", luar.New(L, MakeCommand))
	L.SetGlobal("CurView", luar.New(L, CurView))
	L.SetGlobal("IsWordChar", luar.New(L, IsWordChar))

	// Used for asynchronous jobs
	L.SetGlobal("JobStart", luar.New(L, JobStart))
	L.SetGlobal("JobSend", luar.New(L, JobSend))
	L.SetGlobal("JobStop", luar.New(L, JobStop))

	LoadPlugins()

	jobs = make(chan JobFunction, 100)
	events = make(chan tcell.Event)

	// Here is the event loop which runs in a separate thread
	go func() {
		for {
			events <- screen.PollEvent()
		}
	}()

	for {
		// Display everything
		RedrawAll()

		var event tcell.Event

		// Check for new events
		select {
		case f := <-jobs:
			// If a new job has finished while running in the background we should execute the callback
			f.function(f.output, f.args...)
			continue
		case event = <-events:
		}

		switch e := event.(type) {
		case *tcell.EventMouse:
			if e.Buttons() == tcell.Button1 {
				// If the user left clicked we check a couple things
				_, h := screen.Size()
				x, y := e.Position()
				if y == h-1 && messenger.message != "" {
					// If the user clicked in the bottom bar, and there is a message down there
					// we copy it to the clipboard.
					// Often error messages are displayed down there so it can be useful to easily
					// copy the message
					clipboard.WriteAll(messenger.message)
					continue
				}

				// We loop through each view in the current tab and make sure the current view
				// it the one being clicked in
				for _, v := range tabs[curTab].views {
					if x >= v.x && x < v.x+v.width && y >= v.y && y < v.y+v.height {
						tabs[curTab].curView = v.Num
					}
				}
			}
		}

		// This function checks the mouse event for the possibility of changing the current tab
		// If the tab was changed it returns true
		if TabbarHandleMouseEvent(event) {
			continue
		}

		if searching {
			// Since searching is done in real time, we need to redraw every time
			// there is a new event in the search bar so we need a special function
			// to run instead of the standard HandleEvent.
			HandleSearchEvent(event, CurView())
		} else {
			// Send it to the view
			CurView().HandleEvent(event)
		}
	}
}
<commit_msg>Add some lines back<commit_after>package main

import (
	"flag"
	"fmt"
	"io/ioutil"
	"os"
	"runtime"

	"github.com/atotto/clipboard"
	"github.com/go-errors/errors"
	"github.com/layeh/gopher-luar"
	"github.com/mattn/go-isatty"
	"github.com/mitchellh/go-homedir"
	"github.com/yuin/gopher-lua"
	"github.com/zyedidia/tcell"
	"github.com/zyedidia/tcell/encoding"
)

const (
	synLinesUp           = 75  // How many lines up to look to do syntax highlighting
	synLinesDown         = 75  // How many lines down to look to do syntax highlighting
	doubleClickThreshold = 400 // How many milliseconds to wait before a second click is not a double click
	undoThreshold        = 500 // If two events are less than n milliseconds apart, undo both of them
)

var (
	// The main screen
	screen tcell.Screen

	// Object to send messages and prompts to the user
	messenger *Messenger

	// The default highlighting style
	// This simply defines the default foreground and background colors
	defStyle tcell.Style

	// Where the user's configuration is
	// This should be $XDG_CONFIG_HOME/micro
	// If $XDG_CONFIG_HOME is not set, it is ~/.config/micro
	configDir string

	// Version is the version number or commit hash
	// This should be set by the linker when compiling
	Version = "Unknown"

	// L is the lua state
	// This is the VM that runs the plugins
	L *lua.LState

	// The list of views
	tabs []*Tab
	// This is the currently open tab
	// It's just an index to the tab in the tabs array
	curTab int

	// Channel of jobs running in the background
	jobs chan JobFunction
	// Event channel
	events chan tcell.Event
)

// LoadInput determines which files should be loaded into buffers
// based on the input stored in os.Args
func LoadInput() []*Buffer {
	// There are a number of ways micro should start given its input

	// 1. If it is given a files in os.Args, it should open those

	// 2. If there is no input file and the input is not a terminal, that means
	// something is being piped in and the stdin should be opened in an
	// empty buffer

	// 3. If there is no input file and the input is a terminal, an empty buffer
	// should be opened

	var filename string
	var input []byte
	var err error
	var buffers []*Buffer

	if len(os.Args) > 1 {
		// Option 1
		// We go through each file and load it
		for i := 1; i < len(os.Args); i++ {
			filename = os.Args[i]
			// Check that the file exists
			if _, e := os.Stat(filename); e == nil {
				// If it exists we load it into a buffer
				input, err = ioutil.ReadFile(filename)
				if err != nil {
					TermMessage(err)
					continue
				}
			}
			// If the file didn't exist, input will be empty, and we'll open an empty buffer
			buffers = append(buffers, NewBuffer(input, filename))
		}
	} else if !isatty.IsTerminal(os.Stdin.Fd()) {
		// Option 2
		// The input is not a terminal, so something is being piped in
		// and we should read from stdin
		input, err = ioutil.ReadAll(os.Stdin)
		buffers = append(buffers, NewBuffer(input, filename))
	} else {
		// Option 3, just open an empty buffer
		buffers = append(buffers, NewBuffer(input, filename))
	}

	return buffers
}

// InitConfigDir finds the configuration directory for micro according to the XDG spec.
// If no directory is found, it creates one.
func InitConfigDir() {
	xdgHome := os.Getenv("XDG_CONFIG_HOME")
	if xdgHome == "" {
		// The user has not set $XDG_CONFIG_HOME so we should act like it was set to ~/.config
		home, err := homedir.Dir()
		if err != nil {
			TermMessage("Error finding your home directory\nCan't load config files")
			return
		}
		xdgHome = home + "/.config"
	}
	configDir = xdgHome + "/micro"

	if _, err := os.Stat(xdgHome); os.IsNotExist(err) {
		// If the xdgHome doesn't exist we should create it
		err = os.Mkdir(xdgHome, os.ModePerm)
		if err != nil {
			TermMessage("Error creating XDG_CONFIG_HOME directory: " + err.Error())
		}
	}

	if _, err := os.Stat(configDir); os.IsNotExist(err) {
		// If the micro specific config directory doesn't exist we should create that too
		err = os.Mkdir(configDir, os.ModePerm)
		if err != nil {
			TermMessage("Error creating configuration directory: " + err.Error())
		}
	}
}

// InitScreen creates and initializes the tcell screen
func InitScreen() {
	// Should we enable true color?
	truecolor := os.Getenv("MICRO_TRUECOLOR") == "1"

	// In order to enable true color, we have to set the TERM to `xterm-truecolor` when
	// initializing tcell, but after that, we can set the TERM back to whatever it was
	oldTerm := os.Getenv("TERM")
	if truecolor {
		os.Setenv("TERM", "xterm-truecolor")
	}

	// Initilize tcell
	var err error
	screen, err = tcell.NewScreen()
	if err != nil {
		fmt.Println(err)
		os.Exit(1)
	}
	if err = screen.Init(); err != nil {
		fmt.Println(err)
		os.Exit(1)
	}

	// Now we can put the TERM back to what it was before
	if truecolor {
		os.Setenv("TERM", oldTerm)
	}

	screen.SetStyle(defStyle)
	screen.EnableMouse()
}

// RedrawAll redraws everything -- all the views and the messenger
func RedrawAll() {
	messenger.Clear()
	DisplayTabs()
	messenger.Display()
	for _, v := range tabs[curTab].views {
		v.Display()
	}
	screen.Show()
}

// Passing -version as a flag will have micro print out the version number
var flagVersion = flag.Bool("version", false, "Show the version number")

func main() {
	flag.Parse()
	if *flagVersion {
		// If -version was passed
		fmt.Println("Micro version:", Version)
		os.Exit(0)
	}

	// Start the Lua VM for running plugins
	L = lua.NewState()
	defer L.Close()

	// Some encoding stuff in case the user isn't using UTF-8
	encoding.Register()
	tcell.SetEncodingFallback(tcell.EncodingFallbackASCII)

	// Find the user's configuration directory (probably $XDG_CONFIG_HOME/micro)
	InitConfigDir()

	// Load the user's settings
	InitSettings()
	InitCommands()
	InitBindings()

	// Load the syntax files, including the colorscheme
	LoadSyntaxFiles()

	// Load the help files
	LoadHelp()

	// Start the screen
	InitScreen()

	// This is just so if we have an error, we can exit cleanly and not completely
	// mess up the terminal being worked in
	// In other words we need to shut down tcell before the program crashes
	defer func() {
		if err := recover(); err != nil {
			screen.Fini()
			fmt.Println("Micro encountered an error:", err)
			// Print the stack trace too
			fmt.Print(errors.Wrap(err, 2).ErrorStack())
			os.Exit(1)
		}
	}()

	// Create a new messenger
	// This is used for sending the user messages in the bottom of the editor
	messenger = new(Messenger)
	messenger.history = make(map[string][]string)

	// Now we load the input
	buffers := LoadInput()
	for _, buf := range buffers {
		// For each buffer we create a new tab and place the view in that tab
		tab := NewTabFromView(NewView(buf))
		tab.SetNum(len(tabs))
		tabs = append(tabs, tab)
		for _, t := range tabs {
			for _, v := range t.views {
				v.Resize(screen.Size())
			}
		}
	}

	// Load all the plugin stuff
	// We give plugins access to a bunch of variables here which could be useful to them
	L.SetGlobal("OS", luar.New(L, runtime.GOOS))
	L.SetGlobal("tabs", luar.New(L, tabs))
	L.SetGlobal("curTab", luar.New(L, curTab))
	L.SetGlobal("messenger", luar.New(L, messenger))
	L.SetGlobal("GetOption", luar.New(L, GetOption))
	L.SetGlobal("AddOption", luar.New(L, AddOption))
	L.SetGlobal("BindKey", luar.New(L, BindKey))
	L.SetGlobal("MakeCommand", luar.New(L, MakeCommand))
	L.SetGlobal("CurView", luar.New(L, CurView))
	L.SetGlobal("IsWordChar", luar.New(L, IsWordChar))

	// Used for asynchronous jobs
	L.SetGlobal("JobStart", luar.New(L, JobStart))
	L.SetGlobal("JobSend", luar.New(L, JobSend))
	L.SetGlobal("JobStop", luar.New(L, JobStop))

	LoadPlugins()

	jobs = make(chan JobFunction, 100)
	events = make(chan tcell.Event)

	// Here is the event loop which runs in a separate thread
	go func() {
		for {
			events <- screen.PollEvent()
		}
	}()

	for {
		// Display everything
		RedrawAll()

		var event tcell.Event

		// Check for new events
		select {
		case f := <-jobs:
			// If a new job has finished while running in the background we should execute the callback
			f.function(f.output, f.args...)
			continue
		case event = <-events:
		}

		switch e := event.(type) {
		case *tcell.EventMouse:
			if e.Buttons() == tcell.Button1 {
				// If the user left clicked we check a couple things
				_, h := screen.Size()
				x, y := e.Position()
				if y == h-1 && messenger.message != "" {
					// If the user clicked in the bottom bar, and there is a message down there
					// we copy it to the clipboard.
					// Often error messages are displayed down there so it can be useful to easily
					// copy the message
					clipboard.WriteAll(messenger.message)
					continue
				}

				// We loop through each view in the current tab and make sure the current view
				// it the one being clicked in
				for _, v := range tabs[curTab].views {
					if x >= v.x && x < v.x+v.width && y >= v.y && y < v.y+v.height {
						tabs[curTab].curView = v.Num
					}
				}
			}
		}

		// This function checks the mouse event for the possibility of changing the current tab
		// If the tab was changed it returns true
		if TabbarHandleMouseEvent(event) {
			continue
		}

		if searching {
			// Since searching is done in real time, we need to redraw every time
			// there is a new event in the search bar so we need a special function
			// to run instead of the standard HandleEvent.
			HandleSearchEvent(event, CurView())
		} else {
			// Send it to the view
			CurView().HandleEvent(event)
		}
	}
}
<|endoftext|>


<commit_before>// Copyright 2013 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// oracle: a tool for answering questions about Go source code.
//
// With -format=plain, the oracle prints query results to the standard
// output in an editor-friendly format in which every line of output
// is of the form "pos: text", where pos = "-" if unknown.
//
// With -format=json, the oracle prints structured data in JSON syntax.
//
package main

import (
	"bufio"
	"bytes"
	"encoding/json"
	"flag"
	"fmt"
	"go/build"
	"io"
	"log"
	"os"
	"runtime"
	"runtime/pprof"

	"code.google.com/p/go.tools/oracle"
)

var posFlag = flag.String("pos", "",
	"Filename and byte offset or extent of a syntax element about which to query, "+
		"e.g. foo.go:#123,#456, bar.go:#123.")

var modeFlag = flag.String("mode", "",
	"Mode of query to perform: e.g. callers, describe, etc.")

var ptalogFlag = flag.String("ptalog", "",
	"Location of the points-to analysis log file, or empty to disable logging.")

var formatFlag = flag.String("format", "plain", "Output format: 'plain' or 'json'.")

const usage = `Go source code oracle.
Usage: oracle [<flag> ...] [<arg> ...]
Use -help flag to display options.

The -mode flag is required; the -pos flag is required in most modes.

Examples:

Describe the syntax at offset 532 in this file (an import spec):
% oracle -mode=describe -pos=src/code.google.com/p/go.tools/cmd/oracle/main.go:#532 \
   code.google.com/p/go.tools/cmd/oracle

Print the callgraph of the trivial web-server in JSON format:
% oracle -mode=callgraph -format=json src/pkg/net/http/triv.go

`

var cpuprofile = flag.String("cpuprofile", "", "write cpu profile to file")

func init() {
	// If $GOMAXPROCS isn't set, use the full capacity of the machine.
	// For small machines, use at least 4 threads.
	if os.Getenv("GOMAXPROCS") == "" {
		n := runtime.NumCPU()
		if n < 4 {
			n = 4
		}
		runtime.GOMAXPROCS(n)
	}
}

func main() {
	flag.Parse()
	args := flag.Args()

	if len(args) == 0 {
		fmt.Fprint(os.Stderr, usage)
		os.Exit(1)
	}

	// Set up points-to analysis log file.
	var ptalog io.Writer
	if *ptalogFlag != "" {
		if f, err := os.Create(*ptalogFlag); err != nil {
			log.Fatalf(err.Error())
		} else {
			buf := bufio.NewWriter(f)
			ptalog = buf
			defer func() {
				buf.Flush()
				f.Close()
			}()
		}
	}

	// Profiling support.
	if *cpuprofile != "" {
		f, err := os.Create(*cpuprofile)
		if err != nil {
			log.Fatal(err)
		}
		pprof.StartCPUProfile(f)
		defer pprof.StopCPUProfile()
	}

	// -format flag
	if *formatFlag != "json" && *formatFlag != "plain" {
		fmt.Fprintf(os.Stderr, "Error: illegal -format value: %q", *formatFlag)
		os.Exit(1)
	}

	// Ask the oracle.
	res, err := oracle.Query(args, *modeFlag, *posFlag, ptalog, &build.Default)
	if err != nil {
		fmt.Fprintf(os.Stderr, "Error: %s\n", err)
		os.Exit(1)
	}

	// Print the result.
	switch *formatFlag {
	case "json":
		b, err := json.Marshal(res)
		if err != nil {
			fmt.Fprintf(os.Stderr, "JSON error: %s\n", err.Error())
			os.Exit(1)
		}
		var buf bytes.Buffer
		if err := json.Indent(&buf, b, "", "\t"); err != nil {
			fmt.Fprintf(os.Stderr, "json.Indent failed: %s", err)
			os.Exit(1)
		}
		os.Stdout.Write(buf.Bytes())

	case "plain":
		res.WriteTo(os.Stdout)
	}
}
<commit_msg>go.tools/oracle: restore edits lost in rev aa9b649bccd9 (botched merge)<commit_after>// Copyright 2013 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// oracle: a tool for answering questions about Go source code.
//
// With -format=plain, the oracle prints query results to the standard
// output in an editor-friendly format in which every line of output
// is of the form "pos: text", where pos = "-" if unknown.
//
// With -format=json, the oracle prints structured data in JSON syntax.
//
package main

import (
	"bufio"
	"bytes"
	"encoding/json"
	"flag"
	"fmt"
	"go/build"
	"io"
	"log"
	"os"
	"runtime"
	"runtime/pprof"

	"code.google.com/p/go.tools/importer"
	"code.google.com/p/go.tools/oracle"
)

var posFlag = flag.String("pos", "",
	"Filename and byte offset or extent of a syntax element about which to query, "+
		"e.g. foo.go:#123,#456, bar.go:#123.")

var modeFlag = flag.String("mode", "",
	"Mode of query to perform: e.g. callers, describe, etc.")

var ptalogFlag = flag.String("ptalog", "",
	"Location of the points-to analysis log file, or empty to disable logging.")

var formatFlag = flag.String("format", "plain", "Output format: 'plain' or 'json'.")

const usage = `Go source code oracle.
Usage: oracle [<flag> ...] <args> ...
Use -help flag to display options.

The -mode flag is required; the -pos flag is required in most modes.

Examples:

Describe the syntax at offset 532 in this file (an import spec):
% oracle -mode=describe -pos=src/code.google.com/p/go.tools/cmd/oracle/main.go:#532 \
   code.google.com/p/go.tools/cmd/oracle

Print the callgraph of the trivial web-server in JSON format:
% oracle -mode=callgraph -format=json src/pkg/net/http/triv.go

` + importer.InitialPackagesUsage

var cpuprofile = flag.String("cpuprofile", "", "write cpu profile to file")

func init() {
	// If $GOMAXPROCS isn't set, use the full capacity of the machine.
	// For small machines, use at least 4 threads.
	if os.Getenv("GOMAXPROCS") == "" {
		n := runtime.NumCPU()
		if n < 4 {
			n = 4
		}
		runtime.GOMAXPROCS(n)
	}
}

func main() {
	flag.Parse()
	args := flag.Args()

	if len(args) == 0 {
		fmt.Fprint(os.Stderr, usage)
		os.Exit(1)
	}

	// Set up points-to analysis log file.
	var ptalog io.Writer
	if *ptalogFlag != "" {
		if f, err := os.Create(*ptalogFlag); err != nil {
			log.Fatal(err)
		} else {
			buf := bufio.NewWriter(f)
			ptalog = buf
			defer func() {
				buf.Flush()
				f.Close()
			}()
		}
	}

	// Profiling support.
	if *cpuprofile != "" {
		f, err := os.Create(*cpuprofile)
		if err != nil {
			log.Fatal(err)
		}
		pprof.StartCPUProfile(f)
		defer pprof.StopCPUProfile()
	}

	// -format flag
	if *formatFlag != "json" && *formatFlag != "plain" {
		fmt.Fprintf(os.Stderr, "Error: illegal -format value: %q", *formatFlag)
		os.Exit(1)
	}

	// Ask the oracle.
	res, err := oracle.Query(args, *modeFlag, *posFlag, ptalog, &build.Default)
	if err != nil {
		fmt.Fprintf(os.Stderr, "%s\n", err)
		os.Exit(1)
	}

	// Print the result.
	switch *formatFlag {
	case "json":
		b, err := json.Marshal(res)
		if err != nil {
			fmt.Fprintf(os.Stderr, "JSON error: %s\n", err)
			os.Exit(1)
		}
		var buf bytes.Buffer
		if err := json.Indent(&buf, b, "", "\t"); err != nil {
			fmt.Fprintf(os.Stderr, "json.Indent failed: %s", err)
			os.Exit(1)
		}
		os.Stdout.Write(buf.Bytes())

	case "plain":
		res.WriteTo(os.Stdout)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"context"

	"github.com/restic/restic/internal/repository"
	"github.com/restic/restic/internal/restic"
)

// FindFilteredSnapshots yields Snapshots, either given explicitly by `snapshotIDs` or filtered from the list of all snapshots.
func FindFilteredSnapshots(ctx context.Context, repo *repository.Repository, hosts []string, tags []restic.TagList, paths []string, snapshotIDs []string) <-chan *restic.Snapshot {
	out := make(chan *restic.Snapshot)
	go func() {
		defer close(out)
		if len(snapshotIDs) != 0 {
			var (
				id         restic.ID
				usedFilter bool
				err        error
			)
			ids := make(restic.IDs, 0, len(snapshotIDs))
			// Process all snapshot IDs given as arguments.
			for _, s := range snapshotIDs {
				if s == "latest" {
					usedFilter = true
					id, err = restic.FindLatestSnapshot(ctx, repo, paths, tags, hosts)
					if err != nil {
						Warnf("Ignoring %q, no snapshot matched given filter (Paths:%v Tags:%v Hosts:%v)\n", s, paths, tags, hosts)
						continue
					}
				} else {
					id, err = restic.FindSnapshot(ctx, repo, s)
					if err != nil {
						Warnf("Ignoring %q, it is not a snapshot id\n", s)
						continue
					}
				}
				ids = append(ids, id)
			}

			// Give the user some indication their filters are not used.
			if !usedFilter && (len(hosts) != 0 || len(tags) != 0 || len(paths) != 0) {
				Warnf("Ignoring filters as there are explicit snapshot ids given\n")
			}

			for _, id := range ids.Uniq() {
				sn, err := restic.LoadSnapshot(ctx, repo, id)
				if err != nil {
					Warnf("Ignoring %q, could not load snapshot: %v\n", id, err)
					continue
				}
				select {
				case <-ctx.Done():
					return
				case out <- sn:
				}
			}
			return
		}

		snapshots, err := restic.FindFilteredSnapshots(ctx, repo, hosts, tags, paths)
		if err != nil {
			Warnf("could not load snapshots: %v\n", err)
			return
		}

		for _, sn := range snapshots {
			select {
			case <-ctx.Done():
				return
			case out <- sn:
			}
		}
	}()
	return out
}
<commit_msg>Improve snapshot filter warning message<commit_after>package main

import (
	"context"

	"github.com/restic/restic/internal/repository"
	"github.com/restic/restic/internal/restic"
)

// FindFilteredSnapshots yields Snapshots, either given explicitly by `snapshotIDs` or filtered from the list of all snapshots.
func FindFilteredSnapshots(ctx context.Context, repo *repository.Repository, hosts []string, tags []restic.TagList, paths []string, snapshotIDs []string) <-chan *restic.Snapshot {
	out := make(chan *restic.Snapshot)
	go func() {
		defer close(out)
		if len(snapshotIDs) != 0 {
			var (
				id         restic.ID
				usedFilter bool
				err        error
			)
			ids := make(restic.IDs, 0, len(snapshotIDs))
			// Process all snapshot IDs given as arguments.
			for _, s := range snapshotIDs {
				if s == "latest" {
					usedFilter = true
					id, err = restic.FindLatestSnapshot(ctx, repo, paths, tags, hosts)
					if err != nil {
						Warnf("Ignoring %q, no snapshot matched given filter (Paths:%v Tags:%v Hosts:%v)\n", s, paths, tags, hosts)
						continue
					}
				} else {
					id, err = restic.FindSnapshot(ctx, repo, s)
					if err != nil {
						Warnf("Ignoring %q: %v\n", s, err)
						continue
					}
				}
				ids = append(ids, id)
			}

			// Give the user some indication their filters are not used.
			if !usedFilter && (len(hosts) != 0 || len(tags) != 0 || len(paths) != 0) {
				Warnf("Ignoring filters as there are explicit snapshot ids given\n")
			}

			for _, id := range ids.Uniq() {
				sn, err := restic.LoadSnapshot(ctx, repo, id)
				if err != nil {
					Warnf("Ignoring %q, could not load snapshot: %v\n", id, err)
					continue
				}
				select {
				case <-ctx.Done():
					return
				case out <- sn:
				}
			}
			return
		}

		snapshots, err := restic.FindFilteredSnapshots(ctx, repo, hosts, tags, paths)
		if err != nil {
			Warnf("could not load snapshots: %v\n", err)
			return
		}

		for _, sn := range snapshots {
			select {
			case <-ctx.Done():
				return
			case out <- sn:
			}
		}
	}()
	return out
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"reflect"

	memdb "github.com/hashicorp/go-memdb"
)

// Uint64Index is used to extract a field from an object using reflection and
// builds an index on that field.
type Uint64Index struct {
	Field string
}

// FromObject implements the memdb.SingleIndexer interface.
func (s *Uint64Index) FromObject(obj interface{}) (bool, []byte, error) {
	v := reflect.ValueOf(obj)
	v = reflect.Indirect(v) // Dereference the pointer if any
	fv := v.FieldByName(s.Field)
	if !fv.IsValid() {
		return false, nil,
			fmt.Errorf("field '%s' for %#v is invalid", s.Field, obj)
	}
	return true, []byte(fmt.Sprintf("%d\x00", fv.Uint())), nil
}

// FromArgs implements the memdb.Indexer interface.
func (s *Uint64Index) FromArgs(args ...interface{}) ([]byte, error) {
	if len(args) != 1 {
		return nil, fmt.Errorf("must provide only a single argument")
	}
	if _, ok := args[0].(uint64); !ok {
		return nil, fmt.Errorf("argument must be a uint64: %#v", args[0])
	}
	return []byte(fmt.Sprintf("%d\x00", args[0])), nil
}

// Schema constants.
const (
	KeyValueStore = "kvs"
	SessionStore  = "ss"
	RequestStore  = "rs"

	Identifier = "id"
)

var schema = &memdb.DBSchema{
	Tables: map[string]*memdb.TableSchema{
		KeyValueStore: &memdb.TableSchema{
			Name: KeyValueStore,
			Indexes: map[string]*memdb.IndexSchema{
				Identifier: &memdb.IndexSchema{
					Name:   Identifier,
					Unique: true,
					Indexer: &memdb.StringFieldIndex{
						Field: "Key",
					},
				},
			},
		},
		SessionStore: &memdb.TableSchema{
			Name: SessionStore,
			Indexes: map[string]*memdb.IndexSchema{
				Identifier: &memdb.IndexSchema{
					Name:   Identifier,
					Unique: true,
					Indexer: &Uint64Index{
						Field: "ClientID",
					},
				},
			},
		},
		RequestStore: &memdb.TableSchema{
			Name: RequestStore,
			Indexes: map[string]*memdb.IndexSchema{
				Identifier: &memdb.IndexSchema{
					Name:   Identifier,
					Unique: true,
					Indexer: &memdb.CompoundIndex{
						Indexes: []memdb.Indexer{
							&Uint64Index{
								Field: "ClientID",
							},
							&Uint64Index{
								Field: "ClientSeq",
							},
						},
					},
				},
			},
		},
	},
}
<commit_msg>rkvd/schema.go: Use FormatUint in favor of Sprintf<commit_after>package main

import (
	"fmt"
	"reflect"
	"strconv"

	memdb "github.com/hashicorp/go-memdb"
)

// Uint64Index is used to extract a field from an object using reflection and
// builds an index on that field.
type Uint64Index struct {
	Field string
}

// FromObject implements the memdb.SingleIndexer interface.
func (s *Uint64Index) FromObject(obj interface{}) (bool, []byte, error) {
	v := reflect.ValueOf(obj)
	v = reflect.Indirect(v) // Dereference the pointer if any
	fv := v.FieldByName(s.Field)
	if !fv.IsValid() {
		return false, nil,
			fmt.Errorf("field '%s' for %#v is invalid", s.Field, obj)
	}
	val := strconv.FormatUint(fv.Uint(), 10)
	val += "\x00"
	return true, []byte(val), nil
}

// FromArgs implements the memdb.Indexer interface.
func (s *Uint64Index) FromArgs(args ...interface{}) ([]byte, error) {
	if len(args) != 1 {
		return nil, fmt.Errorf("must provide only a single argument")
	}
	i, ok := args[0].(uint64)
	if !ok {
		return nil, fmt.Errorf("argument must be a uint64: %#v", args[0])
	}
	val := strconv.FormatUint(i, 10)
	val += "\x00"
	return []byte(val), nil
}

// Schema constants.
const (
	KeyValueStore = "kvs"
	SessionStore  = "ss"
	RequestStore  = "rs"

	Identifier = "id"
)

var schema = &memdb.DBSchema{
	Tables: map[string]*memdb.TableSchema{
		KeyValueStore: &memdb.TableSchema{
			Name: KeyValueStore,
			Indexes: map[string]*memdb.IndexSchema{
				Identifier: &memdb.IndexSchema{
					Name:   Identifier,
					Unique: true,
					Indexer: &memdb.StringFieldIndex{
						Field: "Key",
					},
				},
			},
		},
		SessionStore: &memdb.TableSchema{
			Name: SessionStore,
			Indexes: map[string]*memdb.IndexSchema{
				Identifier: &memdb.IndexSchema{
					Name:   Identifier,
					Unique: true,
					Indexer: &Uint64Index{
						Field: "ClientID",
					},
				},
			},
		},
		RequestStore: &memdb.TableSchema{
			Name: RequestStore,
			Indexes: map[string]*memdb.IndexSchema{
				Identifier: &memdb.IndexSchema{
					Name:   Identifier,
					Unique: true,
					Indexer: &memdb.CompoundIndex{
						Indexes: []memdb.Indexer{
							&Uint64Index{
								Field: "ClientID",
							},
							&Uint64Index{
								Field: "ClientSeq",
							},
						},
					},
				},
			},
		},
	},
}
<|endoftext|>


<commit_before>package terraform

import (
	"strings"
	"sync"
	"testing"

	"github.com/hashicorp/terraform/addrs"
)

func TestNodeDestroyResourceDynamicExpand_deposedCount(t *testing.T) {
	var stateLock sync.RWMutex
	state := &State{
		Modules: []*ModuleState{
			&ModuleState{
				Path: rootModulePath,
				Resources: map[string]*ResourceState{
					"aws_instance.bar.0": &ResourceState{
						Type: "aws_instance",
						Deposed: []*InstanceState{
							&InstanceState{
								ID: "foo",
							},
						},
					},
					"aws_instance.bar.1": &ResourceState{
						Type: "aws_instance",
						Deposed: []*InstanceState{
							&InstanceState{
								ID: "bar",
							},
						},
					},
				},
			},
		},
	}

	m := testModule(t, "apply-cbd-count")
	n := &NodeDestroyResourceInstance{
		NodeAbstractResourceInstance: &NodeAbstractResourceInstance{
			NodeAbstractResource: NodeAbstractResource{
				Addr: addrs.RootModuleInstance.Resource(
					addrs.ManagedResourceMode, "aws_instance", "bar",
				),
				Config: m.Module.ManagedResources["aws_instance.bar"],
			},
			InstanceKey:   addrs.IntKey(0),
			ResourceState: state.Modules[0].Resources["aws_instance.bar.0"],
		},
	}

	g, err := n.DynamicExpand(&MockEvalContext{
		PathPath:   addrs.RootModuleInstance,
		StateState: state,
		StateLock:  &stateLock,
	})
	if err != nil {
		t.Fatalf("err: %s", err)
	}

	actual := strings.TrimSpace(g.String())
	expected := strings.TrimSpace(`
aws_instance.bar.0 (deposed #0)
`)
	if actual != expected {
		t.Fatalf("bad:\n\n%s", actual)
	}
}
<commit_msg>core: Fix test for NodeDestroyResource.DynamicExpand<commit_after>package terraform

import (
	"strings"
	"sync"
	"testing"

	"github.com/hashicorp/terraform/addrs"
)

func TestNodeDestroyResourceDynamicExpand_deposedCount(t *testing.T) {
	var stateLock sync.RWMutex
	state := &State{
		Modules: []*ModuleState{
			&ModuleState{
				Path: rootModulePath,
				Resources: map[string]*ResourceState{
					"aws_instance.bar.0": &ResourceState{
						Type: "aws_instance",
						Deposed: []*InstanceState{
							&InstanceState{
								ID: "foo",
							},
						},
						Provider: "provider.aws",
					},
					"aws_instance.bar.1": &ResourceState{
						Type: "aws_instance",
						Deposed: []*InstanceState{
							&InstanceState{
								ID: "bar",
							},
						},
						Provider: "provider.aws",
					},
				},
			},
		},
	}

	m := testModule(t, "apply-cbd-count")
	n := &NodeDestroyResourceInstance{
		NodeAbstractResourceInstance: &NodeAbstractResourceInstance{
			NodeAbstractResource: NodeAbstractResource{
				Addr: addrs.RootModuleInstance.Resource(
					addrs.ManagedResourceMode, "aws_instance", "bar",
				),
				Config: m.Module.ManagedResources["aws_instance.bar"],
			},
			InstanceKey:   addrs.IntKey(0),
			ResourceState: state.Modules[0].Resources["aws_instance.bar.0"],
		},
	}

	g, err := n.DynamicExpand(&MockEvalContext{
		PathPath:   addrs.RootModuleInstance,
		StateState: state,
		StateLock:  &stateLock,
	})
	if err != nil {
		t.Fatalf("err: %s", err)
	}

	got := strings.TrimSpace(g.String())
	want := strings.TrimSpace(`
aws_instance.bar[0] (deposed #0)
`)
	if got != want {
		t.Fatalf("wrong result\n\ngot:\n%s\n\nwant:\n%s", got, want)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"os"
	"os/signal"
	"syscall"

	"github.com/golang/glog"
	"github.com/libopenstorage/stork/drivers/volume"
	_ "github.com/libopenstorage/stork/drivers/volume/portworx"
	"github.com/libopenstorage/stork/pkg/extender"
	"github.com/libopenstorage/stork/pkg/initializer"
	"github.com/libopenstorage/stork/pkg/monitor"
	"github.com/libopenstorage/stork/pkg/snapshot"
	log "github.com/sirupsen/logrus"
	"github.com/urfave/cli"
	api_v1 "k8s.io/api/core/v1"
	clientset "k8s.io/client-go/kubernetes"
	core_v1 "k8s.io/client-go/kubernetes/typed/core/v1"
	"k8s.io/client-go/rest"
	"k8s.io/client-go/tools/leaderelection"
	"k8s.io/client-go/tools/leaderelection/resourcelock"
	"k8s.io/client-go/tools/record"
	"k8s.io/kubernetes/pkg/api/legacyscheme"
	"k8s.io/kubernetes/pkg/client/leaderelectionconfig"
)

var ext *extender.Extender

func main() {
	// Parse empty flags to suppress warnings from the snapshotter which uses
	// glog
	err := flag.CommandLine.Parse([]string{})
	if err != nil {
		log.Warnf("Error parsing flag: %v", err)
	}
	err = flag.Set("logtostderr", "true")
	if err != nil {
		log.Fatalf("Error setting glog flag: %v", err)
	}

	app := cli.NewApp()
	app.Name = "stork"
	app.Usage = "STorage Orchestartor Runtime for Kubernetes (STORK)"
	app.Version = "1.1.1"
	app.Action = run

	app.Flags = []cli.Flag{
		cli.BoolFlag{
			Name:  "verbose",
			Usage: "Enable verbose logging",
		},
		cli.StringFlag{
			Name:  "driver,d",
			Usage: "Storage driver name",
		},
		cli.BoolTFlag{
			Name:  "leader-elect",
			Usage: "Enable leader election (default: true)",
		},
		cli.StringFlag{
			Name:  "lock-object-name",
			Usage: "Name for the lock object (default: stork)",
			Value: "stork",
		},
		cli.StringFlag{
			Name:  "lock-object-namespace",
			Usage: "Namespace for the lock object (default: kube-system)",
			Value: "kube-system",
		},
		cli.BoolTFlag{
			Name:  "snapshotter",
			Usage: "Enable snapshotter (default: true)",
		},
		cli.BoolTFlag{
			Name:  "extender",
			Usage: "Enable scheduler extender for hyperconvergence (default: true)",
		},
		cli.BoolTFlag{
			Name:  "health-monitor",
			Usage: "Enable health monitoring of the storage driver (default: true)",
		},
		cli.Int64Flag{
			Name:  "health-monitor-interval",
			Usage: "The interval in seconds to monitor the health of the storage driver (default: 120, min: 30)",
		},
		cli.BoolFlag{
			Name:  "app-initializer",
			Usage: "EXPERIMENTAL: Enable application initializer to update scheduler name automatically (default: false)",
		},
	}

	if err := app.Run(os.Args); err != nil {
		log.Fatalf("Error starting stork: %v", err)
	}
}
func run(c *cli.Context) {

	driverName := c.String("driver")
	if len(driverName) == 0 {
		log.Fatalf("driver option is required")
	}

	verbose := c.Bool("verbose")
	if verbose {
		log.SetLevel(log.DebugLevel)
	}

	d, err := volume.Get(driverName)
	if err != nil {
		log.Fatalf("Error getting Stork Driver %v: %v", driverName, err)
	}

	if err = d.Init(nil); err != nil {
		log.Fatalf("Error initializing Stork Driver %v: %v", driverName, err)
	}

	if c.Bool("extender") {
		ext = &extender.Extender{
			Driver: d,
		}

		if err = ext.Start(); err != nil {
			log.Fatalf("Error starting scheduler extender: %v", err)
		}
	}

	runFunc := func(_ <-chan struct{}) {
		runStork(d, c)
	}

	leaderConfig := leaderelectionconfig.DefaultLeaderElectionConfiguration()
	leaderConfig.LeaderElect = c.BoolT("leader-elect")

	if leaderConfig.LeaderElect {
		config, err := rest.InClusterConfig()
		if err != nil {
			log.Fatalf("Error getting cluster config: %v", err)
		}

		k8sClient, err := clientset.NewForConfig(config)
		if err != nil {
			log.Fatalf("Error getting client, %v", err)
		}

		leaderConfig.ResourceLock = resourcelock.ConfigMapsResourceLock
		lockObjectName := c.String("lock-object-name")
		lockObjectNamespace := c.String("lock-object-namespace")

		eventBroadcaster := record.NewBroadcaster()
		eventBroadcaster.StartLogging(glog.Infof)
		eventBroadcaster.StartRecordingToSink(&core_v1.EventSinkImpl{Interface: core_v1.New(k8sClient.Core().RESTClient()).Events("")})
		recorder := eventBroadcaster.NewRecorder(legacyscheme.Scheme, api_v1.EventSource{Component: "stork"})

		id, err := os.Hostname()
		if err != nil {
			log.Fatalf("Error getting hostname: %v", err)
		}

		lockConfig := resourcelock.ResourceLockConfig{
			Identity:      id,
			EventRecorder: recorder,
		}

		resourceLock, err := resourcelock.New(
			leaderConfig.ResourceLock,
			lockObjectNamespace,
			lockObjectName,
			k8sClient.CoreV1(),
			lockConfig)
		if err != nil {
			log.Fatalf("Error creating resource lock: %v", err)
		}

		leaderElector, err := leaderelection.NewLeaderElector(
			leaderelection.LeaderElectionConfig{
				Lock:          resourceLock,
				LeaseDuration: leaderConfig.LeaseDuration.Duration,
				RenewDeadline: leaderConfig.RenewDeadline.Duration,
				RetryPeriod:   leaderConfig.RetryPeriod.Duration,

				Callbacks: leaderelection.LeaderCallbacks{
					OnStartedLeading: runFunc,
					OnStoppedLeading: func() {
						log.Fatalf("Stork lost master")
					},
				},
			})
		if err != nil {
			log.Fatalf("Error creating leader elector: %v", err)
		}

		leaderElector.Run()
	} else {
		runFunc(nil)
	}
}

func runStork(d volume.Driver, c *cli.Context) {
	initializer := &initializer.Initializer{
		Driver: d,
	}
	if c.Bool("app-initializer") {
		if err := initializer.Start(); err != nil {
			log.Fatalf("Error starting initializer: %v", err)
		}
	}

	monitor := &monitor.Monitor{
		Driver:      d,
		IntervalSec: c.Int64("health-monitor-interval"),
	}

	if c.Bool("health-monitor") {
		if err := monitor.Start(); err != nil {
			log.Fatalf("Error starting storage monitor: %v", err)
		}
	}

	snapshotController := &snapshotcontroller.SnapshotController{
		Driver: d,
	}
	if c.Bool("snapshotter") {
		if err := snapshotController.Start(); err != nil {
			log.Fatalf("Error starting snapshot controller: %v", err)
		}
	}

	signalChan := make(chan os.Signal, 1)
	signal.Notify(signalChan, syscall.SIGINT, syscall.SIGTERM)
	for {
		<-signalChan
		log.Printf("Shutdown signal received, exiting...")
		if c.Bool("extender") {
			if err := ext.Stop(); err != nil {
				log.Warnf("Error stopping extender: %v", err)
			}
		}
		if c.Bool("health-monitor") {
			if err := monitor.Stop(); err != nil {
				log.Warnf("Error stopping monitor: %v", err)
			}
		}
		if c.Bool("snapshotter") {
			if err := snapshotController.Stop(); err != nil {
				log.Warnf("Error stopping snapshot controller: %v", err)
			}
		}
		if c.Bool("app-initializer") {
			if err := initializer.Stop(); err != nil {
				log.Warnf("Error stopping app-initializer: %v", err)
			}
		}
		if err := d.Stop(); err != nil {
			log.Warnf("Error stopping driver: %v", err)
		}
		os.Exit(0)
	}
}
<commit_msg>Start migration controller from cmd<commit_after>package main

import (
	"flag"
	"os"
	"os/signal"
	"syscall"

	"github.com/libopenstorage/stork/drivers/volume"
	_ "github.com/libopenstorage/stork/drivers/volume/portworx"
	"github.com/libopenstorage/stork/pkg/controller"
	"github.com/libopenstorage/stork/pkg/extender"
	"github.com/libopenstorage/stork/pkg/initializer"
	"github.com/libopenstorage/stork/pkg/migration"
	"github.com/libopenstorage/stork/pkg/monitor"
	"github.com/libopenstorage/stork/pkg/snapshot"
	log "github.com/sirupsen/logrus"
	"github.com/urfave/cli"
	api_v1 "k8s.io/api/core/v1"
	clientset "k8s.io/client-go/kubernetes"
	core_v1 "k8s.io/client-go/kubernetes/typed/core/v1"
	"k8s.io/client-go/rest"
	"k8s.io/client-go/tools/leaderelection"
	"k8s.io/client-go/tools/leaderelection/resourcelock"
	"k8s.io/client-go/tools/record"
	"k8s.io/kubernetes/pkg/api/legacyscheme"
	"k8s.io/kubernetes/pkg/client/leaderelectionconfig"
)

const (
	defaultLockObjectName      = "stork"
	defaultLockObjectNamespace = "kube-system"
	eventComponentName         = "stork"
)

var ext *extender.Extender

func main() {
	// Parse empty flags to suppress warnings from the snapshotter which uses
	// glog
	err := flag.CommandLine.Parse([]string{})
	if err != nil {
		log.Warnf("Error parsing flag: %v", err)
	}
	err = flag.Set("logtostderr", "true")
	if err != nil {
		log.Fatalf("Error setting glog flag: %v", err)
	}

	app := cli.NewApp()
	app.Name = "stork"
	app.Usage = "STorage Orchestartor Runtime for Kubernetes (STORK)"
	app.Version = "1.1.1"
	app.Action = run

	app.Flags = []cli.Flag{
		cli.BoolFlag{
			Name:  "verbose",
			Usage: "Enable verbose logging",
		},
		cli.StringFlag{
			Name:  "driver,d",
			Usage: "Storage driver name",
		},
		cli.BoolTFlag{
			Name:  "leader-elect",
			Usage: "Enable leader election (default: true)",
		},
		cli.StringFlag{
			Name:  "lock-object-name",
			Usage: "Name for the lock object (default: stork)",
			Value: defaultLockObjectName,
		},
		cli.StringFlag{
			Name:  "lock-object-namespace",
			Usage: "Namespace for the lock object (default: kube-system)",
			Value: defaultLockObjectNamespace,
		},
		cli.BoolTFlag{
			Name:  "snapshotter",
			Usage: "Enable snapshotter (default: true)",
		},
		cli.BoolTFlag{
			Name:  "extender",
			Usage: "Enable scheduler extender for hyperconvergence (default: true)",
		},
		cli.BoolTFlag{
			Name:  "health-monitor",
			Usage: "Enable health monitoring of the storage driver (default: true)",
		},
		cli.Int64Flag{
			Name:  "health-monitor-interval",
			Usage: "The interval in seconds to monitor the health of the storage driver (default: 120, min: 30)",
		},
		cli.BoolTFlag{
			Name:  "migration-controller",
			Usage: "Start the migration controller (default: true)",
		},
		cli.BoolFlag{
			Name:  "app-initializer",
			Usage: "EXPERIMENTAL: Enable application initializer to update scheduler name automatically (default: false)",
		},
	}

	if err := app.Run(os.Args); err != nil {
		log.Fatalf("Error starting stork: %v", err)
	}
}
func run(c *cli.Context) {

	driverName := c.String("driver")
	if len(driverName) == 0 {
		log.Fatalf("driver option is required")
	}

	verbose := c.Bool("verbose")
	if verbose {
		log.SetLevel(log.DebugLevel)
	}

	d, err := volume.Get(driverName)
	if err != nil {
		log.Fatalf("Error getting Stork Driver %v: %v", driverName, err)
	}

	if err = d.Init(nil); err != nil {
		log.Fatalf("Error initializing Stork Driver %v: %v", driverName, err)
	}

	config, err := rest.InClusterConfig()
	if err != nil {
		log.Fatalf("Error getting cluster config: %v", err)
	}

	k8sClient, err := clientset.NewForConfig(config)
	if err != nil {
		log.Fatalf("Error getting client, %v", err)
	}

	eventBroadcaster := record.NewBroadcaster()
	eventBroadcaster.StartRecordingToSink(&core_v1.EventSinkImpl{Interface: core_v1.New(k8sClient.Core().RESTClient()).Events("")})
	recorder := eventBroadcaster.NewRecorder(legacyscheme.Scheme, api_v1.EventSource{Component: eventComponentName})

	if c.Bool("extender") {
		ext = &extender.Extender{
			Driver: d,
		}

		if err = ext.Start(); err != nil {
			log.Fatalf("Error starting scheduler extender: %v", err)
		}
	}

	runFunc := func(_ <-chan struct{}) {
		runStork(d, recorder, c)
	}

	leaderConfig := leaderelectionconfig.DefaultLeaderElectionConfiguration()
	leaderConfig.LeaderElect = c.BoolT("leader-elect")

	if leaderConfig.LeaderElect {

		leaderConfig.ResourceLock = resourcelock.ConfigMapsResourceLock
		lockObjectName := c.String("lock-object-name")
		lockObjectNamespace := c.String("lock-object-namespace")

		id, err := os.Hostname()
		if err != nil {
			log.Fatalf("Error getting hostname: %v", err)
		}

		lockConfig := resourcelock.ResourceLockConfig{
			Identity:      id,
			EventRecorder: recorder,
		}

		resourceLock, err := resourcelock.New(
			leaderConfig.ResourceLock,
			lockObjectNamespace,
			lockObjectName,
			k8sClient.CoreV1(),
			lockConfig)
		if err != nil {
			log.Fatalf("Error creating resource lock: %v", err)
		}

		leaderElector, err := leaderelection.NewLeaderElector(
			leaderelection.LeaderElectionConfig{
				Lock:          resourceLock,
				LeaseDuration: leaderConfig.LeaseDuration.Duration,
				RenewDeadline: leaderConfig.RenewDeadline.Duration,
				RetryPeriod:   leaderConfig.RetryPeriod.Duration,

				Callbacks: leaderelection.LeaderCallbacks{
					OnStartedLeading: runFunc,
					OnStoppedLeading: func() {
						log.Fatalf("Stork lost master")
					},
				},
			})
		if err != nil {
			log.Fatalf("Error creating leader elector: %v", err)
		}

		leaderElector.Run()
	} else {
		runFunc(nil)
	}
}

func runStork(d volume.Driver, recorder record.EventRecorder, c *cli.Context) {
	if err := controller.Init(); err != nil {
		log.Fatalf("Error initializing controller: %v", err)
	}

	initializer := &initializer.Initializer{
		Driver: d,
	}
	if c.Bool("app-initializer") {
		if err := initializer.Start(); err != nil {
			log.Fatalf("Error starting initializer: %v", err)
		}
	}

	monitor := &monitor.Monitor{
		Driver:      d,
		IntervalSec: c.Int64("health-monitor-interval"),
	}

	if c.Bool("health-monitor") {
		if err := monitor.Start(); err != nil {
			log.Fatalf("Error starting storage monitor: %v", err)
		}
	}

	snapshotController := &snapshotcontroller.SnapshotController{
		Driver: d,
	}
	if c.Bool("snapshotter") {
		if err := snapshotController.Start(); err != nil {
			log.Fatalf("Error starting snapshot controller: %v", err)
		}
	}

	if c.Bool("migration-controller") {
		migration := migration.Migration{
			Driver:   d,
			Recorder: recorder,
		}
		if err := migration.Init(); err != nil {
			log.Fatalf("Error initializing migration: %v", err)
		}
	}

	// The controller should be started at the end
	err := controller.Run()
	if err != nil {
		log.Fatalf("Error starting controller: %v", err)
	}

	signalChan := make(chan os.Signal, 1)
	signal.Notify(signalChan, syscall.SIGINT, syscall.SIGTERM)
	for {
		<-signalChan
		log.Printf("Shutdown signal received, exiting...")
		if c.Bool("extender") {
			if err := ext.Stop(); err != nil {
				log.Warnf("Error stopping extender: %v", err)
			}
		}
		if c.Bool("health-monitor") {
			if err := monitor.Stop(); err != nil {
				log.Warnf("Error stopping monitor: %v", err)
			}
		}
		if c.Bool("snapshotter") {
			if err := snapshotController.Stop(); err != nil {
				log.Warnf("Error stopping snapshot controller: %v", err)
			}
		}
		if c.Bool("app-initializer") {
			if err := initializer.Stop(); err != nil {
				log.Warnf("Error stopping app-initializer: %v", err)
			}
		}
		if err := d.Stop(); err != nil {
			log.Warnf("Error stopping driver: %v", err)
		}
		os.Exit(0)
	}
}
<|endoftext|>


<commit_before>package cmd

import (
	"fmt"
	"os"
	"path/filepath"

	"github.com/Masterminds/cookoo"
)

// ReadyToGlide fails if the environment is not sufficient for using glide.
//
// Most importantly, it fails if glide.yaml is not present in the current
// working directory.
func ReadyToGlide(c cookoo.Context, p *cookoo.Params) (interface{}, cookoo.Interrupt) {
	fname := p.Get("filename", "glide.yaml").(string)
	if _, err := os.Stat(fname); err != nil {
		cwd, _ := os.Getwd()
		return false, fmt.Errorf("%s is missing from %s", fname, cwd)
	}
	return true, nil
}

// Return the path to the vendor directory.
func VendorPath(c cookoo.Context) (string, error) {
	vendor := c.Get("VendorDir", "vendor").(string)
	filename := c.Get("yaml", "glide.yaml").(string)
	cwd, err := os.Getwd()
	if err != nil {
		return "", err
	}

	// Find the directory that contains glide.yaml
	yamldir, err := glideWD(cwd, filename)
	if err != nil {
		return cwd, err
	}

	gopath := filepath.Join(yamldir, vendor)

	return gopath, nil
}

func glideWD(dir, filename string) (string, error) {
	fullpath := filepath.Join(dir, filename)

	if _, err := os.Stat(fullpath); err == nil {
		return dir, nil
	}

	base := filepath.Dir(dir)
	if base == dir {
		return "", fmt.Errorf("Cannot resolve parent of %s", base)
	}

	return glideWD(base, filename)
}
<commit_msg>Removed old file.<commit_after><|endoftext|>


<commit_before>package main

import (
	"log"
	"os"

	jsondb "github.com/ebittleman/voting/database/json"
	"github.com/ebittleman/voting/eventmanager"
	"github.com/ebittleman/voting/eventstore/json"
	"github.com/ebittleman/voting/views"
	"github.com/ebittleman/voting/voting/commands"
	"github.com/ebittleman/voting/voting/handlers"
	"github.com/ebittleman/voting/voting/model"
	votingViews "github.com/ebittleman/voting/voting/views"
	uuid "github.com/satori/go.uuid"
)

func main() {
	if code := run(); code != 0 {
		os.Exit(code)
	}
}

func run() int {
	conn, err := jsondb.Open("./.data")
	if err != nil {
		log.Println("Fatal: ", err)
		return 1
	}
	defer conn.Close()

	eventStore, err := json.New(conn)
	if err != nil {
		log.Println("Fatal: ", err)
		return 1
	}

	viewsTable, err := views.NewTable(conn)
	if err != nil {
		log.Println("Fatal: ", err)
		return 1
	}

	openPollsView, err := votingViews.NewOpenPolls(eventStore)
	if err != nil {
		log.Println("Fatal: ", err)
		return 1
	}
	defer openPollsView.Close()

	eventManager := eventmanager.New()

	openPollsHandler := handlers.NewOpenPolls(openPollsView, viewsTable, eventManager)
	defer openPollsHandler.Close()
	defer eventManager.Close()

	id := uuid.NewV4().String()
	createPoll := commands.NewCreatePoll(
		eventStore,
		eventManager,
		id,
		[]model.Issue{
			model.Issue{
				Topic: "What's for dinner?",
				Choices: []string{
					"Chicken",
					"Beef",
					"Veggies",
				},
			},
		},
	)

	err = createPoll.Run()
	if err != nil {
		log.Println("Fatal: ", err)
		return 1
	}

	openPoll := commands.NewOpenPoll(
		eventStore,
		eventManager,
		id,
	)

	err = openPoll.Run()
	if err != nil {
		log.Println("Fatal: ", err)
		return 1
	}

	return 0
}
<commit_msg>cleaned up example main a bit<commit_after>package main

import (
	"log"
	"os"

	jsondb "github.com/ebittleman/voting/database/json"
	"github.com/ebittleman/voting/eventmanager"
	"github.com/ebittleman/voting/eventstore/json"
	"github.com/ebittleman/voting/views"
	"github.com/ebittleman/voting/voting/commands"
	"github.com/ebittleman/voting/voting/handlers"
	"github.com/ebittleman/voting/voting/model"
	votingViews "github.com/ebittleman/voting/voting/views"
	uuid "github.com/satori/go.uuid"
)

func main() {
	if code := run(); code != 0 {
		os.Exit(code)
	}
}

func run() int {
	// get a directory to put our json files in
	conn, err := jsondb.Open("./.data")
	if err != nil {
		log.Println("Fatal: ", err)
		return 1
	}
	defer conn.Close()

	// creates an event store that will write to event.json when it closes
	eventStore, err := json.New(conn)
	if err != nil {
		log.Println("Fatal: ", err)
		return 1
	}

	// creates a simple json table for store view data
	viewsTable, err := views.NewTable(conn)
	if err != nil {
		log.Println("Fatal: ", err)
		return 1
	}

	// processes the current event store and builds a view of all "Open" polls
	openPollsView, err := votingViews.NewOpenPolls(eventStore)
	if err != nil {
		log.Println("Fatal: ", err)
		return 1
	}
	defer openPollsView.Close()

	// component that routes events in the local process
	eventManager := eventmanager.New()
	defer eventManager.Close()

	// listens for PollOpened and PollClosed events, triggers the openPollsView
	// to rebuild, then saves it to the viewsTable
	openPollsHandler := handlers.NewOpenPolls(openPollsView, viewsTable, eventManager)
	defer openPollsHandler.Close()

	// generate a new poll id
	id := uuid.NewV4().String()

	// initialize a new CreatePoll command
	createPoll := commands.NewCreatePoll(
		eventStore,
		eventManager,
		id,
		[]model.Issue{
			model.Issue{
				Topic: "What's for dinner?",
				Choices: []string{
					"Chicken",
					"Beef",
					"Veggies",
				},
			},
		},
	)

	// create the poll
	err = createPoll.Run()
	if err != nil {
		log.Println("Fatal: ", err)
		return 1
	}

	// initialize the OpenPoll command and reuse the the same id to open our
	// newly created poll
	openPoll := commands.NewOpenPoll(
		eventStore,
		eventManager,
		id,
	)

	// open the poll
	err = openPoll.Run()
	if err != nil {
		log.Println("Fatal: ", err)
		return 1
	}

	return 0
}
<|endoftext|>


<commit_before>package manager

import (
	"crypto/rand"
	"encoding/json"
	"fmt"
	"strconv"

	"github.com/libopenstorage/openstorage/api"
	clusterclient "github.com/libopenstorage/openstorage/api/client/cluster"
	"github.com/libopenstorage/openstorage/cluster"
	"github.com/portworx/kvdb"
	"github.com/sirupsen/logrus"
)

const (
	// ClusterPairKey is the key at which info about cluster pairs is stored in kvdb
	ClusterPairKey = "cluster/pair"
	// ClusterPairDefaultKey is the key at which the id for the default pair is stored
	clusterPairDefaultKey = "cluster/pair/default"
)

// CreatePair remote pairs this cluster with a remote cluster.
func (c *ClusterManager) CreatePair(
	request *api.ClusterPairCreateRequest,
) (*api.ClusterPairCreateResponse, error) {
	remoteIp := request.RemoteClusterIp

	// Pair with remote server
	logrus.Infof("Attempting to pair with cluster at IP %v", remoteIp)

	processRequest := &api.ClusterPairProcessRequest{
		SourceClusterId:    c.Uuid(),
		RemoteClusterToken: request.RemoteClusterToken,
	}

	endpoint := "http://" + remoteIp + ":" + strconv.FormatUint(uint64(request.RemoteClusterPort), 10)
	clnt, err := clusterclient.NewClusterClient(endpoint, cluster.APIVersion)
	if err != nil {
		return nil, err
	}
	remoteCluster := clusterclient.ClusterManager(clnt)

	// Issue a remote pair request
	resp, err := remoteCluster.ProcessPairRequest(processRequest)
	if err != nil {
		logrus.Warnf("Unable to pair with %v: %v", remoteIp, err)
		return nil, err
	}

	// Alert all listeners that we are pairing with a cluster.
	for e := c.listeners.Front(); e != nil; e = e.Next() {
		err = e.Value.(cluster.ClusterListener).CreatePair(
			resp,
		)
		if err != nil {
			logrus.Errorf("Unable to notify %v on a cluster pair event: %v",
				e.Value.(cluster.ClusterListener).String(),
				err,
			)
			return nil, err
		}
	}

	pairInfo := &api.ClusterPairInfo{
		Id:               resp.RemoteClusterId,
		Name:             resp.RemoteClusterName,
		Endpoint:         endpoint,
		CurrentEndpoints: resp.RemoteClusterEndpoints,
		Token:            request.RemoteClusterToken,
		Options:          resp.Options,
	}

	err = pairCreate(pairInfo, request.SetDefault)
	if err != nil {
		return nil, err
	}
	logrus.Infof("Successfully paired with cluster ID %v", resp.RemoteClusterId)

	response := &api.ClusterPairCreateResponse{
		RemoteClusterId:   pairInfo.Id,
		RemoteClusterName: pairInfo.Name,
	}
	return response, nil
}

// ProcessPairRequest handles a remote cluster's pair request
func (c *ClusterManager) ProcessPairRequest(
	request *api.ClusterPairProcessRequest,
) (*api.ClusterPairProcessResponse, error) {
	if request.SourceClusterId == c.Uuid() {
		return nil, fmt.Errorf("Cannot create cluster pair with self")
	}

	response := &api.ClusterPairProcessResponse{
		RemoteClusterId:   c.Uuid(),
		RemoteClusterName: c.config.ClusterId,
	}

	// Get the token without resetting it
	tokenResp, err := c.GetPairToken(false)
	if err != nil {
		return nil, fmt.Errorf("Error getting Cluster Token: %v", err)
	}
	if tokenResp.Token != request.RemoteClusterToken {
		return nil, fmt.Errorf("Token mismatch during pairing")
	}

	// Alert all listeners that we have received a pair request
	for e := c.listeners.Front(); e != nil; e = e.Next() {
		err := e.Value.(cluster.ClusterListener).ProcessPairRequest(
			request,
			response,
		)
		if err != nil {
			logrus.Errorf("Unable to notify %v on a a cluster remote pair request: %v",
				e.Value.(cluster.ClusterListener).String(),
				err,
			)

			return nil, err
		}
	}

	logrus.Infof("Successfully paired with remote cluster %v", request.SourceClusterId)

	return response, nil
}

func (c *ClusterManager) RefreshPair(
	id string,
) error {
	pair, err := pairGet(id)
	if err != nil {
		return err
	}
	processRequest := &api.ClusterPairProcessRequest{
		SourceClusterId:    c.Uuid(),
		RemoteClusterToken: pair.Token,
	}

	endpoints := pair.CurrentEndpoints
	endpoints = append(endpoints, pair.Endpoint)
	for _, endpoint := range endpoints {
		clnt, err := clusterclient.NewClusterClient(endpoint, cluster.APIVersion)
		if err != nil {
			logrus.Warnf("Unable to create cluster client for %v: %v", endpoint, err)
			continue
		}
		remoteCluster := clusterclient.ClusterManager(clnt)

		// Issue a remote pair request to get updated info about the cluster
		resp, err := remoteCluster.ProcessPairRequest(processRequest)
		if err != nil {
			logrus.Warnf("Unable to get pair info from %v: %v", endpoint, err)
			continue
		}
		pairInfo := &api.ClusterPairInfo{
			Id:               resp.RemoteClusterId,
			Name:             resp.RemoteClusterName,
			Endpoint:         pair.Endpoint,
			CurrentEndpoints: resp.RemoteClusterEndpoints,
			Token:            pair.Token,
		}
		// Use original options and override with updates ones. This
		// prevents any options we created locally from getting overriden
		pairInfo.Options = pair.Options
		for k, v := range resp.Options {
			pairInfo.Options[k] = v
		}

		return pairUpdate(pairInfo)
	}
	return fmt.Errorf("error updating pair info for %v, all endpoints are unreachable", id)
}

func (c *ClusterManager) DeletePair(
	id string,
) error {
	if err := pairDelete(id); err != nil {
		return err
	}

	// Right now the listeners aren't notified of the delete.
	// Need to add that so that they can stop any operations with that cluster

	logrus.Infof("Successfully deleted pairing with cluster %v", id)
	return nil
}

func (c *ClusterManager) GetPair(
	id string,
) (*api.ClusterPairGetResponse, error) {
	var err error
	if id == "" {
		id, err = getDefaultPairId()
		if err != nil {
			return nil, err
		}
	}
	pair, err := pairGet(id)
	if err != nil {
		return nil, err
	}
	return &api.ClusterPairGetResponse{
		PairInfo: pair,
	}, nil
}

func (c *ClusterManager) EnumeratePairs() (*api.ClusterPairsEnumerateResponse, error) {
	response := &api.ClusterPairsEnumerateResponse{}
	pairs, err := pairList()
	if err != nil {
		return nil, err
	}
	response.Pairs = pairs
	response.DefaultId, err = getDefaultPairId()
	if err != nil {
		logrus.Warnf("Error getting default cluster pair: %v", err)
	}
	return response, nil
}

func (c *ClusterManager) GetPairToken(
	reset bool,
) (*api.ClusterPairTokenGetResponse, error) {
	kvdb := kvdb.Instance()
	kvlock, err := kvdb.LockWithID(clusterLockKey, c.config.NodeId)
	if err != nil {
		logrus.Errorf("Unable to obtain cluster lock for getting cluster pair token: %v", err)
		return nil, err
	}
	defer kvdb.Unlock(kvlock)

	db, _, err := readClusterInfo()
	if err != nil {
		return nil, err
	}

	// Generate a token if we don't have one or a reset has been requested
	if db.PairToken == "" || reset {
		b := make([]byte, 64)
		rand.Read(b)
		db.PairToken = fmt.Sprintf("%x", b)

		_, err = writeClusterInfo(&db)
		if err != nil {
			return nil, err
		}
	}

	return &api.ClusterPairTokenGetResponse{
		Token: db.PairToken,
	}, nil
}

func pairList() (map[string]*api.ClusterPairInfo, error) {
	kvdb := kvdb.Instance()

	pairs := make(map[string]*api.ClusterPairInfo)
	kv, err := kvdb.Enumerate(ClusterPairKey)
	if err != nil {
		return nil, err
	}

	for _, v := range kv {
		if v.Key == clusterPairDefaultKey {
			continue
		}
		info := &api.ClusterPairInfo{}
		err = json.Unmarshal(v.Value, &info)
		if err != nil {
			return nil, err
		}
		pairs[info.Id] = info
	}

	return pairs, nil
}

func pairCreate(info *api.ClusterPairInfo, setDefault bool) error {
	kv := kvdb.Instance()
	kvp, err := kv.Lock(ClusterPairKey)
	if err != nil {
		return err
	}
	defer kv.Unlock(kvp)

	key := ClusterPairKey + "/" + info.Id
	_, err = kv.Create(key, info, 0)
	if err != nil {
		if err == kvdb.ErrExist {
			kvp, err = kv.Get(key)
			if err != nil {
				return err
			}
			storedInfo := &api.ClusterPairInfo{}
			err = json.Unmarshal(kvp.Value, &storedInfo)
			if err != nil {
				return err
			}
			if info.Token != storedInfo.Token {
				return fmt.Errorf("Invalid token for already paired cluster %v", info.Id)
			}
		} else {
			return err
		}
	}

	defaultId, err := getDefaultPairId()
	// Set this pair as the default if no default is set or it has
	// explicilty been asked
	if setDefault || err == kvdb.ErrNotFound || defaultId == "" {
		err = setDefaultPairId(info.Id)
		if err != nil {
			return err
		}
	}

	return nil
}

// Return the default pair id if set, error if none set
func getDefaultPairId() (string, error) {
	kv := kvdb.Instance()
	kvp, err := kv.Get(clusterPairDefaultKey)
	if err != nil {
		return "", err
	}
	return string(kvp.Value), nil
}

func setDefaultPairId(id string) error {
	kv := kvdb.Instance()
	_, err := kv.Put(clusterPairDefaultKey, id, 0)
	if err != nil {
		return err
	}
	return nil
}

func deleteDefaultPairId() error {
	kv := kvdb.Instance()
	_, err := kv.Delete(clusterPairDefaultKey)
	if err != nil {
		return err
	}
	return nil
}

func pairUpdate(info *api.ClusterPairInfo) error {
	kvdb := kvdb.Instance()
	kvp, err := kvdb.Lock(ClusterPairKey)
	if err != nil {
		return err
	}
	defer kvdb.Unlock(kvp)

	key := ClusterPairKey + "/" + info.Id
	_, err = kvdb.Update(key, info, 0)
	if err != nil {
		return err
	}

	return nil
}

func pairDelete(id string) error {
	kv := kvdb.Instance()
	kvp, err := kv.Lock(ClusterPairKey)
	if err != nil {
		return err
	}
	defer kv.Unlock(kvp)

	defaultId, err := getDefaultPairId()
	if err != kvdb.ErrNotFound && defaultId == id {
		defaultUpdated := false
		// Set one of the other pairs as the default
		pairs, err := pairList()
		if err != nil {
			logrus.Warnf("Error getting clusterpairs, will not update default: %v", err)
		} else {
			for _, pair := range pairs {
				if pair.Id != id {
					err := setDefaultPairId(pair.Id)
					if err != nil {
						logrus.Warnf("Error updating default clusterpair: %v", err)
					} else {
						defaultUpdated = true
						break
					}
				}
			}

		}
		if !defaultUpdated {
			err = deleteDefaultPairId()
			if err != nil {
				return fmt.Errorf("error deleting default pair id")
			}
		}
	}

	key := ClusterPairKey + "/" + id
	_, err = kv.Delete(key)
	if err != nil {
		return err
	}
	return nil
}

func pairGet(id string) (*api.ClusterPairInfo, error) {
	kv := kvdb.Instance()
	kvp, err := kv.Lock(ClusterPairKey)
	if err != nil {
		return nil, err
	}
	defer kv.Unlock(kvp)

	key := ClusterPairKey + "/" + id
	kvp, err = kv.Get(key)
	if err != nil {
		return nil, err
	}
	info := &api.ClusterPairInfo{}
	err = json.Unmarshal(kvp.Value, &info)
	if err != nil {
		return nil, err
	}
	return info, nil
}
<commit_msg>Change cluster pair log to debug<commit_after>package manager

import (
	"crypto/rand"
	"encoding/json"
	"fmt"
	"strconv"

	"github.com/libopenstorage/openstorage/api"
	clusterclient "github.com/libopenstorage/openstorage/api/client/cluster"
	"github.com/libopenstorage/openstorage/cluster"
	"github.com/portworx/kvdb"
	"github.com/sirupsen/logrus"
)

const (
	// ClusterPairKey is the key at which info about cluster pairs is stored in kvdb
	ClusterPairKey = "cluster/pair"
	// ClusterPairDefaultKey is the key at which the id for the default pair is stored
	clusterPairDefaultKey = "cluster/pair/default"
)

// CreatePair remote pairs this cluster with a remote cluster.
func (c *ClusterManager) CreatePair(
	request *api.ClusterPairCreateRequest,
) (*api.ClusterPairCreateResponse, error) {
	remoteIp := request.RemoteClusterIp

	// Pair with remote server
	logrus.Infof("Attempting to pair with cluster at IP %v", remoteIp)

	processRequest := &api.ClusterPairProcessRequest{
		SourceClusterId:    c.Uuid(),
		RemoteClusterToken: request.RemoteClusterToken,
	}

	endpoint := "http://" + remoteIp + ":" + strconv.FormatUint(uint64(request.RemoteClusterPort), 10)
	clnt, err := clusterclient.NewClusterClient(endpoint, cluster.APIVersion)
	if err != nil {
		return nil, err
	}
	remoteCluster := clusterclient.ClusterManager(clnt)

	// Issue a remote pair request
	resp, err := remoteCluster.ProcessPairRequest(processRequest)
	if err != nil {
		logrus.Warnf("Unable to pair with %v: %v", remoteIp, err)
		return nil, err
	}

	// Alert all listeners that we are pairing with a cluster.
	for e := c.listeners.Front(); e != nil; e = e.Next() {
		err = e.Value.(cluster.ClusterListener).CreatePair(
			resp,
		)
		if err != nil {
			logrus.Errorf("Unable to notify %v on a cluster pair event: %v",
				e.Value.(cluster.ClusterListener).String(),
				err,
			)
			return nil, err
		}
	}

	pairInfo := &api.ClusterPairInfo{
		Id:               resp.RemoteClusterId,
		Name:             resp.RemoteClusterName,
		Endpoint:         endpoint,
		CurrentEndpoints: resp.RemoteClusterEndpoints,
		Token:            request.RemoteClusterToken,
		Options:          resp.Options,
	}

	err = pairCreate(pairInfo, request.SetDefault)
	if err != nil {
		return nil, err
	}
	logrus.Infof("Successfully paired with cluster ID %v", resp.RemoteClusterId)

	response := &api.ClusterPairCreateResponse{
		RemoteClusterId:   pairInfo.Id,
		RemoteClusterName: pairInfo.Name,
	}
	return response, nil
}

// ProcessPairRequest handles a remote cluster's pair request
func (c *ClusterManager) ProcessPairRequest(
	request *api.ClusterPairProcessRequest,
) (*api.ClusterPairProcessResponse, error) {
	if request.SourceClusterId == c.Uuid() {
		return nil, fmt.Errorf("Cannot create cluster pair with self")
	}

	response := &api.ClusterPairProcessResponse{
		RemoteClusterId:   c.Uuid(),
		RemoteClusterName: c.config.ClusterId,
	}

	// Get the token without resetting it
	tokenResp, err := c.GetPairToken(false)
	if err != nil {
		return nil, fmt.Errorf("Error getting Cluster Token: %v", err)
	}
	if tokenResp.Token != request.RemoteClusterToken {
		return nil, fmt.Errorf("Token mismatch during pairing")
	}

	// Alert all listeners that we have received a pair request
	for e := c.listeners.Front(); e != nil; e = e.Next() {
		err := e.Value.(cluster.ClusterListener).ProcessPairRequest(
			request,
			response,
		)
		if err != nil {
			logrus.Errorf("Unable to notify %v on a a cluster remote pair request: %v",
				e.Value.(cluster.ClusterListener).String(),
				err,
			)

			return nil, err
		}
	}

	logrus.Infof("Successfully paired with remote cluster %v", request.SourceClusterId)

	return response, nil
}

func (c *ClusterManager) RefreshPair(
	id string,
) error {
	pair, err := pairGet(id)
	if err != nil {
		return err
	}
	processRequest := &api.ClusterPairProcessRequest{
		SourceClusterId:    c.Uuid(),
		RemoteClusterToken: pair.Token,
	}

	endpoints := pair.CurrentEndpoints
	endpoints = append(endpoints, pair.Endpoint)
	for _, endpoint := range endpoints {
		clnt, err := clusterclient.NewClusterClient(endpoint, cluster.APIVersion)
		if err != nil {
			logrus.Warnf("Unable to create cluster client for %v: %v", endpoint, err)
			continue
		}
		remoteCluster := clusterclient.ClusterManager(clnt)

		// Issue a remote pair request to get updated info about the cluster
		resp, err := remoteCluster.ProcessPairRequest(processRequest)
		if err != nil {
			logrus.Warnf("Unable to get pair info from %v: %v", endpoint, err)
			continue
		}
		pairInfo := &api.ClusterPairInfo{
			Id:               resp.RemoteClusterId,
			Name:             resp.RemoteClusterName,
			Endpoint:         pair.Endpoint,
			CurrentEndpoints: resp.RemoteClusterEndpoints,
			Token:            pair.Token,
		}
		// Use original options and override with updates ones. This
		// prevents any options we created locally from getting overriden
		pairInfo.Options = pair.Options
		for k, v := range resp.Options {
			pairInfo.Options[k] = v
		}

		return pairUpdate(pairInfo)
	}
	return fmt.Errorf("error updating pair info for %v, all endpoints are unreachable", id)
}

func (c *ClusterManager) DeletePair(
	id string,
) error {
	if err := pairDelete(id); err != nil {
		return err
	}

	// Right now the listeners aren't notified of the delete.
	// Need to add that so that they can stop any operations with that cluster

	logrus.Infof("Successfully deleted pairing with cluster %v", id)
	return nil
}

func (c *ClusterManager) GetPair(
	id string,
) (*api.ClusterPairGetResponse, error) {
	var err error
	if id == "" {
		id, err = getDefaultPairId()
		if err != nil {
			return nil, err
		}
	}
	pair, err := pairGet(id)
	if err != nil {
		return nil, err
	}
	return &api.ClusterPairGetResponse{
		PairInfo: pair,
	}, nil
}

func (c *ClusterManager) EnumeratePairs() (*api.ClusterPairsEnumerateResponse, error) {
	response := &api.ClusterPairsEnumerateResponse{}
	pairs, err := pairList()
	if err != nil {
		return nil, err
	}
	response.Pairs = pairs
	response.DefaultId, err = getDefaultPairId()
	if err != nil {
		logrus.Debugf("Error getting default cluster pair: %v", err)
	}
	return response, nil
}

func (c *ClusterManager) GetPairToken(
	reset bool,
) (*api.ClusterPairTokenGetResponse, error) {
	kvdb := kvdb.Instance()
	kvlock, err := kvdb.LockWithID(clusterLockKey, c.config.NodeId)
	if err != nil {
		logrus.Errorf("Unable to obtain cluster lock for getting cluster pair token: %v", err)
		return nil, err
	}
	defer kvdb.Unlock(kvlock)

	db, _, err := readClusterInfo()
	if err != nil {
		return nil, err
	}

	// Generate a token if we don't have one or a reset has been requested
	if db.PairToken == "" || reset {
		b := make([]byte, 64)
		rand.Read(b)
		db.PairToken = fmt.Sprintf("%x", b)

		_, err = writeClusterInfo(&db)
		if err != nil {
			return nil, err
		}
	}

	return &api.ClusterPairTokenGetResponse{
		Token: db.PairToken,
	}, nil
}

func pairList() (map[string]*api.ClusterPairInfo, error) {
	kvdb := kvdb.Instance()

	pairs := make(map[string]*api.ClusterPairInfo)
	kv, err := kvdb.Enumerate(ClusterPairKey)
	if err != nil {
		return nil, err
	}

	for _, v := range kv {
		if v.Key == clusterPairDefaultKey {
			continue
		}
		info := &api.ClusterPairInfo{}
		err = json.Unmarshal(v.Value, &info)
		if err != nil {
			return nil, err
		}
		pairs[info.Id] = info
	}

	return pairs, nil
}

func pairCreate(info *api.ClusterPairInfo, setDefault bool) error {
	kv := kvdb.Instance()
	kvp, err := kv.Lock(ClusterPairKey)
	if err != nil {
		return err
	}
	defer kv.Unlock(kvp)

	key := ClusterPairKey + "/" + info.Id
	_, err = kv.Create(key, info, 0)
	if err != nil {
		if err == kvdb.ErrExist {
			kvp, err = kv.Get(key)
			if err != nil {
				return err
			}
			storedInfo := &api.ClusterPairInfo{}
			err = json.Unmarshal(kvp.Value, &storedInfo)
			if err != nil {
				return err
			}
			if info.Token != storedInfo.Token {
				return fmt.Errorf("Invalid token for already paired cluster %v", info.Id)
			}
		} else {
			return err
		}
	}

	defaultId, err := getDefaultPairId()
	// Set this pair as the default if no default is set or it has
	// explicilty been asked
	if setDefault || err == kvdb.ErrNotFound || defaultId == "" {
		err = setDefaultPairId(info.Id)
		if err != nil {
			return err
		}
	}

	return nil
}

// Return the default pair id if set, error if none set
func getDefaultPairId() (string, error) {
	kv := kvdb.Instance()
	kvp, err := kv.Get(clusterPairDefaultKey)
	if err != nil {
		return "", err
	}
	return string(kvp.Value), nil
}

func setDefaultPairId(id string) error {
	kv := kvdb.Instance()
	_, err := kv.Put(clusterPairDefaultKey, id, 0)
	if err != nil {
		return err
	}
	return nil
}

func deleteDefaultPairId() error {
	kv := kvdb.Instance()
	_, err := kv.Delete(clusterPairDefaultKey)
	if err != nil {
		return err
	}
	return nil
}

func pairUpdate(info *api.ClusterPairInfo) error {
	kvdb := kvdb.Instance()
	kvp, err := kvdb.Lock(ClusterPairKey)
	if err != nil {
		return err
	}
	defer kvdb.Unlock(kvp)

	key := ClusterPairKey + "/" + info.Id
	_, err = kvdb.Update(key, info, 0)
	if err != nil {
		return err
	}

	return nil
}

func pairDelete(id string) error {
	kv := kvdb.Instance()
	kvp, err := kv.Lock(ClusterPairKey)
	if err != nil {
		return err
	}
	defer kv.Unlock(kvp)

	defaultId, err := getDefaultPairId()
	if err != kvdb.ErrNotFound && defaultId == id {
		defaultUpdated := false
		// Set one of the other pairs as the default
		pairs, err := pairList()
		if err != nil {
			logrus.Warnf("Error getting clusterpairs, will not update default: %v", err)
		} else {
			for _, pair := range pairs {
				if pair.Id != id {
					err := setDefaultPairId(pair.Id)
					if err != nil {
						logrus.Warnf("Error updating default clusterpair: %v", err)
					} else {
						defaultUpdated = true
						break
					}
				}
			}

		}
		if !defaultUpdated {
			err = deleteDefaultPairId()
			if err != nil {
				return fmt.Errorf("error deleting default pair id")
			}
		}
	}

	key := ClusterPairKey + "/" + id
	_, err = kv.Delete(key)
	if err != nil {
		return err
	}
	return nil
}

func pairGet(id string) (*api.ClusterPairInfo, error) {
	kv := kvdb.Instance()
	kvp, err := kv.Lock(ClusterPairKey)
	if err != nil {
		return nil, err
	}
	defer kv.Unlock(kvp)

	key := ClusterPairKey + "/" + id
	kvp, err = kv.Get(key)
	if err != nil {
		return nil, err
	}
	info := &api.ClusterPairInfo{}
	err = json.Unmarshal(kvp.Value, &info)
	if err != nil {
		return nil, err
	}
	return info, nil
}
<|endoftext|>


<commit_before><commit_msg>fix panic when trying to run an empty pipeline<commit_after><|endoftext|>


<commit_before>package module

import (
	"sync"
)

type Module interface {
	OnInit()
	OnDestroy()
	Run(closeSig chan bool)
}

type module struct {
	mi       Module
	closeSig chan bool
	wg       sync.WaitGroup
}

var mods []*module

func Register(mi Module) {
	m := &module{
		mi:       mi,
		closeSig: make(chan bool, 1),
	}

	mods = append(mods, m)
}

func Init() {
	for i := 0; i < len(mods); i++ {
		mods[i].mi.OnInit()
	}

	for i := 0; i < len(mods); i++ {
		go run(mods[i])
	}
}

func Destroy() {
	for i := len(mods) - 1; i >= 0; i-- {
		m := mods[i]
		m.closeSig <- true
		m.wg.Wait()
		m.mi.OnDestroy()
	}
}

func run(m *module) {
	m.wg.Add(1)
	m.mi.Run(m.closeSig)
	m.wg.Done()
}
<commit_msg>new module<commit_after>package module

import (
	"sync"
)

type Module interface {
	OnInit()
	OnDestroy()
	Run(closeSig chan bool)
}

type module struct {
	mi       Module
	closeSig chan bool
	wg       sync.WaitGroup
}

var mods []*module

func Register(mi Module) {
	m := new(module)
	m.mi = mi
	m.closeSig = make(chan bool, 1)

	mods = append(mods, m)
}

func Init() {
	for i := 0; i < len(mods); i++ {
		mods[i].mi.OnInit()
	}

	for i := 0; i < len(mods); i++ {
		go run(mods[i])
	}
}

func Destroy() {
	for i := len(mods) - 1; i >= 0; i-- {
		m := mods[i]
		m.closeSig <- true
		m.wg.Wait()
		m.mi.OnDestroy()
	}
}

func run(m *module) {
	m.wg.Add(1)
	m.mi.Run(m.closeSig)
	m.wg.Done()
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"os"
	"os/signal"
	"strings"
	"syscall"
	"time"

	"github.com/sirupsen/logrus"

	"github.com/supergiant/control/pkg/controlplane"
	"github.com/supergiant/control/pkg/proxy"
)

var (
	version       = "unstable"
	addr          = flag.String("address", "0.0.0.0", "network interface to attach server to")
	port          = flag.Int("port", 8080, "tcp port to listen for incoming requests")
	storageMode   = flag.String("storage-mode", "file", "storage type either file(default), memory or etcd")
	storageURI    = flag.String("storage-uri", "supertiant.db", "uri of storage depends on selected storage type, for memory storage type this is empty")
	templatesDir  = flag.String("templates", "/etc/supergiant/templates/", "supergiant will load script templates from the specified directory on start")
	logLevel      = flag.String("log-level", "INFO", "logging level, e.g. info, warning, debug, error, fatal")
	logFormat     = flag.String("log-format", "txt", "logging format [txt json]")
	spawnInterval = flag.Int("spawnInterval", 5, "interval between API calls to cloud provider for creating instance")
	//TODO: rewrite to single flag port-range
	ProxiesPortRangeFrom = flag.Int("proxies-port-from", 60200, "first tcp port in a range of binding reverse proxies for service apps")
	ProxiesPortRangeTo   = flag.Int("proxies-port-to", 60250, "last tcp port in a range of binding reverse proxies for service apps")
	pprofListenStr       = flag.String("pprofListenStr", "",
		"pprof listen str host:port")
)

func main() {
	flag.Parse()

	configureLogging(*logLevel, *logFormat)

	cfg := &controlplane.Config{
		Addr:          *addr,
		Port:          *port,
		StorageMode:   *storageMode,
		StorageURI:    *storageURI,
		TemplatesDir:  *templatesDir,
		ReadTimeout:   time.Second * 20,
		WriteTimeout:  time.Second * 10,
		IdleTimeout:   time.Second * 120,
		SpawnInterval: time.Second * time.Duration(*spawnInterval),

		PprofListenStr: *pprofListenStr,

		ProxiesPortRange: proxy.PortRange{int32(*ProxiesPortRangeFrom), int32(*ProxiesPortRangeTo)},
		Version:          version,
	}

	server, err := controlplane.New(cfg)
	if err != nil {
		logrus.Infof("configuration: %+v", *cfg)
		logrus.Fatalf("broken configuration: %v", err)
	}

	sigs := make(chan os.Signal, 1)
	signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM)

	go func() {
		<-sigs
		logrus.Info("shutting down...")
		server.Shutdown()
	}()

	logrus.Infof("supergiant is starting on port %d", *port)
	server.Start()
}

// TODO: create sglog package
func configureLogging(level, format string) {
	l, err := logrus.ParseLevel(level)
	if err != nil {
		// set logLevel to INFO by default
		l = logrus.InfoLevel
	}
	logrus.SetLevel(l)

	switch strings.TrimSpace(format) {
	case "json":
		logrus.SetFormatter(&logrus.JSONFormatter{})
	default:
		logrus.SetFormatter(&logrus.TextFormatter{
			FullTimestamp: true,
		})
	}
}
<commit_msg>Supertiant -> Supergiant (#1317)<commit_after>package main

import (
	"flag"
	"os"
	"os/signal"
	"strings"
	"syscall"
	"time"

	"github.com/sirupsen/logrus"

	"github.com/supergiant/control/pkg/controlplane"
	"github.com/supergiant/control/pkg/proxy"
)

var (
	version       = "unstable"
	addr          = flag.String("address", "0.0.0.0", "network interface to attach server to")
	port          = flag.Int("port", 8080, "tcp port to listen for incoming requests")
	storageMode   = flag.String("storage-mode", "file", "storage type either file(default), memory or etcd")
	storageURI    = flag.String("storage-uri", "supergiant.db", "uri of storage depends on selected storage type, for memory storage type this is empty")
	templatesDir  = flag.String("templates", "/etc/supergiant/templates/", "supergiant will load script templates from the specified directory on start")
	logLevel      = flag.String("log-level", "INFO", "logging level, e.g. info, warning, debug, error, fatal")
	logFormat     = flag.String("log-format", "txt", "logging format [txt json]")
	spawnInterval = flag.Int("spawnInterval", 5, "interval between API calls to cloud provider for creating instance")
	//TODO: rewrite to single flag port-range
	ProxiesPortRangeFrom = flag.Int("proxies-port-from", 60200, "first tcp port in a range of binding reverse proxies for service apps")
	ProxiesPortRangeTo   = flag.Int("proxies-port-to", 60250, "last tcp port in a range of binding reverse proxies for service apps")
	pprofListenStr       = flag.String("pprofListenStr", "",
		"pprof listen str host:port")
)

func main() {
	flag.Parse()

	configureLogging(*logLevel, *logFormat)

	cfg := &controlplane.Config{
		Addr:          *addr,
		Port:          *port,
		StorageMode:   *storageMode,
		StorageURI:    *storageURI,
		TemplatesDir:  *templatesDir,
		ReadTimeout:   time.Second * 20,
		WriteTimeout:  time.Second * 10,
		IdleTimeout:   time.Second * 120,
		SpawnInterval: time.Second * time.Duration(*spawnInterval),

		PprofListenStr: *pprofListenStr,

		ProxiesPortRange: proxy.PortRange{int32(*ProxiesPortRangeFrom), int32(*ProxiesPortRangeTo)},
		Version:          version,
	}

	server, err := controlplane.New(cfg)
	if err != nil {
		logrus.Infof("configuration: %+v", *cfg)
		logrus.Fatalf("broken configuration: %v", err)
	}

	sigs := make(chan os.Signal, 1)
	signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM)

	go func() {
		<-sigs
		logrus.Info("shutting down...")
		server.Shutdown()
	}()

	logrus.Infof("supergiant is starting on port %d", *port)
	server.Start()
}

// TODO: create sglog package
func configureLogging(level, format string) {
	l, err := logrus.ParseLevel(level)
	if err != nil {
		// set logLevel to INFO by default
		l = logrus.InfoLevel
	}
	logrus.SetLevel(l)

	switch strings.TrimSpace(format) {
	case "json":
		logrus.SetFormatter(&logrus.JSONFormatter{})
	default:
		logrus.SetFormatter(&logrus.TextFormatter{
			FullTimestamp: true,
		})
	}
}
<|endoftext|>


<commit_before>// Copyright 2021 PingCAP, Inc.
//
// 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 ddltest

import (
	"fmt"
	"os"
	"testing"

	zaplog "github.com/pingcap/log"
	"github.com/pingcap/tidb/util/logutil"
	"github.com/pingcap/tidb/util/testbridge"
	"go.uber.org/goleak"
)

func TestMain(m *testing.M) {
	testbridge.WorkaroundGoCheckFlags()
	err := logutil.InitLogger(&logutil.LogConfig{Config: zaplog.Config{Level: *logLevel}})
	if err != nil {
		fmt.Fprint(os.Stderr, err.Error())
		os.Exit(1)
	}
	opts := []goleak.Option{
		goleak.IgnoreTopFunction("go.etcd.io/etcd/pkg/logutil.(*MergeLogger).outputLoop"),
		goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"),
		goleak.IgnoreTopFunction("internal/poll.runtime_pollWait"),
		goleak.IgnoreTopFunction("net/http.(*persistConn).writeLoop"),
	}
	goleak.VerifyTestMain(m, opts...)
}
<commit_msg>*: skip mysql client goroutine leak detection in integration ddl (#30467)<commit_after>// Copyright 2021 PingCAP, Inc.
//
// 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 ddltest

import (
	"fmt"
	"os"
	"testing"

	zaplog "github.com/pingcap/log"
	"github.com/pingcap/tidb/util/logutil"
	"github.com/pingcap/tidb/util/testbridge"
	"go.uber.org/goleak"
)

func TestMain(m *testing.M) {
	testbridge.WorkaroundGoCheckFlags()
	err := logutil.InitLogger(&logutil.LogConfig{Config: zaplog.Config{Level: *logLevel}})
	if err != nil {
		fmt.Fprint(os.Stderr, err.Error())
		os.Exit(1)
	}
	opts := []goleak.Option{
		goleak.IgnoreTopFunction("go.etcd.io/etcd/pkg/logutil.(*MergeLogger).outputLoop"),
		goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"),
		goleak.IgnoreTopFunction("internal/poll.runtime_pollWait"),
		goleak.IgnoreTopFunction("net/http.(*persistConn).writeLoop"),
		goleak.IgnoreTopFunction("github.com/go-sql-driver/mysql.(*mysqlConn).startWatcher.func1"),
		goleak.IgnoreTopFunction("database/sql.(*DB).connectionOpener"),
	}
	goleak.VerifyTestMain(m, opts...)
}
<|endoftext|>