cvdlink 0.1.1__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.
- cvdlink-0.1.1/FeatureCloud/__init__.py +0 -0
- cvdlink-0.1.1/FeatureCloud/api/__init__.py +0 -0
- cvdlink-0.1.1/FeatureCloud/api/cli/__init__.py +0 -0
- cvdlink-0.1.1/FeatureCloud/api/cli/__main__.py +115 -0
- cvdlink-0.1.1/FeatureCloud/api/cli/app/__init__.py +0 -0
- cvdlink-0.1.1/FeatureCloud/api/cli/app/commands.py +182 -0
- cvdlink-0.1.1/FeatureCloud/api/cli/controller/__init__.py +0 -0
- cvdlink-0.1.1/FeatureCloud/api/cli/controller/commands.py +181 -0
- cvdlink-0.1.1/FeatureCloud/api/cli/test/__init__.py +0 -0
- cvdlink-0.1.1/FeatureCloud/api/cli/test/commands.py +251 -0
- cvdlink-0.1.1/FeatureCloud/api/cli/test/workflow/__init__.py +0 -0
- cvdlink-0.1.1/FeatureCloud/api/cli/test/workflow/commands.py +32 -0
- cvdlink-0.1.1/FeatureCloud/api/imp/__init__.py +0 -0
- cvdlink-0.1.1/FeatureCloud/api/imp/app/__init__.py +0 -0
- cvdlink-0.1.1/FeatureCloud/api/imp/app/commands.py +278 -0
- cvdlink-0.1.1/FeatureCloud/api/imp/controller/__init__.py +0 -0
- cvdlink-0.1.1/FeatureCloud/api/imp/controller/commands.py +246 -0
- cvdlink-0.1.1/FeatureCloud/api/imp/exceptions.py +29 -0
- cvdlink-0.1.1/FeatureCloud/api/imp/test/__init__.py +0 -0
- cvdlink-0.1.1/FeatureCloud/api/imp/test/api/__init__.py +0 -0
- cvdlink-0.1.1/FeatureCloud/api/imp/test/api/backend/__init__.py +0 -0
- cvdlink-0.1.1/FeatureCloud/api/imp/test/api/backend/auth.py +54 -0
- cvdlink-0.1.1/FeatureCloud/api/imp/test/api/backend/project.py +84 -0
- cvdlink-0.1.1/FeatureCloud/api/imp/test/api/controller.py +97 -0
- cvdlink-0.1.1/FeatureCloud/api/imp/test/commands.py +124 -0
- cvdlink-0.1.1/FeatureCloud/api/imp/test/helper.py +40 -0
- cvdlink-0.1.1/FeatureCloud/api/imp/util.py +45 -0
- cvdlink-0.1.1/FeatureCloud/app/__init__.py +0 -0
- cvdlink-0.1.1/FeatureCloud/app/api/__init__.py +0 -0
- cvdlink-0.1.1/FeatureCloud/app/api/http_ctrl.py +48 -0
- cvdlink-0.1.1/FeatureCloud/app/api/http_web.py +16 -0
- cvdlink-0.1.1/FeatureCloud/app/engine/__init__.py +0 -0
- cvdlink-0.1.1/FeatureCloud/app/engine/app.py +1214 -0
- cvdlink-0.1.1/FeatureCloud/app/engine/library.py +46 -0
- cvdlink-0.1.1/FeatureCloud/workflow/__init__.py +0 -0
- cvdlink-0.1.1/FeatureCloud/workflow/app.py +197 -0
- cvdlink-0.1.1/FeatureCloud/workflow/controller.py +17 -0
- cvdlink-0.1.1/FeatureCloud/workflow/example_wf.py +83 -0
- cvdlink-0.1.1/FeatureCloud/workflow/workflow.py +86 -0
- cvdlink-0.1.1/LICENSE +201 -0
- cvdlink-0.1.1/PKG-INFO +176 -0
- cvdlink-0.1.1/README.md +135 -0
- cvdlink-0.1.1/cvdlink.egg-info/PKG-INFO +176 -0
- cvdlink-0.1.1/cvdlink.egg-info/SOURCES.txt +51 -0
- cvdlink-0.1.1/cvdlink.egg-info/dependency_links.txt +1 -0
- cvdlink-0.1.1/cvdlink.egg-info/entry_points.txt +5 -0
- cvdlink-0.1.1/cvdlink.egg-info/requires.txt +16 -0
- cvdlink-0.1.1/cvdlink.egg-info/top_level.txt +1 -0
- cvdlink-0.1.1/setup.cfg +12 -0
- cvdlink-0.1.1/setup.py +33 -0
- cvdlink-0.1.1/tests/test_pip_package.py +55 -0
- cvdlink-0.1.1/tests/test_testbed.py +88 -0
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import click
|
|
2
|
+
import requests
|
|
3
|
+
from importlib.metadata import version, PackageNotFoundError
|
|
4
|
+
|
|
5
|
+
from FeatureCloud.api.cli.test.commands import test
|
|
6
|
+
from FeatureCloud.api.cli.controller.commands import controller
|
|
7
|
+
from FeatureCloud.api.cli.app.commands import app
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def _safe_version(*dist_names: str) -> str:
|
|
11
|
+
"""
|
|
12
|
+
Return the installed distribution version for the first matching name.
|
|
13
|
+
Avoids crashing if a distribution name is not installed in the environment.
|
|
14
|
+
"""
|
|
15
|
+
for name in dist_names:
|
|
16
|
+
try:
|
|
17
|
+
return version(name)
|
|
18
|
+
except PackageNotFoundError:
|
|
19
|
+
continue
|
|
20
|
+
return "0.0.0+unknown"
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def get_latest_version(package_name: str):
|
|
24
|
+
url = f"https://pypi.org/pypi/{package_name}/json"
|
|
25
|
+
try:
|
|
26
|
+
response = requests.get(url, timeout=3)
|
|
27
|
+
response.raise_for_status()
|
|
28
|
+
data = response.json()
|
|
29
|
+
return data["info"]["version"]
|
|
30
|
+
except requests.exceptions.RequestException:
|
|
31
|
+
return None
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def get_warning_string():
|
|
35
|
+
"""
|
|
36
|
+
Warn if local FeatureCloud version appears older than PyPI 'featurecloud'.
|
|
37
|
+
Never crash if the package metadata doesn't exist (e.g., editable installs, renamed dists).
|
|
38
|
+
"""
|
|
39
|
+
online_version = get_latest_version("featurecloud")
|
|
40
|
+
if not online_version:
|
|
41
|
+
return None
|
|
42
|
+
|
|
43
|
+
online_version_list = online_version.split(".")
|
|
44
|
+
|
|
45
|
+
# Use only the distribution names that actually exist in your env.
|
|
46
|
+
# In your output, FeatureCloud is installed (editable) and featurecloud is available as normalized.
|
|
47
|
+
local_version = _safe_version("FeatureCloud", "featurecloud")
|
|
48
|
+
|
|
49
|
+
if local_version == "0.0.0+unknown":
|
|
50
|
+
return None
|
|
51
|
+
|
|
52
|
+
local_version_list = local_version.split(".")
|
|
53
|
+
|
|
54
|
+
for idx in range(min(len(online_version_list), len(local_version_list))):
|
|
55
|
+
try:
|
|
56
|
+
if int(online_version_list[idx]) > int(local_version_list[idx]):
|
|
57
|
+
click.echo(
|
|
58
|
+
click.style(
|
|
59
|
+
f"WARNING: your version is out of date. "
|
|
60
|
+
f"Your version is {local_version} but "
|
|
61
|
+
f"version {online_version} is available",
|
|
62
|
+
fg="yellow",
|
|
63
|
+
bold=True,
|
|
64
|
+
)
|
|
65
|
+
)
|
|
66
|
+
break
|
|
67
|
+
except Exception:
|
|
68
|
+
return None
|
|
69
|
+
|
|
70
|
+
return None
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def _init_ctx_profile(ctx: click.Context, default_profile: str) -> None:
|
|
76
|
+
"""Initialize click context object with a default profile."""
|
|
77
|
+
ctx.ensure_object(dict)
|
|
78
|
+
# Only set if not already set, so nested calls won't overwrite
|
|
79
|
+
ctx.obj.setdefault("profile", default_profile)
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
# ---------- FeatureCloud CLI root ----------
|
|
83
|
+
|
|
84
|
+
@click.version_option(version=_safe_version("featurecloud", "FeatureCloud", "cvdlink"))
|
|
85
|
+
@click.group("first-level", epilog=get_warning_string())
|
|
86
|
+
@click.pass_context
|
|
87
|
+
def fc_cli(ctx: click.Context) -> None:
|
|
88
|
+
"""FeatureCloud CLI."""
|
|
89
|
+
_init_ctx_profile(ctx, default_profile="featurecloud")
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
fc_cli.add_command(test)
|
|
93
|
+
fc_cli.add_command(controller)
|
|
94
|
+
fc_cli.add_command(app)
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
# ---------- CVDLink CLI root ----------
|
|
98
|
+
|
|
99
|
+
@click.version_option(version=_safe_version("cvdlink", "featurecloud", "FeatureCloud"))
|
|
100
|
+
@click.group("first-level", epilog=get_warning_string())
|
|
101
|
+
@click.pass_context
|
|
102
|
+
def cvdlink_cli(ctx: click.Context) -> None:
|
|
103
|
+
"""CVDLink CLI (based on FeatureCloud)."""
|
|
104
|
+
_init_ctx_profile(ctx, default_profile="cvdlink")
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
cvdlink_cli.add_command(test)
|
|
108
|
+
cvdlink_cli.add_command(controller)
|
|
109
|
+
cvdlink_cli.add_command(app)
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
if __name__ == "__main__":
|
|
113
|
+
# Fallback if someone runs `python -m FeatureCloud.api.cli.__main__`
|
|
114
|
+
# Default to FeatureCloud behavior.
|
|
115
|
+
fc_cli()
|
|
File without changes
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import click
|
|
3
|
+
import tqdm
|
|
4
|
+
|
|
5
|
+
from FeatureCloud.api.imp.app import commands
|
|
6
|
+
from FeatureCloud.api.imp.exceptions import FCException
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
@click.group("app")
|
|
10
|
+
def app() -> None:
|
|
11
|
+
"""app related commands"""
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
# @TODO Create a list available templates function
|
|
15
|
+
|
|
16
|
+
@app.command('new')
|
|
17
|
+
@click.argument('name', type=click.Path(), nargs=1)
|
|
18
|
+
@click.argument('directory', type=click.Path(), nargs=1, required=False)
|
|
19
|
+
@click.option('--template-name',
|
|
20
|
+
help='You can specify a template from the available ones: app-blank, app-dice, app-round. '
|
|
21
|
+
'If not provided, an empty project will be created using the app-blank template.')
|
|
22
|
+
def new(name: click.Path, directory: click.Path, template_name: str):
|
|
23
|
+
"""
|
|
24
|
+
Create new app
|
|
25
|
+
|
|
26
|
+
NAME is the app name. Because of Docker naming restrictions, it should be in lowercase.
|
|
27
|
+
|
|
28
|
+
DIRECTORY is the directory where your app will be created (in a subdirectory NAME)
|
|
29
|
+
|
|
30
|
+
Example: featurecloud app new my-new-app . --template-name=app-blank
|
|
31
|
+
|
|
32
|
+
"""
|
|
33
|
+
try:
|
|
34
|
+
arguments = locals().items()
|
|
35
|
+
for key, value in arguments:
|
|
36
|
+
if key == 'name':
|
|
37
|
+
value_lowercase = value.lower()
|
|
38
|
+
if value != value_lowercase:
|
|
39
|
+
click.echo("Uppercase letters are not allowed in app name. The new app name is: " + value_lowercase)
|
|
40
|
+
|
|
41
|
+
path = commands.new(**{k: v for k, v in arguments if v})
|
|
42
|
+
click.echo(f'Path to your app: {os.path.abspath(path)}')
|
|
43
|
+
click.echo('Enjoy!')
|
|
44
|
+
except FCException as e:
|
|
45
|
+
click.echo(f'Error: {e}')
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
@app.command('build')
|
|
49
|
+
@click.argument('path', type=click.Path(), default=".", required=False)
|
|
50
|
+
@click.argument('image_name', type=str, default='', required=False)
|
|
51
|
+
@click.argument('tag', type=str, default="latest", nargs=1, required=False)
|
|
52
|
+
@click.argument('rm', type=bool, default=True, nargs=1, required=False)
|
|
53
|
+
def build(path: click.Path, image_name: str, tag: str, rm: bool):
|
|
54
|
+
"""
|
|
55
|
+
|
|
56
|
+
Build app
|
|
57
|
+
|
|
58
|
+
PATH is the location of your app
|
|
59
|
+
|
|
60
|
+
IMAGE_NAME is the Docker image name. Because of Docker naming restrictions, it should be in lowercase.
|
|
61
|
+
|
|
62
|
+
TAG the Docker tag for the version to be built. Default: 'latest'.
|
|
63
|
+
|
|
64
|
+
RM remove intermediary containers.
|
|
65
|
+
|
|
66
|
+
Example: featurecloud app build ./my-new-app my-new-app first_version True
|
|
67
|
+
"""
|
|
68
|
+
try:
|
|
69
|
+
arguments = locals().items()
|
|
70
|
+
for key, value in arguments:
|
|
71
|
+
if key == 'image_name':
|
|
72
|
+
value_lowercase = value.lower()
|
|
73
|
+
if value != value_lowercase:
|
|
74
|
+
click.echo(
|
|
75
|
+
"Uppercase letters are not allowed in image name. The new image name is: " + value_lowercase)
|
|
76
|
+
click.echo(f'Building {image_name}:{tag} ...')
|
|
77
|
+
build_log_generator = commands.build(**{k: v for k, v in arguments if v})
|
|
78
|
+
# Iterate over the response
|
|
79
|
+
for output in build_log_generator:
|
|
80
|
+
if 'stream' in output:
|
|
81
|
+
stream_output = output['stream'].strip()
|
|
82
|
+
if 'Step' in stream_output:
|
|
83
|
+
click.echo(stream_output)
|
|
84
|
+
click.echo(f'Image {image_name}:{tag} built successfully')
|
|
85
|
+
except FCException as e:
|
|
86
|
+
click.echo(f'Error: {e}')
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
@app.command('download')
|
|
90
|
+
@click.argument('name', type=str, default=None, nargs=1, required=True)
|
|
91
|
+
@click.argument('tag', type=str, default="latest", nargs=1, required=False)
|
|
92
|
+
def download(name: str, tag: str):
|
|
93
|
+
"""
|
|
94
|
+
Download an image from FeatureCloud repository
|
|
95
|
+
|
|
96
|
+
NAME is the image name
|
|
97
|
+
|
|
98
|
+
TAG is the image versioning tag. Default: 'latest'.
|
|
99
|
+
|
|
100
|
+
Example: featurecloud app download my_app:latest
|
|
101
|
+
"""
|
|
102
|
+
try:
|
|
103
|
+
result = commands.download(**{k: v for k, v in locals().items() if v})
|
|
104
|
+
for _ in tqdm.tqdm(result, desc=f"Downloading {name}:{tag} ..."):
|
|
105
|
+
pass
|
|
106
|
+
except FCException as e:
|
|
107
|
+
click.echo(f'Error: {e}')
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
@app.command('publish')
|
|
111
|
+
@click.argument('name', type=str, default=None, nargs=1, required=True)
|
|
112
|
+
@click.argument('tag', type=str, default="latest", nargs=1, required=False)
|
|
113
|
+
def publish(name: str, tag: str):
|
|
114
|
+
"""
|
|
115
|
+
Publish an app in FeatureCloud repository
|
|
116
|
+
|
|
117
|
+
NAME is the image name
|
|
118
|
+
|
|
119
|
+
TAG is the image versioning tag. Default: 'latest'.
|
|
120
|
+
|
|
121
|
+
Example: featurecloud app publish my-app:latest
|
|
122
|
+
|
|
123
|
+
The app should be created and the image name set in the AI Store prior publishing
|
|
124
|
+
|
|
125
|
+
"""
|
|
126
|
+
try:
|
|
127
|
+
result = commands.publish(**{k: v for k, v in locals().items() if v})
|
|
128
|
+
for _ in tqdm.tqdm(result, desc=f"Uploading {name}:{tag} ..."):
|
|
129
|
+
pass
|
|
130
|
+
except FCException as e:
|
|
131
|
+
if str(e).find("authentication required") > -1:
|
|
132
|
+
click.echo(
|
|
133
|
+
f'Image cannot be pushed. A docker login is necessary to featurecloud.ai with user credentials or the app is inexistent in Featurecloud AI Store. In this case please create an app in AI Store with the specified image name.')
|
|
134
|
+
click.echo(f'Error: {e}')
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
@app.command('remove')
|
|
138
|
+
@click.argument('name', type=str, default=None, nargs=1, required=True)
|
|
139
|
+
@click.argument('tag', type=str, default="latest", nargs=1, required=False)
|
|
140
|
+
def remove(name: str, tag: str):
|
|
141
|
+
"""
|
|
142
|
+
Delete app image(s) from the local repository. This command will not delete the app from FeatureCloud AI Store.
|
|
143
|
+
|
|
144
|
+
NAME is the image name to be deleted
|
|
145
|
+
|
|
146
|
+
TAG is the image versioning tag. If set to 'all', all versions will be deleted. Default: 'latest'.
|
|
147
|
+
|
|
148
|
+
Example: featurecloud app remove my-app all
|
|
149
|
+
"""
|
|
150
|
+
try:
|
|
151
|
+
result = commands.remove(**{k: v for k, v in locals().items() if v})
|
|
152
|
+
if len(result) == 0:
|
|
153
|
+
click.echo(f'No image found')
|
|
154
|
+
else:
|
|
155
|
+
click.echo(f'Removed image(s): {",".join(result)}')
|
|
156
|
+
except FCException as e:
|
|
157
|
+
click.echo(f'Error: {e}')
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
@app.command('plot-states')
|
|
161
|
+
@click.argument('path', type=click.Path(), default=".", required=False)
|
|
162
|
+
@click.option('--package', default=".", help='package/s which include/s the states')
|
|
163
|
+
@click.option('--states', default='main', help='python files containing the states seperated by comma')
|
|
164
|
+
@click.option('--plot_name', default="state_diagram", help='name of the plotted diagram file')
|
|
165
|
+
def plot_diagram(path: str, package: str, states: str, plot_name: str):
|
|
166
|
+
"""
|
|
167
|
+
Plot and store the state diagram of the app in the app directory
|
|
168
|
+
|
|
169
|
+
Path is the path to directory containing the app
|
|
170
|
+
|
|
171
|
+
Package is the relative path of the subpackage containing the states
|
|
172
|
+
|
|
173
|
+
States is a comma seperated list of .py files including the states
|
|
174
|
+
|
|
175
|
+
Plot_name is the name of the plotted diagram file
|
|
176
|
+
|
|
177
|
+
Example: featurecloud app plot-states /home/my-app mystates --states states.py --plot_name myplot
|
|
178
|
+
"""
|
|
179
|
+
try:
|
|
180
|
+
commands.plot_state_diagram(**{k: v for k, v in locals().items() if v})
|
|
181
|
+
except FCException as e:
|
|
182
|
+
click.echo(f'Error: {e}')
|
|
File without changes
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
import os
|
|
2
|
+
|
|
3
|
+
import click
|
|
4
|
+
from FeatureCloud.api.imp.exceptions import FCException
|
|
5
|
+
|
|
6
|
+
from FeatureCloud.api.imp.controller import commands
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
@click.group("controller")
|
|
10
|
+
def controller() -> None:
|
|
11
|
+
"""Controller start/stop. Obtain general information about the controller."""
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
@controller.command('start')
|
|
15
|
+
@click.argument('name', type=str, default=commands.DEFAULT_CONTROLLER_NAME, nargs=1, required=False)
|
|
16
|
+
@click.option('--port', default=8000, help='Controller port number. Optional parameter (e.g. --port=8000).', required=False)
|
|
17
|
+
@click.option('--data-dir', default='data', help='Controller data directory. Optional parameter (e.g. --data-dir=./data).', required=False)
|
|
18
|
+
@click.option('--controller-image', default="", help='Controller image name (e.g., featurecloud.ai/controller:latest)', required=False)
|
|
19
|
+
@click.option('--gpu', help='Start controller with GPU access. If this succeeds, controller can allow GPU access for apps.', default=False, required=False)
|
|
20
|
+
@click.option('--mount', help='Use this option when you want mount a folder that is available only to the '
|
|
21
|
+
'controller\'s protected environment, e.g. to upload input data for apps.', default='', required=False)
|
|
22
|
+
@click.option('--blockchain-address', help='Address of application that connects to the blockchain system', default='', required=False)
|
|
23
|
+
|
|
24
|
+
# NEW – specific overrides
|
|
25
|
+
@click.option(
|
|
26
|
+
"--global-endpoint",
|
|
27
|
+
default="",
|
|
28
|
+
help="Override manager.globalEndpoint (default taken from config.yml).",
|
|
29
|
+
)
|
|
30
|
+
@click.option(
|
|
31
|
+
"--registry",
|
|
32
|
+
default="",
|
|
33
|
+
help="Override manager.registry (default taken from config.yml).",
|
|
34
|
+
)
|
|
35
|
+
@click.option(
|
|
36
|
+
"--relay-address",
|
|
37
|
+
default="",
|
|
38
|
+
help="Override relay.address (default taken from config.yml).",
|
|
39
|
+
)
|
|
40
|
+
@click.option(
|
|
41
|
+
"--poll-interval",
|
|
42
|
+
type=int,
|
|
43
|
+
default=0,
|
|
44
|
+
help="Override workflow.pollInterval (default taken from config.yml).",
|
|
45
|
+
)
|
|
46
|
+
@click.option(
|
|
47
|
+
"--query-interval",
|
|
48
|
+
type=int,
|
|
49
|
+
default=0,
|
|
50
|
+
help="Override workflow.queryInterval (default taken from config.yml).",
|
|
51
|
+
)
|
|
52
|
+
@click.option(
|
|
53
|
+
"--config-file",
|
|
54
|
+
default="",
|
|
55
|
+
help="Path to config file inside container (defaults to config.yml).",
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
@click.pass_context
|
|
59
|
+
def start(ctx: click.Context,
|
|
60
|
+
name: str,
|
|
61
|
+
port: int,
|
|
62
|
+
data_dir: str,
|
|
63
|
+
controller_image: str,
|
|
64
|
+
gpu: bool,
|
|
65
|
+
mount: str,
|
|
66
|
+
blockchain_address: str,
|
|
67
|
+
global_endpoint: str,
|
|
68
|
+
registry: str,
|
|
69
|
+
relay_address: str,
|
|
70
|
+
poll_interval: int,
|
|
71
|
+
query_interval: int,
|
|
72
|
+
config_file: str,
|
|
73
|
+
) -> None:
|
|
74
|
+
"""Start a controller instance.
|
|
75
|
+
|
|
76
|
+
NAME is the controller instance name
|
|
77
|
+
|
|
78
|
+
Example: featurecloud controller start my-fc-controller --gpu=True
|
|
79
|
+
"""
|
|
80
|
+
profile = "featurecloud"
|
|
81
|
+
if ctx is not None and isinstance(ctx.obj, dict):
|
|
82
|
+
profile = ctx.obj.get("profile", "featurecloud")
|
|
83
|
+
try:
|
|
84
|
+
commands.start(
|
|
85
|
+
name=name,
|
|
86
|
+
port=port,
|
|
87
|
+
data_dir=data_dir,
|
|
88
|
+
controller_image=controller_image,
|
|
89
|
+
with_gpu=gpu,
|
|
90
|
+
mount=mount,
|
|
91
|
+
blockchain_address=blockchain_address,
|
|
92
|
+
profile=profile,
|
|
93
|
+
global_endpoint=global_endpoint,
|
|
94
|
+
registry=registry,
|
|
95
|
+
relay_address=relay_address,
|
|
96
|
+
poll_interval=poll_interval,
|
|
97
|
+
query_interval=query_interval,
|
|
98
|
+
config_file=config_file,
|
|
99
|
+
)
|
|
100
|
+
click.echo(
|
|
101
|
+
click.style(
|
|
102
|
+
f"Started controller '{name}' using profile '{profile}'",
|
|
103
|
+
fg="green",
|
|
104
|
+
bold=True,
|
|
105
|
+
)
|
|
106
|
+
)
|
|
107
|
+
except FCException as e:
|
|
108
|
+
if str(e).find("port is already allocated") > -1:
|
|
109
|
+
click.echo(f'Controller could not be started. Port {port} is already allocated.')
|
|
110
|
+
else:
|
|
111
|
+
click.echo(f'Controller could not be started. Error: {e}')
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
@controller.command('stop')
|
|
115
|
+
@click.argument('name', type=str, default=commands.DEFAULT_CONTROLLER_NAME, nargs=1, required=False)
|
|
116
|
+
def stop(name: str) -> None:
|
|
117
|
+
"""Stop controller instance.
|
|
118
|
+
|
|
119
|
+
NAME is the controller instance name
|
|
120
|
+
|
|
121
|
+
Example: featurecloud controller stop my-fc-controller
|
|
122
|
+
"""
|
|
123
|
+
|
|
124
|
+
try:
|
|
125
|
+
result = commands.stop(name)
|
|
126
|
+
if len(result) == 0:
|
|
127
|
+
click.echo(f'No controller found with name {name}')
|
|
128
|
+
else:
|
|
129
|
+
click.echo(f'Stopped controller(s): {",".join(result)}')
|
|
130
|
+
except FCException as e:
|
|
131
|
+
click.echo(f'Controller could not be stopped. Error: {e}')
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
@controller.command('logs')
|
|
135
|
+
@click.argument('name', type=str, default=commands.DEFAULT_CONTROLLER_NAME, nargs=1, required=False)
|
|
136
|
+
@click.option('--tail', help='View the tail of controller logs. (e.g. featurecloud controller logs --tail=True)', default=False, required=False)
|
|
137
|
+
@click.option('--log-level', default='debug', help='Log level filter. Will filter more sever errors than specified (e.g. featurecloud controller logs --log-level=debug).', required=False)
|
|
138
|
+
def logs(name: str, log_level: str, tail: bool) -> None:
|
|
139
|
+
"""Display the logs for the controller instance.
|
|
140
|
+
|
|
141
|
+
NAME is the controller instance name.
|
|
142
|
+
|
|
143
|
+
Example: featurecloud controller logs my-fc-controller
|
|
144
|
+
"""
|
|
145
|
+
|
|
146
|
+
try:
|
|
147
|
+
for line in commands.logs(name, tail, log_level):
|
|
148
|
+
click.echo(line)
|
|
149
|
+
except FCException as e:
|
|
150
|
+
click.echo(f'Error: {e}')
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
@controller.command('status')
|
|
154
|
+
@click.argument('name', type=str, default=commands.DEFAULT_CONTROLLER_NAME, nargs=1, required=False)
|
|
155
|
+
def status(name: str) -> None:
|
|
156
|
+
"""Display general status of the controller.
|
|
157
|
+
|
|
158
|
+
NAME is the controller instance name.
|
|
159
|
+
|
|
160
|
+
Example: featurecloud controller status my-fc-controller
|
|
161
|
+
"""
|
|
162
|
+
|
|
163
|
+
try:
|
|
164
|
+
container = commands.status(name)
|
|
165
|
+
click.echo(f'Id: {container.short_id}, Status: {container.status}')
|
|
166
|
+
except FCException as e:
|
|
167
|
+
click.echo(f'Error: {e}')
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
@controller.command('ls')
|
|
171
|
+
def ls() -> None:
|
|
172
|
+
"""Lists all running controller instances"""
|
|
173
|
+
try:
|
|
174
|
+
result = commands.ls()
|
|
175
|
+
if len(result) == 0:
|
|
176
|
+
click.echo('No controllers found')
|
|
177
|
+
else:
|
|
178
|
+
for container in result:
|
|
179
|
+
click.echo(f'Id: {container.short_id}, Status: {container.status}')
|
|
180
|
+
except FCException as e:
|
|
181
|
+
click.echo(f'Error: {e}')
|
|
File without changes
|