Thanks to visit codestin.com
Credit goes to github.com

Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 0 additions & 30 deletions internal/findprocess/findprocess.go

This file was deleted.

70 changes: 0 additions & 70 deletions internal/findprocess/findprocess_test.go

This file was deleted.

24 changes: 0 additions & 24 deletions internal/findprocess/findprocess_unix.go

This file was deleted.

14 changes: 0 additions & 14 deletions internal/findprocess/findprocess_windows.go

This file was deleted.

19 changes: 16 additions & 3 deletions internal/lib/sandbox/namespaces.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ const (
IPCNS NSType = "ipc"
UTSNS NSType = "uts"
USERNS NSType = "user"
PIDNS NSType = "pid"
numNamespaces = 4
)

Expand Down Expand Up @@ -274,6 +275,14 @@ func (s *Sandbox) UserNsJoin(nspath string) error {
return err
}

// PidNs specific functions

// PidNsPath returns the path to the pid namespace of the sandbox.
// If the sandbox uses the host namespace, the empty string is returned.
func (s *Sandbox) PidNsPath() string {
return s.nsPath(nil, PIDNS)
}

// nsJoin checks if the current iface is nil, and if so gets the namespace at nsPath
func nsJoin(nsPath string, nsType NSType, currentIface NamespaceIface) (NamespaceIface, error) {
if currentIface != nil {
Expand All @@ -294,7 +303,11 @@ func (s *Sandbox) nsPath(ns NamespaceIface, nsType NSType) string {
func infraPid(infra *oci.Container) int {
pid := -1
if infra != nil {
pid = infra.State().Pid
var err error
pid, err = infra.Pid()
if err != nil {
logrus.Errorf("pid for infra container %s not found: %v", infra.ID(), err)
}
}
return pid
}
Expand All @@ -303,9 +316,9 @@ func infraPid(infra *oci.Container) int {
// calling a container.State() in batch operations
func nsPathGivenInfraPid(ns NamespaceIface, nsType NSType, infraPid int) string {
// caller is responsible for checking if infraContainer
// is valid. If not, infraPid should be negative
// is valid. If not, infraPid should be less than or equal to 0
if ns == nil || ns.Get() == nil {
if infraPid >= 0 {
if infraPid > 0 {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this doesn't match the new comment on 319. Did you mean to remove the = here?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the comment is meant to say "if it's not valid". is the wording unclear?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I also had to read this part twice, and it is correct, including the comment, but maybe not super clear. Not sure how to improve it though :(

return infraNsPath(nsType, infraPid)
}
return ""
Expand Down
16 changes: 15 additions & 1 deletion internal/lib/sandbox/namespaces_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -342,6 +342,13 @@ var _ = t.Describe("SandboxManagedNamespaces", func() {
// Then
Expect(ns).To(Equal(""))
})
It("should get nothing when pid not set", func() {
// Given
// When
ns := testSandbox.PidNsPath()
// Then
Expect(ns).To(Equal(""))
})
It("should get something when network is set", func() {
// Given
managedNamespaces := []sandbox.NSType{"net"}
Expand Down Expand Up @@ -427,6 +434,7 @@ var _ = t.Describe("SandboxManagedNamespaces", func() {
nsPaths := testSandbox.NamespacePaths()
// Then
Expect(len(nsPaths)).To(Equal(0))
Expect(testSandbox.PidNsPath()).To(BeEmpty())
})
It("should get something when infra set and pid running", func() {
// Given
Expand All @@ -438,6 +446,7 @@ var _ = t.Describe("SandboxManagedNamespaces", func() {
Expect(ns.Path()).To(ContainSubstring("/proc"))
}
Expect(len(nsPaths)).To(Equal(numManagedNamespaces))
Expect(testSandbox.PidNsPath()).To(ContainSubstring("/proc"))
})
It("should get nothing when infra set with pid not running", func() {
// Given
Expand All @@ -447,8 +456,9 @@ var _ = t.Describe("SandboxManagedNamespaces", func() {
nsPaths := testSandbox.NamespacePaths()
// Then
Expect(len(nsPaths)).To(Equal(0))
Expect(testSandbox.PidNsPath()).To(BeEmpty())
})
It("should get managed path despite infra set", func() {
It("should get managed path (except pid) despite infra set", func() {
// Given
setupInfraContainerWithPid(1)
getPath := pinNamespacesFunctor{
Expand All @@ -468,6 +478,8 @@ var _ = t.Describe("SandboxManagedNamespaces", func() {
Expect(ns.Path()).NotTo(ContainSubstring("/proc"))
}
Expect(len(nsPaths)).To(Equal(numManagedNamespaces))

Expect(testSandbox.PidNsPath()).To(ContainSubstring("/proc"))
})
})
t.Describe("NamespacePaths without infra", func() {
Expand Down Expand Up @@ -495,6 +507,8 @@ func setupInfraContainerWithPid(pid int) {
cstate.State = specs.State{
Pid: pid,
}
// eat error here because callers may send invalid pids to test against
_ = cstate.SetInitPid(pid) // nolint:errcheck
testContainer.SetState(cstate)

Expect(testSandbox.SetInfraContainer(testContainer)).To(BeNil())
Expand Down
112 changes: 111 additions & 1 deletion internal/oci/container.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ const defaultStopSignalInt = 15
var (
defaultStopSignal = strconv.Itoa(defaultStopSignalInt)
ErrContainerStopped = errors.New("container is already stopped")
ErrNotFound = errors.New("container process not found")
)

// Container represents a runtime container.
Expand Down Expand Up @@ -78,6 +79,11 @@ type ContainerState struct {
ExitCode *int32 `json:"exitCode,omitempty"`
OOMKilled bool `json:"oomKilled,omitempty"`
Error string `json:"error,omitempty"`
InitPid int `json:"initPid,omitempty"`
// The unix start time of the container's init PID.
// This is used to track whether the PID we have stored
// is the same as the corresponding PID on the host.
InitStartTime int `json:"initStartTime,omitempty"`
}

// NewContainer creates a container object.
Expand Down Expand Up @@ -154,6 +160,12 @@ func (c *Container) StopSignal() syscall.Signal {
}

// FromDisk restores container's state from disk
// Calls to FromDisk should always be preceded by call to Runtime.UpdateContainerStatus.
// This is because FromDisk() initializes the InitStartTime for the saved container state
// when CRI-O is being upgraded to a version that supports tracking PID,
// but does no verification the container is actually still running. If we assume the container
// is still running, we could incorrectly think a process with the same PID running on the host
// is our container. A call to `$runtime state` will protect us against this.
func (c *Container) FromDisk() error {
jsonSource, err := os.Open(c.StatePath())
if err != nil {
Expand All @@ -162,7 +174,37 @@ func (c *Container) FromDisk() error {
defer jsonSource.Close()

dec := json.NewDecoder(jsonSource)
return dec.Decode(c.state)
tmpState := &ContainerState{}
if err := dec.Decode(tmpState); err != nil {
return err
}

// this is to handle the situation in which we're upgrading
// versions of cri-o, and we didn't used to have this information in the state
if tmpState.InitPid == 0 && tmpState.InitStartTime == 0 && tmpState.Pid != 0 {
if err := tmpState.SetInitPid(tmpState.Pid); err != nil {
return err
}
logrus.Infof("PID information for container %s updated to %d %d", c.id, tmpState.InitPid, tmpState.InitStartTime)
}
c.state = tmpState
return nil
}

// SetInitPid initializes the InitPid and InitStartTime for the container state
// given a PID.
// These values should be set once, and not changed again.
func (cstate *ContainerState) SetInitPid(pid int) error {
if cstate.InitPid != 0 || cstate.InitStartTime != 0 {
return errors.Errorf("pid and start time already initialized: %d %d", cstate.InitPid, cstate.InitStartTime)
}
cstate.InitPid = pid
startTime, err := getPidStartTime(pid)
if err != nil {
return err
}
cstate.InitStartTime = startTime
return nil
}

// StatePath returns the containers state.json path
Expand Down Expand Up @@ -346,6 +388,74 @@ func (c *Container) exitFilePath() string {
return filepath.Join(c.dir, "exit")
}

// IsAlive is a function that checks if a container's init PID exists.
// It is used to check a container state when we don't want a `$runtime state` call
func (c *Container) IsAlive() bool {
_, err := c.pid()
if err != nil {
logrus.Errorf("checking if PID of %s is running failed: %v", c.id, err)
return false
}

return true
}

// Pid returns the container's init PID.
// It will fail if the saved PID no longer belongs to the container.
func (c *Container) Pid() (int, error) {
c.opLock.Lock()
defer c.opLock.Unlock()
return c.pid()
}

// pid returns the container's init PID.
// It checks that we have an InitPid defined in the state, that PID can be found
// and it is the same process that was originally started by the runtime.
func (c *Container) pid() (int, error) {
if c.state == nil {
return 0, errors.New("state not initialized")
}
if c.state.InitPid <= 0 {
return 0, errors.New("PID not initialized")
}

// container has stopped (as pid is initialized but the runc state has overwritten it)
if c.state.Pid == 0 {
return 0, ErrNotFound
}

if err := c.verifyPid(); err != nil {
return 0, err
}
return c.state.InitPid, nil
}

// verifyPid checks that the start time for the process on the node is the same
// as the start time we saved after creating the container.
// This is the simplest way to verify we are operating on the container
// process, and haven't run into PID wrap.
func (c *Container) verifyPid() error {
startTime, err := getPidStartTime(c.state.InitPid)
if err != nil {
return err
}

if startTime != c.state.InitStartTime {
return errors.New("PID running but not the original container. PID wrap may have occurred")
}
return nil
}

// getPidStartTime reads the kernel's /proc entry for stime for PID.
func getPidStartTime(pid int) (int, error) {
var st unix.Stat_t
if err := unix.Stat(fmt.Sprintf("/proc/%d", pid), &st); err != nil {
return 0, errors.Wrapf(ErrNotFound, err.Error())
}

return int(st.Ctim.Sec), nil
}

// ShouldBeStopped checks whether the container state is in a place
// where attempting to stop it makes sense
// a container is not stoppable if it's paused or stopped
Expand Down
Loading