<commit_before>// Copyright (c) 2012 VMware, Inc.

package sigar

import (
	"bufio"
	"bytes"
	"io"
	"io/ioutil"
	"os"
	"strconv"
	"strings"
	"syscall"
)

const procd = "/proc/"

var system struct {
	ticks uint64
	btime uint64
}

func init() {
	system.ticks = 100 // C.sysconf(C._SC_CLK_TCK)

	// grab system boot time
	readFile(procd+"stat", func(line string) bool {
		if strings.HasPrefix(line, "btime") {
			system.btime, _ = strtoull(line[6:])
			return false // stop reading
		}
		return true
	})
}

func (self *LoadAverage) Get() error {
	line, err := ioutil.ReadFile(procd + "loadavg")
	if err != nil {
		return nil
	}

	fields := strings.Fields(string(line))

	self.One, _ = strconv.ParseFloat(fields[0], 64)
	self.Five, _ = strconv.ParseFloat(fields[1], 64)
	self.Fifteen, _ = strconv.ParseFloat(fields[2], 64)

	return nil
}

func (self *Uptime) Get() error {
	sysinfo := syscall.Sysinfo_t{}

	if err := syscall.Sysinfo(&sysinfo); err != nil {
		return err
	}

	self.Length = float64(sysinfo.Uptime)

	return nil
}

func (self *Mem) Get() error {
	var buffers, cached uint64
	table := map[string]*uint64{
		"MemTotal": &self.Total,
		"MemFree":  &self.Free,
		"Buffers":  &buffers,
		"Cached":   &cached,
	}

	if err := parseMeminfo(table); err != nil {
		return err
	}

	self.Used = self.Total - self.Free
	kern := buffers + cached
	self.ActualFree = self.Free + kern
	self.ActualUsed = self.Used - kern

	return nil
}

func (self *Swap) Get() error {
	sysinfo := syscall.Sysinfo_t{}

	if err := syscall.Sysinfo(&sysinfo); err != nil {
		return err
	}

	self.Total = uint64(sysinfo.Totalswap)
	self.Free = uint64(sysinfo.Freeswap)
	self.Used = self.Total - self.Free

	return nil
}

func (self *Cpu) Get() error {
	return readFile(procd+"stat", func(line string) bool {
		if line[0:4] == "cpu " {
			parseCpuStat(self, line)
			return false
		}
		return true
	})
}

func (self *CpuList) Get() error {
	capacity := len(self.List)
	if capacity == 0 {
		capacity = 4
	}
	list := make([]Cpu, 0, capacity)

	err := readFile(procd+"stat", func(line string) bool {
		if line[0:3] == "cpu" && line[3] != ' ' {
			cpu := Cpu{}
			parseCpuStat(&cpu, line)
			list = append(list, cpu)
		}
		return true
	})

	self.List = list

	return err
}

func (self *FileSystemList) Get() error {
	capacity := len(self.List)
	if capacity == 0 {
		capacity = 10
	}
	fslist := make([]FileSystem, 0, capacity)

	err := readFile("/etc/mtab", func(line string) bool {
		fields := strings.Fields(line)

		fs := FileSystem{}
		fs.DevName = fields[0]
		fs.DirName = fields[1]
		fs.SysTypeName = fields[2]
		fs.Options = fields[3]

		fslist = append(fslist, fs)

		return true
	})

	self.List = fslist

	return err
}

func (self *ProcList) Get() error {
	dir, err := os.Open(procd)
	if err != nil {
		return err
	}
	defer dir.Close()

	const readAllDirnames = -1 // see os.File.Readdirnames doc

	names, err := dir.Readdirnames(readAllDirnames)
	if err != nil {
		return err
	}

	capacity := len(names)
	list := make([]int, 0, capacity)

	for _, name := range names {
		if name[0] < '0' || name[0] > '9' {
			continue
		}
		pid, err := strconv.Atoi(name)
		if err == nil {
			list = append(list, pid)
		}
	}

	self.List = list

	return nil
}

func (self *ProcState) Get(pid int) error {
	contents, err := readProcFile(pid, "stat")
	if err != nil {
		return err
	}

	fields := strings.Fields(string(contents))

	self.Name = fields[1][1 : len(fields[1])-1] // strip ()'s

	self.State = RunState(fields[2][0])

	self.Ppid, _ = strconv.Atoi(fields[3])

	self.Tty, _ = strconv.Atoi(fields[6])

	self.Priority, _ = strconv.Atoi(fields[17])

	self.Nice, _ = strconv.Atoi(fields[18])

	self.Processor, _ = strconv.Atoi(fields[38])

	return nil
}

func (self *ProcMem) Get(pid int) error {
	contents, err := readProcFile(pid, "statm")
	if err != nil {
		return err
	}

	fields := strings.Fields(string(contents))

	size, _ := strtoull(fields[0])
	self.Size = size << 12

	rss, _ := strtoull(fields[1])
	self.Resident = rss << 12

	share, _ := strtoull(fields[2])
	self.Share = share << 12

	contents, err = readProcFile(pid, "stat")
	if err != nil {
		return err
	}

	fields = strings.Fields(string(contents))

	self.MinorFaults, _ = strtoull(fields[10])
	self.MajorFaults, _ = strtoull(fields[12])
	self.PageFaults = self.MinorFaults + self.MajorFaults

	return nil
}

func (self *ProcTime) Get(pid int) error {
	contents, err := readProcFile(pid, "stat")
	if err != nil {
		return err
	}

	fields := strings.Fields(string(contents))

	user, _ := strtoull(fields[13])
	sys, _ := strtoull(fields[14])
	// convert to millis
	self.User = user * (1000 / system.ticks)
	self.Sys = sys * (1000 / system.ticks)
	self.Total = self.User + self.Sys

	// convert to millis
	self.StartTime, _ = strtoull(fields[21])
	self.StartTime /= system.ticks
	self.StartTime += system.btime
	self.StartTime *= 1000

	return nil
}

func (self *ProcArgs) Get(pid int) error {
	contents, err := readProcFile(pid, "cmdline")
	if err != nil {
		return err
	}

	bbuf := bytes.NewBuffer(contents)

	var args []string

	for {
		arg, err := bbuf.ReadBytes(0)
		if err == io.EOF {
			break
		}
		args = append(args, string(chop(arg)))
	}

	self.List = args

	return nil
}

func (self *ProcExe) Get(pid int) error {
	fields := map[string]*string{
		"exe":  &self.Name,
		"cwd":  &self.Cwd,
		"root": &self.Root,
	}

	for name, field := range fields {
		val, err := os.Readlink(procFileName(pid, name))

		if err != nil {
			return err
		}

		*field = val
	}

	return nil
}

func parseMeminfo(table map[string]*uint64) error {
	return readFile(procd+"meminfo", func(line string) bool {
		fields := strings.Split(line, ":")

		if ptr := table[fields[0]]; ptr != nil {
			num := strings.TrimLeft(fields[1], " ")
			val, err := strtoull(strings.Fields(num)[0])
			if err == nil {
				*ptr = val * 1024
			}
		}

		return true
	})
}

func parseCpuStat(self *Cpu, line string) error {
	fields := strings.Fields(line)

	self.User, _ = strtoull(fields[1])
	self.Nice, _ = strtoull(fields[2])
	self.Sys, _ = strtoull(fields[3])
	self.Idle, _ = strtoull(fields[4])
	self.Wait, _ = strtoull(fields[5])
	self.Irq, _ = strtoull(fields[6])
	self.SoftIrq, _ = strtoull(fields[7])
	self.Stolen, _ = strtoull(fields[8])

	return nil
}

func readFile(file string, handler func(string) bool) error {
	contents, err := ioutil.ReadFile(file)
	if err != nil {
		return err
	}

	reader := bufio.NewReader(bytes.NewBuffer(contents))

	for {
		line, _, err := reader.ReadLine()
		if err == io.EOF {
			break
		}
		if !handler(string(line)) {
			break
		}
	}

	return nil
}

func strtoull(val string) (uint64, error) {
	return strconv.ParseUint(val, 10, 64)
}

func procFileName(pid int, name string) string {
	return procd + strconv.Itoa(pid) + "/" + name
}

func readProcFile(pid int, name string) ([]byte, error) {
	path := procFileName(pid, name)
	contents, err := ioutil.ReadFile(path)

	if err != nil {
		if perr, ok := err.(*os.PathError); ok {
			if perr.Err == syscall.ENOENT {
				return nil, syscall.ESRCH
			}
		}
	}

	return contents, err
}
<commit_msg>Revert "Fix a compilation error on x86 systems by converting TotalSwap and FreeSwap to uint64."<commit_after>// Copyright (c) 2012 VMware, Inc.

package sigar

import (
	"bufio"
	"bytes"
	"io"
	"io/ioutil"
	"os"
	"strconv"
	"strings"
	"syscall"
)

const procd = "/proc/"

var system struct {
	ticks uint64
	btime uint64
}

func init() {
	system.ticks = 100 // C.sysconf(C._SC_CLK_TCK)

	// grab system boot time
	readFile(procd+"stat", func(line string) bool {
		if strings.HasPrefix(line, "btime") {
			system.btime, _ = strtoull(line[6:])
			return false // stop reading
		}
		return true
	})
}

func (self *LoadAverage) Get() error {
	line, err := ioutil.ReadFile(procd + "loadavg")
	if err != nil {
		return nil
	}

	fields := strings.Fields(string(line))

	self.One, _ = strconv.ParseFloat(fields[0], 64)
	self.Five, _ = strconv.ParseFloat(fields[1], 64)
	self.Fifteen, _ = strconv.ParseFloat(fields[2], 64)

	return nil
}

func (self *Uptime) Get() error {
	sysinfo := syscall.Sysinfo_t{}

	if err := syscall.Sysinfo(&sysinfo); err != nil {
		return err
	}

	self.Length = float64(sysinfo.Uptime)

	return nil
}

func (self *Mem) Get() error {
	var buffers, cached uint64
	table := map[string]*uint64{
		"MemTotal": &self.Total,
		"MemFree":  &self.Free,
		"Buffers":  &buffers,
		"Cached":   &cached,
	}

	if err := parseMeminfo(table); err != nil {
		return err
	}

	self.Used = self.Total - self.Free
	kern := buffers + cached
	self.ActualFree = self.Free + kern
	self.ActualUsed = self.Used - kern

	return nil
}

func (self *Swap) Get() error {
	sysinfo := syscall.Sysinfo_t{}

	if err := syscall.Sysinfo(&sysinfo); err != nil {
		return err
	}

	self.Total = sysinfo.Totalswap
	self.Free = sysinfo.Freeswap
	self.Used = self.Total - self.Free

	return nil
}

func (self *Cpu) Get() error {
	return readFile(procd+"stat", func(line string) bool {
		if line[0:4] == "cpu " {
			parseCpuStat(self, line)
			return false
		}
		return true
	})
}

func (self *CpuList) Get() error {
	capacity := len(self.List)
	if capacity == 0 {
		capacity = 4
	}
	list := make([]Cpu, 0, capacity)

	err := readFile(procd+"stat", func(line string) bool {
		if line[0:3] == "cpu" && line[3] != ' ' {
			cpu := Cpu{}
			parseCpuStat(&cpu, line)
			list = append(list, cpu)
		}
		return true
	})

	self.List = list

	return err
}

func (self *FileSystemList) Get() error {
	capacity := len(self.List)
	if capacity == 0 {
		capacity = 10
	}
	fslist := make([]FileSystem, 0, capacity)

	err := readFile("/etc/mtab", func(line string) bool {
		fields := strings.Fields(line)

		fs := FileSystem{}
		fs.DevName = fields[0]
		fs.DirName = fields[1]
		fs.SysTypeName = fields[2]
		fs.Options = fields[3]

		fslist = append(fslist, fs)

		return true
	})

	self.List = fslist

	return err
}

func (self *ProcList) Get() error {
	dir, err := os.Open(procd)
	if err != nil {
		return err
	}
	defer dir.Close()

	const readAllDirnames = -1 // see os.File.Readdirnames doc

	names, err := dir.Readdirnames(readAllDirnames)
	if err != nil {
		return err
	}

	capacity := len(names)
	list := make([]int, 0, capacity)

	for _, name := range names {
		if name[0] < '0' || name[0] > '9' {
			continue
		}
		pid, err := strconv.Atoi(name)
		if err == nil {
			list = append(list, pid)
		}
	}

	self.List = list

	return nil
}

func (self *ProcState) Get(pid int) error {
	contents, err := readProcFile(pid, "stat")
	if err != nil {
		return err
	}

	fields := strings.Fields(string(contents))

	self.Name = fields[1][1 : len(fields[1])-1] // strip ()'s

	self.State = RunState(fields[2][0])

	self.Ppid, _ = strconv.Atoi(fields[3])

	self.Tty, _ = strconv.Atoi(fields[6])

	self.Priority, _ = strconv.Atoi(fields[17])

	self.Nice, _ = strconv.Atoi(fields[18])

	self.Processor, _ = strconv.Atoi(fields[38])

	return nil
}

func (self *ProcMem) Get(pid int) error {
	contents, err := readProcFile(pid, "statm")
	if err != nil {
		return err
	}

	fields := strings.Fields(string(contents))

	size, _ := strtoull(fields[0])
	self.Size = size << 12

	rss, _ := strtoull(fields[1])
	self.Resident = rss << 12

	share, _ := strtoull(fields[2])
	self.Share = share << 12

	contents, err = readProcFile(pid, "stat")
	if err != nil {
		return err
	}

	fields = strings.Fields(string(contents))

	self.MinorFaults, _ = strtoull(fields[10])
	self.MajorFaults, _ = strtoull(fields[12])
	self.PageFaults = self.MinorFaults + self.MajorFaults

	return nil
}

func (self *ProcTime) Get(pid int) error {
	contents, err := readProcFile(pid, "stat")
	if err != nil {
		return err
	}

	fields := strings.Fields(string(contents))

	user, _ := strtoull(fields[13])
	sys, _ := strtoull(fields[14])
	// convert to millis
	self.User = user * (1000 / system.ticks)
	self.Sys = sys * (1000 / system.ticks)
	self.Total = self.User + self.Sys

	// convert to millis
	self.StartTime, _ = strtoull(fields[21])
	self.StartTime /= system.ticks
	self.StartTime += system.btime
	self.StartTime *= 1000

	return nil
}

func (self *ProcArgs) Get(pid int) error {
	contents, err := readProcFile(pid, "cmdline")
	if err != nil {
		return err
	}

	bbuf := bytes.NewBuffer(contents)

	var args []string

	for {
		arg, err := bbuf.ReadBytes(0)
		if err == io.EOF {
			break
		}
		args = append(args, string(chop(arg)))
	}

	self.List = args

	return nil
}

func (self *ProcExe) Get(pid int) error {
	fields := map[string]*string{
		"exe":  &self.Name,
		"cwd":  &self.Cwd,
		"root": &self.Root,
	}

	for name, field := range fields {
		val, err := os.Readlink(procFileName(pid, name))

		if err != nil {
			return err
		}

		*field = val
	}

	return nil
}

func parseMeminfo(table map[string]*uint64) error {
	return readFile(procd+"meminfo", func(line string) bool {
		fields := strings.Split(line, ":")

		if ptr := table[fields[0]]; ptr != nil {
			num := strings.TrimLeft(fields[1], " ")
			val, err := strtoull(strings.Fields(num)[0])
			if err == nil {
				*ptr = val * 1024
			}
		}

		return true
	})
}

func parseCpuStat(self *Cpu, line string) error {
	fields := strings.Fields(line)

	self.User, _ = strtoull(fields[1])
	self.Nice, _ = strtoull(fields[2])
	self.Sys, _ = strtoull(fields[3])
	self.Idle, _ = strtoull(fields[4])
	self.Wait, _ = strtoull(fields[5])
	self.Irq, _ = strtoull(fields[6])
	self.SoftIrq, _ = strtoull(fields[7])
	self.Stolen, _ = strtoull(fields[8])

	return nil
}

func readFile(file string, handler func(string) bool) error {
	contents, err := ioutil.ReadFile(file)
	if err != nil {
		return err
	}

	reader := bufio.NewReader(bytes.NewBuffer(contents))

	for {
		line, _, err := reader.ReadLine()
		if err == io.EOF {
			break
		}
		if !handler(string(line)) {
			break
		}
	}

	return nil
}

func strtoull(val string) (uint64, error) {
	return strconv.ParseUint(val, 10, 64)
}

func procFileName(pid int, name string) string {
	return procd + strconv.Itoa(pid) + "/" + name
}

func readProcFile(pid int, name string) ([]byte, error) {
	path := procFileName(pid, name)
	contents, err := ioutil.ReadFile(path)

	if err != nil {
		if perr, ok := err.(*os.PathError); ok {
			if perr.Err == syscall.ENOENT {
				return nil, syscall.ESRCH
			}
		}
	}

	return contents, err
}
<|endoftext|>


<commit_before>package main

import (
	"os"
	"os/exec"
	"path/filepath"
	"strings"
)

func setPath() error {
	prevpath := os.Getenv("PATH")
	newPath := filepath.Join(toolDirPath, "bin") + string(os.PathListSeparator) + prevpath
	return os.Setenv("PATH", newPath)
}

func setGoEnv() error {
	newGoBin := filepath.Join(toolDirPath, "bin")
	if err := os.Setenv("GOBIN", newGoBin); err != nil {
		return err
	}

	prevGoPath := os.Getenv("GOPATH")
	newGoPath := toolDirPath + string(os.PathListSeparator) + prevGoPath
	return os.Setenv("GOPATH", newGoPath)
}

func do() {
	args := positionalArgs
	if len(args) == 0 {
		fatal("no command passed to retool do", nil)
	}

	if err := setPath(); err != nil {
		fatal("unable to set PATH", err)
	}
	if err := setGoEnv(); err != nil {
		fatal("unable to set up go environment variables", err)
	}

	cmd := exec.Command(args[0], args[1:]...)
	cmd.Stdin = os.Stdin
	cmd.Stderr = os.Stderr
	cmd.Stdout = os.Stdout

	err := cmd.Run()
	if err != nil {
		msg := "failed on '" + strings.Join(args, " ") + "'"
		fatal(msg, err)
	}
}
<commit_msg>Document go env settings' justifications, fix GOPATH order (#18)<commit_after>package main

import (
	"os"
	"os/exec"
	"path/filepath"
	"strings"
)

func setPath() error {
	prevpath := os.Getenv("PATH")
	newPath := filepath.Join(toolDirPath, "bin") + string(os.PathListSeparator) + prevpath
	return os.Setenv("PATH", newPath)
}

// setGoEnv sets GOBIN and GOPATH to point to _tools/bin and $GOPATH:_tools,
// respectively. This is done for github.com/alecthomas/gometalinter
// compatibility: gometalinter vendors its own linters, and checks for their
// existence by checking in each GOPATH entry for
// src/github.com/alecthomas/gometalinter/_linters.
//
// GOBIN is set so gometalinter will use it to decide where to put its vendored
// linters with the gometalinter --install command, and so that it prefers the
// binaries built in _tools/bin when executing linters.
func setGoEnv() error {
	newGoBin := filepath.Join(toolDirPath, "bin")
	if err := os.Setenv("GOBIN", newGoBin); err != nil {
		return err
	}

	prevGoPath := os.Getenv("GOPATH")
	newGoPath := prevGoPath + string(os.PathListSeparator) + toolDirPath
	return os.Setenv("GOPATH", newGoPath)
}

func do() {
	args := positionalArgs
	if len(args) == 0 {
		fatal("no command passed to retool do", nil)
	}

	if err := setPath(); err != nil {
		fatal("unable to set PATH", err)
	}
	if err := setGoEnv(); err != nil {
		fatal("unable to set up go environment variables", err)
	}

	cmd := exec.Command(args[0], args[1:]...)
	cmd.Stdin = os.Stdin
	cmd.Stderr = os.Stderr
	cmd.Stdout = os.Stdout

	err := cmd.Run()
	if err != nil {
		msg := "failed on '" + strings.Join(args, " ") + "'"
		fatal(msg, err)
	}
}
<|endoftext|>


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

// This file implements scopes and the objects they contain.

package ast

import (
	"bytes"
	"fmt"
	"go/token"
)

// A Scope maintains the set of named language entities declared
// in the scope and a link to the immediately surrounding (outer)
// scope.
//
type Scope struct {
	Outer   *Scope
	Objects map[string]*Object
}

// NewScope creates a new scope nested in the outer scope.
func NewScope(outer *Scope) *Scope {
	const n = 4 // initial scope capacity
	return &Scope{outer, make(map[string]*Object, n)}
}

// Lookup returns the object with the given name if it is
// found in scope s, otherwise it returns nil. Outer scopes
// are ignored.
//
func (s *Scope) Lookup(name string) *Object {
	return s.Objects[name]
}

// Insert attempts to insert a named object obj into the scope s.
// If the scope already contains an object alt with the same name,
// Insert leaves the scope unchanged and returns alt. Otherwise
// it inserts obj and returns nil."
//
func (s *Scope) Insert(obj *Object) (alt *Object) {
	if alt = s.Objects[obj.Name]; alt == nil {
		s.Objects[obj.Name] = obj
	}
	return
}

// Debugging support
func (s *Scope) String() string {
	var buf bytes.Buffer
	fmt.Fprintf(&buf, "scope %p {", s)
	if s != nil && len(s.Objects) > 0 {
		fmt.Fprintln(&buf)
		for _, obj := range s.Objects {
			fmt.Fprintf(&buf, "\t%s %s\n", obj.Kind, obj.Name)
		}
	}
	fmt.Fprintf(&buf, "}\n")
	return buf.String()
}

// ----------------------------------------------------------------------------
// Objects

// An Object describes a named language entity such as a package,
// constant, type, variable, function (incl. methods), or label.
//
// The Data fields contains object-specific data:
//
//	Kind    Data type         Data value
//	Pkg	*types.Package    package scope
//	Con     int               iota for the respective declaration
//	Con     != nil            constant value
//	Typ     *Scope            (used as method scope during type checking - transient)
//
type Object struct {
	Kind ObjKind
	Name string      // declared name
	Decl interface{} // corresponding Field, XxxSpec, FuncDecl, LabeledStmt, AssignStmt, Scope; or nil
	Data interface{} // object-specific data; or nil
	Type interface{} // place holder for type information; may be nil
}

// NewObj creates a new object of a given kind and name.
func NewObj(kind ObjKind, name string) *Object {
	return &Object{Kind: kind, Name: name}
}

// Pos computes the source position of the declaration of an object name.
// The result may be an invalid position if it cannot be computed
// (obj.Decl may be nil or not correct).
func (obj *Object) Pos() token.Pos {
	name := obj.Name
	switch d := obj.Decl.(type) {
	case *Field:
		for _, n := range d.Names {
			if n.Name == name {
				return n.Pos()
			}
		}
	case *ImportSpec:
		if d.Name != nil && d.Name.Name == name {
			return d.Name.Pos()
		}
		return d.Path.Pos()
	case *ValueSpec:
		for _, n := range d.Names {
			if n.Name == name {
				return n.Pos()
			}
		}
	case *TypeSpec:
		if d.Name.Name == name {
			return d.Name.Pos()
		}
	case *FuncDecl:
		if d.Name.Name == name {
			return d.Name.Pos()
		}
	case *LabeledStmt:
		if d.Label.Name == name {
			return d.Label.Pos()
		}
	case *AssignStmt:
		for _, x := range d.Lhs {
			if ident, isIdent := x.(*Ident); isIdent && ident.Name == name {
				return ident.Pos()
			}
		}
	case *Scope:
		// predeclared object - nothing to do for now
	}
	return token.NoPos
}

// ObjKind describes what an object represents.
type ObjKind int

// The list of possible Object kinds.
const (
	Bad ObjKind = iota // for error handling
	Pkg                // package
	Con                // constant
	Typ                // type
	Var                // variable
	Fun                // function or method
	Lbl                // label
)

var objKindStrings = [...]string{
	Bad: "bad",
	Pkg: "package",
	Con: "const",
	Typ: "type",
	Var: "var",
	Fun: "func",
	Lbl: "label",
}

func (kind ObjKind) String() string { return objKindStrings[kind] }
<commit_msg>go/ast: fix typo in comment<commit_after>// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// This file implements scopes and the objects they contain.

package ast

import (
	"bytes"
	"fmt"
	"go/token"
)

// A Scope maintains the set of named language entities declared
// in the scope and a link to the immediately surrounding (outer)
// scope.
//
type Scope struct {
	Outer   *Scope
	Objects map[string]*Object
}

// NewScope creates a new scope nested in the outer scope.
func NewScope(outer *Scope) *Scope {
	const n = 4 // initial scope capacity
	return &Scope{outer, make(map[string]*Object, n)}
}

// Lookup returns the object with the given name if it is
// found in scope s, otherwise it returns nil. Outer scopes
// are ignored.
//
func (s *Scope) Lookup(name string) *Object {
	return s.Objects[name]
}

// Insert attempts to insert a named object obj into the scope s.
// If the scope already contains an object alt with the same name,
// Insert leaves the scope unchanged and returns alt. Otherwise
// it inserts obj and returns nil."
//
func (s *Scope) Insert(obj *Object) (alt *Object) {
	if alt = s.Objects[obj.Name]; alt == nil {
		s.Objects[obj.Name] = obj
	}
	return
}

// Debugging support
func (s *Scope) String() string {
	var buf bytes.Buffer
	fmt.Fprintf(&buf, "scope %p {", s)
	if s != nil && len(s.Objects) > 0 {
		fmt.Fprintln(&buf)
		for _, obj := range s.Objects {
			fmt.Fprintf(&buf, "\t%s %s\n", obj.Kind, obj.Name)
		}
	}
	fmt.Fprintf(&buf, "}\n")
	return buf.String()
}

// ----------------------------------------------------------------------------
// Objects

// An Object describes a named language entity such as a package,
// constant, type, variable, function (incl. methods), or label.
//
// The Data fields contains object-specific data:
//
//	Kind    Data type         Data value
//	Pkg	*types.Package    package scope
//	Con     int               iota for the respective declaration
//	Con     != nil            constant value
//	Typ     *Scope            (used as method scope during type checking - transient)
//
type Object struct {
	Kind ObjKind
	Name string      // declared name
	Decl interface{} // corresponding Field, XxxSpec, FuncDecl, LabeledStmt, AssignStmt, Scope; or nil
	Data interface{} // object-specific data; or nil
	Type interface{} // placeholder for type information; may be nil
}

// NewObj creates a new object of a given kind and name.
func NewObj(kind ObjKind, name string) *Object {
	return &Object{Kind: kind, Name: name}
}

// Pos computes the source position of the declaration of an object name.
// The result may be an invalid position if it cannot be computed
// (obj.Decl may be nil or not correct).
func (obj *Object) Pos() token.Pos {
	name := obj.Name
	switch d := obj.Decl.(type) {
	case *Field:
		for _, n := range d.Names {
			if n.Name == name {
				return n.Pos()
			}
		}
	case *ImportSpec:
		if d.Name != nil && d.Name.Name == name {
			return d.Name.Pos()
		}
		return d.Path.Pos()
	case *ValueSpec:
		for _, n := range d.Names {
			if n.Name == name {
				return n.Pos()
			}
		}
	case *TypeSpec:
		if d.Name.Name == name {
			return d.Name.Pos()
		}
	case *FuncDecl:
		if d.Name.Name == name {
			return d.Name.Pos()
		}
	case *LabeledStmt:
		if d.Label.Name == name {
			return d.Label.Pos()
		}
	case *AssignStmt:
		for _, x := range d.Lhs {
			if ident, isIdent := x.(*Ident); isIdent && ident.Name == name {
				return ident.Pos()
			}
		}
	case *Scope:
		// predeclared object - nothing to do for now
	}
	return token.NoPos
}

// ObjKind describes what an object represents.
type ObjKind int

// The list of possible Object kinds.
const (
	Bad ObjKind = iota // for error handling
	Pkg                // package
	Con                // constant
	Typ                // type
	Var                // variable
	Fun                // function or method
	Lbl                // label
)

var objKindStrings = [...]string{
	Bad: "bad",
	Pkg: "package",
	Con: "const",
	Typ: "type",
	Var: "var",
	Fun: "func",
	Lbl: "label",
}

func (kind ObjKind) String() string { return objKindStrings[kind] }
<|endoftext|>


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

package http

import (
	"bufio"
	"io"
	"net"
	"net/textproto"
	"os"
	"sync"
)

var (
	ErrPersistEOF = &ProtocolError{"persistent connection closed"}
	ErrPipeline   = &ProtocolError{"pipeline error"}
)

// A ServerConn reads requests and sends responses over an underlying
// connection, until the HTTP keepalive logic commands an end. ServerConn
// also allows hijacking the underlying connection by calling Hijack
// to regain control over the connection. ServerConn supports pipe-lining,
// i.e. requests can be read out of sync (but in the same order) while the
// respective responses are sent.
type ServerConn struct {
	lk              sync.Mutex // read-write protects the following fields
	c               net.Conn
	r               *bufio.Reader
	re, we          os.Error // read/write errors
	lastbody        io.ReadCloser
	nread, nwritten int
	pipereq         map[*Request]uint

	pipe textproto.Pipeline
}

// NewServerConn returns a new ServerConn reading and writing c.  If r is not
// nil, it is the buffer to use when reading c.
func NewServerConn(c net.Conn, r *bufio.Reader) *ServerConn {
	if r == nil {
		r = bufio.NewReader(c)
	}
	return &ServerConn{c: c, r: r, pipereq: make(map[*Request]uint)}
}

// Hijack detaches the ServerConn and returns the underlying connection as well
// as the read-side bufio which may have some left over data. Hijack may be
// called before Read has signaled the end of the keep-alive logic. The user
// should not call Hijack while Read or Write is in progress.
func (sc *ServerConn) Hijack() (c net.Conn, r *bufio.Reader) {
	sc.lk.Lock()
	defer sc.lk.Unlock()
	c = sc.c
	r = sc.r
	sc.c = nil
	sc.r = nil
	return
}

// Close calls Hijack and then also closes the underlying connection
func (sc *ServerConn) Close() os.Error {
	c, _ := sc.Hijack()
	if c != nil {
		return c.Close()
	}
	return nil
}

// Read returns the next request on the wire. An ErrPersistEOF is returned if
// it is gracefully determined that there are no more requests (e.g. after the
// first request on an HTTP/1.0 connection, or after a Connection:close on a
// HTTP/1.1 connection).
func (sc *ServerConn) Read() (req *Request, err os.Error) {

	// Ensure ordered execution of Reads and Writes
	id := sc.pipe.Next()
	sc.pipe.StartRequest(id)
	defer func() {
		sc.pipe.EndRequest(id)
		if req == nil {
			sc.pipe.StartResponse(id)
			sc.pipe.EndResponse(id)
		} else {
			// Remember the pipeline id of this request
			sc.lk.Lock()
			sc.pipereq[req] = id
			sc.lk.Unlock()
		}
	}()

	sc.lk.Lock()
	if sc.we != nil { // no point receiving if write-side broken or closed
		defer sc.lk.Unlock()
		return nil, sc.we
	}
	if sc.re != nil {
		defer sc.lk.Unlock()
		return nil, sc.re
	}
	if sc.r == nil { // connection closed by user in the meantime
		defer sc.lk.Unlock()
		return nil, os.EBADF
	}
	r := sc.r
	lastbody := sc.lastbody
	sc.lastbody = nil
	sc.lk.Unlock()

	// Make sure body is fully consumed, even if user does not call body.Close
	if lastbody != nil {
		// body.Close is assumed to be idempotent and multiple calls to
		// it should return the error that its first invocation
		// returned.
		err = lastbody.Close()
		if err != nil {
			sc.lk.Lock()
			defer sc.lk.Unlock()
			sc.re = err
			return nil, err
		}
	}

	req, err = ReadRequest(r)
	sc.lk.Lock()
	defer sc.lk.Unlock()
	if err != nil {
		if err == io.ErrUnexpectedEOF {
			// A close from the opposing client is treated as a
			// graceful close, even if there was some unparse-able
			// data before the close.
			sc.re = ErrPersistEOF
			return nil, sc.re
		} else {
			sc.re = err
			return req, err
		}
	}
	sc.lastbody = req.Body
	sc.nread++
	if req.Close {
		sc.re = ErrPersistEOF
		return req, sc.re
	}
	return req, err
}

// Pending returns the number of unanswered requests
// that have been received on the connection.
func (sc *ServerConn) Pending() int {
	sc.lk.Lock()
	defer sc.lk.Unlock()
	return sc.nread - sc.nwritten
}

// Write writes resp in response to req. To close the connection gracefully, set the
// Response.Close field to true. Write should be considered operational until
// it returns an error, regardless of any errors returned on the Read side.
func (sc *ServerConn) Write(req *Request, resp *Response) os.Error {

	// Retrieve the pipeline ID of this request/response pair
	sc.lk.Lock()
	id, ok := sc.pipereq[req]
	sc.pipereq[req] = 0, false
	if !ok {
		sc.lk.Unlock()
		return ErrPipeline
	}
	sc.lk.Unlock()

	// Ensure pipeline order
	sc.pipe.StartResponse(id)
	defer sc.pipe.EndResponse(id)

	sc.lk.Lock()
	if sc.we != nil {
		defer sc.lk.Unlock()
		return sc.we
	}
	if sc.c == nil { // connection closed by user in the meantime
		defer sc.lk.Unlock()
		return os.EBADF
	}
	c := sc.c
	if sc.nread <= sc.nwritten {
		defer sc.lk.Unlock()
		return os.NewError("persist server pipe count")
	}
	if resp.Close {
		// After signaling a keep-alive close, any pipelined unread
		// requests will be lost. It is up to the user to drain them
		// before signaling.
		sc.re = ErrPersistEOF
	}
	sc.lk.Unlock()

	err := resp.Write(c)
	sc.lk.Lock()
	defer sc.lk.Unlock()
	if err != nil {
		sc.we = err
		return err
	}
	sc.nwritten++

	return nil
}

// A ClientConn sends request and receives headers over an underlying
// connection, while respecting the HTTP keepalive logic. ClientConn
// supports hijacking the connection calling Hijack to
// regain control of the underlying net.Conn and deal with it as desired.
type ClientConn struct {
	lk              sync.Mutex // read-write protects the following fields
	c               net.Conn
	r               *bufio.Reader
	re, we          os.Error // read/write errors
	lastbody        io.ReadCloser
	nread, nwritten int
	pipereq         map[*Request]uint

	pipe     textproto.Pipeline
	writeReq func(*Request, io.Writer) os.Error
}

// NewClientConn returns a new ClientConn reading and writing c.  If r is not
// nil, it is the buffer to use when reading c.
func NewClientConn(c net.Conn, r *bufio.Reader) *ClientConn {
	if r == nil {
		r = bufio.NewReader(c)
	}
	return &ClientConn{
		c:        c,
		r:        r,
		pipereq:  make(map[*Request]uint),
		writeReq: (*Request).Write,
	}
}

// NewProxyClientConn works like NewClientConn but writes Requests
// using Request's WriteProxy method.
func NewProxyClientConn(c net.Conn, r *bufio.Reader) *ClientConn {
	cc := NewClientConn(c, r)
	cc.writeReq = (*Request).WriteProxy
	return cc
}

// Hijack detaches the ClientConn and returns the underlying connection as well
// as the read-side bufio which may have some left over data. Hijack may be
// called before the user or Read have signaled the end of the keep-alive
// logic. The user should not call Hijack while Read or Write is in progress.
func (cc *ClientConn) Hijack() (c net.Conn, r *bufio.Reader) {
	cc.lk.Lock()
	defer cc.lk.Unlock()
	c = cc.c
	r = cc.r
	cc.c = nil
	cc.r = nil
	return
}

// Close calls Hijack and then also closes the underlying connection
func (cc *ClientConn) Close() os.Error {
	c, _ := cc.Hijack()
	if c != nil {
		return c.Close()
	}
	return nil
}

// Write writes a request. An ErrPersistEOF error is returned if the connection
// has been closed in an HTTP keepalive sense. If req.Close equals true, the
// keepalive connection is logically closed after this request and the opposing
// server is informed. An ErrUnexpectedEOF indicates the remote closed the
// underlying TCP connection, which is usually considered as graceful close.
func (cc *ClientConn) Write(req *Request) (err os.Error) {

	// Ensure ordered execution of Writes
	id := cc.pipe.Next()
	cc.pipe.StartRequest(id)
	defer func() {
		cc.pipe.EndRequest(id)
		if err != nil {
			cc.pipe.StartResponse(id)
			cc.pipe.EndResponse(id)
		} else {
			// Remember the pipeline id of this request
			cc.lk.Lock()
			cc.pipereq[req] = id
			cc.lk.Unlock()
		}
	}()

	cc.lk.Lock()
	if cc.re != nil { // no point sending if read-side closed or broken
		defer cc.lk.Unlock()
		return cc.re
	}
	if cc.we != nil {
		defer cc.lk.Unlock()
		return cc.we
	}
	if cc.c == nil { // connection closed by user in the meantime
		defer cc.lk.Unlock()
		return os.EBADF
	}
	c := cc.c
	if req.Close {
		// We write the EOF to the write-side error, because there
		// still might be some pipelined reads
		cc.we = ErrPersistEOF
	}
	cc.lk.Unlock()

	err = cc.writeReq(req, c)
	cc.lk.Lock()
	defer cc.lk.Unlock()
	if err != nil {
		cc.we = err
		return err
	}
	cc.nwritten++

	return nil
}

// Pending returns the number of unanswered requests
// that have been sent on the connection.
func (cc *ClientConn) Pending() int {
	cc.lk.Lock()
	defer cc.lk.Unlock()
	return cc.nwritten - cc.nread
}

// Read reads the next response from the wire. A valid response might be
// returned together with an ErrPersistEOF, which means that the remote
// requested that this be the last request serviced. Read can be called
// concurrently with Write, but not with another Read.
func (cc *ClientConn) Read(req *Request) (*Response, os.Error) {
	return cc.readUsing(req, ReadResponse)
}

// readUsing is the implementation of Read with a replaceable
// ReadResponse-like function, used by the Transport.
func (cc *ClientConn) readUsing(req *Request, readRes func(*bufio.Reader, *Request) (*Response, os.Error)) (resp *Response, err os.Error) {
	// Retrieve the pipeline ID of this request/response pair
	cc.lk.Lock()
	id, ok := cc.pipereq[req]
	cc.pipereq[req] = 0, false
	if !ok {
		cc.lk.Unlock()
		return nil, ErrPipeline
	}
	cc.lk.Unlock()

	// Ensure pipeline order
	cc.pipe.StartResponse(id)
	defer cc.pipe.EndResponse(id)

	cc.lk.Lock()
	if cc.re != nil {
		defer cc.lk.Unlock()
		return nil, cc.re
	}
	if cc.r == nil { // connection closed by user in the meantime
		defer cc.lk.Unlock()
		return nil, os.EBADF
	}
	r := cc.r
	lastbody := cc.lastbody
	cc.lastbody = nil
	cc.lk.Unlock()

	// Make sure body is fully consumed, even if user does not call body.Close
	if lastbody != nil {
		// body.Close is assumed to be idempotent and multiple calls to
		// it should return the error that its first invokation
		// returned.
		err = lastbody.Close()
		if err != nil {
			cc.lk.Lock()
			defer cc.lk.Unlock()
			cc.re = err
			return nil, err
		}
	}

	resp, err = readRes(r, req)
	cc.lk.Lock()
	defer cc.lk.Unlock()
	if err != nil {
		cc.re = err
		return resp, err
	}
	cc.lastbody = resp.Body

	cc.nread++

	if resp.Close {
		cc.re = ErrPersistEOF // don't send any more requests
		return resp, cc.re
	}
	return resp, err
}

// Do is convenience method that writes a request and reads a response.
func (cc *ClientConn) Do(req *Request) (resp *Response, err os.Error) {
	err = cc.Write(req)
	if err != nil {
		return
	}
	return cc.Read(req)
}
<commit_msg>http: document that ServerConn and ClientConn are low-level<commit_after>// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package http

import (
	"bufio"
	"io"
	"net"
	"net/textproto"
	"os"
	"sync"
)

var (
	ErrPersistEOF = &ProtocolError{"persistent connection closed"}
	ErrPipeline   = &ProtocolError{"pipeline error"}
)

// A ServerConn reads requests and sends responses over an underlying
// connection, until the HTTP keepalive logic commands an end. ServerConn
// also allows hijacking the underlying connection by calling Hijack
// to regain control over the connection. ServerConn supports pipe-lining,
// i.e. requests can be read out of sync (but in the same order) while the
// respective responses are sent.
//
// ServerConn is low-level and should not be needed by most applications.
// See Server.
type ServerConn struct {
	lk              sync.Mutex // read-write protects the following fields
	c               net.Conn
	r               *bufio.Reader
	re, we          os.Error // read/write errors
	lastbody        io.ReadCloser
	nread, nwritten int
	pipereq         map[*Request]uint

	pipe textproto.Pipeline
}

// NewServerConn returns a new ServerConn reading and writing c.  If r is not
// nil, it is the buffer to use when reading c.
func NewServerConn(c net.Conn, r *bufio.Reader) *ServerConn {
	if r == nil {
		r = bufio.NewReader(c)
	}
	return &ServerConn{c: c, r: r, pipereq: make(map[*Request]uint)}
}

// Hijack detaches the ServerConn and returns the underlying connection as well
// as the read-side bufio which may have some left over data. Hijack may be
// called before Read has signaled the end of the keep-alive logic. The user
// should not call Hijack while Read or Write is in progress.
func (sc *ServerConn) Hijack() (c net.Conn, r *bufio.Reader) {
	sc.lk.Lock()
	defer sc.lk.Unlock()
	c = sc.c
	r = sc.r
	sc.c = nil
	sc.r = nil
	return
}

// Close calls Hijack and then also closes the underlying connection
func (sc *ServerConn) Close() os.Error {
	c, _ := sc.Hijack()
	if c != nil {
		return c.Close()
	}
	return nil
}

// Read returns the next request on the wire. An ErrPersistEOF is returned if
// it is gracefully determined that there are no more requests (e.g. after the
// first request on an HTTP/1.0 connection, or after a Connection:close on a
// HTTP/1.1 connection).
func (sc *ServerConn) Read() (req *Request, err os.Error) {

	// Ensure ordered execution of Reads and Writes
	id := sc.pipe.Next()
	sc.pipe.StartRequest(id)
	defer func() {
		sc.pipe.EndRequest(id)
		if req == nil {
			sc.pipe.StartResponse(id)
			sc.pipe.EndResponse(id)
		} else {
			// Remember the pipeline id of this request
			sc.lk.Lock()
			sc.pipereq[req] = id
			sc.lk.Unlock()
		}
	}()

	sc.lk.Lock()
	if sc.we != nil { // no point receiving if write-side broken or closed
		defer sc.lk.Unlock()
		return nil, sc.we
	}
	if sc.re != nil {
		defer sc.lk.Unlock()
		return nil, sc.re
	}
	if sc.r == nil { // connection closed by user in the meantime
		defer sc.lk.Unlock()
		return nil, os.EBADF
	}
	r := sc.r
	lastbody := sc.lastbody
	sc.lastbody = nil
	sc.lk.Unlock()

	// Make sure body is fully consumed, even if user does not call body.Close
	if lastbody != nil {
		// body.Close is assumed to be idempotent and multiple calls to
		// it should return the error that its first invocation
		// returned.
		err = lastbody.Close()
		if err != nil {
			sc.lk.Lock()
			defer sc.lk.Unlock()
			sc.re = err
			return nil, err
		}
	}

	req, err = ReadRequest(r)
	sc.lk.Lock()
	defer sc.lk.Unlock()
	if err != nil {
		if err == io.ErrUnexpectedEOF {
			// A close from the opposing client is treated as a
			// graceful close, even if there was some unparse-able
			// data before the close.
			sc.re = ErrPersistEOF
			return nil, sc.re
		} else {
			sc.re = err
			return req, err
		}
	}
	sc.lastbody = req.Body
	sc.nread++
	if req.Close {
		sc.re = ErrPersistEOF
		return req, sc.re
	}
	return req, err
}

// Pending returns the number of unanswered requests
// that have been received on the connection.
func (sc *ServerConn) Pending() int {
	sc.lk.Lock()
	defer sc.lk.Unlock()
	return sc.nread - sc.nwritten
}

// Write writes resp in response to req. To close the connection gracefully, set the
// Response.Close field to true. Write should be considered operational until
// it returns an error, regardless of any errors returned on the Read side.
func (sc *ServerConn) Write(req *Request, resp *Response) os.Error {

	// Retrieve the pipeline ID of this request/response pair
	sc.lk.Lock()
	id, ok := sc.pipereq[req]
	sc.pipereq[req] = 0, false
	if !ok {
		sc.lk.Unlock()
		return ErrPipeline
	}
	sc.lk.Unlock()

	// Ensure pipeline order
	sc.pipe.StartResponse(id)
	defer sc.pipe.EndResponse(id)

	sc.lk.Lock()
	if sc.we != nil {
		defer sc.lk.Unlock()
		return sc.we
	}
	if sc.c == nil { // connection closed by user in the meantime
		defer sc.lk.Unlock()
		return os.EBADF
	}
	c := sc.c
	if sc.nread <= sc.nwritten {
		defer sc.lk.Unlock()
		return os.NewError("persist server pipe count")
	}
	if resp.Close {
		// After signaling a keep-alive close, any pipelined unread
		// requests will be lost. It is up to the user to drain them
		// before signaling.
		sc.re = ErrPersistEOF
	}
	sc.lk.Unlock()

	err := resp.Write(c)
	sc.lk.Lock()
	defer sc.lk.Unlock()
	if err != nil {
		sc.we = err
		return err
	}
	sc.nwritten++

	return nil
}

// A ClientConn sends request and receives headers over an underlying
// connection, while respecting the HTTP keepalive logic. ClientConn
// supports hijacking the connection calling Hijack to
// regain control of the underlying net.Conn and deal with it as desired.
//
// ClientConn is low-level and should not be needed by most applications.
// See Client.
type ClientConn struct {
	lk              sync.Mutex // read-write protects the following fields
	c               net.Conn
	r               *bufio.Reader
	re, we          os.Error // read/write errors
	lastbody        io.ReadCloser
	nread, nwritten int
	pipereq         map[*Request]uint

	pipe     textproto.Pipeline
	writeReq func(*Request, io.Writer) os.Error
}

// NewClientConn returns a new ClientConn reading and writing c.  If r is not
// nil, it is the buffer to use when reading c.
func NewClientConn(c net.Conn, r *bufio.Reader) *ClientConn {
	if r == nil {
		r = bufio.NewReader(c)
	}
	return &ClientConn{
		c:        c,
		r:        r,
		pipereq:  make(map[*Request]uint),
		writeReq: (*Request).Write,
	}
}

// NewProxyClientConn works like NewClientConn but writes Requests
// using Request's WriteProxy method.
func NewProxyClientConn(c net.Conn, r *bufio.Reader) *ClientConn {
	cc := NewClientConn(c, r)
	cc.writeReq = (*Request).WriteProxy
	return cc
}

// Hijack detaches the ClientConn and returns the underlying connection as well
// as the read-side bufio which may have some left over data. Hijack may be
// called before the user or Read have signaled the end of the keep-alive
// logic. The user should not call Hijack while Read or Write is in progress.
func (cc *ClientConn) Hijack() (c net.Conn, r *bufio.Reader) {
	cc.lk.Lock()
	defer cc.lk.Unlock()
	c = cc.c
	r = cc.r
	cc.c = nil
	cc.r = nil
	return
}

// Close calls Hijack and then also closes the underlying connection
func (cc *ClientConn) Close() os.Error {
	c, _ := cc.Hijack()
	if c != nil {
		return c.Close()
	}
	return nil
}

// Write writes a request. An ErrPersistEOF error is returned if the connection
// has been closed in an HTTP keepalive sense. If req.Close equals true, the
// keepalive connection is logically closed after this request and the opposing
// server is informed. An ErrUnexpectedEOF indicates the remote closed the
// underlying TCP connection, which is usually considered as graceful close.
func (cc *ClientConn) Write(req *Request) (err os.Error) {

	// Ensure ordered execution of Writes
	id := cc.pipe.Next()
	cc.pipe.StartRequest(id)
	defer func() {
		cc.pipe.EndRequest(id)
		if err != nil {
			cc.pipe.StartResponse(id)
			cc.pipe.EndResponse(id)
		} else {
			// Remember the pipeline id of this request
			cc.lk.Lock()
			cc.pipereq[req] = id
			cc.lk.Unlock()
		}
	}()

	cc.lk.Lock()
	if cc.re != nil { // no point sending if read-side closed or broken
		defer cc.lk.Unlock()
		return cc.re
	}
	if cc.we != nil {
		defer cc.lk.Unlock()
		return cc.we
	}
	if cc.c == nil { // connection closed by user in the meantime
		defer cc.lk.Unlock()
		return os.EBADF
	}
	c := cc.c
	if req.Close {
		// We write the EOF to the write-side error, because there
		// still might be some pipelined reads
		cc.we = ErrPersistEOF
	}
	cc.lk.Unlock()

	err = cc.writeReq(req, c)
	cc.lk.Lock()
	defer cc.lk.Unlock()
	if err != nil {
		cc.we = err
		return err
	}
	cc.nwritten++

	return nil
}

// Pending returns the number of unanswered requests
// that have been sent on the connection.
func (cc *ClientConn) Pending() int {
	cc.lk.Lock()
	defer cc.lk.Unlock()
	return cc.nwritten - cc.nread
}

// Read reads the next response from the wire. A valid response might be
// returned together with an ErrPersistEOF, which means that the remote
// requested that this be the last request serviced. Read can be called
// concurrently with Write, but not with another Read.
func (cc *ClientConn) Read(req *Request) (*Response, os.Error) {
	return cc.readUsing(req, ReadResponse)
}

// readUsing is the implementation of Read with a replaceable
// ReadResponse-like function, used by the Transport.
func (cc *ClientConn) readUsing(req *Request, readRes func(*bufio.Reader, *Request) (*Response, os.Error)) (resp *Response, err os.Error) {
	// Retrieve the pipeline ID of this request/response pair
	cc.lk.Lock()
	id, ok := cc.pipereq[req]
	cc.pipereq[req] = 0, false
	if !ok {
		cc.lk.Unlock()
		return nil, ErrPipeline
	}
	cc.lk.Unlock()

	// Ensure pipeline order
	cc.pipe.StartResponse(id)
	defer cc.pipe.EndResponse(id)

	cc.lk.Lock()
	if cc.re != nil {
		defer cc.lk.Unlock()
		return nil, cc.re
	}
	if cc.r == nil { // connection closed by user in the meantime
		defer cc.lk.Unlock()
		return nil, os.EBADF
	}
	r := cc.r
	lastbody := cc.lastbody
	cc.lastbody = nil
	cc.lk.Unlock()

	// Make sure body is fully consumed, even if user does not call body.Close
	if lastbody != nil {
		// body.Close is assumed to be idempotent and multiple calls to
		// it should return the error that its first invokation
		// returned.
		err = lastbody.Close()
		if err != nil {
			cc.lk.Lock()
			defer cc.lk.Unlock()
			cc.re = err
			return nil, err
		}
	}

	resp, err = readRes(r, req)
	cc.lk.Lock()
	defer cc.lk.Unlock()
	if err != nil {
		cc.re = err
		return resp, err
	}
	cc.lastbody = resp.Body

	cc.nread++

	if resp.Close {
		cc.re = ErrPersistEOF // don't send any more requests
		return resp, cc.re
	}
	return resp, err
}

// Do is convenience method that writes a request and reads a response.
func (cc *ClientConn) Do(req *Request) (resp *Response, err os.Error) {
	err = cc.Write(req)
	if err != nil {
		return
	}
	return cc.Read(req)
}
<|endoftext|>


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

package net

import (
	"regexp";
	"testing";
)

type DialErrorTest struct {
	Net	string;
	Laddr	string;
	Raddr	string;
	Pattern	string;
}

var dialErrorTests = []DialErrorTest{
	DialErrorTest{
		"datakit", "", "mh/astro/r70",
		"dial datakit mh/astro/r70: unknown network datakit",
	},
	DialErrorTest{
		"tcp", "", "127.0.0.1:☺",
		"dial tcp 127.0.0.1:☺: unknown port tcp/☺",
	},
	DialErrorTest{
		"tcp", "", "no-such-name.google.com.:80",
		"dial tcp no-such-name.google.com.:80: lookup no-such-name.google.com.( on .*)?: no (.*)",
	},
	DialErrorTest{
		"tcp", "", "no-such-name.no-such-top-level-domain.:80",
		"dial tcp no-such-name.no-such-top-level-domain.:80: lookup no-such-name.no-such-top-level-domain.( on .*)?: no (.*)",
	},
	DialErrorTest{
		"tcp", "", "no-such-name:80",
		`dial tcp no-such-name:80: lookup no-such-name\.(.*\.)?( on .*)?: no (.*)`,
	},
	DialErrorTest{
		"tcp", "", "mh/astro/r70:http",
		"dial tcp mh/astro/r70:http: lookup mh/astro/r70: invalid domain name",
	},
	DialErrorTest{
		"unix", "", "/etc/file-not-found",
		"dial unix /etc/file-not-found: no such file or directory",
	},
	DialErrorTest{
		"unix", "", "/etc/",
		"dial unix /etc/: (permission denied|socket operation on non-socket)",
	},
}

func TestDialError(t *testing.T) {
	for i, tt := range dialErrorTests {
		c, e := Dial(tt.Net, tt.Laddr, tt.Raddr);
		if c != nil {
			c.Close()
		}
		if e == nil {
			t.Errorf("#%d: nil error, want match for %#q", i, tt.Pattern);
			continue;
		}
		s := e.String();
		match, _ := regexp.MatchString(tt.Pattern, s);
		if !match {
			t.Errorf("#%d: %q, want match for %#q", i, s, tt.Pattern)
		}
	}
}
<commit_msg>net: disable dns error test<commit_after>// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package net

import (
	"flag";
	"regexp";
	"testing";
)

var runErrorTest = flag.Bool("run_error_test", false, "let TestDialError check for dns errors")

type DialErrorTest struct {
	Net	string;
	Laddr	string;
	Raddr	string;
	Pattern	string;
}

var dialErrorTests = []DialErrorTest{
	DialErrorTest{
		"datakit", "", "mh/astro/r70",
		"dial datakit mh/astro/r70: unknown network datakit",
	},
	DialErrorTest{
		"tcp", "", "127.0.0.1:☺",
		"dial tcp 127.0.0.1:☺: unknown port tcp/☺",
	},
	DialErrorTest{
		"tcp", "", "no-such-name.google.com.:80",
		"dial tcp no-such-name.google.com.:80: lookup no-such-name.google.com.( on .*)?: no (.*)",
	},
	DialErrorTest{
		"tcp", "", "no-such-name.no-such-top-level-domain.:80",
		"dial tcp no-such-name.no-such-top-level-domain.:80: lookup no-such-name.no-such-top-level-domain.( on .*)?: no (.*)",
	},
	DialErrorTest{
		"tcp", "", "no-such-name:80",
		`dial tcp no-such-name:80: lookup no-such-name\.(.*\.)?( on .*)?: no (.*)`,
	},
	DialErrorTest{
		"tcp", "", "mh/astro/r70:http",
		"dial tcp mh/astro/r70:http: lookup mh/astro/r70: invalid domain name",
	},
	DialErrorTest{
		"unix", "", "/etc/file-not-found",
		"dial unix /etc/file-not-found: no such file or directory",
	},
	DialErrorTest{
		"unix", "", "/etc/",
		"dial unix /etc/: (permission denied|socket operation on non-socket)",
	},
}

func TestDialError(t *testing.T) {
	if !*runErrorTest {
		t.Logf("test disabled; use --run_error_test to enable");
		return;
	}
	for i, tt := range dialErrorTests {
		c, e := Dial(tt.Net, tt.Laddr, tt.Raddr);
		if c != nil {
			c.Close()
		}
		if e == nil {
			t.Errorf("#%d: nil error, want match for %#q", i, tt.Pattern);
			continue;
		}
		s := e.String();
		match, _ := regexp.MatchString(tt.Pattern, s);
		if !match {
			t.Errorf("#%d: %q, want match for %#q", i, s, tt.Pattern)
		}
	}
}
<|endoftext|>


<commit_before>package parse

import (
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"strings"

	"gopkg.in/yaml.v2"

	"github.com/pachyderm/pachyderm/src/pps"
)

var (
	versionToParseFunc = map[string]func(string, *config) (*pps.Pipeline, error){
		"v1": parsePipelineV1,
	}
)

type parser struct{}

func newParser() *parser {
	return &parser{}
}

func (p *parser) ParsePipeline(dirPath string) (*pps.Pipeline, error) {
	return parsePipeline(dirPath)
}

type config struct {
	Version string
	Include []string
}

func parsePipeline(dirPath string) (*pps.Pipeline, error) {
	dirPath = filepath.Clean(dirPath)
	config, err := parseConfig(dirPath)
	if err != nil {
		return nil, err
	}
	if config.Version == "" {
		return nil, fmt.Errorf("no version specified in pps.yml")
	}
	parseFunc, ok := versionToParseFunc[config.Version]
	if !ok {
		return nil, fmt.Errorf("unknown pps specification version: %s", config.Version)
	}
	return parseFunc(dirPath, config)
}

func parseConfig(dirPath string) (*config, error) {
	configFilePath := filepath.Join(dirPath, "pps.yml")
	if err := checkFileExists(configFilePath); err != nil {
		return nil, err
	}
	data, err := ioutil.ReadFile(configFilePath)
	if err != nil {
		return nil, err
	}
	config := &config{}
	if err := yaml.Unmarshal(data, config); err != nil {
		return nil, err
	}
	return config, nil
}

func parsePipelineV1(dirPath string, config *config) (*pps.Pipeline, error) {
	filePaths := make([]string, len(config.Include))
	for i, include := range config.Include {
		filePaths[i] = filepath.Join(dirPath, include)
	}
	pipeline := &pps.Pipeline{
		NameToNode:          make(map[string]*pps.Node),
		NameToDockerService: make(map[string]*pps.DockerService),
	}
	names := make(map[string]bool)
	for _, filePath := range filePaths {
		data, err := ioutil.ReadFile(filePath)
		if err != nil {
			return nil, err
		}
		m := make(map[interface{}]interface{})
		if err := yaml.Unmarshal(data, &m); err != nil {
			return nil, err
		}
		ppsMetaObj, ok := m["pps"]
		if !ok {
			return nil, fmt.Errorf("no pps section for %s", filePath)
		}
		ppsMeta := ppsMetaObj.(map[interface{}]interface{})
		if ppsMeta["kind"] == "" {
			return nil, fmt.Errorf("no kind specified for %s", filePath)
		}
		nameObj, ok := ppsMeta["name"]
		if !ok {
			return nil, fmt.Errorf("no name specified for %s", filePath)
		}
		name := strings.TrimSpace(nameObj.(string))
		if name == "" {
			return nil, fmt.Errorf("no name specified for %s", filePath)
		}
		if _, ok := names[name]; ok {
			return nil, fmt.Errorf("duplicate element: %s", name)
		}
		kindObj, ok := ppsMeta["kind"]
		if !ok {
			return nil, fmt.Errorf("no kind specified for %s", filePath)
		}
		kind := strings.TrimSpace(kindObj.(string))
		switch kind {
		case "node":
			node := &pps.Node{}
			if err := yaml.Unmarshal(data, node); err != nil {
				return nil, err
			}
			pipeline.NameToNode[name] = node
		case "docker_service":
			dockerService := &pps.DockerService{}
			if err := yaml.Unmarshal(data, dockerService); err != nil {
				return nil, err
			}
			if dockerService.Build != "" {
				dockerService.Build = filepath.Clean(filepath.Join(filepath.Dir(filePath), dockerService.Build))
			}
			pipeline.NameToDockerService[name] = dockerService
		default:
			return nil, fmt.Errorf("unknown kind %s for %s", kind, filePath)
		}
	}
	return pipeline, nil
}

func getElementForPipelineFile(dirPath string, filePath string) (*pps.Element, error) {
	filePath := filepath.Join(dirPath, filePath)
	data, err := ioutil.ReadFile(filePath)
	if err != nil {
		return nil, err
	}
	m := make(map[interface{}]interface{})
	if err := yaml.Unmarshal(data, &m); err != nil {
		return nil, err
	}
	ppsMetaObj, ok := m["pps"]
	if !ok {
		return nil, fmt.Errorf("no pps section for %s", filePath)
	}
	ppsMeta := ppsMetaObj.(map[interface{}]interface{})
	if ppsMeta["kind"] == "" {
		return nil, fmt.Errorf("no kind specified for %s", filePath)
	}
	nameObj, ok := ppsMeta["name"]
	if !ok {
		return nil, fmt.Errorf("no name specified for %s", filePath)
	}
	name := strings.TrimSpace(nameObj.(string))
	if name == "" {
		return nil, fmt.Errorf("no name specified for %s", filePath)
	}
	kindObj, ok := ppsMeta["kind"]
	if !ok {
		return nil, fmt.Errorf("no kind specified for %s", filePath)
	}
	kind := strings.TrimSpace(kindObj.(string))
	switch kind {
	case "node":
		node := &pps.Node{}
		if err := yaml.Unmarshal(data, node); err != nil {
			return nil, err
		}
		element.Node = node
	case "docker_service":
		dockerService := &pps.DockerService{}
		if err := yaml.Unmarshal(data, dockerService); err != nil {
			return nil, err
		}
		if dockerService.Build != "" {
			dockerService.Build = filepath.Clean(filepath.Join(filepath.Dir(filePath), dockerService.Build))
		}
		element.DockerService = dockerService
	default:
		return nil, fmt.Errorf("unknown kind %s for %s", kind, filePath)
	}
	return element, nil
}

func checkFileExists(path string) error {
	_, err := os.Stat(path)
	return err
}
<commit_msg>fix compile for src/pps/parse<commit_after>package parse

import (
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"strings"

	"gopkg.in/yaml.v2"

	"github.com/pachyderm/pachyderm/src/pps"
)

var (
	versionToParseFunc = map[string]func(string, *config) (*pps.Pipeline, error){
		"v1": parsePipelineV1,
	}
)

type parser struct{}

func newParser() *parser {
	return &parser{}
}

func (p *parser) ParsePipeline(dirPath string) (*pps.Pipeline, error) {
	return parsePipeline(dirPath)
}

type config struct {
	Version string
	Include []string
}

func parsePipeline(dirPath string) (*pps.Pipeline, error) {
	dirPath = filepath.Clean(dirPath)
	config, err := parseConfig(dirPath)
	if err != nil {
		return nil, err
	}
	if config.Version == "" {
		return nil, fmt.Errorf("no version specified in pps.yml")
	}
	parseFunc, ok := versionToParseFunc[config.Version]
	if !ok {
		return nil, fmt.Errorf("unknown pps specification version: %s", config.Version)
	}
	return parseFunc(dirPath, config)
}

func parseConfig(dirPath string) (*config, error) {
	configFilePath := filepath.Join(dirPath, "pps.yml")
	if err := checkFileExists(configFilePath); err != nil {
		return nil, err
	}
	data, err := ioutil.ReadFile(configFilePath)
	if err != nil {
		return nil, err
	}
	config := &config{}
	if err := yaml.Unmarshal(data, config); err != nil {
		return nil, err
	}
	return config, nil
}

func parsePipelineV1(dirPath string, config *config) (*pps.Pipeline, error) {
	filePaths := make([]string, len(config.Include))
	for i, include := range config.Include {
		filePaths[i] = filepath.Join(dirPath, include)
	}
	pipeline := &pps.Pipeline{
		NameToNode:          make(map[string]*pps.Node),
		NameToDockerService: make(map[string]*pps.DockerService),
	}
	names := make(map[string]bool)
	for _, filePath := range filePaths {
		data, err := ioutil.ReadFile(filePath)
		if err != nil {
			return nil, err
		}
		m := make(map[interface{}]interface{})
		if err := yaml.Unmarshal(data, &m); err != nil {
			return nil, err
		}
		ppsMetaObj, ok := m["pps"]
		if !ok {
			return nil, fmt.Errorf("no pps section for %s", filePath)
		}
		ppsMeta := ppsMetaObj.(map[interface{}]interface{})
		if ppsMeta["kind"] == "" {
			return nil, fmt.Errorf("no kind specified for %s", filePath)
		}
		nameObj, ok := ppsMeta["name"]
		if !ok {
			return nil, fmt.Errorf("no name specified for %s", filePath)
		}
		name := strings.TrimSpace(nameObj.(string))
		if name == "" {
			return nil, fmt.Errorf("no name specified for %s", filePath)
		}
		if _, ok := names[name]; ok {
			return nil, fmt.Errorf("duplicate element: %s", name)
		}
		kindObj, ok := ppsMeta["kind"]
		if !ok {
			return nil, fmt.Errorf("no kind specified for %s", filePath)
		}
		kind := strings.TrimSpace(kindObj.(string))
		switch kind {
		case "node":
			node := &pps.Node{}
			if err := yaml.Unmarshal(data, node); err != nil {
				return nil, err
			}
			pipeline.NameToNode[name] = node
		case "docker_service":
			dockerService := &pps.DockerService{}
			if err := yaml.Unmarshal(data, dockerService); err != nil {
				return nil, err
			}
			if dockerService.Build != "" {
				dockerService.Build = filepath.Clean(filepath.Join(filepath.Dir(filePath), dockerService.Build))
			}
			pipeline.NameToDockerService[name] = dockerService
		default:
			return nil, fmt.Errorf("unknown kind %s for %s", kind, filePath)
		}
	}
	return pipeline, nil
}

func checkFileExists(path string) error {
	_, err := os.Stat(path)
	return err
}
<|endoftext|>


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

package gles

import (
	"context"

	"github.com/google/gapid/gapis/resolve"
	"github.com/google/gapid/gapis/service/path"
)

// instances returns the path to the Instances field of the currently bound
// context, and the context at p.
func instances(ctx context.Context, p path.Node) (*path.Field, *Context, error) {
	if cmdPath := path.FindCommand(p); cmdPath != nil {
		stateObj, err := resolve.APIState(ctx, cmdPath.StateAfter())
		if err != nil {
			return nil, nil, err
		}
		state := stateObj.(*State)
		context, ok := state.Contexts[state.CurrentThread]
		if !ok {
			return nil, nil, nil
		}
		return cmdPath.StateAfter().
			Field("Contexts").
			MapIndex(uint64(state.CurrentThread)).
			Field("Instances"), context, nil
	}
	return nil, nil, nil
}

// Link returns the link to the attribute vertex array in the state block.
// If nil, nil is returned then the path cannot be followed.
func (o AttributeLocation) Link(ctx context.Context, p path.Node) (path.Node, error) {
	i, c, err := instances(ctx, p)
	if i == nil {
		return nil, err
	}
	va, ok := c.Objects.VertexArrays[c.BoundVertexArray]
	if !ok || !va.VertexAttributeArrays.Contains(o) {
		return nil, nil
	}
	return i.
		Field("VertexArrays").
		MapIndex(uint64(c.BoundVertexArray)).
		Field("VertexAttributeArrays").
		MapIndex(uint64(o)), nil
}

// Link returns the link to the buffer object in the state block.
// If nil, nil is returned then the path cannot be followed.
func (o BufferId) Link(ctx context.Context, p path.Node) (path.Node, error) {
	i, c, err := instances(ctx, p)
	if i == nil || !c.SharedObjects.Buffers.Contains(o) {
		return nil, err
	}
	return i.Field("Buffers").MapIndex(int64(o)), nil
}

// Link returns the link to the framebuffer object in the state block.
// If nil, nil is returned then the path cannot be followed.
func (o FramebufferId) Link(ctx context.Context, p path.Node) (path.Node, error) {
	i, c, err := instances(ctx, p)
	if i == nil || !c.Objects.Framebuffers.Contains(o) {
		return nil, err
	}
	return i.Field("Framebuffers").MapIndex(int64(o)), nil
}

// Link returns the link to the program in the state block.
// If nil, nil is returned then the path cannot be followed.
func (o ProgramId) Link(ctx context.Context, p path.Node) (path.Node, error) {
	i, c, err := instances(ctx, p)
	if i == nil || !c.SharedObjects.Programs.Contains(o) {
		return nil, err
	}
	return i.Field("Programs").MapIndex(int64(o)), nil
}

// Link returns the link to the query object in the state block.
// If nil, nil is returned then the path cannot be followed.
func (o QueryId) Link(ctx context.Context, p path.Node) (path.Node, error) {
	i, c, err := instances(ctx, p)
	if i == nil || !c.Objects.Queries.Contains(o) {
		return nil, err
	}
	return i.Field("Queries").MapIndex(int64(o)), nil
}

// Link returns the link to the renderbuffer object in the state block.
// If nil, nil is returned then the path cannot be followed.
func (o RenderbufferId) Link(ctx context.Context, p path.Node) (path.Node, error) {
	i, c, err := instances(ctx, p)
	if i == nil || !c.SharedObjects.Renderbuffers.Contains(o) {
		return nil, err
	}
	return i.Field("Renderbuffers").MapIndex(int64(o)), nil
}

// Link returns the link to the shader object in the state block.
// If nil, nil is returned then the path cannot be followed.
func (o ShaderId) Link(ctx context.Context, p path.Node) (path.Node, error) {
	i, c, err := instances(ctx, p)
	if i == nil || !c.SharedObjects.Shaders.Contains(o) {
		return nil, err
	}
	return i.Field("Shaders").MapIndex(int64(o)), nil
}

// Link returns the link to the texture object in the state block.
// If nil, nil is returned then the path cannot be followed.
func (o TextureId) Link(ctx context.Context, p path.Node) (path.Node, error) {
	i, c, err := instances(ctx, p)
	if i == nil || !c.SharedObjects.Textures.Contains(o) {
		return nil, err
	}
	return i.Field("Textures").MapIndex(int64(o)), nil
}

// Link returns the link to the uniform in the state block.
// If nil, nil is returned then the path cannot be followed.
func (o UniformLocation) Link(ctx context.Context, p path.Node) (path.Node, error) {
	i, c, err := instances(ctx, p)
	if i == nil {
		return nil, err
	}

	atom, err := resolve.Command(ctx, path.FindCommand(p))
	if err != nil {
		return nil, err
	}

	var program ProgramId
	switch atom := atom.(type) {
	case *GlGetActiveUniform:
		program = atom.Program
	case *GlGetUniformLocation:
		program = atom.Program
	default:
		program = c.BoundProgram
	}

	prog, ok := c.SharedObjects.Programs[program]
	if !ok || !prog.Uniforms.Contains(o) {
		return nil, nil
	}

	return i.
		Field("Programs").
		MapIndex(int64(program)).
		Field("Uniforms").
		MapIndex(int64(o)), nil
}

// Link returns the link to the vertex array in the state block.
// If nil, nil is returned then the path cannot be followed.
func (o VertexArrayId) Link(ctx context.Context, p path.Node) (path.Node, error) {
	i, c, err := instances(ctx, p)
	if i == nil || !c.Objects.VertexArrays.Contains(o) {
		return nil, err
	}
	return i.Field("VertexArrays").MapIndex(int64(o)), nil
}
<commit_msg>Fix GL object links<commit_after>// Copyright (C) 2017 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package gles

import (
	"context"

	"github.com/google/gapid/gapis/resolve"
	"github.com/google/gapid/gapis/service/path"
)

// objects returns the path to the Objects field of the currently bound
// context, and the context at p.
func objects(ctx context.Context, p path.Node) (*path.Field, *Context, error) {
	if cmdPath := path.FindCommand(p); cmdPath != nil {
		stateObj, err := resolve.APIState(ctx, cmdPath.StateAfter())
		if err != nil {
			return nil, nil, err
		}
		state := stateObj.(*State)
		context, ok := state.Contexts[state.CurrentThread]
		if !ok {
			return nil, nil, nil
		}
		return cmdPath.StateAfter().
			Field("Contexts").
			MapIndex(uint64(state.CurrentThread)).
			Field("Objects"), context, nil
	}
	return nil, nil, nil
}

// sharedObjects returns the path to the SharedObjects field of the currently bound
// context, and the context at p.
func sharedObjects(ctx context.Context, p path.Node) (*path.Field, *Context, error) {
	if cmdPath := path.FindCommand(p); cmdPath != nil {
		stateObj, err := resolve.APIState(ctx, cmdPath.StateAfter())
		if err != nil {
			return nil, nil, err
		}
		state := stateObj.(*State)
		context, ok := state.Contexts[state.CurrentThread]
		if !ok {
			return nil, nil, nil
		}
		return cmdPath.StateAfter().
			Field("Contexts").
			MapIndex(uint64(state.CurrentThread)).
			Field("SharedObjects"), context, nil
	}
	return nil, nil, nil
}

// Link returns the link to the attribute vertex array in the state block.
// If nil, nil is returned then the path cannot be followed.
func (o AttributeLocation) Link(ctx context.Context, p path.Node) (path.Node, error) {
	i, c, err := objects(ctx, p)
	if i == nil {
		return nil, err
	}
	va, ok := c.Objects.VertexArrays[c.BoundVertexArray]
	if !ok || !va.VertexAttributeArrays.Contains(o) {
		return nil, nil
	}
	return i.
		Field("VertexArrays").
		MapIndex(uint64(c.BoundVertexArray)).
		Field("VertexAttributeArrays").
		MapIndex(uint64(o)), nil
}

// Link returns the link to the buffer object in the state block.
// If nil, nil is returned then the path cannot be followed.
func (o BufferId) Link(ctx context.Context, p path.Node) (path.Node, error) {
	i, c, err := sharedObjects(ctx, p)
	if i == nil || !c.SharedObjects.Buffers.Contains(o) {
		return nil, err
	}
	return i.Field("Buffers").MapIndex(int64(o)), nil
}

// Link returns the link to the framebuffer object in the state block.
// If nil, nil is returned then the path cannot be followed.
func (o FramebufferId) Link(ctx context.Context, p path.Node) (path.Node, error) {
	i, c, err := objects(ctx, p)
	if i == nil || !c.Objects.Framebuffers.Contains(o) {
		return nil, err
	}
	return i.Field("Framebuffers").MapIndex(int64(o)), nil
}

// Link returns the link to the program in the state block.
// If nil, nil is returned then the path cannot be followed.
func (o ProgramId) Link(ctx context.Context, p path.Node) (path.Node, error) {
	i, c, err := sharedObjects(ctx, p)
	if i == nil || !c.SharedObjects.Programs.Contains(o) {
		return nil, err
	}
	return i.Field("Programs").MapIndex(int64(o)), nil
}

// Link returns the link to the query object in the state block.
// If nil, nil is returned then the path cannot be followed.
func (o QueryId) Link(ctx context.Context, p path.Node) (path.Node, error) {
	i, c, err := objects(ctx, p)
	if i == nil || !c.Objects.Queries.Contains(o) {
		return nil, err
	}
	return i.Field("Queries").MapIndex(int64(o)), nil
}

// Link returns the link to the renderbuffer object in the state block.
// If nil, nil is returned then the path cannot be followed.
func (o RenderbufferId) Link(ctx context.Context, p path.Node) (path.Node, error) {
	i, c, err := sharedObjects(ctx, p)
	if i == nil || !c.SharedObjects.Renderbuffers.Contains(o) {
		return nil, err
	}
	return i.Field("Renderbuffers").MapIndex(int64(o)), nil
}

// Link returns the link to the shader object in the state block.
// If nil, nil is returned then the path cannot be followed.
func (o ShaderId) Link(ctx context.Context, p path.Node) (path.Node, error) {
	i, c, err := sharedObjects(ctx, p)
	if i == nil || !c.SharedObjects.Shaders.Contains(o) {
		return nil, err
	}
	return i.Field("Shaders").MapIndex(int64(o)), nil
}

// Link returns the link to the texture object in the state block.
// If nil, nil is returned then the path cannot be followed.
func (o TextureId) Link(ctx context.Context, p path.Node) (path.Node, error) {
	i, c, err := sharedObjects(ctx, p)
	if i == nil || !c.SharedObjects.Textures.Contains(o) {
		return nil, err
	}
	return i.Field("Textures").MapIndex(int64(o)), nil
}

// Link returns the link to the uniform in the state block.
// If nil, nil is returned then the path cannot be followed.
func (o UniformLocation) Link(ctx context.Context, p path.Node) (path.Node, error) {
	i, c, err := sharedObjects(ctx, p)
	if i == nil {
		return nil, err
	}

	atom, err := resolve.Command(ctx, path.FindCommand(p))
	if err != nil {
		return nil, err
	}

	var program ProgramId
	switch atom := atom.(type) {
	case *GlGetActiveUniform:
		program = atom.Program
	case *GlGetUniformLocation:
		program = atom.Program
	default:
		program = c.BoundProgram
	}

	prog, ok := c.SharedObjects.Programs[program]
	if !ok || !prog.Uniforms.Contains(o) {
		return nil, nil
	}

	return i.
		Field("Programs").
		MapIndex(int64(program)).
		Field("Uniforms").
		MapIndex(int64(o)), nil
}

// Link returns the link to the vertex array in the state block.
// If nil, nil is returned then the path cannot be followed.
func (o VertexArrayId) Link(ctx context.Context, p path.Node) (path.Node, error) {
	i, c, err := objects(ctx, p)
	if i == nil || !c.Objects.VertexArrays.Contains(o) {
		return nil, err
	}
	return i.Field("VertexArrays").MapIndex(int64(o)), nil
}
<|endoftext|>


<commit_before>// errchk $G $D/$F.go

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

package main
type I int
type S struct { f map[I]int }
var v1 = S{ make(map[int]int) }		// OK--names are ignored.
var v2 map[I]int = map[int]int{}	// OK.
var v3 = S{ make(map[uint]int) }	// ERROR "cannot|illegal|incompatible|wrong"
<commit_msg>We should not silently permit a named type to match an unnamed type.  This is OK in a conversion but not in an assignment.<commit_after>// errchk $G $D/$F.go

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

package main
type I int
type S struct { f map[I]int }
var v1 = S{ make(map[int]int) }		// ERROR "cannot|illegal|incompatible|wrong"
var v2 map[I]int = map[int]int{}	// ERROR "cannot|illegal|incompatible|wrong"
var v3 = S{ make(map[uint]int) }	// ERROR "cannot|illegal|incompatible|wrong"
<|endoftext|>


<commit_before>package libsquash

import (
	"archive/tar"
	"bytes"
	"encoding/json"
	"io"
	"io/ioutil"
	"os"
	"regexp"
	"strings"

	"github.com/winchman/libsquash/tarball"
)

var uuidRegex = regexp.MustCompile("^[a-f0-9]{64}$")

func (e *export) IngestImageMetadata(tarstream io.Reader) error {
	if err := tarball.Walk(tarstream, func(t *tarball.TarFile) error {
		switch Type(t) {
		case Ignore:
			return nil
		case Repositories:
			if err := json.NewDecoder(t.Stream).Decode(&e.Repositories); err != nil {
				return err
			}
			// Export may have multiple branches with the same parent. Abort.
			for _, v := range e.Repositories {
				commits := map[string]string{}
				for tag, commit := range *v {
					commits[commit] = tag
				}
				if len(commits) > 1 {
					return errorMultipleBranchesSameParent
				}
			}
		case JSON:
			uuid := t.NameParts()[0]
			if e.Layers[uuid] == nil {
				e.Layers[uuid] = &layer{}
			}
			e.Layers[uuid].JSONHeader = t.Header
			if err := json.NewDecoder(t.Stream).Decode(&e.Layers[uuid].LayerConfig); err != nil {
				return err
			}
		case LayerTar:
			uuid := t.NameParts()[0]
			if e.Layers[uuid] == nil {
				e.Layers[uuid] = &layer{}
			}
			if err := tarball.Walk(t.Stream, func(tf *tarball.TarFile) error {
				filePath := nameWithoutWhiteoutPrefix(tf.Name())
				if e.fileToLayers[filePath] == nil {
					e.fileToLayers[filePath] = []fileLoc{}
				}
				foundWhiteout := isWhiteout(tf.Name())
				e.fileToLayers[filePath] = append(e.fileToLayers[filePath], fileLoc{
					uuid:     uuid,
					whiteout: foundWhiteout,
				})

				if foundWhiteout {
					e.whiteouts = append(e.whiteouts, whiteoutFile{
						prefix: filePath,
						uuid:   uuid,
					})
				}
				return nil
			}); err != nil {
				return err
			}
		}
		return nil
	}); err != nil {
		return err
	}

	return e.populateFileData()
}

func (e *export) populateFileData() error {

	e.start = e.FirstSquash()

	// Can't find a previously squashed layer, default to root
	if e.start == nil {
		e.start = e.Root()
	}

	if e.start == nil {
		return errorNoFROM
	}

	index := 0
	current := e.start
	orderMap := map[string]int{}
	for {
		orderMap[current.LayerConfig.ID] = index
		index++
		current = e.ChildOf(current.LayerConfig.ID)
		if current == nil {
			break
		}
	}

	for path, fileLocs := range e.fileToLayers {
		if len(fileLocs) > 0 {
			greatest := fileLocs[0]
			for _, loc := range fileLocs {
				if orderMap[loc.uuid] > orderMap[greatest.uuid] {
					greatest = loc
				}
			}
			if e.layerToFiles[greatest.uuid] == nil {
				e.layerToFiles[greatest.uuid] = map[string]bool{}
			}

			// if name matches whiteout prefix and the whiteout file is found
			// in a layer that is >= greatest.uuid, skip
			uuidContainingWhiteout, matches := matchesWhiteout(path, e.whiteouts)
			if (matches && orderMap[uuidContainingWhiteout] >= orderMap[greatest.uuid]) || greatest.whiteout {
				delete(e.layerToFiles[greatest.uuid], path)
			} else {
				e.layerToFiles[greatest.uuid][path] = true
			}
		}
	}

	return nil
}

func (e *export) SquashLayers(into, from *layer, tarstream io.Reader, outstream io.Writer) (imageID string, err error) {
	tempfile, err := ioutil.TempFile("", "libsquash")
	if err != nil {
		return "", err
	}

	defer func() {
		_ = tempfile.Close()
		_ = os.RemoveAll(tempfile.Name())
	}()

	var squashLayerTarWriter = tar.NewWriter(tempfile)

	if err = tarball.Walk(tarstream, func(t *tarball.TarFile) error {
		nameParts := t.NameParts()
		switch Type(t) {
		case Directory:
			uuidPart := nameParts[0]
			e.Layers[uuidPart].DirHeader = t.Header
		case LayerTar:
			uuidPart := nameParts[0]
			e.Layers[uuidPart].LayerTarHeader = t.Header
			if err := tarball.Walk(t.Stream, func(tf *tarball.TarFile) error {
				filePath := nameWithoutWhiteoutPrefix(tf.Name())
				if e.layerToFiles[uuidPart][filePath] {
					squashLayerTarWriter.WriteHeader(tf.Header)
					if _, err := io.Copy(squashLayerTarWriter, tf.Stream); err != nil {
						return err
					}
				}
				return nil
			}); err != nil {
				return err
			}
		case Version:
			uuidPart := nameParts[0]
			e.Layers[uuidPart].VersionHeader = t.Header
		}
		return nil
	}); err != nil {
		return "", err
	}

	debugf("Squashing from %s into %s\n", from.LayerConfig.ID[:12], into.LayerConfig.ID[:12])

	if err := squashLayerTarWriter.Close(); err != nil {
		return "", err
	}

	if _, err = tempfile.Seek(0, 0); err != nil {
		return "", err
	}

	debug("  -  Rewriting child history")
	if err := e.rewriteChildren(into); err != nil {
		return "", err
	}

	var tw = tar.NewWriter(outstream)
	var latestDirHeader, latestVersionHeader, latestJSONHeader, latestTarHeader *tar.Header

	squashedLayerConfig := into.LayerConfig

	current := e.Root()
	order := []*layer{} // TODO: optimize, remove this
	for {
		order = append(order, current)
		current = e.ChildOf(current.LayerConfig.ID)
		if current == nil {
			break
		}
	}

	var retID string

	for index, current := range order {
		var dir = current.DirHeader
		if latestDirHeader == nil {
			latestDirHeader = dir
		}
		if dir == nil {
			dir = latestDirHeader
		}
		dir.Name = current.LayerConfig.ID + "/"
		if err := tw.WriteHeader(dir); err != nil {
			return "", err
		}

		var version = current.VersionHeader
		if latestVersionHeader == nil {
			latestVersionHeader = version
		}
		if version == nil {
			version = latestVersionHeader
		}
		version.Name = current.LayerConfig.ID + "/VERSION"
		if err := tw.WriteHeader(version); err != nil {
			return "", err
		}
		if _, err := tw.Write([]byte("1.0")); err != nil {
			return "", err
		}

		var jsonHdr = current.JSONHeader
		if latestJSONHeader == nil {
			latestJSONHeader = jsonHdr
		}
		if jsonHdr == nil {
			jsonHdr = latestJSONHeader
		}
		jsonHdr.Name = current.LayerConfig.ID + "/json"

		var jsonBytes []byte
		var err error
		if current.LayerConfig.ID == into.LayerConfig.ID {
			jsonBytes, err = json.Marshal(squashedLayerConfig)
		} else {
			jsonBytes, err = json.Marshal(current.LayerConfig)
		}
		if err != nil {
			return "", err
		}
		jsonHdr.Size = int64(len(jsonBytes))
		if err := tw.WriteHeader(jsonHdr); err != nil {
			return "", err
		}
		if _, err := tw.Write(jsonBytes); err != nil {
			return "", err
		}

		var layerTar = current.LayerTarHeader
		if latestTarHeader == nil {
			latestTarHeader = layerTar
		}
		if layerTar == nil {
			layerTar = latestTarHeader
		}

		layerTar.Name = current.LayerConfig.ID + "/layer.tar"

		if current.LayerConfig.ID == into.LayerConfig.ID {
			fi, err := tempfile.Stat()
			if err != nil {
				return "", err
			}
			layerTar.Size = fi.Size()
			tw.WriteHeader(layerTar)
			if _, err := io.Copy(tw, tempfile); err != nil {
				return "", nil
			}
		} else {
			layerTar.Size = 1024
			tw.WriteHeader(layerTar)
			tw.Write(bytes.Repeat([]byte("\x00"), 1024))
		}

		if index == len(order)-1 {
			retID = current.LayerConfig.ID
		}
	}

	if err := tw.Close(); err != nil {
		return "", err
	}

	return retID, nil
}

func (e *export) rewriteChildren(from *layer) error {
	var entry = from

	squashID := entry.LayerConfig.ID
	for {
		if entry == nil {
			break
		}

		child := e.ChildOf(entry.LayerConfig.ID)

		/*
			if the layer is not the squash layer
				if: we have a #(nop) that is not an ADD, skip it
				else: remove the stuff in the layer.tar
		*/
		if entry.LayerConfig.ID != squashID && !strings.Contains(entry.Cmd(), "#(squash)") {
			if strings.Contains(entry.Cmd(), "#(nop)") && !strings.Contains(entry.Cmd(), "ADD") {
				if err := e.ReplaceLayer(entry); err != nil {
					return err
				}
			} else {
				e.RemoveLayer(entry)
			}
		}

		entry = child
	}
	return nil
}
<commit_msg>Trivial update in comment<commit_after>package libsquash

import (
	"archive/tar"
	"bytes"
	"encoding/json"
	"io"
	"io/ioutil"
	"os"
	"regexp"
	"strings"

	"github.com/winchman/libsquash/tarball"
)

var uuidRegex = regexp.MustCompile("^[a-f0-9]{64}$")

func (e *export) IngestImageMetadata(tarstream io.Reader) error {
	if err := tarball.Walk(tarstream, func(t *tarball.TarFile) error {
		switch Type(t) {
		case Ignore:
			return nil
		case Repositories:
			if err := json.NewDecoder(t.Stream).Decode(&e.Repositories); err != nil {
				return err
			}
			// Export may have multiple branches with the same parent; if so, abort.
			for _, v := range e.Repositories {
				commits := map[string]string{}
				for tag, commit := range *v {
					commits[commit] = tag
				}
				if len(commits) > 1 {
					return errorMultipleBranchesSameParent
				}
			}
		case JSON:
			uuid := t.NameParts()[0]
			if e.Layers[uuid] == nil {
				e.Layers[uuid] = &layer{}
			}
			e.Layers[uuid].JSONHeader = t.Header
			if err := json.NewDecoder(t.Stream).Decode(&e.Layers[uuid].LayerConfig); err != nil {
				return err
			}
		case LayerTar:
			uuid := t.NameParts()[0]
			if e.Layers[uuid] == nil {
				e.Layers[uuid] = &layer{}
			}
			if err := tarball.Walk(t.Stream, func(tf *tarball.TarFile) error {
				filePath := nameWithoutWhiteoutPrefix(tf.Name())
				if e.fileToLayers[filePath] == nil {
					e.fileToLayers[filePath] = []fileLoc{}
				}
				foundWhiteout := isWhiteout(tf.Name())
				e.fileToLayers[filePath] = append(e.fileToLayers[filePath], fileLoc{
					uuid:     uuid,
					whiteout: foundWhiteout,
				})

				if foundWhiteout {
					e.whiteouts = append(e.whiteouts, whiteoutFile{
						prefix: filePath,
						uuid:   uuid,
					})
				}
				return nil
			}); err != nil {
				return err
			}
		}
		return nil
	}); err != nil {
		return err
	}

	return e.populateFileData()
}

func (e *export) populateFileData() error {

	e.start = e.FirstSquash()

	// Can't find a previously squashed layer, default to root
	if e.start == nil {
		e.start = e.Root()
	}

	if e.start == nil {
		return errorNoFROM
	}

	index := 0
	current := e.start
	orderMap := map[string]int{}
	for {
		orderMap[current.LayerConfig.ID] = index
		index++
		current = e.ChildOf(current.LayerConfig.ID)
		if current == nil {
			break
		}
	}

	for path, fileLocs := range e.fileToLayers {
		if len(fileLocs) > 0 {
			greatest := fileLocs[0]
			for _, loc := range fileLocs {
				if orderMap[loc.uuid] > orderMap[greatest.uuid] {
					greatest = loc
				}
			}
			if e.layerToFiles[greatest.uuid] == nil {
				e.layerToFiles[greatest.uuid] = map[string]bool{}
			}

			// if name matches whiteout prefix and the whiteout file is found
			// in a layer that is >= greatest.uuid, skip
			uuidContainingWhiteout, matches := matchesWhiteout(path, e.whiteouts)
			if (matches && orderMap[uuidContainingWhiteout] >= orderMap[greatest.uuid]) || greatest.whiteout {
				delete(e.layerToFiles[greatest.uuid], path)
			} else {
				e.layerToFiles[greatest.uuid][path] = true
			}
		}
	}

	return nil
}

func (e *export) SquashLayers(into, from *layer, tarstream io.Reader, outstream io.Writer) (imageID string, err error) {
	tempfile, err := ioutil.TempFile("", "libsquash")
	if err != nil {
		return "", err
	}

	defer func() {
		_ = tempfile.Close()
		_ = os.RemoveAll(tempfile.Name())
	}()

	var squashLayerTarWriter = tar.NewWriter(tempfile)

	if err = tarball.Walk(tarstream, func(t *tarball.TarFile) error {
		nameParts := t.NameParts()
		switch Type(t) {
		case Directory:
			uuidPart := nameParts[0]
			e.Layers[uuidPart].DirHeader = t.Header
		case LayerTar:
			uuidPart := nameParts[0]
			e.Layers[uuidPart].LayerTarHeader = t.Header
			if err := tarball.Walk(t.Stream, func(tf *tarball.TarFile) error {
				filePath := nameWithoutWhiteoutPrefix(tf.Name())
				if e.layerToFiles[uuidPart][filePath] {
					squashLayerTarWriter.WriteHeader(tf.Header)
					if _, err := io.Copy(squashLayerTarWriter, tf.Stream); err != nil {
						return err
					}
				}
				return nil
			}); err != nil {
				return err
			}
		case Version:
			uuidPart := nameParts[0]
			e.Layers[uuidPart].VersionHeader = t.Header
		}
		return nil
	}); err != nil {
		return "", err
	}

	debugf("Squashing from %s into %s\n", from.LayerConfig.ID[:12], into.LayerConfig.ID[:12])

	if err := squashLayerTarWriter.Close(); err != nil {
		return "", err
	}

	if _, err = tempfile.Seek(0, 0); err != nil {
		return "", err
	}

	debug("  -  Rewriting child history")
	if err := e.rewriteChildren(into); err != nil {
		return "", err
	}

	var tw = tar.NewWriter(outstream)
	var latestDirHeader, latestVersionHeader, latestJSONHeader, latestTarHeader *tar.Header

	squashedLayerConfig := into.LayerConfig

	current := e.Root()
	order := []*layer{} // TODO: optimize, remove this
	for {
		order = append(order, current)
		current = e.ChildOf(current.LayerConfig.ID)
		if current == nil {
			break
		}
	}

	var retID string

	for index, current := range order {
		var dir = current.DirHeader
		if latestDirHeader == nil {
			latestDirHeader = dir
		}
		if dir == nil {
			dir = latestDirHeader
		}
		dir.Name = current.LayerConfig.ID + "/"
		if err := tw.WriteHeader(dir); err != nil {
			return "", err
		}

		var version = current.VersionHeader
		if latestVersionHeader == nil {
			latestVersionHeader = version
		}
		if version == nil {
			version = latestVersionHeader
		}
		version.Name = current.LayerConfig.ID + "/VERSION"
		if err := tw.WriteHeader(version); err != nil {
			return "", err
		}
		if _, err := tw.Write([]byte("1.0")); err != nil {
			return "", err
		}

		var jsonHdr = current.JSONHeader
		if latestJSONHeader == nil {
			latestJSONHeader = jsonHdr
		}
		if jsonHdr == nil {
			jsonHdr = latestJSONHeader
		}
		jsonHdr.Name = current.LayerConfig.ID + "/json"

		var jsonBytes []byte
		var err error
		if current.LayerConfig.ID == into.LayerConfig.ID {
			jsonBytes, err = json.Marshal(squashedLayerConfig)
		} else {
			jsonBytes, err = json.Marshal(current.LayerConfig)
		}
		if err != nil {
			return "", err
		}
		jsonHdr.Size = int64(len(jsonBytes))
		if err := tw.WriteHeader(jsonHdr); err != nil {
			return "", err
		}
		if _, err := tw.Write(jsonBytes); err != nil {
			return "", err
		}

		var layerTar = current.LayerTarHeader
		if latestTarHeader == nil {
			latestTarHeader = layerTar
		}
		if layerTar == nil {
			layerTar = latestTarHeader
		}

		layerTar.Name = current.LayerConfig.ID + "/layer.tar"

		if current.LayerConfig.ID == into.LayerConfig.ID {
			fi, err := tempfile.Stat()
			if err != nil {
				return "", err
			}
			layerTar.Size = fi.Size()
			tw.WriteHeader(layerTar)
			if _, err := io.Copy(tw, tempfile); err != nil {
				return "", nil
			}
		} else {
			layerTar.Size = 1024
			tw.WriteHeader(layerTar)
			tw.Write(bytes.Repeat([]byte("\x00"), 1024))
		}

		if index == len(order)-1 {
			retID = current.LayerConfig.ID
		}
	}

	if err := tw.Close(); err != nil {
		return "", err
	}

	return retID, nil
}

func (e *export) rewriteChildren(from *layer) error {
	var entry = from

	squashID := entry.LayerConfig.ID
	for {
		if entry == nil {
			break
		}

		child := e.ChildOf(entry.LayerConfig.ID)

		/*
			if the layer is not the squash layer
				if: we have a #(nop) that is not an ADD, skip it
				else: remove the stuff in the layer.tar
		*/
		if entry.LayerConfig.ID != squashID && !strings.Contains(entry.Cmd(), "#(squash)") {
			if strings.Contains(entry.Cmd(), "#(nop)") && !strings.Contains(entry.Cmd(), "ADD") {
				if err := e.ReplaceLayer(entry); err != nil {
					return err
				}
			} else {
				e.RemoveLayer(entry)
			}
		}

		entry = child
	}
	return nil
}
<|endoftext|>


<commit_before>package testing

import (
	"fmt"
	"io/ioutil"
	. "launchpad.net/gocheck"
	"launchpad.net/juju-core/charm"
	"launchpad.net/juju-core/environs"
	"launchpad.net/juju-core/environs/dummy"
	"launchpad.net/juju-core/juju"
	state "launchpad.net/juju-core/state"
	"launchpad.net/juju-core/testing"
	"net/url"
	"os"
	"path/filepath"
)

// JujuConnSuite provides a freshly bootstrapped juju.Conn
// for each test. It also includes testing.LoggingSuite.
//
// It also sets up $HOME and environs.VarDir to
// temporary directories; the former is primed to
// hold the dummy environments.yaml file.
//
// The name of the dummy environment is "dummyenv".
type JujuConnSuite struct {
	testing.LoggingSuite
	testing.ZkSuite
	Conn      *juju.Conn
	State     *state.State
	rootDir   string // the faked-up root directory.
	oldHome   string
	oldVarDir string
}

var config = []byte(`
environments:
    dummyenv:
        type: dummy
        zookeeper: true
        authorized-keys: 'i-am-a-key'
`)

func (s *JujuConnSuite) SetUpTest(c *C) {
	s.LoggingSuite.SetUpTest(c)

	s.rootDir = c.MkDir()
	s.oldHome = os.Getenv("HOME")
	home := filepath.Join(s.rootDir, "/home/ubuntu")
	err := os.MkdirAll(home, 0777)
	c.Assert(err, IsNil)
	os.Setenv("HOME", home)

	s.oldVarDir = environs.VarDir
	varDir := filepath.Join(s.rootDir, environs.VarDir)
	err = os.MkdirAll(varDir, 0777)
	c.Assert(err, IsNil)
	environs.VarDir = varDir

	err = os.Mkdir(filepath.Join(home, ".juju"), 0777)
	c.Assert(err, IsNil)

	err = ioutil.WriteFile(filepath.Join(home, ".juju", "environments.yaml"), config, 0666)
	c.Assert(err, IsNil)
	conn, err := juju.NewConn("dummyenv")
	c.Assert(err, IsNil)

	// sanity check we've got the correct environment.
	c.Assert(conn.Environ.Name(), Equals, "dummyenv")

	c.Assert(conn.Bootstrap(false), IsNil)
	s.Conn = conn
	s.State, err = conn.State()
	c.Assert(err, IsNil)
}

func (s *JujuConnSuite) TearDownTest(c *C) {
	dummy.Reset()
	c.Assert(s.Conn.Close(), IsNil)
	s.Conn = nil
	s.State = nil
	os.Setenv("HOME", s.oldHome)
	s.oldHome = ""
	environs.VarDir = s.oldVarDir
	s.oldVarDir = ""
	s.rootDir = ""
	s.LoggingSuite.TearDownTest(c)
}

// WriteConfig writes a juju config file to the "home" directory.
func (s *JujuConnSuite) WriteConfig(config string) {
	if s.rootDir == "" {
		panic("SetUpTest has not been called; will not overwrite $HOME/.juju/environments.yaml")
	}
	path := filepath.Join(os.Getenv("HOME"), ".juju", "environments.yaml")
	err := ioutil.WriteFile(path, []byte(config), 0666)
	if err != nil {
		panic(err)
	}
}

func (s *JujuConnSuite) StateInfo(c *C) *state.Info {
	return &state.Info{Addrs: []string{testing.ZkAddr}}
}

func (s *JujuConnSuite) AddTestingCharm(c *C, name string) *state.Charm {
	ch := testing.Charms.Dir(name)
	ident := fmt.Sprintf("%s-%d", name, ch.Revision())
	curl := charm.MustParseURL("local:series/" + ident)
	bundleURL, err := url.Parse("http://bundles.example.com/" + ident)
	c.Assert(err, IsNil)
	sch, err := s.State.AddCharm(ch, curl, bundleURL, ident+"-sha256")
	c.Assert(err, IsNil)
	return sch
}
<commit_msg>juju/testing: use 0600 mode for environments.yaml<commit_after>package testing

import (
	"fmt"
	"io/ioutil"
	. "launchpad.net/gocheck"
	"launchpad.net/juju-core/charm"
	"launchpad.net/juju-core/environs"
	"launchpad.net/juju-core/environs/dummy"
	"launchpad.net/juju-core/juju"
	state "launchpad.net/juju-core/state"
	"launchpad.net/juju-core/testing"
	"net/url"
	"os"
	"path/filepath"
)

// JujuConnSuite provides a freshly bootstrapped juju.Conn
// for each test. It also includes testing.LoggingSuite.
//
// It also sets up $HOME and environs.VarDir to
// temporary directories; the former is primed to
// hold the dummy environments.yaml file.
//
// The name of the dummy environment is "dummyenv".
type JujuConnSuite struct {
	testing.LoggingSuite
	testing.ZkSuite
	Conn      *juju.Conn
	State     *state.State
	rootDir   string // the faked-up root directory.
	oldHome   string
	oldVarDir string
}

var config = []byte(`
environments:
    dummyenv:
        type: dummy
        zookeeper: true
        authorized-keys: 'i-am-a-key'
`)

func (s *JujuConnSuite) SetUpTest(c *C) {
	s.LoggingSuite.SetUpTest(c)

	s.rootDir = c.MkDir()
	s.oldHome = os.Getenv("HOME")
	home := filepath.Join(s.rootDir, "/home/ubuntu")
	err := os.MkdirAll(home, 0777)
	c.Assert(err, IsNil)
	os.Setenv("HOME", home)

	s.oldVarDir = environs.VarDir
	varDir := filepath.Join(s.rootDir, environs.VarDir)
	err = os.MkdirAll(varDir, 0777)
	c.Assert(err, IsNil)
	environs.VarDir = varDir

	err = os.Mkdir(filepath.Join(home, ".juju"), 0777)
	c.Assert(err, IsNil)

	err = ioutil.WriteFile(filepath.Join(home, ".juju", "environments.yaml"), config, 0600)
	c.Assert(err, IsNil)
	conn, err := juju.NewConn("dummyenv")
	c.Assert(err, IsNil)

	// sanity check we've got the correct environment.
	c.Assert(conn.Environ.Name(), Equals, "dummyenv")

	c.Assert(conn.Bootstrap(false), IsNil)
	s.Conn = conn
	s.State, err = conn.State()
	c.Assert(err, IsNil)
}

func (s *JujuConnSuite) TearDownTest(c *C) {
	dummy.Reset()
	c.Assert(s.Conn.Close(), IsNil)
	s.Conn = nil
	s.State = nil
	os.Setenv("HOME", s.oldHome)
	s.oldHome = ""
	environs.VarDir = s.oldVarDir
	s.oldVarDir = ""
	s.rootDir = ""
	s.LoggingSuite.TearDownTest(c)
}

// WriteConfig writes a juju config file to the "home" directory.
func (s *JujuConnSuite) WriteConfig(config string) {
	if s.rootDir == "" {
		panic("SetUpTest has not been called; will not overwrite $HOME/.juju/environments.yaml")
	}
	path := filepath.Join(os.Getenv("HOME"), ".juju", "environments.yaml")
	err := ioutil.WriteFile(path, []byte(config), 0600)
	if err != nil {
		panic(err)
	}
}

func (s *JujuConnSuite) StateInfo(c *C) *state.Info {
	return &state.Info{Addrs: []string{testing.ZkAddr}}
}

func (s *JujuConnSuite) AddTestingCharm(c *C, name string) *state.Charm {
	ch := testing.Charms.Dir(name)
	ident := fmt.Sprintf("%s-%d", name, ch.Revision())
	curl := charm.MustParseURL("local:series/" + ident)
	bundleURL, err := url.Parse("http://bundles.example.com/" + ident)
	c.Assert(err, IsNil)
	sch, err := s.State.AddCharm(ch, curl, bundleURL, ident+"-sha256")
	c.Assert(err, IsNil)
	return sch
}
<|endoftext|>