Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
path: root/lib/gitorious/diagnostics.rb
blob: 6f76c4add5e7f8355c785f74d057a687dcd07931 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
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
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
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
297

# encoding: utf-8
#--
#   Copyright (C) 2012 Gitorious AS
#
#   This program is free software: you can redistribute it and/or modify
#   it under the terms of the GNU Affero General Public License as published by
#   the Free Software Foundation, either version 3 of the License, or
#   (at your option) any later version.
#
#   This program is distributed in the hope that it will be useful,
#   but WITHOUT ANY WARRANTY; without even the implied warranty of
#   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#   GNU Affero General Public License for more details.
#
#   You should have received a copy of the GNU Affero General Public License
#   along with this program.  If not, see <http://www.gnu.org/licenses/>.
#++


module Gitorious
  module Diagnostics

    # Overall
    
    def everything_healthy?
      #git_operations_work? &&
        web_interface_reachable? &&
        git_user_ok? &&
        rails_process_owned_by_git_user? &&
        atleast_one_gitorious_account_present? &&
        repo_dir_ok? &&
        tarball_dirs_ok? &&
        authorized_keys_ok? &&
        not_using_reserved_hostname? &&
        ssh_deamon_up? &&
        git_daemon_up? &&
        poller_up? &&
        mysql_up? &&
        ultrasphinx_up? &&
        queue_service_up? &&
        memcached_up? &&
        enough_disk_free? &&
        enough_RAM_free? &&
        healthy_cpu_load_average?
    end

    def ascii_test(label)
      test_result = yield
      green = "\x1b[32m"
      red = "\x1b[31m"
      status_color = test_result ? green : red
      reset = "\x1b[0m"
      ("#{status_color}#{label}".ljust(50)+"#{test_result}#{reset}")
    end
    
    def health_text_summary
      puts ascii_test("everything healthy?"){ everything_healthy? }

      # @git operations work?  #{git_operations_work?}"
      puts ascii_test("git user ok?") {git_user_ok?}
      puts ascii_test("gitorius front web page is up?"){web_interface_reachable?}
      puts ascii_test("rails process present & owned by git user?"){rails_process_owned_by_git_user?}
      puts ascii_test("atleast one gitorious account present?") {atleast_one_gitorious_account_present?}
      puts ascii_test("repo base dir present, owned by git user?"){repo_dir_ok?}
      puts ascii_test("tarball dirs present, owned by git user?"){tarball_dirs_ok?}
      puts ascii_test("git user has ~/.ssh/authorized_keys file?"){authorized_keys_ok?}
      puts ascii_test("hostname not bound to a 'git.*' subdomain?"){not_using_reserved_hostname?}

      puts ascii_test("ssh deamon is up?"){ssh_deamon_up?}
      puts ascii_test("git daemon is up?"){git_daemon_up?}
      puts ascii_test("poller is up?"){poller_up?}
      puts ascii_test("mysql is up?"){mysql_up?}
      puts ascii_test("ultrasphinx is up?"){ultrasphinx_up?}
      puts ascii_test("queue service is up?"){queue_service_up?}
      puts ascii_test("memcached is up?"){memcached_up?}

      puts ascii_test("enough disk free?"){enough_disk_free?}
      puts ascii_test("enough RAM free?"){enough_RAM_free?}
      puts ascii_test("healthy cpu load average?"){healthy_cpu_load_average?}

      puts "\n\nuptime:\n"
      puts `uptime`
      puts "\n\nfree:\n"
      puts `free -m`
      puts "\n\nvmstat:\n"
      puts `vmstat`
      puts "\n\ndf:\n"
      puts `df -h`
    end

    
    
    # Core functionality

    # TODO finish this one and wire it up 
    def git_operations_work?
      false
      # Needs initial config of test user/key
      # Add initial step during server config
      # throw useful error in web console if this isnt done
     
      # test project/repo
      # needs corresponding public key for a matching user in app
      # needs keypair for the gitorious user running the test
      # Could seed db with test user, and create priv key on first run
      # of diagnostic tool?
      # CAN TEST This seeding by grepping authorized_keys file
      
      # shell out, test clone/push/pull or just git ls-remote? of test repo
      # ssh -i /var/www/gitorious/data/seeded_private_key

      # do cleanup before and after test run
    end
    
    def web_interface_reachable?
      `curl --silent localhost:80 | grep Gitorious | wc -l`.to_i > 0 
    end

    def git_user_ok?
      user_exists?(git_user)
    end

    def rails_process_owned_by_git_user?
      `ps U #{git_user} | grep Rails | wc -l`.to_i > 0
    end
    
    def atleast_one_gitorious_account_present?
      User.count > 0
    end

    def repo_dir_ok?
      path = RepositoryRoot.default_base_path
      (dir_present?(path) && owned_by_user?(path, git_user))
    end

    def tarball_dirs_ok?
      cache_path = GitoriousConfig["archive_cache_dir"]
      work_path = GitoriousConfig["archive_work_dir"]
      
      (dir_present?(cache_path) &&
       owned_by_user?(cache_path, git_user) &&
       dir_present?(work_path) &&
       owned_by_user?(work_path, git_user))
    end

    def authorized_keys_ok?
      path = File.expand_path("~/.ssh/authorized_keys")
      (file_present?(path) && owned_by_user?(path, git_user))
    end

    def not_using_reserved_hostname?
      !GitoriousConfig.using_reserved_hostname?
    end

    # TODO impl and wire this one up as well
    def outbound_mail_delivery_working?
      false
    end
    
    # TODO impl and wire this one up as well
    def public_mode_correctly_setup?
      false
    end

    # Services and daemons

    def ssh_deamon_up?
      atleast_one_process_name_matching("sshd")
    end

    def git_daemon_up?
      # TODO handle all known alternatives: git-proxy, <nothing>
      # + in gitorious.org case, also haproxy
      atleast_one_process_name_matching("git-daemon") ||
        atleast_one_process_name_matching("git-proxy")
    end

    def poller_up?
      atleast_one_process_name_matching("poller")
    end

    def mysql_up?
      atleast_one_process_name_matching("mysqld")
    end

    def ultrasphinx_up?
      atleast_one_process_name_matching("searchd")
      # TODO + does it respond on configured port, expected by rails app?
      # TODO check ps -o for pid, we got the pidfile for it
    end

    def queue_service_up?
      if GitoriousConfig["messaging_adapter"] != "sync"
        return (atleast_one_process_name_matching("stomp") ||
                atleast_one_process_name_matching("resque") ||
                atleast_one_process_name_matching("activemq"))
      else
        true
      end
      # TODO can we ping stomp? queue service can be on remote box....
      # TODO just check if there's anything on specified port for queue service
    end

    def memcached_up?
      atleast_one_process_name_matching("memcached")
    end

    # TODO impl and wire this one up as well
    def xsendfile_enabled?
      # NOTE: should only be used for apache
      # Only useful/true if http_cloning is allowed
      
      # TODO, one or two appropaches
      #1: enabled and confed in apache
      #2: can do:
      # curl
      # http://git.[gitorious_host]/gitorious/mainline.git/info/refs
      # -> fil
    end

   
    # Host system health
    
    MAX_HEALTHY_DISK_USAGE = 90 #%
    
    def enough_disk_free?
      percent_str = `df -Ph #{RepositoryRoot.default_base_path} | awk 'NR==2 {print $5}'`
      percent_str.chomp "%"
      percent_free = percent_free.to_i
      return (percent_free < MAX_HEALTHY_DISK_USAGE)
    end

    MAX_HEALTHY_RAM_USAGE = 90 #%
    
    def enough_RAM_free?
      free_numbers = `free -mt | tail -n 1`.chomp.split(" ")
      total = free_numbers[1].to_i
      free = free_numbers[3].to_i
      percent_free = (free*100)/total
      return (percent_free > (100-MAX_HEALTHY_RAM_USAGE))
    end

    MAX_HEALTHY_CPU_LOAD = 90 #%
    
    def healthy_cpu_load_average?
      load_percent_last_15_min = `uptime`.chomp.split(" ").last.to_f
      return (load_percent_last_15_min < MAX_HEALTHY_CPU_LOAD.to_f)
    end


    
    private
     
    def atleast_one_process_name_matching(str)
      matching_processes_count = (`ps -ef | grep #{str} | grep -v grep | wc -l`.to_i)      
      matching_processes_count > 0
    end

    def dir_present?(path)
      Dir[path].count > 0
    end

    def file_present?(path)
      File.exist?(path)
    end

    def owned_by_user?(path, username)
      file_owner_uid = File.stat(path).uid.to_i
      user_uid = `id -u #{username}`.chomp.to_i
      (user_uid != 0 && (file_owner_uid == user_uid))
    end

    def user_exists?(username)
      `grep '^#{username}' /etc/passwd`
      ($? == 0)
    end

    def current_user?(username)
      ENV['USER'] == username
    end

    def git_user
      GitoriousConfig["gitorious_user"]
    end
  end
end