package org.jfrog.bamboo.util; import com.atlassian.bamboo.build.ViewBuildResults; import com.atlassian.bamboo.plan.cache.ImmutablePlan; import com.atlassian.bamboo.task.TaskDefinition; import com.atlassian.bamboo.task.runtime.RuntimeTaskDefinition; import com.google.common.base.Predicate; import com.google.common.collect.ArrayListMultimap; import com.google.common.collect.Maps; import com.google.common.collect.Multimap; import org.apache.commons.lang.StringUtils; import org.apache.http.HttpResponse; import org.apache.http.client.HttpClient; import org.apache.http.client.methods.HttpGet; import org.apache.http.impl.client.DefaultHttpClient; import org.apache.log4j.Logger; import org.apache.tools.ant.types.Commandline; import org.jetbrains.annotations.NotNull; import org.jfrog.bamboo.admin.BintrayConfig; import org.jfrog.bamboo.admin.ServerConfig; import org.jfrog.bamboo.admin.ServerConfigManager; import org.jfrog.bamboo.context.AbstractBuildContext; import org.jfrog.bamboo.security.EncryptionHelper; import org.jfrog.build.api.BuildInfoConfigProperties; import org.jfrog.build.extractor.clientConfiguration.client.ArtifactoryBuildInfoClient; import java.io.IOException; import java.util.List; import java.util.Map; /** * Utility class that serves as a helper for common operations of a task. * * @author Tomer Cohen */ public class TaskUtils { private static final char PROPERTIES_DELIMITER = ';'; private static final char KEY_VALUE_SEPARATOR = '='; /* This is the name of the "Download Artifacts" task in bamboo, we are looking it up as downloading artifacts is a pre condition to our task */ private static final String DOWNLOAD_ARTIFACTS_TASK_KEY = "com.atlassian.bamboo.plugins.bamboo-artifact-downloader-plugin:artifactdownloadertask"; /** * Get an escaped version of the environment map that is to be passed onwards to the extractors. Bamboo escapes the * key of the property and replaces all '.' into '_' as well as adds the "bamboo" prefixHence a conversion back is * needed. * * @param env The original environment map. * @return The escaped environment map. */ public static Map<String, String> getEscapedEnvMap(Map<String, String> env) { Map<String, String> result = Maps.newHashMap(); if (env != null) { for (Map.Entry<String, String> entry : env.entrySet()) { String escaped = entry.getKey().replace('_', '.'); escaped = StringUtils.removeStart(escaped, "bamboo."); result.put(escaped, entry.getValue()); } } return result; } /** * Append the path of the build info properties file as a system property to the list of arguments that is given to * the build (as a -D param). */ public static void appendBuildInfoPropertiesArgument(List<String> arguments, String buildInfoPropertiesFile) { if ((arguments != null) && StringUtils.isNotBlank(buildInfoPropertiesFile)) { arguments.add(Commandline.quoteArgument("-D" + BuildInfoConfigProperties.PROP_PROPS_FILE + "=" + buildInfoPropertiesFile)); } } /** * Create Multimap that represent build/deployment matrix param to attach uploaded artifacts * * @param propertiesInput String that separated by semicolon to parse into map * @return Multimap that represents the deployment properties, empty map if no properties attaches */ public static Multimap<String, String> extractMatrixParamFromString(String propertiesInput) { Multimap<String, String> matrixParams = ArrayListMultimap.create(); String[] matrixParamString = StringUtils.split(propertiesInput, PROPERTIES_DELIMITER); for (String s : matrixParamString) { String[] keyValueArr = StringUtils.split(s, KEY_VALUE_SEPARATOR); boolean validProperty = keyValueArr.length == 2; if (validProperty) { // No whitespace allowed in key String formatKey = keyValueArr[0].replace(" ", StringUtils.EMPTY); matrixParams.put(formatKey, keyValueArr[1].trim()); } } return matrixParams; } /** * Get the Id of selected Artifactory * * @param definition task that uses Artifactory * @return artifactory id */ public static String getSelectedServerId(TaskDefinition definition) { if (definition == null) { return StringUtils.EMPTY; } Map<String, String> configuration = definition.getConfiguration(); Map<String, String> filtered = Maps.filterKeys(configuration, new Predicate<String>() { public boolean apply(String input) { return StringUtils.endsWith(input, AbstractBuildContext.SERVER_ID_PARAM); } }); return filtered.values().iterator().next(); } /** * Searching for the "Artifacts Download" task in a list of tasks * * @param runtimeTaskDefinitionList all the job tasks * @return Artifacts Download task, null if no such task exists */ public static RuntimeTaskDefinition findDownloadArtifactsTask(@NotNull List<RuntimeTaskDefinition> runtimeTaskDefinitionList) { for (RuntimeTaskDefinition rtd : runtimeTaskDefinitionList) { if (rtd.getPluginKey().equals(DOWNLOAD_ARTIFACTS_TASK_KEY)) { return rtd; } } throw new IllegalStateException("\"Artifacts Download\" task must run before the \"Artifactory Deployment\" task."); } /** * Search for any Artifactory build task (Maven, Gradle, Ivy, Generic) * * @return configuration map for this task */ public static Map<String, String> findConfigurationForBuildTask(ViewBuildResults viewBuildResults) { List<TaskDefinition> taskDefinitions = viewBuildResults.getImmutableBuild().getBuildDefinition().getTaskDefinitions(); for (TaskDefinition taskDefinition : taskDefinitions) { if (StringUtils.startsWith(taskDefinition.getPluginKey(), ConstantValues.ARTIFACTORY_PLUGIN_KEY)) { return taskDefinition.getConfiguration(); } } throw new IllegalStateException("This job has no Artifactory task."); } /** * Find maven or gradle task for a specific plan * * @param plan - plan configuration in which we are trying to find the maven/gradle task * @return appropriate task definition or null in case no such task exist */ public static TaskDefinition getMavenOrGradleTaskDefinition(ImmutablePlan plan) { if (plan == null) { return null; } List<TaskDefinition> definitions = plan.getBuildDefinition().getTaskDefinitions(); if (definitions.isEmpty()) { return null; } return TaskDefinitionHelper.findMavenOrGradleDefinition(definitions); } /** * Get Server config object for specific build * * @param plan server config for this plan * @return ServerConfig object with Artifactory details */ public static ServerConfig getArtifactoryServerConfig(ImmutablePlan plan) { TaskDefinition mavenOrGradleTaskDefinition = TaskDefinitionHelper.getPushToBintrayEnabledTaskDefinition(plan); String serverIdStr = TaskUtils.getSelectedServerId(mavenOrGradleTaskDefinition); if (StringUtils.isNotEmpty(serverIdStr)) { long serverId = Long.parseLong(serverIdStr); return ServerConfigManager.getInstance().getServerConfigById(serverId); } throw new IllegalStateException("Error while trying to create ArtifactoryBuildInfoClient"); } public static ArtifactoryBuildInfoClient createClient(ServerConfigManager serverConfigManager, ServerConfig serverConfig, AbstractBuildContext context, Logger log) { String serverUrl = substituteVariables(serverConfigManager, serverConfig.getUrl()); String username = substituteVariables(serverConfigManager, context.getDeployerUsername()); if (StringUtils.isBlank(username)) { username = substituteVariables(serverConfigManager, serverConfig.getUsername()); } ArtifactoryBuildInfoClient client; BuildInfoLog bambooLog = new BuildInfoLog(log); if (StringUtils.isBlank(username)) { client = new ArtifactoryBuildInfoClient(serverUrl, bambooLog); } else { String password = substituteVariables(serverConfigManager, context.getDeployerPassword()); if (StringUtils.isBlank(password)) { password = substituteVariables(serverConfigManager, serverConfig.getPassword()); } client = new ArtifactoryBuildInfoClient(serverUrl, username, password, bambooLog); } client.setConnectionTimeout(serverConfig.getTimeout()); return client; } /** * Test connection with Bintray * * @return Http status code from Bintray */ public static int testBintrayConnection(String bintrayUrl, String bintrayUser, String bintrayApiKey) throws IOException { HttpClient client = new DefaultHttpClient(); String testUrl = bintrayUrl + "users/" + bintrayUser; HttpGet testConnectionRequest = new HttpGet(testUrl); testConnectionRequest.setHeader(HttpUtils.createAuthorizationHeader(bintrayUser, bintrayApiKey)); HttpResponse response = client.execute(testConnectionRequest); return response.getStatusLine().getStatusCode(); } /** * Substitute (replace) Bamboo variable names with their defined values */ private static String substituteVariables(ServerConfigManager serverConfigManager, String s) { return s != null ? serverConfigManager.substituteVariables(s) : null; } public static String decryptIfNeeded(String s) { try { s = EncryptionHelper.decrypt(s); } catch (RuntimeException e) { // Ignore. The field may not be encrypted. } return s; } public static BintrayConfig getBintrayConfig() { ServerConfigManager serverConfigManager = ServerConfigManager.getInstance(); return serverConfigManager.getBintrayConfig(); } public static String getBintrayUrl() { String bintrayUrl = System.getenv("BAMBOO_BINTRAY_URL"); if (org.apache.commons.lang3.StringUtils.isEmpty(bintrayUrl)) { bintrayUrl = ConstantValues.BINTRAY_URL; } return bintrayUrl; } }