build: move e2e dependencies into e2e/go.mod

Several packages are only used while running the e2e suite. These
packages are less important to update, as the they can not influence the
final executable that is part of the Ceph-CSI container-image.

By moving these dependencies out of the main Ceph-CSI go.mod, it is
easier to identify if a reported CVE affects Ceph-CSI, or only the
testing (like most of the Kubernetes CVEs).

Signed-off-by: Niels de Vos <ndevos@ibm.com>
This commit is contained in:
Niels de Vos
2025-03-04 08:57:28 +01:00
committed by mergify[bot]
parent 15da101b1b
commit bec6090996
8047 changed files with 1407827 additions and 3453 deletions

19
e2e/vendor/k8s.io/cri-client/pkg/doc.go generated vendored Normal file
View File

@ -0,0 +1,19 @@
/*
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 remote contains gRPC implementation of internalapi.RuntimeService
// and internalapi.ImageManagerService.
package cri

33
e2e/vendor/k8s.io/cri-client/pkg/internal/log.go generated vendored Normal file
View File

@ -0,0 +1,33 @@
/*
Copyright 2024 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 internal
import "k8s.io/klog/v2"
func Log(logger *klog.Logger, level int, msg string, keyAndValues ...any) {
if logger == nil {
return
}
logger.V(level).Info(msg, keyAndValues...)
}
func LogErr(logger *klog.Logger, err error, msg string, keyAndValues ...any) {
if logger == nil {
return
}
logger.Error(err, msg, keyAndValues...)
}

487
e2e/vendor/k8s.io/cri-client/pkg/logs/logs.go generated vendored Normal file
View File

@ -0,0 +1,487 @@
/*
Copyright 2017 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 logs
import (
"bufio"
"bytes"
"context"
"encoding/json"
"errors"
"fmt"
"io"
"math"
"os"
"path/filepath"
"time"
"github.com/fsnotify/fsnotify"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
v1 "k8s.io/api/core/v1"
internalapi "k8s.io/cri-api/pkg/apis"
runtimeapi "k8s.io/cri-api/pkg/apis/runtime/v1"
"k8s.io/klog/v2"
remote "k8s.io/cri-client/pkg"
"k8s.io/cri-client/pkg/internal"
)
// Notice that the current CRI logs implementation doesn't handle
// log rotation.
// * It will not retrieve logs in rotated log file.
// * If log rotation happens when following the log:
// * If the rotation is using create mode, we'll still follow the old file.
// * If the rotation is using copytruncate, we'll be reading at the original position and get nothing.
const (
// RFC3339NanoFixed is the fixed width version of time.RFC3339Nano.
RFC3339NanoFixed = "2006-01-02T15:04:05.000000000Z07:00"
// RFC3339NanoLenient is the variable width RFC3339 time format for lenient parsing of strings into timestamps.
RFC3339NanoLenient = "2006-01-02T15:04:05.999999999Z07:00"
// timeFormatOut is the format for writing timestamps to output.
timeFormatOut = RFC3339NanoFixed
// timeFormatIn is the format for parsing timestamps from other logs.
timeFormatIn = RFC3339NanoLenient
// logForceCheckPeriod is the period to check for a new read
logForceCheckPeriod = 1 * time.Second
)
var (
// eol is the end-of-line sign in the log.
eol = []byte{'\n'}
// delimiter is the delimiter for timestamp and stream type in log line.
delimiter = []byte{' '}
// tagDelimiter is the delimiter for log tags.
tagDelimiter = []byte(runtimeapi.LogTagDelimiter)
)
// logMessage is the CRI internal log type.
type logMessage struct {
timestamp time.Time
stream runtimeapi.LogStreamType
log []byte
}
// reset resets the log to nil.
func (l *logMessage) reset() {
l.timestamp = time.Time{}
l.stream = ""
l.log = nil
}
// LogOptions is the CRI internal type of all log options.
type LogOptions struct {
tail int64
bytes int64
since time.Time
follow bool
timestamp bool
}
// NewLogOptions convert the v1.PodLogOptions to CRI internal LogOptions.
func NewLogOptions(apiOpts *v1.PodLogOptions, now time.Time) *LogOptions {
opts := &LogOptions{
tail: -1, // -1 by default which means read all logs.
bytes: -1, // -1 by default which means read all logs.
follow: apiOpts.Follow,
timestamp: apiOpts.Timestamps,
}
if apiOpts.TailLines != nil {
opts.tail = *apiOpts.TailLines
}
if apiOpts.LimitBytes != nil {
opts.bytes = *apiOpts.LimitBytes
}
if apiOpts.SinceSeconds != nil {
opts.since = now.Add(-time.Duration(*apiOpts.SinceSeconds) * time.Second)
}
if apiOpts.SinceTime != nil && apiOpts.SinceTime.After(opts.since) {
opts.since = apiOpts.SinceTime.Time
}
return opts
}
// parseFunc is a function parsing one log line to the internal log type.
// Notice that the caller must make sure logMessage is not nil.
type parseFunc func([]byte, *logMessage) error
var parseFuncs = []parseFunc{
parseCRILog, // CRI log format parse function
parseDockerJSONLog, // Docker JSON log format parse function
}
// parseCRILog parses logs in CRI log format. CRI Log format example:
//
// 2016-10-06T00:17:09.669794202Z stdout P log content 1
// 2016-10-06T00:17:09.669794203Z stderr F log content 2
func parseCRILog(log []byte, msg *logMessage) error {
var err error
// Parse timestamp
idx := bytes.Index(log, delimiter)
if idx < 0 {
return fmt.Errorf("timestamp is not found")
}
msg.timestamp, err = time.Parse(timeFormatIn, string(log[:idx]))
if err != nil {
return fmt.Errorf("unexpected timestamp format %q: %v", timeFormatIn, err)
}
// Parse stream type
log = log[idx+1:]
idx = bytes.Index(log, delimiter)
if idx < 0 {
return fmt.Errorf("stream type is not found")
}
msg.stream = runtimeapi.LogStreamType(log[:idx])
if msg.stream != runtimeapi.Stdout && msg.stream != runtimeapi.Stderr {
return fmt.Errorf("unexpected stream type %q", msg.stream)
}
// Parse log tag
log = log[idx+1:]
idx = bytes.Index(log, delimiter)
if idx < 0 {
return fmt.Errorf("log tag is not found")
}
// Keep this forward compatible.
tags := bytes.Split(log[:idx], tagDelimiter)
partial := runtimeapi.LogTag(tags[0]) == runtimeapi.LogTagPartial
// Trim the tailing new line if this is a partial line.
if partial && len(log) > 0 && log[len(log)-1] == '\n' {
log = log[:len(log)-1]
}
// Get log content
msg.log = log[idx+1:]
return nil
}
// jsonLog is a log message, typically a single entry from a given log stream.
// since the data structure is originally from docker, we should be careful to
// with any changes to jsonLog
type jsonLog struct {
// Log is the log message
Log string `json:"log,omitempty"`
// Stream is the log source
Stream string `json:"stream,omitempty"`
// Created is the created timestamp of log
Created time.Time `json:"time"`
}
// parseDockerJSONLog parses logs in Docker JSON log format. Docker JSON log format
// example:
//
// {"log":"content 1","stream":"stdout","time":"2016-10-20T18:39:20.57606443Z"}
// {"log":"content 2","stream":"stderr","time":"2016-10-20T18:39:20.57606444Z"}
func parseDockerJSONLog(log []byte, msg *logMessage) error {
var l = &jsonLog{}
// TODO: JSON decoding is fairly expensive, we should evaluate this.
if err := json.Unmarshal(log, l); err != nil {
return fmt.Errorf("failed with %v to unmarshal log %q", err, l)
}
msg.timestamp = l.Created
msg.stream = runtimeapi.LogStreamType(l.Stream)
msg.log = []byte(l.Log)
return nil
}
// getParseFunc returns proper parse function based on the sample log line passed in.
func getParseFunc(log []byte) (parseFunc, error) {
for _, p := range parseFuncs {
if err := p(log, &logMessage{}); err == nil {
return p, nil
}
}
return nil, fmt.Errorf("unsupported log format: %q", log)
}
// logWriter controls the writing into the stream based on the log options.
type logWriter struct {
stdout io.Writer
stderr io.Writer
opts *LogOptions
remain int64
}
// errMaximumWrite is returned when all bytes have been written.
var errMaximumWrite = errors.New("maximum write")
// errShortWrite is returned when the message is not fully written.
var errShortWrite = errors.New("short write")
func newLogWriter(stdout io.Writer, stderr io.Writer, opts *LogOptions) *logWriter {
w := &logWriter{
stdout: stdout,
stderr: stderr,
opts: opts,
remain: math.MaxInt64, // initialize it as infinity
}
if opts.bytes >= 0 {
w.remain = opts.bytes
}
return w
}
// writeLogs writes logs into stdout, stderr.
func (w *logWriter) write(msg *logMessage, addPrefix bool) error {
if msg.timestamp.Before(w.opts.since) {
// Skip the line because it's older than since
return nil
}
line := msg.log
if w.opts.timestamp && addPrefix {
prefix := append([]byte(msg.timestamp.Format(timeFormatOut)), delimiter[0])
line = append(prefix, line...)
}
// If the line is longer than the remaining bytes, cut it.
if int64(len(line)) > w.remain {
line = line[:w.remain]
}
// Get the proper stream to write to.
var stream io.Writer
switch msg.stream {
case runtimeapi.Stdout:
stream = w.stdout
case runtimeapi.Stderr:
stream = w.stderr
default:
return fmt.Errorf("unexpected stream type %q", msg.stream)
}
// Since v1.32, either w.stdout or w.stderr may be nil if the stream is not requested.
// In such case, we should neither count the bytes nor write to the stream.
if stream == nil {
return nil
}
n, err := stream.Write(line)
w.remain -= int64(n)
if err != nil {
return err
}
// If the line has not been fully written, return errShortWrite
if n < len(line) {
return errShortWrite
}
// If there are no more bytes left, return errMaximumWrite
if w.remain <= 0 {
return errMaximumWrite
}
return nil
}
// ReadLogs read the container log and redirect into stdout and stderr.
// Note that containerID is only needed when following the log, or else
// just pass in empty string "".
func ReadLogs(ctx context.Context, logger *klog.Logger, path, containerID string, opts *LogOptions, runtimeService internalapi.RuntimeService, stdout, stderr io.Writer) error {
// fsnotify has different behavior for symlinks in different platform,
// for example it follows symlink on Linux, but not on Windows,
// so we explicitly resolve symlinks before reading the logs.
// There shouldn't be security issue because the container log
// path is owned by kubelet and the container runtime.
evaluated, err := filepath.EvalSymlinks(path)
if err != nil {
return fmt.Errorf("failed to try resolving symlinks in path %q: %v", path, err)
}
path = evaluated
f, err := openFileShareDelete(path)
if err != nil {
return fmt.Errorf("failed to open log file %q: %v", path, err)
}
defer f.Close()
// Search start point based on tail line.
start, err := findTailLineStartIndex(f, opts.tail)
if err != nil {
return fmt.Errorf("failed to tail %d lines of log file %q: %v", opts.tail, path, err)
}
if _, err := f.Seek(start, io.SeekStart); err != nil {
return fmt.Errorf("failed to seek %d in log file %q: %v", start, path, err)
}
limitedMode := (opts.tail >= 0) && (!opts.follow)
limitedNum := opts.tail
// Start parsing the logs.
r := bufio.NewReader(f)
// Do not create watcher here because it is not needed if `Follow` is false.
var watcher *fsnotify.Watcher
var parse parseFunc
var stop bool
isNewLine := true
found := true
writer := newLogWriter(stdout, stderr, opts)
msg := &logMessage{}
baseName := filepath.Base(path)
dir := filepath.Dir(path)
for {
if stop || (limitedMode && limitedNum == 0) {
internal.Log(logger, 2, "Finished parsing log file", "path", path)
return nil
}
l, err := r.ReadBytes(eol[0])
if err != nil {
if err != io.EOF { // This is an real error
return fmt.Errorf("failed to read log file %q: %v", path, err)
}
if opts.follow {
// The container is not running, we got to the end of the log.
if !found {
return nil
}
// Reset seek so that if this is an incomplete line,
// it will be read again.
if _, err := f.Seek(-int64(len(l)), io.SeekCurrent); err != nil {
return fmt.Errorf("failed to reset seek in log file %q: %v", path, err)
}
if watcher == nil {
// Initialize the watcher if it has not been initialized yet.
if watcher, err = fsnotify.NewWatcher(); err != nil {
return fmt.Errorf("failed to create fsnotify watcher: %v", err)
}
defer watcher.Close()
if err := watcher.Add(dir); err != nil {
return fmt.Errorf("failed to watch directory %q: %w", dir, err)
}
// If we just created the watcher, try again to read as we might have missed
// the event.
continue
}
var recreated bool
// Wait until the next log change.
found, recreated, err = waitLogs(ctx, logger, containerID, baseName, watcher, runtimeService)
if err != nil {
return err
}
if recreated {
newF, err := openFileShareDelete(path)
if err != nil {
if os.IsNotExist(err) {
continue
}
return fmt.Errorf("failed to open log file %q: %v", path, err)
}
defer newF.Close()
f.Close()
f = newF
r = bufio.NewReader(f)
}
// If the container exited consume data until the next EOF
continue
}
// Should stop after writing the remaining content.
stop = true
if len(l) == 0 {
continue
}
internal.Log(logger, 0, "Incomplete line in log file", "path", path, "line", l)
}
if parse == nil {
// Initialize the log parsing function.
parse, err = getParseFunc(l)
if err != nil {
return fmt.Errorf("failed to get parse function: %v", err)
}
}
// Parse the log line.
msg.reset()
if err := parse(l, msg); err != nil {
internal.LogErr(logger, err, "Failed when parsing line in log file", "path", path, "line", l)
continue
}
// Write the log line into the stream.
if err := writer.write(msg, isNewLine); err != nil {
if err == errMaximumWrite {
internal.Log(logger, 2, "Finished parsing log file, hit bytes limit", "path", path, "limit", opts.bytes)
return nil
}
internal.LogErr(logger, err, "Failed when writing line to log file", "path", path, "line", msg)
return err
}
if limitedMode {
limitedNum--
}
if len(msg.log) > 0 {
isNewLine = msg.log[len(msg.log)-1] == eol[0]
} else {
isNewLine = true
}
}
}
func isContainerRunning(ctx context.Context, logger *klog.Logger, id string, r internalapi.RuntimeService) (bool, error) {
resp, err := r.ContainerStatus(ctx, id, false)
if err != nil {
// Assume that the container is still running when the runtime is
// unavailable. Most runtimes support that containers can be in running
// state even if their CRI server is not available right now.
if status.Code(err) == codes.Unavailable {
return true, nil
}
return false, err
}
status := resp.GetStatus()
if status == nil {
return false, remote.ErrContainerStatusNil
}
// Only keep following container log when it is running.
if status.State != runtimeapi.ContainerState_CONTAINER_RUNNING {
internal.Log(logger, 5, "Container is not running", "containerId", id, "state", status.State)
// Do not return error because it's normal that the container stops
// during waiting.
return false, nil
}
return true, nil
}
// waitLogs wait for the next log write. It returns two booleans and an error. The first boolean
// indicates whether a new log is found; the second boolean if the log file was recreated;
// the error is error happens during waiting new logs.
func waitLogs(ctx context.Context, logger *klog.Logger, id string, logName string, w *fsnotify.Watcher, runtimeService internalapi.RuntimeService) (bool, bool, error) {
// no need to wait if the pod is not running
if running, err := isContainerRunning(ctx, logger, id, runtimeService); !running {
return false, false, err
}
errRetry := 5
for {
select {
case <-ctx.Done():
return false, false, fmt.Errorf("context cancelled")
case e := <-w.Events:
switch e.Op {
case fsnotify.Write, fsnotify.Rename, fsnotify.Remove, fsnotify.Chmod:
return true, false, nil
case fsnotify.Create:
return true, filepath.Base(e.Name) == logName, nil
default:
internal.LogErr(logger, nil, "Received unexpected fsnotify event, retrying", "event", e)
}
case err := <-w.Errors:
internal.LogErr(logger, err, "Received fsnotify watch error, retrying unless no more retries left", "retries", errRetry)
if errRetry == 0 {
return false, false, err
}
errRetry--
case <-time.After(logForceCheckPeriod):
return true, false, nil
}
}
}

29
e2e/vendor/k8s.io/cri-client/pkg/logs/logs_other.go generated vendored Normal file
View File

@ -0,0 +1,29 @@
//go:build !windows
// +build !windows
/*
Copyright 2024 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 logs
import (
"os"
)
func openFileShareDelete(path string) (*os.File, error) {
// Noop. Only relevant for Windows.
return os.Open(path)
}

49
e2e/vendor/k8s.io/cri-client/pkg/logs/logs_windows.go generated vendored Normal file
View File

@ -0,0 +1,49 @@
//go:build windows
// +build windows
/*
Copyright 2024 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 logs
import (
"os"
"syscall"
)
// Based on Windows implementation of Windows' syscall.Open
// https://cs.opensource.google/go/go/+/refs/tags/go1.22.2:src/syscall/syscall_windows.go;l=342
// In addition to syscall.Open, this function also adds the syscall.FILE_SHARE_DELETE flag to sharemode,
// which will allow us to read from the file without blocking the file from being deleted or renamed.
// This is essential for Log Rotation which is done by renaming the open file. Without this, the file rename would fail.
func openFileShareDelete(path string) (*os.File, error) {
pathp, err := syscall.UTF16PtrFromString(path)
if err != nil {
return nil, err
}
var access uint32 = syscall.GENERIC_READ
var sharemode uint32 = syscall.FILE_SHARE_READ | syscall.FILE_SHARE_WRITE | syscall.FILE_SHARE_DELETE
var createmode uint32 = syscall.OPEN_EXISTING
var attrs uint32 = syscall.FILE_ATTRIBUTE_NORMAL
handle, err := syscall.CreateFile(pathp, access, sharemode, nil, createmode, attrs, 0)
if err != nil {
return nil, err
}
return os.NewFile(uintptr(handle), path), nil
}

62
e2e/vendor/k8s.io/cri-client/pkg/logs/tail.go generated vendored Normal file
View File

@ -0,0 +1,62 @@
/*
Copyright 2024 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 logs
import (
"bytes"
"io"
)
// blockSize is the block size used in tail.
const blockSize = 1024
// findTailLineStartIndex returns the start of last nth line.
// * If n < 0, return the beginning of the file.
// * If n >= 0, return the beginning of last nth line.
// Notice that if the last line is incomplete (no end-of-line), it will not be counted
// as one line.
func findTailLineStartIndex(f io.ReadSeeker, n int64) (int64, error) {
if n < 0 {
return 0, nil
}
size, err := f.Seek(0, io.SeekEnd)
if err != nil {
return 0, err
}
var left, cnt int64
buf := make([]byte, blockSize)
for right := size; right > 0 && cnt <= n; right -= blockSize {
left = right - blockSize
if left < 0 {
left = 0
buf = make([]byte, right)
}
if _, err := f.Seek(left, io.SeekStart); err != nil {
return 0, err
}
if _, err := f.Read(buf); err != nil {
return 0, err
}
cnt += int64(bytes.Count(buf, eol))
}
for ; cnt > n; cnt-- {
idx := bytes.Index(buf, eol) + 1
buf = buf[idx:]
left += int64(idx)
}
return left, nil
}

248
e2e/vendor/k8s.io/cri-client/pkg/remote_image.go generated vendored Normal file
View File

@ -0,0 +1,248 @@
/*
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 cri
import (
"context"
"errors"
"fmt"
"time"
"go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc"
"go.opentelemetry.io/otel/trace"
"google.golang.org/grpc"
"google.golang.org/grpc/backoff"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/credentials/insecure"
"google.golang.org/grpc/status"
tracing "k8s.io/component-base/tracing"
internalapi "k8s.io/cri-api/pkg/apis"
runtimeapi "k8s.io/cri-api/pkg/apis/runtime/v1"
"k8s.io/klog/v2"
"k8s.io/cri-client/pkg/internal"
"k8s.io/cri-client/pkg/util"
)
// remoteImageService is a gRPC implementation of internalapi.ImageManagerService.
type remoteImageService struct {
timeout time.Duration
imageClient runtimeapi.ImageServiceClient
logger *klog.Logger
}
// NewRemoteImageService creates a new internalapi.ImageManagerService.
func NewRemoteImageService(endpoint string, connectionTimeout time.Duration, tp trace.TracerProvider, logger *klog.Logger) (internalapi.ImageManagerService, error) {
internal.Log(logger, 3, "Connecting to image service", "endpoint", endpoint)
addr, dialer, err := util.GetAddressAndDialer(endpoint)
if err != nil {
return nil, err
}
ctx, cancel := context.WithTimeout(context.Background(), connectionTimeout)
defer cancel()
var dialOpts []grpc.DialOption
dialOpts = append(dialOpts,
grpc.WithTransportCredentials(insecure.NewCredentials()),
grpc.WithAuthority("localhost"),
grpc.WithContextDialer(dialer),
grpc.WithDefaultCallOptions(grpc.MaxCallRecvMsgSize(maxMsgSize)))
if tp != nil {
tracingOpts := []otelgrpc.Option{
otelgrpc.WithMessageEvents(otelgrpc.ReceivedEvents, otelgrpc.SentEvents),
otelgrpc.WithPropagators(tracing.Propagators()),
otelgrpc.WithTracerProvider(tp),
}
// Even if there is no TracerProvider, the otelgrpc still handles context propagation.
// See https://github.com/open-telemetry/opentelemetry-go/tree/main/example/passthrough
dialOpts = append(dialOpts,
grpc.WithUnaryInterceptor(otelgrpc.UnaryClientInterceptor(tracingOpts...)),
grpc.WithStreamInterceptor(otelgrpc.StreamClientInterceptor(tracingOpts...)))
}
connParams := grpc.ConnectParams{
Backoff: backoff.DefaultConfig,
}
connParams.MinConnectTimeout = minConnectionTimeout
connParams.Backoff.BaseDelay = baseBackoffDelay
connParams.Backoff.MaxDelay = maxBackoffDelay
dialOpts = append(dialOpts,
grpc.WithConnectParams(connParams),
)
conn, err := grpc.DialContext(ctx, addr, dialOpts...)
if err != nil {
internal.LogErr(logger, err, "Connect remote image service failed", "address", addr)
return nil, err
}
service := &remoteImageService{
timeout: connectionTimeout,
logger: logger,
}
if err := service.validateServiceConnection(ctx, conn, endpoint); err != nil {
return nil, fmt.Errorf("validate service connection: %w", err)
}
return service, nil
}
func (r *remoteImageService) log(level int, msg string, keyAndValues ...any) {
internal.Log(r.logger, level, msg, keyAndValues...)
}
func (r *remoteImageService) logErr(err error, msg string, keyAndValues ...any) {
internal.LogErr(r.logger, err, msg, keyAndValues...)
}
// validateServiceConnection tries to connect to the remote image service by
// using the CRI v1 API version and fails if that's not possible.
func (r *remoteImageService) validateServiceConnection(ctx context.Context, conn *grpc.ClientConn, endpoint string) error {
r.log(4, "Validating the CRI v1 API image version")
r.imageClient = runtimeapi.NewImageServiceClient(conn)
if _, err := r.imageClient.ImageFsInfo(ctx, &runtimeapi.ImageFsInfoRequest{}); err != nil {
return fmt.Errorf("validate CRI v1 image API for endpoint %q: %w", endpoint, err)
}
r.log(2, "Validated CRI v1 image API")
return nil
}
// ListImages lists available images.
func (r *remoteImageService) ListImages(ctx context.Context, filter *runtimeapi.ImageFilter) ([]*runtimeapi.Image, error) {
ctx, cancel := context.WithTimeout(ctx, r.timeout)
defer cancel()
return r.listImagesV1(ctx, filter)
}
func (r *remoteImageService) listImagesV1(ctx context.Context, filter *runtimeapi.ImageFilter) ([]*runtimeapi.Image, error) {
resp, err := r.imageClient.ListImages(ctx, &runtimeapi.ListImagesRequest{
Filter: filter,
})
if err != nil {
r.logErr(err, "ListImages with filter from image service failed", "filter", filter)
return nil, err
}
return resp.Images, nil
}
// ImageStatus returns the status of the image.
func (r *remoteImageService) ImageStatus(ctx context.Context, image *runtimeapi.ImageSpec, verbose bool) (*runtimeapi.ImageStatusResponse, error) {
ctx, cancel := context.WithTimeout(ctx, r.timeout)
defer cancel()
return r.imageStatusV1(ctx, image, verbose)
}
func (r *remoteImageService) imageStatusV1(ctx context.Context, image *runtimeapi.ImageSpec, verbose bool) (*runtimeapi.ImageStatusResponse, error) {
resp, err := r.imageClient.ImageStatus(ctx, &runtimeapi.ImageStatusRequest{
Image: image,
Verbose: verbose,
})
if err != nil {
r.logErr(err, "Get ImageStatus from image service failed", "image", image.Image)
return nil, err
}
if resp.Image != nil {
if resp.Image.Id == "" || resp.Image.Size_ == 0 {
errorMessage := fmt.Sprintf("Id or size of image %q is not set", image.Image)
err := errors.New(errorMessage)
r.logErr(err, "ImageStatus failed", "image", image.Image)
return nil, err
}
}
return resp, nil
}
// PullImage pulls an image with authentication config.
func (r *remoteImageService) PullImage(ctx context.Context, image *runtimeapi.ImageSpec, auth *runtimeapi.AuthConfig, podSandboxConfig *runtimeapi.PodSandboxConfig) (string, error) {
ctx, cancel := context.WithCancel(ctx)
defer cancel()
return r.pullImageV1(ctx, image, auth, podSandboxConfig)
}
func (r *remoteImageService) pullImageV1(ctx context.Context, image *runtimeapi.ImageSpec, auth *runtimeapi.AuthConfig, podSandboxConfig *runtimeapi.PodSandboxConfig) (string, error) {
resp, err := r.imageClient.PullImage(ctx, &runtimeapi.PullImageRequest{
Image: image,
Auth: auth,
SandboxConfig: podSandboxConfig,
})
if err != nil {
r.logErr(err, "PullImage from image service failed", "image", image.Image)
// We can strip the code from unknown status errors since they add no value
// and will make them easier to read in the logs/events.
//
// It also ensures that checking custom error types from pkg/kubelet/images/types.go
// works in `imageManager.EnsureImageExists` (pkg/kubelet/images/image_manager.go).
statusErr, ok := status.FromError(err)
if ok && statusErr.Code() == codes.Unknown {
return "", errors.New(statusErr.Message())
}
return "", err
}
if resp.ImageRef == "" {
r.logErr(errors.New("PullImage failed"), "ImageRef of image is not set", "image", image.Image)
errorMessage := fmt.Sprintf("imageRef of image %q is not set", image.Image)
return "", errors.New(errorMessage)
}
return resp.ImageRef, nil
}
// RemoveImage removes the image.
func (r *remoteImageService) RemoveImage(ctx context.Context, image *runtimeapi.ImageSpec) (err error) {
ctx, cancel := context.WithTimeout(ctx, r.timeout)
defer cancel()
if _, err = r.imageClient.RemoveImage(ctx, &runtimeapi.RemoveImageRequest{
Image: image,
}); err != nil {
r.logErr(err, "RemoveImage from image service failed", "image", image.Image)
return err
}
return nil
}
// ImageFsInfo returns information of the filesystem that is used to store images.
func (r *remoteImageService) ImageFsInfo(ctx context.Context) (*runtimeapi.ImageFsInfoResponse, error) {
ctx, cancel := context.WithTimeout(ctx, r.timeout)
defer cancel()
return r.imageFsInfoV1(ctx)
}
func (r *remoteImageService) imageFsInfoV1(ctx context.Context) (*runtimeapi.ImageFsInfoResponse, error) {
resp, err := r.imageClient.ImageFsInfo(ctx, &runtimeapi.ImageFsInfoRequest{})
if err != nil {
r.logErr(err, "ImageFsInfo from image service failed")
return nil, err
}
return resp, nil
}

897
e2e/vendor/k8s.io/cri-client/pkg/remote_runtime.go generated vendored Normal file
View File

@ -0,0 +1,897 @@
/*
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 cri
import (
"context"
"errors"
"fmt"
"io"
"strings"
"time"
"go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc"
"go.opentelemetry.io/otel/trace"
"google.golang.org/grpc"
"google.golang.org/grpc/backoff"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/credentials/insecure"
"google.golang.org/grpc/status"
"k8s.io/component-base/logs/logreduction"
tracing "k8s.io/component-base/tracing"
internalapi "k8s.io/cri-api/pkg/apis"
runtimeapi "k8s.io/cri-api/pkg/apis/runtime/v1"
"k8s.io/klog/v2"
utilexec "k8s.io/utils/exec"
"k8s.io/cri-client/pkg/internal"
"k8s.io/cri-client/pkg/util"
)
// remoteRuntimeService is a gRPC implementation of internalapi.RuntimeService.
type remoteRuntimeService struct {
timeout time.Duration
runtimeClient runtimeapi.RuntimeServiceClient
// Cache last per-container error message to reduce log spam
logReduction *logreduction.LogReduction
logger *klog.Logger
}
const (
// How frequently to report identical errors
identicalErrorDelay = 1 * time.Minute
// connection parameters
maxBackoffDelay = 3 * time.Second
baseBackoffDelay = 100 * time.Millisecond
minConnectionTimeout = 5 * time.Second
)
// CRIVersion is the type for valid Container Runtime Interface (CRI) API
// versions.
type CRIVersion string
var (
// ErrContainerStatusNil indicates that the returned container status is nil.
ErrContainerStatusNil = errors.New("container status is nil")
// ErrCommandTimedOut indicates that the exec sync command timed.
ErrCommandTimedOut = errors.New("command timed out")
)
const (
// CRIVersionV1 references the v1 CRI API.
CRIVersionV1 CRIVersion = "v1"
)
// NewRemoteRuntimeService creates a new internalapi.RuntimeService.
func NewRemoteRuntimeService(endpoint string, connectionTimeout time.Duration, tp trace.TracerProvider, logger *klog.Logger) (internalapi.RuntimeService, error) {
internal.Log(logger, 3, "Connecting to runtime service", "endpoint", endpoint)
addr, dialer, err := util.GetAddressAndDialer(endpoint)
if err != nil {
return nil, err
}
ctx, cancel := context.WithTimeout(context.Background(), connectionTimeout)
defer cancel()
var dialOpts []grpc.DialOption
dialOpts = append(dialOpts,
grpc.WithTransportCredentials(insecure.NewCredentials()),
grpc.WithAuthority("localhost"),
grpc.WithContextDialer(dialer),
grpc.WithDefaultCallOptions(grpc.MaxCallRecvMsgSize(maxMsgSize)))
if tp != nil {
tracingOpts := []otelgrpc.Option{
otelgrpc.WithMessageEvents(otelgrpc.ReceivedEvents, otelgrpc.SentEvents),
otelgrpc.WithPropagators(tracing.Propagators()),
otelgrpc.WithTracerProvider(tp),
}
// Even if there is no TracerProvider, the otelgrpc still handles context propagation.
// See https://github.com/open-telemetry/opentelemetry-go/tree/main/example/passthrough
dialOpts = append(dialOpts,
grpc.WithUnaryInterceptor(otelgrpc.UnaryClientInterceptor(tracingOpts...)),
grpc.WithStreamInterceptor(otelgrpc.StreamClientInterceptor(tracingOpts...)))
}
connParams := grpc.ConnectParams{
Backoff: backoff.DefaultConfig,
}
connParams.MinConnectTimeout = minConnectionTimeout
connParams.Backoff.BaseDelay = baseBackoffDelay
connParams.Backoff.MaxDelay = maxBackoffDelay
dialOpts = append(dialOpts,
grpc.WithConnectParams(connParams),
)
conn, err := grpc.DialContext(ctx, addr, dialOpts...)
if err != nil {
internal.LogErr(logger, err, "Connect remote runtime failed", "address", addr)
return nil, err
}
service := &remoteRuntimeService{
timeout: connectionTimeout,
logReduction: logreduction.NewLogReduction(identicalErrorDelay),
logger: logger,
}
if err := service.validateServiceConnection(ctx, conn, endpoint); err != nil {
return nil, fmt.Errorf("validate service connection: %w", err)
}
return service, nil
}
func (r *remoteRuntimeService) log(level int, msg string, keyAndValues ...any) {
internal.Log(r.logger, level, msg, keyAndValues...)
}
func (r *remoteRuntimeService) logErr(err error, msg string, keyAndValues ...any) {
internal.LogErr(r.logger, err, msg, keyAndValues...)
}
// validateServiceConnection tries to connect to the remote runtime service by
// using the CRI v1 API version and fails if that's not possible.
func (r *remoteRuntimeService) validateServiceConnection(ctx context.Context, conn *grpc.ClientConn, endpoint string) error {
r.log(4, "Validating the CRI v1 API runtime version")
r.runtimeClient = runtimeapi.NewRuntimeServiceClient(conn)
if _, err := r.runtimeClient.Version(ctx, &runtimeapi.VersionRequest{}); err != nil {
return fmt.Errorf("validate CRI v1 runtime API for endpoint %q: %w", endpoint, err)
}
r.log(2, "Validated CRI v1 runtime API")
return nil
}
// Version returns the runtime name, runtime version and runtime API version.
func (r *remoteRuntimeService) Version(ctx context.Context, apiVersion string) (*runtimeapi.VersionResponse, error) {
r.log(10, "[RemoteRuntimeService] Version", "apiVersion", apiVersion, "timeout", r.timeout)
ctx, cancel := context.WithTimeout(ctx, r.timeout)
defer cancel()
return r.versionV1(ctx, apiVersion)
}
func (r *remoteRuntimeService) versionV1(ctx context.Context, apiVersion string) (*runtimeapi.VersionResponse, error) {
typedVersion, err := r.runtimeClient.Version(ctx, &runtimeapi.VersionRequest{
Version: apiVersion,
})
if err != nil {
r.logErr(err, "Version from runtime service failed")
return nil, err
}
r.log(10, "[RemoteRuntimeService] Version Response", "apiVersion", typedVersion)
if typedVersion.Version == "" || typedVersion.RuntimeName == "" || typedVersion.RuntimeApiVersion == "" || typedVersion.RuntimeVersion == "" {
return nil, fmt.Errorf("not all fields are set in VersionResponse (%q)", *typedVersion)
}
return typedVersion, err
}
// RunPodSandbox creates and starts a pod-level sandbox. Runtimes should ensure
// the sandbox is in ready state.
func (r *remoteRuntimeService) RunPodSandbox(ctx context.Context, config *runtimeapi.PodSandboxConfig, runtimeHandler string) (string, error) {
// Use 2 times longer timeout for sandbox operation (4 mins by default)
// TODO: Make the pod sandbox timeout configurable.
timeout := r.timeout * 2
r.log(10, "[RemoteRuntimeService] RunPodSandbox", "config", config, "runtimeHandler", runtimeHandler, "timeout", timeout)
ctx, cancel := context.WithTimeout(ctx, timeout)
defer cancel()
resp, err := r.runtimeClient.RunPodSandbox(ctx, &runtimeapi.RunPodSandboxRequest{
Config: config,
RuntimeHandler: runtimeHandler,
})
if err != nil {
r.logErr(err, "RunPodSandbox from runtime service failed")
return "", err
}
podSandboxID := resp.PodSandboxId
if podSandboxID == "" {
errorMessage := fmt.Sprintf("PodSandboxId is not set for sandbox %q", config.Metadata)
err := errors.New(errorMessage)
r.logErr(err, "RunPodSandbox failed")
return "", err
}
r.log(10, "[RemoteRuntimeService] RunPodSandbox Response", "podSandboxID", podSandboxID)
return podSandboxID, nil
}
// StopPodSandbox stops the sandbox. If there are any running containers in the
// sandbox, they should be forced to termination.
func (r *remoteRuntimeService) StopPodSandbox(ctx context.Context, podSandBoxID string) (err error) {
r.log(10, "[RemoteRuntimeService] StopPodSandbox", "podSandboxID", podSandBoxID, "timeout", r.timeout)
ctx, cancel := context.WithTimeout(ctx, r.timeout)
defer cancel()
if _, err := r.runtimeClient.StopPodSandbox(ctx, &runtimeapi.StopPodSandboxRequest{
PodSandboxId: podSandBoxID,
}); err != nil {
r.logErr(err, "StopPodSandbox from runtime service failed", "podSandboxID", podSandBoxID)
return err
}
r.log(10, "[RemoteRuntimeService] StopPodSandbox Response", "podSandboxID", podSandBoxID)
return nil
}
// RemovePodSandbox removes the sandbox. If there are any containers in the
// sandbox, they should be forcibly removed.
func (r *remoteRuntimeService) RemovePodSandbox(ctx context.Context, podSandBoxID string) (err error) {
r.log(10, "[RemoteRuntimeService] RemovePodSandbox", "podSandboxID", podSandBoxID, "timeout", r.timeout)
ctx, cancel := context.WithTimeout(ctx, r.timeout)
defer cancel()
if _, err := r.runtimeClient.RemovePodSandbox(ctx, &runtimeapi.RemovePodSandboxRequest{
PodSandboxId: podSandBoxID,
}); err != nil {
r.logErr(err, "RemovePodSandbox from runtime service failed", "podSandboxID", podSandBoxID)
return err
}
r.log(10, "[RemoteRuntimeService] RemovePodSandbox Response", "podSandboxID", podSandBoxID)
return nil
}
// PodSandboxStatus returns the status of the PodSandbox.
func (r *remoteRuntimeService) PodSandboxStatus(ctx context.Context, podSandBoxID string, verbose bool) (*runtimeapi.PodSandboxStatusResponse, error) {
r.log(10, "[RemoteRuntimeService] PodSandboxStatus", "podSandboxID", podSandBoxID, "timeout", r.timeout)
ctx, cancel := context.WithTimeout(ctx, r.timeout)
defer cancel()
return r.podSandboxStatusV1(ctx, podSandBoxID, verbose)
}
func (r *remoteRuntimeService) podSandboxStatusV1(ctx context.Context, podSandBoxID string, verbose bool) (*runtimeapi.PodSandboxStatusResponse, error) {
resp, err := r.runtimeClient.PodSandboxStatus(ctx, &runtimeapi.PodSandboxStatusRequest{
PodSandboxId: podSandBoxID,
Verbose: verbose,
})
if err != nil {
return nil, err
}
r.log(10, "[RemoteRuntimeService] PodSandboxStatus Response", "podSandboxID", podSandBoxID, "status", resp.Status)
status := resp.Status
if resp.Status != nil {
if err := verifySandboxStatus(status); err != nil {
return nil, err
}
}
return resp, nil
}
// ListPodSandbox returns a list of PodSandboxes.
func (r *remoteRuntimeService) ListPodSandbox(ctx context.Context, filter *runtimeapi.PodSandboxFilter) ([]*runtimeapi.PodSandbox, error) {
r.log(10, "[RemoteRuntimeService] ListPodSandbox", "filter", filter, "timeout", r.timeout)
ctx, cancel := context.WithTimeout(ctx, r.timeout)
defer cancel()
return r.listPodSandboxV1(ctx, filter)
}
func (r *remoteRuntimeService) listPodSandboxV1(ctx context.Context, filter *runtimeapi.PodSandboxFilter) ([]*runtimeapi.PodSandbox, error) {
resp, err := r.runtimeClient.ListPodSandbox(ctx, &runtimeapi.ListPodSandboxRequest{
Filter: filter,
})
if err != nil {
r.logErr(err, "ListPodSandbox with filter from runtime service failed", "filter", filter)
return nil, err
}
r.log(10, "[RemoteRuntimeService] ListPodSandbox Response", "filter", filter, "items", resp.Items)
return resp.Items, nil
}
// CreateContainer creates a new container in the specified PodSandbox.
func (r *remoteRuntimeService) CreateContainer(ctx context.Context, podSandBoxID string, config *runtimeapi.ContainerConfig, sandboxConfig *runtimeapi.PodSandboxConfig) (string, error) {
r.log(10, "[RemoteRuntimeService] CreateContainer", "podSandboxID", podSandBoxID, "timeout", r.timeout)
ctx, cancel := context.WithTimeout(ctx, r.timeout)
defer cancel()
return r.createContainerV1(ctx, podSandBoxID, config, sandboxConfig)
}
func (r *remoteRuntimeService) createContainerV1(ctx context.Context, podSandBoxID string, config *runtimeapi.ContainerConfig, sandboxConfig *runtimeapi.PodSandboxConfig) (string, error) {
resp, err := r.runtimeClient.CreateContainer(ctx, &runtimeapi.CreateContainerRequest{
PodSandboxId: podSandBoxID,
Config: config,
SandboxConfig: sandboxConfig,
})
if err != nil {
r.logErr(err, "CreateContainer in sandbox from runtime service failed", "podSandboxID", podSandBoxID)
return "", err
}
r.log(10, "[RemoteRuntimeService] CreateContainer", "podSandboxID", podSandBoxID, "containerID", resp.ContainerId)
if resp.ContainerId == "" {
errorMessage := fmt.Sprintf("ContainerId is not set for container %q", config.Metadata)
err := errors.New(errorMessage)
r.logErr(err, "CreateContainer failed")
return "", err
}
return resp.ContainerId, nil
}
// StartContainer starts the container.
func (r *remoteRuntimeService) StartContainer(ctx context.Context, containerID string) (err error) {
r.log(10, "[RemoteRuntimeService] StartContainer", "containerID", containerID, "timeout", r.timeout)
ctx, cancel := context.WithTimeout(ctx, r.timeout)
defer cancel()
if _, err := r.runtimeClient.StartContainer(ctx, &runtimeapi.StartContainerRequest{
ContainerId: containerID,
}); err != nil {
r.logErr(err, "StartContainer from runtime service failed", "containerID", containerID)
return err
}
r.log(10, "[RemoteRuntimeService] StartContainer Response", "containerID", containerID)
return nil
}
// StopContainer stops a running container with a grace period (i.e., timeout).
func (r *remoteRuntimeService) StopContainer(ctx context.Context, containerID string, timeout int64) (err error) {
r.log(10, "[RemoteRuntimeService] StopContainer", "containerID", containerID, "timeout", timeout)
// Use timeout + default timeout (2 minutes) as timeout to leave extra time
// for SIGKILL container and request latency.
t := r.timeout + time.Duration(timeout)*time.Second
ctx, cancel := context.WithTimeout(ctx, t)
defer cancel()
r.logReduction.ClearID(containerID)
if _, err := r.runtimeClient.StopContainer(ctx, &runtimeapi.StopContainerRequest{
ContainerId: containerID,
Timeout: timeout,
}); err != nil {
r.logErr(err, "StopContainer from runtime service failed", "containerID", containerID)
return err
}
r.log(10, "[RemoteRuntimeService] StopContainer Response", "containerID", containerID)
return nil
}
// RemoveContainer removes the container. If the container is running, the container
// should be forced to removal.
func (r *remoteRuntimeService) RemoveContainer(ctx context.Context, containerID string) (err error) {
r.log(10, "[RemoteRuntimeService] RemoveContainer", "containerID", containerID, "timeout", r.timeout)
ctx, cancel := context.WithTimeout(ctx, r.timeout)
defer cancel()
r.logReduction.ClearID(containerID)
if _, err := r.runtimeClient.RemoveContainer(ctx, &runtimeapi.RemoveContainerRequest{
ContainerId: containerID,
}); err != nil {
r.logErr(err, "RemoveContainer from runtime service failed", "containerID", containerID)
return err
}
r.log(10, "[RemoteRuntimeService] RemoveContainer Response", "containerID", containerID)
return nil
}
// ListContainers lists containers by filters.
func (r *remoteRuntimeService) ListContainers(ctx context.Context, filter *runtimeapi.ContainerFilter) ([]*runtimeapi.Container, error) {
r.log(10, "[RemoteRuntimeService] ListContainers", "filter", filter, "timeout", r.timeout)
ctx, cancel := context.WithTimeout(ctx, r.timeout)
defer cancel()
return r.listContainersV1(ctx, filter)
}
func (r *remoteRuntimeService) listContainersV1(ctx context.Context, filter *runtimeapi.ContainerFilter) ([]*runtimeapi.Container, error) {
resp, err := r.runtimeClient.ListContainers(ctx, &runtimeapi.ListContainersRequest{
Filter: filter,
})
if err != nil {
r.logErr(err, "ListContainers with filter from runtime service failed", "filter", filter)
return nil, err
}
r.log(10, "[RemoteRuntimeService] ListContainers Response", "filter", filter, "containers", resp.Containers)
return resp.Containers, nil
}
// ContainerStatus returns the container status.
func (r *remoteRuntimeService) ContainerStatus(ctx context.Context, containerID string, verbose bool) (*runtimeapi.ContainerStatusResponse, error) {
r.log(10, "[RemoteRuntimeService] ContainerStatus", "containerID", containerID, "timeout", r.timeout)
ctx, cancel := context.WithTimeout(ctx, r.timeout)
defer cancel()
return r.containerStatusV1(ctx, containerID, verbose)
}
func (r *remoteRuntimeService) containerStatusV1(ctx context.Context, containerID string, verbose bool) (*runtimeapi.ContainerStatusResponse, error) {
resp, err := r.runtimeClient.ContainerStatus(ctx, &runtimeapi.ContainerStatusRequest{
ContainerId: containerID,
Verbose: verbose,
})
if err != nil {
// Don't spam the log with endless messages about the same failure.
if r.logReduction.ShouldMessageBePrinted(err.Error(), containerID) {
r.logErr(err, "ContainerStatus from runtime service failed", "containerID", containerID)
}
return nil, err
}
r.logReduction.ClearID(containerID)
r.log(10, "[RemoteRuntimeService] ContainerStatus Response", "containerID", containerID, "status", resp.Status)
status := resp.Status
if resp.Status != nil {
if err := verifyContainerStatus(status); err != nil {
r.logErr(err, "verify ContainerStatus failed", "containerID", containerID)
return nil, err
}
}
return resp, nil
}
// UpdateContainerResources updates a containers resource config
func (r *remoteRuntimeService) UpdateContainerResources(ctx context.Context, containerID string, resources *runtimeapi.ContainerResources) (err error) {
r.log(10, "[RemoteRuntimeService] UpdateContainerResources", "containerID", containerID, "timeout", r.timeout)
ctx, cancel := context.WithTimeout(ctx, r.timeout)
defer cancel()
if _, err := r.runtimeClient.UpdateContainerResources(ctx, &runtimeapi.UpdateContainerResourcesRequest{
ContainerId: containerID,
Linux: resources.GetLinux(),
Windows: resources.GetWindows(),
}); err != nil {
r.logErr(err, "UpdateContainerResources from runtime service failed", "containerID", containerID)
return err
}
r.log(10, "[RemoteRuntimeService] UpdateContainerResources Response", "containerID", containerID)
return nil
}
// ExecSync executes a command in the container, and returns the stdout output.
// If command exits with a non-zero exit code, an error is returned.
func (r *remoteRuntimeService) ExecSync(ctx context.Context, containerID string, cmd []string, timeout time.Duration) (stdout []byte, stderr []byte, err error) {
r.log(10, "[RemoteRuntimeService] ExecSync", "containerID", containerID, "timeout", timeout)
// Do not set timeout when timeout is 0.
var cancel context.CancelFunc
if timeout != 0 {
// Use timeout + default timeout (2 minutes) as timeout to leave some time for
// the runtime to do cleanup.
ctx, cancel = context.WithTimeout(ctx, r.timeout+timeout)
} else {
ctx, cancel = context.WithCancel(ctx)
}
defer cancel()
return r.execSyncV1(ctx, containerID, cmd, timeout)
}
func (r *remoteRuntimeService) execSyncV1(ctx context.Context, containerID string, cmd []string, timeout time.Duration) (stdout []byte, stderr []byte, err error) {
timeoutSeconds := int64(timeout.Seconds())
req := &runtimeapi.ExecSyncRequest{
ContainerId: containerID,
Cmd: cmd,
Timeout: timeoutSeconds,
}
resp, err := r.runtimeClient.ExecSync(ctx, req)
if err != nil {
r.logErr(err, "ExecSync cmd from runtime service failed", "containerID", containerID, "cmd", cmd)
// interpret DeadlineExceeded gRPC errors as timedout errors
if status.Code(err) == codes.DeadlineExceeded {
err = fmt.Errorf("%w: %q timed out after %s", ErrCommandTimedOut, strings.Join(cmd, " "), timeout)
}
return nil, nil, err
}
r.log(10, "[RemoteRuntimeService] ExecSync Response", "containerID", containerID, "exitCode", resp.ExitCode)
err = nil
if resp.ExitCode != 0 {
err = utilexec.CodeExitError{
Err: fmt.Errorf("command '%s' exited with %d: %s", strings.Join(cmd, " "), resp.ExitCode, resp.Stderr),
Code: int(resp.ExitCode),
}
}
return resp.Stdout, resp.Stderr, err
}
// Exec prepares a streaming endpoint to execute a command in the container, and returns the address.
func (r *remoteRuntimeService) Exec(ctx context.Context, req *runtimeapi.ExecRequest) (*runtimeapi.ExecResponse, error) {
r.log(10, "[RemoteRuntimeService] Exec", "timeout", r.timeout)
ctx, cancel := context.WithTimeout(ctx, r.timeout)
defer cancel()
return r.execV1(ctx, req)
}
func (r *remoteRuntimeService) execV1(ctx context.Context, req *runtimeapi.ExecRequest) (*runtimeapi.ExecResponse, error) {
resp, err := r.runtimeClient.Exec(ctx, req)
if err != nil {
r.logErr(err, "Exec cmd from runtime service failed", "containerID", req.ContainerId, "cmd", req.Cmd)
return nil, err
}
r.log(10, "[RemoteRuntimeService] Exec Response")
if resp.Url == "" {
errorMessage := "URL is not set"
err := errors.New(errorMessage)
r.logErr(err, "Exec failed")
return nil, err
}
return resp, nil
}
// Attach prepares a streaming endpoint to attach to a running container, and returns the address.
func (r *remoteRuntimeService) Attach(ctx context.Context, req *runtimeapi.AttachRequest) (*runtimeapi.AttachResponse, error) {
r.log(10, "[RemoteRuntimeService] Attach", "containerID", req.ContainerId, "timeout", r.timeout)
ctx, cancel := context.WithTimeout(ctx, r.timeout)
defer cancel()
return r.attachV1(ctx, req)
}
func (r *remoteRuntimeService) attachV1(ctx context.Context, req *runtimeapi.AttachRequest) (*runtimeapi.AttachResponse, error) {
resp, err := r.runtimeClient.Attach(ctx, req)
if err != nil {
r.logErr(err, "Attach container from runtime service failed", "containerID", req.ContainerId)
return nil, err
}
r.log(10, "[RemoteRuntimeService] Attach Response", "containerID", req.ContainerId)
if resp.Url == "" {
errorMessage := "URL is not set"
err := errors.New(errorMessage)
r.logErr(err, "Attach failed")
return nil, err
}
return resp, nil
}
// PortForward prepares a streaming endpoint to forward ports from a PodSandbox, and returns the address.
func (r *remoteRuntimeService) PortForward(ctx context.Context, req *runtimeapi.PortForwardRequest) (*runtimeapi.PortForwardResponse, error) {
r.log(10, "[RemoteRuntimeService] PortForward", "podSandboxID", req.PodSandboxId, "port", req.Port, "timeout", r.timeout)
ctx, cancel := context.WithTimeout(ctx, r.timeout)
defer cancel()
return r.portForwardV1(ctx, req)
}
func (r *remoteRuntimeService) portForwardV1(ctx context.Context, req *runtimeapi.PortForwardRequest) (*runtimeapi.PortForwardResponse, error) {
resp, err := r.runtimeClient.PortForward(ctx, req)
if err != nil {
r.logErr(err, "PortForward from runtime service failed", "podSandboxID", req.PodSandboxId)
return nil, err
}
r.log(10, "[RemoteRuntimeService] PortForward Response", "podSandboxID", req.PodSandboxId)
if resp.Url == "" {
errorMessage := "URL is not set"
err := errors.New(errorMessage)
r.logErr(err, "PortForward failed")
return nil, err
}
return resp, nil
}
// UpdateRuntimeConfig updates the config of a runtime service. The only
// update payload currently supported is the pod CIDR assigned to a node,
// and the runtime service just proxies it down to the network plugin.
func (r *remoteRuntimeService) UpdateRuntimeConfig(ctx context.Context, runtimeConfig *runtimeapi.RuntimeConfig) (err error) {
r.log(10, "[RemoteRuntimeService] UpdateRuntimeConfig", "runtimeConfig", runtimeConfig, "timeout", r.timeout)
ctx, cancel := context.WithTimeout(ctx, r.timeout)
defer cancel()
// Response doesn't contain anything of interest. This translates to an
// Event notification to the network plugin, which can't fail, so we're
// really looking to surface destination unreachable.
if _, err := r.runtimeClient.UpdateRuntimeConfig(ctx, &runtimeapi.UpdateRuntimeConfigRequest{
RuntimeConfig: runtimeConfig,
}); err != nil {
return err
}
r.log(10, "[RemoteRuntimeService] UpdateRuntimeConfig Response", "runtimeConfig", runtimeConfig)
return nil
}
// Status returns the status of the runtime.
func (r *remoteRuntimeService) Status(ctx context.Context, verbose bool) (*runtimeapi.StatusResponse, error) {
r.log(10, "[RemoteRuntimeService] Status", "timeout", r.timeout)
ctx, cancel := context.WithTimeout(ctx, r.timeout)
defer cancel()
return r.statusV1(ctx, verbose)
}
func (r *remoteRuntimeService) statusV1(ctx context.Context, verbose bool) (*runtimeapi.StatusResponse, error) {
resp, err := r.runtimeClient.Status(ctx, &runtimeapi.StatusRequest{
Verbose: verbose,
})
if err != nil {
r.logErr(err, "Status from runtime service failed")
return nil, err
}
r.log(10, "[RemoteRuntimeService] Status Response", "status", resp.Status)
if resp.Status == nil || len(resp.Status.Conditions) < 2 {
errorMessage := "RuntimeReady or NetworkReady condition are not set"
err := errors.New(errorMessage)
r.logErr(err, "Status failed")
return nil, err
}
return resp, nil
}
// ContainerStats returns the stats of the container.
func (r *remoteRuntimeService) ContainerStats(ctx context.Context, containerID string) (*runtimeapi.ContainerStats, error) {
r.log(10, "[RemoteRuntimeService] ContainerStats", "containerID", containerID, "timeout", r.timeout)
ctx, cancel := context.WithTimeout(ctx, r.timeout)
defer cancel()
return r.containerStatsV1(ctx, containerID)
}
func (r *remoteRuntimeService) containerStatsV1(ctx context.Context, containerID string) (*runtimeapi.ContainerStats, error) {
resp, err := r.runtimeClient.ContainerStats(ctx, &runtimeapi.ContainerStatsRequest{
ContainerId: containerID,
})
if err != nil {
if r.logReduction.ShouldMessageBePrinted(err.Error(), containerID) {
r.logErr(err, "ContainerStats from runtime service failed", "containerID", containerID)
}
return nil, err
}
r.logReduction.ClearID(containerID)
r.log(10, "[RemoteRuntimeService] ContainerStats Response", "containerID", containerID, "stats", resp.GetStats())
return resp.GetStats(), nil
}
// ListContainerStats returns the list of ContainerStats given the filter.
func (r *remoteRuntimeService) ListContainerStats(ctx context.Context, filter *runtimeapi.ContainerStatsFilter) ([]*runtimeapi.ContainerStats, error) {
r.log(10, "[RemoteRuntimeService] ListContainerStats", "filter", filter)
ctx, cancel := context.WithTimeout(ctx, r.timeout)
defer cancel()
return r.listContainerStatsV1(ctx, filter)
}
func (r *remoteRuntimeService) listContainerStatsV1(ctx context.Context, filter *runtimeapi.ContainerStatsFilter) ([]*runtimeapi.ContainerStats, error) {
resp, err := r.runtimeClient.ListContainerStats(ctx, &runtimeapi.ListContainerStatsRequest{
Filter: filter,
})
if err != nil {
r.logErr(err, "ListContainerStats with filter from runtime service failed", "filter", filter)
return nil, err
}
r.log(10, "[RemoteRuntimeService] ListContainerStats Response", "filter", filter, "stats", resp.GetStats())
return resp.GetStats(), nil
}
// PodSandboxStats returns the stats of the pod.
func (r *remoteRuntimeService) PodSandboxStats(ctx context.Context, podSandboxID string) (*runtimeapi.PodSandboxStats, error) {
r.log(10, "[RemoteRuntimeService] PodSandboxStats", "podSandboxID", podSandboxID, "timeout", r.timeout)
ctx, cancel := context.WithTimeout(ctx, r.timeout)
defer cancel()
return r.podSandboxStatsV1(ctx, podSandboxID)
}
func (r *remoteRuntimeService) podSandboxStatsV1(ctx context.Context, podSandboxID string) (*runtimeapi.PodSandboxStats, error) {
resp, err := r.runtimeClient.PodSandboxStats(ctx, &runtimeapi.PodSandboxStatsRequest{
PodSandboxId: podSandboxID,
})
if err != nil {
if r.logReduction.ShouldMessageBePrinted(err.Error(), podSandboxID) {
r.logErr(err, "PodSandbox from runtime service failed", "podSandboxID", podSandboxID)
}
return nil, err
}
r.logReduction.ClearID(podSandboxID)
r.log(10, "[RemoteRuntimeService] PodSandbox Response", "podSandboxID", podSandboxID, "stats", resp.GetStats())
return resp.GetStats(), nil
}
// ListPodSandboxStats returns the list of pod sandbox stats given the filter
func (r *remoteRuntimeService) ListPodSandboxStats(ctx context.Context, filter *runtimeapi.PodSandboxStatsFilter) ([]*runtimeapi.PodSandboxStats, error) {
r.log(10, "[RemoteRuntimeService] ListPodSandboxStats", "filter", filter)
// Set timeout, because runtimes are able to cache disk stats results
ctx, cancel := context.WithTimeout(ctx, r.timeout)
defer cancel()
return r.listPodSandboxStatsV1(ctx, filter)
}
func (r *remoteRuntimeService) listPodSandboxStatsV1(ctx context.Context, filter *runtimeapi.PodSandboxStatsFilter) ([]*runtimeapi.PodSandboxStats, error) {
resp, err := r.runtimeClient.ListPodSandboxStats(ctx, &runtimeapi.ListPodSandboxStatsRequest{
Filter: filter,
})
if err != nil {
r.logErr(err, "ListPodSandboxStats with filter from runtime service failed", "filter", filter)
return nil, err
}
r.log(10, "[RemoteRuntimeService] ListPodSandboxStats Response", "filter", filter, "stats", resp.GetStats())
return resp.GetStats(), nil
}
// ReopenContainerLog reopens the container log file.
func (r *remoteRuntimeService) ReopenContainerLog(ctx context.Context, containerID string) (err error) {
r.log(10, "[RemoteRuntimeService] ReopenContainerLog", "containerID", containerID, "timeout", r.timeout)
ctx, cancel := context.WithTimeout(ctx, r.timeout)
defer cancel()
if _, err := r.runtimeClient.ReopenContainerLog(ctx, &runtimeapi.ReopenContainerLogRequest{ContainerId: containerID}); err != nil {
r.logErr(err, "ReopenContainerLog from runtime service failed", "containerID", containerID)
return err
}
r.log(10, "[RemoteRuntimeService] ReopenContainerLog Response", "containerID", containerID)
return nil
}
// CheckpointContainer triggers a checkpoint of the given CheckpointContainerRequest
func (r *remoteRuntimeService) CheckpointContainer(ctx context.Context, options *runtimeapi.CheckpointContainerRequest) error {
r.log(10,
"[RemoteRuntimeService] CheckpointContainer",
"options",
options,
)
if options == nil {
return errors.New("CheckpointContainer requires non-nil CheckpointRestoreOptions parameter")
}
if options.Timeout < 0 {
return errors.New("CheckpointContainer requires the timeout value to be > 0")
}
ctx, cancel := func(ctx context.Context) (context.Context, context.CancelFunc) {
defaultTimeout := int64(r.timeout / time.Second)
if options.Timeout > defaultTimeout {
// The user requested a specific timeout, let's use that if it
// is larger than the CRI default.
return context.WithTimeout(ctx, time.Duration(options.Timeout)*time.Second)
}
// If the user requested a timeout less than the
// CRI default, let's use the CRI default.
options.Timeout = defaultTimeout
return context.WithTimeout(ctx, r.timeout)
}(ctx)
defer cancel()
_, err := r.runtimeClient.CheckpointContainer(
ctx,
options,
)
if err != nil {
r.logErr(
err,
"CheckpointContainer from runtime service failed",
"containerID",
options.ContainerId,
)
return err
}
r.log(10,
"[RemoteRuntimeService] CheckpointContainer Response",
"containerID",
options.ContainerId,
)
return nil
}
func (r *remoteRuntimeService) GetContainerEvents(ctx context.Context, containerEventsCh chan *runtimeapi.ContainerEventResponse, connectionEstablishedCallback func(runtimeapi.RuntimeService_GetContainerEventsClient)) error {
containerEventsStreamingClient, err := r.runtimeClient.GetContainerEvents(ctx, &runtimeapi.GetEventsRequest{})
if err != nil {
r.logErr(err, "GetContainerEvents failed to get streaming client")
return err
}
if connectionEstablishedCallback != nil {
// The connection is successfully established and we have a streaming client ready for use.
connectionEstablishedCallback(containerEventsStreamingClient)
}
for {
resp, err := containerEventsStreamingClient.Recv()
if err == io.EOF {
r.logErr(err, "container events stream is closed")
return err
}
if err != nil {
r.logErr(err, "failed to receive streaming container event")
return err
}
if resp != nil {
containerEventsCh <- resp
r.log(4, "container event received", "resp", resp)
}
}
}
// ListMetricDescriptors gets the descriptors for the metrics that will be returned in ListPodSandboxMetrics.
func (r *remoteRuntimeService) ListMetricDescriptors(ctx context.Context) ([]*runtimeapi.MetricDescriptor, error) {
ctx, cancel := context.WithTimeout(ctx, r.timeout)
defer cancel()
resp, err := r.runtimeClient.ListMetricDescriptors(ctx, &runtimeapi.ListMetricDescriptorsRequest{})
if err != nil {
r.logErr(err, "ListMetricDescriptors from runtime service failed")
return nil, err
}
r.log(10, "[RemoteRuntimeService] ListMetricDescriptors Response", "stats", resp.GetDescriptors())
return resp.GetDescriptors(), nil
}
// ListPodSandboxMetrics retrieves the metrics for all pod sandboxes.
func (r *remoteRuntimeService) ListPodSandboxMetrics(ctx context.Context) ([]*runtimeapi.PodSandboxMetrics, error) {
ctx, cancel := context.WithTimeout(ctx, r.timeout)
defer cancel()
resp, err := r.runtimeClient.ListPodSandboxMetrics(ctx, &runtimeapi.ListPodSandboxMetricsRequest{})
if err != nil {
r.logErr(err, "ListPodSandboxMetrics from runtime service failed")
return nil, err
}
r.log(10, "[RemoteRuntimeService] ListPodSandboxMetrics Response", "stats", resp.GetPodMetrics())
return resp.GetPodMetrics(), nil
}
// RuntimeConfig returns the configuration information of the runtime.
func (r *remoteRuntimeService) RuntimeConfig(ctx context.Context) (*runtimeapi.RuntimeConfigResponse, error) {
ctx, cancel := context.WithTimeout(ctx, r.timeout)
defer cancel()
resp, err := r.runtimeClient.RuntimeConfig(ctx, &runtimeapi.RuntimeConfigRequest{})
if err != nil {
r.logErr(err, "RuntimeConfig from runtime service failed")
return nil, err
}
r.log(10, "[RemoteRuntimeService] RuntimeConfigResponse", "linuxConfig", resp.GetLinux())
return resp, nil
}

124
e2e/vendor/k8s.io/cri-client/pkg/util/util_unix.go generated vendored Normal file
View File

@ -0,0 +1,124 @@
//go:build freebsd || linux || darwin
// +build freebsd linux darwin
/*
Copyright 2017 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 util
import (
"context"
"fmt"
"net"
"net/url"
"os"
"path/filepath"
"golang.org/x/sys/unix"
)
const (
// unixProtocol is the network protocol of unix socket.
unixProtocol = "unix"
)
// CreateListener creates a listener on the specified endpoint.
func CreateListener(endpoint string) (net.Listener, error) {
protocol, addr, err := parseEndpointWithFallbackProtocol(endpoint, unixProtocol)
if err != nil {
return nil, err
}
if protocol != unixProtocol {
return nil, fmt.Errorf("only support unix socket endpoint")
}
// Unlink to cleanup the previous socket file.
err = unix.Unlink(addr)
if err != nil && !os.IsNotExist(err) {
return nil, fmt.Errorf("failed to unlink socket file %q: %w", addr, err)
}
if err := os.MkdirAll(filepath.Dir(addr), 0750); err != nil {
return nil, fmt.Errorf("error creating socket directory %q: %w", filepath.Dir(addr), err)
}
// Create the socket on a tempfile and move it to the destination socket to handle improper cleanup
file, err := os.CreateTemp(filepath.Dir(addr), "")
if err != nil {
return nil, fmt.Errorf("failed to create temporary file: %w", err)
}
if err := os.Remove(file.Name()); err != nil {
return nil, fmt.Errorf("failed to remove temporary file: %w", err)
}
l, err := net.Listen(protocol, file.Name())
if err != nil {
return nil, err
}
if err = os.Rename(file.Name(), addr); err != nil {
return nil, fmt.Errorf("failed to move temporary file to addr %q: %w", addr, err)
}
return l, nil
}
// GetAddressAndDialer returns the address parsed from the given endpoint and a context dialer.
func GetAddressAndDialer(endpoint string) (string, func(ctx context.Context, addr string) (net.Conn, error), error) {
protocol, addr, err := parseEndpointWithFallbackProtocol(endpoint, unixProtocol)
if err != nil {
return "", nil, err
}
if protocol != unixProtocol {
return "", nil, fmt.Errorf("only support unix socket endpoint")
}
return addr, dial, nil
}
func dial(ctx context.Context, addr string) (net.Conn, error) {
return (&net.Dialer{}).DialContext(ctx, unixProtocol, addr)
}
func parseEndpointWithFallbackProtocol(endpoint string, fallbackProtocol string) (protocol string, addr string, err error) {
if protocol, addr, err = parseEndpoint(endpoint); err != nil && protocol == "" {
fallbackEndpoint := fallbackProtocol + "://" + endpoint
protocol, addr, err = parseEndpoint(fallbackEndpoint)
}
return
}
func parseEndpoint(endpoint string) (string, string, error) {
u, err := url.Parse(endpoint)
if err != nil {
return "", "", err
}
switch u.Scheme {
case "tcp":
return "tcp", u.Host, nil
case "unix":
return "unix", u.Path, nil
case "":
return "", "", fmt.Errorf("using %q as endpoint is deprecated, please consider using full url format", endpoint)
default:
return u.Scheme, "", fmt.Errorf("protocol %q not supported", u.Scheme)
}
}

View File

@ -0,0 +1,36 @@
//go:build !freebsd && !linux && !windows && !darwin
// +build !freebsd,!linux,!windows,!darwin
/*
Copyright 2017 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 util
import (
"context"
"fmt"
"net"
)
// CreateListener creates a listener on the specified endpoint.
func CreateListener(endpoint string) (net.Listener, error) {
return nil, fmt.Errorf("CreateListener is unsupported in this build")
}
// GetAddressAndDialer returns the address parsed from the given endpoint and a context dialer.
func GetAddressAndDialer(endpoint string) (string, func(ctx context.Context, addr string) (net.Conn, error), error) {
return "", nil, fmt.Errorf("GetAddressAndDialer is unsupported in this build")
}

108
e2e/vendor/k8s.io/cri-client/pkg/util/util_windows.go generated vendored Normal file
View File

@ -0,0 +1,108 @@
//go:build windows
// +build windows
/*
Copyright 2017 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 util
import (
"context"
"fmt"
"net"
"net/url"
"strings"
"github.com/Microsoft/go-winio"
)
const (
tcpProtocol = "tcp"
npipeProtocol = "npipe"
)
// CreateListener creates a listener on the specified endpoint.
func CreateListener(endpoint string) (net.Listener, error) {
protocol, addr, err := parseEndpoint(endpoint)
if err != nil {
return nil, err
}
switch protocol {
case tcpProtocol:
return net.Listen(tcpProtocol, addr)
case npipeProtocol:
return winio.ListenPipe(addr, nil)
default:
return nil, fmt.Errorf("only support tcp and npipe endpoint")
}
}
// GetAddressAndDialer returns the address parsed from the given endpoint and a context dialer.
func GetAddressAndDialer(endpoint string) (string, func(ctx context.Context, addr string) (net.Conn, error), error) {
protocol, addr, err := parseEndpoint(endpoint)
if err != nil {
return "", nil, err
}
if protocol == tcpProtocol {
return addr, tcpDial, nil
}
if protocol == npipeProtocol {
return addr, npipeDial, nil
}
return "", nil, fmt.Errorf("only support tcp and npipe endpoint")
}
func tcpDial(ctx context.Context, addr string) (net.Conn, error) {
return (&net.Dialer{}).DialContext(ctx, tcpProtocol, addr)
}
func npipeDial(ctx context.Context, addr string) (net.Conn, error) {
return winio.DialPipeContext(ctx, addr)
}
func parseEndpoint(endpoint string) (string, string, error) {
// url.Parse doesn't recognize \, so replace with / first.
endpoint = strings.Replace(endpoint, "\\", "/", -1)
u, err := url.Parse(endpoint)
if err != nil {
return "", "", err
}
if u.Scheme == "tcp" {
return "tcp", u.Host, nil
} else if u.Scheme == "npipe" {
if strings.HasPrefix(u.Path, "//./pipe") {
return "npipe", u.Path, nil
}
// fallback host if not provided.
host := u.Host
if host == "" {
host = "."
}
return "npipe", fmt.Sprintf("//%s%s", host, u.Path), nil
} else if u.Scheme == "" {
return "", "", fmt.Errorf("Using %q as endpoint is deprecated, please consider using full url format", endpoint)
} else {
return u.Scheme, "", fmt.Errorf("protocol %q not supported", u.Scheme)
}
}

79
e2e/vendor/k8s.io/cri-client/pkg/utils.go generated vendored Normal file
View File

@ -0,0 +1,79 @@
/*
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 cri
import (
"fmt"
runtimeapi "k8s.io/cri-api/pkg/apis/runtime/v1"
)
// maxMsgSize use 16MB as the default message size limit.
// grpc library default is 4MB
const maxMsgSize = 1024 * 1024 * 16
// verifySandboxStatus verified whether all required fields are set in PodSandboxStatus.
func verifySandboxStatus(status *runtimeapi.PodSandboxStatus) error {
if status.Id == "" {
return fmt.Errorf("status.Id is not set")
}
if status.Metadata == nil {
return fmt.Errorf("status.Metadata is not set")
}
metadata := status.Metadata
if metadata.Name == "" || metadata.Namespace == "" || metadata.Uid == "" {
return fmt.Errorf("metadata.Name, metadata.Namespace or metadata.Uid is not in metadata %q", metadata)
}
if status.CreatedAt == 0 {
return fmt.Errorf("status.CreatedAt is not set")
}
return nil
}
// verifyContainerStatus verified whether all required fields are set in ContainerStatus.
func verifyContainerStatus(status *runtimeapi.ContainerStatus) error {
if status.Id == "" {
return fmt.Errorf("status.Id is not set")
}
if status.Metadata == nil {
return fmt.Errorf("status.Metadata is not set")
}
metadata := status.Metadata
if metadata.Name == "" {
return fmt.Errorf("metadata.Name is not in metadata %q", metadata)
}
if status.CreatedAt == 0 {
return fmt.Errorf("status.CreatedAt is not set")
}
if status.Image == nil || status.Image.Image == "" {
return fmt.Errorf("status.Image is not set")
}
if status.ImageRef == "" {
return fmt.Errorf("status.ImageRef is not set")
}
return nil
}