minfra-cli 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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)