// SPDX-License-Identifier: Apache-2.0 // Copyright Authors of K9s package dao import ( "context" "errors" "fmt" "log/slog" "github.com/derailed/k9s/internal" "github.com/derailed/k9s/internal/client" "github.com/derailed/k9s/internal/render" "github.com/derailed/k9s/internal/slogs" batchv1 "k8s.io/api/batch/v1" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/labels" "k8s.io/apimachinery/pkg/runtime" ) var ( _ Accessor = (*Job)(nil) _ Nuker = (*Job)(nil) _ Loggable = (*Job)(nil) _ ImageLister = (*Deployment)(nil) ) // Job represents a K8s job resource. type Job struct { Resource } // ListImages lists container images. func (j *Job) ListImages(_ context.Context, fqn string) ([]string, error) { job, err := j.GetInstance(fqn) if err != nil { return nil, err } return render.ExtractImages(&job.Spec.Template.Spec), nil } // List returns a collection of resources. func (j *Job) List(ctx context.Context, ns string) ([]runtime.Object, error) { oo, err := j.Resource.List(ctx, ns) if err != nil { return nil, err } ctrl, _ := ctx.Value(internal.KeyPath).(string) _, n := client.Namespaced(ctrl) ll := make([]runtime.Object, 0, 10) for _, o := range oo { var j batchv1.Job err = runtime.DefaultUnstructuredConverter.FromUnstructured(o.(*unstructured.Unstructured).Object, &j) if err != nil { return nil, errors.New("expecting Job resource") } if n == "" { ll = append(ll, o) continue } for _, r := range j.OwnerReferences { if r.Name == n { ll = append(ll, o) } } } return ll, nil } // TailLogs tail logs for all pods represented by this Job. func (j *Job) TailLogs(ctx context.Context, opts *LogOptions) ([]LogChan, error) { o, err := j.getFactory().Get(j.gvr, opts.Path, true, labels.Everything()) if err != nil { return nil, err } var job batchv1.Job err = runtime.DefaultUnstructuredConverter.FromUnstructured(o.(*unstructured.Unstructured).Object, &job) if err != nil { return nil, errors.New("expecting a job resource") } if job.Spec.Selector == nil || len(job.Spec.Selector.MatchLabels) == 0 { return nil, fmt.Errorf("no valid selector found for job: %s", opts.Path) } return podLogs(ctx, job.Spec.Selector.MatchLabels, opts) } func (j *Job) GetInstance(fqn string) (*batchv1.Job, error) { o, err := j.getFactory().Get(j.gvr, fqn, true, labels.Everything()) if err != nil { return nil, err } var job batchv1.Job err = runtime.DefaultUnstructuredConverter.FromUnstructured(o.(*unstructured.Unstructured).Object, &job) if err != nil { return nil, errors.New("expecting a job resource") } return &job, nil } // ScanSA scans for serviceaccount refs. func (j *Job) ScanSA(_ context.Context, fqn string, wait bool) (Refs, error) { ns, n := client.Namespaced(fqn) oo, err := j.getFactory().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 serviceAccountMatches(job.Spec.Template.Spec.ServiceAccountName, n) { refs = append(refs, Ref{ GVR: j.GVR(), FQN: client.FQN(job.Namespace, job.Name), }) } } return refs, nil } // Scan scans for resource references. func (j *Job) Scan(_ context.Context, gvr *client.GVR, fqn string, wait bool) (Refs, error) { ns, n := client.Namespaced(fqn) oo, err := j.getFactory().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 client.CmGVR: if !hasConfigMap(&job.Spec.Template.Spec, n) { continue } refs = append(refs, Ref{ GVR: j.GVR(), FQN: client.FQN(job.Namespace, job.Name), }) case client.SecGVR: found, err := hasSecret(j.Factory, &job.Spec.Template.Spec, job.Namespace, n, wait) if err != nil { slog.Warn("Locate secret failed", slogs.FQN, fqn, slogs.Error, err, ) continue } if !found { continue } refs = append(refs, Ref{ GVR: j.GVR(), FQN: client.FQN(job.Namespace, job.Name), }) case client.PcGVR: if !hasPC(&job.Spec.Template.Spec, n) { continue } refs = append(refs, Ref{ GVR: j.GVR(), FQN: client.FQN(job.Namespace, job.Name), }) } } return refs, nil }