sensu-plugins/sensu-plugins-kubernetes

View on GitHub
bin/check-kube-service-available.rb

Summary

Maintainability
C
1 day
Test Coverage
#! /usr/bin/env ruby
# frozen_string_literal: false

#   check-kube-pods-service-available
#
# DESCRIPTION:
# => Check if your kube services are up and ready
#
# OUTPUT:
#   plain text
#
# PLATFORMS:
#   Linux
#
# DEPENDENCIES:
#   gem: sensu-plugin
#   gem: kube-client
#
# USAGE:
# -s, --api-server URL             URL to API server
# -v, --api-version VERSION        API version. Defaults to 'v1'
#     --in-cluster                 Use service account authentication
#     --ca-file CA-FILE            CA file to verify API server cert
#     --cert CERT-FILE             Client cert to present
#     --key KEY-FILE               Client key for the client cert
# -u, --user USER                  User with access to API
#     --password PASSWORD          If user is passed, also pass a password
#     --token TOKEN                Bearer token for authorization
#     --token-file TOKEN-FILE      File containing bearer token for authorization
# -l, --list SERVICES              List of services to check (required)
# -p, --pending SECONDS            Time (in seconds) a pod may be pending for and be valid
#
# NOTES:
#
# LICENSE:
#   Barry Martin <nyxcharon@gmail.com>
#   Released under the same terms as Sensu (the MIT license); see LICENSE
#   for details.
#

require 'sensu-plugins-kubernetes/cli'
require 'time'

class AllServicesUp < Sensu::Plugins::Kubernetes::CLI
  @options = Sensu::Plugins::Kubernetes::CLI.options.dup

  option :service_list,
         description: 'List of services to check',
         short: '-l SERVICES',
         long: '--list',
         required: true

  option :pendingTime,
         description: 'Time (in seconds) a pod may be pending for and be valid',
         short: '-p SECONDS',
         long: '--pending',
         default: 0,
         proc: proc(&:to_i)

  def run
    services = parse_list(config[:service_list])
    failed_services = []
    s = client.get_services
    # TODO: come back and clean me up
    s.each do |a| # rubocop:disable Metrics/BlockLength
      next unless services.include?(a.metadata.name)

      # Build the selector key so we can fetch the corresponding pod
      selector_key = []
      services.delete(a.metadata.name)
      a.spec.selector.to_h.each do |k, v|
        selector_key << "#{k}=#{v}"
      end
      next if selector_key.empty?

      # Get the pod
      pod = nil
      begin
        pod = client.get_pods(label_selector: selector_key.join(',').to_s)
      rescue StandardError
        failed_services << a.metadata.name.to_s
      end
      # Make sure our pod is running
      next if pod.nil?

      pod_available = false
      pod.each do |p|
        case p.status.phase
        when 'Pending'
          next if p.status.startTime.nil?

          if (Time.now - Time.parse(p.status.startTime)).to_i < config[:pendingTime]
            pod_available = true
            break
          end
        when 'Running'
          p.status.conditions.each do |c|
            next unless c.type == 'Ready'

            if c.status == 'True'
              pod_available = true
              break
            end
            break if pod_available
          end
        end
        failed_services << "#{p.metadata.namespace}.#{p.metadata.name}" if pod_available == false
      end
    end

    if failed_services.empty? && services.empty?
      ok 'All services are reporting as up'
    end

    if !failed_services.empty?
      critical "All services are not ready: #{failed_services.join(' ')}"
    else
      critical "Some services could not be checked: #{services.join(' ')}"
    end
  rescue KubeException => e
    critical 'API error: ' << e.message
  end

  def parse_list(list)
    return list.split(',') if list&.include?(',')
    return [list] if list

    ['']
  end
end