github.com/openshift/installer@v1.4.17/pkg/asset/machines/aws/machines.go (about) 1 // Package aws generates Machine objects for aws. 2 package aws 3 4 import ( 5 "fmt" 6 "sort" 7 8 "github.com/pkg/errors" 9 corev1 "k8s.io/api/core/v1" 10 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 11 "k8s.io/apimachinery/pkg/runtime" 12 "k8s.io/apimachinery/pkg/util/sets" 13 "k8s.io/utils/pointer" 14 15 v1 "github.com/openshift/api/config/v1" 16 machinev1 "github.com/openshift/api/machine/v1" 17 machineapi "github.com/openshift/api/machine/v1beta1" 18 "github.com/openshift/installer/pkg/types" 19 "github.com/openshift/installer/pkg/types/aws" 20 ) 21 22 type machineProviderInput struct { 23 clusterID string 24 region string 25 subnet string 26 instanceType string 27 osImage string 28 zone string 29 role string 30 userDataSecret string 31 instanceProfile string 32 root *aws.EC2RootVolume 33 imds aws.EC2Metadata 34 userTags map[string]string 35 publicSubnet bool 36 securityGroupIDs []string 37 } 38 39 // Machines returns a list of machines for a machinepool. 40 func Machines(clusterID string, region string, subnets map[string]string, pool *types.MachinePool, role, userDataSecret string, userTags map[string]string) ([]machineapi.Machine, *machinev1.ControlPlaneMachineSet, error) { 41 if poolPlatform := pool.Platform.Name(); poolPlatform != aws.Name { 42 return nil, nil, fmt.Errorf("non-AWS machine-pool: %q", poolPlatform) 43 } 44 mpool := pool.Platform.AWS 45 46 total := int64(1) 47 if pool.Replicas != nil { 48 total = *pool.Replicas 49 } 50 51 instanceProfile := mpool.IAMProfile 52 if len(instanceProfile) == 0 { 53 instanceProfile = fmt.Sprintf("%s-%s-profile", clusterID, role) 54 } 55 56 var machines []machineapi.Machine 57 machineSetProvider := &machineapi.AWSMachineProviderConfig{} 58 for idx := int64(0); idx < total; idx++ { 59 zone := mpool.Zones[int(idx)%len(mpool.Zones)] 60 subnet, ok := subnets[zone] 61 if len(subnets) > 0 && !ok { 62 return nil, nil, errors.Errorf("no subnet for zone %s", zone) 63 } 64 provider, err := provider(&machineProviderInput{ 65 clusterID: clusterID, 66 region: region, 67 subnet: subnet, 68 instanceType: mpool.InstanceType, 69 osImage: mpool.AMIID, 70 zone: zone, 71 role: role, 72 userDataSecret: userDataSecret, 73 instanceProfile: instanceProfile, 74 root: &mpool.EC2RootVolume, 75 imds: mpool.EC2Metadata, 76 userTags: userTags, 77 publicSubnet: false, 78 securityGroupIDs: pool.Platform.AWS.AdditionalSecurityGroupIDs, 79 }) 80 if err != nil { 81 return nil, nil, errors.Wrap(err, "failed to create provider") 82 } 83 *machineSetProvider = *provider 84 machine := machineapi.Machine{ 85 TypeMeta: metav1.TypeMeta{ 86 APIVersion: "machine.openshift.io/v1beta1", 87 Kind: "Machine", 88 }, 89 ObjectMeta: metav1.ObjectMeta{ 90 Namespace: "openshift-machine-api", 91 Name: fmt.Sprintf("%s-%s-%d", clusterID, pool.Name, idx), 92 Labels: map[string]string{ 93 "machine.openshift.io/cluster-api-cluster": clusterID, 94 "machine.openshift.io/cluster-api-machine-role": role, 95 "machine.openshift.io/cluster-api-machine-type": role, 96 }, 97 }, 98 Spec: machineapi.MachineSpec{ 99 ProviderSpec: machineapi.ProviderSpec{ 100 Value: &runtime.RawExtension{Object: provider}, 101 }, 102 // we don't need to set Versions, because we control those via operators. 103 }, 104 } 105 106 machines = append(machines, machine) 107 } 108 109 replicas := int32(total) 110 failureDomains := []machinev1.AWSFailureDomain{} 111 112 for _, zone := range mpool.Zones { 113 subnet := subnets[zone] 114 domain := machinev1.AWSFailureDomain{ 115 Subnet: &machinev1.AWSResourceReference{}, 116 Placement: machinev1.AWSFailureDomainPlacement{ 117 AvailabilityZone: zone, 118 }, 119 } 120 if subnet == "" { 121 domain.Subnet.Type = machinev1.AWSFiltersReferenceType 122 domain.Subnet.Filters = &[]machinev1.AWSResourceFilter{ 123 { 124 Name: "tag:Name", 125 Values: []string{ 126 fmt.Sprintf("%s-subnet-private-%s", clusterID, zone), 127 }, 128 }, 129 } 130 } else { 131 domain.Subnet.Type = machinev1.AWSIDReferenceType 132 domain.Subnet.ID = pointer.String(subnet) 133 } 134 failureDomains = append(failureDomains, domain) 135 } 136 137 machineSetProvider.Placement.AvailabilityZone = "" 138 machineSetProvider.Subnet = machineapi.AWSResourceReference{} 139 controlPlaneMachineSet := &machinev1.ControlPlaneMachineSet{ 140 TypeMeta: metav1.TypeMeta{ 141 APIVersion: "machine.openshift.io/v1", 142 Kind: "ControlPlaneMachineSet", 143 }, 144 ObjectMeta: metav1.ObjectMeta{ 145 Namespace: "openshift-machine-api", 146 Name: "cluster", 147 Labels: map[string]string{ 148 "machine.openshift.io/cluster-api-cluster": clusterID, 149 }, 150 }, 151 Spec: machinev1.ControlPlaneMachineSetSpec{ 152 Replicas: &replicas, 153 State: machinev1.ControlPlaneMachineSetStateActive, 154 Selector: metav1.LabelSelector{ 155 MatchLabels: map[string]string{ 156 "machine.openshift.io/cluster-api-machine-role": role, 157 "machine.openshift.io/cluster-api-machine-type": role, 158 "machine.openshift.io/cluster-api-cluster": clusterID, 159 }, 160 }, 161 Template: machinev1.ControlPlaneMachineSetTemplate{ 162 MachineType: machinev1.OpenShiftMachineV1Beta1MachineType, 163 OpenShiftMachineV1Beta1Machine: &machinev1.OpenShiftMachineV1Beta1MachineTemplate{ 164 FailureDomains: &machinev1.FailureDomains{ 165 Platform: v1.AWSPlatformType, 166 AWS: &failureDomains, 167 }, 168 ObjectMeta: machinev1.ControlPlaneMachineSetTemplateObjectMeta{ 169 Labels: map[string]string{ 170 "machine.openshift.io/cluster-api-cluster": clusterID, 171 "machine.openshift.io/cluster-api-machine-role": role, 172 "machine.openshift.io/cluster-api-machine-type": role, 173 }, 174 }, 175 Spec: machineapi.MachineSpec{ 176 ProviderSpec: machineapi.ProviderSpec{ 177 Value: &runtime.RawExtension{Object: machineSetProvider}, 178 }, 179 }, 180 }, 181 }, 182 }, 183 } 184 return machines, controlPlaneMachineSet, nil 185 } 186 187 func provider(in *machineProviderInput) (*machineapi.AWSMachineProviderConfig, error) { 188 tags, err := tagsFromUserTags(in.clusterID, in.userTags) 189 if err != nil { 190 return nil, errors.Wrap(err, "failed to create machineapi.TagSpecifications from UserTags") 191 } 192 193 sgFilters := []machineapi.Filter{ 194 { 195 Name: "tag:Name", 196 Values: []string{fmt.Sprintf("%s-node", in.clusterID)}, 197 }, 198 { 199 Name: "tag:Name", 200 Values: []string{fmt.Sprintf("%s-lb", in.clusterID)}, 201 }, 202 } 203 204 if in.role == "master" { 205 cpFilter := machineapi.Filter{ 206 Name: "tag:Name", 207 Values: []string{fmt.Sprintf("%s-controlplane", in.clusterID)}, 208 } 209 sgFilters = append(sgFilters, cpFilter) 210 } 211 212 securityGroups := []machineapi.AWSResourceReference{} 213 for _, filter := range sgFilters { 214 securityGroups = append(securityGroups, machineapi.AWSResourceReference{ 215 Filters: []machineapi.Filter{filter}, 216 }) 217 } 218 securityGroupsIn := []machineapi.AWSResourceReference{} 219 for _, sgID := range in.securityGroupIDs { 220 sgID := sgID 221 securityGroupsIn = append(securityGroupsIn, machineapi.AWSResourceReference{ 222 ID: &sgID, 223 }) 224 } 225 sort.SliceStable(securityGroupsIn, func(i, j int) bool { 226 return *securityGroupsIn[i].ID < *securityGroupsIn[j].ID 227 }) 228 securityGroups = append(securityGroups, securityGroupsIn...) 229 230 config := &machineapi.AWSMachineProviderConfig{ 231 TypeMeta: metav1.TypeMeta{ 232 APIVersion: "machine.openshift.io/v1beta1", 233 Kind: "AWSMachineProviderConfig", 234 }, 235 InstanceType: in.instanceType, 236 BlockDevices: []machineapi.BlockDeviceMappingSpec{ 237 { 238 EBS: &machineapi.EBSBlockDeviceSpec{ 239 VolumeType: pointer.String(in.root.Type), 240 VolumeSize: pointer.Int64(int64(in.root.Size)), 241 Iops: pointer.Int64(int64(in.root.IOPS)), 242 Encrypted: pointer.Bool(true), 243 KMSKey: machineapi.AWSResourceReference{ARN: pointer.String(in.root.KMSKeyARN)}, 244 }, 245 }, 246 }, 247 Tags: tags, 248 IAMInstanceProfile: &machineapi.AWSResourceReference{ 249 ID: pointer.String(in.instanceProfile), 250 }, 251 UserDataSecret: &corev1.LocalObjectReference{Name: in.userDataSecret}, 252 CredentialsSecret: &corev1.LocalObjectReference{Name: "aws-cloud-credentials"}, 253 Placement: machineapi.Placement{Region: in.region, AvailabilityZone: in.zone}, 254 SecurityGroups: securityGroups, 255 } 256 257 visibility := "private" 258 if in.publicSubnet { 259 config.PublicIP = pointer.Bool(in.publicSubnet) 260 visibility = "public" 261 } 262 263 subnetFilters := []machineapi.Filter{ 264 { 265 Name: "tag:Name", 266 Values: []string{ 267 fmt.Sprintf("%s-subnet-%s-%s", in.clusterID, visibility, in.zone), 268 }, 269 }, 270 } 271 272 if in.subnet == "" { 273 config.Subnet.Filters = subnetFilters 274 } else { 275 config.Subnet.ID = pointer.String(in.subnet) 276 } 277 278 if in.osImage == "" { 279 config.AMI.Filters = []machineapi.Filter{{ 280 Name: "tag:Name", 281 Values: []string{fmt.Sprintf("%s-ami-%s", in.clusterID, in.region)}, 282 }} 283 } else { 284 config.AMI.ID = pointer.String(in.osImage) 285 } 286 287 if in.imds.Authentication != "" { 288 config.MetadataServiceOptions.Authentication = machineapi.MetadataServiceAuthentication(in.imds.Authentication) 289 } 290 291 return config, nil 292 } 293 294 func tagsFromUserTags(clusterID string, usertags map[string]string) ([]machineapi.TagSpecification, error) { 295 tags := []machineapi.TagSpecification{ 296 {Name: fmt.Sprintf("kubernetes.io/cluster/%s", clusterID), Value: "owned"}, 297 } 298 forbiddenTags := sets.NewString() 299 for idx := range tags { 300 forbiddenTags.Insert(tags[idx].Name) 301 } 302 303 userTagKeys := make([]string, 0, len(usertags)) 304 for key := range usertags { 305 userTagKeys = append(userTagKeys, key) 306 } 307 sort.Strings(userTagKeys) 308 309 for _, k := range userTagKeys { 310 if forbiddenTags.Has(k) { 311 return nil, fmt.Errorf("user tags may not clobber %s", k) 312 } 313 tags = append(tags, machineapi.TagSpecification{Name: k, Value: usertags[k]}) 314 } 315 return tags, nil 316 } 317 318 // ConfigMasters sets the PublicIP flag and assigns a set of load balancers to the given machines 319 func ConfigMasters(machines []machineapi.Machine, controlPlane *machinev1.ControlPlaneMachineSet, clusterID string, publish types.PublishingStrategy) { 320 lbrefs := []machineapi.LoadBalancerReference{{ 321 Name: fmt.Sprintf("%s-int", clusterID), 322 Type: machineapi.NetworkLoadBalancerType, 323 }} 324 325 if publish == types.ExternalPublishingStrategy { 326 lbrefs = append(lbrefs, machineapi.LoadBalancerReference{ 327 Name: fmt.Sprintf("%s-ext", clusterID), 328 Type: machineapi.NetworkLoadBalancerType, 329 }) 330 } 331 332 for _, machine := range machines { 333 providerSpec := machine.Spec.ProviderSpec.Value.Object.(*machineapi.AWSMachineProviderConfig) 334 providerSpec.LoadBalancers = lbrefs 335 } 336 337 providerSpec := controlPlane.Spec.Template.OpenShiftMachineV1Beta1Machine.Spec.ProviderSpec.Value.Object.(*machineapi.AWSMachineProviderConfig) 338 providerSpec.LoadBalancers = lbrefs 339 }