mirror of
https://github.com/puppetlabs/vmfloaty.git
synced 2026-01-26 13:28:42 -05:00
Compare commits
No commits in common. "main" and "v0.7.8" have entirely different histories.
50 changed files with 848 additions and 4557 deletions
|
|
@ -1,9 +0,0 @@
|
||||||
**/*.yml
|
|
||||||
**/*.yaml
|
|
||||||
**/*.md
|
|
||||||
**/*example
|
|
||||||
**/Dockerfile*
|
|
||||||
coverage
|
|
||||||
examples
|
|
||||||
scripts
|
|
||||||
vendor
|
|
||||||
17
.github/PULL_REQUEST_TEMPLATE
vendored
17
.github/PULL_REQUEST_TEMPLATE
vendored
|
|
@ -1,17 +0,0 @@
|
||||||
## Status
|
|
||||||
|
|
||||||
[Ready for Merge | In Progress | ???]
|
|
||||||
|
|
||||||
## Description
|
|
||||||
|
|
||||||
FIXME
|
|
||||||
|
|
||||||
## Related Issues
|
|
||||||
|
|
||||||
-
|
|
||||||
|
|
||||||
## Todos
|
|
||||||
|
|
||||||
- [ ] Tests
|
|
||||||
- [ ] Documentation
|
|
||||||
|
|
||||||
19
.github/dependabot.yml
vendored
19
.github/dependabot.yml
vendored
|
|
@ -1,19 +0,0 @@
|
||||||
version: 2
|
|
||||||
updates:
|
|
||||||
- package-ecosystem: bundler
|
|
||||||
directory: "/"
|
|
||||||
schedule:
|
|
||||||
interval: weekly
|
|
||||||
open-pull-requests-limit: 10
|
|
||||||
|
|
||||||
- package-ecosystem: docker
|
|
||||||
directory: "/"
|
|
||||||
schedule:
|
|
||||||
interval: weekly
|
|
||||||
open-pull-requests-limit: 10
|
|
||||||
|
|
||||||
- package-ecosystem: github-actions
|
|
||||||
directory: "/"
|
|
||||||
schedule:
|
|
||||||
interval: weekly
|
|
||||||
open-pull-requests-limit: 10
|
|
||||||
12
.github/workflows/auto_release_prep.yml
vendored
12
.github/workflows/auto_release_prep.yml
vendored
|
|
@ -1,12 +0,0 @@
|
||||||
name: Automated release prep
|
|
||||||
|
|
||||||
on:
|
|
||||||
workflow_dispatch:
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
auto_release_prep:
|
|
||||||
uses: puppetlabs/release-engineering-repo-standards/.github/workflows/auto_release_prep.yml@v1
|
|
||||||
secrets: inherit
|
|
||||||
with:
|
|
||||||
project-type: ruby
|
|
||||||
version-file-path: lib/vmfloaty/version.rb
|
|
||||||
8
.github/workflows/dependabot_merge.yml
vendored
8
.github/workflows/dependabot_merge.yml
vendored
|
|
@ -1,8 +0,0 @@
|
||||||
name: Dependabot auto-merge
|
|
||||||
|
|
||||||
on: pull_request
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
dependabot_merge:
|
|
||||||
uses: puppetlabs/release-engineering-repo-standards/.github/workflows/dependabot_merge.yml@v1
|
|
||||||
secrets: inherit
|
|
||||||
8
.github/workflows/ensure_label.yml
vendored
8
.github/workflows/ensure_label.yml
vendored
|
|
@ -1,8 +0,0 @@
|
||||||
name: Ensure label
|
|
||||||
|
|
||||||
on: pull_request
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
ensure_label:
|
|
||||||
uses: puppetlabs/release-engineering-repo-standards/.github/workflows/ensure_label.yml@v1
|
|
||||||
secrets: inherit
|
|
||||||
118
.github/workflows/release.yml
vendored
118
.github/workflows/release.yml
vendored
|
|
@ -1,118 +0,0 @@
|
||||||
name: Tag Release & Push Gem & Docker
|
|
||||||
|
|
||||||
on: workflow_dispatch
|
|
||||||
|
|
||||||
permissions:
|
|
||||||
contents: write
|
|
||||||
issues: read
|
|
||||||
pull-requests: read
|
|
||||||
packages: write
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
release:
|
|
||||||
name: Validate Docs, Tag, and Docker Push
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: Checkout code
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
with:
|
|
||||||
ref: ${{ github.ref }}
|
|
||||||
clean: true
|
|
||||||
fetch-depth: 0
|
|
||||||
|
|
||||||
- name: Get New Version
|
|
||||||
id: nv
|
|
||||||
run: |
|
|
||||||
version=$(grep VERSION lib/vmfloaty/version.rb |rev |cut -d "'" -f2 |rev)
|
|
||||||
echo "version=$version" >> $GITHUB_OUTPUT
|
|
||||||
echo "Found version $version from lib/vmfloaty/version.rb"
|
|
||||||
|
|
||||||
- name: Get Current Version
|
|
||||||
uses: actions/github-script@v7
|
|
||||||
id: cv
|
|
||||||
with:
|
|
||||||
script: |
|
|
||||||
const { data: response } = await github.rest.repos.getLatestRelease({
|
|
||||||
owner: context.repo.owner,
|
|
||||||
repo: context.repo.repo,
|
|
||||||
})
|
|
||||||
console.log(`The latest release is ${response.tag_name}`)
|
|
||||||
return response.tag_name
|
|
||||||
result-encoding: string
|
|
||||||
|
|
||||||
- name: Generate Changelog
|
|
||||||
uses: docker://githubchangeloggenerator/github-changelog-generator:1.16.2
|
|
||||||
with:
|
|
||||||
args: >-
|
|
||||||
--future-release ${{ steps.nv.outputs.version }}
|
|
||||||
env:
|
|
||||||
CHANGELOG_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
|
|
||||||
- name: Validate Changelog
|
|
||||||
run : |
|
|
||||||
set -e
|
|
||||||
if output=$(git status --porcelain) && [ ! -z "$output" ]; then
|
|
||||||
echo "Here is the current git status:"
|
|
||||||
git status
|
|
||||||
echo
|
|
||||||
echo "The following changes were detected:"
|
|
||||||
git --no-pager diff
|
|
||||||
echo "Uncommitted PRs found in the changelog. Please submit a release prep PR of changes after running './release-prep ${{ steps.nv.outputs.version }}'"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
- name: Generate Release Notes
|
|
||||||
uses: docker://githubchangeloggenerator/github-changelog-generator:1.16.2
|
|
||||||
with:
|
|
||||||
args: >-
|
|
||||||
--since-tag ${{ steps.cv.outputs.result }}
|
|
||||||
--future-release ${{ steps.nv.outputs.version }}
|
|
||||||
--output release-notes.md
|
|
||||||
env:
|
|
||||||
CHANGELOG_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
|
|
||||||
- name: Tag Release
|
|
||||||
uses: ncipollo/release-action@v1
|
|
||||||
with:
|
|
||||||
tag: ${{ steps.nv.outputs.version }}
|
|
||||||
token: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
bodyfile: release-notes.md
|
|
||||||
draft: false
|
|
||||||
prerelease: false
|
|
||||||
|
|
||||||
- name: Set up Docker Buildx
|
|
||||||
uses: docker/setup-buildx-action@v3
|
|
||||||
|
|
||||||
- name: Login to GitHub Container Registry
|
|
||||||
uses: docker/login-action@v3
|
|
||||||
with:
|
|
||||||
registry: ghcr.io
|
|
||||||
username: ${{ github.actor }}
|
|
||||||
password: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
|
|
||||||
- name: Build and push
|
|
||||||
uses: docker/build-push-action@v5
|
|
||||||
with:
|
|
||||||
push: true
|
|
||||||
tags: |
|
|
||||||
ghcr.io/${{ github.repository }}:${{ steps.nv.outputs.version }}
|
|
||||||
ghcr.io/${{ github.repository }}:latest
|
|
||||||
|
|
||||||
- name: Set up Ruby 3.2
|
|
||||||
uses: actions/setup-ruby@v1
|
|
||||||
with:
|
|
||||||
version: 3.2.x
|
|
||||||
|
|
||||||
- name: Build gem
|
|
||||||
run: gem build *.gemspec
|
|
||||||
|
|
||||||
- name: Publish gem
|
|
||||||
run: |
|
|
||||||
mkdir -p $HOME/.gem
|
|
||||||
touch $HOME/.gem/credentials
|
|
||||||
chmod 0600 $HOME/.gem/credentials
|
|
||||||
printf -- "---\n:rubygems_api_key: ${GEM_HOST_API_KEY}\n" > $HOME/.gem/credentials
|
|
||||||
gem push *.gem
|
|
||||||
env:
|
|
||||||
GEM_HOST_API_KEY: ${{secrets.RUBYGEMS_AUTH_TOKEN}}
|
|
||||||
39
.github/workflows/security.yml
vendored
39
.github/workflows/security.yml
vendored
|
|
@ -1,39 +0,0 @@
|
||||||
name: Security
|
|
||||||
on:
|
|
||||||
workflow_dispatch:
|
|
||||||
push:
|
|
||||||
branches:
|
|
||||||
- main
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
scan:
|
|
||||||
name: Mend Scanning
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- name: checkout repo content
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
with:
|
|
||||||
fetch-depth: 1
|
|
||||||
- name: setup ruby
|
|
||||||
uses: ruby/setup-ruby@v1
|
|
||||||
with:
|
|
||||||
ruby-version: 2.7
|
|
||||||
# setup a package lock if one doesn't exist, otherwise do nothing
|
|
||||||
- name: check lock
|
|
||||||
run: '[ -f "Gemfile.lock" ] && echo "package lock file exists, skipping" || bundle lock'
|
|
||||||
# install java
|
|
||||||
- uses: actions/setup-java@v4
|
|
||||||
with:
|
|
||||||
distribution: 'temurin' # See 'Supported distributions' for available options
|
|
||||||
java-version: '17'
|
|
||||||
# download mend
|
|
||||||
- name: download_mend
|
|
||||||
run: curl -o wss-unified-agent.jar https://unified-agent.s3.amazonaws.com/wss-unified-agent.jar
|
|
||||||
- name: run mend
|
|
||||||
run: java -jar wss-unified-agent.jar
|
|
||||||
env:
|
|
||||||
WS_APIKEY: ${{ secrets.MEND_API_KEY }}
|
|
||||||
WS_WSS_URL: https://saas-eu.whitesourcesoftware.com/agent
|
|
||||||
WS_USERKEY: ${{ secrets.MEND_TOKEN }}
|
|
||||||
WS_PRODUCTNAME: RE
|
|
||||||
WS_PROJECTNAME: ${{ github.event.repository.name }}
|
|
||||||
54
.github/workflows/test.yml
vendored
54
.github/workflows/test.yml
vendored
|
|
@ -1,54 +0,0 @@
|
||||||
# This workflow uses actions that are not certified by GitHub.
|
|
||||||
# They are provided by a third-party and are governed by
|
|
||||||
# separate terms of service, privacy policy, and support
|
|
||||||
# documentation.
|
|
||||||
# This workflow will download a prebuilt Ruby version, install dependencies and run tests with Rake
|
|
||||||
# For more information see: https://github.com/marketplace/actions/setup-ruby-jruby-and-truffleruby
|
|
||||||
|
|
||||||
name: Test
|
|
||||||
|
|
||||||
on:
|
|
||||||
pull_request:
|
|
||||||
branches:
|
|
||||||
- main
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
spec:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
strategy:
|
|
||||||
matrix:
|
|
||||||
ruby-version:
|
|
||||||
- '2.7'
|
|
||||||
- '3.0'
|
|
||||||
- '3.1'
|
|
||||||
- '3.2'
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
- name: Set up Ruby
|
|
||||||
# To automatically get bug fixes and new Ruby versions for ruby/setup-ruby,
|
|
||||||
# change this to (see https://github.com/ruby/setup-ruby#versioning):
|
|
||||||
# uses: ruby/setup-ruby@v1
|
|
||||||
uses: ruby/setup-ruby@v1
|
|
||||||
with:
|
|
||||||
ruby-version: ${{ matrix.ruby-version }}
|
|
||||||
bundler-cache: true # runs 'bundle install' and caches installed gems automatically
|
|
||||||
- name: Run tests
|
|
||||||
run: bundle exec rake spec
|
|
||||||
- name: Coveralls
|
|
||||||
uses: coverallsapp/github-action@v2
|
|
||||||
with:
|
|
||||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
flag-name: run-${{ matrix.ruby-version }}
|
|
||||||
parallel: true
|
|
||||||
|
|
||||||
finish:
|
|
||||||
needs: spec
|
|
||||||
if: ${{ always() }}
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- name: Coveralls Finished
|
|
||||||
uses: coverallsapp/github-action@v2
|
|
||||||
with:
|
|
||||||
github-token: ${{ secrets.github_token }}
|
|
||||||
parallel-finished: true
|
|
||||||
|
|
@ -1,3 +0,0 @@
|
||||||
project=vmfloaty
|
|
||||||
user=puppetlabs
|
|
||||||
exclude_labels=maintenance
|
|
||||||
4
.gitignore
vendored
4
.gitignore
vendored
|
|
@ -22,13 +22,11 @@ build/
|
||||||
|
|
||||||
## Environment normalisation:
|
## Environment normalisation:
|
||||||
/.bundle/
|
/.bundle/
|
||||||
/vendor/
|
|
||||||
/lib/bundler/man/
|
/lib/bundler/man/
|
||||||
|
|
||||||
.dccache
|
|
||||||
|
|
||||||
# for a library or gem, you might want to ignore these files since the code is
|
# for a library or gem, you might want to ignore these files since the code is
|
||||||
# intended to run in multiple environments; otherwise, check them in:
|
# intended to run in multiple environments; otherwise, check them in:
|
||||||
|
Gemfile.lock
|
||||||
.ruby-version
|
.ruby-version
|
||||||
.ruby-gemset
|
.ruby-gemset
|
||||||
|
|
||||||
|
|
|
||||||
5
.travis.yml
Normal file
5
.travis.yml
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
sudo: false
|
||||||
|
language: ruby
|
||||||
|
rvm:
|
||||||
|
- 2.0.0-p247
|
||||||
|
script: rspec spec
|
||||||
340
CHANGELOG.md
340
CHANGELOG.md
|
|
@ -1,340 +0,0 @@
|
||||||
# Changelog
|
|
||||||
|
|
||||||
## [1.8.1](https://github.com/puppetlabs/vmfloaty/tree/1.8.1) (2023-08-07)
|
|
||||||
|
|
||||||
[Full Changelog](https://github.com/puppetlabs/vmfloaty/compare/1.8.0...1.8.1)
|
|
||||||
|
|
||||||
**Fixed bugs:**
|
|
||||||
|
|
||||||
- status and summary broken for pooler service after v3 [\#185](https://github.com/puppetlabs/vmfloaty/issues/185)
|
|
||||||
- \(RE-15687\) Use relative path for pooler status and summary [\#186](https://github.com/puppetlabs/vmfloaty/pull/186) ([yachub](https://github.com/yachub))
|
|
||||||
|
|
||||||
**Merged pull requests:**
|
|
||||||
|
|
||||||
- Bump rubocop from 1.54.2 to 1.55.1 [\#183](https://github.com/puppetlabs/vmfloaty/pull/183) ([dependabot[bot]](https://github.com/apps/dependabot))
|
|
||||||
- Bump rubocop from 1.52.0 to 1.54.2 [\#182](https://github.com/puppetlabs/vmfloaty/pull/182) ([dependabot[bot]](https://github.com/apps/dependabot))
|
|
||||||
- Bump rubocop from 1.51.0 to 1.52.0 [\#177](https://github.com/puppetlabs/vmfloaty/pull/177) ([dependabot[bot]](https://github.com/apps/dependabot))
|
|
||||||
- Bump rubocop from 1.50.2 to 1.51.0 [\#176](https://github.com/puppetlabs/vmfloaty/pull/176) ([dependabot[bot]](https://github.com/apps/dependabot))
|
|
||||||
- Bump rubocop from 1.49.0 to 1.50.2 [\#174](https://github.com/puppetlabs/vmfloaty/pull/174) ([dependabot[bot]](https://github.com/apps/dependabot))
|
|
||||||
- Bump rubocop from 1.48.1 to 1.49.0 [\#173](https://github.com/puppetlabs/vmfloaty/pull/173) ([dependabot[bot]](https://github.com/apps/dependabot))
|
|
||||||
- Update simplecov requirement from ~\> 0.21.2 to ~\> 0.22.0 [\#167](https://github.com/puppetlabs/vmfloaty/pull/167) ([dependabot[bot]](https://github.com/apps/dependabot))
|
|
||||||
- Update rspec requirement from ~\> 3.11.0 to ~\> 3.12.0 [\#166](https://github.com/puppetlabs/vmfloaty/pull/166) ([dependabot[bot]](https://github.com/apps/dependabot))
|
|
||||||
|
|
||||||
## [1.8.0](https://github.com/puppetlabs/vmfloaty/tree/1.8.0) (2023-03-21)
|
|
||||||
|
|
||||||
[Full Changelog](https://github.com/puppetlabs/vmfloaty/compare/1.7.0...1.8.0)
|
|
||||||
|
|
||||||
**Implemented enhancements:**
|
|
||||||
|
|
||||||
- Docker, Actions, and Docs Updates [\#170](https://github.com/puppetlabs/vmfloaty/pull/170) ([yachub](https://github.com/yachub))
|
|
||||||
|
|
||||||
**Fixed bugs:**
|
|
||||||
|
|
||||||
- Fix `floaty list --active` for vmpooler api v2 [\#169](https://github.com/puppetlabs/vmfloaty/pull/169) ([yachub](https://github.com/yachub))
|
|
||||||
|
|
||||||
**Merged pull requests:**
|
|
||||||
|
|
||||||
- \(RE-15111\) Migrate Synk to Mend [\#168](https://github.com/puppetlabs/vmfloaty/pull/168) ([yachub](https://github.com/yachub))
|
|
||||||
- \(RE-14811\) Move codeowners from DIO to RE [\#165](https://github.com/puppetlabs/vmfloaty/pull/165) ([yachub](https://github.com/yachub))
|
|
||||||
- Add Snyk action and Move to RE org [\#164](https://github.com/puppetlabs/vmfloaty/pull/164) ([yachub](https://github.com/yachub))
|
|
||||||
- Add release-engineering to codeowners [\#163](https://github.com/puppetlabs/vmfloaty/pull/163) ([yachub](https://github.com/yachub))
|
|
||||||
|
|
||||||
## [1.7.0](https://github.com/puppetlabs/vmfloaty/tree/1.7.0) (2022-04-05)
|
|
||||||
|
|
||||||
[Full Changelog](https://github.com/puppetlabs/vmfloaty/compare/v1.6.0...1.7.0)
|
|
||||||
|
|
||||||
**Implemented enhancements:**
|
|
||||||
|
|
||||||
- \(maint\) Add Ruby 3.1 to test matrix + dockerfile and drop EOL Ruby 2.6 [\#159](https://github.com/puppetlabs/vmfloaty/pull/159) ([yachub](https://github.com/yachub))
|
|
||||||
- \(DIO-3101\) Add VMPooler API v2 Support [\#158](https://github.com/puppetlabs/vmfloaty/pull/158) ([yachub](https://github.com/yachub))
|
|
||||||
|
|
||||||
**Fixed bugs:**
|
|
||||||
|
|
||||||
- \(maint\) Remove colorize usage from `floaty status` [\#160](https://github.com/puppetlabs/vmfloaty/pull/160) ([yachub](https://github.com/yachub))
|
|
||||||
|
|
||||||
**Merged pull requests:**
|
|
||||||
|
|
||||||
- v1.7.0 release prep [\#162](https://github.com/puppetlabs/vmfloaty/pull/162) ([yachub](https://github.com/yachub))
|
|
||||||
- \(maint\) removing previous maintainers [\#157](https://github.com/puppetlabs/vmfloaty/pull/157) ([binford2k](https://github.com/binford2k))
|
|
||||||
- Update rspec requirement from ~\> 3.10.0 to ~\> 3.11.0 [\#155](https://github.com/puppetlabs/vmfloaty/pull/155) ([dependabot[bot]](https://github.com/apps/dependabot))
|
|
||||||
- Docs on contributing and releasing [\#152](https://github.com/puppetlabs/vmfloaty/pull/152) ([genebean](https://github.com/genebean))
|
|
||||||
|
|
||||||
## [v1.6.0](https://github.com/puppetlabs/vmfloaty/tree/v1.6.0) (2022-02-16)
|
|
||||||
|
|
||||||
[Full Changelog](https://github.com/puppetlabs/vmfloaty/compare/v1.5.0...v1.6.0)
|
|
||||||
|
|
||||||
**Merged pull requests:**
|
|
||||||
|
|
||||||
- \(DIO-2700\) Vmfloaty should not use the Colorize gem [\#156](https://github.com/puppetlabs/vmfloaty/pull/156) ([sbeaulie](https://github.com/sbeaulie))
|
|
||||||
- \(maint\) Fix up nspooler list active bug [\#154](https://github.com/puppetlabs/vmfloaty/pull/154) ([cthorn42](https://github.com/cthorn42))
|
|
||||||
- Minor cleanup to the readme [\#151](https://github.com/puppetlabs/vmfloaty/pull/151) ([genebean](https://github.com/genebean))
|
|
||||||
- Release prep for 1.5.0 [\#150](https://github.com/puppetlabs/vmfloaty/pull/150) ([genebean](https://github.com/genebean))
|
|
||||||
|
|
||||||
## [v1.5.0](https://github.com/puppetlabs/vmfloaty/tree/v1.5.0) (2021-10-12)
|
|
||||||
|
|
||||||
[Full Changelog](https://github.com/puppetlabs/vmfloaty/compare/v1.4.0...v1.5.0)
|
|
||||||
|
|
||||||
**Merged pull requests:**
|
|
||||||
|
|
||||||
- DIO 2412- Ondemand and Priority flag added to SSH command [\#149](https://github.com/puppetlabs/vmfloaty/pull/149) ([tanisha-payne](https://github.com/tanisha-payne))
|
|
||||||
- \(DIO-2135\) Update docker FROM image to ruby 3.0.2 [\#148](https://github.com/puppetlabs/vmfloaty/pull/148) ([sbeaulie](https://github.com/sbeaulie))
|
|
||||||
- Migrate CI to GitHub Actions [\#147](https://github.com/puppetlabs/vmfloaty/pull/147) ([genebean](https://github.com/genebean))
|
|
||||||
- v1.4.0 release prep [\#146](https://github.com/puppetlabs/vmfloaty/pull/146) ([genebean](https://github.com/genebean))
|
|
||||||
|
|
||||||
## [v1.4.0](https://github.com/puppetlabs/vmfloaty/tree/v1.4.0) (2021-07-16)
|
|
||||||
|
|
||||||
[Full Changelog](https://github.com/puppetlabs/vmfloaty/compare/v1.3.0...v1.4.0)
|
|
||||||
|
|
||||||
**Merged pull requests:**
|
|
||||||
|
|
||||||
- \(maint\) Use latest Faraday/webmock, update specs [\#145](https://github.com/puppetlabs/vmfloaty/pull/145) ([nmburgan](https://github.com/nmburgan))
|
|
||||||
- Update commander requirement from \>= 4.4.3, \< 4.6.0 to \>= 4.4.3, \< 4.7.0 [\#140](https://github.com/puppetlabs/vmfloaty/pull/140) ([dependabot[bot]](https://github.com/apps/dependabot))
|
|
||||||
- Release prep for v1.3.0 [\#137](https://github.com/puppetlabs/vmfloaty/pull/137) ([sbeaulie](https://github.com/sbeaulie))
|
|
||||||
- Run the rubocop auto\_correct [\#135](https://github.com/puppetlabs/vmfloaty/pull/135) ([sbeaulie](https://github.com/sbeaulie))
|
|
||||||
|
|
||||||
## [v1.3.0](https://github.com/puppetlabs/vmfloaty/tree/v1.3.0) (2021-03-03)
|
|
||||||
|
|
||||||
[Full Changelog](https://github.com/puppetlabs/vmfloaty/compare/v1.2.0...v1.3.0)
|
|
||||||
|
|
||||||
**Merged pull requests:**
|
|
||||||
|
|
||||||
- \(DIO-1522\) Show the VM state \(running, destroyed\) and colorize in red… [\#134](https://github.com/puppetlabs/vmfloaty/pull/134) ([sbeaulie](https://github.com/sbeaulie))
|
|
||||||
- Release prep for 1.2.0 [\#132](https://github.com/puppetlabs/vmfloaty/pull/132) ([genebean](https://github.com/genebean))
|
|
||||||
- Update rubocop requirement from ~\> 0.52 to ~\> 1.6 [\#124](https://github.com/puppetlabs/vmfloaty/pull/124) ([dependabot[bot]](https://github.com/apps/dependabot))
|
|
||||||
|
|
||||||
## [v1.2.0](https://github.com/puppetlabs/vmfloaty/tree/v1.2.0) (2021-02-11)
|
|
||||||
|
|
||||||
[Full Changelog](https://github.com/puppetlabs/vmfloaty/compare/v1.1.1...v1.2.0)
|
|
||||||
|
|
||||||
**Merged pull requests:**
|
|
||||||
|
|
||||||
- \(DIO-908\) Floaty can now report the status of ABS requests [\#131](https://github.com/puppetlabs/vmfloaty/pull/131) ([sbeaulie](https://github.com/sbeaulie))
|
|
||||||
- Update rspec requirement from ~\> 3.9.0 to ~\> 3.10.0 [\#116](https://github.com/puppetlabs/vmfloaty/pull/116) ([dependabot[bot]](https://github.com/apps/dependabot))
|
|
||||||
|
|
||||||
## [v1.1.1](https://github.com/puppetlabs/vmfloaty/tree/v1.1.1) (2020-10-16)
|
|
||||||
|
|
||||||
[Full Changelog](https://github.com/puppetlabs/vmfloaty/compare/v1.1.0...v1.1.1)
|
|
||||||
|
|
||||||
**Merged pull requests:**
|
|
||||||
|
|
||||||
- V1.1.1 [\#112](https://github.com/puppetlabs/vmfloaty/pull/112) ([sbeaulie](https://github.com/sbeaulie))
|
|
||||||
|
|
||||||
## [v1.1.0](https://github.com/puppetlabs/vmfloaty/tree/v1.1.0) (2020-10-09)
|
|
||||||
|
|
||||||
[Full Changelog](https://github.com/puppetlabs/vmfloaty/compare/v1.0.0...v1.1.0)
|
|
||||||
|
|
||||||
**Merged pull requests:**
|
|
||||||
|
|
||||||
- \(maint\) Add more uniqueness to jobid and useful termination message [\#107](https://github.com/puppetlabs/vmfloaty/pull/107) ([sbeaulie](https://github.com/sbeaulie))
|
|
||||||
- \(maint\) Fix bug with detecting ABS service [\#106](https://github.com/puppetlabs/vmfloaty/pull/106) ([sbeaulie](https://github.com/sbeaulie))
|
|
||||||
- \(maint\) Don't require config file for list --active [\#105](https://github.com/puppetlabs/vmfloaty/pull/105) ([sbeaulie](https://github.com/sbeaulie))
|
|
||||||
- \(maint\) Don't require configuration file for get [\#104](https://github.com/puppetlabs/vmfloaty/pull/104) ([nwolfe](https://github.com/nwolfe))
|
|
||||||
- \(maint\) Add vmpooler\_fallback to the get service check [\#103](https://github.com/puppetlabs/vmfloaty/pull/103) ([cthorn42](https://github.com/cthorn42))
|
|
||||||
- Update completion scripts for `service` subcommands [\#102](https://github.com/puppetlabs/vmfloaty/pull/102) ([scotje](https://github.com/scotje))
|
|
||||||
- Bump to version 1.0.0 [\#100](https://github.com/puppetlabs/vmfloaty/pull/100) ([genebean](https://github.com/genebean))
|
|
||||||
|
|
||||||
## [v1.0.0](https://github.com/puppetlabs/vmfloaty/tree/v1.0.0) (2020-09-22)
|
|
||||||
|
|
||||||
[Full Changelog](https://github.com/puppetlabs/vmfloaty/compare/0.11.1...v1.0.0)
|
|
||||||
|
|
||||||
**Merged pull requests:**
|
|
||||||
|
|
||||||
- \(maint\) Fix for ABS PR\#306 that includes json responses [\#99](https://github.com/puppetlabs/vmfloaty/pull/99) ([sbeaulie](https://github.com/sbeaulie))
|
|
||||||
- \(maint\) Support any vmpooler for ABS via vmpooler\_fallback [\#98](https://github.com/puppetlabs/vmfloaty/pull/98) ([sbeaulie](https://github.com/sbeaulie))
|
|
||||||
- \(DIO-991\) Add service command [\#97](https://github.com/puppetlabs/vmfloaty/pull/97) ([genebean](https://github.com/genebean))
|
|
||||||
- \( DIO-911\) Include job\_id in ABS --json output [\#96](https://github.com/puppetlabs/vmfloaty/pull/96) ([sbeaulie](https://github.com/sbeaulie))
|
|
||||||
- ABS enables fallback to vmpooler for some scenarios [\#94](https://github.com/puppetlabs/vmfloaty/pull/94) ([sbeaulie](https://github.com/sbeaulie))
|
|
||||||
- WIP \(DIO-911\) Include job\_id in ABS --json output [\#92](https://github.com/puppetlabs/vmfloaty/pull/92) ([nwolfe](https://github.com/nwolfe))
|
|
||||||
- \(maint\) Remove warning about missing configuration file [\#91](https://github.com/puppetlabs/vmfloaty/pull/91) ([nwolfe](https://github.com/nwolfe))
|
|
||||||
- Add tab completion script for zsh and fix bash completion for ABS services [\#90](https://github.com/puppetlabs/vmfloaty/pull/90) ([scotje](https://github.com/scotje))
|
|
||||||
|
|
||||||
## [0.11.1](https://github.com/puppetlabs/vmfloaty/tree/0.11.1) (2020-08-20)
|
|
||||||
|
|
||||||
[Full Changelog](https://github.com/puppetlabs/vmfloaty/compare/0.10.0...0.11.1)
|
|
||||||
|
|
||||||
**Merged pull requests:**
|
|
||||||
|
|
||||||
- Update version for 0.11.1 release [\#89](https://github.com/puppetlabs/vmfloaty/pull/89) ([mattkirby](https://github.com/mattkirby))
|
|
||||||
- \(maint\) Fix using ABS service without a .vmfloaty.yml file [\#88](https://github.com/puppetlabs/vmfloaty/pull/88) ([cthorn42](https://github.com/cthorn42))
|
|
||||||
- Fix the argument list for nonstandardpooler [\#87](https://github.com/puppetlabs/vmfloaty/pull/87) ([jarretlavallee](https://github.com/jarretlavallee))
|
|
||||||
- Json output for delete/list + better ABS error handling [\#86](https://github.com/puppetlabs/vmfloaty/pull/86) ([mcdonaldseanp](https://github.com/mcdonaldseanp))
|
|
||||||
- Bump version for 0.11.0 release [\#85](https://github.com/puppetlabs/vmfloaty/pull/85) ([mattkirby](https://github.com/mattkirby))
|
|
||||||
- Print all non-success output to STDERR [\#84](https://github.com/puppetlabs/vmfloaty/pull/84) ([austb](https://github.com/austb))
|
|
||||||
- Update travis.yml [\#83](https://github.com/puppetlabs/vmfloaty/pull/83) ([rooneyshuman](https://github.com/rooneyshuman))
|
|
||||||
- Bump version to 0.10.0 for release [\#82](https://github.com/puppetlabs/vmfloaty/pull/82) ([mattkirby](https://github.com/mattkirby))
|
|
||||||
|
|
||||||
## [0.10.0](https://github.com/puppetlabs/vmfloaty/tree/0.10.0) (2020-08-04)
|
|
||||||
|
|
||||||
[Full Changelog](https://github.com/puppetlabs/vmfloaty/compare/v0.9.2-retag-for-gh-actions-for-real...0.10.0)
|
|
||||||
|
|
||||||
**Merged pull requests:**
|
|
||||||
|
|
||||||
- Update Dependabot config file [\#78](https://github.com/puppetlabs/vmfloaty/pull/78) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
|
||||||
- Update rspec requirement from ~\> 3.5.0 to ~\> 3.9.0 [\#75](https://github.com/puppetlabs/vmfloaty/pull/75) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
|
||||||
- Update commander requirement from ~\> 4.4.3 to \>= 4.4.3, \< 4.6.0 [\#73](https://github.com/puppetlabs/vmfloaty/pull/73) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
|
|
||||||
- Fix formatting of CODEOWNERS [\#71](https://github.com/puppetlabs/vmfloaty/pull/71) ([genebean](https://github.com/genebean))
|
|
||||||
- Add Dependabot and Coveralls [\#70](https://github.com/puppetlabs/vmfloaty/pull/70) ([genebean](https://github.com/genebean))
|
|
||||||
- Update docs [\#69](https://github.com/puppetlabs/vmfloaty/pull/69) ([genebean](https://github.com/genebean))
|
|
||||||
- Remove old maintainer note [\#68](https://github.com/puppetlabs/vmfloaty/pull/68) ([briancain](https://github.com/briancain))
|
|
||||||
- Add support for vmpooler on demand provisioning [\#67](https://github.com/puppetlabs/vmfloaty/pull/67) ([mattkirby](https://github.com/mattkirby))
|
|
||||||
|
|
||||||
## [v0.9.2-retag-for-gh-actions-for-real](https://github.com/puppetlabs/vmfloaty/tree/v0.9.2-retag-for-gh-actions-for-real) (2020-02-05)
|
|
||||||
|
|
||||||
[Full Changelog](https://github.com/puppetlabs/vmfloaty/compare/v0.9.2...v0.9.2-retag-for-gh-actions-for-real)
|
|
||||||
|
|
||||||
**Merged pull requests:**
|
|
||||||
|
|
||||||
- Update gempush action to remove GPR publish [\#66](https://github.com/puppetlabs/vmfloaty/pull/66) ([highb](https://github.com/highb))
|
|
||||||
|
|
||||||
## [v0.9.2](https://github.com/puppetlabs/vmfloaty/tree/v0.9.2) (2020-02-05)
|
|
||||||
|
|
||||||
[Full Changelog](https://github.com/puppetlabs/vmfloaty/compare/v0.9.2-retag-for-gh-actions...v0.9.2)
|
|
||||||
|
|
||||||
## [v0.9.2-retag-for-gh-actions](https://github.com/puppetlabs/vmfloaty/tree/v0.9.2-retag-for-gh-actions) (2020-02-05)
|
|
||||||
|
|
||||||
[Full Changelog](https://github.com/puppetlabs/vmfloaty/compare/v0.9.0...v0.9.2-retag-for-gh-actions)
|
|
||||||
|
|
||||||
**Merged pull requests:**
|
|
||||||
|
|
||||||
- Bump version.rb to 0.9.2 for release [\#65](https://github.com/puppetlabs/vmfloaty/pull/65) ([highb](https://github.com/highb))
|
|
||||||
- Create gempush.yml Github Action [\#64](https://github.com/puppetlabs/vmfloaty/pull/64) ([highb](https://github.com/highb))
|
|
||||||
- Bump faraday dependency for Ruby 2.7 compatibility [\#62](https://github.com/puppetlabs/vmfloaty/pull/62) ([nicklewis](https://github.com/nicklewis))
|
|
||||||
- SSH Command respects ABS now and tests should fail if the API changes… [\#61](https://github.com/puppetlabs/vmfloaty/pull/61) ([mikkergimenez](https://github.com/mikkergimenez))
|
|
||||||
- \(QENG-7604\) Add support for Job IDs to ABS delete [\#60](https://github.com/puppetlabs/vmfloaty/pull/60) ([highb](https://github.com/highb))
|
|
||||||
- Bump version.rb to 0.9.1 [\#59](https://github.com/puppetlabs/vmfloaty/pull/59) ([highb](https://github.com/highb))
|
|
||||||
- Fix error with delete command for vmpooler and nspooler [\#58](https://github.com/puppetlabs/vmfloaty/pull/58) ([mikkergimenez](https://github.com/mikkergimenez))
|
|
||||||
|
|
||||||
## [v0.9.0](https://github.com/puppetlabs/vmfloaty/tree/v0.9.0) (2019-12-17)
|
|
||||||
|
|
||||||
[Full Changelog](https://github.com/puppetlabs/vmfloaty/compare/v0.8.2...v0.9.0)
|
|
||||||
|
|
||||||
**Implemented enhancements:**
|
|
||||||
|
|
||||||
- Add abs vm get [\#53](https://github.com/puppetlabs/vmfloaty/pull/53) ([mikkergimenez](https://github.com/mikkergimenez))
|
|
||||||
|
|
||||||
**Fixed bugs:**
|
|
||||||
|
|
||||||
- vmfloaty reports an error on latest API version output [\#48](https://github.com/puppetlabs/vmfloaty/issues/48)
|
|
||||||
|
|
||||||
**Merged pull requests:**
|
|
||||||
|
|
||||||
- ABS will sometimes return null values in the /status/queue endpoint [\#57](https://github.com/puppetlabs/vmfloaty/pull/57) ([mikkergimenez](https://github.com/mikkergimenez))
|
|
||||||
- Minor version bump to 0.9.0 [\#56](https://github.com/puppetlabs/vmfloaty/pull/56) ([highb](https://github.com/highb))
|
|
||||||
- Update pooler provider to throw an exception if the API returns non-OK [\#55](https://github.com/puppetlabs/vmfloaty/pull/55) ([highb](https://github.com/highb))
|
|
||||||
- Update Faraday to 0.15, remove unnecessary headers [\#54](https://github.com/puppetlabs/vmfloaty/pull/54) ([highb](https://github.com/highb))
|
|
||||||
- change urls in docs to use example.net/.com [\#50](https://github.com/puppetlabs/vmfloaty/pull/50) ([steveax](https://github.com/steveax))
|
|
||||||
- Rubocop cleanup [\#49](https://github.com/puppetlabs/vmfloaty/pull/49) ([rodjek](https://github.com/rodjek))
|
|
||||||
|
|
||||||
## [v0.8.2](https://github.com/puppetlabs/vmfloaty/tree/v0.8.2) (2018-01-05)
|
|
||||||
|
|
||||||
[Full Changelog](https://github.com/puppetlabs/vmfloaty/compare/v0.8.1...v0.8.2)
|
|
||||||
|
|
||||||
**Merged pull requests:**
|
|
||||||
|
|
||||||
- 🎂🎂🎂 Add --json option for `floaty get` [\#47](https://github.com/puppetlabs/vmfloaty/pull/47) ([nicklewis](https://github.com/nicklewis))
|
|
||||||
|
|
||||||
## [v0.8.1](https://github.com/puppetlabs/vmfloaty/tree/v0.8.1) (2017-10-24)
|
|
||||||
|
|
||||||
[Full Changelog](https://github.com/puppetlabs/vmfloaty/compare/v0.8.0...v0.8.1)
|
|
||||||
|
|
||||||
**Merged pull requests:**
|
|
||||||
|
|
||||||
- Bump commander version to clear up deprecation warnings [\#46](https://github.com/puppetlabs/vmfloaty/pull/46) ([highb](https://github.com/highb))
|
|
||||||
|
|
||||||
## [v0.8.0](https://github.com/puppetlabs/vmfloaty/tree/v0.8.0) (2017-10-13)
|
|
||||||
|
|
||||||
[Full Changelog](https://github.com/puppetlabs/vmfloaty/compare/v0.7.9...v0.8.0)
|
|
||||||
|
|
||||||
**Closed issues:**
|
|
||||||
|
|
||||||
- don't automatically call system pager for help screens [\#20](https://github.com/puppetlabs/vmfloaty/issues/20)
|
|
||||||
|
|
||||||
**Merged pull requests:**
|
|
||||||
|
|
||||||
- Add configuration for multiple pooler services and integration with nspooler [\#45](https://github.com/puppetlabs/vmfloaty/pull/45) ([caseywilliams](https://github.com/caseywilliams))
|
|
||||||
|
|
||||||
## [v0.7.9](https://github.com/puppetlabs/vmfloaty/tree/v0.7.9) (2017-07-31)
|
|
||||||
|
|
||||||
[Full Changelog](https://github.com/puppetlabs/vmfloaty/compare/v0.7.8...v0.7.9)
|
|
||||||
|
|
||||||
**Closed issues:**
|
|
||||||
|
|
||||||
- Handle when vmfloaty cannot reach vmpooler [\#39](https://github.com/puppetlabs/vmfloaty/issues/39)
|
|
||||||
|
|
||||||
**Merged pull requests:**
|
|
||||||
|
|
||||||
- Add basic bash completion script and framework for others [\#44](https://github.com/puppetlabs/vmfloaty/pull/44) ([scotje](https://github.com/scotje))
|
|
||||||
- Developersdevelopersdevelopers [\#43](https://github.com/puppetlabs/vmfloaty/pull/43) ([mckern](https://github.com/mckern))
|
|
||||||
|
|
||||||
## [v0.7.8](https://github.com/puppetlabs/vmfloaty/tree/v0.7.8) (2016-12-20)
|
|
||||||
|
|
||||||
[Full Changelog](https://github.com/puppetlabs/vmfloaty/compare/v0.7.7...v0.7.8)
|
|
||||||
|
|
||||||
## [v0.7.7](https://github.com/puppetlabs/vmfloaty/tree/v0.7.7) (2016-12-14)
|
|
||||||
|
|
||||||
[Full Changelog](https://github.com/puppetlabs/vmfloaty/compare/v0.7.6...v0.7.7)
|
|
||||||
|
|
||||||
## [v0.7.6](https://github.com/puppetlabs/vmfloaty/tree/v0.7.6) (2016-12-09)
|
|
||||||
|
|
||||||
[Full Changelog](https://github.com/puppetlabs/vmfloaty/compare/v0.7.5...v0.7.6)
|
|
||||||
|
|
||||||
**Closed issues:**
|
|
||||||
|
|
||||||
- Improve the help text for floaty commands [\#41](https://github.com/puppetlabs/vmfloaty/issues/41)
|
|
||||||
- Require force flag for pool requests larger than 5? [\#40](https://github.com/puppetlabs/vmfloaty/issues/40)
|
|
||||||
|
|
||||||
## [v0.7.5](https://github.com/puppetlabs/vmfloaty/tree/v0.7.5) (2016-12-06)
|
|
||||||
|
|
||||||
[Full Changelog](https://github.com/puppetlabs/vmfloaty/compare/1f86113243eb2e898b21c29892c05477e3487d2d...v0.7.5)
|
|
||||||
|
|
||||||
**Implemented enhancements:**
|
|
||||||
|
|
||||||
- Improve how to specify number of vms in get request [\#8](https://github.com/puppetlabs/vmfloaty/issues/8)
|
|
||||||
- Improve output from commands [\#3](https://github.com/puppetlabs/vmfloaty/issues/3)
|
|
||||||
|
|
||||||
**Fixed bugs:**
|
|
||||||
|
|
||||||
- floaty snapshot fails to authenticate [\#4](https://github.com/puppetlabs/vmfloaty/issues/4)
|
|
||||||
|
|
||||||
**Closed issues:**
|
|
||||||
|
|
||||||
- floaty snapshot should warn users about how long it takes to get snapshot [\#37](https://github.com/puppetlabs/vmfloaty/issues/37)
|
|
||||||
- floaty modify should allow to make changes on more than one machine [\#36](https://github.com/puppetlabs/vmfloaty/issues/36)
|
|
||||||
- Pooler.modify raises exception if Token is nil [\#34](https://github.com/puppetlabs/vmfloaty/issues/34)
|
|
||||||
- Improve error handling in Pooler and Auth classes [\#33](https://github.com/puppetlabs/vmfloaty/issues/33)
|
|
||||||
- Handle vmpooler responses when token is invalid in Pooler class [\#32](https://github.com/puppetlabs/vmfloaty/issues/32)
|
|
||||||
- Misuse of 'floaty revert ...' seems to create a new snapshot [\#31](https://github.com/puppetlabs/vmfloaty/issues/31)
|
|
||||||
- Test [\#30](https://github.com/puppetlabs/vmfloaty/issues/30)
|
|
||||||
- Don't system exit in Auth class [\#29](https://github.com/puppetlabs/vmfloaty/issues/29)
|
|
||||||
- Add flag to auto-ssh into a newly created \(single\) VM [\#28](https://github.com/puppetlabs/vmfloaty/issues/28)
|
|
||||||
- Handle vmpooler URL key that doesn't have 'https' [\#27](https://github.com/puppetlabs/vmfloaty/issues/27)
|
|
||||||
- Abstract vmfloaty cli related errors to command class rather than pooler class [\#26](https://github.com/puppetlabs/vmfloaty/issues/26)
|
|
||||||
- Don't puts results in `delete` method in Pooler library [\#25](https://github.com/puppetlabs/vmfloaty/issues/25)
|
|
||||||
- Improve `get` output [\#24](https://github.com/puppetlabs/vmfloaty/issues/24)
|
|
||||||
- specs don't work [\#22](https://github.com/puppetlabs/vmfloaty/issues/22)
|
|
||||||
- Add ability to get additional disk space for a running vm [\#19](https://github.com/puppetlabs/vmfloaty/issues/19)
|
|
||||||
- Have a force option for `delete --all` [\#17](https://github.com/puppetlabs/vmfloaty/issues/17)
|
|
||||||
- Stop printing json response in library methods [\#14](https://github.com/puppetlabs/vmfloaty/issues/14)
|
|
||||||
- Stop system exiting in library methods [\#13](https://github.com/puppetlabs/vmfloaty/issues/13)
|
|
||||||
- List active vms for a given token [\#12](https://github.com/puppetlabs/vmfloaty/issues/12)
|
|
||||||
- Provide a way to clean up vms obtained by a token [\#11](https://github.com/puppetlabs/vmfloaty/issues/11)
|
|
||||||
- Allow spaces when passing in vms for commands [\#10](https://github.com/puppetlabs/vmfloaty/issues/10)
|
|
||||||
- Write Tests for Pooler class [\#9](https://github.com/puppetlabs/vmfloaty/issues/9)
|
|
||||||
- Document all valid config file keys [\#7](https://github.com/puppetlabs/vmfloaty/issues/7)
|
|
||||||
- Write up simple "introduction" to using the tool [\#6](https://github.com/puppetlabs/vmfloaty/issues/6)
|
|
||||||
- Document how to use Pooler class for ruby scripts [\#5](https://github.com/puppetlabs/vmfloaty/issues/5)
|
|
||||||
- Convert vmfloaty to use latest pooler API [\#1](https://github.com/puppetlabs/vmfloaty/issues/1)
|
|
||||||
|
|
||||||
**Merged pull requests:**
|
|
||||||
|
|
||||||
- Show the status of pools with `floaty status` [\#38](https://github.com/puppetlabs/vmfloaty/pull/38) ([nicklewis](https://github.com/nicklewis))
|
|
||||||
- Show tag values in `list --active` [\#23](https://github.com/puppetlabs/vmfloaty/pull/23) ([justinstoller](https://github.com/justinstoller))
|
|
||||||
- \(\#19\) Update vmfloaty to expect /api/v1 in URL for disk endpoint [\#21](https://github.com/puppetlabs/vmfloaty/pull/21) ([briancain](https://github.com/briancain))
|
|
||||||
- \(\#17\) Add a force option for delete --all [\#18](https://github.com/puppetlabs/vmfloaty/pull/18) ([briancain](https://github.com/briancain))
|
|
||||||
- \(\#12\) List active vms for a given token [\#16](https://github.com/puppetlabs/vmfloaty/pull/16) ([briancain](https://github.com/briancain))
|
|
||||||
- Cleanup vmfloaty library and command processor [\#15](https://github.com/puppetlabs/vmfloaty/pull/15) ([briancain](https://github.com/briancain))
|
|
||||||
- \(\#1\) Update with commander [\#2](https://github.com/puppetlabs/vmfloaty/pull/2) ([briancain](https://github.com/briancain))
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
\* *This Changelog was automatically generated by [github_changelog_generator](https://github.com/github-changelog-generator/github-changelog-generator)*
|
|
||||||
|
|
@ -1,3 +0,0 @@
|
||||||
# Set the default code owners
|
|
||||||
* @puppetlabs/release-engineering
|
|
||||||
|
|
||||||
23
Dockerfile
23
Dockerfile
|
|
@ -1,23 +0,0 @@
|
||||||
FROM ruby:3.3.5-slim-bullseye
|
|
||||||
|
|
||||||
LABEL org.opencontainers.image.authors="@puppetlabs/release-engineering"
|
|
||||||
LABEL org.opencontainers.image.title="vmfloaty"
|
|
||||||
LABEL org.opencontainers.image.source=https://github.com/puppetlabs/vmfloaty
|
|
||||||
LABEL org.opencontainers.image.description="A CLI helper tool for VMPooler"
|
|
||||||
|
|
||||||
RUN apt-get update -qq && apt-get install -y build-essential less make openssh-client
|
|
||||||
|
|
||||||
RUN groupadd --gid 1000 floatygroup \
|
|
||||||
&& useradd --uid 1000 --gid 1000 -m floatyuser
|
|
||||||
|
|
||||||
USER floatyuser
|
|
||||||
|
|
||||||
WORKDIR /home/floatyuser/app
|
|
||||||
COPY --chown=floatyuser:floatygroup . .
|
|
||||||
|
|
||||||
RUN gem install bundler \
|
|
||||||
&& bundle install \
|
|
||||||
&& gem build vmfloaty.gemspec \
|
|
||||||
&& gem install vmfloaty*.gem
|
|
||||||
|
|
||||||
ENTRYPOINT [ "floaty" ]
|
|
||||||
23
Gemfile
23
Gemfile
|
|
@ -1,18 +1,11 @@
|
||||||
# frozen_string_literal: true
|
|
||||||
|
|
||||||
source 'https://rubygems.org'
|
source 'https://rubygems.org'
|
||||||
|
|
||||||
|
gem 'commander'
|
||||||
|
gem 'faraday', '0.9.2'
|
||||||
|
gem 'colorize', '~> 0.8'
|
||||||
|
|
||||||
|
gem 'rspec'
|
||||||
|
gem 'webmock', '1.21.0'
|
||||||
|
gem 'rake'
|
||||||
|
|
||||||
gemspec
|
gemspec
|
||||||
|
|
||||||
gem 'rake', require: false
|
|
||||||
|
|
||||||
group :test do
|
|
||||||
gem 'simplecov', '~> 0.22.0'
|
|
||||||
gem 'simplecov-html', '~> 0.13.1'
|
|
||||||
gem 'simplecov-lcov', '~> 0.8.0'
|
|
||||||
gem 'pry'
|
|
||||||
gem 'rb-readline'
|
|
||||||
gem 'rspec', '~> 3.13.0'
|
|
||||||
gem 'rubocop', '~> 1.66'
|
|
||||||
gem 'webmock', '~> 3.23'
|
|
||||||
end
|
|
||||||
|
|
|
||||||
125
Gemfile.lock
125
Gemfile.lock
|
|
@ -1,125 +0,0 @@
|
||||||
PATH
|
|
||||||
remote: .
|
|
||||||
specs:
|
|
||||||
vmfloaty (1.8.1)
|
|
||||||
commander (>= 4.4.3, < 4.7.0)
|
|
||||||
faraday (~> 1.5, >= 1.5.1)
|
|
||||||
|
|
||||||
GEM
|
|
||||||
remote: https://rubygems.org/
|
|
||||||
specs:
|
|
||||||
addressable (2.8.6)
|
|
||||||
public_suffix (>= 2.0.2, < 6.0)
|
|
||||||
ast (2.4.2)
|
|
||||||
bigdecimal (3.1.8)
|
|
||||||
coderay (1.1.3)
|
|
||||||
commander (4.6.0)
|
|
||||||
highline (~> 2.0.0)
|
|
||||||
crack (1.0.0)
|
|
||||||
bigdecimal
|
|
||||||
rexml
|
|
||||||
diff-lcs (1.5.1)
|
|
||||||
docile (1.4.0)
|
|
||||||
faraday (1.10.3)
|
|
||||||
faraday-em_http (~> 1.0)
|
|
||||||
faraday-em_synchrony (~> 1.0)
|
|
||||||
faraday-excon (~> 1.1)
|
|
||||||
faraday-httpclient (~> 1.0)
|
|
||||||
faraday-multipart (~> 1.0)
|
|
||||||
faraday-net_http (~> 1.0)
|
|
||||||
faraday-net_http_persistent (~> 1.0)
|
|
||||||
faraday-patron (~> 1.0)
|
|
||||||
faraday-rack (~> 1.0)
|
|
||||||
faraday-retry (~> 1.0)
|
|
||||||
ruby2_keywords (>= 0.0.4)
|
|
||||||
faraday-em_http (1.0.0)
|
|
||||||
faraday-em_synchrony (1.0.0)
|
|
||||||
faraday-excon (1.1.0)
|
|
||||||
faraday-httpclient (1.0.1)
|
|
||||||
faraday-multipart (1.0.4)
|
|
||||||
multipart-post (~> 2)
|
|
||||||
faraday-net_http (1.0.1)
|
|
||||||
faraday-net_http_persistent (1.2.0)
|
|
||||||
faraday-patron (1.0.0)
|
|
||||||
faraday-rack (1.0.0)
|
|
||||||
faraday-retry (1.0.3)
|
|
||||||
hashdiff (1.1.0)
|
|
||||||
highline (2.0.3)
|
|
||||||
json (2.7.2)
|
|
||||||
language_server-protocol (3.17.0.3)
|
|
||||||
method_source (1.0.0)
|
|
||||||
multipart-post (2.3.0)
|
|
||||||
parallel (1.26.3)
|
|
||||||
parser (3.3.5.0)
|
|
||||||
ast (~> 2.4.1)
|
|
||||||
racc
|
|
||||||
pry (0.14.2)
|
|
||||||
coderay (~> 1.1)
|
|
||||||
method_source (~> 1.0)
|
|
||||||
public_suffix (5.0.5)
|
|
||||||
racc (1.8.1)
|
|
||||||
rainbow (3.1.1)
|
|
||||||
rake (13.2.1)
|
|
||||||
rb-readline (0.5.5)
|
|
||||||
regexp_parser (2.9.2)
|
|
||||||
rexml (3.3.6)
|
|
||||||
strscan
|
|
||||||
rspec (3.13.0)
|
|
||||||
rspec-core (~> 3.13.0)
|
|
||||||
rspec-expectations (~> 3.13.0)
|
|
||||||
rspec-mocks (~> 3.13.0)
|
|
||||||
rspec-core (3.13.0)
|
|
||||||
rspec-support (~> 3.13.0)
|
|
||||||
rspec-expectations (3.13.0)
|
|
||||||
diff-lcs (>= 1.2.0, < 2.0)
|
|
||||||
rspec-support (~> 3.13.0)
|
|
||||||
rspec-mocks (3.13.0)
|
|
||||||
diff-lcs (>= 1.2.0, < 2.0)
|
|
||||||
rspec-support (~> 3.13.0)
|
|
||||||
rspec-support (3.13.0)
|
|
||||||
rubocop (1.66.1)
|
|
||||||
json (~> 2.3)
|
|
||||||
language_server-protocol (>= 3.17.0)
|
|
||||||
parallel (~> 1.10)
|
|
||||||
parser (>= 3.3.0.2)
|
|
||||||
rainbow (>= 2.2.2, < 4.0)
|
|
||||||
regexp_parser (>= 2.4, < 3.0)
|
|
||||||
rubocop-ast (>= 1.32.2, < 2.0)
|
|
||||||
ruby-progressbar (~> 1.7)
|
|
||||||
unicode-display_width (>= 2.4.0, < 3.0)
|
|
||||||
rubocop-ast (1.32.3)
|
|
||||||
parser (>= 3.3.1.0)
|
|
||||||
ruby-progressbar (1.13.0)
|
|
||||||
ruby2_keywords (0.0.5)
|
|
||||||
simplecov (0.22.0)
|
|
||||||
docile (~> 1.1)
|
|
||||||
simplecov-html (~> 0.11)
|
|
||||||
simplecov_json_formatter (~> 0.1)
|
|
||||||
simplecov-html (0.13.1)
|
|
||||||
simplecov-lcov (0.8.0)
|
|
||||||
simplecov_json_formatter (0.1.4)
|
|
||||||
strscan (3.1.0)
|
|
||||||
unicode-display_width (2.5.0)
|
|
||||||
webmock (3.23.1)
|
|
||||||
addressable (>= 2.8.0)
|
|
||||||
crack (>= 0.3.2)
|
|
||||||
hashdiff (>= 0.4.0, < 2.0.0)
|
|
||||||
|
|
||||||
PLATFORMS
|
|
||||||
aarch64-linux
|
|
||||||
x86_64-linux
|
|
||||||
|
|
||||||
DEPENDENCIES
|
|
||||||
pry
|
|
||||||
rake
|
|
||||||
rb-readline
|
|
||||||
rspec (~> 3.13.0)
|
|
||||||
rubocop (~> 1.66)
|
|
||||||
simplecov (~> 0.22.0)
|
|
||||||
simplecov-html (~> 0.13.1)
|
|
||||||
simplecov-lcov (~> 0.8.0)
|
|
||||||
vmfloaty!
|
|
||||||
webmock (~> 3.23)
|
|
||||||
|
|
||||||
BUNDLED WITH
|
|
||||||
2.4.8
|
|
||||||
190
LICENSE
190
LICENSE
|
|
@ -1,192 +1,6 @@
|
||||||
Apache License
|
vmfloaty
|
||||||
Version 2.0, January 2004
|
|
||||||
http://www.apache.org/licenses/
|
|
||||||
|
|
||||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
Copyright (C) 2014-2016 Puppet
|
||||||
|
|
||||||
1. Definitions.
|
|
||||||
|
|
||||||
"License" shall mean the terms and conditions for use, reproduction,
|
|
||||||
and distribution as defined by Sections 1 through 9 of this document.
|
|
||||||
|
|
||||||
"Licensor" shall mean the copyright owner or entity authorized by
|
|
||||||
the copyright owner that is granting the License.
|
|
||||||
|
|
||||||
"Legal Entity" shall mean the union of the acting entity and all
|
|
||||||
other entities that control, are controlled by, or are under common
|
|
||||||
control with that entity. For the purposes of this definition,
|
|
||||||
"control" means (i) the power, direct or indirect, to cause the
|
|
||||||
direction or management of such entity, whether by contract or
|
|
||||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
|
||||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
|
||||||
|
|
||||||
"You" (or "Your") shall mean an individual or Legal Entity
|
|
||||||
exercising permissions granted by this License.
|
|
||||||
|
|
||||||
"Source" form shall mean the preferred form for making modifications,
|
|
||||||
including but not limited to software source code, documentation
|
|
||||||
source, and configuration files.
|
|
||||||
|
|
||||||
"Object" form shall mean any form resulting from mechanical
|
|
||||||
transformation or translation of a Source form, including but
|
|
||||||
not limited to compiled object code, generated documentation,
|
|
||||||
and conversions to other media types.
|
|
||||||
|
|
||||||
"Work" shall mean the work of authorship, whether in Source or
|
|
||||||
Object form, made available under the License, as indicated by a
|
|
||||||
copyright notice that is included in or attached to the work
|
|
||||||
(an example is provided in the Appendix below).
|
|
||||||
|
|
||||||
"Derivative Works" shall mean any work, whether in Source or Object
|
|
||||||
form, that is based on (or derived from) the Work and for which the
|
|
||||||
editorial revisions, annotations, elaborations, or other modifications
|
|
||||||
represent, as a whole, an original work of authorship. For the purposes
|
|
||||||
of this License, Derivative Works shall not include works that remain
|
|
||||||
separable from, or merely link (or bind by name) to the interfaces of,
|
|
||||||
the Work and Derivative Works thereof.
|
|
||||||
|
|
||||||
"Contribution" shall mean any work of authorship, including
|
|
||||||
the original version of the Work and any modifications or additions
|
|
||||||
to that Work or Derivative Works thereof, that is intentionally
|
|
||||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
|
||||||
or by an individual or Legal Entity authorized to submit on behalf of
|
|
||||||
the copyright owner. For the purposes of this definition, "submitted"
|
|
||||||
means any form of electronic, verbal, or written communication sent
|
|
||||||
to the Licensor or its representatives, including but not limited to
|
|
||||||
communication on electronic mailing lists, source code control systems,
|
|
||||||
and issue tracking systems that are managed by, or on behalf of, the
|
|
||||||
Licensor for the purpose of discussing and improving the Work, but
|
|
||||||
excluding communication that is conspicuously marked or otherwise
|
|
||||||
designated in writing by the copyright owner as "Not a Contribution."
|
|
||||||
|
|
||||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
|
||||||
on behalf of whom a Contribution has been received by Licensor and
|
|
||||||
subsequently incorporated within the Work.
|
|
||||||
|
|
||||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
|
||||||
this License, each Contributor hereby grants to You a perpetual,
|
|
||||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
||||||
copyright license to reproduce, prepare Derivative Works of,
|
|
||||||
publicly display, publicly perform, sublicense, and distribute the
|
|
||||||
Work and such Derivative Works in Source or Object form.
|
|
||||||
|
|
||||||
3. Grant of Patent License. Subject to the terms and conditions of
|
|
||||||
this License, each Contributor hereby grants to You a perpetual,
|
|
||||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
||||||
(except as stated in this section) patent license to make, have made,
|
|
||||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
|
||||||
where such license applies only to those patent claims licensable
|
|
||||||
by such Contributor that are necessarily infringed by their
|
|
||||||
Contribution(s) alone or by combination of their Contribution(s)
|
|
||||||
with the Work to which such Contribution(s) was submitted. If You
|
|
||||||
institute patent litigation against any entity (including a
|
|
||||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
|
||||||
or a Contribution incorporated within the Work constitutes direct
|
|
||||||
or contributory patent infringement, then any patent licenses
|
|
||||||
granted to You under this License for that Work shall terminate
|
|
||||||
as of the date such litigation is filed.
|
|
||||||
|
|
||||||
4. Redistribution. You may reproduce and distribute copies of the
|
|
||||||
Work or Derivative Works thereof in any medium, with or without
|
|
||||||
modifications, and in Source or Object form, provided that You
|
|
||||||
meet the following conditions:
|
|
||||||
|
|
||||||
(a) You must give any other recipients of the Work or
|
|
||||||
Derivative Works a copy of this License; and
|
|
||||||
|
|
||||||
(b) You must cause any modified files to carry prominent notices
|
|
||||||
stating that You changed the files; and
|
|
||||||
|
|
||||||
(c) You must retain, in the Source form of any Derivative Works
|
|
||||||
that You distribute, all copyright, patent, trademark, and
|
|
||||||
attribution notices from the Source form of the Work,
|
|
||||||
excluding those notices that do not pertain to any part of
|
|
||||||
the Derivative Works; and
|
|
||||||
|
|
||||||
(d) If the Work includes a "NOTICE" text file as part of its
|
|
||||||
distribution, then any Derivative Works that You distribute must
|
|
||||||
include a readable copy of the attribution notices contained
|
|
||||||
within such NOTICE file, excluding those notices that do not
|
|
||||||
pertain to any part of the Derivative Works, in at least one
|
|
||||||
of the following places: within a NOTICE text file distributed
|
|
||||||
as part of the Derivative Works; within the Source form or
|
|
||||||
documentation, if provided along with the Derivative Works; or,
|
|
||||||
within a display generated by the Derivative Works, if and
|
|
||||||
wherever such third-party notices normally appear. The contents
|
|
||||||
of the NOTICE file are for informational purposes only and
|
|
||||||
do not modify the License. You may add Your own attribution
|
|
||||||
notices within Derivative Works that You distribute, alongside
|
|
||||||
or as an addendum to the NOTICE text from the Work, provided
|
|
||||||
that such additional attribution notices cannot be construed
|
|
||||||
as modifying the License.
|
|
||||||
|
|
||||||
You may add Your own copyright statement to Your modifications and
|
|
||||||
may provide additional or different license terms and conditions
|
|
||||||
for use, reproduction, or distribution of Your modifications, or
|
|
||||||
for any such Derivative Works as a whole, provided Your use,
|
|
||||||
reproduction, and distribution of the Work otherwise complies with
|
|
||||||
the conditions stated in this License.
|
|
||||||
|
|
||||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
|
||||||
any Contribution intentionally submitted for inclusion in the Work
|
|
||||||
by You to the Licensor shall be under the terms and conditions of
|
|
||||||
this License, without any additional terms or conditions.
|
|
||||||
Notwithstanding the above, nothing herein shall supersede or modify
|
|
||||||
the terms of any separate license agreement you may have executed
|
|
||||||
with Licensor regarding such Contributions.
|
|
||||||
|
|
||||||
6. Trademarks. This License does not grant permission to use the trade
|
|
||||||
names, trademarks, service marks, or product names of the Licensor,
|
|
||||||
except as required for reasonable and customary use in describing the
|
|
||||||
origin of the Work and reproducing the content of the NOTICE file.
|
|
||||||
|
|
||||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
|
||||||
agreed to in writing, Licensor provides the Work (and each
|
|
||||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
|
||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
|
||||||
implied, including, without limitation, any warranties or conditions
|
|
||||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
|
||||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
|
||||||
appropriateness of using or redistributing the Work and assume any
|
|
||||||
risks associated with Your exercise of permissions under this License.
|
|
||||||
|
|
||||||
8. Limitation of Liability. In no event and under no legal theory,
|
|
||||||
whether in tort (including negligence), contract, or otherwise,
|
|
||||||
unless required by applicable law (such as deliberate and grossly
|
|
||||||
negligent acts) or agreed to in writing, shall any Contributor be
|
|
||||||
liable to You for damages, including any direct, indirect, special,
|
|
||||||
incidental, or consequential damages of any character arising as a
|
|
||||||
result of this License or out of the use or inability to use the
|
|
||||||
Work (including but not limited to damages for loss of goodwill,
|
|
||||||
work stoppage, computer failure or malfunction, or any and all
|
|
||||||
other commercial damages or losses), even if such Contributor
|
|
||||||
has been advised of the possibility of such damages.
|
|
||||||
|
|
||||||
9. Accepting Warranty or Additional Liability. While redistributing
|
|
||||||
the Work or Derivative Works thereof, You may choose to offer,
|
|
||||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
|
||||||
or other liability obligations and/or rights consistent with this
|
|
||||||
License. However, in accepting such obligations, You may act only
|
|
||||||
on Your own behalf and on Your sole responsibility, not on behalf
|
|
||||||
of any other Contributor, and only if You agree to indemnify,
|
|
||||||
defend, and hold each Contributor harmless for any liability
|
|
||||||
incurred by, or claims asserted against, such Contributor by reason
|
|
||||||
of your accepting any such warranty or additional liability.
|
|
||||||
|
|
||||||
END OF TERMS AND CONDITIONS
|
|
||||||
|
|
||||||
APPENDIX: How to apply the Apache License to your work.
|
|
||||||
|
|
||||||
To apply the Apache License to your work, attach the following
|
|
||||||
boilerplate notice, with the fields enclosed by brackets "{}"
|
|
||||||
replaced with your own identifying information. (Don't include
|
|
||||||
the brackets!) The text should be enclosed in the appropriate
|
|
||||||
comment syntax for the file format. We also recommend that a
|
|
||||||
file or class name and description of purpose be included on the
|
|
||||||
same "printed page" as the copyright notice for easier
|
|
||||||
identification within third-party archives.
|
|
||||||
|
|
||||||
Copyright {yyyy} {name of copyright owner}
|
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
|
|
|
||||||
166
README.md
166
README.md
|
|
@ -1,75 +1,37 @@
|
||||||
# vmfloaty
|
vmfloaty
|
||||||
|
========
|
||||||
|
|
||||||
[](https://badge.fury.io/rb/vmfloaty)
|
[](https://badge.fury.io/rb/vmfloaty) [](https://travis-ci.org/briancain/vmfloaty)
|
||||||
[](https://github.com/puppetlabs/vmfloaty/actions/workflows/test.yml)
|
|
||||||
|
|
||||||
A CLI helper tool for [Puppet's VMPooler](https://github.com/puppetlabs/vmpooler) to help you stay afloat.
|
A CLI helper tool for [Puppet Labs vmpooler](https://github.com/puppetlabs/vmpooler) to help you stay afloat.
|
||||||
|
|
||||||

|
<img src="http://i.imgur.com/xGcGwuH.jpg" width=200 height=200>
|
||||||
|
|
||||||
- [vmfloaty](#vmfloaty)
|
|
||||||
- [Install](#install)
|
|
||||||
- [Ruby](#ruby)
|
|
||||||
- [Docker](#docker)
|
|
||||||
- [Usage](#usage)
|
|
||||||
- [Example workflow](#example-workflow)
|
|
||||||
- [vmfloaty dotfile](#vmfloaty-dotfile)
|
|
||||||
- [Basic configuration](#basic-configuration)
|
|
||||||
- [Using multiple services](#using-multiple-services)
|
|
||||||
- [Using backends besides VMPooler](#using-backends-besides-vmpooler)
|
|
||||||
- [Valid config keys](#valid-config-keys)
|
|
||||||
- [Tab Completion](#tab-completion)
|
|
||||||
- [VMPooler API](#vmpooler-api)
|
|
||||||
- [Using the Pooler class](#using-the-pooler-class)
|
|
||||||
- [Example Projects](#example-projects)
|
|
||||||
- [Contributing](#contributing)
|
|
||||||
- [Code Reviews](#code-reviews)
|
|
||||||
- [Releasing](#releasing)
|
|
||||||
- [Special thanks](#special-thanks)
|
|
||||||
|
|
||||||
## Install
|
## Install
|
||||||
|
|
||||||
### Ruby
|
|
||||||
|
|
||||||
Grab the latest from ruby gems...
|
Grab the latest from ruby gems...
|
||||||
|
|
||||||
```bash
|
|
||||||
gem install vmfloaty
|
|
||||||
```
|
```
|
||||||
|
$ gem install vmfloaty
|
||||||
### Docker
|
...
|
||||||
|
...
|
||||||
Run the docker image:
|
$ floaty --help
|
||||||
|
```
|
||||||
`docker run -it --rm -v ~/.vmfloaty.yml:/home/floatyuser/.vmfloaty.yml ghcr.io/puppetlabs/vmfloaty --help`
|
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
```plain
|
```
|
||||||
$ floaty --help
|
|
||||||
NAME:
|
|
||||||
|
|
||||||
floaty
|
|
||||||
|
|
||||||
DESCRIPTION:
|
|
||||||
|
|
||||||
A CLI helper tool for Puppet's VMPooler to help you stay afloat
|
|
||||||
|
|
||||||
COMMANDS:
|
|
||||||
|
|
||||||
completion Outputs path to completion script
|
|
||||||
delete Schedules the deletion of a host or hosts
|
delete Schedules the deletion of a host or hosts
|
||||||
get Gets a vm or vms based on the os argument
|
get Gets a vm or vms based on the os argument
|
||||||
help Display global or [command] help documentation
|
help Display global or [command] help documentation
|
||||||
list Shows a list of available vms from the pooler or vms obtained with a token
|
list Shows a list of available vms from the pooler or vms obtained with a token
|
||||||
modify Modify a VM's tags, time to live, disk space, or reservation reason
|
modify Modify a vms tags, time to live, and disk space
|
||||||
query Get information about a given vm
|
query Get information about a given vm
|
||||||
revert Reverts a vm to a specified snapshot
|
revert Reverts a vm to a specified snapshot
|
||||||
service Display information about floaty services and their configuration
|
|
||||||
snapshot Takes a snapshot of a given vm
|
snapshot Takes a snapshot of a given vm
|
||||||
ssh Grabs a single vm and sshs into it
|
ssh Grabs a single vm and sshs into it
|
||||||
status Prints the status of pools in the pooler service
|
status Prints the status of pools in vmpooler
|
||||||
summary Prints a summary of a pooler service
|
summary Prints a summary of vmpooler
|
||||||
token Retrieves or deletes a token or checks token status
|
token Retrieves or deletes a token or checks token status
|
||||||
|
|
||||||
GLOBAL OPTIONS:
|
GLOBAL OPTIONS:
|
||||||
|
|
@ -88,113 +50,55 @@ $ floaty --help
|
||||||
|
|
||||||
Grabbing a token for authenticated pooler requests:
|
Grabbing a token for authenticated pooler requests:
|
||||||
|
|
||||||
```bash
|
```
|
||||||
floaty token get --user username --url https://vmpooler.example.net/api/v1
|
floaty token get --user username --url https://vmpooler.mycompany.net/api/v1
|
||||||
```
|
```
|
||||||
|
|
||||||
This command will then ask you to log in. If successful, it will return a token that you can save either in a dotfile or use with other cli commands.
|
This command will then ask you to log in. If successful, it will return a token that you can save either in a dotfile or use with other cli commands.
|
||||||
|
|
||||||
Grabbing vms:
|
Grabbing vms:
|
||||||
|
|
||||||
```bash
|
```
|
||||||
floaty get centos-7-x86_64=2 debian-7-x86_64 windows-10=3 --token mytokenstring --url https://vmpooler.example.net/api/v1
|
floaty get centos-7-x86_64=2 debian-7-x86_64 windows-10=3 --token mytokenstring --url https://vmpooler.mycompany.net/api/v1
|
||||||
```
|
```
|
||||||
|
|
||||||
### vmfloaty dotfile
|
### vmfloaty dotfile
|
||||||
|
|
||||||
If you do not wish to continually specify various config options with the cli, you can `~/.vmfloaty.yml` for some defaults. You can get a list of valid service types and example configuration files via `floaty service types` and `floaty service examples`, respectively.
|
If you do not wish to continuely specify various config options with the cli, you can have a dotfile in your home directory for some defaults. For example:
|
||||||
|
|
||||||
#### Basic configuration
|
|
||||||
|
|
||||||
This is the simplest type of configuration where you only need a single service:
|
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
# file at ~/.vmfloaty.yml
|
#file at /Users/me/.vmfloaty.yml
|
||||||
url: 'https://vmpooler.example.net/api/v1'
|
url: 'https://vmpooler.mycompany.net/api/v1'
|
||||||
user: 'brian'
|
user: 'brian'
|
||||||
token: 'tokenstring'
|
token: 'tokenstring'
|
||||||
```
|
```
|
||||||
|
|
||||||
Run `floaty service examples` to see additional configuration options
|
Now vmfloaty will use those config files if no flag was specified.
|
||||||
|
|
||||||
#### Using multiple services
|
|
||||||
|
|
||||||
Most commands allow you to specify a `--service <servicename>` option to allow the use of multiple pooler instances. This can be useful when you'd rather not specify a `--url` or `--token` by hand for alternate services.
|
|
||||||
|
|
||||||
- If you run `floaty` without a `--service <name>` option, vmfloaty will use the first configured service by default.
|
|
||||||
- If keys are missing for a configured service, vmfloaty will attempt to fall back to the top-level values.
|
|
||||||
This makes it so you can specify things like `user` once at the top of your `~/.vmfloaty.yml`.
|
|
||||||
|
|
||||||
#### Using backends besides VMPooler
|
|
||||||
|
|
||||||
vmfloaty supports additional backends besides VMPooler. To see a complete list, run `floaty service types`. The output of `floaty service examples` will show you how to configure each of the supported backends.
|
|
||||||
|
|
||||||
#### Valid config keys
|
#### Valid config keys
|
||||||
|
|
||||||
Here are the keys that vmfloaty currently supports:
|
Here are the keys that vmfloaty currently supports:
|
||||||
|
|
||||||
- verbose (Boolean)
|
- verbose
|
||||||
- token (String)
|
+ Boolean
|
||||||
- user (String)
|
- token
|
||||||
- url (String)
|
+ String
|
||||||
- services (String)
|
- user
|
||||||
- type (String)
|
+ String
|
||||||
- vmpooler_fallback (String)
|
- url
|
||||||
|
+ String
|
||||||
|
|
||||||
### Tab Completion
|
## vmpooler API
|
||||||
|
|
||||||
There is a basic completion script for Bash (and possibly other shells) included with the gem in the [extras/completions](https://github.com/puppetlabs/vmfloaty/blob/master/extras/completions) folder. To activate, that file simply needs to be sourced somehow in your shell profile.
|
This cli tool uses the [vmpooler API](https://github.com/puppetlabs/vmpooler/blob/master/API.md).
|
||||||
|
|
||||||
For convenience, the path to the completion script for the currently active version of the gem can be found with the `floaty completion` subcommand. This makes it easy to add the completion script to your profile like so:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
source $(floaty completion --shell bash)
|
|
||||||
```
|
|
||||||
|
|
||||||
If you are running on macOS and use Homebrew's `bash-completion` formula, you can symlink the script to `/usr/local/etc/bash_completion.d/floaty` and it will be sourced automatically:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
ln -s $(floaty completion --shell bash) /usr/local/etc/bash_completion.d/floaty
|
|
||||||
```
|
|
||||||
|
|
||||||
There is also tab completion for zsh:
|
|
||||||
|
|
||||||
```zsh
|
|
||||||
source $(floaty completion --shell zsh)
|
|
||||||
```
|
|
||||||
|
|
||||||
## VMPooler API
|
|
||||||
|
|
||||||
This cli tool uses the [VMPooler API](https://github.com/puppetlabs/vmpooler/blob/master/API.md).
|
|
||||||
|
|
||||||
## Using the Pooler class
|
## Using the Pooler class
|
||||||
|
|
||||||
vmfloaty providers a `Pooler` class that gives users the ability to make requests to VMPooler without having to write their own requests. It also provides an `Auth` class for managing VMPooler tokens within your application.
|
vmfloaty providers a `Pooler` class that gives users the ability to make requests to vmpooler without having to write their own requests. It also provides an `Auth` class for managing vmpooler tokens within your application.
|
||||||
|
|
||||||
### Example Projects
|
### Example Projects
|
||||||
|
|
||||||
- [John McCabe: vmpooler-bitbar](https://github.com/johnmccabe/vmpooler-bitbar/)
|
- [John McCabe: vmpooler-bitbar](https://github.com/johnmccabe/vmpooler-bitbar/)
|
||||||
- vmpooler status and management in your menubar with bitbar
|
+ vmpooler status and management in your menubar with bitbar
|
||||||
- [Brian Cain: vagrant-vmpooler](https://github.com/briancain/vagrant-vmpooler)
|
- [Brian Cain: vagrant-vmpooler](https://github.com/briancain/vagrant-vmpooler)
|
||||||
- Use Vagrant to manage your vmpooler instances
|
+ Use Vagrant to manage your vmpooler instances
|
||||||
|
|
||||||
## Contributing
|
|
||||||
|
|
||||||
PR's are welcome! We always love to see how others think this tool can be made better.
|
|
||||||
|
|
||||||
### Code Reviews
|
|
||||||
|
|
||||||
Please wait for multiple code owners to sign off on any notable change.
|
|
||||||
|
|
||||||
## Releasing
|
|
||||||
|
|
||||||
Follow these steps to publish a new GitHub release, build and push the gem to <https://rubygems.org>, and build and push a Docker Image to GitHub Container Registry:
|
|
||||||
|
|
||||||
1. Bump the "VERSION" in `lib/vmfloaty/version.rb` appropriately based on changes in `CHANGELOG.md` since the last release.
|
|
||||||
2. Run `./release-prep` to update `Gemfile.lock` and `CHANGELOG.md`.
|
|
||||||
3. Commit and push changes to a new branch, then open a pull request against `main` and be sure to add the "maintenance" label.
|
|
||||||
4. After the pull request is approved and merged, then navigate to <https://github.com/puppetlabs/vmfloaty/actions/workflows/release.yml> --> Run workflow --> select "main" branch --> Run workflow. This will publish a GitHub release, build and push the gem to RubyGems, and build and push a Docker Image to GitHub Container Registry.
|
|
||||||
|
|
||||||
## Special thanks
|
|
||||||
|
|
||||||
Special thanks to [Brian Cain](https://github.com/briancain) as he is the original author of vmfloaty! Vast amounts of this code exist thanks to his efforts.
|
|
||||||
|
|
|
||||||
15
Rakefile
15
Rakefile
|
|
@ -1,9 +1,6 @@
|
||||||
# frozen_string_literal: true
|
|
||||||
|
|
||||||
require 'rubygems'
|
require 'rubygems'
|
||||||
require 'bundler/setup'
|
require 'bundler/setup'
|
||||||
require 'rspec/core/rake_task'
|
require 'rspec/core/rake_task'
|
||||||
require 'rubocop/rake_task'
|
|
||||||
|
|
||||||
# Immediately sync all stdout so that tools like buildbot can
|
# Immediately sync all stdout so that tools like buildbot can
|
||||||
# immediately load in the output.
|
# immediately load in the output.
|
||||||
|
|
@ -11,7 +8,7 @@ $stdout.sync = true
|
||||||
$stderr.sync = true
|
$stderr.sync = true
|
||||||
|
|
||||||
# Change to the directory of this file.
|
# Change to the directory of this file.
|
||||||
Dir.chdir(File.expand_path(__dir__))
|
Dir.chdir(File.expand_path("../", __FILE__))
|
||||||
|
|
||||||
# This installs the tasks that help with gem creation and
|
# This installs the tasks that help with gem creation and
|
||||||
# publishing.
|
# publishing.
|
||||||
|
|
@ -19,13 +16,7 @@ Bundler::GemHelper.install_tasks
|
||||||
|
|
||||||
# Install the `spec` task so that we can run tests.
|
# Install the `spec` task so that we can run tests.
|
||||||
RSpec::Core::RakeTask.new(:spec) do |t|
|
RSpec::Core::RakeTask.new(:spec) do |t|
|
||||||
t.rspec_opts = '--order defined'
|
t.rspec_opts = "--order defined"
|
||||||
end
|
end
|
||||||
|
|
||||||
desc 'Run RuboCop'
|
|
||||||
RuboCop::RakeTask.new(:rubocop) do |task|
|
|
||||||
task.options << '--display-cop-names'
|
|
||||||
end
|
|
||||||
|
|
||||||
# Default task is to run the unit tests
|
# Default task is to run the unit tests
|
||||||
task default: :spec
|
task :default => :spec
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
#!/usr/bin/env ruby
|
#!/usr/bin/env ruby
|
||||||
# frozen_string_literal: true
|
|
||||||
|
|
||||||
$LOAD_PATH.unshift(File.expand_path('../lib', __dir__))
|
$LOAD_PATH.unshift(File.expand_path('../../lib', __FILE__))
|
||||||
|
|
||||||
require 'vmfloaty'
|
require 'vmfloaty'
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,38 +0,0 @@
|
||||||
#!/usr/bin/env bash
|
|
||||||
|
|
||||||
_vmfloaty()
|
|
||||||
{
|
|
||||||
local cur prev commands template_arg_commands hostname_arg_commands service_subcommands
|
|
||||||
|
|
||||||
COMPREPLY=()
|
|
||||||
cur="${COMP_WORDS[COMP_CWORD]}"
|
|
||||||
prev="${COMP_WORDS[COMP_CWORD-1]}"
|
|
||||||
|
|
||||||
commands="delete get help list modify query revert service snapshot ssh status summary token"
|
|
||||||
template_arg_commands="get ssh"
|
|
||||||
hostname_arg_commands="delete modify query revert snapshot"
|
|
||||||
service_subcommands="types examples"
|
|
||||||
|
|
||||||
if [[ $cur == -* ]] ; then
|
|
||||||
# TODO: option completion
|
|
||||||
COMPREPLY=()
|
|
||||||
elif [[ $template_arg_commands =~ (^| )$prev($| ) ]] ; then
|
|
||||||
if [[ -z "$_vmfloaty_avail_templates" ]] ; then
|
|
||||||
# TODO: need a --hostnameonly equivalent here because the section headers of
|
|
||||||
# `floaty list` are adding some spurious entries (including files in current
|
|
||||||
# directory because part of the headers is `**` which is getting expanded)
|
|
||||||
_vmfloaty_avail_templates=$(floaty list 2>/dev/null)
|
|
||||||
fi
|
|
||||||
|
|
||||||
COMPREPLY=( $(compgen -W "${_vmfloaty_avail_templates}" -- "${cur}") )
|
|
||||||
elif [[ $hostname_arg_commands =~ (^| )$prev($| ) ]] ; then
|
|
||||||
_vmfloaty_active_hostnames=$(floaty list --active --hostnameonly 2>/dev/null)
|
|
||||||
COMPREPLY=( $(compgen -W "${_vmfloaty_active_hostnames}" -- "${cur}") )
|
|
||||||
elif [[ "service" == $prev ]] ; then
|
|
||||||
COMPREPLY=( $(compgen -W "${service_subcommands}" -- "${cur}") )
|
|
||||||
elif [[ $1 == $prev ]] ; then
|
|
||||||
# only show top level commands we are at root
|
|
||||||
COMPREPLY=( $(compgen -W "${commands}" -- "${cur}") )
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
complete -F _vmfloaty floaty
|
|
||||||
|
|
@ -1,43 +0,0 @@
|
||||||
_floaty()
|
|
||||||
{
|
|
||||||
local line commands template_arg_commands hostname_arg_commands service_subcommands
|
|
||||||
|
|
||||||
commands="delete get help list modify query revert service snapshot ssh status summary token"
|
|
||||||
|
|
||||||
template_arg_commands=("get" "ssh")
|
|
||||||
hostname_arg_commands=("delete" "modify" "query" "revert" "snapshot")
|
|
||||||
service_subcommands=("types" "examples")
|
|
||||||
|
|
||||||
_arguments -C \
|
|
||||||
"1: :(${commands})" \
|
|
||||||
"*::arg:->args"
|
|
||||||
|
|
||||||
if ((template_arg_commands[(Ie)$line[1]])); then
|
|
||||||
_floaty_template_sub
|
|
||||||
elif ((hostname_arg_commands[(Ie)$line[1]])); then
|
|
||||||
_floaty_hostname_sub
|
|
||||||
elif [[ "service" == $line[1] ]]; then
|
|
||||||
_arguments "1: :(${service_subcommands})"
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
_floaty_template_sub()
|
|
||||||
{
|
|
||||||
if [[ -z "$_vmfloaty_avail_templates" ]] ; then
|
|
||||||
# TODO: need a --hostnameonly equivalent here because the section headers of
|
|
||||||
# `floaty list` are adding some spurious entries (including files in current
|
|
||||||
# directory because part of the headers is `**` which is getting expanded)
|
|
||||||
_vmfloaty_avail_templates=$(floaty list 2>/dev/null)
|
|
||||||
fi
|
|
||||||
|
|
||||||
_arguments "1: :(${_vmfloaty_avail_templates})"
|
|
||||||
}
|
|
||||||
|
|
||||||
_floaty_hostname_sub()
|
|
||||||
{
|
|
||||||
_vmfloaty_active_hostnames=$(floaty list --active --hostnameonly 2>/dev/null)
|
|
||||||
|
|
||||||
_arguments "1: :(${_vmfloaty_active_hostnames})"
|
|
||||||
}
|
|
||||||
|
|
||||||
compdef _floaty floaty
|
|
||||||
BIN
float.jpg
BIN
float.jpg
Binary file not shown.
|
Before Width: | Height: | Size: 11 KiB |
685
lib/vmfloaty.rb
685
lib/vmfloaty.rb
|
|
@ -1,83 +1,107 @@
|
||||||
# frozen_string_literal: true
|
#!/usr/bin/env ruby
|
||||||
|
|
||||||
require 'rubygems'
|
require 'rubygems'
|
||||||
require 'commander'
|
require 'commander'
|
||||||
|
require 'colorize'
|
||||||
require 'json'
|
require 'json'
|
||||||
require 'pp'
|
require 'pp'
|
||||||
require 'uri'
|
|
||||||
require 'vmfloaty/auth'
|
require 'vmfloaty/auth'
|
||||||
require 'vmfloaty/pooler'
|
require 'vmfloaty/pooler'
|
||||||
require 'vmfloaty/version'
|
require 'vmfloaty/version'
|
||||||
require 'vmfloaty/conf'
|
require 'vmfloaty/conf'
|
||||||
require 'vmfloaty/utils'
|
require 'vmfloaty/utils'
|
||||||
require 'vmfloaty/service'
|
|
||||||
require 'vmfloaty/ssh'
|
require 'vmfloaty/ssh'
|
||||||
require 'vmfloaty/logger'
|
|
||||||
|
|
||||||
class Vmfloaty
|
class Vmfloaty
|
||||||
include Commander::Methods
|
include Commander::Methods
|
||||||
|
|
||||||
def run # rubocop:disable Metrics/AbcSize
|
def run
|
||||||
program :version, Vmfloaty::VERSION
|
program :version, Version.get
|
||||||
program :description,
|
program :description, 'A CLI helper tool for Puppet Labs vmpooler to help you stay afloat'
|
||||||
"A CLI helper tool for Puppet's vmpooler to help you stay afloat.\n\nConfiguration may be placed in a ~/.vmfloaty.yml file."
|
|
||||||
|
|
||||||
config = Conf.read_config
|
config = Conf.read_config
|
||||||
|
|
||||||
command :get do |c|
|
command :get do |c|
|
||||||
c.syntax = 'floaty get os_type0 os_type1=x ox_type2=y [options]'
|
c.syntax = 'floaty get os_type0 os_type1=x ox_type2=y [options]'
|
||||||
c.summary = 'Gets a vm or vms based on the os argument'
|
c.summary = 'Gets a vm or vms based on the os argument'
|
||||||
c.description = 'A command to retrieve vms from a pooler service. Can either be a single vm, or multiple with the `=` syntax.'
|
c.description = 'A command to retrieve vms from vmpooler. Can either be a single vm, or multiple with the `=` syntax.'
|
||||||
c.example 'Gets a few vms', 'floaty get centos=3 debian --user brian --url http://vmpooler.example.com'
|
c.example 'Gets a few vms', 'floaty get centos=3 debian --user brian --url http://vmpooler.example.com'
|
||||||
c.option '--verbose', 'Enables verbose output'
|
c.option '--verbose', 'Enables verbose output'
|
||||||
c.option '--service STRING', String, 'Configured pooler service name'
|
|
||||||
c.option '--user STRING', String, 'User to authenticate with'
|
c.option '--user STRING', String, 'User to authenticate with'
|
||||||
c.option '--url STRING', String, 'URL of pooler service'
|
c.option '--url STRING', String, 'URL of vmpooler'
|
||||||
c.option '--token STRING', String, 'Token for pooler service'
|
c.option '--token STRING', String, 'Token for vmpooler'
|
||||||
c.option '--priority STRING', 'Priority for supported backends(ABS) (High(1), Medium(2), Low(3))'
|
|
||||||
c.option '--notoken', 'Makes a request without a token'
|
c.option '--notoken', 'Makes a request without a token'
|
||||||
c.option '--force', 'Forces vmfloaty to get requested vms'
|
c.option '--force', 'Forces vmfloaty to get requested vms'
|
||||||
c.option '--json', 'Prints retrieved vms in JSON format'
|
|
||||||
c.option '--ondemand', 'Requested vms are provisioned upon receival of the request, tracked by a request ID'
|
|
||||||
c.option '--continue STRING', String, 'resume polling ABS for job_id, for use when the cli was interrupted'
|
|
||||||
c.option '--loglevel STRING', String, 'the log level to use (debug, info, error)'
|
|
||||||
c.action do |args, options|
|
c.action do |args, options|
|
||||||
verbose = options.verbose || config['verbose']
|
verbose = options.verbose || config['verbose']
|
||||||
FloatyLogger.setlevel = options.loglevel if options.loglevel
|
token = options.token || config['token']
|
||||||
service = Service.new(options, config)
|
user = options.user ||= config['user']
|
||||||
use_token = !options.notoken
|
url = options.url ||= config['url']
|
||||||
|
no_token = options.notoken
|
||||||
force = options.force
|
force = options.force
|
||||||
|
|
||||||
if args.empty?
|
if args.empty?
|
||||||
FloatyLogger.error 'No operating systems provided to obtain. See `floaty get --help` for more information on how to get VMs.'
|
STDERR.puts "No operating systems provided to obtain. See `floaty get --help` for more information on how to get VMs."
|
||||||
exit 1
|
exit 1
|
||||||
end
|
end
|
||||||
|
|
||||||
os_types = Utils.generate_os_hash(args)
|
os_types = Utils.generate_os_hash(args)
|
||||||
|
|
||||||
if os_types.empty?
|
|
||||||
FloatyLogger.error 'No operating systems provided to obtain. See `floaty get --help` for more information on how to get VMs.'
|
|
||||||
exit 1
|
|
||||||
end
|
|
||||||
|
|
||||||
max_pool_request = 5
|
max_pool_request = 5
|
||||||
large_pool_requests = os_types.select { |_, v| v > max_pool_request }
|
large_pool_requests = os_types.select{|k,v| v > max_pool_request}
|
||||||
if !large_pool_requests.empty? && !force
|
if ! large_pool_requests.empty? and ! force
|
||||||
FloatyLogger.error "Requesting vms over #{max_pool_request} requires a --force flag."
|
STDERR.puts "Requesting vms over #{max_pool_request} requires a --force flag."
|
||||||
FloatyLogger.error 'Try again with `floaty get --force`'
|
STDERR.puts "Try again with `floaty get --force`"
|
||||||
exit 1
|
exit 1
|
||||||
end
|
end
|
||||||
|
|
||||||
response = service.retrieve(verbose, os_types, use_token, options.ondemand, options.continue)
|
unless os_types.empty?
|
||||||
request_id = response['request_id'] if options.ondemand
|
if no_token
|
||||||
response = service.wait_for_request(verbose, request_id) if options.ondemand
|
begin
|
||||||
|
response = Pooler.retrieve(verbose, os_types, nil, url)
|
||||||
hosts = Utils.standardize_hostnames(response)
|
rescue MissingParamError
|
||||||
|
STDERR.puts e
|
||||||
if options.json || options.ondemand
|
STDERR.puts "See `floaty get --help` for more information on how to get VMs."
|
||||||
puts JSON.pretty_generate(hosts)
|
rescue AuthError => e
|
||||||
|
STDERR.puts e
|
||||||
|
exit 1
|
||||||
|
end
|
||||||
|
puts Utils.format_hosts(response)
|
||||||
|
exit 0
|
||||||
else
|
else
|
||||||
puts Utils.format_host_output(hosts)
|
unless token
|
||||||
|
puts "No token found. Retrieving a token..."
|
||||||
|
if !user
|
||||||
|
STDERR.puts "You did not provide a user to authenticate to vmpooler with"
|
||||||
|
exit 1
|
||||||
|
end
|
||||||
|
pass = password "Enter your password please:", '*'
|
||||||
|
begin
|
||||||
|
token = Auth.get_token(verbose, url, user, pass)
|
||||||
|
rescue TokenError => e
|
||||||
|
STDERR.puts e
|
||||||
|
exit 1
|
||||||
|
end
|
||||||
|
|
||||||
|
puts "\nToken retrieved!"
|
||||||
|
puts token
|
||||||
|
end
|
||||||
|
|
||||||
|
begin
|
||||||
|
response = Pooler.retrieve(verbose, os_types, token, url)
|
||||||
|
rescue MissingParamError
|
||||||
|
STDERR.puts e
|
||||||
|
STDERR.puts "See `floaty get --help` for more information on how to get VMs."
|
||||||
|
rescue AuthError => e
|
||||||
|
STDERR.puts e
|
||||||
|
exit 1
|
||||||
|
end
|
||||||
|
puts Utils.format_hosts(response)
|
||||||
|
exit 0
|
||||||
|
end
|
||||||
|
else
|
||||||
|
STDERR.puts "No operating systems provided to obtain. See `floaty get --help` for more information on how to get VMs."
|
||||||
|
exit 1
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
@ -85,52 +109,37 @@ class Vmfloaty
|
||||||
command :list do |c|
|
command :list do |c|
|
||||||
c.syntax = 'floaty list [options]'
|
c.syntax = 'floaty list [options]'
|
||||||
c.summary = 'Shows a list of available vms from the pooler or vms obtained with a token'
|
c.summary = 'Shows a list of available vms from the pooler or vms obtained with a token'
|
||||||
c.description = 'List will either show all vm templates available in pooler service, or with the --active flag it will list vms obtained with a pooler service token.'
|
c.description = 'List will either show all vm templates available in vmpooler, or with the --active flag it will list vms obtained with a vmpooler token.'
|
||||||
c.example 'Filter the list on centos', 'floaty list centos --url http://vmpooler.example.com'
|
c.example 'Filter the list on centos', 'floaty list centos --url http://vmpooler.example.com'
|
||||||
c.option '--verbose', 'Enables verbose output'
|
c.option '--verbose', 'Enables verbose output'
|
||||||
c.option '--service STRING', String, 'Configured pooler service name'
|
|
||||||
c.option '--active', 'Prints information about active vms for a given token'
|
c.option '--active', 'Prints information about active vms for a given token'
|
||||||
c.option '--json', 'Prints information as JSON'
|
c.option '--token STRING', String, 'Token for vmpooler'
|
||||||
c.option '--hostnameonly', 'When listing active vms, prints only hostnames, one per line'
|
c.option '--url STRING', String, 'URL of vmpooler'
|
||||||
c.option '--token STRING', String, 'Token for pooler service'
|
|
||||||
c.option '--url STRING', String, 'URL of pooler service'
|
|
||||||
c.option '--user STRING', String, 'User to authenticate with'
|
|
||||||
c.option '--loglevel STRING', String, 'the log level to use (debug, info, error)'
|
|
||||||
c.action do |args, options|
|
c.action do |args, options|
|
||||||
verbose = options.verbose || config['verbose']
|
verbose = options.verbose || config['verbose']
|
||||||
FloatyLogger.setlevel = options.loglevel if options.loglevel
|
|
||||||
|
|
||||||
service = Service.new(options, config)
|
|
||||||
filter = args[0]
|
filter = args[0]
|
||||||
|
url = options.url ||= config['url']
|
||||||
|
token = options.token || config['token']
|
||||||
|
active = options.active
|
||||||
|
|
||||||
if options.active
|
if active
|
||||||
# list active vms
|
# list active vms
|
||||||
running_vms = if service.type == 'ABS'
|
begin
|
||||||
# this is actually job_ids
|
running_vms = Utils.get_all_token_vms(verbose, url, token)
|
||||||
service.list_active_job_ids(verbose, service.url, service.user)
|
rescue TokenError => e
|
||||||
else
|
STDERR.puts e
|
||||||
service.list_active(verbose)
|
exit 1
|
||||||
|
rescue Exception => e
|
||||||
|
STDERR.puts e
|
||||||
|
exit 1
|
||||||
end
|
end
|
||||||
host = URI.parse(service.url).host
|
|
||||||
if running_vms.empty?
|
if ! running_vms.nil?
|
||||||
if options.json
|
Utils.prettyprint_hosts(running_vms, verbose, url)
|
||||||
puts {}.to_json
|
|
||||||
else
|
|
||||||
FloatyLogger.info "You have no running VMs on #{host}"
|
|
||||||
end
|
|
||||||
elsif options.json
|
|
||||||
puts Utils.get_host_data(verbose, service, running_vms).to_json
|
|
||||||
elsif options.hostnameonly
|
|
||||||
Utils.get_host_data(verbose, service, running_vms).each do |hostname, host_data|
|
|
||||||
Utils.print_fqdn_for_host(service, hostname, host_data)
|
|
||||||
end
|
|
||||||
else
|
|
||||||
puts "Your VMs on #{host}:"
|
|
||||||
Utils.pretty_print_hosts(verbose, service, running_vms)
|
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
# list available vms from pooler
|
# list available vms from pooler
|
||||||
os_list = service.list(verbose, filter)
|
os_list = Pooler.list(verbose, url, filter)
|
||||||
puts os_list
|
puts os_list
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
@ -139,78 +148,139 @@ class Vmfloaty
|
||||||
command :query do |c|
|
command :query do |c|
|
||||||
c.syntax = 'floaty query hostname [options]'
|
c.syntax = 'floaty query hostname [options]'
|
||||||
c.summary = 'Get information about a given vm'
|
c.summary = 'Get information about a given vm'
|
||||||
c.description = 'Given a hostname from the pooler service, vmfloaty with query the service to get various details about the vm. If using ABS, you can query a job_id'
|
c.description = 'Given a hostname from vmpooler, vmfloaty with query vmpooler to get various details about the vm.'
|
||||||
c.example 'Get information about a sample host', 'floaty query hostname --url http://vmpooler.example.com'
|
c.example 'Get information about a sample host', 'floaty query hostname --url http://vmpooler.example.com'
|
||||||
c.option '--verbose', 'Enables verbose output'
|
c.option '--verbose', 'Enables verbose output'
|
||||||
c.option '--service STRING', String, 'Configured pooler service name'
|
c.option '--url STRING', String, 'URL of vmpooler'
|
||||||
c.option '--url STRING', String, 'URL of pooler service'
|
|
||||||
c.action do |args, options|
|
c.action do |args, options|
|
||||||
verbose = options.verbose || config['verbose']
|
verbose = options.verbose || config['verbose']
|
||||||
service = Service.new(options, config)
|
url = options.url ||= config['url']
|
||||||
hostname = args[0]
|
hostname = args[0]
|
||||||
|
|
||||||
query_req = service.query(verbose, hostname)
|
query_req = Pooler.query(verbose, url, hostname)
|
||||||
pp query_req
|
pp query_req
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
command :modify do |c|
|
command :modify do |c|
|
||||||
c.syntax = 'floaty modify hostname [options]'
|
c.syntax = 'floaty modify hostname [options]'
|
||||||
c.summary = 'Modify a VM\'s tags, time to live, disk space, or reservation reason'
|
c.summary = 'Modify a vms tags, time to live, and disk space'
|
||||||
c.description = 'This command makes modifications to the virtual machines state in the pooler service. You can either append tags to the vm, increase how long it stays active for, or increase the amount of disk space.'
|
c.description = 'This command makes modifications to the virtual machines state in vmpooler. You can either append tags to the vm, increase how long it stays active for, or increase the amount of disk space.'
|
||||||
c.example 'Modifies myhost1 to have a TTL of 12 hours and adds a custom tag',
|
c.example 'Modifies myhost1 to have a TTL of 12 hours and adds a custom tag', 'floaty modify myhost1 --lifetime 12 --url https://myurl --token mytokenstring --tags \'{"tag":"myvalue"}\''
|
||||||
'floaty modify myhost1 --lifetime 12 --url https://myurl --token mytokenstring --tags \'{"tag":"myvalue"}\''
|
|
||||||
c.option '--verbose', 'Enables verbose output'
|
c.option '--verbose', 'Enables verbose output'
|
||||||
c.option '--service STRING', String, 'Configured pooler service name'
|
c.option '--url STRING', String, 'URL of vmpooler'
|
||||||
c.option '--url STRING', String, 'URL of pooler service'
|
c.option '--token STRING', String, 'Token for vmpooler'
|
||||||
c.option '--token STRING', String, 'Token for pooler service'
|
c.option '--lifetime INT', Integer, 'VM TTL (Integer, in hours)'
|
||||||
c.option '--lifetime INT', Integer, 'VM TTL (Integer, in hours) [vmpooler only]'
|
c.option '--disk INT', Integer, 'Increases VM disk space (Integer, in gb)'
|
||||||
c.option '--disk INT', Integer, 'Increases VM disk space (Integer, in gb) [vmpooler only]'
|
c.option '--tags STRING', String, 'free-form VM tagging (json)'
|
||||||
c.option '--tags STRING', String, 'free-form VM tagging (json) [vmpooler only]'
|
|
||||||
c.option '--reason STRING', String, 'VM reservation reason [nspooler only]'
|
|
||||||
c.option '--all', 'Modifies all vms acquired by a token'
|
c.option '--all', 'Modifies all vms acquired by a token'
|
||||||
c.action do |args, options|
|
c.action do |args, options|
|
||||||
verbose = options.verbose || config['verbose']
|
verbose = options.verbose || config['verbose']
|
||||||
service = Service.new(options, config)
|
url = options.url ||= config['url']
|
||||||
hostname = args[0]
|
hostname = args[0]
|
||||||
|
lifetime = options.lifetime
|
||||||
|
disk = options.disk
|
||||||
|
tags = JSON.parse(options.tags) if options.tags
|
||||||
|
token = options.token || config['token']
|
||||||
modify_all = options.all
|
modify_all = options.all
|
||||||
|
|
||||||
if hostname.nil? && !modify_all
|
running_vms = nil
|
||||||
FloatyLogger.error 'ERROR: Provide a hostname or specify --all.'
|
|
||||||
|
if modify_all
|
||||||
|
begin
|
||||||
|
running_vms = Utils.get_all_token_vms(verbose, url, token)
|
||||||
|
rescue Exception => e
|
||||||
|
STDERR.puts e
|
||||||
|
end
|
||||||
|
elsif hostname.include? ","
|
||||||
|
running_vms = hostname.split(",")
|
||||||
|
end
|
||||||
|
|
||||||
|
if lifetime || tags
|
||||||
|
# all vms
|
||||||
|
if !running_vms.nil?
|
||||||
|
begin
|
||||||
|
modify_hash = {}
|
||||||
|
modify_flag = true
|
||||||
|
|
||||||
|
running_vms.each do |vm|
|
||||||
|
modify_hash[vm] = Pooler.modify(verbose, url, vm, token, lifetime, tags)
|
||||||
|
end
|
||||||
|
|
||||||
|
modify_hash.each do |hostname,status|
|
||||||
|
if status == false
|
||||||
|
STDERR.puts "Could not modify #{hostname}."
|
||||||
|
modify_flag = false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if modify_flag
|
||||||
|
puts "Successfully modified all vms. Use `floaty list --active` to see the results."
|
||||||
|
end
|
||||||
|
rescue Exception => e
|
||||||
|
STDERR.puts e
|
||||||
exit 1
|
exit 1
|
||||||
end
|
end
|
||||||
running_vms =
|
|
||||||
if modify_all
|
|
||||||
service.list_active(verbose)
|
|
||||||
else
|
else
|
||||||
hostname.split(',')
|
# Single Vm
|
||||||
|
begin
|
||||||
|
modify_req = Pooler.modify(verbose, url, hostname, token, lifetime, tags)
|
||||||
|
rescue TokenError => e
|
||||||
|
STDERR.puts e
|
||||||
|
exit 1
|
||||||
end
|
end
|
||||||
|
|
||||||
tags = options.tags ? JSON.parse(options.tags) : nil
|
if modify_req["ok"]
|
||||||
modify_hash = {
|
puts "Successfully modified vm #{hostname}."
|
||||||
lifetime: options.lifetime,
|
else
|
||||||
disk: options.disk,
|
STDERR.puts "Could not modify given host #{hostname} at #{url}."
|
||||||
tags: tags,
|
puts modify_req
|
||||||
reason: options.reason
|
exit 1
|
||||||
}
|
end
|
||||||
modify_hash.delete_if { |_, value| value.nil? }
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if disk
|
||||||
|
# all vms
|
||||||
|
if !running_vms.nil?
|
||||||
|
begin
|
||||||
|
modify_hash = {}
|
||||||
|
modify_flag = true
|
||||||
|
|
||||||
unless modify_hash.empty?
|
|
||||||
ok = true
|
|
||||||
modified_hash = {}
|
|
||||||
running_vms.each do |vm|
|
running_vms.each do |vm|
|
||||||
modified_hash[vm] = service.modify(verbose, vm, modify_hash)
|
modify_hash[vm] = Pooler.disk(verbose, url, vm, token, disk)
|
||||||
rescue ModifyError => e
|
end
|
||||||
FloatyLogger.error e
|
|
||||||
ok = false
|
modify_hash.each do |hostname,status|
|
||||||
|
if status == false
|
||||||
|
STDERR.puts "Could not update disk space on #{hostname}."
|
||||||
|
modify_flag = false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if modify_flag
|
||||||
|
puts "Successfully made request to update disk space on all vms."
|
||||||
|
end
|
||||||
|
rescue Exception => e
|
||||||
|
STDERR.puts e
|
||||||
|
exit 1
|
||||||
end
|
end
|
||||||
if ok
|
|
||||||
if modify_all
|
|
||||||
puts "Successfully modified all #{running_vms.count} VMs."
|
|
||||||
else
|
else
|
||||||
puts "Successfully modified VM #{hostname}."
|
# single vm
|
||||||
|
begin
|
||||||
|
disk_req = Pooler.disk(verbose, url, hostname, token, disk)
|
||||||
|
rescue TokenError => e
|
||||||
|
STDERR.puts e
|
||||||
|
exit 1
|
||||||
|
end
|
||||||
|
|
||||||
|
if disk_req["ok"]
|
||||||
|
puts "Successfully made request to update disk space of vm #{hostname}."
|
||||||
|
else
|
||||||
|
STDERR.puts "Could not modify given host #{hostname} at #{url}."
|
||||||
|
puts disk_req
|
||||||
|
exit 1
|
||||||
end
|
end
|
||||||
puts 'Use `floaty list --active` to see the results.'
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
@ -218,122 +288,97 @@ class Vmfloaty
|
||||||
|
|
||||||
command :delete do |c|
|
command :delete do |c|
|
||||||
c.syntax = 'floaty delete hostname,hostname2 [options]'
|
c.syntax = 'floaty delete hostname,hostname2 [options]'
|
||||||
c.syntax += "\n floaty delete job1,job2 [options] (only supported with ABS)"
|
|
||||||
c.summary = 'Schedules the deletion of a host or hosts'
|
c.summary = 'Schedules the deletion of a host or hosts'
|
||||||
c.description = 'Given a comma separated list of hostnames, or --all for all vms, vmfloaty makes a request to the pooler service to schedule the deletion of those vms. If you are using the ABS service, you can also pass in JobIDs here. Note that passing in a Job ID will delete *all* of the hosts in the job.' # rubocop:disable Layout/LineLength
|
c.description = 'Given a comma separated list of hostnames, or --all for all vms, vmfloaty makes a request to vmpooler to schedule the deletion of those vms.'
|
||||||
c.example 'Schedules the deletion of a host or hosts', 'floaty delete myhost1,myhost2 --url http://vmpooler.example.com'
|
c.example 'Schedules the deletion of a host or hosts', 'floaty delete myhost1,myhost2 --url http://vmpooler.example.com'
|
||||||
c.example 'Schedules the deletion of a JobID or JobIDs', 'floaty delete 1579300120799,1579300120800 --url http://abs.example.com'
|
|
||||||
c.option '--verbose', 'Enables verbose output'
|
c.option '--verbose', 'Enables verbose output'
|
||||||
c.option '--service STRING', String, 'Configured pooler service name'
|
|
||||||
c.option '--all', 'Deletes all vms acquired by a token'
|
c.option '--all', 'Deletes all vms acquired by a token'
|
||||||
c.option '-f', 'Does not prompt user when deleting all vms'
|
c.option '-f', 'Does not prompt user when deleting all vms'
|
||||||
c.option '--json', 'Outputs hosts scheduled for deletion as JSON'
|
c.option '--token STRING', String, 'Token for vmpooler'
|
||||||
c.option '--token STRING', String, 'Token for pooler service'
|
c.option '--url STRING', String, 'URL of vmpooler'
|
||||||
c.option '--url STRING', String, 'URL of pooler service'
|
|
||||||
c.option '--user STRING', String, 'User to authenticate with'
|
|
||||||
c.option '--loglevel STRING', String, 'the log level to use (debug, info, error)'
|
|
||||||
c.action do |args, options|
|
c.action do |args, options|
|
||||||
verbose = options.verbose || config['verbose']
|
verbose = options.verbose || config['verbose']
|
||||||
FloatyLogger.setlevel = options.loglevel if options.loglevel
|
|
||||||
|
|
||||||
service = Service.new(options, config)
|
|
||||||
hostnames = args[0]
|
hostnames = args[0]
|
||||||
|
token = options.token || config['token']
|
||||||
|
url = options.url ||= config['url']
|
||||||
delete_all = options.all
|
delete_all = options.all
|
||||||
force = options.f
|
force = options.f
|
||||||
|
|
||||||
failures = []
|
|
||||||
successes = []
|
|
||||||
|
|
||||||
if delete_all
|
if delete_all
|
||||||
running_vms = if service.type == 'ABS'
|
# get vms with token
|
||||||
# this is actually job_ids
|
begin
|
||||||
service.list_active_job_ids(verbose, service.url, service.user)
|
running_vms = Utils.get_all_token_vms(verbose, url, token)
|
||||||
else
|
rescue TokenError => e
|
||||||
service.list_active(verbose)
|
STDERR.puts e
|
||||||
end
|
exit 1
|
||||||
if running_vms.empty?
|
rescue Exception => e
|
||||||
if options.json
|
STDERR.puts e
|
||||||
puts {}.to_json
|
|
||||||
else
|
|
||||||
FloatyLogger.info 'You have no running VMs.'
|
|
||||||
end
|
|
||||||
else
|
|
||||||
confirmed = true
|
|
||||||
unless force
|
|
||||||
Utils.pretty_print_hosts(verbose, service, running_vms, true)
|
|
||||||
# Confirm deletion
|
|
||||||
confirmed = agree('Delete all these VMs? [y/N]')
|
|
||||||
end
|
|
||||||
if confirmed
|
|
||||||
response = service.delete(verbose, running_vms)
|
|
||||||
response.each do |hostname, result|
|
|
||||||
if result['ok']
|
|
||||||
successes << hostname
|
|
||||||
else
|
|
||||||
failures << hostname
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
elsif hostnames || args
|
|
||||||
hostnames = hostnames.split(',')
|
|
||||||
results = service.delete(verbose, hostnames)
|
|
||||||
results.each do |hostname, result|
|
|
||||||
if result['ok']
|
|
||||||
successes << hostname
|
|
||||||
else
|
|
||||||
failures << hostname
|
|
||||||
end
|
|
||||||
end
|
|
||||||
else
|
|
||||||
FloatyLogger.info 'You did not provide any hosts to delete'
|
|
||||||
exit 1
|
exit 1
|
||||||
end
|
end
|
||||||
|
|
||||||
unless failures.empty?
|
if ! running_vms.nil?
|
||||||
FloatyLogger.info 'Unable to delete the following VMs:'
|
Utils.prettyprint_hosts(running_vms, verbose, url)
|
||||||
failures.each do |hostname|
|
# query y/n
|
||||||
FloatyLogger.info "- #{hostname}"
|
puts
|
||||||
end
|
|
||||||
FloatyLogger.info 'Check `floaty list --active`; Do you need to specify a different service?'
|
|
||||||
end
|
|
||||||
|
|
||||||
unless successes.empty?
|
if force
|
||||||
if options.json
|
ans = true
|
||||||
puts successes.to_json
|
|
||||||
else
|
else
|
||||||
puts 'Scheduled the following VMs for deletion:'
|
ans = agree("Delete all VMs associated with token #{token}? [y/N]")
|
||||||
output = ''
|
end
|
||||||
successes.each do |hostname|
|
|
||||||
output += "- #{hostname}\n"
|
if ans
|
||||||
|
# delete vms
|
||||||
|
puts "Scheduling all vms for for deletion"
|
||||||
|
response = Pooler.delete(verbose, url, running_vms, token)
|
||||||
|
response.each do |host,vals|
|
||||||
|
if vals['ok'] == false
|
||||||
|
STDERR.puts "There was a problem with your request for vm #{host}."
|
||||||
|
STDERR.puts vals
|
||||||
|
end
|
||||||
end
|
end
|
||||||
puts output
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
exit 1 unless failures.empty?
|
exit 0
|
||||||
|
end
|
||||||
|
|
||||||
|
if hostnames.nil?
|
||||||
|
STDERR.puts "You did not provide any hosts to delete"
|
||||||
|
exit 1
|
||||||
|
else
|
||||||
|
hosts = hostnames.split(',')
|
||||||
|
begin
|
||||||
|
Pooler.delete(verbose, url, hosts, token)
|
||||||
|
rescue TokenError => e
|
||||||
|
STDERR.puts e
|
||||||
|
exit 1
|
||||||
|
end
|
||||||
|
|
||||||
|
puts "Schedulered vmpooler to delete vms #{hosts}."
|
||||||
|
exit 0
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
command :snapshot do |c|
|
command :snapshot do |c|
|
||||||
c.syntax = 'floaty snapshot hostname [options]'
|
c.syntax = 'floaty snapshot hostname [options]'
|
||||||
c.summary = 'Takes a snapshot of a given vm'
|
c.summary = 'Takes a snapshot of a given vm'
|
||||||
c.description = 'Will request a snapshot be taken of the given hostname in the pooler service. This command is known to take a while depending on how much load is on the pooler service.'
|
c.description = 'Will request a snapshot be taken of the given hostname in vmpooler. This command is known to take a while depending on how much load is on vmpooler.'
|
||||||
c.example 'Takes a snapshot for a given host',
|
c.example 'Takes a snapshot for a given host', 'floaty snapshot myvm.example.com --url http://vmpooler.example.com --token a9znth9dn01t416hrguu56ze37t790bl'
|
||||||
'floaty snapshot myvm.example.com --url http://vmpooler.example.com --token a9znth9dn01t416hrguu56ze37t790bl'
|
|
||||||
c.option '--verbose', 'Enables verbose output'
|
c.option '--verbose', 'Enables verbose output'
|
||||||
c.option '--service STRING', String, 'Configured pooler service name'
|
c.option '--url STRING', String, 'URL of vmpooler'
|
||||||
c.option '--url STRING', String, 'URL of pooler service'
|
c.option '--token STRING', String, 'Token for vmpooler'
|
||||||
c.option '--token STRING', String, 'Token for pooler service'
|
|
||||||
c.action do |args, options|
|
c.action do |args, options|
|
||||||
verbose = options.verbose || config['verbose']
|
verbose = options.verbose || config['verbose']
|
||||||
service = Service.new(options, config)
|
url = options.url ||= config['url']
|
||||||
hostname = args[0]
|
hostname = args[0]
|
||||||
|
token = options.token ||= config['token']
|
||||||
|
|
||||||
begin
|
begin
|
||||||
snapshot_req = service.snapshot(verbose, hostname)
|
snapshot_req = Pooler.snapshot(verbose, url, hostname, token)
|
||||||
rescue TokenError, ModifyError => e
|
rescue TokenError => e
|
||||||
FloatyLogger.error e
|
STDERR.puts e
|
||||||
exit 1
|
exit 1
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
@ -345,28 +390,27 @@ class Vmfloaty
|
||||||
command :revert do |c|
|
command :revert do |c|
|
||||||
c.syntax = 'floaty revert hostname snapshot [options]'
|
c.syntax = 'floaty revert hostname snapshot [options]'
|
||||||
c.summary = 'Reverts a vm to a specified snapshot'
|
c.summary = 'Reverts a vm to a specified snapshot'
|
||||||
c.description = 'Given a snapshot SHA, vmfloaty will request a revert to the pooler service to go back to a previous snapshot.'
|
c.description = 'Given a snapshot SHA, vmfloaty will request a revert to vmpooler to go back to a previous snapshot.'
|
||||||
c.example 'Reverts to a snapshot for a given host',
|
c.example 'Reverts to a snapshot for a given host', 'floaty revert myvm.example.com n4eb4kdtp7rwv4x158366vd9jhac8btq --url http://vmpooler.example.com --token a9znth9dn01t416hrguu56ze37t790bl'
|
||||||
'floaty revert myvm.example.com n4eb4kdtp7rwv4x158366vd9jhac8btq --url http://vmpooler.example.com --token a9znth9dn01t416hrguu56ze37t790bl'
|
|
||||||
c.option '--verbose', 'Enables verbose output'
|
c.option '--verbose', 'Enables verbose output'
|
||||||
c.option '--service STRING', String, 'Configured pooler service name'
|
c.option '--url STRING', String, 'URL of vmpooler'
|
||||||
c.option '--url STRING', String, 'URL of pooler service'
|
c.option '--token STRING', String, 'Token for vmpooler'
|
||||||
c.option '--token STRING', String, 'Token for pooler service'
|
|
||||||
c.option '--snapshot STRING', String, 'SHA of snapshot'
|
c.option '--snapshot STRING', String, 'SHA of snapshot'
|
||||||
c.action do |args, options|
|
c.action do |args, options|
|
||||||
verbose = options.verbose || config['verbose']
|
verbose = options.verbose || config['verbose']
|
||||||
service = Service.new(options, config)
|
url = options.url ||= config['url']
|
||||||
hostname = args[0]
|
hostname = args[0]
|
||||||
|
token = options.token || config['token']
|
||||||
snapshot_sha = args[1] || options.snapshot
|
snapshot_sha = args[1] || options.snapshot
|
||||||
|
|
||||||
if args[1] && options.snapshot
|
if args[1] && options.snapshot
|
||||||
FloatyLogger.info "Two snapshot arguments were given....using snapshot #{snapshot_sha}"
|
STDERR.puts "Two snapshot arguments were given....using snapshot #{snapshot_sha}"
|
||||||
end
|
end
|
||||||
|
|
||||||
begin
|
begin
|
||||||
revert_req = service.revert(verbose, hostname, snapshot_sha)
|
revert_req = Pooler.revert(verbose, url, hostname, token, snapshot_sha)
|
||||||
rescue TokenError, ModifyError => e
|
rescue TokenError => e
|
||||||
FloatyLogger.error e
|
STDERR.puts e
|
||||||
exit 1
|
exit 1
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
@ -376,39 +420,42 @@ class Vmfloaty
|
||||||
|
|
||||||
command :status do |c|
|
command :status do |c|
|
||||||
c.syntax = 'floaty status [options]'
|
c.syntax = 'floaty status [options]'
|
||||||
c.summary = 'Prints the status of pools in the pooler service'
|
c.summary = 'Prints the status of pools in vmpooler'
|
||||||
c.description = 'Makes a request to the pooler service to request the information about vm pools and how many are ready to be used, what pools are empty, etc.'
|
c.description = 'Makes a request to vmpooler to request the information about vm pools and how many are ready to be used, what pools are empty, etc.'
|
||||||
c.example 'Gets the current pooler service status', 'floaty status --url http://vmpooler.example.com'
|
c.example 'Gets the current vmpooler status', 'floaty status --url http://vmpooler.example.com'
|
||||||
c.option '--verbose', 'Enables verbose output'
|
c.option '--verbose', 'Enables verbose output'
|
||||||
c.option '--service STRING', String, 'Configured pooler service name'
|
c.option '--url STRING', String, 'URL of vmpooler'
|
||||||
c.option '--url STRING', String, 'URL of pooler service'
|
|
||||||
c.option '--json', 'Prints status in JSON format'
|
c.option '--json', 'Prints status in JSON format'
|
||||||
c.option '--loglevel STRING', String, 'the log level to use (debug, info, error)'
|
c.action do |args, options|
|
||||||
c.action do |_, options|
|
|
||||||
verbose = options.verbose || config['verbose']
|
verbose = options.verbose || config['verbose']
|
||||||
FloatyLogger.setlevel = options.loglevel if options.loglevel
|
url = options.url ||= config['url']
|
||||||
service = Service.new(options, config)
|
|
||||||
|
status = Pooler.status(verbose, url)
|
||||||
|
message = status['status']['message']
|
||||||
|
pools = status['pools']
|
||||||
|
|
||||||
if options.json
|
if options.json
|
||||||
pp service.status(verbose)
|
pp status
|
||||||
else
|
else
|
||||||
Utils.pretty_print_status(verbose, service)
|
Utils.prettyprint_status(status, message, pools, verbose)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
exit status['status']['ok']
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
command :summary do |c|
|
command :summary do |c|
|
||||||
c.syntax = 'floaty summary [options]'
|
c.syntax = 'floaty summary [options]'
|
||||||
c.summary = 'Prints a summary of a pooler service'
|
c.summary = 'Prints a summary of vmpooler'
|
||||||
c.description = 'Gives a very detailed summary of information related to the pooler service.'
|
c.description = 'Gives a very detailed summary of information related to vmpooler.'
|
||||||
c.example 'Gets the current day summary of the pooler service', 'floaty summary --url http://vmpooler.example.com'
|
c.example 'Gets the current day summary of vmpooler', 'floaty summary --url http://vmpooler.example.com'
|
||||||
c.option '--verbose', 'Enables verbose output'
|
c.option '--verbose', 'Enables verbose output'
|
||||||
c.option '--service STRING', String, 'Configured pooler service name'
|
c.option '--url STRING', String, 'URL of vmpooler'
|
||||||
c.option '--url STRING', String, 'URL of pooler service'
|
c.action do |args, options|
|
||||||
c.action do |_, options|
|
|
||||||
verbose = options.verbose || config['verbose']
|
verbose = options.verbose || config['verbose']
|
||||||
service = Service.new(options, config)
|
url = options.url ||= config['url']
|
||||||
|
|
||||||
summary = service.summary(verbose)
|
summary = Pooler.summary(verbose, url)
|
||||||
pp summary
|
pp summary
|
||||||
exit 0
|
exit 0
|
||||||
end
|
end
|
||||||
|
|
@ -417,43 +464,54 @@ class Vmfloaty
|
||||||
command :token do |c|
|
command :token do |c|
|
||||||
c.syntax = 'floaty token <get delete status> [options]'
|
c.syntax = 'floaty token <get delete status> [options]'
|
||||||
c.summary = 'Retrieves or deletes a token or checks token status'
|
c.summary = 'Retrieves or deletes a token or checks token status'
|
||||||
c.description = 'This command is used to manage your pooler service token. Through the various options, you are able to get a new token, delete an existing token, and request a tokens status.'
|
c.description = 'This command is used to manage your vmpooler token. Through the various options, you are able to get a new token, delete an existing token, and request a tokens status.'
|
||||||
c.example 'Gets a token from the pooler', 'floaty token get'
|
c.example 'Gets a token from the pooler', 'floaty token get'
|
||||||
c.option '--verbose', 'Enables verbose output'
|
c.option '--verbose', 'Enables verbose output'
|
||||||
c.option '--service STRING', String, 'Configured pooler service name'
|
c.option '--url STRING', String, 'URL of vmpooler'
|
||||||
c.option '--url STRING', String, 'URL of pooler service'
|
|
||||||
c.option '--user STRING', String, 'User to authenticate with'
|
c.option '--user STRING', String, 'User to authenticate with'
|
||||||
c.option '--token STRING', String, 'Token for pooler service'
|
c.option '--token STRING', String, 'Token for vmpooler'
|
||||||
c.action do |args, options|
|
c.action do |args, options|
|
||||||
verbose = options.verbose || config['verbose']
|
verbose = options.verbose || config['verbose']
|
||||||
service = Service.new(options, config)
|
|
||||||
action = args.first
|
action = args.first
|
||||||
|
url = options.url ||= config['url']
|
||||||
|
token = args[1] ||= options.token ||= config['token']
|
||||||
|
user = options.user ||= config['user']
|
||||||
|
|
||||||
begin
|
|
||||||
case action
|
case action
|
||||||
when 'get'
|
when "get"
|
||||||
token = service.get_new_token(verbose)
|
pass = password "Enter your password please:", '*'
|
||||||
puts token
|
begin
|
||||||
when 'delete'
|
token = Auth.get_token(verbose, url, user, pass)
|
||||||
result = service.delete_token(verbose, options.token)
|
|
||||||
puts result
|
|
||||||
when 'status'
|
|
||||||
token_value = options.token
|
|
||||||
token_value = args[1] if token_value.nil?
|
|
||||||
status = service.token_status(verbose, token_value)
|
|
||||||
puts status
|
|
||||||
when nil
|
|
||||||
FloatyLogger.error 'No action provided'
|
|
||||||
exit 1
|
|
||||||
else
|
|
||||||
FloatyLogger.error "Unknown action: #{action}"
|
|
||||||
exit 1
|
|
||||||
end
|
|
||||||
rescue TokenError => e
|
rescue TokenError => e
|
||||||
FloatyLogger.error e
|
STDERR.puts e
|
||||||
exit 1
|
exit 1
|
||||||
end
|
end
|
||||||
|
puts token
|
||||||
exit 0
|
exit 0
|
||||||
|
when "delete"
|
||||||
|
pass = password "Enter your password please:", '*'
|
||||||
|
begin
|
||||||
|
result = Auth.delete_token(verbose, url, user, pass, token)
|
||||||
|
rescue TokenError => e
|
||||||
|
STDERR.puts e
|
||||||
|
exit 1
|
||||||
|
end
|
||||||
|
puts result
|
||||||
|
exit 0
|
||||||
|
when "status"
|
||||||
|
begin
|
||||||
|
status = Auth.token_status(verbose, url, token)
|
||||||
|
rescue TokenError => e
|
||||||
|
STDERR.puts e
|
||||||
|
exit 1
|
||||||
|
end
|
||||||
|
puts status
|
||||||
|
exit 0
|
||||||
|
when nil
|
||||||
|
STDERR.puts "No action provided"
|
||||||
|
else
|
||||||
|
STDERR.puts "Unknown action: #{action}"
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
@ -463,119 +521,44 @@ class Vmfloaty
|
||||||
c.description = 'This command simply will grab a vm template that was requested, and then ssh the user into the machine all at once.'
|
c.description = 'This command simply will grab a vm template that was requested, and then ssh the user into the machine all at once.'
|
||||||
c.example 'SSHs into a centos vm', 'floaty ssh centos7 --url https://vmpooler.example.com'
|
c.example 'SSHs into a centos vm', 'floaty ssh centos7 --url https://vmpooler.example.com'
|
||||||
c.option '--verbose', 'Enables verbose output'
|
c.option '--verbose', 'Enables verbose output'
|
||||||
c.option '--service STRING', String, 'Configured pooler service name'
|
c.option '--url STRING', String, 'URL of vmpooler'
|
||||||
c.option '--url STRING', String, 'URL of pooler service'
|
|
||||||
c.option '--user STRING', String, 'User to authenticate with'
|
c.option '--user STRING', String, 'User to authenticate with'
|
||||||
c.option '--token STRING', String, 'Token for pooler service'
|
c.option '--token STRING', String, 'Token for vmpooler'
|
||||||
c.option '--notoken', 'Makes a request without a token'
|
c.option '--notoken', 'Makes a request without a token'
|
||||||
c.option '--priority STRING', 'Priority for supported backends(ABS) (High(1), Medium(2), Low(3))'
|
|
||||||
c.option '--ondemand', 'Requested vms are provisioned upon receival of the request, tracked by a request ID'
|
|
||||||
c.action do |args, options|
|
c.action do |args, options|
|
||||||
verbose = options.verbose || config['verbose']
|
verbose = options.verbose || config['verbose']
|
||||||
service = Service.new(options, config)
|
url = options.url ||= config['url']
|
||||||
use_token = !options.notoken
|
token = options.token ||= config['token']
|
||||||
|
user = options.user ||= config['user']
|
||||||
|
no_token = options.notoken
|
||||||
|
|
||||||
if args.empty?
|
if args.empty?
|
||||||
FloatyLogger.error 'No operating systems provided to obtain. See `floaty ssh --help` for more information on how to get VMs.'
|
STDERR.puts "No operating systems provided to obtain. See `floaty ssh --help` for more information on how to get VMs."
|
||||||
exit 1
|
exit 1
|
||||||
end
|
end
|
||||||
|
|
||||||
host_os = args.first
|
host_os = args.first
|
||||||
|
|
||||||
FloatyLogger.info "Can't ssh to multiple hosts; Using #{host_os} only..." if args.length > 1
|
if !no_token && !token
|
||||||
|
puts "No token found. Retrieving a token..."
|
||||||
service.ssh(verbose, host_os, use_token, options.ondemand)
|
if !user
|
||||||
exit 0
|
STDERR.puts "You did not provide a user to authenticate to vmpooler with"
|
||||||
|
exit 1
|
||||||
end
|
end
|
||||||
end
|
pass = password "Enter your password please:", '*'
|
||||||
|
begin
|
||||||
command :completion do |c|
|
token = Auth.get_token(verbose, url, user, pass)
|
||||||
c.syntax = 'floaty completion [options]'
|
rescue TokenError => e
|
||||||
c.summary = 'Outputs path to completion script'
|
STDERR.puts e
|
||||||
c.description = Utils.strip_heredoc(<<-DESCRIPTION)
|
STDERR.puts 'Could not get token...requesting vm without a token anyway...'
|
||||||
Outputs path to a completion script for the specified shell (or 'bash' if not specified). This makes it easy to add the completion script to your profile:
|
|
||||||
|
|
||||||
source $(floaty completion --shell bash)
|
|
||||||
|
|
||||||
This subcommand will exit non-zero with an error message if no completion script is available for the requested shell.
|
|
||||||
DESCRIPTION
|
|
||||||
c.example 'Gets path to bash tab completion script', 'floaty completion --shell bash'
|
|
||||||
c.option '--shell STRING', String, 'Shell to request completion script for'
|
|
||||||
c.action do |_, options|
|
|
||||||
shell = (options.shell || 'bash').downcase.strip
|
|
||||||
completion_file = File.expand_path(File.join('..', '..', 'extras', 'completions', "floaty.#{shell}"), __FILE__)
|
|
||||||
|
|
||||||
if File.exist?(completion_file)
|
|
||||||
puts completion_file
|
|
||||||
exit 0
|
|
||||||
else
|
else
|
||||||
FloatyLogger.error "Could not find completion file for '#{shell}': No such file #{completion_file}"
|
puts "\nToken retrieved!"
|
||||||
exit 1
|
puts token
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
command :service do |c|
|
Ssh.ssh(verbose, host_os, token, url)
|
||||||
c.syntax = 'floaty service <types examples>'
|
exit 0
|
||||||
c.summary = 'Display information about floaty services and their configuration'
|
|
||||||
c.description = 'Display information about floaty services to aid in setting up a configuration file.'
|
|
||||||
c.example 'Print a list of the valid service types', 'floaty service types'
|
|
||||||
c.example 'Print a sample config file with multiple services', 'floaty service examples'
|
|
||||||
c.example 'list vms from the service named "nspooler-prod"', 'floaty list --service nspooler-prod'
|
|
||||||
c.action do |args, _options|
|
|
||||||
action = args.first
|
|
||||||
|
|
||||||
example_config = Utils.strip_heredoc(<<-CONFIG)
|
|
||||||
# Sample ~/.vmfloaty.yml with just vmpooler
|
|
||||||
user: 'jdoe'
|
|
||||||
url: 'https://vmpooler.example.net'
|
|
||||||
token: '456def789'
|
|
||||||
|
|
||||||
# Sample ~/.vmfloaty.yml with multiple services
|
|
||||||
# Note: when the --service is not specified on the command line,
|
|
||||||
# the first service listed here is selected automatically
|
|
||||||
user: 'jdoe'
|
|
||||||
services:
|
|
||||||
abs-prod:
|
|
||||||
type: 'abs'
|
|
||||||
url: 'https://abs.example.net/api/v2'
|
|
||||||
token: '123abc456'
|
|
||||||
vmpooler_fallback: 'vmpooler-prod'
|
|
||||||
nspooler-prod:
|
|
||||||
type: 'nspooler'
|
|
||||||
url: 'https://nspooler.example.net'
|
|
||||||
token: '789ghi012'
|
|
||||||
vmpooler-dev:
|
|
||||||
type: 'vmpooler'
|
|
||||||
url: 'https://vmpooler-dev.example.net'
|
|
||||||
token: '987dsa654'
|
|
||||||
vmpooler-prod:
|
|
||||||
type: 'vmpooler'
|
|
||||||
url: 'https://vmpooler.example.net'
|
|
||||||
token: '456def789'
|
|
||||||
|
|
||||||
CONFIG
|
|
||||||
|
|
||||||
types_output = Utils.strip_heredoc(<<-TYPES)
|
|
||||||
The values on the left below can be used in ~/.vmfloaty.yml as the value of type:
|
|
||||||
|
|
||||||
abs: Puppet's Always Be Scheduling
|
|
||||||
nspooler: Puppet's Non-standard Pooler, aka NSPooler
|
|
||||||
vmpooler: Puppet's VMPooler
|
|
||||||
TYPES
|
|
||||||
|
|
||||||
case action
|
|
||||||
when 'examples'
|
|
||||||
FloatyLogger.info example_config
|
|
||||||
when 'types'
|
|
||||||
FloatyLogger.info types_output
|
|
||||||
when nil
|
|
||||||
FloatyLogger.error 'No action provided'
|
|
||||||
exit 1
|
|
||||||
else
|
|
||||||
FloatyLogger.error "Unknown action: #{action}"
|
|
||||||
exit 1
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,422 +0,0 @@
|
||||||
# frozen_string_literal: true
|
|
||||||
|
|
||||||
require 'vmfloaty/errors'
|
|
||||||
require 'vmfloaty/http'
|
|
||||||
require 'vmfloaty/utils'
|
|
||||||
require 'faraday'
|
|
||||||
require 'json'
|
|
||||||
|
|
||||||
class ABS
|
|
||||||
# List active VMs in ABS
|
|
||||||
# This is what a job request looks like:
|
|
||||||
# {
|
|
||||||
# "state":"filled",
|
|
||||||
# "last_processed":"2019-10-31 20:59:33 +0000",
|
|
||||||
# "allocated_resources": [
|
|
||||||
# {
|
|
||||||
# "hostname":"h3oyntawjm7xdch.delivery.puppetlabs.net",
|
|
||||||
# "type":"centos-7.2-tmpfs-x86_64",
|
|
||||||
# "engine":"vmpooler"}
|
|
||||||
# ],
|
|
||||||
# "audit_log":{
|
|
||||||
# "2019-10-30 20:33:12 +0000":"Allocated h3oyntawjm7xdch.delivery.puppetlabs.net for job 1572467589"
|
|
||||||
# },
|
|
||||||
# "request":{
|
|
||||||
# "resources":{
|
|
||||||
# "centos-7.2-tmpfs-x86_64":1
|
|
||||||
# },
|
|
||||||
# "job": {
|
|
||||||
# "id":1572467589,
|
|
||||||
# "tags": {
|
|
||||||
# "user":"mikker",
|
|
||||||
# "url_string":"floaty://mikker/1572467589"
|
|
||||||
# },
|
|
||||||
# "user":"mikker",
|
|
||||||
# "time-received":1572467589
|
|
||||||
# }
|
|
||||||
# }
|
|
||||||
# }
|
|
||||||
#
|
|
||||||
@active_hostnames = {}
|
|
||||||
|
|
||||||
def self.list_active_job_ids(verbose, url, user)
|
|
||||||
all_job_ids = []
|
|
||||||
@active_hostnames = {}
|
|
||||||
get_active_requests(verbose, url, user).each do |req_hash|
|
|
||||||
@active_hostnames[req_hash['request']['job']['id']] = req_hash # full hash saved for later retrieval
|
|
||||||
all_job_ids.push(req_hash['request']['job']['id'])
|
|
||||||
end
|
|
||||||
|
|
||||||
all_job_ids
|
|
||||||
end
|
|
||||||
|
|
||||||
def self.list_active(verbose, url, _token, user)
|
|
||||||
hosts = []
|
|
||||||
get_active_requests(verbose, url, user).each do |req_hash|
|
|
||||||
next unless req_hash.key?('allocated_resources')
|
|
||||||
|
|
||||||
req_hash['allocated_resources'].each do |onehost|
|
|
||||||
hosts.push(onehost['hostname'])
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
hosts
|
|
||||||
end
|
|
||||||
|
|
||||||
def self.get_active_requests(verbose, url, user)
|
|
||||||
conn = Http.get_conn(verbose, supported_abs_url(url))
|
|
||||||
res = conn.get 'status/queue'
|
|
||||||
if valid_json?(res.body)
|
|
||||||
requests = JSON.parse(res.body)
|
|
||||||
else
|
|
||||||
FloatyLogger.warn "Warning: couldn't parse body returned from abs/status/queue"
|
|
||||||
end
|
|
||||||
|
|
||||||
ret_val = []
|
|
||||||
|
|
||||||
requests.each do |req|
|
|
||||||
next if req == 'null'
|
|
||||||
|
|
||||||
if valid_json?(req) # legacy ABS had another JSON string always-be-scheduling/pull/306
|
|
||||||
req_hash = JSON.parse(req)
|
|
||||||
elsif req.is_a?(Hash)
|
|
||||||
req_hash = req
|
|
||||||
else
|
|
||||||
FloatyLogger.warn "Warning: couldn't parse request returned from abs/status/queue"
|
|
||||||
next
|
|
||||||
end
|
|
||||||
|
|
||||||
begin
|
|
||||||
next unless user == req_hash['request']['job']['user']
|
|
||||||
|
|
||||||
ret_val.push(req_hash)
|
|
||||||
rescue NoMethodError
|
|
||||||
FloatyLogger.warn "Warning: couldn't parse user returned from abs/status/queue: "
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
ret_val
|
|
||||||
end
|
|
||||||
|
|
||||||
def self.all_job_resources_accounted_for(allocated_resources, hosts)
|
|
||||||
allocated_host_list = allocated_resources.map { |ar| ar['hostname'] }
|
|
||||||
(allocated_host_list - hosts).empty?
|
|
||||||
end
|
|
||||||
|
|
||||||
def self.delete(verbose, url, hosts, token, user)
|
|
||||||
# In ABS terms, this is a "returned" host.
|
|
||||||
conn = Http.get_conn(verbose, supported_abs_url(url))
|
|
||||||
conn.headers['X-AUTH-TOKEN'] = token if token
|
|
||||||
|
|
||||||
FloatyLogger.info "Trying to delete hosts #{hosts}" if verbose
|
|
||||||
requests = get_active_requests(verbose, url, user)
|
|
||||||
|
|
||||||
jobs_to_delete = []
|
|
||||||
|
|
||||||
ret_status = {}
|
|
||||||
hosts.each do |host|
|
|
||||||
ret_status[host] = {
|
|
||||||
'ok' => false
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
requests.each do |req_hash|
|
|
||||||
next unless req_hash['state'] == 'allocated' || req_hash['state'] == 'filled'
|
|
||||||
|
|
||||||
if hosts.include? req_hash['request']['job']['id']
|
|
||||||
jobs_to_delete.push(req_hash)
|
|
||||||
next
|
|
||||||
end
|
|
||||||
|
|
||||||
req_hash['allocated_resources'].each do |vm_name, _i|
|
|
||||||
if hosts.include? vm_name['hostname']
|
|
||||||
if all_job_resources_accounted_for(req_hash['allocated_resources'], hosts)
|
|
||||||
ret_status[vm_name['hostname']] = {
|
|
||||||
'ok' => true
|
|
||||||
}
|
|
||||||
jobs_to_delete.push(req_hash)
|
|
||||||
else
|
|
||||||
FloatyLogger.info "When using ABS you must delete all vms that you requested at the same time: Can't delete #{req_hash['request']['job']['id']}: #{hosts} does not include all of #{req_hash['allocated_resources']}"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
response_body = {}
|
|
||||||
|
|
||||||
jobs_to_delete.each do |job|
|
|
||||||
req_obj = {
|
|
||||||
'job_id' => job['request']['job']['id'],
|
|
||||||
'hosts' => job['allocated_resources']
|
|
||||||
}
|
|
||||||
|
|
||||||
FloatyLogger.info "Deleting #{req_obj}" if verbose
|
|
||||||
|
|
||||||
return_result = conn.post 'return', req_obj.to_json
|
|
||||||
req_obj['hosts'].each do |host|
|
|
||||||
response_body[host['hostname']] = { 'ok' => true } if return_result.body == 'OK'
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
response_body
|
|
||||||
end
|
|
||||||
|
|
||||||
# List available VMs in ABS
|
|
||||||
def self.list(verbose, url, os_filter = nil)
|
|
||||||
conn = Http.get_conn(verbose, supported_abs_url(url))
|
|
||||||
|
|
||||||
os_list = []
|
|
||||||
|
|
||||||
res = conn.get 'status/platforms/vmpooler'
|
|
||||||
if valid_json?(res.body)
|
|
||||||
res_body = JSON.parse(res.body)
|
|
||||||
if res_body.key?('vmpooler_platforms')
|
|
||||||
os_list << '*** VMPOOLER Pools ***'
|
|
||||||
os_list += if res_body['vmpooler_platforms'].is_a?(String)
|
|
||||||
JSON.parse(res_body['vmpooler_platforms']) # legacy ABS had another JSON string always-be-scheduling/pull/306
|
|
||||||
else
|
|
||||||
res_body['vmpooler_platforms']
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
res = conn.get 'status/platforms/ondemand_vmpooler'
|
|
||||||
if valid_json?(res.body)
|
|
||||||
res_body = JSON.parse(res.body)
|
|
||||||
if res_body.key?('ondemand_vmpooler_platforms') && res_body['ondemand_vmpooler_platforms'] != '[]'
|
|
||||||
os_list << ''
|
|
||||||
os_list << '*** VMPOOLER ONDEMAND Pools ***'
|
|
||||||
if res_body['ondemand_vmpooler_platforms'].is_a?(String)
|
|
||||||
os_list += JSON.parse(res_body['ondemand_vmpooler_platforms']) # legacy ABS had another JSON string always-be-scheduling/pull/306
|
|
||||||
else
|
|
||||||
os_list += res_body['ondemand_vmpooler_platforms']
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
res = conn.get 'status/platforms/nspooler'
|
|
||||||
if valid_json?(res.body)
|
|
||||||
res_body = JSON.parse(res.body)
|
|
||||||
if res_body.key?('nspooler_platforms')
|
|
||||||
os_list << ''
|
|
||||||
os_list << '*** NSPOOLER Pools ***'
|
|
||||||
os_list += if res_body['nspooler_platforms'].is_a?(String)
|
|
||||||
JSON.parse(res_body['nspooler_platforms']) # legacy ABS had another JSON string always-be-scheduling/pull/306
|
|
||||||
else
|
|
||||||
res_body['nspooler_platforms']
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
res = conn.get 'status/platforms/aws'
|
|
||||||
if valid_json?(res.body)
|
|
||||||
res_body = JSON.parse(res.body)
|
|
||||||
if res_body.key?('aws_platforms')
|
|
||||||
os_list << ''
|
|
||||||
os_list << '*** AWS Pools ***'
|
|
||||||
os_list += if res_body['aws_platforms'].is_a?(String)
|
|
||||||
JSON.parse(res_body['aws_platforms']) # legacy ABS had another JSON string always-be-scheduling/pull/306
|
|
||||||
else
|
|
||||||
res_body['aws_platforms']
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
os_list.delete 'ok'
|
|
||||||
|
|
||||||
os_filter ? os_list.select { |i| i[/#{os_filter}/] } : os_list
|
|
||||||
end
|
|
||||||
|
|
||||||
# Retrieve an OS from ABS.
|
|
||||||
def self.retrieve(verbose, os_types, token, url, user, config, _ondemand = nil, continue = nil)
|
|
||||||
#
|
|
||||||
# Contents of post must be like:
|
|
||||||
#
|
|
||||||
# {
|
|
||||||
# "resources": {
|
|
||||||
# "centos-7-i386": 1,
|
|
||||||
# "ubuntu-1404-x86_64": 2
|
|
||||||
# },
|
|
||||||
# "job": {
|
|
||||||
# "id": "12345",
|
|
||||||
# "tags": {
|
|
||||||
# "user": "username",
|
|
||||||
# }
|
|
||||||
# }
|
|
||||||
# }
|
|
||||||
|
|
||||||
conn = Http.get_conn(verbose, supported_abs_url(url))
|
|
||||||
conn.headers['X-AUTH-TOKEN'] = token if token
|
|
||||||
|
|
||||||
saved_job_id = if continue.nil?
|
|
||||||
"#{user}-#{DateTime.now.strftime('%Q')}"
|
|
||||||
else
|
|
||||||
continue
|
|
||||||
end
|
|
||||||
|
|
||||||
req_obj = {
|
|
||||||
resources: os_types,
|
|
||||||
job: {
|
|
||||||
id: saved_job_id,
|
|
||||||
tags: {
|
|
||||||
user: user
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if config['vmpooler_fallback'] # optional and not available as cli flag
|
|
||||||
vmpooler_config = Utils.get_vmpooler_service_config(config['vmpooler_fallback'])
|
|
||||||
# request with this token, on behalf of this user
|
|
||||||
req_obj[:vm_token] = vmpooler_config['token']
|
|
||||||
end
|
|
||||||
|
|
||||||
if config['priority']
|
|
||||||
req_obj[:priority] = case config['priority']
|
|
||||||
when 'high'
|
|
||||||
1
|
|
||||||
when 'medium'
|
|
||||||
2
|
|
||||||
when 'low'
|
|
||||||
3
|
|
||||||
else
|
|
||||||
config['priority'].to_i
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
FloatyLogger.info "Posting to ABS #{req_obj.to_json}" if verbose
|
|
||||||
|
|
||||||
# os_string = os_type.map { |os, num| Array(os) * num }.flatten.join('+')
|
|
||||||
# raise MissingParamError, 'No operating systems provided to obtain.' if os_string.empty?
|
|
||||||
FloatyLogger.info "Requesting VMs with job_id: #{saved_job_id} Will retry for up to an hour."
|
|
||||||
res = conn.post 'request', req_obj.to_json
|
|
||||||
|
|
||||||
retries = 360
|
|
||||||
|
|
||||||
status = validate_queue_status_response(res.status, res.body, 'Initial request', verbose)
|
|
||||||
|
|
||||||
begin
|
|
||||||
(1..retries).each do |i|
|
|
||||||
res_body = check_queue(conn, saved_job_id, req_obj, verbose)
|
|
||||||
return translated(res_body, saved_job_id) if res_body.is_a?(Array) # when we get a response with hostnames
|
|
||||||
|
|
||||||
sleep_seconds = 10 if i >= 10
|
|
||||||
sleep_seconds = i if i < 10
|
|
||||||
FloatyLogger.info "Waiting #{sleep_seconds}s (x#{i}) #{res_body.strip}"
|
|
||||||
|
|
||||||
sleep(sleep_seconds)
|
|
||||||
end
|
|
||||||
rescue SystemExit, Interrupt
|
|
||||||
FloatyLogger.info "\n\nFloaty interrupted, you can resume polling with\n1) `floaty get [same arguments] and adding the flag --continue #{saved_job_id}` or query the state of the queue via\n2) `floaty query #{saved_job_id}` or delete it via\n3) `floaty delete #{saved_job_id}`"
|
|
||||||
exit 1
|
|
||||||
end
|
|
||||||
nil
|
|
||||||
end
|
|
||||||
|
|
||||||
#
|
|
||||||
# We should fix the ABS API to be more like the vmpooler or nspooler api, but for now
|
|
||||||
#
|
|
||||||
def self.translated(res_body, job_id)
|
|
||||||
vmpooler_formatted_body = { 'job_id' => job_id }
|
|
||||||
|
|
||||||
res_body.each do |host|
|
|
||||||
if vmpooler_formatted_body[host['type']] && vmpooler_formatted_body[host['type']]['hostname'].instance_of?(Array)
|
|
||||||
vmpooler_formatted_body[host['type']]['hostname'] << host['hostname']
|
|
||||||
else
|
|
||||||
vmpooler_formatted_body[host['type']] = { 'hostname' => [host['hostname']] }
|
|
||||||
end
|
|
||||||
end
|
|
||||||
vmpooler_formatted_body['ok'] = true
|
|
||||||
|
|
||||||
vmpooler_formatted_body
|
|
||||||
end
|
|
||||||
|
|
||||||
def self.check_queue(conn, _job_id, req_obj, verbose)
|
|
||||||
res = conn.post 'request', req_obj.to_json
|
|
||||||
status = validate_queue_status_response(res.status, res.body, 'Check queue request', verbose)
|
|
||||||
unless res.body.empty? || !valid_json?(res.body)
|
|
||||||
res_body = JSON.parse(res.body)
|
|
||||||
return res_body
|
|
||||||
end
|
|
||||||
res.body
|
|
||||||
end
|
|
||||||
|
|
||||||
def self.snapshot(_verbose, _url, _hostname, _token)
|
|
||||||
raise NoMethodError, "Can't snapshot with ABS, use '--service vmpooler' (even for vms checked out with ABS)"
|
|
||||||
end
|
|
||||||
|
|
||||||
def self.status(verbose, url)
|
|
||||||
conn = Http.get_conn(verbose, supported_abs_url(url))
|
|
||||||
|
|
||||||
res = conn.get 'status'
|
|
||||||
|
|
||||||
res.body == 'OK'
|
|
||||||
end
|
|
||||||
|
|
||||||
def self.summary(_verbose, _url)
|
|
||||||
raise NoMethodError, 'summary is not defined for ABS'
|
|
||||||
end
|
|
||||||
|
|
||||||
def self.query(verbose, url, job_id)
|
|
||||||
# return saved hostnames from the last time list_active was run
|
|
||||||
# preventing having to query the API again.
|
|
||||||
# This works as long as query is called after list_active
|
|
||||||
return @active_hostnames if @active_hostnames && !@active_hostnames.empty?
|
|
||||||
|
|
||||||
# If using the cli query job_id
|
|
||||||
conn = Http.get_conn(verbose, supported_abs_url(url))
|
|
||||||
queue_info_res = conn.get "status/queue/info/#{job_id}"
|
|
||||||
if valid_json?(queue_info_res.body)
|
|
||||||
queue_info = JSON.parse(queue_info_res.body)
|
|
||||||
else
|
|
||||||
FloatyLogger.warn "Could not parse the status/queue/info/#{job_id}"
|
|
||||||
end
|
|
||||||
queue_info
|
|
||||||
end
|
|
||||||
|
|
||||||
def self.modify(_verbose, _url, _hostname, _token, _modify_hash)
|
|
||||||
raise NoMethodError, 'modify is not defined for ABS'
|
|
||||||
end
|
|
||||||
|
|
||||||
def self.disk(_verbose, _url, _hostname, _token, _disk)
|
|
||||||
raise NoMethodError, 'disk is not defined for ABS'
|
|
||||||
end
|
|
||||||
|
|
||||||
def self.revert(_verbose, _url, _hostname, _token, _snapshot_sha)
|
|
||||||
raise NoMethodError, 'revert is not defined for ABS'
|
|
||||||
end
|
|
||||||
|
|
||||||
# Validate the http code returned during a queue status request.
|
|
||||||
#
|
|
||||||
# Return a success message that can be displayed if the status code is
|
|
||||||
# success, otherwise raise an error.
|
|
||||||
def self.validate_queue_status_response(status_code, body, request_name, verbose)
|
|
||||||
case status_code
|
|
||||||
when 200
|
|
||||||
"#{request_name} returned success (Code 200)" if verbose
|
|
||||||
when 202
|
|
||||||
"#{request_name} returned accepted, processing (Code 202)" if verbose
|
|
||||||
when 401
|
|
||||||
raise AuthError, "HTTP #{status_code}: The token provided could not authenticate.\n#{body}"
|
|
||||||
else
|
|
||||||
raise "HTTP #{status_code}: #{request_name} request to ABS failed!\n#{body}"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def self.valid_json?(json)
|
|
||||||
JSON.parse(json)
|
|
||||||
true
|
|
||||||
rescue TypeError, JSON::ParserError => e
|
|
||||||
false
|
|
||||||
end
|
|
||||||
|
|
||||||
# when missing, adds the required api/v2 in the url
|
|
||||||
def self.supported_abs_url(url)
|
|
||||||
expected_ending = 'api/v2'
|
|
||||||
unless url.include?(expected_ending)
|
|
||||||
# add a slash if missing
|
|
||||||
expected_ending = "/#{expected_ending}" if url[-1] != '/'
|
|
||||||
url = "#{url}#{expected_ending}"
|
|
||||||
end
|
|
||||||
url
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
@ -1,5 +1,3 @@
|
||||||
# frozen_string_literal: true
|
|
||||||
|
|
||||||
require 'faraday'
|
require 'faraday'
|
||||||
require 'json'
|
require 'json'
|
||||||
require 'vmfloaty/http'
|
require 'vmfloaty/http'
|
||||||
|
|
@ -9,36 +7,46 @@ class Auth
|
||||||
def self.get_token(verbose, url, user, password)
|
def self.get_token(verbose, url, user, password)
|
||||||
conn = Http.get_conn_with_auth(verbose, url, user, password)
|
conn = Http.get_conn_with_auth(verbose, url, user, password)
|
||||||
|
|
||||||
resp = conn.post 'token'
|
resp = conn.post "token"
|
||||||
|
|
||||||
res_body = JSON.parse(resp.body)
|
res_body = JSON.parse(resp.body)
|
||||||
return res_body['token'] if res_body['ok']
|
if res_body["ok"]
|
||||||
|
return res_body["token"]
|
||||||
|
else
|
||||||
raise TokenError, "HTTP #{resp.status}: There was a problem requesting a token:\n#{res_body}"
|
raise TokenError, "HTTP #{resp.status}: There was a problem requesting a token:\n#{res_body}"
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def self.delete_token(verbose, url, user, password, token)
|
def self.delete_token(verbose, url, user, password, token)
|
||||||
raise TokenError, 'You did not provide a token' if token.nil?
|
if token.nil?
|
||||||
|
raise TokenError, 'You did not provide a token'
|
||||||
|
end
|
||||||
|
|
||||||
conn = Http.get_conn_with_auth(verbose, url, user, password)
|
conn = Http.get_conn_with_auth(verbose, url, user, password)
|
||||||
|
|
||||||
response = conn.delete "token/#{token}"
|
response = conn.delete "token/#{token}"
|
||||||
res_body = JSON.parse(response.body)
|
res_body = JSON.parse(response.body)
|
||||||
return res_body if res_body['ok']
|
if res_body["ok"]
|
||||||
|
return res_body
|
||||||
|
else
|
||||||
raise TokenError, "HTTP #{response.status}: There was a problem deleting a token:\n#{res_body}"
|
raise TokenError, "HTTP #{response.status}: There was a problem deleting a token:\n#{res_body}"
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def self.token_status(verbose, url, token)
|
def self.token_status(verbose, url, token)
|
||||||
raise TokenError, 'You did not provide a token' if token.nil?
|
if token.nil?
|
||||||
|
raise TokenError, 'You did not provide a token'
|
||||||
|
end
|
||||||
|
|
||||||
conn = Http.get_conn(verbose, url)
|
conn = Http.get_conn(verbose, url)
|
||||||
|
|
||||||
response = conn.get "token/#{token}"
|
response = conn.get "token/#{token}"
|
||||||
res_body = JSON.parse(response.body)
|
res_body = JSON.parse(response.body)
|
||||||
|
|
||||||
return res_body if res_body['ok']
|
if res_body["ok"]
|
||||||
|
return res_body
|
||||||
|
else
|
||||||
raise TokenError, "HTTP #{response.status}: There was a problem getting the status of a token:\n#{res_body}"
|
raise TokenError, "HTTP #{response.status}: There was a problem getting the status of a token:\n#{res_body}"
|
||||||
end
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,13 @@
|
||||||
# frozen_string_literal: true
|
|
||||||
|
|
||||||
require 'yaml'
|
require 'yaml'
|
||||||
|
|
||||||
class Conf
|
class Conf
|
||||||
|
|
||||||
def self.read_config
|
def self.read_config
|
||||||
conf = {}
|
conf = {}
|
||||||
begin
|
begin
|
||||||
conf = YAML.load_file("#{Dir.home}/.vmfloaty.yml")
|
conf = YAML.load_file("#{Dir.home}/.vmfloaty.yml")
|
||||||
rescue StandardError
|
rescue
|
||||||
# ignore
|
STDERR.puts "WARNING: There was no config file at #{Dir.home}/.vmfloaty.yml"
|
||||||
end
|
end
|
||||||
conf
|
conf
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -1,25 +1,17 @@
|
||||||
# frozen_string_literal: true
|
|
||||||
|
|
||||||
class AuthError < StandardError
|
class AuthError < StandardError
|
||||||
def initialize(msg = 'Could not authenticate to pooler')
|
def initialize(msg="Could not authenticate to pooler")
|
||||||
super
|
super
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
class TokenError < StandardError
|
class TokenError < StandardError
|
||||||
def initialize(msg = 'Could not do operation with token provided')
|
def initialize(msg="Could not do operation with token provided")
|
||||||
super
|
super
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
class MissingParamError < StandardError
|
class MissingParamError < StandardError
|
||||||
def initialize(msg = 'Argument provided to function is missing')
|
def initialize(msg="Argument provided to function is missing")
|
||||||
super
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
class ModifyError < StandardError
|
|
||||||
def initialize(msg = 'Could not modify VM')
|
|
||||||
super
|
super
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -1,45 +1,60 @@
|
||||||
# frozen_string_literal: true
|
|
||||||
|
|
||||||
require 'faraday'
|
require 'faraday'
|
||||||
require 'uri'
|
require 'uri'
|
||||||
|
|
||||||
class Http
|
class Http
|
||||||
def self.url?(url)
|
def self.is_url(url)
|
||||||
# This method exists because it seems like Farady
|
# This method exists because it seems like Farady
|
||||||
# has no handling around if a user gives us a URI
|
# has no handling around if a user gives us a URI
|
||||||
# with no protocol on the beginning of the URL
|
# with no protocol on the beginning of the URL
|
||||||
|
|
||||||
uri = URI.parse(url)
|
uri = URI.parse(url)
|
||||||
|
|
||||||
return true if uri.is_a?(URI::HTTP) || uri.is_a?(URI::HTTPS)
|
if uri.kind_of?(URI::HTTP) or uri.kind_of?(URI::HTTPS)
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
false
|
return false
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.get_conn(verbose, url)
|
def self.get_conn(verbose, url)
|
||||||
raise 'Did not provide a url to connect to' if url.nil?
|
if url.nil?
|
||||||
|
raise "Did not provide a url to connect to"
|
||||||
|
end
|
||||||
|
|
||||||
url = "https://#{url}" unless url?(url)
|
unless is_url(url)
|
||||||
|
url = "https://#{url}"
|
||||||
|
end
|
||||||
|
|
||||||
Faraday.new(url: url, ssl: { verify: false }) do |faraday|
|
conn = Faraday.new(:url => url, :ssl => {:verify => false}) do |faraday|
|
||||||
faraday.request :url_encoded
|
faraday.request :url_encoded
|
||||||
faraday.response :logger if verbose
|
faraday.response :logger if verbose
|
||||||
faraday.adapter Faraday.default_adapter
|
faraday.adapter Faraday.default_adapter
|
||||||
end
|
end
|
||||||
|
|
||||||
|
return conn
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.get_conn_with_auth(verbose, url, user, password)
|
def self.get_conn_with_auth(verbose, url, user, password)
|
||||||
raise 'Did not provide a url to connect to' if url.nil?
|
if url.nil?
|
||||||
|
raise "Did not provide a url to connect to"
|
||||||
|
end
|
||||||
|
|
||||||
raise 'You did not provide a user to authenticate with' if user.nil?
|
if user.nil?
|
||||||
|
raise "You did not provide a user to authenticate with"
|
||||||
|
end
|
||||||
|
|
||||||
url = "https://#{url}" unless url?(url)
|
unless is_url(url)
|
||||||
|
url = "https://#{url}"
|
||||||
|
end
|
||||||
|
|
||||||
Faraday.new(url: url, ssl: { verify: false }) do |faraday|
|
conn = Faraday.new(:url => url, :ssl => {:verify => false}) do |faraday|
|
||||||
faraday.request :url_encoded
|
faraday.request :url_encoded
|
||||||
faraday.request :basic_auth, user, password
|
faraday.request :basic_auth, user, password
|
||||||
faraday.response :logger if verbose
|
faraday.response :logger if verbose
|
||||||
faraday.adapter Faraday.default_adapter
|
faraday.adapter Faraday.default_adapter
|
||||||
end
|
end
|
||||||
|
|
||||||
|
return conn
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -1,43 +0,0 @@
|
||||||
# frozen_string_literal: true
|
|
||||||
|
|
||||||
require 'logger'
|
|
||||||
|
|
||||||
class FloatyLogger < ::Logger
|
|
||||||
def self.logger
|
|
||||||
@@logger ||= FloatyLogger.new
|
|
||||||
end
|
|
||||||
|
|
||||||
def self.info(msg)
|
|
||||||
FloatyLogger.logger.info msg
|
|
||||||
end
|
|
||||||
|
|
||||||
def self.warn(msg)
|
|
||||||
FloatyLogger.logger.warn msg
|
|
||||||
end
|
|
||||||
|
|
||||||
def self.error(msg)
|
|
||||||
FloatyLogger.logger.error msg
|
|
||||||
end
|
|
||||||
|
|
||||||
def self.setlevel=(level)
|
|
||||||
level = level.downcase
|
|
||||||
case level
|
|
||||||
when 'debug'
|
|
||||||
logger.level = ::Logger::DEBUG
|
|
||||||
when 'info'
|
|
||||||
logger.level = ::Logger::INFO
|
|
||||||
when 'error'
|
|
||||||
logger.level = ::Logger::ERROR
|
|
||||||
else
|
|
||||||
error('set loglevel to debug, info or error')
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def initialize
|
|
||||||
super($stderr)
|
|
||||||
self.level = ::Logger::INFO
|
|
||||||
self.formatter = proc do |_severity, _datetime, _progname, msg|
|
|
||||||
"#{msg}\n"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
@ -1,120 +0,0 @@
|
||||||
# frozen_string_literal: true
|
|
||||||
|
|
||||||
require 'vmfloaty/errors'
|
|
||||||
require 'vmfloaty/http'
|
|
||||||
require 'faraday'
|
|
||||||
require 'json'
|
|
||||||
|
|
||||||
class NonstandardPooler
|
|
||||||
def self.list(verbose, url, os_filter = nil)
|
|
||||||
conn = Http.get_conn(verbose, url)
|
|
||||||
|
|
||||||
response = conn.get 'status'
|
|
||||||
response_body = JSON.parse(response.body)
|
|
||||||
os_list = response_body.keys.sort
|
|
||||||
os_list.delete 'ok'
|
|
||||||
|
|
||||||
os_filter ? os_list.select { |i| i[/#{os_filter}/] } : os_list
|
|
||||||
end
|
|
||||||
|
|
||||||
def self.list_active(verbose, url, token, _user)
|
|
||||||
status = Auth.token_status(verbose, url, token)
|
|
||||||
status['reserved_hosts'] || []
|
|
||||||
end
|
|
||||||
|
|
||||||
def self.retrieve(verbose, os_type, token, url, _user, _options, _ondemand = nil, _continue = nil)
|
|
||||||
conn = Http.get_conn(verbose, url)
|
|
||||||
conn.headers['X-AUTH-TOKEN'] = token if token
|
|
||||||
|
|
||||||
os_string = os_type.map { |os, num| Array(os) * num }.flatten.join('+')
|
|
||||||
raise MissingParamError, 'No operating systems provided to obtain.' if os_string.empty?
|
|
||||||
|
|
||||||
response = conn.post "host/#{os_string}"
|
|
||||||
|
|
||||||
res_body = JSON.parse(response.body)
|
|
||||||
|
|
||||||
if res_body['ok']
|
|
||||||
res_body
|
|
||||||
elsif response.status == 401
|
|
||||||
raise AuthError, "HTTP #{response.status}: The token provided could not authenticate to the pooler.\n#{res_body}"
|
|
||||||
else
|
|
||||||
raise "HTTP #{response.status}: Failed to obtain VMs from the pooler at #{url}/host/#{os_string}. #{res_body}"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def self.modify(verbose, url, hostname, token, modify_hash)
|
|
||||||
raise TokenError, 'Token provided was nil; Request cannot be made to modify VM' if token.nil?
|
|
||||||
|
|
||||||
modify_hash.each do |key, _value|
|
|
||||||
raise ModifyError, "Configured service type does not support modification of #{key}" unless %i[reason
|
|
||||||
reserved_for_reason].include? key
|
|
||||||
end
|
|
||||||
|
|
||||||
if modify_hash[:reason]
|
|
||||||
# "reason" is easier to type than "reserved_for_reason", but nspooler needs the latter
|
|
||||||
modify_hash[:reserved_for_reason] = modify_hash.delete :reason
|
|
||||||
end
|
|
||||||
|
|
||||||
conn = Http.get_conn(verbose, url)
|
|
||||||
conn.headers['X-AUTH-TOKEN'] = token
|
|
||||||
|
|
||||||
response = conn.put do |req|
|
|
||||||
req.url "host/#{hostname}"
|
|
||||||
req.body = modify_hash.to_json
|
|
||||||
end
|
|
||||||
|
|
||||||
response.body.empty? ? {} : JSON.parse(response.body)
|
|
||||||
end
|
|
||||||
|
|
||||||
def self.disk(_verbose, _url, _hostname, _token, _disk)
|
|
||||||
raise ModifyError, 'Configured service type does not support modification of disk space'
|
|
||||||
end
|
|
||||||
|
|
||||||
def self.snapshot(_verbose, _url, _hostname, _token)
|
|
||||||
raise ModifyError, 'Configured service type does not support snapshots'
|
|
||||||
end
|
|
||||||
|
|
||||||
def self.revert(_verbose, _url, _hostname, _token, _snapshot_sha)
|
|
||||||
raise ModifyError, 'Configured service type does not support snapshots'
|
|
||||||
end
|
|
||||||
|
|
||||||
def self.delete(verbose, url, hosts, token, _user)
|
|
||||||
raise TokenError, 'Token provided was nil; Request cannot be made to delete VM' if token.nil?
|
|
||||||
|
|
||||||
conn = Http.get_conn(verbose, url)
|
|
||||||
|
|
||||||
conn.headers['X-AUTH-TOKEN'] = token if token
|
|
||||||
|
|
||||||
response_body = {}
|
|
||||||
|
|
||||||
hosts = hosts.split(',') unless hosts.is_a? Array
|
|
||||||
hosts.each do |host|
|
|
||||||
response = conn.delete "host/#{host}"
|
|
||||||
res_body = JSON.parse(response.body)
|
|
||||||
response_body[host] = res_body
|
|
||||||
end
|
|
||||||
|
|
||||||
response_body
|
|
||||||
end
|
|
||||||
|
|
||||||
def self.status(verbose, url)
|
|
||||||
conn = Http.get_conn(verbose, url)
|
|
||||||
|
|
||||||
response = conn.get '/status'
|
|
||||||
JSON.parse(response.body)
|
|
||||||
end
|
|
||||||
|
|
||||||
def self.summary(verbose, url)
|
|
||||||
conn = Http.get_conn(verbose, url)
|
|
||||||
|
|
||||||
response = conn.get '/summary'
|
|
||||||
JSON.parse(response.body)
|
|
||||||
end
|
|
||||||
|
|
||||||
def self.query(verbose, url, hostname)
|
|
||||||
conn = Http.get_conn(verbose, url)
|
|
||||||
|
|
||||||
response = conn.get "host/#{hostname}"
|
|
||||||
JSON.parse(response.body)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
@ -1,136 +1,108 @@
|
||||||
# frozen_string_literal: true
|
|
||||||
|
|
||||||
require 'faraday'
|
require 'faraday'
|
||||||
require 'vmfloaty/http'
|
require 'vmfloaty/http'
|
||||||
require 'json'
|
require 'json'
|
||||||
require 'vmfloaty/errors'
|
require 'vmfloaty/errors'
|
||||||
|
|
||||||
class Pooler
|
class Pooler
|
||||||
def self.list(verbose, url, os_filter = nil)
|
def self.list(verbose, url, os_filter=nil)
|
||||||
conn = Http.get_conn(verbose, url)
|
conn = Http.get_conn(verbose, url)
|
||||||
|
|
||||||
response = conn.get 'vm'
|
response = conn.get 'vm'
|
||||||
response_body = JSON.parse(response.body)
|
response_body = JSON.parse(response.body)
|
||||||
|
|
||||||
if os_filter
|
if os_filter
|
||||||
response_body.select { |i| i[/#{os_filter}/] }
|
hosts = response_body.select { |i| i[/#{os_filter}/] }
|
||||||
else
|
else
|
||||||
response_body
|
hosts = response_body
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.list_active(verbose, url, token, _user)
|
hosts
|
||||||
status = Auth.token_status(verbose, url, token)
|
|
||||||
vms = []
|
|
||||||
vms = status[token]['vms']['running'] if status[token] && status[token]['vms']
|
|
||||||
vms
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.retrieve(verbose, os_type, token, url, _user, _options, ondemand = nil, _continue = nil)
|
def self.retrieve(verbose, os_type, token, url)
|
||||||
# NOTE:
|
# NOTE:
|
||||||
# Developers can use `Utils.generate_os_hash` to
|
# Developers can use `Utils.generate_os_hash` to
|
||||||
# generate the os_type param.
|
# generate the os_type param.
|
||||||
conn = Http.get_conn(verbose, url)
|
conn = Http.get_conn(verbose, url)
|
||||||
conn.headers['X-AUTH-TOKEN'] = token if token
|
if token
|
||||||
|
conn.headers['X-AUTH-TOKEN'] = token
|
||||||
|
end
|
||||||
|
|
||||||
os_string = os_type.map { |os, num| Array(os) * num }.flatten.join('+')
|
os_string = ""
|
||||||
raise MissingParamError, 'No operating systems provided to obtain.' if os_string.empty?
|
os_type.each do |os,num|
|
||||||
|
num.times do |i|
|
||||||
|
os_string << os+"+"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
response = conn.post "vm/#{os_string}" unless ondemand
|
os_string = os_string.chomp("+")
|
||||||
response ||= conn.post "ondemandvm/#{os_string}"
|
|
||||||
|
if os_string.size == 0
|
||||||
|
raise MissingParamError, "No operating systems provided to obtain."
|
||||||
|
end
|
||||||
|
|
||||||
|
response = conn.post "vm/#{os_string}"
|
||||||
|
|
||||||
res_body = JSON.parse(response.body)
|
res_body = JSON.parse(response.body)
|
||||||
|
|
||||||
if res_body['ok']
|
if res_body["ok"]
|
||||||
res_body
|
res_body
|
||||||
elsif response.status == 401
|
elsif response.status == 401
|
||||||
raise AuthError, "HTTP #{response.status}: The token provided could not authenticate to the pooler.\n#{res_body}"
|
raise AuthError, "HTTP #{response.status}: The token provided could not authenticate to the pooler.\n#{res_body}"
|
||||||
elsif response.status == 403
|
|
||||||
raise "HTTP #{response.status}: Failed to obtain VMs from the pooler at #{url}/vm/#{os_string}. Request exceeds the configured per pool maximum. #{res_body}"
|
|
||||||
else
|
else
|
||||||
unless ondemand
|
|
||||||
raise "HTTP #{response.status}: Failed to obtain VMs from the pooler at #{url}/vm/#{os_string}. #{res_body}"
|
raise "HTTP #{response.status}: Failed to obtain VMs from the pooler at #{url}/vm/#{os_string}. #{res_body}"
|
||||||
end
|
end
|
||||||
|
|
||||||
raise "HTTP #{response.status}: Failed to obtain VMs from the pooler at #{url}/ondemandvm/#{os_string}. #{res_body}"
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.wait_for_request(verbose, request_id, url, timeout = 300)
|
def self.modify(verbose, url, hostname, token, lifetime, tags)
|
||||||
start_time = Time.now
|
if token.nil?
|
||||||
while check_ondemandvm(verbose, request_id, url) == false
|
raise TokenError, "Token provided was nil. Request cannot be made to modify vm"
|
||||||
return false if (Time.now - start_time).to_i > timeout
|
|
||||||
|
|
||||||
FloatyLogger.info "waiting for request #{request_id} to be fulfilled"
|
|
||||||
sleep 5
|
|
||||||
end
|
|
||||||
FloatyLogger.info 'The request has been fulfilled'
|
|
||||||
check_ondemandvm(verbose, request_id, url)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.check_ondemandvm(verbose, request_id, url)
|
modify_body = {}
|
||||||
conn = Http.get_conn(verbose, url)
|
if lifetime
|
||||||
|
modify_body['lifetime'] = lifetime
|
||||||
response = conn.get "ondemandvm/#{request_id}"
|
|
||||||
res_body = JSON.parse(response.body)
|
|
||||||
return res_body if response.status == 200
|
|
||||||
|
|
||||||
return false if response.status == 202
|
|
||||||
|
|
||||||
raise "HTTP #{response.status}: The request cannot be found, or an unknown error occurred" if response.status == 404
|
|
||||||
|
|
||||||
false
|
|
||||||
end
|
end
|
||||||
|
if tags
|
||||||
def self.modify(verbose, url, hostname, token, modify_hash)
|
modify_body['tags'] = tags
|
||||||
raise TokenError, 'Token provided was nil. Request cannot be made to modify vm' if token.nil?
|
|
||||||
|
|
||||||
modify_hash.each_key do |key|
|
|
||||||
raise ModifyError, "Configured service type does not support modification of #{key}." unless %i[tags lifetime
|
|
||||||
disk].include? key
|
|
||||||
end
|
end
|
||||||
|
|
||||||
conn = Http.get_conn(verbose, url)
|
conn = Http.get_conn(verbose, url)
|
||||||
conn.headers['X-AUTH-TOKEN'] = token
|
conn.headers['X-AUTH-TOKEN'] = token
|
||||||
|
|
||||||
if modify_hash['disk']
|
|
||||||
disk(verbose, url, hostname, token, modify_hash['disk'])
|
|
||||||
modify_hash.delete 'disk'
|
|
||||||
end
|
|
||||||
|
|
||||||
response = conn.put do |req|
|
response = conn.put do |req|
|
||||||
req.url "vm/#{hostname}"
|
req.url "vm/#{hostname}"
|
||||||
req.body = modify_hash.to_json
|
req.body = modify_body.to_json
|
||||||
end
|
end
|
||||||
|
|
||||||
res_body = JSON.parse(response.body)
|
res_body = JSON.parse(response.body)
|
||||||
|
|
||||||
if res_body['ok']
|
|
||||||
res_body
|
res_body
|
||||||
elsif response.status == 401
|
|
||||||
raise AuthError, "HTTP #{response.status}: The token provided could not authenticate to the pooler.\n#{res_body}"
|
|
||||||
else
|
|
||||||
raise ModifyError, "HTTP #{response.status}: Failed to modify VMs from the pooler vm/#{hostname}. #{res_body}"
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.disk(verbose, url, hostname, token, disk)
|
def self.disk(verbose, url, hostname, token, disk)
|
||||||
raise TokenError, 'Token provided was nil. Request cannot be made to modify vm' if token.nil?
|
if token.nil?
|
||||||
|
raise TokenError, "Token provided was nil. Request cannot be made to modify vm"
|
||||||
|
end
|
||||||
|
|
||||||
conn = Http.get_conn(verbose, url)
|
conn = Http.get_conn(verbose, url)
|
||||||
conn.headers['X-AUTH-TOKEN'] = token
|
conn.headers['X-AUTH-TOKEN'] = token
|
||||||
|
|
||||||
response = conn.post "vm/#{hostname}/disk/#{disk}"
|
response = conn.post "vm/#{hostname}/disk/#{disk}"
|
||||||
|
|
||||||
JSON.parse(response.body)
|
res_body = JSON.parse(response.body)
|
||||||
|
res_body
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.delete(verbose, url, hosts, token, _user)
|
def self.delete(verbose, url, hosts, token)
|
||||||
raise TokenError, 'Token provided was nil. Request cannot be made to delete vm' if token.nil?
|
if token.nil?
|
||||||
|
raise TokenError, "Token provided was nil. Request cannot be made to delete vm"
|
||||||
|
end
|
||||||
|
|
||||||
conn = Http.get_conn(verbose, url)
|
conn = Http.get_conn(verbose, url)
|
||||||
|
|
||||||
conn.headers['X-AUTH-TOKEN'] = token if token
|
if token
|
||||||
|
conn.headers['X-AUTH-TOKEN'] = token
|
||||||
|
end
|
||||||
|
|
||||||
response_body = {}
|
response_body = {}
|
||||||
|
|
||||||
|
|
@ -146,43 +118,55 @@ class Pooler
|
||||||
def self.status(verbose, url)
|
def self.status(verbose, url)
|
||||||
conn = Http.get_conn(verbose, url)
|
conn = Http.get_conn(verbose, url)
|
||||||
|
|
||||||
response = conn.get 'status'
|
response = conn.get '/status'
|
||||||
JSON.parse(response.body)
|
res_body = JSON.parse(response.body)
|
||||||
|
res_body
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.summary(verbose, url)
|
def self.summary(verbose, url)
|
||||||
conn = Http.get_conn(verbose, url)
|
conn = Http.get_conn(verbose, url)
|
||||||
|
|
||||||
response = conn.get 'summary'
|
response = conn.get '/summary'
|
||||||
JSON.parse(response.body)
|
res_body = JSON.parse(response.body)
|
||||||
|
res_body
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.query(verbose, url, hostname)
|
def self.query(verbose, url, hostname)
|
||||||
conn = Http.get_conn(verbose, url)
|
conn = Http.get_conn(verbose, url)
|
||||||
|
|
||||||
response = conn.get "vm/#{hostname}"
|
response = conn.get "vm/#{hostname}"
|
||||||
JSON.parse(response.body)
|
res_body = JSON.parse(response.body)
|
||||||
|
|
||||||
|
res_body
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.snapshot(verbose, url, hostname, token)
|
def self.snapshot(verbose, url, hostname, token)
|
||||||
raise TokenError, 'Token provided was nil. Request cannot be made to snapshot vm' if token.nil?
|
if token.nil?
|
||||||
|
raise TokenError, "Token provided was nil. Request cannot be made to snapshot vm"
|
||||||
|
end
|
||||||
|
|
||||||
conn = Http.get_conn(verbose, url)
|
conn = Http.get_conn(verbose, url)
|
||||||
conn.headers['X-AUTH-TOKEN'] = token
|
conn.headers['X-AUTH-TOKEN'] = token
|
||||||
|
|
||||||
response = conn.post "vm/#{hostname}/snapshot"
|
response = conn.post "vm/#{hostname}/snapshot"
|
||||||
JSON.parse(response.body)
|
res_body = JSON.parse(response.body)
|
||||||
|
res_body
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.revert(verbose, url, hostname, token, snapshot_sha)
|
def self.revert(verbose, url, hostname, token, snapshot_sha)
|
||||||
raise TokenError, 'Token provided was nil. Request cannot be made to revert vm' if token.nil?
|
if token.nil?
|
||||||
|
raise TokenError, "Token provided was nil. Request cannot be made to revert vm"
|
||||||
|
end
|
||||||
|
|
||||||
conn = Http.get_conn(verbose, url)
|
conn = Http.get_conn(verbose, url)
|
||||||
conn.headers['X-AUTH-TOKEN'] = token
|
conn.headers['X-AUTH-TOKEN'] = token
|
||||||
|
|
||||||
raise "Snapshot SHA provided was nil, could not revert #{hostname}" if snapshot_sha.nil?
|
if snapshot_sha.nil?
|
||||||
|
raise "Snapshot SHA provided was nil, could not revert #{hostname}"
|
||||||
|
end
|
||||||
|
|
||||||
response = conn.post "vm/#{hostname}/snapshot/#{snapshot_sha}"
|
response = conn.post "vm/#{hostname}/snapshot/#{snapshot_sha}"
|
||||||
JSON.parse(response.body)
|
res_body = JSON.parse(response.body)
|
||||||
|
res_body
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -1,152 +0,0 @@
|
||||||
# frozen_string_literal: true
|
|
||||||
|
|
||||||
require 'commander/user_interaction'
|
|
||||||
require 'commander/command'
|
|
||||||
require 'vmfloaty/utils'
|
|
||||||
require 'vmfloaty/ssh'
|
|
||||||
|
|
||||||
class Service
|
|
||||||
attr_reader :config
|
|
||||||
attr_accessor :silent
|
|
||||||
|
|
||||||
def initialize(options, config_hash = {})
|
|
||||||
options ||= Commander::Command::Options.new
|
|
||||||
@config = Utils.get_service_config config_hash, options
|
|
||||||
@service_object = Utils.get_service_object @config['type']
|
|
||||||
@silent = false
|
|
||||||
end
|
|
||||||
|
|
||||||
def method_missing(method_name, *args, &block)
|
|
||||||
if @service_object.respond_to?(method_name)
|
|
||||||
@service_object.send(method_name, *args, &block)
|
|
||||||
else
|
|
||||||
super
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def respond_to_missing?(method_name, *)
|
|
||||||
@service_object.respond_to?(method_name) || super
|
|
||||||
end
|
|
||||||
|
|
||||||
def url
|
|
||||||
@config['url']
|
|
||||||
end
|
|
||||||
|
|
||||||
def type
|
|
||||||
@service_object.name
|
|
||||||
end
|
|
||||||
|
|
||||||
def user
|
|
||||||
unless @config['user']
|
|
||||||
FloatyLogger.info "Enter your #{@config['url']} service username:"
|
|
||||||
@config['user'] = $stdin.gets.chomp
|
|
||||||
end
|
|
||||||
@config['user']
|
|
||||||
end
|
|
||||||
|
|
||||||
def token
|
|
||||||
unless @config['token']
|
|
||||||
FloatyLogger.info 'No token found. Retrieving a token...'
|
|
||||||
@config['token'] = get_new_token(nil)
|
|
||||||
end
|
|
||||||
@config['token']
|
|
||||||
end
|
|
||||||
|
|
||||||
def get_new_token(verbose)
|
|
||||||
username = user
|
|
||||||
pass = Commander::UI.password "Enter your #{@config['url']} service password:", '*'
|
|
||||||
Auth.get_token(verbose, url, username, pass)
|
|
||||||
end
|
|
||||||
|
|
||||||
def delete_token(verbose, token_value = @config['token'])
|
|
||||||
username = user
|
|
||||||
pass = Commander::UI.password "Enter your #{@config['url']} service password:", '*'
|
|
||||||
Auth.delete_token(verbose, url, username, pass, token_value)
|
|
||||||
end
|
|
||||||
|
|
||||||
def token_status(verbose, token_value)
|
|
||||||
token_value ||= @config['token']
|
|
||||||
Auth.token_status(verbose, url, token_value)
|
|
||||||
end
|
|
||||||
|
|
||||||
def list(verbose, os_filter = nil)
|
|
||||||
@service_object.list verbose, url, os_filter
|
|
||||||
end
|
|
||||||
|
|
||||||
def list_active(verbose)
|
|
||||||
@service_object.list_active verbose, url, token, user
|
|
||||||
end
|
|
||||||
|
|
||||||
def retrieve(verbose, os_types, use_token = true, ondemand = nil, continue = nil)
|
|
||||||
FloatyLogger.info 'Requesting a vm without a token...' unless use_token
|
|
||||||
token_value = use_token ? token : nil
|
|
||||||
@service_object.retrieve verbose, os_types, token_value, url, user, @config, ondemand, continue
|
|
||||||
end
|
|
||||||
|
|
||||||
def wait_for_request(verbose, requestid)
|
|
||||||
@service_object.wait_for_request verbose, requestid, url
|
|
||||||
end
|
|
||||||
|
|
||||||
def ssh(verbose, host_os, use_token = true, ondemand = nil)
|
|
||||||
token_value = nil
|
|
||||||
if use_token
|
|
||||||
begin
|
|
||||||
token_value = token || get_new_token(verbose)
|
|
||||||
rescue TokenError => e
|
|
||||||
FloatyLogger.error e
|
|
||||||
FloatyLogger.info 'Could not get token... requesting vm without a token anyway...'
|
|
||||||
end
|
|
||||||
end
|
|
||||||
Ssh.ssh(verbose, self, host_os, token_value, ondemand)
|
|
||||||
end
|
|
||||||
|
|
||||||
def query(verbose, hostname)
|
|
||||||
@service_object.query verbose, url, hostname
|
|
||||||
end
|
|
||||||
|
|
||||||
def modify(verbose, hostname, modify_hash)
|
|
||||||
maybe_use_vmpooler
|
|
||||||
@service_object.modify verbose, url, hostname, token, modify_hash
|
|
||||||
end
|
|
||||||
|
|
||||||
def delete(verbose, hosts)
|
|
||||||
@service_object.delete verbose, url, hosts, token, user
|
|
||||||
end
|
|
||||||
|
|
||||||
def status(verbose)
|
|
||||||
@service_object.status verbose, url
|
|
||||||
end
|
|
||||||
|
|
||||||
def summary(verbose)
|
|
||||||
maybe_use_vmpooler
|
|
||||||
@service_object.summary verbose, url
|
|
||||||
end
|
|
||||||
|
|
||||||
def snapshot(verbose, hostname)
|
|
||||||
maybe_use_vmpooler
|
|
||||||
@service_object.snapshot verbose, url, hostname, token
|
|
||||||
end
|
|
||||||
|
|
||||||
def revert(verbose, hostname, snapshot_sha)
|
|
||||||
maybe_use_vmpooler
|
|
||||||
@service_object.revert verbose, url, hostname, token, snapshot_sha
|
|
||||||
end
|
|
||||||
|
|
||||||
def disk(verbose, hostname, disk)
|
|
||||||
maybe_use_vmpooler
|
|
||||||
@service_object.disk(verbose, url, hostname, token, disk)
|
|
||||||
end
|
|
||||||
|
|
||||||
# some methods do not exist for ABS, and if possible should target the Pooler service
|
|
||||||
def maybe_use_vmpooler
|
|
||||||
if @service_object == ABS # this is not an instance
|
|
||||||
unless silent
|
|
||||||
FloatyLogger.info 'The service in use is ABS, but the requested method should run against vmpooler directly, using fallback_vmpooler config from ~/.vmfloaty.yml'
|
|
||||||
self.silent = true
|
|
||||||
end
|
|
||||||
|
|
||||||
@config = Utils.get_vmpooler_service_config(@config['vmpooler_fallback'])
|
|
||||||
@service_object = Pooler
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
@ -1,61 +1,43 @@
|
||||||
# frozen_string_literal: true
|
|
||||||
|
|
||||||
class Ssh
|
class Ssh
|
||||||
|
|
||||||
def self.which(cmd)
|
def self.which(cmd)
|
||||||
# Gets path of executable for given command
|
# Gets path of executable for given command
|
||||||
|
|
||||||
exts = ENV['PATHEXT'] ? ENV['PATHEXT'].split(';') : ['']
|
exts = ENV['PATHEXT'] ? ENV['PATHEXT'].split(';') : ['']
|
||||||
ENV['PATH'].split(File::PATH_SEPARATOR).each do |path|
|
ENV['PATH'].split(File::PATH_SEPARATOR).each do |path|
|
||||||
exts.each do |ext|
|
exts.each { |ext|
|
||||||
exe = File.join(path, "#{cmd}#{ext}")
|
exe = File.join(path, "#{cmd}#{ext}")
|
||||||
return exe if File.executable?(exe) && !File.directory?(exe)
|
return exe if File.executable?(exe) && !File.directory?(exe)
|
||||||
|
}
|
||||||
end
|
end
|
||||||
end
|
return nil
|
||||||
nil
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.command_string(verbose, service, host_os, use_token, ondemand = nil)
|
def self.ssh(verbose, host_os, token, url)
|
||||||
ssh_path = which('ssh')
|
ssh_path = which("ssh")
|
||||||
raise 'Could not determine path to ssh' unless ssh_path
|
if !ssh_path
|
||||||
os_types = Utils.generate_os_hash([host_os])
|
raise "Could not determine path to ssh"
|
||||||
|
end
|
||||||
|
os_types = {}
|
||||||
os_types[host_os] = 1
|
os_types[host_os] = 1
|
||||||
|
|
||||||
response = service.retrieve(verbose, os_types, use_token, ondemand)
|
response = Pooler.retrieve(verbose, os_types, token, url)
|
||||||
raise "Could not get vm from #{service.type}:\n #{response}" unless response['ok']
|
if response["ok"] == true
|
||||||
|
if host_os =~ /win/
|
||||||
user = /win/.match?(host_os) ? 'Administrator' : 'root'
|
user = "Administrator"
|
||||||
|
|
||||||
if ondemand
|
|
||||||
requestid = response['request_id']
|
|
||||||
service.wait_for_request(verbose, requestid)
|
|
||||||
hosts = service.check_ondemandvm(verbose, requestid, service.url)
|
|
||||||
if hosts['domain'].nil?
|
|
||||||
hostname = hosts[host_os]['hostname']
|
|
||||||
hostname = hosts[host_os]['hostname'][0] if hosts[host_os]['hostname'].is_a?(Array)
|
|
||||||
else
|
else
|
||||||
# Provides backwards compatibility with VMPooler API v1
|
user = "root"
|
||||||
hostname = "#{hosts[host_os]['hostname']}.#{hosts['domain']}"
|
|
||||||
hostname = "#{hosts[host_os]['hostname'][0]}.#{hosts['domain']}" if hosts[host_os]['hostname'].is_a?(Array)
|
|
||||||
end
|
|
||||||
else
|
|
||||||
if response['domain'].nil?
|
|
||||||
hostname = response[host_os]['hostname']
|
|
||||||
hostname = response[host_os]['hostname'][0] if response[host_os]['hostname'].is_a?(Array)
|
|
||||||
else
|
|
||||||
# Provides backwards compatibility with VMPooler API v1
|
|
||||||
hostname = "#{response[host_os]['hostname']}.#{response['domain']}"
|
|
||||||
hostname = "#{response[host_os]['hostname'][0]}.#{response['domain']}" if response[host_os]['hostname'].is_a?(Array)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
"#{ssh_path} #{user}@#{hostname}"
|
hostname = "#{response[host_os]["hostname"]}.#{response["domain"]}"
|
||||||
end
|
cmd = "#{ssh_path} #{user}@#{hostname}"
|
||||||
|
|
||||||
def self.ssh(verbose, service, host_os, use_token, ondemand)
|
|
||||||
cmd = command_string(verbose, service, host_os, use_token, ondemand)
|
|
||||||
# TODO: Should this respect more ssh settings? Can it be configured
|
# TODO: Should this respect more ssh settings? Can it be configured
|
||||||
# by users ssh config and does this respect those settings?
|
# by users ssh config and does this respect those settings?
|
||||||
Kernel.exec(cmd)
|
Kernel.exec(cmd)
|
||||||
nil
|
else
|
||||||
|
raise "Could not get vm from vmpooler:\n #{response}"
|
||||||
|
end
|
||||||
|
return
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -1,84 +1,27 @@
|
||||||
# frozen_string_literal: true
|
|
||||||
|
|
||||||
require 'vmfloaty/abs'
|
|
||||||
require 'vmfloaty/nonstandard_pooler'
|
|
||||||
require 'vmfloaty/pooler'
|
require 'vmfloaty/pooler'
|
||||||
require 'vmfloaty/conf'
|
|
||||||
|
|
||||||
class Utils
|
class Utils
|
||||||
# TODO: Takes the json response body from an HTTP GET
|
# TODO: Takes the json response body from an HTTP GET
|
||||||
# request and "pretty prints" it
|
# request and "pretty prints" it
|
||||||
def self.standardize_hostnames(response_body)
|
def self.format_hosts(hostname_hash)
|
||||||
# vmpooler api v1 response body example when `floaty get` arguments are `ubuntu-1610-x86_64=2 centos-7-x86_64`:
|
host_hash = {}
|
||||||
# {
|
|
||||||
# "ok": true,
|
|
||||||
# "domain": "delivery.mycompany.net",
|
|
||||||
# "ubuntu-1610-x86_64": {
|
|
||||||
# "hostname": ["gdoy8q3nckuob0i", "ctnktsd0u11p9tm"]
|
|
||||||
# },
|
|
||||||
# "centos-7-x86_64": {
|
|
||||||
# "hostname": "dlgietfmgeegry2"
|
|
||||||
# }
|
|
||||||
# }
|
|
||||||
|
|
||||||
# vmpooler api v2 response body example when `floaty get` arguments are `ubuntu-1610-x86_64=2 centos-7-x86_64`:
|
hostname_hash.delete("ok")
|
||||||
# {
|
domain = hostname_hash["domain"]
|
||||||
# "ok": true,
|
hostname_hash.each do |type, hosts|
|
||||||
# "ubuntu-1610-x86_64": {
|
if type != "domain"
|
||||||
# "hostname": ["gdoy8q3nckuob0i.pooler.example.com", "ctnktsd0u11p9tm.pooler.example.com"]
|
if hosts["hostname"].kind_of?(Array)
|
||||||
# },
|
hosts["hostname"].map!{|host| host + "." + domain }
|
||||||
# "centos-7-x86_64": {
|
else
|
||||||
# "hostname": "dlgietfmgeegry2.pooler.example.com"
|
hosts["hostname"] = hosts["hostname"] + "." + domain
|
||||||
# }
|
|
||||||
# }
|
|
||||||
|
|
||||||
# nonstandard pooler response body example when `floaty get` arguments are `solaris-11-sparc=2 ubuntu-16.04-power8`:
|
|
||||||
# {
|
|
||||||
# "ok": true,
|
|
||||||
# "solaris-10-sparc": {
|
|
||||||
# "hostname": ["sol10-10.delivery.mycompany.net", "sol10-11.delivery.mycompany.net"]
|
|
||||||
# },
|
|
||||||
# "ubuntu-16.04-power8": {
|
|
||||||
# "hostname": "power8-ubuntu1604-6.delivery.mycompany.net"
|
|
||||||
# }
|
|
||||||
# }
|
|
||||||
|
|
||||||
# abs pooler response body example when `floaty get` arguments are :
|
|
||||||
# {
|
|
||||||
# "hostname"=>"thin-soutane.delivery.puppetlabs.net",
|
|
||||||
# "type"=>"centos-7.2-tmpfs-x86_64",
|
|
||||||
# "engine"=>"vmpooler"
|
|
||||||
# }
|
|
||||||
|
|
||||||
unless response_body.delete('ok')
|
|
||||||
raise ArgumentError,
|
|
||||||
"Bad GET response passed to format_hosts: #{response_body.to_json}"
|
|
||||||
end
|
end
|
||||||
|
|
||||||
# vmpooler reports the domain separately from the hostname
|
host_hash[type] = hosts["hostname"]
|
||||||
domain = response_body.delete('domain')
|
end
|
||||||
|
|
||||||
result = {}
|
|
||||||
|
|
||||||
# ABS has a job_id associated with hosts so pass that along
|
|
||||||
abs_job_id = response_body.delete('job_id')
|
|
||||||
result['job_id'] = abs_job_id unless abs_job_id.nil?
|
|
||||||
|
|
||||||
filtered_response_body = response_body.reject { |key, _| %w[request_id ready].include?(key) }
|
|
||||||
filtered_response_body.each do |os, value|
|
|
||||||
hostnames = Array(value['hostname'])
|
|
||||||
hostnames.map! { |host| "#{host}.#{domain}" } if domain
|
|
||||||
result[os] = hostnames
|
|
||||||
end
|
end
|
||||||
|
|
||||||
result
|
host_hash.to_json
|
||||||
end
|
|
||||||
|
|
||||||
def self.format_host_output(hosts)
|
|
||||||
hosts.flat_map do |os, names|
|
|
||||||
# Assume hosts are stored in Arrays and ignore everything else
|
|
||||||
names.map { |name| "- #{name} (#{os})" } if names.is_a? Array
|
|
||||||
end.join("\n")
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.generate_os_hash(os_args)
|
def self.generate_os_hash(os_args)
|
||||||
|
|
@ -92,248 +35,82 @@ class Utils
|
||||||
# ...]
|
# ...]
|
||||||
os_types = {}
|
os_types = {}
|
||||||
os_args.each do |arg|
|
os_args.each do |arg|
|
||||||
os_arr = arg.split('=')
|
os_arr = arg.split("=")
|
||||||
os_types[os_arr[0]] = os_arr.size == 1 ? 1 : os_arr[1].to_i
|
if os_arr.size == 1
|
||||||
|
# assume they didn't specify an = sign if split returns 1 size
|
||||||
|
os_types[os_arr[0]] = 1
|
||||||
|
else
|
||||||
|
os_types[os_arr[0]] = os_arr[1].to_i
|
||||||
|
end
|
||||||
end
|
end
|
||||||
os_types
|
os_types
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.print_fqdn_for_host(service, hostname, host_data)
|
def self.get_vm_info(hosts, verbose, url)
|
||||||
case service.type
|
vms = {}
|
||||||
when 'ABS'
|
hosts.each do |host|
|
||||||
abs_hostnames = []
|
vm_info = Pooler.query(verbose, url, host)
|
||||||
|
if vm_info['ok']
|
||||||
host_data['allocated_resources'].each do |vm_name, _i|
|
vms[host] = {}
|
||||||
abs_hostnames << vm_name['hostname']
|
vms[host]['domain'] = vm_info[host]['domain']
|
||||||
|
vms[host]['template'] = vm_info[host]['template']
|
||||||
|
vms[host]['lifetime'] = vm_info[host]['lifetime']
|
||||||
|
vms[host]['running'] = vm_info[host]['running']
|
||||||
|
vms[host]['tags'] = vm_info[host]['tags']
|
||||||
|
end
|
||||||
|
end
|
||||||
|
vms
|
||||||
end
|
end
|
||||||
|
|
||||||
puts abs_hostnames.join("\n")
|
def self.prettyprint_hosts(hosts, verbose, url)
|
||||||
when 'Pooler'
|
puts "Running VMs:"
|
||||||
if host_data['domain'].nil?
|
vm_info = get_vm_info(hosts, verbose, url)
|
||||||
puts hostname
|
vm_info.each do |vm,info|
|
||||||
else
|
domain = info['domain']
|
||||||
puts "#{hostname}.#{host_data['domain']}"
|
template = info['template']
|
||||||
end
|
lifetime = info['lifetime']
|
||||||
when 'NonstandardPooler'
|
running = info['running']
|
||||||
puts host_data['fqdn']
|
tags = info['tags'] || {}
|
||||||
else
|
|
||||||
raise "Invalid service type #{service.type}"
|
tag_pairs = tags.map {|key,value| "#{key}: #{value}" }
|
||||||
|
duration = "#{running}/#{lifetime} hours"
|
||||||
|
metadata = [template, duration, *tag_pairs]
|
||||||
|
|
||||||
|
puts "- #{vm}.#{domain} (#{metadata.join(", ")})"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.pretty_print_hosts(verbose, service, hostnames = [], print_to_stderr = false, indent = 0)
|
def self.get_all_token_vms(verbose, url, token)
|
||||||
output_target = print_to_stderr ? $stderr : $stdout
|
# get vms with token
|
||||||
|
status = Auth.token_status(verbose, url, token)
|
||||||
|
|
||||||
fetched_data = get_host_data(verbose, service, hostnames)
|
vms = status[token]['vms']
|
||||||
fetched_data.each do |hostname, host_data|
|
if vms.nil?
|
||||||
case service.type
|
raise "You have no running vms"
|
||||||
when 'ABS'
|
|
||||||
# For ABS, 'hostname' variable is the jobID
|
|
||||||
#
|
|
||||||
# Create a vmpooler service to query each hostname there so as to get the metadata too
|
|
||||||
|
|
||||||
output_target.puts "- [JobID:#{host_data['request']['job']['id']}] <#{host_data['state']}>"
|
|
||||||
host_data['allocated_resources'].each do |allocated_resources, _i|
|
|
||||||
if (allocated_resources['engine'] == 'vmpooler' || allocated_resources['engine'] == 'ondemand') && service.config['vmpooler_fallback']
|
|
||||||
vmpooler_service = service.clone
|
|
||||||
vmpooler_service.silent = true
|
|
||||||
vmpooler_service.maybe_use_vmpooler
|
|
||||||
pretty_print_hosts(verbose, vmpooler_service, allocated_resources['hostname'].split('.')[0],
|
|
||||||
print_to_stderr, indent + 2)
|
|
||||||
else
|
|
||||||
# TODO: we could add more specific metadata for the other services, nspooler and aws
|
|
||||||
output_target.puts " - #{allocated_resources['hostname']} (#{allocated_resources['type']})"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
when 'Pooler'
|
|
||||||
tag_pairs = []
|
|
||||||
tag_pairs = host_data['tags'].map { |key, value| "#{key}: #{value}" } unless host_data['tags'].nil?
|
|
||||||
duration = "#{host_data['running']}/#{host_data['lifetime']} hours"
|
|
||||||
metadata = [host_data['state'], host_data['template'], duration, *tag_pairs]
|
|
||||||
# For backwards compatibility with vmpooler api v1
|
|
||||||
message =
|
|
||||||
if host_data['domain']
|
|
||||||
"- #{hostname}.#{host_data['domain']} (#{metadata.join(', ')})".gsub(/^/, ' ' * indent)
|
|
||||||
else
|
|
||||||
"- #{host_data['fqdn']} (#{metadata.join(', ')})".gsub(/^/, ' ' * indent)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
if host_data['state'] && host_data['state'] == 'destroyed'
|
running_vms = vms['running']
|
||||||
output_target.puts "- DESTROYED #{hostname}.#{host_data['domain']}".gsub(/^/, ' ' * indent)
|
running_vms
|
||||||
else
|
|
||||||
output_target.puts message
|
|
||||||
end
|
|
||||||
when 'NonstandardPooler'
|
|
||||||
line = "- #{host_data['fqdn']} (#{host_data['os_triple']}"
|
|
||||||
line += ", #{host_data['hours_left_on_reservation']}h remaining"
|
|
||||||
line += ", reason: #{host_data['reserved_for_reason']}" unless host_data['reserved_for_reason'].nil? || host_data['reserved_for_reason'].empty?
|
|
||||||
line += ')'
|
|
||||||
output_target.puts line
|
|
||||||
else
|
|
||||||
raise "Invalid service type #{service.type}"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.get_host_data(verbose, service, hostnames = [])
|
def self.prettyprint_status(status, message, pools, verbose)
|
||||||
result = {}
|
pools.select! {|name,pool| pool['ready'] < pool['max']} if ! verbose
|
||||||
hostnames = [hostnames] unless hostnames.is_a? Array
|
|
||||||
hostnames.each do |hostname|
|
|
||||||
response = service.query(verbose, hostname)
|
|
||||||
host_data = response[hostname]
|
|
||||||
if block_given?
|
|
||||||
yield host_data result
|
|
||||||
else
|
|
||||||
case service.type
|
|
||||||
when 'ABS'
|
|
||||||
# For ABS, 'hostname' variable is the jobID
|
|
||||||
result[hostname] = host_data if host_data['state'] == 'allocated' || host_data['state'] == 'filled'
|
|
||||||
when 'Pooler'
|
|
||||||
result[hostname] = host_data
|
|
||||||
when 'NonstandardPooler'
|
|
||||||
result[hostname] = host_data
|
|
||||||
else
|
|
||||||
raise "Invalid service type #{service.type}"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
rescue StandardError => e
|
|
||||||
FloatyLogger.error("Something went wrong while trying to gather information on #{hostname}:")
|
|
||||||
FloatyLogger.error(e)
|
|
||||||
end
|
|
||||||
result
|
|
||||||
end
|
|
||||||
|
|
||||||
def self.pretty_print_status(verbose, service)
|
|
||||||
status_response = service.status(verbose)
|
|
||||||
|
|
||||||
case service.type
|
|
||||||
when 'Pooler'
|
|
||||||
message = status_response['status']['message']
|
|
||||||
pools = status_response['pools']
|
|
||||||
pools.select! { |_, pool| pool['ready'] < pool['max'] } unless verbose
|
|
||||||
|
|
||||||
width = pools.keys.map(&:length).max
|
width = pools.keys.map(&:length).max
|
||||||
pools.each do |name, pool|
|
pools.each do |name,pool|
|
||||||
|
begin
|
||||||
max = pool['max']
|
max = pool['max']
|
||||||
ready = pool['ready']
|
ready = pool['ready']
|
||||||
pending = pool['pending']
|
pending = pool['pending']
|
||||||
missing = max - ready - pending
|
missing = max - ready - pending
|
||||||
char = 'o'
|
char = 'o'
|
||||||
puts "#{name.ljust(width)} #{(char * ready)}#{(char * pending)}#{(char * missing)}"
|
puts "#{name.ljust(width)} #{(char*ready).green}#{(char*pending).yellow}#{(char*missing).red}"
|
||||||
rescue StandardError => e
|
rescue => e
|
||||||
FloatyLogger.error "#{name.ljust(width)} #{e}"
|
puts "#{name.ljust(width)} #{e.red}"
|
||||||
end
|
|
||||||
puts message
|
|
||||||
when 'NonstandardPooler'
|
|
||||||
pools = status_response
|
|
||||||
pools.delete 'ok'
|
|
||||||
pools.select! { |_, pool| pool['available_hosts'] < pool['total_hosts'] } unless verbose
|
|
||||||
|
|
||||||
width = pools.keys.map(&:length).max
|
|
||||||
pools.each do |name, pool|
|
|
||||||
max = pool['total_hosts']
|
|
||||||
ready = pool['available_hosts']
|
|
||||||
pending = pool['pending'] || 0 # not available for nspooler
|
|
||||||
missing = max - ready - pending
|
|
||||||
char = 'o'
|
|
||||||
puts "#{name.ljust(width)} #{(char * ready)}#{(char * pending)}#{(char * missing)}"
|
|
||||||
rescue StandardError => e
|
|
||||||
FloatyLogger.error "#{name.ljust(width)} #{e}"
|
|
||||||
end
|
|
||||||
when 'ABS'
|
|
||||||
FloatyLogger.error 'ABS Not OK' unless status_response
|
|
||||||
puts 'ABS is OK' if status_response
|
|
||||||
else
|
|
||||||
raise "Invalid service type #{service.type}"
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# Adapted from ActiveSupport
|
puts
|
||||||
def self.strip_heredoc(str)
|
puts message.colorize(status['status']['ok'] ? :default : :red)
|
||||||
min_indent = str.scan(/^[ \t]*(?=\S)/).min
|
|
||||||
min_indent_size = min_indent.nil? ? 0 : min_indent.size
|
|
||||||
|
|
||||||
str.gsub(/^[ \t]{#{min_indent_size}}/, '')
|
|
||||||
end
|
|
||||||
|
|
||||||
def self.get_service_object(type = '')
|
|
||||||
abs_strings = %w[abs alwaysbescheduling always_be_scheduling]
|
|
||||||
nspooler_strings = %w[ns nspooler nonstandard nonstandard_pooler]
|
|
||||||
vmpooler_strings = %w[vmpooler]
|
|
||||||
if abs_strings.include? type.downcase
|
|
||||||
ABS
|
|
||||||
elsif nspooler_strings.include? type.downcase
|
|
||||||
NonstandardPooler
|
|
||||||
elsif vmpooler_strings.include? type.downcase
|
|
||||||
Pooler
|
|
||||||
else
|
|
||||||
Pooler
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def self.get_service_config(config, options)
|
|
||||||
# The top-level url, user, and token values in the config file are treated as defaults
|
|
||||||
service_config = {
|
|
||||||
'url' => config['url'],
|
|
||||||
'user' => config['user'],
|
|
||||||
'token' => config['token'],
|
|
||||||
'vmpooler_fallback' => config['vmpooler_fallback'],
|
|
||||||
'type' => config['type'] || 'vmpooler'
|
|
||||||
}
|
|
||||||
|
|
||||||
if config['services']
|
|
||||||
if options.service.nil?
|
|
||||||
# If the user did not specify a service name at the command line, but configured services do exist,
|
|
||||||
# use the first configured service in the list by default.
|
|
||||||
_, values = config['services'].first
|
|
||||||
service_config.merge! values
|
|
||||||
else
|
|
||||||
# If the user provided a service name at the command line, use that service if posible, or fail
|
|
||||||
unless config['services'][options.service]
|
|
||||||
raise ArgumentError,
|
|
||||||
"Could not find a configured service named '#{options.service}' in ~/.vmfloaty.yml"
|
|
||||||
end
|
|
||||||
|
|
||||||
# If the service is configured but some values are missing, use the top-level defaults to fill them in
|
|
||||||
service_config.merge! config['services'][options.service]
|
|
||||||
end
|
|
||||||
# No config file but service is declared on command line
|
|
||||||
elsif !config['services'] && options.service
|
|
||||||
service_config['type'] = options.service
|
|
||||||
end
|
|
||||||
|
|
||||||
# Prioritize an explicitly specified url, user, or token if the user provided one
|
|
||||||
service_config['priority'] = options.priority unless options.priority.nil?
|
|
||||||
service_config['url'] = options.url unless options.url.nil?
|
|
||||||
service_config['token'] = options.token unless options.token.nil?
|
|
||||||
service_config['user'] = options.user unless options.user.nil?
|
|
||||||
|
|
||||||
service_config
|
|
||||||
end
|
|
||||||
|
|
||||||
# This method gets the vmpooler service configured in ~/.vmfloaty
|
|
||||||
def self.get_vmpooler_service_config(vmpooler_fallback)
|
|
||||||
config = Conf.read_config
|
|
||||||
# The top-level url, user, and token values in the config file are treated as defaults
|
|
||||||
service_config = {
|
|
||||||
'url' => config['url'],
|
|
||||||
'user' => config['user'],
|
|
||||||
'token' => config['token'],
|
|
||||||
'type' => 'vmpooler'
|
|
||||||
}
|
|
||||||
|
|
||||||
# at a minimum, the url needs to be configured
|
|
||||||
if config['services'] && config['services'][vmpooler_fallback] && config['services'][vmpooler_fallback]['url']
|
|
||||||
# If the service is configured but some values are missing, use the top-level defaults to fill them in
|
|
||||||
service_config.merge! config['services'][vmpooler_fallback]
|
|
||||||
elsif vmpooler_fallback.nil?
|
|
||||||
raise ArgumentError,
|
|
||||||
"The abs service should have a key named 'vmpooler_fallback' in ~/.vmfloaty.yml with a value that points to a vmpooler service name use this format:\nservices:\n myabs:\n url: 'http://abs.com'\n user: 'superman'\n token: 'kryptonite'\n vmpooler_fallback: 'myvmpooler'\n myvmpooler:\n url: 'http://vmpooler.com'\n user: 'superman'\n token: 'kryptonite'"
|
|
||||||
else
|
|
||||||
raise ArgumentError,
|
|
||||||
"Could not find a configured service named '#{vmpooler_fallback}' in ~/.vmfloaty.yml use this format:\nservices:\n #{vmpooler_fallback}:\n url: 'http://vmpooler.com'\n user: 'superman'\n token: 'kryptonite'"
|
|
||||||
end
|
|
||||||
|
|
||||||
service_config
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,8 @@
|
||||||
# frozen_string_literal: true
|
|
||||||
|
|
||||||
class Vmfloaty
|
class Version
|
||||||
VERSION = '1.8.1'
|
@version = '0.7.8'
|
||||||
|
|
||||||
|
def self.get
|
||||||
|
@version
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
12
release-prep
12
release-prep
|
|
@ -1,12 +0,0 @@
|
||||||
#!/usr/bin/env bash
|
|
||||||
|
|
||||||
# bundle install
|
|
||||||
docker run -t --rm \
|
|
||||||
-v $(pwd):/app \
|
|
||||||
$(grep ^FROM ./Dockerfile |cut -d ' ' -f2) \
|
|
||||||
/bin/bash -c 'apt-get update -qq && apt-get install -y --no-install-recommends build-essential make openssh-client && cd /app && gem install bundler && bundle install --jobs 3; echo "LOCK_FILE_UPDATE_EXIT_CODE=$?"'
|
|
||||||
|
|
||||||
# Update Changelog
|
|
||||||
docker run -t --rm -e CHANGELOG_GITHUB_TOKEN -v $(pwd):/usr/local/src/your-app \
|
|
||||||
githubchangeloggenerator/github-changelog-generator:1.16.2 \
|
|
||||||
github_changelog_generator --future-release $(grep VERSION lib/vmfloaty/version.rb |rev |cut -d "'" -f2 |rev)
|
|
||||||
|
|
@ -1,53 +1,2 @@
|
||||||
# frozen_string_literal: true
|
|
||||||
|
|
||||||
require 'simplecov'
|
|
||||||
require 'simplecov-lcov'
|
|
||||||
require 'base64'
|
|
||||||
|
|
||||||
SimpleCov::Formatter::LcovFormatter.config do |c|
|
|
||||||
c.report_with_single_file = true
|
|
||||||
c.single_report_path = 'coverage/lcov.info'
|
|
||||||
end
|
|
||||||
|
|
||||||
SimpleCov.formatter = SimpleCov::Formatter::MultiFormatter.new(
|
|
||||||
[
|
|
||||||
SimpleCov::Formatter::HTMLFormatter,
|
|
||||||
SimpleCov::Formatter::LcovFormatter
|
|
||||||
]
|
|
||||||
)
|
|
||||||
|
|
||||||
SimpleCov.start do
|
|
||||||
add_filter %r{^/spec/}
|
|
||||||
end
|
|
||||||
|
|
||||||
require 'vmfloaty'
|
require 'vmfloaty'
|
||||||
require 'webmock/rspec'
|
require 'webmock/rspec'
|
||||||
|
|
||||||
# Mock Commander Options object to allow pre-population with values
|
|
||||||
class MockOptions < Commander::Command::Options
|
|
||||||
def initialize(values = {})
|
|
||||||
@table = values
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
RSpec.configure do |config|
|
|
||||||
config.color = true
|
|
||||||
config.tty = true
|
|
||||||
config.formatter = :documentation
|
|
||||||
end
|
|
||||||
|
|
||||||
def get_headers(username: nil, password: nil, token: nil, content_type: nil, content_length: nil)
|
|
||||||
headers = {
|
|
||||||
'Accept' => '*/*',
|
|
||||||
'Accept-Encoding' => /gzip/,
|
|
||||||
'User-Agent' => /Faraday/,
|
|
||||||
}
|
|
||||||
if username && password
|
|
||||||
auth = Base64.encode64("#{username}:#{password}").chomp
|
|
||||||
headers['Authorization'] = "Basic #{auth}"
|
|
||||||
end
|
|
||||||
headers['X-Auth-Token'] = token if token
|
|
||||||
headers['Content-Type'] = content_type if content_type
|
|
||||||
headers['Content-Length'] = content_length.to_s if content_length
|
|
||||||
headers
|
|
||||||
end
|
|
||||||
|
|
@ -1,93 +0,0 @@
|
||||||
# frozen_string_literal: true
|
|
||||||
|
|
||||||
require 'spec_helper'
|
|
||||||
require_relative '../../../lib/vmfloaty/auth'
|
|
||||||
|
|
||||||
user = 'first.last'
|
|
||||||
pass = 'password'
|
|
||||||
|
|
||||||
describe Pooler do
|
|
||||||
|
|
||||||
before :each do
|
|
||||||
@abs_url = 'https://abs.example.com/api/v2'
|
|
||||||
end
|
|
||||||
|
|
||||||
describe '#get_token' do
|
|
||||||
before :each do
|
|
||||||
@get_token_response = '{"ok": true,"token":"utpg2i2xswor6h8ttjhu3d47z53yy47y"}'
|
|
||||||
@token = 'utpg2i2xswor6h8ttjhu3d47z53yy47y'
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'returns a token from abs' do
|
|
||||||
stub_request(:post, 'https://abs.example.com/api/v2/token')
|
|
||||||
.with(headers: get_headers(username: user, password: pass, content_length: 0))
|
|
||||||
.to_return(status: 200, body: @get_token_response, headers: {})
|
|
||||||
|
|
||||||
token = Auth.get_token(false, @abs_url, user, pass)
|
|
||||||
expect(token).to eq @token
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'raises a token error if something goes wrong' do
|
|
||||||
stub_request(:post, 'https://abs.example.com/api/v2/token')
|
|
||||||
.with(headers: get_headers(username: user, password: pass, content_length: 0))
|
|
||||||
.to_return(status: 500, body: '{"ok":false}', headers: {})
|
|
||||||
|
|
||||||
expect { Auth.get_token(false, @abs_url, user, pass) }.to raise_error(TokenError)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe '#delete_token' do
|
|
||||||
before :each do
|
|
||||||
@delete_token_response = '{"ok":true}'
|
|
||||||
@token = 'utpg2i2xswor6h8ttjhu3d47z53yy47y'
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'deletes the specified token' do
|
|
||||||
stub_request(:delete, 'https://abs.example.com/api/v2/token/utpg2i2xswor6h8ttjhu3d47z53yy47y')
|
|
||||||
.with(headers: get_headers(username: user, password: pass))
|
|
||||||
.to_return(status: 200, body: @delete_token_response, headers: {})
|
|
||||||
|
|
||||||
expect(Auth.delete_token(false, @abs_url, user, pass,
|
|
||||||
@token)).to eq JSON.parse(@delete_token_response)
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'raises a token error if something goes wrong' do
|
|
||||||
stub_request(:delete, 'https://abs.example.com/api/v2/token/utpg2i2xswor6h8ttjhu3d47z53yy47y')
|
|
||||||
.with(headers: get_headers(username: user, password: pass))
|
|
||||||
.to_return(status: 500, body: '{"ok":false}', headers: {})
|
|
||||||
|
|
||||||
expect { Auth.delete_token(false, @abs_url, user, pass, @token) }.to raise_error(TokenError)
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'raises a token error if no token provided' do
|
|
||||||
expect { Auth.delete_token(false, @abs_url, user, pass, nil) }.to raise_error(TokenError)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe '#token_status' do
|
|
||||||
before :each do
|
|
||||||
@token_status_response = '{"ok":true,"utpg2i2xswor6h8ttjhu3d47z53yy47y":{"created":"2015-04-28 19:17:47 -0700"}}'
|
|
||||||
@token = 'utpg2i2xswor6h8ttjhu3d47z53yy47y'
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'checks the status of a token' do
|
|
||||||
stub_request(:get, "#{@abs_url}/token/utpg2i2xswor6h8ttjhu3d47z53yy47y")
|
|
||||||
.with(headers: get_headers)
|
|
||||||
.to_return(status: 200, body: @token_status_response, headers: {})
|
|
||||||
|
|
||||||
expect(Auth.token_status(false, @abs_url, @token)).to eq JSON.parse(@token_status_response)
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'raises a token error if something goes wrong' do
|
|
||||||
stub_request(:get, "#{@abs_url}/token/utpg2i2xswor6h8ttjhu3d47z53yy47y")
|
|
||||||
.with(headers: get_headers)
|
|
||||||
.to_return(status: 500, body: '{"ok":false}', headers: {})
|
|
||||||
|
|
||||||
expect { Auth.token_status(false, @abs_url, @token) }.to raise_error(TokenError)
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'raises a token error if no token provided' do
|
|
||||||
expect { Auth.token_status(false, @abs_url, nil) }.to raise_error(TokenError)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
@ -1,182 +0,0 @@
|
||||||
# frozen_string_literal: true
|
|
||||||
|
|
||||||
require 'spec_helper'
|
|
||||||
require 'vmfloaty/utils'
|
|
||||||
require 'vmfloaty/errors'
|
|
||||||
require 'vmfloaty/abs'
|
|
||||||
|
|
||||||
describe ABS do
|
|
||||||
before :each do
|
|
||||||
end
|
|
||||||
|
|
||||||
describe '#list' do
|
|
||||||
it 'skips empty platforms and lists aws' do
|
|
||||||
stub_request(:get, 'http://foo/api/v2/status/platforms/vmpooler')
|
|
||||||
.to_return(status: 200, body: '', headers: {})
|
|
||||||
stub_request(:get, 'http://foo/api/v2/status/platforms/ondemand_vmpooler')
|
|
||||||
.to_return(status: 200, body: '', headers: {})
|
|
||||||
stub_request(:get, 'http://foo/api/v2/status/platforms/nspooler')
|
|
||||||
.to_return(status: 200, body: '', headers: {})
|
|
||||||
body = '{
|
|
||||||
"aws_platforms": [
|
|
||||||
"amazon-6-x86_64",
|
|
||||||
"amazon-7-x86_64",
|
|
||||||
"amazon-7-arm64",
|
|
||||||
"centos-7-x86-64-west",
|
|
||||||
"redhat-8-arm64"
|
|
||||||
]
|
|
||||||
}'
|
|
||||||
stub_request(:get, 'http://foo/api/v2/status/platforms/aws')
|
|
||||||
.to_return(status: 200, body: body, headers: {})
|
|
||||||
|
|
||||||
results = ABS.list(false, 'http://foo')
|
|
||||||
|
|
||||||
expect(results).to include('amazon-6-x86_64', 'amazon-7-x86_64', 'amazon-7-arm64', 'centos-7-x86-64-west',
|
|
||||||
'redhat-8-arm64')
|
|
||||||
end
|
|
||||||
it 'legacy JSON string, prior to PR 306' do
|
|
||||||
stub_request(:get, 'http://foo/api/v2/status/platforms/vmpooler')
|
|
||||||
.to_return(status: 200, body: '', headers: {})
|
|
||||||
stub_request(:get, 'http://foo/api/v2/status/platforms/ondemand_vmpooler')
|
|
||||||
.to_return(status: 200, body: '', headers: {})
|
|
||||||
stub_request(:get, 'http://foo/api/v2/status/platforms/nspooler')
|
|
||||||
.to_return(status: 200, body: '', headers: {})
|
|
||||||
body = '{
|
|
||||||
"aws_platforms": "[\"amazon-6-x86_64\",\"amazon-7-x86_64\",\"amazon-7-arm64\",\"centos-7-x86-64-west\",\"redhat-8-arm64\"]"
|
|
||||||
}'
|
|
||||||
stub_request(:get, 'http://foo/api/v2/status/platforms/aws')
|
|
||||||
.to_return(status: 200, body: body, headers: {})
|
|
||||||
|
|
||||||
results = ABS.list(false, 'http://foo')
|
|
||||||
|
|
||||||
expect(results).to include('amazon-6-x86_64', 'amazon-7-x86_64', 'amazon-7-arm64', 'centos-7-x86-64-west',
|
|
||||||
'redhat-8-arm64')
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe '#format' do
|
|
||||||
it 'returns an hash formatted like a vmpooler return, plus the job_id' do
|
|
||||||
job_id = 'generated_by_floaty_12345'
|
|
||||||
abs_formatted_response = [
|
|
||||||
{ 'hostname' => 'aaaaaaaaaaaaaaa.delivery.puppetlabs.net', 'type' => 'centos-7.2-x86_64',
|
|
||||||
'engine' => 'vmpooler' },
|
|
||||||
{ 'hostname' => 'aaaaaaaaaaaaaab.delivery.puppetlabs.net', 'type' => 'centos-7.2-x86_64',
|
|
||||||
'engine' => 'vmpooler' },
|
|
||||||
{ 'hostname' => 'aaaaaaaaaaaaaac.delivery.puppetlabs.net', 'type' => 'ubuntu-7.2-x86_64',
|
|
||||||
'engine' => 'vmpooler' }
|
|
||||||
]
|
|
||||||
|
|
||||||
vmpooler_formatted_response = ABS.translated(abs_formatted_response, job_id)
|
|
||||||
|
|
||||||
vmpooler_formatted_compare = {
|
|
||||||
'centos-7.2-x86_64' => {},
|
|
||||||
'ubuntu-7.2-x86_64' => {}
|
|
||||||
}
|
|
||||||
|
|
||||||
vmpooler_formatted_compare['centos-7.2-x86_64']['hostname'] =
|
|
||||||
['aaaaaaaaaaaaaaa.delivery.puppetlabs.net', 'aaaaaaaaaaaaaab.delivery.puppetlabs.net']
|
|
||||||
vmpooler_formatted_compare['ubuntu-7.2-x86_64']['hostname'] = ['aaaaaaaaaaaaaac.delivery.puppetlabs.net']
|
|
||||||
|
|
||||||
vmpooler_formatted_compare['ok'] = true
|
|
||||||
|
|
||||||
vmpooler_formatted_compare['job_id'] = job_id
|
|
||||||
|
|
||||||
expect(vmpooler_formatted_response).to eq(vmpooler_formatted_compare)
|
|
||||||
vmpooler_formatted_response.delete('ok')
|
|
||||||
vmpooler_formatted_compare.delete('ok')
|
|
||||||
expect(vmpooler_formatted_response).to eq(vmpooler_formatted_compare)
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'won\'t delete a job if not all vms are listed' do
|
|
||||||
hosts = ['host1']
|
|
||||||
allocated_resources = [
|
|
||||||
{
|
|
||||||
'hostname' => 'host1'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
'hostname' => 'host2'
|
|
||||||
}
|
|
||||||
]
|
|
||||||
expect(ABS.all_job_resources_accounted_for(allocated_resources, hosts)).to eq(false)
|
|
||||||
|
|
||||||
hosts = %w[host1 host2]
|
|
||||||
allocated_resources = [
|
|
||||||
{
|
|
||||||
'hostname' => 'host1'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
'hostname' => 'host2'
|
|
||||||
}
|
|
||||||
]
|
|
||||||
expect(ABS.all_job_resources_accounted_for(allocated_resources, hosts)).to eq(true)
|
|
||||||
end
|
|
||||||
|
|
||||||
before :each do
|
|
||||||
@abs_url = 'https://abs.example.com'
|
|
||||||
end
|
|
||||||
|
|
||||||
describe '#test_abs_status_queue_endpoint' do
|
|
||||||
before :each do
|
|
||||||
# rubocop:disable Layout/LineLength
|
|
||||||
@active_requests_response = '
|
|
||||||
[
|
|
||||||
{ "state":"allocated","last_processed":"2019-12-16 23:00:34 +0000","allocated_resources":[{"hostname":"take-this.delivery.puppetlabs.net","type":"win-2012r2-x86_64","engine":"vmpooler"}],"audit_log":{"2019-12-13 16:45:29 +0000":"Allocated take-this.delivery.puppetlabs.net for job 1576255517241"},"request":{"resources":{"win-2012r2-x86_64":1},"job":{"id":"1576255517241","tags":{"user":"test-user"},"user":"test-user","time-received":1576255519},"priority":1}},
|
|
||||||
"null",
|
|
||||||
{"state":"allocated","last_processed":"2019-12-16 23:00:34 +0000","allocated_resources":[{"hostname":"not-this.delivery.puppetlabs.net","type":"win-2012r2-x86_64","engine":"vmpooler"}],"audit_log":{"2019-12-13 16:46:14 +0000":"Allocated not-this.delivery.puppetlabs.net for job 1576255565159"},"request":{"resources":{"win-2012r2-x86_64":1},"job":{"id":"1576255565159","tags":{"user":"not-test-user"},"user":"not-test-user","time-received":1576255566},"priority":1}}
|
|
||||||
]'
|
|
||||||
# rubocop:enable Layout/LineLength
|
|
||||||
@token = 'utpg2i2xswor6h8ttjhu3d47z53yy47y'
|
|
||||||
@test_user = 'test-user'
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'will skip a line with a null value returned from abs' do
|
|
||||||
stub_request(:get, 'https://abs.example.com/api/v2/status/queue')
|
|
||||||
.to_return(status: 200, body: @active_requests_response, headers: {})
|
|
||||||
|
|
||||||
ret = ABS.get_active_requests(false, @abs_url, @test_user)
|
|
||||||
|
|
||||||
expect(ret[0]).to include(
|
|
||||||
'allocated_resources' => [{
|
|
||||||
'hostname' => 'take-this.delivery.puppetlabs.net',
|
|
||||||
'type' => 'win-2012r2-x86_64',
|
|
||||||
'engine' => 'vmpooler'
|
|
||||||
}]
|
|
||||||
)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe '#test_abs_delete_jobid' do
|
|
||||||
before :each do
|
|
||||||
# rubocop:disable Layout/LineLength
|
|
||||||
@active_requests_response = '
|
|
||||||
[
|
|
||||||
{ "state":"allocated", "last_processed":"2020-01-17 22:29:13 +0000", "allocated_resources":[{"hostname":"craggy-chord.delivery.puppetlabs.net", "type":"centos-7-x86_64", "engine":"vmpooler"}, {"hostname":"visible-revival.delivery.puppetlabs.net", "type":"centos-7-x86_64", "engine":"vmpooler"}], "audit_log":{"2020-01-17 22:28:45 +0000":"Allocated craggy-chord.delivery.puppetlabs.net, visible-revival.delivery.puppetlabs.net for job 1579300120799"}, "request":{"resources":{"centos-7-x86_64":2}, "job":{"id":"1579300120799", "tags":{"user":"test-user"}, "user":"test-user", "time-received":1579300120}, "priority":3}}
|
|
||||||
]'
|
|
||||||
@return_request = {
|
|
||||||
"job_id" => "1579300120799",
|
|
||||||
"hosts" => [{"hostname"=>"craggy-chord.delivery.puppetlabs.net","type"=>"centos-7-x86_64","engine"=>"vmpooler"},
|
|
||||||
{"hostname"=>"visible-revival.delivery.puppetlabs.net","type"=>"centos-7-x86_64","engine"=>"vmpooler"}]
|
|
||||||
}
|
|
||||||
# rubocop:enable Layout/LineLength
|
|
||||||
@token = 'utpg2i2xswor6h8ttjhu3d47z53yy47y'
|
|
||||||
@test_user = 'test-user'
|
|
||||||
# Job ID
|
|
||||||
@hosts = ['1579300120799']
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'will delete the whole job' do
|
|
||||||
stub_request(:get, 'https://abs.example.com/api/v2/status/queue')
|
|
||||||
.to_return(status: 200, body: @active_requests_response, headers: {})
|
|
||||||
stub_request(:post, 'https://abs.example.com/api/v2/return')
|
|
||||||
.with(headers: get_headers(content_type: 'application/x-www-form-urlencoded', token: @token), body: @return_request.to_json)
|
|
||||||
.to_return(status: 200, body: 'OK', headers: {})
|
|
||||||
|
|
||||||
ret = ABS.delete(false, @abs_url, @hosts, @token, @test_user)
|
|
||||||
|
|
||||||
expect(ret).to include(
|
|
||||||
'craggy-chord.delivery.puppetlabs.net' => { 'ok' => true }, 'visible-revival.delivery.puppetlabs.net' => { 'ok' => true }
|
|
||||||
)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
@ -1,92 +1,86 @@
|
||||||
# frozen_string_literal: true
|
|
||||||
|
|
||||||
require 'spec_helper'
|
require 'spec_helper'
|
||||||
require_relative '../../lib/vmfloaty/auth'
|
require_relative '../../lib/vmfloaty/auth'
|
||||||
|
|
||||||
user = 'first.last'
|
|
||||||
pass = 'password'
|
|
||||||
|
|
||||||
describe Pooler do
|
describe Pooler do
|
||||||
before :each do
|
before :each do
|
||||||
@vmpooler_url = 'https://vmpooler.example.com'
|
@vmpooler_url = "https://vmpooler.example.com"
|
||||||
end
|
end
|
||||||
|
|
||||||
describe '#get_token' do
|
describe "#get_token" do
|
||||||
before :each do
|
before :each do
|
||||||
@get_token_response = '{"ok": true,"token":"utpg2i2xswor6h8ttjhu3d47z53yy47y"}'
|
@get_token_response = "{\"ok\": true,\"token\":\"utpg2i2xswor6h8ttjhu3d47z53yy47y\"}"
|
||||||
@token = 'utpg2i2xswor6h8ttjhu3d47z53yy47y'
|
@token = "utpg2i2xswor6h8ttjhu3d47z53yy47y"
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'returns a token from vmpooler' do
|
it "returns a token from vmpooler" do
|
||||||
stub_request(:post, 'https://vmpooler.example.com/token')
|
stub_request(:post, "https://first.last:password@vmpooler.example.com/token").
|
||||||
.with(headers: get_headers(username: user, password: pass, content_length: 0))
|
with(:headers => {'Accept'=>'*/*', 'Accept-Encoding'=>'gzip;q=1.0,deflate;q=0.6,identity;q=0.3', 'Content-Length'=>'0', 'User-Agent'=>'Faraday v0.9.2'}).
|
||||||
.to_return(status: 200, body: @get_token_response, headers: {})
|
to_return(:status => 200, :body => @get_token_response, :headers => {})
|
||||||
|
|
||||||
token = Auth.get_token(false, @vmpooler_url, user, pass)
|
token = Auth.get_token(false, @vmpooler_url, "first.last", "password")
|
||||||
expect(token).to eq @token
|
expect(token).to eq @token
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'raises a token error if something goes wrong' do
|
it "raises a token error if something goes wrong" do
|
||||||
stub_request(:post, 'https://vmpooler.example.com/token')
|
stub_request(:post, "https://first.last:password@vmpooler.example.com/token").
|
||||||
.with(headers: get_headers(username: user, password: pass, content_length: 0))
|
with(:headers => {'Accept'=>'*/*', 'Accept-Encoding'=>'gzip;q=1.0,deflate;q=0.6,identity;q=0.3', 'Content-Length'=>'0', 'User-Agent'=>'Faraday v0.9.2'}).
|
||||||
.to_return(status: 500, body: '{"ok":false}', headers: {})
|
to_return(:status => 500, :body => "{\"ok\":false}", :headers => {})
|
||||||
|
|
||||||
expect { Auth.get_token(false, @vmpooler_url, user, pass) }.to raise_error(TokenError)
|
expect{ Auth.get_token(false, @vmpooler_url, "first.last", "password") }.to raise_error(TokenError)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe '#delete_token' do
|
describe "#delete_token" do
|
||||||
before :each do
|
before :each do
|
||||||
@delete_token_response = '{"ok":true}'
|
@delete_token_response = "{\"ok\":true}"
|
||||||
@token = 'utpg2i2xswor6h8ttjhu3d47z53yy47y'
|
@token = "utpg2i2xswor6h8ttjhu3d47z53yy47y"
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'deletes the specified token' do
|
it "deletes the specified token" do
|
||||||
stub_request(:delete, 'https://vmpooler.example.com/token/utpg2i2xswor6h8ttjhu3d47z53yy47y')
|
stub_request(:delete, "https://first.last:password@vmpooler.example.com/token/utpg2i2xswor6h8ttjhu3d47z53yy47y").
|
||||||
.with(headers: get_headers(username: user, password: pass))
|
with(:headers => {'Accept'=>'*/*', 'Accept-Encoding'=>'gzip;q=1.0,deflate;q=0.6,identity;q=0.3', 'User-Agent'=>'Faraday v0.9.2'}).
|
||||||
.to_return(status: 200, body: @delete_token_response, headers: {})
|
to_return(:status => 200, :body => @delete_token_response, :headers => {})
|
||||||
|
|
||||||
expect(Auth.delete_token(false, @vmpooler_url, user, pass,
|
expect(Auth.delete_token(false, @vmpooler_url, "first.last", "password", @token)).to eq JSON.parse(@delete_token_response)
|
||||||
@token)).to eq JSON.parse(@delete_token_response)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'raises a token error if something goes wrong' do
|
it "raises a token error if something goes wrong" do
|
||||||
stub_request(:delete, 'https://vmpooler.example.com/token/utpg2i2xswor6h8ttjhu3d47z53yy47y')
|
stub_request(:delete, "https://first.last:password@vmpooler.example.com/token/utpg2i2xswor6h8ttjhu3d47z53yy47y").
|
||||||
.with(headers: get_headers(username: user, password: pass))
|
with(:headers => {'Accept'=>'*/*', 'Accept-Encoding'=>'gzip;q=1.0,deflate;q=0.6,identity;q=0.3', 'User-Agent'=>'Faraday v0.9.2'}).
|
||||||
.to_return(status: 500, body: '{"ok":false}', headers: {})
|
to_return(:status => 500, :body => "{\"ok\":false}", :headers => {})
|
||||||
|
|
||||||
expect { Auth.delete_token(false, @vmpooler_url, 'first.last', 'password', @token) }.to raise_error(TokenError)
|
expect{ Auth.delete_token(false, @vmpooler_url, "first.last", "password", @token) }.to raise_error(TokenError)
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'raises a token error if no token provided' do
|
it "raises a token error if no token provided" do
|
||||||
expect { Auth.delete_token(false, @vmpooler_url, 'first.last', 'password', nil) }.to raise_error(TokenError)
|
expect{ Auth.delete_token(false, @vmpooler_url, "first.last", "password", nil) }.to raise_error(TokenError)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe '#token_status' do
|
describe "#token_status" do
|
||||||
before :each do
|
before :each do
|
||||||
@token_status_response = '{"ok":true,"utpg2i2xswor6h8ttjhu3d47z53yy47y":{"created":"2015-04-28 19:17:47 -0700"}}'
|
@token_status_response = "{\"ok\":true,\"utpg2i2xswor6h8ttjhu3d47z53yy47y\":{\"created\":\"2015-04-28 19:17:47 -0700\"}}"
|
||||||
@token = 'utpg2i2xswor6h8ttjhu3d47z53yy47y'
|
@token = "utpg2i2xswor6h8ttjhu3d47z53yy47y"
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'checks the status of a token' do
|
it "checks the status of a token" do
|
||||||
stub_request(:get, "#{@vmpooler_url}/token/utpg2i2xswor6h8ttjhu3d47z53yy47y")
|
stub_request(:get, "#{@vmpooler_url}/token/utpg2i2xswor6h8ttjhu3d47z53yy47y").
|
||||||
.with(headers: get_headers)
|
with(:headers => {'Accept'=>'*/*', 'Accept-Encoding'=>'gzip;q=1.0,deflate;q=0.6,identity;q=0.3', 'User-Agent'=>'Faraday v0.9.2'}).
|
||||||
.to_return(status: 200, body: @token_status_response, headers: {})
|
to_return(:status => 200, :body => @token_status_response, :headers => {})
|
||||||
|
|
||||||
expect(Auth.token_status(false, @vmpooler_url, @token)).to eq JSON.parse(@token_status_response)
|
expect(Auth.token_status(false, @vmpooler_url, @token)).to eq JSON.parse(@token_status_response)
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'raises a token error if something goes wrong' do
|
it "raises a token error if something goes wrong" do
|
||||||
stub_request(:get, "#{@vmpooler_url}/token/utpg2i2xswor6h8ttjhu3d47z53yy47y")
|
stub_request(:get, "#{@vmpooler_url}/token/utpg2i2xswor6h8ttjhu3d47z53yy47y").
|
||||||
.with(headers: get_headers)
|
with(:headers => {'Accept'=>'*/*', 'Accept-Encoding'=>'gzip;q=1.0,deflate;q=0.6,identity;q=0.3', 'User-Agent'=>'Faraday v0.9.2'}).
|
||||||
.to_return(status: 500, body: '{"ok":false}', headers: {})
|
to_return(:status => 500, :body => "{\"ok\":false}", :headers => {})
|
||||||
|
|
||||||
expect { Auth.token_status(false, @vmpooler_url, @token) }.to raise_error(TokenError)
|
expect{ Auth.token_status(false, @vmpooler_url, @token) }.to raise_error(TokenError)
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'raises a token error if no token provided' do
|
it "raises a token error if no token provided" do
|
||||||
expect { Auth.token_status(false, @vmpooler_url, nil) }.to raise_error(TokenError)
|
expect{ Auth.token_status(false, @vmpooler_url, nil) }.to raise_error(TokenError)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -1,312 +0,0 @@
|
||||||
# frozen_string_literal: true
|
|
||||||
|
|
||||||
require 'spec_helper'
|
|
||||||
require 'vmfloaty/utils'
|
|
||||||
require 'vmfloaty/errors'
|
|
||||||
require 'vmfloaty/nonstandard_pooler'
|
|
||||||
|
|
||||||
describe NonstandardPooler do
|
|
||||||
before :each do
|
|
||||||
@nspooler_url = 'https://nspooler.example.com'
|
|
||||||
@auth_token_headers = get_headers(token: 'token-value')
|
|
||||||
end
|
|
||||||
|
|
||||||
describe '#list' do
|
|
||||||
before :each do
|
|
||||||
@status_response_body = <<~BODY
|
|
||||||
{
|
|
||||||
"ok": true,
|
|
||||||
"solaris-10-sparc": {
|
|
||||||
"total_hosts": 11,
|
|
||||||
"available_hosts": 11
|
|
||||||
},
|
|
||||||
"ubuntu-16.04-power8": {
|
|
||||||
"total_hosts": 10,
|
|
||||||
"available_hosts": 10
|
|
||||||
},
|
|
||||||
"aix-7.2-power": {
|
|
||||||
"total_hosts": 5,
|
|
||||||
"available_hosts": 4
|
|
||||||
}
|
|
||||||
}
|
|
||||||
BODY
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'returns an array with operating systems from the pooler' do
|
|
||||||
stub_request(:get, "#{@nspooler_url}/status")
|
|
||||||
.to_return(status: 200, body: @status_response_body, headers: {})
|
|
||||||
|
|
||||||
list = NonstandardPooler.list(false, @nspooler_url, nil)
|
|
||||||
expect(list).to be_an_instance_of Array
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'filters operating systems based on the filter param' do
|
|
||||||
stub_request(:get, "#{@nspooler_url}/status")
|
|
||||||
.to_return(status: 200, body: @status_response_body, headers: {})
|
|
||||||
|
|
||||||
list = NonstandardPooler.list(false, @nspooler_url, 'aix')
|
|
||||||
expect(list).to be_an_instance_of Array
|
|
||||||
expect(list.size).to equal 1
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'returns nothing if the filter does not match' do
|
|
||||||
stub_request(:get, "#{@nspooler_url}/status")
|
|
||||||
.to_return(status: 199, body: @status_response_body, headers: {})
|
|
||||||
|
|
||||||
list = NonstandardPooler.list(false, @nspooler_url, 'windows')
|
|
||||||
expect(list).to be_an_instance_of Array
|
|
||||||
expect(list.size).to equal 0
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe '#list_active' do
|
|
||||||
before :each do
|
|
||||||
@token_status_body_active = <<~BODY
|
|
||||||
{
|
|
||||||
"ok": true,
|
|
||||||
"user": "first.last",
|
|
||||||
"created": "2017-09-18 01:25:41 +0000",
|
|
||||||
"last_accessed": "2017-09-21 19:46:25 +0000",
|
|
||||||
"reserved_hosts": ["sol10-9", "sol10-11"]
|
|
||||||
}
|
|
||||||
BODY
|
|
||||||
@token_status_body_empty = <<~BODY
|
|
||||||
{
|
|
||||||
"ok": true,
|
|
||||||
"user": "first.last",
|
|
||||||
"created": "2017-09-18 01:25:41 +0000",
|
|
||||||
"last_accessed": "2017-09-21 19:46:25 +0000",
|
|
||||||
"reserved_hosts": []
|
|
||||||
}
|
|
||||||
BODY
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'prints an output of fqdn, template, and duration' do
|
|
||||||
allow(Auth).to receive(:token_status)
|
|
||||||
.with(false, @nspooler_url, 'token-value')
|
|
||||||
.and_return(JSON.parse(@token_status_body_active))
|
|
||||||
|
|
||||||
list = NonstandardPooler.list_active(false, @nspooler_url, 'token-value', 'user')
|
|
||||||
expect(list).to eql %w[sol10-9 sol10-11]
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe '#retrieve' do
|
|
||||||
before :each do
|
|
||||||
@retrieve_response_body_single = <<~BODY
|
|
||||||
{
|
|
||||||
"ok": true,
|
|
||||||
"solaris-11-sparc": {
|
|
||||||
"hostname": "sol11-4.delivery.puppetlabs.net"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
BODY
|
|
||||||
@retrieve_response_body_many = <<~BODY
|
|
||||||
{
|
|
||||||
"ok": true,
|
|
||||||
"solaris-10-sparc": {
|
|
||||||
"hostname": [
|
|
||||||
"sol10-9.delivery.puppetlabs.net",
|
|
||||||
"sol10-10.delivery.puppetlabs.net"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"aix-7.1-power": {
|
|
||||||
"hostname": "pe-aix-71-ci-acceptance.delivery.puppetlabs.net"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
BODY
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'raises an AuthError if the token is invalid' do
|
|
||||||
stub_request(:post, "#{@nspooler_url}/host/solaris-11-sparc")
|
|
||||||
.with(headers: get_headers(token: 'token-value'))
|
|
||||||
.to_return(status: 401, body: '{"ok":false,"reason": "token: token-value does not exist"}', headers: {})
|
|
||||||
|
|
||||||
vm_hash = { 'solaris-11-sparc' => 1 }
|
|
||||||
expect do
|
|
||||||
NonstandardPooler.retrieve(false, vm_hash, 'token-value', @nspooler_url, 'first.last', {})
|
|
||||||
end.to raise_error(AuthError)
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'retrieves a single vm with a token' do
|
|
||||||
stub_request(:post, "#{@nspooler_url}/host/solaris-11-sparc")
|
|
||||||
.with(headers: @auth_token_headers)
|
|
||||||
.to_return(status: 200, body: @retrieve_response_body_single, headers: {})
|
|
||||||
|
|
||||||
vm_hash = { 'solaris-11-sparc' => 1 }
|
|
||||||
vm_req = NonstandardPooler.retrieve(false, vm_hash, 'token-value', @nspooler_url, 'first.last', {})
|
|
||||||
expect(vm_req).to be_an_instance_of Hash
|
|
||||||
expect(vm_req['ok']).to equal true
|
|
||||||
expect(vm_req['solaris-11-sparc']['hostname']).to eq 'sol11-4.delivery.puppetlabs.net'
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'retrieves a multiple vms with a token' do
|
|
||||||
stub_request(:post, "#{@nspooler_url}/host/aix-7.1-power+solaris-10-sparc+solaris-10-sparc")
|
|
||||||
.with(headers: @auth_token_headers)
|
|
||||||
.to_return(status: 200, body: @retrieve_response_body_many, headers: {})
|
|
||||||
|
|
||||||
vm_hash = { 'aix-7.1-power' => 1, 'solaris-10-sparc' => 2 }
|
|
||||||
vm_req = NonstandardPooler.retrieve(false, vm_hash, 'token-value', @nspooler_url, 'first.last', {})
|
|
||||||
expect(vm_req).to be_an_instance_of Hash
|
|
||||||
expect(vm_req['ok']).to equal true
|
|
||||||
expect(vm_req['solaris-10-sparc']['hostname']).to be_an_instance_of Array
|
|
||||||
expect(vm_req['solaris-10-sparc']['hostname']).to eq ['sol10-9.delivery.puppetlabs.net',
|
|
||||||
'sol10-10.delivery.puppetlabs.net']
|
|
||||||
expect(vm_req['aix-7.1-power']['hostname']).to eq 'pe-aix-71-ci-acceptance.delivery.puppetlabs.net'
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe '#modify' do
|
|
||||||
before :each do
|
|
||||||
@modify_response_body_success = '{"ok":true}'
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'raises an error if the user tries to modify an unsupported attribute' do
|
|
||||||
stub_request(:put, 'https://nspooler.example.com/host/myfakehost')
|
|
||||||
.with(body: { '{}' => true },
|
|
||||||
headers: @auth_token_headers)
|
|
||||||
.to_return(status: 200, body: '', headers: {})
|
|
||||||
details = { lifetime: 12 }
|
|
||||||
expect { NonstandardPooler.modify(false, @nspooler_url, 'myfakehost', 'token-value', details) }
|
|
||||||
.to raise_error(ModifyError)
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'modifies the reason of a vm' do
|
|
||||||
modify_request_body = { '{"reserved_for_reason":"testing"}' => nil }
|
|
||||||
stub_request(:put, "#{@nspooler_url}/host/myfakehost")
|
|
||||||
.with(body: modify_request_body,
|
|
||||||
headers: @auth_token_headers)
|
|
||||||
.to_return(status: 200, body: '{"ok": true}', headers: {})
|
|
||||||
|
|
||||||
modify_hash = { reason: 'testing' }
|
|
||||||
modify_req = NonstandardPooler.modify(false, @nspooler_url, 'myfakehost', 'token-value', modify_hash)
|
|
||||||
expect(modify_req['ok']).to be true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe '#status' do
|
|
||||||
before :each do
|
|
||||||
@status_response_body = '{"capacity":{"current":716,"total":717,"percent": 99.9},"status":{"ok":true,"message":"Battle station fully armed and operational."}}'
|
|
||||||
# TODO: make this report stuff like 'broken'
|
|
||||||
@status_response_body = <<~BODY
|
|
||||||
{
|
|
||||||
"ok": true,
|
|
||||||
"solaris-10-sparc": {
|
|
||||||
"total_hosts": 11,
|
|
||||||
"available_hosts": 10
|
|
||||||
},
|
|
||||||
"ubuntu-16.04-power8": {
|
|
||||||
"total_hosts": 10,
|
|
||||||
"available_hosts": 10
|
|
||||||
},
|
|
||||||
"aix-7.2-power": {
|
|
||||||
"total_hosts": 5,
|
|
||||||
"available_hosts": 4
|
|
||||||
}
|
|
||||||
}
|
|
||||||
BODY
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'prints the status' do
|
|
||||||
stub_request(:get, "#{@nspooler_url}/status")
|
|
||||||
.to_return(status: 200, body: @status_response_body, headers: {})
|
|
||||||
|
|
||||||
status = NonstandardPooler.status(false, @nspooler_url)
|
|
||||||
expect(status).to be_an_instance_of Hash
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe '#summary' do
|
|
||||||
before :each do
|
|
||||||
@status_response_body = <<~BODY
|
|
||||||
{
|
|
||||||
"ok": true,
|
|
||||||
"total": 57,
|
|
||||||
"available": 39,
|
|
||||||
"in_use": 16,
|
|
||||||
"resetting": 2,
|
|
||||||
"broken": 0
|
|
||||||
}
|
|
||||||
BODY
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'prints the summary' do
|
|
||||||
stub_request(:get, "#{@nspooler_url}/summary")
|
|
||||||
.to_return(status: 200, body: @status_response_body, headers: {})
|
|
||||||
|
|
||||||
summary = NonstandardPooler.summary(false, @nspooler_url)
|
|
||||||
expect(summary).to be_an_instance_of Hash
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe '#query' do
|
|
||||||
before :each do
|
|
||||||
@query_response_body = <<~BODY
|
|
||||||
{
|
|
||||||
"ok": true,
|
|
||||||
"sol10-11": {
|
|
||||||
"fqdn": "sol10-11.delivery.puppetlabs.net",
|
|
||||||
"os_triple": "solaris-10-sparc",
|
|
||||||
"reserved_by_user": "first.last",
|
|
||||||
"reserved_for_reason": "testing",
|
|
||||||
"hours_left_on_reservation": 29.12
|
|
||||||
}
|
|
||||||
}
|
|
||||||
BODY
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'makes a query about a vm' do
|
|
||||||
stub_request(:get, "#{@nspooler_url}/host/sol10-11")
|
|
||||||
.to_return(status: 200, body: @query_response_body, headers: {})
|
|
||||||
|
|
||||||
query_req = NonstandardPooler.query(false, @nspooler_url, 'sol10-11')
|
|
||||||
expect(query_req).to be_an_instance_of Hash
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe '#delete' do
|
|
||||||
before :each do
|
|
||||||
@delete_response_success = '{"ok": true}'
|
|
||||||
@delete_response_failure = '{"ok": false, "failure": "ERROR: fakehost does not exist"}'
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'deletes a single existing vm' do
|
|
||||||
stub_request(:delete, "#{@nspooler_url}/host/sol11-7")
|
|
||||||
.with(headers: @auth_token_headers)
|
|
||||||
.to_return(status: 200, body: @delete_response_success, headers: {})
|
|
||||||
|
|
||||||
request = NonstandardPooler.delete(false, @nspooler_url, 'sol11-7', 'token-value', nil)
|
|
||||||
expect(request['sol11-7']['ok']).to be true
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'does not delete a nonexistant vm' do
|
|
||||||
stub_request(:delete, "#{@nspooler_url}/host/fakehost")
|
|
||||||
.with(headers: @auth_token_headers)
|
|
||||||
.to_return(status: 401, body: @delete_response_failure, headers: {})
|
|
||||||
|
|
||||||
request = NonstandardPooler.delete(false, @nspooler_url, 'fakehost', 'token-value', nil)
|
|
||||||
expect(request['fakehost']['ok']).to be false
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe '#snapshot' do
|
|
||||||
it 'logs an error explaining that snapshots are not supported' do
|
|
||||||
expect { NonstandardPooler.snapshot(false, @nspooler_url, 'myfakehost', 'token-value') }
|
|
||||||
.to raise_error(ModifyError)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe '#revert' do
|
|
||||||
it 'logs an error explaining that snapshots are not supported' do
|
|
||||||
expect { NonstandardPooler.revert(false, @nspooler_url, 'myfakehost', 'token-value', 'snapshot-sha') }
|
|
||||||
.to raise_error(ModifyError)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe '#disk' do
|
|
||||||
it 'logs an error explaining that disk modification is not supported' do
|
|
||||||
expect { NonstandardPooler.disk(false, @nspooler_url, 'myfakehost', 'token-value', 'diskname') }
|
|
||||||
.to raise_error(ModifyError)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
@ -1,247 +1,223 @@
|
||||||
# frozen_string_literal: true
|
|
||||||
|
|
||||||
require 'spec_helper'
|
require 'spec_helper'
|
||||||
require_relative '../../lib/vmfloaty/pooler'
|
require_relative '../../lib/vmfloaty/pooler'
|
||||||
|
|
||||||
describe Pooler do
|
describe Pooler do
|
||||||
before :each do
|
before :each do
|
||||||
@vmpooler_url = 'https://vmpooler.example.com'
|
@vmpooler_url = "https://vmpooler.example.com"
|
||||||
end
|
end
|
||||||
|
|
||||||
describe '#list' do
|
describe "#list" do
|
||||||
before :each do
|
before :each do
|
||||||
@list_response_body = '["debian-7-i386","debian-7-x86_64","centos-7-x86_64"]'
|
@list_response_body = "[\"debian-7-i386\",\"debian-7-x86_64\",\"centos-7-x86_64\"]"
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'returns a hash with operating systems from the pooler' do
|
it "returns a hash with operating systems from the pooler" do
|
||||||
stub_request(:get, "#{@vmpooler_url}/vm")
|
stub_request(:get, "#{@vmpooler_url}/vm").
|
||||||
.to_return(status: 200, body: @list_response_body, headers: {})
|
to_return(:status => 200, :body => @list_response_body, :headers => {})
|
||||||
|
|
||||||
list = Pooler.list(false, @vmpooler_url, nil)
|
list = Pooler.list(false, @vmpooler_url, nil)
|
||||||
expect(list).to be_an_instance_of Array
|
expect(list).to be_an_instance_of Array
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'filters operating systems based on the filter param' do
|
it "filters operating systems based on the filter param" do
|
||||||
stub_request(:get, "#{@vmpooler_url}/vm")
|
stub_request(:get, "#{@vmpooler_url}/vm").
|
||||||
.to_return(status: 200, body: @list_response_body, headers: {})
|
to_return(:status => 200, :body => @list_response_body, :headers => {})
|
||||||
|
|
||||||
list = Pooler.list(false, @vmpooler_url, 'deb')
|
list = Pooler.list(false, @vmpooler_url, "deb")
|
||||||
expect(list).to be_an_instance_of Array
|
expect(list).to be_an_instance_of Array
|
||||||
expect(list.size).to equal 2
|
expect(list.size).to equal 2
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'returns nothing if the filter does not match' do
|
it "returns nothing if the filter does not match" do
|
||||||
stub_request(:get, "#{@vmpooler_url}/vm")
|
stub_request(:get, "#{@vmpooler_url}/vm").
|
||||||
.to_return(status: 200, body: @list_response_body, headers: {})
|
to_return(:status => 200, :body => @list_response_body, :headers => {})
|
||||||
|
|
||||||
list = Pooler.list(false, @vmpooler_url, 'windows')
|
list = Pooler.list(false, @vmpooler_url, "windows")
|
||||||
expect(list).to be_an_instance_of Array
|
expect(list).to be_an_instance_of Array
|
||||||
expect(list.size).to equal 0
|
expect(list.size).to equal 0
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe '#retrieve' do
|
describe "#retrieve" do
|
||||||
before :each do
|
before :each do
|
||||||
@retrieve_response_body_single = '{"ok":true,"debian-7-i386":{"hostname":"fq6qlpjlsskycq6"}}'
|
@retrieve_response_body_single = "{\"ok\":true,\"debian-7-i386\":{\"hostname\":\"fq6qlpjlsskycq6\"}}"
|
||||||
@retrieve_response_body_double = '{"ok":true,"debian-7-i386":{"hostname":["sc0o4xqtodlul5w","4m4dkhqiufnjmxy"]},"centos-7-x86_64":{"hostname":"zb91y9qbrbf6d3q"}}'
|
@retrieve_response_body_double = "{\"ok\":true,\"debian-7-i386\":{\"hostname\":[\"sc0o4xqtodlul5w\",\"4m4dkhqiufnjmxy\"]},\"centos-7-x86_64\":{\"hostname\":\"zb91y9qbrbf6d3q\"}}"
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'raises an AuthError if the token is invalid' do
|
it "raises an AuthError if the token is invalid" do
|
||||||
stub_request(:post, "#{@vmpooler_url}/vm/debian-7-i386")
|
stub_request(:post, "#{@vmpooler_url}/vm/debian-7-i386").
|
||||||
.with(headers: { 'X-Auth-Token' => 'mytokenfile' })
|
with(:headers => {'Accept'=>'*/*', 'Accept-Encoding'=>'gzip;q=1.0,deflate;q=0.6,identity;q=0.3', 'Content-Length'=>'0', 'User-Agent'=>'Faraday v0.9.2', 'X-Auth-Token'=>'mytokenfile'}).
|
||||||
.to_return(status: 401, body: '{"ok":false}', headers: {})
|
to_return(:status => 401, :body => "{\"ok\":false}", :headers => {})
|
||||||
|
|
||||||
vm_hash = {}
|
vm_hash = {}
|
||||||
vm_hash['debian-7-i386'] = 1
|
vm_hash['debian-7-i386'] = 1
|
||||||
expect { Pooler.retrieve(false, vm_hash, 'mytokenfile', @vmpooler_url, 'user', {}) }.to raise_error(AuthError)
|
expect{ Pooler.retrieve(false, vm_hash, 'mytokenfile', @vmpooler_url) }.to raise_error(AuthError)
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'retrieves a single vm with a token' do
|
it "retrieves a single vm with a token" do
|
||||||
stub_request(:post, "#{@vmpooler_url}/vm/debian-7-i386")
|
stub_request(:post, "#{@vmpooler_url}/vm/debian-7-i386").
|
||||||
.with(headers: { 'X-Auth-Token' => 'mytokenfile' })
|
with(:headers => {'Accept'=>'*/*', 'Accept-Encoding'=>'gzip;q=1.0,deflate;q=0.6,identity;q=0.3', 'Content-Length'=>'0', 'User-Agent'=>'Faraday v0.9.2', 'X-Auth-Token'=>'mytokenfile'}).
|
||||||
.to_return(status: 200, body: @retrieve_response_body_single, headers: {})
|
to_return(:status => 200, :body => @retrieve_response_body_single, :headers => {})
|
||||||
|
|
||||||
vm_hash = {}
|
vm_hash = {}
|
||||||
vm_hash['debian-7-i386'] = 1
|
vm_hash['debian-7-i386'] = 1
|
||||||
vm_req = Pooler.retrieve(false, vm_hash, 'mytokenfile', @vmpooler_url, 'user', {})
|
vm_req = Pooler.retrieve(false, vm_hash, 'mytokenfile', @vmpooler_url)
|
||||||
expect(vm_req).to be_an_instance_of Hash
|
expect(vm_req).to be_an_instance_of Hash
|
||||||
expect(vm_req['ok']).to equal true
|
expect(vm_req["ok"]).to equal true
|
||||||
expect(vm_req['debian-7-i386']['hostname']).to eq 'fq6qlpjlsskycq6'
|
expect(vm_req["debian-7-i386"]["hostname"]).to eq "fq6qlpjlsskycq6"
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'retrieves a multiple vms with a token' do
|
it "retrieves a multiple vms with a token" do
|
||||||
stub_request(:post, "#{@vmpooler_url}/vm/debian-7-i386+debian-7-i386+centos-7-x86_64")
|
stub_request(:post, "#{@vmpooler_url}/vm/debian-7-i386+debian-7-i386+centos-7-x86_64").
|
||||||
.with(headers: { 'X-Auth-Token' => 'mytokenfile' })
|
with(:headers => {'Accept'=>'*/*', 'Accept-Encoding'=>'gzip;q=1.0,deflate;q=0.6,identity;q=0.3', 'Content-Length'=>'0', 'User-Agent'=>'Faraday v0.9.2', 'X-Auth-Token'=>'mytokenfile'}).
|
||||||
.to_return(status: 200, body: @retrieve_response_body_double, headers: {})
|
to_return(:status => 200, :body => @retrieve_response_body_double, :headers => {})
|
||||||
|
|
||||||
vm_hash = {}
|
vm_hash = {}
|
||||||
vm_hash['debian-7-i386'] = 2
|
vm_hash['debian-7-i386'] = 2
|
||||||
vm_hash['centos-7-x86_64'] = 1
|
vm_hash['centos-7-x86_64'] = 1
|
||||||
vm_req = Pooler.retrieve(false, vm_hash, 'mytokenfile', @vmpooler_url, 'user', {})
|
vm_req = Pooler.retrieve(false, vm_hash, 'mytokenfile', @vmpooler_url)
|
||||||
expect(vm_req).to be_an_instance_of Hash
|
expect(vm_req).to be_an_instance_of Hash
|
||||||
expect(vm_req['ok']).to equal true
|
expect(vm_req["ok"]).to equal true
|
||||||
expect(vm_req['debian-7-i386']['hostname']).to be_an_instance_of Array
|
expect(vm_req["debian-7-i386"]["hostname"]).to be_an_instance_of Array
|
||||||
expect(vm_req['debian-7-i386']['hostname']).to eq %w[sc0o4xqtodlul5w 4m4dkhqiufnjmxy]
|
expect(vm_req["debian-7-i386"]["hostname"]).to eq ["sc0o4xqtodlul5w", "4m4dkhqiufnjmxy"]
|
||||||
expect(vm_req['centos-7-x86_64']['hostname']).to eq 'zb91y9qbrbf6d3q'
|
expect(vm_req["centos-7-x86_64"]["hostname"]).to eq "zb91y9qbrbf6d3q"
|
||||||
end
|
|
||||||
|
|
||||||
context 'with ondemand provisioning' do
|
|
||||||
let(:ondemand_response) { '{"ok":true,"request_id":"1234"}' }
|
|
||||||
it 'retreives the vm with a token' do
|
|
||||||
stub_request(:post, "#{@vmpooler_url}/ondemandvm/debian-7-i386")
|
|
||||||
.with(headers: { 'X-Auth-Token' => 'mytokenfile' })
|
|
||||||
.to_return(status: 200, body: ondemand_response, headers: {})
|
|
||||||
|
|
||||||
stub_request(:get, "#{@vmpooler_url}/ondemandvm/1234")
|
|
||||||
.to_return(status: 200, body: @retrieve_response_body_single, headers: {})
|
|
||||||
|
|
||||||
vm_hash = {}
|
|
||||||
vm_hash['debian-7-i386'] = 1
|
|
||||||
Pooler.retrieve(false, vm_hash, 'mytokenfile', @vmpooler_url, 'user', {}, true)
|
|
||||||
vm_req = Pooler.check_ondemandvm(false, '1234', @vmpooler_url)
|
|
||||||
expect(vm_req).to be_an_instance_of Hash
|
|
||||||
expect(vm_req['ok']).to equal true
|
|
||||||
expect(vm_req['debian-7-i386']['hostname']).to eq 'fq6qlpjlsskycq6'
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe '#modify' do
|
describe "#modify" do
|
||||||
before :each do
|
before :each do
|
||||||
@modify_response_body_success = '{"ok":true}'
|
@modify_response_body_success = "{\"ok\":true}"
|
||||||
@modify_response_body_fail = '{"ok":false}'
|
@modify_response_body_fail = "{\"ok\":false}"
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'raises a TokenError if token provided is nil' do
|
it "raises a TokenError if token provided is nil" do
|
||||||
expect { Pooler.modify(false, @vmpooler_url, 'myfakehost', nil, {}) }.to raise_error(TokenError)
|
expect{ Pooler.modify(false, @vmpooler_url, 'myfakehost', nil, 12, nil) }.to raise_error(TokenError)
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'modifies the TTL of a vm' do
|
it "modifies the TTL of a vm" do
|
||||||
modify_hash = { lifetime: 12 }
|
stub_request(:put, "#{@vmpooler_url}/vm/fq6qlpjlsskycq6").
|
||||||
stub_request(:put, "#{@vmpooler_url}/vm/fq6qlpjlsskycq6")
|
with(:body => {"{\"lifetime\":12}"=>true},
|
||||||
.with(body: { '{"lifetime":12}' => nil },
|
:headers => {'Accept'=>'*/*', 'Accept-Encoding'=>'gzip;q=1.0,deflate;q=0.6,identity;q=0.3', 'Content-Type'=>'application/x-www-form-urlencoded', 'User-Agent'=>'Faraday v0.9.2', 'X-Auth-Token'=>'mytokenfile'}).
|
||||||
headers: get_headers(content_type: 'application/x-www-form-urlencoded', token: 'mytokenfile'))
|
to_return(:status => 200, :body => @modify_response_body_success, :headers => {})
|
||||||
.to_return(status: 200, body: @modify_response_body_success, headers: {})
|
|
||||||
|
|
||||||
modify_req = Pooler.modify(false, @vmpooler_url, 'fq6qlpjlsskycq6', 'mytokenfile', modify_hash)
|
modify_req = Pooler.modify(false, @vmpooler_url, 'fq6qlpjlsskycq6', 'mytokenfile', 12, nil)
|
||||||
expect(modify_req['ok']).to be true
|
expect(modify_req["ok"]).to be true
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe '#delete' do
|
describe "#delete" do
|
||||||
before :each do
|
before :each do
|
||||||
@delete_response_body_success = '{"ok":true}'
|
@delete_response_body_success = "{\"ok\":true}"
|
||||||
@delete_response = { 'fq6qlpjlsskycq6' => { 'ok' => true } }
|
@delete_response = {"fq6qlpjlsskycq6"=>{"ok"=>true}}
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'deletes a specified vm' do
|
it "deletes a specified vm" do
|
||||||
stub_request(:delete, "#{@vmpooler_url}/vm/fq6qlpjlsskycq6")
|
stub_request(:delete, "#{@vmpooler_url}/vm/fq6qlpjlsskycq6").
|
||||||
.with(headers: { 'X-Auth-Token' => 'mytokenfile' })
|
with(:headers => {'Accept'=>'*/*', 'Accept-Encoding'=>'gzip;q=1.0,deflate;q=0.6,identity;q=0.3', 'User-Agent'=>'Faraday v0.9.2', 'X-Auth-Token'=>'mytokenfile'}).
|
||||||
.to_return(status: 200, body: @delete_response_body_success, headers: {})
|
to_return(:status => 200, :body => @delete_response_body_success, :headers => {})
|
||||||
|
|
||||||
expect(Pooler.delete(false, @vmpooler_url, ['fq6qlpjlsskycq6'], 'mytokenfile', nil)).to eq @delete_response
|
expect(Pooler.delete(false, @vmpooler_url, ['fq6qlpjlsskycq6'], 'mytokenfile')).to eq @delete_response
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'raises a token error if no token provided' do
|
it "raises a token error if no token provided" do
|
||||||
expect { Pooler.delete(false, @vmpooler_url, ['myfakehost'], nil, nil) }.to raise_error(TokenError)
|
expect{ Pooler.delete(false, @vmpooler_url, ['myfakehost'], nil) }.to raise_error(TokenError)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe '#status' do
|
describe "#status" do
|
||||||
before :each do
|
before :each do
|
||||||
# smaller version
|
#smaller version
|
||||||
@status_response_body = '{"capacity":{"current":716,"total":717,"percent": 99.9},"status":{"ok":true,"message":"Battle station fully armed and operational."}}'
|
@status_response_body = "{\"capacity\":{\"current\":716,\"total\":717,\"percent\": 99.9},\"status\":{\"ok\":true,\"message\":\"Battle station fully armed and operational.\"}}"
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'prints the status' do
|
it "prints the status" do
|
||||||
stub_request(:get, "#{@vmpooler_url}/status")
|
stub_request(:get, "#{@vmpooler_url}/status").
|
||||||
.to_return(status: 200, body: @status_response_body, headers: {})
|
with(:headers => {'Accept'=>'*/*', 'Accept-Encoding'=>'gzip;q=1.0,deflate;q=0.6,identity;q=0.3', 'User-Agent'=>'Faraday v0.9.2'}).
|
||||||
|
to_return(:status => 200, :body => @status_response_body, :headers => {})
|
||||||
|
|
||||||
status = Pooler.status(false, @vmpooler_url)
|
status = Pooler.status(false, @vmpooler_url)
|
||||||
expect(status).to be_an_instance_of Hash
|
expect(status).to be_an_instance_of Hash
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe '#summary' do
|
describe "#summary" do
|
||||||
before :each do
|
before :each do
|
||||||
@status_response_body = ''
|
@status_response_body = ""
|
||||||
|
|
||||||
it 'prints the summary' do
|
it "prints the summary" do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe '#query' do
|
describe "#query" do
|
||||||
before :each do
|
before :each do
|
||||||
@query_response_body = '{"ok": true,"fq6qlpjlsskycq6":{"template":"debian-7-x86_64","lifetime": 2,"running": 0.08,"state":"running","snapshots":["n4eb4kdtp7rwv4x158366vd9jhac8btq" ],"domain": "delivery.puppetlabs.net"}}'
|
@query_response_body = "{\"ok\": true,\"fq6qlpjlsskycq6\":{\"template\":\"debian-7-x86_64\",\"lifetime\": 2,\"running\": 0.08,\"state\":\"running\",\"snapshots\":[\"n4eb4kdtp7rwv4x158366vd9jhac8btq\" ],\"domain\": \"delivery.puppetlabs.net\"}}"
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'makes a query about a vm' do
|
it "makes a query about a vm" do
|
||||||
stub_request(:get, "#{@vmpooler_url}/vm/fq6qlpjlsskycq6")
|
stub_request(:get, "#{@vmpooler_url}/vm/fq6qlpjlsskycq6").
|
||||||
.to_return(status: 200, body: @query_response_body, headers: {})
|
with(:headers => {'Accept'=>'*/*', 'Accept-Encoding'=>'gzip;q=1.0,deflate;q=0.6,identity;q=0.3', 'User-Agent'=>'Faraday v0.9.2'}).
|
||||||
|
to_return(:status => 200, :body => @query_response_body, :headers => {})
|
||||||
|
|
||||||
query_req = Pooler.query(false, @vmpooler_url, 'fq6qlpjlsskycq6')
|
query_req = Pooler.query(false, @vmpooler_url, 'fq6qlpjlsskycq6')
|
||||||
expect(query_req).to be_an_instance_of Hash
|
expect(query_req).to be_an_instance_of Hash
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe '#snapshot' do
|
describe "#snapshot" do
|
||||||
before :each do
|
before :each do
|
||||||
@snapshot_response_body = '{"ok":true}'
|
@snapshot_response_body = "{\"ok\":true}"
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'makes a snapshot for a single vm' do
|
it "makes a snapshot for a single vm" do
|
||||||
stub_request(:post, "#{@vmpooler_url}/vm/fq6qlpjlsskycq6/snapshot")
|
stub_request(:post, "#{@vmpooler_url}/vm/fq6qlpjlsskycq6/snapshot").
|
||||||
.with(headers: { 'X-Auth-Token' => 'mytokenfile' })
|
with(:headers => {'Accept'=>'*/*', 'Accept-Encoding'=>'gzip;q=1.0,deflate;q=0.6,identity;q=0.3', 'Content-Length'=>'0', 'User-Agent'=>'Faraday v0.9.2', 'X-Auth-Token'=>'mytokenfile'}).
|
||||||
.to_return(status: 200, body: @snapshot_response_body, headers: {})
|
to_return(:status => 200, :body => @snapshot_response_body, :headers => {})
|
||||||
|
|
||||||
snapshot_req = Pooler.snapshot(false, @vmpooler_url, 'fq6qlpjlsskycq6', 'mytokenfile')
|
snapshot_req = Pooler.snapshot(false, @vmpooler_url, 'fq6qlpjlsskycq6', 'mytokenfile')
|
||||||
expect(snapshot_req['ok']).to be true
|
expect(snapshot_req["ok"]).to be true
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe '#revert' do
|
describe "#revert" do
|
||||||
before :each do
|
before :each do
|
||||||
@revert_response_body = '{"ok":true}'
|
@revert_response_body = "{\"ok\":true}"
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'makes a request to revert a vm from a snapshot' do
|
it "makes a request to revert a vm from a snapshot" do
|
||||||
stub_request(:post, "#{@vmpooler_url}/vm/fq6qlpjlsskycq6/snapshot/dAfewKNfaweLKNve")
|
stub_request(:post, "#{@vmpooler_url}/vm/fq6qlpjlsskycq6/snapshot/dAfewKNfaweLKNve").
|
||||||
.with(headers: { 'X-Auth-Token' => 'mytokenfile' })
|
with(:headers => {'Accept'=>'*/*', 'Accept-Encoding'=>'gzip;q=1.0,deflate;q=0.6,identity;q=0.3', 'Content-Length'=>'0', 'User-Agent'=>'Faraday v0.9.2', 'X-Auth-Token'=>'mytokenfile'}).
|
||||||
.to_return(status: 200, body: @revert_response_body, headers: {})
|
to_return(:status => 200, :body => @revert_response_body, :headers => {})
|
||||||
|
|
||||||
revert_req = Pooler.revert(false, @vmpooler_url, 'fq6qlpjlsskycq6', 'mytokenfile', 'dAfewKNfaweLKNve')
|
revert_req = Pooler.revert(false, @vmpooler_url, 'fq6qlpjlsskycq6', 'mytokenfile', 'dAfewKNfaweLKNve')
|
||||||
expect(revert_req['ok']).to be true
|
expect(revert_req["ok"]).to be true
|
||||||
end
|
end
|
||||||
|
|
||||||
it "doesn't make a request to revert a vm if snapshot is not provided" do
|
it "doesn't make a request to revert a vm if snapshot is not provided" do
|
||||||
expect do
|
expect{ Pooler.revert(false, @vmpooler_url, 'fq6qlpjlsskycq6', 'mytokenfile', nil) }.to raise_error(RuntimeError, "Snapshot SHA provided was nil, could not revert fq6qlpjlsskycq6")
|
||||||
Pooler.revert(false, @vmpooler_url, 'fq6qlpjlsskycq6', 'mytokenfile',
|
|
||||||
nil)
|
|
||||||
end.to raise_error(RuntimeError, 'Snapshot SHA provided was nil, could not revert fq6qlpjlsskycq6')
|
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'raises a TokenError if no token was provided' do
|
it "raises a TokenError if no token was provided" do
|
||||||
expect { Pooler.revert(false, @vmpooler_url, 'myfakehost', nil, 'shaaaaaaa') }.to raise_error(TokenError)
|
expect{ Pooler.revert(false, @vmpooler_url, 'myfakehost', nil, 'shaaaaaaa') }.to raise_error(TokenError)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe '#disk' do
|
describe "#disk" do
|
||||||
before :each do
|
before :each do
|
||||||
@disk_response_body_success = '{"ok":true}'
|
@disk_response_body_success = "{\"ok\":true}"
|
||||||
@disk_response_body_fail = '{"ok":false}'
|
@disk_response_body_fail = "{\"ok\":false}"
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'makes a request to extend disk space of a vm' do
|
it "makes a request to extend disk space of a vm" do
|
||||||
stub_request(:post, "#{@vmpooler_url}/vm/fq6qlpjlsskycq6/disk/12")
|
stub_request(:post, "#{@vmpooler_url}/vm/fq6qlpjlsskycq6/disk/12").
|
||||||
.with(headers: { 'X-Auth-Token' => 'mytokenfile' }).to_return(status: 200, body: @disk_response_body_success, headers: {})
|
with(:headers => {'Accept'=>'*/*', 'Accept-Encoding'=>'gzip;q=1.0,deflate;q=0.6,identity;q=0.3', 'Content-Length'=>'0', 'User-Agent'=>'Faraday v0.9.2', 'X-Auth-Token'=>'mytokenfile'}). to_return(:status => 200, :body => @disk_response_body_success, :headers => {})
|
||||||
|
|
||||||
disk_req = Pooler.disk(false, @vmpooler_url, 'fq6qlpjlsskycq6', 'mytokenfile', 12)
|
disk_req = Pooler.disk(false, @vmpooler_url, 'fq6qlpjlsskycq6', 'mytokenfile', 12)
|
||||||
expect(disk_req['ok']).to be true
|
expect(disk_req["ok"]).to be true
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'raises a TokenError if no token was provided' do
|
it "raises a TokenError if no token was provided" do
|
||||||
expect { Pooler.disk(false, @vmpooler_url, 'myfakehost', nil, 12) }.to raise_error(TokenError)
|
expect{ Pooler.disk(false, @vmpooler_url, 'myfakehost', nil, 12) }.to raise_error(TokenError)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -1,79 +0,0 @@
|
||||||
# frozen_string_literal: true
|
|
||||||
|
|
||||||
require_relative '../../lib/vmfloaty/service'
|
|
||||||
|
|
||||||
describe Service do
|
|
||||||
describe '#initialize' do
|
|
||||||
it 'store configuration options' do
|
|
||||||
options = MockOptions.new({})
|
|
||||||
config = { 'url' => 'http://example.url' }
|
|
||||||
service = Service.new(options, config)
|
|
||||||
expect(service.config).to include config
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe '#get_new_token' do
|
|
||||||
it 'prompts the user for their password and retrieves a token' do
|
|
||||||
config = { 'user' => 'first.last', 'url' => 'http://default.url' }
|
|
||||||
service = Service.new(MockOptions.new, config)
|
|
||||||
allow($stdout).to receive(:puts).with('Enter your http://default.url service password:')
|
|
||||||
allow(Commander::UI).to(receive(:password)
|
|
||||||
.with('Enter your http://default.url service password:', '*')
|
|
||||||
.and_return('hunter2'))
|
|
||||||
allow(Auth).to(receive(:get_token)
|
|
||||||
.with(nil, config['url'], config['user'], 'hunter2')
|
|
||||||
.and_return('token-value'))
|
|
||||||
expect(service.get_new_token(nil)).to eql 'token-value'
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'prompts the user for their username and password if the username is unknown' do
|
|
||||||
config = { 'url' => 'http://default.url' }
|
|
||||||
service = Service.new(MockOptions.new({}), config)
|
|
||||||
allow($stdout).to receive(:puts).with 'Enter your http://default.url service username:'
|
|
||||||
allow($stdout).to receive(:puts).with "\n"
|
|
||||||
allow($stdin).to receive(:gets).and_return('first.last')
|
|
||||||
allow(Commander::UI).to(receive(:password)
|
|
||||||
.with('Enter your http://default.url service password:', '*')
|
|
||||||
.and_return('hunter2'))
|
|
||||||
allow(Auth).to(receive(:get_token)
|
|
||||||
.with(nil, config['url'], 'first.last', 'hunter2')
|
|
||||||
.and_return('token-value'))
|
|
||||||
expect(service.get_new_token(nil)).to eql 'token-value'
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe '#delete_token' do
|
|
||||||
it 'deletes a token' do
|
|
||||||
service = Service.new(MockOptions.new, 'user' => 'first.last', 'url' => 'http://default.url')
|
|
||||||
allow(Commander::UI).to(receive(:password)
|
|
||||||
.with('Enter your http://default.url service password:', '*')
|
|
||||||
.and_return('hunter2'))
|
|
||||||
allow(Auth).to(receive(:delete_token)
|
|
||||||
.with(nil, 'http://default.url', 'first.last', 'hunter2', 'token-value')
|
|
||||||
.and_return('ok' => true))
|
|
||||||
expect(service.delete_token(nil, 'token-value')).to eql('ok' => true)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe '#token_status' do
|
|
||||||
it 'reports the status of a token' do
|
|
||||||
config = {
|
|
||||||
'user' => 'first.last',
|
|
||||||
'url' => 'http://default.url'
|
|
||||||
}
|
|
||||||
options = MockOptions.new('token' => 'token-value')
|
|
||||||
service = Service.new(options, config)
|
|
||||||
status = {
|
|
||||||
'ok' => true,
|
|
||||||
'user' => config['user'],
|
|
||||||
'created' => '2017-09-22 02:04:18 +0000',
|
|
||||||
'last_accessed' => '2017-09-22 02:04:28 +0000',
|
|
||||||
'reserved_hosts' => []
|
|
||||||
}
|
|
||||||
allow(Auth).to(receive(:token_status)
|
|
||||||
.with(nil, config['url'], 'token-value')
|
|
||||||
.and_return(status))
|
|
||||||
expect(service.token_status(nil, 'token-value')).to eql(status)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
@ -1,112 +0,0 @@
|
||||||
# frozen_string_literal: true
|
|
||||||
|
|
||||||
require 'spec_helper'
|
|
||||||
require 'vmfloaty/ssh'
|
|
||||||
|
|
||||||
class ServiceStub
|
|
||||||
def retrieve(_verbose, os_types, _use_token, ondemand)
|
|
||||||
if os_types.keys[0] == 'abs_host_string'
|
|
||||||
return {
|
|
||||||
os_types.keys[0] => { 'hostname' => ['abs-hostname.delivery.puppetlabs.net'] },
|
|
||||||
'ok' => true
|
|
||||||
}
|
|
||||||
|
|
||||||
elsif os_types.keys[0] == 'vmpooler_api_v2_host_string'
|
|
||||||
return {
|
|
||||||
os_types.keys[0] => { 'hostname' => ['vmpooler-v2-hostname.delivery.puppetlabs.net'] },
|
|
||||||
'ok' => true
|
|
||||||
}
|
|
||||||
|
|
||||||
else
|
|
||||||
return {
|
|
||||||
os_types.keys[0] => { 'hostname' => 'vmpooler-v1-hostname' },
|
|
||||||
'domain' => 'delivery.puppetlabs.net',
|
|
||||||
'ok' => true
|
|
||||||
}
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def type
|
|
||||||
return 'abs' if os_types == 'abs_host_string'
|
|
||||||
return 'vmpooler' if os_types == 'vmpooler_api_v1_host_string' || os_types == 'vmpooler_api_v2_host_string'
|
|
||||||
end
|
|
||||||
|
|
||||||
def wait_for_request(verbose, requestid)
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe Ssh do
|
|
||||||
before :each do
|
|
||||||
end
|
|
||||||
|
|
||||||
context "for pooled requests" do
|
|
||||||
it 'gets a hostname string for abs' do
|
|
||||||
verbose = false
|
|
||||||
service = ServiceStub.new
|
|
||||||
host_os = 'abs_host_string'
|
|
||||||
use_token = false
|
|
||||||
cmd = Ssh.command_string(verbose, service, host_os, use_token)
|
|
||||||
expect(cmd).to match(/ssh root@abs-hostname.delivery.puppetlabs.net/)
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'gets a hostname string for vmpooler api v1' do
|
|
||||||
verbose = true
|
|
||||||
service = ServiceStub.new
|
|
||||||
host_os = 'vmpooler_api_v1_host_string'
|
|
||||||
use_token = false
|
|
||||||
cmd = Ssh.command_string(verbose, service, host_os, use_token)
|
|
||||||
expect(cmd).to match(/ssh root@vmpooler-v1-hostname.delivery.puppetlabs.net/)
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'gets a hostname string for vmpooler api v2' do
|
|
||||||
verbose = false
|
|
||||||
service = ServiceStub.new
|
|
||||||
host_os = 'vmpooler_api_v2_host_string'
|
|
||||||
use_token = false
|
|
||||||
cmd = Ssh.command_string(verbose, service, host_os, use_token)
|
|
||||||
expect(cmd).to match(/ssh root@vmpooler-v2-hostname.delivery.puppetlabs.net/)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context "for ondemand requests" do
|
|
||||||
let(:service) { ServiceStub.new }
|
|
||||||
let(:url) { 'http://pooler.example.com' }
|
|
||||||
|
|
||||||
it 'gets a hostname string for abs' do
|
|
||||||
verbose = false
|
|
||||||
host_os = 'abs_host_string'
|
|
||||||
use_token = false
|
|
||||||
ondemand = true
|
|
||||||
response = {'abs_host_string' => { 'hostname' => ['abs-hostname.delivery.puppetlabs.net']}}
|
|
||||||
allow(service).to receive(:url)
|
|
||||||
allow(service).to receive(:check_ondemandvm).and_return(response)
|
|
||||||
cmd = Ssh.command_string(verbose, service, host_os, use_token, ondemand)
|
|
||||||
expect(cmd).to match(/ssh root@abs-hostname.delivery.puppetlabs.net/)
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'gets a hostname string for abs' do
|
|
||||||
verbose = false
|
|
||||||
host_os = 'vmpooler_api_v1_host_string'
|
|
||||||
use_token = false
|
|
||||||
ondemand = true
|
|
||||||
response = {'vmpooler_api_v1_host_string' => { 'hostname' => ['vmpooler_api_v1_host_string.delivery.puppetlabs.net']}}
|
|
||||||
allow(service).to receive(:url)
|
|
||||||
allow(service).to receive(:check_ondemandvm).and_return(response)
|
|
||||||
cmd = Ssh.command_string(verbose, service, host_os, use_token, ondemand)
|
|
||||||
expect(cmd).to match(/ssh root@vmpooler_api_v1_host_string.delivery.puppetlabs.net/)
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'gets a hostname string for abs' do
|
|
||||||
verbose = false
|
|
||||||
host_os = 'vmpooler_api_v2_host_string'
|
|
||||||
use_token = false
|
|
||||||
ondemand = true
|
|
||||||
response = {'vmpooler_api_v2_host_string' => { 'hostname' => ['vmpooler_api_v2_host_string.delivery.puppetlabs.net']}}
|
|
||||||
allow(service).to receive(:url)
|
|
||||||
allow(service).to receive(:check_ondemandvm).and_return(response)
|
|
||||||
cmd = Ssh.command_string(verbose, service, host_os, use_token, ondemand)
|
|
||||||
expect(cmd).to match(/ssh root@vmpooler_api_v2_host_string.delivery.puppetlabs.net/)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
@ -1,727 +1,91 @@
|
||||||
# frozen_string_literal: true
|
|
||||||
|
|
||||||
require 'spec_helper'
|
require 'spec_helper'
|
||||||
require 'json'
|
require 'json'
|
||||||
require 'commander/command'
|
|
||||||
require_relative '../../lib/vmfloaty/utils'
|
require_relative '../../lib/vmfloaty/utils'
|
||||||
|
|
||||||
# allow changing config in service for tests
|
|
||||||
class Service
|
|
||||||
attr_writer :config
|
|
||||||
end
|
|
||||||
|
|
||||||
describe Utils do
|
describe Utils do
|
||||||
describe '#standardize_hostnames' do
|
|
||||||
|
describe "#get_hosts" do
|
||||||
before :each do
|
before :each do
|
||||||
@vmpooler_api_v1_response_body = '{
|
@hostname_hash = "{\"ok\":true,\"debian-7-i386\":{\"hostname\":[\"sc0o4xqtodlul5w\",\"4m4dkhqiufnjmxy\"]},\"debian-7-x86_64\":{\"hostname\":\"zb91y9qbrbf6d3q\"},\"domain\":\"company.com\"}"
|
||||||
"ok": true,
|
@format_hash = "{\"debian-7-i386\":[\"sc0o4xqtodlul5w.company.com\",\"4m4dkhqiufnjmxy.company.com\"],\"debian-7-x86_64\":\"zb91y9qbrbf6d3q.company.com\"}"
|
||||||
"domain": "delivery.mycompany.net",
|
|
||||||
"ubuntu-1610-x86_64": {
|
|
||||||
"hostname": ["gdoy8q3nckuob0i", "ctnktsd0u11p9tm"]
|
|
||||||
},
|
|
||||||
"centos-7-x86_64": {
|
|
||||||
"hostname": "dlgietfmgeegry2"
|
|
||||||
}
|
|
||||||
}'
|
|
||||||
@vmpooler_api_v2_response_body = '{
|
|
||||||
"ok": true,
|
|
||||||
"ubuntu-1610-x86_64": {
|
|
||||||
"hostname": ["gdoy8q3nckuob0i.delivery.mycompany.net", "ctnktsd0u11p9tm.delivery.mycompany.net"]
|
|
||||||
},
|
|
||||||
"centos-7-x86_64": {
|
|
||||||
"hostname": "dlgietfmgeegry2.delivery.mycompany.net"
|
|
||||||
}
|
|
||||||
}'
|
|
||||||
@nonstandard_response_body = '{
|
|
||||||
"ok": true,
|
|
||||||
"solaris-10-sparc": {
|
|
||||||
"hostname": ["sol10-10.delivery.mycompany.net", "sol10-11.delivery.mycompany.net"]
|
|
||||||
},
|
|
||||||
"ubuntu-16.04-power8": {
|
|
||||||
"hostname": "power8-ubuntu16.04-6.delivery.mycompany.net"
|
|
||||||
}
|
|
||||||
}'
|
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'formats a result from vmpooler v1 api into a hash of os to hostnames' do
|
it "formats a hostname hash into os, hostnames, and domain name" do
|
||||||
result = Utils.standardize_hostnames(JSON.parse(@vmpooler_api_v1_response_body))
|
|
||||||
expect(result).to eq('centos-7-x86_64' => ['dlgietfmgeegry2.delivery.mycompany.net'],
|
|
||||||
'ubuntu-1610-x86_64' => ['gdoy8q3nckuob0i.delivery.mycompany.net',
|
|
||||||
'ctnktsd0u11p9tm.delivery.mycompany.net'])
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'formats a result from vmpooler v2 api into a hash of os to hostnames' do
|
expect(Utils.format_hosts(JSON.parse(@hostname_hash))).to eq @format_hash
|
||||||
result = Utils.standardize_hostnames(JSON.parse(@vmpooler_api_v2_response_body))
|
|
||||||
expect(result).to eq('centos-7-x86_64' => ['dlgietfmgeegry2.delivery.mycompany.net'],
|
|
||||||
'ubuntu-1610-x86_64' => ['gdoy8q3nckuob0i.delivery.mycompany.net',
|
|
||||||
'ctnktsd0u11p9tm.delivery.mycompany.net'])
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'formats a result from the nonstandard pooler into a hash of os to hostnames' do
|
|
||||||
result = Utils.standardize_hostnames(JSON.parse(@nonstandard_response_body))
|
|
||||||
expect(result).to eq('solaris-10-sparc' => ['sol10-10.delivery.mycompany.net', 'sol10-11.delivery.mycompany.net'],
|
|
||||||
'ubuntu-16.04-power8' => ['power8-ubuntu16.04-6.delivery.mycompany.net'])
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe '#format_host_output' do
|
describe "#generate_os_hash" do
|
||||||
before :each do
|
before :each do
|
||||||
@vmpooler_results = {
|
@host_hash = {"centos"=>1, "debian"=>5, "windows"=>1}
|
||||||
'centos-7-x86_64' => ['dlgietfmgeegry2.delivery.mycompany.net'],
|
|
||||||
'ubuntu-1610-x86_64' => ['gdoy8q3nckuob0i.delivery.mycompany.net', 'ctnktsd0u11p9tm.delivery.mycompany.net']
|
|
||||||
}
|
|
||||||
@nonstandard_results = {
|
|
||||||
'solaris-10-sparc' => ['sol10-10.delivery.mycompany.net', 'sol10-11.delivery.mycompany.net'],
|
|
||||||
'ubuntu-16.04-power8' => ['power8-ubuntu16.04-6.delivery.mycompany.net']
|
|
||||||
}
|
|
||||||
@vmpooler_output = <<~OUT.chomp
|
|
||||||
- dlgietfmgeegry2.delivery.mycompany.net (centos-7-x86_64)
|
|
||||||
- gdoy8q3nckuob0i.delivery.mycompany.net (ubuntu-1610-x86_64)
|
|
||||||
- ctnktsd0u11p9tm.delivery.mycompany.net (ubuntu-1610-x86_64)
|
|
||||||
OUT
|
|
||||||
@nonstandard_output = <<~OUT.chomp
|
|
||||||
- sol10-10.delivery.mycompany.net (solaris-10-sparc)
|
|
||||||
- sol10-11.delivery.mycompany.net (solaris-10-sparc)
|
|
||||||
- power8-ubuntu16.04-6.delivery.mycompany.net (ubuntu-16.04-power8)
|
|
||||||
OUT
|
|
||||||
end
|
|
||||||
it 'formats a hostname hash from vmpooler into a list that includes the os' do
|
|
||||||
expect(Utils.format_host_output(@vmpooler_results)).to eq(@vmpooler_output)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'formats a hostname hash from the nonstandard pooler into a list that includes the os' do
|
it "takes an array of os arguments and returns a formatted hash" do
|
||||||
expect(Utils.format_host_output(@nonstandard_results)).to eq(@nonstandard_output)
|
host_arg = ["centos", "debian=5", "windows=1"]
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe '#get_service_object' do
|
|
||||||
it 'assumes vmpooler by default' do
|
|
||||||
expect(Utils.get_service_object).to be Pooler
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'uses abs when told explicitly' do
|
|
||||||
expect(Utils.get_service_object('abs')).to be ABS
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'uses nspooler when told explicitly' do
|
|
||||||
expect(Utils.get_service_object('nspooler')).to be NonstandardPooler
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'uses vmpooler when told explicitly' do
|
|
||||||
expect(Utils.get_service_object('vmpooler')).to be Pooler
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe '#get_service_config' do
|
|
||||||
before :each do
|
|
||||||
@default_config = {
|
|
||||||
'url' => 'http://default.url',
|
|
||||||
'user' => 'first.last.default',
|
|
||||||
'token' => 'default-token'
|
|
||||||
}
|
|
||||||
@services_config = {
|
|
||||||
'services' => {
|
|
||||||
'vm' => {
|
|
||||||
'url' => 'http://vmpooler.url',
|
|
||||||
'user' => 'first.last.vmpooler',
|
|
||||||
'token' => 'vmpooler-token'
|
|
||||||
},
|
|
||||||
'ns' => {
|
|
||||||
'url' => 'http://nspooler.url',
|
|
||||||
'user' => 'first.last.nspooler',
|
|
||||||
'token' => 'nspooler-token'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
it "returns the first service configured under 'services' as the default if available" do
|
|
||||||
config = @default_config.merge @services_config
|
|
||||||
options = MockOptions.new({})
|
|
||||||
expect(Utils.get_service_config(config, options)).to include @services_config['services']['vm']
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'allows selection by configured service key' do
|
|
||||||
config = @default_config.merge @services_config
|
|
||||||
options = MockOptions.new(service: 'ns')
|
|
||||||
expect(Utils.get_service_config(config, options)).to include @services_config['services']['ns']
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'uses top-level service config values as defaults when configured service values are missing' do
|
|
||||||
config = @default_config.merge @services_config
|
|
||||||
config['services']['vm'].delete 'url'
|
|
||||||
options = MockOptions.new(service: 'vm')
|
|
||||||
expect(Utils.get_service_config(config, options)['url']).to eq 'http://default.url'
|
|
||||||
end
|
|
||||||
|
|
||||||
it "raises an error if passed a service name that hasn't been configured" do
|
|
||||||
config = @default_config.merge @services_config
|
|
||||||
options = MockOptions.new(service: 'none')
|
|
||||||
expect { Utils.get_service_config(config, options) }.to raise_error ArgumentError
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'prioritizes values passed as command line options over configuration options' do
|
|
||||||
config = @default_config
|
|
||||||
options = MockOptions.new(url: 'http://alternate.url', token: 'alternate-token')
|
|
||||||
expected = config.merge('url' => 'http://alternate.url', 'token' => 'alternate-token')
|
|
||||||
expect(Utils.get_service_config(config, options)).to include expected
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe '#generate_os_hash' do
|
|
||||||
before :each do
|
|
||||||
@host_hash = { 'centos' => 1, 'debian' => 5, 'windows' => 1 }
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'takes an array of os arguments and returns a formatted hash' do
|
|
||||||
host_arg = ['centos', 'debian=5', 'windows=1']
|
|
||||||
expect(Utils.generate_os_hash(host_arg)).to eq @host_hash
|
expect(Utils.generate_os_hash(host_arg)).to eq @host_hash
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'returns an empty hash if there are no arguments provided' do
|
it "returns an empty hash if there are no arguments provided" do
|
||||||
host_arg = []
|
host_arg = []
|
||||||
expect(Utils.generate_os_hash(host_arg)).to be_empty
|
expect(Utils.generate_os_hash(host_arg)).to be_empty
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe '#print_fqdn_for_host' do
|
describe '#prettyprint_hosts' do
|
||||||
|
let(:host_without_tags) { 'mcpy42eqjxli9g2' }
|
||||||
|
let(:host_with_tags) { 'aiydvzpg23r415q' }
|
||||||
let(:url) { 'http://pooler.example.com' }
|
let(:url) { 'http://pooler.example.com' }
|
||||||
|
|
||||||
subject { Utils.print_fqdn_for_host(service, hostname, host_data) }
|
let(:host_info_with_tags) do
|
||||||
|
|
||||||
describe 'with vmpooler host' do
|
|
||||||
let(:service) { Service.new(MockOptions.new, 'url' => url) }
|
|
||||||
let(:hostname) { 'mcpy42eqjxli9g2' }
|
|
||||||
let(:domain) { 'delivery.mycompany.net' }
|
|
||||||
let(:fqdn) { [hostname, domain].join('.') }
|
|
||||||
|
|
||||||
let(:host_data) do
|
|
||||||
{
|
{
|
||||||
'template' => 'ubuntu-1604-x86_64',
|
host_with_tags => {
|
||||||
'lifetime' => 12,
|
"template" => "redhat-7-x86_64",
|
||||||
'running' => 9.66,
|
"lifetime" => 48,
|
||||||
'state' => 'running',
|
"running" => 7.67,
|
||||||
'ip' => '127.0.0.1',
|
"tags" => {
|
||||||
'domain' => domain
|
"user" => "bob",
|
||||||
}
|
"role" => "agent"
|
||||||
end
|
|
||||||
|
|
||||||
it 'outputs fqdn for host' do
|
|
||||||
expect($stdout).to receive(:puts).with(fqdn)
|
|
||||||
|
|
||||||
subject
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe 'with nonstandard pooler host' do
|
|
||||||
let(:service) { Service.new(MockOptions.new, 'url' => url, 'type' => 'ns') }
|
|
||||||
let(:hostname) { 'sol11-9.delivery.mycompany.net' }
|
|
||||||
let(:host_data) do
|
|
||||||
{
|
|
||||||
'fqdn' => hostname,
|
|
||||||
'os_triple' => 'solaris-11-sparc',
|
|
||||||
'reserved_by_user' => 'first.last',
|
|
||||||
'reserved_for_reason' => '',
|
|
||||||
'hours_left_on_reservation' => 35.89
|
|
||||||
}
|
|
||||||
end
|
|
||||||
let(:fqdn) { hostname } # for nspooler these are the same
|
|
||||||
|
|
||||||
it 'outputs fqdn for host' do
|
|
||||||
expect($stdout).to receive(:puts).with(fqdn)
|
|
||||||
|
|
||||||
subject
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe 'with ABS host' do
|
|
||||||
let(:service) { Service.new(MockOptions.new, 'url' => url, 'type' => 'abs') }
|
|
||||||
let(:hostname) { '1597952189390' }
|
|
||||||
let(:fqdn) { 'example-noun.delivery.puppetlabs.net' }
|
|
||||||
let(:template) { 'ubuntu-1604-x86_64' }
|
|
||||||
|
|
||||||
# This seems to be the miminal stub response from ABS for the current output
|
|
||||||
let(:host_data) do
|
|
||||||
{
|
|
||||||
'state' => 'allocated',
|
|
||||||
'allocated_resources' => [
|
|
||||||
{
|
|
||||||
'hostname' => fqdn,
|
|
||||||
'type' => template,
|
|
||||||
'enging' => 'vmpooler'
|
|
||||||
}
|
|
||||||
],
|
|
||||||
'request' => {
|
|
||||||
'job' => {
|
|
||||||
'id' => hostname
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'outputs fqdn for host' do
|
|
||||||
expect($stdout).to receive(:puts).with(fqdn)
|
|
||||||
|
|
||||||
subject
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe '#pretty_print_hosts' do
|
|
||||||
let(:url) { 'http://pooler.example.com' }
|
|
||||||
let(:verbose) { nil }
|
|
||||||
let(:print_to_stderr) { false }
|
|
||||||
|
|
||||||
before(:each) do
|
|
||||||
allow(service).to receive(:query)
|
|
||||||
.with(anything, hostname)
|
|
||||||
.and_return(response_body)
|
|
||||||
end
|
|
||||||
|
|
||||||
subject { Utils.pretty_print_hosts(verbose, service, hostname, print_to_stderr) }
|
|
||||||
|
|
||||||
describe 'with vmpooler api v2 service' do
|
|
||||||
let(:service) { Service.new(MockOptions.new, 'url' => url) }
|
|
||||||
|
|
||||||
let(:hostname) { 'mcpy42eqjxli9g2' }
|
|
||||||
let(:fqdn) { [hostname, 'delivery.puppetlabs.net'].join('.') }
|
|
||||||
|
|
||||||
let(:response_body) do
|
|
||||||
{
|
|
||||||
hostname => {
|
|
||||||
'template' => 'ubuntu-1604-x86_64',
|
|
||||||
'lifetime' => 12,
|
|
||||||
'running' => 9.66,
|
|
||||||
'state' => 'running',
|
|
||||||
'ip' => '127.0.0.1',
|
|
||||||
'fqdn' => fqdn
|
|
||||||
}
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
let(:default_output) { "- #{fqdn} (running, ubuntu-1604-x86_64, 9.66/12 hours)" }
|
|
||||||
|
|
||||||
it 'prints output with host fqdn, template and duration info' do
|
|
||||||
expect($stdout).to receive(:puts).with(default_output)
|
|
||||||
|
|
||||||
subject
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'when tags are supplied' do
|
|
||||||
let(:hostname) { 'aiydvzpg23r415q' }
|
|
||||||
let(:response_body) do
|
|
||||||
{
|
|
||||||
hostname => {
|
|
||||||
'template' => 'redhat-7-x86_64',
|
|
||||||
'lifetime' => 48,
|
|
||||||
'running' => 7.67,
|
|
||||||
'state' => 'running',
|
|
||||||
'tags' => {
|
|
||||||
'user' => 'bob',
|
|
||||||
'role' => 'agent'
|
|
||||||
},
|
},
|
||||||
'ip' => '127.0.0.1',
|
"domain" => "delivery.puppetlabs.net"
|
||||||
'fqdn' => fqdn
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'prints output with host fqdn, template, duration info, and tags' do
|
let(:host_info_without_tags) do
|
||||||
output = "- #{fqdn} (running, redhat-7-x86_64, 7.67/48 hours, user: bob, role: agent)"
|
|
||||||
|
|
||||||
expect($stdout).to receive(:puts).with(output)
|
|
||||||
|
|
||||||
subject
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'when print_to_stderr option is true' do
|
|
||||||
let(:print_to_stderr) { true }
|
|
||||||
|
|
||||||
it 'outputs to stderr instead of stdout' do
|
|
||||||
expect($stderr).to receive(:puts).with(default_output)
|
|
||||||
|
|
||||||
subject
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe 'with vmpooler api v1 service' do
|
|
||||||
let(:service) { Service.new(MockOptions.new, 'url' => url) }
|
|
||||||
|
|
||||||
let(:hostname) { 'mcpy42eqjxli9g2' }
|
|
||||||
let(:domain) { 'delivery.mycompany.net' }
|
|
||||||
let(:fqdn) { [hostname, domain].join('.') }
|
|
||||||
|
|
||||||
let(:response_body) do
|
|
||||||
{
|
{
|
||||||
hostname => {
|
host_without_tags => {
|
||||||
'template' => 'ubuntu-1604-x86_64',
|
"template" => "ubuntu-1604-x86_64",
|
||||||
'lifetime' => 12,
|
"lifetime" => 12,
|
||||||
'running' => 9.66,
|
"running" => 9.66,
|
||||||
'state' => 'running',
|
"domain" => "delivery.puppetlabs.net"
|
||||||
'ip' => '127.0.0.1',
|
|
||||||
'domain' => domain
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
let(:default_output) { "- #{fqdn} (running, ubuntu-1604-x86_64, 9.66/12 hours)" }
|
let(:output_with_tags) { "- #{host_with_tags}.delivery.puppetlabs.net (redhat-7-x86_64, 7.67/48 hours, user: bob, role: agent)" }
|
||||||
|
let(:output_without_tags) { "- #{host_without_tags}.delivery.puppetlabs.net (ubuntu-1604-x86_64, 9.66/12 hours)" }
|
||||||
|
|
||||||
it 'prints output with host fqdn, template and duration info' do
|
it 'prints an output with host fqdn, template and duration info' do
|
||||||
expect($stdout).to receive(:puts).with(default_output)
|
allow(Utils).to receive(:get_vm_info).
|
||||||
|
with(host_without_tags, false, url).
|
||||||
|
and_return(host_info_without_tags)
|
||||||
|
|
||||||
subject
|
expect(Utils).to receive(:puts).with("Running VMs:")
|
||||||
|
expect(Utils).to receive(:puts).with(output_without_tags)
|
||||||
|
|
||||||
|
Utils.prettyprint_hosts(host_without_tags, false, url)
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when tags are supplied' do
|
it 'prints an output with host fqdn, template, duration info, and tags when supplied' do
|
||||||
let(:hostname) { 'aiydvzpg23r415q' }
|
allow(Utils).to receive(:get_vm_info).
|
||||||
let(:response_body) do
|
with(host_with_tags, false, url).
|
||||||
{
|
and_return(host_info_with_tags)
|
||||||
hostname => {
|
|
||||||
'template' => 'redhat-7-x86_64',
|
|
||||||
'lifetime' => 48,
|
|
||||||
'running' => 7.67,
|
|
||||||
'state' => 'running',
|
|
||||||
'tags' => {
|
|
||||||
'user' => 'bob',
|
|
||||||
'role' => 'agent'
|
|
||||||
},
|
|
||||||
'ip' => '127.0.0.1',
|
|
||||||
'domain' => domain
|
|
||||||
}
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'prints output with host fqdn, template, duration info, and tags' do
|
expect(Utils).to receive(:puts).with("Running VMs:")
|
||||||
output = "- #{fqdn} (running, redhat-7-x86_64, 7.67/48 hours, user: bob, role: agent)"
|
expect(Utils).to receive(:puts).with(output_with_tags)
|
||||||
|
|
||||||
expect($stdout).to receive(:puts).with(output)
|
Utils.prettyprint_hosts(host_with_tags, false, url)
|
||||||
|
|
||||||
subject
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'when print_to_stderr option is true' do
|
|
||||||
let(:print_to_stderr) { true }
|
|
||||||
|
|
||||||
it 'outputs to stderr instead of stdout' do
|
|
||||||
expect($stderr).to receive(:puts).with(default_output)
|
|
||||||
|
|
||||||
subject
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe 'with nonstandard pooler service' do
|
|
||||||
let(:service) { Service.new(MockOptions.new, 'url' => url, 'type' => 'ns') }
|
|
||||||
|
|
||||||
let(:hostname) { 'sol11-9.delivery.mycompany.net' }
|
|
||||||
let(:response_body) do
|
|
||||||
{
|
|
||||||
hostname => {
|
|
||||||
'fqdn' => hostname,
|
|
||||||
'os_triple' => 'solaris-11-sparc',
|
|
||||||
'reserved_by_user' => 'first.last',
|
|
||||||
'reserved_for_reason' => '',
|
|
||||||
'hours_left_on_reservation' => 35.89
|
|
||||||
}
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
let(:default_output) { "- #{hostname} (solaris-11-sparc, 35.89h remaining)" }
|
|
||||||
|
|
||||||
it 'prints output with host, template, and time remaining' do
|
|
||||||
expect($stdout).to receive(:puts).with(default_output)
|
|
||||||
|
|
||||||
subject
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'when reason is supplied' do
|
|
||||||
let(:response_body) do
|
|
||||||
{
|
|
||||||
hostname => {
|
|
||||||
'fqdn' => hostname,
|
|
||||||
'os_triple' => 'solaris-11-sparc',
|
|
||||||
'reserved_by_user' => 'first.last',
|
|
||||||
'reserved_for_reason' => 'testing',
|
|
||||||
'hours_left_on_reservation' => 35.89
|
|
||||||
}
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'prints output with host, template, time remaining, and reason' do
|
|
||||||
output = '- sol11-9.delivery.mycompany.net (solaris-11-sparc, 35.89h remaining, reason: testing)'
|
|
||||||
|
|
||||||
expect($stdout).to receive(:puts).with(output)
|
|
||||||
|
|
||||||
subject
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'when print_to_stderr option is true' do
|
|
||||||
let(:print_to_stderr) { true }
|
|
||||||
|
|
||||||
it 'outputs to stderr instead of stdout' do
|
|
||||||
expect($stderr).to receive(:puts).with(default_output)
|
|
||||||
|
|
||||||
subject
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe 'with ABS service' do
|
|
||||||
let(:service) { Service.new(MockOptions.new, 'url' => url, 'type' => 'abs') }
|
|
||||||
|
|
||||||
let(:hostname) { '1597952189390' }
|
|
||||||
let(:fqdn) { 'example-noun.delivery.mycompany.net' }
|
|
||||||
let(:fqdn_hostname) { 'example-noun' }
|
|
||||||
let(:template) { 'ubuntu-1604-x86_64' }
|
|
||||||
|
|
||||||
# This seems to be the miminal stub response from ABS for the current output
|
|
||||||
let(:response_body) do
|
|
||||||
{
|
|
||||||
hostname => {
|
|
||||||
'state' => 'allocated',
|
|
||||||
'allocated_resources' => [
|
|
||||||
{
|
|
||||||
'hostname' => fqdn,
|
|
||||||
'type' => template,
|
|
||||||
'engine' => 'vmpooler'
|
|
||||||
}
|
|
||||||
],
|
|
||||||
'request' => {
|
|
||||||
'job' => {
|
|
||||||
'id' => hostname
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
# The vmpooler response contains metadata that is printed
|
|
||||||
let(:domain) { 'delivery.mycompany.net' }
|
|
||||||
let(:response_body_vmpooler) do
|
|
||||||
{
|
|
||||||
fqdn_hostname => {
|
|
||||||
'template' => template,
|
|
||||||
'lifetime' => 48,
|
|
||||||
'running' => 7.67,
|
|
||||||
'state' => 'running',
|
|
||||||
'tags' => {
|
|
||||||
'user' => 'bob',
|
|
||||||
'role' => 'agent'
|
|
||||||
},
|
|
||||||
'ip' => '127.0.0.1',
|
|
||||||
'domain' => domain
|
|
||||||
}
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
before(:each) do
|
|
||||||
allow(Utils).to receive(:get_vmpooler_service_config).and_return({
|
|
||||||
'url' => 'http://vmpooler.example.com',
|
|
||||||
'token' => 'krypto-knight'
|
|
||||||
})
|
|
||||||
allow(service).to receive(:query)
|
|
||||||
.with(anything, fqdn_hostname)
|
|
||||||
.and_return(response_body_vmpooler)
|
|
||||||
end
|
|
||||||
|
|
||||||
let(:default_output_first_line) { "- [JobID:#{hostname}] <allocated>" }
|
|
||||||
let(:default_output_second_line) { " - #{fqdn} (#{template})" }
|
|
||||||
|
|
||||||
it 'prints output with job id, host, and template' do
|
|
||||||
expect($stdout).to receive(:puts).with(default_output_first_line)
|
|
||||||
expect($stdout).to receive(:puts).with(default_output_second_line)
|
|
||||||
|
|
||||||
subject
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'prints more information when vmpooler_fallback is set output with job id, host, template, lifetime, user and role' do
|
|
||||||
fallback = { 'vmpooler_fallback' => 'vmpooler' }
|
|
||||||
service.config.merge! fallback
|
|
||||||
default_output_second_line = " - #{fqdn} (running, #{template}, 7.67/48 hours, user: bob, role: agent)"
|
|
||||||
expect($stdout).to receive(:puts).with(default_output_first_line)
|
|
||||||
expect($stdout).to receive(:puts).with(default_output_second_line)
|
|
||||||
|
|
||||||
subject
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'prints DESTROYED and hostname when destroyed' do
|
|
||||||
fallback = { 'vmpooler_fallback' => 'vmpooler' }
|
|
||||||
service.config.merge! fallback
|
|
||||||
response_body_vmpooler[fqdn_hostname]['state'] = 'destroyed'
|
|
||||||
default_output_second_line = " - DESTROYED #{fqdn}"
|
|
||||||
expect($stdout).to receive(:puts).with(default_output_first_line)
|
|
||||||
expect($stdout).to receive(:puts).with(default_output_second_line)
|
|
||||||
|
|
||||||
subject
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'when print_to_stderr option is true' do
|
|
||||||
let(:print_to_stderr) { true }
|
|
||||||
|
|
||||||
it 'outputs to stderr instead of stdout' do
|
|
||||||
expect($stderr).to receive(:puts).with(default_output_first_line)
|
|
||||||
expect($stderr).to receive(:puts).with(default_output_second_line)
|
|
||||||
|
|
||||||
subject
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe 'with ABS service returning vmpooler and nspooler resources' do
|
|
||||||
let(:service) { Service.new(MockOptions.new, 'url' => url, 'type' => 'abs') }
|
|
||||||
|
|
||||||
let(:hostname) { '1597952189390' }
|
|
||||||
let(:fqdn) { 'this-noun.delivery.mycompany.net' }
|
|
||||||
let(:fqdn_ns) { 'that-noun.delivery.mycompany.net' }
|
|
||||||
let(:fqdn_hostname) { 'this-noun' }
|
|
||||||
let(:fqdn_ns_hostname) { 'that-noun' }
|
|
||||||
let(:template) { 'ubuntu-1604-x86_64' }
|
|
||||||
let(:template_ns) { 'solaris-10-sparc' }
|
|
||||||
|
|
||||||
# This seems to be the miminal stub response from ABS for the current output
|
|
||||||
let(:response_body) do
|
|
||||||
{
|
|
||||||
hostname => {
|
|
||||||
'state' => 'allocated',
|
|
||||||
'allocated_resources' => [
|
|
||||||
{
|
|
||||||
'hostname' => fqdn,
|
|
||||||
'type' => template,
|
|
||||||
'engine' => 'vmpooler'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
'hostname' => fqdn_ns,
|
|
||||||
'type' => template_ns,
|
|
||||||
'engine' => 'nspooler'
|
|
||||||
}
|
|
||||||
],
|
|
||||||
'request' => {
|
|
||||||
'job' => {
|
|
||||||
'id' => hostname
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
# The vmpooler response contains metadata that is printed
|
|
||||||
let(:domain) { 'delivery.mycompany.net' }
|
|
||||||
let(:response_body_vmpooler) do
|
|
||||||
{
|
|
||||||
fqdn_hostname => {
|
|
||||||
'template' => template,
|
|
||||||
'lifetime' => 48,
|
|
||||||
'running' => 7.67,
|
|
||||||
'state' => 'running',
|
|
||||||
'tags' => {
|
|
||||||
'user' => 'bob',
|
|
||||||
'role' => 'agent'
|
|
||||||
},
|
|
||||||
'ip' => '127.0.0.1',
|
|
||||||
'domain' => domain
|
|
||||||
}
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
before(:each) do
|
|
||||||
allow(Utils).to receive(:get_vmpooler_service_config).and_return({
|
|
||||||
'url' => 'http://vmpooler.example.com',
|
|
||||||
'token' => 'krypto-knight'
|
|
||||||
})
|
|
||||||
allow(service).to receive(:query)
|
|
||||||
.with(anything, fqdn_hostname)
|
|
||||||
.and_return(response_body_vmpooler)
|
|
||||||
end
|
|
||||||
|
|
||||||
let(:default_output_first_line) { "- [JobID:#{hostname}] <allocated>" }
|
|
||||||
let(:default_output_second_line) { " - #{fqdn} (#{template})" }
|
|
||||||
let(:default_output_third_line) { " - #{fqdn_ns} (#{template_ns})" }
|
|
||||||
|
|
||||||
it 'prints output with job id, host, and template' do
|
|
||||||
expect($stdout).to receive(:puts).with(default_output_first_line)
|
|
||||||
expect($stdout).to receive(:puts).with(default_output_second_line)
|
|
||||||
expect($stdout).to receive(:puts).with(default_output_third_line)
|
|
||||||
|
|
||||||
subject
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'when print_to_stderr option is true' do
|
|
||||||
let(:print_to_stderr) { true }
|
|
||||||
|
|
||||||
it 'outputs to stderr instead of stdout' do
|
|
||||||
expect($stderr).to receive(:puts).with(default_output_first_line)
|
|
||||||
expect($stderr).to receive(:puts).with(default_output_second_line)
|
|
||||||
expect($stderr).to receive(:puts).with(default_output_third_line)
|
|
||||||
|
|
||||||
subject
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe '#get_vmpooler_service_config' do
|
|
||||||
let(:Conf) { double }
|
|
||||||
it 'returns an error if the vmpooler_fallback is not setup' do
|
|
||||||
config = {
|
|
||||||
'user' => 'foo',
|
|
||||||
'services' => {
|
|
||||||
'myabs' => {
|
|
||||||
'url' => 'http://abs.com',
|
|
||||||
'token' => 'krypto-night',
|
|
||||||
'type' => 'abs'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
allow(Conf).to receive(:read_config).and_return(config)
|
|
||||||
expect do
|
|
||||||
Utils.get_vmpooler_service_config(config['services']['myabs']['vmpooler_fallback'])
|
|
||||||
end.to raise_error(ArgumentError)
|
|
||||||
end
|
|
||||||
it 'returns an error if the vmpooler_fallback is setup but cannot be found' do
|
|
||||||
config = {
|
|
||||||
'user' => 'foo',
|
|
||||||
'services' => {
|
|
||||||
'myabs' => {
|
|
||||||
'url' => 'http://abs.com',
|
|
||||||
'token' => 'krypto-night',
|
|
||||||
'type' => 'abs',
|
|
||||||
'vmpooler_fallback' => 'myvmpooler'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
allow(Conf).to receive(:read_config).and_return(config)
|
|
||||||
expect do
|
|
||||||
Utils.get_vmpooler_service_config(config['services']['myabs']['vmpooler_fallback'])
|
|
||||||
end.to raise_error(ArgumentError,
|
|
||||||
/myvmpooler/)
|
|
||||||
end
|
|
||||||
it 'returns the vmpooler_fallback config' do
|
|
||||||
config = {
|
|
||||||
'user' => 'foo',
|
|
||||||
'services' => {
|
|
||||||
'myabs' => {
|
|
||||||
'url' => 'http://abs.com',
|
|
||||||
'token' => 'krypto-night',
|
|
||||||
'type' => 'abs',
|
|
||||||
'vmpooler_fallback' => 'myvmpooler'
|
|
||||||
},
|
|
||||||
'myvmpooler' => {
|
|
||||||
'url' => 'http://vmpooler.com',
|
|
||||||
'token' => 'krypto-knight'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
allow(Conf).to receive(:read_config).and_return(config)
|
|
||||||
expect(Utils.get_vmpooler_service_config(config['services']['myabs']['vmpooler_fallback'])).to include({
|
|
||||||
'url' => 'http://vmpooler.com',
|
|
||||||
'token' => 'krypto-knight',
|
|
||||||
'user' => 'foo',
|
|
||||||
'type' => 'vmpooler'
|
|
||||||
})
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -1,39 +0,0 @@
|
||||||
# frozen_string_literal: true
|
|
||||||
|
|
||||||
# All of the interfaces for the different services must be the
|
|
||||||
# same, otherwise there will be errors when you change the caller
|
|
||||||
# for the services from services.rb.
|
|
||||||
#
|
|
||||||
|
|
||||||
require_relative '../../lib/vmfloaty/pooler'
|
|
||||||
require_relative '../../lib/vmfloaty/abs'
|
|
||||||
require_relative '../../lib/vmfloaty/nonstandard_pooler'
|
|
||||||
|
|
||||||
shared_examples 'a vmfloaty service' do
|
|
||||||
it { is_expected.to respond_to(:delete).with(5).arguments }
|
|
||||||
it { is_expected.to respond_to(:disk).with(5).arguments }
|
|
||||||
it { is_expected.to respond_to(:list).with(3).arguments }
|
|
||||||
it { is_expected.to respond_to(:list_active).with(4).arguments }
|
|
||||||
it { is_expected.to respond_to(:modify).with(5).arguments }
|
|
||||||
it { is_expected.to respond_to(:retrieve).with(6).arguments }
|
|
||||||
it { is_expected.to respond_to(:revert).with(5).arguments }
|
|
||||||
it { is_expected.to respond_to(:query).with(3).arguments }
|
|
||||||
it { is_expected.to respond_to(:snapshot).with(4).arguments }
|
|
||||||
it { is_expected.to respond_to(:status).with(2).arguments }
|
|
||||||
it { is_expected.to respond_to(:summary).with(2).arguments }
|
|
||||||
end
|
|
||||||
|
|
||||||
describe Pooler do
|
|
||||||
subject { Pooler }
|
|
||||||
it_behaves_like 'a vmfloaty service'
|
|
||||||
end
|
|
||||||
|
|
||||||
describe ABS do
|
|
||||||
subject { ABS }
|
|
||||||
it_behaves_like 'a vmfloaty service'
|
|
||||||
end
|
|
||||||
|
|
||||||
describe NonstandardPooler do
|
|
||||||
subject { NonstandardPooler }
|
|
||||||
it_behaves_like 'a vmfloaty service'
|
|
||||||
end
|
|
||||||
|
|
@ -1,26 +1,17 @@
|
||||||
# frozen_string_literal: true
|
|
||||||
|
|
||||||
$LOAD_PATH.push File.expand_path('lib', __dir__)
|
|
||||||
require 'vmfloaty/version'
|
|
||||||
|
|
||||||
Gem::Specification.new do |s|
|
Gem::Specification.new do |s|
|
||||||
s.name = 'vmfloaty'
|
s.name = 'vmfloaty'
|
||||||
s.version = Vmfloaty::VERSION
|
s.version = '0.7.8'
|
||||||
s.authors = [
|
s.authors = ['Brian Cain']
|
||||||
'Brian Cain',
|
s.email = ['brian.cain@puppetlabs.com']
|
||||||
'Puppet'
|
s.license = 'Apache'
|
||||||
]
|
s.homepage = 'https://github.com/briancain/vmfloaty'
|
||||||
s.email = 'info@puppet.com'
|
|
||||||
s.license = 'Apache-2.0'
|
|
||||||
s.homepage = 'https://github.com/puppetlabs/vmfloaty'
|
|
||||||
s.description = 'A helper tool for vmpooler to help you stay afloat'
|
s.description = 'A helper tool for vmpooler to help you stay afloat'
|
||||||
s.summary = 'CLI application to interface with vmpooler'
|
s.summary = 'CLI application to interface with vmpooler'
|
||||||
|
|
||||||
s.executables = ['floaty']
|
s.executables = ['floaty']
|
||||||
s.files = Dir['LICENSE', 'README.md', 'lib/**/*', 'extras/**/*']
|
s.files = Dir['LICENSE', 'README.md', 'lib/**/*']
|
||||||
s.test_files = Dir['spec/**/*']
|
s.test_files = Dir['spec/**/*']
|
||||||
s.require_path = 'lib'
|
s.require_path = 'lib'
|
||||||
|
s.add_dependency 'commander', '~> 4.3'
|
||||||
s.add_dependency 'commander', '>= 4.4.3', '< 4.7.0'
|
s.add_dependency 'faraday', '~> 0.9'
|
||||||
s.add_dependency 'faraday', '~> 1.5', '>= 1.5.1'
|
s.add_dependency 'colorize', '0.8.1'
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,3 @@
|
||||||
|
url: 'http://vmpooler.example.com/api/v1'
|
||||||
user: 'brian'
|
user: 'brian'
|
||||||
services:
|
token: 'token here'
|
||||||
main:
|
|
||||||
url: 'https://vmpooler.example.net/api/v1'
|
|
||||||
token: 'tokenstring'
|
|
||||||
alternate:
|
|
||||||
url: 'https://vmpooler.example.com/api/v1'
|
|
||||||
token: 'alternate-tokenstring'
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue