Skip to content

Commit 64cd106

Browse files
committed
add support logs for namespace k8s.io
Signed-off-by: zhaojizhuang <[email protected]>
1 parent cfe96a9 commit 64cd106

File tree

9 files changed

+730
-14
lines changed

9 files changed

+730
-14
lines changed

cmd/nerdctl/logs.go

Lines changed: 29 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,11 @@ import (
2525
"syscall"
2626

2727
"github.com/containerd/containerd"
28+
containerstore "github.com/containerd/containerd/pkg/cri/store/container"
2829
"github.com/containerd/nerdctl/pkg/clientutil"
2930
"github.com/containerd/nerdctl/pkg/idutil/containerwalker"
3031
"github.com/containerd/nerdctl/pkg/labels"
32+
"github.com/containerd/nerdctl/pkg/labels/k8slabels"
3133
"github.com/containerd/nerdctl/pkg/logging"
3234
"github.com/sirupsen/logrus"
3335
"github.com/spf13/cobra"
@@ -62,8 +64,8 @@ func logsAction(cmd *cobra.Command, args []string) error {
6264
}
6365

6466
switch globalOptions.Namespace {
65-
case "moby", "k8s.io":
66-
logrus.Warn("Currently, `nerdctl logs` only supports containers created with `nerdctl run -d`")
67+
case "moby":
68+
logrus.Warn("Currently, `nerdctl logs` only supports containers created with `nerdctl run -d` or CRI")
6769
}
6870
client, ctx, cancel, err := clientutil.NewClient(cmd.Context(), globalOptions.Namespace, globalOptions.Address)
6971
if err != nil {
@@ -113,6 +115,12 @@ func logsAction(cmd *cobra.Command, args []string) error {
113115
if err != nil {
114116
return err
115117
}
118+
119+
logPath, err := getLogPath(ctx, found.Container)
120+
if err != nil {
121+
return err
122+
}
123+
116124
task, err := found.Container.Task(ctx, nil)
117125
if err != nil {
118126
return err
@@ -143,13 +151,14 @@ func logsAction(cmd *cobra.Command, args []string) error {
143151
ContainerID: found.Container.ID(),
144152
Namespace: l[labels.Namespace],
145153
DatastoreRootPath: dataStore,
154+
LogPath: logPath,
146155
Follow: follow,
147156
Timestamps: timestamps,
148157
Tail: tail,
149158
Since: since,
150159
Until: until,
151160
}
152-
logViewer, err := logging.InitContainerLogViewer(logViewOpts, stopChannel)
161+
logViewer, err := logging.InitContainerLogViewer(l, logViewOpts, stopChannel)
153162
if err != nil {
154163
return err
155164
}
@@ -167,6 +176,23 @@ func logsAction(cmd *cobra.Command, args []string) error {
167176
return nil
168177
}
169178

179+
func getLogPath(ctx context.Context, container containerd.Container) (string, error) {
180+
extensions, err := container.Extensions(ctx)
181+
if err != nil {
182+
return "", fmt.Errorf("get extensions for container %s,failed: %#v", container.ID(), err)
183+
}
184+
metaData := extensions[k8slabels.ContainerMetadataExtension]
185+
var meta containerstore.Metadata
186+
if metaData != nil {
187+
err = meta.UnmarshalJSON(metaData.GetValue())
188+
if err != nil {
189+
return "", fmt.Errorf("unmarshal extensions for container %s,failed: %#v", container.ID(), err)
190+
}
191+
}
192+
193+
return meta.LogPath, nil
194+
}
195+
170196
func logsShellComplete(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
171197
// show container names (TODO: only show containers with logs)
172198
return shellCompleteContainerNames(cmd, nil)

go.mod

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,19 @@ require (
5858
gotest.tools/v3 v3.4.0
5959
)
6060

61+
require (
62+
github.com/beorn7/perks v1.0.1 // indirect
63+
github.com/cespare/xxhash/v2 v2.1.2 // indirect
64+
github.com/docker/go-metrics v0.0.1 // indirect
65+
github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect
66+
github.com/prometheus/client_golang v1.14.0 // indirect
67+
github.com/prometheus/client_model v0.3.0 // indirect
68+
github.com/prometheus/common v0.37.0 // indirect
69+
github.com/prometheus/procfs v0.8.0 // indirect
70+
github.com/tchap/go-patricia/v2 v2.3.1 // indirect
71+
k8s.io/cri-api v0.26.0-beta.0 // indirect
72+
)
73+
6174
require (
6275
github.com/AdaLogics/go-fuzz-headers v0.0.0-20221206110420-d395f97c4830 // indirect
6376
github.com/AdamKorcz/go-118-fuzz-build v0.0.0-20221215162035-5330a85ea652 // indirect

go.sum

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,7 @@ github.com/benbjohnson/clock v1.0.3/go.mod h1:bGMdMPoPVvcYyt1gHDf4J2KE153Yf9BuiU
132132
github.com/beorn7/perks v0.0.0-20160804104726-4c0e84591b9a/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
133133
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
134134
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
135+
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
135136
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
136137
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
137138
github.com/bitly/go-simplejson v0.5.0/go.mod h1:cXHtHw4XUPsvGaxgjIAn8PhEWG9NfngEKAMDJEczWVA=
@@ -154,6 +155,7 @@ github.com/certifi/gocertifi v0.0.0-20191021191039-0944d244cd40/go.mod h1:sGbDF6
154155
github.com/certifi/gocertifi v0.0.0-20200922220541-2c3bb06c6054/go.mod h1:sGbDF6GwGcLpkNXPUTkMRoywsNa/ol15pxFe6ERfguA=
155156
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
156157
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
158+
github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE=
157159
github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
158160
github.com/checkpoint-restore/go-criu/v4 v4.1.0/go.mod h1:xUQBLp4RLc5zJtWY++yjOoMoB5lihDt7fai+75m+rGw=
159161
github.com/checkpoint-restore/go-criu/v5 v5.0.0/go.mod h1:cfwC0EG7HMUenopBsUf9d89JlCLQIfgVcNsNN0t6T2M=
@@ -387,6 +389,7 @@ github.com/docker/go-events v0.0.0-20170721190031-9461782956ad/go.mod h1:Uw6Uezg
387389
github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c h1:+pKlWGMw7gf6bQ+oDZB4KHQFypsfjYlq/C4rfL7D3g8=
388390
github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c/go.mod h1:Uw6UezgYA44ePAFQYUehOuCzmy5zmg/+nl2ZfMWGkpA=
389391
github.com/docker/go-metrics v0.0.0-20180209012529-399ea8c73916/go.mod h1:/u0gXw0Gay3ceNrsHubL3BtdOL2fHf93USgMTe0W5dI=
392+
github.com/docker/go-metrics v0.0.1 h1:AgB/0SvBxihN0X8OR4SjsblXkbMvalQ8cjmtKQ2rQV8=
390393
github.com/docker/go-metrics v0.0.1/go.mod h1:cG1hvH2utMXtqgqqYE9plW6lDxS3/5ayHzueweSI3Vw=
391394
github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
392395
github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4=
@@ -720,6 +723,8 @@ github.com/mattn/go-shellwords v1.0.12 h1:M2zGm7EW6UQJvDeQxo4T51eKPurbeFbe8WtebG
720723
github.com/mattn/go-shellwords v1.0.12/go.mod h1:EZzvwXDESEeg03EKmM+RmDnNOPKG4lLtQsUlTZDWQ8Y=
721724
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
722725
github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4=
726+
github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo=
727+
github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4=
723728
github.com/maxbrunsfeld/counterfeiter/v6 v6.2.2/go.mod h1:eD9eIE7cdwcMi9rYluz88Jz2VyhSmden33/aXg4oVIY=
724729
github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
725730
github.com/miekg/pkcs11 v1.0.3/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs=
@@ -889,12 +894,14 @@ github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP
889894
github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0=
890895
github.com/prometheus/client_golang v1.11.1/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0=
891896
github.com/prometheus/client_golang v1.12.1/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY=
897+
github.com/prometheus/client_golang v1.14.0 h1:nJdhIvne2eSX/XRAFV9PcvFFRbrjbcTUj0VP62TMhnw=
892898
github.com/prometheus/client_golang v1.14.0/go.mod h1:8vpkKitgIVNcqrRBWh1C4TIUQgYNtG/XQE4E/Zae36Y=
893899
github.com/prometheus/client_model v0.0.0-20171117100541-99fa1f4be8e5/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
894900
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
895901
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
896902
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
897903
github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
904+
github.com/prometheus/client_model v0.3.0 h1:UBgGFHqYdG/TPFD1B1ogZywDqEkwp3fBMvqdiQ7Xew4=
898905
github.com/prometheus/client_model v0.3.0/go.mod h1:LDGWKZIo7rky3hgvBe+caln+Dr3dPggB5dvjtD7w9+w=
899906
github.com/prometheus/common v0.0.0-20180110214958-89604d197083/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
900907
github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
@@ -905,6 +912,7 @@ github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB8
905912
github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc=
906913
github.com/prometheus/common v0.30.0/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls=
907914
github.com/prometheus/common v0.32.1/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls=
915+
github.com/prometheus/common v0.37.0 h1:ccBbHCgIiT9uSoFY0vX8H3zsNR5eLt17/RQLUvn8pXE=
908916
github.com/prometheus/common v0.37.0/go.mod h1:phzohg0JFMnBEFGxTDbfu3QyL5GI8gTQJFhYO5B3mfA=
909917
github.com/prometheus/procfs v0.0.0-20180125133057-cb4147076ac7/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
910918
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
@@ -1007,6 +1015,8 @@ github.com/syndtr/gocapability v0.0.0-20170704070218-db04d3cc01c8/go.mod h1:hkRG
10071015
github.com/syndtr/gocapability v0.0.0-20180916011248-d98352740cb2/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww=
10081016
github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww=
10091017
github.com/tchap/go-patricia v2.2.6+incompatible/go.mod h1:bmLyhP68RS6kStMGxByiQ23RP/odRBOTVjwp2cDyi6I=
1018+
github.com/tchap/go-patricia/v2 v2.3.1 h1:6rQp39lgIYZ+MHmdEq4xzuk1t7OdC35z/xm0BGhTkes=
1019+
github.com/tchap/go-patricia/v2 v2.3.1/go.mod h1:VZRHKAb53DLaG+nA9EaYYiaEx6YztwDlLElMsnSHD4k=
10101020
github.com/tidwall/gjson v1.14.4 h1:uo0p8EbA09J7RQaflQ1aBRffTR7xedD2bcIVSYxLnkM=
10111021
github.com/tidwall/gjson v1.14.4/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
10121022
github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA=
@@ -1742,6 +1752,8 @@ k8s.io/cri-api v0.20.6/go.mod h1:ew44AjNXwyn1s0U4xCKGodU7J1HzBeZ1MpGrpa5r8Yc=
17421752
k8s.io/cri-api v0.23.1/go.mod h1:REJE3PSU0h/LOV1APBrupxrEJqnoxZC8KWzkBUHwrK4=
17431753
k8s.io/cri-api v0.25.0/go.mod h1:J1rAyQkSJ2Q6I+aBMOVgg2/cbbebso6FNa0UagiR0kc=
17441754
k8s.io/cri-api v0.26.0-alpha.3/go.mod h1:E49tenyB7esgfIguEd7+g9qYhHOr9peyyBcSaeH6Gxw=
1755+
k8s.io/cri-api v0.26.0-beta.0 h1:mVt2/80VZy2dRN4mpVYJbPB9L/Zt/EvVev++zPTV9Tw=
1756+
k8s.io/cri-api v0.26.0-beta.0/go.mod h1:E49tenyB7esgfIguEd7+g9qYhHOr9peyyBcSaeH6Gxw=
17451757
k8s.io/gengo v0.0.0-20200413195148-3a45101e95ac/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0=
17461758
k8s.io/gengo v0.0.0-20200428234225-8167cfdcfc14/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0=
17471759
k8s.io/gengo v0.0.0-20201113003025-83324d819ded/go.mod h1:FiNAH4ZV3gBg2Kwh89tzAEV2be7d5xI0vBa/VySYy3E=

pkg/labels/k8slabels/k8slabels.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,4 +21,7 @@ const (
2121
PodNamespace = "io.kubernetes.pod.namespace"
2222
PodName = "io.kubernetes.pod.name"
2323
ContainerName = "io.kubernetes.container.name"
24+
25+
ContainerMetadataExtension = "io.cri-containerd.container.metadata"
26+
ContainerType = "io.cri-containerd.kind"
2427
)

pkg/logging/log_viewer.go

Lines changed: 22 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import (
2323
"os/exec"
2424
"path/filepath"
2525

26+
"github.com/containerd/nerdctl/pkg/labels/k8slabels"
2627
"github.com/sirupsen/logrus"
2728
)
2829

@@ -44,6 +45,7 @@ func RegisterLogViewer(driverName string, lvfn LogViewerFunc) {
4445
func init() {
4546
RegisterLogViewer("json-file", viewLogsJSONFile)
4647
RegisterLogViewer("journald", viewLogsJournald)
48+
RegisterLogViewer("text", viewLogsTextFile)
4749
}
4850

4951
// Returns a LogViewerFunc for the provided logging driver name.
@@ -64,6 +66,8 @@ type LogViewOptions struct {
6466
// Absolute path to the nerdctl datastore's root.
6567
DatastoreRootPath string
6668

69+
LogPath string
70+
6771
// Whether or not to follow the output of the container logs.
6872
Follow bool
6973

@@ -98,8 +102,8 @@ func (lvo *LogViewOptions) Validate() error {
98102
// Implements functionality for loading the logging configuration and
99103
// fetching/outputting container logs based on its internal LogViewOptions.
100104
type ContainerLogViewer struct {
101-
// Logging configuration.
102-
loggingConfig LogConfig
105+
// Logging logDriver.
106+
logDriver string
103107

104108
// Log viewing options and filters.
105109
logViewingOptions LogViewOptions
@@ -110,17 +114,24 @@ type ContainerLogViewer struct {
110114

111115
// Validates the given LogViewOptions, loads the logging config for the
112116
// given container and returns a ContainerLogViewer.
113-
func InitContainerLogViewer(lvopts LogViewOptions, stopChannel chan os.Signal) (*ContainerLogViewer, error) {
114-
if err := lvopts.Validate(); err != nil {
115-
return nil, fmt.Errorf("invalid LogViewOptions provided (%#v): %s", lvopts, err)
116-
}
117+
func InitContainerLogViewer(containerLabels map[string]string, lvopts LogViewOptions, stopChannel chan os.Signal) (*ContainerLogViewer, error) {
118+
var logDriver string
119+
if _, ok := containerLabels[k8slabels.ContainerType]; ok {
120+
logDriver = "text"
121+
} else {
122+
if err := lvopts.Validate(); err != nil {
123+
return nil, fmt.Errorf("invalid LogViewOptions provided (%#v): %s", lvopts, err)
124+
}
117125

118-
lcfg, err := LoadLogConfig(lvopts.DatastoreRootPath, lvopts.Namespace, lvopts.ContainerID)
119-
if err != nil {
120-
return nil, fmt.Errorf("failed to load logging config: %s", err)
126+
lcfg, err := LoadLogConfig(lvopts.DatastoreRootPath, lvopts.Namespace, lvopts.ContainerID)
127+
if err != nil {
128+
return nil, fmt.Errorf("failed to load logging config: %s", err)
129+
}
130+
logDriver = lcfg.Driver
121131
}
132+
122133
lv := &ContainerLogViewer{
123-
loggingConfig: lcfg,
134+
logDriver: logDriver,
124135
logViewingOptions: lvopts,
125136
stopChannel: stopChannel,
126137
}
@@ -130,7 +141,7 @@ func InitContainerLogViewer(lvopts LogViewOptions, stopChannel chan os.Signal) (
130141

131142
// Prints all logs for this LogViewer's containers to the provided io.Writers.
132143
func (lv *ContainerLogViewer) PrintLogsTo(stdout, stderr io.Writer) error {
133-
viewerFunc, err := getLogViewer(lv.loggingConfig.Driver)
144+
viewerFunc, err := getLogViewer(lv.logDriver)
134145
if err != nil {
135146
return err
136147
}

pkg/logging/tail/tail.go

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
/*
2+
Copyright 2017 The Kubernetes Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
// Copied from https://github.com/kubernetes/kubernetes/blob/master/pkg/util/tail/tail.go
18+
package tail
19+
20+
import (
21+
"bytes"
22+
"io"
23+
)
24+
25+
const (
26+
// blockSize is the block size used in tail.
27+
blockSize = 1024
28+
)
29+
30+
var (
31+
// eol is the end-of-line sign in the log.
32+
eol = []byte{'\n'}
33+
)
34+
35+
// FindTailLineStartIndex returns the start of last nth line.
36+
// * If n <= 0, return the beginning of the file.
37+
// * If n > 0, return the beginning of last nth line.
38+
// Notice that if the last line is incomplete (no end-of-line), it will not be counted
39+
// as one line.
40+
func FindTailLineStartIndex(f io.ReadSeeker, n uint) (int64, error) {
41+
if n <= 0 {
42+
return 0, nil
43+
}
44+
size, err := f.Seek(0, io.SeekEnd)
45+
if err != nil {
46+
return 0, err
47+
}
48+
var left, cnt int64
49+
buf := make([]byte, blockSize)
50+
for right := size; right > 0 && uint(cnt) <= n; right -= blockSize {
51+
left = right - blockSize
52+
if left < 0 {
53+
left = 0
54+
buf = make([]byte, right)
55+
}
56+
if _, err := f.Seek(left, io.SeekStart); err != nil {
57+
return 0, err
58+
}
59+
if _, err := f.Read(buf); err != nil {
60+
return 0, err
61+
}
62+
cnt += int64(bytes.Count(buf, eol))
63+
}
64+
for ; uint(cnt) > n; cnt-- {
65+
idx := bytes.Index(buf, eol) + 1
66+
buf = buf[idx:]
67+
left += int64(idx)
68+
}
69+
return left, nil
70+
}

pkg/logging/tail/tail_test.go

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
/*
2+
Copyright 2017 The Kubernetes Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
// Copied from https://github.com/kubernetes/kubernetes/blob/master/pkg/util/tail/tail_test.go
18+
package tail
19+
20+
import (
21+
"bytes"
22+
"strings"
23+
"testing"
24+
)
25+
26+
func TestTail(t *testing.T) {
27+
line := strings.Repeat("a", blockSize)
28+
testBytes := []byte(line + "\n" +
29+
line + "\n" +
30+
line + "\n" +
31+
line + "\n" +
32+
line[blockSize/2:]) // incomplete line
33+
34+
for c, test := range []struct {
35+
n uint32
36+
start int64
37+
}{
38+
{n: 0, start: 0},
39+
{n: 1, start: int64(len(line)+1) * 3},
40+
{n: 9999, start: 0},
41+
} {
42+
t.Logf("TestCase #%d: %+v", c, test)
43+
r := bytes.NewReader(testBytes)
44+
s, err := FindTailLineStartIndex(r, uint(test.n))
45+
if err != nil {
46+
t.Error(err)
47+
}
48+
if s != test.start {
49+
t.Errorf("%d != %d", s, test.start)
50+
}
51+
}
52+
}

0 commit comments

Comments
 (0)