Merge pull request #39446 from fraenkel/env_secrets

Automatic merge from submit-queue (batch tested with PRs 39446, 40023, 36853)

Create environment variables from secrets

Allow environment variables to be populated from entire secrets.

**Release note**:
```release-note
Populate environment variables from a secrets.
```
pull/6/head
Kubernetes Submit Queue 2017-01-19 18:53:46 -08:00 committed by GitHub
commit 2d4d2f913f
28 changed files with 2358 additions and 1217 deletions

View File

@ -35499,6 +35499,10 @@
"prefix": {
"description": "An optional identifer to prepend to each key in the ConfigMap. Must be a C_IDENTIFIER.",
"type": "string"
},
"secretRef": {
"description": "The Secret to select from",
"$ref": "#/definitions/v1.SecretEnvSource"
}
}
},
@ -38203,6 +38207,15 @@
}
}
},
"v1.SecretEnvSource": {
"description": "SecretEnvSource selects a Secret to populate the environment variables with.\n\nThe contents of the target Secret's Data field will represent the key-value pairs as environment variables.",
"properties": {
"name": {
"description": "Name of the referent. More info: http://kubernetes.io/docs/user-guide/identifiers#names",
"type": "string"
}
}
},
"v1.SecretKeySelector": {
"description": "SecretKeySelector selects a key of a Secret.",
"required": [

View File

@ -2257,6 +2257,10 @@
"configMapRef": {
"$ref": "v1.ConfigMapEnvSource",
"description": "The ConfigMap to select from"
},
"secretRef": {
"$ref": "v1.SecretEnvSource",
"description": "The Secret to select from"
}
}
},
@ -2270,6 +2274,16 @@
}
}
},
"v1.SecretEnvSource": {
"id": "v1.SecretEnvSource",
"description": "SecretEnvSource selects a Secret to populate the environment variables with.\n\nThe contents of the target Secret's Data field will represent the key-value pairs as environment variables.",
"properties": {
"name": {
"type": "string",
"description": "Name of the referent. More info: http://kubernetes.io/docs/user-guide/identifiers#names"
}
}
},
"v1.EnvVar": {
"id": "v1.EnvVar",
"description": "EnvVar represents an environment variable present in a Container.",

View File

@ -2262,6 +2262,10 @@
"configMapRef": {
"$ref": "v1.ConfigMapEnvSource",
"description": "The ConfigMap to select from"
},
"secretRef": {
"$ref": "v1.SecretEnvSource",
"description": "The Secret to select from"
}
}
},
@ -2275,6 +2279,16 @@
}
}
},
"v1.SecretEnvSource": {
"id": "v1.SecretEnvSource",
"description": "SecretEnvSource selects a Secret to populate the environment variables with.\n\nThe contents of the target Secret's Data field will represent the key-value pairs as environment variables.",
"properties": {
"name": {
"type": "string",
"description": "Name of the referent. More info: http://kubernetes.io/docs/user-guide/identifiers#names"
}
}
},
"v1.EnvVar": {
"id": "v1.EnvVar",
"description": "EnvVar represents an environment variable present in a Container.",

View File

@ -8634,6 +8634,10 @@
"configMapRef": {
"$ref": "v1.ConfigMapEnvSource",
"description": "The ConfigMap to select from"
},
"secretRef": {
"$ref": "v1.SecretEnvSource",
"description": "The Secret to select from"
}
}
},
@ -8647,6 +8651,16 @@
}
}
},
"v1.SecretEnvSource": {
"id": "v1.SecretEnvSource",
"description": "SecretEnvSource selects a Secret to populate the environment variables with.\n\nThe contents of the target Secret's Data field will represent the key-value pairs as environment variables.",
"properties": {
"name": {
"type": "string",
"description": "Name of the referent. More info: http://kubernetes.io/docs/user-guide/identifiers#names"
}
}
},
"v1.EnvVar": {
"id": "v1.EnvVar",
"description": "EnvVar represents an environment variable present in a Container.",

View File

@ -18726,6 +18726,10 @@
"configMapRef": {
"$ref": "v1.ConfigMapEnvSource",
"description": "The ConfigMap to select from"
},
"secretRef": {
"$ref": "v1.SecretEnvSource",
"description": "The Secret to select from"
}
}
},
@ -18739,6 +18743,16 @@
}
}
},
"v1.SecretEnvSource": {
"id": "v1.SecretEnvSource",
"description": "SecretEnvSource selects a Secret to populate the environment variables with.\n\nThe contents of the target Secret's Data field will represent the key-value pairs as environment variables.",
"properties": {
"name": {
"type": "string",
"description": "Name of the referent. More info: http://kubernetes.io/docs/user-guide/identifiers#names"
}
}
},
"v1.EnvVar": {
"id": "v1.EnvVar",
"description": "EnvVar represents an environment variable present in a Container.",

View File

@ -1512,6 +1512,43 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
</tbody>
</table>
</div>
<div class="sect2">
<h3 id="_v1_secretenvsource">v1.SecretEnvSource</h3>
<div class="paragraph">
<p>SecretEnvSource selects a Secret to populate the environment variables with.</p>
</div>
<div class="paragraph">
<p>The contents of the target Secret&#8217;s Data field will represent the key-value pairs as environment variables.</p>
</div>
<table class="tableblock frame-all grid-all" style="width:100%; ">
<colgroup>
<col style="width:20%;">
<col style="width:20%;">
<col style="width:20%;">
<col style="width:20%;">
<col style="width:20%;">
</colgroup>
<thead>
<tr>
<th class="tableblock halign-left valign-top">Name</th>
<th class="tableblock halign-left valign-top">Description</th>
<th class="tableblock halign-left valign-top">Required</th>
<th class="tableblock halign-left valign-top">Schema</th>
<th class="tableblock halign-left valign-top">Default</th>
</tr>
</thead>
<tbody>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">name</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Name of the referent. More info: <a href="http://kubernetes.io/docs/user-guide/identifiers#names">http://kubernetes.io/docs/user-guide/identifiers#names</a></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">false</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">string</p></td>
<td class="tableblock halign-left valign-top"></td>
</tr>
</tbody>
</table>
</div>
<div class="sect2">
<h3 id="_v1_statusdetails">v1.StatusDetails</h3>
@ -2555,6 +2592,13 @@ Populated by the system when a graceful deletion is requested. Read-only. More i
<td class="tableblock halign-left valign-top"><p class="tableblock"><a href="#_v1_configmapenvsource">v1.ConfigMapEnvSource</a></p></td>
<td class="tableblock halign-left valign-top"></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">secretRef</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">The Secret to select from</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">false</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><a href="#_v1_secretenvsource">v1.SecretEnvSource</a></p></td>
<td class="tableblock halign-left valign-top"></td>
</tr>
</tbody>
</table>
@ -4967,7 +5011,7 @@ Examples:<br>
</div>
<div id="footer">
<div id="footer-text">
Last updated 2017-01-18 15:32:06 UTC
Last updated 2017-01-19 19:02:01 UTC
</div>
</div>
</body>

View File

@ -1416,6 +1416,43 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
</tbody>
</table>
</div>
<div class="sect2">
<h3 id="_v1_secretenvsource">v1.SecretEnvSource</h3>
<div class="paragraph">
<p>SecretEnvSource selects a Secret to populate the environment variables with.</p>
</div>
<div class="paragraph">
<p>The contents of the target Secret&#8217;s Data field will represent the key-value pairs as environment variables.</p>
</div>
<table class="tableblock frame-all grid-all" style="width:100%; ">
<colgroup>
<col style="width:20%;">
<col style="width:20%;">
<col style="width:20%;">
<col style="width:20%;">
<col style="width:20%;">
</colgroup>
<thead>
<tr>
<th class="tableblock halign-left valign-top">Name</th>
<th class="tableblock halign-left valign-top">Description</th>
<th class="tableblock halign-left valign-top">Required</th>
<th class="tableblock halign-left valign-top">Schema</th>
<th class="tableblock halign-left valign-top">Default</th>
</tr>
</thead>
<tbody>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">name</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Name of the referent. More info: <a href="http://kubernetes.io/docs/user-guide/identifiers#names">http://kubernetes.io/docs/user-guide/identifiers#names</a></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">false</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">string</p></td>
<td class="tableblock halign-left valign-top"></td>
</tr>
</tbody>
</table>
</div>
<div class="sect2">
<h3 id="_v1_statusdetails">v1.StatusDetails</h3>
@ -2466,6 +2503,13 @@ Populated by the system when a graceful deletion is requested. Read-only. More i
<td class="tableblock halign-left valign-top"><p class="tableblock"><a href="#_v1_configmapenvsource">v1.ConfigMapEnvSource</a></p></td>
<td class="tableblock halign-left valign-top"></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">secretRef</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">The Secret to select from</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">false</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><a href="#_v1_secretenvsource">v1.SecretEnvSource</a></p></td>
<td class="tableblock halign-left valign-top"></td>
</tr>
</tbody>
</table>
@ -4899,7 +4943,7 @@ Examples:<br>
</div>
<div id="footer">
<div id="footer-text">
Last updated 2017-01-18 15:32:28 UTC
Last updated 2017-01-19 19:02:17 UTC
</div>
</div>
</body>

View File

@ -1663,6 +1663,43 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
</tbody>
</table>
</div>
<div class="sect2">
<h3 id="_v1_secretenvsource">v1.SecretEnvSource</h3>
<div class="paragraph">
<p>SecretEnvSource selects a Secret to populate the environment variables with.</p>
</div>
<div class="paragraph">
<p>The contents of the target Secret&#8217;s Data field will represent the key-value pairs as environment variables.</p>
</div>
<table class="tableblock frame-all grid-all" style="width:100%; ">
<colgroup>
<col style="width:20%;">
<col style="width:20%;">
<col style="width:20%;">
<col style="width:20%;">
<col style="width:20%;">
</colgroup>
<thead>
<tr>
<th class="tableblock halign-left valign-top">Name</th>
<th class="tableblock halign-left valign-top">Description</th>
<th class="tableblock halign-left valign-top">Required</th>
<th class="tableblock halign-left valign-top">Schema</th>
<th class="tableblock halign-left valign-top">Default</th>
</tr>
</thead>
<tbody>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">name</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Name of the referent. More info: <a href="http://kubernetes.io/docs/user-guide/identifiers#names">http://kubernetes.io/docs/user-guide/identifiers#names</a></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">false</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">string</p></td>
<td class="tableblock halign-left valign-top"></td>
</tr>
</tbody>
</table>
</div>
<div class="sect2">
<h3 id="_v1_capabilities">v1.Capabilities</h3>
@ -2406,6 +2443,13 @@ Populated by the system when a graceful deletion is requested. Read-only. More i
<td class="tableblock halign-left valign-top"><p class="tableblock"><a href="#_v1_configmapenvsource">v1.ConfigMapEnvSource</a></p></td>
<td class="tableblock halign-left valign-top"></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">secretRef</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">The Secret to select from</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">false</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><a href="#_v1_secretenvsource">v1.SecretEnvSource</a></p></td>
<td class="tableblock halign-left valign-top"></td>
</tr>
</tbody>
</table>
@ -7516,7 +7560,7 @@ Both these may change in the future. Incoming requests are matched against the h
</div>
<div id="footer">
<div id="footer-text">
Last updated 2017-01-18 15:32:45 UTC
Last updated 2017-01-19 19:02:29 UTC
</div>
</div>
</body>

View File

@ -1704,6 +1704,43 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
</tbody>
</table>
</div>
<div class="sect2">
<h3 id="_v1_secretenvsource">v1.SecretEnvSource</h3>
<div class="paragraph">
<p>SecretEnvSource selects a Secret to populate the environment variables with.</p>
</div>
<div class="paragraph">
<p>The contents of the target Secret&#8217;s Data field will represent the key-value pairs as environment variables.</p>
</div>
<table class="tableblock frame-all grid-all" style="width:100%; ">
<colgroup>
<col style="width:20%;">
<col style="width:20%;">
<col style="width:20%;">
<col style="width:20%;">
<col style="width:20%;">
</colgroup>
<thead>
<tr>
<th class="tableblock halign-left valign-top">Name</th>
<th class="tableblock halign-left valign-top">Description</th>
<th class="tableblock halign-left valign-top">Required</th>
<th class="tableblock halign-left valign-top">Schema</th>
<th class="tableblock halign-left valign-top">Default</th>
</tr>
</thead>
<tbody>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">name</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Name of the referent. More info: <a href="http://kubernetes.io/docs/user-guide/identifiers#names">http://kubernetes.io/docs/user-guide/identifiers#names</a></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">false</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">string</p></td>
<td class="tableblock halign-left valign-top"></td>
</tr>
</tbody>
</table>
</div>
<div class="sect2">
<h3 id="_v1_scalestatus">v1.ScaleStatus</h3>
@ -2550,6 +2587,13 @@ Populated by the system when a graceful deletion is requested. Read-only. More i
<td class="tableblock halign-left valign-top"><p class="tableblock"><a href="#_v1_configmapenvsource">v1.ConfigMapEnvSource</a></p></td>
<td class="tableblock halign-left valign-top"></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">secretRef</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">The Secret to select from</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">false</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><a href="#_v1_secretenvsource">v1.SecretEnvSource</a></p></td>
<td class="tableblock halign-left valign-top"></td>
</tr>
</tbody>
</table>
@ -9143,7 +9187,7 @@ Examples:<br>
</div>
<div id="footer">
<div id="footer-text">
Last updated 2017-01-18 15:31:58 UTC
Last updated 2017-01-19 19:01:55 UTC
</div>
</div>
</body>

View File

@ -10101,6 +10101,10 @@
"prefix": {
"description": "An optional identifer to prepend to each key in the ConfigMap. Must be a C_IDENTIFIER.",
"type": "string"
},
"secretRef": {
"description": "The Secret to select from",
"$ref": "#/definitions/v1.SecretEnvSource"
}
}
},
@ -11598,6 +11602,15 @@
}
}
},
"v1.SecretEnvSource": {
"description": "SecretEnvSource selects a Secret to populate the environment variables with.\n\nThe contents of the target Secret's Data field will represent the key-value pairs as environment variables.",
"properties": {
"name": {
"description": "Name of the referent. More info: http://kubernetes.io/docs/user-guide/identifiers#names",
"type": "string"
}
}
},
"v1.SecretKeySelector": {
"description": "SecretKeySelector selects a key of a Secret.",
"required": [

View File

@ -386,11 +386,16 @@ func FuzzerFor(t *testing.T, version schema.GroupVersion, src rand.Source) *fuzz
}
if c.RandBool() {
c.Fuzz(&ev.ConfigMapRef)
} else {
c.Fuzz(&ev.SecretRef)
}
},
func(cm *api.ConfigMapEnvSource, c fuzz.Continue) {
c.FuzzNoCustom(cm) // fuzz self without calling this function again
},
func(s *api.SecretEnvSource, c fuzz.Continue) {
c.FuzzNoCustom(s) // fuzz self without calling this function again
},
func(sc *api.SecurityContext, c fuzz.Continue) {
c.FuzzNoCustom(sc) // fuzz self without calling this function again
if c.RandBool() {

View File

@ -1142,6 +1142,9 @@ type EnvFromSource struct {
// The ConfigMap to select from.
//+optional
ConfigMapRef *ConfigMapEnvSource
// The Secret to select from.
//+optional
SecretRef *SecretEnvSource
}
// ConfigMapEnvSource selects a ConfigMap to populate the environment
@ -1154,6 +1157,16 @@ type ConfigMapEnvSource struct {
LocalObjectReference
}
// SecretEnvSource selects a Secret to populate the environment
// variables with.
//
// The contents of the target Secret's Data field will represent the
// key-value pairs as environment variables.
type SecretEnvSource struct {
// The Secret to select from.
LocalObjectReference
}
// HTTPHeader describes a custom header to be used in HTTP probes
type HTTPHeader struct {
// The header field name

File diff suppressed because it is too large Load Diff

View File

@ -799,6 +799,10 @@ message EnvFromSource {
// The ConfigMap to select from
// +optional
optional ConfigMapEnvSource configMapRef = 2;
// The Secret to select from
// +optional
optional SecretEnvSource secretRef = 3;
}
// EnvVar represents an environment variable present in a Container.
@ -3067,6 +3071,16 @@ message Secret {
optional string type = 3;
}
// SecretEnvSource selects a Secret to populate the environment
// variables with.
//
// The contents of the target Secret's Data field will represent the
// key-value pairs as environment variables.
message SecretEnvSource {
// The Secret to select from.
optional LocalObjectReference localObjectReference = 1;
}
// SecretKeySelector selects a key of a Secret.
message SecretKeySelector {
// The name of the secret in the pod's namespace to select from.

View File

@ -19828,14 +19828,15 @@ func (x *EnvFromSource) CodecEncodeSelf(e *codec1978.Encoder) {
} else {
yysep2 := !z.EncBinary()
yy2arr2 := z.EncBasicHandle().StructToArray
var yyq2 [2]bool
var yyq2 [3]bool
_, _, _ = yysep2, yyq2, yy2arr2
const yyr2 bool = false
yyq2[0] = x.Prefix != ""
yyq2[1] = x.ConfigMapRef != nil
yyq2[2] = x.SecretRef != nil
var yynn2 int
if yyr2 || yy2arr2 {
r.EncodeArrayStart(2)
r.EncodeArrayStart(3)
} else {
yynn2 = 0
for _, b := range yyq2 {
@ -19894,6 +19895,29 @@ func (x *EnvFromSource) CodecEncodeSelf(e *codec1978.Encoder) {
}
}
}
if yyr2 || yy2arr2 {
z.EncSendContainerState(codecSelfer_containerArrayElem1234)
if yyq2[2] {
if x.SecretRef == nil {
r.EncodeNil()
} else {
x.SecretRef.CodecEncodeSelf(e)
}
} else {
r.EncodeNil()
}
} else {
if yyq2[2] {
z.EncSendContainerState(codecSelfer_containerMapKey1234)
r.EncodeString(codecSelferC_UTF81234, string("secretRef"))
z.EncSendContainerState(codecSelfer_containerMapValue1234)
if x.SecretRef == nil {
r.EncodeNil()
} else {
x.SecretRef.CodecEncodeSelf(e)
}
}
}
if yyr2 || yy2arr2 {
z.EncSendContainerState(codecSelfer_containerArrayEnd1234)
} else {
@ -19978,6 +20002,17 @@ func (x *EnvFromSource) codecDecodeSelfFromMap(l int, d *codec1978.Decoder) {
}
x.ConfigMapRef.CodecDecodeSelf(d)
}
case "secretRef":
if r.TryDecodeAsNil() {
if x.SecretRef != nil {
x.SecretRef = nil
}
} else {
if x.SecretRef == nil {
x.SecretRef = new(SecretEnvSource)
}
x.SecretRef.CodecDecodeSelf(d)
}
default:
z.DecStructFieldNotFound(-1, yys3)
} // end switch yys3
@ -19989,16 +20024,16 @@ func (x *EnvFromSource) codecDecodeSelfFromArray(l int, d *codec1978.Decoder) {
var h codecSelfer1234
z, r := codec1978.GenHelperDecoder(d)
_, _, _ = h, z, r
var yyj7 int
var yyb7 bool
var yyhl7 bool = l >= 0
yyj7++
if yyhl7 {
yyb7 = yyj7 > l
var yyj8 int
var yyb8 bool
var yyhl8 bool = l >= 0
yyj8++
if yyhl8 {
yyb8 = yyj8 > l
} else {
yyb7 = r.CheckBreak()
yyb8 = r.CheckBreak()
}
if yyb7 {
if yyb8 {
z.DecSendContainerState(codecSelfer_containerArrayEnd1234)
return
}
@ -20006,21 +20041,21 @@ func (x *EnvFromSource) codecDecodeSelfFromArray(l int, d *codec1978.Decoder) {
if r.TryDecodeAsNil() {
x.Prefix = ""
} else {
yyv8 := &x.Prefix
yym9 := z.DecBinary()
_ = yym9
yyv9 := &x.Prefix
yym10 := z.DecBinary()
_ = yym10
if false {
} else {
*((*string)(yyv8)) = r.DecodeString()
*((*string)(yyv9)) = r.DecodeString()
}
}
yyj7++
if yyhl7 {
yyb7 = yyj7 > l
yyj8++
if yyhl8 {
yyb8 = yyj8 > l
} else {
yyb7 = r.CheckBreak()
yyb8 = r.CheckBreak()
}
if yyb7 {
if yyb8 {
z.DecSendContainerState(codecSelfer_containerArrayEnd1234)
return
}
@ -20035,18 +20070,39 @@ func (x *EnvFromSource) codecDecodeSelfFromArray(l int, d *codec1978.Decoder) {
}
x.ConfigMapRef.CodecDecodeSelf(d)
}
for {
yyj7++
if yyhl7 {
yyb7 = yyj7 > l
} else {
yyb7 = r.CheckBreak()
yyj8++
if yyhl8 {
yyb8 = yyj8 > l
} else {
yyb8 = r.CheckBreak()
}
if yyb8 {
z.DecSendContainerState(codecSelfer_containerArrayEnd1234)
return
}
z.DecSendContainerState(codecSelfer_containerArrayElem1234)
if r.TryDecodeAsNil() {
if x.SecretRef != nil {
x.SecretRef = nil
}
if yyb7 {
} else {
if x.SecretRef == nil {
x.SecretRef = new(SecretEnvSource)
}
x.SecretRef.CodecDecodeSelf(d)
}
for {
yyj8++
if yyhl8 {
yyb8 = yyj8 > l
} else {
yyb8 = r.CheckBreak()
}
if yyb8 {
break
}
z.DecSendContainerState(codecSelfer_containerArrayElem1234)
z.DecStructFieldNotFound(yyj7-1, "")
z.DecStructFieldNotFound(yyj8-1, "")
}
z.DecSendContainerState(codecSelfer_containerArrayEnd1234)
}
@ -20232,6 +20288,187 @@ func (x *ConfigMapEnvSource) codecDecodeSelfFromArray(l int, d *codec1978.Decode
z.DecSendContainerState(codecSelfer_containerArrayEnd1234)
}
func (x *SecretEnvSource) CodecEncodeSelf(e *codec1978.Encoder) {
var h codecSelfer1234
z, r := codec1978.GenHelperEncoder(e)
_, _, _ = h, z, r
if x == nil {
r.EncodeNil()
} else {
yym1 := z.EncBinary()
_ = yym1
if false {
} else if z.HasExtensions() && z.EncExt(x) {
} else {
yysep2 := !z.EncBinary()
yy2arr2 := z.EncBasicHandle().StructToArray
var yyq2 [1]bool
_, _, _ = yysep2, yyq2, yy2arr2
const yyr2 bool = false
yyq2[0] = x.Name != ""
var yynn2 int
if yyr2 || yy2arr2 {
r.EncodeArrayStart(1)
} else {
yynn2 = 0
for _, b := range yyq2 {
if b {
yynn2++
}
}
r.EncodeMapStart(yynn2)
yynn2 = 0
}
if yyr2 || yy2arr2 {
z.EncSendContainerState(codecSelfer_containerArrayElem1234)
if yyq2[0] {
yym4 := z.EncBinary()
_ = yym4
if false {
} else {
r.EncodeString(codecSelferC_UTF81234, string(x.Name))
}
} else {
r.EncodeString(codecSelferC_UTF81234, "")
}
} else {
if yyq2[0] {
z.EncSendContainerState(codecSelfer_containerMapKey1234)
r.EncodeString(codecSelferC_UTF81234, string("name"))
z.EncSendContainerState(codecSelfer_containerMapValue1234)
yym5 := z.EncBinary()
_ = yym5
if false {
} else {
r.EncodeString(codecSelferC_UTF81234, string(x.Name))
}
}
}
if yyr2 || yy2arr2 {
z.EncSendContainerState(codecSelfer_containerArrayEnd1234)
} else {
z.EncSendContainerState(codecSelfer_containerMapEnd1234)
}
}
}
}
func (x *SecretEnvSource) CodecDecodeSelf(d *codec1978.Decoder) {
var h codecSelfer1234
z, r := codec1978.GenHelperDecoder(d)
_, _, _ = h, z, r
yym1 := z.DecBinary()
_ = yym1
if false {
} else if z.HasExtensions() && z.DecExt(x) {
} else {
yyct2 := r.ContainerType()
if yyct2 == codecSelferValueTypeMap1234 {
yyl2 := r.ReadMapStart()
if yyl2 == 0 {
z.DecSendContainerState(codecSelfer_containerMapEnd1234)
} else {
x.codecDecodeSelfFromMap(yyl2, d)
}
} else if yyct2 == codecSelferValueTypeArray1234 {
yyl2 := r.ReadArrayStart()
if yyl2 == 0 {
z.DecSendContainerState(codecSelfer_containerArrayEnd1234)
} else {
x.codecDecodeSelfFromArray(yyl2, d)
}
} else {
panic(codecSelferOnlyMapOrArrayEncodeToStructErr1234)
}
}
}
func (x *SecretEnvSource) codecDecodeSelfFromMap(l int, d *codec1978.Decoder) {
var h codecSelfer1234
z, r := codec1978.GenHelperDecoder(d)
_, _, _ = h, z, r
var yys3Slc = z.DecScratchBuffer() // default slice to decode into
_ = yys3Slc
var yyhl3 bool = l >= 0
for yyj3 := 0; ; yyj3++ {
if yyhl3 {
if yyj3 >= l {
break
}
} else {
if r.CheckBreak() {
break
}
}
z.DecSendContainerState(codecSelfer_containerMapKey1234)
yys3Slc = r.DecodeBytes(yys3Slc, true, true)
yys3 := string(yys3Slc)
z.DecSendContainerState(codecSelfer_containerMapValue1234)
switch yys3 {
case "name":
if r.TryDecodeAsNil() {
x.Name = ""
} else {
yyv4 := &x.Name
yym5 := z.DecBinary()
_ = yym5
if false {
} else {
*((*string)(yyv4)) = r.DecodeString()
}
}
default:
z.DecStructFieldNotFound(-1, yys3)
} // end switch yys3
} // end for yyj3
z.DecSendContainerState(codecSelfer_containerMapEnd1234)
}
func (x *SecretEnvSource) codecDecodeSelfFromArray(l int, d *codec1978.Decoder) {
var h codecSelfer1234
z, r := codec1978.GenHelperDecoder(d)
_, _, _ = h, z, r
var yyj6 int
var yyb6 bool
var yyhl6 bool = l >= 0
yyj6++
if yyhl6 {
yyb6 = yyj6 > l
} else {
yyb6 = r.CheckBreak()
}
if yyb6 {
z.DecSendContainerState(codecSelfer_containerArrayEnd1234)
return
}
z.DecSendContainerState(codecSelfer_containerArrayElem1234)
if r.TryDecodeAsNil() {
x.Name = ""
} else {
yyv7 := &x.Name
yym8 := z.DecBinary()
_ = yym8
if false {
} else {
*((*string)(yyv7)) = r.DecodeString()
}
}
for {
yyj6++
if yyhl6 {
yyb6 = yyj6 > l
} else {
yyb6 = r.CheckBreak()
}
if yyb6 {
break
}
z.DecSendContainerState(codecSelfer_containerArrayElem1234)
z.DecStructFieldNotFound(yyj6-1, "")
}
z.DecSendContainerState(codecSelfer_containerArrayEnd1234)
}
func (x *HTTPHeader) CodecEncodeSelf(e *codec1978.Encoder) {
var h codecSelfer1234
z, r := codec1978.GenHelperEncoder(e)
@ -62738,7 +62975,7 @@ func (x codecSelfer1234) decSliceEnvFromSource(v *[]EnvFromSource, d *codec1978.
yyrg1 := len(yyv1) > 0
yyv21 := yyv1
yyrl1, yyrt1 = z.DecInferLen(yyl1, z.DecBasicHandle().MaxInitLen, 24)
yyrl1, yyrt1 = z.DecInferLen(yyl1, z.DecBasicHandle().MaxInitLen, 32)
if yyrt1 {
if yyrl1 <= cap(yyv1) {
yyv1 = yyv1[:yyrl1]

View File

@ -1243,6 +1243,9 @@ type EnvFromSource struct {
// The ConfigMap to select from
// +optional
ConfigMapRef *ConfigMapEnvSource `json:"configMapRef,omitempty" protobuf:"bytes,2,opt,name=configMapRef"`
// The Secret to select from
// +optional
SecretRef *SecretEnvSource `json:"secretRef,omitempty" protobuf:"bytes,3,opt,name=secretRef"`
}
// ConfigMapEnvSource selects a ConfigMap to populate the environment
@ -1255,6 +1258,16 @@ type ConfigMapEnvSource struct {
LocalObjectReference `json:",inline" protobuf:"bytes,1,opt,name=localObjectReference"`
}
// SecretEnvSource selects a Secret to populate the environment
// variables with.
//
// The contents of the target Secret's Data field will represent the
// key-value pairs as environment variables.
type SecretEnvSource struct {
// The Secret to select from.
LocalObjectReference `json:",inline" protobuf:"bytes,1,opt,name=localObjectReference"`
}
// HTTPHeader describes a custom header to be used in HTTP probes
type HTTPHeader struct {
// The header field name

View File

@ -437,6 +437,7 @@ var map_EnvFromSource = map[string]string{
"": "EnvFromSource represents the source of a set of ConfigMaps",
"prefix": "An optional identifer to prepend to each key in the ConfigMap. Must be a C_IDENTIFIER.",
"configMapRef": "The ConfigMap to select from",
"secretRef": "The Secret to select from",
}
func (EnvFromSource) SwaggerDoc() map[string]string {
@ -1588,6 +1589,14 @@ func (Secret) SwaggerDoc() map[string]string {
return map_Secret
}
var map_SecretEnvSource = map[string]string{
"": "SecretEnvSource selects a Secret to populate the environment variables with.\n\nThe contents of the target Secret's Data field will represent the key-value pairs as environment variables.",
}
func (SecretEnvSource) SwaggerDoc() map[string]string {
return map_SecretEnvSource
}
var map_SecretKeySelector = map[string]string{
"": "SecretKeySelector selects a key of a Secret.",
"key": "The key of the secret to select from. Must be a valid secret key.",

View File

@ -309,6 +309,8 @@ func RegisterConversions(scheme *runtime.Scheme) error {
Convert_api_SELinuxOptions_To_v1_SELinuxOptions,
Convert_v1_Secret_To_api_Secret,
Convert_api_Secret_To_v1_Secret,
Convert_v1_SecretEnvSource_To_api_SecretEnvSource,
Convert_api_SecretEnvSource_To_v1_SecretEnvSource,
Convert_v1_SecretKeySelector_To_api_SecretKeySelector,
Convert_api_SecretKeySelector_To_v1_SecretKeySelector,
Convert_v1_SecretList_To_api_SecretList,
@ -1205,6 +1207,7 @@ func Convert_api_EndpointsList_To_v1_EndpointsList(in *api.EndpointsList, out *E
func autoConvert_v1_EnvFromSource_To_api_EnvFromSource(in *EnvFromSource, out *api.EnvFromSource, s conversion.Scope) error {
out.Prefix = in.Prefix
out.ConfigMapRef = (*api.ConfigMapEnvSource)(unsafe.Pointer(in.ConfigMapRef))
out.SecretRef = (*api.SecretEnvSource)(unsafe.Pointer(in.SecretRef))
return nil
}
@ -1215,6 +1218,7 @@ func Convert_v1_EnvFromSource_To_api_EnvFromSource(in *EnvFromSource, out *api.E
func autoConvert_api_EnvFromSource_To_v1_EnvFromSource(in *api.EnvFromSource, out *EnvFromSource, s conversion.Scope) error {
out.Prefix = in.Prefix
out.ConfigMapRef = (*ConfigMapEnvSource)(unsafe.Pointer(in.ConfigMapRef))
out.SecretRef = (*SecretEnvSource)(unsafe.Pointer(in.SecretRef))
return nil
}
@ -3760,6 +3764,28 @@ func Convert_api_Secret_To_v1_Secret(in *api.Secret, out *Secret, s conversion.S
return autoConvert_api_Secret_To_v1_Secret(in, out, s)
}
func autoConvert_v1_SecretEnvSource_To_api_SecretEnvSource(in *SecretEnvSource, out *api.SecretEnvSource, s conversion.Scope) error {
if err := Convert_v1_LocalObjectReference_To_api_LocalObjectReference(&in.LocalObjectReference, &out.LocalObjectReference, s); err != nil {
return err
}
return nil
}
func Convert_v1_SecretEnvSource_To_api_SecretEnvSource(in *SecretEnvSource, out *api.SecretEnvSource, s conversion.Scope) error {
return autoConvert_v1_SecretEnvSource_To_api_SecretEnvSource(in, out, s)
}
func autoConvert_api_SecretEnvSource_To_v1_SecretEnvSource(in *api.SecretEnvSource, out *SecretEnvSource, s conversion.Scope) error {
if err := Convert_api_LocalObjectReference_To_v1_LocalObjectReference(&in.LocalObjectReference, &out.LocalObjectReference, s); err != nil {
return err
}
return nil
}
func Convert_api_SecretEnvSource_To_v1_SecretEnvSource(in *api.SecretEnvSource, out *SecretEnvSource, s conversion.Scope) error {
return autoConvert_api_SecretEnvSource_To_v1_SecretEnvSource(in, out, s)
}
func autoConvert_v1_SecretKeySelector_To_api_SecretKeySelector(in *SecretKeySelector, out *api.SecretKeySelector, s conversion.Scope) error {
if err := Convert_v1_LocalObjectReference_To_api_LocalObjectReference(&in.LocalObjectReference, &out.LocalObjectReference, s); err != nil {
return err

View File

@ -172,6 +172,7 @@ func RegisterDeepCopies(scheme *runtime.Scheme) error {
conversion.GeneratedDeepCopyFunc{Fn: DeepCopy_v1_ResourceRequirements, InType: reflect.TypeOf(&ResourceRequirements{})},
conversion.GeneratedDeepCopyFunc{Fn: DeepCopy_v1_SELinuxOptions, InType: reflect.TypeOf(&SELinuxOptions{})},
conversion.GeneratedDeepCopyFunc{Fn: DeepCopy_v1_Secret, InType: reflect.TypeOf(&Secret{})},
conversion.GeneratedDeepCopyFunc{Fn: DeepCopy_v1_SecretEnvSource, InType: reflect.TypeOf(&SecretEnvSource{})},
conversion.GeneratedDeepCopyFunc{Fn: DeepCopy_v1_SecretKeySelector, InType: reflect.TypeOf(&SecretKeySelector{})},
conversion.GeneratedDeepCopyFunc{Fn: DeepCopy_v1_SecretList, InType: reflect.TypeOf(&SecretList{})},
conversion.GeneratedDeepCopyFunc{Fn: DeepCopy_v1_SecretVolumeSource, InType: reflect.TypeOf(&SecretVolumeSource{})},
@ -873,6 +874,11 @@ func DeepCopy_v1_EnvFromSource(in interface{}, out interface{}, c *conversion.Cl
*out = new(ConfigMapEnvSource)
**out = **in
}
if in.SecretRef != nil {
in, out := &in.SecretRef, &out.SecretRef
*out = new(SecretEnvSource)
**out = **in
}
return nil
}
}
@ -2767,6 +2773,15 @@ func DeepCopy_v1_Secret(in interface{}, out interface{}, c *conversion.Cloner) e
}
}
func DeepCopy_v1_SecretEnvSource(in interface{}, out interface{}, c *conversion.Cloner) error {
{
in := in.(*SecretEnvSource)
out := out.(*SecretEnvSource)
*out = *in
return nil
}
}
func DeepCopy_v1_SecretKeySelector(in interface{}, out interface{}, c *conversion.Cloner) error {
{
in := in.(*SecretKeySelector)

View File

@ -1260,9 +1260,22 @@ func validateEnvFrom(vars []api.EnvFromSource, fldPath *field.Path) field.ErrorL
allErrs = append(allErrs, field.Invalid(idxPath.Child("prefix"), ev.Prefix, msg))
}
}
numSources := 0
if ev.ConfigMapRef != nil {
numSources++
allErrs = append(allErrs, validateConfigMapEnvSource(ev.ConfigMapRef, idxPath.Child("configMapRef"))...)
}
if ev.SecretRef != nil {
numSources++
allErrs = append(allErrs, validateSecretEnvSource(ev.SecretRef, idxPath.Child("secretRef"))...)
}
if numSources == 0 {
allErrs = append(allErrs, field.Invalid(fldPath, "", "must specify one of: `configMapRef` or `secretRef`"))
} else if numSources > 1 {
allErrs = append(allErrs, field.Invalid(fldPath, "", "may not have more than one field specified at a time"))
}
}
return allErrs
}
@ -1275,6 +1288,14 @@ func validateConfigMapEnvSource(configMapSource *api.ConfigMapEnvSource, fldPath
return allErrs
}
func validateSecretEnvSource(secretSource *api.SecretEnvSource, fldPath *field.Path) field.ErrorList {
allErrs := field.ErrorList{}
if len(secretSource.Name) == 0 {
allErrs = append(allErrs, field.Required(fldPath.Child("name"), ""))
}
return allErrs
}
var validContainerResourceDivisorForCPU = sets.NewString("1m", "1")
var validContainerResourceDivisorForMemory = sets.NewString("1", "1k", "1M", "1G", "1T", "1P", "1E", "1Ki", "1Mi", "1Gi", "1Ti", "1Pi", "1Ei")

View File

@ -2285,6 +2285,17 @@ func TestValidateEnvFrom(t *testing.T) {
LocalObjectReference: api.LocalObjectReference{Name: "abc"},
},
},
{
SecretRef: &api.SecretEnvSource{
LocalObjectReference: api.LocalObjectReference{Name: "abc"},
},
},
{
Prefix: "pre_",
SecretRef: &api.SecretEnvSource{
LocalObjectReference: api.LocalObjectReference{Name: "abc"},
},
},
}
if errs := validateEnvFrom(successCase, field.NewPath("field")); len(errs) != 0 {
t.Errorf("expected success: %v", errs)
@ -2316,6 +2327,46 @@ func TestValidateEnvFrom(t *testing.T) {
},
expectedError: `field[0].prefix: Invalid value: "a.b": ` + idErrMsg,
},
{
name: "zero-length name",
envs: []api.EnvFromSource{
{
SecretRef: &api.SecretEnvSource{
LocalObjectReference: api.LocalObjectReference{Name: ""}},
},
},
expectedError: "field[0].secretRef.name: Required value",
},
{
name: "invalid prefix",
envs: []api.EnvFromSource{
{
Prefix: "a.b",
SecretRef: &api.SecretEnvSource{
LocalObjectReference: api.LocalObjectReference{Name: "abc"}},
},
},
expectedError: `field[0].prefix: Invalid value: "a.b": ` + idErrMsg,
},
{
name: "no refs",
envs: []api.EnvFromSource{
{},
},
expectedError: "field: Invalid value: \"\": must specify one of: `configMapRef` or `secretRef`",
},
{
name: "multiple refs",
envs: []api.EnvFromSource{
{
SecretRef: &api.SecretEnvSource{
LocalObjectReference: api.LocalObjectReference{Name: "abc"}},
ConfigMapRef: &api.ConfigMapEnvSource{
LocalObjectReference: api.LocalObjectReference{Name: "abc"}},
},
},
expectedError: "field: Invalid value: \"\": may not have more than one field specified at a time",
},
}
for _, tc := range errorCases {
if errs := validateEnvFrom(tc.envs, field.NewPath("field")); len(errs) == 0 {

View File

@ -175,6 +175,7 @@ func RegisterDeepCopies(scheme *runtime.Scheme) error {
conversion.GeneratedDeepCopyFunc{Fn: DeepCopy_api_ResourceRequirements, InType: reflect.TypeOf(&ResourceRequirements{})},
conversion.GeneratedDeepCopyFunc{Fn: DeepCopy_api_SELinuxOptions, InType: reflect.TypeOf(&SELinuxOptions{})},
conversion.GeneratedDeepCopyFunc{Fn: DeepCopy_api_Secret, InType: reflect.TypeOf(&Secret{})},
conversion.GeneratedDeepCopyFunc{Fn: DeepCopy_api_SecretEnvSource, InType: reflect.TypeOf(&SecretEnvSource{})},
conversion.GeneratedDeepCopyFunc{Fn: DeepCopy_api_SecretKeySelector, InType: reflect.TypeOf(&SecretKeySelector{})},
conversion.GeneratedDeepCopyFunc{Fn: DeepCopy_api_SecretList, InType: reflect.TypeOf(&SecretList{})},
conversion.GeneratedDeepCopyFunc{Fn: DeepCopy_api_SecretVolumeSource, InType: reflect.TypeOf(&SecretVolumeSource{})},
@ -901,6 +902,11 @@ func DeepCopy_api_EnvFromSource(in interface{}, out interface{}, c *conversion.C
*out = new(ConfigMapEnvSource)
**out = **in
}
if in.SecretRef != nil {
in, out := &in.SecretRef, &out.SecretRef
*out = new(SecretEnvSource)
**out = **in
}
return nil
}
}
@ -2799,6 +2805,15 @@ func DeepCopy_api_Secret(in interface{}, out interface{}, c *conversion.Cloner)
}
}
func DeepCopy_api_SecretEnvSource(in interface{}, out interface{}, c *conversion.Cloner) error {
{
in := in.(*SecretEnvSource)
out := out.(*SecretEnvSource)
*out = *in
return nil
}
}
func DeepCopy_api_SecretKeySelector(in interface{}, out interface{}, c *conversion.Cloner) error {
{
in := in.(*SecretKeySelector)

View File

@ -1924,11 +1924,17 @@ var OpenAPIDefinitions *openapi.OpenAPIDefinitions = &openapi.OpenAPIDefinitions
Ref: spec.MustCreateRef("#/definitions/v1.ConfigMapEnvSource"),
},
},
"secretRef": {
SchemaProps: spec.SchemaProps{
Description: "The Secret to select from",
Ref: spec.MustCreateRef("#/definitions/v1.SecretEnvSource"),
},
},
},
},
},
Dependencies: []string{
"v1.ConfigMapEnvSource"},
"v1.ConfigMapEnvSource", "v1.SecretEnvSource"},
},
"v1.EnvVar": {
Schema: spec.Schema{
@ -7367,6 +7373,23 @@ var OpenAPIDefinitions *openapi.OpenAPIDefinitions = &openapi.OpenAPIDefinitions
Dependencies: []string{
"v1.ObjectMeta"},
},
"v1.SecretEnvSource": {
Schema: spec.Schema{
SchemaProps: spec.SchemaProps{
Description: "SecretEnvSource selects a Secret to populate the environment variables with.\n\nThe contents of the target Secret's Data field will represent the key-value pairs as environment variables.",
Properties: map[string]spec.Schema{
"name": {
SchemaProps: spec.SchemaProps{
Description: "Name of the referent. More info: http://kubernetes.io/docs/user-guide/identifiers#names",
Type: []string{"string"},
Format: "",
},
},
},
},
},
Dependencies: []string{},
},
"v1.SecretKeySelector": {
Schema: spec.Schema{
SchemaProps: spec.SchemaProps{

View File

@ -1052,10 +1052,19 @@ func describeContainerEnvFrom(container api.Container, resolverFn EnvVarResolver
w.Write(LEVEL_2, "Environment Variables from:%s\n", none)
for _, e := range container.EnvFrom {
from := ""
name := ""
if e.ConfigMapRef != nil {
from = "ConfigMap"
name = e.ConfigMapRef.Name
} else if e.SecretRef != nil {
from = "Secret"
name = e.SecretRef.Name
}
if len(e.Prefix) == 0 {
w.Write(LEVEL_3, "%s\tConfigMap\n", e.ConfigMapRef.Name)
w.Write(LEVEL_3, "%s\t%s\n", name, from)
} else {
w.Write(LEVEL_3, "%s\tConfigMap with prefix '%s'\n", e.ConfigMapRef.Name, e.Prefix)
w.Write(LEVEL_3, "%s\t%s with prefix '%s'\n", name, from, e.Prefix)
}
}
}

View File

@ -306,6 +306,24 @@ func TestDescribeContainers(t *testing.T) {
},
expectedElements: []string{"test", "State", "Waiting", "Ready", "True", "Restart Count", "7", "Image", "image", "envname", "xyz", "a123\tConfigMap with prefix 'p_'"},
},
{
container: api.Container{Name: "test", Image: "image", Env: []api.EnvVar{{Name: "envname", Value: "xyz"}}, EnvFrom: []api.EnvFromSource{{SecretRef: &api.SecretEnvSource{LocalObjectReference: api.LocalObjectReference{Name: "a123"}}}}},
status: api.ContainerStatus{
Name: "test",
Ready: true,
RestartCount: 7,
},
expectedElements: []string{"test", "State", "Waiting", "Ready", "True", "Restart Count", "7", "Image", "image", "envname", "xyz", "a123\tSecret"},
},
{
container: api.Container{Name: "test", Image: "image", Env: []api.EnvVar{{Name: "envname", Value: "xyz"}}, EnvFrom: []api.EnvFromSource{{Prefix: "p_", SecretRef: &api.SecretEnvSource{LocalObjectReference: api.LocalObjectReference{Name: "a123"}}}}},
status: api.ContainerStatus{
Name: "test",
Ready: true,
RestartCount: 7,
},
expectedElements: []string{"test", "State", "Waiting", "Ready", "True", "Restart Count", "7", "Image", "image", "envname", "xyz", "a123\tSecret with prefix 'p_'"},
},
// Command
{
container: api.Container{Name: "test", Image: "image", Command: []string{"sleep", "1000"}},

View File

@ -418,13 +418,15 @@ func (kl *Kubelet) makeEnvironmentVariables(pod *v1.Pod, container *v1.Container
var (
configMaps = make(map[string]*v1.ConfigMap)
secrets = make(map[string]*v1.Secret)
tmpEnv = make(map[string]string)
)
// Env will override EnvFrom variables.
// Process EnvFrom first then allow Env to replace existing values.
for _, envFrom := range container.EnvFrom {
if envFrom.ConfigMapRef != nil {
switch {
case envFrom.ConfigMapRef != nil:
name := envFrom.ConfigMapRef.Name
configMap, ok := configMaps[name]
if !ok {
@ -432,12 +434,12 @@ func (kl *Kubelet) makeEnvironmentVariables(pod *v1.Pod, container *v1.Container
return result, fmt.Errorf("Couldn't get configMap %v/%v, no kubeClient defined", pod.Namespace, name)
}
configMap, err = kl.kubeClient.Core().ConfigMaps(pod.Namespace).Get(name, metav1.GetOptions{})
if err != nil {
return result, err
}
configMaps[name] = configMap
}
for k, v := range configMap.Data {
if len(envFrom.Prefix) > 0 {
k = envFrom.Prefix + k
@ -445,14 +447,31 @@ func (kl *Kubelet) makeEnvironmentVariables(pod *v1.Pod, container *v1.Container
if errMsgs := utilvalidation.IsCIdentifier(k); len(errMsgs) != 0 {
return result, fmt.Errorf("Invalid environment variable name, %v, from configmap %v/%v: %s", k, pod.Namespace, name, errMsgs[0])
}
// Accesses apiserver+Pods.
// So, the master may set service env vars, or kubelet may. In case both are doing
// it, we delete the key from the kubelet-generated ones so we don't have duplicate
// env vars.
// TODO: remove this next line once all platforms use apiserver+Pods.
delete(serviceEnv, k)
tmpEnv[k] = v
}
case envFrom.SecretRef != nil:
name := envFrom.SecretRef.Name
secret, ok := secrets[name]
if !ok {
if kl.kubeClient == nil {
return result, fmt.Errorf("Couldn't get secret %v/%v, no kubeClient defined", pod.Namespace, name)
}
secret, err = kl.kubeClient.Core().Secrets(pod.Namespace).Get(name, metav1.GetOptions{})
if err != nil {
return result, err
}
secrets[name] = secret
}
for k, v := range secret.Data {
if len(envFrom.Prefix) > 0 {
k = envFrom.Prefix + k
}
if errMsgs := utilvalidation.IsCIdentifier(k); len(errMsgs) != 0 {
return result, fmt.Errorf("Invalid environment variable name, %v, from secret %v/%v: %s", k, pod.Namespace, name, errMsgs[0])
}
tmpEnv[k] = string(v)
}
}
}
@ -466,17 +485,9 @@ func (kl *Kubelet) makeEnvironmentVariables(pod *v1.Pod, container *v1.Container
// 2. Create the container's environment in the order variables are declared
// 3. Add remaining service environment vars
var (
secrets = make(map[string]*v1.Secret)
mappingFunc = expansion.MappingFuncFor(tmpEnv, serviceEnv)
)
for _, envVar := range container.Env {
// Accesses apiserver+Pods.
// So, the master may set service env vars, or kubelet may. In case both are doing
// it, we delete the key from the kubelet-generated ones so we don't have duplicate
// env vars.
// TODO: remove this next line once all platforms use apiserver+Pods.
delete(serviceEnv, envVar.Name)
runtimeVal := envVar.Value
if runtimeVal != "" {
// Step 1a: expand variable references
@ -548,7 +559,14 @@ func (kl *Kubelet) makeEnvironmentVariables(pod *v1.Pod, container *v1.Container
// Append remaining service env vars.
for k, v := range serviceEnv {
result = append(result, kubecontainer.EnvVar{Name: k, Value: v})
// Accesses apiserver+Pods.
// So, the master may set service env vars, or kubelet may. In case both are doing
// it, we skip the key from the kubelet-generated ones so we don't have duplicate
// env vars.
// TODO: remove this next line once all platforms use apiserver+Pods.
if _, present := tmpEnv[k]; !present {
result = append(result, kubecontainer.EnvVar{Name: k, Value: v})
}
}
return result, nil
}

View File

@ -274,6 +274,7 @@ func TestMakeEnvironmentVariables(t *testing.T) {
masterServiceNs string // the namespace to read master service info from
nilLister bool // whether the lister should be nil
configMap *v1.ConfigMap // an optional ConfigMap to pull from
secret *v1.Secret // an optional Secret to pull from
expectedEnvs []kubecontainer.EnvVar // a set of expected environment vars
expectedError bool // does the test fail
}{
@ -766,6 +767,160 @@ func TestMakeEnvironmentVariables(t *testing.T) {
},
},
},
{
name: "secret",
ns: "test1",
container: &v1.Container{
EnvFrom: []v1.EnvFromSource{
{
SecretRef: &v1.SecretEnvSource{LocalObjectReference: v1.LocalObjectReference{Name: "test-secret"}},
},
{
Prefix: "p_",
SecretRef: &v1.SecretEnvSource{LocalObjectReference: v1.LocalObjectReference{Name: "test-secret"}},
},
},
Env: []v1.EnvVar{
{
Name: "TEST_LITERAL",
Value: "test-test-test",
},
{
Name: "EXPANSION_TEST",
Value: "$(REPLACE_ME)",
},
{
Name: "DUPE_TEST",
Value: "ENV_VAR",
},
},
},
masterServiceNs: "nothing",
nilLister: false,
secret: &v1.Secret{
ObjectMeta: metav1.ObjectMeta{
Namespace: "test1",
Name: "test-secret",
},
Data: map[string][]byte{
"REPLACE_ME": []byte("FROM_SECRET"),
"DUPE_TEST": []byte("SECRET"),
},
},
expectedEnvs: []kubecontainer.EnvVar{
{
Name: "TEST_LITERAL",
Value: "test-test-test",
},
{
Name: "TEST_SERVICE_HOST",
Value: "1.2.3.3",
},
{
Name: "TEST_SERVICE_PORT",
Value: "8083",
},
{
Name: "TEST_PORT",
Value: "tcp://1.2.3.3:8083",
},
{
Name: "TEST_PORT_8083_TCP",
Value: "tcp://1.2.3.3:8083",
},
{
Name: "TEST_PORT_8083_TCP_PROTO",
Value: "tcp",
},
{
Name: "TEST_PORT_8083_TCP_PORT",
Value: "8083",
},
{
Name: "TEST_PORT_8083_TCP_ADDR",
Value: "1.2.3.3",
},
{
Name: "REPLACE_ME",
Value: "FROM_SECRET",
},
{
Name: "EXPANSION_TEST",
Value: "FROM_SECRET",
},
{
Name: "DUPE_TEST",
Value: "ENV_VAR",
},
{
Name: "p_REPLACE_ME",
Value: "FROM_SECRET",
},
{
Name: "p_DUPE_TEST",
Value: "SECRET",
},
},
},
{
name: "secret_missing",
ns: "test1",
container: &v1.Container{
EnvFrom: []v1.EnvFromSource{
{SecretRef: &v1.SecretEnvSource{LocalObjectReference: v1.LocalObjectReference{Name: "test-secret"}}},
},
},
masterServiceNs: "nothing",
expectedError: true,
},
{
name: "secret_invalid_keys",
ns: "test1",
container: &v1.Container{
EnvFrom: []v1.EnvFromSource{
{SecretRef: &v1.SecretEnvSource{LocalObjectReference: v1.LocalObjectReference{Name: "test-secret"}}},
},
},
masterServiceNs: "nothing",
secret: &v1.Secret{
ObjectMeta: metav1.ObjectMeta{
Namespace: "test1",
Name: "test-secret",
},
Data: map[string][]byte{
"1234": []byte("abc"),
},
},
expectedError: true,
},
{
name: "secret_invalid_keys_valid",
ns: "test",
container: &v1.Container{
EnvFrom: []v1.EnvFromSource{
{
Prefix: "p_",
SecretRef: &v1.SecretEnvSource{LocalObjectReference: v1.LocalObjectReference{Name: "test-secret"}},
},
},
},
masterServiceNs: "",
secret: &v1.Secret{
ObjectMeta: metav1.ObjectMeta{
Namespace: "test1",
Name: "test-secret",
},
Data: map[string][]byte{
"1234": []byte("abc"),
},
},
expectedEnvs: []kubecontainer.EnvVar{
{
Name: "p_1234",
Value: "abc",
},
},
},
}
for _, tc := range testCases {
@ -786,6 +941,14 @@ func TestMakeEnvironmentVariables(t *testing.T) {
return true, tc.configMap, err
})
testKubelet.fakeKubeClient.AddReactor("get", "secrets", func(action core.Action) (bool, runtime.Object, error) {
var err error
if tc.secret == nil {
err = errors.New("no secret defined")
}
return true, tc.secret, err
})
testPod := &v1.Pod{
ObjectMeta: metav1.ObjectMeta{
Namespace: tc.ns,

View File

@ -193,8 +193,62 @@ var _ = framework.KubeDescribe("Secrets", func() {
"SECRET_DATA=value-1",
})
})
It("should be consumable via the environment [Conformance]", func() {
name := "secret-test-" + string(uuid.NewUUID())
secret := newEnvFromSecret(f.Namespace.Name, name)
By(fmt.Sprintf("creating secret %v/%v", f.Namespace.Name, secret.Name))
var err error
if secret, err = f.ClientSet.Core().Secrets(f.Namespace.Name).Create(secret); err != nil {
framework.Failf("unable to create test secret %s: %v", secret.Name, err)
}
pod := &v1.Pod{
ObjectMeta: metav1.ObjectMeta{
Name: "pod-configmaps-" + string(uuid.NewUUID()),
},
Spec: v1.PodSpec{
Containers: []v1.Container{
{
Name: "env-test",
Image: "gcr.io/google_containers/busybox:1.24",
Command: []string{"sh", "-c", "env"},
EnvFrom: []v1.EnvFromSource{
{
SecretRef: &v1.SecretEnvSource{LocalObjectReference: v1.LocalObjectReference{Name: name}},
},
{
Prefix: "p_",
SecretRef: &v1.SecretEnvSource{LocalObjectReference: v1.LocalObjectReference{Name: name}},
},
},
},
},
RestartPolicy: v1.RestartPolicyNever,
},
}
f.TestContainerOutput("consume secrets", pod, 0, []string{
"data_1=value-1", "data_2=value-2", "data_3=value-3",
"p_data_1=value-1", "p_data_2=value-2", "p_data_3=value-3",
})
})
})
func newEnvFromSecret(namespace, name string) *v1.Secret {
return &v1.Secret{
ObjectMeta: metav1.ObjectMeta{
Namespace: namespace,
Name: name,
},
Data: map[string][]byte{
"data_1": []byte("value-1\n"),
"data_2": []byte("value-2\n"),
"data_3": []byte("value-3\n"),
},
}
}
func secretForTest(namespace, name string) *v1.Secret {
return &v1.Secret{
ObjectMeta: metav1.ObjectMeta{