From e1ebde9f9279cd5cd8a63587bcebc45f8b913f9d Mon Sep 17 00:00:00 2001 From: Clayton Coleman Date: Wed, 22 Jun 2016 12:27:17 -0400 Subject: [PATCH] Add spec.nodeName and spec.serviceAccountName to downward env var The serviceAccountName is occasionally useful for clients running on Kube that need to know who they are when talking to other components. The nodeName is useful for PetSet or DaemonSet pods that need to make calls back to the API to fetch info about their node. Both fields are immutable, and cannot easily be retrieved in another way. --- pkg/api/types.go | 3 ++- pkg/api/v1/conversion.go | 13 ++++++------ pkg/api/v1/types.go | 3 ++- pkg/api/v1/types_swagger_doc_generated.go | 2 +- pkg/api/validation/validation.go | 2 +- pkg/api/validation/validation_test.go | 24 ++++++++++++++++++++--- pkg/kubelet/kubelet.go | 4 ++++ pkg/kubelet/kubelet_test.go | 24 +++++++++++++++++++++++ pkg/volume/downwardapi/downwardapi.go | 1 + 9 files changed, 63 insertions(+), 13 deletions(-) diff --git a/pkg/api/types.go b/pkg/api/types.go index 3c01ca773be..62606aef6c2 100644 --- a/pkg/api/types.go +++ b/pkg/api/types.go @@ -878,7 +878,8 @@ type EnvVar struct { // EnvVarSource represents a source for the value of an EnvVar. // Only one of its fields may be set. type EnvVarSource struct { - // Selects a field of the pod; only name and namespace are supported. + // Selects a field of the pod: supports metadata.name, metadata.namespace, metadata.labels, metadata.annotations, + // spec.nodeName, spec.serviceAccountName, status.podIP. FieldRef *ObjectFieldSelector `json:"fieldRef,omitempty"` // Selects a resource of the container: only resources limits and requests // (limits.cpu, limits.memory, requests.cpu and requests.memory) are currently supported. diff --git a/pkg/api/v1/conversion.go b/pkg/api/v1/conversion.go index d2cb2dc7acb..7e5a7cff6bf 100644 --- a/pkg/api/v1/conversion.go +++ b/pkg/api/v1/conversion.go @@ -192,14 +192,15 @@ func addConversionFuncs(scheme *runtime.Scheme) error { err = scheme.AddFieldLabelConversionFunc("v1", "Pod", func(label, value string) (string, string, error) { switch label { - case "metadata.name", - "metadata.namespace", + case "metadata.annotations", "metadata.labels", - "metadata.annotations", - "status.phase", - "status.podIP", + "metadata.name", + "metadata.namespace", "spec.nodeName", - "spec.restartPolicy": + "spec.restartPolicy", + "spec.serviceAccountName", + "status.phase", + "status.podIP": return label, value, nil // This is for backwards compatibility with old v1 clients which send spec.host case "spec.host": diff --git a/pkg/api/v1/types.go b/pkg/api/v1/types.go index b7721211b84..6d5de4c5f74 100644 --- a/pkg/api/v1/types.go +++ b/pkg/api/v1/types.go @@ -990,7 +990,8 @@ type EnvVar struct { // EnvVarSource represents a source for the value of an EnvVar. type EnvVarSource struct { - // Selects a field of the pod; only name and namespace are supported. + // Selects a field of the pod: supports metadata.name, metadata.namespace, metadata.labels, metadata.annotations, + // spec.nodeName, spec.serviceAccountName, status.podIP. FieldRef *ObjectFieldSelector `json:"fieldRef,omitempty" protobuf:"bytes,1,opt,name=fieldRef"` // Selects a resource of the container: only resources limits and requests // (limits.cpu, limits.memory, requests.cpu and requests.memory) are currently supported. diff --git a/pkg/api/v1/types_swagger_doc_generated.go b/pkg/api/v1/types_swagger_doc_generated.go index d1023eb4acc..6b4db441fce 100644 --- a/pkg/api/v1/types_swagger_doc_generated.go +++ b/pkg/api/v1/types_swagger_doc_generated.go @@ -424,7 +424,7 @@ func (EnvVar) SwaggerDoc() map[string]string { var map_EnvVarSource = map[string]string{ "": "EnvVarSource represents a source for the value of an EnvVar.", - "fieldRef": "Selects a field of the pod; only name and namespace are supported.", + "fieldRef": "Selects a field of the pod: supports metadata.name, metadata.namespace, metadata.labels, metadata.annotations, spec.nodeName, spec.serviceAccountName, status.podIP.", "resourceFieldRef": "Selects a resource of the container: only resources limits and requests (limits.cpu, limits.memory, requests.cpu and requests.memory) are currently supported.", "configMapKeyRef": "Selects a key of a ConfigMap.", "secretKeyRef": "Selects a key of a secret in the pod's namespace", diff --git a/pkg/api/validation/validation.go b/pkg/api/validation/validation.go index f44b67dd229..13722e09a4d 100644 --- a/pkg/api/validation/validation.go +++ b/pkg/api/validation/validation.go @@ -1208,7 +1208,7 @@ func validateEnv(vars []api.EnvVar, fldPath *field.Path) field.ErrorList { return allErrs } -var validFieldPathExpressionsEnv = sets.NewString("metadata.name", "metadata.namespace", "status.podIP") +var validFieldPathExpressionsEnv = sets.NewString("metadata.name", "metadata.namespace", "spec.nodeName", "spec.serviceAccountName", "status.podIP") var validContainerResourceFieldPathExpressions = sets.NewString("limits.cpu", "limits.memory", "requests.cpu", "requests.memory") func validateEnvVarValueFrom(ev api.EnvVar, fldPath *field.Path) field.ErrorList { diff --git a/pkg/api/validation/validation_test.go b/pkg/api/validation/validation_test.go index b55447fc8d2..bf9cfd2bebd 100644 --- a/pkg/api/validation/validation_test.go +++ b/pkg/api/validation/validation_test.go @@ -2214,6 +2214,24 @@ func TestValidateEnv(t *testing.T) { }, }, }, + { + Name: "abc", + ValueFrom: &api.EnvVarSource{ + FieldRef: &api.ObjectFieldSelector{ + APIVersion: testapi.Default.GroupVersion().String(), + FieldPath: "spec.nodeName", + }, + }, + }, + { + Name: "abc", + ValueFrom: &api.EnvVarSource{ + FieldRef: &api.ObjectFieldSelector{ + APIVersion: testapi.Default.GroupVersion().String(), + FieldPath: "spec.serviceAccountName", + }, + }, + }, { Name: "secret_value", ValueFrom: &api.EnvVarSource{ @@ -2381,7 +2399,7 @@ func TestValidateEnv(t *testing.T) { }, }, }}, - expectedError: `[0].valueFrom.fieldRef.fieldPath: Unsupported value: "metadata.labels": supported values: metadata.name, metadata.namespace, status.podIP`, + expectedError: `[0].valueFrom.fieldRef.fieldPath: Unsupported value: "metadata.labels": supported values: metadata.name, metadata.namespace, spec.nodeName, spec.serviceAccountName, status.podIP`, }, { name: "invalid fieldPath annotations", @@ -2394,7 +2412,7 @@ func TestValidateEnv(t *testing.T) { }, }, }}, - expectedError: `[0].valueFrom.fieldRef.fieldPath: Unsupported value: "metadata.annotations": supported values: metadata.name, metadata.namespace, status.podIP`, + expectedError: `[0].valueFrom.fieldRef.fieldPath: Unsupported value: "metadata.annotations": supported values: metadata.name, metadata.namespace, spec.nodeName, spec.serviceAccountName, status.podIP`, }, { name: "unsupported fieldPath", @@ -2407,7 +2425,7 @@ func TestValidateEnv(t *testing.T) { }, }, }}, - expectedError: `valueFrom.fieldRef.fieldPath: Unsupported value: "status.phase": supported values: metadata.name, metadata.namespace, status.podIP`, + expectedError: `valueFrom.fieldRef.fieldPath: Unsupported value: "status.phase": supported values: metadata.name, metadata.namespace, spec.nodeName, spec.serviceAccountName, status.podIP`, }, } for _, tc := range errorCases { diff --git a/pkg/kubelet/kubelet.go b/pkg/kubelet/kubelet.go index 470e67413a0..3ac93a5dfb7 100644 --- a/pkg/kubelet/kubelet.go +++ b/pkg/kubelet/kubelet.go @@ -1440,6 +1440,10 @@ func (kl *Kubelet) podFieldSelectorRuntimeValue(fs *api.ObjectFieldSelector, pod return "", err } switch internalFieldPath { + case "spec.nodeName": + return pod.Spec.NodeName, nil + case "spec.serviceAccountName": + return pod.Spec.ServiceAccountName, nil case "status.podIP": return podIP, nil } diff --git a/pkg/kubelet/kubelet_test.go b/pkg/kubelet/kubelet_test.go index 4e95769045b..12392b91dae 100644 --- a/pkg/kubelet/kubelet_test.go +++ b/pkg/kubelet/kubelet_test.go @@ -1379,6 +1379,24 @@ func TestMakeEnvironmentVariables(t *testing.T) { }, }, }, + { + Name: "POD_NODE_NAME", + ValueFrom: &api.EnvVarSource{ + FieldRef: &api.ObjectFieldSelector{ + APIVersion: testapi.Default.GroupVersion().String(), + FieldPath: "spec.nodeName", + }, + }, + }, + { + Name: "POD_SERVICE_ACCOUNT_NAME", + ValueFrom: &api.EnvVarSource{ + FieldRef: &api.ObjectFieldSelector{ + APIVersion: testapi.Default.GroupVersion().String(), + FieldPath: "spec.serviceAccountName", + }, + }, + }, { Name: "POD_IP", ValueFrom: &api.EnvVarSource{ @@ -1395,6 +1413,8 @@ func TestMakeEnvironmentVariables(t *testing.T) { expectedEnvs: []kubecontainer.EnvVar{ {Name: "POD_NAME", Value: "dapi-test-pod-name"}, {Name: "POD_NAMESPACE", Value: "downward-api"}, + {Name: "POD_NODE_NAME", Value: "node-name"}, + {Name: "POD_SERVICE_ACCOUNT_NAME", Value: "special"}, {Name: "POD_IP", Value: "1.2.3.4"}, }, }, @@ -1546,6 +1566,10 @@ func TestMakeEnvironmentVariables(t *testing.T) { Namespace: tc.ns, Name: "dapi-test-pod-name", }, + Spec: api.PodSpec{ + ServiceAccountName: "special", + NodeName: "node-name", + }, } podIP := "1.2.3.4" diff --git a/pkg/volume/downwardapi/downwardapi.go b/pkg/volume/downwardapi/downwardapi.go index 679c73114c3..4a640fdcc6a 100644 --- a/pkg/volume/downwardapi/downwardapi.go +++ b/pkg/volume/downwardapi/downwardapi.go @@ -215,6 +215,7 @@ func (d *downwardAPIVolume) collectData(defaultMode *int32) (map[string]volumeut fileProjection.Mode = *defaultMode } if fileInfo.FieldRef != nil { + // TODO: unify with Kubelet.podFieldSelectorRuntimeValue if values, err := fieldpath.ExtractFieldPathAsString(d.pod, fileInfo.FieldRef.FieldPath); err != nil { glog.Errorf("Unable to extract field %s: %s", fileInfo.FieldRef.FieldPath, err.Error()) errlist = append(errlist, err)