engine: resources: Fix missing file when specified without content
If the file res was defined with state => "exists" but no content specified, it was not created. This patch fixes this bug and adds a test and an example.
This commit is contained in:
@@ -569,6 +569,52 @@ func (obj *FileRes) syncCheckApply(apply bool, src, dst string) (bool, error) {
|
|||||||
return checkOK, nil
|
return checkOK, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// state performs a CheckApply of the file state to create an empty file.
|
||||||
|
func (obj *FileRes) stateCheckApply(apply bool) (bool, error) {
|
||||||
|
if obj.State == "" { // state is not specified
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := os.Stat(obj.path)
|
||||||
|
|
||||||
|
if err != nil && !os.IsNotExist(err) {
|
||||||
|
return false, errwrap.Wrapf(err, "could not stat file")
|
||||||
|
}
|
||||||
|
|
||||||
|
if obj.State == "absent" && os.IsNotExist(err) {
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if obj.State == "exists" && err == nil {
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// state is not okay, no work done, exit, but without error
|
||||||
|
if !apply {
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if obj.State == "absent" {
|
||||||
|
return false, nil // defer the work to contentCheckApply
|
||||||
|
}
|
||||||
|
|
||||||
|
if obj.Content == nil && !obj.isDir {
|
||||||
|
// Create an empty file to ensure one exists. Don't O_TRUNC it,
|
||||||
|
// in case one is magically created right after our exists test.
|
||||||
|
// The chmod used is what is used by the os.Create function.
|
||||||
|
// TODO: is using O_EXCL okay?
|
||||||
|
f, err := os.OpenFile(obj.path, os.O_WRONLY|os.O_CREATE|os.O_EXCL, 0666)
|
||||||
|
if err != nil {
|
||||||
|
return false, errwrap.Wrapf(err, "problem creating empty file")
|
||||||
|
}
|
||||||
|
if err := f.Close(); err != nil {
|
||||||
|
return false, errwrap.Wrapf(err, "problem closing empty file")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false, nil // defer the Content != nil and isDir work to later...
|
||||||
|
}
|
||||||
|
|
||||||
// contentCheckApply performs a CheckApply for the file existence and content.
|
// contentCheckApply performs a CheckApply for the file existence and content.
|
||||||
func (obj *FileRes) contentCheckApply(apply bool) (checkOK bool, _ error) {
|
func (obj *FileRes) contentCheckApply(apply bool) (checkOK bool, _ error) {
|
||||||
obj.init.Logf("contentCheckApply(%t)", apply)
|
obj.init.Logf("contentCheckApply(%t)", apply)
|
||||||
@@ -755,6 +801,12 @@ func (obj *FileRes) CheckApply(apply bool) (checkOK bool, _ error) {
|
|||||||
|
|
||||||
checkOK = true
|
checkOK = true
|
||||||
|
|
||||||
|
// always run stateCheckApply before contentCheckApply, they go together
|
||||||
|
if c, err := obj.stateCheckApply(apply); err != nil {
|
||||||
|
return false, err
|
||||||
|
} else if !c {
|
||||||
|
checkOK = false
|
||||||
|
}
|
||||||
if c, err := obj.contentCheckApply(apply); err != nil {
|
if c, err := obj.contentCheckApply(apply); err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
} else if !c {
|
} else if !c {
|
||||||
|
|||||||
@@ -268,6 +268,54 @@ func TestResources1(t *testing.T) {
|
|||||||
cleanup: func() error { return os.Remove(f) },
|
cleanup: func() error { return os.Remove(f) },
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
{
|
||||||
|
r := makeRes("file", "r1")
|
||||||
|
res := r.(*FileRes) // if this panics, the test will panic
|
||||||
|
p := "/tmp/emptyfile"
|
||||||
|
res.Path = p
|
||||||
|
res.State = "exists"
|
||||||
|
|
||||||
|
timeline := []Step{
|
||||||
|
NewStartupStep(1000 * 60), // startup
|
||||||
|
NewChangedStep(1000*60, false), // did we do something?
|
||||||
|
fileExpect(p, ""), // check initial state
|
||||||
|
NewClearChangedStep(1000 * 15), // did we do something?
|
||||||
|
}
|
||||||
|
|
||||||
|
testCases = append(testCases, test{
|
||||||
|
name: "touch file",
|
||||||
|
res: res,
|
||||||
|
fail: false,
|
||||||
|
timeline: timeline,
|
||||||
|
expect: func() error { return nil },
|
||||||
|
startup: func() error { return nil },
|
||||||
|
cleanup: func() error { return os.Remove(p) },
|
||||||
|
})
|
||||||
|
}
|
||||||
|
{
|
||||||
|
r := makeRes("file", "r1")
|
||||||
|
res := r.(*FileRes) // if this panics, the test will panic
|
||||||
|
p := "/tmp/existingfile"
|
||||||
|
res.Path = p
|
||||||
|
res.State = "exists"
|
||||||
|
content := "some existing text\n"
|
||||||
|
|
||||||
|
timeline := []Step{
|
||||||
|
NewStartupStep(1000 * 60), // startup
|
||||||
|
NewChangedStep(1000*60, true), // did we do something?
|
||||||
|
fileExpect(p, content), // check initial state
|
||||||
|
}
|
||||||
|
|
||||||
|
testCases = append(testCases, test{
|
||||||
|
name: "existing file",
|
||||||
|
res: res,
|
||||||
|
fail: false,
|
||||||
|
timeline: timeline,
|
||||||
|
expect: func() error { return nil },
|
||||||
|
startup: func() error { return ioutil.WriteFile(p, []byte(content), 0666) },
|
||||||
|
cleanup: func() error { return os.Remove(p) },
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
names := []string{}
|
names := []string{}
|
||||||
for index, tc := range testCases { // run all the tests
|
for index, tc := range testCases { // run all the tests
|
||||||
|
|||||||
@@ -4,11 +4,16 @@ exec "exec1" {
|
|||||||
args => ["-c", "print(\"i'm in python!\")",],
|
args => ["-c", "print(\"i'm in python!\")",],
|
||||||
}
|
}
|
||||||
|
|
||||||
|
file "/tmp/whatever" {
|
||||||
|
state => "exists",
|
||||||
|
}
|
||||||
|
|
||||||
exec "exec2" {
|
exec "exec2" {
|
||||||
cmd => "echo hello world > /tmp/whatever",
|
cmd => "echo hello world > /tmp/whatever",
|
||||||
shell => "/bin/bash",
|
shell => "/bin/bash",
|
||||||
ifcmd => "! diff <(cat /tmp/whatever) <(echo hello world)",
|
ifcmd => "! diff <(cat /tmp/whatever) <(echo hello world)",
|
||||||
ifshell => "/bin/bash",
|
ifshell => "/bin/bash",
|
||||||
watchcmd => "touch /tmp/whatever && /usr/bin/inotifywait -e modify -m /tmp/whatever",
|
watchcmd => "/usr/bin/inotifywait -e modify -m /tmp/whatever",
|
||||||
watchshell => "/bin/bash",
|
|
||||||
|
Depend => File["/tmp/whatever"], # so that inotifywait can startup
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user