<commit_before>package main

import (
	"context"
	"fmt"
	"time"

	"github.com/brotherlogic/goserver/utils"

	dpb "github.com/brotherlogic/discovery/proto"
	pbgs "github.com/brotherlogic/goserver/proto"
)

func (s *Server) trackUpTime(ctx context.Context) error {
	conn, err := s.DialMaster("discover")
	if err != nil {
		return err
	}
	defer conn.Close()

	client := pbgs.NewGoserverServiceClient(conn)
	state, err := client.State(ctx, &pbgs.Empty{})

	if err != nil {
		return err
	}

	for _, st := range state.GetStates() {
		if st.Key == "startup_time" {
			s.discoverStartup = time.Unix(st.TimeValue, 0)
			return nil
		}
	}

	return fmt.Errorf("No change made: %v -> %v", err, state)
}

func (s *Server) runOnChange() error {
	ctx, cancel := utils.ManualContext("gbsrr", "gbsrr", time.Minute, false)
	defer cancel()
	s.Log(fmt.Sprintf("Resyncing"))
	_, err := s.Reregister(ctx, &pbgs.ReregisterRequest{})
	if err != nil {
		return err
	}
	for _, job := range s.njobs {
		if job.Port > 0 {
			conn, err := s.FDial(fmt.Sprintf("%v:%v", s.Registry.Ip, job.Port))
			if err != nil {
				s.Log(fmt.Sprintf("Cannot dial %v,%v -> %v", s.Registry.Ip, job.Port, err))
				break
			}
			defer conn.Close()
			client := pbgs.NewGoserverServiceClient(conn)
			_, err = client.Reregister(ctx, &pbgs.ReregisterRequest{})
			if err != nil {
				s.Log(fmt.Sprintf("Reregister failed: %v", err))
			}
		}
	}

	return nil
}

func (s *Server) unregisterChildren() error {
	ctx, cancel := utils.BuildContext("gobuildslave-unreg", "gobuildslave")
	defer cancel()

	conn, err := s.DoDial(&dpb.RegistryEntry{Ip: utils.RegistryIP, Port: utils.RegistryPort})
	if err != nil {
		return err
	}
	defer conn.Close()

	client := dpb.NewDiscoveryServiceV2Client(conn)
	_, err = client.Unregister(ctx, &dpb.UnregisterRequest{Service: &dpb.RegistryEntry{Identifier: s.Registry.Identifier}})

	return err
}
<commit_msg>Adds some better logging<commit_after>package main

import (
	"context"
	"fmt"
	"time"

	pb "github.com/brotherlogic/gobuildslave/proto"
	"github.com/brotherlogic/goserver/utils"

	dpb "github.com/brotherlogic/discovery/proto"
	pbgs "github.com/brotherlogic/goserver/proto"
)

func (s *Server) trackUpTime(ctx context.Context) error {
	conn, err := s.DialMaster("discover")
	if err != nil {
		return err
	}
	defer conn.Close()

	client := pbgs.NewGoserverServiceClient(conn)
	state, err := client.State(ctx, &pbgs.Empty{})

	if err != nil {
		return err
	}

	for _, st := range state.GetStates() {
		if st.Key == "startup_time" {
			s.discoverStartup = time.Unix(st.TimeValue, 0)
			return nil
		}
	}

	return fmt.Errorf("No change made: %v -> %v", err, state)
}

func (s *Server) runOnChange() error {
	ctx, cancel := utils.ManualContext("gbsrr", "gbsrr", time.Minute, false)
	defer cancel()

	var jj *pb.JobAssignment
	if len(s.njobs) > 0 {
		for _, val := range s.njobs {
			jj = val
		}
	}
	s.Log(fmt.Sprintf("Resyncing on %v jobs (First is %v)", len(s.njobs), jj))
	_, err := s.Reregister(ctx, &pbgs.ReregisterRequest{})
	if err != nil {
		return err
	}
	for _, job := range s.njobs {
		if job.Port > 0 {
			conn, err := s.FDial(fmt.Sprintf("%v:%v", s.Registry.Ip, job.Port))
			if err != nil {
				s.Log(fmt.Sprintf("Cannot dial %v,%v -> %v", s.Registry.Ip, job.Port, err))
				break
			}
			defer conn.Close()
			client := pbgs.NewGoserverServiceClient(conn)
			_, err = client.Reregister(ctx, &pbgs.ReregisterRequest{})
			if err != nil {
				s.Log(fmt.Sprintf("Reregister failed: %v", err))
			}
		}
	}

	return nil
}

func (s *Server) unregisterChildren() error {
	ctx, cancel := utils.BuildContext("gobuildslave-unreg", "gobuildslave")
	defer cancel()

	conn, err := s.DoDial(&dpb.RegistryEntry{Ip: utils.RegistryIP, Port: utils.RegistryPort})
	if err != nil {
		return err
	}
	defer conn.Close()

	client := dpb.NewDiscoveryServiceV2Client(conn)
	_, err = client.Unregister(ctx, &dpb.UnregisterRequest{Service: &dpb.RegistryEntry{Identifier: s.Registry.Identifier}})

	return err
}
<|endoftext|>


<commit_before>package main

import (
    "archive/tar"
    "bufio"
    "compress/gzip"
    "fmt"
    "io"
    "net/http"
    "os"
    "regexp"
    "sort"
    "strings"
)

const URL = "https://storage.googleapis.com/golang/"

type Interface struct {
    Name       string
    Package    string
    SourceFile string
    LineNumber int
}

type ByName []Interface

func (b ByName) Len() int           {return len(b)}
func (b ByName) Swap(i, j int)      {b[i], b[j] = b[j], b[i]}
func (b ByName) Less(i, j int) bool {return b[i].Name < b[j].Name}

func parseSourceFile(filename string, source io.Reader) []Interface {
    regexpPackage := regexp.MustCompile(`package\s+(.*)`)
    regexpInterface := regexp.MustCompile(`\s*type\s+([A-Z]\w*)\s+interface\s+{`)
    interfaces := make([]Interface, 0)
    reader := bufio.NewReader(source)
    var pack []byte
    lineNumber := 0
    for {
        line, err := reader.ReadBytes('\n')
        if err != nil && err != io.EOF {
            panic("Error parsing source file")
        }
        packageMatch := regexpPackage.FindSubmatch(line)
        if len(packageMatch) > 0 {
            pack = packageMatch[1]
        } else {
            matches := regexpInterface.FindSubmatch(line)
            if len(matches) > 0 {
                interf := Interface {
                    Name:       string(matches[1]),
                    Package:    string(pack),
                    SourceFile: filename,
                    LineNumber: lineNumber,
                }
                interfaces = append(interfaces, interf)
            }
        }
        if err == io.EOF {
            break
        }
        lineNumber += 1
    }
    return interfaces
}

func main() {
    // read version on command line
    if len(os.Args) != 2 {
        panic("Must pass go version on command line")
    }
    version := os.Args[1]
    // download compressed archive
    fmt.Println("Downloading archive...")
    response, err := http.Get(URL+"go"+version+".src.tar.gz")
    if err!=nil {
        panic(err)
    }
    defer response.Body.Close()
    // gunzip the archive stream
    gzipReader, err := gzip.NewReader(response.Body)
    if err != nil {
        panic(err)
    }
    // parse tar source files in go/src/pkg
    fmt.Println("Parsing archive...")
    tarReader := tar.NewReader(gzipReader)
    interfaces := make([]Interface, 0)
    for {
        header, err := tarReader.Next()
        if err != nil {
            break
        }
        if strings.HasPrefix(header.Name, "go/src/pkg") &&
           strings.HasSuffix(header.Name, ".go") &&
           !strings.HasSuffix(header.Name, "doc.go") {
            newInterfaces := parseSourceFile(header.Name, tarReader)
            interfaces = append(interfaces, newInterfaces...)
        }
    }
    // print the result
    sort.Sort(ByName(interfaces))
    for _, interf := range interfaces {
        println(interf.Name)
    }
}
<commit_msg>Done print<commit_after>package main

import (
    "archive/tar"
    "bufio"
    "compress/gzip"
    "fmt"
    "io"
    "net/http"
    "os"
    "regexp"
    "sort"
    "strconv"
    "strings"
)

const URL = "https://storage.googleapis.com/golang/"

type Interface struct {
    Name       string
    Package    string
    SourceFile string
    LineNumber string
}

type ByName []Interface

func (b ByName) Len() int           {return len(b)}
func (b ByName) Swap(i, j int)      {b[i], b[j] = b[j], b[i]}
func (b ByName) Less(i, j int) bool {return b[i].Name < b[j].Name}

func parseSourceFile(filename string, source io.Reader) []Interface {
    regexpInterface := regexp.MustCompile(`\s*type\s+([A-Z]\w*)\s+interface\s+{`)
    interfaces := make([]Interface, 0)
    reader := bufio.NewReader(source)
    pack := filename[11:strings.LastIndex(filename, "/")]
    lineNumber := 1
    for {
        line, err := reader.ReadBytes('\n')
        if err != nil && err != io.EOF {
            panic("Error parsing source file")
        }
        matches := regexpInterface.FindSubmatch(line)
        if len(matches) > 0 {
            interf := Interface {
                Name:       string(matches[1]),
                Package:    string(pack),
                SourceFile: filename,
                LineNumber: strconv.Itoa(lineNumber),
            }
            interfaces = append(interfaces, interf)
        }
        if err == io.EOF {
            break
        }
        lineNumber += 1
    }
    return interfaces
}

func printInterfaces(interfaces []Interface) {
    lenName := 0
    lenPackage := 0
    lenSourceFile := 0
    lenLineNumber := 0
    for _, i := range interfaces {
        if len(i.Name) > lenName {
            lenName = len(i.Name)
        }
        if len(i.Package) > lenPackage {
            lenPackage = len(i.Package)
        }
        if len(i.SourceFile) > lenSourceFile {
            lenSourceFile = len(i.SourceFile)
        }
        if len(i.LineNumber) > lenLineNumber {
            lenLineNumber = len(i.LineNumber)
        }
    }
    formatLine := "| %-" + strconv.Itoa(lenName) + "s | %-" + strconv.Itoa(lenPackage) +
        "s | %-" + strconv.Itoa(lenSourceFile) + "s | %-" + strconv.Itoa(lenLineNumber) +
        "s |\n"
    fmt.Printf(formatLine, "Interface", "Package", "Source File", "Line")
    separator := "|" + strings.Repeat("-", lenName+2) + "|" + strings.Repeat("-", lenPackage+2) +
        "|" + strings.Repeat("-", lenSourceFile+2) + "|" + strings.Repeat("-", lenLineNumber+2) + "|"
    fmt.Println(separator)
    for _, i := range interfaces {
        fmt.Printf(formatLine, i.Name, i.Package, i.SourceFile, i.LineNumber)
    }
}

func main() {
    // read version on command line
    if len(os.Args) != 2 {
        panic("Must pass go version on command line")
    }
    version := os.Args[1]
    // download compressed archive
    println("Downloading archive...")
    response, err := http.Get(URL+"go"+version+".src.tar.gz")
    if err!=nil {
        panic(err)
    }
    defer response.Body.Close()
    // gunzip the archive stream
    gzipReader, err := gzip.NewReader(response.Body)
    if err != nil {
        panic(err)
    }
    // parse tar source files in go/src/pkg
    println("Parsing archive...")
    tarReader := tar.NewReader(gzipReader)
    interfaces := make([]Interface, 0)
    for {
        header, err := tarReader.Next()
        if err != nil {
            break
        }
        if strings.HasPrefix(header.Name, "go/src/pkg") &&
           strings.HasSuffix(header.Name, ".go") &&
           !strings.HasSuffix(header.Name, "doc.go") {
            newInterfaces := parseSourceFile(header.Name, tarReader)
            interfaces = append(interfaces, newInterfaces...)
        }
    }
    // print the result
    sort.Sort(ByName(interfaces))
    printInterfaces(interfaces)
}
<|endoftext|>


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

package main

import (
	"bytes"
	"flag"
	"fmt"
	"go/build"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"os/exec"
	"path/filepath"
	"regexp"
	"runtime"
	"strconv"
	"strings"
	"sync"

	// Imports so that goinstall automatically installs them.
	_ "code.google.com/p/go-tour/pic"
	_ "code.google.com/p/go-tour/tree"
	_ "code.google.com/p/go-tour/wc"
)

const basePkg = "code.google.com/p/go-tour/"

var (
	httpListen = flag.String("http", "127.0.0.1:3999", "host:port to listen on")
	htmlOutput = flag.Bool("html", false, "render program output as HTML")
)

var (
	// a source of numbers, for naming temporary files
	uniq = make(chan int)
)

func main() {
	flag.Parse()

	// source of unique numbers
	go func() {
		for i := 0; ; i++ {
			uniq <- i
		}
	}()

	// find and serve the go tour files
	p, err := build.Default.Import(basePkg, "", build.FindOnly)
	if err != nil {
		log.Fatalf("Couldn't find tour files: %v", err)
	}
	root := p.Dir
	log.Println("Serving content from", root)
	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		if r.URL.Path == "/favicon.ico" || r.URL.Path == "/" {
			fn := filepath.Join(root, "static", r.URL.Path[1:])
			http.ServeFile(w, r, fn)
			return
		}
		http.Error(w, "not found", 404)
	})
	http.Handle("/static/", http.FileServer(http.Dir(root)))
	http.HandleFunc("/kill", kill)

	if !strings.HasPrefix(*httpListen, "127.0.0.1") &&
		!strings.HasPrefix(*httpListen, "localhost") {
		log.Print(localhostWarning)
	}

	log.Printf("Serving at http://%s/", *httpListen)
	log.Fatal(http.ListenAndServe(*httpListen, nil))
}

const localhostWarning = `
WARNING!  WARNING!  WARNING!

I appear to be listening on an address that is not localhost.
Anyone with access to this address and port will have access
to this machine as the user running gotour.

If you don't understand this message, hit Control-C to terminate this process.

WARNING!  WARNING!  WARNING!
`

var running struct {
	sync.Mutex
	cmd *exec.Cmd
}

func stopRun() {
	running.Lock()
	if running.cmd != nil {
		running.cmd.Process.Kill()
		running.cmd = nil
	}
	running.Unlock()
}

func kill(w http.ResponseWriter, r *http.Request) {
	stopRun()
}

var (
	commentRe = regexp.MustCompile(`(?m)^#.*\n`)
	tmpdir    string
)

func init() {
	// find real temporary directory (for rewriting filename in output)
	var err error
	tmpdir, err = filepath.EvalSymlinks(os.TempDir())
	if err != nil {
		log.Fatal(err)
	}
}

func compile(req *http.Request) (out []byte, err error) {
	stopRun()

	// x is the base name for .go, .6, executable files
	x := filepath.Join(tmpdir, "compile"+strconv.Itoa(<-uniq))
	src := x + ".go"
	bin := x
	if runtime.GOOS == "windows" {
		bin += ".exe"
	}

	// rewrite filename in error output
	defer func() {
		if err != nil {
			// drop messages from the go tool like '# _/compile0'
			out = commentRe.ReplaceAll(out, nil)
		}
		out = bytes.Replace(out, []byte(src+":"), []byte("main.go:"), -1)
	}()

	// write body to x.go
	body := []byte(req.FormValue("body"))
	defer os.Remove(src)
	if err = ioutil.WriteFile(src, body, 0666); err != nil {
		return
	}

	// build x.go, creating x
	dir, file := filepath.Split(src)
	out, err = run(dir, "go", "build", "-o", bin, file)
	defer os.Remove(bin)
	if err != nil {
		return
	}

	// run x
	return run("", bin)
}

// run executes the specified command and returns its output and an error.
func run(dir string, args ...string) ([]byte, error) {
	var buf bytes.Buffer
	cmd := exec.Command(args[0], args[1:]...)
	cmd.Dir = dir
	cmd.Stdout = &buf
	cmd.Stderr = cmd.Stdout

	// Start command and leave in 'running'.
	running.Lock()
	if running.cmd != nil {
		defer running.Unlock()
		return nil, fmt.Errorf("already running %s", running.cmd.Path)
	}
	if err := cmd.Start(); err != nil {
		running.Unlock()
		return nil, err
	}
	running.cmd = cmd
	running.Unlock()

	// Wait for the command.  Clean up,
	err := cmd.Wait()
	running.Lock()
	if running.cmd == cmd {
		running.cmd = nil
	}
	running.Unlock()
	return buf.Bytes(), err
}
<commit_msg>gotour: make the console messages more descriptive<commit_after>// Copyright 2011 The Go Authors.  All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package main

import (
	"bytes"
	"flag"
	"fmt"
	"go/build"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"os/exec"
	"path/filepath"
	"regexp"
	"runtime"
	"strconv"
	"strings"
	"sync"

	// Imports so that goinstall automatically installs them.
	_ "code.google.com/p/go-tour/pic"
	_ "code.google.com/p/go-tour/tree"
	_ "code.google.com/p/go-tour/wc"
)

const basePkg = "code.google.com/p/go-tour/"

var (
	httpListen = flag.String("http", "127.0.0.1:3999", "host:port to listen on")
	htmlOutput = flag.Bool("html", false, "render program output as HTML")
)

var (
	// a source of numbers, for naming temporary files
	uniq = make(chan int)
)

func main() {
	flag.Parse()

	// source of unique numbers
	go func() {
		for i := 0; ; i++ {
			uniq <- i
		}
	}()

	// find and serve the go tour files
	p, err := build.Default.Import(basePkg, "", build.FindOnly)
	if err != nil {
		log.Fatalf("Couldn't find tour files: %v", err)
	}
	root := p.Dir
	log.Println("Serving content from", root)
	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		if r.URL.Path == "/favicon.ico" || r.URL.Path == "/" {
			fn := filepath.Join(root, "static", r.URL.Path[1:])
			http.ServeFile(w, r, fn)
			return
		}
		http.Error(w, "not found", 404)
	})
	http.Handle("/static/", http.FileServer(http.Dir(root)))
	http.HandleFunc("/kill", kill)

	if !strings.HasPrefix(*httpListen, "127.0.0.1") &&
		!strings.HasPrefix(*httpListen, "localhost") {
		log.Print(localhostWarning)
	}

	log.Printf("Open your web browser and visit http://%s/", *httpListen)
	log.Fatal(http.ListenAndServe(*httpListen, nil))
}

const localhostWarning = `
WARNING!  WARNING!  WARNING!

I appear to be listening on an address that is not localhost.
Anyone with access to this address and port will have access
to this machine as the user running gotour.

If you don't understand this message, hit Control-C to terminate this process.

WARNING!  WARNING!  WARNING!
`

var running struct {
	sync.Mutex
	cmd *exec.Cmd
}

func stopRun() {
	running.Lock()
	if running.cmd != nil {
		running.cmd.Process.Kill()
		running.cmd = nil
	}
	running.Unlock()
}

func kill(w http.ResponseWriter, r *http.Request) {
	stopRun()
}

var (
	commentRe = regexp.MustCompile(`(?m)^#.*\n`)
	tmpdir    string
)

func init() {
	// find real temporary directory (for rewriting filename in output)
	var err error
	tmpdir, err = filepath.EvalSymlinks(os.TempDir())
	if err != nil {
		log.Fatal(err)
	}
}

func compile(req *http.Request) (out []byte, err error) {
	stopRun()

	// x is the base name for .go, .6, executable files
	x := filepath.Join(tmpdir, "compile"+strconv.Itoa(<-uniq))
	src := x + ".go"
	bin := x
	if runtime.GOOS == "windows" {
		bin += ".exe"
	}

	// rewrite filename in error output
	defer func() {
		if err != nil {
			// drop messages from the go tool like '# _/compile0'
			out = commentRe.ReplaceAll(out, nil)
		}
		out = bytes.Replace(out, []byte(src+":"), []byte("main.go:"), -1)
	}()

	// write body to x.go
	body := []byte(req.FormValue("body"))
	defer os.Remove(src)
	if err = ioutil.WriteFile(src, body, 0666); err != nil {
		return
	}

	// build x.go, creating x
	dir, file := filepath.Split(src)
	out, err = run(dir, "go", "build", "-o", bin, file)
	defer os.Remove(bin)
	if err != nil {
		return
	}

	// run x
	return run("", bin)
}

// run executes the specified command and returns its output and an error.
func run(dir string, args ...string) ([]byte, error) {
	var buf bytes.Buffer
	cmd := exec.Command(args[0], args[1:]...)
	cmd.Dir = dir
	cmd.Stdout = &buf
	cmd.Stderr = cmd.Stdout

	// Start command and leave in 'running'.
	running.Lock()
	if running.cmd != nil {
		defer running.Unlock()
		return nil, fmt.Errorf("already running %s", running.cmd.Path)
	}
	if err := cmd.Start(); err != nil {
		running.Unlock()
		return nil, err
	}
	running.cmd = cmd
	running.Unlock()

	// Wait for the command.  Clean up,
	err := cmd.Wait()
	running.Lock()
	if running.cmd == cmd {
		running.cmd = nil
	}
	running.Unlock()
	return buf.Bytes(), err
}
<|endoftext|>


<commit_before>package gottyclient

import (
	"bufio"
	"fmt"
	"net/http"
	"net/url"
	"os"
	"strings"
	"unicode/utf8"

	"github.com/Sirupsen/logrus"
	"github.com/gorilla/websocket"
	"golang.org/x/crypto/ssh/terminal"
)

// GetWebsocketURL transforms a GoTTY http URL to its WebSocket URL
func GetWebsocketURL(httpURL string) (*url.URL, error) {
	target, err := url.Parse(httpURL)
	if err != nil {
		return nil, err
	}

	if target.Scheme == "https" {
		target.Scheme = "wss"
	} else {
		target.Scheme = "ws"
	}

	target.Path = strings.TrimLeft(target.Path+"ws", "/")

	return target, nil
}

type Client struct {
	Dialer    *websocket.Dialer
	Conn      *websocket.Conn
	Headers   http.Header
	Target    string
	Connected bool
}

// Connect tries to dial a websocket server
func (c *Client) Connect() error {
	conn, _, err := c.Dialer.Dial(c.Target, c.Headers)
	if err != nil {
		return err
	}
	c.Conn = conn
	return nil
}

// Close will nicely close the dialer
func (c *Client) Close() {
	c.Conn.Close()
}

// Loop will look indefinitely for new messages
func (c *Client) Loop() error {
	if !c.Connected {
		err := c.Connect()
		if err != nil {
			return err
		}
	}

	done := make(chan bool)
	go c.readLoop(done)
	go c.writeLoop(done)
	<-done
	return nil
}

func (c *Client) writeLoop(done chan bool) {
	oldState, err := terminal.MakeRaw(0)
	if err == nil {
		defer terminal.Restore(0, oldState)
	}

	reader := bufio.NewReader(os.Stdin)
	for {
		x, size, err := reader.ReadRune()
		if size <= 0 || err != nil {
			done <- true
			return
		}

		p := make([]byte, size)
		utf8.EncodeRune(p, x)

		err = c.Conn.WriteMessage(websocket.TextMessage, append([]byte("0"), p...))
		if err != nil {
			done <- true
			return
		}
	}
}

func (c *Client) readLoop(done chan bool) {
	for {
		_, data, err := c.Conn.ReadMessage()
		if err != nil {
			done <- true
			logrus.Warnf("c.Conn.ReadMessage: %v", err)
			return
		}

		switch data[0] {
		case '0': // data
			fmt.Print(string(data[1:]))
		case '1': // new title
			newTitle := string(data[1:])
			fmt.Printf("\033]0;%s\007", newTitle)
		case '2': // json prefs
			logrus.Warnf("Unhandled protocol message: json pref: %s", string(data))
		default:
			logrus.Warnf("Unhandled protocol message: %s", string(data))
		}
	}
}

// NewClient returns a GoTTY client object
func NewClient(httpURL string) (*Client, error) {
	target, err := GetWebsocketURL(httpURL)
	if err != nil {
		return nil, err
	}

	return &Client{
		Dialer: &websocket.Dialer{},
		Target: target.String(),
	}, nil
}
<commit_msg>Handling tty size<commit_after>package gottyclient

import (
	"bufio"
	"encoding/json"
	"fmt"
	"net/http"
	"net/url"
	"os"
	"os/signal"
	"strings"
	"syscall"
	"unicode/utf8"
	"unsafe"

	"github.com/Sirupsen/logrus"
	"github.com/gorilla/websocket"
	"golang.org/x/crypto/ssh/terminal"
)

// GetWebsocketURL transforms a GoTTY http URL to its WebSocket URL
func GetWebsocketURL(httpURL string) (*url.URL, error) {
	target, err := url.Parse(httpURL)
	if err != nil {
		return nil, err
	}

	if target.Scheme == "https" {
		target.Scheme = "wss"
	} else {
		target.Scheme = "ws"
	}

	target.Path = strings.TrimLeft(target.Path+"ws", "/")

	return target, nil
}

type Client struct {
	Dialer    *websocket.Dialer
	Conn      *websocket.Conn
	Headers   http.Header
	Target    string
	Connected bool
}

// Connect tries to dial a websocket server
func (c *Client) Connect() error {
	conn, _, err := c.Dialer.Dial(c.Target, c.Headers)
	if err != nil {
		return err
	}
	c.Conn = conn
	return nil
}

// Close will nicely close the dialer
func (c *Client) Close() {
	c.Conn.Close()
}

// Loop will look indefinitely for new messages
func (c *Client) Loop() error {
	if !c.Connected {
		err := c.Connect()
		if err != nil {
			return err
		}
	}

	done := make(chan bool)
	go c.readLoop(done)
	go c.writeLoop(done)
	go c.termsizeLoop(done)
	<-done
	return nil
}

type winsize struct {
	Rows    uint16 `json:"rows"`
	Columns uint16 `json:"columns"`
	// unused
	x uint16
	y uint16
}

func (c *Client) termsizeLoop(done chan bool) {
	ch := make(chan os.Signal, 1)
	signal.Notify(ch, syscall.SIGWINCH)
	ws := winsize{}

	for {
		syscall.Syscall(syscall.SYS_IOCTL,
			uintptr(0), uintptr(syscall.TIOCGWINSZ),
			uintptr(unsafe.Pointer(&ws)))

		b, err := json.Marshal(ws)
		if err != nil {
			logrus.Warnf("json.Marshal error: %v", err)
		}

		err = c.Conn.WriteMessage(websocket.TextMessage, append([]byte("1"), b...))
		if err != nil {
			logrus.Warnf("ws.WriteMessage failed: %v", err)
		}

		<-ch
	}
}

func (c *Client) writeLoop(done chan bool) {
	oldState, err := terminal.MakeRaw(0)
	if err == nil {
		defer terminal.Restore(0, oldState)
	}

	reader := bufio.NewReader(os.Stdin)
	for {
		x, size, err := reader.ReadRune()
		if size <= 0 || err != nil {
			done <- true
			return
		}

		p := make([]byte, size)
		utf8.EncodeRune(p, x)

		err = c.Conn.WriteMessage(websocket.TextMessage, append([]byte("0"), p...))
		if err != nil {
			done <- true
			return
		}
	}
}

func (c *Client) readLoop(done chan bool) {
	for {
		_, data, err := c.Conn.ReadMessage()
		if err != nil {
			done <- true
			logrus.Warnf("c.Conn.ReadMessage: %v", err)
			return
		}

		switch data[0] {
		case '0': // data
			fmt.Print(string(data[1:]))
		case '1': // new title
			newTitle := string(data[1:])
			fmt.Printf("\033]0;%s\007", newTitle)
		case '2': // json prefs
			logrus.Warnf("Unhandled protocol message: json pref: %s", string(data))
		default:
			logrus.Warnf("Unhandled protocol message: %s", string(data))
		}
	}
}

// NewClient returns a GoTTY client object
func NewClient(httpURL string) (*Client, error) {
	target, err := GetWebsocketURL(httpURL)
	if err != nil {
		return nil, err
	}

	return &Client{
		Dialer: &websocket.Dialer{},
		Target: target.String(),
	}, nil
}
<|endoftext|>


<commit_before>package grayt

import (
	"context"
	"encoding/json"
	"fmt"
	"image"
	"image/png"
	"io/ioutil"
	"log"
	"net/http"
	_ "net/http/pprof"
	"strconv"
	"sync"
	"sync/atomic"
	"time"
)

func ListenAndServe(addr string) error {
	http.Handle("/", http.FileServer(http.Dir("assets")))
	http.HandleFunc("/scenes", middleware(handleGetScenesCollection))
	http.HandleFunc("/renders", middleware(handlePostRendersCollection))

	log.Printf("Listening for HTTP on %v", addr)
	return http.ListenAndServe(addr, nil)
}

func middleware(fn http.HandlerFunc) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		start := time.Now()
		log.Printf(`info="handling request" method=%q path=%q`, r.Method, r.URL.Path)
		fn(w, r)
		log.Printf(`info="finished" duration=%q`, time.Since(start))
	}
}

/*
	POST  /renders                    - Adds a new render resource, not started, with default settings.
	GET   /renders/{uuid}             - Gets all information about the render resource.
	PUT   /renders/{uuid}/{property}  - Sets property of the render.
	GET   /renders/{uuid}/image       - Creates an image.
*/

type resource struct {
	sync.Mutex
	uuid   string
	render *render
	cancel func() // set to nil if the render isn't running

	sceneFunc func() Scene
	sceneName string
}

func writeError(w http.ResponseWriter, status int) {
	http.Error(w, http.StatusText(status), status)
}

func internalError(w http.ResponseWriter, err error) {
	http.Error(w, err.Error(), http.StatusInternalServerError)
}

func handleGetScenesCollection(w http.ResponseWriter, r *http.Request) {
	var ss []string
	for s := range scenes {
		ss = append(ss, s)
	}
	if err := json.NewEncoder(w).Encode(ss); err != nil {
		internalError(w, err)
	}
}

var lastUUID int

func handlePostRendersCollection(w http.ResponseWriter, r *http.Request) {
	if r.Method != http.MethodPost {
		writeError(w, http.StatusMethodNotAllowed)
		return
	}

	// TODO: Switch to proper UUIDs once the UI is up and running (while using
	// cURL for interaction, deterministic UUIDs are nice).
	lastUUID++
	uuid := fmt.Sprintf("%d", lastUUID)

	rsrc := &resource{uuid: uuid}

	fmt.Fprintf(w, `{"uuid":%q}`, uuid)

	http.HandleFunc("/renders/"+uuid, middleware(rsrc.handleGetAll))
	http.HandleFunc("/renders/"+uuid+"/image", middleware(rsrc.handleGetImage))
	http.HandleFunc("/renders/"+uuid+"/scene", middleware(rsrc.handlePutScene))
	http.HandleFunc("/renders/"+uuid+"/running", middleware(rsrc.handlePutRunning))
}

func (rsrc *resource) handleGetAll(w http.ResponseWriter, r *http.Request) {
	rsrc.Lock()
	defer rsrc.Unlock()

	var completed, passes uint64
	if rsrc.render != nil {
		completed = atomic.LoadUint64(&rsrc.render.completed)
		passes = atomic.LoadUint64(&rsrc.render.passes)
	}

	props := struct {
		UUID      string `json:"uuid"`
		Running   bool   `json:"running"`
		Scene     string `json:"scene"`
		Completed uint64 `json:"completed"`
		Passes    uint64 `json:"passes"`
	}{
		rsrc.uuid,
		rsrc.render != nil,
		rsrc.sceneName,
		completed,
		passes,
	}

	if err := json.NewEncoder(w).Encode(props); err != nil {
		internalError(w, err)
	}
}

func (rsrc *resource) handleGetImage(w http.ResponseWriter, r *http.Request) {
	rsrc.Lock()
	defer rsrc.Unlock()

	if rsrc.render == nil {
		img := image.NewGray(image.Rect(0, 0, 320, 240))
		if err := png.Encode(w, img); err != nil {
			internalError(w, err)
		}
		return
	}

	img := rsrc.render.accum.toImage(1.0)
	if err := png.Encode(w, img); err != nil {
		internalError(w, err)
		return
	}

}

func (rsrc *resource) handlePutScene(w http.ResponseWriter, r *http.Request) {
	rsrc.Lock()
	defer rsrc.Unlock()

	if r.Method != http.MethodPut {
		writeError(w, http.StatusMethodNotAllowed)
		return
	}

	buf, err := ioutil.ReadAll(r.Body)
	if err != nil {
		internalError(w, err)
		return
	}

	sceneName := string(buf)
	sceneFunc, ok := scenes[sceneName]
	if !ok {
		http.Error(w, fmt.Sprintf("scene %q not found", string(buf)), http.StatusBadRequest)
		return
	}
	rsrc.sceneFunc = sceneFunc
	rsrc.sceneName = sceneName
}

func (rsrc *resource) handlePutRunning(w http.ResponseWriter, r *http.Request) {
	rsrc.Lock()
	defer rsrc.Unlock()

	if r.Method != http.MethodPut {
		writeError(w, http.StatusMethodNotAllowed)
		return
	}

	buf, err := ioutil.ReadAll(r.Body)
	if err != nil {
		internalError(w, err)
		return
	}

	b, err := strconv.ParseBool(string(buf))
	if err != nil {
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}

	if (rsrc.cancel != nil) == b {
		// Already in the correct state.
		return
	}

	if !b {
		rsrc.cancel()
		rsrc.cancel = nil
		return
	}

	if rsrc.render == nil {
		scene := rsrc.sceneFunc() // TODO: This could take some time to run.
		const pxWide = 320
		pxHigh := pxWide * scene.Camera.aspectHigh / scene.Camera.aspectWide
		rsrc.render = &render{
			pxWide:     pxWide,
			numWorkers: 1,
			scene:      scene,
			accum:      newAccumulator(pxWide, pxHigh),
		}
	}
	var ctx context.Context
	ctx, rsrc.cancel = context.WithCancel(context.Background())
	go func() {
		rsrc.render.traceImage(ctx)
	}()
}
<commit_msg>Use a proper UUID<commit_after>package grayt

import (
	"context"
	"encoding/json"
	"fmt"
	"image"
	"image/png"
	"io/ioutil"
	"log"
	"net/http"
	_ "net/http/pprof"
	"strconv"
	"sync"
	"sync/atomic"
	"time"

	uuid "github.com/satori/go.uuid"
)

func ListenAndServe(addr string) error {
	http.Handle("/", http.FileServer(http.Dir("assets")))
	http.HandleFunc("/scenes", middleware(handleGetScenesCollection))
	http.HandleFunc("/renders", middleware(handlePostRendersCollection))

	log.Printf("Listening for HTTP on %v", addr)
	return http.ListenAndServe(addr, nil)
}

func middleware(fn http.HandlerFunc) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		start := time.Now()
		log.Printf(`info="handling request" method=%q path=%q`, r.Method, r.URL.Path)
		fn(w, r)
		log.Printf(`info="finished" duration=%q`, time.Since(start))
	}
}

/*
	POST  /renders                    - Adds a new render resource, not started, with default settings.
	GET   /renders/{uuid}             - Gets all information about the render resource.
	PUT   /renders/{uuid}/{property}  - Sets property of the render.
	GET   /renders/{uuid}/image       - Creates an image.
*/

type resource struct {
	sync.Mutex
	uuid   uuid.UUID
	render *render
	cancel func() // set to nil if the render isn't running

	sceneFunc func() Scene
	sceneName string
}

func writeError(w http.ResponseWriter, status int) {
	http.Error(w, http.StatusText(status), status)
}

func internalError(w http.ResponseWriter, err error) {
	http.Error(w, err.Error(), http.StatusInternalServerError)
}

func handleGetScenesCollection(w http.ResponseWriter, r *http.Request) {
	var ss []string
	for s := range scenes {
		ss = append(ss, s)
	}
	if err := json.NewEncoder(w).Encode(ss); err != nil {
		internalError(w, err)
	}
}

func handlePostRendersCollection(w http.ResponseWriter, r *http.Request) {
	if r.Method != http.MethodPost {
		writeError(w, http.StatusMethodNotAllowed)
		return
	}

	id := uuid.Must(uuid.NewV4())

	rsrc := &resource{uuid: id}

	fmt.Fprintf(w, `{"uuid":%q}`, id)

	http.HandleFunc("/renders/"+id.String(), middleware(rsrc.handleGetAll))
	http.HandleFunc("/renders/"+id.String()+"/image", middleware(rsrc.handleGetImage))
	http.HandleFunc("/renders/"+id.String()+"/scene", middleware(rsrc.handlePutScene))
	http.HandleFunc("/renders/"+id.String()+"/running", middleware(rsrc.handlePutRunning))
}

func (rsrc *resource) handleGetAll(w http.ResponseWriter, r *http.Request) {
	rsrc.Lock()
	defer rsrc.Unlock()

	var completed, passes uint64
	if rsrc.render != nil {
		completed = atomic.LoadUint64(&rsrc.render.completed)
		passes = atomic.LoadUint64(&rsrc.render.passes)
	}

	props := struct {
		ID        uuid.UUID `json:"uuid"`
		Running   bool      `json:"running"`
		Scene     string    `json:"scene"`
		Completed uint64    `json:"completed"`
		Passes    uint64    `json:"passes"`
	}{
		rsrc.uuid,
		rsrc.render != nil,
		rsrc.sceneName,
		completed,
		passes,
	}

	if err := json.NewEncoder(w).Encode(props); err != nil {
		internalError(w, err)
	}
}

func (rsrc *resource) handleGetImage(w http.ResponseWriter, r *http.Request) {
	rsrc.Lock()
	defer rsrc.Unlock()

	if rsrc.render == nil {
		img := image.NewGray(image.Rect(0, 0, 320, 240))
		if err := png.Encode(w, img); err != nil {
			internalError(w, err)
		}
		return
	}

	img := rsrc.render.accum.toImage(1.0)
	if err := png.Encode(w, img); err != nil {
		internalError(w, err)
		return
	}

}

func (rsrc *resource) handlePutScene(w http.ResponseWriter, r *http.Request) {
	rsrc.Lock()
	defer rsrc.Unlock()

	if r.Method != http.MethodPut {
		writeError(w, http.StatusMethodNotAllowed)
		return
	}

	buf, err := ioutil.ReadAll(r.Body)
	if err != nil {
		internalError(w, err)
		return
	}

	sceneName := string(buf)
	sceneFunc, ok := scenes[sceneName]
	if !ok {
		http.Error(w, fmt.Sprintf("scene %q not found", string(buf)), http.StatusBadRequest)
		return
	}
	rsrc.sceneFunc = sceneFunc
	rsrc.sceneName = sceneName
}

func (rsrc *resource) handlePutRunning(w http.ResponseWriter, r *http.Request) {
	rsrc.Lock()
	defer rsrc.Unlock()

	if r.Method != http.MethodPut {
		writeError(w, http.StatusMethodNotAllowed)
		return
	}

	buf, err := ioutil.ReadAll(r.Body)
	if err != nil {
		internalError(w, err)
		return
	}

	b, err := strconv.ParseBool(string(buf))
	if err != nil {
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}

	if (rsrc.cancel != nil) == b {
		// Already in the correct state.
		return
	}

	if !b {
		rsrc.cancel()
		rsrc.cancel = nil
		return
	}

	if rsrc.render == nil {
		scene := rsrc.sceneFunc() // TODO: This could take some time to run.
		const pxWide = 320
		pxHigh := pxWide * scene.Camera.aspectHigh / scene.Camera.aspectWide
		rsrc.render = &render{
			pxWide:     pxWide,
			numWorkers: 1,
			scene:      scene,
			accum:      newAccumulator(pxWide, pxHigh),
		}
	}
	var ctx context.Context
	ctx, rsrc.cancel = context.WithCancel(context.Background())
	go func() {
		rsrc.render.traceImage(ctx)
	}()
}
<|endoftext|>


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

// Except for this comment, this file is a verbatim copy of the file
// with the same name in $GOROOT/src/go/internal/gccgoimporter.

package gccgoimporter

import (
	"go/types"
	"testing"
)

// importablePackages is a list of packages that we verify that we can
// import. This should be all standard library packages in all relevant
// versions of gccgo. Note that since gccgo follows a different release
// cycle, and since different systems have different versions installed,
// we can't use the last-two-versions rule of the gc toolchain.
var importablePackages = [...]string{
	"archive/tar",
	"archive/zip",
	"bufio",
	"bytes",
	"compress/bzip2",
	"compress/flate",
	"compress/gzip",
	"compress/lzw",
	"compress/zlib",
	"container/heap",
	"container/list",
	"container/ring",
	"crypto/aes",
	"crypto/cipher",
	"crypto/des",
	"crypto/dsa",
	"crypto/ecdsa",
	"crypto/elliptic",
	"crypto",
	"crypto/hmac",
	"crypto/md5",
	"crypto/rand",
	"crypto/rc4",
	"crypto/rsa",
	"crypto/sha1",
	"crypto/sha256",
	"crypto/sha512",
	"crypto/subtle",
	"crypto/tls",
	"crypto/x509",
	"crypto/x509/pkix",
	"database/sql/driver",
	"database/sql",
	"debug/dwarf",
	"debug/elf",
	"debug/gosym",
	"debug/macho",
	"debug/pe",
	"encoding/ascii85",
	"encoding/asn1",
	"encoding/base32",
	"encoding/base64",
	"encoding/binary",
	"encoding/csv",
	"encoding/gob",
	// "encoding", // Added in GCC 4.9.
	"encoding/hex",
	"encoding/json",
	"encoding/pem",
	"encoding/xml",
	"errors",
	"expvar",
	"flag",
	"fmt",
	"go/ast",
	"go/build",
	"go/doc",
	// "go/format", // Added in GCC 4.8.
	"go/parser",
	"go/printer",
	"go/scanner",
	"go/token",
	"hash/adler32",
	"hash/crc32",
	"hash/crc64",
	"hash/fnv",
	"hash",
	"html",
	"html/template",
	"image/color",
	// "image/color/palette", // Added in GCC 4.9.
	"image/draw",
	"image/gif",
	"image",
	"image/jpeg",
	"image/png",
	"index/suffixarray",
	"io",
	"io/ioutil",
	"log",
	"log/syslog",
	"math/big",
	"math/cmplx",
	"math",
	"math/rand",
	"mime",
	"mime/multipart",
	"net",
	"net/http/cgi",
	// "net/http/cookiejar", // Added in GCC 4.8.
	"net/http/fcgi",
	"net/http",
	"net/http/httptest",
	"net/http/httputil",
	"net/http/pprof",
	"net/mail",
	"net/rpc",
	"net/rpc/jsonrpc",
	"net/smtp",
	"net/textproto",
	"net/url",
	"os/exec",
	"os",
	"os/signal",
	"os/user",
	"path/filepath",
	"path",
	"reflect",
	"regexp",
	"regexp/syntax",
	"runtime/debug",
	"runtime",
	"runtime/pprof",
	"sort",
	"strconv",
	"strings",
	"sync/atomic",
	"sync",
	"syscall",
	"testing",
	"testing/iotest",
	"testing/quick",
	"text/scanner",
	"text/tabwriter",
	"text/template",
	"text/template/parse",
	"time",
	"unicode",
	"unicode/utf16",
	"unicode/utf8",
}

func TestInstallationImporter(t *testing.T) {
	// This test relies on gccgo being around.
	gpath := gccgoPath()
	if gpath == "" {
		t.Skip("This test needs gccgo")
	}

	var inst GccgoInstallation
	err := inst.InitFromDriver(gpath)
	if err != nil {
		t.Fatal(err)
	}
	imp := inst.GetImporter(nil, nil)

	// Ensure we don't regress the number of packages we can parse. First import
	// all packages into the same map and then each individually.
	pkgMap := make(map[string]*types.Package)
	for _, pkg := range importablePackages {
		_, err = imp(pkgMap, pkg, ".", nil)
		if err != nil {
			t.Error(err)
		}
	}

	for _, pkg := range importablePackages {
		_, err = imp(make(map[string]*types.Package), pkg, ".", nil)
		if err != nil {
			t.Error(err)
		}
	}

	// Test for certain specific entities in the imported data.
	for _, test := range [...]importerTest{
		{pkgpath: "io", name: "Reader", want: "type Reader interface{Read(p []byte) (n int, err error)}"},
		{pkgpath: "io", name: "ReadWriter", want: "type ReadWriter interface{Reader; Writer}"},
		{pkgpath: "math", name: "Pi", want: "const Pi untyped float"},
		{pkgpath: "math", name: "Sin", want: "func Sin(x float64) float64"},
		{pkgpath: "sort", name: "Ints", want: "func Ints(a []int)"},
		{pkgpath: "unsafe", name: "Pointer", want: "type Pointer"},
	} {
		runImporterTest(t, imp, nil, &test)
	}
}
<commit_msg>go/internal/gccgoimporter: fix up gccgo installation test<commit_after>// Copyright 2013 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Except for this comment, this file is a verbatim copy of the file
// with the same name in $GOROOT/src/go/internal/gccgoimporter.

package gccgoimporter

import (
	"go/types"
	"testing"
)

// importablePackages is a list of packages that we verify that we can
// import. This should be all standard library packages in all relevant
// versions of gccgo. Note that since gccgo follows a different release
// cycle, and since different systems have different versions installed,
// we can't use the last-two-versions rule of the gc toolchain.
var importablePackages = [...]string{
	"archive/tar",
	"archive/zip",
	"bufio",
	"bytes",
	"compress/bzip2",
	"compress/flate",
	"compress/gzip",
	"compress/lzw",
	"compress/zlib",
	"container/heap",
	"container/list",
	"container/ring",
	"crypto/aes",
	"crypto/cipher",
	"crypto/des",
	"crypto/dsa",
	"crypto/ecdsa",
	"crypto/elliptic",
	"crypto",
	"crypto/hmac",
	"crypto/md5",
	"crypto/rand",
	"crypto/rc4",
	"crypto/rsa",
	"crypto/sha1",
	"crypto/sha256",
	"crypto/sha512",
	"crypto/subtle",
	"crypto/tls",
	"crypto/x509",
	"crypto/x509/pkix",
	"database/sql/driver",
	"database/sql",
	"debug/dwarf",
	"debug/elf",
	"debug/gosym",
	"debug/macho",
	"debug/pe",
	"encoding/ascii85",
	"encoding/asn1",
	"encoding/base32",
	"encoding/base64",
	"encoding/binary",
	"encoding/csv",
	"encoding/gob",
	// "encoding", // Added in GCC 4.9.
	"encoding/hex",
	"encoding/json",
	"encoding/pem",
	"encoding/xml",
	"errors",
	"expvar",
	"flag",
	"fmt",
	"go/ast",
	"go/build",
	"go/doc",
	// "go/format", // Added in GCC 4.8.
	"go/parser",
	"go/printer",
	"go/scanner",
	"go/token",
	"hash/adler32",
	"hash/crc32",
	"hash/crc64",
	"hash/fnv",
	"hash",
	"html",
	"html/template",
	"image/color",
	// "image/color/palette", // Added in GCC 4.9.
	"image/draw",
	"image/gif",
	"image",
	"image/jpeg",
	"image/png",
	"index/suffixarray",
	"io",
	"io/ioutil",
	"log",
	"log/syslog",
	"math/big",
	"math/cmplx",
	"math",
	"math/rand",
	"mime",
	"mime/multipart",
	"net",
	"net/http/cgi",
	// "net/http/cookiejar", // Added in GCC 4.8.
	"net/http/fcgi",
	"net/http",
	"net/http/httptest",
	"net/http/httputil",
	"net/http/pprof",
	"net/mail",
	"net/rpc",
	"net/rpc/jsonrpc",
	"net/smtp",
	"net/textproto",
	"net/url",
	"os/exec",
	"os",
	"os/signal",
	"os/user",
	"path/filepath",
	"path",
	"reflect",
	"regexp",
	"regexp/syntax",
	"runtime/debug",
	"runtime",
	"runtime/pprof",
	"sort",
	"strconv",
	"strings",
	"sync/atomic",
	"sync",
	"syscall",
	"testing",
	"testing/iotest",
	"testing/quick",
	"text/scanner",
	"text/tabwriter",
	"text/template",
	"text/template/parse",
	"time",
	"unicode",
	"unicode/utf16",
	"unicode/utf8",
}

func TestInstallationImporter(t *testing.T) {
	// This test relies on gccgo being around.
	gpath := gccgoPath()
	if gpath == "" {
		t.Skip("This test needs gccgo")
	}

	var inst GccgoInstallation
	err := inst.InitFromDriver(gpath)
	if err != nil {
		t.Fatal(err)
	}
	imp := inst.GetImporter(nil, nil)

	// Ensure we don't regress the number of packages we can parse. First import
	// all packages into the same map and then each individually.
	pkgMap := make(map[string]*types.Package)
	for _, pkg := range importablePackages {
		_, err = imp(pkgMap, pkg, ".", nil)
		if err != nil {
			t.Error(err)
		}
	}

	for _, pkg := range importablePackages {
		_, err = imp(make(map[string]*types.Package), pkg, ".", nil)
		if err != nil {
			t.Error(err)
		}
	}

	// Test for certain specific entities in the imported data.
	for _, test := range [...]importerTest{
		{pkgpath: "io", name: "Reader", want: "type Reader interface{Read(p []byte) (n int, err error)}"},
		{pkgpath: "io", name: "ReadWriter", want: "type ReadWriter interface{Reader; Writer}"},
		{pkgpath: "math", name: "Pi", want: "const Pi untyped float"},
		{pkgpath: "math", name: "Sin", want: "func Sin(x float64) float64"},
		{pkgpath: "sort", name: "Search", want: "func Search(n int, f func(int) bool) int"},
		{pkgpath: "unsafe", name: "Pointer", want: "type Pointer"},
	} {
		runImporterTest(t, imp, nil, &test)
	}
}
<|endoftext|>


<commit_before>package weed_server

import (
	"errors"
	"net/http"

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

func (vs *VolumeServer) PostHandler(w http.ResponseWriter, r *http.Request) {
	if e := r.ParseForm(); e != nil {
		glog.V(0).Infoln("form parse error:", e)
		writeJsonError(w, r, http.StatusBadRequest, e)
		return
	}
	vid, _, _, _, _ := parseURLPath(r.URL.Path)
	volumeId, ve := storage.NewVolumeId(vid)
	if ve != nil {
		glog.V(0).Infoln("NewVolumeId error:", ve)
		writeJsonError(w, r, http.StatusBadRequest, ve)
		return
	}
	needle, ne := storage.NewNeedle(r, vs.FixJpgOrientation)
	if ne != nil {
		writeJsonError(w, r, http.StatusBadRequest, ne)
		return
	}

	ret := operation.UploadResult{}
	size, errorStatus := topology.ReplicatedWrite(vs.GetMasterNode(),
		vs.store, volumeId, needle, r)
	httpStatus := http.StatusCreated
	if errorStatus != "" {
		httpStatus = http.StatusInternalServerError
		ret.Error = errorStatus
	}
	if needle.HasName() {
		ret.Name = string(needle.Name)
	}
	ret.Size = size
	writeJsonQuiet(w, r, httpStatus, ret)
}

func (vs *VolumeServer) DeleteHandler(w http.ResponseWriter, r *http.Request) {
	n := new(storage.Needle)
	vid, fid, _, _, _ := parseURLPath(r.URL.Path)
	volumeId, _ := storage.NewVolumeId(vid)
	n.ParsePath(fid)

	glog.V(2).Infoln("deleting", n)

	cookie := n.Cookie
	count, ok := vs.store.ReadVolumeNeedle(volumeId, n)

	if ok != nil {
		m := make(map[string]uint32)
		m["size"] = 0
		writeJsonQuiet(w, r, http.StatusNotFound, m)
		return
	}

	if n.Cookie != cookie {
		glog.V(0).Infoln("delete", r.URL.Path, "with unmaching cookie from ", r.RemoteAddr, "agent", r.UserAgent())
		return
	}
	if n.IsChunkedManifest(){
		chunkManifest, e := operation.LoadChunkManifest(n.Data, n.IsGzipped())
		if e != nil {
			writeJsonError(w, r, http.StatusInternalServerError, errors.New("Load chunks manifest error: " + e.Error()))
			return
		}
		if e := chunkManifest.DeleteChunks(vs.GetMasterNode()); e != nil {
			writeJsonError(w, r, http.StatusInternalServerError, errors.New("Delete chunks error: " + e.Error()))
			return
		}
		count = chunkManifest.Size
	}

	ret := topology.ReplicatedDelete(vs.GetMasterNode(), vs.store, volumeId, n, r)

	if ret != 0 {
		m := make(map[string]uint32)
		m["size"] = uint32(count)
		writeJsonQuiet(w, r, http.StatusAccepted, m)
	} else {
		writeJsonError(w, r, http.StatusInternalServerError, errors.New("Deletion Failed."))
	}

}

//Experts only: takes multiple fid parameters. This function does not propagate deletes to replicas.
func (vs *VolumeServer) batchDeleteHandler(w http.ResponseWriter, r *http.Request) {
	r.ParseForm()
	var ret []operation.DeleteResult
	for _, fid := range r.Form["fid"] {
		vid, id_cookie, err := operation.ParseFileId(fid)
		if err != nil {
			ret = append(ret, operation.DeleteResult{Fid: fid, Error: err.Error()})
			continue
		}
		n := new(storage.Needle)
		volumeId, _ := storage.NewVolumeId(vid)
		n.ParsePath(id_cookie)
		glog.V(4).Infoln("batch deleting", n)
		cookie := n.Cookie
		if _, err := vs.store.ReadVolumeNeedle(volumeId, n); err != nil {
			ret = append(ret, operation.DeleteResult{Fid: fid, Error: err.Error()})
			continue
		}
		if n.Cookie != cookie {
			ret = append(ret, operation.DeleteResult{Fid: fid, Error: "File Random Cookie does not match."})
			glog.V(0).Infoln("deleting", fid, "with unmaching cookie from ", r.RemoteAddr, "agent", r.UserAgent())
			return
		}
		if size, err := vs.store.Delete(volumeId, n); err != nil {
			ret = append(ret, operation.DeleteResult{Fid: fid, Error: err.Error()})
		} else {
			ret = append(ret, operation.DeleteResult{Fid: fid, Size: int(size)})
		}
	}

	writeJsonQuiet(w, r, http.StatusAccepted, ret)
}
<commit_msg>delete file return needle size<commit_after>package weed_server

import (
	"errors"
	"net/http"

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

func (vs *VolumeServer) PostHandler(w http.ResponseWriter, r *http.Request) {
	if e := r.ParseForm(); e != nil {
		glog.V(0).Infoln("form parse error:", e)
		writeJsonError(w, r, http.StatusBadRequest, e)
		return
	}
	vid, _, _, _, _ := parseURLPath(r.URL.Path)
	volumeId, ve := storage.NewVolumeId(vid)
	if ve != nil {
		glog.V(0).Infoln("NewVolumeId error:", ve)
		writeJsonError(w, r, http.StatusBadRequest, ve)
		return
	}
	needle, ne := storage.NewNeedle(r, vs.FixJpgOrientation)
	if ne != nil {
		writeJsonError(w, r, http.StatusBadRequest, ne)
		return
	}

	ret := operation.UploadResult{}
	size, errorStatus := topology.ReplicatedWrite(vs.GetMasterNode(),
		vs.store, volumeId, needle, r)
	httpStatus := http.StatusCreated
	if errorStatus != "" {
		httpStatus = http.StatusInternalServerError
		ret.Error = errorStatus
	}
	if needle.HasName() {
		ret.Name = string(needle.Name)
	}
	ret.Size = size
	writeJsonQuiet(w, r, httpStatus, ret)
}

func (vs *VolumeServer) DeleteHandler(w http.ResponseWriter, r *http.Request) {
	n := new(storage.Needle)
	vid, fid, _, _, _ := parseURLPath(r.URL.Path)
	volumeId, _ := storage.NewVolumeId(vid)
	n.ParsePath(fid)

	glog.V(2).Infoln("deleting", n)

	cookie := n.Cookie

	if _, ok := vs.store.ReadVolumeNeedle(volumeId, n); ok != nil {
		m := make(map[string]uint32)
		m["size"] = 0
		writeJsonQuiet(w, r, http.StatusNotFound, m)
		return
	}

	if n.Cookie != cookie {
		glog.V(0).Infoln("delete", r.URL.Path, "with unmaching cookie from ", r.RemoteAddr, "agent", r.UserAgent())
		return
	}

	count := int64(n.Size)

	if n.IsChunkedManifest(){
		chunkManifest, e := operation.LoadChunkManifest(n.Data, n.IsGzipped())
		if e != nil {
			writeJsonError(w, r, http.StatusInternalServerError, errors.New("Load chunks manifest error: " + e.Error()))
			return
		}
		if e := chunkManifest.DeleteChunks(vs.GetMasterNode()); e != nil {
			writeJsonError(w, r, http.StatusInternalServerError, errors.New("Delete chunks error: " + e.Error()))
			return
		}
		count = chunkManifest.Size
	}

	ret := topology.ReplicatedDelete(vs.GetMasterNode(), vs.store, volumeId, n, r)

	if ret != 0 {
		m := make(map[string]int64)
		m["size"] = count
		writeJsonQuiet(w, r, http.StatusAccepted, m)
	} else {
		writeJsonError(w, r, http.StatusInternalServerError, errors.New("Deletion Failed."))
	}

}

//Experts only: takes multiple fid parameters. This function does not propagate deletes to replicas.
func (vs *VolumeServer) batchDeleteHandler(w http.ResponseWriter, r *http.Request) {
	r.ParseForm()
	var ret []operation.DeleteResult
	for _, fid := range r.Form["fid"] {
		vid, id_cookie, err := operation.ParseFileId(fid)
		if err != nil {
			ret = append(ret, operation.DeleteResult{Fid: fid, Error: err.Error()})
			continue
		}
		n := new(storage.Needle)
		volumeId, _ := storage.NewVolumeId(vid)
		n.ParsePath(id_cookie)
		glog.V(4).Infoln("batch deleting", n)
		cookie := n.Cookie
		if _, err := vs.store.ReadVolumeNeedle(volumeId, n); err != nil {
			ret = append(ret, operation.DeleteResult{Fid: fid, Error: err.Error()})
			continue
		}
		if n.Cookie != cookie {
			ret = append(ret, operation.DeleteResult{Fid: fid, Error: "File Random Cookie does not match."})
			glog.V(0).Infoln("deleting", fid, "with unmaching cookie from ", r.RemoteAddr, "agent", r.UserAgent())
			return
		}
		if size, err := vs.store.Delete(volumeId, n); err != nil {
			ret = append(ret, operation.DeleteResult{Fid: fid, Error: err.Error()})
		} else {
			ret = append(ret, operation.DeleteResult{Fid: fid, Size: int(size)})
		}
	}

	writeJsonQuiet(w, r, http.StatusAccepted, ret)
}
<|endoftext|>


<commit_before>/*
Copyright The Helm Authors.

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 installer // import "k8s.io/helm/cmd/helm/installer"

import (
	apierrors "k8s.io/apimachinery/pkg/api/errors"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/client-go/kubernetes"
	corev1 "k8s.io/client-go/kubernetes/typed/core/v1"
)

const (
	deploymentName = "tiller-deploy"
	serviceName    = "tiller-deploy"
	secretName     = "tiller-secret"
)

// Uninstall uses Kubernetes client to uninstall Tiller.
func Uninstall(client kubernetes.Interface, opts *Options) error {
	if err := deleteService(client.Core(), opts.Namespace); err != nil {
		return err
	}
	if err := deleteDeployment(client, opts.Namespace); err != nil {
		return err
	}
	return deleteSecret(client.Core(), opts.Namespace)
}

// deleteService deletes the Tiller Service resource
func deleteService(client corev1.ServicesGetter, namespace string) error {
	err := client.Services(namespace).Delete(serviceName, &metav1.DeleteOptions{})
	return ingoreNotFound(err)
}

// deleteDeployment deletes the Tiller Deployment resource
// We need to use the reaper instead of the kube API because GC for deployment dependents
// is not yet supported at the k8s server level (<= 1.5)
func deleteDeployment(client kubernetes.Interface, namespace string) error {
	err := client.Extensions().Deployments(namespace).Delete(deploymentName, &metav1.DeleteOptions{})
	return ingoreNotFound(err)
}

// deleteSecret deletes the Tiller Secret resource
func deleteSecret(client corev1.SecretsGetter, namespace string) error {
	err := client.Secrets(namespace).Delete(secretName, &metav1.DeleteOptions{})
	return ingoreNotFound(err)
}

func ingoreNotFound(err error) error {
	if apierrors.IsNotFound(err) {
		return nil
	}
	return err
}
<commit_msg>Changing deprecated library reference and setting delete propagation background policy (#5161)<commit_after>/*
Copyright The Helm Authors.

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 installer // import "k8s.io/helm/cmd/helm/installer"

import (
	apierrors "k8s.io/apimachinery/pkg/api/errors"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/client-go/kubernetes"
	corev1 "k8s.io/client-go/kubernetes/typed/core/v1"
)

const (
	deploymentName = "tiller-deploy"
	serviceName    = "tiller-deploy"
	secretName     = "tiller-secret"
)

// Uninstall uses Kubernetes client to uninstall Tiller.
func Uninstall(client kubernetes.Interface, opts *Options) error {
	if err := deleteService(client.Core(), opts.Namespace); err != nil {
		return err
	}
	if err := deleteDeployment(client, opts.Namespace); err != nil {
		return err
	}
	return deleteSecret(client.Core(), opts.Namespace)
}

// deleteService deletes the Tiller Service resource
func deleteService(client corev1.ServicesGetter, namespace string) error {
	err := client.Services(namespace).Delete(serviceName, &metav1.DeleteOptions{})
	return ingoreNotFound(err)
}

// deleteDeployment deletes the Tiller Deployment resource
func deleteDeployment(client kubernetes.Interface, namespace string) error {
	policy := metav1.DeletePropagationBackground
	err := client.AppsV1().Deployments(namespace).Delete(deploymentName, &metav1.DeleteOptions{
		PropagationPolicy: &policy,
	})
	return ingoreNotFound(err)
}

// deleteSecret deletes the Tiller Secret resource
func deleteSecret(client corev1.SecretsGetter, namespace string) error {
	err := client.Secrets(namespace).Delete(secretName, &metav1.DeleteOptions{})
	return ingoreNotFound(err)
}

func ingoreNotFound(err error) error {
	if apierrors.IsNotFound(err) {
		return nil
	}
	return err
}
<|endoftext|>


<commit_before>package manager

import (
	"bufio"
	"fmt"
	"os"
	"strconv"
	"strings"

	"github.com/docker/infrakit/cmd/infrakit/base"
	"github.com/docker/infrakit/pkg/cli"
	"github.com/docker/infrakit/pkg/discovery"
	logutil "github.com/docker/infrakit/pkg/log"
	"github.com/docker/infrakit/pkg/manager"
	"github.com/docker/infrakit/pkg/plugin"
	"github.com/docker/infrakit/pkg/rpc/client"
	group_rpc "github.com/docker/infrakit/pkg/rpc/group"
	manager_rpc "github.com/docker/infrakit/pkg/rpc/manager"
	metadata_rpc "github.com/docker/infrakit/pkg/rpc/metadata"
	"github.com/docker/infrakit/pkg/spi/group"
	"github.com/docker/infrakit/pkg/spi/metadata"
	"github.com/docker/infrakit/pkg/types"
	"github.com/sergi/go-diff/diffmatchpatch"
	"github.com/spf13/cobra"
)

var log = logutil.New("module", "cli/manager")

func init() {
	base.Register(Command)
}

// Command is the entrypoint
func Command(plugins func() discovery.Plugins) *cobra.Command {

	var groupPlugin group.Plugin
	var groupPluginName string

	var updatablePlugin metadata.Updatable
	var updatablePluginName string

	cmd := &cobra.Command{
		Use:   "manager",
		Short: "Access the manager",
		PersistentPreRunE: func(c *cobra.Command, args []string) error {
			if err := cli.EnsurePersistentPreRunE(c); err != nil {
				return err
			}

			// Scan for a manager
			pm, err := plugins().List()
			if err != nil {
				return err
			}

			for name, endpoint := range pm {

				rpcClient, err := client.New(endpoint.Address, manager.InterfaceSpec)
				if err == nil {

					m := manager_rpc.Adapt(rpcClient)

					isLeader, err := m.IsLeader()
					if err != nil {
						return err
					}

					log.Debug("Found manager", "name", name, "leader", isLeader)
					if isLeader {

						groupPlugin = group_rpc.Adapt(rpcClient)
						groupPluginName = name

						log.Debug("Found manager", "name", name, "addr", endpoint.Address)

						updatablePlugin = metadata_rpc.AdaptUpdatable(rpcClient)
						updatablePluginName = name

						log.Debug("Found updatable", "name", name, "addr", endpoint.Address)
						break
					}
				}
			}

			// We need to enforce the requirement that we run on a leader node.
			if groupPlugin == nil {
				return fmt.Errorf("Cannot perform manager operations on a non-leader node")
			}
			return nil
		},
	}
	pretend := cmd.PersistentFlags().Bool("pretend", false, "Don't actually make changes; explain where appropriate")

	templateFlags, toJSON, fromJSON, processTemplate := base.TemplateProcessor(plugins)

	///////////////////////////////////////////////////////////////////////////////////
	// commit
	commit := &cobra.Command{
		Use:   "commit <template_URL>",
		Short: "Commit a multi-group configuration, as specified by the URL.  Read from stdin if url is '-'",
		RunE: func(cmd *cobra.Command, args []string) error {

			if len(args) != 1 {
				cmd.Usage()
				os.Exit(1)
			}

			view, err := base.ReadFromStdinIfElse(
				func() bool { return args[0] == "-" },
				func() (string, error) { return processTemplate(args[0]) },
				toJSON,
			)
			if err != nil {
				return err
			}

			// In any case, the view should be in JSON format

			// Treat this as an Any and then convert
			any := types.AnyString(view)

			groups := []plugin.Spec{}
			err = any.Decode(&groups)
			if err != nil {
				log.Warn("Error parsing the template for plugin specs.")
				return err
			}

			// Check the list of plugins
			for _, gp := range groups {

				endpoint, err := plugins().Find(gp.Plugin)
				if err != nil {
					return err
				}

				// unmarshal the group spec
				spec := group.Spec{}
				if gp.Properties != nil {
					err = gp.Properties.Decode(&spec)
					if err != nil {
						return err
					}
				}

				// TODO(chungers) -- we need to enforce and confirm the type of this.
				// Right now we assume the RPC endpoint is indeed a group.
				target, err := group_rpc.NewClient(endpoint.Address)

				log.Debug("commit", "plugin", gp.Plugin, "address", endpoint.Address, "err", err, "spec", spec)

				if err != nil {
					return err
				}

				plan, err := target.CommitGroup(spec, *pretend)
				if err != nil {
					return err
				}

				fmt.Println("Group", spec.ID, "with plugin", gp.Plugin, "plan:", plan)
			}

			return nil
		},
	}
	commit.Flags().AddFlagSet(templateFlags)

	///////////////////////////////////////////////////////////////////////////////////
	// inspect
	inspect := &cobra.Command{
		Use:   "inspect",
		Short: "Inspect returns the plugin configurations known by the manager",
		RunE: func(cmd *cobra.Command, args []string) error {

			if len(args) != 0 {
				cmd.Usage()
				os.Exit(1)
			}

			out, err := getGlobalConfig(groupPlugin, groupPluginName)
			if err != nil {
				return err
			}

			view, err := types.AnyValue(out)
			if err != nil {
				return err
			}

			buff, err := fromJSON(view.Bytes())
			if err != nil {
				return err
			}

			fmt.Println(string(buff))

			return nil
		},
	}
	inspect.Flags().AddFlagSet(templateFlags)

	///////////////////////////////////////////////////////////////////////////////////
	// change
	change := &cobra.Command{
		Use:   "change",
		Short: "Change returns the plugin configurations known by the manager",
	}
	vars := change.Flags().StringSliceP("var", "v", []string{}, "key=value pairs")
	commitChange := change.Flags().BoolP("commit", "c", false, "Commit changes")

	// This is the only interactive command.  We want to show the user the proposal, with the diff
	// and when the user accepts the change, call a commit.
	change.RunE = func(cmd *cobra.Command, args []string) error {

		if len(args) != 0 {
			cmd.Usage()
			os.Exit(1)
		}

		log.Info("applying changes")

		// get the changes
		changes, err := changeSet(*vars)
		if err != nil {
			return err
		}
		current, proposed, cas, err := updatablePlugin.Changes(changes)
		if err != nil {
			return err
		}
		currentBuff, err := current.MarshalYAML()
		if err != nil {
			return err
		}

		proposedBuff, err := proposed.MarshalYAML()
		if err != nil {
			return err
		}

		// render the proposal
		fmt.Printf("Proposed changes, hash=%s\n", cas)
		dmp := diffmatchpatch.New()
		diffs := dmp.DiffMain(string(currentBuff), string(proposedBuff), false)
		fmt.Println(dmp.DiffPrettyText(diffs))

		if *commitChange {
			// ask for final approval
			input := bufio.NewReader(os.Stdin)
			fmt.Fprintf(os.Stderr, "\n\nCommit? [y/n] ")
			text, _ := input.ReadString('\n')
			text = strings.Trim(text, " \t\n")

			agree, err := parseBool(text)
			if err != nil {
				return fmt.Errorf("not boolean %v", text)
			}
			if !agree {
				fmt.Fprintln(os.Stderr, "Not committing. Bye.")
				os.Exit(0)
			}

			return updatablePlugin.Commit(proposed, cas)
		}

		return nil
	}
	change.Flags().AddFlagSet(templateFlags)

	///////////////////////////////////////////////////////////////////////////////////
	// change-list
	changeList := &cobra.Command{
		Use:   "ls",
		Short: "Lists all the changeable paths",
		RunE: func(cmd *cobra.Command, args []string) error {

			if len(args) != 0 {
				cmd.Usage()
				os.Exit(1)
			}

			all, err := types.ListAll(updatablePlugin, types.PathFromString("."))
			if err != nil {
				return err
			}

			types.Sort(all)
			for _, p := range all {
				fmt.Println(p.String())
			}
			return nil
		},
	}
	///////////////////////////////////////////////////////////////////////////////////
	// change-cat
	changeGet := &cobra.Command{
		Use:   "cat",
		Short: "Cat returns the current value at given path",
		RunE: func(cmd *cobra.Command, args []string) error {

			if len(args) != 1 {
				cmd.Usage()
				os.Exit(1)
			}

			path := types.PathFromString(args[0])
			any, err := updatablePlugin.Get(path)
			if err != nil {
				return err
			}

			fmt.Println(any.String())

			return nil
		},
	}
	change.AddCommand(changeList, changeGet)

	cmd.AddCommand(commit, inspect, change)

	return cmd
}

func parseBool(text string) (bool, error) {
	agree, err := strconv.ParseBool(text)
	if err == nil {
		return agree, nil
	}
	switch strings.ToLower(text) {
	case "yes", "ok", "y":
		return true, nil
	case "no", "nope", "n":
		return false, nil
	}
	return false, err
}

func getGlobalConfig(groupPlugin group.Plugin, groupPluginName string) ([]plugin.Spec, error) {
	specs, err := groupPlugin.InspectGroups()
	if err != nil {
		return nil, err
	}

	// the format is plugin.Spec
	out := []plugin.Spec{}
	for _, spec := range specs {

		any, err := types.AnyValue(spec)
		if err != nil {
			return nil, err
		}

		out = append(out, plugin.Spec{
			Plugin:     plugin.Name(groupPluginName),
			Properties: any,
		})
	}
	return out, nil
}

// changeSet returns a set of changes from the input pairs of path / value
func changeSet(kvPairs []string) ([]metadata.Change, error) {
	changes := []metadata.Change{}

	for _, kv := range kvPairs {

		parts := strings.SplitN(kv, "=", 2)
		key := strings.Trim(parts[0], " \t\n")
		value := strings.Trim(parts[1], " \t\n")

		change := metadata.Change{
			Path:  types.PathFromString(key),
			Value: types.AnyYAMLMust([]byte(value)),
		}

		changes = append(changes, change)
	}
	return changes, nil
}
<commit_msg>Remove confirmation on "infrakit manager change -c .." (#638)<commit_after>package manager

import (
	"fmt"
	"os"
	"strconv"
	"strings"

	"github.com/docker/infrakit/cmd/infrakit/base"
	"github.com/docker/infrakit/pkg/cli"
	"github.com/docker/infrakit/pkg/discovery"
	logutil "github.com/docker/infrakit/pkg/log"
	"github.com/docker/infrakit/pkg/manager"
	"github.com/docker/infrakit/pkg/plugin"
	"github.com/docker/infrakit/pkg/rpc/client"
	group_rpc "github.com/docker/infrakit/pkg/rpc/group"
	manager_rpc "github.com/docker/infrakit/pkg/rpc/manager"
	metadata_rpc "github.com/docker/infrakit/pkg/rpc/metadata"
	"github.com/docker/infrakit/pkg/spi/group"
	"github.com/docker/infrakit/pkg/spi/metadata"
	"github.com/docker/infrakit/pkg/types"
	"github.com/sergi/go-diff/diffmatchpatch"
	"github.com/spf13/cobra"
)

var log = logutil.New("module", "cli/manager")

func init() {
	base.Register(Command)
}

// Command is the entrypoint
func Command(plugins func() discovery.Plugins) *cobra.Command {

	var groupPlugin group.Plugin
	var groupPluginName string

	var updatablePlugin metadata.Updatable
	var updatablePluginName string

	cmd := &cobra.Command{
		Use:   "manager",
		Short: "Access the manager",
		PersistentPreRunE: func(c *cobra.Command, args []string) error {
			if err := cli.EnsurePersistentPreRunE(c); err != nil {
				return err
			}

			// Scan for a manager
			pm, err := plugins().List()
			if err != nil {
				return err
			}

			for name, endpoint := range pm {

				rpcClient, err := client.New(endpoint.Address, manager.InterfaceSpec)
				if err == nil {

					m := manager_rpc.Adapt(rpcClient)

					isLeader, err := m.IsLeader()
					if err != nil {
						return err
					}

					log.Debug("Found manager", "name", name, "leader", isLeader)
					if isLeader {

						groupPlugin = group_rpc.Adapt(rpcClient)
						groupPluginName = name

						log.Debug("Found manager", "name", name, "addr", endpoint.Address)

						updatablePlugin = metadata_rpc.AdaptUpdatable(rpcClient)
						updatablePluginName = name

						log.Debug("Found updatable", "name", name, "addr", endpoint.Address)
						break
					}
				}
			}

			// We need to enforce the requirement that we run on a leader node.
			if groupPlugin == nil {
				return fmt.Errorf("Cannot perform manager operations on a non-leader node")
			}
			return nil
		},
	}
	pretend := cmd.PersistentFlags().Bool("pretend", false, "Don't actually make changes; explain where appropriate")

	templateFlags, toJSON, fromJSON, processTemplate := base.TemplateProcessor(plugins)

	///////////////////////////////////////////////////////////////////////////////////
	// commit
	commit := &cobra.Command{
		Use:   "commit <template_URL>",
		Short: "Commit a multi-group configuration, as specified by the URL.  Read from stdin if url is '-'",
		RunE: func(cmd *cobra.Command, args []string) error {

			if len(args) != 1 {
				cmd.Usage()
				os.Exit(1)
			}

			view, err := base.ReadFromStdinIfElse(
				func() bool { return args[0] == "-" },
				func() (string, error) { return processTemplate(args[0]) },
				toJSON,
			)
			if err != nil {
				return err
			}

			// In any case, the view should be in JSON format

			// Treat this as an Any and then convert
			any := types.AnyString(view)

			groups := []plugin.Spec{}
			err = any.Decode(&groups)
			if err != nil {
				log.Warn("Error parsing the template for plugin specs.")
				return err
			}

			// Check the list of plugins
			for _, gp := range groups {

				endpoint, err := plugins().Find(gp.Plugin)
				if err != nil {
					return err
				}

				// unmarshal the group spec
				spec := group.Spec{}
				if gp.Properties != nil {
					err = gp.Properties.Decode(&spec)
					if err != nil {
						return err
					}
				}

				// TODO(chungers) -- we need to enforce and confirm the type of this.
				// Right now we assume the RPC endpoint is indeed a group.
				target, err := group_rpc.NewClient(endpoint.Address)

				log.Debug("commit", "plugin", gp.Plugin, "address", endpoint.Address, "err", err, "spec", spec)

				if err != nil {
					return err
				}

				plan, err := target.CommitGroup(spec, *pretend)
				if err != nil {
					return err
				}

				fmt.Println("Group", spec.ID, "with plugin", gp.Plugin, "plan:", plan)
			}

			return nil
		},
	}
	commit.Flags().AddFlagSet(templateFlags)

	///////////////////////////////////////////////////////////////////////////////////
	// inspect
	inspect := &cobra.Command{
		Use:   "inspect",
		Short: "Inspect returns the plugin configurations known by the manager",
		RunE: func(cmd *cobra.Command, args []string) error {

			if len(args) != 0 {
				cmd.Usage()
				os.Exit(1)
			}

			out, err := getGlobalConfig(groupPlugin, groupPluginName)
			if err != nil {
				return err
			}

			view, err := types.AnyValue(out)
			if err != nil {
				return err
			}

			buff, err := fromJSON(view.Bytes())
			if err != nil {
				return err
			}

			fmt.Println(string(buff))

			return nil
		},
	}
	inspect.Flags().AddFlagSet(templateFlags)

	///////////////////////////////////////////////////////////////////////////////////
	// change
	change := &cobra.Command{
		Use:   "change",
		Short: "Change returns the plugin configurations known by the manager",
	}
	vars := change.Flags().StringSliceP("var", "v", []string{}, "key=value pairs")
	commitChange := change.Flags().BoolP("commit", "c", false, "Commit changes")

	// This is the only interactive command.  We want to show the user the proposal, with the diff
	// and when the user accepts the change, call a commit.
	change.RunE = func(cmd *cobra.Command, args []string) error {

		if len(args) != 0 {
			cmd.Usage()
			os.Exit(1)
		}

		// get the changes
		changes, err := changeSet(*vars)
		if err != nil {
			return err
		}
		current, proposed, cas, err := updatablePlugin.Changes(changes)
		if err != nil {
			return err
		}
		currentBuff, err := current.MarshalYAML()
		if err != nil {
			return err
		}

		proposedBuff, err := proposed.MarshalYAML()
		if err != nil {
			return err
		}

		if *commitChange {
			fmt.Printf("Committing changes, hash=%s\n", cas)
		} else {
			fmt.Printf("Proposed changes, hash=%s\n", cas)
		}

		// Render the delta
		dmp := diffmatchpatch.New()
		diffs := dmp.DiffMain(string(currentBuff), string(proposedBuff), false)
		fmt.Println(dmp.DiffPrettyText(diffs))

		if *commitChange {
			return updatablePlugin.Commit(proposed, cas)
		}

		return nil
	}
	change.Flags().AddFlagSet(templateFlags)

	///////////////////////////////////////////////////////////////////////////////////
	// change-list
	changeList := &cobra.Command{
		Use:   "ls",
		Short: "Lists all the changeable paths",
		RunE: func(cmd *cobra.Command, args []string) error {

			if len(args) != 0 {
				cmd.Usage()
				os.Exit(1)
			}

			all, err := types.ListAll(updatablePlugin, types.PathFromString("."))
			if err != nil {
				return err
			}

			types.Sort(all)
			for _, p := range all {
				fmt.Println(p.String())
			}
			return nil
		},
	}
	///////////////////////////////////////////////////////////////////////////////////
	// change-cat
	changeGet := &cobra.Command{
		Use:   "cat",
		Short: "Cat returns the current value at given path",
		RunE: func(cmd *cobra.Command, args []string) error {

			if len(args) != 1 {
				cmd.Usage()
				os.Exit(1)
			}

			path := types.PathFromString(args[0])
			any, err := updatablePlugin.Get(path)
			if err != nil {
				return err
			}

			fmt.Println(any.String())

			return nil
		},
	}
	change.AddCommand(changeList, changeGet)

	cmd.AddCommand(commit, inspect, change)

	return cmd
}

func parseBool(text string) (bool, error) {
	agree, err := strconv.ParseBool(text)
	if err == nil {
		return agree, nil
	}
	switch strings.ToLower(text) {
	case "yes", "ok", "y":
		return true, nil
	case "no", "nope", "n":
		return false, nil
	}
	return false, err
}

func getGlobalConfig(groupPlugin group.Plugin, groupPluginName string) ([]plugin.Spec, error) {
	specs, err := groupPlugin.InspectGroups()
	if err != nil {
		return nil, err
	}

	// the format is plugin.Spec
	out := []plugin.Spec{}
	for _, spec := range specs {

		any, err := types.AnyValue(spec)
		if err != nil {
			return nil, err
		}

		out = append(out, plugin.Spec{
			Plugin:     plugin.Name(groupPluginName),
			Properties: any,
		})
	}
	return out, nil
}

// changeSet returns a set of changes from the input pairs of path / value
func changeSet(kvPairs []string) ([]metadata.Change, error) {
	changes := []metadata.Change{}

	for _, kv := range kvPairs {

		parts := strings.SplitN(kv, "=", 2)
		key := strings.Trim(parts[0], " \t\n")
		value := strings.Trim(parts[1], " \t\n")

		change := metadata.Change{
			Path:  types.PathFromString(key),
			Value: types.AnyYAMLMust([]byte(value)),
		}

		changes = append(changes, change)
	}
	return changes, nil
}
<|endoftext|>


<commit_before>/*
Copyright 2015 The Kubernetes Authors.

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 app

import (
	"errors"
	"io/ioutil"
	"strconv"

	"github.com/golang/glog"

	"k8s.io/kubernetes/pkg/util/mount"
	"k8s.io/kubernetes/pkg/util/sysctl"
)

// Conntracker is an interface to the global sysctl. Descriptions of the various
// sysctl fields can be found here:
//
// https://www.kernel.org/doc/Documentation/networking/nf_conntrack-sysctl.txt
type Conntracker interface {
	// SetMax adjusts nf_conntrack_max.
	SetMax(max int) error
	// SetTCPEstablishedTimeout adjusts nf_conntrack_tcp_timeout_established.
	SetTCPEstablishedTimeout(seconds int) error
	// SetTCPCloseWaitTimeout nf_conntrack_tcp_timeout_close_wait.
	SetTCPCloseWaitTimeout(seconds int) error
}

type realConntracker struct{}

var readOnlySysFSError = errors.New("readOnlySysFS")

func (rct realConntracker) SetMax(max int) error {
	if err := rct.setIntSysCtl("nf_conntrack_max", max); err != nil {
		return err
	}
	// sysfs is expected to be mounted as 'rw'. However, it may be
	// unexpectedly mounted as 'ro' by docker because of a known docker
	// issue (https://github.com/docker/docker/issues/24000). Setting
	// conntrack will fail when sysfs is readonly. When that happens, we
	// don't set conntrack hashsize and return a special error
	// readOnlySysFSError here. The caller should deal with
	// readOnlySysFSError differently.
	writable, err := isSysFSWritable()
	if err != nil {
		return err
	}
	if !writable {
		return readOnlySysFSError
	}
	// TODO: generify this and sysctl to a new sysfs.WriteInt()
	glog.Infof("Setting conntrack hashsize to %d", max/4)
	return writeIntStringFile("/sys/module/nf_conntrack/parameters/hashsize", max/4)
}

func (rct realConntracker) SetTCPEstablishedTimeout(seconds int) error {
	return rct.setIntSysCtl("nf_conntrack_tcp_timeout_established", seconds)
}

func (rct realConntracker) SetTCPCloseWaitTimeout(seconds int) error {
	return rct.setIntSysCtl("nf_conntrack_tcp_timeout_close_wait", seconds)
}

func (realConntracker) setIntSysCtl(name string, value int) error {
	entry := "net/netfilter/" + name

	glog.Infof("Set sysctl '%v' to %v", entry, value)
	if err := sysctl.New().SetSysctl(entry, value); err != nil {
		return err
	}
	return nil
}

// isSysFSWritable checks /proc/mounts to see whether sysfs is 'rw' or not.
func isSysFSWritable() (bool, error) {
	const permWritable = "rw"
	const sysfsDevice = "sysfs"
	m := mount.New("" /* default mount path */)
	mountPoints, err := m.List()
	if err != nil {
		glog.Errorf("failed to list mount points: %v", err)
		return false, err
	}

	for _, mountPoint := range mountPoints {
		const sysfsDevice = "sysfs"
		if mountPoint.Device != sysfsDevice {
			continue
		}
		// Check whether sysfs is 'rw'
		const permWritable = "rw"
		if len(mountPoint.Opts) > 0 && mountPoint.Opts[0] == permWritable {
			return true, nil
		}
		glog.Errorf("sysfs is not writable: %+v (mount options are %v)",
			mountPoint, mountPoint.Opts)
		return false, readOnlySysFSError
	}

	return false, errors.New("No sysfs mounted")
}

func writeIntStringFile(filename string, value int) error {
	return ioutil.WriteFile(filename, []byte(strconv.Itoa(value)), 0640)
}
<commit_msg>Check sysfs Type instead of Device<commit_after>/*
Copyright 2015 The Kubernetes Authors.

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 app

import (
	"errors"
	"io/ioutil"
	"strconv"

	"github.com/golang/glog"

	"k8s.io/kubernetes/pkg/util/mount"
	"k8s.io/kubernetes/pkg/util/sysctl"
)

// Conntracker is an interface to the global sysctl. Descriptions of the various
// sysctl fields can be found here:
//
// https://www.kernel.org/doc/Documentation/networking/nf_conntrack-sysctl.txt
type Conntracker interface {
	// SetMax adjusts nf_conntrack_max.
	SetMax(max int) error
	// SetTCPEstablishedTimeout adjusts nf_conntrack_tcp_timeout_established.
	SetTCPEstablishedTimeout(seconds int) error
	// SetTCPCloseWaitTimeout nf_conntrack_tcp_timeout_close_wait.
	SetTCPCloseWaitTimeout(seconds int) error
}

type realConntracker struct{}

var readOnlySysFSError = errors.New("readOnlySysFS")

func (rct realConntracker) SetMax(max int) error {
	if err := rct.setIntSysCtl("nf_conntrack_max", max); err != nil {
		return err
	}
	// sysfs is expected to be mounted as 'rw'. However, it may be
	// unexpectedly mounted as 'ro' by docker because of a known docker
	// issue (https://github.com/docker/docker/issues/24000). Setting
	// conntrack will fail when sysfs is readonly. When that happens, we
	// don't set conntrack hashsize and return a special error
	// readOnlySysFSError here. The caller should deal with
	// readOnlySysFSError differently.
	writable, err := isSysFSWritable()
	if err != nil {
		return err
	}
	if !writable {
		return readOnlySysFSError
	}
	// TODO: generify this and sysctl to a new sysfs.WriteInt()
	glog.Infof("Setting conntrack hashsize to %d", max/4)
	return writeIntStringFile("/sys/module/nf_conntrack/parameters/hashsize", max/4)
}

func (rct realConntracker) SetTCPEstablishedTimeout(seconds int) error {
	return rct.setIntSysCtl("nf_conntrack_tcp_timeout_established", seconds)
}

func (rct realConntracker) SetTCPCloseWaitTimeout(seconds int) error {
	return rct.setIntSysCtl("nf_conntrack_tcp_timeout_close_wait", seconds)
}

func (realConntracker) setIntSysCtl(name string, value int) error {
	entry := "net/netfilter/" + name

	glog.Infof("Set sysctl '%v' to %v", entry, value)
	if err := sysctl.New().SetSysctl(entry, value); err != nil {
		return err
	}
	return nil
}

// isSysFSWritable checks /proc/mounts to see whether sysfs is 'rw' or not.
func isSysFSWritable() (bool, error) {
	const permWritable = "rw"
	const sysfsDevice = "sysfs"
	m := mount.New("" /* default mount path */)
	mountPoints, err := m.List()
	if err != nil {
		glog.Errorf("failed to list mount points: %v", err)
		return false, err
	}

	for _, mountPoint := range mountPoints {
		const sysfsDevice = "sysfs"
		if mountPoint.Type != sysfsDevice {
			continue
		}
		// Check whether sysfs is 'rw'
		const permWritable = "rw"
		if len(mountPoint.Opts) > 0 && mountPoint.Opts[0] == permWritable {
			return true, nil
		}
		glog.Errorf("sysfs is not writable: %+v (mount options are %v)",
			mountPoint, mountPoint.Opts)
		return false, readOnlySysFSError
	}

	return false, errors.New("No sysfs mounted")
}

func writeIntStringFile(filename string, value int) error {
	return ioutil.WriteFile(filename, []byte(strconv.Itoa(value)), 0640)
}
<|endoftext|>


<commit_before>package server

import (
	"net/http"
	"os"
	"path"
	"strings"

	"github.com/mholt/caddy/middleware"
	"github.com/mholt/caddy/middleware/browse"
)

// This FileServer is adapted from the one in net/http by
// the Go authors. Significant modifications have been made.
//
//
// License:
//
// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
func FileServer(root http.FileSystem, hide []string) middleware.Handler {
	return &fileHandler{root: root, hide: hide}
}

type fileHandler struct {
	root http.FileSystem
	hide []string // list of files to treat as "Not Found"
}

func (f *fileHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, error) {
	upath := r.URL.Path
	if !strings.HasPrefix(upath, "/") {
		upath = "/" + upath
		r.URL.Path = upath
	}
	return f.serveFile(w, r, path.Clean(upath))
}

// name is '/'-separated, not filepath.Separator.
func (fh *fileHandler) serveFile(w http.ResponseWriter, r *http.Request, name string) (int, error) {
	f, err := fh.root.Open(name)
	if err != nil {
		if os.IsPermission(err) {
			return http.StatusForbidden, err
		}
		return http.StatusNotFound, nil
	}
	defer f.Close()

	d, err1 := f.Stat()
	if err1 != nil {
		if os.IsPermission(err) {
			return http.StatusForbidden, err
		}
		return http.StatusNotFound, nil
	}

	// redirect to canonical path
	url := r.URL.Path
	if d.IsDir() {
		// Ensure / at end of directory url
		if url[len(url)-1] != '/' {
			redirect(w, r, path.Base(url)+"/")
			return http.StatusMovedPermanently, nil
		}
	} else {
		// Ensure no / at end of file url
		if url[len(url)-1] == '/' {
			redirect(w, r, "../"+path.Base(url))
			return http.StatusMovedPermanently, nil
		}
	}

	// use contents of an index file, if present, for directory
	if d.IsDir() {
		for _, indexPage := range browse.IndexPages {
			index := strings.TrimSuffix(name, "/") + "/" + indexPage
			ff, err := fh.root.Open(index)
			if err == nil {
				defer ff.Close()
				dd, err := ff.Stat()
				if err == nil {
					name = index
					d = dd
					f = ff
					break
				}
			}
		}
	}

	// Still a directory? (we didn't find an index file)
	// Return 404 to hide the fact that the folder exists
	if d.IsDir() {
		return http.StatusNotFound, nil
	}

	// If the file is supposed to be hidden, return a 404
	// (TODO: If the slice gets large, a set may be faster)
	for _, hiddenPath := range fh.hide {
		if d.Name() == hiddenPath {
			return http.StatusNotFound, nil
		}
	}

	// Note: Errors generated by ServeContent are written immediately
	// to the response. This usually only happens if seeking fails (rare).
	http.ServeContent(w, r, d.Name(), d.ModTime(), f)

	return http.StatusOK, nil
}

// redirect is taken from http.localRedirect of the std lib. It
// sends an HTTP redirect to the client but will preserve the
// query string for the new path.
func redirect(w http.ResponseWriter, r *http.Request, newPath string) {
	if q := r.URL.RawQuery; q != "" {
		newPath += "?" + q
	}
	http.Redirect(w, r, newPath, http.StatusMovedPermanently)
}
<commit_msg>Fixed config file leak, but new todo item<commit_after>package server

import (
	"net/http"
	"os"
	"path"
	"strings"

	"github.com/mholt/caddy/middleware"
	"github.com/mholt/caddy/middleware/browse"
)

// This FileServer is adapted from the one in net/http by
// the Go authors. Significant modifications have been made.
//
//
// License:
//
// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
func FileServer(root http.FileSystem, hide []string) middleware.Handler {
	return &fileHandler{root: root, hide: hide}
}

type fileHandler struct {
	root http.FileSystem
	hide []string // list of files to treat as "Not Found"
}

func (f *fileHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, error) {
	upath := r.URL.Path
	if !strings.HasPrefix(upath, "/") {
		upath = "/" + upath
		r.URL.Path = upath
	}
	return f.serveFile(w, r, path.Clean(upath))
}

// name is '/'-separated, not filepath.Separator.
func (fh *fileHandler) serveFile(w http.ResponseWriter, r *http.Request, name string) (int, error) {
	f, err := fh.root.Open(name)
	if err != nil {
		if os.IsPermission(err) {
			return http.StatusForbidden, err
		}
		return http.StatusNotFound, nil
	}
	defer f.Close()

	d, err1 := f.Stat()
	if err1 != nil {
		if os.IsPermission(err) {
			return http.StatusForbidden, err
		}
		return http.StatusNotFound, nil
	}

	// redirect to canonical path
	url := r.URL.Path
	if d.IsDir() {
		// Ensure / at end of directory url
		if url[len(url)-1] != '/' {
			redirect(w, r, path.Base(url)+"/")
			return http.StatusMovedPermanently, nil
		}
	} else {
		// Ensure no / at end of file url
		if url[len(url)-1] == '/' {
			redirect(w, r, "../"+path.Base(url))
			return http.StatusMovedPermanently, nil
		}
	}

	// use contents of an index file, if present, for directory
	if d.IsDir() {
		for _, indexPage := range browse.IndexPages {
			index := strings.TrimSuffix(name, "/") + "/" + indexPage
			ff, err := fh.root.Open(index)
			if err == nil {
				defer ff.Close()
				dd, err := ff.Stat()
				if err == nil {
					name = index
					d = dd
					f = ff
					break
				}
			}
		}
	}

	// Still a directory? (we didn't find an index file)
	// Return 404 to hide the fact that the folder exists
	if d.IsDir() {
		return http.StatusNotFound, nil
	}

	// If the file is supposed to be hidden, return a 404
	// (TODO: If the slice gets large, a set may be faster)
	for _, hiddenPath := range fh.hide {
		// Case-insensitive file systems may have loaded "CaddyFile" when
		// we think we got "Caddyfile", which poses a security risk if we
		// aren't careful here: case-insensitive comparison is required!
		// TODO: This matches file NAME only, regardless of path. In other
		// words, trying to serve another file with the same name as the
		// active config file will result in a 404 when it shouldn't.
		if strings.EqualFold(d.Name(), path.Base(hiddenPath)) {
			return http.StatusNotFound, nil
		}
	}

	// Note: Errors generated by ServeContent are written immediately
	// to the response. This usually only happens if seeking fails (rare).
	http.ServeContent(w, r, d.Name(), d.ModTime(), f)

	return http.StatusOK, nil
}

// redirect is taken from http.localRedirect of the std lib. It
// sends an HTTP redirect to the client but will preserve the
// query string for the new path.
func redirect(w http.ResponseWriter, r *http.Request, newPath string) {
	if q := r.URL.RawQuery; q != "" {
		newPath += "?" + q
	}
	http.Redirect(w, r, newPath, http.StatusMovedPermanently)
}
<|endoftext|>