sct 0.1.14 → 0.1.19

Sign up to get free protection for your applications and to get access to all the features.
Files changed (63) hide show
  1. checksums.yaml +4 -4
  2. data/bin/sct +3 -4
  3. data/{.DS_Store → cluster/lib/.DS_Store} +0 -0
  4. data/cluster/lib/cluster.rb +6 -0
  5. data/cluster/lib/cluster/commands_generator.rb +95 -0
  6. data/cluster/lib/cluster/module.rb +7 -0
  7. data/cluster/lib/cluster/runner.rb +239 -0
  8. data/{lib → sct/lib}/.DS_Store +0 -0
  9. data/sct/lib/sct.rb +17 -0
  10. data/sct/lib/sct/.DS_Store +0 -0
  11. data/sct/lib/sct/cli_tools_distributor.rb +46 -0
  12. data/{lib → sct/lib}/sct/command.rb +0 -0
  13. data/sct/lib/sct/commands/hostfile.rb +68 -0
  14. data/sct/lib/sct/commands/init.rb +37 -0
  15. data/sct/lib/sct/commands/mysqlproxy.rb +20 -0
  16. data/sct/lib/sct/commands_generator.rb +56 -0
  17. data/sct/lib/sct/tools.rb +12 -0
  18. data/sct/lib/sct/version.rb +3 -0
  19. data/sct_core/lib/.DS_Store +0 -0
  20. data/sct_core/lib/sct_core.rb +13 -0
  21. data/{lib/sct → sct_core/lib/sct_core}/.DS_Store +0 -0
  22. data/sct_core/lib/sct_core/command_executor.rb +104 -0
  23. data/{lib/sct → sct_core/lib/sct_core}/config.rb +3 -3
  24. data/sct_core/lib/sct_core/core_ext/string.rb +9 -0
  25. data/{lib/sct/setup/helpers.rb → sct_core/lib/sct_core/helper.rb} +2 -2
  26. data/sct_core/lib/sct_core/module.rb +0 -0
  27. data/sct_core/lib/sct_core/sct_pty.rb +53 -0
  28. data/sct_core/lib/sct_core/ui/implementations/shell.rb +129 -0
  29. data/sct_core/lib/sct_core/ui/interface.rb +120 -0
  30. data/sct_core/lib/sct_core/ui/ui.rb +26 -0
  31. data/shell/README.md +0 -0
  32. data/shell/lib/shell.rb +3 -0
  33. data/{lib/sct → shell/lib/shell}/ClassLevelInheritableAttributes.rb +0 -0
  34. data/shell/lib/shell/commands_generator.rb +14 -0
  35. data/{lib/sct → shell/lib/shell}/docker/composer.rb +4 -3
  36. data/{lib/sct → shell/lib/shell}/docker/docker.rb +7 -10
  37. data/{lib/sct → shell/lib/shell}/docker/php.rb +3 -2
  38. data/{lib/sct → shell/lib/shell}/docker/yarn.rb +4 -3
  39. data/shell/lib/shell/module.rb +9 -0
  40. data/shell/lib/shell/runner.rb +34 -0
  41. data/shell/lib/shell/tools.rb +7 -0
  42. metadata +92 -54
  43. data/.gitignore +0 -12
  44. data/.rspec +0 -3
  45. data/.travis.yml +0 -7
  46. data/CODE_OF_CONDUCT.md +0 -74
  47. data/Gemfile +0 -4
  48. data/Gemfile.lock +0 -48
  49. data/LICENSE.txt +0 -21
  50. data/README.md +0 -134
  51. data/Rakefile +0 -6
  52. data/lib/sct.rb +0 -61
  53. data/lib/sct/command_interface.rb +0 -18
  54. data/lib/sct/command_option.rb +0 -14
  55. data/lib/sct/commands/cluster.rb +0 -121
  56. data/lib/sct/commands/composer.rb +0 -29
  57. data/lib/sct/commands/hostfile.rb +0 -125
  58. data/lib/sct/commands/init.rb +0 -51
  59. data/lib/sct/commands/mysqlproxy.rb +0 -38
  60. data/lib/sct/commands/php.rb +0 -37
  61. data/lib/sct/commands/yarn.rb +0 -26
  62. data/lib/sct/version.rb +0 -3
  63. data/sct.gemspec +0 -40
File without changes
@@ -0,0 +1,68 @@
1
+ module Sct
2
+ class HostfileCommand
3
+
4
+ def execute(args, options)
5
+ return UI.error("SCT has not been initialized. Run 'sct init' first.") unless SctCore::Config.exists
6
+
7
+ return UI.error("This command needs to be run with sudo.") unless SctCore::Helper.isSudo
8
+
9
+ return unless SctCore::Helper.ingressAddress
10
+
11
+ ingressAddress = SctCore::Helper.ingressAddress
12
+
13
+ entries = [
14
+ {
15
+ host: "spend.cloud.local",
16
+ comment: "The spend cloud ingress url"
17
+ },
18
+ {
19
+ host: "mail.spend.cloud.local",
20
+ comment: "The spend cloud mail url"
21
+ },
22
+ {
23
+ host: "config.spend.cloud.local",
24
+ comment: "The spend cloud config url"
25
+ },
26
+ {
27
+ host: "spend-cloud.spend.cloud.local",
28
+ comment: "The spend cloud web app url"
29
+ },
30
+ {
31
+ host: "docs.spend.cloud.local",
32
+ comment: "The spend cloud documentation url"
33
+ }
34
+ ]
35
+
36
+ if options.path
37
+ hosts_paths = [options.path]
38
+ else
39
+ hosts_paths = ["/etc/hosts"]
40
+
41
+ if SctCore::Helper.operatingSystem == SctCore::Helper::WINDOWS
42
+ hosts_paths << "/mnt/c/Windows/System32/drivers/etc/hosts"
43
+ end
44
+ end
45
+
46
+ hosts_paths.each do |hosts_path|
47
+ line_ending = hosts_path == "/mnt/c/Windows/System32/drivers/etc/hosts" ? "\r\n" : "\n"
48
+
49
+ lines = File.readlines hosts_path
50
+
51
+ # select the lines that do not include any entry
52
+ lines = lines.select { |line| ! entries.any? { |entry| line.include? entry[:host] } }
53
+
54
+ # add entries
55
+ entries.each do |entry|
56
+ lines << "#{ingressAddress} #{entry[:host]} # #{entry[:comment]}#{line_ending}"
57
+ end
58
+
59
+ File.write hosts_path, lines.join
60
+
61
+ UI.success("Patched #{hosts_path} with #{ingressAddress}")
62
+ end
63
+
64
+ end
65
+
66
+ end
67
+
68
+ end
@@ -0,0 +1,37 @@
1
+ require 'sct_core/ui/ui'
2
+ require 'sct_core/config'
3
+
4
+ module Sct
5
+
6
+ class InitCommand
7
+
8
+ def execute(args, options)
9
+
10
+ dir = SctCore::Config.dir
11
+
12
+ cli = HighLine.new
13
+
14
+ email = cli.ask("What is your email address?") { |q|
15
+ q.validate = URI::MailTo::EMAIL_REGEXP
16
+ }
17
+
18
+ cloud_proxy_path = cli.ask("What is the path of your cloud proxy json credentials?") { |q|
19
+ q.default = "~/.config/gcloud/application_default_credentials.json"
20
+ }
21
+
22
+ contents = ""
23
+ contents << "email=#{email}\n"
24
+ contents << "cloud-proxy-path=#{File.expand_path(cloud_proxy_path)}\n"
25
+
26
+ if !File.directory?(dir)
27
+ FileUtils.mkdir_p(dir)
28
+ end
29
+
30
+ File.write(SctCore::Config.path, contents)
31
+
32
+ puts "Generated config file at #{SctCore::Config.path}"
33
+ end
34
+
35
+ end
36
+
37
+ end
@@ -0,0 +1,20 @@
1
+ module Sct
2
+ class MysqlproxyCommand
3
+
4
+ DEFAULT_SECRET_NAME = "gcloud-credentials"
5
+
6
+ def execute(args, options)
7
+
8
+ return UI.error("SCT has not been initialized. Run 'sct init' first.") unless SctCore::Config.exists
9
+
10
+ path = SctCore::Config.get('cloud-proxy-path')
11
+
12
+ system("kubectl delete secret gcloud-credentials")
13
+ system("kubectl create secret generic gcloud-credentials --from-file=#{path}")
14
+
15
+ UI.success("Authenticated with secret-name: '#{DEFAULT_SECRET_NAME}'")
16
+ end
17
+
18
+ end
19
+
20
+ end
@@ -0,0 +1,56 @@
1
+ require 'commander'
2
+ require 'sct/command'
3
+
4
+ module Sct
5
+ class CommandsGenerator
6
+ include Commander::Methods
7
+
8
+ def self.start
9
+ self.new.run
10
+ end
11
+
12
+ def run
13
+ program :name, 'sct'
14
+ program :version, Sct::VERSION
15
+ program :summary, 'CLI helper tool for local SCT development'
16
+ program :description, 'SCT is a CLI tool for developers using the Visma Continuous Deployment Model in conjunction with the Google Cloud Platform (GCP). It provides multiple command to set up and maintain a kubernetes cluster on a machine for local development'
17
+
18
+ global_option('--verbose') { $verbose = true }
19
+
20
+ command :init do |c|
21
+ c.syntax = 'sct init'
22
+ c.description = 'setup sct'
23
+
24
+ c.action do |args, options|
25
+ UI.important("setting up sct")
26
+ Sct::InitCommand.new.execute(args, options)
27
+ end
28
+ end
29
+
30
+ command :hostfile do |c|
31
+
32
+ c.syntax = 'sct hostfile'
33
+ c.description = 'patch hostfile with kubernetes ip'
34
+
35
+ c.action do |args, options|
36
+ UI.important("Trying to patch hostfile")
37
+ Sct::HostfileCommand.new.execute(args, options)
38
+ end
39
+ end
40
+
41
+ command :'mysql proxy' do |c|
42
+
43
+ c.syntax = 'sct mysql proxy'
44
+ c.description = 'setup google mysql proxy'
45
+
46
+ c.action do |args, options|
47
+ UI.important("Trying to setup mysql proxy")
48
+ Sct::MysqlproxyCommand.new.execute(args, options)
49
+ end
50
+
51
+ end
52
+
53
+ run!
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,12 @@
1
+ module Sct
2
+ TOOLS = [
3
+ :sct,
4
+ :shell,
5
+ :cluster
6
+ ]
7
+
8
+ # a list of all the config files we currently expect
9
+ TOOL_CONFIG_FILES = [
10
+
11
+ ]
12
+ end
@@ -0,0 +1,3 @@
1
+ module Sct
2
+ VERSION = "0.1.19"
3
+ end
Binary file
@@ -0,0 +1,13 @@
1
+ require_relative 'sct_core/core_ext/string'
2
+ require_relative 'sct_core/config'
3
+ require_relative 'sct_core/helper'
4
+ require_relative 'sct_core/command_executor'
5
+ require_relative 'sct_core/ui/ui'
6
+ require_relative 'sct_core/sct_pty'
7
+
8
+ # third party code
9
+ require 'colored'
10
+ require 'commander'
11
+
12
+ # these need to be imported after commander
13
+ require_relative 'sct_core/module'
@@ -0,0 +1,104 @@
1
+ require_relative 'ui/ui'
2
+ require_relative 'sct_pty'
3
+
4
+ module SctCore
5
+ # Executes commands and takes care of error handling and more
6
+ class CommandExecutor
7
+ class << self
8
+ # Cross-platform way of finding an executable in the $PATH. Respects the $PATHEXT, which lists
9
+ # valid file extensions for executables on Windows.
10
+ #
11
+ # which('ruby') #=> /usr/bin/ruby
12
+ #
13
+ # Derived from https://stackoverflow.com/a/5471032/3005
14
+ def which(cmd)
15
+ # PATHEXT contains the list of file extensions that Windows considers executable, semicolon separated.
16
+ # e.g. ".COM;.EXE;.BAT;.CMD"
17
+ exts = ENV['PATHEXT'] ? ENV['PATHEXT'].split(';') : []
18
+ exts << '' # Always have an empty string (= no file extension)
19
+
20
+ ENV['PATH'].split(File::PATH_SEPARATOR).each do |path|
21
+ exts.each do |ext|
22
+ cmd_path = File.join(path, "#{cmd}#{ext}")
23
+ return cmd_path if Helper.executable?(cmd_path)
24
+ end
25
+ end
26
+
27
+ return nil
28
+ end
29
+
30
+ # @param command [String] The command to be executed
31
+ # @param print_all [Boolean] Do we want to print out the command output while running?
32
+ # @param print_command [Boolean] Should we print the command that's being executed
33
+ # @param error [Block] A block that's called if an error occurs
34
+ # @param prefix [Array] An array containing a prefix + block which might get applied to the output
35
+ # @param loading [String] A loading string that is shown before the first output
36
+ # @param suppress_output [Boolean] Should we print the command's output?
37
+ # @return [String] All the output as string
38
+ def execute(command: nil, print_all: false, print_command: true, error: nil, prefix: nil, loading: nil, suppress_output: false)
39
+ print_all = true if $verbose
40
+ prefix ||= {}
41
+
42
+ output = []
43
+ command = command.join(" ") if command.kind_of?(Array)
44
+ command = self.escape_characters_in_string(command)
45
+ UI.command(command) if print_command
46
+
47
+ if print_all && loading # this is only used to show the "Loading text"...
48
+ UI.command_output(loading)
49
+ end
50
+
51
+ begin
52
+ status = SctCore::SctPty.spawn(command) do |command_stdout, command_stdin, pid|
53
+ command_stdout.each do |l|
54
+ line = l.chomp
55
+ output << line
56
+
57
+ next unless print_all
58
+
59
+ # Prefix the current line with a string
60
+ prefix.each do |element|
61
+ line = element[:prefix] + line if element[:block] && element[:block].call(line)
62
+ end
63
+
64
+ UI.command_output(line) unless suppress_output
65
+ end
66
+ end
67
+ rescue => ex
68
+ # SctPty adds exit_status on to StandardError so every error will have a status code
69
+ status = ex.exit_status
70
+
71
+ # This could happen when the environment is wrong:
72
+ # > invalid byte sequence in US-ASCII (ArgumentError)
73
+ output << ex.to_s
74
+ o = output.join("\n")
75
+ puts(o)
76
+ if error
77
+ error.call(o, nil)
78
+ else
79
+ raise ex
80
+ end
81
+ end
82
+
83
+ # Exit status for build command, should be 0 if build succeeded
84
+ if status != 0
85
+ o = output.join("\n")
86
+ puts(o) unless suppress_output # the user has the right to see the raw output
87
+ UI.error("Exit status: #{status}")
88
+ if error
89
+ error.call(o, status)
90
+ else
91
+ UI.user_error!("Exit status: #{status}")
92
+ end
93
+ end
94
+
95
+ return output.join("\n")
96
+ end
97
+
98
+ def escape_characters_in_string(string)
99
+ pattern = /(\'|\"|\.|\*|\/|\-|\\|\)|\$|\+|\(|\^|\?|\!|\~|\`)/
100
+ string.gsub(pattern){|match|"\\" + match}
101
+ end
102
+ end
103
+ end
104
+ end
@@ -1,8 +1,8 @@
1
- module Sct
1
+ module SctCore
2
2
  class Config
3
3
 
4
4
  def self.dir
5
- return "#{Sct::Helpers.homePath}/.config/sct"
5
+ return "#{SctCore::Helper.homePath}/.config/sct"
6
6
  end
7
7
 
8
8
  def self.path
@@ -30,7 +30,7 @@ module Sct
30
30
 
31
31
  contents = File.read(self.path)
32
32
 
33
- return Sct::Helpers.to_hash(contents)
33
+ return SctCore::Helper.to_hash(contents)
34
34
  end
35
35
  end
36
36
  end
@@ -0,0 +1,9 @@
1
+ class String
2
+ def sct_class
3
+ split('_').collect!(&:capitalize).join
4
+ end
5
+
6
+ def sct_module
7
+ self.sct_class
8
+ end
9
+ end
@@ -1,5 +1,5 @@
1
- module Sct
2
- class Helpers
1
+ module SctCore
2
+ class Helper
3
3
 
4
4
  WINDOWS = "Windows"
5
5
  MAC_OS = "MacOS"
File without changes
@@ -0,0 +1,53 @@
1
+ class StandardError
2
+ def exit_status
3
+ return -1
4
+ end
5
+ end
6
+
7
+ module SctCore
8
+ class SctPtyError < StandardError
9
+ attr_reader :exit_status
10
+ def initialize(e, exit_status)
11
+ super(e)
12
+ set_backtrace(e.backtrace) if e
13
+ @exit_status = exit_status
14
+ end
15
+ end
16
+
17
+ class SctPty
18
+ def self.spawn(command)
19
+ require 'pty'
20
+ PTY.spawn(command) do |command_stdout, command_stdin, pid|
21
+ begin
22
+ yield(command_stdout, command_stdin, pid)
23
+ rescue Errno::EIO
24
+ # Exception ignored intentionally.
25
+ # https://stackoverflow.com/questions/10238298/ruby-on-linux-pty-goes-away-without-eof-raises-errnoeio
26
+ # This is expected on some linux systems, that indicates that the subcommand finished
27
+ # and we kept trying to read, ignore it
28
+ ensure
29
+ begin
30
+ Process.wait(pid)
31
+ rescue Errno::ECHILD, PTY::ChildExited
32
+ # The process might have exited.
33
+ end
34
+ end
35
+ end
36
+ $?.exitstatus
37
+ rescue LoadError
38
+ require 'open3'
39
+ Open3.popen2e(command) do |command_stdin, command_stdout, p| # note the inversion
40
+ yield(command_stdout, command_stdin, p.value.pid)
41
+
42
+ command_stdin.close
43
+ command_stdout.close
44
+ p.value.exitstatus
45
+ end
46
+ rescue StandardError => e
47
+ # Wrapping any error in SctPtyError to allow
48
+ # callers to see and use $?.exitstatus that
49
+ # would usually get returned
50
+ raise SctPtyError.new(e, $?.exitstatus)
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,129 @@
1
+ require_relative '../interface'
2
+
3
+ module SctCore
4
+ # Shell is the terminal output of things
5
+ # For documentation for each of the methods open `interface.rb`
6
+ class Shell < Interface
7
+ require 'tty-screen'
8
+
9
+ def log
10
+ $stdout.sync = true
11
+ end
12
+
13
+ def writeToUser(message)
14
+ puts message
15
+ end
16
+
17
+ def error(message)
18
+ writeToUser(message.to_s.red)
19
+ end
20
+
21
+ def important(message)
22
+ writeToUser(message.to_s.yellow)
23
+ end
24
+
25
+ def success(message)
26
+ writeToUser(message.to_s.green)
27
+ end
28
+
29
+ def message(message)
30
+ writeToUser(message.to_s)
31
+ end
32
+
33
+ def deprecated(message)
34
+ writeToUser(message.to_s.deprecated)
35
+ end
36
+
37
+ def command(message)
38
+ writeToUser("$ #{message}".cyan)
39
+ end
40
+
41
+ def command_output(message)
42
+ actual = (message.split("\r").last || "") # as clearing the line will remove the `>` and the time stamp
43
+ actual.split("\n").each do |msg|
44
+ # prefix = msg.include?("▸") ? "" : "▸ "
45
+ prefix = ""
46
+ writeToUser(prefix + "" + msg.magenta)
47
+ end
48
+ end
49
+
50
+ def verbose(message)
51
+ message.to_s if $verbose
52
+ end
53
+
54
+ def header(message)
55
+ format = format_string
56
+ if message.length + 8 < TTY::Screen.width - format.length
57
+ message = "--- #{message} ---"
58
+ i = message.length
59
+ else
60
+ i = TTY::Screen.width - format.length
61
+ end
62
+ success("-" * i)
63
+ success(message)
64
+ success("-" * i)
65
+ end
66
+
67
+ def content_error(content, error_line)
68
+ error_line = error_line.to_i
69
+ return unless error_line > 0
70
+
71
+ contents = content.split(/\r?\n/).map(&:chomp)
72
+
73
+ start_line = error_line - 2 < 1 ? 1 : error_line - 2
74
+ end_line = error_line + 2 < contents.length ? error_line + 2 : contents.length
75
+
76
+ Range.new(start_line, end_line).each do |line|
77
+ str = line == error_line ? " => " : " "
78
+ str << line.to_s.rjust(Math.log10(end_line) + 1)
79
+ str << ":\t#{contents[line - 1]}"
80
+ error(str)
81
+ end
82
+ end
83
+
84
+ #####################################################
85
+ # @!group Errors: Inputs
86
+ #####################################################
87
+
88
+ def interactive?
89
+ interactive = true
90
+ interactive = false if $stdout.isatty == false
91
+ return interactive
92
+ end
93
+
94
+ def input(message)
95
+ verify_interactive!(message)
96
+ ask("#{format_string}#{message.to_s.yellow}").to_s.strip
97
+ end
98
+
99
+ def confirm(message)
100
+ verify_interactive!(message)
101
+ agree("#{format_string}#{message.to_s.yellow} (y/n)", true)
102
+ end
103
+
104
+ def select(message, options)
105
+ verify_interactive!(message)
106
+
107
+ important(message)
108
+ choose(*options)
109
+ end
110
+
111
+ def password(message)
112
+ verify_interactive!(message)
113
+
114
+ ask("#{format_string}#{message.to_s.yellow}") { |q| q.echo = "*" }
115
+ end
116
+
117
+ def user_error!(error_message, options = {})
118
+ writeToUser(error_message.yellow)
119
+ end
120
+
121
+ private
122
+
123
+ def verify_interactive!(message)
124
+ return if interactive?
125
+ important(message)
126
+ crash!("Could not retrieve response as sct runs in non-interactive mode")
127
+ end
128
+ end
129
+ end