Module: Gitlab::TaskHelpers
- Extended by:
- TaskHelpers
- Includes:
- Utils::StrongMemoize
- Included in:
- Backup::Manager, TaskHelpers, SystemCheck::Helpers
- Defined in:
- lib/gitlab/task_helpers.rb
Instance Method Summary collapse
-
#ask_to_continue ⇒ Object
Ask if the user wants to continue.
- #checkout_or_clone_version(version:, repo:, target_dir:, clone_opts: [], checkout_opts: []) ⇒ Object
- #checkout_version(version, target_dir, checkout_opts: []) ⇒ Object
- #clone_repo(repo, target_dir, clone_opts: []) ⇒ Object
- #download_package_file_version(version:, repo:, package_name:, package_file:, package_checksums_sha256:, target_path:) ⇒ Object
- #get_partition_info(partition_name, connection) ⇒ Object
-
#get_version(component_version) ⇒ Object
this function implements the same logic we have in omnibus for dealing with components version.
- #gid_for(group_name) ⇒ Object
- #gitlab_user ⇒ Object
- #gitlab_user? ⇒ Boolean
- #invoke_and_time_task(task) ⇒ Object
-
#os_name ⇒ Object
Check which OS is running.
-
#prompt(message, choices = nil) ⇒ Object
Prompt the user to input something.
-
#prompt_for_password(message = 'Enter password: ') ⇒ Object
Prompt the user to input a password.
-
#run_and_match(command, regexp) ⇒ Object
Runs the given command and matches the output against the given pattern.
-
#run_command(command) ⇒ Object
Runs the given command.
-
#run_command!(command) ⇒ Object
Runs the given command and raises a Gitlab::TaskFailedError exception if the command does not exit with 0.
- #uid_for(user_name) ⇒ Object
- #user_home ⇒ Object
- #warn_user_is_not_gitlab ⇒ Object
Instance Method Details
#ask_to_continue ⇒ Object
Ask if the user wants to continue
Returns “yes” the user chose to continue Raises Gitlab::TaskAbortedByUserError if the user chose not to continue
27 28 29 30 31 32 |
# File 'lib/gitlab/task_helpers.rb', line 27 def ask_to_continue return if Gitlab::Utils.to_boolean(ENV['GITLAB_ASSUME_YES']) answer = prompt(Rainbow("Do you want to continue (yes/no)? ").blue, %w[yes no]) raise Gitlab::TaskAbortedByUserError unless answer == "yes" end |
#checkout_or_clone_version(version:, repo:, target_dir:, clone_opts: [], checkout_opts: []) ⇒ Object
215 216 217 218 |
# File 'lib/gitlab/task_helpers.rb', line 215 def checkout_or_clone_version(version:, repo:, target_dir:, clone_opts: [], checkout_opts: []) clone_repo(repo, target_dir, clone_opts: clone_opts) unless Dir.exist?(target_dir) checkout_version(get_version(version), target_dir, checkout_opts: checkout_opts) end |
#checkout_version(version, target_dir, checkout_opts: []) ⇒ Object
233 234 235 236 237 238 239 240 241 242 |
# File 'lib/gitlab/task_helpers.rb', line 233 def checkout_version(version, target_dir, checkout_opts: []) # Explicitly setting the git protocol version to v2 allows older Git binaries # to do have a shallow clone obtain objects by object ID. run_command!(%W[#{Gitlab.config.git.bin_path} -C #{target_dir} config protocol.version 2]) run_command!(%W[#{Gitlab.config.git.bin_path} -C #{target_dir} fetch --quiet origin #{version}]) run_command!( %W[#{Gitlab.config.git.bin_path} -C #{target_dir} checkout -f --quiet FETCH_HEAD] + checkout_opts + %w[--] ) end |
#clone_repo(repo, target_dir, clone_opts: []) ⇒ Object
229 230 231 |
# File 'lib/gitlab/task_helpers.rb', line 229 def clone_repo(repo, target_dir, clone_opts: []) run_command!(%W[#{Gitlab.config.git.bin_path} clone] + clone_opts + %W[-- #{repo} #{target_dir}]) end |
#download_package_file_version(version:, repo:, package_name:, package_file:, package_checksums_sha256:, target_path:) ⇒ Object
157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 |
# File 'lib/gitlab/task_helpers.rb', line 157 def download_package_file_version( version:, repo:, package_name:, package_file:, package_checksums_sha256:, target_path:) project_path = repo .delete_prefix('https://gitlab.com/') .delete_suffix('.git') uri = URI( format('https://gitlab.com/api/v4/projects/%{path}/packages/generic/%{name}/%{version}/%{file}', path: CGI.escape(project_path), name: CGI.escape(package_name), version: CGI.escape(version), file: CGI.escape(package_file) )) success = true Tempfile.create(package_file, binmode: true) do |file| Net::HTTP.start(uri.host, uri.port, use_ssl: true) do |http| request = Net::HTTP::Get.new uri http.request(request) do |response| if response.code == '302' # Allow redirects elsif response.code == '200' response.read_body do |fragment| file.write(fragment) end else warn "HTTP Code: #{response.code} for #{uri}" success = false break end end file.close if success expected = package_checksums_sha256[package_file] actual = Digest::SHA256.file(file.path).hexdigest unless expected == actual raise <<~MESSAGE ERROR: Checksum mismatch for `#{package_file}`: Expected: #{expected.inspect} Actual: #{actual.inspect} MESSAGE end FileUtils.mkdir_p(File.dirname(target_path)) FileUtils.mv(file, target_path) end end end success end |
#get_partition_info(partition_name, connection) ⇒ Object
244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 |
# File 'lib/gitlab/task_helpers.rb', line 244 def get_partition_info(partition_name, connection) partition_info_query = <<~SQL SELECT child_ns.nspname AS target_schema, child.relname AS target_partition, parent_ns.nspname AS parent_schema, parent.relname AS parent_table, (inh.inhrelid IS NOT NULL) AS is_attached, pg_get_expr(child.relpartbound, child.oid) AS partition_bounds FROM pg_class child JOIN pg_namespace child_ns ON child.relnamespace = child_ns.oid LEFT JOIN pg_inherits inh ON child.oid = inh.inhrelid LEFT JOIN pg_class parent ON inh.inhparent = parent.oid LEFT JOIN pg_namespace parent_ns ON parent.relnamespace = parent_ns.oid WHERE child.relname = $1 LIMIT 1 SQL # We have to normalize check_clause formatting between PG16 and PG17. # PG16 returns: ((partition_id = 100)) with double parentheses # PG17 returns: (partition_id = 100) with single parentheses # # The regex strips one layer of parentheses from PG16's format to match PG17. check_constraints_query = <<~'SQL' SELECT tc.constraint_name, format('CONSTRAINT %I CHECK (%s)', tc.constraint_name, regexp_replace(cc.check_clause, '^\(\(([^()]+)\)\)$', '(\1)') ) AS constraint_clause, regexp_replace(cc.check_clause, '^\(\(([^()]+)\)\)$', '(\1)') AS raw_check_clause FROM information_schema.table_constraints tc JOIN information_schema.check_constraints cc ON tc.constraint_name = cc.constraint_name AND tc.constraint_schema = cc.constraint_schema WHERE tc.table_schema = $1 AND tc.table_name = $2 AND tc.constraint_type = 'CHECK' ORDER BY tc.constraint_name; SQL partition_info = connection.exec_query(partition_info_query, nil, [partition_name]) return if partition_info.empty? partition_info = partition_info.first check_constraints = connection.exec_query( check_constraints_query, nil, [partition_info['target_schema'], partition_name] ).to_a partition_info.merge({ 'check_constraints' => check_constraints }) end |
#get_version(component_version) ⇒ Object
this function implements the same logic we have in omnibus for dealing with components version
221 222 223 224 225 226 227 |
# File 'lib/gitlab/task_helpers.rb', line 221 def get_version(component_version) # If not a valid version string following SemVer it is probably a branch name or a SHA # commit of one of our own component so it doesn't need `v` prepended return component_version unless /^\d+\.\d+\.\d+(-rc\d+)?$/.match?(component_version) "v#{component_version}" end |
#gid_for(group_name) ⇒ Object
122 123 124 125 126 |
# File 'lib/gitlab/task_helpers.rb', line 122 def gid_for(group_name) Etc.getgrnam(group_name).gid rescue ArgumentError # no group "group #{group_name} doesn't exist" end |
#gitlab_user ⇒ Object
128 129 130 |
# File 'lib/gitlab/task_helpers.rb', line 128 def gitlab_user Gitlab.config.gitlab.user end |
#gitlab_user? ⇒ Boolean
132 133 134 135 136 137 |
# File 'lib/gitlab/task_helpers.rb', line 132 def gitlab_user? strong_memoize(:is_gitlab_user) do current_user = run_command(%w[whoami]).chomp current_user == gitlab_user end end |
#invoke_and_time_task(task) ⇒ Object
17 18 19 20 21 |
# File 'lib/gitlab/task_helpers.rb', line 17 def invoke_and_time_task(task) start = Time.now Rake::Task[task].invoke puts "`#{task}` finished in #{Time.now - start} seconds" end |
#os_name ⇒ Object
Check which OS is running
It will primarily use lsb_relase to determine the OS. It has fallbacks to Debian, SuSE, OS X and systems running systemd.
38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 |
# File 'lib/gitlab/task_helpers.rb', line 38 def os_name os_name = run_command(%w[lsb_release -irs]) os_name ||= if File.readable?('/etc/system-release') File.read('/etc/system-release') elsif File.readable?('/etc/debian_version') "Debian #{File.read('/etc/debian_version')}" elsif File.readable?('/etc/SuSE-release') File.read('/etc/SuSE-release') elsif os_x_version = run_command(%w[sw_vers -productVersion]) "Mac OS X #{os_x_version}" elsif File.readable?('/etc/os-release') File.read('/etc/os-release').match(/PRETTY_NAME=\"(.+)\"/)[1] end os_name.try(:squish) end |
#prompt(message, choices = nil) ⇒ Object
Prompt the user to input something
message - the message to display before input choices - array of strings of acceptable answers or nil for any answer
Returns the user’s answer
62 63 64 65 66 67 68 |
# File 'lib/gitlab/task_helpers.rb', line 62 def prompt(, choices = nil) begin print() answer = $stdin.gets.chomp end while choices.present? && choices.exclude?(answer) answer end |
#prompt_for_password(message = 'Enter password: ') ⇒ Object
Prompt the user to input a password
message - custom message to display before input
73 74 75 76 77 78 79 80 |
# File 'lib/gitlab/task_helpers.rb', line 73 def prompt_for_password( = 'Enter password: ') unless $stdin.tty? print() return $stdin.gets.chomp end $stdin.getpass() end |
#run_and_match(command, regexp) ⇒ Object
Runs the given command and matches the output against the given pattern
Returns nil if nothing matched Returns the MatchData if the pattern matched
see also #run_command see also String#match
89 90 91 |
# File 'lib/gitlab/task_helpers.rb', line 89 def run_and_match(command, regexp) run_command(command).try(:match, regexp) end |
#run_command(command) ⇒ Object
Runs the given command
Returns ” if the command was not found Returns the output of the command otherwise
see also #run_and_match
99 100 101 102 103 104 |
# File 'lib/gitlab/task_helpers.rb', line 99 def run_command(command) output, _ = Gitlab::Popen.popen(command) output rescue Errno::ENOENT '' # if the command does not exist, return an empty string end |
#run_command!(command) ⇒ Object
Runs the given command and raises a Gitlab::TaskFailedError exception if the command does not exit with 0
Returns the output of the command otherwise
110 111 112 113 114 115 116 |
# File 'lib/gitlab/task_helpers.rb', line 110 def run_command!(command) output, status = Gitlab::Popen.popen(command) raise Gitlab::TaskFailedError, output unless status == 0 output end |
#uid_for(user_name) ⇒ Object
118 119 120 |
# File 'lib/gitlab/task_helpers.rb', line 118 def uid_for(user_name) run_command(%W[id -u #{user_name}]).chomp.to_i end |
#user_home ⇒ Object
153 154 155 |
# File 'lib/gitlab/task_helpers.rb', line 153 def user_home Rails.env.test? ? Rails.root.join('tmp/tests') : Gitlab.config.gitlab.user_home end |
#warn_user_is_not_gitlab ⇒ Object
139 140 141 142 143 144 145 146 147 148 149 150 151 |
# File 'lib/gitlab/task_helpers.rb', line 139 def warn_user_is_not_gitlab return if gitlab_user? strong_memoize(:warned_user_not_gitlab) do current_user = run_command(%w[whoami]).chomp puts Rainbow(" Warning ").color(:black).background(:yellow) puts " You are running as user #{Rainbow(current_user).magenta}, we hope you know what you are doing." puts " Things may work\/fail for the wrong reasons." puts " For correct results you should run this as user #{Rainbow(gitlab_user).magenta}." puts "" end end |