<commit_before>// Copyright 2016 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 pixiecore

import (
	"bytes"
	"errors"
	"fmt"
	"io"
	"net"
	"net/http"
	"net/url"
	"strconv"
	"text/template"
)

func serveHTTP(l net.Listener, handlers ...func(*http.ServeMux)) error {
	mux := http.NewServeMux()
	for _, h := range handlers {
		h(mux)
	}
	if err := http.Serve(l, mux); err != nil {
		return fmt.Errorf("HTTP server shut down: %s", err)
	}
	return nil
}

func (s *Server) serveHTTP(mux *http.ServeMux) {
	mux.HandleFunc("/_/ipxe", s.handleIpxe)
	mux.HandleFunc("/_/file", s.handleFile)
	mux.HandleFunc("/_/booting", s.handleBooting)
}

func (s *Server) handleIpxe(w http.ResponseWriter, r *http.Request) {
	macStr := r.URL.Query().Get("mac")
	if macStr == "" {
		s.debug("HTTP", "Bad request %q from %s, missing MAC address", r.URL, r.RemoteAddr)
		http.Error(w, "missing MAC address parameter", http.StatusBadRequest)
		return
	}
	archStr := r.URL.Query().Get("arch")
	if archStr == "" {
		s.debug("HTTP", "Bad request %q from %s, missing architecture", r.URL, r.RemoteAddr)
		http.Error(w, "missing architecture parameter", http.StatusBadRequest)
		return
	}

	mac, err := net.ParseMAC(macStr)
	if err != nil {
		s.debug("HTTP", "Bad request %q from %s, invalid MAC address %q (%s)", r.URL, r.RemoteAddr, macStr, err)
		http.Error(w, "invalid MAC address", http.StatusBadRequest)
		return
	}

	i, err := strconv.Atoi(archStr)
	if err != nil {
		s.debug("HTTP", "Bad request %q from %s, invalid architecture %q (%s)", r.URL, r.RemoteAddr, archStr, err)
		http.Error(w, "invalid architecture", http.StatusBadRequest)
		return
	}
	arch := Architecture(i)
	switch arch {
	case ArchIA32, ArchX64:
	default:
		s.debug("HTTP", "Bad request %q from %s, unknown architecture %q", r.URL, r.RemoteAddr, arch)
		http.Error(w, "unknown architecture", http.StatusBadRequest)
		return
	}

	mach := Machine{
		MAC:  mac,
		Arch: arch,
	}
	spec, err := s.Booter.BootSpec(mach)
	if err != nil {
		s.log("HTTP", "Couldn't get a bootspec for %s (query %q from %s): %s", mac, r.URL, r.RemoteAddr, err)
		http.Error(w, "couldn't get a bootspec", http.StatusInternalServerError)
		return
	}
	if spec == nil {
		// TODO: make ipxe abort netbooting so it can fall through to
		// other boot options - unsure if that's possible.
		s.debug("HTTP", "No boot spec for %s (query %q from %s), ignoring boot request", mac, r.URL, r.RemoteAddr)
		http.Error(w, "you don't netboot", http.StatusNotFound)
		return
	}
	script, err := ipxeScript(mach, spec, r.Host)
	if err != nil {
		s.log("HTTP", "Failed to assemble ipxe script for %s (query %q from %s): %s", mac, r.URL, r.RemoteAddr, err)
		http.Error(w, "couldn't get a boot script", http.StatusInternalServerError)
		return
	}

	s.log("HTTP", "Sending ipxe boot script to %s", r.RemoteAddr)
	s.machineEvent(mac, machineStateIpxeScript, "Sent iPXE boot script")
	w.Header().Set("Content-Type", "text/plain")
	w.Write(script)
}

func (s *Server) handleFile(w http.ResponseWriter, r *http.Request) {
	name := r.URL.Query().Get("name")
	if name == "" {
		s.debug("HTTP", "Bad request %q from %s, missing filename", r.URL, r.RemoteAddr)
		http.Error(w, "missing filename", http.StatusBadRequest)
	}

	f, sz, err := s.Booter.ReadBootFile(ID(name))
	if err != nil {
		s.log("HTTP", "Error getting file %q (query %q from %s): %s", name, r.URL, r.RemoteAddr, err)
		http.Error(w, "couldn't get file", http.StatusInternalServerError)
		return
	}
	defer f.Close()
	if sz >= 0 {
		w.Header().Set("Content-Length", strconv.FormatInt(sz, 10))
	} else {
		s.log("HTTP", "Unknown file size for %q, boot will be VERY slow (can your Booter provide file sizes?)", name)
	}
	if _, err = io.Copy(w, f); err != nil {
		s.log("HTTP", "Copy of %q to %s (query %q) failed: %s", name, r.RemoteAddr, r.URL, err)
		return
	}
	s.log("HTTP", "Sent file %q to %s", name, r.RemoteAddr)

	switch r.URL.Query().Get("type") {
	case "kernel":
		mac, err := net.ParseMAC(r.URL.Query().Get("mac"))
		if err != nil {
			s.log("HTTP", "File fetch provided invalid MAC address %q", r.URL.Query().Get("mac"))
			return
		}
		s.machineEvent(mac, machineStateKernel, "Sent kernel %q", name)
	case "initrd":
		mac, err := net.ParseMAC(r.URL.Query().Get("mac"))
		if err != nil {
			s.log("HTTP", "File fetch provided invalid MAC address %q", r.URL.Query().Get("mac"))
			return
		}
		s.machineEvent(mac, machineStateInitrd, "Sent initrd %q", name)
	}
}

func (s *Server) handleBooting(w http.ResponseWriter, r *http.Request) {
	// This handler always errors out.
	http.Error(w, "", http.StatusInternalServerError)

	macStr := r.URL.Query().Get("mac")
	if macStr == "" {
		s.debug("HTTP", "Bad request %q from %s, missing MAC address", r.URL, r.RemoteAddr)
		return
	}
	mac, err := net.ParseMAC(macStr)
	if err != nil {
		s.debug("HTTP", "Bad request %q from %s, invalid MAC address %q (%s)", r.URL, r.RemoteAddr, macStr, err)
		return
	}
	s.machineEvent(mac, machineStateBooted, "Booting into OS")
}

func ipxeScript(mach Machine, spec *Spec, serverHost string) ([]byte, error) {
	if spec.Kernel == "" {
		return nil, errors.New("spec is missing Kernel")
	}

	urlTemplate := fmt.Sprintf("http://%s/_/file?name=%%s&type=%%s&mac=%%s", serverHost)
	var b bytes.Buffer
	b.WriteString("#!ipxe\n")
	u := fmt.Sprintf(urlTemplate, url.QueryEscape(string(spec.Kernel)), "kernel", url.QueryEscape(mach.MAC.String()))
	fmt.Fprintf(&b, "kernel --name kernel %s\n", u)
	for i, initrd := range spec.Initrd {
		u = fmt.Sprintf(urlTemplate, url.QueryEscape(string(initrd)), "initrd", url.QueryEscape(mach.MAC.String()))
		fmt.Fprintf(&b, "initrd --name initrd%d %s\n", i, u)
	}

	fmt.Fprintf(&b, "imgfetch --name ready http://%s/_/booting?mac=%s ||\n", serverHost, url.QueryEscape(mach.MAC.String()))
	b.WriteString("imgfree ready ||\n")

	b.WriteString("boot kernel ")
	for i := range spec.Initrd {
		fmt.Fprintf(&b, "initrd=initrd%d ", i)
	}

	f := func(id string) string {
		return fmt.Sprintf("http://%s/_/file?name=%s", serverHost, url.QueryEscape(id))
	}
	cmdline, err := expandCmdline(spec.Cmdline, template.FuncMap{"ID": f})
	if err != nil {
		return nil, fmt.Errorf("expanding cmdline %q: %s", spec.Cmdline, err)
	}
	b.WriteString(cmdline)
	b.WriteByte('\n')

	return b.Bytes(), nil
}
<commit_msg>handleBooting should return a no-op script, not an error.<commit_after>// Copyright 2016 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 pixiecore

import (
	"bytes"
	"errors"
	"fmt"
	"io"
	"net"
	"net/http"
	"net/url"
	"strconv"
	"text/template"
)

func serveHTTP(l net.Listener, handlers ...func(*http.ServeMux)) error {
	mux := http.NewServeMux()
	for _, h := range handlers {
		h(mux)
	}
	if err := http.Serve(l, mux); err != nil {
		return fmt.Errorf("HTTP server shut down: %s", err)
	}
	return nil
}

func (s *Server) serveHTTP(mux *http.ServeMux) {
	mux.HandleFunc("/_/ipxe", s.handleIpxe)
	mux.HandleFunc("/_/file", s.handleFile)
	mux.HandleFunc("/_/booting", s.handleBooting)
}

func (s *Server) handleIpxe(w http.ResponseWriter, r *http.Request) {
	macStr := r.URL.Query().Get("mac")
	if macStr == "" {
		s.debug("HTTP", "Bad request %q from %s, missing MAC address", r.URL, r.RemoteAddr)
		http.Error(w, "missing MAC address parameter", http.StatusBadRequest)
		return
	}
	archStr := r.URL.Query().Get("arch")
	if archStr == "" {
		s.debug("HTTP", "Bad request %q from %s, missing architecture", r.URL, r.RemoteAddr)
		http.Error(w, "missing architecture parameter", http.StatusBadRequest)
		return
	}

	mac, err := net.ParseMAC(macStr)
	if err != nil {
		s.debug("HTTP", "Bad request %q from %s, invalid MAC address %q (%s)", r.URL, r.RemoteAddr, macStr, err)
		http.Error(w, "invalid MAC address", http.StatusBadRequest)
		return
	}

	i, err := strconv.Atoi(archStr)
	if err != nil {
		s.debug("HTTP", "Bad request %q from %s, invalid architecture %q (%s)", r.URL, r.RemoteAddr, archStr, err)
		http.Error(w, "invalid architecture", http.StatusBadRequest)
		return
	}
	arch := Architecture(i)
	switch arch {
	case ArchIA32, ArchX64:
	default:
		s.debug("HTTP", "Bad request %q from %s, unknown architecture %q", r.URL, r.RemoteAddr, arch)
		http.Error(w, "unknown architecture", http.StatusBadRequest)
		return
	}

	mach := Machine{
		MAC:  mac,
		Arch: arch,
	}
	spec, err := s.Booter.BootSpec(mach)
	if err != nil {
		s.log("HTTP", "Couldn't get a bootspec for %s (query %q from %s): %s", mac, r.URL, r.RemoteAddr, err)
		http.Error(w, "couldn't get a bootspec", http.StatusInternalServerError)
		return
	}
	if spec == nil {
		// TODO: make ipxe abort netbooting so it can fall through to
		// other boot options - unsure if that's possible.
		s.debug("HTTP", "No boot spec for %s (query %q from %s), ignoring boot request", mac, r.URL, r.RemoteAddr)
		http.Error(w, "you don't netboot", http.StatusNotFound)
		return
	}
	script, err := ipxeScript(mach, spec, r.Host)
	if err != nil {
		s.log("HTTP", "Failed to assemble ipxe script for %s (query %q from %s): %s", mac, r.URL, r.RemoteAddr, err)
		http.Error(w, "couldn't get a boot script", http.StatusInternalServerError)
		return
	}

	s.log("HTTP", "Sending ipxe boot script to %s", r.RemoteAddr)
	s.machineEvent(mac, machineStateIpxeScript, "Sent iPXE boot script")
	w.Header().Set("Content-Type", "text/plain")
	w.Write(script)
}

func (s *Server) handleFile(w http.ResponseWriter, r *http.Request) {
	name := r.URL.Query().Get("name")
	if name == "" {
		s.debug("HTTP", "Bad request %q from %s, missing filename", r.URL, r.RemoteAddr)
		http.Error(w, "missing filename", http.StatusBadRequest)
	}

	f, sz, err := s.Booter.ReadBootFile(ID(name))
	if err != nil {
		s.log("HTTP", "Error getting file %q (query %q from %s): %s", name, r.URL, r.RemoteAddr, err)
		http.Error(w, "couldn't get file", http.StatusInternalServerError)
		return
	}
	defer f.Close()
	if sz >= 0 {
		w.Header().Set("Content-Length", strconv.FormatInt(sz, 10))
	} else {
		s.log("HTTP", "Unknown file size for %q, boot will be VERY slow (can your Booter provide file sizes?)", name)
	}
	if _, err = io.Copy(w, f); err != nil {
		s.log("HTTP", "Copy of %q to %s (query %q) failed: %s", name, r.RemoteAddr, r.URL, err)
		return
	}
	s.log("HTTP", "Sent file %q to %s", name, r.RemoteAddr)

	switch r.URL.Query().Get("type") {
	case "kernel":
		mac, err := net.ParseMAC(r.URL.Query().Get("mac"))
		if err != nil {
			s.log("HTTP", "File fetch provided invalid MAC address %q", r.URL.Query().Get("mac"))
			return
		}
		s.machineEvent(mac, machineStateKernel, "Sent kernel %q", name)
	case "initrd":
		mac, err := net.ParseMAC(r.URL.Query().Get("mac"))
		if err != nil {
			s.log("HTTP", "File fetch provided invalid MAC address %q", r.URL.Query().Get("mac"))
			return
		}
		s.machineEvent(mac, machineStateInitrd, "Sent initrd %q", name)
	}
}

func (s *Server) handleBooting(w http.ResponseWriter, r *http.Request) {
	// This should not always errors out.
	// if we send http.StatusInternalServerError iPXE will get a image not found error
	// http.Error(w, "", http.StatusInternalServerError)
	// return a empty commented script
	fmt.Fprintf(w, "# Booting")

	macStr := r.URL.Query().Get("mac")
	if macStr == "" {
		s.debug("HTTP", "Bad request %q from %s, missing MAC address", r.URL, r.RemoteAddr)
		return
	}
	mac, err := net.ParseMAC(macStr)
	if err != nil {
		s.debug("HTTP", "Bad request %q from %s, invalid MAC address %q (%s)", r.URL, r.RemoteAddr, macStr, err)
		return
	}
	s.machineEvent(mac, machineStateBooted, "Booting into OS")
}

func ipxeScript(mach Machine, spec *Spec, serverHost string) ([]byte, error) {
	if spec.Kernel == "" {
		return nil, errors.New("spec is missing Kernel")
	}

	urlTemplate := fmt.Sprintf("http://%s/_/file?name=%%s&type=%%s&mac=%%s", serverHost)
	var b bytes.Buffer
	b.WriteString("#!ipxe\n")
	u := fmt.Sprintf(urlTemplate, url.QueryEscape(string(spec.Kernel)), "kernel", url.QueryEscape(mach.MAC.String()))
	fmt.Fprintf(&b, "kernel --name kernel %s\n", u)
	for i, initrd := range spec.Initrd {
		u = fmt.Sprintf(urlTemplate, url.QueryEscape(string(initrd)), "initrd", url.QueryEscape(mach.MAC.String()))
		fmt.Fprintf(&b, "initrd --name initrd%d %s\n", i, u)
	}

	fmt.Fprintf(&b, "imgfetch --name ready http://%s/_/booting?mac=%s ||\n", serverHost, url.QueryEscape(mach.MAC.String()))
	b.WriteString("imgfree ready ||\n")

	b.WriteString("boot kernel ")
	for i := range spec.Initrd {
		fmt.Fprintf(&b, "initrd=initrd%d ", i)
	}

	f := func(id string) string {
		return fmt.Sprintf("http://%s/_/file?name=%s", serverHost, url.QueryEscape(id))
	}
	cmdline, err := expandCmdline(spec.Cmdline, template.FuncMap{"ID": f})
	if err != nil {
		return nil, fmt.Errorf("expanding cmdline %q: %s", spec.Cmdline, err)
	}
	b.WriteString(cmdline)
	b.WriteByte('\n')

	return b.Bytes(), nil
}
<|endoftext|>


<commit_before>package cli

// the cli package contains urfave/cli related structs that help make up
// the command line for buildah commands. it resides here so other projects
// that vendor in this code can use them too.

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

	"github.com/containers/buildah"
	"github.com/containers/buildah/util"
	"github.com/opencontainers/runtime-spec/specs-go"
	"github.com/pkg/errors"
	"github.com/spf13/pflag"
)

// LayerResults represents the results of the layer flags
type LayerResults struct {
	ForceRm bool
	Layers  bool
}

// UserNSResults represents the results for the UserNS flags
type UserNSResults struct {
	UserNS            string
	UserNSUIDMap      []string
	UserNSGIDMap      []string
	UserNSUIDMapUser  string
	UserNSGIDMapGroup string
}

// NameSpaceResults represents the results for Namespace flags
type NameSpaceResults struct {
	IPC           string
	Network       string
	CNIConfigDir  string
	CNIPlugInPath string
	PID           string
	UTS           string
}

// BudResults represents the results for Bud flags
type BudResults struct {
	Annotation          []string
	Authfile            string
	BuildArg            []string
	CacheFrom           string
	CertDir             string
	Compress            bool
	Creds               string
	DisableCompression  bool
	DisableContentTrust bool
	File                []string
	Format              string
	Iidfile             string
	Label               []string
	Logfile             string
	Loglevel            int
	NoCache             bool
	Platform            string
	Pull                bool
	PullAlways          bool
	Quiet               bool
	Rm                  bool
	Runtime             string
	RuntimeFlags        []string
	SignaturePolicy     string
	Squash              bool
	Tag                 []string
	Target              string
	TlsVerify           bool
}

// FromAndBugResults represents the results for common flags
// in bud and from
type FromAndBudResults struct {
	AddHost      []string
	BlobCache    string
	CapAdd       []string
	CapDrop      []string
	CgroupParent string
	CPUPeriod    uint64
	CPUQuota     int64
	CPUSetCPUs   string
	CPUSetMems   string
	CPUShares    uint64
	DNSSearch    []string
	DNSServers   []string
	DNSOptions   []string
	HttpProxy    bool
	Isolation    string
	Memory       string
	MemorySwap   string
	SecurityOpt  []string
	ShmSize      string
	Ulimit       []string
	Volumes      []string
}

// GetUserNSFlags returns the common flags for usernamespace
func GetUserNSFlags(flags *UserNSResults) pflag.FlagSet {
	usernsFlags := pflag.FlagSet{}
	usernsFlags.StringVar(&flags.UserNS, "userns", "", "'container', `path` of user namespace to join, or 'host'")
	usernsFlags.StringSliceVar(&flags.UserNSUIDMap, "userns-uid-map", []string{}, "`containerID:hostID:length` UID mapping to use in user namespace")
	usernsFlags.StringSliceVar(&flags.UserNSGIDMap, "userns-gid-map", []string{}, "`containerID:hostID:length` GID mapping to use in user namespace")
	usernsFlags.StringVar(&flags.UserNSUIDMapUser, "userns-uid-map-user", "", "`name` of entries from /etc/subuid to use to set user namespace UID mapping")
	usernsFlags.StringVar(&flags.UserNSGIDMapGroup, "userns-gid-map-group", "", "`name` of entries from /etc/subgid to use to set user namespace GID mapping")
	return usernsFlags
}

// GetNameSpaceFlags returns the common flags for a namespace menu
func GetNameSpaceFlags(flags *NameSpaceResults) pflag.FlagSet {
	fs := pflag.FlagSet{}
	fs.StringVar(&flags.IPC, string(specs.IPCNamespace), "", "'container', `path` of IPC namespace to join, or 'host'")
	fs.StringVar(&flags.Network, string(specs.NetworkNamespace), "", "'container', `path` of network namespace to join, or 'host'")
	// TODO How do we alias net and network?
	fs.StringVar(&flags.Network, "net", "", "'container', `path` of network namespace to join, or 'host'")
	fs.MarkHidden("net")
	fs.StringVar(&flags.CNIConfigDir, "cni-config-dir", util.DefaultCNIConfigDir, "`directory` of CNI configuration files")
	fs.StringVar(&flags.CNIPlugInPath, "cni-plugin-path", util.DefaultCNIPluginPath, "`path` of CNI network plugins")
	fs.StringVar(&flags.PID, string(specs.PIDNamespace), "", "container, `path` of PID namespace to join, or 'host'")
	fs.StringVar(&flags.UTS, string(specs.UTSNamespace), "", "container, :`path` of UTS namespace to join, or 'host'")
	return fs
}

// GetLayerFlags returns the common flags for layers
func GetLayerFlags(flags *LayerResults) pflag.FlagSet {
	fs := pflag.FlagSet{}
	fs.BoolVar(&flags.ForceRm, "force-rm", false, "Always remove intermediate containers after a build, even if the build is unsuccessful.")
	fs.BoolVar(&flags.Layers, "layers", UseLayers(), fmt.Sprintf("cache intermediate layers during build. Use BUILDAH_LAYERS environment variable to override."))
	return fs
}

// GetBudFlags returns common bud flags
func GetBudFlags(flags *BudResults) pflag.FlagSet {
	fs := pflag.FlagSet{}
	fs.StringArrayVar(&flags.Annotation, "annotation", []string{}, "Set metadata for an image (default [])")
	fs.StringVar(&flags.Authfile, "authfile", GetDefaultAuthFile(), "path of the authentication file.")
	fs.StringArrayVar(&flags.BuildArg, "build-arg", []string{}, "`argument=value` to supply to the builder")
	fs.StringVar(&flags.CacheFrom, "cache-from", "", "Images to utilise as potential cache sources. The build process does not currently support caching so this is a NOOP.")
	fs.StringVar(&flags.CertDir, "cert-dir", "", "use certificates at the specified path to access the registry")
	fs.BoolVar(&flags.Compress, "compress", false, "This is legacy option, which has no effect on the image")
	fs.StringVar(&flags.Creds, "creds", "", "use `[username[:password]]` for accessing the registry")
	fs.BoolVarP(&flags.DisableCompression, "disable-compression", "D", true, "don't compress layers by default")
	fs.BoolVar(&flags.DisableContentTrust, "disable-content-trust", false, "This is a Docker specific option and is a NOOP")
	fs.StringSliceVarP(&flags.File, "file", "f", []string{}, "`pathname or URL` of a Dockerfile")
	fs.StringVar(&flags.Format, "format", DefaultFormat(), "`format` of the built image's manifest and metadata. Use BUILDAH_FORMAT environment variable to override.")
	fs.StringVar(&flags.Iidfile, "iidfile", "", "`file` to write the image ID to")
	fs.StringArrayVar(&flags.Label, "label", []string{}, "Set metadata for an image (default [])")
	fs.BoolVar(&flags.NoCache, "no-cache", false, "Do not use existing cached images for the container build. Build from the start with a new set of cached layers.")
	fs.StringVar(&flags.Logfile, "logfile", "", "log to `file` instead of stdout/stderr")
	fs.IntVar(&flags.Loglevel, "loglevel", 0, "adjust logging level (range from -2 to 3)")
	fs.StringVar(&flags.Platform, "platform", "", "CLI compatibility: no action or effect")
	fs.BoolVar(&flags.Pull, "pull", true, "pull the image if not present")
	fs.BoolVar(&flags.PullAlways, "pull-always", false, "pull the image, even if a version is present")
	fs.BoolVarP(&flags.Quiet, "quiet", "q", false, "refrain from announcing build instructions and image read/write progress")
	fs.BoolVar(&flags.Rm, "rm", true, "Remove intermediate containers after a successful build")
	fs.StringVar(&flags.Runtime, "runtime", util.Runtime(), "`path` to an alternate runtime. Use BUILDAH_RUNTIME environment variable to override.")
	fs.StringSliceVar(&flags.RuntimeFlags, "runtime-flag", []string{}, "add global flags for the container runtime")
	fs.StringVar(&flags.SignaturePolicy, "signature-policy", "", "`pathname` of signature policy file (not usually used)")
	fs.BoolVar(&flags.Squash, "squash", false, "Squash newly built layers into a single new layer.")
	fs.StringArrayVarP(&flags.Tag, "tag", "t", []string{}, "tagged `name` to apply to the built image")
	fs.StringVar(&flags.Target, "target", "", "set the target build stage to build")
	fs.BoolVar(&flags.TlsVerify, "tls-verify", true, "require HTTPS and verify certificates when accessing the registry")
	return fs
}

func GetFromAndBudFlags(flags *FromAndBudResults, usernsResults *UserNSResults, namespaceResults *NameSpaceResults) pflag.FlagSet {
	fs := pflag.FlagSet{}
	fs.StringSliceVar(&flags.AddHost, "add-host", []string{}, "add a custom host-to-IP mapping (`host:ip`) (default [])")
	fs.StringVar(&flags.BlobCache, "blob-cache", "", "assume image blobs in the specified directory will be available for pushing")
	fs.MarkHidden("blob-cache")
	fs.StringSliceVar(&flags.CapAdd, "cap-add", []string{}, "add the specified capability when running (default [])")
	fs.StringSliceVar(&flags.CapDrop, "cap-drop", []string{}, "drop the specified capability when running (default [])")
	fs.StringVar(&flags.CgroupParent, "cgroup-parent", "", "optional parent cgroup for the container")
	fs.Uint64Var(&flags.CPUPeriod, "cpu-period", 0, "limit the CPU CFS (Completely Fair Scheduler) period")
	fs.Int64Var(&flags.CPUQuota, "cpu-quota", 0, "limit the CPU CFS (Completely Fair Scheduler) quota")
	fs.Uint64VarP(&flags.CPUShares, "cpu-shares", "c", 0, "CPU shares (relative weight)")
	fs.StringVar(&flags.CPUSetCPUs, "cpuset-cpus", "", "CPUs in which to allow execution (0-3, 0,1)")
	fs.StringVar(&flags.CPUSetMems, "cpuset-mems", "", "memory nodes (MEMs) in which to allow execution (0-3, 0,1). Only effective on NUMA systems.")
	fs.StringSliceVar(&flags.DNSSearch, "dns-search", []string{}, "Set custom DNS search domains")
	fs.StringSliceVar(&flags.DNSServers, "dns", []string{}, "Set custom DNS servers")
	fs.StringSliceVar(&flags.DNSOptions, "dns-option", []string{}, "Set custom DNS options")
	fs.BoolVar(&flags.HttpProxy, "http-proxy", true, "pass thru HTTP Proxy environment variables")
	fs.StringVar(&flags.Isolation, "isolation", DefaultIsolation(), "`type` of process isolation to use. Use BUILDAH_ISOLATION environment variable to override.")
	fs.StringVarP(&flags.Memory, "memory", "m", "", "memory limit (format: <number>[<unit>], where unit = b, k, m or g)")
	fs.StringVar(&flags.MemorySwap, "memory-swap", "", "swap limit equal to memory plus swap: '-1' to enable unlimited swap")
	fs.StringArrayVar(&flags.SecurityOpt, "security-opt", []string{}, "security options (default [])")
	fs.StringVar(&flags.ShmSize, "shm-size", "65536k", "size of '/dev/shm'. The format is `<number><unit>`.")
	fs.StringSliceVar(&flags.Ulimit, "ulimit", []string{}, "ulimit options (default [])")
	fs.StringSliceVarP(&flags.Volumes, "volume", "v", []string{}, "bind mount a volume into the container (default [])")

	// Add in the usernamespace and namespaceflags
	usernsFlags := GetUserNSFlags(usernsResults)
	namespaceFlags := GetNameSpaceFlags(namespaceResults)
	fs.AddFlagSet(&usernsFlags)
	fs.AddFlagSet(&namespaceFlags)

	return fs
}

// UseLayers returns true if BUILDAH_LAYERS is set to "1" or "true"
// otherwise it returns false
func UseLayers() bool {
	layers := os.Getenv("BUILDAH_LAYERS")
	if strings.ToLower(layers) == "true" || layers == "1" {
		return true
	}
	return false
}

// DefaultFormat returns the default image format
func DefaultFormat() string {
	format := os.Getenv("BUILDAH_FORMAT")
	if format != "" {
		return format
	}
	return buildah.OCI
}

// DefaultIsolation returns the default image format
func DefaultIsolation() string {
	isolation := os.Getenv("BUILDAH_ISOLATION")
	if isolation != "" {
		return isolation
	}
	return buildah.OCI
}

// DefaultHistory returns the default add-history setting
func DefaultHistory() bool {
	history := os.Getenv("BUILDAH_HISTORY")
	if strings.ToLower(history) == "true" || history == "1" {
		return true
	}
	return false
}

func VerifyFlagsArgsOrder(args []string) error {
	for _, arg := range args {
		if strings.HasPrefix(arg, "-") {
			return errors.Errorf("No options (%s) can be specified after the image or container name", arg)
		}
	}
	return nil
}

func GetDefaultAuthFile() string {
	authfile := os.Getenv("REGISTRY_AUTH_FILE")
	if authfile != "" {
		return authfile
	}
	runtimeDir := os.Getenv("XDG_RUNTIME_DIR")
	if runtimeDir != "" {
		return filepath.Join(runtimeDir, "containers/auth.json")
	}
	return ""
}
<commit_msg>pkg/cli: panic when flags can't be hidden<commit_after>package cli

// the cli package contains urfave/cli related structs that help make up
// the command line for buildah commands. it resides here so other projects
// that vendor in this code can use them too.

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

	"github.com/containers/buildah"
	"github.com/containers/buildah/util"
	"github.com/opencontainers/runtime-spec/specs-go"
	"github.com/pkg/errors"
	"github.com/spf13/pflag"
)

// LayerResults represents the results of the layer flags
type LayerResults struct {
	ForceRm bool
	Layers  bool
}

// UserNSResults represents the results for the UserNS flags
type UserNSResults struct {
	UserNS            string
	UserNSUIDMap      []string
	UserNSGIDMap      []string
	UserNSUIDMapUser  string
	UserNSGIDMapGroup string
}

// NameSpaceResults represents the results for Namespace flags
type NameSpaceResults struct {
	IPC           string
	Network       string
	CNIConfigDir  string
	CNIPlugInPath string
	PID           string
	UTS           string
}

// BudResults represents the results for Bud flags
type BudResults struct {
	Annotation          []string
	Authfile            string
	BuildArg            []string
	CacheFrom           string
	CertDir             string
	Compress            bool
	Creds               string
	DisableCompression  bool
	DisableContentTrust bool
	File                []string
	Format              string
	Iidfile             string
	Label               []string
	Logfile             string
	Loglevel            int
	NoCache             bool
	Platform            string
	Pull                bool
	PullAlways          bool
	Quiet               bool
	Rm                  bool
	Runtime             string
	RuntimeFlags        []string
	SignaturePolicy     string
	Squash              bool
	Tag                 []string
	Target              string
	TlsVerify           bool
}

// FromAndBugResults represents the results for common flags
// in bud and from
type FromAndBudResults struct {
	AddHost      []string
	BlobCache    string
	CapAdd       []string
	CapDrop      []string
	CgroupParent string
	CPUPeriod    uint64
	CPUQuota     int64
	CPUSetCPUs   string
	CPUSetMems   string
	CPUShares    uint64
	DNSSearch    []string
	DNSServers   []string
	DNSOptions   []string
	HttpProxy    bool
	Isolation    string
	Memory       string
	MemorySwap   string
	SecurityOpt  []string
	ShmSize      string
	Ulimit       []string
	Volumes      []string
}

// GetUserNSFlags returns the common flags for usernamespace
func GetUserNSFlags(flags *UserNSResults) pflag.FlagSet {
	usernsFlags := pflag.FlagSet{}
	usernsFlags.StringVar(&flags.UserNS, "userns", "", "'container', `path` of user namespace to join, or 'host'")
	usernsFlags.StringSliceVar(&flags.UserNSUIDMap, "userns-uid-map", []string{}, "`containerID:hostID:length` UID mapping to use in user namespace")
	usernsFlags.StringSliceVar(&flags.UserNSGIDMap, "userns-gid-map", []string{}, "`containerID:hostID:length` GID mapping to use in user namespace")
	usernsFlags.StringVar(&flags.UserNSUIDMapUser, "userns-uid-map-user", "", "`name` of entries from /etc/subuid to use to set user namespace UID mapping")
	usernsFlags.StringVar(&flags.UserNSGIDMapGroup, "userns-gid-map-group", "", "`name` of entries from /etc/subgid to use to set user namespace GID mapping")
	return usernsFlags
}

// GetNameSpaceFlags returns the common flags for a namespace menu
func GetNameSpaceFlags(flags *NameSpaceResults) pflag.FlagSet {
	fs := pflag.FlagSet{}
	fs.StringVar(&flags.IPC, string(specs.IPCNamespace), "", "'container', `path` of IPC namespace to join, or 'host'")
	fs.StringVar(&flags.Network, string(specs.NetworkNamespace), "", "'container', `path` of network namespace to join, or 'host'")
	// TODO How do we alias net and network?
	fs.StringVar(&flags.Network, "net", "", "'container', `path` of network namespace to join, or 'host'")
	if err := fs.MarkHidden("net"); err != nil {
		panic(fmt.Sprintf("error marking net flag as hidden: %v", err))
	}
	fs.StringVar(&flags.CNIConfigDir, "cni-config-dir", util.DefaultCNIConfigDir, "`directory` of CNI configuration files")
	fs.StringVar(&flags.CNIPlugInPath, "cni-plugin-path", util.DefaultCNIPluginPath, "`path` of CNI network plugins")
	fs.StringVar(&flags.PID, string(specs.PIDNamespace), "", "container, `path` of PID namespace to join, or 'host'")
	fs.StringVar(&flags.UTS, string(specs.UTSNamespace), "", "container, :`path` of UTS namespace to join, or 'host'")
	return fs
}

// GetLayerFlags returns the common flags for layers
func GetLayerFlags(flags *LayerResults) pflag.FlagSet {
	fs := pflag.FlagSet{}
	fs.BoolVar(&flags.ForceRm, "force-rm", false, "Always remove intermediate containers after a build, even if the build is unsuccessful.")
	fs.BoolVar(&flags.Layers, "layers", UseLayers(), fmt.Sprintf("cache intermediate layers during build. Use BUILDAH_LAYERS environment variable to override."))
	return fs
}

// GetBudFlags returns common bud flags
func GetBudFlags(flags *BudResults) pflag.FlagSet {
	fs := pflag.FlagSet{}
	fs.StringArrayVar(&flags.Annotation, "annotation", []string{}, "Set metadata for an image (default [])")
	fs.StringVar(&flags.Authfile, "authfile", GetDefaultAuthFile(), "path of the authentication file.")
	fs.StringArrayVar(&flags.BuildArg, "build-arg", []string{}, "`argument=value` to supply to the builder")
	fs.StringVar(&flags.CacheFrom, "cache-from", "", "Images to utilise as potential cache sources. The build process does not currently support caching so this is a NOOP.")
	fs.StringVar(&flags.CertDir, "cert-dir", "", "use certificates at the specified path to access the registry")
	fs.BoolVar(&flags.Compress, "compress", false, "This is legacy option, which has no effect on the image")
	fs.StringVar(&flags.Creds, "creds", "", "use `[username[:password]]` for accessing the registry")
	fs.BoolVarP(&flags.DisableCompression, "disable-compression", "D", true, "don't compress layers by default")
	fs.BoolVar(&flags.DisableContentTrust, "disable-content-trust", false, "This is a Docker specific option and is a NOOP")
	fs.StringSliceVarP(&flags.File, "file", "f", []string{}, "`pathname or URL` of a Dockerfile")
	fs.StringVar(&flags.Format, "format", DefaultFormat(), "`format` of the built image's manifest and metadata. Use BUILDAH_FORMAT environment variable to override.")
	fs.StringVar(&flags.Iidfile, "iidfile", "", "`file` to write the image ID to")
	fs.StringArrayVar(&flags.Label, "label", []string{}, "Set metadata for an image (default [])")
	fs.BoolVar(&flags.NoCache, "no-cache", false, "Do not use existing cached images for the container build. Build from the start with a new set of cached layers.")
	fs.StringVar(&flags.Logfile, "logfile", "", "log to `file` instead of stdout/stderr")
	fs.IntVar(&flags.Loglevel, "loglevel", 0, "adjust logging level (range from -2 to 3)")
	fs.StringVar(&flags.Platform, "platform", "", "CLI compatibility: no action or effect")
	fs.BoolVar(&flags.Pull, "pull", true, "pull the image if not present")
	fs.BoolVar(&flags.PullAlways, "pull-always", false, "pull the image, even if a version is present")
	fs.BoolVarP(&flags.Quiet, "quiet", "q", false, "refrain from announcing build instructions and image read/write progress")
	fs.BoolVar(&flags.Rm, "rm", true, "Remove intermediate containers after a successful build")
	fs.StringVar(&flags.Runtime, "runtime", util.Runtime(), "`path` to an alternate runtime. Use BUILDAH_RUNTIME environment variable to override.")
	fs.StringSliceVar(&flags.RuntimeFlags, "runtime-flag", []string{}, "add global flags for the container runtime")
	fs.StringVar(&flags.SignaturePolicy, "signature-policy", "", "`pathname` of signature policy file (not usually used)")
	fs.BoolVar(&flags.Squash, "squash", false, "Squash newly built layers into a single new layer.")
	fs.StringArrayVarP(&flags.Tag, "tag", "t", []string{}, "tagged `name` to apply to the built image")
	fs.StringVar(&flags.Target, "target", "", "set the target build stage to build")
	fs.BoolVar(&flags.TlsVerify, "tls-verify", true, "require HTTPS and verify certificates when accessing the registry")
	return fs
}

func GetFromAndBudFlags(flags *FromAndBudResults, usernsResults *UserNSResults, namespaceResults *NameSpaceResults) pflag.FlagSet {
	fs := pflag.FlagSet{}
	fs.StringSliceVar(&flags.AddHost, "add-host", []string{}, "add a custom host-to-IP mapping (`host:ip`) (default [])")
	fs.StringVar(&flags.BlobCache, "blob-cache", "", "assume image blobs in the specified directory will be available for pushing")
	if err := fs.MarkHidden("blob-cache"); err != nil {
		panic(fmt.Sprintf("error marking net flag as hidden: %v", err))
	}
	fs.StringSliceVar(&flags.CapAdd, "cap-add", []string{}, "add the specified capability when running (default [])")
	fs.StringSliceVar(&flags.CapDrop, "cap-drop", []string{}, "drop the specified capability when running (default [])")
	fs.StringVar(&flags.CgroupParent, "cgroup-parent", "", "optional parent cgroup for the container")
	fs.Uint64Var(&flags.CPUPeriod, "cpu-period", 0, "limit the CPU CFS (Completely Fair Scheduler) period")
	fs.Int64Var(&flags.CPUQuota, "cpu-quota", 0, "limit the CPU CFS (Completely Fair Scheduler) quota")
	fs.Uint64VarP(&flags.CPUShares, "cpu-shares", "c", 0, "CPU shares (relative weight)")
	fs.StringVar(&flags.CPUSetCPUs, "cpuset-cpus", "", "CPUs in which to allow execution (0-3, 0,1)")
	fs.StringVar(&flags.CPUSetMems, "cpuset-mems", "", "memory nodes (MEMs) in which to allow execution (0-3, 0,1). Only effective on NUMA systems.")
	fs.StringSliceVar(&flags.DNSSearch, "dns-search", []string{}, "Set custom DNS search domains")
	fs.StringSliceVar(&flags.DNSServers, "dns", []string{}, "Set custom DNS servers")
	fs.StringSliceVar(&flags.DNSOptions, "dns-option", []string{}, "Set custom DNS options")
	fs.BoolVar(&flags.HttpProxy, "http-proxy", true, "pass thru HTTP Proxy environment variables")
	fs.StringVar(&flags.Isolation, "isolation", DefaultIsolation(), "`type` of process isolation to use. Use BUILDAH_ISOLATION environment variable to override.")
	fs.StringVarP(&flags.Memory, "memory", "m", "", "memory limit (format: <number>[<unit>], where unit = b, k, m or g)")
	fs.StringVar(&flags.MemorySwap, "memory-swap", "", "swap limit equal to memory plus swap: '-1' to enable unlimited swap")
	fs.StringArrayVar(&flags.SecurityOpt, "security-opt", []string{}, "security options (default [])")
	fs.StringVar(&flags.ShmSize, "shm-size", "65536k", "size of '/dev/shm'. The format is `<number><unit>`.")
	fs.StringSliceVar(&flags.Ulimit, "ulimit", []string{}, "ulimit options (default [])")
	fs.StringSliceVarP(&flags.Volumes, "volume", "v", []string{}, "bind mount a volume into the container (default [])")

	// Add in the usernamespace and namespaceflags
	usernsFlags := GetUserNSFlags(usernsResults)
	namespaceFlags := GetNameSpaceFlags(namespaceResults)
	fs.AddFlagSet(&usernsFlags)
	fs.AddFlagSet(&namespaceFlags)

	return fs
}

// UseLayers returns true if BUILDAH_LAYERS is set to "1" or "true"
// otherwise it returns false
func UseLayers() bool {
	layers := os.Getenv("BUILDAH_LAYERS")
	if strings.ToLower(layers) == "true" || layers == "1" {
		return true
	}
	return false
}

// DefaultFormat returns the default image format
func DefaultFormat() string {
	format := os.Getenv("BUILDAH_FORMAT")
	if format != "" {
		return format
	}
	return buildah.OCI
}

// DefaultIsolation returns the default image format
func DefaultIsolation() string {
	isolation := os.Getenv("BUILDAH_ISOLATION")
	if isolation != "" {
		return isolation
	}
	return buildah.OCI
}

// DefaultHistory returns the default add-history setting
func DefaultHistory() bool {
	history := os.Getenv("BUILDAH_HISTORY")
	if strings.ToLower(history) == "true" || history == "1" {
		return true
	}
	return false
}

func VerifyFlagsArgsOrder(args []string) error {
	for _, arg := range args {
		if strings.HasPrefix(arg, "-") {
			return errors.Errorf("No options (%s) can be specified after the image or container name", arg)
		}
	}
	return nil
}

func GetDefaultAuthFile() string {
	authfile := os.Getenv("REGISTRY_AUTH_FILE")
	if authfile != "" {
		return authfile
	}
	runtimeDir := os.Getenv("XDG_RUNTIME_DIR")
	if runtimeDir != "" {
		return filepath.Join(runtimeDir, "containers/auth.json")
	}
	return ""
}
<|endoftext|>


<commit_before>package fs

import (
	"bytes"
	"os"
	"path/filepath"
	"strings"
	"sync"
	"time"
)

// NewVirtualFilesystem yields an in-memory filesystem.
func NewVirtualFilesystem() Filesystem {
	return &virtualFilesystem{
		files: map[string]*virtualFile{},
	}
}

type virtualFilesystem struct {
	mtx   sync.RWMutex
	files map[string]*virtualFile
}

func (fs *virtualFilesystem) Create(path string) (File, error) {
	fs.mtx.Lock()
	defer fs.mtx.Unlock()
	// os.Create truncates any existing file. So we do, too.
	f := &virtualFile{path, bytes.Buffer{}, time.Now(), time.Now()}
	fs.files[path] = f
	return f, nil
}

func (fs *virtualFilesystem) Open(path string) (File, error) {
	fs.mtx.Lock()
	defer fs.mtx.Unlock()
	f, ok := fs.files[path]
	if !ok {
		return nil, os.ErrNotExist
	}
	return f, nil
}

func (fs *virtualFilesystem) Remove(path string) error {
	fs.mtx.Lock()
	defer fs.mtx.Unlock()
	if _, ok := fs.files[path]; !ok {
		return os.ErrNotExist
	}
	delete(fs.files, path)
	return nil
}

func (fs *virtualFilesystem) Rename(oldname, newname string) error {
	fs.mtx.Lock()
	defer fs.mtx.Unlock()
	f, ok := fs.files[oldname]
	if !ok {
		return os.ErrNotExist
	}
	delete(fs.files, oldname)
	fs.files[newname] = f // potentially destructive to newname!
	return nil
}

func (fs *virtualFilesystem) Exists(path string) bool {
	fs.mtx.Lock()
	defer fs.mtx.Unlock()
	_, ok := fs.files[path]
	return ok
}

func (fs *virtualFilesystem) MkdirAll(path string) error {
	return nil
}

func (fs *virtualFilesystem) Chtimes(path string, atime, mtime time.Time) error {
	fs.mtx.Lock()
	defer fs.mtx.Unlock()
	f, ok := fs.files[path]
	if !ok {
		return os.ErrNotExist
	}
	f.atime, f.mtime = atime, mtime
	return nil
}

func (fs *virtualFilesystem) Walk(root string, walkFn filepath.WalkFunc) error {
	fs.mtx.Lock()
	defer fs.mtx.Unlock()
	for path, f := range fs.files {
		if !strings.HasPrefix(path, root) {
			continue // TODO(pb): this heuristic could be better, if necessary
		}
		if err := walkFn(path, virtualFileInfo{
			name:  f.name,
			size:  int64(f.buf.Len()),
			mtime: f.mtime,
		}, nil); err != nil {
			return err
		}
	}
	return nil
}

func (fs *virtualFilesystem) Lock(path string) (r Releaser, existed bool, err error) {
	fs.mtx.Lock()
	defer fs.mtx.Unlock()
	if _, ok := fs.files[path]; ok {
		return virtualReleaser(func() error { return fs.Remove(path) }), true, nil
	}

	// Copy/paste.
	fs.files[path] = &virtualFile{path, bytes.Buffer{}, time.Now(), time.Now()}

	return virtualReleaser(func() error { return fs.Remove(path) }), false, nil
}

type virtualFile struct {
	name  string
	buf   bytes.Buffer
	atime time.Time
	mtime time.Time
}

func (f *virtualFile) Read(p []byte) (int, error)  { return f.buf.Read(p) }
func (f *virtualFile) Write(p []byte) (int, error) { return f.buf.Write(p) }
func (f *virtualFile) Close() error                { return nil }
func (f *virtualFile) Name() string                { return f.name }
func (f *virtualFile) Size() int64                 { return int64(f.buf.Len()) }
func (f *virtualFile) Sync() error                 { return nil }

type virtualFileInfo struct {
	name  string
	size  int64
	mtime time.Time
}

func (fi virtualFileInfo) Name() string       { return fi.name }
func (fi virtualFileInfo) Size() int64        { return fi.size }
func (fi virtualFileInfo) Mode() os.FileMode  { return os.FileMode(0666) }
func (fi virtualFileInfo) ModTime() time.Time { return fi.mtime }
func (fi virtualFileInfo) IsDir() bool        { return false }
func (fi virtualFileInfo) Sys() interface{}   { return nil }

type virtualReleaser func() error

func (r virtualReleaser) Release() error { return r() }
<commit_msg>pkg/fs: fix races in virtual FS<commit_after>package fs

import (
	"bytes"
	"os"
	"path/filepath"
	"strings"
	"sync"
	"time"
)

// NewVirtualFilesystem yields an in-memory filesystem.
func NewVirtualFilesystem() Filesystem {
	return &virtualFilesystem{
		files: map[string]*virtualFile{},
	}
}

type virtualFilesystem struct {
	mtx   sync.RWMutex
	files map[string]*virtualFile
}

func (fs *virtualFilesystem) Create(path string) (File, error) {
	fs.mtx.Lock()
	defer fs.mtx.Unlock()
	// os.Create truncates any existing file. So we do, too.
	f := &virtualFile{
		name:  path,
		atime: time.Now(),
		mtime: time.Now(),
	}
	fs.files[path] = f
	return f, nil
}

func (fs *virtualFilesystem) Open(path string) (File, error) {
	fs.mtx.Lock()
	defer fs.mtx.Unlock()
	f, ok := fs.files[path]
	if !ok {
		return nil, os.ErrNotExist
	}
	return f, nil
}

func (fs *virtualFilesystem) Remove(path string) error {
	fs.mtx.Lock()
	defer fs.mtx.Unlock()
	if _, ok := fs.files[path]; !ok {
		return os.ErrNotExist
	}
	delete(fs.files, path)
	return nil
}

func (fs *virtualFilesystem) Rename(oldname, newname string) error {
	fs.mtx.Lock()
	defer fs.mtx.Unlock()
	f, ok := fs.files[oldname]
	if !ok {
		return os.ErrNotExist
	}
	delete(fs.files, oldname)
	fs.files[newname] = f // potentially destructive to newname!
	return nil
}

func (fs *virtualFilesystem) Exists(path string) bool {
	fs.mtx.Lock()
	defer fs.mtx.Unlock()
	_, ok := fs.files[path]
	return ok
}

func (fs *virtualFilesystem) MkdirAll(path string) error {
	return nil
}

func (fs *virtualFilesystem) Chtimes(path string, atime, mtime time.Time) error {
	fs.mtx.Lock()
	defer fs.mtx.Unlock()
	f, ok := fs.files[path]
	if !ok {
		return os.ErrNotExist
	}
	f.atime, f.mtime = atime, mtime
	return nil
}

func (fs *virtualFilesystem) Walk(root string, walkFn filepath.WalkFunc) error {
	fs.mtx.Lock()
	defer fs.mtx.Unlock()
	for path, f := range fs.files {
		if !strings.HasPrefix(path, root) {
			continue // TODO(pb): this heuristic could be better, if necessary
		}
		if err := walkFn(path, virtualFileInfo{
			name:  f.name,
			size:  int64(f.buf.Len()),
			mtime: f.mtime,
		}, nil); err != nil {
			return err
		}
	}
	return nil
}

func (fs *virtualFilesystem) Lock(path string) (r Releaser, existed bool, err error) {
	fs.mtx.Lock()
	defer fs.mtx.Unlock()
	if _, ok := fs.files[path]; ok {
		return virtualReleaser(func() error { return fs.Remove(path) }), true, nil
	}

	// Copy/paste.
	fs.files[path] = &virtualFile{
		name:  path,
		atime: time.Now(),
		mtime: time.Now(),
	}

	return virtualReleaser(func() error { return fs.Remove(path) }), false, nil
}

type virtualFile struct {
	name  string
	mtx   sync.Mutex
	buf   bytes.Buffer
	atime time.Time
	mtime time.Time
}

func (f *virtualFile) Read(p []byte) (int, error) {
	f.mtx.Lock()
	defer f.mtx.Unlock()
	return f.buf.Read(p)
}

func (f *virtualFile) Write(p []byte) (int, error) {
	f.mtx.Lock()
	defer f.mtx.Unlock()
	return f.buf.Write(p)
}

func (f *virtualFile) Close() error { return nil }
func (f *virtualFile) Name() string { return f.name }

func (f *virtualFile) Size() int64 {
	f.mtx.Lock()
	defer f.mtx.Unlock()
	return int64(f.buf.Len())
}

func (f *virtualFile) Sync() error { return nil }

type virtualFileInfo struct {
	name  string
	size  int64
	mtime time.Time
}

func (fi virtualFileInfo) Name() string       { return fi.name }
func (fi virtualFileInfo) Size() int64        { return fi.size }
func (fi virtualFileInfo) Mode() os.FileMode  { return os.FileMode(0666) }
func (fi virtualFileInfo) ModTime() time.Time { return fi.mtime }
func (fi virtualFileInfo) IsDir() bool        { return false }
func (fi virtualFileInfo) Sys() interface{}   { return nil }

type virtualReleaser func() error

func (r virtualReleaser) Release() error { return r() }
<|endoftext|>


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

// This code is a modified version of path/filepath/symlink.go from the Go standard library.

package symlink

import (
	"bytes"
	"errors"
	"os"
	"path/filepath"
	"strings"

	"github.com/docker/docker/pkg/system"
)

// FollowSymlinkInScope is a wrapper around evalSymlinksInScope that returns an
// absolute path. This function handles paths in a platform-agnostic manner.
func FollowSymlinkInScope(path, root string) (string, error) {
	path, err := filepath.Abs(filepath.FromSlash(path))
	if err != nil {
		return "", err
	}
	root, err = filepath.Abs(filepath.FromSlash(root))
	if err != nil {
		return "", err
	}
	return evalSymlinksInScope(path, root)
}

// evalSymlinksInScope will evaluate symlinks in `path` within a scope `root` and return
// a result guaranteed to be contained within the scope `root`, at the time of the call.
// Symlinks in `root` are not evaluated and left as-is.
// Errors encountered while attempting to evaluate symlinks in path will be returned.
// Non-existing paths are valid and do not constitute an error.
// `path` has to contain `root` as a prefix, or else an error will be returned.
// Trying to break out from `root` does not constitute an error.
//
// Example:
//   If /foo/bar -> /outside,
//   FollowSymlinkInScope("/foo/bar", "/foo") == "/foo/outside" instead of "/oustide"
//
// IMPORTANT: it is the caller's responsibility to call evalSymlinksInScope *after* relevant symlinks
// are created and not to create subsequently, additional symlinks that could potentially make a
// previously-safe path, unsafe. Example: if /foo/bar does not exist, evalSymlinksInScope("/foo/bar", "/foo")
// would return "/foo/bar". If one makes /foo/bar a symlink to /baz subsequently, then "/foo/bar" should
// no longer be considered safely contained in "/foo".
func evalSymlinksInScope(path, root string) (string, error) {
	root = filepath.Clean(root)
	if path == root {
		return path, nil
	}
	if !strings.HasPrefix(path, root) {
		return "", errors.New("evalSymlinksInScope: " + path + " is not in " + root)
	}
	const maxIter = 255
	originalPath := path
	// given root of "/a" and path of "/a/b/../../c" we want path to be "/b/../../c"
	path = path[len(root):]
	if root == string(filepath.Separator) {
		path = string(filepath.Separator) + path
	}
	if !strings.HasPrefix(path, string(filepath.Separator)) {
		return "", errors.New("evalSymlinksInScope: " + path + " is not in " + root)
	}
	path = filepath.Clean(path)
	// consume path by taking each frontmost path element,
	// expanding it if it's a symlink, and appending it to b
	var b bytes.Buffer
	// b here will always be considered to be the "current absolute path inside
	// root" when we append paths to it, we also append a slash and use
	// filepath.Clean after the loop to trim the trailing slash
	for n := 0; path != ""; n++ {
		if n > maxIter {
			return "", errors.New("evalSymlinksInScope: too many links in " + originalPath)
		}

		// find next path component, p
		i := strings.IndexRune(path, filepath.Separator)
		var p string
		if i == -1 {
			p, path = path, ""
		} else {
			p, path = path[:i], path[i+1:]
		}

		if p == "" {
			continue
		}

		// this takes a b.String() like "b/../" and a p like "c" and turns it
		// into "/b/../c" which then gets filepath.Cleaned into "/c" and then
		// root gets prepended and we Clean again (to remove any trailing slash
		// if the first Clean gave us just "/")
		cleanP := filepath.Clean(string(filepath.Separator) + b.String() + p)
		if isDriveOrRoot(cleanP) {
			// never Lstat "/" itself, or drive letters on Windows
			b.Reset()
			continue
		}
		fullP := filepath.Clean(root + cleanP)

		fi, err := os.Lstat(fullP)
		if os.IsNotExist(err) {
			// if p does not exist, accept it
			b.WriteString(p)
			b.WriteRune(filepath.Separator)
			continue
		}
		if err != nil {
			return "", err
		}
		if fi.Mode()&os.ModeSymlink == 0 {
			b.WriteString(p)
			b.WriteRune(filepath.Separator)
			continue
		}

		// it's a symlink, put it at the front of path
		dest, err := os.Readlink(fullP)
		if err != nil {
			return "", err
		}
		if system.IsAbs(dest) {
			b.Reset()
		}
		path = dest + string(filepath.Separator) + path
	}

	// see note above on "fullP := ..." for why this is double-cleaned and
	// what's happening here
	return filepath.Clean(root + filepath.Clean(string(filepath.Separator)+b.String())), nil
}

// EvalSymlinks returns the path name after the evaluation of any symbolic
// links.
// If path is relative the result will be relative to the current directory,
// unless one of the components is an absolute symbolic link.
// This version has been updated to support long paths prepended with `\\?\`.
func EvalSymlinks(path string) (string, error) {
	return evalSymlinks(path)
}
<commit_msg>Spelling fixes<commit_after>// Copyright 2012 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE.BSD file.

// This code is a modified version of path/filepath/symlink.go from the Go standard library.

package symlink

import (
	"bytes"
	"errors"
	"os"
	"path/filepath"
	"strings"

	"github.com/docker/docker/pkg/system"
)

// FollowSymlinkInScope is a wrapper around evalSymlinksInScope that returns an
// absolute path. This function handles paths in a platform-agnostic manner.
func FollowSymlinkInScope(path, root string) (string, error) {
	path, err := filepath.Abs(filepath.FromSlash(path))
	if err != nil {
		return "", err
	}
	root, err = filepath.Abs(filepath.FromSlash(root))
	if err != nil {
		return "", err
	}
	return evalSymlinksInScope(path, root)
}

// evalSymlinksInScope will evaluate symlinks in `path` within a scope `root` and return
// a result guaranteed to be contained within the scope `root`, at the time of the call.
// Symlinks in `root` are not evaluated and left as-is.
// Errors encountered while attempting to evaluate symlinks in path will be returned.
// Non-existing paths are valid and do not constitute an error.
// `path` has to contain `root` as a prefix, or else an error will be returned.
// Trying to break out from `root` does not constitute an error.
//
// Example:
//   If /foo/bar -> /outside,
//   FollowSymlinkInScope("/foo/bar", "/foo") == "/foo/outside" instead of "/outside"
//
// IMPORTANT: it is the caller's responsibility to call evalSymlinksInScope *after* relevant symlinks
// are created and not to create subsequently, additional symlinks that could potentially make a
// previously-safe path, unsafe. Example: if /foo/bar does not exist, evalSymlinksInScope("/foo/bar", "/foo")
// would return "/foo/bar". If one makes /foo/bar a symlink to /baz subsequently, then "/foo/bar" should
// no longer be considered safely contained in "/foo".
func evalSymlinksInScope(path, root string) (string, error) {
	root = filepath.Clean(root)
	if path == root {
		return path, nil
	}
	if !strings.HasPrefix(path, root) {
		return "", errors.New("evalSymlinksInScope: " + path + " is not in " + root)
	}
	const maxIter = 255
	originalPath := path
	// given root of "/a" and path of "/a/b/../../c" we want path to be "/b/../../c"
	path = path[len(root):]
	if root == string(filepath.Separator) {
		path = string(filepath.Separator) + path
	}
	if !strings.HasPrefix(path, string(filepath.Separator)) {
		return "", errors.New("evalSymlinksInScope: " + path + " is not in " + root)
	}
	path = filepath.Clean(path)
	// consume path by taking each frontmost path element,
	// expanding it if it's a symlink, and appending it to b
	var b bytes.Buffer
	// b here will always be considered to be the "current absolute path inside
	// root" when we append paths to it, we also append a slash and use
	// filepath.Clean after the loop to trim the trailing slash
	for n := 0; path != ""; n++ {
		if n > maxIter {
			return "", errors.New("evalSymlinksInScope: too many links in " + originalPath)
		}

		// find next path component, p
		i := strings.IndexRune(path, filepath.Separator)
		var p string
		if i == -1 {
			p, path = path, ""
		} else {
			p, path = path[:i], path[i+1:]
		}

		if p == "" {
			continue
		}

		// this takes a b.String() like "b/../" and a p like "c" and turns it
		// into "/b/../c" which then gets filepath.Cleaned into "/c" and then
		// root gets prepended and we Clean again (to remove any trailing slash
		// if the first Clean gave us just "/")
		cleanP := filepath.Clean(string(filepath.Separator) + b.String() + p)
		if isDriveOrRoot(cleanP) {
			// never Lstat "/" itself, or drive letters on Windows
			b.Reset()
			continue
		}
		fullP := filepath.Clean(root + cleanP)

		fi, err := os.Lstat(fullP)
		if os.IsNotExist(err) {
			// if p does not exist, accept it
			b.WriteString(p)
			b.WriteRune(filepath.Separator)
			continue
		}
		if err != nil {
			return "", err
		}
		if fi.Mode()&os.ModeSymlink == 0 {
			b.WriteString(p)
			b.WriteRune(filepath.Separator)
			continue
		}

		// it's a symlink, put it at the front of path
		dest, err := os.Readlink(fullP)
		if err != nil {
			return "", err
		}
		if system.IsAbs(dest) {
			b.Reset()
		}
		path = dest + string(filepath.Separator) + path
	}

	// see note above on "fullP := ..." for why this is double-cleaned and
	// what's happening here
	return filepath.Clean(root + filepath.Clean(string(filepath.Separator)+b.String())), nil
}

// EvalSymlinks returns the path name after the evaluation of any symbolic
// links.
// If path is relative the result will be relative to the current directory,
// unless one of the components is an absolute symbolic link.
// This version has been updated to support long paths prepended with `\\?\`.
func EvalSymlinks(path string) (string, error) {
	return evalSymlinks(path)
}
<|endoftext|>


<commit_before>package client

import (
	"errors"
	"fmt"
	"time"

	"strconv"

	"sourcegraph.com/sourcegraph/api_router"
	"sourcegraph.com/sourcegraph/srcgraph/db_common"
	"sourcegraph.com/sourcegraph/srcgraph/repo"
	"sourcegraph.com/sourcegraph/srcgraph/task2"
)

// BuildsService communicates with the build-related endpoints in the
// Sourcegraph API.
type BuildsService interface {
	// Get fetches a build.
	Get(build BuildSpec, opt *BuildGetOptions) (*Build, Response, error)

	// List builds.
	List(opt *BuildListOptions) ([]*Build, Response, error)

	// ListByRepository lists builds for a repository.
	ListByRepository(repo RepositorySpec, opt *BuildListByRepositoryOptions) ([]*Build, Response, error)

	// Create a new build. The build will run asynchronously (Create does not
	// wait for it to return. To monitor the build's status, use Get.)
	Create(repo RepositorySpec, conf BuildConfig) (*Build, Response, error)

	// ListBuildTasks lists the tasks associated with a build.
	ListBuildTasks(build BuildSpec, opt *BuildTaskListOptions) ([]*BuildTask, Response, error)
}

type buildsService struct {
	client *Client
}

var _ BuildsService = &buildsService{}

type BuildSpec struct {
	BID int64
}

func (s *BuildSpec) RouteVars() map[string]string {
	return map[string]string{"BID": fmt.Sprintf("%d", s.BID)}
}

// A Build represents a scheduled, completed, or failed repository analysis and
// import job.
type Build struct {
	BID       int64
	Repo      repo.RID
	CommitID  string             `db:"commit_id"`
	CreatedAt time.Time          `db:"created_at"`
	StartedAt db_common.NullTime `db:"started_at"`
	EndedAt   db_common.NullTime `db:"ended_at"`
	Success   bool
	Failure   bool

	// Host is the hostname of the machine that is working on this build.
	Host string

	// Tries is the number of times that this build has started to run. Builds
	// may be retried after a failure or timeout, in which case Tries > 1.
	Tries int

	BuildConfig

	// RepoURI is populated (as a convenience) in results by Get and List but
	// should not be set when creating builds (it will be ignored).
	RepoURI repo.URI `db:"repo_uri" json:",omitempty"`
}

// IDString returns a succinct string that uniquely identifies this build.
func (b *Build) IDString() string { return buildIDString(b.BID) }

func buildIDString(bid int64) string { return "B" + strconv.FormatInt(bid, 36) }

// LogURL is the URL to the logs for this build.
func (b *Build) LogURL() string {
	return task2.LogURLForTag(b.IDString())
}

// A BuildTask represents an individual step of a build.
type BuildTask struct {
	TID int64

	// BID is the build that this task is a part of.
	BID int64

	UnitType string
	Unit     string

	Title string

	StartedAt db_common.NullTime `db:"started_at"`
	EndedAt   db_common.NullTime `db:"ended_at"`

	Success bool
	Failure bool
}

// IDString returns a succinct string that uniquely identifies this build task.
func (t *BuildTask) IDString() string {
	return buildIDString(t.BID) + "-T" + strconv.FormatInt(t.TID, 36)
}

// LogURL is the URL to the logs for this task.
func (t *BuildTask) LogURL() string {
	return task2.LogURLForTag(t.IDString())
}

// BuildConfig configures a repository build.
type BuildConfig struct {
	// Import is whether to import the build data into the database when the
	// build is complete. The data must be imported for Sourcegraph's web app or
	// API to use it, except that unimported build data is available through the
	// BuildData service. (TODO(sqs): BuildData isn't yet implemented.)
	Import bool

	// Queue is whether this build should be enqueued. If enqueued, any worker
	// may begin running this build. If not enqueued, it is up to the client to
	// run the build and update it accordingly.
	Queue bool
}

var ErrBuildNotFound = errors.New("build not found")

type BuildGetOptions struct{}

func (s *buildsService) Get(build BuildSpec, opt *BuildGetOptions) (*Build, Response, error) {
	url, err := s.client.url(api_router.Build, build.RouteVars(), opt)
	if err != nil {
		return nil, nil, err
	}

	req, err := s.client.NewRequest("GET", url.String(), nil)
	if err != nil {
		return nil, nil, err
	}

	var build_ *Build
	resp, err := s.client.Do(req, &build_)
	if err != nil {
		return nil, resp, err
	}

	return build_, nil, nil
}

type BuildListOptions struct {
	Queued    bool `url:",omitempty"`
	Active    bool `url:",omitempty"`
	Ended     bool `url:",omitempty"`
	Succeeded bool `url:",omitempty"`
	Failed    bool `url:",omitempty"`

	ListOptions
}

func (s *buildsService) List(opt *BuildListOptions) ([]*Build, Response, error) {
	url, err := s.client.url(api_router.Builds, nil, opt)
	if err != nil {
		return nil, nil, err
	}

	req, err := s.client.NewRequest("GET", url.String(), nil)
	if err != nil {
		return nil, nil, err
	}

	var builds []*Build
	resp, err := s.client.Do(req, &builds)
	if err != nil {
		return nil, resp, err
	}

	return builds, resp, nil
}

type BuildListByRepositoryOptions struct {
	BuildListOptions
	Rev string `url:",omitempty"`
}

func (s *buildsService) ListByRepository(repo RepositorySpec, opt *BuildListByRepositoryOptions) ([]*Build, Response, error) {
	url, err := s.client.url(api_router.RepositoryBuilds, map[string]string{"RepoURI": repo.URI}, opt)
	if err != nil {
		return nil, nil, err
	}

	req, err := s.client.NewRequest("GET", url.String(), nil)
	if err != nil {
		return nil, nil, err
	}

	var builds []*Build
	resp, err := s.client.Do(req, &builds)
	if err != nil {
		return nil, resp, err
	}

	return builds, resp, nil
}

func (s *buildsService) Create(repo RepositorySpec, conf BuildConfig) (*Build, Response, error) {
	url, err := s.client.url(api_router.RepositoryBuildsCreate, repo.RouteVars(), nil)
	if err != nil {
		return nil, nil, err
	}

	req, err := s.client.NewRequest("POST", url.String(), conf)
	if err != nil {
		return nil, nil, err
	}

	var build *Build
	resp, err := s.client.Do(req, &build)
	if err != nil {
		return nil, resp, err
	}

	return build, resp, nil
}

type BuildTaskListOptions struct{ ListOptions }

func (s *buildsService) ListBuildTasks(build BuildSpec, opt *BuildTaskListOptions) ([]*BuildTask, Response, error) {
	url, err := s.client.url(api_router.BuildTasks, build.RouteVars(), opt)
	if err != nil {
		return nil, nil, err
	}

	req, err := s.client.NewRequest("GET", url.String(), nil)
	if err != nil {
		return nil, nil, err
	}

	var tasks []*BuildTask
	resp, err := s.client.Do(req, &tasks)
	if err != nil {
		return nil, resp, err
	}

	return tasks, resp, nil
}

type MockBuildsService struct {
	Get_              func(build BuildSpec, opt *BuildGetOptions) (*Build, Response, error)
	List_             func(opt *BuildListOptions) ([]*Build, Response, error)
	ListByRepository_ func(repo RepositorySpec, opt *BuildListByRepositoryOptions) ([]*Build, Response, error)
	Create_           func(repo RepositorySpec, conf BuildConfig) (*Build, Response, error)
	ListBuildTasks_   func(build BuildSpec, opt *BuildTaskListOptions) ([]*BuildTask, Response, error)
}

var _ BuildsService = MockBuildsService{}

func (s MockBuildsService) Get(build BuildSpec, opt *BuildGetOptions) (*Build, Response, error) {
	if s.Get_ == nil {
		return nil, &HTTPResponse{}, nil
	}
	return s.Get_(build, opt)
}

func (s MockBuildsService) List(opt *BuildListOptions) ([]*Build, Response, error) {
	if s.List_ == nil {
		return nil, nil, nil
	}
	return s.List_(opt)
}

func (s MockBuildsService) ListByRepository(repo RepositorySpec, opt *BuildListByRepositoryOptions) ([]*Build, Response, error) {
	if s.ListByRepository_ == nil {
		return nil, &HTTPResponse{}, nil
	}
	return s.ListByRepository_(repo, opt)
}

func (s MockBuildsService) Create(repo RepositorySpec, conf BuildConfig) (*Build, Response, error) {
	if s.Create_ == nil {
		return nil, nil, nil
	}
	return s.Create_(repo, conf)
}

func (s MockBuildsService) ListBuildTasks(build BuildSpec, opt *BuildTaskListOptions) ([]*BuildTask, Response, error) {
	if s.ListBuildTasks_ == nil {
		return nil, nil, nil
	}
	return s.ListBuildTasks_(build, opt)
}
<commit_msg>build sort<commit_after>package client

import (
	"errors"
	"fmt"
	"time"

	"strconv"

	"sourcegraph.com/sourcegraph/api_router"
	"sourcegraph.com/sourcegraph/srcgraph/db_common"
	"sourcegraph.com/sourcegraph/srcgraph/repo"
	"sourcegraph.com/sourcegraph/srcgraph/task2"
)

// BuildsService communicates with the build-related endpoints in the
// Sourcegraph API.
type BuildsService interface {
	// Get fetches a build.
	Get(build BuildSpec, opt *BuildGetOptions) (*Build, Response, error)

	// List builds.
	List(opt *BuildListOptions) ([]*Build, Response, error)

	// ListByRepository lists builds for a repository.
	ListByRepository(repo RepositorySpec, opt *BuildListByRepositoryOptions) ([]*Build, Response, error)

	// Create a new build. The build will run asynchronously (Create does not
	// wait for it to return. To monitor the build's status, use Get.)
	Create(repo RepositorySpec, conf BuildConfig) (*Build, Response, error)

	// ListBuildTasks lists the tasks associated with a build.
	ListBuildTasks(build BuildSpec, opt *BuildTaskListOptions) ([]*BuildTask, Response, error)
}

type buildsService struct {
	client *Client
}

var _ BuildsService = &buildsService{}

type BuildSpec struct {
	BID int64
}

func (s *BuildSpec) RouteVars() map[string]string {
	return map[string]string{"BID": fmt.Sprintf("%d", s.BID)}
}

// A Build represents a scheduled, completed, or failed repository analysis and
// import job.
type Build struct {
	BID       int64
	Repo      repo.RID
	CommitID  string             `db:"commit_id"`
	CreatedAt time.Time          `db:"created_at"`
	StartedAt db_common.NullTime `db:"started_at"`
	EndedAt   db_common.NullTime `db:"ended_at"`
	Success   bool
	Failure   bool

	// Host is the hostname of the machine that is working on this build.
	Host string

	// Tries is the number of times that this build has started to run. Builds
	// may be retried after a failure or timeout, in which case Tries > 1.
	Tries int

	BuildConfig

	// RepoURI is populated (as a convenience) in results by Get and List but
	// should not be set when creating builds (it will be ignored).
	RepoURI repo.URI `db:"repo_uri" json:",omitempty"`
}

// IDString returns a succinct string that uniquely identifies this build.
func (b *Build) IDString() string { return buildIDString(b.BID) }

func buildIDString(bid int64) string { return "B" + strconv.FormatInt(bid, 36) }

// LogURL is the URL to the logs for this build.
func (b *Build) LogURL() string {
	return task2.LogURLForTag(b.IDString())
}

// A BuildTask represents an individual step of a build.
type BuildTask struct {
	TID int64

	// BID is the build that this task is a part of.
	BID int64

	UnitType string
	Unit     string

	Title string

	StartedAt db_common.NullTime `db:"started_at"`
	EndedAt   db_common.NullTime `db:"ended_at"`

	Success bool
	Failure bool
}

// IDString returns a succinct string that uniquely identifies this build task.
func (t *BuildTask) IDString() string {
	return buildIDString(t.BID) + "-T" + strconv.FormatInt(t.TID, 36)
}

// LogURL is the URL to the logs for this task.
func (t *BuildTask) LogURL() string {
	return task2.LogURLForTag(t.IDString())
}

// BuildConfig configures a repository build.
type BuildConfig struct {
	// Import is whether to import the build data into the database when the
	// build is complete. The data must be imported for Sourcegraph's web app or
	// API to use it, except that unimported build data is available through the
	// BuildData service. (TODO(sqs): BuildData isn't yet implemented.)
	Import bool

	// Queue is whether this build should be enqueued. If enqueued, any worker
	// may begin running this build. If not enqueued, it is up to the client to
	// run the build and update it accordingly.
	Queue bool
}

var ErrBuildNotFound = errors.New("build not found")

type BuildGetOptions struct{}

func (s *buildsService) Get(build BuildSpec, opt *BuildGetOptions) (*Build, Response, error) {
	url, err := s.client.url(api_router.Build, build.RouteVars(), opt)
	if err != nil {
		return nil, nil, err
	}

	req, err := s.client.NewRequest("GET", url.String(), nil)
	if err != nil {
		return nil, nil, err
	}

	var build_ *Build
	resp, err := s.client.Do(req, &build_)
	if err != nil {
		return nil, resp, err
	}

	return build_, nil, nil
}

type BuildListOptions struct {
	Queued    bool `url:",omitempty"`
	Active    bool `url:",omitempty"`
	Ended     bool `url:",omitempty"`
	Succeeded bool `url:",omitempty"`
	Failed    bool `url:",omitempty"`

	Sort      string `url:",omitempty"`
	Direction string `url:",omitempty"`

	ListOptions
}

func (s *buildsService) List(opt *BuildListOptions) ([]*Build, Response, error) {
	url, err := s.client.url(api_router.Builds, nil, opt)
	if err != nil {
		return nil, nil, err
	}

	req, err := s.client.NewRequest("GET", url.String(), nil)
	if err != nil {
		return nil, nil, err
	}

	var builds []*Build
	resp, err := s.client.Do(req, &builds)
	if err != nil {
		return nil, resp, err
	}

	return builds, resp, nil
}

type BuildListByRepositoryOptions struct {
	BuildListOptions
	Rev string `url:",omitempty"`
}

func (s *buildsService) ListByRepository(repo RepositorySpec, opt *BuildListByRepositoryOptions) ([]*Build, Response, error) {
	url, err := s.client.url(api_router.RepositoryBuilds, map[string]string{"RepoURI": repo.URI}, opt)
	if err != nil {
		return nil, nil, err
	}

	req, err := s.client.NewRequest("GET", url.String(), nil)
	if err != nil {
		return nil, nil, err
	}

	var builds []*Build
	resp, err := s.client.Do(req, &builds)
	if err != nil {
		return nil, resp, err
	}

	return builds, resp, nil
}

func (s *buildsService) Create(repo RepositorySpec, conf BuildConfig) (*Build, Response, error) {
	url, err := s.client.url(api_router.RepositoryBuildsCreate, repo.RouteVars(), nil)
	if err != nil {
		return nil, nil, err
	}

	req, err := s.client.NewRequest("POST", url.String(), conf)
	if err != nil {
		return nil, nil, err
	}

	var build *Build
	resp, err := s.client.Do(req, &build)
	if err != nil {
		return nil, resp, err
	}

	return build, resp, nil
}

type BuildTaskListOptions struct{ ListOptions }

func (s *buildsService) ListBuildTasks(build BuildSpec, opt *BuildTaskListOptions) ([]*BuildTask, Response, error) {
	url, err := s.client.url(api_router.BuildTasks, build.RouteVars(), opt)
	if err != nil {
		return nil, nil, err
	}

	req, err := s.client.NewRequest("GET", url.String(), nil)
	if err != nil {
		return nil, nil, err
	}

	var tasks []*BuildTask
	resp, err := s.client.Do(req, &tasks)
	if err != nil {
		return nil, resp, err
	}

	return tasks, resp, nil
}

type MockBuildsService struct {
	Get_              func(build BuildSpec, opt *BuildGetOptions) (*Build, Response, error)
	List_             func(opt *BuildListOptions) ([]*Build, Response, error)
	ListByRepository_ func(repo RepositorySpec, opt *BuildListByRepositoryOptions) ([]*Build, Response, error)
	Create_           func(repo RepositorySpec, conf BuildConfig) (*Build, Response, error)
	ListBuildTasks_   func(build BuildSpec, opt *BuildTaskListOptions) ([]*BuildTask, Response, error)
}

var _ BuildsService = MockBuildsService{}

func (s MockBuildsService) Get(build BuildSpec, opt *BuildGetOptions) (*Build, Response, error) {
	if s.Get_ == nil {
		return nil, &HTTPResponse{}, nil
	}
	return s.Get_(build, opt)
}

func (s MockBuildsService) List(opt *BuildListOptions) ([]*Build, Response, error) {
	if s.List_ == nil {
		return nil, nil, nil
	}
	return s.List_(opt)
}

func (s MockBuildsService) ListByRepository(repo RepositorySpec, opt *BuildListByRepositoryOptions) ([]*Build, Response, error) {
	if s.ListByRepository_ == nil {
		return nil, &HTTPResponse{}, nil
	}
	return s.ListByRepository_(repo, opt)
}

func (s MockBuildsService) Create(repo RepositorySpec, conf BuildConfig) (*Build, Response, error) {
	if s.Create_ == nil {
		return nil, nil, nil
	}
	return s.Create_(repo, conf)
}

func (s MockBuildsService) ListBuildTasks(build BuildSpec, opt *BuildTaskListOptions) ([]*BuildTask, Response, error) {
	if s.ListBuildTasks_ == nil {
		return nil, nil, nil
	}
	return s.ListBuildTasks_(build, opt)
}
<|endoftext|>


<commit_before>package notations

import "testing"

func TestExtract(t *testing.T) {

	text := `@mention @anothermention #tag $1223.93 some@email.com @ v @`

	notationsMap := Extract(text, "@#$")

	if len(notationsMap["@"]) != 2 {
		t.Error("Extracted invalid notations")
	}

	n := notationsMap["@"][0]
	if n.Value != "mention" && n.Slice.Begin != 0 &&
		text[n.Slice.Begin:n.Slice.End] != n.Value {
		t.Error("Failed to extract mention at the beginning of the text")
	}

	n = notationsMap["@"][1]
	if n.Value != "mention" && text[n.Slice.Begin:n.Slice.End] != n.Value {
		t.Error("Failed to extract mention in middle of the text")
	}

	n = notationsMap["#"][0]
	if n.Value != "tag" && text[n.Slice.Begin:n.Slice.End] != n.Value {
		t.Error("Failed to extract tag")
	}

	n = notationsMap["$"][0]
	if n.Value != "1223.93" && text[n.Slice.Begin:n.Slice.End] != n.Value {
		t.Error("Failed to extract amount")
	}

}
<commit_msg>Update test with example<commit_after>package notations

import (
	"fmt"
	"testing"

	"github.com/jmsegrev/notations"
)

func TestExtract(t *testing.T) {

	text := `@mention @anothermention #tag $1223.93 some@email.com @ v @`

	notationsMap := Extract(text, "@#$")

	if len(notationsMap["@"]) != 2 {
		t.Error("Extracted invalid notations")
	}

	n := notationsMap["@"][0]
	if n.Value != "mention" && n.Slice.Begin != 0 &&
		text[n.Slice.Begin:n.Slice.End] != n.Value {
		t.Error("Failed to extract mention at the beginning of the text")
	}

	n = notationsMap["@"][1]
	if n.Value != "mention" && text[n.Slice.Begin:n.Slice.End] != n.Value {
		t.Error("Failed to extract mention in middle of the text")
	}

	n = notationsMap["#"][0]
	if n.Value != "tag" && text[n.Slice.Begin:n.Slice.End] != n.Value {
		t.Error("Failed to extract tag")
	}

	n = notationsMap["$"][0]
	if n.Value != "1223.93" && text[n.Slice.Begin:n.Slice.End] != n.Value {
		t.Error("Failed to extract amount")
	}

}

func ExampleExtract() {

	txt := `@username assume this is a reply, by @anotherusername that has
	some #tag1 and #tag2. It also works the same way for %any &other *symbol,
	but you provably will only use things like $2377.12.`

	notationsMap := notations.Extract(txt, "@#$%&*$")
	for key, notations := range notationsMap {
		fmt.Printf("%s: ", key)
		for _, notation := range notations {
			fmt.Printf("%s ", notation.Value)
		}
		fmt.Println("")
	}

	// Output:
	// @: username anotherusername
	// #: tag1 tag2
	// %: any
	// &: other
	// *: symbol
	// $: 2377.12

}
<|endoftext|>


<commit_before>package toml

import (
	"encoding"
	"fmt"
	"reflect"
	"time"

	"github.com/pelletier/go-toml/v2/internal/ast"
	"github.com/pelletier/go-toml/v2/internal/tracker"
)

func Unmarshal(data []byte, v interface{}) error {
	p := parser{}
	p.Reset(data)
	d := decoder{}
	return d.FromParser(&p, v)
}

type decoder struct {
	// Tracks position in Go arrays.
	arrayIndexes map[reflect.Value]int

	// Tracks keys that have been seen, with which type.
	seen tracker.Seen
}

func (d *decoder) arrayIndex(append bool, v reflect.Value) int {
	if d.arrayIndexes == nil {
		d.arrayIndexes = make(map[reflect.Value]int, 1)
	}

	idx, ok := d.arrayIndexes[v]

	if !ok {
		d.arrayIndexes[v] = 0
	} else if append {
		idx++
		d.arrayIndexes[v] = idx
	}
	return idx
}

func (d *decoder) FromParser(p *parser, v interface{}) error {
	r := reflect.ValueOf(v)
	if r.Kind() != reflect.Ptr {
		return fmt.Errorf("need to target a pointer, not %s", r.Kind())
	}
	if r.IsNil() {
		return fmt.Errorf("target pointer must be non-nil")
	}

	var skipUntilTable bool
	var root target = valueTarget(r.Elem())
	current := root

	for p.NextExpression() {
		node := p.Expression()

		if node.Kind == ast.KeyValue && skipUntilTable {
			continue
		}

		err := d.seen.CheckExpression(node)
		if err != nil {
			return err
		}

		var found bool
		switch node.Kind {
		case ast.KeyValue:
			err = d.unmarshalKeyValue(current, node)
			found = true
		case ast.Table:
			current, found, err = d.scopeWithKey(root, node.Key())
		case ast.ArrayTable:
			current, found, err = d.scopeWithArrayTable(root, node.Key())
		default:
			panic(fmt.Errorf("this should not be a top level node type: %s", node.Kind))
		}

		if err != nil {
			return err
		}

		if !found {
			skipUntilTable = true
		}
	}

	return p.Error()
}

// scopeWithKey performs target scoping when unmarshaling an ast.KeyValue node.
//
// The goal is to hop from target to target recursively using the names in key.
// Parts of the key should be used to resolve field names for structs, and as
// keys when targeting maps.
//
// When encountering slices, it should always use its last element, and error
// if the slice does not have any.
func (d *decoder) scopeWithKey(x target, key ast.Iterator) (target, bool, error) {
	var err error
	found := true

	for key.Next() {
		n := key.Node()
		x, found, err = d.scopeTableTarget(false, x, string(n.Data))
		if err != nil || !found {
			return nil, found, err
		}
	}
	return x, true, nil
}

// scopeWithArrayTable performs target scoping when unmarshaling an
// ast.ArrayTable node.
//
// It is the same as scopeWithKey, but when scoping the last part of the key
// it creates a new element in the array instead of using the last one.
func (d *decoder) scopeWithArrayTable(x target, key ast.Iterator) (target, bool, error) {
	var err error
	found := true
	for key.Next() {
		n := key.Node()
		if !n.Next().Valid() { // want to stop at one before last
			break
		}
		x, found, err = d.scopeTableTarget(false, x, string(n.Data))
		if err != nil || !found {
			return nil, found, err
		}
	}
	n := key.Node()
	x, found, err = d.scopeTableTarget(false, x, string(n.Data))
	if err != nil || !found {
		return x, found, err
	}

	v := x.get()

	if v.Kind() == reflect.Ptr {
		x, err = scopePtr(x)
		if err != nil {
			return x, false, err
		}
		v = x.get()
	}

	if v.Kind() == reflect.Interface {
		x, err = scopeInterface(true, x)
		if err != nil {
			return x, found, err
		}
		v = x.get()
	}

	switch v.Kind() {
	case reflect.Slice:
		x, err = scopeSlice(true, x)
	case reflect.Array:
		x, err = d.scopeArray(true, x)
	}

	return x, found, err
}

func (d *decoder) unmarshalKeyValue(x target, node ast.Node) error {
	assertNode(ast.KeyValue, node)

	x, found, err := d.scopeWithKey(x, node.Key())
	if err != nil {
		return err
	}

	// A struct in the path was not found. Skip this value.
	if !found {
		return nil
	}

	return d.unmarshalValue(x, node.Value())
}

var textUnmarshalerType = reflect.TypeOf(new(encoding.TextUnmarshaler)).Elem()

func tryTextUnmarshaler(x target, node ast.Node) (bool, error) {
	v := x.get()

	if v.Kind() != reflect.Struct {
		return false, nil
	}
	if v.Type().Implements(textUnmarshalerType) {
		return true, v.Interface().(encoding.TextUnmarshaler).UnmarshalText(node.Data)
	}
	if v.CanAddr() && v.Addr().Type().Implements(textUnmarshalerType) {
		return true, v.Addr().Interface().(encoding.TextUnmarshaler).UnmarshalText(node.Data)
	}
	return false, nil
}

func (d *decoder) unmarshalValue(x target, node ast.Node) error {
	v := x.get()
	if v.Kind() == reflect.Ptr {
		if !v.Elem().IsValid() {
			err := x.set(reflect.New(v.Type().Elem()))
			if err != nil {
				return err
			}
			v = x.get()
		}
		return d.unmarshalValue(valueTarget(v.Elem()), node)
	}

	ok, err := tryTextUnmarshaler(x, node)
	if ok {
		return err
	}

	switch node.Kind {
	case ast.String:
		return unmarshalString(x, node)
	case ast.Bool:
		return unmarshalBool(x, node)
	case ast.Integer:
		return unmarshalInteger(x, node)
	case ast.Float:
		return unmarshalFloat(x, node)
	case ast.Array:
		return d.unmarshalArray(x, node)
	case ast.InlineTable:
		return d.unmarshalInlineTable(x, node)
	case ast.LocalDateTime:
		return unmarshalLocalDateTime(x, node)
	case ast.DateTime:
		return unmarshalDateTime(x, node)
	default:
		panic(fmt.Errorf("unhandled unmarshalValue kind %s", node.Kind))
	}
}

func unmarshalLocalDateTime(x target, node ast.Node) error {
	assertNode(ast.LocalDateTime, node)
	v, rest, err := parseLocalDateTime(node.Data)
	if err != nil {
		return err
	}
	if len(rest) > 0 {
		return fmt.Errorf("extra characters at the end of a local date time")
	}
	return setLocalDateTime(x, v)
}

func unmarshalDateTime(x target, node ast.Node) error {
	assertNode(ast.DateTime, node)
	v, err := parseDateTime(node.Data)
	if err != nil {
		return err
	}
	return setDateTime(x, v)
}

func setLocalDateTime(x target, v LocalDateTime) error {
	return x.set(reflect.ValueOf(v))
}

func setDateTime(x target, v time.Time) error {
	return x.set(reflect.ValueOf(v))
}

func unmarshalString(x target, node ast.Node) error {
	assertNode(ast.String, node)
	return setString(x, string(node.Data))
}

func unmarshalBool(x target, node ast.Node) error {
	assertNode(ast.Bool, node)
	v := node.Data[0] == 't'
	return setBool(x, v)
}

func unmarshalInteger(x target, node ast.Node) error {
	assertNode(ast.Integer, node)
	v, err := parseInteger(node.Data)
	if err != nil {
		return err
	}
	return setInt64(x, v)
}

func unmarshalFloat(x target, node ast.Node) error {
	assertNode(ast.Float, node)
	v, err := parseFloat(node.Data)
	if err != nil {
		return err
	}
	return setFloat64(x, v)
}

func (d *decoder) unmarshalInlineTable(x target, node ast.Node) error {
	assertNode(ast.InlineTable, node)

	it := node.Children()
	for it.Next() {
		n := it.Node()
		err := d.unmarshalKeyValue(x, n)
		if err != nil {
			return err
		}
	}
	return nil
}

func (d *decoder) unmarshalArray(x target, node ast.Node) error {
	assertNode(ast.Array, node)

	err := ensureValueIndexable(x)
	if err != nil {
		return err
	}

	it := node.Children()
	idx := 0
	for it.Next() {
		n := it.Node()
		v, err := elementAt(x, idx)
		if err != nil {
			return err
		}
		if v == nil {
			// when we go out of bound for an array just stop processing it to
			// mimic encoding/json
			break
		}
		err = d.unmarshalValue(v, n)
		if err != nil {
			return err
		}
		idx++
	}
	return nil
}

func assertNode(expected ast.Kind, node ast.Node) {
	if node.Kind != expected {
		panic(fmt.Errorf("expected node of kind %s, not %s", expected, node.Kind))
	}
}
<commit_msg>Add decoder interface<commit_after>package toml

import (
	"encoding"
	"fmt"
	"io"
	"io/ioutil"
	"reflect"
	"time"

	"github.com/pelletier/go-toml/v2/internal/ast"
	"github.com/pelletier/go-toml/v2/internal/tracker"
)

func Unmarshal(data []byte, v interface{}) error {
	p := parser{}
	p.Reset(data)
	d := decoder{}
	return d.FromParser(&p, v)
}

// Decoder reads and decode a TOML document from an input stream.
type Decoder struct {
	r io.Reader
}

// NewDecoder creates a new Decoder that will read from r.
func NewDecoder(r io.Reader) *Decoder {
	return &Decoder{r: r}
}

// Decode the whole content of r into v.
func (d *Decoder) Decode(v interface{}) error {
	b, err := ioutil.ReadAll(d.r)
	if err != nil {
		return err
	}
	p := parser{}
	p.Reset(b)
	dec := decoder{}
	return dec.FromParser(&p, v)
}

type decoder struct {
	// Tracks position in Go arrays.
	arrayIndexes map[reflect.Value]int

	// Tracks keys that have been seen, with which type.
	seen tracker.Seen
}

func (d *decoder) arrayIndex(append bool, v reflect.Value) int {
	if d.arrayIndexes == nil {
		d.arrayIndexes = make(map[reflect.Value]int, 1)
	}

	idx, ok := d.arrayIndexes[v]

	if !ok {
		d.arrayIndexes[v] = 0
	} else if append {
		idx++
		d.arrayIndexes[v] = idx
	}
	return idx
}

func (d *decoder) FromParser(p *parser, v interface{}) error {
	r := reflect.ValueOf(v)
	if r.Kind() != reflect.Ptr {
		return fmt.Errorf("need to target a pointer, not %s", r.Kind())
	}
	if r.IsNil() {
		return fmt.Errorf("target pointer must be non-nil")
	}

	var skipUntilTable bool
	var root target = valueTarget(r.Elem())
	current := root

	for p.NextExpression() {
		node := p.Expression()

		if node.Kind == ast.KeyValue && skipUntilTable {
			continue
		}

		err := d.seen.CheckExpression(node)
		if err != nil {
			return err
		}

		var found bool
		switch node.Kind {
		case ast.KeyValue:
			err = d.unmarshalKeyValue(current, node)
			found = true
		case ast.Table:
			current, found, err = d.scopeWithKey(root, node.Key())
		case ast.ArrayTable:
			current, found, err = d.scopeWithArrayTable(root, node.Key())
		default:
			panic(fmt.Errorf("this should not be a top level node type: %s", node.Kind))
		}

		if err != nil {
			return err
		}

		if !found {
			skipUntilTable = true
		}
	}

	return p.Error()
}

// scopeWithKey performs target scoping when unmarshaling an ast.KeyValue node.
//
// The goal is to hop from target to target recursively using the names in key.
// Parts of the key should be used to resolve field names for structs, and as
// keys when targeting maps.
//
// When encountering slices, it should always use its last element, and error
// if the slice does not have any.
func (d *decoder) scopeWithKey(x target, key ast.Iterator) (target, bool, error) {
	var err error
	found := true

	for key.Next() {
		n := key.Node()
		x, found, err = d.scopeTableTarget(false, x, string(n.Data))
		if err != nil || !found {
			return nil, found, err
		}
	}
	return x, true, nil
}

// scopeWithArrayTable performs target scoping when unmarshaling an
// ast.ArrayTable node.
//
// It is the same as scopeWithKey, but when scoping the last part of the key
// it creates a new element in the array instead of using the last one.
func (d *decoder) scopeWithArrayTable(x target, key ast.Iterator) (target, bool, error) {
	var err error
	found := true
	for key.Next() {
		n := key.Node()
		if !n.Next().Valid() { // want to stop at one before last
			break
		}
		x, found, err = d.scopeTableTarget(false, x, string(n.Data))
		if err != nil || !found {
			return nil, found, err
		}
	}
	n := key.Node()
	x, found, err = d.scopeTableTarget(false, x, string(n.Data))
	if err != nil || !found {
		return x, found, err
	}

	v := x.get()

	if v.Kind() == reflect.Ptr {
		x, err = scopePtr(x)
		if err != nil {
			return x, false, err
		}
		v = x.get()
	}

	if v.Kind() == reflect.Interface {
		x, err = scopeInterface(true, x)
		if err != nil {
			return x, found, err
		}
		v = x.get()
	}

	switch v.Kind() {
	case reflect.Slice:
		x, err = scopeSlice(true, x)
	case reflect.Array:
		x, err = d.scopeArray(true, x)
	}

	return x, found, err
}

func (d *decoder) unmarshalKeyValue(x target, node ast.Node) error {
	assertNode(ast.KeyValue, node)

	x, found, err := d.scopeWithKey(x, node.Key())
	if err != nil {
		return err
	}

	// A struct in the path was not found. Skip this value.
	if !found {
		return nil
	}

	return d.unmarshalValue(x, node.Value())
}

var textUnmarshalerType = reflect.TypeOf(new(encoding.TextUnmarshaler)).Elem()

func tryTextUnmarshaler(x target, node ast.Node) (bool, error) {
	v := x.get()

	if v.Kind() != reflect.Struct {
		return false, nil
	}
	if v.Type().Implements(textUnmarshalerType) {
		return true, v.Interface().(encoding.TextUnmarshaler).UnmarshalText(node.Data)
	}
	if v.CanAddr() && v.Addr().Type().Implements(textUnmarshalerType) {
		return true, v.Addr().Interface().(encoding.TextUnmarshaler).UnmarshalText(node.Data)
	}
	return false, nil
}

func (d *decoder) unmarshalValue(x target, node ast.Node) error {
	v := x.get()
	if v.Kind() == reflect.Ptr {
		if !v.Elem().IsValid() {
			err := x.set(reflect.New(v.Type().Elem()))
			if err != nil {
				return err
			}
			v = x.get()
		}
		return d.unmarshalValue(valueTarget(v.Elem()), node)
	}

	ok, err := tryTextUnmarshaler(x, node)
	if ok {
		return err
	}

	switch node.Kind {
	case ast.String:
		return unmarshalString(x, node)
	case ast.Bool:
		return unmarshalBool(x, node)
	case ast.Integer:
		return unmarshalInteger(x, node)
	case ast.Float:
		return unmarshalFloat(x, node)
	case ast.Array:
		return d.unmarshalArray(x, node)
	case ast.InlineTable:
		return d.unmarshalInlineTable(x, node)
	case ast.LocalDateTime:
		return unmarshalLocalDateTime(x, node)
	case ast.DateTime:
		return unmarshalDateTime(x, node)
	default:
		panic(fmt.Errorf("unhandled unmarshalValue kind %s", node.Kind))
	}
}

func unmarshalLocalDateTime(x target, node ast.Node) error {
	assertNode(ast.LocalDateTime, node)
	v, rest, err := parseLocalDateTime(node.Data)
	if err != nil {
		return err
	}
	if len(rest) > 0 {
		return fmt.Errorf("extra characters at the end of a local date time")
	}
	return setLocalDateTime(x, v)
}

func unmarshalDateTime(x target, node ast.Node) error {
	assertNode(ast.DateTime, node)
	v, err := parseDateTime(node.Data)
	if err != nil {
		return err
	}
	return setDateTime(x, v)
}

func setLocalDateTime(x target, v LocalDateTime) error {
	return x.set(reflect.ValueOf(v))
}

func setDateTime(x target, v time.Time) error {
	return x.set(reflect.ValueOf(v))
}

func unmarshalString(x target, node ast.Node) error {
	assertNode(ast.String, node)
	return setString(x, string(node.Data))
}

func unmarshalBool(x target, node ast.Node) error {
	assertNode(ast.Bool, node)
	v := node.Data[0] == 't'
	return setBool(x, v)
}

func unmarshalInteger(x target, node ast.Node) error {
	assertNode(ast.Integer, node)
	v, err := parseInteger(node.Data)
	if err != nil {
		return err
	}
	return setInt64(x, v)
}

func unmarshalFloat(x target, node ast.Node) error {
	assertNode(ast.Float, node)
	v, err := parseFloat(node.Data)
	if err != nil {
		return err
	}
	return setFloat64(x, v)
}

func (d *decoder) unmarshalInlineTable(x target, node ast.Node) error {
	assertNode(ast.InlineTable, node)

	it := node.Children()
	for it.Next() {
		n := it.Node()
		err := d.unmarshalKeyValue(x, n)
		if err != nil {
			return err
		}
	}
	return nil
}

func (d *decoder) unmarshalArray(x target, node ast.Node) error {
	assertNode(ast.Array, node)

	err := ensureValueIndexable(x)
	if err != nil {
		return err
	}

	it := node.Children()
	idx := 0
	for it.Next() {
		n := it.Node()
		v, err := elementAt(x, idx)
		if err != nil {
			return err
		}
		if v == nil {
			// when we go out of bound for an array just stop processing it to
			// mimic encoding/json
			break
		}
		err = d.unmarshalValue(v, n)
		if err != nil {
			return err
		}
		idx++
	}
	return nil
}

func assertNode(expected ast.Kind, node ast.Node) {
	if node.Kind != expected {
		panic(fmt.Errorf("expected node of kind %s, not %s", expected, node.Kind))
	}
}
<|endoftext|>