prepd 0.1.1 → 0.3.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 (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