Start adding state classes; update dependencies

This commit is contained in:
garrettmills 2020-02-27 00:31:07 -06:00
parent e2016069f2
commit a49a26da48
No known key found for this signature in database
GPG Key ID: 6ACD58D6ADACFC6E
14 changed files with 333 additions and 80 deletions

View File

@ -13,6 +13,11 @@ class ServiceConfig extends Injectable {
install_to: 'multi-user.target',
}
constructor(merge_data = {}) {
super()
this._data = {...this._data, ...merge_data}
}
name(set) { return this._get_or_set('name', set) }
description(set) { return this._get_or_set('description', set) }

View File

@ -0,0 +1,11 @@
class Routine {
constructor(steps) {
this.steps = steps
}
apply_to(host) {
// TODO
}
}
module.exports = exports = Routine

View File

@ -5,6 +5,8 @@ class DirectoryState extends State {
if ( !(await this.check()) ) {
const path = await this._path()
await path.touch(true)
// TODO directories
}
}

View File

@ -1,64 +0,0 @@
const State = require('../State')
const UniversalPath = require('../../logical/UniversalPath')
class FileContentState extends State {
static get services() {
return [...super.services, 'hosts']
}
async apply() {
const target_path = await this._target()
if ( target_path.is_valid() ) {
}
if ( this._config.source ) {
const source_path = await this._source()
await source_path.copy_to(target_path)
} else {
await target_path.echo(this._config.contents)
}
}
async check() {
if ( this._config.source ) {
const source_path = await this._source()
const source_hash = await source_path.hash()
const target_path = await this._target()
if ( !target_path.is_file() ) return false
const target_hash = await target_path.hash()
return source_hash === target_hash
} else {
const localhost = this.hosts.get('localhost')
const temp_path = await localhost.get_temp_file()
await temp_path.echo(this._config.contents)
const source_hash = await temp_path.hash()
const target_path = await this._target()
if ( !target_path.is_file() ) return false
const target_hash = await target_path.hash()
return source_hash === target_hash
}
}
async reverse() {
await (await this._target()).echo('')
}
async _target() {
if ( typeof this._config.target === 'string' ) {
return UniversalPath.fromString(this._config.target)
} else return this._config.target
}
async _source() {
if ( typeof this._config.target === 'string' ) {
return UniversalPath.fromString(this._config.target)
} else return this._config.target
}
}
module.exports = exports = FileContentState

View File

@ -5,6 +5,12 @@ class FileState extends State {
if ( !(await this.check()) ) {
const path = await this._path()
await path.touch()
if ( this._config.contents ) {
await path.echo(this._config.contents)
} else if ( this._config.template ) {
await path.copy_from(this._config.template)
}
}
}

View File

@ -0,0 +1,28 @@
const State = require('../State')
class PackageCacheClearedState extends State {
static get services() {
return [...super.services, 'output']
}
constructor(host, config) {
if ( !host.packages ) {
throw new Error(`Cannot apply package state to host ${host.name}: missing package manager API.`)
}
super(host, config)
}
async apply() {
return this._host.packages.clear_cache()
}
async check() {
return false
}
async reverse() {
this.output.warn(`Package cache cleared state does not currently support reversal. (Host: ${this._host.name})`)
}
}
module.exports = exports = PackageCacheClearedState

View File

@ -0,0 +1,24 @@
const State = require('../State')
class PackageState extends State {
constructor(host, config) {
if ( !host.packages ) {
throw new Error(`Cannot apply package state to host ${host.name}: missing package manager API.`)
}
super(host, config)
}
async apply() {
return this._host.packages.install(this._config.package)
}
async check() {
return this._host.packages.is_installed(this._config.package)
}
async reverse() {
return this._host.packages.uninstall(this._config.package)
}
}
module.exports = exports = PackageState

View File

@ -0,0 +1,28 @@
const State = require('../State')
class ServiceDaemonReloadState extends State {
static get services() {
return [...super.services, 'output']
}
constructor(host, config) {
if ( !host.services ) {
throw new Error(`Cannot apply service state to host ${host.name}: missing service manager API.`)
}
super(host, config)
}
async apply() {
return this._host.services.daemon_reload()
}
async check() {
return false
}
async reverse() {
this.output.warn(`Service daemon reload state does not currently support reversal. (Host: ${this._host.name})`)
}
}
module.exports = exports = ServiceDaemonReloadState

View File

@ -0,0 +1,29 @@
const State = require('../State')
class ServiceRestartState extends State {
static get services() {
return [...super.services, 'output']
}
constructor(host, config) {
if ( !host.services ) {
throw new Error(`Cannot apply service state to host ${host.name}: missing service manager API.`)
}
super(host, config)
}
async apply() {
const services = Array.isArray(this._config.service) ? this._config.service : [this._config.service]
return this._host.services.restart(...services)
}
async check() {
return false
}
async reverse() {
this.output.warn(`Service restart state does not currently support reversal. (Host: ${this._host.name})`)
}
}
module.exports = exports = ServiceRestartState

View File

@ -0,0 +1,29 @@
const State = require('../State')
class ServiceState extends State {
constructor(host, config) {
if ( !host.services ) {
throw new Error(`Cannot apply service state to host ${host.name}: missing service manager API.`)
}
super(host, config)
}
async apply() {
const services = Array.isArray(this._config.service) ? this._config.service : [this._config.service]
return this._host.services.start(...services)
}
async check() {
const services = Array.isArray(this._config.service) ? this._config.service : [this._config.service]
let states = await this._host.services.status(...services)
if ( !Array.isArray(states) ) states = [states]
return states.every(x => x.status === 'running')
}
async reverse() {
const services = Array.isArray(this._config.service) ? this._config.service : [this._config.service]
return this._host.services.stop(...services)
}
}
module.exports = exports = ServiceState

View File

@ -0,0 +1,29 @@
const State = require('../State')
class UpdateState extends State {
static get services() {
return [...super.services, 'output']
}
constructor(host, config) {
if ( !host.packages ) {
throw new Error(`Cannot apply update state to host ${host.name}: missing package manager API.`)
}
super(host, config)
}
async apply() {
const packages = (await this._host.packages.list_updates()).map(x => x.name)
await this._host.packages.update(...packages)
}
async check() {
return (await this._host.packages.list_updates()).length === 0
}
async reverse() {
this.output.warn(`Update state does not currently support reversing package updates. (Host: ${this._host.name})`)
}
}
module.exports = exports = UpdateState

View File

@ -0,0 +1,50 @@
const { Service } = require('flitter-di')
/*
* states Service
* -------------------------------------------------------------
* This is a service file that will be made available through Flitter's
* dependency injector to the rest of the application based on its given
* canonical name.
*
* e.g. app.di().service("states")
*/
class StatesService extends Service {
static #state_map = {
// TODO apache and nginx states - virtual host, reverse proxy
// TODO file/directory permissions state - chmod & chown
// TODO file download state
// TODO file unpack state - zips, tarballs
// TODO package repository states - import keys, install repository
// TODO service manager states - service enabled, service installed
// TODO git states - clone repo, ref checked out
'fs.file': require('../classes/state/fs/FileState'),
'fs.directory': require('../classes/state/fs/DirectoryState'),
'package.present': require('../classes/state/os/PackageState'),
'package.updates': require('../classes/state/os/UpdateState'),
'package.cache.clear': require('../classes/state/os/PackageCacheClearedState'),
'service.running': require('../classes/state/os/ServiceState'),
'service.restarted': require('../classes/state/os/ServiceRestartState'),
'service.daemon.reloaded': require('../classes/state/os/ServiceDaemonReloadState'),
}
static get services() {
return [...super.services, 'app', 'configs']
}
from_config(host, state_config) {
const type = state_config.type
delete state_config.type
const StepClass = this.constructor.#state_map[type]
this.app.di().make(StepClass)
if ( !StepClass ) throw new Error(`Invalid or unknown step type: ${type}`)
return new StepClass(host, state_config)
}
}
module.exports = exports = StatesService

View File

@ -19,12 +19,12 @@
"cross-zip": "^2.1.6",
"flitter-agenda": "^0.5.0",
"flitter-auth": "^0.15.1",
"flitter-cli": "^0.15.1",
"flitter-di": "^0.4.0",
"flitter-flap": "^0.5.1",
"flitter-cli": "^0.15.2",
"flitter-di": "^0.4.1",
"flitter-flap": "^0.5.2",
"flitter-forms": "^0.8.0",
"flitter-upload": "^0.7.6",
"libflitter": "^0.44.0",
"libflitter": "^0.46.8",
"moment": "^2.24.0",
"scp": "^0.0.3",
"ssh2": "^0.8.7",

100
yarn.lock
View File

@ -399,6 +399,14 @@ bcrypt@^3.0.4:
nan "2.13.2"
node-pre-gyp "0.12.0"
bl@^2.2.0:
version "2.2.0"
resolved "https://registry.yarnpkg.com/bl/-/bl-2.2.0.tgz#e1a574cdf528e4053019bb800b041c0ac88da493"
integrity sha512-wbgvOpqopSr7uq6fJrLH8EsvYMJf9gzfo2jCsL2eTy75qXPukA4pCgHamOQkZtY5vmfVtjB+P3LNlMHW5CEZXA==
dependencies:
readable-stream "^2.3.5"
safe-buffer "^5.1.1"
bluebird@3.5.1:
version "3.5.1"
resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.5.1.tgz#d9551f9de98f1fcda1e683d17ee91a0602ee2eb9"
@ -768,6 +776,11 @@ delegates@^1.0.0:
resolved "https://registry.yarnpkg.com/delegates/-/delegates-1.0.0.tgz#84c6e159b81904fdca59a0ef44cd870d31250f9a"
integrity sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=
denque@^1.4.1:
version "1.4.1"
resolved "https://registry.yarnpkg.com/denque/-/denque-1.4.1.tgz#6744ff7641c148c3f8a69c307e51235c1f4a37cf"
integrity sha512-OfzPuSZKGcgr96rf1oODnfjqBFmr1DVoc/TrItj3Ohe0Ah1C5WX5Baquw/9U9KovnQ88EqmJbD66rKYUQYN1tQ==
depd@~1.1.2:
version "1.1.2"
resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9"
@ -1030,10 +1043,10 @@ flitter-auth@^0.15.1:
uuid "^3.3.2"
validator "^10.11.0"
flitter-cli@^0.15.1:
version "0.15.1"
resolved "https://registry.yarnpkg.com/flitter-cli/-/flitter-cli-0.15.1.tgz#21aed6154c248e9e271918af8b5d5a986a98eb95"
integrity sha512-7Hnecf8YJN9vR0PZggk68KhD7dKX/YXup3nbThq5DAJmGewwdakTa8/wNUGnZoR5CAtyynSKxaIsJ4M/iwledw==
flitter-cli@^0.15.2:
version "0.15.2"
resolved "https://registry.yarnpkg.com/flitter-cli/-/flitter-cli-0.15.2.tgz#67a299711debccb3902a398301d2130ac31b00dc"
integrity sha512-xHt6c0QwLghBs54o4qWnq6drwwvTppMfM7ZlUJ3IBaqx9cxM59bipP8FMXLVUTm8gxH7AekJPSROCDK89ij+eA==
dependencies:
command-line-args "^5.0.2"
es6-promisify "^6.0.1"
@ -1045,6 +1058,11 @@ flitter-di@^0.4.0:
resolved "https://registry.yarnpkg.com/flitter-di/-/flitter-di-0.4.0.tgz#d4845e07624c06d178403cdfb79f0e400c1483aa"
integrity sha512-Zgsqct2dpdXPHCeQHZZQ3qoLjM7aTx/WspgoSrQjleZZPkMynov+TMFntkzkrdkKtIySq7BjJUW3e48MLS9v4Q==
flitter-di@^0.4.1:
version "0.4.1"
resolved "https://registry.yarnpkg.com/flitter-di/-/flitter-di-0.4.1.tgz#c77c40807aea5d3a3cc4f8f5eb2338b63108428d"
integrity sha512-Px1tJYCpeCV/Tl9EjUX202+Y9mpIkZfDpBNmKkgBLkcmBvuAXN4eGTUlBUxWUiTlv1zhePzoxRe9vEEiCzQ4aQ==
flitter-flap@^0.4.0:
version "0.4.0"
resolved "https://registry.yarnpkg.com/flitter-flap/-/flitter-flap-0.4.0.tgz#ab1cdfc5b9ffe0b5b9fe2e3a8bdc87e9aac82c0e"
@ -1055,10 +1073,10 @@ flitter-flap@^0.4.0:
node-migration "^1.0.1"
touch "^3.1.0"
flitter-flap@^0.5.1:
version "0.5.1"
resolved "https://registry.yarnpkg.com/flitter-flap/-/flitter-flap-0.5.1.tgz#35f0c5eaca21c6704ec671930b68ca98fa7f905a"
integrity sha512-syxaLdvoDI8zx/HDoKsj1bM1DQKYDQLxHWS7uC3eGXbCv/fvcbYHoTkSKCJu1io4jVPi8BzGuhkwEmhEvMBuzw==
flitter-flap@^0.5.2:
version "0.5.2"
resolved "https://registry.yarnpkg.com/flitter-flap/-/flitter-flap-0.5.2.tgz#eeca4dfdfc32b79da46fb64b58a156b052c09239"
integrity sha512-hM605+tynZqmrlD3BnvXdEF6fe3ZXX48awRTKCouMSLdCWW5Ycx9leb87QgTLitKQpWXB+eMXvZUu+6Q4KjzHA==
dependencies:
del "^4.1.0"
js-beautify "^1.10.2"
@ -1074,6 +1092,17 @@ flitter-forms@^0.8.0:
recursive-readdir "^2.2.2"
validator "^10.11.0"
flitter-orm@^0.2.4:
version "0.2.4"
resolved "https://registry.yarnpkg.com/flitter-orm/-/flitter-orm-0.2.4.tgz#539f7631fd286955b01ce6034a0bb68142540f5d"
integrity sha512-7yhwwzzBpPIyW4VC9nHY+Pe9pM+EFYwljYKkK1BEMy8XNk6JADhcLiwZGJmxK38vQ8D7SEdFpZiux3fB68uVnQ==
dependencies:
flitter-di "^0.4.0"
json-stringify-safe "^5.0.1"
mongodb "^3.5.1"
object-hash "^2.0.1"
uuid "^3.4.0"
flitter-upload@^0.7.6:
version "0.7.6"
resolved "https://registry.yarnpkg.com/flitter-upload/-/flitter-upload-0.7.6.tgz#eeae6c2cf63609e84670be1ea2c3af78ed935f99"
@ -1451,6 +1480,11 @@ jsesc@~0.5.0:
resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-0.5.0.tgz#e7dee66e35d6fc16f710fe91d5cf69f70f08911d"
integrity sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0=
json-stringify-safe@^5.0.1:
version "5.0.1"
resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb"
integrity sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=
json5@^0.4.0:
version "0.4.0"
resolved "https://registry.yarnpkg.com/json5/-/json5-0.4.0.tgz#054352e4c4c80c86c0923877d449de176a732c8d"
@ -1536,10 +1570,10 @@ leven@^1.0.2:
resolved "https://registry.yarnpkg.com/leven/-/leven-1.0.2.tgz#9144b6eebca5f1d0680169f1a6770dcea60b75c3"
integrity sha1-kUS27ryl8dBoAWnxpncNzqYLdcM=
libflitter@^0.44.0:
version "0.44.0"
resolved "https://registry.yarnpkg.com/libflitter/-/libflitter-0.44.0.tgz#f3674d0a3a639fc14efbfdbf37a9388e35d74adb"
integrity sha512-Dp/TXRNzeiiwvP+TxagdGX5aIzKXF6wU98xc5hObCoRkrqPGHMpaNJylxgU5fVopgwT1Li0gC317nApXOcxcmA==
libflitter@^0.46.8:
version "0.46.8"
resolved "https://registry.yarnpkg.com/libflitter/-/libflitter-0.46.8.tgz#1500d70956628ff1db9719ff4086a8b0b313aa6e"
integrity sha512-ZAo4iUeTLz+6crqQG9UUJyliT6L7+kuZyTB39q8FC8qIdwtLd6C28Jkx82sBcYp9JZ5qJsbNYjSlF42Yt/msbg==
dependencies:
colors "^1.3.3"
connect-mongodb-session "^2.2.0"
@ -1551,6 +1585,7 @@ libflitter@^0.44.0:
express-graphql "^0.9.0"
express-session "^1.15.6"
flitter-di "^0.4.0"
flitter-orm "^0.2.4"
graphql "^14.5.4"
http-status "^1.4.2"
mongo-schematic-class "^1.0.3"
@ -1796,6 +1831,19 @@ mongodb@3.3.3:
optionalDependencies:
saslprep "^1.0.0"
mongodb@^3.5.1:
version "3.5.4"
resolved "https://registry.yarnpkg.com/mongodb/-/mongodb-3.5.4.tgz#f7609cfa9f8c56c35e844b4216ddc3a1b1ec5bef"
integrity sha512-xGH41Ig4dkSH5ROGezkgDbsgt/v5zbNUwE3TcFsSbDc6Qn3Qil17dhLsESSDDPTiyFDCPJRpfd4887dtsPgKtA==
dependencies:
bl "^2.2.0"
bson "^1.1.1"
denque "^1.4.1"
require_optional "^1.0.1"
safe-buffer "^5.1.2"
optionalDependencies:
saslprep "^1.0.0"
mongodb@~3.4.0:
version "3.4.1"
resolved "https://registry.yarnpkg.com/mongodb/-/mongodb-3.4.1.tgz#0d15e57e0ea0fc85b7a4fb9291b374c2e71652dc"
@ -2030,6 +2078,11 @@ object-assign@^4.0.1, object-assign@^4.1.0:
resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863"
integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=
object-hash@^2.0.1:
version "2.0.3"
resolved "https://registry.yarnpkg.com/object-hash/-/object-hash-2.0.3.tgz#d12db044e03cd2ca3d77c0570d87225b02e1e6ea"
integrity sha512-JPKn0GMu+Fa3zt3Bmr66JhokJU5BaNBIh4ZeTlaCBzrBsOeXzwcKKAK1tbLiPKgvwmPXsDvvLHoWh5Bm7ofIYg==
on-finished@~2.3.0:
version "2.3.0"
resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.3.0.tgz#20f1336481b083cd75337992a16971aa2d906947"
@ -2381,6 +2434,19 @@ readable-stream@^2.0.6:
string_decoder "~1.1.1"
util-deprecate "~1.0.1"
readable-stream@^2.3.5:
version "2.3.7"
resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.7.tgz#1eca1cf711aef814c04f62252a36a62f6cb23b57"
integrity sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==
dependencies:
core-util-is "~1.0.0"
inherits "~2.0.3"
isarray "~1.0.0"
process-nextick-args "~2.0.0"
safe-buffer "~5.1.1"
string_decoder "~1.1.1"
util-deprecate "~1.0.1"
recast@0.10.33:
version "0.10.33"
resolved "https://registry.yarnpkg.com/recast/-/recast-0.10.33.tgz#942808f7aa016f1fa7142c461d7e5704aaa8d697"
@ -2543,6 +2609,11 @@ safe-buffer@5.1.2, safe-buffer@^5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1:
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d"
integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==
safe-buffer@^5.1.1:
version "5.2.0"
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.0.tgz#b74daec49b1148f88c64b68d49b1e815c1f2f519"
integrity sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg==
safe-json-parse@~1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/safe-json-parse/-/safe-json-parse-1.0.1.tgz#3e76723e38dfdda13c9b1d29a1e07ffee4b30b57"
@ -2956,6 +3027,11 @@ uuid@^3.3.2:
resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.3.2.tgz#1b4af4955eb3077c501c23872fc6513811587131"
integrity sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==
uuid@^3.4.0:
version "3.4.0"
resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee"
integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==
uuid@latest:
version "3.3.3"
resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.3.3.tgz#4568f0216e78760ee1dbf3a4d2cf53e224112866"