diff --git a/pkg/kubelet/kuberuntime/kuberuntime_logs.go b/pkg/kubelet/kuberuntime/kuberuntime_logs.go index b4bc61341b7..57a6b0e1fac 100644 --- a/pkg/kubelet/kuberuntime/kuberuntime_logs.go +++ b/pkg/kubelet/kuberuntime/kuberuntime_logs.go @@ -21,8 +21,9 @@ import ( "io" "time" - "k8s.io/api/core/v1" - "k8s.io/kubernetes/pkg/kubelet/kuberuntime/logs" + v1 "k8s.io/api/core/v1" + "k8s.io/cri-client/pkg/logs" + "k8s.io/klog/v2" ) // ReadLogs read the container log and redirect into stdout and stderr. @@ -31,6 +32,6 @@ import ( func (m *kubeGenericRuntimeManager) ReadLogs(ctx context.Context, path, containerID string, apiOpts *v1.PodLogOptions, stdout, stderr io.Writer) error { // Convert v1.PodLogOptions into internal log options. opts := logs.NewLogOptions(apiOpts, time.Now()) - - return logs.ReadLogs(ctx, path, containerID, opts, m.runtimeService, stdout, stderr) + logger := klog.Background() + return logs.ReadLogs(ctx, &logger, path, containerID, opts, m.runtimeService, stdout, stderr) } diff --git a/pkg/kubelet/types/constants.go b/pkg/kubelet/types/constants.go index 8677f4066c9..9f343542d47 100644 --- a/pkg/kubelet/types/constants.go +++ b/pkg/kubelet/types/constants.go @@ -19,10 +19,6 @@ package types const ( // ResolvConfDefault is the system default DNS resolver configuration. ResolvConfDefault = "/etc/resolv.conf" - // 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" ) // User visible keys for managing node allocatable enforcement on the node. diff --git a/pkg/kubelet/types/types.go b/pkg/kubelet/types/types.go index 3ecc9c16951..8e2499121e6 100644 --- a/pkg/kubelet/types/types.go +++ b/pkg/kubelet/types/types.go @@ -22,6 +22,7 @@ import ( v1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/types" + "k8s.io/cri-client/pkg/logs" ) // TODO: Reconcile custom types in kubelet/types and this subpackage @@ -45,7 +46,7 @@ func NewTimestamp() *Timestamp { // ConvertToTimestamp takes a string, parses it using the RFC3339NanoLenient layout, // and converts it to a Timestamp object. func ConvertToTimestamp(timeString string) *Timestamp { - parsed, _ := time.Parse(RFC3339NanoLenient, timeString) + parsed, _ := time.Parse(logs.RFC3339NanoLenient, timeString) return &Timestamp{parsed} } @@ -57,7 +58,7 @@ func (t *Timestamp) Get() time.Time { // GetString returns the time in the string format using the RFC3339NanoFixed // layout. func (t *Timestamp) GetString() string { - return t.time.Format(RFC3339NanoFixed) + return t.time.Format(logs.RFC3339NanoFixed) } // SortedContainerStatuses is a type to help sort container statuses based on container names. diff --git a/pkg/util/tail/tail.go b/pkg/util/tail/tail.go index a3c7e7398d7..88965f2d468 100644 --- a/pkg/util/tail/tail.go +++ b/pkg/util/tail/tail.go @@ -17,7 +17,6 @@ limitations under the License. package tail import ( - "bytes" "io" "os" ) @@ -27,11 +26,6 @@ const ( blockSize = 1024 ) -var ( - // eol is the end-of-line sign in the log. - eol = []byte{'\n'} -) - // ReadAtMost reads at most max bytes from the end of the file identified by path or // returns an error. It returns true if the file was longer than max. It will // allocate up to max bytes. @@ -59,40 +53,3 @@ func ReadAtMost(path string, max int64) ([]byte, bool, error) { data, err := io.ReadAll(f) return data, offset > 0, err } - -// 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 -} diff --git a/pkg/util/tail/tail_test.go b/pkg/util/tail/tail_test.go index fce8d2ffb1d..accc17d8b16 100644 --- a/pkg/util/tail/tail_test.go +++ b/pkg/util/tail/tail_test.go @@ -17,7 +17,6 @@ limitations under the License. package tail import ( - "bytes" "os" "strings" "testing" @@ -89,32 +88,3 @@ func TestReadAtMost(t *testing.T) { } } } - -func TestTail(t *testing.T) { - line := strings.Repeat("a", blockSize) - testBytes := []byte(line + "\n" + - line + "\n" + - line + "\n" + - line + "\n" + - line[blockSize/2:]) // incomplete line - - for c, test := range []struct { - n int64 - start int64 - }{ - {n: -1, start: 0}, - {n: 0, start: int64(len(line)+1) * 4}, - {n: 1, start: int64(len(line)+1) * 3}, - {n: 9999, start: 0}, - } { - t.Logf("TestCase #%d: %+v", c, test) - r := bytes.NewReader(testBytes) - s, err := FindTailLineStartIndex(r, test.n) - if err != nil { - t.Error(err) - } - if s != test.start { - t.Errorf("%d != %d", s, test.start) - } - } -} diff --git a/staging/publishing/import-restrictions.yaml b/staging/publishing/import-restrictions.yaml index fcb6bc63f68..ad48080bc87 100644 --- a/staging/publishing/import-restrictions.yaml +++ b/staging/publishing/import-restrictions.yaml @@ -305,8 +305,10 @@ - baseImportPath: "./staging/src/k8s.io/cri-client" allowedImports: + - k8s.io/api - k8s.io/apimachinery - k8s.io/apiserver + - k8s.io/client-go - k8s.io/component-base - k8s.io/cri-api - k8s.io/cri-client diff --git a/staging/src/k8s.io/cri-client/go.mod b/staging/src/k8s.io/cri-client/go.mod index ae6c9e88796..2c5c3990bb4 100644 --- a/staging/src/k8s.io/cri-client/go.mod +++ b/staging/src/k8s.io/cri-client/go.mod @@ -6,13 +6,16 @@ go 1.22.0 require ( github.com/Microsoft/go-winio v0.6.0 + github.com/fsnotify/fsnotify v1.7.0 github.com/stretchr/testify v1.8.4 go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.46.0 go.opentelemetry.io/otel/sdk v1.20.0 go.opentelemetry.io/otel/trace v1.20.0 golang.org/x/sys v0.20.0 google.golang.org/grpc v1.59.0 + k8s.io/api v0.0.0 k8s.io/apimachinery v0.0.0 + k8s.io/client-go v0.0.0 k8s.io/component-base v0.0.0 k8s.io/cri-api v0.0.0 k8s.io/klog/v2 v2.120.1 @@ -25,12 +28,25 @@ require ( github.com/cenkalti/backoff/v4 v4.2.1 // indirect github.com/cespare/xxhash/v2 v2.2.0 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect + github.com/emicklei/go-restful/v3 v3.11.0 // indirect github.com/felixge/httpsnoop v1.0.3 // indirect github.com/go-logr/logr v1.4.1 // indirect github.com/go-logr/stdr v1.2.2 // indirect + github.com/go-openapi/jsonpointer v0.19.6 // indirect + github.com/go-openapi/jsonreference v0.20.2 // indirect + github.com/go-openapi/swag v0.22.4 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/golang/protobuf v1.5.4 // indirect + github.com/google/gnostic-models v0.6.8 // indirect + github.com/google/gofuzz v1.2.0 // indirect + github.com/google/uuid v1.3.1 // indirect github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0 // indirect + github.com/josharian/intern v1.0.0 // indirect + github.com/json-iterator/go v1.1.12 // indirect + github.com/mailru/easyjson v0.7.7 // indirect + github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect + github.com/modern-go/reflect2 v1.0.2 // indirect + github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/prometheus/client_golang v1.19.0 // indirect github.com/prometheus/client_model v0.6.0 // indirect @@ -53,9 +69,12 @@ require ( google.golang.org/genproto/googleapis/api v0.0.0-20230822172742-b8732ec3820d // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20230822172742-b8732ec3820d // indirect google.golang.org/protobuf v1.33.0 // indirect + gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect - k8s.io/client-go v0.0.0 // indirect + k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340 // indirect + sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect + sigs.k8s.io/structured-merge-diff/v4 v4.4.1 // indirect ) replace ( diff --git a/staging/src/k8s.io/cri-client/go.sum b/staging/src/k8s.io/cri-client/go.sum index 50085b5c959..3eef10e519f 100644 --- a/staging/src/k8s.io/cri-client/go.sum +++ b/staging/src/k8s.io/cri-client/go.sum @@ -5,10 +5,12 @@ cloud.google.com/go/compute/metadata v0.3.0/go.mod h1:zFmK7XCadkQkj6TtorcaGlCW1h github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= github.com/Microsoft/go-winio v0.6.0 h1:slsWYD/zyx7lCXoZVlvQrj0hPTM1HI4+v1sIda2yDvg= github.com/Microsoft/go-winio v0.6.0/go.mod h1:cTAf44im0RAYeL23bpB+fzCyDH2MJiz2BO69KH/soAE= +github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ= github.com/alecthomas/kingpin/v2 v2.4.0/go.mod h1:0gyi0zQnjuFk8xrkNKamJoyUo382HRL7ATRpFZCw6tE= github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137/go.mod h1:OMCwj8VM1Kc9e19TLln2VL61YJF0x1XFtfdL4JdbSyE= github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= +github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM= @@ -21,14 +23,20 @@ github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XL github.com/cncf/udpa/go v0.0.0-20220112060539-c52dc94e7fbe/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI= github.com/cncf/xds/go v0.0.0-20230607035331-e9ce68804cb4 h1:/inchEIKaYC1Akx+H+gqO04wryn5h75LSazbRlnya1k= github.com/cncf/xds/go v0.0.0-20230607035331-e9ce68804cb4/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/emicklei/go-restful/v3 v3.11.0 h1:rAQeMHw1c7zTmncogyy8VvRZwtkmkZ4FxERmMY4rD+g= github.com/emicklei/go-restful/v3 v3.11.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= github.com/envoyproxy/go-control-plane v0.11.1/go.mod h1:uhMcXKCQMEJHiAb0w+YGefQLaTEw+YhGluxZkrTmD0g= github.com/envoyproxy/protoc-gen-validate v1.0.2 h1:QkIBuU5k+x7/QXPvPPnWXWlCdaBFApVqftFV6k087DA= github.com/envoyproxy/protoc-gen-validate v1.0.2/go.mod h1:GpiZQP3dDbg4JouG/NNS7QWXpgx6x8QiMKdmN72jogE= github.com/felixge/httpsnoop v1.0.3 h1:s/nj+GCswXYzN5v2DpNMuMQYe+0DDwt5WVCU6CWBdXk= github.com/felixge/httpsnoop v1.0.3/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= +github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= +github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= github.com/fxamacker/cbor/v2 v2.6.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ= github.com/go-kit/log v0.2.1/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0= github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs= @@ -38,8 +46,12 @@ github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ4 github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= github.com/go-logr/zapr v1.3.0/go.mod h1:YKepepNBd1u/oyhd/yQmtjVXmm9uML4IXUgMOwR8/Gg= +github.com/go-openapi/jsonpointer v0.19.6 h1:eCs3fxoIi3Wh6vtgmLTOjdhSpiqphQ+DaPn38N2ZdrE= github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs= +github.com/go-openapi/jsonreference v0.20.2 h1:3sVjiK66+uXK/6oQ8xgcRKcFgQ5KXa2KvnJRumpMGbE= github.com/go-openapi/jsonreference v0.20.2/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En5Ap4rVB5KVcIDZG2k= +github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= +github.com/go-openapi/swag v0.22.4 h1:QLMzNJnMGPRNDCbySlcj1x01tzU8/9LTTL9hZZZogBU= github.com/go-openapi/swag v0.22.4/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= @@ -50,11 +62,16 @@ github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4er github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= github.com/google/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA= +github.com/google/gnostic-models v0.6.8 h1:yo/ABAfM5IMRsS1VnXjTBvUb61tFIHozhlYvRgGre9I= github.com/google/gnostic-models v0.6.8/go.mod h1:5n7qKqH0f5wFt+aWF8CW6pZLLNOfYuF5OpfBSENuI8U= +github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/pprof v0.0.0-20240525223248-4bfdf5a9a2af/go.mod h1:K1liHPHnj73Fdn/EKuT8nrFqBihUSKXoLYU0BuatOYo= +github.com/google/uuid v1.3.1 h1:KjJaJ9iWZ3jOFZIf1Lqf4laDRCasjl0BCmnEGxkdLb4= github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= @@ -62,21 +79,31 @@ github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0 h1:YBftPWNWd4WwGqtY2yeZL2ef8rH github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0/go.mod h1:YN5jB8ie0yfIUg6VvR9Kz84aCaG7AsGZnLjhHbUqwPg= github.com/imdario/mergo v0.3.6/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= +github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= +github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= github.com/moby/spdystream v0.2.0/go.mod h1:f7i0iNDQJ059oMTcWxx8MA/zKFIuD/lY+0GqbN2Wy8c= github.com/moby/term v0.0.0-20221205130635-1aeaba878587/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw= @@ -84,6 +111,7 @@ github.com/onsi/ginkgo/v2 v2.19.0/go.mod h1:rlwLi9PilAFJ8jCg9UE1QP6VBpd6/xj3SRC0 github.com/onsi/gomega v1.33.1/go.mod h1:U4R44UsT+9eLIaYRB2a5qajjtQYn0hauxvRm16AVYg0= github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/prometheus/client_golang v1.19.0 h1:ygXvpU1AoN1MhdzckN+PyD9QJOSD4x7kmXYlnfbA6JU= @@ -101,7 +129,13 @@ github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic github.com/spf13/cobra v1.8.0/go.mod h1:WXLWApfZ71AjXPya3WOlMsY9yMs7YeiHhFVlvLyhcho= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= @@ -175,6 +209,7 @@ golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8T golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/genproto v0.0.0-20230822172742-b8732ec3820d h1:VBu5YqKPv6XiJ199exd8Br+Aetz+o08F+PLMnwJQHAY= google.golang.org/genproto v0.0.0-20230822172742-b8732ec3820d/go.mod h1:yZTlhN0tQnXo3h00fuXNCxJdLdIdnVFVBaRJ5LWBbw4= @@ -190,16 +225,24 @@ gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8 gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/evanphx/json-patch.v4 v4.12.0/go.mod h1:p8EYWUEYMpynmqDbY58zCKCFZw8pRWMG4EsWvDvM72M= +gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= +gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +k8s.io/gengo/v2 v2.0.0-20240228010128-51d4e06bde70/go.mod h1:VH3AT8AaQOqiGjMF9p0/IM1Dj+82ZwjfxUP1IxaHE+8= k8s.io/klog/v2 v2.120.1 h1:QXU6cPEOIslTGvZaXvFWiP9VKyeet3sawzTOvdXb4Vw= k8s.io/klog/v2 v2.120.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE= +k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340 h1:BZqlfIlq5YbRMFko6/PM7FjZpUb45WallggurYhKGag= k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340/go.mod h1:yD4MZYeKMBwQKVht279WycxKyM84kkAx2DPrTXaeb98= k8s.io/utils v0.0.0-20230726121419-3b25d923346b h1:sgn3ZU783SCgtaSJjpcVVlRqd6GSnlTLKgpAAttJvpI= k8s.io/utils v0.0.0-20230726121419-3b25d923346b/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= +sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo= sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0= +sigs.k8s.io/structured-merge-diff/v4 v4.4.1 h1:150L+0vs/8DA78h1u02ooW1/fFq/Lwr+sGiqlzvrtq4= sigs.k8s.io/structured-merge-diff/v4 v4.4.1/go.mod h1:N8hJocpFajUSSeSJ9bOZ77VzejKZaXsTtZo4/u7Io08= +sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E= sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY= diff --git a/staging/src/k8s.io/cri-client/pkg/internal/log.go b/staging/src/k8s.io/cri-client/pkg/internal/log.go new file mode 100644 index 00000000000..d3d3dd4e31e --- /dev/null +++ b/staging/src/k8s.io/cri-client/pkg/internal/log.go @@ -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...) +} diff --git a/pkg/kubelet/kuberuntime/logs/logs.go b/staging/src/k8s.io/cri-client/pkg/logs/logs.go similarity index 87% rename from pkg/kubelet/kuberuntime/logs/logs.go rename to staging/src/k8s.io/cri-client/pkg/logs/logs.go index ebeaef5c98a..8642f5a180a 100644 --- a/pkg/kubelet/kuberuntime/logs/logs.go +++ b/staging/src/k8s.io/cri-client/pkg/logs/logs.go @@ -30,14 +30,14 @@ import ( "time" "github.com/fsnotify/fsnotify" - "k8s.io/klog/v2" 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/kubernetes/pkg/kubelet/types" - "k8s.io/kubernetes/pkg/util/tail" + "k8s.io/cri-client/pkg/internal" ) // Notice that the current CRI logs implementation doesn't handle @@ -46,13 +46,17 @@ import ( // * 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. -// TODO(random-liu): Support log rotation. 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 = types.RFC3339NanoFixed + timeFormatOut = RFC3339NanoFixed // timeFormatIn is the format for parsing timestamps from other logs. - timeFormatIn = types.RFC3339NanoLenient + timeFormatIn = RFC3339NanoLenient // logForceCheckPeriod is the period to check for a new read logForceCheckPeriod = 1 * time.Second @@ -280,7 +284,7 @@ func (w *logWriter) write(msg *logMessage, addPrefix bool) error { // 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, path, containerID string, opts *LogOptions, runtimeService internalapi.RuntimeService, stdout, stderr io.Writer) error { +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. @@ -298,7 +302,7 @@ func ReadLogs(ctx context.Context, path, containerID string, opts *LogOptions, r defer f.Close() // Search start point based on tail line. - start, err := tail.FindTailLineStartIndex(f, opts.tail) + 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) } @@ -322,7 +326,7 @@ func ReadLogs(ctx context.Context, path, containerID string, opts *LogOptions, r dir := filepath.Dir(path) for { if stop || (limitedMode && limitedNum == 0) { - klog.V(2).InfoS("Finished parsing log file", "path", path) + internal.Log(logger, 2, "Finished parsing log file", "path", path) return nil } l, err := r.ReadBytes(eol[0]) @@ -355,7 +359,7 @@ func ReadLogs(ctx context.Context, path, containerID string, opts *LogOptions, r } var recreated bool // Wait until the next log change. - found, recreated, err = waitLogs(ctx, containerID, baseName, watcher, runtimeService) + found, recreated, err = waitLogs(ctx, logger, containerID, baseName, watcher, runtimeService) if err != nil { return err } @@ -380,7 +384,7 @@ func ReadLogs(ctx context.Context, path, containerID string, opts *LogOptions, r if len(l) == 0 { continue } - klog.InfoS("Incomplete line in log file", "path", path, "line", l) + internal.Log(logger, 0, "Incomplete line in log file", "path", path, "line", l) } if parse == nil { // Initialize the log parsing function. @@ -392,16 +396,16 @@ func ReadLogs(ctx context.Context, path, containerID string, opts *LogOptions, r // Parse the log line. msg.reset() if err := parse(l, msg); err != nil { - klog.ErrorS(err, "Failed when parsing line in log file", "path", path, "line", l) + 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 { - klog.V(2).InfoS("Finished parsing log file, hit bytes limit", "path", path, "limit", opts.bytes) + internal.Log(logger, 2, "Finished parsing log file, hit bytes limit", "path", path, "limit", opts.bytes) return nil } - klog.ErrorS(err, "Failed when writing line to log file", "path", path, "line", msg) + internal.LogErr(logger, err, "Failed when writing line to log file", "path", path, "line", msg) return err } if limitedMode { @@ -415,7 +419,7 @@ func ReadLogs(ctx context.Context, path, containerID string, opts *LogOptions, r } } -func isContainerRunning(ctx context.Context, id string, r internalapi.RuntimeService) (bool, error) { +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 { return false, err @@ -426,7 +430,7 @@ func isContainerRunning(ctx context.Context, id string, r internalapi.RuntimeSer } // Only keep following container log when it is running. if status.State != runtimeapi.ContainerState_CONTAINER_RUNNING { - klog.V(5).InfoS("Container is not running", "containerId", id, "state", status.State) + 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 @@ -437,9 +441,9 @@ func isContainerRunning(ctx context.Context, id string, r internalapi.RuntimeSer // 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, id string, logName string, w *fsnotify.Watcher, runtimeService internalapi.RuntimeService) (bool, bool, error) { +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, id, runtimeService); !running { + if running, err := isContainerRunning(ctx, logger, id, runtimeService); !running { return false, false, err } errRetry := 5 @@ -454,10 +458,10 @@ func waitLogs(ctx context.Context, id string, logName string, w *fsnotify.Watche case fsnotify.Create: return true, filepath.Base(e.Name) == logName, nil default: - klog.ErrorS(nil, "Received unexpected fsnotify event, retrying", "event", e) + internal.LogErr(logger, nil, "Received unexpected fsnotify event, retrying", "event", e) } case err := <-w.Errors: - klog.ErrorS(err, "Received fsnotify watch error, retrying unless no more retries left", "retries", errRetry) + internal.LogErr(logger, err, "Received fsnotify watch error, retrying unless no more retries left", "retries", errRetry) if errRetry == 0 { return false, false, err } diff --git a/pkg/kubelet/kuberuntime/logs/logs_test.go b/staging/src/k8s.io/cri-client/pkg/logs/logs_test.go similarity index 95% rename from pkg/kubelet/kuberuntime/logs/logs_test.go rename to staging/src/k8s.io/cri-client/pkg/logs/logs_test.go index 3ae1d689742..59febfc00ba 100644 --- a/pkg/kubelet/kuberuntime/logs/logs_test.go +++ b/staging/src/k8s.io/cri-client/pkg/logs/logs_test.go @@ -28,17 +28,14 @@ import ( "testing" "time" - utiltesting "k8s.io/client-go/util/testing" - - v1 "k8s.io/api/core/v1" - apitesting "k8s.io/cri-api/pkg/apis/testing" - "k8s.io/kubernetes/pkg/kubelet/types" - "k8s.io/utils/pointer" - "github.com/stretchr/testify/assert" + v1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + utiltesting "k8s.io/client-go/util/testing" runtimeapi "k8s.io/cri-api/pkg/apis/runtime/v1" + apitesting "k8s.io/cri-api/pkg/apis/testing" + "k8s.io/utils/ptr" ) func TestLogOptions(t *testing.T) { @@ -102,42 +99,42 @@ func TestReadLogs(t *testing.T) { { name: "using TailLines 2 should output last 2 lines", podLogOptions: v1.PodLogOptions{ - TailLines: pointer.Int64(2), + TailLines: ptr.To[int64](2), }, expected: "line2\nline3\n", }, { name: "using TailLines 4 should output all lines when the log has less than 4 lines", podLogOptions: v1.PodLogOptions{ - TailLines: pointer.Int64(4), + TailLines: ptr.To[int64](4), }, expected: "line1\nline2\nline3\n", }, { name: "using TailLines 0 should output nothing", podLogOptions: v1.PodLogOptions{ - TailLines: pointer.Int64(0), + TailLines: ptr.To[int64](0), }, expected: "", }, { name: "using LimitBytes 9 should output first 9 bytes", podLogOptions: v1.PodLogOptions{ - LimitBytes: pointer.Int64(9), + LimitBytes: ptr.To[int64](9), }, expected: "line1\nlin", }, { name: "using LimitBytes 100 should output all bytes when the log has less than 100 bytes", podLogOptions: v1.PodLogOptions{ - LimitBytes: pointer.Int64(100), + LimitBytes: ptr.To[int64](100), }, expected: "line1\nline2\nline3\n", }, { name: "using LimitBytes 0 should output nothing", podLogOptions: v1.PodLogOptions{ - LimitBytes: pointer.Int64(0), + LimitBytes: ptr.To[int64](0), }, expected: "", }, @@ -166,7 +163,7 @@ func TestReadLogs(t *testing.T) { name: "using follow combined with TailLines 2 should output the last 2 lines", podLogOptions: v1.PodLogOptions{ Follow: true, - TailLines: pointer.Int64(2), + TailLines: ptr.To[int64](2), }, expected: "line2\nline3\n", }, @@ -199,7 +196,7 @@ func TestReadLogs(t *testing.T) { opts := NewLogOptions(&tc.podLogOptions, time.Now()) stdoutBuf := bytes.NewBuffer(nil) stderrBuf := bytes.NewBuffer(nil) - err = ReadLogs(context.TODO(), file.Name(), containerID, opts, fakeRuntimeService, stdoutBuf, stderrBuf) + err = ReadLogs(context.TODO(), nil, file.Name(), containerID, opts, fakeRuntimeService, stdoutBuf, stderrBuf) if err != nil { t.Fatalf(err.Error()) @@ -245,7 +242,7 @@ func TestReadRotatedLog(t *testing.T) { Follow: true, } opts := NewLogOptions(&podLogOptions, time.Now()) - _ = ReadLogs(ctx, fileName, containerID, opts, fakeRuntimeService, stdoutBuf, stderrBuf) + _ = ReadLogs(ctx, nil, fileName, containerID, opts, fakeRuntimeService, stdoutBuf, stderrBuf) }(ctx) // log in stdout @@ -262,7 +259,7 @@ func TestReadRotatedLog(t *testing.T) { for line := 0; line < 10; line++ { // Write the first three lines to log file - now := time.Now().Format(types.RFC3339NanoLenient) + now := time.Now().Format(RFC3339NanoLenient) if line%2 == 0 { file.WriteString(fmt.Sprintf( `{"log":"line%d\n","stream":"stdout","time":"%s"}`+"\n", line, now)) @@ -523,7 +520,7 @@ func TestReadLogsLimitsWithTimestamps(t *testing.T) { var buf bytes.Buffer w := io.MultiWriter(&buf) - err = ReadLogs(context.Background(), tmpfile.Name(), "", &LogOptions{tail: -1, bytes: -1, timestamp: true}, nil, w, w) + err = ReadLogs(context.Background(), nil, tmpfile.Name(), "", &LogOptions{tail: -1, bytes: -1, timestamp: true}, nil, w, w) assert.NoError(t, err) lineCount := 0 diff --git a/staging/src/k8s.io/cri-client/pkg/logs/tail.go b/staging/src/k8s.io/cri-client/pkg/logs/tail.go new file mode 100644 index 00000000000..881a3b1f458 --- /dev/null +++ b/staging/src/k8s.io/cri-client/pkg/logs/tail.go @@ -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 +} diff --git a/staging/src/k8s.io/cri-client/pkg/logs/tail_test.go b/staging/src/k8s.io/cri-client/pkg/logs/tail_test.go new file mode 100644 index 00000000000..fab0d1031cd --- /dev/null +++ b/staging/src/k8s.io/cri-client/pkg/logs/tail_test.go @@ -0,0 +1,52 @@ +/* +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" + "strings" + "testing" +) + +func TestTail(t *testing.T) { + line := strings.Repeat("a", blockSize) + testBytes := []byte(line + "\n" + + line + "\n" + + line + "\n" + + line + "\n" + + line[blockSize/2:]) // incomplete line + + for c, test := range []struct { + n int64 + start int64 + }{ + {n: -1, start: 0}, + {n: 0, start: int64(len(line)+1) * 4}, + {n: 1, start: int64(len(line)+1) * 3}, + {n: 9999, start: 0}, + } { + t.Logf("TestCase #%d: %+v", c, test) + r := bytes.NewReader(testBytes) + s, err := findTailLineStartIndex(r, test.n) + if err != nil { + t.Error(err) + } + if s != test.start { + t.Errorf("%d != %d", s, test.start) + } + } +} diff --git a/staging/src/k8s.io/cri-client/pkg/remote_image.go b/staging/src/k8s.io/cri-client/pkg/remote_image.go index 638cb678790..b067697766b 100644 --- a/staging/src/k8s.io/cri-client/pkg/remote_image.go +++ b/staging/src/k8s.io/cri-client/pkg/remote_image.go @@ -29,12 +29,14 @@ import ( "google.golang.org/grpc/codes" "google.golang.org/grpc/credentials/insecure" "google.golang.org/grpc/status" - tracing "k8s.io/component-base/tracing" - "k8s.io/cri-client/pkg/util" - "k8s.io/klog/v2" + 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. @@ -46,7 +48,7 @@ type remoteImageService struct { // NewRemoteImageService creates a new internalapi.ImageManagerService. func NewRemoteImageService(endpoint string, connectionTimeout time.Duration, tp trace.TracerProvider, logger *klog.Logger) (internalapi.ImageManagerService, error) { - log(logger, 3, "Connecting to image service", "endpoint", endpoint) + internal.Log(logger, 3, "Connecting to image service", "endpoint", endpoint) addr, dialer, err := util.GetAddressAndDialer(endpoint) if err != nil { return nil, err @@ -86,7 +88,7 @@ func NewRemoteImageService(endpoint string, connectionTimeout time.Duration, tp conn, err := grpc.DialContext(ctx, addr, dialOpts...) if err != nil { - logErr(logger, err, "Connect remote image service failed", "address", addr) + internal.LogErr(logger, err, "Connect remote image service failed", "address", addr) return nil, err } @@ -103,11 +105,11 @@ func NewRemoteImageService(endpoint string, connectionTimeout time.Duration, tp } func (r *remoteImageService) log(level int, msg string, keyAndValues ...any) { - log(r.logger, level, msg, keyAndValues...) + internal.Log(r.logger, level, msg, keyAndValues...) } func (r *remoteImageService) logErr(err error, msg string, keyAndValues ...any) { - logErr(r.logger, err, msg, keyAndValues...) + internal.LogErr(r.logger, err, msg, keyAndValues...) } // validateServiceConnection tries to connect to the remote image service by diff --git a/staging/src/k8s.io/cri-client/pkg/remote_runtime.go b/staging/src/k8s.io/cri-client/pkg/remote_runtime.go index 62d158a3125..772f8171fe0 100644 --- a/staging/src/k8s.io/cri-client/pkg/remote_runtime.go +++ b/staging/src/k8s.io/cri-client/pkg/remote_runtime.go @@ -31,14 +31,16 @@ import ( "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/cri-client/pkg/util" "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. @@ -79,7 +81,7 @@ const ( // NewRemoteRuntimeService creates a new internalapi.RuntimeService. func NewRemoteRuntimeService(endpoint string, connectionTimeout time.Duration, tp trace.TracerProvider, logger *klog.Logger) (internalapi.RuntimeService, error) { - log(logger, 3, "Connecting to runtime service", "endpoint", endpoint) + internal.Log(logger, 3, "Connecting to runtime service", "endpoint", endpoint) addr, dialer, err := util.GetAddressAndDialer(endpoint) if err != nil { return nil, err @@ -118,7 +120,7 @@ func NewRemoteRuntimeService(endpoint string, connectionTimeout time.Duration, t conn, err := grpc.DialContext(ctx, addr, dialOpts...) if err != nil { - logErr(logger, err, "Connect remote runtime failed", "address", addr) + internal.LogErr(logger, err, "Connect remote runtime failed", "address", addr) return nil, err } @@ -136,11 +138,11 @@ func NewRemoteRuntimeService(endpoint string, connectionTimeout time.Duration, t } func (r *remoteRuntimeService) log(level int, msg string, keyAndValues ...any) { - log(r.logger, level, msg, keyAndValues...) + internal.Log(r.logger, level, msg, keyAndValues...) } func (r *remoteRuntimeService) logErr(err error, msg string, keyAndValues ...any) { - logErr(r.logger, err, msg, keyAndValues...) + internal.LogErr(r.logger, err, msg, keyAndValues...) } // validateServiceConnection tries to connect to the remote runtime service by diff --git a/staging/src/k8s.io/cri-client/pkg/utils.go b/staging/src/k8s.io/cri-client/pkg/utils.go index 782096ddef1..b82f37b90d2 100644 --- a/staging/src/k8s.io/cri-client/pkg/utils.go +++ b/staging/src/k8s.io/cri-client/pkg/utils.go @@ -20,7 +20,6 @@ import ( "fmt" runtimeapi "k8s.io/cri-api/pkg/apis/runtime/v1" - "k8s.io/klog/v2" ) // maxMsgSize use 16MB as the default message size limit. @@ -78,17 +77,3 @@ func verifyContainerStatus(status *runtimeapi.ContainerStatus) error { return nil } - -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...) -}