mirror of
				https://github.com/optim-enterprises-bv/kubernetes.git
				synced 2025-10-31 18:28:13 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			227 lines
		
	
	
		
			6.0 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			227 lines
		
	
	
		
			6.0 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| /*
 | |
| Copyright 2015 The Kubernetes Authors All rights reserved.
 | |
| 
 | |
| 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 (
 | |
| 	"fmt"
 | |
| 	"reflect"
 | |
| 	"strconv"
 | |
| 	"strings"
 | |
| 	"time"
 | |
| 
 | |
| 	clientset "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset"
 | |
| 
 | |
| 	log "github.com/golang/glog"
 | |
| 	mesos "github.com/mesos/mesos-go/mesosproto"
 | |
| 	"k8s.io/kubernetes/pkg/api"
 | |
| 	"k8s.io/kubernetes/pkg/api/errors"
 | |
| 	"k8s.io/kubernetes/pkg/api/unversioned"
 | |
| 	"k8s.io/kubernetes/pkg/util/validation"
 | |
| )
 | |
| 
 | |
| const (
 | |
| 	labelPrefix         = "k8s.mesosphere.io/attribute-"
 | |
| 	clientRetryCount    = 5
 | |
| 	clientRetryInterval = time.Second
 | |
| )
 | |
| 
 | |
| // Create creates a new node api object with the given hostname,
 | |
| // slave attribute labels and annotations
 | |
| func Create(
 | |
| 	client *clientset.Clientset,
 | |
| 	hostName string,
 | |
| 	slaveAttrLabels,
 | |
| 	annotations map[string]string,
 | |
| ) (*api.Node, error) {
 | |
| 	n := api.Node{
 | |
| 		ObjectMeta: api.ObjectMeta{
 | |
| 			Name: hostName,
 | |
| 		},
 | |
| 		Spec: api.NodeSpec{
 | |
| 			ExternalID: hostName,
 | |
| 		},
 | |
| 		Status: api.NodeStatus{
 | |
| 			Phase: api.NodePending,
 | |
| 			// WORKAROUND(sttts): make sure that the Ready condition is the
 | |
| 			// first one. The kube-ui v3 depends on this assumption.
 | |
| 			// TODO(sttts): remove this workaround when kube-ui v4 is used or we
 | |
| 			//              merge this with the statusupdate in the controller manager.
 | |
| 			Conditions: []api.NodeCondition{
 | |
| 				{
 | |
| 					Type:              api.NodeReady,
 | |
| 					Status:            api.ConditionTrue,
 | |
| 					Reason:            slaveReadyReason,
 | |
| 					Message:           slaveReadyMessage,
 | |
| 					LastHeartbeatTime: unversioned.Now(),
 | |
| 				},
 | |
| 			},
 | |
| 		},
 | |
| 	}
 | |
| 
 | |
| 	n.Labels = mergeMaps(
 | |
| 		map[string]string{"kubernetes.io/hostname": hostName},
 | |
| 		slaveAttrLabels,
 | |
| 	)
 | |
| 
 | |
| 	n.Annotations = annotations
 | |
| 
 | |
| 	// try to create
 | |
| 	return client.Nodes().Create(&n)
 | |
| }
 | |
| 
 | |
| // Update updates an existing node api object
 | |
| // by looking up the given hostname.
 | |
| // The updated node merges the given slave attribute labels
 | |
| // and annotations with the found api object.
 | |
| func Update(
 | |
| 	client *clientset.Clientset,
 | |
| 	hostname string,
 | |
| 	slaveAttrLabels,
 | |
| 	annotations map[string]string,
 | |
| ) (n *api.Node, err error) {
 | |
| 	for i := 0; i < clientRetryCount; i++ {
 | |
| 		n, err = client.Nodes().Get(hostname)
 | |
| 		if err != nil {
 | |
| 			return nil, fmt.Errorf("error getting node %q: %v", hostname, err)
 | |
| 		}
 | |
| 		if n == nil {
 | |
| 			return nil, fmt.Errorf("no node instance returned for %q", hostname)
 | |
| 		}
 | |
| 
 | |
| 		// update labels derived from Mesos slave attributes, keep all other labels
 | |
| 		n.Labels = mergeMaps(
 | |
| 			filterMap(n.Labels, IsNotSlaveAttributeLabel),
 | |
| 			slaveAttrLabels,
 | |
| 		)
 | |
| 		n.Annotations = mergeMaps(n.Annotations, annotations)
 | |
| 
 | |
| 		n, err = client.Nodes().Update(n)
 | |
| 		if err == nil && !errors.IsConflict(err) {
 | |
| 			return n, nil
 | |
| 		}
 | |
| 
 | |
| 		log.Infof("retry %d/%d: error updating node %v err %v", i, clientRetryCount, n, err)
 | |
| 		time.Sleep(time.Duration(i) * clientRetryInterval)
 | |
| 	}
 | |
| 
 | |
| 	return nil, err
 | |
| }
 | |
| 
 | |
| // CreateOrUpdate creates a node api object or updates an existing one
 | |
| func CreateOrUpdate(
 | |
| 	client *clientset.Clientset,
 | |
| 	hostname string,
 | |
| 	slaveAttrLabels,
 | |
| 	annotations map[string]string,
 | |
| ) (*api.Node, error) {
 | |
| 	n, err := Create(client, hostname, slaveAttrLabels, annotations)
 | |
| 	if err == nil {
 | |
| 		return n, nil
 | |
| 	}
 | |
| 
 | |
| 	if !errors.IsAlreadyExists(err) {
 | |
| 		return nil, fmt.Errorf("unable to register %q with the apiserver: %v", hostname, err)
 | |
| 	}
 | |
| 
 | |
| 	// fall back to update an old node with new labels
 | |
| 	return Update(client, hostname, slaveAttrLabels, annotations)
 | |
| }
 | |
| 
 | |
| // IsNotSlaveAttributeLabel returns true iff the given label is not derived from a slave attribute
 | |
| func IsNotSlaveAttributeLabel(key, value string) bool {
 | |
| 	return !IsSlaveAttributeLabel(key, value)
 | |
| }
 | |
| 
 | |
| // IsSlaveAttributeLabel returns true iff the given label is derived from a slave attribute
 | |
| func IsSlaveAttributeLabel(key, value string) bool {
 | |
| 	return strings.HasPrefix(key, labelPrefix)
 | |
| }
 | |
| 
 | |
| // IsUpToDate returns true iff the node's slave labels match the given attributes labels
 | |
| func IsUpToDate(n *api.Node, labels map[string]string) bool {
 | |
| 	slaveLabels := map[string]string{}
 | |
| 	for k, v := range n.Labels {
 | |
| 		if IsSlaveAttributeLabel(k, "") {
 | |
| 			slaveLabels[k] = v
 | |
| 		}
 | |
| 	}
 | |
| 	return reflect.DeepEqual(slaveLabels, labels)
 | |
| }
 | |
| 
 | |
| // SlaveAttributesToLabels converts slave attributes into string key/value labels
 | |
| func SlaveAttributesToLabels(attrs []*mesos.Attribute) map[string]string {
 | |
| 	l := map[string]string{}
 | |
| 	for _, a := range attrs {
 | |
| 		if a == nil {
 | |
| 			continue
 | |
| 		}
 | |
| 
 | |
| 		var v string
 | |
| 		k := labelPrefix + a.GetName()
 | |
| 
 | |
| 		switch a.GetType() {
 | |
| 		case mesos.Value_TEXT:
 | |
| 			v = a.GetText().GetValue()
 | |
| 		case mesos.Value_SCALAR:
 | |
| 			v = strconv.FormatFloat(a.GetScalar().GetValue(), 'G', -1, 64)
 | |
| 		}
 | |
| 
 | |
| 		if !validation.IsQualifiedName(k) {
 | |
| 			log.V(3).Infof("ignoring invalid node label name %q", k)
 | |
| 			continue
 | |
| 		}
 | |
| 
 | |
| 		if !validation.IsValidLabelValue(v) {
 | |
| 			log.V(3).Infof("ignoring invalid node label %s value: %q", k, v)
 | |
| 			continue
 | |
| 		}
 | |
| 
 | |
| 		l[k] = v
 | |
| 	}
 | |
| 	return l
 | |
| }
 | |
| 
 | |
| // filterMap filters the given map and returns a new map
 | |
| // containing all original elements matching the given key-value predicate.
 | |
| func filterMap(m map[string]string, predicate func(string, string) bool) map[string]string {
 | |
| 	result := make(map[string]string, len(m))
 | |
| 	for k, v := range m {
 | |
| 		if predicate(k, v) {
 | |
| 			result[k] = v
 | |
| 		}
 | |
| 	}
 | |
| 	return result
 | |
| }
 | |
| 
 | |
| // mergeMaps merges all given maps into a single map.
 | |
| // There is no advanced key conflict resolution.
 | |
| // The last key from the given maps wins.
 | |
| func mergeMaps(ms ...map[string]string) map[string]string {
 | |
| 	var l int
 | |
| 	for _, m := range ms {
 | |
| 		l += len(m)
 | |
| 	}
 | |
| 
 | |
| 	result := make(map[string]string, l)
 | |
| 	for _, m := range ms {
 | |
| 		for k, v := range m {
 | |
| 			result[k] = v
 | |
| 		}
 | |
| 	}
 | |
| 	return result
 | |
| }
 | 
