-
-
Notifications
You must be signed in to change notification settings - Fork 28
/
Copy pathremote.py
202 lines (166 loc) · 5.74 KB
/
remote.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
__author__ = "Vanessa Sochat"
__copyright__ = "Copyright 2021-2022, Vanessa Sochat"
__license__ = "MPL 2.0"
import os
import re
import subprocess as sp
import sys
import requests
import shpc.utils
from shpc.logger import logger
from .provider import Provider, Result
class RemoteResult(Result):
"""
A remote result provides courtesy functions for interacting with
a container yaml recipe on GitHub.
"""
def __init__(self, module, spec, load=True, config=None):
self.module = module
self._spec = spec
self._config = config
if load:
self.load(spec)
def load(self, spec):
"""
Load the settings file into the settings object
"""
if "config" not in spec:
logger.exit("Cannot find required 'config' key in remote metadata.")
self.package_file = spec["config_url"]
self._config = spec["config"]
def get_overrides(self, tag):
"""
Load a filesystem based override file
"""
logger.exit(
"We don't have support for remote overrides yet! Please open an issue!"
)
def save(self, package_file):
raise ValueError("Remote save to a GitHub registry is not supported.")
def load_wrapper_script(self, container_tech, script):
"""
Get the content of a wrapper script, if it exists.
"""
wrapper_script = self.find_wrapper_script(container_tech, script)
if wrapper_script:
url = os.path.join(self.dirname, wrapper_script)
response = requests.get(url)
if response.status_code != 200:
logger.warning(
"Could not find wrapper script %s in remote registry." % script
)
return response.text
def override_exists(self, tag):
"""
Check if an override exists.
"""
logger.exit(
"We don't have support for remote overrides yet! Please open an issue!"
)
class VersionControl(Provider):
def __init__(self, *args, **kwargs):
self.tag = kwargs.get("tag")
# Cache of remote container metadata
self._cache = {}
# E.g., subdirectory with registry files
self.subdir = kwargs.get("subdir")
super().__init__(*args, **kwargs)
self._url = self.source
@classmethod
def matches(cls, source):
return cls.provider_name in source and source.startswith("http")
@property
def source_url(self):
"""
Retrieve a parsed / formatted url, ensuring https and without git.
"""
url = self.source
if not url.startswith("http"):
url = "https://%s" % url
if url.endswith(".git"):
url = url[:-4]
return url
@property
def web_url(self):
"""
Retrieve the web url, either pages or (eventually) custom.
"""
parts = self.source_url.split("/")[3:]
return "https://%s.%s.io/%s/library.json" % (
parts[0],
self.provider_name,
"/".join(parts[1:]),
)
def exists(self, name):
"""
Determine if a module exists in the registry.
"""
dirname = self.source
if self.subdir:
dirname = os.path.join(dirname, self.subdir)
return os.path.exists(os.path.join(dirname, name))
def clone(self, tmpdir=None):
"""
Clone the known source URL to a temporary directory
"""
tmpdir = tmpdir or shpc.utils.get_tmpdir()
cmd = ["git", "clone", "--depth", "1"]
if self.tag:
cmd += ["-b", self.tag]
cmd += [self._url, tmpdir]
self.source = tmpdir
try:
sp.run(cmd, check=True)
except sp.CalledProcessError as e:
raise ValueError("Failed to clone repository {}:\n{}", self.source, e)
return tmpdir
def iter_modules(self):
"""
yield module names
"""
dirname = self.source
if self.subdir:
dirname = os.path.join(dirname, self.subdir)
# Find modules based on container.yaml
for filename in shpc.utils.recursive_find(dirname, "container.yaml"):
module = os.path.dirname(filename).replace(dirname, "").strip(os.sep)
if not module:
continue
yield dirname, module
def find(self, name):
"""
Find a particular entry in a registry
"""
self._update_cache()
if name in self._cache:
return RemoteResult(name, self._cache[name])
def _update_cache(self, force=False):
"""
Update local cache from a registry.
"""
if self._cache and not force:
return
# Check for exposed library API on GitHub or GitLab pages
response = requests.get(self.web_url)
if response.status_code != 200:
sys.exit(
"Remote %s is not deploying a Registry API (%s). Open a GitHub issue to ask for help."
% (self.source, self.web_url)
)
self._cache = response.json()
def iter_registry(self, filter_string=None):
"""
Yield metadata about containers in a remote registry.
"""
self._update_cache()
# This assumes blob/main branch
for uri, entry in self._cache.items():
# If the user has provided a filter, honor it
if filter_string and not re.search(filter_string, uri):
continue
# Assemble a faux config with tags so we don't hit remote
yield RemoteResult(uri, entry, load=False, config=entry["config"])
class GitHub(VersionControl):
provider_name = "github"
class GitLab(VersionControl):
provider_name = "gitlab"