Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
62 commits
Select commit Hold shift + click to select a range
1266a14
drop manifest requirement when attaching gcsfuse storage
gcie Feb 28, 2025
2b5ec8a
docs
gcie Mar 3, 2025
e79d17d
rename `storage delete` to `storage detach
gcie Mar 3, 2025
bb3c6ba
Merge branch 'develop' into HEAD
gcie Mar 5, 2025
5889d79
do not require manifest in fuse workflow tests
gcie Mar 5, 2025
f5a1068
Merge branch 'develop' into gcie-storage-detach
gcie Mar 5, 2025
3138b60
fix: redundant --cluster parameter
gcie Mar 5, 2025
0a58416
Merge branch 'gcie-storage-attach-gcsfuse' into gcie-storage-detach
gcie Mar 5, 2025
60afe23
change 'user' to 'you'
gcie Mar 5, 2025
940642d
unify gcsfuse template generation algo to match filestore
gcie Mar 5, 2025
26b0efa
gcsfuse storage attach add missing size in docs
gcie Mar 5, 2025
3355eb1
dromp manifest requirement for attaching filestore storage
gcie Mar 6, 2025
c1abbe8
remove obsolete `--manifest` param
gcie Mar 6, 2025
e493524
fix: linting and typing
gcie Mar 6, 2025
aa30b99
fix: linting
gcie Mar 6, 2025
4e15c21
separate filestore instance name from storage volume names
gcie Mar 6, 2025
166cc06
fix: typo
gcie Mar 6, 2025
5bfee31
minor fixes
gcie Mar 7, 2025
95eed8a
remove obsolete error messages
gcie Mar 7, 2025
711a40b
docs
gcie Mar 7, 2025
841a371
Merge branch 'develop' into gcie-storage-attach-gcsfuse
gcie Mar 7, 2025
d2b3e03
fix: readme inconsistencies
gcie Mar 10, 2025
0f09abb
fix: add project id to storage manifest file path
gcie Mar 10, 2025
70310a8
refactor create_storage_crds
gcie Mar 10, 2025
4f63ba9
use Availability.ZONAL instead of hardcoded string value
gcie Mar 10, 2025
d74c584
move loading filestore location logic to load_location function
gcie Mar 10, 2025
2e9c5dc
Merge branch 'develop' into gcie-storage-attach-gcsfuse
gcie Mar 10, 2025
d65e0bd
fix: fuse test messages
gcie Mar 10, 2025
9669815
fix tests: always remove fuse storage after failing tests
gcie Mar 10, 2025
15e0f2d
filestore delete
gcie Mar 10, 2025
86af125
fix error caused by same storage names
gcie Mar 11, 2025
10dc1f7
Merge branch 'gcie-storage-attach-gcsfuse' into gcie-storage-detach
gcie Mar 11, 2025
291b7c0
checking if filestore has attached storages before deletion
gcie Mar 11, 2025
20bff7c
fix: bugs in filestore name passing
gcie Mar 11, 2025
43f2d0e
xpk storage delete docs
gcie Mar 11, 2025
be231ee
add typing of args for storage commands
gcie Mar 12, 2025
89f33a8
Merge branch 'develop' into gcie-storage-detach
gcie Mar 14, 2025
964db17
remove filestore deletion at the end of tests, because it is now hand…
gcie Mar 14, 2025
a7fff0e
missing kind arguments for storage delete
gcie Mar 14, 2025
fc737a8
fix: func name typo from merging
gcie Mar 14, 2025
a15b463
Merge branch 'gcie-storage-detach' into gcie-typing-args
gcie Mar 14, 2025
88c1703
linting
gcie Mar 14, 2025
9428897
fix: typo in func name from merging (again)
gcie Mar 14, 2025
f053305
add loading instance when prompted for fullname
gcie Mar 14, 2025
c6e7340
fix: linting
gcie Mar 14, 2025
ec70da6
handle no annotation the old way
gcie Mar 14, 2025
f2ca921
fix: linting (again)
gcie Mar 14, 2025
de9c928
Merge branch 'gcie-storage-detach' into gcie-typing-args
gcie Mar 17, 2025
7444d08
Merge branch 'develop' into gcie-typing-args
gcie Mar 17, 2025
2c274f7
Merge branch 'develop' into gcie-typing-args
gcie Mar 18, 2025
d475925
args package, refactoring, typing cluster args
gcie Mar 19, 2025
34972c0
ClusterConfig typing
gcie Mar 20, 2025
c1c0d5e
fix: linting, google cla
gcie Mar 20, 2025
c29ba16
Merge branch 'develop' into gcie-typing-args
gcie Apr 23, 2025
e5f643c
refactor args to separate module
gcie Apr 23, 2025
f859abd
typing parsers
gcie Apr 23, 2025
1c250b7
class docstrings
gcie Apr 23, 2025
941be0a
fix circular import
gcie Apr 23, 2025
a35bece
linting
gcie Apr 24, 2025
312d2d2
fix pytype
gcie Apr 24, 2025
bd7da24
add missing 'mount_options' to storage create args
gcie Apr 24, 2025
4bfdda2
Merge branch 'develop' into gcie-typing-args
gcie Apr 25, 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
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ dev = [
version = {attr = "xpk.core.config.__version__"}

[tool.setuptools]
packages = ["xpk", "xpk.parser", "xpk.core", "xpk.commands", "xpk.api", "xpk.templates", "xpk.utils", "xpk.core.blueprint", "xpk.core.remote_state", "xpk.core.workload_decorators"]
packages = ["xpk", "xpk.parser", "xpk.core", "xpk.commands", "xpk.api", "xpk.args", "xpk.templates", "xpk.utils", "xpk.core.blueprint", "xpk.core.remote_state", "xpk.core.workload_decorators"]
package-dir = {"" = "src"}
package-data = {"xpk.api" = ["storage_crd.yaml"], "xpk.templates" = ["storage.yaml"]}

Expand Down
15 changes: 15 additions & 0 deletions src/xpk/args/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
"""
Copyright 2024 Google LLC

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

https://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.
"""
22 changes: 22 additions & 0 deletions src/xpk/args/batch.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
"""
Copyright 2024 Google LLC

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

https://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 .cluster import ClusterConfig
from .slurm import SlurmConfig


class BatchArgs(ClusterConfig, SlurmConfig):
script: str = None
24 changes: 24 additions & 0 deletions src/xpk/args/cluster.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
"""
Copyright 2025 Google LLC

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

https://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 .gcloud_context import GcloudConfig


class ClusterConfig(GcloudConfig):
"""Class representing cluster config"""

kind_cluster: bool = False
cluster: str = None
21 changes: 21 additions & 0 deletions src/xpk/args/common.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
"""
Copyright 2025 Google LLC

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

https://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.
"""


class GlobalConfig:
"""Class representing global args type"""

dry_run: bool = False
128 changes: 128 additions & 0 deletions src/xpk/args/gcloud_context.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
"""
Copyright 2025 Google LLC

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

https://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 Optional

from google.api_core.exceptions import PermissionDenied
from google.cloud import resourcemanager_v3

import subprocess
import sys
from ..utils.console import xpk_exit, xpk_print
from .common import GlobalConfig


def get_project():
"""Get GCE project from `gcloud config get project`.

Returns:
The project name.
"""
completed_command = subprocess.run(
['gcloud', 'config', 'get', 'project'], check=True, capture_output=True
)
project_outputs = completed_command.stdout.decode().strip().split('\n')
if len(project_outputs) < 1 or project_outputs[-1] == '':
sys.exit(
'You must specify the project in the project flag or set it with'
" 'gcloud config set project <project>'"
)
return project_outputs[
-1
] # The project name lives on the last line of the output


def get_zone():
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This are general functions, not specific to args. Maybe they should be moved to utils?

"""Get GCE zone from `gcloud config get compute/zone`.

Returns:
The zone name.
"""
completed_command = subprocess.run(
['gcloud', 'config', 'get', 'compute/zone'],
check=True,
capture_output=True,
)
zone_outputs = completed_command.stdout.decode().strip().split('\n')
if len(zone_outputs) < 1 or zone_outputs[-1] == '':
sys.exit(
"You must specify the zone in the zone flag or set it with 'gcloud"
" config set compute/zone <zone>'"
)
return zone_outputs[-1] # The zone name lives on the last line of the output


class GcloudConfig(GlobalConfig):
"""Class representing gcloud project config"""

gke_version: Optional[str] = None

_zone: Optional[str] = None
_project: Optional[str] = None
_project_number: Optional[str] = None

@property
def zone(self) -> str:
if self._zone is None:
self._zone = get_zone()
if self._project is None:
self._project = get_project()
xpk_print(f'Working on {self._project} and {self._zone}')
return str(self._zone)

@zone.setter
def zone(self, value: str):
self._zone = value

@property
def region(self):
return '-'.join(self.zone.split('-')[:2])

@property
def project(self) -> str:
if self._project is None:
self._project = get_project()
if self._zone is None:
self._zone = get_zone()
xpk_print(f'Working on {self._project} and {self._zone}')
return str(self._project)

@project.setter
def project(self, value: str):
self._project = value

@property
def project_number(self) -> str:
if self._project_number is None:
client = resourcemanager_v3.ProjectsClient()
request = resourcemanager_v3.GetProjectRequest()
request.name = f'projects/{self.project}'
try:
response = client.get_project(request=request)
except PermissionDenied as e:
xpk_print(
f"Couldn't translate project id: {self.project} to project number."
f' Error: {e}'
)
xpk_exit(1)
parts = response.name.split('/', 1)
xpk_print(f'Project number for project: {self.project} is {parts[1]}')
self._project_number = str(parts[1])
return str(self._project_number)

@project_number.setter
def project_number(self, value: str):
self._project_number = value
38 changes: 38 additions & 0 deletions src/xpk/args/slurm.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
"""
Copyright 2025 Google LLC

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

https://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 Optional


class SlurmConfig:
"""Class representing slurm args type"""

ignore_unknown_flags: bool = False
array: Optional[str] = None
cpus_per_task: Optional[str] = None
gpus_per_task: Optional[str] = None
mem: Optional[str] = None
mem_per_task: Optional[str] = None
mem_per_cpu: Optional[str] = None
mem_per_gpu: Optional[str] = None
nodes: Optional[int] = None
ntasks: Optional[int] = None
output: Optional[str] = None
error: Optional[str] = None
input: Optional[str] = None
job_name: Optional[str] = None
chdir: Optional[str] = None
time: Optional[str] = None
85 changes: 85 additions & 0 deletions src/xpk/args/storage.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
"""
Copyright 2024 Google LLC

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

https://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 Optional, Literal, TypeAlias
from .cluster import ClusterConfig


StorageType: TypeAlias = Literal[
'gcsfuse', 'gcpfilestore', 'parallelstore', 'pd'
]

StorageAccessMode: TypeAlias = Literal[
'ReadWriteOnce', 'ReadOnlyMany', 'ReadWriteMany'
]

FilestoreTier: TypeAlias = Literal[
'BASIC_HDD', 'BASIC_SSD', 'ZONAL', 'REGIONAL', 'ENTERPRISE'
]


class StorageAttachArgs(ClusterConfig):
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

StorageAttachArgs and StorageCreateArgs share many fields. Can we move them to new, shared class?

"""Class representing storage attach args type"""

name: str = None
type: StorageType = None
auto_mount: bool = None
mount_point: str = None
readonly: bool = None
size: Optional[int] = None
bucket: Optional[str] = None
vol: Optional[str] = None
access_mode: StorageAccessMode = 'ReadWriteMany'
instance: Optional[str] = None
prefetch_metadata: bool = True
manifest: Optional[str] = None
mount_options: Optional[str] = 'implicit-dirs'


class StorageCreateArgs(ClusterConfig):
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe it should be dataclass? If I want to test StorageCreate command in unit tests(in the future) and I want to pass StorageCreateArgs, is there any way to pass them via constructor?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"""Class representing storage create args type"""

name: str = None
access_mode: StorageAccessMode = 'ReadWriteMany'
vol: str = 'default'
size: int = None
tier: FilestoreTier = 'BASIC_HDD'
type: Literal['gcpfilestore'] = 'gcpfilestore'
auto_mount: bool = None
mount_point: str = None
readonly: bool = None
instance: Optional[str] = None
manifest: Optional[str] = None
mount_options: Optional[str] = 'implicit-dirs'


class StorageDeleteArgs(ClusterConfig):
"""Class representing storage delete args type"""

name: str = None
force: Optional[bool] = False


class StorageDetachArgs(ClusterConfig):
"""Class representing storage detach args type"""

name: str = None


class StorageListArgs(ClusterConfig):
"""Class representing storage list args type"""

pass
35 changes: 35 additions & 0 deletions src/xpk/args/utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
"""
Copyright 2024 Google LLC

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

https://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.
"""

import inspect
from argparse import Namespace
from typing import Any


def apply_args(main_args: Namespace, annotation: Any) -> Any:
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bad naming imo. What does apply_args mean?

args = annotation()
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is no information that annotation is callable. Please add docstring for this function


# getters and setters
for param in inspect.get_annotations(annotation):
if param in main_args:
setattr(args, param, getattr(main_args, param))

# parameters
for param, _ in inspect.getmembers(annotation):
if param in main_args:
setattr(args, param, getattr(main_args, param))

return args # pytype: disable=bad-return-type
Loading