@skyramp/skyramp 0.4.31
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.
- package/LICENSE +21 -0
- package/README.md +93 -0
- package/package.json +23 -0
- package/src/classes/Endpoint.d.ts +6 -0
- package/src/classes/Endpoint.js +68 -0
- package/src/classes/GrpcEndpoint.d.ts +4 -0
- package/src/classes/GrpcEndpoint.js +12 -0
- package/src/classes/RestEndpoint.d.ts +4 -0
- package/src/classes/RestEndpoint.js +12 -0
- package/src/classes/Scenario.d.ts +8 -0
- package/src/classes/Scenario.js +80 -0
- package/src/classes/SkyrampClient.d.ts +17 -0
- package/src/classes/SkyrampClient.js +200 -0
- package/src/index.d.ts +6 -0
- package/src/index.js +11 -0
- package/src/lib.js +30 -0
- package/src/utils.js +58 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2023 Skyramp
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
# skyramp
|
|
2
|
+
|
|
3
|
+
`skyramp` is an npm module that provides utility functions for leveraging [skyramp CLI](https://skyramp.dev/docs/commands/skyramp/) commands. The `skyramp` module provides functionalities to create and apply mock configurations for both gRPC and REST APIs. It also offers features for testing and asserting scenarios in various test environments. The module exports the following classes: `RestEndpoint`, `GrpcEndpoint`, `Scenario`, and `SkyrampClient`.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
To install Skyramp, simply run the following command in your terminal:
|
|
7
|
+
```bash
|
|
8
|
+
npm install skyramp
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Usage
|
|
12
|
+
Once you've installed Skyramp, you can import it into your project like this:
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
const { GrpcEndpoint, RestEndpoint, SkyrampClient, Scenario } = require('skyramp');
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
### SkyrampClient
|
|
19
|
+
The `SkyrampClient` class is the main entry point to interact with the skyramp module. It allows you to apply configurations, start test scenarios, and deploy and delete workers. First, you must set up the `SkyrampClient` with a kubernetes cluster.
|
|
20
|
+
|
|
21
|
+
**Example: Provision Local Cluster with Skyramp**
|
|
22
|
+
```bash
|
|
23
|
+
const skyrampClient = new SkyrampClient();
|
|
24
|
+
skyrampClient.applyLocal().then(function() {
|
|
25
|
+
// Local cluster provisioned
|
|
26
|
+
}).catch(function(error) {
|
|
27
|
+
// Error occurred during provisioning
|
|
28
|
+
});
|
|
29
|
+
```
|
|
30
|
+
Once you have a `SkyrampClient` instance configured with a kubernetes cluster, you can deploy the Skyramp Worker in-cluster, for applying mocks and running tests.
|
|
31
|
+
**Example: Deploy Skyramp Worker**
|
|
32
|
+
```bash
|
|
33
|
+
skyrampClient.deployWorker('my-namespace').then(function() {
|
|
34
|
+
// Worker deployed successfully
|
|
35
|
+
}).catch(function(error) {
|
|
36
|
+
// Error occurred during deployment
|
|
37
|
+
});
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
### RestEndpoint
|
|
41
|
+
The `RestEndpoint` class represents a REST API endpoint and provides methods to configure mock responses to apply them to the `SkyrampClient`.
|
|
42
|
+
|
|
43
|
+
**Example: Create REST Mock Configuration**
|
|
44
|
+
```bash
|
|
45
|
+
restEndpoint = new RestEndpoint('artists', 'artists-GET', 50050, 'api/openapi/artists.yaml');
|
|
46
|
+
restEndpoint.mockMethodFromFile('artists-GET', 'files/rest-values.yaml');
|
|
47
|
+
skyrampClient.mockerApply('my-namespace', restEndpoint).then(function() {
|
|
48
|
+
// Mock applied successfully
|
|
49
|
+
}).catch(function(error) {
|
|
50
|
+
// Error occurred during mock application
|
|
51
|
+
});
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
### GrpcEndpoint
|
|
55
|
+
The `GrpcEndpoint` class represents a gRPC API endpoint and provides methods to configure mock responses to apply them to the `SkyrampClient`.
|
|
56
|
+
|
|
57
|
+
**Example: Create gRPC Mock Configuration**
|
|
58
|
+
```bash
|
|
59
|
+
grpcEndpoint = new GrpcEndpoint('routeguide', 'RouteGuide', 50051, 'api/pb/routeguide.proto');
|
|
60
|
+
grpcEndpoint.mockMethodFromFile('GetFeature', 'files/grpc-response.yaml');
|
|
61
|
+
skyrampClient.mockerApply('my-namespace', grpcEndpoint).then(function() {
|
|
62
|
+
// Mock applied successfully
|
|
63
|
+
}).catch(function(error) {
|
|
64
|
+
// Error occurred during mock application
|
|
65
|
+
});
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
### Scenario
|
|
69
|
+
The `Scenario` class allows you to define test scenarios by specifying a series of API requests and assertions to be made. Once a `Scenario` is created, you can start it using the `SkyrampClient` instance.
|
|
70
|
+
|
|
71
|
+
**Example: Test Assert Scenario (REST)**
|
|
72
|
+
```bash
|
|
73
|
+
scenario = new Scenario('rest-test')
|
|
74
|
+
step1 = scenario.addRequest(restEndpoint, 'artists-GET');
|
|
75
|
+
step2 = scenario.addAssertEqual(`${step1}.res.items[0].artist_name`, 'abc');
|
|
76
|
+
skyrampClient.testerStart('test-worker', scenario).then(function() {
|
|
77
|
+
// Test scenario started
|
|
78
|
+
}).catch(function(error) {
|
|
79
|
+
// Error occurred during the test scenario
|
|
80
|
+
});
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
**Example: Test Assert Scenario (gRPC)**
|
|
84
|
+
```bash
|
|
85
|
+
scenario = new Scenario('routeguide-assert-test')
|
|
86
|
+
step1 = scenario.addRequestFromFile(grpcEndpoint, 'GetFeature', 'files/grpc-request.yaml');
|
|
87
|
+
step2 = scenario.addAssertEqual(`${step1}.res.name`, 'test-feature');
|
|
88
|
+
skyrampClient.testerStart('test-worker', scenario).then(function() {
|
|
89
|
+
// Test scenario started
|
|
90
|
+
}).catch(function(error) {
|
|
91
|
+
// Error occurred during the test scenario
|
|
92
|
+
});
|
|
93
|
+
```
|
package/package.json
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@skyramp/skyramp",
|
|
3
|
+
"version": "0.4.31",
|
|
4
|
+
"description": "module for leveraging skyramp cli functionality",
|
|
5
|
+
"scripts": {
|
|
6
|
+
"pack": "npm pack"
|
|
7
|
+
},
|
|
8
|
+
"files": [
|
|
9
|
+
"lib/**",
|
|
10
|
+
"src/*.js",
|
|
11
|
+
"src/*.ts",
|
|
12
|
+
"src/classes/*.js",
|
|
13
|
+
"src/classes/*.ts"
|
|
14
|
+
],
|
|
15
|
+
"main": "src/index.js",
|
|
16
|
+
"author": "",
|
|
17
|
+
"license": "MIT",
|
|
18
|
+
"dependencies": {
|
|
19
|
+
"fs": "^0.0.1-security",
|
|
20
|
+
"js-yaml": "^4.1.0",
|
|
21
|
+
"koffi": "^2.5.3"
|
|
22
|
+
}
|
|
23
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export declare class Endpoint {
|
|
2
|
+
constructor(endpointData: string);
|
|
3
|
+
mockMethod(methodName: string, mockObject: string, dynamic?: boolean): void;
|
|
4
|
+
mockMethodFromFile(methodName: string, fileName: string): void;
|
|
5
|
+
writeMockConfigurationToFile(kubernetesService: string): Promise<void>;
|
|
6
|
+
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
const lib = require('../lib');
|
|
2
|
+
const { getYamlBytes, readDataFromFile } = require('../utils');
|
|
3
|
+
const writeMockDescriptionWrapper = lib.func('writeMockDescriptionWrapper', 'string', ['string', 'string']);
|
|
4
|
+
|
|
5
|
+
class Endpoint {
|
|
6
|
+
constructor(endpointData) {
|
|
7
|
+
try {
|
|
8
|
+
const endpoint = JSON.parse(endpointData);
|
|
9
|
+
this.services = endpoint.services;
|
|
10
|
+
this.endpoint = endpoint.endpoints[0];
|
|
11
|
+
for (const method of this.endpoint.methods) {
|
|
12
|
+
method.proxy = true;
|
|
13
|
+
}
|
|
14
|
+
this.responseValues = endpoint.responseValues;
|
|
15
|
+
this.mockDescription = {
|
|
16
|
+
services: this.services,
|
|
17
|
+
endpoints: [this.endpoint],
|
|
18
|
+
responseValues: this.responseValues
|
|
19
|
+
};
|
|
20
|
+
} catch(error) {
|
|
21
|
+
throw new Error(endpointData);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
mockMethod(methodName, mockObject, dynamic=false) {
|
|
26
|
+
for (const method of this.endpoint.methods) {
|
|
27
|
+
if (method.name === methodName || method.type === methodName) {
|
|
28
|
+
const responseName = method.responseValue;
|
|
29
|
+
delete method.proxy;
|
|
30
|
+
for (const responseValue of this.responseValues) {
|
|
31
|
+
if (responseValue.name === responseName) {
|
|
32
|
+
if (dynamic) {
|
|
33
|
+
responseValue.javascriptPath = mockObject;
|
|
34
|
+
delete responseValue.blob;
|
|
35
|
+
} else {
|
|
36
|
+
responseValue.blob = mockObject.responseValue.blob;
|
|
37
|
+
delete responseValue.javascriptPath;
|
|
38
|
+
}
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
throw new Error(`Method ${methodName} not found`);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
mockMethodFromFile(methodName, fileName) {
|
|
48
|
+
const [jsonData, dynamic] = readDataFromFile(fileName);
|
|
49
|
+
return this.mockMethod(methodName, jsonData, dynamic);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
writeMockConfigurationToFile(kubernetesService) {
|
|
53
|
+
const yamlContent = getYamlBytes(this.mockDescription);
|
|
54
|
+
return new Promise((resolve, reject) => {
|
|
55
|
+
writeMockDescriptionWrapper.async(yamlContent, kubernetesService, (err, res) => {
|
|
56
|
+
if (err) {
|
|
57
|
+
reject(err);
|
|
58
|
+
} else if (res) {
|
|
59
|
+
reject(new Error(res));
|
|
60
|
+
} else {
|
|
61
|
+
resolve();
|
|
62
|
+
}
|
|
63
|
+
});
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
module.exports = Endpoint;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
const Endpoint = require('./Endpoint');
|
|
2
|
+
const lib = require('../lib');
|
|
3
|
+
const newGrpcEndpointWrapper = lib.func('newGrpcEndpointWrapper', 'string', ['string', 'string', 'int', 'string']);
|
|
4
|
+
|
|
5
|
+
class GrpcEndpoint extends Endpoint {
|
|
6
|
+
constructor(name, service, port, pbFile) {
|
|
7
|
+
const response = newGrpcEndpointWrapper(name, service, port, pbFile);
|
|
8
|
+
super(response);
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
module.exports = GrpcEndpoint;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
const Endpoint = require('./Endpoint');
|
|
2
|
+
const lib = require('../lib');
|
|
3
|
+
const newRestEndpointWrapper = lib.func('newRestEndpointWrapper', 'string', ['string', 'string', 'int', 'string']);
|
|
4
|
+
|
|
5
|
+
class RestEndpoint extends Endpoint {
|
|
6
|
+
constructor(name, openApiTag, port, openapiFile) {
|
|
7
|
+
const response = newRestEndpointWrapper(name, openApiTag, port, openapiFile);
|
|
8
|
+
super(response);
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
module.exports = RestEndpoint;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export declare class Scenario {
|
|
2
|
+
constructor(name: string);
|
|
3
|
+
addRequest(endpoint: any, methodName: string, requestObject?: string, dynamic?: boolean): string;
|
|
4
|
+
addRequestFromFile(endpoint: any, methodName: string, requestFile: string): string;
|
|
5
|
+
buildRequestsFromEndpoint(endpoint: any): void;
|
|
6
|
+
addAssertEqual(value1: string, value2: string): string;
|
|
7
|
+
writeTestConfigurationToFile(): Promise<void>;
|
|
8
|
+
}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
const lib = require('../lib');
|
|
2
|
+
const { createTestDescriptionFromScenario, getYamlBytes, readDataFromFile } = require('../utils');
|
|
3
|
+
const buildRequestsWrapper = lib.func('buildRequestsWrapper', 'string', ['string']);
|
|
4
|
+
const writeTestDescriptionWrapper = lib.func('writeTestDescriptionWrapper', 'string', ['string', 'string']);
|
|
5
|
+
|
|
6
|
+
class Scenario {
|
|
7
|
+
constructor(name) {
|
|
8
|
+
this.name = name;
|
|
9
|
+
this.steps = [];
|
|
10
|
+
this.services = [];
|
|
11
|
+
this.endpoints = [];
|
|
12
|
+
this.requests = [];
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
addRequest(endpoint, methodName, requestObject=undefined, dynamic=false) {
|
|
16
|
+
this.buildRequestsFromEndpoint(endpoint);
|
|
17
|
+
for (const request of this.requests) {
|
|
18
|
+
if (request.method === methodName) {
|
|
19
|
+
if (dynamic) {
|
|
20
|
+
request.requestValue.javascriptPath = requestObject;
|
|
21
|
+
delete request.requestValue.blob;
|
|
22
|
+
} else if (requestObject !== undefined) {
|
|
23
|
+
request.requestValue = requestObject.requestValue;
|
|
24
|
+
}
|
|
25
|
+
this.steps.push({ request: request.name });
|
|
26
|
+
return request.name;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
throw new Error(`Method ${methodName} not found`);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
addRequestFromFile(endpoint, methodName, requestFile) {
|
|
33
|
+
const [jsonData, dynamic] = readDataFromFile(requestFile);
|
|
34
|
+
return this.addRequest(endpoint, methodName, jsonData, dynamic);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
buildRequestsFromEndpoint(endpoint) {
|
|
38
|
+
if (this.endpoints.indexOf(endpoint.endpoint) !== -1) {
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
const processedMethods = endpoint.endpoint.methods.map(method => {
|
|
42
|
+
const { responseValue, proxy, ...rest } = method;
|
|
43
|
+
return rest;
|
|
44
|
+
});
|
|
45
|
+
this.services.push(...endpoint.services);
|
|
46
|
+
this.endpoints.push({ ...endpoint.endpoint, methods: processedMethods });
|
|
47
|
+
const yamlContent = getYamlBytes(endpoint.mockDescription);
|
|
48
|
+
const response = buildRequestsWrapper(yamlContent);
|
|
49
|
+
try {
|
|
50
|
+
const requests = JSON.parse(response);
|
|
51
|
+
this.requests.push(requests[0]);
|
|
52
|
+
} catch (error) {
|
|
53
|
+
throw new Error(response);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
addAssertEqual(value1, value2) {
|
|
58
|
+
const assertion = `requests.${value1} == "${value2}"`;
|
|
59
|
+
this.steps.push({ asserts: assertion });
|
|
60
|
+
return assertion;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
writeTestConfigurationToFile() {
|
|
64
|
+
const testDescription = createTestDescriptionFromScenario(this);
|
|
65
|
+
const yamlContent = getYamlBytes(testDescription);
|
|
66
|
+
return new Promise((resolve, reject) => {
|
|
67
|
+
writeTestDescriptionWrapper.async(yamlContent, this.name, (err, res) => {
|
|
68
|
+
if (err) {
|
|
69
|
+
reject(err);
|
|
70
|
+
} else if (res) {
|
|
71
|
+
reject(new Error(res));
|
|
72
|
+
} else {
|
|
73
|
+
resolve();
|
|
74
|
+
}
|
|
75
|
+
});
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
module.exports = Scenario;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { Endpoint } from './Endpoint';
|
|
2
|
+
import { Scenario } from './Scenario';
|
|
3
|
+
export declare class SkyrampClient {
|
|
4
|
+
constructor(kubeconfigPath?: string, clusterName?: string, context?: string);
|
|
5
|
+
applyLocal(): Promise<void>;
|
|
6
|
+
addKubeconfig(context: string, clusterName: string, kubeconfigPath: string): Promise<void>;
|
|
7
|
+
removeCluster(clusterName?: string): Promise<void>;
|
|
8
|
+
removeLocal(): Promise<void>;
|
|
9
|
+
removeClusterFromConfig(clusterName: string): Promise<void>;
|
|
10
|
+
mockerApply(namespace: string, address: string, endpoint: Endpoint): Promise<void>;
|
|
11
|
+
|
|
12
|
+
mockerApplyFromFile(namespace: string, address: string, filePath: string): Promise<void>;
|
|
13
|
+
applyMockDescription(namespace: string, address: string, mockYamlContent: string): Promise<void>;
|
|
14
|
+
testerStart(namespace: string, address: string, scenario: Scenario): Promise<void>;
|
|
15
|
+
testerStartFromFile(namespace: string, address: string, filePath: string): Promise<void>;
|
|
16
|
+
runTesterStart(namespace: string, address: string, testYamlContent: string, generateReport: boolean): Promise<void>;
|
|
17
|
+
}
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
const lib = require('../lib');
|
|
2
|
+
const { createTestDescriptionFromScenario, getYamlBytes, readDataFromFile } = require('../utils');
|
|
3
|
+
const applyLocalWrapper = lib.func('applyLocalWrapper', 'string', []);
|
|
4
|
+
const addKubeconfigWrapper = lib.func('addKubeconfigWrapper', 'string', ['string', 'bool', 'string', 'string']);
|
|
5
|
+
const deleteSkyrampWorkerWrapper = lib.func('deleteSkyrampWorkerWrapper', 'string', ['string'])
|
|
6
|
+
const deploySkyrampWorkerWrapper = lib.func('deploySkyrampWorkerWrapper', 'string', ['string', 'string', 'bool']);
|
|
7
|
+
const getKubeConfigPath = lib.func('getKubeConfigPath', 'string', []);
|
|
8
|
+
const removeLocalWrapper = lib.func('removeLocalWrapper', 'string', []);
|
|
9
|
+
const removeClusterFromConfigWrapper = lib.func('removeClusterFromConfigWrapper', 'string', ['string']);
|
|
10
|
+
const runTesterStartWrapper = lib.func('runTesterStartWrapper', 'string', ['string', 'string', 'string', 'bool']);
|
|
11
|
+
const applyMockDescriptionWrapper = lib.func('applyMockDescriptionWrapper', 'string', ['string', 'string', 'string']);
|
|
12
|
+
|
|
13
|
+
class SkyrampClient {
|
|
14
|
+
constructor(kubeconfigPath, clusterName, context) {
|
|
15
|
+
this.workerNamespaces = [];
|
|
16
|
+
if (kubeconfigPath || clusterName || context) {
|
|
17
|
+
this.addKubeconfig(clusterName, context, kubeconfigPath);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
async applyLocal() {
|
|
22
|
+
return new Promise((resolve, reject) => {
|
|
23
|
+
applyLocalWrapper.async((err, res) => {
|
|
24
|
+
if (err) {
|
|
25
|
+
reject(err);
|
|
26
|
+
} else if (res) {
|
|
27
|
+
reject(new Error(res));
|
|
28
|
+
} else {
|
|
29
|
+
this.kubeConfigPath = getKubeConfigPath();
|
|
30
|
+
if (this.kubeConfigPath == "") {
|
|
31
|
+
reject(new Error("no kubeconfig found"));
|
|
32
|
+
} else {
|
|
33
|
+
resolve();
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
});
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
async addKubeConfig(context, clusterName, kubeConfigPath) {
|
|
41
|
+
return new Promise((resolve, reject) => {
|
|
42
|
+
addKubeconfigWrapper.async(context, clusterName, kubeConfigPath, (err, res) => {
|
|
43
|
+
if (err) {
|
|
44
|
+
reject(err);
|
|
45
|
+
} else if (res) {
|
|
46
|
+
reject(new Error(res));
|
|
47
|
+
} else {
|
|
48
|
+
resolve();
|
|
49
|
+
this.kubeConfigPath = kubeConfigPath;
|
|
50
|
+
}
|
|
51
|
+
});
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
async removeCluster(clusterName) {
|
|
56
|
+
if (clusterName) {
|
|
57
|
+
return this.removeClusterFromConfig(clusterName);
|
|
58
|
+
} else {
|
|
59
|
+
return this.removeLocal();
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
async removeLocal() {
|
|
64
|
+
return new Promise((resolve, reject) => {
|
|
65
|
+
removeLocalWrapper.async((err, res) => {
|
|
66
|
+
if (err) {
|
|
67
|
+
reject(err);
|
|
68
|
+
} else if (res) {
|
|
69
|
+
reject(new Error(res));
|
|
70
|
+
} else {
|
|
71
|
+
resolve();
|
|
72
|
+
}
|
|
73
|
+
});
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
async removeClusterFromConfig(clusterName) {
|
|
78
|
+
return new Promise((resolve, reject) => {
|
|
79
|
+
removeClusterFromConfigWrapper.async(clusterName, (err, res) => {
|
|
80
|
+
if (err) {
|
|
81
|
+
reject(err);
|
|
82
|
+
} else if (res) {
|
|
83
|
+
reject(new Error(res));
|
|
84
|
+
} else {
|
|
85
|
+
resolve();
|
|
86
|
+
}
|
|
87
|
+
});
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
async deploySkyrampWorker(namespace, workerImage='', localImage=false) {
|
|
92
|
+
if (this.kubeConfigPath === null) {
|
|
93
|
+
throw new Error('No cluster to deploy worker to.');
|
|
94
|
+
}
|
|
95
|
+
return new Promise((resolve, reject) => {
|
|
96
|
+
deploySkyrampWorkerWrapper.async(namespace, workerImage, localImage, (err, res) => {
|
|
97
|
+
if (err) {
|
|
98
|
+
reject(err);
|
|
99
|
+
} else if (res) {
|
|
100
|
+
reject(new Error(res));
|
|
101
|
+
} else {
|
|
102
|
+
this.workerNamespaces.push(namespace);
|
|
103
|
+
resolve();
|
|
104
|
+
}
|
|
105
|
+
});
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
async deleteSkyrampWorker(namespace) {
|
|
110
|
+
if (this.kubeConfigPath === null) {
|
|
111
|
+
throw new Error('No cluster to delete worker from.');
|
|
112
|
+
}
|
|
113
|
+
const index = this.workerNamespaces.indexOf(namespace);
|
|
114
|
+
if (index === -1) {
|
|
115
|
+
throw new Error(`No worker to delete from ${namespace} namespace.`);
|
|
116
|
+
}
|
|
117
|
+
return new Promise((resolve, reject) => {
|
|
118
|
+
deleteSkyrampWorkerWrapper.async(namespace, (err, res) => {
|
|
119
|
+
if (err) {
|
|
120
|
+
reject(err);
|
|
121
|
+
} else if (res) {
|
|
122
|
+
reject(new Error(res));
|
|
123
|
+
} else {
|
|
124
|
+
this.workerNamespaces.splice(index, 1);
|
|
125
|
+
resolve();
|
|
126
|
+
}
|
|
127
|
+
});
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
async mockerApply(namespace, address, endpoint) {
|
|
132
|
+
try {
|
|
133
|
+
const yamlContent = getYamlBytes(endpoint.mockDescription);
|
|
134
|
+
await this.applyMockDescription(namespace, address, yamlContent);
|
|
135
|
+
} catch (err) {
|
|
136
|
+
throw err;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// NPM only: for VS code extension use
|
|
141
|
+
async mockerApplyFromFile(namespace, address, filepath) {
|
|
142
|
+
try {
|
|
143
|
+
const yamlContent = getYamlBytes(readDataFromFile(filepath)[0]);
|
|
144
|
+
await this.applyMockDescription(namespace, address, yamlContent);
|
|
145
|
+
} catch (err) {
|
|
146
|
+
throw err;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
async applyMockDescription(namespace, address, yamlContent) {
|
|
151
|
+
return new Promise((resolve, reject) => {
|
|
152
|
+
applyMockDescriptionWrapper.async(namespace, address, yamlContent, (err, res) => {
|
|
153
|
+
if (err) {
|
|
154
|
+
reject(err);
|
|
155
|
+
} else if (res) {
|
|
156
|
+
reject(new Error(res));
|
|
157
|
+
} else {
|
|
158
|
+
resolve();
|
|
159
|
+
}
|
|
160
|
+
});
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
async testerStart(namespace, address, scenario) {
|
|
165
|
+
try {
|
|
166
|
+
const testDescription = createTestDescriptionFromScenario(scenario);
|
|
167
|
+
const testYamlContent = getYamlBytes(testDescription);
|
|
168
|
+
await this.runTesterStart(namespace, address, testYamlContent, false);
|
|
169
|
+
} catch (err) {
|
|
170
|
+
throw err;
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
// NPM only: for VS code extension use
|
|
175
|
+
async testerStartFromFile(namespace, address, filepath) {
|
|
176
|
+
try {
|
|
177
|
+
const testDescription = readDataFromFile(filepath)[0]
|
|
178
|
+
const testYamlContent = getYamlBytes(testDescription);
|
|
179
|
+
await this.runTesterStart(namespace, address, testYamlContent, true);
|
|
180
|
+
} catch (err) {
|
|
181
|
+
throw err;
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
async runTesterStart(namespace, address, testYamlContent, generateReport) {
|
|
186
|
+
return new Promise((resolve, reject) => {
|
|
187
|
+
runTesterStartWrapper.async(namespace, address, testYamlContent, generateReport, (err, res) => {
|
|
188
|
+
if (err) {
|
|
189
|
+
reject(err);
|
|
190
|
+
} else if (res) {
|
|
191
|
+
reject(new Error(res));
|
|
192
|
+
} else {
|
|
193
|
+
resolve();
|
|
194
|
+
}
|
|
195
|
+
});
|
|
196
|
+
});
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
module.exports = SkyrampClient;
|
package/src/index.d.ts
ADDED
package/src/index.js
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
const SkyrampClient = require('./classes/SkyrampClient');
|
|
2
|
+
const GrpcEndpoint = require('./classes/GrpcEndpoint');
|
|
3
|
+
const RestEndpoint = require('./classes/RestEndpoint');
|
|
4
|
+
const Scenario = require('./classes/Scenario');
|
|
5
|
+
|
|
6
|
+
module.exports = {
|
|
7
|
+
SkyrampClient,
|
|
8
|
+
GrpcEndpoint,
|
|
9
|
+
RestEndpoint,
|
|
10
|
+
Scenario
|
|
11
|
+
}
|
package/src/lib.js
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
const koffi = require('koffi');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
|
|
4
|
+
const platform = process.platform;
|
|
5
|
+
const arch = process.arch;
|
|
6
|
+
let libPath = '';
|
|
7
|
+
|
|
8
|
+
if (platform === 'linux') {
|
|
9
|
+
if (arch === 'x64') {
|
|
10
|
+
libPath = 'dev-linux-amd64.so';
|
|
11
|
+
} else if (arch === 'ia32') {
|
|
12
|
+
libPath = 'dev-linux-386.so';
|
|
13
|
+
}
|
|
14
|
+
} else if (platform === 'darwin') {
|
|
15
|
+
if (arch === 'x64') {
|
|
16
|
+
libPath = 'dev-darwin-amd64.dylib';
|
|
17
|
+
} else if (arch === 'arm64') {
|
|
18
|
+
libPath = 'dev-darwin-arm64.dylib';
|
|
19
|
+
}
|
|
20
|
+
} else if (platform === 'win32') {
|
|
21
|
+
libPath = 'dev-windows-amd64.dll';
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
if (!libPath) {
|
|
25
|
+
throw new Error(`Unsupported platform or architecture: ${platform}-${arch}`);
|
|
26
|
+
}
|
|
27
|
+
const libFullPath = path.join(__dirname, '..', 'lib', libPath);
|
|
28
|
+
const lib = koffi.load(libFullPath);
|
|
29
|
+
|
|
30
|
+
module.exports = lib;
|
package/src/utils.js
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
const fs = require('fs');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
const yaml = require('js-yaml');
|
|
4
|
+
|
|
5
|
+
function getYamlBytes(jsonObject) {
|
|
6
|
+
try {
|
|
7
|
+
const yamlContent = yaml.dump(jsonObject, {lineWidth: 100});
|
|
8
|
+
return yamlContent;
|
|
9
|
+
} catch (error) {
|
|
10
|
+
throw new Error('Error converting to YAML bytes:', error);
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function createTestDescriptionFromScenario(scenario) {
|
|
15
|
+
return {
|
|
16
|
+
test: {
|
|
17
|
+
name: scenario.name,
|
|
18
|
+
testPattern: [{
|
|
19
|
+
startAt: 1,
|
|
20
|
+
scenario: scenario.name
|
|
21
|
+
}]
|
|
22
|
+
},
|
|
23
|
+
services: scenario.services,
|
|
24
|
+
endpoints: scenario.endpoints,
|
|
25
|
+
scenarios: [{
|
|
26
|
+
name: scenario.name,
|
|
27
|
+
steps: scenario.steps
|
|
28
|
+
}],
|
|
29
|
+
requests: scenario.requests
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function readDataFromFile(filename) {
|
|
34
|
+
try {
|
|
35
|
+
const fileExtension = path.extname(filename);
|
|
36
|
+
let jsonData;
|
|
37
|
+
if (fileExtension === '.json') {
|
|
38
|
+
const jsonContent = fs.readFileSync(filename, 'utf8');
|
|
39
|
+
jsonData = JSON.parse(jsonContent);
|
|
40
|
+
} else if (fileExtension === '.yaml' || fileExtension === '.yml') {
|
|
41
|
+
const yamlContent = fs.readFileSync(filename, 'utf8');
|
|
42
|
+
jsonData = yaml.load(yamlContent);
|
|
43
|
+
} else if (fileExtension === '.js') {
|
|
44
|
+
return [filename, true];
|
|
45
|
+
} else {
|
|
46
|
+
throw new Error('Unsupported file format. Only .json, .yaml, and .js files are supported.');
|
|
47
|
+
}
|
|
48
|
+
return [jsonData, false];
|
|
49
|
+
} catch(error) {
|
|
50
|
+
throw new Error('Error reading or parsing file: ', error);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
module.exports = {
|
|
55
|
+
createTestDescriptionFromScenario,
|
|
56
|
+
getYamlBytes,
|
|
57
|
+
readDataFromFile
|
|
58
|
+
}
|