Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Integration of various features #51

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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,6 @@ pom.xml.asc
lib
classes
/target
.iml
.idea/
*.iml
79 changes: 78 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,13 @@ using the following command:

$ lein beanstalk deploy development

#### Custom WAR

If you're so inclined, you can also deploy a custom WAR by passing the file
to the deploy command.

$ lein beanstalk deploy development target/myproject.war

### Info

To get information about the application itself run
Expand Down Expand Up @@ -186,6 +193,35 @@ environment
```
By default the CNAME prefix is `<project-name>-<environment>`.

### Aliases

If you deploy multiple services to Elastic Beanstalk, you'll realize
that environment names must be unique across all of your applications.
Aliases allow you to refer to a standard name across your projects,
while deploying to an environment named suing either the defaults or
what is supplied for `:name`.


Below are the defaults.
```clojure
:aws {:beanstalk {:environments [{:alias "development"
:name "myproject-dev"}
{:alias "staging"
:name "myproject-staging"}
{:alias "production"
:name "myproject"}]
...}
...}
```

You may refer to either the alias or the name when running lein-beanstalk
commands.

$ lein beanstalk deploy development
$ lein beanstalk deploy myproject-dev

Both of the above commands deploy to `myproject-dev.elasticbeanstalk.com`

### Environment Variables

You can specify environment variables that will be added to the system
Expand All @@ -203,6 +239,44 @@ If the environment variable name is a keyword, it is upper-cased and
underscores ("_") are substituted for dashes ("-"). e.g.
`:database-url` becomes `"DATABASE_URL"`.

### Choosing an alternate stack

The default stack chosen is 32bit Amazon Linux running Tomcat 7. You
can customize the stack used:

:aws {:beanstalk {:stack-name "64bit Amazon Linux running Tomcat 7"}}

The [full list][4] of available stacks that you are likely to use:

* 32bit Amazon Linux running Tomcat 7
* 64bit Amazon Linux running Tomcat 7
* 32bit Amazon Linux running Tomcat 6
* 64bit Amazon Linux running Tomcat 6
=======
### Configuring instance type, autoscaling, VPC, SSH, AMI, SSL

You can customize many [other settings][5] on a per beanstalk environment
basis with an options key:

:aws
{:beanstalk
{:environments
[{:name "dev"
:options {"aws:autoscaling:asg" {"MinSize" "1" "MaxSize" "1"}
"aws:autoscaling:launchconfiguration" {"InstanceType" "m1.medium"
"EC2KeyName" "mykey"
"ImageId" "ami-cbab67a2"}}}]}}

### Configuring the application tier ###

Amazon recently added support for [worker tiers][6], which are useful for running background tasks.
The default stack is built for a web application. To deploy as a worker, supply the following options
for the `:app-tier` key.

:aws
{:beanstalk
{:app-tier {:name "Worker" :type "SQS/HTTP" :version "1.0"}}}

### S3 Buckets

[Amazon Elastic Beanstalk][1] uses
Expand Down Expand Up @@ -230,7 +304,7 @@ The following regions are recognized:
* `eu-west-1`
* `us-west-1`
* `us-west-2`

* `eu-central-1`

## Trouble-Shooting

Expand All @@ -247,3 +321,6 @@ application. e.g. for Compojure add
[1]: http://aws.amazon.com/elasticbeanstalk
[2]: http://aws.amazon.com
[3]: http://aws.amazon.com/s3
[4]: http://docs.aws.amazon.com/elasticbeanstalk/latest/APIReference/API_ListAvailableSolutionStacks.html
[5]: http://docs.aws.amazon.com/elasticbeanstalk/latest/dg/command-options.html
[6]: http://aws.typepad.com/aws/2013/12/background-task-handling-for-aws-elastic-beanstalk.html
11 changes: 8 additions & 3 deletions project.clj
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
(defproject lein-beanstalk "0.2.7"
:description "Leiningen plugin for Amazon's Elastic Beanstalk"
:url "https://github.com/weavejester/lein-beanstalk"
:dependencies [[org.clojure/clojure "1.2.1"]
[com.amazonaws/aws-java-sdk "1.3.31"]
[lein-ring "0.8.2"]]
:dependencies [[com.amazonaws/aws-java-sdk "1.10.5.1"]

;; The dependencies prevent the runtime error:
;; NoSuchMethodError JsonFactory.requiresPropertyOrdering()Z
[com.fasterxml.jackson.core/jackson-core "2.2.3"]
[com.fasterxml.jackson.core/jackson-databind "2.2.3"]

[lein-ring "0.9.6"]]
:eval-in-leiningen true)
30 changes: 21 additions & 9 deletions src/leiningen/beanstalk.clj
Original file line number Diff line number Diff line change
Expand Up @@ -9,25 +9,28 @@
(defn default-environments
[project]
(let [project-name (:name project)]
[{:name "development" :cname-prefix (str project-name "-dev")}
{:name "staging" :cname-prefix (str project-name "-staging")}
{:name "production" :cname-prefix project-name}]))
[{:alias "development" :name (str project-name "-dev") :cname-prefix (str project-name "-dev")}
{:alias "staging" :name (str project-name "-staging") :cname-prefix (str project-name "-staging")}
{:alias "production" :name project-name :cname-prefix project-name}]))

(defn project-environments
[project]
(for [env (-> project :aws :beanstalk :environments)]
(if (map? env)
(merge {:cname-prefix (str (:name project) "-" (:name env))} env)
(merge {:cname-prefix (str (:name project) "-" (or (:alias env) (:name env)))}
(merge {:name (or (:name env) (str (:name project) "-" (:alias env)))} env))
{:name env, :cname-prefix (str (:name project) "-" env)})))

(defn get-project-env [project env-name]
(->> (or (seq (project-environments project))
(default-environments project))
(filter #(= (:name %) env-name))
(filter #(or (= (:name %) env-name) (= (:alias %) env-name)))
(first)))

(defn war-filename [project]
(str (:name project) "-" (aws/app-version project) ".war"))
(if (nil? (:name project))
(str (clojure.string/replace project #".war" "") "-" (aws/app-version project) ".war")
(str (:name project) "-" (aws/app-version project) ".war")))

(defn deploy
"Deploy the current project to Amazon Elastic Beanstalk."
Expand All @@ -40,16 +43,25 @@
(aws/s3-upload-file project path)
(aws/create-app-version project filename)
(aws/deploy-environment project env))
(println (str "Environment '" env-name "' not defined!"))))
([project env-name war-file]
(if-let [env (get-project-env project env-name)]
(let [filename (war-filename war-file)
path war-file]
(aws/s3-upload-file project path filename)
(aws/create-app-version project filename)
(aws/deploy-environment project env))
(println (str "Environment '" env-name "' not defined!")))))

(defn terminate
"Terminate the environment for the current project on Amazon Elastic Beanstalk."
([project]
(println "Usage: lein beanstalk terminate <environment>"))
([project env-name]
(if-not (get-project-env project env-name)
(println (str "Environment '" env-name "' not in project.clj"))
(aws/terminate-environment project env-name))))
(let [name (:name (get-project-env project env-name))]
(if-not name
(println (str "Environment '" name "' not in project.clj"))
(aws/terminate-environment project name)))))

(def app-info-indent "\n ")

Expand Down
75 changes: 57 additions & 18 deletions src/leiningen/beanstalk/aws.clj
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
com.amazonaws.services.elasticbeanstalk.model.UpdateEnvironmentRequest
com.amazonaws.services.elasticbeanstalk.model.S3Location
com.amazonaws.services.elasticbeanstalk.model.TerminateEnvironmentRequest
com.amazonaws.services.elasticbeanstalk.model.EnvironmentTier
com.amazonaws.services.s3.AmazonS3Client
com.amazonaws.services.s3.model.Region))

Expand Down Expand Up @@ -51,8 +52,20 @@
(or (-> project :aws :beanstalk :app-name)
(:name project)))

(defn app-tier [project]
(doto (EnvironmentTier.)
(.setName (or (-> project :aws :beanstalk :app-tier :name)
"WebServer"))
(.setType (or (-> project :aws :beanstalk :app-tier :type)
"Standard"))
(.setVersion (or (-> project :aws :beanstalk :app-tier :version)
"1.0"))))

(defn app-version [project]
(str (:version project) "-" current-timestamp))
(if (nil? (:version project))
(str current-timestamp)
(str (:version project) "-" current-timestamp)))


(defn s3-bucket-name [project]
(or (-> project :aws :beanstalk :s3-bucket)
Expand All @@ -65,6 +78,7 @@
{:us-east-1 (ep "s3.amazonaws.com" "US_Standard")
:us-west-1 (ep "s3-us-west-1.amazonaws.com" "US_West")
:us-west-2 (ep "s3-us-west-2.amazonaws.com" "US_West_2")
:eu-central-1 (ep "s3-eu-central-1.amazonaws.com" "EU_Frankfurt")
:eu-west-1 (ep "s3-eu-west-1.amazonaws.com" "EU_Ireland")
:ap-southeast-1 (ep "s3-ap-southeast-1.amazonaws.com" "AP_Singapore")
:ap-southeast-2 (ep "s3-ap-southeast-2.amazonaws.com" "AP_Sydney")
Expand All @@ -75,6 +89,7 @@
{:us-east-1 "elasticbeanstalk.us-east-1.amazonaws.com"
:us-west-1 "elasticbeanstalk.us-west-1.amazonaws.com"
:us-west-2 "elasticbeanstalk.us-west-2.amazonaws.com"
:eu-central-1 "elasticbeanstalk.eu-central-1.amazonaws.com"
:eu-west-1 "elasticbeanstalk.eu-west-1.amazonaws.com"
:ap-southeast-1 "elasticbeanstalk.ap-southeast-1.amazonaws.com"
:ap-southeast-2 "elasticbeanstalk.ap-southeast-2.amazonaws.com"
Expand All @@ -88,15 +103,25 @@
(when-not (.doesBucketExist client bucket)
(.createBucket client bucket region)))

(defn s3-upload-file [project filepath]
(let [bucket (s3-bucket-name project)
file (io/file filepath)
ep-desc (project-endpoint project s3-endpoints)]
(doto (AmazonS3Client. (credentials project))
(.setEndpoint (:ep ep-desc))
(create-bucket bucket (:region ep-desc))
(.putObject bucket (.getName file) file))
(println "Uploaded" (.getName file) "to S3 Bucket")))
(defn s3-upload-file
([project filepath]
(let [bucket (s3-bucket-name project)
file (io/file filepath)
ep-desc (project-endpoint project s3-endpoints)]
(doto (AmazonS3Client. (credentials project))
(.setEndpoint (:ep ep-desc))
(create-bucket bucket (:region ep-desc))
(.putObject bucket (.getName file) file))
(println "Uploaded" (.getName file) "to S3 Bucket")))
([project filepath filename]
(let [bucket (s3-bucket-name project)
file (io/file filepath)
ep-desc (project-endpoint project s3-endpoints)]
(doto (AmazonS3Client. (credentials project))
(.setEndpoint (:ep ep-desc))
(create-bucket bucket (:region ep-desc))
(.putObject bucket filename file))
(println "Uploaded" filename "to S3 Bucket"))))

(defn- beanstalk-client [project]
(doto (AWSElasticBeanstalkClient. (credentials project))
Expand Down Expand Up @@ -143,6 +168,7 @@

(defn env-var-options [project options]
(for [[key value] (merge (default-env-vars project)
(-> project :aws :beanstalk :env)
(:env options))]
(ConfigurationOptionSetting.
"aws:elasticbeanstalk:application:environment"
Expand All @@ -151,19 +177,32 @@
key)
value)))

(defn extra-options
[options]
(apply concat
(for [[namespace keyvals] options]
(for [[key value] keyvals]
(ConfigurationOptionSetting. namespace key value)))))

(defn create-environment [project env]
(println (str "Creating '" (:name env) "' environment")
"(this may take several minutes)")
(.createEnvironment
(beanstalk-client project)
(doto (CreateEnvironmentRequest.)
(.setApplicationName (app-name project))
(.setEnvironmentName (:name env))
(.setVersionLabel (app-version project))
(.setOptionSettings (env-var-options project env))
(.setCNAMEPrefix (:cname-prefix env))
(.setSolutionStackName (or (-> project :aws :beanstalk :stack-name)
"32bit Amazon Linux running Tomcat 7")))))
(let [request (CreateEnvironmentRequest.)]
(doto request
(.setApplicationName (app-name project))
(.setEnvironmentName (:name env))
(.setTier (app-tier project))
(.setVersionLabel (app-version project))
(.setOptionSettings (concat (env-var-options project env)
(extra-options (merge (-> project :aws :beanstalk :options)
(:options env)))))
(.setSolutionStackName (or (-> project :aws :beanstalk :stack-name)
"32bit Amazon Linux running Tomcat 7")))
(if (= (.getName (.getTier request)) "WebServer")
(.setCNAMEPrefix request (:cname-prefix env)))
request)))

(defn update-environment-settings [project env options]
(.updateEnvironment
Expand Down