Files
zuul/zuul/driver/elasticsearch/connection.py
T
Daniel Pawlik fcca09289f Change custom type field to default one
With the new Elasticsearch does not support custom field type [1].

[1] https://www.elastic.co/guide/en/elasticsearch/reference/7.17/removal-of-types.html#_custom_type_field

Change-Id: I0b154da0a4736c6b7758f9936356d5b7097c35ad
2022-09-12 09:31:01 +02:00

159 lines
6.3 KiB
Python

# Copyright 2019 Red Hat, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import yaml
import logging
from datetime import datetime
from elasticsearch import Elasticsearch
from elasticsearch.client import IndicesClient
from elasticsearch.helpers import bulk
from elasticsearch.helpers import BulkIndexError
from zuul.connection import BaseConnection
class ElasticsearchConnection(BaseConnection):
driver_name = 'elasticsearch'
log = logging.getLogger("zuul.ElasticSearchConnection")
properties = {
# Common attribute
"uuid": {"type": "keyword"},
"build_type": {"type": "keyword"},
"result": {"type": "keyword"},
"duration": {"type": "integer"},
"@timestamp": {"type": "date"},
# BuildSet type specific attributes
"zuul_ref": {"type": "keyword"},
"pipeline": {"type": "keyword"},
"project": {"type": "keyword"},
"branch": {"type": "keyword"},
"change": {"type": "integer"},
"patchset": {"type": "keyword"},
"ref": {"type": "keyword"},
"oldrev": {"type": "keyword"},
"newrev": {"type": "keyword"},
"ref_url": {"type": "keyword"},
"message": {"type": "text"},
"tenant": {"type": "keyword"},
# Build type specific attibutes
"buildset_uuid": {"type": "keyword"},
"job_name": {"type": "keyword"},
"start_time": {"type": "date", "format": "epoch_second"},
"end_time": {"type": "date", "format": "epoch_second"},
"voting": {"type": "boolean"},
"log_url": {"type": "keyword"},
"nodeset": {"type": "keyword"}
}
def __init__(self, driver, connection_name, connection_config):
super(ElasticsearchConnection, self).__init__(
driver, connection_name, connection_config)
self.uri = self.connection_config.get('uri').split(',')
self.cnx_opts = {}
use_ssl = self.connection_config.get('use_ssl', True)
if isinstance(use_ssl, str):
if use_ssl.lower() == 'false':
use_ssl = False
else:
use_ssl = True
self.cnx_opts['use_ssl'] = use_ssl
if use_ssl:
verify_certs = self.connection_config.get('verify_certs', True)
if isinstance(verify_certs, str):
if verify_certs.lower() == 'false':
verify_certs = False
else:
verify_certs = True
self.cnx_opts['verify_certs'] = verify_certs
self.cnx_opts['ca_certs'] = self.connection_config.get(
'ca_certs', None)
self.cnx_opts['client_cert'] = self.connection_config.get(
'client_cert', None)
self.cnx_opts['client_key'] = self.connection_config.get(
'client_key', None)
self.es = Elasticsearch(
self.uri, **self.cnx_opts)
try:
self.log.debug("Elasticsearch info: %s" % self.es.info())
except Exception as e:
self.log.warn("An error occured on estabilishing "
"connection to Elasticsearch: %s" % e)
self.ic = IndicesClient(self.es)
def setIndex(self, index):
settings = {
'mappings': {
'_doc': {
"properties": self.properties
}
}
}
try:
self.ic.create(index=index, ignore=400, body=settings)
except Exception:
self.log.exception(
"Unable to create the index %s on connection %s" % (
index, self.connection_name))
def gen(self, it, index):
for source in it:
d = {}
source['@timestamp'] = datetime.utcfromtimestamp(
int(source['start_time'])).strftime("%Y-%m-%dT%H:%M:%S.%fZ")
d['_index'] = index
d['_op_type'] = 'index'
d['_source'] = source
yield d
def add_docs(self, source_it, index):
self.setIndex(index)
try:
bulk(self.es, self.gen(source_it, index))
self.es.indices.refresh(index=index)
self.log.debug('%s docs indexed to %s' % (
len(source_it), self.connection_name))
except BulkIndexError as exc:
self.log.warn("Some docs failed to be indexed (%s)" % exc)
# We give flexibility by allowing any type of job's vars and
# zuul return data to be indexed with EL dynamic mapping enabled.
# It may happen that a doc own a field with a value that does not
# match the previous data type that EL has detected for that field.
# In that case the whole doc is not indexed by EL.
# Here we want to mitigate by indexing the errorneous docs in a
# <index-name>.errorneous index by flattening the doc data as yaml.
# This ensures the doc is indexed and can be tracked and eventually
# be modified and re-indexed by an operator.
errorneous_docs = []
for d in exc.errors:
if d['index']['error']['type'] == 'mapper_parsing_exception':
errorneous_doc = {
'uuid': d['index']['data']['uuid'],
'blob': yaml.dump(d['index']['data'])
}
errorneous_docs.append(errorneous_doc)
try:
mapping_errorneous_index = "%s.errorneous" % index
bulk(
self.es,
self.gen(errorneous_docs, mapping_errorneous_index))
self.es.indices.refresh(index=mapping_errorneous_index)
self.log.info(
"%s errorneous docs indexed" % (len(errorneous_docs)))
except BulkIndexError as exc:
self.log.warn(
"Some errorneous docs failed to be indexed (%s)" % exc)