skyramp 0.4.30__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.
- skyramp-0.4.30/PKG-INFO +23 -0
- skyramp-0.4.30/README.md +14 -0
- skyramp-0.4.30/pyproject.toml +13 -0
- skyramp-0.4.30/src/skyramp/__init__.py +7 -0
- skyramp-0.4.30/src/skyramp/client.py +183 -0
- skyramp-0.4.30/src/skyramp/endpoint.py +166 -0
- skyramp-0.4.30/src/skyramp/utils.py +53 -0
skyramp-0.4.30/PKG-INFO
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: skyramp
|
|
3
|
+
Version: 0.4.30
|
|
4
|
+
Summary: module for leveraging skyramp cli functionality
|
|
5
|
+
Requires-Python: >=3.0
|
|
6
|
+
Description-Content-Type: text/markdown
|
|
7
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
8
|
+
|
|
9
|
+
# Skyramp
|
|
10
|
+
Skyramp is a pip module that provides utility functions for leveraging [skyramp CLI](https://skyramp.dev/docs/commands/skyramp/) commands.
|
|
11
|
+
|
|
12
|
+
## Installation
|
|
13
|
+
To install Skyramp, simply run the following command in your terminal:
|
|
14
|
+
```
|
|
15
|
+
pip install skyramp
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## Usage
|
|
19
|
+
Once you've installed Skyramp, you can import it into your project like this:
|
|
20
|
+
```
|
|
21
|
+
import skyramp
|
|
22
|
+
```
|
|
23
|
+
|
skyramp-0.4.30/README.md
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# Skyramp
|
|
2
|
+
Skyramp is a pip module that provides utility functions for leveraging [skyramp CLI](https://skyramp.dev/docs/commands/skyramp/) commands.
|
|
3
|
+
|
|
4
|
+
## Installation
|
|
5
|
+
To install Skyramp, simply run the following command in your terminal:
|
|
6
|
+
```
|
|
7
|
+
pip install skyramp
|
|
8
|
+
```
|
|
9
|
+
|
|
10
|
+
## Usage
|
|
11
|
+
Once you've installed Skyramp, you can import it into your project like this:
|
|
12
|
+
```
|
|
13
|
+
import skyramp
|
|
14
|
+
```
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["flit_core >=3.2,<4"]
|
|
3
|
+
build-backend = "flit_core.buildapi"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "skyramp"
|
|
7
|
+
version = "0.4.30"
|
|
8
|
+
description = "module for leveraging skyramp cli functionality"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.0"
|
|
11
|
+
classifiers = [
|
|
12
|
+
"License :: OSI Approved :: MIT License"
|
|
13
|
+
]
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Defines a Skyramp client, which can be used to interact with a cluster.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import ctypes
|
|
6
|
+
import yaml
|
|
7
|
+
|
|
8
|
+
from skyramp.utils import _library, _call_function
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class _Client:
|
|
12
|
+
"""
|
|
13
|
+
Skyramp client object which can be used to interact with a cluster.
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
def __init__(
|
|
17
|
+
self,
|
|
18
|
+
kubeconfig_path: str = "",
|
|
19
|
+
cluster_name: str = "",
|
|
20
|
+
context: str = "",
|
|
21
|
+
) -> None:
|
|
22
|
+
"""
|
|
23
|
+
Initializes a Skyramp Client.
|
|
24
|
+
|
|
25
|
+
kubeconfig_path: The filesystem path of a kubeconfig
|
|
26
|
+
cluster_name: The name of the cluster.
|
|
27
|
+
context: The Kubernetes context within a kubeconfig
|
|
28
|
+
"""
|
|
29
|
+
self.kubeconfig_path = kubeconfig_path
|
|
30
|
+
self.cluster_name = cluster_name
|
|
31
|
+
self.context = context
|
|
32
|
+
|
|
33
|
+
self._namespace_set = set()
|
|
34
|
+
|
|
35
|
+
def apply_local(self) -> None:
|
|
36
|
+
"""
|
|
37
|
+
Creates a local cluster.
|
|
38
|
+
"""
|
|
39
|
+
apply_local_function = _library.applyLocalWrapper
|
|
40
|
+
argtypes = []
|
|
41
|
+
restype = ctypes.c_char_p
|
|
42
|
+
|
|
43
|
+
_call_function(apply_local_function, argtypes, restype, [])
|
|
44
|
+
|
|
45
|
+
self.kubeconfig_path = self._get_kubeconfig_path()
|
|
46
|
+
if not self.kubeconfig_path:
|
|
47
|
+
raise Exception("no kubeconfig found")
|
|
48
|
+
|
|
49
|
+
def remove_local(self) -> None:
|
|
50
|
+
"""
|
|
51
|
+
Removes a local cluster.
|
|
52
|
+
"""
|
|
53
|
+
func = _library.removeLocalWrapper
|
|
54
|
+
argtypes = []
|
|
55
|
+
restype = ctypes.c_char_p
|
|
56
|
+
|
|
57
|
+
_call_function(func, argtypes, restype, [])
|
|
58
|
+
|
|
59
|
+
def add_kubeconfig(
|
|
60
|
+
self,
|
|
61
|
+
context: str,
|
|
62
|
+
cluster_name: str,
|
|
63
|
+
kubeconfig_path: str,
|
|
64
|
+
) -> None:
|
|
65
|
+
"""
|
|
66
|
+
Adds a preexisting Kubeconfig file to Skyramp.
|
|
67
|
+
|
|
68
|
+
context: The kubeconfig context to use
|
|
69
|
+
cluster_name: Name of the cluster
|
|
70
|
+
kubeconfig_path: filepath of the kubeconfig
|
|
71
|
+
"""
|
|
72
|
+
func = _library.addKubeconfigWrapper
|
|
73
|
+
argtypes = [ctypes.c_char_p, ctypes.c_bool, ctypes.c_char_p, ctypes.c_char_p]
|
|
74
|
+
restype = ctypes.c_char_p
|
|
75
|
+
|
|
76
|
+
_call_function(
|
|
77
|
+
func,
|
|
78
|
+
argtypes,
|
|
79
|
+
restype,
|
|
80
|
+
[
|
|
81
|
+
context.encode(),
|
|
82
|
+
cluster_name.encode(),
|
|
83
|
+
kubeconfig_path.encode(),
|
|
84
|
+
],
|
|
85
|
+
)
|
|
86
|
+
|
|
87
|
+
self.kubeconfig_path = kubeconfig_path
|
|
88
|
+
|
|
89
|
+
def remove_cluster(self, cluster_name: str) -> None:
|
|
90
|
+
"""
|
|
91
|
+
Removes a cluster, corresponding to the name, from Skyramp
|
|
92
|
+
"""
|
|
93
|
+
func = _library.removeClusterFromConfigWrapper
|
|
94
|
+
argtypes = [ctypes.c_char_p]
|
|
95
|
+
restype = ctypes.c_char_p
|
|
96
|
+
|
|
97
|
+
_call_function(func, argtypes, restype, [cluster_name.encode()])
|
|
98
|
+
|
|
99
|
+
def deploy_skyramp_worker(
|
|
100
|
+
self, namespace: str, worker_image: str, local_image: bool
|
|
101
|
+
) -> None:
|
|
102
|
+
"""
|
|
103
|
+
Installs a Skyramp worker onto a cluster if one is registered with Skyramp
|
|
104
|
+
"""
|
|
105
|
+
if not self.kubeconfig_path:
|
|
106
|
+
raise Exception("no cluster to deploy worker to")
|
|
107
|
+
|
|
108
|
+
func = _library.deploySkyrampWorkerWrapper
|
|
109
|
+
argtypes = [ctypes.c_char_p, ctypes.c_char_p, ctypes.c_bool]
|
|
110
|
+
restype = ctypes.c_char_p
|
|
111
|
+
|
|
112
|
+
_call_function(
|
|
113
|
+
func,
|
|
114
|
+
argtypes,
|
|
115
|
+
restype,
|
|
116
|
+
[namespace.encode(), worker_image.encode(), local_image],
|
|
117
|
+
)
|
|
118
|
+
|
|
119
|
+
self._namespace_set.add(namespace)
|
|
120
|
+
|
|
121
|
+
def delete_skyramp_worker(self, namespace: str) -> None:
|
|
122
|
+
"""
|
|
123
|
+
Removes the Skyramp worker, if a Skyramp worker is installed on a registered Skyramp cluster
|
|
124
|
+
"""
|
|
125
|
+
if not self.kubeconfig_path:
|
|
126
|
+
raise Exception("no cluster to delete worker from")
|
|
127
|
+
|
|
128
|
+
if namespace not in self._namespace_set:
|
|
129
|
+
raise Exception(f"no worker to delete from {namespace} namespace")
|
|
130
|
+
|
|
131
|
+
func = _library.deleteSkyrampWorkerWrapper
|
|
132
|
+
argtypes = [ctypes.c_char_p]
|
|
133
|
+
restype = ctypes.c_char_p
|
|
134
|
+
|
|
135
|
+
_call_function(func, argtypes, restype, [namespace.encode()])
|
|
136
|
+
|
|
137
|
+
self._namespace_set.remove(namespace)
|
|
138
|
+
|
|
139
|
+
def mocker_apply(self, namespace: str, endpoint: str) -> None:
|
|
140
|
+
"""
|
|
141
|
+
Applies a configuration to mocker.
|
|
142
|
+
namespace: The namespace where mocker resides
|
|
143
|
+
endpoint: The Skyramp enpdoint object
|
|
144
|
+
"""
|
|
145
|
+
yaml_string = yaml.dump(endpoint.mock_description)
|
|
146
|
+
|
|
147
|
+
func = _library.applyMockDescriptionWrapper
|
|
148
|
+
argtypes = [
|
|
149
|
+
ctypes.c_char_p,
|
|
150
|
+
ctypes.c_char_p,
|
|
151
|
+
]
|
|
152
|
+
restype = ctypes.c_char_p # pylint: disable=duplicate-code
|
|
153
|
+
|
|
154
|
+
_call_function(
|
|
155
|
+
func,
|
|
156
|
+
argtypes,
|
|
157
|
+
restype,
|
|
158
|
+
[
|
|
159
|
+
namespace.encode(),
|
|
160
|
+
yaml_string.encode(),
|
|
161
|
+
],
|
|
162
|
+
)
|
|
163
|
+
|
|
164
|
+
# This will be implemented when scenarios are added.
|
|
165
|
+
# def tester_start(self, namespace: str, scenario: str) -> str:
|
|
166
|
+
# """
|
|
167
|
+
# Runs tester at the given namespace and test name
|
|
168
|
+
# namespace: The namespace where mocker resides
|
|
169
|
+
# """
|
|
170
|
+
# func = _library.runTesterStartWrapper
|
|
171
|
+
# argtypes = [ctypes.c_char_p, ctypes.c_char_p]
|
|
172
|
+
# restype = ctypes.c_char_p
|
|
173
|
+
|
|
174
|
+
# _call_function(
|
|
175
|
+
# func, argtypes, restype, [namespace.encode(), test_description.encode()]
|
|
176
|
+
# )
|
|
177
|
+
|
|
178
|
+
def _get_kubeconfig_path(self) -> str:
|
|
179
|
+
func = _library.getKubeConfigPath
|
|
180
|
+
argtypes = []
|
|
181
|
+
restype = ctypes.c_char_p
|
|
182
|
+
|
|
183
|
+
return _call_function(func, argtypes, restype, [], True)
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Contains helpers for interacting with Skyramp endpoints.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from abc import ABC, abstractmethod
|
|
6
|
+
|
|
7
|
+
import os
|
|
8
|
+
import ctypes
|
|
9
|
+
import json
|
|
10
|
+
import yaml
|
|
11
|
+
|
|
12
|
+
from skyramp.utils import _library, _call_function
|
|
13
|
+
|
|
14
|
+
class _Endpoint(ABC):
|
|
15
|
+
"""
|
|
16
|
+
Base class for endpoints. This should not be used for instantiation.
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
@abstractmethod
|
|
20
|
+
def __init__(self, endpoint_data: str) -> None:
|
|
21
|
+
try:
|
|
22
|
+
endpoint = json.loads(endpoint_data)
|
|
23
|
+
self.services = endpoint["services"]
|
|
24
|
+
self.endpoint = endpoint["endpoints"][0]
|
|
25
|
+
|
|
26
|
+
for method in self.endpoint["methods"]:
|
|
27
|
+
method["proxy"] = True
|
|
28
|
+
|
|
29
|
+
self.response_values = endpoint["responseValues"]
|
|
30
|
+
|
|
31
|
+
self.mock_description = {
|
|
32
|
+
"services": self.services,
|
|
33
|
+
"endpoints": [self.endpoint],
|
|
34
|
+
"responseValues": self.response_values
|
|
35
|
+
}
|
|
36
|
+
except:
|
|
37
|
+
raise Exception("failed to parse endpoint data")
|
|
38
|
+
|
|
39
|
+
def mock_method(self, method_name: str, mock_object: str, dynamic: bool = False) -> None:
|
|
40
|
+
"""
|
|
41
|
+
Adds the given mock_data blob to the method name for this endpoint.
|
|
42
|
+
"""
|
|
43
|
+
for method in self.endpoint["methods"]:
|
|
44
|
+
if method_name not in [method.get("name"), method.get("type")]:
|
|
45
|
+
continue
|
|
46
|
+
|
|
47
|
+
response_name = method["responseValue"]
|
|
48
|
+
method.pop("proxy", None)
|
|
49
|
+
|
|
50
|
+
for response_value in self.response_values:
|
|
51
|
+
if response_value["name"] != response_name:
|
|
52
|
+
continue
|
|
53
|
+
|
|
54
|
+
if dynamic:
|
|
55
|
+
response_value["javascriptPath"] = mock_object
|
|
56
|
+
response_value.pop("blob", None)
|
|
57
|
+
else:
|
|
58
|
+
response_value["blob"] = mock_object["responseValue"]["blob"]
|
|
59
|
+
response_value.pop("javascriptPath", None)
|
|
60
|
+
return
|
|
61
|
+
|
|
62
|
+
raise Exception(f"method {method_name} not found")
|
|
63
|
+
|
|
64
|
+
def mock_method_from_file(self, method_name: str, file_name: str) -> None:
|
|
65
|
+
"""
|
|
66
|
+
Uses the given mock data from a provided file, and associates it with the
|
|
67
|
+
corresponding method_name for this endpoint.
|
|
68
|
+
"""
|
|
69
|
+
_, file_ext = os.path.splitext(file_name)
|
|
70
|
+
|
|
71
|
+
try:
|
|
72
|
+
with open(file_name) as file:
|
|
73
|
+
file_contents = file.read()
|
|
74
|
+
except:
|
|
75
|
+
raise Exception(f"failed to open file: {file_name}")
|
|
76
|
+
|
|
77
|
+
dynamic = False
|
|
78
|
+
|
|
79
|
+
if file_ext == ".json":
|
|
80
|
+
data = json.loads(file_contents)
|
|
81
|
+
elif file_ext in [".yaml", ".yml"]:
|
|
82
|
+
data = yaml.safe_load(file_contents)
|
|
83
|
+
elif file_ext == ".js":
|
|
84
|
+
dynamic = True
|
|
85
|
+
data = file_name
|
|
86
|
+
else:
|
|
87
|
+
raise Exception(
|
|
88
|
+
f"unsupported file format: {file_ext}. Only JSON and YAML are supported"
|
|
89
|
+
)
|
|
90
|
+
|
|
91
|
+
return self.mock_method(method_name=method_name, mock_object=data, dynamic=dynamic)
|
|
92
|
+
|
|
93
|
+
def write_mock_configuration_to_file(self, alias: str) -> None:
|
|
94
|
+
"""
|
|
95
|
+
Persists (as a file to be used by Skyramp) all of the mock configurations
|
|
96
|
+
for this endpoint.
|
|
97
|
+
|
|
98
|
+
alias: The name of the networking alias that will be used to reach this endpoint.
|
|
99
|
+
For example, it can be the Kubernetes service name or the Docker alias name.
|
|
100
|
+
"""
|
|
101
|
+
try:
|
|
102
|
+
yaml_content = yaml.dump(self.mock_description)
|
|
103
|
+
except:
|
|
104
|
+
raise Exception("failed to convert mock description to YAML")
|
|
105
|
+
|
|
106
|
+
func = _library.writeMockDescriptionWrapper
|
|
107
|
+
argtypes = [ctypes.c_char_p, ctypes.c_char_p]
|
|
108
|
+
restype = ctypes.c_char_p
|
|
109
|
+
|
|
110
|
+
_call_function(func, argtypes, restype, [yaml_content.encode(), alias.encode()])
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
class _GrpcEndpoint(_Endpoint):
|
|
114
|
+
"""
|
|
115
|
+
Represents an endpoint of a gRPC based service.
|
|
116
|
+
"""
|
|
117
|
+
|
|
118
|
+
def __init__(self, name: str, service: str, port: int, pb_file: str) -> None:
|
|
119
|
+
"""
|
|
120
|
+
name: Name of the endpoint
|
|
121
|
+
service: The service name to associate with this endpoint
|
|
122
|
+
port: Port number where the endpoint will be reached
|
|
123
|
+
pb_file: Protobuf file with definitions corresponding to this endpoint
|
|
124
|
+
"""
|
|
125
|
+
func = _library.newGrpcEndpointWrapper
|
|
126
|
+
argtypes = [ctypes.c_char_p, ctypes.c_char_p, ctypes.c_int, ctypes.c_char_p]
|
|
127
|
+
restype = ctypes.c_char_p
|
|
128
|
+
|
|
129
|
+
output = _call_function(
|
|
130
|
+
func,
|
|
131
|
+
argtypes,
|
|
132
|
+
restype,
|
|
133
|
+
[name.encode(), service.encode(), port, pb_file.encode()],
|
|
134
|
+
True,
|
|
135
|
+
)
|
|
136
|
+
|
|
137
|
+
super().__init__(output)
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
class _RestEndpoint(_Endpoint):
|
|
141
|
+
"""
|
|
142
|
+
Represents an endpoint of a REST based service.
|
|
143
|
+
"""
|
|
144
|
+
|
|
145
|
+
def __init__(
|
|
146
|
+
self, name: str, openapi_tag: str, port: int, openapi_file: str
|
|
147
|
+
) -> None:
|
|
148
|
+
"""
|
|
149
|
+
name: name of the endpoint
|
|
150
|
+
openapi_tag: (optional) tag to filter an OpenAPI file
|
|
151
|
+
port: Port number where the endpoint will be reached
|
|
152
|
+
openapi_file: (optional) OpenAPI file with definitions corresponding to this endpoint
|
|
153
|
+
"""
|
|
154
|
+
func = _library.newRestEndpointWrapper
|
|
155
|
+
argtypes = [ctypes.c_char_p, ctypes.c_char_p, ctypes.c_int, ctypes.c_char_p]
|
|
156
|
+
restype = ctypes.c_char_p
|
|
157
|
+
|
|
158
|
+
output = _call_function(
|
|
159
|
+
func,
|
|
160
|
+
argtypes,
|
|
161
|
+
restype,
|
|
162
|
+
[name.encode(), openapi_tag.encode(), port, openapi_file.encode()],
|
|
163
|
+
True,
|
|
164
|
+
)
|
|
165
|
+
|
|
166
|
+
super().__init__(output)
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Contains internal utilities
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import platform
|
|
6
|
+
import os
|
|
7
|
+
import ctypes
|
|
8
|
+
|
|
9
|
+
def _get_c_library():
|
|
10
|
+
system = platform.system()
|
|
11
|
+
processor = platform.processor()
|
|
12
|
+
|
|
13
|
+
lib_dir = os.path.join(os.path.dirname(__file__), "lib")
|
|
14
|
+
lib_file = ""
|
|
15
|
+
|
|
16
|
+
if system == "Darwin":
|
|
17
|
+
if processor == "arm":
|
|
18
|
+
lib_file = "dev-darwin-arm64.dylib"
|
|
19
|
+
|
|
20
|
+
if lib_file == "":
|
|
21
|
+
raise Exception(
|
|
22
|
+
f"unsupported system and architecture. System: {system}, Architecture: {processor}"
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
lib_path = os.path.join(lib_dir, lib_file)
|
|
26
|
+
|
|
27
|
+
return ctypes.cdll.LoadLibrary(lib_path)
|
|
28
|
+
|
|
29
|
+
def _call_function(func, argtypes, restype, args, return_output=False):
|
|
30
|
+
func.argtypes = argtypes
|
|
31
|
+
func.restype = restype
|
|
32
|
+
|
|
33
|
+
output = func(*args)
|
|
34
|
+
if not output:
|
|
35
|
+
return None
|
|
36
|
+
|
|
37
|
+
output_bytes = ctypes.string_at(output)
|
|
38
|
+
output = output_bytes.decode()
|
|
39
|
+
|
|
40
|
+
if return_output:
|
|
41
|
+
return output
|
|
42
|
+
|
|
43
|
+
# If output is not expected, the result output is parsed as an exception
|
|
44
|
+
if len(output) > 0:
|
|
45
|
+
raise Exception(output)
|
|
46
|
+
|
|
47
|
+
return None
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
_library = _get_c_library()
|
|
51
|
+
|
|
52
|
+
if _library is None:
|
|
53
|
+
raise Exception("failed to load Skyramp C library")
|