<commit_before>/*
    MP3Cat is a fast command line utility for concatenating MP3 files
    without re-encoding. It supports both constant bit rate (CBR) and
    variable bit rate (VBR) files.
*/
package main

import (
    "fmt"
    "io"
    "os"
    "path"
    "path/filepath"
    "runtime"
    "golang.org/x/crypto/ssh/terminal"
    "github.com/dmulholl/mp3lib"
    "github.com/dmulholl/janus-go/janus"
)

const version = "4.0.0"

var helptext = fmt.Sprintf(`
Usage: %s [FLAGS] [OPTIONS] [ARGUMENTS]

  This tool concatenates MP3 files without re-encoding. It can join constant
  bit-rate (CBR) files, variable bit-rate (VBR) files, or a mixture of both.

  If a set of input CBR files share the same bit-rate, the output file will
  also be CBR; if the input files have different bit-rates, the output file
  will be VBR.

  Files to be merged can be specified as a list of filenames:

    $ mp3cat one.mp3 two.mp3 three.mp3

  Alternatively, an entire directory of .mp3 files can be merged:

    $ mp3cat --dir /path/to/directory

Arguments:
  [files]                 List of files to merge.

Options:
  -c, --copy-meta <n>     Copy the ID3 metadata tag from the n-th input file.
  -d, --dir <path>        Directory of files to merge.
  -i, --interlace <path>  Interlace a spacer file between each input file.
  -o, --out <path>        Output filepath. Defaults to 'output.mp3'.

Flags:
  -f, --force             Overwrite an existing output file.
  -h, --help              Display this help text and exit.
  -q, --quiet             Run in quiet mode.
  -v, --version           Display the application's version number and exit.
`, filepath.Base(os.Args[0]))

func main() {

    // Parse the command line arguments.
    parser := janus.NewParser()
    parser.Helptext = helptext
    parser.Version = version
    parser.NewFlag("force f")
    parser.NewFlag("quiet q")
    parser.NewFlag("debug")
    parser.NewString("out o", "output.mp3")
    parser.NewString("dir d")
    parser.NewString("interlace i")
    parser.NewInt("copy-meta c")
    parser.Parse()

    // Make sure we have a list of files to merge.
    var files []string
    if parser.Found("dir") {
        pattern := "*.[Mm][Pp]3"
        globs, err := filepath.Glob(path.Join(parser.GetString("dir"), pattern))
        if err != nil {
            fmt.Fprintln(os.Stderr, err)
            os.Exit(1)
        }
        if globs == nil || len(globs) == 0 {
            fmt.Fprintln(os.Stderr, "Error: no files found.")
            os.Exit(1)
        }
        files = globs
    } else if parser.HasArgs() {
        files = parser.GetArgs()
    } else {
        fmt.Fprintln(os.Stderr, "Error: you must specify files to merge.")
        os.Exit(1)
    }

    // Are we copying the ID3 tag from the n-th input file?
    var tagpath string
    if parser.Found("copy-meta") {
        tagindex := parser.GetInt("copy-meta") - 1
        if tagindex < 0 || tagindex > (len(files) - 1) {
            fmt.Fprintln(os.Stderr, "Error: --copy-meta argument is invalid.")
            os.Exit(1)
        }
        tagpath = files[tagindex]
    }

    // Are we interlacing a spacer file?
    if parser.Found("interlace") {
        files = interlace(files, parser.GetString("interlace"))
    }

    // Make sure all the files in the list actually exist.
    validateFiles(files)

    // Set debug mode if the user supplied a --debug flag.
    if parser.GetFlag("debug") {
        mp3lib.DebugMode = true
    }

    // Merge the input files.
    merge(
        parser.GetString("out"),
        tagpath,
        files,
        parser.GetFlag("force"),
        parser.GetFlag("quiet"))
}

// Check that all the files in the list exist.
func validateFiles(files []string) {
    for _, file := range files {
        if _, err := os.Stat(file); err != nil {
            fmt.Fprintf(
                os.Stderr,
                "Error: the file '%v' does not exist.\n", file)
            os.Exit(1)
        }
    }
}

// Interlace a spacer file between each file in the list.
func interlace(files []string, spacer string) []string {
    var interlaced []string
    for _, file := range files {
        interlaced = append(interlaced, file)
        interlaced = append(interlaced, spacer)
    }
    return interlaced[:len(interlaced) - 1]
}

// Create a new file at the specified output path containing the merged
// contents of the list of input files.
func merge(outpath, tagpath string, inpaths []string, force, quiet bool) {

    var totalFrames uint32
    var totalBytes uint32
    var totalFiles int
    var firstBitRate int
    var isVBR bool

    // Only overwrite an existing file if the --force flag has been used.
    if _, err := os.Stat(outpath); err == nil {
        if !force {
            fmt.Fprintf(
                os.Stderr,
                "Error: the file '%v' already exists.\n", outpath)
            os.Exit(1)
        }
    }

    // If the list of input files includes the output file we'll end up in an
    // infinite loop.
    for _, filepath := range inpaths {
        if filepath == outpath {
            fmt.Fprintln(
                os.Stderr,
                "Error: the list of input files includes the output file.")
            os.Exit(1)
        }
    }

    // Create the output file.
    outfile, err := os.Create(outpath)
    if err != nil {
        fmt.Fprintln(os.Stderr, err)
        os.Exit(1)
    }

    if !quiet {
        printLine()
    }

    // Loop over the input files and append their MP3 frames to the output file.
    for _, inpath := range inpaths {
        if !quiet {
            fmt.Println("+", inpath)
        }

        infile, err := os.Open(inpath)
        if err != nil {
            fmt.Fprintln(os.Stderr, err)
            os.Exit(1)
        }

        isFirstFrame := true

        for {
            // Read the next frame from the input file.
            frame := mp3lib.NextFrame(infile)
            if frame == nil {
                break
            }

            // Skip the first frame if it's a VBR header.
            if isFirstFrame {
                isFirstFrame = false
                if mp3lib.IsXingHeader(frame) || mp3lib.IsVbriHeader(frame) {
                    continue
                }
            }

            // If we detect more than one bitrate we'll need to add a VBR
            // header to the output file.
            if firstBitRate == 0 {
                firstBitRate = frame.BitRate
            } else if frame.BitRate != firstBitRate {
                isVBR = true
            }

            // Write the frame to the output file.
            _, err := outfile.Write(frame.RawBytes)
            if err != nil {
                fmt.Fprintln(os.Stderr, err)
                os.Exit(1)
            }

            totalFrames += 1
            totalBytes += uint32(len(frame.RawBytes))
        }

        infile.Close()
        totalFiles += 1
    }

    outfile.Close()
    if !quiet {
        printLine()
    }

    // If we detected multiple bitrates, prepend a VBR header to the file.
    if isVBR {
        if !quiet {
            fmt.Println("• Multiple bitrates detected. Adding VBR header.")
        }
        addXingHeader(outpath, totalFrames, totalBytes)
    }

    // Copy the ID3v2 tag from the n-th input file if requested. Order of
    // operations is important here. The ID3 tag must be the first item in
    // the file - in particular, it must come *before* any VBR header.
    if tagpath != "" {
        if !quiet {
            fmt.Printf("• Copying ID3 tag from: %s\n", tagpath)
        }
        addID3v2Tag(outpath, tagpath)
    }

    // Print a count of the number of files merged.
    if !quiet {
        fmt.Printf("• %v files merged.\n", totalFiles)
        printLine()
    }
}

// Prepend an Xing VBR header to the specified MP3 file.
func addXingHeader(filepath string, totalFrames, totalBytes uint32) {

    outputFile, err := os.Create(filepath + ".mp3cat.tmp")
    if err != nil {
        fmt.Fprintln(os.Stderr, err)
        os.Exit(1)
    }

    inputFile, err := os.Open(filepath)
    if err != nil {
        fmt.Fprintln(os.Stderr, err)
        os.Exit(1)
    }

    xingHeader := mp3lib.NewXingHeader(totalFrames, totalBytes)

    _, err = outputFile.Write(xingHeader.RawBytes)
    if err != nil {
        fmt.Fprintln(os.Stderr, err)
        os.Exit(1)
    }

    _, err = io.Copy(outputFile, inputFile)
    if err != nil {
        fmt.Fprintln(os.Stderr, err)
        os.Exit(1)
    }

    outputFile.Close()
    inputFile.Close()

    err = os.Remove(filepath)
    if err != nil {
        fmt.Fprintln(os.Stderr, err)
        os.Exit(1)
    }

    err = os.Rename(filepath + ".mp3cat.tmp", filepath)
    if err != nil {
        fmt.Fprintln(os.Stderr, err)
        os.Exit(1)
    }
}

// Prepend an ID3v2 tag to the MP3 file at mp3Path, copying from tagPath.
func addID3v2Tag(mp3Path, tagPath string) {

    tagFile, err := os.Open(tagPath)
    if err != nil {
        fmt.Fprintln(os.Stderr, err)
        os.Exit(1)
    }

    id3tag := mp3lib.NextID3v2Tag(tagFile)
    tagFile.Close()

    if id3tag != nil {
        outputFile, err := os.Create(mp3Path + ".mp3cat.tmp")
        if err != nil {
            fmt.Fprintln(os.Stderr, err)
            os.Exit(1)
        }

        inputFile, err := os.Open(mp3Path)
        if err != nil {
            fmt.Fprintln(os.Stderr, err)
            os.Exit(1)
        }

        _, err = outputFile.Write(id3tag.RawBytes)
        if err != nil {
            fmt.Fprintln(os.Stderr, err)
            os.Exit(1)
        }

        _, err = io.Copy(outputFile, inputFile)
        if err != nil {
            fmt.Fprintln(os.Stderr, err)
            os.Exit(1)
        }

        outputFile.Close()
        inputFile.Close()

        err = os.Remove(mp3Path)
        if err != nil {
            fmt.Fprintln(os.Stderr, err)
            os.Exit(1)
        }

        err = os.Rename(mp3Path + ".mp3cat.tmp", mp3Path)
        if err != nil {
            fmt.Fprintln(os.Stderr, err)
            os.Exit(1)
        }
    }
}

// Print a line to stdout if we're running in a terminal.
func printLine() {
    if terminal.IsTerminal(int(os.Stdout.Fd())) {
        width, _, err := terminal.GetSize(int(os.Stdout.Fd()))
        if err == nil {
            if runtime.GOOS == "windows" {
                for i := 0; i < width; i++ {
                    fmt.Print("-")
                }
                fmt.Println()
            } else {
                fmt.Print("\u001B[90m")
                for i := 0; i < width; i++ {
                    fmt.Print("─")
                }
                fmt.Println("\u001B[0m")
            }
        }
    }
}
<commit_msg>Housekeeping<commit_after>/*
    MP3Cat is a fast command line utility for concatenating MP3 files
    without re-encoding. It supports both constant bit rate (CBR) and
    variable bit rate (VBR) files.
*/
package main

import (
    "fmt"
    "io"
    "os"
    "path"
    "path/filepath"
    "runtime"
    "golang.org/x/crypto/ssh/terminal"
    "github.com/dmulholl/mp3lib"
    "github.com/dmulholl/janus-go/janus"
)

const version = "4.0.1"

var helptext = fmt.Sprintf(`
Usage: %s [FLAGS] [OPTIONS] [ARGUMENTS]

  This tool concatenates MP3 files without re-encoding. It can join constant
  bit-rate (CBR) files, variable bit-rate (VBR) files, or a mixture of both.

  If a set of input CBR files share the same bit-rate, the output file will
  also be CBR; if the input files have different bit-rates, the output file
  will be VBR.

  Files to be merged can be specified as a list of filenames:

    $ mp3cat one.mp3 two.mp3 three.mp3

  Alternatively, an entire directory of .mp3 files can be merged:

    $ mp3cat --dir /path/to/directory

Arguments:
  [files]                 List of files to merge.

Options:
  -c, --copy-meta <n>     Copy the ID3 metadata tag from the n-th input file.
  -d, --dir <path>        Directory of files to merge.
  -i, --interlace <path>  Interlace a spacer file between each input file.
  -o, --out <path>        Output filepath. Defaults to 'output.mp3'.

Flags:
  -f, --force             Overwrite an existing output file.
  -h, --help              Display this help text and exit.
  -q, --quiet             Run in quiet mode. Only output error messages.
  -v, --version           Display the application's version number and exit.
`, filepath.Base(os.Args[0]))

func main() {

    // Parse the command line arguments.
    parser := janus.NewParser()
    parser.Helptext = helptext
    parser.Version = version
    parser.NewFlag("force f")
    parser.NewFlag("quiet q")
    parser.NewFlag("debug")
    parser.NewString("out o", "output.mp3")
    parser.NewString("dir d")
    parser.NewString("interlace i")
    parser.NewInt("copy-meta c")
    parser.Parse()

    // Make sure we have a list of files to merge.
    var files []string
    if parser.Found("dir") {
        pattern := "*.[Mm][Pp]3"
        globs, err := filepath.Glob(path.Join(parser.GetString("dir"), pattern))
        if err != nil {
            fmt.Fprintln(os.Stderr, err)
            os.Exit(1)
        }
        if globs == nil || len(globs) == 0 {
            fmt.Fprintln(os.Stderr, "Error: no files found.")
            os.Exit(1)
        }
        files = globs
    } else if parser.HasArgs() {
        files = parser.GetArgs()
    } else {
        fmt.Fprintln(os.Stderr, "Error: you must specify files to merge.")
        os.Exit(1)
    }

    // Are we copying the ID3 tag from the n-th input file?
    var tagpath string
    if parser.Found("copy-meta") {
        tagindex := parser.GetInt("copy-meta") - 1
        if tagindex < 0 || tagindex > (len(files) - 1) {
            fmt.Fprintln(os.Stderr, "Error: --copy-meta argument is invalid.")
            os.Exit(1)
        }
        tagpath = files[tagindex]
    }

    // Are we interlacing a spacer file?
    if parser.Found("interlace") {
        files = interlace(files, parser.GetString("interlace"))
    }

    // Make sure all the files in the list actually exist.
    validateFiles(files)

    // Set debug mode if the user supplied a --debug flag.
    if parser.GetFlag("debug") {
        mp3lib.DebugMode = true
    }

    // Merge the input files.
    merge(
        parser.GetString("out"),
        tagpath,
        files,
        parser.GetFlag("force"),
        parser.GetFlag("quiet"))
}

// Check that all the files in the list exist.
func validateFiles(files []string) {
    for _, file := range files {
        if _, err := os.Stat(file); err != nil {
            fmt.Fprintf(
                os.Stderr,
                "Error: the file '%v' does not exist.\n", file)
            os.Exit(1)
        }
    }
}

// Interlace a spacer file between each file in the list.
func interlace(files []string, spacer string) []string {
    var interlaced []string
    for _, file := range files {
        interlaced = append(interlaced, file)
        interlaced = append(interlaced, spacer)
    }
    return interlaced[:len(interlaced) - 1]
}

// Create a new file at the specified output path containing the merged
// contents of the list of input files.
func merge(outpath, tagpath string, inpaths []string, force, quiet bool) {

    var totalFrames uint32
    var totalBytes uint32
    var totalFiles int
    var firstBitRate int
    var isVBR bool

    // Only overwrite an existing file if the --force flag has been used.
    if _, err := os.Stat(outpath); err == nil {
        if !force {
            fmt.Fprintf(
                os.Stderr,
                "Error: the file '%v' already exists.\n", outpath)
            os.Exit(1)
        }
    }

    // If the list of input files includes the output file we'll end up in an
    // infinite loop.
    for _, filepath := range inpaths {
        if filepath == outpath {
            fmt.Fprintln(
                os.Stderr,
                "Error: the list of input files includes the output file.")
            os.Exit(1)
        }
    }

    // Create the output file.
    outfile, err := os.Create(outpath)
    if err != nil {
        fmt.Fprintln(os.Stderr, err)
        os.Exit(1)
    }

    if !quiet {
        printLine()
    }

    // Loop over the input files and append their MP3 frames to the output file.
    for _, inpath := range inpaths {
        if !quiet {
            fmt.Println("+", inpath)
        }

        infile, err := os.Open(inpath)
        if err != nil {
            fmt.Fprintln(os.Stderr, err)
            os.Exit(1)
        }

        isFirstFrame := true

        for {
            // Read the next frame from the input file.
            frame := mp3lib.NextFrame(infile)
            if frame == nil {
                break
            }

            // Skip the first frame if it's a VBR header.
            if isFirstFrame {
                isFirstFrame = false
                if mp3lib.IsXingHeader(frame) || mp3lib.IsVbriHeader(frame) {
                    continue
                }
            }

            // If we detect more than one bitrate we'll need to add a VBR
            // header to the output file.
            if firstBitRate == 0 {
                firstBitRate = frame.BitRate
            } else if frame.BitRate != firstBitRate {
                isVBR = true
            }

            // Write the frame to the output file.
            _, err := outfile.Write(frame.RawBytes)
            if err != nil {
                fmt.Fprintln(os.Stderr, err)
                os.Exit(1)
            }

            totalFrames += 1
            totalBytes += uint32(len(frame.RawBytes))
        }

        infile.Close()
        totalFiles += 1
    }

    outfile.Close()
    if !quiet {
        printLine()
    }

    // If we detected multiple bitrates, prepend a VBR header to the file.
    if isVBR {
        if !quiet {
            fmt.Println("• Multiple bitrates detected. Adding VBR header.")
        }
        addXingHeader(outpath, totalFrames, totalBytes)
    }

    // Copy the ID3v2 tag from the n-th input file if requested. Order of
    // operations is important here. The ID3 tag must be the first item in
    // the file - in particular, it must come *before* any VBR header.
    if tagpath != "" {
        if !quiet {
            fmt.Printf("• Copying ID3 tag from: %s\n", tagpath)
        }
        addID3v2Tag(outpath, tagpath)
    }

    // Print a count of the number of files merged.
    if !quiet {
        fmt.Printf("• %v files merged.\n", totalFiles)
        printLine()
    }
}

// Prepend an Xing VBR header to the specified MP3 file.
func addXingHeader(filepath string, totalFrames, totalBytes uint32) {

    outputFile, err := os.Create(filepath + ".mp3cat.tmp")
    if err != nil {
        fmt.Fprintln(os.Stderr, err)
        os.Exit(1)
    }

    inputFile, err := os.Open(filepath)
    if err != nil {
        fmt.Fprintln(os.Stderr, err)
        os.Exit(1)
    }

    xingHeader := mp3lib.NewXingHeader(totalFrames, totalBytes)

    _, err = outputFile.Write(xingHeader.RawBytes)
    if err != nil {
        fmt.Fprintln(os.Stderr, err)
        os.Exit(1)
    }

    _, err = io.Copy(outputFile, inputFile)
    if err != nil {
        fmt.Fprintln(os.Stderr, err)
        os.Exit(1)
    }

    outputFile.Close()
    inputFile.Close()

    err = os.Remove(filepath)
    if err != nil {
        fmt.Fprintln(os.Stderr, err)
        os.Exit(1)
    }

    err = os.Rename(filepath + ".mp3cat.tmp", filepath)
    if err != nil {
        fmt.Fprintln(os.Stderr, err)
        os.Exit(1)
    }
}

// Prepend an ID3v2 tag to the MP3 file at mp3Path, copying from tagPath.
func addID3v2Tag(mp3Path, tagPath string) {

    tagFile, err := os.Open(tagPath)
    if err != nil {
        fmt.Fprintln(os.Stderr, err)
        os.Exit(1)
    }

    id3tag := mp3lib.NextID3v2Tag(tagFile)
    tagFile.Close()

    if id3tag != nil {
        outputFile, err := os.Create(mp3Path + ".mp3cat.tmp")
        if err != nil {
            fmt.Fprintln(os.Stderr, err)
            os.Exit(1)
        }

        inputFile, err := os.Open(mp3Path)
        if err != nil {
            fmt.Fprintln(os.Stderr, err)
            os.Exit(1)
        }

        _, err = outputFile.Write(id3tag.RawBytes)
        if err != nil {
            fmt.Fprintln(os.Stderr, err)
            os.Exit(1)
        }

        _, err = io.Copy(outputFile, inputFile)
        if err != nil {
            fmt.Fprintln(os.Stderr, err)
            os.Exit(1)
        }

        outputFile.Close()
        inputFile.Close()

        err = os.Remove(mp3Path)
        if err != nil {
            fmt.Fprintln(os.Stderr, err)
            os.Exit(1)
        }

        err = os.Rename(mp3Path + ".mp3cat.tmp", mp3Path)
        if err != nil {
            fmt.Fprintln(os.Stderr, err)
            os.Exit(1)
        }
    }
}

// Print a line to stdout if we're running in a terminal.
func printLine() {
    if terminal.IsTerminal(int(os.Stdout.Fd())) {
        width, _, err := terminal.GetSize(int(os.Stdout.Fd()))
        if err == nil {
            if runtime.GOOS == "windows" {
                for i := 0; i < width; i++ {
                    fmt.Print("-")
                }
                fmt.Println()
            } else {
                fmt.Print("\u001B[90m")
                for i := 0; i < width; i++ {
                    fmt.Print("─")
                }
                fmt.Println("\u001B[0m")
            }
        }
    }
}
<|endoftext|>


<commit_before>package main

import (
	"github.com/mehrdadrad/myping/cli"
	"github.com/mehrdadrad/myping/icmp"
	"github.com/mehrdadrad/myping/icmp/telia"
	"net"
	"regexp"
	"strings"
)

type Provider interface {
	Set(host, version string)
	GetDefaultNode() string
	GetNodes() map[string]string
	ChangeNode(node string)
	Ping() (string, error)
}

var providers = map[string]Provider{"telia": new(telia.Provider)}

func validateProvider(p string) (string, error) {
	p = strings.ToLower(p)
	return p, nil
}

func main() {
	var (
		err    error
		cPName string = "local"
	)

	rep := make(chan string, 1)
	cmd := make(chan string, 1)
	nxt := make(chan struct{}, 1)

	c := cli.Init("local")
	go c.Run(cmd, nxt)

	r, _ := regexp.Compile("(ping|connect|node) (.*)")

	for {
		select {
		case req := <-cmd:
			subReq := r.FindStringSubmatch(req)
			if len(subReq) == 0 {
				println("syntax error")
				nxt <- struct{}{}
				continue
			}
			switch {
			case subReq[1] == "ping" && cPName == "local":
				p := icmp.NewPing()
				ra, err := net.ResolveIPAddr("ip", subReq[2])
				if err != nil {
					println("cannot resolve", subReq[2], ": Unknown host")
					nxt <- struct{}{}
					continue
				}
				p.IP(ra.String())
				for n := 0; n < 4; n++ {
					p.Ping(rep)
					println(<-rep)
				}
				nxt <- struct{}{}
			case subReq[1] == "ping":
				providers[cPName].Set(subReq[2], "ipv4")
				m, _ := providers[cPName].Ping()
				println(m)
				nxt <- struct{}{}
			case subReq[1] == "node":
				providers[cPName].ChangeNode(subReq[2])
				c.SetPrompt(cPName + "/" + subReq[2])
				nxt <- struct{}{}
			case subReq[1] == "connect":
				var pName string
				if pName, err = validateProvider(subReq[2]); err != nil {
					println("provider not available")
					nxt <- struct{}{}
					continue
				}
				cPName = pName
				c.SetPrompt(cPName + "/" + providers[cPName].GetDefaultNode())
				nxt <- struct{}{}
			}
		}
	}
}
<commit_msg>added local command to back to local, added validation<commit_after>package main

import (
	"errors"
	"github.com/mehrdadrad/myping/cli"
	"github.com/mehrdadrad/myping/icmp"
	"github.com/mehrdadrad/myping/icmp/telia"
	"net"
	"regexp"
	"strings"
)

type Provider interface {
	Set(host, version string)
	GetDefaultNode() string
	GetNodes() map[string]string
	ChangeNode(node string)
	Ping() (string, error)
}

var providers = map[string]Provider{"telia": new(telia.Provider)}

func validateProvider(p string) (string, error) {
	match, _ := regexp.MatchString("(telia|level3)", p)
	p = strings.ToLower(p)
	if match {
		return p, nil
	} else {
		return "", errors.New("provider not support")
	}
}

func main() {
	var (
		err    error
		cPName string = "local"
	)

	rep := make(chan string, 1)
	cmd := make(chan string, 1)
	nxt := make(chan struct{}, 1)

	c := cli.Init("local")
	go c.Run(cmd, nxt)

	r, _ := regexp.Compile(`(ping|connect|node|local) (.*)`)

	for {
		select {
		case req := <-cmd:
			subReq := r.FindStringSubmatch(req)
			if len(subReq) == 0 {
				println("syntax error")
				nxt <- struct{}{}
				continue
			}
			switch {
			case subReq[1] == "ping" && cPName == "local":
				p := icmp.NewPing()
				ra, err := net.ResolveIPAddr("ip", subReq[2])
				if err != nil {
					println("cannot resolve", subReq[2], ": Unknown host")
					nxt <- struct{}{}
					continue
				}
				p.IP(ra.String())
				for n := 0; n < 4; n++ {
					p.Ping(rep)
					println(<-rep)
				}
				nxt <- struct{}{}
			case subReq[1] == "ping":
				providers[cPName].Set(subReq[2], "ipv4")
				m, _ := providers[cPName].Ping()
				println(m)
				nxt <- struct{}{}
			case subReq[1] == "node":
				providers[cPName].ChangeNode(subReq[2])
				c.SetPrompt(cPName + "/" + subReq[2])
				nxt <- struct{}{}
			case subReq[1] == "local":
				cPName = "local"
				c.SetPrompt(cPName)
				nxt <- struct{}{}
			case subReq[1] == "connect":
				var pName string
				if pName, err = validateProvider(subReq[2]); err != nil {
					println("provider not available")
					nxt <- struct{}{}
					continue
				}
				cPName = pName
				c.SetPrompt(cPName + "/" + providers[cPName].GetDefaultNode())
				nxt <- struct{}{}
			}
		}
	}
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"log"
	"os"
	"strings"

	"github.com/jinzhu/gorm"
	"github.com/serbe/ncp"
)

func (a *App) get() error {
	var (
		err error
		i   int64
	)
	for _, parseurl := range urls {
		topics, err := a.net.ParseForumTree(parseurl, false)
		if err != nil {
			log.Println("ParseForumTree ", err)
			return err
		}
		for _, topic := range topics {
			_, err := a.getTorrentByHref(topic.Href)
			if err == gorm.ErrRecordNotFound {
				film, err := a.net.ParseTopic(topic, false)
				if err == nil {
					i++
					film = a.checkName(film)
					a.createTorrent(film)
				}
			}
		}
	}
	if i > 0 {
		log.Println("Adding", i, "new films")
	} else {
		log.Println("No adding new films")
	}
	return err
}

func (a *App) update() error {
	var (
		i        int64
		err      error
		torrents []Torrent
	)

	torrents, err = a.getWithDownload()
	if err != nil {
		return err
	}
	for _, tor := range torrents {
		var topic ncp.Topic
		topic.Href = tor.Href
		f, err := a.net.ParseTopic(topic, false)
		if err == nil {
			if f.NNM != tor.NNM || f.Seeders != tor.Seeders || f.Leechers != tor.Leechers || f.Torrent != tor.Torrent {
				i++
				a.updateTorrent(tor.ID, f)
			}
		} else {
			return err
		}
	}
	if i > 0 {
		log.Println("Update", i, "movies")
	} else {
		log.Println("No movies update")
	}
	return nil
}

func (a *App) name() error {
	var i int64
	movies, err := a.getMovies()
	if err != nil {
		return err
	}
	for _, movie := range movies {
		if movie.Name == strings.ToUpper(movie.Name) {
			lowerName, err := a.getLowerName(movie)
			if err == nil {
				i++
				a.updateName(movie.ID, lowerName)
			}
		}
	}
	if i > 0 {
		log.Println(i, "name fixed")
	} else {
		log.Println("No fixed names")
	}
	return nil
}

func (a *App) rating() error {
	var (
		i int64
	)
	movies, err := a.getNoRating()
	if err != nil {
		return err
	}
	for _, movie := range movies {
		if movie.Kinopoisk == 0 || movie.IMDb == 0 || movie.Duration == "" {
			kp, err := a.getRating(movie)
			if err == nil {
				i++
				_ = a.updateRating(movie, kp)
			}
		}
	}
	if i > 0 {
		log.Println(i, "ratings update")
	} else {
		log.Println("No update ratings")
	}
	return nil
}

func (a *App) poster() error {
	var (
		i int64
	)
	movies, err := a.getMovies()
	if err != nil {
		return err
	}
	for _, movie := range movies {
		if movie.Poster != "" {
			_, err = os.Stat(a.hd + movie.Poster)
			if err == nil {
				poster, err := a.getPoster(movie.PosterURL)
				if err == nil {
					i++
					_ = a.updatePoster(movie, poster)
				}
			}
		} else {
			poster, err := a.getPoster(movie.PosterURL)
			if err == nil {
				i++
				_ = a.updatePoster(movie, poster)
			}
		}
	}
	if i > 0 {
		log.Println(i, "posters update")
	} else {
		log.Println("No update posters")
	}
	return nil
}

func main() {
	args := os.Args
	if contain(args, "help") {
		fmt.Println(`Usage:
	nctool COMMAND

Commands:
	help    показать справку
	get     получить новые фильмы
	update  обновление информации фильмов
	name    поиск и исправление имен фильмов
	rating  получение рейтинга Кинопоиска и IMDb
	poster  получение постеров`)
		os.Exit(0)
	}
	if containCommand(args) == false {
		fmt.Println(`comand not found: use "nctool help"`)
		os.Exit(1)
	}
	app, err := appInit()
	if err != nil {
		os.Exit(1)
	}
	if contain(args, "get") {
		log.Println("Start getting new films")
		err := app.get()
		log.Println("End getting new films")
		exit(err)
	}
	if contain(args, "update") {
		log.Println("Start update topics")
		err := app.update()
		log.Println("End update topics")
		exit(err)
	}
	if contain(args, "name") {
		log.Println("Start fix names")
		err := app.name()
		log.Println("End fix names")
		exit(err)
	}
	if contain(args, "rating") {
		log.Println("Start get ratings")
		err := app.rating()
		log.Println("End get ratings")
		exit(err)
	}
	if contain(args, "poster") {
		log.Println("Start get posters")
		err := app.poster()
		log.Println("End get posters")
		exit(err)
	}
}
<commit_msg>move commands to file<commit_after>package main

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

func main() {
	args := os.Args
	if contain(args, "help") {
		fmt.Println(`Usage:
	nctool COMMAND

Commands:
	help    показать справку
	get     получить новые фильмы
	update  обновление информации фильмов
	name    поиск и исправление имен фильмов
	rating  получение рейтинга Кинопоиска и IMDb
	poster  получение постеров`)
		os.Exit(0)
	}
	if containCommand(args) == false {
		fmt.Println(`comand not found: use "nctool help"`)
		os.Exit(1)
	}
	app, err := appInit()
	if err != nil {
		os.Exit(1)
	}
	if contain(args, "get") {
		log.Println("Start getting new films")
		err := app.get()
		log.Println("End getting new films")
		exit(err)
	}
	if contain(args, "update") {
		log.Println("Start update topics")
		err := app.update()
		log.Println("End update topics")
		exit(err)
	}
	if contain(args, "name") {
		log.Println("Start fix names")
		err := app.name()
		log.Println("End fix names")
		exit(err)
	}
	if contain(args, "rating") {
		log.Println("Start get ratings")
		err := app.rating()
		log.Println("End get ratings")
		exit(err)
	}
	if contain(args, "poster") {
		log.Println("Start get posters")
		err := app.poster()
		log.Println("End get posters")
		exit(err)
	}
}
<|endoftext|>


<commit_before>package virtualbox

import (
	"fmt"
	"github.com/mitchellh/multistep"
	"github.com/mitchellh/packer/packer"
	"os"
)

// This step uploads a file containing the VirtualBox version, which
// can be useful for various provisioning reasons.
type stepUploadGuestAdditions struct{}

func (s *stepUploadGuestAdditions) Run(state map[string]interface{}) multistep.StepAction {
	comm := state["communicator"].(packer.Communicator)
	config := state["config"].(*config)
	guestAdditionsPath := state["guest_additions_path"].(string)
	ui := state["ui"].(packer.Ui)

	f, err := os.Open(guestAdditionsPath)
	if err != nil {
		state["error"] = fmt.Errorf("Error opening guest additions ISO: %s", err)
		return multistep.ActionHalt
	}

	ui.Say("Upload VirtualBox guest additions ISO...")
	if err := comm.Upload(config.GuestAdditionsPath, f); err != nil {
		state["error"] = fmt.Errorf("Error uploading guest additions: %s", err)
		return multistep.ActionHalt
	}

	return multistep.ActionContinue
}

func (s *stepUploadGuestAdditions) Cleanup(state map[string]interface{}) {}
<commit_msg>builder/virtualbox: treat guest_additions_path as a template<commit_after>package virtualbox

import (
	"bytes"
	"fmt"
	"github.com/mitchellh/multistep"
	"github.com/mitchellh/packer/packer"
	"os"
	"text/template"
)

type guestAdditionsPathTemplate struct {
	Version string
}

// This step uploads the guest additions ISO to the VM.
type stepUploadGuestAdditions struct{}

func (s *stepUploadGuestAdditions) Run(state map[string]interface{}) multistep.StepAction {
	comm := state["communicator"].(packer.Communicator)
	config := state["config"].(*config)
	driver := state["driver"].(Driver)
	guestAdditionsPath := state["guest_additions_path"].(string)
	ui := state["ui"].(packer.Ui)

	version, err := driver.Version()
	if err != nil {
		state["error"] = fmt.Errorf("Error reading version for guest additions upload: %s", err)
		return multistep.ActionHalt
	}

	f, err := os.Open(guestAdditionsPath)
	if err != nil {
		state["error"] = fmt.Errorf("Error opening guest additions ISO: %s", err)
		return multistep.ActionHalt
	}

	tplData := &guestAdditionsPathTemplate{
		Version: version,
	}

	var processedPath bytes.Buffer
	t := template.Must(template.New("path").Parse(config.GuestAdditionsPath))
	t.Execute(&processedPath, tplData)

	ui.Say("Upload VirtualBox guest additions ISO...")
	if err := comm.Upload(processedPath.String(), f); err != nil {
		state["error"] = fmt.Errorf("Error uploading guest additions: %s", err)
		return multistep.ActionHalt
	}

	return multistep.ActionContinue
}

func (s *stepUploadGuestAdditions) Cleanup(state map[string]interface{}) {}
<|endoftext|>


<commit_before>// Copyright (c) Ilia Kravets, 2015. All rights reserved. PROVIDED "AS IS"
// WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED. See LICENSE file for details.

package processor

import (
	"errors"
	"fmt"

	"github.com/google/gopacket"

	"my/ev/packet"
)

type PayloadDetector interface {
	Detect([]byte, *[]gopacket.DecodingLayer) (gopacket.LayerType, error)
}
type PayloadMux struct {
	data          []byte
	decodedLayers *[]gopacket.DecodingLayer
	detectors     []PayloadDetector
	nextLayer     gopacket.LayerType
}

var _ gopacket.Layer = &PayloadMux{}

func (p *PayloadMux) String() string {
	return fmt.Sprintf("%d byte(s)", len(p.data))
}
func (p *PayloadMux) LayerType() gopacket.LayerType {
	return gopacket.LayerTypePayload
}
func (p *PayloadMux) LayerContents() []byte {
	return p.data
}
func (p *PayloadMux) LayerPayload() []byte {
	return p.data
}
func (p *PayloadMux) CanDecode() gopacket.LayerClass {
	return gopacket.LayerTypePayload
}
func (p *PayloadMux) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
	p.data = data
	p.nextLayer = gopacket.LayerTypeZero
	for _, d := range p.detectors {
		layer, err := d.Detect(data, p.decodedLayers)
		if err == nil {
			p.nextLayer = layer
			return nil
		}
	}
	return nil
}
func (p *PayloadMux) NextLayerType() gopacket.LayerType {
	return p.nextLayer
}

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

type payloadMuxLayerFactory struct {
	decodedLayers *[]gopacket.DecodingLayer
	detectors     []PayloadDetector
}

var _ packet.DecodingLayerFactory = &payloadMuxLayerFactory{}

func (f *payloadMuxLayerFactory) Create(layerType gopacket.LayerType) gopacket.DecodingLayer {
	return &PayloadMux{
		decodedLayers: f.decodedLayers,
		detectors:     f.detectors,
	}
}
func (f *payloadMuxLayerFactory) SupportedLayers() gopacket.LayerClass {
	return gopacket.LayerTypePayload
}
func (f *payloadMuxLayerFactory) AddDetector(detector PayloadDetector) {
	f.detectors = append(f.detectors, detector)
}
func (f *payloadMuxLayerFactory) SetDecodedLayers(decodedLayers *[]gopacket.DecodingLayer) {
	f.decodedLayers = decodedLayers
}

/************************************************************************/
type EndpointPayloadDetector struct {
	srcEndpointMap map[gopacket.Endpoint]gopacket.LayerType
	dstEndpointMap map[gopacket.Endpoint]gopacket.LayerType
}

var _ PayloadDetector = &EndpointPayloadDetector{}
var EndpointPayloadDetectorFailedError = errors.New("payload detection by endpoint failed")

func NewEndpointPayloadDetector() *EndpointPayloadDetector {
	return &EndpointPayloadDetector{
		srcEndpointMap: make(map[gopacket.Endpoint]gopacket.LayerType),
		dstEndpointMap: make(map[gopacket.Endpoint]gopacket.LayerType),
	}
}
func (d *EndpointPayloadDetector) Detect(payload []byte, decodedLayers *[]gopacket.DecodingLayer) (layer gopacket.LayerType, err error) {
	err = EndpointPayloadDetectorFailedError
	for _, dl := range *decodedLayers {
		switch l := dl.(type) {
		case gopacket.TransportLayer:
			layer, err = d.detectByFlow(payload, l.TransportFlow())
		case gopacket.NetworkLayer:
			layer, err = d.detectByFlow(payload, l.NetworkFlow())
		case gopacket.LinkLayer:
			layer, err = d.detectByFlow(payload, l.LinkFlow())
		}
		if err == nil {
			break
		}
	}
	return
}
func (d *EndpointPayloadDetector) detectByFlow(payload []byte, flow gopacket.Flow) (layer gopacket.LayerType, err error) {
	var ok bool
	if layer, ok = d.srcEndpointMap[flow.Src()]; ok {
		return
	}
	if layer, ok = d.dstEndpointMap[flow.Dst()]; ok {
		return
	}
	return gopacket.LayerTypeZero, EndpointPayloadDetectorFailedError
}
func (d *EndpointPayloadDetector) addSrcMap(src gopacket.Endpoint, lt gopacket.LayerType) {
	d.srcEndpointMap[src] = lt
}
func (d *EndpointPayloadDetector) addDstMap(dst gopacket.Endpoint, lt gopacket.LayerType) {
	d.dstEndpointMap[dst] = lt
}
<commit_msg>packet/processor: add UdpDstPortPayloadDetector<commit_after>// Copyright (c) Ilia Kravets, 2015. All rights reserved. PROVIDED "AS IS"
// WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED. See LICENSE file for details.

package processor

import (
	"errors"
	"fmt"

	"github.com/google/gopacket"
	"github.com/google/gopacket/layers"

	"my/ev/packet"
)

type PayloadDetector interface {
	Detect([]byte, *[]gopacket.DecodingLayer) (gopacket.LayerType, error)
}
type PayloadMux struct {
	data          []byte
	decodedLayers *[]gopacket.DecodingLayer
	detectors     []PayloadDetector
	nextLayer     gopacket.LayerType
}

var _ gopacket.Layer = &PayloadMux{}

func (p *PayloadMux) String() string {
	return fmt.Sprintf("%d byte(s)", len(p.data))
}
func (p *PayloadMux) LayerType() gopacket.LayerType {
	return gopacket.LayerTypePayload
}
func (p *PayloadMux) LayerContents() []byte {
	return p.data
}
func (p *PayloadMux) LayerPayload() []byte {
	return p.data
}
func (p *PayloadMux) CanDecode() gopacket.LayerClass {
	return gopacket.LayerTypePayload
}
func (p *PayloadMux) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
	p.data = data
	p.nextLayer = gopacket.LayerTypeZero
	for _, d := range p.detectors {
		layer, err := d.Detect(data, p.decodedLayers)
		if err == nil {
			p.nextLayer = layer
			return nil
		}
	}
	return nil
}
func (p *PayloadMux) NextLayerType() gopacket.LayerType {
	return p.nextLayer
}

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

type payloadMuxLayerFactory struct {
	decodedLayers *[]gopacket.DecodingLayer
	detectors     []PayloadDetector
}

var _ packet.DecodingLayerFactory = &payloadMuxLayerFactory{}

func (f *payloadMuxLayerFactory) Create(layerType gopacket.LayerType) gopacket.DecodingLayer {
	return &PayloadMux{
		decodedLayers: f.decodedLayers,
		detectors:     f.detectors,
	}
}
func (f *payloadMuxLayerFactory) SupportedLayers() gopacket.LayerClass {
	return gopacket.LayerTypePayload
}
func (f *payloadMuxLayerFactory) AddDetector(detector PayloadDetector) {
	f.detectors = append(f.detectors, detector)
}
func (f *payloadMuxLayerFactory) SetDecodedLayers(decodedLayers *[]gopacket.DecodingLayer) {
	f.decodedLayers = decodedLayers
}

/************************************************************************/
type EndpointPayloadDetector struct {
	srcEndpointMap map[gopacket.Endpoint]gopacket.LayerType
	dstEndpointMap map[gopacket.Endpoint]gopacket.LayerType
}

var _ PayloadDetector = &EndpointPayloadDetector{}
var EndpointPayloadDetectorFailedError = errors.New("payload detection by endpoint failed")

func NewEndpointPayloadDetector() *EndpointPayloadDetector {
	return &EndpointPayloadDetector{
		srcEndpointMap: make(map[gopacket.Endpoint]gopacket.LayerType),
		dstEndpointMap: make(map[gopacket.Endpoint]gopacket.LayerType),
	}
}
func (d *EndpointPayloadDetector) Detect(payload []byte, decodedLayers *[]gopacket.DecodingLayer) (layer gopacket.LayerType, err error) {
	err = EndpointPayloadDetectorFailedError
	for _, dl := range *decodedLayers {
		switch l := dl.(type) {
		case gopacket.TransportLayer:
			layer, err = d.detectByFlow(payload, l.TransportFlow())
		case gopacket.NetworkLayer:
			layer, err = d.detectByFlow(payload, l.NetworkFlow())
		case gopacket.LinkLayer:
			layer, err = d.detectByFlow(payload, l.LinkFlow())
		}
		if err == nil {
			break
		}
	}
	return
}
func (d *EndpointPayloadDetector) detectByFlow(payload []byte, flow gopacket.Flow) (layer gopacket.LayerType, err error) {
	var ok bool
	if layer, ok = d.srcEndpointMap[flow.Src()]; ok {
		return
	}
	if layer, ok = d.dstEndpointMap[flow.Dst()]; ok {
		return
	}
	return gopacket.LayerTypeZero, EndpointPayloadDetectorFailedError
}
func (d *EndpointPayloadDetector) addSrcMap(src gopacket.Endpoint, lt gopacket.LayerType) {
	d.srcEndpointMap[src] = lt
}
func (d *EndpointPayloadDetector) addDstMap(dst gopacket.Endpoint, lt gopacket.LayerType) {
	d.dstEndpointMap[dst] = lt
}

/************************************************************************/
type UdpDstPortPayloadDetector struct {
	portMap map[layers.UDPPort]gopacket.LayerType
}

var _ PayloadDetector = &UdpDstPortPayloadDetector{}
var UdpDstPortPayloadDetectorFailedError = errors.New("payload detection by UdpDstPort failed")

func NewUdpDstPortPayloadDetector() *UdpDstPortPayloadDetector {
	return &UdpDstPortPayloadDetector{
		portMap: make(map[layers.UDPPort]gopacket.LayerType),
	}
}
func (d *UdpDstPortPayloadDetector) Detect(payload []byte, decodedLayers *[]gopacket.DecodingLayer) (layer gopacket.LayerType, err error) {
	err = UdpDstPortPayloadDetectorFailedError
	for _, dl := range *decodedLayers {
		if u, ok := dl.(*layers.UDP); ok {
			if layer, ok = d.portMap[u.DstPort]; ok {
				err = nil
			}
			break
		}
	}
	return
}
func (d *UdpDstPortPayloadDetector) addPortMap(dst layers.UDPPort, lt gopacket.LayerType) {
	d.portMap[dst] = lt
}
<|endoftext|>


<commit_before>// Copyright (C) 2016 JT Olds
// See LICENSE for copying information

package webhelp

import (
	"context"
	"net/http"
	"strconv"
	"sync/atomic"
)

// StringArgMux is a way to pull off arbitrary path elements from an incoming
// URL. You'll need to create one with NewStringArgMux.
type StringArgMux int64

var argMuxCounter int64

func NewStringArgMux() StringArgMux {
	return StringArgMux(atomic.AddInt64(&argMuxCounter, 1))
}

// Shift takes an http.Handler and returns a new http.Handler that does
// additional request processing. When an incoming request is processed, the
// new http.Handler pulls the next path element off of the incoming request
// path and puts the value in the current Context. It then passes processing
// off to the wrapped http.Handler. The value will be an empty string if no
// argument is found.
func (a StringArgMux) Shift(h http.Handler) http.Handler {
	return a.OptShift(h, h)
}

type stringOptShift struct {
	a               StringArgMux
	found, notfound http.Handler
}

// OptShift is like Shift but the first handler is used only if there's no
// argument found, and the second handler is used if there is.
func (a StringArgMux) OptShift(notfound, found http.Handler) http.Handler {
	return stringOptShift{a: a, found: found, notfound: notfound}
}

func (ssi stringOptShift) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	arg, newpath := Shift(r.URL.Path)
	if arg == "" {
		ssi.notfound.ServeHTTP(w, r)
		return
	}
	r.URL.Path = newpath
	ctx := context.WithValue(r.Context(), ssi.a, arg)
	ssi.found.ServeHTTP(w, r.WithContext(ctx))
}

func (ssi stringOptShift) Routes(cb func(string, string, []string)) {
	Routes(ssi.found, func(method, path string, annotations []string) {
		cb(method, "/<string>"+path, annotations)
	})
	Routes(ssi.notfound, cb)
}

var _ http.Handler = stringOptShift{}
var _ RouteLister = stringOptShift{}

// Get returns a stored value for the Arg from the Context, or "" if no value
// was found (which won't be the case if a higher-level handler was this
// argmux)
func (a StringArgMux) Get(ctx context.Context) (val string) {
	if val, ok := ctx.Value(a).(string); ok {
		return val
	}
	return ""
}

// IntArgMux is a way to pull off numeric path elements from an incoming
// URL. You'll need to create one with NewIntArgMux.
type IntArgMux int64

func NewIntArgMux() IntArgMux {
	return IntArgMux(atomic.AddInt64(&argMuxCounter, 1))
}

type notFoundHandler struct{}

func (notFoundHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	HandleError(w, r, ErrNotFound.New("resource: %#v", r.URL.Path))
}

func (notFoundHandler) Routes(cb func(string, string, []string)) {}

var _ http.Handler = notFoundHandler{}
var _ RouteLister = notFoundHandler{}

// Shift takes an http.Handler and returns a new http.Handler that does
// additional request processing. When an incoming request is processed, the
// new http.Handler pulls the next path element off of the incoming request
// path and puts the value in the current Context. It then passes processing
// off to the wrapped http.Handler. It responds with a 404 if no numeric value
// is found.
func (a IntArgMux) Shift(h http.Handler) http.Handler {
	return a.OptShift(notFoundHandler{}, h)
}

type intOptShift struct {
	a               IntArgMux
	found, notfound http.Handler
}

// OptShift is like Shift but will only use the second handler if there's no
// numeric argument found and the first handler otherwise
func (a IntArgMux) OptShift(notfound, found http.Handler) http.Handler {
	return intOptShift{a: a, found: found, notfound: notfound}
}

func (isi intOptShift) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	str, newpath := Shift(r.URL.Path)
	val, err := strconv.ParseInt(str, 10, 64)
	if err != nil {
		isi.notfound.ServeHTTP(w, r)
		return
	}
	r.URL.Path = newpath
	ctx := context.WithValue(r.Context(), isi.a, val)
	isi.found.ServeHTTP(w, r.WithContext(ctx))
}

func (isi intOptShift) Routes(cb func(string, string, []string)) {
	Routes(isi.found, func(method, path string, annotations []string) {
		cb(method, "/<int>"+path, annotations)
	})
	Routes(isi.notfound, cb)
}

var _ http.Handler = intOptShift{}
var _ RouteLister = intOptShift{}

// Get returns a stored value for the Arg from the Context and ok = true if
// found, or ok = false if no value was found (which won't be the case if a
// higher-level handler was this argmux)
func (a IntArgMux) Get(ctx context.Context) (val int64, ok bool) {
	if val, ok := ctx.Value(a).(int64); ok {
		return val, true
	}
	return 0, false
}
<commit_msg>add MustGet to argmux<commit_after>// Copyright (C) 2016 JT Olds
// See LICENSE for copying information

package webhelp

import (
	"context"
	"net/http"
	"strconv"
	"sync/atomic"
)

// StringArgMux is a way to pull off arbitrary path elements from an incoming
// URL. You'll need to create one with NewStringArgMux.
type StringArgMux int64

var argMuxCounter int64

func NewStringArgMux() StringArgMux {
	return StringArgMux(atomic.AddInt64(&argMuxCounter, 1))
}

// Shift takes an http.Handler and returns a new http.Handler that does
// additional request processing. When an incoming request is processed, the
// new http.Handler pulls the next path element off of the incoming request
// path and puts the value in the current Context. It then passes processing
// off to the wrapped http.Handler. The value will be an empty string if no
// argument is found.
func (a StringArgMux) Shift(h http.Handler) http.Handler {
	return a.OptShift(h, h)
}

type stringOptShift struct {
	a               StringArgMux
	found, notfound http.Handler
}

// OptShift is like Shift but the first handler is used only if there's no
// argument found, and the second handler is used if there is.
func (a StringArgMux) OptShift(notfound, found http.Handler) http.Handler {
	return stringOptShift{a: a, found: found, notfound: notfound}
}

func (ssi stringOptShift) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	arg, newpath := Shift(r.URL.Path)
	if arg == "" {
		ssi.notfound.ServeHTTP(w, r)
		return
	}
	r.URL.Path = newpath
	ctx := context.WithValue(r.Context(), ssi.a, arg)
	ssi.found.ServeHTTP(w, r.WithContext(ctx))
}

func (ssi stringOptShift) Routes(cb func(string, string, []string)) {
	Routes(ssi.found, func(method, path string, annotations []string) {
		cb(method, "/<string>"+path, annotations)
	})
	Routes(ssi.notfound, cb)
}

var _ http.Handler = stringOptShift{}
var _ RouteLister = stringOptShift{}

// Get returns a stored value for the Arg from the Context, or "" if no value
// was found (which won't be the case if a higher-level handler was this
// argmux)
func (a StringArgMux) Get(ctx context.Context) (val string) {
	if val, ok := ctx.Value(a).(string); ok {
		return val
	}
	return ""
}

// IntArgMux is a way to pull off numeric path elements from an incoming
// URL. You'll need to create one with NewIntArgMux.
type IntArgMux int64

func NewIntArgMux() IntArgMux {
	return IntArgMux(atomic.AddInt64(&argMuxCounter, 1))
}

type notFoundHandler struct{}

func (notFoundHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	HandleError(w, r, ErrNotFound.New("resource: %#v", r.URL.Path))
}

func (notFoundHandler) Routes(cb func(string, string, []string)) {}

var _ http.Handler = notFoundHandler{}
var _ RouteLister = notFoundHandler{}

// Shift takes an http.Handler and returns a new http.Handler that does
// additional request processing. When an incoming request is processed, the
// new http.Handler pulls the next path element off of the incoming request
// path and puts the value in the current Context. It then passes processing
// off to the wrapped http.Handler. It responds with a 404 if no numeric value
// is found.
func (a IntArgMux) Shift(h http.Handler) http.Handler {
	return a.OptShift(notFoundHandler{}, h)
}

type intOptShift struct {
	a               IntArgMux
	found, notfound http.Handler
}

// OptShift is like Shift but will only use the second handler if there's no
// numeric argument found and the first handler otherwise
func (a IntArgMux) OptShift(notfound, found http.Handler) http.Handler {
	return intOptShift{a: a, found: found, notfound: notfound}
}

func (isi intOptShift) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	str, newpath := Shift(r.URL.Path)
	val, err := strconv.ParseInt(str, 10, 64)
	if err != nil {
		isi.notfound.ServeHTTP(w, r)
		return
	}
	r.URL.Path = newpath
	ctx := context.WithValue(r.Context(), isi.a, val)
	isi.found.ServeHTTP(w, r.WithContext(ctx))
}

func (isi intOptShift) Routes(cb func(string, string, []string)) {
	Routes(isi.found, func(method, path string, annotations []string) {
		cb(method, "/<int>"+path, annotations)
	})
	Routes(isi.notfound, cb)
}

var _ http.Handler = intOptShift{}
var _ RouteLister = intOptShift{}

// Get returns a stored value for the Arg from the Context and ok = true if
// found, or ok = false if no value was found (which won't be the case if a
// higher-level handler was this argmux)
func (a IntArgMux) Get(ctx context.Context) (val int64, ok bool) {
	if val, ok := ctx.Value(a).(int64); ok {
		return val, true
	}
	return 0, false
}

// MustGet is like Get but panics in cases when ok would be false.
func (a IntArgMux) MustGet(ctx context.Context) (val int64) {
	if val, ok := ctx.Value(a).(int64); ok {
		return val
	}
	panic("Required argument missing")
}
<|endoftext|>


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

package flushfs_test

import (
	"sync"
	"testing"

	"github.com/jacobsa/fuse/samples"
	"github.com/jacobsa/fuse/samples/flushfs"
	. "github.com/jacobsa/ogletest"
)

func TestFlushFS(t *testing.T) { RunTests(t) }

////////////////////////////////////////////////////////////////////////
// Boilerplate
////////////////////////////////////////////////////////////////////////

type FlushFSTest struct {
	samples.SampleTest

	mu sync.Mutex

	// GUARDED_BY(mu)
	flushes []string

	// GUARDED_BY(mu)
	fsyncs []string
}

func init() { RegisterTestSuite(&FlushFSTest{}) }

func (t *FlushFSTest) SetUp(ti *TestInfo) {
	var err error

	// Set up a file system.
	reportTo := func(slice *[]string) func(string) {
		return func(s string) {
			t.mu.Lock()
			defer t.mu.Unlock()
			*slice = append(*slice, s)
		}
	}

	t.FileSystem, err = flushfs.NewFileSystem(
		reportTo(&t.flushes),
		reportTo(&t.fsyncs))

	if err != nil {
		panic(err)
	}

	// Mount it.
	t.SampleTest.SetUp(ti)
}

////////////////////////////////////////////////////////////////////////
// Test functions
////////////////////////////////////////////////////////////////////////

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

package flushfs_test

import (
	"sync"
	"testing"

	"github.com/jacobsa/fuse/samples"
	"github.com/jacobsa/fuse/samples/flushfs"
	. "github.com/jacobsa/ogletest"
)

func TestFlushFS(t *testing.T) { RunTests(t) }

////////////////////////////////////////////////////////////////////////
// Boilerplate
////////////////////////////////////////////////////////////////////////

type FlushFSTest struct {
	samples.SampleTest

	mu sync.Mutex

	// GUARDED_BY(mu)
	flushes []string

	// GUARDED_BY(mu)
	fsyncs []string
}

func init() { RegisterTestSuite(&FlushFSTest{}) }

func (t *FlushFSTest) SetUp(ti *TestInfo) {
	var err error

	// Set up a file system.
	reportTo := func(slice *[]string) func(string) {
		return func(s string) {
			t.mu.Lock()
			defer t.mu.Unlock()
			*slice = append(*slice, s)
		}
	}

	t.FileSystem, err = flushfs.NewFileSystem(
		reportTo(&t.flushes),
		reportTo(&t.fsyncs))

	if err != nil {
		panic(err)
	}

	// Mount it.
	t.SampleTest.SetUp(ti)
}

////////////////////////////////////////////////////////////////////////
// Test functions
////////////////////////////////////////////////////////////////////////

func (t *FlushFSTest) FlushesOnClose_NonOverlappingFileHandles() {
	AssertTrue(false, "TODO")
}

func (t *FlushFSTest) FlushesOnClose_OverlappingFileHandles() {
	AssertTrue(false, "TODO")
}

func (t *FlushFSTest) FlushesOnFsync() {
	AssertTrue(false, "TODO")
}
<|endoftext|>


<commit_before>package types

import (
	"os"
	"strconv"
	"strings"
	"unicode"

	"github.com/onsi/ginkgo/formatter"
)

type Deprecation struct {
	Message string
	DocLink string
	Version string
}

type deprecations struct{}

var Deprecations = deprecations{}

func (d deprecations) CustomReporter() Deprecation {
	return Deprecation{
		Message: "Support for custom reporters has been removed in V2.  Please read the documentation linked to below for Ginkgo's new behavior and for a migration path:",
		DocLink: "removed-custom-reporters",
		Version: "1.16.0",
	}
}

func (d deprecations) Async() Deprecation {
	return Deprecation{
		Message: "You are passing a Done channel to a test node to test asynchronous behavior.  This is deprecated in Ginkgo V2.  Your test will run synchronously and the timeout will be ignored.",
		DocLink: "removed-async-testing",
		Version: "1.16.0",
	}
}

func (d deprecations) Measure() Deprecation {
	return Deprecation{
		Message: "Measure is deprecated and will be removed in Ginkgo V2.  Please migrate to gomega/gmeasure.",
		DocLink: "removed-measure",
		Version: "1.16.0",
	}
}

func (d deprecations) CurrentGinkgoTestDescription() Deprecation {
	return Deprecation{
		Message: "CurrentGinkgoTestDescription() is deprecated in Ginkgo V2.  Use CurrentSpecReport() instead.",
		DocLink: "changed-currentginkgotestdescription",
		Version: "1.16.0",
	}
}

func (d deprecations) Convert() Deprecation {
	return Deprecation{
		Message: "The convert command is deprecated in Ginkgo V2",
		DocLink: "removed-ginkgo-convert",
		Version: "1.16.0",
	}
}

func (d deprecations) Blur() Deprecation {
	return Deprecation{
		Message: "The blur command is deprecated in Ginkgo V2.  Use 'ginkgo unfocus' instead.",
		Version: "1.16.0",
	}
}

type DeprecationTracker struct {
	deprecations map[Deprecation][]CodeLocation
}

func NewDeprecationTracker() *DeprecationTracker {
	return &DeprecationTracker{
		deprecations: map[Deprecation][]CodeLocation{},
	}
}

func (d *DeprecationTracker) TrackDeprecation(deprecation Deprecation, cl ...CodeLocation) {
	ackVersion := os.Getenv("ACK_GINKGO_DEPRECATIONS")
	if deprecation.Version != "" && ackVersion != "" {
		ack := ParseSemVer(ackVersion)
		version := ParseSemVer(deprecation.Version)
		if ack.GreaterThanOrEqualTo(version) {
			return
		}
	}

	if len(cl) == 1 {
		d.deprecations[deprecation] = append(d.deprecations[deprecation], cl[0])
	} else {
		d.deprecations[deprecation] = []CodeLocation{}
	}
}

func (d *DeprecationTracker) DidTrackDeprecations() bool {
	return len(d.deprecations) > 0
}

func (d *DeprecationTracker) DeprecationsReport() string {
	out := formatter.F("{{light-yellow}}You're using deprecated Ginkgo functionality:{{/}}\n")
	out += formatter.F("{{light-yellow}}============================================={{/}}\n")
	for deprecation, locations := range d.deprecations {
		out += formatter.Fi(1, "{{yellow}}"+deprecation.Message+"{{/}}\n")
		if deprecation.DocLink != "" {
			out += formatter.Fi(1, "{{bold}}Learn more at:{{/}} {{cyan}}{{underline}}https://github.com/onsi/ginkgo/blob/v2/docs/MIGRATING_TO_V2.md#%s{{/}}\n", deprecation.DocLink)
		}
		for _, location := range locations {
			out += formatter.Fi(2, "{{gray}}%s{{/}}\n", location)
		}
	}
	out += formatter.F("\n{{gray}}To silence deprecations that can be silenced set the following environment variable:{{/}}\n")
	out += formatter.Fi(1, "{{gray}}ACK_GINKGO_DEPRECATIONS=%s{{/}}\n", VERSION)
	return out
}

type SemVer struct {
	Major int
	Minor int
	Patch int
}

func (s SemVer) GreaterThanOrEqualTo(o SemVer) bool {
	return (s.Major > o.Major) ||
		(s.Major == o.Major && s.Minor > o.Minor) ||
		(s.Major == o.Major && s.Minor == o.Minor && s.Patch >= o.Patch)
}

func ParseSemVer(semver string) SemVer {
	out := SemVer{}
	semver = strings.TrimFunc(semver, func(r rune) bool {
		return !(unicode.IsNumber(r) || r == '.')
	})
	components := strings.Split(semver, ".")
	if len(components) > 0 {
		out.Major, _ = strconv.Atoi(components[0])
	}
	if len(components) > 1 {
		out.Minor, _ = strconv.Atoi(components[1])
	}
	if len(components) > 2 {
		out.Patch, _ = strconv.Atoi(components[2])
	}
	return out
}
<commit_msg>update deprecation message for Measure to track with Master<commit_after>package types

import (
	"os"
	"strconv"
	"strings"
	"unicode"

	"github.com/onsi/ginkgo/formatter"
)

type Deprecation struct {
	Message string
	DocLink string
	Version string
}

type deprecations struct{}

var Deprecations = deprecations{}

func (d deprecations) CustomReporter() Deprecation {
	return Deprecation{
		Message: "Support for custom reporters has been removed in V2.  Please read the documentation linked to below for Ginkgo's new behavior and for a migration path:",
		DocLink: "removed-custom-reporters",
		Version: "1.16.0",
	}
}

func (d deprecations) Async() Deprecation {
	return Deprecation{
		Message: "You are passing a Done channel to a test node to test asynchronous behavior.  This is deprecated in Ginkgo V2.  Your test will run synchronously and the timeout will be ignored.",
		DocLink: "removed-async-testing",
		Version: "1.16.0",
	}
}

func (d deprecations) Measure() Deprecation {
	return Deprecation{
		Message: "Measure is deprecated and will be removed in Ginkgo V2.  Please migrate to gomega/gmeasure.",
		DocLink: "removed-measure",
		Version: "1.16.3",
	}
}

func (d deprecations) CurrentGinkgoTestDescription() Deprecation {
	return Deprecation{
		Message: "CurrentGinkgoTestDescription() is deprecated in Ginkgo V2.  Use CurrentSpecReport() instead.",
		DocLink: "changed-currentginkgotestdescription",
		Version: "1.16.0",
	}
}

func (d deprecations) Convert() Deprecation {
	return Deprecation{
		Message: "The convert command is deprecated in Ginkgo V2",
		DocLink: "removed-ginkgo-convert",
		Version: "1.16.0",
	}
}

func (d deprecations) Blur() Deprecation {
	return Deprecation{
		Message: "The blur command is deprecated in Ginkgo V2.  Use 'ginkgo unfocus' instead.",
		Version: "1.16.0",
	}
}

type DeprecationTracker struct {
	deprecations map[Deprecation][]CodeLocation
}

func NewDeprecationTracker() *DeprecationTracker {
	return &DeprecationTracker{
		deprecations: map[Deprecation][]CodeLocation{},
	}
}

func (d *DeprecationTracker) TrackDeprecation(deprecation Deprecation, cl ...CodeLocation) {
	ackVersion := os.Getenv("ACK_GINKGO_DEPRECATIONS")
	if deprecation.Version != "" && ackVersion != "" {
		ack := ParseSemVer(ackVersion)
		version := ParseSemVer(deprecation.Version)
		if ack.GreaterThanOrEqualTo(version) {
			return
		}
	}

	if len(cl) == 1 {
		d.deprecations[deprecation] = append(d.deprecations[deprecation], cl[0])
	} else {
		d.deprecations[deprecation] = []CodeLocation{}
	}
}

func (d *DeprecationTracker) DidTrackDeprecations() bool {
	return len(d.deprecations) > 0
}

func (d *DeprecationTracker) DeprecationsReport() string {
	out := formatter.F("{{light-yellow}}You're using deprecated Ginkgo functionality:{{/}}\n")
	out += formatter.F("{{light-yellow}}============================================={{/}}\n")
	for deprecation, locations := range d.deprecations {
		out += formatter.Fi(1, "{{yellow}}"+deprecation.Message+"{{/}}\n")
		if deprecation.DocLink != "" {
			out += formatter.Fi(1, "{{bold}}Learn more at:{{/}} {{cyan}}{{underline}}https://github.com/onsi/ginkgo/blob/v2/docs/MIGRATING_TO_V2.md#%s{{/}}\n", deprecation.DocLink)
		}
		for _, location := range locations {
			out += formatter.Fi(2, "{{gray}}%s{{/}}\n", location)
		}
	}
	out += formatter.F("\n{{gray}}To silence deprecations that can be silenced set the following environment variable:{{/}}\n")
	out += formatter.Fi(1, "{{gray}}ACK_GINKGO_DEPRECATIONS=%s{{/}}\n", VERSION)
	return out
}

type SemVer struct {
	Major int
	Minor int
	Patch int
}

func (s SemVer) GreaterThanOrEqualTo(o SemVer) bool {
	return (s.Major > o.Major) ||
		(s.Major == o.Major && s.Minor > o.Minor) ||
		(s.Major == o.Major && s.Minor == o.Minor && s.Patch >= o.Patch)
}

func ParseSemVer(semver string) SemVer {
	out := SemVer{}
	semver = strings.TrimFunc(semver, func(r rune) bool {
		return !(unicode.IsNumber(r) || r == '.')
	})
	components := strings.Split(semver, ".")
	if len(components) > 0 {
		out.Major, _ = strconv.Atoi(components[0])
	}
	if len(components) > 1 {
		out.Minor, _ = strconv.Atoi(components[1])
	}
	if len(components) > 2 {
		out.Patch, _ = strconv.Atoi(components[2])
	}
	return out
}
<|endoftext|>


<commit_before>package git

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

	"golang.org/x/sys/unix"
)

func SetMTimes(git_dir, checkout_dir, ref string) error {
	// From https://github.com/rosylilly/git-set-mtime with tweaks
	// Copyright (c) 2014 Sho Kusano

	// MIT License

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

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

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

	lsFiles := Command(git_dir, "git", "ls-tree", "-z", "-r", "--name-only", ref)
	lsFiles.Stdout = nil
	out, err := lsFiles.Output()
	if err != nil {
		return fmt.Errorf("git ls-files: %v", err)
	}

	dirMTimes := map[string]time.Time{}

	chtime := func(path string, atime, mtime time.Time) error {
		// https://github.com/torvalds/linux/blob/2decb2682f80759f631c8332f9a2a34a02150a03/include/uapi/linux/fcntl.h#L56
		var utimes [2]unix.Timespec
		utimes[0] = unix.NsecToTimespec(atime.UnixNano())
		utimes[1] = unix.NsecToTimespec(mtime.UnixNano())

		if e := unix.UtimesNanoAt(unix.AT_FDCWD, path, utimes[0:], unix.AT_SYMLINK_NOFOLLOW); e != nil {
			return &os.PathError{"futimesat", path, e}
		}
		return nil
	}

	files := strings.Split(strings.TrimRight(string(out), "\x00"), "\x00")
	for _, file := range files {
		gitLog := Command(git_dir, "git", "log", "-1", "--date=rfc2822",
			"--format=%cd", ref, "--", file)
		gitLog.Stdout = nil

		out, err := gitLog.Output()
		if err != nil {
			return fmt.Errorf("git log: %v", err)
		}

		mStr := strings.TrimSpace(strings.TrimLeft(string(out), "Date:"))
		mTime, err := time.Parse("Mon, 2 Jan 2006 15:04:05 -0700", mStr)
		if err != nil {
			return fmt.Errorf("parse time: %v", err)
		}

		// Loop over each directory in the path to `file`, updating `dirMTimes`
		// to take the most recent time seen.
		dir := filepath.Dir(file)
		for {
			if other, ok := dirMTimes[dir]; ok {
				if mTime.After(other) {
					// file mTime is more recent than previous seen for 'dir'
					dirMTimes[dir] = mTime
				}
			} else {
				// first occurrence of dir
				dirMTimes[dir] = mTime
			}

			// Remove one directory from the path until it isn't changed anymore
			if dir == filepath.Dir(dir) {
				break
			}
			dir = filepath.Dir(dir)
		}

		err = chtime(checkout_dir+"/"+file, mTime, mTime)
		if err != nil {
			return fmt.Errorf("chtimes: %v", err)
		}

		fmt.Printf("%s: %s\n", file, mTime)
	}

	for dir, mTime := range dirMTimes {
		err = chtime(checkout_dir+"/"+dir, mTime, mTime)
		if err != nil {
			return fmt.Errorf("chtimes: %v", err)
		}
		fmt.Printf("%s: %s\n", dir, mTime)
	}
	return nil
}
<commit_msg>Remove prints from SetMTimes<commit_after>package git

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

	"golang.org/x/sys/unix"
)

func SetMTimes(git_dir, checkout_dir, ref string) error {
	// From https://github.com/rosylilly/git-set-mtime with tweaks
	// Copyright (c) 2014 Sho Kusano

	// MIT License

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

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

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

	lsFiles := Command(git_dir, "git", "ls-tree", "-z", "-r", "--name-only", ref)
	lsFiles.Stdout = nil
	out, err := lsFiles.Output()
	if err != nil {
		return fmt.Errorf("git ls-files: %v", err)
	}

	dirMTimes := map[string]time.Time{}

	chtime := func(path string, atime, mtime time.Time) error {
		// https://github.com/torvalds/linux/blob/2decb2682f80759f631c8332f9a2a34a02150a03/include/uapi/linux/fcntl.h#L56
		var utimes [2]unix.Timespec
		utimes[0] = unix.NsecToTimespec(atime.UnixNano())
		utimes[1] = unix.NsecToTimespec(mtime.UnixNano())

		if e := unix.UtimesNanoAt(unix.AT_FDCWD, path, utimes[0:], unix.AT_SYMLINK_NOFOLLOW); e != nil {
			return &os.PathError{"futimesat", path, e}
		}
		return nil
	}

	files := strings.Split(strings.TrimRight(string(out), "\x00"), "\x00")
	for _, file := range files {
		gitLog := Command(git_dir, "git", "log", "-1", "--date=rfc2822",
			"--format=%cd", ref, "--", file)
		gitLog.Stdout = nil

		out, err := gitLog.Output()
		if err != nil {
			return fmt.Errorf("git log: %v", err)
		}

		mStr := strings.TrimSpace(strings.TrimLeft(string(out), "Date:"))
		mTime, err := time.Parse("Mon, 2 Jan 2006 15:04:05 -0700", mStr)
		if err != nil {
			return fmt.Errorf("parse time: %v", err)
		}

		// Loop over each directory in the path to `file`, updating `dirMTimes`
		// to take the most recent time seen.
		dir := filepath.Dir(file)
		for {
			if other, ok := dirMTimes[dir]; ok {
				if mTime.After(other) {
					// file mTime is more recent than previous seen for 'dir'
					dirMTimes[dir] = mTime
				}
			} else {
				// first occurrence of dir
				dirMTimes[dir] = mTime
			}

			// Remove one directory from the path until it isn't changed anymore
			if dir == filepath.Dir(dir) {
				break
			}
			dir = filepath.Dir(dir)
		}

		err = chtime(checkout_dir+"/"+file, mTime, mTime)
		if err != nil {
			return fmt.Errorf("chtimes: %v", err)
		}
	}

	for dir, mTime := range dirMTimes {
		err = chtime(checkout_dir+"/"+dir, mTime, mTime)
		if err != nil {
			return fmt.Errorf("chtimes: %v", err)
		}
	}
	return nil
}
<|endoftext|>


<commit_before>package judge

import (
	"fmt"
	"github.com/gogather/com"
	"path/filepath"
	"strconv"
)

type Info struct {
	sid         string
	id          int
	buildLog    string
	buildResult bool
	runLog      string
	runResult   bool
	buildPath   string
}

func (this *Info) Gather(sid string, id int, buildPath string) map[string]interface{} {
	this.sid = sid
	this.id = id
	this.buildPath = buildPath

	this.buildLog = this.getLog("BUILD.LOG")
	if this.buildResult = this.getResult("BUILDRESULT"); this.buildResult {
		this.runLog = this.getLog("RUN.LOG")
		this.runResult = this.getResult("RUNRESULT")
	} else {
		this.runLog = ""
		this.runResult = false
	}

	return map[string]interface{}{
		"build_log":    this.buildLog,
		"build_result": this.buildResult,
		"run_log":      this.runLog,
		"run_result":   this.runResult,
	}
}

func (this *Info) getLog(file string) string {
	path := filepath.Join(this.buildPath, this.sid, fmt.Sprintf("%d", this.id), file)
	if com.PathExist(path) {
		return com.ReadFile(path)
	} else {
		return ""
	}
}

// get the result
func (this *Info) getResult(file string) bool {
	path := filepath.Join(this.buildPath, this.sid, fmt.Sprintf("%d", this.id), file)
	if com.PathExist(path) {
		content := com.ReadFile(path)
		if result, err := strconv.Atoi(content); err != nil {
			return false
		} else {
			if result == 0 {
				return true
			} else {
				return false
			}
		}
	} else {
		return false
	}
}
<commit_msg>fixed for gather result<commit_after>package judge

import (
	"fmt"
	"github.com/gogather/com"
	"path/filepath"
	"strconv"
)

type Info struct {
	sid         string
	id          int
	buildLog    string
	buildResult int
	runLog      string
	runResult   int
	buildPath   string
}

func (this *Info) Gather(sid string, id int, buildPath string) map[string]interface{} {
	this.sid = sid
	this.id = id
	this.buildPath = buildPath

	this.buildLog = this.getLog("BUILD.LOG")
	if this.buildResult = this.getResult("BUILDRESULT"); this.buildResult == 0 {
		this.runLog = this.getLog("RUN.LOG")
		this.runResult = this.getResult("RUNRESULT")
	} else {
		this.runLog = ""
		this.runResult = -1
	}

	return map[string]interface{}{
		"build_log":    this.buildLog,
		"build_result": this.buildResult,
		"run_log":      this.runLog,
		"run_result":   this.runResult,
	}
}

func (this *Info) getLog(file string) string {
	path := filepath.Join(this.buildPath, this.sid, fmt.Sprintf("%d", this.id), file)
	if com.PathExist(path) {
		return com.ReadFile(path)
	} else {
		return ""
	}
}

// get the result
func (this *Info) getResult(file string) int {
	path := filepath.Join(this.buildPath, this.sid, fmt.Sprintf("%d", this.id), file)
	if com.PathExist(path) {
		content := com.ReadFile(path)
		content = com.Strim(content)
		if result, err := strconv.Atoi(content); err != nil {
			return result
		} else {
			return -1
		}
	} else {
		return -1
	}
}
<|endoftext|>


<commit_before>package server

import (
	"fmt"
	"github.com/boltdb/bolt"
	proto "github.com/golang/protobuf/proto"
	uuid "github.com/nu7hatch/gouuid"
	"golang.org/x/net/context"
	"google.golang.org/grpc/metadata"
	"strings"
	"tes/config"
	"tes/ga4gh"
	"time"
)

// TODO these should probably be unexported names

// TaskBucket defines the name of a bucket which maps
// job ID -> ga4gh_task_exec.Task struct
var TaskBucket = []byte("tasks")

// TaskAuthBucket defines the name of a bucket which maps
// job ID -> JWT token string
var TaskAuthBucket = []byte("tasks-auth")

// JobsQueued defines the name of a bucket which maps
// job ID -> nil
var JobsQueued = []byte("jobs-queued")

// JobState maps: job ID -> state string
var JobState = []byte("jobs-state")

// JobsLog defines the name of a bucket which maps
// job ID -> ga4gh_task_exec.JobLog struct
var JobsLog = []byte("jobs-log")

// JobWorker defines the name a bucket which maps
// job ID -> worker ID
var JobWorker = []byte("job-worker")

// Workers maps:
// worker ID -> ga4gh_task_ref.Worker struct
var Workers = []byte("workers")

// TaskBolt provides handlers for gRPC endpoints.
// Data is stored/retrieved from the BoltDB key-value database.
type TaskBolt struct {
	db           *bolt.DB
	serverConfig config.Config
}

// NewTaskBolt returns a new instance of TaskBolt, accessing the database at
// the given path, and including the given ServerConfig.
func NewTaskBolt(conf config.Config) (*TaskBolt, error) {
	db, err := bolt.Open(conf.DBPath, 0600, &bolt.Options{
		Timeout: time.Second * 5,
	})
	if err != nil {
		return nil, err
	}

	//Check to make sure all the required buckets have been created
	db.Update(func(tx *bolt.Tx) error {
		if tx.Bucket(TaskBucket) == nil {
			tx.CreateBucket(TaskBucket)
		}
		if tx.Bucket(TaskAuthBucket) == nil {
			tx.CreateBucket(TaskAuthBucket)
		}
		if tx.Bucket(JobsQueued) == nil {
			tx.CreateBucket(JobsQueued)
		}
		if tx.Bucket(JobState) == nil {
			tx.CreateBucket(JobState)
		}
		if tx.Bucket(JobsLog) == nil {
			tx.CreateBucket(JobsLog)
		}
		if tx.Bucket(Workers) == nil {
			tx.CreateBucket(Workers)
		}
		if tx.Bucket(JobWorker) == nil {
			tx.CreateBucket(JobWorker)
		}
		return nil
	})
	return &TaskBolt{db: db, serverConfig: conf}, nil
}

// ReadQueue returns a slice of queued Jobs. Up to "n" jobs are returned.
func (taskBolt *TaskBolt) ReadQueue(n int) []*ga4gh_task_exec.Job {
	jobs := make([]*ga4gh_task_exec.Job, 0)
	taskBolt.db.View(func(tx *bolt.Tx) error {

		// Iterate over the JobsQueued bucket, reading the first `n` jobs
		c := tx.Bucket(JobsQueued).Cursor()
		for k, _ := c.First(); k != nil && len(jobs) < n; k, _ = c.Next() {
			id := string(k)
			job := getJob(tx, id)
			jobs = append(jobs, job)
		}
		return nil
	})
	return jobs
}

// getJWT
// This function extracts the JWT token from the rpc header and returns the string
func getJWT(ctx context.Context) string {
	jwt := ""
	v, _ := metadata.FromContext(ctx)
	auth, ok := v["authorization"]
	if !ok {
		return jwt
	}
	for _, i := range auth {
		if strings.HasPrefix(i, "JWT ") {
			jwt = strings.TrimPrefix(i, "JWT ")
		}
	}
	return jwt
}

// RunTask documentation
// TODO: documentation
func (taskBolt *TaskBolt) RunTask(ctx context.Context, task *ga4gh_task_exec.Task) (*ga4gh_task_exec.JobID, error) {
	jobID, _ := uuid.NewV4()
	log := log.WithFields("jobID", jobID)

	log.Debug("RunTask called", "task", task)

	if len(task.Docker) == 0 {
		return nil, fmt.Errorf("No docker commands found")
	}

	// Check inputs of the task
	for _, input := range task.GetInputs() {
		diskFound := false
		for _, res := range task.Resources.Volumes {
			if strings.HasPrefix(input.Path, res.MountPoint) {
				diskFound = true
			}
		}
		if !diskFound {
			return nil, fmt.Errorf("Required volume '%s' not found in resources", input.Path)
		}
		//Fixing blank value to File by default... Is this too much hand holding?
		if input.Class == "" {
			input.Class = "File"
		}
	}

	for _, output := range task.GetOutputs() {
		if output.Class == "" {
			output.Class = "File"
		}
	}

	jwt := getJWT(ctx)
	log.Debug("JWT", "token", jwt)

	ch := make(chan *ga4gh_task_exec.JobID, 1)
	err := taskBolt.db.Update(func(tx *bolt.Tx) error {

		taskopB := tx.Bucket(TaskBucket)
		v, _ := proto.Marshal(task)
		taskopB.Put([]byte(jobID.String()), v)

		taskopA := tx.Bucket(TaskAuthBucket)
		taskopA.Put([]byte(jobID.String()), []byte(jwt))

		queueB := tx.Bucket(JobsQueued)
		queueB.Put([]byte(jobID.String()), []byte{})
		ch <- &ga4gh_task_exec.JobID{Value: jobID.String()}
		return nil
	})
	if err != nil {
		return nil, err
	}
	a := <-ch
	return a, err
}

func getJobState(tx *bolt.Tx, id string) ga4gh_task_exec.State {
	idBytes := []byte(id)
	s := tx.Bucket(JobState).Get(idBytes)
	if s == nil {
		return ga4gh_task_exec.State_Unknown
	}
	// map the string into the protobuf enum
	v := ga4gh_task_exec.State_value[string(s)]
	return ga4gh_task_exec.State(v)
}

func getJob(tx *bolt.Tx, jobID string) *ga4gh_task_exec.Job {
	bT := tx.Bucket(TaskBucket)
	v := bT.Get([]byte(jobID))
	task := &ga4gh_task_exec.Task{}
	proto.Unmarshal(v, task)

	job := ga4gh_task_exec.Job{}
	job.JobID = jobID
	job.Task = task
	job.State = getJobState(tx, jobID)
	return &job
}

func loadJobLogs(tx *bolt.Tx, job *ga4gh_task_exec.Job) {
	//if there is logging info
	bucket := tx.Bucket(JobsLog)
	out := make([]*ga4gh_task_exec.JobLog, 0)

	for i := range job.Task.Docker {
		o := bucket.Get([]byte(fmt.Sprint(job.JobID, i)))
		if o != nil {
			var log ga4gh_task_exec.JobLog
			proto.Unmarshal(o, &log)
			out = append(out, &log)
		}
	}

	job.Logs = out
}

// GetJob gets a job, which describes a running task
func (taskBolt *TaskBolt) GetJob(ctx context.Context, id *ga4gh_task_exec.JobID) (*ga4gh_task_exec.Job, error) {
	log.Debug("GetJob called", "jobID", id.Value)

	var job *ga4gh_task_exec.Job
	err := taskBolt.db.View(func(tx *bolt.Tx) error {
		job = getJob(tx, id.Value)
		loadJobLogs(tx, job)
		return nil
	})
	return job, err
}

// ListJobs returns a list of jobIDs
func (taskBolt *TaskBolt) ListJobs(ctx context.Context, in *ga4gh_task_exec.JobListRequest) (*ga4gh_task_exec.JobListResponse, error) {
	log.Debug("ListJobs called")

	jobs := make([]*ga4gh_task_exec.JobDesc, 0, 10)

	taskBolt.db.View(func(tx *bolt.Tx) error {
		taskopB := tx.Bucket(TaskBucket)
		c := taskopB.Cursor()

		for k, v := c.First(); k != nil; k, v = c.Next() {
			jobID := string(k)
			jobState := getJobState(tx, jobID)

			task := &ga4gh_task_exec.Task{}
			proto.Unmarshal(v, task)

			job := &ga4gh_task_exec.JobDesc{
				JobID: jobID,
				State: jobState,
				Task: &ga4gh_task_exec.TaskDesc{
					Name:        task.Name,
					ProjectID:   task.ProjectID,
					Description: task.Description,
				},
			}
			jobs = append(jobs, job)
		}
		return nil
	})

	out := ga4gh_task_exec.JobListResponse{
		Jobs: jobs,
	}

	return &out, nil
}

// CancelJob cancels a job
func (taskBolt *TaskBolt) CancelJob(ctx context.Context, taskop *ga4gh_task_exec.JobID) (*ga4gh_task_exec.JobID, error) {
	log := log.WithFields("jobID", taskop.Value)
	log.Info("Canceling job")

	err := taskBolt.db.Update(func(tx *bolt.Tx) error {
		return transitionJobState(tx, taskop.Value, ga4gh_task_exec.State_Canceled)
	})
	if err != nil {
		return nil, err
	}
	return taskop, nil
}

// GetServiceInfo provides an endpoint for TES clients to get information about this server.
// Could include:
// - resource availability
// - support storage systems
// - versions
// - etc.
func (taskBolt *TaskBolt) GetServiceInfo(ctx context.Context, info *ga4gh_task_exec.ServiceInfoRequest) (*ga4gh_task_exec.ServiceInfo, error) {
	//BUG: this isn't the best translation, probably lossy.
	//     Maybe ServiceInfo data structure schema needs to be refactored
	//     For example, you can't have multiple S3 endpoints
	out := map[string]string{}
	for _, i := range taskBolt.serverConfig.Storage {
		if i.Local.Valid() {
			out["Local.AllowedDirs"] = strings.Join(i.Local.AllowedDirs, ",")
		}
		if i.S3.Valid() {
			out["S3.Endpoint"] = i.S3.Endpoint
		}
	}
	return &ga4gh_task_exec.ServiceInfo{StorageConfig: out}, nil
}
<commit_msg>Fix: set job to queued state on RunTask<commit_after>package server

import (
	"fmt"
	"github.com/boltdb/bolt"
	proto "github.com/golang/protobuf/proto"
	uuid "github.com/nu7hatch/gouuid"
	"golang.org/x/net/context"
	"google.golang.org/grpc/metadata"
	"strings"
	"tes/config"
	"tes/ga4gh"
	"time"
)

// TODO these should probably be unexported names

// TaskBucket defines the name of a bucket which maps
// job ID -> ga4gh_task_exec.Task struct
var TaskBucket = []byte("tasks")

// TaskAuthBucket defines the name of a bucket which maps
// job ID -> JWT token string
var TaskAuthBucket = []byte("tasks-auth")

// JobsQueued defines the name of a bucket which maps
// job ID -> nil
var JobsQueued = []byte("jobs-queued")

// JobState maps: job ID -> state string
var JobState = []byte("jobs-state")

// JobsLog defines the name of a bucket which maps
// job ID -> ga4gh_task_exec.JobLog struct
var JobsLog = []byte("jobs-log")

// JobWorker defines the name a bucket which maps
// job ID -> worker ID
var JobWorker = []byte("job-worker")

// Workers maps:
// worker ID -> ga4gh_task_ref.Worker struct
var Workers = []byte("workers")

// TaskBolt provides handlers for gRPC endpoints.
// Data is stored/retrieved from the BoltDB key-value database.
type TaskBolt struct {
	db           *bolt.DB
	serverConfig config.Config
}

// NewTaskBolt returns a new instance of TaskBolt, accessing the database at
// the given path, and including the given ServerConfig.
func NewTaskBolt(conf config.Config) (*TaskBolt, error) {
	db, err := bolt.Open(conf.DBPath, 0600, &bolt.Options{
		Timeout: time.Second * 5,
	})
	if err != nil {
		return nil, err
	}

	//Check to make sure all the required buckets have been created
	db.Update(func(tx *bolt.Tx) error {
		if tx.Bucket(TaskBucket) == nil {
			tx.CreateBucket(TaskBucket)
		}
		if tx.Bucket(TaskAuthBucket) == nil {
			tx.CreateBucket(TaskAuthBucket)
		}
		if tx.Bucket(JobsQueued) == nil {
			tx.CreateBucket(JobsQueued)
		}
		if tx.Bucket(JobState) == nil {
			tx.CreateBucket(JobState)
		}
		if tx.Bucket(JobsLog) == nil {
			tx.CreateBucket(JobsLog)
		}
		if tx.Bucket(Workers) == nil {
			tx.CreateBucket(Workers)
		}
		if tx.Bucket(JobWorker) == nil {
			tx.CreateBucket(JobWorker)
		}
		return nil
	})
	return &TaskBolt{db: db, serverConfig: conf}, nil
}

// ReadQueue returns a slice of queued Jobs. Up to "n" jobs are returned.
func (taskBolt *TaskBolt) ReadQueue(n int) []*ga4gh_task_exec.Job {
	jobs := make([]*ga4gh_task_exec.Job, 0)
	taskBolt.db.View(func(tx *bolt.Tx) error {

		// Iterate over the JobsQueued bucket, reading the first `n` jobs
		c := tx.Bucket(JobsQueued).Cursor()
		for k, _ := c.First(); k != nil && len(jobs) < n; k, _ = c.Next() {
			id := string(k)
			job := getJob(tx, id)
			jobs = append(jobs, job)
		}
		return nil
	})
	return jobs
}

// getJWT
// This function extracts the JWT token from the rpc header and returns the string
func getJWT(ctx context.Context) string {
	jwt := ""
	v, _ := metadata.FromContext(ctx)
	auth, ok := v["authorization"]
	if !ok {
		return jwt
	}
	for _, i := range auth {
		if strings.HasPrefix(i, "JWT ") {
			jwt = strings.TrimPrefix(i, "JWT ")
		}
	}
	return jwt
}

// RunTask documentation
// TODO: documentation
func (taskBolt *TaskBolt) RunTask(ctx context.Context, task *ga4gh_task_exec.Task) (*ga4gh_task_exec.JobID, error) {
	jobID, _ := uuid.NewV4()
	log := log.WithFields("jobID", jobID)

	log.Debug("RunTask called", "task", task)

	if len(task.Docker) == 0 {
		return nil, fmt.Errorf("No docker commands found")
	}

	// Check inputs of the task
	for _, input := range task.GetInputs() {
		diskFound := false
		for _, res := range task.Resources.Volumes {
			if strings.HasPrefix(input.Path, res.MountPoint) {
				diskFound = true
			}
		}
		if !diskFound {
			return nil, fmt.Errorf("Required volume '%s' not found in resources", input.Path)
		}
		//Fixing blank value to File by default... Is this too much hand holding?
		if input.Class == "" {
			input.Class = "File"
		}
	}

	for _, output := range task.GetOutputs() {
		if output.Class == "" {
			output.Class = "File"
		}
	}

	jwt := getJWT(ctx)
	log.Debug("JWT", "token", jwt)

	ch := make(chan *ga4gh_task_exec.JobID, 1)
	err := taskBolt.db.Update(func(tx *bolt.Tx) error {
		idBytes := []byte(jobID.String())

		taskopB := tx.Bucket(TaskBucket)
		v, _ := proto.Marshal(task)
		taskopB.Put(idBytes, v)

		tx.Bucket(JobState).Put(idBytes, []byte(ga4gh_task_exec.State_Queued.String()))

		taskopA := tx.Bucket(TaskAuthBucket)
		taskopA.Put(idBytes, []byte(jwt))

		queueB := tx.Bucket(JobsQueued)
		queueB.Put(idBytes, []byte{})
		ch <- &ga4gh_task_exec.JobID{Value: jobID.String()}
		return nil
	})
	if err != nil {
		return nil, err
	}
	a := <-ch
	return a, err
}

func getJobState(tx *bolt.Tx, id string) ga4gh_task_exec.State {
	idBytes := []byte(id)
	s := tx.Bucket(JobState).Get(idBytes)
	if s == nil {
		return ga4gh_task_exec.State_Unknown
	}
	// map the string into the protobuf enum
	v := ga4gh_task_exec.State_value[string(s)]
	return ga4gh_task_exec.State(v)
}

func getJob(tx *bolt.Tx, jobID string) *ga4gh_task_exec.Job {
	bT := tx.Bucket(TaskBucket)
	v := bT.Get([]byte(jobID))
	task := &ga4gh_task_exec.Task{}
	proto.Unmarshal(v, task)

	job := ga4gh_task_exec.Job{}
	job.JobID = jobID
	job.Task = task
	job.State = getJobState(tx, jobID)
	return &job
}

func loadJobLogs(tx *bolt.Tx, job *ga4gh_task_exec.Job) {
	//if there is logging info
	bucket := tx.Bucket(JobsLog)
	out := make([]*ga4gh_task_exec.JobLog, 0)

	for i := range job.Task.Docker {
		o := bucket.Get([]byte(fmt.Sprint(job.JobID, i)))
		if o != nil {
			var log ga4gh_task_exec.JobLog
			proto.Unmarshal(o, &log)
			out = append(out, &log)
		}
	}

	job.Logs = out
}

// GetJob gets a job, which describes a running task
func (taskBolt *TaskBolt) GetJob(ctx context.Context, id *ga4gh_task_exec.JobID) (*ga4gh_task_exec.Job, error) {
	log.Debug("GetJob called", "jobID", id.Value)

	var job *ga4gh_task_exec.Job
	err := taskBolt.db.View(func(tx *bolt.Tx) error {
		job = getJob(tx, id.Value)
		loadJobLogs(tx, job)
		return nil
	})
	return job, err
}

// ListJobs returns a list of jobIDs
func (taskBolt *TaskBolt) ListJobs(ctx context.Context, in *ga4gh_task_exec.JobListRequest) (*ga4gh_task_exec.JobListResponse, error) {
	log.Debug("ListJobs called")

	jobs := make([]*ga4gh_task_exec.JobDesc, 0, 10)

	taskBolt.db.View(func(tx *bolt.Tx) error {
		taskopB := tx.Bucket(TaskBucket)
		c := taskopB.Cursor()

		for k, v := c.First(); k != nil; k, v = c.Next() {
			jobID := string(k)
			jobState := getJobState(tx, jobID)

			task := &ga4gh_task_exec.Task{}
			proto.Unmarshal(v, task)

			job := &ga4gh_task_exec.JobDesc{
				JobID: jobID,
				State: jobState,
				Task: &ga4gh_task_exec.TaskDesc{
					Name:        task.Name,
					ProjectID:   task.ProjectID,
					Description: task.Description,
				},
			}
			jobs = append(jobs, job)
		}
		return nil
	})

	out := ga4gh_task_exec.JobListResponse{
		Jobs: jobs,
	}

	return &out, nil
}

// CancelJob cancels a job
func (taskBolt *TaskBolt) CancelJob(ctx context.Context, taskop *ga4gh_task_exec.JobID) (*ga4gh_task_exec.JobID, error) {
	log := log.WithFields("jobID", taskop.Value)
	log.Info("Canceling job")

	err := taskBolt.db.Update(func(tx *bolt.Tx) error {
		return transitionJobState(tx, taskop.Value, ga4gh_task_exec.State_Canceled)
	})
	if err != nil {
		return nil, err
	}
	return taskop, nil
}

// GetServiceInfo provides an endpoint for TES clients to get information about this server.
// Could include:
// - resource availability
// - support storage systems
// - versions
// - etc.
func (taskBolt *TaskBolt) GetServiceInfo(ctx context.Context, info *ga4gh_task_exec.ServiceInfoRequest) (*ga4gh_task_exec.ServiceInfo, error) {
	//BUG: this isn't the best translation, probably lossy.
	//     Maybe ServiceInfo data structure schema needs to be refactored
	//     For example, you can't have multiple S3 endpoints
	out := map[string]string{}
	for _, i := range taskBolt.serverConfig.Storage {
		if i.Local.Valid() {
			out["Local.AllowedDirs"] = strings.Join(i.Local.AllowedDirs, ",")
		}
		if i.S3.Valid() {
			out["S3.Endpoint"] = i.S3.Endpoint
		}
	}
	return &ga4gh_task_exec.ServiceInfo{StorageConfig: out}, nil
}
<|endoftext|>