purpleidea / mgmt

Next generation distributed, event-driven, parallel config management!
https://purpleidea.com/tags/mgmtconfig/
GNU General Public License v3.0
3.59k stars 311 forks source link

mgmt behaves incorrectly when the same exec field is defined twice in an mcl file #575

Open adamsigal opened 4 years ago

adamsigal commented 4 years ago

Versions:

Description:

When creating an exec resource in mcl, if the same field is defined more than once, it is not immediately prohibited. This causes errors down the line.

Here is a simple example file to demonstrate:

exec "exec2" {
    shell => "/bin/bash",
    ifcmd => "/bin/true",
    ifshell => "/bin/bash",
        cmd => "/usr/bin/env",
    cmd => "/usr/bin",
}

running this example produces the following output:

2019-12-18 14:24:34.006325 I | cli: lang: lexing/parsing...
2019-12-18 14:24:34.010397 I | cli: lang: init...
2019-12-18 14:24:34.010432 I | cli: lang: interpolating...
2019-12-18 14:24:34.010514 I | cli: lang: building scope...
2019-12-18 14:24:34.010660 I | cli: lang: running type unification...
2019-12-18 14:24:34.011103 I | cli: lang: input: examples/lang/exec_duplicate_field.mcl
2019-12-18 14:24:34.011128 I | cli: lang: tree:
.
├── exec_duplicate_field.mcl
└── metadata.yaml
This is: mgmt, version: 0.0.21-44-g590a4d0-dirty
Copyright (C) 2013-2019+ James Shubin and the project contributors
Written by James Shubin <james@shubin.ca> and the project contributors
14:24:34 main: start: 1576639474011281688
14:24:34 main: warning: working prefix directory is temporary!
14:24:34 main: working prefix is: /tmp/mgmt-compy-250335721
14:24:34 PGP: Created key: 4CD81B74
14:24:34 main: no seeds specified!
14:24:34 etcd: running...
14:24:34 etcd: watching chooser...
14:24:34 etcd: bootstrapping...
14:24:34 etcd: nominated: compy=http://localhost:2380
14:24:34 etcd: waiting for server...
14:24:34 etcd: server: runServer: (newCluster=true): compy=http://localhost:2380
14:24:34 etcd: server: starting...
14:24:35 etcd: server: ready
14:24:35 etcd: connect...
14:24:35 etcd: connected!
14:24:35 etcd: chooser: set dynamic cluster size to: 5
14:24:35 etcd: watching nominees...
14:24:35 etcd: nominated: compy=http://localhost:2380
14:24:35 etcd: watching volunteers...
14:24:35 etcd: volunteering...
14:24:35 etcd: watching endpoints...
14:24:35 main: etcd is ready!
14:24:35 main: waiting...
14:24:35 etcd: volunteers: compy=http://localhost:2380
14:24:35 etcd: chooser: (compy=http://localhost:2380)/(compy=http://localhost:2380)
14:24:35 etcd: chooser result(+/-): []/[]
14:24:35 main: running...
14:24:35 main: waiting...
14:24:35 gapi: generating new graph...
14:24:35 gapi: swap!
14:24:35 gapi: lang: lexing/parsing...
14:24:35 etcd: volunteers: compy=http://localhost:2380
14:24:35 etcd: chooser: (compy=http://localhost:2380)/(compy=http://localhost:2380)
14:24:35 etcd: chooser result(+/-): []/[]
14:24:35 gapi: lang: init...
14:24:35 gapi: lang: interpolating...
14:24:35 gapi: lang: building scope...
14:24:35 gapi: lang: running type unification...
14:24:35 gapi: lang: building function graph...
14:24:35 gapi: lang: function engine initializing...
14:24:35 gapi: lang: function engine validating...
14:24:35 gapi: lang: function engine starting...
14:24:35 gapi: lang: stream...
14:24:35 gapi: lang: loop...
14:24:35 gapi: lang: funcs: func `str("/bin/true")` started
14:24:35 gapi: lang: funcs: func `str("/usr/bin")` started
14:24:35 gapi: lang: funcs: func `str("/bin/bash")` started
14:24:35 gapi: lang: funcs: func `str("/bin/true")` changed
14:24:35 gapi: lang: funcs: func `str("/bin/true")` stopped
14:24:35 gapi: lang: funcs: func `str("/bin/bash")` started
14:24:35 gapi: lang: funcs: func `str("/usr/bin/env")` started
14:24:35 gapi: lang: funcs: func `str("/bin/bash")` changed
14:24:35 gapi: lang: funcs: func `str("exec2")` started
14:24:35 gapi: lang: funcs: func `str("/usr/bin")` changed
14:24:35 gapi: lang: funcs: func `str("/usr/bin")` stopped
14:24:35 gapi: lang: funcs: func `str("/bin/bash")` stopped
14:24:35 gapi: lang: funcs: func `str("exec2")` changed
14:24:35 gapi: lang: funcs: func `str("exec2")` stopped
14:24:35 gapi: lang: funcs: func `str("/bin/bash")` changed
14:24:35 gapi: lang: funcs: func `str("/bin/bash")` stopped
14:24:35 gapi: lang: funcs: func `str("/usr/bin/env")` changed
14:24:35 gapi: lang: funcs: func `str("/usr/bin/env")` stopped
14:24:35 gapi: generating new graph...
14:24:35 gapi: generating new graph...
14:24:35 gapi: lang: running interpret...
14:24:35 engine: autoedge: adding autoedges...
14:24:35 engine: autogroup: algorithm: wrappedGrouper: NonReachabilityGrouper...
14:24:35 main: commit...
14:24:35 engine: graph sync...
14:24:35 main: graph: Vertices(1), Edges(0)
14:24:35 main: waiting...
14:24:35 gapi: lang: running interpret...
14:24:35 engine: autoedge: adding autoedges...
14:24:35 engine: autogroup: algorithm: wrappedGrouper: NonReachabilityGrouper...
14:24:35 engine: Worker(exec[exec2])
14:24:35 main: commit...
14:24:35 engine: graph sync...
14:24:35 main: graph: Vertices(1), Edges(0)
14:24:35 main: waiting...
14:24:35 engine: Watch(exec[exec2])
14:24:35 engine: exec[exec2]: CheckApply(true)
14:24:35 engine: exec[exec2]: resource: ifcmd output is empty!
14:24:35 engine: exec[exec2]: resource: Apply
14:24:35 engine: exec[exec2]: CheckApply(true): Return(false, exit status 126
cmd error, exit status: 126
github.com/purpleidea/mgmt/util/errwrap.Wrapf
    /home/<user>/go/src/github.com/purpleidea/mgmt/util/errwrap/errwrap.go:29
github.com/purpleidea/mgmt/engine/resources.(*ExecRes).CheckApply
    /home/<user>/go/src/github.com/purpleidea/mgmt/engine/resources/exec.go:453
github.com/purpleidea/mgmt/engine/graph.(*Engine).Process
    /home/<user>/go/src/github.com/purpleidea/mgmt/engine/graph/actions.go:157
github.com/purpleidea/mgmt/engine/graph.(*Engine).Worker
    /home/<user>/go/src/github.com/purpleidea/mgmt/engine/graph/actions.go:522
github.com/purpleidea/mgmt/engine/graph.(*Engine).Commit.func1.2.1
    /home/<user>/go/src/github.com/purpleidea/mgmt/engine/graph/engine.go:232
runtime.goexit
    /usr/local/go/src/runtime/asm_amd64.s:1337)
14:24:35 engine: Watch(exec[exec2]): Exited(<nil>)
14:24:35 engine: Worker(exec[exec2]): Exited(exit status 126
cmd error, exit status: 126
github.com/purpleidea/mgmt/util/errwrap.Wrapf
    /home/<user>/go/src/github.com/purpleidea/mgmt/util/errwrap/errwrap.go:29
github.com/purpleidea/mgmt/engine/resources.(*ExecRes).CheckApply
    /home/<user>/go/src/github.com/purpleidea/mgmt/engine/resources/exec.go:453
github.com/purpleidea/mgmt/engine/graph.(*Engine).Process
    /home/<user>/go/src/github.com/purpleidea/mgmt/engine/graph/actions.go:157
github.com/purpleidea/mgmt/engine/graph.(*Engine).Worker
    /home/<user>/go/src/github.com/purpleidea/mgmt/engine/graph/actions.go:522
github.com/purpleidea/mgmt/engine/graph.(*Engine).Commit.func1.2.1
    /home/<user>/go/src/github.com/purpleidea/mgmt/engine/graph/engine.go:232
runtime.goexit
    /usr/local/go/src/runtime/asm_amd64.s:1337
error during Process()
github.com/purpleidea/mgmt/util/errwrap.Wrapf
    /home/<user>/go/src/github.com/purpleidea/mgmt/util/errwrap/errwrap.go:29
github.com/purpleidea/mgmt/engine/graph.(*Engine).Process
    /home/<user>/go/src/github.com/purpleidea/mgmt/engine/graph/actions.go:238
github.com/purpleidea/mgmt/engine/graph.(*Engine).Worker
    /home/<user>/go/src/github.com/purpleidea/mgmt/engine/graph/actions.go:522
github.com/purpleidea/mgmt/engine/graph.(*Engine).Commit.func1.2.1
    /home/<user>/go/src/github.com/purpleidea/mgmt/engine/graph/engine.go:232
runtime.goexit
    /usr/local/go/src/runtime/asm_amd64.s:1337)
purpleidea commented 4 years ago

You should try an example with

cmd => "/usr/bin/echo hello", cmd => "/usr/bin/echo world",

and see what happens!

But yeah, as discussed, I think this is a good (and possibly not too difficult) bug for you to work on after the env stuff is done or if you're blocked. The algorithm is trivial, however the most difficult part of this will be finding and deciding where the best place this code should be is. LMK how your reading goes.