<commit_before>// Copyright 2017 Tomas Machalek <tomas.machalek@gmail.com>
//
// 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 vertigo

import (
	"regexp"
	"strings"
)

var (
	tagSrchRegexp  = regexp.MustCompile("^<([\\w\\d\\p{Po}]+)(\\s+.*?|)/?>$")
	attrValRegexp  = regexp.MustCompile("(\\w+)=\"([^\"]+)\"")
	closeTagRegexp = regexp.MustCompile("</([^>]+)\\s*>")
)

// this is quite simplified but it should work for our purposes
func isElement(tagSrc string) bool {
	return strings.HasPrefix(tagSrc, "<") && strings.HasSuffix(tagSrc, ">")
}

func isOpenElement(tagSrc string) bool {
	return isElement(tagSrc) && !strings.HasPrefix(tagSrc, "</") &&
		!strings.HasSuffix(tagSrc, "/>")
}

func isCloseElement(tagSrc string) bool {
	return isElement(tagSrc) && strings.HasPrefix(tagSrc, "</")
}

func isSelfCloseElement(tagSrc string) bool {
	return isElement(tagSrc) && strings.HasSuffix(tagSrc, "/>")
}

func parseAttrVal(src string) map[string]string {
	ans := make(map[string]string)
	srch := attrValRegexp.FindAllStringSubmatch(src, -1)
	for i := 0; i < len(srch); i++ {
		ans[srch[i][1]] = srch[i][2]
	}
	return ans
}

func parseLine(line string, elmStack structAttrAccumulator) (interface{}, error) {
	switch {
	case isOpenElement(line):
		srch := tagSrchRegexp.FindStringSubmatch(line)
		meta := &Structure{Name: srch[1], Attrs: parseAttrVal(srch[2])}
		err := elmStack.Begin(meta)
		return meta, err
	case isCloseElement(line):
		srch := closeTagRegexp.FindStringSubmatch(line)
		elm, err := elmStack.End(srch[1])
		if err != nil {
			return nil, err
		}
		return &StructureClose{Name: elm.Name}, nil
	case isSelfCloseElement(line):
		srch := tagSrchRegexp.FindStringSubmatch(line)
		return &Structure{Name: srch[1], Attrs: parseAttrVal(srch[2]), IsEmpty: true}, nil
	default:
		items := strings.Split(line, "\t")
		return &Token{
			Word:        items[0],
			Attrs:       items[1:],
			StructAttrs: elmStack.GetAttrs(),
		}, nil
	}
}
<commit_msg>Handle match error in a better way<commit_after>// Copyright 2017 Tomas Machalek <tomas.machalek@gmail.com>
//
// 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 vertigo

import (
	"fmt"
	"regexp"
	"strings"
)

var (
	tagSrchRegexp  = regexp.MustCompile("^<([\\w\\d\\p{Po}]+)(\\s+.*?|)/?>$")
	attrValRegexp  = regexp.MustCompile("(\\w+)=\"([^\"]+)\"")
	closeTagRegexp = regexp.MustCompile("</([^>]+)\\s*>")
)

// this is quite simplified but it should work for our purposes
func isElement(tagSrc string) bool {
	return strings.HasPrefix(tagSrc, "<") && strings.HasSuffix(tagSrc, ">")
}

func isOpenElement(tagSrc string) bool {
	return isElement(tagSrc) && !strings.HasPrefix(tagSrc, "</") &&
		!strings.HasSuffix(tagSrc, "/>")
}

func isCloseElement(tagSrc string) bool {
	return isElement(tagSrc) && strings.HasPrefix(tagSrc, "</")
}

func isSelfCloseElement(tagSrc string) bool {
	return isElement(tagSrc) && strings.HasSuffix(tagSrc, "/>")
}

func parseAttrVal(src string) map[string]string {
	ans := make(map[string]string)
	srch := attrValRegexp.FindAllStringSubmatch(src, -1)
	for i := 0; i < len(srch); i++ {
		ans[srch[i][1]] = srch[i][2]
	}
	return ans
}

func parseLine(line string, elmStack structAttrAccumulator) (interface{}, error) {
	switch {
	case isOpenElement(line):
		srch := tagSrchRegexp.FindStringSubmatch(line)
		if len(srch) < 3 {
			return nil, fmt.Errorf("Cannot parse open element '%s'", line)
		}
		meta := &Structure{Name: srch[1], Attrs: parseAttrVal(srch[2])}
		err := elmStack.Begin(meta)
		return meta, err
	case isCloseElement(line):
		srch := closeTagRegexp.FindStringSubmatch(line)
		elm, err := elmStack.End(srch[1])
		if err != nil {
			return nil, err
		}
		return &StructureClose{Name: elm.Name}, nil
	case isSelfCloseElement(line):
		srch := tagSrchRegexp.FindStringSubmatch(line)
		return &Structure{Name: srch[1], Attrs: parseAttrVal(srch[2]), IsEmpty: true}, nil
	default:
		items := strings.Split(line, "\t")
		return &Token{
			Word:        items[0],
			Attrs:       items[1:],
			StructAttrs: elmStack.GetAttrs(),
		}, nil
	}
}
<|endoftext|>


<commit_before>package collectors

import (
	"fmt"
	"io/ioutil"

	"strconv"
	"strings"

	"bosun.org/metadata"
	"bosun.org/opentsdb"
)

func WatchProcesses(procs []*WatchedProc) error {
	collectors = append(collectors, &IntervalCollector{
		F: func() (opentsdb.MultiDataPoint, error) {
			return c_linux_processes(procs)
		},
		name: "c_linux_processes",
	})
	return nil
}

func linuxProcMonitor(w *WatchedProc, md *opentsdb.MultiDataPoint) error {
	var err error
	for pid, id := range w.Processes {
		stats_file, e := ioutil.ReadFile("/proc/" + pid + "/stat")
		if e != nil {
			w.Remove(pid)
			continue
		}
		io_file, e := ioutil.ReadFile("/proc/" + pid + "/io")
		if e != nil {
			w.Remove(pid)
			continue
		}
		limits, e := ioutil.ReadFile("/proc/" + pid + "/limits")
		if e != nil {
			w.Remove(pid)
			continue
		}
		fds, e := ioutil.ReadDir("/proc/" + pid + "/fd")
		if e != nil {
			w.Remove(pid)
			continue
		}
		stats := strings.Fields(string(stats_file))
		if len(stats) < 24 {
			err = fmt.Errorf("stats too short")
			continue
		}
		var io []string
		for _, line := range strings.Split(string(io_file), "\n") {
			f := strings.Fields(line)
			if len(f) == 2 {
				io = append(io, f[1])
			}
		}
		if len(io) < 6 {
			err = fmt.Errorf("io too short")
			continue
		}
		tags := opentsdb.TagSet{"name": w.Name, "id": strconv.Itoa(id)}
		for _, line := range strings.Split(string(limits), "\n") {
			f := strings.Fields(line)
			if len(f) == 6 && strings.Join(f[0:3], " ") == "Max open files" {
				if f[3] != "unlimited" {
					Add(md, "linux.proc.num_fds_slim", f[3], tags, metadata.Gauge, metadata.Files, descLinuxSoftFileLimit)
					Add(md, "linux.proc.num_fds_hlim", f[4], tags, metadata.Gauge, metadata.Files, descLinuxHardFileLimit)
				}
			}
		}
		Add(md, "linux.proc.cpu", stats[13], opentsdb.TagSet{"type": "user"}.Merge(tags), metadata.Counter, metadata.Pct, descLinuxProcCPUUser)
		Add(md, "linux.proc.cpu", stats[14], opentsdb.TagSet{"type": "system"}.Merge(tags), metadata.Counter, metadata.Pct, descLinuxProcCPUSystem)
		Add(md, "linux.proc.mem.fault", stats[9], opentsdb.TagSet{"type": "minflt"}.Merge(tags), metadata.Counter, metadata.Fault, descLinuxProcMemFaultMin)
		Add(md, "linux.proc.mem.fault", stats[11], opentsdb.TagSet{"type": "majflt"}.Merge(tags), metadata.Counter, metadata.Fault, descLinuxProcMemFaultMax)
		Add(md, "linux.proc.mem.virtual", stats[22], tags, metadata.Gauge, metadata.Bytes, descLinuxProcMemVirtual)
		Add(md, "linux.proc.mem.rss", stats[23], tags, metadata.Gauge, metadata.Page, descLinuxProcMemRss)
		Add(md, "linux.proc.char_io", io[0], opentsdb.TagSet{"type": "read"}.Merge(tags), metadata.Counter, metadata.Bytes, descLinuxProcCharIoRead)
		Add(md, "linux.proc.char_io", io[1], opentsdb.TagSet{"type": "write"}.Merge(tags), metadata.Counter, metadata.Bytes, descLinuxProcCharIoWrite)
		Add(md, "linux.proc.syscall", io[2], opentsdb.TagSet{"type": "read"}.Merge(tags), metadata.Counter, metadata.Syscall, descLinuxProcSyscallRead)
		Add(md, "linux.proc.syscall", io[3], opentsdb.TagSet{"type": "write"}.Merge(tags), metadata.Counter, metadata.Syscall, descLinuxProcSyscallWrite)
		Add(md, "linux.proc.io_bytes", io[4], opentsdb.TagSet{"type": "read"}.Merge(tags), metadata.Counter, metadata.Bytes, descLinuxProcIoBytesRead)
		Add(md, "linux.proc.io_bytes", io[5], opentsdb.TagSet{"type": "write"}.Merge(tags), metadata.Counter, metadata.Bytes, descLinuxProcIoBytesWrite)
		Add(md, "linux.proc.num_fds", len(fds), tags, metadata.Gauge, metadata.Files, descLinuxProcFd)
	}
	return err
}

const (
	descLinuxProcCPUUser      = "The amount of time that this process has been scheduled in user mode."
	descLinuxProcCPUSystem    = "The amount of time that this process has been scheduled in kernel mode"
	descLinuxProcMemFaultMin  = "The number of minor faults the process has made which have not required loading a memory page from disk."
	descLinuxProcMemFaultMax  = "The number of major faults the process has made which have required loading a memory page from disk."
	descLinuxProcMemVirtual   = "The virtual memory size."
	descLinuxProcMemRss       = "The resident set size (number of pages the process has in real memory."
	descLinuxProcCharIoRead   = "The number of bytes which this task has caused to be read from storage. This is simply the sum of bytes which this process passed to read(2) and similar system calls. It includes things such as terminal I/O and is unaffected by whether or not actual physical disk I/O was required (the read might have been satisfied from pagecache)"
	descLinuxProcCharIoWrite  = "The number of bytes which this task has caused, or shall cause to be written to disk. Similar caveats apply here as with read."
	descLinuxProcSyscallRead  = "An attempt to count the number of read I/O operations—that is, system calls such as read(2) and pread(2)."
	descLinuxProcSyscallWrite = "Attempt to count the number of write I/O operations—that is, system calls such as write(2) and pwrite(2)."
	descLinuxProcIoBytesRead  = "An attempt to count the number of bytes which this process really did cause to be fetched from the storage layer. This is accurate for block-backed filesystems."
	descLinuxProcIoBytesWrite = "An Attempt to count the number of bytes which this process caused to be sent to the storage layer."
	descLinuxProcFd           = "The number of open file descriptors."
	descLinuxSoftFileLimit    = "The soft limit on the number of open file descriptors."
	descLinuxHardFileLimit    = "The hard limit on the number of open file descriptors."
)

func getLinuxProccesses() ([]*Process, error) {
	files, err := ioutil.ReadDir("/proc")
	if err != nil {
		return nil, err
	}
	var pids []string
	for _, f := range files {
		if _, err := strconv.Atoi(f.Name()); err == nil && f.IsDir() {
			pids = append(pids, f.Name())
		}
	}
	var lps []*Process
	for _, pid := range pids {
		cmdline, err := ioutil.ReadFile("/proc/" + pid + "/cmdline")
		if err != nil {
			//Continue because the pid might not exist any more
			continue
		}
		cl := strings.Split(string(cmdline), "\x00")
		if len(cl) < 1 || len(cl[0]) == 0 {
			continue
		}
		lp := &Process{
			Pid:     pid,
			Command: cl[0],
		}
		if len(cl) > 1 {
			lp.Arguments = strings.Join(cl[1:], "")
		}
		lps = append(lps, lp)
	}
	return lps, nil
}

func c_linux_processes(procs []*WatchedProc) (opentsdb.MultiDataPoint, error) {
	var md opentsdb.MultiDataPoint
	lps, err := getLinuxProccesses()
	if err != nil {
		return nil, nil
	}
	for _, w := range procs {
		w.Check(lps)
		if e := linuxProcMonitor(w, &md); e != nil {
			err = e
		}
	}
	return md, err
}
<commit_msg>cmd/scollector: Cheaper way to get fd count in Linux<commit_after>package collectors

import (
	"fmt"
	"io/ioutil"
	"os"

	"strconv"
	"strings"

	"bosun.org/metadata"
	"bosun.org/opentsdb"
)

func WatchProcesses(procs []*WatchedProc) error {
	collectors = append(collectors, &IntervalCollector{
		F: func() (opentsdb.MultiDataPoint, error) {
			return c_linux_processes(procs)
		},
		name: "c_linux_processes",
	})
	return nil
}

func linuxProcMonitor(w *WatchedProc, md *opentsdb.MultiDataPoint) error {
	var err error
	for pid, id := range w.Processes {
		stats_file, e := ioutil.ReadFile("/proc/" + pid + "/stat")
		if e != nil {
			w.Remove(pid)
			continue
		}
		io_file, e := ioutil.ReadFile("/proc/" + pid + "/io")
		if e != nil {
			w.Remove(pid)
			continue
		}
		limits, e := ioutil.ReadFile("/proc/" + pid + "/limits")
		if e != nil {
			w.Remove(pid)
			continue
		}
		fd_dir, e := os.Open("/proc/" + pid + "/fd")
		if e != nil {
			w.Remove(pid)
			continue
		}
		fds, e := fd_dir.Readdirnames(0)
		if e != nil {
			w.Remove(pid)
			continue
		}
		stats := strings.Fields(string(stats_file))
		if len(stats) < 24 {
			err = fmt.Errorf("stats too short")
			continue
		}
		var io []string
		for _, line := range strings.Split(string(io_file), "\n") {
			f := strings.Fields(line)
			if len(f) == 2 {
				io = append(io, f[1])
			}
		}
		if len(io) < 6 {
			err = fmt.Errorf("io too short")
			continue
		}
		tags := opentsdb.TagSet{"name": w.Name, "id": strconv.Itoa(id)}
		for _, line := range strings.Split(string(limits), "\n") {
			f := strings.Fields(line)
			if len(f) == 6 && strings.Join(f[0:3], " ") == "Max open files" {
				if f[3] != "unlimited" {
					Add(md, "linux.proc.num_fds_slim", f[3], tags, metadata.Gauge, metadata.Files, descLinuxSoftFileLimit)
					Add(md, "linux.proc.num_fds_hlim", f[4], tags, metadata.Gauge, metadata.Files, descLinuxHardFileLimit)
				}
			}
		}
		Add(md, "linux.proc.cpu", stats[13], opentsdb.TagSet{"type": "user"}.Merge(tags), metadata.Counter, metadata.Pct, descLinuxProcCPUUser)
		Add(md, "linux.proc.cpu", stats[14], opentsdb.TagSet{"type": "system"}.Merge(tags), metadata.Counter, metadata.Pct, descLinuxProcCPUSystem)
		Add(md, "linux.proc.mem.fault", stats[9], opentsdb.TagSet{"type": "minflt"}.Merge(tags), metadata.Counter, metadata.Fault, descLinuxProcMemFaultMin)
		Add(md, "linux.proc.mem.fault", stats[11], opentsdb.TagSet{"type": "majflt"}.Merge(tags), metadata.Counter, metadata.Fault, descLinuxProcMemFaultMax)
		Add(md, "linux.proc.mem.virtual", stats[22], tags, metadata.Gauge, metadata.Bytes, descLinuxProcMemVirtual)
		Add(md, "linux.proc.mem.rss", stats[23], tags, metadata.Gauge, metadata.Page, descLinuxProcMemRss)
		Add(md, "linux.proc.char_io", io[0], opentsdb.TagSet{"type": "read"}.Merge(tags), metadata.Counter, metadata.Bytes, descLinuxProcCharIoRead)
		Add(md, "linux.proc.char_io", io[1], opentsdb.TagSet{"type": "write"}.Merge(tags), metadata.Counter, metadata.Bytes, descLinuxProcCharIoWrite)
		Add(md, "linux.proc.syscall", io[2], opentsdb.TagSet{"type": "read"}.Merge(tags), metadata.Counter, metadata.Syscall, descLinuxProcSyscallRead)
		Add(md, "linux.proc.syscall", io[3], opentsdb.TagSet{"type": "write"}.Merge(tags), metadata.Counter, metadata.Syscall, descLinuxProcSyscallWrite)
		Add(md, "linux.proc.io_bytes", io[4], opentsdb.TagSet{"type": "read"}.Merge(tags), metadata.Counter, metadata.Bytes, descLinuxProcIoBytesRead)
		Add(md, "linux.proc.io_bytes", io[5], opentsdb.TagSet{"type": "write"}.Merge(tags), metadata.Counter, metadata.Bytes, descLinuxProcIoBytesWrite)
		Add(md, "linux.proc.num_fds", len(fds), tags, metadata.Gauge, metadata.Files, descLinuxProcFd)
	}
	return err
}

const (
	descLinuxProcCPUUser      = "The amount of time that this process has been scheduled in user mode."
	descLinuxProcCPUSystem    = "The amount of time that this process has been scheduled in kernel mode"
	descLinuxProcMemFaultMin  = "The number of minor faults the process has made which have not required loading a memory page from disk."
	descLinuxProcMemFaultMax  = "The number of major faults the process has made which have required loading a memory page from disk."
	descLinuxProcMemVirtual   = "The virtual memory size."
	descLinuxProcMemRss       = "The resident set size (number of pages the process has in real memory."
	descLinuxProcCharIoRead   = "The number of bytes which this task has caused to be read from storage. This is simply the sum of bytes which this process passed to read(2) and similar system calls. It includes things such as terminal I/O and is unaffected by whether or not actual physical disk I/O was required (the read might have been satisfied from pagecache)"
	descLinuxProcCharIoWrite  = "The number of bytes which this task has caused, or shall cause to be written to disk. Similar caveats apply here as with read."
	descLinuxProcSyscallRead  = "An attempt to count the number of read I/O operations—that is, system calls such as read(2) and pread(2)."
	descLinuxProcSyscallWrite = "Attempt to count the number of write I/O operations—that is, system calls such as write(2) and pwrite(2)."
	descLinuxProcIoBytesRead  = "An attempt to count the number of bytes which this process really did cause to be fetched from the storage layer. This is accurate for block-backed filesystems."
	descLinuxProcIoBytesWrite = "An Attempt to count the number of bytes which this process caused to be sent to the storage layer."
	descLinuxProcFd           = "The number of open file descriptors."
	descLinuxSoftFileLimit    = "The soft limit on the number of open file descriptors."
	descLinuxHardFileLimit    = "The hard limit on the number of open file descriptors."
)

func getLinuxProccesses() ([]*Process, error) {
	files, err := ioutil.ReadDir("/proc")
	if err != nil {
		return nil, err
	}
	var pids []string
	for _, f := range files {
		if _, err := strconv.Atoi(f.Name()); err == nil && f.IsDir() {
			pids = append(pids, f.Name())
		}
	}
	var lps []*Process
	for _, pid := range pids {
		cmdline, err := ioutil.ReadFile("/proc/" + pid + "/cmdline")
		if err != nil {
			//Continue because the pid might not exist any more
			continue
		}
		cl := strings.Split(string(cmdline), "\x00")
		if len(cl) < 1 || len(cl[0]) == 0 {
			continue
		}
		lp := &Process{
			Pid:     pid,
			Command: cl[0],
		}
		if len(cl) > 1 {
			lp.Arguments = strings.Join(cl[1:], "")
		}
		lps = append(lps, lp)
	}
	return lps, nil
}

func c_linux_processes(procs []*WatchedProc) (opentsdb.MultiDataPoint, error) {
	var md opentsdb.MultiDataPoint
	lps, err := getLinuxProccesses()
	if err != nil {
		return nil, nil
	}
	for _, w := range procs {
		w.Check(lps)
		if e := linuxProcMonitor(w, &md); e != nil {
			err = e
		}
	}
	return md, err
}
<|endoftext|>


<commit_before>// Copyright 2016 The Ebiten 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 main

import (
	"fmt"
	"image/color"
	"log"
	"math"

	"github.com/hajimehoshi/ebiten"
	"github.com/hajimehoshi/ebiten/ebitenutil"
	"github.com/hajimehoshi/ebiten/examples/common"
)

const (
	screenWidth  = 320
	screenHeight = 240
	maxAngle     = 256
	maxLean      = 16
)

var (
	skyColor     = color.RGBA{0x66, 0xcc, 0xff, 0xff}
	thePlayer    = &player{}
	gophersImage *ebiten.Image
	groundImage  *ebiten.Image
)

type player struct {
	x16   int
	y16   int
	angle int
	lean  int
}

func round(x float64) float64 {
	return math.Floor(x + 0.5)
}

func (p *player) MoveForward() {
	w, h := gophersImage.Size()
	mx := w * 16
	my := h * 16
	s, c := math.Sincos(float64(p.angle) * 2 * math.Pi / maxAngle)
	p.x16 += int(round(16 * c))
	p.y16 += int(round(16 * s))
	for mx <= p.x16 {
		p.x16 -= mx
	}
	for my <= p.y16 {
		p.y16 -= my
	}
	for p.x16 < 0 {
		p.x16 += mx
	}
	for p.y16 < 0 {
		p.y16 += my
	}
}

func (p *player) RotateRight() {
	p.angle++
	if maxAngle <= p.angle {
		p.angle -= maxAngle
	}
	p.lean++
	if maxLean < p.lean {
		p.lean = maxLean
	}
}

func (p *player) RotateLeft() {
	p.angle--
	if p.angle < 0 {
		p.angle += maxAngle
	}
	p.lean--
	if p.lean < -maxLean {
		p.lean = -maxLean
	}
}

func (p *player) Stabilize() {
	if 0 < p.lean {
		p.lean--
	}
	if p.lean < 0 {
		p.lean++
	}
}

func (p *player) Position() (int, int) {
	return p.x16, p.y16
}

func (p *player) Angle() int {
	return p.angle
}

func updateGroundImage(ground *ebiten.Image) error {
	if err := ground.Clear(); err != nil {
		return nil
	}
	x16, y16 := thePlayer.Position()
	a := thePlayer.Angle()
	gw, gh := ground.Size()
	w, h := gophersImage.Size()
	for j := -1; j <= 1; j++ {
		for i := -1; i <= 1; i++ {
			op := &ebiten.DrawImageOptions{}
			op.GeoM.Translate(float64(-x16)/16, float64(-y16)/16)
			op.GeoM.Translate(float64(w*i), float64(h*j))
			op.GeoM.Rotate(float64(-a)*2*math.Pi/maxAngle + math.Pi*3.0/2.0)
			op.GeoM.Translate(float64(gw)/2, float64(gh))
			if err := ground.DrawImage(gophersImage, op); err != nil {
				return err
			}
		}
	}
	return nil
}

type groundParts struct {
	image *ebiten.Image
}

func (g *groundParts) Len() int {
	_, h := g.image.Size()
	return h
}

func (g *groundParts) Src(i int) (int, int, int, int) {
	w, _ := g.image.Size()
	r := 60 - (i+10)/4
	return w/2 - r, i, w/2 + r, i + 1
}

func (g *groundParts) Dst(i int) (int, int, int, int) {
	w, _ := g.image.Size()
	return 0, i, w, i + 1
}

func drawGroundImage(screen *ebiten.Image, ground *ebiten.Image) error {
	w, _ := ground.Size()
	op := &ebiten.DrawImageOptions{}
	op.GeoM.Translate(-float64(w)/2, 0)
	op.GeoM.Rotate(-1 * float64(thePlayer.lean) / maxLean * math.Pi / 8)
	op.GeoM.Translate(float64(w)/2, 0)
	op.GeoM.Translate(float64(screenWidth-w)/2, screenHeight/2)
	op.ImageParts = &groundParts{ground}
	if err := screen.DrawImage(ground, op); err != nil {
		return err
	}
	return nil
}

func update(screen *ebiten.Image) error {
	if ebiten.IsKeyPressed(ebiten.KeySpace) {
		thePlayer.MoveForward()
	}
	rotated := false
	if ebiten.IsKeyPressed(ebiten.KeyRight) {
		thePlayer.RotateRight()
		rotated = true
	}
	if ebiten.IsKeyPressed(ebiten.KeyLeft) {
		thePlayer.RotateLeft()
		rotated = true
	}
	if !rotated {
		thePlayer.Stabilize()
	}

	if err := screen.Fill(skyColor); err != nil {
		return err
	}
	if err := updateGroundImage(groundImage); err != nil {
		return err
	}
	if err := drawGroundImage(screen, groundImage); err != nil {
		return err
	}

	tutrial := "Space: Move foward\nLeft/Right: Rotate"
	msg := fmt.Sprintf("FPS: %0.2f\n%s", ebiten.CurrentFPS(), tutrial)
	if err := ebitenutil.DebugPrint(screen, msg); err != nil {
		return err
	}
	return nil
}

func main() {
	var err error
	gophersImage, _, err = common.AssetImage("gophers.jpg", ebiten.FilterNearest)
	if err != nil {
		log.Fatal(err)
	}
	groundImage, err = ebiten.NewImage(screenWidth+70, screenHeight/2+50, ebiten.FilterNearest)
	if err != nil {
		log.Fatal(err)
	}
	if err := ebiten.Run(update, screenWidth, screenHeight, 2, "Air Ship (Ebiten Demo)"); err != nil {
		log.Fatal(err)
	}
}
<commit_msg>examples/airship: Remove unexpected blue spots<commit_after>// Copyright 2016 The Ebiten 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 main

import (
	"fmt"
	"image/color"
	"log"
	"math"

	"github.com/hajimehoshi/ebiten"
	"github.com/hajimehoshi/ebiten/ebitenutil"
	"github.com/hajimehoshi/ebiten/examples/common"
)

const (
	screenWidth  = 320
	screenHeight = 240
	maxAngle     = 256
	maxLean      = 16
)

var (
	skyColor  = color.RGBA{0x66, 0xcc, 0xff, 0xff}
	thePlayer = &player{
		x16:   16 * 100,
		y16:   16 * 200,
		angle: maxAngle * 3 / 4,
	}
	gophersImage *ebiten.Image
	groundImage  *ebiten.Image
)

type player struct {
	x16   int
	y16   int
	angle int
	lean  int
}

func round(x float64) float64 {
	return math.Floor(x + 0.5)
}

func (p *player) MoveForward() {
	w, h := gophersImage.Size()
	mx := w * 16
	my := h * 16
	s, c := math.Sincos(float64(p.angle) * 2 * math.Pi / maxAngle)
	p.x16 += int(round(16 * c))
	p.y16 += int(round(16 * s))
	for mx <= p.x16 {
		p.x16 -= mx
	}
	for my <= p.y16 {
		p.y16 -= my
	}
	for p.x16 < 0 {
		p.x16 += mx
	}
	for p.y16 < 0 {
		p.y16 += my
	}
}

func (p *player) RotateRight() {
	p.angle++
	if maxAngle <= p.angle {
		p.angle -= maxAngle
	}
	p.lean++
	if maxLean < p.lean {
		p.lean = maxLean
	}
}

func (p *player) RotateLeft() {
	p.angle--
	if p.angle < 0 {
		p.angle += maxAngle
	}
	p.lean--
	if p.lean < -maxLean {
		p.lean = -maxLean
	}
}

func (p *player) Stabilize() {
	if 0 < p.lean {
		p.lean--
	}
	if p.lean < 0 {
		p.lean++
	}
}

func (p *player) Position() (int, int) {
	return p.x16, p.y16
}

func (p *player) Angle() int {
	return p.angle
}

func updateGroundImage(ground *ebiten.Image) error {
	x16, y16 := thePlayer.Position()
	a := thePlayer.Angle()
	gw, gh := ground.Size()
	w, h := gophersImage.Size()
	for j := -1; j <= 1; j++ {
		for i := -1; i <= 1; i++ {
			op := &ebiten.DrawImageOptions{}
			op.GeoM.Translate(float64(-x16)/16, float64(-y16)/16)
			op.GeoM.Translate(float64(w*i), float64(h*j))
			op.GeoM.Rotate(float64(-a)*2*math.Pi/maxAngle + math.Pi*3.0/2.0)
			op.GeoM.Translate(float64(gw)/2, float64(gh))
			if err := ground.DrawImage(gophersImage, op); err != nil {
				return err
			}
		}
	}
	return nil
}

type groundParts struct {
	image *ebiten.Image
}

func (g *groundParts) Len() int {
	_, h := g.image.Size()
	return h
}

func (g *groundParts) Src(i int) (int, int, int, int) {
	w, _ := g.image.Size()
	return 0, i, w, i + 1
}

func (g *groundParts) Dst(i int) (int, int, int, int) {
	w, _ := g.image.Size()
	r := 30 + i*2
	// TODO: If the last value is i + 1, there would be unexpected spots on the screen.
	// i + 1 should work.
	return -r, i, w + r, i + 2
}

func drawGroundImage(screen *ebiten.Image, ground *ebiten.Image) error {
	w, _ := ground.Size()
	op := &ebiten.DrawImageOptions{}
	op.GeoM.Translate(-float64(w)/2, 0)
	op.GeoM.Rotate(-1 * float64(thePlayer.lean) / maxLean * math.Pi / 8)
	op.GeoM.Translate(float64(w)/2, 0)
	op.GeoM.Translate(float64(screenWidth-w)/2, screenHeight/2)
	op.ImageParts = &groundParts{ground}
	if err := screen.DrawImage(ground, op); err != nil {
		return err
	}
	return nil
}

func update(screen *ebiten.Image) error {
	if ebiten.IsKeyPressed(ebiten.KeySpace) {
		thePlayer.MoveForward()
	}
	rotated := false
	if ebiten.IsKeyPressed(ebiten.KeyRight) {
		thePlayer.RotateRight()
		rotated = true
	}
	if ebiten.IsKeyPressed(ebiten.KeyLeft) {
		thePlayer.RotateLeft()
		rotated = true
	}
	if !rotated {
		thePlayer.Stabilize()
	}

	if err := screen.Fill(skyColor); err != nil {
		return err
	}
	if err := updateGroundImage(groundImage); err != nil {
		return err
	}
	if err := drawGroundImage(screen, groundImage); err != nil {
		return err
	}

	tutrial := "Space: Move foward\nLeft/Right: Rotate"
	msg := fmt.Sprintf("FPS: %0.2f\n%s", ebiten.CurrentFPS(), tutrial)
	if err := ebitenutil.DebugPrint(screen, msg); err != nil {
		return err
	}
	return nil
}

func main() {
	var err error
	gophersImage, _, err = common.AssetImage("gophers.jpg", ebiten.FilterNearest)
	if err != nil {
		log.Fatal(err)
	}
	groundImage, err = ebiten.NewImage(screenWidth+70, screenHeight/2+50, ebiten.FilterNearest)
	if err != nil {
		log.Fatal(err)
	}
	if err := ebiten.Run(update, screenWidth, screenHeight, 2, "Air Ship (Ebiten Demo)"); err != nil {
		log.Fatal(err)
	}
}
<|endoftext|>


<commit_before>package agent

import (
	"bufio"
	"context"
	"math/rand"
	"os"
	"path/filepath"
	"strings"
	"time"

	"github.com/opencontainers/runc/libcontainer/system"
	"github.com/rancher/k3s/pkg/daemons/config"
	"github.com/sirupsen/logrus"
	"k8s.io/apimachinery/pkg/util/net"
	"k8s.io/component-base/logs"
	app2 "k8s.io/kubernetes/cmd/kube-proxy/app"
	"k8s.io/kubernetes/cmd/kubelet/app"

	_ "k8s.io/kubernetes/pkg/client/metrics/prometheus" // for client metric registration
	_ "k8s.io/kubernetes/pkg/version/prometheus"        // for version metric registration
)

func Agent(config *config.Agent) error {
	rand.Seed(time.Now().UTC().UnixNano())

	kubelet(config)
	kubeProxy(config)

	return nil
}

func kubeProxy(cfg *config.Agent) {
	argsMap := map[string]string{
		"proxy-mode":           "iptables",
		"healthz-bind-address": "127.0.0.1",
		"kubeconfig":           cfg.KubeConfig,
		"cluster-cidr":         cfg.ClusterCIDR.String(),
	}
	args := config.GetArgsList(argsMap, cfg.ExtraKubeProxyArgs)

	command := app2.NewProxyCommand()
	command.SetArgs(args)
	go func() {
		err := command.Execute()
		logrus.Fatalf("kube-proxy exited: %v", err)
	}()
}

func kubelet(cfg *config.Agent) {
	command := app.NewKubeletCommand(context.Background().Done())
	logs.InitLogs()
	defer logs.FlushLogs()

	argsMap := map[string]string{
		"healthz-bind-address":     "127.0.0.1",
		"read-only-port":           "0",
		"allow-privileged":         "true",
		"cluster-domain":           "cluster.local",
		"kubeconfig":               cfg.KubeConfig,
		"eviction-hard":            "imagefs.available<5%,nodefs.available<5%",
		"eviction-minimum-reclaim": "imagefs.available=10%,nodefs.available=10%",
		"fail-swap-on":             "false",
		//"cgroup-root": "/k3s",
		"cgroup-driver":                "cgroupfs",
		"authentication-token-webhook": "true",
	}
	if cfg.RootDir != "" {
		argsMap["root-dir"] = cfg.RootDir
		argsMap["cert-dir"] = filepath.Join(cfg.RootDir, "pki")
		argsMap["seccomp-profile-root"] = filepath.Join(cfg.RootDir, "seccomp")
	}
	if cfg.CNIConfDir != "" {
		argsMap["cni-conf-dir"] = cfg.CNIConfDir
	}
	if cfg.CNIBinDir != "" {
		argsMap["cni-bin-dir"] = cfg.CNIBinDir
	}
	if len(cfg.ClusterDNS) > 0 {
		argsMap["cluster-dns"] = cfg.ClusterDNS.String()
	}
	if cfg.ResolvConf != "" {
		argsMap["resolv-conf"] = cfg.ResolvConf
	}
	if cfg.RuntimeSocket != "" {
		argsMap["container-runtime"] = "remote"
		argsMap["container-runtime-endpoint"] = cfg.RuntimeSocket
		argsMap["serialize-image-pulls"] = "false"
	}
	if cfg.ListenAddress != "" {
		argsMap["address"] = cfg.ListenAddress
	}
	if cfg.CACertPath != "" {
		argsMap["anonymous-auth"] = "false"
		argsMap["client-ca-file"] = cfg.CACertPath
	}
	if cfg.NodeName != "" {
		argsMap["hostname-override"] = cfg.NodeName
	}
	defaultIP, err := net.ChooseHostInterface()
	if err != nil || defaultIP.String() != cfg.NodeIP {
		argsMap["node-ip"] = cfg.NodeIP
	}
	root, hasCFS := checkCgroups()
	if !hasCFS {
		logrus.Warn("Disabling CPU quotas due to missing cpu.cfs_period_us")
		argsMap["cpu-cfs-quota"] = "false"
	}
	if root != "" {
		argsMap["runtime-cgroups"] = root
		argsMap["kubelet-cgroups"] = root
	}
	if system.RunningInUserNS() {
		argsMap["feature-gates"] = "DevicePlugins=false"
	}

	args := config.GetArgsList(argsMap, cfg.ExtraKubeletArgs)
	command.SetArgs(args)

	go func() {
		logrus.Infof("Running kubelet %s", config.ArgString(args))
		logrus.Fatalf("kubelet exited: %v", command.Execute())
	}()
}

func checkCgroups() (string, bool) {
	f, err := os.Open("/proc/self/cgroup")
	if err != nil {
		return "", false
	}
	defer f.Close()

	ret := false
	root := ""
	scan := bufio.NewScanner(f)
	for scan.Scan() {
		parts := strings.Split(scan.Text(), ":")
		if len(parts) < 3 {
			continue
		}
		systems := strings.Split(parts[1], ",")
		for _, system := range systems {
			if system == "cpu" {
				p := filepath.Join("/sys/fs/cgroup", parts[1], parts[2], "cpu.cfs_period_us")
				if _, err := os.Stat(p); err == nil {
					ret = true
				}
			} else if system == "name=systemd" {
				last := parts[len(parts)-1]
				i := strings.LastIndex(last, ".slice")
				if i > 0 {
					root = "/systemd" + last[:i+len(".slice")]
				}
			}
		}
	}

	return root, ret
}
<commit_msg>Check for cgroup pids support<commit_after>package agent

import (
	"bufio"
	"context"
	"math/rand"
	"os"
	"path/filepath"
	"strings"
	"time"

	"github.com/opencontainers/runc/libcontainer/system"
	"github.com/rancher/k3s/pkg/daemons/config"
	"github.com/sirupsen/logrus"
	"k8s.io/apimachinery/pkg/util/net"
	"k8s.io/component-base/logs"
	app2 "k8s.io/kubernetes/cmd/kube-proxy/app"
	"k8s.io/kubernetes/cmd/kubelet/app"

	_ "k8s.io/kubernetes/pkg/client/metrics/prometheus" // for client metric registration
	_ "k8s.io/kubernetes/pkg/version/prometheus"        // for version metric registration
)

func Agent(config *config.Agent) error {
	rand.Seed(time.Now().UTC().UnixNano())

	kubelet(config)
	kubeProxy(config)

	return nil
}

func kubeProxy(cfg *config.Agent) {
	argsMap := map[string]string{
		"proxy-mode":           "iptables",
		"healthz-bind-address": "127.0.0.1",
		"kubeconfig":           cfg.KubeConfig,
		"cluster-cidr":         cfg.ClusterCIDR.String(),
	}
	args := config.GetArgsList(argsMap, cfg.ExtraKubeProxyArgs)

	command := app2.NewProxyCommand()
	command.SetArgs(args)
	go func() {
		err := command.Execute()
		logrus.Fatalf("kube-proxy exited: %v", err)
	}()
}

func kubelet(cfg *config.Agent) {
	command := app.NewKubeletCommand(context.Background().Done())
	logs.InitLogs()
	defer logs.FlushLogs()

	argsMap := map[string]string{
		"healthz-bind-address":     "127.0.0.1",
		"read-only-port":           "0",
		"allow-privileged":         "true",
		"cluster-domain":           "cluster.local",
		"kubeconfig":               cfg.KubeConfig,
		"eviction-hard":            "imagefs.available<5%,nodefs.available<5%",
		"eviction-minimum-reclaim": "imagefs.available=10%,nodefs.available=10%",
		"fail-swap-on":             "false",
		//"cgroup-root": "/k3s",
		"cgroup-driver":                "cgroupfs",
		"authentication-token-webhook": "true",
	}
	if cfg.RootDir != "" {
		argsMap["root-dir"] = cfg.RootDir
		argsMap["cert-dir"] = filepath.Join(cfg.RootDir, "pki")
		argsMap["seccomp-profile-root"] = filepath.Join(cfg.RootDir, "seccomp")
	}
	if cfg.CNIConfDir != "" {
		argsMap["cni-conf-dir"] = cfg.CNIConfDir
	}
	if cfg.CNIBinDir != "" {
		argsMap["cni-bin-dir"] = cfg.CNIBinDir
	}
	if len(cfg.ClusterDNS) > 0 {
		argsMap["cluster-dns"] = cfg.ClusterDNS.String()
	}
	if cfg.ResolvConf != "" {
		argsMap["resolv-conf"] = cfg.ResolvConf
	}
	if cfg.RuntimeSocket != "" {
		argsMap["container-runtime"] = "remote"
		argsMap["container-runtime-endpoint"] = cfg.RuntimeSocket
		argsMap["serialize-image-pulls"] = "false"
	}
	if cfg.ListenAddress != "" {
		argsMap["address"] = cfg.ListenAddress
	}
	if cfg.CACertPath != "" {
		argsMap["anonymous-auth"] = "false"
		argsMap["client-ca-file"] = cfg.CACertPath
	}
	if cfg.NodeName != "" {
		argsMap["hostname-override"] = cfg.NodeName
	}
	defaultIP, err := net.ChooseHostInterface()
	if err != nil || defaultIP.String() != cfg.NodeIP {
		argsMap["node-ip"] = cfg.NodeIP
	}
	root, hasCFS, hasPIDs := checkCgroups()
	if !hasCFS {
		logrus.Warn("Disabling CPU quotas due to missing cpu.cfs_period_us")
		argsMap["cpu-cfs-quota"] = "false"
	}
	if !hasPIDs {
		logrus.Warn("Disabling pod PIDs limit feature due to missing cgroup pids support")
		argsMap["cgroups-per-qos"] = "false"
		argsMap["enforce-node-allocatable"] = ""
		argsMap["feature-gates"] = addFeatureGate(argsMap["feature-gates"], "SupportPodPidsLimit=false")
	}
	if root != "" {
		argsMap["runtime-cgroups"] = root
		argsMap["kubelet-cgroups"] = root
	}
	if system.RunningInUserNS() {
		argsMap["feature-gates"] = addFeatureGate(argsMap["feature-gates"], "DevicePlugins=false")
	}

	args := config.GetArgsList(argsMap, cfg.ExtraKubeletArgs)
	command.SetArgs(args)

	go func() {
		logrus.Infof("Running kubelet %s", config.ArgString(args))
		logrus.Fatalf("kubelet exited: %v", command.Execute())
	}()
}

func addFeatureGate(current, new string) string {
	if current == "" {
		return new
	}
	return current + "," + new
}

func checkCgroups() (root string, hasCFS bool, hasPIDs bool) {
	f, err := os.Open("/proc/self/cgroup")
	if err != nil {
		return "", false, false
	}
	defer f.Close()

	scan := bufio.NewScanner(f)
	for scan.Scan() {
		parts := strings.Split(scan.Text(), ":")
		if len(parts) < 3 {
			continue
		}
		systems := strings.Split(parts[1], ",")
		for _, system := range systems {
			if system == "pids" {
				hasPIDs = true
			} else if system == "cpu" {
				p := filepath.Join("/sys/fs/cgroup", parts[1], parts[2], "cpu.cfs_period_us")
				if _, err := os.Stat(p); err == nil {
					hasCFS = true
				}
			} else if system == "name=systemd" {
				last := parts[len(parts)-1]
				i := strings.LastIndex(last, ".slice")
				if i > 0 {
					root = "/systemd" + last[:i+len(".slice")]
				}
			}
		}
	}

	return root, hasCFS, hasPIDs
}
<|endoftext|>


<commit_before>/*
   Copyright 2020 Docker Compose CLI 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 e2e

import (
	"fmt"
	"strings"
	"testing"

	testify "github.com/stretchr/testify/assert"
	"gotest.tools/v3/assert"
)

func TestStartStop(t *testing.T) {
	c := NewParallelE2eCLI(t, binDir)
	const projectName = "e2e-start-stop"

	getProjectRegx := func(status string) string {
		// match output with random spaces like:
		// e2e-start-stop      running(3)
		return fmt.Sprintf("%s\\s+%s\\(%d\\)", projectName, status, 2)
	}

	getServiceRegx := func(service string, status string) string {
		// match output with random spaces like:
		// e2e-start-stop-db-1      "echo hello"       db          running
		return fmt.Sprintf("%s-%s-1.+%s\\s+%s", projectName, service, service, status)
	}

	t.Run("Up a project", func(t *testing.T) {
		res := c.RunDockerComposeCmd("-f", "./fixtures/start-stop/compose.yaml", "--project-name", projectName, "up", "-d")
		assert.Assert(t, strings.Contains(res.Combined(), "Container e2e-start-stop-simple-1  Started"), res.Combined())

		res = c.RunDockerComposeCmd("ls", "--all")
		testify.Regexp(t, getProjectRegx("running"), res.Stdout())

		res = c.RunDockerComposeCmd("--project-name", projectName, "ps")
		testify.Regexp(t, getServiceRegx("simple", "running"), res.Stdout())
		testify.Regexp(t, getServiceRegx("another", "running"), res.Stdout())
	})

	t.Run("stop project", func(t *testing.T) {
		c.RunDockerComposeCmd("-f", "./fixtures/start-stop/compose.yaml", "--project-name", projectName, "stop")

		res := c.RunDockerComposeCmd("ls")
		assert.Assert(t, !strings.Contains(res.Combined(), "e2e-start-stop"), res.Combined())

		res = c.RunDockerComposeCmd("ls", "--all")
		testify.Regexp(t, getProjectRegx("exited"), res.Stdout())

		res = c.RunDockerComposeCmd("--project-name", projectName, "ps")
		assert.Assert(t, !strings.Contains(res.Combined(), "e2e-start-stop-words-1"), res.Combined())

		res = c.RunDockerComposeCmd("--project-name", projectName, "ps", "--all")
		testify.Regexp(t, getServiceRegx("simple", "exited"), res.Stdout())
		testify.Regexp(t, getServiceRegx("another", "exited"), res.Stdout())
	})

	t.Run("start project", func(t *testing.T) {
		c.RunDockerComposeCmd("-f", "./fixtures/start-stop/compose.yaml", "--project-name", projectName, "start")

		res := c.RunDockerComposeCmd("ls")
		testify.Regexp(t, getProjectRegx("running"), res.Stdout())
	})

	t.Run("pause project", func(t *testing.T) {
		c.RunDockerComposeCmd("-f", "./fixtures/start-stop/compose.yaml", "--project-name", projectName, "pause")

		res := c.RunDockerComposeCmd("ls", "--all")
		testify.Regexp(t, getProjectRegx("paused"), res.Stdout())
	})

	t.Run("unpause project", func(t *testing.T) {
		c.RunDockerComposeCmd("-f", "./fixtures/start-stop/compose.yaml", "--project-name", projectName, "unpause")

		res := c.RunDockerComposeCmd("ls")
		testify.Regexp(t, getProjectRegx("running"), res.Stdout())
	})

	t.Run("down", func(t *testing.T) {
		_ = c.RunDockerComposeCmd("--project-name", projectName, "down")
	})
}

func TestStartStopWithDependencies(t *testing.T) {
	c := NewParallelE2eCLI(t, binDir)
	const projectName = "e2e-start-stop-with-dependencies"

	defer c.RunDockerComposeCmd("--project-name", projectName, "rm", "-fsv")

	t.Run("Up", func(t *testing.T) {
		res := c.RunDockerComposeCmd("-f", "./fixtures/dependencies/compose.yaml", "--project-name", projectName, "up", "-d")
		assert.Assert(t, strings.Contains(res.Combined(), "Container e2e-start-stop-with-dependencies-foo-1  Started"), res.Combined())
		assert.Assert(t, strings.Contains(res.Combined(), "Container e2e-start-stop-with-dependencies-bar-1  Started"), res.Combined())
	})

	t.Run("stop foo", func(t *testing.T) {
		res := c.RunDockerComposeCmd("--project-name", projectName, "stop", "foo")

		assert.Assert(t, strings.Contains(res.Combined(), "Container e2e-start-stop-with-dependencies-foo-1  Stopped"), res.Combined())

		res = c.RunDockerComposeCmd("--project-name", projectName, "ps", "--status", "running")
		assert.Assert(t, strings.Contains(res.Combined(), "e2e-start-stop-with-dependencies-bar-1"), res.Combined())
		assert.Assert(t, !strings.Contains(res.Combined(), "e2e-start-stop-with-dependencies-foo-1"), res.Combined())
	})

	t.Run("start foo", func(t *testing.T) {
		res := c.RunDockerComposeCmd("--project-name", projectName, "stop")
		assert.Assert(t, strings.Contains(res.Combined(), "Container e2e-start-stop-with-dependencies-bar-1  Stopped"), res.Combined())

		res = c.RunDockerComposeCmd("--project-name", projectName, "start", "foo")
		assert.Assert(t, strings.Contains(res.Combined(), "Container e2e-start-stop-with-dependencies-bar-1  Started"), res.Combined())
		assert.Assert(t, strings.Contains(res.Combined(), "Container e2e-start-stop-with-dependencies-foo-1  Started"), res.Combined())

		res = c.RunDockerComposeCmd("--project-name", projectName, "ps", "--status", "running")
		assert.Assert(t, strings.Contains(res.Combined(), "e2e-start-stop-with-dependencies-bar-1"), res.Combined())
		assert.Assert(t, strings.Contains(res.Combined(), "e2e-start-stop-with-dependencies-foo-1"), res.Combined())
	})

	t.Run("down", func(t *testing.T) {
		_ = c.RunDockerComposeCmd("--project-name", projectName, "down")
	})
}
<commit_msg>fix race condition on start-stop e2e tests running in parrallel<commit_after>/*
   Copyright 2020 Docker Compose CLI 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 e2e

import (
	"fmt"
	"strings"
	"testing"

	testify "github.com/stretchr/testify/assert"
	"gotest.tools/v3/assert"
)

func TestStartStop(t *testing.T) {
	c := NewParallelE2eCLI(t, binDir)
	const projectName = "e2e-start-stop-no-dependencies"

	getProjectRegx := func(status string) string {
		// match output with random spaces like:
		// e2e-start-stop      running(3)
		return fmt.Sprintf("%s\\s+%s\\(%d\\)", projectName, status, 2)
	}

	getServiceRegx := func(service string, status string) string {
		// match output with random spaces like:
		// e2e-start-stop-db-1      "echo hello"       db          running
		return fmt.Sprintf("%s-%s-1.+%s\\s+%s", projectName, service, service, status)
	}

	t.Run("Up a project", func(t *testing.T) {
		res := c.RunDockerComposeCmd("-f", "./fixtures/start-stop/compose.yaml", "--project-name", projectName, "up", "-d")
		assert.Assert(t, strings.Contains(res.Combined(), "Container e2e-start-stop-no-dependencies-simple-1  Started"), res.Combined())

		res = c.RunDockerComposeCmd("ls", "--all")
		testify.Regexp(t, getProjectRegx("running"), res.Stdout())

		res = c.RunDockerComposeCmd("--project-name", projectName, "ps")
		testify.Regexp(t, getServiceRegx("simple", "running"), res.Stdout())
		testify.Regexp(t, getServiceRegx("another", "running"), res.Stdout())
	})

	t.Run("stop project", func(t *testing.T) {
		c.RunDockerComposeCmd("-f", "./fixtures/start-stop/compose.yaml", "--project-name", projectName, "stop")

		res := c.RunDockerComposeCmd("ls")
		assert.Assert(t, !strings.Contains(res.Combined(), "e2e-start-stop-no-dependencies"), res.Combined())

		res = c.RunDockerComposeCmd("ls", "--all")
		testify.Regexp(t, getProjectRegx("exited"), res.Stdout())

		res = c.RunDockerComposeCmd("--project-name", projectName, "ps")
		assert.Assert(t, !strings.Contains(res.Combined(), "e2e-start-stop-no-dependencies-words-1"), res.Combined())

		res = c.RunDockerComposeCmd("--project-name", projectName, "ps", "--all")
		testify.Regexp(t, getServiceRegx("simple", "exited"), res.Stdout())
		testify.Regexp(t, getServiceRegx("another", "exited"), res.Stdout())
	})

	t.Run("start project", func(t *testing.T) {
		c.RunDockerComposeCmd("-f", "./fixtures/start-stop/compose.yaml", "--project-name", projectName, "start")

		res := c.RunDockerComposeCmd("ls")
		testify.Regexp(t, getProjectRegx("running"), res.Stdout())
	})

	t.Run("pause project", func(t *testing.T) {
		c.RunDockerComposeCmd("-f", "./fixtures/start-stop/compose.yaml", "--project-name", projectName, "pause")

		res := c.RunDockerComposeCmd("ls", "--all")
		testify.Regexp(t, getProjectRegx("paused"), res.Stdout())
	})

	t.Run("unpause project", func(t *testing.T) {
		c.RunDockerComposeCmd("-f", "./fixtures/start-stop/compose.yaml", "--project-name", projectName, "unpause")

		res := c.RunDockerComposeCmd("ls")
		testify.Regexp(t, getProjectRegx("running"), res.Stdout())
	})

	t.Run("down", func(t *testing.T) {
		_ = c.RunDockerComposeCmd("--project-name", projectName, "down")
	})
}

func TestStartStopWithDependencies(t *testing.T) {
	c := NewParallelE2eCLI(t, binDir)
	const projectName = "e2e-start-stop-with-dependencies"

	defer c.RunDockerComposeCmd("--project-name", projectName, "rm", "-fsv")

	t.Run("Up", func(t *testing.T) {
		res := c.RunDockerComposeCmd("-f", "./fixtures/dependencies/compose.yaml", "--project-name", projectName, "up", "-d")
		assert.Assert(t, strings.Contains(res.Combined(), "Container e2e-start-stop-with-dependencies-foo-1  Started"), res.Combined())
		assert.Assert(t, strings.Contains(res.Combined(), "Container e2e-start-stop-with-dependencies-bar-1  Started"), res.Combined())
	})

	t.Run("stop foo", func(t *testing.T) {
		res := c.RunDockerComposeCmd("--project-name", projectName, "stop", "foo")

		assert.Assert(t, strings.Contains(res.Combined(), "Container e2e-start-stop-with-dependencies-foo-1  Stopped"), res.Combined())

		res = c.RunDockerComposeCmd("--project-name", projectName, "ps", "--status", "running")
		assert.Assert(t, strings.Contains(res.Combined(), "e2e-start-stop-with-dependencies-bar-1"), res.Combined())
		assert.Assert(t, !strings.Contains(res.Combined(), "e2e-start-stop-with-dependencies-foo-1"), res.Combined())
	})

	t.Run("start foo", func(t *testing.T) {
		res := c.RunDockerComposeCmd("--project-name", projectName, "stop")
		assert.Assert(t, strings.Contains(res.Combined(), "Container e2e-start-stop-with-dependencies-bar-1  Stopped"), res.Combined())

		res = c.RunDockerComposeCmd("--project-name", projectName, "start", "foo")
		assert.Assert(t, strings.Contains(res.Combined(), "Container e2e-start-stop-with-dependencies-bar-1  Started"), res.Combined())
		assert.Assert(t, strings.Contains(res.Combined(), "Container e2e-start-stop-with-dependencies-foo-1  Started"), res.Combined())

		res = c.RunDockerComposeCmd("--project-name", projectName, "ps", "--status", "running")
		assert.Assert(t, strings.Contains(res.Combined(), "e2e-start-stop-with-dependencies-bar-1"), res.Combined())
		assert.Assert(t, strings.Contains(res.Combined(), "e2e-start-stop-with-dependencies-foo-1"), res.Combined())
	})

	t.Run("down", func(t *testing.T) {
		_ = c.RunDockerComposeCmd("--project-name", projectName, "down")
	})
}
<|endoftext|>


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

package fileutil

import (
	"io"
	"os"
	"syscall"
)

// This used to call syscall.Flock() but that call fails with EBADF on NFS.
// An alternative is lockf() which works on NFS but that call lets a process lock
// the same file twice. Instead, use Linux's non-standard open file descriptor
// locks which will block if the process already holds the file lock.
//
// constants from /usr/include/bits/fcntl-linux.h
const (
	F_OFD_GETLK  = 37
	F_OFD_SETLK  = 37
	F_OFD_SETLKW = 38
)

var (
	wrlck = syscall.Flock_t{
		Type:   syscall.F_WRLCK,
		Whence: int16(io.SeekStart),
		Start:  0,
		Len:    0,
	}

	linuxTryLockFile = flockTryLockFile
	linuxLockFile    = flockLockFile
)

func init() {
	// use open file descriptor locks if the system supports it
	getlk := syscall.Flock_t{Type: syscall.F_RDLCK}
	if err := syscall.FcntlFlock(0, F_OFD_GETLK, &getlk); err == nil {
		linuxTryLockFile = ofdTryLockFile
		linuxLockFile = ofdLockFile
	}
}

func TryLockFile(path string, flag int, perm os.FileMode) (*LockedFile, error) {
	return linuxTryLockFile(path, flag, perm)
}

func ofdTryLockFile(path string, flag int, perm os.FileMode) (*LockedFile, error) {
	f, err := os.OpenFile(path, flag, perm)
	if err != nil {
		return nil, err
	}

	flock := wrlck
	if err = syscall.FcntlFlock(f.Fd(), F_OFD_SETLK, &flock); err != nil {
		f.Close()
		if err == syscall.EWOULDBLOCK {
			err = ErrLocked
		}
		return nil, err
	}
	return &LockedFile{f}, nil
}

func LockFile(path string, flag int, perm os.FileMode) (*LockedFile, error) {
	return linuxLockFile(path, flag, perm)
}

func ofdLockFile(path string, flag int, perm os.FileMode) (*LockedFile, error) {
	f, err := os.OpenFile(path, flag, perm)
	if err != nil {
		return nil, err
	}

	flock := wrlck
	err = syscall.FcntlFlock(f.Fd(), F_OFD_SETLKW, &flock)

	if err != nil {
		f.Close()
		return nil, err
	}
	return &LockedFile{f}, err
}
<commit_msg>pkg/fileutil: clarify flock errors<commit_after>// Copyright 2016 The etcd 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

package fileutil

import (
	"fmt"
	"io"
	"os"
	"syscall"
)

// This used to call syscall.Flock() but that call fails with EBADF on NFS.
// An alternative is lockf() which works on NFS but that call lets a process lock
// the same file twice. Instead, use Linux's non-standard open file descriptor
// locks which will block if the process already holds the file lock.
//
// constants from /usr/include/bits/fcntl-linux.h
const (
	F_OFD_GETLK  = 37
	F_OFD_SETLK  = 37
	F_OFD_SETLKW = 38
)

var (
	wrlck = syscall.Flock_t{
		Type:   syscall.F_WRLCK,
		Whence: int16(io.SeekStart),
		Start:  0,
		Len:    0,
	}

	linuxTryLockFile = flockTryLockFile
	linuxLockFile    = flockLockFile
)

func init() {
	// use open file descriptor locks if the system supports it
	getlk := syscall.Flock_t{Type: syscall.F_RDLCK}
	if err := syscall.FcntlFlock(0, F_OFD_GETLK, &getlk); err == nil {
		linuxTryLockFile = ofdTryLockFile
		linuxLockFile = ofdLockFile
	}
}

func TryLockFile(path string, flag int, perm os.FileMode) (*LockedFile, error) {
	return linuxTryLockFile(path, flag, perm)
}

func ofdTryLockFile(path string, flag int, perm os.FileMode) (*LockedFile, error) {
	f, err := os.OpenFile(path, flag, perm)
	if err != nil {
		return nil, fmt.Errorf("ofdTryLockFile failed to open %q (%v)", path, err)
	}

	flock := wrlck
	if err = syscall.FcntlFlock(f.Fd(), F_OFD_SETLK, &flock); err != nil {
		f.Close()
		if err == syscall.EWOULDBLOCK {
			err = ErrLocked
		}
		return nil, err
	}
	return &LockedFile{f}, nil
}

func LockFile(path string, flag int, perm os.FileMode) (*LockedFile, error) {
	return linuxLockFile(path, flag, perm)
}

func ofdLockFile(path string, flag int, perm os.FileMode) (*LockedFile, error) {
	f, err := os.OpenFile(path, flag, perm)
	if err != nil {
		return nil, fmt.Errorf("ofdLockFile failed to open %q (%v)", path, err)
	}

	flock := wrlck
	err = syscall.FcntlFlock(f.Fd(), F_OFD_SETLKW, &flock)
	if err != nil {
		f.Close()
		return nil, err
	}
	return &LockedFile{f}, nil
}
<|endoftext|>


<commit_before>package github

import (
	"net/http"
	"sync"

	"github.com/dgrijalva/jwt-go"
	"github.com/google/go-github/github"
	"github.com/hellofresh/janus/pkg/config"
	"github.com/hellofresh/janus/pkg/jwt/provider"
	"github.com/pkg/errors"
)

func init() {
	provider.Register("github", &Provider{})
}

// Provider abstracts the authentication for github
type Provider struct {
	provider.Verifier

	teams  []Team
	config config.Credentials
}

// Build acts like the constructor for a provider
func (gp *Provider) Build(config config.Credentials) provider.Provider {
	client := NewClient()
	teams := gp.teamConfigsToTeam(config.Github.Teams)

	return &Provider{
		Verifier: provider.NewVerifierBasket(
			NewTeamVerifier(teams, client),
			NewOrganizationVerifier(config.Github.Organizations, client),
		),
		teams:  teams,
		config: config,
	}
}

// GetClaims returns a JWT Map Claim
func (gp *Provider) GetClaims(httpClient *http.Client) (jwt.MapClaims, error) {
	client := NewClient()

	var (
		wg            sync.WaitGroup
		user          *github.User
		usersOrgTeams OrganizationTeams
		errs          []error
	)

	wg.Add(2)
	go func(user *github.User, client Client, httpClient *http.Client) {
		defer wg.Done()
		user, err := client.CurrentUser(httpClient)
		if err != nil {
			errs = append(errs, errors.Wrap(err, "failed to get github users"))
		}
	}(user, client, httpClient)

	go func(usersOrgTeams OrganizationTeams, client Client, httpClient *http.Client) {
		defer wg.Done()
		usersOrgTeams, err := client.Teams(httpClient)
		if err != nil {
			errs = append(errs, errors.Wrap(err, "failed to get github teams"))
		}
	}(usersOrgTeams, client, httpClient)

	wg.Wait()

	if len(errs) > 0 {
		return nil, errs[0]
	}

	return jwt.MapClaims{
		"sub":      *user.Login,
		"is_admin": gp.isAdmin(usersOrgTeams),
	}, nil
}

func (gp *Provider) teamConfigsToTeam(configTeams map[string]string) []Team {
	teams := []Team{}
	for org, team := range configTeams {
		teams = append(teams, Team{
			Name:         team,
			Organization: org,
		})
	}
	return teams
}

func (gp *Provider) isAdmin(usersOrgTeams OrganizationTeams) bool {
	for _, team := range gp.teams {
		if teams, ok := usersOrgTeams[team.Organization]; ok {
			for _, teamUserBelongsTo := range teams {
				if teamUserBelongsTo == gp.config.JanusAdminTeam {
					return true
				}
			}
		}
	}
	return false
}
<commit_msg>Combine errors message<commit_after>package github

import (
	"net/http"
	"strings"
	"sync"

	"github.com/dgrijalva/jwt-go"
	"github.com/google/go-github/github"
	"github.com/hellofresh/janus/pkg/config"
	"github.com/hellofresh/janus/pkg/jwt/provider"
	"github.com/pkg/errors"
)

func init() {
	provider.Register("github", &Provider{})
}

// Provider abstracts the authentication for github
type Provider struct {
	provider.Verifier

	teams  []Team
	config config.Credentials
}

// Build acts like the constructor for a provider
func (gp *Provider) Build(config config.Credentials) provider.Provider {
	client := NewClient()
	teams := gp.teamConfigsToTeam(config.Github.Teams)

	return &Provider{
		Verifier: provider.NewVerifierBasket(
			NewTeamVerifier(teams, client),
			NewOrganizationVerifier(config.Github.Organizations, client),
		),
		teams:  teams,
		config: config,
	}
}

// GetClaims returns a JWT Map Claim
func (gp *Provider) GetClaims(httpClient *http.Client) (jwt.MapClaims, error) {
	client := NewClient()

	var (
		wg            sync.WaitGroup
		user          *github.User
		usersOrgTeams OrganizationTeams
		errs          []string
	)

	wg.Add(2)
	go func() {
		defer wg.Done()
		var err error
		user, err = client.CurrentUser(httpClient)
		if err != nil {
			errs = append(errs, "failed to get github users")
		}
	}()

	go func() {
		defer wg.Done()
		var err error
		usersOrgTeams, err = client.Teams(httpClient)
		if err != nil {
			errs = append(errs, "failed to get github teams")
		}
	}()
	wg.Wait()

	if len(errs) > 0 {
		return nil, errors.New(strings.Join(errs, ", "))
	}

	return jwt.MapClaims{
		"sub":      *user.Login,
		"is_admin": gp.isAdmin(usersOrgTeams),
	}, nil
}

func (gp *Provider) teamConfigsToTeam(configTeams map[string]string) []Team {
	teams := []Team{}
	for org, team := range configTeams {
		teams = append(teams, Team{
			Name:         team,
			Organization: org,
		})
	}
	return teams
}

func (gp *Provider) isAdmin(usersOrgTeams OrganizationTeams) bool {
	for _, team := range gp.teams {
		if teams, ok := usersOrgTeams[team.Organization]; ok {
			for _, teamUserBelongsTo := range teams {
				if teamUserBelongsTo == gp.config.JanusAdminTeam {
					return true
				}
			}
		}
	}
	return false
}
<|endoftext|>


<commit_before>package scripts

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

	"github.com/golang/glog"
	"github.com/openshift/source-to-image/pkg/api"
)

// Environment represents a single environment variable definition
type Environment struct {
	Name  string
	Value string
}

// GetEnvironment gets the .sti/environment file located in the sources and
// parse it into []environment
func GetEnvironment(config *api.Config) ([]Environment, error) {
	envPath := filepath.Join(config.WorkingDir, api.Source, ".sti", api.Environment)
	if _, err := os.Stat(envPath); os.IsNotExist(err) {
		return nil, errors.New("no evironment file found in application sources")
	}

	f, err := os.Open(envPath)
	if err != nil {
		return nil, errors.New("unable to read .sti/environment file")
	}
	defer f.Close()

	result := []Environment{}

	scanner := bufio.NewScanner(f)
	for scanner.Scan() {
		s := scanner.Text()
		// Allow for comments in environment file
		if strings.HasPrefix(s, "#") {
			continue
		}
		parts := strings.SplitN(s, "=", 2)
		if len(parts) != 2 {
			continue
		}
		e := Environment{
			Name:  strings.TrimSpace(parts[0]),
			Value: strings.TrimSpace(parts[1]),
		}
		glog.V(1).Infof("Setting '%s' to '%s'", e.Name, e.Value)
		result = append(result, e)
	}

	return result, scanner.Err()
}

// ConvertEnvironment converts the []Environment to "key=val" strings
func ConvertEnvironment(env []Environment) (result []string) {
	for _, e := range env {
		result = append(result, fmt.Sprintf("%s=%s", e.Name, e.Value))
	}
	return
}

// ConvertEnvironmentToDocker converts the []Environment into Dockerfile format
func ConvertEnvironmentToDocker(env []Environment) (result string) {
	for i, e := range env {
		if i == 0 {
			result += fmt.Sprintf("ENV %s=\"%s\"", e.Name, e.Value)
		} else {
			result += fmt.Sprintf(" \\\n\t%s=\"%s\"", e.Name, e.Value)
		}
	}
	result += "\n"
	return
}
<commit_msg>minor typo fix<commit_after>package scripts

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

	"github.com/golang/glog"
	"github.com/openshift/source-to-image/pkg/api"
)

// Environment represents a single environment variable definition
type Environment struct {
	Name  string
	Value string
}

// GetEnvironment gets the .sti/environment file located in the sources and
// parse it into []environment
func GetEnvironment(config *api.Config) ([]Environment, error) {
	envPath := filepath.Join(config.WorkingDir, api.Source, ".sti", api.Environment)
	if _, err := os.Stat(envPath); os.IsNotExist(err) {
		return nil, errors.New("no environment file found in application sources")
	}

	f, err := os.Open(envPath)
	if err != nil {
		return nil, errors.New("unable to read .sti/environment file")
	}
	defer f.Close()

	result := []Environment{}

	scanner := bufio.NewScanner(f)
	for scanner.Scan() {
		s := scanner.Text()
		// Allow for comments in environment file
		if strings.HasPrefix(s, "#") {
			continue
		}
		parts := strings.SplitN(s, "=", 2)
		if len(parts) != 2 {
			continue
		}
		e := Environment{
			Name:  strings.TrimSpace(parts[0]),
			Value: strings.TrimSpace(parts[1]),
		}
		glog.V(1).Infof("Setting '%s' to '%s'", e.Name, e.Value)
		result = append(result, e)
	}

	return result, scanner.Err()
}

// ConvertEnvironment converts the []Environment to "key=val" strings
func ConvertEnvironment(env []Environment) (result []string) {
	for _, e := range env {
		result = append(result, fmt.Sprintf("%s=%s", e.Name, e.Value))
	}
	return
}

// ConvertEnvironmentToDocker converts the []Environment into Dockerfile format
func ConvertEnvironmentToDocker(env []Environment) (result string) {
	for i, e := range env {
		if i == 0 {
			result += fmt.Sprintf("ENV %s=\"%s\"", e.Name, e.Value)
		} else {
			result += fmt.Sprintf(" \\\n\t%s=\"%s\"", e.Name, e.Value)
		}
	}
	result += "\n"
	return
}
<|endoftext|>


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

package control

import (
	"bytes"
	"encoding/json"
	"fmt"
	"sort"
	"text/tabwriter"
	"time"

	"gvisor.googlesource.com/gvisor/pkg/abi/linux"
	"gvisor.googlesource.com/gvisor/pkg/sentry/fs"
	"gvisor.googlesource.com/gvisor/pkg/sentry/fs/host"
	"gvisor.googlesource.com/gvisor/pkg/sentry/kernel"
	"gvisor.googlesource.com/gvisor/pkg/sentry/kernel/auth"
	"gvisor.googlesource.com/gvisor/pkg/sentry/kernel/kdefs"
	ktime "gvisor.googlesource.com/gvisor/pkg/sentry/kernel/time"
	"gvisor.googlesource.com/gvisor/pkg/sentry/limits"
	"gvisor.googlesource.com/gvisor/pkg/sentry/usage"
	"gvisor.googlesource.com/gvisor/pkg/urpc"
)

// Proc includes task-related functions.
//
// At the moment, this is limited to exec support.
type Proc struct {
	Kernel *kernel.Kernel
}

// ExecArgs is the set of arguments to exec.
type ExecArgs struct {
	// Filename is the filename to load.
	//
	// If this is provided as "", then the file will be guessed via Argv[0].
	Filename string `json:"filename"`

	// Argv is a list of arguments.
	Argv []string `json:"argv"`

	// Envv is a list of environment variables.
	Envv []string `json:"envv"`

	// Root defines the root directory for the new process. A reference on
	// Root must be held for the lifetime of the ExecArgs. If Root is nil,
	// it will default to the VFS root.
	Root *fs.Dirent

	// WorkingDirectory defines the working directory for the new process.
	WorkingDirectory string `json:"wd"`

	// KUID is the UID to run with in the root user namespace. Defaults to
	// root if not set explicitly.
	KUID auth.KUID

	// KGID is the GID to run with in the root user namespace. Defaults to
	// the root group if not set explicitly.
	KGID auth.KGID

	// ExtraKGIDs is the list of additional groups to which the user
	// belongs.
	ExtraKGIDs []auth.KGID

	// Capabilities is the list of capabilities to give to the process.
	Capabilities *auth.TaskCapabilities

	// StdioIsPty indicates that FDs 0, 1, and 2 are connected to a host
	// pty fd.
	StdioIsPty bool

	// FilePayload determines the files to give to the new process.
	urpc.FilePayload

	// ContainerID is the container for the process being executed.
	ContainerID string
}

// Exec runs a new task.
func (proc *Proc) Exec(args *ExecArgs, waitStatus *uint32) error {
	newTG, _, err := proc.execAsync(args)
	if err != nil {
		return err
	}

	// Wait for completion.
	newTG.WaitExited()
	*waitStatus = newTG.ExitStatus().Status()
	return nil
}

// ExecAsync runs a new task, but doesn't wait for it to finish. It is defined
// as a function rather than a method to avoid exposing execAsync as an RPC.
func ExecAsync(proc *Proc, args *ExecArgs) (*kernel.ThreadGroup, kernel.ThreadID, error) {
	return proc.execAsync(args)
}

// execAsync runs a new task, but doesn't wait for it to finish. It returns the
// newly created thread group and its PID.
func (proc *Proc) execAsync(args *ExecArgs) (*kernel.ThreadGroup, kernel.ThreadID, error) {
	// Import file descriptors.
	l := limits.NewLimitSet()
	fdm := proc.Kernel.NewFDMap()
	defer fdm.DecRef()

	creds := auth.NewUserCredentials(
		args.KUID,
		args.KGID,
		args.ExtraKGIDs,
		args.Capabilities,
		proc.Kernel.RootUserNamespace())

	initArgs := kernel.CreateProcessArgs{
		Filename:                args.Filename,
		Argv:                    args.Argv,
		Envv:                    args.Envv,
		WorkingDirectory:        args.WorkingDirectory,
		Root:                    args.Root,
		Credentials:             creds,
		FDMap:                   fdm,
		Umask:                   0022,
		Limits:                  l,
		MaxSymlinkTraversals:    linux.MaxSymlinkTraversals,
		UTSNamespace:            proc.Kernel.RootUTSNamespace(),
		IPCNamespace:            proc.Kernel.RootIPCNamespace(),
		AbstractSocketNamespace: proc.Kernel.RootAbstractSocketNamespace(),
		ContainerID:             args.ContainerID,
	}
	if initArgs.Root != nil {
		// initArgs must hold a reference on Root. This ref is dropped
		// in CreateProcess.
		initArgs.Root.IncRef()
	}
	ctx := initArgs.NewContext(proc.Kernel)

	if initArgs.Filename == "" {
		// Get the full path to the filename from the PATH env variable.
		paths := fs.GetPath(initArgs.Envv)
		f, err := proc.Kernel.RootMountNamespace().ResolveExecutablePath(ctx, initArgs.WorkingDirectory, initArgs.Argv[0], paths)
		if err != nil {
			return nil, 0, fmt.Errorf("error finding executable %q in PATH %v: %v", initArgs.Argv[0], paths, err)
		}
		initArgs.Filename = f
	}

	mounter := fs.FileOwnerFromContext(ctx)
	for appFD, f := range args.FilePayload.Files {
		enableIoctl := args.StdioIsPty && appFD <= 2

		// Import the given file FD. This dups the FD as well.
		file, err := host.ImportFile(ctx, int(f.Fd()), mounter, enableIoctl)
		if err != nil {
			return nil, 0, err
		}
		defer file.DecRef()

		// We're done with this file.
		f.Close()

		if err := fdm.NewFDAt(kdefs.FD(appFD), file, kernel.FDFlags{}, l); err != nil {
			return nil, 0, err
		}
	}

	return proc.Kernel.CreateProcess(initArgs)
}

// PsArgs is the set of arguments to ps.
type PsArgs struct {
	// JSON will force calls to Ps to return the result as a JSON payload.
	JSON bool
}

// Ps provides a process listing for the running kernel.
func (proc *Proc) Ps(args *PsArgs, out *string) error {
	var p []*Process
	if e := Processes(proc.Kernel, "", &p); e != nil {
		return e
	}
	if !args.JSON {
		*out = ProcessListToTable(p)
	} else {
		s, e := ProcessListToJSON(p)
		if e != nil {
			return e
		}
		*out = s
	}
	return nil
}

// Process contains information about a single process in a Sandbox.
// TODO: Implement TTY field.
type Process struct {
	UID auth.KUID       `json:"uid"`
	PID kernel.ThreadID `json:"pid"`
	// Parent PID
	PPID kernel.ThreadID `json:"ppid"`
	// Processor utilization
	C int32 `json:"c"`
	// Start time
	STime string `json:"stime"`
	// CPU time
	Time string `json:"time"`
	// Executable shortname (e.g. "sh" for /bin/sh)
	Cmd string `json:"cmd"`
}

// ProcessListToTable prints a table with the following format:
// UID       PID       PPID      C         STIME     TIME       CMD
// 0         1         0         0         14:04     505262ns   tail
func ProcessListToTable(pl []*Process) string {
	var buf bytes.Buffer
	tw := tabwriter.NewWriter(&buf, 10, 1, 3, ' ', 0)
	fmt.Fprint(tw, "UID\tPID\tPPID\tC\tSTIME\tTIME\tCMD")
	for _, d := range pl {
		fmt.Fprintf(tw, "\n%d\t%d\t%d\t%d\t%s\t%s\t%s",
			d.UID,
			d.PID,
			d.PPID,
			d.C,
			d.STime,
			d.Time,
			d.Cmd)
	}
	tw.Flush()
	return buf.String()
}

// ProcessListToJSON will return the JSON representation of ps.
func ProcessListToJSON(pl []*Process) (string, error) {
	b, err := json.Marshal(pl)
	if err != nil {
		return "", fmt.Errorf("couldn't marshal process list %v: %v", pl, err)
	}
	return string(b), nil
}

// PrintPIDsJSON prints a JSON object containing only the PIDs in pl. This
// behavior is the same as runc's.
func PrintPIDsJSON(pl []*Process) (string, error) {
	pids := make([]kernel.ThreadID, 0, len(pl))
	for _, d := range pl {
		pids = append(pids, d.PID)
	}
	b, err := json.Marshal(pids)
	if err != nil {
		return "", fmt.Errorf("couldn't marshal PIDs %v: %v", pids, err)
	}
	return string(b), nil
}

// Processes retrieves information about processes running in the sandbox with
// the given container id. All processes are returned if 'containerID' is empty.
func Processes(k *kernel.Kernel, containerID string, out *[]*Process) error {
	ts := k.TaskSet()
	now := k.RealtimeClock().Now()
	for _, tg := range ts.Root.ThreadGroups() {
		pid := ts.Root.IDOfThreadGroup(tg)
		// If tg has already been reaped ignore it.
		if pid == 0 {
			continue
		}
		if containerID != "" && containerID != tg.Leader().ContainerID() {
			continue
		}

		ppid := kernel.ThreadID(0)
		if tg.Leader().Parent() != nil {
			ppid = ts.Root.IDOfThreadGroup(tg.Leader().Parent().ThreadGroup())
		}
		*out = append(*out, &Process{
			UID:   tg.Leader().Credentials().EffectiveKUID,
			PID:   pid,
			PPID:  ppid,
			STime: formatStartTime(now, tg.Leader().StartTime()),
			C:     percentCPU(tg.CPUStats(), tg.Leader().StartTime(), now),
			Time:  tg.CPUStats().SysTime.String(),
			Cmd:   tg.Leader().Name(),
		})
	}
	sort.Slice(*out, func(i, j int) bool { return (*out)[i].PID < (*out)[j].PID })
	return nil
}

// formatStartTime formats startTime depending on the current time:
// - If startTime was today, HH:MM is used.
// - If startTime was not today but was this year, MonDD is used (e.g. Jan02)
// - If startTime was not this year, the year is used.
func formatStartTime(now, startTime ktime.Time) string {
	nowS, nowNs := now.Unix()
	n := time.Unix(nowS, nowNs)
	startTimeS, startTimeNs := startTime.Unix()
	st := time.Unix(startTimeS, startTimeNs)
	format := "15:04"
	if st.YearDay() != n.YearDay() {
		format = "Jan02"
	}
	if st.Year() != n.Year() {
		format = "2006"
	}
	return st.Format(format)
}

func percentCPU(stats usage.CPUStats, startTime, now ktime.Time) int32 {
	// Note: In procps, there is an option to include child CPU stats. As
	// it is disabled by default, we do not include them.
	total := stats.UserTime + stats.SysTime
	lifetime := now.Sub(startTime)
	if lifetime <= 0 {
		return 0
	}
	percentCPU := total * 100 / lifetime
	// Cap at 99% since procps does the same.
	if percentCPU > 99 {
		percentCPU = 99
	}
	return int32(percentCPU)
}
<commit_msg>Fix possible panic in control.Processes.<commit_after>// Copyright 2018 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package control

import (
	"bytes"
	"encoding/json"
	"fmt"
	"sort"
	"text/tabwriter"
	"time"

	"gvisor.googlesource.com/gvisor/pkg/abi/linux"
	"gvisor.googlesource.com/gvisor/pkg/sentry/fs"
	"gvisor.googlesource.com/gvisor/pkg/sentry/fs/host"
	"gvisor.googlesource.com/gvisor/pkg/sentry/kernel"
	"gvisor.googlesource.com/gvisor/pkg/sentry/kernel/auth"
	"gvisor.googlesource.com/gvisor/pkg/sentry/kernel/kdefs"
	ktime "gvisor.googlesource.com/gvisor/pkg/sentry/kernel/time"
	"gvisor.googlesource.com/gvisor/pkg/sentry/limits"
	"gvisor.googlesource.com/gvisor/pkg/sentry/usage"
	"gvisor.googlesource.com/gvisor/pkg/urpc"
)

// Proc includes task-related functions.
//
// At the moment, this is limited to exec support.
type Proc struct {
	Kernel *kernel.Kernel
}

// ExecArgs is the set of arguments to exec.
type ExecArgs struct {
	// Filename is the filename to load.
	//
	// If this is provided as "", then the file will be guessed via Argv[0].
	Filename string `json:"filename"`

	// Argv is a list of arguments.
	Argv []string `json:"argv"`

	// Envv is a list of environment variables.
	Envv []string `json:"envv"`

	// Root defines the root directory for the new process. A reference on
	// Root must be held for the lifetime of the ExecArgs. If Root is nil,
	// it will default to the VFS root.
	Root *fs.Dirent

	// WorkingDirectory defines the working directory for the new process.
	WorkingDirectory string `json:"wd"`

	// KUID is the UID to run with in the root user namespace. Defaults to
	// root if not set explicitly.
	KUID auth.KUID

	// KGID is the GID to run with in the root user namespace. Defaults to
	// the root group if not set explicitly.
	KGID auth.KGID

	// ExtraKGIDs is the list of additional groups to which the user
	// belongs.
	ExtraKGIDs []auth.KGID

	// Capabilities is the list of capabilities to give to the process.
	Capabilities *auth.TaskCapabilities

	// StdioIsPty indicates that FDs 0, 1, and 2 are connected to a host
	// pty fd.
	StdioIsPty bool

	// FilePayload determines the files to give to the new process.
	urpc.FilePayload

	// ContainerID is the container for the process being executed.
	ContainerID string
}

// Exec runs a new task.
func (proc *Proc) Exec(args *ExecArgs, waitStatus *uint32) error {
	newTG, _, err := proc.execAsync(args)
	if err != nil {
		return err
	}

	// Wait for completion.
	newTG.WaitExited()
	*waitStatus = newTG.ExitStatus().Status()
	return nil
}

// ExecAsync runs a new task, but doesn't wait for it to finish. It is defined
// as a function rather than a method to avoid exposing execAsync as an RPC.
func ExecAsync(proc *Proc, args *ExecArgs) (*kernel.ThreadGroup, kernel.ThreadID, error) {
	return proc.execAsync(args)
}

// execAsync runs a new task, but doesn't wait for it to finish. It returns the
// newly created thread group and its PID.
func (proc *Proc) execAsync(args *ExecArgs) (*kernel.ThreadGroup, kernel.ThreadID, error) {
	// Import file descriptors.
	l := limits.NewLimitSet()
	fdm := proc.Kernel.NewFDMap()
	defer fdm.DecRef()

	creds := auth.NewUserCredentials(
		args.KUID,
		args.KGID,
		args.ExtraKGIDs,
		args.Capabilities,
		proc.Kernel.RootUserNamespace())

	initArgs := kernel.CreateProcessArgs{
		Filename:                args.Filename,
		Argv:                    args.Argv,
		Envv:                    args.Envv,
		WorkingDirectory:        args.WorkingDirectory,
		Root:                    args.Root,
		Credentials:             creds,
		FDMap:                   fdm,
		Umask:                   0022,
		Limits:                  l,
		MaxSymlinkTraversals:    linux.MaxSymlinkTraversals,
		UTSNamespace:            proc.Kernel.RootUTSNamespace(),
		IPCNamespace:            proc.Kernel.RootIPCNamespace(),
		AbstractSocketNamespace: proc.Kernel.RootAbstractSocketNamespace(),
		ContainerID:             args.ContainerID,
	}
	if initArgs.Root != nil {
		// initArgs must hold a reference on Root. This ref is dropped
		// in CreateProcess.
		initArgs.Root.IncRef()
	}
	ctx := initArgs.NewContext(proc.Kernel)

	if initArgs.Filename == "" {
		// Get the full path to the filename from the PATH env variable.
		paths := fs.GetPath(initArgs.Envv)
		f, err := proc.Kernel.RootMountNamespace().ResolveExecutablePath(ctx, initArgs.WorkingDirectory, initArgs.Argv[0], paths)
		if err != nil {
			return nil, 0, fmt.Errorf("error finding executable %q in PATH %v: %v", initArgs.Argv[0], paths, err)
		}
		initArgs.Filename = f
	}

	mounter := fs.FileOwnerFromContext(ctx)
	for appFD, f := range args.FilePayload.Files {
		enableIoctl := args.StdioIsPty && appFD <= 2

		// Import the given file FD. This dups the FD as well.
		file, err := host.ImportFile(ctx, int(f.Fd()), mounter, enableIoctl)
		if err != nil {
			return nil, 0, err
		}
		defer file.DecRef()

		// We're done with this file.
		f.Close()

		if err := fdm.NewFDAt(kdefs.FD(appFD), file, kernel.FDFlags{}, l); err != nil {
			return nil, 0, err
		}
	}

	return proc.Kernel.CreateProcess(initArgs)
}

// PsArgs is the set of arguments to ps.
type PsArgs struct {
	// JSON will force calls to Ps to return the result as a JSON payload.
	JSON bool
}

// Ps provides a process listing for the running kernel.
func (proc *Proc) Ps(args *PsArgs, out *string) error {
	var p []*Process
	if e := Processes(proc.Kernel, "", &p); e != nil {
		return e
	}
	if !args.JSON {
		*out = ProcessListToTable(p)
	} else {
		s, e := ProcessListToJSON(p)
		if e != nil {
			return e
		}
		*out = s
	}
	return nil
}

// Process contains information about a single process in a Sandbox.
// TODO: Implement TTY field.
type Process struct {
	UID auth.KUID       `json:"uid"`
	PID kernel.ThreadID `json:"pid"`
	// Parent PID
	PPID kernel.ThreadID `json:"ppid"`
	// Processor utilization
	C int32 `json:"c"`
	// Start time
	STime string `json:"stime"`
	// CPU time
	Time string `json:"time"`
	// Executable shortname (e.g. "sh" for /bin/sh)
	Cmd string `json:"cmd"`
}

// ProcessListToTable prints a table with the following format:
// UID       PID       PPID      C         STIME     TIME       CMD
// 0         1         0         0         14:04     505262ns   tail
func ProcessListToTable(pl []*Process) string {
	var buf bytes.Buffer
	tw := tabwriter.NewWriter(&buf, 10, 1, 3, ' ', 0)
	fmt.Fprint(tw, "UID\tPID\tPPID\tC\tSTIME\tTIME\tCMD")
	for _, d := range pl {
		fmt.Fprintf(tw, "\n%d\t%d\t%d\t%d\t%s\t%s\t%s",
			d.UID,
			d.PID,
			d.PPID,
			d.C,
			d.STime,
			d.Time,
			d.Cmd)
	}
	tw.Flush()
	return buf.String()
}

// ProcessListToJSON will return the JSON representation of ps.
func ProcessListToJSON(pl []*Process) (string, error) {
	b, err := json.Marshal(pl)
	if err != nil {
		return "", fmt.Errorf("couldn't marshal process list %v: %v", pl, err)
	}
	return string(b), nil
}

// PrintPIDsJSON prints a JSON object containing only the PIDs in pl. This
// behavior is the same as runc's.
func PrintPIDsJSON(pl []*Process) (string, error) {
	pids := make([]kernel.ThreadID, 0, len(pl))
	for _, d := range pl {
		pids = append(pids, d.PID)
	}
	b, err := json.Marshal(pids)
	if err != nil {
		return "", fmt.Errorf("couldn't marshal PIDs %v: %v", pids, err)
	}
	return string(b), nil
}

// Processes retrieves information about processes running in the sandbox with
// the given container id. All processes are returned if 'containerID' is empty.
func Processes(k *kernel.Kernel, containerID string, out *[]*Process) error {
	ts := k.TaskSet()
	now := k.RealtimeClock().Now()
	for _, tg := range ts.Root.ThreadGroups() {
		pid := ts.Root.IDOfThreadGroup(tg)
		// If tg has already been reaped ignore it.
		if pid == 0 {
			continue
		}
		if containerID != "" && containerID != tg.Leader().ContainerID() {
			continue
		}

		ppid := kernel.ThreadID(0)
		if p := tg.Leader().Parent(); p != nil {
			ppid = ts.Root.IDOfThreadGroup(p.ThreadGroup())
		}
		*out = append(*out, &Process{
			UID:   tg.Leader().Credentials().EffectiveKUID,
			PID:   pid,
			PPID:  ppid,
			STime: formatStartTime(now, tg.Leader().StartTime()),
			C:     percentCPU(tg.CPUStats(), tg.Leader().StartTime(), now),
			Time:  tg.CPUStats().SysTime.String(),
			Cmd:   tg.Leader().Name(),
		})
	}
	sort.Slice(*out, func(i, j int) bool { return (*out)[i].PID < (*out)[j].PID })
	return nil
}

// formatStartTime formats startTime depending on the current time:
// - If startTime was today, HH:MM is used.
// - If startTime was not today but was this year, MonDD is used (e.g. Jan02)
// - If startTime was not this year, the year is used.
func formatStartTime(now, startTime ktime.Time) string {
	nowS, nowNs := now.Unix()
	n := time.Unix(nowS, nowNs)
	startTimeS, startTimeNs := startTime.Unix()
	st := time.Unix(startTimeS, startTimeNs)
	format := "15:04"
	if st.YearDay() != n.YearDay() {
		format = "Jan02"
	}
	if st.Year() != n.Year() {
		format = "2006"
	}
	return st.Format(format)
}

func percentCPU(stats usage.CPUStats, startTime, now ktime.Time) int32 {
	// Note: In procps, there is an option to include child CPU stats. As
	// it is disabled by default, we do not include them.
	total := stats.UserTime + stats.SysTime
	lifetime := now.Sub(startTime)
	if lifetime <= 0 {
		return 0
	}
	percentCPU := total * 100 / lifetime
	// Cap at 99% since procps does the same.
	if percentCPU > 99 {
		percentCPU = 99
	}
	return int32(percentCPU)
}
<|endoftext|>


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

package server

import (
	"io"
	"strings"
	"sync"

	"github.com/deepfabric/elasticell/pkg/log"
	"github.com/deepfabric/elasticell/pkg/pb/raftcmdpb"
	"github.com/deepfabric/elasticell/pkg/pool"
	"github.com/deepfabric/elasticell/pkg/raftstore"
	"github.com/deepfabric/elasticell/pkg/redis"
	"github.com/deepfabric/elasticell/pkg/util"
	"github.com/fagongzi/goetty"
)

// RedisServer is provide a redis like server
type RedisServer struct {
	sync.RWMutex

	store         *raftstore.Store
	s             *goetty.Server
	typeMapping   map[string]raftcmdpb.CMDType
	localHandlers map[raftcmdpb.CMDType]func(*raftcmdpb.Request, *session) ([]byte, error)
	handlers      map[raftcmdpb.CMDType]func(raftcmdpb.CMDType, redis.Command, *session) ([]byte, error)
	routing       *routing
}

// Start used for start the redis server
func (s *RedisServer) Start() error {
	return s.s.Start(s.doConnection)
}

// Stop is used for stop redis server
func (s *RedisServer) Stop() error {
	s.s.Stop()
	return nil
}

func (s *RedisServer) init() {
	s.routing = newRouting()
	s.localHandlers = make(map[raftcmdpb.CMDType]func(*raftcmdpb.Request, *session) ([]byte, error))
	s.handlers = make(map[raftcmdpb.CMDType]func(raftcmdpb.CMDType, redis.Command, *session) ([]byte, error))
	s.typeMapping = make(map[string]raftcmdpb.CMDType)

	for k, v := range raftcmdpb.CMDType_value {
		s.typeMapping[strings.ToLower(k)] = raftcmdpb.CMDType(v)
	}

	s.localHandlers[raftcmdpb.Ping] = s.onPing

	// server
	s.handlers[raftcmdpb.Del] = s.onDel

	// kv
	s.handlers[raftcmdpb.Set] = s.onSet
	s.handlers[raftcmdpb.Get] = s.onGet
	s.handlers[raftcmdpb.Incrby] = s.onIncrBy
	s.handlers[raftcmdpb.Incr] = s.onIncr
	s.handlers[raftcmdpb.Decrby] = s.onDecrby
	s.handlers[raftcmdpb.Decr] = s.onDecr
	s.handlers[raftcmdpb.GetSet] = s.onGetSet
	s.handlers[raftcmdpb.Append] = s.onAppend
	s.handlers[raftcmdpb.Setnx] = s.onSetNX
	s.handlers[raftcmdpb.StrLen] = s.onStrLen

	// hash
	s.handlers[raftcmdpb.HSet] = s.onHSet
	s.handlers[raftcmdpb.HDel] = s.onHDel
	s.handlers[raftcmdpb.HExists] = s.onHExists
	s.handlers[raftcmdpb.HGet] = s.onHGet
	s.handlers[raftcmdpb.HGetAll] = s.onHGetAll
	s.handlers[raftcmdpb.HIncrBy] = s.onHIncrBy
	s.handlers[raftcmdpb.HKeys] = s.onHKeys
	s.handlers[raftcmdpb.HLen] = s.onHLen
	s.handlers[raftcmdpb.HMGet] = s.onHMGet
	s.handlers[raftcmdpb.HMSet] = s.onHMSet
	s.handlers[raftcmdpb.HSetNX] = s.onHSetNX
	s.handlers[raftcmdpb.HStrLen] = s.onHStrLen
	s.handlers[raftcmdpb.HVals] = s.onHVals

	// list
	s.handlers[raftcmdpb.LIndex] = s.onLIndex
	s.handlers[raftcmdpb.LInsert] = s.onLInsert
	s.handlers[raftcmdpb.LLEN] = s.onLLen
	s.handlers[raftcmdpb.LPop] = s.onLPop
	s.handlers[raftcmdpb.LPush] = s.onLPush
	s.handlers[raftcmdpb.LPushX] = s.onLPushX
	s.handlers[raftcmdpb.LRange] = s.onLRange
	s.handlers[raftcmdpb.LRem] = s.onLRem
	s.handlers[raftcmdpb.LSet] = s.onLSet
	s.handlers[raftcmdpb.LTrim] = s.onLTrim
	s.handlers[raftcmdpb.RPop] = s.onRPop
	s.handlers[raftcmdpb.RPush] = s.onRPush
	s.handlers[raftcmdpb.RPushX] = s.onRPushX

	// sets
	s.handlers[raftcmdpb.SAdd] = s.onSAdd
	s.handlers[raftcmdpb.SCard] = s.onSCard
	s.handlers[raftcmdpb.SRem] = s.onSRem
	s.handlers[raftcmdpb.SMembers] = s.onSMembers
	s.handlers[raftcmdpb.SIsMember] = s.onSIsMember
	s.handlers[raftcmdpb.SPop] = s.onSPop

	// zset
	s.handlers[raftcmdpb.ZAdd] = s.onZAdd
	s.handlers[raftcmdpb.ZCard] = s.onZCard
	s.handlers[raftcmdpb.ZCount] = s.onZCount
	s.handlers[raftcmdpb.ZIncrBy] = s.onZIncrBy
	s.handlers[raftcmdpb.ZLexCount] = s.onZLexCount
	s.handlers[raftcmdpb.ZRange] = s.onZRange
	s.handlers[raftcmdpb.ZRangeByLex] = s.onZRangeByLex
	s.handlers[raftcmdpb.ZRangeByScore] = s.onZRangeByScore
	s.handlers[raftcmdpb.ZRank] = s.onZRank
	s.handlers[raftcmdpb.ZRem] = s.onZRem
	s.handlers[raftcmdpb.ZRemRangeByLex] = s.onZRemRangeByLex
	s.handlers[raftcmdpb.ZRemRangeByRank] = s.onZRemRangeByRank
	s.handlers[raftcmdpb.ZRemRangeByScore] = s.onZRemRangeByScore
	s.handlers[raftcmdpb.ZScore] = s.onZScore
}

func (s *RedisServer) doConnection(session goetty.IOSession) error {
	addr := session.RemoteAddr()
	log.Debugf("redis-[%s]: connected", addr)

	// every client has 2 goroutines, read and write
	rs := newSession(session)
	s.routing.put(rs.id, rs)

	go rs.writeLoop()
	defer func() {
		s.routing.delete(rs.id)
		rs.close()
	}()

	for {
		value, err := session.Read()
		if err != nil {
			if err == io.EOF {
				return nil
			}

			log.Errorf("redis-[%s]: read from cli failed, errors\n %+v",
				addr,
				err)
			return err
		}

		if req, ok := value.(redis.Command); ok {
			log.Debugf("redis-[%s]: read a redis command, cmd=<%+v>", addr, req)

			err = s.onRedisCommand(req, rs)
			if err != nil {
				rsp := pool.AcquireResponse()
				rsp.ErrorResult = util.StringToSlice(err.Error())
				rs.onResp(rsp)
			}
		} else if req, ok := value.(*raftcmdpb.Request); ok {
			if len(req.UUID) > 0 {
				log.Debugf("req: read a raft req. from=<%s>, uuid=<%d>, cmd=<%s>",
					addr,
					req.UUID,
					req.Cmd)
			}

			rs.setFromProxy()
			err = s.onProxyReq(req, rs)
			if err != nil {
				rsp := pool.AcquireResponse()
				rsp.ErrorResult = util.StringToSlice(err.Error())
				rs.onResp(rsp)
				pool.ReleaseRequest(req)
			}
		}
	}
}

func (s *RedisServer) onResp(resp *raftcmdpb.RaftCMDResponse) {
	var errorResult []byte
	hasError := resp.Header != nil

	for _, rsp := range resp.Responses {
		rs := s.routing.get(rsp.SessionID)
		if rs != nil {
			if hasError {
				if resp.Header.Error.RaftEntryTooLarge == nil {
					rsp.Type = raftcmdpb.RaftError
				} else {
					rsp.Type = raftcmdpb.Invalid
				}

				if errorResult == nil {
					errorResult = util.MustMarshal(resp.Header)
				}

				rsp.ErrorResult = errorResult
			}

			rs.onResp(rsp)
		} else {
			pool.ReleaseResponse(rsp)
		}
	}

	pool.ReleaseRaftCMDResponse(resp)
}

func (s *RedisServer) onProxyReq(req *raftcmdpb.Request, session *session) error {
	req.Type = s.typeMapping[strings.ToLower(util.SliceToString(req.Cmd[0]))]
	req.SessionID = session.id

	if h, ok := s.localHandlers[req.Type]; ok {
		h(req, session)
		return nil
	}

	if len(req.Cmd) < 2 {
		rsp := pool.AcquireResponse()
		rsp.UUID = req.UUID
		rsp.ErrorResult = redis.ErrNotSupportCommand
		session.onResp(rsp)
		return nil
	}

	err := s.store.OnProxyReq(req, s.onResp)
	if err != nil {
		return err
	}

	return nil
}

func (s *RedisServer) onRedisCommand(cmd redis.Command, session *session) error {
	t := s.typeMapping[cmd.CmdString()]

	if h, ok := s.localHandlers[t]; ok {
		req := pool.AcquireRequest()
		req.Cmd = cmd
		h(req, session)
		return nil
	}

	h, ok := s.handlers[t]
	if !ok {
		rsp := pool.AcquireResponse()
		rsp.ErrorResult = redis.ErrNotSupportCommand
		session.onResp(rsp)
		return nil
	}

	_, err := h(t, cmd, session)
	if err != nil {
		return err
	}

	return nil
}

func (s *RedisServer) onDel(cmdType raftcmdpb.CMDType, cmd redis.Command, session *session) ([]byte, error) {
	args := cmd.Args()
	if len(args) != 1 {
		rsp := pool.AcquireResponse()
		rsp.ErrorResult = redis.ErrInvalidCommandResp
		session.onResp(rsp)
		return nil, nil
	}

	return s.store.OnRedisCommand(session.id, cmdType, cmd, s.onResp)
}

func (s *RedisServer) onPing(req *raftcmdpb.Request, session *session) ([]byte, error) {
	rsp := pool.AcquireResponse()
	rsp.UUID = req.UUID
	rsp.StatusResult = redis.PongResp
	session.onResp(rsp)
	return nil, nil
}
<commit_msg>fix: missing UUID when resp error<commit_after>// Copyright 2016 DeepFabric, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// See the License for the specific language governing permissions and
// limitations under the License.

package server

import (
	"io"
	"strings"
	"sync"

	"github.com/deepfabric/elasticell/pkg/log"
	"github.com/deepfabric/elasticell/pkg/pb/raftcmdpb"
	"github.com/deepfabric/elasticell/pkg/pool"
	"github.com/deepfabric/elasticell/pkg/raftstore"
	"github.com/deepfabric/elasticell/pkg/redis"
	"github.com/deepfabric/elasticell/pkg/util"
	"github.com/fagongzi/goetty"
)

// RedisServer is provide a redis like server
type RedisServer struct {
	sync.RWMutex

	store         *raftstore.Store
	s             *goetty.Server
	typeMapping   map[string]raftcmdpb.CMDType
	localHandlers map[raftcmdpb.CMDType]func(*raftcmdpb.Request, *session) ([]byte, error)
	handlers      map[raftcmdpb.CMDType]func(raftcmdpb.CMDType, redis.Command, *session) ([]byte, error)
	routing       *routing
}

// Start used for start the redis server
func (s *RedisServer) Start() error {
	return s.s.Start(s.doConnection)
}

// Stop is used for stop redis server
func (s *RedisServer) Stop() error {
	s.s.Stop()
	return nil
}

func (s *RedisServer) init() {
	s.routing = newRouting()
	s.localHandlers = make(map[raftcmdpb.CMDType]func(*raftcmdpb.Request, *session) ([]byte, error))
	s.handlers = make(map[raftcmdpb.CMDType]func(raftcmdpb.CMDType, redis.Command, *session) ([]byte, error))
	s.typeMapping = make(map[string]raftcmdpb.CMDType)

	for k, v := range raftcmdpb.CMDType_value {
		s.typeMapping[strings.ToLower(k)] = raftcmdpb.CMDType(v)
	}

	s.localHandlers[raftcmdpb.Ping] = s.onPing

	// server
	s.handlers[raftcmdpb.Del] = s.onDel

	// kv
	s.handlers[raftcmdpb.Set] = s.onSet
	s.handlers[raftcmdpb.Get] = s.onGet
	s.handlers[raftcmdpb.Incrby] = s.onIncrBy
	s.handlers[raftcmdpb.Incr] = s.onIncr
	s.handlers[raftcmdpb.Decrby] = s.onDecrby
	s.handlers[raftcmdpb.Decr] = s.onDecr
	s.handlers[raftcmdpb.GetSet] = s.onGetSet
	s.handlers[raftcmdpb.Append] = s.onAppend
	s.handlers[raftcmdpb.Setnx] = s.onSetNX
	s.handlers[raftcmdpb.StrLen] = s.onStrLen

	// hash
	s.handlers[raftcmdpb.HSet] = s.onHSet
	s.handlers[raftcmdpb.HDel] = s.onHDel
	s.handlers[raftcmdpb.HExists] = s.onHExists
	s.handlers[raftcmdpb.HGet] = s.onHGet
	s.handlers[raftcmdpb.HGetAll] = s.onHGetAll
	s.handlers[raftcmdpb.HIncrBy] = s.onHIncrBy
	s.handlers[raftcmdpb.HKeys] = s.onHKeys
	s.handlers[raftcmdpb.HLen] = s.onHLen
	s.handlers[raftcmdpb.HMGet] = s.onHMGet
	s.handlers[raftcmdpb.HMSet] = s.onHMSet
	s.handlers[raftcmdpb.HSetNX] = s.onHSetNX
	s.handlers[raftcmdpb.HStrLen] = s.onHStrLen
	s.handlers[raftcmdpb.HVals] = s.onHVals

	// list
	s.handlers[raftcmdpb.LIndex] = s.onLIndex
	s.handlers[raftcmdpb.LInsert] = s.onLInsert
	s.handlers[raftcmdpb.LLEN] = s.onLLen
	s.handlers[raftcmdpb.LPop] = s.onLPop
	s.handlers[raftcmdpb.LPush] = s.onLPush
	s.handlers[raftcmdpb.LPushX] = s.onLPushX
	s.handlers[raftcmdpb.LRange] = s.onLRange
	s.handlers[raftcmdpb.LRem] = s.onLRem
	s.handlers[raftcmdpb.LSet] = s.onLSet
	s.handlers[raftcmdpb.LTrim] = s.onLTrim
	s.handlers[raftcmdpb.RPop] = s.onRPop
	s.handlers[raftcmdpb.RPush] = s.onRPush
	s.handlers[raftcmdpb.RPushX] = s.onRPushX

	// sets
	s.handlers[raftcmdpb.SAdd] = s.onSAdd
	s.handlers[raftcmdpb.SCard] = s.onSCard
	s.handlers[raftcmdpb.SRem] = s.onSRem
	s.handlers[raftcmdpb.SMembers] = s.onSMembers
	s.handlers[raftcmdpb.SIsMember] = s.onSIsMember
	s.handlers[raftcmdpb.SPop] = s.onSPop

	// zset
	s.handlers[raftcmdpb.ZAdd] = s.onZAdd
	s.handlers[raftcmdpb.ZCard] = s.onZCard
	s.handlers[raftcmdpb.ZCount] = s.onZCount
	s.handlers[raftcmdpb.ZIncrBy] = s.onZIncrBy
	s.handlers[raftcmdpb.ZLexCount] = s.onZLexCount
	s.handlers[raftcmdpb.ZRange] = s.onZRange
	s.handlers[raftcmdpb.ZRangeByLex] = s.onZRangeByLex
	s.handlers[raftcmdpb.ZRangeByScore] = s.onZRangeByScore
	s.handlers[raftcmdpb.ZRank] = s.onZRank
	s.handlers[raftcmdpb.ZRem] = s.onZRem
	s.handlers[raftcmdpb.ZRemRangeByLex] = s.onZRemRangeByLex
	s.handlers[raftcmdpb.ZRemRangeByRank] = s.onZRemRangeByRank
	s.handlers[raftcmdpb.ZRemRangeByScore] = s.onZRemRangeByScore
	s.handlers[raftcmdpb.ZScore] = s.onZScore
}

func (s *RedisServer) doConnection(session goetty.IOSession) error {
	addr := session.RemoteAddr()
	log.Debugf("redis-[%s]: connected", addr)

	// every client has 2 goroutines, read and write
	rs := newSession(session)
	s.routing.put(rs.id, rs)

	go rs.writeLoop()
	defer func() {
		s.routing.delete(rs.id)
		rs.close()
	}()

	for {
		value, err := session.Read()
		if err != nil {
			if err == io.EOF {
				return nil
			}

			log.Errorf("redis-[%s]: read from cli failed, errors\n %+v",
				addr,
				err)
			return err
		}

		if req, ok := value.(redis.Command); ok {
			log.Debugf("redis-[%s]: read a redis command, cmd=<%+v>", addr, req)

			err = s.onRedisCommand(req, rs)
			if err != nil {
				rsp := pool.AcquireResponse()
				rsp.ErrorResult = util.StringToSlice(err.Error())
				rs.onResp(rsp)
			}
		} else if req, ok := value.(*raftcmdpb.Request); ok {
			if len(req.UUID) > 0 {
				log.Debugf("req: read a raft req. from=<%s>, uuid=<%d>, cmd=<%s>",
					addr,
					req.UUID,
					req.Cmd)
			}

			rs.setFromProxy()
			err = s.onProxyReq(req, rs)
			if err != nil {
				rsp := pool.AcquireResponse()
				rsp.ErrorResult = util.StringToSlice(err.Error())
				rsp.UUID = req.UUID
				rs.onResp(rsp)
				pool.ReleaseRequest(req)
			}
		}
	}
}

func (s *RedisServer) onResp(resp *raftcmdpb.RaftCMDResponse) {
	var errorResult []byte
	hasError := resp.Header != nil

	for _, rsp := range resp.Responses {
		rs := s.routing.get(rsp.SessionID)
		if rs != nil {
			if hasError {
				if resp.Header.Error.RaftEntryTooLarge == nil {
					rsp.Type = raftcmdpb.RaftError
				} else {
					rsp.Type = raftcmdpb.Invalid
				}

				if errorResult == nil {
					errorResult = util.MustMarshal(resp.Header)
				}

				rsp.ErrorResult = errorResult
			}

			rs.onResp(rsp)
		} else {
			pool.ReleaseResponse(rsp)
		}
	}

	pool.ReleaseRaftCMDResponse(resp)
}

func (s *RedisServer) onProxyReq(req *raftcmdpb.Request, session *session) error {
	req.Type = s.typeMapping[strings.ToLower(util.SliceToString(req.Cmd[0]))]
	req.SessionID = session.id

	if h, ok := s.localHandlers[req.Type]; ok {
		h(req, session)
		return nil
	}

	if len(req.Cmd) < 2 {
		rsp := pool.AcquireResponse()
		rsp.UUID = req.UUID
		rsp.ErrorResult = redis.ErrNotSupportCommand
		session.onResp(rsp)
		return nil
	}

	err := s.store.OnProxyReq(req, s.onResp)
	if err != nil {
		return err
	}

	return nil
}

func (s *RedisServer) onRedisCommand(cmd redis.Command, session *session) error {
	t := s.typeMapping[cmd.CmdString()]

	if h, ok := s.localHandlers[t]; ok {
		req := pool.AcquireRequest()
		req.Cmd = cmd
		h(req, session)
		return nil
	}

	h, ok := s.handlers[t]
	if !ok {
		rsp := pool.AcquireResponse()
		rsp.ErrorResult = redis.ErrNotSupportCommand
		session.onResp(rsp)
		return nil
	}

	_, err := h(t, cmd, session)
	if err != nil {
		return err
	}

	return nil
}

func (s *RedisServer) onDel(cmdType raftcmdpb.CMDType, cmd redis.Command, session *session) ([]byte, error) {
	args := cmd.Args()
	if len(args) != 1 {
		rsp := pool.AcquireResponse()
		rsp.ErrorResult = redis.ErrInvalidCommandResp
		session.onResp(rsp)
		return nil, nil
	}

	return s.store.OnRedisCommand(session.id, cmdType, cmd, s.onResp)
}

func (s *RedisServer) onPing(req *raftcmdpb.Request, session *session) ([]byte, error) {
	rsp := pool.AcquireResponse()
	rsp.UUID = req.UUID
	rsp.StatusResult = redis.PongResp
	session.onResp(rsp)
	return nil, nil
}
<|endoftext|>