Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
69 commits
Select commit Hold shift + click to select a range
1e9cc8b
Update charon.spec to remove transitive deps
ligangty Jul 11, 2024
370d8aa
Merge pull request #260 from ligangty/main
ligangty Jul 11, 2024
1e9dba5
Use fixed image tag instead of floating latest for Container file
ligangty Aug 28, 2024
d692d8b
Merge pull request #262 from ligangty/main
ligangty Aug 28, 2024
8b3d6a2
Add config option to be able to use non-default charon configuration
ligangty Sep 23, 2024
a1fc373
Merge pull request #273 from ligangty/config
ligangty Sep 23, 2024
b9ab2e9
Replace space in error log file name of product and version
ligangty Sep 23, 2024
0064e42
Merge pull request #274 from ligangty/logs
ligangty Sep 23, 2024
c2a9957
chore: fixed a api doc and added some comments
ligangty Sep 24, 2024
28902ad
Merge pull request #275 from ligangty/maven
ligangty Sep 24, 2024
26ad29c
chore: fix a return value and a var declare issue
ligangty Sep 27, 2024
01355c8
Merge pull request #276 from ligangty/maven
ligangty Sep 27, 2024
1bdd911
Add Makefile
ligangty Sep 27, 2024
37e0c49
Merge pull request #277 from ligangty/build
ligangty Sep 29, 2024
07d700a
Enable mypy types check and fix reported issues
ligangty Sep 29, 2024
84e9cc4
Merge pull request #278 from ligangty/types
ligangty Sep 30, 2024
ea7d920
Enable test_check_invalidation for CloudFront test
ligangty Sep 30, 2024
d6021e2
Merge pull request #279 from ligangty/main
ligangty Sep 30, 2024
c0f773a
fix: MMENG-4284 npm del error when deleting a package which has overl…
ligangty Dec 16, 2024
b390ee1
Merge pull request #280 from ligangty/overlap
ligangty Dec 16, 2024
ec8c70f
Merge branch 'main' into release
ligangty Dec 16, 2024
6051b36
Merge pull request #281 from ligangty/rel
ligangty Dec 16, 2024
7a9abcf
Update version to 1.3.3
ligangty Dec 16, 2024
c1834e8
Merge pull request #282 from ligangty/ver
ligangty Dec 16, 2024
874c5da
Merge branch 'main' into release
ligangty Dec 16, 2024
c14663d
Merge pull request #283 from ligangty/release
ligangty Dec 16, 2024
8795a04
chore: add --version flag to support version check
ligangty Dec 17, 2024
945c763
Fix mmeng-4362: re-sort the indexing page items
ligangty May 9, 2025
9675864
Fix pip warning: add pyproject.toml
ligangty May 12, 2025
99c6fc8
Update version to 1.3.4
ligangty Jun 23, 2025
fd998b8
Merge pull request #325 from ligangty/release
ligangty Jun 23, 2025
14cd191
RADAS: add radas configurations
ligangty Apr 28, 2025
2a2bef0
RADAS: Add sign command skeleton
ligangty Apr 28, 2025
5c7f7f4
Feat: Support to accept the response of signing result from RADAS
yma955 May 13, 2025
070c45a
Format Code and fix typo
yma955 May 13, 2025
558d09c
Rename radas_sign_timeout_retry_count and radas_sign_timeout_retry_in…
yma955 May 19, 2025
dbbdf5e
Use oras registry_config and registry url parse to finalize login
yma955 May 19, 2025
c928769
Change sign_result_loc to cmd flag both for sign request and maven up…
yma955 May 20, 2025
01fa988
Update quay_radas_registry_config to default None since it's not nece…
yma955 May 20, 2025
ea019f7
Use radas_config validate instead of is_radas_config_enable option
yma955 May 20, 2025
f060c5f
Ignore the registry config if the provided config path is not valid t…
yma955 May 20, 2025
4b344b0
Add is_radas_enabled unified method to check radas enablement
yma955 May 20, 2025
ae24bcd
Change on_message process method without using threads
yma955 May 21, 2025
81488ad
Remove timeout retry handling for sign result fetch from maven upload
yma955 May 21, 2025
d433464
Some changes on sign cmd and config
yma955 May 21, 2025
229e44f
Fix: add back the radas_config type for mypy check
ligangty May 26, 2025
53b0196
Add Unit tests for RADAS signing results parse and generation
yma955 May 27, 2025
d4b329c
Change 'result' to 'results' ref signing/radas-nonprod test samples
yma955 May 27, 2025
3639cf0
Add request_id match logic for radas message receiver
yma955 May 28, 2025
a767747
Add sign response status and errors for receiver then use to control …
yma955 May 29, 2025
124e01d
Feature of send radas sign request and unit test for it
shokakucarrier May 29, 2025
979ba60
Refactor: refactor the RadasSender
ligangty May 30, 2025
0879956
Refactor: Refactor the RadasReceiver
ligangty Jun 5, 2025
7ec924a
Some chore changes:
ligangty Jun 5, 2025
21e6966
RADAS: change the request_queue to request_channel in config
ligangty Jun 5, 2025
de5be71
Add missing oras deps in project.toml and setup.py
ligangty Jun 6, 2025
4b1f5df
RADAS: update Containerfile for radas support
ligangty Jun 9, 2025
244f167
RADAS: add default ignore patterns for signing
ligangty Jun 9, 2025
925fd49
Update project version to 1.4.0
ligangty Jun 11, 2025
6fc03c2
RADAS: use result file directly instead of folder in maven upload
ligangty Jun 11, 2025
6ab9749
RADAS: Added some log
ligangty Jun 12, 2025
5c5c4db
RADAS: adjust some logging
ligangty Jun 12, 2025
f692025
RADAS: fix radas response format
ligangty Jun 13, 2025
ca0f12e
control oras version under 0.2.31 to make it be compatible with pytho…
ligangty Jun 23, 2025
a2b61ae
RADAS: fix a flag typo in upload for radas signing
ligangty Jun 25, 2025
0498e2a
Chore: RADAS: some logging adjustment
ligangty Jun 26, 2025
8a7774b
Fix: RADAS: fix a list index out of bounds issue
ligangty Jun 26, 2025
ed636d8
Update charon.spec and pyproject.toml with version update
ligangty Jun 27, 2025
6df053a
Merge branch 'release' into kflux
ligangty Jun 27, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 18 additions & 1 deletion charon.spec
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
%global owner Commonjava
%global modulename charon

%global charon_version 1.3.3
%global charon_version 1.4.0
%global sdist_tar_name %{modulename}-%{charon_version}

%global python3_pkgversion 3
Expand Down Expand Up @@ -64,6 +64,23 @@ export LANG=en_US.UTF-8 LANGUAGE=en_US.en LC_ALL=en_US.UTF-8


%changelog
* Fri Jun 27 2025 Gang Li <gli@redhat.com>
- 1.4.0 release
- Add RADAS signature support

* Mon Jun 23 2025 Gang Li <gli@redhat.com>
- 1.3.4 release
- Fix the sorting problem of index page items

* Mon Dec 16 2024 Gang Li <gli@redhat.com>
- 1.3.3 release
- Fix npm del error when deleting a package which has overlapped name with others
- Some code refinement

* Thu Jul 11 2024 Gang Li <gli@redhat.com>
- 1.3.2 release
- Some updates in the Containerfile.

* Tue May 7 2024 Gang Li <gli@redhat.com>
- 1.3.1 release
- Add checksum refresh command: refresh checksum files for maven artifacts
Expand Down
10 changes: 8 additions & 2 deletions charon/cmd/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,16 +13,19 @@
See the License for the specific language governing permissions and
limitations under the License.
"""
from click import group
from click import group, version_option, pass_context
from charon.cmd.cmd_upload import upload
from charon.cmd.cmd_delete import delete
from charon.cmd.cmd_index import index
from charon.cmd.cmd_checksum import init_checksum, checksum
from charon.cmd.cmd_cache import init_cf, cf
from charon.cmd.cmd_sign import sign


@group()
def cli():
@version_option()
@pass_context
def cli(ctx):
"""Charon is a tool to synchronize several types of
artifacts repository data to Red Hat Ronda
service (maven.repository.redhat.com).
Expand All @@ -41,3 +44,6 @@ def cli():
# init checksum command
init_checksum()
cli.add_command(checksum)

# radas sign cmd
cli.add_command(sign)
137 changes: 137 additions & 0 deletions charon/cmd/cmd_sign.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
"""
Copyright (C) 2022 Red Hat, Inc. (https://github.com/Commonjava/charon)

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.
"""
from typing import List

from charon.config import get_config
from charon.pkgs.radas_sign import sign_in_radas
from charon.cmd.internal import _decide_mode
from charon.constants import DEFAULT_RADAS_SIGN_IGNORES

from click import command, option, argument

import traceback
import logging
import sys
import datetime

logger = logging.getLogger(__name__)


@argument(
"repo_url",
type=str
)
@option(
"--requester",
"-r",
help="""
The requester who sends the signing request.
""",
required=True
)
@option(
"--result_path",
"-p",
help="""
The path which will save the sign result file.
""",
required=True
)
@option(
"--ignore_patterns",
"-i",
multiple=True,
help="""
The regex patterns list to filter out the files which should
not be allowed to upload to S3. Can accept more than one pattern.
"""
)
@option(
"--config",
"-c",
help="""
The charon configuration yaml file path. Default is
$HOME/.charon/charon.yaml
"""
)
@option(
"--sign_key",
"-k",
help="""
rpm-sign key to be used, will replace {{ key }} in default configuration for signature.
Does noting if detach_signature_command does not contain {{ key }} field.
""",
required=True
)
@option(
"--debug",
"-D",
help="Debug mode, will print all debug logs for problem tracking.",
is_flag=True,
default=False
)
@option(
"--quiet",
"-q",
help="Quiet mode, will shrink most of the logs except warning and errors.",
is_flag=True,
default=False
)
@command()
def sign(
repo_url: str,
requester: str,
result_path: str,
sign_key: str,
ignore_patterns: List[str] = None,
config: str = None,
debug=False,
quiet=False
):
"""Do signing against files in the repo zip in repo_url through
radas service. The repo_url points to the maven zip repository
in quay.io, which will be sent as the source of the signing.
"""
logger.debug("%s", ignore_patterns)
try:
current = datetime.datetime.now().strftime("%Y%m%d%I%M")
_decide_mode("radas_sign", current, is_quiet=quiet, is_debug=debug)
conf = get_config(config)
if not conf:
logger.error("The charon configuration is not valid!")
sys.exit(1)
radas_conf = conf.get_radas_config()
if not radas_conf or not radas_conf.validate():
logger.error("The configuration for radas is not valid!")
sys.exit(1)
# All ignore files in global config should also be ignored in signing.
ig_patterns = conf.get_ignore_patterns()
ig_patterns.extend(DEFAULT_RADAS_SIGN_IGNORES)
if ignore_patterns:
ig_patterns.extend(ignore_patterns)
ig_patterns = list(set(ig_patterns))
args = {
"repo_url": repo_url,
"requester": requester,
"sign_key": sign_key,
"result_path": result_path,
"ignore_patterns": ig_patterns,
"radas_config": radas_conf
}
sign_in_radas(**args) # type: ignore
except Exception:
print(traceback.format_exc())
sys.exit(2)
14 changes: 12 additions & 2 deletions charon/cmd/cmd_upload.py
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,14 @@
default=False
)
@option("--dryrun", "-n", is_flag=True, default=False)
@option(
"--sign_result_file",
"-l",
help="""
The path of the file which contains radas signature result.
Upload will use the file to generate the corresponding .asc files
""",
)
@command()
def upload(
repo: str,
Expand All @@ -150,7 +158,8 @@ def upload(
sign_key: str = "redhatdevel",
debug=False,
quiet=False,
dryrun=False
dryrun=False,
sign_result_file=None,
):
"""Upload all files from a released product REPO to Ronda
Service. The REPO points to a product released tarball which
Expand Down Expand Up @@ -221,7 +230,8 @@ def upload(
key=sign_key,
dry_run=dryrun,
manifest_bucket_name=manifest_bucket_name,
config=config
config=config,
sign_result_file=sign_result_file
)
if not succeeded:
sys.exit(1)
Expand Down
118 changes: 114 additions & 4 deletions charon/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
See the License for the specific language governing permissions and
limitations under the License.
"""

import logging
import os
from typing import Dict, List, Optional
Expand All @@ -24,6 +25,104 @@
logger = logging.getLogger(__name__)


class RadasConfig(object):
def __init__(self, data: Dict):
self.__umb_host: str = data.get("umb_host", None)
self.__umb_host_port: str = data.get("umb_host_port", "5671")
self.__result_queue: str = data.get("result_queue", None)
self.__request_chan: str = data.get("request_channel", None)
self.__client_ca: str = data.get("client_ca", None)
self.__client_key: str = data.get("client_key", None)
self.__client_key_pass_file: str = data.get("client_key_pass_file", None)
self.__root_ca: str = data.get("root_ca", "/etc/pki/tls/certs/ca-bundle.crt")
self.__quay_radas_registry_config: Optional[str] = data.get(
"quay_radas_registry_config", None
)
self.__radas_sign_timeout_retry_count: int = data.get("radas_sign_timeout_retry_count", 10)
self.__radas_sign_timeout_retry_interval: int = data.get(
"radas_sign_timeout_retry_interval", 60
)
self.__radas_receiver_timeout: int = int(data.get("radas_receiver_timeout", 1800))

def validate(self) -> bool:
if not self.__umb_host:
logger.error("Missing host name setting for UMB!")
return False
if not self.__result_queue:
logger.error("Missing the queue setting to receive signing result in UMB!")
return False
if not self.__request_chan:
logger.error("Missing the queue setting to send signing request in UMB!")
return False
if self.__client_ca and not os.access(self.__client_ca, os.R_OK):
logger.error("The client CA file is not valid!")
return False
if self.__client_key and not os.access(self.__client_key, os.R_OK):
logger.error("The client key file is not valid!")
return False
if self.__client_key_pass_file and not os.access(self.__client_key_pass_file, os.R_OK):
logger.error("The client key password file is not valid!")
return False
if self.__root_ca and not os.access(self.__root_ca, os.R_OK):
logger.error("The root ca file is not valid!")
return False
if self.__quay_radas_registry_config and not os.access(
self.__quay_radas_registry_config, os.R_OK
):
self.__quay_radas_registry_config = None
logger.warning(
"The quay registry config for oras is not valid, will ignore the registry config!"
)
return True

def umb_target(self) -> str:
if self.ssl_enabled():
return f"amqps://{self.__umb_host.strip()}:{self.__umb_host_port}"
else:
return f"amqp://{self.__umb_host.strip()}:{self.__umb_host_port}"

def result_queue(self) -> str:
return self.__result_queue.strip()

def request_channel(self) -> str:
return self.__request_chan.strip()

def client_ca(self) -> str:
return self.__client_ca.strip()

def client_key(self) -> str:
return self.__client_key.strip()

def client_key_password(self) -> str:
pass_file = self.__client_key_pass_file
if os.access(pass_file, os.R_OK):
with open(pass_file, "r") as f:
return f.read().strip()
elif pass_file:
logger.warning("The key password file is not accessible. Will ignore the password.")
return ""

def root_ca(self) -> str:
return self.__root_ca.strip()

def ssl_enabled(self) -> bool:
return bool(self.__client_ca and self.__client_key and self.__root_ca)

def quay_radas_registry_config(self) -> Optional[str]:
if self.__quay_radas_registry_config:
return self.__quay_radas_registry_config.strip()
return None

def radas_sign_timeout_retry_count(self) -> int:
return self.__radas_sign_timeout_retry_count

def radas_sign_timeout_retry_interval(self) -> int:
return self.__radas_sign_timeout_retry_interval

def receiver_timeout(self) -> int:
return self.__radas_receiver_timeout


class CharonConfig(object):
"""CharonConfig is used to store all configurations for charon
tools.
Expand All @@ -39,6 +138,13 @@ def __init__(self, data: Dict):
self.__ignore_signature_suffix: Dict = data.get("ignore_signature_suffix", None)
self.__signature_command: str = data.get("detach_signature_command", None)
self.__aws_cf_enable: bool = data.get("aws_cf_enable", False)
radas_config: Dict = data.get("radas", None)
self.__radas_config: Optional[RadasConfig] = None
if radas_config:
self.__radas_config = RadasConfig(radas_config)
self.__radas_enabled = bool(self.__radas_config and self.__radas_config.validate())
else:
self.__radas_enabled = False

def get_ignore_patterns(self) -> List[str]:
return self.__ignore_patterns
Expand Down Expand Up @@ -67,19 +173,23 @@ def get_detach_signature_command(self) -> str:
def is_aws_cf_enable(self) -> bool:
return self.__aws_cf_enable

def is_radas_enabled(self) -> bool:
return self.__radas_enabled

def get_radas_config(self) -> Optional[RadasConfig]:
return self.__radas_config


def get_config(cfgPath=None) -> CharonConfig:
config_file_path = cfgPath
if not config_file_path or not os.path.isfile(config_file_path):
config_file_path = os.path.join(os.getenv("HOME", ""), ".charon", CONFIG_FILE)
data = read_yaml_from_file_path(config_file_path, 'schemas/charon.json')
data = read_yaml_from_file_path(config_file_path, "schemas/charon.json")
return CharonConfig(data)


def get_template(template_file: str) -> str:
template = os.path.join(
os.getenv("HOME", ''), ".charon/template", template_file
)
template = os.path.join(os.getenv("HOME", ""), ".charon/template", template_file)
if os.path.isfile(template):
with open(template, encoding="utf-8") as file_:
return file_.read()
Expand Down
Loading