From a4d8f74c7d8b7963845a63bcc1117c416d019450 Mon Sep 17 00:00:00 2001 From: Jeremy Rickard Date: Mon, 30 Jul 2018 14:38:36 -0600 Subject: [PATCH] Enabling Liveness and Readiness Probes in ACI Provider (#280) * Enabling Liveness and Readiness Probes in ACI Provider * Adding a check to ensure both exec and httpGet are not provided --- providers/azure/aci.go | 56 ++++++++++++++++ providers/azure/aci_test.go | 125 ++++++++++++++++++++++++++++++++++++ 2 files changed, 181 insertions(+) diff --git a/providers/azure/aci.go b/providers/azure/aci.go index fc13c318e..1884f94fc 100644 --- a/providers/azure/aci.go +++ b/providers/azure/aci.go @@ -722,11 +722,67 @@ func (p *ACIProvider) getContainers(pod *v1.Pod) ([]aci.Container, error) { } } + if container.LivenessProbe != nil { + probe, err := getProbe(container.LivenessProbe) + if err != nil { + return nil, err + } + c.LivenessProbe = probe + } + + if container.ReadinessProbe != nil { + probe, err := getProbe(container.ReadinessProbe) + if err != nil { + return nil, err + } + c.ReadinessProbe = probe + } + containers = append(containers, c) } return containers, nil } +func getProbe(probe *v1.Probe) (*aci.ContainerProbe, error) { + + if probe.Handler.Exec != nil && probe.Handler.HTTPGet != nil { + return nil, fmt.Errorf("probe may not specify more than one of \"exec\" and \"httpGet\"") + } + + if probe.Handler.Exec == nil && probe.Handler.HTTPGet == nil { + return nil, fmt.Errorf("probe must specify one of \"exec\" and \"httpGet\"") + } + + // Probes have can have a Exec or HTTP Get Handler. + // Create those if they exist, then add to the + // ContainerProbe struct + var exec *aci.ContainerExecProbe + if probe.Handler.Exec != nil { + exec = &aci.ContainerExecProbe{ + Command: probe.Handler.Exec.Command, + } + } + + var httpGET *aci.ContainerHTTPGetProbe + if probe.Handler.HTTPGet != nil { + httpGET = &aci.ContainerHTTPGetProbe{ + Port: probe.Handler.HTTPGet.Port.IntValue(), + Path: probe.Handler.HTTPGet.Path, + Scheme: string(probe.Handler.HTTPGet.Scheme), + } + } + + return &aci.ContainerProbe{ + Exec: exec, + HTTPGet: httpGET, + InitialDelaySeconds: probe.InitialDelaySeconds, + Period: probe.PeriodSeconds, + FailureThreshold: probe.FailureThreshold, + SuccessThreshold: probe.SuccessThreshold, + TimeoutSeconds: probe.TimeoutSeconds, + }, nil +} + func (p *ACIProvider) getVolumes(pod *v1.Pod) ([]aci.Volume, error) { volumes := make([]aci.Volume, 0, len(pod.Spec.Volumes)) for _, v := range pod.Spec.Volumes { diff --git a/providers/azure/aci_test.go b/providers/azure/aci_test.go index a71e5101e..6a030c219 100644 --- a/providers/azure/aci_test.go +++ b/providers/azure/aci_test.go @@ -20,6 +20,7 @@ import ( "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/resource" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/util/intstr" "k8s.io/client-go/kubernetes/fake" ) @@ -460,3 +461,127 @@ func prepareMocks() (*AADMock, *ACIMock, *ACIProvider, error) { func ptrQuantity(q resource.Quantity) *resource.Quantity { return &q } + +func TestCreatePodWithLivenessProbe(t *testing.T) { + _, aciServerMocker, provider, err := prepareMocks() + + if err != nil { + t.Fatal("Unable to prepare the mocks", err) + } + + podName := "pod-" + uuid.New().String() + podNamespace := "ns-" + uuid.New().String() + + aciServerMocker.OnCreate = func(subscription, resourceGroup, containerGroup string, cg *aci.ContainerGroup) (int, interface{}) { + assert.Equal(t, fakeSubscription, subscription, "Subscription doesn't match") + assert.Equal(t, fakeResourceGroup, resourceGroup, "Resource group doesn't match") + assert.NotNil(t, cg, "Container group is nil") + assert.Equal(t, podNamespace+"-"+podName, containerGroup, "Container group name is not expected") + assert.NotNil(t, cg.ContainerGroupProperties, "Container group properties should not be nil") + assert.NotNil(t, cg.ContainerGroupProperties.Containers, "Containers should not be nil") + assert.Equal(t, 1, len(cg.ContainerGroupProperties.Containers), "1 Container is expected") + assert.Equal(t, "nginx", cg.ContainerGroupProperties.Containers[0].Name, "Container nginx is expected") + assert.NotNil(t, cg.Containers[0].LivenessProbe, "Liveness probe expected") + assert.Equal(t, cg.Containers[0].LivenessProbe.InitialDelaySeconds, 10, "Initial Probe Delay doesn't match") + assert.Equal(t, cg.Containers[0].LivenessProbe.Period, 5, "Probe Period doesn't match") + assert.Equal(t, cg.Containers[0].LivenessProbe.TimeoutSeconds, 60, "Probe Timeout doesn't match") + assert.Equal(t, cg.Containers[0].LivenessProbe.SuccessThreshold, 3, "Probe Success Threshold doesn't match") + assert.Equal(t, cg.Containers[0].LivenessProbe.FailureThreshold, 5, "Probe Failure Threshold doesn't match") + assert.NotNil(t, cg.Containers[0].LivenessProbe.HTTPGet, "Expected an HTTP Get Probe") + + pod := &v1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Name: podName, + Namespace: podNamespace, + }, + Spec: v1.PodSpec{ + Containers: []v1.Container{ + v1.Container{ + Name: "nginx", + LivenessProbe: &v1.Probe{ + Handler: v1.Handler{ + HTTPGet: &v1.HTTPGetAction{ + Port: intstr.FromString("8080"), + Path: "/", + }, + }, + InitialDelaySeconds: 10, + PeriodSeconds: 5, + TimeoutSeconds: 60, + SuccessThreshold: 3, + FailureThreshold: 5, + }, + }, + }, + }, + } + + if err := provider.CreatePod(pod); err != nil { + t.Fatal("Failed to create pod", err) + } + + return http.StatusOK, cg + } +} + +func TestCreatePodWithReadinessProbe(t *testing.T) { + _, aciServerMocker, provider, err := prepareMocks() + + if err != nil { + t.Fatal("Unable to prepare the mocks", err) + } + + podName := "pod-" + uuid.New().String() + podNamespace := "ns-" + uuid.New().String() + + aciServerMocker.OnCreate = func(subscription, resourceGroup, containerGroup string, cg *aci.ContainerGroup) (int, interface{}) { + assert.Equal(t, fakeSubscription, subscription, "Subscription doesn't match") + assert.Equal(t, fakeResourceGroup, resourceGroup, "Resource group doesn't match") + assert.NotNil(t, cg, "Container group is nil") + assert.Equal(t, podNamespace+"-"+podName, containerGroup, "Container group name is not expected") + assert.NotNil(t, cg.ContainerGroupProperties, "Container group properties should not be nil") + assert.NotNil(t, cg.ContainerGroupProperties.Containers, "Containers should not be nil") + assert.Equal(t, 1, len(cg.ContainerGroupProperties.Containers), "1 Container is expected") + assert.Equal(t, "nginx", cg.ContainerGroupProperties.Containers[0].Name, "Container nginx is expected") + assert.NotNil(t, cg.Containers[0].ReadinessProbe, "Readiness probe expected") + assert.Equal(t, cg.Containers[0].ReadinessProbe.InitialDelaySeconds, 10, "Initial Probe Delay doesn't match") + assert.Equal(t, cg.Containers[0].ReadinessProbe.Period, 5, "Probe Period doesn't match") + assert.Equal(t, cg.Containers[0].ReadinessProbe.TimeoutSeconds, 60, "Probe Timeout doesn't match") + assert.Equal(t, cg.Containers[0].ReadinessProbe.SuccessThreshold, 3, "Probe Success Threshold doesn't match") + assert.Equal(t, cg.Containers[0].ReadinessProbe.FailureThreshold, 5, "Probe Failure Threshold doesn't match") + assert.NotNil(t, cg.Containers[0].ReadinessProbe.HTTPGet, "Expected an HTTP Get Probe") + + pod := &v1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Name: podName, + Namespace: podNamespace, + }, + Spec: v1.PodSpec{ + Containers: []v1.Container{ + v1.Container{ + Name: "nginx", + ReadinessProbe: &v1.Probe{ + Handler: v1.Handler{ + HTTPGet: &v1.HTTPGetAction{ + Port: intstr.FromString("8080"), + Path: "/", + }, + }, + InitialDelaySeconds: 10, + PeriodSeconds: 5, + TimeoutSeconds: 60, + SuccessThreshold: 3, + FailureThreshold: 5, + }, + }, + }, + }, + } + + if err := provider.CreatePod(pod); err != nil { + t.Fatal("Failed to create pod", err) + } + + return http.StatusOK, cg + } +}