add resource ref support for sec, sa and cm
parent
01cdc5b86e
commit
678143e2a3
|
|
@ -79,7 +79,7 @@ linters-settings:
|
|||
# exclude: /path/to/file.txt
|
||||
|
||||
funlen:
|
||||
lines: 75
|
||||
lines: 100
|
||||
statements: 40
|
||||
|
||||
govet:
|
||||
|
|
|
|||
|
|
@ -0,0 +1,57 @@
|
|||
<img src="https://raw.githubusercontent.com/derailed/k9s/master/assets/k9s_small.png" align="right" width="200" height="auto"/>
|
||||
|
||||
# Release v0.19.8
|
||||
|
||||
## 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 is as ever very much noticed and appreciated!
|
||||
|
||||
Also if you dig this tool, consider joining our [sponsorhip 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)
|
||||
|
||||
---
|
||||
|
||||
## Music Behind This Release
|
||||
|
||||
And now for something a `beat` different...
|
||||
I figured, why not share one of the tunes I was spinning when powering thru teh bugs.
|
||||
I've just discovered this Turkish band and thought perhaps you can listen when reading this release notes?
|
||||
|
||||
[Ruh - She Past Away](https://www.youtube.com/watch?v=B7f-opGKOyI)
|
||||
|
||||
NOTE! Mind you I grew up with the `The Cure`, so likely not for everyone here 🙀
|
||||
NOTE! If you dig this please lmk and will make this a k9s release tradition...
|
||||
|
||||
## PortForward revisited
|
||||
|
||||
While performing port-forwards it could be convenient to use a different IP address on a per cluster basis. For this reason, we are introducing a configuration setting that allows you to set the host IP address for the port-forward dialog on a given cluster. The address currently defaults to `localhost`.
|
||||
|
||||
Big Thanks and all credits goes to [Stowe4077](https://github.com/Stowe4077) and that very cute dog for raising this issue in the first place!!
|
||||
|
||||
In order to change the configuration, edit your k9s config file as follows:
|
||||
|
||||
```yaml
|
||||
k9s:
|
||||
...
|
||||
clusters:
|
||||
blee:
|
||||
namespace:
|
||||
active: ""
|
||||
favorites:
|
||||
- fred
|
||||
- default
|
||||
view:
|
||||
active: po
|
||||
portForwardAddress: 1.2.3.4
|
||||
```
|
||||
|
||||
## Resolved Bugs/Features/PRs
|
||||
|
||||
* [Issue #734](https://github.com/derailed/k9s/issues/734)
|
||||
* [Issue #733](https://github.com/derailed/k9s/issues/733)
|
||||
* [Issue #716](https://github.com/derailed/k9s/issues/716)
|
||||
|
||||
---
|
||||
|
||||
<img src="https://raw.githubusercontent.com/derailed/k9s/master/assets/imhotep_logo.png" width="32" height="auto"/> © 2020 Imhotep Software LLC. All materials licensed under [Apache v2.0](http://www.apache.org/licenses/LICENSE-2.0)
|
||||
1
go.mod
1
go.mod
|
|
@ -3,6 +3,7 @@ module github.com/derailed/k9s
|
|||
go 1.13
|
||||
|
||||
require (
|
||||
9fans.net/go v0.0.2
|
||||
github.com/atotto/clipboard v0.1.2
|
||||
github.com/derailed/popeye v0.8.3
|
||||
github.com/derailed/tview v0.3.10
|
||||
|
|
|
|||
2
go.sum
2
go.sum
|
|
@ -1,3 +1,5 @@
|
|||
9fans.net/go v0.0.2 h1:RYM6lWITV8oADrwLfdzxmt8ucfW6UtP9v1jg4qAbqts=
|
||||
9fans.net/go v0.0.2/go.mod h1:lfPdxjq9v8pVQXUMBCx5EO5oLXWQFlKRQgs1kEkjoIM=
|
||||
bazil.org/fuse v0.0.0-20160811212531-371fbbdaa898/go.mod h1:Xbm+BRKSBEpa4q4hTSxohYNQpsxXPbPry4JJWOB3LB8=
|
||||
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
|
|
|
|||
|
|
@ -1,8 +1,9 @@
|
|||
package config
|
||||
|
||||
import (
|
||||
"github.com/derailed/k9s/internal/client"
|
||||
)
|
||||
import "github.com/derailed/k9s/internal/client"
|
||||
|
||||
// DefaultPFAddress specifies the default PortForward host address.
|
||||
const DefaultPFAddress = "localhost"
|
||||
|
||||
// Cluster tracks K9s cluster configuration.
|
||||
type Cluster struct {
|
||||
|
|
@ -10,6 +11,7 @@ type Cluster struct {
|
|||
View *View `yaml:"view"`
|
||||
FeatureGates *FeatureGates `yaml:"featureGates"`
|
||||
ShellPod *ShellPod `yaml:"shellPod"`
|
||||
PortForwardAddress string `yaml:"portForwardAddress"`
|
||||
}
|
||||
|
||||
// NewCluster creates a new cluster configuration.
|
||||
|
|
@ -17,6 +19,7 @@ func NewCluster() *Cluster {
|
|||
return &Cluster{
|
||||
Namespace: NewNamespace(),
|
||||
View: NewView(),
|
||||
PortForwardAddress: DefaultPFAddress,
|
||||
FeatureGates: NewFeatureGates(),
|
||||
ShellPod: NewShellPod(),
|
||||
}
|
||||
|
|
@ -24,6 +27,10 @@ func NewCluster() *Cluster {
|
|||
|
||||
// Validate a cluster config.
|
||||
func (c *Cluster) Validate(conn client.Connection, ks KubeSettings) {
|
||||
if c.PortForwardAddress == "" {
|
||||
c.PortForwardAddress = DefaultPFAddress
|
||||
}
|
||||
|
||||
if c.Namespace == nil {
|
||||
c.Namespace = NewNamespace()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -289,6 +289,7 @@ var expectedConfig = `k9s:
|
|||
limits:
|
||||
cpu: 100m
|
||||
memory: 100Mi
|
||||
portForwardAddress: localhost
|
||||
fred:
|
||||
namespace:
|
||||
active: default
|
||||
|
|
@ -308,6 +309,7 @@ var expectedConfig = `k9s:
|
|||
limits:
|
||||
cpu: 100m
|
||||
memory: 100Mi
|
||||
portForwardAddress: localhost
|
||||
minikube:
|
||||
namespace:
|
||||
active: kube-system
|
||||
|
|
@ -327,6 +329,7 @@ var expectedConfig = `k9s:
|
|||
limits:
|
||||
cpu: 100m
|
||||
memory: 100Mi
|
||||
portForwardAddress: localhost
|
||||
thresholds:
|
||||
cpu:
|
||||
critical: 90
|
||||
|
|
@ -366,6 +369,7 @@ var resetConfig = `k9s:
|
|||
limits:
|
||||
cpu: 100m
|
||||
memory: 100Mi
|
||||
portForwardAddress: localhost
|
||||
thresholds:
|
||||
cpu:
|
||||
critical: 90
|
||||
|
|
|
|||
|
|
@ -87,13 +87,15 @@ func (a *Alias) load() error {
|
|||
if _, ok := a.Alias[meta.Kind]; ok || IsK9sMeta(meta) {
|
||||
continue
|
||||
}
|
||||
a.Define(gvr.String(), strings.ToLower(meta.Kind), meta.Name)
|
||||
gvrs := gvr.String()
|
||||
a.Define(gvrs, strings.ToLower(meta.Kind), meta.Name)
|
||||
if meta.SingularName != "" {
|
||||
a.Define(gvr.String(), meta.SingularName)
|
||||
a.Define(gvrs, meta.SingularName)
|
||||
}
|
||||
if meta.ShortNames != nil {
|
||||
a.Define(gvr.String(), meta.ShortNames...)
|
||||
a.Define(gvrs, meta.ShortNames...)
|
||||
}
|
||||
a.Define(gvrs, gvrs)
|
||||
}
|
||||
|
||||
return nil
|
||||
|
|
|
|||
|
|
@ -0,0 +1,150 @@
|
|||
package dao
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/derailed/k9s/internal"
|
||||
"github.com/derailed/k9s/internal/client"
|
||||
"github.com/rs/zerolog/log"
|
||||
)
|
||||
|
||||
// RefScanner represents a resource reference scanner.
|
||||
type RefScanner interface {
|
||||
// Init initializes the scanner
|
||||
Init(Factory, client.GVR)
|
||||
// Scan scan the resource for references.
|
||||
Scan(ctx context.Context, gvr, fqn string, wait bool) (Refs, error)
|
||||
ScanSA(ctx context.Context, fqn string, wait bool) (Refs, error)
|
||||
}
|
||||
|
||||
// Ref represents a resource reference.
|
||||
type Ref struct {
|
||||
GVR string
|
||||
FQN string
|
||||
}
|
||||
|
||||
// Refs represents a collection of resource references.
|
||||
type Refs []Ref
|
||||
|
||||
var (
|
||||
_ RefScanner = (*Deployment)(nil)
|
||||
_ RefScanner = (*StatefulSet)(nil)
|
||||
_ RefScanner = (*DaemonSet)(nil)
|
||||
_ RefScanner = (*Job)(nil)
|
||||
_ RefScanner = (*CronJob)(nil)
|
||||
_ RefScanner = (*Pod)(nil)
|
||||
)
|
||||
|
||||
func scanners() map[string]RefScanner {
|
||||
return map[string]RefScanner{
|
||||
"apps/v1/deployments": &Deployment{},
|
||||
"apps/v1/statefulsets": &StatefulSet{},
|
||||
"apps/v1/daemonsets": &DaemonSet{},
|
||||
"batch/v1/jobs": &Job{},
|
||||
"batch/v1beta1/cronjobs": &CronJob{},
|
||||
"v1/pods": &Pod{},
|
||||
}
|
||||
}
|
||||
|
||||
func ScanForRefs(ctx context.Context, f Factory) (Refs, error) {
|
||||
defer func(t time.Time) {
|
||||
log.Debug().Msgf("Cluster Scan %v", time.Since(t))
|
||||
}(time.Now())
|
||||
|
||||
gvr, ok := ctx.Value(internal.KeyGVR).(string)
|
||||
if !ok {
|
||||
return nil, errors.New("expecting context GVR")
|
||||
}
|
||||
fqn, ok := ctx.Value(internal.KeyPath).(string)
|
||||
if !ok {
|
||||
return nil, errors.New("expecting context Path")
|
||||
}
|
||||
wait, ok := ctx.Value(internal.KeyWait).(bool)
|
||||
if !ok {
|
||||
return nil, errors.New("expecting context Wait")
|
||||
}
|
||||
|
||||
ss := scanners()
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(len(ss))
|
||||
out := make(chan Refs)
|
||||
for k, s := range ss {
|
||||
go func(ctx context.Context, kind string, s RefScanner, out chan Refs, wait bool) {
|
||||
defer wg.Done()
|
||||
s.Init(f, client.NewGVR(kind))
|
||||
refs, err := s.Scan(ctx, gvr, fqn, wait)
|
||||
if err != nil {
|
||||
log.Error().Err(err).Msgf("scan failed for %T", s)
|
||||
return
|
||||
}
|
||||
select {
|
||||
case out <- refs:
|
||||
case <-ctx.Done():
|
||||
return
|
||||
}
|
||||
}(ctx, k, s, out, wait)
|
||||
}
|
||||
|
||||
go func() {
|
||||
wg.Wait()
|
||||
close(out)
|
||||
}()
|
||||
|
||||
res := make(Refs, 0, 10)
|
||||
for refs := range out {
|
||||
res = append(res, refs...)
|
||||
}
|
||||
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func ScanForSARefs(ctx context.Context, f Factory) (Refs, error) {
|
||||
defer func(t time.Time) {
|
||||
log.Debug().Msgf("Cluster Scan %v", time.Since(t))
|
||||
}(time.Now())
|
||||
|
||||
fqn, ok := ctx.Value(internal.KeyPath).(string)
|
||||
if !ok {
|
||||
return nil, errors.New("expecting context Path")
|
||||
}
|
||||
wait, ok := ctx.Value(internal.KeyWait).(bool)
|
||||
if !ok {
|
||||
return nil, errors.New("expecting context Wait")
|
||||
}
|
||||
|
||||
ss := scanners()
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(len(ss))
|
||||
out := make(chan Refs)
|
||||
for k, s := range ss {
|
||||
go func(ctx context.Context, kind string, s RefScanner, out chan Refs, wait bool) {
|
||||
defer wg.Done()
|
||||
s.Init(f, client.NewGVR(kind))
|
||||
refs, err := s.ScanSA(ctx, fqn, wait)
|
||||
if err != nil {
|
||||
log.Error().Err(err).Msgf("scan failed for %T", s)
|
||||
return
|
||||
}
|
||||
select {
|
||||
case out <- refs:
|
||||
case <-ctx.Done():
|
||||
return
|
||||
}
|
||||
}(ctx, k, s, out, wait)
|
||||
}
|
||||
|
||||
go func() {
|
||||
wg.Wait()
|
||||
close(out)
|
||||
}()
|
||||
|
||||
res := make(Refs, 0, 10)
|
||||
for refs := range out {
|
||||
res = append(res, refs...)
|
||||
}
|
||||
|
||||
return res, nil
|
||||
}
|
||||
|
|
@ -111,7 +111,7 @@ func getContainerStatus(co string, status v1.PodStatus) *v1.ContainerStatus {
|
|||
}
|
||||
|
||||
func (c *Container) fetchPod(fqn string) (*v1.Pod, error) {
|
||||
o, err := c.Factory.Get("v1/pods", fqn, false, labels.Everything())
|
||||
o, err := c.Factory.Get("v1/pods", fqn, true, labels.Everything())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,11 +2,17 @@ package dao
|
|||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/derailed/k9s/internal/client"
|
||||
"github.com/rs/zerolog/log"
|
||||
batchv1 "k8s.io/api/batch/v1"
|
||||
batchv1beta1 "k8s.io/api/batch/v1beta1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/apimachinery/pkg/labels"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/util/rand"
|
||||
)
|
||||
|
||||
|
|
@ -24,7 +30,7 @@ type CronJob struct {
|
|||
|
||||
// Run a CronJob.
|
||||
func (c *CronJob) Run(path string) error {
|
||||
ns, n := client.Namespaced(path)
|
||||
ns, _ := client.Namespaced(path)
|
||||
auth, err := c.Client().CanI(ns, "batch/v1beta1/cronjobs", []string{client.GetVerb, client.CreateVerb})
|
||||
if err != nil {
|
||||
return err
|
||||
|
|
@ -36,11 +42,17 @@ func (c *CronJob) Run(path string) error {
|
|||
// BOZO!! Factory resource??
|
||||
ctx, cancel := context.WithTimeout(context.Background(), client.CallTimeout)
|
||||
defer cancel()
|
||||
cj, err := c.Client().DialOrDie().BatchV1beta1().CronJobs(ns).Get(ctx, n, metav1.GetOptions{})
|
||||
o, err := c.Factory.Get("batch/v1/cronjobs", path, true, labels.Everything())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var cj batchv1beta1.CronJob
|
||||
err = runtime.DefaultUnstructuredConverter.FromUnstructured(o.(*unstructured.Unstructured).Object, &cj)
|
||||
if err != nil {
|
||||
return errors.New("expecting CronJob resource")
|
||||
}
|
||||
|
||||
var jobName = cj.Name
|
||||
if len(cj.Name) >= maxJobNameSize {
|
||||
jobName = cj.Name[0:maxJobNameSize]
|
||||
|
|
@ -58,3 +70,70 @@ func (c *CronJob) Run(path string) error {
|
|||
|
||||
return err
|
||||
}
|
||||
|
||||
func (c *CronJob) ScanSA(ctx context.Context, fqn string, wait bool) (Refs, error) {
|
||||
ns, n := client.Namespaced(fqn)
|
||||
oo, err := c.Factory.List(c.GVR(), ns, wait, labels.Everything())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
refs := make(Refs, 0, len(oo))
|
||||
for _, o := range oo {
|
||||
var cj batchv1beta1.CronJob
|
||||
err = runtime.DefaultUnstructuredConverter.FromUnstructured(o.(*unstructured.Unstructured).Object, &cj)
|
||||
if err != nil {
|
||||
return nil, errors.New("expecting CronJob resource")
|
||||
}
|
||||
if cj.Spec.JobTemplate.Spec.Template.Spec.ServiceAccountName == n {
|
||||
refs = append(refs, Ref{
|
||||
GVR: c.GVR(),
|
||||
FQN: client.FQN(cj.Namespace, cj.Name),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return refs, nil
|
||||
}
|
||||
|
||||
func (c *CronJob) Scan(ctx context.Context, gvr, fqn string, wait bool) (Refs, error) {
|
||||
ns, n := client.Namespaced(fqn)
|
||||
oo, err := c.Factory.List(c.GVR(), ns, wait, labels.Everything())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
refs := make(Refs, 0, len(oo))
|
||||
for _, o := range oo {
|
||||
var cj batchv1beta1.CronJob
|
||||
err = runtime.DefaultUnstructuredConverter.FromUnstructured(o.(*unstructured.Unstructured).Object, &cj)
|
||||
if err != nil {
|
||||
return nil, errors.New("expecting CronJob resource")
|
||||
}
|
||||
switch gvr {
|
||||
case "v1/configmaps":
|
||||
if !hasConfigMap(&cj.Spec.JobTemplate.Spec.Template.Spec, n) {
|
||||
continue
|
||||
}
|
||||
refs = append(refs, Ref{
|
||||
GVR: c.GVR(),
|
||||
FQN: client.FQN(cj.Namespace, cj.Name),
|
||||
})
|
||||
case "v1/secrets":
|
||||
found, err := hasSecret(c.Factory, &cj.Spec.JobTemplate.Spec.Template.Spec, cj.Namespace, n, wait)
|
||||
if err != nil {
|
||||
log.Warn().Err(err).Msgf("locate secret %q", fqn)
|
||||
continue
|
||||
}
|
||||
if !found {
|
||||
continue
|
||||
}
|
||||
refs = append(refs, Ref{
|
||||
GVR: c.GVR(),
|
||||
FQN: client.FQN(cj.Namespace, cj.Name),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return refs, nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -35,6 +35,7 @@ func Describe(c client.Connection, gvr client.GVR, path string) (string, error)
|
|||
log.Error().Err(err).Msgf("Unable to find describer for %#v", mapping)
|
||||
return "", err
|
||||
}
|
||||
log.Debug().Msgf("Describing %q -- %q", ns, n)
|
||||
|
||||
return d.Describe(ns, n, describe.DescriberSettings{ShowEvents: true})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,7 +6,9 @@ import (
|
|||
"fmt"
|
||||
|
||||
"github.com/derailed/k9s/internal/client"
|
||||
"github.com/rs/zerolog/log"
|
||||
appsv1 "k8s.io/api/apps/v1"
|
||||
v1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/apimachinery/pkg/labels"
|
||||
|
|
@ -123,3 +125,171 @@ func (*Deployment) Load(f Factory, fqn string) (*appsv1.Deployment, error) {
|
|||
|
||||
return &dp, nil
|
||||
}
|
||||
|
||||
func (d *Deployment) ScanSA(ctx context.Context, fqn string, wait bool) (Refs, error) {
|
||||
ns, n := client.Namespaced(fqn)
|
||||
oo, err := d.Factory.List(d.GVR(), ns, wait, labels.Everything())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
refs := make(Refs, 0, len(oo))
|
||||
for _, o := range oo {
|
||||
var dp appsv1.Deployment
|
||||
err = runtime.DefaultUnstructuredConverter.FromUnstructured(o.(*unstructured.Unstructured).Object, &dp)
|
||||
if err != nil {
|
||||
return nil, errors.New("expecting Deployment resource")
|
||||
}
|
||||
if dp.Spec.Template.Spec.ServiceAccountName == n {
|
||||
refs = append(refs, Ref{
|
||||
GVR: d.GVR(),
|
||||
FQN: client.FQN(dp.Namespace, dp.Name),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return refs, nil
|
||||
}
|
||||
|
||||
func (d *Deployment) Scan(ctx context.Context, gvr, fqn string, wait bool) (Refs, error) {
|
||||
ns, n := client.Namespaced(fqn)
|
||||
oo, err := d.Factory.List(d.GVR(), ns, wait, labels.Everything())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
refs := make(Refs, 0, len(oo))
|
||||
for _, o := range oo {
|
||||
var dp appsv1.Deployment
|
||||
err = runtime.DefaultUnstructuredConverter.FromUnstructured(o.(*unstructured.Unstructured).Object, &dp)
|
||||
if err != nil {
|
||||
return nil, errors.New("expecting Deployment resource")
|
||||
}
|
||||
switch gvr {
|
||||
case "v1/configmaps":
|
||||
if !hasConfigMap(&dp.Spec.Template.Spec, n) {
|
||||
continue
|
||||
}
|
||||
refs = append(refs, Ref{
|
||||
GVR: d.GVR(),
|
||||
FQN: client.FQN(dp.Namespace, dp.Name),
|
||||
})
|
||||
case "v1/secrets":
|
||||
found, err := hasSecret(d.Factory, &dp.Spec.Template.Spec, dp.Namespace, n, wait)
|
||||
if err != nil {
|
||||
log.Warn().Err(err).Msgf("scanning secret %q", fqn)
|
||||
continue
|
||||
}
|
||||
if !found {
|
||||
continue
|
||||
}
|
||||
refs = append(refs, Ref{
|
||||
GVR: d.GVR(),
|
||||
FQN: client.FQN(dp.Namespace, dp.Name),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return refs, nil
|
||||
}
|
||||
|
||||
func hasConfigMap(spec *v1.PodSpec, name string) bool {
|
||||
for _, c := range spec.InitContainers {
|
||||
if containerHasConfigMap(c, name) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
for _, c := range spec.Containers {
|
||||
if containerHasConfigMap(c, name) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
for _, v := range spec.Volumes {
|
||||
if cm := v.VolumeSource.ConfigMap; cm != nil {
|
||||
if cm.LocalObjectReference.Name == name {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// BOZO !! Need to deal with ephemeral containers.
|
||||
func hasSecret(f Factory, spec *v1.PodSpec, ns, name string, wait bool) (bool, error) {
|
||||
for _, c := range spec.InitContainers {
|
||||
if containerHasSecret(c, name) {
|
||||
return true, nil
|
||||
}
|
||||
}
|
||||
for _, c := range spec.Containers {
|
||||
if containerHasSecret(c, name) {
|
||||
return true, nil
|
||||
}
|
||||
}
|
||||
|
||||
saName := spec.ServiceAccountName
|
||||
if saName != "" {
|
||||
o, err := f.Get("v1/serviceaccounts", client.FQN(ns, saName), wait, labels.Everything())
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
var sa v1.ServiceAccount
|
||||
err = runtime.DefaultUnstructuredConverter.FromUnstructured(o.(*unstructured.Unstructured).Object, &sa)
|
||||
if err != nil {
|
||||
return false, errors.New("expecting ServiceAccount resource")
|
||||
}
|
||||
|
||||
for _, ref := range sa.Secrets {
|
||||
if ref.Namespace == ns && ref.Name == name {
|
||||
return true, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for _, v := range spec.Volumes {
|
||||
if sec := v.VolumeSource.Secret; sec != nil {
|
||||
if sec.SecretName == name {
|
||||
return true, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
return false, nil
|
||||
}
|
||||
|
||||
func containerHasSecret(c v1.Container, name string) bool {
|
||||
for _, e := range c.EnvFrom {
|
||||
if e.SecretRef != nil && e.SecretRef.Name == name {
|
||||
return true
|
||||
}
|
||||
}
|
||||
for _, e := range c.Env {
|
||||
if e.ValueFrom == nil || e.ValueFrom.SecretKeyRef == nil {
|
||||
continue
|
||||
}
|
||||
if e.ValueFrom.SecretKeyRef.Name == name {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func containerHasConfigMap(c v1.Container, name string) bool {
|
||||
for _, e := range c.EnvFrom {
|
||||
if e.ConfigMapRef != nil && e.ConfigMapRef.Name == name {
|
||||
return true
|
||||
}
|
||||
}
|
||||
for _, e := range c.Env {
|
||||
if e.ValueFrom == nil || e.ValueFrom.ConfigMapKeyRef == nil {
|
||||
continue
|
||||
}
|
||||
if e.ValueFrom.ConfigMapKeyRef.Name == name {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ import (
|
|||
"github.com/derailed/k9s/internal"
|
||||
"github.com/derailed/k9s/internal/client"
|
||||
"github.com/derailed/k9s/internal/watch"
|
||||
"github.com/rs/zerolog/log"
|
||||
appsv1 "k8s.io/api/apps/v1"
|
||||
v1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
|
|
@ -143,6 +144,73 @@ func (d *DaemonSet) GetInstance(fqn string) (*appsv1.DaemonSet, error) {
|
|||
return &ds, nil
|
||||
}
|
||||
|
||||
func (d *DaemonSet) ScanSA(ctx context.Context, fqn string, wait bool) (Refs, error) {
|
||||
ns, n := client.Namespaced(fqn)
|
||||
oo, err := d.Factory.List(d.GVR(), ns, wait, labels.Everything())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
refs := make(Refs, 0, len(oo))
|
||||
for _, o := range oo {
|
||||
var ds appsv1.DaemonSet
|
||||
err = runtime.DefaultUnstructuredConverter.FromUnstructured(o.(*unstructured.Unstructured).Object, &ds)
|
||||
if err != nil {
|
||||
return nil, errors.New("expecting DaemonSet resource")
|
||||
}
|
||||
if ds.Spec.Template.Spec.ServiceAccountName == n {
|
||||
refs = append(refs, Ref{
|
||||
GVR: d.GVR(),
|
||||
FQN: client.FQN(ds.Namespace, ds.Name),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return refs, nil
|
||||
}
|
||||
|
||||
func (d *DaemonSet) Scan(ctx context.Context, gvr, fqn string, wait bool) (Refs, error) {
|
||||
ns, n := client.Namespaced(fqn)
|
||||
oo, err := d.Factory.List(d.GVR(), ns, wait, labels.Everything())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
refs := make(Refs, 0, len(oo))
|
||||
for _, o := range oo {
|
||||
var ds appsv1.DaemonSet
|
||||
err = runtime.DefaultUnstructuredConverter.FromUnstructured(o.(*unstructured.Unstructured).Object, &ds)
|
||||
if err != nil {
|
||||
return nil, errors.New("expecting StatefulSet resource")
|
||||
}
|
||||
switch gvr {
|
||||
case "v1/configmaps":
|
||||
if !hasConfigMap(&ds.Spec.Template.Spec, n) {
|
||||
continue
|
||||
}
|
||||
refs = append(refs, Ref{
|
||||
GVR: d.GVR(),
|
||||
FQN: client.FQN(ds.Namespace, ds.Name),
|
||||
})
|
||||
case "v1/secrets":
|
||||
found, err := hasSecret(d.Factory, &ds.Spec.Template.Spec, ds.Namespace, n, wait)
|
||||
if err != nil {
|
||||
log.Warn().Err(err).Msgf("locate secret %q", fqn)
|
||||
continue
|
||||
}
|
||||
if !found {
|
||||
continue
|
||||
}
|
||||
refs = append(refs, Ref{
|
||||
GVR: d.GVR(),
|
||||
FQN: client.FQN(ds.Namespace, ds.Name),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return refs, nil
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Helpers...
|
||||
|
||||
|
|
|
|||
|
|
@ -57,7 +57,6 @@ func (g *Generic) List(ctx context.Context, ns string) ([]runtime.Object, error)
|
|||
|
||||
// Get returns a given resource.
|
||||
func (g *Generic) Get(ctx context.Context, path string) (runtime.Object, error) {
|
||||
log.Debug().Msgf("GENERIC-GET %q", path)
|
||||
var opts metav1.GetOptions
|
||||
ns, n := client.Namespaced(path)
|
||||
dial := g.dynClient()
|
||||
|
|
|
|||
|
|
@ -5,6 +5,8 @@ import (
|
|||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/derailed/k9s/internal/client"
|
||||
"github.com/rs/zerolog/log"
|
||||
batchv1 "k8s.io/api/batch/v1"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/apimachinery/pkg/labels"
|
||||
|
|
@ -41,3 +43,70 @@ func (j *Job) TailLogs(ctx context.Context, c LogChan, opts LogOptions) error {
|
|||
|
||||
return podLogs(ctx, c, job.Spec.Selector.MatchLabels, opts)
|
||||
}
|
||||
|
||||
func (j *Job) ScanSA(ctx context.Context, fqn string, wait bool) (Refs, error) {
|
||||
ns, n := client.Namespaced(fqn)
|
||||
oo, err := j.Factory.List(j.GVR(), ns, wait, labels.Everything())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
refs := make(Refs, 0, len(oo))
|
||||
for _, o := range oo {
|
||||
var job batchv1.Job
|
||||
err = runtime.DefaultUnstructuredConverter.FromUnstructured(o.(*unstructured.Unstructured).Object, &job)
|
||||
if err != nil {
|
||||
return nil, errors.New("expecting Job resource")
|
||||
}
|
||||
if job.Spec.Template.Spec.ServiceAccountName == n {
|
||||
refs = append(refs, Ref{
|
||||
GVR: j.GVR(),
|
||||
FQN: client.FQN(job.Namespace, job.Name),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return refs, nil
|
||||
}
|
||||
|
||||
func (j *Job) Scan(ctx context.Context, gvr, fqn string, wait bool) (Refs, error) {
|
||||
ns, n := client.Namespaced(fqn)
|
||||
oo, err := j.Factory.List(j.GVR(), ns, wait, labels.Everything())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
refs := make(Refs, 0, len(oo))
|
||||
for _, o := range oo {
|
||||
var job batchv1.Job
|
||||
err = runtime.DefaultUnstructuredConverter.FromUnstructured(o.(*unstructured.Unstructured).Object, &job)
|
||||
if err != nil {
|
||||
return nil, errors.New("expecting Job resource")
|
||||
}
|
||||
switch gvr {
|
||||
case "v1/configmaps":
|
||||
if !hasConfigMap(&job.Spec.Template.Spec, n) {
|
||||
continue
|
||||
}
|
||||
refs = append(refs, Ref{
|
||||
GVR: j.GVR(),
|
||||
FQN: client.FQN(job.Namespace, job.Name),
|
||||
})
|
||||
case "v1/secrets":
|
||||
found, err := hasSecret(j.Factory, &job.Spec.Template.Spec, job.Namespace, n, wait)
|
||||
if err != nil {
|
||||
log.Warn().Err(err).Msgf("locate secret %q", fqn)
|
||||
continue
|
||||
}
|
||||
if !found {
|
||||
continue
|
||||
}
|
||||
refs = append(refs, Ref{
|
||||
GVR: j.GVR(),
|
||||
FQN: client.FQN(job.Namespace, job.Name),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return refs, nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -69,7 +69,7 @@ func (p *Pod) Get(ctx context.Context, path string) (runtime.Object, error) {
|
|||
func (p *Pod) List(ctx context.Context, ns string) ([]runtime.Object, error) {
|
||||
sel, ok := ctx.Value(internal.KeyFields).(string)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("expecting a fieldSelector in context")
|
||||
sel = ""
|
||||
}
|
||||
fsel, err := labels.ConvertSelectorToLabelsMap(sel)
|
||||
if err != nil {
|
||||
|
|
@ -155,7 +155,7 @@ func (p *Pod) Pod(fqn string) (string, error) {
|
|||
|
||||
// GetInstance returns a pod instance.
|
||||
func (p *Pod) GetInstance(fqn string) (*v1.Pod, error) {
|
||||
o, err := p.Factory.Get(p.gvr.String(), fqn, false, labels.Everything())
|
||||
o, err := p.Factory.Get(p.gvr.String(), fqn, true, labels.Everything())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
@ -226,6 +226,84 @@ func (p *Pod) TailLogs(ctx context.Context, c LogChan, opts LogOptions) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (p *Pod) ScanSA(ctx context.Context, fqn string, wait bool) (Refs, error) {
|
||||
ns, n := client.Namespaced(fqn)
|
||||
oo, err := p.Factory.List(p.GVR(), ns, wait, labels.Everything())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
refs := make(Refs, 0, len(oo))
|
||||
for _, o := range oo {
|
||||
var pod v1.Pod
|
||||
err = runtime.DefaultUnstructuredConverter.FromUnstructured(o.(*unstructured.Unstructured).Object, &pod)
|
||||
if err != nil {
|
||||
return nil, errors.New("expecting Deployment resource")
|
||||
}
|
||||
// Just pick controller less pods...
|
||||
if len(pod.ObjectMeta.OwnerReferences) > 0 {
|
||||
continue
|
||||
}
|
||||
if pod.Spec.ServiceAccountName == n {
|
||||
refs = append(refs, Ref{
|
||||
GVR: p.GVR(),
|
||||
FQN: client.FQN(pod.Namespace, pod.Name),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return refs, nil
|
||||
}
|
||||
|
||||
func (p *Pod) Scan(ctx context.Context, gvr, fqn string, wait bool) (Refs, error) {
|
||||
ns, n := client.Namespaced(fqn)
|
||||
oo, err := p.Factory.List(p.GVR(), ns, wait, labels.Everything())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
refs := make(Refs, 0, len(oo))
|
||||
for _, o := range oo {
|
||||
var pod v1.Pod
|
||||
err = runtime.DefaultUnstructuredConverter.FromUnstructured(o.(*unstructured.Unstructured).Object, &pod)
|
||||
if err != nil {
|
||||
return nil, errors.New("expecting Pod resource")
|
||||
}
|
||||
// Just pick controller less pods...
|
||||
if len(pod.ObjectMeta.OwnerReferences) > 0 {
|
||||
continue
|
||||
}
|
||||
switch gvr {
|
||||
case "v1/configmaps":
|
||||
if !hasConfigMap(&pod.Spec, n) {
|
||||
continue
|
||||
}
|
||||
refs = append(refs, Ref{
|
||||
GVR: p.GVR(),
|
||||
FQN: client.FQN(pod.Namespace, pod.Name),
|
||||
})
|
||||
case "v1/secrets":
|
||||
found, err := hasSecret(p.Factory, &pod.Spec, pod.Namespace, n, wait)
|
||||
if err != nil {
|
||||
log.Warn().Err(err).Msgf("locate secret %q", fqn)
|
||||
continue
|
||||
}
|
||||
if !found {
|
||||
continue
|
||||
}
|
||||
refs = append(refs, Ref{
|
||||
GVR: p.GVR(),
|
||||
FQN: client.FQN(pod.Namespace, pod.Name),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return refs, nil
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Helpers...
|
||||
|
||||
func tailLogs(ctx context.Context, logger Logger, c LogChan, opts LogOptions) error {
|
||||
log.Debug().Msgf("Tailing logs for %q:%q", opts.Path, opts.Container)
|
||||
req, err := logger.Logs(opts.Path, opts.ToPodLogOptions())
|
||||
|
|
@ -270,9 +348,6 @@ func readLogs(stream io.ReadCloser, c LogChan, opts LogOptions) {
|
|||
}
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Helpers...
|
||||
|
||||
func podMetricsFor(o runtime.Object, mmx *mv1beta1.PodMetricsList) *mv1beta1.PodMetrics {
|
||||
if mmx == nil {
|
||||
return nil
|
||||
|
|
|
|||
|
|
@ -0,0 +1,84 @@
|
|||
package dao
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
|
||||
"github.com/derailed/k9s/internal"
|
||||
"github.com/derailed/k9s/internal/client"
|
||||
"github.com/derailed/k9s/internal/render"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
)
|
||||
|
||||
var (
|
||||
_ Accessor = (*Reference)(nil)
|
||||
)
|
||||
|
||||
type Reference struct {
|
||||
NonResource
|
||||
}
|
||||
|
||||
func (r *Reference) List(ctx context.Context, ns string) ([]runtime.Object, error) {
|
||||
gvr, ok := ctx.Value(internal.KeyGVR).(string)
|
||||
if !ok {
|
||||
return nil, errors.New("No context GVR found")
|
||||
}
|
||||
switch gvr {
|
||||
case "v1/serviceaccounts":
|
||||
return r.ScanSA(ctx)
|
||||
default:
|
||||
return r.Scan(ctx)
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Reference) Get(ctx context.Context, path string) (runtime.Object, error) {
|
||||
panic("NYI")
|
||||
}
|
||||
|
||||
func (r *Reference) Scan(ctx context.Context) ([]runtime.Object, error) {
|
||||
refs, err := ScanForRefs(ctx, r.Factory)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
fqn, ok := ctx.Value(internal.KeyPath).(string)
|
||||
if !ok {
|
||||
return nil, errors.New("expecting context Path")
|
||||
}
|
||||
ns, _ := client.Namespaced(fqn)
|
||||
oo := make([]runtime.Object, 0, len(refs))
|
||||
for _, ref := range refs {
|
||||
_, n := client.Namespaced(ref.FQN)
|
||||
oo = append(oo, render.ReferenceRes{
|
||||
Namespace: ns,
|
||||
Name: n,
|
||||
GVR: ref.GVR,
|
||||
})
|
||||
}
|
||||
|
||||
return oo, nil
|
||||
}
|
||||
|
||||
func (r *Reference) ScanSA(ctx context.Context) ([]runtime.Object, error) {
|
||||
refs, err := ScanForSARefs(ctx, r.Factory)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
fqn, ok := ctx.Value(internal.KeyPath).(string)
|
||||
if !ok {
|
||||
return nil, errors.New("expecting context Path")
|
||||
}
|
||||
ns, _ := client.Namespaced(fqn)
|
||||
oo := make([]runtime.Object, 0, len(refs))
|
||||
for _, ref := range refs {
|
||||
_, n := client.Namespaced(ref.FQN)
|
||||
oo = append(oo, render.ReferenceRes{
|
||||
Namespace: ns,
|
||||
Name: n,
|
||||
GVR: ref.GVR,
|
||||
})
|
||||
}
|
||||
|
||||
return oo, nil
|
||||
}
|
||||
|
|
@ -158,6 +158,13 @@ func loadK9s(m ResourceMetas) {
|
|||
SingularName: "xray",
|
||||
Categories: []string{"k9s"},
|
||||
}
|
||||
m[client.NewGVR("references")] = metav1.APIResource{
|
||||
Name: "references",
|
||||
Kind: "References",
|
||||
SingularName: "reference",
|
||||
Verbs: []string{},
|
||||
Categories: []string{"k9s"},
|
||||
}
|
||||
m[client.NewGVR("aliases")] = metav1.APIResource{
|
||||
Name: "aliases",
|
||||
Kind: "Aliases",
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import (
|
|||
"fmt"
|
||||
|
||||
"github.com/derailed/k9s/internal/client"
|
||||
"github.com/rs/zerolog/log"
|
||||
appsv1 "k8s.io/api/apps/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
|
|
@ -110,7 +111,7 @@ func (s *StatefulSet) Pod(fqn string) (string, error) {
|
|||
}
|
||||
|
||||
func (s *StatefulSet) getStatefulSet(fqn string) (*appsv1.StatefulSet, error) {
|
||||
o, err := s.Factory.Get(s.gvr.String(), fqn, false, labels.Everything())
|
||||
o, err := s.Factory.Get(s.gvr.String(), fqn, true, labels.Everything())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
@ -123,3 +124,70 @@ func (s *StatefulSet) getStatefulSet(fqn string) (*appsv1.StatefulSet, error) {
|
|||
|
||||
return &sts, nil
|
||||
}
|
||||
|
||||
func (s *StatefulSet) ScanSA(ctx context.Context, fqn string, wait bool) (Refs, error) {
|
||||
ns, n := client.Namespaced(fqn)
|
||||
oo, err := s.Factory.List(s.GVR(), ns, wait, labels.Everything())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
refs := make(Refs, 0, len(oo))
|
||||
for _, o := range oo {
|
||||
var sts appsv1.StatefulSet
|
||||
err = runtime.DefaultUnstructuredConverter.FromUnstructured(o.(*unstructured.Unstructured).Object, &sts)
|
||||
if err != nil {
|
||||
return nil, errors.New("expecting StatefulSet resource")
|
||||
}
|
||||
if sts.Spec.Template.Spec.ServiceAccountName == n {
|
||||
refs = append(refs, Ref{
|
||||
GVR: s.GVR(),
|
||||
FQN: client.FQN(sts.Namespace, sts.Name),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return refs, nil
|
||||
}
|
||||
|
||||
func (s *StatefulSet) Scan(ctx context.Context, gvr, fqn string, wait bool) (Refs, error) {
|
||||
ns, n := client.Namespaced(fqn)
|
||||
oo, err := s.Factory.List(s.GVR(), ns, wait, labels.Everything())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
refs := make(Refs, 0, len(oo))
|
||||
for _, o := range oo {
|
||||
var sts appsv1.StatefulSet
|
||||
err = runtime.DefaultUnstructuredConverter.FromUnstructured(o.(*unstructured.Unstructured).Object, &sts)
|
||||
if err != nil {
|
||||
return nil, errors.New("expecting StatefulSet resource")
|
||||
}
|
||||
switch gvr {
|
||||
case "v1/configmaps":
|
||||
if !hasConfigMap(&sts.Spec.Template.Spec, n) {
|
||||
continue
|
||||
}
|
||||
refs = append(refs, Ref{
|
||||
GVR: s.GVR(),
|
||||
FQN: client.FQN(sts.Namespace, sts.Name),
|
||||
})
|
||||
case "v1/secrets":
|
||||
found, err := hasSecret(s.Factory, &sts.Spec.Template.Spec, sts.Namespace, n, wait)
|
||||
if err != nil {
|
||||
log.Warn().Err(err).Msgf("locate secret %q", fqn)
|
||||
continue
|
||||
}
|
||||
if !found {
|
||||
continue
|
||||
}
|
||||
refs = append(refs, Ref{
|
||||
GVR: s.GVR(),
|
||||
FQN: client.FQN(sts.Namespace, sts.Name),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return refs, nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,7 +6,6 @@ import (
|
|||
|
||||
"github.com/derailed/k9s/internal"
|
||||
"github.com/derailed/k9s/internal/client"
|
||||
"github.com/rs/zerolog/log"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
metav1beta1 "k8s.io/apimachinery/pkg/apis/meta/v1beta1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
|
|
@ -45,7 +44,7 @@ func (t *Table) Get(ctx context.Context, path string) (runtime.Object, error) {
|
|||
func (t *Table) List(ctx context.Context, ns string) ([]runtime.Object, error) {
|
||||
labelSel, ok := ctx.Value(internal.KeyLabels).(string)
|
||||
if !ok {
|
||||
log.Debug().Msgf("No label selector found in context. Listing all resources")
|
||||
labelSel = ""
|
||||
}
|
||||
|
||||
a := fmt.Sprintf(gvFmt, metav1beta1.SchemeGroupVersion.Version, metav1beta1.GroupName)
|
||||
|
|
|
|||
|
|
@ -29,4 +29,5 @@ const (
|
|||
KeyToast ContextKey = "toast"
|
||||
KeyWithMetrics ContextKey = "withMetrics"
|
||||
KeyViewConfig ContextKey = "viewConfig"
|
||||
KeyWait ContextKey = "wait"
|
||||
)
|
||||
|
|
|
|||
|
|
@ -10,6 +10,10 @@ import (
|
|||
// BOZO!! Break up deps and merge into single registrar
|
||||
var Registry = map[string]ResourceMeta{
|
||||
// Custom...
|
||||
"references": {
|
||||
DAO: &dao.Reference{},
|
||||
Renderer: &render.Reference{},
|
||||
},
|
||||
"helm": {
|
||||
DAO: &dao.Helm{},
|
||||
Renderer: &render.Helm{},
|
||||
|
|
|
|||
|
|
@ -202,7 +202,7 @@ func (t *Table) refresh(ctx context.Context) {
|
|||
defer atomic.StoreInt32(&t.inUpdate, 0)
|
||||
|
||||
if err := t.reconcile(ctx); err != nil {
|
||||
log.Error().Err(err).Msg("Reconcile failed")
|
||||
log.Error().Err(err).Msgf("reconcile failed %q::%q", t.gvr, t.instance)
|
||||
t.fireTableLoadFailed(err)
|
||||
return
|
||||
}
|
||||
|
|
@ -237,7 +237,6 @@ func (t *Table) reconcile(ctx context.Context) error {
|
|||
oo, err = []runtime.Object{o}, e
|
||||
}
|
||||
if err != nil {
|
||||
log.Error().Err(err).Msg("Reconcile failed to list resource")
|
||||
return err
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,67 @@
|
|||
package render
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/derailed/k9s/internal/client"
|
||||
"github.com/gdamore/tcell"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
)
|
||||
|
||||
// Reference renders a reference to screen.
|
||||
type Reference struct{}
|
||||
|
||||
// ColorerFunc colors a resource row.
|
||||
func (Reference) ColorerFunc() ColorerFunc {
|
||||
return func(ns string, _ Header, re RowEvent) tcell.Color {
|
||||
return tcell.ColorCadetBlue
|
||||
}
|
||||
}
|
||||
|
||||
// Header returns a header row.
|
||||
func (Reference) Header(ns string) Header {
|
||||
return Header{
|
||||
HeaderColumn{Name: "NAMESPACE"},
|
||||
HeaderColumn{Name: "NAME"},
|
||||
HeaderColumn{Name: "GVR"},
|
||||
}
|
||||
}
|
||||
|
||||
// Render renders a K8s resource to screen.
|
||||
// BOZO!! Pass in a row with pre-alloc fields??
|
||||
func (Reference) Render(o interface{}, ns string, r *Row) error {
|
||||
ref, ok := o.(ReferenceRes)
|
||||
if !ok {
|
||||
return fmt.Errorf("expected ReferenceRes, but got %T", o)
|
||||
}
|
||||
|
||||
r.ID = client.FQN(ref.Namespace, ref.Name)
|
||||
r.Fields = append(r.Fields,
|
||||
ref.Namespace,
|
||||
ref.Name,
|
||||
ref.GVR,
|
||||
)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Helpers...
|
||||
|
||||
// ReferenceRes represents a reference resource.
|
||||
type ReferenceRes struct {
|
||||
Namespace string
|
||||
Name string
|
||||
GVR string
|
||||
}
|
||||
|
||||
// GetObjectKind returns a schema object.
|
||||
func (ReferenceRes) GetObjectKind() schema.ObjectKind {
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeepCopyObject returns a container copy.
|
||||
func (a ReferenceRes) DeepCopyObject() runtime.Object {
|
||||
return a
|
||||
}
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
package render_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/derailed/k9s/internal/render"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestReferenceRender(t *testing.T) {
|
||||
o := render.ReferenceRes{
|
||||
Namespace: "ns1",
|
||||
Name: "blee",
|
||||
GVR: "v1/secrets",
|
||||
}
|
||||
|
||||
var (
|
||||
ref = render.Reference{}
|
||||
r render.Row
|
||||
)
|
||||
assert.Nil(t, ref.Render(o, "fred", &r))
|
||||
assert.Equal(t, "ns1/blee", r.ID)
|
||||
assert.Equal(t, render.Fields{
|
||||
"ns1",
|
||||
"blee",
|
||||
"v1/secrets",
|
||||
}, r.Fields)
|
||||
}
|
||||
|
|
@ -32,12 +32,13 @@ func NewAlias(gvr client.GVR) ResourceViewer {
|
|||
return &a
|
||||
}
|
||||
|
||||
// Init initialiazes the view.
|
||||
// Init initializes the view.
|
||||
func (a *Alias) Init(ctx context.Context) error {
|
||||
if err := a.ResourceViewer.Init(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
a.GetTable().GetModel().SetNamespace("*")
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -394,16 +394,18 @@ func (b *Browser) setNamespace(ns string) {
|
|||
}
|
||||
|
||||
func (b *Browser) defaultContext() context.Context {
|
||||
ctx := context.Background()
|
||||
ctx = context.WithValue(ctx, internal.KeyFactory, b.app.factory)
|
||||
ctx := context.WithValue(context.Background(), internal.KeyFactory, b.app.factory)
|
||||
ctx = context.WithValue(ctx, internal.KeyGVR, b.GVR().String())
|
||||
if b.Path != "" {
|
||||
ctx = context.WithValue(ctx, internal.KeyPath, b.Path)
|
||||
|
||||
ctx = context.WithValue(ctx, internal.KeyLabels, "")
|
||||
}
|
||||
// BOZO!!
|
||||
// ctx = context.WithValue(ctx, internal.KeyLabels, "")
|
||||
if ui.IsLabelSelector(b.CmdBuff().GetText()) {
|
||||
ctx = context.WithValue(ctx, internal.KeyLabels, ui.TrimLabelSelector(b.CmdBuff().GetText()))
|
||||
}
|
||||
ctx = context.WithValue(ctx, internal.KeyFields, "")
|
||||
// BOZO!!
|
||||
// ctx = context.WithValue(ctx, internal.KeyFields, "")
|
||||
ctx = context.WithValue(ctx, internal.KeyNamespace, client.CleanseNamespace(b.App().Config.ActiveNamespace()))
|
||||
|
||||
return ctx
|
||||
|
|
|
|||
|
|
@ -0,0 +1,71 @@
|
|||
package view
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/derailed/k9s/internal"
|
||||
"github.com/derailed/k9s/internal/client"
|
||||
"github.com/derailed/k9s/internal/dao"
|
||||
"github.com/derailed/k9s/internal/ui"
|
||||
"github.com/gdamore/tcell"
|
||||
)
|
||||
|
||||
// ConfigMap represents a configmap viewer.
|
||||
type ConfigMap struct {
|
||||
ResourceViewer
|
||||
}
|
||||
|
||||
// NewConfigMap returns a new viewer.
|
||||
func NewConfigMap(gvr client.GVR) ResourceViewer {
|
||||
s := ConfigMap{
|
||||
ResourceViewer: NewBrowser(gvr),
|
||||
}
|
||||
s.SetBindKeysFn(s.bindKeys)
|
||||
|
||||
return &s
|
||||
}
|
||||
|
||||
func (s *ConfigMap) bindKeys(aa ui.KeyActions) {
|
||||
aa.Add(ui.KeyActions{
|
||||
ui.KeyR: ui.NewKeyAction("Referenced by", s.refCmd, true),
|
||||
})
|
||||
}
|
||||
|
||||
func (s *ConfigMap) refCmd(evt *tcell.EventKey) *tcell.EventKey {
|
||||
return scanRefs(evt, s.App(), s.GetTable(), "v1/configmaps")
|
||||
}
|
||||
|
||||
func scanRefs(evt *tcell.EventKey, a *App, t *Table, gvr string) *tcell.EventKey {
|
||||
path := t.GetSelectedItem()
|
||||
if path == "" {
|
||||
return evt
|
||||
}
|
||||
|
||||
ctx := context.Background()
|
||||
refs, err := dao.ScanForRefs(refContext(gvr, path, true)(ctx), a.factory)
|
||||
if err != nil {
|
||||
a.Flash().Err(err)
|
||||
return nil
|
||||
}
|
||||
if len(refs) == 0 {
|
||||
a.Flash().Warnf("No references found at this time for %s::%s. Check again later!", gvr, path)
|
||||
return nil
|
||||
} else {
|
||||
a.Flash().Infof("Viewing references for %s::%s", gvr, path)
|
||||
}
|
||||
view := NewReference(client.NewGVR("references"))
|
||||
view.SetContextFn(refContext(gvr, path, false))
|
||||
if err := a.inject(view); err != nil {
|
||||
a.Flash().Err(err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func refContext(gvr, path string, wait bool) ContextFunc {
|
||||
return func(ctx context.Context) context.Context {
|
||||
ctx = context.WithValue(ctx, internal.KeyPath, path)
|
||||
ctx = context.WithValue(ctx, internal.KeyGVR, gvr)
|
||||
return context.WithValue(ctx, internal.KeyWait, wait)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
package view_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/derailed/k9s/internal/client"
|
||||
"github.com/derailed/k9s/internal/view"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestConfigMapNew(t *testing.T) {
|
||||
s := view.NewConfigMap(client.NewGVR("v1/configmaps"))
|
||||
|
||||
assert.Nil(t, s.Init(makeCtx()))
|
||||
assert.Equal(t, "ConfigMaps", s.Name())
|
||||
assert.Equal(t, 5, len(s.Hints()))
|
||||
}
|
||||
|
|
@ -27,7 +27,8 @@ func ShowPortForwards(v ResourceViewer, path string, ports []string, okFn PortFo
|
|||
SetLabelColor(styles.K9s.Info.FgColor.Color()).
|
||||
SetFieldTextColor(styles.K9s.Info.SectionColor.Color())
|
||||
|
||||
p1, p2, address := ports[0], extractPort(ports[0]), "localhost"
|
||||
address := v.App().Config.CurrentCluster().PortForwardAddress
|
||||
p1, p2 := ports[0], extractPort(ports[0])
|
||||
f.AddInputField("Container Port:", p1, 30, nil, func(p string) {
|
||||
p1 = p
|
||||
})
|
||||
|
|
|
|||
|
|
@ -147,7 +147,7 @@ func showFwdDialog(v ResourceViewer, path string, cb PortForwardCB) error {
|
|||
|
||||
func fetchPodPorts(f *watch.Factory, path string) (map[string][]v1.ContainerPort, error) {
|
||||
log.Debug().Msgf("Fetching ports on pod %q", path)
|
||||
o, err := f.Get("v1/pods", path, false, labels.Everything())
|
||||
o, err := f.Get("v1/pods", path, true, labels.Everything())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,63 @@
|
|||
package view
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/derailed/k9s/internal/client"
|
||||
"github.com/derailed/k9s/internal/render"
|
||||
"github.com/derailed/k9s/internal/ui"
|
||||
"github.com/gdamore/tcell"
|
||||
)
|
||||
|
||||
// Reference represents resource references.
|
||||
type Reference struct {
|
||||
ResourceViewer
|
||||
}
|
||||
|
||||
// NewReference returns a new alias view.
|
||||
func NewReference(gvr client.GVR) ResourceViewer {
|
||||
r := Reference{
|
||||
ResourceViewer: NewBrowser(gvr),
|
||||
}
|
||||
r.GetTable().SetColorerFn(render.Reference{}.ColorerFunc())
|
||||
r.GetTable().SetBorderFocusColor(tcell.ColorMediumSpringGreen)
|
||||
r.GetTable().SetSelectedStyle(tcell.ColorWhite, tcell.ColorMediumSpringGreen, tcell.AttrNone)
|
||||
r.SetBindKeysFn(r.bindKeys)
|
||||
|
||||
return &r
|
||||
}
|
||||
|
||||
// Init initializes the view.
|
||||
func (r *Reference) Init(ctx context.Context) error {
|
||||
if err := r.ResourceViewer.Init(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
r.GetTable().GetModel().SetNamespace(client.AllNamespaces)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *Reference) bindKeys(aa ui.KeyActions) {
|
||||
aa.Delete(ui.KeyShiftA, tcell.KeyCtrlS, tcell.KeyCtrlSpace, ui.KeySpace)
|
||||
aa.Delete(tcell.KeyCtrlW, tcell.KeyCtrlL, tcell.KeyCtrlZ)
|
||||
aa.Add(ui.KeyActions{
|
||||
tcell.KeyEnter: ui.NewKeyAction("Goto", r.gotoCmd, true),
|
||||
ui.KeyShiftV: ui.NewKeyAction("Sort GVR", r.GetTable().SortColCmd("GVR", true), false),
|
||||
})
|
||||
}
|
||||
|
||||
func (r *Reference) gotoCmd(evt *tcell.EventKey) *tcell.EventKey {
|
||||
row, _ := r.GetTable().GetSelection()
|
||||
if row == 0 {
|
||||
return evt
|
||||
}
|
||||
|
||||
path := r.GetTable().GetSelectedItem()
|
||||
gvr := ui.TrimCell(r.GetTable().SelectTable, row, 2)
|
||||
|
||||
if err := r.App().gotoResource(client.NewGVR(gvr).R(), path, false); err != nil {
|
||||
r.App().Flash().Err(err)
|
||||
}
|
||||
|
||||
return evt
|
||||
}
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
package view_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/derailed/k9s/internal/client"
|
||||
"github.com/derailed/k9s/internal/view"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestReferenceNew(t *testing.T) {
|
||||
s := view.NewReference(client.NewGVR("references"))
|
||||
|
||||
assert.Nil(t, s.Init(makeCtx()))
|
||||
assert.Equal(t, "References", s.Name())
|
||||
assert.Equal(t, 3, len(s.Hints()))
|
||||
}
|
||||
|
|
@ -45,6 +45,12 @@ func coreViewers(vv MetaViewers) {
|
|||
vv[client.NewGVR("v1/secrets")] = MetaViewer{
|
||||
viewerFn: NewSecret,
|
||||
}
|
||||
vv[client.NewGVR("v1/configmaps")] = MetaViewer{
|
||||
viewerFn: NewConfigMap,
|
||||
}
|
||||
vv[client.NewGVR("v1/serviceaccounts")] = MetaViewer{
|
||||
viewerFn: NewServiceAccount,
|
||||
}
|
||||
vv[client.NewGVR("v1/persistentvolumeclaims")] = MetaViewer{
|
||||
viewerFn: NewPersistentVolumeClaim,
|
||||
}
|
||||
|
|
@ -72,6 +78,9 @@ func miscViewers(vv MetaViewers) {
|
|||
vv[client.NewGVR("aliases")] = MetaViewer{
|
||||
viewerFn: NewAlias,
|
||||
}
|
||||
vv[client.NewGVR("references")] = MetaViewer{
|
||||
viewerFn: NewReference,
|
||||
}
|
||||
vv[client.NewGVR("pulses")] = MetaViewer{
|
||||
viewerFn: NewPulse,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,62 @@
|
|||
package view
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/derailed/k9s/internal/client"
|
||||
"github.com/derailed/k9s/internal/dao"
|
||||
"github.com/derailed/k9s/internal/ui"
|
||||
"github.com/gdamore/tcell"
|
||||
)
|
||||
|
||||
// ServiceAccount represents a serviceaccount viewer.
|
||||
type ServiceAccount struct {
|
||||
ResourceViewer
|
||||
}
|
||||
|
||||
// NewServiceAccount returns a new viewer.
|
||||
func NewServiceAccount(gvr client.GVR) ResourceViewer {
|
||||
s := ServiceAccount{
|
||||
ResourceViewer: NewBrowser(gvr),
|
||||
}
|
||||
s.SetBindKeysFn(s.bindKeys)
|
||||
|
||||
return &s
|
||||
}
|
||||
|
||||
func (s *ServiceAccount) bindKeys(aa ui.KeyActions) {
|
||||
aa.Add(ui.KeyActions{
|
||||
ui.KeyR: ui.NewKeyAction("Referenced by", s.refCmd, true),
|
||||
})
|
||||
}
|
||||
|
||||
func (s *ServiceAccount) refCmd(evt *tcell.EventKey) *tcell.EventKey {
|
||||
return scanSARefs(evt, s.App(), s.GetTable(), "v1/serviceaccounts")
|
||||
}
|
||||
|
||||
func scanSARefs(evt *tcell.EventKey, a *App, t *Table, gvr string) *tcell.EventKey {
|
||||
path := t.GetSelectedItem()
|
||||
if path == "" {
|
||||
return evt
|
||||
}
|
||||
|
||||
ctx := context.Background()
|
||||
refs, err := dao.ScanForSARefs(refContext(gvr, path, true)(ctx), a.factory)
|
||||
if err != nil {
|
||||
a.Flash().Err(err)
|
||||
return nil
|
||||
}
|
||||
if len(refs) == 0 {
|
||||
a.Flash().Warnf("No references found at this time for %s::%s. Check again later!", gvr, path)
|
||||
return nil
|
||||
} else {
|
||||
a.Flash().Infof("Viewing references for %s::%s", gvr, path)
|
||||
}
|
||||
view := NewReference(client.NewGVR("references"))
|
||||
view.SetContextFn(refContext(gvr, path, false))
|
||||
if err := a.inject(view); err != nil {
|
||||
a.Flash().Err(err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
@ -29,10 +29,15 @@ func NewSecret(gvr client.GVR) ResourceViewer {
|
|||
|
||||
func (s *Secret) bindKeys(aa ui.KeyActions) {
|
||||
aa.Add(ui.KeyActions{
|
||||
tcell.KeyCtrlX: ui.NewKeyAction("Decode", s.decodeCmd, true),
|
||||
ui.KeyX: ui.NewKeyAction("Decode", s.decodeCmd, true),
|
||||
ui.KeyR: ui.NewKeyAction("Referenced by", s.refCmd, true),
|
||||
})
|
||||
}
|
||||
|
||||
func (s *Secret) refCmd(evt *tcell.EventKey) *tcell.EventKey {
|
||||
return scanRefs(evt, s.App(), s.GetTable(), "v1/secrets")
|
||||
}
|
||||
|
||||
func (s *Secret) decodeCmd(evt *tcell.EventKey) *tcell.EventKey {
|
||||
path := s.GetTable().GetSelectedItem()
|
||||
if path == "" {
|
||||
|
|
|
|||
|
|
@ -13,5 +13,5 @@ func TestSecretNew(t *testing.T) {
|
|||
|
||||
assert.Nil(t, s.Init(makeCtx()))
|
||||
assert.Equal(t, "Secrets", s.Name())
|
||||
assert.Equal(t, 5, len(s.Hints()))
|
||||
assert.Equal(t, 6, len(s.Hints()))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -43,7 +43,23 @@ func init() {
|
|||
Verbs: []string{"get", "list", "watch", "delete"},
|
||||
Categories: []string{"k9s"},
|
||||
})
|
||||
dao.MetaAccess.RegisterMeta("v1/configmaps", metav1.APIResource{
|
||||
Name: "configmaps",
|
||||
SingularName: "configmap",
|
||||
Namespaced: true,
|
||||
Kind: "ConfigMaps",
|
||||
Verbs: []string{"get", "list", "watch", "delete"},
|
||||
Categories: []string{"k9s"},
|
||||
})
|
||||
|
||||
dao.MetaAccess.RegisterMeta("references", metav1.APIResource{
|
||||
Name: "references",
|
||||
SingularName: "reference",
|
||||
Namespaced: true,
|
||||
Kind: "References",
|
||||
Verbs: []string{"get", "list", "watch", "delete"},
|
||||
Categories: []string{"k9s"},
|
||||
})
|
||||
dao.MetaAccess.RegisterMeta("aliases", metav1.APIResource{
|
||||
Name: "aliases",
|
||||
SingularName: "alias",
|
||||
|
|
|
|||
|
|
@ -70,19 +70,38 @@ func (f *Factory) List(gvr, ns string, wait bool, labels labels.Selector) ([]run
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if wait {
|
||||
f.waitForCacheSync(ns)
|
||||
}
|
||||
if client.IsClusterScoped(ns) {
|
||||
return inf.Lister().List(labels)
|
||||
}
|
||||
|
||||
log.Debug().Msgf("LIST %q::%q -- %t::%t", gvr, ns, wait, inf.Informer().HasSynced())
|
||||
if client.IsAllNamespace(ns) {
|
||||
ns = client.AllNamespaces
|
||||
}
|
||||
|
||||
var oo []runtime.Object
|
||||
if client.IsClusterScoped(ns) {
|
||||
oo, err = inf.Lister().List(labels)
|
||||
} else {
|
||||
oo, err = inf.Lister().ByNamespace(ns).List(labels)
|
||||
}
|
||||
if !wait || (wait && inf.Informer().HasSynced()) {
|
||||
return oo, err
|
||||
}
|
||||
|
||||
f.waitForCacheSync(ns)
|
||||
if client.IsClusterScoped(ns) {
|
||||
return inf.Lister().List(labels)
|
||||
}
|
||||
return inf.Lister().ByNamespace(ns).List(labels)
|
||||
}
|
||||
|
||||
// HasSynced checks if given informer is up to date.
|
||||
func (f *Factory) HasSynced(gvr, ns string) (bool, error) {
|
||||
inf, err := f.CanForResource(ns, gvr, client.MonitorAccess)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
return inf.Informer().HasSynced(), nil
|
||||
}
|
||||
|
||||
// Get retrieves a given resource.
|
||||
func (f *Factory) Get(gvr, path string, wait bool, sel labels.Selector) (runtime.Object, error) {
|
||||
ns, n := namespaced(path)
|
||||
|
|
@ -90,14 +109,21 @@ func (f *Factory) Get(gvr, path string, wait bool, sel labels.Selector) (runtime
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if wait {
|
||||
f.waitForCacheSync(ns)
|
||||
log.Debug().Msgf("GET %q::%q -- %t::%t", gvr, path, wait, inf.Informer().HasSynced())
|
||||
var o runtime.Object
|
||||
if client.IsClusterScoped(ns) {
|
||||
o, err = inf.Lister().Get(n)
|
||||
} else {
|
||||
o, err = inf.Lister().ByNamespace(ns).Get(n)
|
||||
}
|
||||
if !wait || (wait && inf.Informer().HasSynced()) {
|
||||
return o, err
|
||||
}
|
||||
|
||||
f.waitForCacheSync(ns)
|
||||
if client.IsClusterScoped(ns) {
|
||||
return inf.Lister().Get(n)
|
||||
}
|
||||
|
||||
return inf.Lister().ByNamespace(ns).Get(n)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -89,7 +89,7 @@ func addRef(f dao.Factory, parent *TreeNode, gvr, id string, optional *bool) {
|
|||
}
|
||||
|
||||
func validate(f dao.Factory, n *TreeNode, optional *bool) {
|
||||
res, err := f.Get(n.GVR, n.ID, false, labels.Everything())
|
||||
res, err := f.Get(n.GVR, n.ID, true, labels.Everything())
|
||||
if err != nil || res == nil {
|
||||
if optional == nil || !*optional {
|
||||
log.Warn().Err(err).Msgf("Missing ref %q::%q", n.GVR, n.ID)
|
||||
|
|
|
|||
|
|
@ -103,7 +103,7 @@ func (*Pod) serviceAccountRef(ctx context.Context, f dao.Factory, parent *TreeNo
|
|||
}
|
||||
|
||||
id := client.FQN(ns, spec.ServiceAccountName)
|
||||
o, err := f.Get("v1/serviceaccounts", id, false, labels.Everything())
|
||||
o, err := f.Get("v1/serviceaccounts", id, true, labels.Everything())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue