mirror of https://github.com/k3s-io/k3s
Merge pull request #59847 from mtaufen/dkcfg-explicit-keys
Automatic merge from submit-queue (batch tested with PRs 63624, 59847). If you want to cherry-pick this change to another branch, please follow the instructions <a href="https://github.com/kubernetes/community/blob/master/contributors/devel/cherry-picks.md">here</a>. explicit kubelet config key in Node.Spec.ConfigSource.ConfigMap This makes the Kubelet config key in the ConfigMap an explicit part of the API, so we can stop using magic key names. As part of this change, we are retiring ConfigMapRef for ConfigMap. ```release-note You must now specify Node.Spec.ConfigSource.ConfigMap.KubeletConfigKey when using dynamic Kubelet config to tell the Kubelet which key of the ConfigMap identifies its config file. ```pull/8/head
commit
b2fe2a0a6d
|
@ -75671,6 +75671,36 @@
|
|||
}
|
||||
]
|
||||
},
|
||||
"io.k8s.api.core.v1.ConfigMapNodeConfigSource": {
|
||||
"description": "ConfigMapNodeConfigSource contains the information to reference a ConfigMap as a config source for the Node.",
|
||||
"required": [
|
||||
"namespace",
|
||||
"name",
|
||||
"kubeletConfigKey"
|
||||
],
|
||||
"properties": {
|
||||
"kubeletConfigKey": {
|
||||
"description": "KubeletConfigKey declares which key of the referenced ConfigMap corresponds to the KubeletConfiguration structure This field is required in all cases.",
|
||||
"type": "string"
|
||||
},
|
||||
"name": {
|
||||
"description": "Name is the metadata.name of the referenced ConfigMap. This field is required in all cases.",
|
||||
"type": "string"
|
||||
},
|
||||
"namespace": {
|
||||
"description": "Namespace is the metadata.namespace of the referenced ConfigMap. This field is required in all cases.",
|
||||
"type": "string"
|
||||
},
|
||||
"resourceVersion": {
|
||||
"description": "ResourceVersion is the metadata.ResourceVersion of the referenced ConfigMap. This field is forbidden in Node.Spec.",
|
||||
"type": "string"
|
||||
},
|
||||
"uid": {
|
||||
"description": "UID is the metadata.UID of the referenced ConfigMap. This field is currently reqired in Node.Spec.",
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"io.k8s.api.core.v1.ConfigMapProjection": {
|
||||
"description": "Adapts a ConfigMap into a projected volume.\n\nThe contents of the target ConfigMap's Data field will be presented in a projected volume as files using the keys in the Data field as the file names, unless the items element is populated with specific mappings of keys to paths. Note that this is identical to a configmap volume source without the default mode.",
|
||||
"properties": {
|
||||
|
@ -77242,25 +77272,11 @@
|
|||
"io.k8s.api.core.v1.NodeConfigSource": {
|
||||
"description": "NodeConfigSource specifies a source of node configuration. Exactly one subfield (excluding metadata) must be non-nil.",
|
||||
"properties": {
|
||||
"apiVersion": {
|
||||
"description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources",
|
||||
"type": "string"
|
||||
},
|
||||
"configMapRef": {
|
||||
"$ref": "#/definitions/io.k8s.api.core.v1.ObjectReference"
|
||||
},
|
||||
"kind": {
|
||||
"description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds",
|
||||
"type": "string"
|
||||
"configMap": {
|
||||
"description": "ConfigMap is a reference to a Node's ConfigMap",
|
||||
"$ref": "#/definitions/io.k8s.api.core.v1.ConfigMapNodeConfigSource"
|
||||
}
|
||||
},
|
||||
"x-kubernetes-group-version-kind": [
|
||||
{
|
||||
"group": "",
|
||||
"kind": "NodeConfigSource",
|
||||
"version": "v1"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"io.k8s.api.core.v1.NodeDaemonEndpoints": {
|
||||
"description": "NodeDaemonEndpoints lists ports opened by daemons running on the Node.",
|
||||
|
|
|
@ -18718,16 +18718,40 @@
|
|||
"id": "v1.NodeConfigSource",
|
||||
"description": "NodeConfigSource specifies a source of node configuration. Exactly one subfield (excluding metadata) must be non-nil.",
|
||||
"properties": {
|
||||
"kind": {
|
||||
"configMap": {
|
||||
"$ref": "v1.ConfigMapNodeConfigSource",
|
||||
"description": "ConfigMap is a reference to a Node's ConfigMap"
|
||||
}
|
||||
}
|
||||
},
|
||||
"v1.ConfigMapNodeConfigSource": {
|
||||
"id": "v1.ConfigMapNodeConfigSource",
|
||||
"description": "ConfigMapNodeConfigSource contains the information to reference a ConfigMap as a config source for the Node.",
|
||||
"required": [
|
||||
"namespace",
|
||||
"name",
|
||||
"kubeletConfigKey"
|
||||
],
|
||||
"properties": {
|
||||
"namespace": {
|
||||
"type": "string",
|
||||
"description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds"
|
||||
"description": "Namespace is the metadata.namespace of the referenced ConfigMap. This field is required in all cases."
|
||||
},
|
||||
"apiVersion": {
|
||||
"name": {
|
||||
"type": "string",
|
||||
"description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources"
|
||||
"description": "Name is the metadata.name of the referenced ConfigMap. This field is required in all cases."
|
||||
},
|
||||
"configMapRef": {
|
||||
"$ref": "v1.ObjectReference"
|
||||
"uid": {
|
||||
"type": "string",
|
||||
"description": "UID is the metadata.UID of the referenced ConfigMap. This field is currently reqired in Node.Spec."
|
||||
},
|
||||
"resourceVersion": {
|
||||
"type": "string",
|
||||
"description": "ResourceVersion is the metadata.ResourceVersion of the referenced ConfigMap. This field is forbidden in Node.Spec."
|
||||
},
|
||||
"kubeletConfigKey": {
|
||||
"type": "string",
|
||||
"description": "KubeletConfigKey declares which key of the referenced ConfigMap corresponds to the KubeletConfiguration structure This field is required in all cases."
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
|
@ -116,10 +116,11 @@ func updateNodeWithConfigMap(client clientset.Interface, nodeName string) error
|
|||
}
|
||||
|
||||
node.Spec.ConfigSource = &v1.NodeConfigSource{
|
||||
ConfigMapRef: &v1.ObjectReference{
|
||||
Name: kubeadmconstants.KubeletBaseConfigurationConfigMap,
|
||||
Namespace: metav1.NamespaceSystem,
|
||||
UID: kubeletCfg.UID,
|
||||
ConfigMap: &v1.ConfigMapNodeConfigSource{
|
||||
Name: kubeadmconstants.KubeletBaseConfigurationConfigMap,
|
||||
Namespace: metav1.NamespaceSystem,
|
||||
UID: kubeletCfg.UID,
|
||||
KubeletConfigKey: kubeadmconstants.KubeletBaseConfigurationConfigMapKey,
|
||||
},
|
||||
}
|
||||
|
||||
|
|
|
@ -50,7 +50,7 @@ func TestCreateBaseKubeletConfiguration(t *testing.T) {
|
|||
},
|
||||
Spec: v1.NodeSpec{
|
||||
ConfigSource: &v1.NodeConfigSource{
|
||||
ConfigMapRef: &v1.ObjectReference{
|
||||
ConfigMap: &v1.ConfigMapNodeConfigSource{
|
||||
UID: "",
|
||||
},
|
||||
},
|
||||
|
@ -94,7 +94,7 @@ func TestUpdateNodeWithConfigMap(t *testing.T) {
|
|||
},
|
||||
Spec: v1.NodeSpec{
|
||||
ConfigSource: &v1.NodeConfigSource{
|
||||
ConfigMapRef: &v1.ObjectReference{
|
||||
ConfigMap: &v1.ConfigMapNodeConfigSource{
|
||||
UID: "",
|
||||
},
|
||||
},
|
||||
|
|
|
@ -403,9 +403,6 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
|
|||
<p><a href="#_v1_node">v1.Node</a></p>
|
||||
</li>
|
||||
<li>
|
||||
<p><a href="#_v1_nodeconfigsource">v1.NodeConfigSource</a></p>
|
||||
</li>
|
||||
<li>
|
||||
<p><a href="#_v1_nodelist">v1.NodeList</a></p>
|
||||
</li>
|
||||
<li>
|
||||
|
@ -3661,6 +3658,68 @@ Examples:<br>
|
|||
</tbody>
|
||||
</table>
|
||||
|
||||
</div>
|
||||
<div class="sect2">
|
||||
<h3 id="_v1_configmapnodeconfigsource">v1.ConfigMapNodeConfigSource</h3>
|
||||
<div class="paragraph">
|
||||
<p>ConfigMapNodeConfigSource contains the information to reference a ConfigMap as a config source for the Node.</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">namespace</p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">Namespace is the metadata.namespace of the referenced ConfigMap. This field is required in all cases.</p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">true</p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">string</p></td>
|
||||
<td class="tableblock halign-left valign-top"></td>
|
||||
</tr>
|
||||
<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 is the metadata.name of the referenced ConfigMap. This field is required in all cases.</p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">true</p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">string</p></td>
|
||||
<td class="tableblock halign-left valign-top"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">uid</p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">UID is the metadata.UID of the referenced ConfigMap. This field is currently reqired in Node.Spec.</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>
|
||||
<tr>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">resourceVersion</p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">ResourceVersion is the metadata.ResourceVersion of the referenced ConfigMap. This field is forbidden in Node.Spec.</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>
|
||||
<tr>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">kubeletConfigKey</p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">KubeletConfigKey declares which key of the referenced ConfigMap corresponds to the KubeletConfiguration structure This field is required in all cases.</p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">true</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_endpointport">v1.EndpointPort</h3>
|
||||
|
@ -5830,24 +5889,10 @@ Examples:<br>
|
|||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">kind</p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: <a href="https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds">https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds</a></p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">configMap</p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">ConfigMap is a reference to a Node’s ConfigMap</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>
|
||||
<tr>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">apiVersion</p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: <a href="https://git.k8s.io/community/contributors/devel/api-conventions.md#resources">https://git.k8s.io/community/contributors/devel/api-conventions.md#resources</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>
|
||||
<tr>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">configMapRef</p></td>
|
||||
<td class="tableblock halign-left valign-top"></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_objectreference">v1.ObjectReference</a></p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock"><a href="#_v1_configmapnodeconfigsource">v1.ConfigMapNodeConfigSource</a></p></td>
|
||||
<td class="tableblock halign-left valign-top"></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
|
|
|
@ -60,7 +60,6 @@ func addKnownTypes(scheme *runtime.Scheme) error {
|
|||
&ServiceProxyOptions{},
|
||||
&NodeList{},
|
||||
&Node{},
|
||||
&NodeConfigSource{},
|
||||
&NodeProxyOptions{},
|
||||
&Endpoints{},
|
||||
&EndpointsList{},
|
||||
|
|
|
@ -3257,12 +3257,48 @@ type NodeSpec struct {
|
|||
DoNotUse_ExternalID string
|
||||
}
|
||||
|
||||
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
|
||||
|
||||
// NodeConfigSource specifies a source of node configuration. Exactly one subfield must be non-nil.
|
||||
type NodeConfigSource struct {
|
||||
metav1.TypeMeta
|
||||
ConfigMapRef *ObjectReference
|
||||
ConfigMap *ConfigMapNodeConfigSource
|
||||
}
|
||||
|
||||
type ConfigMapNodeConfigSource struct {
|
||||
// Namespace is the metadata.namespace of the referenced ConfigMap.
|
||||
// This field is required in all cases.
|
||||
Namespace string
|
||||
|
||||
// Name is the metadata.name of the referenced ConfigMap.
|
||||
// This field is required in all cases.
|
||||
Name string
|
||||
|
||||
// UID is the metadata.UID of the referenced ConfigMap.
|
||||
// This field is currently reqired in Node.Spec.
|
||||
// TODO(#61643): This field will be forbidden in Node.Spec when #61643 is resolved.
|
||||
// #61643 changes the behavior of dynamic Kubelet config to respect
|
||||
// ConfigMap updates, and thus removes the ability to pin the Spec to a given UID.
|
||||
// TODO(#56896): This field will be required in Node.Status when #56896 is resolved.
|
||||
// #63314 (the PR that resolves #56896) adds a structured status to the Node
|
||||
// object for reporting information about the config. This status requires UID
|
||||
// and ResourceVersion, so that it represents a fully-explicit description of
|
||||
// the configuration in use, while (see previous TODO) the Spec will be
|
||||
// restricted to namespace/name in #61643.
|
||||
// +optional
|
||||
UID types.UID
|
||||
|
||||
// ResourceVersion is the metadata.ResourceVersion of the referenced ConfigMap.
|
||||
// This field is forbidden in Node.Spec.
|
||||
// TODO(#56896): This field will be required in Node.Status when #56896 is resolved.
|
||||
// #63314 (the PR that resolves #56896) adds a structured status to the Node
|
||||
// object for reporting information about the config. This status requires UID
|
||||
// and ResourceVersion, so that it represents a fully-explicit description of
|
||||
// the configuration in use, while (see previous TODO) the Spec will be
|
||||
// restricted to namespace/name in #61643.
|
||||
// +optional
|
||||
ResourceVersion string
|
||||
|
||||
// KubeletConfigKey declares which key of the referenced ConfigMap corresponds to the KubeletConfiguration structure
|
||||
// This field is required in all cases.
|
||||
KubeletConfigKey string
|
||||
}
|
||||
|
||||
// DaemonEndpoint contains information about a single Daemon endpoint.
|
||||
|
|
|
@ -82,6 +82,8 @@ func RegisterConversions(scheme *runtime.Scheme) error {
|
|||
Convert_core_ConfigMapKeySelector_To_v1_ConfigMapKeySelector,
|
||||
Convert_v1_ConfigMapList_To_core_ConfigMapList,
|
||||
Convert_core_ConfigMapList_To_v1_ConfigMapList,
|
||||
Convert_v1_ConfigMapNodeConfigSource_To_core_ConfigMapNodeConfigSource,
|
||||
Convert_core_ConfigMapNodeConfigSource_To_v1_ConfigMapNodeConfigSource,
|
||||
Convert_v1_ConfigMapProjection_To_core_ConfigMapProjection,
|
||||
Convert_core_ConfigMapProjection_To_v1_ConfigMapProjection,
|
||||
Convert_v1_ConfigMapVolumeSource_To_core_ConfigMapVolumeSource,
|
||||
|
@ -941,6 +943,34 @@ func Convert_core_ConfigMapList_To_v1_ConfigMapList(in *core.ConfigMapList, out
|
|||
return autoConvert_core_ConfigMapList_To_v1_ConfigMapList(in, out, s)
|
||||
}
|
||||
|
||||
func autoConvert_v1_ConfigMapNodeConfigSource_To_core_ConfigMapNodeConfigSource(in *v1.ConfigMapNodeConfigSource, out *core.ConfigMapNodeConfigSource, s conversion.Scope) error {
|
||||
out.Namespace = in.Namespace
|
||||
out.Name = in.Name
|
||||
out.UID = types.UID(in.UID)
|
||||
out.ResourceVersion = in.ResourceVersion
|
||||
out.KubeletConfigKey = in.KubeletConfigKey
|
||||
return nil
|
||||
}
|
||||
|
||||
// Convert_v1_ConfigMapNodeConfigSource_To_core_ConfigMapNodeConfigSource is an autogenerated conversion function.
|
||||
func Convert_v1_ConfigMapNodeConfigSource_To_core_ConfigMapNodeConfigSource(in *v1.ConfigMapNodeConfigSource, out *core.ConfigMapNodeConfigSource, s conversion.Scope) error {
|
||||
return autoConvert_v1_ConfigMapNodeConfigSource_To_core_ConfigMapNodeConfigSource(in, out, s)
|
||||
}
|
||||
|
||||
func autoConvert_core_ConfigMapNodeConfigSource_To_v1_ConfigMapNodeConfigSource(in *core.ConfigMapNodeConfigSource, out *v1.ConfigMapNodeConfigSource, s conversion.Scope) error {
|
||||
out.Namespace = in.Namespace
|
||||
out.Name = in.Name
|
||||
out.UID = types.UID(in.UID)
|
||||
out.ResourceVersion = in.ResourceVersion
|
||||
out.KubeletConfigKey = in.KubeletConfigKey
|
||||
return nil
|
||||
}
|
||||
|
||||
// Convert_core_ConfigMapNodeConfigSource_To_v1_ConfigMapNodeConfigSource is an autogenerated conversion function.
|
||||
func Convert_core_ConfigMapNodeConfigSource_To_v1_ConfigMapNodeConfigSource(in *core.ConfigMapNodeConfigSource, out *v1.ConfigMapNodeConfigSource, s conversion.Scope) error {
|
||||
return autoConvert_core_ConfigMapNodeConfigSource_To_v1_ConfigMapNodeConfigSource(in, out, s)
|
||||
}
|
||||
|
||||
func autoConvert_v1_ConfigMapProjection_To_core_ConfigMapProjection(in *v1.ConfigMapProjection, out *core.ConfigMapProjection, s conversion.Scope) error {
|
||||
if err := Convert_v1_LocalObjectReference_To_core_LocalObjectReference(&in.LocalObjectReference, &out.LocalObjectReference, s); err != nil {
|
||||
return err
|
||||
|
@ -2586,7 +2616,7 @@ func Convert_core_NodeCondition_To_v1_NodeCondition(in *core.NodeCondition, out
|
|||
}
|
||||
|
||||
func autoConvert_v1_NodeConfigSource_To_core_NodeConfigSource(in *v1.NodeConfigSource, out *core.NodeConfigSource, s conversion.Scope) error {
|
||||
out.ConfigMapRef = (*core.ObjectReference)(unsafe.Pointer(in.ConfigMapRef))
|
||||
out.ConfigMap = (*core.ConfigMapNodeConfigSource)(unsafe.Pointer(in.ConfigMap))
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -2596,7 +2626,7 @@ func Convert_v1_NodeConfigSource_To_core_NodeConfigSource(in *v1.NodeConfigSourc
|
|||
}
|
||||
|
||||
func autoConvert_core_NodeConfigSource_To_v1_NodeConfigSource(in *core.NodeConfigSource, out *v1.NodeConfigSource, s conversion.Scope) error {
|
||||
out.ConfigMapRef = (*v1.ObjectReference)(unsafe.Pointer(in.ConfigMapRef))
|
||||
out.ConfigMap = (*v1.ConfigMapNodeConfigSource)(unsafe.Pointer(in.ConfigMap))
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
|
@ -4116,7 +4116,7 @@ func ValidateNodeUpdate(node, oldNode *core.Node) field.ErrorList {
|
|||
// Allow updates to Node.Spec.ConfigSource if DynamicKubeletConfig feature gate is enabled
|
||||
if utilfeature.DefaultFeatureGate.Enabled(features.DynamicKubeletConfig) {
|
||||
if node.Spec.ConfigSource != nil {
|
||||
allErrs = append(allErrs, validateNodeConfigSource(node.Spec.ConfigSource, field.NewPath("spec", "configSource"))...)
|
||||
allErrs = append(allErrs, validateNodeConfigSourceSpec(node.Spec.ConfigSource, field.NewPath("spec", "configSource"))...)
|
||||
}
|
||||
oldNode.Spec.ConfigSource = node.Spec.ConfigSource
|
||||
}
|
||||
|
@ -4131,15 +4131,13 @@ func ValidateNodeUpdate(node, oldNode *core.Node) field.ErrorList {
|
|||
return allErrs
|
||||
}
|
||||
|
||||
func validateNodeConfigSource(source *core.NodeConfigSource, fldPath *field.Path) field.ErrorList {
|
||||
// validation specific to Node.Spec.ConfigSource
|
||||
func validateNodeConfigSourceSpec(source *core.NodeConfigSource, fldPath *field.Path) field.ErrorList {
|
||||
allErrs := field.ErrorList{}
|
||||
count := int(0)
|
||||
if ref := source.ConfigMapRef; ref != nil {
|
||||
if source.ConfigMap != nil {
|
||||
count++
|
||||
// name, namespace, and UID must all be non-empty for ConfigMapRef
|
||||
if ref.Name == "" || ref.Namespace == "" || string(ref.UID) == "" {
|
||||
allErrs = append(allErrs, field.Invalid(fldPath.Child("configMapRef"), ref, "name, namespace, and UID must all be non-empty"))
|
||||
}
|
||||
allErrs = append(allErrs, validateConfigMapNodeConfigSourceSpec(source.ConfigMap, fldPath.Child("configMap"))...)
|
||||
}
|
||||
// add more subfields here in the future as they are added to NodeConfigSource
|
||||
|
||||
|
@ -4150,6 +4148,50 @@ func validateNodeConfigSource(source *core.NodeConfigSource, fldPath *field.Path
|
|||
return allErrs
|
||||
}
|
||||
|
||||
// validation specific to Node.Spec.ConfigSource.ConfigMap
|
||||
func validateConfigMapNodeConfigSourceSpec(source *core.ConfigMapNodeConfigSource, fldPath *field.Path) field.ErrorList {
|
||||
allErrs := field.ErrorList{}
|
||||
// TODO(#61643): Prevent ref.UID from being set here when we switch from requiring UID to respecting all ConfigMap updates
|
||||
if string(source.UID) == "" {
|
||||
allErrs = append(allErrs, field.Required(fldPath.Child("uid"), "uid must be set in spec"))
|
||||
}
|
||||
// resourceVersion must not be set in spec
|
||||
if source.ResourceVersion != "" {
|
||||
allErrs = append(allErrs, field.Forbidden(fldPath.Child("resourceVersion"), "resourceVersion must not be set in spec"))
|
||||
}
|
||||
return append(allErrs, validateConfigMapNodeConfigSource(source, fldPath)...)
|
||||
}
|
||||
|
||||
// common validation
|
||||
func validateConfigMapNodeConfigSource(source *core.ConfigMapNodeConfigSource, fldPath *field.Path) field.ErrorList {
|
||||
allErrs := field.ErrorList{}
|
||||
// validate target configmap namespace
|
||||
if source.Namespace == "" {
|
||||
allErrs = append(allErrs, field.Required(fldPath.Child("namespace"), "namespace must be set in spec"))
|
||||
} else {
|
||||
for _, msg := range ValidateNameFunc(ValidateNamespaceName)(source.Namespace, false) {
|
||||
allErrs = append(allErrs, field.Invalid(fldPath.Child("namespace"), source.Namespace, msg))
|
||||
}
|
||||
}
|
||||
// validate target configmap name
|
||||
if source.Name == "" {
|
||||
allErrs = append(allErrs, field.Required(fldPath.Child("name"), "name must be set in spec"))
|
||||
} else {
|
||||
for _, msg := range ValidateNameFunc(ValidateConfigMapName)(source.Name, false) {
|
||||
allErrs = append(allErrs, field.Invalid(fldPath.Child("name"), source.Name, msg))
|
||||
}
|
||||
}
|
||||
// validate kubeletConfigKey against rules for configMap key names
|
||||
if source.KubeletConfigKey == "" {
|
||||
allErrs = append(allErrs, field.Required(fldPath.Child("kubeletConfigKey"), "kubeletConfigKey must be set in spec"))
|
||||
} else {
|
||||
for _, msg := range validation.IsConfigMapKey(source.KubeletConfigKey) {
|
||||
allErrs = append(allErrs, field.Invalid(fldPath.Child("kubeletConfigKey"), source.KubeletConfigKey, msg))
|
||||
}
|
||||
}
|
||||
return allErrs
|
||||
}
|
||||
|
||||
// Validate compute resource typename.
|
||||
// Refer to docs/design/resources.md for more details.
|
||||
func validateResourceName(value string, fldPath *field.Path) field.ErrorList {
|
||||
|
|
|
@ -631,6 +631,22 @@ func (in *ConfigMapList) DeepCopyObject() runtime.Object {
|
|||
return nil
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *ConfigMapNodeConfigSource) DeepCopyInto(out *ConfigMapNodeConfigSource) {
|
||||
*out = *in
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ConfigMapNodeConfigSource.
|
||||
func (in *ConfigMapNodeConfigSource) DeepCopy() *ConfigMapNodeConfigSource {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(ConfigMapNodeConfigSource)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *ConfigMapProjection) DeepCopyInto(out *ConfigMapProjection) {
|
||||
*out = *in
|
||||
|
@ -2364,13 +2380,12 @@ func (in *NodeCondition) DeepCopy() *NodeCondition {
|
|||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *NodeConfigSource) DeepCopyInto(out *NodeConfigSource) {
|
||||
*out = *in
|
||||
out.TypeMeta = in.TypeMeta
|
||||
if in.ConfigMapRef != nil {
|
||||
in, out := &in.ConfigMapRef, &out.ConfigMapRef
|
||||
if in.ConfigMap != nil {
|
||||
in, out := &in.ConfigMap, &out.ConfigMap
|
||||
if *in == nil {
|
||||
*out = nil
|
||||
} else {
|
||||
*out = new(ObjectReference)
|
||||
*out = new(ConfigMapNodeConfigSource)
|
||||
**out = **in
|
||||
}
|
||||
}
|
||||
|
@ -2387,14 +2402,6 @@ func (in *NodeConfigSource) DeepCopy() *NodeConfigSource {
|
|||
return out
|
||||
}
|
||||
|
||||
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
|
||||
func (in *NodeConfigSource) DeepCopyObject() runtime.Object {
|
||||
if c := in.DeepCopy(); c != nil {
|
||||
return c
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *NodeDaemonEndpoints) DeepCopyInto(out *NodeDaemonEndpoints) {
|
||||
*out = *in
|
||||
|
|
|
@ -17,6 +17,7 @@ go_library(
|
|||
],
|
||||
importpath = "k8s.io/kubernetes/pkg/kubelet/apis/kubeletconfig",
|
||||
deps = [
|
||||
"//vendor/k8s.io/api/core/v1:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/runtime/schema:go_default_library",
|
||||
|
|
|
@ -46,6 +46,7 @@ func addKnownTypes(scheme *runtime.Scheme) error {
|
|||
// TODO this will get cleaned up with the scheme types are fixed
|
||||
scheme.AddKnownTypes(SchemeGroupVersion,
|
||||
&KubeletConfiguration{},
|
||||
&SerializedNodeConfigSource{},
|
||||
)
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -17,6 +17,7 @@ limitations under the License.
|
|||
package kubeletconfig
|
||||
|
||||
import (
|
||||
"k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
|
@ -333,3 +334,15 @@ type KubeletAnonymousAuthentication struct {
|
|||
// Anonymous requests have a username of system:anonymous, and a group name of system:unauthenticated.
|
||||
Enabled bool
|
||||
}
|
||||
|
||||
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
|
||||
|
||||
// SerializedNodeConfigSource allows us to serialize NodeConfigSource
|
||||
// This type is used internally by the Kubelet for tracking checkpointed dynamic configs.
|
||||
// It exists in the kubeletconfig API group because it is classified as a versioned input to the Kubelet.
|
||||
type SerializedNodeConfigSource struct {
|
||||
metav1.TypeMeta
|
||||
// Source is the source that we are serializing
|
||||
// +optional
|
||||
Source v1.NodeConfigSource
|
||||
}
|
||||
|
|
|
@ -23,6 +23,7 @@ go_library(
|
|||
"//pkg/kubelet/types:go_default_library",
|
||||
"//pkg/master/ports:go_default_library",
|
||||
"//pkg/util/pointer:go_default_library",
|
||||
"//vendor/k8s.io/api/core/v1:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/conversion:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library",
|
||||
|
|
|
@ -45,6 +45,7 @@ func init() {
|
|||
func addKnownTypes(scheme *runtime.Scheme) error {
|
||||
scheme.AddKnownTypes(SchemeGroupVersion,
|
||||
&KubeletConfiguration{},
|
||||
&SerializedNodeConfigSource{},
|
||||
)
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -17,6 +17,7 @@ limitations under the License.
|
|||
package v1beta1
|
||||
|
||||
import (
|
||||
"k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
|
@ -512,3 +513,15 @@ type KubeletAnonymousAuthentication struct {
|
|||
// +optional
|
||||
Enabled *bool `json:"enabled,omitempty"`
|
||||
}
|
||||
|
||||
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
|
||||
|
||||
// SerializedNodeConfigSource allows us to serialize v1.NodeConfigSource.
|
||||
// This type is used internally by the Kubelet for tracking checkpointed dynamic configs.
|
||||
// It exists in the kubeletconfig API group because it is classified as a versioned input to the Kubelet.
|
||||
type SerializedNodeConfigSource struct {
|
||||
metav1.TypeMeta `json:",inline"`
|
||||
// Source is the source that we are serializing
|
||||
// +optional
|
||||
Source v1.NodeConfigSource `json:"source,omitempty" protobuf:"bytes,1,opt,name=source"`
|
||||
}
|
||||
|
|
|
@ -51,6 +51,8 @@ func RegisterConversions(scheme *runtime.Scheme) error {
|
|||
Convert_kubeletconfig_KubeletWebhookAuthorization_To_v1beta1_KubeletWebhookAuthorization,
|
||||
Convert_v1beta1_KubeletX509Authentication_To_kubeletconfig_KubeletX509Authentication,
|
||||
Convert_kubeletconfig_KubeletX509Authentication_To_v1beta1_KubeletX509Authentication,
|
||||
Convert_v1beta1_SerializedNodeConfigSource_To_kubeletconfig_SerializedNodeConfigSource,
|
||||
Convert_kubeletconfig_SerializedNodeConfigSource_To_v1beta1_SerializedNodeConfigSource,
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -453,3 +455,23 @@ func autoConvert_kubeletconfig_KubeletX509Authentication_To_v1beta1_KubeletX509A
|
|||
func Convert_kubeletconfig_KubeletX509Authentication_To_v1beta1_KubeletX509Authentication(in *kubeletconfig.KubeletX509Authentication, out *KubeletX509Authentication, s conversion.Scope) error {
|
||||
return autoConvert_kubeletconfig_KubeletX509Authentication_To_v1beta1_KubeletX509Authentication(in, out, s)
|
||||
}
|
||||
|
||||
func autoConvert_v1beta1_SerializedNodeConfigSource_To_kubeletconfig_SerializedNodeConfigSource(in *SerializedNodeConfigSource, out *kubeletconfig.SerializedNodeConfigSource, s conversion.Scope) error {
|
||||
out.Source = in.Source
|
||||
return nil
|
||||
}
|
||||
|
||||
// Convert_v1beta1_SerializedNodeConfigSource_To_kubeletconfig_SerializedNodeConfigSource is an autogenerated conversion function.
|
||||
func Convert_v1beta1_SerializedNodeConfigSource_To_kubeletconfig_SerializedNodeConfigSource(in *SerializedNodeConfigSource, out *kubeletconfig.SerializedNodeConfigSource, s conversion.Scope) error {
|
||||
return autoConvert_v1beta1_SerializedNodeConfigSource_To_kubeletconfig_SerializedNodeConfigSource(in, out, s)
|
||||
}
|
||||
|
||||
func autoConvert_kubeletconfig_SerializedNodeConfigSource_To_v1beta1_SerializedNodeConfigSource(in *kubeletconfig.SerializedNodeConfigSource, out *SerializedNodeConfigSource, s conversion.Scope) error {
|
||||
out.Source = in.Source
|
||||
return nil
|
||||
}
|
||||
|
||||
// Convert_kubeletconfig_SerializedNodeConfigSource_To_v1beta1_SerializedNodeConfigSource is an autogenerated conversion function.
|
||||
func Convert_kubeletconfig_SerializedNodeConfigSource_To_v1beta1_SerializedNodeConfigSource(in *kubeletconfig.SerializedNodeConfigSource, out *SerializedNodeConfigSource, s conversion.Scope) error {
|
||||
return autoConvert_kubeletconfig_SerializedNodeConfigSource_To_v1beta1_SerializedNodeConfigSource(in, out, s)
|
||||
}
|
||||
|
|
|
@ -426,3 +426,29 @@ func (in *KubeletX509Authentication) DeepCopy() *KubeletX509Authentication {
|
|||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *SerializedNodeConfigSource) DeepCopyInto(out *SerializedNodeConfigSource) {
|
||||
*out = *in
|
||||
out.TypeMeta = in.TypeMeta
|
||||
in.Source.DeepCopyInto(&out.Source)
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SerializedNodeConfigSource.
|
||||
func (in *SerializedNodeConfigSource) DeepCopy() *SerializedNodeConfigSource {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(SerializedNodeConfigSource)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
|
||||
func (in *SerializedNodeConfigSource) DeepCopyObject() runtime.Object {
|
||||
if c := in.DeepCopy(); c != nil {
|
||||
return c
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -246,3 +246,29 @@ func (in *KubeletX509Authentication) DeepCopy() *KubeletX509Authentication {
|
|||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *SerializedNodeConfigSource) DeepCopyInto(out *SerializedNodeConfigSource) {
|
||||
*out = *in
|
||||
out.TypeMeta = in.TypeMeta
|
||||
in.Source.DeepCopyInto(&out.Source)
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SerializedNodeConfigSource.
|
||||
func (in *SerializedNodeConfigSource) DeepCopy() *SerializedNodeConfigSource {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(SerializedNodeConfigSource)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
|
||||
func (in *SerializedNodeConfigSource) DeepCopyObject() runtime.Object {
|
||||
if c := in.DeepCopy(); c != nil {
|
||||
return c
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -19,12 +19,12 @@ go_library(
|
|||
"//pkg/kubelet/kubeletconfig/checkpoint:go_default_library",
|
||||
"//pkg/kubelet/kubeletconfig/checkpoint/store:go_default_library",
|
||||
"//pkg/kubelet/kubeletconfig/status:go_default_library",
|
||||
"//pkg/kubelet/kubeletconfig/util/equal:go_default_library",
|
||||
"//pkg/kubelet/kubeletconfig/util/log:go_default_library",
|
||||
"//pkg/kubelet/kubeletconfig/util/panic:go_default_library",
|
||||
"//pkg/util/filesystem:go_default_library",
|
||||
"//vendor/github.com/golang/glog:go_default_library",
|
||||
"//vendor/k8s.io/api/core/v1:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/api/equality:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/fields:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library",
|
||||
|
|
|
@ -31,7 +31,9 @@ go_library(
|
|||
],
|
||||
importpath = "k8s.io/kubernetes/pkg/kubelet/kubeletconfig/checkpoint",
|
||||
deps = [
|
||||
"//pkg/api/legacyscheme:go_default_library",
|
||||
"//pkg/kubelet/apis/kubeletconfig:go_default_library",
|
||||
"//pkg/kubelet/apis/kubeletconfig/scheme:go_default_library",
|
||||
"//pkg/kubelet/apis/kubeletconfig/v1beta1:go_default_library",
|
||||
"//pkg/kubelet/kubeletconfig/status:go_default_library",
|
||||
"//pkg/kubelet/kubeletconfig/util/codec:go_default_library",
|
||||
"//pkg/kubelet/kubeletconfig/util/log:go_default_library",
|
||||
|
|
|
@ -24,7 +24,9 @@ import (
|
|||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
clientset "k8s.io/client-go/kubernetes"
|
||||
"k8s.io/kubernetes/pkg/api/legacyscheme"
|
||||
kubeletconfiginternal "k8s.io/kubernetes/pkg/kubelet/apis/kubeletconfig"
|
||||
"k8s.io/kubernetes/pkg/kubelet/apis/kubeletconfig/scheme"
|
||||
kubeletconfigv1beta1 "k8s.io/kubernetes/pkg/kubelet/apis/kubeletconfig/v1beta1"
|
||||
"k8s.io/kubernetes/pkg/kubelet/kubeletconfig/status"
|
||||
utilcodec "k8s.io/kubernetes/pkg/kubelet/kubeletconfig/util/codec"
|
||||
utillog "k8s.io/kubernetes/pkg/kubelet/kubeletconfig/util/log"
|
||||
|
@ -46,6 +48,8 @@ type Payload interface {
|
|||
type RemoteConfigSource interface {
|
||||
// UID returns a globally unique identifier of the source described by the remote config source object
|
||||
UID() string
|
||||
// KubeletFilename returns the name of the Kubelet config file as it should appear in the keys of Payload.Files()
|
||||
KubeletFilename() string
|
||||
// APIPath returns the API path to the remote resource, e.g. its SelfLink
|
||||
APIPath() string
|
||||
// Download downloads the remote config source object returns a Payload backed by the object,
|
||||
|
@ -60,25 +64,18 @@ type RemoteConfigSource interface {
|
|||
object() interface{}
|
||||
}
|
||||
|
||||
// NewRemoteConfigSource constructs a RemoteConfigSource from a v1/NodeConfigSource object, or returns
|
||||
// a sanitized failure reason and an error if the `source` is blatantly invalid.
|
||||
// NewRemoteConfigSource constructs a RemoteConfigSource from a v1/NodeConfigSource object
|
||||
// You should only call this with a non-nil config source.
|
||||
// Note that the API server validates Node.Spec.ConfigSource.
|
||||
func NewRemoteConfigSource(source *apiv1.NodeConfigSource) (RemoteConfigSource, string, error) {
|
||||
// exactly one subfield of the config source must be non-nil, toady ConfigMapRef is the only reference
|
||||
if source.ConfigMapRef == nil {
|
||||
// NOTE: Even though the API server validates the config, we check whether all *known* fields are
|
||||
// nil here, so that if a new API server allows a new config source type, old clients can send
|
||||
// an error message rather than crashing due to a nil pointer dereference.
|
||||
|
||||
// exactly one reference subfield of the config source must be non-nil, today ConfigMap is the only reference subfield
|
||||
if source.ConfigMap == nil {
|
||||
return nil, status.FailSyncReasonAllNilSubfields, fmt.Errorf("%s, NodeConfigSource was: %#v", status.FailSyncReasonAllNilSubfields, source)
|
||||
}
|
||||
|
||||
// validate the NodeConfigSource:
|
||||
|
||||
// at this point we know we're using the ConfigMapRef subfield
|
||||
ref := source.ConfigMapRef
|
||||
|
||||
// name, namespace, and UID must all be non-empty for ConfigMapRef
|
||||
if ref.Name == "" || ref.Namespace == "" || string(ref.UID) == "" {
|
||||
return nil, status.FailSyncReasonPartialObjectReference, fmt.Errorf("%s, ObjectReference was: %#v", status.FailSyncReasonPartialObjectReference, ref)
|
||||
}
|
||||
|
||||
return &remoteConfigMap{source}, "", nil
|
||||
}
|
||||
|
||||
|
@ -86,21 +83,25 @@ func NewRemoteConfigSource(source *apiv1.NodeConfigSource) (RemoteConfigSource,
|
|||
// e.g. the metadata stored by checkpoint/store/fsstore.go
|
||||
func DecodeRemoteConfigSource(data []byte) (RemoteConfigSource, error) {
|
||||
// decode the remote config source
|
||||
obj, err := runtime.Decode(legacyscheme.Codecs.UniversalDecoder(), data)
|
||||
_, codecs, err := scheme.NewSchemeAndCodecs()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
obj, err := runtime.Decode(codecs.UniversalDecoder(), data)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to decode, error: %v", err)
|
||||
}
|
||||
|
||||
// for now we assume we are trying to load an apiv1.NodeConfigSource,
|
||||
// for now we assume we are trying to load an kubeletconfigv1beta1.SerializedNodeConfigSource,
|
||||
// this may need to be extended if e.g. a new version of the api is born
|
||||
|
||||
// convert it to the external NodeConfigSource type, so we're consistently working with the external type outside of the on-disk representation
|
||||
cs := &apiv1.NodeConfigSource{}
|
||||
err = legacyscheme.Scheme.Convert(obj, cs, nil)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to convert decoded object into a v1 NodeConfigSource, error: %v", err)
|
||||
cs, ok := obj.(*kubeletconfiginternal.SerializedNodeConfigSource)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("failed to cast decoded remote config source to *k8s.io/kubernetes/pkg/kubelet/apis/kubeletconfig.SerializedNodeConfigSource")
|
||||
}
|
||||
source, _, err := NewRemoteConfigSource(cs)
|
||||
|
||||
// we use the v1.NodeConfigSource type on internal and external, so no need to convert to external here
|
||||
source, _, err := NewRemoteConfigSource(&cs.Source)
|
||||
return source, err
|
||||
}
|
||||
|
||||
|
@ -121,32 +122,36 @@ type remoteConfigMap struct {
|
|||
var _ RemoteConfigSource = (*remoteConfigMap)(nil)
|
||||
|
||||
func (r *remoteConfigMap) UID() string {
|
||||
return string(r.source.ConfigMapRef.UID)
|
||||
return string(r.source.ConfigMap.UID)
|
||||
}
|
||||
|
||||
func (r *remoteConfigMap) KubeletFilename() string {
|
||||
return r.source.ConfigMap.KubeletConfigKey
|
||||
}
|
||||
|
||||
const configMapAPIPathFmt = "/api/v1/namespaces/%s/configmaps/%s"
|
||||
|
||||
func (r *remoteConfigMap) APIPath() string {
|
||||
ref := r.source.ConfigMapRef
|
||||
ref := r.source.ConfigMap
|
||||
return fmt.Sprintf(configMapAPIPathFmt, ref.Namespace, ref.Name)
|
||||
}
|
||||
|
||||
func (r *remoteConfigMap) Download(client clientset.Interface) (Payload, string, error) {
|
||||
var reason string
|
||||
uid := string(r.source.ConfigMapRef.UID)
|
||||
uid := string(r.source.ConfigMap.UID)
|
||||
|
||||
utillog.Infof("attempting to download ConfigMap with UID %q", uid)
|
||||
|
||||
// get the ConfigMap via namespace/name, there doesn't seem to be a way to get it by UID
|
||||
cm, err := client.CoreV1().ConfigMaps(r.source.ConfigMapRef.Namespace).Get(r.source.ConfigMapRef.Name, metav1.GetOptions{})
|
||||
cm, err := client.CoreV1().ConfigMaps(r.source.ConfigMap.Namespace).Get(r.source.ConfigMap.Name, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
reason = fmt.Sprintf(status.FailSyncReasonDownloadFmt, r.APIPath())
|
||||
return nil, reason, fmt.Errorf("%s, error: %v", reason, err)
|
||||
}
|
||||
|
||||
// ensure that UID matches the UID on the reference, the ObjectReference must be unambiguous
|
||||
if r.source.ConfigMapRef.UID != cm.UID {
|
||||
reason = fmt.Sprintf(status.FailSyncReasonUIDMismatchFmt, r.source.ConfigMapRef.UID, r.APIPath(), cm.UID)
|
||||
// ensure that UID matches the UID on the source
|
||||
if r.source.ConfigMap.UID != cm.UID {
|
||||
reason = fmt.Sprintf(status.FailSyncReasonUIDMismatchFmt, r.source.ConfigMap.UID, r.APIPath(), cm.UID)
|
||||
return nil, reason, fmt.Errorf(reason)
|
||||
}
|
||||
|
||||
|
@ -161,11 +166,12 @@ func (r *remoteConfigMap) Download(client clientset.Interface) (Payload, string,
|
|||
}
|
||||
|
||||
func (r *remoteConfigMap) Encode() ([]byte, error) {
|
||||
encoder, err := utilcodec.NewYAMLEncoder(apiv1.GroupName)
|
||||
encoder, err := utilcodec.NewKubeletconfigYAMLEncoder(kubeletconfigv1beta1.SchemeGroupVersion)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
data, err := runtime.Encode(encoder, r.source)
|
||||
|
||||
data, err := runtime.Encode(encoder, &kubeletconfigv1beta1.SerializedNodeConfigSource{Source: *r.source})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
|
@ -36,33 +36,30 @@ func TestNewRemoteConfigSource(t *testing.T) {
|
|||
expect RemoteConfigSource
|
||||
err string
|
||||
}{
|
||||
// all NodeConfigSource subfields nil
|
||||
{"all NodeConfigSource subfields nil",
|
||||
&apiv1.NodeConfigSource{}, nil, "exactly one subfield must be non-nil"},
|
||||
{"ConfigMapRef: empty name, namespace, and UID",
|
||||
&apiv1.NodeConfigSource{ConfigMapRef: &apiv1.ObjectReference{}}, nil, "invalid ObjectReference"},
|
||||
// ConfigMapRef: empty name and namespace
|
||||
{"ConfigMapRef: empty name and namespace",
|
||||
&apiv1.NodeConfigSource{ConfigMapRef: &apiv1.ObjectReference{UID: "uid"}}, nil, "invalid ObjectReference"},
|
||||
// ConfigMapRef: empty name and UID
|
||||
{"ConfigMapRef: empty name and UID",
|
||||
&apiv1.NodeConfigSource{ConfigMapRef: &apiv1.ObjectReference{Namespace: "namespace"}}, nil, "invalid ObjectReference"},
|
||||
// ConfigMapRef: empty namespace and UID
|
||||
{"ConfigMapRef: empty namespace and UID",
|
||||
&apiv1.NodeConfigSource{ConfigMapRef: &apiv1.ObjectReference{Name: "name"}}, nil, "invalid ObjectReference"},
|
||||
// ConfigMapRef: empty UID
|
||||
{"ConfigMapRef: empty namespace and UID",
|
||||
&apiv1.NodeConfigSource{ConfigMapRef: &apiv1.ObjectReference{Name: "name", Namespace: "namespace"}}, nil, "invalid ObjectReference"},
|
||||
// ConfigMapRef: empty namespace
|
||||
{"ConfigMapRef: empty namespace and UID",
|
||||
&apiv1.NodeConfigSource{ConfigMapRef: &apiv1.ObjectReference{Name: "name", UID: "uid"}}, nil, "invalid ObjectReference"},
|
||||
// ConfigMapRef: empty name
|
||||
{"ConfigMapRef: empty namespace and UID",
|
||||
&apiv1.NodeConfigSource{ConfigMapRef: &apiv1.ObjectReference{Namespace: "namespace", UID: "uid"}}, nil, "invalid ObjectReference"},
|
||||
// ConfigMapRef: valid reference
|
||||
{"ConfigMapRef: valid reference",
|
||||
&apiv1.NodeConfigSource{ConfigMapRef: &apiv1.ObjectReference{Name: "name", Namespace: "namespace", UID: "uid"}},
|
||||
&remoteConfigMap{&apiv1.NodeConfigSource{ConfigMapRef: &apiv1.ObjectReference{Name: "name", Namespace: "namespace", UID: "uid"}}}, ""},
|
||||
{
|
||||
desc: "all NodeConfigSource subfields nil",
|
||||
source: &apiv1.NodeConfigSource{},
|
||||
expect: nil,
|
||||
err: "exactly one subfield must be non-nil",
|
||||
},
|
||||
{
|
||||
desc: "ConfigMap: valid reference",
|
||||
source: &apiv1.NodeConfigSource{
|
||||
ConfigMap: &apiv1.ConfigMapNodeConfigSource{
|
||||
Name: "name",
|
||||
Namespace: "namespace",
|
||||
UID: "uid",
|
||||
KubeletConfigKey: "kubelet",
|
||||
}},
|
||||
expect: &remoteConfigMap{&apiv1.NodeConfigSource{
|
||||
ConfigMap: &apiv1.ConfigMapNodeConfigSource{
|
||||
Name: "name",
|
||||
Namespace: "namespace",
|
||||
UID: "uid",
|
||||
KubeletConfigKey: "kubelet",
|
||||
}}},
|
||||
err: "",
|
||||
},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
|
@ -82,7 +79,12 @@ func TestNewRemoteConfigSource(t *testing.T) {
|
|||
|
||||
func TestRemoteConfigMapUID(t *testing.T) {
|
||||
const expect = "uid"
|
||||
source, _, err := NewRemoteConfigSource(&apiv1.NodeConfigSource{ConfigMapRef: &apiv1.ObjectReference{Name: "name", Namespace: "namespace", UID: expect}})
|
||||
source, _, err := NewRemoteConfigSource(&apiv1.NodeConfigSource{ConfigMap: &apiv1.ConfigMapNodeConfigSource{
|
||||
Name: "name",
|
||||
Namespace: "namespace",
|
||||
UID: expect,
|
||||
KubeletConfigKey: "kubelet",
|
||||
}})
|
||||
if err != nil {
|
||||
t.Fatalf("error constructing remote config source: %v", err)
|
||||
}
|
||||
|
@ -93,14 +95,22 @@ func TestRemoteConfigMapUID(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestRemoteConfigMapAPIPath(t *testing.T) {
|
||||
const namespace = "namespace"
|
||||
const name = "name"
|
||||
source, _, err := NewRemoteConfigSource(&apiv1.NodeConfigSource{ConfigMapRef: &apiv1.ObjectReference{Name: name, Namespace: namespace, UID: "uid"}})
|
||||
const (
|
||||
name = "name"
|
||||
namespace = "namespace"
|
||||
)
|
||||
source, _, err := NewRemoteConfigSource(&apiv1.NodeConfigSource{ConfigMap: &apiv1.ConfigMapNodeConfigSource{
|
||||
Name: name,
|
||||
Namespace: namespace,
|
||||
UID: "uid",
|
||||
KubeletConfigKey: "kubelet",
|
||||
}})
|
||||
if err != nil {
|
||||
t.Fatalf("error constructing remote config source: %v", err)
|
||||
}
|
||||
expect := fmt.Sprintf(configMapAPIPathFmt, namespace, name)
|
||||
path := source.APIPath()
|
||||
|
||||
if expect != path {
|
||||
t.Errorf("expect %q, but got %q", expect, path)
|
||||
}
|
||||
|
@ -133,18 +143,39 @@ func TestRemoteConfigMapDownload(t *testing.T) {
|
|||
expect Payload
|
||||
err string
|
||||
}{
|
||||
// object doesn't exist
|
||||
{"object doesn't exist",
|
||||
makeSource(&apiv1.NodeConfigSource{ConfigMapRef: &apiv1.ObjectReference{Name: "bogus", Namespace: "namespace", UID: "bogus"}}),
|
||||
nil, "not found"},
|
||||
// UID of downloaded object doesn't match UID of referent found via namespace/name
|
||||
{"UID is incorrect for namespace/name",
|
||||
makeSource(&apiv1.NodeConfigSource{ConfigMapRef: &apiv1.ObjectReference{Name: "name", Namespace: "namespace", UID: "bogus"}}),
|
||||
nil, "does not match"},
|
||||
// successful download
|
||||
{"object exists and reference is correct",
|
||||
makeSource(&apiv1.NodeConfigSource{ConfigMapRef: &apiv1.ObjectReference{Name: "name", Namespace: "namespace", UID: "uid"}}),
|
||||
payload, ""},
|
||||
{
|
||||
desc: "object doesn't exist",
|
||||
source: makeSource(&apiv1.NodeConfigSource{ConfigMap: &apiv1.ConfigMapNodeConfigSource{
|
||||
Name: "bogus",
|
||||
Namespace: "namespace",
|
||||
UID: "bogus",
|
||||
KubeletConfigKey: "kubelet",
|
||||
}}),
|
||||
expect: nil,
|
||||
err: "not found",
|
||||
},
|
||||
{
|
||||
desc: "UID is incorrect for namespace/name",
|
||||
source: makeSource(&apiv1.NodeConfigSource{ConfigMap: &apiv1.ConfigMapNodeConfigSource{
|
||||
Name: "name",
|
||||
Namespace: "namespace",
|
||||
UID: "bogus",
|
||||
KubeletConfigKey: "kubelet",
|
||||
}}),
|
||||
expect: nil,
|
||||
err: "does not match",
|
||||
},
|
||||
{
|
||||
desc: "object exists and reference is correct",
|
||||
source: makeSource(&apiv1.NodeConfigSource{ConfigMap: &apiv1.ConfigMapNodeConfigSource{
|
||||
Name: "name",
|
||||
Namespace: "namespace",
|
||||
UID: "uid",
|
||||
KubeletConfigKey: "kubelet",
|
||||
}}),
|
||||
expect: payload,
|
||||
err: "",
|
||||
},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
|
@ -173,10 +204,12 @@ func TestEqualRemoteConfigSources(t *testing.T) {
|
|||
{"a nil", nil, &remoteConfigMap{}, false},
|
||||
{"b nil", &remoteConfigMap{}, nil, false},
|
||||
{"neither nil, equal", &remoteConfigMap{}, &remoteConfigMap{}, true},
|
||||
{"neither nil, not equal",
|
||||
&remoteConfigMap{&apiv1.NodeConfigSource{ConfigMapRef: &apiv1.ObjectReference{Name: "a"}}},
|
||||
&remoteConfigMap{},
|
||||
false},
|
||||
{
|
||||
desc: "neither nil, not equal",
|
||||
a: &remoteConfigMap{&apiv1.NodeConfigSource{ConfigMap: &apiv1.ConfigMapNodeConfigSource{Name: "a"}}},
|
||||
b: &remoteConfigMap{&apiv1.NodeConfigSource{ConfigMap: &apiv1.ConfigMapNodeConfigSource{KubeletConfigKey: "kubelet"}}},
|
||||
expect: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
|
|
|
@ -35,7 +35,6 @@ const (
|
|||
lastKnownGoodFile = "last-known-good"
|
||||
|
||||
checkpointsDir = "checkpoints"
|
||||
kubeletKey = "kubelet" // TODO(mtaufen): eventually the API will have a way to parameterize the kubelet file name, and then we can remove this
|
||||
)
|
||||
|
||||
// fsStore is for tracking checkpoints in the local filesystem, implements Store
|
||||
|
@ -101,7 +100,7 @@ func (s *fsStore) Load(source checkpoint.RemoteConfigSource) (*kubeletconfig.Kub
|
|||
}
|
||||
// load the kubelet config file
|
||||
utillog.Infof("loading kubelet configuration checkpoint for source %s", sourceFmt)
|
||||
loader, err := configfiles.NewFsLoader(s.fs, filepath.Join(s.checkpointPath(source.UID()), kubeletKey))
|
||||
loader, err := configfiles.NewFsLoader(s.fs, filepath.Join(s.checkpointPath(source.UID()), source.KubeletFilename()))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
|
@ -125,7 +125,12 @@ func TestFsStoreExists(t *testing.T) {
|
|||
for _, c := range cases {
|
||||
t.Run(c.desc, func(t *testing.T) {
|
||||
source, _, err := checkpoint.NewRemoteConfigSource(&apiv1.NodeConfigSource{
|
||||
ConfigMapRef: &apiv1.ObjectReference{Name: "name", Namespace: "namespace", UID: c.uid}})
|
||||
ConfigMap: &apiv1.ConfigMapNodeConfigSource{
|
||||
Name: "name",
|
||||
Namespace: "namespace",
|
||||
UID: c.uid,
|
||||
KubeletConfigKey: "kubelet",
|
||||
}})
|
||||
if err != nil {
|
||||
t.Fatalf("error constructing remote config source: %v", err)
|
||||
}
|
||||
|
@ -214,7 +219,10 @@ func TestFsStoreLoad(t *testing.T) {
|
|||
t.Fatalf("error encoding KubeletConfiguration: %v", err)
|
||||
}
|
||||
// construct a payload that contains the kubeletconfig
|
||||
const uid = "uid"
|
||||
const (
|
||||
uid = "uid"
|
||||
kubeletKey = "kubelet"
|
||||
)
|
||||
p, err := checkpoint.NewConfigMapPayload(&apiv1.ConfigMap{
|
||||
ObjectMeta: metav1.ObjectMeta{UID: types.UID(uid)},
|
||||
Data: map[string]string{
|
||||
|
@ -241,7 +249,12 @@ func TestFsStoreLoad(t *testing.T) {
|
|||
for _, c := range cases {
|
||||
t.Run(c.desc, func(t *testing.T) {
|
||||
source, _, err := checkpoint.NewRemoteConfigSource(&apiv1.NodeConfigSource{
|
||||
ConfigMapRef: &apiv1.ObjectReference{Name: "name", Namespace: "namespace", UID: c.uid}})
|
||||
ConfigMap: &apiv1.ConfigMapNodeConfigSource{
|
||||
Name: "name",
|
||||
Namespace: "namespace",
|
||||
UID: c.uid,
|
||||
KubeletConfigKey: kubeletKey,
|
||||
}})
|
||||
if err != nil {
|
||||
t.Fatalf("error constructing remote config source: %v", err)
|
||||
}
|
||||
|
@ -291,7 +304,12 @@ func TestFsStoreCurrent(t *testing.T) {
|
|||
}
|
||||
|
||||
source, _, err := checkpoint.NewRemoteConfigSource(&apiv1.NodeConfigSource{
|
||||
ConfigMapRef: &apiv1.ObjectReference{Name: "name", Namespace: "namespace", UID: "uid"}})
|
||||
ConfigMap: &apiv1.ConfigMapNodeConfigSource{
|
||||
Name: "name",
|
||||
Namespace: "namespace",
|
||||
UID: "uid",
|
||||
KubeletConfigKey: "kubelet",
|
||||
}})
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
|
@ -329,7 +347,12 @@ func TestFsStoreLastKnownGood(t *testing.T) {
|
|||
}
|
||||
|
||||
source, _, err := checkpoint.NewRemoteConfigSource(&apiv1.NodeConfigSource{
|
||||
ConfigMapRef: &apiv1.ObjectReference{Name: "name", Namespace: "namespace", UID: "uid"}})
|
||||
ConfigMap: &apiv1.ConfigMapNodeConfigSource{
|
||||
Name: "name",
|
||||
Namespace: "namespace",
|
||||
UID: "uid",
|
||||
KubeletConfigKey: "kubelet",
|
||||
}})
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
|
@ -367,15 +390,21 @@ func TestFsStoreSetCurrent(t *testing.T) {
|
|||
}
|
||||
|
||||
const uid = "uid"
|
||||
expect := fmt.Sprintf(`apiVersion: v1
|
||||
configMapRef:
|
||||
name: name
|
||||
namespace: namespace
|
||||
uid: %s
|
||||
kind: NodeConfigSource
|
||||
expect := fmt.Sprintf(`apiVersion: kubelet.config.k8s.io/v1beta1
|
||||
kind: SerializedNodeConfigSource
|
||||
source:
|
||||
configMap:
|
||||
kubeletConfigKey: kubelet
|
||||
name: name
|
||||
namespace: namespace
|
||||
uid: %s
|
||||
`, uid)
|
||||
source, _, err := checkpoint.NewRemoteConfigSource(&apiv1.NodeConfigSource{ConfigMapRef: &apiv1.ObjectReference{
|
||||
Name: "name", Namespace: "namespace", UID: types.UID(uid)}})
|
||||
source, _, err := checkpoint.NewRemoteConfigSource(&apiv1.NodeConfigSource{ConfigMap: &apiv1.ConfigMapNodeConfigSource{
|
||||
Name: "name",
|
||||
Namespace: "namespace",
|
||||
UID: types.UID(uid),
|
||||
KubeletConfigKey: "kubelet",
|
||||
}})
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
|
@ -399,15 +428,21 @@ func TestFsStoreSetLastKnownGood(t *testing.T) {
|
|||
}
|
||||
|
||||
const uid = "uid"
|
||||
expect := fmt.Sprintf(`apiVersion: v1
|
||||
configMapRef:
|
||||
name: name
|
||||
namespace: namespace
|
||||
uid: %s
|
||||
kind: NodeConfigSource
|
||||
expect := fmt.Sprintf(`apiVersion: kubelet.config.k8s.io/v1beta1
|
||||
kind: SerializedNodeConfigSource
|
||||
source:
|
||||
configMap:
|
||||
kubeletConfigKey: kubelet
|
||||
name: name
|
||||
namespace: namespace
|
||||
uid: %s
|
||||
`, uid)
|
||||
source, _, err := checkpoint.NewRemoteConfigSource(&apiv1.NodeConfigSource{ConfigMapRef: &apiv1.ObjectReference{
|
||||
Name: "name", Namespace: "namespace", UID: types.UID(uid)}})
|
||||
source, _, err := checkpoint.NewRemoteConfigSource(&apiv1.NodeConfigSource{ConfigMap: &apiv1.ConfigMapNodeConfigSource{
|
||||
Name: "name",
|
||||
Namespace: "namespace",
|
||||
UID: types.UID(uid),
|
||||
KubeletConfigKey: "kubelet",
|
||||
}})
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
|
@ -430,11 +465,21 @@ func TestFsStoreReset(t *testing.T) {
|
|||
t.Fatalf("error constructing store: %v", err)
|
||||
}
|
||||
|
||||
source, _, err := checkpoint.NewRemoteConfigSource(&apiv1.NodeConfigSource{ConfigMapRef: &apiv1.ObjectReference{Name: "name", Namespace: "namespace", UID: "uid"}})
|
||||
source, _, err := checkpoint.NewRemoteConfigSource(&apiv1.NodeConfigSource{ConfigMap: &apiv1.ConfigMapNodeConfigSource{
|
||||
Name: "name",
|
||||
Namespace: "namespace",
|
||||
UID: "uid",
|
||||
KubeletConfigKey: "kubelet",
|
||||
}})
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
otherSource, _, err := checkpoint.NewRemoteConfigSource(&apiv1.NodeConfigSource{ConfigMapRef: &apiv1.ObjectReference{Name: "other-name", Namespace: "namespace", UID: "other-uid"}})
|
||||
otherSource, _, err := checkpoint.NewRemoteConfigSource(&apiv1.NodeConfigSource{ConfigMap: &apiv1.ConfigMapNodeConfigSource{
|
||||
Name: "other-name",
|
||||
Namespace: "namespace",
|
||||
UID: "other-uid",
|
||||
KubeletConfigKey: "kubelet",
|
||||
}})
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
|
@ -498,7 +543,12 @@ func TestFsStoreReadRemoteConfigSource(t *testing.T) {
|
|||
}
|
||||
|
||||
source, _, err := checkpoint.NewRemoteConfigSource(&apiv1.NodeConfigSource{
|
||||
ConfigMapRef: &apiv1.ObjectReference{Name: "name", Namespace: "namespace", UID: "uid"}})
|
||||
ConfigMap: &apiv1.ConfigMapNodeConfigSource{
|
||||
Name: "name",
|
||||
Namespace: "namespace",
|
||||
UID: "uid",
|
||||
KubeletConfigKey: "kubelet",
|
||||
}})
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
|
@ -534,7 +584,12 @@ func TestFsStoreWriteRemoteConfigSource(t *testing.T) {
|
|||
t.Fatalf("error constructing store: %v", err)
|
||||
}
|
||||
|
||||
source, _, err := checkpoint.NewRemoteConfigSource(&apiv1.NodeConfigSource{ConfigMapRef: &apiv1.ObjectReference{Name: "name", Namespace: "namespace", UID: "uid"}})
|
||||
source, _, err := checkpoint.NewRemoteConfigSource(&apiv1.NodeConfigSource{ConfigMap: &apiv1.ConfigMapNodeConfigSource{
|
||||
Name: "name",
|
||||
Namespace: "namespace",
|
||||
UID: "uid",
|
||||
KubeletConfigKey: "kubelet",
|
||||
}})
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
|
|
|
@ -26,11 +26,21 @@ import (
|
|||
)
|
||||
|
||||
func TestReset(t *testing.T) {
|
||||
source, _, err := checkpoint.NewRemoteConfigSource(&apiv1.NodeConfigSource{ConfigMapRef: &apiv1.ObjectReference{Name: "name", Namespace: "namespace", UID: "uid"}})
|
||||
source, _, err := checkpoint.NewRemoteConfigSource(&apiv1.NodeConfigSource{ConfigMap: &apiv1.ConfigMapNodeConfigSource{
|
||||
Name: "name",
|
||||
Namespace: "namespace",
|
||||
UID: "uid",
|
||||
KubeletConfigKey: "kubelet",
|
||||
}})
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
otherSource, _, err := checkpoint.NewRemoteConfigSource(&apiv1.NodeConfigSource{ConfigMapRef: &apiv1.ObjectReference{Name: "other-name", Namespace: "namespace", UID: "other-uid"}})
|
||||
otherSource, _, err := checkpoint.NewRemoteConfigSource(&apiv1.NodeConfigSource{ConfigMap: &apiv1.ConfigMapNodeConfigSource{
|
||||
Name: "other-name",
|
||||
Namespace: "namespace",
|
||||
UID: "other-uid",
|
||||
KubeletConfigKey: "kubelet",
|
||||
}})
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
|
|
|
@ -65,11 +65,11 @@ const (
|
|||
FailSyncReasonFmt = "failed to sync, reason: %s"
|
||||
// FailSyncReasonAllNilSubfields is used when no subfields are set
|
||||
FailSyncReasonAllNilSubfields = "invalid NodeConfigSource, exactly one subfield must be non-nil, but all were nil"
|
||||
// FailSyncReasonPartialObjectReference is used when some required subfields remain unset
|
||||
FailSyncReasonPartialObjectReference = "invalid ObjectReference, all of UID, Name, and Namespace must be specified"
|
||||
// FailSyncReasonPartialConfigMapSource is used when some required subfields remain unset
|
||||
FailSyncReasonPartialConfigMapSource = "invalid ConfigSource.ConfigMap, all of UID, Name, Namespace, and KubeletConfigKey must be specified"
|
||||
// FailSyncReasonUIDMismatchFmt is used when there is a UID mismatch between the referenced and downloaded ConfigMaps,
|
||||
// this can happen because objects must be downloaded by namespace/name, rather than by UID
|
||||
FailSyncReasonUIDMismatchFmt = "invalid ConfigSource.ConfigMapRef.UID: %s does not match %s.UID: %s"
|
||||
FailSyncReasonUIDMismatchFmt = "invalid ConfigSource.ConfigMap.UID: %s does not match %s.UID: %s"
|
||||
// FailSyncReasonDownloadFmt is used when the download fails, e.g. due to network issues
|
||||
FailSyncReasonDownloadFmt = "failed to download: %s"
|
||||
// FailSyncReasonInformer is used when the informer fails to report the Node object
|
||||
|
|
|
@ -32,7 +32,7 @@ import (
|
|||
|
||||
// EncodeKubeletConfig encodes an internal KubeletConfiguration to an external YAML representation
|
||||
func EncodeKubeletConfig(internal *kubeletconfig.KubeletConfiguration, targetVersion schema.GroupVersion) ([]byte, error) {
|
||||
encoder, err := newKubeletConfigYAMLEncoder(targetVersion)
|
||||
encoder, err := NewKubeletconfigYAMLEncoder(targetVersion)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -44,8 +44,8 @@ func EncodeKubeletConfig(internal *kubeletconfig.KubeletConfiguration, targetVer
|
|||
return data, nil
|
||||
}
|
||||
|
||||
// newKubeletConfigYAMLEncoder returns an encoder that can write a KubeletConfig to YAML
|
||||
func newKubeletConfigYAMLEncoder(targetVersion schema.GroupVersion) (runtime.Encoder, error) {
|
||||
// NewKubeletconfigYAMLEncoder returns an encoder that can write objects in the kubeletconfig API group to YAML
|
||||
func NewKubeletconfigYAMLEncoder(targetVersion schema.GroupVersion) (runtime.Encoder, error) {
|
||||
_, codecs, err := scheme.NewSchemeAndCodecs()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
|
|
@ -18,33 +18,6 @@ package equal
|
|||
|
||||
import apiv1 "k8s.io/api/core/v1"
|
||||
|
||||
// ConfigSourceEq returns true if the two config sources are semantically equivalent in the context of dynamic config
|
||||
func ConfigSourceEq(a, b *apiv1.NodeConfigSource) bool {
|
||||
if a == b {
|
||||
return true
|
||||
} else if a == nil || b == nil {
|
||||
// not equal, and one is nil
|
||||
return false
|
||||
}
|
||||
// check equality of config source subifelds
|
||||
if a.ConfigMapRef != b.ConfigMapRef {
|
||||
return ObjectRefEq(a.ConfigMapRef, b.ConfigMapRef)
|
||||
}
|
||||
// all internal subfields of the config source are equal
|
||||
return true
|
||||
}
|
||||
|
||||
// ObjectRefEq returns true if the two object references are semantically equivalent in the context of dynamic config
|
||||
func ObjectRefEq(a, b *apiv1.ObjectReference) bool {
|
||||
if a == b {
|
||||
return true
|
||||
} else if a == nil || b == nil {
|
||||
// not equal, and one is nil
|
||||
return false
|
||||
}
|
||||
return a.UID == b.UID && a.Namespace == b.Namespace && a.Name == b.Name
|
||||
}
|
||||
|
||||
// KubeletConfigOkEq returns true if the two conditions are semantically equivalent in the context of dynamic config
|
||||
func KubeletConfigOkEq(a, b *apiv1.NodeCondition) bool {
|
||||
return a.Message == b.Message && a.Reason == b.Reason && a.Status == b.Status
|
||||
|
|
|
@ -21,13 +21,13 @@ import (
|
|||
"time"
|
||||
|
||||
apiv1 "k8s.io/api/core/v1"
|
||||
apiequality "k8s.io/apimachinery/pkg/api/equality"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/fields"
|
||||
kuberuntime "k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/watch"
|
||||
clientset "k8s.io/client-go/kubernetes"
|
||||
"k8s.io/client-go/tools/cache"
|
||||
utilequal "k8s.io/kubernetes/pkg/kubelet/kubeletconfig/util/equal"
|
||||
utillog "k8s.io/kubernetes/pkg/kubelet/kubeletconfig/util/log"
|
||||
)
|
||||
|
||||
|
@ -94,7 +94,7 @@ func (cc *Controller) onUpdateNodeEvent(oldObj interface{}, newObj interface{})
|
|||
utillog.Errorf("failed to cast old object to Node, couldn't handle event")
|
||||
return
|
||||
}
|
||||
if !utilequal.ConfigSourceEq(oldNode.Spec.ConfigSource, newNode.Spec.ConfigSource) {
|
||||
if !apiequality.Semantic.DeepEqual(oldNode.Spec.ConfigSource, newNode.Spec.ConfigSource) {
|
||||
cc.pokeConfigSourceWorker()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -91,9 +91,19 @@ func Test_nodePlugin_Admit(t *testing.T) {
|
|||
mynodeObjMeta = metav1.ObjectMeta{Name: "mynode"}
|
||||
mynodeObj = &api.Node{ObjectMeta: mynodeObjMeta}
|
||||
mynodeObjConfigA = &api.Node{ObjectMeta: mynodeObjMeta, Spec: api.NodeSpec{ConfigSource: &api.NodeConfigSource{
|
||||
ConfigMapRef: &api.ObjectReference{Name: "foo", Namespace: "bar", UID: "fooUID"}}}}
|
||||
ConfigMap: &api.ConfigMapNodeConfigSource{
|
||||
Name: "foo",
|
||||
Namespace: "bar",
|
||||
UID: "fooUID",
|
||||
KubeletConfigKey: "kubelet",
|
||||
}}}}
|
||||
mynodeObjConfigB = &api.Node{ObjectMeta: mynodeObjMeta, Spec: api.NodeSpec{ConfigSource: &api.NodeConfigSource{
|
||||
ConfigMapRef: &api.ObjectReference{Name: "qux", Namespace: "bar", UID: "quxUID"}}}}
|
||||
ConfigMap: &api.ConfigMapNodeConfigSource{
|
||||
Name: "qux",
|
||||
Namespace: "bar",
|
||||
UID: "quxUID",
|
||||
KubeletConfigKey: "kubelet",
|
||||
}}}}
|
||||
othernodeObj = &api.Node{ObjectMeta: metav1.ObjectMeta{Name: "othernode"}}
|
||||
|
||||
mymirrorpod = makeTestPod("ns", "mymirrorpod", "mynode", true)
|
||||
|
|
|
@ -408,7 +408,7 @@ func (g *Graph) DeleteVolumeAttachment(name string) {
|
|||
g.deleteVertex_locked(vaVertexType, "", name)
|
||||
}
|
||||
|
||||
// SetNodeConfigMap sets up edges for the Node.Spec.ConfigSource.ConfigMapRef relationship:
|
||||
// SetNodeConfigMap sets up edges for the Node.Spec.ConfigSource.ConfigMap relationship:
|
||||
//
|
||||
// configmap -> node
|
||||
func (g *Graph) SetNodeConfigMap(nodeName, configMapName, configMapNamespace string) {
|
||||
|
|
|
@ -84,19 +84,19 @@ func (g *graphPopulator) updateNode(oldObj, obj interface{}) {
|
|||
oldNode = oldObj.(*api.Node)
|
||||
}
|
||||
|
||||
// we only set up rules for ConfigMapRef today, because that is the only reference type
|
||||
// we only set up rules for ConfigMap today, because that is the only reference type
|
||||
|
||||
var name, namespace string
|
||||
if source := node.Spec.ConfigSource; source != nil && source.ConfigMapRef != nil {
|
||||
name = source.ConfigMapRef.Name
|
||||
namespace = source.ConfigMapRef.Namespace
|
||||
if source := node.Spec.ConfigSource; source != nil && source.ConfigMap != nil {
|
||||
name = source.ConfigMap.Name
|
||||
namespace = source.ConfigMap.Namespace
|
||||
}
|
||||
|
||||
var oldName, oldNamespace string
|
||||
if oldNode != nil {
|
||||
if oldSource := oldNode.Spec.ConfigSource; oldSource != nil && oldSource.ConfigMapRef != nil {
|
||||
oldName = oldSource.ConfigMapRef.Name
|
||||
oldNamespace = oldSource.ConfigMapRef.Namespace
|
||||
if oldSource := oldNode.Spec.ConfigSource; oldSource != nil && oldSource.ConfigMap != nil {
|
||||
oldName = oldSource.ConfigMap.Name
|
||||
oldNamespace = oldSource.ConfigMap.Namespace
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -753,10 +753,11 @@ func generate(opts sampleDataOpts) ([]*api.Node, []*api.Pod, []*api.PersistentVo
|
|||
ObjectMeta: metav1.ObjectMeta{Name: nodeName},
|
||||
Spec: api.NodeSpec{
|
||||
ConfigSource: &api.NodeConfigSource{
|
||||
ConfigMapRef: &api.ObjectReference{
|
||||
Name: name,
|
||||
Namespace: "ns0",
|
||||
UID: types.UID(fmt.Sprintf("ns0-%s", name)),
|
||||
ConfigMap: &api.ConfigMapNodeConfigSource{
|
||||
Name: name,
|
||||
Namespace: "ns0",
|
||||
UID: types.UID(fmt.Sprintf("ns0-%s", name)),
|
||||
KubeletConfigKey: "kubelet",
|
||||
},
|
||||
},
|
||||
},
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -439,6 +439,34 @@ message ConfigMapList {
|
|||
repeated ConfigMap items = 2;
|
||||
}
|
||||
|
||||
// ConfigMapNodeConfigSource contains the information to reference a ConfigMap as a config source for the Node.
|
||||
message ConfigMapNodeConfigSource {
|
||||
// Namespace is the metadata.namespace of the referenced ConfigMap.
|
||||
// This field is required in all cases.
|
||||
optional string namespace = 1;
|
||||
|
||||
// Name is the metadata.name of the referenced ConfigMap.
|
||||
// This field is required in all cases.
|
||||
optional string name = 2;
|
||||
|
||||
// UID is the metadata.UID of the referenced ConfigMap.
|
||||
// This field is currently reqired in Node.Spec.
|
||||
// TODO(#61643): This field will be forbidden in Node.Spec when #61643 is resolved.
|
||||
// TODO(#56896): This field will be required in Node.Status when #56896 is resolved.
|
||||
// +optional
|
||||
optional string uid = 3;
|
||||
|
||||
// ResourceVersion is the metadata.ResourceVersion of the referenced ConfigMap.
|
||||
// This field is forbidden in Node.Spec.
|
||||
// TODO(#56896): This field will be required in Node.Status when #56896 is resolved.
|
||||
// +optional
|
||||
optional string resourceVersion = 4;
|
||||
|
||||
// KubeletConfigKey declares which key of the referenced ConfigMap corresponds to the KubeletConfiguration structure
|
||||
// This field is required in all cases.
|
||||
optional string kubeletConfigKey = 5;
|
||||
}
|
||||
|
||||
// Adapts a ConfigMap into a projected volume.
|
||||
//
|
||||
// The contents of the target ConfigMap's Data field will be presented in a
|
||||
|
@ -1815,7 +1843,8 @@ message NodeCondition {
|
|||
|
||||
// NodeConfigSource specifies a source of node configuration. Exactly one subfield (excluding metadata) must be non-nil.
|
||||
message NodeConfigSource {
|
||||
optional ObjectReference configMapRef = 1;
|
||||
// ConfigMap is a reference to a Node's ConfigMap
|
||||
optional ConfigMapNodeConfigSource configMap = 2;
|
||||
}
|
||||
|
||||
// NodeDaemonEndpoints lists ports opened by daemons running on the Node.
|
||||
|
|
|
@ -57,7 +57,6 @@ func addKnownTypes(scheme *runtime.Scheme) error {
|
|||
&Endpoints{},
|
||||
&EndpointsList{},
|
||||
&Node{},
|
||||
&NodeConfigSource{},
|
||||
&NodeList{},
|
||||
&NodeProxyOptions{},
|
||||
&Binding{},
|
||||
|
|
|
@ -3640,12 +3640,49 @@ type NodeSpec struct {
|
|||
DoNotUse_ExternalID string `json:"externalID,omitempty" protobuf:"bytes,2,opt,name=externalID"`
|
||||
}
|
||||
|
||||
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
|
||||
|
||||
// NodeConfigSource specifies a source of node configuration. Exactly one subfield (excluding metadata) must be non-nil.
|
||||
type NodeConfigSource struct {
|
||||
metav1.TypeMeta `json:",inline"`
|
||||
ConfigMapRef *ObjectReference `json:"configMapRef,omitempty" protobuf:"bytes,1,opt,name=configMapRef"`
|
||||
// For historical context, regarding the below kind, apiVersion, and configMapRef deprecation tags:
|
||||
// 1. kind/apiVersion were used by the kubelet to persist this struct to disk (they had no protobuf tags)
|
||||
// 2. configMapRef and proto tag 1 were used by the API to refer to a configmap,
|
||||
// but used a generic ObjectReference type that didn't really have the fields we needed
|
||||
// All uses/persistence of the NodeConfigSource struct prior to 1.11 were gated by alpha feature flags,
|
||||
// so there was no persisted data for these fields that needed to be migrated/handled.
|
||||
|
||||
// +k8s:deprecated=kind
|
||||
// +k8s:deprecated=apiVersion
|
||||
// +k8s:deprecated=configMapRef,protobuf=1
|
||||
|
||||
// ConfigMap is a reference to a Node's ConfigMap
|
||||
ConfigMap *ConfigMapNodeConfigSource `json:"configMap,omitempty" protobuf:"bytes,2,opt,name=configMap"`
|
||||
}
|
||||
|
||||
// ConfigMapNodeConfigSource contains the information to reference a ConfigMap as a config source for the Node.
|
||||
type ConfigMapNodeConfigSource struct {
|
||||
// Namespace is the metadata.namespace of the referenced ConfigMap.
|
||||
// This field is required in all cases.
|
||||
Namespace string `json:"namespace" protobuf:"bytes,1,opt,name=namespace"`
|
||||
|
||||
// Name is the metadata.name of the referenced ConfigMap.
|
||||
// This field is required in all cases.
|
||||
Name string `json:"name" protobuf:"bytes,2,opt,name=name"`
|
||||
|
||||
// UID is the metadata.UID of the referenced ConfigMap.
|
||||
// This field is currently reqired in Node.Spec.
|
||||
// TODO(#61643): This field will be forbidden in Node.Spec when #61643 is resolved.
|
||||
// TODO(#56896): This field will be required in Node.Status when #56896 is resolved.
|
||||
// +optional
|
||||
UID types.UID `json:"uid,omitempty" protobuf:"bytes,3,opt,name=uid"`
|
||||
|
||||
// ResourceVersion is the metadata.ResourceVersion of the referenced ConfigMap.
|
||||
// This field is forbidden in Node.Spec.
|
||||
// TODO(#56896): This field will be required in Node.Status when #56896 is resolved.
|
||||
// +optional
|
||||
ResourceVersion string `json:"resourceVersion,omitempty" protobuf:"bytes,4,opt,name=resourceVersion"`
|
||||
|
||||
// KubeletConfigKey declares which key of the referenced ConfigMap corresponds to the KubeletConfiguration structure
|
||||
// This field is required in all cases.
|
||||
KubeletConfigKey string `json:"kubeletConfigKey" protobuf:"bytes,5,opt,name=kubeletConfigKey"`
|
||||
}
|
||||
|
||||
// DaemonEndpoint contains information about a single Daemon endpoint.
|
||||
|
|
|
@ -262,6 +262,19 @@ func (ConfigMapList) SwaggerDoc() map[string]string {
|
|||
return map_ConfigMapList
|
||||
}
|
||||
|
||||
var map_ConfigMapNodeConfigSource = map[string]string{
|
||||
"": "ConfigMapNodeConfigSource contains the information to reference a ConfigMap as a config source for the Node.",
|
||||
"namespace": "Namespace is the metadata.namespace of the referenced ConfigMap. This field is required in all cases.",
|
||||
"name": "Name is the metadata.name of the referenced ConfigMap. This field is required in all cases.",
|
||||
"uid": "UID is the metadata.UID of the referenced ConfigMap. This field is currently reqired in Node.Spec.",
|
||||
"resourceVersion": "ResourceVersion is the metadata.ResourceVersion of the referenced ConfigMap. This field is forbidden in Node.Spec.",
|
||||
"kubeletConfigKey": "KubeletConfigKey declares which key of the referenced ConfigMap corresponds to the KubeletConfiguration structure This field is required in all cases.",
|
||||
}
|
||||
|
||||
func (ConfigMapNodeConfigSource) SwaggerDoc() map[string]string {
|
||||
return map_ConfigMapNodeConfigSource
|
||||
}
|
||||
|
||||
var map_ConfigMapProjection = map[string]string{
|
||||
"": "Adapts a ConfigMap into a projected volume.\n\nThe contents of the target ConfigMap's Data field will be presented in a projected volume as files using the keys in the Data field as the file names, unless the items element is populated with specific mappings of keys to paths. Note that this is identical to a configmap volume source without the default mode.",
|
||||
"items": "If unspecified, each key-value pair in the Data field of the referenced ConfigMap will be projected into the volume as a file whose name is the key and content is the value. If specified, the listed keys will be projected into the specified paths, and unlisted keys will not be present. If a key is specified which is not present in the ConfigMap, the volume setup will error unless it is marked optional. Paths must be relative and may not contain the '..' path or start with '..'.",
|
||||
|
@ -969,7 +982,8 @@ func (NodeCondition) SwaggerDoc() map[string]string {
|
|||
}
|
||||
|
||||
var map_NodeConfigSource = map[string]string{
|
||||
"": "NodeConfigSource specifies a source of node configuration. Exactly one subfield (excluding metadata) must be non-nil.",
|
||||
"": "NodeConfigSource specifies a source of node configuration. Exactly one subfield (excluding metadata) must be non-nil.",
|
||||
"configMap": "ConfigMap is a reference to a Node's ConfigMap",
|
||||
}
|
||||
|
||||
func (NodeConfigSource) SwaggerDoc() map[string]string {
|
||||
|
|
|
@ -631,6 +631,22 @@ func (in *ConfigMapList) DeepCopyObject() runtime.Object {
|
|||
return nil
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *ConfigMapNodeConfigSource) DeepCopyInto(out *ConfigMapNodeConfigSource) {
|
||||
*out = *in
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ConfigMapNodeConfigSource.
|
||||
func (in *ConfigMapNodeConfigSource) DeepCopy() *ConfigMapNodeConfigSource {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(ConfigMapNodeConfigSource)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *ConfigMapProjection) DeepCopyInto(out *ConfigMapProjection) {
|
||||
*out = *in
|
||||
|
@ -2360,13 +2376,12 @@ func (in *NodeCondition) DeepCopy() *NodeCondition {
|
|||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *NodeConfigSource) DeepCopyInto(out *NodeConfigSource) {
|
||||
*out = *in
|
||||
out.TypeMeta = in.TypeMeta
|
||||
if in.ConfigMapRef != nil {
|
||||
in, out := &in.ConfigMapRef, &out.ConfigMapRef
|
||||
if in.ConfigMap != nil {
|
||||
in, out := &in.ConfigMap, &out.ConfigMap
|
||||
if *in == nil {
|
||||
*out = nil
|
||||
} else {
|
||||
*out = new(ObjectReference)
|
||||
*out = new(ConfigMapNodeConfigSource)
|
||||
**out = **in
|
||||
}
|
||||
}
|
||||
|
@ -2383,14 +2398,6 @@ func (in *NodeConfigSource) DeepCopy() *NodeConfigSource {
|
|||
return out
|
||||
}
|
||||
|
||||
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
|
||||
func (in *NodeConfigSource) DeepCopyObject() runtime.Object {
|
||||
if c := in.DeepCopy(); c != nil {
|
||||
return c
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *NodeDaemonEndpoints) DeepCopyInto(out *NodeDaemonEndpoints) {
|
||||
*out = *in
|
||||
|
|
|
@ -84,10 +84,12 @@ var _ = framework.KubeDescribe("DynamicKubeletConfiguration [Feature:DynamicKube
|
|||
// were initially set via the locally provisioned configuration.
|
||||
// This is the same strategy several other e2e node tests use.
|
||||
setAndTestKubeletConfigState(f, &configState{desc: "reset to original values",
|
||||
configSource: &apiv1.NodeConfigSource{ConfigMapRef: &apiv1.ObjectReference{
|
||||
UID: originalConfigMap.UID,
|
||||
Namespace: originalConfigMap.Namespace,
|
||||
Name: originalConfigMap.Name}},
|
||||
configSource: &apiv1.NodeConfigSource{ConfigMap: &apiv1.ConfigMapNodeConfigSource{
|
||||
UID: originalConfigMap.UID,
|
||||
Namespace: originalConfigMap.Namespace,
|
||||
Name: originalConfigMap.Name,
|
||||
KubeletConfigKey: "kubelet",
|
||||
}},
|
||||
expectConfigOk: &apiv1.NodeCondition{Type: apiv1.NodeKubeletConfigOk, Status: apiv1.ConditionTrue,
|
||||
Message: fmt.Sprintf(status.CurRemoteMessageFmt, configMapAPIPath(originalConfigMap)),
|
||||
Reason: status.CurRemoteOkayReason},
|
||||
|
@ -123,8 +125,8 @@ var _ = framework.KubeDescribe("DynamicKubeletConfiguration [Feature:DynamicKube
|
|||
framework.ExpectNoError(err)
|
||||
|
||||
states := []configState{
|
||||
// Node.Spec.ConfigSource is nil
|
||||
{desc: "Node.Spec.ConfigSource is nil",
|
||||
{
|
||||
desc: "Node.Spec.ConfigSource is nil",
|
||||
configSource: nil,
|
||||
expectConfigOk: &apiv1.NodeCondition{Type: apiv1.NodeKubeletConfigOk, Status: apiv1.ConditionTrue,
|
||||
Message: status.CurLocalMessage,
|
||||
|
@ -132,65 +134,107 @@ var _ = framework.KubeDescribe("DynamicKubeletConfiguration [Feature:DynamicKube
|
|||
expectConfig: nil,
|
||||
event: true,
|
||||
},
|
||||
|
||||
// Node.Spec.ConfigSource has all nil subfields
|
||||
{desc: "Node.Spec.ConfigSource has all nil subfields",
|
||||
configSource: &apiv1.NodeConfigSource{},
|
||||
apierr: "exactly one reference subfield must be non-nil",
|
||||
},
|
||||
|
||||
// Node.Spec.ConfigSource.ConfigMapRef is partial
|
||||
{desc: "Node.Spec.ConfigSource.ConfigMapRef is partial",
|
||||
configSource: &apiv1.NodeConfigSource{ConfigMapRef: &apiv1.ObjectReference{
|
||||
UID: "foo",
|
||||
Name: "bar"}}, // missing Namespace
|
||||
apierr: "name, namespace, and UID must all be non-empty",
|
||||
// Node.Spec.ConfigSource.ConfigMap is partial
|
||||
{desc: "Node.Spec.ConfigSource.ConfigMap is partial",
|
||||
configSource: &apiv1.NodeConfigSource{ConfigMap: &apiv1.ConfigMapNodeConfigSource{
|
||||
UID: "foo",
|
||||
Name: "bar",
|
||||
KubeletConfigKey: "kubelet",
|
||||
}}, // missing Namespace
|
||||
apierr: "spec.configSource.configMap.namespace: Required value: namespace must be set in spec",
|
||||
},
|
||||
|
||||
// Node.Spec.ConfigSource's UID does not align with namespace/name
|
||||
{desc: "Node.Spec.ConfigSource.ConfigMapRef.UID does not align with Namespace/Name",
|
||||
configSource: &apiv1.NodeConfigSource{ConfigMapRef: &apiv1.ObjectReference{UID: "foo",
|
||||
Namespace: correctConfigMap.Namespace,
|
||||
Name: correctConfigMap.Name}},
|
||||
{desc: "Node.Spec.ConfigSource.ConfigMap.ResourceVersion is illegally specified",
|
||||
configSource: &apiv1.NodeConfigSource{ConfigMap: &apiv1.ConfigMapNodeConfigSource{
|
||||
UID: "foo",
|
||||
Name: "bar",
|
||||
Namespace: "baz",
|
||||
ResourceVersion: "1",
|
||||
KubeletConfigKey: "kubelet",
|
||||
}},
|
||||
apierr: "spec.configSource.configMap.resourceVersion: Forbidden: resourceVersion must not be set in spec",
|
||||
},
|
||||
{desc: "Node.Spec.ConfigSource.ConfigMap has invalid namespace",
|
||||
configSource: &apiv1.NodeConfigSource{ConfigMap: &apiv1.ConfigMapNodeConfigSource{
|
||||
UID: "foo",
|
||||
Name: "bar",
|
||||
Namespace: "../baz",
|
||||
KubeletConfigKey: "kubelet",
|
||||
}},
|
||||
apierr: "spec.configSource.configMap.namespace: Invalid value",
|
||||
},
|
||||
{desc: "Node.Spec.ConfigSource.ConfigMap has invalid name",
|
||||
configSource: &apiv1.NodeConfigSource{ConfigMap: &apiv1.ConfigMapNodeConfigSource{
|
||||
UID: "foo",
|
||||
Name: "../bar",
|
||||
Namespace: "baz",
|
||||
KubeletConfigKey: "kubelet",
|
||||
}},
|
||||
apierr: "spec.configSource.configMap.name: Invalid value",
|
||||
},
|
||||
{desc: "Node.Spec.ConfigSource.ConfigMap has invalid kubeletConfigKey",
|
||||
configSource: &apiv1.NodeConfigSource{ConfigMap: &apiv1.ConfigMapNodeConfigSource{
|
||||
UID: "foo",
|
||||
Name: "bar",
|
||||
Namespace: "baz",
|
||||
KubeletConfigKey: "../qux",
|
||||
}},
|
||||
apierr: "spec.configSource.configMap.kubeletConfigKey: Invalid value",
|
||||
},
|
||||
{
|
||||
desc: "Node.Spec.ConfigSource.ConfigMap.UID does not align with Namespace/Name",
|
||||
configSource: &apiv1.NodeConfigSource{ConfigMap: &apiv1.ConfigMapNodeConfigSource{
|
||||
UID: "foo",
|
||||
Namespace: correctConfigMap.Namespace,
|
||||
Name: correctConfigMap.Name,
|
||||
KubeletConfigKey: "kubelet",
|
||||
}},
|
||||
expectConfigOk: &apiv1.NodeCondition{Type: apiv1.NodeKubeletConfigOk, Status: apiv1.ConditionFalse,
|
||||
Message: "",
|
||||
Reason: fmt.Sprintf(status.FailSyncReasonFmt, fmt.Sprintf(status.FailSyncReasonUIDMismatchFmt, "foo", configMapAPIPath(correctConfigMap), correctConfigMap.UID))},
|
||||
expectConfig: nil,
|
||||
event: false,
|
||||
},
|
||||
|
||||
// correct
|
||||
{desc: "correct",
|
||||
configSource: &apiv1.NodeConfigSource{ConfigMapRef: &apiv1.ObjectReference{
|
||||
UID: correctConfigMap.UID,
|
||||
Namespace: correctConfigMap.Namespace,
|
||||
Name: correctConfigMap.Name}},
|
||||
{
|
||||
desc: "correct",
|
||||
configSource: &apiv1.NodeConfigSource{ConfigMap: &apiv1.ConfigMapNodeConfigSource{
|
||||
UID: correctConfigMap.UID,
|
||||
Namespace: correctConfigMap.Namespace,
|
||||
Name: correctConfigMap.Name,
|
||||
KubeletConfigKey: "kubelet",
|
||||
}},
|
||||
expectConfigOk: &apiv1.NodeCondition{Type: apiv1.NodeKubeletConfigOk, Status: apiv1.ConditionTrue,
|
||||
Message: fmt.Sprintf(status.CurRemoteMessageFmt, configMapAPIPath(correctConfigMap)),
|
||||
Reason: status.CurRemoteOkayReason},
|
||||
expectConfig: correctKC,
|
||||
event: true,
|
||||
},
|
||||
|
||||
// fail-parse
|
||||
{desc: "fail-parse",
|
||||
configSource: &apiv1.NodeConfigSource{ConfigMapRef: &apiv1.ObjectReference{
|
||||
UID: failParseConfigMap.UID,
|
||||
Namespace: failParseConfigMap.Namespace,
|
||||
Name: failParseConfigMap.Name}},
|
||||
{
|
||||
desc: "fail-parse",
|
||||
configSource: &apiv1.NodeConfigSource{ConfigMap: &apiv1.ConfigMapNodeConfigSource{
|
||||
UID: failParseConfigMap.UID,
|
||||
Namespace: failParseConfigMap.Namespace,
|
||||
Name: failParseConfigMap.Name,
|
||||
KubeletConfigKey: "kubelet",
|
||||
}},
|
||||
expectConfigOk: &apiv1.NodeCondition{Type: apiv1.NodeKubeletConfigOk, Status: apiv1.ConditionFalse,
|
||||
Message: status.LkgLocalMessage,
|
||||
Reason: fmt.Sprintf(status.CurFailLoadReasonFmt, configMapAPIPath(failParseConfigMap))},
|
||||
expectConfig: nil,
|
||||
event: true,
|
||||
},
|
||||
|
||||
// fail-validate
|
||||
{desc: "fail-validate",
|
||||
configSource: &apiv1.NodeConfigSource{ConfigMapRef: &apiv1.ObjectReference{
|
||||
UID: failValidateConfigMap.UID,
|
||||
Namespace: failValidateConfigMap.Namespace,
|
||||
Name: failValidateConfigMap.Name}},
|
||||
{
|
||||
desc: "fail-validate",
|
||||
configSource: &apiv1.NodeConfigSource{ConfigMap: &apiv1.ConfigMapNodeConfigSource{
|
||||
UID: failValidateConfigMap.UID,
|
||||
Namespace: failValidateConfigMap.Namespace,
|
||||
Name: failValidateConfigMap.Name,
|
||||
KubeletConfigKey: "kubelet",
|
||||
}},
|
||||
expectConfigOk: &apiv1.NodeCondition{Type: apiv1.NodeKubeletConfigOk, Status: apiv1.ConditionFalse,
|
||||
Message: status.LkgLocalMessage,
|
||||
Reason: fmt.Sprintf(status.CurFailValidateReasonFmt, configMapAPIPath(failValidateConfigMap))},
|
||||
|
@ -229,10 +273,12 @@ var _ = framework.KubeDescribe("DynamicKubeletConfiguration [Feature:DynamicKube
|
|||
states := []configState{
|
||||
// intended lkg
|
||||
{desc: "intended last-known-good",
|
||||
configSource: &apiv1.NodeConfigSource{ConfigMapRef: &apiv1.ObjectReference{
|
||||
UID: lkgConfigMap.UID,
|
||||
Namespace: lkgConfigMap.Namespace,
|
||||
Name: lkgConfigMap.Name}},
|
||||
configSource: &apiv1.NodeConfigSource{ConfigMap: &apiv1.ConfigMapNodeConfigSource{
|
||||
UID: lkgConfigMap.UID,
|
||||
Namespace: lkgConfigMap.Namespace,
|
||||
Name: lkgConfigMap.Name,
|
||||
KubeletConfigKey: "kubelet",
|
||||
}},
|
||||
expectConfigOk: &apiv1.NodeCondition{Type: apiv1.NodeKubeletConfigOk, Status: apiv1.ConditionTrue,
|
||||
Message: fmt.Sprintf(status.CurRemoteMessageFmt, configMapAPIPath(lkgConfigMap)),
|
||||
Reason: status.CurRemoteOkayReason},
|
||||
|
@ -242,10 +288,12 @@ var _ = framework.KubeDescribe("DynamicKubeletConfiguration [Feature:DynamicKube
|
|||
|
||||
// bad config
|
||||
{desc: "bad config",
|
||||
configSource: &apiv1.NodeConfigSource{ConfigMapRef: &apiv1.ObjectReference{
|
||||
UID: badConfigMap.UID,
|
||||
Namespace: badConfigMap.Namespace,
|
||||
Name: badConfigMap.Name}},
|
||||
configSource: &apiv1.NodeConfigSource{ConfigMap: &apiv1.ConfigMapNodeConfigSource{
|
||||
UID: badConfigMap.UID,
|
||||
Namespace: badConfigMap.Namespace,
|
||||
Name: badConfigMap.Name,
|
||||
KubeletConfigKey: "kubelet",
|
||||
}},
|
||||
expectConfigOk: &apiv1.NodeCondition{Type: apiv1.NodeKubeletConfigOk, Status: apiv1.ConditionFalse,
|
||||
Message: fmt.Sprintf(status.LkgRemoteMessageFmt, configMapAPIPath(lkgConfigMap)),
|
||||
Reason: fmt.Sprintf(status.CurFailLoadReasonFmt, configMapAPIPath(badConfigMap))},
|
||||
|
@ -259,6 +307,55 @@ var _ = framework.KubeDescribe("DynamicKubeletConfiguration [Feature:DynamicKube
|
|||
})
|
||||
})
|
||||
|
||||
Context("When a remote config becomes the new last-known-good, and then Node.ConfigSource.ConfigMap.KubeletConfigKey is updated to use a new, bad config", func() {
|
||||
It("the Kubelet should report a status and configz indicating that it rolled back to the new last-known-good", func() {
|
||||
const badConfigKey = "bad"
|
||||
var err error
|
||||
// we base the "lkg" configmap off of the current configuration
|
||||
lkgKC := originalKC.DeepCopy()
|
||||
combinedConfigMap := newKubeletConfigMap("dynamic-kubelet-config-test-combined", lkgKC)
|
||||
combinedConfigMap.Data[badConfigKey] = "{0xdeadbeef}"
|
||||
combinedConfigMap, err = f.ClientSet.CoreV1().ConfigMaps("kube-system").Create(combinedConfigMap)
|
||||
framework.ExpectNoError(err)
|
||||
|
||||
states := []configState{
|
||||
// intended lkg
|
||||
{desc: "intended last-known-good",
|
||||
configSource: &apiv1.NodeConfigSource{ConfigMap: &apiv1.ConfigMapNodeConfigSource{
|
||||
UID: combinedConfigMap.UID,
|
||||
Namespace: combinedConfigMap.Namespace,
|
||||
Name: combinedConfigMap.Name,
|
||||
KubeletConfigKey: "kubelet",
|
||||
}},
|
||||
expectConfigOk: &apiv1.NodeCondition{Type: apiv1.NodeKubeletConfigOk, Status: apiv1.ConditionTrue,
|
||||
Message: fmt.Sprintf(status.CurRemoteMessageFmt, configMapAPIPath(combinedConfigMap)),
|
||||
Reason: status.CurRemoteOkayReason},
|
||||
expectConfig: lkgKC,
|
||||
event: true,
|
||||
},
|
||||
|
||||
// bad config
|
||||
{desc: "bad config",
|
||||
configSource: &apiv1.NodeConfigSource{ConfigMap: &apiv1.ConfigMapNodeConfigSource{
|
||||
UID: combinedConfigMap.UID,
|
||||
Namespace: combinedConfigMap.Namespace,
|
||||
Name: combinedConfigMap.Name,
|
||||
KubeletConfigKey: badConfigKey,
|
||||
}},
|
||||
expectConfigOk: &apiv1.NodeCondition{Type: apiv1.NodeKubeletConfigOk, Status: apiv1.ConditionFalse,
|
||||
// TODO(mtaufen): status should be more informative, and report the key being used
|
||||
Message: fmt.Sprintf(status.LkgRemoteMessageFmt, configMapAPIPath(combinedConfigMap)),
|
||||
Reason: fmt.Sprintf(status.CurFailLoadReasonFmt, configMapAPIPath(combinedConfigMap))},
|
||||
expectConfig: lkgKC,
|
||||
event: true,
|
||||
},
|
||||
}
|
||||
|
||||
// wait 12 minutes after setting the first config to ensure it has time to pass the trial duration
|
||||
testBothDirections(f, &states[0], states[1:], 12*time.Minute)
|
||||
})
|
||||
})
|
||||
|
||||
// This stress test will help turn up resource leaks across kubelet restarts that can, over time,
|
||||
// break our ability to dynamically update kubelet config
|
||||
Context("When changing the configuration 100 times", func() {
|
||||
|
@ -280,10 +377,12 @@ var _ = framework.KubeDescribe("DynamicKubeletConfiguration [Feature:DynamicKube
|
|||
|
||||
states := []configState{
|
||||
{desc: "cm1",
|
||||
configSource: &apiv1.NodeConfigSource{ConfigMapRef: &apiv1.ObjectReference{
|
||||
UID: cm1.UID,
|
||||
Namespace: cm1.Namespace,
|
||||
Name: cm1.Name}},
|
||||
configSource: &apiv1.NodeConfigSource{ConfigMap: &apiv1.ConfigMapNodeConfigSource{
|
||||
UID: cm1.UID,
|
||||
Namespace: cm1.Namespace,
|
||||
Name: cm1.Name,
|
||||
KubeletConfigKey: "kubelet",
|
||||
}},
|
||||
expectConfigOk: &apiv1.NodeCondition{Type: apiv1.NodeKubeletConfigOk, Status: apiv1.ConditionTrue,
|
||||
Message: fmt.Sprintf(status.CurRemoteMessageFmt, configMapAPIPath(cm1)),
|
||||
Reason: status.CurRemoteOkayReason},
|
||||
|
@ -292,10 +391,12 @@ var _ = framework.KubeDescribe("DynamicKubeletConfiguration [Feature:DynamicKube
|
|||
},
|
||||
|
||||
{desc: "cm2",
|
||||
configSource: &apiv1.NodeConfigSource{ConfigMapRef: &apiv1.ObjectReference{
|
||||
UID: cm2.UID,
|
||||
Namespace: cm2.Namespace,
|
||||
Name: cm2.Name}},
|
||||
configSource: &apiv1.NodeConfigSource{ConfigMap: &apiv1.ConfigMapNodeConfigSource{
|
||||
UID: cm2.UID,
|
||||
Namespace: cm2.Namespace,
|
||||
Name: cm2.Name,
|
||||
KubeletConfigKey: "kubelet",
|
||||
}},
|
||||
expectConfigOk: &apiv1.NodeCondition{Type: apiv1.NodeKubeletConfigOk, Status: apiv1.ConditionTrue,
|
||||
Message: fmt.Sprintf(status.CurRemoteMessageFmt, configMapAPIPath(cm2)),
|
||||
Reason: status.CurRemoteOkayReason},
|
||||
|
@ -335,7 +436,7 @@ func testBothDirections(f *framework.Framework, first *configState, states []con
|
|||
}
|
||||
}
|
||||
|
||||
// setAndTestKubeletConfigState tests that after setting the config source, the ConfigOk condition
|
||||
// setAndTestKubeletConfigState tests that after setting the config source, the KubeletConfigOk condition
|
||||
// and (if appropriate) configuration exposed via conifgz are as expected.
|
||||
// The configuration will be converted to the internal type prior to comparison.
|
||||
func setAndTestKubeletConfigState(f *framework.Framework, state *configState, expectEvent bool) {
|
||||
|
@ -479,8 +580,8 @@ func checkEvent(f *framework.Framework, desc string, expect *apiv1.NodeConfigSou
|
|||
// ensure the message is what we expect (including the resource path)
|
||||
expectMessage := fmt.Sprintf(controller.EventMessageFmt, controller.LocalConfigMessage)
|
||||
if expect != nil {
|
||||
if expect.ConfigMapRef != nil {
|
||||
expectMessage = fmt.Sprintf(controller.EventMessageFmt, fmt.Sprintf("/api/v1/namespaces/%s/configmaps/%s", expect.ConfigMapRef.Namespace, expect.ConfigMapRef.Name))
|
||||
if expect.ConfigMap != nil {
|
||||
expectMessage = fmt.Sprintf(controller.EventMessageFmt, fmt.Sprintf("/api/v1/namespaces/%s/configmaps/%s", expect.ConfigMap.Namespace, expect.ConfigMap.Name))
|
||||
}
|
||||
}
|
||||
if expectMessage != recent.Message {
|
||||
|
|
|
@ -57,8 +57,10 @@ var startServices = flag.Bool("start-services", true, "If true, start local node
|
|||
var stopServices = flag.Bool("stop-services", true, "If true, stop local node services after running tests")
|
||||
var busyboxImage = "busybox"
|
||||
|
||||
// Kubelet internal cgroup name for node allocatable cgroup.
|
||||
const defaultNodeAllocatableCgroup = "kubepods"
|
||||
const (
|
||||
// Kubelet internal cgroup name for node allocatable cgroup.
|
||||
defaultNodeAllocatableCgroup = "kubepods"
|
||||
)
|
||||
|
||||
func getNodeSummary() (*stats.Summary, error) {
|
||||
req, err := http.NewRequest("GET", *kubeletAddress+"/stats/summary", nil)
|
||||
|
@ -167,10 +169,11 @@ func setKubeletConfiguration(f *framework.Framework, kubeCfg *kubeletconfig.Kube
|
|||
|
||||
// create the reference and set Node.Spec.ConfigSource
|
||||
src := &apiv1.NodeConfigSource{
|
||||
ConfigMapRef: &apiv1.ObjectReference{
|
||||
Namespace: "kube-system",
|
||||
Name: cm.Name,
|
||||
UID: cm.UID,
|
||||
ConfigMap: &apiv1.ConfigMapNodeConfigSource{
|
||||
Namespace: "kube-system",
|
||||
Name: cm.Name,
|
||||
UID: cm.UID,
|
||||
KubeletConfigKey: "kubelet",
|
||||
},
|
||||
}
|
||||
|
||||
|
|
|
@ -54,7 +54,6 @@ var kindWhiteList = sets.NewString(
|
|||
"ExportOptions",
|
||||
"GetOptions",
|
||||
"ListOptions",
|
||||
"NodeConfigSource",
|
||||
"NodeProxyOptions",
|
||||
"PodAttachOptions",
|
||||
"PodExecOptions",
|
||||
|
|
|
@ -286,12 +286,13 @@ func TestNodeAuthorizer(t *testing.T) {
|
|||
return err
|
||||
}
|
||||
node2.Spec.ConfigSource = &api.NodeConfigSource{
|
||||
ConfigMapRef: &api.ObjectReference{
|
||||
ConfigMap: &api.ConfigMapNodeConfigSource{
|
||||
Namespace: "ns",
|
||||
Name: "myconfigmapconfigsource",
|
||||
// validation just requires UID to be non-empty and it isn't necessary for GET,
|
||||
// so we just use a bogus one for the test
|
||||
UID: "uid",
|
||||
UID: "uid",
|
||||
KubeletConfigKey: "kubelet",
|
||||
},
|
||||
}
|
||||
_, err = client.Core().Nodes().Update(node2)
|
||||
|
|
|
@ -437,7 +437,6 @@ var ephemeralWhiteList = createEphemeralWhiteList(
|
|||
gvk("", "v1", "RangeAllocation"), // stored in various places in etcd but cannot be directly created
|
||||
gvk("", "v1", "ComponentStatus"), // status info not stored in etcd
|
||||
gvk("", "v1", "SerializedReference"), // used for serilization, not stored in etcd
|
||||
gvk("", "v1", "NodeConfigSource"), // subfield of node.spec, but shouldn't be directly created
|
||||
gvk("", "v1", "PodStatusResult"), // wrapper object not stored in etcd
|
||||
// --
|
||||
|
||||
|
|
Loading…
Reference in New Issue