<commit_before>package main

import (
	"fmt"
	"io"
	"os"
	"os/exec"
	"path/filepath"
	"time"

	"github.com/gonuts/logger"
)

type External struct {
	cmd string
	err error
}
type FixFct func(*Context) error

type Context struct {
	msg       *logger.Logger
	cfg       Config
	siteroot  string // where to install software, binaries, ...
	repourl   string
	rpmprefix string
	dbpath    string
	etcdir    string
	yumconf   string
	yumreposd string
	yum       *YumClient
	tmpdir    string
	bindir    string
	libdir    string
	initfile  string

	extstatus map[string]External
	reqext    []string
	extfix    map[string]FixFct
}

func New(cfg Config, dbg bool) (*Context, error) {
	var err error
	siteroot := cfg.Siteroot()
	ctx := Context{
		cfg:       cfg,
		msg:       logger.NewLogger("pkr", logger.INFO, os.Stdout),
		siteroot:  siteroot,
		repourl:   cfg.RepoUrl(),
		rpmprefix: cfg.Prefix(),
		dbpath:    filepath.Join(siteroot, "var", "lib", "rpm"),
		etcdir:    filepath.Join(siteroot, "etc"),
		yumconf:   filepath.Join(siteroot, "etc", "yum.conf"),
		yumreposd: filepath.Join(siteroot, "etc", "yum.repos.d"),
		tmpdir:    filepath.Join(siteroot, "tmp"),
		bindir:    filepath.Join(siteroot, "usr", "bin"),
		libdir:    filepath.Join(siteroot, "lib"),
		initfile:  filepath.Join(siteroot, "etc", "repoinit"),
	}
	if dbg {
		ctx.msg.SetLevel(logger.DEBUG)
	}
	for _, dir := range []string{
		ctx.tmpdir,
		ctx.bindir,
		ctx.libdir,
	} {
		err = os.MkdirAll(dir, 0644)
		if err != nil {
			ctx.msg.Errorf("could not create directory %q: %v\n", dir, err)
			return nil, err
		}
	}
	os.Setenv("PATH", os.Getenv("PATH")+string(os.PathListSeparator)+ctx.bindir)

	// make sure the db is initialized
	err = ctx.initRpmDb()
	if err != nil {
		return nil, err
	}

	// yum
	err = ctx.initYum()
	if err != nil {
		return nil, err
	}

	ctx.yum = NewClient(ctx.siteroot)
	if ctx.yum == nil {
		return nil, err
	}

	// defining structures and checking if all needed tools are available
	ctx.extstatus = make(map[string]External)
	ctx.reqext = []string{"rpm"}
	ctx.extfix = make(map[string]FixFct)
	err = ctx.checkPreRequisites()
	if err != nil {
		return nil, err
	}

	err = ctx.checkRepository()
	if err != nil {
		return nil, err
	}

	return &ctx, err
}

func (ctx *Context) Exit(rc int) {
	os.Exit(rc)
}

// initRpmDb initializes the RPM database
func (ctx *Context) initRpmDb() error {
	var err error
	msg := ctx.msg
	msg.Infof("RPM DB in %q\n", ctx.dbpath)
	err = os.MkdirAll(ctx.dbpath, 0644)
	if err != nil {
		msg.Errorf(
			"could not create directory %q for RPM DB: %v\n",
			ctx.dbpath,
			err,
		)
		return err
	}

	pkgdir := filepath.Join(ctx.dbpath, "Packages")
	if !path_exists(pkgdir) {
		msg.Infof("Initializing RPM db\n")
		cmd := exec.Command(
			"rpm",
			"--dbpath", ctx.dbpath,
			"--initdb",
		)
		out, err := cmd.CombinedOutput()
		msg.Debugf(string(out))
		if err != nil {
			return fmt.Errorf("error initializing RPM DB: %v", err)
		}
	}
	return err
}

func (ctx *Context) initYum() error {
	var err error
	err = os.MkdirAll(ctx.etcdir, 0644)
	if err != nil {
		return fmt.Errorf("could not create dir %q: %v", ctx.etcdir, err)
	}

	if !path_exists(ctx.yumconf) {
		yum, err := os.Create(ctx.yumconf)
		if err != nil {
			return err
		}
		defer yum.Close()
		err = ctx.writeYumConf(yum)
		if err != nil {
			return err
		}
		err = yum.Sync()
		if err != nil {
			return err
		}
		err = yum.Close()
		if err != nil {
			return err
		}
	}
	err = ctx.cfg.InitYum(ctx)
	return err
}

// checkPreRequisites makes sure that all external tools required by
// this tool to perform the installation are present
func (ctx *Context) checkPreRequisites() error {
	var err error
	extmissing := false
	missing := make([]string, 0)

	for _, ext := range ctx.reqext {
		cmd, err := exec.LookPath(ext)
		ctx.extstatus[ext] = External{
			cmd: cmd,
			err: err,
		}
	}

	for k, ext := range ctx.extstatus {
		if ext.err == nil {
			ctx.msg.Infof("%s: Found %q\n", k, ext.cmd)
			continue
		}
		ctx.msg.Infof("%s: Missing - trying compensatory measure\n", k)
		fix, ok := ctx.extfix[k]
		if !ok {
			extmissing = true
			missing = append(missing, k)
			continue
		}

		err = fix(ctx)
		if err != nil {
			return err
		}

		cmd, err := exec.LookPath(k)
		ctx.extstatus[k] = External{
			cmd: cmd,
			err: err,
		}
		if err == nil {
			ctx.msg.Infof("%s: Found %q\n", k, cmd)
			continue
		}
		ctx.msg.Infof("%s: Missing\n", k)
		extmissing = true
		missing = append(missing, k)
	}

	if extmissing {
		err = fmt.Errorf("missing external(s): %v", missing)
	}
	return err
}

func (ctx *Context) checkRepository() error {
	var err error
	if !path_exists(ctx.initfile) {
		fini, err := os.Create(ctx.initfile)
		if err != nil {
			return err
		}
		defer fini.Close()
		_, err = fini.WriteString(time.Now().Format(time.RFC3339) + "\n")
		if err != nil {
			return err
		}
		err = fini.Sync()
		if err != nil {
			return err
		}
		return fini.Close()
	}
	err = ctx.checkUpdates()
	return err
}

func (ctx *Context) writeYumConf(w io.Writer) error {
	var err error
	const tmpl = `
[main]
#CONFVERSION 0001
cachedir=/var/cache/yum
debuglevel=2
logfile=/var/log/yum.log
pkgpolicy=newest
distroverpkg=redhat-release
tolerant=1
exactarch=1
obsoletes=1
plugins=1
gpgcheck=0
installroot=%s
reposdir=/etc/yum.repos.d
`
	_, err = fmt.Fprintf(w, tmpl, ctx.siteroot)
	return err
}

func (ctx *Context) writeYumRepo(w io.Writer, data map[string]string) error {
	var err error
	const tmpl = `
[%s]
#REPOVERSION 0001
name=%s
baseurl=%s
enabled=1
`
	_, err = fmt.Fprintf(w, tmpl,
		data["name"],
		data["name"],
		data["url"],
	)
	return err
}

// checkUpdates checks whether packages could be updated in the repository
func (ctx *Context) checkUpdates() error {
	var err error
	return err
}

// install performs the whole download/install procedure (eq. yum install)
func (ctx *Context) install(project, version, cmtconfig string) error {
	var err error
	ctx.msg.Infof("Installing %s/%s/%s\n", project, version, cmtconfig)
	return err
}

// EOF
<commit_msg>context: give a default value to siteroot<commit_after>package main

import (
	"fmt"
	"io"
	"os"
	"os/exec"
	"path/filepath"
	"time"

	"github.com/gonuts/logger"
)

type External struct {
	cmd string
	err error
}
type FixFct func(*Context) error

type Context struct {
	msg       *logger.Logger
	cfg       Config
	siteroot  string // where to install software, binaries, ...
	repourl   string
	rpmprefix string
	dbpath    string
	etcdir    string
	yumconf   string
	yumreposd string
	yum       *YumClient
	tmpdir    string
	bindir    string
	libdir    string
	initfile  string

	extstatus map[string]External
	reqext    []string
	extfix    map[string]FixFct
}

func New(cfg Config, dbg bool) (*Context, error) {
	var err error
	siteroot := cfg.Siteroot()
	if siteroot == "" {
		siteroot = "/opt/cern-sw"
	}

	ctx := Context{
		cfg:       cfg,
		msg:       logger.NewLogger("pkr", logger.INFO, os.Stdout),
		siteroot:  siteroot,
		repourl:   cfg.RepoUrl(),
		rpmprefix: cfg.Prefix(),
		dbpath:    filepath.Join(siteroot, "var", "lib", "rpm"),
		etcdir:    filepath.Join(siteroot, "etc"),
		yumconf:   filepath.Join(siteroot, "etc", "yum.conf"),
		yumreposd: filepath.Join(siteroot, "etc", "yum.repos.d"),
		tmpdir:    filepath.Join(siteroot, "tmp"),
		bindir:    filepath.Join(siteroot, "usr", "bin"),
		libdir:    filepath.Join(siteroot, "lib"),
		initfile:  filepath.Join(siteroot, "etc", "repoinit"),
	}
	if dbg {
		ctx.msg.SetLevel(logger.DEBUG)
	}
	for _, dir := range []string{
		ctx.tmpdir,
		ctx.bindir,
		ctx.libdir,
	} {
		err = os.MkdirAll(dir, 0644)
		if err != nil {
			ctx.msg.Errorf("could not create directory %q: %v\n", dir, err)
			return nil, err
		}
	}
	os.Setenv("PATH", os.Getenv("PATH")+string(os.PathListSeparator)+ctx.bindir)

	// make sure the db is initialized
	err = ctx.initRpmDb()
	if err != nil {
		return nil, err
	}

	// yum
	err = ctx.initYum()
	if err != nil {
		return nil, err
	}

	ctx.yum = NewClient(ctx.siteroot)
	if ctx.yum == nil {
		return nil, err
	}

	// defining structures and checking if all needed tools are available
	ctx.extstatus = make(map[string]External)
	ctx.reqext = []string{"rpm"}
	ctx.extfix = make(map[string]FixFct)
	err = ctx.checkPreRequisites()
	if err != nil {
		return nil, err
	}

	err = ctx.checkRepository()
	if err != nil {
		return nil, err
	}

	return &ctx, err
}

func (ctx *Context) Exit(rc int) {
	os.Exit(rc)
}

// initRpmDb initializes the RPM database
func (ctx *Context) initRpmDb() error {
	var err error
	msg := ctx.msg
	msg.Infof("RPM DB in %q\n", ctx.dbpath)
	err = os.MkdirAll(ctx.dbpath, 0644)
	if err != nil {
		msg.Errorf(
			"could not create directory %q for RPM DB: %v\n",
			ctx.dbpath,
			err,
		)
		return err
	}

	pkgdir := filepath.Join(ctx.dbpath, "Packages")
	if !path_exists(pkgdir) {
		msg.Infof("Initializing RPM db\n")
		cmd := exec.Command(
			"rpm",
			"--dbpath", ctx.dbpath,
			"--initdb",
		)
		out, err := cmd.CombinedOutput()
		msg.Debugf(string(out))
		if err != nil {
			return fmt.Errorf("error initializing RPM DB: %v", err)
		}
	}
	return err
}

func (ctx *Context) initYum() error {
	var err error
	err = os.MkdirAll(ctx.etcdir, 0644)
	if err != nil {
		return fmt.Errorf("could not create dir %q: %v", ctx.etcdir, err)
	}

	if !path_exists(ctx.yumconf) {
		yum, err := os.Create(ctx.yumconf)
		if err != nil {
			return err
		}
		defer yum.Close()
		err = ctx.writeYumConf(yum)
		if err != nil {
			return err
		}
		err = yum.Sync()
		if err != nil {
			return err
		}
		err = yum.Close()
		if err != nil {
			return err
		}
	}
	err = ctx.cfg.InitYum(ctx)
	return err
}

// checkPreRequisites makes sure that all external tools required by
// this tool to perform the installation are present
func (ctx *Context) checkPreRequisites() error {
	var err error
	extmissing := false
	missing := make([]string, 0)

	for _, ext := range ctx.reqext {
		cmd, err := exec.LookPath(ext)
		ctx.extstatus[ext] = External{
			cmd: cmd,
			err: err,
		}
	}

	for k, ext := range ctx.extstatus {
		if ext.err == nil {
			ctx.msg.Infof("%s: Found %q\n", k, ext.cmd)
			continue
		}
		ctx.msg.Infof("%s: Missing - trying compensatory measure\n", k)
		fix, ok := ctx.extfix[k]
		if !ok {
			extmissing = true
			missing = append(missing, k)
			continue
		}

		err = fix(ctx)
		if err != nil {
			return err
		}

		cmd, err := exec.LookPath(k)
		ctx.extstatus[k] = External{
			cmd: cmd,
			err: err,
		}
		if err == nil {
			ctx.msg.Infof("%s: Found %q\n", k, cmd)
			continue
		}
		ctx.msg.Infof("%s: Missing\n", k)
		extmissing = true
		missing = append(missing, k)
	}

	if extmissing {
		err = fmt.Errorf("missing external(s): %v", missing)
	}
	return err
}

func (ctx *Context) checkRepository() error {
	var err error
	if !path_exists(ctx.initfile) {
		fini, err := os.Create(ctx.initfile)
		if err != nil {
			return err
		}
		defer fini.Close()
		_, err = fini.WriteString(time.Now().Format(time.RFC3339) + "\n")
		if err != nil {
			return err
		}
		err = fini.Sync()
		if err != nil {
			return err
		}
		return fini.Close()
	}
	err = ctx.checkUpdates()
	return err
}

func (ctx *Context) writeYumConf(w io.Writer) error {
	var err error
	const tmpl = `
[main]
#CONFVERSION 0001
cachedir=/var/cache/yum
debuglevel=2
logfile=/var/log/yum.log
pkgpolicy=newest
distroverpkg=redhat-release
tolerant=1
exactarch=1
obsoletes=1
plugins=1
gpgcheck=0
installroot=%s
reposdir=/etc/yum.repos.d
`
	_, err = fmt.Fprintf(w, tmpl, ctx.siteroot)
	return err
}

func (ctx *Context) writeYumRepo(w io.Writer, data map[string]string) error {
	var err error
	const tmpl = `
[%s]
#REPOVERSION 0001
name=%s
baseurl=%s
enabled=1
`
	_, err = fmt.Fprintf(w, tmpl,
		data["name"],
		data["name"],
		data["url"],
	)
	return err
}

// checkUpdates checks whether packages could be updated in the repository
func (ctx *Context) checkUpdates() error {
	var err error
	return err
}

// install performs the whole download/install procedure (eq. yum install)
func (ctx *Context) install(project, version, cmtconfig string) error {
	var err error
	ctx.msg.Infof("Installing %s/%s/%s\n", project, version, cmtconfig)
	return err
}

// EOF
<|endoftext|>


<commit_before>package rest

import (
	"encoding/json"
	"net/http"
	"net/url"
	"strconv"
)

func NewContext() *Context {
	c := new(Context)
	c.params = &urlValues{&url.Values{}}

	// Default "Decode" method
	c.Decode = func(out interface{}) error {
		defer c.req.Body.Close()
		return json.NewDecoder(c.req.Body).Decode(out)
	}

	return c
}

type urlValues struct {
	*url.Values
}

func (self *urlValues) Int(key string) int {
	self.Get(key)
	i, _ := strconv.Atoi(key)
	return i
}

type Context struct {
	req                   *http.Request
	params                *urlValues
	user                  User
	runParseMultipartForm bool
	store                 *store
	handler               *Handler
	AutoSetUser           func()
	Decode                func(out interface{}) error
}

func (self *Context) User() User {
	if self.user.Id == "" {
		self.AutoSetUser()
	}

	return self.user
}

func (self *Context) Handler() *Handler {
	return self.handler
}

func (self *Context) SetHandler(handler *Handler) {
	self.handler = handler
}

func (self *Context) SetUser(user User) {
	self.user = user
}

func (self *Context) Store() *store {
	if self.store == nil {
		self.store = newStore()
	}

	return self.store
}

func (self *Context) SetReq(req *http.Request) {
	self.req = req
}

func (self *Context) Req() *http.Request {
	return self.req
}

func (self *Context) initParams() {
	if self.runParseMultipartForm {
		return
	}

	// 32m
	self.req.ParseMultipartForm(32 << 20)
	self.params = &urlValues{&self.req.Form}
	self.runParseMultipartForm = true
}

func (self *Context) Params() *urlValues {
	self.initParams()
	return self.params
}
<commit_msg>fix the Int method does not work<commit_after>package rest

import (
	"encoding/json"
	"net/http"
	"net/url"
	"strconv"
)

func NewContext() *Context {
	c := new(Context)
	c.params = &urlValues{&url.Values{}}

	// Default "Decode" method
	c.Decode = func(out interface{}) error {
		defer c.req.Body.Close()
		return json.NewDecoder(c.req.Body).Decode(out)
	}

	return c
}

type urlValues struct {
	*url.Values
}

func (self *urlValues) Int(key string) int {
	i, _ := strconv.Atoi(self.Get(key))
	return i
}

type Context struct {
	req                   *http.Request
	params                *urlValues
	user                  User
	runParseMultipartForm bool
	store                 *store
	handler               *Handler
	AutoSetUser           func()
	Decode                func(out interface{}) error
}

func (self *Context) User() User {
	if self.user.Id == "" {
		self.AutoSetUser()
	}

	return self.user
}

func (self *Context) Handler() *Handler {
	return self.handler
}

func (self *Context) SetHandler(handler *Handler) {
	self.handler = handler
}

func (self *Context) SetUser(user User) {
	self.user = user
}

func (self *Context) Store() *store {
	if self.store == nil {
		self.store = newStore()
	}

	return self.store
}

func (self *Context) SetReq(req *http.Request) {
	self.req = req
}

func (self *Context) Req() *http.Request {
	return self.req
}

func (self *Context) initParams() {
	if self.runParseMultipartForm {
		return
	}

	// 32m
	self.req.ParseMultipartForm(32 << 20)
	self.params = &urlValues{&self.req.Form}
	self.runParseMultipartForm = true
}

func (self *Context) Params() *urlValues {
	self.initParams()
	return self.params
}
<|endoftext|>


<commit_before>package filecache

import (
	"errors"
	"os"
	"path"
	"path/filepath"
	"sync"
	"time"

	"github.com/anacrolix/log"

	"github.com/anacrolix/missinggo/v2/pproffd"
	"github.com/anacrolix/missinggo/v2/resource"
)

const (
	dirPerm  = 0755
	filePerm = 0644
)

type Cache struct {
	root     string
	mu       sync.Mutex
	capacity int64
	filled   int64
	policy   Policy
	items    map[key]itemState
}

type CacheInfo struct {
	Capacity int64
	Filled   int64
	NumItems int
}

type ItemInfo struct {
	Path     key
	Accessed time.Time
	Size     int64
}

// Calls the function for every item known to be in the cache.
func (me *Cache) WalkItems(cb func(ItemInfo)) {
	me.mu.Lock()
	defer me.mu.Unlock()
	for k, ii := range me.items {
		cb(ItemInfo{
			Path:     k,
			Accessed: ii.Accessed,
			Size:     ii.Size,
		})
	}
}

func (me *Cache) Info() (ret CacheInfo) {
	me.mu.Lock()
	defer me.mu.Unlock()
	ret.Capacity = me.capacity
	ret.Filled = me.filled
	ret.NumItems = len(me.items)
	return
}

// Setting a negative capacity means unlimited.
func (me *Cache) SetCapacity(capacity int64) {
	me.mu.Lock()
	defer me.mu.Unlock()
	me.capacity = capacity
}

func NewCache(root string) (ret *Cache, err error) {
	root, err = filepath.Abs(root)
	ret = &Cache{
		root:     root,
		capacity: -1, // unlimited
	}
	ret.mu.Lock()
	go func() {
		defer ret.mu.Unlock()
		ret.rescan()
	}()
	return
}

// An empty return path is an error.
func sanitizePath(p string) (ret key) {
	if p == "" {
		return
	}
	ret = key(path.Clean("/" + p))
	if ret[0] == '/' {
		ret = ret[1:]
	}
	return
}

// Leaf is a descendant of root.
func pruneEmptyDirs(root string, leaf string) (err error) {
	rootInfo, err := os.Stat(root)
	if err != nil {
		return
	}
	for {
		var leafInfo os.FileInfo
		leafInfo, err = os.Stat(leaf)
		if os.IsNotExist(err) {
			goto parent
		}
		if err != nil {
			return
		}
		if !leafInfo.IsDir() {
			return
		}
		if os.SameFile(rootInfo, leafInfo) {
			return
		}
		if os.Remove(leaf) != nil {
			return
		}
	parent:
		leaf = filepath.Dir(leaf)
	}
}

func (me *Cache) Remove(path string) error {
	me.mu.Lock()
	defer me.mu.Unlock()
	return me.remove(sanitizePath(path))
}

var (
	ErrBadPath = errors.New("bad path")
	ErrIsDir   = errors.New("is directory")
)

func (me *Cache) StatFile(path string) (os.FileInfo, error) {
	return os.Stat(me.realpath(sanitizePath(path)))
}

func (me *Cache) OpenFile(path string, flag int) (ret *File, err error) {
	key := sanitizePath(path)
	if key == "" {
		err = ErrIsDir
		return
	}
	f, err := os.OpenFile(me.realpath(key), flag, filePerm)
	if flag&os.O_CREATE != 0 && os.IsNotExist(err) {
		// Ensure intermediate directories and try again.
		dirErr := os.MkdirAll(filepath.Dir(me.realpath(key)), dirPerm)
		f, err = os.OpenFile(me.realpath(key), flag, filePerm)
		if dirErr != nil && os.IsNotExist(err) {
			return nil, dirErr
		}
		if err != nil {
			go me.pruneEmptyDirs(key)
		}
	}
	if err != nil {
		return
	}
	ret = &File{
		path: key,
		f:    pproffd.WrapOSFile(f),
		onRead: func(n int) {
			me.mu.Lock()
			defer me.mu.Unlock()
			me.updateItem(key, func(i *itemState, ok bool) bool {
				i.Accessed = time.Now()
				return ok
			})
		},
		afterWrite: func(endOff int64) {
			me.mu.Lock()
			defer me.mu.Unlock()
			me.updateItem(key, func(i *itemState, ok bool) bool {
				i.Accessed = time.Now()
				if endOff > i.Size {
					i.Size = endOff
				}
				return ok
			})
		},
	}
	me.mu.Lock()
	defer me.mu.Unlock()
	me.updateItem(key, func(i *itemState, ok bool) bool {
		if !ok {
			*i, ok = me.statKey(key)
		}
		i.Accessed = time.Now()
		return ok
	})
	return
}

func (me *Cache) rescan() {
	me.filled = 0
	me.policy = new(lru)
	me.items = make(map[key]itemState)
	err := filepath.Walk(me.root, func(path string, info os.FileInfo, err error) error {
		if os.IsNotExist(err) {
			return nil
		}
		if err != nil {
			return err
		}
		if info.IsDir() {
			return nil
		}
		path, err = filepath.Rel(me.root, path)
		if err != nil {
			log.Print(err)
			return nil
		}
		key := sanitizePath(path)
		me.updateItem(key, func(i *itemState, ok bool) bool {
			if ok {
				panic("scanned duplicate items")
			}
			*i, ok = me.statKey(key)
			return ok
		})
		return nil
	})
	if err != nil {
		panic(err)
	}
}

func (me *Cache) statKey(k key) (i itemState, ok bool) {
	fi, err := os.Stat(me.realpath(k))
	if os.IsNotExist(err) {
		return
	}
	if err != nil {
		panic(err)
	}
	i.FromOSFileInfo(fi)
	ok = true
	return
}

func (me *Cache) updateItem(k key, u func(*itemState, bool) bool) {
	ii, ok := me.items[k]
	me.filled -= ii.Size
	if u(&ii, ok) {
		me.filled += ii.Size
		me.policy.Used(k, ii.Accessed)
		me.items[k] = ii
	} else {
		me.policy.Forget(k)
		delete(me.items, k)
	}
	me.trimToCapacity()
}

func (me *Cache) realpath(path key) string {
	return filepath.Join(me.root, filepath.FromSlash(string(path)))
}

func (me *Cache) TrimToCapacity() {
	me.mu.Lock()
	defer me.mu.Unlock()
	me.trimToCapacity()
}

func (me *Cache) pruneEmptyDirs(path key) {
	pruneEmptyDirs(me.root, me.realpath(path))
}

func (me *Cache) remove(path key) error {
	err := os.Remove(me.realpath(path))
	if os.IsNotExist(err) {
		err = nil
	}
	if err != nil {
		return err
	}
	me.pruneEmptyDirs(path)
	me.updateItem(path, func(*itemState, bool) bool {
		return false
	})
	return nil
}

func (me *Cache) trimToCapacity() {
	if me.capacity < 0 {
		return
	}
	for me.filled > me.capacity {
		me.remove(me.policy.Choose().(key))
	}
}

// TODO: Do I need this?
func (me *Cache) pathInfo(p string) itemState {
	return me.items[sanitizePath(p)]
}

func (me *Cache) Rename(from, to string) (err error) {
	_from := sanitizePath(from)
	_to := sanitizePath(to)
	me.mu.Lock()
	defer me.mu.Unlock()
	err = os.MkdirAll(filepath.Dir(me.realpath(_to)), dirPerm)
	if err != nil {
		return
	}
	err = os.Rename(me.realpath(_from), me.realpath(_to))
	if err != nil {
		return
	}
	// We can do a dance here to copy the state from the old item, but lets
	// just stat the new item for now.
	me.updateItem(_from, func(i *itemState, ok bool) bool {
		return false
	})
	me.updateItem(_to, func(i *itemState, ok bool) bool {
		*i, ok = me.statKey(_to)
		return ok
	})
	return
}

func (me *Cache) Stat(path string) (os.FileInfo, error) {
	return os.Stat(me.realpath(sanitizePath(path)))
}

func (me *Cache) AsResourceProvider() resource.Provider {
	return &uniformResourceProvider{me}
}
<commit_msg>filecache: Fix concurrent use of Cache<commit_after>package filecache

import (
	"errors"
	"os"
	"path"
	"path/filepath"
	"sync"
	"syscall"
	"time"

	"github.com/anacrolix/log"

	"github.com/anacrolix/missinggo/v2/pproffd"
	"github.com/anacrolix/missinggo/v2/resource"
)

const (
	dirPerm  = 0755
	filePerm = 0644
)

type Cache struct {
	root     string
	mu       sync.Mutex
	capacity int64
	filled   int64
	policy   Policy
	items    map[key]itemState
}

type CacheInfo struct {
	Capacity int64
	Filled   int64
	NumItems int
}

type ItemInfo struct {
	Path     key
	Accessed time.Time
	Size     int64
}

// Calls the function for every item known to be in the cache.
func (me *Cache) WalkItems(cb func(ItemInfo)) {
	me.mu.Lock()
	defer me.mu.Unlock()
	for k, ii := range me.items {
		cb(ItemInfo{
			Path:     k,
			Accessed: ii.Accessed,
			Size:     ii.Size,
		})
	}
}

func (me *Cache) Info() (ret CacheInfo) {
	me.mu.Lock()
	defer me.mu.Unlock()
	ret.Capacity = me.capacity
	ret.Filled = me.filled
	ret.NumItems = len(me.items)
	return
}

// Setting a negative capacity means unlimited.
func (me *Cache) SetCapacity(capacity int64) {
	me.mu.Lock()
	defer me.mu.Unlock()
	me.capacity = capacity
}

func NewCache(root string) (ret *Cache, err error) {
	root, err = filepath.Abs(root)
	ret = &Cache{
		root:     root,
		capacity: -1, // unlimited
	}
	ret.mu.Lock()
	go func() {
		defer ret.mu.Unlock()
		ret.rescan()
	}()
	return
}

// An empty return path is an error.
func sanitizePath(p string) (ret key) {
	if p == "" {
		return
	}
	ret = key(path.Clean("/" + p))
	if ret[0] == '/' {
		ret = ret[1:]
	}
	return
}

// Leaf is a descendant of root.
func pruneEmptyDirs(root string, leaf string) (err error) {
	rootInfo, err := os.Stat(root)
	if err != nil {
		return
	}
	for {
		var leafInfo os.FileInfo
		leafInfo, err = os.Stat(leaf)
		if os.IsNotExist(err) {
			goto parent
		}
		if err != nil {
			return
		}
		if !leafInfo.IsDir() {
			return
		}
		if os.SameFile(rootInfo, leafInfo) {
			return
		}
		if os.Remove(leaf) != nil {
			return
		}
	parent:
		leaf = filepath.Dir(leaf)
	}
}

func (me *Cache) Remove(path string) error {
	me.mu.Lock()
	defer me.mu.Unlock()
	return me.remove(sanitizePath(path))
}

var (
	ErrBadPath = errors.New("bad path")
	ErrIsDir   = errors.New("is directory")
)

func (me *Cache) StatFile(path string) (os.FileInfo, error) {
	return os.Stat(me.realpath(sanitizePath(path)))
}

func isMissingDir(err error) bool {
	// I'm not sure why we get EINVAL for missing path components sometimes. It happens on MacOS. I
	// wonder if it would happen the same on other OS.
	return errors.Is(err, syscall.EINVAL) || errors.Is(err, syscall.ENOENT)
}

func (me *Cache) OpenFile(path string, flag int) (ret *File, err error) {
	key := sanitizePath(path)
	if key == "" {
		err = ErrIsDir
		return
	}
	filePath := me.realpath(key)
	f, err := os.OpenFile(filePath, flag, filePerm)
	// Ensure intermediate directories and try again.
	if flag&os.O_CREATE != 0 && isMissingDir(err) {
		dirPath := filepath.Dir(filePath)
		if dirPath == "" {
			panic(key)
		}
		defer func() {
			if err != nil {
				go me.pruneEmptyDirs(key)
			}
		}()
		for {
			err = os.MkdirAll(dirPath, dirPerm)
			if isMissingDir(err) {
				continue
			} else if err != nil {
				return
			}
			f, err = os.OpenFile(filePath, flag, filePerm)
			if isMissingDir(err) {
				continue
			}
			break
		}
	}
	if err != nil {
		return
	}
	ret = &File{
		path: key,
		f:    pproffd.WrapOSFile(f),
		onRead: func(n int) {
			me.mu.Lock()
			defer me.mu.Unlock()
			me.updateItem(key, func(i *itemState, ok bool) bool {
				i.Accessed = time.Now()
				return ok
			})
		},
		afterWrite: func(endOff int64) {
			me.mu.Lock()
			defer me.mu.Unlock()
			me.updateItem(key, func(i *itemState, ok bool) bool {
				i.Accessed = time.Now()
				if endOff > i.Size {
					i.Size = endOff
				}
				return ok
			})
		},
	}
	me.mu.Lock()
	defer me.mu.Unlock()
	me.updateItem(key, func(i *itemState, ok bool) bool {
		if !ok {
			*i, ok = me.statKey(key)
		}
		i.Accessed = time.Now()
		return ok
	})
	return
}

func (me *Cache) rescan() {
	me.filled = 0
	me.policy = new(lru)
	me.items = make(map[key]itemState)
	err := filepath.Walk(me.root, func(path string, info os.FileInfo, err error) error {
		if os.IsNotExist(err) {
			return nil
		}
		if err != nil {
			return err
		}
		if info.IsDir() {
			return nil
		}
		path, err = filepath.Rel(me.root, path)
		if err != nil {
			log.Print(err)
			return nil
		}
		key := sanitizePath(path)
		me.updateItem(key, func(i *itemState, ok bool) bool {
			if ok {
				panic("scanned duplicate items")
			}
			*i, ok = me.statKey(key)
			return ok
		})
		return nil
	})
	if err != nil {
		panic(err)
	}
}

func (me *Cache) statKey(k key) (i itemState, ok bool) {
	fi, err := os.Stat(me.realpath(k))
	if os.IsNotExist(err) {
		return
	}
	if err != nil {
		panic(err)
	}
	i.FromOSFileInfo(fi)
	ok = true
	return
}

func (me *Cache) updateItem(k key, u func(*itemState, bool) bool) {
	ii, ok := me.items[k]
	me.filled -= ii.Size
	if u(&ii, ok) {
		me.filled += ii.Size
		me.policy.Used(k, ii.Accessed)
		me.items[k] = ii
	} else {
		me.policy.Forget(k)
		delete(me.items, k)
	}
	me.trimToCapacity()
}

func (me *Cache) realpath(path key) string {
	return filepath.Join(me.root, filepath.FromSlash(string(path)))
}

func (me *Cache) TrimToCapacity() {
	me.mu.Lock()
	defer me.mu.Unlock()
	me.trimToCapacity()
}

func (me *Cache) pruneEmptyDirs(path key) {
	pruneEmptyDirs(me.root, me.realpath(path))
}

func (me *Cache) remove(path key) error {
	err := os.Remove(me.realpath(path))
	if os.IsNotExist(err) {
		err = nil
	}
	if err != nil {
		return err
	}
	me.pruneEmptyDirs(path)
	me.updateItem(path, func(*itemState, bool) bool {
		return false
	})
	return nil
}

func (me *Cache) trimToCapacity() {
	if me.capacity < 0 {
		return
	}
	for me.filled > me.capacity {
		me.remove(me.policy.Choose().(key))
	}
}

// TODO: Do I need this?
func (me *Cache) pathInfo(p string) itemState {
	return me.items[sanitizePath(p)]
}

func (me *Cache) Rename(from, to string) (err error) {
	_from := sanitizePath(from)
	_to := sanitizePath(to)
	me.mu.Lock()
	defer me.mu.Unlock()
	err = os.MkdirAll(filepath.Dir(me.realpath(_to)), dirPerm)
	if err != nil {
		return
	}
	err = os.Rename(me.realpath(_from), me.realpath(_to))
	if err != nil {
		return
	}
	// We can do a dance here to copy the state from the old item, but lets
	// just stat the new item for now.
	me.updateItem(_from, func(i *itemState, ok bool) bool {
		return false
	})
	me.updateItem(_to, func(i *itemState, ok bool) bool {
		*i, ok = me.statKey(_to)
		return ok
	})
	return
}

func (me *Cache) Stat(path string) (os.FileInfo, error) {
	return os.Stat(me.realpath(sanitizePath(path)))
}

func (me *Cache) AsResourceProvider() resource.Provider {
	return &uniformResourceProvider{me}
}
<|endoftext|>


<commit_before>package cache

import (
	"github.com/insionng/vodka"
)

const VodkaCacheStoreKey = "VodkaCacheStore"

func Store(value interface{}) Cache {
	var cacher Cache
	switch v := value.(type) {
	case vodka.Context:
		cacher = v.Get(VodkaCacheStoreKey).(Cache)
		if cacher == nil {
			panic("VodkaStore not found, forget to Use Middleware ?")
		}
	default:

		panic("unknown Context")
	}

	if cacher == nil {
		panic("cache context not found")
	}

	return cacher
}

func VodkaCacher(opt Options) vodka.MiddlewareFunc {
	return func(h vodka.HandlerFunc) vodka.HandlerFunc {
		return func(c vodka.Context) error {
			tagcache, err := New(opt)
			if err != nil {
				return err
			}

			c.Set(VodkaCacheStoreKey, tagcache)

			if err = h(c); err != nil {
				return err
			}

			return nil
		}
	}
}
<commit_msg>just for vodka v2+<commit_after>package cache

import (
	"github.com/insionng/vodka"
)

const VodkaCacheStoreKey = "VodkaCacheStore"

func Store(value interface{}) Cache {
	var cacher Cache
	switch v := value.(type) {
	case vodka.Context:
		cacher = v.Get(VodkaCacheStoreKey).(Cache)
		if cacher == nil {
			panic("VodkaStore not found, forget to Use Middleware ?")
		}
	default:

		panic("unknown Context")
	}

	if cacher == nil {
		panic("cache context not found")
	}

	return cacher
}

func VodkaCacher(opt Options) vodka.MiddlewareFunc {
	return func(next vodka.HandlerFunc) vodka.HandlerFunc {
		return func(self vodka.Context) error {
			tagcache, err := New(opt)
			if err != nil {
				return err
			}

			self.Set(VodkaCacheStoreKey, tagcache)

			if err = next(self); err != nil {
				return err
			}

			return nil
		}
	}
}
<|endoftext|>


<commit_before>// Copyright 2015 The Cockroach 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. See the AUTHORS file
// for names of contributors.
//
// Author: Marc Berhault (marc@cockroachlabs.com)

package main

import (
	"database/sql"
	"flag"
	"fmt"
	"log"
	"os"

	"bazil.org/fuse"
	"bazil.org/fuse/fs"
	_ "bazil.org/fuse/fs/fstestutil"
	"github.com/cockroachdb/cockroach/security"
	"github.com/cockroachdb/cockroach/security/securitytest"
	"github.com/cockroachdb/cockroach/server"
	_ "github.com/cockroachdb/cockroach/sql/driver"
)

var usage = func() {
	fmt.Fprintf(os.Stderr, "Usage of %s:\n", os.Args[0])
	fmt.Fprintf(os.Stderr, "  %s <mountpoint>\n", os.Args[0])
	flag.PrintDefaults()
}

func main() {
	flag.Usage = usage
	flag.Parse()

	if flag.NArg() != 1 {
		usage()
		os.Exit(2)
	}
	mountpoint := flag.Arg(0)

	security.SetReadFileFn(securitytest.Asset)
	serv := server.StartTestServer(nil)
	defer serv.Stop()
	url := "https://root@" + serv.ServingAddr() + "?certs=test_certs"

	// Open DB connection first.
	db, err := sql.Open("cockroach", url)
	if err != nil {
		log.Fatal(err)
	}
	// defer db.Close()

	cfs := CFS{db}
	if err := cfs.initSchema(); err != nil {
		log.Fatal(err)
	}

	{
		// For testing only.
		if err := cfs.create(rootNodeID, Node{Name: "myfile", IsDir: false}); err != nil {
			log.Fatal(err)
		}
		if err := cfs.create(rootNodeID, Node{Name: "mydir", IsDir: true}); err != nil {
			log.Fatal(err)
		}
		results, err := cfs.list(0)
		if err != nil {
			log.Fatal(err)
		}
		log.Print(results)
	}

	// Mount filesystem.
	c, err := fuse.Mount(
		mountpoint,
		fuse.FSName("CockroachFS"),
		fuse.Subtype("CockroachFS"),
		fuse.LocalVolume(),
		fuse.VolumeName(""),
	)
	if err != nil {
		log.Fatal(err)
	}
	defer c.Close()

	// Serve root.
	err = fs.Serve(c, cfs)
	if err != nil {
		log.Fatal(err)
	}

	// check if the mount process has an error to report
	<-c.Ready
	if err := c.MountError; err != nil {
		log.Fatal(err)
	}
}
<commit_msg>Unmount the filesystem on interrupt.<commit_after>// Copyright 2015 The Cockroach 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. See the AUTHORS file
// for names of contributors.
//
// Author: Marc Berhault (marc@cockroachlabs.com)

package main

import (
	"database/sql"
	"flag"
	"fmt"
	"log"
	"os"
	"os/signal"

	"bazil.org/fuse"
	"bazil.org/fuse/fs"
	_ "bazil.org/fuse/fs/fstestutil"
	"github.com/cockroachdb/cockroach/security"
	"github.com/cockroachdb/cockroach/security/securitytest"
	"github.com/cockroachdb/cockroach/server"
	_ "github.com/cockroachdb/cockroach/sql/driver"
)

var usage = func() {
	fmt.Fprintf(os.Stderr, "Usage of %s:\n", os.Args[0])
	fmt.Fprintf(os.Stderr, "  %s <mountpoint>\n", os.Args[0])
	flag.PrintDefaults()
}

func main() {
	flag.Usage = usage
	flag.Parse()

	if flag.NArg() != 1 {
		usage()
		os.Exit(2)
	}
	mountpoint := flag.Arg(0)

	security.SetReadFileFn(securitytest.Asset)
	serv := server.StartTestServer(nil)
	defer serv.Stop()
	url := "https://root@" + serv.ServingAddr() + "?certs=test_certs"

	// Open DB connection first.
	db, err := sql.Open("cockroach", url)
	if err != nil {
		log.Fatal(err)
	}
	// defer db.Close()

	cfs := CFS{db}
	if err := cfs.initSchema(); err != nil {
		log.Fatal(err)
	}

	{
		// For testing only.
		if err := cfs.create(rootNodeID, Node{Name: "myfile", IsDir: false}); err != nil {
			log.Fatal(err)
		}
		if err := cfs.create(rootNodeID, Node{Name: "mydir", IsDir: true}); err != nil {
			log.Fatal(err)
		}
		results, err := cfs.list(0)
		if err != nil {
			log.Fatal(err)
		}
		log.Print(results)
	}

	// Mount filesystem.
	c, err := fuse.Mount(
		mountpoint,
		fuse.FSName("CockroachFS"),
		fuse.Subtype("CockroachFS"),
		fuse.LocalVolume(),
		fuse.VolumeName(""),
	)
	if err != nil {
		log.Fatal(err)
	}
	defer c.Close()

	go func() {
		sig := make(chan os.Signal, 1)
		signal.Notify(sig, os.Interrupt)
		<-sig
		fuse.Unmount(mountpoint)
	}()

	// Serve root.
	err = fs.Serve(c, cfs)
	if err != nil {
		log.Fatal(err)
	}

	// check if the mount process has an error to report
	<-c.Ready
	if err := c.MountError; err != nil {
		log.Fatal(err)
	}
}
<|endoftext|>


<commit_before>/*
Copyright 2018 The Kubernetes Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package chaos

import (
	"fmt"
	"math/rand"
	"os/exec"
	"sync"
	"time"

	"k8s.io/perf-tests/clusterloader2/api"
	"k8s.io/perf-tests/clusterloader2/pkg/util"

	"k8s.io/api/core/v1"
	"k8s.io/apimachinery/pkg/util/sets"
	"k8s.io/apimachinery/pkg/util/wait"
	clientset "k8s.io/client-go/kubernetes"
	"k8s.io/klog"
)

// NodeKiller is a utility to simulate node failures.
type NodeKiller struct {
	config   api.NodeFailureConfig
	client   clientset.Interface
	provider string
	// killedNodes stores names of the nodes that have been killed by NodeKiller.
	killedNodes sets.String
}

// NewNodeKiller creates new NodeKiller.
func NewNodeKiller(config api.NodeFailureConfig, client clientset.Interface, provider string) (*NodeKiller, error) {
	if provider != "gce" && provider != "gke" {
		return nil, fmt.Errorf("provider %q is not supported by NodeKiller")
	}
	return &NodeKiller{config, client, provider, sets.NewString()}, nil
}

// Run starts NodeKiller until stopCh is closed.
func (k *NodeKiller) Run(stopCh <-chan struct{}) {
	// wait.JitterUntil starts work immediately, so wait first.
	time.Sleep(wait.Jitter(time.Duration(k.config.Interval), k.config.JitterFactor))
	wait.JitterUntil(func() {
		nodes, err := k.pickNodes()
		if err != nil {
			klog.Errorf("Unable to pick nodes to kill: %v", err)
			return
		}
		k.kill(nodes)
	}, time.Duration(k.config.Interval), k.config.JitterFactor, true, stopCh)
}

func (k *NodeKiller) pickNodes() ([]v1.Node, error) {
	allNodes, err := util.GetSchedulableUntainedNodes(k.client)
	if err != nil {
		return nil, err
	}
	nodes := allNodes[:0]
	for _, node := range allNodes {
		if !k.killedNodes.Has(node.Name) {
			nodes = append(nodes, node)
		}
	}
	rand.Shuffle(len(nodes), func(i, j int) {
		nodes[i], nodes[j] = nodes[j], nodes[i]
	})
	numNodes := int(k.config.FailureRate * float64(len(nodes)))
	if len(nodes) > numNodes {
		return nodes[:numNodes], nil
	}
	return nodes, nil
}

func (k *NodeKiller) kill(nodes []v1.Node) {
	wg := sync.WaitGroup{}
	wg.Add(len(nodes))
	for _, node := range nodes {
		k.killedNodes.Insert(node.Name)
		node := node
		go func() {
			defer wg.Done()

			klog.Infof("Stopping docker and kubelet on %q to simulate failure", node.Name)
			err := ssh("sudo systemctl stop docker kubelet", &node)
			if err != nil {
				klog.Errorf("ERROR while stopping node %q: %v", node.Name, err)
				return
			}

			time.Sleep(time.Duration(k.config.SimulatedDowntime))

			klog.Infof("Rebooting %q to repair the node", node.Name)
			err = ssh("sudo reboot", &node)
			if err != nil {
				klog.Errorf("Error while rebooting node %q: %v", node.Name, err)
				return
			}
		}()
	}
	wg.Wait()
}

func ssh(command string, node *v1.Node) error {
	zone, ok := node.Labels["failure-domain.beta.kubernetes.io/zone"]
	if !ok {
		return fmt.Errorf("unknown zone for %q node: no failure-domain.beta.kubernetes.io/zone label", node.Name)
	}
	cmd := exec.Command("gcloud", "compute", "ssh", "--zone", zone, "--command", command, node.Name)
	output, err := cmd.CombinedOutput()
	klog.Infof("ssh to %q finished with %q: %v", node.Name, string(output), err)
	return err
}
<commit_msg>adding node killer log prefix<commit_after>/*
Copyright 2018 The Kubernetes Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package chaos

import (
	"fmt"
	"math/rand"
	"os/exec"
	"sync"
	"time"

	"k8s.io/perf-tests/clusterloader2/api"
	"k8s.io/perf-tests/clusterloader2/pkg/util"

	"k8s.io/api/core/v1"
	"k8s.io/apimachinery/pkg/util/sets"
	"k8s.io/apimachinery/pkg/util/wait"
	clientset "k8s.io/client-go/kubernetes"
	"k8s.io/klog"
)

// NodeKiller is a utility to simulate node failures.
type NodeKiller struct {
	config   api.NodeFailureConfig
	client   clientset.Interface
	provider string
	// killedNodes stores names of the nodes that have been killed by NodeKiller.
	killedNodes sets.String
}

// NewNodeKiller creates new NodeKiller.
func NewNodeKiller(config api.NodeFailureConfig, client clientset.Interface, provider string) (*NodeKiller, error) {
	if provider != "gce" && provider != "gke" {
		return nil, fmt.Errorf("provider %q is not supported by NodeKiller")
	}
	return &NodeKiller{config, client, provider, sets.NewString()}, nil
}

// Run starts NodeKiller until stopCh is closed.
func (k *NodeKiller) Run(stopCh <-chan struct{}) {
	// wait.JitterUntil starts work immediately, so wait first.
	time.Sleep(wait.Jitter(time.Duration(k.config.Interval), k.config.JitterFactor))
	wait.JitterUntil(func() {
		nodes, err := k.pickNodes()
		if err != nil {
			klog.Errorf("%s: Unable to pick nodes to kill: %v", k, err)
			return
		}
		k.kill(nodes)
	}, time.Duration(k.config.Interval), k.config.JitterFactor, true, stopCh)
}

func (k *NodeKiller) pickNodes() ([]v1.Node, error) {
	allNodes, err := util.GetSchedulableUntainedNodes(k.client)
	if err != nil {
		return nil, err
	}
	nodes := allNodes[:0]
	for _, node := range allNodes {
		if !k.killedNodes.Has(node.Name) {
			nodes = append(nodes, node)
		}
	}
	rand.Shuffle(len(nodes), func(i, j int) {
		nodes[i], nodes[j] = nodes[j], nodes[i]
	})
	numNodes := int(k.config.FailureRate * float64(len(nodes)))
	if len(nodes) > numNodes {
		return nodes[:numNodes], nil
	}
	return nodes, nil
}

func (k *NodeKiller) kill(nodes []v1.Node) {
	wg := sync.WaitGroup{}
	wg.Add(len(nodes))
	for _, node := range nodes {
		k.killedNodes.Insert(node.Name)
		node := node
		go func() {
			defer wg.Done()

			klog.Infof("%s: Stopping docker and kubelet on %q to simulate failure", k, node.Name)
			err := k.ssh("sudo systemctl stop docker kubelet", &node)
			if err != nil {
				klog.Errorf("%s: ERROR while stopping node %q: %v", k, node.Name, err)
				return
			}

			time.Sleep(time.Duration(k.config.SimulatedDowntime))

			klog.Infof("%s: Rebooting %q to repair the node", k, node.Name)
			err = k.ssh("sudo reboot", &node)
			if err != nil {
				klog.Errorf("%s: Error while rebooting node %q: %v", k, node.Name, err)
				return
			}
		}()
	}
	wg.Wait()
}

func (k *NodeKiller) String() string {
	return "NodeKiller"
}

func (k *NodeKiller) ssh(command string, node *v1.Node) error {
	zone, ok := node.Labels["failure-domain.beta.kubernetes.io/zone"]
	if !ok {
		return fmt.Errorf("unknown zone for %q node: no failure-domain.beta.kubernetes.io/zone label", node.Name)
	}
	cmd := exec.Command("gcloud", "compute", "ssh", "--zone", zone, "--command", command, node.Name)
	output, err := cmd.CombinedOutput()
	klog.Infof("%s: ssh to %q finished with %q: %v", k, node.Name, string(output), err)
	return err
}
<|endoftext|>


<commit_before>package fix

import (
	"errors"
	"fmt"
	"github.com/ghthor/gospec"
	. "github.com/ghthor/gospec"
	"github.com/ghthor/journal/git"
	"github.com/ghthor/journal/git/gittest"
	"io/ioutil"
	"os"
	"os/exec"
	"path/filepath"
	"sort"
	"strings"
	"time"
)

//A layout to use as the entry's filename
const filenameLayout = "2006-01-02-1504-MST"

type entryFilenames []string

func (f entryFilenames) Len() int { return len(f) }
func (f entryFilenames) Less(i, j int) bool {
	iTime, err := time.Parse(filenameLayout, f[i])
	if err != nil {
		panic(err)
	}

	jTime, err := time.Parse(filenameLayout, f[j])
	if err != nil {
		panic(err)
	}

	return jTime.After(iTime)
}
func (f entryFilenames) Swap(i, j int) { f[i], f[j] = f[j], f[i] }

// Copy the journal_cases/case_0/ files to directory
func entriesIn(directory string) (entries []string, err error) {
	err = filepath.Walk(directory, func(path string, info os.FileInfo, err error) error {
		if err != nil {
			return err
		}

		if !info.IsDir() {
			if !strings.Contains(filepath.Dir(path), ".git") {
				entries = append(entries, info.Name())
			}
		}
		return nil
	})

	return
}

func newCase0(prefix string) (string, []string, error) {
	// Create a _test/ directory for case_0/
	d, err := ioutil.TempDir("_test", prefix+"_")
	if err != nil {
		return d, nil, err
	}

	// cp -r journal_cases/case_0
	err = exec.Command("cp", "-r", journal_case_0_directory, d).Run()
	if err != nil {
		return d, nil, err
	}

	entries, err := entriesIn(d)
	if err != nil {
		return d, nil, err
	}

	return filepath.Join(d, "case_0"), entries, nil
}

const journal_case_0_directory = "journal_cases/case_0"

// I haven't found a way to store a git repository's
// .git folder in another repository so we have to
// build it during test initialization.
// This function is intended to be called during the TestUnitSpecs()
// function so the cleanupFn can be deferred.
func initCase0() (cleanupFn func(), err error) {
	// Collect the entries we have to commit
	filenames := make([]string, 0, 6)
	err = filepath.Walk(journal_case_0_directory, func(path string, info os.FileInfo, err error) error {
		if err != nil {
			return err
		}

		if !info.IsDir() {
			filenames = append(filenames, info.Name())
		}
		return nil
	})
	if err != nil {
		return nil, err
	}

	// git init
	err = git.Init(journal_case_0_directory)
	if err != nil {
		return nil, err
	}

	// commit the entries
	sort.Sort(entryFilenames(filenames))

	for i, entryFilename := range filenames {
		changes := git.NewChangesIn(journal_case_0_directory)
		changes.Add(git.ChangedFile(entryFilename))
		changes.Msg = fmt.Sprintf("Commit Msg | Entry %d\n", i+1)
		err = changes.Commit()
		if err != nil {
			return nil, err
		}
	}

	// Return a closure that will remove the `journal_cases/case_0/.git` directory
	return func() {
		err := os.RemoveAll(filepath.Join(journal_case_0_directory, ".git"))
		if err != nil {
			panic(err)
		}
	}, nil
}

func mvEntriesIn(directory string, entries []string) (movedEntries []string, err error) {
	err = os.Mkdir(filepath.Join(directory, "entry"), 0700)
	if err != nil {
		return
	}

	mvArgs := make([]string, 0, len(entries)+1)
	mvArgs = append(mvArgs, entries...)
	mvArgs = append(mvArgs, "entry/")

	mvPath, err := exec.LookPath("mv")
	if err != nil {
		return
	}

	mvEntries := exec.Command(mvPath, mvArgs...)
	mvEntries.Dir = directory

	err = mvEntries.Run()
	if err != nil {
		return nil, errors.New(fmt.Sprintf("error moving entries to %s : %v", filepath.Join(directory, "entry/"), err))
	}

	// Update filepaths
	movedEntries = entries
	for i, entry := range entries {
		movedEntries[i] = filepath.Join("entry", entry)
	}
	return
}

func FixCase0(directory string) error {
	entries, err := entriesIn(directory)
	if err != nil {
		return err
	}

	if len(entries) == 0 {
		return errors.New(fmt.Sprintf("%s contains no entries", directory))
	}

	entries, err = mvEntriesIn(directory, entries)
	if err != nil {
		return err
	}

	return nil
}

func DescribeJournalCase0(c gospec.Context) {
	c.Specify("case 0", func() {
		c.Specify("is created as a git repository", func() {
			d, entries, err := newCase0("case_0_init")
			c.Assume(err, IsNil)

			c.Assume(d, gittest.IsAGitRepository)
			c.Expect(git.IsClean(d), IsNil)

			c.Specify("and contains committed entry", func() {
				for i := 0; i < len(entries); i++ {
					entryFilename := entries[i]

					c.Specify(entryFilename, func() {
						// Check that the files were commited in the correct order
						o, err := git.Command(d, "show", "--name-only", "--pretty=format:",
							fmt.Sprintf("HEAD%s", strings.Repeat("^", len(entries)-1-i))).Output()
						c.Assume(err, IsNil)
						c.Expect(strings.TrimSpace(string(o)), Equals, entryFilename)
					})
				}

				// Verify the git tree hash is the same
				o, err := git.Command(d, "show", "-s", "--pretty=format:%T").Output()
				c.Assume(err, IsNil)
				c.Expect(string(o), Equals, "eda50d431c6ffed54ad220b15e5451d4c19d2d02")
			})
		})

		c.Specify("can be fixed", func() {
			d, expectedEntries, err := newCase0("case_0_fix")
			c.Assume(err, IsNil)

			c.Expect(FixCase0(d), IsNil)

			c.Specify("by moving entries into `entry/`", func() {
				info, err := os.Stat(filepath.Join(d, "entry"))
				c.Expect(err, IsNil)
				c.Expect(info.IsDir(), IsTrue)

				actualEntries, err := entriesIn(filepath.Join(d, "entry"))
				c.Assume(err, IsNil)

				c.Expect(actualEntries, ContainsExactly, expectedEntries)
			})
		})
	})
}
<commit_msg>Sort the entry filenames found during walk collection<commit_after>package fix

import (
	"errors"
	"fmt"
	"github.com/ghthor/gospec"
	. "github.com/ghthor/gospec"
	"github.com/ghthor/journal/git"
	"github.com/ghthor/journal/git/gittest"
	"io/ioutil"
	"os"
	"os/exec"
	"path/filepath"
	"sort"
	"strings"
	"time"
)

//A layout to use as the entry's filename
const filenameLayout = "2006-01-02-1504-MST"

type entryFilenames []string

func (f entryFilenames) Len() int { return len(f) }
func (f entryFilenames) Less(i, j int) bool {
	iTime, err := time.Parse(filenameLayout, f[i])
	if err != nil {
		panic(err)
	}

	jTime, err := time.Parse(filenameLayout, f[j])
	if err != nil {
		panic(err)
	}

	return jTime.After(iTime)
}
func (f entryFilenames) Swap(i, j int) { f[i], f[j] = f[j], f[i] }

// Copy the journal_cases/case_0/ files to directory
func entriesIn(directory string) (entries []string, err error) {
	err = filepath.Walk(directory, func(path string, info os.FileInfo, err error) error {
		if err != nil {
			return err
		}

		if !info.IsDir() {
			if !strings.Contains(filepath.Dir(path), ".git") {
				entries = append(entries, info.Name())
			}
		}
		return nil
	})

	sort.Sort(entryFilenames(entries))

	return
}

func newCase0(prefix string) (string, []string, error) {
	// Create a _test/ directory for case_0/
	d, err := ioutil.TempDir("_test", prefix+"_")
	if err != nil {
		return d, nil, err
	}

	// cp -r journal_cases/case_0
	err = exec.Command("cp", "-r", journal_case_0_directory, d).Run()
	if err != nil {
		return d, nil, err
	}

	entries, err := entriesIn(d)
	if err != nil {
		return d, nil, err
	}

	return filepath.Join(d, "case_0"), entries, nil
}

const journal_case_0_directory = "journal_cases/case_0"

// I haven't found a way to store a git repository's
// .git folder in another repository so we have to
// build it during test initialization.
// This function is intended to be called during the TestUnitSpecs()
// function so the cleanupFn can be deferred.
func initCase0() (cleanupFn func(), err error) {
	// Collect the entries we have to commit
	filenames := make([]string, 0, 6)
	err = filepath.Walk(journal_case_0_directory, func(path string, info os.FileInfo, err error) error {
		if err != nil {
			return err
		}

		if !info.IsDir() {
			filenames = append(filenames, info.Name())
		}
		return nil
	})
	if err != nil {
		return nil, err
	}

	// git init
	err = git.Init(journal_case_0_directory)
	if err != nil {
		return nil, err
	}

	// commit the entries
	sort.Sort(entryFilenames(filenames))

	for i, entryFilename := range filenames {
		changes := git.NewChangesIn(journal_case_0_directory)
		changes.Add(git.ChangedFile(entryFilename))
		changes.Msg = fmt.Sprintf("Commit Msg | Entry %d\n", i+1)
		err = changes.Commit()
		if err != nil {
			return nil, err
		}
	}

	// Return a closure that will remove the `journal_cases/case_0/.git` directory
	return func() {
		err := os.RemoveAll(filepath.Join(journal_case_0_directory, ".git"))
		if err != nil {
			panic(err)
		}
	}, nil
}

func mvEntriesIn(directory string, entries []string) (movedEntries []string, err error) {
	err = os.Mkdir(filepath.Join(directory, "entry"), 0700)
	if err != nil {
		return
	}

	mvArgs := make([]string, 0, len(entries)+1)
	mvArgs = append(mvArgs, entries...)
	mvArgs = append(mvArgs, "entry/")

	mvPath, err := exec.LookPath("mv")
	if err != nil {
		return
	}

	mvEntries := exec.Command(mvPath, mvArgs...)
	mvEntries.Dir = directory

	err = mvEntries.Run()
	if err != nil {
		return nil, errors.New(fmt.Sprintf("error moving entries to %s : %v", filepath.Join(directory, "entry/"), err))
	}

	// Update filepaths
	movedEntries = entries
	for i, entry := range entries {
		movedEntries[i] = filepath.Join("entry", entry)
	}
	return
}

func FixCase0(directory string) error {
	entries, err := entriesIn(directory)
	if err != nil {
		return err
	}

	if len(entries) == 0 {
		return errors.New(fmt.Sprintf("%s contains no entries", directory))
	}

	entries, err = mvEntriesIn(directory, entries)
	if err != nil {
		return err
	}

	return nil
}

func DescribeJournalCase0(c gospec.Context) {
	c.Specify("case 0", func() {
		c.Specify("is created as a git repository", func() {
			d, entries, err := newCase0("case_0_init")
			c.Assume(err, IsNil)

			c.Assume(d, gittest.IsAGitRepository)
			c.Expect(git.IsClean(d), IsNil)

			c.Specify("and contains committed entry", func() {
				for i := 0; i < len(entries); i++ {
					entryFilename := entries[i]

					c.Specify(entryFilename, func() {
						// Check that the files were commited in the correct order
						o, err := git.Command(d, "show", "--name-only", "--pretty=format:",
							fmt.Sprintf("HEAD%s", strings.Repeat("^", len(entries)-1-i))).Output()
						c.Assume(err, IsNil)
						c.Expect(strings.TrimSpace(string(o)), Equals, entryFilename)
					})
				}

				// Verify the git tree hash is the same
				o, err := git.Command(d, "show", "-s", "--pretty=format:%T").Output()
				c.Assume(err, IsNil)
				c.Expect(string(o), Equals, "eda50d431c6ffed54ad220b15e5451d4c19d2d02")
			})
		})

		c.Specify("can be fixed", func() {
			d, expectedEntries, err := newCase0("case_0_fix")
			c.Assume(err, IsNil)

			c.Expect(FixCase0(d), IsNil)

			c.Specify("by moving entries into `entry/`", func() {
				info, err := os.Stat(filepath.Join(d, "entry"))
				c.Expect(err, IsNil)
				c.Expect(info.IsDir(), IsTrue)

				actualEntries, err := entriesIn(filepath.Join(d, "entry"))
				c.Assume(err, IsNil)

				c.Expect(actualEntries, ContainsExactly, expectedEntries)
			})
		})
	})
}
<|endoftext|>


<commit_before>package main

import (
	"os"
	"strings"
	"sync"
	"time"

	log "github.com/sirupsen/logrus"
	"github.com/spf13/cobra"

	"github.com/authelia/authelia/v4/internal/utils"
)

func buildAutheliaBinary(xflags []string, buildkite bool) {
	if buildkite {
		var wg sync.WaitGroup

		s := time.Now()

		wg.Add(2)

		go func() {
			defer wg.Done()

			cmd := utils.CommandWithStdout("gox", "-output={{.Dir}}-{{.OS}}-{{.Arch}}-musl", "-buildmode=pie", "-trimpath", "-cgo", "-ldflags=-linkmode=external -s -w "+strings.Join(xflags, " "), "-osarch=linux/amd64 linux/arm linux/arm64", "./cmd/authelia/")

			cmd.Env = append(os.Environ(),
				"CGO_CPPFLAGS=-D_FORTIFY_SOURCE=2 -fstack-protector-strong", "CGO_LDFLAGS=-Wl,-z,relro,-z,now",
				"GOX_LINUX_ARM_CC=arm-linux-musleabihf-gcc", "GOX_LINUX_ARM64_CC=aarch64-linux-musl-gcc")

			err := cmd.Run()
			if err != nil {
				log.Fatal(err)
			}
		}()

		go func() {
			defer wg.Done()

			cmd := utils.CommandWithStdout("bash", "-c", "docker run --rm -e GOX_LINUX_ARM_CC=arm-linux-gnueabihf-gcc -e GOX_LINUX_ARM64_CC=aarch64-linux-gnu-gcc -e GOX_FREEBSD_AMD64_CC=x86_64-pc-freebsd13-gcc -v ${PWD}:/workdir -v /buildkite/.go:/root/go authelia/crossbuild "+
				"gox -output={{.Dir}}-{{.OS}}-{{.Arch}} -buildmode=pie -trimpath -cgo -ldflags=\"-linkmode=external -s -w "+strings.Join(xflags, " ")+"\" -osarch=\"linux/amd64 linux/arm linux/arm64 freebsd/amd64\" ./cmd/authelia/")

			err := cmd.Run()
			if err != nil {
				log.Fatal(err)
			}
		}()

		wg.Wait()

		e := time.Since(s)

		log.Debugf("Binary compilation completed in %s.", e)
	} else {
		cmd := utils.CommandWithStdout("go", "build", "-buildmode=pie", "-trimpath", "-o", OutputDir+"/authelia", "-ldflags", "-linkmode=external -s -w "+strings.Join(xflags, " "), "./cmd/authelia/")

		cmd.Env = append(os.Environ(),
			"CGO_CPPFLAGS=-D_FORTIFY_SOURCE=2 -fstack-protector-strong", "CGO_LDFLAGS=-Wl,-z,relro,-z,now")

		err := cmd.Run()
		if err != nil {
			log.Fatal(err)
		}
	}
}

func buildFrontend(branch string) {
	cmd := utils.CommandWithStdout("pnpm", "install")
	cmd.Dir = webDirectory

	err := cmd.Run()
	if err != nil {
		log.Fatal(err)
	}

	if !strings.HasPrefix(branch, "renovate/") {
		cmd = utils.CommandWithStdout("pnpm", "build")
		cmd.Dir = webDirectory

		err = cmd.Run()
		if err != nil {
			log.Fatal(err)
		}
	}
}

func buildSwagger() {
	swaggerVer := "3.52.4"
	cmd := utils.CommandWithStdout("bash", "-c", "wget -q https://github.com/swagger-api/swagger-ui/archive/v"+swaggerVer+".tar.gz -O ./v"+swaggerVer+".tar.gz")

	err := cmd.Run()
	if err != nil {
		log.Fatal(err)
	}

	cmd = utils.CommandWithStdout("cp", "-r", "api", "internal/server/public_html")

	err = cmd.Run()
	if err != nil {
		log.Fatal(err)
	}

	cmd = utils.CommandWithStdout("tar", "-C", "internal/server/public_html/api", "--exclude=index.html", "--strip-components=2", "-xf", "v"+swaggerVer+".tar.gz", "swagger-ui-"+swaggerVer+"/dist")

	err = cmd.Run()
	if err != nil {
		log.Fatal(err)
	}

	cmd = utils.CommandWithStdout("rm", "./v"+swaggerVer+".tar.gz")

	err = cmd.Run()
	if err != nil {
		log.Fatal(err)
	}
}

func cleanAssets() {
	if err := os.Rename("internal/server/public_html", OutputDir+"/public_html"); err != nil {
		log.Fatal(err)
	}

	cmd := utils.CommandWithStdout("mkdir", "-p", "internal/server/public_html/api")

	if err := cmd.Run(); err != nil {
		log.Fatal(err)
	}

	cmd = utils.CommandWithStdout("bash", "-c", "touch internal/server/public_html/{index.html,api/index.html,api/openapi.yml}")

	if err := cmd.Run(); err != nil {
		log.Fatal(err)
	}
}

// Build build Authelia.
func Build(cobraCmd *cobra.Command, args []string) {
	buildkite, _ := cobraCmd.Flags().GetBool("buildkite")
	branch := os.Getenv("BUILDKITE_BRANCH")

	if strings.HasPrefix(branch, "renovate/") {
		buildFrontend(branch)
		log.Info("Skip building Authelia for deps...")
		os.Exit(0)
	}

	log.Info("Building Authelia...")

	Clean(cobraCmd, args)

	xflags, err := getXFlags(branch, os.Getenv("BUILDKITE_BUILD_NUMBER"), "")
	if err != nil {
		log.Fatal(err)
	}

	log.Debug("Creating `" + OutputDir + "` directory")
	err = os.MkdirAll(OutputDir, os.ModePerm)

	if err != nil {
		log.Fatal(err)
	}

	log.Debug("Building Authelia frontend...")
	buildFrontend(branch)

	log.Debug("Building swagger-ui frontend...")
	buildSwagger()

	if buildkite {
		log.Debug("Building Authelia Go binaries with gox...")
	} else {
		log.Debug("Building Authelia Go binary...")
	}

	buildAutheliaBinary(xflags, buildkite)

	cleanAssets()
}
<commit_msg>build(deps): update swagger-ui to v4.1.0 (#2576)<commit_after>package main

import (
	"os"
	"strings"
	"sync"
	"time"

	log "github.com/sirupsen/logrus"
	"github.com/spf13/cobra"

	"github.com/authelia/authelia/v4/internal/utils"
)

func buildAutheliaBinary(xflags []string, buildkite bool) {
	if buildkite {
		var wg sync.WaitGroup

		s := time.Now()

		wg.Add(2)

		go func() {
			defer wg.Done()

			cmd := utils.CommandWithStdout("gox", "-output={{.Dir}}-{{.OS}}-{{.Arch}}-musl", "-buildmode=pie", "-trimpath", "-cgo", "-ldflags=-linkmode=external -s -w "+strings.Join(xflags, " "), "-osarch=linux/amd64 linux/arm linux/arm64", "./cmd/authelia/")

			cmd.Env = append(os.Environ(),
				"CGO_CPPFLAGS=-D_FORTIFY_SOURCE=2 -fstack-protector-strong", "CGO_LDFLAGS=-Wl,-z,relro,-z,now",
				"GOX_LINUX_ARM_CC=arm-linux-musleabihf-gcc", "GOX_LINUX_ARM64_CC=aarch64-linux-musl-gcc")

			err := cmd.Run()
			if err != nil {
				log.Fatal(err)
			}
		}()

		go func() {
			defer wg.Done()

			cmd := utils.CommandWithStdout("bash", "-c", "docker run --rm -e GOX_LINUX_ARM_CC=arm-linux-gnueabihf-gcc -e GOX_LINUX_ARM64_CC=aarch64-linux-gnu-gcc -e GOX_FREEBSD_AMD64_CC=x86_64-pc-freebsd13-gcc -v ${PWD}:/workdir -v /buildkite/.go:/root/go authelia/crossbuild "+
				"gox -output={{.Dir}}-{{.OS}}-{{.Arch}} -buildmode=pie -trimpath -cgo -ldflags=\"-linkmode=external -s -w "+strings.Join(xflags, " ")+"\" -osarch=\"linux/amd64 linux/arm linux/arm64 freebsd/amd64\" ./cmd/authelia/")

			err := cmd.Run()
			if err != nil {
				log.Fatal(err)
			}
		}()

		wg.Wait()

		e := time.Since(s)

		log.Debugf("Binary compilation completed in %s.", e)
	} else {
		cmd := utils.CommandWithStdout("go", "build", "-buildmode=pie", "-trimpath", "-o", OutputDir+"/authelia", "-ldflags", "-linkmode=external -s -w "+strings.Join(xflags, " "), "./cmd/authelia/")

		cmd.Env = append(os.Environ(),
			"CGO_CPPFLAGS=-D_FORTIFY_SOURCE=2 -fstack-protector-strong", "CGO_LDFLAGS=-Wl,-z,relro,-z,now")

		err := cmd.Run()
		if err != nil {
			log.Fatal(err)
		}
	}
}

func buildFrontend(branch string) {
	cmd := utils.CommandWithStdout("pnpm", "install")
	cmd.Dir = webDirectory

	err := cmd.Run()
	if err != nil {
		log.Fatal(err)
	}

	if !strings.HasPrefix(branch, "renovate/") {
		cmd = utils.CommandWithStdout("pnpm", "build")
		cmd.Dir = webDirectory

		err = cmd.Run()
		if err != nil {
			log.Fatal(err)
		}
	}
}

func buildSwagger() {
	swaggerVer := "4.1.0"
	cmd := utils.CommandWithStdout("bash", "-c", "wget -q https://github.com/swagger-api/swagger-ui/archive/v"+swaggerVer+".tar.gz -O ./v"+swaggerVer+".tar.gz")

	err := cmd.Run()
	if err != nil {
		log.Fatal(err)
	}

	cmd = utils.CommandWithStdout("cp", "-r", "api", "internal/server/public_html")

	err = cmd.Run()
	if err != nil {
		log.Fatal(err)
	}

	cmd = utils.CommandWithStdout("tar", "-C", "internal/server/public_html/api", "--exclude=index.html", "--strip-components=2", "-xf", "v"+swaggerVer+".tar.gz", "swagger-ui-"+swaggerVer+"/dist")

	err = cmd.Run()
	if err != nil {
		log.Fatal(err)
	}

	cmd = utils.CommandWithStdout("rm", "./v"+swaggerVer+".tar.gz")

	err = cmd.Run()
	if err != nil {
		log.Fatal(err)
	}
}

func cleanAssets() {
	if err := os.Rename("internal/server/public_html", OutputDir+"/public_html"); err != nil {
		log.Fatal(err)
	}

	cmd := utils.CommandWithStdout("mkdir", "-p", "internal/server/public_html/api")

	if err := cmd.Run(); err != nil {
		log.Fatal(err)
	}

	cmd = utils.CommandWithStdout("bash", "-c", "touch internal/server/public_html/{index.html,api/index.html,api/openapi.yml}")

	if err := cmd.Run(); err != nil {
		log.Fatal(err)
	}
}

// Build build Authelia.
func Build(cobraCmd *cobra.Command, args []string) {
	buildkite, _ := cobraCmd.Flags().GetBool("buildkite")
	branch := os.Getenv("BUILDKITE_BRANCH")

	if strings.HasPrefix(branch, "renovate/") {
		buildFrontend(branch)
		log.Info("Skip building Authelia for deps...")
		os.Exit(0)
	}

	log.Info("Building Authelia...")

	Clean(cobraCmd, args)

	xflags, err := getXFlags(branch, os.Getenv("BUILDKITE_BUILD_NUMBER"), "")
	if err != nil {
		log.Fatal(err)
	}

	log.Debug("Creating `" + OutputDir + "` directory")
	err = os.MkdirAll(OutputDir, os.ModePerm)

	if err != nil {
		log.Fatal(err)
	}

	log.Debug("Building Authelia frontend...")
	buildFrontend(branch)

	log.Debug("Building swagger-ui frontend...")
	buildSwagger()

	if buildkite {
		log.Debug("Building Authelia Go binaries with gox...")
	} else {
		log.Debug("Building Authelia Go binary...")
	}

	buildAutheliaBinary(xflags, buildkite)

	cleanAssets()
}
<|endoftext|>


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

// This file tests bit sets
package bitset

import (
	"testing"
)

func TestbBitSetNew(t *testing.T) {
	v := New(10000)
	if v.Bit(0) != false {
		t.Errorf("Unable to make a bit set and read its 0th value.")
	}
}

func TestBitSetIsClear(t *testing.T) {
	v := New(1000)
	for i := uint(0); i < 1000; i++ {
		if v.Bit(i) != false {
			t.Errorf("Bit %d is set, and it shouldn't be.", i)
		}
	}
}

func TestBitSetAndGet(t *testing.T) {
	v := New(1000)
	v.SetBit(100)
	if v.Bit(100) != true {
		t.Errorf("Bit %d is clear, and it shouldn't be.", 100)
	}
}

func TestLotsOfSetsAndGets(t *testing.T) {
	tot := uint(100000)
	v := New(tot)
	for i := uint(0); i < tot; i+=2 {
		v.SetBit(i)
	}
	for i := uint(0); i < tot; i++ {
		if i % 2 == 0 {
			if v.Bit(i) != true {
				t.Errorf("Bit %d is clear, and it shouldn't be.", i)
			}
		} else {
			if v.Bit(i) != false {
				t.Errorf("Bit %d is set, and it shouldn't be.", i)
			}
		}
	}
}

func TestClear(t *testing.T) {
	tot := uint(1000)
	v := New(tot)
	for i := uint(0); i < tot; i++ {
		v.SetBit(i)
	}
	v.Clear()
	for i := uint(0); i < tot; i++ {
		if v.Bit(i) != false {
			t.Errorf("Bit %d is set, and it shouldn't be.", i)
			break
		}
	}
}

func TestOutOfBoundsBad(t *testing.T) {
	v := New(64)
	defer func() {
	        if r := recover(); r != nil {
	            t.Error("Out of index error within the next set of bits should not have caused a panic")
	        }
	    }()
	v.SetBit(1000)
}

func TestOutOfBoundsOK(t *testing.T) {
	v := New(65)
	defer func() {
	        if r := recover(); r != nil {
	            t.Error("Out of index error within the next set of bits should not caused a panic")
	        }
	    }()
	v.SetBit(66)
}

func TestMaxSizet(t *testing.T) {
	v := New(1000)
	if v.MaxSize() != 1000 {
		t.Errorf("MaxSize should be 1000, but is %d.", v.MaxSize())
	}
}

func TestSize(t *testing.T) {
	tot := uint(64*4+11) // just some multi unit64 number
	v := New(tot)
	checkLast := true
	for i := uint(0); i < tot; i++ {
		sz := v.Size()
		if sz != i {
			t.Errorf("Size reported as %d, but it should be %d", sz, i)
			checkLast = false
			break
		}
		v.SetBit(i)
	}
	if checkLast {
		sz := v.Size()
		if sz != tot {
			t.Errorf("After all bits set, size reported as %d, but it should be %d", sz, tot)
		}
	}
}
<commit_msg>another sanity test on Size<commit_after>// Copyright 2011 Will Fitzgerald. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// This file tests bit sets
package bitset

import (
	"testing"
)

func TestbBitSetNew(t *testing.T) {
	v := New(10000)
	if v.Bit(0) != false {
		t.Errorf("Unable to make a bit set and read its 0th value.")
	}
}

func TestBitSetIsClear(t *testing.T) {
	v := New(1000)
	for i := uint(0); i < 1000; i++ {
		if v.Bit(i) != false {
			t.Errorf("Bit %d is set, and it shouldn't be.", i)
		}
	}
}

func TestBitSetAndGet(t *testing.T) {
	v := New(1000)
	v.SetBit(100)
	if v.Bit(100) != true {
		t.Errorf("Bit %d is clear, and it shouldn't be.", 100)
	}
}

func TestLotsOfSetsAndGets(t *testing.T) {
	tot := uint(100000)
	v := New(tot)
	for i := uint(0); i < tot; i+=2 {
		v.SetBit(i)
	}
	for i := uint(0); i < tot; i++ {
		if i % 2 == 0 {
			if v.Bit(i) != true {
				t.Errorf("Bit %d is clear, and it shouldn't be.", i)
			}
		} else {
			if v.Bit(i) != false {
				t.Errorf("Bit %d is set, and it shouldn't be.", i)
			}
		}
	}
}

func TestClear(t *testing.T) {
	tot := uint(1000)
	v := New(tot)
	for i := uint(0); i < tot; i++ {
		v.SetBit(i)
	}
	v.Clear()
	for i := uint(0); i < tot; i++ {
		if v.Bit(i) != false {
			t.Errorf("Bit %d is set, and it shouldn't be.", i)
			break
		}
	}
}

func TestOutOfBoundsBad(t *testing.T) {
	v := New(64)
	defer func() {
	        if r := recover(); r != nil {
	            t.Error("Out of index error within the next set of bits should not have caused a panic")
	        }
	    }()
	v.SetBit(1000)
}

func TestOutOfBoundsOK(t *testing.T) {
	v := New(65)
	defer func() {
	        if r := recover(); r != nil {
	            t.Error("Out of index error within the next set of bits should not caused a panic")
	        }
	    }()
	v.SetBit(66)
}

func TestMaxSizet(t *testing.T) {
	v := New(1000)
	if v.MaxSize() != 1000 {
		t.Errorf("MaxSize should be 1000, but is %d.", v.MaxSize())
	}
}

func TestSize(t *testing.T) {
	tot := uint(64*4+11) // just some multi unit64 number
	v := New(tot)
	checkLast := true
	for i := uint(0); i < tot; i++ {
		sz := v.Size()
		if sz != i {
			t.Errorf("Size reported as %d, but it should be %d", sz, i)
			checkLast = false
			break
		}
		v.SetBit(i)
	}
	if checkLast {
		sz := v.Size()
		if sz != tot {
			t.Errorf("After all bits set, size reported as %d, but it should be %d", sz, tot)
		}
	}
}

// test setting every 3rd bit, just in case something odd is happening
func TestSize2(t *testing.T) {
	tot := uint(64*4+11) // just some multi unit64 number
	v := New(tot)
	for i := uint(0); i < tot; i+=3 {
		sz := v.Size()
		if sz != i/3 {
			t.Errorf("Size reported as %d, but it should be %d", sz, i)
			break
		}
		v.SetBit(i)
	}
}
<|endoftext|>


<commit_before>package lifecycle

import (
	"time"

	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
	. "github.com/vito/cmdtest/matchers"

	. "github.com/vito/runtime-integration/helpers"
)

var cfConfig = LoadCfConfig()

var _ = Describe("Changing an app's start command", func() {
	BeforeEach(func() {
		AppName = RandomName()

		Expect(
			Cf(
				"push", AppName,
				"-p", doraPath,
				"-c", "FOO=foo bundle exec rackup config.ru -p $PORT",
			),
		).To(SayWithTimeout("Started", 2*time.Minute))
	})

	AfterEach(func() {
		Expect(Cf("delete", AppName, "-f")).To(
			SayWithTimeout("OK", 30*time.Second),
		)
	})

	FIt("takes effect after a restart, not requiring a push", func() {
		Eventually(Curling("/env/FOO")).Should(Say("foo"))

		var response AppQueryResponse

		ApiRequest("GET", "/v2/apps?q=name:" + AppName, &response)

		Expect(response.Resources).To(HaveLen(1))

		appGuid := response.Resources[0].Metadata.Guid

		ApiRequest(
			"PUT",
			"/v2/apps/" + appGuid,
			nil,
			`{"command":"FOO=bar bundle exec rackup config.ru -p $PORT"}`,
		)

		restart := Cf("restart", AppName)

		Expect(restart).To(Say("Stopping"))
		Expect(restart).To(Say("OK"))

		Expect(restart).To(Say("Starting"))
		Expect(restart).To(Say("OK"))

		Expect(restart).To(Say("Started"))

		Eventually(Curling("/env/FOO")).Should(Say("bar"))
	})
})
<commit_msg>woops, un-focus start command test<commit_after>package lifecycle

import (
	"time"

	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
	. "github.com/vito/cmdtest/matchers"

	. "github.com/vito/runtime-integration/helpers"
)

var cfConfig = LoadCfConfig()

var _ = Describe("Changing an app's start command", func() {
	BeforeEach(func() {
		AppName = RandomName()

		Expect(
			Cf(
				"push", AppName,
				"-p", doraPath,
				"-c", "FOO=foo bundle exec rackup config.ru -p $PORT",
			),
		).To(SayWithTimeout("Started", 2*time.Minute))
	})

	AfterEach(func() {
		Expect(Cf("delete", AppName, "-f")).To(
			SayWithTimeout("OK", 30*time.Second),
		)
	})

	It("takes effect after a restart, not requiring a push", func() {
		Eventually(Curling("/env/FOO")).Should(Say("foo"))

		var response AppQueryResponse

		ApiRequest("GET", "/v2/apps?q=name:" + AppName, &response)

		Expect(response.Resources).To(HaveLen(1))

		appGuid := response.Resources[0].Metadata.Guid

		ApiRequest(
			"PUT",
			"/v2/apps/" + appGuid,
			nil,
			`{"command":"FOO=bar bundle exec rackup config.ru -p $PORT"}`,
		)

		restart := Cf("restart", AppName)

		Expect(restart).To(Say("Stopping"))
		Expect(restart).To(Say("OK"))

		Expect(restart).To(Say("Starting"))
		Expect(restart).To(Say("OK"))

		Expect(restart).To(Say("Started"))

		Eventually(Curling("/env/FOO")).Should(Say("bar"))
	})
})
<|endoftext|>


<commit_before>// This Source Code Form is subject to the terms of the Mozilla Public
// License, version 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

// Implements Git Smart HTTP backend using its C implementation as reference:
// https://github.com/git/git/blob/master/http-backend.c
package main

import (
	"flag"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"os/exec"
	"path/filepath"
	"regexp"
	"runtime"
	"strconv"
	"strings"
	"time"

	"github.com/BurntSushi/toml"
	"github.com/c4milo/handlers/logger"
	"github.com/hashicorp/logutils"
	"github.com/stretchr/graceful"
)

// Version is injected in build time and defined in the Makefile
var Version string

// Name is injected in build time and defined in the Makefile
var Name string

type Config struct {
	Bind            string `toml:"bind"`
	Port            uint   `toml:"port"`
	ReposPath       string `toml:"repos_path"`
	LogLevel        string `toml:"log_level"`
	LogFilePath     string `toml:"log_file"`
	ShutdownTimeout string `toml:"shutdown_timeout"`
}

// Default configuration
var config Config = Config{
	Bind:            "localhost",
	Port:            12345,
	LogLevel:        "WARN",
	ShutdownTimeout: "15s",
}

// Configuration file path
var configFile string

func init() {
	if !checkGitVersion(2, 2, 1) {
		log.Fatalln("Git >= v2.2.1 is required")
	}

	reposPath, err := ioutil.TempDir(os.TempDir(), Name)
	if err != nil {
		log.Fatalf("%v\n", err)
	}
	config.ReposPath = reposPath

	flag.StringVar(&configFile, "f", "", "config file path")
	flag.Parse()

	if _, err := toml.DecodeFile(configFile, &config); err != nil {
		log.Printf("[ERROR] %v", err)
		log.Print("[ERROR] Parsing config file, using default configuration")
	}
}

func GitDHTTPHandler(w http.ResponseWriter, req *http.Request) {
	handlers := map[*regexp.Regexp]func(http.ResponseWriter, *http.Request, string){
		regexp.MustCompile("(.*?)/git-upload-pack$"):  UploadPack,
		regexp.MustCompile("(.*?)/git-receive-pack$"): ReceivePack,
		regexp.MustCompile("(.*?)/info/refs$"):        InfoRefs,
	}

	for re, handler := range handlers {
		if m := re.FindStringSubmatch(req.URL.Path); m != nil {
			repoPath := m[1]
			handler(w, req, repoPath)
			return
		}
	}
	w.WriteHeader(http.StatusBadRequest)
	w.Write([]byte("Bad Request"))
}

func main() {
	var logWriter io.Writer
	if config.LogFilePath != "" {
		var err error
		logWriter, err = os.OpenFile(config.LogFilePath, os.O_RDWR|os.O_APPEND, 0660)
		if err != nil {
			log.Printf("[WARN] %v", err)
		}
	}

	if logWriter == nil {
		logWriter = os.Stderr
	}

	filter := &logutils.LevelFilter{
		Levels:   []logutils.LogLevel{"DEBUG", "WARN", "ERROR"},
		MinLevel: logutils.LogLevel(config.LogLevel),
		Writer:   logWriter,
	}

	log.SetOutput(filter)

	mux := http.DefaultServeMux
	mux.HandleFunc("/", GitDHTTPHandler)

	address := fmt.Sprintf("%s:%d", config.Bind, config.Port)
	timeout, err := time.ParseDuration(config.ShutdownTimeout)
	if err != nil {
		log.Fatalf("[ERROR] %v", err)
	}

	log.Printf("[INFO] Listening on %s...", address)
	log.Printf("[INFO] Serving Git repositories over HTTP from %s", config.ReposPath)
	graceful.Run(address, timeout, logger.Handler(mux, logger.AppName("gitd")))
}

// Runs git-upload-pack in a safe manner
func UploadPack(w http.ResponseWriter, req *http.Request, repoPath string) {
	if req.Method != "POST" {
		w.WriteHeader(http.StatusMethodNotAllowed)
		w.Write([]byte("Method Not Allowed"))
		return
	}
	process := "git-upload-pack"
	cwd := filepath.Join(config.ReposPath, repoPath)

	headers := w.Header()
	headers.Add("Content-Type", fmt.Sprintf("application/x-%s-result", process))
	w.WriteHeader(http.StatusOK)

	cmd := exec.Command(process, "--stateless-rpc", ".")
	cmd.Dir = cwd
	runCommand(w, req.Body, cmd)
}

//Runs git-receive-pack in a safe manner
func ReceivePack(w http.ResponseWriter, req *http.Request, repoPath string) {
	if req.Method != "POST" {
		w.WriteHeader(http.StatusMethodNotAllowed)
		w.Write([]byte("Method Not Allowed"))
		return
	}
	process := "git-receive-pack"
	cwd := filepath.Join(config.ReposPath, repoPath)

	headers := w.Header()
	headers.Add("Content-Type", fmt.Sprintf("application/x-%s-result", process))
	w.WriteHeader(http.StatusOK)

	cmd := exec.Command(process, "--stateless-rpc", ".")
	cmd.Dir = cwd
	runCommand(w, req.Body, cmd)
}

func InfoRefs(w http.ResponseWriter, req *http.Request, repoPath string) {
	if req.Method != "GET" {
		w.WriteHeader(http.StatusMethodNotAllowed)
		w.Write([]byte("Method Not Allowed"))
		return
	}

	process := req.URL.Query().Get("service")
	cwd := filepath.Join(config.ReposPath, repoPath)

	if process != "git-receive-pack" && process != "git-upload-pack" {
		w.WriteHeader(http.StatusBadRequest)
		w.Write([]byte("Bad Request"))
		return
	}

	headers := w.Header()
	headers.Add("Content-Type", fmt.Sprintf("application/x-%s-advertisement", process))
	w.WriteHeader(http.StatusOK)

	w.Write(packetWrite(fmt.Sprintf("# service=%s\n", process)))
	w.Write(packetFlush())

	cmd := exec.Command(process, "--stateless-rpc", "--advertise-refs", ".")
	cmd.Dir = cwd
	runCommand(w, req.Body, cmd)
}

// Executes a shell command and pipes its output to HTTP response writer.
// DO NOT expose this function directly to end users as it creates a security breach
func runCommand(w io.Writer, r io.Reader, cmd *exec.Cmd) {
	if cmd.Dir != "" {
		cmd.Dir = sanitize(cmd.Dir)
	}

	log.Printf("[DEBUG] Running command from %s: %s %s ", cmd.Dir, cmd.Path, cmd.Args)

	stdin, err := cmd.StdinPipe()
	if err != nil {
		log.Printf("[ERROR] %v", err)
	}

	stdout, err := cmd.StdoutPipe()
	if err != nil {
		log.Printf("[ERROR] %v", err)
	}

	if err := cmd.Start(); err != nil {
		log.Printf("[ERROR] %v", err)
	}

	io.Copy(stdin, r)
	io.Copy(w, stdout)
	cmd.Wait()
}

// Returns bytes of a git packet containing the given string
func packetWrite(str string) []byte {
	s := strconv.FormatInt(int64((len(str) + 4)), 16)

	m := len(s) % 4
	if m != 0 {
		s = strings.Repeat("0", 4-m) + s
	}

	return []byte(s + str)
}

func packetFlush() []byte {
	return []byte("0000")
}

// Sanitizes name to avoid overwriting sensitive system files
// or executing forbidden binaries
func sanitize(name string) string {
	// Gets rid of volume drive label in Windows
	if len(name) > 1 && name[1] == ':' && runtime.GOOS == "windows" {
		name = name[2:]
	}

	name = filepath.Clean(name)
	name = filepath.ToSlash(name)
	for strings.HasPrefix(name, "../") {
		name = name[3:]
	}
	return name
}

func checkGitVersion(major, minor, patch uint) bool {
	//TODO
	return true
}
<commit_msg>Makes it more idiomatic<commit_after>// This Source Code Form is subject to the terms of the Mozilla Public
// License, version 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

// Implements Git Smart HTTP backend using its C implementation as reference:
// https://github.com/git/git/blob/master/http-backend.c
package main

import (
	"flag"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"os/exec"
	"path/filepath"
	"regexp"
	"runtime"
	"strconv"
	"strings"
	"time"

	"github.com/BurntSushi/toml"
	"github.com/c4milo/handlers/logger"
	"github.com/hashicorp/logutils"
	"github.com/stretchr/graceful"
)

// Version is injected in build time and defined in the Makefile
var Version string

// Name is injected in build time and defined in the Makefile
var Name string

type Config struct {
	Bind            string `toml:"bind"`
	Port            uint   `toml:"port"`
	ReposPath       string `toml:"repos_path"`
	LogLevel        string `toml:"log_level"`
	LogFilePath     string `toml:"log_file"`
	ShutdownTimeout string `toml:"shutdown_timeout"`
}

// Default configuration
var config Config = Config{
	Bind:            "localhost",
	Port:            12345,
	LogLevel:        "WARN",
	ShutdownTimeout: "15s",
}

// Configuration file path
var configFile string

func init() {
	if !checkGitVersion(2, 2, 1) {
		log.Fatalln("Git >= v2.2.1 is required")
	}

	reposPath, err := ioutil.TempDir(os.TempDir(), Name)
	if err != nil {
		log.Fatalf("%v\n", err)
	}
	config.ReposPath = reposPath

	flag.StringVar(&configFile, "f", "", "config file path")
	flag.Parse()

	if _, err := toml.DecodeFile(configFile, &config); err != nil {
		log.Printf("[ERROR] %v", err)
		log.Print("[ERROR] Parsing config file, using default configuration")
	}
}

func Handler(w http.ResponseWriter, req *http.Request) {
	handlers := map[*regexp.Regexp]func(http.ResponseWriter, *http.Request, string){
		regexp.MustCompile("(.*?)/git-upload-pack$"):  UploadPack,
		regexp.MustCompile("(.*?)/git-receive-pack$"): ReceivePack,
		regexp.MustCompile("(.*?)/info/refs$"):        InfoRefs,
	}

	for re, handler := range handlers {
		if m := re.FindStringSubmatch(req.URL.Path); m != nil {
			repoPath := m[1]
			handler(w, req, repoPath)
			return
		}
	}
	w.WriteHeader(http.StatusBadRequest)
	w.Write([]byte("Bad Request"))
}

func main() {
	var logWriter io.Writer
	if config.LogFilePath != "" {
		var err error
		logWriter, err = os.OpenFile(config.LogFilePath, os.O_RDWR|os.O_APPEND, 0660)
		if err != nil {
			log.Printf("[WARN] %v", err)
		}
	}

	if logWriter == nil {
		logWriter = os.Stderr
	}

	filter := &logutils.LevelFilter{
		Levels:   []logutils.LogLevel{"DEBUG", "WARN", "ERROR"},
		MinLevel: logutils.LogLevel(config.LogLevel),
		Writer:   logWriter,
	}

	log.SetOutput(filter)

	mux := http.DefaultServeMux
	mux.HandleFunc("/", Handler)

	address := fmt.Sprintf("%s:%d", config.Bind, config.Port)
	timeout, err := time.ParseDuration(config.ShutdownTimeout)
	if err != nil {
		log.Fatalf("[ERROR] %v", err)
	}

	log.Printf("[INFO] Listening on %s...", address)
	log.Printf("[INFO] Serving Git repositories over HTTP from %s", config.ReposPath)
	graceful.Run(address, timeout, logger.Handler(mux, logger.AppName("gitd")))
}

// Runs git-upload-pack in a safe manner
func UploadPack(w http.ResponseWriter, req *http.Request, repoPath string) {
	if req.Method != "POST" {
		w.WriteHeader(http.StatusMethodNotAllowed)
		w.Write([]byte("Method Not Allowed"))
		return
	}
	process := "git-upload-pack"
	cwd := filepath.Join(config.ReposPath, repoPath)

	headers := w.Header()
	headers.Add("Content-Type", fmt.Sprintf("application/x-%s-result", process))
	w.WriteHeader(http.StatusOK)

	cmd := exec.Command(process, "--stateless-rpc", ".")
	cmd.Dir = cwd
	runCommand(w, req.Body, cmd)
}

//Runs git-receive-pack in a safe manner
func ReceivePack(w http.ResponseWriter, req *http.Request, repoPath string) {
	if req.Method != "POST" {
		w.WriteHeader(http.StatusMethodNotAllowed)
		w.Write([]byte("Method Not Allowed"))
		return
	}
	process := "git-receive-pack"
	cwd := filepath.Join(config.ReposPath, repoPath)

	headers := w.Header()
	headers.Add("Content-Type", fmt.Sprintf("application/x-%s-result", process))
	w.WriteHeader(http.StatusOK)

	cmd := exec.Command(process, "--stateless-rpc", ".")
	cmd.Dir = cwd
	runCommand(w, req.Body, cmd)
}

func InfoRefs(w http.ResponseWriter, req *http.Request, repoPath string) {
	if req.Method != "GET" {
		w.WriteHeader(http.StatusMethodNotAllowed)
		w.Write([]byte("Method Not Allowed"))
		return
	}

	process := req.URL.Query().Get("service")
	cwd := filepath.Join(config.ReposPath, repoPath)

	if process != "git-receive-pack" && process != "git-upload-pack" {
		w.WriteHeader(http.StatusBadRequest)
		w.Write([]byte("Bad Request"))
		return
	}

	headers := w.Header()
	headers.Add("Content-Type", fmt.Sprintf("application/x-%s-advertisement", process))
	w.WriteHeader(http.StatusOK)

	w.Write(packetWrite(fmt.Sprintf("# service=%s\n", process)))
	w.Write(packetFlush())

	cmd := exec.Command(process, "--stateless-rpc", "--advertise-refs", ".")
	cmd.Dir = cwd
	runCommand(w, req.Body, cmd)
}

// Executes a shell command and pipes its output to HTTP response writer.
// DO NOT expose this function directly to end users as it creates a security breach
func runCommand(w io.Writer, r io.Reader, cmd *exec.Cmd) {
	if cmd.Dir != "" {
		cmd.Dir = sanitize(cmd.Dir)
	}

	log.Printf("[DEBUG] Running command from %s: %s %s ", cmd.Dir, cmd.Path, cmd.Args)

	stdin, err := cmd.StdinPipe()
	if err != nil {
		log.Printf("[ERROR] %v", err)
	}

	stdout, err := cmd.StdoutPipe()
	if err != nil {
		log.Printf("[ERROR] %v", err)
	}

	if err := cmd.Start(); err != nil {
		log.Printf("[ERROR] %v", err)
	}

	io.Copy(stdin, r)
	io.Copy(w, stdout)
	cmd.Wait()
}

// Returns bytes of a git packet containing the given string
func packetWrite(str string) []byte {
	s := strconv.FormatInt(int64((len(str) + 4)), 16)

	m := len(s) % 4
	if m != 0 {
		s = strings.Repeat("0", 4-m) + s
	}

	return []byte(s + str)
}

func packetFlush() []byte {
	return []byte("0000")
}

// Sanitizes name to avoid overwriting sensitive system files
// or executing forbidden binaries
func sanitize(name string) string {
	// Gets rid of volume drive label in Windows
	if len(name) > 1 && name[1] == ':' && runtime.GOOS == "windows" {
		name = name[2:]
	}

	name = filepath.Clean(name)
	name = filepath.ToSlash(name)
	for strings.HasPrefix(name, "../") {
		name = name[3:]
	}
	return name
}

func checkGitVersion(major, minor, patch uint) bool {
	//TODO
	return true
}
<|endoftext|>