mirror of
https://github.com/quay/quay.git
synced 2026-01-27 18:42:52 +03:00
* Convert all Python2 to Python3 syntax. * Removes oauth2lib dependency * Replace mockredis with fakeredis * byte/str conversions * Removes nonexisting __nonzero__ in Python3 * Python3 Dockerfile and related * [PROJQUAY-98] Replace resumablehashlib with rehash * PROJQUAY-123 - replace gpgme with python3-gpg * [PROJQUAY-135] Fix unhashable class error * Update external dependencies for Python 3 - Move github.com/app-registry/appr to github.com/quay/appr - github.com/coderanger/supervisor-stdout - github.com/DevTable/container-cloud-config - Update to latest mockldap with changes applied from coreos/mockldap - Update dependencies in requirements.txt and requirements-dev.txt * Default FLOAT_REPR function to str in json encoder and removes keyword assignment True, False, and str were not keywords in Python2... * [PROJQUAY-165] Replace package `bencode` with `bencode.py` - Bencode is not compatible with Python 3.x and is no longer maintained. Bencode.py appears to be a drop-in replacement/fork that is compatible with Python 3. * Make sure monkey.patch is called before anything else ( * Removes anunidecode dependency and replaces it with text_unidecode * Base64 encode/decode pickle dumps/loads when storing value in DB Base64 encodes/decodes the serialized values when storing them in the DB. Also make sure to return a Python3 string instead of a Bytes when coercing for db, otherwise, Postgres' TEXT field will convert it into a hex representation when storing the value. * Implement __hash__ on Digest class In Python 3, if a class defines __eq__() but not __hash__(), its instances will not be usable as items in hashable collections (e.g sets). * Remove basestring check * Fix expected message in credentials tests * Fix usage of Cryptography.Fernet for Python3 (#219) - Specifically, this addresses the issue where Byte<->String conversions weren't being applied correctly. * Fix utils - tar+stream layer format utils - filelike util * Fix storage tests * Fix endpoint tests * Fix workers tests * Fix docker's empty layer bytes * Fix registry tests * Appr * Enable CI for Python 3.6 * Skip buildman tests Skip buildman tests while it's being rewritten to allow ci to pass. * Install swig for CI * Update expected exception type in redis validation test * Fix gpg signing calls Fix gpg calls for updated gpg wrapper, and add signing tests. * Convert / to // for Python3 integer division * WIP: Update buildman to use asyncio instead of trollius. This dependency is considered deprecated/abandoned and was only used as an implementation/backport of asyncio on Python 2.x This is a work in progress, and is included in the PR just to get the rest of the tests passing. The builder is actually being rewritten. * Target Python 3.8 * Removes unused files - Removes unused files that were added accidentally while rebasing - Small fixes/cleanup - TODO tasks comments * Add TODO to verify rehash backward compat with resumablehashlib * Revert "[PROJQUAY-135] Fix unhashable class error" and implements __hash__ instead. This reverts commit 735e38e3c1d072bf50ea864bc7e119a55d3a8976. Instead, defines __hash__ for encryped fields class, using the parent field's implementation. * Remove some unused files ad imports Co-authored-by: Kenny Lee Sin Cheong <kenny.lee@redhat.com> Co-authored-by: Tom McKay <thomasmckay@redhat.com>
226 lines
7.6 KiB
Python
226 lines
7.6 KiB
Python
import logging
|
|
import json
|
|
|
|
from jsonschema import validate, ValidationError
|
|
from buildtrigger.triggerutil import (
|
|
RepositoryReadException,
|
|
TriggerActivationException,
|
|
TriggerStartException,
|
|
ValidationRequestException,
|
|
InvalidPayloadException,
|
|
SkipRequestException,
|
|
raise_if_skipped_build,
|
|
find_matching_branches,
|
|
)
|
|
|
|
from buildtrigger.basehandler import BuildTriggerHandler
|
|
|
|
from buildtrigger.bitbuckethandler import (
|
|
BITBUCKET_WEBHOOK_PAYLOAD_SCHEMA as bb_schema,
|
|
get_transformed_webhook_payload as bb_payload,
|
|
)
|
|
|
|
from buildtrigger.githubhandler import (
|
|
GITHUB_WEBHOOK_PAYLOAD_SCHEMA as gh_schema,
|
|
get_transformed_webhook_payload as gh_payload,
|
|
)
|
|
|
|
from buildtrigger.gitlabhandler import (
|
|
GITLAB_WEBHOOK_PAYLOAD_SCHEMA as gl_schema,
|
|
get_transformed_webhook_payload as gl_payload,
|
|
)
|
|
|
|
from util.security.ssh import generate_ssh_keypair
|
|
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
# Defines an ordered set of tuples of the schemas and associated transformation functions
|
|
# for incoming webhook payloads.
|
|
SCHEMA_AND_HANDLERS = [
|
|
(gh_schema, gh_payload),
|
|
(bb_schema, bb_payload),
|
|
(gl_schema, gl_payload),
|
|
]
|
|
|
|
|
|
def custom_trigger_payload(metadata, git_url):
|
|
# First try the customhandler schema. If it matches, nothing more to do.
|
|
custom_handler_validation_error = None
|
|
try:
|
|
validate(metadata, CustomBuildTrigger.payload_schema)
|
|
except ValidationError as vex:
|
|
custom_handler_validation_error = vex
|
|
|
|
# Otherwise, try the defined schemas, in order, until we find a match.
|
|
for schema, handler in SCHEMA_AND_HANDLERS:
|
|
try:
|
|
validate(metadata, schema)
|
|
except ValidationError:
|
|
continue
|
|
|
|
result = handler(metadata)
|
|
result["git_url"] = git_url
|
|
return result
|
|
|
|
# If we have reached this point and no other schemas validated, then raise the error for the
|
|
# custom schema.
|
|
if custom_handler_validation_error is not None:
|
|
raise InvalidPayloadException(custom_handler_validation_error.message)
|
|
|
|
metadata["git_url"] = git_url
|
|
return metadata
|
|
|
|
|
|
class CustomBuildTrigger(BuildTriggerHandler):
|
|
payload_schema = {
|
|
"type": "object",
|
|
"properties": {
|
|
"commit": {
|
|
"type": "string",
|
|
"description": "first 7 characters of the SHA-1 identifier for a git commit",
|
|
"pattern": "^([A-Fa-f0-9]{7,})$",
|
|
},
|
|
"ref": {
|
|
"type": "string",
|
|
"description": "git reference for a git commit",
|
|
"pattern": "^refs\/(heads|tags|remotes)\/(.+)$",
|
|
},
|
|
"default_branch": {
|
|
"type": "string",
|
|
"description": "default branch of the git repository",
|
|
},
|
|
"commit_info": {
|
|
"type": "object",
|
|
"description": "metadata about a git commit",
|
|
"properties": {
|
|
"url": {"type": "string", "description": "URL to view a git commit",},
|
|
"message": {"type": "string", "description": "git commit message",},
|
|
"date": {"type": "string", "description": "timestamp for a git commit"},
|
|
"author": {
|
|
"type": "object",
|
|
"description": "metadata about the author of a git commit",
|
|
"properties": {
|
|
"username": {
|
|
"type": "string",
|
|
"description": "username of the author",
|
|
},
|
|
"url": {
|
|
"type": "string",
|
|
"description": "URL to view the profile of the author",
|
|
},
|
|
"avatar_url": {
|
|
"type": "string",
|
|
"description": "URL to view the avatar of the author",
|
|
},
|
|
},
|
|
"required": ["username", "url", "avatar_url"],
|
|
},
|
|
"committer": {
|
|
"type": "object",
|
|
"description": "metadata about the committer of a git commit",
|
|
"properties": {
|
|
"username": {
|
|
"type": "string",
|
|
"description": "username of the committer",
|
|
},
|
|
"url": {
|
|
"type": "string",
|
|
"description": "URL to view the profile of the committer",
|
|
},
|
|
"avatar_url": {
|
|
"type": "string",
|
|
"description": "URL to view the avatar of the committer",
|
|
},
|
|
},
|
|
"required": ["username", "url", "avatar_url"],
|
|
},
|
|
},
|
|
"required": ["url", "message", "date"],
|
|
},
|
|
},
|
|
"required": ["commit", "ref", "default_branch"],
|
|
}
|
|
|
|
@classmethod
|
|
def service_name(cls):
|
|
return "custom-git"
|
|
|
|
def is_active(self):
|
|
return "credentials" in self.config
|
|
|
|
def _metadata_from_payload(self, payload, git_url):
|
|
# Parse the JSON payload.
|
|
try:
|
|
metadata = json.loads(payload)
|
|
except ValueError as vex:
|
|
raise InvalidPayloadException(vex.message)
|
|
|
|
return custom_trigger_payload(metadata, git_url)
|
|
|
|
def handle_trigger_request(self, request):
|
|
payload = request.data
|
|
if not payload:
|
|
raise InvalidPayloadException("Missing expected payload")
|
|
|
|
logger.debug("Payload %s", payload)
|
|
|
|
metadata = self._metadata_from_payload(payload, self.config["build_source"])
|
|
prepared = self.prepare_build(metadata)
|
|
|
|
# Check if we should skip this build.
|
|
raise_if_skipped_build(prepared, self.config)
|
|
|
|
return prepared
|
|
|
|
def manual_start(self, run_parameters=None):
|
|
# commit_sha is the only required parameter
|
|
commit_sha = run_parameters.get("commit_sha")
|
|
if commit_sha is None:
|
|
raise TriggerStartException("missing required parameter")
|
|
|
|
config = self.config
|
|
metadata = {
|
|
"commit": commit_sha,
|
|
"git_url": config["build_source"],
|
|
}
|
|
|
|
try:
|
|
return self.prepare_build(metadata, is_manual=True)
|
|
except ValidationError as ve:
|
|
raise TriggerStartException(ve.message)
|
|
|
|
def activate(self, standard_webhook_url):
|
|
config = self.config
|
|
public_key, private_key = generate_ssh_keypair()
|
|
config["credentials"] = [
|
|
{"name": "SSH Public Key", "value": public_key,},
|
|
{"name": "Webhook Endpoint URL", "value": standard_webhook_url,},
|
|
]
|
|
self.config = config
|
|
return config, {"private_key": private_key}
|
|
|
|
def deactivate(self):
|
|
config = self.config
|
|
config.pop("credentials", None)
|
|
self.config = config
|
|
return config
|
|
|
|
def get_repository_url(self):
|
|
return None
|
|
|
|
def list_build_source_namespaces(self):
|
|
raise NotImplementedError
|
|
|
|
def list_build_sources_for_namespace(self, namespace):
|
|
raise NotImplementedError
|
|
|
|
def list_build_subdirs(self):
|
|
raise NotImplementedError
|
|
|
|
def list_field_values(self, field_name, limit=None):
|
|
raise NotImplementedError
|
|
|
|
def load_dockerfile_contents(self):
|
|
raise NotImplementedError
|