mirror of
https://github.com/ceph/ceph-csi.git
synced 2025-06-14 18:53:35 +00:00
rebase: update K8s packages to v0.32.1
Update K8s packages in go.mod to v0.32.1 Signed-off-by: Praveen M <m.praveen@ibm.com>
This commit is contained in:
201
vendor/k8s.io/cri-client/LICENSE
generated
vendored
Normal file
201
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
vendor/k8s.io/cri-client/pkg/doc.go
generated
vendored
Normal file
19
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
vendor/k8s.io/cri-client/pkg/internal/log.go
generated
vendored
Normal file
33
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
vendor/k8s.io/cri-client/pkg/logs/logs.go
generated
vendored
Normal file
487
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
vendor/k8s.io/cri-client/pkg/logs/logs_other.go
generated
vendored
Normal file
29
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
vendor/k8s.io/cri-client/pkg/logs/logs_windows.go
generated
vendored
Normal file
49
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
vendor/k8s.io/cri-client/pkg/logs/tail.go
generated
vendored
Normal file
62
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
vendor/k8s.io/cri-client/pkg/remote_image.go
generated
vendored
Normal file
248
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
vendor/k8s.io/cri-client/pkg/remote_runtime.go
generated
vendored
Normal file
897
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
vendor/k8s.io/cri-client/pkg/util/util_unix.go
generated
vendored
Normal file
124
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
vendor/k8s.io/cri-client/pkg/util/util_unsupported.go
generated
vendored
Normal file
36
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
vendor/k8s.io/cri-client/pkg/util/util_windows.go
generated
vendored
Normal file
108
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
vendor/k8s.io/cri-client/pkg/utils.go
generated
vendored
Normal file
79
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