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.
parent 0abda6bd
...@@ -878,7 +878,8 @@ type EnvVar struct { ...@@ -878,7 +878,8 @@ type EnvVar struct {
// EnvVarSource represents a source for the value of an EnvVar. // EnvVarSource represents a source for the value of an EnvVar.
// Only one of its fields may be set. // Only one of its fields may be set.
type EnvVarSource struct { 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"` FieldRef *ObjectFieldSelector `json:"fieldRef,omitempty"`
// Selects a resource of the container: only resources limits and requests // Selects a resource of the container: only resources limits and requests
// (limits.cpu, limits.memory, requests.cpu and requests.memory) are currently supported. // (limits.cpu, limits.memory, requests.cpu and requests.memory) are currently supported.
......
...@@ -192,14 +192,15 @@ func addConversionFuncs(scheme *runtime.Scheme) error { ...@@ -192,14 +192,15 @@ func addConversionFuncs(scheme *runtime.Scheme) error {
err = scheme.AddFieldLabelConversionFunc("v1", "Pod", err = scheme.AddFieldLabelConversionFunc("v1", "Pod",
func(label, value string) (string, string, error) { func(label, value string) (string, string, error) {
switch label { switch label {
case "metadata.name", case "metadata.annotations",
"metadata.namespace",
"metadata.labels", "metadata.labels",
"metadata.annotations", "metadata.name",
"status.phase", "metadata.namespace",
"status.podIP",
"spec.nodeName", "spec.nodeName",
"spec.restartPolicy": "spec.restartPolicy",
"spec.serviceAccountName",
"status.phase",
"status.podIP":
return label, value, nil return label, value, nil
// This is for backwards compatibility with old v1 clients which send spec.host // This is for backwards compatibility with old v1 clients which send spec.host
case "spec.host": case "spec.host":
......
...@@ -990,7 +990,8 @@ type EnvVar struct { ...@@ -990,7 +990,8 @@ type EnvVar struct {
// EnvVarSource represents a source for the value of an EnvVar. // EnvVarSource represents a source for the value of an EnvVar.
type EnvVarSource struct { 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"` FieldRef *ObjectFieldSelector `json:"fieldRef,omitempty" protobuf:"bytes,1,opt,name=fieldRef"`
// Selects a resource of the container: only resources limits and requests // Selects a resource of the container: only resources limits and requests
// (limits.cpu, limits.memory, requests.cpu and requests.memory) are currently supported. // (limits.cpu, limits.memory, requests.cpu and requests.memory) are currently supported.
......
...@@ -424,7 +424,7 @@ func (EnvVar) SwaggerDoc() map[string]string { ...@@ -424,7 +424,7 @@ func (EnvVar) SwaggerDoc() map[string]string {
var map_EnvVarSource = map[string]string{ var map_EnvVarSource = map[string]string{
"": "EnvVarSource represents a source for the value of an EnvVar.", "": "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.", "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.", "configMapKeyRef": "Selects a key of a ConfigMap.",
"secretKeyRef": "Selects a key of a secret in the pod's namespace", "secretKeyRef": "Selects a key of a secret in the pod's namespace",
......
...@@ -1208,7 +1208,7 @@ func validateEnv(vars []api.EnvVar, fldPath *field.Path) field.ErrorList { ...@@ -1208,7 +1208,7 @@ func validateEnv(vars []api.EnvVar, fldPath *field.Path) field.ErrorList {
return allErrs 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") var validContainerResourceFieldPathExpressions = sets.NewString("limits.cpu", "limits.memory", "requests.cpu", "requests.memory")
func validateEnvVarValueFrom(ev api.EnvVar, fldPath *field.Path) field.ErrorList { func validateEnvVarValueFrom(ev api.EnvVar, fldPath *field.Path) field.ErrorList {
......
...@@ -2215,6 +2215,24 @@ func TestValidateEnv(t *testing.T) { ...@@ -2215,6 +2215,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", Name: "secret_value",
ValueFrom: &api.EnvVarSource{ ValueFrom: &api.EnvVarSource{
SecretKeyRef: &api.SecretKeySelector{ SecretKeyRef: &api.SecretKeySelector{
...@@ -2381,7 +2399,7 @@ func TestValidateEnv(t *testing.T) { ...@@ -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", name: "invalid fieldPath annotations",
...@@ -2394,7 +2412,7 @@ func TestValidateEnv(t *testing.T) { ...@@ -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", name: "unsupported fieldPath",
...@@ -2407,7 +2425,7 @@ func TestValidateEnv(t *testing.T) { ...@@ -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 { for _, tc := range errorCases {
......
...@@ -1440,6 +1440,10 @@ func (kl *Kubelet) podFieldSelectorRuntimeValue(fs *api.ObjectFieldSelector, pod ...@@ -1440,6 +1440,10 @@ func (kl *Kubelet) podFieldSelectorRuntimeValue(fs *api.ObjectFieldSelector, pod
return "", err return "", err
} }
switch internalFieldPath { switch internalFieldPath {
case "spec.nodeName":
return pod.Spec.NodeName, nil
case "spec.serviceAccountName":
return pod.Spec.ServiceAccountName, nil
case "status.podIP": case "status.podIP":
return podIP, nil return podIP, nil
} }
......
...@@ -1380,6 +1380,24 @@ func TestMakeEnvironmentVariables(t *testing.T) { ...@@ -1380,6 +1380,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", Name: "POD_IP",
ValueFrom: &api.EnvVarSource{ ValueFrom: &api.EnvVarSource{
FieldRef: &api.ObjectFieldSelector{ FieldRef: &api.ObjectFieldSelector{
...@@ -1395,6 +1413,8 @@ func TestMakeEnvironmentVariables(t *testing.T) { ...@@ -1395,6 +1413,8 @@ func TestMakeEnvironmentVariables(t *testing.T) {
expectedEnvs: []kubecontainer.EnvVar{ expectedEnvs: []kubecontainer.EnvVar{
{Name: "POD_NAME", Value: "dapi-test-pod-name"}, {Name: "POD_NAME", Value: "dapi-test-pod-name"},
{Name: "POD_NAMESPACE", Value: "downward-api"}, {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"}, {Name: "POD_IP", Value: "1.2.3.4"},
}, },
}, },
...@@ -1546,6 +1566,10 @@ func TestMakeEnvironmentVariables(t *testing.T) { ...@@ -1546,6 +1566,10 @@ func TestMakeEnvironmentVariables(t *testing.T) {
Namespace: tc.ns, Namespace: tc.ns,
Name: "dapi-test-pod-name", Name: "dapi-test-pod-name",
}, },
Spec: api.PodSpec{
ServiceAccountName: "special",
NodeName: "node-name",
},
} }
podIP := "1.2.3.4" podIP := "1.2.3.4"
......
...@@ -215,6 +215,7 @@ func (d *downwardAPIVolume) collectData(defaultMode *int32) (map[string]volumeut ...@@ -215,6 +215,7 @@ func (d *downwardAPIVolume) collectData(defaultMode *int32) (map[string]volumeut
fileProjection.Mode = *defaultMode fileProjection.Mode = *defaultMode
} }
if fileInfo.FieldRef != nil { if fileInfo.FieldRef != nil {
// TODO: unify with Kubelet.podFieldSelectorRuntimeValue
if values, err := fieldpath.ExtractFieldPathAsString(d.pod, fileInfo.FieldRef.FieldPath); err != nil { 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()) glog.Errorf("Unable to extract field %s: %s", fileInfo.FieldRef.FieldPath, err.Error())
errlist = append(errlist, err) errlist = append(errlist, err)
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment