diff --git a/core/driverdb/drivers_linux.go b/core/driverdb/drivers_linux.go index b480517fc..a21ddab43 100644 --- a/core/driverdb/drivers_linux.go +++ b/core/driverdb/drivers_linux.go @@ -11,8 +11,10 @@ import ( _ "github.com/opensvc/om3/drivers/poolloop" _ "github.com/opensvc/om3/drivers/poolvg" _ "github.com/opensvc/om3/drivers/rescontainerdocker" + _ "github.com/opensvc/om3/drivers/rescontainerdockercli" _ "github.com/opensvc/om3/drivers/rescontainerkvm" _ "github.com/opensvc/om3/drivers/rescontainerlxc" + _ "github.com/opensvc/om3/drivers/rescontainerpodman" _ "github.com/opensvc/om3/drivers/rescontainervbox" _ "github.com/opensvc/om3/drivers/resdiskcrypt" _ "github.com/opensvc/om3/drivers/resdiskdrbd" diff --git a/core/nodeaction/node.go b/core/nodeaction/node.go index 0d81783f8..622e261fc 100644 --- a/core/nodeaction/node.go +++ b/core/nodeaction/node.go @@ -13,6 +13,8 @@ import ( "sigs.k8s.io/yaml" "github.com/google/uuid" + "github.com/rs/zerolog/log" + "github.com/opensvc/om3/core/actionrouter" "github.com/opensvc/om3/core/client" "github.com/opensvc/om3/core/event" @@ -27,7 +29,6 @@ import ( "github.com/opensvc/om3/util/plog" "github.com/opensvc/om3/util/pubsub" "github.com/opensvc/om3/util/xsession" - "github.com/rs/zerolog/log" ) type ( diff --git a/core/object/core_action.go b/core/object/core_action.go index f7b7e8ca2..0f41ef2f7 100644 --- a/core/object/core_action.go +++ b/core/object/core_action.go @@ -285,11 +285,12 @@ func (t *actor) action(ctx context.Context, fn resourceset.DoFunc) error { Attr("argv", os.Args). Attr("cwd", wd). Attr("action", action.Name). - Attr("origin", env.Origin()) - logger.Infof("do %s (origin %s)", os.Args, env.Origin()) + Attr("origin", env.Origin()). + Attr("crm", "true") + logger.Infof("do %s %s (origin %s)", action.Name, os.Args, env.Origin()) beginTime := time.Now() defer func() { - logger.Attr("duration", time.Now().Sub(beginTime)).Infof("done %s in %s", os.Args, time.Now().Sub(beginTime)) + logger.Attr("duration", time.Now().Sub(beginTime)).Infof("done %s %s in %s", action.Name, os.Args, time.Now().Sub(beginTime)) }() // daemon instance monitor updates diff --git a/daemon_status.json b/daemon_status.json deleted file mode 100644 index 31f526dab..000000000 --- a/daemon_status.json +++ /dev/null @@ -1,2026 +0,0 @@ -{ - "cluster": { - "id": "e3f02dd2-60f1-11eb-9c7b-cbb92200e3c3", - "name": "u2004-cga", - "nodes": [ - "u2004-local-1", - "u2004-local-2" - ] - }, - "collector": { - "configured": 0, - "created": 0, - "state": "", - "tid": 0 - }, - "dns": { - "configured": 1617109557.372303, - "created": 1617109162.976318, - "state": "running", - "tid": 2646 - }, - "scheduler": { - "configured": 1617109162.9794884, - "created": 1617109162.9794884, - "state": "running", - "tid": 2649, - "delayed": [ - { - "action": "sync_all", - "csum": "e522d792e2c0e927d71b2a93e835d01b", - "expire": 0, - "path": "git-webhook", - "queued": 1617109570.6377351, - "rid": "sync#i0" - }, - { - "action": "collect_stats", - "csum": "ac7d5715c8f29a897dc3689f3726fade,1809592f347e0cacd917fe24b4aee3bb", - "expire": 0, - "path": "", - "queued": 1617109784.5912282, - "rid": "" - }, - { - "action": "status", - "csum": "4f135510aefa6997790b2c70186baec1", - "expire": 0, - "path": "flag", - "queued": 1617109784.5912282, - "rid": "" - }, - { - "action": "status", - "csum": "80724c030eb7b126c8acc3666370948a", - "expire": 0, - "path": "flag-18", - "queued": 1617109784.5912282, - "rid": "" - }, - { - "action": "status", - "csum": "8a6b86f760b54015c0bca23711b8964c", - "expire": 0, - "path": "flag-13", - "queued": 1617109784.5912282, - "rid": "" - }, - { - "action": "status", - "csum": "27ec335a0d1adfea26bf4dd779a1890d", - "expire": 0, - "path": "flag-19", - "queued": 1617109784.5912282, - "rid": "" - }, - { - "action": "status", - "csum": "e522d792e2c0e927d71b2a93e835d01b", - "expire": 0, - "path": "git-webhook", - "queued": 1617109784.5912282, - "rid": "" - }, - { - "action": "status", - "csum": "ee8765d9934085b50c4e582bd6dd2ce1", - "expire": 0, - "path": "flag-12", - "queued": 1617109784.5912282, - "rid": "" - }, - { - "action": "status", - "csum": "d92f95513c6256d300308089f2b02855", - "expire": 0, - "path": "flag-11", - "queued": 1617109784.5912282, - "rid": "" - }, - { - "action": "status", - "csum": "7f139c90b65452253f0a387470f30000", - "expire": 0, - "path": "flag-10", - "queued": 1617109784.5912282, - "rid": "" - }, - { - "action": "status", - "csum": "9e00733e32281422b842263b33e48dea", - "expire": 0, - "path": "flag-17", - "queued": 1617109786.684085, - "rid": "" - }, - { - "action": "status", - "csum": "6d47c72ba6e758bb8b82165bbc2d9509", - "expire": 0, - "path": "flag-16", - "queued": 1617109786.684085, - "rid": "" - }, - { - "action": "status", - "csum": "3e768dcc076a5a890a77f6332d0495e1", - "expire": 0, - "path": "flag-15", - "queued": 1617109786.684085, - "rid": "" - }, - { - "action": "status", - "csum": "6c2b4d541610f489687650abe7c0ddcb", - "expire": 0, - "path": "flag-14", - "queued": 1617109786.684085, - "rid": "" - }, - { - "action": "status", - "csum": "ca0fe6c1cdfa9d75995e821a2d8fd16f", - "expire": 0, - "path": "flx/svc/blue-1", - "queued": 1617109786.684085, - "rid": "" - }, - { - "action": "status", - "csum": "597b7bb37b8388b0086560f9eac2c1de", - "expire": 0, - "path": "dck/vol/data", - "queued": 1617109786.684085, - "rid": "" - }, - { - "action": "status", - "csum": "a58717b5c074238d6fa2aea6a07548c5", - "expire": 0, - "path": "oci/svc/n48", - "queued": 1617109786.684085, - "rid": "" - }, - { - "action": "status", - "csum": "caff2a05d00428e8760dbec7570ae00b", - "expire": 0, - "path": "oci/svc/n26", - "queued": 1617109786.684085, - "rid": "" - }, - { - "action": "status", - "csum": "967f53beb496924cb09eef3999eef375", - "expire": 0, - "path": "oci/svc/n50", - "queued": 1617109786.684085, - "rid": "" - }, - { - "action": "status", - "csum": "ccfa6dafb48fead37b2caaf90eeb25f7", - "expire": 0, - "path": "dck/svc/qa", - "queued": 1617109787.952776, - "rid": "" - }, - { - "action": "status", - "csum": "d7693a59bea33da5496e66c2d9c891c9", - "expire": 0, - "path": "oci/svc/n27", - "queued": 1617109788.9685016, - "rid": "" - }, - { - "action": "status", - "csum": "700bb3cddeeb35d4ae932a40f950e4d9", - "expire": 0, - "path": "oci/svc/n52", - "queued": 1617109788.9685016, - "rid": "" - }, - { - "action": "status", - "csum": "3e5074d0077ef8e9a9e873192c91412b", - "expire": 0, - "path": "oci/svc/n8", - "queued": 1617109788.9685016, - "rid": "" - }, - { - "action": "status", - "csum": "950dfa0f001aa37397ebd58b5b35f59f", - "expire": 0, - "path": "oci/svc/n49", - "queued": 1617109788.9685016, - "rid": "" - }, - { - "action": "status", - "csum": "07b4fddaa3397ede503464a885687763", - "expire": 0, - "path": "oci/svc/n20", - "queued": 1617109788.9685016, - "rid": "" - }, - { - "action": "status", - "csum": "4e4bbc0f6ba8f485c867d1ab839de91e", - "expire": 0, - "path": "oci/svc/n60", - "queued": 1617109788.9685016, - "rid": "" - }, - { - "action": "status", - "csum": "29a00012ff8a64378fc4f22cfbca13b2", - "expire": 0, - "path": "oci/svc/n14", - "queued": 1617109788.9685016, - "rid": "" - }, - { - "action": "status", - "csum": "86ec7e0641973e4232e76ed9bee225ed", - "expire": 0, - "path": "oci/svc/n4", - "queued": 1617109788.9685016, - "rid": "" - }, - { - "action": "status", - "csum": "19729b95f94f2a0d08f9e514197d6f74", - "expire": 0, - "path": "oci/svc/n53", - "queued": 1617109788.9685016, - "rid": "" - }, - { - "action": "status", - "csum": "9da8aba8a4f0fcbc89cf8ff3bfe06ad9", - "expire": 0, - "path": "oci/svc/n18", - "queued": 1617109788.9685016, - "rid": "" - }, - { - "action": "status", - "csum": "29a861ec4d3dea86e0ee3abce70bc4b1", - "expire": 0, - "path": "oci/svc/n25", - "queued": 1617109788.9685016, - "rid": "" - }, - { - "action": "status", - "csum": "a3beb417a5f050ab67ec3495dd5fcb8f", - "expire": 0, - "path": "oci/svc/n34", - "queued": 1617109788.9685016, - "rid": "" - }, - { - "action": "status", - "csum": "2050fc7182e5c7833e545abdc703322d", - "expire": 0, - "path": "oci/svc/n33", - "queued": 1617109788.9685016, - "rid": "" - }, - { - "action": "status", - "csum": "c06a3507dcd407c1ef45a6692740f279", - "expire": 0, - "path": "oci/svc/n24", - "queued": 1617109788.9685016, - "rid": "" - }, - { - "action": "status", - "csum": "c390dd2bfdfe26f15156e19388ce2645", - "expire": 0, - "path": "oci/svc/n7", - "queued": 1617109788.9685016, - "rid": "" - }, - { - "action": "status", - "csum": "9ea1cd8a7b40873ae25788259690b15d", - "expire": 0, - "path": "oci/svc/n17", - "queued": 1617109791.839007, - "rid": "" - }, - { - "action": "status", - "csum": "8a559816b42c0ce54bf39e27ca413424", - "expire": 0, - "path": "oci/svc/n45", - "queued": 1617109791.839007, - "rid": "" - }, - { - "action": "status", - "csum": "47241cd733d7494fb273d2df60cd3b0e", - "expire": 0, - "path": "oci/svc/n36", - "queued": 1617109791.839007, - "rid": "" - }, - { - "action": "status", - "csum": "b5266a41d15bd9ca2a3d97fc9f58a672", - "expire": 0, - "path": "oci/svc/n30", - "queued": 1617109791.839007, - "rid": "" - }, - { - "action": "status", - "csum": "f83dfff4d90719ad5fab5c6d5436f8d1", - "expire": 0, - "path": "oci/svc/n54", - "queued": 1617109791.839007, - "rid": "" - }, - { - "action": "status", - "csum": "e529db96d25b0e2ffd3b0cb5be6ce06b", - "expire": 0, - "path": "oci/svc/n11", - "queued": 1617109791.839007, - "rid": "" - }, - { - "action": "status", - "csum": "dd2e20f2df234a110fedfd60ac717591", - "expire": 0, - "path": "oci/svc/n32", - "queued": 1617109791.839007, - "rid": "" - }, - { - "action": "status", - "csum": "ca817fa0c9f62b7fe294d5a329dfac4a", - "expire": 0, - "path": "oci/svc/n16", - "queued": 1617109791.839007, - "rid": "" - }, - { - "action": "status", - "csum": "5f2ff0106364e0f580cbf68155eef33f", - "expire": 0, - "path": "oci/svc/n13", - "queued": 1617109791.839007, - "rid": "" - }, - { - "action": "status", - "csum": "a11965948ca1e0093b4100ab47ff2f89", - "expire": 0, - "path": "oci/svc/n3", - "queued": 1617109791.839007, - "rid": "" - }, - { - "action": "status", - "csum": "51735aefa2b9ad51a32cd96ecb7e86cc", - "expire": 0, - "path": "oci/svc/n9", - "queued": 1617109791.839007, - "rid": "" - }, - { - "action": "status", - "csum": "5bd6a719b4bf8f06cd88b8bb986108b0", - "expire": 0, - "path": "oci/svc/n23", - "queued": 1617109791.839007, - "rid": "" - }, - { - "action": "status", - "csum": "499c0ad78851d4d3b0e596112ecaa4c2", - "expire": 0, - "path": "oci/svc/n29", - "queued": 1617109791.839007, - "rid": "" - }, - { - "action": "status", - "csum": "869a90ddb176be83104c9e907b9d5818", - "expire": 0, - "path": "oci/svc/n37", - "queued": 1617109791.839007, - "rid": "" - }, - { - "action": "status", - "csum": "09c1b348ed5bbdcc5818392c1f5731fc", - "expire": 0, - "path": "oci/svc/n59", - "queued": 1617109791.839007, - "rid": "" - }, - { - "action": "status", - "csum": "74d2a95f3e3d2c0a21f1dfa59b8fc46e", - "expire": 0, - "path": "oci/svc/n38", - "queued": 1617109793.3396902, - "rid": "" - }, - { - "action": "status", - "csum": "79f6f3ef96716e090e8a3d5fbd8646db", - "expire": 0, - "path": "oci/svc/n56", - "queued": 1617109793.3396902, - "rid": "" - }, - { - "action": "status", - "csum": "3d96dc94826083f66bc9f36071b7f614", - "expire": 0, - "path": "oci/svc/n55", - "queued": 1617109793.3396902, - "rid": "" - }, - { - "action": "status", - "csum": "c889cd68ebcb18d785d890226afe27e6", - "expire": 0, - "path": "oci/svc/n22", - "queued": 1617109793.3396902, - "rid": "" - }, - { - "action": "status", - "csum": "75aa4cc456627ee4ffb5352a7f5775a4", - "expire": 0, - "path": "oci/svc/n44", - "queued": 1617109793.3396902, - "rid": "" - }, - { - "action": "status", - "csum": "553976183ce0b25728498f7f39952c83", - "expire": 0, - "path": "oci/svc/n1", - "queued": 1617109793.3396902, - "rid": "" - }, - { - "action": "status", - "csum": "3e196c264f2f730fb7f3505eb80e1b8e", - "expire": 0, - "path": "oci/svc/n43", - "queued": 1617109793.3396902, - "rid": "" - }, - { - "action": "status", - "csum": "b03bdf802350a98a307207538ff86315", - "expire": 0, - "path": "oci/svc/n31", - "queued": 1617109793.3396902, - "rid": "" - }, - { - "action": "status", - "csum": "9f926df6d84acf03c02895fe4736d7fd", - "expire": 0, - "path": "oci/svc/n51", - "queued": 1617109793.3396902, - "rid": "" - }, - { - "action": "status", - "csum": "b08516a27e14346705f9c25776474895", - "expire": 0, - "path": "oci/svc/n42", - "queued": 1617109793.3396902, - "rid": "" - }, - { - "action": "status", - "csum": "8fee8c5b1940cd75cd41b5aa853ffe6e", - "expire": 0, - "path": "oci/svc/n10", - "queued": 1617109793.3396902, - "rid": "" - }, - { - "action": "status", - "csum": "e5744704a5ef8fdad1354e53e51c6252", - "expire": 0, - "path": "oci/svc/n21", - "queued": 1617109793.3396902, - "rid": "" - }, - { - "action": "status", - "csum": "3270f093a89afff68b52908ab61e069f", - "expire": 0, - "path": "oci/svc/n57", - "queued": 1617109793.3396902, - "rid": "" - }, - { - "action": "status", - "csum": "5d3ee09b23e6a78828e7e531eabf8a64", - "expire": 0, - "path": "oci/svc/n40", - "queued": 1617109793.3396902, - "rid": "" - }, - { - "action": "status", - "csum": "bb2bed17877422bbd3fa5f638ed3ef12", - "expire": 0, - "path": "oci/svc/n15", - "queued": 1617109793.3396902, - "rid": "" - }, - { - "action": "status", - "csum": "150f86d1b670ad339bedca74e0896c47", - "expire": 0, - "path": "oci/svc/n6", - "queued": 1617109794.468047, - "rid": "" - }, - { - "action": "status", - "csum": "72b19f9aa633b6f32b3b3fcc46557584", - "expire": 0, - "path": "oci/vol/n48", - "queued": 1617109794.468047, - "rid": "" - }, - { - "action": "status", - "csum": "b2acb8a93accb0385febae15e51a96a5", - "expire": 0, - "path": "oci/vol/n26", - "queued": 1617109794.468047, - "rid": "" - }, - { - "action": "status", - "csum": "efb01efa97e60f5f4207222b1d5615be", - "expire": 0, - "path": "oci/vol/n50", - "queued": 1617109794.468047, - "rid": "" - }, - { - "action": "status", - "csum": "fee5a748b3394c4c99fcb89e2f1f3c13", - "expire": 0, - "path": "oci/vol/n52", - "queued": 1617109794.468047, - "rid": "" - }, - { - "action": "status", - "csum": "2e49949fc43788560c5bce7db8f7eccf", - "expire": 0, - "path": "oci/vol/n8", - "queued": 1617109794.468047, - "rid": "" - }, - { - "action": "status", - "csum": "87a082618b2bf52bb0b4d76aa816fd70", - "expire": 0, - "path": "oci/svc/n39", - "queued": 1617109795.6864853, - "rid": "" - }, - { - "action": "status", - "csum": "92eec7cc964beb5894e5276a8395e336", - "expire": 0, - "path": "oci/svc/n5", - "queued": 1617109795.6864853, - "rid": "" - }, - { - "action": "status", - "csum": "4cda97e5895d9e9da9f9cd277d737c44", - "expire": 0, - "path": "oci/svc/n19", - "queued": 1617109795.6864853, - "rid": "" - }, - { - "action": "status", - "csum": "3d69dd53c67480ed81640a7c19d3b23c", - "expire": 0, - "path": "oci/svc/n46", - "queued": 1617109795.6864853, - "rid": "" - }, - { - "action": "status", - "csum": "d71354efa2d38abec74d3dd2505a41d6", - "expire": 0, - "path": "oci/svc/n12", - "queued": 1617109795.6864853, - "rid": "" - }, - { - "action": "status", - "csum": "9ece943a3062bb58f13582b03b3b5e4b", - "expire": 0, - "path": "oci/svc/n41", - "queued": 1617109795.6864853, - "rid": "" - }, - { - "action": "status", - "csum": "e0886a3dbf982bb879b27ddf4387ff85", - "expire": 0, - "path": "oci/svc/n47", - "queued": 1617109795.6864853, - "rid": "" - }, - { - "action": "status", - "csum": "979dd7f64e415ba5d50da8360def2469", - "expire": 0, - "path": "oci/svc/n35", - "queued": 1617109795.6864853, - "rid": "" - }, - { - "action": "status", - "csum": "d8e2561ababb5ff484609a6107e8dc13", - "expire": 0, - "path": "oci/svc/n2", - "queued": 1617109795.6864853, - "rid": "" - }, - { - "action": "status", - "csum": "ceb519195ef9ab37600b62e9d7e75fe1", - "expire": 0, - "path": "oci/svc/n58", - "queued": 1617109795.6864853, - "rid": "" - }, - { - "action": "status", - "csum": "a5fc0ddc498a69478dc0d839d502f65a", - "expire": 0, - "path": "oci/svc/n28", - "queued": 1617109795.6864853, - "rid": "" - }, - { - "action": "status", - "csum": "ca8e359727bc153a5ef0afd368a6d4d4", - "expire": 0, - "path": "oci/vol/n27", - "queued": 1617109795.6864853, - "rid": "" - }, - { - "action": "status", - "csum": "12d0ab62e43b6719d3bc3800929f2e87", - "expire": 0, - "path": "oci/vol/n49", - "queued": 1617109795.6864853, - "rid": "" - }, - { - "action": "status", - "csum": "499ec88bc5322e873feebd1f34764eb1", - "expire": 0, - "path": "oci/vol/n20", - "queued": 1617109795.6864853, - "rid": "" - }, - { - "action": "status", - "csum": "6681f0990855678e0516c3583bdd0b66", - "expire": 0, - "path": "oci/vol/n60", - "queued": 1617109795.6864853, - "rid": "" - }, - { - "action": "status", - "csum": "f5ceeab8e00645f8603eaa1494096225", - "expire": 0, - "path": "oci/vol/n14", - "queued": 1617109795.6864853, - "rid": "" - }, - { - "action": "status", - "csum": "046c161c97fbd0318cfcf953b64faddb", - "expire": 0, - "path": "oci/vol/n4", - "queued": 1617109796.8367133, - "rid": "" - }, - { - "action": "status", - "csum": "daabad3bb94ed524cad84a3d9a8a98cd", - "expire": 0, - "path": "oci/vol/n53", - "queued": 1617109796.8367133, - "rid": "" - }, - { - "action": "status", - "csum": "733bf36715c57b51cd98a65bd8711d90", - "expire": 0, - "path": "oci/vol/n18", - "queued": 1617109796.8367133, - "rid": "" - }, - { - "action": "status", - "csum": "c9fc292c146c44e4ce797b6e9ff18423", - "expire": 0, - "path": "oci/vol/n25", - "queued": 1617109796.8367133, - "rid": "" - }, - { - "action": "status", - "csum": "bf57b6e67c5a354566839eab25dfc66f", - "expire": 0, - "path": "oci/vol/n34", - "queued": 1617109796.8367133, - "rid": "" - }, - { - "action": "status", - "csum": "384f8fe1648eae05f971d6c33de47876", - "expire": 0, - "path": "oci/vol/n33", - "queued": 1617109796.8367133, - "rid": "" - }, - { - "action": "status", - "csum": "9775f90a9db852f1761cf9c0d99382d3", - "expire": 0, - "path": "oci/vol/n24", - "queued": 1617109796.8367133, - "rid": "" - }, - { - "action": "status", - "csum": "1a7e1de6ee647c4265e8175298058fc3", - "expire": 0, - "path": "oci/vol/n17", - "queued": 1617109796.8367133, - "rid": "" - }, - { - "action": "status", - "csum": "c383cabb0f20fe0ded0e071636e82bda", - "expire": 0, - "path": "oci/vol/n30", - "queued": 1617109796.8367133, - "rid": "" - }, - { - "action": "status", - "csum": "cca3f9d5e1450ef2c005f21298f8e5bc", - "expire": 0, - "path": "oci/vol/n7", - "queued": 1617109798.775528, - "rid": "" - }, - { - "action": "status", - "csum": "8dbf1629950dfad6cdae97888c40c24a", - "expire": 0, - "path": "oci/vol/n45", - "queued": 1617109798.775528, - "rid": "" - }, - { - "action": "status", - "csum": "4132a56ef3ef325e1341255c09395f1c", - "expire": 0, - "path": "oci/vol/n36", - "queued": 1617109798.775528, - "rid": "" - }, - { - "action": "status", - "csum": "7a603d12ec147a2988f5bceeb21e5d3e", - "expire": 0, - "path": "oci/vol/n54", - "queued": 1617109798.775528, - "rid": "" - }, - { - "action": "status", - "csum": "d850ded1a12f9c7a40cdbc313b62eefb", - "expire": 0, - "path": "oci/vol/n11", - "queued": 1617109798.775528, - "rid": "" - }, - { - "action": "status", - "csum": "a1f6ac9f03b3ff1d4adf6eff4a9ec2ee", - "expire": 0, - "path": "oci/vol/n32", - "queued": 1617109798.775528, - "rid": "" - }, - { - "action": "status", - "csum": "ed57fe17c362b75fa8caee0f03819f99", - "expire": 0, - "path": "oci/vol/n16", - "queued": 1617109798.775528, - "rid": "" - }, - { - "action": "status", - "csum": "d89e2ba66e477a60e042115fbace1bea", - "expire": 0, - "path": "oci/vol/n13", - "queued": 1617109798.775528, - "rid": "" - }, - { - "action": "status", - "csum": "25f634b14a2ea854b3a14a308ccbf8f5", - "expire": 0, - "path": "oci/vol/n3", - "queued": 1617109798.775528, - "rid": "" - }, - { - "action": "status", - "csum": "7cec80ae4902833276ba3af7e92ca550", - "expire": 0, - "path": "oci/vol/n9", - "queued": 1617109798.775528, - "rid": "" - }, - { - "action": "status", - "csum": "3b4ba71f9c1858ab5550f64263c3989d", - "expire": 0, - "path": "oci/vol/n23", - "queued": 1617109798.775528, - "rid": "" - }, - { - "action": "status", - "csum": "ca57596e8417f81cd3c89b7341d5b249", - "expire": 0, - "path": "oci/vol/n29", - "queued": 1617109798.775528, - "rid": "" - }, - { - "action": "status", - "csum": "1d07ec6581d93c8ad67576b15d2bff8b", - "expire": 0, - "path": "oci/vol/n37", - "queued": 1617109798.775528, - "rid": "" - }, - { - "action": "status", - "csum": "2503999367ee8baa9b6fbf3519853a77", - "expire": 0, - "path": "oci/vol/n59", - "queued": 1617109798.775528, - "rid": "" - }, - { - "action": "status", - "csum": "30e51d2f5b416991142545114a687a28", - "expire": 0, - "path": "oci/vol/n38", - "queued": 1617109798.775528, - "rid": "" - }, - { - "action": "status", - "csum": "7235c7e71d0582688dac8c86c537faa9", - "expire": 0, - "path": "oci/vol/n56", - "queued": 1617109798.775528, - "rid": "" - }, - { - "action": "status", - "csum": "2c8f88e57fab7749abd6278d48ee1f33", - "expire": 0, - "path": "oci/vol/n55", - "queued": 1617109799.2576623, - "rid": "" - }, - { - "action": "status", - "csum": "6636fae557fc4972a7a414da8104b5a0", - "expire": 0, - "path": "oci/vol/n22", - "queued": 1617109799.2576623, - "rid": "" - }, - { - "action": "status", - "csum": "5c307dcbc08f77de27392400bed7345c", - "expire": 0, - "path": "oci/vol/n44", - "queued": 1617109799.2576623, - "rid": "" - }, - { - "action": "status", - "csum": "6829c42fa87435e541605c9c02eac6b7", - "expire": 0, - "path": "oci/vol/n1", - "queued": 1617109799.2576623, - "rid": "" - }, - { - "action": "status", - "csum": "cdf5f9a96a078d5150756bb45f5e7c4c", - "expire": 0, - "path": "oci/vol/n43", - "queued": 1617109799.2576623, - "rid": "" - }, - { - "action": "status", - "csum": "15a0d467564f3d338232233e302dc895", - "expire": 0, - "path": "oci/vol/n31", - "queued": 1617109799.2576623, - "rid": "" - }, - { - "action": "status", - "csum": "286dbdf80da5f3d205586337e1a0dcc1", - "expire": 0, - "path": "oci/vol/n51", - "queued": 1617109799.2576623, - "rid": "" - }, - { - "action": "status", - "csum": "af087cc92bdc960c8733ca1d8d88287f", - "expire": 0, - "path": "oci/vol/n42", - "queued": 1617109799.2576623, - "rid": "" - }, - { - "action": "status", - "csum": "650118c8d3c634b8392655b5ccdadcee", - "expire": 0, - "path": "oci/vol/n10", - "queued": 1617109799.2576623, - "rid": "" - }, - { - "action": "status", - "csum": "53b2d87a73e0849a9551b4135b2ee23e", - "expire": 0, - "path": "oci/vol/n21", - "queued": 1617109799.2576623, - "rid": "" - }, - { - "action": "status", - "csum": "ddd83b45a41a1edced09d2a8e215e599", - "expire": 0, - "path": "oci/vol/n57", - "queued": 1617109799.2576623, - "rid": "" - }, - { - "action": "status", - "csum": "bb0cb2df25671f5c02bc78e23f995e8b", - "expire": 0, - "path": "oci/vol/n40", - "queued": 1617109799.2576623, - "rid": "" - }, - { - "action": "status", - "csum": "2ae2b38a4d4444755edfe5e312d1009e", - "expire": 0, - "path": "oci/vol/n15", - "queued": 1617109799.2576623, - "rid": "" - }, - { - "action": "status", - "csum": "1d888b23d78a8c73d2b11379ee409235", - "expire": 0, - "path": "oci/vol/n39", - "queued": 1617109799.2576623, - "rid": "" - }, - { - "action": "status", - "csum": "80cb0938c60c2f11245b302e597fd278", - "expire": 0, - "path": "oci/vol/n6", - "queued": 1617109799.2576623, - "rid": "" - }, - { - "action": "status", - "csum": "12717e5131280ae388f42f6780a7dfee", - "expire": 0, - "path": "oci/vol/n5", - "queued": 1617109799.2576623, - "rid": "" - }, - { - "action": "status", - "csum": "259a98d7fec4e8e7918cc4e197a6d856", - "expire": 0, - "path": "oci/vol/n19", - "queued": 1617109800.4507885, - "rid": "" - }, - { - "action": "status", - "csum": "6f4608055b89004a3d964df32b233830", - "expire": 0, - "path": "oci/vol/n46", - "queued": 1617109800.4507885, - "rid": "" - }, - { - "action": "status", - "csum": "e5cbdcdf114fc429b7cf714fc2a9dcbc", - "expire": 0, - "path": "oci/vol/n12", - "queued": 1617109800.4507885, - "rid": "" - }, - { - "action": "status", - "csum": "ac64a0ac2d784d96c184e76e6811f88a", - "expire": 0, - "path": "oci/vol/n41", - "queued": 1617109800.4507885, - "rid": "" - }, - { - "action": "status", - "csum": "33ff4d7e55c51d11fc083e6f714855c1", - "expire": 0, - "path": "oci/vol/n47", - "queued": 1617109800.4507885, - "rid": "" - }, - { - "action": "status", - "csum": "25dfa1ff91c360fc90c617cfc9e839b8", - "expire": 0, - "path": "oci/vol/n35", - "queued": 1617109800.4507885, - "rid": "" - }, - { - "action": "status", - "csum": "0846cad4866bac21e8cb4c170f455b00", - "expire": 0, - "path": "oci/vol/n2", - "queued": 1617109800.4507885, - "rid": "" - }, - { - "action": "status", - "csum": "ea5601bbcc5c9c02ec237cba77a926b4", - "expire": 0, - "path": "oci/vol/n58", - "queued": 1617109800.4507885, - "rid": "" - }, - { - "action": "status", - "csum": "4cc9a2de89000263e2e5f07dc74babd9", - "expire": 0, - "path": "oci/vol/n28", - "queued": 1617109800.4507885, - "rid": "" - }, - { - "action": "status", - "csum": "7e325c0022c7585a3f5b4db7c0188843", - "expire": 0, - "path": "demo/svc/SVC-1", - "queued": 1617109800.4507885, - "rid": "" - }, - { - "action": "status", - "csum": "f47492caeb5a2decf236d96c6c121ed1", - "expire": 0, - "path": "system/svc/vip", - "queued": 1617109800.4507885, - "rid": "" - }, - { - "action": "status", - "csum": "d47fa3681da13c5d5a93b8d0168e5d26", - "expire": 0, - "path": "system/svc/dns", - "queued": 1617109800.4507885, - "rid": "" - }, - { - "action": "status", - "csum": "cb330c0c3a3a18ded29dc019392f9c69", - "expire": 0, - "path": "system/vol/dns-vol1", - "queued": 1617109800.4507885, - "rid": "" - }, - { - "action": "status", - "csum": "6975d8ab278cc7804bc36fee81318571", - "expire": 0, - "path": "qa/svc/node-16", - "queued": 1617109800.4507885, - "rid": "" - }, - { - "action": "status", - "csum": "e1930f5ee3bd1df76a89f760e6c88422", - "expire": 0, - "path": "qa/svc/node-12", - "queued": 1617109800.4507885, - "rid": "" - }, - { - "action": "status", - "csum": "fd5cd2951e137b7bc0bf7397c52fbc03", - "expire": 0, - "path": "qa/svc/forlink", - "queued": 1617109800.4507885, - "rid": "" - }, - { - "action": "status", - "csum": "90e4289741e4e4686ef7199a0ed0411d", - "expire": 0, - "path": "qa/svc/node-11", - "queued": 1617109801.649024, - "rid": "" - }, - { - "action": "status", - "csum": "10a13a6d32161b8494d46cc3845177f5", - "expire": 0, - "path": "qa/svc/node-10", - "queued": 1617109801.649024, - "rid": "" - }, - { - "action": "status", - "csum": "0b6d49c652d68ac978ce7919dc700873", - "expire": 0, - "path": "qa/svc/node-6", - "queued": 1617109801.649024, - "rid": "" - }, - { - "action": "status", - "csum": "85f89785b36c2816eff5f2b01e9a16fe", - "expire": 0, - "path": "qa/svc/node-7", - "queued": 1617109801.649024, - "rid": "" - }, - { - "action": "status", - "csum": "618931646170da853590b90d71508d58", - "expire": 0, - "path": "qa/svc/node-8", - "queued": 1617109801.649024, - "rid": "" - }, - { - "action": "status", - "csum": "3c2106bb4752d2afb6fc133494283a4f", - "expire": 0, - "path": "qa/svc/node-2", - "queued": 1617109801.649024, - "rid": "" - }, - { - "action": "status", - "csum": "a7555ae1b53ac5665d98fb27d0b157cd", - "expire": 0, - "path": "qa/svc/node-4", - "queued": 1617109801.649024, - "rid": "" - }, - { - "action": "status", - "csum": "faee4a807f9c8c7c712dfec2722ec5b9", - "expire": 0, - "path": "qa/svc/node-3", - "queued": 1617109801.649024, - "rid": "" - }, - { - "action": "status", - "csum": "e480b70bd92e6ab1449e320a63094e45", - "expire": 0, - "path": "qa/svc/node-9", - "queued": 1617109801.649024, - "rid": "" - }, - { - "action": "status", - "csum": "173727e474111a9e861eadeb4bf99ee9", - "expire": 0, - "path": "qa/svc/node-15", - "queued": 1617109801.649024, - "rid": "" - }, - { - "action": "status", - "csum": "c75efe68592e048f12c3060156cb108c", - "expire": 0, - "path": "qa/svc/node-5", - "queued": 1617109801.649024, - "rid": "" - }, - { - "action": "status", - "csum": "f8c18bca7231b8fabfee473cb89038df", - "expire": 0, - "path": "qa/svc/node-14", - "queued": 1617109801.649024, - "rid": "" - }, - { - "action": "status", - "csum": "70aef0173a460f015d72a821f6f4e39b", - "expire": 0, - "path": "qa/svc/node-1", - "queued": 1617109801.649024, - "rid": "" - }, - { - "action": "status", - "csum": "bdc84681984f87cf6d06234080c2979c", - "expire": 0, - "path": "qa/svc/node-13", - "queued": 1617109801.649024, - "rid": "" - }, - { - "action": "resource_monitor", - "csum": "4f135510aefa6997790b2c70186baec1", - "expire": 0, - "path": "flag", - "queued": 1617110204.5715961, - "rid": "" - }, - { - "action": "resource_monitor", - "csum": "80724c030eb7b126c8acc3666370948a", - "expire": 0, - "path": "flag-18", - "queued": 1617110204.5715961, - "rid": "" - }, - { - "action": "resource_monitor", - "csum": "8a6b86f760b54015c0bca23711b8964c", - "expire": 0, - "path": "flag-13", - "queued": 1617110204.5715961, - "rid": "" - }, - { - "action": "resource_monitor", - "csum": "27ec335a0d1adfea26bf4dd779a1890d", - "expire": 0, - "path": "flag-19", - "queued": 1617110204.5715961, - "rid": "" - }, - { - "action": "resource_monitor", - "csum": "ee8765d9934085b50c4e582bd6dd2ce1", - "expire": 0, - "path": "flag-12", - "queued": 1617110204.5715961, - "rid": "" - }, - { - "action": "resource_monitor", - "csum": "d92f95513c6256d300308089f2b02855", - "expire": 0, - "path": "flag-11", - "queued": 1617110204.5715961, - "rid": "" - }, - { - "action": "resource_monitor", - "csum": "7f139c90b65452253f0a387470f30000", - "expire": 0, - "path": "flag-10", - "queued": 1617110206.508879, - "rid": "" - }, - { - "action": "resource_monitor", - "csum": "9e00733e32281422b842263b33e48dea", - "expire": 0, - "path": "flag-17", - "queued": 1617110206.508879, - "rid": "" - }, - { - "action": "resource_monitor", - "csum": "6d47c72ba6e758bb8b82165bbc2d9509", - "expire": 0, - "path": "flag-16", - "queued": 1617110206.508879, - "rid": "" - }, - { - "action": "resource_monitor", - "csum": "3e768dcc076a5a890a77f6332d0495e1", - "expire": 0, - "path": "flag-15", - "queued": 1617110206.508879, - "rid": "" - }, - { - "action": "resource_monitor", - "csum": "6c2b4d541610f489687650abe7c0ddcb", - "expire": 0, - "path": "flag-14", - "queued": 1617110206.508879, - "rid": "" - }, - { - "action": "resource_monitor", - "csum": "f47492caeb5a2decf236d96c6c121ed1", - "expire": 0, - "path": "system/svc/vip", - "queued": 1617110221.374315, - "rid": "" - } - ] - }, - "listener": { - "configured": 1617109556.8201823, - "created": 1617109162.976821, - "state": "running", - "tid": 2647, - "config": { - "addr": "::", - "port": 1214 - }, - "stats": { - "sessions": { - "accepted": 1357, - "auth_validated": 1356, - "rx": 1119188, - "tx": 2456976, - "alive": { - "4550fd09-d347-4386-a399-6732fd29c03a": { - "addr": "local", - "created": 1617110229.914641, - "encrypted": false, - "progress": "sending POST /collector_xmlrpc result", - "tid": 21214 - }, - "4ae34d65-997e-48d5-92f4-29e5efaf6eac": { - "addr": "::ffff:172.20.16.1", - "created": 1617110256.4128346, - "encrypted": false, - "progress": "init", - "tid": 0 - }, - "edd9714e-0534-4d7f-960c-4e9dea5f5121": { - "addr": "local", - "created": 1617110229.9276044, - "encrypted": false, - "progress": "sending POST /object_status result", - "tid": 21215 - } - }, - "clients": { - "::ffff:172.20.16.1": { - "accepted": 12, - "auth_validated": 11, - "rx": 3966, - "tx": 1563018 - }, - "local": { - "accepted": 1345, - "auth_validated": 1345, - "rx": 1115222, - "tx": 893958 - } - } - } - } - }, - "monitor": { - "configured": 1617109567.56157, - "created": 1617109162.977066, - "state": "running", - "tid": 2648, - "compat": true, - "frozen": false, - "nodes": { - "u2004-local-1": { - "agent": "2.1-991", - "speaker": false, - "api": 6, - "arbitrators": null, - "compat": 10, - "env": "PRD", - "frozen": 0, - "gen": { - "u2004-local-1": 1 - }, - "labels": { - "group": "1" - }, - "min_avail_mem": 2, - "min_avail_swap": 10, - "monitor": { - "global_expect": "", - "status": "idle", - "status_updated": 1617109175.900141, - "global_expect_updated": 1617109175.9001412 - }, - "services": { - "config": { - "cluster": { - "csum": "1809592f347e0cacd917fe24b4aee3bb", - "scope": [ - "u2004-local-1", - "u2004-local-2" - ], - "Updated": 1617109555.8168223 - }, - "oci/cfg/config": { - "csum": "de2c2be0ded4582ccc67b4c932b1572f", - "scope": [ - "u2004-local-1", - "u2004-local-2" - ], - "Updated": 1616423497.3771157 - }, - "oci/svc/n1": { - "csum": "553976183ce0b25728498f7f39952c83", - "scope": [ - "u2004-local-1" - ], - "Updated": 1616173889.4282453 - }, - "oci/vol/n1": { - "csum": "6829c42fa87435e541605c9c02eac6b7", - "scope": [ - "u2004-local-1" - ], - "Updated": 1616173890.1566093 - }, - "system/sec/ca-u2004-cga": { - "csum": "42df517dbbd0d87a3f4da4df4b526049", - "scope": [ - "u2004-local-1", - "u2004-local-2" - ], - "Updated": 1616782905.1886475 - }, - "system/sec/cert-u2004-cga": { - "csum": "900a6ddde99d7ff43d53549ec7ffb2ed", - "scope": [ - "u2004-local-1", - "u2004-local-2" - ], - "Updated": 1616782930.9606483 - }, - "system/svc/dns": { - "csum": "d47fa3681da13c5d5a93b8d0168e5d26", - "scope": [ - "u2004-local-1" - ], - "Updated": 1611787822.1715717 - }, - "system/svc/vip": { - "csum": "f47492caeb5a2decf236d96c6c121ed1", - "scope": [ - "u2004-local-1", - "u2004-local-2" - ], - "Updated": 1611790399.386116 - }, - "system/vol/dns-vol1": { - "csum": "cb330c0c3a3a18ded29dc019392f9c69", - "scope": [ - "u2004-local-1" - ], - "Updated": 1611791046.70739 - } - }, - "status": { - "cluster": { - "csum": "a7a79239cadee30e6d0135a7c72334e5", - "frozen": 0, - "kind": "ccfg", - "monitor": { - "global_expect": "", - "local_expect": "", - "status": "idle", - "status_updated": 0, - "global_expect_updated": 0, - "placement": "" - }, - "priority": 50, - "updated": 1617109556.9880373, - "scale": null - }, - "flag": { - "app": "default", - "avail": "down", - "overall": "down", - "csum": "3aa31f17920249d95373771f8d35741e", - "env": "PRD", - "frozen": 1615919541.1516054, - "kind": "svc", - "monitor": { - "global_expect": "", - "local_expect": "", - "status": "idle", - "status_updated": 0, - "global_expect_updated": 0, - "placement": "" - }, - "orchestrate": "ha", - "topology": "flex", - "placement": "nodes order", - "priority": 50, - "provisioned": true, - "updated": 1617110229.92438, - "flex_target": 2, - "flex_min": 1, - "flex_max": 2, - "resources": { - "app#1": { - "label": "simple: sleep", - "log": [ - "info: not evaluated (instance not up)" - ], - "status": "n/a", - "type": "app.simple", - "provisioned": { - "mtime": 0 - }, - "monitor": true, - "restart": 2 - }, - "fs#1": { - "label": "fs.flag", - "status": "down", - "type": "fs.flag", - "provisioned": { - "mtime": 1615389351.168108, - "state": true - }, - "monitor": true - }, - "sync#i0": { - "label": "rsync svc config to nodes", - "status": "n/a", - "type": "sync.rsync", - "provisioned": { - "mtime": 0 - }, - "disable": true, - "optional": true - }, - "task#0": { - "label": "task.host", - "status": "n/a", - "type": "task.host", - "provisioned": { - "mtime": 0 - }, - "monitor": true, - "optional": true - } - }, - "running": [ - "task#0" - ], - "scale": null - }, - "oci/cfg/config": { - "csum": "5998feb0dd652ab5988954e6039c9d66", - "frozen": 0, - "kind": "cfg", - "monitor": { - "global_expect": "", - "local_expect": "", - "status": "idle", - "status_updated": 0, - "global_expect_updated": 0, - "placement": "" - }, - "priority": 50, - "updated": 1617109565.2748265, - "scale": null - }, - "oci/svc/n1": { - "app": "default", - "avail": "down", - "overall": "down", - "csum": "0e4e4f718ccb5d840f09f458bc7fac63", - "env": "PRD", - "frozen": 1616786860.4007812, - "kind": "svc", - "monitor": { - "global_expect": "", - "local_expect": "", - "status": "idle", - "status_updated": 0, - "global_expect_updated": 0, - "placement": "" - }, - "orchestrate": "ha", - "topology": "failover", - "placement": "nodes order", - "priority": 50, - "provisioned": true, - "updated": 1617109791.6386855, - "resources": { - "container#1": { - "label": "podman docker.io/library/python", - "log": [ - "info: can not find container id" - ], - "status": "down", - "type": "container.podman", - "provisioned": { - "mtime": 0 - } - }, - "ip#1": { - "label": "netns bridge 10.0.3.111/24 lxcbr0@container#1", - "status": "down", - "type": "ip.netns", - "provisioned": { - "mtime": 1616173892.7699156, - "state": true - }, - "info": { - "ipaddr": "10.0.3.111", - "ipdev": "lxcbr0", - "netmask": "24" - }, - "tags": [ - "container#1", - "docker" - ] - }, - "volume#1": { - "label": "n1", - "log": [ - "info: oci/vol/n1 avail down" - ], - "status": "down", - "type": "volume", - "provisioned": { - "mtime": 1616173890.2686653, - "state": true - } - } - }, - "scale": null - }, - "oci/vol/n1": { - "app": "default", - "avail": "down", - "overall": "down", - "csum": "96a351cbbc0cb221d7a14b5713efd34a", - "env": "PRD", - "frozen": 0, - "kind": "vol", - "monitor": { - "global_expect": "", - "local_expect": "", - "status": "idle", - "status_updated": 0, - "global_expect_updated": 0, - "placement": "leader" - }, - "topology": "failover", - "placement": "nodes order", - "priority": 50, - "provisioned": true, - "updated": 1617109798.6847541, - "resources": { - "fs#1": { - "label": "fs.flag", - "status": "down", - "type": "fs.flag", - "provisioned": { - "mtime": 1616173890.1886253, - "state": true - } - }, - "fs#2": { - "label": "dir /var/lib/opensvc/pool/directory/n1.oci.vol.u2004-cga", - "status": "n/a", - "type": "fs.directory", - "provisioned": { - "mtime": 1616173890.1926274, - "state": true - } - } - }, - "children": [ - "oci/svc/n1" - ], - "scale": null - }, - "system/sec/ca-u2004-cga": { - "csum": "90280c07e713964e73361d5ed87a1ebb", - "frozen": 0, - "kind": "sec", - "monitor": { - "global_expect": "", - "local_expect": "", - "status": "idle", - "status_updated": 0, - "global_expect_updated": 0, - "placement": "" - }, - "priority": 50, - "updated": 1617109565.2841756, - "scale": null - }, - "system/sec/cert-u2004-cga": { - "csum": "90280c07e713964e73361d5ed87a1ebb", - "frozen": 0, - "kind": "sec", - "monitor": { - "global_expect": "", - "local_expect": "", - "status": "idle", - "status_updated": 0, - "global_expect_updated": 0, - "placement": "" - }, - "priority": 50, - "updated": 1617109565.2816348, - "scale": null - }, - "system/svc/dns": { - "app": "default", - "avail": "down", - "overall": "down", - "csum": "c89d725ec1cd9cfcb3ae00979240adbd", - "env": "PRD", - "frozen": 1616787238.200794, - "kind": "svc", - "monitor": { - "global_expect": "", - "local_expect": "", - "status": "idle", - "status_updated": 0, - "global_expect_updated": 0, - "placement": "" - }, - "orchestrate": "ha", - "topology": "flex", - "placement": "nodes order", - "priority": 50, - "provisioned": true, - "updated": 1617109799.8563495, - "flex_target": 1, - "flex_min": 1, - "flex_max": 1, - "resources": { - "container#0": { - "label": "podman opensvc/pdns_server:4.2.1-r0", - "log": [ - "info: can not find container id" - ], - "status": "down", - "type": "container.podman", - "provisioned": { - "mtime": 0 - } - }, - "container#1": { - "label": "podman opensvc/pdns_recursor:4.2.0-r5", - "log": [ - "info: can not find container id" - ], - "status": "down", - "type": "container.podman", - "provisioned": { - "mtime": 0 - } - }, - "container#2": { - "label": "podman opensvc/pdns_janitor:latest", - "log": [ - "info: can not find container id" - ], - "status": "down", - "type": "container.podman", - "provisioned": { - "mtime": 0 - } - }, - "volume#1": { - "label": "dns-vol1", - "log": [ - "info: system/vol/dns-vol1 avail down" - ], - "status": "down", - "type": "volume", - "provisioned": { - "mtime": 1611791046.158739, - "state": true - } - } - }, - "scale": null - }, - "system/svc/vip": { - "app": "default", - "avail": "down", - "overall": "down", - "csum": "78f886846f0efe9f1a8738eeab1af74f", - "env": "PRD", - "frozen": 1616787238.168794, - "kind": "svc", - "monitor": { - "global_expect": "", - "local_expect": "", - "status": "idle", - "status_updated": 0, - "global_expect_updated": 0, - "placement": "" - }, - "orchestrate": "ha", - "topology": "failover", - "placement": "nodes order", - "priority": 50, - "provisioned": true, - "updated": 1617109799.9261427, - "resources": { - "ip#0": { - "label": "172.20.16.160/24 eth2", - "status": "down", - "type": "ip", - "provisioned": { - "mtime": 1611788247.388094, - "state": true - }, - "monitor": true, - "info": { - "ipaddr": "172.20.16.160", - "ipdev": "eth2", - "netmask": "24" - }, - "restart": 1 - }, - "sync#i0": { - "label": "rsync svc config to nodes", - "status": "n/a", - "type": "sync.rsync", - "provisioned": { - "mtime": 0 - }, - "disable": true, - "optional": true - } - }, - "scale": null - }, - "system/vol/dns-vol1": { - "app": "default", - "avail": "down", - "overall": "down", - "csum": "315ef13fdd35ffa1e944aa4c8bee6fd8", - "env": "PRD", - "frozen": 0, - "kind": "vol", - "monitor": { - "global_expect": "", - "local_expect": "", - "status": "idle", - "status_updated": 0, - "global_expect_updated": 0, - "placement": "leader" - }, - "topology": "flex", - "placement": "nodes order", - "priority": 50, - "provisioned": true, - "updated": 1617109799.761613, - "flex_max": 1, - "resources": { - "fs#1": { - "label": "fs.flag", - "status": "down", - "type": "fs.flag", - "provisioned": { - "mtime": 1611791046.90739, - "state": true - } - }, - "fs#2": { - "label": "dir /var/lib/opensvc/pool/directory/dns-vol1.system.vol.u2004", - "status": "n/a", - "type": "fs.directory", - "provisioned": { - "mtime": 1611791046.94739, - "state": true - } - } - }, - "children": [ - "system/svc/dns" - ], - "scale": null - } - } - }, - "stats": { - "load_15m": 0.2, - "mem_avail": 94, - "mem_total": 7960, - "score": 99, - "swap_avail": 100, - "swap_total": 979 - } - } - }, - "services": { - "cluster": {}, - "oci/vol/n1": { - "avail": "down", - "overall": "down", - "frozen": "thawed", - "placement": "optimal", - "provisioned": true - }, - "oci/svc/n1": { - "avail": "down", - "overall": "down", - "frozen": "frozen", - "placement": "optimal", - "provisioned": true - }, - "system/sec/ca-u2004-cga": {}, - "system/sec/cert-u2004-cga": {}, - "system/svc/dns": { - "avail": "down", - "overall": "down", - "frozen": "frozen", - "placement": "optimal", - "provisioned": true - }, - "system/svc/vip": { - "avail": "down", - "overall": "down", - "frozen": "frozen", - "placement": "optimal", - "provisioned": true - }, - "system/usr/admin": {}, - "system/vol/dns-vol1": { - "avail": "down", - "overall": "down", - "frozen": "thawed", - "placement": "n/a", - "provisioned": true - } - } - } -} diff --git a/drivers/rescontainerdocker/main.go b/drivers/rescontainerdocker/main.go index 12c11b424..ccecbcb5e 100644 --- a/drivers/rescontainerdocker/main.go +++ b/drivers/rescontainerdocker/main.go @@ -19,72 +19,23 @@ import ( "github.com/cpuguy83/go-docker/errdefs" "github.com/cpuguy83/go-docker/image" "github.com/cpuguy83/go-docker/image/imageapi" - "github.com/google/uuid" "github.com/kballard/go-shellquote" "golang.org/x/sys/unix" "github.com/opensvc/om3/core/actionrollback" - "github.com/opensvc/om3/core/naming" - "github.com/opensvc/om3/core/provisioned" "github.com/opensvc/om3/core/resource" "github.com/opensvc/om3/core/resourceid" "github.com/opensvc/om3/core/status" "github.com/opensvc/om3/core/vpath" + "github.com/opensvc/om3/drivers/rescontainerocibase" "github.com/opensvc/om3/util/envprovider" "github.com/opensvc/om3/util/file" - "github.com/opensvc/om3/util/pg" "github.com/opensvc/om3/util/stringslice" ) -const ( - AlwaysPolicy = "always" - OncePolicy = "once" -) - type ( T struct { - resource.T - resource.SCSIPersistentReservation - ObjectDomain string `json:"object_domain"` - PG pg.Config `json:"pg"` - Path naming.Path `json:"path"` - ObjectID uuid.UUID `json:"object_id"` - SCSIReserv bool `json:"scsireserv"` - PromoteRW bool `json:"promote_rw"` - NoPreemptAbort bool `json:"no_preempt_abort"` - OsvcRootPath string `json:"osvc_root_path"` - GuestOS string `json:"guest_os"` - Name string `json:"name"` - Hostname string `json:"hostname"` - Image string `json:"image"` - ImagePullPolicy string `json:"image_pull_policy"` - CWD string `json:"cwd"` - User string `json:"user"` - Command []string `json:"command"` - DNS []string `json:"dns"` - DNSSearch []string `json:"dns_search"` - RunArgs []string `json:"run_args"` - Entrypoint []string `json:"entrypoint"` - Detach bool `json:"detach"` - Remove bool `json:"remove"` - Privileged bool `json:"privileged"` - Init bool `json:"init"` - Interactive bool `json:"interactive"` - TTY bool `json:"tty"` - VolumeMounts []string `json:"volume_mounts"` - Env []string `json:"environment"` - SecretsEnv []string `json:"secrets_environment"` - ConfigsEnv []string `json:"configs_environment"` - Devices []string `json:"devices"` - NetNS string `json:"netns"` - UserNS string `json:"userns"` - PIDNS string `json:"pidns"` - IPCNS string `json:"ipcns"` - UTSNS string `json:"utsns"` - RegistryCreds string `json:"registry_creds"` - PullTimeout *time.Duration `json:"pull_timeout"` - StartTimeout *time.Duration `json:"start_timeout"` - StopTimeout *time.Duration `json:"stop_timeout"` + rescontainerocibase.BT } containerNamer interface { @@ -174,17 +125,6 @@ func (t T) pull(ctx context.Context) error { return err } -func (t T) labels() (map[string]string, error) { - data := make(map[string]string) - data["com.opensvc.id"] = t.containerLabelID() - data["com.opensvc.path"] = t.Path.String() - data["com.opensvc.namespace"] = t.Path.Namespace - data["com.opensvc.kind"] = t.Path.Kind.String() - data["com.opensvc.name"] = t.Path.Name - data["com.opensvc.rid"] = t.ResourceID.String() - return data, nil -} - func (t T) mounts() ([]mount.Mount, error) { mounts := make([]mount.Mount, 0) for _, s := range t.VolumeMounts { @@ -263,12 +203,12 @@ func (t T) Start(ctx context.Context) error { t.Log().Infof("container %s is already running", name) return nil } else { - if t.needPreStartRemove() { + if t.NeedPreStartRemove() { t.Log().Infof("remove leftover container %s", name) if err := cs.Remove(ctx, name); err != nil { return err } - if t.ImagePullPolicy == AlwaysPolicy { + if t.IsAlwaysImagePullPolicy() { if err := t.pull(ctx); err != nil { return err } @@ -285,7 +225,7 @@ func (t T) Start(ctx context.Context) error { } } } else { - if t.ImagePullPolicy == AlwaysPolicy { + if t.IsAlwaysImagePullPolicy() { if err := t.pull(ctx); err != nil { return err } @@ -353,7 +293,6 @@ func (t T) start(ctx context.Context, c *container.Container) error { func (t T) create(ctx context.Context) (*container.Container, error) { var ( env []string - labels map[string]string devices []containerapi.DeviceMapping mounts []mount.Mount err error @@ -361,9 +300,6 @@ func (t T) create(ctx context.Context) (*container.Container, error) { if env, err = t.env(); err != nil { return nil, err } - if labels, err = t.labels(); err != nil { - return nil, err - } if devices, err = t.devices(); err != nil { return nil, err } @@ -379,7 +315,7 @@ func (t T) create(ctx context.Context) (*container.Container, error) { Entrypoint: t.entrypoint(), Image: t.Image, WorkingDir: t.CWD, - Labels: labels, + Labels: t.Labels(), OpenStdin: t.Interactive, StopTimeout: t.stopTimeout(), StopSignal: "SIGKILL", @@ -652,22 +588,6 @@ func (t T) isDockerdPinging(ctx context.Context) error { return nil } -func (t T) Label() string { - return t.Image -} - -func (t T) Provision(ctx context.Context) error { - return nil -} - -func (t T) Unprovision(ctx context.Context) error { - return nil -} - -func (t T) Provisioned() (provisioned.T, error) { - return provisioned.NotApplicable, nil -} - func containerID(ctx context.Context, name string) string { inspect, err := cli().ContainerService().Inspect(ctx, name) if err != nil { @@ -676,26 +596,6 @@ func containerID(ctx context.Context, name string) string { return inspect.ID } -// ContainerName formats a docker container name -func (t T) ContainerName() string { - if t.Name != "" { - return t.Name - } - var s string - switch t.Path.Namespace { - case "root", "": - s = "" - default: - s = t.Path.Namespace + ".." - } - s = s + t.Path.Name + "." + strings.ReplaceAll(t.ResourceID.String(), "#", ".") - return s -} - -func (t T) containerLabelID() string { - return fmt.Sprintf("%s.%s", t.ObjectID, t.ResourceID.String()) -} - func (t T) entrypoint() []string { if len(t.Entrypoint) > 0 { return t.Entrypoint @@ -808,10 +708,6 @@ func (t T) Enter() error { return cmd.Run() } -func (t T) LinkNames() []string { - return []string{t.RID()} -} - func (t T) needDNS() bool { switch t.NetNS { case "", "none": @@ -848,10 +744,6 @@ func (t T) dnsSearch() []string { return []string{dom0, dom1, dom2} } -func (t T) needPreStartRemove() bool { - return t.Remove || !t.Detach -} - func (t T) hostname() string { if !t.needDNS() { return "" diff --git a/drivers/rescontainerdocker/manifest.go b/drivers/rescontainerdocker/manifest.go index 632929264..b118ae3a9 100644 --- a/drivers/rescontainerdocker/manifest.go +++ b/drivers/rescontainerdocker/manifest.go @@ -1,20 +1,11 @@ package rescontainerdocker import ( - "embed" - "github.com/opensvc/om3/core/driver" - "github.com/opensvc/om3/core/keywords" "github.com/opensvc/om3/core/manifest" - "github.com/opensvc/om3/core/naming" - "github.com/opensvc/om3/drivers/rescontainer" - "github.com/opensvc/om3/util/converters" ) var ( - //go:embed text - fs embed.FS - drvID = driver.NewID(driver.GroupContainer, "docker") altDrvID = driver.NewID(driver.GroupContainer, "oci") ) @@ -26,261 +17,5 @@ func init() { // Manifest exposes to the core the input expected by the driver. func (t T) Manifest() *manifest.T { - m := manifest.New(drvID, t) - m.Kinds.Or(naming.KindSvc) - m.AddKeywords(manifest.SCSIPersistentReservationKeywords...) - m.Add( - manifest.ContextObjectPath, - manifest.ContextObjectID, - manifest.ContextObjectDomain, - manifest.ContextDNS, - keywords.Keyword{ - Option: "name", - Attr: "Name", - Scopable: true, - DefaultText: keywords.NewText(fs, "text/kw/name.default"), - Text: keywords.NewText(fs, "text/kw/name"), - Example: "osvcprd..rundeck.container.db", - }, - keywords.Keyword{ - Option: "hostname", - Attr: "Hostname", - Scopable: true, - Example: "nginx1", - Text: keywords.NewText(fs, "text/kw/hostname"), - }, - keywords.Keyword{ - Option: "dns_search", - Attr: "DNSSearch", - Converter: converters.List, - Aliases: []string{}, - Scopable: true, - Required: false, - Example: "opensvc.com", - Text: keywords.NewText(fs, "text/kw/dns_search"), - }, - keywords.Keyword{ - Option: "image", - Attr: "Image", - Aliases: []string{"run_image"}, - Scopable: true, - Required: true, - Example: "google/pause", - Text: keywords.NewText(fs, "text/kw/image"), - }, - keywords.Keyword{ - Option: "image_pull_policy", - Attr: "ImagePullPolicy", - Scopable: true, - Candidates: []string{"once", "always"}, - Example: "once", - Text: keywords.NewText(fs, "text/kw/image_pull_policy"), - }, - keywords.Keyword{ - Option: "cwd", - Attr: "CWD", - Scopable: true, - Example: "/opt/foo", - Text: keywords.NewText(fs, "text/kw/cwd"), - }, - keywords.Keyword{ - Option: "command", - Attr: "Command", - Aliases: []string{"run_command"}, - Scopable: true, - Converter: converters.Shlex, - Example: "/opt/tomcat/bin/catalina.sh", - Text: keywords.NewText(fs, "text/kw/command"), - }, - keywords.Keyword{ - Option: "run_args", - Attr: "RunArgs", - Scopable: true, - Converter: converters.Shlex, - Example: "-v /opt/docker.opensvc.com/vol1:/vol1:rw -p 37.59.71.25:8080:8080", - Text: keywords.NewText(fs, "text/kw/run_args"), - }, - keywords.Keyword{ - Option: "entrypoint", - Attr: "Entrypoint", - Scopable: true, - Converter: converters.Shlex, - Example: "/bin/sh", - Text: keywords.NewText(fs, "text/kw/entrypoint"), - }, - keywords.Keyword{ - Option: "detach", - Attr: "Detach", - Scopable: true, - Converter: converters.Bool, - Default: "true", - Text: keywords.NewText(fs, "text/kw/detach"), - }, - keywords.Keyword{ - Option: "rm", - Attr: "Remove", - Scopable: true, - Converter: converters.Bool, - Example: "false", - Text: keywords.NewText(fs, "text/kw/rm"), - }, - keywords.Keyword{ - Option: "privileged", - Attr: "Privileged", - Scopable: true, - Converter: converters.Bool, - Text: keywords.NewText(fs, "text/kw/privileged"), - }, - keywords.Keyword{ - Option: "init", - Attr: "Init", - Scopable: true, - Default: "true", - Converter: converters.Bool, - Text: keywords.NewText(fs, "text/kw/init"), - }, - keywords.Keyword{ - Option: "interactive", - Attr: "Interactive", - Scopable: true, - Converter: converters.Bool, - Text: keywords.NewText(fs, "text/kw/interactive"), - }, - keywords.Keyword{ - Option: "tty", - Attr: "TTY", - Scopable: true, - Converter: converters.Bool, - Text: keywords.NewText(fs, "text/kw/tty"), - }, - keywords.Keyword{ - Option: "volume_mounts", - Attr: "VolumeMounts", - Scopable: true, - Converter: converters.Shlex, - Example: "myvol1:/vol1 myvol2:/vol2:rw /localdir:/data:ro", - Text: keywords.NewText(fs, "text/kw/volume_mounts"), - }, - keywords.Keyword{ - Option: "environment", - Attr: "Env", - Scopable: true, - Converter: converters.Shlex, - Text: keywords.NewText(fs, "text/kw/environment"), - Example: "KEY=cert1/server.key PASSWORD=db/password", - }, - keywords.Keyword{ - Option: "configs_environment", - Attr: "ConfigsEnv", - Scopable: true, - Converter: converters.Shlex, - Text: keywords.NewText(fs, "text/kw/configs_environment"), - Example: "CRT=cert1/server.crt PEM=cert1/server.pem", - }, - keywords.Keyword{ - Option: "devices", - Attr: "Devices", - Scopable: true, - Converter: converters.Shlex, - Text: keywords.NewText(fs, "text/kw/devices"), - Example: "myvol1:/dev/xvda myvol2:/dev/xvdb", - }, - keywords.Keyword{ - Option: "netns", - Attr: "NetNS", - Aliases: []string{"net"}, - Scopable: true, - Example: "container#0", - Text: keywords.NewText(fs, "text/kw/netns"), - }, - keywords.Keyword{ - Option: "user", - Attr: "User", - Scopable: true, - Example: "guest", - Text: keywords.NewText(fs, "text/kw/user"), - }, - keywords.Keyword{ - Option: "userns", - Attr: "UserNS", - Scopable: true, - Example: "container#0", - Text: keywords.NewText(fs, "text/kw/userns"), - }, - keywords.Keyword{ - Option: "pidns", - Attr: "PIDNS", - Scopable: true, - Example: "container#0", - Text: keywords.NewText(fs, "text/kw/pidns"), - }, - keywords.Keyword{ - Option: "ipcns", - Attr: "IPCNS", - Scopable: true, - Example: "container#0", - Text: keywords.NewText(fs, "text/kw/ipcns"), - }, - keywords.Keyword{ - Option: "utsns", - Attr: "UTSNS", - Scopable: true, - Candidates: []string{"", "host"}, - Example: "container#0", - Text: keywords.NewText(fs, "text/kw/utsns"), - }, - keywords.Keyword{ - Option: "registry_creds", - Attr: "RegistryCreds", - Scopable: true, - Example: "creds-registry-opensvc-com", - Text: keywords.NewText(fs, "text/kw/registry_creds"), - }, - keywords.Keyword{ - Option: "pull_timeout", - Attr: "PullTimeout", - Scopable: true, - Converter: converters.Duration, - Text: keywords.NewText(fs, "text/kw/pull_timeout"), - Example: "2m", - Default: "2m", - }, - keywords.Keyword{ - Option: "start_timeout", - Attr: "StartTimeout", - Scopable: true, - Converter: converters.Duration, - Text: keywords.NewText(fs, "text/kw/start_timeout"), - Example: "1m5s", - Default: "5s", - }, - keywords.Keyword{ - Option: "stop_timeout", - Attr: "StopTimeout", - Scopable: true, - Converter: converters.Duration, - Text: keywords.NewText(fs, "text/kw/stop_timeout"), - Example: "2m", - Default: "2m30s", - }, - keywords.Keyword{ - Option: "secrets_environment", - Attr: "SecretsEnv", - Scopable: true, - Converter: converters.Shlex, - Text: keywords.NewText(fs, "text/kw/secrets_environment"), - Example: "CRT=cert1/server.pem sec1/*", - }, - keywords.Keyword{ - Option: "configs_environment", - Attr: "ConfigsEnv", - Scopable: true, - Converter: converters.Shlex, - Text: keywords.NewText(fs, "text/kw/configs_environment"), - Example: "PORT=http/port webapp/app1* {name}/* {name}-debug/settings", - }, - rescontainer.KWOsvcRootPath, - rescontainer.KWGuestOS, - ) - return m + return t.BT.ManifestWithID(drvID) } diff --git a/drivers/rescontainerdockercli/caps.go b/drivers/rescontainerdockercli/caps.go new file mode 100644 index 000000000..498463b2c --- /dev/null +++ b/drivers/rescontainerdockercli/caps.go @@ -0,0 +1,24 @@ +package rescontainerdockercli + +import ( + "os/exec" + + "github.com/opensvc/om3/util/capabilities" +) + +func init() { + capabilities.Register(capabilitiesScanner) +} + +func capabilitiesScanner() ([]string, error) { + l := make([]string, 0) + drvCap := drvID.Cap() + if _, err := exec.LookPath("docker"); err != nil { + return l, nil + } + l = append(l, drvCap) + l = append(l, drvCap+".registry_creds") + l = append(l, drvCap+".signal") + l = append(l, altDrvID.Cap()) + return l, nil +} diff --git a/drivers/rescontainerdockercli/main.go b/drivers/rescontainerdockercli/main.go new file mode 100644 index 000000000..1be8e10af --- /dev/null +++ b/drivers/rescontainerdockercli/main.go @@ -0,0 +1,105 @@ +package rescontainerdockercli + +import ( + "context" + "os/exec" + "strings" + "time" + + "github.com/opensvc/om3/core/resource" + "github.com/opensvc/om3/drivers/rescontainerocibase" +) + +type ( + T struct { + *rescontainerocibase.BT + } + + ExecutorArg struct { + *rescontainerocibase.ExecutorArg + exe string + inspectRefresher inspectRefresher + } + + inspectRefresher interface { + InspectRefresh(context.Context) (rescontainerocibase.Inspecter, error) + } +) + +func New() resource.Driver { + bt := &rescontainerocibase.BT{} + t := &T{BT: bt} + executorArg := &ExecutorArg{ + ExecutorArg: &rescontainerocibase.ExecutorArg{ + BT: bt, + RunArgsDNSOptionOption: "--dns-option", + }, + exe: "docker", + } + executor := rescontainerocibase.NewExecutor("docker", executorArg, t) + executorArg.inspectRefresher = executor + _ = t.WithExecuter(executor) + return t +} + +func (ea *ExecutorArg) WaitNotRunning(ctx context.Context) error { + var cmd *exec.Cmd + a := rescontainerocibase.Args{ + {Option: "container"}, + {Option: "wait"}, + {Option: ea.BT.ContainerName()}, + } + if ctx != nil { + select { + case <-ctx.Done(): + return ctx.Err() + default: + cmd = exec.CommandContext(ctx, ea.exe, a.AsStrings()...) + } + } else { + cmd = exec.Command(ea.exe, a.AsStrings()...) + } + ea.Log().Infof("%s %s", ea.exe, strings.Join(a.Obfuscate(), " ")) + if err := cmd.Run(); err != nil { + ea.Log().Infof("%s %s: %s", ea.exe, strings.Join(a.Obfuscate(), " "), err) + return err + } + return nil +} + +func (ea *ExecutorArg) WaitRemoved(ctx context.Context) error { + if ctx != nil { + var cancel context.CancelFunc + ctx, cancel = context.WithTimeout(ctx, 10*time.Second) + defer cancel() + } + if removed, err := ea.isRemoved(ctx); err != nil { + return err + } else if removed { + return nil + } + ticker := time.NewTicker(1 * time.Second) + defer ticker.Stop() + for { + select { + case <-ticker.C: + if removed, err := ea.isRemoved(ctx); err != nil { + return err + } else if removed { + return nil + } + case <-ctx.Done(): + return ctx.Err() + } + } +} + +func (ea *ExecutorArg) isRemoved(ctx context.Context) (bool, error) { + if inspect, err := ea.inspectRefresher.InspectRefresh(ctx); err == nil { + ea.BT.Log().Debugf("is removed: %v", inspect == nil) + return inspect == nil, nil + } else { + ea.BT.Log().Debugf("is removed: false") + return false, err + } +} diff --git a/drivers/rescontainerdockercli/manifest.go b/drivers/rescontainerdockercli/manifest.go new file mode 100644 index 000000000..0bc39aecd --- /dev/null +++ b/drivers/rescontainerdockercli/manifest.go @@ -0,0 +1,21 @@ +package rescontainerdockercli + +import ( + "github.com/opensvc/om3/core/driver" + "github.com/opensvc/om3/core/manifest" +) + +var ( + drvID = driver.NewID(driver.GroupContainer, "dockercli") + altDrvID = driver.NewID(driver.GroupContainer, "oci") +) + +func init() { + driver.Register(drvID, New) + driver.Register(altDrvID, New) +} + +// Manifest exposes to the core the input expected by the driver. +func (t T) Manifest() *manifest.T { + return t.BT.ManifestWithID(drvID) +} diff --git a/drivers/rescontainerocibase/arg.go b/drivers/rescontainerocibase/arg.go new file mode 100644 index 000000000..62cf9340b --- /dev/null +++ b/drivers/rescontainerocibase/arg.go @@ -0,0 +1,41 @@ +package rescontainerocibase + +type ( + Arg struct { + Option string + Value string + Obfuscate bool + Multi bool + HasValue bool + } + + Args []Arg +) + +func (a *Args) AsStrings() []string { + var l []string + for _, v := range *a { + if v.HasValue { + l = append(l, v.Option, v.Value) + } else { + l = append(l, v.Option) + } + } + return l +} + +func (a *Args) Obfuscate() []string { + var l []string + for _, v := range *a { + if v.HasValue { + if v.Obfuscate { + l = append(l, v.Option, "obfuscate") + } else { + l = append(l, v.Option, v.Value) + } + } else { + l = append(l, v.Option) + } + } + return l +} diff --git a/drivers/rescontainerocibase/executor.go b/drivers/rescontainerocibase/executor.go new file mode 100644 index 000000000..5c5dc7e88 --- /dev/null +++ b/drivers/rescontainerocibase/executor.go @@ -0,0 +1,229 @@ +package rescontainerocibase + +import ( + "context" + "fmt" + "os" + "os/exec" + "strings" + "time" + + "github.com/opensvc/om3/util/plog" +) + +type ( + // Executor implements Executer interface to manage containers. + Executor struct { + // bin is the main container executor cli command + bin string + + // args is the ExecutorArgser used by executor focusing on resource information + args ExecutorArgser + + // inspected is set to true when container has been inspected at least + // once. + inspected bool + + // inspecter is the latest result of inspect refresh + inspecter Inspecter + + // logger provides a resource logger for executor + logger Logger + } +) + +func NewExecutor(exe string, args ExecutorArgser, log Logger) *Executor { + return &Executor{bin: exe, args: args, logger: log} +} + +func (e *Executor) Enter() error { + var enterCmd string + candidates := []string{"/bin/bash", "/bin/sh"} + enterArgs := e.args.EnterCmdCheckArgs() + ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + for _, candidate := range candidates { + args := append(enterArgs, candidate) + cmd := exec.CommandContext(ctx, e.bin, args...) + _ = cmd.Run() + + switch cmd.ProcessState.ExitCode() { + case 126, 127: + continue + default: + enterCmd = candidate + break + } + } + cancel() + if enterCmd == "" { + return fmt.Errorf("can''t enter: container needs at least one of following command: %s", + strings.Join(candidates, ", ")) + } + cmd := exec.Command(e.bin, append(e.args.EnterCmdArgs(), enterCmd)...) + cmd.Stdin = os.Stdin + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + return cmd.Run() +} + +func (e *Executor) HasImage(ctx context.Context) (bool, string, error) { + var cmd *exec.Cmd + a := e.args.HasImageArgs() + if ctx != nil { + select { + case <-ctx.Done(): + return false, "", ctx.Err() + default: + cmd = exec.CommandContext(ctx, e.bin, a.AsStrings()...) + } + } else { + cmd = exec.Command(e.bin, a.AsStrings()...) + } + e.log().Debugf("call %s %s", e.bin, a.Obfuscate()) + if b, err := cmd.Output(); err != nil { + e.log().Debugf("call %s %s failed: %s", e.bin, a.Obfuscate(), err) + return false, "", nil + } else { + imageID := strings.TrimSuffix(string(b), "\n") + return true, imageID, nil + } +} + +func (e *Executor) Inspect() Inspecter { + if !e.inspected { + // TODO: find callers, InspectRefresh should have been called first. + e.log().Infof("inspect called before Inspect refreshed, use dedicated context") + ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) + defer cancel() + i, _ := e.InspectRefresh(ctx) + return i + } + return e.inspecter +} + +func (e *Executor) InspectRefresh(ctx context.Context) (Inspecter, error) { + var cmd *exec.Cmd + a := e.args.InspectArgs() + if ctx != nil { + select { + case <-ctx.Done(): + e.log().Errorf("inspect context done: %s", ctx.Err()) + return nil, ctx.Err() + default: + cmd = exec.CommandContext(ctx, e.bin, a.AsStrings()...) + } + } else { + cmd = exec.Command(e.bin, a.AsStrings()...) + } + e.inspected = true + e.log().Debugf("engine inspect: %s %s", e.bin, strings.Join(a.Obfuscate(), " ")) + if b, err := cmd.Output(); err != nil { + e.inspecter = nil + e.log().Debugf("inspect: %s", err) + return nil, nil + } else if i, err := e.args.InspectParser(b); err != nil { + e.inspecter = nil + e.log().Debugf("inspect parse: %s", err) + return nil, err + } else { + e.inspecter = i + e.log().Debugf("inspect success") + return i, nil + } +} + +func (e *Executor) InspectRefreshed() bool { + return e.inspected +} + +func (e *Executor) Pull(ctx context.Context) error { + return e.doExecRun(ctx, nil, e.args.PullArgs()) +} + +func (e *Executor) Remove(ctx context.Context) error { + if err := e.doExecRun(ctx, nil, e.args.RemoveArgs()); err != nil { + if inspect, err := e.InspectRefresh(ctx); err == nil && inspect == nil { + e.log().Debugf("remove: container removed") + return nil + } + return err + } + return nil +} + +func (e *Executor) Run(ctx context.Context) error { + if a, err := e.args.RunArgs(); err != nil { + return fmt.Errorf("can't prepare args for run command: %s", err) + } else if environ, err := e.args.RunCmdEnv(); err != nil { + return fmt.Errorf("can't prepare run command environ: %s", err) + } else { + return e.doExecRun(ctx, environ, a) + } +} + +func (e *Executor) Start(ctx context.Context) error { + return e.doExecRun(ctx, nil, e.args.StartArgs()) +} + +func (e *Executor) Stop(ctx context.Context) error { + if err := e.doExecRun(ctx, nil, e.args.StopArgs()); err != nil { + if inspect, err := e.InspectRefresh(ctx); err == nil && inspect == nil { + e.log().Debugf("stop: container removed") + return nil + } + return err + } + return nil +} + +func (e *Executor) WaitNotRunning(ctx context.Context) error { + if err := e.args.WaitNotRunning(ctx); err != nil { + if inspect, err := e.InspectRefresh(ctx); err == nil && inspect == nil { + e.log().Debugf("wait not running: container removed") + return nil + } + return err + } + return nil +} + +func (e *Executor) WaitRemoved(ctx context.Context) error { + if err := e.args.WaitRemoved(ctx); err != nil { + if inspect, err := e.InspectRefresh(ctx); err == nil && inspect == nil { + e.log().Debugf("wait removed: container removed") + return nil + } + return err + } + return nil +} + +// doExecRun runs e.bin a.AsStrings(). Depending on ctx value, exec.Command or exec.CommandContext is used. +func (e *Executor) doExecRun(ctx context.Context, environ map[string]string, a Args) error { + var cmd *exec.Cmd + if ctx != nil { + select { + case <-ctx.Done(): + return ctx.Err() + default: + cmd = exec.CommandContext(ctx, e.bin, a.AsStrings()...) + } + } else { + cmd = exec.Command(e.bin, a.AsStrings()...) + } + if len(environ) > 0 { + envL := os.Environ() + for k, v := range environ { + e.log().Debugf("exec with env %s=xxx", k) + envL = append(envL, fmt.Sprintf("%s=%s", k, v)) + } + cmd.Env = envL + } + + e.log().Infof("%s %s", e.bin, strings.Join(a.Obfuscate(), " ")) + return cmd.Run() +} + +func (e *Executor) log() *plog.Logger { + return e.logger.Log() +} diff --git a/drivers/rescontainerocibase/executor_args.go b/drivers/rescontainerocibase/executor_args.go new file mode 100644 index 000000000..557b531e5 --- /dev/null +++ b/drivers/rescontainerocibase/executor_args.go @@ -0,0 +1,311 @@ +package rescontainerocibase + +import ( + "context" + "encoding/json" + "fmt" + "os" + "strings" + + "github.com/opensvc/om3/util/file" + "github.com/opensvc/om3/util/plog" +) + +type ( + // ExecutorArg implements ExecutorArgser + ExecutorArg struct { + BT *BT + + // RunArgsCGroupParentDisable disable the "--cgroup-parent" RunArgs setting + RunArgsCGroupParentDisable bool + + // RunArgsDNSOptionOption is the option name used during RunArgs + // to set container dns options (example "--dns-option"). + RunArgsDNSOptionOption string + + // runArgsEnvM is internal store for the environment variables that + // must be added to the exec.Cmd Env, during the Executor.Run() call. + // It is returned by ExecutorArg.RunCmdEnv() calls. + // Its value is computed during ExecutorArg.RunArgs() from the BT.GenEnv() + // results. + runArgsEnvM map[string]string + } +) + +func (ea *ExecutorArg) EnterCmdArgs() []string { + return []string{"exec", "-it", ea.BT.ContainerName()} +} + +func (ea *ExecutorArg) EnterCmdCheckArgs() []string { + return []string{"exec", ea.BT.ContainerName()} +} + +func (ea *ExecutorArg) HasImageArgs() Args { + return Args{ + {Option: "image"}, + {Option: "inspect"}, + {Option: "--format", Value: "{{.ID}}", HasValue: true}, + {Option: ea.BT.Image}, + } +} + +func (ea *ExecutorArg) InspectArgs() Args { + return Args{ + {Option: "container"}, + {Option: "inspect"}, + {Option: "--format", Value: "{{json .}}", HasValue: true}, + {Option: ea.BT.ContainerName()}, + } +} + +func (ea *ExecutorArg) InspectParser(b []byte) (Inspecter, error) { + data := &InspectData{} + if err := json.Unmarshal(b, data); err != nil { + return nil, err + } else { + return data, nil + } +} + +func (ea *ExecutorArg) Log() *plog.Logger { + return ea.BT.Log() +} + +func (ea *ExecutorArg) PullArgs() Args { + return Args{ + {Option: "image"}, + {Option: "pull", Value: ea.BT.Image, HasValue: true}, + } +} + +func (ea *ExecutorArg) RemoveArgs() Args { + return Args{ + {Option: "container"}, + {Option: "rm", Value: ea.BT.ContainerName(), HasValue: true}, + } +} + +func (ea *ExecutorArg) RunArgs() (Args, error) { + bt := ea.BT + ea.runArgsEnvM = make(map[string]string) + a := Args{ + {Option: "container"}, + {Option: "run"}, + {Option: "--name", Value: bt.ContainerName(), HasValue: true}, + } + if bt.Hostname != "" { + a = append(a, Arg{Option: "--hostname", Value: bt.Hostname, HasValue: true}) + } + if bt.TTY { + a = append(a, Arg{Option: "--tty"}) + } + if bt.Detach { + a = append(a, Arg{Option: "--detach"}) + } + if bt.Privileged { + a = append(a, Arg{Option: "--privileged"}) + } + if bt.Interactive { + a = append(a, Arg{Option: "--interactive"}) + } + if len(bt.Entrypoint) > 0 { + a = append(a, Arg{Option: "--entrypoint", Value: bt.Entrypoint[0], HasValue: true}) + } + + for _, f := range []func() (Args, error){ + ea.runArgsForNS, + ea.runArgsMounts, + ea.runArgsEnv, + } { + if args, err := f(); err != nil { + return a, err + } else { + a = append(a, args...) + } + } + a = append(a, ea.runArgsLabels()...) + a = append(a, ea.runArgsDNS()...) + a = append(a, ea.runArgsDNSSearch()...) + a = append(a, ea.runArgsDNSOption()...) + a = append(a, ea.runArgsCGroupParent()...) + + if ea.BT.Remove { + a = append(a, Arg{Option: "--rm"}) + } + // TODO: --devices + // TODO: merge run_args + a = append(a, Arg{Option: bt.Image}) + + a = append(a, ea.runArgsCommand()...) + + return a, nil +} + +func (ea *ExecutorArg) RunCmdEnv() (map[string]string, error) { + return ea.runArgsEnvM, nil +} + +func (ea *ExecutorArg) StartArgs() Args { + return Args{ + {Option: "container"}, + {Option: "start", Value: ea.BT.executer.Inspect().ID(), HasValue: true}, + } +} + +func (ea *ExecutorArg) StopArgs() Args { + a := Args{ + {Option: "container"}, + {Option: "stop"}, + {Option: ea.BT.ContainerName()}, + } + if ea.BT.StopTimeout != nil && *ea.BT.StopTimeout > 0 { + arg := Arg{ + Option: "--time", + Value: fmt.Sprintf("%.0f", ea.BT.StopTimeout.Seconds()), + HasValue: true, + } + a = append(a, arg) + } + return a +} + +func (ea *ExecutorArg) WaitNotRunning(ctx context.Context) error { + //TODO implement me + panic("implement me") +} + +func (ea *ExecutorArg) WaitRemoved(ctx context.Context) error { + //TODO implement me + panic("implement me") +} + +func (ea *ExecutorArg) runArgsCGroupParent() Args { + if !ea.RunArgsCGroupParentDisable || ea.BT.PG.ID == "" { + return nil + } + return Args{ + {Option: "--cgroup-parent", Value: ea.BT.PG.ID, HasValue: true}, + } +} + +func (ea *ExecutorArg) runArgsCommand() Args { + a := make(Args, 0, len(ea.BT.Command)) + for _, s := range ea.BT.Command { + a = append(a, Arg{Option: s}) + } + return a +} + +func (ea *ExecutorArg) runArgsDNS() Args { + if !ea.needDNS() { + return nil + } + return multiArgs("--dns", ea.BT.DNS...) +} + +func (ea *ExecutorArg) runArgsDNSOption() Args { + if !ea.needDNS() { + return nil + } + return multiArgs(ea.RunArgsDNSOptionOption, "ndots:2", "edns0", "use-vc") +} + +func (ea *ExecutorArg) runArgsDNSSearch() Args { + if len(ea.BT.DNSSearch) > 0 { + return multiArgs("--dns-search", ea.BT.DNSSearch...) + } + if !ea.needDNS() { + return nil + } + dom0 := ea.BT.ObjectDomain + dom1 := strings.SplitN(dom0, ".", 2)[1] + dom2 := strings.SplitN(dom1, ".", 2)[1] + return multiArgs("--dns-search", dom0, dom1, dom2) +} + +func (ea *ExecutorArg) runArgsEnv() (Args, error) { + if l, m, err := ea.BT.GenEnv(); err != nil { + return nil, err + } else { + ea.runArgsEnvM = m + args := make(Args, 0, len(l)) + for _, v := range l { + args = append(args, Arg{Option: "-e", Value: v, HasValue: true, Multi: true}) + } + return args, nil + } +} + +func (ea *ExecutorArg) runArgsForNS() (Args, error) { + type nsCandidate struct { + kw string + opt string + ns string + } + bt := ea.BT + nsCandidates := []nsCandidate{ + {kw: "netns", opt: "--net", ns: bt.NetNS}, + {kw: "pidns", opt: "--pid", ns: bt.PIDNS}, + {kw: "ipcns", opt: "--ipc", ns: bt.IPCNS}, + {kw: "utsns", opt: "--uts", ns: bt.UTSNS}, + {kw: "userns", opt: "--userns", ns: bt.UserNS}, + } + var a Args + for _, c := range nsCandidates { + if value, err := ea.BT.FormatNS(c.ns); err != nil { + return a, fmt.Errorf("unable to prepare option '%s' from kw setting '%s=%s': %s", c.opt, c.kw, c.ns, err) + } else if value != "" { + a = append(a, Arg{Option: c.opt, Value: value, HasValue: true}) + } + } + return a, nil +} + +func (ea *ExecutorArg) runArgsLabels() []Arg { + m := ea.BT.Labels() + labels := make([]string, 0, len(m)) + for k, v := range m { + labels = append(labels, fmt.Sprintf("%s=%s", k, v)) + } + return multiArgs("--label", labels...) +} + +func (ea *ExecutorArg) runArgsMounts() (Args, error) { + args := make(Args, 0) + mounts, err := ea.BT.Mounts() + if err != nil { + return args, err + } + for _, m := range mounts { + if !file.Exists(m.Source) { + ea.Log().Infof("create missing mount source %s", m.Source) + if err := os.MkdirAll(m.Source, os.ModePerm); err != nil { + return nil, err + } + } + args = append(args, Arg{ + Option: "--volume", + Value: fmt.Sprintf("%s:%s:%s", m.Source, m.Target, m.Option), + Multi: true, + HasValue: true, + }) + } + return args, nil +} + +func (ea *ExecutorArg) needDNS() bool { + switch ea.BT.NetNS { + case "", "none": + return true + default: + return false + } +} + +func multiArgs(option string, l ...string) Args { + a := make(Args, 0, len(l)) + for _, s := range l { + a = append(a, Arg{Option: option, Value: s, HasValue: true, Multi: true}) + } + return a +} diff --git a/drivers/rescontainerocibase/inspect.go b/drivers/rescontainerocibase/inspect.go new file mode 100644 index 000000000..c96fe4c7f --- /dev/null +++ b/drivers/rescontainerocibase/inspect.go @@ -0,0 +1,123 @@ +package rescontainerocibase + +import ( + "encoding/json" + "strings" +) + +type ( + // InspectData implements Inspecter + InspectData struct { + Id string + Image string + InspectDataConfig InspectDataConfig `json:"Config"` + InspectDataHostConfig InspectDataHostConfig `json:"HostConfig"` + NetworkSettings struct { + SandboxKey string + } + State InspectDataState + } + + InspectDataConfig struct { + Entrypoint InspectDataConfigEntrypoint + Hostname string + OpenStdin bool + Tty bool + } + + InspectDataConfigEntrypoint []string + + InspectDataHostConfig struct { + AutoRemove bool + IpcMode string + Privileged bool + NetworkMode string + PidMode string + UsernsMode string + UTSMode string + } + + InspectDataState struct { + Pid int + Running bool + Status string + } +) + +func (i *InspectData) Config() *InspectDataConfig { + if i == nil { + return nil + } + return &i.InspectDataConfig +} + +func (i *InspectData) Defined() bool { + if i != nil { + return true + } + return false +} + +func (i *InspectData) HostConfig() *InspectDataHostConfig { + if i == nil { + return nil + } + return &i.InspectDataHostConfig +} + +func (i *InspectData) ID() string { + if i == nil { + return "" + } + return i.Id +} + +func (i *InspectData) ImageID() string { + if i == nil { + return "" + } + return i.Image +} + +func (i *InspectData) PID() int { + if i == nil { + return 0 + } + return i.State.Pid +} + +func (i *InspectData) Running() bool { + if i == nil { + return false + } + return i.State.Running +} + +func (i *InspectData) Status() string { + if i == nil { + return "" + } + return i.State.Status +} + +func (i *InspectData) SandboxKey() string { + if i == nil { + return "" + } + return i.NetworkSettings.SandboxKey +} + +func (i *InspectDataConfigEntrypoint) UnmarshalJSON(b []byte) error { + var j any + err := json.Unmarshal(b, &j) + if err != nil { + return err + } + switch v := j.(type) { + case string: + *i = strings.Split(v, " ") + case []string: + *i = v + } + return nil +} diff --git a/drivers/rescontainerocibase/main.go b/drivers/rescontainerocibase/main.go new file mode 100644 index 000000000..d31933f67 --- /dev/null +++ b/drivers/rescontainerocibase/main.go @@ -0,0 +1,782 @@ +// Package rescontainerocibase provides base settings for to implement resource +// container oci drivers. +// +// It Defines BT that may help container oci composition for resource container +// oci driver interface. +// +// It Defines Executor that implements Executer interface. +// +// It Defines ExecutorArg that implements ExecutorArgser interface. +package rescontainerocibase + +import ( + "context" + "fmt" + "strings" + "syscall" + "time" + + "github.com/google/uuid" + "github.com/kballard/go-shellquote" + "golang.org/x/sys/unix" + + "github.com/opensvc/om3/core/actionrollback" + "github.com/opensvc/om3/core/naming" + "github.com/opensvc/om3/core/provisioned" + "github.com/opensvc/om3/core/resource" + "github.com/opensvc/om3/core/resourceid" + "github.com/opensvc/om3/core/status" + "github.com/opensvc/om3/core/vpath" + "github.com/opensvc/om3/util/envprovider" + "github.com/opensvc/om3/util/file" + "github.com/opensvc/om3/util/pg" + "github.com/opensvc/om3/util/plog" + "github.com/opensvc/om3/util/stringslice" +) + +type ( + BT struct { + resource.T + resource.SCSIPersistentReservation + ObjectDomain string `json:"object_domain"` + PG pg.Config `json:"pg"` + Path naming.Path `json:"path"` + ObjectID uuid.UUID `json:"object_id"` + SCSIReserv bool `json:"scsireserv"` + PromoteRW bool `json:"promote_rw"` + NoPreemptAbort bool `json:"no_preempt_abort"` + OsvcRootPath string `json:"osvc_root_path"` + GuestOS string `json:"guest_os"` + Name string `json:"name"` + Hostname string `json:"hostname"` + Image string `json:"image"` + ImagePullPolicy string `json:"image_pull_policy"` + CWD string `json:"cwd"` + User string `json:"user"` + Command []string `json:"command"` + DNS []string `json:"dns"` + DNSSearch []string `json:"dns_search"` + RunArgs []string `json:"run_args"` + Entrypoint []string `json:"entrypoint"` + Detach bool `json:"detach"` + Remove bool `json:"remove"` + Privileged bool `json:"privileged"` + Init bool `json:"init"` + Interactive bool `json:"interactive"` + TTY bool `json:"tty"` + VolumeMounts []string `json:"volume_mounts"` + Env []string `json:"environment"` + SecretsEnv []string `json:"secrets_environment"` + ConfigsEnv []string `json:"configs_environment"` + Devices []string `json:"devices"` + NetNS string `json:"netns"` + UserNS string `json:"userns"` + PIDNS string `json:"pidns"` + IPCNS string `json:"ipcns"` + UTSNS string `json:"utsns"` + RegistryCreds string `json:"registry_creds"` + PullTimeout *time.Duration `json:"pull_timeout"` + StartTimeout *time.Duration `json:"start_timeout"` + StopTimeout *time.Duration `json:"stop_timeout"` + + executer Executer + xContainer map[string]containerNamer + } + + BindMount struct { + Source string + Target string + Option string + } +) + +type ( + // ExecuteContainer interface defines the functions used to manage container + // lifecycle. + ExecuteContainer interface { + Enter() error + Start(context.Context) error + Stop(context.Context) error + Remove(context.Context) error + Run(context.Context) error + } + + // ExecuteImager interface defines the functions used to manage container + // image lifecycle. + ExecuteImager interface { + HasImage(context.Context) (bool, string, error) + Pull(context.Context) error + } + + // ExecuteInspecter interface defines the functions used to retrieve container + // inspecter. + ExecuteInspecter interface { + Inspect() Inspecter + InspectRefresh(context.Context) (Inspecter, error) + InspectRefreshed() bool + } + + // ExecuteWaiter interface defines the functions used to manage container + // wait functions. + ExecuteWaiter interface { + WaitNotRunning(context.Context) error + WaitRemoved(context.Context) error + } + + // Executer defines interfaces for container operations. It must be + // implemented by container executors. + Executer interface { + ExecuteContainer + ExecuteImager + ExecuteInspecter + ExecuteWaiter + } + + // ExecutorContainerArgser defines interfaces functions that provides + // args for container resource operations. + ExecutorContainerArgser interface { + EnterCmdArgs() []string + EnterCmdCheckArgs() []string + RemoveArgs() Args + RunArgs() (Args, error) + RunCmdEnv() (map[string]string, error) + StartArgs() Args + StopArgs() Args + } + + // ExecutorInspectArgser defines interfaces functions that provides + // args for container resource inspect operations. + ExecutorInspectArgser interface { + HasImageArgs() Args + InspectArgs() Args + InspectParser([]byte) (Inspecter, error) + } + + // ExecutorImageArgser defines interfaces functions that provides args for + // image operations. + ExecutorImageArgser interface { + PullArgs() Args + } + + // ExecutorArgser defines interfaces for container executor args. + // The ExecutorArgser interface is meant to define the required arguments + // or methods that a container executor should have, focusing on resource + // information. These arguments are used by executors to manage containers. + ExecutorArgser interface { + ExecutorContainerArgser + ExecutorImageArgser + ExecutorInspectArgser + ExecuteWaiter + } + + // Inspecter defines interfaces functions that a container inspector must + // provide. + Inspecter interface { + Config() *InspectDataConfig + Defined() bool + ID() string + ImageID() string + HostConfig() *InspectDataHostConfig + PID() int + Running() bool + SandboxKey() string + Status() string + } + + Logger interface { + Log() *plog.Logger + } +) + +// defines used internal interfaces +type ( + containerNamer interface { + ContainerName() string + } + + containerIDer interface { + ContainerID() string + } + + containerInspectRefresher interface { + ContainerInspectRefresh(context.Context) (Inspecter, error) + } +) + +const ( + imagePullPolicyAlways = "always" + imagePullPolicyOnce = "once" +) + +func (t *BT) Configure() error { + l := t.T.Log().Attr("container_name", t.ContainerName()) + t.SetLoggerForTest(l) + return nil +} + +func (t *BT) IsAlwaysImagePullPolicy() bool { + return t.ImagePullPolicy == imagePullPolicyAlways +} + +func (t *BT) ContainerID() string { + if i := t.executer.Inspect(); i == nil { + return "" + } else { + return i.ID() + } +} + +// ContainerName formats a docker container name +func (t *BT) ContainerName() string { + if t.Name != "" { + return t.Name + } + var s string + switch t.Path.Namespace { + case "root", "": + s = "" + default: + s = t.Path.Namespace + ".." + } + s = s + t.Path.Name + "." + strings.ReplaceAll(t.ResourceID.String(), "#", ".") + return s +} + +func (t *BT) ContainerInspectRefresh(ctx context.Context) (Inspecter, error) { + return t.executer.InspectRefresh(ctx) +} + +func (t *BT) Enter() error { + return t.executer.Enter() +} + +func (t *BT) FormatNS(s string) (string, error) { + switch s { + case "", "none", "host": + return s, nil + } + rid, err := resourceid.Parse(s) + if err != nil { + return "", fmt.Errorf("invalid value %s (must be none, host or container#)", s) + } + r := t.GetObjectDriver().ResourceByID(rid.String()) + if r == nil { + return "", fmt.Errorf("resource %s not found", s) + } + if i, ok := r.(containerNamer); ok { + name := i.ContainerName() + return "container:" + name, nil + } + return "", fmt.Errorf("resource %s has no ns", s) +} + +// GenEnv returns the list of environment variables from the resource/object and +// its ConfigsEnv: []string{"PUBLICVAR1=Value", ...} +// secret var names from its SecretsEnv are added to the list: "SECRETVAR1", "SECRETVAR2",... +// values for secrets are added to the returned envM: {"SECRETVAR1":"SECRETVALUE1", ...} +// It may be used by executorArgser to prepare run args and run command environement. +func (t *BT) GenEnv() (envL []string, envM map[string]string, err error) { + envM = make(map[string]string) + envL = []string{ + "OPENSVC_RID=" + t.RID(), + "OPENSVC_NAME=" + t.Path.String(), + "OPENSVC_KIND=" + t.Path.Kind.String(), + "OPENSVC_ID=" + t.ObjectID.String(), + "OPENSVC_NAMESPACE=" + t.Path.Namespace, + } + if len(t.Env) > 0 { + envL = append(envL, t.Env...) + } + if tempEnv, err := envprovider.From(t.ConfigsEnv, t.Path.Namespace, "cfg"); err != nil { + return nil, nil, err + } else { + for _, s := range tempEnv { + t.Log().Infof("env: %s", s) + } + envL = append(envL, tempEnv...) + } + if tempEnv, err := envprovider.From(t.SecretsEnv, t.Path.Namespace, "sec"); err != nil { + return nil, nil, err + } else { + for _, s := range tempEnv { + kv := strings.SplitN(s, "=", 2) + if len(kv) != 2 { + return nil, nil, fmt.Errorf("can't prepare env from secrets") + } + t.Log().Infof("sec %s: %s=%s", s, kv[0], kv[1]) + envM[kv[0]] = kv[1] + envL = append(envL, kv[0]) + } + } + return envL, envM, nil +} + +func (t *BT) Label() string { + return t.Image +} + +func (t *BT) Labels() map[string]string { + data := make(map[string]string) + data["com.opensvc.id"] = t.containerLabelID() + data["com.opensvc.path"] = t.Path.String() + data["com.opensvc.namespace"] = t.Path.Namespace + data["com.opensvc.kind"] = t.Path.Kind.String() + data["com.opensvc.name"] = t.Path.Name + data["com.opensvc.rid"] = t.ResourceID.String() + return data +} + +func (t *BT) LinkNames() []string { + return []string{t.RID()} +} + +func (t *BT) Mounts() ([]BindMount, error) { + mounts := make([]BindMount, 0) + for _, s := range t.VolumeMounts { + var source, target, opt string + l := strings.Split(s, ":") + n := len(l) + switch n { + case 2: + source = l[0] + target = l[1] + opt = "rw" + case 3: + source = l[0] + target = l[1] + opt = l[2] + default: + return mounts, fmt.Errorf("invalid volumes_mount entry: %s: 1-2 column-characters allowed", s) + } + if len(source) == 0 { + return mounts, fmt.Errorf("invalid volumes_mount entry: %s: empty source", s) + } + if len(target) == 0 { + return mounts, fmt.Errorf("invalid volumes_mount entry: %s: empty target", s) + } + if strings.HasPrefix(source, "/") { + // pass + } else if srcRealpath, err := vpath.HostPath(source, t.Path.Namespace); err != nil { + return mounts, err + } else if file.IsProtected(srcRealpath) { + return mounts, fmt.Errorf("invalid volumes_mount entry: %s: expanded to the protected path %s", s, srcRealpath) + } else { + source = srcRealpath + } + + mounts = append(mounts, BindMount{Source: source, Target: target, Option: opt}) + } + return mounts, nil +} + +func (t *BT) NeedPreStartRemove() bool { + return t.Remove || !t.Detach +} + +func (t *BT) NetNSPath() (string, error) { + if i := t.executer.Inspect(); i == nil { + return "", nil + } else { + return i.SandboxKey(), nil + } +} + +func (t *BT) PID() int { + if i := t.executer.Inspect(); i == nil { + return 0 + } else { + return i.PID() + } +} + +func (t *BT) Provision(_ context.Context) error { + return nil +} + +func (t *BT) Provisioned() (provisioned.T, error) { + return provisioned.NotApplicable, nil +} + +func (t *BT) Signal(sig syscall.Signal) error { + name := t.ContainerName() + inspect, err := t.executer.InspectRefresh(nil) + if err != nil { + t.Log().Errorf("signal: inspect refresh container %s: %s", name, err) + } else if inspect == nil { + t.Log().Infof("skip signal: container %s not found: %s", name) + return nil + } + if !inspect.Running() { + t.Log().Infof("skip signal: container %s not running", name) + return nil + } + pid := inspect.PID() + if pid == 0 { + t.Log().Infof("skip signal: can't detect container %s pid", name) + } + t.Log().Infof("send %s signal to container %s (pid %d)", unix.SignalName(sig), name, pid) + return syscall.Kill(pid, sig) +} + +func (t *BT) Start(ctx context.Context) error { + name := t.ContainerName() + log := t.Log() + + logError := func(err error) error { + return t.logMainAction("start", err) + } + + inspect := t.executer.Inspect() + if inspect == nil || !inspect.Defined() { + return logError(t.pullAndRun(ctx)) + } + if inspect.Running() { + log.Infof("container start %s: already started", name) + return nil + } else { + // it is defined + status := inspect.Status() + log.Debugf("container start %s: defined with status %s", name, status) + if t.NeedPreStartRemove() { + log.Infof("container start %s: remove leftover container", name) + if err := t.executer.Remove(ctx); err != nil { + return logError(err) + } + return logError(t.pullAndRun(ctx)) + } else if status == "initialized" { + log.Infof("container status %s, try fix with stop first", status) + if err := t.executer.Stop(ctx); err != nil { + return err + } + return logError(t.findAndStart(ctx)) + } else { + log.Infof("container status %s", status) + return logError(t.findAndStart(ctx)) + } + } +} + +func (t *BT) Stop(ctx context.Context) error { + name := t.ContainerName() + log := t.Log() + + logError := func(err error) error { + return t.logMainAction(fmt.Sprintf("container stop %s:", t.RID()), err) + } + + inspect := t.executer.Inspect() + if inspect == nil { + log.Infof("already stopped") + return nil + } + + if inspect.Running() { + if t.StopTimeout != nil && *t.StopTimeout > 0 { + var cancel context.CancelFunc + ctx, cancel = context.WithTimeout(ctx, *t.StopTimeout) + defer cancel() + log.Debugf("stopping with timeout %s", *t.StopTimeout) + } else { + log.Debugf("stopping") + } + defer func() { + _, _ = t.executer.InspectRefresh(ctx) + }() + if err := t.executer.Stop(ctx); err != nil { + t.Log().Errorf("stop: %s", err) + return err + } + log.Debugf("container stopped") + } + + if t.Remove { + if hostConfig := inspect.HostConfig(); hostConfig != nil && !hostConfig.AutoRemove { + t.Log().Debugf("remove container %s", name) + if err := t.executer.Remove(ctx); err != nil { + return logError(fmt.Errorf("can't remove container %s", name)) + } + } + t.Log().Debugf("wait removed condition") + if err := t.executer.WaitRemoved(ctx); err != nil { + t.Log().Warnf("wait removed: %s", err) + return err + } else { + t.Log().Debugf("removed") + return nil + } + } else { + t.Log().Debugf("wait not running condition") + if err := t.executer.WaitNotRunning(ctx); err != nil { + t.Log().Warnf("wait not running: %s", err) + return err + } + t.Log().Debugf("wait not running: done") + } + return nil +} + +func (t *BT) Status(ctx context.Context) status.T { + if !t.Detach { + t.Log().Debugf("status n/a on not dettach") + return status.NotApplicable + } + + var inspect Inspecter + var err error + t.Log().Debugf("Status.enter") + defer t.Log().Debugf("Status.return") + if !t.executer.InspectRefreshed() { + inspect, err = t.executer.InspectRefresh(ctx) + if err != nil { + t.StatusLog().Error("inspect: %s", err) + t.Log().Debugf("status down on inspect refresh error: %s", err) + return status.Down + } + } else { + inspect = t.executer.Inspect() + } + if inspect == nil { + t.Log().Debugf("status down on inspect nil") + return status.Down + } + if inspectConfig := inspect.Config(); inspectConfig != nil { + if t.Hostname != "" && inspectConfig.Hostname != t.Hostname { + t.warnAttrDiff("hostname", inspectConfig.Hostname, t.Hostname) + } + if inspectConfig.OpenStdin != t.Interactive { + t.warnAttrDiff("interactive", fmt.Sprint(inspectConfig.OpenStdin), fmt.Sprint(t.Interactive)) + } + if len(t.Entrypoint) > 0 && !stringslice.Equal(inspectConfig.Entrypoint, t.Entrypoint) { + t.warnAttrDiff("entrypoint", shellquote.Join(inspectConfig.Entrypoint...), shellquote.Join(t.Entrypoint...)) + } + if inspectConfig.Tty != t.TTY { + t.warnAttrDiff("tty", fmt.Sprint(inspectConfig.Tty), fmt.Sprint(t.TTY)) + } + } + if inspectHostConfig := inspect.HostConfig(); inspectHostConfig != nil { + if inspectHostConfig.Privileged != t.Privileged { + t.warnAttrDiff("privileged", fmt.Sprint(inspectHostConfig.Privileged), fmt.Sprint(t.Privileged)) + } + t.statusInspectNS(ctx, "netns", inspectHostConfig.NetworkMode, t.NetNS) + t.statusInspectNS(ctx, "pidns", inspectHostConfig.PidMode, t.PIDNS) + t.statusInspectNS(ctx, "ipcns", inspectHostConfig.IpcMode, t.IPCNS) + t.statusInspectNS(ctx, "utsns", inspectHostConfig.UTSMode, t.UTSNS) + t.statusInspectNS(ctx, "userns", inspectHostConfig.UsernsMode, t.UserNS) + } + + if _, imageID, err := t.executer.HasImage(ctx); err == nil { + containerImageID := inspect.ImageID() + if containerImageID != imageID { + t.warnAttrDiff("image "+t.Image, containerImageID, imageID) + } + } + + if !inspect.Running() { + if t.Remove { + t.StatusLog().Warn("container is not running") + return status.Warn + } + return status.Down + } + return status.Up +} + +func (t *BT) Unprovision(_ context.Context) error { + return nil +} + +func (t *BT) WithExecuter(c Executer) *BT { + t.executer = c + return t +} + +// NetNSPath implements the resource.NetNSPather optional interface. +// Used by ip.netns and ip.route to configure network stuff in the container. +func (t *BT) containerLabelID() string { + return fmt.Sprintf("%s.%s", t.ObjectID, t.ResourceID.String()) +} + +func (t *BT) findAndStart(ctx context.Context) error { + name := t.ContainerName() + i := t.executer.Inspect() + id := i.ID() + errs := make(chan error, 1) + go func() { + if t.StartTimeout != nil && *t.StartTimeout > 0 { + t.Log().Infof("container start %s (%s) with timeout %s", name, id, t.StartTimeout) + var cancel context.CancelFunc + ctx, cancel = context.WithTimeout(ctx, *t.StartTimeout) + defer cancel() + } else { + t.Log().Infof("container start %s (%s) without timeout", name, id) + } + + inspectRefresh := func() { + _, err := t.executer.InspectRefresh(context.Background()) + if err != nil { + t.Log().Warnf("findAndStart InspectRefresh: %s", err) + } + } + + if err := t.executer.Start(ctx); err != nil { + errs <- err + defer inspectRefresh() + return + } + t.Log().Debugf("started") + if t.Detach { + // t.executer.Wait(ctx, WaitConditionRunning) return err not found + // use check running instead + t.Log().Infof("check running") + inspect, err := t.executer.InspectRefresh(context.Background()) + if err != nil { + err = fmt.Errorf("check running: can't inspect: %s", err) + } else if inspect == nil { + err = fmt.Errorf("check running: inspect is nil") + } else if inspect.Running() { + t.Log().Debugf("check running: ok") + } else { + err = fmt.Errorf("check running: false") + } + if err != nil { + t.Log().Warnf("%s", err) + } + errs <- err + return + } + defer inspectRefresh() + t.Log().Infof("wait not running") + if err := t.executer.WaitNotRunning(ctx); err != nil { + t.Log().Debugf("wait not running: %s", err) + errs <- nil + return + } else { + t.Log().Debugf("wait not running: done") + errs <- nil + return + } + }() + + var timerC <-chan time.Time + if t.StartTimeout != nil && *t.StartTimeout > 0 { + timerC = time.After(*t.StartTimeout) + } else { + timerC = make(<-chan time.Time) + } + select { + case err := <-errs: + if err == nil { + actionrollback.Register(ctx, func() error { + return t.Stop(ctx) + }) + return nil + } + err = fmt.Errorf("container start %s (%s): %s", name, id, err) + t.Log().Errorf("%s", err) + return err + case <-timerC: + err := fmt.Errorf("container start %s (%s): timeout", name, id) + t.Log().Errorf("%s", err) + return err + } +} + +func (t *BT) logMainAction(s string, err error) error { + if err != nil { + err = fmt.Errorf("%s: %s", s, err) + t.Log().Errorf("%s", err) + return err + } + return nil +} + +func (t *BT) pull(ctx context.Context) error { + if err := t.executer.Pull(ctx); err != nil { + return fmt.Errorf("can't pull image %s: %s", t.Image, err) + } + return nil +} + +func (t *BT) pullAndRun(ctx context.Context) error { + if t.IsAlwaysImagePullPolicy() { + t.Log().Debugf("container start: with image policy: always") + if err := t.pull(ctx); err != nil { + return err + } + } else if hasImage, _, err := t.executer.HasImage(ctx); err != nil { + return fmt.Errorf("unable to detect if image %s exists localy: %s", t.Image, err) + } else if !hasImage { + if err := t.pull(ctx); err != nil { + return err + } + } + defer func() { + _, _ = t.executer.InspectRefresh(ctx) + }() + return t.executer.Run(ctx) +} + +func (t *BT) statusInspectNS(ctx context.Context, attr, current, target string) { + switch target { + case "": + return + case "none", "host": + if current != target { + t.warnAttrDiff(attr, current, target) + } + return + } + rid, err := resourceid.Parse(target) + if err != nil { + t.StatusLog().Warn("%s: invalid value %s (must be none, host or container#)", attr, target) + return + } + + if t.xContainer == nil { + t.xContainer = make(map[string]containerNamer) + } + + tgt, ok := t.xContainer[rid.String()] + if !ok { + if r := t.GetObjectDriver().ResourceByID(rid.String()); r == nil { + t.StatusLog().Warn("%s: %s resource not found", attr, target) + return + } else if tgt, ok = r.(containerNamer); !ok { + t.StatusLog().Warn("%s resource %s is not a container namer", attr, target) + return + } else { + if r, ok := r.(containerInspectRefresher); ok { + if _, err := r.ContainerInspectRefresh(ctx); err != nil { + t.StatusLog().Warn("%s resource %s inspect error", attr, target) + } + } + t.xContainer[rid.String()] = tgt + } + } + + var tgtName, tgtID string + if tgt == nil { + t.StatusLog().Warn("%s: %s resource not found", attr, target) + return + } else { + tgtName = "container:" + tgt.ContainerName() + if i, ok := tgt.(containerIDer); ok { + tgtID = "container:" + i.ContainerID() + } + } + + switch { + case tgtName == current: + t.Log().Debugf("valid %s cross-resource reference to %s: %s", attr, tgtName, current) + case tgtID == current: + t.Log().Debugf("valid %s cross-resource reference to %s: %s", attr, tgtID, current) + default: + t.Log().Debugf("invalid %s cross-resource reference to %s: found %s instead of %s or %s", + attr, target, current, tgtName, tgtID) + t.warnAttrDiff(attr, current, tgtName) + } +} + +func (t *BT) warnAttrDiff(attr, current, target string) { + t.StatusLog().Warn("%s is %s, should be %s", attr, current, target) +} diff --git a/drivers/rescontainerocibase/manifest.go b/drivers/rescontainerocibase/manifest.go new file mode 100644 index 000000000..afc11e5d3 --- /dev/null +++ b/drivers/rescontainerocibase/manifest.go @@ -0,0 +1,287 @@ +package rescontainerocibase + +import ( + "embed" + + "github.com/opensvc/om3/core/driver" + "github.com/opensvc/om3/core/keywords" + "github.com/opensvc/om3/core/manifest" + "github.com/opensvc/om3/core/naming" + "github.com/opensvc/om3/drivers/rescontainer" + "github.com/opensvc/om3/util/converters" +) + +var ( + //go:embed text + fs embed.FS +) + +// ManifestWithID exposes to the core the input expected by the driver. +func (t *BT) ManifestWithID(drvID driver.ID) *manifest.T { + m := manifest.New(drvID, t) + m.Kinds.Or(naming.KindSvc) + m.AddKeywords(manifest.SCSIPersistentReservationKeywords...) + m.Add( + manifest.ContextObjectPath, + manifest.ContextObjectID, + manifest.ContextObjectDomain, + manifest.ContextDNS, + keywords.Keyword{ + Option: "name", + Attr: "Name", + Scopable: true, + DefaultText: keywords.NewText(fs, "text/kw/name.default"), + Text: keywords.NewText(fs, "text/kw/name"), + Example: "osvcprd..rundeck.container.db", + }, + keywords.Keyword{ + Option: "hostname", + Attr: "Hostname", + Scopable: true, + Example: "nginx1", + Text: keywords.NewText(fs, "text/kw/hostname"), + }, + keywords.Keyword{ + Option: "dns_search", + Attr: "DNSSearch", + Converter: converters.List, + Aliases: []string{}, + Scopable: true, + Required: false, + Example: "opensvc.com", + Text: keywords.NewText(fs, "text/kw/dns_search"), + }, + keywords.Keyword{ + Option: "image", + Attr: "Image", + Aliases: []string{"run_image"}, + Scopable: true, + Required: true, + Example: "google/pause", + Text: keywords.NewText(fs, "text/kw/image"), + }, + keywords.Keyword{ + Option: "image", + Attr: "Image", + Aliases: []string{"run_image"}, + Scopable: true, + Required: true, + Example: "google/pause", + Text: keywords.NewText(fs, "text/kw/image"), + }, + keywords.Keyword{ + Option: "image_pull_policy", + Attr: "ImagePullPolicy", + Scopable: true, + Candidates: []string{imagePullPolicyOnce, imagePullPolicyAlways}, + Example: imagePullPolicyOnce, + Text: keywords.NewText(fs, "text/kw/image_pull_policy"), + }, + keywords.Keyword{ + Option: "cwd", + Attr: "CWD", + Scopable: true, + Example: "/opt/foo", + Text: keywords.NewText(fs, "text/kw/cwd"), + }, + keywords.Keyword{ + Option: "command", + Attr: "Command", + Aliases: []string{"run_command"}, + Scopable: true, + Converter: converters.Shlex, + Example: "/opt/tomcat/bin/catalina.sh", + Text: keywords.NewText(fs, "text/kw/command"), + }, + keywords.Keyword{ + Option: "run_args", + Attr: "RunArgs", + Scopable: true, + Converter: converters.Shlex, + Example: "-v /opt/docker.opensvc.com/vol1:/vol1:rw -p 37.59.71.25:8080:8080", + Text: keywords.NewText(fs, "text/kw/run_args"), + }, + keywords.Keyword{ + Option: "entrypoint", + Attr: "Entrypoint", + Scopable: true, + Converter: converters.Shlex, + Example: "/bin/sh", + Text: keywords.NewText(fs, "text/kw/entrypoint"), + }, + keywords.Keyword{ + Option: "detach", + Attr: "Detach", + Scopable: true, + Converter: converters.Bool, + Default: "true", + Text: keywords.NewText(fs, "text/kw/detach"), + }, + keywords.Keyword{ + Option: "rm", + Attr: "Remove", + Scopable: true, + Converter: converters.Bool, + Example: "false", + Text: keywords.NewText(fs, "text/kw/rm"), + }, + keywords.Keyword{ + Option: "privileged", + Attr: "Privileged", + Scopable: true, + Converter: converters.Bool, + Text: keywords.NewText(fs, "text/kw/privileged"), + }, + keywords.Keyword{ + Option: "init", + Attr: "Init", + Scopable: true, + Default: "true", + Converter: converters.Bool, + Text: keywords.NewText(fs, "text/kw/init"), + }, + keywords.Keyword{ + Option: "interactive", + Attr: "Interactive", + Scopable: true, + Converter: converters.Bool, + Text: keywords.NewText(fs, "text/kw/interactive"), + }, + keywords.Keyword{ + Option: "tty", + Attr: "TTY", + Scopable: true, + Converter: converters.Bool, + Text: keywords.NewText(fs, "text/kw/tty"), + }, + keywords.Keyword{ + Option: "volume_mounts", + Attr: "VolumeMounts", + Scopable: true, + Converter: converters.Shlex, + Example: "myvol1:/vol1 myvol2:/vol2:rw /localdir:/data:ro", + Text: keywords.NewText(fs, "text/kw/volume_mounts"), + }, + keywords.Keyword{ + Option: "environment", + Attr: "Env", + Scopable: true, + Converter: converters.Shlex, + Text: keywords.NewText(fs, "text/kw/environment"), + Example: "KEY=cert1/server.key PASSWORD=db/password", + }, + keywords.Keyword{ + Option: "configs_environment", + Attr: "ConfigsEnv", + Scopable: true, + Converter: converters.Shlex, + Text: keywords.NewText(fs, "text/kw/configs_environment"), + Example: "CRT=cert1/server.crt PEM=cert1/server.pem", + }, + keywords.Keyword{ + Option: "devices", + Attr: "Devices", + Scopable: true, + Converter: converters.Shlex, + Text: keywords.NewText(fs, "text/kw/devices"), + Example: "myvol1:/dev/xvda myvol2:/dev/xvdb", + }, + keywords.Keyword{ + Option: "netns", + Attr: "NetNS", + Aliases: []string{"net"}, + Scopable: true, + Example: "container#0", + Text: keywords.NewText(fs, "text/kw/netns"), + }, + keywords.Keyword{ + Option: "user", + Attr: "User", + Scopable: true, + Example: "guest", + Text: keywords.NewText(fs, "text/kw/user"), + }, + keywords.Keyword{ + Option: "userns", + Attr: "UserNS", + Scopable: true, + Example: "container#0", + Text: keywords.NewText(fs, "text/kw/userns"), + }, + keywords.Keyword{ + Option: "pidns", + Attr: "PIDNS", + Scopable: true, + Example: "container#0", + Text: keywords.NewText(fs, "text/kw/pidns"), + }, + keywords.Keyword{ + Option: "ipcns", + Attr: "IPCNS", + Scopable: true, + Example: "container#0", + Text: keywords.NewText(fs, "text/kw/ipcns"), + }, + keywords.Keyword{ + Option: "utsns", + Attr: "UTSNS", + Scopable: true, + Candidates: []string{"", "host"}, + Example: "container#0", + Text: keywords.NewText(fs, "text/kw/utsns"), + }, + keywords.Keyword{ + Option: "registry_creds", + Attr: "RegistryCreds", + Scopable: true, + Example: "creds-registry-opensvc-com", + Text: keywords.NewText(fs, "text/kw/registry_creds"), + }, + keywords.Keyword{ + Option: "pull_timeout", + Attr: "PullTimeout", + Scopable: true, + Converter: converters.Duration, + Text: keywords.NewText(fs, "text/kw/pull_timeout"), + Example: "2m", + Default: "2m", + }, + keywords.Keyword{ + Option: "start_timeout", + Attr: "StartTimeout", + Scopable: true, + Converter: converters.Duration, + Text: keywords.NewText(fs, "text/kw/start_timeout"), + Example: "1m5s", + Default: "5s", + }, + keywords.Keyword{ + Option: "stop_timeout", + Attr: "StopTimeout", + Scopable: true, + Converter: converters.Duration, + Text: keywords.NewText(fs, "text/kw/stop_timeout"), + Example: "2m", + Default: "2m30s", + }, + keywords.Keyword{ + Option: "secrets_environment", + Attr: "SecretsEnv", + Scopable: true, + Converter: converters.Shlex, + Text: keywords.NewText(fs, "text/kw/secrets_environment"), + Example: "CRT=cert1/server.pem sec1/*", + }, + keywords.Keyword{ + Option: "configs_environment", + Attr: "ConfigsEnv", + Scopable: true, + Converter: converters.Shlex, + Text: keywords.NewText(fs, "text/kw/configs_environment"), + Example: "PORT=http/port webapp/app1* {name}/* {name}-debug/settings", + }, + rescontainer.KWOsvcRootPath, + rescontainer.KWGuestOS, + ) + return m +} diff --git a/drivers/rescontainerdocker/text/kw/command b/drivers/rescontainerocibase/text/kw/command similarity index 100% rename from drivers/rescontainerdocker/text/kw/command rename to drivers/rescontainerocibase/text/kw/command diff --git a/drivers/rescontainerdocker/text/kw/configs_environment b/drivers/rescontainerocibase/text/kw/configs_environment similarity index 100% rename from drivers/rescontainerdocker/text/kw/configs_environment rename to drivers/rescontainerocibase/text/kw/configs_environment diff --git a/drivers/rescontainerdocker/text/kw/cwd b/drivers/rescontainerocibase/text/kw/cwd similarity index 100% rename from drivers/rescontainerdocker/text/kw/cwd rename to drivers/rescontainerocibase/text/kw/cwd diff --git a/drivers/rescontainerdocker/text/kw/detach b/drivers/rescontainerocibase/text/kw/detach similarity index 100% rename from drivers/rescontainerdocker/text/kw/detach rename to drivers/rescontainerocibase/text/kw/detach diff --git a/drivers/rescontainerdocker/text/kw/devices b/drivers/rescontainerocibase/text/kw/devices similarity index 100% rename from drivers/rescontainerdocker/text/kw/devices rename to drivers/rescontainerocibase/text/kw/devices diff --git a/drivers/rescontainerdocker/text/kw/dns_search b/drivers/rescontainerocibase/text/kw/dns_search similarity index 100% rename from drivers/rescontainerdocker/text/kw/dns_search rename to drivers/rescontainerocibase/text/kw/dns_search diff --git a/drivers/rescontainerdocker/text/kw/entrypoint b/drivers/rescontainerocibase/text/kw/entrypoint similarity index 100% rename from drivers/rescontainerdocker/text/kw/entrypoint rename to drivers/rescontainerocibase/text/kw/entrypoint diff --git a/drivers/rescontainerdocker/text/kw/environment b/drivers/rescontainerocibase/text/kw/environment similarity index 100% rename from drivers/rescontainerdocker/text/kw/environment rename to drivers/rescontainerocibase/text/kw/environment diff --git a/drivers/rescontainerdocker/text/kw/hostname b/drivers/rescontainerocibase/text/kw/hostname similarity index 100% rename from drivers/rescontainerdocker/text/kw/hostname rename to drivers/rescontainerocibase/text/kw/hostname diff --git a/drivers/rescontainerdocker/text/kw/image b/drivers/rescontainerocibase/text/kw/image similarity index 100% rename from drivers/rescontainerdocker/text/kw/image rename to drivers/rescontainerocibase/text/kw/image diff --git a/drivers/rescontainerdocker/text/kw/image_pull_policy b/drivers/rescontainerocibase/text/kw/image_pull_policy similarity index 100% rename from drivers/rescontainerdocker/text/kw/image_pull_policy rename to drivers/rescontainerocibase/text/kw/image_pull_policy diff --git a/drivers/rescontainerdocker/text/kw/init b/drivers/rescontainerocibase/text/kw/init similarity index 100% rename from drivers/rescontainerdocker/text/kw/init rename to drivers/rescontainerocibase/text/kw/init diff --git a/drivers/rescontainerdocker/text/kw/interactive b/drivers/rescontainerocibase/text/kw/interactive similarity index 100% rename from drivers/rescontainerdocker/text/kw/interactive rename to drivers/rescontainerocibase/text/kw/interactive diff --git a/drivers/rescontainerdocker/text/kw/ipcns b/drivers/rescontainerocibase/text/kw/ipcns similarity index 100% rename from drivers/rescontainerdocker/text/kw/ipcns rename to drivers/rescontainerocibase/text/kw/ipcns diff --git a/drivers/rescontainerdocker/text/kw/name b/drivers/rescontainerocibase/text/kw/name similarity index 100% rename from drivers/rescontainerdocker/text/kw/name rename to drivers/rescontainerocibase/text/kw/name diff --git a/drivers/rescontainerdocker/text/kw/name.default b/drivers/rescontainerocibase/text/kw/name.default similarity index 100% rename from drivers/rescontainerdocker/text/kw/name.default rename to drivers/rescontainerocibase/text/kw/name.default diff --git a/drivers/rescontainerdocker/text/kw/netns b/drivers/rescontainerocibase/text/kw/netns similarity index 100% rename from drivers/rescontainerdocker/text/kw/netns rename to drivers/rescontainerocibase/text/kw/netns diff --git a/drivers/rescontainerdocker/text/kw/pidns b/drivers/rescontainerocibase/text/kw/pidns similarity index 100% rename from drivers/rescontainerdocker/text/kw/pidns rename to drivers/rescontainerocibase/text/kw/pidns diff --git a/drivers/rescontainerdocker/text/kw/privileged b/drivers/rescontainerocibase/text/kw/privileged similarity index 100% rename from drivers/rescontainerdocker/text/kw/privileged rename to drivers/rescontainerocibase/text/kw/privileged diff --git a/drivers/rescontainerdocker/text/kw/pull_timeout b/drivers/rescontainerocibase/text/kw/pull_timeout similarity index 100% rename from drivers/rescontainerdocker/text/kw/pull_timeout rename to drivers/rescontainerocibase/text/kw/pull_timeout diff --git a/drivers/rescontainerdocker/text/kw/registry_creds b/drivers/rescontainerocibase/text/kw/registry_creds similarity index 100% rename from drivers/rescontainerdocker/text/kw/registry_creds rename to drivers/rescontainerocibase/text/kw/registry_creds diff --git a/drivers/rescontainerdocker/text/kw/rm b/drivers/rescontainerocibase/text/kw/rm similarity index 100% rename from drivers/rescontainerdocker/text/kw/rm rename to drivers/rescontainerocibase/text/kw/rm diff --git a/drivers/rescontainerdocker/text/kw/run_args b/drivers/rescontainerocibase/text/kw/run_args similarity index 100% rename from drivers/rescontainerdocker/text/kw/run_args rename to drivers/rescontainerocibase/text/kw/run_args diff --git a/drivers/rescontainerdocker/text/kw/secrets_environment b/drivers/rescontainerocibase/text/kw/secrets_environment similarity index 100% rename from drivers/rescontainerdocker/text/kw/secrets_environment rename to drivers/rescontainerocibase/text/kw/secrets_environment diff --git a/drivers/rescontainerdocker/text/kw/start_timeout b/drivers/rescontainerocibase/text/kw/start_timeout similarity index 100% rename from drivers/rescontainerdocker/text/kw/start_timeout rename to drivers/rescontainerocibase/text/kw/start_timeout diff --git a/drivers/rescontainerdocker/text/kw/stop_timeout b/drivers/rescontainerocibase/text/kw/stop_timeout similarity index 100% rename from drivers/rescontainerdocker/text/kw/stop_timeout rename to drivers/rescontainerocibase/text/kw/stop_timeout diff --git a/drivers/rescontainerdocker/text/kw/tty b/drivers/rescontainerocibase/text/kw/tty similarity index 100% rename from drivers/rescontainerdocker/text/kw/tty rename to drivers/rescontainerocibase/text/kw/tty diff --git a/drivers/rescontainerdocker/text/kw/user b/drivers/rescontainerocibase/text/kw/user similarity index 100% rename from drivers/rescontainerdocker/text/kw/user rename to drivers/rescontainerocibase/text/kw/user diff --git a/drivers/rescontainerdocker/text/kw/userns b/drivers/rescontainerocibase/text/kw/userns similarity index 100% rename from drivers/rescontainerdocker/text/kw/userns rename to drivers/rescontainerocibase/text/kw/userns diff --git a/drivers/rescontainerdocker/text/kw/utsns b/drivers/rescontainerocibase/text/kw/utsns similarity index 100% rename from drivers/rescontainerdocker/text/kw/utsns rename to drivers/rescontainerocibase/text/kw/utsns diff --git a/drivers/rescontainerdocker/text/kw/volume_mounts b/drivers/rescontainerocibase/text/kw/volume_mounts similarity index 100% rename from drivers/rescontainerdocker/text/kw/volume_mounts rename to drivers/rescontainerocibase/text/kw/volume_mounts diff --git a/drivers/rescontainerpodman/caps.go b/drivers/rescontainerpodman/caps.go new file mode 100644 index 000000000..983522465 --- /dev/null +++ b/drivers/rescontainerpodman/caps.go @@ -0,0 +1,24 @@ +package rescontainerpodman + +import ( + "os/exec" + + "github.com/opensvc/om3/util/capabilities" +) + +func init() { + capabilities.Register(capabilitiesScanner) +} + +func capabilitiesScanner() ([]string, error) { + l := make([]string, 0) + drvCap := drvID.Cap() + if _, err := exec.LookPath("podman"); err != nil { + return l, nil + } + l = append(l, drvCap) + l = append(l, drvCap+".registry_creds") + l = append(l, drvCap+".signal") + l = append(l, altDrvID.Cap()) + return l, nil +} diff --git a/drivers/rescontainerpodman/main.go b/drivers/rescontainerpodman/main.go new file mode 100644 index 000000000..be54810c8 --- /dev/null +++ b/drivers/rescontainerpodman/main.go @@ -0,0 +1,78 @@ +package rescontainerpodman + +import ( + "context" + "os/exec" + "strings" + + "github.com/opensvc/om3/core/resource" + "github.com/opensvc/om3/drivers/rescontainerocibase" +) + +type ( + T struct { + *rescontainerocibase.BT + } + + ExecutorArg struct { + *rescontainerocibase.ExecutorArg + exe string + } +) + +func New() resource.Driver { + bt := &rescontainerocibase.BT{} + t := &T{BT: bt} + executorArg := &ExecutorArg{ + ExecutorArg: &rescontainerocibase.ExecutorArg{ + BT: bt, + RunArgsDNSOptionOption: "--dns-opt", + }, + exe: "podman", + } + executor := rescontainerocibase.NewExecutor("podman", executorArg, t) + _ = t.WithExecuter(executor) + return t +} + +func (ea *ExecutorArg) WaitRemoved(ctx context.Context) error { + a := rescontainerocibase.Args{ + {Option: "container"}, + {Option: "wait"}, + {Option: "--ignore"}, + {Option: "--condition", Value: "removing", HasValue: true}, + {Option: ea.BT.ContainerName()}, + } + return ea.wait(ctx, a) +} + +func (ea *ExecutorArg) WaitNotRunning(ctx context.Context) error { + a := rescontainerocibase.Args{ + {Option: "container"}, + {Option: "wait"}, + {Option: "--ignore"}, + {Option: "--condition", Value: "stopped", HasValue: true}, + {Option: ea.BT.ContainerName()}, + } + return ea.wait(ctx, a) +} + +func (ea *ExecutorArg) wait(ctx context.Context, a rescontainerocibase.Args) error { + var cmd *exec.Cmd + if ctx != nil { + select { + case <-ctx.Done(): + return ctx.Err() + default: + cmd = exec.CommandContext(ctx, ea.exe, a.AsStrings()...) + } + } else { + cmd = exec.Command(ea.exe, a.AsStrings()...) + } + ea.BT.Log().Infof("%s %s", ea.exe, strings.Join(a.Obfuscate(), " ")) + if err := cmd.Run(); err != nil { + ea.BT.Log().Debugf("%s %s: %s", ea.exe, strings.Join(a.Obfuscate(), " "), err) + return err + } + return nil +} diff --git a/drivers/rescontainerpodman/manifest.go b/drivers/rescontainerpodman/manifest.go new file mode 100644 index 000000000..ecd3e041e --- /dev/null +++ b/drivers/rescontainerpodman/manifest.go @@ -0,0 +1,21 @@ +package rescontainerpodman + +import ( + "github.com/opensvc/om3/core/driver" + "github.com/opensvc/om3/core/manifest" +) + +var ( + drvID = driver.NewID(driver.GroupContainer, "podman") + altDrvID = driver.NewID(driver.GroupContainer, "oci") +) + +func init() { + driver.Register(drvID, New) + driver.Register(altDrvID, New) +} + +// Manifest exposes to the core the input expected by the driver. +func (t T) Manifest() *manifest.T { + return t.BT.ManifestWithID(drvID) +} diff --git a/drivers/restaskdocker/main.go b/drivers/restaskdocker/main.go index 08b71d39c..32259649f 100644 --- a/drivers/restaskdocker/main.go +++ b/drivers/restaskdocker/main.go @@ -16,6 +16,7 @@ import ( "github.com/opensvc/om3/core/resource" "github.com/opensvc/om3/core/status" "github.com/opensvc/om3/drivers/rescontainerdocker" + "github.com/opensvc/om3/drivers/rescontainerocibase" "github.com/opensvc/om3/drivers/restask" "github.com/opensvc/om3/util/pg" ) @@ -71,46 +72,48 @@ func New() resource.Driver { func (t T) Container() *rescontainerdocker.T { return &rescontainerdocker.T{ - T: t.BaseTask.T, - Detach: false, - SCSIPersistentReservation: t.SCSIPersistentReservation, - PG: t.PG, - Path: t.Path, - ObjectID: t.ObjectID, - SCSIReserv: t.SCSIReserv, - PromoteRW: t.PromoteRW, - NoPreemptAbort: t.NoPreemptAbort, - OsvcRootPath: t.OsvcRootPath, - GuestOS: t.GuestOS, - Name: t.Name, - Hostname: t.Hostname, - Image: t.Image, - ImagePullPolicy: t.ImagePullPolicy, - CWD: t.CWD, - User: t.User, - Command: t.Command, - DNS: t.DNS, - DNSSearch: t.DNSSearch, - RunArgs: t.RunArgs, - Entrypoint: t.Entrypoint, - Remove: t.Remove, - Privileged: t.Privileged, - Init: t.Init, - Interactive: t.Interactive, - TTY: t.TTY, - VolumeMounts: t.VolumeMounts, - Env: t.Env, - SecretsEnv: t.SecretsEnv, - ConfigsEnv: t.ConfigsEnv, - Devices: t.Devices, - NetNS: t.NetNS, - UserNS: t.UserNS, - PIDNS: t.PIDNS, - IPCNS: t.IPCNS, - UTSNS: t.UTSNS, - RegistryCreds: t.RegistryCreds, - PullTimeout: t.PullTimeout, - StartTimeout: t.Timeout, + BT: rescontainerocibase.BT{ + T: t.BaseTask.T, + Detach: false, + SCSIPersistentReservation: t.SCSIPersistentReservation, + PG: t.PG, + Path: t.Path, + ObjectID: t.ObjectID, + SCSIReserv: t.SCSIReserv, + PromoteRW: t.PromoteRW, + NoPreemptAbort: t.NoPreemptAbort, + OsvcRootPath: t.OsvcRootPath, + GuestOS: t.GuestOS, + Name: t.Name, + Hostname: t.Hostname, + Image: t.Image, + ImagePullPolicy: t.ImagePullPolicy, + CWD: t.CWD, + User: t.User, + Command: t.Command, + DNS: t.DNS, + DNSSearch: t.DNSSearch, + RunArgs: t.RunArgs, + Entrypoint: t.Entrypoint, + Remove: t.Remove, + Privileged: t.Privileged, + Init: t.Init, + Interactive: t.Interactive, + TTY: t.TTY, + VolumeMounts: t.VolumeMounts, + Env: t.Env, + SecretsEnv: t.SecretsEnv, + ConfigsEnv: t.ConfigsEnv, + Devices: t.Devices, + NetNS: t.NetNS, + UserNS: t.UserNS, + PIDNS: t.PIDNS, + IPCNS: t.IPCNS, + UTSNS: t.UTSNS, + RegistryCreds: t.RegistryCreds, + PullTimeout: t.PullTimeout, + StartTimeout: t.Timeout, + }, } } diff --git a/qodana.yaml b/qodana.yaml deleted file mode 100644 index 215d80806..000000000 --- a/qodana.yaml +++ /dev/null @@ -1,29 +0,0 @@ -#-------------------------------------------------------------------------------# -# Qodana analysis is configured by qodana.yaml file # -# https://www.jetbrains.com/help/qodana/qodana-yaml.html # -#-------------------------------------------------------------------------------# -version: "1.0" - -#Specify inspection profile for code analysis -profile: - name: qodana.starter - -#Enable inspections -#include: -# - name: - -#Disable inspections -#exclude: -# - name: -# paths: -# - - -#Execute shell command before Qodana execution (Applied in CI/CD pipeline) -#bootstrap: sh ./prepare-qodana.sh - -#Install IDE plugins before Qodana execution (Applied in CI/CD pipeline) -#plugins: -# - id: #(plugin id can be found at https://plugins.jetbrains.com) - -#Specify Qodana linter for analysis (Applied in CI/CD pipeline) -linter: jetbrains/qodana-go:latest diff --git a/sqodana.yaml b/sqodana.yaml deleted file mode 100644 index e69de29bb..000000000