github.com/openshift/installer@v1.4.17/pkg/asset/machines/openstack/machines_test.go (about) 1 package openstack 2 3 import ( 4 "fmt" 5 "strings" 6 "testing" 7 8 machinev1 "github.com/openshift/api/machine/v1" 9 "github.com/openshift/installer/pkg/types/openstack" 10 ) 11 12 func mpWithZones(zones ...string) func(*openstack.MachinePool) { 13 return func(mpool *openstack.MachinePool) { 14 mpool.Zones = zones 15 } 16 } 17 18 func mpWithRootVolumeZones(zones ...string) func(*openstack.MachinePool) { 19 return func(mpool *openstack.MachinePool) { 20 if mpool.RootVolume != nil { 21 mpool.RootVolume.Zones = zones 22 } else { 23 mpool.RootVolume = &openstack.RootVolume{Zones: zones} 24 } 25 } 26 } 27 28 func mpWithRootVolumeTypes(types ...string) func(*openstack.MachinePool) { 29 return func(mpool *openstack.MachinePool) { 30 if mpool.RootVolume != nil { 31 mpool.RootVolume.Types = types 32 } else { 33 mpool.RootVolume = &openstack.RootVolume{Types: types} 34 } 35 } 36 } 37 38 func generateMachinePool(options ...func(*openstack.MachinePool)) openstack.MachinePool { 39 mpool := openstack.MachinePool{} 40 for _, apply := range options { 41 apply(&mpool) 42 } 43 return mpool 44 } 45 46 func TestFailureDomains(t *testing.T) { 47 type checkFunc func([]machinev1.OpenStackFailureDomain, error) error 48 check := func(fns ...checkFunc) []checkFunc { return fns } 49 50 hasNFailureDomains := func(want int) checkFunc { 51 return func(fds []machinev1.OpenStackFailureDomain, _ error) error { 52 if have := len(fds); want != have { 53 return fmt.Errorf("expected %d failure domains, got %d", want, have) 54 } 55 return nil 56 } 57 } 58 59 hasComputeZones := func(wantZones ...string) checkFunc { 60 return func(fds []machinev1.OpenStackFailureDomain, _ error) error { 61 haveZones := make([]string, len(fds)) 62 for i := range fds { 63 haveZones[i] = fds[i].AvailabilityZone 64 } 65 66 if wantLen, haveLen := len(wantZones), len(haveZones); wantLen != haveLen { 67 return fmt.Errorf("expected compute zones %v (len %d), got %v (len %d)", wantZones, wantLen, haveZones, haveLen) 68 } 69 70 for i := range fds { 71 if want, have := wantZones[i], haveZones[i]; want != have { 72 return fmt.Errorf("expected compute zones %v, got %v", wantZones, haveZones) 73 } 74 } 75 76 return nil 77 } 78 } 79 80 hasNilRootVolume := func(fds []machinev1.OpenStackFailureDomain, _ error) error { 81 for i := range fds { 82 if fds[i].RootVolume != nil { 83 return fmt.Errorf("failure domain %d has unexpectedly non-nil RootVolume", i) 84 } 85 } 86 return nil 87 } 88 89 hasRootVolumeZones := func(wantZones ...string) checkFunc { 90 return func(fds []machinev1.OpenStackFailureDomain, _ error) error { 91 haveZones := make([]string, len(fds)) 92 for i := range fds { 93 if fds[i].RootVolume == nil { 94 return fmt.Errorf("failure domain %d has unexpectedly nil RootVolume", i) 95 } 96 haveZones[i] = fds[i].RootVolume.AvailabilityZone 97 } 98 99 if wantLen, haveLen := len(wantZones), len(haveZones); wantLen != haveLen { 100 return fmt.Errorf("expected root volume zones %v, got %v", wantZones, haveZones) 101 } 102 103 for i := range fds { 104 if want, have := wantZones[i], haveZones[i]; want != have { 105 return fmt.Errorf("expected root volume zones %v, got %v", wantZones, haveZones) 106 } 107 } 108 109 return nil 110 } 111 } 112 113 hasRootVolumeTypes := func(wantTypes ...string) checkFunc { 114 return func(fds []machinev1.OpenStackFailureDomain, _ error) error { 115 haveTypes := make([]string, len(fds)) 116 for i := range fds { 117 if fds[i].RootVolume == nil { 118 return fmt.Errorf("failure domain %d has unexpectedly nil RootVolume", i) 119 } 120 haveTypes[i] = fds[i].RootVolume.VolumeType 121 } 122 123 if wantLen, haveLen := len(wantTypes), len(haveTypes); wantLen != haveLen { 124 return fmt.Errorf("expected root volume types %v, got %v", wantTypes, haveTypes) 125 } 126 127 for i := range fds { 128 if want, have := wantTypes[i], haveTypes[i]; want != have { 129 return fmt.Errorf("expected root volume types %v, got %v", wantTypes, haveTypes) 130 } 131 } 132 133 return nil 134 } 135 } 136 137 doesNotPanic := func(_ []machinev1.OpenStackFailureDomain, have error) error { 138 if have != nil { 139 return fmt.Errorf("unexpected panic: %w", have) 140 } 141 return nil 142 } 143 144 panicsWith := func(want string) checkFunc { 145 return func(_ []machinev1.OpenStackFailureDomain, have error) error { 146 if have == nil { 147 return fmt.Errorf("unexpectedly, didn't panic") 148 } 149 if have := fmt.Sprintf("%v", have); !strings.Contains(have, want) { 150 return fmt.Errorf("expected panic with %q, got %q", want, have) 151 } 152 return nil 153 } 154 } 155 156 for _, tc := range [...]struct { 157 name string 158 mpool openstack.MachinePool 159 checks []checkFunc 160 }{ 161 { 162 "no_zones", 163 generateMachinePool(), 164 check( 165 hasNFailureDomains(1), 166 hasComputeZones(""), 167 hasNilRootVolume, 168 doesNotPanic, 169 ), 170 }, 171 { 172 "one_compute_zone", 173 generateMachinePool( 174 mpWithZones("one"), 175 ), 176 check( 177 hasNFailureDomains(1), 178 hasComputeZones("one"), 179 hasNilRootVolume, 180 doesNotPanic, 181 ), 182 }, 183 { 184 "three_compute_zones", 185 generateMachinePool( 186 mpWithZones("one", "two", "three"), 187 ), 188 check( 189 hasNFailureDomains(3), 190 hasComputeZones("one", "two", "three"), 191 hasNilRootVolume, 192 doesNotPanic, 193 ), 194 }, 195 { 196 "three_compute_zones_one_root_volume_zone", 197 generateMachinePool( 198 mpWithZones("one", "two", "three"), 199 mpWithRootVolumeZones("volume_one"), 200 mpWithRootVolumeTypes("type-1"), 201 ), 202 check( 203 hasNFailureDomains(3), 204 hasComputeZones("one", "two", "three"), 205 hasRootVolumeZones("volume_one", "volume_one", "volume_one"), 206 hasRootVolumeTypes("type-1", "type-1", "type-1"), 207 doesNotPanic, 208 ), 209 }, 210 { 211 "one_compute_zone_three_root_volume_zones", 212 generateMachinePool( 213 mpWithZones("one"), 214 mpWithRootVolumeZones("volume_one", "volume_two", "volume_three"), 215 mpWithRootVolumeTypes("type-1"), 216 ), 217 check( 218 hasNFailureDomains(3), 219 hasComputeZones("one", "one", "one"), 220 hasRootVolumeZones("volume_one", "volume_two", "volume_three"), 221 hasRootVolumeTypes("type-1", "type-1", "type-1"), 222 doesNotPanic, 223 ), 224 }, 225 { 226 "three_compute_zone_two_root_volume_zones_panics", 227 generateMachinePool( 228 mpWithZones("one", "two", "three"), 229 mpWithRootVolumeZones("volume_one", "volume_two"), 230 ), 231 check( 232 // We have to check for a partial result here, because the mapping 233 // of compute zones to root volume zones is handled in a map therefore 234 // the order is not deterministic. 235 panicsWith("availability zones should have equal length"), 236 ), 237 }, 238 { 239 "three_compute_zones_three_root_volume_types", 240 generateMachinePool( 241 mpWithZones("one", "two", "three"), 242 mpWithRootVolumeZones("volume_one", "volume_two", "volume_three"), 243 mpWithRootVolumeTypes("type-1", "type-2", "type-3"), 244 ), 245 check( 246 hasNFailureDomains(3), 247 hasComputeZones("one", "two", "three"), 248 hasRootVolumeZones("volume_one", "volume_two", "volume_three"), 249 hasRootVolumeTypes("type-1", "type-2", "type-3"), 250 doesNotPanic, 251 ), 252 }, 253 { 254 "three_root_volume_types", 255 generateMachinePool( 256 mpWithRootVolumeTypes("type-1", "type-2", "type-3"), 257 ), 258 check( 259 hasNFailureDomains(3), 260 hasComputeZones("", "", ""), 261 hasRootVolumeZones("", "", ""), 262 hasRootVolumeTypes("type-1", "type-2", "type-3"), 263 doesNotPanic, 264 ), 265 }, 266 } { 267 t.Run(tc.name, func(t *testing.T) { 268 failureDomains, recoveredPanic := func() (fds []machinev1.OpenStackFailureDomain, recoveredPanic error) { 269 defer func() { 270 if r := recover(); r != nil { 271 recoveredPanic = fmt.Errorf("%v", r) 272 } 273 }() 274 275 fds = failureDomainsFromSpec(tc.mpool) 276 return 277 }() 278 279 for _, check := range tc.checks { 280 if err := check(failureDomains, recoveredPanic); err != nil { 281 t.Error(err) 282 } 283 } 284 }) 285 } 286 } 287 288 func TestPruneFailureDomains(t *testing.T) { 289 }