OSDN Git Service

Add troubleshooting hints to update hook check
[wvm/gitlab.git] / lib / tasks / gitlab / check.rake
1 namespace :gitlab do
2   desc "GITLAB | Check the configuration of GitLab and its environment"
3   task check: %w{gitlab:env:check
4                  gitlab:gitlab_shell:check
5                  gitlab:sidekiq:check
6                  gitlab:app:check}
7
8
9
10   namespace :app do
11     desc "GITLAB | Check the configuration of the GitLab Rails app"
12     task check: :environment  do
13       warn_user_is_not_gitlab
14       start_checking "GitLab"
15
16       check_database_config_exists
17       check_database_is_not_sqlite
18       check_migrations_are_up
19       check_gitlab_config_exists
20       check_gitlab_config_not_outdated
21       check_log_writable
22       check_tmp_writable
23       check_init_script_exists
24       check_init_script_up_to_date
25       check_projects_have_namespace
26       check_satellites_exist
27       check_redis_version
28       check_git_version
29
30       finished_checking "GitLab"
31     end
32
33
34     # Checks
35     ########################
36
37     def check_database_config_exists
38       print "Database config exists? ... "
39
40       database_config_file = Rails.root.join("config", "database.yml")
41
42       if File.exists?(database_config_file)
43         puts "yes".green
44       else
45         puts "no".red
46         try_fixing_it(
47           "Copy config/database.yml.<your db> to config/database.yml",
48           "Check that the information in config/database.yml is correct"
49         )
50         for_more_information(
51           see_database_guide,
52           "http://guides.rubyonrails.org/getting_started.html#configuring-a-database"
53         )
54         fix_and_rerun
55       end
56     end
57
58     def check_database_is_not_sqlite
59       print "Database is SQLite ... "
60
61       database_config_file = Rails.root.join("config", "database.yml")
62
63       unless File.read(database_config_file) =~ /adapter:\s+sqlite/
64         puts "no".green
65       else
66         puts "yes".red
67         for_more_information(
68           "https://github.com/gitlabhq/gitlabhq/wiki/Migrate-from-SQLite-to-MySQL",
69           see_database_guide
70         )
71         fix_and_rerun
72       end
73     end
74
75     def check_gitlab_config_exists
76       print "GitLab config exists? ... "
77
78       gitlab_config_file = Rails.root.join("config", "gitlab.yml")
79
80       if File.exists?(gitlab_config_file)
81         puts "yes".green
82       else
83         puts "no".red
84         try_fixing_it(
85           "Copy config/gitlab.yml.example to config/gitlab.yml",
86           "Update config/gitlab.yml to match your setup"
87         )
88         for_more_information(
89           see_installation_guide_section "GitLab"
90         )
91         fix_and_rerun
92       end
93     end
94
95     def check_gitlab_config_not_outdated
96       print "GitLab config outdated? ... "
97
98       gitlab_config_file = Rails.root.join("config", "gitlab.yml")
99       unless File.exists?(gitlab_config_file)
100         puts "can't check because of previous errors".magenta
101       end
102
103       # omniauth or ldap could have been deleted from the file
104       unless Gitlab.config['git_host']
105         puts "no".green
106       else
107         puts "yes".red
108         try_fixing_it(
109           "Backup your config/gitlab.yml",
110           "Copy config/gitlab.yml.example to config/gitlab.yml",
111           "Update config/gitlab.yml to match your setup"
112         )
113         for_more_information(
114           see_installation_guide_section "GitLab"
115         )
116         fix_and_rerun
117       end
118     end
119
120     def check_init_script_exists
121       print "Init script exists? ... "
122
123       script_path = "/etc/init.d/gitlab"
124
125       if File.exists?(script_path)
126         puts "yes".green
127       else
128         puts "no".red
129         try_fixing_it(
130           "Install the init script"
131         )
132         for_more_information(
133           see_installation_guide_section "Install Init Script"
134         )
135         fix_and_rerun
136       end
137     end
138
139     def check_init_script_up_to_date
140       print "Init script up-to-date? ... "
141
142       recipe_path = Rails.root.join("lib/support/init.d/", "gitlab")
143       script_path = "/etc/init.d/gitlab"
144
145       unless File.exists?(script_path)
146         puts "can't check because of previous errors".magenta
147         return
148       end
149
150       recipe_content = File.read(recipe_path)
151       script_content = File.read(script_path)
152
153       if recipe_content == script_content
154         puts "yes".green
155       else
156         puts "no".red
157         try_fixing_it(
158           "Redownload the init script"
159         )
160         for_more_information(
161           see_installation_guide_section "Install Init Script"
162         )
163         fix_and_rerun
164       end
165     end
166
167     def check_migrations_are_up
168       print "All migrations up? ... "
169
170       migration_status =  `bundle exec rake db:migrate:status`
171
172       unless migration_status =~ /down\s+\d{14}/
173         puts "yes".green
174       else
175         puts "no".red
176         try_fixing_it(
177           sudo_gitlab("bundle exec rake db:migrate RAILS_ENV=production")
178         )
179         fix_and_rerun
180       end
181     end
182
183     def check_satellites_exist
184       print "Projects have satellites? ... "
185
186       unless Project.count > 0
187         puts "can't check, you have no projects".magenta
188         return
189       end
190       puts ""
191
192       Project.find_each(batch_size: 100) do |project|
193         print "#{project.name_with_namespace.yellow} ... "
194
195         if project.satellite.exists?
196           puts "yes".green
197         elsif project.empty_repo?
198           puts "can't create, repository is empty".magenta
199         else
200           puts "no".red
201           try_fixing_it(
202             sudo_gitlab("bundle exec rake gitlab:satellites:create RAILS_ENV=production"),
203             "If necessary, remove the tmp/repo_satellites directory ...",
204             "... and rerun the above command"
205           )
206           for_more_information(
207             "doc/raketasks/maintenance.md "
208           )
209           fix_and_rerun
210         end
211       end
212     end
213
214     def check_log_writable
215       print "Log directory writable? ... "
216
217       log_path = Rails.root.join("log")
218
219       if File.writable?(log_path)
220         puts "yes".green
221       else
222         puts "no".red
223         try_fixing_it(
224           "sudo chown -R gitlab #{log_path}",
225           "sudo chmod -R u+rwX #{log_path}"
226         )
227         for_more_information(
228           see_installation_guide_section "GitLab"
229         )
230         fix_and_rerun
231       end
232     end
233
234     def check_tmp_writable
235       print "Tmp directory writable? ... "
236
237       tmp_path = Rails.root.join("tmp")
238
239       if File.writable?(tmp_path)
240         puts "yes".green
241       else
242         puts "no".red
243         try_fixing_it(
244           "sudo chown -R gitlab #{tmp_path}",
245           "sudo chmod -R u+rwX #{tmp_path}"
246         )
247         for_more_information(
248           see_installation_guide_section "GitLab"
249         )
250         fix_and_rerun
251       end
252     end
253
254     def check_redis_version
255       print "Redis version >= 2.0.0? ... "
256
257       if run_and_match("redis-cli --version", /redis-cli 2.\d.\d/)
258         puts "yes".green
259       else
260         puts "no".red
261         try_fixing_it(
262           "Update your redis server to a version >= 2.0.0"
263         )
264         for_more_information(
265           "gitlab-public-wiki/wiki/Trouble-Shooting-Guide in section sidekiq"
266         )
267         fix_and_rerun
268       end
269     end
270   end
271
272
273
274   namespace :env do
275     desc "GITLAB | Check the configuration of the environment"
276     task check: :environment  do
277       warn_user_is_not_gitlab
278       start_checking "Environment"
279
280       check_gitlab_git_config
281       check_python2_exists
282       check_python2_version
283
284       finished_checking "Environment"
285     end
286
287
288     # Checks
289     ########################
290
291     def check_gitlab_git_config
292       gitlab_user = Gitlab.config.gitlab.user
293       print "Git configured for #{gitlab_user} user? ... "
294
295       options = {
296         "user.name"  => "GitLab",
297         "user.email" => Gitlab.config.gitlab.email_from
298       }
299       correct_options = options.map do |name, value|
300         run("git config --global --get #{name}").try(:squish) == value
301       end
302
303       if correct_options.all?
304         puts "yes".green
305       else
306         puts "no".red
307         try_fixing_it(
308           sudo_gitlab("git config --global user.name  \"#{options["user.name"]}\""),
309           sudo_gitlab("git config --global user.email \"#{options["user.email"]}\"")
310         )
311         for_more_information(
312           see_installation_guide_section "GitLab"
313         )
314         fix_and_rerun
315       end
316     end
317
318     def check_python2_exists
319       print "Has python2? ... "
320
321       # Python prints its version to STDERR
322       # so we can't just use run("python2 --version")
323       if run_and_match("which python2", /python2$/)
324         puts "yes".green
325       else
326         puts "no".red
327         try_fixing_it(
328           "Make sure you have Python 2.5+ installed",
329           "Link it to python2"
330         )
331         for_more_information(
332           see_installation_guide_section "Packages / Dependencies"
333         )
334         fix_and_rerun
335       end
336     end
337
338     def check_python2_version
339       print "python2 is supported version? ... "
340
341       # Python prints its version to STDERR
342       # so we can't just use run("python2 --version")
343
344       unless run_and_match("which python2", /python2$/)
345         puts "can't check because of previous errors".magenta
346         return
347       end
348
349       if `python2 --version 2>&1` =~ /2\.[567]\.\d/
350         puts "yes".green
351       else
352         puts "no".red
353         try_fixing_it(
354           "Make sure you have Python 2.5+ installed",
355           "Link it to python2"
356         )
357         for_more_information(
358           see_installation_guide_section "Packages / Dependencies"
359         )
360         fix_and_rerun
361       end
362     end
363   end
364
365
366
367   namespace :gitlab_shell do
368     desc "GITLAB | Check the configuration of GitLab Shell"
369     task check: :environment  do
370       warn_user_is_not_gitlab
371       start_checking "GitLab Shell"
372
373       check_gitlab_shell
374       check_repo_base_exists
375       check_repo_base_is_not_symlink
376       check_repo_base_user_and_group
377       check_repo_base_permissions
378       check_update_hook_is_up_to_date
379       check_repos_update_hooks_is_link
380       check_gitlab_shell_self_test
381
382       finished_checking "GitLab Shell"
383     end
384
385
386     # Checks
387     ########################
388
389
390     def check_update_hook_is_up_to_date
391       print "update hook up-to-date? ... "
392
393       hook_file = "update"
394       gitlab_shell_hooks_path = Gitlab.config.gitlab_shell.hooks_path
395       gitlab_shell_hook_file  = File.join(gitlab_shell_hooks_path, hook_file)
396
397       if File.exists?(gitlab_shell_hook_file)
398         puts "yes".green
399       else
400         puts "no".red
401         puts "Could not find #{gitlab_shell_hook_file}"
402         try_fixing_it(
403           'Check the hooks_path in config/gitlab.yml',
404           'Check your gitlab-shell installation'
405         )
406         for_more_information(
407           see_installation_guide_section "GitLab Shell"
408         )
409       end
410     end
411
412     def check_repo_base_exists
413       print "Repo base directory exists? ... "
414
415       repo_base_path = Gitlab.config.gitlab_shell.repos_path
416
417       if File.exists?(repo_base_path)
418         puts "yes".green
419       else
420         puts "no".red
421         puts "#{repo_base_path} is missing".red
422         try_fixing_it(
423           "This should have been created when setting up GitLab Shell.",
424           "Make sure it's set correctly in config/gitlab.yml",
425           "Make sure GitLab Shell is installed correctly."
426         )
427         for_more_information(
428           see_installation_guide_section "GitLab Shell"
429         )
430         fix_and_rerun
431       end
432     end
433
434     def check_repo_base_is_not_symlink
435       print "Repo base directory is a symlink? ... "
436
437       repo_base_path = Gitlab.config.gitlab_shell.repos_path
438       unless File.exists?(repo_base_path)
439         puts "can't check because of previous errors".magenta
440         return
441       end
442
443       unless File.symlink?(repo_base_path)
444         puts "no".green
445       else
446         puts "yes".red
447         try_fixing_it(
448           "Make sure it's set to the real directory in config/gitlab.yml"
449         )
450         fix_and_rerun
451       end
452     end
453
454     def check_repo_base_permissions
455       print "Repo base access is drwxrws---? ... "
456
457       repo_base_path = Gitlab.config.gitlab_shell.repos_path
458       unless File.exists?(repo_base_path)
459         puts "can't check because of previous errors".magenta
460         return
461       end
462
463       if File.stat(repo_base_path).mode.to_s(8).ends_with?("2770")
464         puts "yes".green
465       else
466         puts "no".red
467         try_fixing_it(
468           "sudo chmod -R ug+rwX,o-rwx #{repo_base_path}",
469           "sudo chmod -R ug-s #{repo_base_path}",
470           "find #{repo_base_path} -type d -print0 | sudo xargs -0 chmod g+s"
471         )
472         for_more_information(
473           see_installation_guide_section "GitLab Shell"
474         )
475         fix_and_rerun
476       end
477     end
478
479     def check_repo_base_user_and_group
480       gitlab_shell_ssh_user = Gitlab.config.gitlab_shell.ssh_user
481       gitlab_shell_owner_group = Gitlab.config.gitlab_shell.owner_group
482       print "Repo base owned by #{gitlab_shell_ssh_user}:#{gitlab_shell_owner_group}? ... "
483
484       repo_base_path = Gitlab.config.gitlab_shell.repos_path
485       unless File.exists?(repo_base_path)
486         puts "can't check because of previous errors".magenta
487         return
488       end
489
490       uid = uid_for(gitlab_shell_ssh_user)
491       gid = gid_for(gitlab_shell_owner_group)
492       if File.stat(repo_base_path).uid == uid && File.stat(repo_base_path).gid == gid
493         puts "yes".green
494       else
495         puts "no".red
496         puts "  User id for #{gitlab_shell_ssh_user}: #{uid}. Groupd id for #{gitlab_shell_owner_group}: #{gid}".blue
497         try_fixing_it(
498           "sudo chown -R #{gitlab_shell_ssh_user}:#{gitlab_shell_owner_group} #{repo_base_path}"
499         )
500         for_more_information(
501           see_installation_guide_section "GitLab Shell"
502         )
503         fix_and_rerun
504       end
505     end
506
507     def check_repos_update_hooks_is_link
508       print "update hooks in repos are links: ... "
509
510       hook_file = "update"
511       gitlab_shell_hooks_path = Gitlab.config.gitlab_shell.hooks_path
512       gitlab_shell_hook_file  = File.join(gitlab_shell_hooks_path, hook_file)
513       gitlab_shell_ssh_user = Gitlab.config.gitlab_shell.ssh_user
514
515       unless File.exists?(gitlab_shell_hook_file)
516         puts "can't check because of previous errors".magenta
517         return
518       end
519
520       unless Project.count > 0
521         puts "can't check, you have no projects".magenta
522         return
523       end
524       puts ""
525
526       Project.find_each(batch_size: 100) do |project|
527         print "#{project.name_with_namespace.yellow} ... "
528
529         if project.empty_repo?
530           puts "repository is empty".magenta
531         else
532           project_hook_file = File.join(project.repository.path_to_repo, "hooks", hook_file)
533
534           unless File.exists?(project_hook_file)
535             puts "missing".red
536             try_fixing_it(
537               "sudo -u #{gitlab_shell_ssh_user} ln -sf #{gitlab_shell_hook_file} #{project_hook_file}"
538             )
539             for_more_information(
540               "#{gitlab_shell_user_home}/gitlab-shell/support/rewrite-hooks.sh"
541             )
542             fix_and_rerun
543             next
544           end
545
546           if File.lstat(project_hook_file).symlink? &&
547               File.realpath(project_hook_file) == File.realpath(gitlab_shell_hook_file)
548             puts "ok".green
549           else
550             puts "not a link to GitLab Shell's hook".red
551             try_fixing_it(
552               "sudo -u #{gitlab_shell_ssh_user} ln -sf #{gitlab_shell_hook_file} #{project_hook_file}"
553             )
554             for_more_information(
555               "lib/support/rewrite-hooks.sh"
556             )
557             fix_and_rerun
558           end
559         end
560       end
561     end
562
563     def check_gitlab_shell_self_test
564       gitlab_shell_repo_base = File.expand_path('gitlab-shell', gitlab_shell_user_home)
565       check_cmd = File.expand_path('bin/check', gitlab_shell_repo_base)
566       puts "Running #{check_cmd}"
567       if system(check_cmd, chdir: gitlab_shell_repo_base)
568         puts 'gitlab-shell self-check successful'.green
569       else
570         puts 'gitlab-shell self-check failed'.red
571         try_fixing_it(
572           'Make sure GitLab is running;',
573           'Check the gitlab-shell configuration file:',
574           sudo_gitlab("editor #{File.expand_path('config.yml', gitlab_shell_repo_base)}")
575         )
576         fix_and_rerun
577       end
578     end
579
580     def check_projects_have_namespace
581       print "projects have namespace: ... "
582
583       unless Project.count > 0
584         puts "can't check, you have no projects".magenta
585         return
586       end
587       puts ""
588
589       Project.find_each(batch_size: 100) do |project|
590         print "#{project.name_with_namespace.yellow} ... "
591
592         if project.namespace
593           puts "yes".green
594         else
595           puts "no".red
596           try_fixing_it(
597             "Migrate global projects"
598           )
599           for_more_information(
600             "doc/update/5.4-to-6.0.md in section \"#global-projects\""
601           )
602           fix_and_rerun
603         end
604       end
605     end
606
607     # Helper methods
608     ########################
609
610     def gitlab_shell_user_home
611       File.expand_path("~#{Gitlab.config.gitlab_shell.ssh_user}")
612     end
613
614     def gitlab_shell_version
615       gitlab_shell_version_file = "#{gitlab_shell_user_home}/gitlab-shell/VERSION"
616       if File.readable?(gitlab_shell_version_file)
617         File.read(gitlab_shell_version_file)
618       end
619     end
620
621     def has_gitlab_shell3?
622       gitlab_shell_version.try(:start_with?, "v3.")
623     end
624   end
625
626
627
628   namespace :sidekiq do
629     desc "GITLAB | Check the configuration of Sidekiq"
630     task check: :environment  do
631       warn_user_is_not_gitlab
632       start_checking "Sidekiq"
633
634       check_sidekiq_running
635       only_one_sidekiq_running
636
637       finished_checking "Sidekiq"
638     end
639
640
641     # Checks
642     ########################
643
644     def check_sidekiq_running
645       print "Running? ... "
646
647       if sidekiq_process_match
648         puts "yes".green
649       else
650         puts "no".red
651         try_fixing_it(
652           sudo_gitlab("bundle exec rake sidekiq:start RAILS_ENV=production")
653         )
654         for_more_information(
655           see_installation_guide_section("Install Init Script"),
656           "see log/sidekiq.log for possible errors"
657         )
658         fix_and_rerun
659       end
660     end
661
662     def only_one_sidekiq_running
663       sidekiq_match = sidekiq_process_match
664       return unless sidekiq_match
665
666       print 'Number of Sidekiq processes ... '
667       if sidekiq_match.length == 1
668         puts '1'.green
669       else
670         puts "#{sidekiq_match.length}".red
671         try_fixing_it(
672           'sudo service gitlab stop',
673           'sudo pkill -f sidekiq',
674           'sleep 10 && sudo pkill -9 -f sidekiq',
675           'sudo service gitlab start'
676         )
677         fix_and_rerun
678       end
679     end
680
681     def sidekiq_process_match
682       run_and_match("ps ux | grep -i sidekiq", /(sidekiq \d+\.\d+\.\d+.+$)/)
683     end
684   end
685
686
687   # Helper methods
688   ##########################
689
690   def fix_and_rerun
691     puts "  Please #{"fix the error above"} and rerun the checks.".red
692   end
693
694   def for_more_information(*sources)
695     sources = sources.shift if sources.first.is_a?(Array)
696
697     puts "  For more information see:".blue
698     sources.each do |source|
699       puts "  #{source}"
700     end
701   end
702
703   def finished_checking(component)
704     puts ""
705     puts "Checking #{component.yellow} ... #{"Finished".green}"
706     puts ""
707   end
708
709   def see_database_guide
710     "doc/install/databases.md"
711   end
712
713   def see_installation_guide_section(section)
714     "doc/install/installation.md in section \"#{section}\""
715   end
716
717   def sudo_gitlab(command)
718     gitlab_user = Gitlab.config.gitlab.user
719     "sudo -u #{gitlab_user} -H #{command}"
720   end
721
722   def start_checking(component)
723     puts "Checking #{component.yellow} ..."
724     puts ""
725   end
726
727   def try_fixing_it(*steps)
728     steps = steps.shift if steps.first.is_a?(Array)
729
730     puts "  Try fixing it:".blue
731     steps.each do |step|
732       puts "  #{step}"
733     end
734   end
735
736   def check_gitlab_shell
737     required_version = Gitlab::VersionInfo.new(1, 7, 1)
738     current_version = Gitlab::VersionInfo.parse(gitlab_shell_version)
739
740     print "GitLab Shell version >= #{required_version} ? ... "
741     if current_version.valid? && required_version <= current_version
742       puts "OK (#{current_version})".green
743     else
744       puts "FAIL. Please update gitlab-shell to #{required_version} from #{current_version}".red
745     end
746   end
747
748   def check_git_version
749     required_version = Gitlab::VersionInfo.new(1, 7, 10)
750     current_version = Gitlab::VersionInfo.parse(run("#{Gitlab.config.git.bin_path} --version"))
751
752     puts "Your git bin path is \"#{Gitlab.config.git.bin_path}\""
753     print "Git version >= #{required_version} ? ... "
754
755     if current_version.valid? && required_version <= current_version
756         puts "yes (#{current_version})".green
757     else
758       puts "no".red
759       try_fixing_it(
760         "Update your git to a version >= #{required_version} from #{current_version}"
761       )
762       fix_and_rerun
763     end
764   end
765 end