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(&param); 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  })