Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[RELENG-2812] Add jenkins-cli support for current and next version. Draft for feedback #27

Draft
wants to merge 1 commit into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
122 changes: 122 additions & 0 deletions src/JenkinsCli.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
<?php

namespace TryLib;

use Exception;

const SERVER_VERSION = 'x-jenkins';

/**
* Abstract class for handling different jenkins cli versions.
* The server url is used to get version information and look up correct
* jenkins-cli jar version.
* Currently the jenkins-cli jar is deployed to clients via chef.
*/
abstract class JenkinsCli
{
private const JENKINS_CLI_JAR_VERSION_TABLE = [
"2.19.3" => "/usr/etsy/jenkins-cli.jar",
"2.303.1" => "/usr/etsy/jenkins-cli-2.303.1.jar"
];

/**
* Function to obtain Jenkins server info from given server url.
* Jenkins servers return info that can be used by clients to connect properly.
* This info is returned in http headers whose names start with 'x-'
* Example response:
* Array
* (
* [x-content-type-options] => nosniff
* [x-hudson-theme] => default
* [x-hudson] => 1.395
* [x-jenkins] => 2.19.3
* [x-jenkins-session] => 091ef581
* [x-hudson-cli-port] => 58999
* [x-jenkins-cli-port] => 58999
* [x-jenkins-cli2-port] => 58999
* [x-frame-options] => sameorigin
* [x-instance-identity] => MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAp0vIS8uMTaf6Hep/4SdDkjz2719m+RlzCbDlpYNUvXzT6GNgE3/lnLmXic1wIn2Ym7B+aHkQjm/5bP33VXuob/x+R6dX3iUe93zi7YRG0KUeHXCgglm+y1BymxldqNzFyFVJ22D24Qnt7qoATEaoXLa4VkQ1ZuIaBzaVq0qNZYR7zShQvS7TRD+itqxFGFEKEWGwkh0sFgenSIFpwy9MmOhY11i/+A2VdA5K/KcPnmnW3AMByNUVoaigRkHOCAU2mnBsfFngTpdxd2SEGCxosewsfi/aCLRFU1INCFXgngt/V7sba4U2ADV8E/kS1rTdHDkWVQQMbiSCwKDPXQ30uQIDAQAB
* [x-ssh-endpoint] => try.etsycloud.com:41108
* )
*/

private static function get_jenkins_server_info($server_url)
{

$headers = [];
$curl = curl_init($server_url);
curl_setopt($curl, CURLOPT_URL, $server_url);
curl_setopt($curl, CURLOPT_HEADER, true);
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
// see https://stackoverflow.com/a/41135574/16345588
curl_setopt(
$curl,
CURLOPT_HEADERFUNCTION,
function ($curl, $header) use (&$headers) {
$len = strlen($header);
$header = explode(':', $header, 2);
if (count($header) < 2) // ignore invalid headers
return $len;
$header_key = strtolower(trim($header[0]));
if (substr($header_key, 0, 2) == 'x-') {
$headers[$header_key] = trim($header[1]);
}


return $len;
}
);
echo "Getting server info for ". $server_url . PHP_EOL ;
$response = curl_exec($curl);
return $headers;
}
public static function get_jenkins_cli_info($server_url)
{
try {
$server_info = self::get_jenkins_server_info($server_url);
if (array_key_exists(SERVER_VERSION, $server_info)) {
$server_version = $server_info[SERVER_VERSION];
$jenkins_cli_jar = self::JENKINS_CLI_JAR_VERSION_TABLE[$server_version];

};
} catch (Exception $e) {
echo "Cannot get server info, falling back to NULL.". PHP_EOL;
$jenkins_cli_jar = NULL;
$server_version = NULL;
};
echo "jenkins_cli_jar = ". $jenkins_cli_jar . PHP_EOL;
echo "server_version = ". $server_version . PHP_EOL;
return [$jenkins_cli_jar, $server_version ];
}
public static function get_jenkins_cli_command($jenkins_cli_jar, $server_url, $user, $command)
{
// Get server info from jar name
$server_version = NULL;
foreach(self::JENKINS_CLI_JAR_VERSION_TABLE as $version => $jar){
if ($jenkins_cli_jar == $jar){
$server_version = $version;
break;
}
}
// Usage: java -jar jenkins-cli.jar [-s URL] command [opts...] args...
switch ($server_version) {
case "2.19.3":
$cli_options = "";
break;
case "2.303.1":
$cli_options = sprintf("-ssh -user %s -logger OFF ", $user);
break;
default:
$cli_options = "";
};
$cmd_format = "java -jar %s %s -s %s %s";
$cmd = sprintf(
$cmd_format,
$jenkins_cli_jar,
$cli_options,
$server_url,
$command
);
return $cmd;
}
}
19 changes: 12 additions & 7 deletions src/JenkinsRunner.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
abstract class JenkinsRunner {
protected $jenkins_url;
protected $jenkins_cli;
protected $jenkins_version;
protected $try_job_name;
protected $cmd_runner;

Expand All @@ -25,28 +26,36 @@ abstract class JenkinsRunner {
private $options;
private $callbacks;
private $ssh_key_path;
private $user;

public function __construct(
$jenkins_url,
$jenkins_cli,
$try_job_name,
$cmd_runner
) {
$debug_info = sprintf( "__construct(%s, %s, %s \n",
$jenkins_url,
$jenkins_cli,
$try_job_name,
);
echo $debug_info;
if (filter_var($jenkins_url, FILTER_VALIDATE_URL) !== false) {
$this->jenkins_url = $jenkins_url;
} else {
throw new Exception("jenkins url must include protocol, ie http://, and trailing /, $jenkins_url given");
}
$this->jenkins_cli = $jenkins_cli;

$this->try_job_name = $try_job_name;
$this->cmd_runner = $cmd_runner;

$this->jenkins_cli = $jenkins_cli;
$this->options = array();
$this->callbacks = array();
$this->ssh_key_path = null;
$this->branch = null;
$this->try_status = '';
$this->try_base_url = '';
$this->user = getenv("USER");
}

abstract protected function pollForCompletion($pretty);
Expand All @@ -56,11 +65,7 @@ abstract protected function getBuildCommand();
abstract protected function getBuildExtraArguments($show_results, $show_progress);

public function runJenkinsCommand($command) {
$cmd = sprintf( "java -jar %s -s %s %s",
$this->jenkins_cli,
$this->jenkins_url,
$command
);
$cmd = JenkinsCli::get_jenkins_cli_command($this->jenkins_cli, $this->jenkins_url, $this->user , $command);
$this->cmd_runner->run($cmd, false, false);
}

Expand Down
2 changes: 1 addition & 1 deletion tests/JenkinsRunnerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ function testInvalidUrl() {


function testRunJenkinsCommand() {
$expected_cmd = 'java -jar ' . self::JENKINS_CLI . ' -s ' . self::JENKINS_URL . ' dummy-cmd';
$expected_cmd = 'java -jar ' . self::JENKINS_CLI . ' -s ' . self::JENKINS_URL . ' dummy-cmd';

$this->mock_cmd_runner->expects($this->once())
->method('run')
Expand Down
116 changes: 116 additions & 0 deletions try-next
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
#!/usr/bin/env php
<?php

use TryLib\JenkinsCli;

require_once __DIR__.'/vendor/autoload.php';

$user = getenv('LDAP_USER') ?: getenv('USER');

$default_wc_path = getenv('ETSY_SRC');
if (!$default_wc_path) {
if (preg_match(";development/([^/]+);", getcwd(), $matches) > 0) {
$default_wc_path = "/home/$user/development/".$matches[1];
} else {
$default_wc_path = "/home/$user/development/Etsyweb"; // Backwards compatibility
}
}

$default_branch = 'main';

// In the following, in_array matches _only_ -b <branch> and preg_grep matches _both_
// --branch <branch> and --branch=<branch>
if (in_array("-b", $argv) || preg_grep("/--branch.*/", $argv)) {
// Branch was manually passed in, we can continue as-is
echo "The branch has been set manually, continuing with specified branch...\n";
} else {
// Manually set the 'default' branch
array_push($argv, "-b", $default_branch);
}

$options_tuple = TryLib\TryRunner\Options::parse(
$argv,
"https://try.etsycloud.com/",
"try",
"try",
$default_wc_path
);

$blocklist = array(
'phplib/EtsyConfig/production.php',
'phplib/EtsyConfig/giftcards_production.php'
);

$safelist = $options_tuple[0]->safelist;
if (is_string($safelist)) {
$safelist = array($safelist);
}

$branch = $options_tuple[0]->branch;

$jira_ticket_specified = false;
$get_rodeo_tickets_in_commit_script = $options_tuple[0]->wcpath . '/bin/rodeo/reportRodeoTicketInLocalCommits.php';

if (file_exists($get_rodeo_tickets_in_commit_script)) {
# Let's add a ROD-XX ticket to the extra-param if not specified
# Extra param is an option of the TryLib_Util_PHPOptions_OptDict of $options_tuple[0]
$extra_param = $options_tuple[0]->offsetGet('extra_param');

# Start by transforming the extra_param argument into an array
#(can be null, a string if --extra_param is passed once or an array if --extra_param is passed multiple times)
if (is_null($extra_param) || is_string($extra_param)) {
$extra_param = array($extra_param);
}

# Function to check if extra_param is a jira param
$is_jira_param = function($k) {return strpos($k, 'jira=') === 0;};
if (empty (array_filter($extra_param, $is_jira_param))) {
# No JIRA param specified - let's try to add one
$branch = $options_tuple[0]->branch;
exec($get_rodeo_tickets_in_commit_script . ' ' . $branch, $jira_ticket, $ret);
if ($ret === 0) {
echo "Try Rodeo Sniffer using Rodeo ticket from commit message: " . reset($jira_ticket) . "\n";
$extra_param[] = "jira=" . reset($jira_ticket);
$jira_ticket_specified = true;
}
} else {
$jira_ticket_specified = true;
}
// Filter out potential emtpy values in extra-param and set them back
$options_tuple[0]->offsetSet('extra_param', array_filter($extra_param));
}

$jenkinsserver = $options_tuple[0]->jenkinsserver;
$jenkins_cli_info = JenkinsCli::get_jenkins_cli_info($jenkinsserver);
$jenkins_cli_jar = $jenkins_cli_info[0];
$jenkins_cli_version = $jenkins_cli_info[1];
echo "try-next " . $jenkins_cli_jar . "," . PHP_EOL;
$try_runner = TryLib\TryRunner\Builder::masterProject()
->optionsTuple($options_tuple)
->jenkinsCliJarPath($jenkins_cli_jar)
->prechecks(array(
new TryLib\Precheck\ScriptRunner($options_tuple[0]->wcpath . '/bin/asset-validator.php'),
new TryLib\Precheck\GitWarnOnBlocklisted($blocklist, $safelist, $options_tuple[0]->staged),
new TryLib\Precheck\GitCopyBehind(array($branch)),
new TryLib\Precheck\GitReportUntracked()
))
->sshKeyPath(getenv('HOME') . '/.ssh/try_id_rsa')
->overrideUser($user)
->build();


if (!$jira_ticket_specified) {
$report_scoped_files_in_diff_script = $options_tuple[0]->wcpath . '/bin/rodeo/reportScopedFilesInDiff.php';
if (file_exists($report_scoped_files_in_diff_script)) {
system($report_scoped_files_in_diff_script . ' ' . $try_runner->getPatchLocation(), $ret);
if ($ret !== 0) {
// The rodeo sniffer is gonna fail so prompt if they want to continue or not.
$contin = readline("Would you like to continue (y/N)? ");
if (strlen($contin) === 0 || strtolower(trim($contin))[0] !== 'y') {
exit($ret);
}
}
}
}

exit($try_runner->run());