<commit_before>package main

import (
	"bytes"
	"crypto/tls"
	"crypto/x509"
	"fmt"
	"log"
	"net"
	"strconv"
	"time"
)

var (
	ping   = []byte("ping")
	closed = []byte("close")
)

// Listener accepts connections from devices.
type Listener struct {
	debug    bool
	listener net.Listener
}

// Listen creates a TCP listener with the given PEM encoded X.509 certificate and the private key on the local network address laddr.
// Debug mode logs all server activity.
func Listen(cert, privKey []byte, laddr string, debug bool) (*Listener, error) {
	tlsCert, err := tls.X509KeyPair(cert, privKey)
	pool := x509.NewCertPool()
	ok := pool.AppendCertsFromPEM(cert)
	if err != nil || !ok {
		return nil, fmt.Errorf("failed to parse the certificate or the private key with error: %v", err)
	}

	config := tls.Config{
		Certificates: []tls.Certificate{tlsCert},
		ClientCAs:    pool,
		ClientAuth:   tls.VerifyClientCertIfGiven,
	}

	listener, err := tls.Listen("tcp", laddr, &config)
	if err != nil {
		return nil, err
	}
	if debug {
		log.Printf("Listener created with local network address: %v\n", laddr)
	}

	return &Listener{
		debug:    debug,
		listener: listener,
	}, nil
}

type Session struct {
	id   int
	data interface{}
}

// Accept waits for incoming connections and forwards the client connect/message/disconnect
// events to provided handlers in a new goroutine.
// This function never returns, unless there is an error while accepting a new connection.
func (l *Listener) Accept(handleMsg func(conn *tls.Conn, sess *Session, msg []byte), handleDisconn func(conn *tls.Conn, sess *Session)) error {
	for {
		conn, err := l.listener.Accept()
		if err != nil {
			return fmt.Errorf("error while accepting a new connection from a client: %v", err)
			// todo: it might not be appropriate to break the loop on recoverable errors (like client disconnect during handshake)
			// the underlying fd.accept() does some basic recovery though we might need more: http://golang.org/src/net/fd_unix.go
		}
		tlsconn, _ := conn.(*tls.Conn) // todo: check ok
		if l.debug {
			log.Println("Client connected: listening for messages from client IP:", conn.RemoteAddr())
		}
		go handleClient(tlsconn, l.debug, handleMsg, handleDisconn)
	}
}

// handleClient waits for messages from the connected client and forwards the client message/disconnect
// events to provided handlers in a new goroutine.
// This function never returns, unless there is an error while reading from the channel or the client disconnects.
func handleClient(conn *tls.Conn, debug bool, handleMsg func(conn *tls.Conn, session *Session, msg []byte), handleDisconn func(conn *tls.Conn, session *Session)) {
	defer conn.Close()
	if debug {
		defer log.Println("Closed connection to client with IP:", conn.RemoteAddr())
	}

	header := make([]byte, 4) // so max message size is 9999 bytes
	session := &Session{id: 1}

	for {
		err := conn.SetReadDeadline(time.Now().Add(time.Minute * 5))

		// read the content length header
		n, err := conn.Read(header)
		if err != nil || n == 0 {
			log.Fatalln("Client read error: ", err)
			break
		}

		// calculate the content length
		th := bytes.TrimRight(header, " ")
		n, err = strconv.Atoi(string(th))
		if err != nil || n == 0 {
			log.Fatalln("Client read error: invalid content lenght header sent or content lenght mismatch: ", err)
			break
		}

		// read the message content
		if debug {
			log.Println("Starting to read message content of bytes: ", n)
		}
		msg := make([]byte, n)
		n, err = conn.Read(msg)
		if err != nil || n == 0 {
			log.Fatalln("Client read error: ", err)
			break
		}
		if debug {
			log.Printf("Read %v bytes message '%v' from client with IP: %v\n", n, string(msg), conn.RemoteAddr())
		}

		if n == 4 && bytes.Equal(msg, ping) {
			continue
		}

		if n == 5 && bytes.Equal(msg, closed) {
			go handleDisconn(conn, session)
			return
		}

		go handleMsg(conn, session, msg)
	}
}

// Close closes the listener.
func (l *Listener) Close() error {
	if l.debug {
		defer log.Println("Listener was closed on local network address:", l.listener.Addr())
	}
	return l.listener.Close()
}
<commit_msg>fix typo<commit_after>package main

import (
	"bytes"
	"crypto/tls"
	"crypto/x509"
	"fmt"
	"log"
	"net"
	"strconv"
	"time"
)

var (
	ping   = []byte("ping")
	closed = []byte("close")
)

// Listener accepts connections from devices.
type Listener struct {
	debug    bool
	listener net.Listener
}

// Listen creates a TCP listener with the given PEM encoded X.509 certificate and the private key on the local network address laddr.
// Debug mode logs all server activity.
func Listen(cert, privKey []byte, laddr string, debug bool) (*Listener, error) {
	tlsCert, err := tls.X509KeyPair(cert, privKey)
	pool := x509.NewCertPool()
	ok := pool.AppendCertsFromPEM(cert)
	if err != nil || !ok {
		return nil, fmt.Errorf("failed to parse the certificate or the private key with error: %v", err)
	}

	config := tls.Config{
		Certificates: []tls.Certificate{tlsCert},
		ClientCAs:    pool,
		ClientAuth:   tls.VerifyClientCertIfGiven,
	}

	listener, err := tls.Listen("tcp", laddr, &config)
	if err != nil {
		return nil, err
	}
	if debug {
		log.Printf("Listener created with local network address: %v\n", laddr)
	}

	return &Listener{
		debug:    debug,
		listener: listener,
	}, nil
}

type Session struct {
	id   int
	data interface{}
}

// Accept waits for incoming connections and forwards the client connect/message/disconnect
// events to provided handlers in a new goroutine.
// This function never returns, unless there is an error while accepting a new connection.
func (l *Listener) Accept(handleMsg func(conn *tls.Conn, session *Session, msg []byte), handleDisconn func(conn *tls.Conn, session *Session)) error {
	for {
		conn, err := l.listener.Accept()
		if err != nil {
			return fmt.Errorf("error while accepting a new connection from a client: %v", err)
			// todo: it might not be appropriate to break the loop on recoverable errors (like client disconnect during handshake)
			// the underlying fd.accept() does some basic recovery though we might need more: http://golang.org/src/net/fd_unix.go
		}
		tlsconn, _ := conn.(*tls.Conn) // todo: check ok
		if l.debug {
			log.Println("Client connected: listening for messages from client IP:", conn.RemoteAddr())
		}
		go handleClient(tlsconn, l.debug, handleMsg, handleDisconn)
	}
}

// handleClient waits for messages from the connected client and forwards the client message/disconnect
// events to provided handlers in a new goroutine.
// This function never returns, unless there is an error while reading from the channel or the client disconnects.
func handleClient(conn *tls.Conn, debug bool, handleMsg func(conn *tls.Conn, session *Session, msg []byte), handleDisconn func(conn *tls.Conn, session *Session)) {
	defer conn.Close()
	if debug {
		defer log.Println("Closed connection to client with IP:", conn.RemoteAddr())
	}

	header := make([]byte, 4) // so max message size is 9999 bytes
	session := &Session{id: 1}

	for {
		err := conn.SetReadDeadline(time.Now().Add(time.Minute * 5))

		// read the content length header
		n, err := conn.Read(header)
		if err != nil || n == 0 {
			log.Fatalln("Client read error: ", err)
			break
		}

		// calculate the content length
		th := bytes.TrimRight(header, " ")
		n, err = strconv.Atoi(string(th))
		if err != nil || n == 0 {
			log.Fatalln("Client read error: invalid content lenght header sent or content lenght mismatch: ", err)
			break
		}

		// read the message content
		if debug {
			log.Println("Starting to read message content of bytes: ", n)
		}
		msg := make([]byte, n)
		n, err = conn.Read(msg)
		if err != nil || n == 0 {
			log.Fatalln("Client read error: ", err)
			break
		}
		if debug {
			log.Printf("Read %v bytes message '%v' from client with IP: %v\n", n, string(msg), conn.RemoteAddr())
		}

		if n == 4 && bytes.Equal(msg, ping) {
			continue
		}

		if n == 5 && bytes.Equal(msg, closed) {
			go handleDisconn(conn, session)
			return
		}

		go handleMsg(conn, session, msg)
	}
}

// Close closes the listener.
func (l *Listener) Close() error {
	if l.debug {
		defer log.Println("Listener was closed on local network address:", l.listener.Addr())
	}
	return l.listener.Close()
}
<|endoftext|>


<commit_before>package main

import (
	"crypto/sha256"
	"encoding/hex"
	"encoding/json"
	"flag"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"os"
	"os/exec"
	"path/filepath"
	"runtime"
	"strings"

	"github.com/git-lfs/git-lfs/config"
)

var (
	BuildOS    = flag.String("os", runtime.GOOS, "OS to target: darwin, freebsd, linux, windows")
	BuildArch  = flag.String("arch", "", "Arch to target: 386, amd64")
	BuildAll   = flag.Bool("all", false, "Builds all architectures")
	BuildDwarf = flag.Bool("dwarf", false, "Includes DWARF tables in build artifacts")
	ShowHelp   = flag.Bool("help", false, "Shows help")
	matrixKeys = map[string]string{
		"darwin":  "Mac",
		"freebsd": "FreeBSD",
		"linux":   "Linux",
		"windows": "Windows",
		"amd64":   "AMD64",
	}
	LdFlags []string
)

func mainBuild() {
	if *ShowHelp {
		fmt.Println("usage: script/bootstrap [-os] [-arch] [-all]")
		flag.PrintDefaults()
		return
	}

	fmt.Printf("Using %s\n", runtime.Version())

	genOut, err := exec.Command("go", "generate", "./commands").CombinedOutput()
	if err != nil {
		fmt.Fprintf(os.Stderr, "go generate failed:\n%v", string(genOut))
		os.Exit(1)
	}
	cmd, _ := exec.Command("git", "rev-parse", "--short", "HEAD").Output()

	if len(cmd) > 0 {
		LdFlags = append(LdFlags, "-X", strings.TrimSpace(
			"github.com/git-lfs/git-lfs/config.GitCommit="+string(cmd),
		))
	}
	if !*BuildDwarf {
		LdFlags = append(LdFlags, "-s", "-w")
	}

	buildMatrix := make(map[string]Release)
	errored := false

	if *BuildAll {
		for _, buildos := range []string{"linux", "darwin", "freebsd", "windows"} {
			for _, buildarch := range []string{"amd64", "386"} {
				if err := build(buildos, buildarch, buildMatrix); err != nil {
					errored = true
				}
			}
		}
	} else {
		if err := build(*BuildOS, *BuildArch, buildMatrix); err != nil {
			log.Fatalln(err)
		}
		return // skip build matrix stuff
	}

	if errored {
		os.Exit(1)
	}

	by, err := json.Marshal(buildMatrix)
	if err != nil {
		log.Fatalln("Error encoding build matrix to json:", err)
	}

	file, err := os.Create("bin/releases/build_matrix.json")
	if err != nil {
		log.Fatalln("Error creating build_matrix.json:", err)
	}

	written, err := file.Write(by)
	file.Close()

	if err != nil {
		log.Fatalln("Error writing build_matrix.json", err)
	}

	if jsonSize := len(by); written != jsonSize {
		log.Fatalf("Expected to write %d bytes, actually wrote %d.\n", jsonSize, written)
	}
}

func build(buildos, buildarch string, buildMatrix map[string]Release) error {
	addenv := len(buildos) > 0 && len(buildarch) > 0
	name := "git-lfs-" + config.Version
	dir := "bin"

	if addenv {
		fmt.Printf("Building for %s/%s\n", buildos, buildarch)
		dir = filepath.Join(dir, "releases", buildos+"-"+buildarch, name)
	}

	if err := buildCommand(dir, buildos, buildarch); err != nil {
		return err
	}

	if addenv {
		err := os.MkdirAll(dir, 0755)
		if err != nil {
			log.Println("Error setting up installer:\n", err.Error())
			return err
		}

		err = setupInstaller(buildos, buildarch, dir, buildMatrix)
		if err != nil {
			log.Println("Error setting up installer:\n", err.Error())
			return err
		}
	}

	return nil
}

func buildCommand(dir, buildos, buildarch string) error {
	addenv := len(buildos) > 0 && len(buildarch) > 0

	bin := filepath.Join(dir, "git-lfs")

	if buildos == "windows" {
		bin = bin + ".exe"
	}

	args := make([]string, 1, 6)
	args[0] = "build"
	if len(LdFlags) > 0 {
		args = append(args, "-ldflags", strings.Join(LdFlags, " "))
	}
	args = append(args, "-o", bin, ".")

	cmd := exec.Command("go", args...)
	if addenv {
		cmd.Env = buildGoEnv(buildos, buildarch)
	}

	output, err := cmd.CombinedOutput()
	if len(output) > 0 {
		fmt.Println(string(output))
	}
	return err
}

func buildGoEnv(buildos, buildarch string) []string {
	env := make([]string, 6, 9)
	env[0] = "GOOS=" + buildos
	env[1] = "GOARCH=" + buildarch
	env[2] = "GOPATH=" + os.Getenv("GOPATH")
	env[3] = "GOROOT=" + os.Getenv("GOROOT")
	env[4] = "PATH=" + os.Getenv("PATH")
	env[5] = "GO15VENDOREXPERIMENT=" + os.Getenv("GO15VENDOREXPERIMENT")
	for _, key := range []string{"TMP", "TEMP", "TEMPDIR"} {
		v := os.Getenv(key)
		if len(v) == 0 {
			continue
		}
		env = append(env, key+"="+v)
	}
	return env
}

func setupInstaller(buildos, buildarch, dir string, buildMatrix map[string]Release) error {
	textfiles := []string{
		"README.md", "CHANGELOG.md",
	}

	if buildos == "windows" {
		return winInstaller(textfiles, buildos, buildarch, dir, buildMatrix)
	} else {
		return unixInstaller(textfiles, buildos, buildarch, dir, buildMatrix)
	}
}

func unixInstaller(textfiles []string, buildos, buildarch, dir string, buildMatrix map[string]Release) error {
	for _, filename := range textfiles {
		cmd := exec.Command("cp", filename, filepath.Join(dir, filename))
		if err := logAndRun(cmd); err != nil {
			return err
		}
	}

	fullInstallPath := filepath.Join(dir, "install.sh")
	cmd := exec.Command("cp", "script/install.sh.example", fullInstallPath)
	if err := logAndRun(cmd); err != nil {
		return err
	}

	if err := os.Chmod(fullInstallPath, 0755); err != nil {
		return err
	}

	name := zipName(buildos, buildarch) + ".tar.gz"
	cmd = exec.Command("tar", "czf", "../"+name, filepath.Base(dir))
	cmd.Dir = filepath.Dir(dir)
	if err := logAndRun(cmd); err != nil {
		return nil
	}

	addToMatrix(buildMatrix, buildos, buildarch, name)
	return nil
}

func winInstaller(textfiles []string, buildos, buildarch, dir string, buildMatrix map[string]Release) error {
	for _, filename := range textfiles {
		by, err := ioutil.ReadFile(filename)
		if err != nil {
			return err
		}

		winEndings := strings.Replace(string(by), "\n", "\r\n", -1)
		err = ioutil.WriteFile(filepath.Join(dir, filename), []byte(winEndings), 0644)
		if err != nil {
			return err
		}
	}

	installerPath := filepath.Dir(filepath.Dir(dir))
	name := zipName(buildos, buildarch) + ".zip"
	full := filepath.Join(installerPath, name)
	matches, err := filepath.Glob(dir + "/*")
	if err != nil {
		return err
	}

	args := make([]string, len(matches)+2)
	args[0] = "-j" // junk the zip paths
	args[1] = full
	copy(args[2:], matches)

	cmd := exec.Command("zip", args...)
	if err := logAndRun(cmd); err != nil {
		return err
	}

	addToMatrix(buildMatrix, buildos, buildarch, name)
	return nil
}

func addToMatrix(buildMatrix map[string]Release, buildos, buildarch, name string) {
	buildMatrix[fmt.Sprintf("%s-%s", buildos, buildarch)] = Release{
		Label:    releaseLabel(buildos, buildarch),
		Filename: name,
		SHA256:   hashRelease(name),
	}
}

func hashRelease(name string) string {
	full := filepath.Join("bin/releases", name)
	file, err := os.Open(full)
	if err != nil {
		fmt.Printf("unable to open release %q: %+v\n", full, err)
		os.Exit(1)
	}

	defer file.Close()

	h := sha256.New()
	if _, err = io.Copy(h, file); err != nil {
		fmt.Printf("error reading release %q: %+v\n", full, err)
		os.Exit(1)
	}

	return hex.EncodeToString(h.Sum(nil))
}

func logAndRun(cmd *exec.Cmd) error {
	fmt.Printf(" - %s\n", strings.Join(cmd.Args, " "))
	if len(cmd.Dir) > 0 {
		fmt.Printf("   - in %s\n", cmd.Dir)
	}

	output, err := cmd.CombinedOutput()
	fmt.Println(string(output))
	return err
}

func zipName(os, arch string) string {
	return fmt.Sprintf("git-lfs-%s-%s-%s", os, arch, config.Version)
}

func releaseLabel(buildos, buildarch string) string {
	return fmt.Sprintf("%s %s", key(buildos), key(buildarch))
}

func key(k string) string {
	if s, ok := matrixKeys[k]; ok {
		return s
	}
	return k
}
<commit_msg>script/build: pass -ldflags to compiler<commit_after>package main

import (
	"crypto/sha256"
	"encoding/hex"
	"encoding/json"
	"flag"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"os"
	"os/exec"
	"path/filepath"
	"runtime"
	"strings"

	"github.com/git-lfs/git-lfs/config"
)

var (
	BuildOS      = flag.String("os", runtime.GOOS, "OS to target: darwin, freebsd, linux, windows")
	BuildArch    = flag.String("arch", "", "Arch to target: 386, amd64")
	BuildAll     = flag.Bool("all", false, "Builds all architectures")
	BuildDwarf   = flag.Bool("dwarf", false, "Includes DWARF tables in build artifacts")
	BuildLdFlags = flag.String("ldflags", "", "-ldflags to pass to the compiler")
	ShowHelp     = flag.Bool("help", false, "Shows help")
	matrixKeys   = map[string]string{
		"darwin":  "Mac",
		"freebsd": "FreeBSD",
		"linux":   "Linux",
		"windows": "Windows",
		"amd64":   "AMD64",
	}
	LdFlags []string
)

func mainBuild() {
	if *ShowHelp {
		fmt.Println("usage: script/bootstrap [-os] [-arch] [-all]")
		flag.PrintDefaults()
		return
	}

	fmt.Printf("Using %s\n", runtime.Version())

	genOut, err := exec.Command("go", "generate", "./commands").CombinedOutput()
	if err != nil {
		fmt.Fprintf(os.Stderr, "go generate failed:\n%v", string(genOut))
		os.Exit(1)
	}
	cmd, _ := exec.Command("git", "rev-parse", "--short", "HEAD").Output()

	if len(cmd) > 0 {
		LdFlags = append(LdFlags, "-X", strings.TrimSpace(
			"github.com/git-lfs/git-lfs/config.GitCommit="+string(cmd),
		))
	}
	if !*BuildDwarf {
		LdFlags = append(LdFlags, "-s", "-w")
	}

	buildMatrix := make(map[string]Release)
	errored := false

	if *BuildAll {
		for _, buildos := range []string{"linux", "darwin", "freebsd", "windows"} {
			for _, buildarch := range []string{"amd64", "386"} {
				if err := build(buildos, buildarch, buildMatrix); err != nil {
					errored = true
				}
			}
		}
	} else {
		if err := build(*BuildOS, *BuildArch, buildMatrix); err != nil {
			log.Fatalln(err)
		}
		return // skip build matrix stuff
	}

	if errored {
		os.Exit(1)
	}

	by, err := json.Marshal(buildMatrix)
	if err != nil {
		log.Fatalln("Error encoding build matrix to json:", err)
	}

	file, err := os.Create("bin/releases/build_matrix.json")
	if err != nil {
		log.Fatalln("Error creating build_matrix.json:", err)
	}

	written, err := file.Write(by)
	file.Close()

	if err != nil {
		log.Fatalln("Error writing build_matrix.json", err)
	}

	if jsonSize := len(by); written != jsonSize {
		log.Fatalf("Expected to write %d bytes, actually wrote %d.\n", jsonSize, written)
	}
}

func build(buildos, buildarch string, buildMatrix map[string]Release) error {
	addenv := len(buildos) > 0 && len(buildarch) > 0
	name := "git-lfs-" + config.Version
	dir := "bin"

	if addenv {
		fmt.Printf("Building for %s/%s\n", buildos, buildarch)
		dir = filepath.Join(dir, "releases", buildos+"-"+buildarch, name)
	}

	if err := buildCommand(dir, buildos, buildarch); err != nil {
		return err
	}

	if addenv {
		err := os.MkdirAll(dir, 0755)
		if err != nil {
			log.Println("Error setting up installer:\n", err.Error())
			return err
		}

		err = setupInstaller(buildos, buildarch, dir, buildMatrix)
		if err != nil {
			log.Println("Error setting up installer:\n", err.Error())
			return err
		}
	}

	return nil
}

func buildCommand(dir, buildos, buildarch string) error {
	addenv := len(buildos) > 0 && len(buildarch) > 0

	bin := filepath.Join(dir, "git-lfs")

	if buildos == "windows" {
		bin = bin + ".exe"
	}

	args := make([]string, 1, 6)
	args[0] = "build"
	if len(*BuildLdFlags) > 0 {
		args = append(args, "-ldflags", *BuildLdFlags)
	} else if len(LdFlags) > 0 {
		args = append(args, "-ldflags", strings.Join(LdFlags, " "))
	}
	args = append(args, "-o", bin, ".")

	cmd := exec.Command("go", args...)
	if addenv {
		cmd.Env = buildGoEnv(buildos, buildarch)
	}

	output, err := cmd.CombinedOutput()
	if len(output) > 0 {
		fmt.Println(string(output))
	}
	return err
}

func buildGoEnv(buildos, buildarch string) []string {
	env := make([]string, 6, 9)
	env[0] = "GOOS=" + buildos
	env[1] = "GOARCH=" + buildarch
	env[2] = "GOPATH=" + os.Getenv("GOPATH")
	env[3] = "GOROOT=" + os.Getenv("GOROOT")
	env[4] = "PATH=" + os.Getenv("PATH")
	env[5] = "GO15VENDOREXPERIMENT=" + os.Getenv("GO15VENDOREXPERIMENT")
	for _, key := range []string{"TMP", "TEMP", "TEMPDIR"} {
		v := os.Getenv(key)
		if len(v) == 0 {
			continue
		}
		env = append(env, key+"="+v)
	}
	return env
}

func setupInstaller(buildos, buildarch, dir string, buildMatrix map[string]Release) error {
	textfiles := []string{
		"README.md", "CHANGELOG.md",
	}

	if buildos == "windows" {
		return winInstaller(textfiles, buildos, buildarch, dir, buildMatrix)
	} else {
		return unixInstaller(textfiles, buildos, buildarch, dir, buildMatrix)
	}
}

func unixInstaller(textfiles []string, buildos, buildarch, dir string, buildMatrix map[string]Release) error {
	for _, filename := range textfiles {
		cmd := exec.Command("cp", filename, filepath.Join(dir, filename))
		if err := logAndRun(cmd); err != nil {
			return err
		}
	}

	fullInstallPath := filepath.Join(dir, "install.sh")
	cmd := exec.Command("cp", "script/install.sh.example", fullInstallPath)
	if err := logAndRun(cmd); err != nil {
		return err
	}

	if err := os.Chmod(fullInstallPath, 0755); err != nil {
		return err
	}

	name := zipName(buildos, buildarch) + ".tar.gz"
	cmd = exec.Command("tar", "czf", "../"+name, filepath.Base(dir))
	cmd.Dir = filepath.Dir(dir)
	if err := logAndRun(cmd); err != nil {
		return nil
	}

	addToMatrix(buildMatrix, buildos, buildarch, name)
	return nil
}

func winInstaller(textfiles []string, buildos, buildarch, dir string, buildMatrix map[string]Release) error {
	for _, filename := range textfiles {
		by, err := ioutil.ReadFile(filename)
		if err != nil {
			return err
		}

		winEndings := strings.Replace(string(by), "\n", "\r\n", -1)
		err = ioutil.WriteFile(filepath.Join(dir, filename), []byte(winEndings), 0644)
		if err != nil {
			return err
		}
	}

	installerPath := filepath.Dir(filepath.Dir(dir))
	name := zipName(buildos, buildarch) + ".zip"
	full := filepath.Join(installerPath, name)
	matches, err := filepath.Glob(dir + "/*")
	if err != nil {
		return err
	}

	args := make([]string, len(matches)+2)
	args[0] = "-j" // junk the zip paths
	args[1] = full
	copy(args[2:], matches)

	cmd := exec.Command("zip", args...)
	if err := logAndRun(cmd); err != nil {
		return err
	}

	addToMatrix(buildMatrix, buildos, buildarch, name)
	return nil
}

func addToMatrix(buildMatrix map[string]Release, buildos, buildarch, name string) {
	buildMatrix[fmt.Sprintf("%s-%s", buildos, buildarch)] = Release{
		Label:    releaseLabel(buildos, buildarch),
		Filename: name,
		SHA256:   hashRelease(name),
	}
}

func hashRelease(name string) string {
	full := filepath.Join("bin/releases", name)
	file, err := os.Open(full)
	if err != nil {
		fmt.Printf("unable to open release %q: %+v\n", full, err)
		os.Exit(1)
	}

	defer file.Close()

	h := sha256.New()
	if _, err = io.Copy(h, file); err != nil {
		fmt.Printf("error reading release %q: %+v\n", full, err)
		os.Exit(1)
	}

	return hex.EncodeToString(h.Sum(nil))
}

func logAndRun(cmd *exec.Cmd) error {
	fmt.Printf(" - %s\n", strings.Join(cmd.Args, " "))
	if len(cmd.Dir) > 0 {
		fmt.Printf("   - in %s\n", cmd.Dir)
	}

	output, err := cmd.CombinedOutput()
	fmt.Println(string(output))
	return err
}

func zipName(os, arch string) string {
	return fmt.Sprintf("git-lfs-%s-%s-%s", os, arch, config.Version)
}

func releaseLabel(buildos, buildarch string) string {
	return fmt.Sprintf("%s %s", key(buildos), key(buildarch))
}

func key(k string) string {
	if s, ok := matrixKeys[k]; ok {
		return s
	}
	return k
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"flag"
	"fmt"
	"log"
	"os"
	"os/exec"
	"path/filepath"
	"strings"

	"github.com/github/git-lfs/lfs"
)

var (
	BuildOS    = flag.String("os", "", "OS to target: darwin, freebsd, linux, windows")
	BuildArch  = flag.String("arch", "", "Arch to target: 386, amd64")
	BuildAll   = flag.Bool("all", false, "Builds all architectures")
	ShowHelp   = flag.Bool("help", false, "Shows help")
	matrixKeys = map[string]string{
		"darwin":  "Mac",
		"freebsd": "FreeBSD",
		"linux":   "Linux",
		"windows": "Windows",
		"amd64":   "AMD64",
	}
	LdFlag string
)

func mainBuild() {
	if *ShowHelp {
		fmt.Println("usage: script/bootstrap [-os] [-arch] [-all]")
		flag.PrintDefaults()
		return
	}

	cmd, err := exec.Command("git", "rev-parse", "--short", "HEAD").Output()

	if len(cmd) > 0 {
		LdFlag = strings.TrimSpace("-X github.com/github/git-lfs/lfs.GitCommit " + string(cmd))
	}

	buildMatrix := make(map[string]Release)
	errored := false

	if *BuildAll {
		for _, buildos := range []string{"darwin", "freebsd", "linux", "windows"} {
			for _, buildarch := range []string{"386", "amd64"} {
				if err := build(buildos, buildarch, buildMatrix); err != nil {
					errored = true
				}
			}
		}
	} else {
		if err := build(*BuildOS, *BuildArch, buildMatrix); err != nil {
			log.Fatalln(err)
		}
		return // skip build matrix stuff
	}

	if errored {
		os.Exit(1)
	}

	by, err := json.Marshal(buildMatrix)
	if err != nil {
		log.Fatalln("Error encoding build matrix to json:", err)
	}

	file, err := os.Create("bin/releases/build_matrix.json")
	if err != nil {
		log.Fatalln("Error creating build_matrix.json:", err)
	}

	written, err := file.Write(by)
	file.Close()

	if err != nil {
		log.Fatalln("Error writing build_matrix.json", err)
	}

	if jsonSize := len(by); written != jsonSize {
		log.Fatalf("Expected to write %d bytes, actually wrote %d.\n", jsonSize, written)
	}
}

func build(buildos, buildarch string, buildMatrix map[string]Release) error {
	addenv := len(buildos) > 0 && len(buildarch) > 0
	name := "git-lfs-" + lfs.Version
	dir := "bin"

	if addenv {
		fmt.Printf("Building for %s/%s\n", buildos, buildarch)
		dir = filepath.Join(dir, "releases", buildos+"-"+buildarch, name)
	}

	if err := buildCommand(dir, buildos, buildarch); err != nil {
		return err
	}

	if addenv {
		err := os.MkdirAll(dir, 0755)
		if err != nil {
			log.Println("Error setting up installer:\n", err.Error())
			return err
		}

		err = setupInstaller(buildos, buildarch, dir, buildMatrix)
		if err != nil {
			log.Println("Error setting up installer:\n", err.Error())
			return err
		}
	}

	return nil
}

func buildCommand(dir, buildos, buildarch string) error {
	addenv := len(buildos) > 0 && len(buildarch) > 0

	bin := filepath.Join(dir, "git-lfs")

	if buildos == "windows" {
		bin = bin + ".exe"
	}

	args := make([]string, 1, 6)
	args[0] = "build"
	if len(LdFlag) > 0 {
		args = append(args, "-ldflags", LdFlag)
	}
	args = append(args, "-o", bin, ".")

	cmd := exec.Command("go", args...)
	if addenv {
		cmd.Env = []string{
			"GOOS=" + buildos,
			"GOARCH=" + buildarch,
			"GOPATH=" + os.Getenv("GOPATH"),
			"GOROOT=" + os.Getenv("GOROOT"),
		}
	}

	output, err := cmd.CombinedOutput()
	if len(output) > 0 {
		fmt.Println(string(output))
	}
	return err
}

func setupInstaller(buildos, buildarch, dir string, buildMatrix map[string]Release) error {
	if buildos == "windows" {
		return winInstaller(buildos, buildarch, dir, buildMatrix)
	} else {
		return unixInstaller(buildos, buildarch, dir, buildMatrix)
	}
}

func unixInstaller(buildos, buildarch, dir string, buildMatrix map[string]Release) error {
	fullInstallPath := filepath.Join(dir, "install.sh")
	cmd := exec.Command("cp", "script/install.sh.example", fullInstallPath)
	if err := logAndRun(cmd); err != nil {
		return err
	}

	if err := os.Chmod(fullInstallPath, 0755); err != nil {
		return err
	}

	name := zipName(buildos, buildarch) + ".tar.gz"

	addToMatrix(buildMatrix, buildos, buildarch, name)

	cmd = exec.Command("tar", "czf", "../"+name, filepath.Base(dir))
	cmd.Dir = filepath.Dir(dir)
	return logAndRun(cmd)
}

func addToMatrix(buildMatrix map[string]Release, buildos, buildarch, name string) {
	buildMatrix[fmt.Sprintf("%s-%s", buildos, buildarch)] = Release{
		Label:    releaseLabel(buildos, buildarch),
		Filename: name,
	}
}

func winInstaller(buildos, buildarch, dir string, buildMatrix map[string]Release) error {
	cmd := exec.Command("cp", "script/install.bat.example", filepath.Join(dir, "install.bat"))
	if err := logAndRun(cmd); err != nil {
		return err
	}

	installerPath := filepath.Dir(filepath.Dir(dir))

	name := zipName(buildos, buildarch) + ".zip"
	full := filepath.Join(installerPath, name)
	matches, err := filepath.Glob(dir + "/*")
	if err != nil {
		return err
	}

	addToMatrix(buildMatrix, buildos, buildarch, name)

	args := make([]string, len(matches)+2)
	args[0] = "-j" // junk the zip paths
	args[1] = full
	copy(args[2:], matches)

	cmd = exec.Command("zip", args...)
	return logAndRun(cmd)
}

func logAndRun(cmd *exec.Cmd) error {
	fmt.Printf(" - %s\n", strings.Join(cmd.Args, " "))
	if len(cmd.Dir) > 0 {
		fmt.Printf("   - in %s\n", cmd.Dir)
	}

	output, err := cmd.CombinedOutput()
	fmt.Println(string(output))
	return err
}

func zipName(os, arch string) string {
	return fmt.Sprintf("git-lfs-%s-%s-%s", os, arch, lfs.Version)
}

func releaseLabel(buildos, buildarch string) string {
	return fmt.Sprintf("%s %s", key(buildos), key(buildarch))
}

func key(k string) string {
	if s, ok := matrixKeys[k]; ok {
		return s
	}
	return k
}
<commit_msg>err can just be ignored completely<commit_after>package main

import (
	"encoding/json"
	"flag"
	"fmt"
	"log"
	"os"
	"os/exec"
	"path/filepath"
	"strings"

	"github.com/github/git-lfs/lfs"
)

var (
	BuildOS    = flag.String("os", "", "OS to target: darwin, freebsd, linux, windows")
	BuildArch  = flag.String("arch", "", "Arch to target: 386, amd64")
	BuildAll   = flag.Bool("all", false, "Builds all architectures")
	ShowHelp   = flag.Bool("help", false, "Shows help")
	matrixKeys = map[string]string{
		"darwin":  "Mac",
		"freebsd": "FreeBSD",
		"linux":   "Linux",
		"windows": "Windows",
		"amd64":   "AMD64",
	}
	LdFlag string
)

func mainBuild() {
	if *ShowHelp {
		fmt.Println("usage: script/bootstrap [-os] [-arch] [-all]")
		flag.PrintDefaults()
		return
	}

	cmd, _ := exec.Command("git", "rev-parse", "--short", "HEAD").Output()

	if len(cmd) > 0 {
		LdFlag = strings.TrimSpace("-X github.com/github/git-lfs/lfs.GitCommit " + string(cmd))
	}

	buildMatrix := make(map[string]Release)
	errored := false

	if *BuildAll {
		for _, buildos := range []string{"darwin", "freebsd", "linux", "windows"} {
			for _, buildarch := range []string{"386", "amd64"} {
				if err := build(buildos, buildarch, buildMatrix); err != nil {
					errored = true
				}
			}
		}
	} else {
		if err := build(*BuildOS, *BuildArch, buildMatrix); err != nil {
			log.Fatalln(err)
		}
		return // skip build matrix stuff
	}

	if errored {
		os.Exit(1)
	}

	by, err := json.Marshal(buildMatrix)
	if err != nil {
		log.Fatalln("Error encoding build matrix to json:", err)
	}

	file, err := os.Create("bin/releases/build_matrix.json")
	if err != nil {
		log.Fatalln("Error creating build_matrix.json:", err)
	}

	written, err := file.Write(by)
	file.Close()

	if err != nil {
		log.Fatalln("Error writing build_matrix.json", err)
	}

	if jsonSize := len(by); written != jsonSize {
		log.Fatalf("Expected to write %d bytes, actually wrote %d.\n", jsonSize, written)
	}
}

func build(buildos, buildarch string, buildMatrix map[string]Release) error {
	addenv := len(buildos) > 0 && len(buildarch) > 0
	name := "git-lfs-" + lfs.Version
	dir := "bin"

	if addenv {
		fmt.Printf("Building for %s/%s\n", buildos, buildarch)
		dir = filepath.Join(dir, "releases", buildos+"-"+buildarch, name)
	}

	if err := buildCommand(dir, buildos, buildarch); err != nil {
		return err
	}

	if addenv {
		err := os.MkdirAll(dir, 0755)
		if err != nil {
			log.Println("Error setting up installer:\n", err.Error())
			return err
		}

		err = setupInstaller(buildos, buildarch, dir, buildMatrix)
		if err != nil {
			log.Println("Error setting up installer:\n", err.Error())
			return err
		}
	}

	return nil
}

func buildCommand(dir, buildos, buildarch string) error {
	addenv := len(buildos) > 0 && len(buildarch) > 0

	bin := filepath.Join(dir, "git-lfs")

	if buildos == "windows" {
		bin = bin + ".exe"
	}

	args := make([]string, 1, 6)
	args[0] = "build"
	if len(LdFlag) > 0 {
		args = append(args, "-ldflags", LdFlag)
	}
	args = append(args, "-o", bin, ".")

	cmd := exec.Command("go", args...)
	if addenv {
		cmd.Env = []string{
			"GOOS=" + buildos,
			"GOARCH=" + buildarch,
			"GOPATH=" + os.Getenv("GOPATH"),
			"GOROOT=" + os.Getenv("GOROOT"),
		}
	}

	output, err := cmd.CombinedOutput()
	if len(output) > 0 {
		fmt.Println(string(output))
	}
	return err
}

func setupInstaller(buildos, buildarch, dir string, buildMatrix map[string]Release) error {
	if buildos == "windows" {
		return winInstaller(buildos, buildarch, dir, buildMatrix)
	} else {
		return unixInstaller(buildos, buildarch, dir, buildMatrix)
	}
}

func unixInstaller(buildos, buildarch, dir string, buildMatrix map[string]Release) error {
	fullInstallPath := filepath.Join(dir, "install.sh")
	cmd := exec.Command("cp", "script/install.sh.example", fullInstallPath)
	if err := logAndRun(cmd); err != nil {
		return err
	}

	if err := os.Chmod(fullInstallPath, 0755); err != nil {
		return err
	}

	name := zipName(buildos, buildarch) + ".tar.gz"

	addToMatrix(buildMatrix, buildos, buildarch, name)

	cmd = exec.Command("tar", "czf", "../"+name, filepath.Base(dir))
	cmd.Dir = filepath.Dir(dir)
	return logAndRun(cmd)
}

func addToMatrix(buildMatrix map[string]Release, buildos, buildarch, name string) {
	buildMatrix[fmt.Sprintf("%s-%s", buildos, buildarch)] = Release{
		Label:    releaseLabel(buildos, buildarch),
		Filename: name,
	}
}

func winInstaller(buildos, buildarch, dir string, buildMatrix map[string]Release) error {
	cmd := exec.Command("cp", "script/install.bat.example", filepath.Join(dir, "install.bat"))
	if err := logAndRun(cmd); err != nil {
		return err
	}

	installerPath := filepath.Dir(filepath.Dir(dir))

	name := zipName(buildos, buildarch) + ".zip"
	full := filepath.Join(installerPath, name)
	matches, err := filepath.Glob(dir + "/*")
	if err != nil {
		return err
	}

	addToMatrix(buildMatrix, buildos, buildarch, name)

	args := make([]string, len(matches)+2)
	args[0] = "-j" // junk the zip paths
	args[1] = full
	copy(args[2:], matches)

	cmd = exec.Command("zip", args...)
	return logAndRun(cmd)
}

func logAndRun(cmd *exec.Cmd) error {
	fmt.Printf(" - %s\n", strings.Join(cmd.Args, " "))
	if len(cmd.Dir) > 0 {
		fmt.Printf("   - in %s\n", cmd.Dir)
	}

	output, err := cmd.CombinedOutput()
	fmt.Println(string(output))
	return err
}

func zipName(os, arch string) string {
	return fmt.Sprintf("git-lfs-%s-%s-%s", os, arch, lfs.Version)
}

func releaseLabel(buildos, buildarch string) string {
	return fmt.Sprintf("%s %s", key(buildos), key(buildarch))
}

func key(k string) string {
	if s, ok := matrixKeys[k]; ok {
		return s
	}
	return k
}
<|endoftext|>


<commit_before>// Copyright 2012 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 (
	"fmt"
	"io/ioutil"
	"os"
	"os/exec"
	"path/filepath"
	"runtime"
	"sort"
	"strconv"
	"strings"
	"sync"
	"sync/atomic"
	"time"
)

// pathf is fmt.Sprintf for generating paths
// (on windows it turns / into \ after the printf).
func pathf(format string, args ...interface{}) string {
	return filepath.Clean(fmt.Sprintf(format, args...))
}

// filter returns a slice containing the elements x from list for which f(x) == true.
func filter(list []string, f func(string) bool) []string {
	var out []string
	for _, x := range list {
		if f(x) {
			out = append(out, x)
		}
	}
	return out
}

// uniq returns a sorted slice containing the unique elements of list.
func uniq(list []string) []string {
	out := make([]string, len(list))
	copy(out, list)
	sort.Strings(out)
	keep := out[:0]
	for _, x := range out {
		if len(keep) == 0 || keep[len(keep)-1] != x {
			keep = append(keep, x)
		}
	}
	return keep
}

// splitlines returns a slice with the result of splitting
// the input p after each \n.
func splitlines(p string) []string {
	return strings.SplitAfter(p, "\n")
}

// splitfields replaces the vector v with the result of splitting
// the input p into non-empty fields containing no spaces.
func splitfields(p string) []string {
	return strings.Fields(p)
}

const (
	CheckExit = 1 << iota
	ShowOutput
	Background
)

var outputLock sync.Mutex

// run runs the command line cmd in dir.
// If mode has ShowOutput set, run collects cmd's output and returns it as a string;
// otherwise, run prints cmd's output to standard output after the command finishes.
// If mode has CheckExit set and the command fails, run calls fatal.
// If mode has Background set, this command is being run as a
// Background job. Only bgrun should use the Background mode,
// not other callers.
func run(dir string, mode int, cmd ...string) string {
	if vflag > 1 {
		errprintf("run: %s\n", strings.Join(cmd, " "))
	}

	xcmd := exec.Command(cmd[0], cmd[1:]...)
	xcmd.Dir = dir
	var err error
	data, err := xcmd.CombinedOutput()
	if err != nil && mode&CheckExit != 0 {
		outputLock.Lock()
		if len(data) > 0 {
			xprintf("%s\n", data)
		}
		outputLock.Unlock()
		atomic.AddInt32(&ndone, +1)
		die := func() {
			time.Sleep(100 * time.Millisecond)
			fatal("FAILED: %v", strings.Join(cmd, " "))
		}
		if mode&Background != 0 {
			// This is a background run, and fatal will
			// wait for it to finish before exiting.
			// If we call fatal directly, that's a deadlock.
			// Instead, call fatal in a background goroutine
			// and let this run return normally, so that
			// fatal can wait for it to finish.
			go die()
		} else {
			die()
		}
	}
	if mode&ShowOutput != 0 {
		os.Stdout.Write(data)
	}
	return string(data)
}

var maxbg = 4 /* maximum number of jobs to run at once */

var (
	bgwork = make(chan func())
	bgdone = make(chan struct{}, 1e6)
	nwork  int32
	ndone  int32
)

func bginit() {
	for i := 0; i < maxbg; i++ {
		go bghelper()
	}
}

func bghelper() {
	for {
		(<-bgwork)()
	}
}

// bgrun is like run but runs the command in the background.
// CheckExit|ShowOutput mode is implied (since output cannot be returned).
func bgrun(dir string, cmd ...string) {
	bgwork <- func() {
		run(dir, CheckExit|ShowOutput|Background, cmd...)
	}
}

// bgwait waits for pending bgruns to finish.
func bgwait() {
	var wg sync.WaitGroup
	wg.Add(maxbg)
	for i := 0; i < maxbg; i++ {
		bgwork <- func() {
			wg.Done()
			wg.Wait()
		}
	}
	wg.Wait()
}

// xgetwd returns the current directory.
func xgetwd() string {
	wd, err := os.Getwd()
	if err != nil {
		fatal("%s", err)
	}
	return wd
}

// xrealwd returns the 'real' name for the given path.
// real is defined as what xgetwd returns in that directory.
func xrealwd(path string) string {
	old := xgetwd()
	if err := os.Chdir(path); err != nil {
		fatal("chdir %s: %v", path, err)
	}
	real := xgetwd()
	if err := os.Chdir(old); err != nil {
		fatal("chdir %s: %v", old, err)
	}
	return real
}

// isdir reports whether p names an existing directory.
func isdir(p string) bool {
	fi, err := os.Stat(p)
	return err == nil && fi.IsDir()
}

// isfile reports whether p names an existing file.
func isfile(p string) bool {
	fi, err := os.Stat(p)
	return err == nil && fi.Mode().IsRegular()
}

// mtime returns the modification time of the file p.
func mtime(p string) time.Time {
	fi, err := os.Stat(p)
	if err != nil {
		return time.Time{}
	}
	return fi.ModTime()
}

// isabs reports whether p is an absolute path.
func isabs(p string) bool {
	return filepath.IsAbs(p)
}

// readfile returns the content of the named file.
func readfile(file string) string {
	data, err := ioutil.ReadFile(file)
	if err != nil {
		fatal("%v", err)
	}
	return string(data)
}

// writefile writes b to the named file, creating it if needed.  if
// exec is non-zero, marks the file as executable.
func writefile(b, file string, exec int) {
	mode := os.FileMode(0666)
	if exec != 0 {
		mode = 0777
	}
	err := ioutil.WriteFile(file, []byte(b), mode)
	if err != nil {
		fatal("%v", err)
	}
}

// xmkdir creates the directory p.
func xmkdir(p string) {
	err := os.Mkdir(p, 0777)
	if err != nil {
		fatal("%v", err)
	}
}

// xmkdirall creates the directory p and its parents, as needed.
func xmkdirall(p string) {
	err := os.MkdirAll(p, 0777)
	if err != nil {
		fatal("%v", err)
	}
}

// xremove removes the file p.
func xremove(p string) {
	if vflag > 2 {
		errprintf("rm %s\n", p)
	}
	os.Remove(p)
}

// xremoveall removes the file or directory tree rooted at p.
func xremoveall(p string) {
	if vflag > 2 {
		errprintf("rm -r %s\n", p)
	}
	os.RemoveAll(p)
}

// xreaddir replaces dst with a list of the names of the files in dir.
// The names are relative to dir; they are not full paths.
func xreaddir(dir string) []string {
	f, err := os.Open(dir)
	if err != nil {
		fatal("%v", err)
	}
	defer f.Close()
	names, err := f.Readdirnames(-1)
	if err != nil {
		fatal("reading %s: %v", dir, err)
	}
	return names
}

// xworkdir creates a new temporary directory to hold object files
// and returns the name of that directory.
func xworkdir() string {
	name, err := ioutil.TempDir("", "go-tool-dist-")
	if err != nil {
		fatal("%v", err)
	}
	return name
}

// fatal prints an error message to standard error and exits.
func fatal(format string, args ...interface{}) {
	fmt.Fprintf(os.Stderr, "go tool dist: %s\n", fmt.Sprintf(format, args...))
	bgwait()
	xexit(2)
}

var atexits []func()

// xexit exits the process with return code n.
func xexit(n int) {
	for i := len(atexits) - 1; i >= 0; i-- {
		atexits[i]()
	}
	os.Exit(n)
}

// xatexit schedules the exit-handler f to be run when the program exits.
func xatexit(f func()) {
	atexits = append(atexits, f)
}

// xprintf prints a message to standard output.
func xprintf(format string, args ...interface{}) {
	fmt.Printf(format, args...)
}

// errprintf prints a message to standard output.
func errprintf(format string, args ...interface{}) {
	fmt.Fprintf(os.Stderr, format, args...)
}

// main takes care of OS-specific startup and dispatches to xmain.
func main() {
	os.Setenv("TERM", "dumb") // disable escape codes in clang errors

	slash = string(filepath.Separator)

	gohostos = runtime.GOOS
	switch gohostos {
	case "darwin":
		// Even on 64-bit platform, darwin uname -m prints i386.
		if strings.Contains(run("", CheckExit, "sysctl", "machdep.cpu.extfeatures"), "EM64T") {
			gohostarch = "amd64"
		}
	case "solaris":
		// Even on 64-bit platform, solaris uname -m prints i86pc.
		out := run("", CheckExit, "isainfo", "-n")
		if strings.Contains(out, "amd64") {
			gohostarch = "amd64"
		}
		if strings.Contains(out, "i386") {
			gohostarch = "386"
		}
	case "plan9":
		gohostarch = os.Getenv("objtype")
		if gohostarch == "" {
			fatal("$objtype is unset")
		}
	}

	sysinit()

	if gohostarch == "" {
		// Default Unix system.
		out := run("", CheckExit, "uname", "-m")
		switch {
		case strings.Contains(out, "x86_64"), strings.Contains(out, "amd64"):
			gohostarch = "amd64"
		case strings.Contains(out, "86"):
			gohostarch = "386"
		case strings.Contains(out, "arm"):
			gohostarch = "arm"
		case strings.Contains(out, "ppc64le"):
			gohostarch = "ppc64le"
		case strings.Contains(out, "ppc64"):
			gohostarch = "ppc64"
		default:
			fatal("unknown architecture: %s", out)
		}
	}

	if gohostarch == "arm" {
		maxbg = 1
	}
	bginit()

	// The OS X 10.6 linker does not support external linking mode.
	// See golang.org/issue/5130.
	//
	// OS X 10.6 does not work with clang either, but OS X 10.9 requires it.
	// It seems to work with OS X 10.8, so we default to clang for 10.8 and later.
	// See golang.org/issue/5822.
	//
	// Roughly, OS X 10.N shows up as uname release (N+4),
	// so OS X 10.6 is uname version 10 and OS X 10.8 is uname version 12.
	if gohostos == "darwin" {
		rel := run("", CheckExit, "uname", "-r")
		if i := strings.Index(rel, "."); i >= 0 {
			rel = rel[:i]
		}
		osx, _ := strconv.Atoi(rel)
		if osx <= 6+4 {
			goextlinkenabled = "0"
		}
		if osx >= 8+4 {
			defaultclang = true
		}
	}

	xinit()
	xmain()
	xexit(0)
}

// xsamefile reports whether f1 and f2 are the same file (or dir)
func xsamefile(f1, f2 string) bool {
	fi1, err1 := os.Stat(f1)
	fi2, err2 := os.Stat(f2)
	if err1 != nil || err2 != nil {
		return f1 == f2
	}
	return os.SameFile(fi1, fi2)
}

func cpuid(info *[4]uint32, ax uint32)

func cansse2() bool {
	if gohostarch != "386" && gohostarch != "amd64" {
		return false
	}

	var info [4]uint32
	cpuid(&info, 1)
	return info[3]&(1<<26) != 0 // SSE2
}

func xgetgoarm() string {
	if goos == "nacl" {
		// NaCl guarantees VFPv3 and is always cross-compiled.
		return "7"
	}
	if gohostarch != "arm" || goos != gohostos {
		// Conservative default for cross-compilation.
		return "5"
	}
	if goos == "freebsd" {
		// FreeBSD has broken VFP support.
		return "5"
	}
	if xtryexecfunc(useVFPv3) {
		return "7"
	}
	if xtryexecfunc(useVFPv1) {
		return "6"
	}
	return "5"
}

func xtryexecfunc(f func()) bool {
	// TODO(rsc): Implement.
	// The C cmd/dist used this to test whether certain assembly
	// sequences could be executed properly. It used signals and
	// timers and sigsetjmp, which is basically not possible in Go.
	// We probably have to invoke ourselves as a subprocess instead,
	// to contain the fault/timeout.
	return false
}

func useVFPv1()
func useVFPv3()
<commit_msg>cmd/dist: fix deadlock when compilation command fails<commit_after>// Copyright 2012 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 (
	"fmt"
	"io/ioutil"
	"os"
	"os/exec"
	"path/filepath"
	"runtime"
	"sort"
	"strconv"
	"strings"
	"sync"
	"sync/atomic"
	"time"
)

// pathf is fmt.Sprintf for generating paths
// (on windows it turns / into \ after the printf).
func pathf(format string, args ...interface{}) string {
	return filepath.Clean(fmt.Sprintf(format, args...))
}

// filter returns a slice containing the elements x from list for which f(x) == true.
func filter(list []string, f func(string) bool) []string {
	var out []string
	for _, x := range list {
		if f(x) {
			out = append(out, x)
		}
	}
	return out
}

// uniq returns a sorted slice containing the unique elements of list.
func uniq(list []string) []string {
	out := make([]string, len(list))
	copy(out, list)
	sort.Strings(out)
	keep := out[:0]
	for _, x := range out {
		if len(keep) == 0 || keep[len(keep)-1] != x {
			keep = append(keep, x)
		}
	}
	return keep
}

// splitlines returns a slice with the result of splitting
// the input p after each \n.
func splitlines(p string) []string {
	return strings.SplitAfter(p, "\n")
}

// splitfields replaces the vector v with the result of splitting
// the input p into non-empty fields containing no spaces.
func splitfields(p string) []string {
	return strings.Fields(p)
}

const (
	CheckExit = 1 << iota
	ShowOutput
	Background
)

var outputLock sync.Mutex

// run runs the command line cmd in dir.
// If mode has ShowOutput set, run collects cmd's output and returns it as a string;
// otherwise, run prints cmd's output to standard output after the command finishes.
// If mode has CheckExit set and the command fails, run calls fatal.
// If mode has Background set, this command is being run as a
// Background job. Only bgrun should use the Background mode,
// not other callers.
func run(dir string, mode int, cmd ...string) string {
	if vflag > 1 {
		errprintf("run: %s\n", strings.Join(cmd, " "))
	}

	xcmd := exec.Command(cmd[0], cmd[1:]...)
	xcmd.Dir = dir
	var err error
	data, err := xcmd.CombinedOutput()
	if err != nil && mode&CheckExit != 0 {
		outputLock.Lock()
		if len(data) > 0 {
			xprintf("%s\n", data)
		}
		outputLock.Unlock()
		if mode&Background != 0 {
			bgdied.Done()
		}
		fatal("FAILED: %v", strings.Join(cmd, " "))
	}
	if mode&ShowOutput != 0 {
		outputLock.Lock()
		os.Stdout.Write(data)
		outputLock.Unlock()
	}
	if vflag > 2 {
		errprintf("run: %s DONE\n", strings.Join(cmd, " "))
	}
	return string(data)
}

var maxbg = 4 /* maximum number of jobs to run at once */

var (
	bgwork = make(chan func(), 1e5)
	bgdone = make(chan struct{}, 1e5)

	bgdied sync.WaitGroup
	nwork  int32
	ndone  int32

	dying  = make(chan bool)
	nfatal int32
)

func bginit() {
	bgdied.Add(maxbg)
	for i := 0; i < maxbg; i++ {
		go bghelper()
	}
}

func bghelper() {
	for {
		w := <-bgwork
		w()

		// Stop if we're dying.
		if atomic.LoadInt32(&nfatal) > 0 {
			bgdied.Done()
			return
		}
	}
}

// bgrun is like run but runs the command in the background.
// CheckExit|ShowOutput mode is implied (since output cannot be returned).
func bgrun(dir string, cmd ...string) {
	bgwork <- func() {
		run(dir, CheckExit|ShowOutput|Background, cmd...)
	}
}

// bgwait waits for pending bgruns to finish.
// bgwait must be called from only a single goroutine at a time.
func bgwait() {
	var wg sync.WaitGroup
	wg.Add(maxbg)
	done := make(chan bool)
	for i := 0; i < maxbg; i++ {
		bgwork <- func() {
			wg.Done()

			// Hold up bg goroutine until either the wait finishes
			// or the program starts dying due to a call to fatal.
			select {
			case <-dying:
			case <-done:
			}
		}
	}
	wg.Wait()
	close(done)
}

// xgetwd returns the current directory.
func xgetwd() string {
	wd, err := os.Getwd()
	if err != nil {
		fatal("%s", err)
	}
	return wd
}

// xrealwd returns the 'real' name for the given path.
// real is defined as what xgetwd returns in that directory.
func xrealwd(path string) string {
	old := xgetwd()
	if err := os.Chdir(path); err != nil {
		fatal("chdir %s: %v", path, err)
	}
	real := xgetwd()
	if err := os.Chdir(old); err != nil {
		fatal("chdir %s: %v", old, err)
	}
	return real
}

// isdir reports whether p names an existing directory.
func isdir(p string) bool {
	fi, err := os.Stat(p)
	return err == nil && fi.IsDir()
}

// isfile reports whether p names an existing file.
func isfile(p string) bool {
	fi, err := os.Stat(p)
	return err == nil && fi.Mode().IsRegular()
}

// mtime returns the modification time of the file p.
func mtime(p string) time.Time {
	fi, err := os.Stat(p)
	if err != nil {
		return time.Time{}
	}
	return fi.ModTime()
}

// isabs reports whether p is an absolute path.
func isabs(p string) bool {
	return filepath.IsAbs(p)
}

// readfile returns the content of the named file.
func readfile(file string) string {
	data, err := ioutil.ReadFile(file)
	if err != nil {
		fatal("%v", err)
	}
	return string(data)
}

// writefile writes b to the named file, creating it if needed.  if
// exec is non-zero, marks the file as executable.
func writefile(b, file string, exec int) {
	mode := os.FileMode(0666)
	if exec != 0 {
		mode = 0777
	}
	err := ioutil.WriteFile(file, []byte(b), mode)
	if err != nil {
		fatal("%v", err)
	}
}

// xmkdir creates the directory p.
func xmkdir(p string) {
	err := os.Mkdir(p, 0777)
	if err != nil {
		fatal("%v", err)
	}
}

// xmkdirall creates the directory p and its parents, as needed.
func xmkdirall(p string) {
	err := os.MkdirAll(p, 0777)
	if err != nil {
		fatal("%v", err)
	}
}

// xremove removes the file p.
func xremove(p string) {
	if vflag > 2 {
		errprintf("rm %s\n", p)
	}
	os.Remove(p)
}

// xremoveall removes the file or directory tree rooted at p.
func xremoveall(p string) {
	if vflag > 2 {
		errprintf("rm -r %s\n", p)
	}
	os.RemoveAll(p)
}

// xreaddir replaces dst with a list of the names of the files in dir.
// The names are relative to dir; they are not full paths.
func xreaddir(dir string) []string {
	f, err := os.Open(dir)
	if err != nil {
		fatal("%v", err)
	}
	defer f.Close()
	names, err := f.Readdirnames(-1)
	if err != nil {
		fatal("reading %s: %v", dir, err)
	}
	return names
}

// xworkdir creates a new temporary directory to hold object files
// and returns the name of that directory.
func xworkdir() string {
	name, err := ioutil.TempDir("", "go-tool-dist-")
	if err != nil {
		fatal("%v", err)
	}
	return name
}

// fatal prints an error message to standard error and exits.
func fatal(format string, args ...interface{}) {
	fmt.Fprintf(os.Stderr, "go tool dist: %s\n", fmt.Sprintf(format, args...))

	// Wait for background goroutines to finish,
	// so that exit handler that removes the work directory
	// is not fighting with active writes or open files.
	if atomic.AddInt32(&nfatal, 1) == 1 {
		close(dying)
	}
	for i := 0; i < maxbg; i++ {
		bgwork <- func() {} // wake up workers so they notice nfatal > 0
	}
	bgdied.Wait()

	xexit(2)
}

var atexits []func()

// xexit exits the process with return code n.
func xexit(n int) {
	for i := len(atexits) - 1; i >= 0; i-- {
		atexits[i]()
	}
	os.Exit(n)
}

// xatexit schedules the exit-handler f to be run when the program exits.
func xatexit(f func()) {
	atexits = append(atexits, f)
}

// xprintf prints a message to standard output.
func xprintf(format string, args ...interface{}) {
	fmt.Printf(format, args...)
}

// errprintf prints a message to standard output.
func errprintf(format string, args ...interface{}) {
	fmt.Fprintf(os.Stderr, format, args...)
}

// main takes care of OS-specific startup and dispatches to xmain.
func main() {
	os.Setenv("TERM", "dumb") // disable escape codes in clang errors

	slash = string(filepath.Separator)

	gohostos = runtime.GOOS
	switch gohostos {
	case "darwin":
		// Even on 64-bit platform, darwin uname -m prints i386.
		if strings.Contains(run("", CheckExit, "sysctl", "machdep.cpu.extfeatures"), "EM64T") {
			gohostarch = "amd64"
		}
	case "solaris":
		// Even on 64-bit platform, solaris uname -m prints i86pc.
		out := run("", CheckExit, "isainfo", "-n")
		if strings.Contains(out, "amd64") {
			gohostarch = "amd64"
		}
		if strings.Contains(out, "i386") {
			gohostarch = "386"
		}
	case "plan9":
		gohostarch = os.Getenv("objtype")
		if gohostarch == "" {
			fatal("$objtype is unset")
		}
	}

	sysinit()

	if gohostarch == "" {
		// Default Unix system.
		out := run("", CheckExit, "uname", "-m")
		switch {
		case strings.Contains(out, "x86_64"), strings.Contains(out, "amd64"):
			gohostarch = "amd64"
		case strings.Contains(out, "86"):
			gohostarch = "386"
		case strings.Contains(out, "arm"):
			gohostarch = "arm"
		case strings.Contains(out, "ppc64le"):
			gohostarch = "ppc64le"
		case strings.Contains(out, "ppc64"):
			gohostarch = "ppc64"
		default:
			fatal("unknown architecture: %s", out)
		}
	}

	if gohostarch == "arm" {
		maxbg = 1
	}
	bginit()

	// The OS X 10.6 linker does not support external linking mode.
	// See golang.org/issue/5130.
	//
	// OS X 10.6 does not work with clang either, but OS X 10.9 requires it.
	// It seems to work with OS X 10.8, so we default to clang for 10.8 and later.
	// See golang.org/issue/5822.
	//
	// Roughly, OS X 10.N shows up as uname release (N+4),
	// so OS X 10.6 is uname version 10 and OS X 10.8 is uname version 12.
	if gohostos == "darwin" {
		rel := run("", CheckExit, "uname", "-r")
		if i := strings.Index(rel, "."); i >= 0 {
			rel = rel[:i]
		}
		osx, _ := strconv.Atoi(rel)
		if osx <= 6+4 {
			goextlinkenabled = "0"
		}
		if osx >= 8+4 {
			defaultclang = true
		}
	}

	xinit()
	xmain()
	xexit(0)
}

// xsamefile reports whether f1 and f2 are the same file (or dir)
func xsamefile(f1, f2 string) bool {
	fi1, err1 := os.Stat(f1)
	fi2, err2 := os.Stat(f2)
	if err1 != nil || err2 != nil {
		return f1 == f2
	}
	return os.SameFile(fi1, fi2)
}

func cpuid(info *[4]uint32, ax uint32)

func cansse2() bool {
	if gohostarch != "386" && gohostarch != "amd64" {
		return false
	}

	var info [4]uint32
	cpuid(&info, 1)
	return info[3]&(1<<26) != 0 // SSE2
}

func xgetgoarm() string {
	if goos == "nacl" {
		// NaCl guarantees VFPv3 and is always cross-compiled.
		return "7"
	}
	if gohostarch != "arm" || goos != gohostos {
		// Conservative default for cross-compilation.
		return "5"
	}
	if goos == "freebsd" {
		// FreeBSD has broken VFP support.
		return "5"
	}
	if xtryexecfunc(useVFPv3) {
		return "7"
	}
	if xtryexecfunc(useVFPv1) {
		return "6"
	}
	return "5"
}

func xtryexecfunc(f func()) bool {
	// TODO(rsc): Implement.
	// The C cmd/dist used this to test whether certain assembly
	// sequences could be executed properly. It used signals and
	// timers and sigsetjmp, which is basically not possible in Go.
	// We probably have to invoke ourselves as a subprocess instead,
	// to contain the fault/timeout.
	return false
}

func useVFPv1()
func useVFPv3()
<|endoftext|>


<commit_before>package cloudflare

import (
	"context"
	"encoding/json"
	"fmt"
	"net/http"
	"net/url"
	"strconv"

	"github.com/pkg/errors"
)

// ZoneLockdown represents a Zone Lockdown rule. A rule only permits access to
// the provided URL pattern(s) from the given IP address(es) or subnet(s).
type ZoneLockdown struct {
	ID             string               `json:"id"`
	Description    string               `json:"description"`
	URLs           []string             `json:"urls"`
	Configurations []ZoneLockdownConfig `json:"configurations"`
	Paused         bool                 `json:"paused"`
	Priority       int                  `json:"priority,omitempty"`
}

// ZoneLockdownConfig represents a Zone Lockdown config, which comprises
// a Target ("ip" or "ip_range") and a Value (an IP address or IP+mask,
// respectively.)
type ZoneLockdownConfig struct {
	Target string `json:"target"`
	Value  string `json:"value"`
}

// ZoneLockdownResponse represents a response from the Zone Lockdown endpoint.
type ZoneLockdownResponse struct {
	Result ZoneLockdown `json:"result"`
	Response
	ResultInfo `json:"result_info"`
}

// ZoneLockdownListResponse represents a response from the List Zone Lockdown
// endpoint.
type ZoneLockdownListResponse struct {
	Result []ZoneLockdown `json:"result"`
	Response
	ResultInfo `json:"result_info"`
}

// CreateZoneLockdown creates a Zone ZoneLockdown rule for the given zone ID.
//
// API reference: https://api.cloudflare.com/#zone-ZoneLockdown-create-a-ZoneLockdown-rule
func (api *API) CreateZoneLockdown(ctx context.Context, zoneID string, ld ZoneLockdown) (*ZoneLockdownResponse, error) {
	uri := fmt.Sprintf("/zones/%s/firewall/lockdowns", zoneID)
	res, err := api.makeRequestContext(ctx, http.MethodPost, uri, ld)
	if err != nil {
		return nil, err
	}

	response := &ZoneLockdownResponse{}
	err = json.Unmarshal(res, &response)
	if err != nil {
		return nil, errors.Wrap(err, errUnmarshalError)
	}

	return response, nil
}

// UpdateZoneLockdown updates a Zone ZoneLockdown rule (based on the ID) for the
// given zone ID.
//
// API reference: https://api.cloudflare.com/#zone-ZoneLockdown-update-ZoneLockdown-rule
func (api *API) UpdateZoneLockdown(ctx context.Context, zoneID string, id string, ld ZoneLockdown) (*ZoneLockdownResponse, error) {
	uri := fmt.Sprintf("/zones/%s/firewall/lockdowns/%s", zoneID, id)
	res, err := api.makeRequestContext(ctx, http.MethodPut, uri, ld)
	if err != nil {
		return nil, err
	}

	response := &ZoneLockdownResponse{}
	err = json.Unmarshal(res, &response)
	if err != nil {
		return nil, errors.Wrap(err, errUnmarshalError)
	}

	return response, nil
}

// DeleteZoneLockdown deletes a Zone ZoneLockdown rule (based on the ID) for the
// given zone ID.
//
// API reference: https://api.cloudflare.com/#zone-ZoneLockdown-delete-ZoneLockdown-rule
func (api *API) DeleteZoneLockdown(ctx context.Context, zoneID string, id string) (*ZoneLockdownResponse, error) {
	uri := fmt.Sprintf("/zones/%s/firewall/lockdowns/%s", zoneID, id)
	res, err := api.makeRequestContext(ctx, http.MethodDelete, uri, nil)
	if err != nil {
		return nil, err
	}

	response := &ZoneLockdownResponse{}
	err = json.Unmarshal(res, &response)
	if err != nil {
		return nil, errors.Wrap(err, errUnmarshalError)
	}

	return response, nil
}

// ZoneLockdown retrieves a Zone ZoneLockdown rule (based on the ID) for the
// given zone ID.
//
// API reference: https://api.cloudflare.com/#zone-ZoneLockdown-ZoneLockdown-rule-details
func (api *API) ZoneLockdown(ctx context.Context, zoneID string, id string) (*ZoneLockdownResponse, error) {
	uri := fmt.Sprintf("/zones/%s/firewall/lockdowns/%s", zoneID, id)
	res, err := api.makeRequestContext(ctx, http.MethodGet, uri, nil)
	if err != nil {
		return nil, err
	}

	response := &ZoneLockdownResponse{}
	err = json.Unmarshal(res, &response)
	if err != nil {
		return nil, errors.Wrap(err, errUnmarshalError)
	}

	return response, nil
}

// ListZoneLockdowns retrieves a list of Zone ZoneLockdown rules for a given
// zone ID by page number.
//
// API reference: https://api.cloudflare.com/#zone-ZoneLockdown-list-ZoneLockdown-rules
func (api *API) ListZoneLockdowns(ctx context.Context, zoneID string, page int) (*ZoneLockdownListResponse, error) {
	v := url.Values{}
	if page <= 0 {
		page = 1
	}

	v.Set("page", strconv.Itoa(page))
	v.Set("per_page", strconv.Itoa(100))

	uri := fmt.Sprintf("/zones/%s/firewall/lockdowns?%s", zoneID, v.Encode())
	res, err := api.makeRequestContext(ctx, http.MethodGet, uri, nil)
	if err != nil {
		return nil, err
	}

	response := &ZoneLockdownListResponse{}
	err = json.Unmarshal(res, &response)
	if err != nil {
		return nil, errors.Wrap(err, errUnmarshalError)
	}

	return response, nil
}
<commit_msg>Add `CreatedOn` and `ModifiedOn` to the ZoneLockdown object definition.<commit_after>package cloudflare

import (
	"context"
	"encoding/json"
	"fmt"
	"net/http"
	"net/url"
	"strconv"
	"time"

	"github.com/pkg/errors"
)

// ZoneLockdown represents a Zone Lockdown rule. A rule only permits access to
// the provided URL pattern(s) from the given IP address(es) or subnet(s).
type ZoneLockdown struct {
	ID             string               `json:"id"`
	Description    string               `json:"description"`
	URLs           []string             `json:"urls"`
	Configurations []ZoneLockdownConfig `json:"configurations"`
	Paused         bool                 `json:"paused"`
	Priority       int                  `json:"priority,omitempty"`
	CreatedOn      *time.Time           `json:"created_on,omitempty"`
	ModifiedOn     *time.Time           `json:"modified_on,omitempty"`
}

// ZoneLockdownConfig represents a Zone Lockdown config, which comprises
// a Target ("ip" or "ip_range") and a Value (an IP address or IP+mask,
// respectively.)
type ZoneLockdownConfig struct {
	Target string `json:"target"`
	Value  string `json:"value"`
}

// ZoneLockdownResponse represents a response from the Zone Lockdown endpoint.
type ZoneLockdownResponse struct {
	Result ZoneLockdown `json:"result"`
	Response
	ResultInfo `json:"result_info"`
}

// ZoneLockdownListResponse represents a response from the List Zone Lockdown
// endpoint.
type ZoneLockdownListResponse struct {
	Result []ZoneLockdown `json:"result"`
	Response
	ResultInfo `json:"result_info"`
}

// CreateZoneLockdown creates a Zone ZoneLockdown rule for the given zone ID.
//
// API reference: https://api.cloudflare.com/#zone-ZoneLockdown-create-a-ZoneLockdown-rule
func (api *API) CreateZoneLockdown(ctx context.Context, zoneID string, ld ZoneLockdown) (*ZoneLockdownResponse, error) {
	uri := fmt.Sprintf("/zones/%s/firewall/lockdowns", zoneID)
	res, err := api.makeRequestContext(ctx, http.MethodPost, uri, ld)
	if err != nil {
		return nil, err
	}

	response := &ZoneLockdownResponse{}
	err = json.Unmarshal(res, &response)
	if err != nil {
		return nil, errors.Wrap(err, errUnmarshalError)
	}

	return response, nil
}

// UpdateZoneLockdown updates a Zone ZoneLockdown rule (based on the ID) for the
// given zone ID.
//
// API reference: https://api.cloudflare.com/#zone-ZoneLockdown-update-ZoneLockdown-rule
func (api *API) UpdateZoneLockdown(ctx context.Context, zoneID string, id string, ld ZoneLockdown) (*ZoneLockdownResponse, error) {
	uri := fmt.Sprintf("/zones/%s/firewall/lockdowns/%s", zoneID, id)
	res, err := api.makeRequestContext(ctx, http.MethodPut, uri, ld)
	if err != nil {
		return nil, err
	}

	response := &ZoneLockdownResponse{}
	err = json.Unmarshal(res, &response)
	if err != nil {
		return nil, errors.Wrap(err, errUnmarshalError)
	}

	return response, nil
}

// DeleteZoneLockdown deletes a Zone ZoneLockdown rule (based on the ID) for the
// given zone ID.
//
// API reference: https://api.cloudflare.com/#zone-ZoneLockdown-delete-ZoneLockdown-rule
func (api *API) DeleteZoneLockdown(ctx context.Context, zoneID string, id string) (*ZoneLockdownResponse, error) {
	uri := fmt.Sprintf("/zones/%s/firewall/lockdowns/%s", zoneID, id)
	res, err := api.makeRequestContext(ctx, http.MethodDelete, uri, nil)
	if err != nil {
		return nil, err
	}

	response := &ZoneLockdownResponse{}
	err = json.Unmarshal(res, &response)
	if err != nil {
		return nil, errors.Wrap(err, errUnmarshalError)
	}

	return response, nil
}

// ZoneLockdown retrieves a Zone ZoneLockdown rule (based on the ID) for the
// given zone ID.
//
// API reference: https://api.cloudflare.com/#zone-ZoneLockdown-ZoneLockdown-rule-details
func (api *API) ZoneLockdown(ctx context.Context, zoneID string, id string) (*ZoneLockdownResponse, error) {
	uri := fmt.Sprintf("/zones/%s/firewall/lockdowns/%s", zoneID, id)
	res, err := api.makeRequestContext(ctx, http.MethodGet, uri, nil)
	if err != nil {
		return nil, err
	}

	response := &ZoneLockdownResponse{}
	err = json.Unmarshal(res, &response)
	if err != nil {
		return nil, errors.Wrap(err, errUnmarshalError)
	}

	return response, nil
}

// ListZoneLockdowns retrieves a list of Zone ZoneLockdown rules for a given
// zone ID by page number.
//
// API reference: https://api.cloudflare.com/#zone-ZoneLockdown-list-ZoneLockdown-rules
func (api *API) ListZoneLockdowns(ctx context.Context, zoneID string, page int) (*ZoneLockdownListResponse, error) {
	v := url.Values{}
	if page <= 0 {
		page = 1
	}

	v.Set("page", strconv.Itoa(page))
	v.Set("per_page", strconv.Itoa(100))

	uri := fmt.Sprintf("/zones/%s/firewall/lockdowns?%s", zoneID, v.Encode())
	res, err := api.makeRequestContext(ctx, http.MethodGet, uri, nil)
	if err != nil {
		return nil, err
	}

	response := &ZoneLockdownListResponse{}
	err = json.Unmarshal(res, &response)
	if err != nil {
		return nil, errors.Wrap(err, errUnmarshalError)
	}

	return response, nil
}
<|endoftext|>


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

package git

// #cgo pkg-config: libgit2
// #include <git2.h>
import "C"

import (
	"unsafe"
)

// Commit represents a git commit.
type Commit struct {
	commit *C.struct_git_commit
}

// Free is used to deallocate a commit object.
func (c *Commit) Free() {
	C.git_commit_free(c.commit)
}

// Id returns the hash of the commit.
func (c *Commit) Id() string {
	oid := C.git_commit_id(c.commit)
	defer C.free(unsafe.Pointer(oid))
	id := C.git_oid_allocfmt(oid)
	defer C.free(unsafe.Pointer(id))
	return C.GoString(id)
}

// Repository is the basic type of the git package, it represents a git
// repository.
type Repository struct {
	repository *C.struct_git_repository
}

// InitRepository inits a new repository.
//
// If the path does not exist, it will be created.
//
// Returns an instance of Repository, or an error in case of failure.
func InitRepository(path string, bare bool) (*Repository, error) {
	var cbare C.unsigned = 0
	if bare {
		cbare = 1
	}
	repo := &Repository{}
	cpath := C.CString(path)
	defer C.free(unsafe.Pointer(cpath))
	if C.git_repository_init(&repo.repository, cpath, cbare) != C.GIT_OK {
		return nil, lastErr()
	}
	return repo, nil
}

// OpenRepository opens a repository by its path.
//
// Returns an error in case of failure (e.g.: if the path does not exist; if
// it exists but is not a git repository or if the path exists, is a git
// repository, but the user does not have access to it).
func OpenRepository(path string) (*Repository, error) {
	repo := &Repository{}
	cpath := C.CString(path)
	defer C.free(unsafe.Pointer(cpath))
	if C.git_repository_open(&repo.repository, cpath) != C.GIT_OK {
		return nil, lastErr()
	}
	return repo, nil
}

// Config returns a Config instance, representing the configuration of the
// repository.
func (r *Repository) Config() (*Config, error) {
	conf := &Config{}
	if C.git_repository_config(&conf.config, r.repository) != C.GIT_OK {
		return nil, lastErr()
	}
	return conf, nil
}

// Free is used to deallocate the repository. It should be called to finish the
// repository. It's a good practice to use it with the defer statement:
//
//     repo, err := git.GetRepository("/path/to/repository")
//     // check error
//     defer repo.Free()
//     // use repo
func (r *Repository) Free() {
	C.git_repository_free(r.repository)
}

// Head returns the commit at the head of the repository.
func (r *Repository) Head() (*Commit, error) {
	var reference *C.struct_git_reference
	if C.git_repository_head(&reference, r.repository) != C.GIT_OK {
		return nil, lastErr()
	}
	defer C.git_reference_free(reference)
	c := new(Commit)
	if C.git_commit_lookup(&c.commit, r.repository, C.git_reference_oid(reference)) != C.GIT_OK {
		return nil, lastErr()
	}
	return c, nil
}

// Config represents the configuration of a git repository.
//
// You can use it retrieve or define configurations of the repository.
type Config struct {
	config *C.struct_git_config
}

// Free is used to deallocate the Config instance. It should be called to
// finish the instance. You can use it with the defer statement:
//
//     // get repository instance
//     config, err := repo.Config()
//     // check error
//     defer config.Free()
func (c *Config) Free() {
	C.git_config_free(c.config)
}

// GetBool is used to get boolean config values.
//
// The dot notation is used for configuration parameters. Example:
//
//     v, err := config.GetBool("core.ignorecase")
//     // check errors and use v
func (c *Config) GetBool(name string) (bool, error) {
	var v C.int
	cname := C.CString(name)
	defer C.free(unsafe.Pointer(cname))
	if C.git_config_get_bool(&v, c.config, cname) != C.GIT_OK {
		return false, lastErr()
	}
	return v == 1, nil
}

// SetBool is used to add a boolean setting to the configuration file.
//
// The format of the configuration parameter is the same as in GetBool. If the
// configuration parameter is not declared in the config file, it will be
// created. Example of use:
//
//     err := config.SetBool("core.ignorecase", true)
//     if err != nil {
//         panic(err)
//     }
func (c *Config) SetBool(name string, value bool) error {
	var v C.int = 0
	if value {
		v = 1
	}
	cname := C.CString(name)
	defer C.free(unsafe.Pointer(cname))
	if C.git_config_set_bool(c.config, cname, v) != C.GIT_OK {
		return lastErr()
	}
	return nil
}

// GetString is used to get string config values.
//
// The format of the configuration parameter is the same as in GetBool.
func (c *Config) GetString(name string) (string, error) {
	var v *C.char
	cname := C.CString(name)
	defer C.free(unsafe.Pointer(cname))
	if C.git_config_get_string(&v, c.config, cname) != C.GIT_OK {
		return "", lastErr()
	}
	return C.GoString(v), nil
}

// SetString is used to add a string setting to the config file.
//
// The format of the configuration parameter is the same as in GetBool. If the
// parameter is not declared in the config file, it will be created.
func (c *Config) SetString(name, value string) error {
	cname := C.CString(name)
	defer C.free(unsafe.Pointer(cname))
	cvalue := C.CString(value)
	defer C.free(unsafe.Pointer(cvalue))
	if C.git_config_set_string(c.config, cname, cvalue) != C.GIT_OK {
		return lastErr()
	}
	return nil
}

// GetInt64 is used to get int64 config values.
//
// The format of the configuration parameter is the same as in GetBool.
func (c *Config) GetInt64(name string) (int64, error) {
	var v C.int64_t
	cname := C.CString(name)
	defer C.free(unsafe.Pointer(cname))
	if C.git_config_get_int64(&v, c.config, cname) != C.GIT_OK {
		return 0, lastErr()
	}
	return int64(v), nil
}

// SetInt64 is used to add a int64 setting to the config file.
//
// The format of the configuration parameter is the same as in GetBool. If the
// parameter is not declared in the config file, it will be created.
func (c *Config) SetInt64(name string, value int64) error {
	cname := C.CString(name)
	defer C.free(unsafe.Pointer(cname))
	if C.git_config_set_int64(c.config, cname, C.int64_t(value)) != C.GIT_OK {
		return lastErr()
	}
	return nil
}

// GitError is the type used for errors in this package.
type GitError string

func (err GitError) Error() string {
	return string(err)
}

func lastErr() GitError {
	err := C.giterr_last()
	defer C.free(unsafe.Pointer(err))
	return GitError(C.GoString(err.message))
}
<commit_msg>git: make pointer allocation uniform<commit_after>// Copyright 2012 Francisco Souza. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package git

// #cgo pkg-config: libgit2
// #include <git2.h>
import "C"

import (
	"unsafe"
)

// Commit represents a git commit.
type Commit struct {
	commit *C.struct_git_commit
}

// Free is used to deallocate a commit object.
func (c *Commit) Free() {
	C.git_commit_free(c.commit)
}

// Id returns the hash of the commit.
func (c *Commit) Id() string {
	oid := C.git_commit_id(c.commit)
	defer C.free(unsafe.Pointer(oid))
	id := C.git_oid_allocfmt(oid)
	defer C.free(unsafe.Pointer(id))
	return C.GoString(id)
}

// Repository is the basic type of the git package, it represents a git
// repository.
type Repository struct {
	repository *C.struct_git_repository
}

// InitRepository inits a new repository.
//
// If the path does not exist, it will be created.
//
// Returns an instance of Repository, or an error in case of failure.
func InitRepository(path string, bare bool) (*Repository, error) {
	var cbare C.unsigned = 0
	if bare {
		cbare = 1
	}
	repo := new(Repository)
	cpath := C.CString(path)
	defer C.free(unsafe.Pointer(cpath))
	if C.git_repository_init(&repo.repository, cpath, cbare) != C.GIT_OK {
		return nil, lastErr()
	}
	return repo, nil
}

// OpenRepository opens a repository by its path.
//
// Returns an error in case of failure (e.g.: if the path does not exist; if
// it exists but is not a git repository or if the path exists, is a git
// repository, but the user does not have access to it).
func OpenRepository(path string) (*Repository, error) {
	repo := new(Repository)
	cpath := C.CString(path)
	defer C.free(unsafe.Pointer(cpath))
	if C.git_repository_open(&repo.repository, cpath) != C.GIT_OK {
		return nil, lastErr()
	}
	return repo, nil
}

// Config returns a Config instance, representing the configuration of the
// repository.
func (r *Repository) Config() (*Config, error) {
	conf := new(Config)
	if C.git_repository_config(&conf.config, r.repository) != C.GIT_OK {
		return nil, lastErr()
	}
	return conf, nil
}

// Free is used to deallocate the repository. It should be called to finish the
// repository. It's a good practice to use it with the defer statement:
//
//     repo, err := git.GetRepository("/path/to/repository")
//     // check error
//     defer repo.Free()
//     // use repo
func (r *Repository) Free() {
	C.git_repository_free(r.repository)
}

// Head returns the commit at the head of the repository.
func (r *Repository) Head() (*Commit, error) {
	var reference *C.struct_git_reference
	if C.git_repository_head(&reference, r.repository) != C.GIT_OK {
		return nil, lastErr()
	}
	defer C.git_reference_free(reference)
	c := new(Commit)
	if C.git_commit_lookup(&c.commit, r.repository, C.git_reference_oid(reference)) != C.GIT_OK {
		return nil, lastErr()
	}
	return c, nil
}

// Config represents the configuration of a git repository.
//
// You can use it retrieve or define configurations of the repository.
type Config struct {
	config *C.struct_git_config
}

// Free is used to deallocate the Config instance. It should be called to
// finish the instance. You can use it with the defer statement:
//
//     // get repository instance
//     config, err := repo.Config()
//     // check error
//     defer config.Free()
func (c *Config) Free() {
	C.git_config_free(c.config)
}

// GetBool is used to get boolean config values.
//
// The dot notation is used for configuration parameters. Example:
//
//     v, err := config.GetBool("core.ignorecase")
//     // check errors and use v
func (c *Config) GetBool(name string) (bool, error) {
	var v C.int
	cname := C.CString(name)
	defer C.free(unsafe.Pointer(cname))
	if C.git_config_get_bool(&v, c.config, cname) != C.GIT_OK {
		return false, lastErr()
	}
	return v == 1, nil
}

// SetBool is used to add a boolean setting to the configuration file.
//
// The format of the configuration parameter is the same as in GetBool. If the
// configuration parameter is not declared in the config file, it will be
// created. Example of use:
//
//     err := config.SetBool("core.ignorecase", true)
//     if err != nil {
//         panic(err)
//     }
func (c *Config) SetBool(name string, value bool) error {
	var v C.int = 0
	if value {
		v = 1
	}
	cname := C.CString(name)
	defer C.free(unsafe.Pointer(cname))
	if C.git_config_set_bool(c.config, cname, v) != C.GIT_OK {
		return lastErr()
	}
	return nil
}

// GetString is used to get string config values.
//
// The format of the configuration parameter is the same as in GetBool.
func (c *Config) GetString(name string) (string, error) {
	var v *C.char
	cname := C.CString(name)
	defer C.free(unsafe.Pointer(cname))
	if C.git_config_get_string(&v, c.config, cname) != C.GIT_OK {
		return "", lastErr()
	}
	return C.GoString(v), nil
}

// SetString is used to add a string setting to the config file.
//
// The format of the configuration parameter is the same as in GetBool. If the
// parameter is not declared in the config file, it will be created.
func (c *Config) SetString(name, value string) error {
	cname := C.CString(name)
	defer C.free(unsafe.Pointer(cname))
	cvalue := C.CString(value)
	defer C.free(unsafe.Pointer(cvalue))
	if C.git_config_set_string(c.config, cname, cvalue) != C.GIT_OK {
		return lastErr()
	}
	return nil
}

// GetInt64 is used to get int64 config values.
//
// The format of the configuration parameter is the same as in GetBool.
func (c *Config) GetInt64(name string) (int64, error) {
	var v C.int64_t
	cname := C.CString(name)
	defer C.free(unsafe.Pointer(cname))
	if C.git_config_get_int64(&v, c.config, cname) != C.GIT_OK {
		return 0, lastErr()
	}
	return int64(v), nil
}

// SetInt64 is used to add a int64 setting to the config file.
//
// The format of the configuration parameter is the same as in GetBool. If the
// parameter is not declared in the config file, it will be created.
func (c *Config) SetInt64(name string, value int64) error {
	cname := C.CString(name)
	defer C.free(unsafe.Pointer(cname))
	if C.git_config_set_int64(c.config, cname, C.int64_t(value)) != C.GIT_OK {
		return lastErr()
	}
	return nil
}

// GitError is the type used for errors in this package.
type GitError string

func (err GitError) Error() string {
	return string(err)
}

func lastErr() GitError {
	err := C.giterr_last()
	defer C.free(unsafe.Pointer(err))
	return GitError(C.GoString(err.message))
}
<|endoftext|>


<commit_before>// Copyright 2015 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package persistence

import (
	"fmt"
	"time"

	"github.com/juju/errors"
	charmresource "gopkg.in/juju/charm.v6-unstable/resource"
	"gopkg.in/mgo.v2/bson"
	"gopkg.in/mgo.v2/txn"

	"github.com/juju/juju/resource"
)

const (
	resourcesC = "resources"

	stagedIDSuffix     = "#staged"
	charmstoreIDSuffix = "#charmstore"
)

// resourceID converts an external resource ID into an internal one.
func resourceID(id, subType, subID string) string {
	if subType == "" {
		return fmt.Sprintf("resource#%s", id)
	}
	return fmt.Sprintf("resource#%s#%s-%s", id, subType, subID)
}

func serviceResourceID(id string) string {
	return resourceID(id, "", "")
}

func pendingResourceID(id, pendingID string) string {
	return resourceID(id, "pending", pendingID)
}

func charmStoreResourceID(id string) string {
	return serviceResourceID(id) + charmstoreIDSuffix
}

func unitResourceID(id, unitID string) string {
	return resourceID(id, "unit", unitID)
}

// stagedID converts an external resource ID into an internal staged one.
func stagedID(id string) string {
	return serviceResourceID(id) + stagedIDSuffix
}

// storedResource holds all model-stored information for a resource.
type storedResource struct {
	resource.Resource

	// storagePath is the path to where the resource content is stored.
	storagePath string
}

// charmStoreResource holds the info for a resource as provided by the
// charm store at as specific point in time.
type charmStoreResource struct {
	charmresource.Resource
	id         string
	serviceID  string
	lastPolled time.Time
}

func newStagedResourceOps(stored storedResource) []txn.Op {
	doc := newStagedDoc(stored)

	return []txn.Op{{
		C:      resourcesC,
		Id:     doc.DocID,
		Assert: txn.DocMissing,
		Insert: doc,
	}}
}

func newEnsureStagedSameOps(stored storedResource) []txn.Op {
	doc := newStagedDoc(stored)

	// Other than cause the txn to abort, we don't do anything here.
	return []txn.Op{{
		C:      resourcesC,
		Id:     doc.DocID,
		Assert: doc, // TODO(ericsnow) Is this okay?
	}}
}

func newRemoveStagedOps(id string) []txn.Op {
	fullID := stagedID(id)

	// We don't assert that it exists. We want "missing" to be a noop.
	return []txn.Op{{
		C:      resourcesC,
		Id:     fullID,
		Remove: true,
	}}
}

func newInsertResourceOps(stored storedResource) []txn.Op {
	doc := newResourceDoc(stored)

	return []txn.Op{{
		C:      resourcesC,
		Id:     doc.DocID,
		Assert: txn.DocMissing,
		Insert: doc,
	}}
}

func newUpdateResourceOps(stored storedResource) []txn.Op {
	doc := newResourceDoc(stored)

	// TODO(ericsnow) Using "update" doesn't work right...
	return append([]txn.Op{{
		C:      resourcesC,
		Id:     doc.DocID,
		Assert: txn.DocExists,
		Remove: true,
	}}, newInsertResourceOps(stored)...)
}

func newInsertCharmStoreResourceOps(res charmStoreResource) []txn.Op {
	doc := newCharmStoreResourceDoc(res)

	return []txn.Op{{
		C:      resourcesC,
		Id:     doc.DocID,
		Assert: txn.DocMissing,
		Insert: doc,
	}}
}

func newUpdateCharmStoreResourceOps(res charmStoreResource) []txn.Op {
	doc := newCharmStoreResourceDoc(res)

	// TODO(ericsnow) Using "update" doesn't work right...
	return append([]txn.Op{{
		C:      resourcesC,
		Id:     doc.DocID,
		Assert: txn.DocExists,
		Remove: true,
	}}, newInsertCharmStoreResourceOps(res)...)
}

func newInsertUnitResourceOps(unitID string, stored storedResource) []txn.Op {
	doc := newUnitResourceDoc(unitID, stored)

	return []txn.Op{{
		C:      resourcesC,
		Id:     doc.DocID,
		Assert: txn.DocMissing,
		Insert: doc,
	}}
}

func newUpdateUnitResourceOps(unitID string, stored storedResource) []txn.Op {
	doc := newUnitResourceDoc(unitID, stored)

	// TODO(ericsnow) Using "update" doesn't work right...
	return append([]txn.Op{{
		C:      resourcesC,
		Id:     doc.DocID,
		Assert: txn.DocExists,
		Remove: true,
	}}, newInsertUnitResourceOps(unitID, stored)...)
}

// newResolvePendingResourceOps generates transaction operations that
// will resolve a pending resource doc and make it active.
//
// We trust that the provided resource really is pending
// and that it matches the existing doc with the same ID.
func newResolvePendingResourceOps(pending storedResource, exists bool) []txn.Op {
	oldID := pendingResourceID(pending.ID, pending.PendingID)
	newRes := pending
	newRes.PendingID = ""
	// TODO(ericsnow) Update newRes.StoragePath? Doing so would require
	// moving the resource in the blobstore to the correct path, which
	// we cannot do in the transaction...
	ops := []txn.Op{{
		C:      resourcesC,
		Id:     oldID,
		Assert: txn.DocExists,
		Remove: true,
	}}
	if exists {
		return append(ops, newUpdateResourceOps(newRes)...)
	} else {
		return append(ops, newInsertResourceOps(newRes)...)
	}
}

// newCharmStoreResourceDoc generates a doc that represents the given resource.
func newCharmStoreResourceDoc(res charmStoreResource) *resourceDoc {
	fullID := charmStoreResourceID(res.id)
	return charmStoreResource2Doc(fullID, res)
}

// newUnitResourceDoc generates a doc that represents the given resource.
func newUnitResourceDoc(unitID string, stored storedResource) *resourceDoc {
	fullID := unitResourceID(stored.ID, unitID)
	return unitResource2Doc(fullID, unitID, stored)
}

// newResourceDoc generates a doc that represents the given resource.
func newResourceDoc(stored storedResource) *resourceDoc {
	fullID := serviceResourceID(stored.ID)
	if stored.PendingID != "" {
		fullID = pendingResourceID(stored.ID, stored.PendingID)
	}
	return resource2doc(fullID, stored)
}

// newStagedDoc generates a staging doc that represents the given resource.
func newStagedDoc(stored storedResource) *resourceDoc {
	stagedID := stagedID(stored.ID)
	return resource2doc(stagedID, stored)
}

// resources returns the resource docs for the given service.
func (p Persistence) resources(serviceID string) ([]resourceDoc, error) {
	logger.Tracef("querying db for resources for %q", serviceID)
	var docs []resourceDoc
	query := bson.D{{"service-id", serviceID}}
	if err := p.base.All(resourcesC, query, &docs); err != nil {
		return nil, errors.Trace(err)
	}
	logger.Tracef("found %d resources", len(docs))
	return docs, nil
}

// getOne returns the resource that matches the provided model ID.
func (p Persistence) getOne(resID string) (resourceDoc, error) {
	logger.Tracef("querying db for resource %q", resID)
	id := serviceResourceID(resID)
	var doc resourceDoc
	if err := p.base.One(resourcesC, id, &doc); err != nil {
		return doc, errors.Trace(err)
	}
	return doc, nil
}

// getOnePending returns the resource that matches the provided model ID.
func (p Persistence) getOnePending(resID, pendingID string) (resourceDoc, error) {
	logger.Tracef("querying db for resource %q (pending %q)", resID, pendingID)
	id := pendingResourceID(resID, pendingID)
	var doc resourceDoc
	if err := p.base.One(resourcesC, id, &doc); err != nil {
		return doc, errors.Trace(err)
	}
	return doc, nil
}

// resourceDoc is the top-level document for resources.
type resourceDoc struct {
	DocID     string `bson:"_id"`
	EnvUUID   string `bson:"env-uuid"`
	ID        string `bson:"resource-id"`
	PendingID string `bson:"pending-id"`

	ServiceID string `bson:"service-id"`
	UnitID    string `bson:"unit-id"`

	Name        string `bson:"name"`
	Type        string `bson:"type"`
	Path        string `bson:"path"`
	Description string `bson:"description"`

	Origin      string `bson:"origin"`
	Revision    int    `bson:"revision"`
	Fingerprint []byte `bson:"fingerprint"`
	Size        int64  `bson:"size"`

	Username  string    `bson:"username"`
	Timestamp time.Time `bson:"timestamp-when-added"`

	StoragePath string `bson:"storage-path"`

	LastPolled time.Time `bson:"timestamp-when-last-polled"`
}

func charmStoreResource2Doc(id string, res charmStoreResource) *resourceDoc {
	stored := storedResource{
		Resource: resource.Resource{
			Resource:  res.Resource,
			ID:        res.id,
			ServiceID: res.serviceID,
		},
	}
	doc := resource2doc(id, stored)
	doc.LastPolled = res.lastPolled
	return doc
}

func unitResource2Doc(id, unitID string, stored storedResource) *resourceDoc {
	doc := resource2doc(id, stored)
	doc.UnitID = unitID
	return doc
}

// resource2doc converts the resource into a DB doc.
func resource2doc(id string, stored storedResource) *resourceDoc {
	res := stored.Resource
	// TODO(ericsnow) We may need to limit the resolution of timestamps
	// in order to avoid some conversion problems from Mongo.
	return &resourceDoc{
		DocID:     id,
		ID:        res.ID,
		PendingID: res.PendingID,

		ServiceID: res.ServiceID,

		Name:        res.Name,
		Type:        res.Type.String(),
		Path:        res.Path,
		Description: res.Description,

		Origin:      res.Origin.String(),
		Revision:    res.Revision,
		Fingerprint: res.Fingerprint.Bytes(),
		Size:        res.Size,

		Username:  res.Username,
		Timestamp: res.Timestamp,

		StoragePath: stored.storagePath,
	}
}

// doc2resource returns the resource info represented by the doc.
func doc2resource(doc resourceDoc) (storedResource, error) {
	res, err := doc2basicResource(doc)
	if err != nil {
		return storedResource{}, errors.Trace(err)
	}

	stored := storedResource{
		Resource:    res,
		storagePath: doc.StoragePath,
	}
	return stored, nil
}

// doc2basicResource returns the resource info represented by the doc.
func doc2basicResource(doc resourceDoc) (resource.Resource, error) {
	var res resource.Resource

	resType, err := charmresource.ParseType(doc.Type)
	if err != nil {
		return res, errors.Annotate(err, "got invalid data from DB")
	}

	origin, err := charmresource.ParseOrigin(doc.Origin)
	if err != nil {
		return res, errors.Annotate(err, "got invalid data from DB")
	}

	fp, err := resource.DeserializeFingerprint(doc.Fingerprint)
	if err != nil {
		return res, errors.Annotate(err, "got invalid data from DB")
	}

	res = resource.Resource{
		Resource: charmresource.Resource{
			Meta: charmresource.Meta{
				Name:        doc.Name,
				Type:        resType,
				Path:        doc.Path,
				Description: doc.Description,
			},
			Origin:      origin,
			Revision:    doc.Revision,
			Fingerprint: fp,
			Size:        doc.Size,
		},
		ID:        doc.ID,
		PendingID: doc.PendingID,
		ServiceID: doc.ServiceID,
		Username:  doc.Username,
		Timestamp: doc.Timestamp,
	}
	if err := res.Validate(); err != nil {
		return res, errors.Annotate(err, "got invalid data from DB")
	}
	return res, nil
}
<commit_msg>Rename some constants.<commit_after>// Copyright 2015 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package persistence

import (
	"fmt"
	"time"

	"github.com/juju/errors"
	charmresource "gopkg.in/juju/charm.v6-unstable/resource"
	"gopkg.in/mgo.v2/bson"
	"gopkg.in/mgo.v2/txn"

	"github.com/juju/juju/resource"
)

const (
	resourcesC = "resources"

	resourcesStagedIDSuffix     = "#staged"
	resourcesCharmstoreIDSuffix = "#charmstore"
)

// resourceID converts an external resource ID into an internal one.
func resourceID(id, subType, subID string) string {
	if subType == "" {
		return fmt.Sprintf("resource#%s", id)
	}
	return fmt.Sprintf("resource#%s#%s-%s", id, subType, subID)
}

func serviceResourceID(id string) string {
	return resourceID(id, "", "")
}

func pendingResourceID(id, pendingID string) string {
	return resourceID(id, "pending", pendingID)
}

func charmStoreResourceID(id string) string {
	return serviceResourceID(id) + resourcesCharmstoreIDSuffix
}

func unitResourceID(id, unitID string) string {
	return resourceID(id, "unit", unitID)
}

// stagedID converts an external resource ID into an internal staged one.
func stagedID(id string) string {
	return serviceResourceID(id) + resourcesStagedIDSuffix
}

// storedResource holds all model-stored information for a resource.
type storedResource struct {
	resource.Resource

	// storagePath is the path to where the resource content is stored.
	storagePath string
}

// charmStoreResource holds the info for a resource as provided by the
// charm store at as specific point in time.
type charmStoreResource struct {
	charmresource.Resource
	id         string
	serviceID  string
	lastPolled time.Time
}

func newStagedResourceOps(stored storedResource) []txn.Op {
	doc := newStagedDoc(stored)

	return []txn.Op{{
		C:      resourcesC,
		Id:     doc.DocID,
		Assert: txn.DocMissing,
		Insert: doc,
	}}
}

func newEnsureStagedSameOps(stored storedResource) []txn.Op {
	doc := newStagedDoc(stored)

	// Other than cause the txn to abort, we don't do anything here.
	return []txn.Op{{
		C:      resourcesC,
		Id:     doc.DocID,
		Assert: doc, // TODO(ericsnow) Is this okay?
	}}
}

func newRemoveStagedOps(id string) []txn.Op {
	fullID := stagedID(id)

	// We don't assert that it exists. We want "missing" to be a noop.
	return []txn.Op{{
		C:      resourcesC,
		Id:     fullID,
		Remove: true,
	}}
}

func newInsertResourceOps(stored storedResource) []txn.Op {
	doc := newResourceDoc(stored)

	return []txn.Op{{
		C:      resourcesC,
		Id:     doc.DocID,
		Assert: txn.DocMissing,
		Insert: doc,
	}}
}

func newUpdateResourceOps(stored storedResource) []txn.Op {
	doc := newResourceDoc(stored)

	// TODO(ericsnow) Using "update" doesn't work right...
	return append([]txn.Op{{
		C:      resourcesC,
		Id:     doc.DocID,
		Assert: txn.DocExists,
		Remove: true,
	}}, newInsertResourceOps(stored)...)
}

func newInsertCharmStoreResourceOps(res charmStoreResource) []txn.Op {
	doc := newCharmStoreResourceDoc(res)

	return []txn.Op{{
		C:      resourcesC,
		Id:     doc.DocID,
		Assert: txn.DocMissing,
		Insert: doc,
	}}
}

func newUpdateCharmStoreResourceOps(res charmStoreResource) []txn.Op {
	doc := newCharmStoreResourceDoc(res)

	// TODO(ericsnow) Using "update" doesn't work right...
	return append([]txn.Op{{
		C:      resourcesC,
		Id:     doc.DocID,
		Assert: txn.DocExists,
		Remove: true,
	}}, newInsertCharmStoreResourceOps(res)...)
}

func newInsertUnitResourceOps(unitID string, stored storedResource) []txn.Op {
	doc := newUnitResourceDoc(unitID, stored)

	return []txn.Op{{
		C:      resourcesC,
		Id:     doc.DocID,
		Assert: txn.DocMissing,
		Insert: doc,
	}}
}

func newUpdateUnitResourceOps(unitID string, stored storedResource) []txn.Op {
	doc := newUnitResourceDoc(unitID, stored)

	// TODO(ericsnow) Using "update" doesn't work right...
	return append([]txn.Op{{
		C:      resourcesC,
		Id:     doc.DocID,
		Assert: txn.DocExists,
		Remove: true,
	}}, newInsertUnitResourceOps(unitID, stored)...)
}

// newResolvePendingResourceOps generates transaction operations that
// will resolve a pending resource doc and make it active.
//
// We trust that the provided resource really is pending
// and that it matches the existing doc with the same ID.
func newResolvePendingResourceOps(pending storedResource, exists bool) []txn.Op {
	oldID := pendingResourceID(pending.ID, pending.PendingID)
	newRes := pending
	newRes.PendingID = ""
	// TODO(ericsnow) Update newRes.StoragePath? Doing so would require
	// moving the resource in the blobstore to the correct path, which
	// we cannot do in the transaction...
	ops := []txn.Op{{
		C:      resourcesC,
		Id:     oldID,
		Assert: txn.DocExists,
		Remove: true,
	}}
	if exists {
		return append(ops, newUpdateResourceOps(newRes)...)
	} else {
		return append(ops, newInsertResourceOps(newRes)...)
	}
}

// newCharmStoreResourceDoc generates a doc that represents the given resource.
func newCharmStoreResourceDoc(res charmStoreResource) *resourceDoc {
	fullID := charmStoreResourceID(res.id)
	return charmStoreResource2Doc(fullID, res)
}

// newUnitResourceDoc generates a doc that represents the given resource.
func newUnitResourceDoc(unitID string, stored storedResource) *resourceDoc {
	fullID := unitResourceID(stored.ID, unitID)
	return unitResource2Doc(fullID, unitID, stored)
}

// newResourceDoc generates a doc that represents the given resource.
func newResourceDoc(stored storedResource) *resourceDoc {
	fullID := serviceResourceID(stored.ID)
	if stored.PendingID != "" {
		fullID = pendingResourceID(stored.ID, stored.PendingID)
	}
	return resource2doc(fullID, stored)
}

// newStagedDoc generates a staging doc that represents the given resource.
func newStagedDoc(stored storedResource) *resourceDoc {
	stagedID := stagedID(stored.ID)
	return resource2doc(stagedID, stored)
}

// resources returns the resource docs for the given service.
func (p Persistence) resources(serviceID string) ([]resourceDoc, error) {
	logger.Tracef("querying db for resources for %q", serviceID)
	var docs []resourceDoc
	query := bson.D{{"service-id", serviceID}}
	if err := p.base.All(resourcesC, query, &docs); err != nil {
		return nil, errors.Trace(err)
	}
	logger.Tracef("found %d resources", len(docs))
	return docs, nil
}

// getOne returns the resource that matches the provided model ID.
func (p Persistence) getOne(resID string) (resourceDoc, error) {
	logger.Tracef("querying db for resource %q", resID)
	id := serviceResourceID(resID)
	var doc resourceDoc
	if err := p.base.One(resourcesC, id, &doc); err != nil {
		return doc, errors.Trace(err)
	}
	return doc, nil
}

// getOnePending returns the resource that matches the provided model ID.
func (p Persistence) getOnePending(resID, pendingID string) (resourceDoc, error) {
	logger.Tracef("querying db for resource %q (pending %q)", resID, pendingID)
	id := pendingResourceID(resID, pendingID)
	var doc resourceDoc
	if err := p.base.One(resourcesC, id, &doc); err != nil {
		return doc, errors.Trace(err)
	}
	return doc, nil
}

// resourceDoc is the top-level document for resources.
type resourceDoc struct {
	DocID     string `bson:"_id"`
	EnvUUID   string `bson:"env-uuid"`
	ID        string `bson:"resource-id"`
	PendingID string `bson:"pending-id"`

	ServiceID string `bson:"service-id"`
	UnitID    string `bson:"unit-id"`

	Name        string `bson:"name"`
	Type        string `bson:"type"`
	Path        string `bson:"path"`
	Description string `bson:"description"`

	Origin      string `bson:"origin"`
	Revision    int    `bson:"revision"`
	Fingerprint []byte `bson:"fingerprint"`
	Size        int64  `bson:"size"`

	Username  string    `bson:"username"`
	Timestamp time.Time `bson:"timestamp-when-added"`

	StoragePath string `bson:"storage-path"`

	LastPolled time.Time `bson:"timestamp-when-last-polled"`
}

func charmStoreResource2Doc(id string, res charmStoreResource) *resourceDoc {
	stored := storedResource{
		Resource: resource.Resource{
			Resource:  res.Resource,
			ID:        res.id,
			ServiceID: res.serviceID,
		},
	}
	doc := resource2doc(id, stored)
	doc.LastPolled = res.lastPolled
	return doc
}

func unitResource2Doc(id, unitID string, stored storedResource) *resourceDoc {
	doc := resource2doc(id, stored)
	doc.UnitID = unitID
	return doc
}

// resource2doc converts the resource into a DB doc.
func resource2doc(id string, stored storedResource) *resourceDoc {
	res := stored.Resource
	// TODO(ericsnow) We may need to limit the resolution of timestamps
	// in order to avoid some conversion problems from Mongo.
	return &resourceDoc{
		DocID:     id,
		ID:        res.ID,
		PendingID: res.PendingID,

		ServiceID: res.ServiceID,

		Name:        res.Name,
		Type:        res.Type.String(),
		Path:        res.Path,
		Description: res.Description,

		Origin:      res.Origin.String(),
		Revision:    res.Revision,
		Fingerprint: res.Fingerprint.Bytes(),
		Size:        res.Size,

		Username:  res.Username,
		Timestamp: res.Timestamp,

		StoragePath: stored.storagePath,
	}
}

// doc2resource returns the resource info represented by the doc.
func doc2resource(doc resourceDoc) (storedResource, error) {
	res, err := doc2basicResource(doc)
	if err != nil {
		return storedResource{}, errors.Trace(err)
	}

	stored := storedResource{
		Resource:    res,
		storagePath: doc.StoragePath,
	}
	return stored, nil
}

// doc2basicResource returns the resource info represented by the doc.
func doc2basicResource(doc resourceDoc) (resource.Resource, error) {
	var res resource.Resource

	resType, err := charmresource.ParseType(doc.Type)
	if err != nil {
		return res, errors.Annotate(err, "got invalid data from DB")
	}

	origin, err := charmresource.ParseOrigin(doc.Origin)
	if err != nil {
		return res, errors.Annotate(err, "got invalid data from DB")
	}

	fp, err := resource.DeserializeFingerprint(doc.Fingerprint)
	if err != nil {
		return res, errors.Annotate(err, "got invalid data from DB")
	}

	res = resource.Resource{
		Resource: charmresource.Resource{
			Meta: charmresource.Meta{
				Name:        doc.Name,
				Type:        resType,
				Path:        doc.Path,
				Description: doc.Description,
			},
			Origin:      origin,
			Revision:    doc.Revision,
			Fingerprint: fp,
			Size:        doc.Size,
		},
		ID:        doc.ID,
		PendingID: doc.PendingID,
		ServiceID: doc.ServiceID,
		Username:  doc.Username,
		Timestamp: doc.Timestamp,
	}
	if err := res.Validate(); err != nil {
		return res, errors.Annotate(err, "got invalid data from DB")
	}
	return res, nil
}
<|endoftext|>