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