Issue 840:remove compute.clj and blobstore.clj

This commit is contained in:
Adrian Cole 2012-02-15 14:55:26 +01:00
parent 44db3e0d57
commit ed7b8895e6
6 changed files with 0 additions and 1679 deletions

View File

@ -1,319 +0,0 @@
;
; Licensed to jclouds, Inc. (jclouds) under one or more
; contributor license agreements. See the NOTICE file
; distributed with this work for additional information
; regarding copyright ownership. jclouds 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.
;
(ns
#^{:author "Chas Emerick, cemerick@snowtide.com"
:doc "A clojure binding to the jclouds EBS service interface."}
org.jclouds.ec2.ebs
(:use [clojure.core.incubator :only (-?>)])
(:require (org.jclouds [compute :as compute]))
(:import org.jclouds.aws.domain.Region
org.jclouds.compute.domain.NodeMetadata
(org.jclouds.ec2.domain Volume Volume$Status Snapshot Snapshot$Status AvailabilityZoneInfo)
(org.jclouds.ec2.options DescribeSnapshotsOptions DetachVolumeOptions CreateSnapshotOptions)))
(defn snapshot?
"Returns true iff the argument is a org.jclouds.ec2.domain.Snapshot."
[s]
(instance? Snapshot s))
(defn volume?
"Returns true iff the argument is a org.jclouds.ec2.domain.Volume."
[v]
(instance? Volume v))
(defn #^org.jclouds.ec2.services.ElasticBlockStoreClient
ebs-service
"Returns the synchronous ElasticBlockStoreClient associated with
the specified compute service, or compute/*compute* as bound by with-compute-service."
[& [compute]]
(-> (or compute compute/*compute*)
.getContext .getProviderSpecificContext .getApi .getElasticBlockStoreServices))
(defn get-region
"Coerces the first parameter into a Region string; strings, keywords, and
NodeMetadata instances are acceptable arguments. An optional second argument
is returned if the first cannot be coerced into a region string.
Returns nil otherwise."
([v] (get-region v nil))
([v default-region]
(cond
(string? v) v
(keyword? v) (name v)
(instance? NodeMetadata v) (let [zone (compute/location v)]
; no easier way to go from zone -> region?
(if (> (.indexOf zone "-") -1)
(subs zone 0 (-> zone count dec))
zone))
:else default-region)))
(defn get-volume-id
"Returns a string volume ID taken from the given string, keyword, or Volume argument."
[v]
(cond
(instance? Volume v) (.getId #^Volume v)
(keyword? v) (name v)
(string? v) v
:else (throw (IllegalArgumentException.
(str "Can't obtain volume id from argument of type " (class v))))))
(defn volumes
"Returns a set of org.jclouds.ec2.domain.Volume instances corresponding to the
volumes in the specified region (defaulting to your account's default region).
e.g. (with-compute-service [compute] (volumes))
(with-compute-service [compute] (volumes :us-east-1 \"vol-6b218805\" ...))"
[& [region & volume-ids]]
(set
(.describeVolumesInRegion (ebs-service)
(get-region region)
(into-array String (map get-volume-id
(if (get-region region)
volume-ids
(when region (cons region volume-ids))))))))
(defn- as-string
[v]
(cond
(string? v) v
(keyword? v) (name v)
:else v))
(defn- get-string
[map key]
(as-string (get map key)))
(defn- as-int
[v]
(cond
(number? v) (int v)
(string? v) (Integer/parseInt v)
:else (throw (IllegalArgumentException.
(str "Don't know how to convert object of type " (class v) " to a string")))))
(defn- snapshot-options
[optmap]
(let [string-array #(let [v (% optmap)]
(into-array String (cond
(keyword? v) [(name v)]
(string? v) [v]
:else (map as-string v))))]
(-> (DescribeSnapshotsOptions.)
(.ownedBy (string-array :owner))
(.snapshotIds (string-array :ids))
(.restorableBy (string-array :restorable-by)))))
(defn snapshots
"Returns a set of org.jclouds.aws.ec2.domain.Snapshot instances that match
the criteria provided. Options include:
:region - region string, keyword, or NodeMetadata
:owner - AWS account id (or \"amazon\" or \"self\")
:restorable-by - AWS account id
Multiple values for each type of criteria can be provided by passing a seq
of the appropriate types as values.
(with-compute-service [compute]
(snapshots :owner \"self\")
(snapshots :region :us-west-1 :ids [\"snap-44b3ab2d\" \"snap-9e8821f7\"]))"
[& options]
(let [options (apply hash-map options)
region (:region options)
options (snapshot-options (dissoc options :region))]
(set
(.describeSnapshotsInRegion (ebs-service)
(get-region region)
(into-array DescribeSnapshotsOptions [options])))))
(defn create-snapshot
"Creates a snapshot of a volume in the specified region with an optional description.
If provided, the description must be < 255 characters in length. Returns the
org.jclouds.aws.ec2.domain.Snapshot object representing the created snapshot.
e.g. (with-compute-service [compute]
(create-snapshot some-volume-instance)
(create-snapshot :us-east-1 \"vol-1dbe6785\" nil)
(create-snapshot :us-east-1 \"vol-1dbe6785\" \"super-important data\"))"
([#^Volume volume] (create-snapshot volume nil))
([#^Volume volume description] (create-snapshot (.getRegion volume) (.getId volume) description))
([region volume-id description]
(.createSnapshotInRegion (ebs-service)
(get-region region)
(as-string volume-id)
(into-array CreateSnapshotOptions (when description
[(.withDescription (CreateSnapshotOptions.) description)])))))
(defn delete-snapshot
"Deletes a snapshot in the specified region.
e.g. (with-compute-service [compute]
(delete-snapshot :us-east-1 :snap-252310af)
(delete-snapshot :us-east-1 \"snap-242adf03\"))"
([#^Snapshot snapshot] (delete-snapshot (.getRegion snapshot) (.getId snapshot)))
([region snapshot-id]
(.deleteSnapshotInRegion (ebs-service)
(get-region region)
(as-string snapshot-id))))
(defn get-zone
[v]
(cond
(instance? AvailabilityZoneInfo v) (.getZone v)
(instance? NodeMetadata v) (compute/location #^NodeMetadata v)
(string? v) v
(keyword? v) (name v)
:else (throw (IllegalArgumentException.
(str "Can't obtain zone from argument of type " (class v))))))
(defn attach-volume
"Attaches a volume to an instance, returning the resulting org.jclouds.aws.ec2.domain.Attachment.
e.g. (with-compute-service [compute]
(attach-volume :us-east-1 \"i-a92358c1\" :vol-45228a6d \"/dev/sdh\")
(attach-volume some-node-instance :vol-45228a6d \"/dev/sdh\")
(attach-volume some-node-instance some-volume-instance \"/dev/sdh\"))"
([#^NodeMetadata node volume device]
(attach-volume node (.getProviderId node) (get-volume-id volume) device))
([region instance-id volume-id device]
(apply #(.attachVolumeInRegion (ebs-service)
(get-region region) % %2 %3)
(map as-string [volume-id instance-id device]))))
(defn detach-volume
"Detatches a volume from the instance to which it is currently attached.
The volume may be specified with a Volume instance, a string, or a keyword.
Providing a logical true value for the :force option will cause the volume
to be forcibly detached, regardless of whether it is in-use (mounted) or not.
If the volume is specified as a string or keyword, one of the following options
is additionally required:
:region - the region where the volume is allocated
:node - a node in the region where the volume is allocated
FYI: It appears that issuing a detatch-volume command while the volume in question is mounted
will cause the volume to be detatched immediately upon the volume beign unmounted."
[volume & options]
(let [options (apply hash-map options)
volume-id (get-volume-id volume)
region (get-region (if (instance? Volume volume)
(.getRegion volume)
(or (:region options) (:node options))))]
(when (not region)
(throw (IllegalArgumentException.
"Must specify volume's region via :region or :node options, or by providing a Volume instance.")))
(.detachVolumeInRegion (ebs-service)
region
volume-id
(boolean (:force options))
(into-array DetachVolumeOptions []))))
(defn create-volume
"Creates a new volume given a set of options:
- one of :zone (keyword, string, or AvailabilityZoneInfo) or :node (NodeMetadata)
- one or both of :snapshot (keyword, string, or Snapshot instance) or :size
(string, keyword, or number)
- :device (string or keyword) provided *only* when you want to attach the new volume to
the :node you specified!
Returns a vector of [created org.jclouds.ec2.domain.Volume,
optional org.jclouds.ec2.domain.Attachment]
Note that specifying :node instead of :zone will only attach the created volume
:device is also provided. Otherwise, the node is only used to obtain the desired
availability zone.
Note also that if :device and :node are specified, and the attach operation fails,
you will have \"leaked\" the newly-created volume
(volume creation and attachment cannot be done atomically).
e.g. (with-compute-service [compute]
(create-volume :zone :us-east-1a :size 250)
(create-volume :node node-instance :size 250)
(create-volume :node node-instance :size 250 :device \"/dev/sdj\")
(create-volume :zone :eu-west-1b :snapshot \"snap-252310af\")
(create-volume :zone :eu-west-1b :snapshot snapshot-instance)
(create-volume :zone :eu-west-1b :snapshot \"snap-252310af\" :size :1024))"
[& options]
(when (-> options count odd?)
(throw (IllegalArgumentException. "Must provide key-value pairs, e.g. :zone :us-east-1d :size 200")))
(let [options (apply hash-map options)
snapshot (get-string options :snapshot)
snapshot (if (snapshot? snapshot) (.getId snapshot) snapshot)
size (-?> (get-string options :size) as-int)
#^NodeMetadata node (:node options)
zone (or node (get-string options :zone))
zone (if zone
(get-zone zone)
(throw (IllegalArgumentException. "Must supply a :zone or :node option.")))
ebs (ebs-service)]
(when (and (:device options) (not node))
(throw (IllegalArgumentException. "Cannot create and attach new volume; no :node specified")))
(let [new-volume (cond
(and snapshot size) (.createVolumeFromSnapshotInAvailabilityZone ebs zone size snapshot)
snapshot (.createVolumeFromSnapshotInAvailabilityZone ebs zone snapshot)
size (.createVolumeInAvailabilityZone ebs zone size)
:else (throw (IllegalArgumentException. "Must supply :size and/or :snapshot options.")))]
[new-volume (when (:device options)
(attach-volume node new-volume (as-string (:device options))))])))
(defn delete-volume
"Deletes a volume in the specified region.
e.g. (with-compute-service [compute]
(delete-volume :us-east-1 :vol-45228a6d)
(delete-volume :us-east-1 \"vol-052b846c\"))"
([#^Volume volume]
(delete-volume (.getRegion volume) (.getId volume)))
([region volume-id]
(.deleteVolumeInRegion (ebs-service)
(get-region region)
(as-string volume-id))))
(defn status
"Returns the status of the given entity; works for Volumes and Snapshots."
[k]
(.getStatus k))
(defn status-available?
[#^Volume v]
(= Volume$Status/AVAILABLE (status v)))
(defn status-creating?
[#^Volume v]
(= Volume$Status/CREATING (status v)))
(defn status-deleting?
[#^Volume v]
(= Volume$Status/DELETING (status v)))
(defn status-in-use?
[#^Volume v]
(= Volume$Status/IN_USE (status v)))
(defn status-completed?
[#^Snapshot s]
(= Snapshot$Status/COMPLETED (status s)))
(defn status-error?
[#^Snapshot s]
(= Snapshot$Status/ERROR (status s)))
(defn status-pending?
[#^Snapshot s]
(= Snapshot$Status/PENDING (status s)))

View File

@ -1,83 +0,0 @@
;
; Licensed to jclouds, Inc. (jclouds) under one or more
; contributor license agreements. See the NOTICE file
; distributed with this work for additional information
; regarding copyright ownership. jclouds 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.
;
(ns
#^{:author "Chas Emerick, cemerick@snowtide.com"
:doc "A clojure binding for the jclouds AWS elastic IP address interface."}
org.jclouds.ec2.elastic-ip
(:require (org.jclouds [compute :as compute])
[org.jclouds.ec2.ebs :as ebs])
(:import org.jclouds.compute.domain.NodeMetadata
(org.jclouds.ec2.domain PublicIpInstanceIdPair)))
(defn #^org.jclouds.ec2.services.ElasticIPAddressClient
eip-service
"Returns the synchronous ElasticIPAddressClient associated with
the specified compute service, or compute/*compute* as bound by with-compute-service."
[& [compute]]
(-> (or compute compute/*compute*)
.getContext .getProviderSpecificContext .getApi .getElasticIPAddressServices))
(defn allocate
"Claims a new elastic IP address within the (optionally) specified region for your account.
Region may be a string, keyword, or a node from which the region
is inferred. Returns the IP address as a string."
([] (allocate nil))
([region]
(.allocateAddressInRegion (eip-service) (ebs/get-region region))))
(defn associate
"Associates an elastic IP address with a node."
([#^NodeMetadata node public-ip]
(associate node public-ip (.getProviderId node)))
([region public-ip instance-id]
(.associateAddressInRegion (eip-service)
(ebs/get-region region)
public-ip
instance-id)))
(defn addresses
"Returns a map of elastic IP addresses to maps with slots:
:region - the region (string/keyword/NodeMetadata) the IP address is allocated within
:node-id - the ID of the instance with which the IP address is associated (optional)
You may optionally specify which IP addresses you would like to query."
([] (addresses nil))
([region & public-ips]
(into {} (for [#^PublicIpInstanceIdPair pair (.describeAddressesInRegion (eip-service)
(ebs/get-region region)
(into-array String public-ips))]
[(.getPublicIp pair) (merge {:region (.getRegion pair)}
(when (.getInstanceId pair) {:node-id (.getInstanceId pair)}))]))))
(defn dissociate
"Dissociates an elastic IP address from the node with which it is currently associated."
[region public-ip]
(.disassociateAddressInRegion (eip-service)
(ebs/get-region region)
public-ip))
(defn release
"Disclaims an elastic IP address from your account."
([public-ip] (release nil public-ip))
([region public-ip]
(.releaseAddressInRegion (eip-service)
(ebs/get-region region)
public-ip)))

View File

@ -1,460 +0,0 @@
;
; Licensed to jclouds, Inc. (jclouds) under one or more
; contributor license agreements. See the NOTICE file
; distributed with this work for additional information
; regarding copyright ownership. jclouds 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.
;
(ns org.jclouds.blobstore
"A clojure binding for the jclouds BlobStore.
Current supported services are:
[transient, filesystem, azureblob, atmos, walrus, scaleup-storage, ninefold-storage
googlestorage, synaptic, peer1-storage, aws-s3, eucalyptus-partnercloud-s3,
cloudfiles-us, cloudfiles-uk, swift, scality-rs2, hosteurope-storage
tiscali-storage]
Here's a quick example of how to viewresources in rackspace
(use 'org.jclouds.blobstore)
(def user \"rackspace_username\")
(def password \"rackspace_password\")
(def blobstore-name \"cloudfiles\")
(with-blobstore [blobstore-name user password]
(pprint (locations))
(pprint (containers))
(pprint (blobs blobstore your_container_name)))
See http://code.google.com/p/jclouds for details."
(:use [org.jclouds.core])
(:import [java.io File FileOutputStream OutputStream]
java.util.Properties
[org.jclouds.blobstore
AsyncBlobStore domain.BlobBuilder BlobStore BlobStoreContext
BlobStoreContextFactory domain.BlobMetadata domain.StorageMetadata
domain.Blob options.ListContainerOptions]
org.jclouds.io.Payloads
org.jclouds.io.payloads.PhantomPayload
java.util.Arrays
[java.security DigestOutputStream MessageDigest]
com.google.common.collect.ImmutableSet))
(defn blobstore
"Create a logged in context.
Options for communication style
:sync and :async.
Options can also be specified for extension modules
:log4j :enterprise :ning :apachehc :bouncycastle :joda :gae"
([#^String provider #^String provider-identity #^String provider-credential
& options]
(let [module-keys (set (keys module-lookup))
ext-modules (filter #(module-keys %) options)
opts (apply hash-map (filter #(not (module-keys %)) options))]
(let [context (.. (BlobStoreContextFactory.)
(createContext
provider provider-identity provider-credential
(apply modules (concat ext-modules (opts :extensions)))
(reduce #(do (.put %1 (name (first %2)) (second %2)) %1)
(Properties.) (dissoc opts :extensions))))]
(if (some #(= :async %) options)
(.getAsyncBlobStore context)
(.getBlobStore context))))))
(defn blobstore-context
"Returns a blobstore context from a blobstore."
[blobstore]
(.getContext blobstore))
(defn blob?
[object]
(instance? Blob))
(defn blobstore?
[object]
(or (instance? BlobStore object)
(instance? AsyncBlobStore object)))
(defn blobstore-context?
[object]
(instance? BlobStoreContext object))
(defn as-blobstore
"Tries hard to produce a blobstore from its input arguments"
[& args]
(cond
(blobstore? (first args)) (first args)
(blobstore-context? (first args)) (.getBlobStore (first args))
:else (apply blobstore args)))
(def ^{:dynamic :true} *blobstore*)
(def ^{:dynamic :true} *max-retries* 3)
(defmacro with-blobstore [[& blobstore-or-args] & body]
`(binding [*blobstore* (as-blobstore ~@blobstore-or-args)]
~@body))
(defn containers
"List all containers in a blobstore."
([] (containers *blobstore*))
([blobstore] (.list blobstore)))
(def #^{:private true} list-option-map
{:after-marker #(.afterMarker %1 %2)
:in-directory #(.inDirectory %1 %2)
:max-results #(.maxResults %1 %2)
:with-details #(when %2 (.withDetails %1))
:recursive #(when %2 (.recursive %1))})
(defn list-container
"Low-level container listing. Use list-blobs where possible since
it's higher-level and returns a lazy seq. Options are:
:after-marker string
:in-direcory path
:max-results n
:with-details true
:recursive true"
[blobstore & args]
(if (blobstore? blobstore)
(let [[container-name & args] args
options (apply hash-map args)
list-options (reduce
(fn [lco [k v]]
((list-option-map k) lco v)
lco)
(ListContainerOptions.)
options)]
(.list blobstore container-name list-options))
(apply list-container *blobstore* blobstore args)))
(defn- list-blobs-chunk [container prefix #^BlobStore blobstore & [marker]]
(apply list-container blobstore container
(concat (when prefix
[:in-directory prefix])
(when (string? marker)
[:after-marker marker]))))
(defn- list-blobs-chunks [container prefix #^BlobStore blobstore marker]
(when marker
(let [chunk (list-blobs-chunk container prefix blobstore marker)]
(lazy-seq (cons chunk
(list-blobs-chunks container prefix blobstore
(.getNextMarker chunk)))))))
(defn- concat-elements
"Make a lazy concatenation of the lazy sequences contained in coll. Lazily evaluates coll.
Note: (apply concat coll) or (lazy-cat coll) are not lazy wrt coll itself."
[coll]
(if-let [s (seq coll)]
(lazy-seq (concat (first s) (concat-elements (next s))))))
(defn list-blobs
"Returns a lazy seq of all blobs in the given container."
([container]
(list-blobs container *blobstore*))
([container blobstore]
(list-blobs container nil blobstore))
([container prefix blobstore]
(concat-elements (list-blobs-chunks container prefix blobstore :start))))
(defn locations
"Retrieve the available container locations for the blobstore context."
([] (locations *blobstore*))
([#^BlobStore blobstore]
(seq (.listAssignableLocations blobstore))))
(defn create-container
"Create a container."
([container-name]
(create-container container-name nil *blobstore*))
([container-name location]
(create-container container-name location *blobstore*))
([container-name location #^BlobStore blobstore]
(.createContainerInLocation blobstore location container-name)))
(defn clear-container
"Clear a container."
([container-name]
(clear-container container-name *blobstore*))
([container-name #^BlobStore blobstore]
(.clearContainer blobstore container-name)))
(defn delete-container
"Delete a container."
([container-name]
(delete-container container-name *blobstore*))
([container-name #^BlobStore blobstore]
(.deleteContainer blobstore container-name)))
(defn container-exists?
"Predicate to check presence of a container"
([container-name]
(container-exists? container-name *blobstore*))
([container-name #^BlobStore blobstore]
(.containerExists blobstore container-name)))
(defn directory-exists?
"Predicate to check presence of a directory"
([container-name path]
(directory-exists? container-name path *blobstore*))
([container-name path #^BlobStore blobstore]
(.directoryExists blobstore container-name path)))
(defn create-directory
"Create a directory path."
([container-name path]
(create-directory container-name path *blobstore*))
([container-name path #^BlobStore blobstore]
(.createDirectory blobstore container-name path)))
(defn delete-directory
"Delete a directory path."
([container-name path]
(delete-directory container-name path *blobstore*))
([container-name path #^BlobStore blobstore]
(.deleteDirectory blobstore container-name path)))
(defn blob-exists?
"Predicate to check presence of a blob"
([container-name path]
(blob-exists? container-name path *blobstore*))
([container-name path #^BlobStore blobstore]
(.blobExists blobstore container-name path)))
(defn put-blob
"Put a blob. Metadata in the blob determines location."
([container-name blob]
(put-blob container-name blob *blobstore*))
([container-name blob #^BlobStore blobstore]
(.putBlob blobstore container-name blob)))
(defn blob-metadata
"Get metadata from given path"
([container-name path]
(blob-metadata container-name path *blobstore*))
([container-name path #^BlobStore blobstore]
(.blobMetadata blobstore container-name path)))
(defn ^{:dynamic :true} get-blob
"Get blob from given path"
([container-name path]
(get-blob container-name path *blobstore*))
([container-name path #^BlobStore blobstore]
(.getBlob blobstore container-name path)))
(defn sign-blob-request
"Get a signed http request for manipulating a blob in another application.
ex. curl. The default is for a :get request.
The request argument is used to specify charecteristics of the request
to be signed. The :method key must be set to one of :get, :delete, and
:put. For :put requests, :content-length must be specified. Optionally,
:content-type, :content-disposition, :content-language, :content-encoding
and :content-md5 may be given."
{:deprecated "1.0-beta-10"}
([container-name path]
(sign-blob-request container-name path {:method :get} *blobstore*))
([container-name path
{:keys [method content-type content-length content-md5
content-disposition content-encoding content-language] :as request}]
(sign-blob-request container-name path request *blobstore*))
([container-name path
{:keys [method content-type content-length content-md5
content-disposition content-encoding content-language]} blobstore]
{:pre [(#{:delete :get :put} method)
(or content-length (#{:delete :get} method))]}
(case method
:delete (.signRemoveBlob
(.. blobstore getContext getSigner) container-name path)
:get (.signGetBlob
(.. blobstore getContext getSigner) container-name path)
:put (.signPutBlob
(.. blobstore getContext getSigner) container-name
(doto (.build (.blobBuilder blobstore path))
(.setPayload
(let [payload (PhantomPayload.)
metadata (.getContentMetadata payload)]
;; TODO look into use of ContentMetadata constructor
(doto metadata
(.setContentLength (long content-length))
(.setContentType content-type)
(.setContentMD5 content-md5)
(.setContentDisposition content-disposition)
(.setContentEncoding content-encoding)
(.setContentLanguage content-language))
payload)))))))
(defn sign-get
"Get a signed http GET request for manipulating a blob in another
application, Ex. curl."
([container-name name]
(sign-get container-name name *blobstore*))
([container-name name ^BlobStore blobstore]
(.signGetBlob (.. blobstore getContext getSigner) container-name name)))
(defn sign-put
"Get a signed http PUT request for manipulating a blob in another
application, Ex. curl. A Blob with at least the name and content-length
must be given."
([container-name blob]
(sign-put container-name blob *blobstore*))
([container-name ^Blob blob ^BlobStore blobstore]
(.signPutBlob (.. blobstore getContext getSigner)
container-name
blob)))
(defn sign-delete
"Get a signed http DELETE request for manipulating a blob in another
applicaiton, Ex. curl."
([container-name name]
(sign-delete container-name name *blobstore*))
([container-name name ^BlobStore blobstore]
(.signRemoveBlob (.. blobstore getContext getSigner) container-name name)))
(defn get-blob-stream
"Get an inputstream from the blob at a given path"
([container-name path]
(get-blob-stream container-name path *blobstore*))
([container-name path #^BlobStore blobstore]
(.getInput(.getPayload(.getBlob blobstore container-name path)))))
(defn remove-blob
"Remove blob from given path"
([container-name path]
(remove-blob container-name path *blobstore*))
([container-name path #^BlobStore blobstore]
(.removeBlob blobstore container-name path)))
(defn count-blobs
"Count blobs"
([container-name]
(count-blobs container-name *blobstore*))
([container-name blobstore]
(.countBlobs blobstore container-name)))
(defn blobs
"List the blobs in a container:
blobstore container -> blobs
list the blobs in a container under a path:
blobstore container dir -> blobs
example:
(pprint
(blobs
(blobstore-context flightcaster-creds)
\"somecontainer\" \"some-dir\"))"
([blobstore container-name]
(.list (as-blobstore blobstore) container-name))
([blobstore container-name dir]
(.list (as-blobstore blobstore) container-name
(.inDirectory (new ListContainerOptions) dir))))
(defn blob
"create a new blob with the specified payload"
{:deprecated "1.0-beta-10"}
([#^String name payload]
(blob name payload *blobstore*))
([#^String name payload #^BlobStore blobstore]
(.build
(.payload
(.blobBuilder blobstore name) payload))))
(defn blob2
"Create a new blob with the specified payload and options."
([^String name option-map]
(blob2 name option-map *blobstore*))
([^String name
{:keys [payload content-type content-length content-md5 calculate-md5
content-disposition content-encoding content-language metadata]}
^BlobStore blobstore]
{:pre [(not (and content-md5 calculate-md5))
(not (and (nil? payload) calculate-md5))]}
(let [blob-builder (if payload
(.payload (.blobBuilder blobstore name) payload)
(.forSigning (.blobBuilder blobstore name)))
blob-builder (if content-length ;; Special case, arg is prim.
(.contentLength blob-builder content-length)
blob-builder)
blob-builder (if calculate-md5 ;; Only do calculateMD5 OR contentMD5.
(.calculateMD5 blob-builder)
(if content-md5
(.contentMD5 blob-builder content-md5)
blob-builder))]
(doto blob-builder
(.contentType content-type)
(.contentDisposition content-disposition)
(.contentEncoding content-encoding)
(.contentLanguage content-language)
(.userMetadata metadata))
(.build blob-builder))))
(defn md5-blob
"add a content md5 to a blob, or make a new blob that has an md5.
note that this implies rebuffering, if the blob's payload isn't repeatable"
([#^Blob blob]
(Payloads/calculateMD5 blob))
([#^String name payload]
(md5-blob name payload *blobstore*))
([#^String name payload #^BlobStore blobstore]
(md5-blob (blob2 name {:payload payload} blobstore))))
(defn upload-blob
"Create anrepresenting text data:
container, name, string -> etag"
([container-name name data]
(upload-blob container-name name data *blobstore*))
([container-name name data #^BlobStore blobstore]
(put-blob container-name
(md5-blob name data blobstore) blobstore)))
(defmulti #^{:arglists '[[container-name name target]
[container-name name target blobstore]]}
download-blob (fn [& args]
(if (= (count args) 3)
::short-form
(class (last (butlast args))))))
(defmethod download-blob ::short-form
[container-name name target]
(download-blob container-name name target *blobstore*))
(defmethod download-blob OutputStream [container-name name target blobstore
& [retries]]
(let [blob (get-blob container-name name blobstore)
digest-stream (DigestOutputStream.
target (.md5(.crypto (.utils (blobstore-context blobstore)))))]
(.writeTo (.getPayload blob) digest-stream)
(let [digest (.digest (.getMessageDigest digest-stream))
metadata-digest (.getContentMD5 (.getContentMetadata (.getPayload blob)))]
(when-not (Arrays/equals digest metadata-digest)
(if (<= (or retries 0) *max-retries*)
(recur container-name name target blobstore [(inc (or retries 1))])
(throw (Exception. (format "Download failed for %s/%s"
container-name name))))))))
(defmethod download-blob File [container-name name target blobstore]
(download-blob container-name name (FileOutputStream. target) blobstore))
(define-accessors StorageMetadata "blob" type id name
location-id uri last-modified)
(define-accessors BlobMetadata "blob" content-type)
(defn blob-etag [blob]
(.getETag blob))
(defn blob-md5 [blob]
(.getContentMD5 blob))

View File

@ -1,247 +0,0 @@
;
; Licensed to jclouds, Inc. (jclouds) under one or more
; contributor license agreements. See the NOTICE file
; distributed with this work for additional information
; regarding copyright ownership. jclouds 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.
;
(ns org.jclouds.blobstore-test
(:use [org.jclouds.blobstore] :reload-all)
(:use [clojure.test])
(:import [org.jclouds.blobstore BlobStoreContextFactory]
[org.jclouds.crypto CryptoStreams]
[java.io ByteArrayOutputStream]
[org.jclouds.util Strings2]))
(defn clean-stub-fixture
"This should allow basic tests to easily be run with another service."
[service account key & options]
(fn [f]
(with-blobstore [(apply blobstore service account key options)]
(doseq [container (containers)]
(delete-container (.getName container)))
(f))))
(use-fixtures :each (clean-stub-fixture "transient" "" ""))
(deftest blobstore?-test
(is (blobstore? *blobstore*)))
(deftest as-blobstore-test
(is (blobstore? (blobstore "transient" "user" "password")))
(is (blobstore? (as-blobstore *blobstore*)))
(is (blobstore? (as-blobstore (blobstore-context *blobstore*)))))
(deftest create-existing-container-test
(is (not (container-exists? "")))
(is (create-container "fred"))
(is (container-exists? "fred")))
(deftest create-container-test
(is (create-container "fred"))
(is (container-exists? "fred")))
(deftest locations-test
(is (not (empty? (locations))))
(is (create-container "fred" (first (locations)))))
(deftest containers-test
(is (empty? (containers)))
(is (create-container "fred"))
(is (= 1 (count (containers)))))
(deftest list-container-test
(is (create-container "container"))
(is (empty? (list-container "container")))
(is (upload-blob "container" "blob1" "blob1"))
(is (upload-blob "container" "blob2" "blob2"))
(is (= 2 (count (list-container "container"))))
(is (= 1 (count (list-container "container" :max-results 1))))
(create-directory "container" "dir")
(is (upload-blob "container" "dir/blob2" "blob2"))
(is (= 3 (count-blobs "container")))
(is (= 3 (count (list-container "container"))))
(is (= 4 (count (list-container "container" :recursive true))))
(is (= 3 (count (list-container "container" :with-details true))))
(is (= 1 (count (list-container "container" :in-directory "dir")))))
(deftest list-blobs-test
(is (create-container "container"))
(is (empty? (list-blobs "container")))
(is (empty? (list-blobs "container" "/a" *blobstore*))))
(deftest large-container-list-test
(let [container-name "test"
total-blobs 5000]
;; create a container full of blobs
(create-container container-name)
(dotimes [i total-blobs] (upload-blob container-name (str i) (str i)))
;; verify
(is (= total-blobs (count-blobs container-name)))))
(deftest get-blob-test
(is (create-container "blob"))
(is (upload-blob "blob" "blob1" "blob1"))
(is (upload-blob "blob" "blob2" "blob2"))
(is (= "blob2" (Strings2/toStringAndClose (get-blob-stream "blob" "blob2")))))
(deftest download-blob-test
(let [name "test"
container-name "test-container"
data "test content"
data-file (java.io.File/createTempFile "jclouds" "data")]
(try (create-container container-name)
(upload-blob container-name name data)
(download-blob container-name name data-file)
(is (= data (slurp (.getAbsolutePath data-file))))
(finally (.delete data-file)))))
(deftest download-checksum-test
(binding [get-blob (fn [blobstore c-name name]
(let [blob (.newBlob blobstore name)
md (.getMetadata blob)]
(.setPayload blob "bogus payload")
(.setContentMD5 md (.getBytes "bogus MD5"))
blob))]
(let [name "test"
container-name "test-container"
data "test content"
data-file (java.io.File/createTempFile "jclouds" "data")]
(try (create-container container-name)
(upload-blob container-name name data)
(is (thrown? Exception
(download-blob container-name name data-file)))
(finally (.delete data-file))))))
(deftest sign-blob-request-test
(testing "delete"
(let [request (sign-blob-request "container" "path" {:method :delete})]
(is (= "http://localhost/container/path" (str (.getEndpoint request))))
(is (= "DELETE" (.getMethod request)))))
(testing "default request"
(let [request (sign-blob-request "container" "path")]
(is (= "http://localhost/container/path" (str (.getEndpoint request))))
(is (= "GET" (.getMethod request)))))
(testing "get"
(let [request (sign-blob-request "container" "path" {:method :get})]
(is (= "http://localhost/container/path" (str (.getEndpoint request))))
(is (= "GET" (.getMethod request)))))
(testing "put"
(let [request (sign-blob-request
"container" "path" {:method :put :content-length 10})]
(is (= "http://localhost/container/path" (str (.getEndpoint request))))
(is (= "PUT" (.getMethod request)))
(is (= "10" (first (.get (.getHeaders request) "Content-Length"))))
(is (nil?
(first (.get (.getHeaders request) "Content-Type"))))))
(testing "put with headers"
(let [request (sign-blob-request
"container" "path"
{:method :put :content-length 10
:content-type "x"
:content-language "en"
:content-disposition "f"
:content-encoding "g"})]
(is (= "PUT" (.getMethod request)))
(is (= "10" (first (.get (.getHeaders request) "Content-Length"))))
(is (= "x" (first (.get (.getHeaders request) "Content-Type"))))
(is (= "en" (first (.get (.getHeaders request) "Content-Language"))))
(is (= "f" (first (.get (.getHeaders request) "Content-Disposition"))))
(is (= "g" (first (.get (.getHeaders request) "Content-Encoding")))))))
(deftest sign-get-test
(let [request (sign-get "container" "path")]
(is (= "http://localhost/container/path" (str (.getEndpoint request))))
(is (= "GET" (.getMethod request)))))
(deftest sign-put-test
(let [request (sign-put "container"
(blob2 "path" {:content-length 10}))]
(is (= "http://localhost/container/path" (str (.getEndpoint request))))
(is (= "PUT" (.getMethod request)))
(is (= "10" (first (.get (.getHeaders request) "Content-Length"))))
(is (nil?
(first (.get (.getHeaders request) "Content-Type"))))))
(deftest sign-put-with-headers-test
(let [request (sign-put
"container"
(blob2 "path" {:content-length 10
:content-type "x"
:content-language "en"
:content-disposition "f"
:content-encoding "g"}))]
(is (= "PUT" (.getMethod request)))
(is (= "10" (first (.get (.getHeaders request) "Content-Length"))))
(is (= "x" (first (.get (.getHeaders request) "Content-Type"))))
(is (= "en" (first (.get (.getHeaders request) "Content-Language"))))
(is (= "f" (first (.get (.getHeaders request) "Content-Disposition"))))
(is (= "g" (first (.get (.getHeaders request) "Content-Encoding"))))))
(deftest sign-delete-test
(let [request (sign-delete "container" "path")]
(is (= "http://localhost/container/path" (str (.getEndpoint request))))
(is (= "DELETE" (.getMethod request)))))
(deftest blob2-test
(let [a-blob (blob2 "test-name" {:payload (.getBytes "test-payload")
:calculate-md5 true})]
(is (= (seq (.. a-blob (getPayload) (getContentMetadata) (getContentMD5)))
(seq (CryptoStreams/md5 (.getBytes "test-payload")))))))
;; TODO: more tests involving blob-specific functions
(deftest corruption-hunt
(let [container-name "test"
name "work-file"
total-downloads 100
threads 10]
;; upload
(create-container container-name)
(when-not (blob-exists? container-name name)
(let [data-stream (java.io.ByteArrayOutputStream.)]
(dotimes [i 5000000] (.write data-stream i))
(upload-blob container-name name (.toByteArray data-stream))))
;; download
(let [total (atom total-downloads)]
(defn new-agent []
(agent name))
(defn dl-and-restart [blob-s file]
(when-not (<= @total 0)
(with-open [baos (java.io.ByteArrayOutputStream.)]
(try
(download-blob container-name file baos blob-s)
(catch Exception e
(with-open [of (java.io.FileOutputStream.
(java.io.File/createTempFile "jclouds" ".dl"))]
(.write of (.toByteArray baos)))
(throw e))))
(swap! total dec)
(send *agent* (partial dl-and-restart blob-s))
file))
(defn start-agents []
(let [agents (map (fn [_] (new-agent))
(range threads))]
(doseq [a agents]
(send-off a (partial dl-and-restart *blobstore*)))
agents))
(let [agents (start-agents)]
(apply await agents)
(is (every? nil? (map agent-errors agents)))))))

View File

@ -1,459 +0,0 @@
;
; Licensed to jclouds, Inc. (jclouds) under one or more
; contributor license agreements. See the NOTICE file
; distributed with this work for additional information
; regarding copyright ownership. jclouds 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.
;
(ns org.jclouds.compute
"A clojure binding to the jclouds ComputeService.
Current supported providers are:
[aws-ec2, eucualyptus-partnercloud-ec2, elastichosts-lon-b, nova,
cloudservers-uk, cloudservers-us, byon, cloudsigma-zrh, stub,
trmk-ecloud, trmk-vcloudexpress, vcloud, bluelock, eucalyptus,
slicehost, elastichosts-lon-p, elastichosts-sat-p, elastichosts,
openhosting-east1, serverlove-z1-man, skalicloud-sdg-my, deltacloud]
Here's an example of getting some compute configuration from rackspace:
(use 'org.jclouds.compute)
(def provider \"cloudservers\")
(def provider-identity \"username\")
(def provider-credential \"password\")
;; create a compute service
(def compute
(compute-service provider provider-identity provider-credential))
(with-compute-service [compute]
(pprint (locations))
(pprint (images))
(pprint (nodes))
(pprint (hardware-profiles)))
Here's an example of creating and running a small linux node in the group
webserver:
;; create a compute service using ssh and log4j extensions
(def compute
(compute-service
provider provider-identity provider-credential :sshj :log4j))
(create-node \"webserver\" compute)
See http://code.google.com/p/jclouds for details."
(:use org.jclouds.core
(org.jclouds predicate) [clojure.core.incubator :only (-?>)])
(:import java.io.File
java.util.Properties
[org.jclouds.domain Location]
[org.jclouds.compute
ComputeService ComputeServiceContext ComputeServiceContextFactory]
[org.jclouds.compute.domain
Template TemplateBuilder ComputeMetadata NodeMetadata Hardware
OsFamily Image]
[org.jclouds.compute.options TemplateOptions]
[org.jclouds.compute.predicates
NodePredicates]
[com.google.common.collect ImmutableSet]))
(defmacro deprecate-fwd [old-name new-name] `(defn ~old-name {:deprecated "beta-9"} [& args#] (apply ~new-name args#)))
(defn compute-service
"Create a logged in context."
([#^String provider #^String provider-identity #^String provider-credential
& options]
(let [module-keys (set (keys module-lookup))
ext-modules (filter #(module-keys %) options)
opts (apply hash-map (filter #(not (module-keys %)) options))]
(.. (ComputeServiceContextFactory.)
(createContext
provider provider-identity provider-credential
(apply modules (concat ext-modules (opts :extensions)))
(reduce #(do (.put %1 (name (first %2)) (second %2)) %1)
(Properties.) (dissoc opts :extensions)))
(getComputeService)))))
(defn compute-context
"Returns a compute context from a compute service."
[compute]
(.getContext compute))
(defn compute-service?
[object]
(instance? ComputeService object))
(defn compute-context?
[object]
(instance? ComputeServiceContext object))
(defn as-compute-service
"Tries hard to produce a compute service from its input arguments"
[& args]
(cond
(compute-service? (first args)) (first args)
(compute-context? (first args)) (.getComputeService (first args))
:else (apply compute-service args)))
(def ^{:dynamic :true} *compute*)
(defmacro with-compute-service
"Specify the default compute service"
[[& compute-or-args] & body]
`(binding [*compute* (as-compute-service ~@compute-or-args)]
~@body))
(defn locations
"Retrieve the available compute locations for the compute context."
([] (locations *compute*))
([#^ComputeService compute]
(seq (.listAssignableLocations compute))))
(defn nodes
"Retrieve the existing nodes for the compute context."
([] (nodes *compute*))
([#^ComputeService compute]
(seq (.listNodes compute))))
(defn nodes-with-details
"Retrieve the existing nodes for the compute context."
([] (nodes-with-details *compute*))
([#^ComputeService compute]
(seq (.listNodesDetailsMatching compute (NodePredicates/all)))))
(defn nodes-in-group
"list details of all the nodes in the given group."
([group] (nodes-in-group group *compute*))
([#^String group #^ComputeService compute]
(filter #(= (.getGroup %) group) (nodes-with-details compute))))
(defn images
"Retrieve the available images for the compute context."
([] (images *compute*))
([#^ComputeService compute]
(seq (.listImages compute))))
(defn hardware-profiles
"Retrieve the available node hardware profiles for the compute context."
([] (hardware-profiles *compute*))
([#^ComputeService compute]
(seq (.listHardwareProfiles compute))))
(defn default-template
([] (default-template *compute*))
([#^ComputeService compute]
(.. compute (templateBuilder)
(options
(org.jclouds.compute.options.TemplateOptions$Builder/authorizePublicKey
(slurp (str (. System getProperty "user.home") "/.ssh/id_rsa.pub"))))
build)))
(defn create-nodes
"Create the specified number of nodes using the default or specified
template.
;; Simplest way to add 2 small linux nodes to the group webserver is to run
(create-nodes \"webserver\" 2 compute)
;; which is the same as wrapping the create-nodes command with an implicit
;; compute service.
;; Note that this will actually add another 2 nodes to the set called
;; \"webserver\"
(with-compute-service [compute]
(create-nodes \"webserver\" 2 ))
;; which is the same as specifying the default template
(with-compute-service [compute]
(create-nodes \"webserver\" 2 (default-template)))
;; which, on gogrid, is the same as constructing the smallest centos template
;; that has no layered software
(with-compute-service [compute]
(create-nodes \"webserver\" 2
(build-template
service
{:os-family :centos :smallest true
:image-name-matches \".*w/ None.*\"})))"
([group count]
(create-nodes group count (default-template *compute*) *compute*))
([group count compute-or-template]
(if (compute-service? compute-or-template)
(create-nodes
group count (default-template compute-or-template) compute-or-template)
(create-nodes group count compute-or-template *compute*)))
([group count template #^ComputeService compute]
(seq
(.createNodesInGroup compute group count template))))
(deprecate-fwd run-nodes create-nodes)
(defn create-node
"Create a node using the default or specified template.
;; simplest way to add a small linux node to the group webserver is to run
(create-node \"webserver\" compute)
;; which is the same as wrapping the create-node command with an implicit compute
;; service.
;; Note that this will actually add another node to the set called
;; \"webserver\"
(with-compute-service [compute]
(create-node \"webserver\" ))"
([group]
(first (create-nodes group 1 (default-template *compute*) *compute*)))
([group compute-or-template]
(if (compute-service? compute-or-template)
(first
(create-nodes
group 1 (default-template compute-or-template) compute-or-template))
(first (create-nodes group 1 compute-or-template *compute*))))
([group template compute]
(first (create-nodes group 1 template compute))))
(deprecate-fwd run-node create-node)
(defn #^NodeMetadata node-details
"Retrieve the node metadata, given its id."
([id] (node-details id *compute*))
([id #^ComputeService compute]
(.getNodeMetadata compute id)))
(defn suspend-nodes-in-group
"Reboot all the nodes in the given group."
([group] (suspend-nodes-in-group group *compute*))
([#^String group #^ComputeService compute]
(.suspendNodesMatching compute (NodePredicates/inGroup group))))
(defn suspend-node
"Suspend a node, given its id."
([id] (suspend-node id *compute*))
([id #^ComputeService compute]
(.suspendNode compute id)))
(defn resume-nodes-in-group
"Suspend all the nodes in the given group."
([group] (resume-nodes-in-group group *compute*))
([#^String group #^ComputeService compute]
(.resumeNodesMatching compute (NodePredicates/inGroup group))))
(defn resume-node
"Resume a node, given its id."
([id] (resume-node id *compute*))
([id #^ComputeService compute]
(.resumeNode compute id)))
(defn reboot-nodes-in-group
"Reboot all the nodes in the given group."
([group] (reboot-nodes-in-group group *compute*))
([#^String group #^ComputeService compute]
(.rebootNodesMatching compute (NodePredicates/inGroup group))))
(defn reboot-node
"Reboot a node, given its id."
([id] (reboot-node id *compute*))
([id #^ComputeService compute]
(.rebootNode compute id)))
(defn destroy-nodes-in-group
"Destroy all the nodes in the given group."
([group] (destroy-nodes-in-group group *compute*))
([#^String group #^ComputeService compute]
(.destroyNodesMatching compute (NodePredicates/inGroup group))))
(defn destroy-node
"Destroy a node, given its id."
([id] (destroy-node id *compute*))
([id #^ComputeService compute]
(.destroyNode compute id)))
(defmacro state-predicate [node state]
`(= (.getState ~node)
(. org.jclouds.compute.domain.NodeState ~state)))
(defn pending?
"Predicate for the node being in transition"
[#^NodeMetadata node]
(state-predicate node PENDING))
(defn running?
"Predicate for the node being available for requests."
[#^NodeMetadata node]
(state-predicate node RUNNING))
(defn terminated?
"Predicate for the node being halted."
[#^NodeMetadata node]
(or
(= node nil)
(state-predicate node TERMINATED)))
(defn suspended?
"Predicate for the node being suspended."
[#^NodeMetadata node]
(state-predicate node SUSPENDED))
(defn error-state?
"Predicate for the node being in an error state."
[#^NodeMetadata node]
(state-predicate node ERROR))
(defn unrecognized-state?
"Predicate for the node being in an unrecognized state."
[#^NodeMetadata node]
(state-predicate node UNRECOGNIZED))
(defn public-ips
"Returns the node's public ips"
[#^NodeMetadata node]
(.getPublicAddresses node))
(defn private-ips
"Returns the node's private ips"
[#^NodeMetadata node]
(.getPrivateAddresses node))
(defn group
"Returns a the node's group"
[#^NodeMetadata node]
(.getGroup node))
(defn hostname
"Returns the compute node's name"
[#^ComputeMetadata node]
(.getName node))
(defn location
"Returns the compute node's location id"
[#^ComputeMetadata node]
(-?> node .getLocation .getId))
(defn id
"Returns the compute node's id"
[#^ComputeMetadata node]
(.getId node))
(define-accessors Template image hardware location options)
(define-accessors Image version os-family os-description architecture)
(define-accessors Hardware processors ram volumes)
(define-accessors NodeMetadata "node" credentials hardware state group)
(def
^{:doc "TemplateBuilder functions" :private true}
template-map
(merge
(make-option-map
kw-memfn-0arg [:smallest :fastest :biggest :any])
(make-option-map
kw-memfn-1arg
[:os-family :location-id :architecture :image-id :hardware-id
:os-name-matches :os-version-matches :os-description-matches
:os-64-bit :image-version-matches :image-name-matches
:image-description-matches :min-cores :min-ram])))
(def
^{:doc "TemplateOptions functions" :private true}
options-map
(merge
(make-option-map
kw-memfn-0arg
[:destroy-on-error :enable-monitoring :no-placement-group :no-key-pair
:with-details])
(make-option-map
kw-memfn-1arg
[:run-script :install-private-key :authorize-public-key
:override-credentials-with :override-login-user-with
:override-login-credential-with
;; aws ec2 options
:spot-price :spot-options :placement-group :subnet-id
:block-device-mappings :unmapDeviceNamed :security-groups
:key-pair :user-data])
(make-option-map kw-memfn-varargs [:inbound-ports])
(make-option-map
kw-memfn-2arg
[:block-on-port
;; aws ec2 options
:map-ephemeral-device-to-device-name])
{:map-ebs-snapshot-to-device-name
(memfn-apply mapEBSSnapshotToDeviceName
device-name snapshot-id size-in-gib delete-on-termination)
:map-new-volume-to-device-name
(kw-memfn-apply :map-new-volume-to-device-name
device-name size-in-gib delete-on-termination)}))
(def
^{:doc "All receognised options"}
known-template-options
(set (mapcat keys [options-map template-map])))
(defn os-families []
(. OsFamily values))
(def enum-map {:os-family (os-families)})
(defn translate-enum-value [kword value]
(or (-> (filter #(= (name value) (str %)) (kword enum-map)) first)
value))
(defn apply-option [builder option-map option value]
(when-let [f (option-map option)]
(f builder (translate-enum-value option value))))
;; TODO look at clojure-datalog
(defn build-template
"Creates a template that can be used to run nodes.
The :os-family key expects a keyword version of OsFamily,
eg. :os-family :ubuntu.
The :smallest, :fastest, :biggest, :any, and :destroy-on-error keys expect a
boolean value.
Options correspond to TemplateBuilder methods."
[#^ComputeService compute
{:keys [os-family location-id architecture image-id hardware-id
os-name-matches os-version-matches os-description-matches
os-64-bit image-version-matches image-name-matches
image-description-matches min-cores min-ram
run-script install-private-key authorize-public-key
inbound-ports smallest fastest biggest any destroy-on-error]
:as options}]
(let [builder (.. compute (templateBuilder))]
(doseq [[option value] options]
(when-not (known-template-options option)
(throw (Exception. (format "Invalid template builder option : %s" option))))
;; apply template builder options
(try
(apply-option builder template-map option value)
(catch Exception e
(throw (Exception. (format
"Problem applying template builder %s with value %s: %s"
option (pr-str value) (.getMessage e))
e)))))
(let [template (.build builder)
template-options (.getOptions template)]
(doseq [[option value] options]
;; apply template option options
(try
(apply-option template-options options-map option value)
(catch Exception e
(throw (Exception.
(format
"Problem applying template option %s with value %s: %s"
option (pr-str value) (.getMessage e))
e)))))
template)))

View File

@ -1,111 +0,0 @@
;
; Licensed to jclouds, Inc. (jclouds) under one or more
; contributor license agreements. See the NOTICE file
; distributed with this work for additional information
; regarding copyright ownership. jclouds 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.
;
(ns org.jclouds.compute-test
(:use [org.jclouds.compute] :reload-all)
(:use clojure.test)
(:import
org.jclouds.compute.domain.OsFamily))
(defmacro with-private-vars [[ns fns] & tests]
"Refers private fns from ns and runs tests in context. From users mailing
list, Alan Dipert and MeikelBrandmeyer."
`(let ~(reduce #(conj %1 %2 `@(ns-resolve '~ns '~%2)) [] fns)
~@tests))
(deftest os-families-test
(is (some #{"centos"} (map str (os-families)))))
(defn clean-stub-fixture
"This should allow basic tests to easily be run with another service."
[service account key & options]
(fn [f]
(with-compute-service [(apply compute-service service account key options)]
(doseq [node (nodes)]
(destroy-node (.getId node)))
(f))))
(use-fixtures :each (clean-stub-fixture "stub" "compute.clj" ""))
(deftest compute-service?-test
(is (compute-service? *compute*)))
(deftest as-compute-service-test
(is (compute-service? (compute-service "stub" "compute.clj" "")))
(is (compute-service? (as-compute-service *compute*)))
(is (compute-service? (as-compute-service (compute-context *compute*)))))
(deftest nodes-test
(is (create-node "fred" (build-template
*compute* {} )))
(is (= 1 (count (nodes))))
(is (= 1 (count (nodes-in-group "fred"))))
(suspend-nodes-in-group "fred")
(is (suspended? (first (nodes-in-group "fred"))))
(resume-nodes-in-group "fred")
(is (running? (first (nodes-in-group "fred"))))
(reboot-nodes-in-group "fred")
(is (running? (first (nodes-in-group "fred"))))
(is (create-nodes "fred" 2 (build-template
*compute* {} )))
(is (= 3 (count (nodes-in-group "fred"))))
(is (= "fred" (group (first (nodes)))))
(destroy-nodes-in-group "fred")
(is (terminated? (first (nodes-in-group "fred")))))
(deftest build-template-test
(let [service (compute-service "stub" "compute.clj" "")]
(testing "nullary"
(is (>= (-> (build-template service {:fastest true})
bean :hardware bean :processors first bean :cores)
8.0)))
(testing "one arg"
(is (> (-> (build-template service {:min-ram 512})
bean :hardware bean :ram)
512))
(let [credentials (org.jclouds.domain.Credentials. "user" "pwd")
f (juxt #(.identity %) #(.credential %))
template (build-template
service {:override-credentials-with credentials})
node (create-node "something" template service)]
(is (= (-> node bean :credentials f)
(f credentials))))
(let [user "fred"
f #(.identity %)
template (build-template service {:override-login-user-with user})
node (create-node "something" template service)]
(is (= (-> node bean :credentials f) user)))
(let [credential "fred"
f #(.credential %)
template (build-template
service {:override-login-credential-with credential})
node (create-node "something" template service)]
(is (= (-> node bean :credentials f) credential))))
(testing "enumerated"
(is (= OsFamily/CENTOS
(-> (build-template service {:os-family :centos})
bean :image bean :operatingSystem bean :family))))
(testing "varags"
(is (java.util.Arrays/equals
(int-array [22 8080])
(-> (build-template service {:inbound-ports [22 8080]})
bean :options bean :inboundPorts))))
(testing "invalid"
(is (thrown? Exception (build-template service {:xx :yy}))))))