mirror of
https://github.com/ceph/ceph-csi.git
synced 2025-06-13 18:43:34 +00:00
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:
committed by
mergify[bot]
parent
15da101b1b
commit
bec6090996
201
e2e/vendor/k8s.io/cri-client/LICENSE
generated
vendored
Normal file
201
e2e/vendor/k8s.io/cri-client/LICENSE
generated
vendored
Normal file
@ -0,0 +1,201 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "{}"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright {yyyy} {name of copyright owner}
|
||||
|
||||
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.
|
19
e2e/vendor/k8s.io/cri-client/pkg/doc.go
generated
vendored
Normal file
19
e2e/vendor/k8s.io/cri-client/pkg/doc.go
generated
vendored
Normal 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
33
e2e/vendor/k8s.io/cri-client/pkg/internal/log.go
generated
vendored
Normal 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
487
e2e/vendor/k8s.io/cri-client/pkg/logs/logs.go
generated
vendored
Normal 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
29
e2e/vendor/k8s.io/cri-client/pkg/logs/logs_other.go
generated
vendored
Normal 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
49
e2e/vendor/k8s.io/cri-client/pkg/logs/logs_windows.go
generated
vendored
Normal 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
62
e2e/vendor/k8s.io/cri-client/pkg/logs/tail.go
generated
vendored
Normal 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
248
e2e/vendor/k8s.io/cri-client/pkg/remote_image.go
generated
vendored
Normal 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
897
e2e/vendor/k8s.io/cri-client/pkg/remote_runtime.go
generated
vendored
Normal 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
124
e2e/vendor/k8s.io/cri-client/pkg/util/util_unix.go
generated
vendored
Normal 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)
|
||||
}
|
||||
}
|
36
e2e/vendor/k8s.io/cri-client/pkg/util/util_unsupported.go
generated
vendored
Normal file
36
e2e/vendor/k8s.io/cri-client/pkg/util/util_unsupported.go
generated
vendored
Normal 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
108
e2e/vendor/k8s.io/cri-client/pkg/util/util_windows.go
generated
vendored
Normal 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
79
e2e/vendor/k8s.io/cri-client/pkg/utils.go
generated
vendored
Normal 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
|
||||
}
|
Reference in New Issue
Block a user