minfra-cli 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (60) hide show
  1. checksums.yaml +7 -0
  2. data/.dockerignore +12 -0
  3. data/.gitignore +16 -0
  4. data/.rspec +2 -0
  5. data/CHANGELOG.md +2 -0
  6. data/Dockerfile +12 -0
  7. data/bin/build +20 -0
  8. data/bin/console +16 -0
  9. data/bin/container_exec +9 -0
  10. data/bin/run_tests +74 -0
  11. data/bin/setup.sh +22 -0
  12. data/exe/minfra +6 -0
  13. data/lib/deep_merge.rb +149 -0
  14. data/lib/hash.rb +28 -0
  15. data/lib/minfra/cli/ask.rb +43 -0
  16. data/lib/minfra/cli/command.rb +35 -0
  17. data/lib/minfra/cli/commands/dev.rb +54 -0
  18. data/lib/minfra/cli/commands/kube.rb +279 -0
  19. data/lib/minfra/cli/commands/project/branch.rb +17 -0
  20. data/lib/minfra/cli/commands/project/tag.rb +40 -0
  21. data/lib/minfra/cli/commands/project.rb +113 -0
  22. data/lib/minfra/cli/commands/setup.rb +49 -0
  23. data/lib/minfra/cli/commands/stack/app_template.rb +65 -0
  24. data/lib/minfra/cli/commands/stack/client_template.rb +36 -0
  25. data/lib/minfra/cli/commands/stack/kube_stack_template.rb +94 -0
  26. data/lib/minfra/cli/commands/stack.rb +120 -0
  27. data/lib/minfra/cli/commands/tag.rb +86 -0
  28. data/lib/minfra/cli/common.rb +41 -0
  29. data/lib/minfra/cli/config.rb +111 -0
  30. data/lib/minfra/cli/document.rb +19 -0
  31. data/lib/minfra/cli/hook.rb +65 -0
  32. data/lib/minfra/cli/logging.rb +26 -0
  33. data/lib/minfra/cli/main_command.rb +32 -0
  34. data/lib/minfra/cli/plugins.rb +34 -0
  35. data/lib/minfra/cli/runner.rb +59 -0
  36. data/lib/minfra/cli/templater.rb +63 -0
  37. data/lib/minfra/cli/version.rb +5 -0
  38. data/lib/minfra/cli.rb +80 -0
  39. data/lib/orchparty/ast.rb +53 -0
  40. data/lib/orchparty/cli.rb +69 -0
  41. data/lib/orchparty/context.rb +22 -0
  42. data/lib/orchparty/dsl_parser.rb +229 -0
  43. data/lib/orchparty/dsl_parser_kubernetes.rb +361 -0
  44. data/lib/orchparty/kubernetes_application.rb +305 -0
  45. data/lib/orchparty/plugin.rb +24 -0
  46. data/lib/orchparty/plugins/env.rb +41 -0
  47. data/lib/orchparty/transformations/all.rb +18 -0
  48. data/lib/orchparty/transformations/mixin.rb +73 -0
  49. data/lib/orchparty/transformations/remove_internal.rb +16 -0
  50. data/lib/orchparty/transformations/sort.rb +10 -0
  51. data/lib/orchparty/transformations/variable.rb +56 -0
  52. data/lib/orchparty/transformations.rb +24 -0
  53. data/lib/orchparty/version.rb +3 -0
  54. data/lib/orchparty.rb +59 -0
  55. data/minfra-cli.gemspec +40 -0
  56. data/project.json +7 -0
  57. data/templates/kind.yaml.erb +33 -0
  58. data/templates/kube_config.yaml.erb +7 -0
  59. data/templates/minfra_config.json.erb +26 -0
  60. metadata +196 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: fdef57265500c1ab2f82470de61a21d1d604d4cdbc198f2acbcafdab9cb37548
4
+ data.tar.gz: f7a25a0c65239ccaa49331a0e94d14494b92a44927df112c2258d6b42ef51420
5
+ SHA512:
6
+ metadata.gz: e2875bcf474cbed4017f6fb5e7188745b04357e0181a9ab1d8641d041d880e3152624e21f4fed4317a9c4a24c1c1e3d00c552f4fcaf558324a78d74d08de8930
7
+ data.tar.gz: f6ae3c0c3a82ca453a886a8d1bfc6ea2170a967f42094a482d769cbb40f60706416f2592465202afc2e04f7bd957068730d8da1420b14b6c13c331864770e1ca
data/.dockerignore ADDED
@@ -0,0 +1,12 @@
1
+ tmp/*
2
+ log/*
3
+ deploy/*
4
+ .codeqa/*
5
+ .dockerignore
6
+ .git/*
7
+ .gitignore
8
+ .idea/*
9
+ .rubocop.yml
10
+ public/javascripts/*
11
+ *~
12
+ vendor
data/.gitignore ADDED
@@ -0,0 +1,16 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /_yardoc/
4
+ /coverage/
5
+ /doc/
6
+ /pkg/
7
+ /spec/examples.txt
8
+ /spec/reports/
9
+ /tmp/
10
+ vendor
11
+
12
+ *~
13
+ .byebug_history
14
+ spec/docker-compose.run.yml
15
+
16
+ minfra-cli-*.gem
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --format progress
2
+ --color
data/CHANGELOG.md ADDED
@@ -0,0 +1,2 @@
1
+ # 0.1.0 Initial
2
+ * opensourcing a base state, still WIP!
data/Dockerfile ADDED
@@ -0,0 +1,12 @@
1
+ FROM ruby:2.7.1
2
+
3
+ ENV APP_DIR /code
4
+ WORKDIR $APP_DIR
5
+
6
+ ADD Gemfile $APP_DIR
7
+ #ADD Gemfile.lock $APP_DIR
8
+ RUN bundle install --jobs 4 --retry 3
9
+ ADD . $APP_DIR
10
+
11
+ CMD /bin/bash
12
+
data/bin/build ADDED
@@ -0,0 +1,20 @@
1
+ #!/bin/bash
2
+ set -e
3
+
4
+ app_dir=`pwd`
5
+ prj_file="$app_dir/project.json"
6
+
7
+ repo=`jq -r ".docker.repo" "$prj_file"`
8
+ project=`jq -r ".docker.name" "$prj_file"`
9
+ path=`jq -r ".path" "$prj_file"`
10
+
11
+ ACTION=${1:-local}
12
+
13
+ case "$ACTION" in
14
+ "local" )
15
+ docker build -t $repo/$project:latest $app_dir
16
+ ;;
17
+ * )
18
+ echo "unknown action: '$ACTION'"
19
+ ;;
20
+ esac
data/bin/console ADDED
@@ -0,0 +1,16 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ $LOAD_PATH << File.expand_path('../lib', __dir__)
4
+
5
+ require "bundler/setup"
6
+ require "minfra/cli"
7
+
8
+ # You can add fixtures and/or initialization code here to make experimenting
9
+ # with your gem easier. You can also use a different console, if you like.
10
+
11
+ # (If you use this, don't forget to add pry to your Gemfile!)
12
+ # require "pry"
13
+ # Pry.start
14
+
15
+ require "irb"
16
+ IRB.start
@@ -0,0 +1,9 @@
1
+ #!/bin/bash
2
+
3
+ app_dir=`pwd`
4
+ prj_file="$app_dir/project.json"
5
+
6
+ repo=`jq -r ".docker.repo" "$prj_file"`
7
+ project=`jq -r ".docker.name" "$prj_file"`
8
+
9
+ docker run -ti --rm -v `pwd`:/code $repo/$project:latest /bin/bash
data/bin/run_tests ADDED
@@ -0,0 +1,74 @@
1
+ #!/usr/bin/env ruby
2
+ require 'erb'
3
+ require 'json'
4
+ require 'pathname'
5
+
6
+ root=Pathname.new(File.expand_path('..', __dir__))
7
+ project_info=JSON.parse(File.read(root.join('project.json')))
8
+
9
+ def pull_base_image
10
+ dockerfile = File.read(File.expand_path('../Dockerfile', __dir__))
11
+ base_image = dockerfile.match(/^FROM .+$/).to_s.gsub(/^FROM /, '')
12
+ run("docker pull #{base_image}")
13
+ end
14
+
15
+ def run(command, exit_on_error=true)
16
+ puts "running: #{command}"
17
+ system(command, out: $stdout, err: :out)
18
+ exit_status=$?.exitstatus
19
+ if exit_status.nonzero? && exit_on_error
20
+ exit(exit_status)
21
+ end
22
+ exit_status
23
+ end
24
+
25
+ if ENV["INSIDE_CONTAINER"] == "true" then
26
+ puts 'setting up test and running rspec'
27
+ run("bundle exec rspec --colour --tty #{ARGV.join(' ')}")
28
+ exit(0)
29
+ end
30
+
31
+ def pull_base_image
32
+ dockerfile = File.read(File.expand_path('../Dockerfile', __dir__))
33
+ base_image = dockerfile.match(/^FROM .+$/).to_s.gsub(/^FROM /, '')
34
+ run("docker pull #{base_image}")
35
+ end
36
+
37
+ # We always pull the latest base image, to ensure that we don't build with
38
+ # an old base image that is already present on the Jenkins worker.
39
+ pull_base_image
40
+
41
+ @project_name = project_info["docker"]["name"] # used in docker-compose.yml
42
+
43
+ build_tag= ENV["BUILD_NUMBER"] || Time.now.strftime("%Y%m%d%H%M")
44
+
45
+ cache_tag_code = "git describe --abbrev=0 --tags `git rev-list HEAD --skip=1 --max-count=1 2>/dev/null` 2>/dev/null"
46
+ cache_tag = `#{cache_tag_code}`
47
+
48
+ unless $?.exitstatus == 0
49
+ puts ("getting cache tag failed, fallback to 'latest'")
50
+ cache_tag = "latest"
51
+ end
52
+ cache_tag = false unless ENV["BUILD_ID"]
53
+
54
+ volumes = []
55
+
56
+ volumes << "..:/code" unless ENV["BUILD_ID"]
57
+
58
+ @tag = build_tag
59
+ @cache_tag = nil #cache_tag
60
+ @volumes = volumes
61
+
62
+ # Run docker pull on cache
63
+ if @cache_tag
64
+ run("docker pull 858049876441.dkr.ecr.eu-west-1.amazonaws.com/#{@project_name}:#{@cache_tag}")
65
+ end
66
+
67
+ erb = ERB.new(File.open("#{__dir__}/../spec/docker-compose.yml").read, 0, '>')
68
+ File.write("#{__dir__}/../spec/docker-compose.run.yml", erb.result)
69
+
70
+ run("sudo docker-compose -f #{__dir__}/../spec/docker-compose.run.yml build")
71
+ envs=["-e 'INSIDE_CONTAINER=true'"]
72
+ exit_status=run("sudo docker-compose -f #{__dir__}/../spec/docker-compose.run.yml run --rm #{envs.join(' ')} tester bin/run_tests #{ARGV.join(' ')}", false)
73
+ run("sudo docker-compose -f #{__dir__}/../spec/docker-compose.run.yml down --remove-orphans") unless ENV["KEEP_SPEC_CONTAINERS"]
74
+ exit(exit_status)
data/bin/setup.sh ADDED
@@ -0,0 +1,22 @@
1
+ #!/bin/bash
2
+
3
+ PROJ_NAME=$1
4
+ PROJ_PATH=`pwd`/$PROJ_NAME
5
+
6
+ echo "Setting up your project $PROJ_NAME"
7
+
8
+ mkdir $PROJ_NAME
9
+
10
+ mkdir $PROJ_NAME/stacks
11
+ mkdir $PROJ_NAME/me
12
+ mkdir $PROJ_NAME/apps
13
+ mkdir $PROJ_NAME/infra
14
+ mkdir $PROJ_NAME/host
15
+ mkdir $PROJ_NAME/secrets
16
+ mkdir $PROJ_NAME/bin
17
+
18
+ cd $PROJ_NAME
19
+
20
+ echo "KUBECONFIG=$PROJ_NAME/me/kube/config" > me/dev.env
21
+ echo "HELM_HOME=$PROJ_NAME/me/helm" >> me/dev.env
22
+ echo "MINFRA_HOST_PATH=$PROJ_PATH" >> me/dev.env
data/exe/minfra ADDED
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ $LOAD_PATH << File.expand_path('../lib', __dir__)
4
+ require 'minfra/cli'
5
+ Minfra::Cli::Main.start(ARGV)
6
+
data/lib/deep_merge.rb ADDED
@@ -0,0 +1,149 @@
1
+ module Hashie
2
+ module Extensions
3
+ module DeepMergeConcat
4
+
5
+ def transform_values
6
+ result = self.class.new
7
+ each do |key, value|
8
+ result[key] = yield(value)
9
+ end
10
+ result
11
+ end
12
+
13
+ def transform_values!
14
+ each do |key, value|
15
+ self[key] = yield(value)
16
+ end
17
+ end
18
+
19
+ def transform_keys
20
+ result = self.class.new
21
+ each_key do |key|
22
+ result[yield(key)] = self[key]
23
+ end
24
+ result
25
+ end
26
+
27
+ def transform_keys!
28
+ keys.each do |key|
29
+ self[yield(key)] = delete(key)
30
+ end
31
+ self
32
+ end
33
+
34
+ def deep_sort_by_key_and_sort_array(exclusions = [], &block)
35
+ self.keys.sort(&block).reduce({}) do |seed, key|
36
+ seed[key] = self[key]
37
+ unless exclusions.include?(key.to_s)
38
+ if seed[key].is_a?(Hash)
39
+ seed[key] = seed[key].deep_sort_by_key_and_sort_array(exclusions, &block)
40
+ elsif seed[key].is_a?(Hashie::Array)
41
+ seed[key] = seed[key].sort(&block)
42
+ end
43
+ end
44
+ seed
45
+ end
46
+ end
47
+
48
+ # Returns a new hash with +self+ and +other_hash+ merged recursively.
49
+ def deep_sort(&block)
50
+ copy = dup
51
+ copy.extend(Hashie::Extensions::DeepMergeConcat) unless copy.respond_to?(:deep_sort!)
52
+ copy.deep_sort!(&block)
53
+ end
54
+
55
+ # Returns a new hash with +self+ and +other_hash+ merged recursively.
56
+ # Modifies the receiver in place.
57
+ def deep_sort!(&block)
58
+ _recursive_sort(self, &block)
59
+ self
60
+ end
61
+
62
+ # Returns a new hash with +self+ and +other_hash+ merged recursively.
63
+ def deep_merge_concat(other_hash, &block)
64
+ copy = dup
65
+ copy.extend(Hashie::Extensions::DeepMergeConcat) unless copy.respond_to?(:deep_merge_concat!)
66
+ copy.deep_merge_concat!(other_hash, &block)
67
+ end
68
+
69
+ # Returns a new hash with +self+ and +other_hash+ merged recursively.
70
+ # Modifies the receiver in place.
71
+ def deep_merge_concat!(other_hash, &block)
72
+ return self unless other_hash.is_a?(::Hash)
73
+ _recursive_merge_concat(self, other_hash, &block)
74
+ self
75
+ end
76
+
77
+ def deep_transform_values(&block)
78
+ _deep_transform_values_in_object(self, &block)
79
+ end
80
+
81
+ def deep_transform_values!(&block)
82
+ _deep_transform_values_in_object!(self, &block)
83
+ end
84
+
85
+ private
86
+
87
+ def _deep_transform_values_in_object(object, &block)
88
+ case object
89
+ when Hash
90
+ object.each_with_object({}) do |arg, result|
91
+ key = arg.first
92
+ value = arg.last
93
+ result[key] = _deep_transform_values_in_object(value, &block)
94
+ end
95
+ when Array
96
+ object.map {|e| _deep_transform_values_in_object(e, &block) }
97
+ else
98
+ yield(object)
99
+ end
100
+ end
101
+
102
+ def _deep_transform_values_in_object!(object, &block)
103
+ case object
104
+ when Hash
105
+ object.each do |key, value|
106
+ object[key] = _deep_transform_values_in_object!(value, &block)
107
+ end
108
+ when Array
109
+ object.map! {|e| _deep_transform_values_in_object!(e, &block) }
110
+ else
111
+ yield(object)
112
+ end
113
+ end
114
+
115
+ def _recursive_sort(object, &block)
116
+ case object
117
+ when Hash
118
+ object = Orchparty::AST::Node.new(object.sort {|a, b| block.call(a[0], b[0]) }.to_h)
119
+ object.each do |key, value|
120
+ object[key] = _recursive_sort(value, &block)
121
+ end
122
+ object
123
+ when Array
124
+ object.map! {|e| _recursive_sort(e, &block) }.sort(&block)
125
+ else
126
+ yield(object)
127
+ end
128
+ end
129
+
130
+
131
+ def _recursive_merge_concat(hash, other_hash, &block)
132
+ other_hash.each do |k, v|
133
+ hash[k] = if hash.key?(k) && hash[k].is_a?(::Hash) && v.is_a?(::Hash)
134
+ _recursive_merge(hash[k], v, &block)
135
+ elsif hash.key?(k) && hash[k].is_a?(::Array) && v.is_a?(::Array)
136
+ hash[k].concat(v).uniq
137
+ else
138
+ if hash.key?(k) && block_given?
139
+ block.call(k, hash[k], v)
140
+ else
141
+ v.respond_to?(:deep_dup) ? v.deep_dup : v
142
+ end
143
+ end
144
+ end
145
+ hash
146
+ end
147
+ end
148
+ end
149
+ end
data/lib/hash.rb ADDED
@@ -0,0 +1,28 @@
1
+ class HashUtils
2
+ def self.transform_hash(original, options={}, &block)
3
+ original.inject({}){|result, (key,value)|
4
+ value = if (options[:deep] && Hash === value)
5
+ transform_hash(value, options, &block)
6
+ else
7
+ if Array === value
8
+ value.map{|v|
9
+ if Hash === v
10
+ transform_hash(v, options, &block)
11
+ else
12
+ v
13
+ end}
14
+ else
15
+ value
16
+ end
17
+ end
18
+ block.call(result,key,value)
19
+ result
20
+ }
21
+ end
22
+ # Convert keys to strings, recursively
23
+ def self.deep_stringify_keys(hash)
24
+ transform_hash(hash, :deep => true) {|hash, key, value|
25
+ hash[key.to_s] = value
26
+ }
27
+ end
28
+ end
@@ -0,0 +1,43 @@
1
+ module Minfra
2
+ module Cli
3
+ module Ask
4
+ def self.boolean(question)
5
+ answered = false
6
+ until answered
7
+ STDOUT.write "#{question} (y/n)"
8
+ answer = STDIN.gets.chomp
9
+ if ['y', 'n'].include?(answer)
10
+ answered = true
11
+ else
12
+ STDOUT.write("I just understand 'y' and 'n', again: ")
13
+ end
14
+ end
15
+ answer == 'y'
16
+ end
17
+
18
+ def self.text(question, default=nil)
19
+ begin
20
+ message = format('%s%s: ', question, default && " (#{default})")
21
+ STDOUT.write message
22
+ answer = STDIN.gets.chomp
23
+ answer = default if answer == ''
24
+ end while answer.nil?
25
+ answer
26
+ end
27
+
28
+ def self.placeholders(templater,placeholders={})
29
+ templater.check_missing do |name|
30
+ placeholders[name]=self.text("I need a value for: #{name}") unless placeholders[name]
31
+ end
32
+ placeholders
33
+ end
34
+
35
+ def self.interactive_template(template_file, out_file, placeholders={})
36
+ templater=Templater.new(File.read(template_file))
37
+ params=self.placeholders(templater,placeholders)
38
+ File.write(out_file,templater.render(params))
39
+
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,35 @@
1
+ module Minfra
2
+ module Cli
3
+ class Command < Thor
4
+ include Common
5
+ include Logging
6
+ include Hook
7
+
8
+ attr_reader :minfra_config
9
+ no_commands do
10
+ def invoke_command(*args)
11
+ begin
12
+ subcommand_name = args.first.name.to_sym
13
+ with_hook(subcommand_name) do
14
+ @minfra_config = Minfra::Cli.config
15
+ @minfra_config.load(options['environment']) if options['environment']
16
+ @cli_args = args[1]
17
+ super(*args)
18
+ end
19
+ rescue Minfra::Cli::Config::ConfigNotFoundError => err
20
+ STDERR.puts(err.message)
21
+ STDERR.puts "please run 'minfra setup dev'"
22
+ exit 1
23
+ rescue Minfra::Cli::Config::EnvironmentNotFoundError => err
24
+ STDERR.puts(err.message)
25
+ exit 1
26
+ end
27
+ end
28
+ def cli_args
29
+ @args
30
+ end
31
+
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,54 @@
1
+ require 'fileutils'
2
+ module Minfra
3
+ module Cli
4
+ class Dev < Command
5
+
6
+ desc "describe", "get some info about your config (if it's setup)"
7
+ option :environment, required: false, aliases: ['-e']
8
+ def describe
9
+ pp @minfra_config.describe(options[:environment])
10
+ end
11
+
12
+ desc "create", "create your development cluster"
13
+ def create
14
+ kube.create
15
+ end
16
+
17
+ desc "upgrade", "upgrade kind (moves previous configs and recreates the cluster; use it if you made changes to config/kind.yaml.erb)"
18
+ def upgrade
19
+ info "Destroying existing dev cluster.."
20
+ destroy
21
+ Runner.run("mv #{@minfra_config.base_path.join('me','kind.yaml.erb')} #{@minfra_config.base_path.join('me','kind_old_' + Time.now.strftime("%Y_%m_%dT%H_%M_%SZ") + '.yaml.erb')}", print_stdout: true)
22
+ Runner.run("mv #{@minfra_config.base_path.join('me', 'kube', 'config')} #{@minfra_config.base_path.join('me', 'kube', 'config_old_' + Time.now.strftime("%Y_%m_%dT%H_%M_%SZ"))}", print_stdout: true)
23
+ Runner.run('yes | minfra setup dev', print_stdout: true) # On an existing cluster this should only ask for recreating the files that we moved previously
24
+ info "Creating a new dev cluster.."
25
+ create
26
+ info "I am done upgrading the dev cluster! 🎉"
27
+ end
28
+
29
+ desc "restart", "restart your development cluster"
30
+ def restart
31
+ kube.restart
32
+ end
33
+
34
+ desc "start", "start your development cluster"
35
+ def start
36
+ restart
37
+ end
38
+
39
+ desc "destroy", "tear down your development cluster"
40
+ def destroy
41
+ kube.destroy_dev_cluster
42
+ end
43
+
44
+ private
45
+
46
+ def kube
47
+ @kube ||= Kube.new(options,minfra_config)
48
+ end
49
+
50
+ end
51
+ end
52
+ end
53
+
54
+ Minfra::Cli.register("dev", "Manage your dev cluster.", Minfra::Cli::Dev)