<commit_before>// Copyright 2015-2018 the u-root Authors. All rights reserved
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package main

import (
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"runtime"
	"strings"

	"github.com/u-root/u-root/pkg/golang"
	"github.com/u-root/u-root/pkg/uroot"
	"github.com/u-root/u-root/pkg/uroot/builder"
	"github.com/u-root/u-root/pkg/uroot/initramfs"
)

// multiFlag is used for flags that support multiple invocations, e.g. -files
type multiFlag []string

func (m *multiFlag) String() string {
	return fmt.Sprint(*m)
}

func (m *multiFlag) Set(value string) error {
	*m = append(*m, value)
	return nil
}

// Flags for u-root builder.
var (
	build, format, tmpDir, base, outputPath *string
	uinitCmd, initCmd                       *string
	defaultShell                            *string
	useExistingInit                         *bool
	fourbins                                *bool
	noCommands                              *bool
	extraFiles                              multiFlag
)

func init() {
	fourbins = flag.Bool("fourbins", false, "build installcommand on boot, no ahead of time, so we have only four binares")
	build = flag.String("build", "bb", "u-root build format (e.g. bb or source).")
	format = flag.String("format", "cpio", "Archival format.")

	tmpDir = flag.String("tmpdir", "", "Temporary directory to put binaries in.")

	base = flag.String("base", "", "Base archive to add files to. By default, this is a couple of directories like /bin, /etc, etc. u-root has a default internally supplied set of files; use base=/dev/null if you don't want any base files.")
	useExistingInit = flag.Bool("useinit", false, "Use existing init from base archive (only if --base was specified).")
	outputPath = flag.String("o", "", "Path to output initramfs file.")

	initCmd = flag.String("initcmd", "init", "Symlink target for /init. Can be an absolute path or a u-root command name. Use initcmd=\"\" if you don't want the symlink.")
	uinitCmd = flag.String("uinitcmd", "", "Symlink target for /bin/uinit. Can be an absolute path or a u-root command name. Use uinitcmd=\"\" if you don't want the symlink.")
	defaultShell = flag.String("defaultsh", "elvish", "Default shell. Can be an absolute path or a u-root command name. Use defaultsh=\"\" if you don't want the symlink.")

	noCommands = flag.Bool("nocmd", false, "Build no Go commands; initramfs only")

	flag.Var(&extraFiles, "files", "Additional files, directories, and binaries (with their ldd dependencies) to add to archive. Can be speficified multiple times.")
}

func main() {
	flag.Parse()

	// Main is in a separate functions so defers run on return.
	if err := Main(); err != nil {
		log.Fatal(err)
	}
	log.Printf("Successfully wrote initramfs.")
}

var recommendedVersions = []string{
	"go1.12",
	"go1.13",
}

func isRecommendedVersion(v string) bool {
	for _, r := range recommendedVersions {
		if strings.HasPrefix(v, r) {
			return true
		}
	}
	return false
}

// Main is a separate function so defers are run on return, which they wouldn't
// on exit.
func Main() error {
	env := golang.Default()
	if *fourbins && env.GOROOT == "" {
		log.Fatalf("You have to set GOROOT for fourbins to work")
	}
	if env.CgoEnabled {
		log.Printf("Disabling CGO for u-root...")
		env.CgoEnabled = false
	}
	log.Printf("Build environment: %s", env)
	if env.GOOS != "linux" {
		log.Printf("GOOS is not linux. Did you mean to set GOOS=linux?")
	}

	v, err := env.Version()
	if err != nil {
		log.Printf("Could not get environment's Go version, using runtime's version: %v", err)
		v = runtime.Version()
	}
	if !isRecommendedVersion(v) {
		log.Printf(`WARNING: You are not using one of the recommended Go versions (have = %s, recommended = %v).
			Some packages may not compile.
			Go to https://golang.org/doc/install to find out how to install a newer version of Go,
			or use https://godoc.org/golang.org/dl/%s to install an additional version of Go.`,
			v, recommendedVersions, recommendedVersions[0])
	}

	archiver, err := initramfs.GetArchiver(*format)
	if err != nil {
		return err
	}

	logger := log.New(os.Stderr, "", log.LstdFlags)
	// Open the target initramfs file.
	w, err := archiver.OpenWriter(logger, *outputPath, env.GOOS, env.GOARCH)
	if err != nil {
		return err
	}

	var baseFile initramfs.Reader
	if *base != "" {
		bf, err := os.Open(*base)
		if err != nil {
			return err
		}
		defer bf.Close()
		baseFile = archiver.Reader(bf)
	} else {
		baseFile = uroot.DefaultRamfs.Reader()
	}

	tempDir := *tmpDir
	if tempDir == "" {
		var err error
		tempDir, err = ioutil.TempDir("", "u-root")
		if err != nil {
			return err
		}
		defer os.RemoveAll(tempDir)
	} else if _, err := os.Stat(tempDir); os.IsNotExist(err) {
		if err := os.MkdirAll(tempDir, 0755); err != nil {
			return fmt.Errorf("temporary directory %q did not exist; tried to mkdir but failed: %v", tempDir, err)
		}
	}

	var (
		c           []uroot.Commands
		initCommand = *initCmd
	)
	if !*noCommands {
		var b builder.Builder
		switch *build {
		case "bb":
			b = builder.BBBuilder{}
		case "binary":
			b = builder.BinaryBuilder{}
		case "source":
			b = builder.SourceBuilder{
				FourBins: *fourbins,
			}
		default:
			return fmt.Errorf("could not find builder %q", *build)
		}

		// Resolve globs into package imports.
		//
		// Currently allowed formats:
		//   Go package imports; e.g. github.com/u-root/u-root/cmds/ls (must be in $GOPATH)
		//   Paths to Go package directories; e.g. $GOPATH/src/github.com/u-root/u-root/cmds/*
		var pkgs []string
		for _, a := range flag.Args() {
			p, ok := templates[a]
			if !ok {
				pkgs = append(pkgs, a)
				continue
			}
			pkgs = append(pkgs, p...)
		}
		if len(pkgs) == 0 {
			pkgs = []string{"github.com/u-root/u-root/cmds/core/*"}
		}

		if *fourbins && *build == "source" {
			initCommand = "/go/bin/go"
		}

		// The command-line tool only allows specifying one build mode
		// right now.
		c = append(c, uroot.Commands{
			Builder:  b,
			Packages: pkgs,
		})
	}

	opts := uroot.Opts{
		Env:             env,
		Commands:        c,
		TempDir:         tempDir,
		ExtraFiles:      extraFiles,
		OutputFile:      w,
		BaseArchive:     baseFile,
		UseExistingInit: *useExistingInit,
		InitCmd:         initCommand,
		UinitCmd:        *uinitCmd,
		DefaultShell:    *defaultShell,
	}
	return uroot.CreateInitramfs(logger, opts)
}
<commit_msg>Add go1.14 to recommended versions<commit_after>// Copyright 2015-2018 the u-root Authors. All rights reserved
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package main

import (
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"runtime"
	"strings"

	"github.com/u-root/u-root/pkg/golang"
	"github.com/u-root/u-root/pkg/uroot"
	"github.com/u-root/u-root/pkg/uroot/builder"
	"github.com/u-root/u-root/pkg/uroot/initramfs"
)

// multiFlag is used for flags that support multiple invocations, e.g. -files
type multiFlag []string

func (m *multiFlag) String() string {
	return fmt.Sprint(*m)
}

func (m *multiFlag) Set(value string) error {
	*m = append(*m, value)
	return nil
}

// Flags for u-root builder.
var (
	build, format, tmpDir, base, outputPath *string
	uinitCmd, initCmd                       *string
	defaultShell                            *string
	useExistingInit                         *bool
	fourbins                                *bool
	noCommands                              *bool
	extraFiles                              multiFlag
)

func init() {
	fourbins = flag.Bool("fourbins", false, "build installcommand on boot, no ahead of time, so we have only four binares")
	build = flag.String("build", "bb", "u-root build format (e.g. bb or source).")
	format = flag.String("format", "cpio", "Archival format.")

	tmpDir = flag.String("tmpdir", "", "Temporary directory to put binaries in.")

	base = flag.String("base", "", "Base archive to add files to. By default, this is a couple of directories like /bin, /etc, etc. u-root has a default internally supplied set of files; use base=/dev/null if you don't want any base files.")
	useExistingInit = flag.Bool("useinit", false, "Use existing init from base archive (only if --base was specified).")
	outputPath = flag.String("o", "", "Path to output initramfs file.")

	initCmd = flag.String("initcmd", "init", "Symlink target for /init. Can be an absolute path or a u-root command name. Use initcmd=\"\" if you don't want the symlink.")
	uinitCmd = flag.String("uinitcmd", "", "Symlink target for /bin/uinit. Can be an absolute path or a u-root command name. Use uinitcmd=\"\" if you don't want the symlink.")
	defaultShell = flag.String("defaultsh", "elvish", "Default shell. Can be an absolute path or a u-root command name. Use defaultsh=\"\" if you don't want the symlink.")

	noCommands = flag.Bool("nocmd", false, "Build no Go commands; initramfs only")

	flag.Var(&extraFiles, "files", "Additional files, directories, and binaries (with their ldd dependencies) to add to archive. Can be speficified multiple times.")
}

func main() {
	flag.Parse()

	// Main is in a separate functions so defers run on return.
	if err := Main(); err != nil {
		log.Fatal(err)
	}
	log.Printf("Successfully wrote initramfs.")
}

var recommendedVersions = []string{
	"go1.12",
	"go1.13",
	"go1.14",
}

func isRecommendedVersion(v string) bool {
	for _, r := range recommendedVersions {
		if strings.HasPrefix(v, r) {
			return true
		}
	}
	return false
}

// Main is a separate function so defers are run on return, which they wouldn't
// on exit.
func Main() error {
	env := golang.Default()
	if *fourbins && env.GOROOT == "" {
		log.Fatalf("You have to set GOROOT for fourbins to work")
	}
	if env.CgoEnabled {
		log.Printf("Disabling CGO for u-root...")
		env.CgoEnabled = false
	}
	log.Printf("Build environment: %s", env)
	if env.GOOS != "linux" {
		log.Printf("GOOS is not linux. Did you mean to set GOOS=linux?")
	}

	v, err := env.Version()
	if err != nil {
		log.Printf("Could not get environment's Go version, using runtime's version: %v", err)
		v = runtime.Version()
	}
	if !isRecommendedVersion(v) {
		log.Printf(`WARNING: You are not using one of the recommended Go versions (have = %s, recommended = %v).
			Some packages may not compile.
			Go to https://golang.org/doc/install to find out how to install a newer version of Go,
			or use https://godoc.org/golang.org/dl/%s to install an additional version of Go.`,
			v, recommendedVersions, recommendedVersions[0])
	}

	archiver, err := initramfs.GetArchiver(*format)
	if err != nil {
		return err
	}

	logger := log.New(os.Stderr, "", log.LstdFlags)
	// Open the target initramfs file.
	w, err := archiver.OpenWriter(logger, *outputPath, env.GOOS, env.GOARCH)
	if err != nil {
		return err
	}

	var baseFile initramfs.Reader
	if *base != "" {
		bf, err := os.Open(*base)
		if err != nil {
			return err
		}
		defer bf.Close()
		baseFile = archiver.Reader(bf)
	} else {
		baseFile = uroot.DefaultRamfs.Reader()
	}

	tempDir := *tmpDir
	if tempDir == "" {
		var err error
		tempDir, err = ioutil.TempDir("", "u-root")
		if err != nil {
			return err
		}
		defer os.RemoveAll(tempDir)
	} else if _, err := os.Stat(tempDir); os.IsNotExist(err) {
		if err := os.MkdirAll(tempDir, 0755); err != nil {
			return fmt.Errorf("temporary directory %q did not exist; tried to mkdir but failed: %v", tempDir, err)
		}
	}

	var (
		c           []uroot.Commands
		initCommand = *initCmd
	)
	if !*noCommands {
		var b builder.Builder
		switch *build {
		case "bb":
			b = builder.BBBuilder{}
		case "binary":
			b = builder.BinaryBuilder{}
		case "source":
			b = builder.SourceBuilder{
				FourBins: *fourbins,
			}
		default:
			return fmt.Errorf("could not find builder %q", *build)
		}

		// Resolve globs into package imports.
		//
		// Currently allowed formats:
		//   Go package imports; e.g. github.com/u-root/u-root/cmds/ls (must be in $GOPATH)
		//   Paths to Go package directories; e.g. $GOPATH/src/github.com/u-root/u-root/cmds/*
		var pkgs []string
		for _, a := range flag.Args() {
			p, ok := templates[a]
			if !ok {
				pkgs = append(pkgs, a)
				continue
			}
			pkgs = append(pkgs, p...)
		}
		if len(pkgs) == 0 {
			pkgs = []string{"github.com/u-root/u-root/cmds/core/*"}
		}

		if *fourbins && *build == "source" {
			initCommand = "/go/bin/go"
		}

		// The command-line tool only allows specifying one build mode
		// right now.
		c = append(c, uroot.Commands{
			Builder:  b,
			Packages: pkgs,
		})
	}

	opts := uroot.Opts{
		Env:             env,
		Commands:        c,
		TempDir:         tempDir,
		ExtraFiles:      extraFiles,
		OutputFile:      w,
		BaseArchive:     baseFile,
		UseExistingInit: *useExistingInit,
		InitCmd:         initCommand,
		UinitCmd:        *uinitCmd,
		DefaultShell:    *defaultShell,
	}
	return uroot.CreateInitramfs(logger, opts)
}
<|endoftext|>


<commit_before>package main

import (
	"compress/gzip"
	"crypto/sha1"
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"os"
	"os/exec"
	"path/filepath"
	"runtime"
	"time"

	"github.com/nightlyone/lockfile"
)

var updateTopic = &Topic{
	Name:        "update",
	Description: "update heroku-cli",
}

var updateCmd = &Command{
	Topic:       "update",
	Description: "updates heroku-cli",
	Args:        []Arg{{Name: "channel", Optional: true}},
	Run: func(ctx *Context) {
		channel := ctx.Args["channel"]
		if channel == "" {
			channel = "master"
		}
		Errf("updating plugins... ")
		if err := node.UpdatePackages(); err != nil {
			panic(err)
		}
		Errln("done")
		manifest := getUpdateManifest(channel)
		build := manifest.Builds[runtime.GOOS][runtime.GOARCH]
		Errf("updating to %s (%s)... ", manifest.Version, manifest.Channel)
		update(build.URL, build.Sha1)
		Errln("done")
	},
}

var binPath = filepath.Join(AppDir, "heroku-cli")
var updateLockPath = filepath.Join(AppDir, "updating.lock")

func init() {
	if runtime.GOOS == "windows" {
		binPath = binPath + ".exe"
	}
}

// UpdateIfNeeded checks for and performs an autoupdate if there is a new version out.
func UpdateIfNeeded() {
	if !updateNeeded() {
		return
	}
	node.UpdatePackages()
	manifest := getUpdateManifest(Channel)
	if manifest.Version == Version {
		// Set timestamp of bin so we don't update again
		os.Chtimes(binPath, time.Now(), time.Now())
		return
	}
	if !updatable() {
		Errf("Out of date: You are running %s but %s is out.\n", Version, manifest.Version)
		return
	}
	// Leave out updating text until heroku-cli is used in place of ruby cli
	// So it doesn't confuse users with 2 different version numbers
	//Errf("Updating to %s... ", manifest.Version)
	build := manifest.Builds[runtime.GOOS][runtime.GOARCH]
	update(build.URL, build.Sha1)
	//Errln("done")
	execBin()
	os.Exit(0)
}

func updateNeeded() bool {
	if Version == "dev" {
		return false
	}
	f, err := os.Stat(binPath)
	if err != nil {
		Errln("WARNING: cannot autoupdate. Try running `heroku update` to manually trigger an update.")
		return false
	}
	return f.ModTime().Add(60 * time.Minute).Before(time.Now())
}

type manifest struct {
	Channel, Version string
	Builds           map[string]map[string]struct {
		URL, Sha1 string
	}
}

func getUpdateManifest(channel string) manifest {
	res, err := http.Get("http://d1gvo455cekpjp.cloudfront.net/" + channel + "/manifest.json")
	if err != nil {
		panic(err)
	}
	var m manifest
	json.NewDecoder(res.Body).Decode(&m)
	return m
}

func updatable() bool {
	path, err := filepath.Abs(os.Args[0])
	if err != nil {
		Errln(err)
	}
	return path == binPath
}

func update(url, sha1 string) {
	lock, err := lockfile.New(updateLockPath)
	if err != nil {
		Errln("Cannot initialize update lockfile.")
		panic(err)
	}
	err = lock.TryLock()
	if err != nil {
		panic(err)
	}
	defer lock.Unlock()
	tmp, err := downloadBin(url)
	if err != nil {
		panic(err)
	}
	if fileSha1(tmp) != sha1 {
		panic("SHA mismatch")
	}
	os.Remove(binPath) // on windows you can't rename on top of an existing file
	if err := os.Rename(tmp, binPath); err != nil {
		panic(err)
	}
}

func downloadBin(url string) (string, error) {
	out, err := os.OpenFile(binPath+"~", os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0755)
	if err != nil {
		return "", err
	}
	defer out.Close()
	client := &http.Client{}
	req, err := http.NewRequest("GET", url+".gz", nil)
	if err != nil {
		return "", err
	}
	req.Header.Add("Accept-Encoding", "gzip")
	resp, err := client.Do(req)
	if err != nil {
		return "", err
	}
	defer resp.Body.Close()
	uncompressed, err := gzip.NewReader(resp.Body)
	if err != nil {
		return "", err
	}
	_, err = io.Copy(out, uncompressed)
	return out.Name(), err
}

func fileSha1(path string) string {
	data, err := ioutil.ReadFile(path)
	if err != nil {
		panic(err)
	}
	return fmt.Sprintf("%x", sha1.Sum(data))
}

func execBin() {
	cmd := exec.Command(binPath, os.Args[1:]...)
	cmd.Stdin = os.Stdin
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	cmd.Run()
}
<commit_msg>show message if cant delete heroku-cli bin<commit_after>package main

import (
	"compress/gzip"
	"crypto/sha1"
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"os"
	"os/exec"
	"path/filepath"
	"runtime"
	"time"

	"github.com/nightlyone/lockfile"
)

var updateTopic = &Topic{
	Name:        "update",
	Description: "update heroku-cli",
}

var updateCmd = &Command{
	Topic:       "update",
	Description: "updates heroku-cli",
	Args:        []Arg{{Name: "channel", Optional: true}},
	Run: func(ctx *Context) {
		channel := ctx.Args["channel"]
		if channel == "" {
			channel = "master"
		}
		Errf("updating plugins... ")
		if err := node.UpdatePackages(); err != nil {
			panic(err)
		}
		Errln("done")
		manifest := getUpdateManifest(channel)
		build := manifest.Builds[runtime.GOOS][runtime.GOARCH]
		Errf("updating to %s (%s)... ", manifest.Version, manifest.Channel)
		update(build.URL, build.Sha1)
		Errln("done")
	},
}

var binPath = filepath.Join(AppDir, "heroku-cli")
var updateLockPath = filepath.Join(AppDir, "updating.lock")

func init() {
	if runtime.GOOS == "windows" {
		binPath = binPath + ".exe"
	}
}

// UpdateIfNeeded checks for and performs an autoupdate if there is a new version out.
func UpdateIfNeeded() {
	if !updateNeeded() {
		return
	}
	node.UpdatePackages()
	manifest := getUpdateManifest(Channel)
	if manifest.Version == Version {
		// Set timestamp of bin so we don't update again
		os.Chtimes(binPath, time.Now(), time.Now())
		return
	}
	if !updatable() {
		Errf("Out of date: You are running %s but %s is out.\n", Version, manifest.Version)
		return
	}
	// Leave out updating text until heroku-cli is used in place of ruby cli
	// So it doesn't confuse users with 2 different version numbers
	//Errf("Updating to %s... ", manifest.Version)
	build := manifest.Builds[runtime.GOOS][runtime.GOARCH]
	update(build.URL, build.Sha1)
	//Errln("done")
	execBin()
	os.Exit(0)
}

func updateNeeded() bool {
	if Version == "dev" {
		return false
	}
	f, err := os.Stat(binPath)
	if err != nil {
		Errln("WARNING: cannot autoupdate. Try running `heroku update` to manually trigger an update.")
		return false
	}
	return f.ModTime().Add(60 * time.Minute).Before(time.Now())
}

type manifest struct {
	Channel, Version string
	Builds           map[string]map[string]struct {
		URL, Sha1 string
	}
}

func getUpdateManifest(channel string) manifest {
	res, err := http.Get("http://d1gvo455cekpjp.cloudfront.net/" + channel + "/manifest.json")
	if err != nil {
		panic(err)
	}
	var m manifest
	json.NewDecoder(res.Body).Decode(&m)
	return m
}

func updatable() bool {
	path, err := filepath.Abs(os.Args[0])
	if err != nil {
		Errln(err)
	}
	return path == binPath
}

func update(url, sha1 string) {
	lock, err := lockfile.New(updateLockPath)
	if err != nil {
		Errln("Cannot initialize update lockfile.")
		panic(err)
	}
	err = lock.TryLock()
	if err != nil {
		panic(err)
	}
	defer lock.Unlock()
	tmp, err := downloadBin(url)
	if err != nil {
		panic(err)
	}
	if fileSha1(tmp) != sha1 {
		panic("SHA mismatch")
	}
	// on windows you can't rename on top of an existing file
	if err := os.Remove(binPath); err != nil {
		Errln(err)
	}
	if err := os.Rename(tmp, binPath); err != nil {
		panic(err)
	}
}

func downloadBin(url string) (string, error) {
	out, err := os.OpenFile(binPath+"~", os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0755)
	if err != nil {
		return "", err
	}
	defer out.Close()
	client := &http.Client{}
	req, err := http.NewRequest("GET", url+".gz", nil)
	if err != nil {
		return "", err
	}
	req.Header.Add("Accept-Encoding", "gzip")
	resp, err := client.Do(req)
	if err != nil {
		return "", err
	}
	defer resp.Body.Close()
	uncompressed, err := gzip.NewReader(resp.Body)
	if err != nil {
		return "", err
	}
	_, err = io.Copy(out, uncompressed)
	return out.Name(), err
}

func fileSha1(path string) string {
	data, err := ioutil.ReadFile(path)
	if err != nil {
		panic(err)
	}
	return fmt.Sprintf("%x", sha1.Sum(data))
}

func execBin() {
	cmd := exec.Command(binPath, os.Args[1:]...)
	cmd.Stdin = os.Stdin
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	cmd.Run()
}
<|endoftext|>


<commit_before>// +build linux

package main

import (
	"encoding/json"
	"errors"
	"fmt"
	"os"
	"strconv"

	"github.com/opencontainers/runc/libcontainer/cgroups"
	"github.com/sirupsen/logrus"

	"github.com/docker/go-units"
	"github.com/opencontainers/runc/libcontainer/configs"
	"github.com/opencontainers/runc/libcontainer/intelrdt"
	"github.com/opencontainers/runtime-spec/specs-go"
	"github.com/urfave/cli"
)

func i64Ptr(i int64) *int64   { return &i }
func u64Ptr(i uint64) *uint64 { return &i }
func u16Ptr(i uint16) *uint16 { return &i }

var updateCommand = cli.Command{
	Name:      "update",
	Usage:     "update container resource constraints",
	ArgsUsage: `<container-id>`,
	Flags: []cli.Flag{
		cli.StringFlag{
			Name:  "resources, r",
			Value: "",
			Usage: `path to the file containing the resources to update or '-' to read from the standard input

The accepted format is as follow (unchanged values can be omitted):

{
  "memory": {
    "limit": 0,
    "reservation": 0,
    "swap": 0
  },
  "cpu": {
    "shares": 0,
    "quota": 0,
    "period": 0,
    "realtimeRuntime": 0,
    "realtimePeriod": 0,
    "cpus": "",
    "mems": ""
  },
  "blockIO": {
    "weight": 0
  }
}

Note: if data is to be read from a file or the standard input, all
other options are ignored.
`,
		},

		cli.IntFlag{
			Name:  "blkio-weight",
			Usage: "Specifies per cgroup weight, range is from 10 to 1000",
		},
		cli.StringFlag{
			Name:  "cpu-period",
			Usage: "CPU CFS period to be used for hardcapping (in usecs). 0 to use system default",
		},
		cli.StringFlag{
			Name:  "cpu-quota",
			Usage: "CPU CFS hardcap limit (in usecs). Allowed cpu time in a given period",
		},
		cli.StringFlag{
			Name:  "cpu-share",
			Usage: "CPU shares (relative weight vs. other containers)",
		},
		cli.StringFlag{
			Name:  "cpu-rt-period",
			Usage: "CPU realtime period to be used for hardcapping (in usecs). 0 to use system default",
		},
		cli.StringFlag{
			Name:  "cpu-rt-runtime",
			Usage: "CPU realtime hardcap limit (in usecs). Allowed cpu time in a given period",
		},
		cli.StringFlag{
			Name:  "cpuset-cpus",
			Usage: "CPU(s) to use",
		},
		cli.StringFlag{
			Name:  "cpuset-mems",
			Usage: "Memory node(s) to use",
		},
		cli.StringFlag{
			Name:  "kernel-memory",
			Usage: "(obsoleted; do not use)",
		},
		cli.StringFlag{
			Name:  "kernel-memory-tcp",
			Usage: "(obsoleted; do not use)",
		},
		cli.StringFlag{
			Name:  "memory",
			Usage: "Memory limit (in bytes)",
		},
		cli.StringFlag{
			Name:  "memory-reservation",
			Usage: "Memory reservation or soft_limit (in bytes)",
		},
		cli.StringFlag{
			Name:  "memory-swap",
			Usage: "Total memory usage (memory + swap); set '-1' to enable unlimited swap",
		},
		cli.IntFlag{
			Name:  "pids-limit",
			Usage: "Maximum number of pids allowed in the container",
		},
		cli.StringFlag{
			Name:  "l3-cache-schema",
			Usage: "The string of Intel RDT/CAT L3 cache schema",
		},
		cli.StringFlag{
			Name:  "mem-bw-schema",
			Usage: "The string of Intel RDT/MBA memory bandwidth schema",
		},
	},
	Action: func(context *cli.Context) error {
		if err := checkArgs(context, 1, exactArgs); err != nil {
			return err
		}
		container, err := getContainer(context)
		if err != nil {
			return err
		}

		r := specs.LinuxResources{
			Memory: &specs.LinuxMemory{
				Limit:       i64Ptr(0),
				Reservation: i64Ptr(0),
				Swap:        i64Ptr(0),
				Kernel:      i64Ptr(0),
				KernelTCP:   i64Ptr(0),
			},
			CPU: &specs.LinuxCPU{
				Shares:          u64Ptr(0),
				Quota:           i64Ptr(0),
				Period:          u64Ptr(0),
				RealtimeRuntime: i64Ptr(0),
				RealtimePeriod:  u64Ptr(0),
				Cpus:            "",
				Mems:            "",
			},
			BlockIO: &specs.LinuxBlockIO{
				Weight: u16Ptr(0),
			},
			Pids: &specs.LinuxPids{
				Limit: 0,
			},
		}

		config := container.Config()

		if in := context.String("resources"); in != "" {
			var (
				f   *os.File
				err error
			)
			switch in {
			case "-":
				f = os.Stdin
			default:
				f, err = os.Open(in)
				if err != nil {
					return err
				}
			}
			err = json.NewDecoder(f).Decode(&r)
			if err != nil {
				return err
			}
		} else {
			if val := context.Int("blkio-weight"); val != 0 {
				r.BlockIO.Weight = u16Ptr(uint16(val))
			}
			if val := context.String("cpuset-cpus"); val != "" {
				r.CPU.Cpus = val
			}
			if val := context.String("cpuset-mems"); val != "" {
				r.CPU.Mems = val
			}

			for _, pair := range []struct {
				opt  string
				dest *uint64
			}{

				{"cpu-period", r.CPU.Period},
				{"cpu-rt-period", r.CPU.RealtimePeriod},
				{"cpu-share", r.CPU.Shares},
			} {
				if val := context.String(pair.opt); val != "" {
					var err error
					*pair.dest, err = strconv.ParseUint(val, 10, 64)
					if err != nil {
						return fmt.Errorf("invalid value for %s: %s", pair.opt, err)
					}
				}
			}
			for _, pair := range []struct {
				opt  string
				dest *int64
			}{

				{"cpu-quota", r.CPU.Quota},
				{"cpu-rt-runtime", r.CPU.RealtimeRuntime},
			} {
				if val := context.String(pair.opt); val != "" {
					var err error
					*pair.dest, err = strconv.ParseInt(val, 10, 64)
					if err != nil {
						return fmt.Errorf("invalid value for %s: %s", pair.opt, err)
					}
				}
			}
			for _, pair := range []struct {
				opt  string
				dest *int64
			}{
				{"memory", r.Memory.Limit},
				{"memory-swap", r.Memory.Swap},
				{"kernel-memory", r.Memory.Kernel},
				{"kernel-memory-tcp", r.Memory.KernelTCP},
				{"memory-reservation", r.Memory.Reservation},
			} {
				if val := context.String(pair.opt); val != "" {
					var v int64

					if val != "-1" {
						v, err = units.RAMInBytes(val)
						if err != nil {
							return fmt.Errorf("invalid value for %s: %s", pair.opt, err)
						}
					} else {
						v = -1
					}
					*pair.dest = v
				}
			}

			r.Pids.Limit = int64(context.Int("pids-limit"))
		}

		if *r.Memory.Kernel != 0 || *r.Memory.KernelTCP != 0 {
			logrus.Warn("Kernel memory settings are ignored and will be removed")
		}

		// Update the values
		config.Cgroups.Resources.BlkioWeight = *r.BlockIO.Weight

		// Seting CPU quota and period independently does not make much sense,
		// but historically runc allowed it and this needs to be supported
		// to not break compatibility.
		//
		// For systemd cgroup drivers to set CPU quota/period correctly,
		// it needs to know both values. For fs2 cgroup driver to be compatible
		// with the fs driver, it also needs to know both values.
		//
		// Here in update, previously set values are available from config.
		// If only one of {quota,period} is set and the other is not, leave
		// the unset parameter at the old value (don't overwrite config).
		p, q := *r.CPU.Period, *r.CPU.Quota
		if (p == 0 && q == 0) || (p != 0 && q != 0) {
			// both values are either set or unset (0)
			config.Cgroups.Resources.CpuPeriod = p
			config.Cgroups.Resources.CpuQuota = q
		} else {
			// one is set and the other is not
			if p != 0 {
				// set new period, leave quota at old value
				config.Cgroups.Resources.CpuPeriod = p
			} else if q != 0 {
				// set new quota, leave period at old value
				config.Cgroups.Resources.CpuQuota = q
			}
		}

		config.Cgroups.Resources.CpuShares = *r.CPU.Shares
		// CpuWeight is used for cgroupv2 and should be converted
		config.Cgroups.Resources.CpuWeight = cgroups.ConvertCPUSharesToCgroupV2Value(*r.CPU.Shares)
		config.Cgroups.Resources.CpuRtPeriod = *r.CPU.RealtimePeriod
		config.Cgroups.Resources.CpuRtRuntime = *r.CPU.RealtimeRuntime
		config.Cgroups.Resources.CpusetCpus = r.CPU.Cpus
		config.Cgroups.Resources.CpusetMems = r.CPU.Mems
		config.Cgroups.Resources.Memory = *r.Memory.Limit
		config.Cgroups.Resources.MemoryReservation = *r.Memory.Reservation
		config.Cgroups.Resources.MemorySwap = *r.Memory.Swap
		config.Cgroups.Resources.PidsLimit = r.Pids.Limit
		config.Cgroups.Resources.Unified = r.Unified

		// Update Intel RDT
		l3CacheSchema := context.String("l3-cache-schema")
		memBwSchema := context.String("mem-bw-schema")
		if l3CacheSchema != "" && !intelrdt.IsCATEnabled() {
			return errors.New("Intel RDT/CAT: l3 cache schema is not enabled")
		}

		if memBwSchema != "" && !intelrdt.IsMBAEnabled() {
			return errors.New("Intel RDT/MBA: memory bandwidth schema is not enabled")
		}

		if l3CacheSchema != "" || memBwSchema != "" {
			// If intelRdt is not specified in original configuration, we just don't
			// Apply() to create intelRdt group or attach tasks for this container.
			// In update command, we could re-enable through IntelRdtManager.Apply()
			// and then update intelrdt constraint.
			if config.IntelRdt == nil {
				state, err := container.State()
				if err != nil {
					return err
				}
				config.IntelRdt = &configs.IntelRdt{}
				intelRdtManager := intelrdt.NewManager(&config, container.ID(), state.IntelRdtPath)
				if err := intelRdtManager.Apply(state.InitProcessPid); err != nil {
					return err
				}
			}
			config.IntelRdt.L3CacheSchema = l3CacheSchema
			config.IntelRdt.MemBwSchema = memBwSchema
		}

		// XXX(kolyshkin@): currently "runc update" is unable to change
		// device configuration, so add this to skip device update.
		// This helps in case an extra plugin (nvidia GPU) applies some
		// configuration on top of what runc does.
		// Note this field is not saved into container's state.json.
		config.Cgroups.SkipDevices = true

		return container.Set(config)
	},
}
<commit_msg>runc update: hide --kernel* options<commit_after>// +build linux

package main

import (
	"encoding/json"
	"errors"
	"fmt"
	"os"
	"strconv"

	"github.com/opencontainers/runc/libcontainer/cgroups"
	"github.com/sirupsen/logrus"

	"github.com/docker/go-units"
	"github.com/opencontainers/runc/libcontainer/configs"
	"github.com/opencontainers/runc/libcontainer/intelrdt"
	"github.com/opencontainers/runtime-spec/specs-go"
	"github.com/urfave/cli"
)

func i64Ptr(i int64) *int64   { return &i }
func u64Ptr(i uint64) *uint64 { return &i }
func u16Ptr(i uint16) *uint16 { return &i }

var updateCommand = cli.Command{
	Name:      "update",
	Usage:     "update container resource constraints",
	ArgsUsage: `<container-id>`,
	Flags: []cli.Flag{
		cli.StringFlag{
			Name:  "resources, r",
			Value: "",
			Usage: `path to the file containing the resources to update or '-' to read from the standard input

The accepted format is as follow (unchanged values can be omitted):

{
  "memory": {
    "limit": 0,
    "reservation": 0,
    "swap": 0
  },
  "cpu": {
    "shares": 0,
    "quota": 0,
    "period": 0,
    "realtimeRuntime": 0,
    "realtimePeriod": 0,
    "cpus": "",
    "mems": ""
  },
  "blockIO": {
    "weight": 0
  }
}

Note: if data is to be read from a file or the standard input, all
other options are ignored.
`,
		},

		cli.IntFlag{
			Name:  "blkio-weight",
			Usage: "Specifies per cgroup weight, range is from 10 to 1000",
		},
		cli.StringFlag{
			Name:  "cpu-period",
			Usage: "CPU CFS period to be used for hardcapping (in usecs). 0 to use system default",
		},
		cli.StringFlag{
			Name:  "cpu-quota",
			Usage: "CPU CFS hardcap limit (in usecs). Allowed cpu time in a given period",
		},
		cli.StringFlag{
			Name:  "cpu-share",
			Usage: "CPU shares (relative weight vs. other containers)",
		},
		cli.StringFlag{
			Name:  "cpu-rt-period",
			Usage: "CPU realtime period to be used for hardcapping (in usecs). 0 to use system default",
		},
		cli.StringFlag{
			Name:  "cpu-rt-runtime",
			Usage: "CPU realtime hardcap limit (in usecs). Allowed cpu time in a given period",
		},
		cli.StringFlag{
			Name:  "cpuset-cpus",
			Usage: "CPU(s) to use",
		},
		cli.StringFlag{
			Name:  "cpuset-mems",
			Usage: "Memory node(s) to use",
		},
		cli.StringFlag{
			Name:   "kernel-memory",
			Usage:  "(obsoleted; do not use)",
			Hidden: true,
		},
		cli.StringFlag{
			Name:   "kernel-memory-tcp",
			Usage:  "(obsoleted; do not use)",
			Hidden: true,
		},
		cli.StringFlag{
			Name:  "memory",
			Usage: "Memory limit (in bytes)",
		},
		cli.StringFlag{
			Name:  "memory-reservation",
			Usage: "Memory reservation or soft_limit (in bytes)",
		},
		cli.StringFlag{
			Name:  "memory-swap",
			Usage: "Total memory usage (memory + swap); set '-1' to enable unlimited swap",
		},
		cli.IntFlag{
			Name:  "pids-limit",
			Usage: "Maximum number of pids allowed in the container",
		},
		cli.StringFlag{
			Name:  "l3-cache-schema",
			Usage: "The string of Intel RDT/CAT L3 cache schema",
		},
		cli.StringFlag{
			Name:  "mem-bw-schema",
			Usage: "The string of Intel RDT/MBA memory bandwidth schema",
		},
	},
	Action: func(context *cli.Context) error {
		if err := checkArgs(context, 1, exactArgs); err != nil {
			return err
		}
		container, err := getContainer(context)
		if err != nil {
			return err
		}

		r := specs.LinuxResources{
			Memory: &specs.LinuxMemory{
				Limit:       i64Ptr(0),
				Reservation: i64Ptr(0),
				Swap:        i64Ptr(0),
				Kernel:      i64Ptr(0),
				KernelTCP:   i64Ptr(0),
			},
			CPU: &specs.LinuxCPU{
				Shares:          u64Ptr(0),
				Quota:           i64Ptr(0),
				Period:          u64Ptr(0),
				RealtimeRuntime: i64Ptr(0),
				RealtimePeriod:  u64Ptr(0),
				Cpus:            "",
				Mems:            "",
			},
			BlockIO: &specs.LinuxBlockIO{
				Weight: u16Ptr(0),
			},
			Pids: &specs.LinuxPids{
				Limit: 0,
			},
		}

		config := container.Config()

		if in := context.String("resources"); in != "" {
			var (
				f   *os.File
				err error
			)
			switch in {
			case "-":
				f = os.Stdin
			default:
				f, err = os.Open(in)
				if err != nil {
					return err
				}
			}
			err = json.NewDecoder(f).Decode(&r)
			if err != nil {
				return err
			}
		} else {
			if val := context.Int("blkio-weight"); val != 0 {
				r.BlockIO.Weight = u16Ptr(uint16(val))
			}
			if val := context.String("cpuset-cpus"); val != "" {
				r.CPU.Cpus = val
			}
			if val := context.String("cpuset-mems"); val != "" {
				r.CPU.Mems = val
			}

			for _, pair := range []struct {
				opt  string
				dest *uint64
			}{

				{"cpu-period", r.CPU.Period},
				{"cpu-rt-period", r.CPU.RealtimePeriod},
				{"cpu-share", r.CPU.Shares},
			} {
				if val := context.String(pair.opt); val != "" {
					var err error
					*pair.dest, err = strconv.ParseUint(val, 10, 64)
					if err != nil {
						return fmt.Errorf("invalid value for %s: %s", pair.opt, err)
					}
				}
			}
			for _, pair := range []struct {
				opt  string
				dest *int64
			}{

				{"cpu-quota", r.CPU.Quota},
				{"cpu-rt-runtime", r.CPU.RealtimeRuntime},
			} {
				if val := context.String(pair.opt); val != "" {
					var err error
					*pair.dest, err = strconv.ParseInt(val, 10, 64)
					if err != nil {
						return fmt.Errorf("invalid value for %s: %s", pair.opt, err)
					}
				}
			}
			for _, pair := range []struct {
				opt  string
				dest *int64
			}{
				{"memory", r.Memory.Limit},
				{"memory-swap", r.Memory.Swap},
				{"kernel-memory", r.Memory.Kernel},
				{"kernel-memory-tcp", r.Memory.KernelTCP},
				{"memory-reservation", r.Memory.Reservation},
			} {
				if val := context.String(pair.opt); val != "" {
					var v int64

					if val != "-1" {
						v, err = units.RAMInBytes(val)
						if err != nil {
							return fmt.Errorf("invalid value for %s: %s", pair.opt, err)
						}
					} else {
						v = -1
					}
					*pair.dest = v
				}
			}

			r.Pids.Limit = int64(context.Int("pids-limit"))
		}

		if *r.Memory.Kernel != 0 || *r.Memory.KernelTCP != 0 {
			logrus.Warn("Kernel memory settings are ignored and will be removed")
		}

		// Update the values
		config.Cgroups.Resources.BlkioWeight = *r.BlockIO.Weight

		// Seting CPU quota and period independently does not make much sense,
		// but historically runc allowed it and this needs to be supported
		// to not break compatibility.
		//
		// For systemd cgroup drivers to set CPU quota/period correctly,
		// it needs to know both values. For fs2 cgroup driver to be compatible
		// with the fs driver, it also needs to know both values.
		//
		// Here in update, previously set values are available from config.
		// If only one of {quota,period} is set and the other is not, leave
		// the unset parameter at the old value (don't overwrite config).
		p, q := *r.CPU.Period, *r.CPU.Quota
		if (p == 0 && q == 0) || (p != 0 && q != 0) {
			// both values are either set or unset (0)
			config.Cgroups.Resources.CpuPeriod = p
			config.Cgroups.Resources.CpuQuota = q
		} else {
			// one is set and the other is not
			if p != 0 {
				// set new period, leave quota at old value
				config.Cgroups.Resources.CpuPeriod = p
			} else if q != 0 {
				// set new quota, leave period at old value
				config.Cgroups.Resources.CpuQuota = q
			}
		}

		config.Cgroups.Resources.CpuShares = *r.CPU.Shares
		// CpuWeight is used for cgroupv2 and should be converted
		config.Cgroups.Resources.CpuWeight = cgroups.ConvertCPUSharesToCgroupV2Value(*r.CPU.Shares)
		config.Cgroups.Resources.CpuRtPeriod = *r.CPU.RealtimePeriod
		config.Cgroups.Resources.CpuRtRuntime = *r.CPU.RealtimeRuntime
		config.Cgroups.Resources.CpusetCpus = r.CPU.Cpus
		config.Cgroups.Resources.CpusetMems = r.CPU.Mems
		config.Cgroups.Resources.Memory = *r.Memory.Limit
		config.Cgroups.Resources.MemoryReservation = *r.Memory.Reservation
		config.Cgroups.Resources.MemorySwap = *r.Memory.Swap
		config.Cgroups.Resources.PidsLimit = r.Pids.Limit
		config.Cgroups.Resources.Unified = r.Unified

		// Update Intel RDT
		l3CacheSchema := context.String("l3-cache-schema")
		memBwSchema := context.String("mem-bw-schema")
		if l3CacheSchema != "" && !intelrdt.IsCATEnabled() {
			return errors.New("Intel RDT/CAT: l3 cache schema is not enabled")
		}

		if memBwSchema != "" && !intelrdt.IsMBAEnabled() {
			return errors.New("Intel RDT/MBA: memory bandwidth schema is not enabled")
		}

		if l3CacheSchema != "" || memBwSchema != "" {
			// If intelRdt is not specified in original configuration, we just don't
			// Apply() to create intelRdt group or attach tasks for this container.
			// In update command, we could re-enable through IntelRdtManager.Apply()
			// and then update intelrdt constraint.
			if config.IntelRdt == nil {
				state, err := container.State()
				if err != nil {
					return err
				}
				config.IntelRdt = &configs.IntelRdt{}
				intelRdtManager := intelrdt.NewManager(&config, container.ID(), state.IntelRdtPath)
				if err := intelRdtManager.Apply(state.InitProcessPid); err != nil {
					return err
				}
			}
			config.IntelRdt.L3CacheSchema = l3CacheSchema
			config.IntelRdt.MemBwSchema = memBwSchema
		}

		// XXX(kolyshkin@): currently "runc update" is unable to change
		// device configuration, so add this to skip device update.
		// This helps in case an extra plugin (nvidia GPU) applies some
		// configuration on top of what runc does.
		// Note this field is not saved into container's state.json.
		config.Cgroups.SkipDevices = true

		return container.Set(config)
	},
}
<|endoftext|>


<commit_before>package scamp

import "time"
import "net"

import "golang.org/x/net/ipv4"

type DiscoveryAnnouncer struct {
  services []*Service
  multicastConn *ipv4.PacketConn
  multicastDest *net.UDPAddr
  stopSig (chan bool)
}

func NewDiscoveryAnnouncer() (announcer *DiscoveryAnnouncer, err error) {
  announcer = new(DiscoveryAnnouncer)
  announcer.services = make([]*Service, 0, 0)
  announcer.stopSig = make(chan bool)

  config := DefaultConfig()
  announcer.multicastDest = &net.UDPAddr{IP: config.DiscoveryMulticastIP(), Port: config.DiscoveryMulticastPort()}

  announcer.multicastConn,err = LocalMulticastPacketConn()
  if err != nil {
    return
  }

  return
}

func (announcer *DiscoveryAnnouncer)Stop(){
  announcer.stopSig <- true
}

func (announcer *DiscoveryAnnouncer)Track(serv *Service){
  announcer.services = append(announcer.services, serv)
}

func (announcer *DiscoveryAnnouncer)AnnounceLoop() {
  Trace.Printf("starting announcer loop")

  for {
    select {
    case <- announcer.stopSig:
      return
    default:
      announcer.doAnnounce()
    }

    time.Sleep(time.Duration(defaultAnnounceInterval) * time.Second)
  }
}

func (announcer *DiscoveryAnnouncer)doAnnounce() (err error){
  for _,serv := range announcer.services {
    serviceDesc,err := serv.MarshalText()
    if err != nil {
      Error.Printf("failed to marshal service as text: `%s`. skipping.", err)
    }

    _,err = announcer.multicastConn.WriteTo(serviceDesc, nil, announcer.multicastDest)
    if err != nil {
      return err
    }
  }

  return
}

// Loop for broadcasting service in ServiceProxy format
// AnnounceService(*Service) {

// }
<commit_msg>added debug print statements<commit_after>package scamp

import "time"
import "net"

import "golang.org/x/net/ipv4"

type DiscoveryAnnouncer struct {
  services []*Service
  multicastConn *ipv4.PacketConn
  multicastDest *net.UDPAddr
  stopSig (chan bool)
}

func NewDiscoveryAnnouncer() (announcer *DiscoveryAnnouncer, err error) {
  announcer = new(DiscoveryAnnouncer)
  announcer.services = make([]*Service, 0, 0)
  announcer.stopSig = make(chan bool)

  config := DefaultConfig()
  announcer.multicastDest = &net.UDPAddr{IP: config.DiscoveryMulticastIP(), Port: config.DiscoveryMulticastPort()}

  announcer.multicastConn,err = LocalMulticastPacketConn()
  if err != nil {
    return
  }

  return
}

func (announcer *DiscoveryAnnouncer)Stop(){
  announcer.stopSig <- true
}

func (announcer *DiscoveryAnnouncer)Track(serv *Service){
  announcer.services = append(announcer.services, serv)
}

func (announcer *DiscoveryAnnouncer)AnnounceLoop() {
  Trace.Printf("starting announcer loop")

  for {
    select {
    case <- announcer.stopSig:
      return
    default:
      announcer.doAnnounce()
    }

    time.Sleep(time.Duration(defaultAnnounceInterval) * time.Second)
  }
}

func (announcer *DiscoveryAnnouncer)doAnnounce() (err error){
  for _,serv := range announcer.services {
    serviceDesc,err := serv.MarshalText()
    if err != nil {
      Error.Printf("failed to marshal service as text: `%s`. skipping.", err)
    }
    Info.Printf("serviceDesc: ,": serviceDesc)
    Info.Printf("multicastDest: ,": announcer.multicastDest)
    _,err = announcer.multicastConn.WriteTo(serviceDesc, nil, announcer.multicastDest)
    if err != nil {
      return err
    }
  }

  return
}

// Loop for broadcasting service in ServiceProxy format
// AnnounceService(*Service) {

// }
<|endoftext|>


<commit_before>// Copyright 2015 by caixw, All rights reserved.
// Use of this source code is governed by a MIT
// license that can be found in the LICENSE file.

package upload

import (
	"errors"
	"io"
	"mime/multipart"
	"net/http"
	"os"
	"path/filepath"
	"strings"
	"time"

	"github.com/issue9/unique"
	"github.com/issue9/watermark"
)

// 创建文件的默认权限，比如 Upload.dir 若不存在，会使用此权限创建目录。
const defaultMode os.FileMode = os.ModePerm

// 常用错误类型
var (
	ErrNotAllowExt     = errors.New("不允许的文件上传类型")
	ErrNotAllowSize    = errors.New("文件上传大小超过最大设定值或是文件大小为0")
	ErrUnknownFileSize = errors.New("未知的文件大小")
	ErrNoUploadFile    = errors.New("客户端没有上传文件")
)

// Upload 用于处理文件上传
type Upload struct {
	dir       string
	format    string
	maxSize   int64
	exts      []string
	watermark *watermark.Watermark
	filenames *unique.Unique
}

// New 声明一个 Upload 对象。
//
// dir 上传文件的保存目录，若目录不存在，则会尝试创建;
// format 路径的格式，只能是时间格式
// maxSize 允许上传文件的最大尺寸，单位为 byte；
// exts 允许的扩展名，若为空，将不允许任何文件上传。
func New(dir, format string, maxSize int64, exts ...string) (*Upload, error) {
	// 确保所有的后缀名都是以.作为开始符号的。
	es := make([]string, 0, len(exts))
	for _, ext := range exts {
		if ext[0] != '.' {
			ext = "." + ext
		}
		es = append(es, strings.ToLower(ext))
	}

	// 确保 dir 最后一个字符为目录分隔符。
	last := dir[len(dir)-1]
	if last != '/' && last != filepath.Separator {
		dir = dir + string(filepath.Separator)
	}

	// 若不存在目录，则尝试创建
	if err := os.MkdirAll(dir, defaultMode); err != nil {
		return nil, err
	}

	// 确保 dir 目录存在。
	// NOTE:此处的 dir 最后个字符为/，所以不用判断是否为目录。
	if _, err := os.Stat(dir); err != nil {
		return nil, err
	}

	return &Upload{
		dir:       dir,
		format:    format,
		maxSize:   maxSize,
		exts:      es,
		filenames: unique.New(time.Now().Unix(), 1, 30, "", 36),
	}, nil
}

// 判断扩展名是否符合要求。
func (u *Upload) isAllowExt(ext string) bool {
	if len(ext) == 0 { // 没有扩展名，一律过滤
		return false
	}

	ext = strings.ToLower(ext)
	for _, e := range u.exts {
		if e == ext {
			return true
		}
	}
	return false
}

func (u *Upload) getDestPath(ext string) string {
	n := time.Now()
	return n.Format(u.format) + u.filenames.String() + ext
}

// Dir 获取上传文件的保存目录
func (u *Upload) Dir() string {
	return u.dir
}

// Do 执行上传的操作。会检测上传文件是否符合要求，只要有一个文件不符合，就会中断上传。
// 返回的是相对于 Upload.dir 目录的文件名列表。
func (u *Upload) Do(field string, r *http.Request) ([]string, error) {
	if err := r.ParseMultipartForm(32 << 20); err != nil {
		return nil, err
	}

	if r.MultipartForm == nil || r.MultipartForm.File == nil {
		return nil, ErrNoUploadFile
	}

	heads := r.MultipartForm.File[field]
	if len(heads) == 0 {
		return nil, ErrNoUploadFile
	}

	ret := make([]string, 0, len(heads))

	for _, head := range heads {
		path, err := u.moveFile(head)
		if err != nil {
			return nil, err
		}

		ret = append(ret, path)
	}

	return ret, nil
}

// 将上传的文件移到 u.Dir 目录下。
//
// 返回相对于 u.Dir 的地址
func (u *Upload) moveFile(head *multipart.FileHeader) (string, error) {
	if head.Size > u.maxSize {
		return "", ErrNotAllowSize
	}

	ext := strings.ToLower(filepath.Ext(head.Filename))
	if !u.isAllowExt(ext) {
		return "", ErrNotAllowExt
	}

	file, err := head.Open()
	if err != nil {
		return "", err
	}
	defer file.Close()

	path := u.getDestPath(ext)
	ret := path

	path = u.dir + path
	if err = os.MkdirAll(filepath.Dir(path), defaultMode); err != nil { // 若路径不存在，则创建
		return "", err
	}

	f, err := os.Create(path)
	if err != nil {
		return "", err
	}
	defer f.Close()

	if _, err = io.Copy(f, file); err != nil {
		return "", err
	}

	if u.watermark != nil && watermark.IsAllowExt(ext) {
		if err = u.watermark.Mark(f, ext); err != nil {
			return "", err
		}
	}

	return ret, nil
}

// SetWatermarkFile 设置水印的相关参数。
//
// path 为水印文件的路径；
// padding 为水印在目标不图像上的留白大小；
// pos 水印的位置。
func (u *Upload) SetWatermarkFile(path string, padding int, pos watermark.Pos) error {
	w, err := watermark.New(path, padding, pos)
	if err != nil {
		return err
	}

	u.SetWatermark(w)
	return nil
}

// SetWatermark 设置水印的相关参数。
func (u *Upload) SetWatermark(w *watermark.Watermark) {
	u.watermark = w
}
<commit_msg>直接使用 unique.String()<commit_after>// Copyright 2015 by caixw, All rights reserved.
// Use of this source code is governed by a MIT
// license that can be found in the LICENSE file.

package upload

import (
	"errors"
	"io"
	"mime/multipart"
	"net/http"
	"os"
	"path/filepath"
	"strings"
	"time"

	"github.com/issue9/unique"
	"github.com/issue9/watermark"
)

// 创建文件的默认权限，比如 Upload.dir 若不存在，会使用此权限创建目录。
const defaultMode os.FileMode = os.ModePerm

// 常用错误类型
var (
	ErrNotAllowExt     = errors.New("不允许的文件上传类型")
	ErrNotAllowSize    = errors.New("文件上传大小超过最大设定值或是文件大小为0")
	ErrUnknownFileSize = errors.New("未知的文件大小")
	ErrNoUploadFile    = errors.New("客户端没有上传文件")
)

// Upload 用于处理文件上传
type Upload struct {
	dir       string
	format    string
	maxSize   int64
	exts      []string
	watermark *watermark.Watermark
	filenames *unique.Unique
}

// New 声明一个 Upload 对象。
//
// dir 上传文件的保存目录，若目录不存在，则会尝试创建;
// format 路径的格式，只能是时间格式
// maxSize 允许上传文件的最大尺寸，单位为 byte；
// exts 允许的扩展名，若为空，将不允许任何文件上传。
func New(dir, format string, maxSize int64, exts ...string) (*Upload, error) {
	// 确保所有的后缀名都是以.作为开始符号的。
	es := make([]string, 0, len(exts))
	for _, ext := range exts {
		if ext[0] != '.' {
			ext = "." + ext
		}
		es = append(es, strings.ToLower(ext))
	}

	// 确保 dir 最后一个字符为目录分隔符。
	last := dir[len(dir)-1]
	if last != '/' && last != filepath.Separator {
		dir = dir + string(filepath.Separator)
	}

	// 若不存在目录，则尝试创建
	if err := os.MkdirAll(dir, defaultMode); err != nil {
		return nil, err
	}

	// 确保 dir 目录存在。
	// NOTE:此处的 dir 最后个字符为/，所以不用判断是否为目录。
	if _, err := os.Stat(dir); err != nil {
		return nil, err
	}

	return &Upload{
		dir:       dir,
		format:    format,
		maxSize:   maxSize,
		exts:      es,
		filenames: unique.String(),
	}, nil
}

// 判断扩展名是否符合要求。
func (u *Upload) isAllowExt(ext string) bool {
	if len(ext) == 0 { // 没有扩展名，一律过滤
		return false
	}

	ext = strings.ToLower(ext)
	for _, e := range u.exts {
		if e == ext {
			return true
		}
	}
	return false
}

func (u *Upload) getDestPath(ext string) string {
	n := time.Now()
	return n.Format(u.format) + u.filenames.String() + ext
}

// Dir 获取上传文件的保存目录
func (u *Upload) Dir() string {
	return u.dir
}

// Do 执行上传的操作。会检测上传文件是否符合要求，只要有一个文件不符合，就会中断上传。
// 返回的是相对于 Upload.dir 目录的文件名列表。
func (u *Upload) Do(field string, r *http.Request) ([]string, error) {
	if err := r.ParseMultipartForm(32 << 20); err != nil {
		return nil, err
	}

	if r.MultipartForm == nil || r.MultipartForm.File == nil {
		return nil, ErrNoUploadFile
	}

	heads := r.MultipartForm.File[field]
	if len(heads) == 0 {
		return nil, ErrNoUploadFile
	}

	ret := make([]string, 0, len(heads))

	for _, head := range heads {
		path, err := u.moveFile(head)
		if err != nil {
			return nil, err
		}

		ret = append(ret, path)
	}

	return ret, nil
}

// 将上传的文件移到 u.Dir 目录下。
//
// 返回相对于 u.Dir 的地址
func (u *Upload) moveFile(head *multipart.FileHeader) (string, error) {
	if head.Size > u.maxSize {
		return "", ErrNotAllowSize
	}

	ext := strings.ToLower(filepath.Ext(head.Filename))
	if !u.isAllowExt(ext) {
		return "", ErrNotAllowExt
	}

	file, err := head.Open()
	if err != nil {
		return "", err
	}
	defer file.Close()

	path := u.getDestPath(ext)
	ret := path

	path = u.dir + path
	if err = os.MkdirAll(filepath.Dir(path), defaultMode); err != nil { // 若路径不存在，则创建
		return "", err
	}

	f, err := os.Create(path)
	if err != nil {
		return "", err
	}
	defer f.Close()

	if _, err = io.Copy(f, file); err != nil {
		return "", err
	}

	if u.watermark != nil && watermark.IsAllowExt(ext) {
		if err = u.watermark.Mark(f, ext); err != nil {
			return "", err
		}
	}

	return ret, nil
}

// SetWatermarkFile 设置水印的相关参数。
//
// path 为水印文件的路径；
// padding 为水印在目标不图像上的留白大小；
// pos 水印的位置。
func (u *Upload) SetWatermarkFile(path string, padding int, pos watermark.Pos) error {
	w, err := watermark.New(path, padding, pos)
	if err != nil {
		return err
	}

	u.SetWatermark(w)
	return nil
}

// SetWatermark 设置水印的相关参数。
func (u *Upload) SetWatermark(w *watermark.Watermark) {
	u.watermark = w
}
<|endoftext|>


<commit_before>// sqlinternals - retrieve driver.Rows from sql.*Row / sql.*Rows
//
// Copyright 2013 Arne Hormann. All rights reserved.
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this file,
// You can obtain one at http://mozilla.org/MPL/2.0/.

package sqlinternals

import (
	"database/sql"
	"database/sql/driver"
	"io"
	"testing"
)

type omnithing struct {
	numInputs int
	rows      *testRows
}

func (t *omnithing) Close() error { return nil }

// driver.Driver
func (t *omnithing) Open(name string) (driver.Conn, error) { return t, nil }

// driver.Conn
func (t *omnithing) Prepare(query string) (driver.Stmt, error) { return t, nil }
func (t *omnithing) Begin() (driver.Tx, error)                 { return t, nil }

// driver.Tx
func (t *omnithing) Commit() error   { return nil }
func (t *omnithing) Rollback() error { return nil }

// driver.Stmt
func (t *omnithing) NumInput() int                                   { return t.numInputs }
func (t *omnithing) Exec(args []driver.Value) (driver.Result, error) { return t, nil }
func (t *omnithing) Query(args []driver.Value) (driver.Rows, error)  { return t.rows, nil }

// driver.Result
func (t *omnithing) LastInsertId() (int64, error) { return 0, nil }
func (t *omnithing) RowsAffected() (int64, error) { return 0, nil }

type testRows struct {
	text string
}

// driver.Rows
func (t *testRows) Close() error {
	return nil
}

func (t *testRows) Columns() []string {
	return []string{"testcol"}
}

func (t *testRows) Next(dest []driver.Value) error {
	if t.text == "" {
		return io.EOF
	}
	dest[0] = t.text
	return nil
}

var (
	tester = &omnithing{}
	// make sure the test types implement the interfaces
	_ driver.Driver = tester
	_ driver.Conn   = tester
	_ driver.Tx     = tester
	_ driver.Stmt   = tester
	_ driver.Result = tester
	_ driver.Rows   = &testRows{}
)

const driverType = "test"

func init() {
	sql.Register(driverType, tester)
}

// set to the new values, return the old ones (enables double-defer trickery for reset after use)
func (t *omnithing) setState(inputs int, rows *testRows) (int, *testRows) {
	oldInputs, oldRows := t.numInputs, t.rows
	t.numInputs, t.rows = inputs, rows
	return oldInputs, oldRows
}

func runRowsTest(t *testing.T, inputs int, querier func(conn *sql.DB) (interface{}, error)) {
	// set intial state and restore it after usage
	defer tester.setState(tester.setState(inputs, &testRows{text: "data"}))
	// run a query, retrieve *sql.Rows
	conn, err := sql.Open(driverType, "")
	defer conn.Close()
	rowOrRows, err := querier(conn)
	if closer, ok := rowOrRows.(io.Closer); ok {
		defer closer.Close()
	}
	// check that it is accessible and matches the one in tester.rows
	unwrapped, err := Inspect(rowOrRows)
	if err != nil {
		t.Error(err)
	} else if myrows, ok := unwrapped.(*testRows); !ok || myrows != tester.rows {
		t.Errorf("returned driver.Rows must match those passed in.")
	}
}

func TestRowWithoutArgs(t *testing.T) {
	runRowsTest(t, 0, func(conn *sql.DB) (interface{}, error) {
		return conn.QueryRow("SELECT 1"), nil
	})
}

func TestRowWithArgs(t *testing.T) {
	runRowsTest(t, 1, func(conn *sql.DB) (interface{}, error) {
		return conn.QueryRow("SELECT ?", 1), nil
	})
}

func TestRowsWithoutArgs(t *testing.T) {
	runRowsTest(t, 0, func(conn *sql.DB) (interface{}, error) {
		return conn.Query("SELECT 1")
	})
}

func TestRowsWithArgs(t *testing.T) {
	runRowsTest(t, 1, func(conn *sql.DB) (interface{}, error) {
		return conn.Query("SELECT ?", 1)
	})
}
<commit_msg>improve testability and prepare for additional test types<commit_after>// sqlinternals - retrieve driver.Rows from sql.*Row / sql.*Rows
//
// Copyright 2013 Arne Hormann. All rights reserved.
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this file,
// You can obtain one at http://mozilla.org/MPL/2.0/.

package sqlinternals

import (
	"database/sql"
	"database/sql/driver"
	"io"
	"testing"
)

type omnithing struct {
	numInputs int
	columns   []string
	rows      [][]interface{}
}

func (t *omnithing) Close() error { return nil }

// driver.Driver
func (t *omnithing) Open(name string) (driver.Conn, error) { return t, nil }

// driver.Conn
func (t *omnithing) Prepare(query string) (driver.Stmt, error) { return t, nil }
func (t *omnithing) Begin() (driver.Tx, error)                 { return t, nil }

// driver.Tx
func (t *omnithing) Commit() error   { return nil }
func (t *omnithing) Rollback() error { return nil }

// driver.Stmt
func (t *omnithing) NumInput() int                                   { return t.numInputs }
func (t *omnithing) Exec(args []driver.Value) (driver.Result, error) { return t, nil }
func (t *omnithing) Query(args []driver.Value) (driver.Rows, error)  { return t, nil }

// driver.Result
func (t *omnithing) LastInsertId() (int64, error) { return 0, nil }
func (t *omnithing) RowsAffected() (int64, error) { return 0, nil }

// driver.Rows
func (t *omnithing) Columns() []string { return t.columns }
func (t *omnithing) Next(dest []driver.Value) error {
	if len(t.rows) == 0 {
		return io.EOF
	}
	var row []interface{}
	row, t.rows = t.rows[0], t.rows[1:]
	for i, v := range row {
		dest[i] = v
	}
	return nil
}

func (o *omnithing) setDB(numInputs int, columns []string, cells ...interface{}) *omnithing {
	o.numInputs = numInputs
	o.columns = columns
	numCols, numCells := len(columns), len(cells)
	numRows := numCells / numCols
	if numCols*numRows != numCells {
		panic("wrong number of cells")
	}
	rows := [][]interface{}{}
	for r := 0; r < numRows; r++ {
		cols := []interface{}{}
		for c := 0; c < numCols; c++ {
			cols = append(cols, cells[r*numCols+c])
		}
		rows = append(rows, cols)
	}
	o.rows = rows
	return o
}

type querier func(conn *sql.DB) (interface{}, error)

var (
	testdriver = &omnithing{}
	// make sure the test type implements the interfaces
	_ driver.Driver = testdriver
	_ driver.Conn   = testdriver
	_ driver.Tx     = testdriver
	_ driver.Stmt   = testdriver
	_ driver.Result = testdriver
	_ driver.Rows   = testdriver
)

const driverType = "test"

func init() {
	sql.Register(driverType, testdriver)
}

func runRowsTest(t *testing.T, query querier, numInputs int, columns []string, cells ...interface{}) {
	// set intial state before usage
	testdriver.setDB(numInputs, columns, cells...)
	// run a query, retrieve *sql.Rows
	conn, err := sql.Open(driverType, "")
	defer conn.Close()
	rowOrRows, err := query(conn)
	if closer, ok := rowOrRows.(io.Closer); ok {
		defer closer.Close()
	}
	// check that it is accessible and matches the one in testdriver.rows
	unwrapped, err := Inspect(rowOrRows)
	if err != nil {
		t.Error(err)
		return
	}
	myrows, ok := unwrapped.(*omnithing)
	if !ok || myrows != testdriver {
		t.Errorf("returned driver.Rows must match those passed in.")
	}
}

func TestRowWithoutArgs(t *testing.T) {
	query := func(conn *sql.DB) (interface{}, error) {
		return conn.QueryRow(`SELECT "test"`), nil
	}
	runRowsTest(t, query, 0, []string{"header"}, "test")
}

func TestRowWithArgs(t *testing.T) {
	query := func(conn *sql.DB) (interface{}, error) {
		return conn.QueryRow(`SELECT ?`, "test"), nil
	}
	runRowsTest(t, query, 1, []string{"header"}, "test")
}

func TestRowsWithoutArgs(t *testing.T) {
	query := func(conn *sql.DB) (interface{}, error) {
		return conn.Query(`SELECT "test"`)
	}
	runRowsTest(t, query, 0, []string{"header"}, "test")
}

func TestRowsWithArgs(t *testing.T) {
	query := func(conn *sql.DB) (interface{}, error) {
		return conn.Query(`SELECT ?`, "test")
	}
	runRowsTest(t, query, 1, []string{"header"}, "test")
}
<|endoftext|>


<commit_before>/*
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 e2e_node

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

	"k8s.io/kubernetes/pkg/api"
	"k8s.io/kubernetes/pkg/api/errors"
	client "k8s.io/kubernetes/pkg/client/unversioned"
	"k8s.io/kubernetes/pkg/types"
	"k8s.io/kubernetes/pkg/util"
	"k8s.io/kubernetes/test/e2e/framework"

	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
)

var _ = framework.KubeDescribe("MirrorPod", func() {
	f := NewDefaultFramework("mirror-pod")
	Context("when create a mirror pod ", func() {
		var staticPodName, mirrorPodName string
		BeforeEach(func() {
			ns := f.Namespace.Name
			staticPodName = "static-pod-" + string(util.NewUUID())
			mirrorPodName = staticPodName + "-" + e2es.nodeName

			By("create the static pod")
			err := createStaticPod(e2es.kubeletStaticPodDir, staticPodName, ns, ImageRegistry[nginxImage], api.RestartPolicyAlways)
			Expect(err).ShouldNot(HaveOccurred())

			By("wait for the mirror pod to be running")
			Eventually(func() error {
				return checkMirrorPodRunning(f.Client, mirrorPodName, ns)
			}, 2*time.Minute, time.Second*4).Should(BeNil())
		})
		It("should be updated when static pod updated", func() {
			ns := f.Namespace.Name
			By("get mirror pod uid")
			pod, err := f.Client.Pods(ns).Get(mirrorPodName)
			Expect(err).ShouldNot(HaveOccurred())
			uid := pod.UID

			By("update the static pod container image")
			image := ImageRegistry[pauseImage]
			err = createStaticPod(e2es.kubeletStaticPodDir, staticPodName, ns, image, api.RestartPolicyAlways)
			Expect(err).ShouldNot(HaveOccurred())

			By("wait for the mirror pod to be updated")
			Eventually(func() error {
				return checkMirrorPodRecreatedAndRunnig(f.Client, mirrorPodName, ns, uid)
			}, 2*time.Minute, time.Second*4).Should(BeNil())

			By("check the mirror pod container image is updated")
			pod, err = f.Client.Pods(ns).Get(mirrorPodName)
			Expect(err).ShouldNot(HaveOccurred())
			Expect(len(pod.Spec.Containers)).Should(Equal(1))
			Expect(pod.Spec.Containers[0].Image).Should(Equal(image))
		})
		It("should be recreated when mirror pod gracefully deleted", func() {
			ns := f.Namespace.Name
			By("get mirror pod uid")
			pod, err := f.Client.Pods(ns).Get(mirrorPodName)
			Expect(err).ShouldNot(HaveOccurred())
			uid := pod.UID

			By("delete the mirror pod with grace period 30s")
			err = f.Client.Pods(ns).Delete(mirrorPodName, api.NewDeleteOptions(30))
			Expect(err).ShouldNot(HaveOccurred())

			By("wait for the mirror pod to be recreated")
			Eventually(func() error {
				return checkMirrorPodRecreatedAndRunnig(f.Client, mirrorPodName, ns, uid)
			}, 2*time.Minute, time.Second*4).Should(BeNil())
		})
		It("should be recreated when mirror pod forcibly deleted", func() {
			ns := f.Namespace.Name
			By("get mirror pod uid")
			pod, err := f.Client.Pods(ns).Get(mirrorPodName)
			Expect(err).ShouldNot(HaveOccurred())
			uid := pod.UID

			By("delete the mirror pod with grace period 0s")
			err = f.Client.Pods(ns).Delete(mirrorPodName, api.NewDeleteOptions(0))
			Expect(err).ShouldNot(HaveOccurred())

			By("wait for the mirror pod to be recreated")
			Eventually(func() error {
				return checkMirrorPodRecreatedAndRunnig(f.Client, mirrorPodName, ns, uid)
			}, 2*time.Minute, time.Second*4).Should(BeNil())
		})
		AfterEach(func() {
			ns := f.Namespace.Name
			By("delete the static pod")
			err := deleteStaticPod(e2es.kubeletStaticPodDir, staticPodName, ns)
			Expect(err).ShouldNot(HaveOccurred())

			By("wait for the mirror pod to disappear")
			Eventually(func() error {
				return checkMirrorPodDisappear(f.Client, mirrorPodName, ns)
			}, 2*time.Minute, time.Second*4).Should(BeNil())
		})
	})
})

func staticPodPath(dir, name, namespace string) string {
	return filepath.Join(dir, namespace+"-"+name+".yaml")
}

func createStaticPod(dir, name, namespace, image string, restart api.RestartPolicy) error {
	template := `
apiVersion: v1
kind: Pod
metadata:
  name: %s
  namespace: %s
spec:
  containers:
  - name: test
    image: %s
    restartPolicy: %s
`
	file := staticPodPath(dir, name, namespace)
	podYaml := fmt.Sprintf(template, name, namespace, image, string(restart))

	f, err := os.OpenFile(file, os.O_RDWR|os.O_TRUNC|os.O_CREATE, 0666)
	if err != nil {
		return err
	}
	defer f.Close()

	_, err = f.WriteString(podYaml)
	return err
}

func deleteStaticPod(dir, name, namespace string) error {
	file := staticPodPath(dir, name, namespace)
	return os.Remove(file)
}

func checkMirrorPodDisappear(cl *client.Client, name, namespace string) error {
	_, err := cl.Pods(namespace).Get(name)
	if errors.IsNotFound(err) {
		return nil
	}
	return err
}

func checkMirrorPodRunning(cl *client.Client, name, namespace string) error {
	pod, err := cl.Pods(namespace).Get(name)
	if err != nil {
		return fmt.Errorf("expected the mirror pod %q to appear: %v", name, err)
	}
	if pod.Status.Phase != api.PodRunning {
		return fmt.Errorf("expected the mirror pod %q to be running, got %q", name, pod.Status.Phase)
	}
	return nil
}

func checkMirrorPodRecreatedAndRunnig(cl *client.Client, name, namespace string, oUID types.UID) error {
	pod, err := cl.Pods(namespace).Get(name)
	if err != nil {
		return fmt.Errorf("expected the mirror pod %q to appear: %v", name, err)
	}
	if pod.UID == oUID {
		return fmt.Errorf("expected the uid of mirror pod %q to be changed, got %q", name, pod.UID)
	}
	if pod.Status.Phase != api.PodRunning {
		return fmt.Errorf("expected the mirror pod %q to be running, got %q", name, pod.Status.Phase)
	}
	return nil
}
<commit_msg>Fix a bug in mirror pod node e2e test.<commit_after>/*
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 e2e_node

import (
	goerrors "errors"
	"fmt"
	"os"
	"path/filepath"
	"time"

	"k8s.io/kubernetes/pkg/api"
	"k8s.io/kubernetes/pkg/api/errors"
	client "k8s.io/kubernetes/pkg/client/unversioned"
	"k8s.io/kubernetes/pkg/types"
	"k8s.io/kubernetes/pkg/util"
	"k8s.io/kubernetes/test/e2e/framework"

	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
)

var _ = framework.KubeDescribe("MirrorPod", func() {
	f := NewDefaultFramework("mirror-pod")
	Context("when create a mirror pod ", func() {
		var staticPodName, mirrorPodName string
		BeforeEach(func() {
			ns := f.Namespace.Name
			staticPodName = "static-pod-" + string(util.NewUUID())
			mirrorPodName = staticPodName + "-" + e2es.nodeName

			By("create the static pod")
			err := createStaticPod(e2es.kubeletStaticPodDir, staticPodName, ns, ImageRegistry[nginxImage], api.RestartPolicyAlways)
			Expect(err).ShouldNot(HaveOccurred())

			By("wait for the mirror pod to be running")
			Eventually(func() error {
				return checkMirrorPodRunning(f.Client, mirrorPodName, ns)
			}, 2*time.Minute, time.Second*4).Should(BeNil())
		})
		It("should be updated when static pod updated", func() {
			ns := f.Namespace.Name
			By("get mirror pod uid")
			pod, err := f.Client.Pods(ns).Get(mirrorPodName)
			Expect(err).ShouldNot(HaveOccurred())
			uid := pod.UID

			By("update the static pod container image")
			image := ImageRegistry[pauseImage]
			err = createStaticPod(e2es.kubeletStaticPodDir, staticPodName, ns, image, api.RestartPolicyAlways)
			Expect(err).ShouldNot(HaveOccurred())

			By("wait for the mirror pod to be updated")
			Eventually(func() error {
				return checkMirrorPodRecreatedAndRunnig(f.Client, mirrorPodName, ns, uid)
			}, 2*time.Minute, time.Second*4).Should(BeNil())

			By("check the mirror pod container image is updated")
			pod, err = f.Client.Pods(ns).Get(mirrorPodName)
			Expect(err).ShouldNot(HaveOccurred())
			Expect(len(pod.Spec.Containers)).Should(Equal(1))
			Expect(pod.Spec.Containers[0].Image).Should(Equal(image))
		})
		It("should be recreated when mirror pod gracefully deleted", func() {
			ns := f.Namespace.Name
			By("get mirror pod uid")
			pod, err := f.Client.Pods(ns).Get(mirrorPodName)
			Expect(err).ShouldNot(HaveOccurred())
			uid := pod.UID

			By("delete the mirror pod with grace period 30s")
			err = f.Client.Pods(ns).Delete(mirrorPodName, api.NewDeleteOptions(30))
			Expect(err).ShouldNot(HaveOccurred())

			By("wait for the mirror pod to be recreated")
			Eventually(func() error {
				return checkMirrorPodRecreatedAndRunnig(f.Client, mirrorPodName, ns, uid)
			}, 2*time.Minute, time.Second*4).Should(BeNil())
		})
		It("should be recreated when mirror pod forcibly deleted", func() {
			ns := f.Namespace.Name
			By("get mirror pod uid")
			pod, err := f.Client.Pods(ns).Get(mirrorPodName)
			Expect(err).ShouldNot(HaveOccurred())
			uid := pod.UID

			By("delete the mirror pod with grace period 0s")
			err = f.Client.Pods(ns).Delete(mirrorPodName, api.NewDeleteOptions(0))
			Expect(err).ShouldNot(HaveOccurred())

			By("wait for the mirror pod to be recreated")
			Eventually(func() error {
				return checkMirrorPodRecreatedAndRunnig(f.Client, mirrorPodName, ns, uid)
			}, 2*time.Minute, time.Second*4).Should(BeNil())
		})
		AfterEach(func() {
			ns := f.Namespace.Name
			By("delete the static pod")
			err := deleteStaticPod(e2es.kubeletStaticPodDir, staticPodName, ns)
			Expect(err).ShouldNot(HaveOccurred())

			By("wait for the mirror pod to disappear")
			Eventually(func() error {
				return checkMirrorPodDisappear(f.Client, mirrorPodName, ns)
			}, 2*time.Minute, time.Second*4).Should(BeNil())
		})
	})
})

func staticPodPath(dir, name, namespace string) string {
	return filepath.Join(dir, namespace+"-"+name+".yaml")
}

func createStaticPod(dir, name, namespace, image string, restart api.RestartPolicy) error {
	template := `
apiVersion: v1
kind: Pod
metadata:
  name: %s
  namespace: %s
spec:
  containers:
  - name: test
    image: %s
    restartPolicy: %s
`
	file := staticPodPath(dir, name, namespace)
	podYaml := fmt.Sprintf(template, name, namespace, image, string(restart))

	f, err := os.OpenFile(file, os.O_RDWR|os.O_TRUNC|os.O_CREATE, 0666)
	if err != nil {
		return err
	}
	defer f.Close()

	_, err = f.WriteString(podYaml)
	return err
}

func deleteStaticPod(dir, name, namespace string) error {
	file := staticPodPath(dir, name, namespace)
	return os.Remove(file)
}

func checkMirrorPodDisappear(cl *client.Client, name, namespace string) error {
	_, err := cl.Pods(namespace).Get(name)
	if errors.IsNotFound(err) {
		return nil
	}
	return goerrors.New("pod not disappear")
}

func checkMirrorPodRunning(cl *client.Client, name, namespace string) error {
	pod, err := cl.Pods(namespace).Get(name)
	if err != nil {
		return fmt.Errorf("expected the mirror pod %q to appear: %v", name, err)
	}
	if pod.Status.Phase != api.PodRunning {
		return fmt.Errorf("expected the mirror pod %q to be running, got %q", name, pod.Status.Phase)
	}
	return nil
}

func checkMirrorPodRecreatedAndRunnig(cl *client.Client, name, namespace string, oUID types.UID) error {
	pod, err := cl.Pods(namespace).Get(name)
	if err != nil {
		return fmt.Errorf("expected the mirror pod %q to appear: %v", name, err)
	}
	if pod.UID == oUID {
		return fmt.Errorf("expected the uid of mirror pod %q to be changed, got %q", name, pod.UID)
	}
	if pod.Status.Phase != api.PodRunning {
		return fmt.Errorf("expected the mirror pod %q to be running, got %q", name, pod.Status.Phase)
	}
	return nil
}
<|endoftext|>


<commit_before>package networking

import (
	"context"
	"fmt"
	"os"
	"os/exec"
	"strings"
	"time"

	projectv1 "github.com/openshift/api/project/v1"
	networkclient "github.com/openshift/client-go/network/clientset/versioned/typed/network/v1"
	"github.com/openshift/library-go/pkg/network/networkutils"
	"k8s.io/kubernetes/test/e2e/framework/pod"

	exutil "github.com/openshift/origin/test/extended/util"

	corev1 "k8s.io/api/core/v1"
	kapierrs "k8s.io/apimachinery/pkg/api/errors"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apiserver/pkg/storage/names"
	"k8s.io/client-go/util/retry"
	e2e "k8s.io/kubernetes/test/e2e/framework"
	e2enode "k8s.io/kubernetes/test/e2e/framework/node"
	e2eskipper "k8s.io/kubernetes/test/e2e/framework/skipper"

	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
)

type NodeType int

const (
	// Initial pod start can be delayed O(minutes) by slow docker pulls
	// TODO: Make this 30 seconds once #4566 is resolved.
	podStartTimeout = 5 * time.Minute

	// How often to poll pods and nodes.
	poll = 5 * time.Second

	// How wide to print pod names, by default. Useful for aligning printing to
	// quickly scan through output.
	podPrintWidth = 55

	// Indicator for same or different node
	SAME_NODE      NodeType = iota
	DIFFERENT_NODE NodeType = iota
)

func expectNoError(err error, explain ...interface{}) {
	ExpectWithOffset(1, err).NotTo(HaveOccurred(), explain...)
}

func launchWebserverService(f *e2e.Framework, serviceName string, nodeName string) (serviceAddr string) {
	exutil.LaunchWebserverPod(f, serviceName, nodeName)

	// FIXME: make e2e.LaunchWebserverPod() set the label when creating the pod
	err := retry.RetryOnConflict(retry.DefaultRetry, func() error {
		podClient := f.ClientSet.CoreV1().Pods(f.Namespace.Name)
		pod, err := podClient.Get(context.Background(), serviceName, metav1.GetOptions{})
		if err != nil {
			return err
		}
		if pod.ObjectMeta.Labels == nil {
			pod.ObjectMeta.Labels = make(map[string]string)
		}
		pod.ObjectMeta.Labels["name"] = "web"
		_, err = podClient.Update(context.Background(), pod, metav1.UpdateOptions{})
		return err
	})
	expectNoError(err)

	servicePort := 8080
	service := &corev1.Service{
		ObjectMeta: metav1.ObjectMeta{
			Name: serviceName,
		},
		Spec: corev1.ServiceSpec{
			Type: corev1.ServiceTypeClusterIP,
			Ports: []corev1.ServicePort{
				{
					Protocol: corev1.ProtocolTCP,
					Port:     int32(servicePort),
				},
			},
			Selector: map[string]string{
				"name": "web",
			},
		},
	}
	serviceClient := f.ClientSet.CoreV1().Services(f.Namespace.Name)
	_, err = serviceClient.Create(context.Background(), service, metav1.CreateOptions{})
	expectNoError(err)
	expectNoError(exutil.WaitForEndpoint(f.ClientSet, f.Namespace.Name, serviceName))
	createdService, err := serviceClient.Get(context.Background(), serviceName, metav1.GetOptions{})
	expectNoError(err)
	serviceAddr = fmt.Sprintf("%s:%d", createdService.Spec.ClusterIP, servicePort)
	e2e.Logf("Target service IP:port is %s", serviceAddr)
	return
}

func checkConnectivityToHost(f *e2e.Framework, nodeName string, podName string, host string, timeout time.Duration) error {
	e2e.Logf("Creating an exec pod on node %v", nodeName)
	execPod := pod.CreateExecPodOrFail(f.ClientSet, f.Namespace.Name, fmt.Sprintf("execpod-sourceip-%s", nodeName), func(pod *corev1.Pod) {
		pod.Spec.NodeName = nodeName
	})
	defer func() {
		e2e.Logf("Cleaning up the exec pod")
		err := f.ClientSet.CoreV1().Pods(f.Namespace.Name).Delete(context.Background(), execPod.Name, metav1.DeleteOptions{})
		Expect(err).NotTo(HaveOccurred())
	}()

	var stdout string
	e2e.Logf("Waiting up to %v to wget %s", timeout, host)
	cmd := fmt.Sprintf("wget -T 30 -qO- %s", host)
	var err error
	for start := time.Now(); time.Since(start) < timeout; time.Sleep(2) {
		stdout, err = e2e.RunHostCmd(execPod.Namespace, execPod.Name, cmd)
		if err != nil {
			e2e.Logf("got err: %v, retry until timeout", err)
			continue
		}
		// Need to check output because wget -q might omit the error.
		if strings.TrimSpace(stdout) == "" {
			e2e.Logf("got empty stdout, retry until timeout")
			continue
		}
		break
	}
	return err
}

var cachedNetworkPluginName *string

func networkPluginName() string {
	if cachedNetworkPluginName == nil {
		// We don't use exutil.NewCLI() here because it can't be called from BeforeEach()
		out, err := exec.Command(
			"oc", "--config="+exutil.KubeConfigPath(),
			"get", "clusternetwork", "default",
			"--template={{.pluginName}}",
		).CombinedOutput()
		pluginName := string(out)
		if err != nil {
			e2e.Logf("Could not check network plugin name: %v. Assuming a non-OpenShift plugin", err)
			pluginName = ""
		}
		cachedNetworkPluginName = &pluginName
	}
	return *cachedNetworkPluginName
}

func pluginIsolatesNamespaces() bool {
	if os.Getenv("NETWORKING_E2E_ISOLATION") == "true" {
		return true
	}
	// Assume that only the OpenShift SDN "multitenant" plugin isolates by default
	return networkPluginName() == networkutils.MultiTenantPluginName
}

func pluginImplementsNetworkPolicy() bool {
	switch {
	case os.Getenv("NETWORKING_E2E_NETWORKPOLICY") == "true":
		return true
	case networkPluginName() == networkutils.NetworkPolicyPluginName:
		return true
	default:
		// If we can't detect the plugin, we assume it doesn't support
		// NetworkPolicy, so the tests will work under kubenet
		return false
	}
}

func makeNamespaceGlobal(oc *exutil.CLI, ns *corev1.Namespace) {
	clientConfig := oc.AdminConfig()
	networkClient := networkclient.NewForConfigOrDie(clientConfig)
	netns, err := networkClient.NetNamespaces().Get(context.Background(), ns.Name, metav1.GetOptions{})
	expectNoError(err)
	netns.NetID = 0
	_, err = networkClient.NetNamespaces().Update(context.Background(), netns, metav1.UpdateOptions{})
	expectNoError(err)
}

func makeNamespaceScheduleToAllNodes(f *e2e.Framework) {
	// to avoid hassles dealing with selector limits, set the namespace label selector to empty
	// to allow targeting all nodes
	for {
		ns, err := f.ClientSet.CoreV1().Namespaces().Get(context.Background(), f.Namespace.Name, metav1.GetOptions{})
		expectNoError(err)
		if ns.Annotations == nil {
			ns.Annotations = make(map[string]string)
		}
		ns.Annotations[projectv1.ProjectNodeSelector] = ""
		_, err = f.ClientSet.CoreV1().Namespaces().Update(context.Background(), ns, metav1.UpdateOptions{})
		if err == nil {
			return
		}
		if kapierrs.IsConflict(err) {
			continue
		}
		expectNoError(err)
	}
}

// findAppropriateNodes tries to find a source and destination for a type of node connectivity
// test (same node, or different node).
func findAppropriateNodes(f *e2e.Framework, nodeType NodeType) (*corev1.Node, *corev1.Node, error) {
	nodes, err := e2enode.GetReadySchedulableNodes(f.ClientSet)
	if err != nil {
		e2e.Logf("Unable to get schedulable nodes due to %v", err)
		return nil, nil, err
	}
	candidates := nodes.Items

	if len(candidates) == 0 {
		e2e.Failf("Unable to find any candidate nodes for e2e networking tests in \n%#v", nodes.Items)
	}

	// in general, avoiding masters is a good thing, so see if we can find nodes that aren't masters
	if len(candidates) > 1 {
		var withoutMasters []corev1.Node
		// look for anything that has the label value master or infra and try to skip it
		isAllowed := func(node *corev1.Node) bool {
			for _, value := range node.Labels {
				if value == "master" || value == "infra" {
					return false
				}
			}
			return true
		}
		for _, node := range candidates {
			if !isAllowed(&node) {
				continue
			}
			withoutMasters = append(withoutMasters, node)
		}
		if len(withoutMasters) >= 2 {
			candidates = withoutMasters
		}
	}

	var candidateNames, nodeNames []string
	for _, node := range candidates {
		candidateNames = append(candidateNames, node.Name)
	}
	for _, node := range nodes.Items {
		nodeNames = append(nodeNames, node.Name)
	}

	if nodeType == DIFFERENT_NODE {
		if len(candidates) <= 1 {
			e2eskipper.Skipf("Only one node is available in this environment (%v out of %v)", candidateNames, nodeNames)
		}
		e2e.Logf("Using %s and %s for test (%v out of %v)", candidates[0].Name, candidates[1].Name, candidateNames, nodeNames)
		return &candidates[0], &candidates[1], nil
	}
	e2e.Logf("Using %s for test (%v out of %v)", candidates[0].Name, candidateNames, nodeNames)
	return &candidates[0], &candidates[0], nil
}

func checkPodIsolation(f1, f2 *e2e.Framework, nodeType NodeType) error {
	makeNamespaceScheduleToAllNodes(f1)
	makeNamespaceScheduleToAllNodes(f2)
	serverNode, clientNode, err := findAppropriateNodes(f1, nodeType)
	if err != nil {
		return err
	}
	podName := "isolation-webserver"
	defer f1.ClientSet.CoreV1().Pods(f1.Namespace.Name).Delete(context.Background(), podName, metav1.DeleteOptions{})
	ip := exutil.LaunchWebserverPod(f1, podName, serverNode.Name)

	return checkConnectivityToHost(f2, clientNode.Name, "isolation-wget", ip, 10*time.Second)
}

func checkServiceConnectivity(serverFramework, clientFramework *e2e.Framework, nodeType NodeType) error {
	makeNamespaceScheduleToAllNodes(serverFramework)
	makeNamespaceScheduleToAllNodes(clientFramework)
	serverNode, clientNode, err := findAppropriateNodes(serverFramework, nodeType)
	if err != nil {
		return err
	}
	podName := names.SimpleNameGenerator.GenerateName("service-")
	defer serverFramework.ClientSet.CoreV1().Pods(serverFramework.Namespace.Name).Delete(context.Background(), podName, metav1.DeleteOptions{})
	defer serverFramework.ClientSet.CoreV1().Services(serverFramework.Namespace.Name).Delete(context.Background(), podName, metav1.DeleteOptions{})
	ip := launchWebserverService(serverFramework, podName, serverNode.Name)

	return checkConnectivityToHost(clientFramework, clientNode.Name, "service-wget", ip, 10*time.Second)
}

func InNonIsolatingContext(body func()) {
	Context("when using a plugin that does not isolate namespaces by default", func() {
		BeforeEach(func() {
			if pluginIsolatesNamespaces() {
				e2eskipper.Skipf("This plugin isolates namespaces by default.")
			}
		})

		body()
	})
}

func InIsolatingContext(body func()) {
	Context("when using a plugin that isolates namespaces by default", func() {
		BeforeEach(func() {
			if !pluginIsolatesNamespaces() {
				e2eskipper.Skipf("This plugin does not isolate namespaces by default.")
			}
		})

		body()
	})
}

func InNetworkPolicyContext(body func()) {
	Context("when using a plugin that implements NetworkPolicy", func() {
		BeforeEach(func() {
			if !pluginImplementsNetworkPolicy() {
				e2eskipper.Skipf("This plugin does not implement NetworkPolicy.")
			}
		})

		body()
	})
}

func InPluginContext(plugins []string, body func()) {
	Context(fmt.Sprintf("when using one of the plugins '%s'", strings.Join(plugins, ", ")),
		func() {
			BeforeEach(func() {
				found := false
				for _, plugin := range plugins {
					if networkPluginName() == plugin {
						found = true
						break
					}
				}
				if !found {
					e2eskipper.Skipf("Not using one of the specified plugins")
				}
			})

			body()
		},
	)
}

func InOpenShiftSDNContext(body func()) {
	Context("when using openshift-sdn",
		func() {
			BeforeEach(func() {
				if networkPluginName() == "" {
					e2eskipper.Skipf("Not using openshift-sdn")
				}
			})

			body()
		},
	)
}
<commit_msg>Fix openshift-sdn mode detection in e2e tests<commit_after>package networking

import (
	"context"
	"fmt"
	"os"
	"os/exec"
	"strings"
	"time"

	projectv1 "github.com/openshift/api/project/v1"
	networkclient "github.com/openshift/client-go/network/clientset/versioned/typed/network/v1"
	"github.com/openshift/library-go/pkg/network/networkutils"
	"k8s.io/kubernetes/test/e2e/framework/pod"

	exutil "github.com/openshift/origin/test/extended/util"

	corev1 "k8s.io/api/core/v1"
	kapierrs "k8s.io/apimachinery/pkg/api/errors"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apiserver/pkg/storage/names"
	"k8s.io/client-go/util/retry"
	e2e "k8s.io/kubernetes/test/e2e/framework"
	e2enode "k8s.io/kubernetes/test/e2e/framework/node"
	e2eskipper "k8s.io/kubernetes/test/e2e/framework/skipper"

	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
)

type NodeType int

const (
	// Initial pod start can be delayed O(minutes) by slow docker pulls
	// TODO: Make this 30 seconds once #4566 is resolved.
	podStartTimeout = 5 * time.Minute

	// How often to poll pods and nodes.
	poll = 5 * time.Second

	// How wide to print pod names, by default. Useful for aligning printing to
	// quickly scan through output.
	podPrintWidth = 55

	// Indicator for same or different node
	SAME_NODE      NodeType = iota
	DIFFERENT_NODE NodeType = iota
)

func expectNoError(err error, explain ...interface{}) {
	ExpectWithOffset(1, err).NotTo(HaveOccurred(), explain...)
}

func launchWebserverService(f *e2e.Framework, serviceName string, nodeName string) (serviceAddr string) {
	exutil.LaunchWebserverPod(f, serviceName, nodeName)

	// FIXME: make e2e.LaunchWebserverPod() set the label when creating the pod
	err := retry.RetryOnConflict(retry.DefaultRetry, func() error {
		podClient := f.ClientSet.CoreV1().Pods(f.Namespace.Name)
		pod, err := podClient.Get(context.Background(), serviceName, metav1.GetOptions{})
		if err != nil {
			return err
		}
		if pod.ObjectMeta.Labels == nil {
			pod.ObjectMeta.Labels = make(map[string]string)
		}
		pod.ObjectMeta.Labels["name"] = "web"
		_, err = podClient.Update(context.Background(), pod, metav1.UpdateOptions{})
		return err
	})
	expectNoError(err)

	servicePort := 8080
	service := &corev1.Service{
		ObjectMeta: metav1.ObjectMeta{
			Name: serviceName,
		},
		Spec: corev1.ServiceSpec{
			Type: corev1.ServiceTypeClusterIP,
			Ports: []corev1.ServicePort{
				{
					Protocol: corev1.ProtocolTCP,
					Port:     int32(servicePort),
				},
			},
			Selector: map[string]string{
				"name": "web",
			},
		},
	}
	serviceClient := f.ClientSet.CoreV1().Services(f.Namespace.Name)
	_, err = serviceClient.Create(context.Background(), service, metav1.CreateOptions{})
	expectNoError(err)
	expectNoError(exutil.WaitForEndpoint(f.ClientSet, f.Namespace.Name, serviceName))
	createdService, err := serviceClient.Get(context.Background(), serviceName, metav1.GetOptions{})
	expectNoError(err)
	serviceAddr = fmt.Sprintf("%s:%d", createdService.Spec.ClusterIP, servicePort)
	e2e.Logf("Target service IP:port is %s", serviceAddr)
	return
}

func checkConnectivityToHost(f *e2e.Framework, nodeName string, podName string, host string, timeout time.Duration) error {
	e2e.Logf("Creating an exec pod on node %v", nodeName)
	execPod := pod.CreateExecPodOrFail(f.ClientSet, f.Namespace.Name, fmt.Sprintf("execpod-sourceip-%s", nodeName), func(pod *corev1.Pod) {
		pod.Spec.NodeName = nodeName
	})
	defer func() {
		e2e.Logf("Cleaning up the exec pod")
		err := f.ClientSet.CoreV1().Pods(f.Namespace.Name).Delete(context.Background(), execPod.Name, metav1.DeleteOptions{})
		Expect(err).NotTo(HaveOccurred())
	}()

	var stdout string
	e2e.Logf("Waiting up to %v to wget %s", timeout, host)
	cmd := fmt.Sprintf("wget -T 30 -qO- %s", host)
	var err error
	for start := time.Now(); time.Since(start) < timeout; time.Sleep(2) {
		stdout, err = e2e.RunHostCmd(execPod.Namespace, execPod.Name, cmd)
		if err != nil {
			e2e.Logf("got err: %v, retry until timeout", err)
			continue
		}
		// Need to check output because wget -q might omit the error.
		if strings.TrimSpace(stdout) == "" {
			e2e.Logf("got empty stdout, retry until timeout")
			continue
		}
		break
	}
	return err
}

var cachedNetworkPluginName *string

func networkPluginName() string {
	if cachedNetworkPluginName == nil {
		// We don't use exutil.NewCLI() here because it can't be called from BeforeEach()
		out, err := exec.Command(
			"oc", "--kubeconfig="+exutil.KubeConfigPath(),
			"get", "clusternetwork", "default",
			"--template={{.pluginName}}",
		).CombinedOutput()
		pluginName := string(out)
		if err != nil {
			e2e.Logf("Could not check network plugin name: %v. Assuming a non-OpenShift plugin", err)
			pluginName = ""
		}
		cachedNetworkPluginName = &pluginName
	}
	return *cachedNetworkPluginName
}

func pluginIsolatesNamespaces() bool {
	if os.Getenv("NETWORKING_E2E_ISOLATION") == "true" {
		return true
	}
	// Assume that only the OpenShift SDN "multitenant" plugin isolates by default
	return networkPluginName() == networkutils.MultiTenantPluginName
}

func pluginImplementsNetworkPolicy() bool {
	switch {
	case os.Getenv("NETWORKING_E2E_NETWORKPOLICY") == "true":
		return true
	case networkPluginName() == networkutils.NetworkPolicyPluginName:
		return true
	default:
		// If we can't detect the plugin, we assume it doesn't support
		// NetworkPolicy, so the tests will work under kubenet
		return false
	}
}

func makeNamespaceGlobal(oc *exutil.CLI, ns *corev1.Namespace) {
	clientConfig := oc.AdminConfig()
	networkClient := networkclient.NewForConfigOrDie(clientConfig)
	netns, err := networkClient.NetNamespaces().Get(context.Background(), ns.Name, metav1.GetOptions{})
	expectNoError(err)
	netns.NetID = 0
	_, err = networkClient.NetNamespaces().Update(context.Background(), netns, metav1.UpdateOptions{})
	expectNoError(err)
}

func makeNamespaceScheduleToAllNodes(f *e2e.Framework) {
	// to avoid hassles dealing with selector limits, set the namespace label selector to empty
	// to allow targeting all nodes
	for {
		ns, err := f.ClientSet.CoreV1().Namespaces().Get(context.Background(), f.Namespace.Name, metav1.GetOptions{})
		expectNoError(err)
		if ns.Annotations == nil {
			ns.Annotations = make(map[string]string)
		}
		ns.Annotations[projectv1.ProjectNodeSelector] = ""
		_, err = f.ClientSet.CoreV1().Namespaces().Update(context.Background(), ns, metav1.UpdateOptions{})
		if err == nil {
			return
		}
		if kapierrs.IsConflict(err) {
			continue
		}
		expectNoError(err)
	}
}

// findAppropriateNodes tries to find a source and destination for a type of node connectivity
// test (same node, or different node).
func findAppropriateNodes(f *e2e.Framework, nodeType NodeType) (*corev1.Node, *corev1.Node, error) {
	nodes, err := e2enode.GetReadySchedulableNodes(f.ClientSet)
	if err != nil {
		e2e.Logf("Unable to get schedulable nodes due to %v", err)
		return nil, nil, err
	}
	candidates := nodes.Items

	if len(candidates) == 0 {
		e2e.Failf("Unable to find any candidate nodes for e2e networking tests in \n%#v", nodes.Items)
	}

	// in general, avoiding masters is a good thing, so see if we can find nodes that aren't masters
	if len(candidates) > 1 {
		var withoutMasters []corev1.Node
		// look for anything that has the label value master or infra and try to skip it
		isAllowed := func(node *corev1.Node) bool {
			for _, value := range node.Labels {
				if value == "master" || value == "infra" {
					return false
				}
			}
			return true
		}
		for _, node := range candidates {
			if !isAllowed(&node) {
				continue
			}
			withoutMasters = append(withoutMasters, node)
		}
		if len(withoutMasters) >= 2 {
			candidates = withoutMasters
		}
	}

	var candidateNames, nodeNames []string
	for _, node := range candidates {
		candidateNames = append(candidateNames, node.Name)
	}
	for _, node := range nodes.Items {
		nodeNames = append(nodeNames, node.Name)
	}

	if nodeType == DIFFERENT_NODE {
		if len(candidates) <= 1 {
			e2eskipper.Skipf("Only one node is available in this environment (%v out of %v)", candidateNames, nodeNames)
		}
		e2e.Logf("Using %s and %s for test (%v out of %v)", candidates[0].Name, candidates[1].Name, candidateNames, nodeNames)
		return &candidates[0], &candidates[1], nil
	}
	e2e.Logf("Using %s for test (%v out of %v)", candidates[0].Name, candidateNames, nodeNames)
	return &candidates[0], &candidates[0], nil
}

func checkPodIsolation(f1, f2 *e2e.Framework, nodeType NodeType) error {
	makeNamespaceScheduleToAllNodes(f1)
	makeNamespaceScheduleToAllNodes(f2)
	serverNode, clientNode, err := findAppropriateNodes(f1, nodeType)
	if err != nil {
		return err
	}
	podName := "isolation-webserver"
	defer f1.ClientSet.CoreV1().Pods(f1.Namespace.Name).Delete(context.Background(), podName, metav1.DeleteOptions{})
	ip := exutil.LaunchWebserverPod(f1, podName, serverNode.Name)

	return checkConnectivityToHost(f2, clientNode.Name, "isolation-wget", ip, 10*time.Second)
}

func checkServiceConnectivity(serverFramework, clientFramework *e2e.Framework, nodeType NodeType) error {
	makeNamespaceScheduleToAllNodes(serverFramework)
	makeNamespaceScheduleToAllNodes(clientFramework)
	serverNode, clientNode, err := findAppropriateNodes(serverFramework, nodeType)
	if err != nil {
		return err
	}
	podName := names.SimpleNameGenerator.GenerateName("service-")
	defer serverFramework.ClientSet.CoreV1().Pods(serverFramework.Namespace.Name).Delete(context.Background(), podName, metav1.DeleteOptions{})
	defer serverFramework.ClientSet.CoreV1().Services(serverFramework.Namespace.Name).Delete(context.Background(), podName, metav1.DeleteOptions{})
	ip := launchWebserverService(serverFramework, podName, serverNode.Name)

	return checkConnectivityToHost(clientFramework, clientNode.Name, "service-wget", ip, 10*time.Second)
}

func InNonIsolatingContext(body func()) {
	Context("when using a plugin that does not isolate namespaces by default", func() {
		BeforeEach(func() {
			if pluginIsolatesNamespaces() {
				e2eskipper.Skipf("This plugin isolates namespaces by default.")
			}
		})

		body()
	})
}

func InIsolatingContext(body func()) {
	Context("when using a plugin that isolates namespaces by default", func() {
		BeforeEach(func() {
			if !pluginIsolatesNamespaces() {
				e2eskipper.Skipf("This plugin does not isolate namespaces by default.")
			}
		})

		body()
	})
}

func InNetworkPolicyContext(body func()) {
	Context("when using a plugin that implements NetworkPolicy", func() {
		BeforeEach(func() {
			if !pluginImplementsNetworkPolicy() {
				e2eskipper.Skipf("This plugin does not implement NetworkPolicy.")
			}
		})

		body()
	})
}

func InPluginContext(plugins []string, body func()) {
	Context(fmt.Sprintf("when using one of the plugins '%s'", strings.Join(plugins, ", ")),
		func() {
			BeforeEach(func() {
				found := false
				for _, plugin := range plugins {
					if networkPluginName() == plugin {
						found = true
						break
					}
				}
				if !found {
					e2eskipper.Skipf("Not using one of the specified plugins")
				}
			})

			body()
		},
	)
}

func InOpenShiftSDNContext(body func()) {
	Context("when using openshift-sdn",
		func() {
			BeforeEach(func() {
				if networkPluginName() == "" {
					e2eskipper.Skipf("Not using openshift-sdn")
				}
			})

			body()
		},
	)
}
<|endoftext|>