├── .gitignore ├── config_sample.rb ├── lib └── vagrant_rancheros_guest_plugin.rb ├── README.md ├── Vagrantfile └── LICENSE /.gitignore: -------------------------------------------------------------------------------- 1 | *.box 2 | .vagrant 3 | *.swp 4 | .vagrant_plugin_check 5 | config.rb 6 | -------------------------------------------------------------------------------- /config_sample.rb: -------------------------------------------------------------------------------- 1 | # Box configuration details 2 | # $box = "rancherio/rancheros" 3 | # $box_version = '>=0.4.1' 4 | 5 | # Official CoreOS channel. Either alpha, beta or stable 6 | # $update_channel = "alpha" 7 | # URL to pull CoreOS image from 8 | # $box_url = "https://storage.googleapis.com/%s.release.core-os.net/amd64-usr/current/coreos_production_vagrant.json" % [$update_channel] 9 | 10 | # Tag of the rancher/server image to run 11 | # $rancher_version = 'latest' 12 | 13 | # IP prefix to use when assigning box ip addresses 14 | # $ip_prefix = '192.168.33' 15 | 16 | # Enable syncing of the current directory to the /vagrant path on the guest 17 | # $disable_folder_sync = false 18 | 19 | # Boxes to create in the vagrant environment 20 | $boxes = [ 21 | { 22 | "name" => "rancher-server", 23 | "role" => "server", 24 | "memory" => "1536", 25 | "labels" => [], 26 | }, 27 | { 28 | "name" => "rancher-agent", 29 | "count" => 2, 30 | "memory" => "512", 31 | "labels" => ["type=general"] 32 | }, 33 | ] 34 | -------------------------------------------------------------------------------- /lib/vagrant_rancheros_guest_plugin.rb: -------------------------------------------------------------------------------- 1 | require 'ipaddr' 2 | 3 | ## Hacking this until we get a real plugin 4 | 5 | # Borrowing from http://stackoverflow.com/questions/1825928/netmask-to-cidr-in-ruby 6 | IPAddr.class_eval do 7 | def to_cidr 8 | self.to_i.to_s(2).count("1") 9 | end 10 | end 11 | 12 | module VagrantPlugins 13 | module GuestLinux 14 | class Plugin < Vagrant.plugin("2") 15 | guest_capability("linux", "change_host_name") do 16 | Cap::ChangeHostName 17 | end 18 | 19 | guest_capability("linux", "configure_networks") do 20 | Cap::ConfigureNetworks 21 | end 22 | end 23 | end 24 | end 25 | 26 | module VagrantPlugins 27 | module GuestLinux 28 | module Cap 29 | class ConfigureNetworks 30 | def self.configure_networks(machine, networks) 31 | machine.communicate.tap do |comm| 32 | interfaces = [] 33 | comm.sudo("ip link show|grep eth[1-9]|awk '{print $2}'|sed -e 's/:$//'") do |_, result| 34 | interfaces = result.split("\n") 35 | end 36 | 37 | networks.each do |network| 38 | iface = interfaces[network[:interface].to_i - 1] 39 | 40 | if network[:type] == :static 41 | cidr = IPAddr.new(network[:netmask]).to_cidr 42 | comm.sudo("ros config set rancher.network.interfaces.#{iface}.address #{network[:ip]}/#{cidr}") 43 | comm.sudo("ros config set rancher.network.interfaces.#{iface}.match #{iface}") 44 | comm.sudo("ros config set rancher.network.interfaces.#{iface}.dhcp false") 45 | else 46 | comm.sudo("ros config set rancher.network.interfaces.#{iface}.dhcp true") 47 | end 48 | end 49 | 50 | comm.sudo("system-docker restart network") 51 | end 52 | end 53 | end 54 | end 55 | end 56 | end 57 | 58 | module VagrantPlugins 59 | module GuestLinux 60 | module Cap 61 | class ChangeHostName 62 | def self.change_host_name(machine, name) 63 | machine.communicate.tap do |comm| 64 | if !comm.test("sudo hostname --fqdn | grep '#{name}'") 65 | comm.sudo("echo '#cloud-config' > /var/lib/rancher/conf/cloud-config.yml") 66 | comm.sudo("echo 'hostname: #{name}' >> /var/lib/rancher/conf/cloud-config.yml") 67 | comm.sudo("ros config set cloud_init.datasources '[file:/var/lib/rancher/conf/cloud-config.yml]'") 68 | comm.sudo("system-docker restart cloud-init") 69 | comm.sudo("cloud-init -execute") 70 | end 71 | end 72 | end 73 | end 74 | end 75 | end 76 | end 77 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # rancher-vagrant 2 | 3 | Extensible and automated local Rancher environments using Vagrant 4 | 5 | ## Usage 6 | 7 | ``` 8 | git clone https://github.com/nextrevision/rancher-vagrant 9 | cd rancher-vagrant 10 | vagrant up 11 | ``` 12 | 13 | Assuming the defaults, browse to [http://192.168.33.11:8080](http://192.168.33.11:8080) to access your Rancher server. 14 | 15 | ## Configuration 16 | 17 | You can configure the environment by setting up a custom `config.rb` file in the root of the repository. The available configuration options to you are displayed below: 18 | 19 | | Item | Type | Required | Default | Description | 20 | |------------------------|----------|----------|---------------------|--------------------------------------------------------------------------| 21 | | `$box` | *string* | false | rancherio/rancheros | Vagrant box to use for the environment | 22 | | `$box_url` | *string* | false | `nil` | URL to download the box | 23 | | `$box_version` | *string* | false | `nil` | Version of the box to download | 24 | | `$disable_folder_sync` | *bool* | false | `true` | Disable syncing the current working directory to "/vagrant" on the guest | 25 | | `$ip_prefix` | *string* | false | 192.168.33 | Prefix for all IPs assigned to the guests | 26 | | `$rancher_version` | *string* | false | latest | Version of Rancher to deploy | 27 | | `$boxes` | *array* | true | `[]` | List of boxes (see [Boxes](#boxes) table below) | 28 | 29 | ### Boxes 30 | 31 | | Item | Type | Required | Default | Description | 32 | |-----------------|----------|----------|------------------------------------------------|---------------------------------------------------------------------------------------| 33 | | `name` | *string* | true | | Base name of the box | 34 | | `count` | *string* | false | 1 | Number of guests to create with this config | 35 | | `role` | *string* | false | agent | Role of the box (either "server" or "agent", at least one "server" must be specified) | 36 | | `memory` | *string* | false | 512 | Amount of memory to dedicate to the box (for RancherOS, at least 512 is recommended) | 37 | | `ip` | *string* | false | | IP address to assign to the box (typically best to leave this alone) | 38 | | `install_agent` | *bool* | false | `true` if role==agent, `false` if role==server | Whether or not to run the Rancher agent on the guest | 39 | | `project` | *string* | false | `nil` | Name of the Rancher project or environment to place the box in | 40 | | `project_type` | *string* | false | cattle | Type of project to for the Rancher environment (cattle, swarm, kubernetes) | 41 | | `server` | *string* | false | | Hostname or IP address of the Rancher server to join | 42 | 43 | ## Example 44 | 45 | See [config_sample.rb](config_sample.rb) 46 | -------------------------------------------------------------------------------- /Vagrantfile: -------------------------------------------------------------------------------- 1 | # -*- mode: ruby -*- 2 | # vi: set ft=ruby : 3 | 4 | # set vagrant defaults 5 | $boxes = [] 6 | $box = 'rancherio/rancheros' 7 | $box_url = nil 8 | $box_version = nil 9 | $rancher_version = 'latest' 10 | $ip_prefix = '192.168.33' 11 | $disable_folder_sync = true 12 | 13 | # install the vagrant-rancher provisioner plugin if 14 | # it is not already installed 15 | unless Vagrant.has_plugin?('vagrant-rancher') 16 | puts 'vagrant-rancher plugin not found, installing...' 17 | `vagrant plugin install vagrant-rancher` 18 | abort 'vagrant-rancher plugin installed, but you need to rerun the vagrant command' 19 | end 20 | 21 | # validate that at least one box is setup as a rancher server 22 | def parse_boxes(boxes) 23 | servers = [] 24 | agents = [] 25 | boxes.each do |box| 26 | abort 'Must specify name for box' if box['name'].nil? 27 | if $box == 'rancherio/rancheros' 28 | if !box['memory'].nil? and box['memory'].to_i < 512 29 | puts 'WARNING: Running RancherOS on less than 512MB of RAM has been known to cause issues.' 30 | end 31 | end 32 | if !box['role'].nil? and box['role'] == 'server' 33 | servers.push(box) 34 | else 35 | agents.push(box) 36 | end 37 | end 38 | abort 'At least one server must be specified in the $boxes config' if servers.empty? 39 | return servers + agents 40 | end 41 | 42 | # loop through boxes and return the ip address of the 43 | # first server box found 44 | def get_server_ip(boxes, hostname='') 45 | default_server_ip = nil 46 | boxes.each_with_index do |box, i| 47 | if not box['role'].nil? and box['role'] == 'server' 48 | ip = box['ip'] ? box['ip'] : "#{$ip_prefix}.#{i+1}#{i+1}" 49 | default_server_ip = ip if default_server_ip.nil? 50 | if hostname == "#{box['name']}-%02d" % i 51 | return ip 52 | end 53 | end 54 | end 55 | return default_server_ip 56 | end 57 | 58 | # if there is a user-supplied config.rb use that otherwise 59 | # default to the config_sample.rb 60 | if File.exist?(File.join(File.dirname(__FILE__), 'config.rb')) 61 | CONFIG = File.join(File.dirname(__FILE__), 'config.rb') 62 | else 63 | CONFIG = File.join(File.dirname(__FILE__), 'config_sample.rb') 64 | end 65 | 66 | # load the set config file 67 | if File.exist?(CONFIG) 68 | require CONFIG 69 | end 70 | 71 | # require some capability overrides if the box is rancheros 72 | if $box == 'rancherio/rancheros' 73 | require_relative 'lib/vagrant_rancheros_guest_plugin.rb' 74 | end 75 | 76 | # get a list of sorted boxes (starting with server) 77 | $sorted_boxes = parse_boxes $boxes 78 | 79 | # determine the default server ip 80 | $default_server_ip = get_server_ip $sorted_boxes 81 | 82 | Vagrant.configure(2) do |config| 83 | # Try to use a custom CoreOS box 84 | if $update_channel != nil 85 | config.vm.box = "coreos-%s" % $update_channel 86 | config.vm.box_url = $box_url unless $box_url.nil? 87 | config.vm.box_version = $box_version unless $box_version.nil? 88 | else 89 | # Default to RancherOS if nothing gets overriden 90 | config.vm.box = $box 91 | config.vm.box_url = $box_url unless $box_url.nil? 92 | config.vm.box_version = $box_version unless $box_version.nil? 93 | 94 | end 95 | 96 | if $disable_folder_sync 97 | config.vm.synced_folder '.', '/vagrant', disabled: true 98 | else 99 | # use rsync when box is rancheros 100 | # otherwise stick with the vagrant defaults 101 | if $box == 'rancherio/rancheros' 102 | config.vm.synced_folder ".", "/vagrant", type: "rsync", 103 | rsync__exclude: ".git/", 104 | rsync__args: ["--verbose", "--archive", "--delete", "--copy-links"], 105 | disabled: false 106 | else 107 | config.vm.synced_folder '.', '/vagrant', disabled: false 108 | end 109 | end 110 | 111 | $sorted_boxes.each_with_index do |box, box_index| 112 | # default to only one of each type of box 113 | count = box['count'] || 1 114 | 115 | # loop through the desired number of instances 116 | # for a given box 117 | (1..count).each do |i| 118 | 119 | # configure the hostname, ex. rancher-server-01 120 | hostname = "#{box['name']}-%02d" % i 121 | 122 | # configure node settings 123 | config.vm.define hostname do |node| 124 | # set the hostname 125 | node.vm.hostname = hostname 126 | 127 | # set the node ip address 128 | ip = box['ip'] ? box['ip'] : "#{$ip_prefix}.#{box_index+1}#{i}" 129 | node.vm.network 'private_network', ip: ip 130 | 131 | # override default memory allocation if set in config 132 | unless box['memory'].nil? 133 | node.vm.provider 'virtualbox' do |vb| 134 | vb.memory = box['memory'] 135 | end 136 | end 137 | 138 | if !box['role'].nil? and box['role'] == 'server' 139 | node.vm.provision :rancher do |rancher| 140 | rancher.role = 'server' 141 | rancher.hostname = ip 142 | rancher.version = $rancher_version 143 | rancher.deactivate = true 144 | rancher.install_agent = box['install_agent'] || false 145 | rancher.labels = box['labels'] unless box['labels'].nil? 146 | rancher.project = box['project'] unless box['project'].nil? 147 | rancher.project_type = box['project_type'] unless box['project_type'].nil? 148 | end 149 | else 150 | node.vm.provision :rancher do |rancher| 151 | rancher.role = 'agent' 152 | rancher.hostname = box['server'] || $default_server_ip 153 | rancher.install_agent = box['install_agent'] unless box['install_agent'].nil? 154 | rancher.labels = box['labels'] unless box['labels'].nil? 155 | rancher.project = box['project'] unless box['project'].nil? 156 | rancher.project_type = box['project_type'] unless box['project_type'].nil? 157 | end 158 | end 159 | end 160 | end 161 | end 162 | end 163 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | --------------------------------------------------------------------------------