├── .gitignore ├── Gruntfile.js ├── README.md ├── app └── code │ └── README.md ├── behat.yml ├── composer.json ├── composer.lock ├── docker-compose.yml ├── docker ├── elk │ └── logstash │ │ ├── logstash.conf │ │ └── patterns │ │ ├── default.conf │ │ └── nginx.conf ├── env-sample.php ├── nginx │ ├── Dockerfile │ ├── default.conf │ ├── magento2.conf │ └── nginx.conf ├── node │ └── Dockerfile ├── php │ ├── Dockerfile │ ├── bin │ │ └── start │ └── conf │ │ ├── crontab │ │ ├── docker-php-ext-xdebug.ini │ │ ├── php-fpm.conf │ │ └── php.ini ├── ssl │ ├── Dockerfile │ ├── default.conf │ ├── m2docker.cert │ ├── m2docker.key │ ├── nginx.conf │ └── ssl.conf └── varnish │ ├── Dockerfile │ └── default.vcl ├── features ├── bootstrap │ ├── Context │ │ └── HealthCheckContext.php │ └── Page │ │ └── Homepage.php └── suites │ └── m2docker │ └── healthcheck.feature ├── package.json └── phpspec.yml /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | /.buildpath 3 | /.cache 4 | /.metadata 5 | /.project 6 | /.settings 7 | atlassian* 8 | /nbproject 9 | /sitemap 10 | /.idea 11 | /.gitattributes 12 | 13 | /app/etc/* 14 | /app/code/Magento 15 | /app/design/*/Magento 16 | /app/i18n/magento 17 | /app/*.* 18 | 19 | /bin 20 | 21 | /dev 22 | 23 | /lib 24 | 25 | /node_modules 26 | 27 | /phpserver 28 | 29 | /pub 30 | 31 | /setup 32 | 33 | /update 34 | 35 | /var 36 | 37 | /vendor 38 | 39 | /*.* 40 | 41 | !/.gitignore 42 | !/composer.json 43 | !/composer.lock 44 | !/docker-compose.yml 45 | !/phpspec.yml 46 | !/behat.yml 47 | !/README.md 48 | !/Gruntfile.js 49 | !/package.json 50 | 51 | **/node_modules/ 52 | npm-debug.log -------------------------------------------------------------------------------- /Gruntfile.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright © 2016 Magento. All rights reserved. 3 | * See COPYING.txt for license details. 4 | */ 5 | 6 | // For performance use one level down: 'name/{,*/}*.js' 7 | // If you want to recursively match all subfolders, use: 'name/**/*.js' 8 | module.exports = function (grunt) { 9 | 'use strict'; 10 | 11 | var _ = require('underscore'), 12 | path = require('path'), 13 | themes = require('./dev/tools/grunt/configs/themes'), 14 | configDir = './dev/tools/grunt/configs', 15 | tasks = grunt.file.expand('./dev/tools/grunt/tasks/*'); 16 | 17 | tasks = _.map(tasks, function(task){ return task.replace('.js', '') }); 18 | tasks.push('time-grunt'); 19 | tasks.forEach(function (task) { 20 | require(task)(grunt); 21 | }); 22 | 23 | require('load-grunt-config')(grunt, { 24 | configPath: path.join(__dirname, configDir), 25 | init: true, 26 | jitGrunt: { 27 | staticMappings: { 28 | usebanner: 'grunt-banner' 29 | } 30 | } 31 | }); 32 | 33 | _.each({ 34 | /** 35 | * Assembling tasks. 36 | * ToDo: define default tasks. 37 | */ 38 | default: function () { 39 | grunt.log.subhead('I\'m default task and at the moment I\'m empty, sorry :/'); 40 | }, 41 | 42 | /** 43 | * Production preparation task. 44 | */ 45 | prod: function (component) { 46 | var tasks = [ 47 | 'less', 48 | 'autoprefixer', 49 | 'cssmin', 50 | 'usebanner' 51 | ].map(function(task){ 52 | return task + ':' + component; 53 | }); 54 | 55 | if (typeof component === 'undefined') { 56 | grunt.log.subhead('Tip: Please make sure that u specify prod subtask. By default prod task do nothing'); 57 | } else { 58 | grunt.task.run(tasks); 59 | } 60 | }, 61 | 62 | /** 63 | * Refresh themes. 64 | */ 65 | refresh: function () { 66 | var tasks = [ 67 | 'clean', 68 | 'exec:all' 69 | ]; 70 | _.each(themes, function(theme, name) { 71 | tasks.push('less:' + name); 72 | }); 73 | grunt.task.run(tasks); 74 | }, 75 | 76 | /** 77 | * Documentation 78 | */ 79 | documentation: [ 80 | 'replace:documentation', 81 | 'less:documentation', 82 | 'styledocco:documentation', 83 | 'usebanner:documentationCss', 84 | 'usebanner:documentationLess', 85 | 'usebanner:documentationHtml', 86 | 'clean:var', 87 | 'clean:pub' 88 | ], 89 | 90 | 'legacy-build': [ 91 | 'mage-minify:legacy' 92 | ], 93 | 94 | spec: function (theme) { 95 | var runner = require('./dev/tests/js/jasmine/spec_runner'); 96 | 97 | runner.init(grunt, { theme: theme }); 98 | 99 | grunt.task.run(runner.getTasks()); 100 | } 101 | }, function (task, name) { 102 | grunt.registerTask(name, task); 103 | }); 104 | }; 105 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Magento 2 - Docker Compose 2 | === 3 | 4 | Magento 2.1.1 (with sample data) Docker dev environment which includes: 5 | - PHP 7.0.x - Will use latest FPM alpine base image 6 | - Nginx 7 | - Self signed SSL (separate nginx container for ssl offloading, this is by default. Using dock-cli, it can be accessed via dnsdock alias: [https://m2.docker](https://m2.docker)) 8 | - Varnish 4.x 9 | - MariaDB 10 | - Redis 11 | - Node 6.7.x for frontend dependencies 12 | - ELK stack for logs 13 | - Mailcatcher (accessed via mailcatcher.docker) 14 | - Selenium for Behat tests 15 | 16 | This setup utilises the [dock-cli](https://github.com/inviqa/dock-cli) setup, which provides a DNSDock container, 17 | docker-machine and DNS management for the host machine. If you would like to use Docker for Mac, this setup 18 | would need some slight tweaking, such as using a combination of [dnsmasq and nginx proxy containers](https://adrianperez.org/improving-dev-environments-all-the-http-things/) 19 | 20 | I recommend using [docker-machine-nfs](https://github.com/adlogix/docker-machine-nfs): 21 | 22 | `docker-machine-nfs $DOCKER_MACHINE_NAME --shared-folder=/Users/` 23 | 24 | ###Installation 25 | ``` 26 | dock-cli start OR docker-compose up -d 27 | docker-compose exec php ./bin/composer install 28 | ``` 29 | 30 | Copy the sample env.php over for some suggested settings (e.g use redis for session storage) 31 | ``` 32 | cp docker/env-sample.php app/etc/env.php 33 | ``` 34 | 35 | Run Magento 2 CLI installation command with example settings: 36 | 37 | ``` 38 | docker-compose exec php php ./bin/magento setup:install --admin-firstname=admin --admin-lastname=admin --admin-email=admin@example.com --admin-user=admin --admin-password=123123pass --base-url=https://m2.docker/ --base-url-secure=https://m2.docker/ --use-secure=1 --use-secure-admin=1 --db-password=magento2 --db-host=mariadb.docker --db-name=magento2 --db-user=magento2 --language=en_GB --currency=GBP --timezone=Europe/London --use-sample-data 39 | # you might have to chmod +x ./bin/magento if you get permission errors 40 | ``` 41 | 42 | This will install a Magento 2 instance with sample data, if you don't want this data then remove the flag `--use-sample-data` 43 | 44 | Varnish can be turned by selecting the 'Varnish Cache' option via the admin dashboard: 45 | `Stores > Configuration > Advanced > System > Full Page Cache > Caching Application` 46 | 47 | You can then clear the cache: 48 | ``` 49 | docker-compose exec php ./bin/magento cache:clean 50 | ``` 51 | 52 | --- 53 | 54 | To view the status of the containers: 55 | ``` 56 | dock-cli ps or docker-compose ps -q 57 | ``` 58 | 59 | To start a shell in the web container/service, for debugging purposes: 60 | ``` 61 | docker-compose exec web sh 62 | ``` 63 | 64 | There can be benefits in generating an optimized autoloader. 65 | ``` 66 | docker-compose exec php ./bin/composer dump-autoload -o 67 | ``` 68 | 69 | You can run behat tests: 70 | ``` 71 | docker-compose exec php ./bin/behat 72 | ``` 73 | 74 | To build the frontend dependencies, you can use the node container (`--rm` flag to remove containers once completed, to keep setup clean) 75 | ``` 76 | docker-compose run --rm node npm install 77 | docker-compose run --rm node grunt 78 | ``` 79 | Credit to [Mage Inferno](https://github.com/mageinferno/docker-magento2-php) for some inspiration and ideas on how all of this could be down :+1: -------------------------------------------------------------------------------- /app/code/README.md: -------------------------------------------------------------------------------- 1 | Add project specific module code here. -------------------------------------------------------------------------------- /behat.yml: -------------------------------------------------------------------------------- 1 | default: 2 | suites: 3 | m2docker: 4 | paths: 5 | features: features/suites/m2docker 6 | contexts: 7 | - Context\HealthCheckContext 8 | extensions: 9 | Cjm\Behat\StepThroughExtension: ~ 10 | SensioLabs\Behat\PageObjectExtension: 11 | namespaces: 12 | page: [Page] 13 | Behat\MinkExtension: 14 | base_url: 'https://m2.docker' 15 | sessions: 16 | default: 17 | selenium2: 18 | wd_host: http://selenium:4444/wd/hub 19 | browser: firefox 20 | goutte: 21 | guzzle_parameters: 22 | curl.options: 23 | CURLOPT_SSL_VERIFYPEER: false 24 | CURLOPT_CERTINFO: false 25 | CURLOPT_TIMEOUT: 120 26 | ssl.certificate_authority: false 27 | selenium2: 28 | wd_host: http://selenium:4444/wd/hub 29 | browser: firefox 30 | show_tmp_dir: /tmp -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "project", 3 | "repositories": [ 4 | { 5 | "type": "composer", 6 | "url": "https://repo.magento.com/" 7 | }, 8 | { 9 | "type": "git", 10 | "url": "https://github.com/nagno/phpspec-bootstrap-magento2.git" 11 | }, 12 | { 13 | "type": "git", 14 | "url": "https://github.com/drewhunter/composer-patches.git" 15 | } 16 | ], 17 | "require": { 18 | "magento/product-community-edition": "2.1.1", 19 | "composer/composer": "@alpha" 20 | }, 21 | "require-dev": { 22 | "magento/module-bundle-sample-data": "*", 23 | "magento/module-theme-sample-data": "*", 24 | "magento/module-widget-sample-data": "*", 25 | "magento/module-catalog-sample-data": "*", 26 | "magento/module-customer-sample-data": "*", 27 | "magento/module-cms-sample-data": "*", 28 | "magento/module-catalog-rule-sample-data": "*", 29 | "magento/module-sales-rule-sample-data": "*", 30 | "magento/module-review-sample-data": "*", 31 | "magento/module-tax-sample-data": "*", 32 | "magento/module-sales-sample-data": "*", 33 | "magento/module-grouped-product-sample-data": "*", 34 | "magento/module-downloadable-sample-data": "*", 35 | "magento/module-msrp-sample-data": "*", 36 | "magento/module-configurable-sample-data": "*", 37 | "magento/module-product-links-sample-data": "*", 38 | "magento/module-wishlist-sample-data": "*", 39 | "magento/module-swatches-sample-data": "*", 40 | "magento/sample-data-media": "*", 41 | "magento/module-offline-shipping-sample-data": "*", 42 | "behat/behat": "^3.1.0", 43 | "behat/mink": "^1.7.1", 44 | "behat/mink-extension": "^2.2", 45 | "behat/mink-goutte-driver": "^1.2.1", 46 | "behat/mink-selenium2-driver": "^1.3.1", 47 | "sensiolabs/behat-page-object-extension": "^2.0.0", 48 | "bex/behat-magento2-init": "dev-master", 49 | "ciaranmcnulty/behat-stepthroughextension": "dev-master", 50 | "bossa/phpspec2-expect": "1.*", 51 | "phpspec/phpspec": "~2.3", 52 | "nagno/phpspec-bootstrap-magento2": "~1.0" 53 | }, 54 | "config": { 55 | "use-include-path": true, 56 | "bin-dir": "bin" 57 | }, 58 | "autoload": { 59 | "psr-4": { 60 | "Magento\\Framework\\": "lib/internal/Magento/Framework/", 61 | "Magento\\Setup\\": "setup/src/Magento/Setup/", 62 | "Magento\\": "app/code/Magento/" 63 | }, 64 | "psr-0": { 65 | "": "app/code/" 66 | }, 67 | "files": [ 68 | "app/etc/NonComposerComponentRegistration.php" 69 | ] 70 | }, 71 | "autoload-dev": { 72 | "psr-4": { 73 | "Magento\\Sniffs\\": "dev/tests/static/framework/Magento/Sniffs/", 74 | "Magento\\Tools\\": "dev/tools/Magento/Tools/", 75 | "Magento\\Tools\\Sanity\\": "dev/build/publication/sanity/Magento/Tools/Sanity/", 76 | "Magento\\TestFramework\\Inspection\\": "dev/tests/static/framework/Magento/TestFramework/Inspection/", 77 | "Magento\\TestFramework\\Utility\\": "dev/tests/static/framework/Magento/TestFramework/Utility/" 78 | } 79 | }, 80 | "minimum-stability": "alpha", 81 | "prefer-stable": true, 82 | "extra": { 83 | "magento-force": "override" 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | ssl: 2 | build: ./docker/ssl 3 | links: 4 | - varnish 5 | volumes_from: 6 | - appdata 7 | - logs 8 | environment: 9 | DNSDOCK_ALIAS: m2.docker 10 | 11 | varnish: 12 | build: ./docker/varnish 13 | links: 14 | - web 15 | volumes_from: 16 | - appdata 17 | 18 | web: 19 | build: ./docker/nginx 20 | links: 21 | - php 22 | - db 23 | - redis 24 | volumes_from: 25 | - appdata 26 | - logs 27 | 28 | appdata: 29 | image: tianon/true 30 | volumes: 31 | - ".:/var/www/html" 32 | 33 | php: 34 | build: ./docker/php 35 | links: 36 | - db 37 | - redis 38 | - mail 39 | - logs 40 | - selenium 41 | volumes_from: 42 | - appdata 43 | 44 | db: 45 | image: mariadb:latest 46 | volumes: 47 | - "/var/lib/mysql" 48 | environment: 49 | - MYSQL_ROOT_PASSWORD=root 50 | - MYSQL_DATABASE=magento2 51 | - MYSQL_USER=magento2 52 | - MYSQL_PASSWORD=magento2 53 | 54 | redis: 55 | image: redis 56 | expose: 57 | - "6379" 58 | 59 | 60 | logs: 61 | image: tianon/true 62 | volumes: 63 | - /var/log/nginx 64 | - /var/lib/elasticsearch 65 | 66 | elk: 67 | image: willdurand/elk 68 | volumes: 69 | - ./docker/elk/logstash:/etc/logstash 70 | - ./docker/elk/logstash/patterns:/opt/logstash/patterns 71 | volumes_from: 72 | - appdata 73 | - logs 74 | 75 | mail: 76 | image: helder/mailcatcher 77 | 78 | selenium: 79 | image: selenium/standalone-firefox:2.53.1 80 | expose: 81 | - "4444" 82 | 83 | node: 84 | build: ./docker/node 85 | volumes_from: 86 | - appdata -------------------------------------------------------------------------------- /docker/elk/logstash/logstash.conf: -------------------------------------------------------------------------------- 1 | input { 2 | file { 3 | type => "nginx_access" 4 | path => "/var/log/nginx/access.log" 5 | start_position => beginning 6 | } 7 | file { 8 | type => "nginx_error" 9 | path => "/var/log/nginx/error.log" 10 | start_position => beginning 11 | } 12 | file { 13 | type => "nginx_ssl_access" 14 | path => "/var/log/nginx/ssl-access.log" 15 | start_position => beginning 16 | } 17 | file { 18 | type => "nginx_ssl_error" 19 | path => "/var/log/nginx/ssl-error.log" 20 | start_position => beginning 21 | } 22 | file { 23 | type => "magento_system" 24 | path => "/var/www/html/var/log/system.log" 25 | start_position => beginning 26 | } 27 | file { 28 | type => "magento_exception" 29 | path => "/var/www/html/var/log/exception.log" 30 | start_position => beginning 31 | } 32 | file { 33 | type => "magento_debug" 34 | path => "/var/www/html/var/log/debug.log" 35 | start_position => beginning 36 | } 37 | } 38 | 39 | filter { 40 | if [type] == "nginx_access" { 41 | grok { 42 | patterns_dir => "./patterns" 43 | match => { "message" => "%{NGINXACCESS}"} 44 | } 45 | } 46 | if [type] == "nginx_ssl_access" { 47 | grok { 48 | patterns_dir => "./patterns" 49 | match => { "message" => "%{NGINXACCESS}"} 50 | } 51 | } 52 | if [type] == "nginx_ssl_error" { 53 | grok { 54 | patterns_dir => "./patterns" 55 | match => { "message" => "%{NGINXERROR}"} 56 | overwrite => [ "message" ] 57 | } 58 | } 59 | } 60 | 61 | output { 62 | elasticsearch { 63 | host => "localhost" 64 | cluster => "logstash" 65 | } 66 | } -------------------------------------------------------------------------------- /docker/elk/logstash/patterns/default.conf: -------------------------------------------------------------------------------- 1 | USERNAME [a-zA-Z0-9._-]+ 2 | USER %{USERNAME} 3 | INT (?:[+-]?(?:[0-9]+)) 4 | BASE10NUM (?[+-]?(?:(?:[0-9]+(?:\.[0-9]+)?)|(?:\.[0-9]+))) 5 | NUMBER (?:%{BASE10NUM}) 6 | BASE16NUM (?(?"(?>\\.|[^\\"]+)+"|""|(?>'(?>\\.|[^\\']+)+')|''|(?>`(?>\\.|[^\\`]+)+`)|``)) 17 | UUID [A-Fa-f0-9]{8}-(?:[A-Fa-f0-9]{4}-){3}[A-Fa-f0-9]{12} 18 | # Networking 19 | MAC (?:%{CISCOMAC}|%{WINDOWSMAC}|%{COMMONMAC}) 20 | CISCOMAC (?:(?:[A-Fa-f0-9]{4}\.){2}[A-Fa-f0-9]{4}) 21 | WINDOWSMAC (?:(?:[A-Fa-f0-9]{2}-){5}[A-Fa-f0-9]{2}) 22 | COMMONMAC (?:(?:[A-Fa-f0-9]{2}:){5}[A-Fa-f0-9]{2}) 23 | IPV6 ((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:)))(%.+)? 24 | IPV4 (?/(?>[\w_%!$@:.,-]+|\\.)*)+ 33 | TTY (?:/dev/(pts|tty([pq])?)(\w+)?/?(?:[0-9]+)) 34 | WINPATH (?>[A-Za-z]+:|\\)(?:\\[^\\?*]*)+ 35 | URIPROTO [A-Za-z]+(\+[A-Za-z+]+)? 36 | URIHOST %{IPORHOST}(?::%{POSINT:port})? 37 | # uripath comes loosely from RFC1738, but mostly from what Firefox 38 | # doesn't turn into %XX 39 | URIPATH (?:/[A-Za-z0-9$.+!*'(){},~:;=@#%_\-]*)+ 40 | #URIPARAM \?(?:[A-Za-z0-9]+(?:=(?:[^&]*))?(?:&(?:[A-Za-z0-9]+(?:=(?:[^&]*))?)?)*)? 41 | URIPARAM \?[A-Za-z0-9$.+!*'|(){},~@#%&/=:;_?\-\[\]]* 42 | URIPATHPARAM %{URIPATH}(?:%{URIPARAM})? 43 | URI %{URIPROTO}://(?:%{USER}(?::[^@]*)?@)?(?:%{URIHOST})?(?:%{URIPATHPARAM})? 44 | # Months: January, Feb, 3, 03, 12, December 45 | MONTH \b(?:Jan(?:uary)?|Feb(?:ruary)?|Mar(?:ch)?|Apr(?:il)?|May|Jun(?:e)?|Jul(?:y)?|Aug(?:ust)?|Sep(?:tember)?|Oct(?:ober)?|Nov(?:ember)?|Dec(?:ember)?)\b 46 | MONTHNUM (?:0?[1-9]|1[0-2]) 47 | MONTHNUM2 (?:0[1-9]|1[0-2]) 48 | MONTHDAY (?:(?:0[1-9])|(?:[12][0-9])|(?:3[01])|[1-9]) 49 | # Days: Monday, Tue, Thu, etc... 50 | DAY (?:Mon(?:day)?|Tue(?:sday)?|Wed(?:nesday)?|Thu(?:rsday)?|Fri(?:day)?|Sat(?:urday)?|Sun(?:day)?) 51 | # Years? 52 | YEAR (?>\d\d){1,2} 53 | HOUR (?:2[0123]|[01]?[0-9]) 54 | MINUTE (?:[0-5][0-9]) 55 | # '60' is a leap second in most time standards and thus is valid. 56 | SECOND (?:(?:[0-5]?[0-9]|60)(?:[:.,][0-9]+)?) 57 | TIME (?!<[0-9])%{HOUR}:%{MINUTE}(?::%{SECOND})(?![0-9]) 58 | # datestamp is YYYY/MM/DD-HH:MM:SS.UUUU (or something like it) 59 | DATE_US %{MONTHNUM}[/-]%{MONTHDAY}[/-]%{YEAR} 60 | DATE_EU %{MONTHDAY}[./-]%{MONTHNUM}[./-]%{YEAR} 61 | ISO8601_TIMEZONE (?:Z|[+-]%{HOUR}(?::?%{MINUTE})) 62 | ISO8601_SECOND (?:%{SECOND}|60) 63 | TIMESTAMP_ISO8601 %{YEAR}-%{MONTHNUM}-%{MONTHDAY}[T ]%{HOUR}:?%{MINUTE}(?::?%{SECOND})?%{ISO8601_TIMEZONE}? 64 | DATE %{DATE_US}|%{DATE_EU} 65 | DATESTAMP %{DATE}[- ]%{TIME} 66 | TZ (?:[PMCE][SD]T|UTC) 67 | DATESTAMP_RFC822 %{DAY} %{MONTH} %{MONTHDAY} %{YEAR} %{TIME} %{TZ} 68 | DATESTAMP_RFC2822 %{DAY}, %{MONTHDAY} %{MONTH} %{YEAR} %{TIME} %{ISO8601_TIMEZONE} 69 | DATESTAMP_OTHER %{DAY} %{MONTH} %{MONTHDAY} %{TIME} %{TZ} %{YEAR} 70 | DATESTAMP_EVENTLOG %{YEAR}%{MONTHNUM2}%{MONTHDAY}%{HOUR}%{MINUTE}%{SECOND} 71 | # Syslog Dates: Month Day HH:MM:SS 72 | SYSLOGTIMESTAMP %{MONTH} +%{MONTHDAY} %{TIME} 73 | PROG (?:[\w._/%-]+) 74 | SYSLOGPROG %{PROG:program}(?:\[%{POSINT:pid}\])? 75 | SYSLOGHOST %{IPORHOST} 76 | SYSLOGFACILITY <%{NONNEGINT:facility}.%{NONNEGINT:priority}> 77 | HTTPDATE %{MONTHDAY}/%{MONTH}/%{YEAR}:%{TIME} %{INT} 78 | # Shortcuts 79 | QS %{QUOTEDSTRING} 80 | # Log formats 81 | SYSLOGBASE %{SYSLOGTIMESTAMP:timestamp} (?:%{SYSLOGFACILITY} )?%{SYSLOGHOST:logsource} %{SYSLOGPROG}: 82 | COMMONAPACHELOG %{IPORHOST:clientip} %{USER:ident} %{USER:auth} \[%{HTTPDATE:timestamp}\] "(?:%{WORD:verb} %{NOTSPACE:request}(?: HTTP/%{NUMBER:httpversion})?|%{DATA:rawrequest})" %{NUMBER:response} (?:%{NUMBER:bytes}|-) 83 | COMBINEDAPACHELOG %{COMMONAPACHELOG} %{QS:referrer} %{QS:agent} 84 | # Log Levels 85 | LOGLEVEL ([Aa]lert|ALERT|[Tt]race|TRACE|[Dd]ebug|DEBUG|[Nn]otice|NOTICE|[Ii]nfo|INFO|[Ww]arn?(?:ing)?|WARN?(?:ING)?|[Ee]rr?(?:or)?|ERR?(?:OR)?|[Cc]rit?(?:ical)?|CRIT?(?:ICAL)?|[Ff]atal|FATAL|[Ss]evere|SEVERE|EMERG(?:ENCY)?|[Ee]merg(?:ency)?) -------------------------------------------------------------------------------- /docker/elk/logstash/patterns/nginx.conf: -------------------------------------------------------------------------------- 1 | NGINXACCESS %{IPORHOST:clientip} %{USER:ident} %{USER:auth} \[%{HTTPDATE:timestamp}\] "(?:%{WORD:verb} %{URIPATHPARAM:request}(?: HTTP/%{NUMBER:httpversion})?|-)" %{NUMBER:response} (?:%{NUMBER:bytes}|-) "(?:%{URI:referrer}|-)" %{QS:agent} %{NUMBER:request_time} %{NUMBER:upstream_response_time} %{NUMBER:gzip_ratio} (?:%{WORD:cache_hit}|-)%{GREEDYDATA} 2 | NGINXERROR (?%{YEAR}[./-]%{MONTHNUM}[./-]%{MONTHDAY}[- ]%{TIME}) \[%{LOGLEVEL:severity}\] %{POSINT:pid}#%{NUMBER}: %{GREEDYDATA:errormessage}(?:, client: (?%{IP}|%{HOSTNAME}))(?:, server: %{IPORHOST:server})(?:, request: %{QS:request})?(?:, upstream: \"%{URI:upstream}\")?(?:, host: %{QS:host})?(?:, referrer: \"%{URI:referrer}\") -------------------------------------------------------------------------------- /docker/env-sample.php: -------------------------------------------------------------------------------- 1 | 4 | array ( 5 | 'frontName' => 'admin_k7ip60', 6 | ), 7 | 'crypt' => 8 | array ( 9 | 'key' => '242c2f3519904b4d0283834fb8df0c26', 10 | ), 11 | 'session' => 12 | array ( 13 | 'save' => 'redis', 14 | 'redis' => 15 | array ( 16 | 'host' => 'redis.docker', 17 | 'port' => '6379', 18 | 'password' => '', 19 | 'timeout' => '2.5', 20 | 'persistent_identifier' => '', 21 | 'database' => '0', 22 | 'compression_threshold' => '2048', 23 | 'compression_library' => 'gzip', 24 | 'log_level' => '1', 25 | 'max_concurrency' => '6', 26 | 'break_after_frontend' => '5', 27 | 'break_after_adminhtml' => '30', 28 | 'first_lifetime' => '600', 29 | 'bot_first_lifetime' => '60', 30 | 'bot_lifetime' => '7200', 31 | 'disable_locking' => '0', 32 | 'min_lifetime' => '60', 33 | 'max_lifetime' => '2592000', 34 | ), 35 | ), 36 | 'db' => 37 | array ( 38 | 'table_prefix' => '', 39 | 'connection' => 40 | array ( 41 | 'default' => 42 | array ( 43 | 'host' => 'mariadb.docker', 44 | 'dbname' => 'magento2', 45 | 'username' => 'magento2', 46 | 'password' => 'magento2', 47 | 'active' => '1', 48 | 'model' => 'mysql4', 49 | 'engine' => 'innodb', 50 | 'initStatements' => 'SET NAMES utf8;', 51 | ), 52 | ), 53 | ), 54 | 'resource' => 55 | array ( 56 | 'default_setup' => 57 | array ( 58 | 'connection' => 'default', 59 | ), 60 | ), 61 | 'x-frame-options' => 'SAMEORIGIN', 62 | 'MAGE_MODE' => 'developer', 63 | 'cache_types' => 64 | array ( 65 | 'config' => 1, 66 | 'layout' => 1, 67 | 'block_html' => 1, 68 | 'collections' => 1, 69 | 'reflection' => 1, 70 | 'db_ddl' => 1, 71 | 'eav' => 1, 72 | 'customer_notification' => 1, 73 | 'full_page' => 1, 74 | 'config_integration' => 1, 75 | 'config_integration_api' => 1, 76 | 'translate' => 1, 77 | 'config_webservice' => 1, 78 | 'compiled_config' => 1, 79 | ), 80 | 'cache' => 81 | array ( 82 | 'frontend' => 83 | array ( 84 | 'default' => 85 | array ( 86 | 'backend' => 'Cm_Cache_Backend_Redis', 87 | 'backend_options' => 88 | array ( 89 | 'server' => 'redis.docker', 90 | 'port' => '6379', 91 | 'persistent' => '', 92 | 'database' => '1', 93 | 'force_standalone' => '0', 94 | 'connect_retries' => '1', 95 | 'read_timeout' => '10', 96 | 'automatic_cleaning_factor' => '0', 97 | 'compress_data' => '1', 98 | 'compress_tags' => '1', 99 | 'compress_threshold' => '20480', 100 | 'compression_lib' => 'gzip', 101 | ), 102 | ), 103 | 'page_cache' => 104 | array ( 105 | 'backend' => 'Cm_Cache_Backend_Redis', 106 | 'backend_options' => 107 | array ( 108 | 'server' => 'redis.docker', 109 | 'port' => '6379', 110 | 'persistent' => '', 111 | 'database' => '2', 112 | 'force_standalone' => '0', 113 | 'connect_retries' => '1', 114 | 'read_timeout' => '10', 115 | 'automatic_cleaning_factor' => '0', 116 | 'compress_data' => '0', 117 | 'compress_tags' => '1', 118 | 'compress_threshold' => '20480', 119 | 'compression_lib' => 'gzip', 120 | ), 121 | ), 122 | ), 123 | ), 124 | 'install' => 125 | array ( 126 | 'date' => 'Thu, 29 Sep 2016 10:58:33 +0000', 127 | ), 128 | 'http_cache_hosts' => 129 | array ( 130 | 0 => 131 | array ( 132 | 'host' => 'm2.docker', 133 | ), 134 | ), 135 | ); -------------------------------------------------------------------------------- /docker/nginx/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM nginx:stable-alpine 2 | MAINTAINER Josh Porter 3 | 4 | RUN set -x \ 5 | # && deluser www-data \ 6 | && addgroup -g 82 -S www-data \ 7 | && adduser -u 82 -D -S -G www-data www-data 8 | 9 | COPY nginx.conf /etc/nginx 10 | COPY *.conf /etc/nginx/conf.d/ -------------------------------------------------------------------------------- /docker/nginx/default.conf: -------------------------------------------------------------------------------- 1 | upstream fastcgi_backend { 2 | server php:9000; 3 | } 4 | 5 | map $remote_addr $original_remote_address { 6 | default $remote_addr; 7 | 127.0.0.1 $http_x_real_ip; 8 | } 9 | 10 | server { 11 | listen 80 default_server; 12 | listen [::]:80 default_server; 13 | 14 | server_name m2.docker localhost; 15 | 16 | set $MAGE_ROOT /var/www/html/; 17 | set $MAGE_MODE developer; 18 | 19 | include 'conf.d/magento2.conf'; 20 | } -------------------------------------------------------------------------------- /docker/nginx/magento2.conf: -------------------------------------------------------------------------------- 1 | root $MAGE_ROOT/pub; 2 | 3 | index index.php; 4 | autoindex off; 5 | charset off; 6 | error_page 404 403 = /errors/404.php; 7 | 8 | # PHP entry point for setup application 9 | location ~* ^/setup($|/) { 10 | root $MAGE_ROOT; 11 | location ~ ^/setup/index.php { 12 | fastcgi_pass fastcgi_backend; 13 | fastcgi_index index.php; 14 | fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; 15 | include fastcgi_params; 16 | } 17 | 18 | location ~ ^/setup/(?!pub/). { 19 | deny all; 20 | } 21 | 22 | location ~ ^/setup/pub/ { 23 | add_header X-Frame-Options "SAMEORIGIN"; 24 | } 25 | } 26 | 27 | # PHP entry point for update application 28 | location ~* ^/update($|/) { 29 | root $MAGE_ROOT; 30 | 31 | location ~ ^/update/index.php { 32 | fastcgi_split_path_info ^(/update/index.php)(/.+)$; 33 | fastcgi_pass fastcgi_backend; 34 | fastcgi_index index.php; 35 | fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; 36 | fastcgi_param PATH_INFO $fastcgi_path_info; 37 | include fastcgi_params; 38 | } 39 | 40 | # Deny everything but index.php 41 | location ~ ^/update/(?!pub/). { 42 | deny all; 43 | } 44 | 45 | location ~ ^/update/pub/ { 46 | add_header X-Frame-Options "SAMEORIGIN"; 47 | } 48 | } 49 | 50 | location / { 51 | try_files $uri $uri/ /index.php?$args; 52 | } 53 | 54 | location /pub/ { 55 | location ~ ^/pub/media/(downloadable|customer|import|theme_customization/.*\.xml) { 56 | deny all; 57 | } 58 | alias $MAGE_ROOT/pub/; 59 | add_header X-Frame-Options "SAMEORIGIN"; 60 | } 61 | 62 | location /static/ { 63 | # Uncomment the following line in production mode 64 | # expires max; 65 | 66 | # Remove signature of the static files that is used to overcome the browser cache 67 | location ~ ^/static/version { 68 | rewrite ^/static/(version\d*/)?(.*)$ /static/$2 last; 69 | } 70 | 71 | location ~* \.(ico|jpg|jpeg|png|gif|svg|js|css|swf|eot|ttf|otf|woff|woff2)$ { 72 | add_header Cache-Control "public"; 73 | add_header X-Frame-Options "SAMEORIGIN"; 74 | expires +1y; 75 | 76 | if (!-f $request_filename) { 77 | rewrite ^/static/(version\d*/)?(.*)$ /static.php?resource=$2 last; 78 | } 79 | } 80 | location ~* \.(zip|gz|gzip|bz2|csv|xml)$ { 81 | add_header Cache-Control "no-store"; 82 | add_header X-Frame-Options "SAMEORIGIN"; 83 | expires off; 84 | 85 | if (!-f $request_filename) { 86 | rewrite ^/static/(version\d*/)?(.*)$ /static.php?resource=$2 last; 87 | } 88 | } 89 | if (!-f $request_filename) { 90 | rewrite ^/static/(version\d*/)?(.*)$ /static.php?resource=$2 last; 91 | } 92 | add_header X-Frame-Options "SAMEORIGIN"; 93 | } 94 | 95 | location /media/ { 96 | try_files $uri $uri/ /get.php?$args; 97 | 98 | location ~ ^/media/theme_customization/.*\.xml { 99 | deny all; 100 | } 101 | 102 | location ~* \.(ico|jpg|jpeg|png|gif|svg|js|css|swf|eot|ttf|otf|woff|woff2)$ { 103 | add_header Cache-Control "public"; 104 | add_header X-Frame-Options "SAMEORIGIN"; 105 | expires +1y; 106 | try_files $uri $uri/ /get.php?$args; 107 | } 108 | location ~* \.(zip|gz|gzip|bz2|csv|xml)$ { 109 | add_header Cache-Control "no-store"; 110 | add_header X-Frame-Options "SAMEORIGIN"; 111 | expires off; 112 | try_files $uri $uri/ /get.php?$args; 113 | } 114 | add_header X-Frame-Options "SAMEORIGIN"; 115 | } 116 | 117 | location /media/customer/ { 118 | deny all; 119 | } 120 | 121 | location /media/downloadable/ { 122 | deny all; 123 | } 124 | 125 | location /media/import/ { 126 | deny all; 127 | } 128 | 129 | # PHP entry point for main application 130 | location ~ (index|get|static|report|404|503)\.php$ { 131 | try_files $uri =404; 132 | fastcgi_pass fastcgi_backend; 133 | 134 | fastcgi_param PHP_FLAG "session.auto_start=off \n suhosin.session.cryptua=off"; 135 | fastcgi_connect_timeout 80; 136 | fastcgi_send_timeout 7200; 137 | fastcgi_read_timeout 7200; 138 | fastcgi_buffer_size 128k; 139 | fastcgi_buffers 128 128k; 140 | fastcgi_busy_buffers_size 1024k; 141 | fastcgi_temp_file_write_size 1024k; 142 | 143 | fastcgi_index index.php; 144 | fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; 145 | fastcgi_param MAGE_MODE $MAGE_MODE; 146 | include fastcgi_params; 147 | } 148 | 149 | # Banned locations (only reached if the earlier PHP entry point regexes don't match) 150 | location ~* (\.php$|\.htaccess$|\.git) { 151 | deny all; 152 | } -------------------------------------------------------------------------------- /docker/nginx/nginx.conf: -------------------------------------------------------------------------------- 1 | user www-data; 2 | worker_processes 1; 3 | 4 | error_log /var/log/nginx/error.log debug; 5 | pid /var/run/nginx.pid; 6 | 7 | events { 8 | worker_connections 1024; 9 | multi_accept on; 10 | } 11 | 12 | http { 13 | include /etc/nginx/mime.types; 14 | default_type application/octet-stream; 15 | 16 | log_format main '$remote_addr - $remote_user [$time_local] "$request" ' 17 | '$status $body_bytes_sent "$http_referer" ' 18 | '"$http_user_agent" "$http_x_forwarded_for"'; 19 | 20 | access_log /var/log/nginx/access.log main; 21 | 22 | server_tokens off; 23 | sendfile on; 24 | tcp_nopush on; 25 | tcp_nodelay on; 26 | 27 | add_header X-Frame-Options SAMEORIGIN; 28 | add_header X-Content-Type-Options nosniff; 29 | add_header X-XSS-Protection "1; mode=block"; 30 | 31 | keepalive_timeout 65; 32 | 33 | gzip on; 34 | gzip_comp_level 6; 35 | gzip_min_length 1100; 36 | gzip_buffers 16 8k; 37 | gzip_proxied any; 38 | gzip_types text/plain text/css text/js text/xml text/javascript application/javascript application/x-javascript application/json application/xml application/xml+rss image/svg+xml; 39 | gzip_vary off; 40 | gzip_disable "MSIE [1-6]\."; 41 | 42 | include /etc/nginx/conf.d/default.conf; 43 | } -------------------------------------------------------------------------------- /docker/node/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:6.7-slim 2 | MAINTAINER Josh Porter 3 | 4 | RUN apt-get update && apt-get install -y bzip2 && rm -rf /var/lib/apt/lists/* 5 | RUN npm install -g grunt-cli 6 | 7 | WORKDIR /var/www/html -------------------------------------------------------------------------------- /docker/php/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM php:7.0-fpm-alpine 2 | MAINTAINER Josh Porter 3 | 4 | RUN echo "http://dl-cdn.alpinelinux.org/alpine/edge/testing" >> /etc/apk/repositories && \ 5 | apk update && \ 6 | apk upgrade && \ 7 | apk add --update \ 8 | autoconf gcc g++ make ssmtp wget libjpeg-turbo-dev libpng-dev libxpm-dev libwebp-dev freetype-dev icu-dev libxslt-dev libmcrypt-dev && \ 9 | rm -rf /var/cache/apk/* 10 | 11 | RUN docker-php-ext-configure \ 12 | gd --with-freetype-dir=/usr/include/ --with-jpeg-dir=/usr/include/ --with-webp-dir=/usr/include/ --with-xpm-dir=/usr/include/ 13 | 14 | RUN docker-php-ext-install \ 15 | gd \ 16 | intl \ 17 | mcrypt \ 18 | pdo_mysql \ 19 | xsl \ 20 | zip 21 | 22 | RUN pecl install xdebug && docker-php-ext-enable xdebug 23 | 24 | RUN echo "sendmail_path = /usr/sbin/ssmtp -t" > /usr/local/etc/php/conf.d/sendmail.ini && \ 25 | echo "mailhub=mail:25\nUseTLS=NO\nFromLineOverride=YES" > /etc/ssmtp/ssmtp.conf 26 | 27 | COPY conf/php.ini /usr/local/etc/php 28 | COPY conf/crontab /tmp 29 | COPY conf/docker-php-ext-xdebug.ini /usr/local/etc/php/conf.d 30 | COPY conf/php-fpm.conf /usr/local/etc/php-fpm.d 31 | COPY bin/* /usr/local/bin/ 32 | 33 | CMD ["/usr/local/bin/start"] -------------------------------------------------------------------------------- /docker/php/bin/start: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | crontab -u www-data /tmp/crontab 3 | crond 4 | php-fpm -------------------------------------------------------------------------------- /docker/php/conf/crontab: -------------------------------------------------------------------------------- 1 | * * * * * /usr/local/bin/php /var/www/html/bin/magento cron:run | grep -v "Ran jobs by schedule" >> /var/www/html/var/log/magento.cron.log 2 | * * * * * /usr/local/bin/php /var/www/html/update/cron.php >> /var/www/html/var/log/update.cron.log 3 | * * * * * /usr/local/bin/php /var/www/html/bin/magento setup:cron:run >> /var/www/html/var/log/setup.cron.log -------------------------------------------------------------------------------- /docker/php/conf/docker-php-ext-xdebug.ini: -------------------------------------------------------------------------------- 1 | zend_extension=xdebug.so 2 | xdebug.default_enable = 1 3 | xdebug.remote_autostart = 1 4 | xdebug.remote_connect_back = 1 5 | xdebug.remote_enable = 1 6 | xdebug.remote_handler = "dbgp" 7 | xdebug.remote_port = 9000 8 | xdebug.max_nesting_level = 300 -------------------------------------------------------------------------------- /docker/php/conf/php-fpm.conf: -------------------------------------------------------------------------------- 1 | [global] 2 | daemonize = no 3 | 4 | [www] 5 | pm = dynamic 6 | pm.max_children = 4 7 | pm.start_servers = 3 8 | pm.min_spare_servers = 2 9 | pm.max_spare_servers = 4 10 | pm.max_requests = 200 11 | 12 | ; Ensure worker stdout and stderr are sent to the main error log. 13 | catch_workers_output = yes 14 | 15 | php_flag[display_errors] = on 16 | php_admin_flag[log_errors] = on 17 | php_admin_value[error_log] = /var/log/php-fpm/error.log 18 | -------------------------------------------------------------------------------- /docker/php/conf/php.ini: -------------------------------------------------------------------------------- 1 | memory_limit = 2048M 2 | date.timezone = "Europe/London" 3 | max_execution_time = 600 4 | always_populate_raw_post_data = -1 -------------------------------------------------------------------------------- /docker/ssl/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM nginx:stable-alpine 2 | MAINTAINER Josh Porter 3 | 4 | RUN set -x \ 5 | # && deluser www-data \ 6 | && addgroup -g 82 -S www-data \ 7 | && adduser -u 82 -D -S -G www-data www-data 8 | 9 | COPY nginx.conf /etc/nginx 10 | COPY *.conf /etc/nginx/conf.d/ 11 | COPY m2docker.cert /etc/ssl 12 | COPY m2docker.key /etc/ssl -------------------------------------------------------------------------------- /docker/ssl/default.conf: -------------------------------------------------------------------------------- 1 | map $http_x_forwarded_proto $fastcgi_https { 2 | default $https; 3 | http ''; 4 | https on; 5 | } 6 | 7 | map $remote_addr $original_remote_address { 8 | default $remote_addr; 9 | 127.0.0.1 $http_x_real_ip; 10 | } 11 | 12 | server { 13 | listen 80; 14 | listen [::]:80; 15 | return 301 https://$host$request_uri; 16 | } 17 | 18 | server { 19 | listen 443 default_server; 20 | listen [::]:443 default_server; 21 | 22 | server_name m2.docker localhost; 23 | 24 | include "conf.d/ssl.conf"; 25 | 26 | location / { 27 | real_ip_header X-Real-IP; 28 | real_ip_header X-Forwarded-For; 29 | 30 | proxy_pass http://varnish:80; 31 | proxy_redirect off; 32 | proxy_buffering off; 33 | 34 | proxy_connect_timeout 75s; 35 | send_timeout 300; 36 | 37 | proxy_set_header Host $host; 38 | proxy_set_header X-Real-IP $remote_addr; 39 | proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 40 | proxy_set_header X-Forwarded-Proto https; 41 | proxy_set_header X-Forwarded-Port 443; 42 | } 43 | } 44 | 45 | -------------------------------------------------------------------------------- /docker/ssl/m2docker.cert: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIC+zCCAeOgAwIBAgIJAIo+CCW7KZjTMA0GCSqGSIb3DQEBBQUAMBQxEjAQBgNV 3 | BAMMCW0yLmRvY2tlcjAeFw0xNjA5MzAxNTI1MTRaFw0yNjA5MjgxNTI1MTRaMBQx 4 | EjAQBgNVBAMMCW0yLmRvY2tlcjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC 5 | ggEBAMEM51c3kTjOx9XMRxBZ/d+FcxSjHe1+RPHTaZLTcAv10ZRG/beP8rAfsUlU 6 | z0kmvYC+uQSn1G44OPNzf5ZdpPwhOmYP63NHKzATS4Y6F6o6mE4AkjEvO4d382KV 7 | QlhrAAVsQLfTQsInMWLG2eB3PV6IboHCUt+6/y0yzuHSK3e1W/KPzm6R5WP3DvKM 8 | 4smYdv78QbVhaRtyFzCqR/gRCSHwcKHfcmjQ+78dXD9ueltNMOdLG7XJEPGiI7qW 9 | ++PQNH8ktbjRokHuJ/IxRi60XlIbsciOoSN7xcGA5MqK2AMaEaMVOvrdixq+fjvU 10 | z4y1HSakqbexEqMCKBTCAgFDfokCAwEAAaNQME4wHQYDVR0OBBYEFHOePotU6nRg 11 | P+t5kQB0cG9YUqNqMB8GA1UdIwQYMBaAFHOePotU6nRgP+t5kQB0cG9YUqNqMAwG 12 | A1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEBAGdQs+LKjaYkUe2SK1yp1GfK 13 | MmQ5CSH55U6cPP3txpwcoQaOWpWRi1A0hbBgl4ySJlUc8cgDM5iQ6bIUeswadXer 14 | EEUORbFf8YnyW1h53PTTD/DLdEC6wzCFST4zFFulCmWvAJ1vunnZgFZkIhpv32do 15 | yyAHahc3WsiV5ocUamFwptvTtVbl905MseBxcJVmtImyapGnOlmwxSsYu31tnm8c 16 | owDKQ8QUEbZSSLa+XteSU33MTae/YAOW3eYnfP2zinlCLkpC6IxdlyUE7WMyxL16 17 | HwYzMeCEVF0k9A7nAUr5bZS9fl7NWE7BLDwT36dt1vskqsLKM78gHDJRMnb8K4g= 18 | -----END CERTIFICATE----- 19 | -------------------------------------------------------------------------------- /docker/ssl/m2docker.key: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | MIIEogIBAAKCAQEAwQznVzeROM7H1cxHEFn934VzFKMd7X5E8dNpktNwC/XRlEb9 3 | t4/ysB+xSVTPSSa9gL65BKfUbjg483N/ll2k/CE6Zg/rc0crMBNLhjoXqjqYTgCS 4 | MS87h3fzYpVCWGsABWxAt9NCwicxYsbZ4Hc9XohugcJS37r/LTLO4dIrd7Vb8o/O 5 | bpHlY/cO8oziyZh2/vxBtWFpG3IXMKpH+BEJIfBwod9yaND7vx1cP256W00w50sb 6 | tckQ8aIjupb749A0fyS1uNGiQe4n8jFGLrReUhuxyI6hI3vFwYDkyorYAxoRoxU6 7 | +t2LGr5+O9TPjLUdJqSpt7ESowIoFMICAUN+iQIDAQABAoIBAHIghsNw/lFvLYSf 8 | d4ObMBLsedRBenfkpi2wpkE+7oajlHrsXSIf34Fvuwd3M/QzCuUJ26d88IEYi9Kg 9 | /VSPl/HWUwfzcWZ9NHL/dS6tOUBR06iJfePF4JN9vk0RoF9VInYSQVBrfJP7R571 10 | n+irlYRFc88SapwJmMHHv7aQ4VGqxbCQXK2BaWR2iczb2NsmqE2eZLIDZeHym5C5 11 | sFOUd+CCrQdrFtpcRJuzKeDtaKtY7VbHMmweOBo7BPFOirrsemhx43b5ZrqG2Ow/ 12 | iw5X71jHH24SuMYBCrGkmvo5SyCTzfgYRDPtGJaZwvh4W5KKzw+TIuGaDUMk7R+T 13 | REBPw2ECgYEA9i8m+PKuQMOzo1E0gBk3AGw4x0K40+n3DqfceouAtBHshnOyJ/e7 14 | mQ6IB8CdK2u2AAwLiQCSn2OY6mGJSG+paoXkkgVgEyb29L5O/ZwuLHhtL+i6kKat 15 | UjiAByZFFlxsdGjZWpvaAfDlZoD1tuzargwNnPwq4Xbw8eL5nP4h78sCgYEAyL9n 16 | sHF9DKsMFnNfblMjpbIrwWIPV8mpJL1P1KGM7SkASKCDCnIDry2ZJuc4bWDORCb3 17 | aVl/Jpi+9iJtI5zOUbzovINPHA50MM2z7hchMeYCE8kwrrEtszanBsY7rX/HyhRl 18 | 3yCu+6+sanDYg84QPBQS4k7CTRnDZnqD6nWm2HsCgYAY+P+DEVsLxs5aKsqgk+44 19 | ZMH9tI77MCiXm/+Jjs0ndwn/OY1A8KhfBlJWNvu6g9qadp5U73HwKTJB1FMQvgL5 20 | g0uEZHcyeili+ksY/tbQPAcXbQw/0CiUxpZ2IzOu6QE3bctenaCnwcADfeFvBOfJ 21 | 7/8RhaLz7BTU9KlNe5oFsQKBgFSIbRRapcrI2D039g6SCefDQDOdpFJWQ9iJs20Z 22 | Xiub4Vez55planHA5MYCBbvhUNpYAkUlf6p0Fa21z3l4lVTMFMEVFiH9YCYQ6Nyw 23 | drT5YxSL2uklCNZ3hBwRK54iubG5pzAvJiAr2JG11SajhiiWV2FewukOmT61Bomb 24 | Y1ezAoGAOldXtTGY37E+Ct7ZzrAbRiFXPRzY8b4nrjVy0NtwrupFZIUTYOKhfzJW 25 | eQs4FcpwHb+6WNAp4uuRCjHbRpzgBJQjaNlnl9d6Di5zSf/O6RZ5kICBH5RbYUAN 26 | rmObQN7/e7GUj3djLRaWoRMRRaW+TzX4oTM3eUvHb+MCogtMsVo= 27 | -----END RSA PRIVATE KEY----- 28 | -------------------------------------------------------------------------------- /docker/ssl/nginx.conf: -------------------------------------------------------------------------------- 1 | user www-data; 2 | worker_processes 1; 3 | 4 | error_log /var/log/nginx/ssl-error.log debug; 5 | pid /var/run/nginx.pid; 6 | 7 | events { 8 | worker_connections 1024; 9 | multi_accept on; 10 | } 11 | 12 | http { 13 | include /etc/nginx/mime.types; 14 | default_type application/octet-stream; 15 | 16 | log_format main '$remote_addr - $remote_user [$time_local] "$request" ' 17 | '$status $body_bytes_sent "$http_referer" ' 18 | '"$http_user_agent" "$http_x_forwarded_for"'; 19 | 20 | access_log /var/log/nginx/ssl-access.log main; 21 | 22 | server_tokens off; 23 | sendfile on; 24 | tcp_nopush on; 25 | tcp_nodelay on; 26 | 27 | keepalive_timeout 65; 28 | 29 | include /etc/nginx/conf.d/default.conf; 30 | } -------------------------------------------------------------------------------- /docker/ssl/ssl.conf: -------------------------------------------------------------------------------- 1 | ssl on; 2 | ssl_certificate /etc/ssl/m2docker.cert; 3 | ssl_certificate_key /etc/ssl/m2docker.key; 4 | ssl_session_timeout 1d; 5 | ssl_session_cache shared:SSL:50m; 6 | ssl_protocols TLSv1 TLSv1.1 TLSv1.2; 7 | ssl_ciphers ECDH+AESGCM:DH+AESGCM:ECDH+AES256:DH+AES256:ECDH+AES128:DH+AES:ECDH+3DES:DH+3DES:RSA+AESGCM:RSA+AES:RSA+3DES:!aNULL:!MD5:!DSS; 8 | ssl_prefer_server_ciphers on; 9 | keepalive_timeout 300; -------------------------------------------------------------------------------- /docker/varnish/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM million12/varnish 2 | COPY default.vcl /etc/varnish/default.vcl 3 | ENV VARNISHD_PARAMS -p default_ttl=3600 -p default_grace=3600 -T 127.0.0.1:6082 -S /etc/varnish/secret -------------------------------------------------------------------------------- /docker/varnish/default.vcl: -------------------------------------------------------------------------------- 1 | vcl 4.0; 2 | 3 | import std; 4 | 5 | backend default { 6 | .host = "web"; 7 | .port = "80"; 8 | .connect_timeout = 240s; 9 | .first_byte_timeout = 240s; 10 | .between_bytes_timeout = 240s; 11 | } 12 | 13 | acl purge { 14 | "localhost"; 15 | "127.0.0.1"; 16 | } 17 | 18 | sub vcl_recv { 19 | if (req.method == "PURGE") { 20 | if (client.ip !~ purge) { 21 | return (synth(405, "Method not allowed")); 22 | } 23 | if (!req.http.X-Magento-Tags-Pattern) { 24 | return (synth(400, "X-Magento-Tags-Pattern header required")); 25 | } 26 | ban("obj.http.X-Magento-Tags ~ " + req.http.X-Magento-Tags-Pattern); 27 | return (synth(200, "Purged")); 28 | } 29 | 30 | if (req.method != "GET" && 31 | req.method != "HEAD" && 32 | req.method != "PUT" && 33 | req.method != "POST" && 34 | req.method != "TRACE" && 35 | req.method != "OPTIONS" && 36 | req.method != "DELETE") { 37 | /* Non-RFC2616 or CONNECT which is weird. */ 38 | return (pipe); 39 | } 40 | 41 | # We only deal with GET and HEAD by default 42 | if (req.method != "GET" && req.method != "HEAD") { 43 | return (pass); 44 | } 45 | 46 | # Bypass shopping cart, checkout and search requests 47 | if (req.url ~ "/checkout" || req.url ~ "/catalogsearch") { 48 | return (pass); 49 | } 50 | 51 | # normalize url in case of leading HTTP scheme and domain 52 | set req.url = regsub(req.url, "^http[s]?://", ""); 53 | 54 | # collect all cookies 55 | std.collect(req.http.Cookie); 56 | 57 | # Compression filter. See https://www.varnish-cache.org/trac/wiki/FAQ/Compression 58 | if (req.http.Accept-Encoding) { 59 | if (req.url ~ "\.(jpg|jpeg|png|gif|gz|tgz|bz2|tbz|mp3|ogg|swf|flv)$") { 60 | # No point in compressing these 61 | unset req.http.Accept-Encoding; 62 | } elsif (req.http.Accept-Encoding ~ "gzip") { 63 | set req.http.Accept-Encoding = "gzip"; 64 | } elsif (req.http.Accept-Encoding ~ "deflate" && req.http.user-agent !~ "MSIE") { 65 | set req.http.Accept-Encoding = "deflate"; 66 | } else { 67 | # unkown algorithm 68 | unset req.http.Accept-Encoding; 69 | } 70 | } 71 | 72 | # Remove Google gclid parameters to minimize the cache objects 73 | set req.url = regsuball(req.url,"\?gclid=[^&]+$",""); # strips when QS = "?gclid=AAA" 74 | set req.url = regsuball(req.url,"\?gclid=[^&]+&","?"); # strips when QS = "?gclid=AAA&foo=bar" 75 | set req.url = regsuball(req.url,"&gclid=[^&]+",""); # strips when QS = "?foo=bar&gclid=AAA" or QS = "?foo=bar&gclid=AAA&bar=baz" 76 | 77 | # static files are always cacheable. remove SSL flag and cookie 78 | if (req.url ~ "^/(pub/)?(media|static)/.*\.(ico|css|js|jpg|jpeg|png|gif|tiff|bmp|mp3|ogg|svg|swf|woff|woff2|eot|ttf|otf)$") { 79 | unset req.http.Https; 80 | unset req.http.Cookie; 81 | } 82 | 83 | return (hash); 84 | } 85 | 86 | sub vcl_hash { 87 | if (req.http.cookie ~ "X-Magento-Vary=") { 88 | hash_data(regsub(req.http.cookie, "^.*?X-Magento-Vary=([^;]+);*.*$", "\1")); 89 | } 90 | 91 | # For multi site configurations to not cache each other's content 92 | if (req.http.host) { 93 | hash_data(req.http.host); 94 | } else { 95 | hash_data(server.ip); 96 | } 97 | 98 | # To make sure http users don't see ssl warning 99 | if (req.http.X-Forwarded-Proto) { 100 | hash_data(req.http.X-Forwarded-Proto); 101 | } 102 | } 103 | 104 | sub vcl_backend_response { 105 | if (beresp.http.content-type ~ "text") { 106 | set beresp.do_esi = true; 107 | } 108 | 109 | if (bereq.url ~ "\.js$" || beresp.http.content-type ~ "text") { 110 | set beresp.do_gzip = true; 111 | } 112 | 113 | # cache only successfully responses and 404s 114 | if (beresp.status != 200 && beresp.status != 404) { 115 | set beresp.ttl = 0s; 116 | set beresp.uncacheable = true; 117 | return (deliver); 118 | } elsif (beresp.http.Cache-Control ~ "private") { 119 | set beresp.uncacheable = true; 120 | set beresp.ttl = 86400s; 121 | return (deliver); 122 | } 123 | 124 | if (beresp.http.X-Magento-Debug) { 125 | set beresp.http.X-Magento-Cache-Control = beresp.http.Cache-Control; 126 | } 127 | 128 | # validate if we need to cache it and prevent from setting cookie 129 | # images, css and js are cacheable by default so we have to remove cookie also 130 | if (beresp.ttl > 0s && (bereq.method == "GET" || bereq.method == "HEAD")) { 131 | unset beresp.http.set-cookie; 132 | if (bereq.url !~ "\.(ico|css|js|jpg|jpeg|png|gif|tiff|bmp|gz|tgz|bz2|tbz|mp3|ogg|svg|swf|woff|woff2|eot|ttf|otf)(\?|$)") { 133 | set beresp.http.Pragma = "no-cache"; 134 | set beresp.http.Expires = "-1"; 135 | set beresp.http.Cache-Control = "no-store, no-cache, must-revalidate, max-age=0"; 136 | set beresp.grace = 1m; 137 | } 138 | } 139 | 140 | # If page is not cacheable then bypass varnish for 2 minutes as Hit-For-Pass 141 | if (beresp.ttl <= 0s || 142 | beresp.http.Surrogate-control ~ "no-store" || 143 | (!beresp.http.Surrogate-Control && beresp.http.Vary == "*")) { 144 | # Mark as Hit-For-Pass for the next 2 minutes 145 | set beresp.ttl = 120s; 146 | set beresp.uncacheable = true; 147 | } 148 | return (deliver); 149 | } 150 | 151 | sub vcl_deliver { 152 | if (resp.http.X-Magento-Debug) { 153 | if (resp.http.x-varnish ~ " ") { 154 | set resp.http.X-Magento-Cache-Debug = "HIT"; 155 | } else { 156 | set resp.http.X-Magento-Cache-Debug = "MISS"; 157 | } 158 | } else { 159 | unset resp.http.Age; 160 | } 161 | 162 | unset resp.http.X-Magento-Debug; 163 | unset resp.http.X-Magento-Tags; 164 | unset resp.http.X-Powered-By; 165 | unset resp.http.Server; 166 | unset resp.http.X-Varnish; 167 | unset resp.http.Via; 168 | unset resp.http.Link; 169 | } 170 | -------------------------------------------------------------------------------- /features/bootstrap/Context/HealthCheckContext.php: -------------------------------------------------------------------------------- 1 | _homepage = $homepage; 17 | } 18 | /** 19 | * @Given I have loaded the homepage 20 | */ 21 | public function iHaveLoadedTheHomepage() 22 | { 23 | $this->_homepage->open(); 24 | } 25 | 26 | /** 27 | * @When It is loaded 28 | */ 29 | public function itIsLoaded() 30 | { 31 | $this->_homepage->waitForHomePageLoad(); 32 | } 33 | 34 | /** 35 | * @Then I should see site logo 36 | */ 37 | public function iShouldSeeSiteLogo() 38 | { 39 | $this->_homepage->logoExists(); 40 | } 41 | 42 | } -------------------------------------------------------------------------------- /features/bootstrap/Page/Homepage.php: -------------------------------------------------------------------------------- 1 | ['css' => '.logo'] 13 | ]; 14 | 15 | public function waitForHomePageLoad($maxWait = 120000) 16 | { 17 | $this->getDriver()->wait($maxWait, '(document.readyState == "complete") && (typeof window.jQuery == "function")'); 18 | } 19 | 20 | public function logoExists() 21 | { 22 | $this->hasElement('Logo'); 23 | } 24 | } -------------------------------------------------------------------------------- /features/suites/m2docker/healthcheck.feature: -------------------------------------------------------------------------------- 1 | Feature: Check my site is up and running 2 | @javascript 3 | Scenario: Load the homepage 4 | Given I have loaded the homepage 5 | When It is loaded 6 | Then I should see site logo -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Magento2", 3 | "author": "Magento Commerce Inc.", 4 | "description": "Magento2 node modules dependencies for local development", 5 | "version": "2.0.0", 6 | "license": "(OSL-3.0 OR AFL-3.0)", 7 | "repository": { 8 | "type": "git", 9 | "url": "https://github.com/magento/magento2.git" 10 | }, 11 | "homepage": "http://magento.com/", 12 | "devDependencies": { 13 | "glob": "^5.0.14", 14 | "grunt": "^0.4.5", 15 | "grunt-autoprefixer": "^2.0.0", 16 | "grunt-banner": "^0.4.0", 17 | "grunt-contrib-clean": "^0.6.0", 18 | "grunt-contrib-connect": "^0.9.0", 19 | "grunt-contrib-cssmin": "^0.10.0", 20 | "grunt-contrib-imagemin": "^0.9.2", 21 | "grunt-contrib-jasmine": "^0.8.1", 22 | "grunt-contrib-less": "^0.12.0", 23 | "grunt-contrib-watch": "^0.6.1", 24 | "grunt-eslint": "17.3.1", 25 | "grunt-exec": "^0.4.6", 26 | "grunt-jscs": "2.2.0", 27 | "grunt-replace": "^0.9.2", 28 | "grunt-styledocco": "^0.1.4", 29 | "grunt-template-jasmine-requirejs": "^0.2.3", 30 | "grunt-text-replace": "^0.4.0", 31 | "imagemin-svgo": "^4.0.1", 32 | "load-grunt-config": "^0.16.0", 33 | "morgan": "^1.5.0", 34 | "node-minify": "^1.0.1", 35 | "path": "^0.11.14", 36 | "serve-static": "^1.7.1", 37 | "strip-json-comments": "^1.0.2", 38 | "time-grunt": "^1.0.0", 39 | "underscore": "^1.7.0" 40 | }, 41 | "engines": { 42 | "node": ">=0.10.0" 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /phpspec.yml: -------------------------------------------------------------------------------- 1 | bootstrap: 'app/autoload.php' 2 | suites: 3 | default: 4 | src_path: 'app/code' 5 | formatter.name: pretty 6 | extensions: 7 | - Nagno\Phpspec\BootstrapMagento2\Bootstrap --------------------------------------------------------------------------------