Browse Source

COOK-418 added support for deploying java web applications with tomcat

Seth Chisamore 13 years ago
parent
commit
224195c6af
6 changed files with 249 additions and 5 deletions
  1. 93 2
      README.md
  2. 0 2
      metadata.json
  3. 3 1
      metadata.rb
  4. 107 0
      recipes/java.rb
  5. 37 0
      recipes/tomcat.rb
  6. 9 0
      templates/default/context.xml.erb

+ 93 - 2
README.md

@@ -4,8 +4,9 @@ Application cookbook
 This cookbook is initially designed to be able to describe and deploy web applications. Currently supported:
 
 * Rails
+* Java
 
-Other application stacks (PHP, DJango, JBoss, etc) will be supported as new recipes at a later date.
+Other application stacks (PHP, DJango, etc) will be supported as new recipes at a later date.
 
 This cookbook aims to provide primitives to install/deploy any kind of application driven entirely by data defined in an abstract way through a data bag.
 
@@ -20,6 +21,7 @@ The following Opscode cookbooks are dependencies:
 * runit
 * unicorn
 * apache2
+* tomcat
 
 The following are also dependencies, though the recipes are considered deprecated, may be useful for future development.
 
@@ -39,6 +41,11 @@ Searches the `apps` data bag and checks that a server role in the app exists on
 
 See below regarding the application data bag structure.
 
+java
+----
+
+Using the node's `run_state` that contains the current application in the search, this recipe will install required packages, set up the deployment scaffolding, creates servlet container configurations (including JDBC Data Source resources if required) and then performs a remote_file deploy.
+
 `passenger_apache2`
 -------------------
 
@@ -86,6 +93,13 @@ In order to manage running database migrations (rake db:migrate), you can use a
 
 Simply apply this role to the node's run list when it is time to run migrations, and the recipe will remove the role when done.
 
+tomcat
+-------
+
+Requires `tomcat` cookbook.
+
+Tomcat is installed, default attributes are set for the node and the app specific context.xml is symlinked over to Tomcat's context directory.
+
 unicorn
 -------
 
@@ -112,7 +126,7 @@ Sets up the application stack with Ruby Enterprise Edition, Nginx and Passenger.
 The recipe searches the apps data bag and then installs packages and gems, creates the nginx vhost config and enables the site, sets up the deployment scaffolding, and uses a revision-based deploy for the code. Database and memcached yaml files are written out as well, if required.
 
 ---
-Application Data Bag
+Application Data Bag (Rail's version)
 ====================
 
 The applications data bag expects certain values in order to configure parts of the recipe. Below is a paste of the JSON, where the value is a description of the key. Use your own values, as required. Note that this data bag is also used by the `database` cookbook, so it will contain database information as well. Items that may be ambiguous have an example.
@@ -201,6 +215,82 @@ Note about gems and packages, the version is optional. If specified, the version
       }
     }
 
+
+---
+Application Data Bag (Java version)
+====================
+
+The applications data bag expects certain values in order to configure parts of the recipe. Below is a paste of the JSON, where the value is a description of the key. Use your own values, as required. Note that this data bag is also used by the `database` cookbook, so it will contain database information as well. Items that may be ambiguous have an example.
+
+The application used in examples is named `my_app` and the environment is `production`. Most top-level keys are Arrays, and each top-level key has an entry that describes what it is for, followed by the example entries. Entries that are hashes themselves will have the description in the value.
+
+Note about "type": the recipes listed in the "type" will be included in the run list via `include_recipe` in the application default recipe based on the type matching one of the `server_roles` values.
+
+Note about `databases`, the data specified will be rendered as JNDI Datasource `Resources` in the `context.xml` file. In the `database` cookbook, this information is also used to set up privileges for the application user, and create the databases.
+
+Note about packages, the version is optional. If specified, the version will be passed as a parameter to the resource. Otherwise it will use the latest available version per the default `:install` action for the package provider.
+
+    {
+      "id": "my_app",
+      "server_roles": [
+        "application specific role(s), typically the name of the app, e.g., my_app",
+        "my_app"
+      ],
+      "type": {
+        "my_app": [
+          "recipes in this application cookbook to run for this role",
+          "java",
+          "tomcat"
+        ]
+      },
+      "database_slave_role": [
+        "name of the role used by database slaves, typically named after the app, 'my_app_database_slave'",
+        "my_app_database_slave"
+      ],
+      "database_master_role": [
+        "name of the role used by database master, typically named after the app 'my_app_database_master'",
+        "my_app_database_master"
+      ],
+      "wars": {
+        "production": {
+          "source": "source url of WAR file to deploy",
+          "checksum": "SHA256 (or portion thereof) of the WAR file to deploy"
+        }
+      },
+      "databases": {
+        "production": {
+          "max_active": "100",
+          "max_idle": "30",
+          "max_wait": "10000",
+          "username": "db_user",
+          "adapter": "mysql",
+          "driver": "com.mysql.jdbc.Driver",
+          "password": "awesome_password",
+          "database": "db_name_production"
+        }
+      },
+      "mysql_root_password": {
+        "production": "password for the root user in mysql"
+      },
+      "mysql_debian_password": {
+        "production": "password for the debian-sys-maint user on ubuntu/debian"
+      },
+      "mysql_repl_password": {
+        "production": "password for the 'repl' user for replication."
+      },
+      "snapshots_to_keep": {
+        "production": "if using EBS, integer of the number of snapshots we're going to keep for this environment."
+      },
+      "deploy_to": "path to deploy, e.g. /srv/my_app",
+      "owner": "owner for the application files when deployed",
+      "group": "group for the application files when deployed",
+      "packages": {
+        "package_name": "specific packages required for installation at the OS level to run the app like libraries and specific version, e.g.",
+        "curl": "7.19.5-1ubuntu2"
+      }
+    }
+
+
 ---
 Usage
 =====
@@ -250,6 +340,7 @@ License and Author
 
 Author:: Adam Jacob (<adam@opscode.com>)
 Author:: Joshua Timberman (<joshua@opscode.com>)
+Author:: Seth Chisamore (<schisamo@opscode.com>)
 
 Copyright 2009-2010, Opscode, Inc.
 

File diff suppressed because it is too large
+ 0 - 2
metadata.json


+ 3 - 1
metadata.rb

@@ -5,12 +5,14 @@ description      "Deploys and configures a variety of applications defined from
 long_description  IO.read(File.join(File.dirname(__FILE__), 'README.md'))
 version          "0.7.0"
 recipe           "application", "Loads application databags and selects recipes to use"
+recipe           "application::java", "Deploys a Java web application WAR specified in a data bag with the remote_file resource"
 recipe           "application::passenger-nginx", "Installs Ruby Enterprise with Passenger under Nginx"
 recipe           "application::passenger_apache2", "Sets up a deployed Rails application as a Passenger virtual host in Apache2"
 recipe           "application::rails", "Deploys a Rails application specified in a data bag with the deploy_revision resource"
 recipe           "application::rails_nginx_ree_passenger", "Deprecated recipe that deployed a rails application under Ruby Enterprise Edition, Passenger and Nginx"
+recipe           "application::tomcat", "Sets up the deployed Java application with Tomcat as the servlet container"
 recipe           "application::unicorn", "Sets up the deployed Rails application with Unicorn as the web server"
 
-%w{ ruby_enterprise passenger_enterprise runit unicorn apache2 passenger_apache2}.each do |cb|
+%w{ ruby_enterprise passenger_enterprise runit unicorn apache2 passenger_apache2 tomcat}.each do |cb|
   depends cb
 end

+ 107 - 0
recipes/java.rb

@@ -0,0 +1,107 @@
+#
+# Cookbook Name:: application
+# Recipe:: java
+#
+# Copyright 2010, Opscode, Inc.
+#
+# Licensed 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.
+#
+
+app = node.run_state[:current_app]
+
+###
+# You really most likely don't want to run this recipe from here - let the
+# default application recipe work it's mojo for you.
+###
+
+node.default[:apps][app['id']][node.app_environment][:run_migrations] = false
+
+## First, install any application specific packages
+if app['packages']
+  app['packages'].each do |pkg,ver|
+    package pkg do
+      action :install
+      version ver if ver && ver.length > 0
+    end
+  end
+end
+
+directory app['deploy_to'] do
+  owner app['owner']
+  group app['group']
+  mode '0755'
+  recursive true
+end
+
+directory "#{app['deploy_to']}/releases" do
+  owner app['owner']
+  group app['group']
+  mode '0755'
+  recursive true
+end
+
+directory "#{app['deploy_to']}/shared" do
+  owner app['owner']
+  group app['group']
+  mode '0755'
+  recursive true
+end
+
+%w{ log pids system }.each do |dir|
+
+  directory "#{app['deploy_to']}/shared/#{dir}" do
+    owner app['owner']
+    group app['group']
+    mode '0755'
+    recursive true
+  end
+
+end
+
+if app["database_master_role"]
+  dbm = nil
+  # If we are the database master
+  if node.run_list.roles.include?(app["database_master_role"][0])
+    dbm = node
+  else
+  # Find the database master
+    results = search(:node, "run_list:role\\[#{app["database_master_role"][0]}\\] AND app_environment:#{node[:app_environment]}", nil, 0, 1)
+    rows = results[0]
+    if rows.length == 1
+      dbm = rows[0]
+    end
+  end
+  
+  # Assuming we have one...
+  if dbm
+    template "#{app['deploy_to']}/shared/#{app['id']}.xml" do
+      source "context.xml.erb"
+      owner app["owner"]
+      group app["group"]
+      mode "644"
+      variables(
+        :host => dbm['fqdn'],
+        :database => app['databases'][node.app_environment],
+        :war => "#{app['deploy_to']}/releases/#{app['war'][node.app_environment]['checksum']}.war"
+      )
+    end
+  end
+end
+
+## Then, deploy
+remote_file app['id'] do
+  path "#{app['deploy_to']}/releases/#{app['war'][node.app_environment]['checksum']}.war"
+  source app['war'][node.app_environment]['source']
+  mode "0644"
+  checksum app['war'][node.app_environment]['checksum']
+end

+ 37 - 0
recipes/tomcat.rb

@@ -0,0 +1,37 @@
+#
+# Cookbook Name:: application
+# Recipe:: tomcat 
+#
+# Copyright 2010, Opscode, Inc.
+#
+# Licensed 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.
+#
+
+app = node.run_state[:current_app]
+
+include_recipe "tomcat"
+
+# remove ROOT application
+# TODO create a LWRP to enable/disable tomcat apps
+directory "#{node['tomcat']['webapp_dir']}/ROOT" do
+  recursive true
+  action :delete
+  not_if "test -L #{node['tomcat']['context_dir']}/ROOT.xml"
+end
+link "#{node['tomcat']['context_dir']}/ROOT.xml" do
+  to "#{app['deploy_to']}/shared/#{app['id']}.xml"
+  notifies :restart, resources(:service => "tomcat")
+end
+
+d = resources(:remote_file => app['id'])
+d.notifies :restart, resources(:service => "tomcat")

+ 9 - 0
templates/default/context.xml.erb

@@ -0,0 +1,9 @@
+<Context docBase="<%= @war %>" path="/"
+        debug="5" reloadable="true" crossContext="true" allowLinking="true">
+  <Environment name="appEnvironment" value="<%= node['app_environment'] %>"
+          type="java.lang.String" override="false"/>
+  <Resource name="jdbc/<%= @database['database'] %>" auth="Container" type="javax.sql.DataSource"
+               maxActive="<%= @database['max_active'] %>" maxIdle="<%= @database['max_idle'] %>" maxWait="<%= @database['max_wait'] %>"
+               username="<%= @database['username'] %>" password="<%= @database['password'] %>" driverClassName="<%= @database['driver'] %>"
+               url="jdbc:<%= @database['adapter'] %>://<%= @host %>:3306/<%= @database['database'] %>"/>
+</Context>

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