forked from openshift/installer
/
installconfig.go
221 lines (207 loc) · 9.81 KB
/
installconfig.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
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
package validation
import (
"fmt"
"sort"
"strings"
"github.com/sirupsen/logrus"
"k8s.io/apimachinery/pkg/util/validation/field"
"github.com/openshift/installer/pkg/types"
"github.com/openshift/installer/pkg/types/aws"
awsvalidation "github.com/openshift/installer/pkg/types/aws/validation"
"github.com/openshift/installer/pkg/types/libvirt"
libvirtvalidation "github.com/openshift/installer/pkg/types/libvirt/validation"
"github.com/openshift/installer/pkg/types/openstack"
openstackvalidation "github.com/openshift/installer/pkg/types/openstack/validation"
"github.com/openshift/installer/pkg/validate"
)
const (
masterPoolName = "master"
)
// ClusterDomain returns the cluster domain for a cluster with the specified
// base domain and cluster name.
func ClusterDomain(baseDomain, clusterName string) string {
return fmt.Sprintf("%s.%s", clusterName, baseDomain)
}
// ValidateInstallConfig checks that the specified install config is valid.
func ValidateInstallConfig(c *types.InstallConfig, openStackValidValuesFetcher openstackvalidation.ValidValuesFetcher) field.ErrorList {
allErrs := field.ErrorList{}
if c.TypeMeta.APIVersion == "" {
return field.ErrorList{field.Required(field.NewPath("apiVersion"), "install-config version required")}
}
switch v := c.APIVersion; v {
case types.InstallConfigVersion:
// Current version
default:
return field.ErrorList{field.Invalid(field.NewPath("apiVersion"), c.TypeMeta.APIVersion, fmt.Sprintf("install-config version must be %q", types.InstallConfigVersion))}
}
if c.SSHKey != "" {
if err := validate.SSHPublicKey(c.SSHKey); err != nil {
allErrs = append(allErrs, field.Invalid(field.NewPath("sshKey"), c.SSHKey, err.Error()))
}
}
nameErr := validate.DomainName(c.ObjectMeta.Name, false)
if nameErr != nil {
allErrs = append(allErrs, field.Invalid(field.NewPath("metadata", "name"), c.ObjectMeta.Name, nameErr.Error()))
}
baseDomainErr := validate.DomainName(c.BaseDomain, true)
if baseDomainErr != nil {
allErrs = append(allErrs, field.Invalid(field.NewPath("baseDomain"), c.BaseDomain, baseDomainErr.Error()))
}
if nameErr == nil && baseDomainErr == nil {
clusterDomain := ClusterDomain(c.BaseDomain, c.ObjectMeta.Name)
if err := validate.DomainName(clusterDomain, true); err != nil {
allErrs = append(allErrs, field.Invalid(field.NewPath("baseDomain"), clusterDomain, err.Error()))
}
}
if c.Networking != nil {
allErrs = append(allErrs, validateNetworking(c.Networking, field.NewPath("networking"))...)
} else {
allErrs = append(allErrs, field.Required(field.NewPath("networking"), "networking is required"))
}
allErrs = append(allErrs, validatePlatform(&c.Platform, field.NewPath("platform"), openStackValidValuesFetcher)...)
if c.ControlPlane != nil {
allErrs = append(allErrs, validateControlPlane(&c.Platform, c.ControlPlane, field.NewPath("controlPlane"))...)
} else {
allErrs = append(allErrs, field.Required(field.NewPath("controlPlane"), "controlPlane is required"))
}
allErrs = append(allErrs, validateCompute(&c.Platform, c.Compute, field.NewPath("compute"))...)
if err := validate.ImagePullSecret(c.PullSecret); err != nil {
allErrs = append(allErrs, field.Invalid(field.NewPath("pullSecret"), c.PullSecret, err.Error()))
}
return allErrs
}
func validateNetworking(n *types.Networking, fldPath *field.Path) field.ErrorList {
allErrs := field.ErrorList{}
if n.NetworkType == "" {
allErrs = append(allErrs, field.Required(fldPath.Child("networkType"), "network provider type required"))
}
if n.MachineCIDR != nil {
if err := validate.SubnetCIDR(&n.MachineCIDR.IPNet); err != nil {
allErrs = append(allErrs, field.Invalid(fldPath.Child("machineCIDR"), n.MachineCIDR.String(), err.Error()))
}
} else {
allErrs = append(allErrs, field.Required(fldPath.Child("machineCIDR"), "a machine CIDR is required"))
}
for i, sn := range n.ServiceNetwork {
if err := validate.SubnetCIDR(&sn.IPNet); err != nil {
allErrs = append(allErrs, field.Invalid(fldPath.Child("serviceNetwork").Index(i), sn.String(), err.Error()))
}
if n.MachineCIDR != nil && validate.DoCIDRsOverlap(&sn.IPNet, &n.MachineCIDR.IPNet) {
allErrs = append(allErrs, field.Invalid(fldPath.Child("serviceNetwork").Index(i), sn.String(), "service network must not overlap with machineCIDR"))
}
for j, snn := range n.ServiceNetwork[0:i] {
if validate.DoCIDRsOverlap(&sn.IPNet, &snn.IPNet) {
allErrs = append(allErrs, field.Invalid(fldPath.Child("serviceNetwork").Index(i), sn.String(), fmt.Sprintf("service network must not overlap with service network %d", j)))
}
}
}
if len(n.ServiceNetwork) == 0 {
allErrs = append(allErrs, field.Required(fldPath.Child("serviceNetwork"), "a service network is required"))
}
// Until kubernetes supports multiple service networks e.g. dual stack
if len(n.ServiceNetwork) > 1 {
// the default stringification of this type is unreadable
diag := []string{}
for _, sn := range n.ServiceNetwork {
diag = append(diag, sn.String())
}
allErrs = append(allErrs, field.Invalid(fldPath.Child("serviceNetwork"), strings.Join(diag, ", "), "only one service network can be specified"))
}
for i, cn := range n.ClusterNetwork {
allErrs = append(allErrs, validateClusterNetwork(n, &cn, i, fldPath.Child("clusterNetwork").Index(i))...)
}
if len(n.ClusterNetwork) == 0 {
allErrs = append(allErrs, field.Required(fldPath.Child("clusterNetwork"), "cluster network required"))
}
return allErrs
}
func validateClusterNetwork(n *types.Networking, cn *types.ClusterNetworkEntry, idx int, fldPath *field.Path) field.ErrorList {
allErrs := field.ErrorList{}
if err := validate.SubnetCIDR(&cn.CIDR.IPNet); err != nil {
allErrs = append(allErrs, field.Invalid(fldPath.Child("cidr"), cn.CIDR.IPNet.String(), err.Error()))
}
if n.MachineCIDR != nil && validate.DoCIDRsOverlap(&cn.CIDR.IPNet, &n.MachineCIDR.IPNet) {
allErrs = append(allErrs, field.Invalid(fldPath.Child("cidr"), cn.CIDR.String(), "cluster network must not overlap with machine CIDR"))
}
for i, sn := range n.ServiceNetwork {
if validate.DoCIDRsOverlap(&cn.CIDR.IPNet, &sn.IPNet) {
allErrs = append(allErrs, field.Invalid(fldPath.Child("cidr"), cn.CIDR.String(), fmt.Sprintf("cluster network must not overlap with service network %d", i)))
}
}
for i, acn := range n.ClusterNetwork[0:idx] {
if validate.DoCIDRsOverlap(&cn.CIDR.IPNet, &acn.CIDR.IPNet) {
allErrs = append(allErrs, field.Invalid(fldPath.Child("cidr"), cn.CIDR.String(), fmt.Sprintf("cluster network must not overlap with cluster network %d", i)))
}
}
if cn.HostPrefix < 0 {
allErrs = append(allErrs, field.Invalid(fldPath.Child("hostPrefix"), cn.HostPrefix, "hostPrefix must be positive"))
}
if ones, _ := cn.CIDR.Mask.Size(); cn.HostPrefix < int32(ones) {
allErrs = append(allErrs, field.Invalid(fldPath.Child("hostPrefix"), cn.HostPrefix, "cluster network host subnetwork prefix must not be larger size than CIDR "+cn.CIDR.String()))
}
return allErrs
}
func validateControlPlane(platform *types.Platform, pool *types.MachinePool, fldPath *field.Path) field.ErrorList {
allErrs := field.ErrorList{}
if pool.Name != masterPoolName {
allErrs = append(allErrs, field.NotSupported(fldPath.Child("name"), pool.Name, []string{masterPoolName}))
}
if pool.Replicas != nil && *pool.Replicas == 0 {
allErrs = append(allErrs, field.Invalid(fldPath.Child("replicas"), pool.Replicas, "number of control plane replicas must be positive"))
}
allErrs = append(allErrs, ValidateMachinePool(platform, pool, fldPath)...)
return allErrs
}
func validateCompute(platform *types.Platform, pools []types.MachinePool, fldPath *field.Path) field.ErrorList {
allErrs := field.ErrorList{}
poolNames := map[string]bool{}
foundPositiveReplicas := false
for i, p := range pools {
poolFldPath := fldPath.Index(i)
if p.Name != "worker" {
allErrs = append(allErrs, field.NotSupported(poolFldPath.Child("name"), p.Name, []string{"worker"}))
}
if poolNames[p.Name] {
allErrs = append(allErrs, field.Duplicate(poolFldPath.Child("name"), p.Name))
}
poolNames[p.Name] = true
if p.Replicas != nil && *p.Replicas > 0 {
foundPositiveReplicas = true
}
allErrs = append(allErrs, ValidateMachinePool(platform, &p, poolFldPath)...)
}
if !foundPositiveReplicas {
logrus.Warnf("There are no compute nodes specified. The cluster will not fully initialize without compute nodes.")
}
return allErrs
}
func validatePlatform(platform *types.Platform, fldPath *field.Path, openStackValidValuesFetcher openstackvalidation.ValidValuesFetcher) field.ErrorList {
allErrs := field.ErrorList{}
activePlatform := platform.Name()
platforms := make([]string, len(types.PlatformNames))
copy(platforms, types.PlatformNames)
platforms = append(platforms, types.HiddenPlatformNames...)
sort.Strings(platforms)
i := sort.SearchStrings(platforms, activePlatform)
if i == len(platforms) || platforms[i] != activePlatform {
allErrs = append(allErrs, field.Invalid(fldPath, platform, fmt.Sprintf("must specify one of the platforms (%s)", strings.Join(platforms, ", "))))
}
validate := func(n string, value interface{}, validation func(*field.Path) field.ErrorList) {
if n != activePlatform {
allErrs = append(allErrs, field.Invalid(fldPath, platform, fmt.Sprintf("must only specify a single type of platform; cannot use both %q and %q", activePlatform, n)))
}
allErrs = append(allErrs, validation(fldPath.Child(n))...)
}
if platform.AWS != nil {
validate(aws.Name, platform.AWS, func(f *field.Path) field.ErrorList { return awsvalidation.ValidatePlatform(platform.AWS, f) })
}
if platform.Libvirt != nil {
validate(libvirt.Name, platform.Libvirt, func(f *field.Path) field.ErrorList { return libvirtvalidation.ValidatePlatform(platform.Libvirt, f) })
}
if platform.OpenStack != nil {
validate(openstack.Name, platform.OpenStack, func(f *field.Path) field.ErrorList {
return openstackvalidation.ValidatePlatform(platform.OpenStack, f, openStackValidValuesFetcher)
})
}
return allErrs
}