implementation

This commit is contained in:
AxeZhan 2023-08-18 14:09:41 +08:00
parent fa01dfdb0a
commit 3a96afdfef
3 changed files with 137 additions and 0 deletions

View File

@ -558,6 +558,64 @@ func dropDisabledFields(
} }
// For other types of containers, validateContainers will handle them. // For other types of containers, validateContainers will handle them.
} }
if !utilfeature.DefaultFeatureGate.Enabled(features.PodLifecycleSleepAction) && !podLifecycleSleepActionInUse(oldPodSpec) {
for i := range podSpec.Containers {
if podSpec.Containers[i].Lifecycle == nil {
continue
}
if podSpec.Containers[i].Lifecycle.PreStop != nil {
podSpec.Containers[i].Lifecycle.PreStop.Sleep = nil
}
if podSpec.Containers[i].Lifecycle.PostStart != nil {
podSpec.Containers[i].Lifecycle.PostStart.Sleep = nil
}
}
for i := range podSpec.InitContainers {
if podSpec.InitContainers[i].Lifecycle == nil {
continue
}
if podSpec.InitContainers[i].Lifecycle.PreStop != nil {
podSpec.InitContainers[i].Lifecycle.PreStop.Sleep = nil
}
if podSpec.InitContainers[i].Lifecycle.PostStart != nil {
podSpec.InitContainers[i].Lifecycle.PostStart.Sleep = nil
}
}
for i := range podSpec.EphemeralContainers {
if podSpec.EphemeralContainers[i].Lifecycle == nil {
continue
}
if podSpec.EphemeralContainers[i].Lifecycle.PreStop != nil {
podSpec.EphemeralContainers[i].Lifecycle.PreStop.Sleep = nil
}
if podSpec.EphemeralContainers[i].Lifecycle.PostStart != nil {
podSpec.EphemeralContainers[i].Lifecycle.PostStart.Sleep = nil
}
}
}
}
func podLifecycleSleepActionInUse(podSpec *api.PodSpec) bool {
if podSpec == nil {
return false
}
var inUse bool
VisitContainers(podSpec, AllContainers, func(c *api.Container, containerType ContainerType) bool {
if c.Lifecycle == nil {
return true
}
if c.Lifecycle.PreStop != nil && c.Lifecycle.PreStop.Sleep != nil {
inUse = true
return false
}
if c.Lifecycle.PostStart != nil && c.Lifecycle.PostStart.Sleep != nil {
inUse = true
return false
}
return true
})
return inUse
} }
// dropDisabledPodStatusFields removes disabled fields from the pod status // dropDisabledPodStatusFields removes disabled fields from the pod status

View File

@ -26,6 +26,7 @@ import (
"net/url" "net/url"
"strconv" "strconv"
"strings" "strings"
"time"
v1 "k8s.io/api/core/v1" v1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/types"
@ -86,6 +87,14 @@ func (hr *handlerRunner) Run(ctx context.Context, containerID kubecontainer.Cont
klog.V(1).ErrorS(err, "HTTP lifecycle hook for Container in Pod failed", "path", handler.HTTPGet.Path, "containerName", container.Name, "pod", klog.KObj(pod)) klog.V(1).ErrorS(err, "HTTP lifecycle hook for Container in Pod failed", "path", handler.HTTPGet.Path, "containerName", container.Name, "pod", klog.KObj(pod))
} }
return msg, err return msg, err
case handler.Sleep != nil:
err := hr.runSleepHandler(ctx, handler.Sleep.Seconds)
var msg string
if err != nil {
msg = fmt.Sprintf("Sleep lifecycle hook (%d) for Container %q in Pod %q failed - error: %v", handler.Sleep.Seconds, container.Name, format.Pod(pod), err)
klog.V(1).ErrorS(err, "Sleep lifecycle hook for Container in Pod failed", "sleepSeconds", handler.Sleep.Seconds, "containerName", container.Name, "pod", klog.KObj(pod))
}
return msg, err
default: default:
err := fmt.Errorf("invalid handler: %v", handler) err := fmt.Errorf("invalid handler: %v", handler)
msg := fmt.Sprintf("Cannot run handler: %v", err) msg := fmt.Sprintf("Cannot run handler: %v", err)
@ -117,6 +126,20 @@ func resolvePort(portReference intstr.IntOrString, container *v1.Container) (int
return -1, fmt.Errorf("couldn't find port: %v in %v", portReference, container) return -1, fmt.Errorf("couldn't find port: %v in %v", portReference, container)
} }
func (hr *handlerRunner) runSleepHandler(ctx context.Context, seconds int64) error {
if !utilfeature.DefaultFeatureGate.Enabled(features.PodLifecycleSleepAction) {
return nil
}
c := time.After(time.Duration(seconds) * time.Second)
select {
case <-ctx.Done():
// unexpected termination
return fmt.Errorf("container terminated before sleep hook finished")
case <-c:
return nil
}
}
func (hr *handlerRunner) runHTTPHandler(ctx context.Context, pod *v1.Pod, container *v1.Container, handler *v1.LifecycleHandler, eventRecorder record.EventRecorder) error { func (hr *handlerRunner) runHTTPHandler(ctx context.Context, pod *v1.Pod, container *v1.Container, handler *v1.LifecycleHandler, eventRecorder record.EventRecorder) error {
host := handler.HTTPGet.Host host := handler.HTTPGet.Host
podIP := host podIP := host

View File

@ -859,3 +859,59 @@ func TestIsHTTPResponseError(t *testing.T) {
t.Errorf("unexpected http response error: %v", err) t.Errorf("unexpected http response error: %v", err)
} }
} }
func TestRunSleepHandler(t *testing.T) {
handlerRunner := NewHandlerRunner(&fakeHTTP{}, &fakeContainerCommandRunner{}, nil, nil)
containerID := kubecontainer.ContainerID{Type: "test", ID: "abc1234"}
containerName := "containerFoo"
container := v1.Container{
Name: containerName,
Lifecycle: &v1.Lifecycle{
PreStop: &v1.LifecycleHandler{},
},
}
pod := v1.Pod{}
pod.ObjectMeta.Name = "podFoo"
pod.ObjectMeta.Namespace = "nsFoo"
pod.Spec.Containers = []v1.Container{container}
tests := []struct {
name string
sleepSeconds int64
terminationGracePeriodSeconds int64
expectErr bool
expectedErr string
}{
{
name: "valid seconds",
sleepSeconds: 5,
terminationGracePeriodSeconds: 30,
},
{
name: "longer than TerminationGracePeriodSeconds",
sleepSeconds: 3,
terminationGracePeriodSeconds: 2,
expectErr: true,
expectedErr: "container terminated before sleep hook finished",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.PodLifecycleSleepAction, true)()
pod.Spec.Containers[0].Lifecycle.PreStop.Sleep = &v1.SleepAction{Seconds: tt.sleepSeconds}
ctx, cancel := context.WithTimeout(context.Background(), time.Duration(tt.terminationGracePeriodSeconds)*time.Second)
defer cancel()
_, err := handlerRunner.Run(ctx, containerID, &pod, &container, container.Lifecycle.PreStop)
if !tt.expectErr && err != nil {
t.Errorf("unexpected success")
}
if tt.expectErr && err.Error() != tt.expectedErr {
t.Errorf("%s: expected error want %s, got %s", tt.name, tt.expectedErr, err.Error())
}
})
}
}