| /* |
| Copyright 2014 The Kubernetes Authors. |
| |
| Licensed under the Apache License, Version 2.0 (the "License"); |
| you may not use this file except in compliance with the License. |
| You may obtain a copy of the License at |
| |
| http://www.apache.org/licenses/LICENSE-2.0 |
| |
| Unless required by applicable law or agreed to in writing, software |
| distributed under the License is distributed on an "AS IS" BASIS, |
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| See the License for the specific language governing permissions and |
| limitations under the License. |
| */ |
| |
| package node |
| |
| import ( |
| "context" |
| "fmt" |
| "net" |
| "net/http" |
| "net/url" |
| |
| "k8s.io/apimachinery/pkg/api/errors" |
| metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" |
| "k8s.io/apimachinery/pkg/fields" |
| "k8s.io/apimachinery/pkg/labels" |
| "k8s.io/apimachinery/pkg/runtime" |
| "k8s.io/apimachinery/pkg/types" |
| utilnet "k8s.io/apimachinery/pkg/util/net" |
| "k8s.io/apimachinery/pkg/util/validation/field" |
| "k8s.io/apiserver/pkg/registry/generic" |
| pkgstorage "k8s.io/apiserver/pkg/storage" |
| "k8s.io/apiserver/pkg/storage/names" |
| utilfeature "k8s.io/apiserver/pkg/util/feature" |
| "k8s.io/kubernetes/pkg/api/legacyscheme" |
| api "k8s.io/kubernetes/pkg/apis/core" |
| "k8s.io/kubernetes/pkg/apis/core/validation" |
| "k8s.io/kubernetes/pkg/features" |
| "k8s.io/kubernetes/pkg/kubelet/client" |
| ) |
| |
| // nodeStrategy implements behavior for nodes |
| type nodeStrategy struct { |
| runtime.ObjectTyper |
| names.NameGenerator |
| } |
| |
| // Nodes is the default logic that applies when creating and updating Node |
| // objects. |
| var Strategy = nodeStrategy{legacyscheme.Scheme, names.SimpleNameGenerator} |
| |
| // NamespaceScoped is false for nodes. |
| func (nodeStrategy) NamespaceScoped() bool { |
| return false |
| } |
| |
| // AllowCreateOnUpdate is false for nodes. |
| func (nodeStrategy) AllowCreateOnUpdate() bool { |
| return false |
| } |
| |
| // PrepareForCreate clears fields that are not allowed to be set by end users on creation. |
| func (nodeStrategy) PrepareForCreate(ctx context.Context, obj runtime.Object) { |
| node := obj.(*api.Node) |
| // Nodes allow *all* fields, including status, to be set on create. |
| |
| if !utilfeature.DefaultFeatureGate.Enabled(features.DynamicKubeletConfig) { |
| node.Spec.ConfigSource = nil |
| } |
| } |
| |
| // PrepareForUpdate clears fields that are not allowed to be set by end users on update. |
| func (nodeStrategy) PrepareForUpdate(ctx context.Context, obj, old runtime.Object) { |
| newNode := obj.(*api.Node) |
| oldNode := old.(*api.Node) |
| newNode.Status = oldNode.Status |
| |
| if !utilfeature.DefaultFeatureGate.Enabled(features.DynamicKubeletConfig) { |
| newNode.Spec.ConfigSource = nil |
| oldNode.Spec.ConfigSource = nil |
| } |
| } |
| |
| // Validate validates a new node. |
| func (nodeStrategy) Validate(ctx context.Context, obj runtime.Object) field.ErrorList { |
| node := obj.(*api.Node) |
| return validation.ValidateNode(node) |
| } |
| |
| // Canonicalize normalizes the object after validation. |
| func (nodeStrategy) Canonicalize(obj runtime.Object) { |
| } |
| |
| // ValidateUpdate is the default update validation for an end user. |
| func (nodeStrategy) ValidateUpdate(ctx context.Context, obj, old runtime.Object) field.ErrorList { |
| errorList := validation.ValidateNode(obj.(*api.Node)) |
| return append(errorList, validation.ValidateNodeUpdate(obj.(*api.Node), old.(*api.Node))...) |
| } |
| |
| func (nodeStrategy) AllowUnconditionalUpdate() bool { |
| return true |
| } |
| |
| func (ns nodeStrategy) Export(ctx context.Context, obj runtime.Object, exact bool) error { |
| n, ok := obj.(*api.Node) |
| if !ok { |
| // unexpected programmer error |
| return fmt.Errorf("unexpected object: %v", obj) |
| } |
| ns.PrepareForCreate(ctx, obj) |
| if exact { |
| return nil |
| } |
| // Nodes are the only resources that allow direct status edits, therefore |
| // we clear that without exact so that the node value can be reused. |
| n.Status = api.NodeStatus{} |
| return nil |
| } |
| |
| type nodeStatusStrategy struct { |
| nodeStrategy |
| } |
| |
| var StatusStrategy = nodeStatusStrategy{Strategy} |
| |
| func (nodeStatusStrategy) PrepareForCreate(ctx context.Context, obj runtime.Object) { |
| node := obj.(*api.Node) |
| // Nodes allow *all* fields, including status, to be set on create. |
| |
| if !utilfeature.DefaultFeatureGate.Enabled(features.DynamicKubeletConfig) { |
| node.Status.Config = nil |
| } |
| } |
| |
| func (nodeStatusStrategy) PrepareForUpdate(ctx context.Context, obj, old runtime.Object) { |
| newNode := obj.(*api.Node) |
| oldNode := old.(*api.Node) |
| newNode.Spec = oldNode.Spec |
| |
| if !utilfeature.DefaultFeatureGate.Enabled(features.DynamicKubeletConfig) { |
| newNode.Status.Config = nil |
| oldNode.Status.Config = nil |
| } |
| } |
| |
| func (nodeStatusStrategy) ValidateUpdate(ctx context.Context, obj, old runtime.Object) field.ErrorList { |
| return validation.ValidateNodeUpdate(obj.(*api.Node), old.(*api.Node)) |
| } |
| |
| // Canonicalize normalizes the object after validation. |
| func (nodeStatusStrategy) Canonicalize(obj runtime.Object) { |
| } |
| |
| // ResourceGetter is an interface for retrieving resources by ResourceLocation. |
| type ResourceGetter interface { |
| Get(context.Context, string, *metav1.GetOptions) (runtime.Object, error) |
| } |
| |
| // NodeToSelectableFields returns a field set that represents the object. |
| func NodeToSelectableFields(node *api.Node) fields.Set { |
| objectMetaFieldsSet := generic.ObjectMetaFieldsSet(&node.ObjectMeta, false) |
| specificFieldsSet := fields.Set{ |
| "spec.unschedulable": fmt.Sprint(node.Spec.Unschedulable), |
| } |
| return generic.MergeFieldsSets(objectMetaFieldsSet, specificFieldsSet) |
| } |
| |
| // GetAttrs returns labels and fields of a given object for filtering purposes. |
| func GetAttrs(obj runtime.Object) (labels.Set, fields.Set, bool, error) { |
| nodeObj, ok := obj.(*api.Node) |
| if !ok { |
| return nil, nil, false, fmt.Errorf("not a node") |
| } |
| return labels.Set(nodeObj.ObjectMeta.Labels), NodeToSelectableFields(nodeObj), nodeObj.Initializers != nil, nil |
| } |
| |
| // MatchNode returns a generic matcher for a given label and field selector. |
| func MatchNode(label labels.Selector, field fields.Selector) pkgstorage.SelectionPredicate { |
| return pkgstorage.SelectionPredicate{ |
| Label: label, |
| Field: field, |
| GetAttrs: GetAttrs, |
| IndexFields: []string{"metadata.name"}, |
| } |
| } |
| |
| func NodeNameTriggerFunc(obj runtime.Object) []pkgstorage.MatchValue { |
| node := obj.(*api.Node) |
| result := pkgstorage.MatchValue{IndexName: "metadata.name", Value: node.ObjectMeta.Name} |
| return []pkgstorage.MatchValue{result} |
| } |
| |
| // ResourceLocation returns a URL and transport which one can use to send traffic for the specified node. |
| func ResourceLocation(getter ResourceGetter, connection client.ConnectionInfoGetter, proxyTransport http.RoundTripper, ctx context.Context, id string) (*url.URL, http.RoundTripper, error) { |
| schemeReq, name, portReq, valid := utilnet.SplitSchemeNamePort(id) |
| if !valid { |
| return nil, nil, errors.NewBadRequest(fmt.Sprintf("invalid node request %q", id)) |
| } |
| |
| info, err := connection.GetConnectionInfo(ctx, types.NodeName(name)) |
| if err != nil { |
| return nil, nil, err |
| } |
| |
| // We check if we want to get a default Kubelet's transport. It happens if either: |
| // - no port is specified in request (Kubelet's port is default) |
| // - the requested port matches the kubelet port for this node |
| if portReq == "" || portReq == info.Port { |
| return &url.URL{ |
| Scheme: info.Scheme, |
| Host: net.JoinHostPort(info.Hostname, info.Port), |
| }, |
| info.Transport, |
| nil |
| } |
| |
| // Otherwise, return the requested scheme and port, and the proxy transport |
| return &url.URL{Scheme: schemeReq, Host: net.JoinHostPort(info.Hostname, portReq)}, proxyTransport, nil |
| } |