| 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 | } |