├── test ├── output │ └── .gitignore ├── fig.yml └── test_fig2coreos.rb ├── Gemfile ├── Gemfile.lock ├── .gitignore ├── Rakefile ├── fig2coreos.gemspec ├── LICENSE ├── bin └── fig2coreos ├── README.md └── lib └── fig2coreos.rb /test/output/.gitignore: -------------------------------------------------------------------------------- 1 | media -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | -------------------------------------------------------------------------------- /Gemfile.lock: -------------------------------------------------------------------------------- 1 | GEM 2 | remote: https://rubygems.org/ 3 | specs: 4 | 5 | PLATFORMS 6 | ruby 7 | 8 | DEPENDENCIES 9 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | test/output/media 2 | test/output/.vagrant 3 | test/output/Vagrantfile 4 | test/output/setup-coreos.sh 5 | .DS_Store 6 | *.gem 7 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | require 'rake/testtask' 2 | 3 | Rake::TestTask.new do |t| 4 | t.libs << 'test' 5 | end 6 | 7 | desc "Run tests" 8 | task :default => :test 9 | -------------------------------------------------------------------------------- /fig2coreos.gemspec: -------------------------------------------------------------------------------- 1 | Gem::Specification.new do |s| 2 | s.name = 'fig2coreos' 3 | s.version = '0.1.6' 4 | s.date = '2014-04-24' 5 | s.summary = "Convert fig to coreos formatted configuration files" 6 | s.description = "Convert fig to coreos formatted configuration files" 7 | s.authors = ["Lucas Carlson"] 8 | s.email = 'lucas@rufy.com' 9 | s.files = ["lib/fig2coreos.rb","bin/fig2coreos"] 10 | s.homepage = 'http://github.com/CenturyLinkLabs/fig2coreos' 11 | s.license = 'MIT' 12 | s.executables << 'fig2coreos' 13 | end 14 | -------------------------------------------------------------------------------- /test/fig.yml: -------------------------------------------------------------------------------- 1 | serf: 2 | image: ctlc/serf 3 | ports: 4 | - 7373 5 | - 7946 6 | lb: 7 | image: ctlc/haproxy 8 | ports: 9 | - 80:80 10 | links: 11 | - serf 12 | environment: 13 | HAPROXY_PASSWORD: 38uf8hhf38h 14 | web: 15 | image: ctlc/wordpress 16 | ports: 17 | - 80 18 | environment: 19 | DB_USER: root 20 | DB_PASSWORD: 38uf8hhf38h 21 | links: 22 | - serf 23 | - db 24 | db: 25 | image: orchardup/mysql 26 | links: 27 | - serf 28 | ports: 29 | - 3306 30 | environment: 31 | MYSQL_DATABASE: wordpress 32 | MYSQL_ROOT_PASSWORD: 38uf8hhf38h 33 | -------------------------------------------------------------------------------- /test/test_fig2coreos.rb: -------------------------------------------------------------------------------- 1 | require 'test/unit' 2 | require 'fig2coreos' 3 | 4 | class Fig2CoreOSTest < Test::Unit::TestCase 5 | def setup(options={}) 6 | Fig2CoreOS.convert( 7 | "test-app", 8 | File.join(File.expand_path(File.dirname(__FILE__)), "fig.yml"), 9 | File.join(File.expand_path(File.dirname(__FILE__)), "output"), 10 | options 11 | ) 12 | end 13 | 14 | def test_fig_to_coreos_vagrant 15 | setup(type: 'vagrant') 16 | assert_equal 1, 1 17 | end 18 | 19 | def test_fig_to_coreos_systemd 20 | setup(type: 'systemd') 21 | assert_equal 1, 1 22 | end 23 | end 24 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 CenturyLink Labs 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /bin/fig2coreos: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require 'optparse' 3 | require File.expand_path(File.join(File.dirname(__FILE__), "..", "lib", "fig2coreos")) 4 | 5 | FIG2COREOS_BANNER = "Usage: fig2coreos [options] APP_NAME FIG_YAML OUTPUT_DIRECTORY" 6 | 7 | options = {type: "fleet"} 8 | 9 | opt_parser = OptionParser.new do |opts| 10 | opts.banner = FIG2COREOS_BANNER 11 | 12 | opts.on("-t", "--type TYPE", "Output type: fleet (default) or vagrant (generate a Vagrantfile)") do |type| 13 | options[:type] = type 14 | end 15 | 16 | opts.on( '-h', '--help', 'Display this screen' ) do 17 | puts opts 18 | exit 19 | end 20 | end 21 | 22 | opt_parser.parse! 23 | 24 | if ARGV[0].nil? 25 | puts opt_parser 26 | exit -1 27 | end 28 | 29 | if !File.file?(File.expand_path(ARGV[1].to_s)) 30 | puts opt_parser 31 | exit -1 32 | end 33 | 34 | if !ARGV[2] || !File.directory?(File.expand_path(ARGV[2].to_s)) 35 | if !FileUtils.mkdir_p(File.join(ARGV[2].to_s, "media", "state", "units")) 36 | puts opt_parser 37 | exit -1 38 | end 39 | end 40 | 41 | if options[:type] == "vagrant" && Gem::Version.new(`vagrant --version`.split.last) < Gem::Version.new("1.4.3") 42 | puts "[ERROR] Your version of vagrant (#{vagrant_version}) is out of date, please upgrade to at least 1.4.3 at http://www.vagrantup.com/downloads.html or change the --type" 43 | exit -1 44 | end 45 | 46 | puts Fig2CoreOS.convert(ARGV[0], ARGV[1], ARGV[2], options) 47 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## NOTE 2 | 3 | This repo is no longer being maintained. Users are welcome to fork it, but we make no warranty of its functionality. 4 | 5 | fig2coreos 6 | ========== 7 | Convert fig.yml to CoreOS formatted systemd configuration files 8 | 9 | Install 10 | ------- 11 | 12 | $ sudo gem install fig2coreos 13 | $ fig2coreos 14 | Usage: fig2coreos [options] APP_NAME FIG_YAML OUTPUT_DIRECTORY 15 | -t, --type TYPE Output type: fleet (default) or vagrant (generate a Vagrantfile) 16 | -h, --help Display this screen 17 | 18 | Usage 19 | ----- 20 | 21 | To convert any fig.yml into a set of CoreOS systemd configuration files, just point the command to your fig.yml file and a directory to put your CoreOS files in: 22 | 23 | $ fig2coreos app-name fig.yml coreos-dir 24 | $ cd coreos-dir 25 | $ fleetctl start * 26 | 27 | To see how to setup fleetctl and CoreOS, here is an end-to-end tutorial: https://labs.ctl.io/building-your-first-app-on-coreos/ 28 | 29 | Use with Vagrant 30 | ---------------- 31 | 32 | If you select vagrant output type, fig2coreos will assume you are running this locally with vagrant and VirtualBox installed, so it will create a Vagrantfile which you can run vagrant up in and have a CoreOS running locally with the equivalent of your fig.yml running in it. 33 | 34 | $ cd coreos-dir 35 | $ vagrant up 36 | 37 | The fig2coreos command auto-generates etcd discovery registration and fleet integration as well, so you can inspect your app easily. 38 | 39 | $ vagrant ssh 40 | $ fleetctl list-units 41 | 42 | -------------------------------------------------------------------------------- /lib/fig2coreos.rb: -------------------------------------------------------------------------------- 1 | require 'yaml' 2 | require 'fileutils' 3 | 4 | class Fig2CoreOS 5 | def self.convert(app_name, fig_file, output_dir, options={}) 6 | Fig2CoreOS.new(app_name, fig_file, output_dir, options) 7 | end 8 | 9 | def initialize(app_name, fig_file, output_dir, options={}) 10 | @app_name = app_name 11 | @fig = YAML.load_file(fig_file.to_s) 12 | @output_dir = File.expand_path(output_dir.to_s) 13 | @vagrant = (options[:type] == "vagrant") 14 | 15 | # clean and setup directory structure 16 | FileUtils.rm_rf(Dir[File.join(@output_dir, "*.service")]) 17 | FileUtils.rm_rf(File.join(@output_dir, "media")) 18 | FileUtils.rm_rf(File.join(@output_dir, "setup-coreos.sh")) 19 | FileUtils.rm_rf(File.join(@output_dir, "Vagrantfile")) 20 | 21 | if @vagrant 22 | FileUtils.mkdir_p(File.join(@output_dir, "media", "state", "units")) 23 | create_vagrant_file 24 | end 25 | 26 | create_service_files 27 | exit 0 28 | end 29 | 30 | def create_service_files 31 | @fig.each do |service_name, service| 32 | image = service["image"] 33 | ports = (service["ports"] || []).map{|port| "-p #{port}"} 34 | volumes = (service["volumes"] || []).map{|volume| "-v #{volume}"} 35 | links = (service["links"] || []).map{|link| "--link #{link}_1:#{link}_1"} 36 | envs = (service["environment"] || []).map do |env_name, env_value| 37 | "-e \"#{env_name}=#{env_value}\"" 38 | end 39 | 40 | after = if service["links"] 41 | "#{service["links"].last}.1" 42 | else 43 | "docker" 44 | end 45 | 46 | if @vagrant 47 | base_path = File.join(@output_dir, "media", "state", "units") 48 | else 49 | base_path = @output_dir 50 | end 51 | 52 | File.open(File.join(base_path, "#{service_name}.1.service") , "w") do |file| 53 | file << <<-eof 54 | [Unit] 55 | Description=Run #{service_name}_1 56 | After=#{after}.service 57 | Requires=#{after}.service 58 | 59 | [Service] 60 | Restart=always 61 | RestartSec=10s 62 | ExecStartPre=/usr/bin/docker ps -a -q | xargs docker rm 63 | ExecStart=/usr/bin/docker run -rm -name #{service_name}_1 #{volumes.join(" ")} #{links.join(" ")} #{envs.join(" ")} #{ports.join(" ")} #{image} 64 | ExecStartPost=/usr/bin/docker ps -a -q | xargs docker rm 65 | ExecStop=/usr/bin/docker kill #{service_name}_1 66 | ExecStopPost=/usr/bin/docker ps -a -q | xargs docker rm 67 | 68 | [Install] 69 | WantedBy=local.target 70 | eof 71 | end 72 | 73 | File.open(File.join(base_path, "#{service_name}-discovery.1.service"), "w") do |file| 74 | port = %{\\"port\\": #{service["ports"].first.to_s.split(":").first}, } if service["ports"].to_a.size > 0 75 | file << <<-eof 76 | [Unit] 77 | Description=Announce #{service_name}_1 78 | BindsTo=#{service_name}.1.service 79 | 80 | [Service] 81 | ExecStart=/bin/sh -c "while true; do etcdctl set /services/#{service_name}/#{service_name}_1 '{ \\"host\\": \\"%H\\", #{port}\\"version\\": \\"52c7248a14\\" }' --ttl 60;sleep 45;done" 82 | ExecStop=/usr/bin/etcdctl rm /services/#{service_name}/#{service_name}_1 83 | 84 | [X-Fleet] 85 | X-ConditionMachineOf=#{service_name}.1.service 86 | eof 87 | end 88 | end 89 | end 90 | 91 | def create_vagrant_file 92 | File.open(File.join(@output_dir, "setup-coreos.sh"), "w") do |file| 93 | file << <<-eof 94 | # Switch to root for setting up systemd 95 | sudo -i 96 | 97 | # Clear old containers 98 | systemctl stop local-enable.service 99 | systemctl stop etcd-cluster.service 100 | /usr/bin/docker ps -a -q | xargs docker kill 101 | /usr/bin/docker ps -a -q | xargs docker rm 102 | 103 | # Copy the services into place 104 | eof 105 | @fig.each do |service_name, service| 106 | file << "cp " + File.join("share", "media", "state", "units", "#{service_name}.1.service") + " /media/state/units/#{service_name}.1.service\n" 107 | file << "cp " + File.join("share", "media", "state", "units", "#{service_name}-discovery.1.service") + " /media/state/units/#{service_name}-discovery.1.service\n\n" 108 | end 109 | 110 | file << <<-eof 111 | 112 | # Fix etcd-cluster setup 113 | HOSTNAME=$( /var/run/etcd/MY_IP 122 | 123 | # Replace the existing line: 124 | sed -i -e "s@ExecStart=.*@ExecStart=/usr/bin/etcd -s 192.168.10.2:7001 -sl 0.0.0.0 -cl 0.0.0.0 -c 192.168.10.2:4001 ${CLUSTER_OPT} -d /var/run/etcd/${HOSTNAME} -n $HOSTNAME@" /media/state/units/etcd-cluster.service 125 | 126 | # Start containers and fleet 127 | systemctl daemon-reload 128 | systemctl start etcd-cluster.service 129 | systemctl start local-enable.service 130 | systemctl start fleet 131 | 132 | etcdctl mkdir /services 133 | eof 134 | @fig.each do |service_name, service| 135 | file << "etcdctl mkdir /services/#{service_name}\n" 136 | end 137 | file << "cd /media/state/units; for service in *.service; do fleetctl start $service; done\n" 138 | file << "sleep 3; /usr/bin/docker ps -a -q | xargs docker rm\n" 139 | file << "sleep 3; /usr/bin/docker ps -a -q | xargs docker rm\n" 140 | file << "echo 'SUCCESS'\n" 141 | file << "exit 0\n" 142 | 143 | file.chmod(0755) 144 | end 145 | 146 | File.open(File.join(@output_dir, "Vagrantfile"), "w") do |file| 147 | file << <<-eof 148 | # -*- mode: ruby -*- 149 | # vi: set ft=ruby : 150 | 151 | $expose_port = 8080 152 | 153 | Vagrant.configure("2") do |config| 154 | config.vm.box = "coreos" 155 | config.vm.box_url = "http://storage.core-os.net/coreos/amd64-generic/dev-channel/coreos_production_vagrant.box" 156 | config.vm.hostname = "coreos-#{@app_name}" 157 | 158 | config.vm.provider :virtualbox do |vb, override| 159 | vb.name = "vagrant-coreos-docker-converted-from-fig-#{@app_name}" 160 | # Fix docker not being able to resolve private registry in VirtualBox 161 | vb.customize ["modifyvm", :id, "--natdnshostresolver1", "on"] 162 | vb.customize ["modifyvm", :id, "--natdnsproxy1", "on"] 163 | end 164 | 165 | # Share this folder so provisioner can access Dockerfile an apache.service 166 | # This is from coreos/coreos-vagrant, but won't work on Windows hosts 167 | config.vm.network "private_network", ip: "192.168.10.2" 168 | config.vm.synced_folder ".", "/home/core/share", id: "core", :nfs => true, :mount_options => ['nolock,vers=3,udp'] 169 | 170 | # Forward port 80 from coreos (which is forwarding port 80 of the container) 171 | config.vm.network :forwarded_port, guest: 80, host: $expose_port, host_ip: "127.0.0.1" 172 | 173 | config.vm.provider :vmware_fusion do |vb, override| 174 | override.vm.box_url = "http://storage.core-os.net/coreos/amd64-generic/dev-channel/coreos_production_vagrant_vmware_fusion.box" 175 | end 176 | 177 | # plugin conflict 178 | if Vagrant.has_plugin?("vagrant-vbguest") then 179 | config.vbguest.auto_update = false 180 | end 181 | 182 | # Provision 183 | config.vm.provision "shell", :path => "setup-coreos.sh" 184 | end 185 | eof 186 | end 187 | 188 | if File.directory?(File.join(@output_dir, ".vagrant")) 189 | puts "[SUCCESS] Try this: cd #{@output_dir} && vagrant reload --provision" 190 | else 191 | puts "[SUCCESS] Try this: cd #{@output_dir} && vagrant up" 192 | end 193 | end 194 | end 195 | --------------------------------------------------------------------------------