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

// +build linux,root,integration

package devicemapper_test

import (
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"path/filepath"
	"sync"
	"syscall"
	"testing"

	. "gopkg.in/check.v1"

	"github.com/Sirupsen/logrus"
	"github.com/control-center/serviced/volume"
	"github.com/control-center/serviced/volume/drivertest"
	"github.com/docker/go-units"
	// Register the devicemapper driver
	. "github.com/control-center/serviced/volume/devicemapper"
	"github.com/zenoss/glog"
)

var (
	_                   = Suite(&DeviceMapperSuite{})
	devmapArgs []string = make([]string, 0)
)

func init() {
	// Reduce the size the the base fs and loopback for the tests
	devmapArgs = append(devmapArgs,
		fmt.Sprintf("dm.loopdatasize=%d", 300*1024*1024),
		fmt.Sprintf("dm.loopmetadatasize=%d", 199*1024*1024),
		fmt.Sprintf("dm.basesize=%d", 300*1024*1024),
		"dm.override_udev_sync_check=true")
	if err := initLoopbacks(); err != nil {
		panic(err)
	}
	// Set Docker's logger to debug level, so we can get interesting
	// information if -v
	logrus.SetLevel(logrus.DebugLevel)

	// Also enable glog verbosity so we get other interesting information if tests run with -v
	glog.SetToStderr(true)
	glog.SetVerbosity(2)

}

// getBaseLoopStats inspects /dev/loop0 to collect uid,gid, and mode for the
// loop0 device on the system.  If it does not exist we assume 0,0,0660 for the
// stat data
func getBaseLoopStats() (*syscall.Stat_t, error) {
	loop0, err := os.Stat("/dev/loop0")
	if err != nil {
		if os.IsNotExist(err) {
			return &syscall.Stat_t{
				Uid:  0,
				Gid:  0,
				Mode: 0660,
			}, nil
		}
		return nil, err
	}
	return loop0.Sys().(*syscall.Stat_t), nil
}

// initLoopbacks ensures that the loopback devices are properly created within
// the system running the device mapper tests.
func initLoopbacks() error {
	statT, err := getBaseLoopStats()
	if err != nil {
		return err
	}
	for i := 0; i < 8; i++ {
		loopPath := fmt.Sprintf("/dev/loop%d", i)
		// only create new loopback files if they don't exist
		if _, err := os.Stat(loopPath); err != nil {
			if mkerr := syscall.Mknod(loopPath,
				uint32(statT.Mode|syscall.S_IFBLK), int((7<<8)|(i&0xff)|((i&0xfff00)<<12))); mkerr != nil {
				return mkerr
			}
			os.Chown(loopPath, int(statT.Uid), int(statT.Gid))
		}
	}
	return nil
}

func Test(t *testing.T) { TestingT(t) }

type DeviceMapperSuite struct{}

func (s *DeviceMapperSuite) TestDeviceMapperCreateEmpty(c *C) {
	drivertest.DriverTestCreateEmpty(c, "devicemapper", "", devmapArgs)
}

func (s *DeviceMapperSuite) TestDeviceMapperCreateBase(c *C) {
	drivertest.DriverTestCreateBase(c, "devicemapper", "", devmapArgs)
}

func (s *DeviceMapperSuite) TestDeviceMapperSnapshots(c *C) {
	drivertest.DriverTestSnapshots(c, "devicemapper", "", devmapArgs)
}

func (s *DeviceMapperSuite) TestDeviceMapperSnapshotTags(c *C) {
	drivertest.DriverTestSnapshotTags(c, "devicemapper", "", devmapArgs)
}

func (s *DeviceMapperSuite) TestDeviceMapperExportImport(c *C) {
	drivertest.DriverTestExportImport(c, "devicemapper", "", "", devmapArgs)
}

func (s *DeviceMapperSuite) TestDeviceMapperExcludeDirs(c *C) {

	// Set up import/export volumes

	exportdrv, err := Init(c.MkDir(), []string{})
	c.Assert(err, IsNil)
	defer exportdrv.Cleanup()

	importdrv, err := Init(c.MkDir(), []string{})
	c.Assert(err, IsNil)
	defer importdrv.Cleanup()

	exportvol, err := exportdrv.Create("basetest")
	c.Assert(err, IsNil)
	defer exportdrv.Remove("basetest")

	importvol, err := importdrv.Create("basetest")
	c.Assert(err, IsNil)
	defer importdrv.Remove("basetest")

	// Make two directories, one to be excluded, one to be included, with a file in each
	for _, d := range []string{"include", "exclude"} {
		dir := filepath.Join(exportvol.Path(), d)
		os.MkdirAll(dir, os.ModeDir)
		f1, err := ioutil.TempFile(dir, "dump-")
		c.Assert(err, IsNil)
		defer f1.Close()
		_, err = f1.Write(make([]byte, 1024))
		c.Assert(err, IsNil)
		err = f1.Close()
		c.Assert(err, IsNil)
	}

	err = exportvol.Snapshot("snap", "testing exclusion of dirs in export", []string{})
	c.Assert(err, IsNil)

	// Do export/import, excluding the exclude dir
	wg := &sync.WaitGroup{}
	wg.Add(2)
	r, w := io.Pipe()
	go func() {
		defer wg.Done()
		err := exportvol.Export("snap", "", w, []string{"exclude"})
		if err != nil {
			w.Close()
		}
		c.Check(err, IsNil)
	}()
	go func() {
		defer wg.Done()
		err := importvol.Import("snap", r)
		if err != nil {
			r.Close()
		}
		c.Check(err, IsNil)
	}()
	wg.Wait()

	importvol.Rollback("snap")

	included := filepath.Join(exportvol.Path(), "include")
	_, err = os.Stat(included)
	c.Assert(err, IsNil)

	excluded := filepath.Join(exportvol.Path(), "exclude")

	_, err = os.Stat(excluded)
	c.Assert(err, Not(IsNil))
	c.Assert(os.IsNotExist(err), Equals, true)
}

func (s *DeviceMapperSuite) TestDeviceMapperImportBasesize(c *C) {
	// Set up export volume with larger volume base size
	root1 := c.MkDir()
	basesize1, err := units.RAMInBytes("15M")
	c.Assert(err, IsNil)
	drv1, err := Init(root1, []string{fmt.Sprintf("dm.basesize=%d", basesize1)})
	c.Assert(err, IsNil)
	defer drv1.Cleanup()
	vol1, err := drv1.Create("basetest")
	c.Assert(err, IsNil)
	defer drv1.Remove("basetest")

	// dump 12MB of data into this device
	f1, err := ioutil.TempFile(vol1.Path(), "dump-")
	c.Assert(err, IsNil)
	defer f1.Close()
	garbage := make([]byte, 1024*1024)
	for i := 0; i < 12; i++ {
		_, err = f1.Write(garbage)
		c.Assert(err, IsNil)
	}
	err = f1.Close()
	c.Assert(err, IsNil)

	err = vol1.Snapshot("snap", "testing import/export", []string{})
	c.Assert(err, IsNil)

	// Set up import volume with smaller volume base size
	root2 := c.MkDir()
	basesize2, err := units.RAMInBytes("10M")
	c.Assert(err, IsNil)
	drv2, err := Init(root2, []string{fmt.Sprintf("dm.basesize=%d", basesize2)})
	c.Assert(err, IsNil)
	defer drv2.Cleanup()
	vol2, err := drv2.Create("basetest")
	c.Assert(err, IsNil)
	defer drv2.Remove("basetest")

	// Do export/import
	wg := &sync.WaitGroup{}
	wg.Add(2)
	r, w := io.Pipe()
	go func() {
		defer wg.Done()
		err := vol1.Export("snap", "", w)
		if err != nil {
			w.Close()
		}
		c.Check(err, IsNil)
	}()
	go func() {
		defer wg.Done()
		err := vol2.Import("snap", r)
		if err != nil {
			r.Close()
		}
		c.Check(err, IsNil)
	}()
	wg.Wait()
}

func (s *DeviceMapperSuite) TestSnapShotContainerMounts(c *C) {
	drivertest.DriverTestSnapshotContainerMounts(c, "devicemapper", "", devmapArgs)
}

func (s *DeviceMapperSuite) TestResize(c *C) {
	drivertest.DriverTestResize(c, "devicemapper", "", devmapArgs)
}

func (s *DeviceMapperSuite) TestDeviceMapperBadSnapshots(c *C) {
	badsnapshot := func(label string, vol volume.Volume) error {
		//create an invalid snapshot by snapshotting and then removing .SnapshotInfo
		if err := vol.Snapshot(label, "", []string{}); err != nil {
			return err
		}
		filePath := filepath.Join(vol.Driver().Root(), ".devicemapper", "volumes", fmt.Sprintf("%s_%s", vol.Name(), label), ".SNAPSHOTINFO")
		err := os.Remove(filePath)
		return err
	}

	drivertest.DriverTestBadSnapshot(c, "devicemapper", "", badsnapshot, devmapArgs)
}
<commit_msg>Fix tests<commit_after>// Copyright 2015 The Serviced Authors.
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// +build linux,root,integration

package devicemapper_test

import (
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"path/filepath"
	"sync"
	"syscall"
	"testing"

	. "gopkg.in/check.v1"

	"github.com/Sirupsen/logrus"
	"github.com/control-center/serviced/volume"
	"github.com/control-center/serviced/volume/drivertest"
	"github.com/docker/go-units"
	// Register the devicemapper driver
	. "github.com/control-center/serviced/volume/devicemapper"
	"github.com/zenoss/glog"
)

var (
	_                   = Suite(&DeviceMapperSuite{})
	devmapArgs []string = make([]string, 0)
)

func init() {
	// Reduce the size the the base fs and loopback for the tests
	devmapArgs = append(devmapArgs,
		fmt.Sprintf("dm.loopdatasize=%d", 300*1024*1024),
		fmt.Sprintf("dm.loopmetadatasize=%d", 199*1024*1024),
		fmt.Sprintf("dm.basesize=%d", 300*1024*1024),
		"dm.override_udev_sync_check=true")
	if err := initLoopbacks(); err != nil {
		panic(err)
	}
	// Set Docker's logger to debug level, so we can get interesting
	// information if -v
	logrus.SetLevel(logrus.DebugLevel)

	// Also enable glog verbosity so we get other interesting information if tests run with -v
	glog.SetToStderr(true)
	glog.SetVerbosity(2)

}

// getBaseLoopStats inspects /dev/loop0 to collect uid,gid, and mode for the
// loop0 device on the system.  If it does not exist we assume 0,0,0660 for the
// stat data
func getBaseLoopStats() (*syscall.Stat_t, error) {
	loop0, err := os.Stat("/dev/loop0")
	if err != nil {
		if os.IsNotExist(err) {
			return &syscall.Stat_t{
				Uid:  0,
				Gid:  0,
				Mode: 0660,
			}, nil
		}
		return nil, err
	}
	return loop0.Sys().(*syscall.Stat_t), nil
}

// initLoopbacks ensures that the loopback devices are properly created within
// the system running the device mapper tests.
func initLoopbacks() error {
	statT, err := getBaseLoopStats()
	if err != nil {
		return err
	}
	for i := 0; i < 8; i++ {
		loopPath := fmt.Sprintf("/dev/loop%d", i)
		// only create new loopback files if they don't exist
		if _, err := os.Stat(loopPath); err != nil {
			if mkerr := syscall.Mknod(loopPath,
				uint32(statT.Mode|syscall.S_IFBLK), int((7<<8)|(i&0xff)|((i&0xfff00)<<12))); mkerr != nil {
				return mkerr
			}
			os.Chown(loopPath, int(statT.Uid), int(statT.Gid))
		}
	}
	return nil
}

func Test(t *testing.T) { TestingT(t) }

type DeviceMapperSuite struct{}

func (s *DeviceMapperSuite) TestDeviceMapperCreateEmpty(c *C) {
	drivertest.DriverTestCreateEmpty(c, "devicemapper", "", devmapArgs)
}

func (s *DeviceMapperSuite) TestDeviceMapperCreateBase(c *C) {
	drivertest.DriverTestCreateBase(c, "devicemapper", "", devmapArgs)
}

func (s *DeviceMapperSuite) TestDeviceMapperSnapshots(c *C) {
	drivertest.DriverTestSnapshots(c, "devicemapper", "", devmapArgs)
}

func (s *DeviceMapperSuite) TestDeviceMapperSnapshotTags(c *C) {
	drivertest.DriverTestSnapshotTags(c, "devicemapper", "", devmapArgs)
}

func (s *DeviceMapperSuite) TestDeviceMapperExportImport(c *C) {
	drivertest.DriverTestExportImport(c, "devicemapper", "", "", devmapArgs)
}

func (s *DeviceMapperSuite) TestDeviceMapperExcludeDirs(c *C) {

	// Set up import/export volumes

	exportdrv, err := Init(c.MkDir(), []string{})
	c.Assert(err, IsNil)
	defer exportdrv.Cleanup()

	importdrv, err := Init(c.MkDir(), []string{})
	c.Assert(err, IsNil)
	defer importdrv.Cleanup()

	exportvol, err := exportdrv.Create("basetest")
	c.Assert(err, IsNil)
	defer exportdrv.Remove("basetest")

	importvol, err := importdrv.Create("basetest")
	c.Assert(err, IsNil)
	defer importdrv.Remove("basetest")

	// Make two directories, one to be excluded, one to be included, with a file in each
	for _, d := range []string{"include", "exclude"} {
		dir := filepath.Join(exportvol.Path(), d)
		os.MkdirAll(dir, os.ModeDir)
		f1, err := ioutil.TempFile(dir, "dump-")
		c.Assert(err, IsNil)
		defer f1.Close()
		_, err = f1.Write(make([]byte, 1024))
		c.Assert(err, IsNil)
		err = f1.Close()
		c.Assert(err, IsNil)
	}

	err = exportvol.Snapshot("snap", "testing exclusion of dirs in export", []string{})
	c.Assert(err, IsNil)

	// Do export/import, excluding the exclude dir
	wg := &sync.WaitGroup{}
	wg.Add(2)
	r, w := io.Pipe()
	go func() {
		defer wg.Done()
		err := exportvol.Export("snap", "", w, []string{"exclude"})
		if err != nil {
			w.Close()
		}
		c.Check(err, IsNil)
	}()
	go func() {
		defer wg.Done()
		err := importvol.Import("snap", r)
		if err != nil {
			r.Close()
		}
		c.Check(err, IsNil)
	}()
	wg.Wait()

	importvol.Rollback("snap")

	included := filepath.Join(exportvol.Path(), "include")
	_, err = os.Stat(included)
	c.Assert(err, IsNil)

	excluded := filepath.Join(exportvol.Path(), "exclude")

	_, err = os.Stat(excluded)
	c.Assert(err, Not(IsNil))
	c.Assert(os.IsNotExist(err), Equals, true)
}

func (s *DeviceMapperSuite) TestDeviceMapperImportBasesize(c *C) {
	// Set up export volume with larger volume base size
	root1 := c.MkDir()
	basesize1, err := units.RAMInBytes("15M")
	c.Assert(err, IsNil)
	drv1, err := Init(root1, []string{fmt.Sprintf("dm.basesize=%d", basesize1)})
	c.Assert(err, IsNil)
	defer drv1.Cleanup()
	vol1, err := drv1.Create("basetest")
	c.Assert(err, IsNil)
	defer drv1.Remove("basetest")

	// dump 12MB of data into this device
	f1, err := ioutil.TempFile(vol1.Path(), "dump-")
	c.Assert(err, IsNil)
	defer f1.Close()
	garbage := make([]byte, 1024*1024)
	for i := 0; i < 12; i++ {
		_, err = f1.Write(garbage)
		c.Assert(err, IsNil)
	}
	err = f1.Close()
	c.Assert(err, IsNil)

	err = vol1.Snapshot("snap", "testing import/export", []string{})
	c.Assert(err, IsNil)

	// Set up import volume with smaller volume base size
	root2 := c.MkDir()
	basesize2, err := units.RAMInBytes("10M")
	c.Assert(err, IsNil)
	drv2, err := Init(root2, []string{fmt.Sprintf("dm.basesize=%d", basesize2)})
	c.Assert(err, IsNil)
	defer drv2.Cleanup()
	vol2, err := drv2.Create("basetest")
	c.Assert(err, IsNil)
	defer drv2.Remove("basetest")

	// Do export/import
	wg := &sync.WaitGroup{}
	wg.Add(2)
	r, w := io.Pipe()
	go func() {
		defer wg.Done()
		err := vol1.Export("snap", "", w, []string{})
		if err != nil {
			w.Close()
		}
		c.Check(err, IsNil)
	}()
	go func() {
		defer wg.Done()
		err := vol2.Import("snap", r)
		if err != nil {
			r.Close()
		}
		c.Check(err, IsNil)
	}()
	wg.Wait()
}

func (s *DeviceMapperSuite) TestSnapShotContainerMounts(c *C) {
	drivertest.DriverTestSnapshotContainerMounts(c, "devicemapper", "", devmapArgs)
}

func (s *DeviceMapperSuite) TestResize(c *C) {
	drivertest.DriverTestResize(c, "devicemapper", "", devmapArgs)
}

func (s *DeviceMapperSuite) TestDeviceMapperBadSnapshots(c *C) {
	badsnapshot := func(label string, vol volume.Volume) error {
		//create an invalid snapshot by snapshotting and then removing .SnapshotInfo
		if err := vol.Snapshot(label, "", []string{}); err != nil {
			return err
		}
		filePath := filepath.Join(vol.Driver().Root(), ".devicemapper", "volumes", fmt.Sprintf("%s_%s", vol.Name(), label), ".SNAPSHOTINFO")
		err := os.Remove(filePath)
		return err
	}

	drivertest.DriverTestBadSnapshot(c, "devicemapper", "", badsnapshot, devmapArgs)
}
<|endoftext|>


<commit_before>// Package local provides the default implementation for volumes. It
// is used to mount data volume containers and directories local to
// the host server.
package local

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"reflect"
	"sync"

	"github.com/pkg/errors"

	"github.com/Sirupsen/logrus"
	"github.com/docker/docker/pkg/idtools"
	"github.com/docker/docker/pkg/mount"
	"github.com/docker/docker/utils"
	"github.com/docker/docker/volume"
)

// VolumeDataPathName is the name of the directory where the volume data is stored.
// It uses a very distinctive name to avoid collisions migrating data between
// Docker versions.
const (
	VolumeDataPathName = "_data"
	volumesPathName    = "volumes"
)

var (
	// ErrNotFound is the typed error returned when the requested volume name can't be found
	ErrNotFound = fmt.Errorf("volume not found")
	// volumeNameRegex ensures the name assigned for the volume is valid.
	// This name is used to create the bind directory, so we need to avoid characters that
	// would make the path to escape the root directory.
	volumeNameRegex = utils.RestrictedVolumeNamePattern
)

type validationError struct {
	error
}

func (validationError) IsValidationError() bool {
	return true
}

type activeMount struct {
	count   uint64
	mounted bool
}

// New instantiates a new Root instance with the provided scope. Scope
// is the base path that the Root instance uses to store its
// volumes. The base path is created here if it does not exist.
func New(scope string, rootUID, rootGID int) (*Root, error) {
	rootDirectory := filepath.Join(scope, volumesPathName)

	if err := idtools.MkdirAllAs(rootDirectory, 0700, rootUID, rootGID); err != nil {
		return nil, err
	}

	r := &Root{
		scope:   scope,
		path:    rootDirectory,
		volumes: make(map[string]*localVolume),
		rootUID: rootUID,
		rootGID: rootGID,
	}

	dirs, err := ioutil.ReadDir(rootDirectory)
	if err != nil {
		return nil, err
	}

	mountInfos, err := mount.GetMounts()
	if err != nil {
		logrus.Debugf("error looking up mounts for local volume cleanup: %v", err)
	}

	for _, d := range dirs {
		if !d.IsDir() {
			continue
		}

		name := filepath.Base(d.Name())
		v := &localVolume{
			driverName: r.Name(),
			name:       name,
			path:       r.DataPath(name),
		}
		r.volumes[name] = v
		optsFilePath := filepath.Join(rootDirectory, name, "opts.json")
		if b, err := ioutil.ReadFile(optsFilePath); err == nil {
			opts := optsConfig{}
			if err := json.Unmarshal(b, &opts); err != nil {
				return nil, errors.Wrapf(err, "error while unmarshaling volume options for volume: %s", name)
			}
			// Make sure this isn't an empty optsConfig.
			// This could be empty due to buggy behavior in older versions of Docker.
			if !reflect.DeepEqual(opts, optsConfig{}) {
				v.opts = &opts
			}

			// unmount anything that may still be mounted (for example, from an unclean shutdown)
			for _, info := range mountInfos {
				if info.Mountpoint == v.path {
					mount.Unmount(v.path)
					break
				}
			}
		}
	}

	return r, nil
}

// Root implements the Driver interface for the volume package and
// manages the creation/removal of volumes. It uses only standard vfs
// commands to create/remove dirs within its provided scope.
type Root struct {
	m       sync.Mutex
	scope   string
	path    string
	volumes map[string]*localVolume
	rootUID int
	rootGID int
}

// List lists all the volumes
func (r *Root) List() ([]volume.Volume, error) {
	var ls []volume.Volume
	r.m.Lock()
	for _, v := range r.volumes {
		ls = append(ls, v)
	}
	r.m.Unlock()
	return ls, nil
}

// DataPath returns the constructed path of this volume.
func (r *Root) DataPath(volumeName string) string {
	return filepath.Join(r.path, volumeName, VolumeDataPathName)
}

// Name returns the name of Root, defined in the volume package in the DefaultDriverName constant.
func (r *Root) Name() string {
	return volume.DefaultDriverName
}

// Create creates a new volume.Volume with the provided name, creating
// the underlying directory tree required for this volume in the
// process.
func (r *Root) Create(name string, opts map[string]string) (volume.Volume, error) {
	if err := r.validateName(name); err != nil {
		return nil, err
	}

	r.m.Lock()
	defer r.m.Unlock()

	v, exists := r.volumes[name]
	if exists {
		return v, nil
	}

	path := r.DataPath(name)
	if err := idtools.MkdirAllAs(path, 0755, r.rootUID, r.rootGID); err != nil {
		if os.IsExist(err) {
			return nil, fmt.Errorf("volume already exists under %s", filepath.Dir(path))
		}
		return nil, errors.Wrapf(err, "error while creating volume path '%s'", path)
	}

	var err error
	defer func() {
		if err != nil {
			os.RemoveAll(filepath.Dir(path))
		}
	}()

	v = &localVolume{
		driverName: r.Name(),
		name:       name,
		path:       path,
	}

	if len(opts) != 0 {
		if err = setOpts(v, opts); err != nil {
			return nil, err
		}
		var b []byte
		b, err = json.Marshal(v.opts)
		if err != nil {
			return nil, err
		}
		if err = ioutil.WriteFile(filepath.Join(filepath.Dir(path), "opts.json"), b, 600); err != nil {
			return nil, errors.Wrap(err, "error while persisting volume options")
		}
	}

	r.volumes[name] = v
	return v, nil
}

// Remove removes the specified volume and all underlying data. If the
// given volume does not belong to this driver and an error is
// returned. The volume is reference counted, if all references are
// not released then the volume is not removed.
func (r *Root) Remove(v volume.Volume) error {
	r.m.Lock()
	defer r.m.Unlock()

	lv, ok := v.(*localVolume)
	if !ok {
		return fmt.Errorf("unknown volume type %T", v)
	}

	realPath, err := filepath.EvalSymlinks(lv.path)
	if err != nil {
		if !os.IsNotExist(err) {
			return err
		}
		realPath = filepath.Dir(lv.path)
	}

	if !r.scopedPath(realPath) {
		return fmt.Errorf("Unable to remove a directory of out the Docker root %s: %s", r.scope, realPath)
	}

	if err := removePath(realPath); err != nil {
		return err
	}

	delete(r.volumes, lv.name)
	return removePath(filepath.Dir(lv.path))
}

func removePath(path string) error {
	if err := os.RemoveAll(path); err != nil {
		if os.IsNotExist(err) {
			return nil
		}
		return errors.Wrapf(err, "error removing volume path '%s'", path)
	}
	return nil
}

// Get looks up the volume for the given name and returns it if found
func (r *Root) Get(name string) (volume.Volume, error) {
	r.m.Lock()
	v, exists := r.volumes[name]
	r.m.Unlock()
	if !exists {
		return nil, ErrNotFound
	}
	return v, nil
}

// Scope returns the local volume scope
func (r *Root) Scope() string {
	return volume.LocalScope
}

func (r *Root) validateName(name string) error {
	if len(name) == 1 {
		return validationError{fmt.Errorf("volume name is too short, names should be at least two alphanumeric characters")}
	}
	if !volumeNameRegex.MatchString(name) {
		return validationError{fmt.Errorf("%q includes invalid characters for a local volume name, only %q are allowed", name, utils.RestrictedNameChars)}
	}
	return nil
}

// localVolume implements the Volume interface from the volume package and
// represents the volumes created by Root.
type localVolume struct {
	m sync.Mutex
	// unique name of the volume
	name string
	// path is the path on the host where the data lives
	path string
	// driverName is the name of the driver that created the volume.
	driverName string
	// opts is the parsed list of options used to create the volume
	opts *optsConfig
	// active refcounts the active mounts
	active activeMount
}

// Name returns the name of the given Volume.
func (v *localVolume) Name() string {
	return v.name
}

// DriverName returns the driver that created the given Volume.
func (v *localVolume) DriverName() string {
	return v.driverName
}

// Path returns the data location.
func (v *localVolume) Path() string {
	return v.path
}

// Mount implements the localVolume interface, returning the data location.
func (v *localVolume) Mount(id string) (string, error) {
	v.m.Lock()
	defer v.m.Unlock()
	if v.opts != nil {
		if !v.active.mounted {
			if err := v.mount(); err != nil {
				return "", err
			}
			v.active.mounted = true
		}
		v.active.count++
	}
	return v.path, nil
}

// Umount is for satisfying the localVolume interface and does not do anything in this driver.
func (v *localVolume) Unmount(id string) error {
	v.m.Lock()
	defer v.m.Unlock()
	if v.opts != nil {
		v.active.count--
		if v.active.count == 0 {
			if err := mount.Unmount(v.path); err != nil {
				v.active.count++
				return errors.Wrapf(err, "error while unmounting volume path '%s'", v.path)
			}
			v.active.mounted = false
		}
	}
	return nil
}

func validateOpts(opts map[string]string) error {
	for opt := range opts {
		if !validOpts[opt] {
			return validationError{fmt.Errorf("invalid option key: %q", opt)}
		}
	}
	return nil
}

func (v *localVolume) Status() map[string]interface{} {
	return nil
}
<commit_msg>fixes #17734, clear message for host volumes not starting with /<commit_after>// Package local provides the default implementation for volumes. It
// is used to mount data volume containers and directories local to
// the host server.
package local

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"reflect"
	"sync"

	"github.com/pkg/errors"

	"github.com/Sirupsen/logrus"
	"github.com/docker/docker/pkg/idtools"
	"github.com/docker/docker/pkg/mount"
	"github.com/docker/docker/utils"
	"github.com/docker/docker/volume"
)

// VolumeDataPathName is the name of the directory where the volume data is stored.
// It uses a very distinctive name to avoid collisions migrating data between
// Docker versions.
const (
	VolumeDataPathName = "_data"
	volumesPathName    = "volumes"
)

var (
	// ErrNotFound is the typed error returned when the requested volume name can't be found
	ErrNotFound = fmt.Errorf("volume not found")
	// volumeNameRegex ensures the name assigned for the volume is valid.
	// This name is used to create the bind directory, so we need to avoid characters that
	// would make the path to escape the root directory.
	volumeNameRegex = utils.RestrictedVolumeNamePattern
)

type validationError struct {
	error
}

func (validationError) IsValidationError() bool {
	return true
}

type activeMount struct {
	count   uint64
	mounted bool
}

// New instantiates a new Root instance with the provided scope. Scope
// is the base path that the Root instance uses to store its
// volumes. The base path is created here if it does not exist.
func New(scope string, rootUID, rootGID int) (*Root, error) {
	rootDirectory := filepath.Join(scope, volumesPathName)

	if err := idtools.MkdirAllAs(rootDirectory, 0700, rootUID, rootGID); err != nil {
		return nil, err
	}

	r := &Root{
		scope:   scope,
		path:    rootDirectory,
		volumes: make(map[string]*localVolume),
		rootUID: rootUID,
		rootGID: rootGID,
	}

	dirs, err := ioutil.ReadDir(rootDirectory)
	if err != nil {
		return nil, err
	}

	mountInfos, err := mount.GetMounts()
	if err != nil {
		logrus.Debugf("error looking up mounts for local volume cleanup: %v", err)
	}

	for _, d := range dirs {
		if !d.IsDir() {
			continue
		}

		name := filepath.Base(d.Name())
		v := &localVolume{
			driverName: r.Name(),
			name:       name,
			path:       r.DataPath(name),
		}
		r.volumes[name] = v
		optsFilePath := filepath.Join(rootDirectory, name, "opts.json")
		if b, err := ioutil.ReadFile(optsFilePath); err == nil {
			opts := optsConfig{}
			if err := json.Unmarshal(b, &opts); err != nil {
				return nil, errors.Wrapf(err, "error while unmarshaling volume options for volume: %s", name)
			}
			// Make sure this isn't an empty optsConfig.
			// This could be empty due to buggy behavior in older versions of Docker.
			if !reflect.DeepEqual(opts, optsConfig{}) {
				v.opts = &opts
			}

			// unmount anything that may still be mounted (for example, from an unclean shutdown)
			for _, info := range mountInfos {
				if info.Mountpoint == v.path {
					mount.Unmount(v.path)
					break
				}
			}
		}
	}

	return r, nil
}

// Root implements the Driver interface for the volume package and
// manages the creation/removal of volumes. It uses only standard vfs
// commands to create/remove dirs within its provided scope.
type Root struct {
	m       sync.Mutex
	scope   string
	path    string
	volumes map[string]*localVolume
	rootUID int
	rootGID int
}

// List lists all the volumes
func (r *Root) List() ([]volume.Volume, error) {
	var ls []volume.Volume
	r.m.Lock()
	for _, v := range r.volumes {
		ls = append(ls, v)
	}
	r.m.Unlock()
	return ls, nil
}

// DataPath returns the constructed path of this volume.
func (r *Root) DataPath(volumeName string) string {
	return filepath.Join(r.path, volumeName, VolumeDataPathName)
}

// Name returns the name of Root, defined in the volume package in the DefaultDriverName constant.
func (r *Root) Name() string {
	return volume.DefaultDriverName
}

// Create creates a new volume.Volume with the provided name, creating
// the underlying directory tree required for this volume in the
// process.
func (r *Root) Create(name string, opts map[string]string) (volume.Volume, error) {
	if err := r.validateName(name); err != nil {
		return nil, err
	}

	r.m.Lock()
	defer r.m.Unlock()

	v, exists := r.volumes[name]
	if exists {
		return v, nil
	}

	path := r.DataPath(name)
	if err := idtools.MkdirAllAs(path, 0755, r.rootUID, r.rootGID); err != nil {
		if os.IsExist(err) {
			return nil, fmt.Errorf("volume already exists under %s", filepath.Dir(path))
		}
		return nil, errors.Wrapf(err, "error while creating volume path '%s'", path)
	}

	var err error
	defer func() {
		if err != nil {
			os.RemoveAll(filepath.Dir(path))
		}
	}()

	v = &localVolume{
		driverName: r.Name(),
		name:       name,
		path:       path,
	}

	if len(opts) != 0 {
		if err = setOpts(v, opts); err != nil {
			return nil, err
		}
		var b []byte
		b, err = json.Marshal(v.opts)
		if err != nil {
			return nil, err
		}
		if err = ioutil.WriteFile(filepath.Join(filepath.Dir(path), "opts.json"), b, 600); err != nil {
			return nil, errors.Wrap(err, "error while persisting volume options")
		}
	}

	r.volumes[name] = v
	return v, nil
}

// Remove removes the specified volume and all underlying data. If the
// given volume does not belong to this driver and an error is
// returned. The volume is reference counted, if all references are
// not released then the volume is not removed.
func (r *Root) Remove(v volume.Volume) error {
	r.m.Lock()
	defer r.m.Unlock()

	lv, ok := v.(*localVolume)
	if !ok {
		return fmt.Errorf("unknown volume type %T", v)
	}

	realPath, err := filepath.EvalSymlinks(lv.path)
	if err != nil {
		if !os.IsNotExist(err) {
			return err
		}
		realPath = filepath.Dir(lv.path)
	}

	if !r.scopedPath(realPath) {
		return fmt.Errorf("Unable to remove a directory of out the Docker root %s: %s", r.scope, realPath)
	}

	if err := removePath(realPath); err != nil {
		return err
	}

	delete(r.volumes, lv.name)
	return removePath(filepath.Dir(lv.path))
}

func removePath(path string) error {
	if err := os.RemoveAll(path); err != nil {
		if os.IsNotExist(err) {
			return nil
		}
		return errors.Wrapf(err, "error removing volume path '%s'", path)
	}
	return nil
}

// Get looks up the volume for the given name and returns it if found
func (r *Root) Get(name string) (volume.Volume, error) {
	r.m.Lock()
	v, exists := r.volumes[name]
	r.m.Unlock()
	if !exists {
		return nil, ErrNotFound
	}
	return v, nil
}

// Scope returns the local volume scope
func (r *Root) Scope() string {
	return volume.LocalScope
}

func (r *Root) validateName(name string) error {
	if len(name) == 1 {
		return validationError{fmt.Errorf("volume name is too short, names should be at least two alphanumeric characters")}
	}
	if !volumeNameRegex.MatchString(name) {
		return validationError{fmt.Errorf("%q includes invalid characters for a local volume name, only %q are allowed. If you intented to pass host directory use absolute path starting with /", name, utils.RestrictedNameChars)}
	}
	return nil
}

// localVolume implements the Volume interface from the volume package and
// represents the volumes created by Root.
type localVolume struct {
	m sync.Mutex
	// unique name of the volume
	name string
	// path is the path on the host where the data lives
	path string
	// driverName is the name of the driver that created the volume.
	driverName string
	// opts is the parsed list of options used to create the volume
	opts *optsConfig
	// active refcounts the active mounts
	active activeMount
}

// Name returns the name of the given Volume.
func (v *localVolume) Name() string {
	return v.name
}

// DriverName returns the driver that created the given Volume.
func (v *localVolume) DriverName() string {
	return v.driverName
}

// Path returns the data location.
func (v *localVolume) Path() string {
	return v.path
}

// Mount implements the localVolume interface, returning the data location.
func (v *localVolume) Mount(id string) (string, error) {
	v.m.Lock()
	defer v.m.Unlock()
	if v.opts != nil {
		if !v.active.mounted {
			if err := v.mount(); err != nil {
				return "", err
			}
			v.active.mounted = true
		}
		v.active.count++
	}
	return v.path, nil
}

// Umount is for satisfying the localVolume interface and does not do anything in this driver.
func (v *localVolume) Unmount(id string) error {
	v.m.Lock()
	defer v.m.Unlock()
	if v.opts != nil {
		v.active.count--
		if v.active.count == 0 {
			if err := mount.Unmount(v.path); err != nil {
				v.active.count++
				return errors.Wrapf(err, "error while unmounting volume path '%s'", v.path)
			}
			v.active.mounted = false
		}
	}
	return nil
}

func validateOpts(opts map[string]string) error {
	for opt := range opts {
		if !validOpts[opt] {
			return validationError{fmt.Errorf("invalid option key: %q", opt)}
		}
	}
	return nil
}

func (v *localVolume) Status() map[string]interface{} {
	return nil
}
<|endoftext|>


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

import (
	"image"
	"image/color"
)

type Config struct {
	SeparationMin, SeparationMax int
	CrossEyed                    bool
}

/**
 * Given a depth map and background image, create an autostereogram.
 */
func Imagic(dm, bg image.Image, config Config) image.Image {
	bounds := dm.Bounds()
	min := bounds.Min
	max := bounds.Max
	result := newMutableImage(dm, bg)

	for y := min.Y; y < max.Y; y++ {
		r := magicInflateRow(dm, bg, config, y)
		result.imageRows[y] = r
	}
	return result
}

func magicInflateRow(dm, bg image.Image, config Config, y int) imageRow {
	dmWidth := boundsWidth(dm.Bounds())
	bgWidth := boundsWidth(bg.Bounds())
	dmHeight := boundsHeight(dm.Bounds())
	bgHeight := boundsHeight(bg.Bounds())
	bgY := y * bgHeight / dmHeight
	var sourceIndexes = make([]int, dmWidth)

	// Find desired index of pixel to the left.
	for x := 0; x < len(sourceIndexes); x++ {
		depth := depthAt(dm, x, y)
		offset := sourceOffset(depth, config)
		sourceIndexes[x] = x - int(offset)
	}

	// Skip initial consecutive places that reference negative-indexed pixels.
	initialWidth := 0
	for ; sourceIndexes[initialWidth] < 0; initialWidth++ {
	}

	// Map background onto the first section on left.
	var bgIndexes = make([]int, dmWidth)
	for x := 0; x < initialWidth; x++ {
		bgIndexes[x] = x * bgWidth / initialWidth
	}

	// For the rest, copy pixel index from left to right.
	var usedBgIndexes = make([]bool, dmWidth)
	for x := initialWidth; x < len(bgIndexes); x++ {
		if si := sourceIndexes[x]; si < 0 {
			// If the source index is negative, just use the next bg pixel.
			bgIndexes[x] = bgIndexes[x-1] + 1
		} else if usedBgIndexes[si] {
			// This removes some phantom artifacts for wall-eyed viewing.
			bgIndexes[x] = bgIndexes[x-1] + 1
		} else {
			bgIndexes[x] = bgIndexes[si]
			usedBgIndexes[si] = true
		}
	}

	row := imageRow{}
	row.colors = make([]color.Color, dmWidth)
	for x := 0; x < dmWidth; x++ {
		bgX := bgIndexes[x] // TODO(cartland): Check index bounds.
		row.colors[x] = bg.At(bgX, bgY)
	}
	return row
}

func boundsWidth(bounds image.Rectangle) int {
	return bounds.Max.X - bounds.Min.X
}

func boundsHeight(bounds image.Rectangle) int {
	return bounds.Max.Y - bounds.Min.Y
}

var depthMax = uint32(3000)

func depthAt(dm image.Image, x, y int) uint32 {
	color := dm.At(x, y)
	r, g, b, a := color.RGBA()
	rgb := (r + g + b)       // 3 * 0xFFFF
	rgb = rgb / 3            // 0xFFFF
	rgba := rgb * a / 0xFFFF // 0xFFFF
	depth := rgba * depthMax / 0xFFFF
	return depth
}

func sourceOffset(depth uint32, config Config) uint32 {
	maxWidth := config.SeparationMax - config.SeparationMin
	offsetWidth := depth * uint32(maxWidth) / depthMax
	var offset uint32
	if config.CrossEyed {
		offset = uint32(config.SeparationMin) + offsetWidth
	} else {
		offset = uint32(config.SeparationMax) - offsetWidth
	}
	return offset
}

func newMutableImage(dm, bg image.Image) *mutableImage {
	cm := bg.ColorModel()
	bounds := dm.Bounds()
	var imageRows = make([]imageRow, bounds.Max.Y)
	image := new(mutableImage)
	image.cm = cm
	image.bounds = bounds
	image.imageRows = imageRows
	return image
}

type mutableImage struct {
	cm        color.Model
	bounds    image.Rectangle
	imageRows []imageRow
}

func (i *mutableImage) ColorModel() color.Model {
	return i.cm
}

func (i *mutableImage) Bounds() image.Rectangle {
	return i.bounds
}

func (i *mutableImage) At(x, y int) color.Color {
	return i.imageRows[y].colors[x]
}

type imageRow struct {
	colors []color.Color
}
<commit_msg>Clarify range comments for depthAt<commit_after>/*
 * Copyright 2014 Chris Cartland
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package imagic

import (
	"image"
	"image/color"
)

type Config struct {
	SeparationMin, SeparationMax int
	CrossEyed                    bool
}

/**
 * Given a depth map and background image, create an autostereogram.
 */
func Imagic(dm, bg image.Image, config Config) image.Image {
	bounds := dm.Bounds()
	min := bounds.Min
	max := bounds.Max
	result := newMutableImage(dm, bg)

	for y := min.Y; y < max.Y; y++ {
		r := magicInflateRow(dm, bg, config, y)
		result.imageRows[y] = r
	}
	return result
}

func magicInflateRow(dm, bg image.Image, config Config, y int) imageRow {
	dmWidth := boundsWidth(dm.Bounds())
	bgWidth := boundsWidth(bg.Bounds())
	dmHeight := boundsHeight(dm.Bounds())
	bgHeight := boundsHeight(bg.Bounds())
	bgY := y * bgHeight / dmHeight
	var sourceIndexes = make([]int, dmWidth)

	// Find desired index of pixel to the left.
	for x := 0; x < len(sourceIndexes); x++ {
		depth := depthAt(dm, x, y)
		offset := sourceOffset(depth, config)
		sourceIndexes[x] = x - int(offset)
	}

	// Skip initial consecutive places that reference negative-indexed pixels.
	initialWidth := 0
	for ; sourceIndexes[initialWidth] < 0; initialWidth++ {
	}

	// Map background onto the first section on left.
	var bgIndexes = make([]int, dmWidth)
	for x := 0; x < initialWidth; x++ {
		bgIndexes[x] = x * bgWidth / initialWidth
	}

	// For the rest, copy pixel index from left to right.
	var usedBgIndexes = make([]bool, dmWidth)
	for x := initialWidth; x < len(bgIndexes); x++ {
		if si := sourceIndexes[x]; si < 0 {
			// If the source index is negative, just use the next bg pixel.
			bgIndexes[x] = bgIndexes[x-1] + 1
		} else if usedBgIndexes[si] {
			// This removes some phantom artifacts for wall-eyed viewing.
			bgIndexes[x] = bgIndexes[x-1] + 1
		} else {
			bgIndexes[x] = bgIndexes[si]
			usedBgIndexes[si] = true
		}
	}

	row := imageRow{}
	row.colors = make([]color.Color, dmWidth)
	for x := 0; x < dmWidth; x++ {
		bgX := bgIndexes[x] // TODO(cartland): Check index bounds.
		row.colors[x] = bg.At(bgX, bgY)
	}
	return row
}

func boundsWidth(bounds image.Rectangle) int {
	return bounds.Max.X - bounds.Min.X
}

func boundsHeight(bounds image.Rectangle) int {
	return bounds.Max.Y - bounds.Min.Y
}

var depthMax = uint32(3000)

func depthAt(dm image.Image, x, y int) uint32 {
	color := dm.At(x, y)
	r, g, b, a := color.RGBA()
	rgb := (r + g + b)                // [0, 3 * 0xFFFF]
	rgb = rgb / 3                     // [0, 0xFFFF]
	rgba := rgb * a / 0xFFFF          // [0, 0xFFFF]
	depth := rgba * depthMax / 0xFFFF // [0, depthMax]
	return depth
}

func sourceOffset(depth uint32, config Config) uint32 {
	maxWidth := config.SeparationMax - config.SeparationMin
	offsetWidth := depth * uint32(maxWidth) / depthMax
	var offset uint32
	if config.CrossEyed {
		offset = uint32(config.SeparationMin) + offsetWidth
	} else {
		offset = uint32(config.SeparationMax) - offsetWidth
	}
	return offset
}

func newMutableImage(dm, bg image.Image) *mutableImage {
	cm := bg.ColorModel()
	bounds := dm.Bounds()
	var imageRows = make([]imageRow, bounds.Max.Y)
	image := new(mutableImage)
	image.cm = cm
	image.bounds = bounds
	image.imageRows = imageRows
	return image
}

type mutableImage struct {
	cm        color.Model
	bounds    image.Rectangle
	imageRows []imageRow
}

func (i *mutableImage) ColorModel() color.Model {
	return i.cm
}

func (i *mutableImage) Bounds() image.Rectangle {
	return i.bounds
}

func (i *mutableImage) At(x, y int) color.Color {
	return i.imageRows[y].colors[x]
}

type imageRow struct {
	colors []color.Color
}
<|endoftext|>


<commit_before>/*
MIT License

Copyright (c) 2017 Simon Schmidt

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/

package serializer

import "reflect"
import "github.com/byte-mug/golibs/preciseio"

type CodecElement interface{
	Read(r preciseio.PreciseReader,v reflect.Value) error
	Write(w *preciseio.PreciseWriter,v reflect.Value) error
}

type ceBlob struct{}
func (ce ceBlob) Read(r preciseio.PreciseReader,v reflect.Value) error {
	b,e := r.ReadBlob()
	if e!=nil { return e }
	v.SetBytes(b)
	return nil
}
func (ce ceBlob) Write(w *preciseio.PreciseWriter,v reflect.Value) error {
	return w.WriteBlob(v.Bytes())
}

type ceString struct{}
func (ce ceString) Read(r preciseio.PreciseReader,v reflect.Value) error {
	b,e := r.ReadBlob()
	if e!=nil { return e }
	v.SetString(string(b))
	return nil
}
func (ce ceString) Write(w *preciseio.PreciseWriter,v reflect.Value) error {
	return w.WriteBlob([]byte(v.String()))
}

type ceInt struct{}
func (ce ceInt) Read(r preciseio.PreciseReader,v reflect.Value) error {
	i,e := r.ReadVarint()
	if e!=nil { return e }
	v.SetInt(i)
	return nil
}
func (ce ceInt) Write(w *preciseio.PreciseWriter,v reflect.Value) error {
	return w.WriteVarint(v.Int())
}

type ceUint struct{}
func (ce ceUint) Read(r preciseio.PreciseReader,v reflect.Value) error {
	i,e := r.ReadUvarint()
	if e!=nil { return e }
	v.SetUint(i)
	return nil
}
func (ce ceUint) Write(w *preciseio.PreciseWriter,v reflect.Value) error {
	return w.WriteUvarint(v.Uint())
}

type ceByte struct{}
func (ce ceByte) Read(r preciseio.PreciseReader,v reflect.Value) error {
	b,e := r.R.ReadByte()
	if e!=nil { return e }
	v.SetUint(uint64(b))
	return nil
}
func (ce ceByte) Write(w *preciseio.PreciseWriter,v reflect.Value) error {
	return w.W.WriteByte(byte(v.Uint()))
}

type ceSbyte struct{}
func (ce ceSbyte) Read(r preciseio.PreciseReader,v reflect.Value) error {
	b,e := r.R.ReadByte()
	if e!=nil { return e }
	i := int8(b)
	v.SetInt(int64(i))
	return nil
}
func (ce ceSbyte) Write(w *preciseio.PreciseWriter,v reflect.Value) error {
	i := int8(v.Int())
	return w.W.WriteByte(byte(i))
}

type ceSlice struct{
	child CodecElement
	t reflect.Type
}
func (ce ceSlice) Read(r preciseio.PreciseReader,v reflect.Value) error {
	n,e := r.ReadListLength()
	if e!=nil { return e }
	if n==0 {
		v.Set(reflect.Zero(v.Type()))
		return nil
	}
	nv := reflect.MakeSlice(ce.t,n,n)
	for i:=0; i<n; i++ {
		e = ce.child.Read(r,nv.Index(i))
		if e!=nil { return e }
	}
	v.Set(nv)
	return nil
}
func (ce ceSlice) Write(w *preciseio.PreciseWriter,v reflect.Value) error {
	v = CastV(ce.t,v)
	if v.IsNil() { return w.WriteListLength(0) }
	n := v.Len()
	e := w.WriteListLength(n)
	if e!=nil { return e }
	for i:=0; i<n; i++ {
		e = ce.child.Write(w,v.Index(i))
		if e!=nil { return e }
	}
	return nil
}

type ceMap struct{
	k CodecElement
	v CodecElement
	t reflect.Type
}
func (ce ceMap) Read(r preciseio.PreciseReader,v reflect.Value) error {
	b,e := r.R.ReadByte()
	if e!=nil { return e }
	if b==0 {
		v.Set(reflect.Zero(v.Type()))
		return nil
	}
	n,e := r.ReadListLength()
	if e!=nil { return e }
	nv := reflect.MakeMapWithSize(ce.t,n)
	ckv := reflect.New(ce.t.Key()).Elem()
	cvv := reflect.New(ce.t.Elem()).Elem()
	for i:=0 ; i<n; i++ {
		e = ce.k.Read(r,ckv)
		if e!=nil { return e }
		e = ce.v.Read(r,cvv)
		if e!=nil { return e }
		nv.SetMapIndex(ckv,cvv)
	}
	v.Set(nv)
	return nil
}
func (ce ceMap) Write(w *preciseio.PreciseWriter,v reflect.Value) error {
	v = CastV(ce.t,v)
	if v.IsNil() { return w.W.WriteByte(0) }
	e := w.W.WriteByte(0xff)
	if e!=nil { return e }
	keys := v.MapKeys()
	n := len(keys)
	e = w.WriteListLength(n)
	if e!=nil { return e }
	for _,key := range keys {
		e = ce.k.Write(w,key)
		if e!=nil { return e }
		e = ce.v.Write(w,v.MapIndex(key))
		if e!=nil { return e }
	}
	return nil
}

type ceArray struct{
	child CodecElement
	t reflect.Type
}
func (ce ceArray) Read(r preciseio.PreciseReader,v reflect.Value) error {
	n := ce.t.Len()
	nv := v
	wrongtype := v.Type()!=ce.t // Type-mismatch
	settable := v.CanSet() // Non-Settable
	if wrongtype || !settable { nv = reflect.New(ce.t).Elem() } // Allocate in heap.
	for i:=0; i<n; i++ {
		e := ce.child.Read(r,nv.Index(i))
		if e!=nil { return e }
	}
	if wrongtype || !settable {
		v.Set(nv)
	}
	return nil
}
func (ce ceArray) Write(w *preciseio.PreciseWriter,v reflect.Value) error {
	v = CastV(ce.t,v)
	n := ce.t.Len()
	for i:=0; i<n; i++ {
		e := ce.child.Write(w,v.Index(i))
		if e!=nil { return e }
	}
	return nil
}
type cePtr struct{
	child CodecElement
	t reflect.Type
}
func (ce cePtr) Read(r preciseio.PreciseReader,v reflect.Value) error {
	b,e := r.R.ReadByte()
	if e!=nil { return e }
	if b==0 {
		v.Set(reflect.Zero(v.Type()))
		return nil
	}
	pv := reflect.New(ce.t.Elem())
	v.Set(pv)
	return ce.child.Read(r,pv.Elem())
}
func (ce cePtr) Write(w *preciseio.PreciseWriter,v reflect.Value) error {
	v = CastV(ce.t,v)
	if v.IsNil() { return w.W.WriteByte(0) }
	e := w.W.WriteByte(0xff)
	if e!=nil { return e }
	return ce.child.Write(w,v.Elem())
}

func StripawayPtr(i interface{}) CodecElement {
	t := reflect.TypeOf(i)
	ce := serializerFor(t.Elem())
	if ce==nil { return nil }
	return ceStripawayPtr{ce,t}
}
func StripawayPtrWith(i interface{}, ce CodecElement) CodecElement {
	t := reflect.TypeOf(i)
	if ce==nil { return nil }
	return ceStripawayPtr{ce,t}
}

type ceStripawayPtr struct{
	child CodecElement
	t reflect.Type
}
func (ce ceStripawayPtr) Read(r preciseio.PreciseReader,v reflect.Value) error {
	pv := reflect.New(ce.t.Elem())
	v.Set(pv)
	return ce.child.Read(r,pv.Elem())
}
func (ce ceStripawayPtr) Write(w *preciseio.PreciseWriter,v reflect.Value) error {
	v = CastV(ce.t,v)
	var ev reflect.Value
	if v.IsNil() { ev = reflect.Zero(ce.t.Elem()) } else { ev = v.Elem() }
	return ce.child.Write(w,ev)
}

func AddPtr(i interface{}) CodecElement {
	t := reflect.TypeOf(i)
	ce := serializerFor(t.Elem())
	if ce==nil { return nil }
	return ceStripawayPtr{ce,t}
}
func AddPtrWith(i interface{}, ce CodecElement) CodecElement {
	t := reflect.TypeOf(i)
	if ce==nil { return nil }
	return ceStripawayPtr{ce,t}
}

type ceAddPtr struct{
	child CodecElement
	t reflect.Type
}
func (ce ceAddPtr) Read(r preciseio.PreciseReader,v reflect.Value) error {
	ptr := reflect.New(ce.t).Elem()
	err := ce.child.Read(r,ptr)
	if !cell.IsNil() { v.Set(ptr.Elem()) }
	return err
}
func (ce ceAddPtr) Write(w *preciseio.PreciseWriter,v reflect.Value) error {
	ptr := reflect.New(ce.t.Elem())
	ptr.Elem().Set(CastV(ce.t.Elem(),v))
	return ce.child.Write(w,ptr)
}

<commit_msg>bugfix<commit_after>/*
MIT License

Copyright (c) 2017 Simon Schmidt

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/

package serializer

import "reflect"
import "github.com/byte-mug/golibs/preciseio"

type CodecElement interface{
	Read(r preciseio.PreciseReader,v reflect.Value) error
	Write(w *preciseio.PreciseWriter,v reflect.Value) error
}

type ceBlob struct{}
func (ce ceBlob) Read(r preciseio.PreciseReader,v reflect.Value) error {
	b,e := r.ReadBlob()
	if e!=nil { return e }
	v.SetBytes(b)
	return nil
}
func (ce ceBlob) Write(w *preciseio.PreciseWriter,v reflect.Value) error {
	return w.WriteBlob(v.Bytes())
}

type ceString struct{}
func (ce ceString) Read(r preciseio.PreciseReader,v reflect.Value) error {
	b,e := r.ReadBlob()
	if e!=nil { return e }
	v.SetString(string(b))
	return nil
}
func (ce ceString) Write(w *preciseio.PreciseWriter,v reflect.Value) error {
	return w.WriteBlob([]byte(v.String()))
}

type ceInt struct{}
func (ce ceInt) Read(r preciseio.PreciseReader,v reflect.Value) error {
	i,e := r.ReadVarint()
	if e!=nil { return e }
	v.SetInt(i)
	return nil
}
func (ce ceInt) Write(w *preciseio.PreciseWriter,v reflect.Value) error {
	return w.WriteVarint(v.Int())
}

type ceUint struct{}
func (ce ceUint) Read(r preciseio.PreciseReader,v reflect.Value) error {
	i,e := r.ReadUvarint()
	if e!=nil { return e }
	v.SetUint(i)
	return nil
}
func (ce ceUint) Write(w *preciseio.PreciseWriter,v reflect.Value) error {
	return w.WriteUvarint(v.Uint())
}

type ceByte struct{}
func (ce ceByte) Read(r preciseio.PreciseReader,v reflect.Value) error {
	b,e := r.R.ReadByte()
	if e!=nil { return e }
	v.SetUint(uint64(b))
	return nil
}
func (ce ceByte) Write(w *preciseio.PreciseWriter,v reflect.Value) error {
	return w.W.WriteByte(byte(v.Uint()))
}

type ceSbyte struct{}
func (ce ceSbyte) Read(r preciseio.PreciseReader,v reflect.Value) error {
	b,e := r.R.ReadByte()
	if e!=nil { return e }
	i := int8(b)
	v.SetInt(int64(i))
	return nil
}
func (ce ceSbyte) Write(w *preciseio.PreciseWriter,v reflect.Value) error {
	i := int8(v.Int())
	return w.W.WriteByte(byte(i))
}

type ceSlice struct{
	child CodecElement
	t reflect.Type
}
func (ce ceSlice) Read(r preciseio.PreciseReader,v reflect.Value) error {
	n,e := r.ReadListLength()
	if e!=nil { return e }
	if n==0 {
		v.Set(reflect.Zero(v.Type()))
		return nil
	}
	nv := reflect.MakeSlice(ce.t,n,n)
	for i:=0; i<n; i++ {
		e = ce.child.Read(r,nv.Index(i))
		if e!=nil { return e }
	}
	v.Set(nv)
	return nil
}
func (ce ceSlice) Write(w *preciseio.PreciseWriter,v reflect.Value) error {
	v = CastV(ce.t,v)
	if v.IsNil() { return w.WriteListLength(0) }
	n := v.Len()
	e := w.WriteListLength(n)
	if e!=nil { return e }
	for i:=0; i<n; i++ {
		e = ce.child.Write(w,v.Index(i))
		if e!=nil { return e }
	}
	return nil
}

type ceMap struct{
	k CodecElement
	v CodecElement
	t reflect.Type
}
func (ce ceMap) Read(r preciseio.PreciseReader,v reflect.Value) error {
	b,e := r.R.ReadByte()
	if e!=nil { return e }
	if b==0 {
		v.Set(reflect.Zero(v.Type()))
		return nil
	}
	n,e := r.ReadListLength()
	if e!=nil { return e }
	nv := reflect.MakeMapWithSize(ce.t,n)
	ckv := reflect.New(ce.t.Key()).Elem()
	cvv := reflect.New(ce.t.Elem()).Elem()
	for i:=0 ; i<n; i++ {
		e = ce.k.Read(r,ckv)
		if e!=nil { return e }
		e = ce.v.Read(r,cvv)
		if e!=nil { return e }
		nv.SetMapIndex(ckv,cvv)
	}
	v.Set(nv)
	return nil
}
func (ce ceMap) Write(w *preciseio.PreciseWriter,v reflect.Value) error {
	v = CastV(ce.t,v)
	if v.IsNil() { return w.W.WriteByte(0) }
	e := w.W.WriteByte(0xff)
	if e!=nil { return e }
	keys := v.MapKeys()
	n := len(keys)
	e = w.WriteListLength(n)
	if e!=nil { return e }
	for _,key := range keys {
		e = ce.k.Write(w,key)
		if e!=nil { return e }
		e = ce.v.Write(w,v.MapIndex(key))
		if e!=nil { return e }
	}
	return nil
}

type ceArray struct{
	child CodecElement
	t reflect.Type
}
func (ce ceArray) Read(r preciseio.PreciseReader,v reflect.Value) error {
	n := ce.t.Len()
	nv := v
	wrongtype := v.Type()!=ce.t // Type-mismatch
	settable := v.CanSet() // Non-Settable
	if wrongtype || !settable { nv = reflect.New(ce.t).Elem() } // Allocate in heap.
	for i:=0; i<n; i++ {
		e := ce.child.Read(r,nv.Index(i))
		if e!=nil { return e }
	}
	if wrongtype || !settable {
		v.Set(nv)
	}
	return nil
}
func (ce ceArray) Write(w *preciseio.PreciseWriter,v reflect.Value) error {
	v = CastV(ce.t,v)
	n := ce.t.Len()
	for i:=0; i<n; i++ {
		e := ce.child.Write(w,v.Index(i))
		if e!=nil { return e }
	}
	return nil
}
type cePtr struct{
	child CodecElement
	t reflect.Type
}
func (ce cePtr) Read(r preciseio.PreciseReader,v reflect.Value) error {
	b,e := r.R.ReadByte()
	if e!=nil { return e }
	if b==0 {
		v.Set(reflect.Zero(v.Type()))
		return nil
	}
	pv := reflect.New(ce.t.Elem())
	v.Set(pv)
	return ce.child.Read(r,pv.Elem())
}
func (ce cePtr) Write(w *preciseio.PreciseWriter,v reflect.Value) error {
	v = CastV(ce.t,v)
	if v.IsNil() { return w.W.WriteByte(0) }
	e := w.W.WriteByte(0xff)
	if e!=nil { return e }
	return ce.child.Write(w,v.Elem())
}

func StripawayPtr(i interface{}) CodecElement {
	t := reflect.TypeOf(i)
	ce := serializerFor(t.Elem())
	if ce==nil { return nil }
	return ceStripawayPtr{ce,t}
}
func StripawayPtrWith(i interface{}, ce CodecElement) CodecElement {
	t := reflect.TypeOf(i)
	if ce==nil { return nil }
	return ceStripawayPtr{ce,t}
}

type ceStripawayPtr struct{
	child CodecElement
	t reflect.Type
}
func (ce ceStripawayPtr) Read(r preciseio.PreciseReader,v reflect.Value) error {
	pv := reflect.New(ce.t.Elem())
	v.Set(pv)
	return ce.child.Read(r,pv.Elem())
}
func (ce ceStripawayPtr) Write(w *preciseio.PreciseWriter,v reflect.Value) error {
	v = CastV(ce.t,v)
	var ev reflect.Value
	if v.IsNil() { ev = reflect.Zero(ce.t.Elem()) } else { ev = v.Elem() }
	return ce.child.Write(w,ev)
}

func AddPtr(i interface{}) CodecElement {
	t := reflect.TypeOf(i)
	ce := serializerFor(t.Elem())
	if ce==nil { return nil }
	return ceStripawayPtr{ce,t}
}
func AddPtrWith(i interface{}, ce CodecElement) CodecElement {
	t := reflect.TypeOf(i)
	if ce==nil { return nil }
	return ceStripawayPtr{ce,t}
}

type ceAddPtr struct{
	child CodecElement
	t reflect.Type
}
func (ce ceAddPtr) Read(r preciseio.PreciseReader,v reflect.Value) error {
	ptr := reflect.New(ce.t).Elem()
	err := ce.child.Read(r,ptr)
	if !ptr.IsNil() { v.Set(ptr.Elem()) }
	return err
}
func (ce ceAddPtr) Write(w *preciseio.PreciseWriter,v reflect.Value) error {
	ptr := reflect.New(ce.t.Elem())
	ptr.Elem().Set(CastV(ce.t.Elem(),v))
	return ce.child.Write(w,ptr)
}

<|endoftext|>


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

package server

import (
	"context"
	"net/url"
	"strings"

	aauth "github.com/apigee/apigee-remote-service-golib/auth"
	libAuth "github.com/apigee/apigee-remote-service-golib/auth"
	"github.com/apigee/apigee-remote-service-golib/log"
	"github.com/apigee/apigee-remote-service-golib/quota"
	core "github.com/envoyproxy/go-control-plane/envoy/api/v2/core"
	auth "github.com/envoyproxy/go-control-plane/envoy/service/auth/v2"
	envoy_type "github.com/envoyproxy/go-control-plane/envoy/type"
	"github.com/gogo/googleapis/google/rpc"
	rpcstatus "google.golang.org/genproto/googleapis/rpc/status"
	"google.golang.org/grpc"
)

const (
	jwtFilterMetadataKey = "envoy.filters.http.jwt_authn"
)

// AuthorizationServer server
type AuthorizationServer struct {
	handler *Handler
}

// Register registers
func (a *AuthorizationServer) Register(s *grpc.Server, handler *Handler) {
	auth.RegisterAuthorizationServer(s, a)
	a.handler = handler
}

// Check does check
func (a *AuthorizationServer) Check(ctx context.Context, req *auth.CheckRequest) (*auth.CheckResponse, error) {

	api, ok := req.Attributes.Request.Http.Headers[a.handler.targetHeader]
	if !ok {
		log.Debugf("missing target header %s", a.handler.targetHeader)
		return a.unauthenticated(), nil
	}

	// check for JWT from Envoy filter
	protoBufStruct := req.Attributes.GetMetadataContext().GetFilterMetadata()[jwtFilterMetadataKey]
	fieldsMap := protoBufStruct.GetFields()
	var claims map[string]interface{}
	// TODO: just using the first for now, should configure and/or support multiple
	for k, v := range fieldsMap {
		log.Debugf("Using JWT at key: %s", k)
		claims = DecodeToMap(v.GetStructValue())
	}

	splitPath := strings.SplitN(req.Attributes.Request.Http.Path, "?", 2)
	path := splitPath[0]

	apiKey, ok := req.Attributes.Request.Http.Headers[a.handler.apiKeyHeader] // grab from header

	if !ok && len(splitPath) > 1 { // look in querystring if not in header
		if qs, err := url.ParseQuery(splitPath[1]); err == nil {
			if keys, ok := qs[a.handler.apiKeyHeader]; ok {
				apiKey = keys[0]
			}
		}
	}

	authContext, err := a.handler.authMan.Authenticate(a.handler, apiKey, claims, a.handler.apiKeyClaim)
	switch err {
	case libAuth.ErrNoAuth:
		return a.unauthenticated(), nil
	case libAuth.ErrBadAuth:
		return a.unauthorized(authContext, api), nil
	case libAuth.ErrInternalError:
		return a.internalError(err), nil
	}

	if len(authContext.APIProducts) == 0 {
		return a.unauthorized(authContext, api), nil
	}

	// match products
	products := a.handler.productMan.Resolve(authContext, api, path)
	if len(products) == 0 {
		return a.unauthorized(authContext, api), nil
	}

	// apply quotas to all matched products
	var quotaArgs = quota.Args{QuotaAmount: 1}
	var exceeded bool
	var anyError error
	for _, p := range products {
		if p.QuotaLimitInt > 0 {
			result, err := a.handler.quotaMan.Apply(authContext, p, quotaArgs)
			if err != nil {
				log.Errorf("quota check: %v", err)
				anyError = err
			} else if result.Exceeded > 0 {
				log.Debugf("quota exceeded: %v", p.Name)
				exceeded = true
			}
		}
	}
	if anyError != nil {
		return a.internalError(anyError), nil
	}
	if exceeded {
		return a.quotaExceeded(authContext, api), nil
	}

	return a.authOK(authContext, api), nil
}

func (a *AuthorizationServer) authOK(authContext *aauth.Context, api string) *auth.CheckResponse {

	headers := makeMetadataHeaders(api, authContext)
	headers = append(headers, &core.HeaderValueOption{
		Header: &core.HeaderValue{
			Key:   headerAuthorized,
			Value: "true",
		},
	})

	return &auth.CheckResponse{
		Status: &rpcstatus.Status{
			Code: int32(rpc.OK),
		},
		HttpResponse: &auth.CheckResponse_OkResponse{
			OkResponse: &auth.OkHttpResponse{
				Headers: headers,
			},
		},
	}
}

func (a *AuthorizationServer) unauthenticated() *auth.CheckResponse {
	return a.createDenyResponse(nil, "", rpc.UNAUTHENTICATED)
}

func (a *AuthorizationServer) internalError(err error) *auth.CheckResponse {
	log.Errorf("sending internal error: %v", err)
	return a.createDenyResponse(nil, "", rpc.INTERNAL)
}

func (a *AuthorizationServer) unauthorized(authContext *aauth.Context, api string) *auth.CheckResponse {
	return a.createDenyResponse(authContext, api, rpc.PERMISSION_DENIED)
}

func (a *AuthorizationServer) quotaExceeded(authContext *aauth.Context, api string) *auth.CheckResponse {
	return a.createDenyResponse(authContext, api, rpc.RESOURCE_EXHAUSTED)
}

func (a *AuthorizationServer) createDenyResponse(authContext *aauth.Context, api string, code rpc.Code) *auth.CheckResponse {

	if a.handler.rejectUnauthorized || authContext == nil { // send reject
		log.Debugf("sending denied: %s", code.String())

		return &auth.CheckResponse{
			Status: &rpcstatus.Status{
				Code: int32(code),
			},
			HttpResponse: &auth.CheckResponse_DeniedResponse{
				DeniedResponse: &auth.DeniedHttpResponse{
					Status: &envoy_type.HttpStatus{
						Code: envoy_type.StatusCode(code),
					},
					Body: code.String(),
				},
			},
		}
	}

	log.Debugf("sending ok (actual: %s)", code.String())
	return &auth.CheckResponse{
		Status: &rpcstatus.Status{
			Code: int32(rpc.OK),
		},
		HttpResponse: &auth.CheckResponse_OkResponse{
			OkResponse: &auth.OkHttpResponse{
				Headers: makeMetadataHeaders(api, authContext),
			},
		},
	}
}
<commit_msg>fix returned http code<commit_after>// Copyright 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package server

import (
	"context"
	"net/url"
	"strings"

	aauth "github.com/apigee/apigee-remote-service-golib/auth"
	libAuth "github.com/apigee/apigee-remote-service-golib/auth"
	"github.com/apigee/apigee-remote-service-golib/log"
	"github.com/apigee/apigee-remote-service-golib/quota"
	core "github.com/envoyproxy/go-control-plane/envoy/api/v2/core"
	auth "github.com/envoyproxy/go-control-plane/envoy/service/auth/v2"
	"github.com/gogo/googleapis/google/rpc"
	rpcstatus "google.golang.org/genproto/googleapis/rpc/status"
	"google.golang.org/grpc"
)

const (
	jwtFilterMetadataKey = "envoy.filters.http.jwt_authn"
)

// AuthorizationServer server
type AuthorizationServer struct {
	handler *Handler
}

// Register registers
func (a *AuthorizationServer) Register(s *grpc.Server, handler *Handler) {
	auth.RegisterAuthorizationServer(s, a)
	a.handler = handler
}

// Check does check
func (a *AuthorizationServer) Check(ctx context.Context, req *auth.CheckRequest) (*auth.CheckResponse, error) {

	api, ok := req.Attributes.Request.Http.Headers[a.handler.targetHeader]
	if !ok {
		log.Debugf("missing target header %s", a.handler.targetHeader)
		return a.unauthenticated(), nil
	}

	// check for JWT from Envoy filter
	protoBufStruct := req.Attributes.GetMetadataContext().GetFilterMetadata()[jwtFilterMetadataKey]
	fieldsMap := protoBufStruct.GetFields()
	var claims map[string]interface{}
	// TODO: just using the first for now, should configure and/or support multiple
	for k, v := range fieldsMap {
		log.Debugf("Using JWT at key: %s", k)
		claims = DecodeToMap(v.GetStructValue())
	}

	splitPath := strings.SplitN(req.Attributes.Request.Http.Path, "?", 2)
	path := splitPath[0]

	apiKey, ok := req.Attributes.Request.Http.Headers[a.handler.apiKeyHeader] // grab from header

	if !ok && len(splitPath) > 1 { // look in querystring if not in header
		if qs, err := url.ParseQuery(splitPath[1]); err == nil {
			if keys, ok := qs[a.handler.apiKeyHeader]; ok {
				apiKey = keys[0]
			}
		}
	}

	authContext, err := a.handler.authMan.Authenticate(a.handler, apiKey, claims, a.handler.apiKeyClaim)
	switch err {
	case libAuth.ErrNoAuth:
		return a.unauthenticated(), nil
	case libAuth.ErrBadAuth:
		return a.unauthorized(authContext, api), nil
	case libAuth.ErrInternalError:
		return a.internalError(err), nil
	}

	if len(authContext.APIProducts) == 0 {
		return a.unauthorized(authContext, api), nil
	}

	// match products
	products := a.handler.productMan.Resolve(authContext, api, path)
	if len(products) == 0 {
		return a.unauthorized(authContext, api), nil
	}

	// apply quotas to all matched products
	var quotaArgs = quota.Args{QuotaAmount: 1}
	var exceeded bool
	var anyError error
	for _, p := range products {
		if p.QuotaLimitInt > 0 {
			result, err := a.handler.quotaMan.Apply(authContext, p, quotaArgs)
			if err != nil {
				log.Errorf("quota check: %v", err)
				anyError = err
			} else if result.Exceeded > 0 {
				log.Debugf("quota exceeded: %v", p.Name)
				exceeded = true
			}
		}
	}
	if anyError != nil {
		return a.internalError(anyError), nil
	}
	if exceeded {
		return a.quotaExceeded(authContext, api), nil
	}

	return a.authOK(authContext, api), nil
}

func (a *AuthorizationServer) authOK(authContext *aauth.Context, api string) *auth.CheckResponse {

	headers := makeMetadataHeaders(api, authContext)
	headers = append(headers, &core.HeaderValueOption{
		Header: &core.HeaderValue{
			Key:   headerAuthorized,
			Value: "true",
		},
	})

	return &auth.CheckResponse{
		Status: &rpcstatus.Status{
			Code: int32(rpc.OK),
		},
		HttpResponse: &auth.CheckResponse_OkResponse{
			OkResponse: &auth.OkHttpResponse{
				Headers: headers,
			},
		},
	}
}

func (a *AuthorizationServer) unauthenticated() *auth.CheckResponse {
	return a.createDenyResponse(nil, "", rpc.UNAUTHENTICATED)
}

func (a *AuthorizationServer) internalError(err error) *auth.CheckResponse {
	log.Errorf("sending internal error: %v", err)
	return a.createDenyResponse(nil, "", rpc.INTERNAL)
}

func (a *AuthorizationServer) unauthorized(authContext *aauth.Context, api string) *auth.CheckResponse {
	return a.createDenyResponse(authContext, api, rpc.PERMISSION_DENIED)
}

func (a *AuthorizationServer) quotaExceeded(authContext *aauth.Context, api string) *auth.CheckResponse {
	return a.createDenyResponse(authContext, api, rpc.RESOURCE_EXHAUSTED)
}

func (a *AuthorizationServer) createDenyResponse(authContext *aauth.Context, api string, code rpc.Code) *auth.CheckResponse {

	if a.handler.rejectUnauthorized || authContext == nil { // send reject
		log.Debugf("sending denied: %s", code.String())

		return &auth.CheckResponse{
			Status: &rpcstatus.Status{
				Code: int32(code),
			},
		}
	}

	log.Debugf("sending ok (actual: %s)", code.String())
	return &auth.CheckResponse{
		Status: &rpcstatus.Status{
			Code: int32(rpc.OK),
		},
		HttpResponse: &auth.CheckResponse_OkResponse{
			OkResponse: &auth.OkHttpResponse{
				Headers: makeMetadataHeaders(api, authContext),
			},
		},
	}
}
<|endoftext|>


<commit_before>package Bloco0

import(
	"github.com/jinzhu/gorm"
	"github.com/chapzin/parse-efd-fiscal/SpedConvert"
	"time"
)

type Reg0150 struct {
	gorm.Model
	Reg string		`gorm:"type:varchar(4)"`
	CodPart string		`gorm:"type:varchar(60);"`
	Nome string		`gorm:"type:varchar(100)"`
	CodPais string		`gorm:"type:varchar(5)"`
	Cnpj string		`gorm:"type:varchar(15)"`
	Cpf string		`gorm:"type:varchar(11)"`
	Ie string		`gorm:"type:varchar(14)"`
	CodMun string		`gorm:"type:varchar(7)"`
	Suframa string		`gorm:"type:varchar(9)"`
	Endereco string		`gorm:"type:varchar(60)"`
	Num string		`gorm:"type:varchar(10)"`
	Compl string		`gorm:"type:varchar(60)"`
	Bairro string		`gorm:"type:varchar(60)"`
	DtIni time.Time 	`gorm:"type:date"`
	DtFin time.Time 	`gorm:"type:date"`
	CnpjSped string		`gorm:"type:varchar(14)"`

}

func (Reg0150) TableName() string {
	return "reg_0150"
}

type Reg0150Sped struct {
	Ln []string
	Reg0000 Reg0000
}

func (s Reg0150Sped) GetReg() string {
	return s.Ln[1]
}

func (s Reg0150Sped) GetCodPart() string {
	return s.Ln[2]
}

func (s Reg0150Sped) GetNome() string {
	return s.Ln[3]
}

func (s Reg0150Sped) GetCodPais() string {
	return s.Ln[4]
}

func (s Reg0150Sped) GetCnpj() string {
	return s.Ln[5]
}

func (s Reg0150Sped) GetCpf() string {
	return s.Ln[6]
}

func (s Reg0150Sped) GetIe() string {
	return s.Ln[7]
}

func (s Reg0150Sped) GetCodMun() string {
	return s.Ln[8]
}

func (s Reg0150Sped) GetSuframa() string {
	return s.Ln[9]
}

func (s Reg0150Sped) GetEndereco() string {
	return s.Ln[10]
}

func (s Reg0150Sped) GetNum() string {
	return s.Ln[11]
}

func (s Reg0150Sped) GetCompl() string {
	return s.Ln[12]
}

func (s Reg0150Sped) GetBairro() string {
	return s.Ln[13]
}

func (s Reg0150Sped) GetDtIni() time.Time {
	return s.Reg0000.DtIni
}

func (s Reg0150Sped) GetDtFin() time.Time {
	return s.Reg0000.DtFin
}

func (s Reg0150Sped) GetCnpjSped() string  {
	return s.Reg0000.Cnpj
}

type iReg0150 interface {
	GetReg() string
	GetCodPart() string
	GetNome() string
	GetCodPais() string
	GetCnpj() string
	GetCpf() string
	GetIe() string
	GetCodMun() string
	GetSuframa() string
	GetEndereco() string
	GetNum() string
	GetCompl() string
	GetBairro() string
	GetDtIni() time.Time
	GetDtFin() time.Time
	GetCnpjSped() string
}

type Reg0150Xml struct {
	// para usar o cliente e o endereco deve ser mapeado como abaixo
	//nfe, _ := mxj.NewMapXml(xmlFile) << vindo de um ioutil.ReadFile
	//	cliente, err := nfe.ValuesForKey("dest")
	//endereco, err := nfe.ValuesForKey("enderDest")

	Cliente []interface{}
	Endereco []interface{}
}

func (x Reg0150Xml) GetReg() string {
	return "0000"
}

func (x Reg0150Xml) GetCodPart() string {
	cnpj := SpedConvert.DataXml(x.Cliente,"CNPJ")
	return cnpj
}

func (x Reg0150Xml) GetNome() string {
	nome := SpedConvert.DataXml(x.Cliente,"xNome")
	return nome
}

func (x Reg0150Xml) GetCodPais() string {
	return "1058"
}

func (x Reg0150Xml) GetCnpj() string {
	cnpj := SpedConvert.DataXml(x.Cliente,"CNPJ")
	return cnpj
}

func (x Reg0150Xml) GetCpf() string {
	cpf := SpedConvert.DataXml(x.Cliente,"CNPJ")
	return cpf
}

func (x Reg0150Xml) GetIe() string {
	ie := SpedConvert.DataXml(x.Cliente,"IE")
	return ie
}

func (x Reg0150Xml) GetCodMun() string {
	codMun := SpedConvert.DataXml(x.Endereco,"cMun")
	return codMun
}

func (x Reg0150Xml) GetSuframa() string {
	return ""
}

func (x Reg0150Xml) GetEndereco() string {
	endereco := SpedConvert.DataXml(x.Endereco,"xLgr")
	return endereco
}

func (x Reg0150Xml) GetNum() string  {
	num := SpedConvert.DataXml(x.Endereco,"nro")
	return num
}

func (x Reg0150Xml) GetCompl() string  {
	compl := SpedConvert.DataXml(x.Endereco,"xCpl")
	return compl
}

func (x Reg0150Xml) GetBairro() string  {
	bairro := SpedConvert.DataXml(x.Endereco,"xBairro")
	return bairro
}

func (x Reg0150Xml) GetDtIni() time.Time {
	return SpedConvert.ConvertDataNull()
}

func (x Reg0150Xml) GetDtFin() time.Time {
	return SpedConvert.ConvertDataNull()
}

func (x Reg0150Xml) GetCnpjSped() string {
	return ""
}

func CreateReg0150 (read iReg0150) Reg0150 {
	reg0150 := Reg0150{
		Reg:		read.GetReg(),
		CodPart:	read.GetCodPart(),
		Nome:		read.GetNome(),
		CodPais:	read.GetCodPais(),
		Cnpj:		read.GetCnpj(),
		Cpf:		read.GetCpf(),
		Ie:		read.GetIe(),
		CodMun:		read.GetCodMun(),
		Suframa:	read.GetSuframa(),
		Endereco:	read.GetSuframa(),
		Num:		read.GetNum(),
		Compl:		read.GetCompl(),
		Bairro:		read.GetBairro(),
		DtIni:		read.GetDtIni(),
		DtFin:		read.GetDtFin(),
		CnpjSped:	read.GetCnpjSped(),
	}
	return  reg0150
}
<commit_msg>Modificando estrutura de interface dos modelos e implementacoes para fontes de leitura<commit_after>package Bloco0

import(
	"github.com/jinzhu/gorm"
	"github.com/chapzin/parse-efd-fiscal/SpedConvert"
	"time"
)

type Reg0150 struct {
	gorm.Model
	Reg string		`gorm:"type:varchar(4)"`
	CodPart string		`gorm:"type:varchar(60);"`
	Nome string		`gorm:"type:varchar(100)"`
	CodPais string		`gorm:"type:varchar(5)"`
	Cnpj string		`gorm:"type:varchar(15)"`
	Cpf string		`gorm:"type:varchar(11)"`
	Ie string		`gorm:"type:varchar(14)"`
	CodMun string		`gorm:"type:varchar(7)"`
	Suframa string		`gorm:"type:varchar(9)"`
	Endereco string		`gorm:"type:varchar(60)"`
	Num string		`gorm:"type:varchar(10)"`
	Compl string		`gorm:"type:varchar(60)"`
	Bairro string		`gorm:"type:varchar(60)"`
	DtIni time.Time 	`gorm:"type:date"`
	DtFin time.Time 	`gorm:"type:date"`
	CnpjSped string		`gorm:"type:varchar(14)"`

}

func (Reg0150) TableName() string {
	return "reg_0150"
}

type iReg0150 interface {
	GetReg0150() Reg0150

}

type Reg0150Sped struct {
	Ln []string
	Reg0000 Reg0000
}

func (s Reg0150Sped) GetReg0150() Reg0150 {
	reg0150 := Reg0150{
		Reg:		s.Ln[1],
		CodPart:	s.Ln[2],
		Nome:		s.Ln[3],
		CodPais:	s.Ln[4],
		Cnpj:		s.Ln[5],
		Cpf:		s.Ln[6],
		Ie:			s.Ln[7],
		CodMun:		s.Ln[8],
		Suframa:	s.Ln[9],
		Endereco:	s.Ln[10],
		Num:		s.Ln[11],
		Compl:		s.Ln[12],
		Bairro:		s.Ln[13],
		DtIni:		s.Reg0000.DtIni,
		DtFin:		s.Reg0000.DtFin,
		CnpjSped:	s.Reg0000.Cnpj,
	}
	return  reg0150
}

type Reg0150Xml struct {
	// para usar o cliente e o endereco deve ser mapeado como abaixo
	//nfe, _ := mxj.NewMapXml(xmlFile) << vindo de um ioutil.ReadFile
	//	cliente, err := nfe.ValuesForKey("dest")
	//endereco, err := nfe.ValuesForKey("enderDest")

	Cliente []interface{}
	Endereco []interface{}
}

func (x Reg0150Xml) GetReg0150() Reg0150 {
	reg0150 := Reg0150{
		Reg:		"0150",
		CodPart:	SpedConvert.DataXml(x.Cliente,"CNPJ"),
		Nome:		SpedConvert.DataXml(x.Cliente,"xNome"),
		CodPais:	"1058", //Importante separar esse numero em uma constante em outro arquivo, talvez. Assim, vai ser constante geral para o projeto
		Cnpj:		SpedConvert.DataXml(x.Cliente,"CNPJ"),
		Cpf:		SpedConvert.DataXml(x.Cliente,"CNPJ"),
		Ie:			SpedConvert.DataXml(x.Cliente,"IE"),
		CodMun:		SpedConvert.DataXml(x.Endereco,"cMun"),
		Suframa:	"",
		Endereco:	SpedConvert.DataXml(x.Endereco,"xLgr"),
		Num:		SpedConvert.DataXml(x.Endereco,"nro"),
		Compl:		SpedConvert.DataXml(x.Endereco,"xCpl"),
		Bairro:		SpedConvert.DataXml(x.Endereco,"xBairro"),
		DtIni:		SpedConvert.ConvertDataNull(),
		DtFin:		SpedConvert.ConvertDataNull(),
		CnpjSped:	"",
	}
	return  reg0150
}

func CreateReg0150 (read iReg0150) Reg0150 {
	return read.GetReg0150()
}
<|endoftext|>


<commit_before>package config

import (
	"fmt"
	"github.com/mediocregopher/flagconfig"
	"strings"
)

// The address hyrax should listen on for other hyrax nodes to connect
var MeshListenAddr string

// The address to advertise to other hyrax nodes that we're listening for new
// mesh connections on
var MeshAdvertiseAddr string

// Whether or not this is the first node active in the pool. If it is then
// StorageAddr and InitSecrets will be populated as well
var FirstNode bool

// The address this hyrax node, and others should use to connect to this one's
// backend storage (redis).
var StorageAddr string

// Initial secrets to load in if this is the first-node
var InitSecrets [][]byte

// ListenAddr is a structure containing all the information needed to create a
// listener
type ListenAddr struct {

	// The type of the listen address. At the moment the only option is tcp
	Type string

	// The actual address to listen for client connections on
	Addr string

	// The format to expect data to come in as. At the moment the only option is
	// json
	Format string
}

// The list of currently active ListenAddrs
var ListenAddrs []ListenAddr

func Load() error {
	fc := flagconfig.New("hyrax")
	fc.StrParam(
		"mesh-listen-addr",
		"The address hyrax should listen on for other hyrax nodes to connect",
		":9379",
	)
	fc.StrParam(
		"mesh-advertise-addr",
		"The address to advertise to other hyrax nodes that we're listening for new mesh connections on",
		"127.0.0.1:9379",
	)
	fc.FlagParam(
		"first-node",
		"Set if this is the first node active in the pool. If it is then the init-secrets parameter is required as well",
		false,
	)
	fc.StrParams(
		"init-secret",
		"A global secret key as a string. Can be specified multiple times if this is a first-node",
	)
	fc.StrParam(
		"storage-addr",
		"The address this hyrax node, and others should use to connect to this one's backend storage (redis).",
		"127.0.0.1:6379",
	)
	fc.StrParams(
		"listen-addr",
		"The type, address, and format to listen for client connections on, separated by a \"::\". At the moment the only type is tcp, the only format is json. Can be specified multiple times",
		"tcp::json:::2379",
	)
	if err := fc.Parse(); err != nil {
		return err
	}

	lasRaw := fc.GetStrs("listen-addr")
	las := make([]ListenAddr,len(lasRaw))
	for i := range lasRaw {
		la, err := parseListenAddr(lasRaw[i])
		if err != nil {
			return err
		}
		las[i] = *la
	}

	fn := fc.GetFlag("first-node")
	isRaw := fc.GetStrs("init-secret")
	if fn && len(isRaw) == 0 {
		return fmt.Errorf("first-node set but no init-secrets specified")
	}

	is := make([][]byte, len(isRaw))
	for i := range isRaw {
		is[i] = []byte(isRaw[i])
	}

	MeshListenAddr = fc.GetStr("mesh-listen-addr")
	MeshAdvertiseAddr = fc.GetStr("mesh-advertise-addr")
	FirstNode = fn
	InitSecrets = is
	StorageAddr = fc.GetStr("storage-addr")
	ListenAddrs = las
	return nil
}

func parseListenAddr(param string) (*ListenAddr, error) {
	pieces := strings.SplitN(param,"::",3)
	la := ListenAddr{
		Type: pieces[0],
		Addr: pieces[1],
		Format: pieces[2],
	}

	return &la, nil
}
<commit_msg>fixed config parsing of ListenAddr struct<commit_after>package config

import (
	"fmt"
	"github.com/mediocregopher/flagconfig"
	"strings"
)

// The address hyrax should listen on for other hyrax nodes to connect
var MeshListenAddr string

// The address to advertise to other hyrax nodes that we're listening for new
// mesh connections on
var MeshAdvertiseAddr string

// Whether or not this is the first node active in the pool. If it is then
// StorageAddr and InitSecrets will be populated as well
var FirstNode bool

// The address this hyrax node, and others should use to connect to this one's
// backend storage (redis).
var StorageAddr string

// Initial secrets to load in if this is the first-node
var InitSecrets [][]byte

// ListenAddr is a structure containing all the information needed to create a
// listener
type ListenAddr struct {

	// The type of the listen address. At the moment the only option is tcp
	Type string

	// The format to expect data to come in as. At the moment the only option is
	// json
	Format string

	// The actual address to listen for client connections on
	Addr string

}

// The list of currently active ListenAddrs
var ListenAddrs []ListenAddr

func Load() error {
	fc := flagconfig.New("hyrax")
	fc.StrParam(
		"mesh-listen-addr",
		"The address hyrax should listen on for other hyrax nodes to connect",
		":9379",
	)
	fc.StrParam(
		"mesh-advertise-addr",
		"The address to advertise to other hyrax nodes that we're listening for new mesh connections on",
		"127.0.0.1:9379",
	)
	fc.FlagParam(
		"first-node",
		"Set if this is the first node active in the pool. If it is then the init-secrets parameter is required as well",
		false,
	)
	fc.StrParams(
		"init-secret",
		"A global secret key as a string. Can be specified multiple times if this is a first-node",
	)
	fc.StrParam(
		"storage-addr",
		"The address this hyrax node, and others should use to connect to this one's backend storage (redis).",
		"127.0.0.1:6379",
	)
	fc.StrParams(
		"listen-addr",
		"The type, address, and format to listen for client connections on, separated by a \"::\". At the moment the only type is tcp, the only format is json. Can be specified multiple times",
		"tcp::json:::2379",
	)
	if err := fc.Parse(); err != nil {
		return err
	}

	lasRaw := fc.GetStrs("listen-addr")
	las := make([]ListenAddr,len(lasRaw))
	for i := range lasRaw {
		la, err := parseListenAddr(lasRaw[i])
		if err != nil {
			return err
		}
		las[i] = *la
	}

	fn := fc.GetFlag("first-node")
	isRaw := fc.GetStrs("init-secret")
	if fn && len(isRaw) == 0 {
		return fmt.Errorf("first-node set but no init-secrets specified")
	}

	is := make([][]byte, len(isRaw))
	for i := range isRaw {
		is[i] = []byte(isRaw[i])
	}

	MeshListenAddr = fc.GetStr("mesh-listen-addr")
	MeshAdvertiseAddr = fc.GetStr("mesh-advertise-addr")
	FirstNode = fn
	InitSecrets = is
	StorageAddr = fc.GetStr("storage-addr")
	ListenAddrs = las
	return nil
}

func parseListenAddr(param string) (*ListenAddr, error) {
	pieces := strings.SplitN(param,"::",3)
	la := ListenAddr{
		Type: pieces[0],
		Format: pieces[1],
		Addr: pieces[2],
	}

	return &la, nil
}
<|endoftext|>


<commit_before>package ari

import "fmt"

// Playback describes a session of playing media to a channel
// MediaUri is of the form 'type:name', where type can be one of:
//  - sound : a Sound on the Asterisk system
//  - recording : a StoredRecording on the Asterisk system
//  - number : a number, to be spoken
//  - digits : a set of digits, to be spoken
//  - characters : a set of characters, to be spoken
//  - tone : a tone sequence, which may optionally take a tonezone parameter (e.g, tone:ring:tonezone=fr)
//
// TargetUri is of the form 'type:id', and looks like the following two options:
//  - bridge:bridgeId
//  - channel:channelId

type Playback struct {
	Id        string `json:"id"` // Unique Id for this playback session
	Language  string `json:"language,omitempty"`
	MediaUri  string `json:"media_uri"`  // URI for the media which is to be played
	State     string `json:"state"`      // State of the playback operation
	TargetUri string `json:"target_uri"` // URI of the channel or bridge on which the media should be played (follows format of 'type':'name')

	client *Client // Reference to the client which created or returned this channel
}

//Get a playback's details
//Equivalent to GET /playbacks/{playbackId}
func (c *Client) GetPlaybackDetails(playbackId string) (Playback, error) {
	var m Playback
	err := c.AriGet("/playbacks/"+playbackId, &m)
	if err != nil {
		return m, err
	}
	return m, nil
}

// Control the current Playback
func (p *Playback) Control(playbackId string, operation string) error {
	if p.client == nil {
		return fmt.Errorf("No client found in Playback")
	}
	return p.client.ControlPlayback(playbackId, operation)
}

// Stop the current Playback.
func (p *Playback) Stop(playbackId string) error {
	if p.client == nil {
		return fmt.Errorf("No client found in Playback")
	}
	return p.client.StopPlayback(playbackId)
}

//Equivalent to POST /playbacks/{playbackId}/control
func (c *Client) ControlPlayback(playbackId string, operation string) error {

	//Request structure for controlling playback. Operation is required.
	type request struct {
		Operation string `json:"operation"`
	}

	req := request{operation}

	//Make the request
	err := c.AriPost("/playbacks/"+playbackId+"/control", nil, &req)

	if err != nil {
		return err
	}
	return nil
}

//Stop a playback.
//Equivalent to DELETE /playbacks/{playbackId}
func (c *Client) StopPlayback(playbackId string) error {
	err := c.AriDelete("/playbacks/"+playbackId, nil, nil)
	return err
}
<commit_msg>No more redundant and unnecessary and blatantly stupid arguments<commit_after>package ari

import "fmt"

// Playback describes a session of playing media to a channel
// MediaUri is of the form 'type:name', where type can be one of:
//  - sound : a Sound on the Asterisk system
//  - recording : a StoredRecording on the Asterisk system
//  - number : a number, to be spoken
//  - digits : a set of digits, to be spoken
//  - characters : a set of characters, to be spoken
//  - tone : a tone sequence, which may optionally take a tonezone parameter (e.g, tone:ring:tonezone=fr)
//
// TargetUri is of the form 'type:id', and looks like the following two options:
//  - bridge:bridgeId
//  - channel:channelId

type Playback struct {
	Id        string `json:"id"` // Unique Id for this playback session
	Language  string `json:"language,omitempty"`
	MediaUri  string `json:"media_uri"`  // URI for the media which is to be played
	State     string `json:"state"`      // State of the playback operation
	TargetUri string `json:"target_uri"` // URI of the channel or bridge on which the media should be played (follows format of 'type':'name')

	client *Client // Reference to the client which created or returned this channel
}

//Get a playback's details
//Equivalent to GET /playbacks/{playbackId}
func (c *Client) GetPlaybackDetails(playbackId string) (Playback, error) {
	var m Playback
	err := c.AriGet("/playbacks/"+playbackId, &m)
	if err != nil {
		return m, err
	}
	return m, nil
}

// Control the current Playback
func (p *Playback) Control(operation string) error {
	if p.client == nil {
		return fmt.Errorf("No client found in Playback")
	}
	return p.client.ControlPlayback(p.Id, operation)
}

// Stop the current Playback.
func (p *Playback) Stop() error {
	if p.client == nil {
		return fmt.Errorf("No client found in Playback")
	}
	return p.client.StopPlayback(p.Id)
}

//Equivalent to POST /playbacks/{playbackId}/control
func (c *Client) ControlPlayback(playbackId string, operation string) error {

	//Request structure for controlling playback. Operation is required.
	type request struct {
		Operation string `json:"operation"`
	}

	req := request{operation}

	//Make the request
	err := c.AriPost("/playbacks/"+playbackId+"/control", nil, &req)

	if err != nil {
		return err
	}
	return nil
}

//Stop a playback.
//Equivalent to DELETE /playbacks/{playbackId}
func (c *Client) StopPlayback(playbackId string) error {
	err := c.AriDelete("/playbacks/"+playbackId, nil, nil)
	return err
}
<|endoftext|>


<commit_before>package ari

// Playback represents a communication path for interacting
// with an Asterisk server for playback resources
type Playback interface {
}
<commit_msg>v3 - Add Playback interface methods<commit_after>package ari

// Playback represents a communication path for interacting
// with an Asterisk server for playback resources
type Playback interface {

	// Data gets the playback data
	Data(id string) (PlaybackData, error)

	// Control performs the given operation on the current playback
	Control(id string, op string) error

	// Stop stops the playback
	Stop(id string) error
}

// PlaybackData represents the state of a playback
type PlaybackData struct {
	ID        string `json:"id"` // Unique ID for this playback session
	Language  string `json:"language,omitempty"`
	MediaURI  string `json:"media_uri"`  // URI for the media which is to be played
	State     string `json:"state"`      // State of the playback operation
	TargetURI string `json:"target_uri"` // URI of the channel or bridge on which the media should be played (follows format of 'type':'name')
}

// NewPlaybackHandle builds a handle to the playback id
func NewPlaybackHandle(id string, pb Playback) *PlaybackHandle {
	return &PlaybackHandle{
		id: id,
		p:  pb,
	}
}

// PlaybackHandle is the handle for performing playback operations
type PlaybackHandle struct {
	id string
	p  Playback
}

// Data gets the playback data
func (ph *PlaybackHandle) Data() (pd PlaybackData, err error) {
	pd, err = ph.p.Data(ph.id)
	return
}

// Control performs the given operation
func (ph *PlaybackHandle) Control(op string) (err error) {
	err = ph.p.Control(ph.id, op)
	return
}

// Stop stops the playback
func (ph *PlaybackHandle) Stop() (err error) {
	err = ph.p.Stop(ph.id)
	return
}
<|endoftext|>