checkson 0.1 → 0.6

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: 7ab2f87748c7e3e77a1039fd9b7fff153687069c01edfb7ac94e8594cab6f04f
4
- data.tar.gz: 022660ab8a3dc7b6c324bf109e8a598793702f29fad90b426a7e7afc432d4fb3
3
+ metadata.gz: 8884f708c7fbe9c64243ce90b8942d822ee0bf3b664f1198e97198c7af339b1d
4
+ data.tar.gz: 9e1289046c0f46788c918b3b611f6424a2c15fe632523487d7f6750faa2c3c06
5
5
  SHA512:
6
- metadata.gz: 86a5f89b4cd75ee3302a40decd676c8f9b0b38fc4963fcecb92055fddae5a4b1a0d5fcdf59a5fab7ce92066cd3afdfe6533897a97b8c5aad3ecc580cd49d5ceb
7
- data.tar.gz: 7adaf87b8b50bb79a28900eae335f35930bc0413f9c8e5ac61fb826356296adcd6ea9df0cb541ff79f78300dc55ebeca7b455eb4aaa00163796334c840b458a6
6
+ metadata.gz: aacb646e006f6af49070ad5f7e299bdb546c8cfea6c6b3d99fb92aea3d9f361f13b523f3f3546951d77b9a2ccba7b2a50ac19661b7326a061c05224aaa1e53e2
7
+ data.tar.gz: 76fde610194ed4e82fc4d653b18bed353dc0d269e1d99079faada485f29806c5e27531e2b601c360bb6af9145f65d4c70db9027a2509227ec397ced2de0f6d61
data/README.md CHANGED
@@ -1,3 +1,4 @@
1
1
  # Checkson
2
2
  [![pipeline status](https://gitlab.fsrv.xyz/fsrv/checkson/badges/master/pipeline.svg)](https://gitlab.fsrv.xyz/fsrv/checkson/commits/master)
3
+ [![Gem Version](https://badge.fury.io/rb/checkson.svg)](https://badge.fury.io/rb/checkson)
3
4
 
data/bin/checkson CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env ruby
2
2
  # frozen_string_literal: true
3
3
 
4
- require_relative '../lib/checkson'
4
+ require 'checkson'
5
5
  Checkson::UI.new
data/lib/checkson.rb CHANGED
@@ -1,7 +1,15 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative 'checkson/ui'
4
- require_relative 'checkson/context'
5
- require_relative 'checkson/config'
6
- require_relative 'checkson/checks/base'
7
- require_relative 'checkson/checks/shell'
3
+ module Checkson
4
+ autoload :Config, 'checkson/config'
5
+ autoload :Context, 'checkson/context'
6
+ autoload :APIClient, 'checkson/apiclient'
7
+ autoload :UI, 'checkson/ui'
8
+
9
+ module Check
10
+ autoload :Base, 'checkson/checks/base'
11
+ autoload :DNS, 'checkson/checks/dns'
12
+ autoload :Shell, 'checkson/checks/shell'
13
+ autoload :Process, 'checkson/checks/process'
14
+ end
15
+ end
@@ -0,0 +1,75 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'net/http'
4
+ require 'json'
5
+ require 'uri'
6
+ require 'erb'
7
+ require 'socket'
8
+ require 'digest'
9
+ require 'logger'
10
+
11
+ module Checkson
12
+ class APIClient
13
+ attr_reader :apiaddr
14
+ attr_reader :outfile
15
+ def initialize(apiaddr, outfile)
16
+ @apiaddr = apiaddr
17
+ @outfile = outfile
18
+ end
19
+
20
+ def getchecks
21
+ template = <<-TEMPLATE
22
+ <%@checks.each do |check| %>
23
+ check '<%=check["description"]%>' do
24
+ using <%=check["check"]%>
25
+ <%check["sets"].each do |key,value| %>
26
+ set :<%=key%>, "<%=value%>"
27
+ <%end%>
28
+ <%unless check["helps"].eql?(nil)%>
29
+ <%check["helps"].each do |help| %>
30
+ help '<%= help %>'
31
+ <%end%>
32
+ <%end%>
33
+ end
34
+ <% end %>
35
+ TEMPLATE
36
+ querydata = get
37
+ @checks = querydata['checks'] if querydata['status'].eql? 'ok'
38
+ begin
39
+ remove_empty_lines(ERB.new(template).result(binding))
40
+ rescue StandardError
41
+ warn querydata['error']
42
+ end
43
+ end
44
+
45
+ def writechecks
46
+ File.open(@outfile, 'w') do |f|
47
+ f.write(getchecks)
48
+ end
49
+ end
50
+
51
+ private
52
+
53
+ def remove_empty_lines(string)
54
+ regex = /^[\s]*$\n/
55
+ string.gsub(regex, '')
56
+ end
57
+
58
+ def get
59
+ token = ENV['CHECKSON_API_KEY']
60
+ token ||= 'empty'
61
+ hostname = Socket.gethostname
62
+ query("#{keyhash(token)}/get/#{hostname}")
63
+ end
64
+
65
+ def query(command)
66
+ @ep = "#{@apiaddr}/api/v1"
67
+ route = "#{@ep}/#{command}"
68
+ JSON.parse(Net::HTTP.get(URI.parse(route)))
69
+ end
70
+
71
+ def keyhash(token)
72
+ Digest::SHA512.hexdigest(token)
73
+ end
74
+ end
75
+ end
@@ -1,31 +1,36 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- class Base
4
- attr_reader :messages
5
-
6
- def initialize(_opts = {})
7
- @status = :ok
8
- end
9
-
10
- def ok?
11
- @status == :ok
12
- end
13
-
14
- def failed?
15
- !ok?
16
- end
17
-
18
- protected
19
-
20
- def log(message)
21
- @messages << message
22
- end
23
-
24
- def ok!
25
- @status = :ok
26
- end
27
-
28
- def failed!
29
- @status = :failed
3
+ module Checkson
4
+ module Check
5
+ class Base
6
+ attr_reader :status, :messages
7
+
8
+ def initialize(_opts = {})
9
+ @messages = []
10
+ @status = :ok
11
+ end
12
+
13
+ def ok?
14
+ @status == :ok
15
+ end
16
+
17
+ def failed?
18
+ !ok?
19
+ end
20
+
21
+ protected
22
+
23
+ def log(message)
24
+ @messages << message
25
+ end
26
+
27
+ def ok!
28
+ @status = :ok
29
+ end
30
+
31
+ def failed!
32
+ @status = :failed
33
+ end
34
+ end
30
35
  end
31
36
  end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Checkson
4
+ module Check
5
+ class DNS < Checkson::Check::Base
6
+ def initialize(opts = {})
7
+ @opts = { domain: 'heise.de',
8
+ timeout: 1,
9
+ host: 'localhost' }.merge(opts)
10
+ super()
11
+ end
12
+
13
+ def check
14
+ require 'resolv'
15
+ resolver = Resolv::DNS.new(nameserver: [@opts[:host]])
16
+ resolver.timeouts = @opts[:timeout]
17
+ begin
18
+ resolver.getaddress(@opts[:domain])
19
+ rescue Resolv::ResolvTimeout
20
+ failed!
21
+ log("Resolving #{@opts[:domain]} timed out")
22
+ rescue Resolv::ResolvError
23
+ failed!
24
+ log("Could not resolve #{@opts[:domain]}")
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Checkson
4
+ module Check
5
+ module Pkganager
6
+ class AbstractPkgmanager
7
+ def self.descendants
8
+ ObjectSpace.each_object(Class).select { |klass| klass < self }
9
+ end
10
+ end
11
+
12
+ class Pkgmanager < AbstractPkgmanager
13
+ def self.adapters
14
+ AbstractPkgmanager.descendants - [self]
15
+ end
16
+
17
+ def self.adapter
18
+ @adapter ||= adapters.find { |adapter| adapter.supports_os? Facter['osfamily'].value.downcase }
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Checkson
4
+ module Check
5
+ class Shell < Checkson::Check::Base
6
+ def initialize(opts = {})
7
+ @opts = (@opts || {}).merge(opts)
8
+ super()
9
+ end
10
+
11
+ def check
12
+ raise ArgumentError, 'No code given' unless @opts[:code]
13
+
14
+ execute(@opts[:code])
15
+ failed! unless $?.exitstatus.zero?
16
+ end
17
+
18
+ protected
19
+
20
+ def execute(command)
21
+ `sh -c "#{command.gsub(/"/, '\"')}"`
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'ipaddr'
4
+
5
+ module Checkson
6
+ module Check
7
+ class Process < Checkson::Check::Shell
8
+ def initialize(opts = {})
9
+ @opts = opts
10
+ raise ArgumentError, 'Neither pidfile nor process name given' unless @opts[:pidfile] || @opts[:name]
11
+
12
+ super()
13
+ end
14
+
15
+ def check
16
+ @opts[:pidfile] ? check_pidfile : check_name
17
+ end
18
+
19
+ protected
20
+
21
+ def check_pidfile
22
+ pid = File.read(@opts[:pidfile]).strip
23
+ execute("kill -0 #{pid} 2>&1")
24
+
25
+ unless $?.exitstatus.zero?
26
+ failed!
27
+ log("Process ID #{pid} is not running")
28
+ end
29
+ rescue Errno::ENOENT
30
+ failed!
31
+ log("Cannot open pidfile: #{@opts[:pidfile]}")
32
+ end
33
+
34
+ def check_name
35
+ ps = execute('ps aux')
36
+
37
+ if @opts[:name].is_a?(Regexp) && ps !~ (@opts[:name])
38
+ failed!
39
+ log("No process found matching regular expression `#{@opts[:name].source}`")
40
+ elsif !ps.include?(@opts[:name])
41
+ failed!
42
+ log("No process found matching name `#{@opts[:name]}`")
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end
@@ -1,21 +1,25 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- class Shell < Base
4
- def initialize(opts = {})
5
- @opts = (@opts || {}).merge(opts)
6
- super()
7
- end
3
+ module Checkson
4
+ module Check
5
+ class Shell < Checkson::Check::Base
6
+ def initialize(opts = {})
7
+ @opts = (@opts || {}).merge(opts)
8
+ super()
9
+ end
8
10
 
9
- def check
10
- raise ArgumentError, 'No code given' unless @opts[:code]
11
+ def check
12
+ raise ArgumentError, 'No code given' unless @opts[:code]
11
13
 
12
- execute(@opts[:code])
13
- failed! unless $?.exitstatus.zero?
14
- end
14
+ execute(@opts[:code])
15
+ failed! unless $?.exitstatus.zero?
16
+ end
15
17
 
16
- protected
18
+ protected
17
19
 
18
- def execute(command)
19
- `sh -c "#{command.gsub(/"/, '\"')}"`
20
+ def execute(command)
21
+ `sh -c "#{command.gsub(/"/, '\"')}"`
22
+ end
23
+ end
20
24
  end
21
25
  end
@@ -5,6 +5,7 @@ require 'ostruct'
5
5
  module Checkson
6
6
  class Config
7
7
  attr_reader :checks, :name
8
+ include Checkson::Check
8
9
 
9
10
  def initialize(file)
10
11
  @checks = []
@@ -18,7 +19,8 @@ module Checkson
18
19
  @checks << OpenStruct.new(
19
20
  klass: context.klass,
20
21
  description: description,
21
- params: context.params
22
+ params: context.params,
23
+ help: context.help
22
24
  )
23
25
  end
24
26
  end
@@ -6,6 +6,7 @@ module Checkson
6
6
  def initialize(&block)
7
7
  @kass = nil
8
8
  @params = {}
9
+ @help = []
9
10
  instance_eval(&block)
10
11
  end
11
12
 
@@ -16,5 +17,10 @@ module Checkson
16
17
  def set(key, value)
17
18
  @params[key.to_sym] = value
18
19
  end
20
+
21
+ def help(message = nil)
22
+ @help << message if message
23
+ @help
24
+ end
19
25
  end
20
26
  end
data/lib/checkson/ui.rb CHANGED
@@ -1,6 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'optparse'
4
+ require 'logger'
5
+ require 'uri'
4
6
 
5
7
  module Checkson
6
8
  # Simple user interface for checkson
@@ -8,37 +10,84 @@ module Checkson
8
10
  def initialize
9
11
  set_default_options
10
12
  parse_options
11
- start_cfgd
13
+ start_checks
12
14
  end
13
15
 
14
- protected
16
+ private
15
17
 
16
- def start_cfgd
17
- @config = Checkson::Config.new(@opts[:config])
18
+ def start_checks
19
+ if @opts[:datasource].eql? :api
20
+ client = Checkson::APIClient.new(@opts[:endpoint], '/tmp/checkson.rb')
21
+ client.writechecks
22
+ @opts[:config_file] = client.outfile
23
+ end
24
+ @config = Checkson::Config.new(@opts[:config_file])
25
+
26
+ @num_checks = @config.checks.length
27
+ @num_ok = 0
18
28
  @config.checks.each do |check|
19
29
  c = check.klass.new(check.params)
20
30
  c.check
21
- puts c.ok?
31
+ @num_ok += 1 if c.status.eql? :ok
32
+ output c, check if @opts[:verbose]
22
33
  end
34
+ @num_failed = (@num_checks - @num_ok)
35
+ puts "-> #{@num_checks} checks performed: #{@num_ok} ok, #{@num_failed} failed"
23
36
  end
24
37
 
25
38
  def set_default_options
26
39
  @opts = {
27
- config: '/etc/checkson.rb'
40
+ datasource: :file,
41
+ verbose: false,
42
+ config_file: '/etc/checkson.rb',
43
+ endpoint: 'https://checkson.fsrv.services:8080'
28
44
  }
29
45
  end
30
46
 
31
47
  def parse_options
32
48
  OptionParser.new do |opts|
33
49
  opts.banner = 'Usage: checkson [options]'
34
- opts.on('-c', '--config FILE', 'Config file to use') do |file|
50
+ opts.on('-v', '--[no-]verbose', 'Run verbosely') do |v|
51
+ @opts[:verbose] = v
52
+ end
53
+ opts.on('-d', '--datasource [TYPE]', %i[file api], 'Set datasource type', '(file, api)') do |d|
54
+ @opts[:datasource] = d
55
+ end
56
+ opts.on('-f', '--file [FILE]', 'Config file to use') do |file|
35
57
  if File.exist?(file)
36
- @opts[:config] = file
58
+ @opts[:config_file] = file
37
59
  else
38
60
  die("Config file #{file} does not exist")
39
61
  end
40
62
  end
63
+ opts.on('-e', '--endpoint [ADDR]', 'Config api to use') do |ep|
64
+ if ep =~ URI::DEFAULT_PARSER.make_regexp
65
+ @opts[:endpoint] = ep
66
+ else
67
+ die('URL of endpoint not valid')
68
+ end
69
+ end
41
70
  end.parse!
71
+ logger = Logger.new(STDOUT)
72
+ logger.info "Using datasource `#{@opts[:datasource]}`"
73
+ end
74
+
75
+ def output(sym, check)
76
+ status_column = winsize[1] / 3 * 2
77
+ print check.description
78
+ case sym.status
79
+ when :ok
80
+ puts "\033[#{status_column}G\033[1;32mOK\033[0m"
81
+ when :failed
82
+ puts "\033[#{status_column}G\033[1;31mFAILED\033[0m"
83
+ check.help.each { |message| puts "--> #{message}" } unless check.help.empty?
84
+ sym.messages.each { |message| puts " * #{message}" }
85
+ end
86
+ end
87
+
88
+ def winsize
89
+ require 'io/console'
90
+ IO.console.winsize
42
91
  end
43
92
 
44
93
  def die(msg)
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: checkson
3
3
  version: !ruby/object:Gem::Version
4
- version: '0.1'
4
+ version: '0.6'
5
5
  platform: ruby
6
6
  authors:
7
7
  - Florian Bauer
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-03-17 00:00:00.000000000 Z
11
+ date: 2020-04-13 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: A simple framework for checking node facts
14
14
  email: florian@fsrv.xyz
@@ -20,7 +20,12 @@ files:
20
20
  - README.md
21
21
  - bin/checkson
22
22
  - lib/checkson.rb
23
+ - lib/checkson/apiclient.rb
23
24
  - lib/checkson/checks/base.rb
25
+ - lib/checkson/checks/dns.rb
26
+ - lib/checkson/checks/packagemanagers/abstractpkgmgr.rb
27
+ - lib/checkson/checks/packages.rb
28
+ - lib/checkson/checks/process.rb
24
29
  - lib/checkson/checks/shell.rb
25
30
  - lib/checkson/config.rb
26
31
  - lib/checkson/context.rb