node_controller.go 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606
  1. /*
  2. Copyright 2016 The Kubernetes Authors.
  3. Licensed under the Apache License, Version 2.0 (the "License");
  4. you may not use this file except in compliance with the License.
  5. You may obtain a copy of the License at
  6. http://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. */
  13. package cloud
  14. import (
  15. "context"
  16. "errors"
  17. "fmt"
  18. "time"
  19. "k8s.io/api/core/v1"
  20. apierrors "k8s.io/apimachinery/pkg/api/errors"
  21. metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
  22. "k8s.io/apimachinery/pkg/types"
  23. utilruntime "k8s.io/apimachinery/pkg/util/runtime"
  24. "k8s.io/apimachinery/pkg/util/wait"
  25. coreinformers "k8s.io/client-go/informers/core/v1"
  26. clientset "k8s.io/client-go/kubernetes"
  27. "k8s.io/client-go/kubernetes/scheme"
  28. v1core "k8s.io/client-go/kubernetes/typed/core/v1"
  29. "k8s.io/client-go/tools/cache"
  30. "k8s.io/client-go/tools/record"
  31. clientretry "k8s.io/client-go/util/retry"
  32. cloudprovider "k8s.io/cloud-provider"
  33. cloudnodeutil "k8s.io/cloud-provider/node/helpers"
  34. "k8s.io/klog"
  35. kubeletapis "k8s.io/kubernetes/pkg/kubelet/apis"
  36. schedulerapi "k8s.io/kubernetes/pkg/scheduler/api"
  37. nodeutil "k8s.io/kubernetes/pkg/util/node"
  38. )
  39. // labelReconcileInfo lists Node labels to reconcile, and how to reconcile them.
  40. // primaryKey and secondaryKey are keys of labels to reconcile.
  41. // - If both keys exist, but their values don't match. Use the value from the
  42. // primaryKey as the source of truth to reconcile.
  43. // - If ensureSecondaryExists is true, and the secondaryKey does not
  44. // exist, secondaryKey will be added with the value of the primaryKey.
  45. var labelReconcileInfo = []struct {
  46. primaryKey string
  47. secondaryKey string
  48. ensureSecondaryExists bool
  49. }{
  50. {
  51. // Reconcile the beta and the GA zone label using the beta label as
  52. // the source of truth
  53. // TODO: switch the primary key to GA labels in v1.21
  54. primaryKey: v1.LabelZoneFailureDomain,
  55. secondaryKey: v1.LabelZoneFailureDomainStable,
  56. ensureSecondaryExists: true,
  57. },
  58. {
  59. // Reconcile the beta and the stable region label using the beta label as
  60. // the source of truth
  61. // TODO: switch the primary key to GA labels in v1.21
  62. primaryKey: v1.LabelZoneRegion,
  63. secondaryKey: v1.LabelZoneRegionStable,
  64. ensureSecondaryExists: true,
  65. },
  66. {
  67. // Reconcile the beta and the stable instance-type label using the beta label as
  68. // the source of truth
  69. // TODO: switch the primary key to GA labels in v1.21
  70. primaryKey: v1.LabelInstanceType,
  71. secondaryKey: v1.LabelInstanceTypeStable,
  72. ensureSecondaryExists: true,
  73. },
  74. }
  75. var UpdateNodeSpecBackoff = wait.Backoff{
  76. Steps: 20,
  77. Duration: 50 * time.Millisecond,
  78. Jitter: 1.0,
  79. }
  80. type CloudNodeController struct {
  81. nodeInformer coreinformers.NodeInformer
  82. kubeClient clientset.Interface
  83. recorder record.EventRecorder
  84. cloud cloudprovider.Interface
  85. nodeStatusUpdateFrequency time.Duration
  86. }
  87. // NewCloudNodeController creates a CloudNodeController object
  88. func NewCloudNodeController(
  89. nodeInformer coreinformers.NodeInformer,
  90. kubeClient clientset.Interface,
  91. cloud cloudprovider.Interface,
  92. nodeStatusUpdateFrequency time.Duration) (*CloudNodeController, error) {
  93. eventBroadcaster := record.NewBroadcaster()
  94. recorder := eventBroadcaster.NewRecorder(scheme.Scheme, v1.EventSource{Component: "cloud-node-controller"})
  95. eventBroadcaster.StartLogging(klog.Infof)
  96. klog.Infof("Sending events to api server.")
  97. eventBroadcaster.StartRecordingToSink(&v1core.EventSinkImpl{Interface: kubeClient.CoreV1().Events("")})
  98. if _, ok := cloud.Instances(); !ok {
  99. return nil, errors.New("cloud provider does not support instances")
  100. }
  101. cnc := &CloudNodeController{
  102. nodeInformer: nodeInformer,
  103. kubeClient: kubeClient,
  104. recorder: recorder,
  105. cloud: cloud,
  106. nodeStatusUpdateFrequency: nodeStatusUpdateFrequency,
  107. }
  108. // Use shared informer to listen to add/update of nodes. Note that any nodes
  109. // that exist before node controller starts will show up in the update method
  110. cnc.nodeInformer.Informer().AddEventHandler(cache.ResourceEventHandlerFuncs{
  111. AddFunc: func(obj interface{}) { cnc.AddCloudNode(context.TODO(), obj) },
  112. UpdateFunc: func(oldObj, newObj interface{}) { cnc.UpdateCloudNode(context.TODO(), oldObj, newObj) },
  113. })
  114. return cnc, nil
  115. }
  116. // This controller updates newly registered nodes with information
  117. // from the cloud provider. This call is blocking so should be called
  118. // via a goroutine
  119. func (cnc *CloudNodeController) Run(stopCh <-chan struct{}) {
  120. defer utilruntime.HandleCrash()
  121. // The following loops run communicate with the APIServer with a worst case complexity
  122. // of O(num_nodes) per cycle. These functions are justified here because these events fire
  123. // very infrequently. DO NOT MODIFY this to perform frequent operations.
  124. // Start a loop to periodically update the node addresses obtained from the cloud
  125. wait.Until(func() { cnc.UpdateNodeStatus(context.TODO()) }, cnc.nodeStatusUpdateFrequency, stopCh)
  126. }
  127. // UpdateNodeStatus updates the node status, such as node addresses
  128. func (cnc *CloudNodeController) UpdateNodeStatus(ctx context.Context) {
  129. instances, ok := cnc.cloud.Instances()
  130. if !ok {
  131. utilruntime.HandleError(fmt.Errorf("failed to get instances from cloud provider"))
  132. return
  133. }
  134. nodes, err := cnc.kubeClient.CoreV1().Nodes().List(context.TODO(), metav1.ListOptions{ResourceVersion: "0"})
  135. if err != nil {
  136. klog.Errorf("Error monitoring node status: %v", err)
  137. return
  138. }
  139. for i := range nodes.Items {
  140. cnc.updateNodeAddress(ctx, &nodes.Items[i], instances)
  141. }
  142. for _, node := range nodes.Items {
  143. err = cnc.reconcileNodeLabels(node.Name)
  144. if err != nil {
  145. klog.Errorf("Error reconciling node labels for node %q, err: %v", node.Name, err)
  146. }
  147. }
  148. }
  149. // reconcileNodeLabels reconciles node labels transitioning from beta to GA
  150. func (cnc *CloudNodeController) reconcileNodeLabels(nodeName string) error {
  151. node, err := cnc.nodeInformer.Lister().Get(nodeName)
  152. if err != nil {
  153. // If node not found, just ignore it.
  154. if apierrors.IsNotFound(err) {
  155. return nil
  156. }
  157. return err
  158. }
  159. if node.Labels == nil {
  160. // Nothing to reconcile.
  161. return nil
  162. }
  163. labelsToUpdate := map[string]string{}
  164. for _, r := range labelReconcileInfo {
  165. primaryValue, primaryExists := node.Labels[r.primaryKey]
  166. secondaryValue, secondaryExists := node.Labels[r.secondaryKey]
  167. if !primaryExists {
  168. // The primary label key does not exist. This should not happen
  169. // within our supported version skew range, when no external
  170. // components/factors modifying the node object. Ignore this case.
  171. continue
  172. }
  173. if secondaryExists && primaryValue != secondaryValue {
  174. // Secondary label exists, but not consistent with the primary
  175. // label. Need to reconcile.
  176. labelsToUpdate[r.secondaryKey] = primaryValue
  177. } else if !secondaryExists && r.ensureSecondaryExists {
  178. // Apply secondary label based on primary label.
  179. labelsToUpdate[r.secondaryKey] = primaryValue
  180. }
  181. }
  182. if len(labelsToUpdate) == 0 {
  183. return nil
  184. }
  185. if !cloudnodeutil.AddOrUpdateLabelsOnNode(cnc.kubeClient, labelsToUpdate, node) {
  186. return fmt.Errorf("failed update labels for node %+v", node)
  187. }
  188. return nil
  189. }
  190. // UpdateNodeAddress updates the nodeAddress of a single node
  191. func (cnc *CloudNodeController) updateNodeAddress(ctx context.Context, node *v1.Node, instances cloudprovider.Instances) {
  192. // Do not process nodes that are still tainted
  193. cloudTaint := getCloudTaint(node.Spec.Taints)
  194. if cloudTaint != nil {
  195. klog.V(5).Infof("This node %s is still tainted. Will not process.", node.Name)
  196. return
  197. }
  198. // Node that isn't present according to the cloud provider shouldn't have its address updated
  199. exists, err := ensureNodeExistsByProviderID(ctx, instances, node)
  200. if err != nil {
  201. // Continue to update node address when not sure the node is not exists
  202. klog.Errorf("%v", err)
  203. } else if !exists {
  204. klog.V(4).Infof("The node %s is no longer present according to the cloud provider, do not process.", node.Name)
  205. return
  206. }
  207. nodeAddresses, err := getNodeAddressesByProviderIDOrName(ctx, instances, node)
  208. if err != nil {
  209. klog.Errorf("Error getting node addresses for node %q: %v", node.Name, err)
  210. return
  211. }
  212. if len(nodeAddresses) == 0 {
  213. klog.V(5).Infof("Skipping node address update for node %q since cloud provider did not return any", node.Name)
  214. return
  215. }
  216. // Check if a hostname address exists in the cloud provided addresses
  217. hostnameExists := false
  218. for i := range nodeAddresses {
  219. if nodeAddresses[i].Type == v1.NodeHostName {
  220. hostnameExists = true
  221. break
  222. }
  223. }
  224. // If hostname was not present in cloud provided addresses, use the hostname
  225. // from the existing node (populated by kubelet)
  226. if !hostnameExists {
  227. for _, addr := range node.Status.Addresses {
  228. if addr.Type == v1.NodeHostName {
  229. nodeAddresses = append(nodeAddresses, addr)
  230. }
  231. }
  232. }
  233. // If nodeIP was suggested by user, ensure that
  234. // it can be found in the cloud as well (consistent with the behaviour in kubelet)
  235. if nodeIP, ok := ensureNodeProvidedIPExists(node, nodeAddresses); ok {
  236. if nodeIP == nil {
  237. klog.Errorf("Specified Node IP not found in cloudprovider for node %q", node.Name)
  238. return
  239. }
  240. }
  241. if !nodeAddressesChangeDetected(node.Status.Addresses, nodeAddresses) {
  242. return
  243. }
  244. newNode := node.DeepCopy()
  245. newNode.Status.Addresses = nodeAddresses
  246. _, _, err = nodeutil.PatchNodeStatus(cnc.kubeClient.CoreV1(), types.NodeName(node.Name), node, newNode)
  247. if err != nil {
  248. klog.Errorf("Error patching node with cloud ip addresses = [%v]", err)
  249. }
  250. }
  251. // nodeModifier is used to carry changes to node objects across multiple attempts to update them
  252. // in a retry-if-conflict loop.
  253. type nodeModifier func(*v1.Node)
  254. func (cnc *CloudNodeController) UpdateCloudNode(ctx context.Context, _, newObj interface{}) {
  255. node, ok := newObj.(*v1.Node)
  256. if !ok {
  257. utilruntime.HandleError(fmt.Errorf("unexpected object type: %v", newObj))
  258. return
  259. }
  260. cloudTaint := getCloudTaint(node.Spec.Taints)
  261. if cloudTaint == nil {
  262. // The node has already been initialized so nothing to do.
  263. return
  264. }
  265. cnc.initializeNode(ctx, node)
  266. }
  267. // AddCloudNode handles initializing new nodes registered with the cloud taint.
  268. func (cnc *CloudNodeController) AddCloudNode(ctx context.Context, obj interface{}) {
  269. node := obj.(*v1.Node)
  270. cloudTaint := getCloudTaint(node.Spec.Taints)
  271. if cloudTaint == nil {
  272. klog.V(2).Infof("This node %s is registered without the cloud taint. Will not process.", node.Name)
  273. return
  274. }
  275. cnc.initializeNode(ctx, node)
  276. }
  277. // This processes nodes that were added into the cluster, and cloud initialize them if appropriate
  278. func (cnc *CloudNodeController) initializeNode(ctx context.Context, node *v1.Node) {
  279. klog.Infof("Initializing node %s with cloud provider", node.Name)
  280. instances, ok := cnc.cloud.Instances()
  281. if !ok {
  282. utilruntime.HandleError(fmt.Errorf("failed to get instances from cloud provider"))
  283. return
  284. }
  285. err := clientretry.RetryOnConflict(UpdateNodeSpecBackoff, func() error {
  286. // TODO(wlan0): Move this logic to the route controller using the node taint instead of condition
  287. // Since there are node taints, do we still need this?
  288. // This condition marks the node as unusable until routes are initialized in the cloud provider
  289. if cnc.cloud.ProviderName() == "gce" {
  290. if err := cloudnodeutil.SetNodeCondition(cnc.kubeClient, types.NodeName(node.Name), v1.NodeCondition{
  291. Type: v1.NodeNetworkUnavailable,
  292. Status: v1.ConditionTrue,
  293. Reason: "NoRouteCreated",
  294. Message: "Node created without a route",
  295. LastTransitionTime: metav1.Now(),
  296. }); err != nil {
  297. return err
  298. }
  299. }
  300. return nil
  301. })
  302. if err != nil {
  303. utilruntime.HandleError(err)
  304. return
  305. }
  306. curNode, err := cnc.kubeClient.CoreV1().Nodes().Get(context.TODO(), node.Name, metav1.GetOptions{})
  307. if err != nil {
  308. utilruntime.HandleError(fmt.Errorf("failed to get node %s: %v", node.Name, err))
  309. return
  310. }
  311. cloudTaint := getCloudTaint(curNode.Spec.Taints)
  312. if cloudTaint == nil {
  313. // Node object received from event had the cloud taint but was outdated,
  314. // the node has actually already been initialized.
  315. return
  316. }
  317. nodeModifiers, err := cnc.getNodeModifiersFromCloudProvider(ctx, curNode, instances)
  318. if err != nil {
  319. utilruntime.HandleError(fmt.Errorf("failed to initialize node %s at cloudprovider: %v", node.Name, err))
  320. return
  321. }
  322. nodeModifiers = append(nodeModifiers, func(n *v1.Node) {
  323. n.Spec.Taints = excludeCloudTaint(n.Spec.Taints)
  324. })
  325. err = clientretry.RetryOnConflict(UpdateNodeSpecBackoff, func() error {
  326. curNode, err := cnc.kubeClient.CoreV1().Nodes().Get(context.TODO(), node.Name, metav1.GetOptions{})
  327. if err != nil {
  328. return err
  329. }
  330. for _, modify := range nodeModifiers {
  331. modify(curNode)
  332. }
  333. _, err = cnc.kubeClient.CoreV1().Nodes().Update(context.TODO(), curNode, metav1.UpdateOptions{})
  334. if err != nil {
  335. return err
  336. }
  337. // After adding, call UpdateNodeAddress to set the CloudProvider provided IPAddresses
  338. // So that users do not see any significant delay in IP addresses being filled into the node
  339. cnc.updateNodeAddress(ctx, curNode, instances)
  340. klog.Infof("Successfully initialized node %s with cloud provider", node.Name)
  341. return nil
  342. })
  343. if err != nil {
  344. utilruntime.HandleError(err)
  345. return
  346. }
  347. }
  348. // getNodeModifiersFromCloudProvider returns a slice of nodeModifiers that update
  349. // a node object with provider-specific information.
  350. // All of the returned functions are idempotent, because they are used in a retry-if-conflict
  351. // loop, meaning they could get called multiple times.
  352. func (cnc *CloudNodeController) getNodeModifiersFromCloudProvider(ctx context.Context, node *v1.Node, instances cloudprovider.Instances) ([]nodeModifier, error) {
  353. var nodeModifiers []nodeModifier
  354. if node.Spec.ProviderID == "" {
  355. providerID, err := cloudprovider.GetInstanceProviderID(ctx, cnc.cloud, types.NodeName(node.Name))
  356. if err == nil {
  357. nodeModifiers = append(nodeModifiers, func(n *v1.Node) {
  358. if n.Spec.ProviderID == "" {
  359. n.Spec.ProviderID = providerID
  360. }
  361. })
  362. } else if err == cloudprovider.NotImplemented {
  363. // if the cloud provider being used does not support provider IDs,
  364. // we can safely continue since we will attempt to set node
  365. // addresses given the node name in getNodeAddressesByProviderIDOrName
  366. klog.Warningf("cloud provider does not set node provider ID, using node name to discover node %s", node.Name)
  367. } else {
  368. // if the cloud provider being used supports provider IDs, we want
  369. // to propagate the error so that we re-try in the future; if we
  370. // do not, the taint will be removed, and this will not be retried
  371. return nil, err
  372. }
  373. }
  374. nodeAddresses, err := getNodeAddressesByProviderIDOrName(ctx, instances, node)
  375. if err != nil {
  376. return nil, err
  377. }
  378. // If user provided an IP address, ensure that IP address is found
  379. // in the cloud provider before removing the taint on the node
  380. if nodeIP, ok := ensureNodeProvidedIPExists(node, nodeAddresses); ok {
  381. if nodeIP == nil {
  382. return nil, errors.New("failed to find kubelet node IP from cloud provider")
  383. }
  384. }
  385. if instanceType, err := getInstanceTypeByProviderIDOrName(ctx, instances, node); err != nil {
  386. return nil, err
  387. } else if instanceType != "" {
  388. klog.V(2).Infof("Adding node label from cloud provider: %s=%s", v1.LabelInstanceType, instanceType)
  389. klog.V(2).Infof("Adding node label from cloud provider: %s=%s", v1.LabelInstanceTypeStable, instanceType)
  390. nodeModifiers = append(nodeModifiers, func(n *v1.Node) {
  391. if n.Labels == nil {
  392. n.Labels = map[string]string{}
  393. }
  394. n.Labels[v1.LabelInstanceType] = instanceType
  395. n.Labels[v1.LabelInstanceTypeStable] = instanceType
  396. })
  397. }
  398. if zones, ok := cnc.cloud.Zones(); ok {
  399. zone, err := getZoneByProviderIDOrName(ctx, zones, node)
  400. if err != nil {
  401. return nil, fmt.Errorf("failed to get zone from cloud provider: %v", err)
  402. }
  403. if zone.FailureDomain != "" {
  404. klog.V(2).Infof("Adding node label from cloud provider: %s=%s", v1.LabelZoneFailureDomain, zone.FailureDomain)
  405. klog.V(2).Infof("Adding node label from cloud provider: %s=%s", v1.LabelZoneFailureDomainStable, zone.FailureDomain)
  406. nodeModifiers = append(nodeModifiers, func(n *v1.Node) {
  407. if n.Labels == nil {
  408. n.Labels = map[string]string{}
  409. }
  410. n.Labels[v1.LabelZoneFailureDomain] = zone.FailureDomain
  411. n.Labels[v1.LabelZoneFailureDomainStable] = zone.FailureDomain
  412. })
  413. }
  414. if zone.Region != "" {
  415. klog.V(2).Infof("Adding node label from cloud provider: %s=%s", v1.LabelZoneRegion, zone.Region)
  416. klog.V(2).Infof("Adding node label from cloud provider: %s=%s", v1.LabelZoneRegionStable, zone.Region)
  417. nodeModifiers = append(nodeModifiers, func(n *v1.Node) {
  418. if n.Labels == nil {
  419. n.Labels = map[string]string{}
  420. }
  421. n.Labels[v1.LabelZoneRegion] = zone.Region
  422. n.Labels[v1.LabelZoneRegionStable] = zone.Region
  423. })
  424. }
  425. }
  426. return nodeModifiers, nil
  427. }
  428. func getCloudTaint(taints []v1.Taint) *v1.Taint {
  429. for _, taint := range taints {
  430. if taint.Key == schedulerapi.TaintExternalCloudProvider {
  431. return &taint
  432. }
  433. }
  434. return nil
  435. }
  436. func excludeCloudTaint(taints []v1.Taint) []v1.Taint {
  437. newTaints := []v1.Taint{}
  438. for _, taint := range taints {
  439. if taint.Key == schedulerapi.TaintExternalCloudProvider {
  440. continue
  441. }
  442. newTaints = append(newTaints, taint)
  443. }
  444. return newTaints
  445. }
  446. // ensureNodeExistsByProviderID checks if the instance exists by the provider id,
  447. // If provider id in spec is empty it calls instanceId with node name to get provider id
  448. func ensureNodeExistsByProviderID(ctx context.Context, instances cloudprovider.Instances, node *v1.Node) (bool, error) {
  449. providerID := node.Spec.ProviderID
  450. if providerID == "" {
  451. var err error
  452. providerID, err = instances.InstanceID(ctx, types.NodeName(node.Name))
  453. if err != nil {
  454. if err == cloudprovider.InstanceNotFound {
  455. return false, nil
  456. }
  457. return false, err
  458. }
  459. if providerID == "" {
  460. klog.Warningf("Cannot find valid providerID for node name %q, assuming non existence", node.Name)
  461. return false, nil
  462. }
  463. }
  464. return instances.InstanceExistsByProviderID(ctx, providerID)
  465. }
  466. func getNodeAddressesByProviderIDOrName(ctx context.Context, instances cloudprovider.Instances, node *v1.Node) ([]v1.NodeAddress, error) {
  467. nodeAddresses, err := instances.NodeAddressesByProviderID(ctx, node.Spec.ProviderID)
  468. if err != nil {
  469. providerIDErr := err
  470. nodeAddresses, err = instances.NodeAddresses(ctx, types.NodeName(node.Name))
  471. if err != nil {
  472. return nil, fmt.Errorf("error fetching node by provider ID: %v, and error by node name: %v", providerIDErr, err)
  473. }
  474. }
  475. return nodeAddresses, nil
  476. }
  477. func nodeAddressesChangeDetected(addressSet1, addressSet2 []v1.NodeAddress) bool {
  478. if len(addressSet1) != len(addressSet2) {
  479. return true
  480. }
  481. addressMap1 := map[v1.NodeAddressType]string{}
  482. for i := range addressSet1 {
  483. addressMap1[addressSet1[i].Type] = addressSet1[i].Address
  484. }
  485. for _, v := range addressSet2 {
  486. if addressMap1[v.Type] != v.Address {
  487. return true
  488. }
  489. }
  490. return false
  491. }
  492. func ensureNodeProvidedIPExists(node *v1.Node, nodeAddresses []v1.NodeAddress) (*v1.NodeAddress, bool) {
  493. var nodeIP *v1.NodeAddress
  494. nodeIPExists := false
  495. if providedIP, ok := node.ObjectMeta.Annotations[kubeletapis.AnnotationProvidedIPAddr]; ok {
  496. nodeIPExists = true
  497. for i := range nodeAddresses {
  498. if nodeAddresses[i].Address == providedIP {
  499. nodeIP = &nodeAddresses[i]
  500. break
  501. }
  502. }
  503. }
  504. return nodeIP, nodeIPExists
  505. }
  506. func getInstanceTypeByProviderIDOrName(ctx context.Context, instances cloudprovider.Instances, node *v1.Node) (string, error) {
  507. instanceType, err := instances.InstanceTypeByProviderID(ctx, node.Spec.ProviderID)
  508. if err != nil {
  509. providerIDErr := err
  510. instanceType, err = instances.InstanceType(ctx, types.NodeName(node.Name))
  511. if err != nil {
  512. return "", fmt.Errorf("InstanceType: Error fetching by providerID: %v Error fetching by NodeName: %v", providerIDErr, err)
  513. }
  514. }
  515. return instanceType, err
  516. }
  517. // getZoneByProviderIDorName will attempt to get the zone of node using its providerID
  518. // then it's name. If both attempts fail, an error is returned
  519. func getZoneByProviderIDOrName(ctx context.Context, zones cloudprovider.Zones, node *v1.Node) (cloudprovider.Zone, error) {
  520. zone, err := zones.GetZoneByProviderID(ctx, node.Spec.ProviderID)
  521. if err != nil {
  522. providerIDErr := err
  523. zone, err = zones.GetZoneByNodeName(ctx, types.NodeName(node.Name))
  524. if err != nil {
  525. return cloudprovider.Zone{}, fmt.Errorf("Zone: Error fetching by providerID: %v Error fetching by NodeName: %v", providerIDErr, err)
  526. }
  527. }
  528. return zone, nil
  529. }