Skip to content

Commit

Permalink
Merge pull request #123 from seqeralabs/dev
Browse files Browse the repository at this point in the history
Release v0.4.6
  • Loading branch information
ejseqera authored Mar 4, 2024
2 parents c5f53f6 + bc32a9b commit b098220
Show file tree
Hide file tree
Showing 7 changed files with 142 additions and 74 deletions.
21 changes: 16 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ You will need to have an account on Seqera Platform (see [Plans and pricing](htt

`seqerakit` requires the following dependencies:

1. [Seqera Platform CLI](https://github.com/seqeralabs/tower-cli#1-installation)
1. [Seqera Platform CLI (`>=0.9.2`)](https://github.com/seqeralabs/tower-cli#1-installation)

2. [Python (`>=3.8`)](https://www.python.org/downloads/)

Expand Down Expand Up @@ -82,23 +82,34 @@ Create a Seqera Platform access token using the [Seqera Platform](https://tower.
`seqerakit` reads this token from the environment variable `TOWER_ACCESS_TOKEN`. Please export it into your terminal as shown below:

```bash
export TOWER_ACCESS_TOKEN=<your access token>
export TOWER_ACCESS_TOKEN=<Your access token>
```

For Enterprise installations of Seqera Platform, you will also need to configure the API endpoint that will be used to connect to the Platform. You can do so by exporting the following environment variable:
```bash
export TOWER_API_ENDPOINT=<Tower API URL>
```
By default, this is set to `https://api.tower.nf` to connect to Seqera Platform Cloud.


## Usage

To confirm the installation of `seqerakit`, configuration of the Seqera Platform CLI and connection is working as expected:
To confirm the installation of `seqerakit`, configuration of the Seqera Platform CLI and connection to the Platform is working as expected. This will run the `tw info` command under the hood:

```bash
seqerakit --info
```

Use the `-h` or `--help `parameter to list the available commands and their associated options:

Use the `--help` or `-h` parameter to list the available commands and their associated options:
```bash
seqerakit --help
```

Use `--version` or `-v` to retrieve the current version of your seqerakit installation:
```bash
seqerakit --version
```

### Dryrun

To print the commands that would executed with `tw` when using a YAML file, you can run `seqerakit` with the `--dryrun` flag:
Expand Down
3 changes: 3 additions & 0 deletions seqerakit/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import importlib.metadata as importlib_metadata

__version__ = importlib_metadata.version(__name__)
49 changes: 32 additions & 17 deletions seqerakit/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,48 +24,63 @@
from pathlib import Path
from seqerakit import seqeraplatform, helper, overwrite
from seqerakit.seqeraplatform import ResourceExistsError, ResourceCreationError

from seqerakit import __version__

logger = logging.getLogger(__name__)


def parse_args(args=None):
parser = argparse.ArgumentParser()
parser.add_argument(
parser = argparse.ArgumentParser(
description="Seqerakit: Python wrapper for the Seqera Platform CLI"
)
# General options
general = parser.add_argument_group("General Options")
general.add_argument(
"-l",
"--log_level",
default="INFO",
choices=("CRITICAL", "ERROR", "WARNING", "INFO", "DEBUG"),
help="The desired log level (default: INFO).",
type=str.upper,
help="Set the logging level.",
)
parser.add_argument(
general.add_argument(
"--info",
"-i",
action="store_true",
help="Display information about the Seqera Platform and exit",
help="Display Seqera Platform information and exit.",
)
parser.add_argument(
general.add_argument(
"--dryrun",
"-d",
action="store_true",
help="Print the commands that would be executed without running them.",
help="Print the commands that would be executed.",
)
parser.add_argument(
general.add_argument(
"--version",
"-v",
action="version",
version=f"%(prog)s {__version__}",
help="Show version number and exit.",
)

# YAML processing options
yaml_processing = parser.add_argument_group("YAML Processing Options")
yaml_processing.add_argument(
"yaml",
type=Path,
nargs="*", # allow multiple YAML paths
help="One or more YAML files with Seqera Platform resources to create",
nargs="*",
help="One or more YAML files with Seqera Platform resource definitions.",
)
parser.add_argument(
yaml_processing.add_argument(
"--delete",
action="store_true",
help="Recursively delete all resources defined in the YAML file(s)",
help="Recursively delete resources defined in the YAML files.",
)
parser.add_argument(
yaml_processing.add_argument(
"--cli",
dest="cli_args",
type=str,
help="Additional arguments to pass to Seqera Platform"
" CLI enclosed in double quotes (e.g. '--cli=\"--insecure\"')",
help="Additional Seqera Platform CLI specific options to be passed,"
" enclosed in double quotes (e.g. '--cli=\"--insecure\"').",
)
return parser.parse_args(args)

Expand Down
51 changes: 19 additions & 32 deletions seqerakit/helper.py
Original file line number Diff line number Diff line change
Expand Up @@ -113,10 +113,10 @@ def parse_all_yaml(file_paths, destroy=False):
def parse_block(block_name, item):
# Define the mapping from block names to functions.
block_to_function = {
"credentials": parse_credentials_block,
"compute-envs": parse_compute_envs_block,
"credentials": parse_type_block,
"compute-envs": parse_type_block,
"actions": parse_type_block,
"teams": parse_teams_block,
"actions": parse_actions_block,
"datasets": parse_datasets_block,
"pipelines": parse_pipelines_block,
"launch": parse_launch_block,
Expand All @@ -141,27 +141,28 @@ def parse_generic_block(item):
return cmd_args


def parse_credentials_block(item):
def parse_type_block(item, priority_keys=["type", "config-mode", "file-path"]):
cmd_args = []
for key, value in item.items():
if key == "type":
cmd_args.append(str(value))
elif isinstance(value, bool):
if value:
cmd_args.append(f"--{key}")
else:
cmd_args.extend([f"--{key}", str(value)])
return cmd_args

# Ensure at least one of 'type' or 'file-path' is present
if not any(key in item for key in ["type", "file-path"]):
raise ValueError(
"Please specify at least 'type' or 'file-path' for creating the resource."
)

# Process priority keys first
for key in priority_keys:
if key in item:
cmd_args.append(str(item[key]))
del item[key] # Remove the key to avoid repeating in args

def parse_compute_envs_block(item):
cmd_args = []
for key, value in item.items():
if key == "file-path" or key == "type" or key == "config-mode":
cmd_args.append(str(value))
elif isinstance(value, bool):
if isinstance(value, bool):
if value:
cmd_args.append(f"--{key}")
elif key == "params":
temp_file_name = utils.create_temp_yaml(value)
cmd_args.extend(["--params-file", temp_file_name])
else:
cmd_args.extend([f"--{key}", str(value)])
return cmd_args
Expand Down Expand Up @@ -194,20 +195,6 @@ def parse_teams_block(item):
return (cmd_args, members_cmd_args)


def parse_actions_block(item):
cmd_args = []
temp_file_name = None
for key, value in item.items():
if key == "type":
cmd_args.append(str(value))
elif key == "params":
temp_file_name = utils.create_temp_yaml(value)
cmd_args.extend(["--params-file", temp_file_name])
else:
cmd_args.extend([f"--{key}", str(value)])
return cmd_args


def parse_datasets_block(item):
cmd_args = []
for key, value in item.items():
Expand Down
25 changes: 10 additions & 15 deletions seqerakit/overwrite.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ def __init__(self, sp):
"workspaces": {
"keys": ["name", "organization"],
"method_args": self._get_workspace_args,
"name_key": "workspaceId",
"name_key": "workspaceName",
},
}

Expand Down Expand Up @@ -101,7 +101,6 @@ def handle_overwrite(self, block, args, overwrite=False, destroy=False):
self.block_operations["participants"]["name_key"] = "teamName"
else:
self.block_operations["participants"]["name_key"] = "email"

if self.check_resource_exists(operation["name_key"], sp_args):
# if resource exists and overwrite is true, delete
if overwrite:
Expand Down Expand Up @@ -169,11 +168,7 @@ def _get_workspace_args(self, args):
workspaceId used to delete will be retrieved using the _find_workspace_id()
method.
"""
workspace_id = self._find_workspace_id(
json.loads(self.sp.workspaces.list("-o", "json")),
args["organization"],
args["name"],
)
workspace_id = self._find_workspace_id(args["organization"], args["name"])
return ("delete", "--id", str(workspace_id))

def _get_generic_deletion_args(self, args):
Expand Down Expand Up @@ -267,12 +262,12 @@ def _find_workspace_id(self, organization, workspace_name):
organization name and workspace name. This ID will be used to delete the
workspace.
"""
if "workspaces" in self.cached_jsondata:
workspaces = self.cached_jsondata["workspaces"]
for workspace in workspaces:
if (
workspace.get("orgName") == organization
and workspace.get("workspaceName") == workspace_name
):
return workspace.get("workspaceId")
jsondata = json.loads(self.cached_jsondata)
workspaces = jsondata["workspaces"]
for workspace in workspaces:
if (
workspace.get("orgName") == organization
and workspace.get("workspaceName") == workspace_name
):
return workspace.get("workspaceId")
return None
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

from setuptools import find_packages, setup

VERSION = "0.4.5"
VERSION = "0.4.6"

with open("README.md") as f:
readme = f.read()
Expand Down
65 changes: 61 additions & 4 deletions tests/unit/test_helper.py
Original file line number Diff line number Diff line change
Expand Up @@ -130,14 +130,14 @@ def test_create_mock_dataset_yaml(mock_yaml_file):
assert result["datasets"] == expected_block_output


def test_create_mock_computeevs_yaml(mock_yaml_file):
def test_create_mock_computeevs_source_yaml(mock_yaml_file):
test_data = {
"compute-envs": [
{
"name": "test_computeenv",
"workspace": "my_organization/my_workspace",
"credentials": "my_credentials",
"file-path": "./examples/yaml/computeenvs/computeenvs.yaml",
"file-path": "./computeenvs/computeenv.json",
"wait": "AVAILABLE",
"fusion-v2": True,
"fargate": False,
Expand All @@ -149,9 +149,9 @@ def test_create_mock_computeevs_yaml(mock_yaml_file):
expected_block_output = [
{
"cmd_args": [
"./computeenvs/computeenv.json",
"--credentials",
"my_credentials",
"./examples/yaml/computeenvs/computeenvs.yaml",
"--fusion-v2",
"--name",
"test_computeenv",
Expand All @@ -171,6 +171,43 @@ def test_create_mock_computeevs_yaml(mock_yaml_file):
assert result["compute-envs"] == expected_block_output


def test_create_mock_computeevs_cli_yaml(mock_yaml_file):
test_data = {
"compute-envs": [
{
"name": "test_computeenv",
"workspace": "my_organization/my_workspace",
"credentials": "my_credentials",
"type": "aws-batch",
"config-mode": "forge",
"wait": "AVAILABLE",
}
],
}

expected_block_output = [
{
"cmd_args": [
"aws-batch",
"forge",
"--credentials",
"my_credentials",
"--name",
"test_computeenv",
"--wait",
"AVAILABLE",
"--workspace",
"my_organization/my_workspace",
],
"overwrite": False,
}
]
file_path = mock_yaml_file(test_data)
result = helper.parse_all_yaml([file_path])
assert "compute-envs" in result
assert result["compute-envs"] == expected_block_output


def test_create_mock_pipeline_add_yaml(mock_yaml_file):
test_data = {
"pipelines": [
Expand All @@ -191,7 +228,6 @@ def test_create_mock_pipeline_add_yaml(mock_yaml_file):
}
]
}

# params file cmds parsed separately
expected_block_output = [
{
Expand Down Expand Up @@ -239,3 +275,24 @@ def test_empty_yaml_file(mock_yaml_file):
assert f"The file '{file_path}' is empty or does not contain valid data." in str(
e.value
)


def test_error_type_yaml_file(mock_yaml_file):
test_data = {
"compute-envs": [
{
"name": "test_computeenv",
"workspace": "my_organization/my_workspace",
"credentials": "my_credentials",
"wait": "AVAILABLE",
}
],
}
file_path = mock_yaml_file(test_data)

with pytest.raises(ValueError) as e:
helper.parse_all_yaml([file_path])
assert (
"Please specify at least 'type' or 'file-path' for creating the resource."
in str(e.value)
)

0 comments on commit b098220

Please sign in to comment.