intellij-kubernetes
intellij-kubernetes copied to clipboard
Describe: Impl resource resolution for env vars
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)
}