|
| 1 | +/* |
| 2 | +Copyright 2020 The Kubernetes Authors. |
| 3 | +
|
| 4 | +Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | +you may not use this file except in compliance with the License. |
| 6 | +You may obtain a copy of the License at |
| 7 | +
|
| 8 | + http://www.apache.org/licenses/LICENSE-2.0 |
| 9 | +
|
| 10 | +Unless required by applicable law or agreed to in writing, software |
| 11 | +distributed under the License is distributed on an "AS IS" BASIS, |
| 12 | +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 13 | +See the License for the specific language governing permissions and |
| 14 | +limitations under the License. |
| 15 | +*/ |
| 16 | + |
| 17 | +package huaweicloud |
| 18 | + |
| 19 | +import ( |
| 20 | + "fmt" |
| 21 | + |
| 22 | + apiv1 "k8s.io/api/core/v1" |
| 23 | + "k8s.io/autoscaler/cluster-autoscaler/cloudprovider" |
| 24 | + "k8s.io/klog/v2" |
| 25 | + schedulerframework "k8s.io/kubernetes/pkg/scheduler/framework/v1alpha1" |
| 26 | +) |
| 27 | + |
| 28 | +// AutoScalingGroup represents a HuaweiCloud's 'Auto Scaling Group' which also can be treated as a node group. |
| 29 | +type AutoScalingGroup struct { |
| 30 | + cloudServiceManager CloudServiceManager |
| 31 | + |
| 32 | + groupID string |
| 33 | + minInstanceNumber int |
| 34 | + maxInstanceNumber int |
| 35 | +} |
| 36 | + |
| 37 | +// Check if our AutoScalingGroup implements necessary interface. |
| 38 | +var _ cloudprovider.NodeGroup = &AutoScalingGroup{} |
| 39 | + |
| 40 | +// MaxSize returns maximum size of the node group. |
| 41 | +func (asg *AutoScalingGroup) MaxSize() int { |
| 42 | + return asg.maxInstanceNumber |
| 43 | +} |
| 44 | + |
| 45 | +// MinSize returns minimum size of the node group. |
| 46 | +func (asg *AutoScalingGroup) MinSize() int { |
| 47 | + return asg.minInstanceNumber |
| 48 | +} |
| 49 | + |
| 50 | +// TargetSize returns the current target size of the node group. It is possible that the |
| 51 | +// number of nodes in Kubernetes is different at the moment but should be equal |
| 52 | +// to Size() once everything stabilizes (new nodes finish startup and registration or |
| 53 | +// removed nodes are deleted completely). Implementation required. |
| 54 | +// |
| 55 | +// Target size is desire instance number of the auto scaling group, and not equal to current instance number if the |
| 56 | +// auto scaling group is in increasing or decreasing process. |
| 57 | +func (asg *AutoScalingGroup) TargetSize() (int, error) { |
| 58 | + desireNumber, err := asg.cloudServiceManager.GetDesireInstanceNumber(asg.groupID) |
| 59 | + if err != nil { |
| 60 | + klog.Warningf("failed to get group target size. groupID: %s, error: %v", asg.groupID, err) |
| 61 | + return 0, err |
| 62 | + } |
| 63 | + |
| 64 | + return desireNumber, nil |
| 65 | +} |
| 66 | + |
| 67 | +// IncreaseSize increases the size of the node group. To delete a node you need |
| 68 | +// to explicitly name it and use DeleteNode. This function should wait until |
| 69 | +// node group size is updated. Implementation required. |
| 70 | +func (asg *AutoScalingGroup) IncreaseSize(delta int) error { |
| 71 | + err := asg.cloudServiceManager.IncreaseSizeInstance(asg.groupID, delta) |
| 72 | + if err != nil { |
| 73 | + klog.Warningf("failed to increase size for group: %s, error: %v", asg.groupID, err) |
| 74 | + return err |
| 75 | + } |
| 76 | + |
| 77 | + return nil |
| 78 | +} |
| 79 | + |
| 80 | +// DeleteNodes deletes nodes from this node group. Error is returned either on |
| 81 | +// failure or if the given node doesn't belong to this node group. This function |
| 82 | +// should wait until node group size is updated. Implementation required. |
| 83 | +func (asg *AutoScalingGroup) DeleteNodes(nodes []*apiv1.Node) error { |
| 84 | + instances, err := asg.cloudServiceManager.GetInstances(asg.groupID) |
| 85 | + if err != nil { |
| 86 | + klog.Warningf("failed to get nodes from group: %s, error: %v", asg.groupID, err) |
| 87 | + return err |
| 88 | + } |
| 89 | + |
| 90 | + // TODO(RainbowMango): Check if all node in this group. |
| 91 | + // If one of the node is not belong to this group, just return error. |
| 92 | + |
| 93 | + servers := make([]string, 0, len(instances)) |
| 94 | + for _, instance := range instances { |
| 95 | + servers = append(servers, *instance.InstanceId) |
| 96 | + } |
| 97 | + |
| 98 | + err = asg.cloudServiceManager.DeleteServers(servers) |
| 99 | + if err != nil { |
| 100 | + klog.Warningf("failed to delete nodes. error: %v", err) |
| 101 | + return err |
| 102 | + } |
| 103 | + |
| 104 | + return nil |
| 105 | +} |
| 106 | + |
| 107 | +// DecreaseTargetSize decreases the target size of the node group. This function |
| 108 | +// doesn't permit to delete any existing node and can be used only to reduce the |
| 109 | +// request for new nodes that have not been yet fulfilled. Delta should be negative. |
| 110 | +// It is assumed that cloud provider will not delete the existing nodes when there |
| 111 | +// is an option to just decrease the target. Implementation required. |
| 112 | +func (asg *AutoScalingGroup) DecreaseTargetSize(delta int) error { |
| 113 | + // TODO(RainbowMango): Just remove nodes from group not delete them? |
| 114 | + return cloudprovider.ErrNotImplemented |
| 115 | +} |
| 116 | + |
| 117 | +// Id returns an unique identifier of the node group. |
| 118 | +func (asg *AutoScalingGroup) Id() string { |
| 119 | + return asg.groupID |
| 120 | +} |
| 121 | + |
| 122 | +// Debug returns a string containing all information regarding this node group. |
| 123 | +func (asg *AutoScalingGroup) Debug() string { |
| 124 | + return asg.String() |
| 125 | +} |
| 126 | + |
| 127 | +// Nodes returns a list of all nodes that belong to this node group. |
| 128 | +// It is required that Instance objects returned by this method have Id field set. |
| 129 | +// Other fields are optional. |
| 130 | +// This list should include also instances that might have not become a kubernetes node yet. |
| 131 | +func (asg *AutoScalingGroup) Nodes() ([]cloudprovider.Instance, error) { |
| 132 | + instances, err := asg.cloudServiceManager.GetInstances(asg.groupID) |
| 133 | + if err != nil { |
| 134 | + klog.Warningf("failed to get nodes from group: %s, error: %v", asg.groupID, err) |
| 135 | + return nil, err |
| 136 | + } |
| 137 | + if len(instances) == 0 { |
| 138 | + return nil, nil |
| 139 | + } |
| 140 | + |
| 141 | + // TODO(RainbowMango) Convert AS instances to cloud provider instances. Especially convert status. |
| 142 | + providerInstances := make([]cloudprovider.Instance, 0, len(instances)) |
| 143 | + for i := range instances { |
| 144 | + pInstances := cloudprovider.Instance{ |
| 145 | + Id: *instances[i].InstanceId, |
| 146 | + Status: nil, |
| 147 | + } |
| 148 | + |
| 149 | + providerInstances = append(providerInstances, pInstances) |
| 150 | + } |
| 151 | + return nil, nil |
| 152 | +} |
| 153 | + |
| 154 | +// TemplateNodeInfo returns a schedulerframework.NodeInfo structure of an empty |
| 155 | +// (as if just started) node. This will be used in scale-up simulations to |
| 156 | +// predict what would a new node look like if a node group was expanded. The returned |
| 157 | +// NodeInfo is expected to have a fully populated Node object, with all of the labels, |
| 158 | +// capacity and allocatable information as well as all pods that are started on |
| 159 | +// the node by default, using manifest (most likely only kube-proxy). Implementation optional. |
| 160 | +func (asg *AutoScalingGroup) TemplateNodeInfo() (*schedulerframework.NodeInfo, error) { |
| 161 | + return nil, cloudprovider.ErrNotImplemented |
| 162 | +} |
| 163 | + |
| 164 | +// Exist checks if the node group really exists on the cloud provider side. Allows to tell the |
| 165 | +// theoretical node group from the real one. Implementation required. |
| 166 | +func (asg *AutoScalingGroup) Exist() bool { |
| 167 | + // Since all group synced from remote and we do not support auto provision, |
| 168 | + // so we can assume that the group always exist. |
| 169 | + return true |
| 170 | +} |
| 171 | + |
| 172 | +// Create creates the node group on the cloud provider side. Implementation optional. |
| 173 | +func (asg *AutoScalingGroup) Create() (cloudprovider.NodeGroup, error) { |
| 174 | + return nil, cloudprovider.ErrNotImplemented |
| 175 | +} |
| 176 | + |
| 177 | +// Delete deletes the node group on the cloud provider side. |
| 178 | +// This will be executed only for autoprovisioned node groups, once their size drops to 0. |
| 179 | +// Implementation optional. |
| 180 | +func (asg *AutoScalingGroup) Delete() error { |
| 181 | + return cloudprovider.ErrNotImplemented |
| 182 | +} |
| 183 | + |
| 184 | +// Autoprovisioned returns true if the node group is autoprovisioned. An autoprovisioned group |
| 185 | +// was created by CA and can be deleted when scaled to 0. |
| 186 | +// |
| 187 | +// Always return false because the node group should maintained by user. |
| 188 | +func (asg *AutoScalingGroup) Autoprovisioned() bool { |
| 189 | + return false |
| 190 | +} |
| 191 | + |
| 192 | +// String dumps current groups meta data. |
| 193 | +func (asg *AutoScalingGroup) String() string { |
| 194 | + return fmt.Sprintf("group: %s min=%d max=%d", asg.groupID, asg.minInstanceNumber, asg.maxInstanceNumber) |
| 195 | +} |
0 commit comments