Add v3 api and remove v2

This commit is contained in:
Jake Spain 2023-03-07 14:23:23 -05:00
parent d0d97dd0a8
commit 93201756a0
No known key found for this signature in database
GPG key ID: BC1C4DA0A085E113
13 changed files with 627 additions and 2009 deletions

View file

@ -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.

View file

@ -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
{ {

View file

@ -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

View file

@ -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

View file

@ -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"

View file

@ -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

View file

@ -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 }

View file

@ -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) {
{ {

View file

@ -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

View file

@ -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) {

View file

@ -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

View file

@ -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) {
{ {