commit 92faeb2c1b01e2f5f8bb8a9915209efc92ffa5d4 Author: Glenn Date: Sat Jul 9 16:19:44 2022 +0200 Initial import diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..3933460 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +lib +bin diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..c0a6cfa --- /dev/null +++ b/Dockerfile @@ -0,0 +1,37 @@ + +## Stage 1 : build binary +FROM crystallang/crystal:1.4.1 AS builder + +COPY . /app +WORKDIR /app +RUN shards build + +## Stage 2 : include binary into final image +FROM python:3.7-bullseye AS runner + +RUN apt-get update \ + && apt-get -yq install \ + libevent-core-2.1-7 \ + gettext \ + jq \ + python3-aodhclient \ + python3-barbicanclient \ + python3-ceilometerclient \ + python3-cinderclient \ + python3-cloudkittyclient \ + python3-designateclient \ + python3-gnocchiclient \ + python3-octaviaclient \ + python3-osc-placement \ + python3-openstackclient \ + python3-pankoclient \ + zip \ + && rm -rf /var/lib/apt/lists/* + +# RUN wget https://github.com/sapcc/cyclone/releases/download/v0.1.28/cyclone \ +# -O /usr/bin/cyclone \ +# && chmod +x /usr/bin/cyclone + +COPY --from=builder /app/bin/arkisto /usr/bin/arkisto + +CMD /bin/bash diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..10f496f --- /dev/null +++ b/Makefile @@ -0,0 +1,15 @@ + + +build: + shards build + +docker: docker-build docker-test docker-push + +docker-build: + docker build -t glenux/openstack-arkisto . + +docker-push: + docker push glenux/openstack-arkisto + +docker-test: + docker run glenux/openstack-arkisto arkisto --version diff --git a/README.md b/README.md new file mode 100644 index 0000000..84acc41 --- /dev/null +++ b/README.md @@ -0,0 +1,4 @@ +# Arkisto + +A simple tool to automate openstack snapshots + diff --git a/arkisto.sample.yml b/arkisto.sample.yml new file mode 100644 index 0000000..fc43634 --- /dev/null +++ b/arkisto.sample.yml @@ -0,0 +1,22 @@ +--- +version: "1" + +retention: + days: 8 + cycle: true + max_items: 2 + +targets: + - name: smart-app + volume_id: x + snapshot_prefix: y + - name: smart-db + volume_id: x + snapshot_prefix: y + - name: short-db + volume_id: x2 + snapshot_prefix: y + - name: collector-db + volume_id: x2 + snapshot_prefix: y +# diff --git a/shard.lock b/shard.lock new file mode 100644 index 0000000..514a6ac --- /dev/null +++ b/shard.lock @@ -0,0 +1,6 @@ +version: 2.0 +shards: + tablo: + git: https://github.com/hutou/tablo.git + version: 0.10.1 + diff --git a/shard.yml b/shard.yml new file mode 100644 index 0000000..0c75e9d --- /dev/null +++ b/shard.yml @@ -0,0 +1,25 @@ +--- +name: arkisto +version: 0.1.0 + +authors: + - Glenn Y. Rolland + +description: | + Simple and stupid utility to cycle openstack volume backups/snapshots + +targets: + arkisto: + main: src/arkistoctl/main.cr + +dependencies: + # Text tables + tablo: + github: hutou/tablo + + +# development_dependencies: +# webmock: +# github: manastech/webmock.cr + +license: MIT diff --git a/spec/data/openstack.sample.yml b/spec/data/openstack.sample.yml new file mode 100644 index 0000000..ea93645 --- /dev/null +++ b/spec/data/openstack.sample.yml @@ -0,0 +1,51 @@ +- Attached to: [] + ID: 7b715476-5397-48de-87ac-5a63170fff71 + Name: ovh-managed-kubernetes-e6j07m-pvc-fc264e28-94e3-43c5-afe1-3c99b6d95bfb + Size: 1 + Status: available +- Attached to: [] + ID: c9d05e3e-c668-47fd-8bd6-0cedc9dc02c5 + Name: ovh-managed-kubernetes-e6j07m-pvc-44bca100-2f8c-4be2-9491-d2254a42287f + Size: 2000 + Status: available +- Attached to: [] + ID: 8c87c196-1dd6-4190-bff5-00b543965093 + Name: ovh-managed-kubernetes-e6j07m-pvc-9fad19b4-b32c-452a-9893-524fcdc3a4b1 + Size: 50 + Status: available +- Attached to: + - attached_at: '2022-06-26T19:03:31.000000' + attachment_id: 503f8552-e2f9-4aff-a489-8382bbed3883 + device: /dev/sdb + host_name: null + id: c9b11fcb-a6a4-4387-b55b-34121f97dac4 + server_id: 7ee31946-16ab-4759-be33-6499a339f929 + volume_id: c9b11fcb-a6a4-4387-b55b-34121f97dac4 + ID: c9b11fcb-a6a4-4387-b55b-34121f97dac4 + Name: ovh-managed-kubernetes-e6j07m-pvc-13dcac68-71c9-4794-b0ab-d48e76d843ef + Size: 5800 + Status: in-use +- Attached to: + - attached_at: '2022-06-21T12:47:29.000000' + attachment_id: 569b7110-74e1-4f87-b92d-ab5e1a2a473c + device: /dev/sdb + host_name: null + id: de7b118f-413b-4962-a028-a742df4bc4fc + server_id: e1753464-9328-4eea-ae98-21c332e94dc5 + volume_id: de7b118f-413b-4962-a028-a742df4bc4fc + ID: de7b118f-413b-4962-a028-a742df4bc4fc + Name: ovh-managed-kubernetes-e6j07m-pvc-dfbf1e3a-7ed6-4a90-a4e0-4b5516f88051 + Size: 50 + Status: in-use +- Attached to: + - attached_at: '2022-06-27T17:31:53.000000' + attachment_id: 845a7ff2-ad54-4040-b69b-fce7fdfdece2 + device: /dev/sdb + host_name: null + id: 6d4492e5-6b38-4ad3-b9bf-27ba86e2506b + server_id: 37220c33-19e7-45cc-9c6a-ddf957be3c76 + volume_id: 6d4492e5-6b38-4ad3-b9bf-27ba86e2506b + ID: 6d4492e5-6b38-4ad3-b9bf-27ba86e2506b + Name: ovh-managed-kubernetes-e6j07m-pvc-7bf995bf-ac82-4f42-94ca-78cd9c78959d + Size: 1 + Status: in-use diff --git a/src/arkistoctl/main.cr b/src/arkistoctl/main.cr new file mode 100644 index 0000000..5bf1cac --- /dev/null +++ b/src/arkistoctl/main.cr @@ -0,0 +1,141 @@ +require "yaml" +require "option_parser" +require "colorize" +# require "xdg_basedir" +# require "completion" +require "log" + +require "../lib/actions" +require "../lib/types" +require "../lib/models" +require "../lib/version" + + +module Arkisto + class CtlCli + property config : ConfigModel? + property options : GlobalOptions? + + def initialize + # @config = nil + @options = nil + end + + def self.parse_options(args) : GlobalOptions + # default values + action = NoneAction + config_file = "arkisto.yml" + verbose = true + dry_run = false + + # parse + OptionParser.parse(args) do |parser| + parser.banner = "Usage: #{Version::PROGRAM_ARKISTOCTL} [options] [commands] [arguments]" + + parser.separator + parser.separator "Options" + + parser.on "-c CONFIG_FILE", "--config=CONFIG_FILE", "Use the following config file" do |file| + config_file = file + end + + parser.on "-v", "--verbose", "Be verbose" do + verbose = !verbose + end + + parser.on "--dry-run", "Dry run (no real openstack command is executed" do + dry_run = true + end + + parser.on "--version", "Show version" do + puts "version #{Version::VERSION}" + exit 0 + end + + parser.on "-h", "--help", "Show this help" do + puts parser + exit 0 + end + + parser.on "--completion", "Provide autocompletion for bash" do + # nothing here + end + + parser.on "plan", "Display backup plan" do + action = PlanAction + end + + parser.on "apply", "Apply backup plan" do + action = ApplyAction + end + + parser.separator + + parser.missing_option do |flag| + STDERR.puts parser + STDERR.puts "ERROR: #{flag} requires an argument.".colorize(:red) + exit(1) + end + + parser.invalid_option do |flag| + STDERR.puts parser + STDERR.puts "ERROR: #{flag} is not a valid option.".colorize(:red) + exit(1) + end + + # complete_with Version::PROGRAM_ARKISTOCTL, parser + end + + return { + action: action, + config_file: config_file, + dry_run: dry_run, + verbose: verbose, + }.as(GlobalOptions) + end + + def self.parse_config(options) + config_file = options[:config_file] + puts "Loading configuration... #{config_file}".colorize(:yellow) if options[:verbose] + + if ! File.exists? config_file + STDERR.puts "ERROR: Unable to read configuration file '#{config_file}'".colorize(:red) + exit 1 + end + + yaml_str = File.read(config_file) + config = ConfigModel.from_yaml(yaml_str) + + if config.nil? + STDERR.puts "ERROR: Invalid YAML content in '#{config_file}'" + exit 1 + end + + return config + end + + def self.run(args) + app = CtlCli.new + options = CtlCli.parse_options(args) + config = CtlCli.parse_config(options) + app.options = options + app.config = config + + action = ActionFactory.build( + options[:action], + config, + { + dry_run: options[:dry_run], + verbose: options[:verbose] + } + ) + action.perform + + rescue ex : YAML::ParseException + STDERR.puts "ERROR: #{ex.message}".colorize(:red) + end + end +end + +Arkisto::CtlCli.run(ARGV) +