<commit_before>//
package vagrant

import (
	"bufio"
	"bytes"
	"fmt"
	"io/ioutil"
	"os"
	"os/exec"
	"path/filepath"
	"regexp"
	"strconv"
	"strings"
	"sync"
	"time"

	"github.com/nanobox-io/nanobox/config"
)

//
var err error

// Exists ensure vagrant is installed
func Exists() (exists bool) {
	var err error

	// check if vagrant is installed
	if _, err = exec.LookPath("vagrant"); err == nil {

		// initilize Vagrant incase it hasn't been; there is a chance that Vagrant has
		// never been used meaning there won't be a .vagrant.d folder, so we initialize
		// vagrant just to ensure it's ready to be used with nanobox by running any
		// vagrant command (in this case "vagrant -v").
		if b, err := exec.Command("vagrant", "-v").CombinedOutput(); err != nil {
			config.Fatal("[util/vagrant/vagrant] exec.Command() failed", string(b))
		}

		// read setup_version to determine if the version of vagrant is too old
		// (< 1.5.0) and needs to be migrated
		b, err := ioutil.ReadFile(filepath.Join(config.Home, ".vagrant.d", "setup_version"))
		if err != nil {
			config.Fatal("[util/vagrant/vagrant] ioutil.ReadFile() failed", err.Error())
		}

		// convert the []byte value from the file into a float 'version'
		version, err := strconv.ParseFloat(string(b), 64)
		if err != nil {
			config.Fatal("[util/vagrant/vagrant] strconv.ParseFloat() failed", err.Error())
		}

		// if the current version of vagrant is less than a 'working version' (1.5)
		// give instructions on how to update
		if version < 1.5 {
			fmt.Println(`
Nanobox has detected that you are using an old version of Vagrant (<1.5). Before
you can continue you'll need to run "vagrant update" and follow the instructions
to update Vagrant.
			`)

			// exit here to allow for upgrade
			os.Exit(0)
		}

		// if all checks pass
		exists = true
	}

	return
}

// run runs a vagrant command
func run(cmd *exec.Cmd) error {

	//
	handleCMDout(cmd)

	// start the command; we need this to 'fire and forget' so that we can manually
	// capture and modify the commands output above
	if err := cmd.Start(); err != nil {
		return err
	}

	// wait for the command to complete/fail and exit, giving us a chance to scan
	// the output
	if err := cmd.Wait(); err != nil {
		return err
	}

	return nil
}

// runInContext runs a command in the context of a Vagrantfile (from the same dir)
func runInContext(cmd *exec.Cmd) error {

	// run the command from ~/.nanobox/apps/<config.App>. Running the command from
	// the directory that contains the Vagratfile ensure that the command can
	// atleast run (especially in cases like 'create' where a VM hadn't been created
	// yet, and a UUID isn't available)
	setContext(config.AppDir)

	//
	handleCMDout(cmd)

	// start the command; we need this to 'fire and forget' so that we can manually
	// capture and modify the commands output above
	if err := cmd.Start(); err != nil {
		return err
	}

	// wait for the command to complete/fail and exit, giving us a chance to scan
	// the output
	if err := cmd.Wait(); err != nil {
		return err
	}

	// switch back to project dir
	setContext(config.CWDir)

	return nil
}

// setContext changes the working directory to the designated context
func setContext(context string) {
	if err := os.Chdir(context); err != nil {
		fmt.Printf("No app found at %s. Exiting...\n", config.AppDir)
		os.Exit(1)
	}
}

func customScanner(data []byte, atEOF bool) (advance int, token []byte, err error) {

	//
	if atEOF && len(data) == 0 {
		return 0, nil, nil
	}

	if i := bytes.IndexByte(data, '\n'); i >= 0 {
		// We have a full newline-terminated line.
		return i + 1, dropCR(data[0:i]), nil
	}

	if i := bytes.IndexByte(data, '\r'); i >= 0 {
		return i + 1, dropCR(data[0:i]), nil
	}

	// If we're at EOF, we have a final, non-terminated line. Return it.
	if atEOF {
		return len(data), dropCR(data), nil
	}

	// Request more data.
	return 0, nil, nil
}

// dropCR drops a terminal \r from the data.
func dropCR(data []byte) []byte {
	if len(data) > 0 && data[len(data)-1] == '\r' {
		return data[0 : len(data)-1]
	}
	return data
}

// handleCMDout
func handleCMDout(cmd *exec.Cmd) {

	// create a stderr pipe that will write any error messages to the log
	stderr, err := cmd.StderrPipe()
	if err != nil {
		Fatal("[util/vagrant/vagrant] cmd.StderrPipe() failed", err.Error())
	}

	// log any command errors to the log
	stderrScanner := bufio.NewScanner(stderr)
	go func() {

		//
		var once sync.Once
		for stderrScanner.Scan() {

			// only display the error message once, but log every error
			once.Do(func() { Error("A vagrant error occured", "") })
			Log.Error(stderrScanner.Text())
		}
	}()

	// create a stdout pipe that will allow for scanning the output line-by-line;
	// if needed a stderr pipe could also be created at some point
	stdout, err := cmd.StdoutPipe()
	if err != nil {
		Fatal("[util/vagrant/vagrant] cmd.StdoutPipe() failed", err.Error())
	}

	// scan the command output intercepting only 'important' lines of vagrant output'
	// and tailoring their message so as to not flood the output.
	// styled according to: http://nanodocs.gopagoda.io/engines/style-guide
	stdoutScanner := bufio.NewScanner(stdout)
	stdoutScanner.Split(customScanner)

	// this is a mapping of all the vagrant output and our output to show instead
	filter := map[string]string{
		"VM not created. Moving on...":                   "Nanobox not yet created, use 'nanobox dev' or 'nanobox run' to create it.",
		"VirtualBox VM is already running.":              "This nanobox is already running",
		"Importing base box 'nanobox/boot2docker'...":    "Importing nanobox base image",
		"Booting VM...":                                  "Booting virtual machine",
		"Configuring and enabling network interfaces...": "Configuring virtual network",
		"Mounting shared folders...":                     fmt.Sprintf("Mounting source code (%s)", config.CWDir),
		"Waiting for nanobox server...":                  "Starting nanobox server",
		"Attempting graceful shutdown of VM...":          "Shutting down virtual machine",
		"Forcing shutdown of VM...":                      "Shutting down virtual machine",
		"Saving VM state and suspending execution...":    "Saving virtual machine",
		"Resuming suspended VM...":                       "", // "Resuming virtual machine", // this is output by its command
		"Destroying VM and associated drives...":         "", // "Destroying virtual machine", // this is output by its command
	}

	var done chan bool
	var next chan string
	progressing := false

	//
	go func() {
		for stdoutScanner.Scan() {

			//
			txt := strings.TrimSpace(stdoutScanner.Text())

			// log all vagrant output (might as well)
			Log.Info(txt)

			//
			switch {

			// show the progress bar if vagrant downloads nanobox/boot2docker
			case strings.Contains(txt, "box: Progress:"):

				progressing = true

				// subMatch[1] - percentage downloaded
				// subMatch[2] - amount/s
				// subMatch[3] - estimated time remaining
				subMatch := regexp.MustCompile(`box: Progress: (\d{1,3})% \(Rate: (.*), Estimated time remaining: (\d*:\d*:\d*)`).FindStringSubmatch(txt)

				// if for some reason we don't get the matches we need just skip that
				// line; this should never happen unless vagrant change their ouput
				if len(subMatch) < 4 {
					continue
				}

				// if for some reason the submatch fails to convert to an int just skip
				// the line; this will likely never happen
				i, err := strconv.Atoi(subMatch[1])
				if err != nil {
					continue
				}

				// show download progress: [*** progress *** 0.0%] 00:00:00 remaining
				fmt.Printf("\r\033[K   [%-41s %s%%] %s (%s remaining)", strings.Repeat("*", int(float64(i)/2.5)), subMatch[1], subMatch[2], subMatch[3])

			// if it's NOT a progress string and we were previously progressing
			case !strings.Contains(txt, "box: Progress:") && progressing:
				fmt.Printf("\r\033[K   [**************************************** 100.0%%] 00:00:00 remaining\n")
				progressing = false

				// fallthrough so we dont miss any messages
				fallthrough

			// filter and print any messages received from vagrant
			case strings.HasPrefix(txt, fmt.Sprintf("==> %v: ", config.Nanofile.Name)):
				subMatch := regexp.MustCompile(`==> \S*:\s(.*)`).FindStringSubmatch(txt)

				// if for some reason the regex failed to pull anything just skip the
				// line; this should never happen unless vagrant changes their output
				if len(subMatch) <= 1 {
					continue
				}

				// if the line is found in our filter print it
				if v, ok := filter[subMatch[1]]; ok {

					// indicate that there the next message has arrived by closing the previous
					// message channel
					if next != nil {
						close(next)
					}

					// wait on done (skipping the first wait)
					if done != nil {
						<-done
					}

					// make a new next
					next = make(chan string)

					// make a new done
					done = make(chan bool)

					// fire up the printer/dotter
					go printMessage(next, done)

					// send the next message
					next <- v
				}
			}
		}

		// close next once the scan is complete
		if next != nil {
			close(next)
		}

		// close done once the scan is complete
		// if done != nil {
		// 	close(done)
		// }
	}()
}

// printMessage takes a message and done channel, reading of the message channel
// and printing the message with a loader
func printMessage(msg chan string, done chan bool) {

	var out string

	for {
		select {

		// read the message
		case msg, ok := <-msg:

			// if !ok that means the channel was closed (another message received) so
			// print the final output, close the spinner and done, and return
			if !ok {
				fmt.Printf("\r\033[K   - %s\n", out)

				// stop the spinner
				done <- true

				close(done)
				return
			}

			// because a final message ("") will come when the channel is closed, store
			// the message here after that happens to have the actual messages
			out = msg

			// fire up the spinner
			go func() {

				spinner := `-\|/`
				i := 0

				for {
					select {

					// spin baby spin
					default:
						fmt.Printf("\r\033[K   %s %s", string(spinner[i%len(spinner)]), out)
						<-time.After(time.Second / 24)
						i++

					// message complete, stop spinner
					case <-done:
						return
					}
				}
			}()
		}
	}
}
<commit_msg>commenting out unused vagrant replacement text<commit_after>//
package vagrant

import (
	"bufio"
	"bytes"
	"fmt"
	"io/ioutil"
	"os"
	"os/exec"
	"path/filepath"
	"regexp"
	"strconv"
	"strings"
	"sync"
	"time"

	"github.com/nanobox-io/nanobox/config"
)

//
var err error

// Exists ensure vagrant is installed
func Exists() (exists bool) {
	var err error

	// check if vagrant is installed
	if _, err = exec.LookPath("vagrant"); err == nil {

		// initilize Vagrant incase it hasn't been; there is a chance that Vagrant has
		// never been used meaning there won't be a .vagrant.d folder, so we initialize
		// vagrant just to ensure it's ready to be used with nanobox by running any
		// vagrant command (in this case "vagrant -v").
		if b, err := exec.Command("vagrant", "-v").CombinedOutput(); err != nil {
			config.Fatal("[util/vagrant/vagrant] exec.Command() failed", string(b))
		}

		// read setup_version to determine if the version of vagrant is too old
		// (< 1.5.0) and needs to be migrated
		b, err := ioutil.ReadFile(filepath.Join(config.Home, ".vagrant.d", "setup_version"))
		if err != nil {
			config.Fatal("[util/vagrant/vagrant] ioutil.ReadFile() failed", err.Error())
		}

		// convert the []byte value from the file into a float 'version'
		version, err := strconv.ParseFloat(string(b), 64)
		if err != nil {
			config.Fatal("[util/vagrant/vagrant] strconv.ParseFloat() failed", err.Error())
		}

		// if the current version of vagrant is less than a 'working version' (1.5)
		// give instructions on how to update
		if version < 1.5 {
			fmt.Println(`
Nanobox has detected that you are using an old version of Vagrant (<1.5). Before
you can continue you'll need to run "vagrant update" and follow the instructions
to update Vagrant.
			`)

			// exit here to allow for upgrade
			os.Exit(0)
		}

		// if all checks pass
		exists = true
	}

	return
}

// run runs a vagrant command
func run(cmd *exec.Cmd) error {

	//
	handleCMDout(cmd)

	// start the command; we need this to 'fire and forget' so that we can manually
	// capture and modify the commands output above
	if err := cmd.Start(); err != nil {
		return err
	}

	// wait for the command to complete/fail and exit, giving us a chance to scan
	// the output
	if err := cmd.Wait(); err != nil {
		return err
	}

	return nil
}

// runInContext runs a command in the context of a Vagrantfile (from the same dir)
func runInContext(cmd *exec.Cmd) error {

	// run the command from ~/.nanobox/apps/<config.App>. Running the command from
	// the directory that contains the Vagratfile ensure that the command can
	// atleast run (especially in cases like 'create' where a VM hadn't been created
	// yet, and a UUID isn't available)
	setContext(config.AppDir)

	//
	handleCMDout(cmd)

	// start the command; we need this to 'fire and forget' so that we can manually
	// capture and modify the commands output above
	if err := cmd.Start(); err != nil {
		return err
	}

	// wait for the command to complete/fail and exit, giving us a chance to scan
	// the output
	if err := cmd.Wait(); err != nil {
		return err
	}

	// switch back to project dir
	setContext(config.CWDir)

	return nil
}

// setContext changes the working directory to the designated context
func setContext(context string) {
	if err := os.Chdir(context); err != nil {
		fmt.Printf("No app found at %s. Exiting...\n", config.AppDir)
		os.Exit(1)
	}
}

func customScanner(data []byte, atEOF bool) (advance int, token []byte, err error) {

	//
	if atEOF && len(data) == 0 {
		return 0, nil, nil
	}

	if i := bytes.IndexByte(data, '\n'); i >= 0 {
		// We have a full newline-terminated line.
		return i + 1, dropCR(data[0:i]), nil
	}

	if i := bytes.IndexByte(data, '\r'); i >= 0 {
		return i + 1, dropCR(data[0:i]), nil
	}

	// If we're at EOF, we have a final, non-terminated line. Return it.
	if atEOF {
		return len(data), dropCR(data), nil
	}

	// Request more data.
	return 0, nil, nil
}

// dropCR drops a terminal \r from the data.
func dropCR(data []byte) []byte {
	if len(data) > 0 && data[len(data)-1] == '\r' {
		return data[0 : len(data)-1]
	}
	return data
}

// handleCMDout
func handleCMDout(cmd *exec.Cmd) {

	// create a stderr pipe that will write any error messages to the log
	stderr, err := cmd.StderrPipe()
	if err != nil {
		Fatal("[util/vagrant/vagrant] cmd.StderrPipe() failed", err.Error())
	}

	// log any command errors to the log
	stderrScanner := bufio.NewScanner(stderr)
	go func() {

		//
		var once sync.Once
		for stderrScanner.Scan() {

			// only display the error message once, but log every error
			once.Do(func() { Error("A vagrant error occured", "") })
			Log.Error(stderrScanner.Text())
		}
	}()

	// create a stdout pipe that will allow for scanning the output line-by-line;
	// if needed a stderr pipe could also be created at some point
	stdout, err := cmd.StdoutPipe()
	if err != nil {
		Fatal("[util/vagrant/vagrant] cmd.StdoutPipe() failed", err.Error())
	}

	// scan the command output intercepting only 'important' lines of vagrant output'
	// and tailoring their message so as to not flood the output.
	// styled according to: http://nanodocs.gopagoda.io/engines/style-guide
	stdoutScanner := bufio.NewScanner(stdout)
	stdoutScanner.Split(customScanner)

	// this is a mapping of all the vagrant output and our output to show instead
	filter := map[string]string{
		"VM not created. Moving on...":                   "Nanobox not yet created, use 'nanobox dev' or 'nanobox run' to create it.",
		"VirtualBox VM is already running.":              "This nanobox is already running",
		"Importing base box 'nanobox/boot2docker'...":    "Importing nanobox base image",
		"Booting VM...":                                  "Booting virtual machine",
		"Configuring and enabling network interfaces...": "Configuring virtual network",
		"Mounting shared folders...":                     fmt.Sprintf("Mounting source code (%s)", config.CWDir),
		"Waiting for nanobox server...":                  "Starting nanobox server",
		"Attempting graceful shutdown of VM...":          "Shutting down virtual machine",
		"Forcing shutdown of VM...":                      "Shutting down virtual machine",
		"Saving VM state and suspending execution...":    "Saving virtual machine",
		// "Resuming suspended VM...":                       "Resuming virtual machine",   // this is output by its command
		// "Destroying VM and associated drives...":         "Destroying virtual machine", // this is output by its command
	}

	var done chan bool
	var next chan string
	progressing := false

	//
	go func() {
		for stdoutScanner.Scan() {

			//
			txt := strings.TrimSpace(stdoutScanner.Text())

			// log all vagrant output (might as well)
			Log.Info(txt)

			//
			switch {

			// show the progress bar if vagrant downloads nanobox/boot2docker
			case strings.Contains(txt, "box: Progress:"):

				progressing = true

				// subMatch[1] - percentage downloaded
				// subMatch[2] - amount/s
				// subMatch[3] - estimated time remaining
				subMatch := regexp.MustCompile(`box: Progress: (\d{1,3})% \(Rate: (.*), Estimated time remaining: (\d*:\d*:\d*)`).FindStringSubmatch(txt)

				// if for some reason we don't get the matches we need just skip that
				// line; this should never happen unless vagrant change their ouput
				if len(subMatch) < 4 {
					continue
				}

				// if for some reason the submatch fails to convert to an int just skip
				// the line; this will likely never happen
				i, err := strconv.Atoi(subMatch[1])
				if err != nil {
					continue
				}

				// show download progress: [*** progress *** 0.0%] 00:00:00 remaining
				fmt.Printf("\r\033[K   [%-41s %s%%] %s (%s remaining)", strings.Repeat("*", int(float64(i)/2.5)), subMatch[1], subMatch[2], subMatch[3])

			// if it's NOT a progress string and we were previously progressing
			case !strings.Contains(txt, "box: Progress:") && progressing:
				fmt.Printf("\r\033[K   [**************************************** 100.0%%] 00:00:00 remaining\n")
				progressing = false

				// fallthrough so we dont miss any messages
				fallthrough

			// filter and print any messages received from vagrant
			case strings.HasPrefix(txt, fmt.Sprintf("==> %v: ", config.Nanofile.Name)):
				subMatch := regexp.MustCompile(`==> \S*:\s(.*)`).FindStringSubmatch(txt)

				// if for some reason the regex failed to pull anything just skip the
				// line; this should never happen unless vagrant changes their output
				if len(subMatch) <= 1 {
					continue
				}

				// if the line is found in our filter print it
				if v, ok := filter[subMatch[1]]; ok {

					// indicate that there the next message has arrived by closing the previous
					// message channel
					if next != nil {
						close(next)
					}

					// wait on done (skipping the first wait)
					if done != nil {
						<-done
					}

					// make a new next
					next = make(chan string)

					// make a new done
					done = make(chan bool)

					// fire up the printer/dotter
					go printMessage(next, done)

					// send the next message
					next <- v
				}
			}
		}

		// close next once the scan is complete
		if next != nil {
			close(next)
		}

		// close done once the scan is complete
		// if done != nil {
		// 	close(done)
		// }
	}()
}

// printMessage takes a message and done channel, reading of the message channel
// and printing the message with a loader
func printMessage(msg chan string, done chan bool) {

	var out string

	for {
		select {

		// read the message
		case msg, ok := <-msg:

			// if !ok that means the channel was closed (another message received) so
			// print the final output, close the spinner and done, and return
			if !ok {
				fmt.Printf("\r\033[K   - %s\n", out)

				// stop the spinner
				done <- true

				close(done)
				return
			}

			// because a final message ("") will come when the channel is closed, store
			// the message here after that happens to have the actual messages
			out = msg

			// fire up the spinner
			go func() {

				spinner := `-\|/`
				i := 0

				for {
					select {

					// spin baby spin
					default:
						fmt.Printf("\r\033[K   %s %s", string(spinner[i%len(spinner)]), out)
						<-time.After(time.Second / 24)
						i++

					// message complete, stop spinner
					case <-done:
						return
					}
				}
			}()
		}
	}
}
<|endoftext|>


<commit_before>package cliutils

import (
	"bytes"
	"github.com/codegangsta/cli"
	"encoding/json"
	"os"
	"strconv"
	"strings"
	"runtime"
	"regexp"
	"github.com/jfrogdev/jfrog-cli-go/utils/cliutils/log"
)

const CliAgent = "jfrog-cli-go"

// CLI base commands constants:
const CmdArtifactory = "rt"
const CmdBintray = "bt"
const CmdMissionControl = "mc"
const CmdXray = "xr"

// Error modes (how should the application behave when the CheckError function is invoked):
type OnError string

const (
	OnErrorPanic OnError = "panic"
	OnErrorReturnError OnError = "return"
)

var onError OnError

func init() {
	onError = OnErrorReturnError
	if os.Getenv("JFROG_CLI_ERROR_HANDLING") == string(OnErrorPanic) {
		onError = OnErrorPanic
	}
}

// Exit codes:
type ExitCode struct {
	Code int
}

var ExitCodeError ExitCode = ExitCode{1}
var ExitCodeWarning ExitCode = ExitCode{2}

func CheckError(err error) error {
	if err != nil {
		if onError == OnErrorPanic {
			panic(err)
		}
	}
	return err
}

func CheckErrorWithMessage(err error, message string) error {
	if err != nil {
		log.Error(message)
		err = CheckError(err)
	}
	return err
}

func ExitOnErr(err error) {
	if err != nil {
		Exit(ExitCodeError, err.Error())
	}
}

func Exit(exitCode ExitCode, msg string) {
	if msg != "" {
		log.Error(msg)
	}
	os.Exit(exitCode.Code)
}

func AddTrailingSlashIfNeeded(url string) string {
	if url != "" && !strings.HasSuffix(url, "/") {
		url += "/"
	}
	return url
}

func IndentJson(jsonStr []byte) string {
	var content bytes.Buffer
	err := json.Indent(&content, jsonStr, "", "  ")
	if err == nil {
		return content.String()
	}
	return string(jsonStr)
}

// Creates a string in the form of ["item-1","item-2","item-3"...] from an input
// in the form of item-1,item-1,item-1...
func BuildListString(listStr string) string {
	if listStr == "" {
		return ""
	}
	split := strings.Split(listStr, ",")
	size := len(split)
	str := "[\""
	for i := 0; i < size; i++ {
		str += split[i]
		if i + 1 < size {
			str += "\",\""
		}
	}
	str += "\"]"
	return str
}

func MapToJson(m map[string]string) string {
	first := true
	json := "{"

	for key := range m {
		val := m[key]
		if val != "" {
			if !first {
				json += ","
			}
			first = false
			if !strings.HasPrefix(val, "[") || !strings.HasSuffix(val, "]") {
				val = "\"" + val + "\""
			}
			json += "\"" + key + "\": " + val
		}
	}
	if first {
		return ""
	}
	json += "}"
	return json
}

func ConfirmAnswer(answer string) bool {
	answer = strings.ToLower(answer)
	return answer == "y" || answer == "yes"
}

func GetLogMsgPrefix(threadId int, dryRun bool) string {
	var strDryRun string
	if dryRun {
		strDryRun = "[Dry run] "
	}
	return "[Thread " + strconv.Itoa(threadId) + "] " + strDryRun
}

func GetVersion() string {
	return "1.7.1"
}

func GetConfigVersion() string {
	return "1"
}

func GetUserHomeDir() string {
	if runtime.GOOS == "windows" {
		home := os.Getenv("HOMEDRIVE") + os.Getenv("HOMEPATH")
		if home == "" {
			home = os.Getenv("USERPROFILE")
		}
		return home
	}
	return os.Getenv("HOME")
}

func ReplaceTildeWithUserHome(path string) string {
	if len(path) > 1 && path[0:1] == "~" {
		return GetUserHomeDir() + path[1:len(path)]
	}
	return path
}

// Get the local root path, from which to start collecting artifacts to be uploaded to Artifactory.
func GetRootPathForUpload(path string, useRegExp bool) string {
	// The first step is to split the local path pattern into sections, by the file seperator.
	seperator := "/"
	sections := strings.Split(path, seperator)
	if len(sections) == 1 {
		seperator = "\\"
		sections = strings.Split(path, seperator)
	}

	// Now we start building the root path, making sure to leave out the sub-directory that includes the pattern.
	rootPath := ""
	for _, section := range sections {
		if section == "" {
			continue
		}
		if useRegExp {
			if strings.Index(section, "(") != -1 {
				break
			}
		} else {
			if strings.Index(section, "*") != -1 {
				break
			}
		}
		if rootPath != "" {
			rootPath += seperator
		}
		if section == "~" {
			rootPath += GetUserHomeDir()
		} else {
			rootPath += section
		}
	}
	if len(sections) > 0 && sections[0] == "" {
		rootPath = seperator + rootPath
	}
	if rootPath == "" {
		return "."
	}
	return rootPath
}

func PrepareLocalPathForUpload(localPath string, useRegExp bool) string {
	if localPath == "./" || localPath == ".\\" {
		return "^.*$"
	}
	if strings.HasPrefix(localPath, "./") {
		localPath = localPath[2:]
	} else if strings.HasPrefix(localPath, ".\\") {
		localPath = localPath[3:]
	}
	if !useRegExp {
		localPath = PathToRegExp(localPath)
	}
	return localPath
}

func TrimPath(path string) string {
	path = strings.Replace(path, "\\", "/", -1)
	path = strings.Replace(path, "//", "/", -1)
	path = strings.Replace(path, "../", "", -1)
	path = strings.Replace(path, "./", "", -1)
	return path
}

func GetBoolFlagValue(c *cli.Context, flagName string, defValue bool) bool {
	if c.IsSet(flagName) {
		return c.Bool(flagName)
	}
	return defValue
}

func GetBoolEnvValue(flagName string, defValue bool) (bool, error) {
	envVarValue := os.Getenv(flagName)
	if envVarValue == "" {
		return defValue, nil
	}
	val, err := strconv.ParseBool(envVarValue)
	err = CheckErrorWithMessage(err, "can't parse environment variable " + flagName)
	return val, err
}

func StringToBool(boolVal string, defaultValue bool) (bool, error) {
	if len(boolVal) > 0 {
		result, err := strconv.ParseBool(boolVal)
		CheckError(err)
		return result, err
	}
	return defaultValue, nil
}

func GetDocumentationMessage() string {
	return "You can read the documentation at https://github.com/jfrogdev/jfrog-cli-go/blob/master/README.md"
}

func PathToRegExp(localPath string) string {
	var SPECIAL_CHARS = []string{".", "+"}
	for _, char := range SPECIAL_CHARS {
		localPath = strings.Replace(localPath, char, "\\" + char, -1)
	}
	var wildcard = ".*"
	localPath = strings.Replace(localPath, "*", wildcard, -1)
	if strings.HasSuffix(localPath, "/") || strings.HasSuffix(localPath, "\\") {
		localPath += wildcard
	}
	localPath = "^" + localPath + "$"
	return localPath
}

// Replaces matched regular expression from sourceString to corresponding {i} at destString.
// For example:
//      regexpString = "1(.*)234" ; sourceString = "1hello234" ; destString = "{1}"
//      returns "hello"
func ReformatRegexp(regexpString, sourceString, destString string) (string, error) {
	r, err := regexp.Compile(regexpString)
	err = CheckError(err)
	if err != nil {
		return "", err
	}

	groups := r.FindStringSubmatch(sourceString)
	size := len(groups)
	target := destString
	if size > 0 {
		for i := 1; i < size; i++ {
			group := strings.Replace(groups[i], "\\", "/", -1)
			target = strings.Replace(target, "{" + strconv.Itoa(i) + "}", group, -1)
		}
	}
	return target, nil
}

func GetTestsFileSeperator() string {
	if runtime.GOOS == "windows" {
		return "\\\\"
	}
	return "/"
}

// Remove all chars from the given string.
func StripChars(str, chr string) string {
	return strings.Map(func(r rune) rune {
		if strings.IndexRune(chr, r) < 0 {
			return r
		}
		return -1
	}, str)
}

func MergeMaps(src map[string]string, dst map[string]string) {
	for k, v := range src {
		dst[k] = v
	}
}

func Bool2Int(b bool) int {
	if b {
		return 1
	}
	return 0
}

func SumTrueValues(boolArr []bool) int {
	counter := 0
	for _, val := range boolArr {
		counter += Bool2Int(val)
	}
	return counter
}

type Credentials interface {
	SetUser(string)
	SetPassword(string)
	GetUser() string
	GetPassword() string
}

type Artifact struct {
	LocalPath  string
	TargetPath string
	Symlink    string
}

<commit_msg>Promote version to 1.8.0<commit_after>package cliutils

import (
	"bytes"
	"github.com/codegangsta/cli"
	"encoding/json"
	"os"
	"strconv"
	"strings"
	"runtime"
	"regexp"
	"github.com/jfrogdev/jfrog-cli-go/utils/cliutils/log"
)

const CliAgent = "jfrog-cli-go"

// CLI base commands constants:
const CmdArtifactory = "rt"
const CmdBintray = "bt"
const CmdMissionControl = "mc"
const CmdXray = "xr"

// Error modes (how should the application behave when the CheckError function is invoked):
type OnError string

const (
	OnErrorPanic OnError = "panic"
	OnErrorReturnError OnError = "return"
)

var onError OnError

func init() {
	onError = OnErrorReturnError
	if os.Getenv("JFROG_CLI_ERROR_HANDLING") == string(OnErrorPanic) {
		onError = OnErrorPanic
	}
}

// Exit codes:
type ExitCode struct {
	Code int
}

var ExitCodeError ExitCode = ExitCode{1}
var ExitCodeWarning ExitCode = ExitCode{2}

func CheckError(err error) error {
	if err != nil {
		if onError == OnErrorPanic {
			panic(err)
		}
	}
	return err
}

func CheckErrorWithMessage(err error, message string) error {
	if err != nil {
		log.Error(message)
		err = CheckError(err)
	}
	return err
}

func ExitOnErr(err error) {
	if err != nil {
		Exit(ExitCodeError, err.Error())
	}
}

func Exit(exitCode ExitCode, msg string) {
	if msg != "" {
		log.Error(msg)
	}
	os.Exit(exitCode.Code)
}

func AddTrailingSlashIfNeeded(url string) string {
	if url != "" && !strings.HasSuffix(url, "/") {
		url += "/"
	}
	return url
}

func IndentJson(jsonStr []byte) string {
	var content bytes.Buffer
	err := json.Indent(&content, jsonStr, "", "  ")
	if err == nil {
		return content.String()
	}
	return string(jsonStr)
}

// Creates a string in the form of ["item-1","item-2","item-3"...] from an input
// in the form of item-1,item-1,item-1...
func BuildListString(listStr string) string {
	if listStr == "" {
		return ""
	}
	split := strings.Split(listStr, ",")
	size := len(split)
	str := "[\""
	for i := 0; i < size; i++ {
		str += split[i]
		if i + 1 < size {
			str += "\",\""
		}
	}
	str += "\"]"
	return str
}

func MapToJson(m map[string]string) string {
	first := true
	json := "{"

	for key := range m {
		val := m[key]
		if val != "" {
			if !first {
				json += ","
			}
			first = false
			if !strings.HasPrefix(val, "[") || !strings.HasSuffix(val, "]") {
				val = "\"" + val + "\""
			}
			json += "\"" + key + "\": " + val
		}
	}
	if first {
		return ""
	}
	json += "}"
	return json
}

func ConfirmAnswer(answer string) bool {
	answer = strings.ToLower(answer)
	return answer == "y" || answer == "yes"
}

func GetLogMsgPrefix(threadId int, dryRun bool) string {
	var strDryRun string
	if dryRun {
		strDryRun = "[Dry run] "
	}
	return "[Thread " + strconv.Itoa(threadId) + "] " + strDryRun
}

func GetVersion() string {
	return "1.8.0"
}

func GetConfigVersion() string {
	return "1"
}

func GetUserHomeDir() string {
	if runtime.GOOS == "windows" {
		home := os.Getenv("HOMEDRIVE") + os.Getenv("HOMEPATH")
		if home == "" {
			home = os.Getenv("USERPROFILE")
		}
		return home
	}
	return os.Getenv("HOME")
}

func ReplaceTildeWithUserHome(path string) string {
	if len(path) > 1 && path[0:1] == "~" {
		return GetUserHomeDir() + path[1:len(path)]
	}
	return path
}

// Get the local root path, from which to start collecting artifacts to be uploaded to Artifactory.
func GetRootPathForUpload(path string, useRegExp bool) string {
	// The first step is to split the local path pattern into sections, by the file seperator.
	seperator := "/"
	sections := strings.Split(path, seperator)
	if len(sections) == 1 {
		seperator = "\\"
		sections = strings.Split(path, seperator)
	}

	// Now we start building the root path, making sure to leave out the sub-directory that includes the pattern.
	rootPath := ""
	for _, section := range sections {
		if section == "" {
			continue
		}
		if useRegExp {
			if strings.Index(section, "(") != -1 {
				break
			}
		} else {
			if strings.Index(section, "*") != -1 {
				break
			}
		}
		if rootPath != "" {
			rootPath += seperator
		}
		if section == "~" {
			rootPath += GetUserHomeDir()
		} else {
			rootPath += section
		}
	}
	if len(sections) > 0 && sections[0] == "" {
		rootPath = seperator + rootPath
	}
	if rootPath == "" {
		return "."
	}
	return rootPath
}

func PrepareLocalPathForUpload(localPath string, useRegExp bool) string {
	if localPath == "./" || localPath == ".\\" {
		return "^.*$"
	}
	if strings.HasPrefix(localPath, "./") {
		localPath = localPath[2:]
	} else if strings.HasPrefix(localPath, ".\\") {
		localPath = localPath[3:]
	}
	if !useRegExp {
		localPath = PathToRegExp(localPath)
	}
	return localPath
}

func TrimPath(path string) string {
	path = strings.Replace(path, "\\", "/", -1)
	path = strings.Replace(path, "//", "/", -1)
	path = strings.Replace(path, "../", "", -1)
	path = strings.Replace(path, "./", "", -1)
	return path
}

func GetBoolFlagValue(c *cli.Context, flagName string, defValue bool) bool {
	if c.IsSet(flagName) {
		return c.Bool(flagName)
	}
	return defValue
}

func GetBoolEnvValue(flagName string, defValue bool) (bool, error) {
	envVarValue := os.Getenv(flagName)
	if envVarValue == "" {
		return defValue, nil
	}
	val, err := strconv.ParseBool(envVarValue)
	err = CheckErrorWithMessage(err, "can't parse environment variable " + flagName)
	return val, err
}

func StringToBool(boolVal string, defaultValue bool) (bool, error) {
	if len(boolVal) > 0 {
		result, err := strconv.ParseBool(boolVal)
		CheckError(err)
		return result, err
	}
	return defaultValue, nil
}

func GetDocumentationMessage() string {
	return "You can read the documentation at https://github.com/jfrogdev/jfrog-cli-go/blob/master/README.md"
}

func PathToRegExp(localPath string) string {
	var SPECIAL_CHARS = []string{".", "+"}
	for _, char := range SPECIAL_CHARS {
		localPath = strings.Replace(localPath, char, "\\" + char, -1)
	}
	var wildcard = ".*"
	localPath = strings.Replace(localPath, "*", wildcard, -1)
	if strings.HasSuffix(localPath, "/") || strings.HasSuffix(localPath, "\\") {
		localPath += wildcard
	}
	localPath = "^" + localPath + "$"
	return localPath
}

// Replaces matched regular expression from sourceString to corresponding {i} at destString.
// For example:
//      regexpString = "1(.*)234" ; sourceString = "1hello234" ; destString = "{1}"
//      returns "hello"
func ReformatRegexp(regexpString, sourceString, destString string) (string, error) {
	r, err := regexp.Compile(regexpString)
	err = CheckError(err)
	if err != nil {
		return "", err
	}

	groups := r.FindStringSubmatch(sourceString)
	size := len(groups)
	target := destString
	if size > 0 {
		for i := 1; i < size; i++ {
			group := strings.Replace(groups[i], "\\", "/", -1)
			target = strings.Replace(target, "{" + strconv.Itoa(i) + "}", group, -1)
		}
	}
	return target, nil
}

func GetTestsFileSeperator() string {
	if runtime.GOOS == "windows" {
		return "\\\\"
	}
	return "/"
}

// Remove all chars from the given string.
func StripChars(str, chr string) string {
	return strings.Map(func(r rune) rune {
		if strings.IndexRune(chr, r) < 0 {
			return r
		}
		return -1
	}, str)
}

func MergeMaps(src map[string]string, dst map[string]string) {
	for k, v := range src {
		dst[k] = v
	}
}

func Bool2Int(b bool) int {
	if b {
		return 1
	}
	return 0
}

func SumTrueValues(boolArr []bool) int {
	counter := 0
	for _, val := range boolArr {
		counter += Bool2Int(val)
	}
	return counter
}

type Credentials interface {
	SetUser(string)
	SetPassword(string)
	GetUser() string
	GetPassword() string
}

type Artifact struct {
	LocalPath  string
	TargetPath string
	Symlink    string
}

<|endoftext|>


<commit_before>// Copyright 2021 Google LLC
//
// 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 gnmit is a single-target gNMI collector implementation that can be
// used as an on-device/fake device implementation. It supports the Subscribe RPC
// using the libraries from openconfig/gnmi.
package gnmit

import (
	"context"
	"fmt"
	"net"
	"sync"
	"time"

	log "github.com/golang/glog"
	"github.com/openconfig/gnmi/cache"
	"github.com/openconfig/gnmi/subscribe"
	"github.com/openconfig/lemming/gnmi/gnmistore"
	"github.com/openconfig/lemming/gnmi/oc"
	"github.com/openconfig/ygot/ygot"
	"github.com/openconfig/ygot/ytypes"
	"golang.org/x/exp/slices"
	"google.golang.org/grpc"
	"google.golang.org/grpc/codes"
	"google.golang.org/grpc/metadata"
	"google.golang.org/grpc/status"

	gpb "github.com/openconfig/gnmi/proto/gnmi"
)

const (
	OpenconfigOrigin = "openconfig"
)

var (
	// metadataUpdatePeriod is the period of time after which the metadata for the collector
	// is updated to the client.
	metadataUpdatePeriod = 30 * time.Second
	// sizeUpdatePeriod is the period of time after which the storage size information for
	// the collector is updated to the client.
	sizeUpdatePeriod = 30 * time.Second
)

// periodic runs the function fn every period.
func periodic(period time.Duration, fn func()) {
	if period == 0 {
		return
	}
	t := time.NewTicker(period)
	defer t.Stop()
	for range t.C {
		fn()
	}
}

// GNMIServer implements the gNMI server interface.
type GNMIServer struct {
	// The subscribe Server implements only Subscribe for gNMI.
	*subscribe.Server
	c *Collector

	stateMu     sync.Mutex
	stateSchema *ytypes.Schema
}

// NewServer returns a new collector server implementation that can be registered on
// an existing gRPC server.
//
// - schema is the specification of the schema if gnmi.Set is used.
// - hostname is the name of the target.
// - sendMeta indicates whether metadata should be sent
func NewServer(ctx context.Context, schema *ytypes.Schema, hostname string, sendMeta bool) (*Collector, *GNMIServer, error) {
	c := &Collector{
		inCh:   make(chan *gpb.SubscribeResponse),
		name:   hostname,
		schema: schema,
	}

	log.V(1).Infof("Starting cache target: %v", hostname)
	c.cache = cache.New([]string{hostname})
	t := c.cache.GetTarget(hostname)

	// Initialize the cache with the input schema root.
	if schema != nil {
		notifs, err := ygot.TogNMINotifications(schema.Root, time.Now().UnixNano(), ygot.GNMINotificationsConfig{
			UsePathElem: true,
		})
		if err != nil {
			return nil, nil, fmt.Errorf("gnmit: %v", err)
		}
		for _, notif := range notifs {
			if notif.Prefix == nil {
				notif.Prefix = &gpb.Path{}
			}
			notif.Prefix.Origin = OpenconfigOrigin
			notif.Prefix.Target = hostname
			c.cache.GnmiUpdate(notif)
		}
	}

	if sendMeta {
		go periodic(metadataUpdatePeriod, c.cache.UpdateMetadata)
		go periodic(sizeUpdatePeriod, c.cache.UpdateSize)
	}
	t.Connect()

	// start our single collector from the input channel.
	go func() {
		for {
			select {
			case msg := <-c.inCh:
				if err := c.handleUpdate(msg); err != nil {
					return
				}
			case <-ctx.Done():
				return
			}
		}
	}()

	subscribeSrv, err := subscribe.NewServer(c.cache)
	if err != nil {
		return nil, nil, fmt.Errorf("could not instantiate gNMI server: %v", err)
	}

	stateSchema, err := oc.Schema()
	if err != nil {
		return nil, nil, err
	}
	if err := SetupSchema(stateSchema); err != nil {
		return nil, nil, err
	}

	gnmiserver := &GNMIServer{
		Server:      subscribeSrv, // use the 'subscribe' implementation.
		c:           c,
		stateSchema: stateSchema,
	}

	c.cache.SetClient(subscribeSrv.Update)

	return c, gnmiserver, nil
}

// New returns a new collector that listens on the specified addr (in the form host:port),
// supporting a single downstream target named hostname. sendMeta controls whether the
// metadata *other* than meta/sync and meta/connected is sent by the collector.
//
// New returns the new collector, the address it is listening on in the form hostname:port
// or any errors encounted whilst setting it up.
func New(ctx context.Context, schema *ytypes.Schema, addr, hostname string, sendMeta bool, opts ...grpc.ServerOption) (*Collector, string, error) {
	c, gnmiserver, err := NewServer(ctx, schema, hostname, sendMeta)
	if err != nil {
		return nil, "", err
	}

	// Start gNMI server.
	srv := grpc.NewServer(opts...)
	gpb.RegisterGNMIServer(srv, gnmiserver)
	// Forward streaming updates to clients.
	// Register listening port and start serving.
	lis, err := net.Listen("tcp", addr)
	if err != nil {
		return nil, "", fmt.Errorf("failed to listen: %v", err)
	}

	go func() {
		if err := srv.Serve(lis); err != nil {
			log.Fatalf("Error while serving gnmi target: %v", err)
		}
	}()
	c.stopFn = func() {
		srv.GracefulStop()
	}
	return c, lis.Addr().String(), nil
}

type populateDefaultser interface {
	PopulateDefaults()
}

// SetupSchema takes in a ygot schema object which it assumes to be
// uninitialized. It initializes and validates it, returning any errors
// encountered.
func SetupSchema(schema *ytypes.Schema) error {
	if !schema.IsValid() {
		return fmt.Errorf("cannot obtain valid schema for GoStructs: %v", schema)
	}

	// Initialize the root with default values.
	schema.Root.(populateDefaultser).PopulateDefaults()
	if err := schema.Validate(); err != nil {
		return fmt.Errorf("default root of input schema fails validation: %v", err)
	}

	return nil
}

// NewSettable returns a new collector that listens on the specified addr (in the form host:port),
// supporting a single downstream target named hostname. sendMeta controls whether the
// metadata *other* than meta/sync and meta/connected is sent by the collector.
//
// New returns the new collector, the address it is listening on in the form hostname:port
// or any errors encounted whilst setting it up.
//
// NewSettable is different from New in that the returned collector is
// schema-aware and supports gNMI Set. Currently it is not possible to change
// the schema of a Collector after it is created.
func NewSettable(ctx context.Context, addr string, hostname string, sendMeta bool, schema *ytypes.Schema, opts ...grpc.ServerOption) (*Collector, string, error) {
	if err := SetupSchema(schema); err != nil {
		return nil, "", err
	}
	collector, addr, err := New(ctx, schema, addr, hostname, sendMeta, opts...)
	if err != nil {
		return nil, "", err
	}
	return collector, addr, nil
}

// Stop halts the running collector.
func (c *Collector) Stop() {
	c.stopFn()
}

// handleUpdate handles an input gNMI SubscribeResponse that is received by
// the target.
func (c *Collector) handleUpdate(resp *gpb.SubscribeResponse) error {
	t := c.cache.GetTarget(c.name)
	switch v := resp.Response.(type) {
	case *gpb.SubscribeResponse_Update:
		return t.GnmiUpdate(v.Update)
	case *gpb.SubscribeResponse_SyncResponse:
		t.Sync()
	case *gpb.SubscribeResponse_Error:
		return fmt.Errorf("error in response: %s", v)
	default:
		return fmt.Errorf("unknown response %T: %s", v, v)
	}
	return nil
}

// Collector is a basic gNMI target that supports only the Subscribe
// RPC, and acts as a cache for exactly one target.
type Collector struct {
	cache *cache.Cache

	smu    sync.Mutex
	schema *ytypes.Schema

	// name is the hostname of the client.
	name string
	// inCh is a channel use to write new SubscribeResponses to the client.
	inCh chan *gpb.SubscribeResponse
	// stopFn is the function used to stop the server.
	stopFn func()
}

// TargetUpdate provides an input gNMI SubscribeResponse to update the
// cache and clients with.
func (c *Collector) TargetUpdate(m *gpb.SubscribeResponse) {
	c.inCh <- m
}

func updateCache(cache *cache.Cache, root ygot.GoStruct, target string, dirtyRoot ygot.GoStruct, origin string, preferShadowPath bool) error {
	n, err := ygot.Diff(root, dirtyRoot, &ygot.DiffPathOpt{PreferShadowPath: preferShadowPath})
	if err != nil {
		return fmt.Errorf("gnmit: error while creating update notification for Set: %v", err)
	}
	n.Timestamp = time.Now().UnixNano()
	n.Prefix = &gpb.Path{Origin: origin, Target: target}
	if n.Prefix.Origin == "" {
		n.Prefix.Origin = OpenconfigOrigin
	}

	// Update cache
	t := cache.GetTarget(target)
	var pathsForDelete []string
	for _, path := range n.Delete {
		p, err := ygot.PathToString(path)
		if err != nil {
			return fmt.Errorf("cannot convert deleted path to string: %v", err)
		}
		pathsForDelete = append(pathsForDelete, p)
	}
	log.V(1).Infof("datastore: updating the following values: %+v", n.Update)
	log.V(1).Infof("datastore: deleting the following paths: %+v", pathsForDelete)
	if err := t.GnmiUpdate(n); err != nil {
		return err
	}
	return nil
}

// set updates the datastore and intended configuration with the SetRequest,
// allowing read-only values to be updated.
//
// update indicates whether to update the cache with the values from the set
// request.
func set(schema *ytypes.Schema, cache *cache.Cache, target string, req *gpb.SetRequest, preferShadowPath bool) error {
	prevRoot, err := ygot.DeepCopy(schema.Root)
	if err != nil {
		return fmt.Errorf("gnmit: failed to ygot.DeepCopy the cached root object: %v", err)
	}

	success := false

	// Rollback function
	defer func() {
		if !success {
			log.V(1).Infof("Rolling back set request: %v", req)
			schema.Root = prevRoot
		}
	}()

	var unmarshalOpts []ytypes.UnmarshalOpt
	if preferShadowPath {
		unmarshalOpts = append(unmarshalOpts, &ytypes.PreferShadowPath{})
	}
	if err := ytypes.UnmarshalSetRequest(schema, req, unmarshalOpts...); err != nil {
		return fmt.Errorf("gnmit: %v", err)
	}

	if err := schema.Validate(); err != nil {
		return fmt.Errorf("gnmit: invalid SetRequest: %v", err)
	}

	success = true

	if err := updateCache(cache, prevRoot, target, schema.Root, req.Prefix.Origin, preferShadowPath); err != nil {
		return err
	}
	return nil
}

// Set is a prototype for a gNMI Set operation.
// TODO(wenbli): Add unit test.
func (s *GNMIServer) Set(ctx context.Context, req *gpb.SetRequest) (*gpb.SetResponse, error) {
	md, ok := metadata.FromIncomingContext(ctx)
	if !ok {
		return nil, status.Errorf(codes.Internal, "incoming gNMI SetRequest must specify GNMIMode via the user agent string.")
	}

	// Use ConfigMode by default so that external users don't need to set metadata.
	gnmiMode := gnmistore.ConfigMode
	switch {
	case slices.Contains(md.Get(gnmistore.GNMIModeMetadataKey), string(gnmistore.ConfigMode)):
		gnmiMode = gnmistore.ConfigMode
	case slices.Contains(md.Get(gnmistore.GNMIModeMetadataKey), string(gnmistore.StateMode)):
		gnmiMode = gnmistore.StateMode
	}

	switch gnmiMode {
	case gnmistore.ConfigMode:
		s.c.smu.Lock()
		defer s.c.smu.Unlock()

		log.V(1).Infof("config datastore service received SetRequest: %v", req)
		if s.c.schema == nil {
			return s.UnimplementedGNMIServer.Set(ctx, req)
		}

		// TODO(wenbli): Reject paths that try to modify read-only values.
		// TODO(wenbli): Question: what to do if there are operational-state values in a container that is specified to be replaced or deleted?
		err := set(s.c.schema, s.c.cache, s.c.name, req, true)

		// TODO(wenbli): Currently the SetResponse is not filled.
		return &gpb.SetResponse{
			Timestamp: time.Now().UnixNano(),
		}, err
	case gnmistore.StateMode:
		s.stateMu.Lock()
		defer s.stateMu.Unlock()

		log.V(1).Infof("operational state datastore service received SetRequest: %v", req)
		if s.stateSchema == nil {
			return s.UnimplementedGNMIServer.Set(ctx, req)
		}
		// TODO(wenbli): Reject values that modify config values. We only allow modifying state in this mode.
		if err := set(s.stateSchema, s.c.cache, s.c.name, req, false); err != nil {
			return &gpb.SetResponse{}, status.Errorf(codes.Aborted, "%v", err)
		}

		// This mode is intended to be used internally, and the SetResponse doesn't matter.
		return &gpb.SetResponse{}, nil
	default:
		return nil, status.Errorf(codes.Internal, "incoming gNMI SetRequest must specify a valid GNMIMode via context metadata: %v", md)
	}
}
<commit_msg>don't require metadata<commit_after>// Copyright 2021 Google LLC
//
// 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 gnmit is a single-target gNMI collector implementation that can be
// used as an on-device/fake device implementation. It supports the Subscribe RPC
// using the libraries from openconfig/gnmi.
package gnmit

import (
	"context"
	"fmt"
	"net"
	"sync"
	"time"

	log "github.com/golang/glog"
	"github.com/openconfig/gnmi/cache"
	"github.com/openconfig/gnmi/subscribe"
	"github.com/openconfig/lemming/gnmi/gnmistore"
	"github.com/openconfig/lemming/gnmi/oc"
	"github.com/openconfig/ygot/ygot"
	"github.com/openconfig/ygot/ytypes"
	"golang.org/x/exp/slices"
	"google.golang.org/grpc"
	"google.golang.org/grpc/codes"
	"google.golang.org/grpc/metadata"
	"google.golang.org/grpc/status"

	gpb "github.com/openconfig/gnmi/proto/gnmi"
)

const (
	OpenconfigOrigin = "openconfig"
)

var (
	// metadataUpdatePeriod is the period of time after which the metadata for the collector
	// is updated to the client.
	metadataUpdatePeriod = 30 * time.Second
	// sizeUpdatePeriod is the period of time after which the storage size information for
	// the collector is updated to the client.
	sizeUpdatePeriod = 30 * time.Second
)

// periodic runs the function fn every period.
func periodic(period time.Duration, fn func()) {
	if period == 0 {
		return
	}
	t := time.NewTicker(period)
	defer t.Stop()
	for range t.C {
		fn()
	}
}

// GNMIServer implements the gNMI server interface.
type GNMIServer struct {
	// The subscribe Server implements only Subscribe for gNMI.
	*subscribe.Server
	c *Collector

	stateMu     sync.Mutex
	stateSchema *ytypes.Schema
}

// NewServer returns a new collector server implementation that can be registered on
// an existing gRPC server.
//
// - schema is the specification of the schema if gnmi.Set is used.
// - hostname is the name of the target.
// - sendMeta indicates whether metadata should be sent
func NewServer(ctx context.Context, schema *ytypes.Schema, hostname string, sendMeta bool) (*Collector, *GNMIServer, error) {
	c := &Collector{
		inCh:   make(chan *gpb.SubscribeResponse),
		name:   hostname,
		schema: schema,
	}

	log.V(1).Infof("Starting cache target: %v", hostname)
	c.cache = cache.New([]string{hostname})
	t := c.cache.GetTarget(hostname)

	// Initialize the cache with the input schema root.
	if schema != nil {
		notifs, err := ygot.TogNMINotifications(schema.Root, time.Now().UnixNano(), ygot.GNMINotificationsConfig{
			UsePathElem: true,
		})
		if err != nil {
			return nil, nil, fmt.Errorf("gnmit: %v", err)
		}
		for _, notif := range notifs {
			if notif.Prefix == nil {
				notif.Prefix = &gpb.Path{}
			}
			notif.Prefix.Origin = OpenconfigOrigin
			notif.Prefix.Target = hostname
			c.cache.GnmiUpdate(notif)
		}
	}

	if sendMeta {
		go periodic(metadataUpdatePeriod, c.cache.UpdateMetadata)
		go periodic(sizeUpdatePeriod, c.cache.UpdateSize)
	}
	t.Connect()

	// start our single collector from the input channel.
	go func() {
		for {
			select {
			case msg := <-c.inCh:
				if err := c.handleUpdate(msg); err != nil {
					return
				}
			case <-ctx.Done():
				return
			}
		}
	}()

	subscribeSrv, err := subscribe.NewServer(c.cache)
	if err != nil {
		return nil, nil, fmt.Errorf("could not instantiate gNMI server: %v", err)
	}

	stateSchema, err := oc.Schema()
	if err != nil {
		return nil, nil, err
	}
	if err := SetupSchema(stateSchema); err != nil {
		return nil, nil, err
	}

	gnmiserver := &GNMIServer{
		Server:      subscribeSrv, // use the 'subscribe' implementation.
		c:           c,
		stateSchema: stateSchema,
	}

	c.cache.SetClient(subscribeSrv.Update)

	return c, gnmiserver, nil
}

// New returns a new collector that listens on the specified addr (in the form host:port),
// supporting a single downstream target named hostname. sendMeta controls whether the
// metadata *other* than meta/sync and meta/connected is sent by the collector.
//
// New returns the new collector, the address it is listening on in the form hostname:port
// or any errors encounted whilst setting it up.
func New(ctx context.Context, schema *ytypes.Schema, addr, hostname string, sendMeta bool, opts ...grpc.ServerOption) (*Collector, string, error) {
	c, gnmiserver, err := NewServer(ctx, schema, hostname, sendMeta)
	if err != nil {
		return nil, "", err
	}

	// Start gNMI server.
	srv := grpc.NewServer(opts...)
	gpb.RegisterGNMIServer(srv, gnmiserver)
	// Forward streaming updates to clients.
	// Register listening port and start serving.
	lis, err := net.Listen("tcp", addr)
	if err != nil {
		return nil, "", fmt.Errorf("failed to listen: %v", err)
	}

	go func() {
		if err := srv.Serve(lis); err != nil {
			log.Fatalf("Error while serving gnmi target: %v", err)
		}
	}()
	c.stopFn = func() {
		srv.GracefulStop()
	}
	return c, lis.Addr().String(), nil
}

type populateDefaultser interface {
	PopulateDefaults()
}

// SetupSchema takes in a ygot schema object which it assumes to be
// uninitialized. It initializes and validates it, returning any errors
// encountered.
func SetupSchema(schema *ytypes.Schema) error {
	if !schema.IsValid() {
		return fmt.Errorf("cannot obtain valid schema for GoStructs: %v", schema)
	}

	// Initialize the root with default values.
	schema.Root.(populateDefaultser).PopulateDefaults()
	if err := schema.Validate(); err != nil {
		return fmt.Errorf("default root of input schema fails validation: %v", err)
	}

	return nil
}

// NewSettable returns a new collector that listens on the specified addr (in the form host:port),
// supporting a single downstream target named hostname. sendMeta controls whether the
// metadata *other* than meta/sync and meta/connected is sent by the collector.
//
// New returns the new collector, the address it is listening on in the form hostname:port
// or any errors encounted whilst setting it up.
//
// NewSettable is different from New in that the returned collector is
// schema-aware and supports gNMI Set. Currently it is not possible to change
// the schema of a Collector after it is created.
func NewSettable(ctx context.Context, addr string, hostname string, sendMeta bool, schema *ytypes.Schema, opts ...grpc.ServerOption) (*Collector, string, error) {
	if err := SetupSchema(schema); err != nil {
		return nil, "", err
	}
	collector, addr, err := New(ctx, schema, addr, hostname, sendMeta, opts...)
	if err != nil {
		return nil, "", err
	}
	return collector, addr, nil
}

// Stop halts the running collector.
func (c *Collector) Stop() {
	c.stopFn()
}

// handleUpdate handles an input gNMI SubscribeResponse that is received by
// the target.
func (c *Collector) handleUpdate(resp *gpb.SubscribeResponse) error {
	t := c.cache.GetTarget(c.name)
	switch v := resp.Response.(type) {
	case *gpb.SubscribeResponse_Update:
		return t.GnmiUpdate(v.Update)
	case *gpb.SubscribeResponse_SyncResponse:
		t.Sync()
	case *gpb.SubscribeResponse_Error:
		return fmt.Errorf("error in response: %s", v)
	default:
		return fmt.Errorf("unknown response %T: %s", v, v)
	}
	return nil
}

// Collector is a basic gNMI target that supports only the Subscribe
// RPC, and acts as a cache for exactly one target.
type Collector struct {
	cache *cache.Cache

	smu    sync.Mutex
	schema *ytypes.Schema

	// name is the hostname of the client.
	name string
	// inCh is a channel use to write new SubscribeResponses to the client.
	inCh chan *gpb.SubscribeResponse
	// stopFn is the function used to stop the server.
	stopFn func()
}

// TargetUpdate provides an input gNMI SubscribeResponse to update the
// cache and clients with.
func (c *Collector) TargetUpdate(m *gpb.SubscribeResponse) {
	c.inCh <- m
}

func updateCache(cache *cache.Cache, root ygot.GoStruct, target string, dirtyRoot ygot.GoStruct, origin string, preferShadowPath bool) error {
	n, err := ygot.Diff(root, dirtyRoot, &ygot.DiffPathOpt{PreferShadowPath: preferShadowPath})
	if err != nil {
		return fmt.Errorf("gnmit: error while creating update notification for Set: %v", err)
	}
	n.Timestamp = time.Now().UnixNano()
	n.Prefix = &gpb.Path{Origin: origin, Target: target}
	if n.Prefix.Origin == "" {
		n.Prefix.Origin = OpenconfigOrigin
	}

	// Update cache
	t := cache.GetTarget(target)
	var pathsForDelete []string
	for _, path := range n.Delete {
		p, err := ygot.PathToString(path)
		if err != nil {
			return fmt.Errorf("cannot convert deleted path to string: %v", err)
		}
		pathsForDelete = append(pathsForDelete, p)
	}
	log.V(1).Infof("datastore: updating the following values: %+v", n.Update)
	log.V(1).Infof("datastore: deleting the following paths: %+v", pathsForDelete)
	if err := t.GnmiUpdate(n); err != nil {
		return err
	}
	return nil
}

// set updates the datastore and intended configuration with the SetRequest,
// allowing read-only values to be updated.
//
// update indicates whether to update the cache with the values from the set
// request.
func set(schema *ytypes.Schema, cache *cache.Cache, target string, req *gpb.SetRequest, preferShadowPath bool) error {
	prevRoot, err := ygot.DeepCopy(schema.Root)
	if err != nil {
		return fmt.Errorf("gnmit: failed to ygot.DeepCopy the cached root object: %v", err)
	}

	success := false

	// Rollback function
	defer func() {
		if !success {
			log.V(1).Infof("Rolling back set request: %v", req)
			schema.Root = prevRoot
		}
	}()

	var unmarshalOpts []ytypes.UnmarshalOpt
	if preferShadowPath {
		unmarshalOpts = append(unmarshalOpts, &ytypes.PreferShadowPath{})
	}
	if err := ytypes.UnmarshalSetRequest(schema, req, unmarshalOpts...); err != nil {
		return fmt.Errorf("gnmit: %v", err)
	}

	if err := schema.Validate(); err != nil {
		return fmt.Errorf("gnmit: invalid SetRequest: %v", err)
	}

	success = true

	if err := updateCache(cache, prevRoot, target, schema.Root, req.Prefix.Origin, preferShadowPath); err != nil {
		return err
	}
	return nil
}

// Set is a prototype for a gNMI Set operation.
// TODO(wenbli): Add unit test.
func (s *GNMIServer) Set(ctx context.Context, req *gpb.SetRequest) (*gpb.SetResponse, error) {
	// Use ConfigMode by default so that external users don't need to set metadata.
	gnmiMode := gnmistore.ConfigMode
	md, ok := metadata.FromIncomingContext(ctx)
	if ok {
		switch {
		case slices.Contains(md.Get(gnmistore.GNMIModeMetadataKey), string(gnmistore.ConfigMode)):
			gnmiMode = gnmistore.ConfigMode
		case slices.Contains(md.Get(gnmistore.GNMIModeMetadataKey), string(gnmistore.StateMode)):
			gnmiMode = gnmistore.StateMode
		}
	}

	switch gnmiMode {
	case gnmistore.ConfigMode:
		s.c.smu.Lock()
		defer s.c.smu.Unlock()

		log.V(1).Infof("config datastore service received SetRequest: %v", req)
		if s.c.schema == nil {
			return s.UnimplementedGNMIServer.Set(ctx, req)
		}

		// TODO(wenbli): Reject paths that try to modify read-only values.
		// TODO(wenbli): Question: what to do if there are operational-state values in a container that is specified to be replaced or deleted?
		err := set(s.c.schema, s.c.cache, s.c.name, req, true)

		// TODO(wenbli): Currently the SetResponse is not filled.
		return &gpb.SetResponse{
			Timestamp: time.Now().UnixNano(),
		}, err
	case gnmistore.StateMode:
		s.stateMu.Lock()
		defer s.stateMu.Unlock()

		log.V(1).Infof("operational state datastore service received SetRequest: %v", req)
		if s.stateSchema == nil {
			return s.UnimplementedGNMIServer.Set(ctx, req)
		}
		// TODO(wenbli): Reject values that modify config values. We only allow modifying state in this mode.
		if err := set(s.stateSchema, s.c.cache, s.c.name, req, false); err != nil {
			return &gpb.SetResponse{}, status.Errorf(codes.Aborted, "%v", err)
		}

		// This mode is intended to be used internally, and the SetResponse doesn't matter.
		return &gpb.SetResponse{}, nil
	default:
		return nil, status.Errorf(codes.Internal, "incoming gNMI SetRequest must specify a valid GNMIMode via context metadata: %v", md)
	}
}
<|endoftext|>


<commit_before>package stripe

import (
	"socialapi/workers/payment/paymenterrors"
	"socialapi/workers/payment/paymentmodels"
	"socialapi/workers/payment/paymentstatus"

	stripeCustomer "github.com/stripe/stripe-go/customer"
)

func SubscribeForGroup(token, groupId, email, planTitle, planInterval string) error {
	return subscribe(token, email, planTitle, planInterval, groupId, paymentmodels.GroupCustomer)
}

func Subscribe(token, accId, email, planTitle, planInterval string) error {
	return subscribe(token, email, planTitle, planInterval, accId, paymentmodels.AccountCustomer)
}

func subscribe(token, email, planTitle, planInterval, accId string, cType string) error {
	plan, err := FindPlanByTitleAndInterval(planTitle, planInterval)
	if err != nil {
		return err
	}

	customer, err := paymentmodels.NewCustomer().ByOldIdAndType(accId, paymentmodels.AccountCustomer)
	if err != nil && err != paymenterrors.ErrCustomerNotFound {
		return err
	}

	var subscription *paymentmodels.Subscription
	if customer != nil {
		subscription, err = customer.FindActiveSubscription()
		if err != nil && err != paymenterrors.ErrCustomerNotSubscribedToAnyPlans {
			return err
		}
	}

	status, err := paymentstatus.Check(customer, err, plan)
	if err != nil {
		Log.Error("Subscribing to %s failed for user: %s", plan.Title, customer.Username)
		return err
	}

	switch status {
	case paymentstatus.NewSub, paymentstatus.ExpiredSub:
		err = handleNewSubscription(token, accId, email, cType, plan)
	case paymentstatus.ExistingUserHasNoSub:
		err = handleUserNoSub(customer, token, plan)
	case paymentstatus.AlreadySubscribedToPlan:
		err = paymenterrors.ErrCustomerAlreadySubscribedToPlan
	case paymentstatus.DowngradeToFreePlan:
		err = handleCancel(customer)
	case paymentstatus.DowngradeToNonFreePlan:
		err = handleDowngrade(subscription, customer, plan)
	case paymentstatus.UpgradeFromExistingSub:
		err = handleUpgrade(subscription, customer, plan)
	default:
		Log.Error("User: %s fell into default case when subscribing: %s", customer.Username, plan.Title)
		// user should never come here
	}

	return err
}

func handleNewSubscription(token, id, email, cType string, plan *paymentmodels.Plan) error {
	var customer *paymentmodels.Customer

	switch cType {
	case paymentmodels.AccountCustomer:
		var err error
		if customer, err = CreateCustomer(token, id, email); err != nil {
			return err
		}
	case paymentmodels.GroupCustomer:
		var err error
		if customer, err = CreateCustomerForGroup(token, id, email); err != nil {
			return err
		}
	}

	if _, err := CreateSubscription(customer, plan); err != nil {
		deleteCustomer(customer)
		return err
	}

	return nil
}

func handleUserNoSub(customer *paymentmodels.Customer, token string, plan *paymentmodels.Plan) error {
	if token != "" {
		if err := UpdateCreditCard(customer.OldId, token); err != nil {
			return err
		}
	}

	_, err := CreateSubscription(customer, plan)
	return err
}

func handleCancel(customer *paymentmodels.Customer) error {
	subscriptions, err := customer.FindSubscriptions()
	if err != nil {
		return err
	}

	for _, sub := range subscriptions {
		if err = CancelSubscription(customer, &sub); err != nil {
			Log.Error(err.Error())
		}
	}

	removeCreditCardHelper(customer)

	return nil
}

func deleteCustomer(customer *paymentmodels.Customer) {
	removeCreditCardHelper(customer)

	if err := stripeCustomer.Del(customer.ProviderCustomerId); err != nil {
		Log.Error("Error deleting customer from Stripe: %v", err)
	}

	if err := customer.Delete(); err != nil {
		Log.Error("Removing cc failed for customer: %v. %v", customer.Id, err)
	}
}

func removeCreditCardHelper(customer *paymentmodels.Customer) {
	ccErr := RemoveCreditCard(customer) // outer error is more important
	if ccErr != nil {
		Log.Error("Removing cc failed for customer: %v. %v", customer.Id, ccErr)
	}
}
<commit_msg>payment: fix bug with customer type not being passed<commit_after>package stripe

import (
	"socialapi/workers/payment/paymenterrors"
	"socialapi/workers/payment/paymentmodels"
	"socialapi/workers/payment/paymentstatus"

	stripeCustomer "github.com/stripe/stripe-go/customer"
)

func SubscribeForGroup(token, groupId, email, planTitle, planInterval string) error {
	return subscribe(token, email, planTitle, planInterval, groupId, paymentmodels.GroupCustomer)
}

func Subscribe(token, accId, email, planTitle, planInterval string) error {
	return subscribe(token, email, planTitle, planInterval, accId, paymentmodels.AccountCustomer)
}

func subscribe(token, email, planTitle, planInterval, id string, cType string) error {
	plan, err := FindPlanByTitleAndInterval(planTitle, planInterval)
	if err != nil {
		return err
	}

	customer, err := paymentmodels.NewCustomer().ByOldIdAndType(id, cType)
	if err != nil && err != paymenterrors.ErrCustomerNotFound {
		return err
	}

	var subscription *paymentmodels.Subscription
	if customer != nil {
		subscription, err = customer.FindActiveSubscription()
		if err != nil && err != paymenterrors.ErrCustomerNotSubscribedToAnyPlans {
			return err
		}
	}

	status, err := paymentstatus.Check(customer, err, plan)
	if err != nil {
		Log.Error("Subscribing to %s failed for user: %s", plan.Title, customer.Username)
		return err
	}

	switch status {
	case paymentstatus.NewSub, paymentstatus.ExpiredSub:
		err = handleNewSubscription(token, email, id, cType, plan)
	case paymentstatus.ExistingUserHasNoSub:
		err = handleUserNoSub(customer, token, plan)
	case paymentstatus.AlreadySubscribedToPlan:
		err = paymenterrors.ErrCustomerAlreadySubscribedToPlan
	case paymentstatus.DowngradeToFreePlan:
		err = handleCancel(customer)
	case paymentstatus.DowngradeToNonFreePlan:
		err = handleDowngrade(subscription, customer, plan)
	case paymentstatus.UpgradeFromExistingSub:
		err = handleUpgrade(subscription, customer, plan)
	default:
		Log.Error("User: %s fell into default case when subscribing: %s", customer.Username, plan.Title)
		// user should never come here
	}

	return err
}

func handleNewSubscription(token, email, id, cType string, plan *paymentmodels.Plan) error {
	var customer *paymentmodels.Customer

	switch cType {
	case paymentmodels.AccountCustomer:
		var err error
		if customer, err = CreateCustomer(token, id, email); err != nil {
			return err
		}
	case paymentmodels.GroupCustomer:
		var err error
		if customer, err = CreateCustomerForGroup(token, id, email); err != nil {
			return err
		}
	}

	if _, err := CreateSubscription(customer, plan); err != nil {
		deleteCustomer(customer)
		return err
	}

	return nil
}

func handleUserNoSub(customer *paymentmodels.Customer, token string, plan *paymentmodels.Plan) error {
	if token != "" {
		if err := UpdateCreditCard(customer.OldId, token); err != nil {
			return err
		}
	}

	_, err := CreateSubscription(customer, plan)
	return err
}

func handleCancel(customer *paymentmodels.Customer) error {
	subscriptions, err := customer.FindSubscriptions()
	if err != nil {
		return err
	}

	for _, sub := range subscriptions {
		if err = CancelSubscription(customer, &sub); err != nil {
			Log.Error(err.Error())
		}
	}

	removeCreditCardHelper(customer)

	return nil
}

func deleteCustomer(customer *paymentmodels.Customer) {
	removeCreditCardHelper(customer)

	if err := stripeCustomer.Del(customer.ProviderCustomerId); err != nil {
		Log.Error("Error deleting customer from Stripe: %v", err)
	}

	if err := customer.Delete(); err != nil {
		Log.Error("Removing cc failed for customer: %v. %v", customer.Id, err)
	}
}

func removeCreditCardHelper(customer *paymentmodels.Customer) {
	ccErr := RemoveCreditCard(customer) // outer error is more important
	if ccErr != nil {
		Log.Error("Removing cc failed for customer: %v. %v", customer.Id, ccErr)
	}
}
<|endoftext|>


<commit_before>package vcs

import (
	"runtime"
	"testing"
	"time"
)

// libgit2 seems to crash (in cgo with a segfault) during concurrent access to a
// repository.
//
// stack trace at https://gist.github.com/sqs/ce913ce35599d3377c11
func TestRepository_LibGit2_Concurrency(t *testing.T) {
	p := runtime.GOMAXPROCS(0)
	if p == 1 {
		t.Skip("no point in testing concurrency with GOMAXPROCS=1")
	}

	origRepo := makeGitRepositoryLibGit2(t,
		"echo hello > a",
		"git add a",
		"GIT_COMMITTER_NAME=a GIT_COMMITTER_EMAIL=a@a.com GIT_COMMITTER_DATE=2006-01-02T15:04:05Z git commit --allow-empty -m foo --author='a <a@a.com>' --date 2006-01-02T15:04:05Z",
		"GIT_COMMITTER_NAME=a GIT_COMMITTER_EMAIL=a@a.com GIT_COMMITTER_DATE=2006-01-02T15:04:05Z git commit --allow-empty -m bar --author='a <a@a.com>' --date 2006-01-02T17:04:05Z",
	)

	n := 10
	start := time.Now()
	duration := 5 * time.Second

	for i := 0; i < n; i++ {
		go func() {
			for {
				if time.Since(start) > duration {
					return
				}
				repo, err := OpenGitRepositoryLibGit2(origRepo.dir)
				if err != nil {
					t.Error(err)
					return
				}

				commitID, err := repo.ResolveRevision("master")
				if err != nil {
					t.Error(err)
					return
				}

				_, err = repo.GetCommit(commitID)
				if err != nil {
					t.Error(err)
					return
				}

				_, err = repo.CommitLog(commitID)
				if err != nil {
					t.Error(err)
					return
				}

				fs, err := repo.FileSystem(commitID)
				if err != nil {
					t.Error(err)
					return
				}

				entries, err := fs.ReadDir(".")
				if err != nil {
					t.Error(err)
					return
				}

				if len(entries) != 1 {
					t.Errorf("got entries %v, want 1 entry", entries)
				}

				for _, entry := range entries {
					f, err := fs.Open(entry.Name())
					if err != nil {
						t.Error(err)
						return
					}
					f.Close()
				}
			}
		}()
	}

	time.Sleep(duration + 500*time.Millisecond)
}
<commit_msg>clarify<commit_after>package vcs

import (
	"runtime"
	"testing"
	"time"
)

// This test checks that you have compiled libgit2 with -DTHREADSAFE=ON. If you
// didn't, then this test will crash in cgo.
//
// Example stack trace: https://gist.github.com/sqs/ce913ce35599d3377c11
func TestRepository_LibGit2_Concurrency(t *testing.T) {
	p := runtime.GOMAXPROCS(0)
	if p == 1 {
		t.Skip("no point in testing concurrency with GOMAXPROCS=1")
	}

	origRepo := makeGitRepositoryLibGit2(t,
		"echo hello > a",
		"git add a",
		"GIT_COMMITTER_NAME=a GIT_COMMITTER_EMAIL=a@a.com GIT_COMMITTER_DATE=2006-01-02T15:04:05Z git commit --allow-empty -m foo --author='a <a@a.com>' --date 2006-01-02T15:04:05Z",
		"GIT_COMMITTER_NAME=a GIT_COMMITTER_EMAIL=a@a.com GIT_COMMITTER_DATE=2006-01-02T15:04:05Z git commit --allow-empty -m bar --author='a <a@a.com>' --date 2006-01-02T17:04:05Z",
	)

	n := 10
	start := time.Now()
	duration := 5 * time.Second

	for i := 0; i < n; i++ {
		go func() {
			for {
				if time.Since(start) > duration {
					return
				}
				repo, err := OpenGitRepositoryLibGit2(origRepo.dir)
				if err != nil {
					t.Error(err)
					return
				}

				commitID, err := repo.ResolveRevision("master")
				if err != nil {
					t.Error(err)
					return
				}

				_, err = repo.GetCommit(commitID)
				if err != nil {
					t.Error(err)
					return
				}

				_, err = repo.CommitLog(commitID)
				if err != nil {
					t.Error(err)
					return
				}

				fs, err := repo.FileSystem(commitID)
				if err != nil {
					t.Error(err)
					return
				}

				entries, err := fs.ReadDir(".")
				if err != nil {
					t.Error(err)
					return
				}

				if len(entries) != 1 {
					t.Errorf("got entries %v, want 1 entry", entries)
				}

				for _, entry := range entries {
					f, err := fs.Open(entry.Name())
					if err != nil {
						t.Error(err)
						return
					}
					f.Close()
				}
			}
		}()
	}

	time.Sleep(duration + 500*time.Millisecond)
}
<|endoftext|>


<commit_before>package video

import "io"
import "github.com/32bitkid/bitreader"
import "errors"
import "image"

var ErrUnsupportedVideoStream_ISO_IEC_11172_2 = errors.New("unsupported video stream ISO/IEC 11172-2")

type FrameProvider interface {
	Next() (image.Image, error)
}

func NewFrameProvider(source io.Reader) FrameProvider {
	return &frameProvider{
		NewVideoSequence(bitreader.NewBitReader(source)),
	}
}

type frameProvider struct {
	VideoSequence
}

func (self *frameProvider) Next() (image.Image, error) {

	// Align to next start code
	err := next_start_code(self)
	if err != nil {
		panic(err)
	}

	// Read sequence_header
	err = self.sequence_header()
	if err != nil {
		panic(err)
	}

	// peek for sequence_extension
	val, err := self.Peek32(32)
	if err != nil {
		panic(err)
	}

	if StartCode(val) == ExtensionStartCode {

		err := self.sequence_extension()

		for {
			err = extension_and_user_data(0, self)
			if err != nil {
				panic("extension_and_user_data")
			}

			for {
				nextbits, err := self.Peek32(32)
				if err != nil {
					panic("Peek32")
				}

				if StartCode(nextbits) == GroupStartCode {
					err = self.group_of_pictures_header()
					if err != nil {
						panic("group_of_pictures_header")
					}
					err = extension_and_user_data(1, self)
					if err != nil {
						panic("extension_and_user_data")
					}
				}

				err = self.picture_header()
				if err != nil {
					panic("picture_header")
				}
				err = self.picture_coding_extension()
				if err != nil {
					panic("picture_coding_extension")
				}
				err = extension_and_user_data(2, self)
				if err != nil {
					panic("extension_and_user_data")
				}

				{
					frame, err := self.picture_data()
					if err != nil {
						panic(err)
					}
					if true {
						return frame, nil
					}
				}

				nextbits, err = self.Peek32(32)
				if err != nil {
					panic("peeking")
				}

				if StartCode(nextbits) != PictureStartCode &&
					StartCode(nextbits) != GroupStartCode {
					break
				}
			}

			panic("not implemented: frame_provider")

			nextbits, err := self.Peek32(32)
			if err != nil {
				panic("Peek32")
			}

			if StartCode(nextbits) == SequenceEndStartCode {
				break
			}

		}

		err = self.Trash(32)

		return nil, err
	} else {
		// Stream is MPEG-1 Video
		return nil, ErrUnsupportedVideoStream_ISO_IEC_11172_2
	}

}
<commit_msg>consolidate some of the error handling in frame_provider.<commit_after>package video

import "io"
import "github.com/32bitkid/bitreader"
import "errors"
import "image"

var ErrUnsupportedVideoStream_ISO_IEC_11172_2 = errors.New("unsupported video stream ISO/IEC 11172-2")

type FrameProvider interface {
	Next() (image.Image, error)
}

func NewFrameProvider(source io.Reader) FrameProvider {
	return &frameProvider{
		NewVideoSequence(bitreader.NewBitReader(source)),
	}
}

type frameProvider struct {
	VideoSequence
}

func (self *frameProvider) Next() (image.Image, error) {

	// align to next start code
	if err := next_start_code(self); err != nil {
		panic(err)
	}

	// read sequence_header
	if err := self.sequence_header(); err != nil {
		panic(err)
	}

	// peek for sequence_extension
	if val, err := self.Peek32(32); err != nil {
		panic(err)
	} else if StartCode(val) == ExtensionStartCode {

		if err := self.sequence_extension(); err != nil {
			panic(err)
		}

		for {

			if err := extension_and_user_data(0, self); err != nil {
				panic("extension_and_user_data: " + err.Error())
			}

			for {
				if nextbits, err := self.Peek32(32); err != nil {
					panic("Peek32")
				} else if StartCode(nextbits) == GroupStartCode {
					if err := self.group_of_pictures_header(); err != nil {
						panic("group_of_pictures_header: " + err.Error())
					}
					if err := extension_and_user_data(1, self); err != nil {
						panic("extension_and_user_data:" + err.Error())
					}
				}

				if err := self.picture_header(); err != nil {
					panic("picture_header: " + err.Error())
				}

				if err := self.picture_coding_extension(); err != nil {
					panic("picture_coding_extension: " + err.Error())
				}

				if err := extension_and_user_data(2, self); err != nil {
					panic("extension_and_user_data: " + err.Error())
				}

				{
					frame, err := self.picture_data()
					if err != nil {
						panic(err)
					}
					if true {
						return frame, nil
					}
				}

				if nextbits, err := self.Peek32(32); err != nil {
					panic("peeking: " + err.Error())
				} else if StartCode(nextbits) != PictureStartCode && StartCode(nextbits) != GroupStartCode {
					break
				}
			}

			panic("not implemented: frame_provider")

			if nextbits, err := self.Peek32(32); err != nil {
				panic("Peek32")
			} else if StartCode(nextbits) == SequenceEndStartCode {
				break
			}

		}

		return nil, self.Trash(32)
	} else {
		// Stream is MPEG-1 Video
		return nil, ErrUnsupportedVideoStream_ISO_IEC_11172_2
	}

}
<|endoftext|>


<commit_before>// Common tool initialization.
// import only from package main.
package common

import (
	"flag"
	"net"
	"time"

	"github.com/golang/glog"
	metrics "github.com/rcrowley/go-metrics"
)

const SAMPLE_PERIOD = time.Minute

// Runs commonly-used initialization metrics.
func Init() {
	flag.Parse()
	defer glog.Flush()
	flag.VisitAll(func(f *flag.Flag) {
		glog.Infof("Flags: --%s=%v", f.Name, f.Value)
	})
}

// Runs normal Init functions as well as tracking runtime metrics.
// Sets up Graphite push for go-metrics' DefaultRegistry. Users of
// both InitWithMetrics and metrics.DefaultRegistry will not need to
// run metrics.Graphite(metrics.DefaultRegistry, ...) separately.
func InitWithMetrics(appName, graphiteServer string) {
	Init()

	addr, _ := net.ResolveTCPAddr("tcp", graphiteServer)

	// Runtime metrics.
	metrics.RegisterRuntimeMemStats(metrics.DefaultRegistry)
	go metrics.CaptureRuntimeMemStats(metrics.DefaultRegistry, SAMPLE_PERIOD)
	go metrics.Graphite(metrics.DefaultRegistry, SAMPLE_PERIOD, appName, addr)

	// Uptime.
	uptimeGuage := metrics.GetOrRegisterGaugeFloat64("uptime", metrics.DefaultRegistry)
	go func() {
		startTime := time.Now()
		uptimeGuage.Update(0)
		for _ = range time.Tick(SAMPLE_PERIOD) {
			uptimeGuage.Update(time.Now().Sub(startTime).Seconds())
		}
	}()
}
<commit_msg>Use time's Since() function.<commit_after>// Common tool initialization.
// import only from package main.
package common

import (
	"flag"
	"net"
	"time"

	"github.com/golang/glog"
	metrics "github.com/rcrowley/go-metrics"
)

const SAMPLE_PERIOD = time.Minute

// Runs commonly-used initialization metrics.
func Init() {
	flag.Parse()
	defer glog.Flush()
	flag.VisitAll(func(f *flag.Flag) {
		glog.Infof("Flags: --%s=%v", f.Name, f.Value)
	})
}

// Runs normal Init functions as well as tracking runtime metrics.
// Sets up Graphite push for go-metrics' DefaultRegistry. Users of
// both InitWithMetrics and metrics.DefaultRegistry will not need to
// run metrics.Graphite(metrics.DefaultRegistry, ...) separately.
func InitWithMetrics(appName, graphiteServer string) {
	Init()

	addr, _ := net.ResolveTCPAddr("tcp", graphiteServer)

	// Runtime metrics.
	metrics.RegisterRuntimeMemStats(metrics.DefaultRegistry)
	go metrics.CaptureRuntimeMemStats(metrics.DefaultRegistry, SAMPLE_PERIOD)
	go metrics.Graphite(metrics.DefaultRegistry, SAMPLE_PERIOD, appName, addr)

	// Uptime.
	uptimeGuage := metrics.GetOrRegisterGaugeFloat64("uptime", metrics.DefaultRegistry)
	go func() {
		startTime := time.Now()
		uptimeGuage.Update(0)
		for _ = range time.Tick(SAMPLE_PERIOD) {
			uptimeGuage.Update(time.Since(startTime).Seconds())
		}
	}()
}
<|endoftext|>


<commit_before>package topology

import (
	"errors"
	"math/rand"
	"strings"

	"github.com/chrislusf/seaweedfs/go/glog"
	"github.com/chrislusf/seaweedfs/go/storage"
)

type NodeId string
type Node interface {
	Id() NodeId
	String() string
	FreeSpace() int
	ReserveOneVolume(r int) (*DataNode, error)
	UpAdjustMaxVolumeCountDelta(maxVolumeCountDelta int)
	UpAdjustVolumeCountDelta(volumeCountDelta int)
	UpAdjustActiveVolumeCountDelta(activeVolumeCountDelta int)
	UpAdjustMaxVolumeId(vid storage.VolumeId)

	GetVolumeCount() int
	GetActiveVolumeCount() int
	GetMaxVolumeCount() int
	GetMaxVolumeId() storage.VolumeId
	SetParent(Node)
	LinkChildNode(node Node)
	UnlinkChildNode(nodeId NodeId)
	CollectDeadNodeAndFullVolumes(freshThreshHold int64, volumeSizeLimit uint64)

	IsDataNode() bool
	IsRack() bool
	IsDataCenter() bool
	Children() map[NodeId]Node
	Parent() Node

	GetValue() interface{} //get reference to the topology,dc,rack,datanode
}
type NodeImpl struct {
	id                NodeId
	volumeCount       int
	activeVolumeCount int
	maxVolumeCount    int
	parent            Node
	children          map[NodeId]Node
	maxVolumeId       storage.VolumeId

	//for rack, data center, topology
	nodeType string
	value    interface{}
}

// the first node must satisfy filterFirstNodeFn(), the rest nodes must have one free slot
func (n *NodeImpl) RandomlyPickNodes(numberOfNodes int, filterFirstNodeFn func(dn Node) error) (firstNode Node, restNodes []Node, err error) {
	candidates := make([]Node, 0, len(n.children))
	var errs []string
	for _, node := range n.children {
		if err := filterFirstNodeFn(node); err == nil {
			candidates = append(candidates, node)
		} else {
			errs = append(errs, string(node.Id())+":"+err.Error())
		}
	}
	if len(candidates) == 0 {
		return nil, nil, errors.New("No matching data node found! \n" + strings.Join(errs, "\n"))
	}
	firstNode = candidates[rand.Intn(len(candidates))]
	glog.V(2).Infoln(n.Id(), "picked main node:", firstNode.Id())

	restNodes = make([]Node, numberOfNodes-1)
	candidates = candidates[:0]
	for _, node := range n.children {
		if node.Id() == firstNode.Id() {
			continue
		}
		if node.FreeSpace() <= 0 {
			continue
		}
		glog.V(2).Infoln("select rest node candidate:", node.Id())
		candidates = append(candidates, node)
	}
	glog.V(2).Infoln(n.Id(), "picking", numberOfNodes-1, "from rest", len(candidates), "node candidates")
	ret := len(restNodes) == 0
	for k, node := range candidates {
		if k < len(restNodes) {
			restNodes[k] = node
			if k == len(restNodes)-1 {
				ret = true
			}
		} else {
			r := rand.Intn(k + 1)
			if r < len(restNodes) {
				restNodes[r] = node
			}
		}
	}
	if !ret {
		glog.V(2).Infoln(n.Id(), "failed to pick", numberOfNodes-1, "from rest", len(candidates), "node candidates")
		err = errors.New("Not enough data node found!")
	}
	return
}

func (n *NodeImpl) IsDataNode() bool {
	return n.nodeType == "DataNode"
}
func (n *NodeImpl) IsRack() bool {
	return n.nodeType == "Rack"
}
func (n *NodeImpl) IsDataCenter() bool {
	return n.nodeType == "DataCenter"
}
func (n *NodeImpl) String() string {
	if n.parent != nil {
		return n.parent.String() + ":" + string(n.id)
	}
	return string(n.id)
}
func (n *NodeImpl) Id() NodeId {
	return n.id
}
func (n *NodeImpl) FreeSpace() int {
	return n.maxVolumeCount - n.volumeCount
}
func (n *NodeImpl) SetParent(node Node) {
	n.parent = node
}
func (n *NodeImpl) Children() map[NodeId]Node {
	return n.children
}
func (n *NodeImpl) Parent() Node {
	return n.parent
}
func (n *NodeImpl) GetValue() interface{} {
	return n.value
}
func (n *NodeImpl) ReserveOneVolume(r int) (assignedNode *DataNode, err error) {
	for _, node := range n.children {
		freeSpace := node.FreeSpace()
		// fmt.Println("r =", r, ", node =", node, ", freeSpace =", freeSpace)
		if freeSpace <= 0 {
			continue
		}
		if r >= freeSpace {
			r -= freeSpace
		} else {
			if node.IsDataNode() && node.FreeSpace() > 0 {
				// fmt.Println("vid =", vid, " assigned to node =", node, ", freeSpace =", node.FreeSpace())
				return node.(*DataNode), nil
			}
			assignedNode, err = node.ReserveOneVolume(r)
			if err != nil {
				return
			}
		}
	}
	return
}

func (n *NodeImpl) UpAdjustMaxVolumeCountDelta(maxVolumeCountDelta int) { //can be negative
	n.maxVolumeCount += maxVolumeCountDelta
	if n.parent != nil {
		n.parent.UpAdjustMaxVolumeCountDelta(maxVolumeCountDelta)
	}
}
func (n *NodeImpl) UpAdjustVolumeCountDelta(volumeCountDelta int) { //can be negative
	n.volumeCount += volumeCountDelta
	if n.parent != nil {
		n.parent.UpAdjustVolumeCountDelta(volumeCountDelta)
	}
}
func (n *NodeImpl) UpAdjustActiveVolumeCountDelta(activeVolumeCountDelta int) { //can be negative
	n.activeVolumeCount += activeVolumeCountDelta
	if n.parent != nil {
		n.parent.UpAdjustActiveVolumeCountDelta(activeVolumeCountDelta)
	}
}
func (n *NodeImpl) UpAdjustMaxVolumeId(vid storage.VolumeId) { //can be negative
	if n.maxVolumeId < vid {
		n.maxVolumeId = vid
		if n.parent != nil {
			n.parent.UpAdjustMaxVolumeId(vid)
		}
	}
}
func (n *NodeImpl) GetMaxVolumeId() storage.VolumeId {
	return n.maxVolumeId
}
func (n *NodeImpl) GetVolumeCount() int {
	return n.volumeCount
}
func (n *NodeImpl) GetActiveVolumeCount() int {
	return n.activeVolumeCount
}
func (n *NodeImpl) GetMaxVolumeCount() int {
	return n.maxVolumeCount
}

func (n *NodeImpl) LinkChildNode(node Node) {
	if n.children[node.Id()] == nil {
		n.children[node.Id()] = node
		n.UpAdjustMaxVolumeCountDelta(node.GetMaxVolumeCount())
		n.UpAdjustMaxVolumeId(node.GetMaxVolumeId())
		n.UpAdjustVolumeCountDelta(node.GetVolumeCount())
		n.UpAdjustActiveVolumeCountDelta(node.GetActiveVolumeCount())
		node.SetParent(n)
		glog.V(0).Infoln(n, "adds child", node.Id())
	}
}

func (n *NodeImpl) UnlinkChildNode(nodeId NodeId) {
	node := n.children[nodeId]
	node.SetParent(nil)
	if node != nil {
		delete(n.children, node.Id())
		n.UpAdjustVolumeCountDelta(-node.GetVolumeCount())
		n.UpAdjustActiveVolumeCountDelta(-node.GetActiveVolumeCount())
		n.UpAdjustMaxVolumeCountDelta(-node.GetMaxVolumeCount())
		glog.V(0).Infoln(n, "removes", node, "volumeCount =", n.activeVolumeCount)
	}
}

func (n *NodeImpl) CollectDeadNodeAndFullVolumes(freshThreshHold int64, volumeSizeLimit uint64) {
	if n.IsRack() {
		for _, c := range n.Children() {
			dn := c.(*DataNode) //can not cast n to DataNode
			if dn.LastSeen < freshThreshHold {
				if !dn.Dead {
					dn.Dead = true
					n.GetTopology().chanDeadDataNodes <- dn
				}
			}
			for _, v := range dn.volumes {
				if uint64(v.Size) >= volumeSizeLimit {
					//fmt.Println("volume",v.Id,"size",v.Size,">",volumeSizeLimit)
					n.GetTopology().chanFullVolumes <- v
				}
			}
		}
	} else {
		for _, c := range n.Children() {
			c.CollectDeadNodeAndFullVolumes(freshThreshHold, volumeSizeLimit)
		}
	}
}

func (n *NodeImpl) GetTopology() *Topology {
	var p Node
	p = n
	for p.Parent() != nil {
		p = p.Parent()
	}
	return p.GetValue().(*Topology)
}
<commit_msg>[MOD] fix a bug.if node is nil will get an panic.<commit_after>package topology

import (
	"errors"
	"math/rand"
	"strings"

	"github.com/chrislusf/seaweedfs/go/glog"
	"github.com/chrislusf/seaweedfs/go/storage"
)

type NodeId string
type Node interface {
	Id() NodeId
	String() string
	FreeSpace() int
	ReserveOneVolume(r int) (*DataNode, error)
	UpAdjustMaxVolumeCountDelta(maxVolumeCountDelta int)
	UpAdjustVolumeCountDelta(volumeCountDelta int)
	UpAdjustActiveVolumeCountDelta(activeVolumeCountDelta int)
	UpAdjustMaxVolumeId(vid storage.VolumeId)

	GetVolumeCount() int
	GetActiveVolumeCount() int
	GetMaxVolumeCount() int
	GetMaxVolumeId() storage.VolumeId
	SetParent(Node)
	LinkChildNode(node Node)
	UnlinkChildNode(nodeId NodeId)
	CollectDeadNodeAndFullVolumes(freshThreshHold int64, volumeSizeLimit uint64)

	IsDataNode() bool
	IsRack() bool
	IsDataCenter() bool
	Children() map[NodeId]Node
	Parent() Node

	GetValue() interface{} //get reference to the topology,dc,rack,datanode
}
type NodeImpl struct {
	id                NodeId
	volumeCount       int
	activeVolumeCount int
	maxVolumeCount    int
	parent            Node
	children          map[NodeId]Node
	maxVolumeId       storage.VolumeId

	//for rack, data center, topology
	nodeType string
	value    interface{}
}

// the first node must satisfy filterFirstNodeFn(), the rest nodes must have one free slot
func (n *NodeImpl) RandomlyPickNodes(numberOfNodes int, filterFirstNodeFn func(dn Node) error) (firstNode Node, restNodes []Node, err error) {
	candidates := make([]Node, 0, len(n.children))
	var errs []string
	for _, node := range n.children {
		if err := filterFirstNodeFn(node); err == nil {
			candidates = append(candidates, node)
		} else {
			errs = append(errs, string(node.Id())+":"+err.Error())
		}
	}
	if len(candidates) == 0 {
		return nil, nil, errors.New("No matching data node found! \n" + strings.Join(errs, "\n"))
	}
	firstNode = candidates[rand.Intn(len(candidates))]
	glog.V(2).Infoln(n.Id(), "picked main node:", firstNode.Id())

	restNodes = make([]Node, numberOfNodes-1)
	candidates = candidates[:0]
	for _, node := range n.children {
		if node.Id() == firstNode.Id() {
			continue
		}
		if node.FreeSpace() <= 0 {
			continue
		}
		glog.V(2).Infoln("select rest node candidate:", node.Id())
		candidates = append(candidates, node)
	}
	glog.V(2).Infoln(n.Id(), "picking", numberOfNodes-1, "from rest", len(candidates), "node candidates")
	ret := len(restNodes) == 0
	for k, node := range candidates {
		if k < len(restNodes) {
			restNodes[k] = node
			if k == len(restNodes)-1 {
				ret = true
			}
		} else {
			r := rand.Intn(k + 1)
			if r < len(restNodes) {
				restNodes[r] = node
			}
		}
	}
	if !ret {
		glog.V(2).Infoln(n.Id(), "failed to pick", numberOfNodes-1, "from rest", len(candidates), "node candidates")
		err = errors.New("Not enough data node found!")
	}
	return
}

func (n *NodeImpl) IsDataNode() bool {
	return n.nodeType == "DataNode"
}
func (n *NodeImpl) IsRack() bool {
	return n.nodeType == "Rack"
}
func (n *NodeImpl) IsDataCenter() bool {
	return n.nodeType == "DataCenter"
}
func (n *NodeImpl) String() string {
	if n.parent != nil {
		return n.parent.String() + ":" + string(n.id)
	}
	return string(n.id)
}
func (n *NodeImpl) Id() NodeId {
	return n.id
}
func (n *NodeImpl) FreeSpace() int {
	return n.maxVolumeCount - n.volumeCount
}
func (n *NodeImpl) SetParent(node Node) {
	n.parent = node
}
func (n *NodeImpl) Children() map[NodeId]Node {
	return n.children
}
func (n *NodeImpl) Parent() Node {
	return n.parent
}
func (n *NodeImpl) GetValue() interface{} {
	return n.value
}
func (n *NodeImpl) ReserveOneVolume(r int) (assignedNode *DataNode, err error) {
	for _, node := range n.children {
		freeSpace := node.FreeSpace()
		// fmt.Println("r =", r, ", node =", node, ", freeSpace =", freeSpace)
		if freeSpace <= 0 {
			continue
		}
		if r >= freeSpace {
			r -= freeSpace
		} else {
			if node.IsDataNode() && node.FreeSpace() > 0 {
				// fmt.Println("vid =", vid, " assigned to node =", node, ", freeSpace =", node.FreeSpace())
				return node.(*DataNode), nil
			}
			assignedNode, err = node.ReserveOneVolume(r)
			if err != nil {
				return
			}
		}
	}
	return
}

func (n *NodeImpl) UpAdjustMaxVolumeCountDelta(maxVolumeCountDelta int) { //can be negative
	n.maxVolumeCount += maxVolumeCountDelta
	if n.parent != nil {
		n.parent.UpAdjustMaxVolumeCountDelta(maxVolumeCountDelta)
	}
}
func (n *NodeImpl) UpAdjustVolumeCountDelta(volumeCountDelta int) { //can be negative
	n.volumeCount += volumeCountDelta
	if n.parent != nil {
		n.parent.UpAdjustVolumeCountDelta(volumeCountDelta)
	}
}
func (n *NodeImpl) UpAdjustActiveVolumeCountDelta(activeVolumeCountDelta int) { //can be negative
	n.activeVolumeCount += activeVolumeCountDelta
	if n.parent != nil {
		n.parent.UpAdjustActiveVolumeCountDelta(activeVolumeCountDelta)
	}
}
func (n *NodeImpl) UpAdjustMaxVolumeId(vid storage.VolumeId) { //can be negative
	if n.maxVolumeId < vid {
		n.maxVolumeId = vid
		if n.parent != nil {
			n.parent.UpAdjustMaxVolumeId(vid)
		}
	}
}
func (n *NodeImpl) GetMaxVolumeId() storage.VolumeId {
	return n.maxVolumeId
}
func (n *NodeImpl) GetVolumeCount() int {
	return n.volumeCount
}
func (n *NodeImpl) GetActiveVolumeCount() int {
	return n.activeVolumeCount
}
func (n *NodeImpl) GetMaxVolumeCount() int {
	return n.maxVolumeCount
}

func (n *NodeImpl) LinkChildNode(node Node) {
	if n.children[node.Id()] == nil {
		n.children[node.Id()] = node
		n.UpAdjustMaxVolumeCountDelta(node.GetMaxVolumeCount())
		n.UpAdjustMaxVolumeId(node.GetMaxVolumeId())
		n.UpAdjustVolumeCountDelta(node.GetVolumeCount())
		n.UpAdjustActiveVolumeCountDelta(node.GetActiveVolumeCount())
		node.SetParent(n)
		glog.V(0).Infoln(n, "adds child", node.Id())
	}
}

func (n *NodeImpl) UnlinkChildNode(nodeId NodeId) {
	node := n.children[nodeId]
	if node != nil {
		node.SetParent(nil)
		delete(n.children, node.Id())
		n.UpAdjustVolumeCountDelta(-node.GetVolumeCount())
		n.UpAdjustActiveVolumeCountDelta(-node.GetActiveVolumeCount())
		n.UpAdjustMaxVolumeCountDelta(-node.GetMaxVolumeCount())
		glog.V(0).Infoln(n, "removes", node, "volumeCount =", n.activeVolumeCount)
	}
}

func (n *NodeImpl) CollectDeadNodeAndFullVolumes(freshThreshHold int64, volumeSizeLimit uint64) {
	if n.IsRack() {
		for _, c := range n.Children() {
			dn := c.(*DataNode) //can not cast n to DataNode
			if dn.LastSeen < freshThreshHold {
				if !dn.Dead {
					dn.Dead = true
					n.GetTopology().chanDeadDataNodes <- dn
				}
			}
			for _, v := range dn.volumes {
				if uint64(v.Size) >= volumeSizeLimit {
					//fmt.Println("volume",v.Id,"size",v.Size,">",volumeSizeLimit)
					n.GetTopology().chanFullVolumes <- v
				}
			}
		}
	} else {
		for _, c := range n.Children() {
			c.CollectDeadNodeAndFullVolumes(freshThreshHold, volumeSizeLimit)
		}
	}
}

func (n *NodeImpl) GetTopology() *Topology {
	var p Node
	p = n
	for p.Parent() != nil {
		p = p.Parent()
	}
	return p.GetValue().(*Topology)
}
<|endoftext|>


<commit_before>package graph

import (
	"testing"
)

func getTestGraph() *Graph {
	g := NewGraph()

	a := NewNode("A")
	b := NewNode("B")
	c := NewNode("C")

	g.Add(a)
	g.Add(b)
	g.Add(c)

	g.AddArc(a, b)
	g.AddArc(a, c)
	g.AddArc(b, c)
	g.AddArc(c, b)

	return g
}

func TestCreate(t *testing.T) {
	getTestGraph()
}

func TestPrint(t *testing.T) {
	t.Log(getTestGraph().String())
}

func TestAdjacent(t *testing.T) {
	g := getTestGraph()

	if !g.Adjacent(g.Get("A"), g.Get("B")) {
		t.Error("A should be adjacent to B, but isn't")
	}

	if g.Adjacent(g.Get("C"), g.Get("A")) {
		t.Error("C shouldn't be adjacent to A, but it is")
	}
}

func TestRemove(t *testing.T) {
	g := getTestGraph()
	g.Remove(g.Get("B"))

	if !g.Adjacent(g.Get("A"), g.Get("C")) &&
		g.Get("B") == nil {
		t.Fail()
	}
}

func TestAddArc(t *testing.T) {
	g := getTestGraph()
	g.AddArc(g.Get("B"), g.Get("A"))

	if !g.Adjacent(g.Get("B"), g.Get("A")) {
		t.Fail()
	}
}

func TestRemoveArc(t *testing.T) {
	g := getTestGraph()
	g.RemoveArc(g.Get("A"), g.Get("B"))

	if g.Adjacent(g.Get("A"), g.Get("B")) {
		t.Fail()
	}
}

func BenchmarkAdd(b *testing.B) {
	g := NewGraph()
	b.ResetTimer()

	for i := 0; i < b.N; i++ {
		g.Add(NewNode(string(i)))
	}
}

func BenchmarkAddArc(b *testing.B) {
	g := NewGraph()

	for i := 0; i < b.N; i++ {
		g.Add(NewNode(string(i)))
	}

	b.ResetTimer()

	for i := 0; i < b.N; i++ {
		for j := 0; i < b.N; i++ {
			g.AddArc(g.Get(string(i)), g.Get(string(j)))
		}
	}
}

func BenchmarkRemoveArc(b *testing.B) {
	g := NewGraph()

	for i := 0; i < b.N; i++ {
		g.Add(NewNode(string(i)))
	}

	for i := 0; i < b.N; i++ {
		for j := 0; i < b.N; i++ {
			g.AddArc(g.Get(string(i)), g.Get(string(j)))
		}
	}

	b.ResetTimer()

	for i := 0; i < b.N; i++ {
		for j := 0; i < b.N; i++ {
			g.RemoveArc(g.Get(string(i)), g.Get(string(j)))
		}
	}
}

func BenchmarkRemove(b *testing.B) {
	g := NewGraph()

	for i := 0; i < b.N; i++ {
		g.Add(NewNode(string(i)))
	}

	b.ResetTimer()

	for i := 0; i < b.N; i++ {
		g.Remove(g.Get(string(i)))
	}
}
<commit_msg>removes benchmarks for adding and removing arcs<commit_after>package graph

import (
	"testing"
)

func getTestGraph() *Graph {
	g := NewGraph()

	a := NewNode("A")
	b := NewNode("B")
	c := NewNode("C")

	g.Add(a)
	g.Add(b)
	g.Add(c)

	g.AddArc(a, b)
	g.AddArc(a, c)
	g.AddArc(b, c)
	g.AddArc(c, b)

	return g
}

func TestCreate(t *testing.T) {
	getTestGraph()
}

func TestPrint(t *testing.T) {
	t.Log(getTestGraph().String())
}

func TestAdjacent(t *testing.T) {
	g := getTestGraph()

	if !g.Adjacent(g.Get("A"), g.Get("B")) {
		t.Error("A should be adjacent to B, but isn't")
	}

	if g.Adjacent(g.Get("C"), g.Get("A")) {
		t.Error("C shouldn't be adjacent to A, but it is")
	}
}

func TestRemove(t *testing.T) {
	g := getTestGraph()
	g.Remove(g.Get("B"))

	if !g.Adjacent(g.Get("A"), g.Get("C")) &&
		g.Get("B") == nil {
		t.Fail()
	}
}

func TestAddArc(t *testing.T) {
	g := getTestGraph()
	g.AddArc(g.Get("B"), g.Get("A"))

	if !g.Adjacent(g.Get("B"), g.Get("A")) {
		t.Fail()
	}
}

func TestRemoveArc(t *testing.T) {
	g := getTestGraph()
	g.RemoveArc(g.Get("A"), g.Get("B"))

	if g.Adjacent(g.Get("A"), g.Get("B")) {
		t.Fail()
	}
}

func BenchmarkAdd(b *testing.B) {
	g := NewGraph()
	b.ResetTimer()

	for i := 0; i < b.N; i++ {
		g.Add(NewNode(string(i)))
	}
}

func BenchmarkRemove(b *testing.B) {
	g := NewGraph()

	for i := 0; i < b.N; i++ {
		g.Add(NewNode(string(i)))
	}

	b.ResetTimer()

	for i := 0; i < b.N; i++ {
		g.Remove(g.Get(string(i)))
	}
}

func BenchmarkAdjacent(b *testing.B) {

}
<|endoftext|>