intellij-kubernetes icon indicating copy to clipboard operation
intellij-kubernetes copied to clipboard

Describe: Impl resource resolution for env vars

Open adietish opened this issue 1 year ago • 0 comments

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)
}

adietish avatar Jul 19 '24 18:07 adietish