Browse Source

sVote source code publication

Swisspost 5 years ago
commit
460d0e0780
100 changed files with 6119 additions and 0 deletions
  1. 103 0
      LICENSE.md
  2. 11 0
      README.md
  3. BIN
      dependencies/bsh-core-2.0b6.jar
  4. BIN
      dependencies/cryptography-4.2.1.jar
  5. BIN
      dependencies/iaikPkcs11Wrapper-1.4.jar
  6. BIN
      dependencies/jakarta-oro-2.0.6-local.jar
  7. BIN
      dependencies/log4j-1.3alpha-7-local.jar
  8. BIN
      dependencies/ov-keystore-1.3.0-tests.jar
  9. BIN
      dependencies/ov-keystore-1.3.0.jar
  10. BIN
      dependencies/scytl-gmp-6.1.1-amd64-Linux-gpp-shared.nar
  11. BIN
      dependencies/scytl-gmp-6.1.1-amd64-Windows-gpp-shared.nar
  12. BIN
      dependencies/scytl-math-1.0.1.jar
  13. BIN
      dependencies/scytl-math-native-1.0.1-amd64-Linux-gpp-jni.nar
  14. BIN
      dependencies/scytl-math-native-1.0.1-amd64-Windows-gpp-jni.nar
  15. BIN
      dependencies/shares-1.0.2.jar
  16. BIN
      documentation/Scytl sVote Audit with Control Components.pdf
  17. BIN
      documentation/Scytl sVote Protocol Specifications.pdf
  18. BIN
      documentation/Scytl_sVote_Software_Architecture.pdf
  19. 12 0
      source-code/Copyright.txt
  20. 5 0
      source-code/maven-generic-conf/.gitignore
  21. 15 0
      source-code/maven-generic-conf/README.md
  22. 2 0
      source-code/maven-generic-conf/THIRD-PARTY.txt
  23. 166 0
      source-code/maven-generic-conf/etc/eclipse/scytl_checkstyle.xml
  24. 33 0
      source-code/maven-generic-conf/etc/eclipse/scytl_codetemplates.xml
  25. 251 0
      source-code/maven-generic-conf/etc/eclipse/scytl_javaformat-1.1.xml
  26. 4 0
      source-code/maven-generic-conf/extlibs/barcode4j/INSTALL.txt
  27. BIN
      source-code/maven-generic-conf/extlibs/barcode4j/barcode4j.jar
  28. 11 0
      source-code/maven-generic-conf/extlibs/ec2-plugin/.gitignore
  29. 2 0
      source-code/maven-generic-conf/extlibs/ec2-plugin/TODO.txt
  30. 75 0
      source-code/maven-generic-conf/extlibs/ec2-plugin/pom.xml
  31. 118 0
      source-code/maven-generic-conf/extlibs/ec2-plugin/src/main/java/hudson/plugins/ec2/AmazonEC2Cloud.java
  32. 459 0
      source-code/maven-generic-conf/extlibs/ec2-plugin/src/main/java/hudson/plugins/ec2/EC2Cloud.java
  33. 126 0
      source-code/maven-generic-conf/extlibs/ec2-plugin/src/main/java/hudson/plugins/ec2/EC2Computer.java
  34. 73 0
      source-code/maven-generic-conf/extlibs/ec2-plugin/src/main/java/hudson/plugins/ec2/EC2ComputerLauncher.java
  35. 152 0
      source-code/maven-generic-conf/extlibs/ec2-plugin/src/main/java/hudson/plugins/ec2/EC2PrivateKey.java
  36. 77 0
      source-code/maven-generic-conf/extlibs/ec2-plugin/src/main/java/hudson/plugins/ec2/EC2RetentionStrategy.java
  37. 382 0
      source-code/maven-generic-conf/extlibs/ec2-plugin/src/main/java/hudson/plugins/ec2/EC2Slave.java
  38. 82 0
      source-code/maven-generic-conf/extlibs/ec2-plugin/src/main/java/hudson/plugins/ec2/EC2Tag.java
  39. 180 0
      source-code/maven-generic-conf/extlibs/ec2-plugin/src/main/java/hudson/plugins/ec2/Eucalyptus.java
  40. 23 0
      source-code/maven-generic-conf/extlibs/ec2-plugin/src/main/java/hudson/plugins/ec2/InstanceState.java
  41. 56 0
      source-code/maven-generic-conf/extlibs/ec2-plugin/src/main/java/hudson/plugins/ec2/InstanceTypeConverter.java
  42. 212 0
      source-code/maven-generic-conf/extlibs/ec2-plugin/src/main/java/hudson/plugins/ec2/OpenNebula.java
  43. 40 0
      source-code/maven-generic-conf/extlibs/ec2-plugin/src/main/java/hudson/plugins/ec2/PluginImpl.java
  44. 442 0
      source-code/maven-generic-conf/extlibs/ec2-plugin/src/main/java/hudson/plugins/ec2/SlaveTemplate.java
  45. 27 0
      source-code/maven-generic-conf/extlibs/ec2-plugin/src/main/java/hudson/plugins/ec2/ebs/ZPoolExpandNotice.java
  46. 66 0
      source-code/maven-generic-conf/extlibs/ec2-plugin/src/main/java/hudson/plugins/ec2/ebs/ZPoolMonitor.java
  47. 9 0
      source-code/maven-generic-conf/extlibs/ec2-plugin/src/main/java/hudson/plugins/ec2/ebs/package-info.java
  48. 555 0
      source-code/maven-generic-conf/extlibs/ec2-plugin/src/main/java/hudson/plugins/ec2/one/OneEC2Cloud.java
  49. 139 0
      source-code/maven-generic-conf/extlibs/ec2-plugin/src/main/java/hudson/plugins/ec2/one/OneEC2Computer.java
  50. 458 0
      source-code/maven-generic-conf/extlibs/ec2-plugin/src/main/java/hudson/plugins/ec2/one/OneEC2Slave.java
  51. 82 0
      source-code/maven-generic-conf/extlibs/ec2-plugin/src/main/java/hudson/plugins/ec2/one/OneEC2Tag.java
  52. 54 0
      source-code/maven-generic-conf/extlibs/ec2-plugin/src/main/java/hudson/plugins/ec2/one/OneInstanceType.java
  53. 665 0
      source-code/maven-generic-conf/extlibs/ec2-plugin/src/main/java/hudson/plugins/ec2/one/OneSlaveTemplate.java
  54. 232 0
      source-code/maven-generic-conf/extlibs/ec2-plugin/src/main/java/hudson/plugins/ec2/ssh/EC2UnixLauncher.java
  55. 50 0
      source-code/maven-generic-conf/extlibs/ec2-plugin/src/main/java/hudson/plugins/ec2/ssh/HostKeyVerifierImpl.java
  56. 23 0
      source-code/maven-generic-conf/extlibs/ec2-plugin/src/main/resources/hudson/plugins/ec2/AmazonEC2Cloud/config-entries.jelly
  57. 6 0
      source-code/maven-generic-conf/extlibs/ec2-plugin/src/main/resources/hudson/plugins/ec2/AmazonEC2Cloud/help-region.html
  58. 26 0
      source-code/maven-generic-conf/extlibs/ec2-plugin/src/main/resources/hudson/plugins/ec2/EC2Cloud/computerSet.jelly
  59. 1 0
      source-code/maven-generic-conf/extlibs/ec2-plugin/src/main/resources/hudson/plugins/ec2/EC2Cloud/computerSet.properties
  60. 9 0
      source-code/maven-generic-conf/extlibs/ec2-plugin/src/main/resources/hudson/plugins/ec2/EC2Cloud/config.jelly
  61. 6 0
      source-code/maven-generic-conf/extlibs/ec2-plugin/src/main/resources/hudson/plugins/ec2/EC2Cloud/help-accessId.html
  62. 10 0
      source-code/maven-generic-conf/extlibs/ec2-plugin/src/main/resources/hudson/plugins/ec2/EC2Cloud/help-ec2EndpointUrl.html
  63. 15 0
      source-code/maven-generic-conf/extlibs/ec2-plugin/src/main/resources/hudson/plugins/ec2/EC2Cloud/help-instanceCapStr.html
  64. 1 0
      source-code/maven-generic-conf/extlibs/ec2-plugin/src/main/resources/hudson/plugins/ec2/EC2Cloud/help-jvmopts.html
  65. 12 0
      source-code/maven-generic-conf/extlibs/ec2-plugin/src/main/resources/hudson/plugins/ec2/EC2Cloud/help-privateKey.html
  66. 7 0
      source-code/maven-generic-conf/extlibs/ec2-plugin/src/main/resources/hudson/plugins/ec2/EC2Cloud/help-remoteAdmin.html
  67. 5 0
      source-code/maven-generic-conf/extlibs/ec2-plugin/src/main/resources/hudson/plugins/ec2/EC2Cloud/help-rootCommandPrefix.html
  68. 10 0
      source-code/maven-generic-conf/extlibs/ec2-plugin/src/main/resources/hudson/plugins/ec2/EC2Cloud/help-s3EndpointUrl.html
  69. 7 0
      source-code/maven-generic-conf/extlibs/ec2-plugin/src/main/resources/hudson/plugins/ec2/EC2Cloud/help-userData.html
  70. 104 0
      source-code/maven-generic-conf/extlibs/ec2-plugin/src/main/resources/hudson/plugins/ec2/EC2Computer/configure.jelly
  71. 6 0
      source-code/maven-generic-conf/extlibs/ec2-plugin/src/main/resources/hudson/plugins/ec2/EC2Computer/help-idleTerminationMinutes.html
  72. 6 0
      source-code/maven-generic-conf/extlibs/ec2-plugin/src/main/resources/hudson/plugins/ec2/EC2Computer/help-usePrivateDnsName.html
  73. 4 0
      source-code/maven-generic-conf/extlibs/ec2-plugin/src/main/resources/hudson/plugins/ec2/EC2Slave/help-initScript.html
  74. 3 0
      source-code/maven-generic-conf/extlibs/ec2-plugin/src/main/resources/hudson/plugins/ec2/EC2Slave/help-numExecutors.html
  75. 7 0
      source-code/maven-generic-conf/extlibs/ec2-plugin/src/main/resources/hudson/plugins/ec2/EC2Slave/help-remoteAdmin.html
  76. 5 0
      source-code/maven-generic-conf/extlibs/ec2-plugin/src/main/resources/hudson/plugins/ec2/EC2Slave/help-rootCommandPrefix.html
  77. 5 0
      source-code/maven-generic-conf/extlibs/ec2-plugin/src/main/resources/hudson/plugins/ec2/EC2Slave/help-securityGroups.html
  78. 6 0
      source-code/maven-generic-conf/extlibs/ec2-plugin/src/main/resources/hudson/plugins/ec2/EC2Slave/help-stopOnTerminate.html
  79. 4 0
      source-code/maven-generic-conf/extlibs/ec2-plugin/src/main/resources/hudson/plugins/ec2/EC2Slave/help-subnetId.html
  80. 3 0
      source-code/maven-generic-conf/extlibs/ec2-plugin/src/main/resources/hudson/plugins/ec2/EC2Slave/help-type.html
  81. 9 0
      source-code/maven-generic-conf/extlibs/ec2-plugin/src/main/resources/hudson/plugins/ec2/EC2Slave/help-zone.html
  82. 41 0
      source-code/maven-generic-conf/extlibs/ec2-plugin/src/main/resources/hudson/plugins/ec2/EC2Tag/config.jelly
  83. 21 0
      source-code/maven-generic-conf/extlibs/ec2-plugin/src/main/resources/hudson/plugins/ec2/Eucalyptus/config-entries.jelly
  84. 4 0
      source-code/maven-generic-conf/extlibs/ec2-plugin/src/main/resources/hudson/plugins/ec2/Eucalyptus/help-url.html
  85. 3 0
      source-code/maven-generic-conf/extlibs/ec2-plugin/src/main/resources/hudson/plugins/ec2/Messages.properties
  86. 17 0
      source-code/maven-generic-conf/extlibs/ec2-plugin/src/main/resources/hudson/plugins/ec2/OpenNebula/config-entries.jelly
  87. 4 0
      source-code/maven-generic-conf/extlibs/ec2-plugin/src/main/resources/hudson/plugins/ec2/OpenNebula/help-url.html
  88. 124 0
      source-code/maven-generic-conf/extlibs/ec2-plugin/src/main/resources/hudson/plugins/ec2/SlaveTemplate/config.jelly
  89. 6 0
      source-code/maven-generic-conf/extlibs/ec2-plugin/src/main/resources/hudson/plugins/ec2/SlaveTemplate/help-idleTerminationMinutes.html
  90. 6 0
      source-code/maven-generic-conf/extlibs/ec2-plugin/src/main/resources/hudson/plugins/ec2/SlaveTemplate/help-usePrivateDnsName.html
  91. 40 0
      source-code/maven-generic-conf/extlibs/ec2-plugin/src/main/resources/hudson/plugins/ec2/ebs/ZPoolExpandNotice/index.jelly
  92. 33 0
      source-code/maven-generic-conf/extlibs/ec2-plugin/src/main/resources/hudson/plugins/ec2/ebs/ZPoolExpandNotice/message.jelly
  93. 3 0
      source-code/maven-generic-conf/extlibs/ec2-plugin/src/main/resources/hudson/plugins/ec2/one/Messages.properties
  94. 26 0
      source-code/maven-generic-conf/extlibs/ec2-plugin/src/main/resources/hudson/plugins/ec2/one/OneEC2Cloud/computerSet.jelly
  95. 1 0
      source-code/maven-generic-conf/extlibs/ec2-plugin/src/main/resources/hudson/plugins/ec2/one/OneEC2Cloud/computerSet.properties
  96. 9 0
      source-code/maven-generic-conf/extlibs/ec2-plugin/src/main/resources/hudson/plugins/ec2/one/OneEC2Cloud/config.jelly
  97. 6 0
      source-code/maven-generic-conf/extlibs/ec2-plugin/src/main/resources/hudson/plugins/ec2/one/OneEC2Cloud/help-accessId.html
  98. 10 0
      source-code/maven-generic-conf/extlibs/ec2-plugin/src/main/resources/hudson/plugins/ec2/one/OneEC2Cloud/help-ec2EndpointUrl.html
  99. 15 0
      source-code/maven-generic-conf/extlibs/ec2-plugin/src/main/resources/hudson/plugins/ec2/one/OneEC2Cloud/help-instanceCapStr.html
  100. 1 0
      source-code/maven-generic-conf/extlibs/ec2-plugin/src/main/resources/hudson/plugins/ec2/one/OneEC2Cloud/help-jvmopts.html

+ 103 - 0
LICENSE.md

@@ -0,0 +1,103 @@
+**Electronic Voting Solution Source Code Access Agreement**
+
+1.	Summary
+
+The aim of publishing the source code is to establish and build confidence in the public domain, while obtaining feedback from professional experts and the opportunity to make improvements. Constructive contributions and findings on the source code are always welcome.
+At this stage, we would like to highlight the key points in the terms of use:
+* The OEV (Federal Chancellery Ordinance on Electronic Voting) allows the source code to be examined, modified, compiled and executed for ideational purposes. Publication is organized and regulated in line with this idea.
+* The published code, including the source code, is the intellectual property of the companies Scytl and Post CH Ltd. The code in question is proprietary and not subject to a free and open source software (FOSS) licence.
+* Distributing or publishing the access tools, granting access to third parties and sharing the source code are not permitted.
+* Post CH Ltd must be consulted before any findings may be published. 
+* All feedback regarding vulnerabilities found in the electronic voting solution source code is welcome. All feedback must be reported on the GitLab.com platform as an issue and set to “confidential”. Swiss Post will incorporate the reported vulnerabilities into the development of the system.
+
+2.	INTRODUCTION
+
+This Electronic Voting (EV) Solution Source Code Access Agreement (“the Agreement”) governs the access to the EV Solution Source Code (“the EV Solution Source Code” or “the Source Code”), consisting of (a) the source code of the EV Solution cryptographic protocols and/or its security measures and any other components of the EV Solution as well as (b) the documentation on the system and its operation, as made accessible by its Owners in this Source Code Access Program (“the Program”). The Program includes release 2.1 of the Source Code. The documents and software components made public under www.swisspost.ch/evoting are not part of the Program.
+Scytl Secure Electronic Voting, S.A. (Scytl) and Post CH Ltd (Swiss Post) are the Owners of the EV Solution and the Source Code.
+The Owners own and retain all rights, titles and interests in and to their respective parts of the EV Solution Source Code, including all copyright, patents, trade secret rights, trademarks, and other intellectual property rights therein. 
+The Owners grant access to the EV Solution Source Code in the Program to the extent required by the (Swiss) Federal Chancellery Ordinance on Electronic Voting (“the Ordinance”) . No part of this Agreement shall be construed as to provide surpassing rights or to permit its use for other purposes. 
+The Program takes place because the system has the property of complete verifiability and the system and its operation have been successfully tested according to the Ordinance. The Program aims to give interested persons sufficient time to analyse the Source Code and submit their results to the authorities and to the system providers prior to the canton’s application for general approval. 
+The Owners welcome independent security researchers (Researchers) to evaluate and report any vulnerabilities that may be found in the EV Solution Source Code.
+
+3.	AGREEMENT TO THE EV SOLUTION SOURCE CODE ACCESS AGREEMENT
+
+By signing up to gain access to the EV Solution Source Code, you agree to be bound by the Agreement for the duration of the access to the EV Solution Source Code and thereafter. 
+
+4.	ORGANIZATION OF THE PROGRAM
+
+The Program begins on 07.02.2019 and ends at the termination of the productive use of the release 2.1. 
+Swiss Post provides a repository dedicated to the Program on the GitLab platform (https://www.gitlab.com/swisspost). Researchers will receive access to the repository with their GitLab.com account. The GitLab account is free of charge. Access to the repository is limited for the duration of the Program. Accesses to the repository will be logged. 
+Participants who have found or believe they have found a vulnerability are obliged to submit a report in the GitLab platform as an issue set explicitly to confidential (see para. 7 FAIR USE RESTRICTIONS). The publication of any vulnerability or other test findings has to comply with the Disclosure Policy (see para. 8 REPORTING PROCEDURE), which fully replaces the Responsible Disclosure Policy of GitLab under the Program.
+The Owners will make their best efforts to give timely feedback on a submitted finding. 
+
+5.	REGISTRATION POLICY
+
+Registration is mandatory to access the EV Solution Source Code. 
+You can register via http://www.swisspost.ch/evoting-sourcecode (website operated by Swiss Post). If participating as a team, all members need to register separately. 
+Registration will allow you to access the GitLab repository dedicated to the Program and use the EV Solution Source Code within the scope of the Regulatory License (see para. 6 REGULATORY LICENSE). The means of access are personal and may not be shared with anyone.
+Registration for the Program is open to all natural persons willing to comply with the Agreement, with the exception of natural persons who do not act under their own responsibility, but as employees, civil servants, officers or any other subordinate capacity. Registration is therefore not open to organizations, associations, institutions, administrations, governments, government agencies, foreign states, or any other entity that is not a natural person.
+The registration data will only be used by Swiss Post to ensure the proper operation of the Program and compliance with this Agreement. 
+
+6.	REGULATORY LICENSE
+
+The EV Solution Source Code may be examined, modified, compiled and executed for ideational purposes and Researchers may write and publish studies thereon (“the Purpose”); any other use of the EV Solution Source Code is forbidden, unless a legally valid license is granted by the Owners. 
+Unauthorized reproduction, distribution, transformation, public communication of the EV Solution outside the Purpose is illegal, and may result in civil or criminal liability. 
+
+7.	FAIR USE RESTRICTIONS
+
+The EV Solution Source Code can be accessed for the Purpose under the following fair use rules:
+* Researchers cannot use the EV Solution Source Code for any kind of commercial purposes, including but not limited to, the creation of a product, derivative works of the EV Solution and/or the provision of services.
+* It is forbidden to remove or alter any notices regarding intellectual property rights such as copyright, patents, trademarks or other proprietary rights. 
+* Researchers shall follow the Reporting Procedure to communicate any vulnerabilities, bugs, errors or other issues (“the Vulnerabilities”) found in the EV Solution Source Code and comply with the Responsible Disclosure rules.
+* Researchers shall conduct tests following the deployment, configuration, trust assumptions and infrastructure specifications also published by the Owners. 
+* Researchers shall comply with the applicable law and shall not breach any agreements in order to find Vulnerabilities.
+* Researchers shall not exploit any found Vulnerability on any real elections or votes, or perform any action that is intended to negatively affect the reputation of any of the Owners, any election or vote, electoral authority and any election technology providers and partners.
+* Researchers shall avoid any attempt to modify or destroy data
+.
+
+8.	REPORTING PROCEDURE
+
+Any Vulnerability found in the EV Solution Source Code must be reported in the communication channels provided by the Owners. When communicating via the GitLab platform, Researchers are obliged to submit a report as an issue set explicitly to confidential, and thus make it visible only to team members.
+
+Researchers shall provide sufficient information to reproduce the Vulnerability so that the Owners can act as quickly as possible. Usually, a vulnerability description is sufficient, but for more complex vulnerabilities, more detailed information may be needed.
+
+The Researcher e-mail address must not be managed or accessible by more than one person and the information received in the Researcher e-mail account must not be shared with or forwarded to any other e-mail account.
+
+The Owners will acknowledge reception of the report, and will decide, at their sole discretion, (i) whether the Vulnerability needs correction and (ii) whether to apply any correction reported by the Researcher. 
+The Researcher accepts to provide support to the Owners to verify the potential Vulnerability, and the Owners will update the researcher on any corrections applied.
+
+9.	RESPONSIBLE DISCLOSURE
+
+The Program follows a “responsible disclosure” policy. The following rules apply cumulatively:
+a)	No Vulnerability shall be published without the Researcher having followed previously the Reporting Procedure set out in Clause 8 above;
+b)	No Vulnerability shall be published without the Researcher having at least received the acknowledgment from the Owners on the reported Vulnerability.
+c)	No Vulnerability shall be published within a period of forty five (45) days since the last communication exchanged with the Owners with regards to such potential Vulnerability, unless the Owners have agreed to a shorter period or defined a longer period.
+
+10.	CODE OF CONDUCT
+
+The GitLab Community Code of Conduct (https://about.gitlab.com/community/contribute/code-of-conduct/) applies with respect to the Agreement.
+
+11.	DISCLAIMER
+
+The EV Solution Source Code is provided “as is”. 
+The Owners hereby disclaim any support, representation or warranty of any kind whatsoever, including any implied warranty of fitness for a particular purpose (including the compilation or execution of the source code) or non-infringement.
+
+12.	LIABILITY DISCLAIMER
+
+In no event will the Owners be liable for any damages whatsoever including – but not restricted to – lost revenue or profits or other direct, indirect, special, incidental, cover, or consequential damages arising out of the access to or inability to access the Electronic Voting Solution Source Code, even if advised of the possibility of such damages, except to the extent provided by law (where applicable). 
+
+13.	TERMINATION
+
+The Agreement and Source Code Access expires at the termination of the productive use of the release to which the Program is dedicated. 
+The right to access the EV Solution shall terminate immediately and automatically without notice if the Researcher fails to comply with the terms and conditions under this Agreement. The Researcher shall destroy any copies of the EV Solution Source Code upon termination. 
+The expiry or termination of the Agreement shall not affect the validity of the obligations of the Researcher entered into under the Agreement (including but not limited to the Fair Use Restrictions, the Reporting Procedure and the Responsible Disclosure).
+
+14.	ENTIRE AGREEMENT
+
+This Agreement constitutes the entire agreement between the Owners and the Researcher enabling access to the EV Solution Source Code.
+Researchers will be allowed to access the EV Solution Source Code only if they accept the Agreement by registering for the Program as defined above.
+
+15.	APPLICABLE LAW AND JURISDICTION
+
+This Agreement is governed by and construed in accordance with the laws of Switzerland. 
+Any dispute arising out of or relating to the Agreement shall be submitted and finally resolved by the courts of Berne, Switzerland.

+ 11 - 0
README.md

@@ -0,0 +1,11 @@
+The aim of publishing the source code is to establish and build confidence in the public domain, while obtaining feedback from professional experts and the opportunity to make improvements. Constructive contributions and findings on the source code are always welcome.
+At this stage, we would like to highlight the key points in the terms of use:
+
+*  The OEV (Federal Chancellery Ordinance on Electronic Voting) allows the source code to be examined, modified, compiled and executed for ideational purposes. Publication is organized and regulated in line with this idea.
+*  The published code, including the source code, is the intellectual property of the companies Scytl and Post CH Ltd. The code in question is proprietary and not subject to a free and open source software (FOSS) licence.
+*  Distributing or publishing the access tools, granting access to third parties and sharing the source code are not permitted.
+*  Post CH Ltd must be consulted before any findings may be published. 
+*  All feedback regarding vulnerabilities found in the electronic voting solution source code is welcome. All feedback must be reported on the GitLab.com platform as an issue and set to “confidential”. Swiss Post will incorporate the reported vulnerabilities into the development of the system.
+
+
+Please refer to the [Wiki](https://gitlab.com/swisspost/evoting-solution-int/wikis/home) for complementary infomration as well as a presentation of the published Source Code

BIN
dependencies/bsh-core-2.0b6.jar


BIN
dependencies/cryptography-4.2.1.jar


BIN
dependencies/iaikPkcs11Wrapper-1.4.jar


BIN
dependencies/jakarta-oro-2.0.6-local.jar


BIN
dependencies/log4j-1.3alpha-7-local.jar


BIN
dependencies/ov-keystore-1.3.0-tests.jar


BIN
dependencies/ov-keystore-1.3.0.jar


BIN
dependencies/scytl-gmp-6.1.1-amd64-Linux-gpp-shared.nar


BIN
dependencies/scytl-gmp-6.1.1-amd64-Windows-gpp-shared.nar


BIN
dependencies/scytl-math-1.0.1.jar


BIN
dependencies/scytl-math-native-1.0.1-amd64-Linux-gpp-jni.nar


BIN
dependencies/scytl-math-native-1.0.1-amd64-Windows-gpp-jni.nar


BIN
dependencies/shares-1.0.2.jar


BIN
documentation/Scytl sVote Audit with Control Components.pdf


BIN
documentation/Scytl sVote Protocol Specifications.pdf


BIN
documentation/Scytl_sVote_Software_Architecture.pdf


+ 12 - 0
source-code/Copyright.txt

@@ -0,0 +1,12 @@
+SCYTL SECURE ELECTRONIC VOTING SA (SCYTL) owns and shall retain all rights, title and interests in and to sVote, except for those open source libraries and dependencies which are listed in the 'THIRD-PARTY.txt' file which is part of the source code package.
+
+Except otherwise defined in this copyright notice, SCYTL shall be the sole and only authorized to use and exploit (commercially or non-commercially) sVote, including any and all exploitation rights whether existing or in the future, throughout the world for all the duration of the intellectual property rights, for any purpose and without any limit or restriction.
+
+The use of sVote is only permitted to authorized licensees subject to the terms and conditions of a duly executed and legally binding software license agreement.
+
+Please contact SCYTL for further information on our software license agreements at licensing@scytl.com.
+
+POST CH Ltd (Swiss Post) is the authorized licensee of the sVote, as embedded in the EV Solution, on exclusive basis for the territory of Switzerland including the right to:
+
+(i) Use, license deploy, install and implement the EV Solution and
+(ii) Sub-license the EV Solution to the Swiss Cantons (without further right to sublicensing).

+ 5 - 0
source-code/maven-generic-conf/.gitignore

@@ -0,0 +1,5 @@
+target
+bin
+/.classpath
+/.project
+/.settings

+ 15 - 0
source-code/maven-generic-conf/README.md

@@ -0,0 +1,15 @@
+# maven-generic-conf
+
+Scytl Super POM is the parent project for all Scytl projects.
+
+It is the central point where global configuration is defined.
+
+In maven the dependency is defined as:
+
+```xml
+    <parent>
+        <groupId>com.scytl</groupId>
+        <artifactId>maven-generic-conf</artifactId>
+        <version>1.5.4</version>
+    </parent>
+```

+ 2 - 0
source-code/maven-generic-conf/THIRD-PARTY.txt

@@ -0,0 +1,2 @@
+
+The project has no dependencies.

+ 166 - 0
source-code/maven-generic-conf/etc/eclipse/scytl_checkstyle.xml

@@ -0,0 +1,166 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+	This configuration file was written by the eclipse-cs plugin configuration editor
+-->
+<!--
+Checkstyle-Configuration: ScytlCheckStyle
+Description:
+
+-->
+<!DOCTYPE module PUBLIC "-//Puppy Crawl//DTD Check Configuration 1.2//EN" "http://www.puppycrawl.com/dtds/configuration_1_2.dtd">
+<module name="Checker">
+    <property name="severity" value="warning"/>
+    <module name="TreeWalker">
+        <module name="JavadocMethod">
+            <property name="severity" value="ignore"/>
+            <property name="allowMissingParamTags" value="true"/>
+            <property name="allowMissingThrowsTags" value="true"/>
+            <property name="allowMissingReturnTag" value="true"/>
+        </module>
+        <module name="JavadocType">
+            <property name="severity" value="ignore"/>
+        </module>
+        <module name="JavadocVariable">
+            <property name="severity" value="ignore"/>
+        </module>
+        <module name="JavadocStyle">
+            <property name="severity" value="ignore"/>
+        </module>
+        <module name="ConstantName">
+            <property name="format" value="^[A-Z][A-Z0-9]*(__?[A-Z0-9]+)*$"/>
+        </module>
+        <module name="LocalFinalVariableName"/>
+        <module name="LocalVariableName"/>
+        <module name="MemberName">
+            <property name="format" value="^_[a-z][a-zA-Z0-9]*$"/>
+        </module>
+        <module name="MethodName"/>
+        <module name="PackageName"/>
+        <module name="ParameterName"/>
+        <module name="StaticVariableName">
+            <property name="severity" value="ignore"/>
+        </module>
+        <module name="TypeName"/>
+        <module name="AvoidStarImport"/>
+        <module name="IllegalImport"/>
+        <module name="RedundantImport"/>
+        <module name="UnusedImports"/>
+        <module name="FileLength"/>
+        <module name="LineLength">
+            <property name="severity" value="ignore"/>
+        </module>
+        <module name="MethodLength"/>
+        <module name="ParameterNumber">
+            <property name="severity" value="ignore"/>
+        </module>
+        <module name="EmptyForIteratorPad"/>
+        <module name="MethodParamPad"/>
+        <module name="NoWhitespaceAfter">
+            <property name="tokens" value="BNOT,DEC,DOT,INC,LNOT,UNARY_MINUS,UNARY_PLUS"/>
+        </module>
+        <module name="NoWhitespaceBefore"/>
+        <module name="OperatorWrap"/>
+        <module name="ParenPad"/>
+        <module name="TypecastParenPad">
+            <property name="tokens" value="RPAREN,TYPECAST"/>
+        </module>
+        <module name="TabCharacter"/>
+        <module name="WhitespaceAfter"/>
+        <module name="WhitespaceAround">
+            <property name="tokens" value="ASSIGN,BAND,BAND_ASSIGN,BOR,BOR_ASSIGN,BSR,BSR_ASSIGN,BXOR,BXOR_ASSIGN,COLON,DIV,DIV_ASSIGN,EQUAL,GE,GT,LAND,LCURLY,LE,LITERAL_ASSERT,LITERAL_CATCH,LITERAL_DO,LITERAL_ELSE,LITERAL_FINALLY,LITERAL_FOR,LITERAL_IF,LITERAL_RETURN,LITERAL_SYNCHRONIZED,LITERAL_TRY,LITERAL_WHILE,LOR,LT,MINUS,MINUS_ASSIGN,MOD,MOD_ASSIGN,NOT_EQUAL,PLUS,PLUS_ASSIGN,QUESTION,RCURLY,SL,SLIST,SL_ASSIGN,SR,SR_ASSIGN,STAR,STAR_ASSIGN,LITERAL_ASSERT,TYPE_EXTENSION_AND"/>
+        </module>
+        <module name="ModifierOrder"/>
+        <module name="RedundantModifier"/>
+        <module name="AvoidNestedBlocks"/>
+        <module name="EmptyBlock"/>
+        <module name="LeftCurly"/>
+        <module name="NeedBraces"/>
+        <module name="RightCurly"/>
+        <module name="AvoidInlineConditionals">
+            <property name="severity" value="ignore"/>
+        </module>
+        <module name="DoubleCheckedLocking"/>
+        <module name="EmptyStatement"/>
+        <module name="EqualsHashCode"/>
+        <module name="HiddenField"/>
+        <module name="IllegalInstantiation"/>
+        <module name="InnerAssignment"/>
+        <module name="MagicNumber">
+            <property name="ignoreNumbers" value="-1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10"/>
+        </module>
+        <module name="MissingSwitchDefault"/>
+        <module name="RedundantThrows">
+            <property name="severity" value="ignore"/>
+        </module>
+        <module name="SimplifyBooleanExpression"/>
+        <module name="SimplifyBooleanReturn"/>
+        <module name="DesignForExtension">
+            <property name="severity" value="ignore"/>
+        </module>
+        <module name="FinalClass"/>
+        <module name="HideUtilityClassConstructor"/>
+        <module name="InterfaceIsType">
+            <property name="severity" value="ignore"/>
+        </module>
+        <module name="VisibilityModifier">
+            <metadata name="com.atlassw.tools.eclipse.checkstyle.comment" value="Scytl convention"/>
+            <property name="protectedAllowed" value="true"/>
+        </module>
+        <module name="ArrayTypeStyle"/>
+        <module name="FinalParameters"/>
+        <module name="GenericIllegalRegexp">
+            <property name="severity" value="ignore"/>
+            <property name="format" value="\s+$"/>
+            <property name="message" value="Line has trailing spaces."/>
+        </module>
+        <module name="TodoComment"/>
+        <module name="UpperEll"/>
+        <module name="StaticVariableName">
+            <metadata name="com.atlassw.tools.eclipse.checkstyle.comment" value="Scytl convention"/>
+            <property name="format" value="^_[a-z][a-zA-Z0-9]*$"/>
+        </module>
+        <module name="AnonInnerLength">
+            <property name="max" value="50"/>
+        </module>
+        <module name="ExecutableStatementCount">
+            <property name="tokens" value="INSTANCE_INIT,STATIC_INIT,METHOD_DEF,CTOR_DEF"/>
+        </module>
+        <module name="EmptyForInitializerPad">
+            <property name="severity" value="ignore"/>
+        </module>
+        <module name="CovariantEquals"/>
+        <module name="DefaultComesLast"/>
+        <module name="IllegalCatch"/>
+        <module name="IllegalThrows"/>
+        <module name="JUnitTestCase"/>
+        <module name="ModifiedControlVariable"/>
+        <module name="MultipleStringLiterals"/>
+        <module name="MultipleVariableDeclarations"/>
+        <module name="NestedTryDepth"/>
+        <module name="NestedIfDepth">
+            <property name="max" value="2"/>
+        </module>
+        <module name="PackageDeclaration"/>
+        <module name="SuperFinalize"/>
+        <module name="SuperClone"/>
+        <module name="StringLiteralEquality"/>
+        <module name="ReturnCount">
+            <property name="max" value="3"/>
+        </module>
+        <module name="ClassFanOutComplexity"/>
+        <module name="CyclomaticComplexity"/>
+        <module name="NPathComplexity"/>
+        <module name="ClassDataAbstractionCoupling">
+            <property name="max" value="15"/>
+        </module>
+        <module name="BooleanExpressionComplexity"/>
+        <module name="JavaNCSS">
+            <property name="methodMaximum" value="150"/>
+        </module>
+    </module>
+    <module name="PackageHtml">
+        <property name="severity" value="ignore"/>
+    </module>
+    <module name="NewlineAtEndOfFile"/>
+    <module name="Translation"/>
+</module>

+ 33 - 0
source-code/maven-generic-conf/etc/eclipse/scytl_codetemplates.xml

@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<templates><template id="org.eclipse.jdt.ui.text.codetemplates.gettercomment" name="gettercomment" description="Comment for getter method" context="gettercomment_context" enabled="true" deleted="false" autoinsert="true">/**
+ * @return Returns the ${bare_field_name}.
+ */</template><template id="org.eclipse.jdt.ui.text.codetemplates.settercomment" name="settercomment" description="Comment for setter method" context="settercomment_context" enabled="true" deleted="false" autoinsert="true">/**
+ * @param ${param} The ${bare_field_name} to set.
+ */</template><template id="org.eclipse.jdt.ui.text.codetemplates.constructorcomment" name="constructorcomment" description="Comment for created constructors" context="constructorcomment_context" enabled="true" deleted="false" autoinsert="true">/**
+ * ${tags}
+ */</template><template id="org.eclipse.jdt.ui.text.codetemplates.filecomment" name="filecomment" description="Comment for created Java files" context="filecomment_context" enabled="true" deleted="false" autoinsert="false">/**
+ * $$Id$$
+ * @author &lt;a href="mailto:developer@scytl.com"&gt;developer name&lt;/a&gt;
+ * @date    ${date} ${time}
+ *
+ * Copyright (C) ${year} Scytl Secure Electronic Voting SA
+ *
+ * All rights reserved.
+ *
+ */</template><template id="org.eclipse.jdt.ui.text.codetemplates.typecomment" name="typecomment" description="Comment for created types" context="typecomment_context" enabled="true" deleted="false" autoinsert="true">/**
+ *
+ * ${tags}
+ */</template><template id="org.eclipse.jdt.ui.text.codetemplates.fieldcomment" name="fieldcomment" description="Comment for fields" context="fieldcomment_context" enabled="true" deleted="false" autoinsert="true">/**
+ * 
+ */</template><template id="org.eclipse.jdt.ui.text.codetemplates.methodcomment" name="methodcomment" description="Comment for non-overriding methods" context="methodcomment_context" enabled="true" deleted="false" autoinsert="true">/**
+ * ${tags}
+ */</template><template id="org.eclipse.jdt.ui.text.codetemplates.overridecomment" name="overridecomment" description="Comment for overriding methods" context="overridecomment_context" enabled="true" deleted="false" autoinsert="false">/**
+ * ${see_to_overridden}
+ */</template><template id="org.eclipse.jdt.ui.text.codetemplates.newtype" name="newtype" description="Newly created files" context="newtype_context" enabled="true" deleted="false" autoinsert="true">${filecomment}
+${package_declaration}
+
+${typecomment}
+${type_declaration}</template><template id="org.eclipse.jdt.ui.text.codetemplates.catchblock" name="catchblock" description="Code in new catch blocks" context="catchblock_context" enabled="true" deleted="false" autoinsert="true">// ${todo} Auto-generated catch block
+${exception_var}.printStackTrace();</template><template id="org.eclipse.jdt.ui.text.codetemplates.methodbody" name="methodbody" description="Code in created method stubs" context="methodbody_context" enabled="true" deleted="false" autoinsert="true">// ${todo} Auto-generated method stub
+${body_statement}</template><template id="org.eclipse.jdt.ui.text.codetemplates.constructorbody" name="constructorbody" description="Code in created constructor stubs" context="constructorbody_context" enabled="true" deleted="false" autoinsert="true">${body_statement}
+// ${todo} Auto-generated constructor stub</template><template id="org.eclipse.jdt.ui.text.codetemplates.getterbody" name="getterbody" description="Code in created getters" context="getterbody_context" enabled="true" deleted="false" autoinsert="true">return ${field};</template><template id="org.eclipse.jdt.ui.text.codetemplates.setterbody" name="setterbody" description="Code in created setters" context="setterbody_context" enabled="true" deleted="false" autoinsert="true">${field} = ${param};</template></templates>

+ 251 - 0
source-code/maven-generic-conf/etc/eclipse/scytl_javaformat-1.1.xml

@@ -0,0 +1,251 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<profiles version="10">
+<profile name="Scytl Profile" version="10">
+<setting id="org.eclipse.jdt.core.formatter.align_type_members_on_columns" value="false"/>
+<setting id="org.eclipse.jdt.core.formatter.alignment_for_arguments_in_allocation_expression" value="20"/>
+<setting id="org.eclipse.jdt.core.formatter.alignment_for_arguments_in_enum_constant" value="16"/>
+<setting id="org.eclipse.jdt.core.formatter.alignment_for_arguments_in_explicit_constructor_call" value="20"/>
+<setting id="org.eclipse.jdt.core.formatter.alignment_for_arguments_in_method_invocation" value="20"/>
+<setting id="org.eclipse.jdt.core.formatter.alignment_for_arguments_in_qualified_allocation_expression" value="16"/>
+<setting id="org.eclipse.jdt.core.formatter.alignment_for_assignment" value="20"/>
+<setting id="org.eclipse.jdt.core.formatter.alignment_for_binary_expression" value="20"/>
+<setting id="org.eclipse.jdt.core.formatter.alignment_for_compact_if" value="16"/>
+<setting id="org.eclipse.jdt.core.formatter.alignment_for_conditional_expression" value="80"/>
+<setting id="org.eclipse.jdt.core.formatter.alignment_for_enum_constants" value="0"/>
+<setting id="org.eclipse.jdt.core.formatter.alignment_for_expressions_in_array_initializer" value="16"/>
+<setting id="org.eclipse.jdt.core.formatter.alignment_for_multiple_fields" value="16"/>
+<setting id="org.eclipse.jdt.core.formatter.alignment_for_parameters_in_constructor_declaration" value="16"/>
+<setting id="org.eclipse.jdt.core.formatter.alignment_for_parameters_in_method_declaration" value="16"/>
+<setting id="org.eclipse.jdt.core.formatter.alignment_for_selector_in_method_invocation" value="20"/>
+<setting id="org.eclipse.jdt.core.formatter.alignment_for_superclass_in_type_declaration" value="16"/>
+<setting id="org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_enum_declaration" value="16"/>
+<setting id="org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_type_declaration" value="16"/>
+<setting id="org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_constructor_declaration" value="32"/>
+<setting id="org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_method_declaration" value="32"/>
+<setting id="org.eclipse.jdt.core.formatter.blank_lines_after_imports" value="1"/>
+<setting id="org.eclipse.jdt.core.formatter.blank_lines_after_package" value="1"/>
+<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_field" value="1"/>
+<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_first_class_body_declaration" value="0"/>
+<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_imports" value="1"/>
+<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_member_type" value="1"/>
+<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_method" value="1"/>
+<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_new_chunk" value="1"/>
+<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_package" value="0"/>
+<setting id="org.eclipse.jdt.core.formatter.blank_lines_between_type_declarations" value="1"/>
+<setting id="org.eclipse.jdt.core.formatter.brace_position_for_annotation_type_declaration" value="end_of_line"/>
+<setting id="org.eclipse.jdt.core.formatter.brace_position_for_anonymous_type_declaration" value="end_of_line"/>
+<setting id="org.eclipse.jdt.core.formatter.brace_position_for_array_initializer" value="end_of_line"/>
+<setting id="org.eclipse.jdt.core.formatter.brace_position_for_block" value="end_of_line"/>
+<setting id="org.eclipse.jdt.core.formatter.brace_position_for_block_in_case" value="end_of_line"/>
+<setting id="org.eclipse.jdt.core.formatter.brace_position_for_constructor_declaration" value="end_of_line"/>
+<setting id="org.eclipse.jdt.core.formatter.brace_position_for_enum_constant" value="end_of_line"/>
+<setting id="org.eclipse.jdt.core.formatter.brace_position_for_enum_declaration" value="end_of_line"/>
+<setting id="org.eclipse.jdt.core.formatter.brace_position_for_method_declaration" value="end_of_line"/>
+<setting id="org.eclipse.jdt.core.formatter.brace_position_for_switch" value="end_of_line"/>
+<setting id="org.eclipse.jdt.core.formatter.brace_position_for_type_declaration" value="end_of_line"/>
+<setting id="org.eclipse.jdt.core.formatter.comment.clear_blank_lines" value="true"/>
+<setting id="org.eclipse.jdt.core.formatter.comment.format_comments" value="true"/>
+<setting id="org.eclipse.jdt.core.formatter.comment.format_header" value="false"/>
+<setting id="org.eclipse.jdt.core.formatter.comment.format_html" value="true"/>
+<setting id="org.eclipse.jdt.core.formatter.comment.format_source_code" value="false"/>
+<setting id="org.eclipse.jdt.core.formatter.comment.indent_parameter_description" value="true"/>
+<setting id="org.eclipse.jdt.core.formatter.comment.indent_root_tags" value="true"/>
+<setting id="org.eclipse.jdt.core.formatter.comment.insert_new_line_before_root_tags" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.comment.insert_new_line_for_parameter" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.comment.line_length" value="80"/>
+<setting id="org.eclipse.jdt.core.formatter.compact_else_if" value="true"/>
+<setting id="org.eclipse.jdt.core.formatter.continuation_indentation" value="2"/>
+<setting id="org.eclipse.jdt.core.formatter.continuation_indentation_for_array_initializer" value="2"/>
+<setting id="org.eclipse.jdt.core.formatter.format_guardian_clause_on_one_line" value="false"/>
+<setting id="org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_annotation_declaration_header" value="true"/>
+<setting id="org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_constant_header" value="true"/>
+<setting id="org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_declaration_header" value="true"/>
+<setting id="org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_type_header" value="true"/>
+<setting id="org.eclipse.jdt.core.formatter.indent_breaks_compare_to_cases" value="true"/>
+<setting id="org.eclipse.jdt.core.formatter.indent_empty_lines" value="false"/>
+<setting id="org.eclipse.jdt.core.formatter.indent_statements_compare_to_block" value="true"/>
+<setting id="org.eclipse.jdt.core.formatter.indent_statements_compare_to_body" value="true"/>
+<setting id="org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_cases" value="true"/>
+<setting id="org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_switch" value="false"/>
+<setting id="org.eclipse.jdt.core.formatter.indentation.size" value="4"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_annotation" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_opening_brace_in_array_initializer" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_new_line_at_end_of_file_if_missing" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_new_line_before_catch_in_try_statement" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_new_line_before_closing_brace_in_array_initializer" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_new_line_before_else_in_if_statement" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_new_line_before_finally_in_try_statement" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_new_line_before_while_in_do_statement" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_new_line_in_empty_annotation_declaration" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_new_line_in_empty_anonymous_type_declaration" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_new_line_in_empty_block" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_constant" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_declaration" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_new_line_in_empty_method_body" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_new_line_in_empty_type_declaration" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_and_in_type_parameter" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_assignment_operator" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation_type_declaration" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_binary_operator" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_arguments" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_parameters" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_closing_brace_in_block" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_closing_paren_in_cast" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_colon_in_assert" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_colon_in_case" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_colon_in_conditional" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_colon_in_for" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_colon_in_labeled_statement" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_allocation_expression" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_annotation" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_array_initializer" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_parameters" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_throws" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_constant_arguments" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_declarations" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_explicitconstructorcall_arguments" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_increments" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_inits" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_parameters" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_throws" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_invocation_arguments" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_field_declarations" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_local_declarations" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_parameterized_type_reference" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_superinterfaces" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_arguments" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_parameters" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_ellipsis" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_parameterized_type_reference" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_arguments" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_parameters" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_brace_in_array_initializer" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_allocation_expression" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_reference" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_annotation" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_cast" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_catch" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_constructor_declaration" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_enum_constant" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_for" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_if" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_declaration" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_invocation" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_parenthesized_expression" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_switch" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_synchronized" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_while" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_postfix_operator" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_prefix_operator" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_question_in_conditional" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_question_in_wildcard" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_for" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_unary_operator" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_and_in_type_parameter" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_assignment_operator" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_at_in_annotation_type_declaration" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_binary_operator" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_parameterized_type_reference" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_arguments" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_parameters" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_brace_in_array_initializer" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_allocation_expression" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_reference" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_annotation" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_cast" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_catch" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_constructor_declaration" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_enum_constant" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_for" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_if" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_declaration" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_invocation" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_parenthesized_expression" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_switch" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_synchronized" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_while" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_colon_in_assert" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_colon_in_case" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_colon_in_conditional" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_colon_in_default" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_colon_in_for" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_colon_in_labeled_statement" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_allocation_expression" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_annotation" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_array_initializer" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_parameters" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_throws" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_constant_arguments" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_declarations" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_explicitconstructorcall_arguments" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_increments" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_inits" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_parameters" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_throws" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_invocation_arguments" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_field_declarations" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_local_declarations" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_parameterized_type_reference" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_superinterfaces" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_arguments" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_parameters" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_ellipsis" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_parameterized_type_reference" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_arguments" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_parameters" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_annotation_type_declaration" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_anonymous_type_declaration" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_array_initializer" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_block" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_constructor_declaration" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_constant" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_declaration" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_method_declaration" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_switch" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_type_declaration" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_allocation_expression" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_reference" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_type_reference" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation_type_member_declaration" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_catch" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_constructor_declaration" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_enum_constant" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_for" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_if" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_declaration" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_invocation" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_parenthesized_expression" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_switch" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_synchronized" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_while" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_return" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_postfix_operator" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_prefix_operator" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_question_in_conditional" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_question_in_wildcard" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_semicolon" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_for" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_unary_operator" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_between_brackets_in_array_type_reference" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_braces_in_array_initializer" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_brackets_in_array_allocation_expression" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_annotation_type_member_declaration" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_constructor_declaration" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_enum_constant" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_declaration" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_invocation" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.keep_else_statement_on_same_line" value="false"/>
+<setting id="org.eclipse.jdt.core.formatter.keep_empty_array_initializer_on_one_line" value="false"/>
+<setting id="org.eclipse.jdt.core.formatter.keep_imple_if_on_one_line" value="false"/>
+<setting id="org.eclipse.jdt.core.formatter.keep_then_statement_on_same_line" value="false"/>
+<setting id="org.eclipse.jdt.core.formatter.lineSplit" value="75"/>
+<setting id="org.eclipse.jdt.core.formatter.number_of_blank_lines_at_beginning_of_method_body" value="0"/>
+<setting id="org.eclipse.jdt.core.formatter.number_of_empty_lines_to_preserve" value="1"/>
+<setting id="org.eclipse.jdt.core.formatter.put_empty_statement_on_new_line" value="true"/>
+<setting id="org.eclipse.jdt.core.formatter.tabulation.char" value="space"/>
+<setting id="org.eclipse.jdt.core.formatter.tabulation.size" value="4"/>
+<setting id="org.eclipse.jdt.core.formatter.use_tabs_only_for_leading_indentations" value="false"/>
+</profile>
+</profiles>

+ 4 - 0
source-code/maven-generic-conf/extlibs/barcode4j/INSTALL.txt

@@ -0,0 +1,4 @@
+To install the file do the following:
+
+  alias mvndeploy "mvn deploy:deploy-file -Durl=scpexe://pnyx@nightly01/opt/jakarta-tomcat-5.0.28/webapps/maven-repos"
+  mvndeploy -DgroupId=com.scytl.extlibs -DartifactId=barcode4j -Dversion=2.0alpha2 -Dpackaging=jar -Dfile=barcode4j.jar

BIN
source-code/maven-generic-conf/extlibs/barcode4j/barcode4j.jar


+ 11 - 0
source-code/maven-generic-conf/extlibs/ec2-plugin/.gitignore

@@ -0,0 +1,11 @@
+*.iml
+*.ipr
+*.iws
+target
+
+.classpath
+.project
+.settings
+
+/bin
+/work

+ 2 - 0
source-code/maven-generic-conf/extlibs/ec2-plugin/TODO.txt

@@ -0,0 +1,2 @@
+- the uptime should be displayed there
+- cloud configuration page should be moved elsewhere, like how we configure slaves

+ 75 - 0
source-code/maven-generic-conf/extlibs/ec2-plugin/pom.xml

@@ -0,0 +1,75 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+
+  <parent>
+    <groupId>org.jenkins-ci.plugins</groupId>
+    <artifactId>plugin</artifactId>
+      <version>1.424</version>
+  </parent>
+  
+  <artifactId>ec2</artifactId>
+  <version>1.18-SNAPSHOT</version>
+  <packaging>hpi</packaging>
+  <name>Amazon EC2 plugin</name>
+  <url>http://wiki.jenkins-ci.org/display/JENKINS/Amazon+EC2+Plugin</url>
+
+  <scm>
+    <connection>scm:git:git://github.com/jenkinsci/ec2-plugin.git</connection>
+    <developerConnection>scm:git:git@github.com:jenkinsci/ec2-plugin.git</developerConnection>
+  </scm>
+
+  <dependencies>
+    <dependency>
+      <!-- we only use this to handle key fingerprint. should be able to replace this with trilead -->
+      <groupId>bouncycastle</groupId>
+      <artifactId>bcprov-jdk15</artifactId>
+      <version>140</version>
+    </dependency>
+    <dependency>
+      <groupId>com.amazonaws</groupId>
+      <artifactId>aws-java-sdk</artifactId>
+      <version>1.3.7</version>
+      <exclusions>
+        <exclusion>
+          <groupId>commons-codec</groupId>
+          <artifactId>commons-codec</artifactId>
+        </exclusion>
+      </exclusions>
+    </dependency>
+  </dependencies>
+
+  <developers>
+	<!-- current maintainer -->
+    <developer>
+      <id>francisu</id>
+      <name>Francis Upton IV</name>
+      <email>francis@oaklandsoftware.com</email>
+    </developer>
+	<!-- kohsuke did the original implementation -->
+    <developer>
+      <id>kohsuke</id>
+      <name>Kohsuke Kawaguchi</name>
+      <email>kkawaguchi@cloudbees.com</email>
+    </developer>
+  </developers>
+
+
+    <repositories>
+        <repository>
+            <id>repo.jenkins-ci.org</id>
+            <url>http://repo.jenkins-ci.org/public/</url>
+        </repository>
+    </repositories>
+
+    <pluginRepositories>
+        <pluginRepository>
+            <id>repo.jenkins-ci.org</id>
+            <url>http://repo.jenkins-ci.org/public/</url>
+        </pluginRepository>
+    </pluginRepositories>
+</project>  
+  
+
+
+
+

+ 118 - 0
source-code/maven-generic-conf/extlibs/ec2-plugin/src/main/java/hudson/plugins/ec2/AmazonEC2Cloud.java

@@ -0,0 +1,118 @@
+package hudson.plugins.ec2;
+
+import hudson.Extension;
+import hudson.util.FormValidation;
+import hudson.util.ListBoxModel;
+
+import java.io.IOException;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.List;
+import java.util.Locale;
+
+import javax.servlet.ServletException;
+
+import org.apache.commons.lang.StringUtils;
+import org.kohsuke.stapler.DataBoundConstructor;
+import org.kohsuke.stapler.QueryParameter;
+import org.kohsuke.stapler.StaplerResponse;
+
+import com.amazonaws.services.ec2.AmazonEC2;
+import com.amazonaws.services.ec2.model.DescribeRegionsResult;
+import com.amazonaws.services.ec2.model.Region;
+
+/**
+ * The original implementation of {@link EC2Cloud}.
+ *
+ * @author Kohsuke Kawaguchi
+ */
+public class AmazonEC2Cloud extends EC2Cloud {
+    /**
+     * Represents the region. Can be null for backward compatibility reasons.
+     */
+    private String region;
+
+    // Used when running unit tests
+    public static boolean testMode;
+    
+    
+    @DataBoundConstructor
+    public AmazonEC2Cloud(String accessId, String secretKey, String region, String privateKey, String instanceCapStr, List<SlaveTemplate> templates) {
+        super("ec2-"+region, accessId, secretKey, privateKey, instanceCapStr, templates);
+        this.region = region;
+    }
+
+    public String getRegion() {
+        if (region == null)
+            region = DEFAULT_EC2_HOST; // Backward compatibility
+        // Handles pre 1.14 region names that used the old AwsRegion enum, note we don't change
+        // the region here to keep the meta-data compatible in the case of a downgrade (is that right?)
+        if (region.indexOf('_') > 0)
+        	return region.replace('_', '-').toLowerCase(Locale.ENGLISH);
+        return region;
+    }
+
+    public static URL getEc2EndpointUrl(String region) {
+        try {
+			return new URL("https://" + region + "." + EC2_URL_HOST + "/");
+		} catch (MalformedURLException e) {
+			throw new Error(e); // Impossible
+		}
+    }
+    
+    @Override
+    public URL getEc2EndpointUrl() {
+    	return getEc2EndpointUrl(getRegion());
+    }
+
+    @Override
+    public URL getS3EndpointUrl() {
+        try {
+			return new URL("https://"+getRegion()+".s3.amazonaws.com/");
+		} catch (MalformedURLException e) {
+			throw new Error(e); // Impossible
+		}
+    }
+
+    @Extension
+    public static class DescriptorImpl extends EC2Cloud.DescriptorImpl {
+        @Override
+		public String getDisplayName() {
+            return "Amazon EC2";
+        }
+
+		public ListBoxModel doFillRegionItems(@QueryParameter String accessId,
+				@QueryParameter String secretKey) throws IOException,
+				ServletException {
+			ListBoxModel model = new ListBoxModel();
+			if (testMode) {
+				model.add(DEFAULT_EC2_HOST);
+				return model;
+			}
+				
+			if (!StringUtils.isEmpty(accessId) && !StringUtils.isEmpty(secretKey)) {
+				AmazonEC2 client = connect(accessId, secretKey, new URL(
+						"http://ec2.amazonaws.com"));
+				DescribeRegionsResult regions = client.describeRegions();
+				List<Region> regionList = regions.getRegions();
+				for (Region r : regionList) {
+					model.add(r.getRegionName(), r.getRegionName());
+				}
+			}
+			return model;
+		}
+
+        public FormValidation doTestConnection(
+                @QueryParameter String region,
+                 @QueryParameter String accessId,
+                 @QueryParameter String secretKey,
+                 @QueryParameter String privateKey) throws IOException, ServletException {
+            return super.doTestConnection(getEc2EndpointUrl(region),accessId,secretKey,privateKey);
+        }
+
+        public FormValidation doGenerateKey(
+                StaplerResponse rsp, @QueryParameter String region, @QueryParameter String accessId, @QueryParameter String secretKey) throws IOException, ServletException {
+            return super.doGenerateKey(rsp,getEc2EndpointUrl(region),accessId,secretKey);
+        }
+    }
+}

+ 459 - 0
source-code/maven-generic-conf/extlibs/ec2-plugin/src/main/java/hudson/plugins/ec2/EC2Cloud.java

@@ -0,0 +1,459 @@
+package hudson.plugins.ec2;
+
+import hudson.model.Computer;
+import hudson.model.Descriptor;
+import hudson.model.Hudson;
+import hudson.model.Label;
+import hudson.model.Node;
+import hudson.slaves.Cloud;
+import hudson.slaves.NodeProvisioner.PlannedNode;
+import hudson.util.FormValidation;
+import hudson.util.Secret;
+import hudson.util.StreamTaskListener;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.StringReader;
+import java.io.StringWriter;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Date;
+import java.util.List;
+import java.util.concurrent.Callable;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import javax.servlet.ServletException;
+
+import org.kohsuke.stapler.QueryParameter;
+import org.kohsuke.stapler.StaplerRequest;
+import org.kohsuke.stapler.StaplerResponse;
+
+import com.amazonaws.AmazonClientException;
+import com.amazonaws.auth.AWSCredentials;
+import com.amazonaws.auth.BasicAWSCredentials;
+import com.amazonaws.services.ec2.AmazonEC2;
+import com.amazonaws.services.ec2.AmazonEC2Client;
+import com.amazonaws.services.ec2.model.CreateKeyPairRequest;
+import com.amazonaws.services.ec2.model.Instance;
+import com.amazonaws.services.ec2.model.InstanceStateName;
+import com.amazonaws.services.ec2.model.InstanceType;
+import com.amazonaws.services.ec2.model.KeyPair;
+import com.amazonaws.services.ec2.model.KeyPairInfo;
+import com.amazonaws.services.ec2.model.Reservation;
+import com.amazonaws.services.s3.AmazonS3;
+import com.amazonaws.services.s3.AmazonS3Client;
+import com.amazonaws.services.s3.model.GeneratePresignedUrlRequest;
+
+
+/**
+ * Hudson's view of EC2. 
+ *
+ * @author Kohsuke Kawaguchi
+ */
+public abstract class EC2Cloud extends Cloud {
+
+	public static final String DEFAULT_EC2_HOST = "us-east-1";
+	public static final String EC2_URL_HOST = "ec2.amazonaws.com";
+	
+    private final String accessId;
+    private final Secret secretKey;
+    private final EC2PrivateKey privateKey;
+
+    /**
+     * Upper bound on how many instances we may provision.
+     */
+    public final int instanceCap;
+    private final List<SlaveTemplate> templates;
+    private transient KeyPair usableKeyPair;
+
+    private transient AmazonEC2 connection;
+    
+	private static AWSCredentials awsCredentials;
+    
+    protected EC2Cloud(String id, String accessId, String secretKey, String privateKey, String instanceCapStr, List<SlaveTemplate> templates) {
+        super(id);
+        this.accessId = accessId.trim();
+        this.secretKey = Secret.fromString(secretKey.trim());
+        this.privateKey = new EC2PrivateKey(privateKey);
+
+        if(templates==null) {
+            this.templates=Collections.emptyList();
+        } else {
+            this.templates=templates;
+        }
+
+        if(instanceCapStr.equals("")) {
+            this.instanceCap = Integer.MAX_VALUE;
+        } else {
+            this.instanceCap = Integer.parseInt(instanceCapStr);
+        }
+
+        readResolve(); // set parents
+    }
+
+    public abstract URL getEc2EndpointUrl() throws IOException;
+    public abstract URL getS3EndpointUrl() throws IOException;
+
+    protected Object readResolve() {
+        for (SlaveTemplate t : templates)
+            t.parent = this;
+        return this;
+    }
+
+    public String getAccessId() {
+        return accessId;
+    }
+
+    public String getSecretKey() {
+        return secretKey.getEncryptedValue();
+    }
+
+    public EC2PrivateKey getPrivateKey() {
+        return privateKey;
+    }
+
+    public String getInstanceCapStr() {
+        if(instanceCap==Integer.MAX_VALUE)
+            return "";
+        else
+            return String.valueOf(instanceCap);
+    }
+
+    public List<SlaveTemplate> getTemplates() {
+        return Collections.unmodifiableList(templates);
+    }
+
+    public SlaveTemplate getTemplate(String ami) {
+        for (SlaveTemplate t : templates)
+            if(t.ami.equals(ami))
+                return t;
+        return null;
+    }
+
+    /**
+     * Gets {@link SlaveTemplate} that has the matching {@link Label}.
+     */
+    public SlaveTemplate getTemplate(Label label) {
+        for (SlaveTemplate t : templates)
+        	if(label == null || label.matches(t.getLabelSet()))
+                return t;
+        return null;
+    }
+
+    /**
+     * Gets the {@link KeyPairInfo} used for the launch.
+     */
+    public synchronized KeyPair getKeyPair() throws AmazonClientException, IOException {
+        if(usableKeyPair==null)
+            usableKeyPair = privateKey.find(connect());
+        return usableKeyPair;
+    }
+
+    /**
+     * Counts the number of instances in EC2 currently running that are using the specifed image.
+     *
+     * @param ami If AMI is left null, then all instances are counted.
+     * <p>
+     * This includes those instances that may be started outside Hudson.
+     */
+    public int countCurrentEC2Slaves(String ami) throws AmazonClientException {
+        int n=0;
+        for (Reservation r : connect().describeInstances().getReservations()) {
+            for (Instance i : r.getInstances()) {
+                if (ami == null || ami.equals(i.getImageId())) {
+                    InstanceStateName stateName = InstanceStateName.fromValue(i.getState().getName());
+                    if (stateName == InstanceStateName.Pending || stateName == InstanceStateName.Running)
+                        n++;
+                }
+            }
+        }
+        return n;
+    }
+
+    /**
+     * Debug command to attach to a running instance.
+     */
+    public void doAttach(StaplerRequest req, StaplerResponse rsp, @QueryParameter String id) throws ServletException, IOException, AmazonClientException {
+        checkPermission(PROVISION);
+        SlaveTemplate t = getTemplates().get(0);
+
+        StringWriter sw = new StringWriter();
+        StreamTaskListener listener = new StreamTaskListener(sw);
+        EC2Slave node = t.attach(id,listener);
+        Hudson.getInstance().addNode(node);
+
+        rsp.sendRedirect2(req.getContextPath()+"/computer/"+node.getNodeName());
+    }
+
+    public void doProvision(StaplerRequest req, StaplerResponse rsp, @QueryParameter String ami) throws ServletException, IOException {
+        checkPermission(PROVISION);
+        if(ami==null) {
+            sendError("The 'ami' query parameter is missing",req,rsp);
+            return;
+        }
+        SlaveTemplate t = getTemplate(ami);
+        if(t==null) {
+            sendError("No such AMI: "+ami,req,rsp);
+            return;
+        }
+
+        StringWriter sw = new StringWriter();
+        StreamTaskListener listener = new StreamTaskListener(sw);
+        try {
+            EC2Slave node = t.provision(listener);
+            Hudson.getInstance().addNode(node);
+
+            rsp.sendRedirect2(req.getContextPath()+"/computer/"+node.getNodeName());
+        } catch (AmazonClientException e) {
+            e.printStackTrace(listener.error(e.getMessage()));
+            sendError(sw.toString(),req,rsp);
+        }
+    }
+
+    @Override
+	public Collection<PlannedNode> provision(Label label, int excessWorkload) {
+        try {
+
+
+        	final SlaveTemplate t = getTemplate(label);
+
+            List<PlannedNode> r = new ArrayList<PlannedNode>();
+            for( ; excessWorkload>0; excessWorkload-- ) {
+                if(countCurrentEC2Slaves(null)>=instanceCap) {
+                    LOGGER.log(Level.INFO, "Instance cap reached, not provisioning.");
+                    break;      // maxed out
+                }
+
+                int amiCap = t.getInstanceCap();
+                if (amiCap < countCurrentEC2Slaves(t.ami)) {
+                    LOGGER.log(Level.INFO, "AMI Instance cap reached, not provisioning.");
+                    break;      // maxed out
+                }
+
+
+                r.add(new PlannedNode(t.getDisplayName(),
+                        Computer.threadPoolForRemoting.submit(new Callable<Node>() {
+                            public Node call() throws Exception {
+                                // TODO: record the output somewhere
+                                EC2Slave s = t.provision(new StreamTaskListener(System.out));
+                                Hudson.getInstance().addNode(s);
+                                // EC2 instances may have a long init script. If we declare
+                                // the provisioning complete by returning without the connect
+                                // operation, NodeProvisioner may decide that it still wants
+                                // one more instance, because it sees that (1) all the slaves
+                                // are offline (because it's still being launched) and
+                                // (2) there's no capacity provisioned yet.
+                                //
+                                // deferring the completion of provisioning until the launch
+                                // goes successful prevents this problem.
+                                s.toComputer().connect(false).get();
+                                return s;
+                            }
+                        })
+                        ,t.getNumExecutors()));
+            }
+            return r;
+        } catch (AmazonClientException e) {
+            LOGGER.log(Level.WARNING,"Failed to count the # of live instances on EC2",e);
+            return Collections.emptyList();
+        }
+    }
+
+    @Override
+	public boolean canProvision(Label label) {
+        return getTemplate(label)!=null;
+    }
+
+    /**
+     * Gets the first {@link EC2Cloud} instance configured in the current Hudson, or null if no such thing exists.
+     */
+    public static EC2Cloud get() {
+        return Hudson.getInstance().clouds.get(EC2Cloud.class);
+    }
+
+    /**
+     * Connects to EC2 and returns {@link AmazonEC2}, which can then be used to communicate with EC2.
+     */
+    public synchronized AmazonEC2 connect() throws AmazonClientException {
+        try {
+            if (connection == null) {
+                connection = connect(accessId, secretKey, getEc2EndpointUrl());
+            }
+            return connection;
+        } catch (IOException e) {
+            throw new AmazonClientException("Failed to retrieve the endpoint",e);
+        }
+    }
+
+    /***
+     * Connect to an EC2 instance.
+     * @return {@link AmazonEC2} client
+     */
+    public static AmazonEC2 connect(String accessId, String secretKey, URL endpoint) {
+        return connect(accessId, Secret.fromString(secretKey), endpoint);
+    }
+
+    /***
+     * Connect to an EC2 instance.
+     * @return {@link AmazonEC2} client
+     */
+    public static AmazonEC2 connect(String accessId, Secret secretKey, URL endpoint) {
+    	awsCredentials = new BasicAWSCredentials(accessId, Secret.toString(secretKey));
+        AmazonEC2 client = new AmazonEC2Client(awsCredentials);
+        client.setEndpoint(endpoint.toString());
+        return client;
+    }
+
+    /***
+     * Convert a configured hostname like 'us-east-1' to a FQDN or ip address
+     */
+    public static String convertHostName(String ec2HostName) {
+        if (ec2HostName == null || ec2HostName.length()==0)
+            ec2HostName = DEFAULT_EC2_HOST;
+        if (!ec2HostName.contains("."))
+            ec2HostName = ec2HostName + "." + EC2_URL_HOST;
+	return ec2HostName;
+    }
+
+    /***
+     * Convert a user entered string into a port number
+     * "" -> -1 to indicate default based on SSL setting
+     */
+    public static Integer convertPort(String ec2Port) {
+        if (ec2Port == null || ec2Port.length() == 0)
+            return -1;
+        else
+            return Integer.parseInt(ec2Port);
+    }
+
+    /**
+     * Computes the presigned URL for the given S3 resource.
+     *
+     * @param path
+     *      String like "/bucketName/folder/folder/abc.txt" that represents the resource to request.
+     */
+    public URL buildPresignedURL(String path) throws IOException, AmazonClientException {
+        long expires = System.currentTimeMillis()+60*60*1000;
+        GeneratePresignedUrlRequest request = new GeneratePresignedUrlRequest(path, Secret.toString(secretKey));
+        request.setExpiration(new Date(expires));
+        AmazonS3 s3 = new AmazonS3Client(awsCredentials);
+        return s3.generatePresignedUrl(request);
+    }
+
+    /* Parse a url or return a sensible error */
+    public static URL checkEndPoint(String url) throws FormValidation {
+        try {
+            return new URL(url);
+        } catch (MalformedURLException ex) {
+            throw FormValidation.error("Endpoint URL is not a valid URL");
+        }
+    }
+
+
+    public static abstract class DescriptorImpl extends Descriptor<Cloud> {
+        public InstanceType[] getInstanceTypes() {
+            return InstanceType.values();
+        }
+
+        public FormValidation doCheckAccessId(@QueryParameter String value) throws IOException, ServletException {
+            return FormValidation.validateBase64(value,false,false,Messages.EC2Cloud_InvalidAccessId());
+        }
+
+        public FormValidation doCheckSecretKey(@QueryParameter String value) throws IOException, ServletException {
+            return FormValidation.validateBase64(value,false,false,Messages.EC2Cloud_InvalidSecretKey());
+        }
+
+        public FormValidation doCheckPrivateKey(@QueryParameter String value) throws IOException, ServletException {
+            boolean hasStart=false,hasEnd=false;
+            BufferedReader br = new BufferedReader(new StringReader(value));
+            String line;
+            while ((line = br.readLine()) != null) {
+                if (line.equals("-----BEGIN RSA PRIVATE KEY-----"))
+                    hasStart=true;
+                if (line.equals("-----END RSA PRIVATE KEY-----"))
+                    hasEnd=true;
+            }
+            if(!hasStart)
+                return FormValidation.error("This doesn't look like a private key at all");
+            if(!hasEnd)
+                return FormValidation.error("The private key is missing the trailing 'END RSA PRIVATE KEY' marker. Copy&paste error?");
+            return FormValidation.ok();
+        }
+
+        protected FormValidation doTestConnection( URL ec2endpoint,
+                                     String accessId, String secretKey, String privateKey) throws IOException, ServletException {
+            try {
+                AmazonEC2 ec2 = connect(accessId, secretKey, ec2endpoint);
+                ec2.describeInstances();
+
+                if(accessId==null)
+                    return FormValidation.error("Access ID is not specified");
+                if(secretKey==null)
+                    return FormValidation.error("Secret key is not specified");
+                if(privateKey==null)
+                    return FormValidation.error("Private key is not specified. Click 'Generate Key' to generate one.");
+
+                if(privateKey.trim().length()>0) {
+                    // check if this key exists
+                    EC2PrivateKey pk = new EC2PrivateKey(privateKey);
+                    if(pk.find(ec2)==null)
+                        return FormValidation.error("The EC2 key pair private key isn't registered to this EC2 region (fingerprint is "+pk.getFingerprint()+")");
+                }
+
+                return FormValidation.ok(Messages.EC2Cloud_Success());
+            } catch (AmazonClientException e) {
+                LOGGER.log(Level.WARNING, "Failed to check EC2 credential",e);
+                return FormValidation.error(e.getMessage());
+            }
+        }
+
+        public FormValidation doGenerateKey(StaplerResponse rsp, URL ec2EndpointUrl, String accessId, String secretKey
+        ) throws IOException, ServletException {
+            try {
+                AmazonEC2 ec2 = connect(accessId, secretKey, ec2EndpointUrl);
+                List<KeyPairInfo> existingKeys = ec2.describeKeyPairs().getKeyPairs();
+
+                int n = 0;
+                while(true) {
+                    boolean found = false;
+                    for (KeyPairInfo k : existingKeys) {
+                        if(k.getKeyName().equals("hudson-"+n))
+                            found=true;
+                    }
+                    if(!found)
+                        break;
+                    n++;
+                }
+
+                CreateKeyPairRequest request = new CreateKeyPairRequest("hudson-" + n);
+                KeyPair key = ec2.createKeyPair(request).getKeyPair();
+
+
+                rsp.addHeader("script","findPreviousFormItem(button,'privateKey').value='"+key.getKeyMaterial().replace("\n","\\n")+"'");
+
+                return FormValidation.ok(Messages.EC2Cloud_Success());
+            } catch (AmazonClientException e) {
+                LOGGER.log(Level.WARNING, "Failed to check EC2 credential",e);
+                return FormValidation.error(e.getMessage());
+            }
+        }
+    }
+
+    private static final Logger LOGGER = Logger.getLogger(EC2Cloud.class.getName());
+
+    private static boolean isSSL(URL endpoint) {
+        return endpoint.getProtocol().equals("https");
+    }
+
+    private static int portFromURL(URL endpoint) {
+        int ec2Port = endpoint.getPort();
+        if (ec2Port == -1) {
+            ec2Port = endpoint.getDefaultPort();
+        }
+        return ec2Port;
+    }
+}

+ 126 - 0
source-code/maven-generic-conf/extlibs/ec2-plugin/src/main/java/hudson/plugins/ec2/EC2Computer.java

@@ -0,0 +1,126 @@
+package hudson.plugins.ec2;
+
+import hudson.Util;
+import hudson.slaves.SlaveComputer;
+import java.io.IOException;
+import java.util.Collections;
+
+import org.kohsuke.stapler.HttpRedirect;
+import org.kohsuke.stapler.HttpResponse;
+import com.amazonaws.AmazonClientException;
+import com.amazonaws.services.ec2.AmazonEC2;
+import com.amazonaws.services.ec2.model.DescribeInstancesRequest;
+import com.amazonaws.services.ec2.model.GetConsoleOutputRequest;
+import com.amazonaws.services.ec2.model.Instance;
+
+/**
+ * @author Kohsuke Kawaguchi
+ */
+public class EC2Computer extends SlaveComputer {
+    /**
+     * Cached description of this EC2 instance. Lazily fetched.
+     */
+    private volatile Instance ec2InstanceDescription;
+
+    public EC2Computer(EC2Slave slave) {
+        super(slave);
+    }
+
+    @Override
+    public EC2Slave getNode() {
+        return (EC2Slave)super.getNode();
+    }
+
+    public String getInstanceId() {
+        return getName();
+    }
+
+    /**
+     * Gets the EC2 console output.
+     */
+    public String getConsoleOutput() throws AmazonClientException {
+        AmazonEC2 ec2 = EC2Cloud.get().connect();
+        GetConsoleOutputRequest request = new GetConsoleOutputRequest(getInstanceId());
+        return ec2.getConsoleOutput(request).getOutput();
+    }
+
+    /**
+     * Obtains the instance state description in EC2.
+     *
+     * <p>
+     * This method returns a cached state, so it's not suitable to check {@link Instance#getState()}
+     * and {@link Instance#getStateCode()} from the returned instance (but all the other fields are valid as it won't change.)
+     *
+     * The cache can be flushed using {@link #updateInstanceDescription()}
+     */
+    public Instance describeInstance() throws AmazonClientException {
+        if(ec2InstanceDescription==null)
+            ec2InstanceDescription = _describeInstance();
+        return ec2InstanceDescription;
+    }
+
+    /**
+     * This will flush any cached description held by {@link #describeInstance()}.
+     */
+    public Instance updateInstanceDescription() throws AmazonClientException {
+        return ec2InstanceDescription = _describeInstance();
+    }
+
+    /**
+     * Gets the current state of the instance.
+     *
+     * <p>
+     * Unlike {@link #describeInstance()}, this method always return the current status by calling EC2.
+     */
+    public InstanceState getState() throws AmazonClientException {
+        ec2InstanceDescription=_describeInstance();
+        return InstanceState.find(ec2InstanceDescription.getState().getName());
+    }
+
+    /**
+     * Number of milli-secs since the instance was started.
+     */
+    public long getUptime() throws AmazonClientException {
+        return System.currentTimeMillis()-describeInstance().getLaunchTime().getTime();
+    }
+
+    /**
+     * Returns uptime in the human readable form.
+     */
+    public String getUptimeString() throws AmazonClientException {
+        return Util.getTimeSpanString(getUptime());
+    }
+
+    private Instance _describeInstance() throws AmazonClientException {
+    	DescribeInstancesRequest request = new DescribeInstancesRequest();
+    	request.setInstanceIds(Collections.<String>singletonList(getNode().getInstanceId()));
+        return EC2Cloud.get().connect().describeInstances(request).getReservations().get(0).getInstances().get(0);
+    }
+
+    /**
+     * When the slave is deleted, terminate the instance.
+     */
+    @Override
+    public HttpResponse doDoDelete() throws IOException {
+        checkPermission(DELETE);
+        if (getNode() != null)
+        	getNode().terminate();
+        return new HttpRedirect("..");
+    }
+
+    /** What username to use to run root-like commands
+     *
+     */
+    public String getRemoteAdmin() {
+        return getNode().getRemoteAdmin();
+    }
+
+    public int getSshPort() {
+         return getNode().getSshPort();
+     }
+
+    public String getRootCommandPrefix() {
+        return getNode().getRootCommandPrefix();
+    }
+    
+}

+ 73 - 0
source-code/maven-generic-conf/extlibs/ec2-plugin/src/main/java/hudson/plugins/ec2/EC2ComputerLauncher.java

@@ -0,0 +1,73 @@
+package hudson.plugins.ec2;
+
+import hudson.model.TaskListener;
+import hudson.slaves.ComputerLauncher;
+import hudson.slaves.SlaveComputer;
+
+import java.io.IOException;
+import java.io.PrintStream;
+import java.util.ArrayList;
+import java.util.List;
+
+import com.amazonaws.AmazonClientException;
+import com.amazonaws.services.ec2.AmazonEC2;
+import com.amazonaws.services.ec2.model.Instance;
+import com.amazonaws.services.ec2.model.StartInstancesRequest;
+import com.amazonaws.services.ec2.model.StartInstancesResult;
+
+/**
+ * {@link ComputerLauncher} for EC2 that waits for the instance to really come up before proceeding to
+ * the real user-specified {@link ComputerLauncher}.
+ *
+ * @author Kohsuke Kawaguchi
+ */
+public abstract class EC2ComputerLauncher extends ComputerLauncher {
+    @Override
+    public void launch(SlaveComputer _computer, TaskListener listener) {
+        try {
+            EC2Computer computer = (EC2Computer)_computer;
+            PrintStream logger = listener.getLogger();
+
+            OUTER:
+            while(true) {
+                switch (computer.getState()) {
+                    case PENDING:
+                    case STOPPING:
+                        Thread.sleep(5000); // check every 5 secs
+                        continue OUTER;
+                    case RUNNING:
+                        break OUTER;
+                    case STOPPED:
+                    	AmazonEC2 ec2 = EC2Cloud.get().connect();
+                        List<String> instances = new ArrayList<String>();
+                        instances.add(computer.getInstanceId());
+
+                        StartInstancesRequest siRequest = new StartInstancesRequest(instances);
+                        StartInstancesResult siResult = ec2.startInstances(siRequest);
+                        logger.println("Starting existing instance: "+computer.getInstanceId()+ " result:"+siResult);
+                        continue OUTER;
+                    case SHUTTING_DOWN:
+                    case TERMINATED:
+                        // abort
+                        logger.println("The instance "+computer.getInstanceId()+" appears to be shut down. Aborting launch.");
+                        return;
+                }
+            }
+
+            launch(computer, logger, computer.describeInstance());
+        } catch (AmazonClientException e) {
+            e.printStackTrace(listener.error(e.getMessage()));
+        } catch (IOException e) {
+            e.printStackTrace(listener.error(e.getMessage()));
+        } catch (InterruptedException e) {
+            e.printStackTrace(listener.error(e.getMessage()));
+        }
+
+    }
+
+    /**
+     * Stage 2 of the launch. Called after the EC2 instance comes up.
+     */
+    protected abstract void launch(EC2Computer computer, PrintStream logger, Instance inst)
+            throws AmazonClientException, IOException, InterruptedException;
+}

+ 152 - 0
source-code/maven-generic-conf/extlibs/ec2-plugin/src/main/java/hudson/plugins/ec2/EC2PrivateKey.java

@@ -0,0 +1,152 @@
+package hudson.plugins.ec2;
+
+import hudson.util.Secret;
+
+import java.io.BufferedReader;
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.Reader;
+import java.io.StringReader;
+import java.security.DigestInputStream;
+import java.security.KeyPair;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.security.PrivateKey;
+import java.security.Security;
+
+import org.apache.commons.codec.binary.Hex;
+import org.bouncycastle.openssl.PEMReader;
+import org.bouncycastle.openssl.PasswordFinder;
+
+import com.amazonaws.AmazonClientException;
+import com.amazonaws.services.ec2.AmazonEC2;
+import com.amazonaws.services.ec2.model.KeyPairInfo;
+
+/**
+ * RSA private key (the one that you generate with ec2-add-keypair.) Starts with
+ * "----- BEGIN RSA PRIVATE KEY------\n".
+ * 
+ * @author Kohsuke Kawaguchi
+ */
+public final class EC2PrivateKey {
+    private final Secret privateKey;
+
+    public EC2PrivateKey(final String privateKey) {
+        this.privateKey = Secret.fromString(privateKey.trim());
+    }
+
+    /**
+     * Obtains the fingerprint of the key in the "ab:cd:ef:...:12" format.
+     */
+    /**
+     * Obtains the fingerprint of the key in the "ab:cd:ef:...:12" format.
+     */
+    public String getFingerprint() throws IOException {
+        Security
+            .addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
+        Reader r =
+            new BufferedReader(new StringReader(privateKey.toString()));
+        PEMReader pem = new PEMReader(r, new PasswordFinder() {
+            public char[] getPassword() {
+                throw PRIVATE_KEY_WITH_PASSWORD;
+            }
+        });
+
+        try {
+            KeyPair pair = (KeyPair) pem.readObject();
+            if (pair == null) {
+                return null;
+            }
+            PrivateKey key = pair.getPrivate();
+            return digest(key);
+        } catch (RuntimeException e) {
+            if (e == PRIVATE_KEY_WITH_PASSWORD) {
+                throw new IOException(
+                    "This private key is password protected, which isn't supported yet");
+            }
+            throw e;
+        }
+    }
+
+    /**
+     * Is this file really a private key?
+     */
+    public boolean isPrivateKey() throws IOException {
+        BufferedReader br =
+            new BufferedReader(new StringReader(privateKey.toString()));
+        String line;
+        while ((line = br.readLine()) != null) {
+            if (line.equals("-----BEGIN RSA PRIVATE KEY-----")) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Finds the {@link KeyPairInfo} that corresponds to this key in EC2.
+     */
+    public com.amazonaws.services.ec2.model.KeyPair find(
+            final AmazonEC2 ec2) throws IOException, AmazonClientException {
+        String fp = getFingerprint();
+        for (KeyPairInfo kp : ec2.describeKeyPairs().getKeyPairs()) {
+            if (kp.getKeyFingerprint().equalsIgnoreCase(fp)) {
+                com.amazonaws.services.ec2.model.KeyPair keyPair =
+                    new com.amazonaws.services.ec2.model.KeyPair();
+                keyPair.setKeyName(kp.getKeyName());
+                keyPair.setKeyFingerprint(fp);
+                keyPair.setKeyMaterial(Secret.toString(privateKey));
+                return keyPair;
+            }
+        }
+        return null;
+    }
+
+    @Override
+    public int hashCode() {
+        return privateKey.hashCode();
+    }
+
+    @Override
+    public boolean equals(final Object that) {
+        return that instanceof EC2PrivateKey
+            && privateKey.equals(((EC2PrivateKey) that).privateKey);
+    }
+
+    @Override
+    public String toString() {
+        return privateKey.toString();
+    }
+
+    /* package */static String digest(final PrivateKey k)
+            throws IOException {
+        try {
+            MessageDigest md5 = MessageDigest.getInstance("SHA1");
+
+            DigestInputStream in =
+                new DigestInputStream(new ByteArrayInputStream(
+                    k.getEncoded()), md5);
+            try {
+                while (in.read(new byte[128]) > 0) {
+                    ; // simply discard the input
+                }
+            } finally {
+                in.close();
+            }
+            StringBuilder buf = new StringBuilder();
+            char[] hex = Hex.encodeHex(md5.digest());
+            for (int i = 0; i < hex.length; i += 2) {
+                if (buf.length() > 0) {
+                    buf.append(':');
+                }
+                buf.append(hex, i, 2);
+            }
+            return buf.toString();
+        } catch (NoSuchAlgorithmException e) {
+            throw new AssertionError(e);
+        }
+    }
+
+    private static final RuntimeException PRIVATE_KEY_WITH_PASSWORD =
+        new RuntimeException();
+}

+ 77 - 0
source-code/maven-generic-conf/extlibs/ec2-plugin/src/main/java/hudson/plugins/ec2/EC2RetentionStrategy.java

@@ -0,0 +1,77 @@
+package hudson.plugins.ec2;
+
+import hudson.model.Descriptor;
+import hudson.slaves.RetentionStrategy;
+import hudson.util.TimeUnit2;
+
+import java.util.logging.Logger;
+
+import org.kohsuke.stapler.DataBoundConstructor;
+
+/**
+ * {@link RetentionStrategy} for EC2.
+ *
+ * @author Kohsuke Kawaguchi
+ */
+public class EC2RetentionStrategy extends RetentionStrategy<EC2Computer> {
+    /** Number of minutes of idleness before an instance should be terminated.
+	    A value of zero indicates that the instance should never be automatically terminated */
+    public final int idleTerminationMinutes;
+
+
+    @DataBoundConstructor
+    public EC2RetentionStrategy(String idleTerminationMinutes) {
+        if (idleTerminationMinutes == null || idleTerminationMinutes.trim() == "") {
+            this.idleTerminationMinutes = 0;
+        } else {
+            int value = 30;
+            try {
+                value = Integer.parseInt(idleTerminationMinutes);
+            } catch (NumberFormatException nfe) {
+                LOGGER.info("Malformed default idleTermination value: " + idleTerminationMinutes); 
+            }
+
+            this.idleTerminationMinutes = value;
+        }
+    }
+
+    @Override
+	public synchronized long check(EC2Computer c) {
+
+        /* If we've been told never to terminate, then we're done. */
+        if  (idleTerminationMinutes == 0) return 1;
+        final long idleMilliseconds1 = System.currentTimeMillis() - c.getIdleStartMilliseconds();
+        System.out.println(c.getName() + " idle: " + idleMilliseconds1);
+        
+        if (c.isIdle() && c.isOnline() && !disabled) {
+            // TODO: really think about the right strategy here
+            final long idleMilliseconds = System.currentTimeMillis() - c.getIdleStartMilliseconds();
+            if (idleMilliseconds > TimeUnit2.MINUTES.toMillis(idleTerminationMinutes)) {
+                LOGGER.info("Idle timeout: "+c.getName());
+                c.getNode().idleTimeout();
+            }
+        }
+        return 1;
+    }
+
+    /**
+     * Try to connect to it ASAP.
+     */
+    @Override
+    public void start(EC2Computer c) {
+        c.connect(false);
+    }
+
+    // no registration since this retention strategy is used only for EC2 nodes that we provision automatically.
+    // @Extension
+    public static class DescriptorImpl extends Descriptor<RetentionStrategy<?>> {
+        @Override
+		public String getDisplayName() {
+            return "EC2";
+        }
+    }
+
+    private static final Logger LOGGER = Logger.getLogger(EC2RetentionStrategy.class.getName());
+
+    public static boolean disabled = Boolean.getBoolean(EC2RetentionStrategy.class.getName()+".disabled");
+}

+ 382 - 0
source-code/maven-generic-conf/extlibs/ec2-plugin/src/main/java/hudson/plugins/ec2/EC2Slave.java

@@ -0,0 +1,382 @@
+package hudson.plugins.ec2;
+
+import hudson.Extension;
+import hudson.Util;
+import hudson.model.Computer;
+import hudson.model.Descriptor.FormException;
+import hudson.model.Hudson;
+import hudson.model.Slave;
+import hudson.model.Node;
+import hudson.plugins.ec2.ssh.EC2UnixLauncher;
+import hudson.slaves.NodeProperty;
+import hudson.util.ListBoxModel;
+
+import java.io.IOException;
+import java.util.Collections;
+import java.util.List;
+import java.util.LinkedList;
+import java.util.HashSet;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import javax.servlet.ServletException;
+
+import org.apache.commons.lang.StringUtils;
+import org.kohsuke.stapler.DataBoundConstructor;
+import org.kohsuke.stapler.QueryParameter;
+import org.kohsuke.stapler.StaplerRequest;
+
+import com.amazonaws.AmazonClientException;
+import com.amazonaws.services.ec2.AmazonEC2;
+import com.amazonaws.services.ec2.model.*;
+
+import net.sf.json.JSONObject;
+
+/**
+ * Slave running on EC2.
+ * 
+ * @author Kohsuke Kawaguchi
+ */
+public final class EC2Slave extends Slave {
+    /**
+     * Comes from {@link SlaveTemplate#initScript}.
+     */
+    public final String initScript;
+    public final String remoteAdmin; // e.g. 'ubuntu'
+    public final String rootCommandPrefix; // e.g. 'sudo'
+    public final String jvmopts; //e.g. -Xmx1g
+    public final boolean stopOnTerminate;
+    public final String idleTerminationMinutes;
+    public final boolean usePrivateDnsName;
+    public List<EC2Tag> tags;
+
+    // Temporary stuff that is obtained live from EC2
+    public String publicDNS;
+    public String privateDNS;
+
+    /* The last instance data to be fetched for the slave */
+    private Instance lastFetchInstance = null;
+
+    /* The time at which we fetched the last instance data */
+    private long lastFetchTime = 0;
+
+    /* The time (in milliseconds) after which we will always re-fetch externally changeable EC2 data when we are asked for it */
+    private static final long MIN_FETCH_TIME = 20 * 1000;
+
+
+    /**
+     * For data read from old Hudson, this is 0, so we use that to indicate 22.
+     */
+    private final int sshPort;
+
+    public static final String TEST_ZONE = "testZone";
+    
+    public EC2Slave(String instanceId, String description, String remoteFS, int sshPort, int numExecutors, String labelString, String initScript, String remoteAdmin, String rootCommandPrefix, String jvmopts, boolean stopOnTerminate, String idleTerminationMinutes, String publicDNS, String privateDNS, List<EC2Tag> tags) throws FormException, IOException {
+        this(instanceId, description, remoteFS, sshPort, numExecutors, Mode.NORMAL, labelString, initScript, Collections.<NodeProperty<?>>emptyList(), remoteAdmin, rootCommandPrefix, jvmopts, stopOnTerminate, idleTerminationMinutes, publicDNS, privateDNS, tags, false);
+    }
+
+    public EC2Slave(String instanceId, String description, String remoteFS, int sshPort, int numExecutors, String labelString, String initScript, String remoteAdmin, String rootCommandPrefix, String jvmopts, boolean stopOnTerminate, String idleTerminationMinutes, String publicDNS, String privateDNS, List<EC2Tag> tags, boolean usePrivateDnsName) throws FormException, IOException {
+        this(instanceId, description, remoteFS, sshPort, numExecutors, Mode.NORMAL, labelString, initScript, Collections.<NodeProperty<?>>emptyList(), remoteAdmin, rootCommandPrefix, jvmopts, stopOnTerminate, idleTerminationMinutes, publicDNS, privateDNS, tags, usePrivateDnsName);
+    }
+
+
+    @DataBoundConstructor
+    public EC2Slave(String instanceId, String description, String remoteFS, int sshPort, int numExecutors, Mode mode, String labelString, String initScript, List<? extends NodeProperty<?>> nodeProperties, String remoteAdmin, String rootCommandPrefix, String jvmopts, boolean stopOnTerminate, String idleTerminationMinutes, String publicDNS, String privateDNS, List<EC2Tag> tags, boolean usePrivateDnsName) throws FormException, IOException {
+
+        super(instanceId, description, remoteFS, numExecutors, mode, labelString, new EC2UnixLauncher(), new EC2RetentionStrategy(idleTerminationMinutes), nodeProperties);
+
+        this.initScript  = initScript;
+        this.remoteAdmin = remoteAdmin;
+        this.rootCommandPrefix = rootCommandPrefix;
+        this.jvmopts = jvmopts;
+        this.sshPort = sshPort;
+        this.stopOnTerminate = stopOnTerminate;
+        this.idleTerminationMinutes = idleTerminationMinutes;
+        this.publicDNS = publicDNS;
+        this.privateDNS = privateDNS;
+        this.tags = tags;
+        this.usePrivateDnsName = usePrivateDnsName;
+    }
+
+    /**
+     * Constructor for debugging.
+     */
+    public EC2Slave(String instanceId) throws FormException, IOException {
+        this(instanceId,"debug", "/tmp/hudson", 22, 1, Mode.NORMAL, "debug", "", Collections.<NodeProperty<?>>emptyList(), null, null, null, false, null, "Fake public", "Fake private", null, false);
+    }
+
+    /**
+     * See http://aws.amazon.com/ec2/instance-types/
+     */
+    /*package*/ static int toNumExecutors(InstanceType it) {
+        switch (it) {
+        case T1Micro:       return 1;
+        case M1Small:       return 1;
+        case M1Medium:      return 2;
+        case M1Large:       return 4;
+        case C1Medium:      return 5;
+        case M2Xlarge:      return 6;
+        case M1Xlarge:      return 8;
+        case M22xlarge:     return 13;
+        case C1Xlarge:      return 20;
+        case M24xlarge:     return 26;
+        case Cc14xlarge:    return 33;
+        case Cg14xlarge:    return 33;
+        default:            throw new AssertionError();
+        }
+    }
+
+    /**
+     * EC2 instance ID.
+     */
+    public String getInstanceId() {
+        return getNodeName();
+    }
+
+    @Override
+    public Computer createComputer() {
+        return new EC2Computer(this);
+    }
+
+    public static Instance getInstance(String instanceId) {
+        DescribeInstancesRequest request = new DescribeInstancesRequest();
+    	request.setInstanceIds(Collections.<String>singletonList(instanceId));
+        EC2Cloud cloudInstance = EC2Cloud.get();
+        if (cloudInstance == null)
+        	return null;
+        AmazonEC2 ec2 = cloudInstance.connect();
+    	List<Reservation> reservations = ec2.describeInstances(request).getReservations();
+        Instance i = null;
+    	if (reservations.size() > 0) {
+    		List<Instance> instances = reservations.get(0).getInstances();
+    		if (instances.size() > 0)
+    			i = instances.get(0);
+    	}
+    	return i;
+    }
+    
+    /**
+     * Terminates the instance in EC2.
+     */
+    public void terminate() {
+        try {
+            if (!isAlive(true)) {
+                /* The node has been killed externally, so we've nothing to do here */
+                LOGGER.info("EC2 instance already terminated: "+getInstanceId());
+            } else {
+                AmazonEC2 ec2 = EC2Cloud.get().connect();
+                TerminateInstancesRequest request = new TerminateInstancesRequest(Collections.singletonList(getInstanceId()));
+                ec2.terminateInstances(request);
+                LOGGER.info("Terminated EC2 instance (terminated): "+getInstanceId());
+            }
+            Hudson.getInstance().removeNode(this);
+        } catch (AmazonClientException e) {
+            LOGGER.log(Level.WARNING,"Failed to terminate EC2 instance: "+getInstanceId(),e);
+        } catch (IOException e) {
+            LOGGER.log(Level.WARNING,"Failed to terminate EC2 instance: "+getInstanceId(),e);
+        }
+    }
+
+	void idleTimeout() {
+		LOGGER.info("EC2 instance idle time expired: "+getInstanceId());
+		if (!stopOnTerminate) {
+			terminate();
+			return;
+		}
+
+		try {
+			AmazonEC2 ec2 = EC2Cloud.get().connect();
+			StopInstancesRequest request = new StopInstancesRequest(
+					Collections.singletonList(getInstanceId()));
+			ec2.stopInstances(request);
+			toComputer().disconnect(null);
+		} catch (AmazonClientException e) {
+	        Instance i = getInstance(getNodeName());
+			LOGGER.log(Level.WARNING, "Failed to terminate EC2 instance: "+getInstanceId() + " info: "+((i != null)?i:"") , e);
+		}
+		LOGGER.info("EC2 instance stopped: " + getInstanceId());
+	}
+
+    String getRemoteAdmin() {
+        if (remoteAdmin == null || remoteAdmin.length() == 0)
+            return "root";
+        return remoteAdmin;
+    }
+
+    String getRootCommandPrefix() {
+        if (rootCommandPrefix == null || rootCommandPrefix.length() == 0)
+            return "";
+        return rootCommandPrefix + " ";
+    }
+
+    String getJvmopts() {
+        return Util.fixNull(jvmopts);
+    }
+
+    public int getSshPort() {
+        return sshPort!=0 ? sshPort : 22;
+    }
+
+    public boolean getStopOnTerminate() {
+        return stopOnTerminate;
+    }
+
+    private boolean isAlive(boolean force) {
+        fetchLiveInstanceData(force);
+        if (lastFetchInstance == null) return false;
+        if (lastFetchInstance.getState().getName().equals(InstanceStateName.Terminated.toString())) return false;
+        return true;
+    }
+
+    /* Much of the EC2 data is beyond our direct control, therefore we need to refresh it from time to
+       time to ensure we reflect the reality of the instances. */
+    private void fetchLiveInstanceData( boolean force ) throws AmazonClientException {
+		/* If we've grabbed the data recently, don't bother getting it again unless we are forced */
+        long now = System.currentTimeMillis();
+        if ((lastFetchTime > 0) && (now - lastFetchTime < MIN_FETCH_TIME) && !force) {
+            return;
+        }
+
+        Instance i = getInstance(getNodeName());
+
+        lastFetchTime = now;
+        lastFetchInstance = i;
+        if (i == null)
+        	return;
+
+        publicDNS = i.getPublicDnsName();
+        privateDNS = i.getPrivateIpAddress();
+        tags = new LinkedList<EC2Tag>();
+
+        for (Tag t : i.getTags()) {
+            tags.add(new EC2Tag(t.getKey(), t.getValue()));
+        }
+    }
+
+
+	/* Clears all existing tag data so that we can force the instance into a known state */
+    private void clearLiveInstancedata() throws AmazonClientException {
+        Instance inst = getInstance(getNodeName());
+
+        /* Now that we have our instance, we can clear the tags on it */
+        if (!tags.isEmpty()) {
+            HashSet<Tag> inst_tags = new HashSet<Tag>();
+
+            for(EC2Tag t : tags) {
+                inst_tags.add(new Tag(t.getName(), t.getValue()));
+            }
+
+            DeleteTagsRequest tag_request = new DeleteTagsRequest();
+            tag_request.withResources(inst.getInstanceId()).setTags(inst_tags);
+            EC2Cloud.get().connect().deleteTags(tag_request);
+        }
+    }
+
+
+    /* Sets tags on an instance.  This will not clear existing tag data, so call clearLiveInstancedata if needed */
+    private void pushLiveInstancedata() throws AmazonClientException {
+        Instance inst = getInstance(getNodeName());
+
+        /* Now that we have our instance, we can set tags on it */
+        if (tags != null && !tags.isEmpty()) {
+            HashSet<Tag> inst_tags = new HashSet<Tag>();
+
+            for(EC2Tag t : tags) {
+                inst_tags.add(new Tag(t.getName(), t.getValue()));
+            }            
+
+            CreateTagsRequest tag_request = new CreateTagsRequest();
+            tag_request.withResources(inst.getInstanceId()).setTags(inst_tags);
+            EC2Cloud.get().connect().createTags(tag_request);
+        }
+    }
+
+    public String getPublicDNS() {
+        fetchLiveInstanceData(false);
+        return publicDNS;
+    }
+
+    public String getPrivateDNS() {
+        fetchLiveInstanceData(false);
+        return privateDNS;
+    }
+
+    public List<EC2Tag> getTags() {
+        fetchLiveInstanceData(false);
+        return Collections.unmodifiableList(tags);
+    }
+
+    @Override
+	public Node reconfigure(final StaplerRequest req, JSONObject form) throws FormException {
+        if (form == null) {
+            return null;
+        }
+
+        if (!isAlive(true)) {
+            LOGGER.info("EC2 instance terminated externally: " + getInstanceId());
+            try {
+                Hudson.getInstance().removeNode(this);
+            } catch (IOException ioe) {
+                LOGGER.log(Level.WARNING, "Attempt to reconfigure EC2 instance which has been externally terminated: " + getInstanceId(), ioe);
+            }
+
+            return null;
+        }
+
+        Node result = super.reconfigure(req, form);
+
+        /* Get rid of the old tags, as represented by ourselves. */
+        clearLiveInstancedata();
+
+        /* Set the new tags, as represented by our successor */
+        ((EC2Slave) result).pushLiveInstancedata();
+
+        return result;
+    }
+
+
+    public boolean getUsePrivateDnsName() {
+        return usePrivateDnsName;
+    }
+    
+    public static ListBoxModel fillZoneItems(String accessId, String secretKey, String region) throws IOException, ServletException {
+		ListBoxModel model = new ListBoxModel();
+		if (AmazonEC2Cloud.testMode) {
+			model.add(TEST_ZONE);
+			return model;
+		}
+			
+		if (!StringUtils.isEmpty(accessId) && !StringUtils.isEmpty(secretKey) && !StringUtils.isEmpty(region)) {
+			AmazonEC2 client = EC2Cloud.connect(accessId, secretKey, AmazonEC2Cloud.getEc2EndpointUrl(region));
+			DescribeAvailabilityZonesResult zones = client.describeAvailabilityZones();
+			List<AvailabilityZone> zoneList = zones.getAvailabilityZones();
+			model.add("<not specified>", "");
+			for (AvailabilityZone z : zoneList) {
+				model.add(z.getZoneName(), z.getZoneName());
+			}
+		}
+		return model;
+	}
+    
+    
+    @Extension
+    public static final class DescriptorImpl extends SlaveDescriptor {
+        @Override
+		public String getDisplayName() {
+            return "Amazon EC2";
+        }
+
+        @Override
+        public boolean isInstantiable() {
+            return false;
+        }
+
+        public ListBoxModel doFillZoneItems(@QueryParameter String accessId,
+        		@QueryParameter String secretKey, @QueryParameter String region) throws IOException,
+    			ServletException {
+        	return fillZoneItems(accessId, secretKey, region);
+    	}
+    }
+
+    private static final Logger LOGGER = Logger.getLogger(EC2Slave.class.getName());
+}

+ 82 - 0
source-code/maven-generic-conf/extlibs/ec2-plugin/src/main/java/hudson/plugins/ec2/EC2Tag.java

@@ -0,0 +1,82 @@
+package hudson.plugins.ec2;
+
+import hudson.model.Descriptor;
+import hudson.model.AbstractDescribableImpl;
+import hudson.Extension;
+import org.kohsuke.stapler.DataBoundConstructor;
+
+import java.util.List;
+import java.util.LinkedList;
+
+import com.amazonaws.services.ec2.model.Tag;
+
+
+public class EC2Tag extends AbstractDescribableImpl<EC2Tag>
+{
+   private String name;
+   private String value;
+
+   @DataBoundConstructor
+   public EC2Tag(String name, String value) {
+      this.name = name;
+      this.value = value;
+   }
+
+
+   /* Constructor from Amazon Tag */
+   public EC2Tag(Tag t) {
+       name = t.getKey();
+       value = t.getValue();
+   }
+
+
+   public String getName() {
+      return name;
+   }
+
+
+   public String getValue() {
+      return value;
+   }
+
+
+   @Override
+   public String toString() {
+      return "EC2Tag: " + name + "->" + value;
+   }
+
+
+   @Override
+   public boolean equals(Object o) {
+      if (o == null) return false;
+      if (!(o instanceof EC2Tag)) return false;
+
+      EC2Tag other = (EC2Tag) o;
+      if ((name == null && other.name != null) || !name.equals( other.name)) return false;
+      if ((value == null && other.value != null) || !value.equals( other.value)) return false;
+
+      return true;
+   }
+
+
+   @Extension
+   public static class DescriptorImpl extends Descriptor<EC2Tag> {
+      @Override
+	  public String getDisplayName() { return ""; }
+   }
+
+
+   /* Helper method to convert lists of Amazon tags into internal format */ 
+   public static List<EC2Tag> fromAmazonTags(List<Tag> amazon_tags) {
+       if (null == amazon_tags) {
+           return null;
+       }
+
+       LinkedList<EC2Tag> result = new LinkedList<EC2Tag>();
+       for (Tag t : amazon_tags) {
+           result.add(new EC2Tag(t));
+       }
+
+       return result;
+   }
+}

+ 180 - 0
source-code/maven-generic-conf/extlibs/ec2-plugin/src/main/java/hudson/plugins/ec2/Eucalyptus.java

@@ -0,0 +1,180 @@
+package hudson.plugins.ec2;
+
+import hudson.Extension;
+import hudson.util.FormValidation;
+import hudson.util.IOException2;
+
+import java.io.IOException;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.security.GeneralSecurityException;
+import java.security.KeyManagementException;
+import java.security.NoSuchAlgorithmException;
+import java.security.cert.X509Certificate;
+import java.util.List;
+
+import javax.net.ssl.HostnameVerifier;
+import javax.net.ssl.HttpsURLConnection;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLSession;
+import javax.net.ssl.TrustManager;
+import javax.net.ssl.X509TrustManager;
+import javax.servlet.ServletException;
+
+import org.dom4j.Document;
+import org.dom4j.DocumentException;
+import org.dom4j.Element;
+import org.dom4j.io.SAXReader;
+import org.kohsuke.stapler.DataBoundConstructor;
+import org.kohsuke.stapler.QueryParameter;
+import org.kohsuke.stapler.StaplerResponse;
+
+/**
+ * Eucalyptus.
+ *
+ * @author Kohsuke Kawaguchi
+ */
+public class Eucalyptus extends EC2Cloud {
+    private transient Metadata metadata;
+
+    public final URL url;
+
+    @DataBoundConstructor
+    public Eucalyptus(URL url, String accessId, String secretKey, String privateKey, String instanceCapStr, List<SlaveTemplate> templates) throws IOException {
+        super("eucalyptus", accessId, secretKey, privateKey, instanceCapStr, templates);
+        this.url = url;
+    }
+
+    private Metadata getMetadata() throws IOException {
+        if (metadata==null)
+            metadata = new Metadata(url);
+        return metadata;
+    }
+
+    @Override
+    public URL getEc2EndpointUrl() throws IOException {
+        return getMetadata().ec2endpoint;
+    }
+
+    @Override
+    public URL getS3EndpointUrl() throws IOException {
+        return getMetadata().s3endpoint;
+    }
+
+    @Extension
+    public static class DescriptorImpl extends EC2Cloud.DescriptorImpl {
+        @Override
+		public String getDisplayName() {
+            return "Eucalyptus";
+        }
+
+        @Override
+		public FormValidation doTestConnection(
+                @QueryParameter URL url,
+                @QueryParameter String accessId,
+                @QueryParameter String secretKey,
+                @QueryParameter String privateKey) throws IOException, ServletException {
+            return super.doTestConnection(new Metadata(url).ec2endpoint,accessId,secretKey,privateKey);
+        }
+
+        @Override
+		public FormValidation doGenerateKey(
+                StaplerResponse rsp, @QueryParameter URL url, @QueryParameter String accessId, @QueryParameter String secretKey) throws IOException, ServletException {
+            return super.doGenerateKey(rsp, new Metadata(url).ec2endpoint, accessId,secretKey);
+        }
+    }
+
+    /**
+     * Eucalyptus service endpoint metadata.
+     */
+    static class Metadata {
+        final URL ec2endpoint,s3endpoint;
+
+        Metadata(URL eucalyptus) throws IOException {
+            if (!eucalyptus.getProtocol().equals("https"))
+                throw new IOException("Expecting an HTTPS URL but got "+eucalyptus);
+            URL metadataUrl = new URL(eucalyptus, "/register");
+            try {
+                HttpsURLConnection con = (HttpsURLConnection)metadataUrl.openConnection();
+                makeIgnoreCertificate(con);
+                Document metadata = new SAXReader().read(con.getInputStream());
+                /*
+                       Metadata, as of Eucalyptus 1.5.2, looks like this:
+
+                       <Signature>
+                         <SignedInfo>
+                           <SignatureMethod>http://www.w3.org/2001/04/xmldsig-more#hmac-sha256</SignatureMethod>
+                         </SignedInfo>
+                         <SignatureValue>62595777525d7dbba4b5f361b3e9041d3d37e92611684557e67e85a9222a3ffb  </SignatureValue>
+                         <Object>
+                          <CloudSchema>
+                         <Services type="array">
+                           <Service>
+                             <Name>ec2</Name>
+                             <EndpointUrl>http://eucalyptus.hudson-slaves.sfbay.sun.com:8773/services/Eucalyptus</EndpointUrl>
+                             <Resources type="array">
+                               ...
+                             </Resources>
+                           </Service>
+                           <Service>
+                             <Name>s3</Name>
+                             <EndpointUrl>http://eucalyptus.hudson-slaves.sfbay.sun.com:8773/services/Walrus</EndpointUrl>
+                             <Resources type="array">
+                               ...
+                             </Resources>
+                           </Service>
+                         </Services>
+                         <id>a002c56e-b994-4ed8-956b-b30eda9b6153</id>  <CloudType>eucalyptus</CloudType>
+                         <CloudVersion>1.5.2</CloudVersion>
+                         <SchemaVersion>1.0</SchemaVersion>
+                         <Description>Public cloud in the new cluster</Description>
+                       </CloudSchema>
+
+                    */
+
+                this.ec2endpoint = readURLFromMetadata(metadata, "ec2");
+                this.s3endpoint = readURLFromMetadata(metadata, "s3");
+            } catch (DocumentException e) {
+                throw new IOException2("Failed to parse Eucalyptus metadata at "+metadataUrl,e);
+            } catch (IOException e) {
+                throw new IOException2("Failed to parse Eucalyptus metadata at "+metadataUrl,e);
+            } catch (GeneralSecurityException e) {
+                throw new IOException2("Failed to parse Eucalyptus metadata at "+metadataUrl,e);
+            }
+        }
+
+        /**
+         * Configures the given {@link HttpsURLConnection} so that it'll ignore all the HTTPS certificate checks,
+         * as typical Eucalyptus implementation doesn't come with a valid certificate.
+         */
+        private void makeIgnoreCertificate(HttpsURLConnection con) throws NoSuchAlgorithmException, KeyManagementException {
+            SSLContext sc = SSLContext.getInstance("SSL");
+            TrustManager[] tma = {new X509TrustManager() {
+                public X509Certificate[] getAcceptedIssuers() {
+                    return null;
+                }
+
+                public void checkClientTrusted(X509Certificate[] certs, String authType) {
+                }
+
+                public void checkServerTrusted(X509Certificate[] certs, String authType) {
+                }
+            }};
+            sc.init(null, tma, null);
+
+            con.setSSLSocketFactory(sc.getSocketFactory());
+            con.setHostnameVerifier(new HostnameVerifier() {
+                public boolean verify(String s, SSLSession sslSession) {
+                    return true;    // everything goes
+                }
+            });
+        }
+
+        private URL readURLFromMetadata(Document metadata, String serviceName) throws MalformedURLException {
+            Element e = (Element)metadata.selectSingleNode("//Service[Name/text()='" + serviceName + "']/EndpointUrl");
+            if (e==null)
+                throw new IllegalStateException("Service metadata didn't contain "+serviceName);
+            return new URL(e.getTextTrim());
+        }
+    }
+}

+ 23 - 0
source-code/maven-generic-conf/extlibs/ec2-plugin/src/main/java/hudson/plugins/ec2/InstanceState.java

@@ -0,0 +1,23 @@
+package hudson.plugins.ec2;
+
+/**
+ * Constants that represent the running state of EC2. 
+ *
+ * @author Kohsuke Kawaguchi
+ */
+public enum InstanceState {
+    PENDING,
+    RUNNING,
+    SHUTTING_DOWN,
+    TERMINATED,
+    STOPPING,
+    STOPPED;
+
+    public String getCode() {
+        return name().toLowerCase().replace('_','-');
+    }
+
+    public static InstanceState find(String name) {
+        return Enum.valueOf(InstanceState.class,name.toUpperCase().replace('-','_'));
+    }
+}

+ 56 - 0
source-code/maven-generic-conf/extlibs/ec2-plugin/src/main/java/hudson/plugins/ec2/InstanceTypeConverter.java

@@ -0,0 +1,56 @@
+package hudson.plugins.ec2;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import com.amazonaws.services.ec2.model.InstanceType;
+import com.thoughtworks.xstream.converters.Converter;
+import com.thoughtworks.xstream.converters.MarshallingContext;
+import com.thoughtworks.xstream.converters.UnmarshallingContext;
+import com.thoughtworks.xstream.io.HierarchicalStreamReader;
+import com.thoughtworks.xstream.io.HierarchicalStreamWriter;
+
+/*
+ * Note this is used only to handle the metadata for older versions of the ec2-plugin. The current
+ * versions use the Amazon APIs for this. See PluginImpl for where this is used.
+ */
+public class InstanceTypeConverter implements Converter {
+
+	private static final Map<String, InstanceType> TYPICA_INSTANCE_TYPES = new HashMap<String, InstanceType>();
+	
+	static {
+		TYPICA_INSTANCE_TYPES.put("DEFAULT", InstanceType.M1Small);
+		TYPICA_INSTANCE_TYPES.put("LARGE", InstanceType.M1Large);
+		TYPICA_INSTANCE_TYPES.put("XLARGE", InstanceType.M1Xlarge);
+		TYPICA_INSTANCE_TYPES.put("MEDIUM_HCPU", InstanceType.C1Medium);
+		TYPICA_INSTANCE_TYPES.put("XLARGE_HCPU", InstanceType.C1Xlarge);
+		TYPICA_INSTANCE_TYPES.put("XLARGE_HMEM", InstanceType.M2Xlarge);
+		TYPICA_INSTANCE_TYPES.put("XLARGE_DOUBLE_HMEM", InstanceType.M22xlarge);
+		TYPICA_INSTANCE_TYPES.put("XLARGE_QUAD_HMEM", InstanceType.M24xlarge);
+		TYPICA_INSTANCE_TYPES.put("XLARGE_CLUSTER_COMPUTE", InstanceType.Cc14xlarge);
+	}
+	
+	public boolean canConvert(Class type) {	
+		return InstanceType.class == type;
+	}
+
+	public void marshal(Object source, HierarchicalStreamWriter writer, MarshallingContext context) {
+		InstanceType instanceType = (InstanceType) source;
+		writer.setValue(instanceType.name());
+	}
+
+	public Object unmarshal(HierarchicalStreamReader reader, UnmarshallingContext context) {
+		InstanceType instanceType = null;
+		
+		String stringValue = reader.getValue();
+		
+		try {
+			instanceType = InstanceType.valueOf(stringValue);
+		} catch (IllegalArgumentException e) {
+			instanceType = TYPICA_INSTANCE_TYPES.get(stringValue.toUpperCase());
+		}
+		
+		return instanceType;
+	}
+
+}

+ 212 - 0
source-code/maven-generic-conf/extlibs/ec2-plugin/src/main/java/hudson/plugins/ec2/OpenNebula.java

@@ -0,0 +1,212 @@
+package hudson.plugins.ec2;
+
+import hudson.Extension;
+import hudson.plugins.ec2.one.OneEC2Cloud;
+import hudson.plugins.ec2.one.OneSlaveTemplate;
+import hudson.util.FormValidation;
+import hudson.util.IOException2;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.net.URLConnection;
+import java.security.GeneralSecurityException;
+import java.security.KeyManagementException;
+import java.security.NoSuchAlgorithmException;
+import java.security.cert.X509Certificate;
+import java.util.List;
+
+import javax.net.ssl.HostnameVerifier;
+import javax.net.ssl.HttpsURLConnection;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLSession;
+import javax.net.ssl.TrustManager;
+import javax.net.ssl.X509TrustManager;
+import javax.servlet.ServletException;
+
+import org.dom4j.Document;
+import org.dom4j.Element;
+import org.kohsuke.stapler.DataBoundConstructor;
+import org.kohsuke.stapler.QueryParameter;
+
+/**
+ * The original implementation of {@link EC2Cloud}.
+ * 
+ * @author Kohsuke Kawaguchi
+ */
+public class OpenNebula extends OneEC2Cloud {
+    private transient Metadata metadata;
+
+    // Used when running unit tests
+    public static boolean testMode;
+
+    public final URL url;
+
+    @DataBoundConstructor
+    public OpenNebula(final String url, final String accessId,
+            final String secretKey, final String privateKey,
+            final String instanceCapStr,
+            final List<OneSlaveTemplate> templates)
+            throws MalformedURLException {
+        this(new URL(url), accessId, secretKey, privateKey,
+            instanceCapStr, templates);
+    }
+
+    @DataBoundConstructor
+    public OpenNebula(final URL url, final String accessId,
+            final String secretKey, final String privateKey,
+            final String instanceCapStr,
+            final List<OneSlaveTemplate> templates) {
+        super("one-", accessId, secretKey, privateKey, instanceCapStr,
+            templates);
+        this.url = url;
+    }
+
+    private Metadata getMetadata() throws IOException {
+        if (metadata == null) {
+            metadata = new Metadata(url);
+        }
+        return metadata;
+    }
+
+    @Override
+    public URL getEc2EndpointUrl() throws IOException {
+        return getMetadata().ec2endpoint;
+    }
+
+    @Override
+    public URL getS3EndpointUrl() throws IOException {
+        return getMetadata().s3endpoint;
+    }
+
+    @Extension
+    public static class DescriptorImpl extends OneEC2Cloud.DescriptorImpl {
+        @Override
+        public String getDisplayName() {
+            return "OpenNebula";
+        }
+
+        @Override
+        public FormValidation doTestConnection(
+                @QueryParameter final URL url,
+                @QueryParameter final String accessId,
+                @QueryParameter final String secretKey,
+                @QueryParameter final String privateKey)
+                throws IOException, ServletException {
+            return super.doTestConnection(new Metadata(url).ec2endpoint,
+                accessId, secretKey, privateKey);
+        }
+    }
+
+    /**
+     * OpenNebula service endpoint metadata.
+     */
+    static class Metadata {
+        final URL ec2endpoint, s3endpoint;
+
+        Metadata(final URL oneUrl) throws IOException {
+
+            // InputStream is = readMetadata(oneUrl);
+
+            ec2endpoint = getUrl(oneUrl);
+            s3endpoint = getUrl(oneUrl);
+        }
+
+        /**
+         * @param oneUrl
+         * @return
+         * @throws MalformedURLException
+         */
+        private URL getUrl(final URL oneUrl) throws MalformedURLException {
+            return (null != oneUrl) ? oneUrl : new URL(
+                "http://opennebula:4568");
+        }
+
+        /**
+         * @param oneUrl
+         * @param metadataUrl
+         * @throws IOException
+         * @throws NoSuchAlgorithmException
+         * @throws KeyManagementException
+         */
+        private InputStream readMetadata(final URL url)
+                throws IOException, NoSuchAlgorithmException,
+                KeyManagementException {
+            InputStream is = null;
+            URL metadataUrl = new URL(getUrl(url), "/");
+
+            try {
+                if (getUrl(url).getProtocol().equals("https")) {
+                    HttpsURLConnection con =
+                        (HttpsURLConnection) getUrl(metadataUrl)
+                            .openConnection();
+                    makeIgnoreCertificate(con);
+                    is = con.getInputStream();
+                } else {
+                    URLConnection con =
+                        getUrl(metadataUrl).openConnection();
+                    is = con.getInputStream();
+                }
+            } catch (IOException e) {
+                throw new IOException2(
+                    "Failed to parse OpenNebula metadata at "
+                        + getUrl(metadataUrl), e);
+            } catch (GeneralSecurityException e) {
+                throw new IOException2(
+                    "Failed to parse OpenNebula metadata at "
+                        + getUrl(metadataUrl), e);
+            }
+
+            return is;
+        }
+
+        /**
+         * Configures the given {@link HttpsURLConnection} so that it'll ignore
+         * all the HTTPS certificate checks, as typical Eucalyptus
+         * implementation doesn't come with a valid certificate.
+         */
+        private void makeIgnoreCertificate(final HttpsURLConnection con)
+                throws NoSuchAlgorithmException, KeyManagementException {
+            SSLContext sc = SSLContext.getInstance("SSL");
+            TrustManager[] tma = {new X509TrustManager() {
+                public X509Certificate[] getAcceptedIssuers() {
+                    return null;
+                }
+
+                public void checkClientTrusted(
+                        final X509Certificate[] certs,
+                        final String authType) {
+                }
+
+                public void checkServerTrusted(
+                        final X509Certificate[] certs,
+                        final String authType) {
+                }
+            } };
+            sc.init(null, tma, null);
+
+            con.setSSLSocketFactory(sc.getSocketFactory());
+            con.setHostnameVerifier(new HostnameVerifier() {
+                public boolean verify(final String s,
+                        final SSLSession sslSession) {
+                    return true; // everything goes
+                }
+            });
+        }
+
+        @Deprecated
+        private URL readURLFromMetadata(final Document metadata,
+                final String serviceName) throws MalformedURLException {
+            Element e =
+                (Element) metadata
+                    .selectSingleNode("//Service[Name/text()='"
+                        + serviceName + "']/EndpointUrl");
+            if (e == null) {
+                throw new IllegalStateException(
+                    "Service metadata didn't contain " + serviceName);
+            }
+            return new URL(e.getTextTrim());
+        }
+    }
+}

+ 40 - 0
source-code/maven-generic-conf/extlibs/ec2-plugin/src/main/java/hudson/plugins/ec2/PluginImpl.java

@@ -0,0 +1,40 @@
+package hudson.plugins.ec2;
+
+import jenkins.model.Jenkins;
+import hudson.Extension;
+import hudson.Plugin;
+import hudson.model.Describable;
+import hudson.model.Descriptor;
+import hudson.model.Hudson;
+
+/**
+ * Added to handle backwards compatibility of xstream class name mapping.
+ */
+@Extension
+public class PluginImpl extends Plugin implements Describable<PluginImpl> {
+    @Override
+    public void start() throws Exception {
+        // backward compatibility with the legacy class name
+        Jenkins.XSTREAM.alias("hudson.plugins.ec2.EC2Cloud",AmazonEC2Cloud.class);
+        // backward compatibility with the legacy instance type
+        Jenkins.XSTREAM.registerConverter(new InstanceTypeConverter());
+        
+        load();
+    }
+
+    public DescriptorImpl getDescriptor() {
+        return (DescriptorImpl)Hudson.getInstance().getDescriptorOrDie(getClass());
+    }
+
+    public static PluginImpl get() {
+        return Hudson.getInstance().getPlugin(PluginImpl.class);
+    }
+
+    @Extension
+    public static final class DescriptorImpl extends Descriptor<PluginImpl> {
+        @Override
+        public String getDisplayName() {
+            return "EC2 PluginImpl";
+        }
+    }
+}

+ 442 - 0
source-code/maven-generic-conf/extlibs/ec2-plugin/src/main/java/hudson/plugins/ec2/SlaveTemplate.java

@@ -0,0 +1,442 @@
+package hudson.plugins.ec2;
+
+import hudson.Extension;
+import hudson.Util;
+import hudson.model.Describable;
+import hudson.model.TaskListener;
+import hudson.model.Descriptor;
+import hudson.model.Descriptor.FormException;
+import hudson.model.Hudson;
+import hudson.model.Label;
+import hudson.model.Node;
+import hudson.model.labels.LabelAtom;
+import hudson.util.FormValidation;
+import hudson.util.ListBoxModel;
+
+import java.io.IOException;
+import java.io.PrintStream;
+import java.util.*;
+
+import javax.servlet.ServletException;
+
+import org.apache.commons.codec.binary.Base64;
+import org.apache.commons.lang.StringUtils;
+import org.kohsuke.stapler.DataBoundConstructor;
+import org.kohsuke.stapler.QueryParameter;
+
+import com.amazonaws.AmazonClientException;
+import com.amazonaws.services.ec2.AmazonEC2;
+import com.amazonaws.services.ec2.model.*;
+
+/**
+ * Template of {@link EC2Slave} to launch.
+ *
+ * @author Kohsuke Kawaguchi
+ */
+public class SlaveTemplate implements Describable<SlaveTemplate> {
+    public final String ami;
+    public final String description;
+    public final String zone;
+    public final String securityGroups;
+    public final String remoteFS;
+    public final String sshPort;
+    public final InstanceType type;
+    public final String labels;
+    public final String initScript;
+    public final String userData;
+    public final String numExecutors;
+    public final String remoteAdmin;
+    public final String rootCommandPrefix;
+    public final String jvmopts;
+    public final String subnetId;
+    public final String idleTerminationMinutes;
+    public final int instanceCap;
+    public final boolean stopOnTerminate;
+    private final List<EC2Tag> tags;
+    public final boolean usePrivateDnsName;
+    protected transient EC2Cloud parent;
+    
+
+    private transient /*almost final*/ Set<LabelAtom> labelSet;
+	private transient /*almost final*/ Set<String> securityGroupSet;
+
+    @DataBoundConstructor
+    public SlaveTemplate(String ami, String zone, String securityGroups, String remoteFS, String sshPort, InstanceType type, String labelString, String description, String initScript, String userData, String numExecutors, String remoteAdmin, String rootCommandPrefix, String jvmopts, boolean stopOnTerminate, String subnetId, List<EC2Tag> tags, String idleTerminationMinutes, boolean usePrivateDnsName, String instanceCapStr) {
+        this.ami = ami;
+        this.zone = zone;
+        this.securityGroups = securityGroups;
+        this.remoteFS = remoteFS;
+        this.sshPort = sshPort;
+        this.type = type;
+        this.labels = Util.fixNull(labelString);
+        this.description = description;
+        this.initScript = initScript;
+        this.userData = userData;
+        this.numExecutors = Util.fixNull(numExecutors).trim();
+        this.remoteAdmin = remoteAdmin;
+        this.rootCommandPrefix = rootCommandPrefix;
+        this.jvmopts = jvmopts;
+        this.stopOnTerminate = stopOnTerminate;
+        this.subnetId = subnetId;
+        this.tags = tags;
+        this.idleTerminationMinutes = idleTerminationMinutes;
+        this.usePrivateDnsName = usePrivateDnsName;
+
+        if (null == instanceCapStr || instanceCapStr.equals("")) {
+            this.instanceCap = Integer.MAX_VALUE;
+        } else {
+            this.instanceCap = Integer.parseInt(instanceCapStr);
+        }
+        
+        readResolve(); // initialize
+    }
+    
+    public EC2Cloud getParent() {
+        return parent;
+    }
+
+    public String getLabelString() {
+        return labels;
+    }
+
+    public String getDisplayName() {
+        return description+" ("+ami+")";
+    }
+
+    String getZone() {
+        return zone;
+    }
+
+    public String getSecurityGroupString() {
+        return securityGroups;
+    }
+
+    public Set<String> getSecurityGroupSet() {
+        return securityGroupSet;
+    }
+
+    public Set<String> parseSecurityGroups() {
+        if (securityGroups == null || "".equals(securityGroups.trim())) {
+            return Collections.emptySet();
+        } else {
+            return new HashSet<String>(Arrays.asList(securityGroups.split("\\s*,\\s*")));
+        }
+    }
+
+    public int getNumExecutors() {
+        try {
+            return Integer.parseInt(numExecutors);
+        } catch (NumberFormatException e) {
+            return EC2Slave.toNumExecutors(type);
+        }
+    }
+
+    public int getSshPort() {
+        try {
+            return Integer.parseInt(sshPort);
+        } catch (NumberFormatException e) {
+            return 22;
+        }
+    }
+
+    public String getRemoteAdmin() {
+        return remoteAdmin;
+    }
+
+    public String getRootCommandPrefix() {
+        return rootCommandPrefix;
+    }
+
+    public String getSubnetId() {
+        return subnetId;
+    }
+
+    public List<EC2Tag> getTags() {
+        if (null == tags) return null;
+        return Collections.unmodifiableList(tags);
+    }
+
+    public String getidleTerminationMinutes() {
+        return idleTerminationMinutes;
+    }
+    
+    public Set<LabelAtom> getLabelSet(){
+        return labelSet;
+    }
+
+    public int getInstanceCap() {
+        return instanceCap;
+    }
+
+    public String getInstanceCapStr() {
+        if (instanceCap==Integer.MAX_VALUE) {
+            return "";
+        } else {
+            return String.valueOf(instanceCap);
+        }
+    }
+
+    /**
+     * Does this contain the given label?
+     *
+     * @param l
+     *      can be null to indicate "don't care".
+     */
+    public boolean containsLabel(Label l) {
+        return l==null || labelSet.contains(l);
+    }
+
+    /**
+     * Provisions a new EC2 slave.
+     *
+     * @return always non-null. This needs to be then added to {@link Hudson#addNode(Node)}.
+     */
+    public EC2Slave provision(TaskListener listener) throws AmazonClientException, IOException {
+        PrintStream logger = listener.getLogger();
+        AmazonEC2 ec2 = getParent().connect();
+
+        try {
+            logger.println("Launching "+ami);
+            KeyPair keyPair = parent.getPrivateKey().find(ec2);
+            if(keyPair==null) {
+                throw new AmazonClientException("No matching keypair found on EC2. Is the EC2 private key a valid one?");
+            }
+           
+            RunInstancesRequest riRequest = new RunInstancesRequest(ami, 1, 1);
+
+            List<Filter> diFilters = new ArrayList<Filter>();
+            diFilters.add(new Filter("image-id").withValues(ami));
+            
+            if (StringUtils.isNotBlank(getZone())) {
+            	Placement placement = new Placement(getZone());
+            	riRequest.setPlacement(placement);
+                diFilters.add(new Filter("availability-zone").withValues(getZone()));
+            }
+
+            if (StringUtils.isNotBlank(getSubnetId())) {
+               riRequest.setSubnetId(getSubnetId());
+               diFilters.add(new Filter("subnet-id").withValues(getSubnetId()));
+
+               /* If we have a subnet ID then we can only use VPC security groups */
+               if (!securityGroupSet.isEmpty()) {
+                  List<String> group_ids = new ArrayList<String>();
+
+                  DescribeSecurityGroupsRequest group_req = new DescribeSecurityGroupsRequest();
+                  group_req.withFilters(new Filter("group-name").withValues(securityGroupSet));
+                  DescribeSecurityGroupsResult group_result = ec2.describeSecurityGroups(group_req);
+
+                  for (SecurityGroup group : group_result.getSecurityGroups()) {
+                     if (group.getVpcId() != null && !group.getVpcId().isEmpty()) {
+                        List<Filter> filters = new ArrayList<Filter>();
+                        filters.add(new Filter("vpc-id").withValues(group.getVpcId()));
+                        filters.add(new Filter("state").withValues("available"));
+                        filters.add(new Filter("subnet-id").withValues(getSubnetId()));
+
+                        DescribeSubnetsRequest subnet_req = new DescribeSubnetsRequest();
+                        subnet_req.withFilters(filters);
+                        DescribeSubnetsResult subnet_result = ec2.describeSubnets(subnet_req);
+
+                        List subnets = subnet_result.getSubnets();
+                        if(subnets != null && !subnets.isEmpty()) {
+                           group_ids.add(group.getGroupId());
+                        }
+                     }
+                  }
+
+                  if (securityGroupSet.size() != group_ids.size()) {
+                     throw new AmazonClientException( "Security groups must all be VPC security groups to work in a VPC context" );
+                  }
+
+                  if (!group_ids.isEmpty()) {
+                     riRequest.setSecurityGroupIds(group_ids);
+                     diFilters.add(new Filter("instance.group-id").withValues(group_ids));
+                  }
+               }
+            } else {
+               /* No subnet: we can use standard security groups by name */
+            	riRequest.setSecurityGroups(securityGroupSet);
+            	if (securityGroupSet.size() > 0)
+            		diFilters.add(new Filter("group-name").withValues(securityGroupSet));
+            }
+
+            String userDataString = Base64.encodeBase64String(userData.getBytes());
+            riRequest.setUserData(userDataString);
+            riRequest.setKeyName(keyPair.getKeyName());
+            diFilters.add(new Filter("key-name").withValues(keyPair.getKeyName()));
+            riRequest.setInstanceType(type.toString());
+            diFilters.add(new Filter("instance-type").withValues(type.toString()));
+            
+            HashSet<Tag> inst_tags = null;
+            if (tags != null && !tags.isEmpty()) {
+                inst_tags = new HashSet<Tag>();
+                for(EC2Tag t : tags) {
+                    diFilters.add(new Filter("tag:"+t.getName()).withValues(t.getValue()));
+                }
+            }
+            
+            DescribeInstancesRequest diRequest = new DescribeInstancesRequest();
+            diFilters.add(new Filter("instance-state-name").withValues(InstanceStateName.Stopped.toString(), 
+            		InstanceStateName.Stopping.toString()));
+            diRequest.setFilters(diFilters);
+            logger.println("Looking for existing instances: "+diRequest);
+
+            DescribeInstancesResult diResult = ec2.describeInstances(diRequest);
+            if (diResult.getReservations().size() == 0) {
+                // Have to create a new instance
+                Instance inst = ec2.runInstances(riRequest).getReservation().getInstances().get(0);
+
+                /* Now that we have our instance, we can set tags on it */
+                if (inst_tags != null) {
+                    CreateTagsRequest tag_request = new CreateTagsRequest();
+                    tag_request.withResources(inst.getInstanceId()).setTags(inst_tags);
+                    ec2.createTags(tag_request);
+
+                    // That was a remote request - we should also update our local instance data.
+                    inst.setTags(inst_tags);
+                }
+                logger.println("No existing instance found - created: "+inst);
+                return newSlave(inst);
+            }
+            	
+            Instance inst = diResult.getReservations().get(0).getInstances().get(0);
+            logger.println("Found existing stopped instance: "+inst);
+            List<String> instances = new ArrayList<String>();
+            instances.add(inst.getInstanceId());
+            StartInstancesRequest siRequest = new StartInstancesRequest(instances);
+            StartInstancesResult siResult = ec2.startInstances(siRequest);
+            logger.println("Starting existing instance: "+inst+ " result:"+siResult);
+
+            List<Node> nodes = Hudson.getInstance().getNodes();
+            for (int i = 0, len = nodes.size(); i < len; i++) {
+            	if (!(nodes.get(i) instanceof EC2Slave))
+            		continue;
+            	EC2Slave ec2Node = (EC2Slave) nodes.get(i);
+            	if (ec2Node.getInstanceId().equals(inst.getInstanceId())) {
+                    logger.println("Found existing corresponding: "+ec2Node);
+            		return ec2Node;
+            	}
+            }
+            
+            // Existing slave not found 
+            logger.println("Creating new slave for existing instance: "+inst);
+            return newSlave(inst);
+            
+        } catch (FormException e) {
+            throw new AssertionError(); // we should have discovered all configuration issues upfront
+        }
+    }
+
+
+    private EC2Slave newSlave(Instance inst) throws FormException, IOException {
+        return new EC2Slave(inst.getInstanceId(), description, remoteFS, getSshPort(), getNumExecutors(), labels, initScript, remoteAdmin, rootCommandPrefix, jvmopts, stopOnTerminate, idleTerminationMinutes, inst.getPublicDnsName(), inst.getPrivateDnsName(), EC2Tag.fromAmazonTags(inst.getTags()), usePrivateDnsName);
+    }
+
+    /**
+     * Provisions a new EC2 slave based on the currently running instance on EC2,
+     * instead of starting a new one.
+     */
+    public EC2Slave attach(String instanceId, TaskListener listener) throws AmazonClientException, IOException {
+        PrintStream logger = listener.getLogger();
+        AmazonEC2 ec2 = getParent().connect();
+
+        try {
+            logger.println("Attaching to "+instanceId);
+            DescribeInstancesRequest request = new DescribeInstancesRequest();
+            request.setInstanceIds(Collections.singletonList(instanceId));
+            Instance inst = ec2.describeInstances(request).getReservations().get(0).getInstances().get(0);
+            return newSlave(inst);
+        } catch (FormException e) {
+            throw new AssertionError(); // we should have discovered all configuration issues upfront
+        }
+    }
+
+    /**
+     * Initializes data structure that we don't persist.
+     */
+    protected Object readResolve() {
+        labelSet = Label.parse(labels);
+        securityGroupSet = parseSecurityGroups();
+        return this;
+    }
+
+    public Descriptor<SlaveTemplate> getDescriptor() {
+        return Hudson.getInstance().getDescriptor(getClass());
+    }
+
+    @Extension
+    public static final class DescriptorImpl extends Descriptor<SlaveTemplate> {
+        @Override
+        public String getDisplayName() {
+            return null;
+        }
+
+        /**
+         * Since this shares much of the configuration with {@link EC2Computer}, check its help page, too.
+         */
+        @Override
+        public String getHelpFile(String fieldName) {
+            String p = super.getHelpFile(fieldName);
+            if (p==null)        p = Hudson.getInstance().getDescriptor(EC2Slave.class).getHelpFile(fieldName);
+            return p;
+        }
+
+        /***
+         * Check that the AMI requested is available in the cloud and can be used.
+         */
+        public FormValidation doValidateAmi(
+                @QueryParameter String accessId, @QueryParameter String secretKey,
+                @QueryParameter String region,
+                final @QueryParameter String ami) throws IOException, ServletException {
+            AmazonEC2 ec2 = EC2Cloud.connect(accessId, secretKey, AmazonEC2Cloud.getEc2EndpointUrl(region));
+            if(ec2!=null) {
+                try {
+                    List<String> images = new LinkedList<String>();
+                    images.add(ami);
+                    List<String> owners = new LinkedList<String>();
+                    List<String> users = new LinkedList<String>();
+                    DescribeImagesRequest request = new DescribeImagesRequest();
+                    request.setImageIds(images);
+                    request.setOwners(owners);
+                    request.setExecutableUsers(users);
+                    List<Image> img = ec2.describeImages(request).getImages();
+                    if(img==null || img.isEmpty())
+                        // de-registered AMI causes an empty list to be returned. so be defensive
+                        // against other possibilities
+                        return FormValidation.error("No such AMI, or not usable with this accessId: "+ami);
+                    return FormValidation.ok(img.get(0).getImageLocation()+" by "+img.get(0).getImageOwnerAlias());
+                } catch (AmazonClientException e) {
+                    return FormValidation.error(e.getMessage());
+                }
+            } else
+                return FormValidation.ok();   // can't test
+        }
+
+        public FormValidation doCheckIdleTerminationMinutes(@QueryParameter String value) {
+            if (value == null || value.trim() == "") return FormValidation.ok();
+            try {
+                int val = Integer.parseInt(value);
+                if (val >= 0) return FormValidation.ok();
+            }
+            catch ( NumberFormatException nfe ) {}
+            return FormValidation.error("Idle Termination time must be a non-negative integer (or null)");
+        }
+
+        public FormValidation doCheckInstanceCapStr(@QueryParameter String value) {
+            if (value == null || value.trim() == "") return FormValidation.ok();
+            try {
+                int val = Integer.parseInt(value);
+                if (val >= 0) return FormValidation.ok();
+            } catch ( NumberFormatException nfe ) {}
+            return FormValidation.error("InstanceCap must be a non-negative integer (or null)");
+        }
+        
+        public ListBoxModel doFillZoneItems( @QueryParameter String accessId,
+                                             @QueryParameter String secretKey,
+                                             @QueryParameter String region)
+                                             throws IOException, ServletException
+        {
+            return EC2Slave.fillZoneItems(accessId, secretKey, region);
+        }
+    }
+}
+

+ 27 - 0
source-code/maven-generic-conf/extlibs/ec2-plugin/src/main/java/hudson/plugins/ec2/ebs/ZPoolExpandNotice.java

@@ -0,0 +1,27 @@
+package hudson.plugins.ec2.ebs;
+
+import hudson.model.AdministrativeMonitor;
+import hudson.Extension;
+
+/**
+ * {@link AdministrativeMonitor} that tells the user that ZFS pool is filling up
+ * and they need to add more storage.
+ *
+ * @author Kohsuke Kawaguchi
+ */
+@Extension
+public class ZPoolExpandNotice extends AdministrativeMonitor {
+    /**
+     * Set by {@link ZPoolMonitor}.
+     */
+    /*package*/ boolean activated = false;
+
+    public ZPoolExpandNotice() {
+        super("zpool.ebs");
+    }
+
+    @Override
+	public boolean isActivated() {
+        return activated;
+    }
+}

+ 66 - 0
source-code/maven-generic-conf/extlibs/ec2-plugin/src/main/java/hudson/plugins/ec2/ebs/ZPoolMonitor.java

@@ -0,0 +1,66 @@
+package hudson.plugins.ec2.ebs;
+
+import hudson.model.PeriodicWork;
+import hudson.model.Hudson;
+import hudson.model.AdministrativeMonitor;
+import hudson.util.TimeUnit2;
+import hudson.Extension;
+import org.jvnet.solaris.libzfs.LibZFS;
+import org.jvnet.solaris.libzfs.ZFSFileSystem;
+import org.jvnet.solaris.libzfs.ZFSPool;
+
+import java.net.URL;
+import java.io.IOException;
+
+/**
+ * Once an hour, check if the main zpool is that hosts $HUDSON_HOME has still enough free space.
+ *
+ * @author Kohsuke Kawaguchi
+ */
+@Extension
+public class ZPoolMonitor extends PeriodicWork {
+    @Override
+	public long getRecurrencePeriod() {
+        return TimeUnit2.HOURS.toMillis(1);
+    }
+
+    @Override
+	protected void doRun() {
+        ZFSFileSystem fs=null;
+        try {
+            if(isInsideEC2())
+                fs = new LibZFS().getFileSystemByMountPoint(Hudson.getInstance().getRootDir());
+        } catch (LinkageError e) {
+            // probably not running on OpenSolaris
+        }
+        if(fs==null) {
+            cancel();
+            return;
+        }
+        ZFSPool pool = fs.getPool();
+        long a = pool.getAvailableSize();
+        long t = pool.getSize();
+
+        // if the disk is 90% filled up and the available space is less than 1GB,
+        // notify the user
+        ZPoolExpandNotice zen = AdministrativeMonitor.all().get(ZPoolExpandNotice.class);
+        zen.activated = t/a>10 && a<1000L*1000*1000;
+    }
+
+    private static Boolean isInsideEC2;
+
+    /**
+     * Returns true if this JVM runs inside EC2.
+     */
+    public static synchronized boolean isInsideEC2() {
+        if(isInsideEC2==null) {
+            try {
+                new URL("http://169.254.169.254/latest").openStream().close();
+                isInsideEC2 = true;
+            } catch (IOException e) {
+                isInsideEC2 = false;
+            }
+        }
+        return isInsideEC2;
+    }
+}

+ 9 - 0
source-code/maven-generic-conf/extlibs/ec2-plugin/src/main/java/hudson/plugins/ec2/ebs/package-info.java

@@ -0,0 +1,9 @@
+/**
+ * Code that deals with HUDSON_HOME on EBS, which is used in
+ * conjunction with our EC2 launch wizard.
+ *
+ * <p>
+ * This should eventually move to its own plugin, but for
+ * now I'm putting this here.
+ */
+package hudson.plugins.ec2.ebs;

+ 555 - 0
source-code/maven-generic-conf/extlibs/ec2-plugin/src/main/java/hudson/plugins/ec2/one/OneEC2Cloud.java

@@ -0,0 +1,555 @@
+package hudson.plugins.ec2.one;
+
+import hudson.model.Computer;
+import hudson.model.Descriptor;
+import hudson.model.Hudson;
+import hudson.model.Label;
+import hudson.model.Node;
+import hudson.plugins.ec2.EC2Cloud;
+import hudson.plugins.ec2.EC2PrivateKey;
+import hudson.plugins.ec2.SlaveTemplate;
+import hudson.slaves.Cloud;
+import hudson.slaves.NodeProvisioner.PlannedNode;
+import hudson.util.FormValidation;
+import hudson.util.Secret;
+import hudson.util.StreamTaskListener;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.StringReader;
+import java.io.StringWriter;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Date;
+import java.util.List;
+import java.util.concurrent.Callable;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import javax.servlet.ServletException;
+
+import org.kohsuke.stapler.QueryParameter;
+import org.kohsuke.stapler.StaplerRequest;
+import org.kohsuke.stapler.StaplerResponse;
+
+import com.amazonaws.AmazonClientException;
+import com.amazonaws.auth.AWSCredentials;
+import com.amazonaws.auth.BasicAWSCredentials;
+import com.amazonaws.services.ec2.AmazonEC2;
+import com.amazonaws.services.ec2.AmazonEC2Client;
+import com.amazonaws.services.ec2.model.CreateKeyPairRequest;
+import com.amazonaws.services.ec2.model.Instance;
+import com.amazonaws.services.ec2.model.InstanceStateName;
+import com.amazonaws.services.ec2.model.InstanceType;
+import com.amazonaws.services.ec2.model.KeyPair;
+import com.amazonaws.services.ec2.model.KeyPairInfo;
+import com.amazonaws.services.ec2.model.Reservation;
+import com.amazonaws.services.s3.AmazonS3;
+import com.amazonaws.services.s3.AmazonS3Client;
+import com.amazonaws.services.s3.model.GeneratePresignedUrlRequest;
+
+/**
+ * Hudson's view of EC2.
+ * 
+ * @author Kohsuke Kawaguchi
+ */
+public abstract class OneEC2Cloud extends Cloud {
+
+    public static final String DEFAULT_EC2_HOST = "us-east-1";
+
+    public static final String EC2_URL_HOST = "opennebula";
+
+    public static final String EC2_PORT = "4567";
+
+    protected final String accessId;
+
+    protected final Secret secretKey;
+
+    protected final EC2PrivateKey privateKey;
+
+    /**
+     * Upper bound on how many instances we may provision.
+     */
+    public final int instanceCap;
+
+    protected final List<OneSlaveTemplate> templates;
+
+    protected transient KeyPair usableKeyPair;
+
+    protected transient AmazonEC2 connection;
+
+    protected static AWSCredentials awsCredentials;
+
+    protected OneEC2Cloud(final String id, final String accessId,
+            final String secretKey, final String privateKey,
+            final String instanceCapStr,
+            final List<OneSlaveTemplate> templates) {
+        super(id);
+        this.accessId = accessId.trim();
+        this.secretKey = Secret.fromString(secretKey.trim());
+        this.privateKey =
+            (null != privateKey) ? new EC2PrivateKey(privateKey) : null;
+
+        if (templates == null) {
+            this.templates = Collections.emptyList();
+        } else {
+            this.templates = templates;
+        }
+
+        if (instanceCapStr.equals("")) {
+            instanceCap = Integer.MAX_VALUE;
+        } else {
+            instanceCap = Integer.parseInt(instanceCapStr);
+        }
+
+        readResolve(); // set parents
+    }
+
+    public abstract URL getEc2EndpointUrl() throws IOException;
+
+    public abstract URL getS3EndpointUrl() throws IOException;
+
+    protected Object readResolve() {
+        for (OneSlaveTemplate t : templates) {
+            t.parent = this;
+        }
+        return this;
+    }
+
+    public String getAccessId() {
+        return accessId;
+    }
+
+    public String getSecretKey() {
+        return secretKey.getEncryptedValue();
+    }
+
+    public EC2PrivateKey getPrivateKey() {
+        return privateKey;
+    }
+
+    public String getInstanceCapStr() {
+        if (instanceCap == Integer.MAX_VALUE) {
+            return "";
+        } else {
+            return String.valueOf(instanceCap);
+        }
+    }
+
+    public List<OneSlaveTemplate> getTemplates() {
+        return Collections.unmodifiableList(templates);
+    }
+
+    public OneSlaveTemplate getTemplate(final String ami) {
+        for (OneSlaveTemplate t : templates) {
+            if (t.ami.equals(ami)) {
+                return t;
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Gets {@link SlaveTemplate} that has the matching {@link Label}.
+     */
+    public OneSlaveTemplate getTemplate(final Label label) {
+        for (OneSlaveTemplate t : templates) {
+            if (label == null || label.matches(t.getLabelSet())) {
+                return t;
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Gets the {@link KeyPairInfo} used for the launch.
+     */
+    public synchronized KeyPair getKeyPair()
+            throws AmazonClientException, IOException {
+        if (usableKeyPair == null && null != privateKey) {
+            usableKeyPair = privateKey.find(connect());
+        }
+        return usableKeyPair;
+    }
+
+    /**
+     * Counts the number of instances in EC2 currently running that are using
+     * the specifed image.
+     * 
+     * @param ami
+     *            If AMI is left null, then all instances are counted.
+     *            <p>
+     *            This includes those instances that may be started outside
+     *            Hudson.
+     */
+    public int countCurrentEC2Slaves(final String ami)
+            throws AmazonClientException {
+        int n = 0;
+        for (Reservation r : connect().describeInstances()
+            .getReservations()) {
+            for (Instance i : r.getInstances()) {
+                if (ami == null || ami.equals(i.getImageId())) {
+                    InstanceStateName stateName =
+                        InstanceStateName
+                            .fromValue(i.getState().getName());
+                    if (stateName == InstanceStateName.Pending
+                        || stateName == InstanceStateName.Running) {
+                        n++;
+                    }
+                }
+            }
+        }
+        return n;
+    }
+
+    /**
+     * Debug command to attach to a running instance.
+     */
+    public void doAttach(final StaplerRequest req,
+            final StaplerResponse rsp, @QueryParameter final String id)
+            throws ServletException, IOException, AmazonClientException {
+        checkPermission(PROVISION);
+        OneSlaveTemplate t = getTemplates().get(0);
+
+        StringWriter sw = new StringWriter();
+        StreamTaskListener listener = new StreamTaskListener(sw);
+        OneEC2Slave node = t.attach(id, listener);
+        Hudson.getInstance().addNode(node);
+
+        rsp.sendRedirect2(req.getContextPath() + "/computer/"
+            + node.getNodeName());
+    }
+
+    public void doProvision(final StaplerRequest req,
+            final StaplerResponse rsp, @QueryParameter final String ami)
+            throws ServletException, IOException {
+        checkPermission(PROVISION);
+        if (ami == null) {
+            sendError("The 'ami' query parameter is missing", req, rsp);
+            return;
+        }
+        OneSlaveTemplate t = getTemplate(ami);
+        if (t == null) {
+            sendError("No such AMI: " + ami, req, rsp);
+            return;
+        }
+
+        StringWriter sw = new StringWriter();
+        StreamTaskListener listener = new StreamTaskListener(sw);
+        try {
+            OneEC2Slave node = t.provision(listener);
+            Hudson.getInstance().addNode(node);
+
+            rsp.sendRedirect2(req.getContextPath() + "/computer/"
+                + node.getNodeName());
+        } catch (AmazonClientException e) {
+            e.printStackTrace(listener.error(e.getMessage()));
+            sendError(sw.toString(), req, rsp);
+        }
+    }
+
+    @Override
+    public Collection<PlannedNode> provision(final Label label,
+            int excessWorkload) {
+        try {
+
+            final OneSlaveTemplate t = getTemplate(label);
+
+            List<PlannedNode> r = new ArrayList<PlannedNode>();
+            for (; excessWorkload > 0; excessWorkload--) {
+                if (countCurrentEC2Slaves(null) >= instanceCap) {
+                    LOGGER.log(Level.INFO,
+                        "Instance cap reached, not provisioning.");
+                    break; // maxed out
+                }
+
+                int amiCap = t.getInstanceCap();
+                if (amiCap < countCurrentEC2Slaves(t.ami)) {
+                    LOGGER.log(Level.INFO,
+                        "AMI Instance cap reached, not provisioning.");
+                    break; // maxed out
+                }
+
+                r.add(new PlannedNode(t.getDisplayName(),
+                    Computer.threadPoolForRemoting
+                        .submit(new Callable<Node>() {
+                            public Node call() throws Exception {
+                                // TODO: record the output somewhere
+                                OneEC2Slave s =
+                                    t.provision(new StreamTaskListener(
+                                        System.out));
+                                Hudson.getInstance().addNode(s);
+                                // EC2 instances may have a long init script. If
+                                // we declare
+                                // the provisioning complete by returning
+                                // without the connect
+                                // operation, NodeProvisioner may decide that it
+                                // still wants
+                                // one more instance, because it sees that (1)
+                                // all the slaves
+                                // are offline (because it's still being
+                                // launched) and
+                                // (2) there's no capacity provisioned yet.
+                                //
+                                // deferring the completion of provisioning
+                                // until the launch
+                                // goes successful prevents this problem.
+                                s.toComputer().connect(false).get();
+                                return s;
+                            }
+                        }), t.getNumExecutors()));
+            }
+            return r;
+        } catch (AmazonClientException e) {
+            LOGGER.log(Level.WARNING,
+                "Failed to count the # of live instances on EC2", e);
+            return Collections.emptyList();
+        }
+    }
+
+    @Override
+    public boolean canProvision(final Label label) {
+        return getTemplate(label) != null;
+    }
+
+    /**
+     * Gets the first {@link EC2Cloud} instance configured in the current
+     * Hudson, or null if no such thing exists.
+     */
+    public static OneEC2Cloud get() {
+        return Hudson.getInstance().clouds.get(OneEC2Cloud.class);
+    }
+
+    /**
+     * Connects to EC2 and returns {@link AmazonEC2}, which can then be used to
+     * communicate with EC2.
+     */
+    public synchronized AmazonEC2 connect() throws AmazonClientException {
+        try {
+            if (connection == null) {
+                connection =
+                    connect(accessId, secretKey, getEc2EndpointUrl());
+            }
+            return connection;
+        } catch (IOException e) {
+            throw new AmazonClientException(
+                "Failed to retrieve the endpoint", e);
+        }
+    }
+
+    /***
+     * Connect to an EC2 instance.
+     * 
+     * @return {@link AmazonEC2} client
+     */
+    public static AmazonEC2 connect(final String accessId,
+            final String secretKey, final URL endpoint) {
+        return connect(accessId, Secret.fromString(secretKey), endpoint);
+    }
+
+    /***
+     * Connect to an EC2 instance.
+     * 
+     * @return {@link AmazonEC2} client
+     */
+    public static AmazonEC2 connect(final String accessId,
+            final Secret secretKey, final URL endpoint) {
+        awsCredentials =
+            new BasicAWSCredentials(accessId, Secret.toString(secretKey));
+        AmazonEC2 client = new AmazonEC2Client(awsCredentials);
+        client.setEndpoint(endpoint.toString());
+        return client;
+    }
+
+    /***
+     * Convert a configured hostname like 'us-east-1' to a FQDN or ip address
+     */
+    public static String convertHostName(String ec2HostName) {
+        if (ec2HostName == null || ec2HostName.length() == 0) {
+            ec2HostName = DEFAULT_EC2_HOST;
+        }
+        if (!ec2HostName.contains(".")) {
+            ec2HostName = ec2HostName + "." + EC2_URL_HOST;
+        }
+        return ec2HostName;
+    }
+
+    /***
+     * Convert a user entered string into a port number "" -> -1 to indicate
+     * default based on SSL setting
+     */
+    public static Integer convertPort(final String ec2Port) {
+        if (ec2Port == null || ec2Port.length() == 0) {
+            return -1;
+        } else {
+            return Integer.parseInt(ec2Port);
+        }
+    }
+
+    /**
+     * Computes the presigned URL for the given S3 resource.
+     * 
+     * @param path
+     *            String like "/bucketName/folder/folder/abc.txt" that
+     *            represents the resource to request.
+     */
+    public URL buildPresignedURL(final String path)
+            throws IOException, AmazonClientException {
+        long expires = System.currentTimeMillis() + 60 * 60 * 1000;
+        GeneratePresignedUrlRequest request =
+            new GeneratePresignedUrlRequest(path,
+                Secret.toString(secretKey));
+        request.setExpiration(new Date(expires));
+        AmazonS3 s3 = new AmazonS3Client(awsCredentials);
+        return s3.generatePresignedUrl(request);
+    }
+
+    /* Parse a url or return a sensible error */
+    public static URL checkEndPoint(final String url)
+            throws FormValidation {
+        try {
+            return new URL(url);
+        } catch (MalformedURLException ex) {
+            throw FormValidation.error("Endpoint URL is not a valid URL");
+        }
+    }
+
+    public static abstract class DescriptorImpl extends Descriptor<Cloud> {
+        public InstanceType[] getInstanceTypes() {
+            return InstanceType.values();
+        }
+
+        public FormValidation doCheckAccessId(
+                @QueryParameter final String value)
+                throws IOException, ServletException {
+            return FormValidation.validateBase64(value, false, false,
+                Messages.EC2Cloud_InvalidAccessId());
+        }
+
+        public FormValidation doCheckSecretKey(
+                @QueryParameter final String value)
+                throws IOException, ServletException {
+            return FormValidation.validateBase64(value, false, false,
+                Messages.EC2Cloud_InvalidSecretKey());
+        }
+
+        public FormValidation doCheckPrivateKey(
+                @QueryParameter final String value)
+                throws IOException, ServletException {
+            boolean hasStart = false, hasEnd = false;
+            BufferedReader br =
+                new BufferedReader(new StringReader(value));
+            String line;
+            while ((line = br.readLine()) != null) {
+                if (line.equals("-----BEGIN RSA PRIVATE KEY-----")) {
+                    hasStart = true;
+                }
+                if (line.equals("-----END RSA PRIVATE KEY-----")) {
+                    hasEnd = true;
+                }
+            }
+            if (!hasStart) {
+                return FormValidation
+                    .error("This doesn't look like a private key at all");
+            }
+            if (!hasEnd) {
+                return FormValidation
+                    .error("The private key is missing the trailing 'END RSA PRIVATE KEY' marker. Copy&paste error?");
+            }
+            return FormValidation.ok();
+        }
+
+        protected FormValidation doTestConnection(final URL ec2endpoint,
+                final String accessId, final String secretKey,
+                final String privateKey)
+                throws IOException, ServletException {
+            try {
+                AmazonEC2 ec2 = connect(accessId, secretKey, ec2endpoint);
+                ec2.describeInstances();
+
+                if (accessId == null) {
+                    return FormValidation
+                        .error("Access ID is not specified");
+                }
+                if (secretKey == null) {
+                    return FormValidation
+                        .error("Secret key is not specified");
+                }
+
+                if (null != privateKey && privateKey.trim().length() > 0) {
+                    // check if this key exists
+                    EC2PrivateKey pk = new EC2PrivateKey(privateKey);
+                    if (pk.find(ec2) == null) {
+                        return FormValidation
+                            .error("The EC2 key pair private key isn't registered to this EC2 region (fingerprint is "
+                                + pk.getFingerprint() + ")");
+                    }
+                }
+
+                return FormValidation.ok(Messages.EC2Cloud_Success());
+            } catch (AmazonClientException e) {
+                LOGGER.log(Level.WARNING,
+                    "Failed to check EC2 credential", e);
+                return FormValidation.error(e.getMessage());
+            }
+        }
+
+        public FormValidation doGenerateKey(final StaplerResponse rsp,
+                final URL ec2EndpointUrl, final String accessId,
+                final String secretKey)
+                throws IOException, ServletException {
+            try {
+                AmazonEC2 ec2 =
+                    connect(accessId, secretKey, ec2EndpointUrl);
+                List<KeyPairInfo> existingKeys =
+                    ec2.describeKeyPairs().getKeyPairs();
+
+                int n = 0;
+                while (true) {
+                    boolean found = false;
+                    for (KeyPairInfo k : existingKeys) {
+                        if (k.getKeyName().equals("hudson-" + n)) {
+                            found = true;
+                        }
+                    }
+                    if (!found) {
+                        break;
+                    }
+                    n++;
+                }
+
+                CreateKeyPairRequest request =
+                    new CreateKeyPairRequest("hudson-" + n);
+                KeyPair key = ec2.createKeyPair(request).getKeyPair();
+
+                rsp.addHeader("script",
+                    "findPreviousFormItem(button,'privateKey').value='"
+                        + key.getKeyMaterial().replace("\n", "\\n") + "'");
+
+                return FormValidation.ok(Messages.EC2Cloud_Success());
+            } catch (AmazonClientException e) {
+                LOGGER.log(Level.WARNING,
+                    "Failed to check EC2 credential", e);
+                return FormValidation.error(e.getMessage());
+            }
+        }
+    }
+
+    private static final Logger LOGGER = Logger
+        .getLogger(OneEC2Cloud.class.getName());
+
+    protected static boolean isSSL(final URL endpoint) {
+        return endpoint.getProtocol().equals("https");
+    }
+
+    protected static int portFromURL(final URL endpoint) {
+        int ec2Port = endpoint.getPort();
+        if (ec2Port == -1) {
+            ec2Port = endpoint.getDefaultPort();
+        }
+        return ec2Port;
+    }
+}

+ 139 - 0
source-code/maven-generic-conf/extlibs/ec2-plugin/src/main/java/hudson/plugins/ec2/one/OneEC2Computer.java

@@ -0,0 +1,139 @@
+package hudson.plugins.ec2.one;
+
+import hudson.Util;
+import hudson.plugins.ec2.EC2Cloud;
+import hudson.plugins.ec2.InstanceState;
+import hudson.slaves.SlaveComputer;
+
+import java.io.IOException;
+import java.util.Collections;
+
+import org.kohsuke.stapler.HttpRedirect;
+import org.kohsuke.stapler.HttpResponse;
+
+import com.amazonaws.AmazonClientException;
+import com.amazonaws.services.ec2.AmazonEC2;
+import com.amazonaws.services.ec2.model.DescribeInstancesRequest;
+import com.amazonaws.services.ec2.model.GetConsoleOutputRequest;
+import com.amazonaws.services.ec2.model.Instance;
+
+/**
+ * @author Kohsuke Kawaguchi
+ */
+public class OneEC2Computer extends SlaveComputer {
+    /**
+     * Cached description of this EC2 instance. Lazily fetched.
+     */
+    private volatile Instance ec2InstanceDescription;
+
+    public OneEC2Computer(final OneEC2Slave slave) {
+        super(slave);
+    }
+
+    @Override
+    public OneEC2Slave getNode() {
+        return (OneEC2Slave) super.getNode();
+    }
+
+    public String getInstanceId() {
+        return getName();
+    }
+
+    /**
+     * Gets the EC2 console output.
+     */
+    public String getConsoleOutput() throws AmazonClientException {
+        AmazonEC2 ec2 = OneEC2Cloud.get().connect();
+        GetConsoleOutputRequest request =
+            new GetConsoleOutputRequest(getInstanceId());
+        return ec2.getConsoleOutput(request).getOutput();
+    }
+
+    /**
+     * Obtains the instance state description in EC2.
+     * <p>
+     * This method returns a cached state, so it's not suitable to check
+     * {@link Instance#getState()} and {@link Instance#getStateCode()} from the
+     * returned instance (but all the other fields are valid as it won't
+     * change.) The cache can be flushed using
+     * {@link #updateInstanceDescription()}
+     */
+    public Instance describeInstance() throws AmazonClientException {
+        if (ec2InstanceDescription == null) {
+            ec2InstanceDescription = _describeInstance();
+        }
+        return ec2InstanceDescription;
+    }
+
+    /**
+     * This will flush any cached description held by
+     * {@link #describeInstance()}.
+     */
+    public Instance updateInstanceDescription()
+            throws AmazonClientException {
+        return ec2InstanceDescription = _describeInstance();
+    }
+
+    /**
+     * Gets the current state of the instance.
+     * <p>
+     * Unlike {@link #describeInstance()}, this method always return the current
+     * status by calling EC2.
+     */
+    public InstanceState getState() throws AmazonClientException {
+        ec2InstanceDescription = _describeInstance();
+        return InstanceState.find(ec2InstanceDescription.getState()
+            .getName());
+    }
+
+    /**
+     * Number of milli-secs since the instance was started.
+     */
+    public long getUptime() throws AmazonClientException {
+        return System.currentTimeMillis()
+            - describeInstance().getLaunchTime().getTime();
+    }
+
+    /**
+     * Returns uptime in the human readable form.
+     */
+    public String getUptimeString() throws AmazonClientException {
+        return Util.getTimeSpanString(getUptime());
+    }
+
+    private Instance _describeInstance() throws AmazonClientException {
+        DescribeInstancesRequest request = new DescribeInstancesRequest();
+        request.setInstanceIds(Collections
+            .<String> singletonList(getNode().getInstanceId()));
+        return EC2Cloud.get().connect().describeInstances(request)
+            .getReservations().get(0).getInstances().get(0);
+    }
+
+    /**
+     * When the slave is deleted, terminate the instance.
+     */
+    @Override
+    public HttpResponse doDoDelete() throws IOException {
+        checkPermission(DELETE);
+        if (getNode() != null) {
+            getNode().terminate();
+        }
+        return new HttpRedirect("..");
+    }
+
+    /**
+     * What username to use to run root-like commands
+     */
+    public String getRemoteAdmin() {
+        return getNode().getRemoteAdmin();
+    }
+
+    public int getSshPort() {
+        return getNode().getSshPort();
+    }
+
+    public String getRootCommandPrefix() {
+        return getNode().getRootCommandPrefix();
+    }
+
+}

+ 458 - 0
source-code/maven-generic-conf/extlibs/ec2-plugin/src/main/java/hudson/plugins/ec2/one/OneEC2Slave.java

@@ -0,0 +1,458 @@
+package hudson.plugins.ec2.one;
+
+import hudson.Extension;
+import hudson.Util;
+import hudson.model.Computer;
+import hudson.model.Descriptor.FormException;
+import hudson.model.Hudson;
+import hudson.model.Node;
+import hudson.model.Slave;
+import hudson.plugins.ec2.EC2RetentionStrategy;
+import hudson.plugins.ec2.SlaveTemplate;
+import hudson.plugins.ec2.ssh.EC2UnixLauncher;
+import hudson.slaves.NodeProperty;
+
+import java.io.IOException;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import net.sf.json.JSONObject;
+
+import org.kohsuke.stapler.DataBoundConstructor;
+import org.kohsuke.stapler.StaplerRequest;
+
+import com.amazonaws.AmazonClientException;
+import com.amazonaws.services.ec2.AmazonEC2;
+import com.amazonaws.services.ec2.model.CreateTagsRequest;
+import com.amazonaws.services.ec2.model.DeleteTagsRequest;
+import com.amazonaws.services.ec2.model.DescribeInstancesRequest;
+import com.amazonaws.services.ec2.model.Instance;
+import com.amazonaws.services.ec2.model.InstanceStateName;
+import com.amazonaws.services.ec2.model.Reservation;
+import com.amazonaws.services.ec2.model.StopInstancesRequest;
+import com.amazonaws.services.ec2.model.Tag;
+import com.amazonaws.services.ec2.model.TerminateInstancesRequest;
+
+/**
+ * Slave running on EC2.
+ * 
+ * @author Kohsuke Kawaguchi
+ */
+public final class OneEC2Slave extends Slave {
+    /**
+     * Comes from {@link SlaveTemplate#initScript}.
+     */
+    public final String initScript;
+
+    public final String remoteAdmin; // e.g. 'ubuntu'
+
+    public final String rootCommandPrefix; // e.g. 'sudo'
+
+    public final String jvmopts; // e.g. -Xmx1g
+
+    public final boolean stopOnTerminate;
+
+    public final String idleTerminationMinutes;
+
+    public final boolean usePrivateDnsName;
+
+    public List<OneEC2Tag> tags;
+
+    // Temporary stuff that is obtained live from EC2
+    public String publicDNS;
+
+    public String privateDNS;
+
+    /* The last instance data to be fetched for the slave */
+    private Instance lastFetchInstance = null;
+
+    /* The time at which we fetched the last instance data */
+    private long lastFetchTime = 0;
+
+    /*
+     * The time (in milliseconds) after which we will always re-fetch externally
+     * changeable EC2 data when we are asked for it
+     */
+    private static final long MIN_FETCH_TIME = 20 * 1000;
+
+    /**
+     * For data read from old Hudson, this is 0, so we use that to indicate 22.
+     */
+    private final int sshPort;
+
+    public static final String TEST_ZONE = "testZone";
+
+    public OneEC2Slave(final String instanceId, final String description,
+            final String remoteFS, final int sshPort,
+            final int numExecutors, final String labelString,
+            final String initScript, final String remoteAdmin,
+            final String rootCommandPrefix, final String jvmopts,
+            final boolean stopOnTerminate,
+            final String idleTerminationMinutes, final String publicDNS,
+            final String privateDNS, final List<OneEC2Tag> tags)
+            throws FormException, IOException {
+        this(instanceId, description, remoteFS, sshPort, numExecutors,
+            Mode.NORMAL, labelString, initScript, Collections
+                .<NodeProperty<?>> emptyList(), remoteAdmin,
+            rootCommandPrefix, jvmopts, stopOnTerminate,
+            idleTerminationMinutes, publicDNS, privateDNS, tags, false);
+    }
+
+    public OneEC2Slave(final String instanceId, final String description,
+            final String remoteFS, final int sshPort,
+            final int numExecutors, final String labelString,
+            final String initScript, final String remoteAdmin,
+            final String rootCommandPrefix, final String jvmopts,
+            final boolean stopOnTerminate,
+            final String idleTerminationMinutes, final String publicDNS,
+            final String privateDNS, final List<OneEC2Tag> tags,
+            final boolean usePrivateDnsName)
+            throws FormException, IOException {
+        this(instanceId, description, remoteFS, sshPort, numExecutors,
+            Mode.NORMAL, labelString, initScript, Collections
+                .<NodeProperty<?>> emptyList(), remoteAdmin,
+            rootCommandPrefix, jvmopts, stopOnTerminate,
+            idleTerminationMinutes, publicDNS, privateDNS, tags,
+            usePrivateDnsName);
+    }
+
+    @DataBoundConstructor
+    public OneEC2Slave(final String instanceId, final String description,
+            final String remoteFS, final int sshPort,
+            final int numExecutors, final Mode mode,
+            final String labelString, final String initScript,
+            final List<? extends NodeProperty<?>> nodeProperties,
+            final String remoteAdmin, final String rootCommandPrefix,
+            final String jvmopts, final boolean stopOnTerminate,
+            final String idleTerminationMinutes, final String publicDNS,
+            final String privateDNS, final List<OneEC2Tag> tags,
+            final boolean usePrivateDnsName)
+            throws FormException, IOException {
+
+        super(instanceId, description, remoteFS, numExecutors, mode,
+            labelString, new EC2UnixLauncher(), new EC2RetentionStrategy(
+                idleTerminationMinutes), nodeProperties);
+
+        this.initScript = initScript;
+        this.remoteAdmin = remoteAdmin;
+        this.rootCommandPrefix = rootCommandPrefix;
+        this.jvmopts = jvmopts;
+        this.sshPort = sshPort;
+        this.stopOnTerminate = stopOnTerminate;
+        this.idleTerminationMinutes = idleTerminationMinutes;
+        this.publicDNS = publicDNS;
+        this.privateDNS = privateDNS;
+        this.tags = tags;
+        this.usePrivateDnsName = usePrivateDnsName;
+    }
+
+    /**
+     * Constructor for debugging.
+     */
+    public OneEC2Slave(final String instanceId)
+            throws FormException, IOException {
+        this(instanceId, "debug", "/tmp/hudson", 22, 1, Mode.NORMAL,
+            "debug", "", Collections.<NodeProperty<?>> emptyList(), null,
+            null, null, false, null, "Fake public", "Fake private", null,
+            false);
+    }
+
+    /**
+     * See http://aws.amazon.com/ec2/instance-types/
+     */
+    /* package */static int toNumExecutors(final OneInstanceType type) {
+        switch (type) {
+        case M0:
+            return 1;
+        case M1:
+            return 2;
+        case M2:
+            return 3;
+        case M3:
+            return 4;
+        case M4:
+            return 8;
+        case M1Jenkins:
+            return 2;
+        case M2Jenkins:
+            return 4;
+        default:
+            throw new AssertionError();
+        }
+    }
+
+    /**
+     * EC2 instance ID.
+     */
+    public String getInstanceId() {
+        return getNodeName();
+    }
+
+    @Override
+    public Computer createComputer() {
+        return new OneEC2Computer(this);
+    }
+
+    public static Instance getInstance(final String instanceId) {
+        DescribeInstancesRequest request = new DescribeInstancesRequest();
+        request.setInstanceIds(Collections
+            .<String> singletonList(instanceId));
+        OneEC2Cloud cloudInstance = OneEC2Cloud.get();
+        if (cloudInstance == null) {
+            return null;
+        }
+        AmazonEC2 ec2 = cloudInstance.connect();
+        List<Reservation> reservations =
+            ec2.describeInstances(request).getReservations();
+        Instance i = null;
+        if (reservations.size() > 0) {
+            List<Instance> instances = reservations.get(0).getInstances();
+            if (instances.size() > 0) {
+                i = instances.get(0);
+            }
+        }
+        return i;
+    }
+
+    /**
+     * Terminates the instance in EC2.
+     */
+    public void terminate() {
+        try {
+            if (!isAlive(true)) {
+                /*
+                 * The node has been killed externally, so we've nothing to do
+                 * here
+                 */
+                LOGGER.info("EC2 instance already terminated: "
+                    + getInstanceId());
+            } else {
+                AmazonEC2 ec2 = OneEC2Cloud.get().connect();
+                TerminateInstancesRequest request =
+                    new TerminateInstancesRequest(
+                        Collections.singletonList(getInstanceId()));
+                ec2.terminateInstances(request);
+                LOGGER.info("Terminated EC2 instance (terminated): "
+                    + getInstanceId());
+            }
+            Hudson.getInstance().removeNode(this);
+        } catch (AmazonClientException e) {
+            LOGGER.log(Level.WARNING, "Failed to terminate EC2 instance: "
+                + getInstanceId(), e);
+        } catch (IOException e) {
+            LOGGER.log(Level.WARNING, "Failed to terminate EC2 instance: "
+                + getInstanceId(), e);
+        }
+    }
+
+    void idleTimeout() {
+        LOGGER.info("EC2 instance idle time expired: " + getInstanceId());
+        if (!stopOnTerminate) {
+            terminate();
+            return;
+        }
+
+        try {
+            AmazonEC2 ec2 = OneEC2Cloud.get().connect();
+            StopInstancesRequest request =
+                new StopInstancesRequest(
+                    Collections.singletonList(getInstanceId()));
+            ec2.stopInstances(request);
+            toComputer().disconnect(null);
+        } catch (AmazonClientException e) {
+            Instance i = getInstance(getNodeName());
+            LOGGER.log(Level.WARNING, "Failed to terminate EC2 instance: "
+                + getInstanceId() + " info: " + ((i != null) ? i : ""), e);
+        }
+        LOGGER.info("EC2 instance stopped: " + getInstanceId());
+    }
+
+    String getRemoteAdmin() {
+        if (remoteAdmin == null || remoteAdmin.length() == 0) {
+            return "root";
+        }
+        return remoteAdmin;
+    }
+
+    String getRootCommandPrefix() {
+        if (rootCommandPrefix == null || rootCommandPrefix.length() == 0) {
+            return "";
+        }
+        return rootCommandPrefix + " ";
+    }
+
+    String getJvmopts() {
+        return Util.fixNull(jvmopts);
+    }
+
+    public int getSshPort() {
+        return sshPort != 0 ? sshPort : 22;
+    }
+
+    public boolean getStopOnTerminate() {
+        return stopOnTerminate;
+    }
+
+    private boolean isAlive(final boolean force) {
+        fetchLiveInstanceData(force);
+        if (lastFetchInstance == null) {
+            return false;
+        }
+        if (lastFetchInstance.getState().getName()
+            .equals(InstanceStateName.Terminated.toString())) {
+            return false;
+        }
+        return true;
+    }
+
+    /*
+     * Much of the EC2 data is beyond our direct control, therefore we need to
+     * refresh it from time to time to ensure we reflect the reality of the
+     * instances.
+     */
+    private void fetchLiveInstanceData(final boolean force)
+            throws AmazonClientException {
+        /*
+         * If we've grabbed the data recently, don't bother getting it again
+         * unless we are forced
+         */
+        long now = System.currentTimeMillis();
+        if ((lastFetchTime > 0) && (now - lastFetchTime < MIN_FETCH_TIME)
+            && !force) {
+            return;
+        }
+
+        Instance i = getInstance(getNodeName());
+
+        lastFetchTime = now;
+        lastFetchInstance = i;
+        if (i == null) {
+            return;
+        }
+
+        publicDNS = i.getPublicDnsName();
+        privateDNS = i.getPrivateIpAddress();
+        tags = new LinkedList<OneEC2Tag>();
+
+        for (Tag t : i.getTags()) {
+            tags.add(new OneEC2Tag(t.getKey(), t.getValue()));
+        }
+    }
+
+    /*
+     * Clears all existing tag data so that we can force the instance into a
+     * known state
+     */
+    private void clearLiveInstancedata() throws AmazonClientException {
+        Instance inst = getInstance(getNodeName());
+
+        /* Now that we have our instance, we can clear the tags on it */
+        if (!tags.isEmpty()) {
+            HashSet<Tag> inst_tags = new HashSet<Tag>();
+
+            for (OneEC2Tag t : tags) {
+                inst_tags.add(new Tag(t.getName(), t.getValue()));
+            }
+
+            DeleteTagsRequest tag_request = new DeleteTagsRequest();
+            tag_request.withResources(inst.getInstanceId()).setTags(
+                inst_tags);
+            OneEC2Cloud.get().connect().deleteTags(tag_request);
+        }
+    }
+
+    /*
+     * Sets tags on an instance. This will not clear existing tag data, so call
+     * clearLiveInstancedata if needed
+     */
+    private void pushLiveInstancedata() throws AmazonClientException {
+        Instance inst = getInstance(getNodeName());
+
+        /* Now that we have our instance, we can set tags on it */
+        if (tags != null && !tags.isEmpty()) {
+            HashSet<Tag> inst_tags = new HashSet<Tag>();
+
+            for (OneEC2Tag t : tags) {
+                inst_tags.add(new Tag(t.getName(), t.getValue()));
+            }
+
+            CreateTagsRequest tag_request = new CreateTagsRequest();
+            tag_request.withResources(inst.getInstanceId()).setTags(
+                inst_tags);
+            OneEC2Cloud.get().connect().createTags(tag_request);
+        }
+    }
+
+    public String getPublicDNS() {
+        fetchLiveInstanceData(false);
+        return publicDNS;
+    }
+
+    public String getPrivateDNS() {
+        fetchLiveInstanceData(false);
+        return privateDNS;
+    }
+
+    public List<OneEC2Tag> getTags() {
+        fetchLiveInstanceData(false);
+        return Collections.unmodifiableList(tags);
+    }
+
+    @Override
+    public Node reconfigure(final StaplerRequest req, final JSONObject form)
+            throws FormException {
+        if (form == null) {
+            return null;
+        }
+
+        if (!isAlive(true)) {
+            LOGGER.info("EC2 instance terminated externally: "
+                + getInstanceId());
+            try {
+                Hudson.getInstance().removeNode(this);
+            } catch (IOException ioe) {
+                LOGGER
+                    .log(
+                        Level.WARNING,
+                        "Attempt to reconfigure EC2 instance which has been externally terminated: "
+                            + getInstanceId(), ioe);
+            }
+
+            return null;
+        }
+
+        Node result = super.reconfigure(req, form);
+
+        /* Get rid of the old tags, as represented by ourselves. */
+        clearLiveInstancedata();
+
+        /* Set the new tags, as represented by our successor */
+        ((OneEC2Slave) result).pushLiveInstancedata();
+
+        return result;
+    }
+
+    public boolean getUsePrivateDnsName() {
+        return usePrivateDnsName;
+    }
+
+    @Extension
+    public static final class DescriptorImpl extends SlaveDescriptor {
+        @Override
+        public String getDisplayName() {
+            return "OpenNebula";
+        }
+
+        @Override
+        public boolean isInstantiable() {
+            return false;
+        }
+    }
+
+    private static final Logger LOGGER = Logger
+        .getLogger(OneEC2Slave.class.getName());
+}

+ 82 - 0
source-code/maven-generic-conf/extlibs/ec2-plugin/src/main/java/hudson/plugins/ec2/one/OneEC2Tag.java

@@ -0,0 +1,82 @@
+package hudson.plugins.ec2.one;
+
+import hudson.model.Descriptor;
+import hudson.model.AbstractDescribableImpl;
+import hudson.Extension;
+import org.kohsuke.stapler.DataBoundConstructor;
+
+import java.util.List;
+import java.util.LinkedList;
+
+import com.amazonaws.services.ec2.model.Tag;
+
+
+public class OneEC2Tag extends AbstractDescribableImpl<OneEC2Tag>
+{
+   private String name;
+   private String value;
+
+   @DataBoundConstructor
+   public OneEC2Tag(String name, String value) {
+      this.name = name;
+      this.value = value;
+   }
+
+
+   /* Constructor from Amazon Tag */
+   public OneEC2Tag(Tag t) {
+       name = t.getKey();
+       value = t.getValue();
+   }
+
+
+   public String getName() {
+      return name;
+   }
+
+
+   public String getValue() {
+      return value;
+   }
+
+
+   @Override
+   public String toString() {
+      return "EC2Tag: " + name + "->" + value;
+   }
+
+
+   @Override
+   public boolean equals(Object o) {
+      if (o == null) return false;
+      if (!(o instanceof OneEC2Tag)) return false;
+
+      OneEC2Tag other = (OneEC2Tag) o;
+      if ((name == null && other.name != null) || !name.equals( other.name)) return false;
+      if ((value == null && other.value != null) || !value.equals( other.value)) return false;
+
+      return true;
+   }
+
+
+   @Extension
+   public static class DescriptorImpl extends Descriptor<OneEC2Tag> {
+      @Override
+	  public String getDisplayName() { return ""; }
+   }
+
+
+   /* Helper method to convert lists of Amazon tags into internal format */ 
+   public static List<OneEC2Tag> fromAmazonTags(List<Tag> amazon_tags) {
+       if (null == amazon_tags) {
+           return null;
+       }
+
+       LinkedList<OneEC2Tag> result = new LinkedList<OneEC2Tag>();
+       for (Tag t : amazon_tags) {
+           result.add(new OneEC2Tag(t));
+       }
+
+       return result;
+   }
+}

+ 54 - 0
source-code/maven-generic-conf/extlibs/ec2-plugin/src/main/java/hudson/plugins/ec2/one/OneInstanceType.java

@@ -0,0 +1,54 @@
+/**
+ * $Id$
+ * @author dcampos
+ * @date   Nov 6, 2012 2:54:25 PM
+ *
+ * Copyright (C) 2012 Scytl Secure Electronic Voting SA
+ *
+ * All rights reserved.
+ *
+ */
+package hudson.plugins.ec2.one;
+
+/**
+ *
+ */
+public enum OneInstanceType {
+
+    M0("m0"), M1("m1"), M2("m2"), M3("m3"), M4("m4"), M1Jenkins(
+            "m1.jenkins"), M2Jenkins("m2.jenkins");
+
+    private String value;
+
+    /**
+     * 
+     */
+    private OneInstanceType(final String value) {
+        this.value = value;
+    }
+
+    public static OneInstanceType fromValue(final String value) {
+        if (null == value) {
+            throw new IllegalArgumentException(
+                "Value cannot be null or empty!");
+        }
+
+        for (OneInstanceType it : values()) {
+            if (it.toString().equalsIgnoreCase(value)) {
+                return it;
+            }
+        }
+
+        throw new IllegalArgumentException("Cannot create enum from "
+            + value);
+    }
+
+    /**
+     * @see java.lang.Enum#toString()
+     */
+    @Override
+    public String toString() {
+        return value;
+    }
+
+}

+ 665 - 0
source-code/maven-generic-conf/extlibs/ec2-plugin/src/main/java/hudson/plugins/ec2/one/OneSlaveTemplate.java

@@ -0,0 +1,665 @@
+package hudson.plugins.ec2.one;
+
+import hudson.Extension;
+import hudson.Util;
+import hudson.model.Describable;
+import hudson.model.TaskListener;
+import hudson.model.Descriptor;
+import hudson.model.Descriptor.FormException;
+import hudson.model.Hudson;
+import hudson.model.Label;
+import hudson.model.Node;
+import hudson.model.labels.LabelAtom;
+import hudson.plugins.ec2.EC2Computer;
+import hudson.plugins.ec2.EC2Slave;
+import hudson.util.FormValidation;
+
+import java.io.IOException;
+import java.io.PrintStream;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Set;
+
+import javax.servlet.ServletException;
+
+import org.apache.commons.codec.binary.Base64;
+import org.apache.commons.lang.StringUtils;
+import org.kohsuke.stapler.DataBoundConstructor;
+import org.kohsuke.stapler.QueryParameter;
+
+import com.amazonaws.AmazonClientException;
+import com.amazonaws.services.ec2.AmazonEC2;
+import com.amazonaws.services.ec2.model.CreateTagsRequest;
+import com.amazonaws.services.ec2.model.DescribeImagesRequest;
+import com.amazonaws.services.ec2.model.DescribeInstancesRequest;
+import com.amazonaws.services.ec2.model.DescribeInstancesResult;
+import com.amazonaws.services.ec2.model.DescribeSecurityGroupsRequest;
+import com.amazonaws.services.ec2.model.DescribeSecurityGroupsResult;
+import com.amazonaws.services.ec2.model.DescribeSubnetsRequest;
+import com.amazonaws.services.ec2.model.DescribeSubnetsResult;
+import com.amazonaws.services.ec2.model.Filter;
+import com.amazonaws.services.ec2.model.Image;
+import com.amazonaws.services.ec2.model.Instance;
+import com.amazonaws.services.ec2.model.InstanceStateName;
+import com.amazonaws.services.ec2.model.KeyPair;
+import com.amazonaws.services.ec2.model.Placement;
+import com.amazonaws.services.ec2.model.Reservation;
+import com.amazonaws.services.ec2.model.RunInstancesRequest;
+import com.amazonaws.services.ec2.model.SecurityGroup;
+import com.amazonaws.services.ec2.model.StartInstancesRequest;
+import com.amazonaws.services.ec2.model.StartInstancesResult;
+import com.amazonaws.services.ec2.model.Tag;
+
+/**
+ * Template of {@link EC2Slave} to launch.
+ * 
+ * @author Kohsuke Kawaguchi
+ */
+public class OneSlaveTemplate implements Describable<OneSlaveTemplate> {
+    public final String ami;
+
+    public final String description;
+
+    public final String zone;
+
+    public final String securityGroups;
+
+    public final String remoteFS;
+
+    public final String sshPort;
+
+    public final OneInstanceType type;
+
+    public final String labels;
+
+    public final String initScript;
+
+    public final String userData;
+
+    public final String numExecutors;
+
+    public final String remoteAdmin;
+
+    public final String rootCommandPrefix;
+
+    public final String jvmopts;
+
+    public final String subnetId;
+
+    public final String idleTerminationMinutes;
+
+    public final int instanceCap;
+
+    public final boolean stopOnTerminate;
+
+    private final List<OneEC2Tag> tags;
+
+    public final boolean usePrivateDnsName;
+
+    protected transient OneEC2Cloud parent;
+
+    private transient/* almost final */Set<LabelAtom> labelSet;
+
+    private transient/* almost final */Set<String> securityGroupSet;
+
+    @DataBoundConstructor
+    public OneSlaveTemplate(final String ami, final String zone,
+            final String securityGroups, final String remoteFS,
+            final String sshPort, final OneInstanceType type,
+            final String labelString, final String description,
+            final String initScript, final String userData,
+            final String numExecutors, final String remoteAdmin,
+            final String rootCommandPrefix, final String jvmopts,
+            final boolean stopOnTerminate, final String subnetId,
+            final List<OneEC2Tag> tags,
+            final String idleTerminationMinutes,
+            final boolean usePrivateDnsName, final String instanceCapStr) {
+        this.ami = ami;
+        this.zone = zone;
+        this.securityGroups = securityGroups;
+        this.remoteFS = remoteFS;
+        this.sshPort = sshPort;
+        this.type = type;
+        labels = Util.fixNull(labelString);
+        this.description = description;
+        this.initScript = initScript;
+        this.userData = userData;
+        this.numExecutors = Util.fixNull(numExecutors).trim();
+        this.remoteAdmin = remoteAdmin;
+        this.rootCommandPrefix = rootCommandPrefix;
+        this.jvmopts = jvmopts;
+        this.stopOnTerminate = stopOnTerminate;
+        this.subnetId = subnetId;
+        this.tags = tags;
+        this.idleTerminationMinutes = idleTerminationMinutes;
+        this.usePrivateDnsName = usePrivateDnsName;
+
+        if (null == instanceCapStr || instanceCapStr.equals("")) {
+            instanceCap = Integer.MAX_VALUE;
+        } else {
+            instanceCap = Integer.parseInt(instanceCapStr);
+        }
+
+        readResolve(); // initialize
+    }
+
+    public OneEC2Cloud getParent() {
+        return parent;
+    }
+
+    public String getLabelString() {
+        return labels;
+    }
+
+    public String getDisplayName() {
+        return description + " (" + ami + ")";
+    }
+
+    String getZone() {
+        return zone;
+    }
+
+    public String getSecurityGroupString() {
+        return securityGroups;
+    }
+
+    public Set<String> getSecurityGroupSet() {
+        return securityGroupSet;
+    }
+
+    public Set<String> parseSecurityGroups() {
+        if (securityGroups == null || "".equals(securityGroups.trim())) {
+            return Collections.emptySet();
+        } else {
+            return new HashSet<String>(Arrays.asList(securityGroups
+                .split("\\s*,\\s*")));
+        }
+    }
+
+    public int getNumExecutors() {
+        try {
+            return Integer.parseInt(numExecutors);
+        } catch (NumberFormatException e) {
+            return OneEC2Slave.toNumExecutors(type);
+        }
+    }
+
+    public int getSshPort() {
+        try {
+            return Integer.parseInt(sshPort);
+        } catch (NumberFormatException e) {
+            return 22;
+        }
+    }
+
+    public String getRemoteAdmin() {
+        return remoteAdmin;
+    }
+
+    public String getRootCommandPrefix() {
+        return rootCommandPrefix;
+    }
+
+    public String getSubnetId() {
+        return subnetId;
+    }
+
+    public List<OneEC2Tag> getTags() {
+        if (null == tags) {
+            return null;
+        }
+        return Collections.unmodifiableList(tags);
+    }
+
+    public String getidleTerminationMinutes() {
+        return idleTerminationMinutes;
+    }
+
+    public Set<LabelAtom> getLabelSet() {
+        return labelSet;
+    }
+
+    public int getInstanceCap() {
+        return instanceCap;
+    }
+
+    public String getInstanceCapStr() {
+        if (instanceCap == Integer.MAX_VALUE) {
+            return "";
+        } else {
+            return String.valueOf(instanceCap);
+        }
+    }
+
+    /**
+     * Does this contain the given label?
+     * 
+     * @param l
+     *            can be null to indicate "don't care".
+     */
+    public boolean containsLabel(final Label l) {
+        return l == null || labelSet.contains(l);
+    }
+
+    /**
+     * Provisions a new EC2 slave.
+     * 
+     * @return always non-null. This needs to be then added to
+     *         {@link Hudson#addNode(Node)}.
+     */
+    public OneEC2Slave provision(final TaskListener listener)
+            throws AmazonClientException, IOException {
+        PrintStream logger = listener.getLogger();
+        AmazonEC2 ec2 = getParent().connect();
+
+        try {
+            logger.println("Launching " + ami);
+            KeyPair keyPair =
+                (null != parent.getPrivateKey()) ? parent.getPrivateKey()
+                    .find(ec2) : null;
+
+            RunInstancesRequest riRequest =
+                new RunInstancesRequest(ami, 1, 1);
+
+            List<Filter> diFilters = new ArrayList<Filter>();
+            diFilters.add(new Filter("image-id").withValues(ami));
+
+            if (StringUtils.isNotBlank(getZone())) {
+                Placement placement = new Placement(getZone());
+                riRequest.setPlacement(placement);
+                diFilters.add(new Filter("availability-zone")
+                    .withValues(getZone()));
+            }
+
+            if (StringUtils.isNotBlank(getSubnetId())) {
+                riRequest.setSubnetId(getSubnetId());
+                diFilters.add(new Filter("subnet-id")
+                    .withValues(getSubnetId()));
+
+                /*
+                 * If we have a subnet ID then we can only use VPC security
+                 * groups
+                 */
+                if (!securityGroupSet.isEmpty()) {
+                    List<String> group_ids = new ArrayList<String>();
+
+                    DescribeSecurityGroupsRequest group_req =
+                        new DescribeSecurityGroupsRequest();
+                    group_req.withFilters(new Filter("group-name")
+                        .withValues(securityGroupSet));
+                    DescribeSecurityGroupsResult group_result =
+                        ec2.describeSecurityGroups(group_req);
+
+                    for (SecurityGroup group : group_result
+                        .getSecurityGroups()) {
+                        if (group.getVpcId() != null
+                            && !group.getVpcId().isEmpty()) {
+                            List<Filter> filters = new ArrayList<Filter>();
+                            filters.add(new Filter("vpc-id")
+                                .withValues(group.getVpcId()));
+                            filters.add(new Filter("state")
+                                .withValues("available"));
+                            filters.add(new Filter("subnet-id")
+                                .withValues(getSubnetId()));
+
+                            DescribeSubnetsRequest subnet_req =
+                                new DescribeSubnetsRequest();
+                            subnet_req.withFilters(filters);
+                            DescribeSubnetsResult subnet_result =
+                                ec2.describeSubnets(subnet_req);
+
+                            List subnets = subnet_result.getSubnets();
+                            if (subnets != null && !subnets.isEmpty()) {
+                                group_ids.add(group.getGroupId());
+                            }
+                        }
+                    }
+
+                    if (securityGroupSet.size() != group_ids.size()) {
+                        throw new AmazonClientException(
+                            "Security groups must all be VPC security groups to work in a VPC context");
+                    }
+
+                    if (!group_ids.isEmpty()) {
+                        riRequest.setSecurityGroupIds(group_ids);
+                        diFilters.add(new Filter("instance.group-id")
+                            .withValues(group_ids));
+                    }
+                }
+            } else {
+                /* No subnet: we can use standard security groups by name */
+                riRequest.setSecurityGroups(securityGroupSet);
+                if (securityGroupSet.size() > 0) {
+                    diFilters.add(new Filter("group-name")
+                        .withValues(securityGroupSet));
+                }
+            }
+
+            if (null != userData) {
+                String userDataString =
+                    Base64.encodeBase64String(userData.getBytes());
+                riRequest.setUserData(userDataString);
+            }
+            if (null != keyPair) {
+                riRequest.setKeyName(keyPair.getKeyName());
+                diFilters.add(new Filter("key-name").withValues(keyPair
+                    .getKeyName()));
+            }
+            riRequest.setInstanceType(type.toString());
+            diFilters.add(new Filter("instance-type").withValues(type
+                .toString()));
+
+            HashSet<Tag> inst_tags = null;
+            if (tags != null && !tags.isEmpty()) {
+                inst_tags = new HashSet<Tag>();
+                for (OneEC2Tag t : tags) {
+                    diFilters.add(new Filter("tag:" + t.getName())
+                        .withValues(t.getValue()));
+                }
+            }
+
+            DescribeInstancesRequest diRequest =
+                new DescribeInstancesRequest();
+            diFilters.add(new Filter("instance-state-name").withValues(
+                InstanceStateName.Stopped.toString(),
+                InstanceStateName.Stopping.toString()));
+            diRequest.setFilters(diFilters);
+            logger.println("Looking for existing instances: " + diRequest);
+
+            DescribeInstancesResult diResult =
+                ec2.describeInstances(diRequest);
+
+            filterReservations(diResult.getReservations(), diFilters);
+
+            if (diResult.getReservations().size() == 0) {
+                // Have to create a new instance
+                Instance inst =
+                    ec2.runInstances(riRequest).getReservation()
+                        .getInstances().get(0);
+
+                /* Now that we have our instance, we can set tags on it */
+                if (inst_tags != null) {
+                    CreateTagsRequest tag_request =
+                        new CreateTagsRequest();
+                    tag_request.withResources(inst.getInstanceId())
+                        .setTags(inst_tags);
+                    ec2.createTags(tag_request);
+
+                    // That was a remote request - we should also update our
+                    // local instance data.
+                    inst.setTags(inst_tags);
+                }
+                logger.println("No existing instance found - created: "
+                    + inst);
+                return newSlave(inst);
+            }
+
+            Instance inst =
+                diResult.getReservations().get(0).getInstances().get(0);
+            logger.println("Found existing stopped instance: " + inst);
+            List<String> instances = new ArrayList<String>();
+            instances.add(inst.getInstanceId());
+            StartInstancesRequest siRequest =
+                new StartInstancesRequest(instances);
+            StartInstancesResult siResult = ec2.startInstances(siRequest);
+            logger.println("Starting existing instance: " + inst
+                + " result:" + siResult);
+
+            List<Node> nodes = Hudson.getInstance().getNodes();
+            for (int i = 0, len = nodes.size(); i < len; i++) {
+                if (!(nodes.get(i) instanceof EC2Slave)) {
+                    continue;
+                }
+                OneEC2Slave ec2Node = (OneEC2Slave) nodes.get(i);
+                if (ec2Node.getInstanceId().equals(inst.getInstanceId())) {
+                    logger.println("Found existing corresponding: "
+                        + ec2Node);
+                    return ec2Node;
+                }
+            }
+
+            // Existing slave not found
+            logger.println("Creating new slave for existing instance: "
+                + inst);
+            return newSlave(inst);
+
+        } catch (FormException e) {
+            throw new AssertionError(); // we should have discovered all
+                                        // configuration issues upfront
+        }
+    }
+
+    private OneEC2Slave newSlave(final Instance inst)
+            throws FormException, IOException {
+        return new OneEC2Slave(inst.getInstanceId(), description,
+            remoteFS, getSshPort(), getNumExecutors(), labels, initScript,
+            remoteAdmin, rootCommandPrefix, jvmopts, stopOnTerminate,
+            idleTerminationMinutes, inst.getPublicDnsName(),
+            inst.getPrivateDnsName(), OneEC2Tag.fromAmazonTags(inst
+                .getTags()), usePrivateDnsName);
+    }
+
+    /**
+     * Provisions a new EC2 slave based on the currently running instance on
+     * EC2, instead of starting a new one.
+     */
+    public OneEC2Slave attach(final String instanceId,
+            final TaskListener listener)
+            throws AmazonClientException, IOException {
+        PrintStream logger = listener.getLogger();
+        AmazonEC2 ec2 = getParent().connect();
+
+        try {
+            logger.println("Attaching to " + instanceId);
+            DescribeInstancesRequest request =
+                new DescribeInstancesRequest();
+            request.setInstanceIds(Collections.singletonList(instanceId));
+            Instance inst =
+                ec2.describeInstances(request).getReservations().get(0)
+                    .getInstances().get(0);
+            return newSlave(inst);
+        } catch (FormException e) {
+            throw new AssertionError(); // we should have discovered all
+                                        // configuration issues upfront
+        }
+    }
+
+    /**
+     * Initializes data structure that we don't persist.
+     */
+    protected Object readResolve() {
+        labelSet = Label.parse(labels);
+        securityGroupSet = parseSecurityGroups();
+        return this;
+    }
+
+    public Descriptor<OneSlaveTemplate> getDescriptor() {
+        return Hudson.getInstance().getDescriptor(getClass());
+    }
+
+    @Extension
+    public static final class DescriptorImpl extends
+            Descriptor<OneSlaveTemplate> {
+        @Override
+        public String getDisplayName() {
+            return null;
+        }
+
+        /**
+         * Since this shares much of the configuration with {@link EC2Computer},
+         * check its help page, too.
+         */
+        @Override
+        public String getHelpFile(final String fieldName) {
+            String p = super.getHelpFile(fieldName);
+            if (p == null) {
+                p =
+                    Hudson.getInstance().getDescriptor(OneEC2Slave.class)
+                        .getHelpFile(fieldName);
+            }
+            return p;
+        }
+
+        /***
+         * Check that the AMI requested is available in the cloud and can be
+         * used.
+         */
+        public FormValidation doValidateAmi(
+                @QueryParameter final String url,
+                @QueryParameter final String accessId,
+                @QueryParameter final String secretKey,
+                final @QueryParameter String ami)
+                throws IOException, ServletException {
+            AmazonEC2 ec2 =
+                OneEC2Cloud.connect(accessId, secretKey, new URL(url));
+            if (ec2 != null) {
+                try {
+                    List<String> images = new LinkedList<String>();
+                    images.add(ami);
+                    List<String> owners = new LinkedList<String>();
+                    List<String> users = new LinkedList<String>();
+                    DescribeImagesRequest request =
+                        new DescribeImagesRequest();
+                    request.setImageIds(images);
+                    request.setOwners(owners);
+                    request.setExecutableUsers(users);
+                    List<Image> img =
+                        ec2.describeImages(request).getImages();
+                    if (img == null || img.isEmpty()) {
+                        // de-registered AMI causes an empty list to be
+                        // returned. so be defensive
+                        // against other possibilities
+                        return FormValidation
+                            .error("No such AMI, or not usable with this accessId: "
+                                + ami);
+                    }
+                    return FormValidation.ok(img.get(0).getImageLocation()
+                        + " by " + img.get(0).getImageOwnerAlias());
+                } catch (AmazonClientException e) {
+                    return FormValidation.error(e.getMessage());
+                }
+            } else {
+                return FormValidation.ok(); // can't test
+            }
+        }
+
+        public FormValidation doCheckIdleTerminationMinutes(
+                @QueryParameter final String value) {
+            if (value == null || value.trim() == "") {
+                return FormValidation.ok();
+            }
+            try {
+                int val = Integer.parseInt(value);
+                if (val >= 0) {
+                    return FormValidation.ok();
+                }
+            } catch (NumberFormatException nfe) {
+            }
+            return FormValidation
+                .error("Idle Termination time must be a non-negative integer (or null)");
+        }
+
+        public FormValidation doCheckInstanceCapStr(
+                @QueryParameter final String value) {
+            if (value == null || value.trim() == "") {
+                return FormValidation.ok();
+            }
+            try {
+                int val = Integer.parseInt(value);
+                if (val >= 0) {
+                    return FormValidation.ok();
+                }
+            } catch (NumberFormatException nfe) {
+            }
+            return FormValidation
+                .error("InstanceCap must be a non-negative integer (or null)");
+        }
+
+    }
+
+    private void filterReservations(final List<Reservation> reservations,
+            final List<Filter> filters) {
+
+        for (Filter f : filters) {
+            filterReservationsBy(reservations, f.getName(), f.getValues());
+        }
+
+    }
+
+    private void filterReservationsBy(
+            final List<Reservation> reservations, final String name,
+            final List<String> values) {
+        List<Reservation> emptyReservations = new ArrayList<Reservation>();
+
+        for (Reservation reservation : reservations) {
+            filterReservationBy(reservation, name, values);
+
+            if (reservation.getInstances().size() == 0) {
+                emptyReservations.add(reservation);
+            }
+        }
+
+        reservations.removeAll(emptyReservations);
+    }
+
+    /**
+     * @param reservation
+     * @param name
+     * @param values
+     */
+    private void filterReservationBy(final Reservation reservation,
+            final String name, final List<String> values) {
+        List<Instance> instances = new ArrayList<Instance>();
+        for (Instance inst : reservation.getInstances()) {
+            if (name.equalsIgnoreCase("image-id")) {
+                String data = inst.getImageId();
+                for (String value : values) {
+                    if (value.equalsIgnoreCase(data)) {
+                        instances.add(inst);
+                        break;
+                    }
+                }
+            }
+
+            if (name.equalsIgnoreCase("instance-type")) {
+                String data = inst.getInstanceType();
+                for (String value : values) {
+                    if (value.equalsIgnoreCase(data)) {
+                        instances.add(inst);
+                        break;
+                    }
+                }
+            }
+
+            if (name.equalsIgnoreCase("instance-state-name")) {
+                String data = inst.getState().getName();
+                for (String value : values) {
+                    if (value.equalsIgnoreCase(data)) {
+                        instances.add(inst);
+                        break;
+                    }
+                }
+            }
+
+            if (name.equalsIgnoreCase("key-name")) {
+                String data = inst.getKeyName();
+                for (String value : values) {
+                    if (value.equalsIgnoreCase(data)) {
+                        instances.add(inst);
+                        break;
+                    }
+                }
+            }
+
+            // TODO: Include more filters
+            // TODO: Improve filtering mecanism
+
+        }
+        reservation.getInstances().retainAll(instances);
+    }
+}

+ 232 - 0
source-code/maven-generic-conf/extlibs/ec2-plugin/src/main/java/hudson/plugins/ec2/ssh/EC2UnixLauncher.java

@@ -0,0 +1,232 @@
+package hudson.plugins.ec2.ssh;
+
+import hudson.model.Descriptor;
+import hudson.model.Hudson;
+import hudson.plugins.ec2.EC2ComputerLauncher;
+import hudson.plugins.ec2.EC2Cloud;
+import hudson.plugins.ec2.EC2Computer;
+import hudson.remoting.Channel;
+import hudson.remoting.Channel.Listener;
+import hudson.slaves.ComputerLauncher;
+
+import java.io.IOException;
+import java.io.PrintStream;
+import java.net.URL;
+
+import org.apache.commons.io.IOUtils;
+
+import com.amazonaws.AmazonClientException;
+import com.amazonaws.services.ec2.model.Instance;
+import com.amazonaws.services.ec2.model.KeyPair;
+import com.trilead.ssh2.Connection;
+import com.trilead.ssh2.SCPClient;
+import com.trilead.ssh2.ServerHostKeyVerifier;
+import com.trilead.ssh2.Session;
+
+/**
+ * {@link ComputerLauncher} that connects to a Unix slave on EC2 by using SSH.
+ * 
+ * @author Kohsuke Kawaguchi
+ */
+public class EC2UnixLauncher extends EC2ComputerLauncher {
+
+    private final int FAILED=-1;
+    private final int SAMEUSER=0;
+    private final int RECONNECT=-2;
+    
+    protected String buildUpCommand(EC2Computer computer, String command) {
+    	if (!computer.getRemoteAdmin().equals("root")) {
+    		command = computer.getRootCommandPrefix() + " " + command;
+    	}
+    	return command;
+    }
+
+
+    @Override
+	protected void launch(EC2Computer computer, PrintStream logger, Instance inst) throws IOException, AmazonClientException, InterruptedException {
+
+        final Connection bootstrapConn;
+        final Connection conn;
+        Connection cleanupConn = null; // java's code path analysis for final doesn't work that well.
+        boolean successful = false;
+        
+        try {
+            bootstrapConn = connectToSsh(computer, logger);
+            int bootstrapResult = bootstrap(bootstrapConn, computer, logger);
+            if (bootstrapResult == FAILED)
+                return; // bootstrap closed for us.
+            else if (bootstrapResult == SAMEUSER)
+                cleanupConn = bootstrapConn; // take over the connection
+            else {
+                // connect fresh as ROOT
+                cleanupConn = connectToSsh(computer, logger);
+                KeyPair key = EC2Cloud.get().getKeyPair();
+                if (!cleanupConn.authenticateWithPublicKey(computer.getRemoteAdmin(), key.getKeyMaterial().toCharArray(), "")) {
+                    logger.println("Authentication failed");
+                    return; // failed to connect as root.
+                }
+            }
+            conn = cleanupConn;
+
+            SCPClient scp = conn.createSCPClient();
+            String initScript = computer.getNode().initScript;
+
+            if(initScript!=null && initScript.trim().length()>0 && conn.exec("test -e ~/.hudson-run-init", logger) !=0) {
+                logger.println("Executing init script");
+                scp.put(initScript.getBytes("UTF-8"),"init.sh","/tmp","0700");
+                Session sess = conn.openSession();
+                sess.requestDumbPTY(); // so that the remote side bundles stdout and stderr
+                sess.execCommand(buildUpCommand(computer, "/tmp/init.sh"));
+
+                sess.getStdin().close();    // nothing to write here
+                sess.getStderr().close();   // we are not supposed to get anything from stderr
+                IOUtils.copy(sess.getStdout(),logger);
+
+                int exitStatus = waitCompletion(sess);
+                if (exitStatus!=0) {
+                    logger.println("init script failed: exit code="+exitStatus);
+                    return;
+                }
+
+                // Needs a tty to run sudo.
+                sess = conn.openSession();
+                sess.requestDumbPTY(); // so that the remote side bundles stdout and stderr
+                sess.execCommand(buildUpCommand(computer, "touch ~/.hudson-run-init"));
+            }
+
+            // TODO: parse the version number. maven-enforcer-plugin might help
+            logger.println("Verifying that java exists");
+            if(conn.exec("java -fullversion", logger) !=0) {
+                logger.println("Installing Java");
+
+                String jdk = "java1.6.0_12";
+                String path = "/hudson-ci/jdk/linux-i586/" + jdk + ".tgz";
+
+                URL url = EC2Cloud.get().buildPresignedURL(path);
+                if(conn.exec("wget -nv -O /tmp/" + jdk + ".tgz '" + url + "'", logger) !=0) {
+                    logger.println("Failed to download Java");
+                    return;
+                }
+
+                if(conn.exec(buildUpCommand(computer, "tar xz -C /usr -f /tmp/" + jdk + ".tgz"), logger) !=0) {
+                    logger.println("Failed to install Java");
+                    return;
+                }
+
+                if(conn.exec(buildUpCommand(computer, "ln -s /usr/" + jdk + "/bin/java /bin/java"), logger) !=0) {
+                    logger.println("Failed to symlink Java");
+                    return;
+                }
+            }
+
+            // TODO: on Windows with ec2-sshd, this scp command ends up just putting slave.jar as c:\tmp
+            // bug in ec2-sshd?
+
+            logger.println("Copying slave.jar");
+            scp.put(Hudson.getInstance().getJnlpJars("slave.jar").readFully(),
+                    "slave.jar","/tmp");
+
+            String jvmopts = computer.getNode().jvmopts;
+            String launchString = "java " + (jvmopts != null ? jvmopts : "") + " -jar /tmp/slave.jar";
+            logger.println("Launching slave agent: " + launchString);
+            final Session sess = conn.openSession();
+            sess.execCommand(launchString);
+            computer.setChannel(sess.getStdout(),sess.getStdin(),logger,new Listener() {
+                @Override
+				public void onClosed(Channel channel, IOException cause) {
+                    sess.close();
+                    conn.close();
+                }
+            });
+            successful = true;
+        } finally {
+            if(cleanupConn != null && !successful)
+                cleanupConn.close();
+        }
+    }
+
+    private int bootstrap(Connection bootstrapConn, EC2Computer computer, PrintStream logger) throws IOException, InterruptedException, AmazonClientException {
+        boolean closeBootstrap = true;
+        try {
+            int tries = 20;
+            boolean isAuthenticated = false;
+            KeyPair key = EC2Cloud.get().getKeyPair();
+            while (tries-- > 0) {
+                logger.println("Authenticating as " + computer.getRemoteAdmin());
+                isAuthenticated = bootstrapConn.authenticateWithPublicKey(computer.getRemoteAdmin(), key.getKeyMaterial().toCharArray(), "");
+                if (isAuthenticated) {
+                    break;
+                }
+                logger.println("Authentication failed. Trying again...");
+                Thread.sleep(10000);
+            }
+            if (!isAuthenticated) {
+                logger.println("Authentication failed");
+                return FAILED;
+            }
+            closeBootstrap = false;
+            return SAMEUSER;
+        } finally {
+            if (closeBootstrap)
+                bootstrapConn.close();
+        }
+    }
+
+    private Connection connectToSsh(EC2Computer computer, PrintStream logger) throws AmazonClientException, InterruptedException {
+        while(true) {
+            try {
+                Instance instance = computer.updateInstanceDescription();
+                String vpc_id = instance.getVpcId();
+                String host;
+
+                if (computer.getNode().usePrivateDnsName) {
+                    host = instance.getPrivateDnsName();
+                } else {
+                    /* VPC hosts don't have public DNS names, so we need to use an IP address instead */
+                    if (vpc_id == null || vpc_id.equals("")) {
+                        host = instance.getPublicDnsName();
+                    } else {
+                        host = instance.getPrivateIpAddress();
+                    }
+                }
+
+                if ("0.0.0.0".equals(host)) {
+                    logger.println("Invalid host 0.0.0.0, your host is most likely waiting for an ip address.");
+                    throw new IOException("goto sleep");
+                }
+
+                int port = computer.getSshPort();
+                logger.println("Connecting to " + host + " on port " + port + ". ");
+                Connection conn = new Connection(host, port);
+                // currently OpenSolaris offers no way of verifying the host certificate, so just accept it blindly,
+                // hoping that no man-in-the-middle attack is going on.
+                conn.connect(new ServerHostKeyVerifier() {
+                    public boolean verifyServerHostKey(String hostname, int port, String serverHostKeyAlgorithm, byte[] serverHostKey) throws Exception {
+                        return true;
+                    }
+                });
+                logger.println("Connected via SSH.");
+                return conn; // successfully connected
+            } catch (IOException e) {
+                // keep retrying until SSH comes up
+                logger.println("Waiting for SSH to come up. Sleeping 5.");
+                Thread.sleep(5000);
+            }
+        }
+    }
+
+    private int waitCompletion(Session session) throws InterruptedException {
+        // I noticed that the exit status delivery often gets delayed. Wait up to 1 sec.
+        for( int i=0; i<10; i++ ) {
+            Integer r = session.getExitStatus();
+            if(r!=null) return r;
+            Thread.sleep(100);
+        }
+        return -1;
+    }
+
+    @Override
+	public Descriptor<ComputerLauncher> getDescriptor() {
+        throw new UnsupportedOperationException();
+    }
+}

+ 50 - 0
source-code/maven-generic-conf/extlibs/ec2-plugin/src/main/java/hudson/plugins/ec2/ssh/HostKeyVerifierImpl.java

@@ -0,0 +1,50 @@
+package hudson.plugins.ec2.ssh;
+
+import java.util.logging.Logger;
+
+import com.trilead.ssh2.ServerHostKeyVerifier;
+import com.trilead.ssh2.crypto.digest.MD5;
+
+/**
+ * {@link ServerHostKeyVerifier} that makes sure that the host key fingerprint
+ * showed up in {@link ConsoleOutput#getOutput()}.
+ * 
+ * @author Kohsuke Kawaguchi
+ */
+public class HostKeyVerifierImpl implements ServerHostKeyVerifier {
+    private final String console;
+
+    public HostKeyVerifierImpl(String console) {
+        this.console = console;
+    }
+
+    private String getFingerprint(byte[] serverHostKey) {
+        MD5 md5 = new MD5();
+        md5.update(serverHostKey);
+
+        byte[] fingerprint = new byte[16];
+
+        md5.digest(fingerprint);
+        StringBuilder buf = new StringBuilder();
+        for( byte b : fingerprint ) {
+            if(buf.length()>0)  buf.append(':');
+            buf.append(String.format("%02x",b));
+        }
+        return buf.toString();
+    }
+
+    public boolean verifyServerHostKey(String hostname, int port, String serverHostKeyAlgorithm, byte[] serverHostKey) throws Exception {
+        String fingerprint = getFingerprint(serverHostKey);
+
+        LOGGER.fine("Host key fingerprint of "+hostname+" is "+fingerprint);
+
+        boolean matches = console.contains(fingerprint);
+
+        if(!matches)
+            LOGGER.severe("No matching fingerprint found in the console output: "+console);
+
+        return matches;
+    }
+
+    private static final Logger LOGGER = Logger.getLogger(HostKeyVerifierImpl.class.getName());
+}

+ 23 - 0
source-code/maven-generic-conf/extlibs/ec2-plugin/src/main/resources/hudson/plugins/ec2/AmazonEC2Cloud/config-entries.jelly

@@ -0,0 +1,23 @@
+<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">
+  <f:entry title="${%Access Key ID}" field="accessId">
+    <f:textbox />
+  </f:entry>
+  <f:entry title="${%Secret Access Key}" field="secretKey">
+    <f:password />
+  </f:entry>
+  <f:description>The regions will be populated once the keys above are entered.
+  </f:description>
+  <f:entry title="${%Region}" field="region">
+    <f:select/>
+  </f:entry>
+  <f:entry title="${%EC2 Key Pair's Private Key}" field="privateKey">
+    <f:textarea />
+  </f:entry>
+  <f:advanced>
+    <f:entry title="${%Instance Cap}" field="instanceCapStr">
+      <f:textbox />
+    </f:entry>
+  </f:advanced>
+  <f:validateButton title="${%Generate Key}" progress="${%Generate...}" method="generateKey" with="secretKey,accessId" />
+  <f:validateButton title="${%Test Connection}" progress="${%Testing...}" method="testConnection" with="region,secretKey,accessId,privateKey" />
+</j:jelly>

+ 6 - 0
source-code/maven-generic-conf/extlibs/ec2-plugin/src/main/resources/hudson/plugins/ec2/AmazonEC2Cloud/help-region.html

@@ -0,0 +1,6 @@
+<div>
+    Specifies the geographic region in which your slaves will run. Pick the region closest to you. 
+    Regions can be thought of as
+    independent instances of EC2/S3. See <a href="http://docs.amazonwebservices.com/general/latest/gr/rande.html">AWS documentation</a>
+    for more about what regions are.
+</div>

+ 26 - 0
source-code/maven-generic-conf/extlibs/ec2-plugin/src/main/resources/hudson/plugins/ec2/EC2Cloud/computerSet.jelly

@@ -0,0 +1,26 @@
+<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:if test="${it.hasPermission(it.PROVISION)}">
+    <tr>
+      <td />
+      <td colspan="${monitors.size()+1}">
+        <f:form action="${rootURL}/cloud/${it.name}/provision" method="post" name="provision">
+          <input type="submit" class="ec2-provision-button" value="${%Provision via EC2}" />
+          <select name="ami">
+            <j:forEach var="t" items="${it.templates}">
+              <option value="${t.ami}">${t.displayName}</option>
+            </j:forEach>
+          </select>
+          <st:once>
+            <script>
+              Behaviour.register({
+                ".ec2-provision-button" : function (e) {
+                  new YAHOO.widget.Button(e, { type: "menu", menu: e.nextSibling });
+                }
+              });
+            </script>
+          </st:once>
+        </f:form>
+      </td>
+    </tr>
+  </j:if>
+</j:jelly>

+ 1 - 0
source-code/maven-generic-conf/extlibs/ec2-plugin/src/main/resources/hudson/plugins/ec2/EC2Cloud/computerSet.properties

@@ -0,0 +1 @@
+submit=Provision new instance via EC2 

+ 9 - 0
source-code/maven-generic-conf/extlibs/ec2-plugin/src/main/resources/hudson/plugins/ec2/EC2Cloud/config.jelly

@@ -0,0 +1,9 @@
+<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">
+  <st:include class="${descriptor.clazz}" page="config-entries.jelly" />
+
+  <f:entry title="${%AMIs}" description="${%List of AMIs to be launched as slaves}">
+    <f:repeatable field="templates">
+      <st:include page="config.jelly" class="${descriptor.clazz}" />
+    </f:repeatable>
+  </f:entry>
+</j:jelly>

+ 6 - 0
source-code/maven-generic-conf/extlibs/ec2-plugin/src/main/resources/hudson/plugins/ec2/EC2Cloud/help-accessId.html

@@ -0,0 +1,6 @@
+<div>
+    Jenkins uses this access key ID and secret access key to interface with Amazon EC2.
+    If you have already signed up on EC2, you can obtain this from
+    <a href="http://aws-portal.amazon.com/gp/aws/developer/account/index.html?action=access-key">here</a>.
+    Otherwise, you need to sign up to Amazon Web Services to get one.
+</div>

+ 10 - 0
source-code/maven-generic-conf/extlibs/ec2-plugin/src/main/resources/hudson/plugins/ec2/EC2Cloud/help-ec2EndpointUrl.html

@@ -0,0 +1,10 @@
+<div>
+    This field is optional. It controls what EC2 API endpoint to connect to. 
+    By default https://us-east-1.ec2.amazonaws.com/ is used. To use other 
+    Amazon AWS zones, change this appropriately.
+    
+    When using Ubuntu Enterprise Cloud (Eucalyptus) a url like
+    http://cluster-controler:8773/services/Eucalyptus is needed.
+
+    For other EC2 API-compatible clouds please see your product documentation.
+</div>

+ 15 - 0
source-code/maven-generic-conf/extlibs/ec2-plugin/src/main/resources/hudson/plugins/ec2/EC2Cloud/help-instanceCapStr.html

@@ -0,0 +1,15 @@
+<div>
+    You can place the upward limit to the number of EC2 instances that Jenkins may launch.
+    This is useful for avoiding surprises in the billing statement.
+
+    <P>
+    For example, if this field is 3, Jenkins will only launch a new instance
+    as long as total number of instances you run on EC2 (regardless of whether that is
+    for Jenkins or for other purposes) doesn't exceed this number. In this way,
+    even in the worst case of Jenkins starting instances and forgetting about them,
+    you have an upper bound in the number of instances that are concurrently executed.
+
+    <p>
+    Leave this field empty to remove a cap
+    (note that EC2 appears to have its own instance cap at 20.)
+</div>

+ 1 - 0
source-code/maven-generic-conf/extlibs/ec2-plugin/src/main/resources/hudson/plugins/ec2/EC2Cloud/help-jvmopts.html

@@ -0,0 +1 @@
+<div>Provide space separated JVM startup options (e.g. -Xmx1g for 1 GB Max Heap Size) </div>

+ 12 - 0
source-code/maven-generic-conf/extlibs/ec2-plugin/src/main/resources/hudson/plugins/ec2/EC2Cloud/help-privateKey.html

@@ -0,0 +1,12 @@
+<div>
+    Paste the RSA private key of the EC2 key pair. If you are new to EC2
+    and have no idea what this is, you can just generate one (if you do so,
+    you should save the key into a text file, as you will need it if you
+    want to login to the EC2 instances via ssh.)
+
+    <p>
+    An RSA private key for EC2 is a text
+    that starts with "-----BEGIN RSA PRIVATE KEY-----". You normally
+    generate it via using either the AWS Management Console, the "ec2-add-keypair" tool or a GUI tool
+    like ElastiFox.
+</div>

+ 7 - 0
source-code/maven-generic-conf/extlibs/ec2-plugin/src/main/resources/hudson/plugins/ec2/EC2Cloud/help-remoteAdmin.html

@@ -0,0 +1,7 @@
+<div>
+    This field is optional. Specify the user name used to access the instance using
+    ssh. For many Amazon AMI's this is 'ec2-user'; Ubuntu images use 'ubuntu'. If you
+    don't specify this, 'root' is assumed.
+    If the user does not have direct administrative
+    access, i.e. is not 'root', you should also set the root command prefix, i.e. to 'sudo'. 
+</div>

+ 5 - 0
source-code/maven-generic-conf/extlibs/ec2-plugin/src/main/resources/hudson/plugins/ec2/EC2Cloud/help-rootCommandPrefix.html

@@ -0,0 +1,5 @@
+<div>
+    This field is optional. It supplies a command prefix to use when running an
+    administrative command, e.g. 'sudo'. This is needed when not using 'root'
+    as a user (which is the typical case).
+</div>

+ 10 - 0
source-code/maven-generic-conf/extlibs/ec2-plugin/src/main/resources/hudson/plugins/ec2/EC2Cloud/help-s3EndpointUrl.html

@@ -0,0 +1,10 @@
+<div>
+    This field is optional. It controls what S3 API endpoint to connect to. 
+    By default https://s3.amazonaws.com/ is used (note that this works with all
+    amazonaws zones - s3 transparently redirects between zones).
+
+    When using Ubuntu Enterprise Cloud (Eucalyptus) the default URL should be
+    something like http://cluster-controller-ip:8773/services/Walrus.
+
+    For other EC2/S3 compatible servers please see your product documentation.
+</div>

+ 7 - 0
source-code/maven-generic-conf/extlibs/ec2-plugin/src/main/resources/hudson/plugins/ec2/EC2Cloud/help-userData.html

@@ -0,0 +1,7 @@
+<div>
+  Arbitrary data to pass to the instance.
+
+  <p>
+  The instance will be able to access the data using address
+  <a href="http://169.254.169.254/2009-04-04/user-data">http://169.254.169.254/2009-04-04/user-data</a>.
+</div>

+ 104 - 0
source-code/maven-generic-conf/extlibs/ec2-plugin/src/main/resources/hudson/plugins/ec2/EC2Computer/configure.jelly

@@ -0,0 +1,104 @@
+<!--
+The MIT License
+
+Copyright (c) 2004-2009, Sun Microsystems, Inc.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+-->
+
+<!--
+  This is bit unusual, but so that we can override "name" with "Instance ID", override the config page entirely,
+  instead of just supplying EC2Slave/configure-entries.jelly
+-->
+<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">
+  <l:layout norefresh="true" permission="${app.ADMINISTER}" title="${it.name} Configuration">
+    <st:include page="sidepanel.jelly"/>
+    <l:main-panel>
+      <f:form method="post" action="configSubmit" name="config">
+        <j:set var="instance" value="${it.node}" />
+        <j:set var="descriptor" value="${instance.descriptor}" />
+
+        <f:invisibleEntry>
+           <input type="hidden" name="name" value="${it.name}" />
+        </f:invisibleEntry>
+
+        <f:entry title="${%Instance ID}" field="instanceId">
+          <f:readOnlyTextbox />
+        </f:entry>
+
+        <f:entry title="${%Public DNS}" field="publicDNS">
+          <f:readOnlyTextbox />
+        </f:entry>
+
+        <f:entry title="${%Private DNS}" field="privateDNS">
+          <f:readOnlyTextbox />
+        </f:entry>
+
+        <f:entry title="${%Description}" help="/help/system-config/master-slave/description.html">
+          <f:textbox field="nodeDescription" />
+        </f:entry>
+
+        <f:entry title="${%# of executors}" field="numExecutors">
+          <f:textbox />
+        </f:entry>
+        
+        <f:entry title="${%Labels}" field="labelString">
+          <f:textbox />
+        </f:entry>
+
+        <f:entry title="${%Init Script}" field="initScript">
+          <f:textarea />
+        </f:entry>
+
+        <f:entry title="${%Remote user}" field="remoteAdmin">
+          <f:textbox />
+        </f:entry>
+        
+        <f:entry title="${%Root command prefix}" field="rootCommandPrefix">
+          <f:textbox />
+        </f:entry>
+
+	    <f:entry title="${%Stop/Disconnect on Idle Timeout}" field="stopOnTerminate">
+    	  <f:checkbox />
+    	</f:entry>
+
+        <f:entry title="${%Idle termination time}" field="idleTerminationMinutes">
+            <f:textbox />
+        </f:entry>
+
+
+        <f:entry title="${%Tags}" description="${%EC2 Tag/Value Pairs}">
+            <f:repeatable field="tags">
+                <st:include page="../EC2Tag/config.jelly" />
+            </f:repeatable>
+        </f:entry>
+
+	    <f:entry title="${%Use private DNS}" field="usePrivateDnsName">
+    	  <f:checkbox />
+    	</f:entry>
+
+        <f:descriptorList title="${%Node Properties}" descriptors="${h.getNodePropertyDescriptors(descriptor.clazz)}" field="nodeProperties" />
+
+        <f:block>
+          <f:submit value="${%Save}"/>
+        </f:block>
+      </f:form>
+    </l:main-panel>
+  </l:layout>
+</j:jelly>

+ 6 - 0
source-code/maven-generic-conf/extlibs/ec2-plugin/src/main/resources/hudson/plugins/ec2/EC2Computer/help-idleTerminationMinutes.html

@@ -0,0 +1,6 @@
+<div>
+    Determines how long slaves can remain idle before being stopped or terminated (see the Stop/Disconnect on Idle Timeout setting).
+    <p>
+    Times are expressed in minutes, and a value of 0 (or an empty string) indicates that idle slaves should
+    never be stopped/terminated.
+</div>

+ 6 - 0
source-code/maven-generic-conf/extlibs/ec2-plugin/src/main/resources/hudson/plugins/ec2/EC2Computer/help-usePrivateDnsName.html

@@ -0,0 +1,6 @@
+<div>
+    Connect using the private DNS name instead of the public name.
+    <p>
+    Note that for VPC instances, there is no public DNS name, and so this becomes a choice between using the
+    private DNS name or the private IP address
+</div>

+ 4 - 0
source-code/maven-generic-conf/extlibs/ec2-plugin/src/main/resources/hudson/plugins/ec2/EC2Slave/help-initScript.html

@@ -0,0 +1,4 @@
+<div>
+    This shell script gets executed right after Jenkins connects to a new EC2 instance.
+    This is a good place to execute a system set up, such as installing some packages.
+</div>

+ 3 - 0
source-code/maven-generic-conf/extlibs/ec2-plugin/src/main/resources/hudson/plugins/ec2/EC2Slave/help-numExecutors.html

@@ -0,0 +1,3 @@
+<div>
+  Number of executors to launch on this type of machines. If omitted, auto-configured from the instance type.
+</div>

+ 7 - 0
source-code/maven-generic-conf/extlibs/ec2-plugin/src/main/resources/hudson/plugins/ec2/EC2Slave/help-remoteAdmin.html

@@ -0,0 +1,7 @@
+<div>
+    This field is optional. Specify the user name used to access the instance using
+    ssh. For many Amazon AMI's this is 'ec2-user'; Ubuntu images use 'ubuntu'. If you
+    don't specify this, 'root' is assumed.
+    If the user does not have direct administrative
+    access, i.e. is not 'root', you should also set the root command prefix, i.e. to 'sudo'. 
+</div>

+ 5 - 0
source-code/maven-generic-conf/extlibs/ec2-plugin/src/main/resources/hudson/plugins/ec2/EC2Slave/help-rootCommandPrefix.html

@@ -0,0 +1,5 @@
+<div>
+    This field is optional. It supplies a command prefix to use when running an
+    administrative command, e.g. 'sudo'. This is needed when not using 'root'
+    as a user (which is the typical case).
+</div>

+ 5 - 0
source-code/maven-generic-conf/extlibs/ec2-plugin/src/main/resources/hudson/plugins/ec2/EC2Slave/help-securityGroups.html

@@ -0,0 +1,5 @@
+<div>
+    Specify the security group names (do not specify the security group IDs) that will be applied to this instance.
+    If there are multiple security groups, separate them by a space. If the instance uses a VPC (you have specified
+    a subnet ID for the VPC), then all security groups need to be part of the VPC.
+</div>

+ 6 - 0
source-code/maven-generic-conf/extlibs/ec2-plugin/src/main/resources/hudson/plugins/ec2/EC2Slave/help-stopOnTerminate.html

@@ -0,0 +1,6 @@
+<div>
+    Check this if you want the instance to be stopped instead of terminated when it times out
+    due to being idle. This allows existing instances to be used when provisioning a slave.
+    You would use this option to preserve the state of the instance, including that of the 
+    EBS volume connections.
+</div>

+ 4 - 0
source-code/maven-generic-conf/extlibs/ec2-plugin/src/main/resources/hudson/plugins/ec2/EC2Slave/help-subnetId.html

@@ -0,0 +1,4 @@
+<div>
+    Specify the subnet ID to be used with the VPC. Do not specify the VPC ID, the subnet ID will be used to locate the VPC.
+    When using the VPC, you must specify a security group that is contained in the VPC.
+</div>

+ 3 - 0
source-code/maven-generic-conf/extlibs/ec2-plugin/src/main/resources/hudson/plugins/ec2/EC2Slave/help-type.html

@@ -0,0 +1,3 @@
+<div>
+    Choose the instance type to launch.
+</div>

+ 9 - 0
source-code/maven-generic-conf/extlibs/ec2-plugin/src/main/resources/hudson/plugins/ec2/EC2Slave/help-zone.html

@@ -0,0 +1,9 @@
+<div>
+    Optionally specify the availability zone within the region. This can be important if you are using an Amazon EC2 
+    instance in conjunction with an EBS volume. In this case both the EBS volume and EC2 instance must be in the same
+    availability zone. 
+    See the <a href="http://docs.amazonwebservices.com/AWSEC2/latest/UserGuide/using-regions-availability-zones.html">AWS documentation</a>
+    for more about what availability zones are. 
+    (For the moment you must look up the available zones for the region yourself and type in the zone name, in
+    the future, we plan to have this be a list you can select from).
+</div>

+ 41 - 0
source-code/maven-generic-conf/extlibs/ec2-plugin/src/main/resources/hudson/plugins/ec2/EC2Tag/config.jelly

@@ -0,0 +1,41 @@
+<!--
+The MIT License
+
+Copyright (c) 2011, CloudBees, Inc.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+-->
+
+<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">
+   <table width="100%">
+      <f:entry title="${%Name}" field="name">
+         <f:textbox />
+      </f:entry>
+
+      <f:entry title="${%Value}" field="value">
+         <f:textbox />
+      </f:entry>
+
+      <f:entry title="">
+         <div align="right">
+            <f:repeatableDeleteButton />
+         </div>
+      </f:entry>
+   </table>
+</j:jelly>

+ 21 - 0
source-code/maven-generic-conf/extlibs/ec2-plugin/src/main/resources/hudson/plugins/ec2/Eucalyptus/config-entries.jelly

@@ -0,0 +1,21 @@
+<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">
+  <f:entry title="${%Eucalyptus URL}" field="url">
+    <f:textbox />
+  </f:entry>
+  <f:entry title="${%Access ID}" field="accessId">
+    <f:textbox />
+  </f:entry>
+  <f:entry title="${%Secret Key}" field="secretKey">
+    <f:password />
+  </f:entry>
+  <f:entry title="${%EC2 Key Pair's Private Key}" field="privateKey">
+    <f:textarea />
+  </f:entry>
+  <f:advanced>
+    <f:entry title="${%Instance Cap}" field="instanceCapStr">
+      <f:textbox />
+    </f:entry>
+  </f:advanced>
+  <f:validateButton title="${%Generate Key}" progress="${%Generate...}" method="generateKey" with="url,secretKey,accessId" />
+  <f:validateButton title="${%Test Connection}" progress="${%Testing...}" method="testConnection" with="url,secretKey,accessId,privateKey" />
+</j:jelly>

+ 4 - 0
source-code/maven-generic-conf/extlibs/ec2-plugin/src/main/resources/hudson/plugins/ec2/Eucalyptus/help-url.html

@@ -0,0 +1,4 @@
+<div>
+    Specify the HTTPS URL of the Eucalyptus web UI.
+    Normally, this is something like <tt>https://yourmachine:8443/</tt>
+</div>

+ 3 - 0
source-code/maven-generic-conf/extlibs/ec2-plugin/src/main/resources/hudson/plugins/ec2/Messages.properties

@@ -0,0 +1,3 @@
+EC2Cloud.InvalidAccessId=Invalid AWS access key ID
+EC2Cloud.InvalidSecretKey=Invalid AWS secret access key
+EC2Cloud.Success=Success

+ 17 - 0
source-code/maven-generic-conf/extlibs/ec2-plugin/src/main/resources/hudson/plugins/ec2/OpenNebula/config-entries.jelly

@@ -0,0 +1,17 @@
+<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">
+  <f:entry title="${%OpenNebula URL}" field="url">
+    <f:textbox />
+  </f:entry>
+  <f:entry title="${%Access ID}" field="accessId">
+    <f:textbox />
+  </f:entry>
+  <f:entry title="${%Secret Key}" field="secretKey">
+    <f:password />
+  </f:entry>
+  <f:advanced>
+    <f:entry title="${%Instance Cap}" field="instanceCapStr">
+      <f:textbox />
+    </f:entry>
+  </f:advanced>
+  <f:validateButton title="${%Test Connection}" progress="${%Testing...}" method="testConnection" with="url,secretKey,accessId" />
+</j:jelly>

+ 4 - 0
source-code/maven-generic-conf/extlibs/ec2-plugin/src/main/resources/hudson/plugins/ec2/OpenNebula/help-url.html

@@ -0,0 +1,4 @@
+<div>
+    Specify the HTTP or HTTPS URL of the OpenNebula EC2 endpoint.
+    Normally, this is something like <tt>http[s]://yourmachine:4568/</tt>
+</div>

+ 124 - 0
source-code/maven-generic-conf/extlibs/ec2-plugin/src/main/resources/hudson/plugins/ec2/SlaveTemplate/config.jelly

@@ -0,0 +1,124 @@
+<!--
+The MIT License
+
+Copyright (c) 2011, CloudBees, Inc.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+-->
+<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" >
+  <table width="100%">
+
+  <f:entry title="${%AMI ID}" field="ami">
+    <f:textbox />
+  </f:entry>
+
+  <f:validateButton title="${%Check AMI}" progress="${%Checking...}" method="validateAmi" with="secretKey,accessId,region,ami" />
+
+  <f:entry title="${%Instance Type}" field="type">
+    <f:enum>${it.name()}</f:enum>
+  </f:entry>
+
+  <f:entry title="${%Description}" help="/help/system-config/master-slave/description.html" field="description">
+    <f:textbox />
+  </f:entry>
+
+  <f:entry title="${%Availability Zone}" field="zone">
+    <!-- this is preferred but there is a problem with making it work FRU 22 Feb 12 
+         See: https://groups.google.com/group/jenkinsci-dev/t/af37fa7fe2769b0c -->
+    <!-- <f:select/>-->
+    <f:textbox/>
+  </f:entry>
+
+  <f:entry title="${%Security group names}" field="securityGroups">
+    <f:textbox/>
+  </f:entry>
+
+  <f:entry title="${%Remote FS root}" field="remoteFS">
+    <f:textbox />
+  </f:entry>
+
+  <f:entry title="${%Remote user}" field="remoteAdmin">
+    <f:textbox />
+  </f:entry>
+
+  <f:entry title="${%Root command prefix}" field="rootCommandPrefix">
+    <f:textbox/>
+  </f:entry>
+
+  <f:entry title="${%Labels}" field="labelString">
+    <f:textbox />
+  </f:entry>
+
+  <f:entry title="${%Idle termination time}" field="idleTerminationMinutes">
+    <f:textbox default="30" />
+  </f:entry>  
+
+  <f:entry title="${%Init script}" field="initScript">
+    <f:textarea />
+  </f:entry>
+
+  <f:advanced>
+
+    <f:entry title="${%User Data}" field="userData">
+      <f:textarea />
+    </f:entry>
+
+    <f:entry title="${%Number of Executors}" field="numExecutors">
+      <f:textbox />
+    </f:entry>
+
+    <f:entry title="${%JVM Options}" field="jvmopts">
+      <f:textbox/>
+    </f:entry>
+
+    <f:entry title="${%Remote ssh port}" field="sshPort">
+      <f:textbox />
+    </f:entry>
+
+    <f:entry title="${%Stop/Disconnect on Idle Timeout}" field="stopOnTerminate">
+      <f:checkbox />
+    </f:entry>
+
+    <f:entry title="${%Subnet ID for VPC}" field="subnetId">
+       <f:textbox />
+    </f:entry>
+
+    <f:entry title="${%Tags}" description="${%EC2 Tag/Value Pairs}">
+       <f:repeatable field="tags">
+          <st:include page="config.jelly" class="hudson.plugins.ec2.EC2Tag" />
+       </f:repeatable>
+    </f:entry>
+
+    <f:entry title="${%Use private DNS}" field="usePrivateDnsName">
+      <f:checkbox />
+    </f:entry>
+
+    <f:entry title="${%Instance Cap}" field="instanceCapStr">
+      <f:textbox />
+    </f:entry>
+  </f:advanced>
+
+  <f:entry title="">
+    <div align="right">
+      <f:repeatableDeleteButton />
+    </div>
+  </f:entry>
+
+</table>
+</j:jelly>

+ 6 - 0
source-code/maven-generic-conf/extlibs/ec2-plugin/src/main/resources/hudson/plugins/ec2/SlaveTemplate/help-idleTerminationMinutes.html

@@ -0,0 +1,6 @@
+<div>
+    Determines how long slaves can remain idle before being stopped or terminated (see the Stop/Disconnect on Idle Timeout setting).
+    <p>
+    Times are expressed in minutes, and a value of 0 (or an empty string) indicates that idle slaves should
+    never be stopped/terminated.
+</div>

+ 6 - 0
source-code/maven-generic-conf/extlibs/ec2-plugin/src/main/resources/hudson/plugins/ec2/SlaveTemplate/help-usePrivateDnsName.html

@@ -0,0 +1,6 @@
+<div>
+    Connect using the private DNS name instead of the public name.
+    <p>
+    Note that for VPC instances, there is no public DNS name, and so this becomes a choice between using the
+    private DNS name or the private IP address
+</div>

+ 40 - 0
source-code/maven-generic-conf/extlibs/ec2-plugin/src/main/resources/hudson/plugins/ec2/ebs/ZPoolExpandNotice/index.jelly

@@ -0,0 +1,40 @@
+<!--
+The MIT License
+
+Copyright (c) 2004-2009, Sun Microsystems, Inc., Kohsuke Kawaguchi
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+-->
+
+<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:i="jelly:fmt">
+	<l:layout title="HUDSON_HOME is almost full">
+		<l:main-panel>
+      <h1>JENKINS_HOME is almost full</h1>
+
+      <p>
+        Your <tt>JENKINS_HOME</tt> (${app.rootDir}) is almost full.
+        When this directory completely fills up, it will cause problems
+        because Jenkins can't store any more data.
+      </p><p>
+        
+      </p>
+
+    </l:main-panel>
+	</l:layout>
+</j:jelly>

+ 33 - 0
source-code/maven-generic-conf/extlibs/ec2-plugin/src/main/resources/hudson/plugins/ec2/ebs/ZPoolExpandNotice/message.jelly

@@ -0,0 +1,33 @@
+<!--
+The MIT License
+
+Copyright (c) 2004-2009, Sun Microsystems, Inc., Kohsuke Kawaguchi
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+-->
+
+<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">
+  <div class="warning">
+    <form method="post" action="${rootURL}/administrativeMonitor/zpool.ebs/">
+      Your data directory in Jenkins (${app.rootDir} - AKA <tt>HUDSON_HOME</tt>) is getting close to full.
+      You should act on it before it's completely full.
+      <f:submit value="${%Tell me more}"/>
+    </form>
+  </div>
+</j:jelly>

+ 3 - 0
source-code/maven-generic-conf/extlibs/ec2-plugin/src/main/resources/hudson/plugins/ec2/one/Messages.properties

@@ -0,0 +1,3 @@
+EC2Cloud.InvalidAccessId=Invalid AWS access key ID
+EC2Cloud.InvalidSecretKey=Invalid AWS secret access key
+EC2Cloud.Success=Success

+ 26 - 0
source-code/maven-generic-conf/extlibs/ec2-plugin/src/main/resources/hudson/plugins/ec2/one/OneEC2Cloud/computerSet.jelly

@@ -0,0 +1,26 @@
+<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:if test="${it.hasPermission(it.PROVISION)}">
+    <tr>
+      <td />
+      <td colspan="${monitors.size()+1}">
+        <f:form action="${rootURL}/cloud/${it.name}/provision" method="post" name="provision">
+          <input type="submit" class="ec2-provision-button" value="${%Provision via EC2}" />
+          <select name="ami">
+            <j:forEach var="t" items="${it.templates}">
+              <option value="${t.ami}">${t.displayName}</option>
+            </j:forEach>
+          </select>
+          <st:once>
+            <script>
+              Behaviour.register({
+                ".ec2-provision-button" : function (e) {
+                  new YAHOO.widget.Button(e, { type: "menu", menu: e.nextSibling });
+                }
+              });
+            </script>
+          </st:once>
+        </f:form>
+      </td>
+    </tr>
+  </j:if>
+</j:jelly>

+ 1 - 0
source-code/maven-generic-conf/extlibs/ec2-plugin/src/main/resources/hudson/plugins/ec2/one/OneEC2Cloud/computerSet.properties

@@ -0,0 +1 @@
+submit=Provision new instance via EC2 

+ 9 - 0
source-code/maven-generic-conf/extlibs/ec2-plugin/src/main/resources/hudson/plugins/ec2/one/OneEC2Cloud/config.jelly

@@ -0,0 +1,9 @@
+<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">
+  <st:include class="${descriptor.clazz}" page="config-entries.jelly" />
+
+  <f:entry title="${%AMIs}" description="${%List of AMIs to be launched as slaves}">
+    <f:repeatable field="templates">
+      <st:include page="config.jelly" class="${descriptor.clazz}" />
+    </f:repeatable>
+  </f:entry>
+</j:jelly>

+ 6 - 0
source-code/maven-generic-conf/extlibs/ec2-plugin/src/main/resources/hudson/plugins/ec2/one/OneEC2Cloud/help-accessId.html

@@ -0,0 +1,6 @@
+<div>
+    Jenkins uses this access key ID and secret access key to interface with Amazon EC2.
+    If you have already signed up on EC2, you can obtain this from
+    <a href="http://aws-portal.amazon.com/gp/aws/developer/account/index.html?action=access-key">here</a>.
+    Otherwise, you need to sign up to Amazon Web Services to get one.
+</div>

+ 10 - 0
source-code/maven-generic-conf/extlibs/ec2-plugin/src/main/resources/hudson/plugins/ec2/one/OneEC2Cloud/help-ec2EndpointUrl.html

@@ -0,0 +1,10 @@
+<div>
+    This field is optional. It controls what EC2 API endpoint to connect to. 
+    By default https://us-east-1.ec2.amazonaws.com/ is used. To use other 
+    Amazon AWS zones, change this appropriately.
+    
+    When using Ubuntu Enterprise Cloud (Eucalyptus) a url like
+    http://cluster-controler:8773/services/Eucalyptus is needed.
+
+    For other EC2 API-compatible clouds please see your product documentation.
+</div>

+ 15 - 0
source-code/maven-generic-conf/extlibs/ec2-plugin/src/main/resources/hudson/plugins/ec2/one/OneEC2Cloud/help-instanceCapStr.html

@@ -0,0 +1,15 @@
+<div>
+    You can place the upward limit to the number of EC2 instances that Jenkins may launch.
+    This is useful for avoiding surprises in the billing statement.
+
+    <P>
+    For example, if this field is 3, Jenkins will only launch a new instance
+    as long as total number of instances you run on EC2 (regardless of whether that is
+    for Jenkins or for other purposes) doesn't exceed this number. In this way,
+    even in the worst case of Jenkins starting instances and forgetting about them,
+    you have an upper bound in the number of instances that are concurrently executed.
+
+    <p>
+    Leave this field empty to remove a cap
+    (note that EC2 appears to have its own instance cap at 20.)
+</div>

+ 1 - 0
source-code/maven-generic-conf/extlibs/ec2-plugin/src/main/resources/hudson/plugins/ec2/one/OneEC2Cloud/help-jvmopts.html

@@ -0,0 +1 @@
+<div>Provide space separated JVM startup options (e.g. -Xmx1g for 1 GB Max Heap Size) </div>

Some files were not shown because too many files changed in this diff