k9s/internal/dao/helpers.go

130 lines
3.1 KiB
Go

// SPDX-License-Identifier: Apache-2.0
// Copyright Authors of K9s
package dao
import (
"bytes"
"errors"
"fmt"
"log/slog"
"math"
"github.com/derailed/k9s/internal/client"
"github.com/derailed/k9s/internal/slogs"
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/runtime"
"k8s.io/cli-runtime/pkg/printers"
)
const (
defaultServiceAccount = "default"
// DefaultContainerAnnotation represents the annotation key for the default container.
DefaultContainerAnnotation = "kubectl.kubernetes.io/default-container"
)
// GetDefaultContainer returns a container name if specified in an annotation.
func GetDefaultContainer(m *metav1.ObjectMeta, spec *v1.PodSpec) (string, bool) {
defaultContainer, ok := m.Annotations[DefaultContainerAnnotation]
if !ok {
return "", false
}
for i := range spec.Containers {
if spec.Containers[i].Name == defaultContainer {
return defaultContainer, true
}
}
slog.Warn("Container not found. Annotation ignored",
slogs.Container, defaultContainer,
slogs.Annotation, DefaultContainerAnnotation,
)
return "", false
}
func extractFQN(o runtime.Object) string {
u, ok := o.(*unstructured.Unstructured)
if !ok {
slog.Error("Expecting unstructured", slogs.ResType, fmt.Sprintf("%T", o))
return client.NA
}
return FQN(u.GetNamespace(), u.GetName())
}
// FQN returns a fully qualified resource name.
func FQN(ns, n string) string {
if ns == "" {
return n
}
return ns + "/" + n
}
func inList(ll []string, s string) bool {
for _, l := range ll {
if l == s {
return true
}
}
return false
}
func toPerc(v, dv float64) float64 {
if dv == 0 {
return 0
}
return math.Round((v / dv) * 100)
}
// ToYAML converts a resource to its YAML representation.
func ToYAML(o runtime.Object, showManaged bool) (string, error) {
if o == nil {
return "", errors.New("no object to yamlize")
}
var p printers.ResourcePrinter = &printers.YAMLPrinter{}
if !showManaged {
o = o.DeepCopyObject()
p = &printers.OmitManagedFieldsPrinter{Delegate: p}
}
var buff bytes.Buffer
if err := p.PrintObj(o, &buff); err != nil {
slog.Error("PrintObj failed", slogs.Error, err)
return "", err
}
return buff.String(), nil
}
// serviceAccountMatches validates that the ServiceAccount referenced in the PodSpec matches the incoming
// ServiceAccount. If the PodSpec ServiceAccount is blank kubernetes will use the "default" ServiceAccount
// when deploying the pod, so if the incoming SA is "default" and podSA is an empty string that is also a match.
func serviceAccountMatches(podSA, saName string) bool {
if podSA == "" {
podSA = defaultServiceAccount
}
return podSA == saName
}
// ContinuousRanges takes a sorted slice of integers and returns a slice of
// sub-slices representing continuous ranges of integers.
func ContinuousRanges(indexes []int) [][]int {
var ranges [][]int
for i, p := 1, 0; i <= len(indexes); i++ {
if i == len(indexes) || indexes[i]-indexes[p] != i-p {
ranges = append(ranges, []int{indexes[p], indexes[i-1] + 1})
p = i
}
}
return ranges
}