diff --git a/.gitignore b/.gitignore index a4c24ac..32b9048 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,5 @@ /node_modules/ .DS_Store -/typings/ \ No newline at end of file +/typings/ +/results/ +/service-plans/*/results/ \ No newline at end of file diff --git a/README.md b/README.md index b68990d..a20f4af 100644 --- a/README.md +++ b/README.md @@ -1,44 +1,65 @@ -Emergence -========= +# Emergence [](https://gitter.im/JarvusInnovations/Emergence?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) Emergence is a NodeJS-powered server that provides a web interface for configuring and launching the services that power your website or application. It provides virtualized storage containers for your code and assets that are accessible via WebDAV and API. Each storage container maintains complete version history for all files and can be linked over the web to a parent container that files will be inherited from just-in-time. -Features ---------- -* Rich web interface provides for all setup and management -* Plugin-based support for system services to be configured and run - * Plugins included for nginx and mysql -* Versioned storage containers - * Inherit remote containers over http - * Copy-on-write - * Accessible remotely via WebDAV and locally via API -* PHP development framework - * Classes automatically loaded from storage container - * Lightweight MVC classes optimized for serial inheritance across sites - * Extendable templating system powered by Dwoo - - -Requirements -------------- -* NodeJS -* npm - * underscore - * node-static -* mysql -* nginx -* php-fpm - * php 5.3+ - * apc - * mysqli - - -Installation --------------- +## Features + +- Rich web interface provides for all setup and management +- Plugin-based support for system services to be configured and run + - Plugins included for nginx and mysql +- Versioned storage containers + - Inherit remote containers over http + - Copy-on-write + - Accessible remotely via WebDAV and locally via API +- PHP development framework + - Classes automatically loaded from storage container + - Lightweight MVC classes optimized for serial inheritance across sites + - Extendable templating system powered by Dwoo + + +## Installation + See http://emr.ge/docs +## Building with Habitat + +- `cd ~/Repositories/emergence` +- `hab studio enter` +- `build` +- Optionally export a docker container image: `hab pkg export docker emergence/emergence-kernel` + + +## Debugging with Habitat + +From within studio: `hab sup start emergence/emergence-kernel` + + +## Running with Docker + +- Start a container from new image: `docker run -it -p 9080:80 -p 9083:9083 --name myemergence emergence/emergence-kernel` + + +## Habitat Migration Todo + +- [X] Use habitat config system to generate base service configs, add `include .../var/nginx.sites` to nginx, stick dynamic ish there +- [ ] Compare stock services configs with hab-provided configs +- [ ] Pull Request mariadb fix +- [ ] Get nginx running with configured external config +- [X] Move stock nginx config bodies to .include files (e.g. http.include) +- [X] Add default static nginx site to nginx.conf before sites include +- [ ] Review all initialized permissions +- [ ] Remove shelljs +- [ ] Upgrade dwoo and see if php7 works + +- Get docker container working + - [X] Run new docker container + - [X] Check that Zend is loaded now + - [X] get shell wrapper working + - [X] Figure out why cookies dont work -- set from console for now + - [X] Commit changes -Visit http://serverhost:9083 in your browser +- [ ] PR readline into core-plans php and php5 diff --git a/bin/create-site.sh b/bin/create-site similarity index 84% rename from bin/create-site.sh rename to bin/create-site index 0a098e6..d2167e3 100755 --- a/bin/create-site.sh +++ b/bin/create-site @@ -1,5 +1,21 @@ #!/bin/bash + +# determine location of this script +SOURCE="${BASH_SOURCE[0]}" +while [ -h "$SOURCE" ]; do # resolve $SOURCE until the file is no longer a symlink + DIR="$( cd -P "$( dirname "$SOURCE" )" && pwd )" + SOURCE="$(readlink "$SOURCE")" + [[ $SOURCE != /* ]] && SOURCE="$DIR/$SOURCE" # if $SOURCE was a relative symlink, we need to resolve it relative to the path where the symlink file was located +done +DIR="$( cd -P "$( dirname "$SOURCE" )" && pwd )" + + +# get path to underscore +UNDERSCORE=$DIR/../node_modules/.bin/underscore + + +# parse args DEFAULT_PRECACHE_TREES='dwoo-plugins,event-handlers,html-templates,php-classes,php-config,php-migrations,site-root,site-tasks' DEFAULT_GENERATE_TABLES='UserSession,User' @@ -77,12 +93,8 @@ case $i in esac done -if [ "$GENERATE_TABLES" ] || [ "$PRECACHE_TREES" ] || [ "$ENTER_SHELL" ]; then - command -v underscore >/dev/null 2>&1 || { echo >&2 "underscore must be installed to use --precache-trees or --generate-tables"; exit 1; } -fi - if [ -z "$KERNEL_SOCKET" ]; then - KERNEL_SOCKET="/emergence/kernel.sock" + KERNEL_SOCKET="/hab/svc/emergence-kernel/var/run/kernel.sock" fi if [ ! -S "$KERNEL_SOCKET" ]; then @@ -136,7 +148,7 @@ RESPONSE=$( --unix-socket "${KERNEL_SOCKET}" \ -H "Content-Type: application/json" \ -d "${REQUEST_BODY}" \ - "http:/sites" + "http://localhost/sites" ) echo "$RESPONSE" @@ -145,7 +157,7 @@ if [ -z "$PRECACHE_TREES" ] && [ -z "$GENERATE_TABLES" ] && [ -z "$ENTER_SHELL" exit 0 fi -SITE_HANDLE=$(echo "${RESPONSE}" | underscore --outfmt=text extract data.handle) +SITE_HANDLE=$(echo "${RESPONSE}" | $UNDERSCORE --outfmt=text extract data.handle) if [ "$PRECACHE_TREES" ]; then emergence-shell $SITE_HANDLE 1>&2 << END_OF_PHP diff --git a/bin/git-shell b/bin/git-shell index c508f57..dfb7731 100755 --- a/bin/git-shell +++ b/bin/git-shell @@ -21,7 +21,6 @@ SITE_HANDLE=$1 SITE_LAYER=$2 UNDERSCORE=$DIR/../node_modules/.bin/underscore -EMERGENCE_USER=`cat /emergence/config.json | $UNDERSCORE extract user --outfmt text` BLACK="\033[01;30m" MAGENTA="\033[1;31m" @@ -43,13 +42,12 @@ PS2="\[$ORANGE\]→ \[$RESET\]" \ # check environment -test $EUID -eq 0 || die "emergence-git-shell must be run with sudo" test -x "$UNDERSCORE" || die "unable to execute $UNDERSCORE" test -n "$SITE_HANDLE" -a -n "$SITE_LAYER"|| die "usage: emergence-git-shell site_handle layer_id" # get site directory -SITE_DIR="/emergence/sites/$SITE_HANDLE" +SITE_DIR="/hab/svc/emergence-kernel/data/sites/$SITE_HANDLE" test -d "$SITE_DIR" || die "site $SITE_HANDLE not found at $SITE_DIR" @@ -89,6 +87,20 @@ if [ -n "$GIT_USER_DIRTY" ]; then fi +# setup environment +export PS1 +export PS2 +export SITE_HOSTNAME +export SITE_LAYER +export GIT_SSH + +export HISTFILE="$GIT_DIR.bash_history" +export GIT_AUTHOR_NAME="$GIT_NAME" +export GIT_AUTHOR_EMAIL="$GIT_EMAIL" +export GIT_COMMITTER_NAME="$GIT_NAME" +export GIT_COMMITTER_EMAIL="$GIT_EMAIL" + + # execute subshell echo "Opening git shell at $GIT_DIR" cd $GIT_DIR @@ -97,16 +109,8 @@ git status echo "Committing as $GIT_NAME <$GIT_EMAIL>" -exec sudo -u $EMERGENCE_USER \ - HOME="$GIT_DIR" \ - HISTFILE="$GIT_DIR.bash_history" \ - GIT_SSH="$GIT_SSH" \ - GIT_AUTHOR_NAME="$GIT_NAME" \ - GIT_AUTHOR_EMAIL="$GIT_EMAIL" \ - GIT_COMMITTER_NAME="$GIT_NAME" \ - GIT_COMMITTER_EMAIL="$GIT_EMAIL" \ - PS1="$PS1" \ - PS2="$PS2" \ - SITE_HOSTNAME="$SITE_HOSTNAME" \ - SITE_LAYER="$SITE_LAYER" \ - /bin/bash --norc \ No newline at end of file +if [ "$(whoami)" == "hab" ]; then + HOME="$GIT_DIR" exec bash --norc +else + exec su hab -c "HOME='$GIT_DIR' bash --norc" +fi \ No newline at end of file diff --git a/bin/kernel b/bin/kernel index d231123..47eeacd 100755 --- a/bin/kernel +++ b/bin/kernel @@ -6,160 +6,19 @@ var _ = require('underscore'), url = require('url'), path = require('path'), fs = require('fs'), - sitesLib = require('../kernel-lib/sites.js'); - - -var CONFIG; - -if (fs.existsSync('/emergence/config.json')) { - // try to load existing kernel config - - CONFIG = JSON.parse(fs.readFileSync('/emergence/config.json', 'ascii')); -} else { - // try to smart-init a new kernel config - var issueFile = "", - platform = require('os').platform(), - distro = platform; - - // detect ubuntu - // TODO: find better ways to determine default options than platform detection - if (platform == 'linux') { - try { - issueFile = fs.readFileSync('/etc/issue', 'ascii'); - if (issueFile.match(/Ubuntu/)) { - distro = 'ubuntu'; - } - } catch (err) { - // ignore any failure to real /etc/issue and leave platform set to linux - } - } - - console.log('Initializing new emergence environment for distro="'+distro+'"...'); - - if (!fs.existsSync('/emergence')) { - fs.mkdirSync('/emergence', '775'); - } - - CONFIG = { - user: distro=='ubuntu' ? 'www-data' : 'nobody', - group: distro=='ubuntu' ? 'www-data' : 'nobody', - - server: { - host: "0.0.0.0" - }, - services: { - plugins: { } - } - }; - - - - - // detect nginx - if (fs.existsSync('/usr/sbin/nginx')) { - CONFIG.services.plugins.web = { - type: 'nginx', - autoStart: true, - execPath: '/usr/sbin/nginx', - bindHost: '0.0.0.0' - }; - } else if (fs.existsSync('/usr/local/sbin/nginx')) { - CONFIG.services.plugins.web = { - type: 'nginx', - autoStart: true, - execPath: '/usr/local/sbin/nginx', - bindHost: '0.0.0.0' - }; - } - - // detect mysql - if (fs.existsSync('/usr/sbin/mysqld')) { - CONFIG.services.plugins.sql = { - type: 'mysql', - autoStart: true, - execPath: '/usr/sbin/mysqld', - managerUser: 'root', - managerPassword: sitesLib.generatePassword() - }; - } else if (fs.existsSync('/usr/local/bin/mysqld')) { - CONFIG.services.plugins.sql = { - type: 'mysql', - autoStart: true, - execPath: '/usr/local/bin/mysqld', - managerUser: 'root', - managerPassword: sitesLib.generatePassword() - }; - } else if (fs.existsSync('/usr/bin/mysqld_safe')) { - CONFIG.services.plugins.sql = { - type: 'mysql', - autoStart: true, - execPath: '/usr/bin/mysqld_safe', - managerUser: 'root', - managerPassword: sitesLib.generatePassword() - }; - } - - // detect php-fpm - if (fs.existsSync('/usr/sbin/php-fpm5.6')) { - CONFIG.services.plugins.php = { - type: 'php-fpm', - autoStart: true, - execPath: '/usr/sbin/php-fpm5.6' - }; - } else if (fs.existsSync('/usr/sbin/php-fpm7.0')) { - CONFIG.services.plugins.php = { - type: 'php-fpm', - autoStart: true, - execPath: '/usr/sbin/php-fpm7.0' - }; - } else if (fs.existsSync('/usr/bin/php-fpm')) { - CONFIG.services.plugins.php = { - type: 'php-fpm', - autoStart: true, - execPath: '/usr/bin/php-fpm' - }; - } else if (fs.existsSync('/usr/sbin/php5-fpm')) { - CONFIG.services.plugins.php = { - type: 'php-fpm', - autoStart: true, - execPath: '/usr/sbin/php5-fpm' - }; - } else if (fs.existsSync('/usr/local/sbin/php-fpm')) { - CONFIG.services.plugins.php = { - type: 'php-fpm', - autoStart: true, - execPath: '/usr/local/sbin/php-fpm' - }; - } else if (fs.existsSync('/usr/sbin/php-fpm')) { - CONFIG.services.plugins.php = { - type: 'php-fpm', - autoStart: true, - execPath: '/usr/sbin/php-fpm' - }; - } - - fs.writeFileSync('/emergence/config.json', JSON.stringify(CONFIG, null, 4)); - fs.chmodSync('/emergence/config.json', '600'); - console.log('Generated and wrote initial config: /emergence/config.json'); -} - -// create default admin user -if (!fs.existsSync('/emergence/admins.htpasswd')) { - console.log('Creating default administrative user: admin/admin'); - fs.writeFileSync('/emergence/admins.htpasswd', 'admin:{SHA}0DPiKuNIrrVmD8IUCuw1hQxNqZc='); - fs.chmodSync('/emergence/admins.htpasswd', '600'); -} + sitesLib = require('../kernel-lib/sites.js'), + kernelConfig = require('/hab/svc/emergence-kernel/config/config.json'); // load core modules -var eSites = sitesLib.createSites(CONFIG); -var eServices = require('../kernel-lib/services.js').createServices(eSites, CONFIG); +var eSites = sitesLib.createSites(kernelConfig); +var eServices = require('../kernel-lib/services.js').createServices(eSites, kernelConfig); // instantiate management server var eManagementServer = require('../kernel-lib/server.js').createServer({ sites: eSites, services: eServices -}, CONFIG); +}, kernelConfig); // start server diff --git a/bin/shell b/bin/shell index 5f00f6e..d1787c3 100755 --- a/bin/shell +++ b/bin/shell @@ -20,7 +20,7 @@ DIR="$( cd -P "$( dirname "$SOURCE" )" && pwd )" [ -n "$1" ] || die "Usage: emergence-kernel site-handle" siteHandle=$1 -siteDir="/emergence/sites/$siteHandle" +siteDir="/hab/svc/emergence-kernel/data/sites/$siteHandle" [ -d "$siteDir" ] || die "Site '$siteHandle' not found at '$siteDir'" @@ -29,12 +29,14 @@ siteDir="/emergence/sites/$siteHandle" autoPrependScript=$(mktemp); echo " $autoPrependScript +echo "\$serverConfig = json_decode(file_get_contents('/hab/svc/emergence-kernel/config/config.json'), true);" >> $autoPrependScript +echo "date_default_timezone_set(\$serverConfig['services']['php']['timezone']);" >> $autoPrependScript echo "require('`dirname $DIR`/php-bootstrap/bootstrap.inc.php');" >> $autoPrependScript echo "Site::initialize('$siteDir');" >> $autoPrependScript -# give www-data access to script -chgrp www-data $autoPrependScript +# give hab access to script +chgrp hab $autoPrependScript chmod g+r $autoPrependScript @@ -55,7 +57,13 @@ fi # execute interactive shell -TERM=$TERM sudo -E -u www-data -g www-data php -d auto_prepend_file=$autoPrependScript -d apc.enable_cli=on $INPUT_ARGS +PHP_ARGS="-d auto_prepend_file=$autoPrependScript -d apc.enable_cli=on $INPUT_ARGS" + +if [ "$(whoami)" == "hab" ]; then + TERM=$TERM php $PHP_ARGS +else + TERM=$TERM su hab -c "php $PHP_ARGS" +fi # clean up diff --git a/bin/su b/bin/su index b44cb1e..2fbf742 100755 --- a/bin/su +++ b/bin/su @@ -1,2 +1,2 @@ #!/bin/bash -sudo su -l www-data -p \ No newline at end of file +sudo su -l hab -p \ No newline at end of file diff --git a/clean-runtime.sh b/clean-runtime.sh new file mode 100755 index 0000000..645ffb5 --- /dev/null +++ b/clean-runtime.sh @@ -0,0 +1,8 @@ +#!/bin/bash + +hab sup term +hab pkg exec core/busybox-static killall php-fpm +hab pkg exec core/busybox-static killall nginx +hab pkg exec core/busybox-static killall mysqld +rm /hab/svc/emergence-kernel/var/run/kernel.sock +find /hab/svc/emergence-kernel/var/run/ -type f -delete \ No newline at end of file diff --git a/debug.toml b/debug.toml new file mode 100644 index 0000000..486049c --- /dev/null +++ b/debug.toml @@ -0,0 +1,2 @@ +[php] +stat_scripts=true diff --git a/default-site/index.html b/default-site/index.html new file mode 100644 index 0000000..1b25d78 --- /dev/null +++ b/default-site/index.html @@ -0,0 +1,9 @@ + +
+No site is configure (yet) at this address. If you are the webmaster, visit the emergence control panel and create a site
+ + \ No newline at end of file diff --git a/habitat/config/config.json b/habitat/config/config.json new file mode 100644 index 0000000..f165af2 --- /dev/null +++ b/habitat/config/config.json @@ -0,0 +1,24 @@ +{ + "user": "{{pkg.svc_user}}", + "group": "{{pkg.svc_group}}", + "server": { + "host": "{{cfg.kernel.host}}" + }, + "services": { + "web": { + "type": "nginx", + "packagePath": "{{pkgPathFor "emergence/nginx"}}", + "bindHost": "{{cfg.web.host}}", + "bindPort": "{{cfg.web.port}}" + }, + "sql": { + "type": "mariadb", + "packagePath": "{{pkgPathFor "emergence/mariadb"}}" + }, + "php": { + "type": "php-fpm", + "packagePath": "{{pkgPathFor "emergence/php5"}}", + "timezone": "{{cfg.php.timezone}}" + } + } +} \ No newline at end of file diff --git a/habitat/config/mariadb b/habitat/config/mariadb new file mode 100644 index 0000000..30ff15f --- /dev/null +++ b/habitat/config/mariadb @@ -0,0 +1,37 @@ +[mysqld] +character-set-server = utf8 +user = hab +port = 3306 +socket = {{pkg.svc_var_path}}/run/mariadb.sock +pid-file = {{pkg.svc_var_path}}/run/mariadb.pid +datadir = {{pkg.svc_data_path}}/services/mariadb +basedir = {{pkgPathFor "emergence/mariadb"}} +skip-external-locking +key_buffer_size = 16M +max_allowed_packet = 1M +sort_buffer_size = 512K +net_buffer_length = 8K +read_buffer_size = 256K +read_rnd_buffer_size = 512K +myisam_sort_buffer_size = 8M +log-bin = mysqld-bin +expire_logs_days = 2 +server-id = 1 +tmpdir = /tmp/ +innodb_buffer_pool_size = 16M +innodb_data_file_path = ibdata1:10M:autoextend:max:128M +innodb_log_file_size = 5M +innodb_log_buffer_size = 8M +innodb_log_files_in_group = 2 +innodb_flush_log_at_trx_commit = 1 +innodb_lock_wait_timeout = 50 +innodb_file_per_table +max_binlog_size = 100M +binlog_format = row +table_open_cache = 64 + +{{#if cfg.sql.host~}} + bind-address = {{cfg.sql.host}} +{{~else~}} + skip-networking +{{~/if}} diff --git a/habitat/config/nginx b/habitat/config/nginx new file mode 100644 index 0000000..7ae3810 --- /dev/null +++ b/habitat/config/nginx @@ -0,0 +1,68 @@ +user hab hab; + +worker_processes auto; +events { + worker_connections 1024; + use epoll; +} + +pid {{pkg.svc_var_path}}/run/nginx.pid; +lock_file {{pkg.svc_var_path}}/run/nginx.lock; +error_log {{pkg.svc_var_path}}/log/nginx.err info; + +http { + client_body_temp_path {{pkg.svc_var_path}}/tmp/nginx/client-body; + proxy_temp_path {{pkg.svc_var_path}}/tmp/nginx/proxy; + fastcgi_temp_path {{pkg.svc_var_path}}/tmp/nginx/fastcgi; + scgi_temp_path {{pkg.svc_var_path}}/tmp/nginx/scgi; + uwsgi_temp_path {{pkg.svc_var_path}}/tmp/nginx/uwsgi; + + include {{pkgPathFor "emergence/nginx"}}/config/mime.types; + default_type application/octet-stream; + + log_format main + '$host $remote_addr - $remote_user [$time_local] ' + '"$request" $status $bytes_sent ' + '"$http_referer" "$http_user_agent" ' + '"$gzip_ratio"'; + + client_header_timeout 10m; + client_body_timeout 10m; + send_timeout 10m; + connection_pool_size 256; + client_max_body_size 200m; + client_body_buffer_size 128k; + client_header_buffer_size 1k; + large_client_header_buffers 8 512k; + request_pool_size 4k; + server_names_hash_bucket_size 1024; + types_hash_max_size 2048; + gzip on; + gzip_min_length 1100; + gzip_buffers 4 8k; + gzip_types text/plain text/css text/x-scss text/x-html-template text/x-component text/xml application/xml application/javascript application/json application/php application/atom+xml application/rss+xml application/vnd.ms-fontobject application/x-font-ttf application/xhtml+xml font/opentype image/svg+xml image/x-icon; + output_buffers 1 32k; + postpone_output 1460; + sendfile on; + tcp_nopush on; + tcp_nodelay on; + keepalive_timeout 75 20; + ignore_invalid_headers on; + fastcgi_read_timeout 6h; + fastcgi_buffers 32 64k; + server_tokens off; + + server { + listen {{cfg.web.host}}:{{cfg.web.port}}; + server_name localhost; + access_log off; + + location / { + root {{pkg.path}}/app/default-site; + index index.html; + error_page 404 index.html; + } + } + + include {{pkg.svc_var_path}}/config/nginx.sites; +} \ No newline at end of file diff --git a/habitat/config/php-fpm b/habitat/config/php-fpm new file mode 100644 index 0000000..40c3439 --- /dev/null +++ b/habitat/config/php-fpm @@ -0,0 +1,23 @@ +[global] +pid = {{pkg.svc_var_path}}/run/php-fpm.pid +error_log = {{pkg.svc_var_path}}/log/php-fpm.err + + +[www] +user = hab +group = hab +catch_workers_output = on + +listen = {{pkg.svc_var_path}}/run/php-fpm.sock +listen.owner = {{pkg.svc_user}} +listen.group = {{pkg.svc_group}} + +pm = dynamic +pm.max_children = {{cfg.php.max_children}} +pm.start_servers = {{cfg.php.start_servers}} +pm.min_spare_servers = {{cfg.php.min_spare_servers}} +pm.max_spare_servers = {{cfg.php.max_spare_servers}} + +{{~#if cfg.php.status_path}} +pm.status_path = {{cfg.php.status_path}} +{{~/if}} \ No newline at end of file diff --git a/habitat/config/php.ini b/habitat/config/php.ini new file mode 100644 index 0000000..e2d6706 --- /dev/null +++ b/habitat/config/php.ini @@ -0,0 +1,17 @@ +[PHP] +short_open_tag = on + +upload_max_filesize = {{cfg.php.upload_max_filesize}} +post_max_size = {{cfg.php.post_max_size}} +memory_limit = {{cfg.php.memory_limit}} +error_reporting = {{cfg.php.error_reporting}} +date.timezone = {{cfg.php.timezone}} + +apc.shm_size = {{cfg.php.shm_size}} +apc.shm_segments = 1 +apc.slam_defense = 0 +apc.stat = {{#if cfg.php.stat_scripts}}1{{else}}0{{/if}} + +zend_extension = opcache.so +opcache.enable = on +opcache.validate_timestamps = {{#if cfg.php.stat_scripts}}1{{else}}0{{/if}} \ No newline at end of file diff --git a/habitat/default.toml b/habitat/default.toml new file mode 100644 index 0000000..6cc0d34 --- /dev/null +++ b/habitat/default.toml @@ -0,0 +1,29 @@ +# Use this file to templatize your application's native configuration files. +# See the docs at https://www.habitat.sh/docs/create-packages-configure/. +# You can safely delete this file if you don't need it. + +[kernel] +host="0.0.0.0" + +[web] +host="0.0.0.0" +port=80 + +[sql] +host="127.0.0.1" + +[php] +stat_scripts=false +status_path=false +error_reporting="E_ALL & ~E_NOTICE" +timezone="America/New_York" + +max_children=50 +start_servers=5 +min_spare_servers=5 +max_spare_servers=10 + +upload_max_filesize="200M" +post_max_size="200M" +memory_limit="200M" +shm_size="512M" \ No newline at end of file diff --git a/habitat/hooks/init b/habitat/hooks/init new file mode 100755 index 0000000..032f8d4 --- /dev/null +++ b/habitat/hooks/init @@ -0,0 +1,48 @@ +#!/bin/bash + +# initialize users database +AUTHFILE="{{pkg.svc_data_path}}/users.htpasswd" + +if [ ! -f "$AUTHFILE" ]; then + echo "Initializing $AUTHFILE with admin/admin user" + echo "admin:{SHA}0DPiKuNIrrVmD8IUCuw1hQxNqZc=" > "$AUTHFILE" + chown root:hab "$AUTHFILE" + chmod 640 "$AUTHFILE" +fi + +# fix permissions on config directory +chmod g+rX -R "{{pkg.svc_config_path}}" + +# initialize var directories +cd "{{pkg.svc_var_path}}" +chmod g+rX -R . +for DIR in log run config tmp tmp/nginx; do + if [ ! -d "$DIR" ]; then + mkdir "$DIR" + chmod 770 "$DIR" + chown root:hab "$DIR" + fi +done + +# initialize data directories +cd "{{pkg.svc_data_path}}" +chmod g+rX -R . +for DIR in sites services; do + if [ ! -d "$DIR" ]; then + mkdir "$DIR" + chmod 770 "$DIR" + chown root:hab "$DIR" + fi +done + +# initialize static asset links +cd "{{pkg.svc_static_path}}" +chmod g+rX -R . +ln -sf "{{pkg.path}}/app/php-bootstrap" +ln -sf "{{pkg.path}}/app/kernel-www" + +# initialize bin links +hab pkg binlink core/hab hab +hab pkg binlink core/git git +hab pkg binlink core/openssh ssh-keygen +hab pkg binlink core/openssh ssh \ No newline at end of file diff --git a/habitat/plan.sh b/habitat/plan.sh new file mode 100644 index 0000000..312bc9d --- /dev/null +++ b/habitat/plan.sh @@ -0,0 +1,28 @@ +pkg_name=emergence-kernel +pkg_origin=emergence +pkg_version="1.0.0" +pkg_upstream_url="https://github.com/JarvusInnovations/emergence" +pkg_scaffolding=core/scaffolding-node +pkg_svc_user="root" + +pkg_build_deps=( + core/make + core/gcc + core/python2 +) + +pkg_deps=( + core/coreutils + core/bash + core/git + core/curl + core/openssh + emergence/nginx + emergence/mariadb + emergence/php5 +) + +do_install() { + do_default_install + fix_interpreter "$scaffolding_app_prefix/bin/*" core/bash bin/bash +} diff --git a/kernel-lib/server.js b/kernel-lib/server.js index 84dd2c7..64061a0 100644 --- a/kernel-lib/server.js +++ b/kernel-lib/server.js @@ -1,4 +1,5 @@ -var http = require('http'), +var net = require('net'), + http = require('http'), util = require('util'), fs = require('fs'), path = require('path'), @@ -15,56 +16,80 @@ exports.Server = function(paths, config) { // call events constructor events.EventEmitter.call(me); - // initialize options and apply defaults + // initialize configuration + me.socketPath = '/hab/svc/emergence-kernel/var/run/kernel.sock'; + me.staticDir = '/hab/svc/emergence-kernel/static/kernel-www'; + me.usersPath = '/hab/svc/emergence-kernel/data/users.htpasswd'; + me.paths = paths || {}; - me.options = options || {}; - me.options.host = me.options.host || '0.0.0.0'; - me.options.port = me.options.port || 9083; - me.options.sslKey = me.options.sslKey || null; - me.options.sslCert = me.options.sslCert || null; - me.options.staticDir = me.options.staticDir || path.resolve(__dirname, '../kernel-www'); - me.options.socketPath = 'socketPath' in me.options ? me.options.socketPath : '/emergence/kernel.sock'; - - // initialize state + me.host = options.host || '0.0.0.0'; + me.port = options.port || 9083; + me.defaultSuffix = options.defaultSuffix || null; + me.sslKey = options.sslKey || null; + me.sslCert = options.sslCert || null; }; util.inherits(exports.Server, events.EventEmitter); exports.Server.prototype.start = function() { - // create authenticator - this.httpAuth = require('http-auth')({ - authRealm: 'Emergence Node Management', - authFile: '/emergence/admins.htpasswd' + var me = this, + testConnection; + + // test if socket connection is already open + if (!me.socketPath || !fs.existsSync(me.socketPath)) { + _doStart(); + return; + } + + testConnection = net.createConnection(me.socketPath); + + testConnection.on('connect', function() { + testConnection.close(); + throw 'Socket file already exists and is live: '+me.socketPath; }); - // create static fileserver - this.fileServer = new static.Server(this.options.staticDir); + testConnection.on('error', function() { + console.log('Deleting stale socket file:', me.socketPath); + fs.unlinkSync(me.socketPath); + _doStart(); + }); - // listen on web port - if (this.options.sslKey && this.options.sslCert) { - this.webServer = require('https').createServer({ - key: fs.readFileSync(this.options.sslKey), - cert: fs.readFileSync(this.options.sslCert) - }, this.handleWebRequest.bind(this)).listen(this.options.port, this.options.host); + function _doStart() { + // create authenticator + me.httpAuth = require('http-auth')({ + authRealm: 'Emergence Node Management', + authFile: me.usersPath + }); - this.webProtocol = 'https'; - } else { - this.webServer = require('http').createServer(this.handleWebRequest.bind(this)).listen(this.options.port, this.options.host); + // create static fileserver + me.fileServer = new static.Server(me.staticDir); - this.webProtocol = 'http'; - } + // listen on web port + if (me.sslKey && me.sslCert) { + me.webServer = require('https').createServer({ + key: fs.readFileSync(me.sslKey), + cert: fs.readFileSync(me.sslCert) + }, me.handleWebRequest.bind(me)).listen(me.port, me.host); - // listen on unix socket - if (this.options.socketPath) { - this.socketServer = require('http').createServer(this.handleRequest.bind(this)).listen(this.options.socketPath); - fs.chmodSync(this.options.socketPath, '400'); - } + me.webProtocol = 'https'; + } else { + me.webServer = require('http').createServer(me.handleWebRequest.bind(me)).listen(me.port, me.host); + + me.webProtocol = 'http'; + } + + // listen on unix socket + if (me.socketPath) { + me.socketServer = require('http').createServer(me.handleRequest.bind(me)).listen(me.socketPath); + fs.chmodSync(me.socketPath, '400'); + } - // clean up on exit - process.on('exit', this.close.bind(this)); + // clean up on exit + process.on('exit', me.close.bind(me)); - console.log('Management server listening on '+this.webProtocol+'://'+this.options.host+':'+this.options.port); + console.log('Management server listening on '+me.webProtocol+'://'+me.host+':'+me.port); + } }; exports.createServer = function(paths, options) { @@ -96,7 +121,11 @@ exports.Server.prototype.handleRequest = function(request, response) { if (request.path[0] == 'server-config') { response.writeHead(200, {'Content-Type':'application/json'}); - response.end(JSON.stringify(me.options)); + response.end(JSON.stringify({ + host: me.host, + port: me.port, + defaultSuffix: me.defaultSuffix + })); return; } @@ -123,13 +152,15 @@ exports.Server.prototype.handleRequest = function(request, response) { }; exports.Server.prototype.close = function(options, error) { + var me = this; + console.log('Shutting down management server...'); - if (this.webServer) { - this.webServer.close(); + if (me.webServer) { + me.webServer.close(); } - if (this.socketServer) { - this.socketServer.close(); + if (me.socketServer) { + me.socketServer.close(); } }; \ No newline at end of file diff --git a/kernel-lib/services.js b/kernel-lib/services.js index 0853748..ce10cdf 100644 --- a/kernel-lib/services.js +++ b/kernel-lib/services.js @@ -5,49 +5,17 @@ var _ = require('underscore'), events = require('events'); exports.ServicesController = function(sites, config) { - var me = this, - options = config.services; + var me = this; me.sites = sites; + me.services = config.services; // call events constructor events.EventEmitter.call(me); - // initialize options and apply defaults - me.options = options || {}; - me.options.plugins = me.options.plugins || {}; - me.options.servicesDir = me.options.servicesDir || '/emergence/services'; - me.options.logsDir = me.options.logsDir || me.options.servicesDir+'/logs'; - me.options.configDir = me.options.configDir || me.options.servicesDir+'/etc'; - me.options.runDir = me.options.runDir || me.options.servicesDir+'/run'; - me.options.dataDir = me.options.dataDir || me.options.servicesDir+'/data'; - me.options.user = me.options.user || config.user; - me.options.group = me.options.group || config.group; - - // create required directories - if (!fs.existsSync(me.options.servicesDir)) { - fs.mkdirSync(me.options.servicesDir, '775'); - } - - if (!fs.existsSync(me.options.logsDir)) { - fs.mkdirSync(me.options.logsDir, '775'); - } - - if (!fs.existsSync(me.options.configDir)) { - fs.mkdirSync(me.options.configDir, '775'); - } - - if (!fs.existsSync(me.options.runDir)) { - fs.mkdirSync(me.options.runDir, '775'); - } - - if (!fs.existsSync(me.options.dataDir)) { - fs.mkdirSync(me.options.dataDir, '775'); - } - - // load service plugins + // load services me.services = {}; - _.each(me.options.plugins, function(plugin, name) { + _.each(config.services, function(plugin, name) { console.log('Loading service: '+name); if (_.isString(plugin)) { @@ -59,12 +27,10 @@ exports.ServicesController = function(sites, config) { me.services[name] = plugin; }); - // auto-start service plugins + // start services _.each(me.services, function(service, name) { - if (service.options.autoStart) { - console.log('Autostarting service: '+name); - service.start(); - } + console.log('Starting service: '+name); + service.start(); }); }; diff --git a/kernel-lib/services/mysql.js b/kernel-lib/services/mariadb.js similarity index 55% rename from kernel-lib/services/mysql.js rename to kernel-lib/services/mariadb.js index ae462e2..6990d8e 100644 --- a/kernel-lib/services/mysql.js +++ b/kernel-lib/services/mariadb.js @@ -19,55 +19,43 @@ exports.MysqlService = function(name, controller, options) { // call parent constructor exports.MysqlService.super_.apply(me, arguments); - // default options - me.options.configPath = me.options.configPath || controller.options.configDir + '/my.cnf'; - me.options.execPath = me.options.execPath || '/usr/sbin/mysqld'; - me.options.bindHost = me.options.bindHost || false; - me.options.runDir = me.options.runDir || controller.options.runDir + '/mysqld'; - me.options.pidPath = me.options.pidPath || me.options.runDir + '/mysqld.pid'; - me.options.socketPath = me.options.socketPath || me.options.runDir + '/mysqld.sock'; - me.options.dataDir = me.options.dataDir || controller.options.dataDir + '/mysql'; - me.options.logsDir = me.options.logsDir || controller.options.logsDir + '/mysql'; - me.options.errorLogPath = me.options.errorLogPath || me.options.logsDir + '/mysqld.err'; - me.options.managerUser = me.options.managerUser || 'emergence'; - me.options.managerPassword = me.options.managerPassword || ''; - - - // verify binary - if (!fs.existsSync(me.options.execPath)) { - throw 'execPath not found: ' + me.options.execPath; - } + // initialize configuration + me.packagePath = options.packagePath; + me.execPath = me.packagePath + '/bin/mysqld'; - // check binary version - console.log(me.name+': detecting mysqld version...'); - versionMatch = shell.exec(me.options.execPath+' --version').output.match(/mysqld\s+Ver\s+(\d+(\.\d+)*)(-MariaDB)?/); + me.configPath = '/hab/svc/emergence-kernel/config/mariadb'; + me.pidPath = '/hab/svc/emergence-kernel/var/run/mariadb.pid'; + me.socketPath = '/hab/svc/emergence-kernel/var/run/mariadb.sock'; + me.dataDir = '/hab/svc/emergence-kernel/data/services/mariadb'; + me.statePath = me.dataDir+'/state.json'; - if (!versionMatch) { - throw 'Failed to detect mysql version'; - } + me.execOptions = ['--defaults-file='+me.configPath, '--basedir='+me.packagePath]; - me.mysqldVersion = versionMatch[1]; - me.mysqldIsMaria = versionMatch[3] == '-MariaDB'; - console.log('%s: determined mysqld version: %s', me.name, me.mysqldVersion + (me.mysqldIsMaria ? ' (MariaDB)' : '')); + // load state + if (fs.existsSync(me.statePath)) { + me.state = JSON.parse(fs.readFileSync(me.statePath, 'ascii')); + } // check for existing mysqld process - if (fs.existsSync(me.options.pidPath)) { - me.pid = parseInt(fs.readFileSync(me.options.pidPath, 'ascii')); + if (fs.existsSync(me.pidPath)) { + me.pid = parseInt(fs.readFileSync(me.pidPath, 'ascii')); console.log(me.name+': found existing PID: '+me.pid+', checking /proc/'+me.pid); if (fs.existsSync('/proc/'+me.pid)) { me.status = 'online'; // instantiate MySQL client - me.client = new mariasql({ - unixSocket: me.options.socketPath, - user: me.options.managerUser, - password: me.options.managerPassword, - multiStatements: true - }); + if (me.state) { + me.client = new mariasql({ + unixSocket: me.socketPath, + user: 'root', + password: me.state.rootPassword, + multiStatements: true + }); + } } else { console.log(me.name+': process '+me.pid + ' not found, deleting .pid file'); - fs.unlinkSync(me.options.pidPath); + fs.unlinkSync(me.pidPath); } } @@ -87,55 +75,53 @@ exports.MysqlService.prototype.start = function(firstRun) { return false; } - // write configuration file - this.writeConfig(); + // init data directory if needed + if (!fs.existsSync(me.dataDir)) { + console.log('creating datadir as ', require("os").userInfo().username); + me.status = 'configuring'; - // init logs directory if needed - if (!fs.existsSync(me.options.logsDir)) { - console.log(me.name+': initializing new log directory'); - fs.mkdirSync(me.options.logsDir, '775'); - exec('chown -R mysql:mysql '+me.options.logsDir); - } + console.log(me.name+': initializing new data directory...'); + fs.mkdirSync(me.dataDir); + fs.chownSync(me.dataDir, me.controller.sites.dataUid, me.controller.sites.dataGid); + fs.chmodSync(me.dataDir, '750'); + // initialize state + me.state = { + rootPassword: me.controller.sites.generatePassword() + }; - // init run directory if needed - if (!fs.existsSync(me.options.runDir)) { - console.log(me.name+': initializing new run directory'); - fs.mkdirSync(me.options.runDir, '775'); - exec('chown -R mysql:mysql '+me.options.runDir); - } + fs.writeFileSync(me.statePath, JSON.stringify(me.state, null, 4)); + fs.chmodSync(me.statePath, '600'); - // init data directory if needed - if (!fs.existsSync(me.options.dataDir)) { - console.log(me.name+': initializing new data directory...'); - fs.mkdirSync(me.options.dataDir, '775'); - exec('chown -R mysql:mysql '+me.options.dataDir); + // use mysql_install_db to finish init + exec( + me.packagePath + '/scripts/mysql_install_db '+me.execOptions.join(' '), + function(error, stdout, stderr) { + if (error) { + console.log(me.name+': failed to initialize data directory', error); + return; + } - if (semver.lt(me.mysqldVersion, '5.7.6') || me.mysqldIsMaria) { - exec('mysql_install_db --defaults-file='+me.options.configPath, function(error, stdout, stderr) { me.start(true); - }); - } else { - exec('mysqld --initialize-insecure --user=mysql --datadir='+me.options.dataDir, function(error, stdout, stderr) { - me.start(true); - }); - } + } + ); - me.status = 'configuring'; return true; // not really started, we have to try again after mysql_install_db is done } // instantiate MySQL client - me.client = new mariasql({ - unixSocket: me.options.socketPath, - user: me.options.managerUser, - password: me.options.managerPassword, - multiStatements: true - }); + if (me.state) { + me.client = new mariasql({ + unixSocket: me.socketPath, + user: 'root', + password: me.state.rootPassword, + multiStatements: true + }); + } // spawn process - console.log(me.name+': spawning mysql: '+me.options.execPath); - me.proc = spawn(me.options.execPath, ['--defaults-file='+me.options.configPath, '--console'], {detached: true}); + console.log(me.name+': spawning mysql: '+me.execPath); + me.proc = spawn(me.execPath, me.execOptions.concat(['--console']), {detached: true}); me.pid = me.proc.pid; me.status = 'online'; @@ -208,7 +194,7 @@ exports.MysqlService.prototype.restart = function() { // wait for pid to disappear before attempting start process.stdout.write(me.name+': waiting for shutdown'); - while (fs.existsSync(me.options.pidPath)) { + while (fs.existsSync(me.pidPath)) { process.stdout.write('.'); now = new Date().getTime(); @@ -222,70 +208,6 @@ exports.MysqlService.prototype.restart = function() { return me.start(); }; -exports.MysqlService.prototype.writeConfig = function() { - fs.writeFileSync(this.options.configPath, this.makeConfig()); -}; - -exports.MysqlService.prototype.makeConfig = function() { - var me = this, - config = []; - - config.push( - '[mysqld]', - 'character-set-server = utf8', - 'user = mysql', - 'port = 3306', - 'socket = '+me.options.socketPath, - 'pid-file = '+me.options.pidPath, -// 'log-error = '+me.options.errorLogPath, // disabled due to http://bugs.mysql.com/bug.php?id=65592 -- errors output to STDIN will usually go into emergence-kernel's log - 'basedir = /usr', - 'datadir = '+me.options.dataDir, - 'skip-external-locking', - 'key_buffer_size = 16M', - 'max_allowed_packet = 1M', - 'sort_buffer_size = 512K', - 'net_buffer_length = 8K', - 'read_buffer_size = 256K', - 'read_rnd_buffer_size = 512K', - 'myisam_sort_buffer_size = 8M', -// 'lc-messages-dir = /usr/local/share/mysql', - - 'log-bin = mysqld-bin', - 'expire_logs_days = 2', - 'server-id = 1', - - 'tmpdir = /tmp/', - - 'innodb_buffer_pool_size = 16M', - 'innodb_data_file_path = ibdata1:10M:autoextend:max:128M', - 'innodb_log_file_size = 5M', - 'innodb_log_buffer_size = 8M', - 'innodb_log_files_in_group = 2', - 'innodb_flush_log_at_trx_commit = 1', - 'innodb_lock_wait_timeout = 50', - 'innodb_file_per_table', - 'max_binlog_size = 100M', - 'binlog_format = row' - ); - - if (semver.gt(me.mysqldVersion, '5.6.0')) { - config.push('table_open_cache = 64'); - } else { - config.push('table_cache = 64'); - } - - if (semver.lt(me.mysqldVersion, '5.7.4')) { - config.push('innodb_additional_mem_pool_size = 2M'); - } - - if (me.options.bindHost) { - config.push('bind-address = '+me.options.bindHost); - } else { - config.push('skip-networking'); - } - - return config.join('\n'); -}; exports.MysqlService.prototype.secureInstallation = function() { var me = this, @@ -294,11 +216,7 @@ exports.MysqlService.prototype.secureInstallation = function() { console.log(me.name+': securing installation...'); // set root password - if (semver.lt(me.mysqldVersion, '5.7.0') || me.mysqldIsMaria) { - sql += 'UPDATE mysql.user SET Password=PASSWORD("'+me.options.managerPassword+'") WHERE User="root";'; - } else { - sql += 'UPDATE mysql.user SET authentication_string=PASSWORD("'+me.options.managerPassword+'") WHERE User="root";'; - } + sql += 'UPDATE mysql.user SET Password=PASSWORD("'+me.state.rootPassword+'") WHERE User="root";'; // remove anonymous users sql += 'DELETE FROM mysql.user WHERE User="";'; @@ -315,7 +233,7 @@ exports.MysqlService.prototype.secureInstallation = function() { // open a temporary connection to the new non-secured installation (new mariasql({ - unixSocket: me.options.socketPath, + unixSocket: me.socketPath, user: 'root', password: '', multiStatements: true @@ -334,7 +252,7 @@ exports.MysqlService.prototype.onSiteCreated = function(siteData, requestData, c var me = this, sql = '', dbConfig = { - socket: me.options.socketPath, + socket: me.socketPath, database: siteData.handle, username: siteData.handle, password: me.controller.sites.generatePassword() diff --git a/kernel-lib/services/nginx.js b/kernel-lib/services/nginx.js index b42aba3..9c00ecd 100644 --- a/kernel-lib/services/nginx.js +++ b/kernel-lib/services/nginx.js @@ -14,31 +14,21 @@ exports.NginxService = function(name, controller, options) { // call parent constructor exports.NginxService.super_.apply(me, arguments); - // default options - me.options.configPath = me.options.configPath || controller.options.configDir + '/nginx.conf'; - me.options.execPath = me.options.execPath || '/usr/sbin/nginx'; - me.options.bindHost = me.options.bindHost || '127.0.0.1'; - me.options.bindPort = me.options.bindPort || 80; - me.options.runDir = me.options.runDir || controller.options.runDir + '/nginx'; - me.options.logsDir = me.options.logsDir || controller.options.logsDir + '/nginx'; - me.options.pidPath = me.options.pidPath || me.options.runDir + '/nginx.pid'; - me.options.errorLogPath = me.options.errorLogPath || me.options.logsDir + '/errors.log'; - me.options.miscConfigDir = me.options.miscConfigDir || (process.platform=='darwin'?'/usr/local/etc/nginx':'/etc/nginx'); - me.options.user = me.options.user || controller.options.user; - me.options.group = me.options.group || controller.options.group; - - // create required directories - if (!fs.existsSync(me.options.runDir)) { - fs.mkdirSync(me.options.runDir, '775'); - } + // initialize configuration + me.packagePath = options.packagePath; + me.execPath = me.packagePath + '/bin/nginx'; + + me.configPath = '/hab/svc/emergence-kernel/config/nginx'; + me.sitesConfigPath = '/hab/svc/emergence-kernel/var/config/nginx.sites'; + me.pidPath = '/hab/svc/emergence-kernel/var/run/nginx.pid'; + + me.bindHost = options.bindHost || '127.0.0.1'; + me.bindPort = options.bindPort || 80; - if (!fs.existsSync(me.options.logsDir)) { - fs.mkdirSync(me.options.logsDir, '775'); - } // check for existing master process - if (fs.existsSync(me.options.pidPath)) { - me.pid = parseInt(fs.readFileSync(me.options.pidPath, 'ascii')); + if (fs.existsSync(me.pidPath)) { + me.pid = parseInt(fs.readFileSync(me.pidPath, 'ascii')); console.log(me.name+': found existing PID: '+me.pid); me.status = 'online'; } @@ -57,16 +47,16 @@ util.inherits(exports.NginxService, require('./abstract.js').AbstractService); exports.NginxService.prototype.start = function() { var me = this; - console.log(me.name+': spawning daemon: '+me.options.execPath); + console.log(me.name+': spawning daemon: '+me.execPath); if (me.pid) { console.log(me.name+': already running with PID '+me.pid); return false; } - this.writeConfig(); + this.writeSitesConfig(); - me.proc = spawn(me.options.execPath, ['-c', me.options.configPath]); + me.proc = spawn(me.execPath, ['-c', me.configPath]); me.proc.on('exit', function (code) { @@ -77,16 +67,16 @@ exports.NginxService.prototype.start = function() { } // look for pid - if (fs.existsSync(me.options.pidPath)) { - me.pid = parseInt(fs.readFileSync(me.options.pidPath, 'ascii')); + if (fs.existsSync(me.pidPath)) { + me.pid = parseInt(fs.readFileSync(me.pidPath, 'ascii')); console.log(me.name+': found new PID: '+me.pid); me.status = 'online'; } else { console.log(me.name+': failed to find pid after launching, waiting 1000ms and trying again...'); setTimeout(function() { - if (fs.existsSync(me.options.pidPath)) { - me.pid = parseInt(fs.readFileSync(me.options.pidPath, 'ascii')); + if (fs.existsSync(me.pidPath)) { + me.pid = parseInt(fs.readFileSync(me.pidPath, 'ascii')); console.log(me.name+': found new PID: '+me.pid); me.status = 'online'; } else { @@ -143,7 +133,7 @@ exports.NginxService.prototype.restart = function() { return false; } - this.writeConfig(); + this.writeSitesConfig(); try { process.kill(me.pid, 'SIGHUP'); @@ -158,14 +148,14 @@ exports.NginxService.prototype.restart = function() { }; -exports.NginxService.prototype.writeConfig = function() { - fs.writeFileSync(this.options.configPath, this.makeConfig()); +exports.NginxService.prototype.writeSitesConfig = function() { + fs.writeFileSync(this.sitesConfigPath, this.makeSitesConfig()); }; -exports.NginxService.prototype.makeConfig = function() { +exports.NginxService.prototype.makeSitesConfig = function() { var me = this, - phpSocketPath = me.controller.services['php'].options.socketPath, - phpBootstrapDir = me.controller.services['php'].options.bootstrapDir, + phpSocketPath = me.controller.services['php'].socketPath, + phpBootstrapDir = me.controller.services['php'].bootstrapDir, config = []; // format socket path @@ -173,91 +163,10 @@ exports.NginxService.prototype.makeConfig = function() { phpSocketPath = 'unix:'+phpSocketPath; } - // configure top-level options - config.push( - 'user '+me.options.user+' '+me.options.group+';', - 'worker_processes auto;', - 'pid '+me.options.pidPath+';', - 'error_log '+me.options.errorLogPath+' info;' - ); - - - // configure connection processing - config.push( - 'events {', - ' worker_connections 1024;' - ); - - if (process.platform == 'linux') { - config.push(' use epoll;'); - } - - config.push( - '}' // end events block - ); - - - // configure http options - config.push( - 'http {', - ' include '+me.options.miscConfigDir+'/mime.types;', - ' default_type application/octet-stream;', - - ' log_format main', - ' \'$host $remote_addr - $remote_user [$time_local] \'', - ' \'"$request" $status $bytes_sent \'', - ' \'"$http_referer" "$http_user_agent" \'', - ' \'"$gzip_ratio"\';', - - ' client_header_timeout 10m;', - ' client_body_timeout 10m;', - ' send_timeout 10m;', - - ' connection_pool_size 256;', - ' client_max_body_size 200m;', - ' client_body_buffer_size 128k;', - ' client_header_buffer_size 1k;', - ' large_client_header_buffers 8 512k;', - ' request_pool_size 4k;', - ' server_names_hash_bucket_size 1024;', - ' types_hash_max_size 2048;', - - ' gzip on;', - ' gzip_min_length 1100;', - ' gzip_buffers 4 8k;', - ' gzip_types text/plain text/css text/x-scss text/x-html-template text/x-component text/xml application/xml application/javascript application/json application/php application/atom+xml application/rss+xml application/vnd.ms-fontobject application/x-font-ttf application/xhtml+xml font/opentype image/svg+xml image/x-icon;', - - ' output_buffers 1 32k;', - ' postpone_output 1460;', - - ' sendfile on;', - ' tcp_nopush on;', - ' tcp_nodelay on;', - - ' keepalive_timeout 75 20;', - - ' ignore_invalid_headers on;', - - ' index index.php;', - - ' fastcgi_index index.php;', - ' fastcgi_read_timeout 6h;', - ' fastcgi_buffers 32 64k;', - - ' server_tokens off;' -/* - - ' server {', - ' server_name _;', - ' access_log /emergence/logs/access.log main;', - ' error_log /emergence/logs/error.log info;', - ' }', -*/ - ); - + // configure each site _.each(me.controller.sites.sites, function(site, handle) { var hostnames = site.hostnames.slice(), - siteDir = me.controller.sites.options.sitesDir+'/'+handle, + siteDir = me.controller.sites.sitesDir+'/'+handle, logsDir = siteDir+'/logs', siteConfig = [], sslHostnames, sslHostname; @@ -277,9 +186,9 @@ exports.NginxService.prototype.makeConfig = function() { ' error_log '+logsDir+'/error.log notice;', ' location / {', - ' include '+me.options.miscConfigDir+'/fastcgi_params;', - ' fastcgi_param HTTPS $php_https;', ' fastcgi_pass '+phpSocketPath+';', + ' include '+me.packagePath+'/config/fastcgi_params;', + ' fastcgi_param HTTPS $php_https;', ' fastcgi_param PATH_INFO $fastcgi_script_name;', ' fastcgi_param SITE_ROOT '+siteDir+';', ' fastcgi_param SCRIPT_FILENAME '+phpBootstrapDir+'/web.php;', @@ -290,7 +199,7 @@ exports.NginxService.prototype.makeConfig = function() { // append config config.push( ' server {', - ' listen '+me.options.bindHost+':'+me.options.bindPort+';', + ' listen '+me.bindHost+':'+me.bindPort+';', ' server_name '+hostnames.join(' ')+';', ' set $php_https "";' ); @@ -316,7 +225,7 @@ exports.NginxService.prototype.makeConfig = function() { for (sslHostname in sslHostnames) { config.push( ' server {', - ' listen '+me.options.bindHost+':443;', + ' listen '+me.bindHost+':443;', ' server_name '+sslHostname+';', ' set $php_https on;', @@ -333,10 +242,6 @@ exports.NginxService.prototype.makeConfig = function() { } }); - config.push( - '}' // end http block - ); - return config.join('\n'); }; diff --git a/kernel-lib/services/php-fpm.js b/kernel-lib/services/php-fpm.js index 008e021..f35ff11 100644 --- a/kernel-lib/services/php-fpm.js +++ b/kernel-lib/services/php-fpm.js @@ -15,31 +15,22 @@ exports.PhpFpmService = function(name, controller, options) { // call parent constructor exports.PhpFpmService.super_.apply(me, arguments); - // default options - me.options.bootstrapDir = me.options.bootstrapDir || path.resolve(__dirname, '../../php-bootstrap'); - me.options.configPath = me.options.configPath || controller.options.configDir + '/php-fpm.conf'; - me.options.execPath = me.options.execPath || '/usr/bin/php-fpm'; - me.options.statScripts = me.options.statScripts || false; - me.options.runDir = me.options.runDir || controller.options.runDir + '/php-fpm'; - me.options.logsDir = me.options.logsDir || controller.options.logsDir + '/php-fpm'; - me.options.pidPath = me.options.pidPath || me.options.runDir + '/php-fpm.pid'; - me.options.socketPath = me.options.socketPath || me.options.runDir + '/php-fpm.sock'; - me.options.errorLogPath = me.options.errorLogPath || me.options.logsDir + '/errors.log'; - me.options.user = me.options.user || controller.options.user; - me.options.group = me.options.group || controller.options.group; - - // create required directories - if (!fs.existsSync(me.options.runDir)) { - fs.mkdirSync(me.options.runDir, '775'); - } + // initialize configuration + me.packagePath = options.packagePath; + me.execPath = me.packagePath + '/sbin/php-fpm'; + + me.phpConfigPath = '/hab/svc/emergence-kernel/config/php.ini'; + me.fpmConfigPath = '/hab/svc/emergence-kernel/config/php-fpm'; + me.pidPath = '/hab/svc/emergence-kernel/var/run/php-fpm.pid'; + me.socketPath = '/hab/svc/emergence-kernel/var/run/php-fpm.sock'; + me.errorLogPath = '/hab/svc/emergence-kernel/var/log/php-fpm.err'; + me.dataDir = '/hab/svc/emergence-kernel/var/mariadb'; + me.bootstrapDir = '/hab/svc/emergence-kernel/static/php-bootstrap'; - if (!fs.existsSync(me.options.logsDir)) { - fs.mkdirSync(me.options.logsDir, '775'); - } // check for existing master process - if (fs.existsSync(me.options.pidPath)) { - me.pid = parseInt(fs.readFileSync(me.options.pidPath, 'ascii')); + if (fs.existsSync(me.pidPath)) { + me.pid = parseInt(fs.readFileSync(me.pidPath, 'ascii')); console.log(me.name+': found existing PID: '+me.pid); me.status = 'online'; } @@ -55,16 +46,14 @@ util.inherits(exports.PhpFpmService, require('./abstract.js').AbstractService); exports.PhpFpmService.prototype.start = function() { var me = this; - console.log(me.name+': spawning daemon: '+me.options.execPath); + console.log(me.name+': spawning daemon: '+me.execPath); if (me.pid) { console.log(me.name+': already running with PID '+me.pid); return false; } - this.writeConfig(); - - me.proc = spawn(me.options.execPath, ['--fpm-config', me.options.configPath]); + me.proc = spawn(me.execPath, ['-c', me.phpConfigPath, '--fpm-config', me.fpmConfigPath]); me.proc.on('exit', function (code) { @@ -75,8 +64,8 @@ exports.PhpFpmService.prototype.start = function() { } // look for pid - if (fs.existsSync(me.options.pidPath)) { - me.pid = parseInt(fs.readFileSync(me.options.pidPath, 'ascii')); + if (fs.existsSync(me.pidPath)) { + me.pid = parseInt(fs.readFileSync(me.pidPath, 'ascii')); console.log(me.name+': found new PID: '+me.pid); me.status = 'online'; } else { @@ -131,8 +120,6 @@ exports.PhpFpmService.prototype.restart = function() { return false; } - this.writeConfig(); - try { process.kill(me.pid, 'SIGUSR2'); } catch (error) { @@ -146,67 +133,17 @@ exports.PhpFpmService.prototype.restart = function() { }; -exports.PhpFpmService.prototype.writeConfig = function() { - fs.writeFileSync(this.options.configPath, this.makeConfig()); -}; - -exports.PhpFpmService.prototype.makeConfig = function() { - var me = this, - config = []; - - config.push( - '[global]', - 'pid = '+me.options.pidPath, - 'error_log = '+me.options.errorLogPath - ); - - config.push( - '[www]', - 'user = '+me.options.user, - 'group = '+me.options.group, - 'catch_workers_output = on', - 'listen = '+me.options.socketPath, - 'listen.owner = '+me.options.user, - 'listen.group = '+me.options.group, - 'pm = dynamic', - 'pm.max_children = '+(me.options.maxClients||50), - 'pm.start_servers = '+(me.options.startServers||5), - 'pm.min_spare_servers = '+(me.options.minSpareServers||1), - 'pm.max_spare_servers = '+Math.round((me.options.maxClients||50)/(me.options.startServers||5)) - ); - - if (me.options.statusPath) { - config.push('pm.status_path = '+me.options.statusPath); - } - - config.push( - 'php_admin_flag[short_open_tag] = on', - 'php_admin_value[apc.shm_size] = 512M', - 'php_admin_value[apc.shm_segments] = 1', - 'php_admin_value[apc.slam_defense] = 0', - 'php_admin_value[apc.stat] = '+(me.options.statScripts?'1':'0'), - 'php_admin_value[opcache.validate_timestamps] = '+(me.options.statScripts?'1':'0'), - 'php_admin_value[upload_max_filesize] = '+(me.options.uploadMaxSize ? me.options.uploadMaxSize : '200M'), - 'php_admin_value[post_max_size] = '+(me.options.postMaxSize ? me.options.postMaxSize : '200M'), - 'php_admin_value[memory_limit] = '+(me.options.memoryLimit ? me.options.memoryLimit : '200M'), - 'php_admin_value[error_reporting] = '+(me.options.errorReporting ? me.options.errorReporting : 'E_ALL & ~E_NOTICE'), - 'php_admin_value[date.timezone] = '+(me.options.defaultTimezone ? me.options.defaultTimezone : 'America/New_York') - ); - - return config.join('\n'); -}; - exports.PhpFpmService.prototype.onSiteUpdated = function(siteData) { var me = this, - siteRoot = me.controller.sites.options.sitesDir + '/' + siteData.handle, + siteRoot = me.controller.sites.sitesDir + '/' + siteData.handle, phpClient; console.log(me.name+': clearing config cache for '+siteRoot); // Connect to FPM worker pool phpClient = new phpfpm({ - sockFile: me.options.socketPath, - documentRoot: me.options.bootstrapDir + '/' + sockFile: me.socketPath, + documentRoot: me.bootstrapDir + '/' }); // Clear cached site.json diff --git a/kernel-lib/sites.js b/kernel-lib/sites.js index 956d58f..488dc60 100644 --- a/kernel-lib/sites.js +++ b/kernel-lib/sites.js @@ -6,7 +6,6 @@ var _ = require('underscore'), events = require('events'), posix = require('posix'), spawn = require('child_process').spawn, - hostile = require('hostile'), phpShellScript = path.resolve(__dirname, '../bin/shell'); @@ -21,25 +20,19 @@ exports.Sites = function(config) { // call events constructor events.EventEmitter.call(me); - // initialize options and apply defaults - me.options = options || {}; - me.options.sitesDir = me.options.sitesDir || '/emergence/sites'; - me.options.dataUID = me.options.dataUID || posix.getpwnam(config.user).uid; - me.options.dataGID = me.options.dataGID || posix.getgrnam(config.group).gid; - me.options.dataMode = me.options.dataMode || '775'; + // initialize configuration + me.sitesDir = '/hab/svc/emergence-kernel/data/sites'; + me.dataUid = posix.getpwnam('hab').uid; + me.dataGid = posix.getgrnam('hab').gid; - // create required directories - if (!fs.existsSync(me.options.sitesDir)) { - fs.mkdirSync(me.options.sitesDir, '775'); - } // load sites - console.log('Loading sites from '+me.options.sitesDir+'...'); + console.log('Loading sites from '+me.sitesDir+'...'); me.sites = {}; - _.each(fs.readdirSync(me.options.sitesDir), function(handle) { + _.each(fs.readdirSync(me.sitesDir), function(handle) { try { - me.sites[handle] = JSON.parse(fs.readFileSync(me.options.sitesDir+'/'+handle+'/site.json', 'ascii')); + me.sites[handle] = JSON.parse(fs.readFileSync(me.sitesDir+'/'+handle+'/site.json', 'ascii')); me.sites[handle].handle = handle; console.log('-Loaded: '+me.sites[handle].primary_hostname); } catch (error) { @@ -86,7 +79,7 @@ exports.Sites.prototype.handleRequest = function(request, response, server) { } var handle = request.path[1], - siteDir = me.options.sitesDir + '/' + handle, + siteDir = me.sitesDir + '/' + handle, siteConfigPath = siteDir + '/site.json', params = JSON.parse(request.content), siteData; @@ -145,7 +138,7 @@ exports.Sites.prototype.handleRequest = function(request, response, server) { console.log('Executing shell post for ' + request.path[1] + ':'); console.log(requestData); - phpProc = spawn(phpShellScript, [request.path[1]]); + phpProc = spawn(phpShellScript, [request.path[1]], { uid: me.dataUid, gid: me.dataGid }); phpProcInitialized = false; phpProc.stderr.on('data', function(data) { @@ -178,7 +171,7 @@ exports.Sites.prototype.handleRequest = function(request, response, server) { console.log('Creating developer for ' + request.path[1] + ':' + phpShellScript); response.writeHead(200, {'Content-Type':'application/json'}); - phpProc = spawn(phpShellScript, [request.path[1], '--stdin']); + phpProc = spawn(phpShellScript, [request.path[1], '--stdin'], { uid: me.dataUid, gid: me.dataGid }); phpProc.stdout.on('data', function(data) { console.log('php-cli stdout: ' + data); @@ -220,16 +213,12 @@ exports.Sites.prototype.handleRequest = function(request, response, server) { cfgResult = me.writeSiteConfig(requestData); if (cfgResult.isNew) { - // write primary hostname to /etc/hosts - hostile.set('127.0.0.1', cfgResult.site.primary_hostname); - console.log('added ' + cfgResult.site.primary_hostname + ' to /etc/hosts'); - // notify plugins me.emit('siteCreated', cfgResult.site, requestData, { databaseReady: function() { // execute onSiteCreated within site's container console.log('Executing Site::onSiteCreated() via php-cli'); - phpProc = spawn(phpShellScript, [cfgResult.site.handle]); + phpProc = spawn(phpShellScript, [cfgResult.site.handle], { uid: me.dataUid, gid: me.dataGid }); phpProc.stdout.on('data', function(data) { console.log('php-cli stdout: ' + data); }); phpProc.stderr.on('data', function(data) { console.log('php-cli stderr: ' + data); }); @@ -307,7 +296,7 @@ exports.Sites.prototype.writeSiteConfig = function(requestData) { } // create site directory - var siteDir = me.options.sitesDir+'/'+siteData.handle, + var siteDir = me.sitesDir+'/'+siteData.handle, dataDir = siteDir + '/data', siteDataDir = siteDir + '/site-data', siteConfigPath = siteDir + '/site.json'; @@ -318,13 +307,13 @@ exports.Sites.prototype.writeSiteConfig = function(requestData) { } if (!fs.existsSync(dataDir)) { - fs.mkdirSync(dataDir, me.options.dataMode); - fs.chownSync(dataDir, me.options.dataUID, me.options.dataGID); + fs.mkdirSync(dataDir, '775'); + fs.chownSync(dataDir, me.dataUid, me.dataGid); } if (!fs.existsSync(siteDataDir)) { - fs.mkdirSync(siteDataDir, me.options.dataMode); - fs.chownSync(siteDataDir, me.options.dataUID, me.options.dataGID); + fs.mkdirSync(siteDataDir, '775'); + fs.chownSync(siteDataDir, me.dataUid, me.dataGid); } // write site config to file @@ -341,7 +330,7 @@ exports.Sites.prototype.writeSiteConfig = function(requestData) { exports.Sites.prototype.updateSiteConfig = function(handle, changes) { var me = this, - siteDir = me.options.sitesDir+'/'+handle, + siteDir = me.sitesDir+'/'+handle, filename = siteDir+'/site.json', siteData = this.sites[handle], create_user; diff --git a/package.json b/package.json index 01ebf42..eb80d45 100644 --- a/package.json +++ b/package.json @@ -1,15 +1,17 @@ { "name": "emergence", "preferGlobal": true, - "version": "0.0.3", + "version": "1.0.0", "license": "MIT", + "engines": { + "node": "6.10.3" + }, "dependencies": { "underscore": "1.3.x", "mariasql": "^0.2.6", "node-static": "0.6.x", "http-auth": "1.2.7", "posix": "^4.0.2", - "hostile": "^1.0.2", "shelljs": "^0.5.3", "semver": "^5.1.0", "underscore-cli": "^0.2.19", @@ -25,5 +27,8 @@ "repository": { "type": "git", "url": "https://github.com/JarvusInnovations/Emergence.git" + }, + "scripts": { + "start": "node bin/kernel" } } diff --git a/php-bootstrap/lib/Site.class.php b/php-bootstrap/lib/Site.class.php index 626d0e2..55a95b1 100644 --- a/php-bootstrap/lib/Site.class.php +++ b/php-bootstrap/lib/Site.class.php @@ -165,19 +165,19 @@ public static function handleRequest() } // handle CORS headers - if (isset($_SERVER['HTTP_ORIGIN'])) { - $hostname = strtolower(parse_url($_SERVER['HTTP_ORIGIN'], PHP_URL_HOST)); - if ($hostname == strtolower(static::$hostname) || static::$permittedOrigins == '*' || in_array($hostname, static::$permittedOrigins)) { - header('Access-Control-Allow-Origin: ' . $_SERVER['HTTP_ORIGIN']); - header('Access-Control-Allow-Credentials: true'); - //header('Access-Control-Max-Age: 86400') - } else { - header('HTTP/1.1 403 Forbidden'); - exit(); + if ($_SERVER['REQUEST_METHOD'] == 'OPTIONS' && isset($_SERVER['HTTP_ACCESS_CONTROL_REQUEST_METHOD'])) { + if (isset($_SERVER['HTTP_ORIGIN'])) { + $hostname = strtolower(parse_url($_SERVER['HTTP_ORIGIN'], PHP_URL_HOST)); + if ($hostname == strtolower(static::$hostname) || static::$permittedOrigins == '*' || in_array($hostname, static::$permittedOrigins)) { + header('Access-Control-Allow-Origin: ' . $_SERVER['HTTP_ORIGIN']); + header('Access-Control-Allow-Credentials: true'); + //header('Access-Control-Max-Age: 86400') + } else { + header('HTTP/1.1 403 Forbidden'); + die('CORS access denied for given origin'); + } } - } - if ($_SERVER['REQUEST_METHOD'] == 'OPTIONS' && isset($_SERVER['HTTP_ACCESS_CONTROL_REQUEST_METHOD'])) { header('Access-Control-Allow-Methods: ' . $_SERVER['HTTP_ACCESS_CONTROL_REQUEST_METHOD']); if (isset($_SERVER['HTTP_ACCESS_CONTROL_REQUEST_HEADERS'])) { diff --git a/php-bootstrap/mail.php b/php-bootstrap/mail.php index 92cffbe..b3e00a0 100644 --- a/php-bootstrap/mail.php +++ b/php-bootstrap/mail.php @@ -10,26 +10,26 @@ // get hostmap // TODO: have the kernel maintain this file -$hostmapPath = '/emergence/services/hostmap.inc.php'; +$hostmapPath = '/hab/svc/emergence-kernel/var/hostmap.inc.php'; if (is_readable($hostmapPath)) { $hostmap = require($hostmapPath); } else { $hostmap = []; - - foreach (glob('/emergence/sites/*', GLOB_ONLYDIR) AS $sitePath) { + + foreach (glob('/hab/svc/emergence-kernel/data/sites/*', GLOB_ONLYDIR) AS $sitePath) { $configPath = "$sitePath/site.json"; if (!is_readable($configPath)) { continue; } - + $config = @json_decode(file_get_contents($configPath), true); - + if (!$config) { continue; } - + $hostnames = array_unique(array_merge([$config['primary_hostname']], $config['hostnames'])); - + foreach ($hostnames AS $hostname) { $hostmap['/^' . str_replace('\\*', '.*', preg_quote($hostname)) . '$/i'] = basename($sitePath); } @@ -58,7 +58,7 @@ // bootstrap emergence require('bootstrap.inc.php'); Site::$debug = true; -Site::initialize("/emergence/sites/$siteHandle", $hostname); +Site::initialize("/hab/svc/emergence-kernel/data/sites/$siteHandle", $hostname); // delegate remainder of request to email router diff --git a/rebuild.sh b/rebuild.sh new file mode 100755 index 0000000..c48424a --- /dev/null +++ b/rebuild.sh @@ -0,0 +1,8 @@ +#!/bin/bash + +# reset runtime env +./clean-runtime.sh + +rm -R /hab/svc/*/ + +exec build \ No newline at end of file diff --git a/reset-emergence.sh b/reset-emergence.sh new file mode 100755 index 0000000..0d656db --- /dev/null +++ b/reset-emergence.sh @@ -0,0 +1,4 @@ +#!/bin/bash + +./clean-runtime.sh +rm -R /hab/svc/emergence-kernel \ No newline at end of file diff --git a/run-kernel.sh b/run-kernel.sh new file mode 100755 index 0000000..365d6af --- /dev/null +++ b/run-kernel.sh @@ -0,0 +1,7 @@ +#!/bin/bash + +./clean-runtime.sh + +export NODE_PATH="`hab pkg path emergence/emergence-kernel`/app/node_modules/" + +exec hab pkg exec core/node node bin/kernel \ No newline at end of file diff --git a/run-service.sh b/run-service.sh new file mode 100755 index 0000000..42167f0 --- /dev/null +++ b/run-service.sh @@ -0,0 +1,5 @@ +#!/bin/bash + +./clean-runtime.sh + +exec hab svc start emergence/emergence-kernel \ No newline at end of file diff --git a/run-studio.sh b/run-studio.sh new file mode 100755 index 0000000..59daa06 --- /dev/null +++ b/run-studio.sh @@ -0,0 +1,6 @@ +#!/bin/bash + +export HAB_DOCKER_OPTS="-p 9080:80 -p 9083:9083" +export HAB_ORIGIN=emergence + +exec hab studio enter diff --git a/service-plans/mariadb/config/init.sql b/service-plans/mariadb/config/init.sql new file mode 100644 index 0000000..8c34743 --- /dev/null +++ b/service-plans/mariadb/config/init.sql @@ -0,0 +1,6 @@ +UPDATE mysql.user SET Password=PASSWORD('{{cfg.root_password}}') WHERE User='root'; +DELETE FROM mysql.user WHERE User=''; +DELETE FROM mysql.user WHERE User='root' AND Host NOT IN ('localhost', '127.0.0.1', '::1'); +DROP DATABASE IF EXISTS test; +DELETE FROM mysql.db WHERE Db='test' OR Db='test\\_%'; +FLUSH PRIVILEGES; \ No newline at end of file diff --git a/service-plans/mariadb/config/my.cnf b/service-plans/mariadb/config/my.cnf new file mode 100644 index 0000000..b03580e --- /dev/null +++ b/service-plans/mariadb/config/my.cnf @@ -0,0 +1,23 @@ +[mysqld_safe] +pid-file = {{pkg.svc_var_path}}/mysqld.pid +socket = {{pkg.svc_var_path}}/mysqld.sock +nice = 0 + +[mysqld] +port = {{cfg.port}} +socket = {{pkg.svc_var_path}}/mysqld.sock +datadir = {{pkg.svc_data_path}} +pid-file = {{pkg.svc_var_path}}/mysqld.pid +log-error = mysqld-err.log +log-isam = mysqld-isam.log +log-tc = mysqld-tc.log +aria-log-dir-path = {{pkg.svc_data_path}}/mysqld-aria +master-info-file = mysqld-master.info +{{~#if cfg.general_log}} +general-log = TRUE +general-log-file = {{pkg.svc_var_path}}/mysqld.log +{{~/if}} +{{~#if cfg.bind_address}} +bind-address = {{cfg.bind_address}} +{{~/if}} +init-file = {{pkg.svc_config_path}}/init.sql \ No newline at end of file diff --git a/service-plans/mariadb/default.toml b/service-plans/mariadb/default.toml new file mode 100644 index 0000000..52a153a --- /dev/null +++ b/service-plans/mariadb/default.toml @@ -0,0 +1,4 @@ +root_password = "" +port = 3306 +bind_address = "127.0.0.1" +general_log = true \ No newline at end of file diff --git a/service-plans/mariadb/hooks/init b/service-plans/mariadb/hooks/init new file mode 100644 index 0000000..e5a3270 --- /dev/null +++ b/service-plans/mariadb/hooks/init @@ -0,0 +1,14 @@ +#!/bin/sh + +exec 2>&1 + +#mariadb aria files folder +mkdir -p {{pkg.svc_data_path}}/mysqld-aria + +#mariadb needs to create 'mysql' database +if [ ! -f {{pkg.svc_files_path}}/db_installed ]; then + cd {{pkg.path}} && \ + scripts/mysql_install_db --defaults-file={{pkg.svc_config_path}}/my.cnf \ + --user={{pkg.svc_user}} > \ + {{pkg.svc_files_path}}/db_installed +fi \ No newline at end of file diff --git a/service-plans/mariadb/hooks/run b/service-plans/mariadb/hooks/run new file mode 100644 index 0000000..fffcd13 --- /dev/null +++ b/service-plans/mariadb/hooks/run @@ -0,0 +1,7 @@ +#!/bin/sh + +exec 2>&1 + +#start the mysql server +mysqld_safe --defaults-file={{pkg.svc_config_path}}/my.cnf \ + --user={{pkg.svc_user}} \ No newline at end of file diff --git a/service-plans/mariadb/plan.sh b/service-plans/mariadb/plan.sh new file mode 100644 index 0000000..ab6bf42 --- /dev/null +++ b/service-plans/mariadb/plan.sh @@ -0,0 +1,49 @@ +pkg_name=mariadb +pkg_origin=emergence +pkg_version=10.2.6 +pkg_description="An open source monitoring software for networks and applications" +pkg_maintainer="The Habitat Maintainers