Skip to content

Commit f827863

Browse files
committed
Inject owner references into mirror pods
1 parent f8b45a1 commit f827863

File tree

4 files changed

+155
-8
lines changed

4 files changed

+155
-8
lines changed

pkg/kubelet/kubelet.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -591,7 +591,8 @@ func NewMainKubelet(kubeCfg *kubeletconfiginternal.KubeletConfiguration,
591591
}
592592
}
593593
// podManager is also responsible for keeping secretManager and configMapManager contents up-to-date.
594-
klet.podManager = kubepod.NewBasicPodManager(kubepod.NewBasicMirrorClient(klet.kubeClient), secretManager, configMapManager, checkpointManager)
594+
mirrorPodClient := kubepod.NewBasicMirrorClient(klet.kubeClient, string(nodeName), nodeInfo.NodeLister)
595+
klet.podManager = kubepod.NewBasicPodManager(mirrorPodClient, secretManager, configMapManager, checkpointManager)
595596

596597
klet.statusManager = status.NewManager(klet.kubeClient, klet.podManager, klet)
597598

pkg/kubelet/kubelet_getters.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,9 @@ import (
2424
"path/filepath"
2525

2626
cadvisorapiv1 "github.com/google/cadvisor/info/v1"
27+
v1 "k8s.io/api/core/v1"
2728
"k8s.io/klog"
2829

29-
"k8s.io/api/core/v1"
3030
"k8s.io/apimachinery/pkg/types"
3131
"k8s.io/kubernetes/pkg/kubelet/cm"
3232
"k8s.io/kubernetes/pkg/kubelet/config"

pkg/kubelet/pod/mirror_client.go

Lines changed: 49 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,11 @@ limitations under the License.
1717
package pod
1818

1919
import (
20-
"k8s.io/api/core/v1"
21-
"k8s.io/apimachinery/pkg/api/errors"
20+
"errors"
21+
"fmt"
22+
23+
v1 "k8s.io/api/core/v1"
24+
apierrors "k8s.io/apimachinery/pkg/api/errors"
2225
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
2326
"k8s.io/apimachinery/pkg/types"
2427
clientset "k8s.io/client-go/kubernetes"
@@ -39,16 +42,28 @@ type MirrorClient interface {
3942
DeleteMirrorPod(podFullName string, uid *types.UID) (bool, error)
4043
}
4144

45+
// nodeGetter is a subset a NodeLister, simplified for testing.
46+
type nodeGetter interface {
47+
// Get retrieves the Node for a given name.
48+
Get(name string) (*v1.Node, error)
49+
}
50+
4251
// basicMirrorClient is a functional MirrorClient. Mirror pods are stored in
4352
// the kubelet directly because they need to be in sync with the internal
4453
// pods.
4554
type basicMirrorClient struct {
4655
apiserverClient clientset.Interface
56+
nodeGetter nodeGetter
57+
nodeName string
4758
}
4859

4960
// NewBasicMirrorClient returns a new MirrorClient.
50-
func NewBasicMirrorClient(apiserverClient clientset.Interface) MirrorClient {
51-
return &basicMirrorClient{apiserverClient: apiserverClient}
61+
func NewBasicMirrorClient(apiserverClient clientset.Interface, nodeName string, nodeGetter nodeGetter) MirrorClient {
62+
return &basicMirrorClient{
63+
apiserverClient: apiserverClient,
64+
nodeName: nodeName,
65+
nodeGetter: nodeGetter,
66+
}
5267
}
5368

5469
func (mc *basicMirrorClient) CreateMirrorPod(pod *v1.Pod) error {
@@ -64,8 +79,25 @@ func (mc *basicMirrorClient) CreateMirrorPod(pod *v1.Pod) error {
6479
}
6580
hash := getPodHash(pod)
6681
copyPod.Annotations[kubetypes.ConfigMirrorAnnotationKey] = hash
82+
83+
// With the MirrorPodNodeRestriction feature, mirror pods are required to have an owner reference
84+
// to the owning node.
85+
// See http://git.k8s.io/enhancements/keps/sig-auth/20190916-noderestriction-pods.md
86+
nodeUID, err := mc.getNodeUID()
87+
if err != nil {
88+
return fmt.Errorf("failed to get node UID: %v", err)
89+
}
90+
controller := true
91+
copyPod.OwnerReferences = []metav1.OwnerReference{{
92+
APIVersion: v1.SchemeGroupVersion.String(),
93+
Kind: "Node",
94+
Name: mc.nodeName,
95+
UID: nodeUID,
96+
Controller: &controller,
97+
}}
98+
6799
apiPod, err := mc.apiserverClient.CoreV1().Pods(copyPod.Namespace).Create(&copyPod)
68-
if err != nil && errors.IsAlreadyExists(err) {
100+
if err != nil && apierrors.IsAlreadyExists(err) {
69101
// Check if the existing pod is the same as the pod we want to create.
70102
if h, ok := apiPod.Annotations[kubetypes.ConfigMirrorAnnotationKey]; ok && h == hash {
71103
return nil
@@ -94,7 +126,7 @@ func (mc *basicMirrorClient) DeleteMirrorPod(podFullName string, uid *types.UID)
94126
var GracePeriodSeconds int64
95127
if err := mc.apiserverClient.CoreV1().Pods(namespace).Delete(name, &metav1.DeleteOptions{GracePeriodSeconds: &GracePeriodSeconds, Preconditions: &metav1.Preconditions{UID: uid}}); err != nil {
96128
// Unfortunately, there's no generic error for failing a precondition
97-
if !(errors.IsNotFound(err) || errors.IsConflict(err)) {
129+
if !(apierrors.IsNotFound(err) || apierrors.IsConflict(err)) {
98130
// We should return the error here, but historically this routine does
99131
// not return an error unless it can't parse the pod name
100132
klog.Errorf("Failed deleting a mirror pod %q: %v", podFullName, err)
@@ -104,6 +136,17 @@ func (mc *basicMirrorClient) DeleteMirrorPod(podFullName string, uid *types.UID)
104136
return true, nil
105137
}
106138

139+
func (mc *basicMirrorClient) getNodeUID() (types.UID, error) {
140+
node, err := mc.nodeGetter.Get(mc.nodeName)
141+
if err != nil {
142+
return "", err
143+
}
144+
if node.UID == "" {
145+
return "", errors.New("node UID unset")
146+
}
147+
return node.UID, nil
148+
}
149+
107150
// IsStaticPod returns true if the passed Pod is static.
108151
func IsStaticPod(pod *v1.Pod) bool {
109152
source, err := kubetypes.GetPodSource(pod)

pkg/kubelet/pod/mirror_client_test.go

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,18 @@ limitations under the License.
1717
package pod
1818

1919
import (
20+
"errors"
2021
"testing"
2122

23+
"github.com/stretchr/testify/assert"
24+
"github.com/stretchr/testify/require"
25+
v1 "k8s.io/api/core/v1"
26+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
27+
"k8s.io/apimachinery/pkg/types"
28+
"k8s.io/client-go/kubernetes/fake"
2229
kubecontainer "k8s.io/kubernetes/pkg/kubelet/container"
30+
kubetypes "k8s.io/kubernetes/pkg/kubelet/types"
31+
"k8s.io/utils/pointer"
2332
)
2433

2534
func TestParsePodFullName(t *testing.T) {
@@ -52,3 +61,97 @@ func TestParsePodFullName(t *testing.T) {
5261
}
5362
}
5463
}
64+
65+
func TestCreateMirrorPod(t *testing.T) {
66+
const (
67+
testNodeName = "test-node-name"
68+
testNodeUID = types.UID("test-node-uid-1234")
69+
testPodName = "test-pod-name"
70+
testPodNS = "test-pod-ns"
71+
testPodHash = "123456789"
72+
)
73+
testcases := []struct {
74+
desc string
75+
node *v1.Node
76+
nodeErr error
77+
expectSuccess bool
78+
}{{
79+
desc: "cannot get node",
80+
nodeErr: errors.New("expected: cannot get node"),
81+
expectSuccess: false,
82+
}, {
83+
desc: "node missing UID",
84+
node: &v1.Node{
85+
ObjectMeta: metav1.ObjectMeta{
86+
Name: testNodeName,
87+
},
88+
},
89+
expectSuccess: false,
90+
}, {
91+
desc: "successfully fetched node",
92+
node: &v1.Node{
93+
ObjectMeta: metav1.ObjectMeta{
94+
Name: testNodeName,
95+
UID: testNodeUID,
96+
},
97+
},
98+
expectSuccess: true,
99+
}}
100+
101+
for _, test := range testcases {
102+
t.Run(test.desc, func(t *testing.T) {
103+
clientset := fake.NewSimpleClientset()
104+
nodeGetter := &fakeNodeGetter{
105+
t: t,
106+
expectNodeName: testNodeName,
107+
node: test.node,
108+
err: test.nodeErr,
109+
}
110+
mc := NewBasicMirrorClient(clientset, testNodeName, nodeGetter)
111+
112+
pod := &v1.Pod{
113+
ObjectMeta: metav1.ObjectMeta{
114+
Name: testPodName,
115+
Namespace: testPodNS,
116+
Annotations: map[string]string{
117+
kubetypes.ConfigHashAnnotationKey: testPodHash,
118+
},
119+
},
120+
}
121+
122+
err := mc.CreateMirrorPod(pod)
123+
if !test.expectSuccess {
124+
assert.Error(t, err)
125+
return
126+
}
127+
128+
createdPod, err := clientset.CoreV1().Pods(testPodNS).Get(testPodName, metav1.GetOptions{})
129+
require.NoError(t, err)
130+
131+
// Validate created pod
132+
assert.Equal(t, testPodHash, createdPod.Annotations[kubetypes.ConfigMirrorAnnotationKey])
133+
assert.Len(t, createdPod.OwnerReferences, 1)
134+
expectedOwnerRef := metav1.OwnerReference{
135+
APIVersion: "v1",
136+
Kind: "Node",
137+
Name: testNodeName,
138+
UID: testNodeUID,
139+
Controller: pointer.BoolPtr(true),
140+
}
141+
assert.Equal(t, expectedOwnerRef, createdPod.OwnerReferences[0])
142+
})
143+
}
144+
}
145+
146+
type fakeNodeGetter struct {
147+
t *testing.T
148+
expectNodeName string
149+
150+
node *v1.Node
151+
err error
152+
}
153+
154+
func (f *fakeNodeGetter) Get(nodeName string) (*v1.Node, error) {
155+
require.Equal(f.t, f.expectNodeName, nodeName)
156+
return f.node, f.err
157+
}

0 commit comments

Comments
 (0)