perf + cleanup

mine
derailed 2020-01-08 15:43:48 -07:00
parent ce9e5ee5cb
commit 8af75df9f8
22 changed files with 156 additions and 79 deletions

View File

@ -77,45 +77,42 @@ func makeSAR(ns, gvr string) *authorizationv1.SelfSubjectAccessReview {
}
}
func makeKey(ns, gvr string, vv []string) string {
func makeCacheKey(ns, gvr string, vv []string) string {
return ns + ":" + gvr + "::" + strings.Join(vv, ",")
}
// CanI checks if user has access to a certain resource.
func (a *APIClient) CanI(ns, gvr string, verbs []string) (bool, error) {
defer func(t time.Time) {
log.Debug().Msgf("AUTH elapsed %v", time.Since(t))
func (a *APIClient) CanI(ns, gvr string, verbs []string) (auth bool, err error) {
key := makeCacheKey(ns, gvr, verbs)
defer func(t time.Time) string {
log.Debug().Msgf("AUTH elapsed %t--%q %v", auth, key, time.Since(t))
return "s"
}(time.Now())
log.Debug().Msgf("AUTH %q:%q -- %v", ns, gvr, verbs)
sar := makeSAR(ns, gvr)
key := makeKey(ns, gvr, verbs)
if v, ok := a.cache.Get(key); ok {
if auth, ok := v.(bool); ok {
if auth, ok = v.(bool); ok {
return auth, nil
}
}
dial := a.DialOrDie().AuthorizationV1().SelfSubjectAccessReviews()
dial, sar := a.DialOrDie().AuthorizationV1().SelfSubjectAccessReviews(), makeSAR(ns, gvr)
for _, v := range verbs {
sar.Spec.ResourceAttributes.Verb = v
resp, err := dial.Create(sar)
if err != nil {
log.Warn().Err(err).Msgf(" Dial Failed!")
a.cache.Add(key, false, cacheExpiry)
return false, err
return auth, err
}
if !resp.Status.Allowed {
log.Debug().Msgf(" NO %q ;(", v)
a.cache.Add(key, false, cacheExpiry)
return false, fmt.Errorf("`%s access denied for user on %q:%s", v, ns, gvr)
return auth, fmt.Errorf("`%s access denied for user on %q:%s", v, ns, gvr)
}
}
log.Debug().Msgf(" YES!")
auth = true
a.cache.Add(key, true, cacheExpiry)
return true, nil
return
}
// CurrentNamespaceName return namespace name set via either cli arg or cluster config.

View File

@ -76,7 +76,7 @@ func (m *MetricsServer) FetchNodesMetrics() (*mv1beta1.NodeMetricsList, error) {
return &mx, fmt.Errorf("No metrics-server detected on cluster")
}
auth, err := m.CanI("", "metrics.k8s.io/v1beta1/nodes", []string{"list"})
auth, err := m.CanI("", "metrics.k8s.io/v1beta1/nodes", ListAccess)
if !auth || err != nil {
return &mx, err
}
@ -98,7 +98,7 @@ func (m *MetricsServer) FetchPodsMetrics(ns string) (*mv1beta1.PodMetricsList, e
ns = AllNamespaces
}
auth, err := m.CanI(ns, "metrics.k8s.io/v1beta1/pods", []string{"list"})
auth, err := m.CanI(ns, "metrics.k8s.io/v1beta1/pods", ListAccess)
if !auth || err != nil {
return &mx, err
}
@ -121,7 +121,7 @@ func (m *MetricsServer) FetchPodMetrics(ns, sel string) (*mv1beta1.PodMetrics, e
if ns == NamespaceAll {
ns = AllNamespaces
}
auth, err := m.CanI(ns, "metrics.k8s.io/v1beta1/pods", []string{"get"})
auth, err := m.CanI(ns, "metrics.k8s.io/v1beta1/pods", GetAccess)
if !auth || err != nil {
return &mx, err
}

View File

@ -24,6 +24,34 @@ const (
ClusterScope = "-"
)
const (
// CreateVerb represents create access on a resource.
CreateVerb = "create"
// UpdateVerb represents an update access on a resource.
UpdateVerb = "update"
// UpdateVerb represents a patch access on a resource.
PatchVerb = "patch"
// DeleteVerb represents a delete access on a resource.
DeleteVerb = "delete"
// GetVerb represents a get access on a resource.
GetVerb = "get"
// ListVerb represents a list access on a resource.
ListVerb = "list"
// WatchVerb represents a watch access on a resource.
WatchVerb = "watch"
)
var (
// GetAccess reads a resource.
GetAccess = []string{GetVerb}
// ListAccess list resources.
ListAccess = []string{ListVerb}
// MonitorAcces monitors a collection of resources.
MonitorAccess = []string{ListVerb, WatchVerb}
// ReadAllAccess represents an all read access to a resource.
ReadAllAccess = []string{GetVerb, ListVerb, WatchVerb}
)
// Connection represents a Kubenetes apiserver connection.
// BOZO!! Refactor!
type Connection interface {

View File

@ -4,6 +4,7 @@ import (
"context"
"errors"
"fmt"
"time"
"github.com/derailed/k9s/internal"
"github.com/derailed/k9s/internal/client"
@ -29,32 +30,29 @@ type Container struct {
// List returns a collection of containers.
func (c *Container) List(ctx context.Context, _ string) ([]runtime.Object, error) {
path, ok := ctx.Value(internal.KeyPath).(string)
fqn, ok := ctx.Value(internal.KeyPath).(string)
if !ok {
return nil, fmt.Errorf("no context path for %q", c.gvr)
}
ns, _ := render.Namespaced(path)
o, err := c.Factory.Get("v1/pods", path, true, labels.Everything())
po, err := c.fetchPod(fqn)
if err != nil {
return nil, err
}
var po v1.Pod
err = runtime.DefaultUnstructuredConverter.FromUnstructured(o.(*unstructured.Unstructured).Object, &po)
if err != nil {
return nil, err
}
res := make([]runtime.Object, 0, len(po.Spec.InitContainers)+len(po.Spec.Containers))
mx := client.NewMetricsServer(c.Client())
ns, _ := render.Namespaced(fqn)
var pmx *mv1beta1.PodMetrics
if c.Client() != nil {
var err error
pmx, err = mx.FetchPodMetrics(ns, po.Name)
if err != nil {
log.Warn().Err(err).Msgf("No metrics found for pod %q:%q", ns, po.Name)
if c.Client().HasMetrics() {
mx := client.NewMetricsServer(c.Client())
if c.Client() != nil {
var err error
pmx, err = mx.FetchPodMetrics(ns, po.Name)
if err != nil {
log.Warn().Err(err).Msgf("No metrics found for pod %q:%q", ns, po.Name)
}
}
}
res := make([]runtime.Object, 0, len(po.Spec.InitContainers)+len(po.Spec.Containers))
for _, co := range po.Spec.InitContainers {
res = append(res, makeContainerRes(co, po, pmx, true))
}
@ -87,7 +85,7 @@ func (c *Container) TailLogs(ctx context.Context, logChan chan<- string, opts Lo
// Logs fetch container logs for a given pod and container.
func (c *Container) Logs(path string, opts *v1.PodLogOptions) (*restclient.Request, error) {
ns, _ := client.Namespaced(path)
auth, err := c.Client().CanI(ns, "v1/pods:log", []string{"get"})
auth, err := c.Client().CanI(ns, "v1/pods:log", client.GetAccess)
if !auth || err != nil {
return nil, err
}
@ -99,14 +97,18 @@ func (c *Container) Logs(path string, opts *v1.PodLogOptions) (*restclient.Reque
// ----------------------------------------------------------------------------
// Helpers...
func makeContainerRes(co v1.Container, po v1.Pod, pmx *mv1beta1.PodMetrics, isInit bool) render.ContainerRes {
func makeContainerRes(co v1.Container, po *v1.Pod, pmx *mv1beta1.PodMetrics, isInit bool) render.ContainerRes {
defer func(t time.Time) {
log.Debug().Msgf("MAKE-CO %s -- %v", co.Name, time.Since(t))
}(time.Now())
cmx, err := containerMetrics(co.Name, pmx)
if err != nil {
log.Warn().Err(err).Msgf("Container metrics for %s", co.Name)
log.Warn().Err(err).Msgf("Fail container metrics for %s", co.Name)
}
return render.ContainerRes{
Container: co,
Container: &co,
Status: getContainerStatus(co.Name, po.Status),
Metrics: cmx,
IsInit: isInit,
@ -114,11 +116,7 @@ func makeContainerRes(co v1.Container, po v1.Pod, pmx *mv1beta1.PodMetrics, isIn
}
}
func containerMetrics(n string, mx runtime.Object) (*mv1beta1.ContainerMetrics, error) {
pmx, ok := mx.(*mv1beta1.PodMetrics)
if !ok {
return nil, fmt.Errorf("expecting podmetrics but got `%T", mx)
}
func containerMetrics(n string, pmx *mv1beta1.PodMetrics) (*mv1beta1.ContainerMetrics, error) {
if pmx == nil {
return nil, fmt.Errorf("no metrics for container %s", n)
}
@ -136,7 +134,6 @@ func getContainerStatus(co string, status v1.PodStatus) *v1.ContainerStatus {
return &c
}
}
for _, c := range status.InitContainerStatuses {
if c.Name == co {
return &c
@ -145,3 +142,17 @@ func getContainerStatus(co string, status v1.PodStatus) *v1.ContainerStatus {
return nil
}
func (c *Container) fetchPod(fqn string) (*v1.Pod, error) {
o, err := c.Factory.Get("v1/pods", fqn, true, labels.Everything())
if err != nil {
return nil, err
}
var po v1.Pod
err = runtime.DefaultUnstructuredConverter.FromUnstructured(o.(*unstructured.Unstructured).Object, &po)
if err != nil {
return nil, err
}
return &po, nil
}

View File

@ -10,10 +10,17 @@ import (
"github.com/derailed/k9s/internal/watch"
"github.com/ghodss/yaml"
"github.com/stretchr/testify/assert"
v1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/version"
"k8s.io/client-go/discovery/cached/disk"
"k8s.io/client-go/dynamic"
"k8s.io/client-go/informers"
"k8s.io/client-go/kubernetes"
restclient "k8s.io/client-go/rest"
versioned "k8s.io/metrics/pkg/client/clientset/versioned"
)
func TestContainerList(t *testing.T) {
@ -29,12 +36,36 @@ func TestContainerList(t *testing.T) {
// ----------------------------------------------------------------------------
// Helpers...
type conn struct{}
func makeConn() *conn {
return &conn{}
}
func (c *conn) Config() *client.Config { return nil }
func (c *conn) DialOrDie() kubernetes.Interface { return nil }
func (c *conn) SwitchContextOrDie(ctx string) {}
func (c *conn) CachedDiscoveryOrDie() *disk.CachedDiscoveryClient { return nil }
func (c *conn) RestConfigOrDie() *restclient.Config { return nil }
func (c *conn) MXDial() (*versioned.Clientset, error) { return nil, nil }
func (c *conn) DynDialOrDie() dynamic.Interface { return nil }
func (c *conn) HasMetrics() bool { return false }
func (c *conn) IsNamespaced(n string) bool { return false }
func (c *conn) SupportsResource(group string) bool { return false }
func (c *conn) ValidNamespaces() ([]v1.Namespace, error) { return nil, nil }
func (c *conn) SupportsRes(grp string, versions []string) (string, bool, error) {
return "", false, nil
}
func (c *conn) ServerVersion() (*version.Info, error) { return nil, nil }
func (c *conn) CurrentNamespaceName() (string, error) { return "", nil }
func (c *conn) CanI(ns, gvr string, verbs []string) (bool, error) { return true, nil }
type podFactory struct{}
var _ dao.Factory = testFactory{}
func (f podFactory) Client() client.Connection {
return nil
return makeConn()
}
func (f podFactory) Get(gvr, path string, wait bool, sel labels.Selector) (runtime.Object, error) {
var m map[string]interface{}

View File

@ -22,7 +22,7 @@ type CronJob struct {
// Run a CronJob.
func (c *CronJob) Run(path string) error {
ns, n := client.Namespaced(path)
auth, err := c.Client().CanI(ns, "batch/v1beta1/cronjobs", []string{"get", "create"})
auth, err := c.Client().CanI(ns, "batch/v1beta1/cronjobs", []string{client.GetVerb, client.CreateVerb})
if !auth || err != nil {
return err
}

View File

@ -31,7 +31,7 @@ type Deployment struct {
// Scale a Deployment.
func (d *Deployment) Scale(path string, replicas int32) error {
ns, n := client.Namespaced(path)
auth, err := d.Client().CanI(ns, "apps/v1/deployments:scale", []string{"get", "update"})
auth, err := d.Client().CanI(ns, "apps/v1/deployments:scale", []string{client.GetVerb, client.UpdateVerb})
if !auth || err != nil {
return err
}
@ -59,7 +59,7 @@ func (d *Deployment) Restart(path string) error {
}
ns, _ := client.Namespaced(path)
auth, err := d.Client().CanI(ns, "apps/v1/deployments", []string{"patch"})
auth, err := d.Client().CanI(ns, "apps/v1/deployments", []string{client.PatchVerb})
if !auth || err != nil {
return err
}

View File

@ -44,7 +44,7 @@ func (d *DaemonSet) Restart(path string) error {
return err
}
auth, err := d.Client().CanI(ds.Namespace, "apps/v1/daemonsets", []string{"patch"})
auth, err := d.Client().CanI(ds.Namespace, "apps/v1/daemonsets", []string{client.PatchVerb})
if !auth || err != nil {
return err
}

View File

@ -29,7 +29,6 @@ func (g *Generic) List(ctx context.Context, ns string) ([]runtime.Object, error)
if !ok {
log.Warn().Msgf("No label selector found in context. Listing all resources")
}
if client.IsAllNamespace(ns) {
ns = client.AllNamespaces
}
@ -90,7 +89,7 @@ func (g *Generic) ToYAML(path string) (string, error) {
// Delete deletes a resource.
func (g *Generic) Delete(path string, cascade, force bool) error {
ns, n := client.Namespaced(path)
auth, err := g.Client().CanI(ns, g.gvr.String(), []string{"delete"})
auth, err := g.Client().CanI(ns, g.gvr.String(), []string{client.DeleteVerb})
if !auth || err != nil {
return err
}

View File

@ -4,6 +4,7 @@ import (
"context"
"github.com/derailed/k9s/internal"
"github.com/derailed/k9s/internal/client"
"github.com/derailed/k9s/internal/render"
"github.com/rs/zerolog/log"
v1 "k8s.io/api/core/v1"
@ -59,7 +60,7 @@ func (n *Node) List(ctx context.Context, ns string) ([]runtime.Object, error) {
// FetchNodes retrieves all nodes.
func FetchNodes(f Factory) (*v1.NodeList, error) {
auth, err := f.Client().CanI("", "v1/nodes", []string{"list"})
auth, err := f.Client().CanI("", "v1/nodes", []string{client.ListVerb})
if !auth || err != nil {
return nil, err
}

View File

@ -85,7 +85,7 @@ func (p *Pod) List(ctx context.Context, ns string) ([]runtime.Object, error) {
// Logs fetch container logs for a given pod and container.
func (p *Pod) Logs(path string, opts *v1.PodLogOptions) (*restclient.Request, error) {
ns, _ := client.Namespaced(path)
auth, err := p.Client().CanI(ns, "v1/pods:log", []string{"get"})
auth, err := p.Client().CanI(ns, "v1/pods:log", []string{client.GetVerb})
if !auth || err != nil {
return nil, err
}

View File

@ -25,7 +25,7 @@ type PortForward struct {
// Delete a portforward.
func (p *PortForward) Delete(path string, cascade, force bool) error {
ns, _ := client.Namespaced(path)
auth, err := p.Client().CanI(ns, "v1/pods:portforward", []string{"delete"})
auth, err := p.Client().CanI(ns, "v1/pods:portforward", []string{client.DeleteVerb})
if !auth || err != nil {
return err
}

View File

@ -92,7 +92,7 @@ func (p *PortForwarder) Start(path, co, address string, ports []string) (*portfo
p.path, p.container, p.ports, p.age = path, co, ports, time.Now()
ns, n := client.Namespaced(path)
auth, err := p.CanI(ns, "v1/pods", []string{"get"})
auth, err := p.CanI(ns, "v1/pods", []string{client.GetVerb})
if !auth || err != nil {
return nil, err
}
@ -104,7 +104,7 @@ func (p *PortForwarder) Start(path, co, address string, ports []string) (*portfo
return nil, fmt.Errorf("unable to forward port because pod is not running. Current status=%v", pod.Status.Phase)
}
auth, err = p.CanI(ns, "v1/pods:portforward", []string{"update"})
auth, err = p.CanI(ns, "v1/pods:portforward", []string{client.UpdateVerb})
if !auth || err != nil {
return nil, err
}

View File

@ -31,7 +31,7 @@ type StatefulSet struct {
// Scale a StatefulSet.
func (s *StatefulSet) Scale(path string, replicas int32) error {
ns, n := client.Namespaced(path)
auth, err := s.Client().CanI(ns, "apps/v1/statefulsets:scale", []string{"get", "update"})
auth, err := s.Client().CanI(ns, "apps/v1/statefulsets:scale", []string{client.GetVerb, client.UpdateVerb})
if !auth || err != nil {
return err
}
@ -58,7 +58,7 @@ func (s *StatefulSet) Restart(path string) error {
}
ns, _ := client.Namespaced(path)
auth, err := s.Client().CanI(ns, "apps/v1/statefulsets", []string{"patch"})
auth, err := s.Client().CanI(ns, "apps/v1/statefulsets", []string{client.PatchVerb})
if !auth || err != nil {
return err
}

View File

@ -27,7 +27,6 @@ func (t *Table) List(ctx context.Context, ns string) ([]runtime.Object, error) {
if err != nil {
return nil, err
}
o, err := c.Get().
SetHeader("Accept", a).
Namespace(ns).

View File

@ -15,6 +15,8 @@ import (
"k8s.io/apimachinery/pkg/runtime"
)
const iniRefreshRate = 300 * time.Millisecond
// TableListener represents a table model listener.
type TableListener interface {
// TableDataChanged notifies the model data changed.
@ -167,11 +169,14 @@ func (t *Table) Peek() render.TableData {
func (t *Table) updater(ctx context.Context) {
defer log.Debug().Msgf("Model canceled -- %q", t.gvr)
rate := iniRefreshRate
for {
select {
case <-ctx.Done():
return
case <-time.After(t.refreshRate):
case <-time.After(rate):
rate = t.refreshRate
t.refresh(ctx)
}
}

View File

@ -132,7 +132,7 @@ func gatherMetrics(co ContainerRes) (c, p metric) {
mem: ToMi(mem),
}
rcpu, rmem := containerResources(co.Container)
rcpu, rmem := containerResources(*co.Container)
if rcpu != nil {
p.cpu = AsPerc(toPerc(float64(cpu), float64(rcpu.MilliValue())))
}
@ -187,7 +187,7 @@ func probe(p *v1.Probe) string {
// ContainerRes represents a container and its metrics.
type ContainerRes struct {
Container v1.Container
Container *v1.Container
Status *v1.ContainerStatus
Metrics *mv1beta1.ContainerMetrics
IsInit bool

View File

@ -67,8 +67,8 @@ func makeAge() metav1.Time {
return metav1.Time{Time: testTime()}
}
func makeContainer() v1.Container {
return v1.Container{
func makeContainer() *v1.Container {
return &v1.Container{
Name: "fred",
Image: "img",
Resources: v1.ResourceRequirements{

View File

@ -14,7 +14,6 @@ import (
"github.com/derailed/k9s/internal/render"
"github.com/derailed/k9s/internal/ui"
"github.com/derailed/k9s/internal/ui/dialog"
"github.com/derailed/k9s/internal/watch"
"github.com/gdamore/tcell"
"github.com/rs/zerolog/log"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@ -50,8 +49,9 @@ func (b *Browser) Init(ctx context.Context) error {
if err = b.Table.Init(ctx); err != nil {
return err
}
ns := b.app.Config.ActiveNamespace()
if dao.IsK8sMeta(b.meta) {
if _, e := b.app.factory.CanForResource(b.app.Config.ActiveNamespace(), b.GVR(), []string{"list", "watch"}); e != nil {
if _, e := b.app.factory.CanForResource(ns, b.GVR(), client.MonitorAccess); e != nil {
return e
}
}
@ -65,7 +65,7 @@ func (b *Browser) Init(ctx context.Context) error {
return err
}
b.setNamespace(b.App().Config.ActiveNamespace())
b.setNamespace(ns)
row, _ := b.GetSelection()
if row == 0 && b.GetRowCount() > 0 {
b.Select(1, 0)
@ -296,7 +296,7 @@ func (b *Browser) switchNamespaceCmd(evt *tcell.EventKey) *tcell.EventKey {
ns = client.NamespaceAll
}
auth, err := b.App().factory.Client().CanI(ns, b.GVR(), watch.ReadVerbs)
auth, err := b.App().factory.Client().CanI(ns, b.GVR(), client.MonitorAccess)
if !auth {
b.App().Flash().Err(err)
return nil

View File

@ -4,7 +4,6 @@ import (
"github.com/derailed/k9s/internal/client"
"github.com/derailed/k9s/internal/render"
"github.com/derailed/k9s/internal/ui"
"github.com/derailed/k9s/internal/watch"
"github.com/gdamore/tcell"
"github.com/rs/zerolog/log"
)
@ -60,7 +59,7 @@ func (l *LogsExtender) showLogs(path string, prev bool) {
log.Debug().Msgf("SHOWING LOGS path %q", path)
// Need to load and wait for pods
ns, _ := render.Namespaced(path)
_, err := l.App().factory.CanForResource(ns, "v1/pods", watch.ReadVerbs)
_, err := l.App().factory.CanForResource(ns, "v1/pods", client.MonitorAccess)
if err != nil {
l.App().Flash().Err(err)
return

View File

@ -2,6 +2,7 @@ package watch
import (
"fmt"
"sync"
"time"
"github.com/derailed/k9s/internal/client"
@ -18,15 +19,13 @@ const (
clusterScope = "-"
)
// ReadVerbs lists out RO verbs.
var ReadVerbs = []string{"get", "list", "watch"}
// Factory tracks various resource informers.
type Factory struct {
factories map[string]di.DynamicSharedInformerFactory
client client.Connection
stopChan chan struct{}
forwarders Forwarders
mx sync.RWMutex
}
// NewFactory returns a new informers factory.
@ -54,6 +53,9 @@ func (f *Factory) Terminate() {
close(f.stopChan)
f.stopChan = nil
}
f.mx.Lock()
defer f.mx.Unlock()
for k := range f.factories {
delete(f.factories, k)
}
@ -70,7 +72,7 @@ func (f *Factory) List(gvr, ns string, wait bool, labels labels.Selector) ([]run
ns = allNamespaces
}
log.Debug().Msgf("List %q:%q", ns, gvr)
inf, err := f.CanForResource(ns, gvr, []string{"list", "watch"})
inf, err := f.CanForResource(ns, gvr, client.MonitorAccess)
if err != nil {
return nil, err
}
@ -86,13 +88,18 @@ func (f *Factory) List(gvr, ns string, wait bool, labels labels.Selector) ([]run
// Get retrieves a given resource.
func (f *Factory) Get(gvr, path string, wait bool, sel labels.Selector) (runtime.Object, error) {
defer func(t time.Time) {
log.Debug().Msgf("FACTORY-GET %q--%q elapsed %v", gvr, path, time.Since(t))
}(time.Now())
ns, n := namespaced(path)
log.Debug().Msgf("GET %q:%q::%q", ns, gvr, n)
Debug(f, "", gvr)
inf, err := f.CanForResource(ns, gvr, []string{"get"})
inf, err := f.CanForResource(ns, gvr, []string{client.GetVerb})
if err != nil {
return nil, err
}
DumpFactory(f)
if wait {
f.waitForCacheSync(ns)
}
@ -193,6 +200,8 @@ func (f *Factory) ensureFactory(ns string) di.DynamicSharedInformerFactory {
}
log.Debug().Msgf("FACTORY_NEW for ns %q", ns)
f.mx.Lock()
defer f.mx.Unlock()
f.factories[ns] = di.NewFilteredDynamicSharedInformerFactory(
f.client.DynDialOrDie(),
defaultResync,
@ -210,8 +219,6 @@ func (f *Factory) AddForwarder(pf Forwarder) {
// DeleteForwarder deletes portforward for a given container.
func (f *Factory) DeleteForwarder(path string) {
f.forwarders.Dump()
count := f.forwarders.Kill(path)
log.Warn().Msgf("Deleted (%d) portforward for %q", count, path)
}

View File

@ -28,7 +28,7 @@ func namespaced(n string) (string, string) {
}
// Dump for debug.
func Dump(f *Factory) {
func DumpFactory(f *Factory) {
log.Debug().Msgf("----------- FACTORIES -------------")
for ns := range f.factories {
log.Debug().Msgf(" Factory for NS %q", ns)
@ -37,7 +37,7 @@ func Dump(f *Factory) {
}
// Debug for debug.
func Debug(f *Factory, ns string, gvr string) {
func DebugFactory(f *Factory, ns string, gvr string) {
log.Debug().Msgf("----------- DEBUG FACTORY (%s) -------------", gvr)
fac, ok := f.factories[ns]
if !ok {