ManageIQ/manageiq-providers-kubernetes

View on GitHub
app/models/manageiq/providers/kubernetes/container_manager.rb

Summary

Maintainability
B
4 hrs
Test Coverage
C
74%
class ManageIQ::Providers::Kubernetes::ContainerManager < ManageIQ::Providers::ContainerManager
DEFAULT_PORT = 6443
METRICS_ROLES = %w[prometheus].freeze
 
has_one :infra_manager,
:foreign_key => :parent_ems_id,
:class_name => "ManageIQ::Providers::Kubevirt::InfraManager",
:autosave => true,
:inverse_of => :parent_manager,
:dependent => :destroy
 
include HasInfraManagerMixin
include ManageIQ::Providers::Kubernetes::ContainerManager::Options
 
before_save :stop_event_monitor_queue_on_change, :stop_refresh_worker_queue_on_change
before_destroy :stop_event_monitor, :stop_refresh_worker
 
supports :create
supports :streaming_refresh do
_("Streaming refresh not enabled") unless streaming_refresh_enabled?
end
 
supports :label_mapping
 
def streaming_refresh_enabled?
Settings.ems_refresh[emstype.to_sym]&.streaming_refresh
end
 
def allow_targeted_refresh?
true
end
 
supports :metrics do
_("No metrics endpoint has been added") unless metrics_endpoint_exists?
end
 
def metrics_endpoint_exists?
endpoints.where(:role => METRICS_ROLES).exists?
end
 
def self.ems_type
@ems_type ||= "kubernetes".freeze
end
 
def self.description
@description ||= "Kubernetes".freeze
end
 
def self.raw_connect(hostname, port, options)
kubernetes_connect(hostname, port, options)
end
 
def self.event_monitor_class
ManageIQ::Providers::Kubernetes::ContainerManager::EventCatcher
end
 
def self.refresh_worker_class
self::RefreshWorker
end
 
def self.display_name(number = 1)
n_('Container Provider (Kubernetes)', 'Container Providers (Kubernetes)', number)
end
 
def self.default_port
DEFAULT_PORT
end
 
LABEL_MAPPING_MODELS = %w[
ContainerGroup
ContainerProject
ContainerNode
ContainerReplicator
ContainerRoute
ContainerService
ContainerBuild
].freeze
 
def self.entities_for_label_mapping
LABEL_MAPPING_MODELS.each_with_object({}) { |target_model, entity| entity[target_model] = target_model }
end
 
def self.label_mapping_prefix
ems_type
end
 
def hostname_uniqueness_valid?
return unless hostname_required?
return unless hostname.present? # Presence is checked elsewhere
# check uniqueness per provider type
 
existing_providers = self.class.all - [self]
existing_endpoints = existing_providers.map do |ems|
next if ems.hostname.nil?
 
"#{ems.hostname.downcase}:#{ems.port}"
end
 
errors.add(:hostname, N_("has to be unique per provider type")) if existing_endpoints.include?("#{hostname.downcase}:#{port}")
end
 
def self.params_for_create
{
:fields => [
{
:component => 'sub-form',
:id => 'endpoints-subform',
:name => 'endpoints-subform',
:title => _('Endpoints'),
:fields => [
:component => 'tabs',
:name => 'tabs',
:fields => [
{
:component => 'tab-item',
:id => 'default-tab',
:name => 'default-tab',
:title => _('Default'),
:fields => [
{
:component => 'validate-provider-credentials',
:id => 'authentications.default.valid',
:name => 'authentications.default.valid',
:skipSubmit => true,
:isRequired => true,
:validationDependencies => %w[type zone_id],
:fields => [
Similar blocks of code found in 2 locations. Consider refactoring.
{
:component => "select",
:id => "endpoints.default.security_protocol",
:name => "endpoints.default.security_protocol",
:label => _("Security Protocol"),
:isRequired => true,
:validate => [{:type => "required"}],
:initialValue => 'ssl-with-validation',
:options => [
{
:label => _("SSL"),
:value => "ssl-with-validation"
},
{
:label => _("SSL trusting custom CA"),
:value => "ssl-with-validation-custom-ca"
},
{
:label => _("SSL without validation"),
:value => "ssl-without-validation",
},
]
},
{
:component => "text-field",
:id => "endpoints.default.hostname",
:name => "endpoints.default.hostname",
:label => _("Hostname (or IPv4 or IPv6 address)"),
:isRequired => true,
:validate => [{:type => "required"}],
},
{
:component => "text-field",
:id => "endpoints.default.port",
:name => "endpoints.default.port",
:label => _("API Port"),
:type => "number",
:initialValue => default_port,
:isRequired => true,
:validate => [{:type => "required"}],
},
Similar blocks of code found in 3 locations. Consider refactoring.
{
:component => "textarea",
:id => "endpoints.default.certificate_authority",
:name => "endpoints.default.certificate_authority",
:label => _("Trusted CA Certificates"),
:rows => 10,
:isRequired => true,
:validate => [{:type => "required"}],
:condition => {
:when => 'endpoints.default.security_protocol',
:is => 'ssl-with-validation-custom-ca',
},
},
{
:component => "password-field",
:id => "authentications.bearer.auth_key",
:name => "authentications.bearer.auth_key",
:label => "Token",
:helperText => _('Note: If enabled, the Default, Metrics, and Alert Endpoints must be revalidated when adding or changing the token'),
:type => "password",
:isRequired => true,
:validate => [{:type => "required"}],
},
]
}
]
},
{
:component => 'tab-item',
:id => 'metrics-tab',
:name => 'metrics-tab',
:title => _('Metrics'),
:fields => [
{
:component => 'protocol-selector',
:id => 'metrics_selection',
:name => 'metrics_selection',
:skipSubmit => true,
:initialValue => 'none',
:label => _('Type'),
:options => [
{
:label => _('Disabled'),
:value => 'none',
},
{
:label => _('Prometheus'),
:value => 'prometheus',
:pivot => 'endpoints.prometheus.hostname',
},
],
},
{
:component => 'validate-provider-credentials',
:id => "authentications.prometheus.valid",
:name => "authentications.prometheus.valid",
:skipSubmit => true,
:isRequired => true,
:validationDependencies => %w[type zone_id metrics_selection authentications.bearer.auth_key],
:condition => {
:when => "metrics_selection",
:is => 'prometheus',
},
:fields => [
{
:component => "select",
:id => "endpoints.prometheus.security_protocol",
:name => "endpoints.prometheus.security_protocol",
:label => _("Security Protocol"),
:isRequired => true,
:initialValue => 'ssl-with-validation',
:validate => [{:type => "required"}],
:options => [
{
:label => _("SSL"),
:value => "ssl-with-validation"
},
{
:label => _("SSL trusting custom CA"),
:value => "ssl-with-validation-custom-ca"
},
{
:label => _("SSL without validation"),
:value => "ssl-without-validation"
},
]
},
Similar blocks of code found in 2 locations. Consider refactoring.
{
:component => "text-field",
:id => "endpoints.prometheus.hostname",
:name => "endpoints.prometheus.hostname",
:label => _("Hostname (or IPv4 or IPv6 address)"),
:isRequired => true,
:validate => [{:type => "required"}],
:inputAddon => {
:after => {
:fields => [
{
:component => 'input-addon-button-group',
:id => 'detect-prometheus-group',
:name => 'detect-prometheus-group',
:fields => [
{
:component => 'detect-button',
:id => 'detect-prometheus-button',
:name => 'detect-prometheus-button',
:label => _('Detect'),
:dependencies => [
'endpoints.default.hostname',
'endpoints.default.port',
'endpoints.default.security_protocol',
'endpoints.default.certificate_authority',
'authentications.bearer.auth_key',
],
:target => 'endpoints.prometheus',
},
],
}
],
},
},
},
{
:component => "text-field",
:id => "endpoints.prometheus.port",
:name => "endpoints.prometheus.port",
:label => _("API Port"),
:type => "number",
:initialValue => 443,
:isRequired => true,
:validate => [{:type => "required"}],
},
Similar blocks of code found in 3 locations. Consider refactoring.
{
:component => "textarea",
:id => "endpoints.prometheus.certificate_authority",
:name => "endpoints.prometheus.certificate_authority",
:label => _("Trusted CA Certificates"),
:rows => 10,
:isRequired => true,
:validate => [{:type => "required"}],
:condition => {
:when => 'endpoints.prometheus.security_protocol',
:is => 'ssl-with-validation-custom-ca',
},
},
]
}
]
},
{
:component => 'tab-item',
:id => 'virtualization-tab',
:name => 'virtualization-tab',
:title => _('Virtualization'),
:fields => [
{
:component => 'protocol-selector',
:id => 'virtualization_selection',
:name => 'virtualization_selection',
:skipSubmit => true,
:initialValue => 'none',
:label => _('Type'),
:options => virtualization_options,
},
{
:component => 'validate-provider-credentials',
:id => 'endpoints.virtualization.valid',
:name => 'endpoints.virtualization.valid',
:skipSubmit => true,
:isRequired => true,
:validationDependencies => %w[type zone_id virtualization_selection],
:condition => {
:when => 'virtualization_selection',
:is => 'kubevirt',
},
:fields => [
Similar blocks of code found in 2 locations. Consider refactoring.
{
:component => "select",
:id => "endpoints.kubevirt.security_protocol",
:name => "endpoints.kubevirt.security_protocol",
:label => _("Security Protocol"),
:isRequired => true,
:validate => [{:type => "required"}],
:initialValue => 'ssl-with-validation',
:options => [
{
:label => _("SSL"),
:value => "ssl-with-validation"
},
{
:label => _("SSL trusting custom CA"),
:value => "ssl-with-validation-custom-ca"
},
{
:label => _("SSL without validation"),
:value => "ssl-without-validation"
},
]
},
Similar blocks of code found in 2 locations. Consider refactoring.
{
:component => "text-field",
:id => "endpoints.kubevirt.hostname",
:name => "endpoints.kubevirt.hostname",
:label => _("Hostname (or IPv4 or IPv6 address)"),
:isRequired => true,
:validate => [{:type => "required"}],
:inputAddon => {
:after => {
:fields => [
{
:component => 'input-addon-button-group',
:id => 'detect-kubevirt-group',
:name => 'detect-kubevirt-group',
:fields => [
{
:component => 'detect-button',
:id => 'detect-kubevirt-button',
:name => 'detect-kubevirt-button',
:label => _('Detect'),
:dependencies => [
'endpoints.default.hostname',
'endpoints.default.port',
'endpoints.default.security_protocol',
'endpoints.default.certificate_authority',
'authentications.bearer.auth_key',
],
:target => 'endpoints.kubevirt',
},
],
}
],
},
},
},
{
:component => "text-field",
:id => "endpoints.kubevirt.port",
:name => "endpoints.kubevirt.port",
:label => _("API Port"),
:type => "number",
:initialValue => default_port,
:isRequired => true,
:validate => [{:type => "required"}],
},
Similar blocks of code found in 3 locations. Consider refactoring.
{
:component => "textarea",
:id => "endpoints.kubevirt.certificate_authority",
:name => "endpoints.kubevirt.certificate_authority",
:label => _("Trusted CA Certificates"),
:rows => 10,
:isRequired => true,
:validate => [{:type => "required"}],
:condition => {
:when => 'endpoints.kubevirt.security_protocol',
:is => 'ssl-with-validation-custom-ca',
},
},
{
:component => "password-field",
:id => "authentications.kubevirt.auth_key",
:name => "authentications.kubevirt.auth_key",
:label => "Token",
:type => "password",
:isRequired => true,
:validate => [{:type => "required"}],
},
]
}
]
}
]
]
},
{
:component => 'sub-form',
:id => 'settings-subform',
:name => 'settings-subform',
:title => _('Settings'),
:fields => [
:component => 'tabs',
:name => 'tabs',
:fields => [
{
:component => 'tab-item',
:id => 'proxy-tab',
:name => 'proxy-tab',
:title => _('Proxy'),
:fields => [
{
:component => 'text-field',
:id => 'options.proxy_settings.http_proxy',
:name => 'options.proxy_settings.http_proxy',
:label => _('HTTP Proxy'),
:helperText => _('HTTP Proxy to connect ManageIQ to the provider. example: http://user:password@my_http_proxy'),
:placeholder => VMDB::Util.http_proxy_uri.to_s
}
],
},
{
:component => 'tab-item',
:id => 'image-inspector-tab',
:name => 'image-inspector-tab',
:title => _('Image-Inspector'),
:fields => [
{
:component => 'text-field',
:id => 'options.image_inspector_options.http_proxy',
:name => 'options.image_inspector_options.http_proxy',
:label => _('HTTP Proxy'),
:helperText => _('HTTP Proxy to connect image inspector pods to the internet. example: http://user:password@my_http_proxy')
},
{
:component => 'text-field',
:id => 'options.image_inspector_options.https_proxy',
:name => 'options.image_inspector_options.https_proxy',
:label => _('HTTPS Proxy'),
:helperText => _('HTTPS Proxy to connect image inspector pods to the internet. example: https://user:password@my_https_proxy')
},
{
:component => 'text-field',
:id => 'options.image_inspector_options.no_proxy',
:name => 'options.image_inspector_options.no_proxy',
:label => _('No Proxy'),
:helperText => _("No Proxy lists urls that should'nt be sent to any proxy. example: my_file_server.org")
},
{
:component => 'text-field',
:id => 'options.image_inspector_options.repository',
:name => 'options.image_inspector_options.repository',
:label => _('Repository'),
:helperText => _('Image-Inspector Repository. example: openshift/image-inspector'),
:placeholder => ::Settings.ems.ems_kubernetes.image_inspector_repository
},
{
:component => 'text-field',
:id => 'options.image_inspector_options.registry',
:name => 'options.image_inspector_options.registry',
:label => _('Registry'),
:helperText => _('Registry to provide the image inspector repository. example: docker.io'),
:placeholder => ::Settings.ems.ems_kubernetes.image_inspector_registry
},
{
:component => 'text-field',
:id => 'options.image_inspector_options.image_tag',
:name => 'options.image_inspector_options.image_tag',
:label => _('Image Tag'),
:helperText => _('Image-Inspector image tag. example: 2.1'),
:placeholder => ManageIQ::Providers::Kubernetes::ContainerManager::Scanning::Job::INSPECTOR_IMAGE_TAG
},
{
:component => 'text-field',
:id => 'options.image_inspector_options.cve_url',
:name => 'options.image_inspector_options.cve_url',
:label => _('CVE Location'),
:helperText => _('Alternative URL path for the XCCDF file, where a com.redhat.rhsa-RHEL7.ds.xml.bz2 file is expected. example: http://my_file_server.example.org:3333/xccdf_files/'),
:placeholder => ::Settings.ems.ems_kubernetes.image_inspector_cve_url
},
],
},
],
],
},
],
}
end
 
def self.virtualization_options
[
{
:label => _('Disabled'),
:value => 'none',
},
{
:label => _('KubeVirt'),
:value => 'kubevirt',
:pivot => 'endpoints.kubevirt.hostname',
},
]
end
 
def self.verify_credentials(args)
endpoint_name = args.dig("endpoints").keys.first
endpoint = args.dig("endpoints", endpoint_name)
 
token = args.dig("authentications", "bearer", "auth_key") || args.dig("authentications", "kubevirt", "auth_key")
token = ManageIQ::Password.try_decrypt(token)
token ||= find(args["id"]).authentication_token(endpoint_name == 'kubevirt' ? 'kubevirt' : 'bearer') if args["id"]
 
hostname, port = endpoint&.values_at("hostname", "port")
 
options = {
:bearer => token,
:ssl_options => {
:verify_ssl => endpoint&.dig("security_protocol") == 'ssl-without-validation' ? OpenSSL::SSL::VERIFY_NONE : OpenSSL::SSL::VERIFY_PEER,
:ca_file => endpoint&.dig("certificate_authority")
}
}
 
connection_rescue_block do
case endpoint_name
when 'default'
verify_default_credentials(hostname, port, options)
when 'kubevirt'
verify_kubevirt_credentials(hostname, port, options)
when 'prometheus'
verify_prometheus_credentials(hostname, port, options)
else
raise MiqException::MiqInvalidCredentialsError, _("Unsupported endpoint")
end
end
end
 
def self.connection_rescue_block
require "kubeclient"
require "rest-client"
yield
rescue SocketError,
Errno::ECONNREFUSED,
RestClient::ResourceNotFound,
RestClient::InternalServerError => err
raise MiqException::MiqUnreachableError, err.message
rescue RestClient::Unauthorized, Kubeclient::HttpError => err
raise MiqException::MiqInvalidCredentialsError, err.message
end
 
def self.create_from_params(params, endpoints, authentications)
bearer = authentications.find { |authentication| authentication['authtype'] == 'bearer' }
 
# Replicate the bearer authentication for all endpoints, except for default and kubevirt
endpoints.each do |endpoint|
Avoid immutable Array literals in loops. It is better to extract it into a local variable or a constant.
next if %w[default kubevirt].include?(endpoint['role'])
 
authentications << bearer.merge('authtype' => endpoint['role'])
end
 
super(params, endpoints, authentications)
end
 
def self.raw_api_endpoint(hostname, port, path = '')
URI::HTTPS.build(:host => hostname, :port => port.presence.try(:to_i), :path => path)
end
 
def self.kubernetes_connect(hostname, port, options)
require 'kubeclient'
 
conn = Kubeclient::Client.new(
raw_api_endpoint(hostname, port, options[:path]),
options[:version] || kubernetes_version,
:ssl_options => Kubeclient::Client::DEFAULT_SSL_OPTIONS.merge(options[:ssl_options] || {}),
:auth_options => kubernetes_auth_options(options),
:http_proxy_uri => options[:http_proxy] || VMDB::Util.http_proxy_uri,
:timeouts => {
:open => Settings.ems.ems_kubernetes.open_timeout.to_f_with_method,
:read => Settings.ems.ems_kubernetes.read_timeout.to_f_with_method
}
)
 
# Test the API endpoint at connect time to prevent exception being raised
# on first method call
conn.discover
 
conn
end
 
def self.kubernetes_auth_options(options)
auth_options = {}
if options[:username] && options[:password]
auth_options[:username] = options[:username]
auth_options[:password] = options[:password]
end
auth_options[:bearer_token] = options[:bearer] if options[:bearer]
auth_options
end
 
def self.kubernetes_version
'v1'
end
 
def self.kubernetes_service_catalog_connect(hostname, port, options)
options = {:path => '/apis/servicecatalog.k8s.io', :version => service_catalog_api_version}.merge(options)
kubernetes_connect(hostname, port, options)
end
 
def self.service_catalog_api_version
'v1beta1'
end
 
def self.prometheus_connect(hostname, port, options)
require 'prometheus/api_client'
 
uri = raw_api_endpoint(hostname, port).to_s
credentials = {:token => options[:bearer]}
ssl_options = options[:ssl_options] || {:verify_ssl => OpenSSL::SSL::VERIFY_NONE}
 
http_proxy_uri = options[:http_proxy] || VMDB::Util.http_proxy_uri.to_s
prometheus_options = {
:http_proxy_uri => http_proxy_uri.presence,
:verify_ssl => ssl_options[:verify_ssl],
:ssl_cert_store => ssl_options[:ca_file],
}
 
Prometheus::ApiClient.client(:url => uri, :credentials => credentials, :options => prometheus_options)
end
 
def self.verify_k8s_credentials(kube)
!!kube&.api_valid? && !kube.get_namespaces(:limit => 1).nil?
end
 
def self.verify_default_credentials(hostname, port, options)
verify_k8s_credentials(kubernetes_connect(hostname, port, options))
end
 
def self.verify_prometheus_credentials(hostname, port, options)
Redundant safe navigation detected.
!!prometheus_connect(hostname, port, options)&.query(:query => "ALL")&.kind_of?(Hash)
end
 
def self.kubevirt_connect(hostname, port, options)
ManageIQ::Providers::Kubevirt::InfraManager.raw_connect(:server => hostname, :port => port, :token => options[:bearer])
end
 
def self.verify_kubevirt_credentials(hostname, port, options)
security_protocol = if !options[:verify_ssl]
"ssl-without-validation"
elsif options[:ca_file].present?
"ssl-with-validation-custom-ca"
else
"ssl-with-validation"
end
 
ManageIQ::Providers::Kubevirt::InfraManager.verify_credentials(
"endpoints" => {
"default" => {
"server" => hostname,
"port" => port,
"token" => options[:bearer],
"security_protocol" => security_protocol,
"certificate_authority" => options[:ca_file]
}
}
)
end
 
PERF_ROLLUP_CHILDREN = [:container_nodes]
 
def edit_with_params(params, endpoints, authentications)
bearer = authentications.find { |authentication| authentication['authtype'] == 'bearer' }
kubevirt = authentications.find { |authentication| authentication['authtype'] == 'kubevirt' }
# As the authentication is token-only, no data is being submitted if there's no change as we never send
# down the password to the client. This would cause the deletion of the untouched authentications in the
# super() below. In order to prevent this, the authentications are set to a dummy value if the related
# endpoint exists among the submitted data.
endpoints.each do |endpoint|
case endpoint['role']
when 'default' # The default endpoint is paired with the bearer authentication
authentications << {'authtype' => 'bearer'} unless bearer
when 'kubevirt' # Kubevirt has its own authentication, no need for replication
authentications << {'authtype' => 'kubevirt'} unless kubevirt
else # Replicate the bearer authentication for any other endpoints
auth_key = bearer&.fetch('auth_key', authentication_token)
authentications << {'authtype' => endpoint['role']}.reverse_merge(:auth_key => auth_key)
end
end
 
super(params, endpoints, authentications)
end
 
def verify_default_credentials(options)
options[:service] ||= "kubernetes"
with_provider_connection(options) { |kube| self.class.verify_k8s_credentials(kube) }
end
 
def verify_prometheus_credentials
client = ManageIQ::Providers::Kubernetes::ContainerManager::MetricsCapture::PrometheusClient.new(self)
client.prometheus_try_connect
end
 
def verify_kubevirt_credentials
ensure_infra_manager
options = {
:token => authentication_token(:kubevirt),
}
infra_manager.verify_credentials(:kubevirt, options)
infra_manager.verify_virt_supported(options)
end
 
def api_endpoint
self.class.raw_api_endpoint(hostname, port)
end
 
def verify_ssl_mode(endpoint = default_endpoint)
return OpenSSL::SSL::VERIFY_PEER if endpoint.nil? # secure by default
 
case endpoint.security_protocol
when nil, ''
# Previously providers didn't set security_protocol, defaulted to
# verify_ssl == 1 (VERIFY_PEER) which wasn't enforced but now is.
# However, if they explicitly set verify_ssl == 0, we'll respect that.
endpoint.verify_ssl? ? OpenSSL::SSL::VERIFY_PEER : OpenSSL::SSL::VERIFY_NONE
when 'ssl-without-validation'
OpenSSL::SSL::VERIFY_NONE
else # 'ssl-with-validation', 'ssl-with-validation-custom-ca', secure by default with unexpected values.
OpenSSL::SSL::VERIFY_PEER
end
end
 
def ssl_cert_store(endpoint = default_endpoint)
# Given missing (nil) endpoint, return nil meaning use system CA bundle
endpoint.try(:ssl_cert_store)
end
 
def connect(options = {})
effective_options = connect_options(options)
 
self.class.raw_connect(effective_options[:hostname], effective_options[:port], effective_options)
end
 
def connect_options(options = {})
options.merge(
:hostname => options[:hostname] || address,
:port => options[:port] || port,
:username => options[:username] || authentication_userid(options[:auth_type]),
:password => options[:password] || authentication_password(options[:auth_type]),
:bearer => options[:bearer] || authentication_token(options[:auth_type] || 'bearer'),
:http_proxy => self.options ? self.options.fetch_path(:proxy_settings, :http_proxy) : nil,
:ssl_options => options[:ssl_options] || {
:verify_ssl => verify_ssl_mode,
:cert_store => ssl_cert_store
}
)
end
 
def authentications_to_validate
at = [:bearer]
at << :prometheus if has_authentication_type?(:prometheus)
at << :kubevirt if has_authentication_type?(:kubevirt)
at
end
 
def required_credential_fields(_type)
[:auth_key]
end
 
def supported_auth_attributes
%w(userid password auth_key)
end
 
def verify_credentials(auth_type = nil, options = {})
options = options.merge(:auth_type => auth_type)
 
self.class.connection_rescue_block do
case options[:auth_type].to_s
when "prometheus"
verify_prometheus_credentials
when "kubevirt"
verify_kubevirt_credentials
else
verify_default_credentials(options)
end
end
end
 
def after_update_authentication
super
stop_refresh_worker_queue_on_credential_change
end
 
def ensure_authentications_record
return if authentications.present?
update_authentication(:default => {:userid => "_", :save => false})
end
 
def supported_auth_types
%w[default password bearer prometheus kubevirt]
end
 
def default_authentication_type
:bearer
end
 
def scan_job_create(entity, userid)
check_policy_prevent(:request_containerimage_scan, entity, userid, :raw_scan_job_create)
end
 
def raw_scan_job_create(target_class, target_id = nil, userid = nil, target_name = nil)
raise MiqException::Error, _("target_class must be a class not an instance") if target_class.kind_of?(ContainerImage)
ManageIQ::Providers::Kubernetes::ContainerManager::Scanning::Job.create_job(
:userid => userid,
:name => "Container Image Analysis: '#{target_name}'",
:target_class => target_class,
:target_id => target_id,
:zone => my_zone,
:miq_server_host => MiqServer.my_server.hostname,
:miq_server_guid => MiqServer.my_server.guid,
:ems_id => id,
)
end
 
# policy_event: the event sent to automate for policy resolution
# cb_method: the MiqQueue callback method along with the parameters that is called
# when automate process is done and the event is not prevented to proceed by policy
def check_policy_prevent(policy_event, event_target, userid, cb_method)
cb = {
:class_name => self.class.to_s,
:instance_id => id,
:method_name => :check_policy_prevent_callback,
:args => [cb_method, event_target.class.name, event_target.id, userid, event_target.name],
:server_guid => MiqServer.my_guid
}
enforce_policy(event_target, policy_event, {}, { :miq_callback => cb }) unless policy_event.nil?
end
 
def check_policy_prevent_callback(*action, _status, _message, result)
prevented = false
if result.kind_of?(MiqAeEngine::MiqAeWorkspaceRuntime)
 
event = result.get_obj_from_path("/")['event_stream']
data = event.attributes["full_data"]
prevented = data.fetch_path(:policy, :prevented) if data
end
prevented ? _log.info(event.attributes["message"].to_s) : send(*action)
end
 
def enforce_policy(event_target, event, inputs = {}, options = {})
MiqEvent.raise_evm_event(event_target, event, inputs, options)
end
 
SCAN_CONTENT_PATH = '/api/v1/content'
 
def scan_entity_create(scan_data)
client = ext_management_system.connect(:service => 'kubernetes')
pod_proxy = client.proxy_url(:pod,
scan_data[:pod_name],
scan_data[:pod_port],
scan_data[:pod_namespace])
nethttp_options = {
:use_ssl => true,
:verify_mode => verify_ssl_mode,
:cert_store => ssl_cert_store,
}
MiqContainerGroup.new(pod_proxy + SCAN_CONTENT_PATH,
nethttp_options,
client.headers.stringify_keys,
scan_data[:guest_os])
end
 
def annotate(provider_entity_name, ems_indentifier, annotations, container_project_name = nil)
with_provider_connection do |conn|
conn.send(
"patch_#{provider_entity_name}".to_sym,
ems_indentifier,
{"metadata" => {"annotations" => annotations}},
container_project_name # nil is ok for non namespaced entities (e.g images)
)
end
end
 
def evaluate_alert(_alert_id, _event)
# currently only EmsEvents from Prometheus are tested for node alerts,
# and these should automatically be translated to alerts.
true
end
 
def queue_metrics_capture
self.perf_capture_object.perf_capture_all_queue
end
end