Skip to content
This repository was archived by the owner on May 12, 2021. It is now read-only.

Branch 0.5 - support for CDH3u0 HBase #1

Open
wants to merge 200 commits into
base: branch-0.5
Choose a base branch
from
Open
Changes from all commits
Commits
Show all changes
200 commits
Select commit Hold shift + click to select a range
e520e30
Preparing for release X.Y.Z
tomwhite Sep 2, 2010
944859f
Prepare for 0.2.0 development.
tomwhite Sep 2, 2010
50a997d
WHIRR-84. Log4j is missing from the CLI JAR.
tomwhite Sep 3, 2010
be4acc7
WHIRR-85. Publish Maven artifacts to http://repository.apache.org.
tomwhite Sep 9, 2010
96a86cc
WHIRR-86. Update quick start documentation to work with release 0.1.0.
tomwhite Sep 10, 2010
da2beb3
WHIRR-89. Support maven 3 builds. Contributed by Adrian Cole.
tomwhite Sep 16, 2010
5603b15
WHIRR-93. Fail on checkstyle violation.
tomwhite Sep 20, 2010
842719e
WHIRR-90. Scripts should be versioned.
tomwhite Sep 21, 2010
8f3a416
WHIRR-66. Upgrade to jclouds 1.0-beta-7. Contributed by Adrian Cole.
tomwhite Sep 22, 2010
5f7a7d4
WHIRR-97. Lucid is not stable on EC2.
tomwhite Sep 28, 2010
d8131b6
WHIRR-103. Add more to .gitignore. Contributed by phunt.
tomwhite Sep 28, 2010
cbabae4
WHIRR-100. Create a binary distribution of Whirr.
tomwhite Sep 28, 2010
d29a8b0
WHIRR-105. Add version command to the CLI.
tomwhite Sep 29, 2010
37f002c
WHIRR-102. unknown service NPEs cli, should print the bad service to …
phunt Sep 30, 2010
d88a31e
WHIRR-104. print available services in cli help string
phunt Sep 30, 2010
543bd0c
WHIRR-73. Add a list command to the CLI.
tomwhite Oct 1, 2010
b8d681c
WHIRR-108. Fix checkstyle and rat violations.
tomwhite Oct 1, 2010
1314a82
WHIRR-101. Hadoop on EC2 does not use the /mnt partition.
tomwhite Oct 4, 2010
67f0195
WHIRR-109. Unit tests fail if there is no private key found at ~/.ssh…
tomwhite Oct 4, 2010
8af7366
WHIRR-107. Test failing due to not matching Amazon Linux AMI on EC2.
tomwhite Oct 4, 2010
01f5b1b
WHIRR-106. Improve logging in whirr cli.
tomwhite Oct 4, 2010
1475ada
WHIRR-110. Create client-side Hadoop configuration file during cluste…
tomwhite Oct 5, 2010
97240da
WHIRR-113. Hadoop cluster instances should all start in the same loca…
tomwhite Oct 5, 2010
26137d8
WHIRR-112. Expand documentation.
tomwhite Oct 7, 2010
c7b5b98
WHIRR-114. Support + character in version number.
tomwhite Oct 26, 2010
59df44e
WHIRR-52. Support all services on Rackspace Cloud Servers.
tomwhite Oct 27, 2010
7596b3d
WHIRR-91. Add DISCLAIMER file to CLI JAR
phunt Nov 1, 2010
6d9f083
Preparing for release 0.2.0-incubating
phunt Nov 1, 2010
1d9eb1d
Preparing for release 0.2.0-incubating
phunt Nov 1, 2010
08c56a7
WHIRR-122. whirr site has two FAQ links
phunt Nov 2, 2010
a733c5d
WHIRR-126. Deployment process does not deploy required test JARs
phunt Nov 4, 2010
a98c293
WHIRR-128. Fix DNS resolution for clients running within EC2. Contrib…
tomwhite Nov 9, 2010
8090205
WHIRR-137. Allow use of an arbitrary AMI on EC2.
tomwhite Nov 23, 2010
a76c54c
WHIRR-87. Parallelize Hadoop cluster creation.
tomwhite Nov 30, 2010
1875079
WHIRR-146. Changing the mapred.child.java.opts value does not change …
tomwhite Nov 30, 2010
93b1cb1
Reverted changes to scripts/apache/hadoop/post-configure from WHIRR-146
tomwhite Dec 1, 2010
13b730d
WHIRR-147. Regression on launching clusters from EC2.
tomwhite Dec 1, 2010
faaeac0
WHIRR-151. Credentials not set correctly for Hadoop service configure…
tomwhite Dec 2, 2010
1341b3c
WHIRR-92. Add a benchmark for Hadoop clusters.
tomwhite Dec 3, 2010
d290230
WHIRR-117. Composable services.
tomwhite Dec 7, 2010
bb90f15
WHIRR-153. Add documentation for WHIRR-87 (Parallelize Hadoop cluster…
tomwhite Dec 9, 2010
1a7ea42
WHIRR-115. Distribution should include documentation.
tomwhite Dec 9, 2010
c3fb190
WHIRR-123. Cassandra integration tests hang if whirr's scripts bucket…
tomwhite Dec 9, 2010
ef9d78b
Reverted WHIRR-123
tomwhite Dec 10, 2010
5cf9b18
WHIRR-154. Cassandra: expose JMX port. Contributed by Kelvin Kakugawa.
tomwhite Dec 10, 2010
e6775f0
WHIRR-159. Cassandra and ZooKeeper fail on Ubuntu on Rackspace.
tomwhite Dec 13, 2010
444bea8
WHIRR-160. Improve SSH key diagnostics. Contributed by Andrei Savu.
tomwhite Dec 15, 2010
7bb2d2a
Fix broken imports from WHIRR-160.
tomwhite Dec 15, 2010
64a4109
WHIRR-123. Cassandra integration tests hang if whirr's scripts bucket…
tomwhite Dec 16, 2010
8bb2e0f
WHIRR-150. Allow retrieval of instance roles.
tomwhite Dec 16, 2010
905a6b5
WHIRR-163. Support environment variable interpolation in configuratio…
tomwhite Dec 16, 2010
7e24ebe
WHIRR-156. Cli script doesn't launch post-modularization.
tomwhite Dec 17, 2010
495ff7c
WHIRR-157. Remove service name property.
tomwhite Dec 20, 2010
ba62e5b
WHIRR-166. Improve docs regarding private keys. Contributed by Stu Hood.
tomwhite Dec 20, 2010
f72a54d
WHIRR-162. DnsUtilTest fails when offline or for slow connections. Co…
tomwhite Dec 21, 2010
3ee77d6
WHIRR-174. Fix ZooKeeper to allow stand-alone mode setups. Contribute…
tomwhite Dec 22, 2010
c17bb74
WHIRR-175. ZooKeeper service does not honor instance roles. Contribut…
tomwhite Dec 22, 2010
82bc75f
WHIRR-165. Hadoop integration tests fail due to WHIRR-160 changes.
tomwhite Dec 23, 2010
1d40793
WHIRR-176. Set AWS credentials in the local site file for Hadoop S3 a…
tomwhite Jan 3, 2011
aa05973
WHIRR-25. Add HBase service. Contributed by Lars George.
tomwhite Jan 3, 2011
c992c6e
WHIRR-180. ListClusterCommand.run throws a NullPointerException for u…
tomwhite Jan 3, 2011
824d49b
WHIRR-145. Add Whirr recipes for common configurations.
tomwhite Jan 3, 2011
e69a8a1
WHIRR-178. [Hadoop] Guard useradd against existing user account. Cont…
tomwhite Jan 3, 2011
e54d351
WHIRR-179. [Hadoop] Guard /tmp mkdir call against existing directory.…
tomwhite Jan 3, 2011
dcda62f
WHIRR-184. Add HBase service to list of dependencies. Contributed by …
tomwhite Jan 3, 2011
02538a5
WHIRR-185. [ZooKeeper] Fix selection of instances for getHosts() call…
tomwhite Jan 3, 2011
dc117d2
WHIRR-187. [HBase] Change hbase.tmp.dir to be in line with Hadoop ser…
tomwhite Jan 3, 2011
1cc59cd
WHIRR-181. Add descriptions for CLI command options. Contributed by A…
tomwhite Jan 3, 2011
db8671e
WHIRR-161. Check that both SSH keys belong to the same pair. Contribu…
tomwhite Jan 4, 2011
98af561
WHIRR-164. Tests fail if there is no ~/.ssh/id_rsa keypair. Contribut…
tomwhite Jan 4, 2011
c09f765
WHIRR-155. Support multiple versions of Cassandra. Contributed by Stu…
Jan 5, 2011
2ba090c
WHIRR-155. Support multiple versions of Cassandra. Contributed by Stu…
Jan 5, 2011
ca4ac46
WHIRR-190. Create /tmp in HDFS for Pig. Contributed by Andrei Savu an…
tomwhite Jan 7, 2011
3a080db
WHIRR-202. Improve instance template syntax checking.
tomwhite Jan 13, 2011
f9c311c
WHIRR-200. Cassandra integration test hangs. Contributed by Stu Hood.
tomwhite Jan 14, 2011
0005c24
WHIRR-205. Override service.provider for integration tests. Contribut…
tomwhite Jan 14, 2011
4cdd680
Reverted WHIRR-184 since it was an invalid change.
tomwhite Jan 14, 2011
78a6c53
WHIRR-204. CDH Hadoop integration test fails on Rackspace.
tomwhite Jan 14, 2011
999de90
WHIRR-203. General documentation improvements for 0.3.0.
tomwhite Jan 14, 2011
deee213
WHIRR-206. [HBase] Extract strings to a HBaseServiceConstants interfa…
tomwhite Jan 14, 2011
72b70f9
WHIRR-210. Remove unneeded dependent libraries.
tomwhite Jan 15, 2011
afdb5ae
WHIRR-211. Fix checkstyle errors for 0.3.0.
tomwhite Jan 16, 2011
bd585a0
Preparing for release 0.3.0-incubating
tomwhite Jan 16, 2011
fa1f654
Updating trunk after branching for 0.3.0-incubating
tomwhite Jan 16, 2011
270ac7d
Added myself as a committer to the top-level pom.xml
Jan 18, 2011
88f6fe2
Added larsgeorge to developers
larsgeorge Jan 18, 2011
440e5b6
WHIRR-194. Update the list of supported services on the home page. Co…
tomwhite Jan 19, 2011
7b004e6
Update release notes with 0.3.0 issues.
tomwhite Jan 19, 2011
3191ad8
WHIRR-139. upgrade to version 1 of the enforcer plugin (Jakob Homan v…
Jan 21, 2011
6a46f88
WHIRR-193. Recipe for a HBase Cluster. (asavu)
Jan 24, 2011
cd10919
WHIRR-170. Instances should be started in the order specified in the …
Jan 27, 2011
e1632b6
WHIRR-186. [HBase] Add version support configurable in properties file.
larsgeorge Jan 27, 2011
57f2c97
WHIRR-186. Adds missing properties.
larsgeorge Jan 27, 2011
d4153a2
WHIRR-201. [HBase] Integration test fails. Contributed by largsgeorge.
tomwhite Jan 28, 2011
85db5d2
WHIRR-219. Support dynamic addition of services to CLI.
tomwhite Jan 28, 2011
e1c4a99
WHIRR-217. Log files should not be included in tarball or checked by …
Jan 29, 2011
70acbbc
WHIRR-195. Display available roles instead of service names when runn…
Jan 30, 2011
c946a34
WHIRR-199. Add aliases for short role names like nn, jt, tt, dn, zk. …
tomwhite Jan 31, 2011
848bc5e
WHIRR-183. ZooKeeper Data Directory Cleanup. Contributed by asavu.
tomwhite Jan 31, 2011
2c65bae
WHIRR-124. Upgrade to jclouds 1.0-beta-9 (Adrian Cole via larsgeorge)
larsgeorge Feb 7, 2011
6b46782
Reverted WHIRR-124 since the change is breaking the integration tests
Feb 7, 2011
54f7421
WHIRR-226. Add the ability to destroy a cluster instance (asavu)
Feb 7, 2011
b72b803
WHIRR-124. Upgrade to jclouds 1.0-beta-9 (Adrian Cole via larsgeorge)
Feb 7, 2011
5931284
WHIRR-232. NPE for stopped instances on EC2.
tomwhite Feb 8, 2011
1cedfd8
WHIRR-124. Show warning messages before converting ec2->aws-ec2 and c…
Feb 8, 2011
2fd9258
WHIRR-225. Support locally-supplied scripts.
tomwhite Feb 9, 2011
156b123
Remove empty directories.
tomwhite Feb 9, 2011
7f88dce
Fixed a typo from WHIRR-225. Renamed installRunUrl to install_runurl.
Feb 10, 2011
7cc92de
WHIRR-167. Improve bootstrapping and configuration to be able to isol…
Feb 10, 2011
fe8bbe0
WHIRR-55. Users should be able to override an arbitrary Hadoop proper…
tomwhite Feb 10, 2011
d056a8a
WHIRR-234. Resource functions/install_cdh_hadoop.sh not found when ru…
Feb 11, 2011
86cf425
WHIRR-235. fix whirr.provider in recipes/* (Eugene Koontz via asavu)
Feb 13, 2011
8680d77
WHIRR-207. Handle curl timeouts better (asavu)
Feb 19, 2011
d70c2fc
WHIRR-198. Support user-defined images (Adrian Cole via asavu)
Feb 19, 2011
8ade748
WHIRR-241. Update to use CDH3B4.
tomwhite Feb 24, 2011
b18d037
WHIRR-242. Update documentation for overriding locally supplied scrip…
Feb 26, 2011
e9aa3e3
Preparing for release 0.4.0-incubating
Feb 28, 2011
eb96b76
Added Trunk (unreleased changes) to CHANGES.txt
Feb 28, 2011
6fe3a99
Updated version number in pom files
Feb 28, 2011
f9b5014
Added Release Notes for 0.4.0
Feb 28, 2011
04c234f
WHIRR-244. Add package-level javadoc.
tomwhite Mar 3, 2011
4dfa265
WHIRR-247. Add license headers to service install and configure scrip…
Mar 5, 2011
61faff0
WHIRR-248 Update to jclouds-1.0-beta9b
Mar 7, 2011
2b62ba0
updated changelog
Mar 7, 2011
f44fd7e
WHIRR-129 Add Adrian Cole as a committer in the whirr pom.xml and site
Mar 7, 2011
c0d898b
Updated CHANGES.txt, we will recut 0.4.0 from the current trunk
Mar 10, 2011
9cdcc96
WHIRR-251. Handle Apache cryptography requirements for release (tomwh…
Mar 10, 2011
7c50d6b
WHIRR-250. Ensure all libraries in binary distribution have associate…
Mar 10, 2011
6c081b5
WHIRR-254. Document limitation that a role may only appear in one ins…
Mar 13, 2011
fc58fb0
WHIRR-259. Disable configuration list handling for Hadoop properties
Mar 14, 2011
e1eb360
WHIRR-198. support user-defined images (rewrote to fix classpath orde…
Mar 15, 2011
ebe0e6e
Preparing for release 0.4.0-incubating
Mar 15, 2011
f734933
Preparing trunk for more development
Mar 15, 2011
e7bc4f1
Updated release notes for 0.4.0-incubating
Mar 15, 2011
511d8bf
WHIRR-263. Default tarball not found for Cassandra (broken link in in…
Mar 16, 2011
b0ac2d6
Updated release notes for 0.4.0-incubating
Mar 16, 2011
88052d1
WHIRR-158. Allow users to log into clusters as themselves
Mar 17, 2011
ad6a5cd
WHIRR-233. Change test properties to be less provider bound and impro…
Mar 17, 2011
ae0346f
Updated release notes for 0.4.0-incubating release
Mar 17, 2011
df9cbaa
Fixed typo in release notes
Mar 17, 2011
a57ec9c
WHIRR-265. Missing SVN EOL properties (Sebb via asavu)
Mar 20, 2011
bfd3169
Remove empty (unused) license files. (Part of WHIRR-250.)
tomwhite Mar 21, 2011
cf78f51
WHIRR-253. ZooKeeper service should only authorize ingress to ZooKeep…
Mar 22, 2011
127e312
WHIRR-267. Update NOTICE and LICENSE files to mention 3rd party produ…
Mar 23, 2011
d0df542
Updated release notes for 0.4.0-incubating
Mar 23, 2011
1d555ce
WHIRR-261. Add ElasticSearch as a service
Mar 29, 2011
8eef9dc
WHIRR-268. whirr hangs when the file '/home/andrei/.ssh/known_hosts' …
Mar 31, 2011
b218ec4
WHIRR-271. Classpath needs to be quoted in whirr script
Mar 31, 2011
f757253
WHIRR-274. Add wagon-ssh-external as a maven build extension
Apr 4, 2011
86c47f2
WHIRR-222. Support multiple versions of Hadoop.
tomwhite Apr 5, 2011
b261855
Update svn:ignore properties for elastic search with hidden Eclipse f…
tomwhite Apr 5, 2011
de588cf
WHIRR-262. Services should not have to do reverse DNS lookups.
tomwhite Apr 6, 2011
0a02db1
WHIRR-275. Improve firewall API for services.
tomwhite Apr 11, 2011
d418756
Ignoring log files generated by running whirr
Apr 12, 2011
3f8a9cd
WHIRR-237. Add Voldemort as a service. (Kirk True via asavu)
Apr 12, 2011
52988b9
Update svn:ignore properties for cli and voldermort
tomwhite Apr 12, 2011
ab4a717
WHIRR-278. Refactor ClusterSpec and extract InstanceTemplate class
Apr 12, 2011
af17959
WHIRR-245. Clearly demarcate the user and service provider APIs.
tomwhite Apr 13, 2011
c220af6
Removed some empty folders
Apr 13, 2011
c0ceaee
Removed more empty directories.
tomwhite Apr 13, 2011
36874dd
WHIRR-269. Improve error msg "Key pair is encrypted" (tomwhite via as…
Apr 14, 2011
676ed20
WHIRR-277. Support multiple versions of ZooKeeper
Apr 14, 2011
1bf7fa1
WHIRR-172. Log warning for unrecognized service names.
tomwhite Apr 14, 2011
1aed340
WHIRR-282. Set number of Hadoop slots based on hardware (tomwhite via…
Apr 14, 2011
5d6d401
WHIRR-284. Runurl should only be installed when needed (tomwhite via …
Apr 15, 2011
6bd4204
WHIRR-283. Whirr in 5 minutes.
tomwhite Apr 15, 2011
7596c4f
WHIRR-279. Create ClusterSpec aware BlobStoreContext factory class (a…
Apr 18, 2011
ac87c5b
WHIRR-280. Create a blob cache that could be used for storing local f…
Apr 18, 2011
a7ef7ee
WHIRR-220. Support local tarball upload (asavu)
Apr 18, 2011
fc5f34a
WHIRR-285. Add support for BYON.
tomwhite Apr 27, 2011
ca8fae0
WHIRR-173. Add ClusterAction for generic script execution (asavu)
Apr 27, 2011
8d16024
WHIRR-246. Single place to store/load cluster state (David Alves and …
Apr 28, 2011
2bf1fc7
WHIRR-289. Display role names in list-cluster command (asavu)
Apr 28, 2011
98d2c1b
WHIRR-287. Script for running YCSB on HBase (asavu)
May 3, 2011
cfce961
WHIRR-291. Add "noop" role useful just for provisioning (asavu)
May 5, 2011
01c1fd6
WHIRR-297. Separate ZooKeeper and ElasticSearch install and configura…
May 5, 2011
caa6253
WHIRR-288. Add blob store persistence for cluster state (asavu)
May 5, 2011
96152f7
WHIRR-191. Start other services based on CDH, not just HDFS and MR.
tomwhite May 6, 2011
d66ed90
WHIRR-298. Use all cluster spec properties for hash and equality (tom…
May 6, 2011
8e35bec
WHIRR-300. FAQ entry for noop role. Contributed by asavu.
tomwhite May 9, 2011
ff47b77
WHIRR-216. Improve error message if whirr.instance-templates left out…
tomwhite May 9, 2011
b8e37eb
WHIRR-299. Recipe for BYON provider (asavu)
May 11, 2011
4c821ba
WHIRR-292. Separate Cassandra install and configuration scripts into …
May 11, 2011
37b0900
WHIRR-296. Separate Voldemort install and configuration scripts into …
tomwhite May 13, 2011
66f1a11
WHIRR-61. Make more efficient use of ComputeServiceContext. Contribut…
tomwhite May 15, 2011
7f63977
WHIRR-304. Upgrade to jclouds 1.0-beta-9c. Contributed by Adrian Cole.
tomwhite May 15, 2011
07bd95c
WHIRR-236. Update Configuration Guides with Recipe Info.
tomwhite May 15, 2011
1c5367d
WHIRR-141. Create a logo. Contributed by Alison Wong.
tomwhite May 17, 2011
75ff470
Preparing for release 0.5.0-incubating
tomwhite May 17, 2011
d728928
Branching for 0.5 releases
tomwhite May 17, 2011
38ef9f1
Preparing for release 0.5.0-incubating
tomwhite May 17, 2011
38c2eb7
Merge -r 1126858:1126859 from trunk to branch-0.22. Re-instated comme…
tomwhite May 24, 2011
df38d68
WHIRR-310. Improve Configuration Guide. Contributed by Andrei Savu.
tomwhite May 24, 2011
c74cf75
Merge -r 1126868:1126869 from trunk to branch-0.5. Fixes: WHIRR-312.
tomwhite May 24, 2011
49324b2
Merge -r 1127370:1127371 from trunk to branch-0.5.0. Add release note…
tomwhite May 25, 2011
3cbbb5c
Merge -r 1127686:1127687 from trunk to branch-0.5. Fixes: WHIRR-314.
tomwhite May 25, 2011
e6d970e
Preparing for release 0.5.0-incubating
tomwhite May 26, 2011
a8d32a0
Support installation and configuration of CDH3u0 hbase clusters
geoffblack Jun 23, 2011
8ed650a
Updated configs to fix requirements for HBase
geoffblack Jun 28, 2011
f245f15
Updated recipe with missing property
geoffblack Jun 28, 2011
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,8 @@
.svn
.classpath
.project
.settings
target
.idea/
*.iml
**/*.log
412 changes: 411 additions & 1 deletion CHANGES.txt

Large diffs are not rendered by default.

269 changes: 267 additions & 2 deletions LICENSE.txt

Large diffs are not rendered by default.

5 changes: 5 additions & 0 deletions NOTICE-src.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
Apache Whirr
Copyright 2010-2011 The Apache Software Foundation

This product includes software developed at
The Apache Software Foundation (http://www.apache.org/).
8 changes: 7 additions & 1 deletion NOTICE.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
Apache Whirr
Copyright 2010 The Apache Software Foundation
Copyright 2010-2011 The Apache Software Foundation

This product includes software developed at
The Apache Software Foundation (http://www.apache.org/).

This product includes Common Annotations (JSR 250, https://jsr250.dev.java.net/)
distributed under the CDDL Version 1.0 license.

This product includes Jersey (https://jersey.dev.java.net/)
distributed under the CDDL Version 1.0 license.

39 changes: 38 additions & 1 deletion README.txt
Original file line number Diff line number Diff line change
@@ -1,8 +1,45 @@
Apache Whirr

------------
Whirr is a set of libraries for running cloud services.

Release notes are in src/site/xdoc/release-notes.xml.

Website: http://incubator.apache.org/whirr
Quick start: http://incubator.apache.org/whirr/quick-start-guide.html

Cryptographic Software Notice
-----------------------------
This distribution includes cryptographic software. The country in
which you currently reside may have restrictions on the import,
possession, use, and/or re-export to another country, of
encryption software. BEFORE using any encryption software, please
check your country's laws, regulations and policies concerning the
import, possession, or use, and re-export of encryption software, to
see if this is permitted. See <http://www.wassenaar.org/> for more
information.

The U.S. Government Department of Commerce, Bureau of Industry and
Security (BIS), has classified this software as Export Commodity
Control Number (ECCN) 5D002.C.1, which includes information security
software using or performing cryptographic functions with asymmetric
algorithms. The form and manner of this Apache Software Foundation
distribution makes it eligible for export under the License Exception
ENC Technology Software Unrestricted (TSU) exception (see the BIS
Export Administration Regulations, Section 740.13) for both object
code and source code.

The following provides more details on the included cryptographic
software:

Whirr requires the JSch library for SSH support:
http://www.jcraft.com/jsch/index.html

Whirr requires Not-Yet-Commons-SSL for SSH keypair checking:
http://juliusdavies.ca/commons-ssl/

The jclouds enterprise module requires the BouncyCastle Java cryptography APIs:
http://www.bouncycastle.org/java.html

Export classifications and source links can be found
at http://www.apache.org/licenses/exports/.

28 changes: 28 additions & 0 deletions bin/whirr
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
#!/usr/bin/env bash

# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

bin=`dirname "$0"`
bin=`cd "$bin"; pwd`

if [ -d "$bin/../cli/target/lib" ]; then
classpath="$bin/..:$bin/../cli/target/lib/*:$bin/../cli/target/*"
else
classpath="$bin/..:$bin/../lib/*"
fi

java -cp "$classpath" org.apache.whirr.cli.Main "$@"

4 changes: 2 additions & 2 deletions build-tools/pom.xml
Original file line number Diff line number Diff line change
@@ -20,12 +20,12 @@
<parent>
<groupId>org.apache.whirr</groupId>
<artifactId>whirr</artifactId>
<version>0.1.0-incubating-SNAPSHOT</version>
<version>0.5.0-incubating</version>
<relativePath>../pom.xml</relativePath>
</parent>
<groupId>org.apache.whirr</groupId>
<artifactId>whirr-build-tools</artifactId>
<packaging>jar</packaging>
<version>0.1.0-incubating-SNAPSHOT</version>
<version>0.5.0-incubating</version>
<name>Apache Whirr Build Tools</name>
</project>
54 changes: 54 additions & 0 deletions build-tools/src/assemble-bin.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
<!--
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<assembly xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.0 http://maven.apache.org/xsd/assembly-1.1.0.xsd">
<id>bin</id>
<formats>
<format>tar.gz</format>
</formats>
<moduleSets>
<moduleSet>
<binaries>
<includeDependencies>true</includeDependencies>
<outputDirectory>lib</outputDirectory>
<unpack>false</unpack>
<dependencySets>
<dependencySet/>
</dependencySets>
</binaries>
</moduleSet>
</moduleSets>
<fileSets>
<fileSet>
<directory>${project.basedir}</directory>
<useDefaultExcludes>true</useDefaultExcludes>
<excludes>
<exclude>NOTICE-src.txt</exclude>
<exclude>**/.project</exclude>
<exclude>**/.gitignore</exclude>
<exclude>**/${project.build.directory}/**</exclude>
<exclude>**/dependency-reduced-pom.xml</exclude>
<exclude>**/*.log</exclude>
</excludes>
</fileSet>
<fileSet>
<directory>${project.build.directory}/site</directory>
<outputDirectory>docs</outputDirectory>
</fileSet>
</fileSets>
</assembly>
Original file line number Diff line number Diff line change
@@ -26,11 +26,19 @@
<directory>${project.basedir}</directory>
<useDefaultExcludes>true</useDefaultExcludes>
<excludes>
<exclude>NOTICE*.txt</exclude>
<exclude>**/.project</exclude>
<exclude>**/.gitignore</exclude>
<exclude>**/${project.build.directory}/**</exclude>
<exclude>**/dependency-reduced-pom.xml</exclude>
<exclude>**/*.log</exclude>
</excludes>
</fileSet>
</fileSets>
<files>
<file>
<source>NOTICE-src.txt</source>
<destName>NOTICE.txt</destName>
</file>
</files>
</assembly>
97 changes: 53 additions & 44 deletions cli/pom.xml
Original file line number Diff line number Diff line change
@@ -20,15 +20,20 @@
<parent>
<groupId>org.apache.whirr</groupId>
<artifactId>whirr</artifactId>
<version>0.1.0-incubating-SNAPSHOT</version>
<version>0.5.0-incubating</version>
<relativePath>../pom.xml</relativePath>
</parent>
<groupId>org.apache.whirr</groupId>
<artifactId>whirr-cli</artifactId>
<packaging>jar</packaging>
<version>0.1.0-incubating-SNAPSHOT</version>
<version>0.5.0-incubating</version>
<name>Apache Whirr CLI</name>
<dependencies>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>whirr-voldemort</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>whirr-core</artifactId>
@@ -39,6 +44,11 @@
<artifactId>whirr-cassandra</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>whirr-cdh</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>whirr-hadoop</artifactId>
@@ -50,23 +60,25 @@
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.jclouds</groupId>
<artifactId>jclouds-aws</artifactId>
<groupId>${project.groupId}</groupId>
<artifactId>whirr-elasticsearch</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.jclouds</groupId>
<artifactId>jclouds-compute</artifactId>
<groupId>${project.groupId}</groupId>
<artifactId>whirr-hbase</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.jclouds</groupId>
<artifactId>jclouds-enterprise</artifactId>
<artifactId>jclouds-compute</artifactId>
</dependency>
<dependency>
<groupId>org.jclouds</groupId>
<groupId>org.jclouds.driver</groupId>
<artifactId>jclouds-jsch</artifactId>
</dependency>
<dependency>
<groupId>org.jclouds</groupId>
<groupId>org.jclouds.driver</groupId>
<artifactId>jclouds-log4j</artifactId>
</dependency>
<dependency>
@@ -81,6 +93,18 @@
<groupId>org.mockito</groupId>
<artifactId>mockito-all</artifactId>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
</dependency>
<dependency>
<groupId>com.jcraft</groupId>
<artifactId>jsch</artifactId>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
@@ -96,43 +120,28 @@
</dependency>
</dependencies>
<build>
<resources>
<resource>
<directory>${project.basedir}/src/main/resources</directory>
<filtering>true</filtering>
</resource>
</resources>
<plugins>
<!-- Copy dependencies to lib directory so that bin/whirr works in place -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>1.4</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<finalName>whirr-cli-${project.version}</finalName>
<transformers>
<transformer implementation="org.apache.maven.plugins.shade.resource.ApacheLicenseResourceTransformer" />
<transformer implementation="org.apache.maven.plugins.shade.resource.ApacheNoticeResourceTransformer">
<projectName>Apache Whirr</projectName>
</transformer>
<transformer implementation="org.apache.maven.plugins.shade.resource.ServicesResourceTransformer" />
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
<mainClass>org.apache.whirr.cli.Main</mainClass>
</transformer>
</transformers>
<filters>
<filter>
<artifact>*:*</artifact>
<excludes>
<exclude>META-INF/*.SF</exclude>
<exclude>META-INF/*.DSA</exclude>
<exclude>META-INF/*.RSA</exclude>
</excludes>
</filter>
</filters>
</configuration>
</execution>
</executions>
</plugin>
<artifactId>maven-dependency-plugin</artifactId>
<executions>
<execution>
<phase>install</phase>
<goals>
<goal>copy-dependencies</goal>
</goals>
<configuration>
<outputDirectory>${project.build.directory}/lib</outputDirectory>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
59 changes: 48 additions & 11 deletions cli/src/main/java/org/apache/whirr/cli/Main.java
Original file line number Diff line number Diff line change
@@ -19,16 +19,23 @@
package org.apache.whirr.cli;

import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import org.apache.whirr.cli.command.DestroyClusterCommand;
import org.apache.whirr.cli.command.DestroyInstanceCommand;
import org.apache.whirr.cli.command.LaunchClusterCommand;
import org.apache.whirr.cli.command.ListClusterCommand;
import org.apache.whirr.cli.command.RunScriptCommand;
import org.apache.whirr.cli.command.VersionCommand;
import org.apache.whirr.service.ClusterActionHandler;

import java.io.IOException;
import java.io.InputStream;
import java.io.PrintStream;
import java.util.Arrays;
import java.util.List;
import java.util.Map;

import org.apache.whirr.cli.command.DestroyClusterCommand;
import org.apache.whirr.cli.command.LaunchClusterCommand;
import java.util.ServiceLoader;
import java.util.SortedSet;

/**
* The entry point for the Whirr CLI.
@@ -48,23 +55,53 @@ public class Main {
int run(InputStream in, PrintStream out, PrintStream err,
List<String> list) throws Exception {
if (list.isEmpty()) {
out.println("Usage: whirr COMMAND [ARGS]");
out.println("where COMMAND may be one of:");
out.println();
for (Command command : commandMap.values()) {
out.printf("%" + maxLen + "s %s\n", command.getName(),
command.getDescription());
}
printUsage(out);
return -1;
}
Command command = commandMap.get(list.get(0));
if (command == null) {
err.printf("Unrecognized command '%s'\n", list.get(0));
err.println();
printUsage(err);
return -1;
}
return command.run(in, out, err, list.subList(1, list.size()));
}

private void printUsage(PrintStream stream) {
stream.println("Usage: whirr COMMAND [ARGS]");
stream.println("where COMMAND may be one of:");
stream.println();
for (Command command : commandMap.values()) {
stream.printf("%" + maxLen + "s %s\n", command.getName(),
command.getDescription());
}
stream.println();
stream.println("Available roles for instances:");
for(String roleName : getSortedRoleNames()) {
stream.println(" " + roleName);
}
}

private static SortedSet<String> getSortedRoleNames() {
ServiceLoader<ClusterActionHandler> loader =
ServiceLoader.load(ClusterActionHandler.class);

SortedSet<String> roles = Sets.newTreeSet();
for(ClusterActionHandler handler : loader) {
roles.add(handler.getRole());
}
return roles;
}

public static void main(String... args) throws Exception {
Main main = new Main(
new VersionCommand(),
new LaunchClusterCommand(),
new DestroyClusterCommand()
new DestroyClusterCommand(),
new DestroyInstanceCommand(),
new ListClusterCommand(),
new RunScriptCommand()
);
int rc = main.run(System.in, System.out, System.err, Arrays.asList(args));
System.exit(rc);
Original file line number Diff line number Diff line change
@@ -18,9 +18,9 @@

package org.apache.whirr.cli.command;

import static org.apache.whirr.service.ClusterSpec.Property.CLUSTER_NAME;
import static org.apache.whirr.service.ClusterSpec.Property.IDENTITY;
import static org.apache.whirr.service.ClusterSpec.Property.SERVICE_NAME;
import static org.apache.whirr.ClusterSpec.Property.CLUSTER_NAME;
import static org.apache.whirr.ClusterSpec.Property.IDENTITY;
import static org.apache.whirr.ClusterSpec.Property.INSTANCE_TEMPLATES;

import com.google.common.collect.Maps;

@@ -36,30 +36,53 @@
import org.apache.commons.configuration.Configuration;
import org.apache.commons.configuration.ConfigurationException;
import org.apache.commons.configuration.PropertiesConfiguration;
import org.apache.whirr.ClusterController;
import org.apache.whirr.ClusterControllerFactory;
import org.apache.whirr.ClusterSpec;
import org.apache.whirr.ClusterSpec.Property;
import org.apache.whirr.cli.Command;
import org.apache.whirr.service.ClusterSpec;
import org.apache.whirr.service.ServiceFactory;
import org.apache.whirr.service.ClusterSpec.Property;
import org.apache.whirr.service.ClusterStateStore;
import org.apache.whirr.service.ClusterStateStoreFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
* An abstract command for interacting with clusters.
*/
public abstract class ClusterSpecCommand extends Command {

protected ServiceFactory factory;
public abstract class AbstractClusterSpecCommand extends Command {

private static final Logger LOG =
LoggerFactory.getLogger(AbstractClusterSpecCommand.class);

protected ClusterControllerFactory factory;
protected ClusterStateStoreFactory stateStoreFactory;

protected OptionParser parser = new OptionParser();
private Map<Property, OptionSpec> optionSpecs;
private OptionSpec<String> configOption = parser.accepts("config")
.withRequiredArg().ofType(String.class);
private Map<Property, OptionSpec<?>> optionSpecs;
private OptionSpec<String> configOption = parser
.accepts("config", "Note that Whirr properties specified in " +
"this file should all have a whirr. prefix.")
.withRequiredArg()
.describedAs("config.properties")
.ofType(String.class);

public ClusterSpecCommand(String name, String description, ServiceFactory factory) {
public AbstractClusterSpecCommand(String name, String description,
ClusterControllerFactory factory) {
this(name, description, factory, new ClusterStateStoreFactory());
}

public AbstractClusterSpecCommand(String name, String description,
ClusterControllerFactory factory,
ClusterStateStoreFactory stateStoreFactory) {
super(name, description);

this.factory = factory;

this.stateStoreFactory = stateStoreFactory;

optionSpecs = Maps.newHashMap();
for (Property property : EnumSet.allOf(Property.class)) {
ArgumentAcceptingOptionSpec<?> spec = parser.accepts(property.getSimpleName())
ArgumentAcceptingOptionSpec<?> spec = parser
.accepts(property.getSimpleName(), property.getDescription())
.withRequiredArg()
.ofType(property.getType());
if (property.hasMultipleArguments()) {
@@ -68,12 +91,12 @@ public ClusterSpecCommand(String name, String description, ServiceFactory factor
optionSpecs.put(property, spec);
}
}

protected ClusterSpec getClusterSpec(OptionSet optionSet) throws ConfigurationException {
Configuration optionsConfig = new PropertiesConfiguration();
for (Map.Entry<Property, OptionSpec> entry : optionSpecs.entrySet()) {
for (Map.Entry<Property, OptionSpec<?>> entry : optionSpecs.entrySet()) {
Property property = entry.getKey();
OptionSpec option = entry.getValue();
OptionSpec<?> option = entry.getValue();
if (property.hasMultipleArguments()) {
optionsConfig.setProperty(property.getConfigName(),
optionSet.valuesOf(option));
@@ -89,13 +112,29 @@ protected ClusterSpec getClusterSpec(OptionSet optionSet) throws ConfigurationEx
config.addConfiguration(defaults);
}

for (Property required : EnumSet.of(SERVICE_NAME, CLUSTER_NAME, IDENTITY)) {
for (Property required : EnumSet.of(CLUSTER_NAME, IDENTITY, INSTANCE_TEMPLATES)) {
if (config.getString(required.getConfigName()) == null) {
throw new IllegalArgumentException(String.format("Option '%s' not set.",
required.getSimpleName()));
}
}
return ClusterSpec.fromConfiguration(config);
return new ClusterSpec(config);
}

/**
* Create the specified service
*/
protected ClusterController createClusterController(String serviceName) {
ClusterController controller = factory.create(serviceName);
if (controller == null) {
LOG.warn("Unable to find service {}, using default.", serviceName);
controller = factory.create(null);
}
return controller;
}

protected ClusterStateStore createClusterStateStore(ClusterSpec spec) {
return stateStoreFactory.create(spec);
}

}
Original file line number Diff line number Diff line change
@@ -26,20 +26,20 @@
import joptsimple.OptionParser;
import joptsimple.OptionSet;

import org.apache.whirr.service.ClusterSpec;
import org.apache.whirr.service.Service;
import org.apache.whirr.service.ServiceFactory;
import org.apache.whirr.ClusterController;
import org.apache.whirr.ClusterControllerFactory;
import org.apache.whirr.ClusterSpec;

/**
* A command to destroy a running cluster (terminate and cleanup).
*/
public class DestroyClusterCommand extends ClusterSpecCommand {
public class DestroyClusterCommand extends AbstractClusterSpecCommand {

public DestroyClusterCommand() throws IOException {
this(new ServiceFactory());
this(new ClusterControllerFactory());
}

public DestroyClusterCommand(ServiceFactory factory) {
public DestroyClusterCommand(ClusterControllerFactory factory) {
super("destroy-cluster", "Terminate and cleanup resources for a running cluster.", factory);
}

@@ -56,8 +56,8 @@ public int run(InputStream in, PrintStream out, PrintStream err,
try {
ClusterSpec clusterSpec = getClusterSpec(optionSet);

Service service = factory.create(clusterSpec.getServiceName());
service.destroyCluster(clusterSpec);
ClusterController controller = createClusterController(clusterSpec.getServiceName());
controller.destroyCluster(clusterSpec);
return 0;
} catch (IllegalArgumentException e) {
err.println(e.getMessage());
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.apache.whirr.cli.command;

import java.io.IOException;
import java.io.InputStream;
import java.io.PrintStream;
import java.util.List;

import joptsimple.OptionParser;
import joptsimple.OptionSet;
import joptsimple.OptionSpec;

import org.apache.whirr.ClusterController;
import org.apache.whirr.ClusterControllerFactory;
import org.apache.whirr.ClusterSpec;

/**
* A command to destroy an instance from a cluster
*/
public class DestroyInstanceCommand extends AbstractClusterSpecCommand {

private OptionSpec<String> instanceOption = parser
.accepts("instance-id", "Cluster instance ID")
.withRequiredArg()
.ofType(String.class);

public DestroyInstanceCommand() throws IOException {
this(new ClusterControllerFactory());
}

public DestroyInstanceCommand(ClusterControllerFactory factory) {
super("destroy-instance", "Terminate and cleanup resources " +
"for a single instance.", factory);
}

@Override
public int run(InputStream in, PrintStream out,
PrintStream err, List<String> args) throws Exception {

OptionSet optionSet = parser.parse(args.toArray(new String[0]));
if (!optionSet.nonOptionArguments().isEmpty()) {
printUsage(parser, err);
return -1;
}
try {
if (!optionSet.hasArgument(instanceOption)) {
throw new IllegalArgumentException("You need to specify an instance ID.");
}
ClusterSpec clusterSpec = getClusterSpec(optionSet);
ClusterController controller = createClusterController(clusterSpec.getServiceName());

String instanceId = optionSet.valueOf(instanceOption);
controller.destroyInstance(clusterSpec, instanceId);

return 0;

} catch(IllegalArgumentException e) {
err.println(e.getMessage());
printUsage(parser, err);
return -1;
}
}

private void printUsage(OptionParser parser, PrintStream stream) throws IOException {
stream.println("Usage: whirr destroy-instance --instance-id <ID> [OPTIONS]");
stream.println();
parser.printHelpOn(stream);
}
}
Original file line number Diff line number Diff line change
@@ -26,21 +26,21 @@
import joptsimple.OptionParser;
import joptsimple.OptionSet;

import org.apache.whirr.service.Cluster;
import org.apache.whirr.service.ClusterSpec;
import org.apache.whirr.service.Service;
import org.apache.whirr.service.ServiceFactory;
import org.apache.whirr.Cluster;
import org.apache.whirr.ClusterController;
import org.apache.whirr.ClusterControllerFactory;
import org.apache.whirr.ClusterSpec;

/**
* A command to launch a new cluster.
*/
public class LaunchClusterCommand extends ClusterSpecCommand {
public class LaunchClusterCommand extends AbstractClusterSpecCommand {

public LaunchClusterCommand() throws IOException {
this(new ServiceFactory());
this(new ClusterControllerFactory());
}

public LaunchClusterCommand(ServiceFactory factory) {
public LaunchClusterCommand(ClusterControllerFactory factory) {
super("launch-cluster", "Launch a new cluster running a service.", factory);
}

@@ -57,8 +57,8 @@ public int run(InputStream in, PrintStream out, PrintStream err,

try {
ClusterSpec clusterSpec = getClusterSpec(optionSet);
Service service = factory.create(clusterSpec.getServiceName());
Cluster cluster = service.launchCluster(clusterSpec);
ClusterController controller = createClusterController(clusterSpec.getServiceName());
Cluster cluster = controller.launchCluster(clusterSpec);
out.printf("Started cluster of %s instances\n",
cluster.getInstances().size());
out.println(cluster);
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.apache.whirr.cli.command;

import com.google.common.base.Joiner;

import java.io.IOException;
import java.io.InputStream;
import java.io.PrintStream;
import java.util.List;

import joptsimple.OptionParser;
import joptsimple.OptionSet;

import org.apache.whirr.Cluster;
import org.apache.whirr.ClusterController;
import org.apache.whirr.ClusterControllerFactory;
import org.apache.whirr.ClusterSpec;
import org.apache.whirr.service.ClusterStateStore;
import org.apache.whirr.service.ClusterStateStoreFactory;

/**
* A command to list the nodes in a cluster.
*/
public class ListClusterCommand extends AbstractClusterSpecCommand {

public ListClusterCommand() throws IOException {
this(new ClusterControllerFactory());
}

public ListClusterCommand(ClusterControllerFactory factory) {
this(factory, new ClusterStateStoreFactory());
}

public ListClusterCommand(ClusterControllerFactory factory,
ClusterStateStoreFactory stateStoreFactory) {
super("list-cluster", "List the nodes in a cluster.", factory, stateStoreFactory);
}

@Override
public int run(InputStream in, PrintStream out, PrintStream err,
List<String> args) throws Exception {

OptionSet optionSet = parser.parse(args.toArray(new String[0]));

if (!optionSet.nonOptionArguments().isEmpty()) {
printUsage(parser, err);
return -1;
}
try {
ClusterSpec clusterSpec = getClusterSpec(optionSet);
ClusterStateStore stateStore = createClusterStateStore(clusterSpec);
ClusterController controller = createClusterController(clusterSpec.getServiceName());

for (Cluster.Instance instance : controller.getInstances(clusterSpec, stateStore)) {
out.println(Joiner.on('\t').join(
instance.getId(),
instance.getNodeMetadata().getImageId(),
instance.getPublicIp(),
instance.getPrivateIp(),
instance.getNodeMetadata().getState(),
instance.getNodeMetadata().getLocation().getId(),
Joiner.on(",").join(instance.getRoles())
)
);
}
return 0;
} catch (IllegalArgumentException e) {
err.println(e.getMessage());
printUsage(parser, err);
return -1;
}
}

private void printUsage(OptionParser parser, PrintStream stream) throws IOException {
stream.println("Usage: whirr list-cluster [OPTIONS]");
stream.println();
parser.printHelpOn(stream);
}
}
178 changes: 178 additions & 0 deletions cli/src/main/java/org/apache/whirr/cli/command/RunScriptCommand.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,178 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.apache.whirr.cli.command;

import com.google.common.base.Predicate;
import com.google.common.base.Predicates;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import com.google.common.io.Files;
import joptsimple.OptionParser;
import joptsimple.OptionSet;
import joptsimple.OptionSpec;
import org.apache.commons.lang.StringUtils;
import org.apache.whirr.Cluster;
import org.apache.whirr.ClusterController;
import org.apache.whirr.ClusterControllerFactory;
import org.apache.whirr.ClusterSpec;
import org.apache.whirr.service.ClusterStateStoreFactory;
import org.jclouds.compute.domain.ExecResponse;
import org.jclouds.compute.domain.NodeMetadata;
import org.jclouds.scriptbuilder.domain.Statement;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintStream;
import java.nio.charset.Charset;
import java.util.List;
import java.util.Map;

import static org.apache.whirr.RolePredicates.anyRoleIn;
import static org.jclouds.compute.predicates.NodePredicates.withIds;
import static org.jclouds.scriptbuilder.domain.Statements.exec;

public class RunScriptCommand extends AbstractClusterSpecCommand {

private OptionSpec<String> rolesOption = parser
.accepts("roles", "List of comma separated role names. " +
"E.g. zookeeper,hadoop-namenode")
.withRequiredArg()
.ofType(String.class);

private OptionSpec<String> instancesOption = parser
.accepts("instances", "List of comma separated instance IDs")
.withRequiredArg()
.ofType(String.class);

private OptionSpec<String> scriptOption = parser
.accepts("script", "Path to script file to execute.")
.withRequiredArg()
.ofType(String.class);

public RunScriptCommand() {
this(new ClusterControllerFactory());
}

public RunScriptCommand(ClusterControllerFactory factory) {
this(factory, new ClusterStateStoreFactory());
}

public RunScriptCommand(ClusterControllerFactory factory,
ClusterStateStoreFactory stateStoreFactory) {
super("run-script", "Run a script on a specific instance or a " +
"group of instances matching a role name", factory, stateStoreFactory);
}

@Override
public int run(InputStream in, PrintStream out, PrintStream err,
List<String> args) throws Exception {

OptionSet optionSet = parser.parse(args.toArray(new String[0]));
if (!optionSet.has(scriptOption)) {
err.println("Please specify a script file to be executed.");
printUsage(parser, err);
return -1;
}

if (!(new File(optionSet.valueOf(scriptOption))).exists()) {
err.printf("Script file '%s' not found.", optionSet.valueOf(scriptOption));
printUsage(parser, err);
return -2;
}

try {
ClusterSpec spec = getClusterSpec(optionSet);
ClusterController controller = createClusterController(spec.getServiceName());

Predicate<NodeMetadata> condition = buildFilterPredicate(optionSet, spec);

return handleScriptOutput(out, err, controller.runScriptOnNodesMatching(
spec, condition, execFile(optionSet.valueOf(scriptOption))));

} catch(IllegalArgumentException e) {
err.println(e.getMessage());
printUsage(parser, err);
return -3;
}
}

private Predicate<NodeMetadata> buildFilterPredicate(OptionSet optionSet, ClusterSpec spec)
throws IOException {

Predicate<NodeMetadata> condition = Predicates.alwaysTrue();

if (optionSet.has(instancesOption)) {
String[] ids = optionSet.valueOf(instancesOption).split(",");
return Predicates.and(condition, withIds(ids));

} else if(optionSet.has(rolesOption)) {
String[] roles = optionSet.valueOf(rolesOption).split(",");
List<String> ids = Lists.newArrayList();

Cluster cluster = createClusterStateStore(spec).load();
for (Cluster.Instance instance : cluster.getInstancesMatching(
anyRoleIn(Sets.<String>newHashSet(roles)))) {
ids.add(instance.getId().split("\\/")[1]);
}

condition = Predicates.and(condition,
withIds(ids.toArray(new String[0])));
}
return condition;
}

private int handleScriptOutput(PrintStream out, PrintStream err,
Map<? extends NodeMetadata, ExecResponse> responses) {
int rc = 0;
for (Map.Entry<? extends NodeMetadata, ExecResponse> entry : responses.entrySet()) {
out.printf("** Node %s: %s%n", entry.getKey().getId(),
Iterables.concat(entry.getKey().getPrivateAddresses(),
entry.getKey().getPublicAddresses()));

ExecResponse response = entry.getValue();
if (response.getExitCode() != 0) {
rc = response.getExitCode();
}
out.printf("%s%n", response.getOutput());
err.printf("%s%n", response.getError());
}
return rc;
}

private Statement execFile(String filePath) throws IOException {
return exec(getFileContent(filePath));
}

private String getFileContent(String filePath) throws IOException {
return StringUtils.join(Files.readLines(new File(filePath),
Charset.defaultCharset()),
"\n");
}

private void printUsage(OptionParser parser,
PrintStream stream) throws IOException {
stream.println("Usage: whirr run-script [OPTIONS] --script <script> " +
"[--instances id1,id2] [--roles role1,role2]");
stream.println();
parser.printHelpOn(stream);
}

}
46 changes: 46 additions & 0 deletions cli/src/main/java/org/apache/whirr/cli/command/VersionCommand.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.apache.whirr.cli.command;

import java.io.InputStream;
import java.io.PrintStream;
import java.util.List;

import org.apache.commons.io.IOUtils;
import org.apache.whirr.cli.Command;

public class VersionCommand extends Command {

public VersionCommand() {
super("version", "Print the version number and exit.");
}

@Override
public int run(InputStream in, PrintStream out, PrintStream err,
List<String> args) throws Exception {
InputStream input = getClass().getResourceAsStream("/version-banner.txt");
if (input == null) {
err.printf("Cannot determine version number\n");
return -1;
}
out.write(IOUtils.toByteArray(input));
return 0;
}

}
15 changes: 15 additions & 0 deletions cli/src/main/resources/META-INF/DISCLAIMER.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
Apache Whirr is an effort undergoing incubation at the Apache Software
Foundation (ASF), sponsored by the Apache Incubator PMC.

Incubation is required of all newly accepted projects until a further review
indicates that the infrastructure, communications, and decision making process
have stabilized in a manner consistent with other successful ASF projects.

While incubation status is not necessarily a reflection of the completeness
or stability of the code, it does indicate that the project has yet to be
fully endorsed by the ASF.

For more information about the incubation status of the Whirr project you
can go to the following page:

http://incubator.apache.org/whirr/
59 changes: 59 additions & 0 deletions cli/src/main/resources/log4j.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">

<!--
For more configuration information and examples see the Apache
Log4j website: http://logging.apache.org/log4j/
-->
<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/"
debug="false">

<appender name="CONSOLE" class="org.apache.log4j.ConsoleAppender">
<param name="Threshold" value="INFO" />
<layout class="org.apache.log4j.PatternLayout">
<param name="ConversionPattern" value="%m%n"/>
</layout>
</appender>

<appender name="ROLLINGFILE" class="org.apache.log4j.DailyRollingFileAppender">
<param name="File" value="whirr.log" />
<param name="Append" value="true" />
<param name="DatePattern" value="'.'yyyy-MM-dd" />
<param name="Threshold" value="TRACE" />
<layout class="org.apache.log4j.PatternLayout">
<param name="ConversionPattern" value="%d %-5p [%c] (%t) %m%n" />
</layout>
</appender>

<category name="org.apache.whirr">
<priority value="DEBUG" />
<appender-ref ref="ROLLINGFILE" />
</category>

<category name="jclouds.compute">
<priority value="DEBUG" />
<appender-ref ref="ROLLINGFILE" />
</category>

<root>
<priority value="INFO" />
<appender-ref ref="CONSOLE" />
</root>

</log4j:configuration>
1 change: 1 addition & 0 deletions cli/src/main/resources/version-banner.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Apache Whirr ${version}
12 changes: 11 additions & 1 deletion cli/src/test/java/org/apache/whirr/cli/MainTest.java
Original file line number Diff line number Diff line change
@@ -25,7 +25,7 @@
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

import com.google.inject.internal.Lists;
import com.google.common.collect.Lists;

import java.io.ByteArrayOutputStream;
import java.io.InputStream;
@@ -60,6 +60,16 @@ public void testNoArgs() throws Exception {
assertThat(bytes.toString(), containsString("test-command test description"));
}

@Test
public void testUnrecognizedCommand() throws Exception {
Main main = new Main(new TestCommand());
ByteArrayOutputStream bytes = new ByteArrayOutputStream();
PrintStream err = new PrintStream(bytes);
int rc = main.run(null, null, err, Lists.newArrayList("bogus-command"));
assertThat(rc, is(-1));
assertThat(bytes.toString(), containsString("Unrecognized command 'bogus-command'"));
}

@Test
public void testCommand() throws Exception {
Command command = mock(Command.class);
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.apache.whirr.cli;

import org.apache.whirr.Cluster;
import org.apache.whirr.service.ClusterStateStore;

import java.io.IOException;

/**
* Memory only cluster state storage useful for testing
*/
public class MemoryClusterStateStore extends ClusterStateStore {

private Cluster cluster;

@Override
public Cluster load() throws IOException {
return cluster;
}

@Override
public void save(Cluster cluster) throws IOException {
this.cluster = cluster;
}

@Override
public void destroy() throws IOException {
cluster = null;
}
}
Original file line number Diff line number Diff line change
@@ -21,35 +21,58 @@
import static org.hamcrest.Matchers.is;
import static org.junit.Assert.assertThat;

import java.io.File;
import java.io.InputStream;
import java.io.PrintStream;
import java.util.List;
import java.util.Map;

import joptsimple.OptionSet;

import org.apache.whirr.service.ClusterSpec;
import org.apache.whirr.service.ServiceFactory;
import org.apache.whirr.ClusterControllerFactory;
import org.apache.whirr.ClusterSpec;
import org.apache.whirr.util.KeyPair;
import org.junit.Test;

public class ClusterSpecCommandTest {
public class AbstractClusterSpecCommandTest {

@Test
public void testOverrides() throws Exception {
ClusterSpecCommand clusterSpecCommand = new ClusterSpecCommand("name",
"description", new ServiceFactory()) {
AbstractClusterSpecCommand clusterSpecCommand = new AbstractClusterSpecCommand("name",
"description", new ClusterControllerFactory()) {
@Override
public int run(InputStream in, PrintStream out, PrintStream err,
List<String> args) throws Exception {
return 0;
}
};


Map<String, File> keys = KeyPair.generateTemporaryFiles();
OptionSet optionSet = clusterSpecCommand.parser.parse(
"--service-name", "overridden-test-service",
"--config", "whirr-override-test.properties");
"--config", "whirr-override-test.properties",
"--private-key-file", keys.get("private").getAbsolutePath()
);
ClusterSpec clusterSpec = clusterSpecCommand.getClusterSpec(optionSet);
assertThat(clusterSpec.getServiceName(), is("overridden-test-service"));
assertThat(clusterSpec.getClusterName(), is("test-cluster"));
}


/**
* Ensure that an invalid service name uses the default (after logging a
* warning).
*/
public void testCreateServerWithInvalidClusterControllerName() throws Exception {
AbstractClusterSpecCommand clusterSpecCommand = new AbstractClusterSpecCommand("name",
"description", new ClusterControllerFactory()) {
@Override
public int run(InputStream in, PrintStream out, PrintStream err,
List<String> args) throws Exception {
return 0;
}
};

// following should not fail
clusterSpecCommand.createClusterController("bar");
}
}
Original file line number Diff line number Diff line change
@@ -25,17 +25,20 @@
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

import com.google.common.io.Files;
import com.google.inject.internal.Lists;
import com.google.common.collect.Lists;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.PrintStream;
import java.util.Collections;
import java.util.Map;

import org.apache.whirr.service.ClusterSpec;
import org.apache.whirr.service.Service;
import org.apache.whirr.service.ServiceFactory;
import org.apache.commons.configuration.Configuration;
import org.apache.commons.configuration.PropertiesConfiguration;
import org.apache.whirr.ClusterController;
import org.apache.whirr.ClusterControllerFactory;
import org.apache.whirr.ClusterSpec;
import org.apache.whirr.util.KeyPair;
import org.hamcrest.Matcher;
import org.junit.Before;
import org.junit.Test;
@@ -71,44 +74,41 @@ private Matcher<String> containsUsageString() {
@Test
public void testAllOptions() throws Exception {

ServiceFactory factory = mock(ServiceFactory.class);
Service service = mock(Service.class);
when(factory.create((String) any())).thenReturn(service);
ClusterControllerFactory factory = mock(ClusterControllerFactory.class);
ClusterController controller = mock(ClusterController.class);
when(factory.create((String) any())).thenReturn(controller);

DestroyClusterCommand command = new DestroyClusterCommand(factory);

File privateKeyFile = File.createTempFile("private", "key");
privateKeyFile.deleteOnExit();
Files.write("-----BEGIN RSA PRIVATE KEY-----".getBytes(),
privateKeyFile);

File publicKeyFile = File.createTempFile("public", "key");
publicKeyFile.deleteOnExit();
Files.write("ssh-rsa".getBytes(), publicKeyFile);
Map<String, File> keys = KeyPair.generateTemporaryFiles();

int rc = command.run(null, out, null, Lists.newArrayList(
"--instance-templates", "1 noop",
"--service-name", "test-service",
"--cluster-name", "test-cluster",
"--provider", "rackspace",
"--identity", "myusername", "--credential", "mypassword",
"--private-key-file", privateKeyFile.getAbsolutePath(),
"--public-key-file", publicKeyFile.getAbsolutePath()
"--private-key-file", keys.get("private").getAbsolutePath(),
"--version", "version-string"
));

assertThat(rc, is(0));

ClusterSpec expectedClusterSpec = new ClusterSpec();
Configuration conf = new PropertiesConfiguration();
conf.addProperty("whirr.version", "version-string");
conf.addProperty("whirr.instance-templates", "1 noop");

ClusterSpec expectedClusterSpec = ClusterSpec.withTemporaryKeys(conf);
expectedClusterSpec.setServiceName("test-service");
expectedClusterSpec.setProvider("rackspace");
expectedClusterSpec.setIdentity("myusername");
expectedClusterSpec.setCredential("mypassword");
expectedClusterSpec.setClusterName("test-cluster");
expectedClusterSpec.setPrivateKey(privateKeyFile);
expectedClusterSpec.setPublicKey(publicKeyFile);
expectedClusterSpec.setPrivateKey(keys.get("private"));
expectedClusterSpec.setPublicKey(keys.get("public"));

verify(factory).create("test-service");

verify(service).destroyCluster(expectedClusterSpec);
verify(controller).destroyCluster(expectedClusterSpec);

}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.apache.whirr.cli.command;

import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.is;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

import com.google.common.collect.Lists;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.PrintStream;
import java.util.Collections;
import java.util.Map;

import org.apache.whirr.ClusterController;
import org.apache.whirr.ClusterControllerFactory;
import org.apache.whirr.ClusterSpec;
import org.apache.whirr.util.KeyPair;
import org.junit.Before;
import org.junit.Test;

public class DestroyInstanceCommandTest {

private ByteArrayOutputStream outBytes;
private PrintStream out;

private ByteArrayOutputStream errBytes;
private PrintStream err;

@Before
public void setUp() {
outBytes = new ByteArrayOutputStream();
out = new PrintStream(outBytes);

errBytes = new ByteArrayOutputStream();
err = new PrintStream(errBytes);
}

@Test
public void testInstanceIdMandatory() throws Exception {
DestroyInstanceCommand command = new DestroyInstanceCommand();
int rc = command.run(null, out, err, Collections.<String>emptyList());
assertThat(rc, is(-1));

String errOutput = errBytes.toString();
assertThat(errOutput, containsString("You need to specify " +
"an instance ID."));
assertThat(errOutput, containsString("Usage: whirr destroy-instance" +
" --instance-id <ID> [OPTIONS]"));
}

@Test
public void testDestroyInstanceById() throws Exception {
ClusterControllerFactory factory = mock(ClusterControllerFactory.class);
ClusterController controller = mock(ClusterController.class);
when(factory.create((String) any())).thenReturn(controller);

DestroyInstanceCommand command = new DestroyInstanceCommand(factory);
Map<String, File> keys = KeyPair.generateTemporaryFiles();

int rc = command.run(null, out, null, Lists.newArrayList(
"--instance-templates", "1 noop",
"--instance-id", "region/instanceid",
"--service-name", "test-service",
"--cluster-name", "test-cluster",
"--provider", "rackspace",
"--identity", "myusername", "--credential", "mypassword",
"--private-key-file", keys.get("private").getAbsolutePath(),
"--version", "version-string"
));
assertThat(rc, is(0));

verify(controller).destroyInstance((ClusterSpec) any(),
eq("region/instanceid"));
}
}
Original file line number Diff line number Diff line change
@@ -18,7 +18,6 @@

package org.apache.whirr.cli.command;

import static org.apache.whirr.service.ClusterSpec.Property.INSTANCE_TEMPLATES;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.is;
import static org.junit.Assert.assertThat;
@@ -27,22 +26,24 @@
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import com.google.common.io.ByteStreams;
import com.google.common.io.Files;
import com.google.inject.internal.Lists;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.PrintStream;
import java.util.Collections;
import java.util.Map;

import org.apache.commons.configuration.Configuration;
import org.apache.commons.configuration.PropertiesConfiguration;
import org.apache.whirr.service.Cluster;
import org.apache.whirr.service.ClusterSpec;
import org.apache.whirr.service.Service;
import org.apache.whirr.service.ServiceFactory;
import org.apache.whirr.Cluster;
import org.apache.whirr.ClusterController;
import org.apache.whirr.ClusterControllerFactory;
import org.apache.whirr.ClusterSpec;
import org.apache.whirr.InstanceTemplate;
import org.apache.whirr.util.KeyPair;
import org.hamcrest.Matcher;
import org.junit.Before;
import org.junit.Test;
@@ -59,6 +60,7 @@ public class LaunchClusterCommandTest {
public void setUp() {
outBytes = new ByteArrayOutputStream();
out = new PrintStream(outBytes);

errBytes = new ByteArrayOutputStream();
err = new PrintStream(errBytes);
}
@@ -77,53 +79,97 @@ private Matcher<String> containsUsageString() {
@Test
public void testAllOptions() throws Exception {

ServiceFactory factory = mock(ServiceFactory.class);
Service service = mock(Service.class);
ClusterControllerFactory factory = mock(ClusterControllerFactory.class);
ClusterController controller = mock(ClusterController.class);
Cluster cluster = mock(Cluster.class);
when(factory.create((String) any())).thenReturn(service);
when(service.launchCluster((ClusterSpec) any())).thenReturn(cluster);
when(factory.create((String) any())).thenReturn(controller);
when(controller.launchCluster((ClusterSpec) any())).thenReturn(cluster);

LaunchClusterCommand command = new LaunchClusterCommand(factory);

File privateKeyFile = File.createTempFile("private", "key");
privateKeyFile.deleteOnExit();
Files.write("-----BEGIN RSA PRIVATE KEY-----".getBytes(),
privateKeyFile);

File publicKeyFile = File.createTempFile("public", "key");
publicKeyFile.deleteOnExit();
Files.write("ssh-rsa".getBytes(), publicKeyFile);
Map<String, File> keys = KeyPair.generateTemporaryFiles();

int rc = command.run(null, out, null, Lists.newArrayList(
"--service-name", "test-service",
"--cluster-name", "test-cluster",
"--instance-templates", "1 role1+role2,2 role3",
"--provider", "rackspace",
"--identity", "myusername", "--credential", "mypassword",
"--private-key-file", privateKeyFile.getAbsolutePath(),
"--public-key-file", publicKeyFile.getAbsolutePath()
"--private-key-file", keys.get("private").getAbsolutePath(),
"--version", "version-string"
));

assertThat(rc, is(0));

ClusterSpec expectedClusterSpec = new ClusterSpec();
Configuration conf = new PropertiesConfiguration();
conf.addProperty("whirr.version", "version-string");

ClusterSpec expectedClusterSpec = ClusterSpec.withTemporaryKeys(conf);
expectedClusterSpec.setInstanceTemplates(Lists.newArrayList(
new ClusterSpec.InstanceTemplate(1, Sets.newHashSet("role1", "role2")),
new ClusterSpec.InstanceTemplate(2, Sets.newHashSet("role3"))
new InstanceTemplate(1, ImmutableSet.of("role1", "role2")),
new InstanceTemplate(2, ImmutableSet.of("role3"))
));
expectedClusterSpec.setServiceName("test-service");
expectedClusterSpec.setProvider("rackspace");
expectedClusterSpec.setIdentity("myusername");
expectedClusterSpec.setCredential("mypassword");
expectedClusterSpec.setClusterName("test-cluster");
expectedClusterSpec.setPrivateKey(privateKeyFile);
expectedClusterSpec.setPublicKey(publicKeyFile);
expectedClusterSpec.setPrivateKey(keys.get("private"));
expectedClusterSpec.setPublicKey(keys.get("public"));

verify(factory).create("test-service");

verify(service).launchCluster(expectedClusterSpec);
verify(controller).launchCluster(expectedClusterSpec);

assertThat(outBytes.toString(), containsString("Started cluster of 0 instances"));

}

@Test
public void testMaxPercentFailure() throws Exception {

ClusterControllerFactory factory = mock(ClusterControllerFactory.class);
ClusterController controller = mock(ClusterController.class);
Cluster cluster = mock(Cluster.class);
when(factory.create((String) any())).thenReturn(controller);
when(controller.launchCluster((ClusterSpec) any())).thenReturn(cluster);

LaunchClusterCommand command = new LaunchClusterCommand(factory);
Map<String, File> keys = KeyPair.generateTemporaryFiles();

int rc = command.run(null, out, null, Lists.newArrayList(
"--service-name", "hadoop",
"--cluster-name", "test-cluster",
"--instance-templates", "1 jt+nn,3 dn+tt",
"--instance-templates-max-percent-failures", "60 dn+tt",
"--provider", "ec2",
"--identity", "myusername", "--credential", "mypassword",
"--private-key-file", keys.get("private").getAbsolutePath(),
"--version", "version-string"
));

assertThat(rc, is(0));

Configuration conf = new PropertiesConfiguration();
conf.addProperty("whirr.version", "version-string");
conf.addProperty("whirr.instance-templates-max-percent-failure", "60 dn+tt");

ClusterSpec expectedClusterSpec = ClusterSpec.withTemporaryKeys(conf);
expectedClusterSpec.setInstanceTemplates(Lists.newArrayList(
new InstanceTemplate(1, 1, Sets.newHashSet("jt", "nn")),
new InstanceTemplate(3, 2, Sets.newHashSet("dn", "tt"))
));
expectedClusterSpec.setServiceName("hadoop");
expectedClusterSpec.setProvider("ec2");
expectedClusterSpec.setIdentity("myusername");
expectedClusterSpec.setCredential("mypassword");
expectedClusterSpec.setClusterName("test-cluster");
expectedClusterSpec.setPrivateKey(keys.get("private"));
expectedClusterSpec.setPublicKey(keys.get("public"));

verify(factory).create("hadoop");

verify(controller).launchCluster(expectedClusterSpec);

assertThat(outBytes.toString(), containsString("Started cluster of 0 instances"));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.apache.whirr.cli.command;

import static com.google.common.base.Preconditions.checkArgument;
import static org.hamcrest.Matchers.is;
import static org.junit.Assert.assertThat;
import static org.mockito.Matchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

import com.google.common.collect.Lists;
import com.google.common.collect.Sets;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.PrintStream;
import java.util.Collections;
import java.util.Map;
import java.util.Set;

import org.apache.whirr.Cluster;
import org.apache.whirr.ClusterController;
import org.apache.whirr.ClusterControllerFactory;
import org.apache.whirr.ClusterSpec;
import org.apache.whirr.cli.MemoryClusterStateStore;
import org.apache.whirr.service.ClusterStateStore;
import org.apache.whirr.service.ClusterStateStoreFactory;
import org.apache.whirr.util.KeyPair;
import org.hamcrest.Matcher;
import org.jclouds.compute.domain.NodeMetadata;
import org.jclouds.compute.domain.NodeMetadataBuilder;
import org.jclouds.compute.domain.NodeState;
import org.jclouds.domain.Credentials;
import org.jclouds.domain.LocationBuilder;
import org.jclouds.domain.LocationScope;
import org.junit.Before;
import org.junit.Test;
import org.junit.internal.matchers.StringContains;

public class ListClusterCommandTest {

private ByteArrayOutputStream outBytes;
private PrintStream out;
private ByteArrayOutputStream errBytes;
private PrintStream err;

@Before
public void setUp() {
outBytes = new ByteArrayOutputStream();
out = new PrintStream(outBytes);
errBytes = new ByteArrayOutputStream();
err = new PrintStream(errBytes);
}

@Test
public void testInsufficientOptions() throws Exception {
ListClusterCommand command = new ListClusterCommand();
int rc = command.run(null, null, err, Collections.<String>emptyList());
assertThat(rc, is(-1));
assertThat(errBytes.toString(), containsUsageString());
}

private Matcher<String> containsUsageString() {
return StringContains.containsString("Usage: whirr list-cluster [OPTIONS]");
}

@Test
public void testAllOptions() throws Exception {

ClusterControllerFactory factory = mock(ClusterControllerFactory.class);
ClusterController controller = mock(ClusterController.class);
when(factory.create((String) any())).thenReturn(controller);

NodeMetadata node1 = new NodeMetadataBuilder().name("name1").ids("id1")
.location(new LocationBuilder().scope(LocationScope.PROVIDER)
.id("location-id1").description("location-desc1").build())
.imageId("image-id").state(NodeState.RUNNING)
.publicAddresses(Lists.newArrayList("127.0.0.1"))
.privateAddresses(Lists.newArrayList("127.0.0.1")).build();

NodeMetadata node2 = new NodeMetadataBuilder().name("name2").ids("id2")
.location(new LocationBuilder().scope(LocationScope.PROVIDER)
.id("location-id2").description("location-desc2").build())
.imageId("image-id").state(NodeState.RUNNING)
.publicAddresses(Lists.newArrayList("127.0.0.2"))
.privateAddresses(Lists.newArrayList("127.0.0.2")).build();

when(controller.getNodes((ClusterSpec) any())).thenReturn(
(Set) Sets.newLinkedHashSet(Lists.newArrayList(node1, node2)));
when(controller.getInstances((ClusterSpec)any(), (ClusterStateStore)any()))
.thenCallRealMethod();

ClusterStateStore memStore = new MemoryClusterStateStore();
memStore.save(createTestCluster(
new String[]{"id1", "id2"}, new String[]{"role1", "role2"}));

ClusterStateStoreFactory stateStoreFactory = mock(ClusterStateStoreFactory.class);
when(stateStoreFactory.create((ClusterSpec) any())).thenReturn(memStore);

ListClusterCommand command = new ListClusterCommand(factory, stateStoreFactory);

Map<String, File> keys = KeyPair.generateTemporaryFiles();
int rc = command.run(null, out, null, Lists.newArrayList(
"--instance-templates", "1 noop",
"--service-name", "test-service",
"--cluster-name", "test-cluster",
"--identity", "myusername",
"--private-key-file", keys.get("private").getAbsolutePath())
);

assertThat(rc, is(0));

assertThat(outBytes.toString(), is(
"id1\timage-id\t127.0.0.1\t127.0.0.1\tRUNNING\tlocation-id1\trole1\n" +
"id2\timage-id\t127.0.0.2\t127.0.0.2\tRUNNING\tlocation-id2\trole2\n"));

verify(factory).create("test-service");

}

private Cluster createTestCluster(String[] ids, String[] roles) {
checkArgument(ids.length == roles.length, "each ID should have a role");

Credentials credentials = new Credentials("dummy", "dummy");
Set<Cluster.Instance> instances = Sets.newHashSet();

for(int i = 0; i < ids.length; i++) {
String ip = "127.0.0." + (i + 1);
instances.add(new Cluster.Instance(credentials,
Sets.newHashSet(roles[i]), ip, ip, ids[i], null));
}

return new Cluster(instances);
}


}
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.apache.whirr.cli.command;

import com.google.common.base.Predicate;
import com.google.common.base.Predicates;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import org.apache.whirr.Cluster;
import org.apache.whirr.ClusterController;
import org.apache.whirr.ClusterControllerFactory;
import org.apache.whirr.ClusterSpec;
import org.apache.whirr.cli.MemoryClusterStateStore;
import org.apache.whirr.service.ClusterStateStore;
import org.apache.whirr.service.ClusterStateStoreFactory;
import org.apache.whirr.util.KeyPair;
import org.jclouds.compute.domain.NodeMetadata;
import org.jclouds.domain.Credentials;
import org.jclouds.scriptbuilder.domain.Statement;
import org.junit.Before;
import org.junit.Test;
import org.mockito.ArgumentCaptor;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.PrintStream;
import java.util.Map;
import java.util.Set;

import static com.google.common.base.Preconditions.checkArgument;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.is;
import static org.jclouds.compute.predicates.NodePredicates.withIds;
import static org.mockito.Matchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

public class RunScriptCommandTest {

private ByteArrayOutputStream outBytes;
private PrintStream out;

private ByteArrayOutputStream errBytes;
private PrintStream err;

@Before
public void setUp() {
outBytes = new ByteArrayOutputStream();
out = new PrintStream(outBytes);

errBytes = new ByteArrayOutputStream();
err = new PrintStream(errBytes);
}

@Test
public void testScriptPathIsMandatory() throws Exception {
RunScriptCommand command = new RunScriptCommand();

int rc = command.run(null, out, err, Lists.<String>newArrayList());
assertThat(rc, is(-1));

assertThat(errBytes.toString(),
containsString("Please specify a script file to be executed."));
}

@Test
public void testRunScriptByInstanceId() throws Exception {
ClusterControllerFactory factory = mock(ClusterControllerFactory.class);
ClusterController controller = mock(ClusterController.class);

when(factory.create((String)any())).thenReturn(controller);

RunScriptCommand command = new RunScriptCommand(factory);
Map<String, File> keys = KeyPair.generateTemporaryFiles();

int rc = command.run(null, out, System.err, Lists.newArrayList(
"--script", "/dev/null",
"--instance-templates", "1 noop",
"--instances", "A,B",
"--cluster-name", "test-cluster",
"--provider", "provider",
"--identity", "myusername", "--credential", "mypassword",
"--private-key-file", keys.get("private").getAbsolutePath()
));
assertThat(rc, is(0));

ArgumentCaptor<Predicate> predicate = ArgumentCaptor.forClass(Predicate.class);
verify(controller).runScriptOnNodesMatching(
(ClusterSpec)any(), predicate.capture(), (Statement) any());

// check predicate equality by using the object string representation

Predicate<NodeMetadata> expected = Predicates.and(
Predicates.<NodeMetadata>alwaysTrue(), withIds("A", "B"));
assertThat(predicate.getValue().toString(), is(expected.toString()));
}

@Test
public void testRunScriptByRole() throws Exception {
ClusterControllerFactory factory = mock(ClusterControllerFactory.class);
ClusterController controller = mock(ClusterController.class);
when(factory.create((String)any())).thenReturn(controller);

ClusterStateStore memStore = new MemoryClusterStateStore();
memStore.save(createTestCluster(
new String[]{"reg/A", "reg/B"}, new String[]{"A", "B"}));

ClusterStateStoreFactory stateStoreFactory = mock(ClusterStateStoreFactory.class);
when(stateStoreFactory.create((ClusterSpec) any())).thenReturn(memStore);

RunScriptCommand command = new RunScriptCommand(factory, stateStoreFactory);
Map<String, File> keys = KeyPair.generateTemporaryFiles();

int rc = command.run(null, out, System.err, Lists.newArrayList(
"--instance-templates", "1 noop",
"--script", "/dev/null",
"--roles", "A",
"--cluster-name", "test-cluster",
"--provider", "provider",
"--identity", "myusername", "--credential", "mypassword",
"--private-key-file", keys.get("private").getAbsolutePath()
));
assertThat(rc, is(0));

ArgumentCaptor<Predicate> predicate = ArgumentCaptor.forClass(Predicate.class);
verify(controller).runScriptOnNodesMatching(
(ClusterSpec)any(), predicate.capture(), (Statement) any());

// check predicate equality by using the object string representation

Predicate<NodeMetadata> expected = Predicates.and(
Predicates.<NodeMetadata>alwaysTrue(), withIds("A"));
assertThat(predicate.getValue().toString(), is(expected.toString()));

}

private Cluster createTestCluster(String[] ids, String[] roles) {
checkArgument(ids.length == roles.length, "each ID should have a role");

Credentials credentials = new Credentials("dummy", "dummy");
Set<Cluster.Instance> instances = Sets.newHashSet();

for(int i = 0; i < ids.length; i++) {
String ip = "127.0.0." + (i + 1);
instances.add(new Cluster.Instance(credentials,
Sets.newHashSet(roles[i]), ip, ip, ids[i], null));
}

return new Cluster(instances);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.apache.whirr.cli.command;

import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.startsWith;
import static org.junit.Assert.assertThat;

import java.io.ByteArrayOutputStream;
import java.io.PrintStream;
import java.util.Collections;

import org.junit.Before;
import org.junit.Test;

public class VersionCommandTest {

private ByteArrayOutputStream outBytes;
private PrintStream out;

@Before
public void setUp() {
outBytes = new ByteArrayOutputStream();
out = new PrintStream(outBytes);
}

@Test
public void testOverrides() throws Exception {
VersionCommand command = new VersionCommand();
int rc = command.run(null, out, null, Collections.<String>emptyList());
assertThat(rc, is(0));
assertThat(outBytes.toString(), startsWith("Apache Whirr "));
}

}
1 change: 1 addition & 0 deletions cli/src/test/resources/whirr-override-test.properties
Original file line number Diff line number Diff line change
@@ -19,3 +19,4 @@ whirr.service-name=test-service
whirr.cluster-name=test-cluster
whirr.provider=test-provider
whirr.identity=test-identity
whirr.instance-templates=1 noop
49 changes: 49 additions & 0 deletions contrib/scripts/ycsb_quick_test.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
#!/bin/bash
#
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# Based on http://blog.lars-francke.de/2010/08/16/performance-testing-hbase-using-ycsb/
#

set -x -e

sudo rm -rf /tmp/ycsb
sudo mkdir -p /tmp/ycsb
cd /tmp/ycsb

sudo curl -s -O http://people.apache.org/~asavu/ycsb-0.1.3.tar.gz
sudo tar xfz ycsb-0.1.3.tar.gz

cd /usr/local/hbase-*
sudo ./bin/hbase shell <<EOF
disable 'usertable'
drop 'usertable'
create 'usertable', 'family'
exit
EOF

cd /tmp/ycsb/ycsb-0.1.3/

sudo java -cp build/ycsb.jar:db/hbase/lib/* com.yahoo.ycsb.Client -load -db com.yahoo.ycsb.db.HBaseClient -P workloads/workloada -p columnfamily=family -p recordcount=2000 -s > load.dat

sudo java -cp build/ycsb.jar:db/hbase/lib/* com.yahoo.ycsb.Client -t -db com.yahoo.ycsb.db.HBaseClient -P workloads/workloada -p columnfamily=family -p operationcount=2000 -s -threads 10 -target 100 > transactions.dat

echo "*** Data loading stats"
sudo head -n 11 load.dat

echo "*** Transactions stats"
sudo head -n 11 transactions.dat

78 changes: 69 additions & 9 deletions core/pom.xml
Original file line number Diff line number Diff line change
@@ -20,18 +20,18 @@
<parent>
<groupId>org.apache.whirr</groupId>
<artifactId>whirr</artifactId>
<version>0.1.0-incubating-SNAPSHOT</version>
<version>0.5.0-incubating</version>
<relativePath>../pom.xml</relativePath>
</parent>
<groupId>org.apache.whirr</groupId>
<artifactId>whirr-core</artifactId>
<packaging>jar</packaging>
<version>0.1.0-incubating-SNAPSHOT</version>
<version>0.5.0-incubating</version>
<name>Apache Whirr Core</name>
<dependencies>
<dependency>
<groupId>org.jclouds</groupId>
<artifactId>jclouds-aws</artifactId>
<groupId>ca.juliusdavies</groupId>
<artifactId>not-yet-commons-ssl</artifactId>
</dependency>
<dependency>
<groupId>org.jclouds</groupId>
@@ -42,25 +42,41 @@
<artifactId>jclouds-core</artifactId>
</dependency>
<dependency>
<groupId>org.jclouds</groupId>
<artifactId>jclouds-enterprise</artifactId>
<groupId>org.jclouds.api</groupId>
<artifactId>byon</artifactId>
</dependency>
<dependency>
<groupId>org.jclouds</groupId>
<groupId>org.jclouds.driver</groupId>
<artifactId>jclouds-jsch</artifactId>
</dependency>
<dependency>
<groupId>org.jclouds</groupId>
<groupId>org.jclouds.driver</groupId>
<artifactId>jclouds-log4j</artifactId>
</dependency>
<dependency>
<groupId>org.jclouds.provider</groupId>
<artifactId>aws-ec2</artifactId>
</dependency>
<dependency>
<groupId>org.jclouds.provider</groupId>
<artifactId>cloudservers-us</artifactId>
</dependency>
<dependency>
<groupId>org.jclouds.provider</groupId>
<artifactId>cloudservers-uk</artifactId>
</dependency>
<dependency>
<groupId>org.jclouds</groupId>
<artifactId>jclouds-scriptbuilder</artifactId>
<artifactId>jclouds-allblobstore</artifactId>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-all</artifactId>
</dependency>
<dependency>
<groupId>org.hamcrest</groupId>
<artifactId>hamcrest-all</artifactId>
@@ -73,13 +89,57 @@
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
</dependency>
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.jcraft</groupId>
<artifactId>jsch</artifactId>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpcore</artifactId>
<version>4.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpmime</artifactId>
<version>4.1.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.1.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>dnsjava</groupId>
<artifactId>dnsjava</artifactId>
</dependency>
</dependencies>
<build>
<resources>
<resource>
<directory>${project.basedir}/src/main/resources</directory>
<filtering>true</filtering>
</resource>
</resources>
</build>
</project>
80 changes: 80 additions & 0 deletions core/src/main/java/org/apache/whirr/ByonClusterController.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.apache.whirr;

import static org.apache.whirr.service.ClusterActionHandler.BOOTSTRAP_ACTION;
import static org.apache.whirr.service.ClusterActionHandler.CONFIGURE_ACTION;
import static org.apache.whirr.service.ClusterActionHandler.DESTROY_ACTION;

import java.io.IOException;
import java.util.Map;
import java.util.Set;

import org.apache.whirr.actions.ByonClusterAction;
import org.apache.whirr.service.ClusterActionHandler;
import org.jclouds.compute.domain.NodeMetadata;

/**
* Equivalent of {@link ClusterController}, but for execution in BYON mode
* ("bring your own nodes").
*/
public class ByonClusterController extends ClusterController {

@Override
public String getName() {
return "byon";
}

public Cluster launchCluster(ClusterSpec clusterSpec) throws IOException,
InterruptedException {

Map<String, ClusterActionHandler> handlerMap = new HandlerMapFactory()
.create();

ClusterAction bootstrapper = new ByonClusterAction(BOOTSTRAP_ACTION, getCompute(), handlerMap);
Cluster cluster = bootstrapper.execute(clusterSpec, null);

ClusterAction configurer = new ByonClusterAction(CONFIGURE_ACTION, getCompute(), handlerMap);
cluster = configurer.execute(clusterSpec, cluster);

return cluster;
}

public void destroyCluster(ClusterSpec clusterSpec) throws IOException,
InterruptedException {
Map<String, ClusterActionHandler> handlerMap = new HandlerMapFactory()
.create();

ClusterAction destroyer = new ByonClusterAction(DESTROY_ACTION, getCompute(), handlerMap);
destroyer.execute(clusterSpec, null);
}

@Override
public void destroyInstance(ClusterSpec clusterSpec, String instanceId)
throws IOException {
// TODO
}

@Override
public Set<? extends NodeMetadata> getNodes(ClusterSpec clusterSpec)
throws IOException, InterruptedException {
// TODO return singleton with trivial NodeMetadata for localhost?
return null;
}
}
175 changes: 175 additions & 0 deletions core/src/main/java/org/apache/whirr/Cluster.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,175 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.apache.whirr;

import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;

import com.google.common.base.Objects;
import com.google.common.base.Predicate;
import com.google.common.base.Predicates;
import com.google.common.collect.Iterables;
import com.google.common.collect.Sets;
import com.google.common.net.InetAddresses;

import java.io.IOException;
import java.net.InetAddress;
import java.util.Properties;
import java.util.Set;

import org.apache.whirr.util.DnsUtil;
import org.jclouds.compute.domain.NodeMetadata;
import org.jclouds.domain.Credentials;

/**
* This class represents a real cluster of {@link Instance}s.
*
*/
public class Cluster {

/**
* This class represents a real node running in a cluster. An instance has
* one or more roles.
* @see InstanceTemplate
*/
public static class Instance {
private final Credentials loginCredentials;
private final Set<String> roles;
private final String publicIp;
private String publicHostName;
private final String privateIp;
private String privateHostName;
private final String id;
private final NodeMetadata nodeMetadata;

public Instance(Credentials loginCredentials, Set<String> roles, String publicIp,
String privateIp, String id, NodeMetadata nodeMetadata) {
this.loginCredentials = checkNotNull(loginCredentials, "loginCredentials");
this.roles = checkNotNull(roles, "roles");
this.publicIp = checkNotNull(publicIp, "publicIp");
checkArgument(InetAddresses.isInetAddress(publicIp),
"invalid IP address: %s", publicIp);
this.privateIp = checkNotNull(privateIp, "privateIp");
checkArgument(InetAddresses.isInetAddress(privateIp),
"invalid IP address: %s", privateIp);
this.id = checkNotNull(id, "id");
this.nodeMetadata = nodeMetadata;
}

public Credentials getLoginCredentials() {
return loginCredentials;
}

public Set<String> getRoles() {
return roles;
}

public InetAddress getPublicAddress() throws IOException {
return resolveIpAddress(getPublicIp(), getPublicHostName());
}

public InetAddress getPrivateAddress() throws IOException {
return resolveIpAddress(getPrivateIp(), getPrivateHostName());
}

private InetAddress resolveIpAddress(String ip, String host) throws IOException {
byte[] addr = InetAddresses.forString(ip).getAddress();
return InetAddress.getByAddress(host, addr);
}

public String getPublicIp() {
return publicIp;
}

public synchronized String getPublicHostName() throws IOException {
if (publicHostName == null) {
publicHostName = DnsUtil.resolveAddress(publicIp);
}
return publicHostName;
}

public String getPrivateIp() {
return privateIp;
}

public synchronized String getPrivateHostName() throws IOException {
if (privateHostName == null) {
privateHostName = DnsUtil.resolveAddress(privateIp);
}
return privateHostName;
}

public String getId() {
return id;
}

public NodeMetadata getNodeMetadata() {
return nodeMetadata;
}

public String toString() {
return Objects.toStringHelper(this)
.add("roles", roles)
.add("publicIp", publicIp)
.add("privateIp", privateIp)
.add("id", id)
.add("nodeMetadata", nodeMetadata)
.toString();
}
}

private Set<Instance> instances;
private Properties configuration;

public Cluster(Set<Instance> instances) {
this(instances, new Properties());
}

public Cluster(Set<Instance> instances, Properties configuration) {
this.instances = instances;
this.configuration = configuration;
}

public Set<Instance> getInstances() {
return instances;
}
public Properties getConfiguration() {
return configuration;
}

public Instance getInstanceMatching(Predicate<Instance> predicate) {
return Iterables.getOnlyElement(Sets.filter(instances, predicate));
}

public Set<Instance> getInstancesMatching(Predicate<Instance> predicate) {
return Sets.filter(instances, predicate);
}

public void removeInstancesMatching(Predicate<Instance> predicate) {
instances = Sets.filter(instances, Predicates.not(predicate));
}

public String toString() {
return Objects.toStringHelper(this)
.add("instances", instances)
.add("configuration", configuration)
.toString();
}

}
Original file line number Diff line number Diff line change
@@ -16,32 +16,34 @@
* limitations under the License.
*/

package org.apache.whirr.service;

import com.google.common.collect.ImmutableSet;
import com.google.inject.AbstractModule;
package org.apache.whirr;

import java.io.IOException;
import java.util.Set;

import org.jclouds.compute.ComputeServiceContext;
import org.jclouds.compute.ComputeServiceContextFactory;
import org.jclouds.logging.log4j.config.Log4JLoggingModule;
import org.jclouds.ssh.jsch.config.JschSshClientModule;

import com.google.common.base.Function;

/**
* A convenience class for building jclouds {@link ComputeServiceContext} objects.
* Performs an action on a cluster. Example actions include bootstrapping
* (launching, creating), configuring, or running an arbitrary command on the
* cluster.
*/
public class ComputeServiceContextBuilder {

public static ComputeServiceContext build(ClusterSpec spec) throws IOException {
Set<AbstractModule> wiring = ImmutableSet.of(new JschSshClientModule(),
new Log4JLoggingModule());
public abstract class ClusterAction {

private final Function<ClusterSpec, ComputeServiceContext> getCompute;

ComputeServiceContext context = new ComputeServiceContextFactory()
.createContext(spec.getProvider(), spec.getIdentity(), spec.getCredential(),
wiring);

return context;
protected ClusterAction(final Function<ClusterSpec, ComputeServiceContext> getCompute) {
this.getCompute = getCompute;
}

protected Function<ClusterSpec, ComputeServiceContext> getCompute() {
return getCompute;
}

protected abstract String getAction();

public abstract Cluster execute(ClusterSpec clusterSpec, Cluster cluster)
throws IOException, InterruptedException;

}
220 changes: 220 additions & 0 deletions core/src/main/java/org/apache/whirr/ClusterController.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,220 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.apache.whirr;

import com.google.common.base.Function;
import com.google.common.base.Predicate;
import com.google.common.base.Predicates;

import java.io.IOException;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Set;

import com.google.common.collect.Iterables;
import com.google.common.collect.Sets;
import org.apache.whirr.actions.BootstrapClusterAction;
import org.apache.whirr.actions.ConfigureClusterAction;
import org.apache.whirr.actions.DestroyClusterAction;
import org.apache.whirr.service.ClusterStateStore;
import org.apache.whirr.service.ClusterStateStoreFactory;
import org.apache.whirr.service.ClusterActionHandler;
import org.apache.whirr.service.ComputeCache;
import org.jclouds.compute.ComputeService;
import org.jclouds.compute.ComputeServiceContext;
import org.jclouds.compute.RunScriptOnNodesException;
import org.jclouds.compute.domain.ComputeMetadata;
import org.jclouds.compute.domain.ExecResponse;
import org.jclouds.compute.domain.NodeMetadata;
import org.jclouds.compute.domain.NodeState;
import org.jclouds.domain.Credentials;
import org.jclouds.scriptbuilder.domain.Statement;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import static org.apache.whirr.RolePredicates.withIds;
import static org.jclouds.compute.options.RunScriptOptions.Builder.overrideCredentialsWith;

/**
* This class is used to start and stop clusters.
*/
public class ClusterController {

private static final Logger LOG = LoggerFactory.getLogger(ClusterController.class);

private final Function<ClusterSpec, ComputeServiceContext> getCompute;
private final ClusterStateStoreFactory stateStoreFactory;


public ClusterController() {
this(ComputeCache.INSTANCE, new ClusterStateStoreFactory());
}

public ClusterController(Function<ClusterSpec, ComputeServiceContext> getCompute,
ClusterStateStoreFactory stateStoreFactory) {
this.getCompute = getCompute;
this.stateStoreFactory = stateStoreFactory;
}

/**
* @return the unique name of the service.
*/
public String getName() {
throw new UnsupportedOperationException("No service name");
}

/**
* @return compute service contexts for use in managing the service
*/
protected Function<ClusterSpec, ComputeServiceContext> getCompute() {
return getCompute;
}

/**
* Start the cluster described by <code>clusterSpec</code> and block until the
* cluster is
* available. It is not guaranteed that the service running on the cluster
* has started when this method returns.
* @param clusterSpec
* @return an object representing the running cluster
* @throws IOException if there is a problem while starting the cluster. The
* cluster may or may not have started.
* @throws InterruptedException if the thread is interrupted.
*/
public Cluster launchCluster(ClusterSpec clusterSpec)
throws IOException, InterruptedException {

Map<String, ClusterActionHandler> handlerMap = new HandlerMapFactory().create();

BootstrapClusterAction bootstrapper = new BootstrapClusterAction(getCompute(), handlerMap);
Cluster cluster = bootstrapper.execute(clusterSpec, null);

ConfigureClusterAction configurer = new ConfigureClusterAction(getCompute(), handlerMap);
cluster = configurer.execute(clusterSpec, cluster);

stateStoreFactory.create(clusterSpec).save(cluster);

return cluster;
}


/**
* Stop the cluster and destroy all resources associated with it.
*
* @throws IOException if there is a problem while stopping the cluster. The
* cluster may or may not have been stopped.
* @throws InterruptedException if the thread is interrupted.
*/
public void destroyCluster(ClusterSpec clusterSpec) throws IOException,
InterruptedException {
DestroyClusterAction destroyer = new DestroyClusterAction(getCompute());
destroyer.execute(clusterSpec, null);

stateStoreFactory.create(clusterSpec).destroy();
}

public void destroyInstance(ClusterSpec clusterSpec, String instanceId) throws IOException {
LOG.info("Destroying instance {}", instanceId);

/* Destroy the instance */
ComputeService computeService = getCompute().apply(clusterSpec).getComputeService();
computeService.destroyNode(instanceId);

/* .. and update the cluster state storage */
ClusterStateStore store = stateStoreFactory.create(clusterSpec);
Cluster cluster = store.load();
cluster.removeInstancesMatching(withIds(instanceId));
store.save(cluster);

LOG.info("Instance {} destroyed", instanceId);
}

public Map<? extends NodeMetadata, ExecResponse> runScriptOnNodesMatching(ClusterSpec spec,
Predicate<NodeMetadata> condition, Statement statement) throws IOException, RunScriptOnNodesException {

Credentials credentials = new Credentials(spec.getClusterUser(), spec.getPrivateKey());
ComputeServiceContext context = getCompute().apply(spec);

condition = Predicates.and(runningInGroup(spec.getClusterName()), condition);
return context.getComputeService().runScriptOnNodesMatching(condition,
statement, overrideCredentialsWith(credentials).wrapInInitScript(false).runAsRoot(false));
}

@Deprecated
public Set<? extends NodeMetadata> getNodes(ClusterSpec clusterSpec)
throws IOException, InterruptedException {
ComputeService computeService = getCompute().apply(clusterSpec).getComputeService();
return computeService.listNodesDetailsMatching(
runningInGroup(clusterSpec.getClusterName()));
}

public Set<Cluster.Instance> getInstances(ClusterSpec spec)
throws IOException, InterruptedException {
return getInstances(spec, null);
}

public Set<Cluster.Instance> getInstances(ClusterSpec spec, ClusterStateStore stateStore)
throws IOException, InterruptedException {

Set<Cluster.Instance> instances = Sets.newLinkedHashSet();
Cluster cluster = (stateStore != null) ? stateStore.load() : null;

for(NodeMetadata node : getNodes(spec)) {
instances.add(toInstance(node, cluster, spec));
}

return instances;
}

private Cluster.Instance toInstance(NodeMetadata metadata, Cluster cluster, ClusterSpec spec) {
Credentials credentials = new Credentials(spec.getClusterUser(), spec.getPrivateKey());

Set<String> roles = Sets.newHashSet();
try {
if (cluster != null) {
roles = cluster.getInstanceMatching(withIds(metadata.getId())).getRoles();
}
} catch(NoSuchElementException e) {}

return new Cluster.Instance(credentials, roles,
Iterables.getFirst(metadata.getPublicAddresses(), null),
Iterables.getFirst(metadata.getPrivateAddresses(), null),
metadata.getId(), metadata);
}

public static Predicate<ComputeMetadata> runningInGroup(final String group) {
return new Predicate<ComputeMetadata>() {
@Override
public boolean apply(ComputeMetadata computeMetadata) {
// Not all list calls return NodeMetadata (e.g. VCloud)
if (computeMetadata instanceof NodeMetadata) {
NodeMetadata nodeMetadata = (NodeMetadata) computeMetadata;
return group.equals(nodeMetadata.getGroup())
&& nodeMetadata.getState() == NodeState.RUNNING;
}
return false;
}
@Override
public String toString() {
return "runningInGroup(" + group + ")";
}
};
}

}
66 changes: 66 additions & 0 deletions core/src/main/java/org/apache/whirr/ClusterControllerFactory.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.apache.whirr;

import com.google.common.collect.Sets;

import java.util.ServiceLoader;
import java.util.Set;

/**
* This class is used to create {@link ClusterController} instances.
* <p>
* <i>Implementation note.</i> {@link ClusterController} implementations are discovered
* using a Service Provider
* Interface (SPI), described in {@link ServiceLoader}.
*/
public class ClusterControllerFactory {

private ServiceLoader<ClusterController> serviceLoader =
ServiceLoader.load(ClusterController.class);

/**
* Create an instance of a {@link ClusterController} according to the given
* name. If the name is <code>null</code> then the default {@link ClusterController}
* is returned.
*/
public ClusterController create(String serviceName) {
if (serviceName == null) {
return new ClusterController();
}
for (ClusterController controller : serviceLoader) {
if (controller.getName().equals(serviceName)) {
return controller;
}
}
return null;
}

/**
* Return a collection of available {@link ClusterController} names.
* @return the available service names
*/
public Set<String> availableServices() {
Set<String> result = Sets.newLinkedHashSet();
for (ClusterController s : serviceLoader) {
result.add(s.getName());
}
return result;
}
}
Loading