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

package main

import (
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"strings"
	"syscall"
	"time"

	"github.com/google/syzkaller/dashboard/dashapi"
	"github.com/google/syzkaller/pkg/instance"
	"github.com/google/syzkaller/pkg/log"
	"github.com/google/syzkaller/pkg/osutil"
	"github.com/google/syzkaller/pkg/vcs"
	"github.com/google/syzkaller/prog"
)

const (
	syzkallerRebuildPeriod = 12 * time.Hour
	buildRetryPeriod       = 10 * time.Minute // used for both syzkaller and kernel
)

// SyzUpdater handles everything related to syzkaller updates.
// As kernel builder, it maintains 2 builds:
//  - latest: latest known good syzkaller build
//  - current: currently used syzkaller build
// Additionally it updates and restarts the current executable as necessary.
// Current executable is always built on the same revision as the rest of syzkaller binaries.
type SyzUpdater struct {
	repo          vcs.Repo
	exe           string
	repoAddress   string
	branch        string
	descriptions  string
	gopathDir     string
	syzkallerDir  string
	latestDir     string
	currentDir    string
	syzFiles      map[string]bool
	targets       map[string]bool
	dashboardAddr string
	compilerID    string
	cfg           *Config
}

func NewSyzUpdater(cfg *Config) *SyzUpdater {
	wd, err := os.Getwd()
	if err != nil {
		log.Fatalf("failed to get wd: %v", err)
	}
	bin := os.Args[0]
	if !filepath.IsAbs(bin) {
		bin = filepath.Join(wd, bin)
	}
	bin = filepath.Clean(bin)
	exe := filepath.Base(bin)
	if wd != filepath.Dir(bin) {
		log.Fatalf("%v executable must be in cwd (it will be overwritten on update)", exe)
	}

	gopath := filepath.Join(wd, "gopath")
	syzkallerDir := filepath.Join(gopath, "src", "github.com", "google", "syzkaller")
	osutil.MkdirAll(syzkallerDir)

	// List of required files in syzkaller build (contents of latest/current dirs).
	files := map[string]bool{
		"tag":             true, // contains syzkaller repo git hash
		"bin/syz-ci":      true, // these are just copied from syzkaller dir
		"bin/syz-manager": true,
	}
	targets := make(map[string]bool)
	for _, mgr := range cfg.Managers {
		mgrcfg := mgr.managercfg
		os, vmarch, arch := mgrcfg.TargetOS, mgrcfg.TargetVMArch, mgrcfg.TargetArch
		targets[os+"/"+vmarch+"/"+arch] = true
		files[fmt.Sprintf("bin/%v_%v/syz-fuzzer", os, vmarch)] = true
		files[fmt.Sprintf("bin/%v_%v/syz-execprog", os, vmarch)] = true
		files[fmt.Sprintf("bin/%v_%v/syz-executor", os, arch)] = true
	}
	syzFiles := make(map[string]bool)
	for f := range files {
		syzFiles[f] = true
	}
	compilerID, err := osutil.RunCmd(time.Minute, "", "go", "version")
	if err != nil {
		log.Fatalf("%v", err)
	}
	return &SyzUpdater{
		repo:          vcs.NewSyzkallerRepo(syzkallerDir),
		exe:           exe,
		repoAddress:   cfg.SyzkallerRepo,
		branch:        cfg.SyzkallerBranch,
		descriptions:  cfg.SyzkallerDescriptions,
		gopathDir:     gopath,
		syzkallerDir:  syzkallerDir,
		latestDir:     filepath.Join("syzkaller", "latest"),
		currentDir:    filepath.Join("syzkaller", "current"),
		syzFiles:      syzFiles,
		targets:       targets,
		dashboardAddr: cfg.DashboardAddr,
		compilerID:    strings.TrimSpace(string(compilerID)),
		cfg:           cfg,
	}
}

// UpdateOnStart does 3 things:
//  - ensures that the current executable is fresh
//  - ensures that we have a working syzkaller build in current
func (upd *SyzUpdater) UpdateOnStart(autoupdate bool, shutdown chan struct{}) {
	os.RemoveAll(upd.currentDir)
	latestTag := upd.checkLatest()
	if latestTag != "" {
		var exeMod time.Time
		if st, err := os.Stat(upd.exe); err == nil {
			exeMod = st.ModTime()
		}
		uptodate := prog.GitRevisionBase == latestTag && time.Since(exeMod) < time.Minute
		if uptodate || !autoupdate {
			if uptodate {
				// Have a fresh up-to-date build, probably just restarted.
				log.Logf(0, "current executable is up-to-date (%v)", latestTag)
			} else {
				log.Logf(0, "autoupdate is turned off, using latest build %v", latestTag)
			}
			if err := osutil.LinkFiles(upd.latestDir, upd.currentDir, upd.syzFiles); err != nil {
				log.Fatal(err)
			}
			return
		}
	}
	log.Logf(0, "current executable is on %v", prog.GitRevision)
	log.Logf(0, "latest syzkaller build is on %v", latestTag)

	// No syzkaller build or executable is stale.
	lastCommit := prog.GitRevisionBase
	for {
		lastCommit = upd.pollAndBuild(lastCommit)
		latestTag := upd.checkLatest()
		if latestTag != "" {
			// The build was successful or we had the latest build from previous runs.
			// Either way, use the latest build.
			log.Logf(0, "using syzkaller built on %v", latestTag)
			if err := osutil.LinkFiles(upd.latestDir, upd.currentDir, upd.syzFiles); err != nil {
				log.Fatal(err)
			}
			if autoupdate && prog.GitRevisionBase != latestTag {
				upd.UpdateAndRestart()
			}
			return
		}

		// No good build at all, try again later.
		log.Logf(0, "retrying in %v", buildRetryPeriod)
		select {
		case <-time.After(buildRetryPeriod):
		case <-shutdown:
			os.Exit(0)
		}
	}
}

// WaitForUpdate polls and rebuilds syzkaller.
// Returns when we have a new good build in latest.
func (upd *SyzUpdater) WaitForUpdate() {
	time.Sleep(syzkallerRebuildPeriod)
	latestTag := upd.checkLatest()
	lastCommit := latestTag
	for {
		lastCommit = upd.pollAndBuild(lastCommit)
		if latestTag != upd.checkLatest() {
			break
		}
		time.Sleep(buildRetryPeriod)
	}
	log.Logf(0, "syzkaller: update available, restarting")
}

// UpdateAndRestart updates and restarts the current executable.
// Does not return.
func (upd *SyzUpdater) UpdateAndRestart() {
	log.Logf(0, "restarting executable for update")
	latestBin := filepath.Join(upd.latestDir, "bin", upd.exe)
	if err := osutil.CopyFile(latestBin, upd.exe); err != nil {
		log.Fatal(err)
	}
	if err := syscall.Exec(upd.exe, os.Args, os.Environ()); err != nil {
		log.Fatal(err)
	}
	log.Fatalf("not reachable")
}

func (upd *SyzUpdater) pollAndBuild(lastCommit string) string {
	commit, err := upd.repo.Poll(upd.repoAddress, upd.branch)
	if err != nil {
		log.Logf(0, "syzkaller: failed to poll: %v", err)
		return lastCommit
	}
	log.Logf(0, "syzkaller: poll: %v (%v)", commit.Hash, commit.Title)
	if lastCommit != commit.Hash {
		log.Logf(0, "syzkaller: building ...")
		lastCommit = commit.Hash
		if err := upd.build(commit); err != nil {
			log.Logf(0, "syzkaller: %v", err)
			upd.uploadBuildError(commit, err)
		}
	}
	return lastCommit
}

func (upd *SyzUpdater) build(commit *vcs.Commit) error {
	// syzkaller testing may be slowed down by concurrent kernel builds too much
	// and cause timeout failures, so we serialize it with other builds:
	// https://groups.google.com/forum/#!msg/syzkaller-openbsd-bugs/o-G3vEsyQp4/f_nFpoNKBQAJ
	kernelBuildSem <- struct{}{}
	defer func() { <-kernelBuildSem }()

	if upd.descriptions != "" {
		files, err := ioutil.ReadDir(upd.descriptions)
		if err != nil {
			return fmt.Errorf("failed to read descriptions dir: %v", err)
		}
		for _, f := range files {
			src := filepath.Join(upd.descriptions, f.Name())
			dst := filepath.Join(upd.syzkallerDir, "sys", "linux", f.Name())
			if err := osutil.CopyFile(src, dst); err != nil {
				return err
			}
		}
		cmd := osutil.Command(instance.MakeBin, "generate")
		cmd.Dir = upd.syzkallerDir
		cmd.Env = append([]string{"GOPATH=" + upd.gopathDir}, os.Environ()...)
		if _, err := osutil.Run(time.Hour, cmd); err != nil {
			return osutil.PrependContext("generate failed", err)
		}
	}
	// This will also generate descriptions and should go before the 'go test' below.
	cmd := osutil.Command(instance.MakeBin, "host", "ci")
	cmd.Dir = upd.syzkallerDir
	cmd.Env = append([]string{"GOPATH=" + upd.gopathDir}, os.Environ()...)
	if _, err := osutil.Run(time.Hour, cmd); err != nil {
		return osutil.PrependContext("make host failed", err)
	}
	for target := range upd.targets {
		parts := strings.Split(target, "/")
		cmd = osutil.Command(instance.MakeBin, "target")
		cmd.Dir = upd.syzkallerDir
		cmd.Env = append([]string{}, os.Environ()...)
		cmd.Env = append(cmd.Env,
			"GOPATH="+upd.gopathDir,
			"TARGETOS="+parts[0],
			"TARGETVMARCH="+parts[1],
			"TARGETARCH="+parts[2],
		)
		if _, err := osutil.Run(time.Hour, cmd); err != nil {
			return osutil.PrependContext("make target failed", err)
		}
	}
	cmd = osutil.Command("go", "test", "-short", "./...")
	cmd.Dir = upd.syzkallerDir
	cmd.Env = append([]string{
		"GOPATH=" + upd.gopathDir,
		"SYZ_DISABLE_SANDBOXING=yes",
	}, os.Environ()...)
	if _, err := osutil.Run(time.Hour, cmd); err != nil {
		return osutil.PrependContext("testing failed", err)
	}
	tagFile := filepath.Join(upd.syzkallerDir, "tag")
	if err := osutil.WriteFile(tagFile, []byte(commit.Hash)); err != nil {
		return fmt.Errorf("failed to write tag file: %v", err)
	}
	if err := osutil.CopyFiles(upd.syzkallerDir, upd.latestDir, upd.syzFiles); err != nil {
		return fmt.Errorf("failed to copy syzkaller: %v", err)
	}
	return nil
}

func (upd *SyzUpdater) uploadBuildError(commit *vcs.Commit, buildErr error) {
	var title string
	var output []byte
	if verbose, ok := buildErr.(*osutil.VerboseError); ok {
		title = verbose.Title
		output = verbose.Output
	} else {
		title = buildErr.Error()
	}
	title = "syzkaller: " + title
	for _, mgrcfg := range upd.cfg.Managers {
		if upd.dashboardAddr == "" || mgrcfg.DashboardClient == "" {
			log.Logf(0, "not uploading build error fr %v: no dashboard", mgrcfg.Name)
			continue
		}
		dash := dashapi.New(mgrcfg.DashboardClient, upd.dashboardAddr, mgrcfg.DashboardKey)
		managercfg := mgrcfg.managercfg
		req := &dashapi.BuildErrorReq{
			Build: dashapi.Build{
				Manager:             managercfg.Name,
				ID:                  commit.Hash,
				OS:                  managercfg.TargetOS,
				Arch:                managercfg.TargetArch,
				VMArch:              managercfg.TargetVMArch,
				SyzkallerCommit:     commit.Hash,
				SyzkallerCommitDate: commit.Date,
				CompilerID:          upd.compilerID,
				KernelRepo:          upd.repoAddress,
				KernelBranch:        upd.branch,
			},
			Crash: dashapi.Crash{
				Title: title,
				Log:   output,
			},
		}
		if err := dash.ReportBuildError(req); err != nil {
			// TODO: log ReportBuildError error to dashboard.
			log.Logf(0, "failed to report build error for %v: %v", mgrcfg.Name, err)
		}
	}
}

// checkLatest returns tag of the latest build,
// or an empty string if latest build is missing/broken.
func (upd *SyzUpdater) checkLatest() string {
	if !osutil.FilesExist(upd.latestDir, upd.syzFiles) {
		return ""
	}
	tag, _ := ioutil.ReadFile(filepath.Join(upd.latestDir, "tag"))
	return string(tag)
}
<commit_msg>syz-ci: rebuild syzkaller if latest build does not exist<commit_after>// Copyright 2017 syzkaller project authors. All rights reserved.
// Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file.

package main

import (
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"strings"
	"syscall"
	"time"

	"github.com/google/syzkaller/dashboard/dashapi"
	"github.com/google/syzkaller/pkg/instance"
	"github.com/google/syzkaller/pkg/log"
	"github.com/google/syzkaller/pkg/osutil"
	"github.com/google/syzkaller/pkg/vcs"
	"github.com/google/syzkaller/prog"
)

const (
	syzkallerRebuildPeriod = 12 * time.Hour
	buildRetryPeriod       = 10 * time.Minute // used for both syzkaller and kernel
)

// SyzUpdater handles everything related to syzkaller updates.
// As kernel builder, it maintains 2 builds:
//  - latest: latest known good syzkaller build
//  - current: currently used syzkaller build
// Additionally it updates and restarts the current executable as necessary.
// Current executable is always built on the same revision as the rest of syzkaller binaries.
type SyzUpdater struct {
	repo          vcs.Repo
	exe           string
	repoAddress   string
	branch        string
	descriptions  string
	gopathDir     string
	syzkallerDir  string
	latestDir     string
	currentDir    string
	syzFiles      map[string]bool
	targets       map[string]bool
	dashboardAddr string
	compilerID    string
	cfg           *Config
}

func NewSyzUpdater(cfg *Config) *SyzUpdater {
	wd, err := os.Getwd()
	if err != nil {
		log.Fatalf("failed to get wd: %v", err)
	}
	bin := os.Args[0]
	if !filepath.IsAbs(bin) {
		bin = filepath.Join(wd, bin)
	}
	bin = filepath.Clean(bin)
	exe := filepath.Base(bin)
	if wd != filepath.Dir(bin) {
		log.Fatalf("%v executable must be in cwd (it will be overwritten on update)", exe)
	}

	gopath := filepath.Join(wd, "gopath")
	syzkallerDir := filepath.Join(gopath, "src", "github.com", "google", "syzkaller")
	osutil.MkdirAll(syzkallerDir)

	// List of required files in syzkaller build (contents of latest/current dirs).
	files := map[string]bool{
		"tag":             true, // contains syzkaller repo git hash
		"bin/syz-ci":      true, // these are just copied from syzkaller dir
		"bin/syz-manager": true,
	}
	targets := make(map[string]bool)
	for _, mgr := range cfg.Managers {
		mgrcfg := mgr.managercfg
		os, vmarch, arch := mgrcfg.TargetOS, mgrcfg.TargetVMArch, mgrcfg.TargetArch
		targets[os+"/"+vmarch+"/"+arch] = true
		files[fmt.Sprintf("bin/%v_%v/syz-fuzzer", os, vmarch)] = true
		files[fmt.Sprintf("bin/%v_%v/syz-execprog", os, vmarch)] = true
		files[fmt.Sprintf("bin/%v_%v/syz-executor", os, arch)] = true
	}
	syzFiles := make(map[string]bool)
	for f := range files {
		syzFiles[f] = true
	}
	compilerID, err := osutil.RunCmd(time.Minute, "", "go", "version")
	if err != nil {
		log.Fatalf("%v", err)
	}
	return &SyzUpdater{
		repo:          vcs.NewSyzkallerRepo(syzkallerDir),
		exe:           exe,
		repoAddress:   cfg.SyzkallerRepo,
		branch:        cfg.SyzkallerBranch,
		descriptions:  cfg.SyzkallerDescriptions,
		gopathDir:     gopath,
		syzkallerDir:  syzkallerDir,
		latestDir:     filepath.Join("syzkaller", "latest"),
		currentDir:    filepath.Join("syzkaller", "current"),
		syzFiles:      syzFiles,
		targets:       targets,
		dashboardAddr: cfg.DashboardAddr,
		compilerID:    strings.TrimSpace(string(compilerID)),
		cfg:           cfg,
	}
}

// UpdateOnStart does 3 things:
//  - ensures that the current executable is fresh
//  - ensures that we have a working syzkaller build in current
func (upd *SyzUpdater) UpdateOnStart(autoupdate bool, shutdown chan struct{}) {
	os.RemoveAll(upd.currentDir)
	latestTag := upd.checkLatest()
	if latestTag != "" {
		var exeMod time.Time
		if st, err := os.Stat(upd.exe); err == nil {
			exeMod = st.ModTime()
		}
		uptodate := prog.GitRevisionBase == latestTag && time.Since(exeMod) < time.Minute
		if uptodate || !autoupdate {
			if uptodate {
				// Have a fresh up-to-date build, probably just restarted.
				log.Logf(0, "current executable is up-to-date (%v)", latestTag)
			} else {
				log.Logf(0, "autoupdate is turned off, using latest build %v", latestTag)
			}
			if err := osutil.LinkFiles(upd.latestDir, upd.currentDir, upd.syzFiles); err != nil {
				log.Fatal(err)
			}
			return
		}
	}
	log.Logf(0, "current executable is on %v", prog.GitRevision)
	log.Logf(0, "latest syzkaller build is on %v", latestTag)

	// No syzkaller build or executable is stale.
	lastCommit := prog.GitRevisionBase
	if lastCommit != latestTag {
		// Latest build and syz-ci are inconsistent. Rebuild everything.
		lastCommit = ""
		latestTag = ""
	}
	for {
		lastCommit = upd.pollAndBuild(lastCommit)
		latestTag := upd.checkLatest()
		if latestTag != "" {
			// The build was successful or we had the latest build from previous runs.
			// Either way, use the latest build.
			log.Logf(0, "using syzkaller built on %v", latestTag)
			if err := osutil.LinkFiles(upd.latestDir, upd.currentDir, upd.syzFiles); err != nil {
				log.Fatal(err)
			}
			if autoupdate && prog.GitRevisionBase != latestTag {
				upd.UpdateAndRestart()
			}
			return
		}

		// No good build at all, try again later.
		log.Logf(0, "retrying in %v", buildRetryPeriod)
		select {
		case <-time.After(buildRetryPeriod):
		case <-shutdown:
			os.Exit(0)
		}
	}
}

// WaitForUpdate polls and rebuilds syzkaller.
// Returns when we have a new good build in latest.
func (upd *SyzUpdater) WaitForUpdate() {
	time.Sleep(syzkallerRebuildPeriod)
	latestTag := upd.checkLatest()
	lastCommit := latestTag
	for {
		lastCommit = upd.pollAndBuild(lastCommit)
		if latestTag != upd.checkLatest() {
			break
		}
		time.Sleep(buildRetryPeriod)
	}
	log.Logf(0, "syzkaller: update available, restarting")
}

// UpdateAndRestart updates and restarts the current executable.
// Does not return.
func (upd *SyzUpdater) UpdateAndRestart() {
	log.Logf(0, "restarting executable for update")
	latestBin := filepath.Join(upd.latestDir, "bin", upd.exe)
	if err := osutil.CopyFile(latestBin, upd.exe); err != nil {
		log.Fatal(err)
	}
	if err := syscall.Exec(upd.exe, os.Args, os.Environ()); err != nil {
		log.Fatal(err)
	}
	log.Fatalf("not reachable")
}

func (upd *SyzUpdater) pollAndBuild(lastCommit string) string {
	commit, err := upd.repo.Poll(upd.repoAddress, upd.branch)
	if err != nil {
		log.Logf(0, "syzkaller: failed to poll: %v", err)
		return lastCommit
	}
	log.Logf(0, "syzkaller: poll: %v (%v)", commit.Hash, commit.Title)
	if lastCommit == commit.Hash {
		return lastCommit
	}
	log.Logf(0, "syzkaller: building ...")
	if err := upd.build(commit); err != nil {
		log.Logf(0, "syzkaller: %v", err)
		upd.uploadBuildError(commit, err)
	}
	return commit.Hash
}

func (upd *SyzUpdater) build(commit *vcs.Commit) error {
	// syzkaller testing may be slowed down by concurrent kernel builds too much
	// and cause timeout failures, so we serialize it with other builds:
	// https://groups.google.com/forum/#!msg/syzkaller-openbsd-bugs/o-G3vEsyQp4/f_nFpoNKBQAJ
	kernelBuildSem <- struct{}{}
	defer func() { <-kernelBuildSem }()

	if upd.descriptions != "" {
		files, err := ioutil.ReadDir(upd.descriptions)
		if err != nil {
			return fmt.Errorf("failed to read descriptions dir: %v", err)
		}
		for _, f := range files {
			src := filepath.Join(upd.descriptions, f.Name())
			dst := filepath.Join(upd.syzkallerDir, "sys", "linux", f.Name())
			if err := osutil.CopyFile(src, dst); err != nil {
				return err
			}
		}
		cmd := osutil.Command(instance.MakeBin, "generate")
		cmd.Dir = upd.syzkallerDir
		cmd.Env = append([]string{"GOPATH=" + upd.gopathDir}, os.Environ()...)
		if _, err := osutil.Run(time.Hour, cmd); err != nil {
			return osutil.PrependContext("generate failed", err)
		}
	}
	// This will also generate descriptions and should go before the 'go test' below.
	cmd := osutil.Command(instance.MakeBin, "host", "ci")
	cmd.Dir = upd.syzkallerDir
	cmd.Env = append([]string{"GOPATH=" + upd.gopathDir}, os.Environ()...)
	if _, err := osutil.Run(time.Hour, cmd); err != nil {
		return osutil.PrependContext("make host failed", err)
	}
	for target := range upd.targets {
		parts := strings.Split(target, "/")
		cmd = osutil.Command(instance.MakeBin, "target")
		cmd.Dir = upd.syzkallerDir
		cmd.Env = append([]string{}, os.Environ()...)
		cmd.Env = append(cmd.Env,
			"GOPATH="+upd.gopathDir,
			"TARGETOS="+parts[0],
			"TARGETVMARCH="+parts[1],
			"TARGETARCH="+parts[2],
		)
		if _, err := osutil.Run(time.Hour, cmd); err != nil {
			return osutil.PrependContext("make target failed", err)
		}
	}
	cmd = osutil.Command("go", "test", "-short", "./...")
	cmd.Dir = upd.syzkallerDir
	cmd.Env = append([]string{
		"GOPATH=" + upd.gopathDir,
		"SYZ_DISABLE_SANDBOXING=yes",
	}, os.Environ()...)
	if _, err := osutil.Run(time.Hour, cmd); err != nil {
		return osutil.PrependContext("testing failed", err)
	}
	tagFile := filepath.Join(upd.syzkallerDir, "tag")
	if err := osutil.WriteFile(tagFile, []byte(commit.Hash)); err != nil {
		return fmt.Errorf("failed to write tag file: %v", err)
	}
	if err := osutil.CopyFiles(upd.syzkallerDir, upd.latestDir, upd.syzFiles); err != nil {
		return fmt.Errorf("failed to copy syzkaller: %v", err)
	}
	return nil
}

func (upd *SyzUpdater) uploadBuildError(commit *vcs.Commit, buildErr error) {
	var title string
	var output []byte
	if verbose, ok := buildErr.(*osutil.VerboseError); ok {
		title = verbose.Title
		output = verbose.Output
	} else {
		title = buildErr.Error()
	}
	title = "syzkaller: " + title
	for _, mgrcfg := range upd.cfg.Managers {
		if upd.dashboardAddr == "" || mgrcfg.DashboardClient == "" {
			log.Logf(0, "not uploading build error fr %v: no dashboard", mgrcfg.Name)
			continue
		}
		dash := dashapi.New(mgrcfg.DashboardClient, upd.dashboardAddr, mgrcfg.DashboardKey)
		managercfg := mgrcfg.managercfg
		req := &dashapi.BuildErrorReq{
			Build: dashapi.Build{
				Manager:             managercfg.Name,
				ID:                  commit.Hash,
				OS:                  managercfg.TargetOS,
				Arch:                managercfg.TargetArch,
				VMArch:              managercfg.TargetVMArch,
				SyzkallerCommit:     commit.Hash,
				SyzkallerCommitDate: commit.Date,
				CompilerID:          upd.compilerID,
				KernelRepo:          upd.repoAddress,
				KernelBranch:        upd.branch,
			},
			Crash: dashapi.Crash{
				Title: title,
				Log:   output,
			},
		}
		if err := dash.ReportBuildError(req); err != nil {
			// TODO: log ReportBuildError error to dashboard.
			log.Logf(0, "failed to report build error for %v: %v", mgrcfg.Name, err)
		}
	}
}

// checkLatest returns tag of the latest build,
// or an empty string if latest build is missing/broken.
func (upd *SyzUpdater) checkLatest() string {
	if !osutil.FilesExist(upd.latestDir, upd.syzFiles) {
		return ""
	}
	tag, _ := ioutil.ReadFile(filepath.Join(upd.latestDir, "tag"))
	return string(tag)
}
<|endoftext|>


<commit_before>package paza

import "testing"

type treeTestCase struct {
	Text string
	Name string
	Node *Node
}

func testTree(t *testing.T, set *Set, cases []treeTestCase) {
	for _, c := range cases {
		input := NewInput([]byte(c.Text))
		ok, l, node := set.Call(c.Name, input, 0)
		if !ok || l != len(c.Text) {
			t.Fatalf("match fail: %v %d %v", ok, l, c)
		}
		if !node.Equal(c.Node) {
			pt("== expected ==\n")
			c.Node.Dump(input)
			pt("== return ==\n")
			node.Dump(input)
			t.Fatalf("tree not match")
		}
	}
}

func TestParseTree(t *testing.T) {
	/*
		expr = expr (+ | -) term | term
		term = term (* / /) factor | factor
		factor = [0-9]+ | '(' expr ')'
	*/
	set := NewSet()
	set.AddRec("expr", set.OrdChoice(
		set.NamedConcat("plus-expr", "expr", set.NamedRune("plus-op", '+'), "term"),
		set.NamedConcat("minus-expr", "expr", set.NamedRune("minus-op", '-'), "term"),
		"term",
	))
	set.AddRec("term", set.OrdChoice(
		set.NamedConcat("mul-expr", "term", set.NamedRune("mul-op", '*'), "factor"),
		set.NamedConcat("div-expr", "term", set.NamedRune("div-op", '/'), "factor"),
		"factor",
	))
	set.Add("factor", set.OrdChoice(
		set.NamedRegex("digit", `[0-9]+`),
		set.NamedConcat("quoted", set.NamedRune("left-quote", '('), "expr", set.NamedRune("right-quote", ')')),
	))

	cases := []treeTestCase{
		{"1", "expr", &Node{"expr", 0, 1, []*Node{
			{"term", 0, 1, []*Node{
				{"factor", 0, 1, []*Node{
					{"digit", 0, 1, nil},
				}}}}}}},
		{"1+2", "expr", &Node{"expr", 0, 3, []*Node{
			{"plus-expr", 0, 3, []*Node{
				{"expr", 0, 1, []*Node{
					{"term", 0, 1, []*Node{
						{"factor", 0, 1, []*Node{
							{"digit", 0, 1, nil}}}}}}},
				{"plus-op", 1, 1, nil},
				{"term", 2, 1, []*Node{
					{"factor", 2, 1, []*Node{
						{"digit", 2, 1, nil}}}}}}}}}},
		{"1-2", "expr", &Node{"expr", 0, 3, []*Node{
			{"minus-expr", 0, 3, []*Node{
				{"expr", 0, 1, []*Node{
					{"term", 0, 1, []*Node{
						{"factor", 0, 1, []*Node{
							{"digit", 0, 1, nil}}}}}}},
				{"minus-op", 1, 1, nil},
				{"term", 2, 1, []*Node{
					{"factor", 2, 1, []*Node{
						{"digit", 2, 1, nil}}}}}}}}}},
		{"1*2", "expr", &Node{"expr", 0, 3, []*Node{
			{"term", 0, 3, []*Node{
				{"mul-expr", 0, 3, []*Node{
					{"term", 0, 1, []*Node{
						{"factor", 0, 1, []*Node{
							{"digit", 0, 1, nil}}}}},
					{"mul-op", 1, 1, nil},
					{"factor", 2, 1, []*Node{
						{"digit", 2, 1, nil}}}}}}}}}},
		{"1/2", "expr", &Node{"expr", 0, 3, []*Node{
			{"term", 0, 3, []*Node{
				{"div-expr", 0, 3, []*Node{
					{"term", 0, 1, []*Node{
						{"factor", 0, 1, []*Node{
							{"digit", 0, 1, nil}}}}},
					{"div-op", 1, 1, nil},
					{"factor", 2, 1, []*Node{
						{"digit", 2, 1, nil}}}}}}}}}},
		{"(1)", "expr", &Node{"expr", 0, 3, []*Node{
			{"term", 0, 3, []*Node{
				{"factor", 0, 3, []*Node{
					{"quoted", 0, 3, []*Node{
						{"left-quote", 0, 1, nil},
						{"expr", 1, 1, []*Node{
							{"term", 1, 1, []*Node{
								{"factor", 1, 1, []*Node{
									{"digit", 1, 1, nil}}}}}}},
						{"right-quote", 2, 1, nil}}}}}}}}}},
	}
	testTree(t, set, cases)
}
<commit_msg>more test<commit_after>package paza

import "testing"

type treeTestCase struct {
	Text string
	Name string
	Node *Node
}

func testTree(t *testing.T, set *Set, cases []treeTestCase) {
	for _, c := range cases {
		input := NewInput([]byte(c.Text))
		ok, l, node := set.Call(c.Name, input, 0)
		if !ok || l != len(c.Text) {
			t.Fatalf("match fail: %v %d %v", ok, l, c)
		}
		if !node.Equal(c.Node) {
			pt("== expected ==\n")
			c.Node.Dump(input)
			pt("== return ==\n")
			node.Dump(input)
			t.Fatalf("tree not match")
		}
	}
}

func TestParseTree(t *testing.T) {
	/*
		expr = expr (+ | -) term | term
		term = term (* / /) factor | factor
		factor = [0-9]+ | '(' expr ')'
	*/
	set := NewSet()
	set.AddRec("expr", set.OrdChoice(
		set.NamedConcat("plus-expr", "expr", set.NamedRune("plus-op", '+'), "term"),
		set.NamedConcat("minus-expr", "expr", set.NamedRune("minus-op", '-'), "term"),
		"term",
	))
	set.AddRec("term", set.OrdChoice(
		set.NamedConcat("mul-expr", "term", set.NamedRune("mul-op", '*'), "factor"),
		set.NamedConcat("div-expr", "term", set.NamedRune("div-op", '/'), "factor"),
		"factor",
	))
	set.Add("factor", set.OrdChoice(
		set.NamedRegex("digit", `[0-9]+`),
		set.NamedConcat("quoted", set.NamedRune("left-quote", '('), "expr", set.NamedRune("right-quote", ')')),
	))

	cases := []treeTestCase{
		{"1", "expr", &Node{"expr", 0, 1, []*Node{
			{"term", 0, 1, []*Node{
				{"factor", 0, 1, []*Node{
					{"digit", 0, 1, nil},
				}}}}}}},
		{"1+2", "expr", &Node{"expr", 0, 3, []*Node{
			{"plus-expr", 0, 3, []*Node{
				{"expr", 0, 1, []*Node{
					{"term", 0, 1, []*Node{
						{"factor", 0, 1, []*Node{
							{"digit", 0, 1, nil}}}}}}},
				{"plus-op", 1, 1, nil},
				{"term", 2, 1, []*Node{
					{"factor", 2, 1, []*Node{
						{"digit", 2, 1, nil}}}}}}}}}},
		{"1-2", "expr", &Node{"expr", 0, 3, []*Node{
			{"minus-expr", 0, 3, []*Node{
				{"expr", 0, 1, []*Node{
					{"term", 0, 1, []*Node{
						{"factor", 0, 1, []*Node{
							{"digit", 0, 1, nil}}}}}}},
				{"minus-op", 1, 1, nil},
				{"term", 2, 1, []*Node{
					{"factor", 2, 1, []*Node{
						{"digit", 2, 1, nil}}}}}}}}}},
		{"1*2", "expr", &Node{"expr", 0, 3, []*Node{
			{"term", 0, 3, []*Node{
				{"mul-expr", 0, 3, []*Node{
					{"term", 0, 1, []*Node{
						{"factor", 0, 1, []*Node{
							{"digit", 0, 1, nil}}}}},
					{"mul-op", 1, 1, nil},
					{"factor", 2, 1, []*Node{
						{"digit", 2, 1, nil}}}}}}}}}},
		{"1/2", "expr", &Node{"expr", 0, 3, []*Node{
			{"term", 0, 3, []*Node{
				{"div-expr", 0, 3, []*Node{
					{"term", 0, 1, []*Node{
						{"factor", 0, 1, []*Node{
							{"digit", 0, 1, nil}}}}},
					{"div-op", 1, 1, nil},
					{"factor", 2, 1, []*Node{
						{"digit", 2, 1, nil}}}}}}}}}},
		{"(1)", "expr", &Node{"expr", 0, 3, []*Node{
			{"term", 0, 3, []*Node{
				{"factor", 0, 3, []*Node{
					{"quoted", 0, 3, []*Node{
						{"left-quote", 0, 1, nil},
						{"expr", 1, 1, []*Node{
							{"term", 1, 1, []*Node{
								{"factor", 1, 1, []*Node{
									{"digit", 1, 1, nil}}}}}}},
						{"right-quote", 2, 1, nil}}}}}}}}}},
	}
	testTree(t, set, cases)
}

func TestParseTree2(t *testing.T) {
	set := NewSet()
	set.Add("foo", set.OrdChoice(
		set.NamedByteIn("digit", []byte("1234567890")),
		set.NamedByteRange("alpha", 'a', 'z'),
		set.NamedOrdChoice("punct",
			set.NamedRune("!", '!'),
			set.NamedRune("@", '@')),
		set.NamedOneOrMore("dashes", set.NamedRune("dash", '-')),
	))
	cases := []treeTestCase{
		{"1", "foo", &Node{"foo", 0, 1, []*Node{
			{"digit", 0, 1, nil}}}},
		{"z", "foo", &Node{"foo", 0, 1, []*Node{
			{"alpha", 0, 1, nil}}}},
		{"!", "foo", &Node{"foo", 0, 1, []*Node{
			{"punct", 0, 1, []*Node{
				{"!", 0, 1, nil}}}}}},
		{"-", "foo", &Node{"foo", 0, 1, []*Node{
			{"dashes", 0, 1, []*Node{
				{"dash", 0, 1, nil},
			}}}}},
		{"--", "foo", &Node{"foo", 0, 2, []*Node{
			{"dashes", 0, 2, []*Node{
				{"dash", 0, 1, nil},
				{"dash", 1, 1, nil},
			}}}}},
		{"---", "foo", &Node{"foo", 0, 3, []*Node{
			{"dashes", 0, 3, []*Node{
				{"dash", 0, 1, nil},
				{"dash", 1, 1, nil},
				{"dash", 2, 1, nil},
			}}}}},
	}
	testTree(t, set, cases)
}
<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"encoding/csv"
	"flag"
	"io"
	"log"
	"os"
	"strconv"
)

// takes file from stdin and outputs to stdout in the correct time format, one date per line
func main() {
	flag.Parse()
	var columns []int
	for _, arg := range flag.Args() {
		i, err := strconv.Atoi(arg)
		if err != nil {
			log.Fatalf("arg is not an integer - %s", arg)
		}
		if i < 0 {
			log.Fatalf("arg needs to be >= 0 - %s", arg)
		}
		columns = append(columns, i)
	}
	if len(columns) == 0 {
		log.Fatalf("Need to specify which columns to output")
	}
	csvReader := csv.NewReader(bufio.NewReader(os.Stdin))
	csvWriter := csv.NewWriter(bufio.NewWriter(os.Stdout))
	defer csvWriter.Flush()
	csvReader.Comma = '\t'
	for {
		data, err := csvReader.Read()
		if err == io.EOF {
			return
		}
		if err != nil {
			log.Fatalf("Error reading standard in - %v", err)
		}
		newData := make([]string, len(columns))
		for x, y := range columns {
			newData[x] = data[y]
		}
		err = csvWriter.Write(newData)
		if err != nil {
			log.Fatalf("Error writing standard out - %v", err)
		}
	}
}
<commit_msg>Allowing commas instead of tabs with a flag<commit_after>package main

import (
	"bufio"
	"encoding/csv"
	"flag"
	"io"
	"log"
	"os"
	"strconv"
)

var useComma = flag.Bool("useComma", false, "Specify to use comman instead of tab for delimiter")

// takes file from stdin and outputs to stdout in the correct time format, one date per line
func main() {
	flag.Parse()
	var columns []int
	for _, arg := range flag.Args() {
		i, err := strconv.Atoi(arg)
		if err != nil {
			log.Fatalf("arg is not an integer - %s", arg)
		}
		if i < 0 {
			log.Fatalf("arg needs to be >= 0 - %s", arg)
		}
		columns = append(columns, i)
	}
	if len(columns) == 0 {
		log.Fatalf("Need to specify which columns to output")
	}
	csvReader := csv.NewReader(bufio.NewReader(os.Stdin))
	csvWriter := csv.NewWriter(bufio.NewWriter(os.Stdout))
	defer csvWriter.Flush()
	if !*useComma {
		csvReader.Comma = '\t'
		csvWriter.Comma = '\t'
	}
	for {
		data, err := csvReader.Read()
		if err == io.EOF {
			return
		}
		if err != nil {
			log.Fatalf("Error reading standard in - %v", err)
		}
		newData := make([]string, len(columns))
		for x, y := range columns {
			newData[x] = data[y]
		}
		err = csvWriter.Write(newData)
		if err != nil {
			log.Fatalf("Error writing standard out - %v", err)
		}
	}
}
<|endoftext|>


<commit_before>package tty

import (
	"bufio"
	"os"
	"syscall"
	"errors"
)
s
type TTY struct {
	in  *os.File
	bin *bufio.Reader
	out *os.File
}

func open() (*TTY, error) {
	tty := new(TTY)

	in, err := os.Open("/dev/cons")
	if err != nil {
		return nil, err
	}
	tty.in = in
	tty.bin = bufio.NewReader(in)

	out, err := os.OpenFile("/dev/cons", syscall.O_WRONLY, 0)
	if err != nil {
		return nil, err
	}
	tty.out = out

	return tty, nil
}

func (tty *TTY) buffered() bool {
	return tty.bin.Buffered() > 0
}

func (tty *TTY) readRune() (rune, error) {
	r, _, err := tty.bin.ReadRune()
	return r, err
}

func (tty *TTY) close() (err error) {
	if err2 := tty.in.Close(); err2 != nil {
		err = err2
	}
	if err2 := tty.out.Close(); err2 != nil {
		err = err2
	}
	return
}

func (tty *TTY) size() (int, int, error) {
	return 80, 24, nil
}

func (tty *TTY) sizePixel() (int, int, int, int, error) {
	x, y, _ := tty.size()
	return x, y, -1, -1, errors.New("no implemented method for querying size in pixels on Windows")
}

func (tty *TTY) input() *os.File {
	return tty.in
}

func (tty *TTY) output() *os.File {
	return tty.out
}
<commit_msg>corrected plan 9 error message<commit_after>package tty

import (
	"bufio"
	"os"
	"syscall"
	"errors"
)
s
type TTY struct {
	in  *os.File
	bin *bufio.Reader
	out *os.File
}

func open() (*TTY, error) {
	tty := new(TTY)

	in, err := os.Open("/dev/cons")
	if err != nil {
		return nil, err
	}
	tty.in = in
	tty.bin = bufio.NewReader(in)

	out, err := os.OpenFile("/dev/cons", syscall.O_WRONLY, 0)
	if err != nil {
		return nil, err
	}
	tty.out = out

	return tty, nil
}

func (tty *TTY) buffered() bool {
	return tty.bin.Buffered() > 0
}

func (tty *TTY) readRune() (rune, error) {
	r, _, err := tty.bin.ReadRune()
	return r, err
}

func (tty *TTY) close() (err error) {
	if err2 := tty.in.Close(); err2 != nil {
		err = err2
	}
	if err2 := tty.out.Close(); err2 != nil {
		err = err2
	}
	return
}

func (tty *TTY) size() (int, int, error) {
	return 80, 24, nil
}

func (tty *TTY) sizePixel() (int, int, int, int, error) {
	x, y, _ := tty.size()
	return x, y, -1, -1, errors.New("no implemented method for querying size in pixels on Plan 9")
}

func (tty *TTY) input() *os.File {
	return tty.in
}

func (tty *TTY) output() *os.File {
	return tty.out
}
<|endoftext|>


<commit_before>package qb

import (
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/suite"
	"testing"
)

type TypeTestSuite struct {
	suite.Suite
}

func (suite *TypeTestSuite) TestTypes() {
	dialect := NewDialect("")

	precisionType := Type("FLOAT").Precision(2, 5)

	assert.Equal(suite.T(), "FLOAT(2, 5)", dialect.CompileType(precisionType))

	assert.Equal(suite.T(), "CHAR", dialect.CompileType(Char()))
	assert.Equal(suite.T(), "VARCHAR(255)", dialect.CompileType(Varchar()))
	assert.Equal(suite.T(), "TEXT", dialect.CompileType(Text()))
	assert.Equal(suite.T(), "INT", dialect.CompileType(Int()))
	assert.Equal(suite.T(), "SMALLINT", dialect.CompileType(SmallInt()))
	assert.Equal(suite.T(), "BIGINT", dialect.CompileType(BigInt()))
	assert.Equal(suite.T(), "NUMERIC(2, 5)", dialect.CompileType(Numeric().Precision(2, 5)))
	assert.Equal(suite.T(), "DECIMAL", dialect.CompileType(Decimal()))
	assert.Equal(suite.T(), "FLOAT", dialect.CompileType(Float()))
	assert.Equal(suite.T(), "BOOLEAN", dialect.CompileType(Boolean()))
	assert.Equal(suite.T(), "TIMESTAMP", dialect.CompileType(Timestamp()))
	assert.Equal(suite.T(), "BLOB", dialect.CompileType(Blob()))
}

func TestTypeTestSuite(t *testing.T) {
	suite.Run(t, new(TypeTestSuite))
}

func (suite *TypeTestSuite) TestUnsigned() {
	dialect := NewDialect("mysql")
	assert.Equal(suite.T(), "BIGINT", dialect.CompileType(BigInt().Signed()))
	assert.Equal(suite.T(), "BIGINT UNSIGNED", dialect.CompileType(BigInt().Unsigned()))
	assert.Equal(suite.T(), "NUMERIC(2, 5) UNSIGNED", dialect.CompileType(Numeric().Precision(2, 5).Unsigned()))

	dialect = NewDialect("")
	assert.Equal(suite.T(), "INT", dialect.CompileType(Int().Signed()))
	assert.Equal(suite.T(), "SMALLINT", dialect.CompileType(TinyInt().Unsigned()))
	assert.Equal(suite.T(), "INT", dialect.CompileType(SmallInt().Unsigned()))
	assert.Equal(suite.T(), "BIGINT", dialect.CompileType(Int().Unsigned()))
	assert.Equal(suite.T(), "BIGINT", dialect.CompileType(BigInt().Unsigned()))
}
<commit_msg>Make 'type' tests dialect-agnostic<commit_after>package qb

import (
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/suite"
	"testing"
)

type TypeTestSuite struct {
	suite.Suite
}

func (suite *TypeTestSuite) TestTypes() {
	dialect := NewDialect("")

	precisionType := Type("FLOAT").Precision(2, 5)

	assert.Equal(suite.T(), "FLOAT(2, 5)", dialect.CompileType(precisionType))

	assert.Equal(suite.T(), "CHAR", dialect.CompileType(Char()))
	assert.Equal(suite.T(), "VARCHAR(255)", dialect.CompileType(Varchar()))
	assert.Equal(suite.T(), "TEXT", dialect.CompileType(Text()))
	assert.Equal(suite.T(), "INT", dialect.CompileType(Int()))
	assert.Equal(suite.T(), "SMALLINT", dialect.CompileType(SmallInt()))
	assert.Equal(suite.T(), "BIGINT", dialect.CompileType(BigInt()))
	assert.Equal(suite.T(), "NUMERIC(2, 5)", dialect.CompileType(Numeric().Precision(2, 5)))
	assert.Equal(suite.T(), "DECIMAL", dialect.CompileType(Decimal()))
	assert.Equal(suite.T(), "FLOAT", dialect.CompileType(Float()))
	assert.Equal(suite.T(), "BOOLEAN", dialect.CompileType(Boolean()))
	assert.Equal(suite.T(), "TIMESTAMP", dialect.CompileType(Timestamp()))
	assert.Equal(suite.T(), "BLOB", dialect.CompileType(Blob()))
}

func (suite *TypeTestSuite) TestUnsigned() {
	assert.Equal(suite.T(), "BIGINT", DefaultCompileType(BigInt().Signed(), true))
	assert.Equal(suite.T(), "BIGINT UNSIGNED", DefaultCompileType(BigInt().Unsigned(), true))
	assert.Equal(suite.T(), "NUMERIC(2, 5) UNSIGNED", DefaultCompileType(Numeric().Precision(2, 5).Unsigned(), true))

	assert.Equal(suite.T(), "INT", DefaultCompileType(Int().Signed(), false))
	assert.Equal(suite.T(), "SMALLINT", DefaultCompileType(TinyInt().Unsigned(), false))
	assert.Equal(suite.T(), "INT", DefaultCompileType(SmallInt().Unsigned(), false))
	assert.Equal(suite.T(), "BIGINT", DefaultCompileType(Int().Unsigned(), false))
	assert.Equal(suite.T(), "BIGINT", DefaultCompileType(BigInt().Unsigned(), false))
}

func TestTypeTestSuite(t *testing.T) {
	suite.Run(t, new(TypeTestSuite))
}
<|endoftext|>


<commit_before>/*
Copyright 2018 The Knative 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 v1alpha1

import (
	"github.com/knative/pkg/apis"
	duckv1beta1 "github.com/knative/pkg/apis/duck/v1beta1"
	"github.com/knative/pkg/kmeta"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/util/intstr"
)

// +genclient
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
// +genclient:nonNamespaced

// ClusterIngress is a collection of rules that allow inbound connections to reach the
// endpoints defined by a backend. A ClusterIngress can be configured to give services
// externally-reachable URLs, load balance traffic, offer name based virtual hosting, etc.
//
// This is heavily based on K8s Ingress https://godoc.org/k8s.io/api/extensions/v1beta1#Ingress
// which some highlighted modifications.
type ClusterIngress struct {
	metav1.TypeMeta `json:",inline"`
	// Standard object's metadata.
	// More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata
	// +optional
	metav1.ObjectMeta `json:"metadata,omitempty"`

	// Spec is the desired state of the ClusterIngress.
	// More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#spec-and-status
	// +optional
	Spec IngressSpec `json:"spec,omitempty"`

	// Status is the current state of the ClusterIngress.
	// More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#spec-and-status
	// +optional
	Status IngressStatus `json:"status,omitempty"`
}

// Verify that ClusterIngress adheres to the appropriate interfaces.
var (
	// Check that ClusterIngress may be validated and defaulted.
	_ apis.Validatable = (*ClusterIngress)(nil)
	_ apis.Defaultable = (*ClusterIngress)(nil)

	// Check that we can create OwnerReferences to a ClusterIngress.
	_ kmeta.OwnerRefable = (*ClusterIngress)(nil)
)

// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object

// ClusterIngressList is a collection of ClusterIngress objects.
type ClusterIngressList struct {
	metav1.TypeMeta `json:",inline"`
	// Standard object metadata.
	// More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata
	// +optional
	metav1.ListMeta `json:"metadata,omitempty"`

	// Items is the list of ClusterIngress objects.
	Items []ClusterIngress `json:"items"`
}

// IngressSpec describes the ClusterIngress the user wishes to exist.
//
// In general this follows the same shape as K8s Ingress.
// Some notable differences:
// - Backends now can have namespace:
// - Traffic can be split across multiple backends.
// - Timeout & Retry can be configured.
// - Headers can be appended.
type IngressSpec struct {
	// DeprecatedGeneration was used prior in Kubernetes versions <1.11
	// when metadata.generation was not being incremented by the api server
	//
	// This property will be dropped in future Knative releases and should
	// not be used - use metadata.generation
	//
	// Tracking issue: https://github.com/knative/serving/issues/643
	//
	// +optional
	DeprecatedGeneration int64 `json:"generation,omitempty"`

	// TLS configuration. Currently ClusterIngress only supports a single TLS
	// port: 443. If multiple members of this list specify different hosts, they
	// will be multiplexed on the same port according to the hostname specified
	// through the SNI TLS extension, if the ingress controller fulfilling the
	// ingress supports SNI.
	// +optional
	TLS []ClusterIngressTLS `json:"tls,omitempty"`

	// A list of host rules used to configure the ClusterIngress.
	// +optional
	Rules []ClusterIngressRule `json:"rules,omitempty"`

	// Visibility setting.
	Visibility IngressVisibility `json:"visibility,omitempty"`
}

// IngressVisibility describes whether the Ingress should be exposed to
// public gateways or not.
type IngressVisibility string

const (
	// IngressVisibilityExternalIP is used to denote that the Ingress
	// should be exposed via an external IP, for example a LoadBalancer
	// Service.  This is the default value for IngressVisibility.
	IngressVisibilityExternalIP IngressVisibility = "ExternalIP"
	// IngressVisibilityClusterLocal is used to denote that the Ingress
	// should be only be exposed locally to the cluster.
	IngressVisibilityClusterLocal IngressVisibility = "ClusterLocal"
)

// ClusterIngressTLS describes the transport layer security associated with an ClusterIngress.
type ClusterIngressTLS struct {
	// Hosts is a list of hosts included in the TLS certificate. The values in
	// this list must match the name/s used in the tlsSecret. Defaults to the
	// wildcard host setting for the loadbalancer controller fulfilling this
	// ClusterIngress, if left unspecified.
	// +optional
	Hosts []string `json:"hosts,omitempty"`

	// SecretName is the name of the secret used to terminate SSL traffic.
	SecretName string `json:"secretName,omitempty"`

	// SecretNamespace is the namespace of the secret used to terminate SSL traffic.
	SecretNamespace string `json:"secretNamespace,omitempty"`

	// ServerCertificate identifies the certificate filename in the secret.
	// Defaults to `tls.cert`.
	// +optional
	ServerCertificate string `json:"serverCertificate,omitempty"`

	// PrivateKey identifies the private key filename in the secret.
	// Defaults to `tls.key`.
	// +optional
	PrivateKey string `json:"privateKey,omitempty"`
}

// ClusterIngressRule represents the rules mapping the paths under a specified host to
// the related backend services. Incoming requests are first evaluated for a host
// match, then routed to the backend associated with the matching ClusterIngressRuleValue.
type ClusterIngressRule struct {
	// Host is the fully qualified domain name of a network host, as defined
	// by RFC 3986. Note the following deviations from the "host" part of the
	// URI as defined in the RFC:
	// 1. IPs are not allowed. Currently a rule value can only apply to the
	//	  IP in the Spec of the parent ClusterIngress.
	// 2. The `:` delimiter is not respected because ports are not allowed.
	//	  Currently the port of an ClusterIngress is implicitly :80 for http and
	//	  :443 for https.
	// Both these may change in the future.
	// If the host is unspecified, the ClusterIngress routes all traffic based on the
	// specified ClusterIngressRuleValue.
	// If multiple matching Hosts were provided, the first rule will take precedent.
	// +optional
	Hosts []string `json:"hosts,omitempty"`

	// HTTP represents a rule to apply against incoming requests. If the
	// rule is satisfied, the request is routed to the specified backend.
	HTTP *HTTPClusterIngressRuleValue `json:"http,omitempty"`
}

// HTTPClusterIngressRuleValue is a list of http selectors pointing to backends.
// In the example: http://<host>/<path>?<searchpart> -> backend where
// where parts of the url correspond to RFC 3986, this resource will be used
// to match against everything after the last '/' and before the first '?'
// or '#'.
type HTTPClusterIngressRuleValue struct {
	// A collection of paths that map requests to backends.
	//
	// If they are multiple matching paths, the first match takes precendent.
	Paths []HTTPClusterIngressPath `json:"paths"`

	// TODO: Consider adding fields for ingress-type specific global
	// options usable by a loadbalancer, like http keep-alive.
}

// HTTPClusterIngressPath associates a path regex with a backend. Incoming URLs matching
// the path are forwarded to the backend.
type HTTPClusterIngressPath struct {
	// Path is an extended POSIX regex as defined by IEEE Std 1003.1,
	// (i.e this follows the egrep/unix syntax, not the perl syntax)
	// matched against the path of an incoming request. Currently it can
	// contain characters disallowed from the conventional "path"
	// part of a URL as defined by RFC 3986. Paths must begin with
	// a '/'. If unspecified, the path defaults to a catch all sending
	// traffic to the backend.
	// +optional
	Path string `json:"path,omitempty"`

	// Splits defines the referenced service endpoints to which the traffic
	// will be forwarded to.
	Splits []ClusterIngressBackendSplit `json:"splits"`

	// AppendHeaders allow specifying additional HTTP headers to add
	// before forwarding a request to the destination service.
	//
	// NOTE: This differs from K8s Ingress which doesn't allow header appending.
	// +optional
	AppendHeaders map[string]string `json:"appendHeaders,omitempty"`

	// Timeout for HTTP requests.
	//
	// NOTE: This differs from K8s Ingress which doesn't allow setting timeouts.
	// +optional
	Timeout *metav1.Duration `json:"timeout,omitempty"`

	// Retry policy for HTTP requests.
	//
	// NOTE: This differs from K8s Ingress which doesn't allow retry settings.
	// +optional
	Retries *HTTPRetry `json:"retries,omitempty"`
}

// ClusterIngressBackendSplit describes all endpoints for a given service and port.
type ClusterIngressBackendSplit struct {
	// Specifies the backend receiving the traffic split.
	ClusterIngressBackend `json:",inline"`

	// Specifies the split percentage, a number between 0 and 100.  If
	// only one split is specified, we default to 100.
	//
	// NOTE: This differs from K8s Ingress to allow percentage split.
	Percent int `json:"percent,omitempty"`

	// AppendHeaders allow specifying additional HTTP headers to add
	// before forwarding a request to the destination service.
	//
	// NOTE: This differs from K8s Ingress which doesn't allow header appending.
	// +optional
	AppendHeaders map[string]string `json:"appendHeaders,omitempty"`
}

// ClusterIngressBackend describes all endpoints for a given service and port.
type ClusterIngressBackend struct {
	// Specifies the namespace of the referenced service.
	//
	// NOTE: This differs from K8s Ingress to allow routing to different namespaces.
	ServiceNamespace string `json:"serviceNamespace"`

	// Specifies the name of the referenced service.
	ServiceName string `json:"serviceName"`

	// Specifies the port of the referenced service.
	ServicePort intstr.IntOrString `json:"servicePort"`
}

// HTTPRetry describes the retry policy to use when a HTTP request fails.
type HTTPRetry struct {
	// Number of retries for a given request.
	Attempts int `json:"attempts"`

	// Timeout per retry attempt for a given request. format: 1h/1m/1s/1ms. MUST BE >=1ms.
	PerTryTimeout *metav1.Duration `json:"perTryTimeout"`
}

// IngressStatus describe the current state of the ClusterIngress.
type IngressStatus struct {
	duckv1beta1.Status `json:",inline"`

	// LoadBalancer contains the current status of the load-balancer.
	// +optional
	LoadBalancer *LoadBalancerStatus `json:"loadBalancer,omitempty"`
}

// LoadBalancerStatus represents the status of a load-balancer.
type LoadBalancerStatus struct {
	// Ingress is a list containing ingress points for the load-balancer.
	// Traffic intended for the service should be sent to these ingress points.
	// +optional
	Ingress []LoadBalancerIngressStatus `json:"ingress,omitempty"`
}

// LoadBalancerIngress represents the status of a load-balancer ingress point:
// traffic intended for the service should be sent to an ingress point.
type LoadBalancerIngressStatus struct {
	// IP is set for load-balancer ingress points that are IP based
	// (typically GCE or OpenStack load-balancers)
	// +optional
	IP string `json:"ip,omitempty"`

	// Domain is set for load-balancer ingress points that are DNS based
	// (typically AWS load-balancers)
	// +optional
	Domain string `json:"domain,omitempty"`

	// DomainInternal is set if there is a cluster-local DNS name to access the Ingress.
	//
	// NOTE: This differs from K8s Ingress, since we also desire to have a cluster-local
	//       DNS name to allow routing in case of not having a mesh.
	//
	// +optional
	DomainInternal string `json:"domainInternal,omitempty"`

	// MeshOnly is set if the ClusterIngress is only load-balanced through a Service mesh.
	// +optional
	MeshOnly bool `json:"meshOnly,omitempty"`
}

// ConditionType represents a ClusterIngress condition value
const (
	// ClusterIngressConditionReady is set when the clusterIngress networking setting is
	// configured and it has a load balancer address.
	ClusterIngressConditionReady = apis.ConditionReady

	// ClusterIngressConditionNetworkConfigured is set when the ClusterIngress's underlying
	// network programming has been configured.  This doesn't include conditions of the
	// backends, so even if this should remain true when network is configured and backends
	// are not ready.
	ClusterIngressConditionNetworkConfigured apis.ConditionType = "NetworkConfigured"

	// ClusterIngressConditionLoadBalancerReady is set when the ClusterIngress has
	// a ready LoadBalancer.
	ClusterIngressConditionLoadBalancerReady apis.ConditionType = "LoadBalancerReady"
)
<commit_msg>fix typo (#3617)<commit_after>/*
Copyright 2018 The Knative 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 v1alpha1

import (
	"github.com/knative/pkg/apis"
	duckv1beta1 "github.com/knative/pkg/apis/duck/v1beta1"
	"github.com/knative/pkg/kmeta"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/util/intstr"
)

// +genclient
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
// +genclient:nonNamespaced

// ClusterIngress is a collection of rules that allow inbound connections to reach the
// endpoints defined by a backend. A ClusterIngress can be configured to give services
// externally-reachable URLs, load balance traffic, offer name based virtual hosting, etc.
//
// This is heavily based on K8s Ingress https://godoc.org/k8s.io/api/extensions/v1beta1#Ingress
// which some highlighted modifications.
type ClusterIngress struct {
	metav1.TypeMeta `json:",inline"`
	// Standard object's metadata.
	// More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata
	// +optional
	metav1.ObjectMeta `json:"metadata,omitempty"`

	// Spec is the desired state of the ClusterIngress.
	// More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#spec-and-status
	// +optional
	Spec IngressSpec `json:"spec,omitempty"`

	// Status is the current state of the ClusterIngress.
	// More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#spec-and-status
	// +optional
	Status IngressStatus `json:"status,omitempty"`
}

// Verify that ClusterIngress adheres to the appropriate interfaces.
var (
	// Check that ClusterIngress may be validated and defaulted.
	_ apis.Validatable = (*ClusterIngress)(nil)
	_ apis.Defaultable = (*ClusterIngress)(nil)

	// Check that we can create OwnerReferences to a ClusterIngress.
	_ kmeta.OwnerRefable = (*ClusterIngress)(nil)
)

// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object

// ClusterIngressList is a collection of ClusterIngress objects.
type ClusterIngressList struct {
	metav1.TypeMeta `json:",inline"`
	// Standard object metadata.
	// More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata
	// +optional
	metav1.ListMeta `json:"metadata,omitempty"`

	// Items is the list of ClusterIngress objects.
	Items []ClusterIngress `json:"items"`
}

// IngressSpec describes the ClusterIngress the user wishes to exist.
//
// In general this follows the same shape as K8s Ingress.
// Some notable differences:
// - Backends now can have namespace:
// - Traffic can be split across multiple backends.
// - Timeout & Retry can be configured.
// - Headers can be appended.
type IngressSpec struct {
	// DeprecatedGeneration was used prior in Kubernetes versions <1.11
	// when metadata.generation was not being incremented by the api server
	//
	// This property will be dropped in future Knative releases and should
	// not be used - use metadata.generation
	//
	// Tracking issue: https://github.com/knative/serving/issues/643
	//
	// +optional
	DeprecatedGeneration int64 `json:"generation,omitempty"`

	// TLS configuration. Currently ClusterIngress only supports a single TLS
	// port: 443. If multiple members of this list specify different hosts, they
	// will be multiplexed on the same port according to the hostname specified
	// through the SNI TLS extension, if the ingress controller fulfilling the
	// ingress supports SNI.
	// +optional
	TLS []ClusterIngressTLS `json:"tls,omitempty"`

	// A list of host rules used to configure the ClusterIngress.
	// +optional
	Rules []ClusterIngressRule `json:"rules,omitempty"`

	// Visibility setting.
	Visibility IngressVisibility `json:"visibility,omitempty"`
}

// IngressVisibility describes whether the Ingress should be exposed to
// public gateways or not.
type IngressVisibility string

const (
	// IngressVisibilityExternalIP is used to denote that the Ingress
	// should be exposed via an external IP, for example a LoadBalancer
	// Service.  This is the default value for IngressVisibility.
	IngressVisibilityExternalIP IngressVisibility = "ExternalIP"
	// IngressVisibilityClusterLocal is used to denote that the Ingress
	// should be only be exposed locally to the cluster.
	IngressVisibilityClusterLocal IngressVisibility = "ClusterLocal"
)

// ClusterIngressTLS describes the transport layer security associated with an ClusterIngress.
type ClusterIngressTLS struct {
	// Hosts is a list of hosts included in the TLS certificate. The values in
	// this list must match the name/s used in the tlsSecret. Defaults to the
	// wildcard host setting for the loadbalancer controller fulfilling this
	// ClusterIngress, if left unspecified.
	// +optional
	Hosts []string `json:"hosts,omitempty"`

	// SecretName is the name of the secret used to terminate SSL traffic.
	SecretName string `json:"secretName,omitempty"`

	// SecretNamespace is the namespace of the secret used to terminate SSL traffic.
	SecretNamespace string `json:"secretNamespace,omitempty"`

	// ServerCertificate identifies the certificate filename in the secret.
	// Defaults to `tls.crt`.
	// +optional
	ServerCertificate string `json:"serverCertificate,omitempty"`

	// PrivateKey identifies the private key filename in the secret.
	// Defaults to `tls.key`.
	// +optional
	PrivateKey string `json:"privateKey,omitempty"`
}

// ClusterIngressRule represents the rules mapping the paths under a specified host to
// the related backend services. Incoming requests are first evaluated for a host
// match, then routed to the backend associated with the matching ClusterIngressRuleValue.
type ClusterIngressRule struct {
	// Host is the fully qualified domain name of a network host, as defined
	// by RFC 3986. Note the following deviations from the "host" part of the
	// URI as defined in the RFC:
	// 1. IPs are not allowed. Currently a rule value can only apply to the
	//	  IP in the Spec of the parent ClusterIngress.
	// 2. The `:` delimiter is not respected because ports are not allowed.
	//	  Currently the port of an ClusterIngress is implicitly :80 for http and
	//	  :443 for https.
	// Both these may change in the future.
	// If the host is unspecified, the ClusterIngress routes all traffic based on the
	// specified ClusterIngressRuleValue.
	// If multiple matching Hosts were provided, the first rule will take precedent.
	// +optional
	Hosts []string `json:"hosts,omitempty"`

	// HTTP represents a rule to apply against incoming requests. If the
	// rule is satisfied, the request is routed to the specified backend.
	HTTP *HTTPClusterIngressRuleValue `json:"http,omitempty"`
}

// HTTPClusterIngressRuleValue is a list of http selectors pointing to backends.
// In the example: http://<host>/<path>?<searchpart> -> backend where
// where parts of the url correspond to RFC 3986, this resource will be used
// to match against everything after the last '/' and before the first '?'
// or '#'.
type HTTPClusterIngressRuleValue struct {
	// A collection of paths that map requests to backends.
	//
	// If they are multiple matching paths, the first match takes precendent.
	Paths []HTTPClusterIngressPath `json:"paths"`

	// TODO: Consider adding fields for ingress-type specific global
	// options usable by a loadbalancer, like http keep-alive.
}

// HTTPClusterIngressPath associates a path regex with a backend. Incoming URLs matching
// the path are forwarded to the backend.
type HTTPClusterIngressPath struct {
	// Path is an extended POSIX regex as defined by IEEE Std 1003.1,
	// (i.e this follows the egrep/unix syntax, not the perl syntax)
	// matched against the path of an incoming request. Currently it can
	// contain characters disallowed from the conventional "path"
	// part of a URL as defined by RFC 3986. Paths must begin with
	// a '/'. If unspecified, the path defaults to a catch all sending
	// traffic to the backend.
	// +optional
	Path string `json:"path,omitempty"`

	// Splits defines the referenced service endpoints to which the traffic
	// will be forwarded to.
	Splits []ClusterIngressBackendSplit `json:"splits"`

	// AppendHeaders allow specifying additional HTTP headers to add
	// before forwarding a request to the destination service.
	//
	// NOTE: This differs from K8s Ingress which doesn't allow header appending.
	// +optional
	AppendHeaders map[string]string `json:"appendHeaders,omitempty"`

	// Timeout for HTTP requests.
	//
	// NOTE: This differs from K8s Ingress which doesn't allow setting timeouts.
	// +optional
	Timeout *metav1.Duration `json:"timeout,omitempty"`

	// Retry policy for HTTP requests.
	//
	// NOTE: This differs from K8s Ingress which doesn't allow retry settings.
	// +optional
	Retries *HTTPRetry `json:"retries,omitempty"`
}

// ClusterIngressBackendSplit describes all endpoints for a given service and port.
type ClusterIngressBackendSplit struct {
	// Specifies the backend receiving the traffic split.
	ClusterIngressBackend `json:",inline"`

	// Specifies the split percentage, a number between 0 and 100.  If
	// only one split is specified, we default to 100.
	//
	// NOTE: This differs from K8s Ingress to allow percentage split.
	Percent int `json:"percent,omitempty"`

	// AppendHeaders allow specifying additional HTTP headers to add
	// before forwarding a request to the destination service.
	//
	// NOTE: This differs from K8s Ingress which doesn't allow header appending.
	// +optional
	AppendHeaders map[string]string `json:"appendHeaders,omitempty"`
}

// ClusterIngressBackend describes all endpoints for a given service and port.
type ClusterIngressBackend struct {
	// Specifies the namespace of the referenced service.
	//
	// NOTE: This differs from K8s Ingress to allow routing to different namespaces.
	ServiceNamespace string `json:"serviceNamespace"`

	// Specifies the name of the referenced service.
	ServiceName string `json:"serviceName"`

	// Specifies the port of the referenced service.
	ServicePort intstr.IntOrString `json:"servicePort"`
}

// HTTPRetry describes the retry policy to use when a HTTP request fails.
type HTTPRetry struct {
	// Number of retries for a given request.
	Attempts int `json:"attempts"`

	// Timeout per retry attempt for a given request. format: 1h/1m/1s/1ms. MUST BE >=1ms.
	PerTryTimeout *metav1.Duration `json:"perTryTimeout"`
}

// IngressStatus describe the current state of the ClusterIngress.
type IngressStatus struct {
	duckv1beta1.Status `json:",inline"`

	// LoadBalancer contains the current status of the load-balancer.
	// +optional
	LoadBalancer *LoadBalancerStatus `json:"loadBalancer,omitempty"`
}

// LoadBalancerStatus represents the status of a load-balancer.
type LoadBalancerStatus struct {
	// Ingress is a list containing ingress points for the load-balancer.
	// Traffic intended for the service should be sent to these ingress points.
	// +optional
	Ingress []LoadBalancerIngressStatus `json:"ingress,omitempty"`
}

// LoadBalancerIngress represents the status of a load-balancer ingress point:
// traffic intended for the service should be sent to an ingress point.
type LoadBalancerIngressStatus struct {
	// IP is set for load-balancer ingress points that are IP based
	// (typically GCE or OpenStack load-balancers)
	// +optional
	IP string `json:"ip,omitempty"`

	// Domain is set for load-balancer ingress points that are DNS based
	// (typically AWS load-balancers)
	// +optional
	Domain string `json:"domain,omitempty"`

	// DomainInternal is set if there is a cluster-local DNS name to access the Ingress.
	//
	// NOTE: This differs from K8s Ingress, since we also desire to have a cluster-local
	//       DNS name to allow routing in case of not having a mesh.
	//
	// +optional
	DomainInternal string `json:"domainInternal,omitempty"`

	// MeshOnly is set if the ClusterIngress is only load-balanced through a Service mesh.
	// +optional
	MeshOnly bool `json:"meshOnly,omitempty"`
}

// ConditionType represents a ClusterIngress condition value
const (
	// ClusterIngressConditionReady is set when the clusterIngress networking setting is
	// configured and it has a load balancer address.
	ClusterIngressConditionReady = apis.ConditionReady

	// ClusterIngressConditionNetworkConfigured is set when the ClusterIngress's underlying
	// network programming has been configured.  This doesn't include conditions of the
	// backends, so even if this should remain true when network is configured and backends
	// are not ready.
	ClusterIngressConditionNetworkConfigured apis.ConditionType = "NetworkConfigured"

	// ClusterIngressConditionLoadBalancerReady is set when the ClusterIngress has
	// a ready LoadBalancer.
	ClusterIngressConditionLoadBalancerReady apis.ConditionType = "LoadBalancerReady"
)
<|endoftext|>


<commit_before>// +build linux

/*
Copyright 2016 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 cm

import (
	"fmt"
	"io/ioutil"
	"regexp"
	"strconv"
	"time"

	"github.com/golang/glog"
	"github.com/opencontainers/runc/libcontainer/cgroups/fs"
	"github.com/opencontainers/runc/libcontainer/configs"
	"k8s.io/apimachinery/pkg/util/wait"
	kubecm "k8s.io/kubernetes/pkg/kubelet/cm"
	"k8s.io/kubernetes/pkg/kubelet/dockertools"
	"k8s.io/kubernetes/pkg/kubelet/qos"
	utilversion "k8s.io/kubernetes/pkg/util/version"
)

const (
	// The percent of the machine memory capacity.
	dockerMemoryLimitThresholdPercent = kubecm.DockerMemoryLimitThresholdPercent

	// The minimum memory limit allocated to docker container.
	minDockerMemoryLimit = kubecm.MinDockerMemoryLimit

	// The Docker OOM score adjustment.
	dockerOOMScoreAdj = qos.DockerOOMScoreAdj
)

var (
	memoryCapacityRegexp = regexp.MustCompile(`MemTotal:\s*([0-9]+) kB`)
)

func NewContainerManager(cgroupsName string, client dockertools.DockerInterface) ContainerManager {
	return &containerManager{
		cgroupsName: cgroupsName,
		client:      client,
	}
}

type containerManager struct {
	// Docker client.
	client dockertools.DockerInterface
	// Name of the cgroups.
	cgroupsName string
	// Manager for the cgroups.
	cgroupsManager *fs.Manager
}

func (m *containerManager) Start() error {
	// TODO: check if the required cgroups are mounted.
	if len(m.cgroupsName) != 0 {
		manager, err := createCgroupManager(m.cgroupsName)
		if err != nil {
			return err
		}
		m.cgroupsManager = manager
	}
	go wait.Until(m.doWork, 5*time.Minute, wait.NeverStop)
	return nil
}

func (m *containerManager) doWork() {
	v, err := m.client.Version()
	if err != nil {
		glog.Errorf("Unable to get docker version: %v", err)
		return
	}
	version, err := utilversion.ParseSemantic(v.Version)
	if err != nil {
		glog.Errorf("Unable to parse docker version %q: %v", v.Version, err)
		return
	}
	// EnsureDockerInConatiner does two things.
	//   1. Ensure processes run in the cgroups if m.cgroupsManager is not nil.
	//   2. Ensure processes have the OOM score applied.
	if err := kubecm.EnsureDockerInContainer(version, dockerOOMScoreAdj, m.cgroupsManager); err != nil {
		glog.Errorf("Unable to ensure the docker processes run in the desired containers")
	}
}

func createCgroupManager(name string) (*fs.Manager, error) {
	var memoryLimit uint64
	memoryCapacity, err := getMemoryCapacity()
	if err != nil || memoryCapacity*dockerMemoryLimitThresholdPercent/100 < minDockerMemoryLimit {
		memoryLimit = minDockerMemoryLimit
	}
	glog.V(2).Infof("Configure resource-only container %q with memory limit: %d", name, memoryLimit)

	allowAllDevices := true
	cm := &fs.Manager{
		Cgroups: &configs.Cgroup{
			Parent: "/",
			Name:   name,
			Resources: &configs.Resources{
				Memory:          int64(memoryLimit),
				MemorySwap:      -1,
				AllowAllDevices: &allowAllDevices,
			},
		},
	}
	return cm, nil
}

// getMemoryCapacity returns the memory capacity on the machine in bytes.
func getMemoryCapacity() (uint64, error) {
	out, err := ioutil.ReadFile("/proc/meminfo")
	if err != nil {
		return 0, err
	}
	return parseCapacity(out, memoryCapacityRegexp)
}

// parseCapacity matches a Regexp in a []byte, returning the resulting value in bytes.
// Assumes that the value matched by the Regexp is in KB.
func parseCapacity(b []byte, r *regexp.Regexp) (uint64, error) {
	matches := r.FindSubmatch(b)
	if len(matches) != 2 {
		return 0, fmt.Errorf("failed to match regexp in output: %q", string(b))
	}
	m, err := strconv.ParseUint(string(matches[1]), 10, 64)
	if err != nil {
		return 0, err
	}

	// Convert to bytes.
	return m * 1024, err
}
<commit_msg>update docker version parser for its new versioning scheme<commit_after>// +build linux

/*
Copyright 2016 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 cm

import (
	"fmt"
	"io/ioutil"
	"regexp"
	"strconv"
	"time"

	"github.com/golang/glog"
	"github.com/opencontainers/runc/libcontainer/cgroups/fs"
	"github.com/opencontainers/runc/libcontainer/configs"
	"k8s.io/apimachinery/pkg/util/wait"
	kubecm "k8s.io/kubernetes/pkg/kubelet/cm"
	"k8s.io/kubernetes/pkg/kubelet/dockertools"
	"k8s.io/kubernetes/pkg/kubelet/qos"
	utilversion "k8s.io/kubernetes/pkg/util/version"
)

const (
	// The percent of the machine memory capacity.
	dockerMemoryLimitThresholdPercent = kubecm.DockerMemoryLimitThresholdPercent

	// The minimum memory limit allocated to docker container.
	minDockerMemoryLimit = kubecm.MinDockerMemoryLimit

	// The Docker OOM score adjustment.
	dockerOOMScoreAdj = qos.DockerOOMScoreAdj
)

var (
	memoryCapacityRegexp = regexp.MustCompile(`MemTotal:\s*([0-9]+) kB`)
)

func NewContainerManager(cgroupsName string, client dockertools.DockerInterface) ContainerManager {
	return &containerManager{
		cgroupsName: cgroupsName,
		client:      client,
	}
}

type containerManager struct {
	// Docker client.
	client dockertools.DockerInterface
	// Name of the cgroups.
	cgroupsName string
	// Manager for the cgroups.
	cgroupsManager *fs.Manager
}

func (m *containerManager) Start() error {
	// TODO: check if the required cgroups are mounted.
	if len(m.cgroupsName) != 0 {
		manager, err := createCgroupManager(m.cgroupsName)
		if err != nil {
			return err
		}
		m.cgroupsManager = manager
	}
	go wait.Until(m.doWork, 5*time.Minute, wait.NeverStop)
	return nil
}

func (m *containerManager) doWork() {
	v, err := m.client.Version()
	if err != nil {
		glog.Errorf("Unable to get docker version: %v", err)
		return
	}
	version, err := utilversion.ParseGeneric(v.Version)
	if err != nil {
		glog.Errorf("Unable to parse docker version %q: %v", v.Version, err)
		return
	}
	// EnsureDockerInConatiner does two things.
	//   1. Ensure processes run in the cgroups if m.cgroupsManager is not nil.
	//   2. Ensure processes have the OOM score applied.
	if err := kubecm.EnsureDockerInContainer(version, dockerOOMScoreAdj, m.cgroupsManager); err != nil {
		glog.Errorf("Unable to ensure the docker processes run in the desired containers")
	}
}

func createCgroupManager(name string) (*fs.Manager, error) {
	var memoryLimit uint64
	memoryCapacity, err := getMemoryCapacity()
	if err != nil || memoryCapacity*dockerMemoryLimitThresholdPercent/100 < minDockerMemoryLimit {
		memoryLimit = minDockerMemoryLimit
	}
	glog.V(2).Infof("Configure resource-only container %q with memory limit: %d", name, memoryLimit)

	allowAllDevices := true
	cm := &fs.Manager{
		Cgroups: &configs.Cgroup{
			Parent: "/",
			Name:   name,
			Resources: &configs.Resources{
				Memory:          int64(memoryLimit),
				MemorySwap:      -1,
				AllowAllDevices: &allowAllDevices,
			},
		},
	}
	return cm, nil
}

// getMemoryCapacity returns the memory capacity on the machine in bytes.
func getMemoryCapacity() (uint64, error) {
	out, err := ioutil.ReadFile("/proc/meminfo")
	if err != nil {
		return 0, err
	}
	return parseCapacity(out, memoryCapacityRegexp)
}

// parseCapacity matches a Regexp in a []byte, returning the resulting value in bytes.
// Assumes that the value matched by the Regexp is in KB.
func parseCapacity(b []byte, r *regexp.Regexp) (uint64, error) {
	matches := r.FindSubmatch(b)
	if len(matches) != 2 {
		return 0, fmt.Errorf("failed to match regexp in output: %q", string(b))
	}
	m, err := strconv.ParseUint(string(matches[1]), 10, 64)
	if err != nil {
		return 0, err
	}

	// Convert to bytes.
	return m * 1024, err
}
<|endoftext|>


<commit_before>// Package ieproxy is a utility to retrieve the proxy parameters (especially of Internet Explorer on windows)
//
// On windows, it gathers the parameters from the registry (regedit), while it uses env variable on other platforms
package ieproxy

import "os"

// ProxyConf gathers the configuration for proxy
type ProxyConf struct {
	Static    StaticProxyConf    // static configuration
	Automatic AutomaticProxyConf // automatic configuration
}

// StaticProxyConf containes the configuration for static proxy
type StaticProxyConf struct {
	// Is the proxy active?
	Active bool
	// Proxy address for each scheme (http, https)
	// "" (empty string) is the fallback proxy
	Protocols map[string]string
	// Addresses not to be browsed via the proxy (comma-separated, linux-like)
	NoProxy string
}

// AutomaticProxyConf contains the configuration for automatic proxy
type AutomaticProxyConf struct {
	// Is the proxy active?
	Active bool
	// URL of the .pac file
	URL string
}

// GetConf retrieves the proxy configuration from the Windows Regedit
func GetConf() ProxyConf {
	return getConf()
}

// OverrideEnvWithStaticProxy writes new values to the
// `http_proxy`, `https_proxy` and `no_proxy` environment variables.
// The values are taken from the Windows Regedit (should be called in `init()` function - see example)
func OverrideEnvWithStaticProxy() {
	overrideEnvWithStaticProxy(getConf(), os.Setenv)
}

type envSetter func(string, string) error
<commit_msg>increase coverage<commit_after>// Package ieproxy is a utility to retrieve the proxy parameters (especially of Internet Explorer on windows)
//
// On windows, it gathers the parameters from the registry (regedit), while it uses env variable on other platforms
package ieproxy

import "os"

// ProxyConf gathers the configuration for proxy
type ProxyConf struct {
	Static    StaticProxyConf    // static configuration
	Automatic AutomaticProxyConf // automatic configuration
}

// StaticProxyConf containes the configuration for static proxy
type StaticProxyConf struct {
	// Is the proxy active?
	Active bool
	// Proxy address for each scheme (http, https)
	// "" (empty string) is the fallback proxy
	Protocols map[string]string
	// Addresses not to be browsed via the proxy (comma-separated, linux-like)
	NoProxy string
}

// AutomaticProxyConf contains the configuration for automatic proxy
type AutomaticProxyConf struct {
	// Is the proxy active?
	Active bool
	// URL of the .pac file
	URL string
}

// GetConf retrieves the proxy configuration from the Windows Regedit
func GetConf() ProxyConf {
	return getConf()
}

// OverrideEnvWithStaticProxy writes new values to the
// `http_proxy`, `https_proxy` and `no_proxy` environment variables.
// The values are taken from the Windows Regedit (should be called in `init()` function - see example)
func OverrideEnvWithStaticProxy() {
	overrideEnvWithStaticProxy(GetConf(), os.Setenv)
}

type envSetter func(string, string) error
<|endoftext|>


<commit_before>package main

import (
	"go/build"
	"log"
	"os"
	"path/filepath"
	"sort"
	"strings"
)

func recursiveParseImports(
	imports map[string]bool, path string, cwd string,
) error {
	if path == "C" {
		return nil
	}

	// catch internal vendoring in net/http since go 1.7
	if strings.HasPrefix(path, "golang_org/") {
		return nil
	}

	pkg, err := build.Import(path, cwd, build.IgnoreVendor)
	if err != nil {
		return err
	}

	if path != "." {
		standard := false

		if strings.HasPrefix(pkg.ImportPath, "golang.org/") ||
			(pkg.Goroot && pkg.ImportPath != "") {
			standard = true
		}

		imports[pkg.ImportPath] = standard
	}

	for _, importing := range pkg.Imports {
		_, ok := imports[importing]
		if !ok {
			err = recursiveParseImports(imports, importing, cwd)
			if err != nil {
				return err
			}
		}
	}

	return nil
}

func parseImports(recursive bool) ([]string, error) {
	cwd, err := os.Getwd()
	if err != nil {
		return nil, err
	}

	var (
		allImports = map[string]bool{}
		imports    = []string{}
	)

	filepath.Walk(
		cwd, func(path string, info os.FileInfo, err error) error {
			if err != nil {
				return err
			}

			if filepath.Base(path) == ".git" ||
				filepath.Dir(path) == filepath.Join(cwd, "vendor") {
				return filepath.SkipDir
			}

			if path == filepath.Join(cwd, "vendor") {
				return nil
			}

			if !info.IsDir() {
				return nil
			}

			err = recursiveParseImports(
				allImports,
				".",
				path,
			)
			if _, ok := err.(*build.NoGoError); ok {
				return nil
			}

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

			return nil
		},
	)

	for importing, standard := range allImports {
		if !standard {
			importpath, err := getRootImportpath(importing)
			if err != nil {
				continue
			}

			if inTests {
				importpath = strings.Replace(importpath, "__blankd__", "localhost:60001", -1)
			}

			if isOwnPackage(importpath, cwd) {
				continue
			}

			found := false
			for _, imported := range imports {
				if importpath == imported {
					found = true
					break
				}
			}

			if found {
				continue
			}

			imports = append(imports, importpath)
		}
	}

	sort.Strings(imports)

	return imports, nil
}

func isOwnPackage(path, cwd string) bool {
	for _, gopath := range filepath.SplitList(os.Getenv("GOPATH")) {
		if strings.HasPrefix(filepath.Join(gopath, "src", path), cwd) {
			return true
		}
	}
	return false
}
<commit_msg>do not ignore golang.org/ packages<commit_after>package main

import (
	"go/build"
	"log"
	"os"
	"path/filepath"
	"sort"
	"strings"
)

func recursiveParseImports(
	imports map[string]bool, path string, cwd string,
) error {
	if path == "C" {
		return nil
	}

	// catch internal vendoring in net/http since go 1.7
	if strings.HasPrefix(path, "golang_org/") {
		return nil
	}

	pkg, err := build.Import(path, cwd, build.IgnoreVendor)
	if err != nil {
		return err
	}

	if path != "." {
		standard := false

		if pkg.Goroot && pkg.ImportPath != "" {
			standard = true
		}

		imports[pkg.ImportPath] = standard
	}

	for _, importing := range pkg.Imports {
		_, ok := imports[importing]
		if !ok {
			err = recursiveParseImports(imports, importing, cwd)
			if err != nil {
				return err
			}
		}
	}

	return nil
}

func parseImports(recursive bool) ([]string, error) {
	cwd, err := os.Getwd()
	if err != nil {
		return nil, err
	}

	var (
		allImports = map[string]bool{}
		imports    = []string{}
	)

	filepath.Walk(
		cwd, func(path string, info os.FileInfo, err error) error {
			if err != nil {
				return err
			}

			if filepath.Base(path) == ".git" ||
				filepath.Dir(path) == filepath.Join(cwd, "vendor") {
				return filepath.SkipDir
			}

			if path == filepath.Join(cwd, "vendor") {
				return nil
			}

			if !info.IsDir() {
				return nil
			}

			err = recursiveParseImports(
				allImports,
				".",
				path,
			)
			if _, ok := err.(*build.NoGoError); ok {
				return nil
			}

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

			return nil
		},
	)

	for importing, standard := range allImports {
		if !standard {
			importpath, err := getRootImportpath(importing)
			if err != nil {
				continue
			}

			if inTests {
				importpath = strings.Replace(importpath, "__blankd__", "localhost:60001", -1)
			}

			if isOwnPackage(importpath, cwd) {
				continue
			}

			found := false
			for _, imported := range imports {
				if importpath == imported {
					found = true
					break
				}
			}

			if found {
				continue
			}

			imports = append(imports, importpath)
		}
	}

	sort.Strings(imports)

	return imports, nil
}

func isOwnPackage(path, cwd string) bool {
	for _, gopath := range filepath.SplitList(os.Getenv("GOPATH")) {
		if strings.HasPrefix(filepath.Join(gopath, "src", path), cwd) {
			return true
		}
	}
	return false
}
<|endoftext|>


<commit_before>package kubernetes

import (
	"errors"
	"fmt"
	"io"
	"net/http"
	"time"

	"golang.org/x/net/context"
	"k8s.io/kubernetes/pkg/api"
	"k8s.io/kubernetes/pkg/api/resource"
	"k8s.io/kubernetes/pkg/client/restclient"
	client "k8s.io/kubernetes/pkg/client/unversioned"
	clientcmd "k8s.io/kubernetes/pkg/client/unversioned/clientcmd"
	clientcmdapi "k8s.io/kubernetes/pkg/client/unversioned/clientcmd/api"

	"gitlab.com/gitlab-org/gitlab-ci-multi-runner/common"
)

func init() {
	clientcmd.DefaultCluster = clientcmdapi.Cluster{}
}

func getKubeClientConfig(config *common.KubernetesConfig) (*restclient.Config, error) {
	switch {
	case len(config.CertFile) > 0:
		if len(config.KeyFile) == 0 || len(config.CAFile) == 0 {
			return nil, fmt.Errorf("ca file, cert file and key file must be specified when using file based auth")
		}
		return &restclient.Config{
			Host: config.Host,
			TLSClientConfig: restclient.TLSClientConfig{
				CertFile: config.CertFile,
				KeyFile:  config.KeyFile,
				CAFile:   config.CAFile,
			},
		}, nil

	case len(config.Host) > 0:
		return &restclient.Config{
			Host: config.Host,
		}, nil

	default:
		config, err := clientcmd.NewDefaultClientConfigLoadingRules().Load()
		if err != nil {
			return nil, err
		}

		clientConfig := clientcmd.NewDefaultClientConfig(*config, &clientcmd.ConfigOverrides{})
		return clientConfig.ClientConfig()
	}
}

func getKubeClient(config *common.KubernetesConfig) (*client.Client, error) {
	restConfig, err := getKubeClientConfig(config)
	if err != nil {
		return nil, err
	}

	return client.New(restConfig)
}

func closeKubeClient(client *client.Client) bool {
	if client == nil || client.Client == nil || client.Client.Transport == nil {
		return false
	}
	if transport, _ := client.Client.Transport.(*http.Transport); transport != nil {
		transport.CloseIdleConnections()
		return true
	}
	return false
}

func isRunning(pod *api.Pod) (bool, error) {
	switch pod.Status.Phase {
	case api.PodRunning:
		return true, nil
	case api.PodSucceeded:
		return false, fmt.Errorf("pod already succeeded before it begins running")
	case api.PodFailed:
		return false, fmt.Errorf("pod status is failed")
	default:
		return false, nil
	}
}

type podPhaseResponse struct {
	done  bool
	phase api.PodPhase
	err   error
}

func getPodPhase(c *client.Client, pod *api.Pod, out io.Writer) podPhaseResponse {
	pod, err := c.Pods(pod.Namespace).Get(pod.Name)
	if err != nil {
		return podPhaseResponse{true, api.PodUnknown, err}
	}

	ready, err := isRunning(pod)

	if err != nil {
		return podPhaseResponse{true, pod.Status.Phase, err}
	}

	if ready {
		return podPhaseResponse{true, pod.Status.Phase, nil}
	}

	// check status of containers
	for _, container := range pod.Status.ContainerStatuses {
		if container.Ready {
			continue
		}
		if container.State.Waiting == nil {
			continue
		}

		switch container.State.Waiting.Reason {
		case "ErrImagePull", "ImagePullBackOff":
			err = errors.New(container.State.Waiting.Message)
			err = &common.BuildError{Inner: err}
			return podPhaseResponse{true, api.PodUnknown, err}
		}
	}

	fmt.Fprintf(out, "Waiting for pod %s/%s to be running, status is %s\n", pod.Namespace, pod.Name, pod.Status.Phase)
	return podPhaseResponse{false, pod.Status.Phase, nil}

}

func triggerPodPhaseCheck(c *client.Client, pod *api.Pod, out io.Writer) <-chan podPhaseResponse {
	errc := make(chan podPhaseResponse)
	go func() {
		defer close(errc)
		errc <- getPodPhase(c, pod, out)
	}()
	return errc
}

// waitForPodRunning will use client c to detect when pod reaches the PodRunning
// state. It will check every second, and will return the final PodPhase once
// either PodRunning, PodSucceeded or PodFailed has been reached. In the case of
// PodRunning, it will also wait until all containers within the pod are also Ready
// Returns error if the call to retrieve pod details fails
func waitForPodRunning(ctx context.Context, c *client.Client, pod *api.Pod, out io.Writer) (api.PodPhase, error) {
	for i := 0; i < 60; i++ {
		select {
		case r := <-triggerPodPhaseCheck(c, pod, out):
			if !r.done {
				time.Sleep(3 * time.Second)
				continue
			}

			return r.phase, r.err
		case <-ctx.Done():
			return api.PodUnknown, ctx.Err()
		}
	}
	return api.PodUnknown, errors.New("timedout waiting for pod to start")
}

// limits takes a string representing CPU & memory limits,
// and returns a ResourceList with appropriately scaled Quantity
// values for Kubernetes. This allows users to write "500m" for CPU,
// and "50Mi" for memory (etc.)
func limits(cpu, memory string) (api.ResourceList, error) {
	var rCPU, rMem resource.Quantity
	var err error

	parse := func(s string) (resource.Quantity, error) {
		var q resource.Quantity
		if len(s) == 0 {
			return q, nil
		}
		if q, err = resource.ParseQuantity(s); err != nil {
			return q, fmt.Errorf("error parsing resource limit: %s", err.Error())
		}
		return q, nil
	}

	if rCPU, err = parse(cpu); err != nil {
		return api.ResourceList{}, nil
	}

	if rMem, err = parse(memory); err != nil {
		return api.ResourceList{}, nil
	}

	l := make(api.ResourceList)

	q := resource.Quantity{}
	if rCPU != q {
		l[api.ResourceLimitsCPU] = rCPU
	}
	if rMem != q {
		l[api.ResourceLimitsMemory] = rMem
	}

	return l, nil
}

// buildVariables converts a common.BuildVariables into a list of
// kubernetes EnvVar objects
func buildVariables(bv common.BuildVariables) []api.EnvVar {
	e := make([]api.EnvVar, len(bv))
	for i, b := range bv {
		e[i] = api.EnvVar{
			Name:  b.Key,
			Value: b.Value,
		}
	}
	return e
}
<commit_msg>Try to load the InCluste config first, if that fails load kubectl config<commit_after>package kubernetes

import (
	"errors"
	"fmt"
	"io"
	"net/http"
	"time"

	"golang.org/x/net/context"
	"k8s.io/kubernetes/pkg/api"
	"k8s.io/kubernetes/pkg/api/resource"
	"k8s.io/kubernetes/pkg/client/restclient"
	client "k8s.io/kubernetes/pkg/client/unversioned"
	clientcmd "k8s.io/kubernetes/pkg/client/unversioned/clientcmd"
	clientcmdapi "k8s.io/kubernetes/pkg/client/unversioned/clientcmd/api"

	"gitlab.com/gitlab-org/gitlab-ci-multi-runner/common"
)

func init() {
	clientcmd.DefaultCluster = clientcmdapi.Cluster{}
}

func getKubeClientConfig(config *common.KubernetesConfig) (*restclient.Config, error) {
	switch {
	case len(config.CertFile) > 0:
		if len(config.KeyFile) == 0 || len(config.CAFile) == 0 {
			return nil, fmt.Errorf("ca file, cert file and key file must be specified when using file based auth")
		}
		return &restclient.Config{
			Host: config.Host,
			TLSClientConfig: restclient.TLSClientConfig{
				CertFile: config.CertFile,
				KeyFile:  config.KeyFile,
				CAFile:   config.CAFile,
			},
		}, nil

	case len(config.Host) > 0:
		return &restclient.Config{
			Host: config.Host,
		}, nil

	default:
		// Try in cluster config first
		if inClusterCfg, err := restclient.InClusterConfig(); err == nil {
			return inClusterCfg, nil
		}
		config, err := clientcmd.NewDefaultClientConfigLoadingRules().Load()
		if err != nil {
			return nil, err
		}

		clientConfig := clientcmd.NewDefaultClientConfig(*config, &clientcmd.ConfigOverrides{})
		return clientConfig.ClientConfig()
	}
}

func getKubeClient(config *common.KubernetesConfig) (*client.Client, error) {
	restConfig, err := getKubeClientConfig(config)
	if err != nil {
		return nil, err
	}

	return client.New(restConfig)
}

func closeKubeClient(client *client.Client) bool {
	if client == nil || client.Client == nil || client.Client.Transport == nil {
		return false
	}
	if transport, _ := client.Client.Transport.(*http.Transport); transport != nil {
		transport.CloseIdleConnections()
		return true
	}
	return false
}

func isRunning(pod *api.Pod) (bool, error) {
	switch pod.Status.Phase {
	case api.PodRunning:
		return true, nil
	case api.PodSucceeded:
		return false, fmt.Errorf("pod already succeeded before it begins running")
	case api.PodFailed:
		return false, fmt.Errorf("pod status is failed")
	default:
		return false, nil
	}
}

type podPhaseResponse struct {
	done  bool
	phase api.PodPhase
	err   error
}

func getPodPhase(c *client.Client, pod *api.Pod, out io.Writer) podPhaseResponse {
	pod, err := c.Pods(pod.Namespace).Get(pod.Name)
	if err != nil {
		return podPhaseResponse{true, api.PodUnknown, err}
	}

	ready, err := isRunning(pod)

	if err != nil {
		return podPhaseResponse{true, pod.Status.Phase, err}
	}

	if ready {
		return podPhaseResponse{true, pod.Status.Phase, nil}
	}

	// check status of containers
	for _, container := range pod.Status.ContainerStatuses {
		if container.Ready {
			continue
		}
		if container.State.Waiting == nil {
			continue
		}

		switch container.State.Waiting.Reason {
		case "ErrImagePull", "ImagePullBackOff":
			err = errors.New(container.State.Waiting.Message)
			err = &common.BuildError{Inner: err}
			return podPhaseResponse{true, api.PodUnknown, err}
		}
	}

	fmt.Fprintf(out, "Waiting for pod %s/%s to be running, status is %s\n", pod.Namespace, pod.Name, pod.Status.Phase)
	return podPhaseResponse{false, pod.Status.Phase, nil}

}

func triggerPodPhaseCheck(c *client.Client, pod *api.Pod, out io.Writer) <-chan podPhaseResponse {
	errc := make(chan podPhaseResponse)
	go func() {
		defer close(errc)
		errc <- getPodPhase(c, pod, out)
	}()
	return errc
}

// waitForPodRunning will use client c to detect when pod reaches the PodRunning
// state. It will check every second, and will return the final PodPhase once
// either PodRunning, PodSucceeded or PodFailed has been reached. In the case of
// PodRunning, it will also wait until all containers within the pod are also Ready
// Returns error if the call to retrieve pod details fails
func waitForPodRunning(ctx context.Context, c *client.Client, pod *api.Pod, out io.Writer) (api.PodPhase, error) {
	for i := 0; i < 60; i++ {
		select {
		case r := <-triggerPodPhaseCheck(c, pod, out):
			if !r.done {
				time.Sleep(3 * time.Second)
				continue
			}

			return r.phase, r.err
		case <-ctx.Done():
			return api.PodUnknown, ctx.Err()
		}
	}
	return api.PodUnknown, errors.New("timedout waiting for pod to start")
}

// limits takes a string representing CPU & memory limits,
// and returns a ResourceList with appropriately scaled Quantity
// values for Kubernetes. This allows users to write "500m" for CPU,
// and "50Mi" for memory (etc.)
func limits(cpu, memory string) (api.ResourceList, error) {
	var rCPU, rMem resource.Quantity
	var err error

	parse := func(s string) (resource.Quantity, error) {
		var q resource.Quantity
		if len(s) == 0 {
			return q, nil
		}
		if q, err = resource.ParseQuantity(s); err != nil {
			return q, fmt.Errorf("error parsing resource limit: %s", err.Error())
		}
		return q, nil
	}

	if rCPU, err = parse(cpu); err != nil {
		return api.ResourceList{}, nil
	}

	if rMem, err = parse(memory); err != nil {
		return api.ResourceList{}, nil
	}

	l := make(api.ResourceList)

	q := resource.Quantity{}
	if rCPU != q {
		l[api.ResourceLimitsCPU] = rCPU
	}
	if rMem != q {
		l[api.ResourceLimitsMemory] = rMem
	}

	return l, nil
}

// buildVariables converts a common.BuildVariables into a list of
// kubernetes EnvVar objects
func buildVariables(bv common.BuildVariables) []api.EnvVar {
	e := make([]api.EnvVar, len(bv))
	for i, b := range bv {
		e[i] = api.EnvVar{
			Name:  b.Key,
			Value: b.Value,
		}
	}
	return e
}
<|endoftext|>


<commit_before>package connect

import (
	"errors"
	"fmt"
	"strings"
)

const testVersion = 3

const (
	white = 1 << iota
	black
	connectedWhite
	connectedBlack
)

type colorFlags struct {
	color     int8
	connected int8
}

var flagsBlack = colorFlags{
	color:     black,
	connected: connectedBlack,
}

var flagsWhite = colorFlags{
	color:     white,
	connected: connectedWhite,
}

type coord struct {
	x int
	y int
}

type board struct {
	height int
	width  int
	fields [][]int8
}

func newBoard(lines []string) (board, error) {
	if len(lines) < 1 {
		return board{}, errors.New("No lines given")
	}
	height := int(len(lines))
	if len(lines[0]) < 1 {
		return board{}, errors.New("First line is empty string")
	}
	width := int(len(lines[0]))
	// This trick for 2D arrays comes from Effective Go
	fields := make([][]int8, height)
	fieldsBacker := make([]int8, height*width)
	for i := range fields {
		fields[i], fieldsBacker = fieldsBacker[:width], fieldsBacker[width:]
	}
	for y, line := range lines {
		for x, c := range line {
			switch c {
			case 'X':
				fields[y][x] = black
			case 'O':
				fields[y][x] = white
			}
			// No need for default, zero value already means no stone
		}
	}
	board := board{
		height: height,
		width:  width,
		fields: fields,
	}
	return board, nil
}

// Whether there is a stone of the given color at the given location.
//
// Returns both whether there is a stone of the correct color and
// whether the connected flag was set for it.
func (b board) at(c coord, cf colorFlags) (bool, bool) {
	f := b.fields[c.y][c.x]
	return f&cf.color == cf.color,
		f&cf.connected == cf.connected
}

func (b board) markConnected(c coord, cf colorFlags) {
	b.fields[c.y][c.x] |= cf.connected
}

func (b board) validCoord(c coord) bool {
	return c.x >= 0 && c.x < b.width && c.y >= 0 && c.y < b.height
}

func (b board) neighbours(c coord) []coord {
	coords := make([]coord, 0, 6)
	dirs := []coord{{1, 0}, {-1, 0}, {0, 1}, {0, -1}, {-1, 1}, {1, -1}}
	for _, dir := range dirs {
		nc := coord{x: c.x + dir.x, y: c.y + dir.y}
		if b.validCoord(nc) {
			coords = append(coords, nc)
		}
	}
	return coords
}

func (b board) startCoords(cf colorFlags) []coord {
	if cf.color == white {
		coords := make([]coord, b.width)
		for i := 0; i < b.width; i++ {
			coords[i] = coord{x: i, y: 0}
		}
		return coords
	} else {
		coords := make([]coord, b.height)
		for i := 0; i < b.height; i++ {
			coords[i] = coord{x: 0, y: i}
		}
		return coords
	}
}

func (b board) isTargetCoord(c coord, cf colorFlags) bool {
	if cf.color == white {
		return c.y == b.height-1
	} else {
		return c.x == b.width-1
	}
}

func (b board) evaluate(c coord, cf colorFlags) bool {
	stone, connected := b.at(c, cf)
	if stone && !connected {
		b.markConnected(c, cf)
		if b.isTargetCoord(c, cf) {
			return true
		}
		for _, nc := range b.neighbours(c) {
			if b.evaluate(nc, cf) {
				return true
			}
		}
	}
	return false
}

// Helper for debugging.
func (b board) dump() {
	for y := 0; y < b.height; y++ {
		spaces := strings.Repeat(" ", y)
		chars := make([]string, b.width)
		for x := 0; x < b.width; x++ {
			if b.fields[y][x]&white == white {
				if b.fields[y][x]&connectedWhite == connectedWhite {
					chars[x] = "O"
				} else {
					chars[x] = "o"
				}
			} else if b.fields[y][x]&black == black {
				if b.fields[y][x]&connectedBlack == connectedBlack {
					chars[x] = "X"
				} else {
					chars[x] = "x"
				}
			} else {
				chars[x] = "."
			}
		}
		fmt.Printf("%s%s\n", spaces, strings.Join(chars, " "))
	}
}

// ResultOf evaluates the board and return the winner, "black" or
// "white". If there's no winnner ResultOf returns "".
func ResultOf(lines []string) (string, error) {
	board, err := newBoard(lines)
	if err != nil {
		return "", err
	}
	for _, c := range board.startCoords(flagsBlack) {
		if board.evaluate(c, flagsBlack) {
			return "X", nil
		}
	}
	for _, c := range board.startCoords(flagsWhite) {
		if board.evaluate(c, flagsWhite) {
			return "O", nil
		}
	}
	return "", nil
}
<commit_msg>connect: Remove unnecessary else (golint)<commit_after>package connect

import (
	"errors"
	"fmt"
	"strings"
)

const testVersion = 3

const (
	white = 1 << iota
	black
	connectedWhite
	connectedBlack
)

type colorFlags struct {
	color     int8
	connected int8
}

var flagsBlack = colorFlags{
	color:     black,
	connected: connectedBlack,
}

var flagsWhite = colorFlags{
	color:     white,
	connected: connectedWhite,
}

type coord struct {
	x int
	y int
}

type board struct {
	height int
	width  int
	fields [][]int8
}

func newBoard(lines []string) (board, error) {
	if len(lines) < 1 {
		return board{}, errors.New("No lines given")
	}
	height := int(len(lines))
	if len(lines[0]) < 1 {
		return board{}, errors.New("First line is empty string")
	}
	width := int(len(lines[0]))
	// This trick for 2D arrays comes from Effective Go
	fields := make([][]int8, height)
	fieldsBacker := make([]int8, height*width)
	for i := range fields {
		fields[i], fieldsBacker = fieldsBacker[:width], fieldsBacker[width:]
	}
	for y, line := range lines {
		for x, c := range line {
			switch c {
			case 'X':
				fields[y][x] = black
			case 'O':
				fields[y][x] = white
			}
			// No need for default, zero value already means no stone
		}
	}
	board := board{
		height: height,
		width:  width,
		fields: fields,
	}
	return board, nil
}

// Whether there is a stone of the given color at the given location.
//
// Returns both whether there is a stone of the correct color and
// whether the connected flag was set for it.
func (b board) at(c coord, cf colorFlags) (bool, bool) {
	f := b.fields[c.y][c.x]
	return f&cf.color == cf.color,
		f&cf.connected == cf.connected
}

func (b board) markConnected(c coord, cf colorFlags) {
	b.fields[c.y][c.x] |= cf.connected
}

func (b board) validCoord(c coord) bool {
	return c.x >= 0 && c.x < b.width && c.y >= 0 && c.y < b.height
}

func (b board) neighbours(c coord) []coord {
	coords := make([]coord, 0, 6)
	dirs := []coord{{1, 0}, {-1, 0}, {0, 1}, {0, -1}, {-1, 1}, {1, -1}}
	for _, dir := range dirs {
		nc := coord{x: c.x + dir.x, y: c.y + dir.y}
		if b.validCoord(nc) {
			coords = append(coords, nc)
		}
	}
	return coords
}

func (b board) startCoords(cf colorFlags) []coord {
	if cf.color == white {
		coords := make([]coord, b.width)
		for i := 0; i < b.width; i++ {
			coords[i] = coord{x: i, y: 0}
		}
		return coords
	}
	coords := make([]coord, b.height)
	for i := 0; i < b.height; i++ {
		coords[i] = coord{x: 0, y: i}
	}
	return coords
}

func (b board) isTargetCoord(c coord, cf colorFlags) bool {
	if cf.color == white {
		return c.y == b.height-1
	}
	return c.x == b.width-1
}

func (b board) evaluate(c coord, cf colorFlags) bool {
	stone, connected := b.at(c, cf)
	if stone && !connected {
		b.markConnected(c, cf)
		if b.isTargetCoord(c, cf) {
			return true
		}
		for _, nc := range b.neighbours(c) {
			if b.evaluate(nc, cf) {
				return true
			}
		}
	}
	return false
}

// Helper for debugging.
func (b board) dump() {
	for y := 0; y < b.height; y++ {
		spaces := strings.Repeat(" ", y)
		chars := make([]string, b.width)
		for x := 0; x < b.width; x++ {
			if b.fields[y][x]&white == white {
				if b.fields[y][x]&connectedWhite == connectedWhite {
					chars[x] = "O"
				} else {
					chars[x] = "o"
				}
			} else if b.fields[y][x]&black == black {
				if b.fields[y][x]&connectedBlack == connectedBlack {
					chars[x] = "X"
				} else {
					chars[x] = "x"
				}
			} else {
				chars[x] = "."
			}
		}
		fmt.Printf("%s%s\n", spaces, strings.Join(chars, " "))
	}
}

// ResultOf evaluates the board and return the winner, "black" or
// "white". If there's no winnner ResultOf returns "".
func ResultOf(lines []string) (string, error) {
	board, err := newBoard(lines)
	if err != nil {
		return "", err
	}
	for _, c := range board.startCoords(flagsBlack) {
		if board.evaluate(c, flagsBlack) {
			return "X", nil
		}
	}
	for _, c := range board.startCoords(flagsWhite) {
		if board.evaluate(c, flagsWhite) {
			return "O", nil
		}
	}
	return "", nil
}
<|endoftext|>