gleis 0.6.2 → 1.0.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 3ad8f8e30fef22c8c714d92e602847f01d0ff7168e083cf134c223678545c43a
4
- data.tar.gz: 24e396158f978e59f026e129f5c789eef09be4241c4f2f4f024117e624cc5ec8
3
+ metadata.gz: 8832f19d9aa54a36a1a1d1e7682b1ce6017294c22115bf83755d6eeb7331a2e3
4
+ data.tar.gz: 15f7458349747ce180483878bb02761473324130ac254d0754a388d7fb6535ff
5
5
  SHA512:
6
- metadata.gz: 51d40cf0a453b0ce8186cb0f4a5f99c5d46aa30cada7cae15834c7c9d02a0fb70cfec6168c983628de442a2d9cb04f1ac5ecd52a6c4ddf418c44c339e998b5c6
7
- data.tar.gz: a58043fce6783a3d38d7105abe198ec3bb63599eeb86286804fecac09b142ac7c11302bf99232470b044eb9f03496565771913f426b3acf78cac964fe627cbb5
6
+ metadata.gz: ca53e5e9fae14cd827ce6b476a911a3add034fec36fc70858b157cc898c3325a4c446a37aeb2efc6d320589a250831cbdb91ddd698a90bbcf4d2bd3905e03a92
7
+ data.tar.gz: 685270a20f7c4076aac97dc46784974abfbca9607567e2ef3a061210fcce5028c639631425c077934e51d4eff7720ad870e4e60d461fcae4b8a9431c2e6033ca
@@ -5,6 +5,65 @@ All notable changes to the Gleis CLI will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## 1.0.0 - 2020-08-21
9
+
10
+ ### Added
11
+
12
+ - New command called healthcheck in order to show and change the health check path of an app
13
+
14
+ ### Changed
15
+
16
+ - References to old gleis.cloud domain by new domain name gleis.io
17
+ - Version dependency of thor
18
+
19
+ ## 0.9.0 - 2020-06-26
20
+
21
+ ### Added
22
+
23
+ - Meaningful reason why app create command failed in case of problem
24
+
25
+ ### Fixed
26
+
27
+ - Extra space in SSH key generator comment and typo in user message when command is unavailable
28
+ - Raw invalid token message when session times out during the app logs command in follow mode
29
+
30
+ ## 0.8.0 - 2020-04-19
31
+
32
+ ### Changed
33
+
34
+ - Calls to system commands to check first if external system command exists
35
+ - External system call to which command with own which method
36
+ - Path separator to be dynamic for cross-platform compatibility
37
+ - Password prompt to use IO.noecho instead of depending on stty system command
38
+ - Save path of database dump file to be stored into user home directory instead of tmp
39
+
40
+ ### Fixed
41
+
42
+ - Adding SSH Host configuration in non-existing SSH config file
43
+
44
+ ## 0.7.1 - 2020-03-20
45
+
46
+ ### Changed
47
+
48
+ - API URL to use .app top-level domain
49
+
50
+ ## 0.7.0 - 2020-03-07
51
+
52
+ ### Changed
53
+
54
+ - Version dependency of mdl, rest-client and rubocop
55
+ - Actual year of copyright to be dynamic
56
+ - API POST request read timeout from default of 60s to 600s
57
+
58
+ ### Added
59
+
60
+ - New command called rebuild in order to rebuild an app image from the git repo
61
+ - Rescue read timeout from API request
62
+
63
+ ### Fixed
64
+
65
+ - Error message when restarting a not yet deployed app by deprecating error_text usage
66
+
8
67
  ## 0.6.2 - 2019-08-24
9
68
 
10
69
  ### Fixed
data/exe/gleis CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  require 'gleis/main'
4
4
 
5
- puts "\n### Gleis CLI Copyright (c) 2018-2019 towards GmbH - v#{Gleis::VERSION} - https://gleis.cloud ###\n\n" \
5
+ puts "\n### Gleis CLI Copyright (c) 2018-#{Time.new.year} towards GmbH - v#{Gleis::VERSION} - https://gleis.io ###\n\n" \
6
6
  unless ARGF.argv.include?('-q')
7
7
 
8
8
  trap 'SIGINT' do
@@ -12,17 +12,22 @@ module Gleis
12
12
  resp = RestClient.send(method, url, 'X-Gleis-Token': token, content_type: :json, accept: :json,
13
13
  user_agent: user_agent)
14
14
  when 'post', 'put'
15
- if token
16
- resp =
17
- RestClient.send(method, url, body.to_json, 'X-Gleis-Token': token, content_type: :json, accept: :json,
18
- user_agent: user_agent)
19
- else
20
- resp = RestClient.send(method, url, body.to_json, content_type: :json, accept: :json,
15
+ resp = if token
16
+ RestClient::Request.execute(method: method.to_s, url: url, payload: body.to_json, read_timeout: 600,
17
+ headers: { 'X-Gleis-Token': token, 'Content-Type': 'application/json',
18
+ 'Accept': 'application/json', 'User-Agent': user_agent })
19
+ else
20
+ RestClient.send(method, url, body.to_json, content_type: :json, accept: :json,
21
21
  user_agent: user_agent)
22
- end
22
+ end
23
23
  when 'stream'
24
24
  stream_output = proc { |response|
25
25
  response.read_body do |chunk|
26
+ # Catch timed out session during streaming response to client (e.g. follow log)
27
+ # in order not to display raw "invalid token" JSON response from API
28
+ chunk == '{"message":"invalid token"}' && abort('Session has timed out, please login again (invalid token).')
29
+ # Catch application not found raw JSON response from API
30
+ chunk == '{"message":"app not found"}' && abort('Application not found.')
26
31
  puts chunk
27
32
  end
28
33
  }
@@ -44,12 +49,15 @@ module Gleis
44
49
  abort('Application not found.')
45
50
  rescue RestClient::InternalServerError
46
51
  abort('An error occured on the platform. Please contact support if this problem persists.')
52
+ rescue RestClient::Exceptions::ReadTimeout
53
+ abort('The request timed out reading data from the platform.')
47
54
  rescue StandardError # (e.g. SocketError, Errno::ECONNREFUSED, RestClient::BadGateway, ...)
48
55
  abort('There was an issue connecting to the Gleis API server.')
49
56
  else
50
- # Streaming GET returns Net::HTTPInternalServerError in case of issue and no usable response body
51
- # In that case the class of resp.body == Net::ReadAdapter
52
- if defined? resp
57
+ # In case the user's session expires during streaming the response class will be NilClass
58
+ if (defined? resp) && resp.class != NilClass
59
+ # Streaming GET returns Net::HTTPInternalServerError in case of issue and no usable response body,
60
+ # in that case the class of resp.body == Net::ReadAdapter
53
61
  if resp.body.empty?
54
62
  # If there is no body return whole response object
55
63
  resp
@@ -38,6 +38,7 @@ module Gleis
38
38
  puts "You can then access your app using the URL https://#{body['dns_name']}" if body.key? 'dns_name'
39
39
  else
40
40
  puts 'failed!'
41
+ puts "\nReason for failure: #{body['message']}" unless body['message'].nil?
41
42
  end
42
43
  end
43
44
 
@@ -74,6 +75,7 @@ module Gleis
74
75
 
75
76
  def self.exec(app_name, command)
76
77
  token = Token.check
78
+ abort('The SSH client command ssh is not installed on this system.') unless Utils.which('ssh')
77
79
  config_body = Config.get_env_vars(app_name, token)
78
80
  # Get storage and generate mount parameter
79
81
  mount_param = Utils.generate_exec_mount_param(API.request('get', "storage/#{app_name}", token), app_name)
@@ -86,8 +88,8 @@ module Gleis
86
88
  -p #{p['run_port']} \
87
89
  #{p['run_username']}@#{p['run_server']} \
88
90
  '#{Utils.generate_exec_env_param(config_body['env_vars'])} #{mount_param} \
89
- #{p['registry_server']}/#{dp_body['namespace']}/#{app_name}:#{dp_body['deployments'].last['commit'][0..6]} \
90
- #{command}'")
91
+ #{p['registry_server']}/#{dp_body['namespace']}/#{app_name}:#{dp_body['deployments'].last['commit'][0..6]} \
92
+ #{command}'")
91
93
  else
92
94
  puts 'No deployments found, please deploy your app first.'
93
95
  end
@@ -108,6 +110,28 @@ module Gleis
108
110
  end
109
111
  end
110
112
 
113
+ def self.healthcheck(app_name, path)
114
+ token = Token.check
115
+ if path.empty?
116
+ body = API.request('get', "healthcheck/#{app_name}", token)
117
+ if body['success'] == 1
118
+ puts "The path for the health check of the #{app_name} app is: #{body['data']}"
119
+ else
120
+ puts 'Failed to fetch health check path for app.'
121
+ end
122
+ elsif Utils.uri_path_valid?(path)
123
+ body = API.request('post', 'healthcheck', token, 'name': app_name, 'path': path)
124
+ if body['success'] == 1
125
+ puts "Successfully changed app health check path to: #{path}\n\n"
126
+ puts "Don't forget to restart your app to make your change effective"
127
+ else
128
+ puts "Failed to change app health chech path: #{body['message']}"
129
+ end
130
+ else
131
+ puts 'Invalid health check path'
132
+ end
133
+ end
134
+
111
135
  def self.logs(app_name, follow, process)
112
136
  token = Token.check
113
137
  action = "logs/#{app_name}/#{process}?follow=#{follow}"
@@ -145,13 +169,25 @@ module Gleis
145
169
  end
146
170
  end
147
171
 
172
+ def self.rebuild(app_name)
173
+ token = Token.check
174
+ puts 'Please wait while rebuilding, it could take a few minutes depending on app dependencies and complexity...'
175
+ body = API.request('post', 'rebuild', token, 'name': app_name)
176
+ if body['success'] == 1
177
+ puts "\nSuccessfully rebuilt app from git repo, detailed output from image build below:"
178
+ puts body['data']
179
+ else
180
+ puts 'Failed to rebuild app: ' + body['message']
181
+ end
182
+ end
183
+
148
184
  def self.restart(app_name)
149
185
  token = Token.check
150
186
  body = API.request('post', 'restart', token, 'name': app_name)
151
187
  if body['success'] == 1
152
188
  puts 'Successfully restarted app using rolling update'
153
189
  else
154
- puts 'Failed to restart app: ' + body['error_text']
190
+ puts 'Failed to restart app: ' + body['message']
155
191
  end
156
192
  end
157
193
 
@@ -35,6 +35,11 @@ module Gleis
35
35
  Application.git(options[:app], options[:quiet])
36
36
  end
37
37
 
38
+ desc 'healthcheck [PATH]', 'Show or change the path for the health check'
39
+ def healthcheck(path = '')
40
+ Application.healthcheck(options[:app], path)
41
+ end
42
+
38
43
  desc 'logs', 'View last log entries of a process'
39
44
  option :follow, aliases: :'-f', type: :boolean, default: false,
40
45
  desc: 'Follow live log data'
@@ -62,6 +67,11 @@ module Gleis
62
67
  Application.ps(options[:app])
63
68
  end
64
69
 
70
+ desc 'rebuild', 'Rebuild app from git repo'
71
+ def rebuild
72
+ Application.rebuild(options[:app])
73
+ end
74
+
65
75
  desc 'restart', 'Restart application (rolling update)'
66
76
  def restart
67
77
  Application.restart(options[:app])
@@ -2,14 +2,14 @@ module Gleis
2
2
  # Global parameters of gleis gem and app config env variables
3
3
  class Config
4
4
  # Override default API URL if GLEIS_API_URL env variable is set
5
- API_URL = ENV['GLEIS_API_URL'].nil? ? 'https://api.basel.gleis.one/' : ENV['GLEIS_API_URL']
5
+ API_URL = ENV['GLEIS_API_URL'].nil? ? 'https://api.basel.gleis.app/' : ENV['GLEIS_API_URL']
6
6
  API_VERSION = 'v0'.freeze
7
- SSH_KEY_FILE_BASE = Dir.home + '/.ssh/gleis'
8
- TOKEN_FILE = Dir.home + '/.gleis-token'
7
+ SSH_KEY_FILE_BASE = File.join(Dir.home, '.ssh', 'gleis')
8
+ TOKEN_FILE = File.join(Dir.home, '.gleis-token')
9
9
 
10
10
  def self.get_env_vars(app_name, token)
11
11
  body = API.request('get', "config/#{app_name}", token)
12
- abort("Failed to get app environment variables: #{body['error_text']}") if body['success'] != 1
12
+ abort("Failed to get app environment variables: #{body['message']}") if body['success'] != 1
13
13
  body
14
14
  end
15
15
 
@@ -8,7 +8,7 @@ module Gleis
8
8
  abort('There is no database configured under the DATABASE_URL variable.') unless url
9
9
  db_name = url.split('/').last
10
10
  timestamp = Time.now.strftime('%Y%m%d%H%M%S')
11
- backup_file = "/tmp/#{app_name}_#{db_name}_#{timestamp}.pgdump"
11
+ backup_file = File.join(Dir.home, "#{app_name}_#{db_name}_#{timestamp}.pgdump")
12
12
  if system("pg_dump -f #{backup_file} #{url}")
13
13
  puts "Database configured at DATABASE_URL succesfully backed up locally in #{backup_file}"
14
14
  else
@@ -102,7 +102,7 @@ module Gleis
102
102
 
103
103
  def self.psql(app_name)
104
104
  token = Token.check
105
- abort('The PostgreSQL client psql is not installed on this system.') unless system('which psql >/dev/null')
105
+ abort('The PostgreSQL client psql is not installed on this system.') unless Utils.which('psql')
106
106
  url = Config.get_env_var(app_name, token, 'DATABASE_URL')
107
107
  abort('There is no database configured under the DATABASE_URL variable.') unless url
108
108
  ENV['PGCONNECT_TIMEOUT'] = '5'
@@ -3,6 +3,10 @@ require 'gleis'
3
3
  module Gleis
4
4
  # This class defines all the main command-line interface commands for gleis
5
5
  class Main < Thor
6
+ def self.exit_on_failure?
7
+ true
8
+ end
9
+
6
10
  desc 'login USERNAME', 'Login into Gleis'
7
11
  def login(username)
8
12
  Authentication.login(username)
@@ -21,7 +21,7 @@ module Gleis
21
21
  def self.license
22
22
  puts <<LICENSE
23
23
  Gleis Command Line Interface for the Gleis Platform as a Service
24
- Copyright (C) 2018-2019 towards GmbH
24
+ Copyright (C) 2018-#{Time.new.year} towards GmbH
25
25
 
26
26
  This program is free software: you can redistribute it and/or modify
27
27
  it under the terms of the GNU General Public License as published by
@@ -11,9 +11,13 @@ module Gleis
11
11
 
12
12
  hostname = Socket.gethostname
13
13
  datetime = Time.now.strftime('%Y-%m-%d %H:%M')
14
- # returns true on success
15
- system('ssh-keygen', '-f', key_file, '-b 4096',
16
- "-C generated by Gleis CLI for #{username} by #{ENV['USER']}@#{hostname} on #{datetime}")
14
+ # returns true on success and nil if command is not found or failed
15
+ if Utils.which('ssh-keygen')
16
+ system('ssh-keygen', '-f', key_file, '-b 4096',
17
+ "-Cgenerated by Gleis CLI for #{username} by #{ENV['USER']}@#{hostname} on #{datetime}")
18
+ else
19
+ puts 'The SSH key generator command ssh-keygen is not installed on this system.'
20
+ end
17
21
  end
18
22
 
19
23
  def self.load_public_key(public_key_file)
@@ -21,10 +25,10 @@ module Gleis
21
25
  end
22
26
 
23
27
  def self.add_host_to_config(host_name, private_key_file)
24
- config_file = Dir.home + '/.ssh/config'
28
+ config_file = File.join(Dir.home, '.ssh', 'config')
25
29
  ssh_host_line = "Host #{host_name}"
26
- # Check if SSH config for hosts already exists
27
- return if Utils.line_exists_in_file(config_file, ssh_host_line)
30
+ # Do not continue if host already exists in current SSH config file
31
+ return if File.exist?(config_file) && Utils.line_exists_in_file(config_file, ssh_host_line)
28
32
 
29
33
  f = File.open(config_file, 'a')
30
34
  f.puts "\n# Added by Gleis CLI on #{Time.now.strftime('%Y-%m-%d %H:%M')}"
@@ -51,7 +51,7 @@ module Gleis
51
51
  abort("Directory #{dir} does not exist or is not a directory.") unless Dir.exist?(dir)
52
52
  # Get CLI parameters from API server
53
53
  params = Params.get_cli_parameters(token)
54
- abort('The rsync tool is not installed on this system.') unless system('which rsync >/dev/null')
54
+ abort('The rsync tool is not installed on this system.') unless Utils.which('rsync')
55
55
  body = API.request('get', "storage/#{app_name}", token)
56
56
  if body['success'] == 1
57
57
  if (storage = body['data']['storage'].first)
@@ -1,3 +1,5 @@
1
+ require 'io/console'
2
+
1
3
  module Gleis
2
4
  # This class gathers various utilities as small methods
3
5
  class Utils
@@ -7,9 +9,7 @@ module Gleis
7
9
 
8
10
  def self.prompt_password
9
11
  print 'Password: '
10
- system 'stty -echo'
11
- password = $stdin.gets.chomp
12
- system 'stty echo'
12
+ password = STDIN.noecho(&:gets).chomp
13
13
  puts
14
14
  password
15
15
  end
@@ -32,7 +32,7 @@ module Gleis
32
32
 
33
33
  def self.check_for_local_pg_command(command)
34
34
  abort("The PostgreSQL client required to run the command #{command}) is not installed on this system.") unless
35
- system("which #{command} >/dev/null")
35
+ which(command)
36
36
  end
37
37
 
38
38
  def self.validate_scale_count(count)
@@ -48,7 +48,7 @@ module Gleis
48
48
  end
49
49
 
50
50
  def self.add_remote_to_git_config(remote_url)
51
- config_file = Dir.pwd + '/.git/config'
51
+ config_file = File.join(Dir.pwd, '.git', 'config')
52
52
  return false unless File.exist?(config_file)
53
53
 
54
54
  # Check if gleis remote already exists
@@ -119,5 +119,25 @@ module Gleis
119
119
  def self.update_available?(actual_version)
120
120
  Gem::Version.new(Gleis::VERSION) < Gem::Version.new(actual_version)
121
121
  end
122
+
123
+ def self.which(command)
124
+ file_extensions = ENV['PATHEXT'] ? ENV['PATHEXT'].split(';') : ['']
125
+ ENV['PATH'].split(File::PATH_SEPARATOR).each do |path|
126
+ file_extensions.each do |extension|
127
+ executable = File.join(path, "#{command}#{extension}")
128
+ return executable if File.executable?(executable) && !File.directory?(executable)
129
+ end
130
+ end
131
+ nil
132
+ end
133
+
134
+ def self.uri_path_valid?(path)
135
+ begin
136
+ URI.parse("http://localhost:3000#{path}")
137
+ rescue URI::InvalidURIError
138
+ return false
139
+ end
140
+ true
141
+ end
122
142
  end
123
143
  end
@@ -1,3 +1,3 @@
1
1
  module Gleis
2
- VERSION = '0.6.2'
2
+ VERSION = '1.0.0'
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: gleis
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.6.2
4
+ version: 1.0.0
5
5
  platform: ruby
6
6
  authors:
7
- - Marc Bigler
7
+ - towards GmbH
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2019-08-24 00:00:00.000000000 Z
11
+ date: 2020-08-21 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -44,14 +44,14 @@ dependencies:
44
44
  requirements:
45
45
  - - "~>"
46
46
  - !ruby/object:Gem::Version
47
- version: '0.5'
47
+ version: '0.8'
48
48
  type: :development
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
52
  - - "~>"
53
53
  - !ruby/object:Gem::Version
54
- version: '0.5'
54
+ version: '0.8'
55
55
  - !ruby/object:Gem::Dependency
56
56
  name: rake
57
57
  requirement: !ruby/object:Gem::Requirement
@@ -86,44 +86,44 @@ dependencies:
86
86
  requirements:
87
87
  - - "~>"
88
88
  - !ruby/object:Gem::Version
89
- version: '0.66'
89
+ version: 0.68.0
90
90
  type: :development
91
91
  prerelease: false
92
92
  version_requirements: !ruby/object:Gem::Requirement
93
93
  requirements:
94
94
  - - "~>"
95
95
  - !ruby/object:Gem::Version
96
- version: '0.66'
96
+ version: 0.68.0
97
97
  - !ruby/object:Gem::Dependency
98
98
  name: rest-client
99
99
  requirement: !ruby/object:Gem::Requirement
100
100
  requirements:
101
101
  - - "~>"
102
102
  - !ruby/object:Gem::Version
103
- version: 2.0.0
103
+ version: 2.1.0
104
104
  type: :runtime
105
105
  prerelease: false
106
106
  version_requirements: !ruby/object:Gem::Requirement
107
107
  requirements:
108
108
  - - "~>"
109
109
  - !ruby/object:Gem::Version
110
- version: 2.0.0
110
+ version: 2.1.0
111
111
  - !ruby/object:Gem::Dependency
112
112
  name: thor
113
113
  requirement: !ruby/object:Gem::Requirement
114
114
  requirements:
115
115
  - - "~>"
116
116
  - !ruby/object:Gem::Version
117
- version: 0.20.0
117
+ version: '1.0'
118
118
  type: :runtime
119
119
  prerelease: false
120
120
  version_requirements: !ruby/object:Gem::Requirement
121
121
  requirements:
122
122
  - - "~>"
123
123
  - !ruby/object:Gem::Version
124
- version: 0.20.0
125
- description: Command-line interface to deploy and manage your Rails apps on the Swiss
126
- PaaS https://gleis.cloud
124
+ version: '1.0'
125
+ description: " Command-line interface to deploy and manage your apps on the https://gleis.io
126
+ Swiss Platform as a Service supporting Ruby, Node.js, Python and .NET Core.\n"
127
127
  email:
128
128
  - gleis@towards.ch
129
129
  executables:
@@ -165,7 +165,7 @@ files:
165
165
  - lib/gleis/token.rb
166
166
  - lib/gleis/utils.rb
167
167
  - lib/gleis/version.rb
168
- homepage: https://gleis.cloud
168
+ homepage: https://gleis.io
169
169
  licenses:
170
170
  - GPL-3.0
171
171
  metadata:
@@ -188,5 +188,5 @@ requirements: []
188
188
  rubygems_version: 3.0.3
189
189
  signing_key:
190
190
  specification_version: 4
191
- summary: Gleis CLI to deploy and manage Rails apps on the Gleis PaaS
191
+ summary: Gleis CLI to deploy and manage apps on the Gleis PaaS
192
192
  test_files: []