Commit 76889118 authored by Kubernetes Submit Queue's avatar Kubernetes Submit Queue Committed by GitHub

Merge pull request #45280 from JulienBalestra/run-pod-inside-unique-netns

Automatic merge from submit-queue rkt: Generate a new Network Namespace for each Pod **What this PR does / why we need it**: This PR concerns the Kubelet with the Container runtime rkt. Currently, when a Pod stops and the kubelet restart it, the Pod will use the **same network namespace** based on its PodID. When the Garbage Collection is triggered, it delete all the old resources and the current network namespace. The Pods and all containers inside it loose the _eth0_ interface. I explained more in details in #45149 how to reproduce this behavior. This PR generates a new unique network namespace name for each new/restarting Pod. The Garbage collection retrieve the correct network namespace and remove it safely. **Which issue this PR fixes** : fix #45149 **Special notes for your reviewer**: Following @yifan-gu guidelines, so maybe expecting him for the final review. **Release note**: `NONE`
parents aee07e94 7a2e0e24
...@@ -27,7 +27,9 @@ import ( ...@@ -27,7 +27,9 @@ import (
"golang.org/x/net/context" "golang.org/x/net/context"
"google.golang.org/grpc" "google.golang.org/grpc"
"k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/types"
kubetypes "k8s.io/apimachinery/pkg/types"
"k8s.io/kubernetes/pkg/api/v1" "k8s.io/kubernetes/pkg/api/v1"
kubecontainer "k8s.io/kubernetes/pkg/kubelet/container"
) )
// fakeRktInterface mocks the rktapi.PublicAPIClient interface for testing purpose. // fakeRktInterface mocks the rktapi.PublicAPIClient interface for testing purpose.
...@@ -189,3 +191,17 @@ func (f fakePodGetter) GetPodByUID(uid types.UID) (*v1.Pod, bool) { ...@@ -189,3 +191,17 @@ func (f fakePodGetter) GetPodByUID(uid types.UID) (*v1.Pod, bool) {
p, found := f.pods[uid] p, found := f.pods[uid]
return p, found return p, found
} }
type fakeNetNs struct {
networkNamespace kubecontainer.ContainerID
}
func newFakeNetNs() *fakeNetNs {
return &fakeNetNs{
networkNamespace: kubecontainer.ContainerID{},
}
}
func (f *fakeNetNs) fromRunningUnitFiles(uid kubetypes.UID, latestPod *rktapi.Pod) (kubecontainer.ContainerID, error) {
return kubecontainer.ContainerID{ID: "42"}, nil
}
...@@ -583,6 +583,7 @@ func TestGetPodStatus(t *testing.T) { ...@@ -583,6 +583,7 @@ func TestGetPodStatus(t *testing.T) {
defer ctrl.Finish() defer ctrl.Finish()
fr := newFakeRktInterface() fr := newFakeRktInterface()
fs := newFakeSystemd() fs := newFakeSystemd()
fnet := newFakeNetNs()
fnp := nettest.NewMockNetworkPlugin(ctrl) fnp := nettest.NewMockNetworkPlugin(ctrl)
fos := &containertesting.FakeOS{} fos := &containertesting.FakeOS{}
frh := &containertesting.FakeRuntimeHelper{} frh := &containertesting.FakeRuntimeHelper{}
...@@ -592,6 +593,7 @@ func TestGetPodStatus(t *testing.T) { ...@@ -592,6 +593,7 @@ func TestGetPodStatus(t *testing.T) {
runtimeHelper: frh, runtimeHelper: frh,
os: fos, os: fos,
network: network.NewPluginManager(fnp), network: network.NewPluginManager(fnp),
netns: fnet,
} }
ns := func(seconds int64) int64 { ns := func(seconds int64) int64 {
...@@ -808,6 +810,8 @@ func TestGetPodStatus(t *testing.T) { ...@@ -808,6 +810,8 @@ func TestGetPodStatus(t *testing.T) {
podTimes[podFinishedMarkerPath(r.runtimeHelper.GetPodDir(tt.result.ID), pod.Id)] = tt.result.ContainerStatuses[0].FinishedAt podTimes[podFinishedMarkerPath(r.runtimeHelper.GetPodDir(tt.result.ID), pod.Id)] = tt.result.ContainerStatuses[0].FinishedAt
} }
ctrl := gomock.NewController(t)
r.os.(*containertesting.FakeOS).StatFn = func(name string) (os.FileInfo, error) { r.os.(*containertesting.FakeOS).StatFn = func(name string) (os.FileInfo, error) {
podTime, ok := podTimes[name] podTime, ok := podTimes[name]
if !ok { if !ok {
...@@ -817,9 +821,13 @@ func TestGetPodStatus(t *testing.T) { ...@@ -817,9 +821,13 @@ func TestGetPodStatus(t *testing.T) {
mockFI.EXPECT().ModTime().Return(podTime) mockFI.EXPECT().ModTime().Return(podTime)
return mockFI, nil return mockFI, nil
} }
fnp.EXPECT().Name().Return(tt.networkPluginName)
if tt.networkPluginName == kubenet.KubenetPluginName { if tt.networkPluginName == network.DefaultPluginName {
fnp.EXPECT().Name().Return(tt.networkPluginName)
}
if tt.pods != nil && tt.networkPluginName == kubenet.KubenetPluginName {
fnp.EXPECT().Name().Return(tt.networkPluginName)
if tt.result.IP != "" { if tt.result.IP != "" {
fnp.EXPECT().GetPodNetworkStatus("default", "guestbook", kubecontainer.ContainerID{ID: "42"}). fnp.EXPECT().GetPodNetworkStatus("default", "guestbook", kubecontainer.ContainerID{ID: "42"}).
Return(&network.PodNetworkStatus{IP: net.ParseIP(tt.result.IP)}, nil) Return(&network.PodNetworkStatus{IP: net.ParseIP(tt.result.IP)}, nil)
...@@ -838,7 +846,9 @@ func TestGetPodStatus(t *testing.T) { ...@@ -838,7 +846,9 @@ func TestGetPodStatus(t *testing.T) {
assert.Equal(t, tt.result, status, testCaseHint) assert.Equal(t, tt.result, status, testCaseHint)
assert.Equal(t, []string{"ListPods"}, fr.called, testCaseHint) assert.Equal(t, []string{"ListPods"}, fr.called, testCaseHint)
fnet.networkNamespace = kubecontainer.ContainerID{}
fr.CleanCalls() fr.CleanCalls()
ctrl.Finish()
} }
} }
...@@ -1800,6 +1810,10 @@ func TestGarbageCollect(t *testing.T) { ...@@ -1800,6 +1810,10 @@ func TestGarbageCollect(t *testing.T) {
for _, name := range serviceFileNames { for _, name := range serviceFileNames {
mockFI := containertesting.NewMockFileInfo(ctrl) mockFI := containertesting.NewMockFileInfo(ctrl)
// we need to specify two calls
// first: get all systemd units
// second: filter only the files with a k8s_ prefix
mockFI.EXPECT().Name().Return(name)
mockFI.EXPECT().Name().Return(name) mockFI.EXPECT().Name().Return(name)
fileInfos = append(fileInfos, mockFI) fileInfos = append(fileInfos, mockFI)
} }
...@@ -2002,3 +2016,53 @@ func TestConstructSyslogIdentifier(t *testing.T) { ...@@ -2002,3 +2016,53 @@ func TestConstructSyslogIdentifier(t *testing.T) {
assert.Equal(t, testCase.identifier, identifier, fmt.Sprintf("Test case #%d", i)) assert.Equal(t, testCase.identifier, identifier, fmt.Sprintf("Test case #%d", i))
} }
} }
func TestGetPodSystemdServiceFiles(t *testing.T) {
fs := kubetesting.NewFakeOS()
r := &Runtime{os: fs}
testCases := []struct {
serviceFilesOnDisk []string
expected []string
}{
{
[]string{"one.service", "two.service", "k8s_513ce947-8f6e-4d27-8c03-99f97b78d680.service", "k8s_184482df-8630-4d41-b84f-302684871758.service", "k8s_f4a244d8-5ec2-4f59-b7dd-c9e130d6e7a3.service", "k8s_f5aad446-5598-488f-93a4-5a27e03e7fcb.service"},
[]string{"k8s_513ce947-8f6e-4d27-8c03-99f97b78d680.service", "k8s_184482df-8630-4d41-b84f-302684871758.service", "k8s_f4a244d8-5ec2-4f59-b7dd-c9e130d6e7a3.service", "k8s_f5aad446-5598-488f-93a4-5a27e03e7fcb.service"},
},
{
[]string{"one.service", "two.service"},
[]string{},
},
{
[]string{"one.service", "k8s_513ce947-8f6e-4d27-8c03-99f97b78d680.service"},
[]string{"k8s_513ce947-8f6e-4d27-8c03-99f97b78d680.service"},
},
}
for i, tt := range testCases {
ctrl := gomock.NewController(t)
fs.ReadDirFn = func(dirname string) ([]os.FileInfo, error) {
serviceFileNames := tt.serviceFilesOnDisk
var fileInfos []os.FileInfo
for _, name := range serviceFileNames {
mockFI := containertesting.NewMockFileInfo(ctrl)
// we need to specify two calls
// first: get all systemd units
// second: filter only the files with a k8s_ prefix
mockFI.EXPECT().Name().Return(name)
mockFI.EXPECT().Name().Return(name)
fileInfos = append(fileInfos, mockFI)
}
return fileInfos, nil
}
serviceFiles, err := r.getPodSystemdServiceFiles()
if err != nil {
t.Errorf("%v", err)
}
for _, f := range serviceFiles {
assert.Contains(t, tt.expected, f.Name(), fmt.Sprintf("Test case #%d", i))
}
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment