Skip to content
This repository has been archived by the owner on Jan 26, 2022. It is now read-only.

Initial PHP support from AppFog #105

Closed
wants to merge 17 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions AUTHORS
Expand Up @@ -32,3 +32,4 @@
- Vadim Spivak { email: vspivak@vmware.com } - Vadim Spivak { email: vspivak@vmware.com }
- Wilson Bilkovich { email: wilsonb@vmware.com } - Wilson Bilkovich { email: wilsonb@vmware.com }
- Woon Jung { email: whj@vmware.com } - Woon Jung { email: whj@vmware.com }
- Lucas Carlson { email: lucas@phpfog.com, company: "PHP Fog" }
4 changes: 2 additions & 2 deletions cloud_controller/app/models/app.rb
Expand Up @@ -19,8 +19,8 @@ class App < ActiveRecord::Base


AppStates = %w[STOPPED STARTED] AppStates = %w[STOPPED STARTED]
PackageStates = %w[PENDING STAGED FAILED] PackageStates = %w[PENDING STAGED FAILED]
Runtimes = %w[ruby18 ruby19 java node erlangR14B02] Runtimes = %w[ruby18 ruby19 java node php erlangR14B02]
Frameworks = %w[sinatra rails3 java_web spring grails node otp_rebar lift unknown] Frameworks = %w[sinatra rails3 java_web spring grails node php otp_rebar lift unknown]


validates_presence_of :name, :framework, :runtime validates_presence_of :name, :framework, :runtime


Expand Down
@@ -0,0 +1 @@
<?php phpinfo(); ?>
55 changes: 55 additions & 0 deletions cloud_controller/spec/staging/php_spec.rb
@@ -0,0 +1,55 @@
require 'spec_helper'

describe "A PHP application being staged" do
before do
app_fixture :phpinfo
end

it "is packaged with a startup script" do
stage :php do |staged_dir|
executable = '%VCAP_LOCAL_RUNTIME%'
start_script = File.join(staged_dir, 'startup')
start_script.should be_executable_file
webapp_root = staged_dir.join('app')
webapp_root.should be_directory
script_body = File.read(start_script)
script_body.should == <<-EXPECTED
#!/bin/bash
env > env.log
ruby resources/generate_apache_conf $VCAP_APP_PORT $HOME $VCAP_SERVICES 512m
cd apache
bash ./start.sh > ../logs/stdout.log 2> ../logs/stderr.log &
STARTED=$!
echo "$STARTED" >> ../run.pid
echo "#!/bin/bash" >> ../stop
echo "kill -9 $STARTED" >> ../stop
echo "kill -9 $PPID" >> ../stop
chmod 755 ../stop
wait $STARTED
EXPECTED
end
end

it "requests the specified amount of memory from PHP" do
environment = { :resources => {:memory => 256} }
stage(:php, environment) do |staged_dir|
start_script = File.join(staged_dir, 'startup')
start_script.should be_executable_file
script_body = File.read(start_script)
script_body.should == <<-EXPECTED
#!/bin/bash
env > env.log
ruby resources/generate_apache_conf $VCAP_APP_PORT $HOME $VCAP_SERVICES 256m
cd apache
bash ./start.sh > ../logs/stdout.log 2> ../logs/stderr.log &
STARTED=$!
echo "$STARTED" >> ../run.pid
echo "#!/bin/bash" >> ../stop
echo "kill -9 $STARTED" >> ../stop
echo "kill -9 $PPID" >> ../stop
chmod 755 ../stop
wait $STARTED
EXPECTED
end
end
end
17 changes: 17 additions & 0 deletions cloud_controller/staging/apache_common/apache.rb
@@ -0,0 +1,17 @@
require 'nokogiri'
require 'fileutils'

class Apache
def self.resource_dir
File.join(File.dirname(__FILE__), 'resources')
end

def self.prepare(dir)
FileUtils.cp_r(resource_dir, dir)
output = %x[cd #{dir}; unzip -q resources/apache.zip]
raise "Could not unpack Apache: #{output}" unless $? == 0
FileUtils.rm(File.join(dir, "resources", "apache.zip"))
dir
end

end
Binary file not shown.
@@ -0,0 +1,88 @@
#!/usr/bin/env ruby

# This script executes at app startup time because the
# actual appserver port must be in the apache conf.

apache_port = ARGV[0]
base_dir = ARGV[1]
vcap_services = ARGV[2]
php_ram = ARGV[3]
exit 1 unless apache_port && base_dir && vcap_services && php_ram

require 'erb'
output_path = "apache/sites-available/default"

template = <<-ERB
<VirtualHost *:<%= apache_port %>>
ServerAdmin webmaster@localhost
DocumentRoot <%= base_dir %>/app
ErrorLog ${APACHE_LOG_DIR}/error.log
CustomLog ${APACHE_LOG_DIR}/access.log combined
SetEnv VCAP_SERVICES <%= vcap_services %>
</VirtualHost>
ERB

template = ERB.new(template)
File.open(File.expand_path(output_path), 'wb') do |file|
file.puts(template.result(binding))
end

system "cd apache/sites-enabled && ln -s ../sites-available/default 000-default && cd ../.."

output_path = 'apache/envvars'

template = <<-ERB
unset HOME
export APACHE_BASEDIR=<%= base_dir %>/apache
export APACHE_RUN_USER=www-data
export APACHE_RUN_GROUP=www-data
export APACHE_PID_FILE=<%= base_dir %>/run.pid
export APACHE_RUN_DIR=$APACHE_BASEDIR/run/apache2
export APACHE_LOCK_DIR=$APACHE_BASEDIR/lock/apache2
export APACHE_LOG_DIR=<%= base_dir %>/logs
export LANG=C
export PHP_INI_SCAN_DIR=$APACHE_BASEDIR/php
ERB

template = ERB.new(template)
File.open(File.expand_path(output_path), 'wb') do |file|
file.puts(template.result(binding))
end

output_path = 'apache/ports.conf'

template = <<-ERB
NameVirtualHost *:<%= apache_port %>
Listen <%= apache_port %>
ERB

template = ERB.new(template)
File.open(File.expand_path(output_path), 'wb') do |file|
file.puts(template.result(binding))
end

output_path = 'apache/start.sh'

template = <<-ERB
#!/bin/bash
source <%= base_dir %>/apache/envvars
/usr/sbin/apache2 -d <%= base_dir %>/apache -f <%= base_dir %>/apache/apache2.conf -D FOREGROUND
ERB

template = ERB.new(template)
File.open(File.expand_path(output_path), 'wb') do |file|
file.puts(template.result(binding))
end

`cp -r /etc/php5/conf.d/* apache/php`

output_path = 'apache/php/memory.ini'

template = <<-ERB
memory_limit = <%= php_ram %>
ERB

template = ERB.new(template)
File.open(File.expand_path(output_path), 'wb') do |file|
file.puts(template.result(binding))
end
22 changes: 22 additions & 0 deletions cloud_controller/staging/manifests/php.yml
@@ -0,0 +1,22 @@
---
name: "php"
runtimes:
- "php":
description: "PHP 5"
version: "5.3"
executable: "php"
default: true
app_servers:
- "apache":
description: "Apache"
executable: "false"
default: true
detection:
- "*.php": true
staged_services:
- "name": "mysql"
"version": "*"
- "name": "postgresql"
"version": "*"

# vim: filetype=yaml
41 changes: 41 additions & 0 deletions cloud_controller/staging/php/plugin.rb
@@ -0,0 +1,41 @@
require File.expand_path('../../apache_common/apache', __FILE__)

class PhpPlugin < StagingPlugin
def framework
'php'
end

def stage_application
Dir.chdir(destination_directory) do
create_app_directories
Apache.prepare(destination_directory)
copy_source_files
create_startup_script
end
end

# The Apache start script runs from the root of the staged application.
def change_directory_for_start
"cd apache"
end

def start_command
"bash ./start.sh"
end

private

def startup_script
vars = environment_hash
generate_startup_script(vars) do
<<-PHPEOF
env > env.log
ruby resources/generate_apache_conf $VCAP_APP_PORT $HOME $VCAP_SERVICES #{application_memory}m
PHPEOF
end
end

def apache_server_root
File.join(destination_directory, 'apache')
end
end
7 changes: 7 additions & 0 deletions cloud_controller/staging/php/stage
@@ -0,0 +1,7 @@
#!/usr/bin/env ruby
require File.expand_path('../../common', __FILE__)
plugin_class = StagingPlugin.load_plugin_for('php')
plugin_class.validate_arguments!
plugin_class.new(*ARGV).stage_application

# vim: ts=2 sw=2 filetype=ruby
5 changes: 5 additions & 0 deletions dea/config/dea.yml
Expand Up @@ -43,6 +43,11 @@ runtimes:
version: 1.6.0 version: 1.6.0
version_flag: '-version' version_flag: '-version'
environment: environment:
php:
executable: php
version: 5.3.[2-6]
version_flag: '-v'
environment:
erlangR14B02: erlangR14B02:
executable: /var/vcap/runtimes/erlang-R14B02/bin/erl executable: /var/vcap/runtimes/erlang-R14B02/bin/erl
version: ".* 5.8.3" version: ".* 5.8.3"
Expand Down
51 changes: 51 additions & 0 deletions docs/php.md
@@ -0,0 +1,51 @@
# PHP Support

## Architecture

PHP applications are deployed using Apache and mod_php. For each CloudFoundry instance of the application, an Apache instance is started.

## Demo: Installing Wordpress ##
The Wordpress CMS can be run using CloudFoundry PHP support with very minimal changes.

Steps to get the application to run:

1. <code>curl -O http://wordpress.org/latest.tar.gz</code>
2. <code>tar -xzf latest.tar.gz</code>
3. <code>rm latest.tar.gz</code>
4. <code>cd wordpress</code>
5. <code>echo "<?php" > wp-salt.php</code>
6. <code>curl https://api.wordpress.org/secret-key/1.1/salt/ >> wp-salt.php</code>
7. Create wp-config.php, and set it to:

<?php
$services = getenv("VCAP_SERVICES");
$services_json = json_decode($services,true);
$mysql_config = $services_json["mysql-5.1"][0]["credentials"];

// ** MySQL settings from resource descriptor ** //
define('DB_NAME', $mysql_config["name"]);
define('DB_USER', $mysql_config["user"]);
define('DB_PASSWORD', $mysql_config["password"]);
define('DB_HOST', $mysql_config["hostname"]);
define('DB_PORT', $mysql_config["port"]);

define('DB_CHARSET', 'utf8');
define('DB_COLLATE', '');
define ('WPLANG', '');
define('WP_DEBUG', false);

require('wp-salt.php');

$table_prefix = 'wp_';

/* That's all, stop editing! Happy blogging. */

/** Absolute path to the WordPress directory. */
if ( !defined('ABSPATH') )
define('ABSPATH', dirname(__FILE__) . '/');

/** Sets up WordPress vars and included files. */
require_once(ABSPATH . 'wp-settings.php');
8. <code>vmc push wordpresscf --url wordpresscf.vcap.me -n</code>
9. <code>vmc create-service mysql --bind wordpresscf</code>
10. Visit http://wordpresscf.vcap.me and enjoy your Wordpress install!
36 changes: 36 additions & 0 deletions setup/vcap_setup
Expand Up @@ -255,6 +255,38 @@ if [[ $DEA == true || $ALL == true ]]; then
rm -fr node-v$NODE_VERSION rm -fr node-v$NODE_VERSION
fi fi


# For PHP
if [[ $PLATFORM == 'Linux' ]]; then
apt-get install -qqy libpcre3 libpcre3-dev apache2 php5 php5-mysql php5-mysqli php5-pgsql php5-dev php-pear php5-gd libapr1 libaprutil1 php5-common php5-curl php5-mcrypt php5-imagick php5-xmlrpc zend-framework php5-imap

/etc/init.d/apache2 stop
update-rc.d -f apache2 remove

pear channel-discover pear.phpunit.de
pear channel-discover pear.symfony-project.com
pear channel-discover components.ez.no
pear install phpunit/PHPUnit
yes | pecl install apc
yes | pecl install memcache
yes | pecl install mongo
git clone git://github.com/owlient/phpredis.git
cd phpredis
phpize
./configure
make && make install
cd ..
rm -rf phpredis

echo "extension=apc.so" > /etc/php5/apache2/conf.d/cf.ini
echo "extension=memcache.so" >> /etc/php5/apache2/conf.d/cf.ini
echo "extension=mongo.so" >> /etc/php5/apache2/conf.d/cf.ini
echo "extension=mysql.so" >> /etc/php5/apache2/conf.d/cf.ini
echo "extensions=pgsql.so" >> /etc/php5/apache2/conf.d/cf.ini

elif [[ $PLATFORM == 'MacOSX' ]]; then
echo -e "\033[31mFIXME: Implement for MacOSX \033[0m"
fi

ERLANG_VERSION=R14B02 ERLANG_VERSION=R14B02


if [ ! -d "/var/vcap/runtimes/erlang-$ERLANG_VERSION" ]; then if [ ! -d "/var/vcap/runtimes/erlang-$ERLANG_VERSION" ]; then
Expand Down Expand Up @@ -293,6 +325,10 @@ if [[ $DEA == true || $ALL == true ]]; then
node -v node -v
echo "" echo ""


echo -e "\nPHP should now be installed -->"
php -v
echo ""

echo -e "\nErlang should now be installed -->" echo -e "\nErlang should now be installed -->"
erl -version erl -version
echo "" echo ""
Expand Down