fix #1773
parent
c35949189f
commit
39a55231fa
|
|
@ -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.
|
||||||
|
|
@ -37,11 +33,10 @@ func (d *ScreenDump) List(ctx context.Context, _ string) ([]runtime.Object, erro
|
||||||
return nil, errors.New("no screendump dir found in context")
|
return nil, errors.New("no screendump dir found in context")
|
||||||
}
|
}
|
||||||
|
|
||||||
ff, err := os.ReadDir(SanitizeFilename(dir))
|
ff, err := os.ReadDir(dir)
|
||||||
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)
|
||||||
|
|
|
||||||
|
|
@ -34,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(), s.App().Config.K9s.CurrentCluster)
|
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.CurrentCluster, 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