<commit_before>// Copyright 2014 The rkt Authors
// Copyright 2015 Intel Corp
//
// 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.

//+build linux

package main

// #cgo LDFLAGS: -ldl
// #include <dlfcn.h>
// #include <sys/types.h>
//
// int
// my_sd_pid_get_owner_uid(void *f, pid_t pid, uid_t *uid)
// {
//   int (*sd_pid_get_owner_uid)(pid_t, uid_t *);
//
//   sd_pid_get_owner_uid = (int (*)(pid_t, uid_t *))f;
//   return sd_pid_get_owner_uid(pid, uid);
// }
//
import "C"

// this implements /init of stage1/nspawn+systemd

import (
	"flag"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"os"
	"os/exec"
	"path/filepath"
	"runtime"
	"syscall"
	"strings"

	"github.com/coreos/rkt/Godeps/_workspace/src/github.com/appc/spec/schema/types"

	"github.com/coreos/rkt/common"
	"github.com/coreos/rkt/networking"
	"github.com/coreos/rkt/pkg/sys"
)

const (
	// Path to systemd-nspawn binary within the stage1 rootfs
	nspawnBin = "/usr/bin/systemd-nspawn"
	// Path to lkvm binary within the stage1 rootfs
	lkvmBin = "/usr/bin/lkvm"
	bzImg = "/usr/lib/kernel/vmlinuz.container"
	// Path to the interpreter within the stage1 rootfs
	interpBin = "/usr/lib/ld-linux-x86-64.so.2"
	// Path to the localtime file/symlink in host
	localtimePath = "/etc/localtime"
)

// mirrorLocalZoneInfo tries to reproduce the /etc/localtime target in stage1/ to satisfy systemd-nspawn
func mirrorLocalZoneInfo(root string) {
	zif, err := os.Readlink(localtimePath)
	if err != nil {
		return
	}

	// On some systems /etc/localtime is a relative symlink, make it absolute
	if !filepath.IsAbs(zif) {
		zif = filepath.Join(filepath.Dir(localtimePath), zif)
		zif = filepath.Clean(zif)
	}

	src, err := os.Open(zif)
	if err != nil {
		return
	}
	defer src.Close()

	destp := filepath.Join(common.Stage1RootfsPath(root), zif)

	if err = os.MkdirAll(filepath.Dir(destp), 0755); err != nil {
		return
	}

	dest, err := os.OpenFile(destp, os.O_CREATE|os.O_WRONLY, 0644)
	if err != nil {
		return
	}
	defer dest.Close()

	_, _ = io.Copy(dest, src)
}

var (
	debug       bool
	privNet     bool
	interactive bool
	virtualisation string
)

func init() {
	flag.BoolVar(&debug, "debug", false, "Run in debug mode")
	flag.BoolVar(&privNet, "private-net", false, "Setup private network")
	flag.BoolVar(&interactive, "interactive", false, "The pod is interactive")
	flag.StringVar(&virtualisation, "containment-type", "kvm", "Containment type to use: nspawn or kvm (default)")

	if os.Getenv("RKT_CONTAINMENT_TYPE") != "" {
		virtualisation = os.Getenv("RKT_CONTAINMENT_TYPE")
	}

	// this ensures that main runs only on main thread (thread group leader).
	// since namespace ops (unshare, setns) are done for a single thread, we
	// must ensure that the goroutine does not jump from OS thread to thread
	runtime.LockOSThread()
}

// getArgsEnvNspawn returns the nspawn args and env according to the usr used
func getArgsEnvNspawn(p *Pod) ([]string, []string, error) {
	args := []string{}
	env := os.Environ()

	args = append(args, filepath.Join(common.Stage1RootfsPath(p.Root), nspawnBin))
	args = append(args, "--boot") // Launch systemd in the pod
	out, err := os.Getwd()
	if err != nil {
		return nil, nil, err
	}
	lfd, err := common.GetRktLockFD()
	if err != nil {
		return nil, nil, err
	}
	args = append(args, fmt.Sprintf("--pid-file=%v", filepath.Join(out, "pid")))
	args = append(args, fmt.Sprintf("--keep-fd=%v", lfd))
	args = append(args, fmt.Sprintf("--register=true"))

	if !debug {
		args = append(args, "--quiet") // silence most nspawn output (log_warning is currently not covered by this)
	}

	keepUnit, err := runningFromUnitFile()
	if err != nil {
		return nil, nil, fmt.Errorf("error determining if we're running from a unit file: %v", err)
	}

	if keepUnit {
		args = append(args, "--keep-unit")
	}

	nsargs, err := p.PodToNspawnArgs()
	if err != nil {
		return nil, nil, fmt.Errorf("Failed to generate nspawn args: %v", err)
	}
	args = append(args, nsargs...)

	args = append(args, "--")
	args = append(args, "--default-standard-output=tty")

	if !debug {
		args = append(args, "--log-target=null")
		args = append(args, "--show-status=0")
	}

	return args, env, nil
}

func getArgsEnvKvm(p *Pod) ([]string, []string, error) {
	args := []string{}
	kargs := []string{}
	env := os.Environ()

	args = append(args, filepath.Join(common.Stage1RootfsPath(p.Root), lkvmBin))
	args = append(args, "run")

	args = append(args, "-m 1024")
	args = append(args, "-c 6")

	args = append(args, fmt.Sprintf("--kernel=%v", filepath.Join(common.Stage1RootfsPath(p.Root), bzImg)))
	args = append(args, "--console=virtio")
	kargs = append(kargs, "console=hvc0")

	kargs = append(kargs, "init=/usr/lib/systemd/systemd")

	nsargs, err := p.PodToKvmArgs()
	if err != nil {
		return nil, nil, fmt.Errorf("Failed to generate kvm args: %v", err)
	}
	args = append(args, nsargs...)

	// Arguments to systemd
	kargs = append(kargs, "systemd.default_standard_output=tty")
	if !debug {
		kargs = append(kargs, "systemd.log_target=null")
		kargs = append(kargs, "systemd.show-status=0")
		kargs = append(kargs, "quiet") // silence most nspawn output (log_warning is currently not covered by this)
	}

	args = append(args, "--param")
	args = append(args, strings.Join(kargs, " "))

	return args, env, nil
}

func getArgsEnv(p *Pod) ([]string, []string, error) {
	switch virtualisation {
	case "nspawn":
		return getArgsEnvNspawn(p)
	case "kvm":
		return getArgsEnvKvm(p)
	default:
		return nil, nil, fmt.Errorf("unrecognized containment type: %v", virtualisation)
	}
}

func withClearedCloExec(lfd int, f func() error) error {
	err := sys.CloseOnExec(lfd, false)
	if err != nil {
		return err
	}
	defer sys.CloseOnExec(lfd, true)

	return f()
}

func forwardedPorts(pod *Pod) ([]networking.ForwardedPort, error) {
	fps := []networking.ForwardedPort{}

	for _, ep := range pod.Manifest.Ports {
		n := ""
		fp := networking.ForwardedPort{}

		for _, a := range pod.Manifest.Apps {
			for _, p := range a.App.Ports {
				if p.Name == ep.Name {
					if n == "" {
						fp.Protocol = p.Protocol
						fp.HostPort = ep.HostPort
						fp.PodPort = p.Port
						n = a.Name.String()
					} else {
						return nil, fmt.Errorf("Ambiguous exposed port in PodManifest: %q and %q both define port %q", n, a.Name, p.Name)
					}
				}
			}
		}

		if n == "" {
			return nil, fmt.Errorf("Port name %q is not defined by any apps", ep.Name)
		}

		fps = append(fps, fp)
	}

	// TODO(eyakubovich): validate that there're no conflicts

	return fps, nil
}

func stage1() int {
	uuid, err := types.NewUUID(flag.Arg(0))
	if err != nil {
		fmt.Fprintln(os.Stderr, "UUID is missing or malformed")
		return 1
	}

	root := "."
	p, err := LoadPod(root, uuid)
	if err != nil {
		fmt.Fprintf(os.Stderr, "Failed to load pod: %v\n", err)
		return 1
	}

	// set close-on-exec flag on RKT_LOCK_FD so it gets correctly closed when invoking
	// network plugins
	lfd, err := common.GetRktLockFD()
	if err != nil {
		fmt.Fprintf(os.Stderr, "Failed to get rkt lock fd: %v\n", err)
		return 1
	}

	if err := sys.CloseOnExec(lfd, true); err != nil {
		fmt.Fprintf(os.Stderr, "Failed to set FD_CLOEXEC on rkt lock: %v\n", err)
		return 1
	}

	mirrorLocalZoneInfo(p.Root)

	if privNet {
		fps, err := forwardedPorts(p)
		if err != nil {
			fmt.Fprintln(os.Stderr, err.Error())
			return 6
		}

		n, err := networking.Setup(root, p.UUID, fps)
		if err != nil {
			fmt.Fprintf(os.Stderr, "Failed to setup network: %v\n", err)
			return 6
		}
		defer n.Teardown()

		if err = n.Save(); err != nil {
			fmt.Fprintf(os.Stderr, "Failed to save networking state %v\n", err)
			return 6
		}

		p.MetadataServiceURL = common.MetadataServicePublicURL(n.GetDefaultHostIP())

		if err = registerPod(p, n.GetDefaultIP()); err != nil {
			fmt.Fprintf(os.Stderr, "Failed to register pod: %v\n", err)
			return 6
		}
		defer unregisterPod(p)
	}

	if err = p.PodToSystemd(interactive, virtualisation); err != nil {
		fmt.Fprintf(os.Stderr, "Failed to configure systemd: %v\n", err)
		return 2
	}

	args, env, err := getArgsEnv(p)
	if err != nil {
		fmt.Fprintf(os.Stderr, "Failed to get execution parameters: %v\n", err)
		return 3
	}

	var execFn func() error

	if privNet {
		cmd := exec.Cmd{
			Path:   args[0],
			Args:   args,
			Stdin:  os.Stdin,
			Stdout: os.Stdout,
			Stderr: os.Stderr,
			Env:    env,
		}
		execFn = cmd.Run
	} else {
		execFn = func() error {
			return syscall.Exec(args[0], args, env)
		}
	}

	err = withClearedCloExec(lfd, execFn)
	if err != nil {
		fmt.Fprintf(os.Stderr, "Failed to execute containment: %v\n", err)
		return 5
	}

	return 0
}

func runningFromUnitFile() (ret bool, err error) {
	handle := C.dlopen(C.CString("libsystemd-login.so"), C.RTLD_LAZY)
	if handle == nil {
		// we can't open libsystemd-login.so so we assume systemd is not
		// installed and we're not running from a unit file
		ret = false
		return
	}
	defer func() {
		if r := C.dlclose(handle); r != 0 {
			err = fmt.Errorf("error closing libsystemd-login.so")
		}
	}()

	sd_pid_get_owner_uid := C.dlsym(handle, C.CString("sd_pid_get_owner_uid"))
	if sd_pid_get_owner_uid == nil {
		err = fmt.Errorf("error resolving sd_pid_get_owner_uid function")
		return
	}

	var uid C.uid_t
	errno := C.my_sd_pid_get_owner_uid(sd_pid_get_owner_uid, 0, &uid)
	// when we're running from a unit file, sd_pid_get_owner_uid returns
	// ENOENT (systemd <220) or ENXIO (systemd >=220)
	switch {
	case errno >= 0:
		ret = false
		return
	case syscall.Errno(-errno) == syscall.ENOENT || syscall.Errno(-errno) == syscall.ENXIO:
		ret = true
		return
	default:
		err = fmt.Errorf("error calling sd_pid_get_owner_uid: %v", syscall.Errno(-errno))
		return
	}
}

func main() {
	flag.Parse()

	if !debug {
		log.SetOutput(ioutil.Discard)
	}

	// move code into stage1() helper so defered fns get run
	os.Exit(stage1())
}
<commit_msg>Switch to uncompressed kernel.<commit_after>// Copyright 2014 The rkt Authors
// Copyright 2015 Intel Corp
//
// 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.

//+build linux

package main

// #cgo LDFLAGS: -ldl
// #include <dlfcn.h>
// #include <sys/types.h>
//
// int
// my_sd_pid_get_owner_uid(void *f, pid_t pid, uid_t *uid)
// {
//   int (*sd_pid_get_owner_uid)(pid_t, uid_t *);
//
//   sd_pid_get_owner_uid = (int (*)(pid_t, uid_t *))f;
//   return sd_pid_get_owner_uid(pid, uid);
// }
//
import "C"

// this implements /init of stage1/nspawn+systemd

import (
	"flag"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"os"
	"os/exec"
	"path/filepath"
	"runtime"
	"syscall"
	"strings"

	"github.com/coreos/rkt/Godeps/_workspace/src/github.com/appc/spec/schema/types"

	"github.com/coreos/rkt/common"
	"github.com/coreos/rkt/networking"
	"github.com/coreos/rkt/pkg/sys"
)

const (
	// Path to systemd-nspawn binary within the stage1 rootfs
	nspawnBin = "/usr/bin/systemd-nspawn"
	// Path to lkvm binary within the stage1 rootfs
	lkvmBin = "/usr/bin/lkvm"
	bzImg = "/usr/lib/kernel/vmlinux.container"
	// Path to the interpreter within the stage1 rootfs
	interpBin = "/usr/lib/ld-linux-x86-64.so.2"
	// Path to the localtime file/symlink in host
	localtimePath = "/etc/localtime"
)

// mirrorLocalZoneInfo tries to reproduce the /etc/localtime target in stage1/ to satisfy systemd-nspawn
func mirrorLocalZoneInfo(root string) {
	zif, err := os.Readlink(localtimePath)
	if err != nil {
		return
	}

	// On some systems /etc/localtime is a relative symlink, make it absolute
	if !filepath.IsAbs(zif) {
		zif = filepath.Join(filepath.Dir(localtimePath), zif)
		zif = filepath.Clean(zif)
	}

	src, err := os.Open(zif)
	if err != nil {
		return
	}
	defer src.Close()

	destp := filepath.Join(common.Stage1RootfsPath(root), zif)

	if err = os.MkdirAll(filepath.Dir(destp), 0755); err != nil {
		return
	}

	dest, err := os.OpenFile(destp, os.O_CREATE|os.O_WRONLY, 0644)
	if err != nil {
		return
	}
	defer dest.Close()

	_, _ = io.Copy(dest, src)
}

var (
	debug       bool
	privNet     bool
	interactive bool
	virtualisation string
)

func init() {
	flag.BoolVar(&debug, "debug", false, "Run in debug mode")
	flag.BoolVar(&privNet, "private-net", false, "Setup private network")
	flag.BoolVar(&interactive, "interactive", false, "The pod is interactive")
	flag.StringVar(&virtualisation, "containment-type", "kvm", "Containment type to use: nspawn or kvm (default)")

	if os.Getenv("RKT_CONTAINMENT_TYPE") != "" {
		virtualisation = os.Getenv("RKT_CONTAINMENT_TYPE")
	}

	// this ensures that main runs only on main thread (thread group leader).
	// since namespace ops (unshare, setns) are done for a single thread, we
	// must ensure that the goroutine does not jump from OS thread to thread
	runtime.LockOSThread()
}

// getArgsEnvNspawn returns the nspawn args and env according to the usr used
func getArgsEnvNspawn(p *Pod) ([]string, []string, error) {
	args := []string{}
	env := os.Environ()

	args = append(args, filepath.Join(common.Stage1RootfsPath(p.Root), nspawnBin))
	args = append(args, "--boot") // Launch systemd in the pod
	out, err := os.Getwd()
	if err != nil {
		return nil, nil, err
	}
	lfd, err := common.GetRktLockFD()
	if err != nil {
		return nil, nil, err
	}
	args = append(args, fmt.Sprintf("--pid-file=%v", filepath.Join(out, "pid")))
	args = append(args, fmt.Sprintf("--keep-fd=%v", lfd))
	args = append(args, fmt.Sprintf("--register=true"))

	if !debug {
		args = append(args, "--quiet") // silence most nspawn output (log_warning is currently not covered by this)
	}

	keepUnit, err := runningFromUnitFile()
	if err != nil {
		return nil, nil, fmt.Errorf("error determining if we're running from a unit file: %v", err)
	}

	if keepUnit {
		args = append(args, "--keep-unit")
	}

	nsargs, err := p.PodToNspawnArgs()
	if err != nil {
		return nil, nil, fmt.Errorf("Failed to generate nspawn args: %v", err)
	}
	args = append(args, nsargs...)

	args = append(args, "--")
	args = append(args, "--default-standard-output=tty")

	if !debug {
		args = append(args, "--log-target=null")
		args = append(args, "--show-status=0")
	}

	return args, env, nil
}

func getArgsEnvKvm(p *Pod) ([]string, []string, error) {
	args := []string{}
	kargs := []string{}
	env := os.Environ()

	args = append(args, filepath.Join(common.Stage1RootfsPath(p.Root), lkvmBin))
	args = append(args, "run")

	args = append(args, "-m 1024")
	args = append(args, "-c 6")

	args = append(args, fmt.Sprintf("--kernel=%v", filepath.Join(common.Stage1RootfsPath(p.Root), bzImg)))
	args = append(args, "--console=virtio")
	kargs = append(kargs, "console=hvc0")

	kargs = append(kargs, "init=/usr/lib/systemd/systemd")

	nsargs, err := p.PodToKvmArgs()
	if err != nil {
		return nil, nil, fmt.Errorf("Failed to generate kvm args: %v", err)
	}
	args = append(args, nsargs...)

	// Arguments to systemd
	kargs = append(kargs, "systemd.default_standard_output=tty")
	if !debug {
		kargs = append(kargs, "systemd.log_target=null")
		kargs = append(kargs, "systemd.show-status=0")
		kargs = append(kargs, "quiet") // silence most nspawn output (log_warning is currently not covered by this)
	}

	args = append(args, "--param")
	args = append(args, strings.Join(kargs, " "))

	return args, env, nil
}

func getArgsEnv(p *Pod) ([]string, []string, error) {
	switch virtualisation {
	case "nspawn":
		return getArgsEnvNspawn(p)
	case "kvm":
		return getArgsEnvKvm(p)
	default:
		return nil, nil, fmt.Errorf("unrecognized containment type: %v", virtualisation)
	}
}

func withClearedCloExec(lfd int, f func() error) error {
	err := sys.CloseOnExec(lfd, false)
	if err != nil {
		return err
	}
	defer sys.CloseOnExec(lfd, true)

	return f()
}

func forwardedPorts(pod *Pod) ([]networking.ForwardedPort, error) {
	fps := []networking.ForwardedPort{}

	for _, ep := range pod.Manifest.Ports {
		n := ""
		fp := networking.ForwardedPort{}

		for _, a := range pod.Manifest.Apps {
			for _, p := range a.App.Ports {
				if p.Name == ep.Name {
					if n == "" {
						fp.Protocol = p.Protocol
						fp.HostPort = ep.HostPort
						fp.PodPort = p.Port
						n = a.Name.String()
					} else {
						return nil, fmt.Errorf("Ambiguous exposed port in PodManifest: %q and %q both define port %q", n, a.Name, p.Name)
					}
				}
			}
		}

		if n == "" {
			return nil, fmt.Errorf("Port name %q is not defined by any apps", ep.Name)
		}

		fps = append(fps, fp)
	}

	// TODO(eyakubovich): validate that there're no conflicts

	return fps, nil
}

func stage1() int {
	uuid, err := types.NewUUID(flag.Arg(0))
	if err != nil {
		fmt.Fprintln(os.Stderr, "UUID is missing or malformed")
		return 1
	}

	root := "."
	p, err := LoadPod(root, uuid)
	if err != nil {
		fmt.Fprintf(os.Stderr, "Failed to load pod: %v\n", err)
		return 1
	}

	// set close-on-exec flag on RKT_LOCK_FD so it gets correctly closed when invoking
	// network plugins
	lfd, err := common.GetRktLockFD()
	if err != nil {
		fmt.Fprintf(os.Stderr, "Failed to get rkt lock fd: %v\n", err)
		return 1
	}

	if err := sys.CloseOnExec(lfd, true); err != nil {
		fmt.Fprintf(os.Stderr, "Failed to set FD_CLOEXEC on rkt lock: %v\n", err)
		return 1
	}

	mirrorLocalZoneInfo(p.Root)

	if privNet {
		fps, err := forwardedPorts(p)
		if err != nil {
			fmt.Fprintln(os.Stderr, err.Error())
			return 6
		}

		n, err := networking.Setup(root, p.UUID, fps)
		if err != nil {
			fmt.Fprintf(os.Stderr, "Failed to setup network: %v\n", err)
			return 6
		}
		defer n.Teardown()

		if err = n.Save(); err != nil {
			fmt.Fprintf(os.Stderr, "Failed to save networking state %v\n", err)
			return 6
		}

		p.MetadataServiceURL = common.MetadataServicePublicURL(n.GetDefaultHostIP())

		if err = registerPod(p, n.GetDefaultIP()); err != nil {
			fmt.Fprintf(os.Stderr, "Failed to register pod: %v\n", err)
			return 6
		}
		defer unregisterPod(p)
	}

	if err = p.PodToSystemd(interactive, virtualisation); err != nil {
		fmt.Fprintf(os.Stderr, "Failed to configure systemd: %v\n", err)
		return 2
	}

	args, env, err := getArgsEnv(p)
	if err != nil {
		fmt.Fprintf(os.Stderr, "Failed to get execution parameters: %v\n", err)
		return 3
	}

	var execFn func() error

	if privNet {
		cmd := exec.Cmd{
			Path:   args[0],
			Args:   args,
			Stdin:  os.Stdin,
			Stdout: os.Stdout,
			Stderr: os.Stderr,
			Env:    env,
		}
		execFn = cmd.Run
	} else {
		execFn = func() error {
			return syscall.Exec(args[0], args, env)
		}
	}

	err = withClearedCloExec(lfd, execFn)
	if err != nil {
		fmt.Fprintf(os.Stderr, "Failed to execute containment: %v\n", err)
		return 5
	}

	return 0
}

func runningFromUnitFile() (ret bool, err error) {
	handle := C.dlopen(C.CString("libsystemd-login.so"), C.RTLD_LAZY)
	if handle == nil {
		// we can't open libsystemd-login.so so we assume systemd is not
		// installed and we're not running from a unit file
		ret = false
		return
	}
	defer func() {
		if r := C.dlclose(handle); r != 0 {
			err = fmt.Errorf("error closing libsystemd-login.so")
		}
	}()

	sd_pid_get_owner_uid := C.dlsym(handle, C.CString("sd_pid_get_owner_uid"))
	if sd_pid_get_owner_uid == nil {
		err = fmt.Errorf("error resolving sd_pid_get_owner_uid function")
		return
	}

	var uid C.uid_t
	errno := C.my_sd_pid_get_owner_uid(sd_pid_get_owner_uid, 0, &uid)
	// when we're running from a unit file, sd_pid_get_owner_uid returns
	// ENOENT (systemd <220) or ENXIO (systemd >=220)
	switch {
	case errno >= 0:
		ret = false
		return
	case syscall.Errno(-errno) == syscall.ENOENT || syscall.Errno(-errno) == syscall.ENXIO:
		ret = true
		return
	default:
		err = fmt.Errorf("error calling sd_pid_get_owner_uid: %v", syscall.Errno(-errno))
		return
	}
}

func main() {
	flag.Parse()

	if !debug {
		log.SetOutput(ioutil.Discard)
	}

	// move code into stage1() helper so defered fns get run
	os.Exit(stage1())
}
<|endoftext|>


<commit_before>package password

import (
	"crypto/rand"
	"encoding/base64"
	"errors"
	"fmt"
	"golang.org/x/crypto/scrypt"
)

// Parameters for scrypt key derivation, appropriate for interactive login.
const n = 16384
const r = 8
const p = 1

// Length of the salt. Should be at least 16, using 18 to avoid base64 padding.
const saltLength = 18

// Length of the scrypt derived key.
const keyLength = 32

// Prepend this to tokens to support future upgrades of the hashing function.
var versionHeader = fmt.Sprintf("scrypt(N=%d,r=%d,p=%d,len=%d)$", n, r, p, keyLength)

// Length of combined token as stored in the database.
var tokenLength = saltLength + keyLength + len(versionHeader)

// ErrTokenWrongVersion is generated if a token's versionHeader doesn't match
var ErrTokenWrongVersion = errors.New("Token header did not match current version.")

// ErrTokenWrongLength is generated if a token's length doesn't match
var ErrTokenWrongLength = errors.New("Token did not match expected length.")

// ErrPasswordLength is generated if a password is greater than 1024 bytes
var ErrPasswordLength = errors.New("Password longer than 1 KB, refused as denial of service safeguard.")

// Compare strings via bitwise XOR, i.e. constant-time comparison
func compare(a string, b string) bool {
	if len(a) != len(b) {
		return false
	}
	var x byte
	for i := 0; i < len(b); i++ {
		x |= a[i] ^ b[i]
	}
	return x == 0
}

// Tokenize the salt and salted hash key
func tokenize(salt []byte, key []byte) string {
	return versionHeader + base64.StdEncoding.EncodeToString(append(salt, key...))
}

// Verify that a token is plausible and extract the salt stored in a token
func saltFromToken(token string) ([]byte, error) {
	if len(token) != tokenLength {
		return nil, ErrTokenWrongLength
	}
	if !compare(token[:tokenLength], versionHeader) {
		return nil, ErrTokenWrongVersion
	}
	salt, err := base64.StdEncoding.DecodeString(token[len(versionHeader):][:saltLength])
	if err != nil {
		return nil, err
	}
	return salt, nil
}

// Generate cryptographically-sound random salt via crypto/rand
func createSalt() ([]byte, error) {
	salt := make([]byte, saltLength)
	_, err := rand.Read(salt[:cap(salt)])
	if err != nil {
		return nil, err
	}
	return salt, nil
}

// Calculate scrypt salted hash key from password and salt
func createKey(password string, salt []byte) ([]byte, error) {
	key, err := scrypt.Key([]byte(password), salt, n, r, p, keyLength)
	if err != nil {
		return []byte{}, err
	}
	return key, nil
}

// Hash returns a destructive cryptographic hash of the provided password
func Hash(password string) (string, error) {
	if len(password) > 1024 {
		return "", ErrPasswordLength
	}
	salt, err := createSalt()
	if err != nil {
		return "", err
	}
	key, err := createKey(password, salt)
	if err != nil {
		return "", err
	}
	return tokenize(salt, key), nil
}

// Verify that password is consistent with token
func Verify(password string, token string) (bool, error) {
	if len(password) > 1024 {
		return false, ErrPasswordLength
	}
	salt, err := saltFromToken(token)
	if err != nil {
		return false, err
	}
	key, err := createKey(password, salt)
	if err != nil {
		return false, err
	}
	return compare(tokenize(salt, key), token), nil
}
<commit_msg>Fix error due to len(base64(x)) != len(x)<commit_after>package password

import (
	"crypto/rand"
	"encoding/base64"
	"errors"
	"fmt"
	"golang.org/x/crypto/scrypt"
)

// Parameters for scrypt key derivation, appropriate for interactive login.
const n = 16384
const r = 8
const p = 1

// Length of the salt. Should be at least 16, using 18 to avoid base64 padding.
const saltLength = 18

// Length of the scrypt derived key.
const keyLength = 32

// Prepend this to tokens to support future upgrades of the hashing function.
var versionHeader = fmt.Sprintf("scrypt$NrpL%d/%d/%d/%d$", n, r, p, keyLength)

// ErrTokenWrongVersion is generated if a token's versionHeader doesn't match
var ErrTokenWrongVersion = errors.New("Token header did not match current version.")

// ErrPasswordLength is generated if a password is greater than 1024 bytes
var ErrPasswordLength = errors.New("Password longer than 1 KB, refused as denial of service safeguard.")

// Compare strings via bitwise XOR, i.e. constant-time comparison
func compare(a string, b string) bool {
	if len(a) != len(b) {
		return false
	}
	var x byte
	for i := 0; i < len(b); i++ {
		x |= a[i] ^ b[i]
	}
	return x == 0
}

// Tokenize the salt and salted hash key
func tokenize(salt []byte, key []byte) string {
	return versionHeader + base64.StdEncoding.EncodeToString(append(salt, key...))
}

// Verify that a token is plausible and extract the salt stored in a token
func saltFromToken(token string) ([]byte, error) {
	fmt.Println(token)
	fmt.Println(token[:len(versionHeader)])
	fmt.Println(versionHeader)
	if !compare(token[:len(versionHeader)], versionHeader) {
		return nil, ErrTokenWrongVersion
	}
	decoded, err := base64.StdEncoding.DecodeString(token[len(versionHeader):])
	if err != nil {
		return nil, err
	}
	return decoded[:saltLength], nil
}

// Generate cryptographically-sound random salt via crypto/rand
func createSalt() ([]byte, error) {
	salt := make([]byte, saltLength)
	_, err := rand.Read(salt[:cap(salt)])
	if err != nil {
		return nil, err
	}
	return salt, nil
}

// Calculate scrypt salted hash key from password and salt
func createKey(password string, salt []byte) ([]byte, error) {
	key, err := scrypt.Key([]byte(password), salt, n, r, p, keyLength)
	if err != nil {
		return []byte{}, err
	}
	return key, nil
}

// Hash returns a destructive cryptographic hash of the provided password
func Hash(password string) (string, error) {
	if len(password) > 1024 {
		return "", ErrPasswordLength
	}
	salt, err := createSalt()
	if err != nil {
		return "", err
	}
	key, err := createKey(password, salt)
	if err != nil {
		return "", err
	}
	return tokenize(salt, key), nil
}

// Verify that password is consistent with token
func Verify(password string, token string) (bool, error) {
	if len(password) > 1024 {
		return false, ErrPasswordLength
	}
	salt, err := saltFromToken(token)
	if err != nil {
		return false, err
	}
	key, err := createKey(password, salt)
	if err != nil {
		return false, err
	}
	return compare(tokenize(salt, key), token), nil
}
<|endoftext|>


<commit_before>package command

import (
	"fmt"
	"sort"
	"strings"
	"time"

	"github.com/hashicorp/nomad/api"
)

type AllocStatusCommand struct {
	Meta
}

func (c *AllocStatusCommand) Help() string {
	helpText := `
Usage: nomad alloc-status [options] <allocation>

  Display information about existing allocations and its tasks. This command can
  be used to inspect the current status of all allocation, including its running
  status, metadata, and verbose failure messages reported by internal
  subsystems.

General Options:

  ` + generalOptionsUsage() + `

Alloc Status Options:

  -short
    Display short output, showing only the most recent task event.
`

	return strings.TrimSpace(helpText)
}

func (c *AllocStatusCommand) Synopsis() string {
	return "Display allocation status information and metadata"
}

func (c *AllocStatusCommand) Run(args []string) int {
	var short bool

	flags := c.Meta.FlagSet("alloc-status", FlagSetClient)
	flags.Usage = func() { c.Ui.Output(c.Help()) }
	flags.BoolVar(&short, "short", false, "")

	if err := flags.Parse(args); err != nil {
		return 1
	}

	// Check that we got exactly one allocation ID
	args = flags.Args()
	if len(args) == 0 || len(args) > 2 {
		c.Ui.Error(c.Help())
		return 1
	}
	allocID := args[0]

	// Get the HTTP client
	client, err := c.Meta.Client()
	if err != nil {
		c.Ui.Error(fmt.Sprintf("Error initializing client: %s", err))
		return 1
	}

	// Query the allocation info
	alloc, _, err := client.Allocations().Info(allocID, nil)
	if err != nil {
		c.Ui.Error(fmt.Sprintf("Error querying allocation: %s", err))
		return 1
	}

	// Format the allocation data
	basic := []string{
		fmt.Sprintf("ID|%s", alloc.ID),
		fmt.Sprintf("EvalID|%s", alloc.EvalID),
		fmt.Sprintf("Name|%s", alloc.Name),
		fmt.Sprintf("NodeID|%s", alloc.NodeID),
		fmt.Sprintf("JobID|%s", alloc.JobID),
		fmt.Sprintf("ClientStatus|%s", alloc.ClientStatus),
		fmt.Sprintf("NodesEvaluated|%d", alloc.Metrics.NodesEvaluated),
		fmt.Sprintf("NodesFiltered|%d", alloc.Metrics.NodesFiltered),
		fmt.Sprintf("NodesExhausted|%d", alloc.Metrics.NodesExhausted),
		fmt.Sprintf("AllocationTime|%s", alloc.Metrics.AllocationTime),
		fmt.Sprintf("CoalescedFailures|%d", alloc.Metrics.CoalescedFailures),
	}
	c.Ui.Output(formatKV(basic))

	// Print the state of each task.
	if short {
		c.shortTaskStatus(alloc)
	} else {
		c.taskStatus(alloc)
	}

	// Format the detailed status
	c.Ui.Output("\n==> Status")
	dumpAllocStatus(c.Ui, alloc)

	return 0
}

// shortTaskStatus prints out the current state of each task.
func (c *AllocStatusCommand) shortTaskStatus(alloc *api.Allocation) {
	tasks := make([]string, 0, len(alloc.TaskStates)+1)
	tasks = append(tasks, "Name|State|LastEvent|Time")
	for task := range c.sortedTaskStateIterator(alloc.TaskStates) {
		fmt.Println(task)
		state := alloc.TaskStates[task]
		lastState := state.State
		var lastEvent, lastTime string

		l := len(state.Events)
		if l != 0 {
			last := state.Events[l-1]
			lastEvent = last.Type
			lastTime = c.formatUnixNonoTime(last.Time)
		}

		tasks = append(tasks, fmt.Sprintf("%s|%s|%s|%s",
			task, lastState, lastEvent, lastTime))
	}

	c.Ui.Output("\n==> Tasks")
	c.Ui.Output(formatList(tasks))
}

// taskStatus prints out the most recent events for each task.
func (c *AllocStatusCommand) taskStatus(alloc *api.Allocation) {
	for task := range c.sortedTaskStateIterator(alloc.TaskStates) {
		state := alloc.TaskStates[task]
		events := make([]string, len(state.Events)+1)
		events[0] = "Time|Type|Description"

		size := len(state.Events)
		for i, event := range state.Events {
			formatedTime := c.formatUnixNonoTime(event.Time)

			// Build up the description based on the event type.
			var desc string
			switch event.Type {
			case api.TaskDriverFailure:
				desc = event.DriverError
			case api.TaskKilled:
				desc = event.KillError
			case api.TaskTerminated:
				var parts []string
				parts = append(parts, fmt.Sprintf("Exit Code: %d", event.ExitCode))

				if event.Signal != 0 {
					parts = append(parts, fmt.Sprintf("Signal: %d", event.Signal))
				}

				if event.Message != "" {
					parts = append(parts, fmt.Sprintf("Exit Message: %q", event.Message))
				}
				desc = strings.Join(parts, ", ")
			}

			// Reverse order so we are sorted by time
			events[size-i] = fmt.Sprintf("%s|%s|%s", formatedTime, event.Type, desc)
		}

		c.Ui.Output(fmt.Sprintf("\n==> Task %q is %q\nRecent Events:", task, state.State))
		c.Ui.Output(formatList(events))
	}
}

// formatUnixNonoTime is a helper for formating time for output.
func (c *AllocStatusCommand) formatUnixNonoTime(nano int64) string {
	t := time.Unix(0, nano)
	return t.Format("15:04:05 01/02/06")
}

// sortedTaskStateIterator is a helper that takes the task state map and returns a
// channel that returns the keys in a sorted order.
func (c *AllocStatusCommand) sortedTaskStateIterator(m map[string]*api.TaskState) <-chan string {
	output := make(chan string, len(m))
	keys := make([]string, len(m))
	i := 0
	for k, _ := range m {
		keys[i] = k
		i++
	}
	sort.Strings(keys)

	for _, key := range keys {
		output <- key
	}

	close(output)
	return output
}
<commit_msg>Get rid of incorrect length check<commit_after>package command

import (
	"fmt"
	"sort"
	"strings"
	"time"

	"github.com/hashicorp/nomad/api"
)

type AllocStatusCommand struct {
	Meta
}

func (c *AllocStatusCommand) Help() string {
	helpText := `
Usage: nomad alloc-status [options] <allocation>

  Display information about existing allocations and its tasks. This command can
  be used to inspect the current status of all allocation, including its running
  status, metadata, and verbose failure messages reported by internal
  subsystems.

General Options:

  ` + generalOptionsUsage() + `

Alloc Status Options:

  -short
    Display short output, showing only the most recent task event.
`

	return strings.TrimSpace(helpText)
}

func (c *AllocStatusCommand) Synopsis() string {
	return "Display allocation status information and metadata"
}

func (c *AllocStatusCommand) Run(args []string) int {
	var short bool

	flags := c.Meta.FlagSet("alloc-status", FlagSetClient)
	flags.Usage = func() { c.Ui.Output(c.Help()) }
	flags.BoolVar(&short, "short", false, "")

	if err := flags.Parse(args); err != nil {
		return 1
	}

	// Check that we got exactly one allocation ID
	args = flags.Args()
	if len(args) == 0 {
		c.Ui.Error(c.Help())
		return 1
	}
	allocID := args[0]

	// Get the HTTP client
	client, err := c.Meta.Client()
	if err != nil {
		c.Ui.Error(fmt.Sprintf("Error initializing client: %s", err))
		return 1
	}

	// Query the allocation info
	alloc, _, err := client.Allocations().Info(allocID, nil)
	if err != nil {
		c.Ui.Error(fmt.Sprintf("Error querying allocation: %s", err))
		return 1
	}

	// Format the allocation data
	basic := []string{
		fmt.Sprintf("ID|%s", alloc.ID),
		fmt.Sprintf("EvalID|%s", alloc.EvalID),
		fmt.Sprintf("Name|%s", alloc.Name),
		fmt.Sprintf("NodeID|%s", alloc.NodeID),
		fmt.Sprintf("JobID|%s", alloc.JobID),
		fmt.Sprintf("ClientStatus|%s", alloc.ClientStatus),
		fmt.Sprintf("NodesEvaluated|%d", alloc.Metrics.NodesEvaluated),
		fmt.Sprintf("NodesFiltered|%d", alloc.Metrics.NodesFiltered),
		fmt.Sprintf("NodesExhausted|%d", alloc.Metrics.NodesExhausted),
		fmt.Sprintf("AllocationTime|%s", alloc.Metrics.AllocationTime),
		fmt.Sprintf("CoalescedFailures|%d", alloc.Metrics.CoalescedFailures),
	}
	c.Ui.Output(formatKV(basic))

	// Print the state of each task.
	if short {
		c.shortTaskStatus(alloc)
	} else {
		c.taskStatus(alloc)
	}

	// Format the detailed status
	c.Ui.Output("\n==> Status")
	dumpAllocStatus(c.Ui, alloc)

	return 0
}

// shortTaskStatus prints out the current state of each task.
func (c *AllocStatusCommand) shortTaskStatus(alloc *api.Allocation) {
	tasks := make([]string, 0, len(alloc.TaskStates)+1)
	tasks = append(tasks, "Name|State|LastEvent|Time")
	for task := range c.sortedTaskStateIterator(alloc.TaskStates) {
		fmt.Println(task)
		state := alloc.TaskStates[task]
		lastState := state.State
		var lastEvent, lastTime string

		l := len(state.Events)
		if l != 0 {
			last := state.Events[l-1]
			lastEvent = last.Type
			lastTime = c.formatUnixNonoTime(last.Time)
		}

		tasks = append(tasks, fmt.Sprintf("%s|%s|%s|%s",
			task, lastState, lastEvent, lastTime))
	}

	c.Ui.Output("\n==> Tasks")
	c.Ui.Output(formatList(tasks))
}

// taskStatus prints out the most recent events for each task.
func (c *AllocStatusCommand) taskStatus(alloc *api.Allocation) {
	for task := range c.sortedTaskStateIterator(alloc.TaskStates) {
		state := alloc.TaskStates[task]
		events := make([]string, len(state.Events)+1)
		events[0] = "Time|Type|Description"

		size := len(state.Events)
		for i, event := range state.Events {
			formatedTime := c.formatUnixNonoTime(event.Time)

			// Build up the description based on the event type.
			var desc string
			switch event.Type {
			case api.TaskDriverFailure:
				desc = event.DriverError
			case api.TaskKilled:
				desc = event.KillError
			case api.TaskTerminated:
				var parts []string
				parts = append(parts, fmt.Sprintf("Exit Code: %d", event.ExitCode))

				if event.Signal != 0 {
					parts = append(parts, fmt.Sprintf("Signal: %d", event.Signal))
				}

				if event.Message != "" {
					parts = append(parts, fmt.Sprintf("Exit Message: %q", event.Message))
				}
				desc = strings.Join(parts, ", ")
			}

			// Reverse order so we are sorted by time
			events[size-i] = fmt.Sprintf("%s|%s|%s", formatedTime, event.Type, desc)
		}

		c.Ui.Output(fmt.Sprintf("\n==> Task %q is %q\nRecent Events:", task, state.State))
		c.Ui.Output(formatList(events))
	}
}

// formatUnixNonoTime is a helper for formating time for output.
func (c *AllocStatusCommand) formatUnixNonoTime(nano int64) string {
	t := time.Unix(0, nano)
	return t.Format("15:04:05 01/02/06")
}

// sortedTaskStateIterator is a helper that takes the task state map and returns a
// channel that returns the keys in a sorted order.
func (c *AllocStatusCommand) sortedTaskStateIterator(m map[string]*api.TaskState) <-chan string {
	output := make(chan string, len(m))
	keys := make([]string, len(m))
	i := 0
	for k, _ := range m {
		keys[i] = k
		i++
	}
	sort.Strings(keys)

	for _, key := range keys {
		output <- key
	}

	close(output)
	return output
}
<|endoftext|>


<commit_before>package cl11

import (
	"bytes"
	"testing"
	"unsafe"
)

var scratchSpace = unsafe.Pointer(new([scratchSize]byte))

func sizeCheck(want int, got uintptr, t *testing.T) {
	if want != int(got) {
		t.Error("size mismatch, wanted", want, "got", got)
	}
}

func pointerCheck(want, got unsafe.Pointer, t *testing.T) {
	if want != got {
		t.Error("pointer mismatch, wanted", want, "got", got)
	}
}

// Bool

func TestIntPointerAndSize(t *testing.T) {

	var scratchSpace [8]byte
	scratch := unsafe.Pointer(&scratchSpace[0])

	var anInt int = 1

	pointer, size := getPointerAndSize(anInt, scratch)

	sizeCheck(4, size, t)
	pointerCheck(scratch, pointer, t)
	if anInt != *(*int)(pointer) {
		t.Error("value mismatch, wanted", anInt, "got", *(*int)(scratch))
	}

	pointer, size = getPointerAndSize(&anInt, scratch)

	sizeCheck(4, size, t)
	// Expect scratch since int needs to be converted to int32.
	pointerCheck(scratch, pointer, t)
	if anInt != *(*int)(pointer) {
		t.Error("value mismatch, wanted", anInt, "got", *(*int)(scratch))
	}
}

// Int8
// Int16

func TestInt32PointerAndSize(t *testing.T) {

	var scratchSpace [8]byte
	scratch := unsafe.Pointer(&scratchSpace[0])

	var anInt int32 = 1

	pointer, size := getPointerAndSize(anInt, scratch)

	sizeCheck(4, size, t)
	pointerCheck(scratch, pointer, t)
	if anInt != *(*int32)(pointer) {
		t.Error("value mismatch, wanted", anInt, "got", *(*int)(scratch))
	}

	pointer, size = getPointerAndSize(&anInt, scratch)

	sizeCheck(4, size, t)
	pointerCheck(unsafe.Pointer(&anInt), pointer, t)
	if anInt != *(*int32)(pointer) {
		t.Error("value mismatch, wanted", anInt, "got", *(*int)(scratch))
	}
}

// Int64
// Uint
// Uint8
// Uint16
// Uint32
// Uint64
// Uintptr
// Float32
// Float64
// Complex64
// Complex128
// Array
// Interface
// Ptr
// Slice
// String
// Struct

func TestChar2(t *testing.T) {

	got := NewChar2(Char(1), Char(2))

	for i := 0; i < 2; i++ {
		gotValue := got.Get(i)
		wantValue := Char(i + 1)
		if gotValue != wantValue {
			t.Fatalf("get/new failure: index %d, want %d, got %d", i, gotValue, wantValue)
		}
	}

	var want []int8
	for i := 0; i < 2; i++ {
		v := int8(i) + 3
		got.Set(i, Char(v))
		want = append(want, v)
	}

	gotBytes := toByteSlice(unsafe.Pointer(&got), unsafe.Sizeof(got))
	wantBytes := toByteSlice(unsafe.Pointer(&want[0]), uintptr(len(want))*unsafe.Sizeof(want[0]))
	if !bytes.Equal(gotBytes, wantBytes) {
		t.Fatalf("set failure:\nwant [% x]\ngot  [% x]", wantBytes, gotBytes)
	}
}

func TestDouble16(t *testing.T) {
	var got Double16
	var want []float64
	for i := 0; i < 16; i++ {
		v := float64(i) + 1
		got.Set(i, Double(v))
		want = append(want, v)
	}
	for i := 0; i < len(want); i++ {
		gotBytes := toByteSlice(unsafe.Pointer(&got), unsafe.Sizeof(got))
		wantBytes := toByteSlice(unsafe.Pointer(&want[0]), uintptr(len(want))*unsafe.Sizeof(want[0]))
		if !bytes.Equal(gotBytes, wantBytes) {
			t.Fatalf("set failure:\nwant [% x]\ngot  [% x]", wantBytes, gotBytes)
		}
	}
}
<commit_msg>Removed tests of removed types.<commit_after>package cl11

import (
	"testing"
	"unsafe"
)

var scratchSpace = unsafe.Pointer(new([scratchSize]byte))

func sizeCheck(want int, got uintptr, t *testing.T) {
	if want != int(got) {
		t.Error("size mismatch, wanted", want, "got", got)
	}
}

func pointerCheck(want, got unsafe.Pointer, t *testing.T) {
	if want != got {
		t.Error("pointer mismatch, wanted", want, "got", got)
	}
}

// Bool

func TestIntPointerAndSize(t *testing.T) {

	var scratchSpace [8]byte
	scratch := unsafe.Pointer(&scratchSpace[0])

	var anInt int = 1

	pointer, size := getPointerAndSize(anInt, scratch)

	sizeCheck(4, size, t)
	pointerCheck(scratch, pointer, t)
	if anInt != *(*int)(pointer) {
		t.Error("value mismatch, wanted", anInt, "got", *(*int)(scratch))
	}

	pointer, size = getPointerAndSize(&anInt, scratch)

	sizeCheck(4, size, t)
	// Expect scratch since int needs to be converted to int32.
	pointerCheck(scratch, pointer, t)
	if anInt != *(*int)(pointer) {
		t.Error("value mismatch, wanted", anInt, "got", *(*int)(scratch))
	}
}

// Int8
// Int16

func TestInt32PointerAndSize(t *testing.T) {

	var scratchSpace [8]byte
	scratch := unsafe.Pointer(&scratchSpace[0])

	var anInt int32 = 1

	pointer, size := getPointerAndSize(anInt, scratch)

	sizeCheck(4, size, t)
	pointerCheck(scratch, pointer, t)
	if anInt != *(*int32)(pointer) {
		t.Error("value mismatch, wanted", anInt, "got", *(*int)(scratch))
	}

	pointer, size = getPointerAndSize(&anInt, scratch)

	sizeCheck(4, size, t)
	pointerCheck(unsafe.Pointer(&anInt), pointer, t)
	if anInt != *(*int32)(pointer) {
		t.Error("value mismatch, wanted", anInt, "got", *(*int)(scratch))
	}
}

// Int64
// Uint
// Uint8
// Uint16
// Uint32
// Uint64
// Uintptr
// Float32
// Float64
// Complex64
// Complex128
// Array
// Interface
// Ptr
// Slice
// String
// Struct
<|endoftext|>


<commit_before>/*--------------------------------------------------------*\
|                                                          |
|                          hprose                          |
|                                                          |
| Official WebSite: https://hprose.com                     |
|                                                          |
| rpc/core/service_codec.go                                |
|                                                          |
| LastModified: Feb 17, 2021                               |
| Author: Ma Bingyao <andot@hprose.com>                    |
|                                                          |
\*________________________________________________________*/

package core

import (
	"errors"
	"reflect"

	"github.com/hprose/hprose-golang/v3/encoding"
)

// ServiceCodec for RPC.
type ServiceCodec interface {
	Encode(result interface{}, context ServiceContext) (response []byte, err error)
	Decode(request []byte, context ClientContext) (name string, args []interface{}, err error)
}

type serviceCodec struct {
	Debug  bool
	Simple bool
	encoding.LongType
	encoding.RealType
	encoding.MapType
}

func (c serviceCodec) Encode(result interface{}, context ServiceContext) (response []byte, err error) {
	encoder := new(encoding.Encoder).Simple(c.Simple)
	if c.Simple {
		context.RequestHeaders().Set("simple", true)
	}
	if context.HasRequestHeaders() {
		encoder.WriteTag(encoding.TagHeader)
		encoder.Write((map[string]interface{})(context.RequestHeaders().(dict)))
		encoder.Reset()
	}
	encoder.WriteTag(encoding.TagCall)
	if e, ok := result.(error); ok {
		encoder.WriteTag(encoding.TagError)
		var msg string
		if pe, ok := e.(*PanicError); ok && c.Debug {
			msg = pe.String()
		} else {
			msg = err.Error()
		}
		encoder.WriteString(msg)
	} else {
		encoder.WriteTag(encoding.TagResult)
		encoder.Write(result)
	}
	encoder.WriteTag(encoding.TagEnd)
	return encoder.Bytes(), encoder.Error
}

func (c serviceCodec) Decode(request []byte, context ServiceContext) (name string, args []interface{}, err error) {
	if len(request) == 0 {
		name = "~"
		_, err = c.decodeMethod(name, context)
		return
	}
	decoder := encoding.NewDecoder(request).Simple(false)
	decoder.LongType = c.LongType
	decoder.RealType = c.RealType
	decoder.MapType = c.MapType
	tag := decoder.NextByte()
	if tag == encoding.TagHeader {
		var h map[string]interface{}
		decoder.Decode(&h)
		((dict)(h)).CopyTo(context.ResponseHeaders())
		decoder.Reset()
		tag = decoder.NextByte()
	}
	switch tag {
	case encoding.TagCall:
		if context.RequestHeaders().GetBool("simple") {
			decoder.Simple(true)
		}
		decoder.Decode(&name)
		var method Method
		if method, err = c.decodeMethod(name, context); err == nil {
			args, err = c.decodeArguments(method, decoder, context)
		}
	case encoding.TagEnd:
		name = "~"
		_, err = c.decodeMethod("~", context)
	default:
		err = errors.New("Invalid request:\r\n" + string(request))
	}
	return
}

func (c serviceCodec) decodeMethod(name string, context ServiceContext) (method Method, err error) {
	service := context.Service()
	method = service.Get(name)
	if method == nil {
		err = errors.New("Can't find this method " + name + "().")
	} else {
		context.SetMethod(method)
	}
	return method, err
}

func (c serviceCodec) decodeArguments(method Method, decoder *encoding.Decoder, context ServiceContext) (args []interface{}, err error) {
	tag := decoder.NextByte()
	if tag != encoding.TagList {
		return
	}
	decoder.Reset()
	if method.Missing() {
		decoder.Decode(&args, tag)
		return args, decoder.Error
	}
	count := decoder.ReadInt()
	parameters := method.Parameters()
	if len(parameters) == 0 {
		parameters = make([]reflect.Type, count)
	} else {
		n := len(parameters)
		for i := n; i < count; i++ {
			parameters = append(parameters, nil)
		}
	}
	args = make([]interface{}, count)
	decoder.AddReference(&args)
	for i := 0; i < count; i++ {
		args[i] = decoder.Read(parameters[i])
	}
	decoder.Skip()
	return args, decoder.Error
}
<commit_msg>Update service_codec.go<commit_after>/*--------------------------------------------------------*\
|                                                          |
|                          hprose                          |
|                                                          |
| Official WebSite: https://hprose.com                     |
|                                                          |
| rpc/core/service_codec.go                                |
|                                                          |
| LastModified: Feb 17, 2021                               |
| Author: Ma Bingyao <andot@hprose.com>                    |
|                                                          |
\*________________________________________________________*/

package core

import (
	"errors"
	"reflect"

	"github.com/hprose/hprose-golang/v3/encoding"
)

// ServiceCodec for RPC.
type ServiceCodec interface {
	Encode(result interface{}, context ServiceContext) (response []byte, err error)
	Decode(request []byte, context ServiceContext) (name string, args []interface{}, err error)
}

type serviceCodec struct {
	Debug  bool
	Simple bool
	encoding.LongType
	encoding.RealType
	encoding.MapType
}

func (c serviceCodec) Encode(result interface{}, context ServiceContext) (response []byte, err error) {
	encoder := new(encoding.Encoder).Simple(c.Simple)
	if c.Simple {
		context.RequestHeaders().Set("simple", true)
	}
	if context.HasRequestHeaders() {
		encoder.WriteTag(encoding.TagHeader)
		encoder.Write((map[string]interface{})(context.RequestHeaders().(dict)))
		encoder.Reset()
	}
	encoder.WriteTag(encoding.TagCall)
	if e, ok := result.(error); ok {
		encoder.WriteTag(encoding.TagError)
		var msg string
		if pe, ok := e.(*PanicError); ok && c.Debug {
			msg = pe.String()
		} else {
			msg = err.Error()
		}
		encoder.WriteString(msg)
	} else {
		encoder.WriteTag(encoding.TagResult)
		encoder.Write(result)
	}
	encoder.WriteTag(encoding.TagEnd)
	return encoder.Bytes(), encoder.Error
}

func (c serviceCodec) Decode(request []byte, context ServiceContext) (name string, args []interface{}, err error) {
	if len(request) == 0 {
		name = "~"
		_, err = c.decodeMethod(name, context)
		return
	}
	decoder := encoding.NewDecoder(request).Simple(false)
	decoder.LongType = c.LongType
	decoder.RealType = c.RealType
	decoder.MapType = c.MapType
	tag := decoder.NextByte()
	if tag == encoding.TagHeader {
		var h map[string]interface{}
		decoder.Decode(&h)
		((dict)(h)).CopyTo(context.ResponseHeaders())
		decoder.Reset()
		tag = decoder.NextByte()
	}
	switch tag {
	case encoding.TagCall:
		if context.RequestHeaders().GetBool("simple") {
			decoder.Simple(true)
		}
		decoder.Decode(&name)
		var method Method
		if method, err = c.decodeMethod(name, context); err == nil {
			args, err = c.decodeArguments(method, decoder, context)
		}
	case encoding.TagEnd:
		name = "~"
		_, err = c.decodeMethod("~", context)
	default:
		err = errors.New("Invalid request:\r\n" + string(request))
	}
	return
}

func (c serviceCodec) decodeMethod(name string, context ServiceContext) (method Method, err error) {
	service := context.Service()
	method = service.Get(name)
	if method == nil {
		err = errors.New("Can't find this method " + name + "().")
	} else {
		context.SetMethod(method)
	}
	return method, err
}

func (c serviceCodec) decodeArguments(method Method, decoder *encoding.Decoder, context ServiceContext) (args []interface{}, err error) {
	tag := decoder.NextByte()
	if tag != encoding.TagList {
		return
	}
	decoder.Reset()
	if method.Missing() {
		decoder.Decode(&args, tag)
		return args, decoder.Error
	}
	count := decoder.ReadInt()
	parameters := method.Parameters()
	paramTypes := make([]reflect.Type, count)
	if method.Func().Type().IsVariadic() {
		n := len(parameters)
		copy(paramTypes, parameters[:n-1])
		for i := n; i < count; i++ {
			paramTypes[i] = parameters[n-1].Elem()
		}
	} else {
		copy(paramTypes, parameters[:])
	}
	args = make([]interface{}, count)
	decoder.AddReference(&args)
	for i := 0; i < count; i++ {
		args[i] = decoder.Read(paramTypes[i])
	}
	decoder.Skip()
	return args, decoder.Error
}

// NewServiceCodec returns the ServiceCodec.
func NewServiceCodec(debug bool, simple bool, longType encoding.LongType, realType encoding.RealType, mapType encoding.MapType) ServiceCodec {
	return serviceCodec{debug, simple, longType, realType, mapType}
}
<|endoftext|>


<commit_before>// Copyright (c) 2015 The btcsuite developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.

/*
Package peer provides a common base for creating and managing Bitcoin network
peers.

Overview

This package builds upon the wire package, which provides the fundamental
primitives necessary to speak the bitcoin wire protocol, in order to simplify
the process of creating fully functional peers.  In essence, it provides a
common base for creating concurrent safe fully validating nodes, Simplified
Payment Verification (SPV) nodes, proxies, etc.

A quick overview of the major features peer provides are as follows:

 - Provides a basic concurrent safe bitcoin peer for handling bitcoin
   communications via the peer-to-peer protocol
 - Full duplex reading and writing of bitcoin protocol messages
 - Automatic handling of the initial handshake process including protocol
   version negotiation
 - Asynchronous message queueing of outbound messages with optional channel for
   notification when the message is actually sent
 - Flexible peer configuration
   - Caller is responsible for creating outgoing connections and listening for
     incoming connections so they have flexibility to establish connections as
     they see fit (proxies, etc)
   - User agent name and version
   - Bitcoin network
   - Service support signalling (full nodes, bloom filters, etc)
   - Maximum supported protocol version
   - Ability to register callbacks for handling bitcoin protocol messages
 - Inventory message batching and send trickling with known inventory detection
   and avoidance
 - Automatic periodic keep-alive pinging and pong responses
 - Random nonce generation and self connection detection
 - Proper handling of bloom filter related commands when the caller does not
   specify the related flag to signal support
   - Disconnects the peer when the protocol version is high enough
   - Does not invoke the related callbacks for older protocol versions
 - Snapshottable peer statistics such as the total number of bytes read and
   written, the remote address, user agent, and negotiated protocol version
 - Helper functions pushing addresses, getblocks, getheaders, and reject
   messages
   - These could all be sent manually via the standard message output function,
     but the helpers provide additional nice functionality such as duplicate
     filtering and address randomization
 - Ability to wait for shutdown/disconnect
 - Comprehensive test coverage

Peer Configuration

All peer configuration is handled with the Config struct.  This allows the
caller to specify things such as the user agent name and version, the bitcoin
network to use, which services it supports, and callbacks to invoke when bitcoin
messages are received.  See the documentation for each field of the Config
struct for more details.

Inbound and Outbound Peers

A peer can either be inbound or outbound.  The caller is responsible for
establishing the connection to remote peers and listening for incoming peers.
This provides high flexibility for things such as using proxies, acting as a
proxy, creating bride peers, choosing whether to listen for inbound peers, etc.

For outgoing peers, the NewOutboundPeer function must be used to specify the
configuration followed by invoking Connect with the net.Conn instance.  This
start all async I/O goroutines and initiate the initial negotiation process.
Once that has been completed, the peer is fully functional.

For inbound peers, the NewInboundPeer function must be used to specify the
configuration and net.Conn instance followed by invoking Start.  This will start
all async I/O goroutines and listen for the initial negotiation process.  Once
that has been completed, the peer is fully functional.

Callbacks

In order to do anything useful with a peer, it is necessary to react to bitcoin
messages.  This is accomplished by creating an instance of the MessageListeners
struct with the callbacks to be invoke specified and setting the Listeners field
of the Config struct specified when creating a peer to it.

For convenience, a callback hook for all of the currently supported bitcoin
messages is exposed which receives the peer instance and the concrete message
type.  In addition, a hook for OnRead is provided so even custom messages types
for which this package does not directly provide a hook, as long as they
implement the wire.Message interface, can be used.  Finally, the OnWrite hook
is provided, which in conjunction with OnRead, can be used to track server-wide
byte counts.

It is often useful to use closures which encapsulate state when specifying the
callback handlers.  This provides a clean method for accessing that state when
callbacks are invoked.  TODO(davec): Provide example...

Queuing Messages and Inventory

The QueueMessage function provides the fundamental means to send messages to the
remote peer.  As the name implies, this employs a non-blocking queue.  A done
channel which will be notified when the message is actually sent can optionally
be specified.  There are certain message types which are better send using other
functions which provide additional functionality.

Of special interest are inventory messages.  Rather than manually sending MsgInv
message via Queuemessage, the inventory vectors should be queued using the
QueueInventory function.  It employs batching and trickling along with
intelligent known remote peer inventory detection and avoidance through the use
of a most-recently used algorithm.

Message Sending Helper Functions

In addition to the bare QueueMessage function previously described, the
PushAddrMsg, PushGetBlocksMsg, PushGetHeadersMsg, and PushRejectMsg functions
are provided as a convenience.  While it is of course possible to create and
send these message manually via QueueMessage, these helper functions provided
additional useful functionality that is typically desired.

For example, the PushAddrMsg function automatically limits the addresses to the
maximum number allowed by the message and randomizes the chosen addresses when
there are too many.  This allows the caller to simply provide a slice of known
addresses, such as that returned by the addrmgr package, without having to worry
about the details.

Next, the PushGetBlocksMsg and PushGetHeadersMsg functions will construct proper
messages using a block locator and ignore back to back duplicate requests.

Finally, the PushRejectMsg function can be used to easily create and send an
appropriate reject message based on the provided parameters as well as
optionally provides a flag to cause it to block until the message is actually
sent.

Peer Statistics

A snapshot of the current peer statistics can be obtained with the StatsSnapshot
function.  This includes statistics such as the total number of bytes read and
written, the remote address, user agent, and negotiated protocol version.

Logging

This package provides extensive logging capabilities through the UseLogger
function which allows a btclog.Logger to be specified.  For example, logging at
the debug level provides summaries of every message sent and received, and
logging at the trace level provides full dumps of parsed messages as well as the
raw message bytes using a format similar to hexdump -C.

Bitcoin Improvement Proposals

This package supported all BIPS support by the
[wire](https://godoc.org/github.com/btcsuite/btcd/wire#hdr-Bitcoin_Improvement_Proposals)
package.
*/
package peer
<commit_msg>peer: Correct a few typos in documentation.<commit_after>// Copyright (c) 2015 The btcsuite developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.

/*
Package peer provides a common base for creating and managing Bitcoin network
peers.

Overview

This package builds upon the wire package, which provides the fundamental
primitives necessary to speak the bitcoin wire protocol, in order to simplify
the process of creating fully functional peers.  In essence, it provides a
common base for creating concurrent safe fully validating nodes, Simplified
Payment Verification (SPV) nodes, proxies, etc.

A quick overview of the major features peer provides are as follows:

 - Provides a basic concurrent safe bitcoin peer for handling bitcoin
   communications via the peer-to-peer protocol
 - Full duplex reading and writing of bitcoin protocol messages
 - Automatic handling of the initial handshake process including protocol
   version negotiation
 - Asynchronous message queueing of outbound messages with optional channel for
   notification when the message is actually sent
 - Flexible peer configuration
   - Caller is responsible for creating outgoing connections and listening for
     incoming connections so they have flexibility to establish connections as
     they see fit (proxies, etc)
   - User agent name and version
   - Bitcoin network
   - Service support signalling (full nodes, bloom filters, etc)
   - Maximum supported protocol version
   - Ability to register callbacks for handling bitcoin protocol messages
 - Inventory message batching and send trickling with known inventory detection
   and avoidance
 - Automatic periodic keep-alive pinging and pong responses
 - Random nonce generation and self connection detection
 - Proper handling of bloom filter related commands when the caller does not
   specify the related flag to signal support
   - Disconnects the peer when the protocol version is high enough
   - Does not invoke the related callbacks for older protocol versions
 - Snapshottable peer statistics such as the total number of bytes read and
   written, the remote address, user agent, and negotiated protocol version
 - Helper functions pushing addresses, getblocks, getheaders, and reject
   messages
   - These could all be sent manually via the standard message output function,
     but the helpers provide additional nice functionality such as duplicate
     filtering and address randomization
 - Ability to wait for shutdown/disconnect
 - Comprehensive test coverage

Peer Configuration

All peer configuration is handled with the Config struct.  This allows the
caller to specify things such as the user agent name and version, the bitcoin
network to use, which services it supports, and callbacks to invoke when bitcoin
messages are received.  See the documentation for each field of the Config
struct for more details.

Inbound and Outbound Peers

A peer can either be inbound or outbound.  The caller is responsible for
establishing the connection to remote peers and listening for incoming peers.
This provides high flexibility for things such as connecting via proxies, acting
as a proxy, creating bridge peers, choosing whether to listen for inbound peers,
etc.

For outgoing peers, the NewOutboundPeer function must be used to specify the
configuration followed by invoking Connect with the net.Conn instance.  This
 will start all async I/O goroutines and initiate the initial negotiation
process.  Once that has been completed, the peer is fully functional.

For inbound peers, the NewInboundPeer function must be used to specify the
configuration and net.Conn instance followed by invoking Start.  This will start
all async I/O goroutines and listen for the initial negotiation process.  Once
that has been completed, the peer is fully functional.

Callbacks

In order to do anything useful with a peer, it is necessary to react to bitcoin
messages.  This is accomplished by creating an instance of the MessageListeners
struct with the callbacks to be invoke specified and setting the Listeners field
of the Config struct specified when creating a peer to it.

For convenience, a callback hook for all of the currently supported bitcoin
messages is exposed which receives the peer instance and the concrete message
type.  In addition, a hook for OnRead is provided so even custom messages types
for which this package does not directly provide a hook, as long as they
implement the wire.Message interface, can be used.  Finally, the OnWrite hook
is provided, which in conjunction with OnRead, can be used to track server-wide
byte counts.

It is often useful to use closures which encapsulate state when specifying the
callback handlers.  This provides a clean method for accessing that state when
callbacks are invoked.

Queuing Messages and Inventory

The QueueMessage function provides the fundamental means to send messages to the
remote peer.  As the name implies, this employs a non-blocking queue.  A done
channel which will be notified when the message is actually sent can optionally
be specified.  There are certain message types which are better sent using other
functions which provide additional functionality.

Of special interest are inventory messages.  Rather than manually sending MsgInv
messages via Queuemessage, the inventory vectors should be queued using the
QueueInventory function.  It employs batching and trickling along with
intelligent known remote peer inventory detection and avoidance through the use
of a most-recently used algorithm.

Message Sending Helper Functions

In addition to the bare QueueMessage function previously described, the
PushAddrMsg, PushGetBlocksMsg, PushGetHeadersMsg, and PushRejectMsg functions
are provided as a convenience.  While it is of course possible to create and
send these message manually via QueueMessage, these helper functions provided
additional useful functionality that is typically desired.

For example, the PushAddrMsg function automatically limits the addresses to the
maximum number allowed by the message and randomizes the chosen addresses when
there are too many.  This allows the caller to simply provide a slice of known
addresses, such as that returned by the addrmgr package, without having to worry
about the details.

Next, the PushGetBlocksMsg and PushGetHeadersMsg functions will construct proper
messages using a block locator and ignore back to back duplicate requests.

Finally, the PushRejectMsg function can be used to easily create and send an
appropriate reject message based on the provided parameters as well as
optionally provides a flag to cause it to block until the message is actually
sent.

Peer Statistics

A snapshot of the current peer statistics can be obtained with the StatsSnapshot
function.  This includes statistics such as the total number of bytes read and
written, the remote address, user agent, and negotiated protocol version.

Logging

This package provides extensive logging capabilities through the UseLogger
function which allows a btclog.Logger to be specified.  For example, logging at
the debug level provides summaries of every message sent and received, and
logging at the trace level provides full dumps of parsed messages as well as the
raw message bytes using a format similar to hexdump -C.

Bitcoin Improvement Proposals

This package supports all BIPS supported by the wire packge.
(https://godoc.org/github.com/btcsuite/btcd/wire#hdr-Bitcoin_Improvement_Proposals)
*/
package peer
<|endoftext|>


<commit_before>// +build !windows

package transport

import (
	"net"
	"time"

	"git.apache.org/thrift.git/lib/go/thrift"
	"github.com/pkg/errors"
)

// Open opens the unix domain socket with the provided path and timeout,
// returning a TTransport.
func Open(sockPath string, timeout time.Duration) (*thrift.TSocket, error) {
	addr, err := net.ResolveUnixAddr("unix", sockPath)
	if err != nil {
		return nil, errors.Wrapf(err, "resolving socket path '%s'", sockPath)
	}

	trans := thrift.NewTSocketFromAddrTimeout(addr, timeout)
	if err := trans.Open(); err != nil {
		return nil, errors.Wrap(err, "opening socket transport")
	}

	return trans, nil
}

func OpenServer(listenPath string, timeout time.Duration) (*thrift.TServerSocket, error) {
	addr, err := net.ResolveUnixAddr("unix", listenPath)
	if err != nil {
		return nil, errors.Wrapf(err, "resolving addr (%s)", addr)
	}

	return thrift.NewTServerSocketFromAddrTimeout(addr, 0), nil
}
<commit_msg>wait for unix socket to be available (#51)<commit_after>// +build !windows

package transport

import (
	"context"
	"net"
	"os"
	"time"

	"git.apache.org/thrift.git/lib/go/thrift"
	"github.com/pkg/errors"
)

// Open opens the unix domain socket with the provided path and timeout,
// returning a TTransport.
func Open(sockPath string, timeout time.Duration) (*thrift.TSocket, error) {
	addr, err := net.ResolveUnixAddr("unix", sockPath)
	if err != nil {
		return nil, errors.Wrapf(err, "resolving socket path '%s'", sockPath)
	}

	// the timeout parameter is passed to thrift, which passes it to net.DialTimeout
	// but it looks like net.DialTimeout ignores timeouts for unix socket and immediately returns an error
	// waitForSocket will loop every 200ms to stat the socket path,
	// or until the timeout value passes, similar to the C++ and python implementations.
	if err := waitForSocket(sockPath, timeout); err != nil {
		return nil, errors.Wrapf(err, "waiting for unix socket to be available: %s", sockPath)
	}

	trans := thrift.NewTSocketFromAddrTimeout(addr, timeout)
	if err := trans.Open(); err != nil {
		return nil, errors.Wrap(err, "opening socket transport")
	}

	return trans, nil
}

func OpenServer(listenPath string, timeout time.Duration) (*thrift.TServerSocket, error) {
	addr, err := net.ResolveUnixAddr("unix", listenPath)
	if err != nil {
		return nil, errors.Wrapf(err, "resolving addr (%s)", addr)
	}

	return thrift.NewTServerSocketFromAddrTimeout(addr, 0), nil
}

func waitForSocket(sockPath string, timeout time.Duration) error {
	ticker := time.NewTicker(200 * time.Millisecond)
	defer ticker.Stop()
	ctx, cancel := context.WithTimeout(context.Background(), timeout)
	defer cancel()
	for {
		select {
		case <-ctx.Done():
			return ctx.Err()
		case <-ticker.C:
			if _, err := os.Stat(sockPath); err == nil {
				return nil
			}
		}
	}
}
<|endoftext|>


<commit_before>package pkglib

import (
	"flag"
	"fmt"
	"gopkg.in/yaml.v2"
	"io/ioutil"
	"os"
	"path/filepath"
	"strings"

	"github.com/linuxkit/linuxkit/src/cmd/linuxkit/moby"
)

// Contains fields settable in the build.yml
type pkgInfo struct {
	Image               string            `yaml:"image"`
	Org                 string            `yaml:"org"`
	Arches              []string          `yaml:"arches"`
	ExtraSources        []string          `yaml:"extra-sources"`
	GitRepo             string            `yaml:"gitrepo"` // ??
	Network             bool              `yaml:"network"`
	DisableContentTrust bool              `yaml:"disable-content-trust"`
	DisableCache        bool              `yaml:"disable-cache"`
	Config              *moby.ImageConfig `yaml:"config"`
	Depends             struct {
		DockerImages struct {
			TargetDir string   `yaml:"target-dir"`
			Target    string   `yaml:"target"`
			FromFile  string   `yaml:"from-file"`
			List      []string `yaml:"list"`
		} `yaml:"docker-images"`
	} `yaml:"depends"`
}

// Specifies the source directory for a package and their destination in the build context.
type pkgSource struct {
	src string
	dst string
}

// Pkg encapsulates information about a package's source
type Pkg struct {
	// These correspond to pkgInfo fields
	image         string
	org           string
	arches        []string
	sources       []pkgSource
	gitRepo       string
	network       bool
	trust         bool
	cache         bool
	config        *moby.ImageConfig
	dockerDepends dockerDepends

	// Internal state
	path       string
	hash       string
	dirty      bool
	commitHash string
	git        *git
}

// NewFromCLI creates a Pkg from a set of CLI arguments. Calls fs.Parse()
func NewFromCLI(fs *flag.FlagSet, args ...string) (Pkg, error) {
	// Defaults
	pi := pkgInfo{
		Org:                 "linuxkit",
		Arches:              []string{"amd64", "arm64", "s390x"},
		GitRepo:             "https://github.com/linuxkit/linuxkit",
		Network:             false,
		DisableContentTrust: false,
		DisableCache:        false,
	}

	// TODO(ijc) look for "$(git rev-parse --show-toplevel)/.build-defaults.yml"?

	// Ideally want to look at every directory from root to `pkg`
	// for this file but might be tricky to arrange ordering-wise.

	// These override fields in pi below, bools are in both forms to allow user overrides in either direction
	argDisableCache := fs.Bool("disable-cache", pi.DisableCache, "Disable build cache")
	argEnableCache := fs.Bool("enable-cache", !pi.DisableCache, "Enable build cache")
	argDisableContentTrust := fs.Bool("disable-content-trust", pi.DisableContentTrust, "Disable content trust")
	argEnableContentTrust := fs.Bool("enable-content-trust", !pi.DisableContentTrust, "Enable content trust")
	argNoNetwork := fs.Bool("nonetwork", !pi.Network, "Disallow network use during build")
	argNetwork := fs.Bool("network", pi.Network, "Allow network use during build")

	argOrg := fs.String("org", pi.Org, "Override the hub org")

	// Other arguments
	var buildYML, hash, hashCommit, hashPath string
	var dirty, devMode bool
	fs.StringVar(&buildYML, "build-yml", "build.yml", "Override the name of the yml file")
	fs.StringVar(&hash, "hash", "", "Override the image hash (default is to query git for the package's tree-sh)")
	fs.StringVar(&hashCommit, "hash-commit", "HEAD", "Override the git commit to use for the hash")
	fs.StringVar(&hashPath, "hash-path", "", "Override the directory to use for the image hash, must be a parent of the package dir (default is to use the package dir)")
	fs.BoolVar(&dirty, "force-dirty", false, "Force the pkg to be considered dirty")
	fs.BoolVar(&devMode, "dev", false, "Force org and hash to $USER and \"dev\" respectively")

	fs.Parse(args)

	if fs.NArg() < 1 {
		return Pkg{}, fmt.Errorf("A pkg directory is required")
	}
	if fs.NArg() > 1 {
		return Pkg{}, fmt.Errorf("Unknown extra arguments given: %s", fs.Args()[1:])
	}

	pkg := fs.Arg(0)
	pkgPath, err := filepath.Abs(pkg)
	if err != nil {
		return Pkg{}, err
	}

	if hashPath == "" {
		hashPath = pkgPath
	} else {
		hashPath, err = filepath.Abs(hashPath)
		if err != nil {
			return Pkg{}, err
		}

		if !strings.HasPrefix(pkgPath, hashPath) {
			return Pkg{}, fmt.Errorf("Hash path is not a prefix of the package path")
		}

		// TODO(ijc) pkgPath and hashPath really ought to be in the same git tree too...
	}

	b, err := ioutil.ReadFile(filepath.Join(pkgPath, buildYML))
	if err != nil {
		return Pkg{}, err
	}
	if err := yaml.Unmarshal(b, &pi); err != nil {
		return Pkg{}, err
	}

	if pi.Image == "" {
		return Pkg{}, fmt.Errorf("Image field is required")
	}

	dockerDepends, err := newDockerDepends(pkgPath, &pi)
	if err != nil {
		return Pkg{}, err
	}

	if devMode {
		// If --org is also used then this will be overwritten
		// by argOrg when we iterate over the provided options
		// in the fs.Visit block below.
		pi.Org = os.Getenv("USER")
		if hash == "" {
			hash = "dev"
		}
	}

	// Go's flag package provides no way to see if a flag was set
	// apart from Visit which iterates over only those which were
	// set.
	fs.Visit(func(f *flag.Flag) {
		switch f.Name {
		case "disable-cache":
			pi.DisableCache = *argDisableCache
		case "enable-cache":
			pi.DisableCache = !*argEnableCache
		case "disable-content-trust":
			pi.DisableContentTrust = *argDisableContentTrust
		case "enable-content-trust":
			pi.DisableContentTrust = !*argEnableContentTrust
		case "network":
			pi.Network = *argNetwork
		case "nonetwork":
			pi.Network = !*argNoNetwork
		case "org":
			pi.Org = *argOrg
		}
	})

	git, err := newGit(pkgPath)
	if err != nil {
		return Pkg{}, err
	}

	if git != nil {
		gitDirty, err := git.isDirty(hashPath, hashCommit)
		if err != nil {
			return Pkg{}, err
		}

		dirty = dirty || gitDirty

		if hash == "" {
			if hash, err = git.treeHash(hashPath, hashCommit); err != nil {
				return Pkg{}, err
			}

			if dirty {
				hash += "-dirty"
			}
		}
	}

	return Pkg{
		image:         pi.Image,
		org:           pi.Org,
		hash:          hash,
		commitHash:    hashCommit,
		arches:        pi.Arches,
		sources:       pi.Sources,
		gitRepo:       pi.GitRepo,
		network:       pi.Network,
		trust:         !pi.DisableContentTrust,
		cache:         !pi.DisableCache,
		config:        pi.Config,
		dockerDepends: dockerDepends,
		dirty:         dirty,
		path:          pkgPath,
		git:           git,
	}, nil
}

// Hash returns the hash of the package
func (p Pkg) Hash() string {
	return p.hash
}

// ReleaseTag returns the tag to use for a particular release of the package
func (p Pkg) ReleaseTag(release string) (string, error) {
	if release == "" {
		return "", fmt.Errorf("A release tag is required")
	}
	if p.dirty {
		return "", fmt.Errorf("Cannot release a dirty package")
	}
	tag := p.org + "/" + p.image + ":" + release
	return tag, nil
}

// Tag returns the tag to use for the package
func (p Pkg) Tag() string {
	t := p.hash
	if t == "" {
		t = "latest"
	}
	return p.org + "/" + p.image + ":" + t
}

// TrustEnabled returns true if trust is enabled
func (p Pkg) TrustEnabled() bool {
	return p.trust
}

func (p Pkg) archSupported(want string) bool {
	for _, supp := range p.arches {
		if supp == want {
			return true
		}
	}
	return false
}

func (p Pkg) cleanForBuild() error {
	if p.commitHash != "HEAD" {
		return fmt.Errorf("Cannot build from commit hash != HEAD")
	}
	return nil
}

// Expands path from relative to abs against base, ensuring the result is within base, but is not base itself. Field is the fieldname, to be used for constructing the error.
func makeAbsSubpath(field, base, path string) (string, error) {
	if path == "" {
		return "", nil
	}

	if filepath.IsAbs(path) {
		return "", fmt.Errorf("%s must be relative to package directory", field)
	}

	p, err := filepath.Abs(filepath.Join(base, path))
	if err != nil {
		return "", err
	}

	if p == base {
		return "", fmt.Errorf("%s must not be exactly the package directory", field)
	}

	if !filepath.HasPrefix(p, base) {
		return "", fmt.Errorf("%s must be within package directory", field)
	}

	return p, nil
}
<commit_msg>cmd/pkg: Extract 'extra-sources' and adjust hash calculation<commit_after>package pkglib

import (
	"crypto/sha1"
	"flag"
	"fmt"
	"gopkg.in/yaml.v2"
	"io/ioutil"
	"os"
	"path"
	"path/filepath"
	"strings"

	"github.com/linuxkit/linuxkit/src/cmd/linuxkit/moby"
)

// Contains fields settable in the build.yml
type pkgInfo struct {
	Image               string            `yaml:"image"`
	Org                 string            `yaml:"org"`
	Arches              []string          `yaml:"arches"`
	ExtraSources        []string          `yaml:"extra-sources"`
	GitRepo             string            `yaml:"gitrepo"` // ??
	Network             bool              `yaml:"network"`
	DisableContentTrust bool              `yaml:"disable-content-trust"`
	DisableCache        bool              `yaml:"disable-cache"`
	Config              *moby.ImageConfig `yaml:"config"`
	Depends             struct {
		DockerImages struct {
			TargetDir string   `yaml:"target-dir"`
			Target    string   `yaml:"target"`
			FromFile  string   `yaml:"from-file"`
			List      []string `yaml:"list"`
		} `yaml:"docker-images"`
	} `yaml:"depends"`
}

// Specifies the source directory for a package and their destination in the build context.
type pkgSource struct {
	src string
	dst string
}

// Pkg encapsulates information about a package's source
type Pkg struct {
	// These correspond to pkgInfo fields
	image         string
	org           string
	arches        []string
	sources       []pkgSource
	gitRepo       string
	network       bool
	trust         bool
	cache         bool
	config        *moby.ImageConfig
	dockerDepends dockerDepends

	// Internal state
	path       string
	hash       string
	dirty      bool
	commitHash string
	git        *git
}

// NewFromCLI creates a Pkg from a set of CLI arguments. Calls fs.Parse()
func NewFromCLI(fs *flag.FlagSet, args ...string) (Pkg, error) {
	// Defaults
	pi := pkgInfo{
		Org:                 "linuxkit",
		Arches:              []string{"amd64", "arm64", "s390x"},
		GitRepo:             "https://github.com/linuxkit/linuxkit",
		Network:             false,
		DisableContentTrust: false,
		DisableCache:        false,
	}

	// TODO(ijc) look for "$(git rev-parse --show-toplevel)/.build-defaults.yml"?

	// Ideally want to look at every directory from root to `pkg`
	// for this file but might be tricky to arrange ordering-wise.

	// These override fields in pi below, bools are in both forms to allow user overrides in either direction
	argDisableCache := fs.Bool("disable-cache", pi.DisableCache, "Disable build cache")
	argEnableCache := fs.Bool("enable-cache", !pi.DisableCache, "Enable build cache")
	argDisableContentTrust := fs.Bool("disable-content-trust", pi.DisableContentTrust, "Disable content trust")
	argEnableContentTrust := fs.Bool("enable-content-trust", !pi.DisableContentTrust, "Enable content trust")
	argNoNetwork := fs.Bool("nonetwork", !pi.Network, "Disallow network use during build")
	argNetwork := fs.Bool("network", pi.Network, "Allow network use during build")

	argOrg := fs.String("org", pi.Org, "Override the hub org")

	// Other arguments
	var buildYML, hash, hashCommit, hashPath string
	var dirty, devMode bool
	fs.StringVar(&buildYML, "build-yml", "build.yml", "Override the name of the yml file")
	fs.StringVar(&hash, "hash", "", "Override the image hash (default is to query git for the package's tree-sh)")
	fs.StringVar(&hashCommit, "hash-commit", "HEAD", "Override the git commit to use for the hash")
	fs.StringVar(&hashPath, "hash-path", "", "Override the directory to use for the image hash, must be a parent of the package dir (default is to use the package dir)")
	fs.BoolVar(&dirty, "force-dirty", false, "Force the pkg to be considered dirty")
	fs.BoolVar(&devMode, "dev", false, "Force org and hash to $USER and \"dev\" respectively")

	fs.Parse(args)

	if fs.NArg() < 1 {
		return Pkg{}, fmt.Errorf("A pkg directory is required")
	}
	if fs.NArg() > 1 {
		return Pkg{}, fmt.Errorf("Unknown extra arguments given: %s", fs.Args()[1:])
	}

	pkg := fs.Arg(0)
	pkgPath, err := filepath.Abs(pkg)
	if err != nil {
		return Pkg{}, err
	}

	if hashPath == "" {
		hashPath = pkgPath
	} else {
		hashPath, err = filepath.Abs(hashPath)
		if err != nil {
			return Pkg{}, err
		}

		if !strings.HasPrefix(pkgPath, hashPath) {
			return Pkg{}, fmt.Errorf("Hash path is not a prefix of the package path")
		}

		// TODO(ijc) pkgPath and hashPath really ought to be in the same git tree too...
	}

	b, err := ioutil.ReadFile(filepath.Join(pkgPath, buildYML))
	if err != nil {
		return Pkg{}, err
	}
	if err := yaml.Unmarshal(b, &pi); err != nil {
		return Pkg{}, err
	}

	if pi.Image == "" {
		return Pkg{}, fmt.Errorf("Image field is required")
	}

	dockerDepends, err := newDockerDepends(pkgPath, &pi)
	if err != nil {
		return Pkg{}, err
	}

	if devMode {
		// If --org is also used then this will be overwritten
		// by argOrg when we iterate over the provided options
		// in the fs.Visit block below.
		pi.Org = os.Getenv("USER")
		if hash == "" {
			hash = "dev"
		}
	}

	// Go's flag package provides no way to see if a flag was set
	// apart from Visit which iterates over only those which were
	// set.
	fs.Visit(func(f *flag.Flag) {
		switch f.Name {
		case "disable-cache":
			pi.DisableCache = *argDisableCache
		case "enable-cache":
			pi.DisableCache = !*argEnableCache
		case "disable-content-trust":
			pi.DisableContentTrust = *argDisableContentTrust
		case "enable-content-trust":
			pi.DisableContentTrust = !*argEnableContentTrust
		case "network":
			pi.Network = *argNetwork
		case "nonetwork":
			pi.Network = !*argNoNetwork
		case "org":
			pi.Org = *argOrg
		}
	})

	var srcHashes string
	sources := []pkgSource{{src: pkgPath, dst: "/"}}

	for _, source := range pi.ExtraSources {
		tmp := strings.Split(source, ":")
		if len(tmp) != 2 {
			return Pkg{}, fmt.Errorf("Bad source format in %s", source)
		}
		srcPath := filepath.Clean(tmp[0]) // Should work with windows paths
		dstPath := path.Clean(tmp[1])     // 'path' here because this should be a Unix path

		if !filepath.IsAbs(srcPath) {
			srcPath = filepath.Join(pkgPath, srcPath)
		}

		g, err := newGit(srcPath)
		if err != nil {
			return Pkg{}, err
		}
		if g == nil {
			return Pkg{}, fmt.Errorf("Source %s not in a git repository", srcPath)
		}
		h, err := g.treeHash(srcPath, hashCommit)
		if err != nil {
			return Pkg{}, err
		}

		srcHashes += h
		sources = append(sources, pkgSource{src: srcPath, dst: dstPath})
	}

	git, err := newGit(pkgPath)
	if err != nil {
		return Pkg{}, err
	}

	if git != nil {
		gitDirty, err := git.isDirty(hashPath, hashCommit)
		if err != nil {
			return Pkg{}, err
		}

		dirty = dirty || gitDirty

		if hash == "" {
			if hash, err = git.treeHash(hashPath, hashCommit); err != nil {
				return Pkg{}, err
			}

			if srcHashes != "" {
				hash += srcHashes
				hash = fmt.Sprintf("%x", sha1.Sum([]byte(hash)))
			}

			if dirty {
				hash += "-dirty"
			}
		}
	}

	return Pkg{
		image:         pi.Image,
		org:           pi.Org,
		hash:          hash,
		commitHash:    hashCommit,
		arches:        pi.Arches,
		sources:       sources,
		gitRepo:       pi.GitRepo,
		network:       pi.Network,
		trust:         !pi.DisableContentTrust,
		cache:         !pi.DisableCache,
		config:        pi.Config,
		dockerDepends: dockerDepends,
		dirty:         dirty,
		path:          pkgPath,
		git:           git,
	}, nil
}

// Hash returns the hash of the package
func (p Pkg) Hash() string {
	return p.hash
}

// ReleaseTag returns the tag to use for a particular release of the package
func (p Pkg) ReleaseTag(release string) (string, error) {
	if release == "" {
		return "", fmt.Errorf("A release tag is required")
	}
	if p.dirty {
		return "", fmt.Errorf("Cannot release a dirty package")
	}
	tag := p.org + "/" + p.image + ":" + release
	return tag, nil
}

// Tag returns the tag to use for the package
func (p Pkg) Tag() string {
	t := p.hash
	if t == "" {
		t = "latest"
	}
	return p.org + "/" + p.image + ":" + t
}

// TrustEnabled returns true if trust is enabled
func (p Pkg) TrustEnabled() bool {
	return p.trust
}

func (p Pkg) archSupported(want string) bool {
	for _, supp := range p.arches {
		if supp == want {
			return true
		}
	}
	return false
}

func (p Pkg) cleanForBuild() error {
	if p.commitHash != "HEAD" {
		return fmt.Errorf("Cannot build from commit hash != HEAD")
	}
	return nil
}

// Expands path from relative to abs against base, ensuring the result is within base, but is not base itself. Field is the fieldname, to be used for constructing the error.
func makeAbsSubpath(field, base, path string) (string, error) {
	if path == "" {
		return "", nil
	}

	if filepath.IsAbs(path) {
		return "", fmt.Errorf("%s must be relative to package directory", field)
	}

	p, err := filepath.Abs(filepath.Join(base, path))
	if err != nil {
		return "", err
	}

	if p == base {
		return "", fmt.Errorf("%s must not be exactly the package directory", field)
	}

	if !filepath.HasPrefix(p, base) {
		return "", fmt.Errorf("%s must be within package directory", field)
	}

	return p, nil
}
<|endoftext|>


<commit_before>package authentication

import (
	"bytes"
	"encoding/json"
	"fmt"
	"os/exec"
	"strings"

	"github.com/Azure/go-autorest/autorest"
	"github.com/Azure/go-autorest/autorest/adal"
	"github.com/Azure/go-autorest/autorest/azure/cli"
	"github.com/hashicorp/go-multierror"
)

type azureCliTokenAuth struct {
	profile *azureCLIProfile
}

func (a azureCliTokenAuth) build(b Builder) (authMethod, error) {
	auth := azureCliTokenAuth{
		profile: &azureCLIProfile{
			clientId:       b.ClientID,
			environment:    b.Environment,
			subscriptionId: b.SubscriptionID,
			tenantId:       b.TenantID,
		},
	}
	profilePath, err := cli.ProfilePath()
	if err != nil {
		return nil, fmt.Errorf("Error loading the Profile Path from the Azure CLI: %+v", err)
	}

	profile, err := cli.LoadProfile(profilePath)
	if err != nil {
		return nil, fmt.Errorf("Azure CLI Authorization Profile was not found. Please ensure the Azure CLI is installed and then log-in with `az login`.")
	}

	auth.profile.profile = profile

	// Authenticating as a Service Principal doesn't return all of the information we need for authentication purposes
	// as such Service Principal authentication is supported using the specific auth method
	if authenticatedAsAUser := auth.profile.verifyAuthenticatedAsAUser(); !authenticatedAsAUser {
		return nil, fmt.Errorf("Authenticating using the Azure CLI is only supported as a User (not a Service Principal)")
	}

	err = auth.profile.populateFields()
	if err != nil {
		return nil, fmt.Errorf("Error retrieving the Profile from the Azure CLI: %s Please re-authenticate using `az login`.", err)
	}

	err = auth.profile.populateClientId()
	if err != nil {
		return nil, fmt.Errorf("Error populating Client ID from the Azure CLI: %+v", err)
	}

	return auth, nil
}

func (a azureCliTokenAuth) isApplicable(b Builder) bool {
	return b.SupportsAzureCliToken
}

func (a azureCliTokenAuth) getAuthorizationToken(sender autorest.Sender, oauthConfig *adal.OAuthConfig, endpoint string) (*autorest.BearerAuthorizer, error) {
	// the Azure CLI appears to cache these, so to maintain compatibility with the interface this method is intentionally not on the pointer
	token, err := obtainAuthorizationToken(endpoint, a.profile.subscriptionId)
	if err != nil {
		return nil, fmt.Errorf("Error obtaining Authorization Token from the Azure CLI: %s", err)
	}

	adalToken, err := token.ToADALToken()
	if err != nil {
		return nil, fmt.Errorf("Error converting Authorization Token to an ADAL Token: %s", err)
	}

	spt, err := adal.NewServicePrincipalTokenFromManualToken(*oauthConfig, a.profile.clientId, endpoint, adalToken)
	if err != nil {
		return nil, err
	}

	auth := autorest.NewBearerAuthorizer(spt)
	return auth, nil
}

func (a azureCliTokenAuth) name() string {
	return "Obtaining a token from the Azure CLI"
}

func (a azureCliTokenAuth) populateConfig(c *Config) error {
	c.ClientID = a.profile.clientId
	c.Environment = a.profile.environment
	c.SubscriptionID = a.profile.subscriptionId
	c.TenantID = a.profile.tenantId
	return nil
}

func (a azureCliTokenAuth) validate() error {
	var err *multierror.Error

	errorMessageFmt := "A %s was not found in your Azure CLI Credentials.\n\nPlease login to the Azure CLI again via `az login`"

	if a.profile == nil {
		return fmt.Errorf("Azure CLI Profile is nil - this is an internal error and should be reported.")
	}

	if a.profile.clientId == "" {
		err = multierror.Append(err, fmt.Errorf(errorMessageFmt, "Client ID"))
	}

	if a.profile.subscriptionId == "" {
		err = multierror.Append(err, fmt.Errorf(errorMessageFmt, "Subscription ID"))
	}

	if a.profile.tenantId == "" {
		err = multierror.Append(err, fmt.Errorf(errorMessageFmt, "Tenant ID"))
	}

	return err.ErrorOrNil()
}

func obtainAuthorizationToken(endpoint string, subscriptionId string) (*cli.Token, error) {
	var stderr bytes.Buffer
	var stdout bytes.Buffer

	cmd := exec.Command("az", "account", "get-access-token", "--resource", endpoint, "--subscription", subscriptionId, "-o=json")

	cmd.Stderr = &stderr
	cmd.Stdout = &stdout

	if err := cmd.Start(); err != nil {
		return nil, fmt.Errorf("Error launching Azure CLI: %+v", err)
	}

	if err := cmd.Wait(); err != nil {
		return nil, fmt.Errorf("Error waiting for the Azure CLI: %+v", err)
	}

	stdOutStr := stdout.String()
	stdErrStr := stderr.String()

	if stdErrStr != "" {
		return nil, fmt.Errorf("Error retrieving access token from Azure CLI: %s", strings.TrimSpace(stdErrStr))
	}

	var token *cli.Token
	err := json.Unmarshal([]byte(stdOutStr), &token)
	if err != nil {
		return nil, fmt.Errorf("Error unmarshaling Access Token from the Azure CLI: %s", err)
	}

	return token, nil
}
<commit_msg>f/azurecli-auth: making the error message more helpful<commit_after>package authentication

import (
	"bytes"
	"encoding/json"
	"fmt"
	"os/exec"
	"strings"

	"github.com/Azure/go-autorest/autorest"
	"github.com/Azure/go-autorest/autorest/adal"
	"github.com/Azure/go-autorest/autorest/azure/cli"
	"github.com/hashicorp/go-multierror"
)

type azureCliTokenAuth struct {
	profile *azureCLIProfile
}

func (a azureCliTokenAuth) build(b Builder) (authMethod, error) {
	auth := azureCliTokenAuth{
		profile: &azureCLIProfile{
			clientId:       b.ClientID,
			environment:    b.Environment,
			subscriptionId: b.SubscriptionID,
			tenantId:       b.TenantID,
		},
	}
	profilePath, err := cli.ProfilePath()
	if err != nil {
		return nil, fmt.Errorf("Error loading the Profile Path from the Azure CLI: %+v", err)
	}

	profile, err := cli.LoadProfile(profilePath)
	if err != nil {
		return nil, fmt.Errorf("Azure CLI Authorization Profile was not found. Please ensure the Azure CLI is installed and then log-in with `az login`.")
	}

	auth.profile.profile = profile

	// Authenticating as a Service Principal doesn't return all of the information we need for authentication purposes
	// as such Service Principal authentication is supported using the specific auth method
	if authenticatedAsAUser := auth.profile.verifyAuthenticatedAsAUser(); !authenticatedAsAUser {
		return nil, fmt.Errorf(`Authenticating using the Azure CLI is only supported as a User (not a Service Principal).

To authenticate to Azure using a Service Principal, you can use the separate 'Authenticate using a Service Principal'
auth method - instructions for which can be found in the documentation.

Alternatively you can authenticate using the Azure CLI by using a User Account.`)
	}

	err = auth.profile.populateFields()
	if err != nil {
		return nil, fmt.Errorf("Error retrieving the Profile from the Azure CLI: %s Please re-authenticate using `az login`.", err)
	}

	err = auth.profile.populateClientId()
	if err != nil {
		return nil, fmt.Errorf("Error populating Client ID from the Azure CLI: %+v", err)
	}

	return auth, nil
}

func (a azureCliTokenAuth) isApplicable(b Builder) bool {
	return b.SupportsAzureCliToken
}

func (a azureCliTokenAuth) getAuthorizationToken(sender autorest.Sender, oauthConfig *adal.OAuthConfig, endpoint string) (*autorest.BearerAuthorizer, error) {
	// the Azure CLI appears to cache these, so to maintain compatibility with the interface this method is intentionally not on the pointer
	token, err := obtainAuthorizationToken(endpoint, a.profile.subscriptionId)
	if err != nil {
		return nil, fmt.Errorf("Error obtaining Authorization Token from the Azure CLI: %s", err)
	}

	adalToken, err := token.ToADALToken()
	if err != nil {
		return nil, fmt.Errorf("Error converting Authorization Token to an ADAL Token: %s", err)
	}

	spt, err := adal.NewServicePrincipalTokenFromManualToken(*oauthConfig, a.profile.clientId, endpoint, adalToken)
	if err != nil {
		return nil, err
	}

	auth := autorest.NewBearerAuthorizer(spt)
	return auth, nil
}

func (a azureCliTokenAuth) name() string {
	return "Obtaining a token from the Azure CLI"
}

func (a azureCliTokenAuth) populateConfig(c *Config) error {
	c.ClientID = a.profile.clientId
	c.Environment = a.profile.environment
	c.SubscriptionID = a.profile.subscriptionId
	c.TenantID = a.profile.tenantId
	return nil
}

func (a azureCliTokenAuth) validate() error {
	var err *multierror.Error

	errorMessageFmt := "A %s was not found in your Azure CLI Credentials.\n\nPlease login to the Azure CLI again via `az login`"

	if a.profile == nil {
		return fmt.Errorf("Azure CLI Profile is nil - this is an internal error and should be reported.")
	}

	if a.profile.clientId == "" {
		err = multierror.Append(err, fmt.Errorf(errorMessageFmt, "Client ID"))
	}

	if a.profile.subscriptionId == "" {
		err = multierror.Append(err, fmt.Errorf(errorMessageFmt, "Subscription ID"))
	}

	if a.profile.tenantId == "" {
		err = multierror.Append(err, fmt.Errorf(errorMessageFmt, "Tenant ID"))
	}

	return err.ErrorOrNil()
}

func obtainAuthorizationToken(endpoint string, subscriptionId string) (*cli.Token, error) {
	var stderr bytes.Buffer
	var stdout bytes.Buffer

	cmd := exec.Command("az", "account", "get-access-token", "--resource", endpoint, "--subscription", subscriptionId, "-o=json")

	cmd.Stderr = &stderr
	cmd.Stdout = &stdout

	if err := cmd.Start(); err != nil {
		return nil, fmt.Errorf("Error launching Azure CLI: %+v", err)
	}

	if err := cmd.Wait(); err != nil {
		return nil, fmt.Errorf("Error waiting for the Azure CLI: %+v", err)
	}

	stdOutStr := stdout.String()
	stdErrStr := stderr.String()

	if stdErrStr != "" {
		return nil, fmt.Errorf("Error retrieving access token from Azure CLI: %s", strings.TrimSpace(stdErrStr))
	}

	var token *cli.Token
	err := json.Unmarshal([]byte(stdOutStr), &token)
	if err != nil {
		return nil, fmt.Errorf("Error unmarshaling Access Token from the Azure CLI: %s", err)
	}

	return token, nil
}
<|endoftext|>