parent
306e4f239f
commit
df613ec88d
2
Makefile
2
Makefile
|
|
@ -5,7 +5,7 @@ PACKAGE := github.com/derailed/$(NAME)
|
||||||
GIT_REV ?= $(shell git rev-parse --short HEAD)
|
GIT_REV ?= $(shell git rev-parse --short HEAD)
|
||||||
SOURCE_DATE_EPOCH ?= $(shell date +%s)
|
SOURCE_DATE_EPOCH ?= $(shell date +%s)
|
||||||
DATE ?= $(shell date -u -d @${SOURCE_DATE_EPOCH} +"%Y-%m-%dT%H:%M:%SZ")
|
DATE ?= $(shell date -u -d @${SOURCE_DATE_EPOCH} +"%Y-%m-%dT%H:%M:%SZ")
|
||||||
VERSION ?= v0.25.14
|
VERSION ?= v0.25.15
|
||||||
IMG_NAME := derailed/k9s
|
IMG_NAME := derailed/k9s
|
||||||
IMAGE := ${IMG_NAME}:${VERSION}
|
IMAGE := ${IMG_NAME}:${VERSION}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,27 @@
|
||||||
|
<img src="https://raw.githubusercontent.com/derailed/k9s/master/assets/k9s_small.png" align="right" width="200" height="auto"/>
|
||||||
|
|
||||||
|
# Release v0.25.15
|
||||||
|
|
||||||
|
## Notes
|
||||||
|
|
||||||
|
Thank you to all that contributed with flushing out issues and enhancements for K9s! I'll try to mark some of these issues as fixed. But if you don't mind grab the latest rev and see if we're happier with some of the fixes! If you've filed an issue please help me verify and close. Your support, kindness and awesome suggestions to make K9s better are as ever very much noted and appreciated!
|
||||||
|
|
||||||
|
If you feel K9s is helping your Kubernetes journey, please consider joining our [sponsorship program](https://github.com/sponsors/derailed) and/or make some noise on social! [@kitesurfer](https://twitter.com/kitesurfer)
|
||||||
|
|
||||||
|
On Slack? Please join us [K9slackers](https://join.slack.com/t/k9sers/shared_invite/enQtOTA5MDEyNzI5MTU0LWQ1ZGI3MzliYzZhZWEyNzYxYzA3NjE0YTk1YmFmNzViZjIyNzhkZGI0MmJjYzhlNjdlMGJhYzE2ZGU1NjkyNTM)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Maintenance Release!
|
||||||
|
|
||||||
|
Aye! Hot fix on the way...
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Resolved Issues
|
||||||
|
|
||||||
|
* [Issue #1384](https://github.com/derailed/k9s/issues/1384) Leaving Logs View Causes Crash: "panic: send on closed channel" - with feelings!
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
<img src="https://raw.githubusercontent.com/derailed/k9s/master/assets/imhotep_logo.png" width="32" height="auto"/> © 2021 Imhotep Software LLC. All materials licensed under [Apache v2.0](http://www.apache.org/licenses/LICENSE-2.0)
|
||||||
|
|
@ -55,11 +55,11 @@ func (c *Container) List(ctx context.Context, _ string) ([]runtime.Object, error
|
||||||
}
|
}
|
||||||
|
|
||||||
// TailLogs tails a given container logs.
|
// TailLogs tails a given container logs.
|
||||||
func (c *Container) TailLogs(ctx context.Context, logChan LogChan, opts *LogOptions) error {
|
func (c *Container) TailLogs(ctx context.Context, opts *LogOptions) ([]LogChan, error) {
|
||||||
po := Pod{}
|
po := Pod{}
|
||||||
po.Init(c.Factory, client.NewGVR("v1/pods"))
|
po.Init(c.Factory, client.NewGVR("v1/pods"))
|
||||||
|
|
||||||
return po.TailLogs(ctx, logChan, opts)
|
return po.TailLogs(ctx, opts)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
|
@ -114,16 +114,16 @@ func (d *Deployment) Restart(ctx context.Context, path string) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// TailLogs tail logs for all pods represented by this Deployment.
|
// TailLogs tail logs for all pods represented by this Deployment.
|
||||||
func (d *Deployment) TailLogs(ctx context.Context, c LogChan, opts *LogOptions) error {
|
func (d *Deployment) TailLogs(ctx context.Context, opts *LogOptions) ([]LogChan, error) {
|
||||||
dp, err := d.Load(d.Factory, opts.Path)
|
dp, err := d.Load(d.Factory, opts.Path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return nil, err
|
||||||
}
|
}
|
||||||
if dp.Spec.Selector == nil || len(dp.Spec.Selector.MatchLabels) == 0 {
|
if dp.Spec.Selector == nil || len(dp.Spec.Selector.MatchLabels) == 0 {
|
||||||
return fmt.Errorf("No valid selector found on Deployment %s", opts.Path)
|
return nil, fmt.Errorf("No valid selector found on Deployment %s", opts.Path)
|
||||||
}
|
}
|
||||||
|
|
||||||
return podLogs(ctx, c, dp.Spec.Selector.MatchLabels, opts)
|
return podLogs(ctx, dp.Spec.Selector.MatchLabels, opts)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Pod returns a pod victim by name.
|
// Pod returns a pod victim by name.
|
||||||
|
|
|
||||||
|
|
@ -91,54 +91,59 @@ func (d *DaemonSet) Restart(ctx context.Context, path string) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// TailLogs tail logs for all pods represented by this DaemonSet.
|
// TailLogs tail logs for all pods represented by this DaemonSet.
|
||||||
func (d *DaemonSet) TailLogs(ctx context.Context, c LogChan, opts *LogOptions) error {
|
func (d *DaemonSet) TailLogs(ctx context.Context, opts *LogOptions) ([]LogChan, error) {
|
||||||
ds, err := d.GetInstance(opts.Path)
|
ds, err := d.GetInstance(opts.Path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if ds.Spec.Selector == nil || len(ds.Spec.Selector.MatchLabels) == 0 {
|
if ds.Spec.Selector == nil || len(ds.Spec.Selector.MatchLabels) == 0 {
|
||||||
return fmt.Errorf("no valid selector found on daemonset %q", opts.Path)
|
return nil, fmt.Errorf("no valid selector found on daemonset %q", opts.Path)
|
||||||
}
|
}
|
||||||
|
|
||||||
return podLogs(ctx, c, ds.Spec.Selector.MatchLabels, opts)
|
return podLogs(ctx, ds.Spec.Selector.MatchLabels, opts)
|
||||||
}
|
}
|
||||||
|
|
||||||
func podLogs(ctx context.Context, out LogChan, sel map[string]string, opts *LogOptions) error {
|
func podLogs(ctx context.Context, sel map[string]string, opts *LogOptions) ([]LogChan, error) {
|
||||||
f, ok := ctx.Value(internal.KeyFactory).(*watch.Factory)
|
f, ok := ctx.Value(internal.KeyFactory).(*watch.Factory)
|
||||||
if !ok {
|
if !ok {
|
||||||
return errors.New("expecting a context factory")
|
return nil, errors.New("expecting a context factory")
|
||||||
}
|
}
|
||||||
ls, err := metav1.ParseToLabelSelector(toSelector(sel))
|
ls, err := metav1.ParseToLabelSelector(toSelector(sel))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return nil, err
|
||||||
}
|
}
|
||||||
lsel, err := metav1.LabelSelectorAsSelector(ls)
|
lsel, err := metav1.LabelSelectorAsSelector(ls)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
ns, _ := client.Namespaced(opts.Path)
|
ns, _ := client.Namespaced(opts.Path)
|
||||||
oo, err := f.List("v1/pods", ns, true, lsel)
|
oo, err := f.List("v1/pods", ns, true, lsel)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return nil, err
|
||||||
}
|
}
|
||||||
opts.MultiPods = true
|
opts.MultiPods = true
|
||||||
|
|
||||||
po := Pod{}
|
po := Pod{}
|
||||||
po.Init(f, client.NewGVR("v1/pods"))
|
po.Init(f, client.NewGVR("v1/pods"))
|
||||||
|
|
||||||
|
outs := make([]LogChan, 0, len(oo))
|
||||||
for _, o := range oo {
|
for _, o := range oo {
|
||||||
u, ok := o.(*unstructured.Unstructured)
|
u, ok := o.(*unstructured.Unstructured)
|
||||||
if !ok {
|
if !ok {
|
||||||
return fmt.Errorf("expected unstructured got %t", o)
|
return nil, fmt.Errorf("expected unstructured got %t", o)
|
||||||
}
|
}
|
||||||
opts = opts.Clone()
|
opts = opts.Clone()
|
||||||
opts.Path = client.FQN(u.GetNamespace(), u.GetName())
|
opts.Path = client.FQN(u.GetNamespace(), u.GetName())
|
||||||
if err := po.TailLogs(ctx, out, opts); err != nil {
|
cc, err := po.TailLogs(ctx, opts)
|
||||||
return err
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
}
|
}
|
||||||
|
outs = append(outs, cc...)
|
||||||
}
|
}
|
||||||
return nil
|
|
||||||
|
return outs, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Pod returns a pod victim by name.
|
// Pod returns a pod victim by name.
|
||||||
|
|
|
||||||
|
|
@ -57,23 +57,23 @@ func (j *Job) List(ctx context.Context, ns string) ([]runtime.Object, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// TailLogs tail logs for all pods represented by this Job.
|
// TailLogs tail logs for all pods represented by this Job.
|
||||||
func (j *Job) TailLogs(ctx context.Context, c LogChan, opts *LogOptions) error {
|
func (j *Job) TailLogs(ctx context.Context, opts *LogOptions) ([]LogChan, error) {
|
||||||
o, err := j.Factory.Get(j.gvr.String(), opts.Path, true, labels.Everything())
|
o, err := j.Factory.Get(j.gvr.String(), opts.Path, true, labels.Everything())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
var job batchv1.Job
|
var job batchv1.Job
|
||||||
err = runtime.DefaultUnstructuredConverter.FromUnstructured(o.(*unstructured.Unstructured).Object, &job)
|
err = runtime.DefaultUnstructuredConverter.FromUnstructured(o.(*unstructured.Unstructured).Object, &job)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.New("expecting a job resource")
|
return nil, errors.New("expecting a job resource")
|
||||||
}
|
}
|
||||||
|
|
||||||
if job.Spec.Selector == nil || len(job.Spec.Selector.MatchLabels) == 0 {
|
if job.Spec.Selector == nil || len(job.Spec.Selector.MatchLabels) == 0 {
|
||||||
return fmt.Errorf("No valid selector found on Job %s", opts.Path)
|
return nil, fmt.Errorf("No valid selector found on Job %s", opts.Path)
|
||||||
}
|
}
|
||||||
|
|
||||||
return podLogs(ctx, c, job.Spec.Selector.MatchLabels, opts)
|
return podLogs(ctx, job.Spec.Selector.MatchLabels, opts)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ScanSA scans for serviceaccount refs.
|
// ScanSA scans for serviceaccount refs.
|
||||||
|
|
|
||||||
|
|
@ -42,6 +42,7 @@ func (o *LogOptions) Clone() *LogOptions {
|
||||||
DefaultContainer: o.DefaultContainer,
|
DefaultContainer: o.DefaultContainer,
|
||||||
Lines: o.Lines,
|
Lines: o.Lines,
|
||||||
Previous: o.Previous,
|
Previous: o.Previous,
|
||||||
|
Head: o.Head,
|
||||||
SingleContainer: o.SingleContainer,
|
SingleContainer: o.SingleContainer,
|
||||||
MultiPods: o.MultiPods,
|
MultiPods: o.MultiPods,
|
||||||
ShowTimestamp: o.ShowTimestamp,
|
ShowTimestamp: o.ShowTimestamp,
|
||||||
|
|
@ -82,14 +83,9 @@ func (o *LogOptions) ToPodLogOptions() *v1.PodLogOptions {
|
||||||
TailLines: &o.Lines,
|
TailLines: &o.Lines,
|
||||||
}
|
}
|
||||||
if o.Head {
|
if o.Head {
|
||||||
var maxBytes int64 = 1000
|
var maxBytes int64 = 5000
|
||||||
//var defaultTail int64 = -1
|
|
||||||
//var defaultSince int64
|
|
||||||
|
|
||||||
opts.Follow = false
|
opts.Follow = false
|
||||||
opts.TailLines, opts.SinceSeconds, opts.SinceTime = nil, nil, nil
|
opts.TailLines, opts.SinceSeconds, opts.SinceTime = nil, nil, nil
|
||||||
//opts.TailLines = &defaultTail
|
|
||||||
//opts.SinceSeconds = &defaultSince
|
|
||||||
opts.LimitBytes = &maxBytes
|
opts.LimitBytes = &maxBytes
|
||||||
return &opts
|
return &opts
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/derailed/k9s/internal"
|
"github.com/derailed/k9s/internal"
|
||||||
|
|
@ -125,12 +126,12 @@ func (p *Pod) Logs(path string, opts *v1.PodLogOptions) (*restclient.Request, er
|
||||||
return nil, fmt.Errorf("user is not authorized to view pod logs")
|
return nil, fmt.Errorf("user is not authorized to view pod logs")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ns, n := client.Namespaced(path)
|
||||||
dial, err := p.Client().DialLogs()
|
dial, err := p.Client().DialLogs()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
ns, n := client.Namespaced(path)
|
|
||||||
return dial.CoreV1().Pods(ns).GetLogs(n, opts), nil
|
return dial.CoreV1().Pods(ns).GetLogs(n, opts), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -177,64 +178,49 @@ func (p *Pod) GetInstance(fqn string) (*v1.Pod, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// TailLogs tails a given container logs.
|
// TailLogs tails a given container logs.
|
||||||
func (p *Pod) TailLogs(ctx context.Context, out LogChan, opts *LogOptions) error {
|
func (p *Pod) TailLogs(ctx context.Context, opts *LogOptions) ([]LogChan, error) {
|
||||||
fac, ok := ctx.Value(internal.KeyFactory).(*watch.Factory)
|
fac, ok := ctx.Value(internal.KeyFactory).(*watch.Factory)
|
||||||
if !ok {
|
if !ok {
|
||||||
return errors.New("No factory in context")
|
return nil, errors.New("No factory in context")
|
||||||
}
|
}
|
||||||
o, err := fac.Get(p.gvr.String(), opts.Path, true, labels.Everything())
|
o, err := fac.Get(p.gvr.String(), opts.Path, true, labels.Everything())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return nil, err
|
||||||
}
|
}
|
||||||
var po v1.Pod
|
var po v1.Pod
|
||||||
if err := runtime.DefaultUnstructuredConverter.FromUnstructured(o.(*unstructured.Unstructured).Object, &po); err != nil {
|
if err := runtime.DefaultUnstructuredConverter.FromUnstructured(o.(*unstructured.Unstructured).Object, &po); err != nil {
|
||||||
return err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
coCounts := len(po.Spec.InitContainers) + len(po.Spec.Containers) + len(po.Spec.EphemeralContainers)
|
||||||
if len(po.Spec.InitContainers)+len(po.Spec.Containers) == 1 {
|
if coCounts == 1 {
|
||||||
opts.SingleContainer = true
|
opts.SingleContainer = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
outs := make([]LogChan, 0, coCounts)
|
||||||
if co, ok := GetDefaultLogContainer(po.ObjectMeta, po.Spec); ok && !opts.AllContainers {
|
if co, ok := GetDefaultLogContainer(po.ObjectMeta, po.Spec); ok && !opts.AllContainers {
|
||||||
opts.DefaultContainer = co
|
opts.DefaultContainer = co
|
||||||
return tailLogs(ctx, p, out, opts)
|
return append(outs, tailLogs(ctx, p, opts)), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if opts.HasContainer() && !opts.AllContainers {
|
if opts.HasContainer() && !opts.AllContainers {
|
||||||
return tailLogs(ctx, p, out, opts)
|
return append(outs, tailLogs(ctx, p, opts)), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
var tailed bool
|
|
||||||
for _, co := range po.Spec.InitContainers {
|
for _, co := range po.Spec.InitContainers {
|
||||||
o := opts.Clone()
|
o := opts.Clone()
|
||||||
o.Container = co.Name
|
o.Container = co.Name
|
||||||
if err := tailLogs(ctx, p, out, o); err != nil {
|
outs = append(outs, tailLogs(ctx, p, o))
|
||||||
return err
|
|
||||||
}
|
|
||||||
tailed = true
|
|
||||||
}
|
}
|
||||||
for _, co := range po.Spec.Containers {
|
for _, co := range po.Spec.Containers {
|
||||||
o := opts.Clone()
|
o := opts.Clone()
|
||||||
o.Container = co.Name
|
o.Container = co.Name
|
||||||
if err := tailLogs(ctx, p, out, o); err != nil {
|
outs = append(outs, tailLogs(ctx, p, o))
|
||||||
return err
|
|
||||||
}
|
|
||||||
tailed = true
|
|
||||||
}
|
}
|
||||||
for _, co := range po.Spec.EphemeralContainers {
|
for _, co := range po.Spec.EphemeralContainers {
|
||||||
o := opts.Clone()
|
o := opts.Clone()
|
||||||
o.Container = co.Name
|
o.Container = co.Name
|
||||||
if err := tailLogs(ctx, p, out, o); err != nil {
|
outs = append(outs, tailLogs(ctx, p, o))
|
||||||
return err
|
|
||||||
}
|
|
||||||
tailed = true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if !tailed {
|
return outs, nil
|
||||||
return fmt.Errorf("no loggable containers found for pod %s", opts.Path)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ScanSA scans for ServiceAccount refs.
|
// ScanSA scans for ServiceAccount refs.
|
||||||
|
|
@ -325,23 +311,30 @@ func (p *Pod) Scan(ctx context.Context, gvr, fqn string, wait bool) (Refs, error
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
// Helpers...
|
// Helpers...
|
||||||
|
|
||||||
func tailLogs(ctx context.Context, logger Logger, out LogChan, opts *LogOptions) error {
|
func tailLogs(ctx context.Context, logger Logger, opts *LogOptions) LogChan {
|
||||||
var (
|
var (
|
||||||
err error
|
out = make(LogChan, 2)
|
||||||
req *restclient.Request
|
wg sync.WaitGroup
|
||||||
stream io.ReadCloser
|
|
||||||
)
|
)
|
||||||
|
|
||||||
o := opts.ToPodLogOptions()
|
wg.Add(1)
|
||||||
done:
|
go func() {
|
||||||
|
defer func() {
|
||||||
|
wg.Done()
|
||||||
|
log.Debug().Msgf("<<< RETRY-TAIL DONE!!! %s", opts.Info())
|
||||||
|
}()
|
||||||
|
podOpts := opts.ToPodLogOptions()
|
||||||
|
log.Debug().Msgf(">>> RETRY-TAIL START %s", opts.Info())
|
||||||
|
var stream io.ReadCloser
|
||||||
for r := 0; r < logRetryCount; r++ {
|
for r := 0; r < logRetryCount; r++ {
|
||||||
var e error
|
var e error
|
||||||
req, err = logger.Logs(opts.Path, o)
|
req, err := logger.Logs(opts.Path, podOpts)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
// This call will block if nothing is in the stream!!
|
// This call will block if nothing is in the stream!!
|
||||||
if stream, err = req.Stream(ctx); err == nil {
|
if stream, err = req.Stream(ctx); err == nil {
|
||||||
go readLogs(ctx, stream, out, opts)
|
wg.Add(1)
|
||||||
break
|
go readLogs(ctx, &wg, stream, out, opts)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
e = fmt.Errorf("stream logs failed %w for %s", err, opts.Info())
|
e = fmt.Errorf("stream logs failed %w for %s", err, opts.Info())
|
||||||
log.Error().Err(e).Msg("logs-stream")
|
log.Error().Err(e).Msg("logs-stream")
|
||||||
|
|
@ -352,8 +345,8 @@ done:
|
||||||
|
|
||||||
select {
|
select {
|
||||||
case <-ctx.Done():
|
case <-ctx.Done():
|
||||||
err = ctx.Err()
|
log.Debug().Msgf("LOG CANCELED %s", opts.Info())
|
||||||
break done
|
return
|
||||||
default:
|
default:
|
||||||
if e != nil {
|
if e != nil {
|
||||||
out <- opts.ToErrLogItem(e)
|
out <- opts.ToErrLogItem(e)
|
||||||
|
|
@ -361,42 +354,47 @@ done:
|
||||||
time.Sleep(logRetryWait)
|
time.Sleep(logRetryWait)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}()
|
||||||
|
go func() {
|
||||||
|
wg.Wait()
|
||||||
|
close(out)
|
||||||
|
log.Debug().Msgf("<<< LOG-TAILER %s DONE!!", opts.Info())
|
||||||
|
}()
|
||||||
|
|
||||||
return err
|
return out
|
||||||
}
|
}
|
||||||
|
|
||||||
func readLogs(ctx context.Context, stream io.ReadCloser, c LogChan, opts *LogOptions) {
|
func readLogs(ctx context.Context, wg *sync.WaitGroup, stream io.ReadCloser, out chan<- *LogItem, opts *LogOptions) {
|
||||||
defer func() {
|
defer func() {
|
||||||
if err := stream.Close(); err != nil {
|
if err := stream.Close(); err != nil {
|
||||||
log.Error().Err(err).Msgf("Fail to close stream %s", opts.Info())
|
log.Error().Err(err).Msgf("Fail to close stream %s", opts.Info())
|
||||||
}
|
}
|
||||||
|
log.Debug().Msgf("<<< LOG-READER EXIT!!! %s", opts.Info())
|
||||||
|
wg.Done()
|
||||||
}()
|
}()
|
||||||
|
|
||||||
log.Debug().Msgf("READ_LOGS PROCESSING %#v", opts)
|
log.Debug().Msgf(">>> LOG-READER PROCESSING %#v", opts)
|
||||||
r := bufio.NewReader(stream)
|
r := bufio.NewReader(stream)
|
||||||
for {
|
for {
|
||||||
var item *LogItem
|
var item *LogItem
|
||||||
bytes, err := r.ReadBytes('\n')
|
if bytes, err := r.ReadBytes('\n'); err == nil {
|
||||||
if err == nil {
|
|
||||||
item = opts.ToLogItem(bytes)
|
item = opts.ToLogItem(bytes)
|
||||||
} else {
|
} else {
|
||||||
if errors.Is(err, io.EOF) {
|
if errors.Is(err, io.EOF) {
|
||||||
e := fmt.Errorf("Stream closed %w for %s", err, opts.Info())
|
e := fmt.Errorf("Stream closed %w for %s", err, opts.Info())
|
||||||
item = opts.ToErrLogItem(e)
|
item = opts.ToErrLogItem(e)
|
||||||
log.Warn().Err(e).Msgf("stream closed")
|
log.Debug().Err(e).Msg("log-reader EOF")
|
||||||
} else {
|
} else {
|
||||||
e := fmt.Errorf("Stream failed %w for %s", err, opts.Info())
|
e := fmt.Errorf("Stream canceled %w for %s", err, opts.Info())
|
||||||
item = opts.ToErrLogItem(e)
|
item = opts.ToErrLogItem(e)
|
||||||
log.Warn().Err(e).Msgf("stream read failed")
|
log.Debug().Err(e).Msg("log-reader canceled")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
select {
|
select {
|
||||||
case <-ctx.Done():
|
case <-ctx.Done():
|
||||||
close(c)
|
|
||||||
return
|
return
|
||||||
case c <- item:
|
case out <- item:
|
||||||
if item.IsError {
|
if item.IsError {
|
||||||
close(c)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -132,16 +132,16 @@ func (*StatefulSet) Load(f Factory, fqn string) (*appsv1.StatefulSet, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// TailLogs tail logs for all pods represented by this StatefulSet.
|
// TailLogs tail logs for all pods represented by this StatefulSet.
|
||||||
func (s *StatefulSet) TailLogs(ctx context.Context, c LogChan, opts *LogOptions) error {
|
func (s *StatefulSet) TailLogs(ctx context.Context, opts *LogOptions) ([]LogChan, error) {
|
||||||
sts, err := s.getStatefulSet(opts.Path)
|
sts, err := s.getStatefulSet(opts.Path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.New("expecting StatefulSet resource")
|
return nil, errors.New("expecting StatefulSet resource")
|
||||||
}
|
}
|
||||||
if sts.Spec.Selector == nil || len(sts.Spec.Selector.MatchLabels) == 0 {
|
if sts.Spec.Selector == nil || len(sts.Spec.Selector.MatchLabels) == 0 {
|
||||||
return fmt.Errorf("No valid selector found on StatefulSet %s", opts.Path)
|
return nil, fmt.Errorf("No valid selector found on StatefulSet %s", opts.Path)
|
||||||
}
|
}
|
||||||
|
|
||||||
return podLogs(ctx, c, sts.Spec.Selector.MatchLabels, opts)
|
return podLogs(ctx, sts.Spec.Selector.MatchLabels, opts)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Pod returns a pod victim by name.
|
// Pod returns a pod victim by name.
|
||||||
|
|
|
||||||
|
|
@ -24,16 +24,16 @@ type Service struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// TailLogs tail logs for all pods represented by this Service.
|
// TailLogs tail logs for all pods represented by this Service.
|
||||||
func (s *Service) TailLogs(ctx context.Context, c LogChan, opts *LogOptions) error {
|
func (s *Service) TailLogs(ctx context.Context, opts *LogOptions) ([]LogChan, error) {
|
||||||
svc, err := s.GetInstance(opts.Path)
|
svc, err := s.GetInstance(opts.Path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return nil, err
|
||||||
}
|
}
|
||||||
if svc.Spec.Selector == nil || len(svc.Spec.Selector) == 0 {
|
if svc.Spec.Selector == nil || len(svc.Spec.Selector) == 0 {
|
||||||
return fmt.Errorf("no valid selector found on Service %s", opts.Path)
|
return nil, fmt.Errorf("no valid selector found on Service %s", opts.Path)
|
||||||
}
|
}
|
||||||
|
|
||||||
return podLogs(ctx, c, svc.Spec.Selector, opts)
|
return podLogs(ctx, svc.Spec.Selector, opts)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Pod returns a pod victim by name.
|
// Pod returns a pod victim by name.
|
||||||
|
|
|
||||||
|
|
@ -93,7 +93,7 @@ type NodeMaintainer interface {
|
||||||
// Loggable represents resources with logs.
|
// Loggable represents resources with logs.
|
||||||
type Loggable interface {
|
type Loggable interface {
|
||||||
// TaiLogs streams resource logs.
|
// TaiLogs streams resource logs.
|
||||||
TailLogs(ctx context.Context, c LogChan, opts *LogOptions) error
|
TailLogs(ctx context.Context, opts *LogOptions) ([]LogChan, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Describer describes a resource.
|
// Describer describes a resource.
|
||||||
|
|
|
||||||
|
|
@ -89,19 +89,19 @@ func (l *Log) ToggleShowTimestamp(b bool) {
|
||||||
l.Refresh()
|
l.Refresh()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *Log) Head(ctx context.Context, c dao.LogChan) {
|
func (l *Log) Head(ctx context.Context) {
|
||||||
l.mx.Lock()
|
l.mx.Lock()
|
||||||
{
|
{
|
||||||
l.logOptions.Head = true
|
l.logOptions.Head = true
|
||||||
}
|
}
|
||||||
l.mx.Unlock()
|
l.mx.Unlock()
|
||||||
l.Restart(ctx, c, true)
|
l.Restart(ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetSinceSeconds sets the logs retrieval time.
|
// SetSinceSeconds sets the logs retrieval time.
|
||||||
func (l *Log) SetSinceSeconds(ctx context.Context, c dao.LogChan, i int64) {
|
func (l *Log) SetSinceSeconds(ctx context.Context, i int64) {
|
||||||
l.logOptions.SinceSeconds, l.logOptions.Head = i, false
|
l.logOptions.SinceSeconds, l.logOptions.Head = i, false
|
||||||
l.Restart(ctx, c, true)
|
l.Restart(ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Configure sets logger configuration.
|
// Configure sets logger configuration.
|
||||||
|
|
@ -151,18 +151,16 @@ func (l *Log) Refresh() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Restart restarts the logger.
|
// Restart restarts the logger.
|
||||||
func (l *Log) Restart(ctx context.Context, c dao.LogChan, clear bool) {
|
func (l *Log) Restart(ctx context.Context) {
|
||||||
l.Stop()
|
l.Stop()
|
||||||
if clear {
|
|
||||||
l.Clear()
|
l.Clear()
|
||||||
}
|
|
||||||
l.fireLogResume()
|
l.fireLogResume()
|
||||||
l.Start(ctx, c)
|
l.Start(ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Start starts logging.
|
// Start starts logging.
|
||||||
func (l *Log) Start(ctx context.Context, c dao.LogChan) {
|
func (l *Log) Start(ctx context.Context) {
|
||||||
if err := l.load(ctx, c); err != nil {
|
if err := l.load(ctx); err != nil {
|
||||||
log.Error().Err(err).Msgf("Tail logs failed!")
|
log.Error().Err(err).Msgf("Tail logs failed!")
|
||||||
l.fireLogError(err)
|
l.fireLogError(err)
|
||||||
}
|
}
|
||||||
|
|
@ -170,7 +168,6 @@ func (l *Log) Start(ctx context.Context, c dao.LogChan) {
|
||||||
|
|
||||||
// Stop terminates logging.
|
// Stop terminates logging.
|
||||||
func (l *Log) Stop() {
|
func (l *Log) Stop() {
|
||||||
defer log.Debug().Msgf("<<<< Logger STOPPED!")
|
|
||||||
l.cancel()
|
l.cancel()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -214,16 +211,17 @@ func (l *Log) Filter(q string) {
|
||||||
l.fireLogBuffChanged(0)
|
l.fireLogBuffChanged(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *Log) load(ctx context.Context, c dao.LogChan) error {
|
func (l *Log) cancel() {
|
||||||
|
l.mx.Lock()
|
||||||
|
defer l.mx.Unlock()
|
||||||
if l.cancelFn != nil {
|
if l.cancelFn != nil {
|
||||||
l.cancelFn()
|
l.cancelFn()
|
||||||
|
log.Debug().Msgf("!!! LOG-MODEL CANCELED !!!")
|
||||||
l.cancelFn = nil
|
l.cancelFn = nil
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ctx = context.WithValue(context.Background(), internal.KeyFactory, l.factory)
|
func (l *Log) load(ctx context.Context) error {
|
||||||
ctx, l.cancelFn = context.WithCancel(ctx)
|
|
||||||
go l.updateLogs(ctx, c)
|
|
||||||
|
|
||||||
accessor, err := dao.AccessorFor(l.factory, l.gvr)
|
accessor, err := dao.AccessorFor(l.factory, l.gvr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|
@ -233,36 +231,28 @@ func (l *Log) load(ctx context.Context, c dao.LogChan) error {
|
||||||
return fmt.Errorf("Resource %s is not Loggable", l.gvr)
|
return fmt.Errorf("Resource %s is not Loggable", l.gvr)
|
||||||
}
|
}
|
||||||
|
|
||||||
go func() {
|
l.cancel()
|
||||||
if err = loggable.TailLogs(ctx, c, l.logOptions); err != nil {
|
ctx = context.WithValue(ctx, internal.KeyFactory, l.factory)
|
||||||
|
ctx, l.cancelFn = context.WithCancel(ctx)
|
||||||
|
|
||||||
|
cc, err := loggable.TailLogs(ctx, l.logOptions)
|
||||||
|
if err != nil {
|
||||||
log.Error().Err(err).Msgf("Tail logs failed")
|
log.Error().Err(err).Msgf("Tail logs failed")
|
||||||
l.cancel()
|
l.cancel()
|
||||||
l.fireLogError(err)
|
l.fireLogError(err)
|
||||||
}
|
}
|
||||||
}()
|
for _, c := range cc {
|
||||||
|
go l.updateLogs(ctx, c)
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *Log) cancel() {
|
|
||||||
l.mx.Lock()
|
|
||||||
{
|
|
||||||
if l.cancelFn == nil {
|
|
||||||
l.mx.Unlock()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
l.cancelFn()
|
|
||||||
l.cancelFn = nil
|
|
||||||
}
|
|
||||||
l.mx.Unlock()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Append adds a log line.
|
// Append adds a log line.
|
||||||
func (l *Log) Append(line *dao.LogItem) {
|
func (l *Log) Append(line *dao.LogItem) {
|
||||||
if line == nil || line.IsEmpty() {
|
if line == nil || line.IsEmpty() {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
l.mx.Lock()
|
l.mx.Lock()
|
||||||
defer l.mx.Unlock()
|
defer l.mx.Unlock()
|
||||||
l.logOptions.SinceTime = line.GetTimestamp()
|
l.logOptions.SinceTime = line.GetTimestamp()
|
||||||
|
|
@ -289,19 +279,18 @@ func (l *Log) Notify() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// ToggleAllContainers toggles to show all containers logs.
|
// ToggleAllContainers toggles to show all containers logs.
|
||||||
func (l *Log) ToggleAllContainers(ctx context.Context, c dao.LogChan) {
|
func (l *Log) ToggleAllContainers(ctx context.Context) {
|
||||||
l.logOptions.ToggleAllContainers()
|
l.logOptions.ToggleAllContainers()
|
||||||
l.Restart(ctx, c, true)
|
l.Restart(ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *Log) updateLogs(ctx context.Context, c dao.LogChan) {
|
func (l *Log) updateLogs(ctx context.Context, c dao.LogChan) {
|
||||||
defer log.Debug().Msgf("updateLogs view bailing out!")
|
defer log.Debug().Msgf("<<< LOG-MODEL UPDATER DONE %s!!!!", l.logOptions.Info())
|
||||||
|
log.Debug().Msgf(">>> START LOG-MODEL UPDATER %s", l.logOptions.Info())
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case item, ok := <-c:
|
case item, ok := <-c:
|
||||||
if !ok {
|
if !ok {
|
||||||
log.Debug().Msgf("Closed channel detected. Bailing out!")
|
|
||||||
l.Append(item)
|
l.Append(item)
|
||||||
l.Notify()
|
l.Notify()
|
||||||
return
|
return
|
||||||
|
|
|
||||||
|
|
@ -99,10 +99,9 @@ func TestLogStartStop(t *testing.T) {
|
||||||
v := newTestView()
|
v := newTestView()
|
||||||
m.AddListener(v)
|
m.AddListener(v)
|
||||||
|
|
||||||
c := make(dao.LogChan, 2)
|
|
||||||
ctx, cancel := context.WithCancel(context.Background())
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
defer cancel()
|
defer cancel()
|
||||||
m.Start(ctx, c)
|
m.Start(ctx)
|
||||||
data := dao.NewLogItems()
|
data := dao.NewLogItems()
|
||||||
data.Add(dao.NewLogItemFromString("line1"), dao.NewLogItemFromString("line2"))
|
data.Add(dao.NewLogItemFromString("line1"), dao.NewLogItemFromString("line2"))
|
||||||
for _, d := range data.Items() {
|
for _, d := range data.Items() {
|
||||||
|
|
@ -227,10 +226,9 @@ func TestToggleAllContainers(t *testing.T) {
|
||||||
ctx, cancel := context.WithCancel(context.Background())
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
c := make(dao.LogChan, 2)
|
m.ToggleAllContainers(ctx)
|
||||||
m.ToggleAllContainers(ctx, c)
|
|
||||||
assert.Equal(t, "", m.GetContainer())
|
assert.Equal(t, "", m.GetContainer())
|
||||||
m.ToggleAllContainers(ctx, c)
|
m.ToggleAllContainers(ctx)
|
||||||
assert.Equal(t, "blee", m.GetContainer())
|
assert.Equal(t, "blee", m.GetContainer())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -183,7 +183,7 @@ func (a *App) keyboard(evt *tcell.EventKey) *tcell.EventKey {
|
||||||
|
|
||||||
func (a *App) bindKeys() {
|
func (a *App) bindKeys() {
|
||||||
a.AddActions(ui.KeyActions{
|
a.AddActions(ui.KeyActions{
|
||||||
ui.KeyShiftG: ui.NewSharedKeyAction("DumpGOR", a.dumpGOR, false),
|
ui.KeyShift9: ui.NewSharedKeyAction("DumpGOR", a.dumpGOR, false),
|
||||||
tcell.KeyCtrlE: ui.NewSharedKeyAction("ToggleHeader", a.toggleHeaderCmd, false),
|
tcell.KeyCtrlE: ui.NewSharedKeyAction("ToggleHeader", a.toggleHeaderCmd, false),
|
||||||
tcell.KeyCtrlG: ui.NewSharedKeyAction("toggleCrumbs", a.toggleCrumbsCmd, false),
|
tcell.KeyCtrlG: ui.NewSharedKeyAction("toggleCrumbs", a.toggleCrumbsCmd, false),
|
||||||
ui.KeyHelp: ui.NewSharedKeyAction("Help", a.helpCmd, false),
|
ui.KeyHelp: ui.NewSharedKeyAction("Help", a.helpCmd, false),
|
||||||
|
|
@ -193,9 +193,10 @@ func (a *App) bindKeys() {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *App) dumpGOR(evt *tcell.EventKey) *tcell.EventKey {
|
func (a *App) dumpGOR(evt *tcell.EventKey) *tcell.EventKey {
|
||||||
bb := make([]byte, 5_000_000)
|
log.Debug().Msgf("GOR %d", runtime.NumGoroutine())
|
||||||
runtime.Stack(bb, true)
|
// bb := make([]byte, 5_000_000)
|
||||||
log.Debug().Msgf("GOR\n%s", string(bb))
|
// runtime.Stack(bb, true)
|
||||||
|
// log.Debug().Msgf("GOR\n%s", string(bb))
|
||||||
return evt
|
return evt
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -42,7 +42,6 @@ type Log struct {
|
||||||
cancelFn context.CancelFunc
|
cancelFn context.CancelFunc
|
||||||
cancelUpdates bool
|
cancelUpdates bool
|
||||||
mx sync.Mutex
|
mx sync.Mutex
|
||||||
logChan dao.LogChan
|
|
||||||
follow bool
|
follow bool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -52,7 +51,6 @@ var _ model.Component = (*Log)(nil)
|
||||||
func NewLog(gvr client.GVR, opts *dao.LogOptions) *Log {
|
func NewLog(gvr client.GVR, opts *dao.LogOptions) *Log {
|
||||||
l := Log{
|
l := Log{
|
||||||
Flex: tview.NewFlex(),
|
Flex: tview.NewFlex(),
|
||||||
logChan: make(dao.LogChan, 2),
|
|
||||||
model: model.NewLog(gvr, opts, defaultFlushTimeout),
|
model: model.NewLog(gvr, opts, defaultFlushTimeout),
|
||||||
follow: true,
|
follow: true,
|
||||||
}
|
}
|
||||||
|
|
@ -60,6 +58,10 @@ func NewLog(gvr client.GVR, opts *dao.LogOptions) *Log {
|
||||||
return &l
|
return &l
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func logChan() dao.LogChan {
|
||||||
|
return make(dao.LogChan, 2)
|
||||||
|
}
|
||||||
|
|
||||||
// Init initializes the viewer.
|
// Init initializes the viewer.
|
||||||
func (l *Log) Init(ctx context.Context) (err error) {
|
func (l *Log) Init(ctx context.Context) (err error) {
|
||||||
if l.app, err = extractApp(ctx); err != nil {
|
if l.app, err = extractApp(ctx); err != nil {
|
||||||
|
|
@ -91,7 +93,7 @@ func (l *Log) Init(ctx context.Context) (err error) {
|
||||||
l.bindKeys()
|
l.bindKeys()
|
||||||
|
|
||||||
l.StylesChanged(l.app.Styles)
|
l.StylesChanged(l.app.Styles)
|
||||||
l.goFullScreen()
|
l.toggleFullScreen()
|
||||||
|
|
||||||
l.model.Init(l.app.factory)
|
l.model.Init(l.app.factory)
|
||||||
l.updateTitle()
|
l.updateTitle()
|
||||||
|
|
@ -126,7 +128,6 @@ func (l *Log) LogResume() {
|
||||||
l.mx.Lock()
|
l.mx.Lock()
|
||||||
defer l.mx.Unlock()
|
defer l.mx.Unlock()
|
||||||
|
|
||||||
log.Debug().Msgf("LOG_RESUME!!!")
|
|
||||||
l.cancelUpdates = false
|
l.cancelUpdates = false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -196,18 +197,27 @@ func (l *Log) ExtraHints() map[string]string {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *Log) getContext() context.Context {
|
func (l *Log) cancel() {
|
||||||
|
l.mx.Lock()
|
||||||
|
defer l.mx.Unlock()
|
||||||
if l.cancelFn != nil {
|
if l.cancelFn != nil {
|
||||||
|
log.Debug().Msgf("!!! LOG-VIEWER CANCELED !!!")
|
||||||
l.cancelFn()
|
l.cancelFn()
|
||||||
|
l.cancelFn = nil
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *Log) getContext() context.Context {
|
||||||
|
l.cancel()
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
ctx, l.cancelFn = context.WithCancel(ctx)
|
ctx, l.cancelFn = context.WithCancel(ctx)
|
||||||
|
|
||||||
return ctx
|
return ctx
|
||||||
}
|
}
|
||||||
|
|
||||||
// Start runs the component.
|
// Start runs the component.
|
||||||
func (l *Log) Start() {
|
func (l *Log) Start() {
|
||||||
l.model.Restart(l.getContext(), l.logChan, true)
|
l.model.Start(l.getContext())
|
||||||
l.model.AddListener(l)
|
l.model.AddListener(l)
|
||||||
l.app.Styles.AddListener(l)
|
l.app.Styles.AddListener(l)
|
||||||
l.logs.cmdBuff.AddListener(l)
|
l.logs.cmdBuff.AddListener(l)
|
||||||
|
|
@ -219,14 +229,7 @@ func (l *Log) Start() {
|
||||||
func (l *Log) Stop() {
|
func (l *Log) Stop() {
|
||||||
l.model.RemoveListener(l)
|
l.model.RemoveListener(l)
|
||||||
l.model.Stop()
|
l.model.Stop()
|
||||||
l.mx.Lock()
|
l.cancel()
|
||||||
{
|
|
||||||
if l.cancelFn != nil {
|
|
||||||
l.cancelFn()
|
|
||||||
l.cancelFn = nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
l.mx.Unlock()
|
|
||||||
l.app.Styles.RemoveListener(l)
|
l.app.Styles.RemoveListener(l)
|
||||||
l.logs.cmdBuff.RemoveListener(l)
|
l.logs.cmdBuff.RemoveListener(l)
|
||||||
l.logs.cmdBuff.RemoveListener(l.app.Prompt())
|
l.logs.cmdBuff.RemoveListener(l.app.Prompt())
|
||||||
|
|
@ -238,7 +241,7 @@ func (l *Log) Name() string { return logTitle }
|
||||||
func (l *Log) bindKeys() {
|
func (l *Log) bindKeys() {
|
||||||
l.logs.Actions().Set(ui.KeyActions{
|
l.logs.Actions().Set(ui.KeyActions{
|
||||||
ui.Key0: ui.NewKeyAction("tail", l.sinceCmd(-1), true),
|
ui.Key0: ui.NewKeyAction("tail", l.sinceCmd(-1), true),
|
||||||
ui.Key1: ui.NewKeyAction("head", l.head(), true),
|
ui.Key1: ui.NewKeyAction("head", l.sinceCmd(0), true),
|
||||||
ui.Key2: ui.NewKeyAction("1m", l.sinceCmd(60), true),
|
ui.Key2: ui.NewKeyAction("1m", l.sinceCmd(60), true),
|
||||||
ui.Key3: ui.NewKeyAction("5m", l.sinceCmd(5*60), true),
|
ui.Key3: ui.NewKeyAction("5m", l.sinceCmd(5*60), true),
|
||||||
ui.Key4: ui.NewKeyAction("15m", l.sinceCmd(15*60), true),
|
ui.Key4: ui.NewKeyAction("15m", l.sinceCmd(15*60), true),
|
||||||
|
|
@ -355,24 +358,17 @@ func (l *Log) Flush(lines [][]byte) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
// Actions()...
|
// Actions...
|
||||||
|
|
||||||
func (l *Log) head() func(evt *tcell.EventKey) *tcell.EventKey {
|
func (l *Log) sinceCmd(n int) func(evt *tcell.EventKey) *tcell.EventKey {
|
||||||
return func(evt *tcell.EventKey) *tcell.EventKey {
|
|
||||||
log.Debug().Msgf("!!!!HEAD!!!!")
|
|
||||||
l.cancelUpdates = true
|
|
||||||
l.logs.Clear()
|
|
||||||
l.model.Head(l.getContext(), l.logChan)
|
|
||||||
l.updateTitle()
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (l *Log) sinceCmd(a int) func(evt *tcell.EventKey) *tcell.EventKey {
|
|
||||||
return func(evt *tcell.EventKey) *tcell.EventKey {
|
return func(evt *tcell.EventKey) *tcell.EventKey {
|
||||||
l.logs.Clear()
|
l.logs.Clear()
|
||||||
l.model.SetSinceSeconds(l.getContext(), l.logChan, int64(a))
|
ctx := l.getContext()
|
||||||
|
if n == 0 {
|
||||||
|
l.model.Head(ctx)
|
||||||
|
} else {
|
||||||
|
l.model.SetSinceSeconds(ctx, int64(n))
|
||||||
|
}
|
||||||
l.updateTitle()
|
l.updateTitle()
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
|
@ -384,7 +380,7 @@ func (l *Log) toggleAllContainers(evt *tcell.EventKey) *tcell.EventKey {
|
||||||
return evt
|
return evt
|
||||||
}
|
}
|
||||||
l.indicator.ToggleAllContainers()
|
l.indicator.ToggleAllContainers()
|
||||||
l.model.ToggleAllContainers(l.getContext(), l.logChan)
|
l.model.ToggleAllContainers(l.getContext())
|
||||||
l.updateTitle()
|
l.updateTitle()
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
|
@ -499,12 +495,6 @@ func (l *Log) toggleAutoScrollCmd(evt *tcell.EventKey) *tcell.EventKey {
|
||||||
|
|
||||||
l.indicator.ToggleAutoScroll()
|
l.indicator.ToggleAutoScroll()
|
||||||
l.follow = l.indicator.AutoScroll()
|
l.follow = l.indicator.AutoScroll()
|
||||||
// if l.indicator.AutoScroll() {
|
|
||||||
|
|
||||||
// // l.model.Restart(l.getContext(), l.logChan, false)
|
|
||||||
// } else {
|
|
||||||
// // l.model.Stop()
|
|
||||||
// }
|
|
||||||
l.indicator.Refresh()
|
l.indicator.Refresh()
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
|
@ -515,13 +505,13 @@ func (l *Log) toggleFullScreenCmd(evt *tcell.EventKey) *tcell.EventKey {
|
||||||
return evt
|
return evt
|
||||||
}
|
}
|
||||||
l.indicator.ToggleFullScreen()
|
l.indicator.ToggleFullScreen()
|
||||||
l.goFullScreen()
|
l.toggleFullScreen()
|
||||||
l.indicator.Refresh()
|
l.indicator.Refresh()
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *Log) goFullScreen() {
|
func (l *Log) toggleFullScreen() {
|
||||||
l.SetFullScreen(l.indicator.FullScreen())
|
l.SetFullScreen(l.indicator.FullScreen())
|
||||||
l.Box.SetBorder(!l.indicator.FullScreen())
|
l.Box.SetBorder(!l.indicator.FullScreen())
|
||||||
if l.indicator.FullScreen() {
|
if l.indicator.FullScreen() {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue