/** * Check for the count of EC2 slaves and determine if a new slave can be added. Takes into account * both what Amazon reports as well as an internal count of slaves currently being "provisioned". * Check for the count of EC2 slaves and determine if a new slave can be added. Takes into account * both what Amazon reports as well as an internal count of slaves currently being "provisioned". * * @param templateDesc */ private boolean addProvisionedSlave(String ami, int amiCap, String templateDesc) throws AmazonClientException { int estimatedTotalSlaves = countCurrentEC2Slaves(null, null); int estimatedAmiSlaves = countCurrentEC2Slaves(ami, templateDesc); synchronized (provisioningAmis) { int currentProvisioning; for (int amiCount : provisioningAmis.values()) { estimatedTotalSlaves += amiCount; } try { currentProvisioning = provisioningAmis.get(ami); } catch (NullPointerException npe) { currentProvisioning = 0; } estimatedAmiSlaves += currentProvisioning; if (estimatedTotalSlaves >= instanceCap) { LOGGER.log( Level.INFO, "Total instance cap of " + instanceCap + " reached, not provisioning."); return false; // maxed out } if (estimatedAmiSlaves >= amiCap) { LOGGER.log( Level.INFO, "AMI Instance cap of " + amiCap + " reached for ami " + ami + ", not provisioning."); return false; // maxed out } LOGGER.log( Level.INFO, "Provisioning for AMI " + ami + "; " + "Estimated number of total slaves: " + String.valueOf(estimatedTotalSlaves) + "; " + "Estimated number of slaves for ami " + ami + ": " + String.valueOf(estimatedAmiSlaves)); provisioningAmis.put(ami, currentProvisioning + 1); return true; } }
protected FormValidation doTestConnection( URL ec2endpoint, boolean useInstanceProfileForCredentials, String accessId, String secretKey, String privateKey) throws IOException, ServletException { try { AWSCredentialsProvider credentialsProvider = createCredentialsProvider(useInstanceProfileForCredentials, accessId, secretKey); AmazonEC2 ec2 = connect(credentialsProvider, ec2endpoint); ec2.describeInstances(); if (privateKey == null) return FormValidation.error( "Private key is not specified. Click 'Generate Key' to generate one."); if (privateKey.trim().length() > 0) { // check if this key exists EC2PrivateKey pk = new EC2PrivateKey(privateKey); if (pk.find(ec2) == null) return FormValidation.error( "The EC2 key pair private key isn't registered to this EC2 region (fingerprint is " + pk.getFingerprint() + ")"); } return FormValidation.ok(Messages.EC2Cloud_Success()); } catch (AmazonClientException e) { LOGGER.log(Level.WARNING, "Failed to check EC2 credential", e); return FormValidation.error(e.getMessage()); } }
public FormValidation doGenerateKey( StaplerResponse rsp, URL ec2EndpointUrl, boolean useInstanceProfileForCredentials, String accessId, String secretKey) throws IOException, ServletException { try { AWSCredentialsProvider credentialsProvider = createCredentialsProvider(useInstanceProfileForCredentials, accessId, secretKey); AmazonEC2 ec2 = connect(credentialsProvider, ec2EndpointUrl); List<KeyPairInfo> existingKeys = ec2.describeKeyPairs().getKeyPairs(); int n = 0; while (true) { boolean found = false; for (KeyPairInfo k : existingKeys) { if (k.getKeyName().equals("hudson-" + n)) found = true; } if (!found) break; n++; } CreateKeyPairRequest request = new CreateKeyPairRequest("hudson-" + n); KeyPair key = ec2.createKeyPair(request).getKeyPair(); rsp.addHeader( "script", "findPreviousFormItem(button,'privateKey').value='" + key.getKeyMaterial().replace("\n", "\\n") + "'"); return FormValidation.ok(Messages.EC2Cloud_Success()); } catch (AmazonClientException e) { LOGGER.log(Level.WARNING, "Failed to check EC2 credential", e); return FormValidation.error(e.getMessage()); } }
@Override public Collection<PlannedNode> provision(Label label, int excessWorkload) { try { // Count number of pending executors from spot requests for (EC2SpotSlave n : NodeIterator.nodes(EC2SpotSlave.class)) { // If the slave is online then it is already counted by Jenkins // We only want to count potential additional Spot instance // slaves if (n.getComputer().isOffline() && label.matches(n.getAssignedLabels())) { DescribeSpotInstanceRequestsRequest dsir = new DescribeSpotInstanceRequestsRequest() .withSpotInstanceRequestIds(n.getSpotInstanceRequestId()); for (SpotInstanceRequest sir : connect().describeSpotInstanceRequests(dsir).getSpotInstanceRequests()) { // Count Spot requests that are open and still have a // chance to be active // A request can be active and not yet registered as a // slave. We check above // to ensure only unregistered slaves get counted if (sir.getState().equals("open") || sir.getState().equals("active")) { excessWorkload -= n.getNumExecutors(); } } } } LOGGER.log(Level.INFO, "Excess workload after pending Spot instances: " + excessWorkload); List<PlannedNode> r = new ArrayList<PlannedNode>(); final SlaveTemplate t = getTemplate(label); int amiCap = t.getInstanceCap(); while (excessWorkload > 0) { if (!addProvisionedSlave(t.ami, amiCap, t.description)) { break; } r.add( new PlannedNode( t.getDisplayName(), Computer.threadPoolForRemoting.submit( new Callable<Node>() { public Node call() throws Exception { // TODO: record the output somewhere try { EC2AbstractSlave s = t.provision(StreamTaskListener.fromStdout()); Hudson.getInstance().addNode(s); // EC2 instances may have a long init script. If we // declare // the provisioning complete by returning without // the connect // operation, NodeProvisioner may decide that it // still wants // one more instance, because it sees that (1) all // the slaves // are offline (because it's still being launched) // and // (2) there's no capacity provisioned yet. // // deferring the completion of provisioning until // the launch // goes successful prevents this problem. s.toComputer().connect(false).get(); return s; } finally { decrementAmiSlaveProvision(t.ami); } } }), t.getNumExecutors())); excessWorkload -= t.getNumExecutors(); } return r; } catch (AmazonClientException e) { LOGGER.log(Level.WARNING, "Failed to count the # of live instances on EC2", e); return Collections.emptyList(); } }