Merge branch 'ARM-dev' into local-build-successfull

This commit is contained in:
Arjun Roy Chaudhuri 2016-11-21 10:23:34 -08:00
commit a88cfc803f
26 changed files with 696 additions and 507 deletions

2
.gitignore vendored
View File

@ -6,3 +6,5 @@
*.settings
*.project
*.classpath
nbactions-jenkins.xml

13
pom.xml
View File

@ -7,7 +7,7 @@
</parent>
<artifactId>azure-vm-agents-plugin</artifactId>
<version>0.4.0-SNAPSHOT</version>
<version>0.4.0</version>
<packaging>hpi</packaging>
<name>Azure VM Agents Plugin</name>
<description>Provisions agents on Azure cloud</description>
@ -116,6 +116,17 @@
<artifactId>httpclient</artifactId>
<version>4.5</version>
</dependency>
<dependency>
<groupId>org.jenkins-ci.plugins</groupId>
<artifactId>credentials</artifactId>
<version>1.21</version>
</dependency>
<dependency>
<groupId>org.jenkins-ci.plugins</groupId>
<artifactId>plain-credentials</artifactId>
<version>1.3</version>
</dependency>
</dependencies>
<scm>

View File

@ -28,6 +28,7 @@ import com.microsoft.azure.util.Constants;
import com.microsoft.azure.util.CleanUpAction;
import com.microsoft.azure.util.FailureStage;
import com.microsoft.azure.remote.AzureVMAgentSSHLauncher;
import com.microsoft.azure.util.AzureCredentials;
import hudson.Extension;
import hudson.model.TaskListener;
@ -44,18 +45,20 @@ import org.jvnet.localizer.Localizable;
public class AzureVMAgent extends AbstractCloudSlave {
private static final long serialVersionUID = -760014706860995556L;
private static final long serialVersionUID = -760014706860995557L;
private final String cloudName;
private final String adminUserName;
private final String vmCredentialsId;
private final String azureCredentialsId;
private transient final AzureCredentials.ServicePrincipal credentials;
private final String sshPrivateKey;
private final String sshPassPhrase;
private final String adminPassword;
private final String jvmOptions;
private boolean shutdownOnIdle;
@ -77,16 +80,6 @@ public class AzureVMAgent extends AbstractCloudSlave {
private final Mode mode;
private final String subscriptionId;
private final String clientId;
private final String clientSecret;
private final String oauth2TokenEndpoint;
private final String managementURL;
private String templateName;
private CleanUpAction cleanUpAction;
@ -117,21 +110,17 @@ public class AzureVMAgent extends AbstractCloudSlave {
final RetentionStrategy<AzureVMComputer> retentionStrategy,
final List<? extends NodeProperty<?>> nodeProperties,
final String cloudName,
final String adminUserName,
final String vmCredentialsId,
final String sshPrivateKey,
final String sshPassPhrase,
final String adminPassword,
final String jvmOptions,
final boolean shutdownOnIdle,
final boolean eligibleForReuse,
final String deploymentName,
final int retentionTimeInMin,
final String initScript,
final String subscriptionId,
final String clientId,
final String clientSecret,
final String oauth2TokenEndpoint,
final String managementURL,
final String azureCredentialsId,
final AzureCredentials.ServicePrincipal servicePrincipal,
final String agentLaunchMethod,
final CleanUpAction cleanUpAction,
final Localizable cleanUpReason,
@ -143,10 +132,11 @@ public class AzureVMAgent extends AbstractCloudSlave {
this.cloudName = cloudName;
this.templateName = templateName;
this.adminUserName = adminUserName;
this.vmCredentialsId = vmCredentialsId;
this.azureCredentialsId = azureCredentialsId;
this.credentials = servicePrincipal;
this.sshPrivateKey = sshPrivateKey;
this.sshPassPhrase = sshPassPhrase;
this.adminPassword = adminPassword;
this.jvmOptions = jvmOptions;
this.shutdownOnIdle = shutdownOnIdle;
this.eligibleForReuse = eligibleForReuse;
@ -155,11 +145,6 @@ public class AzureVMAgent extends AbstractCloudSlave {
this.initScript = initScript;
this.osType = osType;
this.mode = mode;
this.subscriptionId = subscriptionId;
this.clientId = clientId;
this.clientSecret = clientSecret;
this.oauth2TokenEndpoint = oauth2TokenEndpoint;
this.managementURL = managementURL;
this.agentLaunchMethod = agentLaunchMethod;
this.setCleanUpAction(cleanUpAction);
this.setCleanupReason(cleanUpReason);
@ -178,21 +163,17 @@ public class AzureVMAgent extends AbstractCloudSlave {
final Mode mode,
final String label,
final String cloudName,
final String adminUserName,
final String vmCredentialsId,
final String sshPrivateKey,
final String sshPassPhrase,
final String adminPassword,
final String jvmOptions,
final boolean shutdownOnIdle,
final boolean eligibleForReuse,
final String deploymentName,
final int retentionTimeInMin,
final String initScript,
final String subscriptionId,
final String clientId,
final String clientSecret,
final String oauth2TokenEndpoint,
final String managementURL,
final String azureCredentialsId,
final AzureCredentials.ServicePrincipal servicePrincipal,
final String agentLaunchMethod,
final CleanUpAction cleanUpAction,
final Localizable cleanUpReason,
@ -213,21 +194,17 @@ public class AzureVMAgent extends AbstractCloudSlave {
new AzureVMCloudRetensionStrategy(retentionTimeInMin),
Collections.<NodeProperty<?>>emptyList(),
cloudName,
adminUserName,
vmCredentialsId,
sshPrivateKey,
sshPassPhrase,
adminPassword,
jvmOptions,
shutdownOnIdle,
eligibleForReuse,
deploymentName,
retentionTimeInMin,
initScript,
subscriptionId,
clientId,
clientSecret,
oauth2TokenEndpoint,
managementURL,
azureCredentialsId,
servicePrincipal,
agentLaunchMethod,
cleanUpAction,
cleanUpReason,
@ -245,28 +222,15 @@ public class AzureVMAgent extends AbstractCloudSlave {
return mode;
}
public String getAdminUserName() {
return adminUserName;
public String getVMCredentialsId() {
return vmCredentialsId;
}
public String getSubscriptionId() {
return subscriptionId;
}
public String getClientId() {
return clientId;
}
public String getClientSecret() {
return clientSecret;
}
public String getOauth2TokenEndpoint() {
return oauth2TokenEndpoint;
}
public String getManagementURL() {
return managementURL;
public AzureCredentials.ServicePrincipal getServicePrincipal()
{
if(credentials == null && azureCredentialsId != null)
return AzureCredentials.getServicePrincipal(azureCredentialsId);
return credentials;
}
public String getSshPrivateKey() {
@ -285,10 +249,6 @@ public class AzureVMAgent extends AbstractCloudSlave {
return deploymentName;
}
public String getAdminPassword() {
return adminPassword;
}
public CleanUpAction getCleanUpAction() {
return cleanUpAction;
}
@ -478,7 +438,7 @@ public class AzureVMAgent extends AbstractCloudSlave {
public String toString() {
return "AzureVMAgent ["
+ "\n\tcloudName=" + cloudName
+ "\n\tadminUserName=" + adminUserName
+ "\n\tVMCredentialsId=" + vmCredentialsId
+ "\n\tjvmOptions=" + jvmOptions
+ "\n\tshutdownOnIdle=" + shutdownOnIdle
+ "\n\tretentionTimeInMin=" + retentionTimeInMin
@ -489,7 +449,7 @@ public class AzureVMAgent extends AbstractCloudSlave {
+ "\n\tpublicDNSName=" + publicDNSName
+ "\n\tsshPort=" + sshPort
+ "\n\tmode=" + mode
+ "\n\tmanagementURL=" + managementURL
+ "\n\tmanagementURL=" + credentials.serviceManagementURL == null ? "<none>" : credentials.serviceManagementURL
+ "\n\ttemplateName=" + templateName
+ "\n\tcleanUpAction=" + cleanUpAction
+ "\n]";

View File

@ -25,6 +25,7 @@ import com.microsoft.azure.management.resources.ResourceManagementService;
import com.microsoft.azure.management.resources.models.DeploymentGetResult;
import com.microsoft.azure.management.resources.models.ProvisioningState;
import com.microsoft.azure.retry.DefaultRetryStrategy;
import com.microsoft.azure.util.AzureUserAgentFilter;
import com.microsoft.azure.util.ExecutionEngine;
import com.microsoft.azure.util.CleanUpAction;
import com.microsoft.windowsazure.Configuration;
@ -100,7 +101,8 @@ public final class AzureVMAgentCleanUpTask extends AsyncPeriodicWork {
try {
final Configuration config = ServiceDelegateHelper.getConfiguration(cloud);
final ResourceManagementClient rmc = ResourceManagementService.create(config);
final ResourceManagementClient rmc = ResourceManagementService.create(config)
.withRequestFilterFirst(new AzureUserAgentFilter());
DeploymentGetResult deployment =
rmc.getDeploymentsOperations().get(info.getResourceGroupName(), info.getDeploymentName());

View File

@ -15,7 +15,16 @@
*/
package com.microsoft.azure;
import com.cloudbees.plugins.credentials.CredentialsMatcher;
import com.cloudbees.plugins.credentials.CredentialsMatchers;
import com.cloudbees.plugins.credentials.CredentialsProvider;
import com.cloudbees.plugins.credentials.common.StandardCredentials;
import com.cloudbees.plugins.credentials.common.StandardListBoxModel;
import com.cloudbees.plugins.credentials.common.StandardUsernamePasswordCredentials;
import com.cloudbees.plugins.credentials.domains.DomainRequirement;
import com.cloudbees.plugins.credentials.domains.URIRequirementBuilder;
import com.microsoft.azure.Messages;
import com.microsoft.azure.util.AzureCredentials;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
@ -38,15 +47,21 @@ import hudson.RelativePath;
import hudson.model.Describable;
import hudson.model.TaskListener;
import hudson.model.Descriptor;
import hudson.model.Item;
import hudson.model.Label;
import hudson.model.Node;
import hudson.model.labels.LabelAtom;
import hudson.security.ACL;
import hudson.util.FormValidation;
import hudson.util.ListBoxModel;
import java.net.URISyntaxException;
import java.util.Collections;
import java.util.Date;
import java.util.Map;
import java.util.logging.Level;
import org.apache.commons.lang.StringUtils;
import org.jenkinsci.plugins.plaincredentials.StringCredentials;
import org.kohsuke.stapler.AncestorInPath;
/**
* This class defines the configuration of Azure instance templates
@ -96,9 +111,7 @@ public class AzureVMAgentTemplate implements Describable<AzureVMAgentTemplate> {
private final String initScript;
private final String adminUserName;
private final String adminPassword;
private final String credentialsId;
private final String agentWorkspace;
@ -146,8 +159,7 @@ public class AzureVMAgentTemplate implements Describable<AzureVMAgentTemplate> {
final String imageVersion,
final String agentLaunchMethod,
final String initScript,
final String adminUserName,
final String adminPassword,
final String credentialsId,
final String virtualNetworkName,
final String subnetName,
final String agentWorkspace,
@ -182,8 +194,7 @@ public class AzureVMAgentTemplate implements Describable<AzureVMAgentTemplate> {
this.shutdownOnIdle = shutdownOnIdle;
this.initScript = initScript;
this.agentLaunchMethod = agentLaunchMethod;
this.adminUserName = adminUserName;
this.adminPassword = adminPassword;
this.credentialsId = credentialsId;
this.virtualNetworkName = virtualNetworkName;
this.subnetName = subnetName;
this.agentWorkspace = agentWorkspace;
@ -274,12 +285,8 @@ public class AzureVMAgentTemplate implements Describable<AzureVMAgentTemplate> {
return initScript;
}
public String getAdminUserName() {
return adminUserName;
}
public String getAdminPassword() {
return adminPassword;
public String getCredentialsId() {
return credentialsId;
}
public String getVirtualNetworkName() {
@ -425,11 +432,8 @@ public class AzureVMAgentTemplate implements Describable<AzureVMAgentTemplate> {
* @throws Exception
*/
public List<String> verifyTemplate() throws Exception {
return AzureVMManagementServiceDelegate.verifyTemplate(azureCloud.getSubscriptionId(),
azureCloud.getClientId(),
azureCloud.getClientSecret(),
azureCloud.getOauth2TokenEndpoint(),
azureCloud.getServiceManagementURL(),
return AzureVMManagementServiceDelegate.verifyTemplate(
azureCloud.getServicePrincipal(),
templateName,
labels,
location,
@ -444,8 +448,7 @@ public class AzureVMAgentTemplate implements Describable<AzureVMAgentTemplate> {
imageVersion,
agentLaunchMethod,
initScript,
adminUserName,
adminPassword,
credentialsId,
virtualNetworkName,
subnetName,
retentionTimeInMin + "",
@ -478,6 +481,13 @@ public class AzureVMAgentTemplate implements Describable<AzureVMAgentTemplate> {
return model;
}
public ListBoxModel doFillCredentialsIdItems(@AncestorInPath Item owner) {
// when configuring the job, you only want those credentials that are available to ACL.SYSTEM selectable
// as we cannot select from a user's credentials unless they are the only user submitting the build
// (which we cannot assume) thus ACL.SYSTEM is correct here.
return new StandardListBoxModel().withAll(CredentialsProvider.lookupCredentials(StandardUsernamePasswordCredentials.class, owner, ACL.SYSTEM, Collections.<DomainRequirement>emptyList()));
}
public ListBoxModel doFillOsTypeItems() throws IOException, ServletException {
ListBoxModel model = new ListBoxModel();
@ -564,17 +574,6 @@ public class AzureVMAgentTemplate implements Describable<AzureVMAgentTemplate> {
return FormValidation.ok();
}
public FormValidation doCheckAdminUserName(@QueryParameter final String value) {
if (StringUtils.isNotBlank(value)) {
if (AzureUtil.isValidUserName(value)) {
return FormValidation.ok();
} else {
return FormValidation.error(Messages.Azure_GC_UserName_Err());
}
}
return FormValidation.ok();
}
public FormValidation doCheckNoOfParallelJobs(@QueryParameter final String value) {
if (StringUtils.isNotBlank(value)) {
String result = AzureVMManagementServiceDelegate.verifyNoOfExecutors(value);
@ -624,11 +623,7 @@ public class AzureVMAgentTemplate implements Describable<AzureVMAgentTemplate> {
}
public FormValidation doVerifyConfiguration(
@RelativePath("..") @QueryParameter String subscriptionId,
@RelativePath("..") @QueryParameter String clientId,
@RelativePath("..") @QueryParameter String clientSecret,
@RelativePath("..") @QueryParameter String oauth2TokenEndpoint,
@RelativePath("..") @QueryParameter String serviceManagementURL,
@RelativePath("..") @QueryParameter String azureCredentialsId,
@RelativePath("..") @QueryParameter String resourceGroupName,
@QueryParameter String templateName,
@QueryParameter String labels,
@ -644,19 +639,18 @@ public class AzureVMAgentTemplate implements Describable<AzureVMAgentTemplate> {
@QueryParameter String imageVersion,
@QueryParameter String agentLaunchMethod,
@QueryParameter String initScript,
@QueryParameter String adminUserName,
@QueryParameter String adminPassword,
@QueryParameter String credentialsId,
@QueryParameter String virtualNetworkName,
@QueryParameter String subnetName,
@QueryParameter String retentionTimeInMin,
@QueryParameter String jvmOptions) {
AzureCredentials.ServicePrincipal servicePrincipal = AzureCredentials.getServicePrincipal(azureCredentialsId);
LOGGER.log(Level.INFO,
"Verify configuration:\n\t"
+ "subscriptionId: {0};\n\t"
+ "clientId: {1};\n\t"
+ "clientSecret: {2};\n\t"
+ "oauth2TokenEndpoint: {3};\n\t"
+ "serviceManagementURL: {4};\n\t"
+ "resourceGroupName: {5};\n\t."
+ "templateName: {6};\n\t"
@ -673,18 +667,16 @@ public class AzureVMAgentTemplate implements Describable<AzureVMAgentTemplate> {
+ "imageVersion: {17};\n\t"
+ "agentLaunchMethod: {18};\n\t"
+ "initScript: {19};\n\t"
+ "adminUserName: {20};\n\t"
+ "adminPassword: {21};\n\t"
+ "virtualNetworkName: {22};\n\t"
+ "subnetName: {23};\n\t"
+ "retentionTimeInMin: {24};\n\t"
+ "jvmOptions: {25};",
+ "credentialsId: {20};\n\t"
+ "virtualNetworkName: {21};\n\t"
+ "subnetName: {22};\n\t"
+ "retentionTimeInMin: {23};\n\t"
+ "jvmOptions: {24};",
new Object[] {
subscriptionId,
clientId,
(StringUtils.isNotBlank(clientSecret) ? "********" : null),
oauth2TokenEndpoint,
serviceManagementURL,
servicePrincipal.subscriptionId.getPlainText(),
(StringUtils.isNotBlank(servicePrincipal.clientId.getPlainText()) ? "********" : null),
(StringUtils.isNotBlank(servicePrincipal.clientSecret.getPlainText()) ? "********" : null),
servicePrincipal.serviceManagementURL,
resourceGroupName,
templateName,
labels,
@ -700,8 +692,7 @@ public class AzureVMAgentTemplate implements Describable<AzureVMAgentTemplate> {
imageVersion,
agentLaunchMethod,
initScript,
adminUserName,
(StringUtils.isNotBlank(adminPassword) ? "********" : null),
credentialsId,
virtualNetworkName,
subnetName,
retentionTimeInMin,
@ -710,18 +701,13 @@ public class AzureVMAgentTemplate implements Describable<AzureVMAgentTemplate> {
// First validate the subscription info. If it is not correct,
// then we can't validate the
String result = AzureVMManagementServiceDelegate.verifyConfiguration(
subscriptionId, clientId, clientSecret, oauth2TokenEndpoint, serviceManagementURL, resourceGroupName);
String result = AzureVMManagementServiceDelegate.verifyConfiguration(servicePrincipal, resourceGroupName);
if (!result.equals(Constants.OP_SUCCESS)) {
return FormValidation.error(result);
}
final List<String> errors = AzureVMManagementServiceDelegate.verifyTemplate(
subscriptionId,
clientId,
clientSecret,
oauth2TokenEndpoint,
serviceManagementURL,
servicePrincipal,
templateName,
labels,
location,
@ -736,8 +722,7 @@ public class AzureVMAgentTemplate implements Describable<AzureVMAgentTemplate> {
imageVersion,
agentLaunchMethod,
initScript,
adminUserName,
adminPassword,
credentialsId,
virtualNetworkName,
subnetName,
retentionTimeInMin,

View File

@ -15,6 +15,9 @@
*/
package com.microsoft.azure;
import com.cloudbees.plugins.credentials.CredentialsProvider;
import com.cloudbees.plugins.credentials.common.StandardListBoxModel;
import com.cloudbees.plugins.credentials.domains.DomainRequirement;
import com.microsoft.azure.management.compute.models.VirtualMachineGetResponse;
import com.microsoft.azure.management.resources.ResourceManagementClient;
import com.microsoft.azure.management.resources.ResourceManagementService;
@ -22,8 +25,11 @@ import com.microsoft.azure.management.resources.models.DeploymentOperation;
import com.microsoft.azure.management.resources.models.ProvisioningState;
import com.microsoft.windowsazure.Configuration;
import com.microsoft.azure.exceptions.AzureCloudException;
import com.microsoft.azure.exceptions.AzureCredentialsValidationException;
import com.microsoft.azure.util.AzureCredentials;
import com.microsoft.azure.util.AzureUtil;
import com.microsoft.azure.util.CleanUpAction;
import com.microsoft.azure.util.AzureUserAgentFilter;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
@ -52,87 +58,83 @@ import hudson.util.StreamTaskListener;
import com.microsoft.azure.util.Constants;
import com.microsoft.azure.util.FailureStage;
import hudson.model.Item;
import hudson.security.ACL;
import hudson.util.ListBoxModel;
import java.nio.charset.Charset;
import java.util.logging.Level;
import org.apache.commons.lang.StringUtils;
import org.kohsuke.stapler.AncestorInPath;
public class AzureVMCloud extends Cloud {
public static final Logger LOGGER = Logger.getLogger(AzureVMCloud.class.getName());
private final String subscriptionId;
private transient final AzureCredentials.ServicePrincipal credentials;
private final String clientId;
private final String clientSecret;
private final String oauth2TokenEndpoint;
private final String serviceManagementURL;
private final String credentialsId;
private final int maxVirtualMachinesLimit;
private final String resourceGroupName;
private final List<AzureVMAgentTemplate> instTemplates;
private final int deploymentTimeout;
// True if the subscription has been verified.
// False otherwise.
private transient boolean configurationValid;
// Approximate virtual machine count. Updated periodically.
private int approximateVirtualMachineCount;
@DataBoundConstructor
public AzureVMCloud(
final String id,
final String subscriptionId,
final String clientId,
final String clientSecret,
final String oauth2TokenEndpoint,
final String serviceManagementURL,
final String azureCredentialsId,
final String maxVirtualMachinesLimit,
final String deploymentTimeout,
final String resourceGroupName,
final List<AzureVMAgentTemplate> instTemplates) {
this(AzureCredentials.getServicePrincipal(azureCredentialsId), azureCredentialsId, maxVirtualMachinesLimit, deploymentTimeout, resourceGroupName, instTemplates);
}
super(AzureUtil.getCloudName(subscriptionId));
this.subscriptionId = subscriptionId;
this.clientId = clientId;
this.clientSecret = clientSecret;
this.oauth2TokenEndpoint = oauth2TokenEndpoint;
private AzureVMCloud(
AzureCredentials.ServicePrincipal credentials,
final String azureCredentialsId,
final String maxVirtualMachinesLimit,
final String deploymentTimeout,
final String resourceGroupName,
final List<AzureVMAgentTemplate> instTemplates) {
super(AzureUtil.getCloudName(credentials.subscriptionId.getPlainText()));
this.credentials = credentials;
this.credentialsId = azureCredentialsId;
this.resourceGroupName = resourceGroupName;
this.serviceManagementURL = StringUtils.isBlank(serviceManagementURL)
? Constants.DEFAULT_MANAGEMENT_URL
: serviceManagementURL;
if (StringUtils.isBlank(maxVirtualMachinesLimit) || !maxVirtualMachinesLimit.matches(Constants.REG_EX_DIGIT)) {
this.maxVirtualMachinesLimit = Constants.DEFAULT_MAX_VM_LIMIT;
} else {
this.maxVirtualMachinesLimit = Integer.parseInt(maxVirtualMachinesLimit);
}
if (StringUtils.isBlank(deploymentTimeout) || !deploymentTimeout.matches(Constants.REG_EX_DIGIT)) {
this.deploymentTimeout = Constants.DEFAULT_DEPLOYMENT_TIMEOUT_SEC;
} else {
this.deploymentTimeout = Integer.parseInt(deploymentTimeout);
}
this.configurationValid = false;
this.instTemplates = instTemplates == null
? Collections.<AzureVMAgentTemplate>emptyList()
: instTemplates;
readResolve();
registerVerificationIfNeeded();
}
/**
* Register the initial verification if required
*/
@ -166,7 +168,7 @@ public class AzureVMCloud extends Cloud {
if (!this.isConfigurationValid()) {
LOGGER.log(Level.INFO, "AzureVMCloud: canProvision: Subscription not verified, or is invalid, cannot provision");
}
final AzureVMAgentTemplate template = AzureVMCloud.this.getAzureAgentTemplate(label);
// return false if there is no template for this label.
if (template == null) {
@ -182,8 +184,8 @@ public class AzureVMCloud extends Cloud {
// The template is available, but not verified. It may be queued for
// verification, but ensure that it's added.
LOGGER.log(Level.INFO,
"AzureVMCloud: canProvision: template {0} is awaiting verification or has failed verification",
template.getTemplateName());
"AzureVMCloud: canProvision: template {0} is awaiting verification or has failed verification",
template.getTemplateName());
AzureVMCloudVerificationTask.registerTemplate(template);
return false;
} else {
@ -191,30 +193,10 @@ public class AzureVMCloud extends Cloud {
}
}
public String getSubscriptionId() {
return subscriptionId;
}
public String getClientId() {
return clientId;
}
public String getOauth2TokenEndpoint() {
return oauth2TokenEndpoint;
}
public String getClientSecret() {
return clientSecret;
}
public String getServiceManagementURL() {
return serviceManagementURL;
}
public int getMaxVirtualMachinesLimit() {
return maxVirtualMachinesLimit;
}
public String getResourceGroupName() {
return resourceGroupName;
}
@ -222,45 +204,60 @@ public class AzureVMCloud extends Cloud {
public int getDeploymentTimeout() {
return deploymentTimeout;
}
public String getAzureCredentialsId() {
return credentialsId;
}
public AzureCredentials.ServicePrincipal getServicePrincipal()
{
if(credentials == null && credentialsId != null)
return AzureCredentials.getServicePrincipal(credentialsId);
return credentials;
}
/**
* Returns the current set of templates.
* Required for config.jelly
* @return
* Returns the current set of templates. Required for config.jelly
*
* @return
*/
public List<AzureVMAgentTemplate> getInstTemplates() {
return Collections.unmodifiableList(instTemplates);
}
/**
* Is the configuration set up and verified?
*
* @return True if the configuration set up and verified, false otherwise.
*/
public boolean isConfigurationValid() {
return configurationValid;
}
/**
* Set the configuration verification status
*
* @param isValid True for verified + valid, false otherwise.
*/
public void setConfigurationValid(boolean isValid) {
configurationValid = isValid;
}
/**
* Retrieves the current approximate virtual machine count
* @return
*
* @return
*/
public int getApproximateVirtualMachineCount() {
synchronized (this) {
return approximateVirtualMachineCount;
}
}
/**
* Given the number of VMs that are desired, returns the number
* of VMs that can be allocated.
* Given the number of VMs that are desired, returns the number of VMs that
* can be allocated.
*
* @param quantityDesired Number that are desired
* @return Number that can be allocated
*/
@ -269,8 +266,7 @@ public class AzureVMCloud extends Cloud {
if (approximateVirtualMachineCount + quantityDesired <= getMaxVirtualMachinesLimit()) {
// Enough available, return the desired quantity
return quantityDesired;
}
else {
} else {
// Not enough available, return what we have. Remember we could
// go negative (if for instance another Jenkins instance had
// a higher limit.
@ -278,9 +274,10 @@ public class AzureVMCloud extends Cloud {
}
}
}
/**
* Adjust the number of currently allocated VMs
*
* @param delta Number to adjust by.
*/
public void adjustVirtualMachineCount(int delta) {
@ -288,18 +285,19 @@ public class AzureVMCloud extends Cloud {
approximateVirtualMachineCount = Math.max(0, approximateVirtualMachineCount + delta);
}
}
/**
* Sets the new approximate virtual machine count. This is run by
* the verification task to update the VM count periodically.
* @param newCount
* Sets the new approximate virtual machine count. This is run by the
* verification task to update the VM count periodically.
*
* @param newCount
*/
public void setVirtualMachineCount(int newCount) {
synchronized (this) {
approximateVirtualMachineCount = newCount;
}
}
/**
* Returns agent template associated with the label.
*
@ -347,12 +345,13 @@ public class AzureVMCloud extends Cloud {
/**
* Once a new deployment is created, construct a new AzureVMAgent object
* given information about the template
*
* @param template Template used to create the new agent
* @param vmName Name of the created VM
* @param deploymentName Name of the deployment containing the VM
* @param config Azure configuration.
* @return New agent. Throws otherwise.
* @throws Exception
* @return New agent. Throws otherwise.
* @throws Exception
*/
private AzureVMAgent createProvisionedAgent(
final AzureVMAgentTemplate template,
@ -360,7 +359,7 @@ public class AzureVMCloud extends Cloud {
final String deploymentName) throws Exception {
LOGGER.log(Level.INFO, "AzureVMCloud: createProvisionedAgent: Waiting for deployment {0} to be completed", deploymentName);
final int sleepTimeInSeconds = 30;
final int timeoutInSeconds = getDeploymentTimeout();
final int maxTries = timeoutInSeconds / sleepTimeInSeconds;
@ -372,15 +371,16 @@ public class AzureVMCloud extends Cloud {
} catch (InterruptedException ex) {
// ignore
}
// Create a new RM client each time because the config may expire while
// in this long running operation
Configuration config = ServiceDelegateHelper.getConfiguration(template);
final ResourceManagementClient rmc = ResourceManagementService.create(config);
final ResourceManagementClient rmc = ResourceManagementService.create(config)
.withRequestFilterFirst(new AzureUserAgentFilter());
final List<DeploymentOperation> ops = rmc.getDeploymentOperationsOperations().
list(resourceGroupName, deploymentName, null).getOperations();
for (DeploymentOperation op : ops) {
final String resource = op.getProperties().getTargetResource().getResourceName();
final String type = op.getProperties().getTargetResource().getResourceType();
@ -398,14 +398,14 @@ public class AzureVMCloud extends Cloud {
finalStatusMessage += " - " + statusMessage;
}
throw new AzureCloudException(String.format("AzureVMCloud: createProvisionedAgent: Deployment %s: %s:%s - %s", new Object[] { state, type, resource, finalStatusMessage }));
throw new AzureCloudException(String.format("AzureVMCloud: createProvisionedAgent: Deployment %s: %s:%s - %s", new Object[]{state, type, resource, finalStatusMessage}));
} else if (ProvisioningState.SUCCEEDED.equals(state)) {
LOGGER.log(Level.INFO, "AzureVMCloud: createProvisionedAgent: VM available: {0}", resource);
final VirtualMachineGetResponse vm
= ServiceDelegateHelper.getComputeManagementClient(config).
getVirtualMachinesOperations().
getWithInstanceView(resourceGroupName, resource);
getVirtualMachinesOperations().
getWithInstanceView(resourceGroupName, resource);
final String osType = vm.getVirtualMachine().getStorageProfile().getOSDisk().
getOperatingSystemType();
@ -414,11 +414,10 @@ public class AzureVMCloud extends Cloud {
// Set the virtual machine details
AzureVMManagementServiceDelegate.setVirtualMachineDetails(newAgent, template);
return newAgent;
}
else {
LOGGER.log(Level.INFO, "AzureVMCloud: createProvisionedAgent: Deployment {0} not yet finished ({1}): {2}:{3} - waited {4} seconds",
new Object[] { deploymentName, state, type, resource,
(maxTries - triesLeft) * sleepTimeInSeconds });
} else {
LOGGER.log(Level.INFO, "AzureVMCloud: createProvisionedAgent: Deployment {0} not yet finished ({1}): {2}:{3} - waited {4} seconds",
new Object[]{deploymentName, state, type, resource,
(maxTries - triesLeft) * sleepTimeInSeconds});
}
}
}
@ -431,7 +430,7 @@ public class AzureVMCloud extends Cloud {
@Override
public Collection<PlannedNode> provision(final Label label, int workLoad) {
LOGGER.log(Level.INFO,
"AzureVMCloud: provision: start for label {0} workLoad {1}", new Object[] { label, workLoad });
"AzureVMCloud: provision: start for label {0} workLoad {1}", new Object[]{label, workLoad});
final AzureVMAgentTemplate template = AzureVMCloud.this.getAzureAgentTemplate(label);
@ -442,7 +441,7 @@ public class AzureVMCloud extends Cloud {
// reuse existing nodes if available
LOGGER.log(Level.INFO, "AzureVMCloud: provision: checking for node reuse options");
for (Computer agentComputer : Jenkins.getInstance().getComputers()) {
if (numberOfAgents == 0) {
if (numberOfAgents == 0) {
break;
}
if (agentComputer instanceof AzureVMComputer && agentComputer.isOffline()) {
@ -498,19 +497,18 @@ public class AzureVMCloud extends Cloud {
int adjustedNumberOfAgents = getAvailableVirtualMachineCount(numberOfAgents);
if (adjustedNumberOfAgents == 0) {
LOGGER.log(Level.INFO, "Not able to create any new nodes, at or above maximum VM count of {0}",
getMaxVirtualMachinesLimit());
getMaxVirtualMachinesLimit());
return plannedNodes;
}
else if (adjustedNumberOfAgents < numberOfAgents) {
} else if (adjustedNumberOfAgents < numberOfAgents) {
LOGGER.log(Level.INFO, "Able to create new nodes, but can only create {0} (desired {1})",
new Object[] { adjustedNumberOfAgents, numberOfAgents } );
new Object[]{adjustedNumberOfAgents, numberOfAgents});
}
final int numberOfNewAgents = adjustedNumberOfAgents;
// Adjust number of nodes available by the number of created nodes.
// Negative to reduce number available.
this.adjustVirtualMachineCount(-adjustedNumberOfAgents);
ExecutorService executorService = Executors.newCachedThreadPool();
Callable<AzureVMDeploymentInfo> callableTask = new Callable<AzureVMDeploymentInfo>() {
@Override
@ -528,31 +526,30 @@ public class AzureVMCloud extends Cloud {
@Override
public Node call() throws Exception {
// Wait for the future to complete
AzureVMDeploymentInfo info = deploymentFuture.get();
final String deploymentName = info.getDeploymentName();
final String vmBaseName = info.getVmBaseName();
final String vmName = String.format("%s%d", vmBaseName, index);
AzureVMAgent agent = null;
try {
agent = createProvisionedAgent(
template,
vmName,
deploymentName);
}
catch (Exception e) {
template,
vmName,
deploymentName);
} catch (Exception e) {
LOGGER.log(
Level.SEVERE,
String.format("Failure creating provisioned agent '%s'", vmName),
e);
Level.SEVERE,
String.format("Failure creating provisioned agent '%s'", vmName),
e);
// Attempt to terminate whatever was created
AzureVMManagementServiceDelegate.terminateVirtualMachine(
ServiceDelegateHelper.getConfiguration(template), vmName,
template.getResourceGroupName());
ServiceDelegateHelper.getConfiguration(template), vmName,
template.getResourceGroupName());
template.getAzureCloud().adjustVirtualMachineCount(1);
// Update the template status given this new issue.
template.handleTemplateProvisioningFailure(e.getMessage(), FailureStage.PROVISIONING);
@ -575,19 +572,19 @@ public class AzureVMCloud extends Cloud {
agent.clearCleanUpAction();
} catch (Exception e) {
LOGGER.log(
Level.SEVERE,
String.format("Failure to in post-provisioning for '%s'", vmName),
e);
Level.SEVERE,
String.format("Failure to in post-provisioning for '%s'", vmName),
e);
// Attempt to terminate whatever was created
AzureVMManagementServiceDelegate.terminateVirtualMachine(
ServiceDelegateHelper.getConfiguration(template), vmName,
template.getResourceGroupName());
ServiceDelegateHelper.getConfiguration(template), vmName,
template.getResourceGroupName());
template.getAzureCloud().adjustVirtualMachineCount(1);
// Update the template status
template.handleTemplateProvisioningFailure(vmName, FailureStage.POSTPROVISIONING);
// Remove the node from jenkins
Jenkins.getInstance().removeNode(agent);
throw e;
@ -611,9 +608,12 @@ public class AzureVMCloud extends Cloud {
}
/**
* Wait till a node that connects through JNLP comes online and connects to Jenkins.
* Wait till a node that connects through JNLP comes online and connects to
* Jenkins.
*
* @param agent Node to wait for
* @throws Exception Throws if the wait time expires or other exception happens.
* @throws Exception Throws if the wait time expires or other exception
* happens.
*/
private void waitUntilJNLPNodeIsOnline(final AzureVMAgent agent) throws Exception {
LOGGER.log(Level.INFO, "Azure Cloud: waitUntilOnline: for agent {0}", agent.getDisplayName());
@ -680,11 +680,11 @@ public class AzureVMCloud extends Cloud {
public int getDefaultMaxVMLimit() {
return Constants.DEFAULT_MAX_VM_LIMIT;
}
public int getDefaultDeploymentTimeout() {
return Constants.DEFAULT_DEPLOYMENT_TIMEOUT_SEC;
}
public String getDefaultResourceGroupName() {
return Constants.DEFAULT_RESOURCE_GROUP_NAME;
}
@ -697,51 +697,25 @@ public class AzureVMCloud extends Cloud {
@QueryParameter String serviceManagementURL,
@QueryParameter String maxVirtualMachinesLimit,
@QueryParameter String deploymentTimeout,
@QueryParameter String azureCredentialsId,
@QueryParameter String resourceGroupName) {
if (StringUtils.isBlank(subscriptionId)) {
return FormValidation.error("Error: Subscription ID is missing");
}
if (StringUtils.isBlank(clientId)) {
return FormValidation.error("Error: Native Client ID is missing");
}
if (StringUtils.isBlank(clientSecret)) {
return FormValidation.error("Error: Azure Password is missing");
}
if (StringUtils.isBlank(oauth2TokenEndpoint)) {
return FormValidation.error("Error: OAuth 2.0 Token Endpoint is missing");
}
if (StringUtils.isBlank(serviceManagementURL)) {
serviceManagementURL = Constants.DEFAULT_MANAGEMENT_URL;
}
if (StringUtils.isBlank(resourceGroupName)) {
resourceGroupName = Constants.DEFAULT_RESOURCE_GROUP_NAME;
}
if (StringUtils.isBlank(maxVirtualMachinesLimit) || !maxVirtualMachinesLimit.matches(Constants.REG_EX_DIGIT)) {
maxVirtualMachinesLimit = Integer.toString(Constants.DEFAULT_MAX_VM_LIMIT);
} else {
maxVirtualMachinesLimit = maxVirtualMachinesLimit;
}
if (StringUtils.isBlank(deploymentTimeout) || !deploymentTimeout.matches(Constants.REG_EX_DIGIT)) {
deploymentTimeout = Integer.toString(Constants.DEFAULT_DEPLOYMENT_TIMEOUT_SEC);
} else {
deploymentTimeout = deploymentTimeout;
AzureCredentials.ServicePrincipal credentials = AzureCredentials.getServicePrincipal(azureCredentialsId);
try {
credentials.Validate(resourceGroupName, maxVirtualMachinesLimit, deploymentTimeout);
} catch (AzureCredentialsValidationException e) {
return FormValidation.error(e.getMessage());
}
return FormValidation.ok(Messages.Azure_Config_Success());
}
String response = AzureVMManagementServiceDelegate.verifyConfiguration(
subscriptionId,
clientId,
clientSecret,
oauth2TokenEndpoint,
serviceManagementURL,
resourceGroupName);
if (Constants.OP_SUCCESS.equalsIgnoreCase(response)) {
return FormValidation.ok(Messages.Azure_Config_Success());
} else {
return FormValidation.error(response);
}
public ListBoxModel doFillAzureCredentialsIdItems(@AncestorInPath Item owner) {
return new StandardListBoxModel().withAll(CredentialsProvider.lookupCredentials(AzureCredentials.class, owner, ACL.SYSTEM, Collections.<DomainRequirement>emptyList()));
}
}
}

View File

@ -15,6 +15,7 @@
*/
package com.microsoft.azure;
import com.microsoft.azure.util.AzureCredentials;
import com.microsoft.windowsazure.Configuration;
import com.microsoft.azure.util.AzureUtil;
import java.io.IOException;
@ -32,6 +33,7 @@ import java.util.List;
import java.util.Set;
import java.util.logging.Level;
import jenkins.model.Jenkins;
import org.apache.commons.lang.StringUtils;
/**
* Performs a few types of verification: 1. Overall subscription verification.
@ -165,8 +167,9 @@ public final class AzureVMCloudVerificationTask extends AsyncPeriodicWork {
agentTemplate.setTemplateVerified(true);
// Reset the status details
agentTemplate.setTemplateStatusDetails("");
} else {
String details = String.join("\n", errors);
}
else {
String details = StringUtils.join(errors, "\n");
LOGGER.log(Level.INFO, "AzureVMCloudVerificationTask: execute: {0} could not be verified:\n{1}",
new Object[]{templateName, details});
// Set the status details to the set of messages
@ -200,8 +203,7 @@ public final class AzureVMCloudVerificationTask extends AsyncPeriodicWork {
LOGGER.info("AzureVMCloudVerificationTask: verifyConfiguration: start");
// Check the sub and off we go
String result = AzureVMManagementServiceDelegate.verifyConfiguration(cloud.getSubscriptionId(),
cloud.getClientId(), cloud.getClientSecret(), cloud.getOauth2TokenEndpoint(), cloud.getServiceManagementURL(), cloud.getResourceGroupName());
String result = AzureVMManagementServiceDelegate.verifyConfiguration(cloud.getServicePrincipal(), cloud.getResourceGroupName());
if (result != Constants.OP_SUCCESS) {
LOGGER.log(Level.INFO, "AzureVMCloudVerificationTask: verifyConfiguration: {0}", result);
cloud.setConfigurationValid(false);
@ -261,7 +263,10 @@ public final class AzureVMCloudVerificationTask extends AsyncPeriodicWork {
* @param template Template to register
*/
private static void registerTemplateHelper(final AzureVMAgentTemplate template) {
String cloudName = AzureUtil.getCloudName(template.getAzureCloud().getSubscriptionId());
String cloudName = "<unknown>";
AzureCredentials.ServicePrincipal sp = template.getAzureCloud().getServicePrincipal();
if(sp != null)
cloudName = AzureUtil.getCloudName(sp.subscriptionId.getPlainText());
LOGGER.log(Level.INFO, "AzureVMCloudVerificationTask: registerTemplateHelper: Registering template {0} on {1} for verification",
new Object[]{template.getTemplateName(), cloudName});
if (cloudTemplates == null) {

View File

@ -15,6 +15,10 @@
*/
package com.microsoft.azure;
import com.cloudbees.plugins.credentials.CredentialsMatchers;
import com.cloudbees.plugins.credentials.CredentialsProvider;
import com.cloudbees.plugins.credentials.common.StandardUsernamePasswordCredentials;
import com.cloudbees.plugins.credentials.domains.DomainRequirement;
import com.fasterxml.jackson.databind.JsonNode;
import hudson.model.Descriptor.FormException;
import com.fasterxml.jackson.databind.ObjectMapper;
@ -71,14 +75,18 @@ import com.microsoft.azure.exceptions.AzureCloudException;
import com.microsoft.azure.exceptions.UnrecoverableCloudException;
import com.microsoft.azure.retry.ExponentialRetryStrategy;
import com.microsoft.azure.retry.NoRetryStrategy;
import com.microsoft.azure.util.AzureCredentials;
import com.microsoft.azure.util.AzureUserAgentFilter;
import com.microsoft.azure.util.AzureUtil;
import com.microsoft.azure.util.CleanUpAction;
import com.microsoft.azure.util.Constants;
import com.microsoft.azure.util.ExecutionEngine;
import com.microsoft.azure.util.FailureStage;
import hudson.security.ACL;
import java.io.InputStream;
import java.net.URI;
import java.util.Arrays;
import java.util.Collections;
import java.util.Date;
import java.util.Iterator;
import java.util.logging.Level;
@ -247,8 +255,11 @@ public class AzureVMManagementServiceDelegate {
}
ObjectNode.class.cast(tmp.get("variables")).put("vmSize", template.getVirtualMachineSize());
ObjectNode.class.cast(tmp.get("variables")).put("adminUsername", template.getAdminUserName());
ObjectNode.class.cast(tmp.get("variables")).put("adminPassword", template.getAdminPassword());
// Grab the username/pass
StandardUsernamePasswordCredentials creds = AzureUtil.getCredentials(template.getCredentialsId());
ObjectNode.class.cast(tmp.get("variables")).put("adminUsername", creds.getUsername());
ObjectNode.class.cast(tmp.get("variables")).put("adminPassword", creds.getPassword().getPlainText());
if (StringUtils.isNotBlank(template.getStorageAccountName())) {
ObjectNode.class.cast(tmp.get("variables")).put("storageAccountName", template.getStorageAccountName());
@ -355,16 +366,18 @@ public class AzureVMManagementServiceDelegate {
final String ipRef = vm.getVirtualMachine().getNetworkProfile().getNetworkInterfaces().get(0).
getReferenceUri();
final NetworkInterface netIF = NetworkResourceProviderService.create(config).
getNetworkInterfacesOperations().get(
final NetworkInterface netIF = NetworkResourceProviderService.create(config)
.withRequestFilterFirst(new AzureUserAgentFilter())
.getNetworkInterfacesOperations().get(
template.getResourceGroupName(),
ipRef.substring(ipRef.lastIndexOf("/") + 1, ipRef.length())).
getNetworkInterface();
final String nicRef = netIF.getIpConfigurations().get(0).getPublicIpAddress().getId();
final PublicIpAddress pubIP = NetworkResourceProviderService.create(config).
getPublicIpAddressesOperations().get(
final PublicIpAddress pubIP = NetworkResourceProviderService.create(config)
.withRequestFilterFirst(new AzureUserAgentFilter())
.getPublicIpAddressesOperations().get(
template.getResourceGroupName(),
nicRef.substring(nicRef.lastIndexOf("/") + 1, nicRef.length())).
getPublicIpAddress();
@ -373,9 +386,9 @@ public class AzureVMManagementServiceDelegate {
azureAgent.setPublicDNSName(pubIP.getDnsSettings().getFqdn());
azureAgent.setSshPort(Constants.DEFAULT_SSH_PORT);
LOGGER.log(Level.INFO, "Azure agent details:\nnodeName{0}\nadminUserName={1}\nshutdownOnIdle={2}\nretentionTimeInMin={3}\nlabels={4}",
new Object[]{azureAgent.getNodeName(), azureAgent.getAdminUserName(), azureAgent.isShutdownOnIdle(),
azureAgent.getRetentionTimeInMin(), azureAgent.getLabelString()});
LOGGER.log(Level.INFO, "Azure agent details:\nnodeName{0}\nadminUserName={1}\nshutdownOnIdle={2}\nretentionTimeInMin={3}\nlabels={4}",
new Object[] { azureAgent.getNodeName(), azureAgent.getVMCredentialsId(), azureAgent.isShutdownOnIdle(),
azureAgent.getRetentionTimeInMin(), azureAgent.getLabelString()});
}
/**
@ -459,21 +472,17 @@ public class AzureVMManagementServiceDelegate {
template.getUseAgentAlwaysIfAvail(),
template.getLabels(),
template.getAzureCloud().getDisplayName(),
template.getAdminUserName(),
template.getCredentialsId(),
null,
null,
template.getAdminPassword(),
template.getJvmOptions(),
template.isShutdownOnIdle(),
false,
deploymentName,
template.getRetentionTimeInMin(),
template.getInitScript(),
azureCloud.getSubscriptionId(),
azureCloud.getClientId(),
azureCloud.getClientSecret(),
azureCloud.getOauth2TokenEndpoint(),
azureCloud.getServiceManagementURL(),
azureCloud.getAzureCredentialsId(),
azureCloud.getServicePrincipal(),
template.getAgentLaunchMethod(),
CleanUpAction.DEFAULT,
null,
@ -491,7 +500,8 @@ public class AzureVMManagementServiceDelegate {
public static List<String> getStorageAccountsInfo(final Configuration config) throws Exception {
List<String> storageAccounts = new ArrayList<String>();
StorageManagementClient client = StorageManagementService.create(config);
StorageManagementClient client = StorageManagementService.create(config)
.withRequestFilterFirst(new AzureUserAgentFilter());
StorageAccountListResponse response = client.getStorageAccountsOperations().list();
for (StorageAccount sa : response.getStorageAccounts()) {
@ -602,36 +612,22 @@ public class AzureVMManagementServiceDelegate {
/**
* Validates certificate configuration.
*
* @param subscriptionId
* @param clientId
* @param oauth2TokenEndpoint
* @param clientSecret
* @param serviceManagementURL
* @param servicePrincipal
* @param resourceGroupName
* @return
*/
public static String verifyConfiguration(
final String subscriptionId,
final String clientId,
final String clientSecret,
final String oauth2TokenEndpoint,
final String serviceManagementURL,
final AzureCredentials.ServicePrincipal servicePrincipal,
final String resourceGroupName) {
if (StringUtils.isBlank(subscriptionId)
|| StringUtils.isBlank(clientId)
|| StringUtils.isBlank(oauth2TokenEndpoint)
|| StringUtils.isBlank(clientSecret)
|| StringUtils.isBlank(resourceGroupName)) {
if (servicePrincipal.isBlank() || StringUtils.isBlank(resourceGroupName)) {
return Messages.Azure_GC_Template_Val_Profile_Missing();
} else {
try {
// Load up the configuration now and do a live verification
Configuration config = ServiceDelegateHelper.loadConfiguration(
subscriptionId, clientId, clientSecret, oauth2TokenEndpoint, serviceManagementURL);
if (!resourceGroupName.matches(Constants.DEFAULT_RESOURCE_GROUP_PATTERN)) {
return Messages.Azure_GC_Template_ResourceGroupName_Err();
}
// Load up the configuration now and do a live verification
Configuration config = ServiceDelegateHelper.loadConfiguration(servicePrincipal);
if (!verifyConfiguration(config, resourceGroupName).equals(Constants.OP_SUCCESS)) {
return Messages.Azure_GC_Template_Val_Profile_Err();
@ -993,11 +989,7 @@ public class AzureVMManagementServiceDelegate {
/**
* Verifies template configuration by making server calls if needed.
*
* @param subscriptionId
* @param clientId
* @param clientSecret
* @param oauth2TokenEndpoint
* @param serviceManagementURL
* @param servicePrincipal
* @param templateName
* @param labels
* @param location
@ -1012,23 +1004,17 @@ public class AzureVMManagementServiceDelegate {
* @param imageVersion
* @param agentLaunchMethod
* @param initScript
* @param adminUserName
* @param adminPassword
* @param credentialsId
* @param virtualNetworkName
* @param subnetName
* @param retentionTimeInMin
* @param templateStatus
* @param jvmOptions
* @param returnOnSingleError
* @param resourceGroupName
* @return
*/
public static List<String> verifyTemplate(
final String subscriptionId,
final String clientId,
final String clientSecret,
final String oauth2TokenEndpoint,
final String serviceManagementURL,
final AzureCredentials.ServicePrincipal servicePrincipal,
final String templateName,
final String labels,
final String location,
@ -1043,8 +1029,7 @@ public class AzureVMManagementServiceDelegate {
final String imageVersion,
final String agentLaunchMethod,
final String initScript,
final String adminUserName,
final String adminPassword,
final String credentialsId,
final String virtualNetworkName,
final String subnetName,
final String retentionTimeInMin,
@ -1057,8 +1042,7 @@ public class AzureVMManagementServiceDelegate {
// Load configuration
try {
config = ServiceDelegateHelper.loadConfiguration(
subscriptionId, clientId, clientSecret, oauth2TokenEndpoint, serviceManagementURL);
config = ServiceDelegateHelper.loadConfiguration(servicePrincipal);
String validationResult;
// Verify basic info about the template
@ -1076,6 +1060,14 @@ public class AzureVMManagementServiceDelegate {
}
//verify password
String adminPassword="";
try {
StandardUsernamePasswordCredentials creds = AzureUtil.getCredentials(credentialsId);
adminPassword = creds.getPassword().getPlainText();
} catch(AzureCloudException e) {
LOGGER.log(Level.SEVERE, "Could not load the VM credentials", e);
}
validationResult = verifyAdminPassword(adminPassword);
addValidationResultIfFailed(validationResult, errors);
if (returnOnSingleError && errors.size() > 0) {
@ -1094,8 +1086,8 @@ public class AzureVMManagementServiceDelegate {
if (returnOnSingleError && errors.size() > 0) {
return errors;
}
validationResult = verifyLocation(location, serviceManagementURL);
validationResult = verifyLocation(location, servicePrincipal.serviceManagementURL);
addValidationResultIfFailed(validationResult, errors);
if (returnOnSingleError && errors.size() > 0) {
return errors;

View File

@ -32,6 +32,8 @@ import com.microsoft.windowsazure.management.ManagementClient;
import com.microsoft.windowsazure.management.ManagementService;
import com.microsoft.azure.exceptions.AzureCloudException;
import com.microsoft.azure.exceptions.UnrecoverableCloudException;
import com.microsoft.azure.util.AzureCredentials;
import com.microsoft.azure.util.AzureUserAgentFilter;
import com.microsoft.azure.util.TokenCache;
import hudson.slaves.Cloud;
import java.util.logging.Level;
@ -50,12 +52,7 @@ public class ServiceDelegateHelper {
public static Configuration getConfiguration(final AzureVMCloud cloud) throws AzureCloudException {
try {
return loadConfiguration(
cloud.getSubscriptionId(),
cloud.getClientId(),
cloud.getClientSecret(),
cloud.getOauth2TokenEndpoint(),
cloud.getServiceManagementURL());
return loadConfiguration(cloud.getServicePrincipal());
} catch (AzureCloudException e) {
LOGGER.log(Level.SEVERE,
"AzureVMManagementServiceDelegate: getConfiguration: Failure loading configuration", e);
@ -65,12 +62,7 @@ public class ServiceDelegateHelper {
public static Configuration getConfiguration(final AzureVMAgent agent) throws AzureCloudException {
try {
return loadConfiguration(
agent.getSubscriptionId(),
agent.getClientId(),
agent.getClientSecret(),
agent.getOauth2TokenEndpoint(),
agent.getManagementURL());
return loadConfiguration(agent.getServicePrincipal());
} catch (AzureCloudException e) {
// let's assume no updated information into the agent instance
LOGGER.log(Level.INFO, "Missing connection with agent {0}", agent.getNodeName());
@ -97,31 +89,18 @@ public class ServiceDelegateHelper {
public static Configuration getConfiguration(final AzureVMAgentTemplate template) throws AzureCloudException {
final AzureVMCloud azureCloud = template.getAzureCloud();
return loadConfiguration(
azureCloud.getSubscriptionId(),
azureCloud.getClientId(),
azureCloud.getClientSecret(),
azureCloud.getOauth2TokenEndpoint(),
azureCloud.getServiceManagementURL());
return loadConfiguration(azureCloud.getServicePrincipal());
}
/**
* Loads configuration object..
*
* @param subscriptionId
* @param clientId
* @param clientSecret
* @param oauth2TokenEndpoint
* @param serviceManagementURL
* @param servicePrincipal
* @return
* @throws AzureCloudException
*/
public static Configuration loadConfiguration(
final String subscriptionId,
final String clientId,
final String clientSecret,
final String oauth2TokenEndpoint,
final String serviceManagementURL)
final AzureCredentials.ServicePrincipal servicePrincipal)
throws AzureCloudException {
// Azure libraries are internally using ServiceLoader.load(...) method which uses context classloader and
@ -131,9 +110,7 @@ public class ServiceDelegateHelper {
Thread.currentThread().setContextClassLoader(AzureVMManagementServiceDelegate.class.getClassLoader());
try {
final Configuration config = TokenCache.getInstance(
subscriptionId, clientId, clientSecret, oauth2TokenEndpoint, serviceManagementURL).get().
getConfiguration();
final Configuration config = TokenCache.getInstance(servicePrincipal).get().getConfiguration();
return config;
} finally {
@ -152,7 +129,8 @@ public class ServiceDelegateHelper {
Thread.currentThread().setContextClassLoader(AzureVMManagementServiceDelegate.class.getClassLoader());
try {
return ResourceManagementService.create(config);
return ResourceManagementService.create(config)
.withRequestFilterFirst(new AzureUserAgentFilter());
} finally {
Thread.currentThread().setContextClassLoader(thread);
}
@ -169,7 +147,8 @@ public class ServiceDelegateHelper {
Thread.currentThread().setContextClassLoader(AzureVMManagementServiceDelegate.class.getClassLoader());
try {
return ComputeManagementService.create(config);
return ComputeManagementService.create(config)
.withRequestFilterFirst(new AzureUserAgentFilter());
} finally {
Thread.currentThread().setContextClassLoader(thread);
}
@ -187,7 +166,8 @@ public class ServiceDelegateHelper {
Thread.currentThread().setContextClassLoader(AzureVMManagementServiceDelegate.class.getClassLoader());
try {
return StorageManagementService.create(config);
return StorageManagementService.create(config)
.withRequestFilterFirst(new AzureUserAgentFilter());
} finally {
Thread.currentThread().setContextClassLoader(thread);
}
@ -199,7 +179,8 @@ public class ServiceDelegateHelper {
Thread.currentThread().setContextClassLoader(AzureVMManagementServiceDelegate.class.getClassLoader());
try {
return ManagementService.create(config);
return ManagementService.create(config)
.withRequestFilterFirst(new AzureUserAgentFilter());
} finally {
Thread.currentThread().setContextClassLoader(thread);
}
@ -211,7 +192,8 @@ public class ServiceDelegateHelper {
Thread.currentThread().setContextClassLoader(AzureVMManagementServiceDelegate.class.getClassLoader());
try {
return NetworkResourceProviderService.create(config);
return NetworkResourceProviderService.create(config)
.withRequestFilterFirst(new AzureUserAgentFilter());
} finally {
Thread.currentThread().setContextClassLoader(thread);
}

View File

@ -42,6 +42,7 @@ import java.util.logging.Logger;
import com.microsoft.windowsazure.Configuration;
import com.microsoft.azure.exceptions.AzureCloudException;
import com.microsoft.azure.util.AzureUserAgentFilter;
import com.microsoft.azure.util.Constants;
import java.util.ArrayList;
import java.util.List;
@ -69,7 +70,8 @@ public class StorageServiceDelegate {
private static List<URI> getStorageAccountURIs(final Configuration config, final String storageAccountName,
final String resourceGroupName) throws
AzureCloudException {
StorageManagementClient client = StorageManagementService.create(config);
StorageManagementClient client = StorageManagementService.create(config)
.withRequestFilterFirst(new AzureUserAgentFilter());
StorageAccountGetPropertiesResponse response;
try {
@ -119,7 +121,8 @@ public class StorageServiceDelegate {
public static StorageAccountGetPropertiesResponse getStorageAccountProps(
final Configuration config, final String storageAccountName, final String resourceGroupName)
throws AzureCloudException {
StorageManagementClient client = StorageManagementService.create(config);
StorageManagementClient client = StorageManagementService.create(config)
.withRequestFilterFirst(new AzureUserAgentFilter());
StorageAccountGetPropertiesResponse response = null;
try {
response = client.getStorageAccountsOperations().getProperties(

View File

@ -0,0 +1,40 @@
/*
* Copyright 2016 clguiman.
/*
Copyright 2016 Microsoft, Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package com.microsoft.azure.exceptions;
public class AzureCredentialsValidationException extends Exception {
public AzureCredentialsValidationException(final String message) {
super(message);
}
public AzureCredentialsValidationException() {
super();
}
public AzureCredentialsValidationException(final String msg, final Exception excep) {
super(msg, excep);
}
public AzureCredentialsValidationException(final Exception excep) {
super(excep);
}
private static final long serialVersionUID = -8157417759456046943L;
}

View File

@ -15,6 +15,10 @@
*/
package com.microsoft.azure.remote;
import com.cloudbees.plugins.credentials.CredentialsMatchers;
import com.cloudbees.plugins.credentials.CredentialsProvider;
import com.cloudbees.plugins.credentials.common.StandardUsernamePasswordCredentials;
import com.cloudbees.plugins.credentials.domains.DomainRequirement;
import com.jcraft.jsch.ChannelExec;
import com.jcraft.jsch.ChannelSftp;
import com.jcraft.jsch.JSch;
@ -25,6 +29,8 @@ import com.microsoft.azure.AzureVMAgent;
import com.microsoft.azure.AzureVMComputer;
import com.microsoft.azure.AzureVMAgentTemplate;
import com.microsoft.azure.Messages;
import com.microsoft.azure.exceptions.AzureCloudException;
import com.microsoft.azure.util.AzureUtil;
import com.microsoft.azure.util.CleanUpAction;
import com.microsoft.azure.util.Constants;
import com.microsoft.azure.util.FailureStage;
@ -33,6 +39,7 @@ import hudson.model.Descriptor;
import hudson.model.TaskListener;
import hudson.remoting.Channel;
import hudson.remoting.Channel.Listener;
import hudson.security.ACL;
import hudson.slaves.ComputerLauncher;
import hudson.slaves.OfflineCause;
import hudson.slaves.SlaveComputer;
@ -44,6 +51,7 @@ import java.io.OutputStream;
import java.io.PrintStream;
import java.net.ConnectException;
import java.net.UnknownHostException;
import java.util.Collections;
import java.util.logging.Level;
import java.util.logging.Logger;
import jenkins.model.Jenkins;
@ -134,8 +142,11 @@ public class AzureVMAgentSSHLauncher extends ComputerLauncher {
// Execute initialization script
// Make sure to change file permission for execute if needed. TODO: need to test
// Grab the username/pass
StandardUsernamePasswordCredentials creds = AzureUtil.getCredentials(agent.getVMCredentialsId());
String command = "sh " + remoteInitFileName;
int exitStatus = executeRemoteCommand(session, command, logger, agent.getExecuteInitScriptAsRoot(), agent.getAdminPassword());
int exitStatus = executeRemoteCommand(session, command, logger, agent.getExecuteInitScriptAsRoot(), creds.getPassword().getPlainText());
if (exitStatus != 0) {
if (agent.getDoNotUseMachineIfInitFails()) {
LOGGER.log(Level.SEVERE, "AzureVMAgentSSHLauncher: launch: init script failed: exit code={0} (marking agent for deletion)", exitStatus);
@ -234,8 +245,6 @@ public class AzureVMAgentSSHLauncher extends ComputerLauncher {
new Object[] { dnsName, sshPort, e.getMessage() });
throw e;
}
}
private void copyFileToRemote(Session jschSession, InputStream stream, String remotePath) throws Exception {
@ -374,7 +383,10 @@ public class AzureVMAgentSSHLauncher extends ComputerLauncher {
while (true) {
currRetryCount++;
try {
session = getRemoteSession(agent.getAdminUserName(), agent.getAdminPassword(), agent.getPublicDNSName(),
// Grab the username/pass
StandardUsernamePasswordCredentials creds = AzureUtil.getCredentials(agent.getVMCredentialsId());
session = getRemoteSession(creds.getUsername(), creds.getPassword().getPlainText(), agent.getPublicDNSName(),
agent.getSshPort());
LOGGER.info("AzureVMAgentSSHLauncher: connectToSsh: Got remote connection");
} catch (Exception e) {

View File

@ -19,6 +19,7 @@ import com.microsoft.aad.adal4j.AuthenticationResult;
import com.microsoft.windowsazure.Configuration;
import com.microsoft.windowsazure.management.configuration.ManagementConfiguration;
import com.microsoft.azure.exceptions.AzureCloudException;
import hudson.util.Secret;
import java.io.IOException;
import java.io.Serializable;
import java.net.URI;
@ -27,21 +28,21 @@ import java.util.Date;
public class AccessToken implements Serializable {
private static final long serialVersionUID = 1L;
private static final long serialVersionUID = 2L;
private final String subscriptionId;
private final Secret subscriptionId;
private final String serviceManagementUrl;
private final String token;
private final Secret token;
private final long expiration;
AccessToken(
final String subscriptionId, final String serviceManagementUrl, final AuthenticationResult authres) {
this.subscriptionId = subscriptionId;
this.subscriptionId = Secret.fromString(subscriptionId);
this.serviceManagementUrl = serviceManagementUrl;
this.token = authres.getAccessToken();
this.token = Secret.fromString(authres.getAccessToken());
// In the 0.9.4 version of the SDK, expiresOn is the number of seconds till expire
this.expiration = System.currentTimeMillis() + authres.getExpiresOn() * 1000;
}
@ -51,8 +52,8 @@ public class AccessToken implements Serializable {
return ManagementConfiguration.configure(
null,
new URI(serviceManagementUrl),
subscriptionId,
token);
subscriptionId.getPlainText(),
token.getPlainText());
} catch (URISyntaxException e) {
throw new AzureCloudException("The syntax of the Url in the publish settings file is incorrect.", e);
} catch (IOException e) {
@ -68,12 +69,8 @@ public class AccessToken implements Serializable {
return expiration < System.currentTimeMillis();
}
public String getToken() {
return token;
}
@Override
public String toString() {
return token;
return token.getPlainText();
}
}

View File

@ -0,0 +1,192 @@
/*
Copyright 2016 Microsoft, Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package com.microsoft.azure.util;
import com.cloudbees.plugins.credentials.CredentialsMatchers;
import com.cloudbees.plugins.credentials.CredentialsProvider;
import com.cloudbees.plugins.credentials.CredentialsScope;
import com.cloudbees.plugins.credentials.domains.DomainRequirement;
import com.cloudbees.plugins.credentials.impl.BaseStandardCredentials;
import com.microsoft.azure.AzureVMManagementServiceDelegate;
import com.microsoft.azure.Messages;
import com.microsoft.azure.exceptions.AzureCredentialsValidationException;
import hudson.Extension;
import hudson.security.ACL;
import hudson.util.FormValidation;
import hudson.util.Secret;
import java.util.Collections;
import jenkins.model.Jenkins;
import org.apache.commons.lang.StringUtils;
import org.kohsuke.stapler.DataBoundConstructor;
import org.kohsuke.stapler.QueryParameter;
/**
*
* @author clguiman
*/
public class AzureCredentials extends BaseStandardCredentials {
public static class ServicePrincipal {
public final Secret subscriptionId;
public final Secret clientId;
public final Secret clientSecret;
public final Secret oauth2TokenEndpoint;
public final String serviceManagementURL;
public ServicePrincipal(
String subscriptionId,
String clientId,
String clientSecret,
String oauth2TokenEndpoint,
String serviceManagementURL) {
this.subscriptionId = Secret.fromString(subscriptionId);
this.clientId = Secret.fromString(clientId);
this.clientSecret = Secret.fromString(clientSecret);
this.oauth2TokenEndpoint = Secret.fromString(oauth2TokenEndpoint);
this.serviceManagementURL = StringUtils.isBlank(serviceManagementURL)
? Constants.DEFAULT_MANAGEMENT_URL
: serviceManagementURL;
}
public ServicePrincipal() {
this.subscriptionId = Secret.fromString("");
this.clientId = Secret.fromString("");
this.clientSecret = Secret.fromString("");
this.oauth2TokenEndpoint = Secret.fromString("");
this.serviceManagementURL = Constants.DEFAULT_MANAGEMENT_URL;
}
public boolean isBlank() {
return StringUtils.isBlank(subscriptionId.getPlainText())
|| StringUtils.isBlank(clientId.getPlainText())
|| StringUtils.isBlank(oauth2TokenEndpoint.getPlainText())
|| StringUtils.isBlank(clientSecret.getPlainText());
}
public void Validate(String resourceGroupName, String maxVMLimit, String deploymentTimeout) throws AzureCredentialsValidationException {
if (StringUtils.isBlank(subscriptionId.getPlainText())) {
throw new AzureCredentialsValidationException("Error: Subscription ID is missing");
}
if (StringUtils.isBlank(clientId.getPlainText())) {
throw new AzureCredentialsValidationException("Error: Native Client ID is missing");
}
if (StringUtils.isBlank(clientSecret.getPlainText())) {
throw new AzureCredentialsValidationException("Error: Azure Password is missing");
}
if (StringUtils.isBlank(oauth2TokenEndpoint.getPlainText())) {
throw new AzureCredentialsValidationException("Error: OAuth 2.0 Token Endpoint is missing");
}
if (StringUtils.isBlank(maxVMLimit) || !maxVMLimit.matches(Constants.REG_EX_DIGIT)) {
throw new AzureCredentialsValidationException("Error: Maximum Virtual Machine Limit should be a positive integer e.g. "+Constants.DEFAULT_MAX_VM_LIMIT);
}
if (StringUtils.isBlank(deploymentTimeout) || !deploymentTimeout.matches(Constants.REG_EX_DIGIT)) {
throw new AzureCredentialsValidationException("Error: Deployment Timeout should be a positive number");
}
if (deploymentTimeout.matches(Constants.REG_EX_DIGIT) && Integer.parseInt(deploymentTimeout) < Constants.DEFAULT_DEPLOYMENT_TIMEOUT_SEC) {
throw new AzureCredentialsValidationException("Error: Deployment Timeout should be at least minimum "+Constants.DEFAULT_DEPLOYMENT_TIMEOUT_SEC);
}
String response = AzureVMManagementServiceDelegate.verifyConfiguration(this, resourceGroupName);
if (!Constants.OP_SUCCESS.equalsIgnoreCase(response)) {
throw new AzureCredentialsValidationException(response);
}
}
}
public final ServicePrincipal data;
@DataBoundConstructor
public AzureCredentials(
CredentialsScope scope,
String id,
String description,
String subscriptionId,
String clientId,
String clientSecret,
String oauth2TokenEndpoint,
String serviceManagementURL) {
super(scope, id, description);
data = new ServicePrincipal(subscriptionId, clientId, clientSecret, oauth2TokenEndpoint, serviceManagementURL);
}
public static AzureCredentials.ServicePrincipal getServicePrincipal(final String credentialsId) {
AzureCredentials creds = CredentialsMatchers.firstOrNull(
CredentialsProvider.lookupCredentials(AzureCredentials.class, Jenkins.getInstance(), ACL.SYSTEM, Collections.<DomainRequirement>emptyList()),
CredentialsMatchers.withId(credentialsId));
if (creds == null) {
return new AzureCredentials.ServicePrincipal();
}
return creds.data;
}
public String getSubscriptionId() {
return data.subscriptionId.getEncryptedValue();
}
public String getClientId() {
return data.clientId.getEncryptedValue();
}
public String getClientSecret() {
return data.clientSecret.getEncryptedValue();
}
public String getOauth2TokenEndpoint() {
return data.oauth2TokenEndpoint.getEncryptedValue();
}
public String getServiceManagementURL() {
return data.serviceManagementURL;
}
@Extension
public static class DescriptorImpl extends BaseStandardCredentialsDescriptor {
@Override
public String getDisplayName() {
return "Microsoft Azure VM Agents";
}
public String getDefaultserviceManagementURL() {
return Constants.DEFAULT_MANAGEMENT_URL;
}
public FormValidation doVerifyConfiguration(
@QueryParameter String subscriptionId,
@QueryParameter String clientId,
@QueryParameter String clientSecret,
@QueryParameter String oauth2TokenEndpoint,
@QueryParameter String serviceManagementURL) {
AzureCredentials.ServicePrincipal servicePrincipal = new AzureCredentials.ServicePrincipal(subscriptionId, clientId, clientSecret, oauth2TokenEndpoint,
serviceManagementURL);
try {
servicePrincipal.Validate(Constants.DEFAULT_RESOURCE_GROUP_NAME, Integer.toString(Constants.DEFAULT_MAX_VM_LIMIT),
Integer.toString(Constants.DEFAULT_DEPLOYMENT_TIMEOUT_SEC));
} catch (AzureCredentialsValidationException e) {
return FormValidation.error(e.getMessage());
}
return FormValidation.ok(Messages.Azure_Config_Success());
}
}
}

View File

@ -0,0 +1,45 @@
/*
Copyright 2016 Microsoft, Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package com.microsoft.azure.util;
import com.microsoft.windowsazure.core.pipeline.filter.ServiceRequestContext;
import com.microsoft.windowsazure.core.pipeline.filter.ServiceRequestFilter;
public class AzureUserAgentFilter implements ServiceRequestFilter {
private static String PLUGIN_NAME = "AzureJenkinsVMAgent";
public void filter(ServiceRequestContext request) {
String version = null;
try {
version = getClass().getPackage().getImplementationVersion();
} catch (Exception e) {
}
if(version == null) {
version = "local";
}
String userAgent;
if (request.getHeader("User-Agent") != null) {
String currentUserAgent = request.getHeader("User-Agent");
userAgent = PLUGIN_NAME + "/" + version + " " + currentUserAgent;
request.removeHeader("User-Agent");
} else {
userAgent = PLUGIN_NAME + "/" + version;
}
request.setHeader("User-Agent", userAgent);
}
}

View File

@ -15,9 +15,17 @@
*/
package com.microsoft.azure.util;
import com.cloudbees.plugins.credentials.CredentialsMatchers;
import com.cloudbees.plugins.credentials.CredentialsProvider;
import com.cloudbees.plugins.credentials.common.StandardUsernamePasswordCredentials;
import com.cloudbees.plugins.credentials.domains.DomainRequirement;
import com.microsoft.azure.exceptions.AzureCloudException;
import hudson.security.ACL;
import java.text.Format;
import java.text.SimpleDateFormat;
import java.util.Collections;
import java.util.Date;
import jenkins.model.Jenkins;
import org.apache.commons.lang.StringUtils;
public class AzureUtil {
@ -342,4 +350,18 @@ public class AzureUtil {
Constants.VM_NAME_HASH_LENGTH, numberOfDigits),
shortenedDeploymentHash);
}
public static StandardUsernamePasswordCredentials getCredentials(String credentialsId) throws AzureCloudException {
// Grab the pass
StandardUsernamePasswordCredentials creds = CredentialsMatchers.firstOrNull(CredentialsProvider.lookupCredentials(
StandardUsernamePasswordCredentials.class, Jenkins.getInstance(), ACL.SYSTEM,
Collections.<DomainRequirement>emptyList()),
CredentialsMatchers.withId(credentialsId));
if (creds == null) {
throw new AzureCloudException("Could not find credentials with id: " + credentialsId);
}
return creds;
}
}

View File

@ -47,64 +47,37 @@ public class TokenCache {
private static final Object tsafe = new Object();
private static TokenCache cache = null;
protected final String subscriptionId;
protected final String clientId;
protected final String clientSecret;
protected final String oauth2TokenEndpoint;
protected final String serviceManagementURL;
protected final AzureCredentials.ServicePrincipal credentials;
private final String path;
public static TokenCache getInstance(
final String subscriptionId,
final String clientId,
final String clientSecret,
final String oauth2TokenEndpoint,
final String serviceManagementURL) {
public static TokenCache getInstance(final AzureCredentials.ServicePrincipal servicePrincipal) {
synchronized (tsafe) {
if (cache == null) {
cache = new TokenCache(
subscriptionId, clientId, clientSecret, oauth2TokenEndpoint, serviceManagementURL);
} else if (cache.subscriptionId == null || !cache.subscriptionId.equals(subscriptionId)
|| cache.clientId == null || !cache.clientId.equals(clientId)
|| cache.clientSecret == null || !cache.clientSecret.equals(clientSecret)
|| cache.oauth2TokenEndpoint == null || !cache.oauth2TokenEndpoint.equals(oauth2TokenEndpoint)
|| cache.serviceManagementURL == null || !cache.serviceManagementURL.equals(serviceManagementURL)) {
cache = new TokenCache(servicePrincipal);
} else if (cache.credentials == null
|| cache.credentials.subscriptionId == null || !cache.credentials.subscriptionId.equals(servicePrincipal.subscriptionId)
|| cache.credentials.clientId == null || !cache.credentials.clientId.equals(servicePrincipal.clientId)
|| cache.credentials.clientSecret == null || !cache.credentials.clientSecret.equals(servicePrincipal.clientSecret)
|| cache.credentials.oauth2TokenEndpoint == null || !cache.credentials.oauth2TokenEndpoint.equals(servicePrincipal.oauth2TokenEndpoint)
|| cache.credentials.serviceManagementURL == null || !cache.credentials.serviceManagementURL.equals(servicePrincipal.serviceManagementURL)) {
cache.clear();
cache = new TokenCache(
subscriptionId, clientId, clientSecret, oauth2TokenEndpoint, serviceManagementURL);
cache = new TokenCache(servicePrincipal);
}
}
return cache;
}
private TokenCache(
final String subscriptionId,
final String clientId,
final String clientSecret,
final String oauth2TokenEndpoint,
final String serviceManagementURL) {
private TokenCache(final AzureCredentials.ServicePrincipal servicePrincipal) {
LOGGER.log(Level.FINEST, "TokenCache: TokenCache: Instantiate new cache manager");
this.subscriptionId = subscriptionId;
this.clientId = clientId;
this.clientSecret = clientSecret;
this.oauth2TokenEndpoint = oauth2TokenEndpoint;
this.credentials = servicePrincipal;
// Compute the cloud name
String cloudName = AzureUtil.getCloudName(subscriptionId);
if (StringUtils.isBlank(serviceManagementURL)) {
this.serviceManagementURL = Constants.DEFAULT_MANAGEMENT_URL;
} else {
this.serviceManagementURL = serviceManagementURL;
}
String cloudName = "<unknown";
if(credentials != null)
cloudName =AzureUtil.getCloudName(credentials.subscriptionId.getPlainText());
final String home = Jenkins.getInstance().root.getPath();
@ -199,13 +172,12 @@ public class TokenCache {
AuthenticationResult authres = null;
try {
LOGGER.log(Level.FINEST, "TokenCache: getNewToken: Aquiring access token: \n\t{0}\n\t{1}\n\t{2}",
new Object[] { oauth2TokenEndpoint, serviceManagementURL, clientId });
LOGGER.log(Level.FINEST, "TokenCache: getNewToken: Aquiring access token:");
final ClientCredential credential = new ClientCredential(clientId, clientSecret);
final ClientCredential credential = new ClientCredential(credentials.clientId.getPlainText(), credentials.clientSecret.getPlainText());
final Future<AuthenticationResult> future = new AuthenticationContext(oauth2TokenEndpoint, false, service).
acquireToken(serviceManagementURL, credential, null);
final Future<AuthenticationResult> future = new AuthenticationContext(credentials.oauth2TokenEndpoint.getPlainText(), false, service).
acquireToken(credentials.serviceManagementURL, credential, null);
authres = future.get();
} catch (MalformedURLException e) {
@ -222,7 +194,7 @@ public class TokenCache {
throw new AzureCloudException("Authentication result was null");
}
final AccessToken token = new AccessToken(subscriptionId, serviceManagementURL, authres);
final AccessToken token = new AccessToken(credentials.subscriptionId.getPlainText(), credentials.serviceManagementURL, authres);
writeTokenFile(token);
return token;

View File

@ -1,5 +1,5 @@
<?jelly escape-by-default='true'?>
<j:jelly xmlns:j="jelly:core" xmlns:st="jelly:stapler" xmlns:d="jelly:define" xmlns:l="/lib/layout" xmlns:t="/lib/hudson" xmlns:f="/lib/form">
<j:jelly xmlns:j="jelly:core" xmlns:st="jelly:stapler" xmlns:d="jelly:define" xmlns:c="/lib/credentials" xmlns:l="/lib/layout" xmlns:t="/lib/hudson" xmlns:f="/lib/form">
<table width="100%">
<f:section title="${%General_Configuration}">
<f:entry title="${%Template_Name}" field="templateName" help="/plugin/azure-vm-agents-plugin/help-templateName.html">
@ -67,6 +67,10 @@
<f:select />
</f:entry>
<f:entry title="${%Admin_Credentials}" field="credentialsId" help="/plugin/azure-vm-agents-plugin/help-credentials.html">
<c:select expressionAllowed="false"/>
</f:entry>
<f:section title="${%Initialization_Configuration}">
<f:entry title="${%Init_Script}" field="initScript" help="/plugin/azure-vm-agents-plugin/help-initScript.html">
<f:textarea />
@ -80,14 +84,6 @@
<f:checkbox default="true"/>
</f:entry>
</f:section>
<f:entry title="${%Username}" field="adminUserName" help="/plugin/azure-vm-agents-plugin/help-adminUserName.html">
<f:textbox />
</f:entry>
<f:entry title="${%Password}" field="adminPassword" help="/plugin/azure-vm-agents-plugin/help-adminPassword.html">
<f:password />
</f:entry>
</f:section>
<f:advanced>
@ -125,6 +121,6 @@
</div>
</f:entry>
<f:validateButton title="${%Verify_Template}" progress="${%Verifying_Template_MSG}" method="verifyConfiguration"
with="subscriptionId,clientId,clientSecret,oauth2TokenEndpoint,serviceManagementURL,resourceGroupName,templateName,labels,location,virtualMachineSize,storageAccountName,noOfParallelJobs,image,osType,imagePublisher,imageOffer,imageSku,imageVersion,agentLaunchMethod,initScript,adminUserName,adminPassword,virtualNetworkName,subnetName,retentionTimeInMin,jvmOptions" />
with="azureCredentialsId,resourceGroupName,templateName,labels,location,virtualMachineSize,storageAccountName,noOfParallelJobs,image,osType,imagePublisher,imageOffer,imageSku,imageVersion,agentLaunchMethod,initScript,credentialsId,virtualNetworkName,subnetName,retentionTimeInMin,jvmOptions" />
</table>
</j:jelly>

View File

@ -27,8 +27,7 @@ Init_Script=Initialization Script
Execute_Init_Script_As_Root=Run Initialization Script As Root (Linux Only)
Do_Not_Use_Machine_If_Init_Fails=Don't Use VM If Initialization Script Fails (Linux Only)
Username=Username
Password=Password
Admin_Credentials=Admin Credentials
Agent_Workspace=Agent Workspace
JVM_Options=JVM Options

View File

@ -1,27 +1,11 @@
<?jelly escape-by-default='true'?>
<j:jelly xmlns:j="jelly:core" xmlns:st="jelly:stapler" xmlns:d="jelly:define"
xmlns:l="/lib/layout" xmlns:t="/lib/hudson" xmlns:f="/lib/form">
xmlns:l="/lib/layout" xmlns:t="/lib/hudson" xmlns:f="/lib/form" xmlns:c="/lib/credentials">
<f:section title="${%Azure_Profile_Configuration}">
<f:entry title="${%Subscription_ID}" field="subscriptionId"
help="/plugin/azure-vm-agents-plugin/help-subscriptionId.html">
<f:password />
<f:entry title="${%Azure_Credentials}" field="azureCredentialsId" help="/plugin/azure-vm-agents-plugin/help-azureCredentials.html">
<c:select expressionAllowed="false"/>
</f:entry>
<f:entry title="${%Client_Id}" field="clientId"
help="/plugin/azure-vm-agents-plugin/help-clientId.html">
<f:password />
</f:entry>
<f:entry title="${%Client_Secret}" field="clientSecret"
help="/plugin/azure-vm-agents-plugin/help-clientSecret.html">
<f:password />
</f:entry>
<f:entry title="${%OAuth2_Token_Endpoint}" field="oauth2TokenEndpoint"
help="/plugin/azure-vm-agents-plugin/help-oauth2TokenEndpoint.html">
<f:password />
</f:entry>
<f:entry title="${%Max_Virtual_Machines_Limit}" field="maxVirtualMachinesLimit" help="/plugin/azure-vm-agents-plugin/help-maxVirtualMachinesLimit.html">
<f:textbox default="${descriptor.getDefaultMaxVMLimit()}" />
</f:entry>
@ -33,15 +17,8 @@
<f:entry title="${%Resource_Group_Name}" field="resourceGroupName" help="/plugin/azure-vm-agents-plugin/help-resourceGroupName.html">
<f:textbox default="${descriptor.getDefaultResourceGroupName()}" />
</f:entry>
<f:advanced>
<f:entry title="${%Service_Management_URL}" field="serviceManagementURL" help="/plugin/azure-vm-agents-plugin/help-serviceManagementURL.html">
<f:textbox default="${descriptor.getDefaultserviceManagementURL()}" />
</f:entry>
</f:advanced>
<f:validateButton title="${%Verify_Configuration}" progress="${%Verifying}" method="verifyConfiguration"
with="subscriptionId,clientId,clientSecret,oauth2TokenEndpoint,serviceManagementURL,resourceGroupName" />
with="azureCredentialsId,maxVirtualMachinesLimit,deploymentTimeout,resourceGroupName" />
</f:section>
<f:entry title="${%Azure_Virtual_Machine_Template}" description="${%Azure_Virtual_Machine_Template_desc}">

View File

@ -1,4 +1,5 @@
Azure_Profile_Configuration=Azure Profile Configuration
Azure_Credentials=Azure Credentials
Subscription_ID=Subscription ID
Client_Id=Client ID
Client_Secret=Client Secret

View File

@ -0,0 +1,23 @@
<?xml version="1.0" encoding="UTF-8"?>
<?jelly escape-by-default='true'?>
<j:jelly xmlns:j="jelly:core" xmlns:f="/lib/form" xmlns:st="jelly:stapler" xmlns:d="jelly:define">
<j:set var="uniqueId" value="${h.generateId()}" />
<f:entry title="${%Subscription ID}" field="subscriptionId" help="/plugin/azure-vm-agents-plugin/help-subscriptionId.html">
<f:password />
</f:entry>
<f:entry title="${%Client ID}" field="clientId" help="/plugin/azure-vm-agents-plugin/help-clientId.html">
<f:password />
</f:entry>
<f:entry title="${%Client Secret}" field="clientSecret" help="/plugin/azure-vm-agents-plugin/help-clientSecret.html">
<f:password />
</f:entry>
<f:entry title="${%OAuth 2.0 Token Endpoint}" field="oauth2TokenEndpoint" help="/plugin/azure-vm-agents-plugin/help-oauth2TokenEndpoint.html">
<f:password />
</f:entry>
<f:entry title="${%Management Service URL}" field="serviceManagementURL" help="/plugin/azure-vm-agents-plugin/help-serviceManagementURL.html">
<f:textbox default="${descriptor.getDefaultserviceManagementURL()}"/>
</f:entry>
<st:include page="id-and-description" class="${descriptor.clazz}"/>
<f:validateButton title="${%Verify Configuration}" progress="${%Verifying}" method="verifyConfiguration"
with="subscriptionId,clientId,clientSecret,oauth2TokenEndpoint,serviceManagementURL" />
</j:jelly>

View File

@ -1,10 +0,0 @@
<div>
The password for the agent machine. Rules for the password:</br>
1. The password must contain at least 8 characters.</br>
2. The password cannot be longer than 123 characters.</br>
3. The password must contain 3 of the following.</br>
&nbsp;&nbsp;&nbsp;&nbsp;a) a lowercase character</br>
&nbsp;&nbsp;&nbsp;&nbsp;b) an uppercase character</br>
&nbsp;&nbsp;&nbsp;&nbsp;c) a number</br>
&nbsp;&nbsp;&nbsp;&nbsp;d) one of the following special characters: !@#$%^&amp;*.
</div>

View File

@ -1,3 +0,0 @@
<div>
Administrator user name for the agent.
</div>

View File

@ -0,0 +1,7 @@
<div>
The Service Principal credentials required to connect to your Azure account.
<br />
More information can be found here: <a target="_blank" href="https://azure.microsoft.com/en-us/documentation/articles/resource-group-authenticate-service-principal">https://azure.microsoft.com/en-us/documentation/articles/resource-group-authenticate-service-principal</a>
<br />
If you want to add an Azure credential click "Add" and select "Microsoft Azure VM Agents" from the Kind drop-down.
</div>

View File

@ -0,0 +1,3 @@
<div>
The credentials for the agent machine.
</div>