1 | /** |
2 | * |
3 | * Copyright (C) 2011 Cloud Conscious, LLC. <info@cloudconscious.com> |
4 | * |
5 | * ==================================================================== |
6 | * Licensed under the Apache License, Version 2.0 (the "License"); |
7 | * you may not use this file except in compliance with the License. |
8 | * You may obtain a copy of the License at |
9 | * |
10 | * http://www.apache.org/licenses/LICENSE-2.0 |
11 | * |
12 | * Unless required by applicable law or agreed to in writing, software |
13 | * distributed under the License is distributed on an "AS IS" BASIS, |
14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
15 | * See the License for the specific language governing permissions and |
16 | * limitations under the License. |
17 | * ==================================================================== |
18 | */ |
19 | package org.jclouds.compute.domain.internal; |
20 | |
21 | import static com.google.common.base.Preconditions.checkNotNull; |
22 | import static com.google.common.base.Predicates.and; |
23 | import static com.google.common.collect.Iterables.filter; |
24 | import static com.google.common.collect.Lists.newArrayList; |
25 | import static org.jclouds.compute.util.ComputeServiceUtils.getCores; |
26 | import static org.jclouds.compute.util.ComputeServiceUtils.getCoresAndSpeed; |
27 | import static org.jclouds.compute.util.ComputeServiceUtils.getSpace; |
28 | |
29 | import java.util.Arrays; |
30 | import java.util.List; |
31 | import java.util.NoSuchElementException; |
32 | import java.util.Set; |
33 | |
34 | import javax.annotation.Resource; |
35 | import javax.inject.Inject; |
36 | import javax.inject.Named; |
37 | import javax.inject.Provider; |
38 | |
39 | import org.jclouds.collect.Memoized; |
40 | import org.jclouds.compute.domain.ComputeMetadata; |
41 | import org.jclouds.compute.domain.Hardware; |
42 | import org.jclouds.compute.domain.Image; |
43 | import org.jclouds.compute.domain.OperatingSystem; |
44 | import org.jclouds.compute.domain.OsFamily; |
45 | import org.jclouds.compute.domain.Template; |
46 | import org.jclouds.compute.domain.TemplateBuilder; |
47 | import org.jclouds.compute.options.TemplateOptions; |
48 | import org.jclouds.compute.reference.ComputeServiceConstants; |
49 | import org.jclouds.domain.Location; |
50 | import org.jclouds.logging.Logger; |
51 | import org.jclouds.util.Lists2; |
52 | |
53 | import com.google.common.annotations.VisibleForTesting; |
54 | import com.google.common.base.Predicate; |
55 | import com.google.common.base.Predicates; |
56 | import com.google.common.base.Supplier; |
57 | import com.google.common.collect.ComparisonChain; |
58 | import com.google.common.collect.ImmutableList; |
59 | import com.google.common.collect.Iterables; |
60 | import com.google.common.collect.Ordering; |
61 | import com.google.common.primitives.Doubles; |
62 | |
63 | /** |
64 | * |
65 | * @author Adrian Cole |
66 | */ |
67 | @SuppressWarnings("unchecked") |
68 | public class TemplateBuilderImpl implements TemplateBuilder { |
69 | |
70 | @Resource |
71 | @Named(ComputeServiceConstants.COMPUTE_LOGGER) |
72 | protected Logger logger = Logger.NULL; |
73 | |
74 | protected final Supplier<Set<? extends Image>> images; |
75 | protected final Supplier<Set<? extends Hardware>> hardwares; |
76 | protected final Supplier<Set<? extends Location>> locations; |
77 | protected final Supplier<Location> defaultLocation; |
78 | protected final Provider<TemplateOptions> optionsProvider; |
79 | protected final Provider<TemplateBuilder> defaultTemplateProvider; |
80 | |
81 | @VisibleForTesting |
82 | protected Location location; |
83 | @VisibleForTesting |
84 | protected String imageId; |
85 | @VisibleForTesting |
86 | protected String hardwareId; |
87 | @VisibleForTesting |
88 | protected String imageVersion; |
89 | @VisibleForTesting |
90 | protected OsFamily osFamily; |
91 | @VisibleForTesting |
92 | protected String osVersion; |
93 | @VisibleForTesting |
94 | protected Boolean os64Bit; |
95 | @VisibleForTesting |
96 | protected String osName; |
97 | @VisibleForTesting |
98 | protected String osDescription; |
99 | @VisibleForTesting |
100 | protected String osArch; |
101 | @VisibleForTesting |
102 | protected String imageName; |
103 | @VisibleForTesting |
104 | protected String imageDescription; |
105 | @VisibleForTesting |
106 | protected double minCores; |
107 | @VisibleForTesting |
108 | protected int minRam; |
109 | @VisibleForTesting |
110 | protected boolean biggest; |
111 | @VisibleForTesting |
112 | protected boolean fastest; |
113 | @VisibleForTesting |
114 | protected TemplateOptions options; |
115 | |
116 | @Inject |
117 | protected TemplateBuilderImpl(@Memoized Supplier<Set<? extends Location>> locations, |
118 | @Memoized Supplier<Set<? extends Image>> images, @Memoized Supplier<Set<? extends Hardware>> hardwares, |
119 | Supplier<Location> defaultLocation2, Provider<TemplateOptions> optionsProvider, |
120 | @Named("DEFAULT") Provider<TemplateBuilder> defaultTemplateProvider) { |
121 | this.locations = locations; |
122 | this.images = images; |
123 | this.hardwares = hardwares; |
124 | this.defaultLocation = defaultLocation2; |
125 | this.optionsProvider = optionsProvider; |
126 | this.defaultTemplateProvider = defaultTemplateProvider; |
127 | } |
128 | |
129 | /** |
130 | * If the current location id is null, then we don't care where to launch a node. |
131 | * |
132 | * If the input location is null, then the data isn't location sensitive |
133 | * |
134 | * If the input location is a parent of the specified location, then we are ok. |
135 | */ |
136 | private final Predicate<ComputeMetadata> locationPredicate = new Predicate<ComputeMetadata>() { |
137 | @Override |
138 | public boolean apply(ComputeMetadata input) { |
139 | boolean returnVal = true; |
140 | if (location != null && input.getLocation() != null) |
141 | returnVal = location.equals(input.getLocation()) || location.getParent() != null |
142 | && location.getParent().equals(input.getLocation()) || location.getParent().getParent() != null |
143 | && location.getParent().getParent().equals(input.getLocation()); |
144 | return returnVal; |
145 | } |
146 | |
147 | @Override |
148 | public String toString() { |
149 | return "location(" + location + ")"; |
150 | } |
151 | }; |
152 | |
153 | private final Predicate<Image> idPredicate = new Predicate<Image>() { |
154 | @Override |
155 | public boolean apply(Image input) { |
156 | boolean returnVal = true; |
157 | if (imageId != null) { |
158 | returnVal = imageId.equals(input.getId()); |
159 | // match our input params so that the later predicates pass. |
160 | if (returnVal) { |
161 | fromImage(input); |
162 | } |
163 | } |
164 | return returnVal; |
165 | } |
166 | |
167 | @Override |
168 | public String toString() { |
169 | return "imageId(" + imageId + ")"; |
170 | } |
171 | }; |
172 | |
173 | private final Predicate<OperatingSystem> osFamilyPredicate = new Predicate<OperatingSystem>() { |
174 | |
175 | @Override |
176 | public boolean apply(OperatingSystem input) { |
177 | boolean returnVal = true; |
178 | if (osFamily != null) |
179 | returnVal = osFamily.equals(input.getFamily()); |
180 | return returnVal; |
181 | } |
182 | |
183 | @Override |
184 | public String toString() { |
185 | return "osFamily(" + osFamily + ")"; |
186 | } |
187 | }; |
188 | |
189 | private final Predicate<OperatingSystem> osNamePredicate = new Predicate<OperatingSystem>() { |
190 | @Override |
191 | public boolean apply(OperatingSystem input) { |
192 | boolean returnVal = true; |
193 | if (osName != null) { |
194 | if (input.getName() == null) |
195 | returnVal = false; |
196 | else |
197 | returnVal = input.getName().contains(osName) || input.getName().matches(osName); |
198 | } |
199 | return returnVal; |
200 | } |
201 | |
202 | @Override |
203 | public String toString() { |
204 | return "osName(" + osName + ")"; |
205 | } |
206 | }; |
207 | |
208 | private final Predicate<OperatingSystem> osDescriptionPredicate = new Predicate<OperatingSystem>() { |
209 | @Override |
210 | public boolean apply(OperatingSystem input) { |
211 | boolean returnVal = true; |
212 | if (osDescription != null) { |
213 | if (input.getDescription() == null) |
214 | returnVal = false; |
215 | else |
216 | returnVal = input.getDescription().contains(osDescription) |
217 | || input.getDescription().matches(osDescription); |
218 | } |
219 | return returnVal; |
220 | } |
221 | |
222 | @Override |
223 | public String toString() { |
224 | return "osDescription(" + osDescription + ")"; |
225 | } |
226 | }; |
227 | |
228 | private final Predicate<OperatingSystem> osVersionPredicate = new Predicate<OperatingSystem>() { |
229 | @Override |
230 | public boolean apply(OperatingSystem input) { |
231 | boolean returnVal = true; |
232 | if (osVersion != null) { |
233 | if (input.getVersion() == null) |
234 | returnVal = false; |
235 | else |
236 | returnVal = input.getVersion().contains(osVersion) || input.getVersion().matches(osVersion); |
237 | } |
238 | return returnVal; |
239 | } |
240 | |
241 | @Override |
242 | public String toString() { |
243 | return "osVersion(" + osVersion + ")"; |
244 | } |
245 | }; |
246 | |
247 | private final Predicate<OperatingSystem> os64BitPredicate = new Predicate<OperatingSystem>() { |
248 | @Override |
249 | public boolean apply(OperatingSystem input) { |
250 | boolean returnVal = true; |
251 | if (os64Bit != null) { |
252 | if (os64Bit) |
253 | return input.is64Bit(); |
254 | else |
255 | return !input.is64Bit(); |
256 | } |
257 | return returnVal; |
258 | } |
259 | |
260 | @Override |
261 | public String toString() { |
262 | return "os64Bit(" + os64Bit + ")"; |
263 | } |
264 | }; |
265 | |
266 | private final Predicate<OperatingSystem> osArchPredicate = new Predicate<OperatingSystem>() { |
267 | @Override |
268 | public boolean apply(OperatingSystem input) { |
269 | boolean returnVal = true; |
270 | if (osArch != null) { |
271 | if (input.getArch() == null) |
272 | returnVal = false; |
273 | else |
274 | returnVal = input.getArch().contains(osArch) || input.getArch().matches(osArch); |
275 | } |
276 | return returnVal; |
277 | } |
278 | |
279 | @Override |
280 | public String toString() { |
281 | return "osArch(" + osArch + ")"; |
282 | } |
283 | }; |
284 | |
285 | private final Predicate<Image> imageVersionPredicate = new Predicate<Image>() { |
286 | @Override |
287 | public boolean apply(Image input) { |
288 | boolean returnVal = true; |
289 | if (imageVersion != null) { |
290 | if (input.getVersion() == null) |
291 | returnVal = false; |
292 | else |
293 | returnVal = input.getVersion().contains(imageVersion) || input.getVersion().matches(imageVersion); |
294 | } |
295 | return returnVal; |
296 | } |
297 | |
298 | @Override |
299 | public String toString() { |
300 | return "imageVersion(" + imageVersion + ")"; |
301 | } |
302 | }; |
303 | |
304 | private final Predicate<Image> imageNamePredicate = new Predicate<Image>() { |
305 | @Override |
306 | public boolean apply(Image input) { |
307 | boolean returnVal = true; |
308 | if (imageName != null) { |
309 | if (input.getName() == null) |
310 | returnVal = false; |
311 | else |
312 | returnVal = input.getName().contains(imageName) || input.getName().matches(imageName); |
313 | } |
314 | return returnVal; |
315 | } |
316 | |
317 | @Override |
318 | public String toString() { |
319 | return "imageName(" + imageName + ")"; |
320 | } |
321 | }; |
322 | private final Predicate<Image> imageDescriptionPredicate = new Predicate<Image>() { |
323 | @Override |
324 | public boolean apply(Image input) { |
325 | boolean returnVal = true; |
326 | if (imageDescription != null) { |
327 | if (input.getDescription() == null) |
328 | returnVal = false; |
329 | else |
330 | returnVal = input.getDescription().equals(imageDescription) |
331 | || input.getDescription().contains(imageDescription) |
332 | || input.getDescription().matches(imageDescription); |
333 | } |
334 | return returnVal; |
335 | } |
336 | |
337 | @Override |
338 | public String toString() { |
339 | return "imageDescription(" + imageDescription + ")"; |
340 | } |
341 | }; |
342 | private final Predicate<Hardware> hardwareIdPredicate = new Predicate<Hardware>() { |
343 | @Override |
344 | public boolean apply(Hardware input) { |
345 | boolean returnVal = true; |
346 | if (hardwareId != null) { |
347 | returnVal = hardwareId.equals(input.getId()); |
348 | // match our input params so that the later predicates pass. |
349 | if (returnVal) { |
350 | fromHardware(input); |
351 | } |
352 | } |
353 | return returnVal; |
354 | } |
355 | |
356 | @Override |
357 | public String toString() { |
358 | return "hardwareId(" + hardwareId + ")"; |
359 | } |
360 | }; |
361 | |
362 | private final Predicate<Hardware> hardwareCoresPredicate = new Predicate<Hardware>() { |
363 | @Override |
364 | public boolean apply(Hardware input) { |
365 | double cores = getCores(input); |
366 | return cores >= TemplateBuilderImpl.this.minCores; |
367 | } |
368 | |
369 | @Override |
370 | public String toString() { |
371 | return "minCores(" + minCores + ")"; |
372 | } |
373 | }; |
374 | |
375 | private final Predicate<Hardware> hardwareRamPredicate = new Predicate<Hardware>() { |
376 | @Override |
377 | public boolean apply(Hardware input) { |
378 | return input.getRam() >= TemplateBuilderImpl.this.minRam; |
379 | } |
380 | |
381 | @Override |
382 | public String toString() { |
383 | return "minRam(" + minRam + ")"; |
384 | } |
385 | }; |
386 | private final Predicate<Hardware> hardwarePredicate = and(hardwareIdPredicate, locationPredicate, |
387 | hardwareCoresPredicate, hardwareRamPredicate); |
388 | |
389 | static final Ordering<Hardware> DEFAULT_SIZE_ORDERING = new Ordering<Hardware>() { |
390 | public int compare(Hardware left, Hardware right) { |
391 | return ComparisonChain.start().compare(getCores(left), getCores(right)).compare(left.getRam(), right.getRam()) |
392 | .compare(getSpace(left), getSpace(right)).result(); |
393 | } |
394 | }; |
395 | static final Ordering<Hardware> BY_CORES_ORDERING = new Ordering<Hardware>() { |
396 | public int compare(Hardware left, Hardware right) { |
397 | return Doubles.compare(getCoresAndSpeed(left), getCoresAndSpeed(right)); |
398 | } |
399 | }; |
400 | static final Ordering<Image> DEFAULT_IMAGE_ORDERING = new Ordering<Image>() { |
401 | public int compare(Image left, Image right) { |
402 | return ComparisonChain.start().compare(left.getName(), right.getName(), |
403 | Ordering.<String> natural().nullsLast()).compare(left.getVersion(), right.getVersion(), |
404 | Ordering.<String> natural().nullsLast()).compare(left.getOperatingSystem().getName(), |
405 | right.getOperatingSystem().getName(),// |
406 | Ordering.<String> natural().nullsLast()).compare(left.getOperatingSystem().getVersion(), |
407 | right.getOperatingSystem().getVersion(),// |
408 | Ordering.<String> natural().nullsLast()).compare(left.getOperatingSystem().getDescription(), |
409 | right.getOperatingSystem().getDescription(),// |
410 | Ordering.<String> natural().nullsLast()).compare(left.getOperatingSystem().getArch(), |
411 | right.getOperatingSystem().getArch()).result(); |
412 | } |
413 | }; |
414 | |
415 | /** |
416 | * {@inheritDoc} |
417 | */ |
418 | @Override |
419 | public TemplateBuilder fromTemplate(Template template) { |
420 | fromHardware(template.getHardware()); |
421 | fromImage(template.getImage()); |
422 | return this; |
423 | } |
424 | |
425 | /** |
426 | * {@inheritDoc} |
427 | */ |
428 | @Override |
429 | public TemplateBuilder fromHardware(Hardware hardware) { |
430 | if (currentLocationWiderThan(hardware.getLocation())) |
431 | this.location = hardware.getLocation(); |
432 | this.minCores = getCores(hardware); |
433 | this.minRam = hardware.getRam(); |
434 | return this; |
435 | } |
436 | |
437 | private boolean currentLocationWiderThan(Location location) { |
438 | return this.location == null || (location != null && this.location.getScope().compareTo(location.getScope()) < 0); |
439 | } |
440 | |
441 | /** |
442 | * {@inheritDoc} |
443 | */ |
444 | @Override |
445 | public TemplateBuilder fromImage(Image image) { |
446 | if (currentLocationWiderThan(image.getLocation())) |
447 | this.location = image.getLocation(); |
448 | if (image.getOperatingSystem().getFamily() != null) |
449 | this.osFamily = image.getOperatingSystem().getFamily(); |
450 | if (image.getName() != null) |
451 | this.imageName = image.getName(); |
452 | if (image.getDescription() != null) |
453 | this.imageDescription = image.getDescription(); |
454 | if (image.getOperatingSystem().getName() != null) |
455 | this.osName = image.getOperatingSystem().getName(); |
456 | if (image.getOperatingSystem().getDescription() != null) |
457 | this.osDescription = image.getOperatingSystem().getDescription(); |
458 | if (image.getVersion() != null) |
459 | this.imageVersion = image.getVersion(); |
460 | if (image.getOperatingSystem().getVersion() != null) |
461 | this.osVersion = image.getOperatingSystem().getVersion(); |
462 | this.os64Bit = image.getOperatingSystem().is64Bit(); |
463 | if (image.getOperatingSystem().getArch() != null) |
464 | this.osArch = image.getOperatingSystem().getArch(); |
465 | return this; |
466 | } |
467 | |
468 | /** |
469 | * {@inheritDoc} |
470 | */ |
471 | @Override |
472 | public TemplateBuilder smallest() { |
473 | this.biggest = false; |
474 | return this; |
475 | } |
476 | |
477 | /** |
478 | * {@inheritDoc} |
479 | */ |
480 | @Override |
481 | public TemplateBuilder biggest() { |
482 | this.biggest = true; |
483 | return this; |
484 | } |
485 | |
486 | /** |
487 | * {@inheritDoc} |
488 | */ |
489 | @Override |
490 | public TemplateBuilder fastest() { |
491 | this.fastest = true; |
492 | return this; |
493 | } |
494 | |
495 | /** |
496 | * {@inheritDoc} |
497 | */ |
498 | @Override |
499 | public TemplateBuilder locationId(final String locationId) { |
500 | Set<? extends Location> locations = this.locations.get(); |
501 | try { |
502 | this.location = Iterables.find(locations, new Predicate<Location>() { |
503 | |
504 | @Override |
505 | public boolean apply(Location input) { |
506 | return input.getId().equals(locationId); |
507 | } |
508 | |
509 | @Override |
510 | public String toString() { |
511 | return "locationId(" + locationId + ")"; |
512 | } |
513 | |
514 | }); |
515 | } catch (NoSuchElementException e) { |
516 | throw new NoSuchElementException(String.format("location id %s not found in: %s", locationId, locations)); |
517 | } |
518 | return this; |
519 | } |
520 | |
521 | /** |
522 | * {@inheritDoc} |
523 | */ |
524 | @Override |
525 | public TemplateBuilder osFamily(OsFamily os) { |
526 | this.osFamily = os; |
527 | return this; |
528 | } |
529 | |
530 | /** |
531 | * {@inheritDoc} |
532 | */ |
533 | @Override |
534 | public Template build() { |
535 | if (nothingChangedExceptOptions()) { |
536 | TemplateBuilder defaultTemplate = defaultTemplateProvider.get(); |
537 | if (options != null) |
538 | defaultTemplate.options(options); |
539 | return defaultTemplate.build(); |
540 | } |
541 | if (location == null) |
542 | location = defaultLocation.get(); |
543 | if (options == null) |
544 | options = optionsProvider.get(); |
545 | logger.debug(">> searching params(%s)", this); |
546 | Set<? extends Image> images = getImages(); |
547 | Predicate<Image> imagePredicate = buildImagePredicate(); |
548 | Iterable<? extends Image> supportedImages = filter(images, buildImagePredicate()); |
549 | if (Iterables.size(supportedImages) == 0) |
550 | throw new NoSuchElementException(String.format( |
551 | "no image matched predicate %s images that didn't match below:\n%s", imagePredicate, images)); |
552 | Hardware hardware = resolveSize(hardwareSorter(), supportedImages); |
553 | Image image = resolveImage(hardware, supportedImages); |
554 | logger.debug("<< matched image(%s)", image); |
555 | |
556 | return new TemplateImpl(image, hardware, location, options); |
557 | } |
558 | |
559 | protected Hardware resolveSize(Ordering<Hardware> hardwareOrdering, final Iterable<? extends Image> images) { |
560 | Set<? extends Hardware> hardwaresl = hardwares.get(); |
561 | Hardware hardware; |
562 | try { |
563 | Iterable<? extends Hardware> hardwaresThatAreCompatibleWithOurImages = filter(hardwaresl, |
564 | new Predicate<Hardware>() { |
565 | @Override |
566 | public boolean apply(final Hardware hardware) { |
567 | return Iterables.any(images, new Predicate<Image>() { |
568 | |
569 | @Override |
570 | public boolean apply(Image input) { |
571 | return hardware.supportsImage().apply(input); |
572 | } |
573 | |
574 | @Override |
575 | public String toString() { |
576 | return "hardware(" + hardware + ").supportsImage()"; |
577 | } |
578 | |
579 | }); |
580 | |
581 | } |
582 | }); |
583 | hardware = hardwareOrdering.max(filter(hardwaresThatAreCompatibleWithOurImages, hardwarePredicate)); |
584 | } catch (NoSuchElementException exception) { |
585 | throw new NoSuchElementException("hardwares don't support any images: " + toString() + "\n" + hardwaresl |
586 | + "\n" + images); |
587 | } |
588 | logger.debug("<< matched hardware(%s)", hardware); |
589 | return hardware; |
590 | } |
591 | |
592 | protected Ordering<Hardware> hardwareSorter() { |
593 | Ordering<Hardware> hardwareOrdering = DEFAULT_SIZE_ORDERING; |
594 | if (!biggest) |
595 | hardwareOrdering = hardwareOrdering.reverse(); |
596 | if (fastest) |
597 | hardwareOrdering = Ordering.compound(ImmutableList.of(BY_CORES_ORDERING, hardwareOrdering)); |
598 | return hardwareOrdering; |
599 | } |
600 | |
601 | /** |
602 | * |
603 | * @param hardware |
604 | * @param supportedImages |
605 | * @throws NoSuchElementException |
606 | * if there's no image that matches the predicate |
607 | */ |
608 | protected Image resolveImage(final Hardware hardware, Iterable<? extends Image> supportedImages) { |
609 | Predicate<Image> imagePredicate = new Predicate<Image>() { |
610 | |
611 | @Override |
612 | public boolean apply(Image arg0) { |
613 | return hardware.supportsImage().apply(arg0); |
614 | } |
615 | |
616 | @Override |
617 | public String toString() { |
618 | return "hardware(" + hardware + ").supportsImage()"; |
619 | } |
620 | }; |
621 | try { |
622 | Iterable<? extends Image> matchingImages = filter(supportedImages, imagePredicate); |
623 | if (logger.isTraceEnabled()) |
624 | logger.trace("<< matched images(%s)", matchingImages); |
625 | List<? extends Image> maxImages = Lists2.multiMax(DEFAULT_IMAGE_ORDERING, matchingImages); |
626 | if (logger.isTraceEnabled()) |
627 | logger.trace("<< best images(%s)", maxImages); |
628 | return maxImages.get(maxImages.size() - 1); |
629 | } catch (NoSuchElementException exception) { |
630 | throw new NoSuchElementException("image didn't match: " + toString() + "\n" + supportedImages); |
631 | } |
632 | } |
633 | |
634 | protected Set<? extends Image> getImages() { |
635 | return images.get(); |
636 | } |
637 | |
638 | private Predicate<Image> buildImagePredicate() { |
639 | List<Predicate<Image>> predicates = newArrayList(); |
640 | if (imageId != null) { |
641 | predicates.add(idPredicate); |
642 | } else { |
643 | if (location != null) |
644 | predicates.add(new Predicate<Image>() { |
645 | |
646 | @Override |
647 | public boolean apply(Image input) { |
648 | return locationPredicate.apply(input); |
649 | } |
650 | |
651 | @Override |
652 | public String toString() { |
653 | return "location(" + location + ")"; |
654 | } |
655 | }); |
656 | |
657 | final List<Predicate<OperatingSystem>> osPredicates = newArrayList(); |
658 | if (osFamily != null) |
659 | osPredicates.add(osFamilyPredicate); |
660 | if (osName != null) |
661 | osPredicates.add(osNamePredicate); |
662 | if (osDescription != null) |
663 | osPredicates.add(osDescriptionPredicate); |
664 | if (osVersion != null) |
665 | osPredicates.add(osVersionPredicate); |
666 | if (os64Bit != null) |
667 | osPredicates.add(os64BitPredicate); |
668 | if (osArch != null) |
669 | osPredicates.add(osArchPredicate); |
670 | predicates.add(new Predicate<Image>() { |
671 | |
672 | @Override |
673 | public boolean apply(Image input) { |
674 | return Predicates.and(osPredicates).apply(input.getOperatingSystem()); |
675 | } |
676 | |
677 | @Override |
678 | public String toString() { |
679 | return Predicates.and(osPredicates).toString(); |
680 | } |
681 | |
682 | }); |
683 | if (imageVersion != null) |
684 | predicates.add(imageVersionPredicate); |
685 | if (imageName != null) |
686 | predicates.add(imageNamePredicate); |
687 | if (imageDescription != null) |
688 | predicates.add(imageDescriptionPredicate); |
689 | } |
690 | |
691 | // looks verbose, but explicit <Image> type needed for this to compile |
692 | // properly |
693 | Predicate<Image> imagePredicate = predicates.size() == 1 ? Iterables.<Predicate<Image>> get(predicates, 0) |
694 | : Predicates.<Image> and(predicates); |
695 | return imagePredicate; |
696 | } |
697 | |
698 | /** |
699 | * {@inheritDoc} |
700 | */ |
701 | @Override |
702 | public TemplateBuilder imageId(String imageId) { |
703 | this.imageId = imageId; |
704 | this.imageName = null; |
705 | this.imageDescription = null; |
706 | this.imageVersion = null; |
707 | this.osFamily = null; |
708 | this.osName = null; |
709 | this.osDescription = null; |
710 | this.osVersion = null; |
711 | this.os64Bit = null; |
712 | this.osArch = null; |
713 | return this; |
714 | } |
715 | |
716 | /** |
717 | * {@inheritDoc} |
718 | */ |
719 | @Override |
720 | public TemplateBuilder imageNameMatches(String nameRegex) { |
721 | this.imageName = nameRegex; |
722 | return this; |
723 | } |
724 | |
725 | /** |
726 | * {@inheritDoc} |
727 | */ |
728 | @Override |
729 | public TemplateBuilder imageDescriptionMatches(String descriptionRegex) { |
730 | this.imageDescription = descriptionRegex; |
731 | return this; |
732 | } |
733 | |
734 | /** |
735 | * {@inheritDoc} |
736 | */ |
737 | @Override |
738 | public TemplateBuilder imageVersionMatches(String imageVersionRegex) { |
739 | this.imageVersion = imageVersionRegex; |
740 | return this; |
741 | } |
742 | |
743 | /** |
744 | * {@inheritDoc} |
745 | */ |
746 | @Override |
747 | public TemplateBuilder osVersionMatches(String osVersionRegex) { |
748 | this.osVersion = osVersionRegex; |
749 | return this; |
750 | } |
751 | |
752 | /** |
753 | * {@inheritDoc} |
754 | */ |
755 | @Override |
756 | public TemplateBuilder osArchMatches(String osArchitectureRegex) { |
757 | this.osArch = osArchitectureRegex; |
758 | return this; |
759 | } |
760 | |
761 | /** |
762 | * {@inheritDoc} |
763 | */ |
764 | @Override |
765 | public TemplateBuilder minCores(double minCores) { |
766 | this.minCores = minCores; |
767 | return this; |
768 | } |
769 | |
770 | /** |
771 | * {@inheritDoc} |
772 | */ |
773 | @Override |
774 | public TemplateBuilder minRam(int megabytes) { |
775 | this.minRam = megabytes; |
776 | return this; |
777 | } |
778 | |
779 | /** |
780 | * {@inheritDoc} |
781 | */ |
782 | @Override |
783 | public TemplateBuilder osNameMatches(String osNameRegex) { |
784 | this.osName = osNameRegex; |
785 | return this; |
786 | } |
787 | |
788 | /** |
789 | * {@inheritDoc} |
790 | */ |
791 | @Override |
792 | public TemplateBuilder osDescriptionMatches(String osDescriptionRegex) { |
793 | this.osDescription = osDescriptionRegex; |
794 | return this; |
795 | } |
796 | |
797 | /** |
798 | * {@inheritDoc} |
799 | */ |
800 | @Override |
801 | public TemplateBuilder hardwareId(String hardwareId) { |
802 | this.hardwareId = hardwareId; |
803 | return this; |
804 | } |
805 | |
806 | /** |
807 | * {@inheritDoc} |
808 | */ |
809 | @Override |
810 | public TemplateBuilder options(TemplateOptions options) { |
811 | this.options = optionsProvider.get(); |
812 | copyTemplateOptions(checkNotNull(options, "options"), this.options); |
813 | return this; |
814 | } |
815 | |
816 | protected void copyTemplateOptions(TemplateOptions from, TemplateOptions to) { |
817 | if (!Arrays.equals(to.getInboundPorts(), from.getInboundPorts())) |
818 | to.inboundPorts(from.getInboundPorts()); |
819 | if (from.getRunScript() != null) |
820 | to.runScript(from.getRunScript()); |
821 | if (from.getPrivateKey() != null) |
822 | to.installPrivateKey(from.getPrivateKey()); |
823 | if (from.getPublicKey() != null) |
824 | to.authorizePublicKey(from.getPublicKey()); |
825 | if (from.getPort() != -1) |
826 | to.blockOnPort(from.getPort(), from.getSeconds()); |
827 | if (from.isIncludeMetadata()) |
828 | to.withMetadata(); |
829 | if (!from.shouldBlockUntilRunning()) |
830 | to.blockUntilRunning(false); |
831 | if (!from.shouldBlockOnComplete()) |
832 | to.blockOnComplete(false); |
833 | if (from.getOverrideCredentials() != null) |
834 | to.withOverridingCredentials(from.getOverrideCredentials()); |
835 | if (from.getTaskName() != null) |
836 | to.nameTask(from.getTaskName()); |
837 | } |
838 | |
839 | @VisibleForTesting |
840 | boolean nothingChangedExceptOptions() { |
841 | return osFamily == null && location == null && imageId == null && hardwareId == null && osName == null |
842 | && osDescription == null && imageVersion == null && osVersion == null && osArch == null |
843 | && os64Bit == null && imageName == null && imageDescription == null && minCores == 0 && minRam == 0 |
844 | && !biggest && !fastest; |
845 | } |
846 | |
847 | /** |
848 | * {@inheritDoc} |
849 | */ |
850 | @Override |
851 | public TemplateBuilder any() { |
852 | return defaultTemplateProvider.get(); |
853 | } |
854 | |
855 | @Override |
856 | public String toString() { |
857 | return "[biggest=" + biggest + ", fastest=" + fastest + ", imageName=" + imageName + ", imageDescription=" |
858 | + imageDescription + ", imageId=" + imageId + ", imageVersion=" + imageVersion + ", location=" |
859 | + location + ", minCores=" + minCores + ", minRam=" + minRam + ", osFamily=" + osFamily + ", osName=" |
860 | + osName + ", osDescription=" + osDescription + ", osVersion=" + osVersion + ", osArch=" + osArch |
861 | + ", os64Bit=" + os64Bit + ", hardwareId=" + hardwareId + "]"; |
862 | } |
863 | |
864 | @Override |
865 | public TemplateBuilder os64Bit(boolean is64Bit) { |
866 | this.os64Bit = is64Bit; |
867 | return this; |
868 | } |
869 | |
870 | } |