Skip to content

Commit 5591ec2

Browse files
committed
Merge branch 'release' into konflux
2 parents 1abdba2 + dd52136 commit 5591ec2

19 files changed

+1547
-20
lines changed

charon/cmd/__init__.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,16 +13,19 @@
1313
See the License for the specific language governing permissions and
1414
limitations under the License.
1515
"""
16-
from click import group
16+
from click import group, version_option, pass_context
1717
from charon.cmd.cmd_upload import upload
1818
from charon.cmd.cmd_delete import delete
1919
from charon.cmd.cmd_index import index
2020
from charon.cmd.cmd_checksum import init_checksum, checksum
2121
from charon.cmd.cmd_cache import init_cf, cf
22+
from charon.cmd.cmd_sign import sign
2223

2324

2425
@group()
25-
def cli():
26+
@version_option()
27+
@pass_context
28+
def cli(ctx):
2629
"""Charon is a tool to synchronize several types of
2730
artifacts repository data to Red Hat Ronda
2831
service (maven.repository.redhat.com).
@@ -41,3 +44,6 @@ def cli():
4144
# init checksum command
4245
init_checksum()
4346
cli.add_command(checksum)
47+
48+
# radas sign cmd
49+
cli.add_command(sign)

charon/cmd/cmd_sign.py

Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
"""
2+
Copyright (C) 2022 Red Hat, Inc. (https://github.com/Commonjava/charon)
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
"""
16+
from typing import List
17+
18+
from charon.config import get_config
19+
from charon.pkgs.radas_sign import sign_in_radas
20+
from charon.cmd.internal import _decide_mode
21+
22+
from click import command, option, argument
23+
24+
import traceback
25+
import logging
26+
import sys
27+
import datetime
28+
29+
logger = logging.getLogger(__name__)
30+
31+
32+
@argument(
33+
"repo_url",
34+
type=str
35+
)
36+
@option(
37+
"--requester",
38+
"-r",
39+
help="""
40+
The requester who sends the signing request.
41+
""",
42+
required=True
43+
)
44+
@option(
45+
"--result_path",
46+
"-p",
47+
help="""
48+
The path which will save the sign result file.
49+
""",
50+
required=True
51+
)
52+
@option(
53+
"--ignore_patterns",
54+
"-i",
55+
multiple=True,
56+
help="""
57+
The regex patterns list to filter out the files which should
58+
not be allowed to upload to S3. Can accept more than one pattern.
59+
"""
60+
)
61+
@option(
62+
"--config",
63+
"-c",
64+
help="""
65+
The charon configuration yaml file path. Default is
66+
$HOME/.charon/charon.yaml
67+
"""
68+
)
69+
@option(
70+
"--sign_key",
71+
"-k",
72+
help="""
73+
rpm-sign key to be used, will replace {{ key }} in default configuration for signature.
74+
Does noting if detach_signature_command does not contain {{ key }} field.
75+
""",
76+
required=True
77+
)
78+
@option(
79+
"--debug",
80+
"-D",
81+
help="Debug mode, will print all debug logs for problem tracking.",
82+
is_flag=True,
83+
default=False
84+
)
85+
@option(
86+
"--quiet",
87+
"-q",
88+
help="Quiet mode, will shrink most of the logs except warning and errors.",
89+
is_flag=True,
90+
default=False
91+
)
92+
@command()
93+
def sign(
94+
repo_url: str,
95+
requester: str,
96+
result_path: str,
97+
sign_key: str,
98+
ignore_patterns: List[str] = None,
99+
config: str = None,
100+
debug=False,
101+
quiet=False,
102+
dryrun=False
103+
):
104+
"""Do signing against files in the repo zip in repo_url through
105+
radas service. The repo_url points to the maven zip repository
106+
in quay.io, which will be sent as the source of the signing.
107+
"""
108+
logger.debug("%s", ignore_patterns)
109+
try:
110+
current = datetime.datetime.now().strftime("%Y%m%d%I%M")
111+
_decide_mode("radas_sign", current, is_quiet=quiet, is_debug=debug)
112+
conf = get_config(config)
113+
if not conf:
114+
logger.error("The charon configuration is not valid!")
115+
sys.exit(1)
116+
radas_conf = conf.get_radas_config()
117+
if not radas_conf or not radas_conf.validate():
118+
logger.error("The configuration for radas is not valid!")
119+
sys.exit(1)
120+
# All ignore files in global config should also be ignored in signing.
121+
ig_patterns = conf.get_ignore_patterns()
122+
if ignore_patterns:
123+
ig_patterns.extend(ignore_patterns)
124+
args = {
125+
"repo_url": repo_url,
126+
"requester": requester,
127+
"sign_key": sign_key,
128+
"result_path": result_path,
129+
"ignore_patterns": ig_patterns,
130+
"radas_config": radas_conf
131+
}
132+
logger.debug("params: %s", args)
133+
sign_in_radas(**args) # type: ignore
134+
except Exception:
135+
print(traceback.format_exc())
136+
sys.exit(2)

charon/cmd/cmd_upload.py

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,16 @@
136136
default=False
137137
)
138138
@option("--dryrun", "-n", is_flag=True, default=False)
139+
@option(
140+
"--sign_result_loc",
141+
"-l",
142+
default="/tmp/sign",
143+
help="""
144+
The local save path for oras to pull the radas signature result.
145+
Sign request will use this path to download the signature result,
146+
Upload will use the file on this path to generate the corresponding .asc files
147+
""",
148+
)
139149
@command()
140150
def upload(
141151
repo: str,
@@ -150,7 +160,8 @@ def upload(
150160
sign_key: str = "redhatdevel",
151161
debug=False,
152162
quiet=False,
153-
dryrun=False
163+
dryrun=False,
164+
sign_result_loc="/tmp/sign"
154165
):
155166
"""Upload all files from a released product REPO to Ronda
156167
Service. The REPO points to a product released tarball which
@@ -221,7 +232,8 @@ def upload(
221232
key=sign_key,
222233
dry_run=dryrun,
223234
manifest_bucket_name=manifest_bucket_name,
224-
config=config
235+
config=config,
236+
sign_result_loc=sign_result_loc
225237
)
226238
if not succeeded:
227239
sys.exit(1)

charon/config.py

Lines changed: 111 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
See the License for the specific language governing permissions and
1414
limitations under the License.
1515
"""
16+
1617
import logging
1718
import os
1819
from typing import Dict, List, Optional
@@ -24,6 +25,101 @@
2425
logger = logging.getLogger(__name__)
2526

2627

28+
class RadasConfig(object):
29+
def __init__(self, data: Dict):
30+
self.__umb_host: str = data.get("umb_host", None)
31+
self.__umb_host_port: str = data.get("umb_host_port", "5671")
32+
self.__result_queue: str = data.get("result_queue", None)
33+
self.__request_chan: str = data.get("request_channel", None)
34+
self.__client_ca: str = data.get("client_ca", None)
35+
self.__client_key: str = data.get("client_key", None)
36+
self.__client_key_pass_file: str = data.get("client_key_pass_file", None)
37+
self.__root_ca: str = data.get("root_ca", "/etc/pki/tls/certs/ca-bundle.crt")
38+
self.__quay_radas_registry_config: Optional[str] = data.get(
39+
"quay_radas_registry_config", None
40+
)
41+
self.__radas_sign_timeout_retry_count: int = data.get("radas_sign_timeout_retry_count", 10)
42+
self.__radas_sign_timeout_retry_interval: int = data.get(
43+
"radas_sign_timeout_retry_interval", 60
44+
)
45+
self.__radas_receiver_timeout: int = int(data.get("radas_receiver_timeout", 1800))
46+
47+
def validate(self) -> bool:
48+
if not self.__umb_host:
49+
logger.error("Missing host name setting for UMB!")
50+
return False
51+
if not self.__result_queue:
52+
logger.error("Missing the queue setting to receive signing result in UMB!")
53+
return False
54+
if not self.__request_chan:
55+
logger.error("Missing the queue setting to send signing request in UMB!")
56+
return False
57+
if self.__client_ca and not os.access(self.__client_ca, os.R_OK):
58+
logger.error("The client CA file is not valid!")
59+
return False
60+
if self.__client_key and not os.access(self.__client_key, os.R_OK):
61+
logger.error("The client key file is not valid!")
62+
return False
63+
if self.__client_key_pass_file and not os.access(self.__client_key_pass_file, os.R_OK):
64+
logger.error("The client key password file is not valid!")
65+
return False
66+
if self.__root_ca and not os.access(self.__root_ca, os.R_OK):
67+
logger.error("The root ca file is not valid!")
68+
return False
69+
if self.__quay_radas_registry_config and not os.access(
70+
self.__quay_radas_registry_config, os.R_OK
71+
):
72+
self.__quay_radas_registry_config = None
73+
logger.warning(
74+
"The quay registry config for oras is not valid, will ignore the registry config!"
75+
)
76+
return True
77+
78+
def umb_target(self) -> str:
79+
return f"amqps://{self.__umb_host.strip()}:{self.__umb_host_port}"
80+
81+
def result_queue(self) -> str:
82+
return self.__result_queue.strip()
83+
84+
def request_channel(self) -> str:
85+
return self.__request_chan.strip()
86+
87+
def client_ca(self) -> str:
88+
return self.__client_ca.strip()
89+
90+
def client_key(self) -> str:
91+
return self.__client_key.strip()
92+
93+
def client_key_password(self) -> str:
94+
pass_file = self.__client_key_pass_file
95+
if os.access(pass_file, os.R_OK):
96+
with open(pass_file, "r") as f:
97+
return f.read().strip()
98+
elif pass_file:
99+
logger.warning("The key password file is not accessible. Will ignore the password.")
100+
return ""
101+
102+
def root_ca(self) -> str:
103+
return self.__root_ca.strip()
104+
105+
def ssl_enabled(self) -> bool:
106+
return bool(self.__client_ca and self.__client_key and self.__root_ca)
107+
108+
def quay_radas_registry_config(self) -> Optional[str]:
109+
if self.__quay_radas_registry_config:
110+
return self.__quay_radas_registry_config.strip()
111+
return None
112+
113+
def radas_sign_timeout_retry_count(self) -> int:
114+
return self.__radas_sign_timeout_retry_count
115+
116+
def radas_sign_timeout_retry_interval(self) -> int:
117+
return self.__radas_sign_timeout_retry_interval
118+
119+
def receiver_timeout(self) -> int:
120+
return self.__radas_receiver_timeout
121+
122+
27123
class CharonConfig(object):
28124
"""CharonConfig is used to store all configurations for charon
29125
tools.
@@ -39,6 +135,13 @@ def __init__(self, data: Dict):
39135
self.__ignore_signature_suffix: Dict = data.get("ignore_signature_suffix", None)
40136
self.__signature_command: str = data.get("detach_signature_command", None)
41137
self.__aws_cf_enable: bool = data.get("aws_cf_enable", False)
138+
radas_config: Dict = data.get("radas", None)
139+
self.__radas_config: Optional[RadasConfig] = None
140+
if radas_config:
141+
self.__radas_config = RadasConfig(radas_config)
142+
self.__radas_enabled = bool(self.__radas_config and self.__radas_config.validate())
143+
else:
144+
self.__radas_enabled = False
42145

43146
def get_ignore_patterns(self) -> List[str]:
44147
return self.__ignore_patterns
@@ -67,19 +170,23 @@ def get_detach_signature_command(self) -> str:
67170
def is_aws_cf_enable(self) -> bool:
68171
return self.__aws_cf_enable
69172

173+
def is_radas_enabled(self) -> bool:
174+
return self.__radas_enabled
175+
176+
def get_radas_config(self) -> Optional[RadasConfig]:
177+
return self.__radas_config
178+
70179

71180
def get_config(cfgPath=None) -> CharonConfig:
72181
config_file_path = cfgPath
73182
if not config_file_path or not os.path.isfile(config_file_path):
74183
config_file_path = os.path.join(os.getenv("HOME", ""), ".charon", CONFIG_FILE)
75-
data = read_yaml_from_file_path(config_file_path, 'schemas/charon.json')
184+
data = read_yaml_from_file_path(config_file_path, "schemas/charon.json")
76185
return CharonConfig(data)
77186

78187

79188
def get_template(template_file: str) -> str:
80-
template = os.path.join(
81-
os.getenv("HOME", ''), ".charon/template", template_file
82-
)
189+
template = os.path.join(os.getenv("HOME", ""), ".charon/template", template_file)
83190
if os.path.isfile(template):
84191
with open(template, encoding="utf-8") as file_:
85192
return file_.read()

charon/constants.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -175,3 +175,5 @@
175175
DEFAULT_ERRORS_LOG = "errors.log"
176176

177177
DEFAULT_REGISTRY = "localhost"
178+
DEFAULT_RADAS_SIGN_TIMEOUT_RETRY_COUNT = 10
179+
DEFAULT_RADAS_SIGN_TIMEOUT_RETRY_INTERVAL = 60

0 commit comments

Comments
 (0)