github.com/thediveo/gons@v0.9.9/reexec/reexec_test.go (about) 1 // Copyright 2019 Harald Albrecht. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package reexec 16 17 import ( 18 "encoding/json" 19 "fmt" 20 "math/rand" 21 "os" 22 "time" 23 24 . "github.com/onsi/ginkgo/v2" 25 . "github.com/onsi/gomega" 26 ) 27 28 func init() { 29 Register("action", func() { 30 fmt.Fprintln(os.Stdout, `"done"`) 31 }) 32 Register("sleepy", func() { 33 fmt.Fprintln(os.Stdout, `"sleeping"`) 34 // Just keep this re-executed child action sleeping; we will be killed 35 // by our parent when the test is done. What a lovely family. 36 select {} 37 }) 38 Register("unintelligible", func() { 39 // Return something the parent process didn't expect. 40 fmt.Fprintln(os.Stdout, `42`) 41 }) 42 Register("envvar", func() { 43 fmt.Fprintf(os.Stdout, "%q\n", os.Getenv("foobar")) 44 }) 45 Register("withparam", func() { 46 var param string 47 if err := json.NewDecoder(os.Stdin).Decode(¶m); err != nil { 48 fmt.Fprint(os.Stderr, err.Error()) 49 return 50 } 51 fmt.Fprintf(os.Stdout, "%q\n", "xx"+param) 52 }) 53 Register("reexec", func() { 54 _ = ForkReexec("reexec", []Namespace{}, nil) 55 }) 56 Register("silent", func() {}) 57 } 58 59 var _ = Describe("reexec", func() { 60 61 It("runs action and exits", func() { 62 ec := -1 63 osExit = func(code int) { ec = code } 64 defer func() { 65 osExit = os.Exit 66 os.Setenv(magicEnvVar, "") 67 }() 68 os.Setenv(magicEnvVar, "silent") 69 CheckAction() 70 Expect(ec).To(Equal(0)) 71 }) 72 73 It("runs action and decodes answer", func() { 74 var s string 75 Expect(ForkReexec("action", []Namespace{}, &s)).NotTo(HaveOccurred()) 76 Expect(s).To(Equal("done")) 77 }) 78 79 It("runs action and passes env var", func() { 80 var s string 81 Expect(ForkReexecEnv("envvar", []Namespace{}, []string{"foobar=baz!"}, &s)).NotTo(HaveOccurred()) 82 Expect(s).To(Equal("baz!")) 83 }) 84 85 It("runs action with parameter", func() { 86 var letters = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ") 87 p := make([]rune, 8192) 88 for i := range p { 89 p[i] = letters[rand.Intn(len(letters))] 90 } 91 var r string 92 Expect(RunReexecAction("withparam", Param(string(p)), Result(&r))).NotTo(HaveOccurred()) 93 Expect(r).To(Equal("xx" + string(p))) 94 }) 95 96 It("panics when re-execution wasn't properly enabled", func() { 97 defer func(old bool) { reexecEnabled = old }(reexecEnabled) 98 reexecEnabled = false 99 Expect(func() { _ = ForkReexec("action", []Namespace{}, nil) }).To(Panic()) 100 }) 101 102 It("doesn't accept registering the same action name twice", func() { 103 Expect(func() { Register("foo", func() {}) }).NotTo(Panic()) 104 Expect(func() { Register("foo", func() {}) }).To(Panic()) 105 }) 106 107 It("doesn't accept triggering a non-registered action", func() { 108 Expect(func() { _ = ForkReexec("xxx", []Namespace{}, nil) }).To(Panic()) 109 }) 110 111 It("panics the child for a non-preregistered action", func() { 112 // Note how registering the bar action here will cause the re-executed 113 // package test child to fail, because this will trigger CheckAction() 114 // without the bar action being registered early enough in the child. 115 Expect(func() { Register("barx", func() {}) }).NotTo(Panic()) 116 err := ForkReexec("barx", []Namespace{}, nil) 117 Expect(err).To(MatchError(MatchRegexp( 118 `.* ReexecAction.Run: child failed with stderr message ` + 119 `"unregistered .* action .*\\"barx\\""`))) 120 }) 121 122 It("panics the child for invalid namespace", func() { 123 // Note that it is not possible to re-enter the current user 124 // namespace, because that would otherwise give us full privileges. We 125 // use this to check that the re-executed child correctly panics when 126 // there are problems entering namespaces. 127 Expect(ForkReexec("action", []Namespace{ 128 {Type: "user", Path: "/proc/self/ns/user"}, 129 }, nil)).To(MatchError(MatchRegexp(`ReexecAction.Run: child failed with stderr message \".* cannot join`))) 130 }) 131 132 It("doesn't re-execute from a re-executed child", func() { 133 Expect(ForkReexec("reexec", []Namespace{}, nil)).To( 134 MatchError(MatchRegexp(`ReexecAction.Run: child failed with stderr message \".* tried to re-execute`))) 135 }) 136 137 It("panics on un-decodable child result", func() { 138 var s string 139 Expect(ForkReexec("unintelligible", []Namespace{}, &s)).To( 140 MatchError(MatchRegexp(`ReexecAction.Run: cannot decode child result`))) 141 }) 142 143 It("terminates a hanging re-executed child", func() { 144 var s string 145 done := make(chan error) 146 go func() { 147 defer GinkgoRecover() 148 done <- nil 149 select { 150 case <-time.After(5 * time.Second): 151 Fail("ForkReexec failed to terminate sleeping re-executed child in time") 152 case <-done: 153 } 154 }() 155 // 156 Expect(ForkReexec("sleepy", []Namespace{}, &s)).ToNot(HaveOccurred()) 157 Expect(s).To(Equal("sleeping")) 158 }) 159 160 })