prepd 0.1.1 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (51) hide show
  1. checksums.yaml +5 -5
  2. data/bin/console +2 -0
  3. data/files/cluster/Vagrantfile +118 -0
  4. data/files/cluster/vagrant.yml +52 -0
  5. data/files/developer/cluster/provision.yml +2 -0
  6. data/files/machine/build.json +52 -0
  7. data/files/machine/debian/stretch/iso.json +101 -0
  8. data/files/machine/debian/stretch/preseed.cfg +404 -0
  9. data/files/machine/json.rb +26 -0
  10. data/files/machine/push.json +56 -0
  11. data/files/machine/rebuild.json +60 -0
  12. data/files/project/provision.yml +20 -0
  13. data/files/project/vars.yml +5 -0
  14. data/files/setup.yml +16 -0
  15. data/files/setup/README.md +21 -0
  16. data/files/setup/ansible.cfg +4 -0
  17. data/files/setup/hosts +1 -0
  18. data/files/setup/setup.yml +19 -0
  19. data/files/setup/vars.yml +20 -0
  20. data/files/workspace/.gitignore +12 -0
  21. data/files/workspace/README.md +11 -0
  22. data/files/workspace/clusters/prepd.yml +17 -0
  23. data/files/workspace/clusters/provision.yml +73 -0
  24. data/files/workspace/clusters/vagrant.rb +106 -0
  25. data/files/workspace/clusters/vagrant.yml +18 -0
  26. data/files/workspace/data/.keep +0 -0
  27. data/files/workspace/developer/ansible.cfg +4 -0
  28. data/files/workspace/developer/credentials/.keep +0 -0
  29. data/files/workspace/developer/hosts +7 -0
  30. data/files/workspace/developer/machines/provision.yml +9 -0
  31. data/files/workspace/developer/provision.yml +15 -0
  32. data/files/workspace/machines/build.yml +34 -0
  33. data/files/workspace/machines/provision.yml +36 -0
  34. data/lib/prepd.rb +95 -34
  35. data/lib/prepd/cli.rb +27 -4
  36. data/lib/prepd/cli/commands.rb +64 -25
  37. data/lib/prepd/cli/options_parser.rb +42 -16
  38. data/lib/prepd/models.rb +7 -261
  39. data/lib/prepd/models/base.rb +124 -0
  40. data/lib/prepd/models/cluster.rb +255 -0
  41. data/lib/prepd/models/data.rb +5 -0
  42. data/lib/prepd/models/developer.rb +129 -0
  43. data/lib/prepd/models/machine.rb +146 -0
  44. data/lib/prepd/models/project.rb +94 -0
  45. data/lib/prepd/models/setup.rb +48 -0
  46. data/lib/prepd/models/workspace.rb +51 -0
  47. data/lib/prepd/version.rb +1 -1
  48. data/prepd.gemspec +4 -6
  49. metadata +47 -37
  50. data/TODO.md +0 -17
  51. data/lib/prepd/schema.rb +0 -23
@@ -0,0 +1,18 @@
1
+ # applies to all clusters
2
+ # NOTE: Dir.pwd returns the directory of the Vagrantfile
3
+ ---
4
+ settings:
5
+ workspace: <%= Pathname.new(Dir.pwd).parent.parent %>
6
+ boxes_dir: <%= Pathname.new(Dir.pwd).parent.parent %>/machines/boxes
7
+ # boxes_dir: <%= Dir.home %>/.prepd/share/packer/debian/stretch/boxes
8
+ # boxes_dir: https://s3-ap-southeast-1.amazonaws.com/c2p4-storage/boxes
9
+ name: <%= Dir.pwd.split('/').last %>
10
+ projects_name: <%= Dir.pwd.split('/').last %>
11
+
12
+ boxes:
13
+ development:
14
+ box: <%= workspace['name'] %>/debian-stretch-amd64-development
15
+ box_url: <%= settings['boxes_dir'] %>/debian-stretch-amd64-development.json
16
+ minikube:
17
+ box: <%= workspace['name'] %>/debian-stretch-amd64-minikube
18
+ box_url: <%= settings['boxes_dir'] %>/debian-stretch-amd64-minikube.json
File without changes
@@ -0,0 +1,4 @@
1
+ [defaults]
2
+ inventory = hosts
3
+ vault_password_file = vault-password.txt
4
+ retry_files_enabled = no
File without changes
@@ -0,0 +1,7 @@
1
+ node0 ansible_connection=local
2
+
3
+ [development]
4
+ node0
5
+
6
+ [kubectl]
7
+ node0
@@ -0,0 +1,9 @@
1
+ # Custom roles for the developer
2
+ ---
3
+ - hosts: developer-base
4
+ tasks:
5
+ - include_role:
6
+ name: prepd/yadr
7
+
8
+ - include_role:
9
+ name: prepd/tmuxinator
@@ -0,0 +1,15 @@
1
+ #!/usr/bin/env ansible-playbook
2
+ ---
3
+ - hosts: development
4
+ tasks:
5
+ - name: Include vars
6
+ include_vars:
7
+ file: vars.yml
8
+
9
+ - include_role:
10
+ name: prepd/yadr
11
+
12
+ - include_role:
13
+ name: prepd/credentials
14
+ vars:
15
+ credentials_src_dirs: credentials
@@ -0,0 +1,34 @@
1
+ ---
2
+ aws:
3
+ profile: vagrant-s3
4
+ s3_bucket: my-storage-bucket
5
+ s3_region: ap-southeast-1
6
+ box_dir: boxes
7
+
8
+ os_images:
9
+ debian_stretch_amd64:
10
+ base_dir: debian/stretch
11
+ base_name: debian-stretch-amd64
12
+ iso_url: https://cdimage.debian.org/debian-cd/9.4.0/amd64/iso-cd/debian-9.4.0-amd64-xfce-CD-1.iso
13
+ iso_checksum: 6715c8c0bedf77c8eef2e8732e117db6
14
+
15
+ images:
16
+ # 1) Start by creating a base image from the OS's official ISO and add ansible
17
+ base:
18
+ source:
19
+ os_image: debian_stretch_amd64
20
+ provisioner: <%= Dir.pwd %>/provision.yml
21
+ # 2) Customize the base image to the developer's requirements
22
+ developer-base:
23
+ source:
24
+ image: base
25
+ provisioner: <%= Pathname.new(Dir.pwd).parent.to_s %>/developer/machines/provision.yml
26
+ # 3) All additional images are derived from the developer's base image
27
+ development:
28
+ source:
29
+ image: developer-base
30
+ provisioner: <%= Dir.pwd %>/provision.yml
31
+ minikube:
32
+ source:
33
+ image: developer-base
34
+ provisioner: <%= Dir.pwd %>/provision.yml
@@ -0,0 +1,36 @@
1
+ ---
2
+ - hosts: base
3
+ tasks:
4
+ - include_role:
5
+ name: prepd/packer/base-provision
6
+ - include_role:
7
+ name: prepd/ansible
8
+
9
+ - hosts: development
10
+ tasks:
11
+ - include_role:
12
+ name: prepd/ruby
13
+ - include_role:
14
+ name: prepd/docker
15
+ - name: Install kubectl
16
+ include_role:
17
+ name: prepd/k8s/kubectl
18
+ - name: Install helm
19
+ include_role:
20
+ name: prepd/k8s/helm
21
+
22
+ - hosts: minikube
23
+ tasks:
24
+ - include_role:
25
+ name: prepd/docker
26
+ - include_role:
27
+ name: prepd/k8s/kubectl
28
+ - include_role:
29
+ name: prepd/k8s/minikube
30
+ vars:
31
+ # See: https://github.com/kubernetes/minikube/issues/2549
32
+ k8s_minikube_bin_url: https://storage.googleapis.com/minikube-builds/2550/minikube-linux-amd64
33
+ - include_role:
34
+ name: prepd/k8s/minikube
35
+ tasks_from: start
36
+ - command: minikube delete
data/lib/prepd.rb CHANGED
@@ -1,47 +1,108 @@
1
- require 'prepd/version'
2
- require 'dotenv'
3
- require 'active_record'
4
- require 'sqlite3'
5
1
  require 'fileutils'
2
+ require 'active_record'
6
3
 
7
4
  module Prepd
8
- def self.work_dir; "#{Dir.home}/.prepd"; end
9
- def self.data_dir; ENV['DATA_DIR']; end
5
+ class StringInquirer < String
6
+ # Copied from ActiveSupport::StringInquirer
7
+ def method_missing(method_name, *arguments)
8
+ if method_name[-1] == '?'
9
+ self == method_name[0..-2]
10
+ else
11
+ super
12
+ end
13
+ end
14
+ end
10
15
 
11
- def self.files; Dir.glob("#{work_dir}/*"); end
16
+ def self.env; @env; end
17
+ def self.env=(env); @env = env; end
12
18
 
13
- def self.config; "#{work_dir}/config"; end
19
+ def self.config_dir; @config_dir end
14
20
 
15
- def self.default_settings
16
- {
17
- 'VERSION' => '1',
18
- 'DATA_DIR' => "#{Dir.home}/prepd",
19
- 'VAGRANT_BASE_BOX' => 'debian/contrib-jessie64'
20
- }
21
+ def self.config_dir=(dir)
22
+ @config_dir = dir
21
23
  end
22
24
 
23
- # Create records for exisitng directories in the DATA_DIR
24
- def self.scan
25
- clients = Dir.entries(ENV['DATA_DIR'])
26
- clients.select { |entry| !entry.starts_with?('.') }.each do |client_name|
27
- c = Client.find_or_create_by(name: client_name)
28
- projects = Dir.entries("#{ENV['DATA_DIR']}/#{client_name}")
29
- projects.select { |entry| !entry.starts_with?('.') }.each do |project_name|
30
- c.projects.find_or_create_by(name: project_name)
31
- end
32
- end
25
+ def self.config_file; "#{config_dir}/config.yml"; end
26
+
27
+ def self.default_config
28
+ { version: 1 }
29
+ end
30
+
31
+ def self.register_workspace(dir)
32
+ config.workspaces << dir
33
+ write_config_file
33
34
  end
34
35
 
35
- FileUtils.mkdir_p work_dir
36
- ActiveRecord::Base.logger = Logger.new(File.open("#{work_dir}/database.log", 'w'))
37
- ActiveRecord::Base.establish_connection(adapter: :sqlite3, database: "#{work_dir}/sqlite.db")
38
- unless File.exists?(config)
39
- File.open(config, 'a') do |f|
40
- default_settings.each { |key, value| f.puts("#{key}=#{value}") }
36
+ def self.verify_workspaces
37
+ config.workspaces ||= []
38
+ config.workspaces.each do |dir|
39
+ next if File.exists?(File.expand_path("#{dir}/prepd-workspace.yml"))
40
+ config.workspaces -= [dir]
41
41
  end
42
+ write_config_file
43
+ end
44
+
45
+ def self.base_config
46
+ write_config_file unless File.exists?(config_file)
47
+ default_config.merge(YAML.load(File.read(config_file)))
48
+ end
49
+
50
+ def self.write_config_file
51
+ FileUtils.mkdir_p(config_dir) unless Dir.exists?(config_dir)
52
+ File.open(config_file, 'w') { |f| f.write(YAML.dump(writable_config)) }
53
+ end
54
+
55
+ def self.writable_config
56
+ config.to_h.select { |k| %i(version workspaces).include?(k) }
57
+ end
58
+
59
+ def self.config=(config)
60
+ @config = config
61
+ end
62
+
63
+ def self.config; @config; end
64
+
65
+ def self.cli_options=(config)
66
+ @cli_options = config
67
+ end
68
+
69
+ def self.cli_options; @cli_options; end
70
+
71
+ def self.log(message)
72
+ STDOUT.puts(message)
73
+ end
74
+
75
+ def self.files_dir
76
+ "#{Pathname.new(File.dirname(__FILE__)).parent}/files"
42
77
  end
43
- Dotenv.load(config)
44
- end
45
78
 
46
- require 'prepd/schema'
47
- require 'prepd/models'
79
+ # Probe system for whether it is virutal or not and default accordingly
80
+ # hostnamectl | grep Virtualization will return a string when the string is found (vm) and '' when not (host)
81
+ # it will fail on apple machines b/c hostnamectl is not a valid command which means it is the host
82
+ def self.machine_is_host?
83
+ return true unless system('hostnamectl > /dev/null 2>&1')
84
+ %x('hostnamectl').index('Virtualization').nil?
85
+ end
86
+
87
+
88
+ def self.create_password_file(config_dir)
89
+ password_dir = "#{config_dir}/vault-keys"
90
+ password_file = "#{password_dir}/password.txt"
91
+ return if File.exists?(password_file)
92
+ FileUtils.mkdir_p(password_dir) unless Dir.exists? password_dir
93
+ write_password_file(password_file)
94
+ end
95
+
96
+ #
97
+ # Generate the key to encrypt ansible-vault files
98
+ #
99
+ def self.write_password_file(file_name = 'password.txt')
100
+ require 'securerandom'
101
+ File.open(file_name, 'w') { |f| f.puts(SecureRandom.uuid) }
102
+ nil
103
+ end
104
+
105
+ def self.git_log
106
+ config.verbose ? '' : '--quiet'
107
+ end
108
+ end
data/lib/prepd/cli.rb CHANGED
@@ -1,8 +1,31 @@
1
1
  require 'pry'
2
- require 'prepd'
2
+ require 'ostruct'
3
3
  require 'prepd/cli/options_parser'
4
4
  require 'prepd/cli/commands'
5
5
 
6
- Prepd.options = Prepd.default_settings
7
- Prepd.options.merge!(Prepd::Cli::OptionsParser.new.parse)
8
- Pry.start(Prepd, prompt: [proc { 'prepd> '}])
6
+ module Prepd
7
+ # Parse any command line arguments
8
+ Prepd.cli_options = OpenStruct.new(Cli::OptionsParser.new.parse)
9
+
10
+ # Load the default config, override with config file valuse and finally override with any command line options
11
+ Prepd.config_dir = Prepd.cli_options.config_dir || "#{Dir.home}/.prepd"
12
+ Prepd.config = OpenStruct.new(base_config.merge(cli_options.to_h))
13
+ config.command = StringInquirer.new(ARGV[0] ? ARGV.shift : 'cli')
14
+
15
+ config.env = StringInquirer.new(Prepd.cli_options.env || 'production' )
16
+ config.working_dir ||= Dir.pwd
17
+
18
+ # Set config values based on machine probe, defaults, config file and cli arguments
19
+ config.machine_type = StringInquirer.new(machine_is_host? ? 'host' : 'vm')
20
+ Prepd.verify_workspaces
21
+
22
+ # Process the command or invoke the console
23
+ if config.command.cli?
24
+ Pry.start(Prepd::Command, prompt: [proc { 'prepd> '}])
25
+ elsif Command.methods(false).include?(config.command.to_sym)
26
+ STDOUT.puts(Command.send(config.command))
27
+ else
28
+ # TODO: show the 'runtime' help
29
+ STDOUT.puts("#{config.command} - No such command. Valid commands are #{Command.methods(false).join(', ')}")
30
+ end
31
+ end
@@ -1,37 +1,76 @@
1
+ require 'prepd/models'
2
+
1
3
  module Prepd
2
- def self.options=(options)
3
- @options = options
4
- end
5
- def self.options; @options; end
4
+ class Command
5
+ def self.config; Prepd.config; end
6
6
 
7
- def self.commands
8
- puts (methods(false) - %i(:options= :options :commands default_settings)).join("\n")
9
- end
7
+ def self.list(type = ARGV.shift)
8
+ return 'invalid type' unless %w(clusters projects machines).include? type
9
+ "Prepd::#{type.classify}".constantize.new.in_component_root { Dir.glob('*') }
10
+ end
10
11
 
11
- def self.new(name)
12
- Client.create(name: name)
13
- end
12
+ def self.new(type = ARGV.shift, name = ARGV.shift, *args)
13
+ cr = Creator.new(type: type)
14
+ # TODO: this should display the appropriate help if name is not supplied
15
+ return cr.errors.full_messages.join("\n") unless cr.valid?
16
+ # return 'Must supply type' unless type
17
+ # return 'Must supply APP_PATH' unless name
18
+ obj = cr.klass.new(name: name)
19
+ return obj.errors.full_messages.join("\n") unless obj.valid?
20
+ obj.create
21
+ nil
22
+ end
14
23
 
15
- def self.rm
16
- FileUtils.rm_rf(work_dir)
17
- FileUtils.rm_rf(data_dir)
18
- end
24
+ def self.build(name = ARGV.shift)
25
+ cr = Machine.new(name: name)
26
+ return cr.errors.full_messages.join("\n") unless cr.valid?
27
+ cr.create
28
+ nil
29
+ end
19
30
 
20
- def self.clients; Client.pluck(:name); end
31
+ def self.show(name = nil)
32
+ name ||= ARGV[0] || Dir.pwd.split('/').last
33
+ return unless obj = klass.find_by(name: name)
34
+ YAML.load(obj.to_yaml)
35
+ end
21
36
 
22
- def self.projects; Project.pluck(:name); end
37
+ def self.up(name = ARGV.shift)
38
+ Cluster.new(name: name).up
39
+ end
23
40
 
24
- def self.current_client
25
- @client
41
+ def self.rm(name = nil)
42
+ name ||= ARGV[0] || Dir.pwd.split('/').last
43
+ return unless obj = klass.find_by(name: name)
44
+ obj.destroy ? nil : obj.errors.full_messages.join('. ')
45
+ end
46
+
47
+ #
48
+ # Setup a new installation of prepd on a workstation
49
+ #
50
+ def self.setup
51
+ obj = Setup.new
52
+ return obj.errors.full_messages.join("\n") unless obj.valid?
53
+ obj.create
54
+ end
26
55
  end
27
56
 
28
- def self.current_client=(client)
29
- STDOUT.puts 'duh'
30
- @client = client
31
- Dir.chdir(client.path) do
32
- Pry.start(client, prompt: [proc { "prepd(#{client.name}) > " }])
57
+ class Creator
58
+ include ActiveModel::Model
59
+ include ActiveModel::Validations::Callbacks
60
+ VALID_CLASSES = %w(workspace cluster project).freeze
61
+
62
+ attr_accessor :type
63
+
64
+ validates :type, presence: true, inclusion: { in: VALID_CLASSES }
65
+
66
+ before_validation :set_type_down
67
+
68
+ def set_type_down
69
+ self.type = self.type.downcase
70
+ end
71
+
72
+ def klass
73
+ Kernel.const_get("Prepd::#{type.capitalize}")
33
74
  end
34
- STDOUT.puts 'duh2'
35
- nil
36
75
  end
37
76
  end
@@ -2,41 +2,67 @@ require 'optparse'
2
2
 
3
3
  module Prepd::Cli
4
4
  class OptionsParser
5
- attr_accessor :options
6
-
7
- def initialize(options = nil)
8
- self.options = options || {}
9
- end
10
-
11
5
  def parse
6
+ options = OpenStruct.new
12
7
  optparse = OptionParser.new do |opts|
13
- opts.on('-c', '--client [OPT]', 'Client') do |value|
14
- options['CLIENT'] = value
8
+ opts.banner = "Usage:\n prepd new AAP_PATH [options]\n\nOptions:"
9
+
10
+ opts.on( '--bump=LEVEL', '# Setup the application with development repositories' ) do |value|
11
+ options.bump = value
15
12
  end
16
13
 
17
- opts.on( '-d', '--data_dir [OPT]', 'Data directory' ) do |value|
18
- options['DATA_DIR'] = value
14
+ opts.on( '--cd=CONFIG_DIR', '# Run from the configuration in directory' ) do |value|
15
+ options.config_dir = value
19
16
  end
20
17
 
21
- opts.on( '-p', '--project [OPT]', 'Project' ) do |value|
22
- options['PROJECT'] = value
18
+ opts.on( '--push', '# Push the box to remote S3 bucket' ) do
19
+ options.push = true
23
20
  end
24
21
 
25
- opts.on('-h', '--help', 'Display this screen') do
22
+ opts.on( '--dev', '# Setup the application with development repositories' ) do |value|
23
+ options.env = 'development'
24
+ end
25
+
26
+ opts.on( '--dir=DIR', '# Set the working directory' ) do |value|
27
+ options.working_dir = value
28
+ end
29
+
30
+ opts.on( '--force', '# Force operation even if it will cause errors' ) do |value|
31
+ options.force = true
32
+ end
33
+
34
+ opts.on('-h', '--help', '# Display this screen') do
35
+ # TODO: If Dir.pwd is a prepd project then putput the 'runtime' commands here
36
+ # Otherwise output the 'prepd new --help' is appropriate
26
37
  puts opts
38
+ puts "\nExample:\n prepd new ~/my/new/project\n"
39
+ puts "\n This generates a skeletal prepd installation in ~/my/new/project"
27
40
  exit
28
41
  end
29
42
 
30
- opts.on('-n', '--no-op', 'Show what would happen but do not execute') do
43
+ opts.on( '-m', '--machine', '# Create a new virtual machine' ) do |value|
44
+ options.create_type = :machine
45
+ end
46
+
47
+ opts.on('-n', '--no-op', '# Show what would happen but do not execute') do
31
48
  options.no_op = true
49
+ options.verbose = true
32
50
  end
33
51
 
34
- opts.on('-v', '--verbose', 'Display additional information') do
52
+ opts.on( '-p', '--project', '# Create a new project' ) do |value|
53
+ options.create_type = :project
54
+ end
55
+
56
+ opts.on('-v', '--verbose', '# Display additional information') do
35
57
  options.verbose = true
36
58
  end
59
+
60
+ opts.on('--yes', '# Automatically say yes') do
61
+ options.yes = true
62
+ end
37
63
  end
38
64
  optparse.parse!
39
- options
65
+ options.to_h
40
66
  end
41
67
  end
42
68
  end