redhat-developer / intellij-kubernetes

IntelliJ Kubernetes plugin
https://plugins.jetbrains.com/plugin/15921-kubernetes-by-red-hat
Eclipse Public License 2.0
20 stars 20 forks source link

Describe: Impl resource resolution for env vars #774

Open adietish opened 4 months ago

adietish commented 4 months ago

related to #553

Env variables in containers (and other resources) can be referenced by expressions.

example: In Pod > spec > containers > [mycontainer] > env (and other places):

env:
   - name: MY_NODE_NAME
     valueFrom:
         fieldRef:
             fieldPath: spec.nodeName

Those expressions need to be resolved and their value displayed. kubectl describe would print the resolved value and the expression (in parenthesis):

    Environment:
      MY_POD_NAME:             describe-test (v1:metadata.name)

Kubectl does it using the function resolverFn in the following ways:

1. EnvVarResolverFunc

https://github.com/kubernetes/kubectl/blob/b4d17b87f57c2be12098c13a8510466c1dfd04d1/pkg/describe/describe.go#L1976

case e.ValueFrom.FieldRef != nil:
    var valueFrom string
    if resolverFn != nil {
        valueFrom = resolverFn(e)
    }
    w.Write(LEVEL_3, "%s:\t%s (%s:%s)\n", e.Name, valueFrom, e.ValueFrom.FieldRef.APIVersion, e.ValueFrom.FieldRef.FieldPath)

resolverFn is defined as follows: https://github.com/kubernetes/kubectl/blob/b4d17b87f57c2be12098c13a8510466c1dfd04d1/pkg/describe/describe.go#L2055

func EnvValueRetriever(pod *corev1.Pod) EnvVarResolverFunc {
    return func(e corev1.EnvVar) string {
        gv, err := schema.ParseGroupVersion(e.ValueFrom.FieldRef.APIVersion)
        if err != nil {
            return ""
        }
        gvk := gv.WithKind("Pod")
        internalFieldPath, _, err := scheme.Scheme.ConvertFieldLabel(gvk, e.ValueFrom.FieldRef.FieldPath, "")
        if err != nil {
            return "" // pod validation should catch this on create
        }

        valueFrom, err := fieldpath.ExtractFieldPathAsString(pod, internalFieldPath)
        if err != nil {
            return "" // pod validation should catch this on create
        }

        return valueFrom
    }
}

2.resourcehelper.ExtractContainerResourceValue

another way to resolve env var expressions is by looking up container resource values: https://github.com/kubernetes/kubectl/blob/b4d17b87f57c2be12098c13a8510466c1dfd04d1/pkg/describe/describe.go#L1980

case e.ValueFrom.ResourceFieldRef != nil:
    valueFrom, err := resourcehelper.ExtractContainerResourceValue(e.ValueFrom.ResourceFieldRef, &container)
    if err != nil {
        valueFrom = ""
    }

Kubectl does this in the following way: https://github.com/kubernetes/kubectl/blob/b4d17b87f57c2be12098c13a8510466c1dfd04d1/pkg/util/resource/resource.go#L190

func ExtractContainerResourceValue(fs *corev1.ResourceFieldSelector, container *corev1.Container) (string, error) {
    divisor := resource.Quantity{}
    if divisor.Cmp(fs.Divisor) == 0 {
        divisor = resource.MustParse("1")
    } else {
        divisor = fs.Divisor
    }

    switch fs.Resource {
    case "limits.cpu":
        return convertResourceCPUToString(container.Resources.Limits.Cpu(), divisor)
    case "limits.memory":
        return convertResourceMemoryToString(container.Resources.Limits.Memory(), divisor)
    case "limits.ephemeral-storage":
        return convertResourceEphemeralStorageToString(container.Resources.Limits.StorageEphemeral(), divisor)
    case "requests.cpu":
        return convertResourceCPUToString(container.Resources.Requests.Cpu(), divisor)
    case "requests.memory":
        return convertResourceMemoryToString(container.Resources.Requests.Memory(), divisor)
    case "requests.ephemeral-storage":
        return convertResourceEphemeralStorageToString(container.Resources.Requests.StorageEphemeral(), divisor)
    }
    // handle extended standard resources with dynamic names
    // example: requests.hugepages-<pageSize> or limits.hugepages-<pageSize>
    if strings.HasPrefix(fs.Resource, "requests.") {
        resourceName := corev1.ResourceName(strings.TrimPrefix(fs.Resource, "requests."))
        if IsHugePageResourceName(resourceName) {
            return convertResourceHugePagesToString(container.Resources.Requests.Name(resourceName, resource.BinarySI), divisor)
        }
    }
    if strings.HasPrefix(fs.Resource, "limits.") {
        resourceName := corev1.ResourceName(strings.TrimPrefix(fs.Resource, "limits."))
        if IsHugePageResourceName(resourceName) {
            return convertResourceHugePagesToString(container.Resources.Limits.Name(resourceName, resource.BinarySI), divisor)
        }
    }
    return "", fmt.Errorf("Unsupported container resource : %v", fs.Resource)
}