Skip to content

Commit

Permalink
Use dao secret
Browse files Browse the repository at this point in the history
  • Loading branch information
placintaalexandru committed Jan 15, 2024
1 parent b8c6421 commit 78b822e
Show file tree
Hide file tree
Showing 8 changed files with 158 additions and 43 deletions.
60 changes: 60 additions & 0 deletions internal/dao/secret.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
package dao

import (
"fmt"
"strings"

v1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/runtime"
)

// Secret represents a secret K8s resource.
type Secret struct {
Resource
decode bool
}

// Describe describes a secret that can be encoded or decoded.
func (s *Secret) Describe(path string) (string, error) {
encodedDescription, err := s.Resource.Describe(path)

if err != nil {
return "", err
}

if !s.decode {
return encodedDescription, nil
}

o, err := s.GetFactory().Get(s.GVR(), path, true, labels.Everything())

if err != nil {
return "", err
}

var secret v1.Secret
err = runtime.DefaultUnstructuredConverter.FromUnstructured(o.(*unstructured.Unstructured).Object, &secret)

dataEndIndex := strings.Index(encodedDescription, "====") + 4

if dataEndIndex == -1 {
return "", fmt.Errorf("unable to find data section in secret description")
}

// Remove the encoded part from k8s's describe API
// More details about the reasoning of index: https://github.com/kubernetes/kubectl/blob/v0.29.0/pkg/describe/describe.go#L2542
decodedDescription := string([]rune(encodedDescription)[0:dataEndIndex])

for k, v := range secret.Data {
decodedDescription = fmt.Sprintf("%s\n%s:\t%s", decodedDescription, k, string(v))
}

return decodedDescription, nil
}

// SetDecode sets the decode flag.
func (s *Secret) SetDecode(flag bool) {
s.decode = flag
}
10 changes: 10 additions & 0 deletions internal/model/describe.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ type Describe struct {
lines []string
refreshRate time.Duration
listeners []ResourceViewerListener
decode bool
}

// NewDescribe returns a new describe resource model.
Expand Down Expand Up @@ -180,6 +181,10 @@ func (d *Describe) describe(ctx context.Context, gvr client.GVR, path string) (s
return "", fmt.Errorf("no describer for %q", meta.DAO.GVR())
}

if desc, ok := meta.DAO.(*dao.Secret); ok {
desc.SetDecode(d.decode)
}

return desc.Describe(path)
}

Expand All @@ -202,3 +207,8 @@ func (d *Describe) RemoveListener(l ResourceViewerListener) {
d.listeners = append(d.listeners[:victim], d.listeners[victim+1:]...)
}
}

// Toggle toggles the decode flag.
func (d *Describe) Toggle() {
d.decode = !d.decode
}
4 changes: 4 additions & 0 deletions internal/model/registry.go
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,10 @@ var Registry = map[string]ResourceMeta{
DAO: &dao.Table{},
Renderer: &render.Event{},
},
"v1/secrets": {
DAO: &dao.Secret{},
Renderer: &render.Secret{},
},

// Apps...
"apps/v1/deployments": {
Expand Down
8 changes: 8 additions & 0 deletions internal/model/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,14 @@ type ResourceViewer interface {
RemoveListener(ResourceViewerListener)
}

// EncDecResourceViewer interface extends the ResourceViewer interface and
// adds a `Toggle` that allows the user to switch between encoded or decoded
// state of the view.
type EncDecResourceViewer interface {
ResourceViewer
Toggle()
}

// Igniter represents a runnable view.
type Igniter interface {
// Start starts a component.
Expand Down
58 changes: 58 additions & 0 deletions internal/render/secret.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright Authors of K9s

package render

import (
"fmt"
"strconv"

"github.com/derailed/k9s/internal/client"
"github.com/derailed/tview"
v1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime"
)

// Secret renders a K8s Secret to screen.
type Secret struct {
Base
}

// Header returns a header row.
func (Secret) Header(ns string) Header {
h := Header{
HeaderColumn{Name: "NAMESPACE"},
HeaderColumn{Name: "NAME"},
HeaderColumn{Name: "TYPE"},
HeaderColumn{Name: "DATA", Align: tview.AlignRight},
HeaderColumn{Name: "AGE", Time: true},
}

return h
}

// Render renders a K8s resource to screen.
func (s Secret) Render(o interface{}, ns string, r *Row) error {
raw, ok := o.(*unstructured.Unstructured)
if !ok {
return fmt.Errorf("expected Deployment, but got %T", o)
}

var secret v1.Secret
err := runtime.DefaultUnstructuredConverter.FromUnstructured(raw.Object, &secret)
if err != nil {
return err
}

r.ID = client.MetaFQN(secret.ObjectMeta)
r.Fields = Fields{
secret.Namespace,
secret.Name,
string(secret.Type),
strconv.Itoa(len(secret.Data)),
ToAge(secret.GetCreationTimestamp()),
}

return nil
}
17 changes: 17 additions & 0 deletions internal/view/live_view.go
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,23 @@ func (v *LiveView) bindKeys() {
ui.KeyM: ui.NewKeyAction("Toggle ManagedFields", v.toggleManagedCmd, true),
})
}
if v.model != nil && v.model.GVR().GVK().Kind == "secrets" {
v.actions.Add(ui.KeyActions{
ui.KeyT: ui.NewKeyAction("Toggle Encoded / Decoded", v.toggleEncodedDecoded, true),
})
}
}

func (v *LiveView) toggleEncodedDecoded(evt *tcell.EventKey) *tcell.EventKey {
m, ok := v.model.(model.EncDecResourceViewer)

if !ok {
return evt
}

m.Toggle()
v.Start()
return nil
}

func (v *LiveView) editCmd(evt *tcell.EventKey) *tcell.EventKey {
Expand Down
42 changes: 0 additions & 42 deletions internal/view/secret.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,6 @@
package view

import (
"fmt"
"strings"

"github.com/derailed/k9s/internal/client"
"github.com/derailed/k9s/internal/dao"
"github.com/derailed/k9s/internal/ui"
Expand Down Expand Up @@ -37,48 +34,9 @@ func (s *Secret) bindKeys(aa ui.KeyActions) {
aa.Add(ui.KeyActions{
ui.KeyX: ui.NewKeyAction("Decode", s.decodeCmd, true),
ui.KeyU: ui.NewKeyAction("UsedBy", s.refCmd, true),
ui.KeyD: ui.NewKeyAction("Describe", s.describe, false),
})
}

func (s *Secret) describe(evt *tcell.EventKey) *tcell.EventKey {
path := s.GetTable().GetSelectedItem()
if path == "" {
return evt
}

o, err := s.App().factory.Get(s.GVR().String(), path, true, labels.Everything())
if err != nil {
s.App().Flash().Err(err)
return nil
}

var secret v1.Secret
err = runtime.DefaultUnstructuredConverter.FromUnstructured(o.(*unstructured.Unstructured).Object, &secret)
if err != nil {
s.App().Flash().Err(err)
return nil
}

describe, err := dao.Describe(s.App().Conn(), s.GVR(), path)

// Remove the encoded part from k8s's describe API
// More details about the reasoning of index: https://github.com/kubernetes/kubectl/blob/v0.29.0/pkg/describe/describe.go#L2542
dataEndIndex := strings.Index(describe, "====") + 4
describe = string([]rune(describe)[0:dataEndIndex])

for k, v := range secret.Data {
describe = fmt.Sprintf("%s\n%s:\t%s", describe, k, string(v))
}

details := NewDetails(s.App(), "Secret Decoder", path, contentYAML, true).Update(describe)
if err := s.App().inject(details, false); err != nil {
s.App().Flash().Err(err)
}

return nil
}

func (s *Secret) refCmd(evt *tcell.EventKey) *tcell.EventKey {
return scanRefs(evt, s.App(), s.GetTable(), dao.SecGVR)
}
Expand Down
2 changes: 1 addition & 1 deletion internal/view/secret_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,5 +16,5 @@ func TestSecretNew(t *testing.T) {

assert.Nil(t, s.Init(makeCtx()))
assert.Equal(t, "Secrets", s.Name())
assert.Equal(t, 8, len(s.Hints()))
assert.Equal(t, 7, len(s.Hints()))
}

0 comments on commit 78b822e

Please sign in to comment.