| 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.strategy; | 
| 20 |   | 
| 21 | import static com.google.common.base.Preconditions.checkNotNull; | 
| 22 | import static com.google.common.base.Preconditions.checkState; | 
| 23 | import static com.google.common.base.Throwables.getRootCause; | 
| 24 | import static org.jclouds.compute.util.ComputeServiceUtils.findReachableSocketOnNode; | 
| 25 |   | 
| 26 | import java.util.Map; | 
| 27 | import java.util.Set; | 
| 28 | import java.util.concurrent.Callable; | 
| 29 |   | 
| 30 | import javax.annotation.Nullable; | 
| 31 | import javax.annotation.Resource; | 
| 32 | import javax.inject.Named; | 
| 33 |   | 
| 34 | import org.jclouds.compute.callables.RunScriptOnNode; | 
| 35 | import org.jclouds.compute.config.CustomizationResponse; | 
| 36 | import org.jclouds.compute.domain.ExecResponse; | 
| 37 | import org.jclouds.compute.domain.NodeMetadata; | 
| 38 | import org.jclouds.compute.options.TemplateOptions; | 
| 39 | import org.jclouds.compute.predicates.RetryIfSocketNotYetOpen; | 
| 40 | import org.jclouds.compute.reference.ComputeServiceConstants; | 
| 41 | import org.jclouds.compute.reference.ComputeServiceConstants.Timeouts; | 
| 42 | import org.jclouds.logging.Logger; | 
| 43 | import org.jclouds.scriptbuilder.domain.Statement; | 
| 44 |   | 
| 45 | import com.google.common.base.Function; | 
| 46 | import com.google.common.base.Predicate; | 
| 47 | import com.google.common.collect.Multimap; | 
| 48 | import com.google.inject.assistedinject.Assisted; | 
| 49 | import com.google.inject.assistedinject.AssistedInject; | 
| 50 |   | 
| 51 | /** | 
| 52 |  *  | 
| 53 |  * @author Adrian Cole | 
| 54 |  */ | 
| 55 | public class CustomizeNodeAndAddToGoodMapOrPutExceptionIntoBadMap implements Callable<Void>, | 
| 56 |          Function<NodeMetadata, Void> { | 
| 57 |    public static interface Factory { | 
| 58 |       Callable<Void> create(TemplateOptions options, NodeMetadata node, Set<NodeMetadata> goodNodes, | 
| 59 |                Map<NodeMetadata, Exception> badNodes, | 
| 60 |                Multimap<NodeMetadata, CustomizationResponse> customizationResponses); | 
| 61 |   | 
| 62 |       Function<NodeMetadata, Void> create(TemplateOptions options, Set<NodeMetadata> goodNodes, | 
| 63 |                Map<NodeMetadata, Exception> badNodes, | 
| 64 |                Multimap<NodeMetadata, CustomizationResponse> customizationResponses); | 
| 65 |    } | 
| 66 |   | 
| 67 |    @Resource | 
| 68 |    @Named(ComputeServiceConstants.COMPUTE_LOGGER) | 
| 69 |    protected Logger logger = Logger.NULL; | 
| 70 |   | 
| 71 |    private final Predicate<NodeMetadata> nodeRunning; | 
| 72 |    private final InitializeRunScriptOnNodeOrPlaceInBadMap.Factory initScriptRunnerFactory; | 
| 73 |    private final GetNodeMetadataStrategy getNode; | 
| 74 |    private final RetryIfSocketNotYetOpen socketTester; | 
| 75 |    private final Timeouts timeouts; | 
| 76 |   | 
| 77 |    @Nullable | 
| 78 |    private final Statement statement; | 
| 79 |    private final TemplateOptions options; | 
| 80 |    private NodeMetadata node; | 
| 81 |    private final Set<NodeMetadata> goodNodes; | 
| 82 |    private final Map<NodeMetadata, Exception> badNodes; | 
| 83 |    private final Multimap<NodeMetadata, CustomizationResponse> customizationResponses; | 
| 84 |   | 
| 85 |    private transient boolean tainted; | 
| 86 |   | 
| 87 |    @AssistedInject | 
| 88 |    public CustomizeNodeAndAddToGoodMapOrPutExceptionIntoBadMap( | 
| 89 |             @Named("NODE_RUNNING") Predicate<NodeMetadata> nodeRunning, GetNodeMetadataStrategy getNode, | 
| 90 |             RetryIfSocketNotYetOpen socketTester, Timeouts timeouts, | 
| 91 |             Function<TemplateOptions, Statement> templateOptionsToStatement, | 
| 92 |             InitializeRunScriptOnNodeOrPlaceInBadMap.Factory initScriptRunnerFactory, | 
| 93 |             @Assisted TemplateOptions options, @Assisted @Nullable NodeMetadata node, | 
| 94 |             @Assisted Set<NodeMetadata> goodNodes, @Assisted Map<NodeMetadata, Exception> badNodes, | 
| 95 |             @Assisted Multimap<NodeMetadata, CustomizationResponse> customizationResponses) { | 
| 96 |       this.statement = checkNotNull(templateOptionsToStatement, "templateOptionsToStatement").apply( | 
| 97 |                checkNotNull(options, "options")); | 
| 98 |       this.nodeRunning = checkNotNull(nodeRunning, "nodeRunning"); | 
| 99 |       this.initScriptRunnerFactory = checkNotNull(initScriptRunnerFactory, "initScriptRunnerFactory"); | 
| 100 |       this.getNode = checkNotNull(getNode, "getNode"); | 
| 101 |       this.socketTester = checkNotNull(socketTester, "socketTester"); | 
| 102 |       this.timeouts = checkNotNull(timeouts, "timeouts"); | 
| 103 |       this.node = node; | 
| 104 |       this.options = checkNotNull(options, "options"); | 
| 105 |       this.goodNodes = checkNotNull(goodNodes, "goodNodes"); | 
| 106 |       this.badNodes = checkNotNull(badNodes, "badNodes"); | 
| 107 |       this.customizationResponses = checkNotNull(customizationResponses, "customizationResponses"); | 
| 108 |    } | 
| 109 |   | 
| 110 |    @AssistedInject | 
| 111 |    public CustomizeNodeAndAddToGoodMapOrPutExceptionIntoBadMap( | 
| 112 |             @Named("NODE_RUNNING") Predicate<NodeMetadata> nodeRunning, GetNodeMetadataStrategy getNode, | 
| 113 |             RetryIfSocketNotYetOpen socketTester, Timeouts timeouts, | 
| 114 |             Function<TemplateOptions, Statement> templateOptionsToStatement, | 
| 115 |             InitializeRunScriptOnNodeOrPlaceInBadMap.Factory initScriptRunnerFactory, | 
| 116 |             @Assisted TemplateOptions options, @Assisted Set<NodeMetadata> goodNodes, | 
| 117 |             @Assisted Map<NodeMetadata, Exception> badNodes, | 
| 118 |             @Assisted Multimap<NodeMetadata, CustomizationResponse> customizationResponses) { | 
| 119 |       this(nodeRunning, getNode, socketTester, timeouts, templateOptionsToStatement, initScriptRunnerFactory, options, | 
| 120 |                null, goodNodes, badNodes, customizationResponses); | 
| 121 |    } | 
| 122 |   | 
| 123 |    @Override | 
| 124 |    public Void call() { | 
| 125 |       checkState(!tainted, "this object is not designed to be reused: %s", toString()); | 
| 126 |       tainted = true; | 
| 127 |       try { | 
| 128 |          if (options.shouldBlockUntilRunning()) { | 
| 129 |             if (nodeRunning.apply(node)) { | 
| 130 |                node = getNode.getNode(node.getId()); | 
| 131 |             } else { | 
| 132 |                throw new IllegalStateException(String.format( | 
| 133 |                         "node didn't achieve the state running on node %s within %d seconds, final state: %s", node | 
| 134 |                                  .getId(), timeouts.nodeRunning / 1000, node.getState())); | 
| 135 |             } | 
| 136 |             if (statement != null) { | 
| 137 |                RunScriptOnNode runner = initScriptRunnerFactory.create(node, statement, options, badNodes).call(); | 
| 138 |                if (runner != null) { | 
| 139 |                   ExecResponse exec = runner.call(); | 
| 140 |                   customizationResponses.put(node, exec); | 
| 141 |                } | 
| 142 |             } | 
| 143 |             if (options.getPort() > 0) { | 
| 144 |                findReachableSocketOnNode(socketTester.seconds(options.getSeconds()), node, options.getPort()); | 
| 145 |             } | 
| 146 |          } | 
| 147 |          logger.debug("<< options applied node(%s)", node.getId()); | 
| 148 |          goodNodes.add(node); | 
| 149 |       } catch (Exception e) { | 
| 150 |          logger.error(e, "<< problem applying options to node(%s): ", node.getId(), getRootCause(e).getMessage()); | 
| 151 |          badNodes.put(node, e); | 
| 152 |       } | 
| 153 |       return null; | 
| 154 |    } | 
| 155 |   | 
| 156 |    @Override | 
| 157 |    public Void apply(NodeMetadata input) { | 
| 158 |       this.node = input; | 
| 159 |       call(); | 
| 160 |       return null; | 
| 161 |    } | 
| 162 | } |