#654 client: Convert client script to entry point
Merged 4 months ago by hlin. Opened 5 months ago by hlin.
hlin/odcs oidc  into  master

client/odcs/client/cli.py client/contrib/odcs
file renamed
+493 -486
@@ -1,82 +1,18 @@ 

- #!/usr/bin/env python3

- 

  import os

  import argparse

  import sys

  

+ try:

+     import tomllib

+ except ImportError:

+     import tomli as tomllib

+ 

  import openidc_client

  import requests.exceptions

  

  import odcs.client.odcs

  import json

  

- env_config = {

-     "fedora": {

-         "prod": {"server_url": "https://odcs.fedoraproject.org"},

-         "staging": {"server_url": "https://odcs.stg.fedoraproject.org"},

-     },

-     "redhat": {

-         "prod": {"server_url": "https://odcs.engineering.redhat.com"},

-         "staging": {"server_url": "https://odcs.stage.engineering.redhat.com"},

-     },

- }

- 

- id_provider_config = {

-     "prod": "https://id.fedoraproject.org/openidc/",

-     "staging": "https://id.stg.fedoraproject.org/openidc/",

- }

- 

- parser = argparse.ArgumentParser(

-     description="""\

- %(prog)s - Command line client.

- 

- If you have problems authenticating with OpenID Connect, try:

- 

-   $ rm -rf ~/.openidc/

- 

- Example usage:

- 

- """,

-     formatter_class=argparse.RawDescriptionHelpFormatter,

- )

- parser.add_argument(

-     "--redhat",

-     action="store_const",

-     const="redhat",

-     default="fedora",

-     dest="infra",

-     help="Use internal ODCS infra environment. If omitted, Fedora Infra will "

-     "be used by default.",

- )

- parser.add_argument(

-     "--staging",

-     action="store_const",

-     const="staging",

-     default="prod",

-     dest="env",

-     help="Use Fedora Infra or internal staging environment, which depends on "

-     "if --redhat is specified. If omitted, production environment will "

-     "be used.",

- )

- parser.add_argument("--server", default=None, help="Use custom ODCS server.")

- parser.add_argument(

-     "--token", default=None, help="OpenIDC token to use or path to token file"

- )

- parser.add_argument(

-     "--no-wait",

-     action="store_true",

-     help="When used, odcs client will not wait for the action to finish.",

- )

- parser.add_argument(

-     "-q", "--quiet", action="store_true", help="Run without detailed log messages"

- )

- parser.add_argument("--watch", action="store_true", help="Watch compose logs")

- 

- subparsers = parser.add_subparsers(

-     description="These commands you can use to operate composes with ODCS"

- )

- 

- 

  KNOWN_ARGS = {

      "--flag": dict(

          default=[], action="append", help="Flag to pass to influence the compose."
@@ -154,452 +90,523 @@ 

      return parser

  

  

- create_command_deprecated = """

- Deprecated: Please use create-* commands instead of the deprecated create command.

- The create command will be removed and bugs with it are not going to be fixed.

- """

- create_parser = subparsers.add_parser(

-     "create",

-     help="Low-level command to create a new compose (Deprecated)",

-     description=create_command_deprecated,

- )

- create_parser.set_defaults(command="create")

- create_parser.add_argument(

-     "source_type",

-     default=None,

-     choices=["tag", "module", "raw_config", "pulp", "build"],

-     help="Type for the source, for example: tag.",

- )

- create_parser.add_argument(

-     "source",

-     default="",

-     help="Source for the compose. May be a koji tag or a "

-     "whitespace separated list of modules.",

- )

- create_parser.add_argument(

-     "packages",

-     metavar="package",

-     nargs="*",

-     help="Packages to be included in the compose.",

- )

- create_parser.add_argument(

-     "builds", metavar="build", nargs="*", help="Builds to be included in the compose."

- )

- _add_arguments(

-     create_parser,

-     "--result",

-     "--sigkey",

-     "--koji-event",

-     "--arch",

-     "--module-defaults-url",

-     "--module-defaults-commit",

-     "--modular-tag",

-     "--lookaside-repo",

-     "--label",

-     "--compose-type",

-     "--target-dir",

-     "--flag",

-     "--scratch-module",

+ DEFAULT_CONFIG_FILE = os.path.join(

+     os.path.dirname(os.path.realpath(__file__)), "odcs.toml"

  )

  

  

- create_tag_parser = subparsers.add_parser(

-     "create-tag", help="Create new compose from Koji tag."

- )

- create_tag_parser.set_defaults(command="create-tag")

- create_tag_parser.add_argument("tag", default="", help="Koji tag name.")

- create_tag_parser.add_argument(

-     "packages",

-     metavar="package",

-     nargs="*",

-     help=(

-         "Koji packages to be included in the compose. If you specify no packages, "

-         "ODCS will simply include all packages in the tag."

-     ),

- )

- _add_arguments(

-     create_tag_parser,

-     "--sigkey",

-     "--koji-event",

-     "--arch",

-     "--module-defaults-url",

-     "--module-defaults-commit",

-     "--modular-tag",

-     "--lookaside-repo",

-     "--target-dir",

-     "--build",

-     "--flag",

-     "--scratch-module",

- )

+ def _load_cfg():

+     "Load client config file."

+     cfg_file = os.path.join(os.path.expanduser("~"), ".config", "odcs.toml")

+     if not os.path.isfile(cfg_file):

+         cfg_file = DEFAULT_CONFIG_FILE

+     with open(cfg_file, "rb") as f:

+         return tomllib.load(f)

  

  

- create_module_parser = subparsers.add_parser(

-     "create-module", help="Create new compose from modules."

- )

- create_module_parser.set_defaults(command="create-module")

- create_module_parser.add_argument(

-     "modules",

-     metavar="modules",

-     nargs="*",

-     help="List of modules in N:S, N:S:V or N:S:V:C format.",

- )

- create_module_parser.add_argument(

-     "--base-module-br-name",

-     help="The name of a base module the module buildrequires",

- )

- create_module_parser.add_argument(

-     "--base-module-br-stream",

-     help="The stream of a base module the module buildrequires",

- )

- create_module_parser.add_argument(

-     "--base-module-br-stream-version-lte",

-     type=int,

-     help=(

-         "The numeric value of the stream version (el8.3.1 is 80301, f33 is 33) of "

-         "the buildrequired module specified by --base-module-br-name must be less "

-         "than equal to the given value."

-     ),

- )

- create_module_parser.add_argument(

-     "--base-module-br-stream-version-gte",

-     type=int,

-     help=(

-         "The numeric value of the stream version (el8.3.1 is 80301, f33 is 33) of "

-         "the buildrequired module specified by --base-module-br-name must be greater "

-         "than equal to the given value."

-     ),

- )

- _add_arguments(

-     create_module_parser,

-     "--sigkey",

-     "--arch",

-     "--modular-tag",

-     "--module-defaults-url",

-     "--module-defaults-commit",

-     "--lookaside-repo",

-     "--target-dir",

-     "--flag",

-     "--scratch-module",

- )

+ def main():

+     parser = argparse.ArgumentParser(

+         description="""\

+     %(prog)s - Command line client.

  

+     CONFIGURATION:

+         The default configuration file is `{}`,

+         and you can copy it to `~/.config/odcs.toml` to add your own configurations.

  

- create_pulp_parser = subparsers.add_parser(

-     "create-pulp", help="Create new compose from Pulp content_sets."

- )

- create_pulp_parser.set_defaults(command="create-pulp")

- create_pulp_parser.add_argument(

-     "content_sets",

-     metavar="content_set",

-     nargs="+",

-     help="Content sets to be included in the compose.",

- )

- _add_arguments(create_pulp_parser, "--target-dir", "--flag")

+     If you have problems authenticating with OpenID Connect, try:

  

+       $ rm -rf ~/.openidc/

  

- create_raw_config_parser = subparsers.add_parser(

-     "create-raw-config", help="Create new compose from Pungi raw configuration."

- )

- create_raw_config_parser.set_defaults(command="create-raw-config")

- create_raw_config_parser.add_argument(

-     "raw_config_name", help="Name of raw_config compose as defined in ODCS Server."

- )

- create_raw_config_parser.add_argument(

-     "raw_config_commit", help="Commit or branch name to get raw_config from."

- )

- _add_arguments(

-     create_raw_config_parser,

-     "--sigkey",

-     "--label",

-     "--no-label",

-     "--compose-type",

-     "--koji-event",

-     "--target-dir",

-     "--build",

- )

+     Example usage:

  

+     """.format(

+             DEFAULT_CONFIG_FILE

+         ),

+         formatter_class=argparse.RawDescriptionHelpFormatter,

+     )

+     parser.add_argument(

+         "--redhat",

+         action="store_const",

+         const="redhat",

+         default="fedora",

+         dest="infra",

+         help="Use internal ODCS infra environment. If omitted, Fedora Infra will "

+         "be used by default.",

+     )

+     parser.add_argument(

+         "--staging",

+         action="store_const",

+         const="staging",

+         default="prod",

+         dest="env",

+         help="Use Fedora Infra or internal staging environment, which depends on "

+         "if --redhat is specified. If omitted, production environment will "

+         "be used.",

+     )

+     parser.add_argument("--server", default=None, help="Use custom ODCS server.")

+     parser.add_argument(

+         "--token", default=None, help="OpenIDC token to use or path to token file"

+     )

+     parser.add_argument(

+         "--no-wait",

+         action="store_true",

+         help="When used, odcs client will not wait for the action to finish.",

+     )

+     parser.add_argument(

+         "-q", "--quiet", action="store_true", help="Run without detailed log messages"

+     )

+     parser.add_argument("--watch", action="store_true", help="Watch compose logs")

  

- create_build_parser = subparsers.add_parser(

-     "create-build", help="Create new compose from Koji builds."

- )

- create_build_parser.set_defaults(command="create-build")

- create_build_parser.add_argument(

-     "builds", metavar="NVR", nargs="+", help="Koji builds NVRs."

- )

- _add_arguments(create_build_parser, "--sigkey", "--flag", "--target-dir", "--arch")

+     subparsers = parser.add_subparsers(

+         description="These commands you can use to operate composes with ODCS"

+     )

+ 

+     create_command_deprecated = """

+     Deprecated: Please use create-* commands instead of the deprecated create command.

+     The create command will be removed and bugs with it are not going to be fixed.

+     """

+     create_parser = subparsers.add_parser(

+         "create",

+         help="Low-level command to create a new compose (Deprecated)",

+         description=create_command_deprecated,

+     )

+     create_parser.set_defaults(command="create")

+     create_parser.add_argument(

+         "source_type",

+         default=None,

+         choices=["tag", "module", "raw_config", "pulp", "build"],

+         help="Type for the source, for example: tag.",

+     )

+     create_parser.add_argument(

+         "source",

+         default="",

+         help="Source for the compose. May be a koji tag or a "

+         "whitespace separated list of modules.",

+     )

+     create_parser.add_argument(

+         "packages",

+         metavar="package",

+         nargs="*",

+         help="Packages to be included in the compose.",

+     )

+     create_parser.add_argument(

+         "builds",

+         metavar="build",

+         nargs="*",

+         help="Builds to be included in the compose.",

+     )

+     _add_arguments(

+         create_parser,

+         "--result",

+         "--sigkey",

+         "--koji-event",

+         "--arch",

+         "--module-defaults-url",

+         "--module-defaults-commit",

+         "--modular-tag",

+         "--lookaside-repo",

+         "--label",

+         "--compose-type",

+         "--target-dir",

+         "--flag",

+         "--scratch-module",

+     )

  

+     create_tag_parser = subparsers.add_parser(

+         "create-tag", help="Create new compose from Koji tag."

+     )

+     create_tag_parser.set_defaults(command="create-tag")

+     create_tag_parser.add_argument("tag", default="", help="Koji tag name.")

+     create_tag_parser.add_argument(

+         "packages",

+         metavar="package",

+         nargs="*",

+         help=(

+             "Koji packages to be included in the compose. If you specify no packages, "

+             "ODCS will simply include all packages in the tag."

+         ),

+     )

+     _add_arguments(

+         create_tag_parser,

+         "--sigkey",

+         "--koji-event",

+         "--arch",

+         "--module-defaults-url",

+         "--module-defaults-commit",

+         "--modular-tag",

+         "--lookaside-repo",

+         "--target-dir",

+         "--build",

+         "--flag",

+         "--scratch-module",

+     )

  

- wait_parser = subparsers.add_parser("wait", help="wait for a compose to finish")

- wait_parser.set_defaults(command="wait")

- wait_parser.add_argument("compose_id", default=None, help="ODCS compose id")

- wait_parser.add_argument("--watch", action="store_true", help="Watch compose logs")

+     create_module_parser = subparsers.add_parser(

+         "create-module", help="Create new compose from modules."

+     )

+     create_module_parser.set_defaults(command="create-module")

+     create_module_parser.add_argument(

+         "modules",

+         metavar="modules",

+         nargs="*",

+         help="List of modules in N:S, N:S:V or N:S:V:C format.",

+     )

+     create_module_parser.add_argument(

+         "--base-module-br-name",

+         help="The name of a base module the module buildrequires",

+     )

+     create_module_parser.add_argument(

+         "--base-module-br-stream",

+         help="The stream of a base module the module buildrequires",

+     )

+     create_module_parser.add_argument(

+         "--base-module-br-stream-version-lte",

+         type=int,

+         help=(

+             "The numeric value of the stream version (el8.3.1 is 80301, f33 is 33) of "

+             "the buildrequired module specified by --base-module-br-name must be less "

+             "than equal to the given value."

+         ),

+     )

+     create_module_parser.add_argument(

+         "--base-module-br-stream-version-gte",

+         type=int,

+         help=(

+             "The numeric value of the stream version (el8.3.1 is 80301, f33 is 33) of "

+             "the buildrequired module specified by --base-module-br-name must be greater "

+             "than equal to the given value."

+         ),

+     )

+     _add_arguments(

+         create_module_parser,

+         "--sigkey",

+         "--arch",

+         "--modular-tag",

+         "--module-defaults-url",

+         "--module-defaults-commit",

+         "--lookaside-repo",

+         "--target-dir",

+         "--flag",

+         "--scratch-module",

+     )

  

+     create_pulp_parser = subparsers.add_parser(

+         "create-pulp", help="Create new compose from Pulp content_sets."

+     )

+     create_pulp_parser.set_defaults(command="create-pulp")

+     create_pulp_parser.add_argument(

+         "content_sets",

+         metavar="content_set",

+         nargs="+",

+         help="Content sets to be included in the compose.",

+     )

+     _add_arguments(create_pulp_parser, "--target-dir", "--flag")

  

- delete_parser = subparsers.add_parser(

-     "delete",

-     help="delete compose",

-     description=(

-         "Cancel compose in wait state or mark finished compose as expired "

-         "for ODCS backends to remove compose data later from ODCS storage. "

-         "Note that compose metadata stored in database won't be removed and always "

-         "accessable via 'odcs get' command."

-     ),

- )

- delete_parser.set_defaults(command="delete")

- delete_parser.add_argument("compose_id", default=None, help="ODCS compose id")

- 

- renew_parser = subparsers.add_parser(

-     "renew",

-     help="renew compose",

-     description=(

-         "Extends the compose expiration time or regenerates expired/failed compose "

-         "or regenerates compose with a new label. "

-         "The regenerated compose will have the same package versions as the original compose."

-     ),

- )

- renew_parser.set_defaults(command="renew")

- renew_parser.add_argument("compose_id", default=None, help="ODCS compose id")

- renew_parser.add_argument(

-     "--label", default=None, help="New label of regenerated compose"

- )

- renew_parser.add_argument(

-     "--seconds-to-live",

-     default=None,

-     help="Number of seconds to extends the compose expiration time",

- )

+     create_raw_config_parser = subparsers.add_parser(

+         "create-raw-config", help="Create new compose from Pungi raw configuration."

+     )

+     create_raw_config_parser.set_defaults(command="create-raw-config")

+     create_raw_config_parser.add_argument(

+         "raw_config_name", help="Name of raw_config compose as defined in ODCS Server."

+     )

+     create_raw_config_parser.add_argument(

+         "raw_config_commit", help="Commit or branch name to get raw_config from."

+     )

+     _add_arguments(

+         create_raw_config_parser,

+         "--sigkey",

+         "--label",

+         "--no-label",

+         "--compose-type",

+         "--koji-event",

+         "--target-dir",

+         "--build",

+     )

  

- get_parser = subparsers.add_parser("get", help="get compose info")

- get_parser.set_defaults(command="get")

- get_parser.add_argument("compose_id", default=None, help="ODCS compose id")

+     create_build_parser = subparsers.add_parser(

+         "create-build", help="Create new compose from Koji builds."

+     )

+     create_build_parser.set_defaults(command="create-build")

+     create_build_parser.add_argument(

+         "builds", metavar="NVR", nargs="+", help="Koji builds NVRs."

+     )

+     _add_arguments(create_build_parser, "--sigkey", "--flag", "--target-dir", "--arch")

+ 

+     wait_parser = subparsers.add_parser("wait", help="wait for a compose to finish")

+     wait_parser.set_defaults(command="wait")

+     wait_parser.add_argument("compose_id", default=None, help="ODCS compose id")

+     wait_parser.add_argument("--watch", action="store_true", help="Watch compose logs")

+ 

+     delete_parser = subparsers.add_parser(

+         "delete",

+         help="delete compose",

+         description=(

+             "Cancel compose in wait state or mark finished compose as expired "

+             "for ODCS backends to remove compose data later from ODCS storage. "

+             "Note that compose metadata stored in database won't be removed and always "

+             "accessable via 'odcs get' command."

+         ),

+     )

+     delete_parser.set_defaults(command="delete")

+     delete_parser.add_argument("compose_id", default=None, help="ODCS compose id")

+ 

+     renew_parser = subparsers.add_parser(

+         "renew",

+         help="renew compose",

+         description=(

+             "Extends the compose expiration time or regenerates expired/failed compose "

+             "or regenerates compose with a new label. "

+             "The regenerated compose will have the same package versions as the original compose."

+         ),

+     )

+     renew_parser.set_defaults(command="renew")

+     renew_parser.add_argument("compose_id", default=None, help="ODCS compose id")

+     renew_parser.add_argument(

+         "--label", default=None, help="New label of regenerated compose"

+     )

+     renew_parser.add_argument(

+         "--seconds-to-live",

+         default=None,

+         help="Number of seconds to extends the compose expiration time",

+     )

  

- about_parser = subparsers.add_parser("about", help="Get information about ODCS server")

- about_parser.set_defaults(command="about")

+     get_parser = subparsers.add_parser("get", help="get compose info")

+     get_parser.set_defaults(command="get")

+     get_parser.add_argument("compose_id", default=None, help="ODCS compose id")

  

- args = parser.parse_args()

+     about_parser = subparsers.add_parser(

+         "about", help="Get information about ODCS server"

+     )

+     about_parser.set_defaults(command="about")

  

- if not hasattr(args, "command"):

-     parser.print_help()

-     sys.exit(0)

+     args = parser.parse_args()

  

- if args.server is None:

-     odcs_url = env_config[args.infra][args.env]["server_url"]

- else:

-     odcs_url = args.server

+     if not hasattr(args, "command"):

+         parser.print_help()

+         sys.exit(0)

  

- if args.infra == "fedora":

-     if args.token:

-         if os.path.exists(args.token):

-             with open(args.token, "r") as token_file:

-                 token = token_file.readline().strip()

-         else:

-             token = args.token

+     cfg = _load_cfg()

+     if args.server is None:

+         odcs_url = cfg[args.infra][args.env]["url"]

      else:

-         id_provider = id_provider_config[args.env]

- 

-         # Get the auth token using the OpenID client.

-         oidc = openidc_client.OpenIDCClient(

-             "odcs",

-             id_provider,

-             {"Token": "Token", "Authorization": "Authorization"},

-             "odcs-authorizer",

-             "notsecret",

-         )

+         odcs_url = args.server

  

-         scopes = [

-             "openid",

-             "https://id.fedoraproject.org/scope/groups",

-             "https://pagure.io/odcs/new-compose",

-             "https://pagure.io/odcs/renew-compose",

-             "https://pagure.io/odcs/delete-compose",

-         ]

-         try:

-             token = oidc.get_token(scopes, new_token=True)

-             token = oidc.report_token_issue()

-         except requests.exceptions.HTTPError as e:

-             print(e.response.text)

-             raise

- 

-     client = odcs.client.odcs.ODCS(

-         odcs_url,

-         auth_mech=odcs.client.odcs.AuthMech.OpenIDC,

-         openidc_token=token,

-     )

- else:

-     client = odcs.client.odcs.ODCS(

-         odcs_url,

-         auth_mech=odcs.client.odcs.AuthMech.Kerberos,

-     )

- 

- request_args = {}

- if getattr(args, "flag", False):

-     request_args["flags"] = args.flag

- if getattr(args, "arch", False):

-     request_args["arches"] = args.arch

- if getattr(args, "lookaside_repo", False):

-     request_args["lookaside_repos"] = args.lookaside_repo

- if getattr(args, "label", False):

-     request_args["label"] = args.label

- if getattr(args, "no_label", False):

-     if "flags" in request_args:

-         request_args["flags"].append("no_label")

-     else:

-         request_args["flags"] = ["no_label"]

- if getattr(args, "compose_type", False):

-     request_args["compose_type"] = args.compose_type

- if getattr(args, "target_dir", False):

-     request_args["target_dir"] = args.target_dir

- 

- otel_endpoint = os.environ.get("OTEL_EXPORTER_OTLP_ENDPOINT", "")

- ctx_token = None

- if otel_endpoint:

-     from opentelemetry import context, trace

-     from opentelemetry.exporter.otlp.proto.http.trace_exporter import (

-         OTLPSpanExporter,

-     )

-     from opentelemetry.instrumentation.requests import RequestsInstrumentor

-     from opentelemetry.sdk.resources import Resource

-     from opentelemetry.sdk.trace import TracerProvider

-     from opentelemetry.sdk.trace.export import (

-         BatchSpanProcessor,

-         ConsoleSpanExporter,

-     )

-     from opentelemetry.trace.propagation.tracecontext import (

-         TraceContextTextMapPropagator,

-     )

- 

-     provider = TracerProvider(

-         resource=Resource(attributes={"service.name": "odcs-client"})

-     )

-     if "console" == otel_endpoint:

-         # This is for debugging the tracing locally.

-         provider.add_span_processor(BatchSpanProcessor(ConsoleSpanExporter()))

-     else:

-         provider.add_span_processor(BatchSpanProcessor(OTLPSpanExporter()))

- 

-     trace.set_tracer_provider(provider)

-     tracer = trace.get_tracer(__name__)

-     carrier = {}

-     traceparent = os.environ.get("TRACEPARENT")

-     ctx = None

-     if traceparent:

-         ctx = TraceContextTextMapPropagator().extract(

-             carrier={"traceparent": traceparent}

-         )

-     with tracer.start_as_current_span("odcs-client", context=ctx) as span:

-         TraceContextTextMapPropagator().inject(carrier)

-         span.set_attribute("command", " ".join(sys.argv))

+     id_provider = cfg[args.infra][args.env]["id_provider"]

  

-     ctx = TraceContextTextMapPropagator().extract(carrier)

-     ctx_token = context.attach(ctx)

-     RequestsInstrumentor().instrument(tracer_provider=provider)

+     if args.infra == "fedora":

+         if args.token:

+             if os.path.exists(args.token):

+                 with open(args.token, "r") as token_file:

+                     token = token_file.readline().strip()

+             else:

+                 token = args.token

+         else:

+             # Get the auth token using the OpenID client.

+             oidc = openidc_client.OpenIDCClient(

+                 "odcs",

+                 id_provider,

+                 {"Token": "Token", "Authorization": "Authorization"},

+                 "odcs-authorizer",

+                 "notsecret",

+             )

  

- try:

-     args.sigkey = [key.replace("none", "") for key in getattr(args, "sigkey", [])]

-     if args.command == "create":

-         print(create_command_deprecated, file=sys.stderr)

-         result = client.new_compose(

-             source=args.source,

-             source_type=args.source_type,

-             packages=args.packages,

-             results=args.result,

-             sigkeys=args.sigkey,

-             koji_event=args.koji_event,

-             builds=args.builds,

-             modular_koji_tags=args.modular_tag,

-             module_defaults_url=args.module_defaults_url,

-             module_defaults_commit=args.module_defaults_commit,

-             scratch_modules=args.scratch_module,

-             **request_args

+             scopes = [

+                 "openid",

+                 "https://id.fedoraproject.org/scope/groups",

+                 "https://pagure.io/odcs/new-compose",

+                 "https://pagure.io/odcs/renew-compose",

+                 "https://pagure.io/odcs/delete-compose",

+             ]

+             try:

+                 token = oidc.get_token(scopes, new_token=True)

+                 token = oidc.report_token_issue()

+             except requests.exceptions.HTTPError as e:

+                 print(e.response.text)

+                 raise

+ 

+         client = odcs.client.odcs.ODCS(

+             odcs_url,

+             auth_mech=odcs.client.odcs.AuthMech.OpenIDC,

+             openidc_token=token,

          )

-     elif args.command == "create-tag":

-         source = odcs.client.odcs.ComposeSourceTag(

-             args.tag,

-             args.packages,

-             args.build,

-             args.sigkey,

-             args.koji_event,

-             args.modular_tag,

-             args.module_defaults_url,

-             args.module_defaults_commit,

-             args.scratch_module,

+     else:

+         client = odcs.client.odcs.ODCS(

+             odcs_url,

+             auth_mech=odcs.client.odcs.AuthMech.Kerberos,

          )

-         result = client.request_compose(source, **request_args)

-     elif args.command == "create-module":

-         if not args.modules and not args.scratch_module:

-             create_module_parser.error("Please give a module or --scratch-module")

-         if (

-             len(args.modules) + len(args.scratch_module) > 1

-             and not (

-                 args.base_module_br_stream

-                 or args.base_module_br_stream_version_lte

-                 or args.base_module_br_stream_version_gte

-                 or args.modular_tag

-             )

-             and args.infra == "redhat"

-         ):

-             print(

-                 "WARNING: Please add --base-module-X or --modular-tag option to the arguments to ensure all composed modules are built for the same release."

-             )

-         source = odcs.client.odcs.ComposeSourceModule(

-             args.modules,

-             args.sigkey,

-             args.module_defaults_url,

-             args.module_defaults_commit,

-             args.scratch_module,

-             base_module_br_name=args.base_module_br_name,

-             base_module_br_stream=args.base_module_br_stream,

-             base_module_br_stream_version_lte=args.base_module_br_stream_version_lte,

-             base_module_br_stream_version_gte=args.base_module_br_stream_version_gte,

-             modular_koji_tags=args.modular_tag,

+ 

+     request_args = {}

+     if getattr(args, "flag", False):

+         request_args["flags"] = args.flag

+     if getattr(args, "arch", False):

+         request_args["arches"] = args.arch

+     if getattr(args, "lookaside_repo", False):

+         request_args["lookaside_repos"] = args.lookaside_repo

+     if getattr(args, "label", False):

+         request_args["label"] = args.label

+     if getattr(args, "no_label", False):

+         if "flags" in request_args:

+             request_args["flags"].append("no_label")

+         else:

+             request_args["flags"] = ["no_label"]

+     if getattr(args, "compose_type", False):

+         request_args["compose_type"] = args.compose_type

+     if getattr(args, "target_dir", False):

+         request_args["target_dir"] = args.target_dir

+ 

+     otel_endpoint = os.environ.get("OTEL_EXPORTER_OTLP_ENDPOINT", "")

+     if otel_endpoint:

+         from opentelemetry import context, trace

+         from opentelemetry.exporter.otlp.proto.http.trace_exporter import (

+             OTLPSpanExporter,

          )

-         result = client.request_compose(source, **request_args)

-     elif args.command == "create-pulp":

-         source = odcs.client.odcs.ComposeSourcePulp(args.content_sets)

-         result = client.request_compose(source, **request_args)

-     elif args.command == "create-raw-config":

-         source = odcs.client.odcs.ComposeSourceRawConfig(

-             args.raw_config_name,

-             args.raw_config_commit,

-             args.koji_event,

-             sigkeys=args.sigkey,

-             builds=args.build,

+         from opentelemetry.instrumentation.requests import RequestsInstrumentor

+         from opentelemetry.sdk.resources import Resource

+         from opentelemetry.sdk.trace import TracerProvider

+         from opentelemetry.sdk.trace.export import (

+             BatchSpanProcessor,

+             ConsoleSpanExporter,

          )

-         result = client.request_compose(source, **request_args)

-     elif args.command == "create-build":

-         source = odcs.client.odcs.ComposeSourceBuild(args.builds, args.sigkey)

-         result = client.request_compose(source, **request_args)

-     elif args.command == "wait":

-         result = client.get_compose(args.compose_id)

-     elif args.command == "delete":

-         args.no_wait = True

-         result = client.delete_compose(args.compose_id)

-     elif args.command == "renew":

-         result = client.renew_compose(

-             args.compose_id, label=args.label, seconds_to_live=args.seconds_to_live

+         from opentelemetry.trace.propagation.tracecontext import (

+             TraceContextTextMapPropagator,

          )

-     elif args.command == "get":

-         result = client.get_compose(args.compose_id)

-     elif args.command == "about":

-         result = client.about()

-     else:

-         print("Unknown command %s" % args.command)

- except requests.exceptions.HTTPError:

-     # error message gets printed in ODCS class.

-     sys.exit(-1)

- 

- if args.no_wait or args.command == "about":

-     print(json.dumps(result, indent=4, sort_keys=True))

- else:

-     if result["state_name"] in ["wait", "generating"]:

-         if not args.quiet:

-             print(

-                 "Waiting for command %s on compose %d to finish."

-                 % (args.command, result["id"])

+ 

+         provider = TracerProvider(

+             resource=Resource(attributes={"service.name": "odcs-client"})

+         )

+         if "console" == otel_endpoint:

+             # This is for debugging the tracing locally.

+             provider.add_span_processor(BatchSpanProcessor(ConsoleSpanExporter()))

+         else:

+             provider.add_span_processor(BatchSpanProcessor(OTLPSpanExporter()))

+ 

+         trace.set_tracer_provider(provider)

+         tracer = trace.get_tracer(__name__)

+         carrier = {}

+         traceparent = os.environ.get("TRACEPARENT")

+         ctx = None

+         if traceparent:

+             ctx = TraceContextTextMapPropagator().extract(

+                 carrier={"traceparent": traceparent}

              )

-         try:

-             result = client.wait_for_compose(result["id"], 3600, watch_logs=args.watch)

-         except (KeyboardInterrupt, SystemExit):

-             pass

+         with tracer.start_as_current_span("odcs-client", context=ctx) as span:

+             TraceContextTextMapPropagator().inject(carrier)

+             span.set_attribute("command", " ".join(sys.argv))

+ 

+         ctx = TraceContextTextMapPropagator().extract(carrier)

+         context.attach(ctx)

+         RequestsInstrumentor().instrument(tracer_provider=provider)

+ 

+     try:

+         args.sigkey = [key.replace("none", "") for key in getattr(args, "sigkey", [])]

+         if args.command == "create":

+             print(create_command_deprecated, file=sys.stderr)

+             result = client.new_compose(

+                 source=args.source,

+                 source_type=args.source_type,

+                 packages=args.packages,

+                 results=args.result,

+                 sigkeys=args.sigkey,

+                 koji_event=args.koji_event,

+                 builds=args.builds,

+                 modular_koji_tags=args.modular_tag,

+                 module_defaults_url=args.module_defaults_url,

+                 module_defaults_commit=args.module_defaults_commit,

+                 scratch_modules=args.scratch_module,

+                 **request_args

+             )

+         elif args.command == "create-tag":

+             source = odcs.client.odcs.ComposeSourceTag(

+                 args.tag,

+                 args.packages,

+                 args.build,

+                 args.sigkey,

+                 args.koji_event,

+                 args.modular_tag,

+                 args.module_defaults_url,

+                 args.module_defaults_commit,

+                 args.scratch_module,

+             )

+             result = client.request_compose(source, **request_args)

+         elif args.command == "create-module":

+             if not args.modules and not args.scratch_module:

+                 create_module_parser.error("Please give a module or --scratch-module")

+             if (

+                 len(args.modules) + len(args.scratch_module) > 1

+                 and not (

+                     args.base_module_br_stream

+                     or args.base_module_br_stream_version_lte

+                     or args.base_module_br_stream_version_gte

+                     or args.modular_tag

+                 )

+                 and args.infra == "redhat"

+             ):

+                 print(

+                     "WARNING: Please add --base-module-X or --modular-tag option to the arguments to ensure all composed modules are built for the same release."

+                 )

+             source = odcs.client.odcs.ComposeSourceModule(

+                 args.modules,

+                 args.sigkey,

+                 args.module_defaults_url,

+                 args.module_defaults_commit,

+                 args.scratch_module,

+                 base_module_br_name=args.base_module_br_name,

+                 base_module_br_stream=args.base_module_br_stream,

+                 base_module_br_stream_version_lte=args.base_module_br_stream_version_lte,

+                 base_module_br_stream_version_gte=args.base_module_br_stream_version_gte,

+                 modular_koji_tags=args.modular_tag,

+             )

+             result = client.request_compose(source, **request_args)

+         elif args.command == "create-pulp":

+             source = odcs.client.odcs.ComposeSourcePulp(args.content_sets)

+             result = client.request_compose(source, **request_args)

+         elif args.command == "create-raw-config":

+             source = odcs.client.odcs.ComposeSourceRawConfig(

+                 args.raw_config_name,

+                 args.raw_config_commit,

+                 args.koji_event,

+                 sigkeys=args.sigkey,

+                 builds=args.build,

+             )

+             result = client.request_compose(source, **request_args)

+         elif args.command == "create-build":

+             source = odcs.client.odcs.ComposeSourceBuild(args.builds, args.sigkey)

+             result = client.request_compose(source, **request_args)

+         elif args.command == "wait":

+             result = client.get_compose(args.compose_id)

+         elif args.command == "delete":

+             args.no_wait = True

+             result = client.delete_compose(args.compose_id)

+         elif args.command == "renew":

+             result = client.renew_compose(

+                 args.compose_id, label=args.label, seconds_to_live=args.seconds_to_live

+             )

+         elif args.command == "get":

+             result = client.get_compose(args.compose_id)

+         elif args.command == "about":

+             result = client.about()

+         else:

+             print("Unknown command %s" % args.command)

+     except requests.exceptions.HTTPError:

+         # error message gets printed in ODCS class.

+         sys.exit(-1)

  

-     print(json.dumps(result, indent=4, sort_keys=True))

-     if result["state_name"] == "failed":

-         sys.exit(1)

+     if args.no_wait or args.command == "about":

+         print(json.dumps(result, indent=4, sort_keys=True))

+     else:

+         if result["state_name"] in ["wait", "generating"]:

+             if not args.quiet:

+                 print(

+                     "Waiting for command %s on compose %d to finish."

+                     % (args.command, result["id"])

+                 )

+             try:

+                 result = client.wait_for_compose(

+                     result["id"], 3600, watch_logs=args.watch

+                 )

+             except (KeyboardInterrupt, SystemExit):

+                 pass

+ 

+         print(json.dumps(result, indent=4, sort_keys=True))

+         if result["state_name"] == "failed":

+             sys.exit(1)

@@ -0,0 +1,19 @@ 

+ # ODCS client config file in TOML format.

+ 

+ [fedora]

+ 

+ [fedora.prod]

+ url = "https://odcs.fedoraproject.org"

+ id_provider = "https://id.fedoraproject.org/openidc/"

+ 

+ [fedora.staging]

+ url = "https://odcs.stg.fedoraproject.org"

+ id_provider = "https://id.stg.fedoraproject.org/openidc/"

+ 

+ [redhat]

+ 

+ [redhat.prod]

+ url = "https://odcs.engineering.redhat.com"

+ 

+ [redhat.staging]

+ url = "https://odcs.stage.engineering.redhat.com"

file modified
+1
@@ -5,3 +5,4 @@ 

  opentelemetry-sdk

  opentelemetry-exporter-otlp-proto-http

  opentelemetry-instrumentation-requests

+ tomli; python_version < '3.11'

file modified
+2 -1
@@ -85,9 +85,10 @@ 

      zip_safe=False,

      install_requires=extras_require["client"],

      tests_require=test_requirements,

-     scripts=["client/contrib/odcs", "server/contrib/odcs-promote-compose"],

+     scripts=["server/contrib/odcs-promote-compose"],

      entry_points={

          "console_scripts": [

+             "odcs = odcs.client.cli:main [client]",

              "odcs-gencert = odcs.server.manage:generatelocalhostcert [server]",

              "odcs-frontend = odcs.server.manage:runssl [server]",

              "odcs-mock-runroot = odcs.server.mock_runroot:mock_runroot_main [server]",

file modified
+1 -1
@@ -54,7 +54,7 @@ 

  basepython = python3

  skip_install = true

  deps = black

- commands = black --check --diff client common server setup.py client/contrib/odcs server/contrib/odcs.wsgi server/contrib/odcs_test_deployment server/contrib/odcs-promote-compose

+ commands = black --check --diff client common server setup.py server/contrib/odcs.wsgi server/contrib/odcs_test_deployment server/contrib/odcs-promote-compose

  

  [testenv:docs]

  basepython = python3

JIRA: RHELCMP-13331
Signed-off-by: Haibo Lin hlin@redhat.com

rebased onto 43ad27b

4 months ago

:thumbsup: Looks reasonable to me.

rebased onto 0519588

4 months ago

Pull-Request has been merged by hlin

4 months ago