<commit_before>package camera

import (
	"fmt"
	vlc "github.com/jteeuwen/go-vlc"
	"os"
	"time"
)

const uri = "rtsp://192.168.1.128:554/12"

var currentPanDirection CameraDirection
var lastDirectionChange time.Time
var cameraIsMoving = false
var escapeSequence = make([]CameraDirection, 4)
var keepPlaying = true

func NewViewer() {
	var inst *vlc.Instance
	var player *vlc.Player
	var evt *vlc.EventManager
	var err error

	if inst, err = vlc.New([]string{"-v"}); err != nil {
		fmt.Fprintf(os.Stderr, "[e] New(): %v", err)
		return
	}
	defer inst.Release()

	if player, err = loadMedia(uri, inst); err != nil {
		fmt.Fprintf(os.Stderr, "[e] Player(): %v", err)
		return
	}
	defer player.Release()

	if evt, err = player.Events(); err != nil {
		fmt.Fprintf(os.Stderr, "[e] Events(): %v", err)
		return
	}

	evt.Attach(vlc.MediaPlayerPositionChanged, onPositionChange, player)

	player.SetMouseInput(false)
	player.SetKeyInput(false)
	player.Play()
	//player.ToggleFullscreen()

	for keepPlaying {
		time.Sleep(1e8)
	}

	player.Stop()
}

func MoveCamera(direction CameraDirection) {
	cameraIsMoving = true
	fmt.Printf("[*] Moving camera to %v\n", direction)
	Move(direction)
}

func StopCamera() {
	cameraIsMoving = false
	fmt.Printf("[*] Stopping camera\n")
	Move(Stop)
}

func loadMedia(uri string, inst *vlc.Instance) (*vlc.Player, error) {
	var media *vlc.Media
	var player *vlc.Player
	var err error

	if media, err = inst.OpenMediaUri(uri); err != nil {
		fmt.Fprintf(os.Stderr, "[e] OpenMediaUri(): %v", err)
		return nil, err
	}

	if player, err = media.NewPlayer(); err != nil {
		fmt.Fprintf(os.Stderr, "[e] NewPlayer(): %v", err)
		media.Release()
		return nil, err
	}

	media.Release()
	media = nil

	return player, nil
}

func onPositionChange(evt *vlc.Event, data interface{}) {
	newDirection := cursorDirection(data.(*vlc.Player))

	if newDirection != currentPanDirection {
		currentPanDirection = newDirection
		lastDirectionChange = time.Now()
		addToEscapeSequence(currentPanDirection)

		if currentPanDirection != Center {
			MoveCamera(currentPanDirection)
		} else {
			StopCamera()
		}
	}

	if cameraIsMoving && time.Since(lastDirectionChange) > time.Duration(StepTime)*time.Second {
		StopCamera()
	}

	if isEscapeSequence() {
		keepPlaying = false
		StopCamera()
	}
}

func isEscapeSequence() bool {
	return escapeSequence[0] == UpLeft && escapeSequence[1] == UpRight && escapeSequence[2] == DownRight && escapeSequence[3] == DownLeft
}

func addToEscapeSequence(direction CameraDirection) {
	escapeSequence = append(escapeSequence, direction)[1:5]
}

func cursorDirection(player *vlc.Player) CameraDirection {
	width, height, _ := player.Size(0)
	limR, limL, limT, limB := calculateLimits(float64(width), float64(height), 0.15)

	x, y, _ := player.Cursor(0)
	panDirection := cursorPosition(limR, limL, limT, limB, float64(x), float64(y))
	return panDirection
}

func cursorPosition(limR, limL, limT, limB float64, x, y float64) CameraDirection {
	switch {
	case x <= limL && y <= limT:
		return UpLeft
	case x <= limL && y >= limB:
		return DownLeft
	case x >= limR && y <= limT:
		return UpRight
	case x >= limR && y >= limB:
		return DownRight
	case x <= limL:
		return Left
	case x >= limR:
		return Right
	case y <= limT:
		return Up
	case y >= limB:
		return Down
	}
	return Center
}

func calculateLimits(width, height, percent float64) (float64, float64, float64, float64) {
	limR := width / (1.0 + percent)
	limL := width * percent
	limT := height * percent
	limB := height / (1.0 + percent)

	return limR, limL, limT, limB
}
<commit_msg>Move RTSP Uri to const file<commit_after>package camera

import (
	"fmt"
	vlc "github.com/jteeuwen/go-vlc"
	"os"
	"time"
)

var currentPanDirection CameraDirection
var lastDirectionChange time.Time
var cameraIsMoving = false
var escapeSequence = make([]CameraDirection, 4)
var keepPlaying = true

func NewViewer() {
	var inst *vlc.Instance
	var player *vlc.Player
	var evt *vlc.EventManager
	var err error

	if inst, err = vlc.New([]string{"-v"}); err != nil {
		fmt.Fprintf(os.Stderr, "[e] New(): %v", err)
		return
	}
	defer inst.Release()

	if player, err = loadMedia(RtspStreamUri, inst); err != nil {
		fmt.Fprintf(os.Stderr, "[e] Player(): %v", err)
		return
	}
	defer player.Release()

	if evt, err = player.Events(); err != nil {
		fmt.Fprintf(os.Stderr, "[e] Events(): %v", err)
		return
	}

	evt.Attach(vlc.MediaPlayerPositionChanged, onPositionChange, player)

	player.SetMouseInput(false)
	player.SetKeyInput(false)
	player.Play()
	//player.ToggleFullscreen()

	for keepPlaying {
		time.Sleep(1e8)
	}

	player.Stop()
}

func MoveCamera(direction CameraDirection) {
	cameraIsMoving = true
	fmt.Printf("[*] Moving camera to %v\n", direction)
	Move(direction)
}

func StopCamera() {
	cameraIsMoving = false
	fmt.Printf("[*] Stopping camera\n")
	Move(Stop)
}

func loadMedia(uri string, inst *vlc.Instance) (*vlc.Player, error) {
	var media *vlc.Media
	var player *vlc.Player
	var err error

	if media, err = inst.OpenMediaUri(uri); err != nil {
		fmt.Fprintf(os.Stderr, "[e] OpenMediaUri(): %v", err)
		return nil, err
	}

	if player, err = media.NewPlayer(); err != nil {
		fmt.Fprintf(os.Stderr, "[e] NewPlayer(): %v", err)
		media.Release()
		return nil, err
	}

	media.Release()
	media = nil

	return player, nil
}

func onPositionChange(evt *vlc.Event, data interface{}) {
	newDirection := cursorDirection(data.(*vlc.Player))

	if newDirection != currentPanDirection {
		currentPanDirection = newDirection
		lastDirectionChange = time.Now()
		addToEscapeSequence(currentPanDirection)

		if currentPanDirection != Center {
			MoveCamera(currentPanDirection)
		} else {
			StopCamera()
		}
	}

	if cameraIsMoving && time.Since(lastDirectionChange) > time.Duration(StepTime)*time.Second {
		StopCamera()
	}

	if isEscapeSequence() {
		keepPlaying = false
		StopCamera()
	}
}

func isEscapeSequence() bool {
	return escapeSequence[0] == UpLeft && escapeSequence[1] == UpRight && escapeSequence[2] == DownRight && escapeSequence[3] == DownLeft
}

func addToEscapeSequence(direction CameraDirection) {
	escapeSequence = append(escapeSequence, direction)[1:5]
}

func cursorDirection(player *vlc.Player) CameraDirection {
	width, height, _ := player.Size(0)
	limR, limL, limT, limB := calculateLimits(float64(width), float64(height), 0.15)

	x, y, _ := player.Cursor(0)
	panDirection := cursorPosition(limR, limL, limT, limB, float64(x), float64(y))
	return panDirection
}

func cursorPosition(limR, limL, limT, limB float64, x, y float64) CameraDirection {
	switch {
	case x <= limL && y <= limT:
		return UpLeft
	case x <= limL && y >= limB:
		return DownLeft
	case x >= limR && y <= limT:
		return UpRight
	case x >= limR && y >= limB:
		return DownRight
	case x <= limL:
		return Left
	case x >= limR:
		return Right
	case y <= limT:
		return Up
	case y >= limB:
		return Down
	}
	return Center
}

func calculateLimits(width, height, percent float64) (float64, float64, float64, float64) {
	limR := width / (1.0 + percent)
	limL := width * percent
	limT := height * percent
	limB := height / (1.0 + percent)

	return limR, limL, limT, limB
}
<|endoftext|>


<commit_before>package pipeline

import (
	"fmt"
	"strconv"
	"strings"
	"time"

	"github.com/colinrgodsey/step-daemon/gcode"
	"github.com/colinrgodsey/step-daemon/io"
	"github.com/colinrgodsey/step-daemon/physics"
	"github.com/colinrgodsey/step-daemon/vec"
)

const syncTimeout = 20 // seconds

type deltaHandler struct {
	head, tail io.Conn
	syncC      chan vec.Vec4

	pos         vec.Vec4
	fr, frScale float64
	abs         bool
}

func (h *deltaHandler) headRead(msg io.Any) {
	switch msg := msg.(type) {
	case gcode.GCode:
		switch {
		case msg.IsG(0), msg.IsG(1): // move
			h.procGMove(msg)
			return
		case msg.IsG(28): // home
			defer h.headRead(gcode.New('G', 114)) // get pos after
		case msg.IsG(29): // z probe
			defer h.headRead(gcode.New('G', 28)) // home after
		case msg.IsG(90): // set absolute
			h.info("setting to absolute coords")
			h.abs = true
		case msg.IsG(91): // set relative
			h.info("setting to relative coords")
			h.abs = false
		case msg.IsG(92): // set pos
			h.pos = msg.Args.GetVec4(h.pos)
		case msg.IsM(114): // get pos
			h.syncC = make(chan vec.Vec4)
			defer h.getPos()
		case msg.IsM(220): // set feedrate
			if x, ok := msg.Args.GetFloat('S'); ok {
				h.frScale = x / 100.0
				h.info("setting feedrate scale to %v", h.frScale)
			}
		}
	}
	h.tail.Write(msg)
}

func (h *deltaHandler) tailRead(msg io.Any) {
	bailParse := func(err error) {
		if err != nil {
			panic("Failed to parse string: " + err.Error())
		}
	}

	switch msg := msg.(type) {
	case string:
		switch {
		case strings.Index(msg, "X:") == 0 && strings.Index(msg, " Count ") > 0:
			// X:0.00 Y:0.00 Z:10.00 E:0.00 Count X:0 Y:0 Z:16000
			spl := strings.Split(msg, " ")
			x, err := strconv.ParseFloat(string(spl[0][2:]), 64)
			bailParse(err)
			y, err := strconv.ParseFloat(string(spl[1][2:]), 64)
			bailParse(err)
			z, err := strconv.ParseFloat(string(spl[2][2:]), 64)
			bailParse(err)
			e, err := strconv.ParseFloat(string(spl[3][2:]), 64)
			bailParse(err)

			//TODO: still not happy about this pattern
			if h.syncC != nil {
				h.info("syncd with device position")
				h.syncC <- vec.NewVec4(x, y, z, e)
			}
		}
	}
	h.head.Write(msg)
}

//TODO: i hate this, replace this later
func (h *deltaHandler) getPos() {
	h.info("syncing with device position")
	select {
	case pos := <-h.syncC:
		h.pos = pos
	case <-time.After(syncTimeout * time.Second):
		panic("timed out while syncing position")
	}
	h.syncC = nil
	h.headRead(gcode.New('G', 92, gcode.ArgV(h.pos)...))
}

func (h *deltaHandler) procGMove(g gcode.GCode) {
	var newPos vec.Vec4
	if h.abs {
		newPos = g.Args.GetVec4(h.pos)
	} else {
		newPos = g.Args.GetVec4(vec.Vec4{}).Add(h.pos)
	}
	if f, ok := g.Args.GetFloat('F'); ok {
		h.fr = f * h.frScale / 60.0
	}
	if newPos.Eq(h.pos) {
		return
	}

	m := physics.NewMove(h.pos, newPos, h.fr)
	h.pos = newPos
	if h.fr != 0 {
		h.tail.Write(m)
	} else {
		h.info("skipped move with 0 feedrate")
	}
}

func (h *deltaHandler) info(s string, args ...interface{}) {
	h.head.Write(fmt.Sprintf("info:"+s, args...))
}

func DeltaHandler(head, tail io.Conn) {
	h := deltaHandler{
		head: head, tail: tail,
		frScale: 1.0,
	}

	go func() {
		for msg := range tail.Rc() {
			h.tailRead(msg)
		}
	}()

	for msg := range head.Rc() {
		h.headRead(msg)
	}
}
<commit_msg>sync fix<commit_after>package pipeline

import (
	"fmt"
	"strconv"
	"strings"
	"time"

	"github.com/colinrgodsey/step-daemon/gcode"
	"github.com/colinrgodsey/step-daemon/io"
	"github.com/colinrgodsey/step-daemon/physics"
	"github.com/colinrgodsey/step-daemon/vec"
)

const syncTimeout = 20 // seconds

type deltaHandler struct {
	head, tail io.Conn
	syncC      chan vec.Vec4

	pos         vec.Vec4
	fr, frScale float64
	abs         bool
}

func (h *deltaHandler) headRead(msg io.Any) {
	switch msg := msg.(type) {
	case gcode.GCode:
		switch {
		case msg.IsG(0), msg.IsG(1): // move
			h.procGMove(msg)
			return
		case msg.IsG(28): // home
			defer h.headRead(gcode.New('M', 114)) // get pos after
		case msg.IsG(29): // z probe
			defer h.headRead(gcode.New('G', 28)) // home after
		case msg.IsG(90): // set absolute
			h.info("setting to absolute coords")
			h.abs = true
		case msg.IsG(91): // set relative
			h.info("setting to relative coords")
			h.abs = false
		case msg.IsG(92): // set pos
			h.pos = msg.Args.GetVec4(h.pos)
		case msg.IsM(114): // get pos
			h.syncC = make(chan vec.Vec4)
			defer h.getPos()
		case msg.IsM(220): // set feedrate
			if x, ok := msg.Args.GetFloat('S'); ok {
				h.frScale = x / 100.0
				h.info("setting feedrate scale to %v", h.frScale)
			}
		}
	}
	h.tail.Write(msg)
}

func (h *deltaHandler) tailRead(msg io.Any) {
	bailParse := func(err error) {
		if err != nil {
			panic("Failed to parse string: " + err.Error())
		}
	}

	switch msg := msg.(type) {
	case string:
		switch {
		case strings.Index(msg, "X:") == 0 && strings.Index(msg, " Count ") > 0:
			// X:0.00 Y:0.00 Z:10.00 E:0.00 Count X:0 Y:0 Z:16000
			spl := strings.Split(msg, " ")
			x, err := strconv.ParseFloat(string(spl[0][2:]), 64)
			bailParse(err)
			y, err := strconv.ParseFloat(string(spl[1][2:]), 64)
			bailParse(err)
			z, err := strconv.ParseFloat(string(spl[2][2:]), 64)
			bailParse(err)
			e, err := strconv.ParseFloat(string(spl[3][2:]), 64)
			bailParse(err)

			//TODO: still not happy about this pattern
			if h.syncC != nil {
				h.info("syncd with device position")
				h.syncC <- vec.NewVec4(x, y, z, e)
			}
		}
	}
	h.head.Write(msg)
}

//TODO: i hate this, replace this later
func (h *deltaHandler) getPos() {
	h.info("syncing with device position")
	select {
	case pos := <-h.syncC:
		h.pos = pos
	case <-time.After(syncTimeout * time.Second):
		panic("timed out while syncing position")
	}
	h.syncC = nil
	h.headRead(gcode.New('G', 92, gcode.ArgV(h.pos)...))
}

func (h *deltaHandler) procGMove(g gcode.GCode) {
	var newPos vec.Vec4
	if h.abs {
		newPos = g.Args.GetVec4(h.pos)
	} else {
		newPos = g.Args.GetVec4(vec.Vec4{}).Add(h.pos)
	}
	if f, ok := g.Args.GetFloat('F'); ok {
		h.fr = f * h.frScale / 60.0
	}
	if newPos.Eq(h.pos) {
		return
	}

	m := physics.NewMove(h.pos, newPos, h.fr)
	h.pos = newPos
	if h.fr != 0 {
		h.tail.Write(m)
	} else {
		h.info("skipped move with 0 feedrate")
	}
}

func (h *deltaHandler) info(s string, args ...interface{}) {
	h.head.Write(fmt.Sprintf("info:"+s, args...))
}

func DeltaHandler(head, tail io.Conn) {
	h := deltaHandler{
		head: head, tail: tail,
		frScale: 1.0,
	}

	go func() {
		for msg := range tail.Rc() {
			h.tailRead(msg)
		}
	}()

	for msg := range head.Rc() {
		h.headRead(msg)
	}
}
<|endoftext|>


<commit_before>// Package docker provides a Pipe that creates and pushes a Docker image
package docker

import (
	"bytes"
	"fmt"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
	"text/template"

	"github.com/apex/log"
	"github.com/apex/log/handlers/cli"
	"github.com/fatih/color"
	"github.com/masterminds/semver"
	"github.com/pkg/errors"
	"golang.org/x/sync/errgroup"

	"github.com/goreleaser/goreleaser/config"
	"github.com/goreleaser/goreleaser/context"
	"github.com/goreleaser/goreleaser/internal/artifact"
	"github.com/goreleaser/goreleaser/pipeline"
)

// ErrNoDocker is shown when docker cannot be found in $PATH
var ErrNoDocker = errors.New("docker not present in $PATH")

// Pipe for docker
type Pipe struct{}

func (Pipe) String() string {
	return "creating Docker images"
}

func deprecateWarn(format string, a ...interface{}) {
	cli.Default.Padding *= 2
	defer func() {
		cli.Default.Padding /= 2
	}()
	log.Warn(color.New(color.Bold, color.FgHiYellow).Sprintf(format, a...))
}

// Default sets the pipe defaults
func (Pipe) Default(ctx *context.Context) error {
	for i := range ctx.Config.Dockers {
		var docker = &ctx.Config.Dockers[i]
		if docker.OldTagTemplate != "" {
			deprecateWarn("`dockers[%d].tag_template` is deprecated. Please consider using `dockers[%d].tag_templates` instead", i, i)
			docker.TagTemplates = append(docker.TagTemplates, docker.OldTagTemplate)
		}
		if len(docker.TagTemplates) == 0 {
			docker.TagTemplates = append(docker.TagTemplates, "{{ .Version }}")
		}
		if docker.Goos == "" {
			docker.Goos = "linux"
		}
		if docker.Goarch == "" {
			docker.Goarch = "amd64"
		}
		if docker.Latest {
			deprecateWarn("`dockers[%d].latest` is deprecated. Please consider adding a `latest` tag to the `dockers[%d].tag_templates` list instead", i, i)
			docker.TagTemplates = append(docker.TagTemplates, "latest")
		}
	}
	// only set defaults if there is exacly 1 docker setup in the config file.
	if len(ctx.Config.Dockers) != 1 {
		return nil
	}
	if ctx.Config.Dockers[0].Binary == "" {
		ctx.Config.Dockers[0].Binary = ctx.Config.Builds[0].Binary
	}
	if ctx.Config.Dockers[0].Dockerfile == "" {
		ctx.Config.Dockers[0].Dockerfile = "Dockerfile"
	}
	return nil
}

// Run the pipe
func (Pipe) Run(ctx *context.Context) error {
	if len(ctx.Config.Dockers) == 0 || ctx.Config.Dockers[0].Image == "" {
		return pipeline.Skip("docker section is not configured")
	}
	_, err := exec.LookPath("docker")
	if err != nil {
		return ErrNoDocker
	}
	return doRun(ctx)
}

func doRun(ctx *context.Context) error {
	var g errgroup.Group
	sem := make(chan bool, ctx.Parallelism)
	for _, docker := range ctx.Config.Dockers {
		docker := docker
		sem <- true
		g.Go(func() error {
			defer func() {
				<-sem
			}()
			log.WithField("docker", docker).Debug("looking for binaries matching")
			var binaries = ctx.Artifacts.Filter(
				artifact.And(
					artifact.ByGoos(docker.Goos),
					artifact.ByGoarch(docker.Goarch),
					artifact.ByGoarm(docker.Goarm),
					artifact.ByType(artifact.Binary),
					func(a artifact.Artifact) bool {
						return a.Extra["Binary"] == docker.Binary
					},
				),
			).List()
			if len(binaries) == 0 {
				log.Warnf("no binaries found for %s", docker.Binary)
			}
			for _, binary := range binaries {
				if err := process(ctx, docker, binary); err != nil {
					return err
				}
			}
			return nil
		})
	}
	return g.Wait()
}

func tagName(ctx *context.Context, tagTemplate string) (string, error) {
	var out bytes.Buffer
	t, err := template.New("tag").Option("missingkey=error").Parse(tagTemplate)
	if err != nil {
		return "", err
	}
	sv, err := semver.NewVersion(ctx.Git.CurrentTag)
	if err != nil {
		return "", err
	}
	data := struct {
		Version, Tag        string
		Major, Minor, Patch int64
		Env                 map[string]string
	}{
		Version: ctx.Version,
		Tag:     ctx.Git.CurrentTag,
		Env:     ctx.Env,
		Major:   sv.Major(),
		Minor:   sv.Minor(),
		Patch:   sv.Patch(),
	}
	err = t.Execute(&out, data)
	return out.String(), err
}

func process(ctx *context.Context, docker config.Docker, artifact artifact.Artifact) error {
	var root = filepath.Dir(artifact.Path)
	var dockerfile = filepath.Join(root, filepath.Base(docker.Dockerfile))
	var images []string
	for _, tagTemplate := range docker.TagTemplates {
		tag, err := tagName(ctx, tagTemplate)
		if err != nil {
			return errors.Wrapf(err, "failed to execute tag template '%s'", tagTemplate)
		}
		images = append(images, fmt.Sprintf("%s:%s", docker.Image, tag))
	}
	if err := os.Link(docker.Dockerfile, dockerfile); err != nil {
		return errors.Wrap(err, "failed to link dockerfile")
	}
	for _, file := range docker.Files {
		if err := link(file, filepath.Join(root, filepath.Base(file))); err != nil {
			return errors.Wrapf(err, "failed to link extra file '%s'", file)
		}
	}
	if err := dockerBuild(ctx, root, dockerfile, images[0]); err != nil {
		return err
	}
	for _, img := range images[1:] {
		if err := dockerTag(ctx, images[0], img); err != nil {
			return err
		}
	}
	return publish(ctx, docker, images)
}

// walks the src, recreating dirs and hard-linking files
func link(src, dest string) error {
	return filepath.Walk(src, func(path string, info os.FileInfo, err error) error {
		if err != nil {
			return err
		}
		// We have the following:
		// - src = "a/b"
		// - dest = "dist/linuxamd64/b"
		// - path = "a/b/c.txt"
		// So we join "a/b" with "c.txt" and use it as the destination.
		var dst = filepath.Join(dest, strings.Replace(path, src, "", 1))
		log.WithFields(log.Fields{
			"src": path,
			"dst": dst,
		}).Info("extra file")
		if info.IsDir() {
			return os.MkdirAll(dst, info.Mode())
		}
		return os.Link(path, dst)
	})
}

func publish(ctx *context.Context, docker config.Docker, images []string) error {
	if !ctx.Publish {
		log.Warn("skipping push because --skip-publish is set")
		return nil
	}
	for _, image := range images {
		if err := dockerPush(ctx, docker, image); err != nil {
			return err
		}
	}
	return nil
}

func dockerBuild(ctx *context.Context, root, dockerfile, image string) error {
	log.WithField("image", image).Info("building docker image")
	/* #nosec */
	var cmd = exec.CommandContext(ctx, "docker", "build", "-f", dockerfile, "-t", image, root)
	log.WithField("cmd", cmd.Args).Debug("executing docker")
	out, err := cmd.CombinedOutput()
	if err != nil {
		return errors.Wrapf(err, "failed to build docker image: \n%s", string(out))
	}
	log.Debugf("docker build output: \n%s", string(out))
	return nil
}

func dockerTag(ctx *context.Context, image, tag string) error {
	log.WithField("image", image).WithField("tag", tag).Info("tagging docker image")
	/* #nosec */
	var cmd = exec.CommandContext(ctx, "docker", "tag", image, tag)
	log.WithField("cmd", cmd.Args).Debug("executing docker")
	out, err := cmd.CombinedOutput()
	if err != nil {
		return errors.Wrapf(err, "failed to tag docker image: \n%s", string(out))
	}
	log.Debugf("docker tag output: \n%s", string(out))
	return nil
}

func dockerPush(ctx *context.Context, docker config.Docker, image string) error {
	log.WithField("image", image).Info("pushing docker image")
	/* #nosec */
	var cmd = exec.CommandContext(ctx, "docker", "push", image)
	log.WithField("cmd", cmd.Args).Debug("executing docker")
	out, err := cmd.CombinedOutput()
	if err != nil {
		return errors.Wrapf(err, "failed to push docker image: \n%s", string(out))
	}
	log.Debugf("docker push output: \n%s", string(out))
	ctx.Artifacts.Add(artifact.Artifact{
		Type:   artifact.DockerImage,
		Name:   image,
		Path:   image,
		Goarch: docker.Goarch,
		Goos:   docker.Goos,
		Goarm:  docker.Goarm,
	})
	return nil
}
<commit_msg>fix: keeping the same naming as other pipes<commit_after>// Package docker provides a Pipe that creates and pushes a Docker image
package docker

import (
	"bytes"
	"fmt"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
	"text/template"

	"github.com/apex/log"
	"github.com/apex/log/handlers/cli"
	"github.com/fatih/color"
	"github.com/masterminds/semver"
	"github.com/pkg/errors"
	"golang.org/x/sync/errgroup"

	"github.com/goreleaser/goreleaser/config"
	"github.com/goreleaser/goreleaser/context"
	"github.com/goreleaser/goreleaser/internal/artifact"
	"github.com/goreleaser/goreleaser/pipeline"
)

// ErrNoDocker is shown when docker cannot be found in $PATH
var ErrNoDocker = errors.New("docker not present in $PATH")

// Pipe for docker
type Pipe struct{}

func (Pipe) String() string {
	return "creating Docker images"
}

func deprecateWarn(format string, a ...interface{}) {
	cli.Default.Padding *= 2
	defer func() {
		cli.Default.Padding /= 2
	}()
	log.Warn(color.New(color.Bold, color.FgHiYellow).Sprintf(format, a...))
}

// Default sets the pipe defaults
func (Pipe) Default(ctx *context.Context) error {
	for i := range ctx.Config.Dockers {
		var docker = &ctx.Config.Dockers[i]
		if docker.OldTagTemplate != "" {
			deprecateWarn("`dockers[%d].tag_template` is deprecated. Please consider using `dockers[%d].tag_templates` instead", i, i)
			docker.TagTemplates = append(docker.TagTemplates, docker.OldTagTemplate)
		}
		if len(docker.TagTemplates) == 0 {
			docker.TagTemplates = append(docker.TagTemplates, "{{ .Version }}")
		}
		if docker.Goos == "" {
			docker.Goos = "linux"
		}
		if docker.Goarch == "" {
			docker.Goarch = "amd64"
		}
		if docker.Latest {
			deprecateWarn("`dockers[%d].latest` is deprecated. Please consider adding a `latest` tag to the `dockers[%d].tag_templates` list instead", i, i)
			docker.TagTemplates = append(docker.TagTemplates, "latest")
		}
	}
	// only set defaults if there is exacly 1 docker setup in the config file.
	if len(ctx.Config.Dockers) != 1 {
		return nil
	}
	if ctx.Config.Dockers[0].Binary == "" {
		ctx.Config.Dockers[0].Binary = ctx.Config.Builds[0].Binary
	}
	if ctx.Config.Dockers[0].Dockerfile == "" {
		ctx.Config.Dockers[0].Dockerfile = "Dockerfile"
	}
	return nil
}

// Run the pipe
func (Pipe) Run(ctx *context.Context) error {
	if len(ctx.Config.Dockers) == 0 || ctx.Config.Dockers[0].Image == "" {
		return pipeline.Skip("docker section is not configured")
	}
	_, err := exec.LookPath("docker")
	if err != nil {
		return ErrNoDocker
	}
	return doRun(ctx)
}

func doRun(ctx *context.Context) error {
	var g errgroup.Group
	sem := make(chan bool, ctx.Parallelism)
	for _, docker := range ctx.Config.Dockers {
		docker := docker
		sem <- true
		g.Go(func() error {
			defer func() {
				<-sem
			}()
			log.WithField("docker", docker).Debug("looking for binaries matching")
			var binaries = ctx.Artifacts.Filter(
				artifact.And(
					artifact.ByGoos(docker.Goos),
					artifact.ByGoarch(docker.Goarch),
					artifact.ByGoarm(docker.Goarm),
					artifact.ByType(artifact.Binary),
					func(a artifact.Artifact) bool {
						return a.Extra["Binary"] == docker.Binary
					},
				),
			).List()
			if len(binaries) == 0 {
				log.Warnf("no binaries found for %s", docker.Binary)
			}
			for _, binary := range binaries {
				if err := process(ctx, docker, binary); err != nil {
					return err
				}
			}
			return nil
		})
	}
	return g.Wait()
}

func tagName(ctx *context.Context, tagTemplate string) (string, error) {
	var out bytes.Buffer
	t, err := template.New("tag").Option("missingkey=error").Parse(tagTemplate)
	if err != nil {
		return "", err
	}
	sv, err := semver.NewVersion(ctx.Git.CurrentTag)
	if err != nil {
		return "", err
	}
	data := struct {
		Version, Tag        string
		Major, Minor, Patch int64
		Env                 map[string]string
	}{
		Version: ctx.Version,
		Tag:     ctx.Git.CurrentTag,
		Env:     ctx.Env,
		Major:   sv.Major(),
		Minor:   sv.Minor(),
		Patch:   sv.Patch(),
	}
	err = t.Execute(&out, data)
	return out.String(), err
}

func process(ctx *context.Context, docker config.Docker, artifact artifact.Artifact) error {
	var root = filepath.Dir(artifact.Path)
	var dockerfile = filepath.Join(root, filepath.Base(docker.Dockerfile))
	var images []string
	for _, tagTemplate := range docker.TagTemplates {
		tag, err := tagName(ctx, tagTemplate)
		if err != nil {
			return errors.Wrapf(err, "failed to execute tag template '%s'", tagTemplate)
		}
		images = append(images, fmt.Sprintf("%s:%s", docker.Image, tag))
	}
	if err := os.Link(docker.Dockerfile, dockerfile); err != nil {
		return errors.Wrap(err, "failed to link dockerfile")
	}
	for _, file := range docker.Files {
		if err := link(file, filepath.Join(root, filepath.Base(file))); err != nil {
			return errors.Wrapf(err, "failed to link extra file '%s'", file)
		}
	}
	if err := dockerBuild(ctx, root, dockerfile, images[0]); err != nil {
		return err
	}
	for _, img := range images[1:] {
		if err := dockerTag(ctx, images[0], img); err != nil {
			return err
		}
	}
	return publish(ctx, docker, images)
}

// walks the src, recreating dirs and hard-linking files
func link(src, dest string) error {
	return filepath.Walk(src, func(path string, info os.FileInfo, err error) error {
		if err != nil {
			return err
		}
		// We have the following:
		// - src = "a/b"
		// - dest = "dist/linuxamd64/b"
		// - path = "a/b/c.txt"
		// So we join "a/b" with "c.txt" and use it as the destination.
		var dst = filepath.Join(dest, strings.Replace(path, src, "", 1))
		log.WithFields(log.Fields{
			"src": path,
			"dst": dst,
		}).Info("extra file")
		if info.IsDir() {
			return os.MkdirAll(dst, info.Mode())
		}
		return os.Link(path, dst)
	})
}

func publish(ctx *context.Context, docker config.Docker, images []string) error {
	if !ctx.Publish {
		log.Warn("skipping push because --skip-publish is set")
		return nil
	}
	for _, image := range images {
		if err := dockerPush(ctx, docker, image); err != nil {
			return err
		}
	}
	return nil
}

func dockerBuild(ctx *context.Context, root, dockerfile, image string) error {
	log.WithField("image", image).Info("building docker image")
	/* #nosec */
	var cmd = exec.CommandContext(ctx, "docker", "build", "-f", dockerfile, "-t", image, root)
	log.WithField("cmd", cmd.Args).Debug("running")
	out, err := cmd.CombinedOutput()
	if err != nil {
		return errors.Wrapf(err, "failed to build docker image: \n%s", string(out))
	}
	log.Debugf("docker build output: \n%s", string(out))
	return nil
}

func dockerTag(ctx *context.Context, image, tag string) error {
	log.WithField("image", image).WithField("tag", tag).Info("tagging docker image")
	/* #nosec */
	var cmd = exec.CommandContext(ctx, "docker", "tag", image, tag)
	log.WithField("cmd", cmd.Args).Debug("running")
	out, err := cmd.CombinedOutput()
	if err != nil {
		return errors.Wrapf(err, "failed to tag docker image: \n%s", string(out))
	}
	log.Debugf("docker tag output: \n%s", string(out))
	return nil
}

func dockerPush(ctx *context.Context, docker config.Docker, image string) error {
	log.WithField("image", image).Info("pushing docker image")
	/* #nosec */
	var cmd = exec.CommandContext(ctx, "docker", "push", image)
	log.WithField("cmd", cmd.Args).Debug("running")
	out, err := cmd.CombinedOutput()
	if err != nil {
		return errors.Wrapf(err, "failed to push docker image: \n%s", string(out))
	}
	log.Debugf("docker push output: \n%s", string(out))
	ctx.Artifacts.Add(artifact.Artifact{
		Type:   artifact.DockerImage,
		Name:   image,
		Path:   image,
		Goarch: docker.Goarch,
		Goos:   docker.Goos,
		Goarm:  docker.Goarm,
	})
	return nil
}
<|endoftext|>


<commit_before>// Copyright 2020 syzkaller project authors. All rights reserved.
// Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file.

package main

import (
	"bufio"
	"encoding/binary"
	"fmt"
	"os"
	"path/filepath"
	"regexp"
	"sort"
	"strconv"

	"github.com/google/syzkaller/pkg/cover/backend"
	"github.com/google/syzkaller/pkg/log"
	"github.com/google/syzkaller/pkg/mgrconfig"
	"github.com/google/syzkaller/pkg/osutil"
	"github.com/google/syzkaller/sys/targets"
)

func createCoverageFilter(cfg *mgrconfig.Config) (string, map[uint32]uint32, error) {
	if len(cfg.CovFilter.Functions)+len(cfg.CovFilter.Files)+len(cfg.CovFilter.RawPCs) == 0 {
		return "", nil, nil
	}
	// Always initialize ReportGenerator because RPCServer.NewInput will need it to filter coverage.
	rg, err := getReportGenerator(cfg)
	if err != nil {
		return "", nil, err
	}
	pcs := make(map[uint32]uint32)
	foreachSymbol := func(apply func(*backend.ObjectUnit)) {
		for _, sym := range rg.Symbols {
			apply(&sym.ObjectUnit)
		}
	}
	if err := covFilterAddFilter(pcs, cfg.CovFilter.Functions, foreachSymbol); err != nil {
		return "", nil, err
	}
	foreachUnit := func(apply func(*backend.ObjectUnit)) {
		for _, unit := range rg.Units {
			apply(&unit.ObjectUnit)
		}
	}
	if err := covFilterAddFilter(pcs, cfg.CovFilter.Files, foreachUnit); err != nil {
		return "", nil, err
	}
	if err := covFilterAddRawPCs(pcs, cfg.CovFilter.RawPCs); err != nil {
		return "", nil, err
	}
	if len(pcs) == 0 {
		return "", nil, nil
	}
	if !cfg.SysTarget.ExecutorUsesShmem {
		return "", nil, fmt.Errorf("coverage filter is only supported for targets that use shmem")
	}
	bitmap := createCoverageBitmap(cfg.SysTarget, pcs)
	filename := filepath.Join(cfg.Workdir, "syz-cover-bitmap")
	if err := osutil.WriteFile(filename, bitmap); err != nil {
		return "", nil, err
	}
	return filename, pcs, nil
}

func covFilterAddFilter(pcs map[uint32]uint32, filters []string, foreach func(func(*backend.ObjectUnit))) error {
	res, err := compileRegexps(filters)
	if err != nil {
		return err
	}
	used := make(map[*regexp.Regexp][]string)
	foreach(func(unit *backend.ObjectUnit) {
		for _, re := range res {
			if re.MatchString(unit.Name) {
				// We add both coverage points and comparison interception points
				// because executor filters comparisons as well.
				for _, pc := range unit.PCs {
					pcs[uint32(pc)] = 1
				}
				for _, pc := range unit.CMPs {
					pcs[uint32(pc)] = 1
				}
				used[re] = append(used[re], unit.Name)
				break
			}
		}
	})
	for _, re := range res {
		sort.Strings(used[re])
		log.Logf(0, "coverage filter: %v: %v", re, used[re])
	}
	if len(res) != len(used) {
		return fmt.Errorf("some filters don't match anything")
	}
	return nil
}

func covFilterAddRawPCs(pcs map[uint32]uint32, rawPCsFiles []string) error {
	re := regexp.MustCompile(`(0x[0-9a-f]+)(?:: (0x[0-9a-f]+))?`)
	for _, f := range rawPCsFiles {
		rawFile, err := os.Open(f)
		if err != nil {
			return fmt.Errorf("failed to open raw PCs file: %v", err)
		}
		defer rawFile.Close()
		s := bufio.NewScanner(rawFile)
		for s.Scan() {
			match := re.FindStringSubmatch(s.Text())
			if match == nil {
				return fmt.Errorf("bad line: %q", s.Text())
			}
			pc, err := strconv.ParseUint(match[1], 0, 64)
			if err != nil {
				return err
			}
			weight, err := strconv.ParseUint(match[2], 0, 32)
			if match[2] != "" && err != nil {
				return err
			}
			// If no weight is detected, set the weight to 0x1 by default.
			if match[2] == "" || weight < 1 {
				weight = 1
			}
			pcs[uint32(pc)] = uint32(weight)
		}
		if err := s.Err(); err != nil {
			return err
		}
	}
	return nil
}

func createCoverageBitmap(target *targets.Target, pcs map[uint32]uint32) []byte {
	start, size := coverageFilterRegion(pcs)
	log.Logf(0, "coverage filter from 0x%x to 0x%x, size 0x%x, pcs %v", start, start+size, size, len(pcs))
	// The file starts with two uint32: covFilterStart and covFilterSize,
	// and a bitmap with size ((covFilterSize>>4) + 7)/8 bytes follow them.
	// 8-bit = 1-byte, additional 1-byte to prevent overflow
	data := make([]byte, 8+((size>>4)+7)/8)
	order := binary.ByteOrder(binary.BigEndian)
	if target.LittleEndian {
		order = binary.LittleEndian
	}
	order.PutUint32(data, start)
	order.PutUint32(data[4:], size)

	bitmap := data[8:]
	for pc := range pcs {
		// The lowest 4-bit is dropped.
		pc = (pc - start) >> 4
		bitmap[pc/8] |= (1 << (pc % 8))
	}
	return data
}

func coverageFilterRegion(pcs map[uint32]uint32) (uint32, uint32) {
	start, end := ^uint32(0), uint32(0)
	for pc := range pcs {
		if start > pc {
			start = pc
		}
		if end < pc {
			end = pc
		}
	}
	// align
	start &= ^uint32(0xf)
	end = (end + 0xf) &^ uint32(0xf)
	return start, end - start
}

func compileRegexps(regexpStrings []string) ([]*regexp.Regexp, error) {
	var regexps []*regexp.Regexp
	for _, rs := range regexpStrings {
		r, err := regexp.Compile(rs)
		if err != nil {
			return nil, fmt.Errorf("failed to compile regexp: %v", err)
		}
		regexps = append(regexps, r)
	}
	return regexps, nil
}
<commit_msg>syz-manager: fix filtered coverage calculate<commit_after>// Copyright 2020 syzkaller project authors. All rights reserved.
// Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file.

package main

import (
	"bufio"
	"encoding/binary"
	"fmt"
	"os"
	"path/filepath"
	"regexp"
	"sort"
	"strconv"

	"github.com/google/syzkaller/pkg/cover/backend"
	"github.com/google/syzkaller/pkg/log"
	"github.com/google/syzkaller/pkg/mgrconfig"
	"github.com/google/syzkaller/pkg/osutil"
	"github.com/google/syzkaller/sys/targets"
)

func createCoverageFilter(cfg *mgrconfig.Config) (string, map[uint32]uint32, error) {
	if len(cfg.CovFilter.Functions)+len(cfg.CovFilter.Files)+len(cfg.CovFilter.RawPCs) == 0 {
		return "", nil, nil
	}
	// Always initialize ReportGenerator because RPCServer.NewInput will need it to filter coverage.
	rg, err := getReportGenerator(cfg)
	if err != nil {
		return "", nil, err
	}
	pcs := make(map[uint32]uint32)
	foreachSymbol := func(apply func(*backend.ObjectUnit)) {
		for _, sym := range rg.Symbols {
			apply(&sym.ObjectUnit)
		}
	}
	if err := covFilterAddFilter(pcs, cfg.CovFilter.Functions, foreachSymbol); err != nil {
		return "", nil, err
	}
	foreachUnit := func(apply func(*backend.ObjectUnit)) {
		for _, unit := range rg.Units {
			apply(&unit.ObjectUnit)
		}
	}
	if err := covFilterAddFilter(pcs, cfg.CovFilter.Files, foreachUnit); err != nil {
		return "", nil, err
	}
	if err := covFilterAddRawPCs(pcs, cfg.CovFilter.RawPCs); err != nil {
		return "", nil, err
	}
	if len(pcs) == 0 {
		return "", nil, nil
	}
	if !cfg.SysTarget.ExecutorUsesShmem {
		return "", nil, fmt.Errorf("coverage filter is only supported for targets that use shmem")
	}
	bitmap := createCoverageBitmap(cfg.SysTarget, pcs)
	filename := filepath.Join(cfg.Workdir, "syz-cover-bitmap")
	if err := osutil.WriteFile(filename, bitmap); err != nil {
		return "", nil, err
	}
	// After finish writing down bitmap file, for accurate filtered coverage,
	// pcs from CMPs should be deleted.
	for _, sym := range rg.Symbols {
		for _, pc := range sym.CMPs {
			delete(pcs, uint32(pc))
		}
	}
	return filename, pcs, nil
}

func covFilterAddFilter(pcs map[uint32]uint32, filters []string, foreach func(func(*backend.ObjectUnit))) error {
	res, err := compileRegexps(filters)
	if err != nil {
		return err
	}
	used := make(map[*regexp.Regexp][]string)
	foreach(func(unit *backend.ObjectUnit) {
		for _, re := range res {
			if re.MatchString(unit.Name) {
				// We add both coverage points and comparison interception points
				// because executor filters comparisons as well.
				for _, pc := range unit.PCs {
					pcs[uint32(pc)] = 1
				}
				for _, pc := range unit.CMPs {
					pcs[uint32(pc)] = 1
				}
				used[re] = append(used[re], unit.Name)
				break
			}
		}
	})
	for _, re := range res {
		sort.Strings(used[re])
		log.Logf(0, "coverage filter: %v: %v", re, used[re])
	}
	if len(res) != len(used) {
		return fmt.Errorf("some filters don't match anything")
	}
	return nil
}

func covFilterAddRawPCs(pcs map[uint32]uint32, rawPCsFiles []string) error {
	re := regexp.MustCompile(`(0x[0-9a-f]+)(?:: (0x[0-9a-f]+))?`)
	for _, f := range rawPCsFiles {
		rawFile, err := os.Open(f)
		if err != nil {
			return fmt.Errorf("failed to open raw PCs file: %v", err)
		}
		defer rawFile.Close()
		s := bufio.NewScanner(rawFile)
		for s.Scan() {
			match := re.FindStringSubmatch(s.Text())
			if match == nil {
				return fmt.Errorf("bad line: %q", s.Text())
			}
			pc, err := strconv.ParseUint(match[1], 0, 64)
			if err != nil {
				return err
			}
			weight, err := strconv.ParseUint(match[2], 0, 32)
			if match[2] != "" && err != nil {
				return err
			}
			// If no weight is detected, set the weight to 0x1 by default.
			if match[2] == "" || weight < 1 {
				weight = 1
			}
			pcs[uint32(pc)] = uint32(weight)
		}
		if err := s.Err(); err != nil {
			return err
		}
	}
	return nil
}

func createCoverageBitmap(target *targets.Target, pcs map[uint32]uint32) []byte {
	start, size := coverageFilterRegion(pcs)
	log.Logf(0, "coverage filter from 0x%x to 0x%x, size 0x%x, pcs %v", start, start+size, size, len(pcs))
	// The file starts with two uint32: covFilterStart and covFilterSize,
	// and a bitmap with size ((covFilterSize>>4) + 7)/8 bytes follow them.
	// 8-bit = 1-byte, additional 1-byte to prevent overflow
	data := make([]byte, 8+((size>>4)+7)/8)
	order := binary.ByteOrder(binary.BigEndian)
	if target.LittleEndian {
		order = binary.LittleEndian
	}
	order.PutUint32(data, start)
	order.PutUint32(data[4:], size)

	bitmap := data[8:]
	for pc := range pcs {
		// The lowest 4-bit is dropped.
		pc = (pc - start) >> 4
		bitmap[pc/8] |= (1 << (pc % 8))
	}
	return data
}

func coverageFilterRegion(pcs map[uint32]uint32) (uint32, uint32) {
	start, end := ^uint32(0), uint32(0)
	for pc := range pcs {
		if start > pc {
			start = pc
		}
		if end < pc {
			end = pc
		}
	}
	// align
	start &= ^uint32(0xf)
	end = (end + 0xf) &^ uint32(0xf)
	return start, end - start
}

func compileRegexps(regexpStrings []string) ([]*regexp.Regexp, error) {
	var regexps []*regexp.Regexp
	for _, rs := range regexpStrings {
		r, err := regexp.Compile(rs)
		if err != nil {
			return nil, fmt.Errorf("failed to compile regexp: %v", err)
		}
		regexps = append(regexps, r)
	}
	return regexps, nil
}
<|endoftext|>


<commit_before>package speed

import (
	"math"
	"testing"
)

// only tests that work on 32 bit architectures or both go here
// tests only working on 64 bit architectures go in _amd64_test.go
func TestIsCompatible(t *testing.T) {
	cases := []struct {
		t      MetricType
		v      interface{}
		result bool
	}{
		{Int32Type, -1, true},
		{Int64Type, -1, true},
		{Uint64Type, -1, false},
		{Uint32Type, -1, false},

		{Int32Type, math.MinInt32, true},
		{Int64Type, math.MinInt32, true},
		{Uint32Type, math.MinInt32, false},
		{Uint64Type, math.MinInt32, false},

		{Int32Type, int32(math.MinInt32), true},
		{Int64Type, int64(math.MinInt32), true},
		{Uint32Type, int32(math.MinInt32), false},
		{Uint64Type, int64(math.MinInt32), false},

		{Int32Type, math.MaxInt32, true},
		{Int64Type, math.MaxInt32, true},
		{Uint32Type, math.MaxInt32, true},
		{Uint64Type, math.MaxInt32, true},

		{Int32Type, int32(math.MaxInt32), true},
		{Int64Type, int64(math.MaxInt32), true},
		{Uint32Type, int32(math.MaxInt32), false},
		{Uint64Type, int64(math.MaxInt32), false},

		{Int64Type, int64(math.MaxInt64), true},

		{Uint32Type, uint32(math.MaxUint32), true},
		{Uint64Type, uint64(math.MaxUint32), true},

		{Uint32Type, uint(math.MaxUint32), true},

		{Uint32Type, math.MaxUint32, true},
		{Uint64Type, uint64(math.MaxUint64), true},
	}

	for _, c := range cases {
		r := c.t.IsCompatible(c.v)
		if r != c.result {
			t.Errorf("%v.IsCompatible(%v(%T)) should be %v, not %v", c.t, c.v, c.v, c.result, r)
		}
	}
}

func TestResolve(t *testing.T) {
	cases := []struct {
		t           MetricType
		val, resval interface{}
	}{
		{Int32Type, 10, int32(10)},
		{Int64Type, 10, int64(10)},
		{Uint32Type, 10, uint32(10)},
		{Uint64Type, 10, uint64(10)},

		{Int32Type, int32(10), int32(10)},
		{Int64Type, int64(10), int64(10)},
		{Uint32Type, uint32(10), uint32(10)},
		{Uint64Type, uint64(10), uint64(10)},

		{Uint32Type, uint(10), uint32(10)},
		{Uint64Type, uint(10), uint64(10)},

		{Uint32Type, uint32(10), uint32(10)},
		{Uint64Type, uint64(10), uint64(10)},

		{FloatType, 3.14, float32(3.14)},
		{DoubleType, 3.14, float64(3.14)},

		{FloatType, float32(3.14), float32(3.14)},
		{DoubleType, float64(3.14), float64(3.14)},
	}

	for _, c := range cases {
		if c.t.resolve(c.val) != c.resval {
			t.Errorf("expected %T to resolve to %T", c.val, c.resval)
		}
	}
}
<commit_msg>metrics: test float and string compatibility<commit_after>package speed

import (
	"math"
	"testing"
)

// only tests that work on 32 bit architectures or both go here
// tests only working on 64 bit architectures go in _amd64_test.go
func TestIsCompatible(t *testing.T) {
	cases := []struct {
		t      MetricType
		v      interface{}
		result bool
	}{
		{Int32Type, -1, true},
		{Int64Type, -1, true},
		{Uint64Type, -1, false},
		{Uint32Type, -1, false},

		{Int32Type, math.MinInt32, true},
		{Int64Type, math.MinInt32, true},
		{Uint32Type, math.MinInt32, false},
		{Uint64Type, math.MinInt32, false},

		{Int32Type, int32(math.MinInt32), true},
		{Int64Type, int64(math.MinInt32), true},
		{Uint32Type, int32(math.MinInt32), false},
		{Uint64Type, int64(math.MinInt32), false},

		{Int32Type, math.MaxInt32, true},
		{Int64Type, math.MaxInt32, true},
		{Uint32Type, math.MaxInt32, true},
		{Uint64Type, math.MaxInt32, true},

		{Int32Type, int32(math.MaxInt32), true},
		{Int64Type, int64(math.MaxInt32), true},
		{Uint32Type, int32(math.MaxInt32), false},
		{Uint64Type, int64(math.MaxInt32), false},

		{Int64Type, int64(math.MaxInt64), true},

		{Uint32Type, uint32(math.MaxUint32), true},
		{Uint64Type, uint64(math.MaxUint32), true},

		{Uint32Type, uint(math.MaxUint32), true},

		{Uint32Type, math.MaxUint32, true},
		{Uint64Type, uint64(math.MaxUint64), true},

		{FloatType, math.MaxFloat32, true},
		{DoubleType, math.MaxFloat32, true},
		{FloatType, -math.MaxFloat32, true},
		{DoubleType, -math.MaxFloat32, true},

		{FloatType, float32(math.MaxFloat32), true},
		{DoubleType, float32(math.MaxFloat32), false},
		{FloatType, float32(-math.MaxFloat32), true},
		{DoubleType, float32(-math.MaxFloat32), false},

		{FloatType, float64(math.MaxFloat32), true},
		{DoubleType, float64(math.MaxFloat32), true},
		{FloatType, float64(-math.MaxFloat32), true},
		{DoubleType, float64(-math.MaxFloat32), true},

		{StringType, 10, false},
		{StringType, 10.10, false},
		{StringType, "10", true},
	}

	for _, c := range cases {
		r := c.t.IsCompatible(c.v)
		if r != c.result {
			t.Errorf("%v.IsCompatible(%v(%T)) should be %v, not %v", c.t, c.v, c.v, c.result, r)
		}
	}
}

func TestResolve(t *testing.T) {
	cases := []struct {
		t           MetricType
		val, resval interface{}
	}{
		{Int32Type, 10, int32(10)},
		{Int64Type, 10, int64(10)},
		{Uint32Type, 10, uint32(10)},
		{Uint64Type, 10, uint64(10)},

		{Int32Type, int32(10), int32(10)},
		{Int64Type, int64(10), int64(10)},
		{Uint32Type, uint32(10), uint32(10)},
		{Uint64Type, uint64(10), uint64(10)},

		{Uint32Type, uint(10), uint32(10)},
		{Uint64Type, uint(10), uint64(10)},

		{Uint32Type, uint32(10), uint32(10)},
		{Uint64Type, uint64(10), uint64(10)},

		{FloatType, 3.14, float32(3.14)},
		{DoubleType, 3.14, float64(3.14)},

		{FloatType, float32(3.14), float32(3.14)},
		{DoubleType, float64(3.14), float64(3.14)},
	}

	for _, c := range cases {
		if c.t.resolve(c.val) != c.resval {
			t.Errorf("expected %T to resolve to %T", c.val, c.resval)
		}
	}
}
<|endoftext|>


<commit_before>package proxy

import (
	"net/http"
	"net/http/httputil"
	"net/url"
	"strings"

	log "github.com/Sirupsen/logrus"
	"github.com/hellofresh/janus/pkg/request"
	"github.com/hellofresh/janus/pkg/router"
)

var (
	// ContextKeyBody defines the db context key
	ContextKeyBody = request.ContextKey("body")
)

// NewSingleHostReverseProxy returns a new ReverseProxy that routes
// URLs to the scheme, host, and base path provided in target. If the
// target's path is "/base" and the incoming request was for "/dir",
// the target request will be for /base/dir.
// NewSingleHostReverseProxy does not rewrite the Host header.
// To rewrite Host headers, use ReverseProxy directly with a custom
// Director policy.
func NewSingleHostReverseProxy(proxy Proxy, transport http.RoundTripper) *httputil.ReverseProxy {
	target, _ := url.Parse(proxy.TargetURL)
	targetQuery := target.RawQuery

	director := func(req *http.Request) {
		req.URL.Scheme = target.Scheme
		req.URL.Host = target.Host
		path := target.Path

		if proxy.AppendListenPath {
			log.Debug("Appending listen path to the target url")
			path = singleJoiningSlash(target.Path, req.URL.Path)
		}

		if proxy.StripListenPath {
			path = singleJoiningSlash(target.Path, req.URL.Path)
			matcher := router.NewListenPathMatcher()
			listenPath := matcher.Extract(proxy.ListenPath)

			log.Debugf("Stripping listen path: %s", listenPath)
			path = strings.Replace(path, listenPath, "", 1)
			if !strings.HasSuffix(target.Path, "/") && strings.HasSuffix(path, "/") {
				path = path[:len(path)-1]
			}
		}

		log.Debugf("Upstream Path is: %s", path)
		req.URL.Path = path

		// This is very important to avoid problems with ssl verification for the HOST header
		if !proxy.PreserveHostHeader {
			log.Debug("Preserving the host header")
			req.Host = target.Host
		}

		if targetQuery == "" || req.URL.RawQuery == "" {
			req.URL.RawQuery = targetQuery + req.URL.RawQuery
		} else {
			req.URL.RawQuery = targetQuery + "&" + req.URL.RawQuery
		}
	}

	return &httputil.ReverseProxy{Director: director, Transport: transport}
}

func cleanSlashes(a string) string {
	endSlash := strings.HasSuffix(a, "//")
	startSlash := strings.HasPrefix(a, "//")

	if startSlash {
		a = "/" + strings.TrimPrefix(a, "//")
	}

	if endSlash {
		a = strings.TrimSuffix(a, "//") + "/"
	}

	return a
}

func singleJoiningSlash(a, b string) string {
	a = cleanSlashes(a)
	b = cleanSlashes(b)

	aslash := strings.HasSuffix(a, "/")
	bslash := strings.HasPrefix(b, "/")

	switch {
	case aslash && bslash:
		log.Debug(a + b)
		return a + b[1:]
	case !aslash && !bslash:
		if len(b) > 0 {
			log.Debug(a + b)
			return a + "/" + b
		}

		log.Debug(a + b)
		return a
	}

	log.Debug(a + b)
	return a + b
}
<commit_msg>Removed unnecessary var<commit_after>package proxy

import (
	"net/http"
	"net/http/httputil"
	"net/url"
	"strings"

	log "github.com/Sirupsen/logrus"
	"github.com/hellofresh/janus/pkg/router"
)

// NewSingleHostReverseProxy returns a new ReverseProxy that routes
// URLs to the scheme, host, and base path provided in target. If the
// target's path is "/base" and the incoming request was for "/dir",
// the target request will be for /base/dir.
// NewSingleHostReverseProxy does not rewrite the Host header.
// To rewrite Host headers, use ReverseProxy directly with a custom
// Director policy.
func NewSingleHostReverseProxy(proxy *Proxy, transport http.RoundTripper) *httputil.ReverseProxy {
	target, _ := url.Parse(proxy.TargetURL)
	targetQuery := target.RawQuery

	director := func(req *http.Request) {
		req.URL.Scheme = target.Scheme
		req.URL.Host = target.Host
		path := target.Path

		if proxy.AppendListenPath {
			log.Debug("Appending listen path to the target url")
			path = singleJoiningSlash(target.Path, req.URL.Path)
		}

		if proxy.StripListenPath {
			path = singleJoiningSlash(target.Path, req.URL.Path)
			matcher := router.NewListenPathMatcher()
			listenPath := matcher.Extract(proxy.ListenPath)

			log.Debugf("Stripping listen path: %s", listenPath)
			path = strings.Replace(path, listenPath, "", 1)
			if !strings.HasSuffix(target.Path, "/") && strings.HasSuffix(path, "/") {
				path = path[:len(path)-1]
			}
		}

		log.Debugf("Upstream Path is: %s", path)
		req.URL.Path = path

		// This is very important to avoid problems with ssl verification for the HOST header
		if !proxy.PreserveHostHeader {
			log.Debug("Preserving the host header")
			req.Host = target.Host
		}

		if targetQuery == "" || req.URL.RawQuery == "" {
			req.URL.RawQuery = targetQuery + req.URL.RawQuery
		} else {
			req.URL.RawQuery = targetQuery + "&" + req.URL.RawQuery
		}
	}

	return &httputil.ReverseProxy{Director: director, Transport: transport}
}

func cleanSlashes(a string) string {
	endSlash := strings.HasSuffix(a, "//")
	startSlash := strings.HasPrefix(a, "//")

	if startSlash {
		a = "/" + strings.TrimPrefix(a, "//")
	}

	if endSlash {
		a = strings.TrimSuffix(a, "//") + "/"
	}

	return a
}

func singleJoiningSlash(a, b string) string {
	a = cleanSlashes(a)
	b = cleanSlashes(b)

	aslash := strings.HasSuffix(a, "/")
	bslash := strings.HasPrefix(b, "/")

	switch {
	case aslash && bslash:
		log.Debug(a + b)
		return a + b[1:]
	case !aslash && !bslash:
		if len(b) > 0 {
			log.Debug(a + b)
			return a + "/" + b
		}

		log.Debug(a + b)
		return a
	}

	log.Debug(a + b)
	return a + b
}
<|endoftext|>


<commit_before>package roaring

import (
	"encoding/binary"
	"fmt"
	"math/rand"
	"testing"

	. "github.com/smartystreets/goconvey/convey"
)

func TestCountTrailingZeros072(t *testing.T) {
	Convey("countTrailingZeros", t, func() {
		// undefined on older cpus, so skip this check on 0.
		//So(countTrailingZerosAsm(0), ShouldEqual, 64)

		So(countTrailingZerosAsm(8), ShouldEqual, 3)
		So(countTrailingZerosAsm(7), ShouldEqual, 0)
		So(countTrailingZerosAsm(1<<17), ShouldEqual, 17)
		So(countTrailingZerosAsm(7<<17), ShouldEqual, 17)
		So(countTrailingZerosAsm(255<<33), ShouldEqual, 33)

		So(countTrailingZerosDeBruijn(0), ShouldEqual, 64)
		So(countTrailingZerosDeBruijn(8), ShouldEqual, 3)
		So(countTrailingZerosDeBruijn(7), ShouldEqual, 0)
		So(countTrailingZerosDeBruijn(1<<17), ShouldEqual, 17)
		So(countTrailingZerosDeBruijn(7<<17), ShouldEqual, 17)
		So(countTrailingZerosDeBruijn(255<<33), ShouldEqual, 33)

	})
}

func getRandomUint64Set(n int) []uint64 {
	seed := int64(42)
	p("seed is %v", seed)
	rand.Seed(seed)

	var buf [8]byte
	var o []uint64
	for i := 0; i < n; i++ {
		rand.Read(buf[:])
		o = append(o, binary.LittleEndian.Uint64(buf[:]))
	}
	return o
}

func getAllOneBitUint64Set() []uint64 {
	o := []uint64{0}
	for i := uint(0); i < 64; i++ {
		o = append(o, 1<<i)
	}
	return o
}

func Benchmark100OrigNumberOfTrailingZeros(b *testing.B) {
	b.StopTimer()

	r := getRandomUint64Set(64)
	r = append(r, getAllOneBitUint64Set()...)

	b.ResetTimer()
	b.StartTimer()
	for i := 0; i < b.N; i++ {
		for i := range r {
			numberOfTrailingZeros(r[i])
		}
	}
}

func Benchmark100CountTrailingZerosDeBruijn(b *testing.B) {
	b.StopTimer()

	r := getRandomUint64Set(64)
	r = append(r, getAllOneBitUint64Set()...)

	b.ResetTimer()
	b.StartTimer()
	for i := 0; i < b.N; i++ {
		for i := range r {
			countTrailingZerosDeBruijn(r[i])
		}
	}
}

func Benchmark100CountTrailingZerosAsm(b *testing.B) {
	b.StopTimer()

	r := getRandomUint64Set(64)
	r = append(r, getAllOneBitUint64Set()...)

	b.ResetTimer()
	b.StartTimer()
	for i := 0; i < b.N; i++ {
		for i := range r {
			countTrailingZerosAsm(r[i])
		}
	}
}

func numberOfTrailingZeros(i uint64) int {
	if i == 0 {
		return 64
	}
	x := i
	n := int64(63)
	y := x << 32
	if y != 0 {
		n -= 32
		x = y
	}
	y = x << 16
	if y != 0 {
		n -= 16
		x = y
	}
	y = x << 8
	if y != 0 {
		n -= 8
		x = y
	}
	y = x << 4
	if y != 0 {
		n -= 4
		x = y
	}
	y = x << 2
	if y != 0 {
		n -= 2
		x = y
	}
	return int(n - int64(uint64(x<<1)>>63))
}

/*
//
// on an Intel(R) Core(TM) i7-5557U CPU @ 3.10GHz:
//
Benchmark100CountTrailingZerosDeBruijn-4   	10000000	       168 ns/op
Benchmark100CountTrailingZerosAsm-4        	 5000000	       278 ns/op
Benchmark100OrigNumberOfTrailingZeros-4    	 3000000	       592 ns/op

// and again:

Benchmark100CountTrailingZerosDeBruijn-4   	10000000	       168 ns/op
Benchmark100CountTrailingZerosAsm-4        	 5000000	       278 ns/op
Benchmark100OrigNumberOfTrailingZeros-4    	 3000000	       585 ns/op
*/
// go test -v -bench=100 -run 101
func Test101CountTrailingZerosCorrectness(t *testing.T) {
	r := getAllOneBitUint64Set()
	for i, v := range r {
		a := countTrailingZerosDeBruijn(v)
		b := countTrailingZerosAsm(v)
		if a != b {
			panic(fmt.Errorf("on r[%v]= v=%v,  a: %v, b:%v", i, v, a, b))
		}
	}
}
<commit_msg>atg. don't test 0 on asm<commit_after>package roaring

import (
	"encoding/binary"
	"fmt"
	"math/rand"
	"testing"

	. "github.com/smartystreets/goconvey/convey"
)

func TestCountTrailingZeros072(t *testing.T) {
	Convey("countTrailingZeros", t, func() {
		// undefined on older cpus, so skip this check on 0.
		//So(countTrailingZerosAsm(0), ShouldEqual, 64)

		So(countTrailingZerosAsm(8), ShouldEqual, 3)
		So(countTrailingZerosAsm(7), ShouldEqual, 0)
		So(countTrailingZerosAsm(1<<17), ShouldEqual, 17)
		So(countTrailingZerosAsm(7<<17), ShouldEqual, 17)
		So(countTrailingZerosAsm(255<<33), ShouldEqual, 33)

		So(countTrailingZerosDeBruijn(0), ShouldEqual, 64)
		So(countTrailingZerosDeBruijn(8), ShouldEqual, 3)
		So(countTrailingZerosDeBruijn(7), ShouldEqual, 0)
		So(countTrailingZerosDeBruijn(1<<17), ShouldEqual, 17)
		So(countTrailingZerosDeBruijn(7<<17), ShouldEqual, 17)
		So(countTrailingZerosDeBruijn(255<<33), ShouldEqual, 33)

	})
}

func getRandomUint64Set(n int) []uint64 {
	seed := int64(42)
	p("seed is %v", seed)
	rand.Seed(seed)

	var buf [8]byte
	var o []uint64
	for i := 0; i < n; i++ {
		rand.Read(buf[:])
		o = append(o, binary.LittleEndian.Uint64(buf[:]))
	}
	return o
}

func getAllOneBitUint64Set() []uint64 {
	var o []uint64
	for i := uint(0); i < 64; i++ {
		o = append(o, 1<<i)
	}
	return o
}

func Benchmark100OrigNumberOfTrailingZeros(b *testing.B) {
	b.StopTimer()

	r := getRandomUint64Set(64)
	r = append(r, getAllOneBitUint64Set()...)

	b.ResetTimer()
	b.StartTimer()
	for i := 0; i < b.N; i++ {
		for i := range r {
			numberOfTrailingZeros(r[i])
		}
	}
}

func Benchmark100CountTrailingZerosDeBruijn(b *testing.B) {
	b.StopTimer()

	r := getRandomUint64Set(64)
	r = append(r, getAllOneBitUint64Set()...)

	b.ResetTimer()
	b.StartTimer()
	for i := 0; i < b.N; i++ {
		for i := range r {
			countTrailingZerosDeBruijn(r[i])
		}
	}
}

func Benchmark100CountTrailingZerosAsm(b *testing.B) {
	b.StopTimer()

	r := getRandomUint64Set(64)
	r = append(r, getAllOneBitUint64Set()...)

	b.ResetTimer()
	b.StartTimer()
	for i := 0; i < b.N; i++ {
		for i := range r {
			countTrailingZerosAsm(r[i])
		}
	}
}

func numberOfTrailingZeros(i uint64) int {
	if i == 0 {
		return 64
	}
	x := i
	n := int64(63)
	y := x << 32
	if y != 0 {
		n -= 32
		x = y
	}
	y = x << 16
	if y != 0 {
		n -= 16
		x = y
	}
	y = x << 8
	if y != 0 {
		n -= 8
		x = y
	}
	y = x << 4
	if y != 0 {
		n -= 4
		x = y
	}
	y = x << 2
	if y != 0 {
		n -= 2
		x = y
	}
	return int(n - int64(uint64(x<<1)>>63))
}

/*
//
// on an Intel(R) Core(TM) i7-5557U CPU @ 3.10GHz:
//
Benchmark100CountTrailingZerosDeBruijn-4   	10000000	       168 ns/op
Benchmark100CountTrailingZerosAsm-4        	 5000000	       278 ns/op
Benchmark100OrigNumberOfTrailingZeros-4    	 3000000	       592 ns/op

// and again:

Benchmark100CountTrailingZerosDeBruijn-4   	10000000	       168 ns/op
Benchmark100CountTrailingZerosAsm-4        	 5000000	       278 ns/op
Benchmark100OrigNumberOfTrailingZeros-4    	 3000000	       585 ns/op
*/
// go test -v -bench=100 -run 101
func Test101CountTrailingZerosCorrectness(t *testing.T) {
	r := getAllOneBitUint64Set()
	for i, v := range r {
		a := countTrailingZerosDeBruijn(v)
		b := countTrailingZerosAsm(v)
		if a != b {
			panic(fmt.Errorf("on r[%v]= v=%v,  a: %v, b:%v", i, v, a, b))
		}
	}
	// don't do zero checks, since the Asm version can be undefined
	// for older architectures
	/*
		related Intel spec:

		    LZCNT is an extension of the BSR instruction. The key difference
		    between LZCNT and BSR is that LZCNT provides operand size as output
		    when source operand is zero, while in the case of BSR instruction,
		    if source operand is zero, the content of destination operand are
		    undefined. On processors that do not support LZCNT, the instruction
		    byte encoding is executed as BSR.

				if countTrailingZerosAsm(0) != countTrailingZerosDeBruijn(0) {
					panic("disagree on zero value")
				}
	*/

}
<|endoftext|>


<commit_before>package tempbackup

import (
	"bytes"
	"io"

	"github.com/pkg/errors"
	"github.com/taku-k/polymerase/pkg/tempbackup/tempbackuppb"
	"github.com/taku-k/polymerase/pkg/utils/log"
	"golang.org/x/net/context"
)

type TempBackupTransferService struct {
	manager *TempBackupManager
}

func NewBackupTransferService(m *TempBackupManager) *TempBackupTransferService {
	return &TempBackupTransferService{
		manager: m,
	}
}

func (s *TempBackupTransferService) TransferFullBackup(
	stream tempbackuppb.BackupTransferService_TransferFullBackupServer,
) error {
	var state *TempBackupState

	log.LogWithGRPC(stream.Context()).Info("Established peer")

	for {
		content, err := stream.Recv()
		if err == io.EOF {
			if err := state.Close(); err != nil {
				return err
			}
			return stream.SendAndClose(&tempbackuppb.BackupReply{
				Message: "success",
				Key:     state.key,
			})
		}
		if err != nil {
			return err
		}
		if state == nil {
			if content.Db == "" {
				return errors.New("empty db is not acceptable")
			}
			state, err = s.manager.OpenFullBackup(content.Db)
			if err != nil {
				return err
			}
			log.WithField("db", content.Db).Info("Connected to db")
			log.WithField("path", state.tempDir).Info("Create temporary directory")
		}
		if err := state.Append(content.Content); err != nil {
			return err
		}
	}
}

func (s *TempBackupTransferService) TransferIncBackup(
	stream tempbackuppb.BackupTransferService_TransferIncBackupServer,
) error {
	var state *TempBackupState
	var content *tempbackuppb.IncBackupContentStream
	var err error
	for {
		content, err = stream.Recv()
		if err == io.EOF {
			if err := state.Close(); err != nil {
				return err
			}
			return stream.SendAndClose(&tempbackuppb.BackupReply{
				Message: "success",
				Key:     state.key,
			})
		}
		if err != nil {
			return err
		}
		if state == nil {
			if content.Db == "" {
				return errors.New("empty db is not acceptable")
			}
			if content.Lsn == "" {
				return errors.New("empty lsn is not acceptable")
			}
			state, err = s.manager.OpenIncBackup(content.Db, content.Lsn)
			if err != nil {
				return err
			}
		}
		if err := state.Append(content.Content); err != nil {
			return err
		}
	}
}

func (s *TempBackupTransferService) PostCheckpoints(
	ctx context.Context,
	req *tempbackuppb.PostCheckpointsRequest) (*tempbackuppb.PostCheckpointsResponse, error) {
	r := bytes.NewReader(req.Content)
	if err := s.manager.storage.PostFile(req.Key, "xtrabackup_checkpoints", r); err != nil {
		return &tempbackuppb.PostCheckpointsResponse{}, err
	}
	return &tempbackuppb.PostCheckpointsResponse{
		Message: "success",
	}, nil
}
<commit_msg>Change to better log<commit_after>package tempbackup

import (
	"bytes"
	"io"

	"github.com/pkg/errors"
	"github.com/taku-k/polymerase/pkg/tempbackup/tempbackuppb"
	"github.com/taku-k/polymerase/pkg/utils/log"
	"golang.org/x/net/context"
)

type TempBackupTransferService struct {
	manager *TempBackupManager
}

func NewBackupTransferService(m *TempBackupManager) *TempBackupTransferService {
	return &TempBackupTransferService{
		manager: m,
	}
}

func (s *TempBackupTransferService) TransferFullBackup(
	stream tempbackuppb.BackupTransferService_TransferFullBackupServer,
) error {
	var state *TempBackupState

	log.LogWithGRPC(stream.Context()).Info("Established peer")

	for {
		content, err := stream.Recv()
		if err == io.EOF {
			if err := state.Close(); err != nil {
				return err
			}
			return stream.SendAndClose(&tempbackuppb.BackupReply{
				Message: "success",
				Key:     state.key,
			})
		}
		if err != nil {
			return err
		}
		if state == nil {
			if content.Db == "" {
				return errors.New("empty db is not acceptable")
			}
			state, err = s.manager.OpenFullBackup(content.Db)
			if err != nil {
				return err
			}
			log.WithField("db", content.Db).
				WithField("temp_path", state.tempDir).
				Info("Start full-backup")
		}
		if err := state.Append(content.Content); err != nil {
			return err
		}
	}
}

func (s *TempBackupTransferService) TransferIncBackup(
	stream tempbackuppb.BackupTransferService_TransferIncBackupServer,
) error {
	var state *TempBackupState

	log.LogWithGRPC(stream.Context()).Info("Established peer")

	for {
		content, err := stream.Recv()
		if err == io.EOF {
			if err := state.Close(); err != nil {
				return err
			}
			return stream.SendAndClose(&tempbackuppb.BackupReply{
				Message: "success",
				Key:     state.key,
			})
		}
		if err != nil {
			return err
		}
		if state == nil {
			if content.Db == "" {
				return errors.New("empty db is not acceptable")
			}
			if content.Lsn == "" {
				return errors.New("empty lsn is not acceptable")
			}
			state, err = s.manager.OpenIncBackup(content.Db, content.Lsn)
			if err != nil {
				return err
			}
			log.WithField("db", content.Db).
				WithField("temp_path", state.tempDir).
				Info("Start inc-backup")
		}
		if err := state.Append(content.Content); err != nil {
			return err
		}
	}
}

func (s *TempBackupTransferService) PostCheckpoints(
	ctx context.Context,
	req *tempbackuppb.PostCheckpointsRequest) (*tempbackuppb.PostCheckpointsResponse, error) {
	r := bytes.NewReader(req.Content)
	if err := s.manager.storage.PostFile(req.Key, "xtrabackup_checkpoints", r); err != nil {
		return &tempbackuppb.PostCheckpointsResponse{}, err
	}
	return &tempbackuppb.PostCheckpointsResponse{
		Message: "success",
	}, nil
}
<|endoftext|>


<commit_before>package config

import (
	"encoding/json"
	"fmt"
	"strings"
	"time"

	"github.com/kelseyhightower/envconfig"
)

type TPR struct {
	ReadyWaitInterval time.Duration `split_words:"true" default:"3s"`
	ReadyWaitTimeout  time.Duration `split_words:"true" default:"30s"`
	ResyncPeriod      time.Duration `split_words:"true" default:"5m"`
}

type Resources struct {
	ResyncPeriodPod        time.Duration `split_words:"true" default:"5m"`
	ResourceCheckInterval  time.Duration `split_words:"true" default:"3s"`
	ResourceCheckTimeout   time.Duration `split_words:"true" default:"10m"`
	PodLabelWaitTimeout    time.Duration `split_words:"true" default:"10m"`
	PodDeletionWaitTimeout time.Duration `split_words:"true" default:"10m"`
}

type Auth struct {
	PamRoleName          string `split_words:"true" default:"zalandos"`
	PamConfiguration     string `split_words:"true" default:"https://info.example.com/oauth2/tokeninfo?access_token= uid realm=/employees"`
	TeamsAPIUrl          string `envconfig:"teams_api_url" default:"https://teams.example.com/api/"`
	OAuthTokenSecretName string `envconfig:"oauth_token_secret_name" default:"postgresql-operator"`
	SuperUsername        string `split_words:"true" default:"postgres"`
	ReplicationUsername  string `split_words:"true" default:"replication"`
}

type Config struct {
	TPR
	Resources
	Auth
	EtcdHost           string `split_words:"true" default:"etcd-client.default.svc.cluster.local:2379"`
	DockerImage        string `split_words:"true" default:"registry.opensource.zalan.do/acid/spilo-9.6:1.2-p12"`
	ServiceAccountName string `split_words:"true" default:"operator"`
	DbHostedZone       string `split_words:"true" default:"db.example.com"`
	EtcdScope          string `split_words:"true" default:"service"`
	WALES3Bucket       string `envconfig: "wal_s3_bucket"`
	KubeIAMRole        string `envconfig: "kube_iam_role"`
	DebugLogging       bool   `split_words:"true" default:"false"`
}

func LoadFromEnv() *Config {
	var cfg Config
	err := envconfig.Process("PGOP", &cfg)
	if err != nil {
		panic(fmt.Errorf("Can't read config: %v", err))
	}
	cfg.EtcdScope = strings.Trim(cfg.EtcdScope, "/")

	return &cfg
}

func (c Config) MustMarshal() string {
	b, err := json.MarshalIndent(c, "", "\t")
	if err != nil {
		panic(err)
	}

	return string(b)
}
<commit_msg>Correct go json tags (extra space).<commit_after>package config

import (
	"encoding/json"
	"fmt"
	"strings"
	"time"

	"github.com/kelseyhightower/envconfig"
)

type TPR struct {
	ReadyWaitInterval time.Duration `split_words:"true" default:"3s"`
	ReadyWaitTimeout  time.Duration `split_words:"true" default:"30s"`
	ResyncPeriod      time.Duration `split_words:"true" default:"5m"`
}

type Resources struct {
	ResyncPeriodPod        time.Duration `split_words:"true" default:"5m"`
	ResourceCheckInterval  time.Duration `split_words:"true" default:"3s"`
	ResourceCheckTimeout   time.Duration `split_words:"true" default:"10m"`
	PodLabelWaitTimeout    time.Duration `split_words:"true" default:"10m"`
	PodDeletionWaitTimeout time.Duration `split_words:"true" default:"10m"`
}

type Auth struct {
	PamRoleName          string `split_words:"true" default:"zalandos"`
	PamConfiguration     string `split_words:"true" default:"https://info.example.com/oauth2/tokeninfo?access_token= uid realm=/employees"`
	TeamsAPIUrl          string `envconfig:"teams_api_url" default:"https://teams.example.com/api/"`
	OAuthTokenSecretName string `envconfig:"oauth_token_secret_name" default:"postgresql-operator"`
	SuperUsername        string `split_words:"true" default:"postgres"`
	ReplicationUsername  string `split_words:"true" default:"replication"`
}

type Config struct {
	TPR
	Resources
	Auth
	EtcdHost           string `split_words:"true" default:"etcd-client.default.svc.cluster.local:2379"`
	DockerImage        string `split_words:"true" default:"registry.opensource.zalan.do/acid/spilo-9.6:1.2-p12"`
	ServiceAccountName string `split_words:"true" default:"operator"`
	DbHostedZone       string `split_words:"true" default:"db.example.com"`
	EtcdScope          string `split_words:"true" default:"service"`
	WALES3Bucket       string `envconfig:"wal_s3_bucket"`
	KubeIAMRole        string `envconfig:"kube_iam_role"`
	DebugLogging       bool   `split_words:"true" default:"false"`
}

func LoadFromEnv() *Config {
	var cfg Config
	err := envconfig.Process("PGOP", &cfg)
	if err != nil {
		panic(fmt.Errorf("Can't read config: %v", err))
	}
	cfg.EtcdScope = strings.Trim(cfg.EtcdScope, "/")

	return &cfg
}

func (c Config) MustMarshal() string {
	b, err := json.MarshalIndent(c, "", "\t")
	if err != nil {
		panic(err)
	}

	return string(b)
}
<|endoftext|>


<commit_before>package agent

import (
	"golang.org/x/crypto/ssh"
	"io/ioutil"
	"fmt"
	"bytes"
)

//http://blog.ralch.com/tutorial/golang-ssh-connection/
func GetConfig(user, keyFile string) (*ssh.ClientConfig, error) {
	b, err := ioutil.ReadFile(keyFile)
	if err != nil {
		return nil, err
	}

	key, err := ssh.ParsePrivateKey(b)
	if err != nil {
		return nil, err
	}

	return &ssh.ClientConfig{
		User: user,
		Auth: []ssh.AuthMethod{ssh.PublicKeys(key)},
	}, nil
}

func RunCmd(c *ssh.Client, cmd, stdin string) (stdout, stderr string, err error) {
	session, err := c.NewSession()
	if err != nil {
		return
	}

	var outbuf, errbuf bytes.Buffer
	outwait, errwait := make(chan struct{}), make(chan struct{})
	outpipe, err := session.StdoutPipe()
	if err != nil {
		return
	}
	go func() {
		outbuf.ReadFrom(outpipe)
		close(outwait)
	}()

	errpipe, err := session.StderrPipe()
	if err != nil {
		return
	}
	go func() {
		errbuf.ReadFrom(errpipe)
		close(errwait)
	}()

	inpipe, err := session.StdinPipe()
	if err != nil {
		return
	}

	err = session.Start(cmd)
	if err != nil {
		fmt.Printf("Failed to run: %s", err)
		return
	}

	if stdin != "" {
		_, err = bytes.NewBufferString(stdin).WriteTo(inpipe)
		if err != nil {
			return
		}
	}
	inpipe.Close()

	// wait throws error if cmd return != 0
	cmdErr := session.Wait()
	<-outwait
	<-errwait
	session.Close()
	return outbuf.String(), errbuf.String(), cmdErr

}

func example() {
	addr := "54.153.91.176:22"

	user, key := "ubuntu", "/Users/bocon/.ssh/onos-warden.pem"

	fmt.Println("Dialing...")
	config, err := GetConfig(user, key)
	if err != nil {
		panic(err)
	}
	connection, err := ssh.Dial("tcp", addr, config)
	if err != nil {
		fmt.Printf("Failed to dial: %s", err)
		return
	}

	var cmd, in, stdout, stderr string
	cmd, in = "date", ""
	stdout, stderr, err = RunCmd(connection, cmd, in)
	fmt.Printf("out: %s, err: %s, err: %v\n", stdout, stderr, err)
}<commit_msg>Adding comments to ssh.go<commit_after>package agent

import (
	"golang.org/x/crypto/ssh"
	"io/ioutil"
	"fmt"
	"bytes"
)

// Returns an ssh.ClientConfig given a username and key filepath
func GetConfig(user, keyFile string) (*ssh.ClientConfig, error) {
	b, err := ioutil.ReadFile(keyFile)
	if err != nil {
		return nil, err
	}

	key, err := ssh.ParsePrivateKey(b)
	if err != nil {
		return nil, err
	}

	return &ssh.ClientConfig{
		User: user,
		Auth: []ssh.AuthMethod{ssh.PublicKeys(key)},
	}, nil
}

// Runs the given command on the ssh.Client and provides stdin to the processes on the remote
// Returns stdout and stderr; err is != nil if exit status is != 0
func RunCmd(c *ssh.Client, cmd, stdin string) (stdout, stderr string, err error) {
	session, err := c.NewSession()
	if err != nil {
		return
	}

	var outbuf, errbuf bytes.Buffer
	outwait, errwait := make(chan struct{}), make(chan struct{})
	outpipe, err := session.StdoutPipe()
	if err != nil {
		return
	}
	go func() {
		outbuf.ReadFrom(outpipe)
		close(outwait)
	}()

	errpipe, err := session.StderrPipe()
	if err != nil {
		return
	}
	go func() {
		errbuf.ReadFrom(errpipe)
		close(errwait)
	}()

	inpipe, err := session.StdinPipe()
	if err != nil {
		return
	}

	err = session.Start(cmd)
	if err != nil {
		fmt.Printf("Failed to run: %s", err)
		return
	}

	if stdin != "" {
		_, err = bytes.NewBufferString(stdin).WriteTo(inpipe)
		if err != nil {
			return
		}
	}
	inpipe.Close()

	// wait throws error if cmd return != 0
	cmdErr := session.Wait()
	<-outwait
	<-errwait
	session.Close()
	return outbuf.String(), errbuf.String(), cmdErr

}

func example() {
	addr := "54.153.91.176:22"

	user, key := "ubuntu", "/Users/bocon/.ssh/onos-warden.pem"

	fmt.Println("Dialing...")
	config, err := GetConfig(user, key)
	if err != nil {
		panic(err)
	}
	connection, err := ssh.Dial("tcp", addr, config)
	if err != nil {
		fmt.Printf("Failed to dial: %s", err)
		return
	}

	var cmd, in, stdout, stderr string
	cmd, in = "date", ""
	stdout, stderr, err = RunCmd(connection, cmd, in)
	fmt.Printf("out: %s, err: %s, err: %v\n", stdout, stderr, err)
}<|endoftext|>


<commit_before>/*
Copyright 2017 The Kubernetes 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.
*/

package local

import (
	"fmt"
	"os"

	"github.com/golang/glog"

	"k8s.io/api/core/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/types"
	"k8s.io/kubernetes/pkg/util/mount"
	"k8s.io/kubernetes/pkg/util/strings"
	"k8s.io/kubernetes/pkg/volume"
	"k8s.io/kubernetes/pkg/volume/util"
	"k8s.io/kubernetes/pkg/volume/validation"
)

// This is the primary entrypoint for volume plugins.
func ProbeVolumePlugins() []volume.VolumePlugin {
	return []volume.VolumePlugin{&localVolumePlugin{}}
}

type localVolumePlugin struct {
	host volume.VolumeHost
}

var _ volume.VolumePlugin = &localVolumePlugin{}
var _ volume.PersistentVolumePlugin = &localVolumePlugin{}

const (
	localVolumePluginName = "kubernetes.io/local-volume"
)

func (plugin *localVolumePlugin) Init(host volume.VolumeHost) error {
	plugin.host = host
	return nil
}

func (plugin *localVolumePlugin) GetPluginName() string {
	return localVolumePluginName
}

func (plugin *localVolumePlugin) GetVolumeName(spec *volume.Spec) (string, error) {
	// This volume is only supported as a PersistentVolumeSource, so the PV name is unique
	return spec.Name(), nil
}

func (plugin *localVolumePlugin) CanSupport(spec *volume.Spec) bool {
	// This volume is only supported as a PersistentVolumeSource
	return (spec.PersistentVolume != nil && spec.PersistentVolume.Spec.Local != nil)
}

func (plugin *localVolumePlugin) RequiresRemount() bool {
	return false
}

func (plugin *localVolumePlugin) SupportsMountOption() bool {
	return false
}

func (plugin *localVolumePlugin) SupportsBulkVolumeVerification() bool {
	return false
}

func (plugin *localVolumePlugin) GetAccessModes() []v1.PersistentVolumeAccessMode {
	// The current meaning of AccessMode is how many nodes can attach to it, not how many pods can mount it
	return []v1.PersistentVolumeAccessMode{
		v1.ReadWriteOnce,
	}
}

func getVolumeSource(spec *volume.Spec) (*v1.LocalVolumeSource, bool, error) {
	if spec.PersistentVolume != nil && spec.PersistentVolume.Spec.Local != nil {
		return spec.PersistentVolume.Spec.Local, spec.ReadOnly, nil
	}

	return nil, false, fmt.Errorf("Spec does not reference a Local volume type")
}

func (plugin *localVolumePlugin) NewMounter(spec *volume.Spec, pod *v1.Pod, _ volume.VolumeOptions) (volume.Mounter, error) {
	volumeSource, readOnly, err := getVolumeSource(spec)
	if err != nil {
		return nil, err
	}

	return &localVolumeMounter{
		localVolume: &localVolume{
			podUID:     pod.UID,
			volName:    spec.Name(),
			mounter:    plugin.host.GetMounter(),
			plugin:     plugin,
			globalPath: volumeSource.Path,
		},
		readOnly: readOnly,
	}, nil

}

func (plugin *localVolumePlugin) NewUnmounter(volName string, podUID types.UID) (volume.Unmounter, error) {
	return &localVolumeUnmounter{
		localVolume: &localVolume{
			podUID:  podUID,
			volName: volName,
			mounter: plugin.host.GetMounter(),
			plugin:  plugin,
		},
	}, nil
}

// TODO: check if no path and no topology constraints are ok
func (plugin *localVolumePlugin) ConstructVolumeSpec(volumeName, mountPath string) (*volume.Spec, error) {
	localVolume := &v1.PersistentVolume{
		ObjectMeta: metav1.ObjectMeta{
			Name: volumeName,
		},
		Spec: v1.PersistentVolumeSpec{
			PersistentVolumeSource: v1.PersistentVolumeSource{
				Local: &v1.LocalVolumeSource{
					Path: "",
				},
			},
		},
	}
	return volume.NewSpecFromPersistentVolume(localVolume, false), nil
}

// Local volumes represent a local directory on a node.
// The directory at the globalPath will be bind-mounted to the pod's directory
type localVolume struct {
	volName string
	podUID  types.UID
	// Global path to the volume
	globalPath string
	// Mounter interface that provides system calls to mount the global path to the pod local path.
	mounter mount.Interface
	plugin  *localVolumePlugin
	// TODO: add metrics
	volume.MetricsNil
}

func (l *localVolume) GetPath() string {
	return l.plugin.host.GetPodVolumeDir(l.podUID, strings.EscapeQualifiedNameForDisk(localVolumePluginName), l.volName)
}

type localVolumeMounter struct {
	*localVolume
	readOnly bool
}

var _ volume.Mounter = &localVolumeMounter{}

func (m *localVolumeMounter) GetAttributes() volume.Attributes {
	return volume.Attributes{
		ReadOnly:        m.readOnly,
		Managed:         !m.readOnly,
		SupportsSELinux: true,
	}
}

// CanMount checks prior to mount operations to verify that the required components (binaries, etc.)
// to mount the volume are available on the underlying node.
// If not, it returns an error
func (m *localVolumeMounter) CanMount() error {
	return nil
}

// SetUp bind mounts the directory to the volume path
func (m *localVolumeMounter) SetUp(fsGroup *int64) error {
	return m.SetUpAt(m.GetPath(), fsGroup)
}

// SetUpAt bind mounts the directory to the volume path and sets up volume ownership
func (m *localVolumeMounter) SetUpAt(dir string, fsGroup *int64) error {
	if m.globalPath == "" {
		err := fmt.Errorf("LocalVolume volume %q path is empty", m.volName)
		return err
	}

	err := validation.ValidatePathNoBacksteps(m.globalPath)
	if err != nil {
		return fmt.Errorf("invalid path: %s %v", m.globalPath, err)
	}

	notMnt, err := m.mounter.IsNotMountPoint(dir)
	glog.V(4).Infof("LocalVolume mount setup: PodDir(%s) VolDir(%s) Mounted(%t) Error(%v), ReadOnly(%t)", dir, m.globalPath, !notMnt, err, m.readOnly)
	if err != nil && !os.IsNotExist(err) {
		glog.Errorf("cannot validate mount point: %s %v", dir, err)
		return err
	}
	if !notMnt {
		return nil
	}

	if err := os.MkdirAll(dir, 0750); err != nil {
		glog.Errorf("mkdir failed on disk %s (%v)", dir, err)
		return err
	}

	// Perform a bind mount to the full path to allow duplicate mounts of the same volume.
	options := []string{"bind"}
	if m.readOnly {
		options = append(options, "ro")
	}

	glog.V(4).Infof("attempting to mount %s", dir)
	err = m.mounter.Mount(m.globalPath, dir, "", options)
	if err != nil {
		glog.Errorf("Mount of volume %s failed: %v", dir, err)
		notMnt, mntErr := m.mounter.IsNotMountPoint(dir)
		if mntErr != nil {
			glog.Errorf("IsNotMountPoint check failed: %v", mntErr)
			return err
		}
		if !notMnt {
			if mntErr = m.mounter.Unmount(dir); mntErr != nil {
				glog.Errorf("Failed to unmount: %v", mntErr)
				return err
			}
			notMnt, mntErr = m.mounter.IsNotMountPoint(dir)
			if mntErr != nil {
				glog.Errorf("IsNotMountPoint check failed: %v", mntErr)
				return err
			}
			if !notMnt {
				// This is very odd, we don't expect it.  We'll try again next sync loop.
				glog.Errorf("%s is still mounted, despite call to unmount().  Will try again next sync loop.", dir)
				return err
			}
		}
		os.Remove(dir)
		return err
	}

	if !m.readOnly {
		// TODO: how to prevent multiple mounts with conflicting fsGroup?
		return volume.SetVolumeOwnership(m, fsGroup)
	}
	return nil
}

type localVolumeUnmounter struct {
	*localVolume
}

var _ volume.Unmounter = &localVolumeUnmounter{}

// TearDown unmounts the bind mount
func (u *localVolumeUnmounter) TearDown() error {
	return u.TearDownAt(u.GetPath())
}

// TearDownAt unmounts the bind mount
func (u *localVolumeUnmounter) TearDownAt(dir string) error {
	glog.V(4).Infof("Unmounting volume %q at path %q\n", u.volName, dir)
	return util.UnmountMountPoint(dir, u.mounter, true) /* extensiveMountPointCheck = true */
}
<commit_msg>Adding metrics support to local volume<commit_after>/*
Copyright 2017 The Kubernetes 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.
*/

package local

import (
	"fmt"
	"os"

	"github.com/golang/glog"

	"k8s.io/api/core/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/types"
	"k8s.io/kubernetes/pkg/util/mount"
	"k8s.io/kubernetes/pkg/util/strings"
	"k8s.io/kubernetes/pkg/volume"
	"k8s.io/kubernetes/pkg/volume/util"
	"k8s.io/kubernetes/pkg/volume/validation"
)

// This is the primary entrypoint for volume plugins.
func ProbeVolumePlugins() []volume.VolumePlugin {
	return []volume.VolumePlugin{&localVolumePlugin{}}
}

type localVolumePlugin struct {
	host volume.VolumeHost
}

var _ volume.VolumePlugin = &localVolumePlugin{}
var _ volume.PersistentVolumePlugin = &localVolumePlugin{}

const (
	localVolumePluginName = "kubernetes.io/local-volume"
)

func (plugin *localVolumePlugin) Init(host volume.VolumeHost) error {
	plugin.host = host
	return nil
}

func (plugin *localVolumePlugin) GetPluginName() string {
	return localVolumePluginName
}

func (plugin *localVolumePlugin) GetVolumeName(spec *volume.Spec) (string, error) {
	// This volume is only supported as a PersistentVolumeSource, so the PV name is unique
	return spec.Name(), nil
}

func (plugin *localVolumePlugin) CanSupport(spec *volume.Spec) bool {
	// This volume is only supported as a PersistentVolumeSource
	return (spec.PersistentVolume != nil && spec.PersistentVolume.Spec.Local != nil)
}

func (plugin *localVolumePlugin) RequiresRemount() bool {
	return false
}

func (plugin *localVolumePlugin) SupportsMountOption() bool {
	return false
}

func (plugin *localVolumePlugin) SupportsBulkVolumeVerification() bool {
	return false
}

func (plugin *localVolumePlugin) GetAccessModes() []v1.PersistentVolumeAccessMode {
	// The current meaning of AccessMode is how many nodes can attach to it, not how many pods can mount it
	return []v1.PersistentVolumeAccessMode{
		v1.ReadWriteOnce,
	}
}

func getVolumeSource(spec *volume.Spec) (*v1.LocalVolumeSource, bool, error) {
	if spec.PersistentVolume != nil && spec.PersistentVolume.Spec.Local != nil {
		return spec.PersistentVolume.Spec.Local, spec.ReadOnly, nil
	}

	return nil, false, fmt.Errorf("Spec does not reference a Local volume type")
}

func (plugin *localVolumePlugin) NewMounter(spec *volume.Spec, pod *v1.Pod, _ volume.VolumeOptions) (volume.Mounter, error) {
	volumeSource, readOnly, err := getVolumeSource(spec)
	if err != nil {
		return nil, err
	}

	return &localVolumeMounter{
		localVolume: &localVolume{
			podUID:          pod.UID,
			volName:         spec.Name(),
			mounter:         plugin.host.GetMounter(),
			plugin:          plugin,
			globalPath:      volumeSource.Path,
			MetricsProvider: volume.NewMetricsStatFS(volumeSource.Path),
		},
		readOnly: readOnly,
	}, nil

}

func (plugin *localVolumePlugin) NewUnmounter(volName string, podUID types.UID) (volume.Unmounter, error) {
	return &localVolumeUnmounter{
		localVolume: &localVolume{
			podUID:  podUID,
			volName: volName,
			mounter: plugin.host.GetMounter(),
			plugin:  plugin,
		},
	}, nil
}

// TODO: check if no path and no topology constraints are ok
func (plugin *localVolumePlugin) ConstructVolumeSpec(volumeName, mountPath string) (*volume.Spec, error) {
	localVolume := &v1.PersistentVolume{
		ObjectMeta: metav1.ObjectMeta{
			Name: volumeName,
		},
		Spec: v1.PersistentVolumeSpec{
			PersistentVolumeSource: v1.PersistentVolumeSource{
				Local: &v1.LocalVolumeSource{
					Path: "",
				},
			},
		},
	}
	return volume.NewSpecFromPersistentVolume(localVolume, false), nil
}

// Local volumes represent a local directory on a node.
// The directory at the globalPath will be bind-mounted to the pod's directory
type localVolume struct {
	volName string
	podUID  types.UID
	// Global path to the volume
	globalPath string
	// Mounter interface that provides system calls to mount the global path to the pod local path.
	mounter mount.Interface
	plugin  *localVolumePlugin
	volume.MetricsProvider
}

func (l *localVolume) GetPath() string {
	return l.plugin.host.GetPodVolumeDir(l.podUID, strings.EscapeQualifiedNameForDisk(localVolumePluginName), l.volName)
}

type localVolumeMounter struct {
	*localVolume
	readOnly bool
}

var _ volume.Mounter = &localVolumeMounter{}

func (m *localVolumeMounter) GetAttributes() volume.Attributes {
	return volume.Attributes{
		ReadOnly:        m.readOnly,
		Managed:         !m.readOnly,
		SupportsSELinux: true,
	}
}

// CanMount checks prior to mount operations to verify that the required components (binaries, etc.)
// to mount the volume are available on the underlying node.
// If not, it returns an error
func (m *localVolumeMounter) CanMount() error {
	return nil
}

// SetUp bind mounts the directory to the volume path
func (m *localVolumeMounter) SetUp(fsGroup *int64) error {
	return m.SetUpAt(m.GetPath(), fsGroup)
}

// SetUpAt bind mounts the directory to the volume path and sets up volume ownership
func (m *localVolumeMounter) SetUpAt(dir string, fsGroup *int64) error {
	if m.globalPath == "" {
		err := fmt.Errorf("LocalVolume volume %q path is empty", m.volName)
		return err
	}

	err := validation.ValidatePathNoBacksteps(m.globalPath)
	if err != nil {
		return fmt.Errorf("invalid path: %s %v", m.globalPath, err)
	}

	notMnt, err := m.mounter.IsNotMountPoint(dir)
	glog.V(4).Infof("LocalVolume mount setup: PodDir(%s) VolDir(%s) Mounted(%t) Error(%v), ReadOnly(%t)", dir, m.globalPath, !notMnt, err, m.readOnly)
	if err != nil && !os.IsNotExist(err) {
		glog.Errorf("cannot validate mount point: %s %v", dir, err)
		return err
	}
	if !notMnt {
		return nil
	}

	if err := os.MkdirAll(dir, 0750); err != nil {
		glog.Errorf("mkdir failed on disk %s (%v)", dir, err)
		return err
	}

	// Perform a bind mount to the full path to allow duplicate mounts of the same volume.
	options := []string{"bind"}
	if m.readOnly {
		options = append(options, "ro")
	}

	glog.V(4).Infof("attempting to mount %s", dir)
	err = m.mounter.Mount(m.globalPath, dir, "", options)
	if err != nil {
		glog.Errorf("Mount of volume %s failed: %v", dir, err)
		notMnt, mntErr := m.mounter.IsNotMountPoint(dir)
		if mntErr != nil {
			glog.Errorf("IsNotMountPoint check failed: %v", mntErr)
			return err
		}
		if !notMnt {
			if mntErr = m.mounter.Unmount(dir); mntErr != nil {
				glog.Errorf("Failed to unmount: %v", mntErr)
				return err
			}
			notMnt, mntErr = m.mounter.IsNotMountPoint(dir)
			if mntErr != nil {
				glog.Errorf("IsNotMountPoint check failed: %v", mntErr)
				return err
			}
			if !notMnt {
				// This is very odd, we don't expect it.  We'll try again next sync loop.
				glog.Errorf("%s is still mounted, despite call to unmount().  Will try again next sync loop.", dir)
				return err
			}
		}
		os.Remove(dir)
		return err
	}

	if !m.readOnly {
		// TODO: how to prevent multiple mounts with conflicting fsGroup?
		return volume.SetVolumeOwnership(m, fsGroup)
	}
	return nil
}

type localVolumeUnmounter struct {
	*localVolume
}

var _ volume.Unmounter = &localVolumeUnmounter{}

// TearDown unmounts the bind mount
func (u *localVolumeUnmounter) TearDown() error {
	return u.TearDownAt(u.GetPath())
}

// TearDownAt unmounts the bind mount
func (u *localVolumeUnmounter) TearDownAt(dir string) error {
	glog.V(4).Infof("Unmounting volume %q at path %q\n", u.volName, dir)
	return util.UnmountMountPoint(dir, u.mounter, true) /* extensiveMountPointCheck = true */
}
<|endoftext|>