Skip to content

Commit 68da92a

Browse files
committed
Add new msc config validate command
1 parent 991f91a commit 68da92a

File tree

11 files changed

+198
-15
lines changed

11 files changed

+198
-15
lines changed

.release_notes/.unreleased.md

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
11
<!-- To avoid merge conflicts, add items at an arbitrary place in the list. -->
2-
3-
- Added partial file caching with configurable chunk sizes, prefetch control, and source version validation for improved performance on large files
4-
2+
- Add `msc config validate` command.
3+
- Added partial file caching with configurable chunk sizes, prefetch control, and source version validation for improved performance on large files.

docs/src/user_guide/cli.rst

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ The ``msc help`` command displays general help information and available command
2323
msc help <command>
2424
2525
commands:
26+
config MSC configuration management commands
2627
glob Find files using Unix-style wildcard patterns with optional attribute filtering
2728
help Display help for commands
2829
ls List files and directories with optional attribute filtering
@@ -131,6 +132,49 @@ The ``msc glob`` command finds files in a storage service using Unix-style wildc
131132
The ``msc glob`` command works by first listing all files in the specified directory using the equivalent of ``msc ls``, then applying the glob pattern as a post-filter to the results. This means that glob patterns are evaluated locally after retrieving the file listing from the storage service. To reduce number of HEAD requests, either opt in to use metadata provider or use ``msc glob`` for only a specific pattern of files.
132133

133134

135+
**********
136+
msc config
137+
**********
138+
139+
The ``msc config`` command provides configuration management utilities for MSC. Currently, it supports the ``validate`` subcommand to validate and display the resolved MSC configuration.
140+
141+
.. code-block:: text
142+
:caption: config validate command help output
143+
144+
$ msc config validate --help
145+
usage: msc config validate [-h] [--format {json,yaml}]
146+
[--config-file CONFIG_FILE_PATH]
147+
148+
options:
149+
-h, --help show this help message and exit
150+
--format {json,yaml} Output format (default: yaml)
151+
--config-file CONFIG_FILE_PATH
152+
Path to a specific config file (overrides default
153+
search paths)
154+
155+
examples:
156+
# Validate and print resolved MSC configuration based on default search path
157+
msc config validate
158+
159+
# Validate and print resolved MSC configuration based on specific config file
160+
msc config validate --config-file /path/to/config.yaml
161+
162+
.. code-block:: text
163+
:caption: Validate and display configuration in YAML format
164+
165+
$ msc config validate
166+
profiles:
167+
local:
168+
storage_provider:
169+
type: file
170+
options:
171+
base_path: /home/user/
172+
s3-bucket:
173+
storage_provider:
174+
type: s3
175+
options:
176+
base_path: my-bucket
177+
134178
******
135179
msc rm
136180
******

src/multistorageclient/commands/cli/actions/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
# limitations under the License.
1515

1616
from .action import ActionRegistry, MSCArgumentParser
17+
from .config import ConfigAction
1718
from .glob import GlobAction
1819
from .help import HelpAction
1920
from .ls import LsAction
@@ -23,6 +24,7 @@
2324
__all__ = [
2425
"ActionRegistry",
2526
"MSCArgumentParser",
27+
"ConfigAction",
2628
"HelpAction",
2729
"SyncAction",
2830
"GlobAction",

src/multistorageclient/commands/cli/actions/action.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
from typing import Optional
2020

2121

22-
class MSCHelpFormatter(argparse.HelpFormatter):
22+
class MSCHelpFormatter(argparse.RawDescriptionHelpFormatter):
2323
"""
2424
Help message formatter for MSC CLI.
2525
"""
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
# SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
2+
# SPDX-License-Identifier: Apache-2.0
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+
import argparse
17+
import json
18+
import sys
19+
20+
import yaml
21+
22+
from multistorageclient.config import StorageClientConfig
23+
24+
from .action import Action
25+
26+
27+
class ConfigAction(Action):
28+
"""
29+
Action for configuration management commands.
30+
"""
31+
32+
def name(self) -> str:
33+
return "config"
34+
35+
def help(self) -> str:
36+
return "Configuration management commands"
37+
38+
def setup_parser(self, parser: argparse.ArgumentParser) -> None:
39+
# Create subparsers for config subcommands
40+
subparsers = parser.add_subparsers(
41+
title="available commands",
42+
dest="subcommand",
43+
required=True,
44+
)
45+
46+
# Add validate subcommand
47+
validate_parser = subparsers.add_parser(
48+
"validate", help="Validate and print configuration used by MSC", add_help=True
49+
)
50+
51+
# Add options specific to validate subcommand
52+
validate_parser.add_argument(
53+
"--format",
54+
choices=["json", "yaml"],
55+
default="yaml",
56+
help="Output format (default: yaml)",
57+
)
58+
59+
validate_parser.add_argument(
60+
"--config-file",
61+
help="Path to a specific config file (overrides default search paths)",
62+
default=None,
63+
metavar="CONFIG_FILE_PATH",
64+
)
65+
66+
validate_parser.epilog = """examples:
67+
# Validate and print resolved MSC configuration based on default search path
68+
msc config validate
69+
70+
# Validate and print resolved MSC configuration based on specific config file
71+
msc config validate --config-file /path/to/config.yaml
72+
"""
73+
74+
def run(self, args: argparse.Namespace) -> int:
75+
"""Run the config action with parsed arguments."""
76+
if args.subcommand == "validate":
77+
return self._run_validate(args)
78+
else:
79+
print(f"Unknown subcommand: {args.subcommand}")
80+
return 1
81+
82+
def _run_validate(self, args: argparse.Namespace) -> int:
83+
"""Handle the 'config validate' subcommand."""
84+
try:
85+
config_file_paths = [args.config_file] if args.config_file else None
86+
87+
# Get the merged and validated config
88+
config_obj = StorageClientConfig.from_file(config_file_paths=config_file_paths)
89+
config_dict = config_obj._config_dict
90+
91+
# Output in requested format
92+
if args.format == "json":
93+
print(json.dumps(config_dict, indent=2))
94+
else: # yaml
95+
print(yaml.dump(config_dict, default_flow_style=False, sort_keys=False))
96+
return 0
97+
98+
except Exception as e:
99+
print(f"Error: {str(e)}", file=sys.stderr)
100+
return 1

src/multistorageclient/commands/cli/actions/glob.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,6 @@ def help(self) -> str:
3131
return "Find files using Unix-style wildcard patterns with optional attribute filtering"
3232

3333
def setup_parser(self, parser: argparse.ArgumentParser) -> None:
34-
parser.formatter_class = argparse.RawDescriptionHelpFormatter
35-
3634
parser.add_argument(
3735
"--attribute-filter-expression",
3836
"-e",

src/multistorageclient/commands/cli/actions/ls.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,6 @@ def help(self) -> str:
3434
return "List files and directories with optional attribute filtering"
3535

3636
def setup_parser(self, parser: argparse.ArgumentParser) -> None:
37-
parser.formatter_class = argparse.RawDescriptionHelpFormatter
38-
3937
parser.add_argument(
4038
"--attribute-filter-expression",
4139
"-e",

src/multistorageclient/commands/cli/actions/rm.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,6 @@ def help(self) -> str:
3131
return "Delete files or directories"
3232

3333
def setup_parser(self, parser: argparse.ArgumentParser) -> None:
34-
parser.formatter_class = argparse.RawDescriptionHelpFormatter
35-
3634
parser.add_argument(
3735
"--debug",
3836
action="store_true",

src/multistorageclient/commands/cli/actions/sync.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,6 @@ def help(self) -> str:
3232
return "Synchronize files from the source storage to the target storage"
3333

3434
def setup_parser(self, parser: argparse.ArgumentParser) -> None:
35-
parser.formatter_class = argparse.RawDescriptionHelpFormatter
36-
3735
parser.add_argument(
3836
"--delete-unmatched-files",
3937
action="store_true",

src/multistorageclient/commands/cli/main.py

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,16 @@
1818

1919
import multistorageclient as msc
2020

21-
from .actions import ActionRegistry, GlobAction, HelpAction, LsAction, MSCArgumentParser, RmAction, SyncAction
21+
from .actions import (
22+
ActionRegistry,
23+
ConfigAction,
24+
GlobAction,
25+
HelpAction,
26+
LsAction,
27+
MSCArgumentParser,
28+
RmAction,
29+
SyncAction,
30+
)
2231

2332

2433
def create_parser() -> MSCArgumentParser:
@@ -49,6 +58,7 @@ def main() -> int:
4958

5059
# Register commands with instances
5160
registry.register_action(HelpAction(registry))
61+
registry.register_action(ConfigAction())
5262
registry.register_action(SyncAction())
5363
registry.register_action(GlobAction())
5464
registry.register_action(LsAction())
@@ -79,8 +89,8 @@ def main() -> int:
7989
cmd_parser = MSCArgumentParser(prog=f"msc {args.command}", description=action.help())
8090
action.setup_parser(cmd_parser)
8191

82-
# Check if --help is in the subcommand arguments
83-
if "--help" in args.args or "-h" in args.args:
92+
# Check if --help is requested for the parent command (not a subcommand)
93+
if args.args == ["--help"] or args.args == ["-h"]:
8494
cmd_parser.print_help()
8595
return 0
8696

0 commit comments

Comments
 (0)