Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Initial PHP support from AppFog #105

Closed
wants to merge 17 commits into from

2 participants

Lucas Carlson Patrick Bozeman
Lucas Carlson

Initial Cloud Foundry support for PHP using standard Apache + mod_php tools. The technology for these pull requests comes out of the knowledge and expertise of running phpfog.com

Related pull requests:

cloudfoundry/vmc#29

cloudfoundry-attic/vcap-tests#6

Patrick Bozeman

Pulled.

Patrick Bozeman pbozeman closed this August 25, 2011
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
This page is out of date. Refresh to see the latest.
1  AUTHORS
@@ -32,3 +32,4 @@
32 32
 - Vadim Spivak              { email: vspivak@vmware.com }
33 33
 - Wilson Bilkovich          { email: wilsonb@vmware.com }
34 34
 - Woon Jung                 { email: whj@vmware.com }
  35
+- Lucas Carlson				{ email: lucas@phpfog.com, company: "PHP Fog" }
4  cloud_controller/app/models/app.rb
@@ -19,8 +19,8 @@ class App < ActiveRecord::Base
19 19
 
20 20
   AppStates = %w[STOPPED STARTED]
21 21
   PackageStates = %w[PENDING STAGED FAILED]
22  
-  Runtimes = %w[ruby18 ruby19 java node erlangR14B02]
23  
-  Frameworks = %w[sinatra rails3 java_web spring grails node otp_rebar lift unknown]
  22
+  Runtimes = %w[ruby18 ruby19 java node php erlangR14B02]
  23
+  Frameworks = %w[sinatra rails3 java_web spring grails node php otp_rebar lift unknown]
24 24
 
25 25
   validates_presence_of :name, :framework, :runtime
26 26
 
1  cloud_controller/spec/fixtures/apps/phpinfo/source/index.php
... ...
@@ -0,0 +1 @@
  1
+<?php phpinfo(); ?>
55  cloud_controller/spec/staging/php_spec.rb
... ...
@@ -0,0 +1,55 @@
  1
+require 'spec_helper'
  2
+
  3
+describe "A PHP application being staged" do
  4
+  before do
  5
+    app_fixture :phpinfo
  6
+  end
  7
+
  8
+  it "is packaged with a startup script" do
  9
+    stage :php do |staged_dir|
  10
+      executable = '%VCAP_LOCAL_RUNTIME%'
  11
+      start_script = File.join(staged_dir, 'startup')
  12
+      start_script.should be_executable_file
  13
+      webapp_root = staged_dir.join('app')
  14
+      webapp_root.should be_directory
  15
+      script_body = File.read(start_script)
  16
+      script_body.should == <<-EXPECTED
  17
+#!/bin/bash
  18
+env > env.log
  19
+ruby resources/generate_apache_conf $VCAP_APP_PORT $HOME $VCAP_SERVICES 512m
  20
+cd apache
  21
+bash ./start.sh > ../logs/stdout.log 2> ../logs/stderr.log &
  22
+STARTED=$!
  23
+echo "$STARTED" >> ../run.pid
  24
+echo "#!/bin/bash" >> ../stop
  25
+echo "kill -9 $STARTED" >> ../stop
  26
+echo "kill -9 $PPID" >> ../stop
  27
+chmod 755 ../stop
  28
+wait $STARTED
  29
+      EXPECTED
  30
+    end
  31
+  end
  32
+
  33
+  it "requests the specified amount of memory from PHP" do
  34
+    environment = { :resources => {:memory => 256} }
  35
+    stage(:php, environment) do |staged_dir|
  36
+      start_script = File.join(staged_dir, 'startup')
  37
+      start_script.should be_executable_file
  38
+      script_body = File.read(start_script)
  39
+      script_body.should == <<-EXPECTED
  40
+#!/bin/bash
  41
+env > env.log
  42
+ruby resources/generate_apache_conf $VCAP_APP_PORT $HOME $VCAP_SERVICES 256m
  43
+cd apache
  44
+bash ./start.sh > ../logs/stdout.log 2> ../logs/stderr.log &
  45
+STARTED=$!
  46
+echo "$STARTED" >> ../run.pid
  47
+echo "#!/bin/bash" >> ../stop
  48
+echo "kill -9 $STARTED" >> ../stop
  49
+echo "kill -9 $PPID" >> ../stop
  50
+chmod 755 ../stop
  51
+wait $STARTED
  52
+      EXPECTED
  53
+    end
  54
+  end
  55
+end
17  cloud_controller/staging/apache_common/apache.rb
... ...
@@ -0,0 +1,17 @@
  1
+require 'nokogiri'
  2
+require 'fileutils'
  3
+
  4
+class Apache
  5
+  def self.resource_dir
  6
+    File.join(File.dirname(__FILE__), 'resources')
  7
+  end
  8
+
  9
+  def self.prepare(dir)
  10
+    FileUtils.cp_r(resource_dir, dir)
  11
+    output = %x[cd #{dir}; unzip -q resources/apache.zip]
  12
+    raise "Could not unpack Apache: #{output}" unless $? == 0
  13
+    FileUtils.rm(File.join(dir, "resources", "apache.zip"))
  14
+    dir
  15
+  end
  16
+
  17
+end
BIN  cloud_controller/staging/apache_common/resources/apache.zip
Binary file not shown
88  cloud_controller/staging/apache_common/resources/generate_apache_conf
... ...
@@ -0,0 +1,88 @@
  1
+#!/usr/bin/env ruby
  2
+
  3
+# This script executes at app startup time because the
  4
+# actual appserver port must be in the apache conf.
  5
+
  6
+apache_port = ARGV[0]
  7
+base_dir = ARGV[1]
  8
+vcap_services = ARGV[2]
  9
+php_ram = ARGV[3]
  10
+exit 1 unless apache_port && base_dir && vcap_services && php_ram
  11
+
  12
+require 'erb'
  13
+output_path = "apache/sites-available/default"
  14
+
  15
+template = <<-ERB
  16
+<VirtualHost *:<%= apache_port %>>
  17
+  ServerAdmin webmaster@localhost
  18
+  DocumentRoot <%= base_dir %>/app
  19
+  ErrorLog ${APACHE_LOG_DIR}/error.log
  20
+  CustomLog ${APACHE_LOG_DIR}/access.log combined
  21
+  SetEnv VCAP_SERVICES <%= vcap_services %>
  22
+</VirtualHost>
  23
+ERB
  24
+
  25
+template = ERB.new(template)
  26
+File.open(File.expand_path(output_path), 'wb') do |file|
  27
+  file.puts(template.result(binding))
  28
+end
  29
+
  30
+system "cd apache/sites-enabled && ln -s ../sites-available/default 000-default && cd ../.."
  31
+
  32
+output_path = 'apache/envvars'
  33
+
  34
+template = <<-ERB
  35
+unset HOME
  36
+export APACHE_BASEDIR=<%= base_dir %>/apache
  37
+export APACHE_RUN_USER=www-data
  38
+export APACHE_RUN_GROUP=www-data
  39
+export APACHE_PID_FILE=<%= base_dir %>/run.pid
  40
+export APACHE_RUN_DIR=$APACHE_BASEDIR/run/apache2
  41
+export APACHE_LOCK_DIR=$APACHE_BASEDIR/lock/apache2
  42
+export APACHE_LOG_DIR=<%= base_dir %>/logs
  43
+export LANG=C
  44
+export PHP_INI_SCAN_DIR=$APACHE_BASEDIR/php
  45
+ERB
  46
+
  47
+template = ERB.new(template)
  48
+File.open(File.expand_path(output_path), 'wb') do |file|
  49
+  file.puts(template.result(binding))
  50
+end
  51
+
  52
+output_path = 'apache/ports.conf'
  53
+
  54
+template = <<-ERB
  55
+NameVirtualHost *:<%= apache_port %>
  56
+Listen <%= apache_port %>
  57
+ERB
  58
+
  59
+template = ERB.new(template)
  60
+File.open(File.expand_path(output_path), 'wb') do |file|
  61
+  file.puts(template.result(binding))
  62
+end
  63
+
  64
+output_path = 'apache/start.sh'
  65
+
  66
+template = <<-ERB
  67
+#!/bin/bash
  68
+source <%= base_dir %>/apache/envvars
  69
+/usr/sbin/apache2 -d <%= base_dir %>/apache -f <%= base_dir %>/apache/apache2.conf -D FOREGROUND
  70
+ERB
  71
+
  72
+template = ERB.new(template)
  73
+File.open(File.expand_path(output_path), 'wb') do |file|
  74
+  file.puts(template.result(binding))
  75
+end
  76
+
  77
+`cp -r /etc/php5/conf.d/* apache/php`
  78
+
  79
+output_path = 'apache/php/memory.ini'
  80
+
  81
+template = <<-ERB
  82
+memory_limit = <%= php_ram %>
  83
+ERB
  84
+
  85
+template = ERB.new(template)
  86
+File.open(File.expand_path(output_path), 'wb') do |file|
  87
+  file.puts(template.result(binding))
  88
+end
22  cloud_controller/staging/manifests/php.yml
... ...
@@ -0,0 +1,22 @@
  1
+---
  2
+name: "php"
  3
+runtimes:
  4
+  - "php":
  5
+      description: "PHP 5"
  6
+      version: "5.3"
  7
+      executable: "php"
  8
+      default: true
  9
+app_servers:
  10
+  - "apache":
  11
+      description: "Apache"
  12
+      executable: "false"
  13
+      default: true
  14
+detection:
  15
+  - "*.php": true
  16
+staged_services: 
  17
+  - "name": "mysql"
  18
+    "version": "*"
  19
+  - "name": "postgresql"
  20
+    "version": "*"
  21
+
  22
+# vim: filetype=yaml
41  cloud_controller/staging/php/plugin.rb
... ...
@@ -0,0 +1,41 @@
  1
+require File.expand_path('../../apache_common/apache', __FILE__)
  2
+
  3
+class PhpPlugin < StagingPlugin
  4
+  def framework
  5
+    'php'
  6
+  end
  7
+
  8
+  def stage_application
  9
+    Dir.chdir(destination_directory) do
  10
+      create_app_directories
  11
+      Apache.prepare(destination_directory)
  12
+      copy_source_files
  13
+      create_startup_script
  14
+    end
  15
+  end
  16
+
  17
+  # The Apache start script runs from the root of the staged application.
  18
+  def change_directory_for_start
  19
+    "cd apache"
  20
+  end
  21
+
  22
+  def start_command
  23
+    "bash ./start.sh"
  24
+  end
  25
+
  26
+  private
  27
+
  28
+  def startup_script
  29
+    vars = environment_hash
  30
+    generate_startup_script(vars) do
  31
+      <<-PHPEOF
  32
+env > env.log
  33
+ruby resources/generate_apache_conf $VCAP_APP_PORT $HOME $VCAP_SERVICES #{application_memory}m
  34
+      PHPEOF
  35
+    end
  36
+  end
  37
+
  38
+  def apache_server_root
  39
+    File.join(destination_directory, 'apache')
  40
+  end
  41
+end
7  cloud_controller/staging/php/stage
... ...
@@ -0,0 +1,7 @@
  1
+#!/usr/bin/env ruby
  2
+require File.expand_path('../../common', __FILE__)
  3
+plugin_class = StagingPlugin.load_plugin_for('php')
  4
+plugin_class.validate_arguments!
  5
+plugin_class.new(*ARGV).stage_application
  6
+
  7
+# vim: ts=2 sw=2 filetype=ruby
5  dea/config/dea.yml
@@ -43,6 +43,11 @@ runtimes:
43 43
     version: 1.6.0
44 44
     version_flag: '-version'
45 45
     environment:
  46
+  php:
  47
+    executable: php
  48
+    version: 5.3.[2-6]
  49
+    version_flag: '-v'
  50
+    environment:
46 51
   erlangR14B02:
47 52
     executable: /var/vcap/runtimes/erlang-R14B02/bin/erl
48 53
     version: ".* 5.8.3"
51  docs/php.md
Source Rendered
... ...
@@ -0,0 +1,51 @@
  1
+# PHP Support
  2
+
  3
+## Architecture
  4
+
  5
+PHP applications are deployed using Apache and mod_php. For each CloudFoundry instance of the application, an Apache instance is started.
  6
+
  7
+## Demo: Installing Wordpress ##
  8
+The Wordpress CMS can be run using CloudFoundry PHP support with very minimal changes.
  9
+
  10
+Steps to get the application to run:
  11
+
  12
+1. <code>curl -O http://wordpress.org/latest.tar.gz</code>
  13
+2. <code>tar -xzf latest.tar.gz</code>
  14
+3. <code>rm latest.tar.gz</code>
  15
+4. <code>cd wordpress</code>
  16
+5. <code>echo "<?php" > wp-salt.php</code>
  17
+6. <code>curl https://api.wordpress.org/secret-key/1.1/salt/ >> wp-salt.php</code>
  18
+7. Create wp-config.php, and set it to:
  19
+
  20
+		<?php
  21
+		$services = getenv("VCAP_SERVICES");
  22
+		$services_json = json_decode($services,true);
  23
+		$mysql_config = $services_json["mysql-5.1"][0]["credentials"];
  24
+
  25
+		// ** MySQL settings from resource descriptor ** //
  26
+		define('DB_NAME', $mysql_config["name"]);
  27
+		define('DB_USER', $mysql_config["user"]);
  28
+		define('DB_PASSWORD', $mysql_config["password"]);
  29
+		define('DB_HOST', $mysql_config["hostname"]);
  30
+		define('DB_PORT', $mysql_config["port"]);
  31
+
  32
+		define('DB_CHARSET', 'utf8');
  33
+		define('DB_COLLATE', '');
  34
+		define ('WPLANG', '');
  35
+		define('WP_DEBUG', false);
  36
+
  37
+		require('wp-salt.php');
  38
+
  39
+		$table_prefix  = 'wp_';
  40
+
  41
+		/* That's all, stop editing! Happy blogging. */
  42
+
  43
+		/** Absolute path to the WordPress directory. */
  44
+		if ( !defined('ABSPATH') )
  45
+			define('ABSPATH', dirname(__FILE__) . '/');
  46
+
  47
+		/** Sets up WordPress vars and included files. */
  48
+		require_once(ABSPATH . 'wp-settings.php');
  49
+8. <code>vmc push wordpresscf --url wordpresscf.vcap.me -n</code>
  50
+9. <code>vmc create-service mysql --bind wordpresscf</code>
  51
+10. Visit http://wordpresscf.vcap.me and enjoy your Wordpress install!
36  setup/vcap_setup
@@ -255,6 +255,38 @@ if [[ $DEA == true || $ALL == true ]]; then
255 255
 	rm -fr node-v$NODE_VERSION
256 256
     fi
257 257
 
  258
+    # For PHP
  259
+    if [[ $PLATFORM == 'Linux' ]]; then
  260
+    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
  261
+
  262
+    /etc/init.d/apache2 stop
  263
+    update-rc.d -f apache2 remove
  264
+
  265
+    pear channel-discover pear.phpunit.de
  266
+    pear channel-discover pear.symfony-project.com
  267
+    pear channel-discover components.ez.no
  268
+    pear install phpunit/PHPUnit
  269
+    yes | pecl install apc
  270
+    yes | pecl install memcache
  271
+    yes | pecl install mongo
  272
+    git clone git://github.com/owlient/phpredis.git
  273
+    cd phpredis
  274
+    phpize
  275
+    ./configure
  276
+    make && make install
  277
+    cd ..
  278
+    rm -rf phpredis
  279
+
  280
+    echo "extension=apc.so" > /etc/php5/apache2/conf.d/cf.ini
  281
+    echo "extension=memcache.so" >> /etc/php5/apache2/conf.d/cf.ini
  282
+    echo "extension=mongo.so" >> /etc/php5/apache2/conf.d/cf.ini
  283
+    echo "extension=mysql.so" >> /etc/php5/apache2/conf.d/cf.ini
  284
+    echo "extensions=pgsql.so" >> /etc/php5/apache2/conf.d/cf.ini
  285
+
  286
+    elif [[ $PLATFORM == 'MacOSX' ]]; then
  287
+    echo -e "\033[31mFIXME: Implement for MacOSX \033[0m"
  288
+    fi
  289
+
258 290
     ERLANG_VERSION=R14B02
259 291
 
260 292
     if [ ! -d "/var/vcap/runtimes/erlang-$ERLANG_VERSION" ]; then
@@ -293,6 +325,10 @@ if [[ $DEA == true || $ALL == true ]]; then
293 325
     node -v
294 326
     echo ""
295 327
 
  328
+    echo -e "\nPHP should now be installed -->"
  329
+    php -v
  330
+    echo ""
  331
+
296 332
     echo -e "\nErlang should now be installed -->"
297 333
     erl -version
298 334
     echo ""
Commit_comment_tip

Tip: You can add notes to lines in a file. Hover to the left of a line to make a note

Something went wrong with that request. Please try again.