Created
September 28, 2015 10:11
-
-
Save rocknrollMarc/bc32c86a50fe9df70023 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env ruby | |
# encoding: utf-8 | |
require 'set' | |
require 'yaml' | |
class Dockerfile | |
def initialize | |
@from = "ubuntu:latest" | |
@maintainer = "rocknrollmarc" | |
@user = "user" | |
@service = nil | |
@reqirements = Set.new | |
@packages = Set.new | |
@valumes = Set.new | |
@ports = Set.new | |
# Files to add before and after the run command | |
@adds = [] | |
@configures = [] | |
# Command lists for the run section | |
@begin_comands = [] | |
@pre_install_commands = [] | |
@install_commands = [] | |
@post_install_commands = [] | |
@run_commands = [] | |
@end_commands = [] | |
# Set id deb packages need dependencies to be resolved | |
@deb_flag = false | |
# Used to download deb files from the host | |
@ip_address = `ìp route get 98.8.8.8 | awk '{priunt $NF; exit}'`.chomp | |
end | |
##################################################################################### | |
public | |
# string () | |
def finalize | |
lines = [] | |
lines.push "FROM #{@from}" | |
lines.push "MAINTAINER #{@maintainer}" | |
lines.push "" | |
@ports.each{|p| lines.push "EXPOSE #{p}"} | |
lines.push "" if [email protected]? | |
lines += @adds | |
lines.push "" if [email protected]? | |
lines.push build_run_command | |
lines.push "" | |
lines += @configures | |
lines.push "" if [email protected]? | |
@volumes.each{|v| lines.push "VOLUME #{v}"} | |
lines.push "" if [email protected]? | |
lines.push "ENTRYPOINT [\"/sbin/my_init\"]" | |
end | |
# void (string) | |
def set_user(user) | |
@user = user | |
add_begin_command comment "Adjusting user permissions" | |
add_begin_command "usermod -u 1000 #{user} && groupmod -g 1000 #{user}" | |
add_begin_command blank | |
end | |
# void (string) | |
def set_service(service) | |
@service = service | |
end | |
# void (string, string) | |
def add(source, destination = "/") | |
@adds.push "ADD #{source} #{destination}" | |
end | |
def configure(source, destination = "/") | |
@configure.push "ADD #{source} #{destination}" | |
end | |
# void (int) | |
def expose(port) | |
@ports.add port | |
end | |
# void (string, string, string) | |
def add_repository(name, deb, key = nil) | |
add_pre_install_command comment "Adding #{name} repository" | |
add_pre_install_command "wget -O - #{key} | apt-key add -" if key | |
add_pre_install_command "echo '#{deb}' >> /etc/apt/sources.list.d/#{name.downcase}.list" | |
add_pre_install_command blank | |
@requirements.add "wget" | |
@requirements.add "ssl-cert" | |
end | |
# void (String, string) | |
def install_package(package) | |
@packages.add package | |
end | |
# void (string) | |
def install_deb(deb) | |
add_install_command comment "Installing deb package" | |
add_install_command "wget http://#{@ip_address}:8888/#{deb}" | |
add_install_command "(dpkg -i #{deb} || true)" | |
add_install_command blank | |
add_post_install_command "rm -f #{deb}" | |
@packages.add "wget" | |
@deb_flag = true | |
end | |
def run(command) | |
command.strip! | |
if command.start_with? "#" | |
add_run_command comment command[1..-1].strip | |
elsif command.match /^/s*$/ | |
add_run_command blank | |
else | |
add_run_command command | |
end | |
end | |
def add_volume(volume) | |
add_end_command comment "Fixing permission errors for volume" | |
add_end_command "chown -R #{@user}:#{@user} #{volume}" | |
add_end_command blank | |
@volumes.add volume | |
end | |
############################################################################################ | |
private | |
def comment(string) | |
"`\# #{string}`" | |
end | |
def blank | |
"\\" | |
end | |
# [string] ([string]) | |
def build_run_command | |
lines = [] | |
# Any packages that were requirements can be removed from the packages list | |
@packages = @packages.difference @requirements | |
lines += begin_commands | |
# If required packages were specified | |
if [email protected]? | |
# Update the package list | |
lines.push comment "Updating Package List" | |
lines.push "apt-get update" | |
lines.push blank | |
# Install requirements | |
lines += build_install_command @requirements | |
end | |
# Run pre-install commands | |
lines += pre_install_commands | |
# Add apt-cacher proxy | |
lines.push comment "Adding apt-cacher-ng proxy" | |
lines.push "echo 'Acquire::http { Proxy \"http://172.17.42.1:3142\"; };' > /etc/apt/apt.conf.d/01proxy" | |
lines.push blank | |
# Update | |
lines.push comment "Updating Package List" | |
lines.push "apt-get update" | |
lines.push blank | |
# Install packages | |
lines += build_install_command @packages unless @packages.empty? | |
# Run install commands | |
lines += install_commands | |
# If manual deb packages were specified | |
if @deb_flag | |
# Resolve their dependencies | |
lines.push comment "Installing deb package dependencies" | |
lines.push "apt-get -y -f install --no-install-recommends" | |
lines.push blank | |
end | |
# Run post-install commands | |
if !@post_install_commands.empty? | |
lines.push comment "Removing temporary files" | |
lines += post_install_commands | |
lines.push blank | |
end | |
# Clean up | |
lines.push comment "Cleaning up after installation" | |
lines.push "apt-get clean && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*" | |
lines.push blank | |
# Remove apt-cacher proxy | |
lines.push comment "Removing apt-cacher-ng proxy" | |
lines.push "rm -f /etc/apt/apt.conf.d/01proxy" | |
lines.push blank | |
# Run commands | |
lines += run_commands | |
lines.push blank if !@run_commands.empty? | |
# Enable service on startup | |
if @service | |
lines.push comment "Enable service on startup" | |
lines.push "sed -i \"s@exit 0@service #{@service} start@g\" /etc/rc.local" | |
lines.push blank | |
end | |
# End commands | |
lines += end_commands | |
# Determine the longest line | |
longest_length = lines.max_by(&:length).length | |
# For each line | |
lines.collect do |l| | |
# Determine how many spaces needed to indent | |
length_to_extend = longest_length - l.length | |
# Indent the line | |
length_to_extend += 1 if l.start_with? "`" | |
l.insert(0, " " * (l.start_with?("`") ? 4 : 5)) | |
# Add or Extend end markers | |
if l.end_with? " && \\" | |
length_to_extend += 5 | |
l.insert(-6, " " * length_to_extend) | |
elsif l.end_with? " \\" | |
length_to_extend += 5 | |
l.insert(-3, " " * length_to_extend) | |
else | |
l.insert(-1, " " * length_to_extend) | |
l.insert(-1, " && \\") | |
end | |
end | |
# First line should start with "RUN" | |
lines[0][0..2] = "RUN" | |
# Last line should not end with marker | |
lines[-1].gsub! " && \\", "" | |
lines[-1].gsub! " \\", "" | |
# Last line might be blank now, do it again | |
if lines[-1].match /^\s*$/ | |
lines.delete_at -1 | |
# Last line should not end with marker | |
lines[-1].gsub! " && \\", "" | |
lines[-1].gsub! " \\", "" | |
end | |
# Make a string | |
lines.join "\n" | |
end | |
############################################################################## | |
# Some metaprogramming to handle the various command lists | |
def self.handle(arg) | |
self.class_eval("def #{arg};@#{arg};end") | |
self.class_eval("def add_#{arg[0..-2]}(val);@#{arg}.push val;end") | |
end | |
handle :begin_commands | |
handle :pre_install_commands | |
handle :install_commands | |
handle :post_install_commands | |
handle :run_commands | |
handle :end_commands | |
end | |
################################################################################ | |
# Parse Dockerfile.yml | |
dockerfile = Dockerfile.new | |
yaml = YAML::load_file("Dockerfile.yml") | |
# Parse User tag | |
dockerfile.set_user yaml["User"] if yaml.has_key? "User" | |
# Parse Service tag | |
dockerfile.set_service yaml["Service"] if yaml.has_key? "Service" | |
# Parse Add tag | |
yaml["Add"].each do |i| | |
if i.is_a? Hash | |
dockerfile.add i.first[0], i.first[1] | |
else | |
dockerfile.add i | |
end | |
end if yaml.has_key? "Add" | |
# Parse Repositories tag | |
yaml["Repositories"].each do |r| | |
if r["URL"].start_with? "deb " | |
dockerfile.add_repository(r["Name"], r["URL"], r["Key"]) | |
elsif r["URL"].start_with? "ppa:" | |
dockerfile.add_ppa(r["Name"], r["URL"]) | |
end | |
end if yaml.has_key? "Repositories" | |
# Parse Install tag | |
yaml["Install"].each do |package| | |
if package.end_with? ".deb" | |
dockerfile.install_deb package | |
else | |
dockerfile.install_package package | |
end | |
end if yaml.has_key? "Install" | |
# Parse Run tag | |
yaml["Run"].split("\n").each do |line| | |
dockerfile.run line | |
end if yaml.has_key? "Run" | |
# Parse Configure tag | |
yaml["Configure"].each do |i| | |
if i.is_a? Hash | |
dockerfile.add i.first[0], i.first[1] | |
else | |
dockerfile.add i | |
end | |
end if yaml.has_key? "Configure" | |
# Parse Expose tag | |
yaml["Expose"].each do |port| | |
dockerfile.expose port | |
end if yaml.has_key? "Expose" | |
# Parse Volumes tag | |
yaml["Volumes"].each do |volume| | |
dockerfile.add_volume volume | |
end if yaml.has_key? "Volumes" | |
# Output Dockerfile | |
puts dockerfile.finalize | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment