oak-cli 0.2.5__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- oak_cli-0.2.5/PKG-INFO +98 -0
- oak_cli-0.2.5/README.md +83 -0
- oak_cli-0.2.5/oak_cli/__init__.py +0 -0
- oak_cli-0.2.5/oak_cli/args_parser/__init__.py +0 -0
- oak_cli-0.2.5/oak_cli/args_parser/apps/__init__.py +0 -0
- oak_cli-0.2.5/oak_cli/args_parser/apps/create.py +40 -0
- oak_cli-0.2.5/oak_cli/args_parser/apps/delete.py +36 -0
- oak_cli-0.2.5/oak_cli/args_parser/apps/main.py +29 -0
- oak_cli-0.2.5/oak_cli/args_parser/apps/status.py +18 -0
- oak_cli-0.2.5/oak_cli/args_parser/main.py +25 -0
- oak_cli-0.2.5/oak_cli/args_parser/services/__init__.py +0 -0
- oak_cli-0.2.5/oak_cli/args_parser/services/deployment.py +53 -0
- oak_cli-0.2.5/oak_cli/args_parser/services/main.py +31 -0
- oak_cli-0.2.5/oak_cli/args_parser/services/status.py +49 -0
- oak_cli-0.2.5/oak_cli/commands/apps/__init__.py +0 -0
- oak_cli-0.2.5/oak_cli/commands/apps/main.py +60 -0
- oak_cli-0.2.5/oak_cli/commands/apps/status.py +21 -0
- oak_cli-0.2.5/oak_cli/commands/services/__init__.py +0 -0
- oak_cli-0.2.5/oak_cli/commands/services/deployment.py +30 -0
- oak_cli-0.2.5/oak_cli/commands/services/get.py +29 -0
- oak_cli-0.2.5/oak_cli/commands/services/status.py +75 -0
- oak_cli-0.2.5/oak_cli/main.py +14 -0
- oak_cli-0.2.5/oak_cli/utils/SLAs/__init__.py +0 -0
- oak_cli-0.2.5/oak_cli/utils/SLAs/blank_app_without_services.SLA.json +13 -0
- oak_cli-0.2.5/oak_cli/utils/SLAs/common.py +13 -0
- oak_cli-0.2.5/oak_cli/utils/SLAs/default_app_with_services.SLA.json +57 -0
- oak_cli-0.2.5/oak_cli/utils/__init__.py +0 -0
- oak_cli-0.2.5/oak_cli/utils/api/__init__.py +0 -0
- oak_cli-0.2.5/oak_cli/utils/api/common.py +31 -0
- oak_cli-0.2.5/oak_cli/utils/api/login.py +33 -0
- oak_cli-0.2.5/oak_cli/utils/api/main.py +108 -0
- oak_cli-0.2.5/oak_cli/utils/argcomplete.py +16 -0
- oak_cli-0.2.5/oak_cli/utils/common.py +6 -0
- oak_cli-0.2.5/oak_cli/utils/logging.py +47 -0
- oak_cli-0.2.5/oak_cli/utils/types.py +22 -0
- oak_cli-0.2.5/pyproject.toml +17 -0
oak_cli-0.2.5/PKG-INFO
ADDED
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: oak_cli
|
|
3
|
+
Version: 0.2.5
|
|
4
|
+
Summary:
|
|
5
|
+
Author: Alexander Malyuk
|
|
6
|
+
Author-email: malyuk.alexander1999@gmail.com
|
|
7
|
+
Requires-Python: >=3.10,<4.0
|
|
8
|
+
Classifier: Programming Language :: Python :: 3
|
|
9
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
10
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
11
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
12
|
+
Requires-Dist: argcomplete (>=3.2.3,<4.0.0)
|
|
13
|
+
Description-Content-Type: text/markdown
|
|
14
|
+
|
|
15
|
+
# Oakestra CLI
|
|
16
|
+
|
|
17
|
+
> NM: Rename system commands to Oakestra
|
|
18
|
+
|
|
19
|
+
**oakestra-cli** is a very basic command line tool for controlling the EdgeIO framework.
|
|
20
|
+
|
|
21
|
+
## Installation
|
|
22
|
+
|
|
23
|
+
Use the package manager [pip](https://pip.pypa.io/en/stable/) and the .whl file in the dist directory to install
|
|
24
|
+
edgeiocli.
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
pip install edgeiocli-0.1.0-py3-none-any.whl
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## Build
|
|
31
|
+
|
|
32
|
+
Use the package [poetry](https://python-poetry.org/) to build the tool and create a new .whl file.
|
|
33
|
+
|
|
34
|
+
Updates the dependency's:
|
|
35
|
+
```bash
|
|
36
|
+
poetry update
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
Installs the package:
|
|
40
|
+
```bash
|
|
41
|
+
poetry install
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
Creates the .whl file:
|
|
45
|
+
```bash
|
|
46
|
+
poetry build
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## Usage
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
edgeiocli COMMAND [ARGS] [OPTIONS]
|
|
53
|
+
|
|
54
|
+
# Try 'edgeiocli --help' for help.
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
## Available Commands
|
|
58
|
+
|
|
59
|
+
- `login [USERNAME]` logs the user into the system and creates access token
|
|
60
|
+
- `logout` logs out the user and deletes the token
|
|
61
|
+
- `change-password` changes the password of the user
|
|
62
|
+
- `application [COMMAND]`
|
|
63
|
+
- `create` creates a new application
|
|
64
|
+
- `delete [APPLICATION_ID]` deletes a application
|
|
65
|
+
- `list-jobs [APPLICATION_ID]` displays all jobs in the application
|
|
66
|
+
- `list` displays all applications of the user
|
|
67
|
+
- `service [COMMAND]`
|
|
68
|
+
- `delete [SERVICE_ID]` deletes the job
|
|
69
|
+
- `create [PATH]` creates a new service
|
|
70
|
+
- `deploy [SERVICE_ID]` tries to deploy a service
|
|
71
|
+
- `user [COMMAND]`
|
|
72
|
+
- `create --role [Admin | Application_Provider | Infrastructure_Provider]` creates a new user
|
|
73
|
+
- `delete [USERNAME] deletes the user`
|
|
74
|
+
- `list` displays all user of the system
|
|
75
|
+
- `set-roles [USERNAME] --role [Admin | Application_Provider | Infrastructure_Provider]`
|
|
76
|
+
|
|
77
|
+
## Deployment
|
|
78
|
+
The given json file should contain only a microservice configuration, as in the example, all the application and user information will be added automatically.
|
|
79
|
+
|
|
80
|
+
```
|
|
81
|
+
{
|
|
82
|
+
...
|
|
83
|
+
"microservice_name": "service",
|
|
84
|
+
"microservice_namespace": "dev",
|
|
85
|
+
"virtualization": "docker",
|
|
86
|
+
"memory": 100,
|
|
87
|
+
...
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
## Contributing
|
|
93
|
+
|
|
94
|
+
This is the first version of **oakestra-cli** and does not yet offer all features, also there
|
|
95
|
+
might be some bugs, if you find one please report it or fix it.
|
|
96
|
+
|
|
97
|
+
To add a new command create a function in the main.py file and add the `@app.command()` annotation to the function. If you want to create sub commands, create the command in the corresponding file.
|
|
98
|
+
|
oak_cli-0.2.5/README.md
ADDED
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
# Oakestra CLI
|
|
2
|
+
|
|
3
|
+
> NM: Rename system commands to Oakestra
|
|
4
|
+
|
|
5
|
+
**oakestra-cli** is a very basic command line tool for controlling the EdgeIO framework.
|
|
6
|
+
|
|
7
|
+
## Installation
|
|
8
|
+
|
|
9
|
+
Use the package manager [pip](https://pip.pypa.io/en/stable/) and the .whl file in the dist directory to install
|
|
10
|
+
edgeiocli.
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
pip install edgeiocli-0.1.0-py3-none-any.whl
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
## Build
|
|
17
|
+
|
|
18
|
+
Use the package [poetry](https://python-poetry.org/) to build the tool and create a new .whl file.
|
|
19
|
+
|
|
20
|
+
Updates the dependency's:
|
|
21
|
+
```bash
|
|
22
|
+
poetry update
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
Installs the package:
|
|
26
|
+
```bash
|
|
27
|
+
poetry install
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
Creates the .whl file:
|
|
31
|
+
```bash
|
|
32
|
+
poetry build
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## Usage
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
edgeiocli COMMAND [ARGS] [OPTIONS]
|
|
39
|
+
|
|
40
|
+
# Try 'edgeiocli --help' for help.
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
## Available Commands
|
|
44
|
+
|
|
45
|
+
- `login [USERNAME]` logs the user into the system and creates access token
|
|
46
|
+
- `logout` logs out the user and deletes the token
|
|
47
|
+
- `change-password` changes the password of the user
|
|
48
|
+
- `application [COMMAND]`
|
|
49
|
+
- `create` creates a new application
|
|
50
|
+
- `delete [APPLICATION_ID]` deletes a application
|
|
51
|
+
- `list-jobs [APPLICATION_ID]` displays all jobs in the application
|
|
52
|
+
- `list` displays all applications of the user
|
|
53
|
+
- `service [COMMAND]`
|
|
54
|
+
- `delete [SERVICE_ID]` deletes the job
|
|
55
|
+
- `create [PATH]` creates a new service
|
|
56
|
+
- `deploy [SERVICE_ID]` tries to deploy a service
|
|
57
|
+
- `user [COMMAND]`
|
|
58
|
+
- `create --role [Admin | Application_Provider | Infrastructure_Provider]` creates a new user
|
|
59
|
+
- `delete [USERNAME] deletes the user`
|
|
60
|
+
- `list` displays all user of the system
|
|
61
|
+
- `set-roles [USERNAME] --role [Admin | Application_Provider | Infrastructure_Provider]`
|
|
62
|
+
|
|
63
|
+
## Deployment
|
|
64
|
+
The given json file should contain only a microservice configuration, as in the example, all the application and user information will be added automatically.
|
|
65
|
+
|
|
66
|
+
```
|
|
67
|
+
{
|
|
68
|
+
...
|
|
69
|
+
"microservice_name": "service",
|
|
70
|
+
"microservice_namespace": "dev",
|
|
71
|
+
"virtualization": "docker",
|
|
72
|
+
"memory": 100,
|
|
73
|
+
...
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
## Contributing
|
|
79
|
+
|
|
80
|
+
This is the first version of **oakestra-cli** and does not yet offer all features, also there
|
|
81
|
+
might be some bugs, if you find one please report it or fix it.
|
|
82
|
+
|
|
83
|
+
To add a new command create a function in the main.py file and add the `@app.command()` annotation to the function. If you want to create sub commands, create the command in the corresponding file.
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import argparse
|
|
2
|
+
from typing import Any
|
|
3
|
+
|
|
4
|
+
from oak_cli.commands.apps.main import send_sla
|
|
5
|
+
from oak_cli.commands.services.deployment import deploy_new_instance
|
|
6
|
+
from oak_cli.utils.SLAs.common import KnownSLA
|
|
7
|
+
from oak_cli.utils.types import Subparsers
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def _aux_create_application(args: Any) -> None:
|
|
11
|
+
apps = send_sla(args.sla)
|
|
12
|
+
if args.deploy:
|
|
13
|
+
for app in apps:
|
|
14
|
+
for service_id in app["microservices"]:
|
|
15
|
+
deploy_new_instance(service_id)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def prepare_applications_create_argparser(applications_subparsers: Subparsers) -> None:
|
|
19
|
+
HELP_TEXT = "creates a new application"
|
|
20
|
+
applications_create_parser = applications_subparsers.add_parser(
|
|
21
|
+
"create",
|
|
22
|
+
aliases=["c"],
|
|
23
|
+
help=HELP_TEXT,
|
|
24
|
+
description=HELP_TEXT,
|
|
25
|
+
formatter_class=argparse.RawTextHelpFormatter,
|
|
26
|
+
)
|
|
27
|
+
applications_create_parser.add_argument(
|
|
28
|
+
"-d",
|
|
29
|
+
"--deploy",
|
|
30
|
+
action="store_true",
|
|
31
|
+
help="deploy all services of the provided application(s)",
|
|
32
|
+
)
|
|
33
|
+
applications_create_parser.add_argument(
|
|
34
|
+
"sla",
|
|
35
|
+
help="creates an application based on a KnowsSLA (if is has its own enum)",
|
|
36
|
+
type=KnownSLA,
|
|
37
|
+
choices=KnownSLA,
|
|
38
|
+
default=KnownSLA.DEFAULT,
|
|
39
|
+
)
|
|
40
|
+
applications_create_parser.set_defaults(func=_aux_create_application)
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import argparse
|
|
2
|
+
from typing import Any
|
|
3
|
+
|
|
4
|
+
from oak_cli.commands.apps.main import delete_all_applications, delete_application
|
|
5
|
+
from oak_cli.utils.types import Subparsers
|
|
6
|
+
|
|
7
|
+
_APP_ID_HELP_TEXT = """
|
|
8
|
+
if a (single) application ID is provided only that one app will be deleted
|
|
9
|
+
if 'all' is provided every application gets deleted"""
|
|
10
|
+
|
|
11
|
+
_DELETION_HELP_TEXT = "deletes one or all applications" + _APP_ID_HELP_TEXT
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def prepare_applications_deletion_argparser(
|
|
15
|
+
applications_subparsers: Subparsers,
|
|
16
|
+
) -> None:
|
|
17
|
+
def aux_delete_appliacations(args: Any):
|
|
18
|
+
if args.all:
|
|
19
|
+
delete_all_applications()
|
|
20
|
+
else:
|
|
21
|
+
delete_application(args.app_id)
|
|
22
|
+
|
|
23
|
+
applications_deletion_parser = applications_subparsers.add_parser(
|
|
24
|
+
"delete",
|
|
25
|
+
aliases=["d"],
|
|
26
|
+
help=_DELETION_HELP_TEXT,
|
|
27
|
+
description=_DELETION_HELP_TEXT,
|
|
28
|
+
formatter_class=argparse.RawTextHelpFormatter,
|
|
29
|
+
)
|
|
30
|
+
applications_deletion_parser.add_argument(
|
|
31
|
+
"-a", "--all", action="store_true", help="delete all applications"
|
|
32
|
+
)
|
|
33
|
+
applications_deletion_parser.add_argument(
|
|
34
|
+
"app_id", nargs="?", type=str, help=_APP_ID_HELP_TEXT
|
|
35
|
+
)
|
|
36
|
+
applications_deletion_parser.set_defaults(func=aux_delete_appliacations)
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import argparse
|
|
2
|
+
from typing import Any
|
|
3
|
+
|
|
4
|
+
from oak_cli.args_parser.apps.create import prepare_applications_create_argparser
|
|
5
|
+
from oak_cli.args_parser.apps.delete import prepare_applications_deletion_argparser
|
|
6
|
+
from oak_cli.args_parser.apps.status import prepare_applications_display_argparser
|
|
7
|
+
from oak_cli.utils.types import Subparsers
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def prepare_applications_argparsers(subparsers: Subparsers) -> None:
|
|
11
|
+
applications_parser = subparsers.add_parser(
|
|
12
|
+
"applications",
|
|
13
|
+
aliases=["a"],
|
|
14
|
+
help="command for application(s) related activities",
|
|
15
|
+
formatter_class=argparse.RawTextHelpFormatter,
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
def applications_parser_print_help(_: Any) -> None:
|
|
19
|
+
applications_parser.print_help()
|
|
20
|
+
|
|
21
|
+
applications_parser.set_defaults(func=applications_parser_print_help)
|
|
22
|
+
|
|
23
|
+
applications_subparsers = applications_parser.add_subparsers(
|
|
24
|
+
dest="applications commands",
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
prepare_applications_create_argparser(applications_subparsers)
|
|
28
|
+
prepare_applications_display_argparser(applications_subparsers)
|
|
29
|
+
prepare_applications_deletion_argparser(applications_subparsers)
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import argparse
|
|
2
|
+
|
|
3
|
+
from oak_cli.commands.apps.status import display_current_applications
|
|
4
|
+
from oak_cli.utils.types import Subparsers
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def prepare_applications_display_argparser(applications_subparsers: Subparsers) -> None:
|
|
8
|
+
HELP_TEXT = "displays the currently available/active applications"
|
|
9
|
+
applications_status_parser = applications_subparsers.add_parser(
|
|
10
|
+
"status",
|
|
11
|
+
aliases=["s"],
|
|
12
|
+
help=HELP_TEXT,
|
|
13
|
+
description=HELP_TEXT,
|
|
14
|
+
formatter_class=argparse.RawTextHelpFormatter,
|
|
15
|
+
)
|
|
16
|
+
applications_status_parser.set_defaults(
|
|
17
|
+
func=lambda _: display_current_applications()
|
|
18
|
+
)
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# PYTHON_ARGCOMPLETE_OK
|
|
2
|
+
|
|
3
|
+
import argparse
|
|
4
|
+
|
|
5
|
+
import argcomplete
|
|
6
|
+
|
|
7
|
+
from oak_cli.args_parser.apps.main import prepare_applications_argparsers
|
|
8
|
+
from oak_cli.args_parser.services.main import prepare_services_argparsers
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def parse_arguments_and_execute() -> None:
|
|
12
|
+
parser = argparse.ArgumentParser()
|
|
13
|
+
|
|
14
|
+
# 'dest' & 'required' are needed to ensure correct behavior if no arguments are passed.
|
|
15
|
+
subparsers = parser.add_subparsers(
|
|
16
|
+
dest="command",
|
|
17
|
+
required=True,
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
prepare_applications_argparsers(subparsers)
|
|
21
|
+
prepare_services_argparsers(subparsers)
|
|
22
|
+
|
|
23
|
+
argcomplete.autocomplete(parser)
|
|
24
|
+
args = parser.parse_args()
|
|
25
|
+
args.func(args)
|
|
File without changes
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import argparse
|
|
2
|
+
from typing import Any
|
|
3
|
+
|
|
4
|
+
from oak_cli.commands.services.deployment import (
|
|
5
|
+
deploy_new_instance,
|
|
6
|
+
undeploy_all_instances_of_service,
|
|
7
|
+
undeploy_instance,
|
|
8
|
+
)
|
|
9
|
+
from oak_cli.utils.types import Subparsers
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def prepare_services_deployment_argparser(services_subparsers: Subparsers) -> None:
|
|
13
|
+
HELP_TEXT = "deploys a service instance"
|
|
14
|
+
services_deployment_parser = services_subparsers.add_parser(
|
|
15
|
+
"deploy",
|
|
16
|
+
aliases=["d"],
|
|
17
|
+
help=HELP_TEXT,
|
|
18
|
+
description=HELP_TEXT,
|
|
19
|
+
formatter_class=argparse.RawTextHelpFormatter,
|
|
20
|
+
)
|
|
21
|
+
services_deployment_parser.add_argument("service_id", type=str)
|
|
22
|
+
services_deployment_parser.set_defaults(
|
|
23
|
+
func=lambda args: deploy_new_instance(args.service_id)
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def prepare_services_undeployment_argparser(services_subparsers: Subparsers) -> None:
|
|
28
|
+
def _aux_undeploy(args: Any) -> None:
|
|
29
|
+
if args.all:
|
|
30
|
+
undeploy_all_instances_of_service(args.service_id)
|
|
31
|
+
elif args.instancenumber:
|
|
32
|
+
undeploy_instance(args.service_id, args.instancenumber)
|
|
33
|
+
else:
|
|
34
|
+
undeploy_instance(args.service_id)
|
|
35
|
+
|
|
36
|
+
HELP_TEXT = "undeploys service instances"
|
|
37
|
+
services_undeployment_parser = services_subparsers.add_parser(
|
|
38
|
+
"undeploy",
|
|
39
|
+
aliases=["u"],
|
|
40
|
+
help=HELP_TEXT,
|
|
41
|
+
description=HELP_TEXT,
|
|
42
|
+
formatter_class=argparse.RawTextHelpFormatter,
|
|
43
|
+
)
|
|
44
|
+
services_undeployment_parser.add_argument("service_id", type=str)
|
|
45
|
+
services_undeployment_parser.add_argument(
|
|
46
|
+
"--instancenumber",
|
|
47
|
+
"-i",
|
|
48
|
+
type=str,
|
|
49
|
+
)
|
|
50
|
+
services_undeployment_parser.add_argument(
|
|
51
|
+
"-a", "--all", action="store_true", help="undeploy all instances of the service"
|
|
52
|
+
)
|
|
53
|
+
services_undeployment_parser.set_defaults(func=_aux_undeploy)
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import argparse
|
|
2
|
+
from typing import Any
|
|
3
|
+
|
|
4
|
+
from oak_cli.args_parser.services.deployment import (
|
|
5
|
+
prepare_services_deployment_argparser,
|
|
6
|
+
prepare_services_undeployment_argparser,
|
|
7
|
+
)
|
|
8
|
+
from oak_cli.args_parser.services.status import prepare_services_display_argparser
|
|
9
|
+
from oak_cli.utils.types import Subparsers
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def prepare_services_argparsers(subparsers: Subparsers) -> None:
|
|
13
|
+
services_parser = subparsers.add_parser(
|
|
14
|
+
"services",
|
|
15
|
+
aliases=["s"],
|
|
16
|
+
help="command for service(s) related activities",
|
|
17
|
+
formatter_class=argparse.RawTextHelpFormatter,
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
def services_parser_print_help(_: Any) -> None:
|
|
21
|
+
services_parser.print_help()
|
|
22
|
+
|
|
23
|
+
services_parser.set_defaults(func=services_parser_print_help)
|
|
24
|
+
|
|
25
|
+
services_subparsers = services_parser.add_subparsers(
|
|
26
|
+
dest="services commands",
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
prepare_services_display_argparser(services_subparsers)
|
|
30
|
+
prepare_services_deployment_argparser(services_subparsers)
|
|
31
|
+
prepare_services_undeployment_argparser(services_subparsers)
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import argparse
|
|
2
|
+
from typing import Any
|
|
3
|
+
|
|
4
|
+
from oak_cli.commands.services.get import get_single_service
|
|
5
|
+
from oak_cli.commands.services.status import (
|
|
6
|
+
display_all_current_services,
|
|
7
|
+
display_single_service,
|
|
8
|
+
)
|
|
9
|
+
from oak_cli.utils.types import Subparsers
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def _aux_display_current_services(args: Any) -> None:
|
|
13
|
+
if not args.serviceid:
|
|
14
|
+
display_all_current_services(verbose=args.verbose, app_id=args.appid)
|
|
15
|
+
elif args.serviceid:
|
|
16
|
+
service = get_single_service(args.serviceid)
|
|
17
|
+
display_single_service(service, verbose=args.verbose)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def prepare_services_display_argparser(services_subparsers: Subparsers) -> None:
|
|
21
|
+
HELP_TEXT = "displays the currently available/active service(s)"
|
|
22
|
+
services_status_parser = services_subparsers.add_parser(
|
|
23
|
+
"status",
|
|
24
|
+
aliases=["s"],
|
|
25
|
+
help=HELP_TEXT,
|
|
26
|
+
description=HELP_TEXT,
|
|
27
|
+
formatter_class=argparse.RawTextHelpFormatter,
|
|
28
|
+
)
|
|
29
|
+
services_status_parser.add_argument(
|
|
30
|
+
"-a",
|
|
31
|
+
"--all",
|
|
32
|
+
help="displays all services",
|
|
33
|
+
action="store_true",
|
|
34
|
+
)
|
|
35
|
+
services_status_parser.add_argument(
|
|
36
|
+
"-v",
|
|
37
|
+
"--verbose",
|
|
38
|
+
action="store_true",
|
|
39
|
+
)
|
|
40
|
+
services_status_parser.add_argument(
|
|
41
|
+
"--appid",
|
|
42
|
+
type=str,
|
|
43
|
+
)
|
|
44
|
+
services_status_parser.add_argument(
|
|
45
|
+
"--serviceid",
|
|
46
|
+
"-i",
|
|
47
|
+
type=str,
|
|
48
|
+
)
|
|
49
|
+
services_status_parser.set_defaults(func=_aux_display_current_services)
|
|
File without changes
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import enum
|
|
2
|
+
import json
|
|
3
|
+
from typing import List
|
|
4
|
+
|
|
5
|
+
from oak_cli.utils.api.common import SYSTEM_MANAGER_URL, HttpMethod
|
|
6
|
+
from oak_cli.utils.api.main import handle_request
|
|
7
|
+
from oak_cli.utils.SLAs.common import get_SLAs_path
|
|
8
|
+
from oak_cli.utils.types import Application, ApplicationId
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def get_application(app_id: ApplicationId) -> Application:
|
|
12
|
+
app = handle_request(
|
|
13
|
+
base_url=SYSTEM_MANAGER_URL,
|
|
14
|
+
http_method=HttpMethod.GET,
|
|
15
|
+
api_endpoint=f"/api/application/{app_id}",
|
|
16
|
+
what_should_happen=f"Get application '{app_id}'",
|
|
17
|
+
)
|
|
18
|
+
return app
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def get_applications() -> List[Application]:
|
|
22
|
+
apps = handle_request(
|
|
23
|
+
base_url=SYSTEM_MANAGER_URL,
|
|
24
|
+
http_method=HttpMethod.GET,
|
|
25
|
+
api_endpoint="/api/applications",
|
|
26
|
+
what_should_happen="Get all applications",
|
|
27
|
+
)
|
|
28
|
+
return apps
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def send_sla(sla_enum: enum) -> List[Application]:
|
|
32
|
+
sla_file_name = f"{sla_enum}.SLA.json"
|
|
33
|
+
SLA = ""
|
|
34
|
+
with open(get_SLAs_path() / sla_file_name, "r") as f:
|
|
35
|
+
SLA = json.load(f)
|
|
36
|
+
|
|
37
|
+
apps = handle_request(
|
|
38
|
+
base_url=SYSTEM_MANAGER_URL,
|
|
39
|
+
http_method=HttpMethod.POST,
|
|
40
|
+
data=SLA,
|
|
41
|
+
api_endpoint="/api/application",
|
|
42
|
+
what_should_happen=f"Create new application based on '{sla_enum}'",
|
|
43
|
+
show_msg_on_success=True,
|
|
44
|
+
)
|
|
45
|
+
return apps
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def delete_application(app_id: ApplicationId) -> None:
|
|
49
|
+
handle_request(
|
|
50
|
+
base_url=SYSTEM_MANAGER_URL,
|
|
51
|
+
http_method=HttpMethod.DELETE,
|
|
52
|
+
api_endpoint=f"/api/application/{app_id}",
|
|
53
|
+
what_should_happen=f"Delete application '{app_id}'",
|
|
54
|
+
show_msg_on_success=True,
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def delete_all_applications() -> None:
|
|
59
|
+
for app in get_applications():
|
|
60
|
+
delete_application(app["applicationID"])
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
from oak_cli.commands.apps.main import get_applications
|
|
2
|
+
from oak_cli.utils.logging import logger
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
def display_current_applications() -> None:
|
|
6
|
+
current_applications = get_applications()
|
|
7
|
+
|
|
8
|
+
def log_aux(key: str, value: str) -> None:
|
|
9
|
+
logger.debug(f" {key}: '{value}'")
|
|
10
|
+
|
|
11
|
+
logger.info(f"Current apps: '{len(current_applications)}'")
|
|
12
|
+
for i, application in enumerate(current_applications):
|
|
13
|
+
logger.info(f" App '{i}':")
|
|
14
|
+
log_aux("id", application["applicationID"])
|
|
15
|
+
log_aux("name", application["application_name"])
|
|
16
|
+
log_aux("ns", application["application_namespace"])
|
|
17
|
+
log_aux("desc", application["application_desc"])
|
|
18
|
+
log_aux(
|
|
19
|
+
"microservices",
|
|
20
|
+
f"{len(application['microservices'])}: {application['microservices']}",
|
|
21
|
+
)
|
|
File without changes
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
from oak_cli.commands.services.get import get_single_service
|
|
2
|
+
from oak_cli.utils.api.common import SYSTEM_MANAGER_URL, HttpMethod
|
|
3
|
+
from oak_cli.utils.api.main import handle_request
|
|
4
|
+
from oak_cli.utils.types import Id, ServiceId
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def deploy_new_instance(service_id: ServiceId) -> None:
|
|
8
|
+
handle_request(
|
|
9
|
+
base_url=SYSTEM_MANAGER_URL,
|
|
10
|
+
http_method=HttpMethod.POST,
|
|
11
|
+
api_endpoint=f"/api/service/{service_id}/instance",
|
|
12
|
+
what_should_happen=f"Deploy a new instance for the service '{service_id}'",
|
|
13
|
+
show_msg_on_success=True,
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def undeploy_instance(service_id: ServiceId, instance_id: Id = None) -> None:
|
|
18
|
+
handle_request(
|
|
19
|
+
base_url=SYSTEM_MANAGER_URL,
|
|
20
|
+
http_method=HttpMethod.DELETE,
|
|
21
|
+
api_endpoint=f"/api/service/{service_id}/instance/{instance_id or 0}",
|
|
22
|
+
what_should_happen=f"Undeploy instance '{instance_id or 0}' for the service '{service_id}'",
|
|
23
|
+
show_msg_on_success=True,
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def undeploy_all_instances_of_service(service_id: ServiceId) -> None:
|
|
28
|
+
service = get_single_service(service_id)
|
|
29
|
+
for instance in service["instance_list"]:
|
|
30
|
+
undeploy_instance(service_id, instance["instance_number"])
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
from typing import List
|
|
2
|
+
|
|
3
|
+
from oak_cli.utils.api.common import SYSTEM_MANAGER_URL, HttpMethod
|
|
4
|
+
from oak_cli.utils.api.main import handle_request
|
|
5
|
+
from oak_cli.utils.types import Service, ServiceId
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def get_single_service(service_id: ServiceId) -> Service:
|
|
9
|
+
service = handle_request(
|
|
10
|
+
base_url=SYSTEM_MANAGER_URL,
|
|
11
|
+
http_method=HttpMethod.GET,
|
|
12
|
+
api_endpoint=f"/api/service/{service_id}",
|
|
13
|
+
what_should_happen=f"Get single service '{service_id}'",
|
|
14
|
+
)
|
|
15
|
+
return service
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def get_all_services(app_id: ServiceId = None) -> List[Service]:
|
|
19
|
+
what_should_happen = "Get all services"
|
|
20
|
+
if app_id:
|
|
21
|
+
what_should_happen += f" of app '{app_id}'"
|
|
22
|
+
|
|
23
|
+
services = handle_request(
|
|
24
|
+
base_url=SYSTEM_MANAGER_URL,
|
|
25
|
+
http_method=HttpMethod.GET,
|
|
26
|
+
api_endpoint=f"/api/services/{app_id or ''}",
|
|
27
|
+
what_should_happen=what_should_happen,
|
|
28
|
+
)
|
|
29
|
+
return services
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
from oak_cli.commands.services.get import get_all_services
|
|
2
|
+
from oak_cli.utils.logging import logger
|
|
3
|
+
from oak_cli.utils.types import ApplicationId, Service
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def _log_aux_service(key: str, value: str, service: Service) -> None:
|
|
7
|
+
value = service.get(value, None)
|
|
8
|
+
if value is not None:
|
|
9
|
+
_log_aux(key, value)
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def _log_aux(key: str, value: str) -> None:
|
|
13
|
+
logger.debug(f" {key}: '{value}'")
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def verbose_log_aux(section_name: str) -> None:
|
|
17
|
+
logger.info(f" - {section_name} -")
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def display_single_service(service: Service, verbose: bool = False) -> None:
|
|
21
|
+
verbose_log_aux("microservice")
|
|
22
|
+
_log_aux_service("id", "microserviceID", service)
|
|
23
|
+
_log_aux_service(
|
|
24
|
+
"microservice name" if verbose else "name", "microservice_name", service
|
|
25
|
+
)
|
|
26
|
+
_log_aux_service(
|
|
27
|
+
"microservice ns" if verbose else "ns", "microservice_namespace", service
|
|
28
|
+
)
|
|
29
|
+
_log_aux("parent app", f"{service['app_name']}: {service['applicationID']}")
|
|
30
|
+
|
|
31
|
+
if verbose:
|
|
32
|
+
verbose_log_aux("service")
|
|
33
|
+
_log_aux_service("service name", "service_name", service)
|
|
34
|
+
|
|
35
|
+
verbose_log_aux("resources")
|
|
36
|
+
_log_aux_service("memory", "memory", service)
|
|
37
|
+
_log_aux_service("vcpus", "vcpus", service)
|
|
38
|
+
if verbose:
|
|
39
|
+
_log_aux_service("storage", "storage", service)
|
|
40
|
+
_log_aux_service("vgpus", "vgpus", service)
|
|
41
|
+
|
|
42
|
+
verbose_log_aux("container")
|
|
43
|
+
_log_aux_service("image", "image", service)
|
|
44
|
+
if verbose:
|
|
45
|
+
_log_aux_service("code", "code", service)
|
|
46
|
+
|
|
47
|
+
verbose_log_aux("networking")
|
|
48
|
+
_log_aux_service("port", "port", service)
|
|
49
|
+
if verbose:
|
|
50
|
+
_log_aux_service("bandwidth in", "bandwidth_in", service)
|
|
51
|
+
_log_aux_service("bandwidth out", "bandwidth_out", service)
|
|
52
|
+
|
|
53
|
+
verbose_log_aux("instances")
|
|
54
|
+
instances = service["instance_list"]
|
|
55
|
+
num_instances = len(instances)
|
|
56
|
+
_log_aux("instances", num_instances)
|
|
57
|
+
if verbose and num_instances > 0:
|
|
58
|
+
for i, instance in enumerate(instances):
|
|
59
|
+
logger.info(f" Instance '{i}':")
|
|
60
|
+
_log_aux(" instance_number", instance["instance_number"])
|
|
61
|
+
_log_aux(" status", instance["status"])
|
|
62
|
+
_log_aux(" publicip", instance["publicip"])
|
|
63
|
+
_log_aux(" cpu", instance["cpu"])
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def display_all_current_services(
|
|
67
|
+
verbose: bool = False,
|
|
68
|
+
app_id: ApplicationId = None,
|
|
69
|
+
) -> None:
|
|
70
|
+
all_current_services = get_all_services(app_id)
|
|
71
|
+
|
|
72
|
+
logger.info(f"All current services: '{len(all_current_services)}'")
|
|
73
|
+
for i, service in enumerate(all_current_services):
|
|
74
|
+
logger.warning(f" Service '{i}':")
|
|
75
|
+
display_single_service(service, verbose)
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# PYTHON_ARGCOMPLETE_OK
|
|
3
|
+
|
|
4
|
+
from oak_cli.args_parser.main import parse_arguments_and_execute
|
|
5
|
+
from oak_cli.utils.argcomplete import handle_argcomplete
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def main():
|
|
9
|
+
handle_argcomplete()
|
|
10
|
+
parse_arguments_and_execute()
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
if __name__ == "__main__":
|
|
14
|
+
main()
|
|
File without changes
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
{
|
|
2
|
+
"sla_version": "v2.0",
|
|
3
|
+
"customerID": "Admin",
|
|
4
|
+
"applications": [
|
|
5
|
+
{
|
|
6
|
+
"applicationID": "",
|
|
7
|
+
"application_name": "blank",
|
|
8
|
+
"application_namespace": "blank",
|
|
9
|
+
"application_desc": "Blank app without any microservices",
|
|
10
|
+
"microservices": []
|
|
11
|
+
}
|
|
12
|
+
]
|
|
13
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import pathlib
|
|
2
|
+
|
|
3
|
+
from oak_cli.utils.common import get_oak_cli_path
|
|
4
|
+
from oak_cli.utils.types import CustomEnum
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def get_SLAs_path() -> pathlib.Path:
|
|
8
|
+
return get_oak_cli_path() / "utils" / "SLAs"
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class KnownSLA(CustomEnum):
|
|
12
|
+
DEFAULT = "default_app_with_services"
|
|
13
|
+
BLANK = "blank_app_without_services"
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
{
|
|
2
|
+
"sla_version": "v2.0",
|
|
3
|
+
"customerID": "Admin",
|
|
4
|
+
"applications": [
|
|
5
|
+
{
|
|
6
|
+
"applicationID": "",
|
|
7
|
+
"application_name": "clientsrvr",
|
|
8
|
+
"application_namespace": "test",
|
|
9
|
+
"application_desc": "Simple demo with curl client and Nginx server",
|
|
10
|
+
"microservices": [
|
|
11
|
+
{
|
|
12
|
+
"microserviceID": "",
|
|
13
|
+
"microservice_name": "curl",
|
|
14
|
+
"microservice_namespace": "test",
|
|
15
|
+
"virtualization": "container",
|
|
16
|
+
"cmd": [
|
|
17
|
+
"sh",
|
|
18
|
+
"-c",
|
|
19
|
+
"tail -f /dev/null"
|
|
20
|
+
],
|
|
21
|
+
"memory": 100,
|
|
22
|
+
"vcpus": 1,
|
|
23
|
+
"vgpus": 0,
|
|
24
|
+
"vtpus": 0,
|
|
25
|
+
"bandwidth_in": 0,
|
|
26
|
+
"bandwidth_out": 0,
|
|
27
|
+
"storage": 0,
|
|
28
|
+
"code": "docker.io/curlimages/curl:7.82.0",
|
|
29
|
+
"state": "",
|
|
30
|
+
"port": "9080",
|
|
31
|
+
"added_files": []
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
"microserviceID": "",
|
|
35
|
+
"microservice_name": "nginx",
|
|
36
|
+
"microservice_namespace": "test",
|
|
37
|
+
"virtualization": "container",
|
|
38
|
+
"cmd": [],
|
|
39
|
+
"memory": 100,
|
|
40
|
+
"vcpus": 1,
|
|
41
|
+
"vgpus": 0,
|
|
42
|
+
"vtpus": 0,
|
|
43
|
+
"bandwidth_in": 0,
|
|
44
|
+
"bandwidth_out": 0,
|
|
45
|
+
"storage": 0,
|
|
46
|
+
"code": "docker.io/library/nginx:latest",
|
|
47
|
+
"state": "",
|
|
48
|
+
"port": "6080:60/tcp",
|
|
49
|
+
"addresses": {
|
|
50
|
+
"rr_ip": "10.30.30.30"
|
|
51
|
+
},
|
|
52
|
+
"added_files": []
|
|
53
|
+
}
|
|
54
|
+
]
|
|
55
|
+
}
|
|
56
|
+
]
|
|
57
|
+
}
|
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import os
|
|
2
|
+
|
|
3
|
+
import requests
|
|
4
|
+
|
|
5
|
+
from oak_cli.utils.types import CustomEnum
|
|
6
|
+
|
|
7
|
+
GITHUB_PREFIX = "https://github.com/"
|
|
8
|
+
SYSTEM_MANAGER_URL = f"http://{os.environ.get('SYSTEM_MANAGER_URL')}:10000"
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class HttpMethod(CustomEnum):
|
|
12
|
+
GET = "get"
|
|
13
|
+
POST = "post"
|
|
14
|
+
PUT = "put"
|
|
15
|
+
PATCH = "patch"
|
|
16
|
+
DELETE = "delete"
|
|
17
|
+
|
|
18
|
+
def call(cls, **kwargs) -> requests.Response:
|
|
19
|
+
method_map = {
|
|
20
|
+
cls.GET: requests.get,
|
|
21
|
+
cls.POST: requests.post,
|
|
22
|
+
cls.PUT: requests.put,
|
|
23
|
+
cls.PATCH: requests.patch,
|
|
24
|
+
cls.DELETE: requests.delete,
|
|
25
|
+
}
|
|
26
|
+
method = method_map.get(cls)
|
|
27
|
+
|
|
28
|
+
if method:
|
|
29
|
+
return method(**kwargs)
|
|
30
|
+
else:
|
|
31
|
+
raise ValueError(f"Unsupported HTTP method: {cls.value}")
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
from http import HTTPStatus
|
|
2
|
+
|
|
3
|
+
import oak_cli.utils.api as oak_api
|
|
4
|
+
|
|
5
|
+
_login_token = ""
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class LoginFailed(Exception):
|
|
9
|
+
pass
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def _login_and_set_token() -> str:
|
|
13
|
+
data = {"username": "Admin", "password": "Admin"}
|
|
14
|
+
headers = {"accept": "application/json", "Content-Type": "application/json"}
|
|
15
|
+
|
|
16
|
+
json_data = oak_api.main.handle_request(
|
|
17
|
+
base_url=oak_api.common.SYSTEM_MANAGER_URL,
|
|
18
|
+
http_method=oak_api.common.HttpMethod.POST,
|
|
19
|
+
api_endpoint="/api/auth/login",
|
|
20
|
+
headers=headers,
|
|
21
|
+
data=data,
|
|
22
|
+
what_should_happen="Login",
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
global _login_token
|
|
26
|
+
_login_token = json_data["token"]
|
|
27
|
+
return _login_token
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def get_login_token() -> str:
|
|
31
|
+
if _login_token == "":
|
|
32
|
+
return _login_and_set_token()
|
|
33
|
+
return _login_token
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import json
|
|
2
|
+
import sys
|
|
3
|
+
from http import HTTPStatus
|
|
4
|
+
from typing import NamedTuple, Optional, Tuple, Union
|
|
5
|
+
|
|
6
|
+
import requests
|
|
7
|
+
|
|
8
|
+
from oak_cli.utils.api.common import HttpMethod
|
|
9
|
+
from oak_cli.utils.api.login import get_login_token
|
|
10
|
+
from oak_cli.utils.logging import logger
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class ApiQueryComponents(NamedTuple):
|
|
14
|
+
url: str
|
|
15
|
+
headers: dict
|
|
16
|
+
data: dict = None
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def _prepare_api_query_components(
|
|
20
|
+
base_url: str,
|
|
21
|
+
api_endpoint: str = None,
|
|
22
|
+
custom_headers: dict = None,
|
|
23
|
+
data: dict = None,
|
|
24
|
+
query_params: str = None,
|
|
25
|
+
) -> ApiQueryComponents:
|
|
26
|
+
url = base_url
|
|
27
|
+
if api_endpoint is not None:
|
|
28
|
+
url = f"{base_url}{api_endpoint}"
|
|
29
|
+
if query_params is not None:
|
|
30
|
+
url += f"?{query_params}"
|
|
31
|
+
headers = custom_headers or {"Authorization": f"Bearer {get_login_token()}"}
|
|
32
|
+
if data and not custom_headers:
|
|
33
|
+
headers["Content-Type"] = "application/json"
|
|
34
|
+
return ApiQueryComponents(url, headers, data)
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def _create_failure_msg(
|
|
38
|
+
what_should_happen: str,
|
|
39
|
+
http_method: HttpMethod,
|
|
40
|
+
url: str,
|
|
41
|
+
response_status: HTTPStatus = None,
|
|
42
|
+
) -> str:
|
|
43
|
+
return (
|
|
44
|
+
" ".join(
|
|
45
|
+
(
|
|
46
|
+
what_should_happen,
|
|
47
|
+
"request failed with",
|
|
48
|
+
str(response_status),
|
|
49
|
+
f"for '{http_method}' '{url}",
|
|
50
|
+
)
|
|
51
|
+
),
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def handle_request(
|
|
56
|
+
base_url: str,
|
|
57
|
+
what_should_happen: str,
|
|
58
|
+
http_method: HttpMethod = HttpMethod.GET,
|
|
59
|
+
api_endpoint: str = None,
|
|
60
|
+
headers: dict = None,
|
|
61
|
+
data: dict = None,
|
|
62
|
+
show_msg_on_success: bool = False,
|
|
63
|
+
special_msg_on_fail: str = None,
|
|
64
|
+
query_params: str = None,
|
|
65
|
+
terminate_when_failed: bool = True,
|
|
66
|
+
) -> Union[HTTPStatus, Tuple[HTTPStatus, Optional[dict]]]:
|
|
67
|
+
|
|
68
|
+
url, headers, data = _prepare_api_query_components(
|
|
69
|
+
base_url, api_endpoint, headers, data, query_params
|
|
70
|
+
)
|
|
71
|
+
args = {
|
|
72
|
+
"url": url,
|
|
73
|
+
"verify": False,
|
|
74
|
+
**({"headers": headers} if headers else {}),
|
|
75
|
+
**({"json": data} if data else {}),
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
try:
|
|
79
|
+
response = http_method.call(**args)
|
|
80
|
+
response_status = HTTPStatus(response.status_code)
|
|
81
|
+
|
|
82
|
+
if response_status == HTTPStatus.OK:
|
|
83
|
+
if show_msg_on_success:
|
|
84
|
+
logger.info(f"Success: {what_should_happen}")
|
|
85
|
+
response = response.json()
|
|
86
|
+
if isinstance(response, str):
|
|
87
|
+
response = json.loads(response)
|
|
88
|
+
if terminate_when_failed:
|
|
89
|
+
return response
|
|
90
|
+
return response_status, response
|
|
91
|
+
else:
|
|
92
|
+
logger.error(f"FAILED: {special_msg_on_fail or what_should_happen}!")
|
|
93
|
+
logger.error(
|
|
94
|
+
_create_failure_msg(
|
|
95
|
+
what_should_happen, http_method, url, response_status
|
|
96
|
+
)
|
|
97
|
+
)
|
|
98
|
+
logger.error(f"response: '{response}'")
|
|
99
|
+
if terminate_when_failed:
|
|
100
|
+
sys.exit(1)
|
|
101
|
+
return response_status, None
|
|
102
|
+
|
|
103
|
+
except requests.exceptions.RequestException as e:
|
|
104
|
+
logger.error(_create_failure_msg(what_should_happen, http_method, url))
|
|
105
|
+
logger.error(e)
|
|
106
|
+
if terminate_when_failed:
|
|
107
|
+
sys.exit(1)
|
|
108
|
+
return HTTPStatus.INTERNAL_SERVER_ERROR, None
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import shlex
|
|
2
|
+
import subprocess
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
def handle_argcomplete() -> None:
|
|
6
|
+
# Note: Ideally this would only occur once in a dedicated postinstall script
|
|
7
|
+
# (similar to debian packages).
|
|
8
|
+
# However, python packages do not seem to allow postinstall scripts.
|
|
9
|
+
# The argcomplete command also is not too customizable to check if it is already installed,
|
|
10
|
+
# because it might behave differently depending on the OS.
|
|
11
|
+
with open("/dev/null", "w") as devnull:
|
|
12
|
+
subprocess.run(
|
|
13
|
+
shlex.split("activate-global-python-argcomplete -y --user"),
|
|
14
|
+
stdout=devnull,
|
|
15
|
+
stderr=devnull,
|
|
16
|
+
)
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# Reference:
|
|
2
|
+
# https://alexandra-zaharia.github.io/posts/make-your-own-custom-color-formatter-with-python-logging/
|
|
3
|
+
|
|
4
|
+
import logging
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class CustomFormatter(logging.Formatter):
|
|
8
|
+
"""Logging colored formatter, adapted from https://stackoverflow.com/a/56944256/3638629"""
|
|
9
|
+
|
|
10
|
+
grey = "\x1b[38;21m"
|
|
11
|
+
yellow = "\x1b[38;5;226m"
|
|
12
|
+
red = "\x1b[38;5;196m"
|
|
13
|
+
bold_red = "\x1b[31;1m"
|
|
14
|
+
blue = "\x1b[38;5;33m"
|
|
15
|
+
light_blue = "\x1b[38;5;45m"
|
|
16
|
+
reset = "\x1b[0m"
|
|
17
|
+
|
|
18
|
+
def __init__(self, fmt):
|
|
19
|
+
super().__init__()
|
|
20
|
+
self.fmt = fmt
|
|
21
|
+
self.FORMATS = {
|
|
22
|
+
logging.DEBUG: self.grey + self.fmt + self.reset,
|
|
23
|
+
logging.INFO: self.light_blue + self.fmt + self.reset,
|
|
24
|
+
logging.WARNING: self.yellow + self.fmt + self.reset,
|
|
25
|
+
logging.ERROR: self.red + self.fmt + self.reset,
|
|
26
|
+
logging.CRITICAL: self.bold_red + self.fmt + self.reset,
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
def format(self, record):
|
|
30
|
+
log_fmt = self.FORMATS.get(record.levelno)
|
|
31
|
+
formatter = logging.Formatter(log_fmt)
|
|
32
|
+
return formatter.format(record)
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
_LOGGER_NAME = "logger"
|
|
36
|
+
|
|
37
|
+
_FORMAT = "%(message)s"
|
|
38
|
+
|
|
39
|
+
logger = logging.getLogger(_LOGGER_NAME)
|
|
40
|
+
logger.setLevel(logging.DEBUG)
|
|
41
|
+
|
|
42
|
+
_formatter = CustomFormatter(_FORMAT)
|
|
43
|
+
|
|
44
|
+
_stream_handler = logging.StreamHandler()
|
|
45
|
+
_stream_handler.setFormatter(_formatter)
|
|
46
|
+
|
|
47
|
+
logger.addHandler(_stream_handler)
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import argparse
|
|
2
|
+
import enum
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class CustomEnum(enum.Enum):
|
|
6
|
+
def __str__(self) -> str:
|
|
7
|
+
return self.value
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
Id = str
|
|
11
|
+
ServiceId = Id
|
|
12
|
+
ApplicationId = Id
|
|
13
|
+
|
|
14
|
+
Service = dict
|
|
15
|
+
Application = dict
|
|
16
|
+
|
|
17
|
+
SLA = dict
|
|
18
|
+
DbObject = dict
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
# Custom type for nicer type hints
|
|
22
|
+
Subparsers = argparse._SubParsersAction
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
[tool.poetry]
|
|
2
|
+
name = "oak_cli"
|
|
3
|
+
version = "0.2.5"
|
|
4
|
+
description = ""
|
|
5
|
+
authors = ["Alexander Malyuk <malyuk.alexander1999@gmail.com>"]
|
|
6
|
+
readme = "README.md"
|
|
7
|
+
|
|
8
|
+
[tool.poetry.dependencies]
|
|
9
|
+
python = "^3.10"
|
|
10
|
+
argcomplete = "^3.2.3"
|
|
11
|
+
|
|
12
|
+
[build-system]
|
|
13
|
+
requires = ["poetry-core"]
|
|
14
|
+
build-backend = "poetry.core.masonry.api"
|
|
15
|
+
|
|
16
|
+
[tool.poetry.scripts]
|
|
17
|
+
oak = "oak_cli.main:main"
|