update vendor

Signed-off-by: Roland.Ma <rolandma@yunify.com>
This commit is contained in:
Roland.Ma
2021-08-11 07:10:14 +00:00
parent a18f72b565
commit ea8f47c73a
2901 changed files with 269317 additions and 43103 deletions

View File

@@ -0,0 +1,340 @@
/*
Copyright 2021 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 process
import (
"bytes"
"html/template"
"sort"
"strings"
)
// RenderTemplates returns an []string to render the templates
//
// Deprecated: will be removed in favor of Arguments.
func RenderTemplates(argTemplates []string, data interface{}) (args []string, err error) {
var t *template.Template
for _, arg := range argTemplates {
t, err = template.New(arg).Parse(arg)
if err != nil {
args = nil
return
}
buf := &bytes.Buffer{}
err = t.Execute(buf, data)
if err != nil {
args = nil
return
}
args = append(args, buf.String())
}
return
}
// SliceToArguments converts a slice of arguments to structured arguments,
// appending each argument that starts with `--` and contains an `=` to the
// argument set (ignoring defaults), returning the rest.
//
// Deprecated: will be removed when RenderTemplates is removed.
func SliceToArguments(sliceArgs []string, args *Arguments) []string {
var rest []string
for i, arg := range sliceArgs {
if arg == "--" {
rest = append(rest, sliceArgs[i:]...)
return rest
}
// skip non-flag arguments, skip arguments w/o equals because we
// can't tell if the next argument should take a value
if !strings.HasPrefix(arg, "--") || !strings.Contains(arg, "=") {
rest = append(rest, arg)
continue
}
parts := strings.SplitN(arg[2:], "=", 2)
name := parts[0]
val := parts[1]
args.AppendNoDefaults(name, val)
}
return rest
}
// TemplateDefaults specifies defaults to be used for joining structured arguments with templates.
//
// Deprecated: will be removed when RenderTemplates is removed.
type TemplateDefaults struct {
// Data will be used to render the template.
Data interface{}
// Defaults will be used to default structured arguments if no template is passed.
Defaults map[string][]string
// MinimalDefaults will be used to default structured arguments if a template is passed.
// Use this for flags which *must* be present.
MinimalDefaults map[string][]string // for api server service-cluster-ip-range
}
// TemplateAndArguments joins structured arguments and non-structured arguments, preserving existing
// behavior. Namely:
//
// 1. if templ has len > 0, it will be rendered against data
// 2. the rendered template values that look like `--foo=bar` will be split
// and appended to args, the rest will be kept around
// 3. the given args will be rendered as string form. If a template is given,
// no defaults will be used, otherwise defaults will be used
// 4. a result of [args..., rest...] will be returned
//
// It returns the resulting rendered arguments, plus the arguments that were
// not transferred to `args` during rendering.
//
// Deprecated: will be removed when RenderTemplates is removed.
func TemplateAndArguments(templ []string, args *Arguments, data TemplateDefaults) (allArgs []string, nonFlagishArgs []string, err error) {
if len(templ) == 0 { // 3 & 4 (no template case)
return args.AsStrings(data.Defaults), nil, nil
}
// 1: render the template
rendered, err := RenderTemplates(templ, data.Data)
if err != nil {
return nil, nil, err
}
// 2: filter out structured args and add them to args
rest := SliceToArguments(rendered, args)
// 3 (template case): render structured args, no defaults (matching the
// legacy case where if Args was specified, no defaults were used)
res := args.AsStrings(data.MinimalDefaults)
// 4: return the rendered structured args + all non-structured args
return append(res, rest...), rest, nil
}
// EmptyArguments constructs an empty set of flags with no defaults.
func EmptyArguments() *Arguments {
return &Arguments{
values: make(map[string]Arg),
}
}
// Arguments are structured, overridable arguments.
// Each Arguments object contains some set of default arguments, which may
// be appended to, or overridden.
//
// When ready, you can serialize them to pass to exec.Command and friends using
// AsStrings.
//
// All flag-setting methods return the *same* instance of Arguments so that you
// can chain calls.
type Arguments struct {
// values contains the user-set values for the arguments.
// `values[key] = dontPass` means "don't pass this flag"
// `values[key] = passAsName` means "pass this flag without args like --key`
// `values[key] = []string{a, b, c}` means "--key=a --key=b --key=c`
// any values not explicitly set here will be copied from defaults on final rendering.
values map[string]Arg
}
// Arg is an argument that has one or more values,
// and optionally falls back to default values.
type Arg interface {
// Append adds new values to this argument, returning
// a new instance contain the new value. The intermediate
// argument should generally be assumed to be consumed.
Append(vals ...string) Arg
// Get returns the full set of values, optionally including
// the passed in defaults. If it returns nil, this will be
// skipped. If it returns a non-nil empty slice, it'll be
// assumed that the argument should be passed as name-only.
Get(defaults []string) []string
}
type userArg []string
func (a userArg) Append(vals ...string) Arg {
return userArg(append(a, vals...)) //nolint:unconvert
}
func (a userArg) Get(_ []string) []string {
return []string(a)
}
type defaultedArg []string
func (a defaultedArg) Append(vals ...string) Arg {
return defaultedArg(append(a, vals...)) //nolint:unconvert
}
func (a defaultedArg) Get(defaults []string) []string {
res := append([]string(nil), defaults...)
return append(res, a...)
}
type dontPassArg struct{}
func (a dontPassArg) Append(vals ...string) Arg {
return userArg(vals)
}
func (dontPassArg) Get(_ []string) []string {
return nil
}
type passAsNameArg struct{}
func (a passAsNameArg) Append(_ ...string) Arg {
return passAsNameArg{}
}
func (passAsNameArg) Get(_ []string) []string {
return []string{}
}
var (
// DontPass indicates that the given argument will not actually be
// rendered.
DontPass Arg = dontPassArg{}
// PassAsName indicates that the given flag will be passed as `--key`
// without any value.
PassAsName Arg = passAsNameArg{}
)
// AsStrings serializes this set of arguments to a slice of strings appropriate
// for passing to exec.Command and friends, making use of the given defaults
// as indicated for each particular argument.
//
// - Any flag in defaults that's not in Arguments will be present in the output
// - Any flag that's present in Arguments will be passed the corresponding
// defaults to do with as it will (ignore, append-to, suppress, etc).
func (a *Arguments) AsStrings(defaults map[string][]string) []string {
// sort for deterministic ordering
keysInOrder := make([]string, 0, len(defaults)+len(a.values))
for key := range defaults {
if _, userSet := a.values[key]; userSet {
continue
}
keysInOrder = append(keysInOrder, key)
}
for key := range a.values {
keysInOrder = append(keysInOrder, key)
}
sort.Strings(keysInOrder)
var res []string
for _, key := range keysInOrder {
vals := a.Get(key).Get(defaults[key])
switch {
case vals == nil: // don't pass
continue
case len(vals) == 0: // pass as name
res = append(res, "--"+key)
default:
for _, val := range vals {
res = append(res, "--"+key+"="+val)
}
}
}
return res
}
// Get returns the value of the given flag. If nil,
// it will not be passed in AsString, otherwise:
//
// len == 0 --> `--key`, len > 0 --> `--key=val1 --key=val2 ...`.
func (a *Arguments) Get(key string) Arg {
if vals, ok := a.values[key]; ok {
return vals
}
return defaultedArg(nil)
}
// Enable configures the given key to be passed as a "name-only" flag,
// like, `--key`.
func (a *Arguments) Enable(key string) *Arguments {
a.values[key] = PassAsName
return a
}
// Disable prevents this flag from be passed.
func (a *Arguments) Disable(key string) *Arguments {
a.values[key] = DontPass
return a
}
// Append adds additional values to this flag. If this flag has
// yet to be set, initial values will include defaults. If you want
// to intentionally ignore defaults/start from scratch, call AppendNoDefaults.
//
// Multiple values will look like `--key=value1 --key=value2 ...`.
func (a *Arguments) Append(key string, values ...string) *Arguments {
vals, present := a.values[key]
if !present {
vals = defaultedArg{}
}
a.values[key] = vals.Append(values...)
return a
}
// AppendNoDefaults adds additional values to this flag. However,
// unlike Append, it will *not* copy values from defaults.
func (a *Arguments) AppendNoDefaults(key string, values ...string) *Arguments {
vals, present := a.values[key]
if !present {
vals = userArg{}
}
a.values[key] = vals.Append(values...)
return a
}
// Set resets the given flag to the specified values, ignoring any existing
// values or defaults.
func (a *Arguments) Set(key string, values ...string) *Arguments {
a.values[key] = userArg(values)
return a
}
// SetRaw sets the given flag to the given Arg value directly. Use this if
// you need to do some complicated deferred logic or something.
//
// Otherwise behaves like Set.
func (a *Arguments) SetRaw(key string, val Arg) *Arguments {
a.values[key] = val
return a
}
// FuncArg is a basic implementation of Arg that can be used for custom argument logic,
// like pulling values out of APIServer, or dynamically calculating values just before
// launch.
//
// The given function will be mapped directly to Arg#Get, and will generally be
// used in conjunction with SetRaw. For example, to set `--some-flag` to the
// API server's CertDir, you could do:
//
// server.Configure().SetRaw("--some-flag", FuncArg(func(defaults []string) []string {
// return []string{server.CertDir}
// }))
//
// FuncArg ignores Appends; if you need to support appending values too, consider implementing
// Arg directly.
type FuncArg func([]string) []string
// Append is a no-op for FuncArg, and just returns itself.
func (a FuncArg) Append(vals ...string) Arg { return a }
// Get delegates functionality to the FuncArg function itself.
func (a FuncArg) Get(defaults []string) []string {
return a(defaults)
}

View File

@@ -0,0 +1,70 @@
/*
Copyright 2021 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 process
import (
"os"
"path/filepath"
"regexp"
"strings"
)
const (
// EnvAssetsPath is the environment variable that stores the global test
// binary location override.
EnvAssetsPath = "KUBEBUILDER_ASSETS"
// EnvAssetOverridePrefix is the environment variable prefix for per-binary
// location overrides.
EnvAssetOverridePrefix = "TEST_ASSET_"
// AssetsDefaultPath is the default location to look for test binaries in,
// if no override was provided.
AssetsDefaultPath = "/usr/local/kubebuilder/bin"
)
// BinPathFinder finds the path to the given named binary, using the following locations
// in order of precedence (highest first). Notice that the various env vars only need
// to be set -- the asset is not checked for existence on the filesystem.
//
// 1. TEST_ASSET_{tr/a-z-/A-Z_/} (if set; asset overrides -- EnvAssetOverridePrefix)
// 1. KUBEBUILDER_ASSETS (if set; global asset path -- EnvAssetsPath)
// 3. assetDirectory (if set; per-config asset directory)
// 4. /usr/local/kubebuilder/bin (AssetsDefaultPath).
func BinPathFinder(symbolicName, assetDirectory string) (binPath string) {
punctuationPattern := regexp.MustCompile("[^A-Z0-9]+")
sanitizedName := punctuationPattern.ReplaceAllString(strings.ToUpper(symbolicName), "_")
leadingNumberPattern := regexp.MustCompile("^[0-9]+")
sanitizedName = leadingNumberPattern.ReplaceAllString(sanitizedName, "")
envVar := EnvAssetOverridePrefix + sanitizedName
// TEST_ASSET_XYZ
if val, ok := os.LookupEnv(envVar); ok {
return val
}
// KUBEBUILDER_ASSETS
if val, ok := os.LookupEnv(EnvAssetsPath); ok {
return filepath.Join(val, symbolicName)
}
// assetDirectory
if assetDirectory != "" {
return filepath.Join(assetDirectory, symbolicName)
}
// default path
return filepath.Join(AssetsDefaultPath, symbolicName)
}

View File

@@ -0,0 +1,277 @@
/*
Copyright 2021 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 process
import (
"crypto/tls"
"fmt"
"io"
"io/ioutil"
"net"
"net/http"
"net/url"
"os"
"os/exec"
"path"
"regexp"
"sync"
"syscall"
"time"
)
// ListenAddr represents some listening address and port.
type ListenAddr struct {
Address string
Port string
}
// URL returns a URL for this address with the given scheme and subpath.
func (l *ListenAddr) URL(scheme string, path string) *url.URL {
return &url.URL{
Scheme: scheme,
Host: l.HostPort(),
Path: path,
}
}
// HostPort returns the joined host-port pair for this address.
func (l *ListenAddr) HostPort() string {
return net.JoinHostPort(l.Address, l.Port)
}
// HealthCheck describes the information needed to health-check a process via
// some health-check URL.
type HealthCheck struct {
url.URL
// HealthCheckPollInterval is the interval which will be used for polling the
// endpoint described by Host, Port, and Path.
//
// If left empty it will default to 100 Milliseconds.
PollInterval time.Duration
}
// State define the state of the process.
type State struct {
Cmd *exec.Cmd
// HealthCheck describes how to check if this process is up. If we get an http.StatusOK,
// we assume the process is ready to operate.
//
// For example, the /healthz endpoint of the k8s API server, or the /health endpoint of etcd.
HealthCheck HealthCheck
Args []string
StopTimeout time.Duration
StartTimeout time.Duration
Dir string
DirNeedsCleaning bool
Path string
// ready holds wether the process is currently in ready state (hit the ready condition) or not.
// It will be set to true on a successful `Start()` and set to false on a successful `Stop()`
ready bool
// waitDone is closed when our call to wait finishes up, and indicates that
// our process has terminated.
waitDone chan struct{}
errMu sync.Mutex
exitErr error
exited bool
}
// Init sets up this process, configuring binary paths if missing, initializing
// temporary directories, etc.
//
// This defaults all defaultable fields.
func (ps *State) Init(name string) error {
if ps.Path == "" {
if name == "" {
return fmt.Errorf("must have at least one of name or path")
}
ps.Path = BinPathFinder(name, "")
}
if ps.Dir == "" {
newDir, err := ioutil.TempDir("", "k8s_test_framework_")
if err != nil {
return err
}
ps.Dir = newDir
ps.DirNeedsCleaning = true
}
if ps.StartTimeout == 0 {
ps.StartTimeout = 20 * time.Second
}
if ps.StopTimeout == 0 {
ps.StopTimeout = 20 * time.Second
}
return nil
}
type stopChannel chan struct{}
// CheckFlag checks the help output of this command for the presence of the given flag, specified
// without the leading `--` (e.g. `CheckFlag("insecure-port")` checks for `--insecure-port`),
// returning true if the flag is present.
func (ps *State) CheckFlag(flag string) (bool, error) {
cmd := exec.Command(ps.Path, "--help")
outContents, err := cmd.CombinedOutput()
if err != nil {
return false, fmt.Errorf("unable to run command %q to check for flag %q: %w", ps.Path, flag, err)
}
pat := `(?m)^\s*--` + flag + `\b` // (m --> multi-line --> ^ matches start of line)
matched, err := regexp.Match(pat, outContents)
if err != nil {
return false, fmt.Errorf("unable to check command %q for flag %q in help output: %w", ps.Path, flag, err)
}
return matched, nil
}
// Start starts the apiserver, waits for it to come up, and returns an error,
// if occurred.
func (ps *State) Start(stdout, stderr io.Writer) (err error) {
if ps.ready {
return nil
}
ps.Cmd = exec.Command(ps.Path, ps.Args...)
ps.Cmd.Stdout = stdout
ps.Cmd.Stderr = stderr
ready := make(chan bool)
timedOut := time.After(ps.StartTimeout)
pollerStopCh := make(stopChannel)
go pollURLUntilOK(ps.HealthCheck.URL, ps.HealthCheck.PollInterval, ready, pollerStopCh)
ps.waitDone = make(chan struct{})
if err := ps.Cmd.Start(); err != nil {
ps.errMu.Lock()
defer ps.errMu.Unlock()
ps.exited = true
return err
}
go func() {
defer close(ps.waitDone)
err := ps.Cmd.Wait()
ps.errMu.Lock()
defer ps.errMu.Unlock()
ps.exitErr = err
ps.exited = true
}()
select {
case <-ready:
ps.ready = true
return nil
case <-ps.waitDone:
if pollerStopCh != nil {
close(pollerStopCh)
}
return fmt.Errorf("timeout waiting for process %s to start successfully "+
"(it may have failed to start, or stopped unexpectedly before becoming ready)",
path.Base(ps.Path))
case <-timedOut:
if pollerStopCh != nil {
close(pollerStopCh)
}
if ps.Cmd != nil {
// intentionally ignore this -- we might've crashed, failed to start, etc
ps.Cmd.Process.Signal(syscall.SIGTERM) //nolint:errcheck
}
return fmt.Errorf("timeout waiting for process %s to start", path.Base(ps.Path))
}
}
// Exited returns true if the process exited, and may also
// return an error (as per Cmd.Wait) if the process did not
// exit with error code 0.
func (ps *State) Exited() (bool, error) {
ps.errMu.Lock()
defer ps.errMu.Unlock()
return ps.exited, ps.exitErr
}
func pollURLUntilOK(url url.URL, interval time.Duration, ready chan bool, stopCh stopChannel) {
client := &http.Client{
Transport: &http.Transport{
TLSClientConfig: &tls.Config{
// there's probably certs *somewhere*,
// but it's fine to just skip validating
// them for health checks during testing
InsecureSkipVerify: true, //nolint:gosec
},
},
}
if interval <= 0 {
interval = 100 * time.Millisecond
}
for {
res, err := client.Get(url.String())
if err == nil {
res.Body.Close()
if res.StatusCode == http.StatusOK {
ready <- true
return
}
}
select {
case <-stopCh:
return
default:
time.Sleep(interval)
}
}
}
// Stop stops this process gracefully, waits for its termination, and cleans up
// the CertDir if necessary.
func (ps *State) Stop() error {
// Always clear the directory if we need to.
defer func() {
if ps.DirNeedsCleaning {
_ = os.RemoveAll(ps.Dir)
}
}()
if ps.Cmd == nil {
return nil
}
if done, _ := ps.Exited(); done {
return nil
}
if err := ps.Cmd.Process.Signal(syscall.SIGTERM); err != nil {
return fmt.Errorf("unable to signal for process %s to stop: %w", ps.Path, err)
}
timedOut := time.After(ps.StopTimeout)
select {
case <-ps.waitDone:
break
case <-timedOut:
return fmt.Errorf("timeout waiting for process %s to stop", path.Base(ps.Path))
}
ps.ready = false
return nil
}