187 lines
3.8 KiB
Crystal
187 lines
3.8 KiB
Crystal
require "colorize"
|
|
require "option_parser"
|
|
require "file_utils"
|
|
|
|
# Not currently used. Waiting for API updates.
|
|
#require "baguette-crystal-base"
|
|
|
|
# Prototyping for the next generation of baguette-crystal-base.
|
|
|
|
class Baguette::Configuration
|
|
property application_name : String
|
|
|
|
def initialize(@application_name)
|
|
end
|
|
end
|
|
|
|
module Baguette::Log
|
|
def info(text)
|
|
STDOUT << (":: ".colorize :blue) << (text.colorize :white) << "\n"
|
|
end
|
|
|
|
def warn(text)
|
|
STDERR << (":: ".colorize :yellow) << (text.colorize :yellow) << "\n"
|
|
end
|
|
|
|
def error(text)
|
|
STDERR << (":: ".colorize :red) << (text.colorize :red) << "\n"
|
|
end
|
|
end
|
|
|
|
module Baguette::Base
|
|
getter configuration : Configuration
|
|
|
|
include Baguette::Log
|
|
end
|
|
|
|
Colorize.on_tty_only!
|
|
|
|
# CAUTION
|
|
# backends: storage, ???
|
|
# rootfs templates
|
|
|
|
class RootFS::Context
|
|
include Baguette::Base
|
|
|
|
def initialize
|
|
@configuration = Baguette::Configuration.new "rootfs"
|
|
end
|
|
|
|
def run
|
|
options_parser = uninitialized OptionParser
|
|
|
|
commands = {
|
|
"new" => ->(args : Array(String)){
|
|
RootFS.new args[1], args[0]
|
|
},
|
|
"save" => ->(args : Array(String)){
|
|
RootFS.load(args[0]).save(args[1])
|
|
},
|
|
"restore" => ->(args : Array(String)){
|
|
RootFS.load(args[0]).restore(args[1])
|
|
},
|
|
# FIXME: This needs access to the list of commands.
|
|
"help" => ->(args : Array(String)){
|
|
STDOUT << options_parser << "\n"
|
|
}
|
|
}
|
|
|
|
command = uninitialized String
|
|
arguments = uninitialized Array(String)
|
|
|
|
options_parser = OptionParser.parse do |parser|
|
|
parser.banner = "usage: #{@configuration.application_name} <command> [options]"
|
|
|
|
parser.on "-c", "--color", "Forces the use of colors." do
|
|
Colorize.enabled = true
|
|
end
|
|
|
|
parser.on "-h", "--help", "Displays this help message." do
|
|
STDOUT << parser << "\n"
|
|
exit 0
|
|
end
|
|
|
|
# FIXME: This leads to unusual behaviors when using the CLI.
|
|
# Replace by dedicated executables.
|
|
parser.unknown_args do |args|
|
|
# args => Array(String)
|
|
|
|
raise "no command provided" if args.size < 1
|
|
|
|
command = args.shift
|
|
arguments = args
|
|
end
|
|
end
|
|
|
|
if proc = commands[command]?
|
|
proc.call(arguments)
|
|
else
|
|
raise "unrecognized command: #{command}"
|
|
end
|
|
rescue e
|
|
if message = e.message
|
|
error message
|
|
else
|
|
error e.to_s
|
|
end
|
|
end
|
|
end
|
|
|
|
class RootFS::RootFS
|
|
include Baguette::Log
|
|
|
|
getter directory : String
|
|
|
|
def initialize(@directory)
|
|
end
|
|
|
|
def self.new(directory, template : String)
|
|
instance = RootFS.allocate
|
|
instance.initialize directory
|
|
instance.create! template
|
|
instance
|
|
end
|
|
|
|
protected def execute(command : String)
|
|
info "Executing: #{command}"
|
|
r = Process.new(
|
|
"sh", ["-c", command],
|
|
input: Process::Redirect::Inherit,
|
|
output: Process::Redirect::Inherit,
|
|
error: Process::Redirect::Inherit
|
|
).wait
|
|
|
|
raise "Command returned #{r.exit_status}." unless r.success?
|
|
end
|
|
|
|
protected def create!(template : String)
|
|
# FIXME: Alternate backends.
|
|
# FIXME: template string parsing.
|
|
is_btrfs = true # FIXME: do the actual detection
|
|
|
|
unless is_btrfs
|
|
raise "can only create rootfs in a btrfs filesystem"
|
|
end
|
|
|
|
execute "btrfs subvolume create '#{@directory}'"
|
|
|
|
execute "debootstrap --arch amd64 '#{template}' '#{@directory}'"
|
|
end
|
|
|
|
def self.load(directory)
|
|
new directory
|
|
end
|
|
|
|
def delete
|
|
show!
|
|
puts "UNIMPLEMENTED: removing rootfs directory here"
|
|
end
|
|
|
|
def save(snapshot_directory)
|
|
execute "btrfs subvolume snapshot '#{@directory}' '#{snapshot_directory}'"
|
|
end
|
|
|
|
def restore(snapshot_directory)
|
|
unmount
|
|
execute "btrfs subvolume delete '#{@directory}'"
|
|
execute "btrfs subvolume snapshot '#{snapshot_directory}' '#{@directory}'"
|
|
end
|
|
|
|
def mount(directory = nil)
|
|
show!
|
|
puts "UNIMPLEMENTED: binding file systems here"
|
|
end
|
|
|
|
def unmount(directory = nil)
|
|
show!
|
|
puts "UNIMPLEMENTED: unbinding file systems here"
|
|
end
|
|
|
|
def show!
|
|
puts "#{@directory}"
|
|
end
|
|
end
|
|
|
|
RootFS::Context.new.run
|
|
|