mirror of
https://github.com/puppetlabs/vmpooler.git
synced 2026-01-25 17:48:41 -05:00
Add v3 api and remove v2
This commit is contained in:
parent
d0d97dd0a8
commit
93201756a0
13 changed files with 627 additions and 2009 deletions
|
|
@ -12,8 +12,7 @@
|
||||||
# SupportedStyles: with_first_parameter, with_fixed_indentation
|
# SupportedStyles: with_first_parameter, with_fixed_indentation
|
||||||
Layout/ParameterAlignment:
|
Layout/ParameterAlignment:
|
||||||
Exclude:
|
Exclude:
|
||||||
- 'lib/vmpooler/api/v1.rb'
|
- 'lib/vmpooler/api/v3.rb'
|
||||||
- 'lib/vmpooler/api/v2.rb'
|
|
||||||
|
|
||||||
# Offense count: 9
|
# Offense count: 9
|
||||||
# Cop supports --auto-correct.
|
# Cop supports --auto-correct.
|
||||||
|
|
@ -22,15 +21,13 @@ Layout/ParameterAlignment:
|
||||||
Layout/CaseIndentation:
|
Layout/CaseIndentation:
|
||||||
Exclude:
|
Exclude:
|
||||||
- 'lib/vmpooler/api/helpers.rb'
|
- 'lib/vmpooler/api/helpers.rb'
|
||||||
- 'lib/vmpooler/api/v1.rb'
|
- 'lib/vmpooler/api/v3.rb'
|
||||||
- 'lib/vmpooler/api/v2.rb'
|
|
||||||
|
|
||||||
# Offense count: 1
|
# Offense count: 1
|
||||||
# Cop supports --auto-correct.
|
# Cop supports --auto-correct.
|
||||||
Layout/ClosingParenthesisIndentation:
|
Layout/ClosingParenthesisIndentation:
|
||||||
Exclude:
|
Exclude:
|
||||||
- 'lib/vmpooler/api/v1.rb'
|
- 'lib/vmpooler/api/v3.rb'
|
||||||
- 'lib/vmpooler/api/v2.rb'
|
|
||||||
|
|
||||||
# Offense count: 1
|
# Offense count: 1
|
||||||
# Cop supports --auto-correct.
|
# Cop supports --auto-correct.
|
||||||
|
|
@ -61,16 +58,14 @@ Layout/EmptyLinesAroundModuleBody:
|
||||||
Layout/FirstHashElementIndentation:
|
Layout/FirstHashElementIndentation:
|
||||||
Exclude:
|
Exclude:
|
||||||
- 'lib/vmpooler/api/helpers.rb'
|
- 'lib/vmpooler/api/helpers.rb'
|
||||||
- 'lib/vmpooler/api/v1.rb'
|
- 'lib/vmpooler/api/v3.rb'
|
||||||
- 'lib/vmpooler/api/v2.rb'
|
|
||||||
|
|
||||||
# Offense count: 1
|
# Offense count: 1
|
||||||
# Cop supports --auto-correct.
|
# Cop supports --auto-correct.
|
||||||
# Configuration parameters: Width, IgnoredPatterns.
|
# Configuration parameters: Width, IgnoredPatterns.
|
||||||
Layout/IndentationWidth:
|
Layout/IndentationWidth:
|
||||||
Exclude:
|
Exclude:
|
||||||
- 'lib/vmpooler/api/v1.rb'
|
- 'lib/vmpooler/api/v3.rb'
|
||||||
- 'lib/vmpooler/api/v2.rb'
|
|
||||||
|
|
||||||
# Offense count: 1
|
# Offense count: 1
|
||||||
# Cop supports --auto-correct.
|
# Cop supports --auto-correct.
|
||||||
|
|
@ -78,8 +73,7 @@ Layout/IndentationWidth:
|
||||||
# SupportedStyles: symmetrical, new_line, same_line
|
# SupportedStyles: symmetrical, new_line, same_line
|
||||||
Layout/MultilineMethodCallBraceLayout:
|
Layout/MultilineMethodCallBraceLayout:
|
||||||
Exclude:
|
Exclude:
|
||||||
- 'lib/vmpooler/api/v1.rb'
|
- 'lib/vmpooler/api/v3.rb'
|
||||||
- 'lib/vmpooler/api/v2.rb'
|
|
||||||
|
|
||||||
# Offense count: 1
|
# Offense count: 1
|
||||||
# Cop supports --auto-correct.
|
# Cop supports --auto-correct.
|
||||||
|
|
@ -93,16 +87,14 @@ Layout/SpaceAroundEqualsInParameterDefault:
|
||||||
# Cop supports --auto-correct.
|
# Cop supports --auto-correct.
|
||||||
Layout/SpaceAroundKeyword:
|
Layout/SpaceAroundKeyword:
|
||||||
Exclude:
|
Exclude:
|
||||||
- 'lib/vmpooler/api/v1.rb'
|
- 'lib/vmpooler/api/v3.rb'
|
||||||
- 'lib/vmpooler/api/v2.rb'
|
|
||||||
|
|
||||||
# Offense count: 1
|
# Offense count: 1
|
||||||
# Cop supports --auto-correct.
|
# Cop supports --auto-correct.
|
||||||
# Configuration parameters: AllowForAlignment.
|
# Configuration parameters: AllowForAlignment.
|
||||||
Layout/SpaceAroundOperators:
|
Layout/SpaceAroundOperators:
|
||||||
Exclude:
|
Exclude:
|
||||||
- 'lib/vmpooler/api/v1.rb'
|
- 'lib/vmpooler/api/v3.rb'
|
||||||
- 'lib/vmpooler/api/v2.rb'
|
|
||||||
|
|
||||||
# Offense count: 8
|
# Offense count: 8
|
||||||
# Cop supports --auto-correct.
|
# Cop supports --auto-correct.
|
||||||
|
|
@ -117,16 +109,14 @@ Layout/SpaceInsideHashLiteralBraces:
|
||||||
# Cop supports --auto-correct.
|
# Cop supports --auto-correct.
|
||||||
Layout/SpaceInsideParens:
|
Layout/SpaceInsideParens:
|
||||||
Exclude:
|
Exclude:
|
||||||
- 'lib/vmpooler/api/v1.rb'
|
- 'lib/vmpooler/api/v3.rb'
|
||||||
- 'lib/vmpooler/api/v2.rb'
|
|
||||||
|
|
||||||
# Offense count: 2
|
# Offense count: 2
|
||||||
# Configuration parameters: AllowSafeAssignment.
|
# Configuration parameters: AllowSafeAssignment.
|
||||||
Lint/AssignmentInCondition:
|
Lint/AssignmentInCondition:
|
||||||
Exclude:
|
Exclude:
|
||||||
- 'lib/vmpooler/api/helpers.rb'
|
- 'lib/vmpooler/api/helpers.rb'
|
||||||
- 'lib/vmpooler/api/v1.rb'
|
- 'lib/vmpooler/api/v3.rb'
|
||||||
- 'lib/vmpooler/api/v2.rb'
|
|
||||||
|
|
||||||
# Offense count: 2
|
# Offense count: 2
|
||||||
Lint/SuppressedException:
|
Lint/SuppressedException:
|
||||||
|
|
@ -158,8 +148,7 @@ Lint/UselessAssignment:
|
||||||
Style/AndOr:
|
Style/AndOr:
|
||||||
Exclude:
|
Exclude:
|
||||||
- 'lib/vmpooler/api/helpers.rb'
|
- 'lib/vmpooler/api/helpers.rb'
|
||||||
- 'lib/vmpooler/api/v1.rb'
|
- 'lib/vmpooler/api/v3.rb'
|
||||||
- 'lib/vmpooler/api/v2.rb'
|
|
||||||
|
|
||||||
# Offense count: 1
|
# Offense count: 1
|
||||||
Style/CaseEquality:
|
Style/CaseEquality:
|
||||||
|
|
@ -180,8 +169,7 @@ Style/For:
|
||||||
Style/HashSyntax:
|
Style/HashSyntax:
|
||||||
Exclude:
|
Exclude:
|
||||||
- 'lib/vmpooler/api/helpers.rb'
|
- 'lib/vmpooler/api/helpers.rb'
|
||||||
- 'lib/vmpooler/api/v1.rb'
|
- 'lib/vmpooler/api/v3.rb'
|
||||||
- 'lib/vmpooler/api/v2.rb'
|
|
||||||
|
|
||||||
# Offense count: 4
|
# Offense count: 4
|
||||||
# Cop supports --auto-correct.
|
# Cop supports --auto-correct.
|
||||||
|
|
@ -189,8 +177,7 @@ Style/HashSyntax:
|
||||||
Style/IfUnlessModifier:
|
Style/IfUnlessModifier:
|
||||||
Exclude:
|
Exclude:
|
||||||
- 'lib/vmpooler/api/helpers.rb'
|
- 'lib/vmpooler/api/helpers.rb'
|
||||||
- 'lib/vmpooler/api/v1.rb'
|
- 'lib/vmpooler/api/v3.rb'
|
||||||
- 'lib/vmpooler/api/v2.rb'
|
|
||||||
|
|
||||||
# Offense count: 3
|
# Offense count: 3
|
||||||
# Cop supports --auto-correct.
|
# Cop supports --auto-correct.
|
||||||
|
|
@ -198,15 +185,13 @@ Style/IfUnlessModifier:
|
||||||
# SupportedStyles: both, prefix, postfix
|
# SupportedStyles: both, prefix, postfix
|
||||||
Style/NegatedIf:
|
Style/NegatedIf:
|
||||||
Exclude:
|
Exclude:
|
||||||
- 'lib/vmpooler/api/v1.rb'
|
- 'lib/vmpooler/api/v3.rb'
|
||||||
- 'lib/vmpooler/api/v2.rb'
|
|
||||||
|
|
||||||
# Offense count: 3
|
# Offense count: 3
|
||||||
# Cop supports --auto-correct.
|
# Cop supports --auto-correct.
|
||||||
Style/Not:
|
Style/Not:
|
||||||
Exclude:
|
Exclude:
|
||||||
- 'lib/vmpooler/api/v1.rb'
|
- 'lib/vmpooler/api/v3.rb'
|
||||||
- 'lib/vmpooler/api/v2.rb'
|
|
||||||
|
|
||||||
# Offense count: 1
|
# Offense count: 1
|
||||||
# Cop supports --auto-correct.
|
# Cop supports --auto-correct.
|
||||||
|
|
@ -215,30 +200,26 @@ Style/Not:
|
||||||
Style/NumericPredicate:
|
Style/NumericPredicate:
|
||||||
Exclude:
|
Exclude:
|
||||||
- 'spec/**/*'
|
- 'spec/**/*'
|
||||||
- 'lib/vmpooler/api/v1.rb'
|
- 'lib/vmpooler/api/v3.rb'
|
||||||
- 'lib/vmpooler/api/v2.rb'
|
|
||||||
|
|
||||||
# Offense count: 2
|
# Offense count: 2
|
||||||
# Cop supports --auto-correct.
|
# Cop supports --auto-correct.
|
||||||
Style/ParallelAssignment:
|
Style/ParallelAssignment:
|
||||||
Exclude:
|
Exclude:
|
||||||
- 'lib/vmpooler/api/v1.rb'
|
- 'lib/vmpooler/api/v3.rb'
|
||||||
- 'lib/vmpooler/api/v2.rb'
|
|
||||||
|
|
||||||
# Offense count: 1
|
# Offense count: 1
|
||||||
# Cop supports --auto-correct.
|
# Cop supports --auto-correct.
|
||||||
# Configuration parameters: AllowSafeAssignment.
|
# Configuration parameters: AllowSafeAssignment.
|
||||||
Style/ParenthesesAroundCondition:
|
Style/ParenthesesAroundCondition:
|
||||||
Exclude:
|
Exclude:
|
||||||
- 'lib/vmpooler/api/v1.rb'
|
- 'lib/vmpooler/api/v3.rb'
|
||||||
- 'lib/vmpooler/api/v2.rb'
|
|
||||||
|
|
||||||
# Offense count: 2
|
# Offense count: 2
|
||||||
# Cop supports --auto-correct.
|
# Cop supports --auto-correct.
|
||||||
Style/PerlBackrefs:
|
Style/PerlBackrefs:
|
||||||
Exclude:
|
Exclude:
|
||||||
- 'lib/vmpooler/api/v1.rb'
|
- 'lib/vmpooler/api/v3.rb'
|
||||||
- 'lib/vmpooler/api/v2.rb'
|
|
||||||
|
|
||||||
# Offense count: 1
|
# Offense count: 1
|
||||||
# Configuration parameters: NamePrefix, NamePrefixBlacklist, NameWhitelist.
|
# Configuration parameters: NamePrefix, NamePrefixBlacklist, NameWhitelist.
|
||||||
|
|
@ -254,8 +235,7 @@ Naming/PredicateName:
|
||||||
# Cop supports --auto-correct.
|
# Cop supports --auto-correct.
|
||||||
Style/RedundantParentheses:
|
Style/RedundantParentheses:
|
||||||
Exclude:
|
Exclude:
|
||||||
- 'lib/vmpooler/api/v1.rb'
|
- 'lib/vmpooler/api/v3.rb'
|
||||||
- 'lib/vmpooler/api/v2.rb'
|
|
||||||
|
|
||||||
# Offense count: 2
|
# Offense count: 2
|
||||||
# Cop supports --auto-correct.
|
# Cop supports --auto-correct.
|
||||||
|
|
@ -276,8 +256,7 @@ Style/RedundantSelf:
|
||||||
# SupportedStyles: single_quotes, double_quotes
|
# SupportedStyles: single_quotes, double_quotes
|
||||||
Style/StringLiterals:
|
Style/StringLiterals:
|
||||||
Exclude:
|
Exclude:
|
||||||
- 'lib/vmpooler/api/v1.rb'
|
- 'lib/vmpooler/api/v3.rb'
|
||||||
- 'lib/vmpooler/api/v2.rb'
|
|
||||||
|
|
||||||
# Offense count: 1
|
# Offense count: 1
|
||||||
# Cop supports --auto-correct.
|
# Cop supports --auto-correct.
|
||||||
|
|
@ -292,8 +271,7 @@ Style/TernaryParentheses:
|
||||||
# SupportedStyles: snake_case, camelCase
|
# SupportedStyles: snake_case, camelCase
|
||||||
Naming/VariableName:
|
Naming/VariableName:
|
||||||
Exclude:
|
Exclude:
|
||||||
- 'lib/vmpooler/api/v1.rb'
|
- 'lib/vmpooler/api/v3.rb'
|
||||||
- 'lib/vmpooler/api/v2.rb'
|
|
||||||
|
|
||||||
# Offense count: 1
|
# Offense count: 1
|
||||||
# Cop supports --auto-correct.
|
# Cop supports --auto-correct.
|
||||||
|
|
|
||||||
|
|
@ -14,12 +14,12 @@ vmpooler provides a REST API for VM management. The following examples use `cur
|
||||||
|
|
||||||
## Major change in V3 versus V2
|
## Major change in V3 versus V2
|
||||||
|
|
||||||
The deprecated api/v1 has been removed.
|
The api/v1 and api/v2 endpoints have been removed. Additionally, the generic api endpoint that reroutes to a versioned endpoint has been removed.
|
||||||
|
|
||||||
The api/v2 endpoint removes the deprecated "domain" key returned in some of the operations like getting a VM, etc. If there is a "domain" configured in the top level configuration or for a specific provider,
|
The api/v3 endpoint removes the deprecated "domain" key returned in some of the operations like getting a VM, etc. If there is a "domain" configured in the top level configuration or for a specific provider,
|
||||||
the hostname now returns an FQDN including that domain. That is to say, we can now have multiple, different domains for each pool instead of only a single domain for all pools, or a domain restricted to a particular provider.
|
the hostname now returns an FQDN including that domain. That is to say, we can now have multiple, different domains for each pool instead of only a single domain for all pools, or a domain restricted to a particular provider.
|
||||||
|
|
||||||
Clients using some of the direct API paths (without specifying api/v1 or api/v2) will now be redirected to v2.
|
Clients using some of the direct API paths (without specifying api/v1 or api/v2) will now now need to specify the versioned endpoint (api/v3).
|
||||||
|
|
||||||
## Major change in V2 versus V1
|
## Major change in V2 versus V1
|
||||||
|
|
||||||
|
|
@ -50,7 +50,7 @@ Return codes:
|
||||||
* 404 when config:auth not found or other error
|
* 404 when config:auth not found or other error
|
||||||
|
|
||||||
```
|
```
|
||||||
$ curl -u jdoe --url vmpooler.example.com/api/v2/token
|
$ curl -u jdoe --url vmpooler.example.com/api/v3/token
|
||||||
Enter host password for user 'jdoe':
|
Enter host password for user 'jdoe':
|
||||||
```
|
```
|
||||||
```json
|
```json
|
||||||
|
|
@ -72,7 +72,7 @@ Return codes:
|
||||||
* 404 when config:auth not found
|
* 404 when config:auth not found
|
||||||
|
|
||||||
```
|
```
|
||||||
$ curl -X POST -u jdoe --url vmpooler.example.com/api/v2/token
|
$ curl -X POST -u jdoe --url vmpooler.example.com/api/v3/token
|
||||||
Enter host password for user 'jdoe':
|
Enter host password for user 'jdoe':
|
||||||
```
|
```
|
||||||
```json
|
```json
|
||||||
|
|
@ -91,7 +91,7 @@ Return codes:
|
||||||
* 404 when config:auth or token not found
|
* 404 when config:auth or token not found
|
||||||
|
|
||||||
```
|
```
|
||||||
$ curl --url vmpooler.example.com/api/v2/token/utpg2i2xswor6h8ttjhu3d47z53yy47y
|
$ curl --url vmpooler.example.com/api/v3/token/utpg2i2xswor6h8ttjhu3d47z53yy47y
|
||||||
```
|
```
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
|
|
@ -120,7 +120,7 @@ Return codes:
|
||||||
* 404 when config:auth not found
|
* 404 when config:auth not found
|
||||||
|
|
||||||
```
|
```
|
||||||
$ curl -X DELETE -u jdoe --url vmpooler.example.com/api/v2/token/utpg2i2xswor6h8ttjhu3d47z53yy47y
|
$ curl -X DELETE -u jdoe --url vmpooler.example.com/api/v3/token/utpg2i2xswor6h8ttjhu3d47z53yy47y
|
||||||
Enter host password for user 'jdoe':
|
Enter host password for user 'jdoe':
|
||||||
```
|
```
|
||||||
```json
|
```json
|
||||||
|
|
@ -139,7 +139,7 @@ Return codes:
|
||||||
* 200 OK
|
* 200 OK
|
||||||
|
|
||||||
```
|
```
|
||||||
$ curl --url vmpooler.example.com/api/v2/vm
|
$ curl --url vmpooler.example.com/api/v3/vm
|
||||||
```
|
```
|
||||||
```json
|
```json
|
||||||
[
|
[
|
||||||
|
|
@ -160,7 +160,7 @@ Return codes:
|
||||||
* 503 when the vm failed to allocate a vm, or the pool is empty
|
* 503 when the vm failed to allocate a vm, or the pool is empty
|
||||||
|
|
||||||
```
|
```
|
||||||
$ curl -d '{"debian-7-i386":"2","debian-7-x86_64":"1"}' --url vmpooler.example.com/api/v2/vm
|
$ curl -d '{"debian-7-i386":"2","debian-7-x86_64":"1"}' --url vmpooler.example.com/api/v3/vm
|
||||||
```
|
```
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
|
|
@ -189,7 +189,7 @@ Return codes:
|
||||||
* 503 when the vm failed to allocate a vm, or the pool is empty
|
* 503 when the vm failed to allocate a vm, or the pool is empty
|
||||||
|
|
||||||
```
|
```
|
||||||
$ curl -d --url vmpooler.example.com/api/v2/vm/debian-7-i386
|
$ curl -d --url vmpooler.example.com/api/v3/vm/debian-7-i386
|
||||||
```
|
```
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
|
|
@ -203,7 +203,7 @@ $ curl -d --url vmpooler.example.com/api/v2/vm/debian-7-i386
|
||||||
Multiple VMs can be requested by using multiple query parameters in the URL:
|
Multiple VMs can be requested by using multiple query parameters in the URL:
|
||||||
|
|
||||||
```
|
```
|
||||||
$ curl -d --url vmpooler.example.com/api/v2/vm/debian-7-i386+debian-7-i386+debian-7-x86_64
|
$ curl -d --url vmpooler.example.com/api/v3/vm/debian-7-i386+debian-7-i386+debian-7-x86_64
|
||||||
```
|
```
|
||||||
|
|
||||||
```json
|
```json
|
||||||
|
|
@ -232,7 +232,7 @@ Return codes:
|
||||||
* 404 when requesting an invalid VM hostname
|
* 404 when requesting an invalid VM hostname
|
||||||
|
|
||||||
```
|
```
|
||||||
$ curl --url vmpooler.example.com/api/v2/vm/pxpmtoonx7fiqg6
|
$ curl --url vmpooler.example.com/api/v3/vm/pxpmtoonx7fiqg6
|
||||||
```
|
```
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
|
|
@ -276,7 +276,7 @@ Return codes:
|
||||||
* 400 when supplied PUT parameters fail validation
|
* 400 when supplied PUT parameters fail validation
|
||||||
|
|
||||||
```
|
```
|
||||||
$ curl -X PUT -d '{"lifetime":"2"}' --url vmpooler.example.com/api/v2/vm/fq6qlpjlsskycq6
|
$ curl -X PUT -d '{"lifetime":"2"}' --url vmpooler.example.com/api/v3/vm/fq6qlpjlsskycq6
|
||||||
```
|
```
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
|
|
@ -285,7 +285,7 @@ $ curl -X PUT -d '{"lifetime":"2"}' --url vmpooler.example.com/api/v2/vm/fq6qlpj
|
||||||
```
|
```
|
||||||
|
|
||||||
```
|
```
|
||||||
$ curl -X PUT -d '{"tags":{"department":"engineering","user":"jdoe"}}' --url vmpooler.example.com/api/v2/vm/fq6qlpjlsskycq6
|
$ curl -X PUT -d '{"tags":{"department":"engineering","user":"jdoe"}}' --url vmpooler.example.com/api/v3/vm/fq6qlpjlsskycq6
|
||||||
```
|
```
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
|
|
@ -303,7 +303,7 @@ Return codes:
|
||||||
* 404 when requesting an invalid VM hostname
|
* 404 when requesting an invalid VM hostname
|
||||||
|
|
||||||
```
|
```
|
||||||
$ curl -X DELETE --url vmpooler.example.com/api/v2/vm/fq6qlpjlsskycq6
|
$ curl -X DELETE --url vmpooler.example.com/api/v3/vm/fq6qlpjlsskycq6
|
||||||
```
|
```
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
|
|
@ -323,7 +323,7 @@ Return codes:
|
||||||
* 404 when requesting an invalid VM hostname or size is not an integer
|
* 404 when requesting an invalid VM hostname or size is not an integer
|
||||||
|
|
||||||
````
|
````
|
||||||
$ curl -X POST -H X-AUTH-TOKEN:a9znth9dn01t416hrguu56ze37t790bl --url vmpooler.example.com/api/v2/vm/fq6qlpjlsskycq6/disk/8
|
$ curl -X POST -H X-AUTH-TOKEN:a9znth9dn01t416hrguu56ze37t790bl --url vmpooler.example.com/api/v3/vm/fq6qlpjlsskycq6/disk/8
|
||||||
````
|
````
|
||||||
````json
|
````json
|
||||||
{
|
{
|
||||||
|
|
@ -337,7 +337,7 @@ $ curl -X POST -H X-AUTH-TOKEN:a9znth9dn01t416hrguu56ze37t790bl --url vmpooler.e
|
||||||
Provisioning and attaching disks can take a moment, but once the task completes it will be reflected in a `GET /vm/<hostname>` query:
|
Provisioning and attaching disks can take a moment, but once the task completes it will be reflected in a `GET /vm/<hostname>` query:
|
||||||
|
|
||||||
````
|
````
|
||||||
$ curl --url vmpooler.example.com/api/v2/vm/fq6qlpjlsskycq6
|
$ curl --url vmpooler.example.com/api/v3/vm/fq6qlpjlsskycq6
|
||||||
````
|
````
|
||||||
````json
|
````json
|
||||||
{
|
{
|
||||||
|
|
@ -367,7 +367,7 @@ Return codes:
|
||||||
* 404 when requesting an invalid VM hostname
|
* 404 when requesting an invalid VM hostname
|
||||||
|
|
||||||
````
|
````
|
||||||
$ curl -X POST -H X-AUTH-TOKEN:a9znth9dn01t416hrguu56ze37t790bl --url vmpooler.example.com/api/v2/vm/fq6qlpjlsskycq6/snapshot
|
$ curl -X POST -H X-AUTH-TOKEN:a9znth9dn01t416hrguu56ze37t790bl --url vmpooler.example.com/api/v3/vm/fq6qlpjlsskycq6/snapshot
|
||||||
````
|
````
|
||||||
````json
|
````json
|
||||||
{
|
{
|
||||||
|
|
@ -381,7 +381,7 @@ $ curl -X POST -H X-AUTH-TOKEN:a9znth9dn01t416hrguu56ze37t790bl --url vmpooler.e
|
||||||
Snapshotting a live VM can take a moment, but once the snapshot task completes it will be reflected in a `GET /vm/<hostname>` query:
|
Snapshotting a live VM can take a moment, but once the snapshot task completes it will be reflected in a `GET /vm/<hostname>` query:
|
||||||
|
|
||||||
````
|
````
|
||||||
$ curl --url vmpooler.example.com/api/v2/vm/fq6qlpjlsskycq6
|
$ curl --url vmpooler.example.com/api/v3/vm/fq6qlpjlsskycq6
|
||||||
````
|
````
|
||||||
````json
|
````json
|
||||||
{
|
{
|
||||||
|
|
@ -408,7 +408,7 @@ Return codes:
|
||||||
* 404 when requesting an invalid VM hostname or snapshot is not valid
|
* 404 when requesting an invalid VM hostname or snapshot is not valid
|
||||||
|
|
||||||
````
|
````
|
||||||
$ curl X POST -H X-AUTH-TOKEN:a9znth9dn01t416hrguu56ze37t790bl --url vmpooler.example.com/api/v2/vm/fq6qlpjlsskycq6/snapshot/n4eb4kdtp7rwv4x158366vd9jhac8btq
|
$ curl X POST -H X-AUTH-TOKEN:a9znth9dn01t416hrguu56ze37t790bl --url vmpooler.example.com/api/v3/vm/fq6qlpjlsskycq6/snapshot/n4eb4kdtp7rwv4x158366vd9jhac8btq
|
||||||
````
|
````
|
||||||
````json
|
````json
|
||||||
{
|
{
|
||||||
|
|
@ -423,7 +423,7 @@ $ curl X POST -H X-AUTH-TOKEN:a9znth9dn01t416hrguu56ze37t790bl --url vmpooler.ex
|
||||||
A "live" status endpoint, representing the current state of the service.
|
A "live" status endpoint, representing the current state of the service.
|
||||||
|
|
||||||
```
|
```
|
||||||
$ curl --url vmpooler.example.com/api/v2/status
|
$ curl --url vmpooler.example.com/api/v3/status
|
||||||
```
|
```
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
|
|
@ -475,7 +475,7 @@ The top level sections are: "capacity", "queue", "clone", "boot", "pools" and "s
|
||||||
If the query parameter 'view' is provided, it will be used to select which top level
|
If the query parameter 'view' is provided, it will be used to select which top level
|
||||||
element to compute and return. Select them by specifying which one you want in a comma
|
element to compute and return. Select them by specifying which one you want in a comma
|
||||||
separated list.
|
separated list.
|
||||||
For example `vmpooler.example.com/api/v2/status?view=capacity,boot`
|
For example `vmpooler.example.com/api/v3/status?view=capacity,boot`
|
||||||
|
|
||||||
##### GET /summary[?from=YYYY-MM-DD[&to=YYYY-MM-DD]]
|
##### GET /summary[?from=YYYY-MM-DD[&to=YYYY-MM-DD]]
|
||||||
|
|
||||||
|
|
@ -492,7 +492,7 @@ Return codes:
|
||||||
|
|
||||||
|
|
||||||
```
|
```
|
||||||
$ curl --url vmpooler.example.com/api/v2/summary
|
$ curl --url vmpooler.example.com/api/v3/summary
|
||||||
```
|
```
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
|
|
@ -584,7 +584,7 @@ $ curl --url vmpooler.example.com/api/v2/summary
|
||||||
|
|
||||||
|
|
||||||
```
|
```
|
||||||
$ curl -G -d 'from=2015-03-10' -d 'to=2015-03-11' --url vmpooler.example.com/api/v2/summary
|
$ curl -G -d 'from=2015-03-10' -d 'to=2015-03-11' --url vmpooler.example.com/api/v3/summary
|
||||||
```
|
```
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
|
|
@ -648,9 +648,9 @@ $ curl -G -d 'from=2015-03-10' -d 'to=2015-03-11' --url vmpooler.example.com/api
|
||||||
```
|
```
|
||||||
|
|
||||||
You can also query only the specific top level section you want by including it after `summary/`.
|
You can also query only the specific top level section you want by including it after `summary/`.
|
||||||
The valid sections are "boot", "clone" or "tag" eg. `vmpooler.example.com/api/v2/summary/boot/`.
|
The valid sections are "boot", "clone" or "tag" eg. `vmpooler.example.com/api/v3/summary/boot/`.
|
||||||
You can further drill-down the data by specifying the second level parameter to query eg
|
You can further drill-down the data by specifying the second level parameter to query eg
|
||||||
`vmpooler.example.com/api/v2/summary/tag/created_by`
|
`vmpooler.example.com/api/v3/summary/tag/created_by`
|
||||||
|
|
||||||
##### GET /poolstat?pool=FOO
|
##### GET /poolstat?pool=FOO
|
||||||
|
|
||||||
|
|
@ -662,7 +662,7 @@ Return codes
|
||||||
* 200 OK
|
* 200 OK
|
||||||
|
|
||||||
```
|
```
|
||||||
$ curl https://vmpooler.example.com/api/v2/poolstat?pool=centos-6-x86_64
|
$ curl https://vmpooler.example.com/api/v3/poolstat?pool=centos-6-x86_64
|
||||||
```
|
```
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
|
|
@ -687,7 +687,7 @@ Return codes
|
||||||
* 200 OK
|
* 200 OK
|
||||||
|
|
||||||
```
|
```
|
||||||
$ curl https://vmpooler.example.com/api/v2/totalrunning
|
$ curl https://vmpooler.example.com/api/v3/totalrunning
|
||||||
```
|
```
|
||||||
|
|
||||||
```json
|
```json
|
||||||
|
|
@ -709,7 +709,7 @@ Return codes
|
||||||
* 400 No configuration found
|
* 400 No configuration found
|
||||||
|
|
||||||
```
|
```
|
||||||
$ curl https://vmpooler.example.com/api/v2/config
|
$ curl https://vmpooler.example.com/api/v3/config
|
||||||
```
|
```
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
|
|
@ -753,7 +753,7 @@ Responses:
|
||||||
* 404 - An unknown error occurred
|
* 404 - An unknown error occurred
|
||||||
* 405 - The endpoint is disabled because experimental features are disabled
|
* 405 - The endpoint is disabled because experimental features are disabled
|
||||||
```
|
```
|
||||||
$ curl -X POST -H "Content-Type: application/json" -d '{"debian-7-i386":"2","debian-7-x86_64":"1"}' --url https://vmpooler.example.com/api/v2/config/poolsize
|
$ curl -X POST -H "Content-Type: application/json" -d '{"debian-7-i386":"2","debian-7-x86_64":"1"}' --url https://vmpooler.example.com/api/v3/config/poolsize
|
||||||
```
|
```
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
|
|
@ -773,7 +773,7 @@ Return codes:
|
||||||
* 405 - The endpoint is disabled because experimental features are disabled
|
* 405 - The endpoint is disabled because experimental features are disabled
|
||||||
|
|
||||||
```
|
```
|
||||||
$ curl -X DELETE -u jdoe --url vmpooler.example.com/api/v2/poolsize/almalinux-8-x86_64
|
$ curl -X DELETE -u jdoe --url vmpooler.example.com/api/v3/poolsize/almalinux-8-x86_64
|
||||||
```
|
```
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
|
|
@ -807,7 +807,7 @@ Responses:
|
||||||
* 404 - An unknown error occurred
|
* 404 - An unknown error occurred
|
||||||
* 405 - The endpoint is disabled because experimental features are disabled
|
* 405 - The endpoint is disabled because experimental features are disabled
|
||||||
```
|
```
|
||||||
$ curl -X POST -H "Content-Type: application/json" -d '{"debian-7-i386":"templates/debian-7-i386"}' --url https://vmpooler.example.com/api/v2/config/pooltemplate
|
$ curl -X POST -H "Content-Type: application/json" -d '{"debian-7-i386":"templates/debian-7-i386"}' --url https://vmpooler.example.com/api/v3/config/pooltemplate
|
||||||
```
|
```
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
|
|
@ -827,7 +827,7 @@ Return codes:
|
||||||
* 405 - The endpoint is disabled because experimental features are disabled
|
* 405 - The endpoint is disabled because experimental features are disabled
|
||||||
|
|
||||||
```
|
```
|
||||||
$ curl -X DELETE -u jdoe --url vmpooler.example.com/api/v2/pooltemplate/almalinux-8-x86_64
|
$ curl -X DELETE -u jdoe --url vmpooler.example.com/api/v3/pooltemplate/almalinux-8-x86_64
|
||||||
```
|
```
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
|
|
@ -855,7 +855,7 @@ Responses:
|
||||||
* 404 - An unknown error occurred
|
* 404 - An unknown error occurred
|
||||||
* 405 - The endpoint is disabled because experimental features are disabled
|
* 405 - The endpoint is disabled because experimental features are disabled
|
||||||
```
|
```
|
||||||
$ curl -X POST -H "Content-Type: application/json" -d '{"debian-7-i386":"1"}' --url https://vmpooler.example.com/api/v2/poolreset
|
$ curl -X POST -H "Content-Type: application/json" -d '{"debian-7-i386":"1"}' --url https://vmpooler.example.com/api/v3/poolreset
|
||||||
```
|
```
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
|
|
@ -883,7 +883,7 @@ Responses:
|
||||||
* 404 - A pool was requested, which is not available in the running configuration, or an unknown error occurred.
|
* 404 - A pool was requested, which is not available in the running configuration, or an unknown error occurred.
|
||||||
* 409 - A request of the matching ID has already been created
|
* 409 - A request of the matching ID has already been created
|
||||||
```
|
```
|
||||||
$ curl -X POST -H "Content-Type: application/json" -d '{"debian-7-i386":"4"}' --url https://vmpooler.example.com/api/v2/ondemandvm
|
$ curl -X POST -H "Content-Type: application/json" -d '{"debian-7-i386":"4"}' --url https://vmpooler.example.com/api/v3/ondemandvm
|
||||||
```
|
```
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
|
|
@ -905,7 +905,7 @@ Responses:
|
||||||
* 202 - The request is not ready yet
|
* 202 - The request is not ready yet
|
||||||
* 404 - The request can not be found, or an unknown error occurred
|
* 404 - The request can not be found, or an unknown error occurred
|
||||||
```
|
```
|
||||||
$ curl https://vmpooler.example.com/api/v2/ondemandvm/e3ff6271-d201-4f31-a315-d17f4e15863a
|
$ curl https://vmpooler.example.com/api/v3/ondemandvm/e3ff6271-d201-4f31-a315-d17f4e15863a
|
||||||
```
|
```
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
|
|
@ -945,7 +945,7 @@ Responses:
|
||||||
* 401 - No auth token provided, or provided auth token is not valid, and auth is enabled
|
* 401 - No auth token provided, or provided auth token is not valid, and auth is enabled
|
||||||
* 404 - The request can not be found, or an unknown error occurred.
|
* 404 - The request can not be found, or an unknown error occurred.
|
||||||
```
|
```
|
||||||
$ curl -X DELETE https://vmpooler.example.com/api/v2/ondemandvm/e3ff6271-d201-4f31-a315-d17f4e15863a
|
$ curl -X DELETE https://vmpooler.example.com/api/v3/ondemandvm/e3ff6271-d201-4f31-a315-d17f4e15863a
|
||||||
```
|
```
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
|
|
@ -3,7 +3,7 @@
|
||||||
module Vmpooler
|
module Vmpooler
|
||||||
class API < Sinatra::Base
|
class API < Sinatra::Base
|
||||||
# Load API components
|
# Load API components
|
||||||
%w[helpers dashboard reroute v2 request_logger healthcheck].each do |lib|
|
%w[helpers dashboard reroute v3 request_logger healthcheck].each do |lib|
|
||||||
require "vmpooler/api/#{lib}"
|
require "vmpooler/api/#{lib}"
|
||||||
end
|
end
|
||||||
# Load dashboard components
|
# Load dashboard components
|
||||||
|
|
@ -53,7 +53,7 @@ module Vmpooler
|
||||||
use Vmpooler::Dashboard
|
use Vmpooler::Dashboard
|
||||||
use Vmpooler::API::Dashboard
|
use Vmpooler::API::Dashboard
|
||||||
use Vmpooler::API::Reroute
|
use Vmpooler::API::Reroute
|
||||||
use Vmpooler::API::V2
|
use Vmpooler::API::V3
|
||||||
end
|
end
|
||||||
|
|
||||||
# Get thee started O WebServer
|
# Get thee started O WebServer
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load diff
|
|
@ -5,8 +5,8 @@ require 'vmpooler/dns'
|
||||||
|
|
||||||
module Vmpooler
|
module Vmpooler
|
||||||
class API
|
class API
|
||||||
class V2 < Vmpooler::API::V1
|
class V3 < Sinatra::Base
|
||||||
api_version = '2'
|
api_version = '3'
|
||||||
api_prefix = "/api/v#{api_version}"
|
api_prefix = "/api/v#{api_version}"
|
||||||
|
|
||||||
helpers do
|
helpers do
|
||||||
|
|
@ -54,7 +54,7 @@ module Vmpooler
|
||||||
end
|
end
|
||||||
|
|
||||||
def get_template_aliases(template)
|
def get_template_aliases(template)
|
||||||
tracer.in_span("Vmpooler::API::V2.#{__method__}") do
|
tracer.in_span("Vmpooler::API::V3.#{__method__}") do
|
||||||
result = []
|
result = []
|
||||||
aliases = Vmpooler::API.settings.config[:alias]
|
aliases = Vmpooler::API.settings.config[:alias]
|
||||||
if aliases
|
if aliases
|
||||||
|
|
@ -125,7 +125,7 @@ module Vmpooler
|
||||||
# what template was used, if successful. Otherwise the tuple contains.
|
# what template was used, if successful. Otherwise the tuple contains.
|
||||||
# nil values.
|
# nil values.
|
||||||
def fetch_single_vm(template)
|
def fetch_single_vm(template)
|
||||||
tracer.in_span("Vmpooler::API::V2.#{__method__}") do
|
tracer.in_span("Vmpooler::API::V3.#{__method__}") do
|
||||||
template_backends = [template]
|
template_backends = [template]
|
||||||
aliases = Vmpooler::API.settings.config[:alias]
|
aliases = Vmpooler::API.settings.config[:alias]
|
||||||
if aliases
|
if aliases
|
||||||
|
|
@ -175,7 +175,7 @@ module Vmpooler
|
||||||
end
|
end
|
||||||
|
|
||||||
def return_vm_to_ready_state(template, vm)
|
def return_vm_to_ready_state(template, vm)
|
||||||
tracer.in_span("Vmpooler::API::V2.#{__method__}") do
|
tracer.in_span("Vmpooler::API::V3.#{__method__}") do
|
||||||
backend.srem("vmpooler__migrating__#{template}", vm)
|
backend.srem("vmpooler__migrating__#{template}", vm)
|
||||||
backend.hdel("vmpooler__active__#{template}", vm)
|
backend.hdel("vmpooler__active__#{template}", vm)
|
||||||
backend.hdel("vmpooler__vm__#{vm}", 'checkout', 'token:token', 'token:user')
|
backend.hdel("vmpooler__vm__#{vm}", 'checkout', 'token:token', 'token:user')
|
||||||
|
|
@ -184,7 +184,7 @@ module Vmpooler
|
||||||
end
|
end
|
||||||
|
|
||||||
def account_for_starting_vm(template, vm)
|
def account_for_starting_vm(template, vm)
|
||||||
tracer.in_span("Vmpooler::API::V2.#{__method__}") do |span|
|
tracer.in_span("Vmpooler::API::V3.#{__method__}") do |span|
|
||||||
user = backend.hget("vmpooler__token__#{request.env['HTTP_X_AUTH_TOKEN']}", 'user')
|
user = backend.hget("vmpooler__token__#{request.env['HTTP_X_AUTH_TOKEN']}", 'user')
|
||||||
span.set_attribute('enduser.id', user)
|
span.set_attribute('enduser.id', user)
|
||||||
has_token_result = has_token?
|
has_token_result = has_token?
|
||||||
|
|
@ -204,7 +204,7 @@ module Vmpooler
|
||||||
end
|
end
|
||||||
|
|
||||||
def update_result_hosts(result, template, vm)
|
def update_result_hosts(result, template, vm)
|
||||||
tracer.in_span("Vmpooler::API::V2.#{__method__}") do
|
tracer.in_span("Vmpooler::API::V3.#{__method__}") do
|
||||||
result[template] ||= {}
|
result[template] ||= {}
|
||||||
if result[template]['hostname']
|
if result[template]['hostname']
|
||||||
result[template]['hostname'] = Array(result[template]['hostname'])
|
result[template]['hostname'] = Array(result[template]['hostname'])
|
||||||
|
|
@ -215,13 +215,8 @@ module Vmpooler
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# The domain in the result body will be set to the one associated with the
|
|
||||||
# last vm added. The part of the response is only being retained for
|
|
||||||
# backwards compatibility as the hostnames are now fqdn's instead of bare
|
|
||||||
# hostnames. This change is a result of now being able to specify a domain
|
|
||||||
# per pool.
|
|
||||||
def atomically_allocate_vms(payload)
|
def atomically_allocate_vms(payload)
|
||||||
tracer.in_span("Vmpooler::API::V2.#{__method__}") do |span|
|
tracer.in_span("Vmpooler::API::V3.#{__method__}") do |span|
|
||||||
result = { 'ok' => false }
|
result = { 'ok' => false }
|
||||||
failed = false
|
failed = false
|
||||||
vms = [] # vmpool, vmname, vmtemplate
|
vms = [] # vmpool, vmname, vmtemplate
|
||||||
|
|
@ -249,7 +244,7 @@ module Vmpooler
|
||||||
return_vm_to_ready_state(vmpool, vmname)
|
return_vm_to_ready_state(vmpool, vmname)
|
||||||
end
|
end
|
||||||
span.add_event('error', attributes: {
|
span.add_event('error', attributes: {
|
||||||
'error.type' => 'Vmpooler::API::V2.atomically_allocate_vms',
|
'error.type' => 'Vmpooler::API::V3.atomically_allocate_vms',
|
||||||
'error.message' => '503 due to failing to allocate one or more vms'
|
'error.message' => '503 due to failing to allocate one or more vms'
|
||||||
})
|
})
|
||||||
status 503
|
status 503
|
||||||
|
|
@ -272,7 +267,7 @@ module Vmpooler
|
||||||
end
|
end
|
||||||
|
|
||||||
def component_to_test(match, labels_string)
|
def component_to_test(match, labels_string)
|
||||||
tracer.in_span("Vmpooler::API::V2.#{__method__}") do
|
tracer.in_span("Vmpooler::API::V3.#{__method__}") do
|
||||||
return if labels_string.nil?
|
return if labels_string.nil?
|
||||||
|
|
||||||
labels_string_parts = labels_string.split(',')
|
labels_string_parts = labels_string.split(',')
|
||||||
|
|
@ -286,7 +281,7 @@ module Vmpooler
|
||||||
end
|
end
|
||||||
|
|
||||||
def update_user_metrics(operation, vmname)
|
def update_user_metrics(operation, vmname)
|
||||||
tracer.in_span("Vmpooler::API::V2.#{__method__}") do |span|
|
tracer.in_span("Vmpooler::API::V3.#{__method__}") do |span|
|
||||||
begin
|
begin
|
||||||
backend.multi
|
backend.multi
|
||||||
backend.hget("vmpooler__vm__#{vmname}", 'tag:jenkins_build_url')
|
backend.hget("vmpooler__vm__#{vmname}", 'tag:jenkins_build_url')
|
||||||
|
|
@ -336,7 +331,7 @@ module Vmpooler
|
||||||
end
|
end
|
||||||
|
|
||||||
def reset_pool_size(poolname)
|
def reset_pool_size(poolname)
|
||||||
tracer.in_span("Vmpooler::API::V2.#{__method__}") do
|
tracer.in_span("Vmpooler::API::V3.#{__method__}") do
|
||||||
result = { 'ok' => false }
|
result = { 'ok' => false }
|
||||||
|
|
||||||
pool_index = pool_index(pools)
|
pool_index = pool_index(pools)
|
||||||
|
|
@ -364,7 +359,7 @@ module Vmpooler
|
||||||
end
|
end
|
||||||
|
|
||||||
def update_pool_size(payload)
|
def update_pool_size(payload)
|
||||||
tracer.in_span("Vmpooler::API::V2.#{__method__}") do
|
tracer.in_span("Vmpooler::API::V3.#{__method__}") do
|
||||||
result = { 'ok' => false }
|
result = { 'ok' => false }
|
||||||
|
|
||||||
pool_index = pool_index(pools)
|
pool_index = pool_index(pools)
|
||||||
|
|
@ -386,7 +381,7 @@ module Vmpooler
|
||||||
end
|
end
|
||||||
|
|
||||||
def reset_pool_template(poolname)
|
def reset_pool_template(poolname)
|
||||||
tracer.in_span("Vmpooler::API::V2.#{__method__}") do
|
tracer.in_span("Vmpooler::API::V3.#{__method__}") do
|
||||||
result = { 'ok' => false }
|
result = { 'ok' => false }
|
||||||
|
|
||||||
pool_index_live = pool_index(pools)
|
pool_index_live = pool_index(pools)
|
||||||
|
|
@ -415,7 +410,7 @@ module Vmpooler
|
||||||
end
|
end
|
||||||
|
|
||||||
def update_pool_template(payload)
|
def update_pool_template(payload)
|
||||||
tracer.in_span("Vmpooler::API::V2.#{__method__}") do
|
tracer.in_span("Vmpooler::API::V3.#{__method__}") do
|
||||||
result = { 'ok' => false }
|
result = { 'ok' => false }
|
||||||
|
|
||||||
pool_index = pool_index(pools)
|
pool_index = pool_index(pools)
|
||||||
|
|
@ -437,7 +432,7 @@ module Vmpooler
|
||||||
end
|
end
|
||||||
|
|
||||||
def reset_pool(payload)
|
def reset_pool(payload)
|
||||||
tracer.in_span("Vmpooler::API::V2.#{__method__}") do
|
tracer.in_span("Vmpooler::API::V3.#{__method__}") do
|
||||||
result = { 'ok' => false }
|
result = { 'ok' => false }
|
||||||
|
|
||||||
payload.each do |poolname, _count|
|
payload.each do |poolname, _count|
|
||||||
|
|
@ -450,7 +445,7 @@ module Vmpooler
|
||||||
end
|
end
|
||||||
|
|
||||||
def update_clone_target(payload)
|
def update_clone_target(payload)
|
||||||
tracer.in_span("Vmpooler::API::V2.#{__method__}") do
|
tracer.in_span("Vmpooler::API::V3.#{__method__}") do
|
||||||
result = { 'ok' => false }
|
result = { 'ok' => false }
|
||||||
|
|
||||||
pool_index = pool_index(pools)
|
pool_index = pool_index(pools)
|
||||||
|
|
@ -472,7 +467,7 @@ module Vmpooler
|
||||||
end
|
end
|
||||||
|
|
||||||
def sync_pool_templates
|
def sync_pool_templates
|
||||||
tracer.in_span("Vmpooler::API::V2.#{__method__}") do
|
tracer.in_span("Vmpooler::API::V3.#{__method__}") do
|
||||||
pool_index = pool_index(pools)
|
pool_index = pool_index(pools)
|
||||||
template_configs = backend.hgetall('vmpooler__config__template')
|
template_configs = backend.hgetall('vmpooler__config__template')
|
||||||
template_configs&.each do |poolname, template|
|
template_configs&.each do |poolname, template|
|
||||||
|
|
@ -484,7 +479,7 @@ module Vmpooler
|
||||||
end
|
end
|
||||||
|
|
||||||
def sync_pool_sizes
|
def sync_pool_sizes
|
||||||
tracer.in_span("Vmpooler::API::V2.#{__method__}") do
|
tracer.in_span("Vmpooler::API::V3.#{__method__}") do
|
||||||
pool_index = pool_index(pools)
|
pool_index = pool_index(pools)
|
||||||
poolsize_configs = backend.hgetall('vmpooler__config__poolsize')
|
poolsize_configs = backend.hgetall('vmpooler__config__poolsize')
|
||||||
poolsize_configs&.each do |poolname, size|
|
poolsize_configs&.each do |poolname, size|
|
||||||
|
|
@ -496,7 +491,7 @@ module Vmpooler
|
||||||
end
|
end
|
||||||
|
|
||||||
def sync_clone_targets
|
def sync_clone_targets
|
||||||
tracer.in_span("Vmpooler::API::V2.#{__method__}") do
|
tracer.in_span("Vmpooler::API::V3.#{__method__}") do
|
||||||
pool_index = pool_index(pools)
|
pool_index = pool_index(pools)
|
||||||
clone_target_configs = backend.hgetall('vmpooler__config__clone_target')
|
clone_target_configs = backend.hgetall('vmpooler__config__clone_target')
|
||||||
clone_target_configs&.each do |poolname, clone_target|
|
clone_target_configs&.each do |poolname, clone_target|
|
||||||
|
|
@ -508,7 +503,7 @@ module Vmpooler
|
||||||
end
|
end
|
||||||
|
|
||||||
def too_many_requested?(payload)
|
def too_many_requested?(payload)
|
||||||
tracer.in_span("Vmpooler::API::V2.#{__method__}") do
|
tracer.in_span("Vmpooler::API::V3.#{__method__}") do
|
||||||
payload&.each do |poolname, count|
|
payload&.each do |poolname, count|
|
||||||
next unless count.to_i > config['max_ondemand_instances_per_request']
|
next unless count.to_i > config['max_ondemand_instances_per_request']
|
||||||
|
|
||||||
|
|
@ -520,7 +515,7 @@ module Vmpooler
|
||||||
end
|
end
|
||||||
|
|
||||||
def generate_ondemand_request(payload)
|
def generate_ondemand_request(payload)
|
||||||
tracer.in_span("Vmpooler::API::V2.#{__method__}") do |span|
|
tracer.in_span("Vmpooler::API::V3.#{__method__}") do |span|
|
||||||
result = { 'ok': false }
|
result = { 'ok': false }
|
||||||
|
|
||||||
requested_instances = payload.reject { |k, _v| k == 'request_id' }
|
requested_instances = payload.reject { |k, _v| k == 'request_id' }
|
||||||
|
|
@ -529,7 +524,7 @@ module Vmpooler
|
||||||
result['message'] = e_message
|
result['message'] = e_message
|
||||||
status 403
|
status 403
|
||||||
span.add_event('error', attributes: {
|
span.add_event('error', attributes: {
|
||||||
'error.type' => 'Vmpooler::API::V2.generate_ondemand_request',
|
'error.type' => 'Vmpooler::API::V3.generate_ondemand_request',
|
||||||
'error.message' => "403 due to #{e_message}"
|
'error.message' => "403 due to #{e_message}"
|
||||||
})
|
})
|
||||||
return result
|
return result
|
||||||
|
|
@ -546,7 +541,7 @@ module Vmpooler
|
||||||
result['message'] = e_message
|
result['message'] = e_message
|
||||||
status 409
|
status 409
|
||||||
span.add_event('error', attributes: {
|
span.add_event('error', attributes: {
|
||||||
'error.type' => 'Vmpooler::API::V2.generate_ondemand_request',
|
'error.type' => 'Vmpooler::API::V3.generate_ondemand_request',
|
||||||
'error.message' => "409 due to #{e_message}"
|
'error.message' => "409 due to #{e_message}"
|
||||||
})
|
})
|
||||||
metrics.increment('ondemandrequest_generate.duplicaterequests')
|
metrics.increment('ondemandrequest_generate.duplicaterequests')
|
||||||
|
|
@ -588,6 +583,505 @@ module Vmpooler
|
||||||
redirect to('/dashboard/')
|
redirect to('/dashboard/')
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Provide run-time statistics
|
||||||
|
#
|
||||||
|
# Example:
|
||||||
|
#
|
||||||
|
# {
|
||||||
|
# "boot": {
|
||||||
|
# "duration": {
|
||||||
|
# "average": 163.6,
|
||||||
|
# "min": 65.49,
|
||||||
|
# "max": 830.07,
|
||||||
|
# "total": 247744.71000000002
|
||||||
|
# },
|
||||||
|
# "count": {
|
||||||
|
# "total": 1514
|
||||||
|
# }
|
||||||
|
# },
|
||||||
|
# "capacity": {
|
||||||
|
# "current": 968,
|
||||||
|
# "total": 975,
|
||||||
|
# "percent": 99.3
|
||||||
|
# },
|
||||||
|
# "clone": {
|
||||||
|
# "duration": {
|
||||||
|
# "average": 17.0,
|
||||||
|
# "min": 4.66,
|
||||||
|
# "max": 637.96,
|
||||||
|
# "total": 25634.15
|
||||||
|
# },
|
||||||
|
# "count": {
|
||||||
|
# "total": 1507
|
||||||
|
# }
|
||||||
|
# },
|
||||||
|
# "queue": {
|
||||||
|
# "pending": 12,
|
||||||
|
# "cloning": 0,
|
||||||
|
# "booting": 12,
|
||||||
|
# "ready": 968,
|
||||||
|
# "running": 367,
|
||||||
|
# "completed": 0,
|
||||||
|
# "total": 1347
|
||||||
|
# },
|
||||||
|
# "pools": {
|
||||||
|
# "ready": 100,
|
||||||
|
# "running": 120,
|
||||||
|
# "pending": 5,
|
||||||
|
# "max": 250,
|
||||||
|
# }
|
||||||
|
# "status": {
|
||||||
|
# "ok": true,
|
||||||
|
# "message": "Battle station fully armed and operational.",
|
||||||
|
# "empty": [ # NOTE: would not have 'ok: true' w/ "empty" pools
|
||||||
|
# "redhat-7-x86_64",
|
||||||
|
# "ubuntu-1404-i386"
|
||||||
|
# ],
|
||||||
|
# "uptime": 179585.9
|
||||||
|
# }
|
||||||
|
#
|
||||||
|
# If the query parameter 'view' is provided, it will be used to select which top level
|
||||||
|
# element to compute and return. Select them by specifying them in a comma separated list.
|
||||||
|
# For example /status?view=capacity,boot
|
||||||
|
# would return only the "capacity" and "boot" statistics. "status" is always returned
|
||||||
|
|
||||||
|
get "#{api_prefix}/status/?" do
|
||||||
|
content_type :json
|
||||||
|
|
||||||
|
if params[:view]
|
||||||
|
views = params[:view].split(",")
|
||||||
|
end
|
||||||
|
|
||||||
|
result = {
|
||||||
|
status: {
|
||||||
|
ok: true,
|
||||||
|
message: 'Battle station fully armed and operational.'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sync_pool_sizes
|
||||||
|
|
||||||
|
result[:capacity] = get_capacity_metrics(pools, backend) unless views and not views.include?("capacity")
|
||||||
|
result[:queue] = get_queue_metrics(pools, backend) unless views and not views.include?("queue")
|
||||||
|
result[:clone] = get_task_metrics(backend, 'clone', Date.today.to_s) unless views and not views.include?("clone")
|
||||||
|
result[:boot] = get_task_metrics(backend, 'boot', Date.today.to_s) unless views and not views.include?("boot")
|
||||||
|
|
||||||
|
# Check for empty pools
|
||||||
|
result[:pools] = {} unless views and not views.include?("pools")
|
||||||
|
ready_hash = get_list_across_pools_redis_scard(pools, 'vmpooler__ready__', backend)
|
||||||
|
running_hash = get_list_across_pools_redis_scard(pools, 'vmpooler__running__', backend)
|
||||||
|
pending_hash = get_list_across_pools_redis_scard(pools, 'vmpooler__pending__', backend)
|
||||||
|
lastBoot_hash = get_list_across_pools_redis_hget(pools, 'vmpooler__lastboot', backend)
|
||||||
|
|
||||||
|
unless views and not views.include?("pools")
|
||||||
|
pools.each do |pool|
|
||||||
|
# REMIND: move this out of the API and into the back-end
|
||||||
|
ready = ready_hash[pool['name']]
|
||||||
|
running = running_hash[pool['name']]
|
||||||
|
pending = pending_hash[pool['name']]
|
||||||
|
max = pool['size']
|
||||||
|
lastBoot = lastBoot_hash[pool['name']]
|
||||||
|
aka = pool['alias']
|
||||||
|
|
||||||
|
result[:pools][pool['name']] = {
|
||||||
|
ready: ready,
|
||||||
|
running: running,
|
||||||
|
pending: pending,
|
||||||
|
max: max,
|
||||||
|
lastBoot: lastBoot
|
||||||
|
}
|
||||||
|
|
||||||
|
if aka
|
||||||
|
result[:pools][pool['name']][:alias] = aka
|
||||||
|
end
|
||||||
|
|
||||||
|
# for backwards compatibility, include separate "empty" stats in "status" block
|
||||||
|
if ready == 0 && max != 0
|
||||||
|
result[:status][:empty] ||= []
|
||||||
|
result[:status][:empty].push(pool['name'])
|
||||||
|
|
||||||
|
result[:status][:ok] = false
|
||||||
|
result[:status][:message] = "Found #{result[:status][:empty].length} empty pools."
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
result[:status][:uptime] = (Time.now - Vmpooler::API.settings.config[:uptime]).round(1) if Vmpooler::API.settings.config[:uptime]
|
||||||
|
|
||||||
|
JSON.pretty_generate(Hash[result.sort_by { |k, _v| k }])
|
||||||
|
end
|
||||||
|
|
||||||
|
# request statistics for specific pools by passing parameter 'pool'
|
||||||
|
# with a coma separated list of pools we want to query ?pool=ABC,DEF
|
||||||
|
# returns the ready, max numbers and the aliases (if set)
|
||||||
|
get "#{api_prefix}/poolstat/?" do
|
||||||
|
content_type :json
|
||||||
|
|
||||||
|
result = {}
|
||||||
|
|
||||||
|
poolscopy = []
|
||||||
|
|
||||||
|
if params[:pool]
|
||||||
|
subpool = params[:pool].split(",")
|
||||||
|
poolscopy = pools.select do |p|
|
||||||
|
if subpool.include?(p['name'])
|
||||||
|
true
|
||||||
|
elsif !p['alias'].nil?
|
||||||
|
if p['alias'].instance_of?(Array)
|
||||||
|
(p['alias'] & subpool).any?
|
||||||
|
elsif p['alias'].instance_of?(String)
|
||||||
|
subpool.include?(p['alias'])
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
result[:pools] = {}
|
||||||
|
|
||||||
|
poolscopy.each do |pool|
|
||||||
|
result[:pools][pool['name']] = {}
|
||||||
|
|
||||||
|
max = pool['size']
|
||||||
|
aka = pool['alias']
|
||||||
|
|
||||||
|
result[:pools][pool['name']][:max] = max
|
||||||
|
|
||||||
|
if aka
|
||||||
|
result[:pools][pool['name']][:alias] = aka
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
ready_hash = get_list_across_pools_redis_scard(poolscopy, 'vmpooler__ready__', backend)
|
||||||
|
|
||||||
|
ready_hash.each { |k, v| result[:pools][k][:ready] = v }
|
||||||
|
|
||||||
|
JSON.pretty_generate(Hash[result.sort_by { |k, _v| k }])
|
||||||
|
end
|
||||||
|
|
||||||
|
# requests the total number of running VMs
|
||||||
|
get "#{api_prefix}/totalrunning/?" do
|
||||||
|
content_type :json
|
||||||
|
queue = {
|
||||||
|
running: 0
|
||||||
|
}
|
||||||
|
|
||||||
|
queue[:running] = get_total_across_pools_redis_scard(pools, 'vmpooler__running__', backend)
|
||||||
|
|
||||||
|
JSON.pretty_generate(queue)
|
||||||
|
end
|
||||||
|
|
||||||
|
get "#{api_prefix}/summary/?" do
|
||||||
|
content_type :json
|
||||||
|
|
||||||
|
result = {
|
||||||
|
daily: []
|
||||||
|
}
|
||||||
|
|
||||||
|
from_param = params[:from] || Date.today.to_s
|
||||||
|
to_param = params[:to] || Date.today.to_s
|
||||||
|
|
||||||
|
# Validate date formats
|
||||||
|
[from_param, to_param].each do |param|
|
||||||
|
if !validate_date_str(param.to_s)
|
||||||
|
halt 400, "Invalid date format '#{param}', must match YYYY-MM-DD."
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
from_date, to_date = Date.parse(from_param), Date.parse(to_param)
|
||||||
|
|
||||||
|
if to_date < from_date
|
||||||
|
halt 400, 'Date range is invalid, \'to\' cannot come before \'from\'.'
|
||||||
|
elsif from_date > Date.today
|
||||||
|
halt 400, 'Date range is invalid, \'from\' must be in the past.'
|
||||||
|
end
|
||||||
|
|
||||||
|
boot = get_task_summary(backend, 'boot', from_date, to_date, :bypool => true)
|
||||||
|
clone = get_task_summary(backend, 'clone', from_date, to_date, :bypool => true)
|
||||||
|
tag = get_tag_summary(backend, from_date, to_date)
|
||||||
|
|
||||||
|
result[:boot] = boot[:boot]
|
||||||
|
result[:clone] = clone[:clone]
|
||||||
|
result[:tag] = tag[:tag]
|
||||||
|
|
||||||
|
daily = {}
|
||||||
|
|
||||||
|
boot[:daily].each do |day|
|
||||||
|
daily[day[:date]] ||= {}
|
||||||
|
daily[day[:date]][:boot] = day[:boot]
|
||||||
|
end
|
||||||
|
|
||||||
|
clone[:daily].each do |day|
|
||||||
|
daily[day[:date]] ||= {}
|
||||||
|
daily[day[:date]][:clone] = day[:clone]
|
||||||
|
end
|
||||||
|
|
||||||
|
tag[:daily].each do |day|
|
||||||
|
daily[day[:date]] ||= {}
|
||||||
|
daily[day[:date]][:tag] = day[:tag]
|
||||||
|
end
|
||||||
|
|
||||||
|
daily.each_key do |day|
|
||||||
|
result[:daily].push({
|
||||||
|
date: day,
|
||||||
|
boot: daily[day][:boot],
|
||||||
|
clone: daily[day][:clone],
|
||||||
|
tag: daily[day][:tag]
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
|
JSON.pretty_generate(result)
|
||||||
|
end
|
||||||
|
|
||||||
|
get "#{api_prefix}/summary/:route/?:key?/?" do
|
||||||
|
content_type :json
|
||||||
|
|
||||||
|
result = {}
|
||||||
|
|
||||||
|
from_param = params[:from] || Date.today.to_s
|
||||||
|
to_param = params[:to] || Date.today.to_s
|
||||||
|
|
||||||
|
# Validate date formats
|
||||||
|
[from_param, to_param].each do |param|
|
||||||
|
if !validate_date_str(param.to_s)
|
||||||
|
halt 400, "Invalid date format '#{param}', must match YYYY-MM-DD."
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
from_date, to_date = Date.parse(from_param), Date.parse(to_param)
|
||||||
|
|
||||||
|
if to_date < from_date
|
||||||
|
halt 400, 'Date range is invalid, \'to\' cannot come before \'from\'.'
|
||||||
|
elsif from_date > Date.today
|
||||||
|
halt 400, 'Date range is invalid, \'from\' must be in the past.'
|
||||||
|
end
|
||||||
|
|
||||||
|
case params[:route]
|
||||||
|
when 'boot'
|
||||||
|
result = get_task_summary(backend, 'boot', from_date, to_date, :bypool => true, :only => params[:key])
|
||||||
|
when 'clone'
|
||||||
|
result = get_task_summary(backend, 'clone', from_date, to_date, :bypool => true, :only => params[:key])
|
||||||
|
when 'tag'
|
||||||
|
result = get_tag_summary(backend, from_date, to_date, :only => params[:key])
|
||||||
|
else
|
||||||
|
halt 404, JSON.pretty_generate({ 'ok' => false })
|
||||||
|
end
|
||||||
|
|
||||||
|
JSON.pretty_generate(result)
|
||||||
|
end
|
||||||
|
|
||||||
|
get "#{api_prefix}/token/?" do
|
||||||
|
content_type :json
|
||||||
|
|
||||||
|
status 404
|
||||||
|
result = { 'ok' => false }
|
||||||
|
|
||||||
|
if Vmpooler::API.settings.config[:auth]
|
||||||
|
status 401
|
||||||
|
|
||||||
|
need_auth!
|
||||||
|
|
||||||
|
backend.keys('vmpooler__token__*').each do |key|
|
||||||
|
data = backend.hgetall(key)
|
||||||
|
|
||||||
|
if data['user'] == Rack::Auth::Basic::Request.new(request.env).username
|
||||||
|
span = OpenTelemetry::Trace.current_span
|
||||||
|
span.set_attribute('enduser.id', data['user'])
|
||||||
|
token = key.split('__').last
|
||||||
|
|
||||||
|
result[token] ||= {}
|
||||||
|
|
||||||
|
result[token]['created'] = data['created']
|
||||||
|
result[token]['last'] = data['last'] || 'never'
|
||||||
|
|
||||||
|
result['ok'] = true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if result['ok']
|
||||||
|
status 200
|
||||||
|
else
|
||||||
|
status 404
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
JSON.pretty_generate(result)
|
||||||
|
end
|
||||||
|
|
||||||
|
get "#{api_prefix}/token/:token/?" do
|
||||||
|
content_type :json
|
||||||
|
|
||||||
|
status 404
|
||||||
|
result = { 'ok' => false }
|
||||||
|
|
||||||
|
if Vmpooler::API.settings.config[:auth]
|
||||||
|
token = backend.hgetall("vmpooler__token__#{params[:token]}")
|
||||||
|
|
||||||
|
if not token.nil? and not token.empty?
|
||||||
|
status 200
|
||||||
|
|
||||||
|
pools.each do |pool|
|
||||||
|
backend.smembers("vmpooler__running__#{pool['name']}").each do |vm|
|
||||||
|
if backend.hget("vmpooler__vm__#{vm}", 'token:token') == params[:token]
|
||||||
|
token['vms'] ||= {}
|
||||||
|
token['vms']['running'] ||= []
|
||||||
|
token['vms']['running'].push(vm)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
result = { 'ok' => true, params[:token] => token }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
JSON.pretty_generate(result)
|
||||||
|
end
|
||||||
|
|
||||||
|
delete "#{api_prefix}/token/:token/?" do
|
||||||
|
content_type :json
|
||||||
|
|
||||||
|
status 404
|
||||||
|
result = { 'ok' => false }
|
||||||
|
|
||||||
|
if Vmpooler::API.settings.config[:auth]
|
||||||
|
status 401
|
||||||
|
|
||||||
|
need_auth!
|
||||||
|
|
||||||
|
if backend.del("vmpooler__token__#{params[:token]}").to_i > 0
|
||||||
|
status 200
|
||||||
|
result['ok'] = true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
JSON.pretty_generate(result)
|
||||||
|
end
|
||||||
|
|
||||||
|
post "#{api_prefix}/token" do
|
||||||
|
content_type :json
|
||||||
|
|
||||||
|
status 404
|
||||||
|
result = { 'ok' => false }
|
||||||
|
|
||||||
|
if Vmpooler::API.settings.config[:auth]
|
||||||
|
status 401
|
||||||
|
|
||||||
|
need_auth!
|
||||||
|
|
||||||
|
o = [('a'..'z'), ('0'..'9')].map(&:to_a).flatten
|
||||||
|
result['token'] = o[rand(25)] + (0...31).map { o[rand(o.length)] }.join
|
||||||
|
|
||||||
|
backend.hset("vmpooler__token__#{result['token']}", 'user', @auth.username)
|
||||||
|
backend.hset("vmpooler__token__#{result['token']}", 'created', Time.now)
|
||||||
|
span = OpenTelemetry::Trace.current_span
|
||||||
|
span.set_attribute('enduser.id', @auth.username)
|
||||||
|
|
||||||
|
status 200
|
||||||
|
result['ok'] = true
|
||||||
|
end
|
||||||
|
|
||||||
|
JSON.pretty_generate(result)
|
||||||
|
end
|
||||||
|
|
||||||
|
get "#{api_prefix}/vm/?" do
|
||||||
|
content_type :json
|
||||||
|
|
||||||
|
result = []
|
||||||
|
|
||||||
|
pools.each do |pool|
|
||||||
|
result.push(pool['name'])
|
||||||
|
end
|
||||||
|
|
||||||
|
JSON.pretty_generate(result)
|
||||||
|
end
|
||||||
|
|
||||||
|
post "#{api_prefix}/ondemandvm/?" do
|
||||||
|
content_type :json
|
||||||
|
metrics.increment('http_requests_vm_total.post.ondemand.requestid')
|
||||||
|
|
||||||
|
need_token! if Vmpooler::API.settings.config[:auth]
|
||||||
|
|
||||||
|
result = { 'ok' => false }
|
||||||
|
|
||||||
|
begin
|
||||||
|
payload = JSON.parse(request.body.read)
|
||||||
|
|
||||||
|
if payload
|
||||||
|
invalid = invalid_templates(payload.reject { |k, _v| k == 'request_id' })
|
||||||
|
if invalid.empty?
|
||||||
|
result = generate_ondemand_request(payload)
|
||||||
|
else
|
||||||
|
result[:bad_templates] = invalid
|
||||||
|
invalid.each do |bad_template|
|
||||||
|
metrics.increment("ondemandrequest_fail.invalid.#{bad_template}")
|
||||||
|
end
|
||||||
|
status 404
|
||||||
|
end
|
||||||
|
else
|
||||||
|
metrics.increment('ondemandrequest_fail.invalid.unknown')
|
||||||
|
status 404
|
||||||
|
end
|
||||||
|
rescue JSON::ParserError
|
||||||
|
span = OpenTelemetry::Trace.current_span
|
||||||
|
span.status = OpenTelemetry::Trace::Status.error('JSON payload could not be parsed')
|
||||||
|
status 400
|
||||||
|
result = {
|
||||||
|
'ok' => false,
|
||||||
|
'message' => 'JSON payload could not be parsed'
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
JSON.pretty_generate(result)
|
||||||
|
end
|
||||||
|
|
||||||
|
post "#{api_prefix}/ondemandvm/:template/?" do
|
||||||
|
content_type :json
|
||||||
|
result = { 'ok' => false }
|
||||||
|
metrics.increment('http_requests_vm_total.delete.ondemand.template')
|
||||||
|
|
||||||
|
need_token! if Vmpooler::API.settings.config[:auth]
|
||||||
|
|
||||||
|
payload = extract_templates_from_query_params(params[:template])
|
||||||
|
|
||||||
|
if payload
|
||||||
|
invalid = invalid_templates(payload.reject { |k, _v| k == 'request_id' })
|
||||||
|
if invalid.empty?
|
||||||
|
result = generate_ondemand_request(payload)
|
||||||
|
else
|
||||||
|
result[:bad_templates] = invalid
|
||||||
|
invalid.each do |bad_template|
|
||||||
|
metrics.increment("ondemandrequest_fail.invalid.#{bad_template}")
|
||||||
|
end
|
||||||
|
status 404
|
||||||
|
end
|
||||||
|
else
|
||||||
|
metrics.increment('ondemandrequest_fail.invalid.unknown')
|
||||||
|
status 404
|
||||||
|
end
|
||||||
|
|
||||||
|
JSON.pretty_generate(result)
|
||||||
|
end
|
||||||
|
|
||||||
|
get "#{api_prefix}/ondemandvm/:requestid/?" do
|
||||||
|
content_type :json
|
||||||
|
metrics.increment('http_requests_vm_total.get.ondemand.request')
|
||||||
|
|
||||||
|
status 404
|
||||||
|
result = check_ondemand_request(params[:requestid])
|
||||||
|
|
||||||
|
JSON.pretty_generate(result)
|
||||||
|
end
|
||||||
|
|
||||||
|
delete "#{api_prefix}/ondemandvm/:requestid/?" do
|
||||||
|
content_type :json
|
||||||
|
need_token! if Vmpooler::API.settings.config[:auth]
|
||||||
|
metrics.increment('http_requests_vm_total.delete.ondemand.request')
|
||||||
|
|
||||||
|
status 404
|
||||||
|
result = delete_ondemand_request(params[:requestid])
|
||||||
|
|
||||||
|
JSON.pretty_generate(result)
|
||||||
|
end
|
||||||
|
|
||||||
post "#{api_prefix}/vm/?" do
|
post "#{api_prefix}/vm/?" do
|
||||||
content_type :json
|
content_type :json
|
||||||
result = { 'ok' => false }
|
result = { 'ok' => false }
|
||||||
|
|
@ -614,7 +1108,7 @@ module Vmpooler
|
||||||
end
|
end
|
||||||
|
|
||||||
def extract_templates_from_query_params(params)
|
def extract_templates_from_query_params(params)
|
||||||
tracer.in_span("Vmpooler::API::V2.#{__method__}") do
|
tracer.in_span("Vmpooler::API::V3.#{__method__}") do
|
||||||
payload = {}
|
payload = {}
|
||||||
|
|
||||||
params.split('+').each do |template|
|
params.split('+').each do |template|
|
||||||
|
|
@ -627,7 +1121,7 @@ module Vmpooler
|
||||||
end
|
end
|
||||||
|
|
||||||
def invalid_templates(payload)
|
def invalid_templates(payload)
|
||||||
tracer.in_span("Vmpooler::API::V2.#{__method__}") do
|
tracer.in_span("Vmpooler::API::V3.#{__method__}") do
|
||||||
invalid = []
|
invalid = []
|
||||||
payload.keys.each do |template|
|
payload.keys.each do |template|
|
||||||
invalid << template unless pool_exists?(template)
|
invalid << template unless pool_exists?(template)
|
||||||
|
|
@ -637,7 +1131,7 @@ module Vmpooler
|
||||||
end
|
end
|
||||||
|
|
||||||
def invalid_template_or_size(payload)
|
def invalid_template_or_size(payload)
|
||||||
tracer.in_span("Vmpooler::API::V2.#{__method__}") do
|
tracer.in_span("Vmpooler::API::V3.#{__method__}") do
|
||||||
invalid = []
|
invalid = []
|
||||||
payload.each do |pool, size|
|
payload.each do |pool, size|
|
||||||
invalid << pool unless pool_exists?(pool)
|
invalid << pool unless pool_exists?(pool)
|
||||||
|
|
@ -652,7 +1146,7 @@ module Vmpooler
|
||||||
end
|
end
|
||||||
|
|
||||||
def invalid_template_or_path(payload)
|
def invalid_template_or_path(payload)
|
||||||
tracer.in_span("Vmpooler::API::V2.#{__method__}") do
|
tracer.in_span("Vmpooler::API::V3.#{__method__}") do
|
||||||
invalid = []
|
invalid = []
|
||||||
payload.each do |pool, template|
|
payload.each do |pool, template|
|
||||||
invalid << pool unless pool_exists?(pool)
|
invalid << pool unless pool_exists?(pool)
|
||||||
|
|
@ -665,7 +1159,7 @@ module Vmpooler
|
||||||
end
|
end
|
||||||
|
|
||||||
def invalid_pool(payload)
|
def invalid_pool(payload)
|
||||||
tracer.in_span("Vmpooler::API::V2.#{__method__}") do
|
tracer.in_span("Vmpooler::API::V3.#{__method__}") do
|
||||||
invalid = []
|
invalid = []
|
||||||
payload.each do |pool, _clone_target|
|
payload.each do |pool, _clone_target|
|
||||||
invalid << pool unless pool_exists?(pool)
|
invalid << pool unless pool_exists?(pool)
|
||||||
|
|
@ -675,7 +1169,7 @@ module Vmpooler
|
||||||
end
|
end
|
||||||
|
|
||||||
def delete_ondemand_request(request_id)
|
def delete_ondemand_request(request_id)
|
||||||
tracer.in_span("Vmpooler::API::V2.#{__method__}") do |span|
|
tracer.in_span("Vmpooler::API::V3.#{__method__}") do |span|
|
||||||
span.set_attribute('vmpooler.request_id', request_id)
|
span.set_attribute('vmpooler.request_id', request_id)
|
||||||
result = { 'ok' => false }
|
result = { 'ok' => false }
|
||||||
|
|
||||||
|
|
@ -684,7 +1178,7 @@ module Vmpooler
|
||||||
e_message = "no request found for request_id '#{request_id}'"
|
e_message = "no request found for request_id '#{request_id}'"
|
||||||
result['message'] = e_message
|
result['message'] = e_message
|
||||||
span.add_event('error', attributes: {
|
span.add_event('error', attributes: {
|
||||||
'error.type' => 'Vmpooler::API::V2.delete_ondemand_request',
|
'error.type' => 'Vmpooler::API::V3.delete_ondemand_request',
|
||||||
'error.message' => e_message
|
'error.message' => e_message
|
||||||
})
|
})
|
||||||
return result
|
return result
|
||||||
|
|
@ -810,85 +1304,8 @@ module Vmpooler
|
||||||
JSON.pretty_generate(result)
|
JSON.pretty_generate(result)
|
||||||
end
|
end
|
||||||
|
|
||||||
post "#{api_prefix}/ondemandvm/?" do
|
|
||||||
content_type :json
|
|
||||||
metrics.increment('http_requests_vm_total.post.ondemand.requestid')
|
|
||||||
|
|
||||||
need_token! if Vmpooler::API.settings.config[:auth]
|
|
||||||
|
|
||||||
result = { 'ok' => false }
|
|
||||||
|
|
||||||
begin
|
|
||||||
payload = JSON.parse(request.body.read)
|
|
||||||
|
|
||||||
if payload
|
|
||||||
invalid = invalid_templates(payload.reject { |k, _v| k == 'request_id' })
|
|
||||||
if invalid.empty?
|
|
||||||
result = generate_ondemand_request(payload)
|
|
||||||
else
|
|
||||||
result[:bad_templates] = invalid
|
|
||||||
invalid.each do |bad_template|
|
|
||||||
metrics.increment("ondemandrequest_fail.invalid.#{bad_template}")
|
|
||||||
end
|
|
||||||
status 404
|
|
||||||
end
|
|
||||||
else
|
|
||||||
metrics.increment('ondemandrequest_fail.invalid.unknown')
|
|
||||||
status 404
|
|
||||||
end
|
|
||||||
rescue JSON::ParserError
|
|
||||||
span = OpenTelemetry::Trace.current_span
|
|
||||||
span.status = OpenTelemetry::Trace::Status.error('JSON payload could not be parsed')
|
|
||||||
status 400
|
|
||||||
result = {
|
|
||||||
'ok' => false,
|
|
||||||
'message' => 'JSON payload could not be parsed'
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
JSON.pretty_generate(result)
|
|
||||||
end
|
|
||||||
|
|
||||||
post "#{api_prefix}/ondemandvm/:template/?" do
|
|
||||||
content_type :json
|
|
||||||
result = { 'ok' => false }
|
|
||||||
metrics.increment('http_requests_vm_total.delete.ondemand.template')
|
|
||||||
|
|
||||||
need_token! if Vmpooler::API.settings.config[:auth]
|
|
||||||
|
|
||||||
payload = extract_templates_from_query_params(params[:template])
|
|
||||||
|
|
||||||
if payload
|
|
||||||
invalid = invalid_templates(payload.reject { |k, _v| k == 'request_id' })
|
|
||||||
if invalid.empty?
|
|
||||||
result = generate_ondemand_request(payload)
|
|
||||||
else
|
|
||||||
result[:bad_templates] = invalid
|
|
||||||
invalid.each do |bad_template|
|
|
||||||
metrics.increment("ondemandrequest_fail.invalid.#{bad_template}")
|
|
||||||
end
|
|
||||||
status 404
|
|
||||||
end
|
|
||||||
else
|
|
||||||
metrics.increment('ondemandrequest_fail.invalid.unknown')
|
|
||||||
status 404
|
|
||||||
end
|
|
||||||
|
|
||||||
JSON.pretty_generate(result)
|
|
||||||
end
|
|
||||||
|
|
||||||
get "#{api_prefix}/ondemandvm/:requestid/?" do
|
|
||||||
content_type :json
|
|
||||||
metrics.increment('http_requests_vm_total.get.ondemand.request')
|
|
||||||
|
|
||||||
status 404
|
|
||||||
result = check_ondemand_request(params[:requestid])
|
|
||||||
|
|
||||||
JSON.pretty_generate(result)
|
|
||||||
end
|
|
||||||
|
|
||||||
def check_ondemand_request(request_id)
|
def check_ondemand_request(request_id)
|
||||||
tracer.in_span("Vmpooler::API::V2.#{__method__}") do |span|
|
tracer.in_span("Vmpooler::API::V3.#{__method__}") do |span|
|
||||||
span.set_attribute('vmpooler.request_id', request_id)
|
span.set_attribute('vmpooler.request_id', request_id)
|
||||||
result = { 'ok' => false }
|
result = { 'ok' => false }
|
||||||
request_hash = backend.hgetall("vmpooler__odrequest__#{request_id}")
|
request_hash = backend.hgetall("vmpooler__odrequest__#{request_id}")
|
||||||
|
|
@ -896,7 +1313,7 @@ module Vmpooler
|
||||||
e_message = "no request found for request_id '#{request_id}'"
|
e_message = "no request found for request_id '#{request_id}'"
|
||||||
result['message'] = e_message
|
result['message'] = e_message
|
||||||
span.add_event('error', attributes: {
|
span.add_event('error', attributes: {
|
||||||
'error.type' => 'Vmpooler::API::V2.check_ondemand_request',
|
'error.type' => 'Vmpooler::API::V3.check_ondemand_request',
|
||||||
'error.message' => e_message
|
'error.message' => e_message
|
||||||
})
|
})
|
||||||
return result
|
return result
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
require 'spec_helper'
|
require 'spec_helper'
|
||||||
require 'rack/test'
|
require 'rack/test'
|
||||||
|
|
||||||
describe Vmpooler::API::V2 do
|
describe Vmpooler::API::V3 do
|
||||||
include Rack::Test::Methods
|
include Rack::Test::Methods
|
||||||
|
|
||||||
def app()
|
def app()
|
||||||
|
|
@ -37,7 +37,7 @@ describe Vmpooler::API::V2 do
|
||||||
}
|
}
|
||||||
|
|
||||||
describe '/config/pooltemplate' do
|
describe '/config/pooltemplate' do
|
||||||
let(:prefix) { '/api/v2' }
|
let(:prefix) { '/api/v3' }
|
||||||
let(:metrics) { Vmpooler::Metrics::DummyStatsd.new }
|
let(:metrics) { Vmpooler::Metrics::DummyStatsd.new }
|
||||||
|
|
||||||
let(:current_time) { Time.now }
|
let(:current_time) { Time.now }
|
||||||
|
|
@ -388,7 +388,7 @@ describe Vmpooler::API::V2 do
|
||||||
end
|
end
|
||||||
|
|
||||||
describe 'GET /config' do
|
describe 'GET /config' do
|
||||||
let(:prefix) { '/api/v2' }
|
let(:prefix) { '/api/v3' }
|
||||||
|
|
||||||
it 'returns pool configuration when set' do
|
it 'returns pool configuration when set' do
|
||||||
get "#{prefix}/config"
|
get "#{prefix}/config"
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
require 'spec_helper'
|
require 'spec_helper'
|
||||||
require 'rack/test'
|
require 'rack/test'
|
||||||
|
|
||||||
describe Vmpooler::API::V2 do
|
describe Vmpooler::API::V3 do
|
||||||
include Rack::Test::Methods
|
include Rack::Test::Methods
|
||||||
|
|
||||||
def app()
|
def app()
|
||||||
|
|
@ -15,7 +15,7 @@ describe Vmpooler::API::V2 do
|
||||||
end
|
end
|
||||||
|
|
||||||
describe '/ondemandvm' do
|
describe '/ondemandvm' do
|
||||||
let(:prefix) { '/api/v2' }
|
let(:prefix) { '/api/v3' }
|
||||||
let(:metrics) { Vmpooler::Metrics::DummyStatsd.new }
|
let(:metrics) { Vmpooler::Metrics::DummyStatsd.new }
|
||||||
let(:config) {
|
let(:config) {
|
||||||
{
|
{
|
||||||
|
|
@ -120,24 +120,6 @@ describe Vmpooler::API::V2 do
|
||||||
expect(redis).to receive(:hset).with("vmpooler__odrequest__#{uuid}", 'requested', 'pool2:pool1:1')
|
expect(redis).to receive(:hset).with("vmpooler__odrequest__#{uuid}", 'requested', 'pool2:pool1:1')
|
||||||
post "#{prefix}/ondemandvm", '{"pool2":"1"}'
|
post "#{prefix}/ondemandvm", '{"pool2":"1"}'
|
||||||
end
|
end
|
||||||
|
|
||||||
# Domain is always included in reply now
|
|
||||||
# context 'with domain set in the config' do
|
|
||||||
# let(:domain) { 'example.com' }
|
|
||||||
# before(:each) do
|
|
||||||
# config[:config]['domain'] = domain
|
|
||||||
# end
|
|
||||||
|
|
||||||
# it 'should include domain in the return reply' do
|
|
||||||
# post "#{prefix}/ondemandvm", '{"poolone":"1"}'
|
|
||||||
# expect_json(true, 201)
|
|
||||||
# expected = {
|
|
||||||
# "ok": true,
|
|
||||||
# "request_id": uuid,
|
|
||||||
# }
|
|
||||||
# expect(last_response.body).to eq(JSON.pretty_generate(expected))
|
|
||||||
# end
|
|
||||||
# end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'with a resource request that exceeds the specified limit' do
|
context 'with a resource request that exceeds the specified limit' do
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
require 'spec_helper'
|
require 'spec_helper'
|
||||||
require 'rack/test'
|
require 'rack/test'
|
||||||
|
|
||||||
describe Vmpooler::API::V2 do
|
describe Vmpooler::API::V3 do
|
||||||
include Rack::Test::Methods
|
include Rack::Test::Methods
|
||||||
|
|
||||||
def app()
|
def app()
|
||||||
|
|
@ -30,7 +30,7 @@ describe Vmpooler::API::V2 do
|
||||||
}
|
}
|
||||||
|
|
||||||
describe '/poolreset' do
|
describe '/poolreset' do
|
||||||
let(:prefix) { '/api/v2' }
|
let(:prefix) { '/api/v3' }
|
||||||
let(:metrics) { Vmpooler::Metrics::DummyStatsd.new }
|
let(:metrics) { Vmpooler::Metrics::DummyStatsd.new }
|
||||||
|
|
||||||
let(:current_time) { Time.now }
|
let(:current_time) { Time.now }
|
||||||
|
|
@ -5,7 +5,7 @@ def has_set_tag?(vm, tag, value)
|
||||||
value == redis.hget("vmpooler__vm__#{vm}", "tag:#{tag}")
|
value == redis.hget("vmpooler__vm__#{vm}", "tag:#{tag}")
|
||||||
end
|
end
|
||||||
|
|
||||||
describe Vmpooler::API::V2 do
|
describe Vmpooler::API::V3 do
|
||||||
include Rack::Test::Methods
|
include Rack::Test::Methods
|
||||||
|
|
||||||
def app()
|
def app()
|
||||||
|
|
@ -20,7 +20,7 @@ describe Vmpooler::API::V2 do
|
||||||
end
|
end
|
||||||
|
|
||||||
describe 'status and metrics endpoints' do
|
describe 'status and metrics endpoints' do
|
||||||
let(:prefix) { '/api/v2' }
|
let(:prefix) { '/api/v3' }
|
||||||
|
|
||||||
let(:config) {
|
let(:config) {
|
||||||
{
|
{
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
require 'spec_helper'
|
require 'spec_helper'
|
||||||
require 'rack/test'
|
require 'rack/test'
|
||||||
|
|
||||||
describe Vmpooler::API::V2 do
|
describe Vmpooler::API::V3 do
|
||||||
include Rack::Test::Methods
|
include Rack::Test::Methods
|
||||||
|
|
||||||
def app()
|
def app()
|
||||||
|
|
@ -16,7 +16,7 @@ describe Vmpooler::API::V2 do
|
||||||
end
|
end
|
||||||
|
|
||||||
describe '/token' do
|
describe '/token' do
|
||||||
let(:prefix) { '/api/v2' }
|
let(:prefix) { '/api/v3' }
|
||||||
let(:current_time) { Time.now }
|
let(:current_time) { Time.now }
|
||||||
let(:config) { {
|
let(:config) { {
|
||||||
config: {}
|
config: {}
|
||||||
|
|
@ -111,7 +111,7 @@ describe Vmpooler::API::V2 do
|
||||||
end
|
end
|
||||||
|
|
||||||
describe '/token/:token' do
|
describe '/token/:token' do
|
||||||
let(:prefix) { '/api/v2' }
|
let(:prefix) { '/api/v3' }
|
||||||
let(:current_time) { Time.now }
|
let(:current_time) { Time.now }
|
||||||
|
|
||||||
before(:each) do
|
before(:each) do
|
||||||
|
|
@ -5,7 +5,7 @@ def has_set_tag?(vm, tag, value)
|
||||||
value == redis.hget("vmpooler__vm__#{vm}", "tag:#{tag}")
|
value == redis.hget("vmpooler__vm__#{vm}", "tag:#{tag}")
|
||||||
end
|
end
|
||||||
|
|
||||||
describe Vmpooler::API::V2 do
|
describe Vmpooler::API::V3 do
|
||||||
include Rack::Test::Methods
|
include Rack::Test::Methods
|
||||||
|
|
||||||
def app()
|
def app()
|
||||||
|
|
@ -20,7 +20,7 @@ describe Vmpooler::API::V2 do
|
||||||
end
|
end
|
||||||
|
|
||||||
describe '/vm/:hostname' do
|
describe '/vm/:hostname' do
|
||||||
let(:prefix) { '/api/v2' }
|
let(:prefix) { '/api/v3' }
|
||||||
let(:metrics) { Vmpooler::Metrics::DummyStatsd.new }
|
let(:metrics) { Vmpooler::Metrics::DummyStatsd.new }
|
||||||
|
|
||||||
let(:config) {
|
let(:config) {
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
require 'spec_helper'
|
require 'spec_helper'
|
||||||
require 'rack/test'
|
require 'rack/test'
|
||||||
|
|
||||||
describe Vmpooler::API::V2 do
|
describe Vmpooler::API::V3 do
|
||||||
include Rack::Test::Methods
|
include Rack::Test::Methods
|
||||||
|
|
||||||
def app()
|
def app()
|
||||||
|
|
@ -16,7 +16,7 @@ describe Vmpooler::API::V2 do
|
||||||
end
|
end
|
||||||
|
|
||||||
describe '/vm' do
|
describe '/vm' do
|
||||||
let(:prefix) { '/api/v2' }
|
let(:prefix) { '/api/v3' }
|
||||||
let(:metrics) { Vmpooler::Metrics::DummyStatsd.new }
|
let(:metrics) { Vmpooler::Metrics::DummyStatsd.new }
|
||||||
let(:config) {
|
let(:config) {
|
||||||
{
|
{
|
||||||
|
|
@ -355,29 +355,27 @@ describe Vmpooler::API::V2 do
|
||||||
expect(pool_has_ready_vm?('pool1', '2abcdefghijklmnop', redis)).to eq(true)
|
expect(pool_has_ready_vm?('pool1', '2abcdefghijklmnop', redis)).to eq(true)
|
||||||
end
|
end
|
||||||
|
|
||||||
# The helper create_ready_vm inherently means that the vm has already reached a
|
it 'returns the second VM when the first fails to respond' do
|
||||||
# ready state and that open_socket already returned sucessfully before being moved to ready.
|
create_running_vm 'pool1', vmname, redis
|
||||||
# it 'returns the second VM when the first fails to respond' do
|
create_ready_vm 'pool1', "2#{vmname}", redis
|
||||||
# create_running_vm 'pool1', vmname, redis
|
|
||||||
# create_ready_vm 'pool1', "2#{vmname}", redis
|
|
||||||
|
|
||||||
# allow_any_instance_of(Vmpooler::API::Helpers).to receive(:open_socket).with(vmname, nil).and_raise('mockerror')
|
allow_any_instance_of(Vmpooler::API::Helpers).to receive(:open_socket).with(vmname, nil).and_raise('mockerror')
|
||||||
# allow_any_instance_of(Vmpooler::API::Helpers).to receive(:open_socket).with("2#{vmname}", nil).and_return(socket)
|
allow_any_instance_of(Vmpooler::API::Helpers).to receive(:open_socket).with("2#{vmname}", nil).and_return(socket)
|
||||||
|
|
||||||
# post "#{prefix}/vm", '{"pool1":"1"}'
|
post "#{prefix}/vm", '{"pool1":"1"}'
|
||||||
# expect_json(ok = true, http = 200)
|
expect_json(ok = true, http = 200)
|
||||||
|
|
||||||
# expected = {
|
expected = {
|
||||||
# ok: true,
|
ok: true,
|
||||||
# pool1: {
|
pool1: {
|
||||||
# hostname: "2#{vmname}.one.example.com"
|
hostname: "2#{vmname}.one.example.com"
|
||||||
# }
|
}
|
||||||
# }
|
}
|
||||||
|
|
||||||
# expect(last_response.body).to eq(JSON.pretty_generate(expected))
|
expect(last_response.body).to eq(JSON.pretty_generate(expected))
|
||||||
|
|
||||||
# expect(pool_has_ready_vm?('pool1', vmname, redis)).to be false
|
expect(pool_has_ready_vm?('pool1', vmname, redis)).to be false
|
||||||
# end
|
end
|
||||||
|
|
||||||
context '(auth not configured)' do
|
context '(auth not configured)' do
|
||||||
it 'does not extend VM lifetime if auth token is provided' do
|
it 'does not extend VM lifetime if auth token is provided' do
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
require 'spec_helper'
|
require 'spec_helper'
|
||||||
require 'rack/test'
|
require 'rack/test'
|
||||||
|
|
||||||
describe Vmpooler::API::V2 do
|
describe Vmpooler::API::V3 do
|
||||||
include Rack::Test::Methods
|
include Rack::Test::Methods
|
||||||
|
|
||||||
def app()
|
def app()
|
||||||
|
|
@ -16,7 +16,7 @@ describe Vmpooler::API::V2 do
|
||||||
end
|
end
|
||||||
|
|
||||||
describe '/vm/:template' do
|
describe '/vm/:template' do
|
||||||
let(:prefix) { '/api/v2' }
|
let(:prefix) { '/api/v3' }
|
||||||
let(:metrics) { Vmpooler::Metrics::DummyStatsd.new }
|
let(:metrics) { Vmpooler::Metrics::DummyStatsd.new }
|
||||||
let(:config) {
|
let(:config) {
|
||||||
{
|
{
|
||||||
Loading…
Add table
Add a link
Reference in a new issue