├── .gitignore ├── PHPExport ├── README.md ├── ReflectionClass.php ├── ReflectionConstant.php ├── ReflectionExtension.php ├── ReflectionMethod.php ├── ReflectionParameter.php └── ReflectionProperty.php ├── README.md └── Tutorials ├── README.md ├── prosody └── how-to-install-prosody-on-ubuntu-14.04-lts.md └── vagrant └── self-hosted-vagrant-boxes-with-versioning.md /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ -------------------------------------------------------------------------------- /PHPExport/README.md: -------------------------------------------------------------------------------- 1 | # hollodotme\Helpers 2 | 3 | ## PHPExport 4 | 5 | This is a small collection of classes that enable you to export every class or 6 | even entire extension into PHP code or a PHP Archive (PHAR). 7 | 8 | ### Why this? 9 | 10 | As I wrote a new class that used the PECL extension "pecl_http 2.0.x", I messed up, 11 | because my development IDE (PphStorm) did not recognized the extension's namespace, classes, methods and so on. 12 | And of course, no auto completion at all! 13 | 14 | Unfortunately there was/is no PHP source code of that extension, so I started exporting it by using the Reflection classes of PHP. 15 | 16 | In the end, I was able to export a PHAR file for the hole extension and could link that file into my IDE as an external library. 17 | 18 | ### How does this work? 19 | 20 | There are 3 ways to export an entire extension and 2 ways to export a single class. 21 | 22 | 1: Export a PHAR for the whole extension 23 | 24 | ```php 25 | exportPHAR('pecl_http.phar', '/var/www/lib', true); 29 | ``` 30 | 31 | 2: Export all files for the whole extension (including sub directories by namespace depth) 32 | 33 | ```php 34 | exportFiles('/var/www/lib/http'); 38 | ``` 39 | 40 | 3: Just print all classes/interfaces/traits from the extension 41 | 42 | ```php 43 | exportCode(); 48 | ``` 49 | 50 | 4: Export a single class/interface/trait to a file 51 | 52 | ```php 53 | exportFile('/var/www/lib/http/Url.php'); 57 | ``` 58 | 59 | 5: Just print a single class/interface/trait 60 | 61 | ```php 62 | exportCode(); 67 | ``` 68 | 69 | Maybe this will help you out, too. 70 | -------------------------------------------------------------------------------- /PHPExport/ReflectionClass.php: -------------------------------------------------------------------------------- 1 | _reflection_class = $reflection_class; 36 | } 37 | 38 | /** 39 | * Exports the PHP code 40 | * 41 | * @return string 42 | */ 43 | public function exportCode() 44 | { 45 | $code_lines = array(); 46 | 47 | $code_lines[] = '_reflection_class->getNamespaceName() ) 51 | { 52 | $code_lines[] = ''; 53 | $code_lines[] = 'namespace ' . $this->_reflection_class->getNamespaceName() . ';'; 54 | $code_lines[] = ''; 55 | } 56 | 57 | // Export the class' signature 58 | $code_lines[] = sprintf( 59 | '%s%s%s %s%s%s', 60 | $this->_reflection_class->isAbstract() ? 'abstract ' : '', 61 | $this->_reflection_class->isFinal() ? 'final ' : '', 62 | $this->_reflection_class->isInterface() 63 | ? 'interface' 64 | : ($this->_reflection_class->isTrait() ? 'trait' : 'class'), 65 | $this->getClassName(), 66 | $this->_getParentClassName() ? " extends {$this->_getParentClassName()}" : '', 67 | $this->_getInterfaceNames() ? (" implements " . join( ', ', $this->_getInterfaceNames() )) : '' 68 | ); 69 | 70 | $code_lines[] = '{'; 71 | $code_lines[] = ''; 72 | 73 | // Export constants 74 | foreach ( $this->_reflection_class->getConstants() as $name => $value ) 75 | { 76 | $reflection_constant = new ReflectionConstant( $name, $value ); 77 | $code_lines[] = "\t" . $reflection_constant->exportCode(); 78 | $code_lines[] = ''; 79 | } 80 | 81 | // Export properties 82 | foreach ( $this->_reflection_class->getProperties() as $property ) 83 | { 84 | $reflection_property = new ReflectionProperty( $property ); 85 | $code_lines[] = "\t" . $reflection_property->exportCode(); 86 | $code_lines[] = ''; 87 | } 88 | 89 | // Export methods 90 | foreach ( $this->_reflection_class->getMethods() as $method ) 91 | { 92 | $reflection_method = new ReflectionMethod( $method ); 93 | $code_lines[] = "\t" . $reflection_method->exportCode(); 94 | $code_lines[] = ''; 95 | } 96 | 97 | $code_lines[] = '}'; 98 | 99 | return join( "\n", $code_lines ); 100 | } 101 | 102 | /** 103 | * Exports the PHP code into a file at the given file path 104 | * 105 | * @param string $file_path File path 106 | * 107 | * @throws \Exception 108 | * @throws \InvalidArgumentException 109 | */ 110 | public function exportFile( $file_path ) 111 | { 112 | $file_path = trim( $file_path ); 113 | if ( empty($file_path) ) 114 | { 115 | throw new \InvalidArgumentException( 'Empty file path given.' ); 116 | } 117 | 118 | $dir = dirname( $file_path ); 119 | if ( !file_exists( $dir ) ) 120 | { 121 | if ( !@mkdir( $dir, 0755, true ) ) 122 | { 123 | throw new \Exception( "Could not create directory: {$dir}" ); 124 | } 125 | } 126 | 127 | $content = $this->exportCode(); 128 | 129 | if ( empty($content) ) 130 | { 131 | throw new \Exception( "Got no content from export for class {$this->_reflection_class->getName()}" ); 132 | } 133 | 134 | $result = file_put_contents( $file_path, $content ); 135 | 136 | if ( $result == false ) 137 | { 138 | throw new \Exception( "Could not write file: {$file_path}" ); 139 | } 140 | } 141 | 142 | /** 143 | * Returns the class' name without namespace 144 | * 145 | * @return string 146 | */ 147 | public function getClassName() 148 | { 149 | $namespace = preg_quote( $this->_reflection_class->getNamespaceName(), '#' ); 150 | $class_name = $this->_reflection_class->getName(); 151 | 152 | return preg_replace( "#^{$namespace}\\\#", '', $class_name ); 153 | } 154 | 155 | /** 156 | * Returns the parent's class full qualified name 157 | * 158 | * @return string 159 | */ 160 | protected function _getParentClassName() 161 | { 162 | $class_name = ''; 163 | $parent = $this->_reflection_class->getParentClass(); 164 | if ( $parent instanceof \ReflectionClass ) 165 | { 166 | $class_name = $parent->getName(); 167 | } 168 | 169 | return $class_name; 170 | } 171 | 172 | /** 173 | * Returns an array of all implemented interfaces 174 | * 175 | * @return array 176 | */ 177 | protected function _getInterfaceNames() 178 | { 179 | $interfaces = $this->_reflection_class->getInterfaceNames(); 180 | $namespace = $this->_reflection_class->getNamespaceName(); 181 | array_walk( 182 | $interfaces, function ( &$name ) use ( $namespace ) 183 | { 184 | if ( !empty($namespace) && strpos( $name, '\\' ) === false ) 185 | { 186 | $name = '\\' . $name; 187 | } 188 | } 189 | ); 190 | 191 | return $interfaces; 192 | } 193 | } 194 | -------------------------------------------------------------------------------- /PHPExport/ReflectionConstant.php: -------------------------------------------------------------------------------- 1 | _name = $name; 36 | $this->_value = $value; 37 | } 38 | 39 | /** 40 | * Exports the PHP code 41 | * 42 | * @return string 43 | */ 44 | public function exportCode() 45 | { 46 | $value = $this->_value; 47 | if ( !is_null( $value ) && !is_numeric( $value ) ) 48 | { 49 | $value = "'{$value}'"; 50 | } 51 | 52 | return sprintf( 53 | 'const %s%s;', 54 | $this->_name, 55 | !is_null( $value ) ? " = {$value}" : '' 56 | ); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /PHPExport/ReflectionExtension.php: -------------------------------------------------------------------------------- 1 | _extension = $extension; 33 | } 34 | 35 | /** 36 | * Exports the PHP code 37 | * 38 | * @return string 39 | */ 40 | public function exportCode() 41 | { 42 | $code_lines = array(); 43 | 44 | foreach ( $this->_extension->getClasses() as $class ) 45 | { 46 | $reflection_class = new ReflectionClass( $class ); 47 | $code_lines[] = $reflection_class->exportCode(); 48 | $code_lines[] = ''; 49 | } 50 | 51 | return join( "\n", $code_lines ); 52 | } 53 | 54 | /** 55 | * Exports all PHP files for this extension 56 | * 57 | * @param string $directory 58 | * @param bool $create_sub_directories 59 | * 60 | * @throws \Exception 61 | * @throws \InvalidArgumentException 62 | */ 63 | public function exportFiles( $directory, $create_sub_directories = true ) 64 | { 65 | $dir = realpath( $directory ); 66 | 67 | if ( empty($dir) || !file_exists( $dir ) ) 68 | { 69 | throw new \InvalidArgumentException( "Directory does not exist: {$directory}" ); 70 | } 71 | 72 | foreach ( $this->_extension->getClasses() as $class ) 73 | { 74 | $reflection_class = new ReflectionClass( $class ); 75 | $current_dir = $dir; 76 | 77 | if ( $create_sub_directories ) 78 | { 79 | $namespaces = explode( '\\', $class->getNamespaceName() ); 80 | array_shift( $namespaces ); 81 | $sub_dirs = join( DIRECTORY_SEPARATOR, $namespaces ); 82 | 83 | if ( !empty($sub_dirs) ) 84 | { 85 | $current_dir = $dir . DIRECTORY_SEPARATOR . $sub_dirs; 86 | if ( !file_exists( $current_dir ) && !@mkdir( $current_dir, 0755, true ) ) 87 | { 88 | throw new \Exception( 'Could not create sub directories: ' . $sub_dirs ); 89 | } 90 | } 91 | } 92 | 93 | $filename = $reflection_class->getClassName() . '.php'; 94 | $file_path = $current_dir . DIRECTORY_SEPARATOR . $filename; 95 | 96 | $result = file_put_contents( $file_path, $reflection_class->exportCode() ); 97 | if ( $result === false ) 98 | { 99 | throw new \Exception( 'Could not create file: ' . $file_path ); 100 | } 101 | } 102 | } 103 | 104 | /** 105 | * Exports a PHAR file for the entire extension 106 | * 107 | * @param string $phar_name 108 | * @param string $output_dir 109 | * @param bool $create_sub_directories 110 | * 111 | * @throws \Exception 112 | */ 113 | public function exportPHAR( $phar_name, $output_dir = '.', $create_sub_directories = true ) 114 | { 115 | ini_set( 'phar.readonly', 0 ); 116 | $temp_dir = sys_get_temp_dir() . DIRECTORY_SEPARATOR . 'PHPExport_' . rand( 1, 9999 ); 117 | 118 | if ( !file_exists( $temp_dir ) && !@mkdir( $temp_dir ) ) 119 | { 120 | throw new \Exception( "Could not create temp directory: {$temp_dir}" ); 121 | } 122 | 123 | $this->exportFiles( $temp_dir, $create_sub_directories ); 124 | 125 | $phar = new \Phar( $output_dir . DIRECTORY_SEPARATOR . $phar_name, 0, $phar_name ); 126 | $phar->buildFromDirectory( $temp_dir, '#\\.php$#' ); 127 | $result = $phar->setStub( $phar->createDefaultStub( 'cli/index.php', 'www/index.php' ) ); 128 | 129 | if ( !$result ) 130 | { 131 | throw new \Exception( 'Could not set stub for phar: ' . $phar_name ); 132 | } 133 | } 134 | } 135 | -------------------------------------------------------------------------------- /PHPExport/ReflectionMethod.php: -------------------------------------------------------------------------------- 1 | _method = $method; 31 | } 32 | 33 | /** 34 | * Exports the PHP code 35 | * 36 | * @return string 37 | */ 38 | public function exportCode() 39 | { 40 | $modifiers = \Reflection::getModifierNames( $this->_method->getModifiers() ); 41 | $params = array(); 42 | 43 | // Export method's parameters 44 | foreach ( $this->_method->getParameters() as $param ) 45 | { 46 | $reflection_parameter = new ReflectionParameter( $param ); 47 | $params[] = $reflection_parameter->exportCode(); 48 | } 49 | 50 | return sprintf( 51 | '%s function %s(%s) {}', 52 | join( ' ', $modifiers ), 53 | $this->_method->getName(), 54 | join( ', ', $params ) 55 | ); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /PHPExport/ReflectionParameter.php: -------------------------------------------------------------------------------- 1 | _parameter = $parameter; 31 | } 32 | 33 | public function exportCode() 34 | { 35 | $default_value = null; 36 | if ( $this->_parameter->isDefaultValueAvailable() ) 37 | { 38 | $default_value = $this->_parameter->getDefaultValue(); 39 | if ( is_scalar( $default_value ) && !is_numeric( $default_value ) ) 40 | { 41 | $default_value = "'{$default_value}'"; 42 | } 43 | } 44 | elseif ( $this->_parameter->isOptional() ) 45 | { 46 | $default_value = 'NULL'; 47 | } 48 | 49 | return sprintf( 50 | '%s%s$%s%s', 51 | $this->_parameter->getClass() ? "{$this->_parameter->getClass()->getName()} " : '', 52 | $this->_parameter->isPassedByReference() ? '&' : '', 53 | $this->_parameter->getName(), 54 | $default_value ? " = {$default_value}" : '' 55 | ); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /PHPExport/ReflectionProperty.php: -------------------------------------------------------------------------------- 1 | _property = $property; 31 | } 32 | 33 | /** 34 | * Exports the PHP code 35 | * 36 | * @return string 37 | */ 38 | public function exportCode() 39 | { 40 | $default_properties = $this->_property->getDeclaringClass()->getDefaultProperties(); 41 | 42 | $modifiers = \Reflection::getModifierNames( $this->_property->getModifiers() ); 43 | 44 | $default_value = null; 45 | if ( array_key_exists( $this->_property->getName(), $default_properties ) ) 46 | { 47 | $default_value = $default_properties[ $this->_property->getName() ]; 48 | if ( !is_numeric( $default_value ) ) 49 | { 50 | $default_value = "'{$default_value}'"; 51 | } 52 | } 53 | 54 | return sprintf( 55 | '%s $%s%s;', 56 | join( ' ', $modifiers ), 57 | $this->_property->getName(), 58 | !is_null( $default_value ) ? " = {$default_value}" : '' 59 | ); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # hollodotme\Helpers 2 | 3 | ## What is this? 4 | 5 | This is a collection of some handy code packages and tutorials to make developer's life easier. 6 | 7 | ## PHPExport 8 | 9 | PHPExport is a small collection of classes to export PHP extentions or classes into PHP code or even a PHAR. 10 | This was designed to make auto completion and namespaces accessible for extentions or classes were only provided as compiled extension but not as PHP code, 11 | e.g. pecl_http Version 2.x. 12 | 13 | Please read the README.md in the subfolder for more information and a "how to use". 14 | 15 | ## Tutorials 16 | 17 | I often come across tool or configuration issues that can be solved by reading tons of documentation. 18 | These tutorials shall summarize each to a single page. Please discuss this and feel free to suggest corrections and improvements! 19 | 20 | For an overview of the tutorials please read the README.md in the subfolder. 21 | 22 | Happy developing! 23 | -------------------------------------------------------------------------------- /Tutorials/README.md: -------------------------------------------------------------------------------- 1 | # Tutorials 2 | 3 | ## How to set up a self-hosted "vagrant cloud" with versioned, self-packaged vagrant boxes 4 | 5 | As I dived into vagrant for a whole new project setup in my company, I was really amazed about how easy it is to get this "up and running". 6 | But then I wanted to adopt the versioned vm service to the private network, because the official vagrant cloud was not an option for the company. 7 | That's the point where the official documentation and support resources will leave you confused. 8 | 9 | Read `vagrant/self-hosted-versioned-boxes.md` for a basic company solution. 10 | -------------------------------------------------------------------------------- /Tutorials/prosody/how-to-install-prosody-on-ubuntu-14.04-lts.md: -------------------------------------------------------------------------------- 1 | # How to install XMPP server prosody with mysql auth backend on Ubuntu 14.04 LTS 2 | 3 | ## Preamble 4 | 5 | Prosody is an alternative to ejabberd and is focused on easy setup and configuration and being efficient with system resources. 6 | You can find all the information about prosody [here](http://prosody.im). 7 | 8 | ### What we will do in this tutorial 9 | 10 | * Of course, install Prosody 11 | * Configure Prosody to use a MySQL backend for authentication 12 | 13 | ### What you will need 14 | 15 | * ssh access to an ubuntu server with sudo privileges 16 | * Maybe a frontend for your MySQL server like phpMyAdmin if you're not familiar with the MySQL command line interface 17 | * A sub-domain to run the jabber server on 18 | * Make sure your server is accessable on port **5222**. 19 | 20 | ## 1. Configure your domains 21 | 22 | * Your server should have an IP address, I will use the `10.100.10.1` for demonstration. 23 | * I will use the following domains to run the XMPP server on: 24 | * jabber.hollo.me 25 | * conference.jabber.hollo.me 26 | 27 | Of course, you should replace the IP and the domains with your real world ones. 28 | 29 | ### Local configuration via /etc/hosts 30 | 31 | Edit your `/etc/hosts` and add the following line to it: 32 | 33 | ```bash 34 | 10.100.10.1 jabber.hollo.me conference.jabber.hollo.me 35 | ``` 36 | 37 | Save the file. That's it. 38 | 39 | This configuration is only for testing the service. 40 | 41 | ### Domain provider / DNS configuration 42 | 43 | * Setup the A-Record of your two domains to point on the IP 10.100.10.1 44 | 45 | You'll have to do this, if your server should be reachable for other clients in a public or private network. 46 | Makes sense. :) 47 | 48 | ## 2. Install MySQL and Prosody 49 | 50 | ### SSH into your server 51 | 52 | ```bash 53 | ssh user-with-sudo@jabber.hollo.me 54 | ``` 55 | 56 | ### Update your system (optional) 57 | 58 | ```bash 59 | jabber.hollo.me$ sudo apt-get update && apt-get dist-upgrade -y 60 | ``` 61 | 62 | ### Install all packages we will need 63 | 64 | ```bash 65 | jabber.hollo.me$ sudo apt-get install mysql-server-5.5 mysql-client-5.5 prosody lua-dbi-mysql 66 | ``` 67 | 68 | Standard MySQL server will do the job, but I prefer using the percona derivate of MySQL, so this line would look like this: 69 | 70 | ```bash 71 | jabber.hollo.me$ sudo apt-get install percona-xtradb-cluster-server-5.5 percona-xtradb-cluster-client-5.5 prosody lua-dbi-mysql 72 | ``` 73 | 74 | * You will be asked for a root password for your mysql server during installation; set one you like. 75 | * After installation both, the mysql server and the prosody server are up and running. 76 | 77 | ### Secure your MySQL server (optional, but strictly recommended) 78 | 79 | ```bash 80 | jabber.hollo.me$ sudo mysql_secure_installation 81 | ``` 82 | 83 | ## 3. Configure MySQL and Prosody 84 | 85 | ### Login to your MySQL server 86 | 87 | ```bash 88 | jabber.hollo.me$ mysql -u root -p 89 | ``` 90 | 91 | ### Create a database named "prosody" 92 | 93 | ```sql 94 | mysql> CREATE DATABASE `prosody`; 95 | ``` 96 | 97 | ### Create a user names "prosody" and grant all privileges for database "prosody" 98 | 99 | ```sql 100 | mysql> CREATE USER 'prosody'@'localhost' IDENTIFIED BY 'secret'; # Replace 'secret' with a password you like! 101 | mysql> GRANT ALL PRIVILEGES ON prosody.* TO 'prosody'@'localhost'; 102 | mysql> quit 103 | ``` 104 | 105 | ### Configure Prosody to use the MySQL backend 106 | 107 | Edit `/etc/prosody/prosody.cfg.lua`: 108 | 109 | ```bash 110 | jabber.hollo.me$ sudo nano /etc/prosody/prosody.cfg.lua 111 | ``` 112 | 113 | Search for the following config block: 114 | 115 | ``` 116 | --storage = "sql" -- Default is "internal" (Debian: "sql" requires one of the 117 | -- lua-dbi-sqlite3, lua-dbi-mysql or lua-dbi-postgresql packages to work) 118 | 119 | -- For the "sql" backend, you can uncomment *one* of the below to configure: 120 | --sql = { driver = "SQLite3", database = "prosody.sqlite" } -- Default. 'database' is the filename. 121 | --sql = { driver = "MySQL", database = "prosody", username = "prosody", password = "secret", host = "localhost" } 122 | --sql = { driver = "PostgreSQL", database = "prosody", username = "prosody", password = "secret", host = "localhost" } 123 | ``` 124 | 125 | Now uncomment the first and the sixth line of this block (remove the `--` and line start) 126 | 127 | The block should now look like this: 128 | 129 | ``` 130 | storage = "sql" -- Default is "internal" (Debian: "sql" requires one of the 131 | -- lua-dbi-sqlite3, lua-dbi-mysql or lua-dbi-postgresql packages to work) 132 | 133 | -- For the "sql" backend, you can uncomment *one* of the below to configure: 134 | --sql = { driver = "SQLite3", database = "prosody.sqlite" } -- Default. 'database' is the filename. 135 | sql = { driver = "MySQL", database = "prosody", username = "prosody", password = "secret", host = "localhost" } 136 | --sql = { driver = "PostgreSQL", database = "prosody", username = "prosody", password = "secret", host = "localhost" } 137 | ``` 138 | 139 | * Replace "secret" with the password you set for the MySQL user 'prosody'. 140 | * Leave anything else as is. 141 | * Save the file and exit nano. 142 | 143 | **Note:** By default Prosody will handle the database scheme by itself the first time it is needed. So we don't need to create tables ourselves. 144 | 145 | ### Create a VHost for Prosody with your domains 146 | 147 | First, create a copy of the example.com VHost file: 148 | 149 | ```bash 150 | jabber.hollo.me$ sudo cp /etc/prosody/conf.avail/example.com.cfg.lua /etc/prosody/conf.avail/jabber.hollo.me.cfg.lua 151 | ``` 152 | 153 | Edit `/etc/prosody/conf.avail/jabber.hollo.me.cfg.lua`: 154 | 155 | ```bash 156 | jabber.hollo.me$ sudo nano /etc/prosody/conf.avail/jabber.hollo.me.cfg.lua 157 | ``` 158 | 159 | The config should look like this: 160 | 161 | ``` 162 | -- Section for example.com 163 | 164 | VirtualHost "example.com" 165 | enabled = false -- Remove this line to enable this host 166 | 167 | -- Assign this host a certificate for TLS, otherwise it would use the one 168 | -- set in the global section (if any). 169 | -- Note that old-style SSL on port 5223 only supports one certificate, and will always 170 | -- use the global one. 171 | ssl = { 172 | key = "/etc/prosody/certs/example.com.key"; 173 | certificate = "/etc/prosody/certs/example.com.crt"; 174 | } 175 | 176 | ------ Components ------ 177 | -- You can specify components to add hosts that provide special services, 178 | -- like multi-user conferences, and transports. 179 | -- For more information on components, see http://prosody.im/doc/components 180 | 181 | -- Set up a MUC (multi-user chat) room server on conference.example.com: 182 | Component "conference.example.com" "muc" 183 | 184 | -- Set up a SOCKS5 bytestream proxy for server-proxied file transfers: 185 | --Component "proxy.example.com" "proxy65" 186 | 187 | ---Set up an external component (default component port is 5347) 188 | --Component "gateway.example.com" 189 | -- component_secret = "password" 190 | ``` 191 | 192 | Edit the content to look like this: 193 | 194 | ``` 195 | -- Section for jabber.hollo.me 196 | 197 | VirtualHost "jabber.hollo.me" 198 | 199 | Component "conference.jabber.hollo.me" "muc" 200 | ``` 201 | 202 | * Save the file and exit nano. 203 | * Yes, that's all we need. 204 | 205 | Now create a symlink to activate the VHost. Prosody automatically loads all VHosts under `/etc/prosody/conf.d`. 206 | 207 | ```bash 208 | jabber.hollo.me$ sudo ln -sf ../conf.avail/jabber.hollo.me.cfg.lua /etc/prosody/conf.d/jabber.hollo.me.cfg.lua 209 | ``` 210 | 211 | ### Restart Prosody 212 | 213 | The basic configuration is done. Now restart Prosody to let the changes take effect. 214 | 215 | ```bash 216 | jabber.hollo.me$ sudo service prosody restart 217 | ``` 218 | 219 | ## 4. Create jabber users 220 | 221 | * Prosody serves a command line interface `prosodyctl` that easily lets you register some users. 222 | * We will add two test users for demonstration 223 | 224 | ```bash 225 | jabber.hollo.me$ sudo prosodyctl register testuser1 jabber.hollo.me secret1 226 | jabber.hollo.me$ sudo prosodyctl register testuser2 jabber.hollo.me secret2 227 | ``` 228 | 229 | **Note:** This is the first time Prosody uses the MySQL backend and will apply the nessecary database scheme. 230 | 231 | ### Check if database scheme was applied (optional) 232 | 233 | Login to your MySQL server: 234 | 235 | ```bash 236 | jabber.hollo.me$ mysql -u root -p 237 | ``` 238 | 239 | Show all tables in database `prosody`: 240 | 241 | ```sql 242 | mysql> SHOW TABLES FROM `prosody`; 243 | ``` 244 | 245 | Output should look like this: 246 | 247 | ``` 248 | +-------------------+ 249 | | Tables_in_prosody | 250 | +-------------------+ 251 | | prosody | 252 | +-------------------+ 253 | 1 row in set (0.00 sec) 254 | ``` 255 | 256 | Now check, if our two testusers were inserted. 257 | 258 | ```sql 259 | mysql> SELECT * FROM `prosody`.`prosody` WHERE 1; 260 | ``` 261 | 262 | Output should look like this: 263 | 264 | ``` 265 | +-----------------+-----------+----------+----------+--------+---------+ 266 | | host | user | store | key | type | value | 267 | +-----------------+-----------+----------+----------+--------+---------+ 268 | | jabber.hollo.me | testuser1 | accounts | password | string | secret1 | 269 | | jabber.hollo.me | testuser2 | accounts | password | string | secret2 | 270 | +-----------------+-----------+----------+----------+--------+---------+ 271 | 2 rows in set (0.00 sec) 272 | ``` 273 | 274 | **Note:** By default the user's secrets are stored in plaintext. You can enable hashing in the config, see: 275 | [mod_auth_internal_hashed](http://prosody.im/doc/modules/mod_auth_internal_hashed) 276 | 277 | -------------------------------------------------------------------------------- /Tutorials/vagrant/self-hosted-vagrant-boxes-with-versioning.md: -------------------------------------------------------------------------------- 1 | # How to set up a self-hosted "vagrant cloud" with versioned, self-packaged vagrant boxes 2 | 3 | ## Preamble 4 | 5 | Before we start setting things up, I assume this is what you know / what you have: 6 | 7 | * What vagrant is and how it basically works (obviously!) 8 | * How to set up a webserver like nginx or apache2 9 | * Basic knowledge about working with linux systems 10 | * A public or private webserver where you can run/configure a webserver (nginx/apache2) and upload/download files. 11 | * A host system with a GUI (e.g. Windows, Mac OS X, etc.) 12 | 13 | The tutorial uses an installation of [Ubuntu 14.04.2 LTS](https://wiki.ubuntu.com/TrustyTahr/ReleaseNotes) 14 | as the guest machine, [VirtualBox](http://virtualbox.org) at version 5.0.0 as provider 15 | and [Vagrant](http://vagrantup.com) at version 1.7.4. 16 | 17 | ## 1. Install the tools 18 | 19 | * Download and install VirtualBox 5.0.0 at http://download.virtualbox.org/virtualbox/5.0.0/ (Choose the installer that fits your system) 20 | * Download and install Vagrant 1.7.4 at https://dl.bintray.com/mitchellh/vagrant/ (Choose the installer that fits your system) 21 | 22 | ## 2. Prepare your virtual machine 23 | 24 | ### 2.1 Import an Ubuntu image to VirtualBox 25 | 26 | * Download a VirtualBox image of Ubuntu 14.04.2 LTS, e.g. at http://virtualboxes.org/images/ubuntu-server/ (all the following steps refer to this image) 27 | * Open the VirtualBox GUI and choose `File > Import appliance ...`, select the `.ova` file you downloaded before. 28 | * Change the appliance settings to fit your needs, for now I'll only change the name of the machine from `ubuntu-14.04-server-amd64` to `devops-template`. 29 | * Important: Make sure to activate `Reinitialize the MAC address of all network cards` checkbox! 30 | * Click `Import` and you'll have a new virtual machine added to VirtualBox after a few minutes ready to run. 31 | 32 | ### 2.2 Setup the virtual machine 33 | 34 | #### Before you boot the vm for the first time: 35 | 36 | * Select the newly imported vm named `devops-template` in VirtualBox GUI and click `Settings` 37 | * Select the tab `Network` 38 | * Activate `Enable Network Adapter` (if not already activated) under the tab `Adapter 1` 39 | * Select `Attached to:` `NAT` ([this is a requirement by Vagrant](http://docs.vagrantup.com/v2/virtualbox/boxes.html)) 40 | * Leave everything else as is. 41 | 42 | #### Configure the guest 43 | 44 | * Select the vm named `devops-template` in VirtualBox GUI and click `Start` (wait until you see the `ubuntu-amd64 login:`) 45 | * Type `ubuntu` as loginname an `reverse` as password. 46 | * First of all, update the machine. This will take a moment. Get a coffee! 47 | 48 | ```bash 49 | $ sudo apt-get update 50 | $ sudo apt-get dist-upgrade -y 51 | ``` 52 | 53 | * Edit the file `/root/.profile` 54 | 55 | ```bash 56 | $ sudo nano /root/.profile 57 | # ~/.profile: executed by Bourne-compatible login shells. 58 | 59 | if [ "$BASH" ]; then 60 | if [ -f ~/.bashrc ]; then 61 | . ~/.bashrc 62 | fi 63 | fi 64 | 65 | mesg n # replace this line by "tty -s && mesg n" 66 | ``` 67 | 68 | Note: This avoids an annoying warning, when you `vagrant up` later. 69 | 70 | * Change the hostname 71 | 72 | ```bash 73 | $ sudo nano /etc/hostname 74 | ubuntu-amd64 # replace this by "devops-template" 75 | ``` 76 | * Let the machine resolve its own hostname 77 | 78 | ```bash 79 | $ sudo nano /etc/hosts 80 | 127.0.0.1 localhost 81 | 127.0.1.1 ubuntu-amd64 # replace this by "127.0.1.1 devops-template" 82 | 83 | # The following lines are desirable for IPv6 capable hosts 84 | ::1 localhost ip6-localhost ip6-loopback 85 | ff02::1 ip6-allnodes 86 | ff02::2 ip6-allrouters 87 | ``` 88 | 89 | * Finalize language settings 90 | 91 | ```bash 92 | $ sudo locale-gen en_US.UTF-8 # Or whatever language you want to use 93 | $ sudo dpkg-reconfigure locales 94 | $ sudo nano /etc/default/locale 95 | LANG="en_US.UTF-8" 96 | LANGUAGE="en_US" 97 | ``` 98 | 99 | * Add the `vagrant` user 100 | 101 | ```bash 102 | $ sudo adduser vagrant 103 | # Set the password to "vargrant" too! 104 | # Set the Full Name to "Vagrant", leave the rest blank 105 | ``` 106 | 107 | * Allow Vagrant to login via insecure private key 108 | 109 | ```bash 110 | # Add a ssh config folder and authorized_keys file 111 | $ sudo mkdir /home/vagrant/.ssh 112 | $ sudo touch /home/vagrant/.ssh/authorized_keys 113 | # Set owner and permissions 114 | $ sudo chown -R vagrant /home/vagrant/.ssh 115 | $ sudo chmod 0700 /home/vagrant/.ssh 116 | $ sudo chmod 0600 /home/vagrant/.ssh/authorized_keys 117 | # Add the insecure public key, see https://github.com/mitchellh/vagrant/tree/master/keys 118 | $ su vagrant 119 | $ curl 'https://raw.githubusercontent.com/mitchellh/vagrant/master/keys/vagrant.pub' >> /home/vagrant/.ssh/authorized_keys 120 | $ exit 121 | ``` 122 | 123 | * Configure the sudo rights of user `vagrant` 124 | 125 | ```bash 126 | $ sudo nano /etc/sudoers.d/vagrant 127 | vagrant ALL=(ALL) NOPASSWD: ALL 128 | $ sudo chmod 0440 /etc/sudoers.d/vagrant 129 | ``` 130 | 131 | * Disable DNS usage for sshd 132 | 133 | ```bash 134 | $ sudo nano /etc/ssh/sshd_config 135 | # Add the following line at the end of the file: 136 | UseDNS no 137 | ``` 138 | 139 | * **Important:** Install the VirtualBox Guest Additions __with the proper version__ 140 | 141 | Hint: Do not install the guest additions with `apt-get install virtualbox-guest-additions-iso` as this release is mostly outdated. 142 | I said above that I'm using VirtualBox in Version `5.0.0`, so the guest additions should be of the same version. 143 | 144 | ```bash 145 | # prepare 146 | $ sudo apt-get install -y linux-headers-generic build-essential dkms 147 | # get the right ISO from http://download.virtualbox.org/virtualbox/ 148 | $ wget http://download.virtualbox.org/virtualbox/5.0.0/VBoxGuestAdditions_5.0.0.iso 149 | # create a mount folder 150 | $ sudo mkdir /media/VBoxGuestAdditions 151 | # mount the ISO 152 | $ sudo mount -o loop,ro VBoxGuestAdditions_5.0.0.iso /media/VBoxGuestAdditions 153 | # install the guest additions 154 | $ sudo sh /media/VBoxGuestAdditions/VBoxLinuxAdditions.run 155 | # remove the ISO 156 | $ rm VBoxGuestAdditions_5.0.0.iso 157 | # unmount the ISO 158 | $ sudo umount /media/VBoxGuestAdditions 159 | # remove the mount folder 160 | $ sudo rmdir /media/VBoxGuestAdditions 161 | ``` 162 | 163 | * **Last but not least:** Change the welcome message and add the version number. We will change this later to see versioning work. 164 | 165 | ```bash 166 | $ rm -rf /etc/motd 167 | $ sudo nano /etc/motd 168 | -- 169 | Welcome to devops-template version 0.1.0! 170 | -- 171 | ``` 172 | 173 | * Reboot the vm to see your changes take effect: 174 | 175 | ```bash 176 | $ sudo shutdown -r now 177 | ``` 178 | 179 | Now you should see a login terminal like this: 180 | 181 | ```bash 182 | Ubuntu 14.04.2 LTS devops-template tty1 183 | 184 | devops-template login: _ 185 | ``` 186 | 187 | Login and check the message of the day (motd) we set up: 188 | 189 | ```bash 190 | Last login: ... 191 | Welcome to Ubuntu ... 192 | 193 | * Documentation: ... 194 | 195 | [...] 196 | 197 | -- 198 | Welcome to devops-template version 0.1.0! 199 | -- 200 | ubuntu@devops-template:~$ _ 201 | ``` 202 | 203 | * Shutdown the vm 204 | 205 | ```bash 206 | $ sudo shutdown -h now 207 | ``` 208 | 209 | Got it? Yeah, preparation is done! 210 | 211 | ## 3. Package the vagrant box 212 | 213 | Remember, we said in the message of the day, that this is version `0.1.0`. 214 | 215 | * Open a terminal on your host machine 216 | * Create two new directories: `VagrantBoxes` and `VagrantTest` 217 | 218 | 219 | ```bash 220 | $ mkdir ~/VagrantBoxes 221 | $ mkdir ~/VagrantTest 222 | ``` 223 | 224 | ### 3.1 Create the box out of the vm named `devops-template` in VirtualBox 225 | 226 | * Change into the `VagrantBoxes` directory. 227 | * Package the box 228 | 229 | ```bash 230 | $ cd ~/VagrantBoxes 231 | $ vagrant package --base 'devops-template' --output 'devops_0.1.0.box' 232 | ``` 233 | 234 | Note: Because we will build multiple versions of the `devops-template` vm, we will put the version number in the name of the box file. 235 | 236 | Packaging will take some time, you may get your next coffee! 237 | 238 | When packaging is done, you should see an output like this: 239 | 240 | ```bash 241 | ==> devops-template: Exporting VM... 242 | ==> devops-template: Compressing package to: ~/VagrantBoxes/devops_0.1.0.box 243 | ``` 244 | 245 | ### 3.2 Test the box 246 | 247 | * Change into the `VagrantTest` directory. 248 | 249 | ```bash 250 | $ cd ~/VagrantTest 251 | ``` 252 | 253 | * Add the box to vagrant 254 | 255 | ```bash 256 | $ vagrant box add 'devops' file://~/VagrantBoxes/devops_0.1.0.box 257 | ``` 258 | 259 | If successful, you should see output like this: 260 | 261 | ```bash 262 | ==> box: Adding box 'devops' (v0) for provider: 263 | box: Downloading: file://~/VagrantBoxes/devops_0.1.0.box 264 | ==> box: Successfully added box 'devops' (v0) for 'virtualbox'! 265 | ``` 266 | 267 | Note the `v0`. There is no version specified yet. 268 | 269 | * Init a vagrant project 270 | 271 | ```bash 272 | $ vagrant init 273 | ``` 274 | 275 | Now there is a `Vagrantfile` with default settings in your current directory. 276 | 277 | * Edit this `Vagrantfile` using a text editor 278 | 279 | ```bash 280 | # Change the following line from 281 | config.vm.box = "base" 282 | # to 283 | config.vm.box = "devops" 284 | # save the file 285 | ``` 286 | 287 | Note: `base` is vagrant's default name for a box. But we told `vagrant box add` the name `devops`. 288 | 289 | * Bring the machine up 290 | 291 | ```bash 292 | $ vagrant up 293 | ``` 294 | 295 | If done, you should see output like this: 296 | 297 | ```bash 298 | Bringing machine 'default' up with 'virtualbox' provider... 299 | ==> default: Importing base box 'devops'... 300 | ==> default: Matching MAC address for NAT networking... 301 | ==> default: Setting the name of the VM: VagrantTest_default_1406634147824_25052 302 | ==> default: Clearing any previously set network interfaces... 303 | ==> default: Preparing network interfaces based on configuration... 304 | default: Adapter 1: nat 305 | ==> default: Forwarding ports... 306 | default: 22 => 2222 (adapter 1) 307 | ==> default: Booting VM... 308 | ==> default: Waiting for machine to boot. This may take a few minutes... 309 | default: SSH address: 127.0.0.1:2222 310 | default: SSH username: vagrant 311 | default: SSH auth method: private key 312 | default: Warning: Connection timeout. Retrying... 313 | ==> default: Machine booted and ready! 314 | ==> default: Checking for guest additions in VM... 315 | ==> default: Mounting shared folders... 316 | default: /vagrant => ~/VagrantTest 317 | ``` 318 | 319 | **Note:** Vagrant's default behaviour is to boot the vm in headless mode (without a GUI in background). 320 | While booting the vm you can see the new vm in your VirtualBox GUI as "-> Running" 321 | 322 | * SSH into the machine 323 | 324 | ```bash 325 | $ vagrant ssh 326 | ``` 327 | 328 | Now you should see our previously added message of the day. 329 | 330 | * Check if your `VagrantTest` folder is mounted 331 | 332 | ```bash 333 | $ cd /vagrant/ 334 | $ ls -la 335 | ``` 336 | 337 | And you should see something like this: 338 | 339 | ```bash 340 | drwxr-xr-x 1 vagrant vagrant 136 Jul 29 13:38 . 341 | drwxr-xr-x 23 root root 4096 Jul 29 13:42 .. 342 | drwxr-xr-x 1 vagrant vagrant 102 Jul 29 13:38 .vagrant 343 | -rw-r--r-- 1 vagrant vagrant 4813 Jul 29 13:39 Vagrantfile 344 | ``` 345 | 346 | **Okay, your box works fine, well done!** 347 | 348 | * Exit the SSH session and destroy the machine 349 | 350 | ```bash 351 | $ exit # On guest 352 | ``` 353 | 354 | ```bash 355 | $ vagrant destroy # On host 356 | ``` 357 | 358 | * Remove the box from vagrant (we will add it later with a version again!) 359 | 360 | ```bash 361 | $ vagrant box remove 'devops' 362 | Removing box 'devops' (v0) with provider 'virtualbox'... 363 | ``` 364 | 365 | ## 4. Using a box catalog for versioning 366 | 367 | In order to serve multiple versions of a vagrant box and enable update notifications we need to set up a box catalog. 368 | This catalog is written in JSON code to a single file. 369 | 370 | To keep things simple at this point we will carry on in the local filesystem of your host. 371 | 372 | ### 4.1 Set up the catalog for the `devops` box 373 | 374 | * Change into the `VagrantBoxes` directory. 375 | 376 | ```bash 377 | cd ~/VagrantBoxes 378 | ``` 379 | 380 | * Create the file `devops.json` with the following content 381 | 382 | ```json 383 | { 384 | "name": "devops", 385 | "description": "This box contains Ubuntu 14.04.2 LTS 64-bit.", 386 | "versions": [{ 387 | "version": "0.1.0", 388 | "providers": [{ 389 | "name": "virtualbox", 390 | "url": "file://~/VagrantBoxes/devops_0.1.0.box", 391 | "checksum_type": "sha1", 392 | "checksum": "d3597dccfdc6953d0a6eff4a9e1903f44f72ab94" 393 | }] 394 | }] 395 | } 396 | ``` 397 | 398 | **What is going on here?** 399 | * We tell the catalog to be related to the box name `devops`. All versions will be grouped under this name. 400 | * We created a clear statement of what is in the box. 401 | * We defined the first version 0.1.0 402 | * We tell that, version 0.1.0 is available for provider virtualbox under the URL file://~/VagrantBoxes/devops_0.1.0.box 403 | * That's why you should put the version into the filename! 404 | * We tell vagrant that there is a sha1-checksum to check when importing the box. 405 | * To determine the checksum of your box, you can simple do sth. like this: 406 | 407 | ```bash 408 | $ openssl sha1 ~/VagrantBoxes/devops_0.1.0.box 409 | SHA1(~/VagrantBoxes/devops_0.1.0.box)= d3597dccfdc6953d0a6eff4a9e1903f44f72ab94 410 | ``` 411 | 412 | Note: This is done on a linux based system. On Windows there will be another way. 413 | 414 | Your catalog is finished! 415 | 416 | ### 4.2 Link the catalog to vagrant instead of the box 417 | 418 | * Change into the `VagrantTest` directory 419 | 420 | ```bash 421 | $ cd ~/VagrantTest 422 | ``` 423 | 424 | * Edit the `Vagrantfile` file in a text editor 425 | 426 | ```ruby 427 | # Under ... 428 | config.vm.box = "devops" 429 | # ... add the line 430 | config.vm.box_url = "file://~/VagrantBoxes/devops.json" 431 | # save the file 432 | ``` 433 | 434 | * Run `vagrant up` __without__ adding the box manually using `vagrant box add` 435 | 436 | ```bash 437 | $ vagrant up 438 | ``` 439 | 440 | When done, you should see output like this: 441 | 442 | ```bash 443 | Bringing machine 'default' up with 'virtualbox' provider... 444 | ==> default: Box 'devops' could not be found. Attempting to find and install... 445 | default: Box Provider: virtualbox 446 | default: Box Version: >= 0 447 | ==> default: Loading metadata for box 'file://~/VagrantBoxes/devops.json' 448 | default: URL: file://~/VagrantBoxes/devops.json 449 | ==> default: Adding box 'devops' (v0.1.0) for provider: virtualbox 450 | default: Downloading: file://~/VagrantBoxes/devops_0.1.0.box 451 | default: Calculating and comparing box checksum... 452 | ==> default: Successfully added box 'devops' (v0.1.0) for 'virtualbox'! 453 | ``` 454 | 455 | **Note** the line `Loading metadata for box 'file://~/VagrantBoxes/devops.json'` that confirms the catalog is read. 456 | **And note** the line `Adding box 'devops' (v0.1.0) for provider: virtualbox` that confirms that version 0.1.0 is added to VirtualBox. 457 | The last two lines confirm that our checksum in the `devops.json` file was correct. 458 | 459 | ```bash 460 | ==> default: Importing base box 'devops'... 461 | ==> default: Matching MAC address for NAT networking... 462 | ==> default: Checking if box 'devops' is up to date... 463 | ==> default: Setting the name of the VM: VagrantTest_default_1406640770074_13210 464 | ==> default: Clearing any previously set network interfaces... 465 | ==> default: Preparing network interfaces based on configuration... 466 | default: Adapter 1: nat 467 | ==> default: Forwarding ports... 468 | default: 22 => 2222 (adapter 1) 469 | ==> default: Booting VM... 470 | ==> default: Waiting for machine to boot. This may take a few minutes... 471 | default: SSH address: 127.0.0.1:2222 472 | default: SSH username: vagrant 473 | default: SSH auth method: private key 474 | default: Warning: Connection timeout. Retrying... 475 | ==> default: Machine booted and ready! 476 | ==> default: Checking for guest additions in VM... 477 | ==> default: Mounting shared folders... 478 | default: /vagrant => ~/VagrantTest 479 | ``` 480 | 481 | Again, our machine is up and running. 482 | If you wish you now can re-check by ssh into the machine, reading the message of the day and listing the content of `/vagrant` on the guest. 483 | 484 | Okay, now we have built a vagrant box with an initial version. **Let's add another version.** 485 | 486 | * Halt the vm (**do not** destroy it!) 487 | 488 | ```bash 489 | $ vagrant halt 490 | ==> default: Attempting graceful shutdown of VM... 491 | ``` 492 | 493 | ### 4.3 Raise the box version by changing the template vm 494 | 495 | * Open the VirtualBox GUI, select the vm named `devops-template` and click `Start` 496 | * Log into the vm after it has booted 497 | * Change the version number in `/etc/motd` from `0.1.0` to `0.1.1` and save the file 498 | 499 | ```bash 500 | $ sudo nano /etc/motd 501 | -- 502 | Welcome to devops-template version 0.1.1! 503 | -- 504 | ``` 505 | 506 | * Shutdown the vm 507 | 508 | ```bash 509 | $ sudo shutdown -h now 510 | ``` 511 | 512 | **To be clear:** Changing the version number in `/etc/motd` has nothing to do with the version itself. It just simulates 513 | a minor but visible change to the vm template. Instead of changing the content of a file, you'll be installing software 514 | or editing configs on a real-world vm. 515 | 516 | * Change into `VagrantBoxes` directory. 517 | 518 | ```bash 519 | $ cd ~/VagrantBoxes 520 | ``` 521 | 522 | * Package the template vm to a new vagrant box 523 | 524 | ```bash 525 | $ vagrant package --base 'devops-template' --output 'devops_0.1.1.box' 526 | ==> devops-template: Exporting VM... 527 | ==> devops-template: Compressing package to: ~/VagrantBoxes/devops_0.1.1.box 528 | ``` 529 | 530 | Note the raised version number `0.1.1` in the output filename! 531 | 532 | Your directory listing of `~/VagrantBoxes` should look like this now: 533 | 534 | ```bash 535 | $ ls -a1 ~/VagrantBoxes 536 | . 537 | .. 538 | devops.json 539 | devops_0.1.0.box 540 | devops_0.1.1.box 541 | ``` 542 | 543 | If you wish you can test your new box like done under chapter 3.2. 544 | 545 | ### 4.4 Add the new version to the catalog 546 | 547 | * Edit the `devops.json` file in a text editor and extend the content to look like this: 548 | 549 | ```json 550 | { 551 | "name": "devops", 552 | "description": "This box contains Ubuntu 14.04.1 LTS 64-bit.", 553 | "versions": [{ 554 | "version": "0.1.0", 555 | "providers": [{ 556 | "name": "virtualbox", 557 | "url": "file:///Users/hollodotme/VagrantBoxes/devops_0.1.0.box", 558 | "checksum_type": "sha1", 559 | "checksum": "d3597dccfdc6953d0a6eff4a9e1903f44f72ab94" 560 | }] 561 | },{ 562 | "version": "0.1.1", 563 | "providers": [{ 564 | "name": "virtualbox", 565 | "url": "file:///Users/hollodotme/VagrantBoxes/devops_0.1.1.box", 566 | "checksum_type": "sha1", 567 | "checksum": "0b530d05896cfa60a3da4243d03eccb924b572e2" 568 | }] 569 | }] 570 | } 571 | ``` 572 | 573 | **Don't forget** to determine the checksum of the newly created box `devops_0.1.1.box`! 574 | 575 | ### 4.5 Check for outdated vagrant box and update 576 | 577 | * Change into `VagrantTest` directory. 578 | 579 | ```bash 580 | $ cd ~/VagrantTest 581 | ``` 582 | 583 | * Ask if your vagrant box is outdated 584 | 585 | ```bash 586 | $ vagrant box outdated 587 | Checking if box 'devops' is up to date... 588 | A newer version of the box 'devops' is available! You currently 589 | have version '0.1.0'. The latest is version '0.1.1'. Run 590 | `vagrant box update` to update. 591 | ``` 592 | 593 | Suprise, suprise - a new version is available! 594 | 595 | **Note:** Instead of manually asking for outdated boxes, vagrant will notify you automatically when you use the 596 | Vagrant commands like `vagrant up`, `vagrant reload`, `vagrant resume`, etc.! 597 | 598 | * Update the box 599 | 600 | ```bash 601 | $ vagrant box update 602 | ==> default: Checking for updates to 'devops' 603 | default: Latest installed version: 0.1.0 604 | default: Version constraints: 605 | default: Provider: virtualbox 606 | ==> default: Updating 'devops' with provider 'virtualbox' from version 607 | ==> default: '0.1.0' to '0.1.1'... 608 | ==> default: Loading metadata for box 'file://~/VagrantBoxes/devops.json' 609 | ==> default: Adding box 'devops' (v0.1.1) for provider: virtualbox 610 | default: Downloading: file://~/VagrantBoxes/devops_0.1.1.box 611 | default: Calculating and comparing box checksum... 612 | ==> default: Successfully added box 'devops' (v0.1.1) for 'virtualbox'! 613 | ``` 614 | 615 | **Note:** The box with version `0.1.0` still exists. Vagrant will never prune your boxes automatically because of potential data loss. 616 | 617 | * Remove the old box 618 | 619 | ```bash 620 | $ vagrant box remove 'devops' 621 | You requested to remove the box 'devops' with provider 622 | 'virtualbox'. This box has multiple versions. You must 623 | explicitly specify which version you want to remove with 624 | the `--box-version` flag. The available versions for this 625 | box are: 626 | 627 | * 0.1.0 628 | * 0.1.1 629 | ``` 630 | 631 | Okay, so... 632 | 633 | ```bash 634 | $ vagrant box remove 'devops' --box-version '0.1.0' 635 | ~/VagrantTest/Vagrantfile:5: warning: already initialized constant VAGRANTFILE_API_VERSION 636 | ~/VagrantTest/Vagrantfile:5: warning: previous definition of VAGRANTFILE_API_VERSION was here 637 | Box 'devops' (v0.1.0) with provider 'virtualbox' appears 638 | to still be in use by at least one Vagrant environment. Removing 639 | the box could corrupt the environment. We recommend destroying 640 | these environments first: 641 | 642 | default (ID: 3206d9d1a427459daac770f2e7e81f1b) 643 | 644 | Are you sure you want to remove this box? [y/N] 645 | ``` 646 | 647 | **N**o! Let's destroy it first. 648 | 649 | ```bash 650 | $ vagrant destroy 651 | default: Are you sure you want to destroy the 'default' VM? [y/N] y 652 | ==> default: Destroying VM and associated drives... 653 | ``` 654 | 655 | Now remove it, please! 656 | 657 | ```bash 658 | $ vagrant box remove 'devops' --box-version '0.1.0' 659 | Removing box 'devops' (v0.1.0) with provider 'virtualbox'... 660 | ``` 661 | 662 | Hell yeah! 663 | 664 | ### 4.6 Check for the change 665 | 666 | * Bring up the machine 667 | 668 | ```bash 669 | $ vagrant up 670 | ``` 671 | 672 | * SSH into the machine 673 | 674 | ```bash 675 | $ vagrant ssh 676 | ``` 677 | 678 | Now you should see the previously changed version number `0.1.1` in the message of the day after login. 679 | 680 | ```bash 681 | -- 682 | Welcome to devops-template version 0.1.1! 683 | -- 684 | ``` 685 | 686 | **So we are up-to-date!** 687 | 688 | __What we did so far:__ 689 | * We created a virtual machine template 690 | * We built two versioned vagrant boxes out of the virtual machine template 691 | * We established a box catalog to serve the versions to the client 692 | * We updated an outdated box 693 | 694 | __What we will do now:__ 695 | * Hosting the box catalog and the boxes on a webserver a.k.a. set up our own vagrant cloud. 696 | 697 | So cleanup your desk: exit your SSH session, destroy the vm and remove it from vagrant. 698 | 699 | ```bash 700 | $ exit # On guest 701 | ``` 702 | 703 | ```bash 704 | $ vagrant destroy # On host 705 | $ vagrant box remove 'devops' 706 | ``` 707 | 708 | ## 5. Hosting 709 | 710 | As I mentioned at the beginning, I assume that you have private/public webserver and access to its config and filesystem. 711 | 712 | For explanation I'll use the domain `www.example.com` targeting to this webserver. 713 | Furthermore `www.example.com` points to `/var/www/` on the webserver's filesystem (document root). 714 | 715 | ### 5.1 Suggested directory structure 716 | 717 | To keep things easy I prefer to separate the catalog and the box files physically in the filesystem. 718 | Keep on reading and you'll understand why. 719 | 720 | ```bash 721 | - /var/www # document root 722 | `- vagrant 723 | `- devops # box name folder 724 | |- boxes # contains all available box files 725 | | |- devops_0.1.0.box # version 0.1.0 726 | | `- devops_0.1.1.box # version 0.1.1 727 | `- devops.json # box catalog 728 | ``` 729 | 730 | Translated to URLs we have three targets to care about (we will use these later): 731 | * The catalog: http://www.example.com/vagrant/devops/devops.json 732 | * Box (v0.1.0): http://www.example.com/vagrant/devops/boxes/devops_0.1.0.box 733 | * Box (v0.1.1): http://www.example.com/vagrant/devops/boxes/devops_0.1.1.box 734 | 735 | ### 5.2 Webserver configuration 736 | 737 | I want to explain the basic webserver configuration with nginx on a linux server, because this is my favorite software. 738 | The configuration can be ported to apache and/or windows as well. 739 | 740 | * SSH into your server. 741 | 742 | ```bash 743 | $ ssh user@example.com 744 | ``` 745 | 746 | * Install nginx 747 | 748 | ```bash 749 | $ sudo apt-get install nginx-full 750 | ``` 751 | 752 | * Create the target folders and set permissions 753 | 754 | ```bash 755 | # Create folders 756 | $ sudo mkdir -p /var/www/vagrant/devops/boxes 757 | # Set owner to www-data 758 | $ sudo chown -R www-data:www-data /var/www 759 | # Set permissions 760 | $ sudo chmod -R 0751 /var/www 761 | ``` 762 | 763 | * Delete the `default` sym-linked config for virtual hosts (vhost) 764 | * Just to make sure there is no colliding config! 765 | 766 | ```bash 767 | $ sudo rm -rf /etc/nginx/sites-enabled/default 768 | ``` 769 | 770 | * Create a new specific vhost config for `www.example.com` 771 | 772 | ```bash 773 | sudo nano /etc/nginx/sites-available/example.com 774 | ``` 775 | 776 | ... with the following content: 777 | 778 | ```bash 779 | server { 780 | listen 80 default_server; 781 | listen [::]:80 ipv6only=on default_server; 782 | 783 | server_name example.com www.example.com; 784 | 785 | root /var/www; 786 | 787 | # Match the box name in location and search for its catalog 788 | # e.g. http://www.example.com/vagrant/devops/ resolves /var/www/vagrant/devops/devops.json 789 | location ~ ^/vagrant/([^\/]+)/$ { 790 | index $1.json; 791 | try_files $uri $uri/ $1.json =404; 792 | autoindex off; 793 | } 794 | 795 | # Enable auto indexing for the folder with box files 796 | location ~ ^/vagrant/([^\/]+)/boxes/$ { 797 | try_files $uri $uri/ =404; 798 | autoindex on; 799 | autoindex_exact_size on; 800 | autoindex_localtime on; 801 | } 802 | 803 | # Serve json files with content type header application/json 804 | location ~ \.json$ { 805 | add_header Content-Type application/json; 806 | } 807 | 808 | # Serve box files with content type application/octet-stream 809 | location ~ \.box$ { 810 | add_header Content-Type application/octet-stream; 811 | } 812 | 813 | # Deny access to document root and the vagrant folder 814 | location ~ ^/(vagrant/)?$ { 815 | return 403; 816 | } 817 | } 818 | ``` 819 | 820 | * Sym-link the vhost config to enable it 821 | 822 | ```bash 823 | $ sudo ln -s /etc/nginx/sites-available/example.com /etc/nginx/sites-enabled/000-example.com 824 | ``` 825 | 826 | * Restart nginx and exit the webserver 827 | 828 | ```bash 829 | $ service nginx restart 830 | $ exit 831 | ``` 832 | 833 | ### 5.3 Change the box catalog 834 | 835 | Now, that our boxes won't be stored any longer on the local filesystem, we have to change their locations in the box catalog. 836 | 837 | * Open `~/VagrantBoxes/devops.json` in your text editor and change the content to this: 838 | 839 | ```json 840 | { 841 | "name": "devops", 842 | "description": "This box contains Ubuntu 14.04.1 LTS 64-bit.", 843 | "versions": [{ 844 | "version": "0.1.0", 845 | "providers": [{ 846 | "name": "virtualbox", 847 | "url": "http://www.example.com/vagrant/devops/boxes/devops_0.1.0.box", 848 | "checksum_type": "sha1", 849 | "checksum": "d3597dccfdc6953d0a6eff4a9e1903f44f72ab94" 850 | }] 851 | },{ 852 | "version": "0.1.1", 853 | "providers": [{ 854 | "name": "virtualbox", 855 | "url": "http://www.example.com/vagrant/devops/boxes/devops_0.1.1.box", 856 | "checksum_type": "sha1", 857 | "checksum": "0b530d05896cfa60a3da4243d03eccb924b572e2" 858 | }] 859 | }] 860 | } 861 | ``` 862 | 863 | * Upload this file to your webserver to directory `/var/www/vagrant/devops/`. 864 | 865 | If you open the URL http://www.example.com/vagrant/devops/ in your browser you should see your JSON box catalog. 866 | 867 | ### 5.4 Upload your boxes 868 | 869 | * Upload both box files to your webserver to directory `/var/www/vagrant/devops/boxes/`. 870 | 871 | If you open the URL http://www.example.com/vagrant/devops/boxes/ in your browser you should see a directory 872 | listing with both box files listed. 873 | 874 | ### 5.5 Change the `Vagrantfile` 875 | 876 | * Change into the `~/VagrantTest` directory on your host. 877 | 878 | ```bash 879 | $ cd ~/VagrantTest 880 | ``` 881 | 882 | * Open the `Vagrantfile` file in your text editor 883 | 884 | ```json 885 | # Change the line 886 | config.vm.box_url = "file://~/VagrantBoxes/devops.json" 887 | # to 888 | config.vm.box_url = "http://www.example.com/vagrant/devops/" 889 | # save the file 890 | ``` 891 | 892 | ### 5.6 Get finally up and running 893 | 894 | * Bring up the machine 895 | 896 | ```bash 897 | $ vagrant up 898 | Bringing machine 'default' up with 'virtualbox' provider... 899 | ==> default: Box 'devops' could not be found. Attempting to find and install... 900 | default: Box Provider: virtualbox 901 | default: Box Version: >= 0 902 | ==> default: Loading metadata for box 'http://www.example.com/vagrant/devops/' 903 | default: URL: http://www.example.com/vagrant/devops/ 904 | ==> default: Adding box 'devops' (v0.1.1) for provider: virtualbox 905 | default: Downloading: http://www.example.com/vagrant/devops/boxes/devops_0.1.1.box 906 | default: Calculating and comparing box checksum... 907 | ==> default: Successfully added box 'devops' (v0.1.1) for 'virtualbox'! 908 | ==> default: Importing base box 'devops'... 909 | ==> default: Matching MAC address for NAT networking... 910 | ==> default: Checking if box 'devops' is up to date... 911 | ==> default: Setting the name of the VM: VagrantTest_default_1406660957112_34972 912 | ==> default: Clearing any previously set network interfaces... 913 | ==> default: Preparing network interfaces based on configuration... 914 | default: Adapter 1: nat 915 | ==> default: Forwarding ports... 916 | default: 22 => 2222 (adapter 1) 917 | ==> default: Booting VM... 918 | ==> default: Waiting for machine to boot. This may take a few minutes... 919 | default: SSH address: 127.0.0.1:2222 920 | default: SSH username: vagrant 921 | default: SSH auth method: private key 922 | default: Warning: Connection timeout. Retrying... 923 | default: Warning: Remote connection disconnect. Retrying... 924 | ==> default: Machine booted and ready! 925 | ==> default: Checking for guest additions in VM... 926 | ==> default: Mounting shared folders... 927 | default: /vagrant => ~/VagrantTest 928 | ``` 929 | 930 | **And here it is: Your own vagrant cloud!** 931 | 932 | ## Epilog 933 | 934 | * Please trigger fixes to this tutorial as an issue to this repo here on github 935 | * For questions you can find me in the [vagrant google group](https://groups.google.com/forum/#!usersettings/general) 936 | * Thanks for reading, I hope this helps boosting your environment! 937 | 938 | ## Further reading 939 | 940 | * [Sets up a nginx server which hosts vagrant boxes](https://github.com/ebmeierj/local_vagrant_box_hosting) by [ebmeierj](https://github.com/ebmeierj) 941 | --------------------------------------------------------------------------------