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
|
"""
Module for jenkinsapi Fingerprint
"""
from __future__ import annotations
import re
import logging
from typing import Any
import requests
from jenkinsapi.jenkinsbase import JenkinsBase
from jenkinsapi.custom_exceptions import ArtifactBroken
log: logging.Logger = logging.getLogger(__name__)
class Fingerprint(JenkinsBase):
"""
Represents a jenkins fingerprint on a single artifact file ??
"""
RE_MD5 = re.compile("^([0-9a-z]{32})$")
def __init__(self, baseurl: str, id_: str, jenkins_obj: "Jenkins") -> None:
self.jenkins_obj: "Jenkins" = jenkins_obj
assert self.RE_MD5.search(id_), (
"%s does not look like " "a valid id" % id_
)
url: str = f"{baseurl}/fingerprint/{id_}/"
JenkinsBase.__init__(self, url, poll=False)
self.id_: str = id_
self.unknown: bool = False # Previously uninitialized in ctor
def get_jenkins_obj(self) -> "Jenkins":
return self.jenkins_obj
def __str__(self) -> str:
return self.id_
def valid(self) -> bool:
"""
Return True / False if valid. If returns True, self.unknown is
set to either True or False, and can be checked if we have
positive validity (fingerprint known at server) or negative
validity (fingerprint not known at server, but not really an
error).
"""
try:
self.poll()
self.unknown = False
except requests.exceptions.HTTPError as err:
# We can't really say anything about the validity of
# fingerprints not found -- but the artifact can still
# exist, so it is not possible to definitely say they are
# valid or not.
# The response object is of type: requests.models.Response
# extract the status code from it
response_obj: Any = err.response
if response_obj.status_code == 404:
logging.warning(
"MD5 cannot be checked if fingerprints are not enabled"
)
self.unknown = True
return True
return False
return True
def validate_for_build(self, filename: str, job: str, build: int) -> bool:
if not self.valid():
log.info("Fingerprint is not known to jenkins.")
return False
if self.unknown:
# not request error, but unknown to jenkins
return True
if self._data["original"] is not None:
if self._data["original"]["name"] == job:
if self._data["original"]["number"] == build:
return True
if self._data["fileName"] != filename:
log.info(
msg="Filename from jenkins (%s) did not match provided (%s)"
% (self._data["fileName"], filename)
)
return False
for usage_item in self._data["usage"]:
if usage_item["name"] == job:
for range_ in usage_item["ranges"]["ranges"]:
if range_["start"] <= build <= range_["end"]:
msg = (
"This artifact was generated by %s "
"between build %i and %i"
% (
job,
range_["start"],
range_["end"],
)
)
log.info(msg=msg)
return True
return False
def validate(self) -> bool:
try:
assert self.valid()
except AssertionError as ae:
raise ArtifactBroken(
"Artifact %s seems to be broken, check %s"
% (self.id_, self.baseurl)
) from ae
except requests.exceptions.HTTPError:
raise ArtifactBroken(
"Unable to validate artifact id %s using %s"
% (self.id_, self.baseurl)
)
return True
def get_info(self):
"""
Returns a tuple of build-name, build# and artifact filename
for a good build.
"""
self.poll()
return (
self._data["original"]["name"],
self._data["original"]["number"],
self._data["fileName"],
)
|