parent
d0f4fa5b90
commit
abb0a39c0f
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.26.5
|
VERSION ?= v0.26.6
|
||||||
IMG_NAME := derailed/k9s
|
IMG_NAME := derailed/k9s
|
||||||
IMAGE := ${IMG_NAME}:${VERSION}
|
IMAGE := ${IMG_NAME}:${VERSION}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,32 @@
|
||||||
|
<img src="https://raw.githubusercontent.com/derailed/k9s/master/assets/k9s.png" align="center" width="800" height="auto"/>
|
||||||
|
|
||||||
|
# Release v0.26.6
|
||||||
|
|
||||||
|
## 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! Also big thanks to all that have allocated their own time to help others on both slack and on this repo!!
|
||||||
|
|
||||||
|
As you may know, K9s is not pimped out by corps with deep pockets, thus 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
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Resolved Issues
|
||||||
|
|
||||||
|
* [Issue #1773](https://github.com/derailed/k9s/issues/1773) CustomResourceDefinition does not display
|
||||||
|
|
||||||
|
## Contributed PRs (Thank you!!)
|
||||||
|
|
||||||
|
* [PR #1777](https://github.com/derailed/k9s/pull/1777) Fix directory path when viewing screendump
|
||||||
|
* [PR #1776](https://github.com/derailed/k9s/pull/1776) Add a closing tag when showing timestamp in log view
|
||||||
|
* [PR #1775](https://github.com/derailed/k9s/pull/1775) Log toggles: add a space after "on" in logs view
|
||||||
|
* [PR #1772](https://github.com/derailed/k9s/pull/1772) docs: update homebrew installation note
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
<img src="https://raw.githubusercontent.com/derailed/k9s/master/assets/imhotep_logo.png" width="32" height="auto"/> © 2022 Imhotep Software LLC. All materials licensed under [Apache v2.0](http://www.apache.org/licenses/LICENSE-2.0)
|
||||||
|
|
@ -4,6 +4,7 @@ import (
|
||||||
"os"
|
"os"
|
||||||
"os/user"
|
"os/user"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"regexp"
|
||||||
|
|
||||||
"github.com/rs/zerolog/log"
|
"github.com/rs/zerolog/log"
|
||||||
v1 "k8s.io/api/core/v1"
|
v1 "k8s.io/api/core/v1"
|
||||||
|
|
@ -16,6 +17,13 @@ const (
|
||||||
DefaultFileMod os.FileMode = 0600
|
DefaultFileMod os.FileMode = 0600
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var invalidPathCharsRX = regexp.MustCompile(`[:]+`)
|
||||||
|
|
||||||
|
// SanitizeFilename sanitizes the dump filename.
|
||||||
|
func SanitizeFilename(name string) string {
|
||||||
|
return invalidPathCharsRX.ReplaceAllString(name, "-")
|
||||||
|
}
|
||||||
|
|
||||||
// InList check if string is in a collection of strings.
|
// InList check if string is in a collection of strings.
|
||||||
func InList(ll []string, n string) bool {
|
func InList(ll []string, n string) bool {
|
||||||
for _, l := range ll {
|
for _, l := range ll {
|
||||||
|
|
|
||||||
|
|
@ -47,6 +47,10 @@ func NewK9s() *K9s {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (k *K9s) CurrentContextDir() string {
|
||||||
|
return SanitizeFilename(k.CurrentContext)
|
||||||
|
}
|
||||||
|
|
||||||
// ActivateCluster initializes the active cluster is not present.
|
// ActivateCluster initializes the active cluster is not present.
|
||||||
func (k *K9s) ActivateCluster(ns string) {
|
func (k *K9s) ActivateCluster(ns string) {
|
||||||
if _, ok := k.Clusters[k.CurrentCluster]; ok {
|
if _, ok := k.Clusters[k.CurrentCluster]; ok {
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ import (
|
||||||
"context"
|
"context"
|
||||||
|
|
||||||
"github.com/derailed/k9s/internal"
|
"github.com/derailed/k9s/internal"
|
||||||
|
v1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
|
||||||
"k8s.io/apimachinery/pkg/labels"
|
"k8s.io/apimachinery/pkg/labels"
|
||||||
"k8s.io/apimachinery/pkg/runtime"
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
)
|
)
|
||||||
|
|
@ -18,6 +19,19 @@ type CustomResourceDefinition struct {
|
||||||
Resource
|
Resource
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IsHappy check for happy deployments.
|
||||||
|
func (c *CustomResourceDefinition) IsHappy(crd v1.CustomResourceDefinition) bool {
|
||||||
|
versions := make([]string, 0, 3)
|
||||||
|
for _, v := range crd.Spec.Versions {
|
||||||
|
if v.Served && !v.Deprecated {
|
||||||
|
versions = append(versions, v.Name)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return len(versions) > 0
|
||||||
|
}
|
||||||
|
|
||||||
// List returns a collection of nodes.
|
// List returns a collection of nodes.
|
||||||
func (c *CustomResourceDefinition) List(ctx context.Context, _ string) ([]runtime.Object, error) {
|
func (c *CustomResourceDefinition) List(ctx context.Context, _ string) ([]runtime.Object, error) {
|
||||||
strLabel, ok := ctx.Value(internal.KeyLabels).(string)
|
strLabel, ok := ctx.Value(internal.KeyLabels).(string)
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,6 @@ import (
|
||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"os"
|
"os"
|
||||||
"regexp"
|
|
||||||
|
|
||||||
"github.com/derailed/k9s/internal"
|
"github.com/derailed/k9s/internal"
|
||||||
"github.com/derailed/k9s/internal/render"
|
"github.com/derailed/k9s/internal/render"
|
||||||
|
|
@ -15,9 +14,6 @@ import (
|
||||||
var (
|
var (
|
||||||
_ Accessor = (*ScreenDump)(nil)
|
_ Accessor = (*ScreenDump)(nil)
|
||||||
_ Nuker = (*ScreenDump)(nil)
|
_ Nuker = (*ScreenDump)(nil)
|
||||||
|
|
||||||
// InvalidCharsRX contains invalid filename characters.
|
|
||||||
invalidPathCharsRX = regexp.MustCompile(`[:]+`)
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// ScreenDump represents a scraped resources.
|
// ScreenDump represents a scraped resources.
|
||||||
|
|
@ -41,7 +37,6 @@ func (d *ScreenDump) List(ctx context.Context, _ string) ([]runtime.Object, erro
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
oo := make([]runtime.Object, len(ff))
|
oo := make([]runtime.Object, len(ff))
|
||||||
for i, f := range ff {
|
for i, f := range ff {
|
||||||
if fi, err := f.Info(); err == nil {
|
if fi, err := f.Info(); err == nil {
|
||||||
|
|
@ -51,10 +46,3 @@ func (d *ScreenDump) List(ctx context.Context, _ string) ([]runtime.Object, erro
|
||||||
|
|
||||||
return oo, nil
|
return oo, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Helpers...
|
|
||||||
|
|
||||||
// SanitizeFilename sanitizes the dump filename.
|
|
||||||
func SanitizeFilename(name string) string {
|
|
||||||
return invalidPathCharsRX.ReplaceAllString(name, "-")
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -122,7 +122,7 @@ func (h *PulseHealth) check(ctx context.Context, ns, gvr string) (*health.Check,
|
||||||
}
|
}
|
||||||
rows := make(render.Rows, len(table.Rows))
|
rows := make(render.Rows, len(table.Rows))
|
||||||
re, _ := meta.Renderer.(Generic)
|
re, _ := meta.Renderer.(Generic)
|
||||||
re.SetTable(table)
|
re.SetTable(ns, table)
|
||||||
for i, row := range table.Rows {
|
for i, row := range table.Rows {
|
||||||
if err := re.Render(row, ns, &rows[i]); err != nil {
|
if err := re.Render(row, ns, &rows[i]); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|
|
||||||
|
|
@ -306,10 +306,16 @@ func hydrate(ns string, oo []runtime.Object, rr render.Rows, re Renderer) error
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Generic represents a generic resource.
|
||||||
type Generic interface {
|
type Generic interface {
|
||||||
SetTable(*metav1beta1.Table)
|
// SetTable sets up the resource tabular definition.
|
||||||
Header(string) render.Header
|
SetTable(ns string, table *metav1beta1.Table)
|
||||||
Render(interface{}, string, *render.Row) error
|
|
||||||
|
// Header returns a resource header.
|
||||||
|
Header(ns string) render.Header
|
||||||
|
|
||||||
|
// Render renders the resource.
|
||||||
|
Render(o interface{}, ns string, row *render.Row) error
|
||||||
}
|
}
|
||||||
|
|
||||||
func genericHydrate(ns string, table *metav1beta1.Table, rr render.Rows, re Renderer) error {
|
func genericHydrate(ns string, table *metav1beta1.Table, rr render.Rows, re Renderer) error {
|
||||||
|
|
@ -317,7 +323,7 @@ func genericHydrate(ns string, table *metav1beta1.Table, rr render.Rows, re Rend
|
||||||
if !ok {
|
if !ok {
|
||||||
return fmt.Errorf("expecting generic renderer but got %T", re)
|
return fmt.Errorf("expecting generic renderer but got %T", re)
|
||||||
}
|
}
|
||||||
gr.SetTable(table)
|
gr.SetTable(ns, table)
|
||||||
for i, row := range table.Rows {
|
for i, row := range table.Rows {
|
||||||
if err := gr.Render(row, ns, &rr[i]); err != nil {
|
if err := gr.Render(row, ns, &rr[i]); err != nil {
|
||||||
return err
|
return err
|
||||||
|
|
|
||||||
|
|
@ -128,7 +128,7 @@ func TestTableGenericHydrate(t *testing.T) {
|
||||||
}
|
}
|
||||||
rr := make([]render.Row, 2)
|
rr := make([]render.Row, 2)
|
||||||
re := render.Generic{}
|
re := render.Generic{}
|
||||||
re.SetTable(&tt)
|
re.SetTable("blee", &tt)
|
||||||
|
|
||||||
assert.Nil(t, genericHydrate("blee", &tt, rr, &re))
|
assert.Nil(t, genericHydrate("blee", &tt, rr, &re))
|
||||||
assert.Equal(t, 2, len(rr))
|
assert.Equal(t, 2, len(rr))
|
||||||
|
|
|
||||||
|
|
@ -305,7 +305,7 @@ func genericTreeHydrate(ctx context.Context, ns string, table *metav1beta1.Table
|
||||||
return fmt.Errorf("expecting xray.Generic renderer but got %T", re)
|
return fmt.Errorf("expecting xray.Generic renderer but got %T", re)
|
||||||
}
|
}
|
||||||
|
|
||||||
tre.SetTable(table)
|
tre.SetTable(ns, table)
|
||||||
// BOZO!! Need table row sorter!!
|
// BOZO!! Need table row sorter!!
|
||||||
for _, row := range table.Rows {
|
for _, row := range table.Rows {
|
||||||
if err := tre.Render(ctx, ns, row); err != nil {
|
if err := tre.Render(ctx, ns, row); err != nil {
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,9 @@
|
||||||
package render
|
package render
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/derailed/k9s/internal/client"
|
"github.com/derailed/k9s/internal/client"
|
||||||
"github.com/rs/zerolog/log"
|
"github.com/rs/zerolog/log"
|
||||||
|
|
@ -19,13 +21,15 @@ type CustomResourceDefinition struct {
|
||||||
func (CustomResourceDefinition) Header(string) Header {
|
func (CustomResourceDefinition) Header(string) Header {
|
||||||
return Header{
|
return Header{
|
||||||
HeaderColumn{Name: "NAME"},
|
HeaderColumn{Name: "NAME"},
|
||||||
|
HeaderColumn{Name: "VERSIONS"},
|
||||||
HeaderColumn{Name: "LABELS", Wide: true},
|
HeaderColumn{Name: "LABELS", Wide: true},
|
||||||
|
HeaderColumn{Name: "VALID", Wide: true},
|
||||||
HeaderColumn{Name: "AGE", Time: true},
|
HeaderColumn{Name: "AGE", Time: true},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Render renders a K8s resource to screen.
|
// Render renders a K8s resource to screen.
|
||||||
func (CustomResourceDefinition) Render(o interface{}, ns string, r *Row) error {
|
func (c CustomResourceDefinition) Render(o interface{}, ns string, r *Row) error {
|
||||||
raw, ok := o.(*unstructured.Unstructured)
|
raw, ok := o.(*unstructured.Unstructured)
|
||||||
if !ok {
|
if !ok {
|
||||||
return fmt.Errorf("Expected CustomResourceDefinition, but got %T", o)
|
return fmt.Errorf("Expected CustomResourceDefinition, but got %T", o)
|
||||||
|
|
@ -37,27 +41,68 @@ func (CustomResourceDefinition) Render(o interface{}, ns string, r *Row) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
var version string
|
versions := make([]string, 0, 3)
|
||||||
for _, v := range crd.Spec.Versions {
|
for _, v := range crd.Spec.Versions {
|
||||||
if v.Served && !v.Deprecated {
|
if v.Served {
|
||||||
version = v.Name
|
n := v.Name
|
||||||
break
|
if v.Deprecated {
|
||||||
|
n += "!"
|
||||||
|
}
|
||||||
|
versions = append(versions, n)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if version == "" {
|
if len(versions) == 0 {
|
||||||
return fmt.Errorf("unable to assert resource version")
|
log.Warn().Msgf("unable to assert CRD versions for %s", crd.GetName())
|
||||||
}
|
}
|
||||||
|
|
||||||
r.ID = client.FQN(client.ClusterScope, crd.GetName())
|
r.ID = client.FQN(client.ClusterScope, crd.GetName())
|
||||||
r.Fields = Fields{
|
r.Fields = Fields{
|
||||||
crd.GetName(),
|
crd.GetName(),
|
||||||
|
naStrings(versions),
|
||||||
mapToIfc(crd.GetLabels()),
|
mapToIfc(crd.GetLabels()),
|
||||||
|
asStatus(c.diagnose(crd.GetName(), crd.Spec.Versions)),
|
||||||
toAge(crd.GetCreationTimestamp()),
|
toAge(crd.GetCreationTimestamp()),
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c CustomResourceDefinition) diagnose(n string, vv []v1.CustomResourceDefinitionVersion) error {
|
||||||
|
if len(vv) == 0 {
|
||||||
|
return fmt.Errorf("unable to assert CRD servers versions for %s", n)
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
ee []error
|
||||||
|
served bool
|
||||||
|
)
|
||||||
|
for _, v := range vv {
|
||||||
|
if v.Served {
|
||||||
|
served = true
|
||||||
|
}
|
||||||
|
if v.Deprecated {
|
||||||
|
if v.DeprecationWarning != nil {
|
||||||
|
ee = append(ee, fmt.Errorf("%s", *v.DeprecationWarning))
|
||||||
|
} else {
|
||||||
|
ee = append(ee, fmt.Errorf("%s[%s] is deprecated!", n, v.Name))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !served {
|
||||||
|
ee = append(ee, fmt.Errorf("CRD %s is no longer served by the api server", n))
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(ee) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
errs := make([]string, 0, len(ee))
|
||||||
|
for _, e := range ee {
|
||||||
|
errs = append(errs, e.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
return errors.New(strings.Join(errs, " - "))
|
||||||
|
}
|
||||||
|
|
||||||
func extractMetaField(m map[string]interface{}, field string) string {
|
func extractMetaField(m map[string]interface{}, field string) string {
|
||||||
f, ok := m[field]
|
f, ok := m[field]
|
||||||
if !ok {
|
if !ok {
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"github.com/rs/zerolog/log"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/derailed/k9s/internal/client"
|
"github.com/derailed/k9s/internal/client"
|
||||||
|
|
@ -16,6 +17,7 @@ const ageTableCol = "Age"
|
||||||
type Generic struct {
|
type Generic struct {
|
||||||
Base
|
Base
|
||||||
table *metav1beta1.Table
|
table *metav1beta1.Table
|
||||||
|
header Header
|
||||||
ageIndex int
|
ageIndex int
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -24,8 +26,9 @@ func (*Generic) IsGeneric() bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetTable sets the tabular resource.
|
// SetTable sets the tabular resource.
|
||||||
func (g *Generic) SetTable(t *metav1beta1.Table) {
|
func (g *Generic) SetTable(ns string, t *metav1beta1.Table) {
|
||||||
g.table = t
|
g.table = t
|
||||||
|
g.header = g.Header(ns)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ColorerFunc colors a resource row.
|
// ColorerFunc colors a resource row.
|
||||||
|
|
@ -35,6 +38,9 @@ func (*Generic) ColorerFunc() ColorerFunc {
|
||||||
|
|
||||||
// Header returns a header row.
|
// Header returns a header row.
|
||||||
func (g *Generic) Header(ns string) Header {
|
func (g *Generic) Header(ns string) Header {
|
||||||
|
if g.header != nil {
|
||||||
|
return g.header
|
||||||
|
}
|
||||||
if g.table == nil {
|
if g.table == nil {
|
||||||
return Header{}
|
return Header{}
|
||||||
}
|
}
|
||||||
|
|
@ -89,6 +95,8 @@ func (g *Generic) Render(o interface{}, ns string, r *Row) error {
|
||||||
}
|
}
|
||||||
if d, ok := duration.(string); ok {
|
if d, ok := duration.(string); ok {
|
||||||
r.Fields = append(r.Fields, d)
|
r.Fields = append(r.Fields, d)
|
||||||
|
} else {
|
||||||
|
log.Warn().Msgf("No Duration detected on age field")
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
|
|
||||||
|
|
@ -83,7 +83,7 @@ func TestGenericRender(t *testing.T) {
|
||||||
u := uu[k]
|
u := uu[k]
|
||||||
t.Run(k, func(t *testing.T) {
|
t.Run(k, func(t *testing.T) {
|
||||||
var r render.Row
|
var r render.Row
|
||||||
re.SetTable(u.table)
|
re.SetTable(u.ns, u.table)
|
||||||
|
|
||||||
assert.Equal(t, u.eHeader, re.Header(u.ns))
|
assert.Equal(t, u.eHeader, re.Header(u.ns))
|
||||||
assert.Nil(t, re.Render(u.table.Rows[0], u.ns, &r))
|
assert.Nil(t, re.Render(u.table.Rows[0], u.ns, &r))
|
||||||
|
|
|
||||||
|
|
@ -72,6 +72,7 @@ func Happy(ns string, h Header, r Row) bool {
|
||||||
if validCol < 0 {
|
if validCol < 0 {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
return strings.TrimSpace(r.Fields[validCol]) == ""
|
return strings.TrimSpace(r.Fields[validCol]) == ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -173,6 +174,13 @@ func missing(s string) string {
|
||||||
return check(s, MissingValue)
|
return check(s, MissingValue)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func naStrings(ss []string) string {
|
||||||
|
if len(ss) == 0 {
|
||||||
|
return NAValue
|
||||||
|
}
|
||||||
|
return strings.Join(ss, ",")
|
||||||
|
}
|
||||||
|
|
||||||
func na(s string) string {
|
func na(s string) string {
|
||||||
return check(s, NAValue)
|
return check(s, NAValue)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -65,7 +65,7 @@ func fileToSubject(path string) string {
|
||||||
}
|
}
|
||||||
|
|
||||||
func benchDir(cfg *config.Config) string {
|
func benchDir(cfg *config.Config) string {
|
||||||
return filepath.Join(perf.K9sBenchDir, cfg.K9s.CurrentCluster)
|
return filepath.Join(perf.K9sBenchDir, cfg.K9s.CurrentContextDir())
|
||||||
}
|
}
|
||||||
|
|
||||||
func readBenchFile(cfg *config.Config, n string) (string, error) {
|
func readBenchFile(cfg *config.Config, n string) (string, error) {
|
||||||
|
|
|
||||||
|
|
@ -282,7 +282,7 @@ func (d *Details) resetCmd(evt *tcell.EventKey) *tcell.EventKey {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *Details) saveCmd(evt *tcell.EventKey) *tcell.EventKey {
|
func (d *Details) saveCmd(evt *tcell.EventKey) *tcell.EventKey {
|
||||||
if path, err := saveYAML(d.app.Config.K9s.GetScreenDumpDir(), d.app.Config.K9s.CurrentCluster, d.title, d.text.GetText(true)); err != nil {
|
if path, err := saveYAML(d.app.Config.K9s.GetScreenDumpDir(), d.app.Config.K9s.CurrentContextDir(), d.title, d.text.GetText(true)); err != nil {
|
||||||
d.app.Flash().Err(err)
|
d.app.Flash().Err(err)
|
||||||
} else {
|
} else {
|
||||||
d.app.Flash().Infof("Log %s saved successfully!", path)
|
d.app.Flash().Infof("Log %s saved successfully!", path)
|
||||||
|
|
|
||||||
|
|
@ -331,7 +331,7 @@ func (v *LiveView) resetCmd(evt *tcell.EventKey) *tcell.EventKey {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *LiveView) saveCmd(evt *tcell.EventKey) *tcell.EventKey {
|
func (v *LiveView) saveCmd(evt *tcell.EventKey) *tcell.EventKey {
|
||||||
if path, err := saveYAML(v.app.Config.K9s.GetScreenDumpDir(), v.app.Config.K9s.CurrentCluster, v.title, v.text.GetText(true)); err != nil {
|
if path, err := saveYAML(v.app.Config.K9s.GetScreenDumpDir(), v.app.Config.K9s.CurrentContextDir(), v.title, v.text.GetText(true)); err != nil {
|
||||||
v.app.Flash().Err(err)
|
v.app.Flash().Err(err)
|
||||||
} else {
|
} else {
|
||||||
v.app.Flash().Infof("Log %s saved successfully!", path)
|
v.app.Flash().Infof("Log %s saved successfully!", path)
|
||||||
|
|
|
||||||
|
|
@ -395,7 +395,7 @@ func (l *Log) filterCmd(evt *tcell.EventKey) *tcell.EventKey {
|
||||||
|
|
||||||
// SaveCmd dumps the logs to file.
|
// SaveCmd dumps the logs to file.
|
||||||
func (l *Log) SaveCmd(*tcell.EventKey) *tcell.EventKey {
|
func (l *Log) SaveCmd(*tcell.EventKey) *tcell.EventKey {
|
||||||
path, err := saveData(l.app.Config.K9s.GetScreenDumpDir(), l.app.Config.K9s.CurrentContext, l.model.GetPath(), l.logs.GetText(true))
|
path, err := saveData(l.app.Config.K9s.GetScreenDumpDir(), l.app.Config.K9s.CurrentContextDir(), l.model.GetPath(), l.logs.GetText(true))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
l.app.Flash().Err(err)
|
l.app.Flash().Err(err)
|
||||||
return nil
|
return nil
|
||||||
|
|
@ -409,8 +409,8 @@ func ensureDir(dir string) error {
|
||||||
return os.MkdirAll(dir, 0744)
|
return os.MkdirAll(dir, 0744)
|
||||||
}
|
}
|
||||||
|
|
||||||
func saveData(screenDumpDir, cluster, fqn, data string) (string, error) {
|
func saveData(screenDumpDir, context, fqn, data string) (string, error) {
|
||||||
dir := filepath.Join(screenDumpDir, dao.SanitizeFilename(cluster))
|
dir := filepath.Join(screenDumpDir, context)
|
||||||
if err := ensureDir(dir); err != nil {
|
if err := ensureDir(dir); err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -151,7 +151,7 @@ func (l *Logger) resetCmd(evt *tcell.EventKey) *tcell.EventKey {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *Logger) saveCmd(evt *tcell.EventKey) *tcell.EventKey {
|
func (l *Logger) saveCmd(evt *tcell.EventKey) *tcell.EventKey {
|
||||||
if path, err := saveYAML(l.app.Config.K9s.GetScreenDumpDir(), l.app.Config.K9s.CurrentCluster, l.title, l.GetText(true)); err != nil {
|
if path, err := saveYAML(l.app.Config.K9s.GetScreenDumpDir(), l.app.Config.K9s.CurrentContextDir(), l.title, l.GetText(true)); err != nil {
|
||||||
l.app.Flash().Err(err)
|
l.app.Flash().Err(err)
|
||||||
} else {
|
} else {
|
||||||
l.app.Flash().Infof("Log %s saved successfully!", path)
|
l.app.Flash().Infof("Log %s saved successfully!", path)
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,6 @@ import (
|
||||||
"github.com/derailed/k9s/internal"
|
"github.com/derailed/k9s/internal"
|
||||||
"github.com/derailed/k9s/internal/client"
|
"github.com/derailed/k9s/internal/client"
|
||||||
"github.com/derailed/k9s/internal/config"
|
"github.com/derailed/k9s/internal/config"
|
||||||
"github.com/derailed/k9s/internal/dao"
|
|
||||||
"github.com/derailed/k9s/internal/ui"
|
"github.com/derailed/k9s/internal/ui"
|
||||||
"github.com/gdamore/tcell/v2"
|
"github.com/gdamore/tcell/v2"
|
||||||
"github.com/rs/zerolog/log"
|
"github.com/rs/zerolog/log"
|
||||||
|
|
@ -35,7 +34,7 @@ func NewScreenDump(gvr client.GVR) ResourceViewer {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *ScreenDump) dirContext(ctx context.Context) context.Context {
|
func (s *ScreenDump) dirContext(ctx context.Context) context.Context {
|
||||||
dir := filepath.Join(s.App().Config.K9s.GetScreenDumpDir(), dao.SanitizeFilename(s.App().Config.K9s.CurrentContext))
|
dir := filepath.Join(s.App().Config.K9s.GetScreenDumpDir(), s.App().Config.K9s.CurrentContextDir())
|
||||||
log.Debug().Msgf("SD-DIR %q", dir)
|
log.Debug().Msgf("SD-DIR %q", dir)
|
||||||
config.EnsureFullPath(dir, config.DefaultDirMod)
|
config.EnsureFullPath(dir, config.DefaultDirMod)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -167,7 +167,7 @@ func (t *Table) BufferActive(state bool, k model.BufferKind) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *Table) saveCmd(evt *tcell.EventKey) *tcell.EventKey {
|
func (t *Table) saveCmd(evt *tcell.EventKey) *tcell.EventKey {
|
||||||
if path, err := saveTable(t.app.Config.K9s.GetScreenDumpDir(), t.app.Config.K9s.CurrentContext, t.GVR().R(), t.Path, t.GetFilteredData()); err != nil {
|
if path, err := saveTable(t.app.Config.K9s.GetScreenDumpDir(), t.app.Config.K9s.CurrentContextDir(), t.GVR().R(), t.Path, t.GetFilteredData()); err != nil {
|
||||||
t.app.Flash().Err(err)
|
t.app.Flash().Err(err)
|
||||||
} else {
|
} else {
|
||||||
t.app.Flash().Infof("File %s saved successfully!", path)
|
t.app.Flash().Infof("File %s saved successfully!", path)
|
||||||
|
|
|
||||||
|
|
@ -9,21 +9,21 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/derailed/k9s/internal/client"
|
"github.com/derailed/k9s/internal/client"
|
||||||
"github.com/derailed/k9s/internal/dao"
|
"github.com/derailed/k9s/internal/config"
|
||||||
"github.com/derailed/k9s/internal/render"
|
"github.com/derailed/k9s/internal/render"
|
||||||
"github.com/derailed/k9s/internal/ui"
|
"github.com/derailed/k9s/internal/ui"
|
||||||
"github.com/rs/zerolog/log"
|
"github.com/rs/zerolog/log"
|
||||||
)
|
)
|
||||||
|
|
||||||
func computeFilename(screenDumpDir, cluster, ns, title, path string) (string, error) {
|
func computeFilename(screenDumpDir, context, ns, title, path string) (string, error) {
|
||||||
now := time.Now().UnixNano()
|
now := time.Now().UnixNano()
|
||||||
|
|
||||||
dir := filepath.Join(screenDumpDir, dao.SanitizeFilename(cluster))
|
dir := filepath.Join(screenDumpDir, context)
|
||||||
if err := ensureDir(dir); err != nil {
|
if err := ensureDir(dir); err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
name := title + "-" + dao.SanitizeFilename(path)
|
name := title + "-" + config.SanitizeFilename(path)
|
||||||
if path == "" {
|
if path == "" {
|
||||||
name = title
|
name = title
|
||||||
}
|
}
|
||||||
|
|
@ -38,13 +38,13 @@ func computeFilename(screenDumpDir, cluster, ns, title, path string) (string, er
|
||||||
return strings.ToLower(filepath.Join(dir, fName)), nil
|
return strings.ToLower(filepath.Join(dir, fName)), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func saveTable(screenDumpDir, cluster, title, path string, data *render.TableData) (string, error) {
|
func saveTable(screenDumpDir, context, title, path string, data *render.TableData) (string, error) {
|
||||||
ns := data.Namespace
|
ns := data.Namespace
|
||||||
if client.IsClusterWide(ns) {
|
if client.IsClusterWide(ns) {
|
||||||
ns = client.NamespaceAll
|
ns = client.NamespaceAll
|
||||||
}
|
}
|
||||||
|
|
||||||
fPath, err := computeFilename(screenDumpDir, cluster, ns, title, path)
|
fPath, err := computeFilename(screenDumpDir, context, ns, title, path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,6 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/derailed/k9s/internal/config"
|
"github.com/derailed/k9s/internal/config"
|
||||||
"github.com/derailed/k9s/internal/dao"
|
|
||||||
"github.com/derailed/tview"
|
"github.com/derailed/tview"
|
||||||
"github.com/rs/zerolog/log"
|
"github.com/rs/zerolog/log"
|
||||||
)
|
)
|
||||||
|
|
@ -61,14 +60,14 @@ func enableRegion(str string) string {
|
||||||
return strings.ReplaceAll(strings.ReplaceAll(str, "<<<", "["), ">>>", "]")
|
return strings.ReplaceAll(strings.ReplaceAll(str, "<<<", "["), ">>>", "]")
|
||||||
}
|
}
|
||||||
|
|
||||||
func saveYAML(screenDumpDir, cluster, name, data string) (string, error) {
|
func saveYAML(screenDumpDir, context, name, data string) (string, error) {
|
||||||
dir := filepath.Join(screenDumpDir, dao.SanitizeFilename(cluster))
|
dir := filepath.Join(screenDumpDir, context)
|
||||||
if err := ensureDir(dir); err != nil {
|
if err := ensureDir(dir); err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
now := time.Now().UnixNano()
|
now := time.Now().UnixNano()
|
||||||
fName := fmt.Sprintf("%s-%d.yml", dao.SanitizeFilename(name), now)
|
fName := fmt.Sprintf("%s-%d.yml", config.SanitizeFilename(name), now)
|
||||||
|
|
||||||
path := filepath.Join(dir, fName)
|
path := filepath.Join(dir, fName)
|
||||||
mod := os.O_CREATE | os.O_WRONLY
|
mod := os.O_CREATE | os.O_WRONLY
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,7 @@ type Generic struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetTable sets the tabular resource.
|
// SetTable sets the tabular resource.
|
||||||
func (g *Generic) SetTable(t *metav1beta1.Table) {
|
func (g *Generic) SetTable(_ string, t *metav1beta1.Table) {
|
||||||
g.table = t
|
g.table = t
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue