forked from kubernetes/kops
-
Notifications
You must be signed in to change notification settings - Fork 0
/
instancegroup.go
155 lines (126 loc) · 4.21 KB
/
instancegroup.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
package api
import (
"fmt"
"github.com/golang/glog"
k8sapi "k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/unversioned"
"k8s.io/kubernetes/pkg/util/validation/field"
)
// InstanceGroup represents a group of instances (either nodes or masters) with the same configuration
type InstanceGroup struct {
unversioned.TypeMeta `json:",inline"`
k8sapi.ObjectMeta `json:"metadata,omitempty"`
Spec InstanceGroupSpec `json:"spec,omitempty"`
}
type InstanceGroupList struct {
Items []InstanceGroup `json:"items"`
}
// InstanceGroupRole string describes the roles of the nodes in this InstanceGroup (master or nodes)
type InstanceGroupRole string
const (
InstanceGroupRoleMaster InstanceGroupRole = "Master"
InstanceGroupRoleNode InstanceGroupRole = "Node"
)
type InstanceGroupSpec struct {
// Type determines the role of instances in this group: masters or nodes
Role InstanceGroupRole `json:"role,omitempty"`
Image string `json:"image,omitempty"`
MinSize *int `json:"minSize,omitempty"`
MaxSize *int `json:"maxSize,omitempty"`
//NodeInstancePrefix string `json:",omitempty"`
//NodeLabels string `json:",omitempty"`
MachineType string `json:"machineType,omitempty"`
//NodeTag string `json:",omitempty"`
// RootVolumeSize is the size of the EBS root volume to use, in GB
RootVolumeSize *int `json:"rootVolumeSize,omitempty"`
// RootVolumeType is the type of the EBS root volume to use (e.g. gp2)
RootVolumeType *string `json:"rootVolumeType,omitempty"`
Zones []string `json:"zones,omitempty"`
// MaxPrice indicates this is a spot-pricing group, with the specified value as our max-price bid
MaxPrice *string `json:"maxPrice,omitempty"`
// AssociatePublicIP is true if we want instances to have a public IP
AssociatePublicIP *bool `json:"associatePublicIp,omitempty"`
// CloudLabels indicates the labels for instances in this group, at the AWS level
CloudLabels map[string]string `json:"cloudLabels,omitempty"`
// NodeLabels indicates the kubernetes labels for nodes in this group
NodeLabels map[string]string `json:"nodeLabels,omitempty"`
}
// PerformAssignmentsInstanceGroups populates InstanceGroups with default values
func PerformAssignmentsInstanceGroups(groups []*InstanceGroup) error {
names := map[string]bool{}
for _, group := range groups {
names[group.Name] = true
}
for _, group := range groups {
// We want to give them a stable Name as soon as possible
if group.Name == "" {
// Loop to find the first unassigned name like `nodes-%d`
i := 0
for {
key := fmt.Sprintf("nodes-%d", i)
if !names[key] {
group.Name = key
names[key] = true
break
}
i++
}
}
}
return nil
}
func (g *InstanceGroup) IsMaster() bool {
switch g.Spec.Role {
case InstanceGroupRoleMaster:
return true
case InstanceGroupRoleNode:
return false
default:
glog.Fatalf("Role not set in group %v", g)
return false
}
}
func (g *InstanceGroup) Validate(strict bool) error {
if g.Name == "" {
return field.Required(field.NewPath("Name"), "")
}
if g.Spec.Role == "" {
return field.Required(field.NewPath("Role"), "Role must be set")
}
switch g.Spec.Role {
case InstanceGroupRoleMaster:
case InstanceGroupRoleNode:
default:
return field.Invalid(field.NewPath("Role"), g.Spec.Role, "Unknown role")
}
if g.IsMaster() {
if len(g.Spec.Zones) == 0 {
return fmt.Errorf("Master InstanceGroup %s did not specify any Zones", g.Name)
}
}
return nil
}
// CrossValidate performs validation of the instance group, including that it is consistent with the Cluster
// It calls Validate, so all that validation is included.
func (g *InstanceGroup) CrossValidate(cluster *Cluster, strict bool) error {
err := g.Validate(strict)
if err != nil {
return err
}
// Check that instance groups are defined in valid zones
{
clusterZones := make(map[string]*ClusterZoneSpec)
for _, z := range cluster.Spec.Zones {
if clusterZones[z.Name] != nil {
return fmt.Errorf("Zones contained a duplicate value: %v", z.Name)
}
clusterZones[z.Name] = z
}
for _, z := range g.Spec.Zones {
if clusterZones[z] == nil {
return fmt.Errorf("InstanceGroup %q is configured in %q, but this is not configured as a Zone in the cluster", g.Name, z)
}
}
}
return nil
}