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
2 changes: 0 additions & 2 deletions .goreleaser.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,6 @@ dockers_v2:
BUILD_ENV: "goreleaser"
flags:
- "--pull"
extra_files: [ "package/log.sh" ]
platforms:
- linux/amd64
- linux/arm64
Expand Down Expand Up @@ -184,7 +183,6 @@ dockers_v2:
- "--sbom=true"
- "--provenance=true"
- "--provenance=mode=max"
extra_files: [ "package/log.sh" ]
platforms:
- linux/amd64
- linux/arm64
Expand Down
1 change: 1 addition & 0 deletions charts/fleet-agent/templates/deployment.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ spec:
name: fleet-agent
command:
- fleetagent
args:
{{- if .Values.debug }}
- --debug
- --debug-level
Expand Down
3 changes: 3 additions & 0 deletions charts/fleet/templates/deployment.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,7 @@ spec:
{{- end }}
command:
- fleetcontroller
args:
{{- if $shard.id }}
- --shard-id
- {{ quote $shard.id }}
Expand Down Expand Up @@ -174,6 +175,7 @@ spec:
imagePullPolicy: "{{ $.Values.image.imagePullPolicy }}"
command:
- fleetcontroller
args:
- cleanup
{{- if $.Values.debug }}
- --debug
Expand Down Expand Up @@ -241,6 +243,7 @@ spec:
imagePullPolicy: "{{ $.Values.image.imagePullPolicy }}"
command:
- fleetcontroller
args:
- agentmanagement
{{- if not $.Values.bootstrap.enabled }}
- --disable-bootstrap
Expand Down
3 changes: 2 additions & 1 deletion charts/fleet/templates/deployment_gitjob.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,9 @@ spec:
- containerPort: 8081
name: metrics
{{- end }}
args:
command:
- fleetcontroller
args:
- gitjob
- --gitjob-image
- "{{ template "system_default_registry" $ }}{{ $.Values.image.repository }}:{{ $.Values.image.tag }}"
Expand Down
3 changes: 2 additions & 1 deletion charts/fleet/templates/deployment_helmops.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,9 @@ spec:
- containerPort: 8081
name: metrics
{{- end }}
args:
command:
- fleetcontroller
args:
- helmops
{{- if $.Values.debug }}
- --debug
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,8 @@ spec:
- ALL
readOnlyRootFilesystem: false
privileged: false
command:
- fleet
args:
- fleet
- cleanup
- clusterregistration
nodeSelector: {{ include "linux-node-selector" . | nindent 8 }}
Expand Down
3 changes: 1 addition & 2 deletions charts/fleet/templates/job_cleanup_gitrepojobs.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -37,9 +37,8 @@ spec:
- ALL
readOnlyRootFilesystem: false
privileged: false
command:
- fleet
args:
- fleet
- cleanup
- gitjob
nodeSelector: {{ include "linux-node-selector" . | nindent 12 }}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,8 @@ spec:
- ALL
readOnlyRootFilesystem: false
privileged: false
command:
- fleet
args:
- fleet
- migrate
- gitrepo-helm-url-regex
env:
Expand Down
102 changes: 69 additions & 33 deletions e2e/single-cluster/signals_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package singlecluster_test

import (
"encoding/json"
"strings"

. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
Expand All @@ -17,21 +18,24 @@ type containerLastTerminatedState struct {
// parseTerminatedState parses the JSON output from kubectl jsonpath query.
// Returns nil if the pod hasn't terminated yet (empty or null JSON output).
func parseTerminatedState(jsonOutput string) (*containerLastTerminatedState, error) {
if jsonOutput == "" || jsonOutput == "{}" {
trimmed := strings.TrimSpace(jsonOutput)
if trimmed == "" || trimmed == "{}" || trimmed == "null" || trimmed == "<no value>" {
return nil, nil
}
Comment thread
thardeck marked this conversation as resolved.

var state containerLastTerminatedState
err := json.Unmarshal([]byte(jsonOutput), &state)
err := json.Unmarshal([]byte(trimmed), &state)
if err != nil {
return nil, err
}
return &state, nil
}

// waitForPodReadyAndKill waits for a pod matching the given label selector to be ready,
// then sends SIGTERM to it.
func waitForPodReadyAndKill(k kubectl.Command, labels string) {
// waitForPodReadyAndTerminate waits for a pod matching the given label selector to be ready,
// then deletes it to trigger graceful pod termination (SIGTERM).
func waitForPodReadyAndTerminate(k kubectl.Command, labels string) string {
var pod string

// Wait for pod to exist and be ready before sending SIGTERM
Eventually(func(g Gomega) {
pods, err := k.Get("pod", "-l", labels, "-o", "jsonpath={.items[*].status.phase}")
Expand All @@ -40,19 +44,25 @@ func waitForPodReadyAndKill(k kubectl.Command, labels string) {
}).Should(Succeed())

Eventually(func(g Gomega) {
pod, err := k.Get("pod", "-l", labels, "-o", "jsonpath={.items[0].metadata.name}")
g.Expect(err).ToNot(HaveOccurred(), pod)
name, err := k.Get("pod", "-l", labels, "-o", `jsonpath={.items[?(@.status.phase=="Running")].metadata.name}`)
g.Expect(err).ToNot(HaveOccurred(), name)
runningPods := strings.Fields(name)
g.Expect(runningPods).ToNot(BeEmpty(), name)
pod = runningPods[0]

out, err := k.Run("exec", pod, "--", "kill", "1")
out, err := k.Run("delete", "pod", pod, "--wait=false")
g.Expect(err).ToNot(HaveOccurred(), out)
Comment thread
thardeck marked this conversation as resolved.
}).Should(Succeed())
Comment thread
thardeck marked this conversation as resolved.

return pod
}

var _ = Describe("Shuts down gracefully", func() {
var (
k kubectl.Command
ns string
labels string
pod string
)

When("the agent receives SIGTERM", func() {
Expand All @@ -62,23 +72,16 @@ var _ = Describe("Shuts down gracefully", func() {
})

JustBeforeEach(func() {
// Wait for deployment to exist and be ready before sending SIGTERM
Eventually(func(g Gomega) {
out, err := k.Get("deployment", "fleet-agent", "-o", "jsonpath={.status.readyReplicas}")
g.Expect(err).ToNot(HaveOccurred(), "deployment should exist")
g.Expect(out).ToNot(BeEmpty(), "deployment should have ready replicas")
}).Should(Succeed())

Eventually(func(g Gomega) {
out, err := k.Run("exec", "deploy/fleet-agent", "--", "kill", "1")
g.Expect(err).ToNot(HaveOccurred(), out)
}).Should(Succeed())
pod = waitForPodReadyAndTerminate(k, "app=fleet-agent")
})

It("exits gracefully", func() {
Eventually(func(g Gomega) {
out, err := k.Get("pod", "-l", "app=fleet-agent", "-o", "jsonpath={.items[0].status.containerStatuses[0].lastState.terminated}")
g.Expect(err).ToNot(HaveOccurred())
out, err := k.Get("pod", pod, "-o", "jsonpath={.status.containerStatuses[0].state.terminated}")
if err != nil {
g.Expect(strings.Contains(strings.ToLower(out), "notfound")).To(BeTrue(), out)
return
}
Comment thread
thardeck marked this conversation as resolved.

state, err := parseTerminatedState(out)
g.Expect(err).ToNot(HaveOccurred())
Expand All @@ -87,6 +90,12 @@ var _ = Describe("Shuts down gracefully", func() {
g.Expect(state.ExitCode).To(Equal(0))
g.Expect(state.Reason).To(Equal("Completed"))
}).Should(Succeed())

Eventually(func(g Gomega) {
pods, err := k.Get("pod", "-l", "app=fleet-agent", "-o", "jsonpath={.items[*].status.phase}")
g.Expect(err).ToNot(HaveOccurred(), pods)
g.Expect(pods).To(ContainSubstring("Running"), "replacement pod should be running")
}).Should(Succeed())
})
})

Expand All @@ -102,17 +111,20 @@ var _ = Describe("Shuts down gracefully", func() {
})

JustBeforeEach(func() {
waitForPodReadyAndKill(k, labels)
pod = waitForPodReadyAndTerminate(k, labels)
})

It("exits gracefully", func() {
Eventually(func(g Gomega) {
out, err := k.Get(
"pod",
"-l", labels,
"-o", `jsonpath={.items[0].status.containerStatuses[?(@.name=="fleet-controller")].lastState.terminated}`,
pod,
"-o", `jsonpath={.status.containerStatuses[?(@.name=="fleet-controller")].state.terminated}`,
)
g.Expect(err).ToNot(HaveOccurred())
if err != nil {
g.Expect(strings.Contains(strings.ToLower(out), "notfound")).To(BeTrue(), out)
return
}
Comment thread
thardeck marked this conversation as resolved.

state, err := parseTerminatedState(out)
g.Expect(err).ToNot(HaveOccurred(), out)
Expand All @@ -121,6 +133,12 @@ var _ = Describe("Shuts down gracefully", func() {
g.Expect(state.ExitCode).To(Equal(0))
g.Expect(state.Reason).To(Equal("Completed"))
}).Should(Succeed())

Eventually(func(g Gomega) {
pods, err := k.Get("pod", "-l", labels, "-o", "jsonpath={.items[*].status.phase}")
g.Expect(err).ToNot(HaveOccurred(), pods)
g.Expect(pods).To(ContainSubstring("Running"), "replacement pod should be running")
}).Should(Succeed())
})
})

Expand All @@ -132,17 +150,20 @@ var _ = Describe("Shuts down gracefully", func() {
})

JustBeforeEach(func() {
waitForPodReadyAndKill(k, labels)
pod = waitForPodReadyAndTerminate(k, labels)
})

It("exits gracefully", func() {
Eventually(func(g Gomega) {
out, err := k.Get(
"pod",
"-l", labels,
"-o", "jsonpath={.items[0].status.containerStatuses[0].lastState.terminated}",
pod,
"-o", "jsonpath={.status.containerStatuses[0].state.terminated}",
)
g.Expect(err).ToNot(HaveOccurred())
if err != nil {
g.Expect(strings.Contains(strings.ToLower(out), "notfound")).To(BeTrue(), out)
return
}
Comment thread
thardeck marked this conversation as resolved.

state, err := parseTerminatedState(out)
g.Expect(err).ToNot(HaveOccurred())
Expand All @@ -151,6 +172,12 @@ var _ = Describe("Shuts down gracefully", func() {
g.Expect(state.ExitCode).To(Equal(0))
g.Expect(state.Reason).To(Equal("Completed"))
}).Should(Succeed())

Eventually(func(g Gomega) {
pods, err := k.Get("pod", "-l", labels, "-o", "jsonpath={.items[*].status.phase}")
g.Expect(err).ToNot(HaveOccurred(), pods)
g.Expect(pods).To(ContainSubstring("Running"), "replacement pod should be running")
}).Should(Succeed())
})
})

Expand All @@ -162,17 +189,20 @@ var _ = Describe("Shuts down gracefully", func() {
})

JustBeforeEach(func() {
waitForPodReadyAndKill(k, labels)
pod = waitForPodReadyAndTerminate(k, labels)
})

It("exits gracefully", func() {
Eventually(func(g Gomega) {
out, err := k.Get(
"pod",
"-l", labels,
"-o", "jsonpath={.items[0].status.containerStatuses[0].lastState.terminated}",
pod,
"-o", "jsonpath={.status.containerStatuses[0].state.terminated}",
)
g.Expect(err).ToNot(HaveOccurred())
if err != nil {
g.Expect(strings.Contains(strings.ToLower(out), "notfound")).To(BeTrue(), out)
return
}
Comment thread
thardeck marked this conversation as resolved.

state, err := parseTerminatedState(out)
g.Expect(err).ToNot(HaveOccurred())
Expand All @@ -181,6 +211,12 @@ var _ = Describe("Shuts down gracefully", func() {
g.Expect(state.ExitCode).To(Equal(0))
g.Expect(state.Reason).To(Equal("Completed"))
}).Should(Succeed())

Eventually(func(g Gomega) {
pods, err := k.Get("pod", "-l", labels, "-o", "jsonpath={.items[*].status.phase}")
g.Expect(err).ToNot(HaveOccurred(), pods)
g.Expect(pods).To(ContainSubstring("Running"), "replacement pod should be running")
}).Should(Succeed())
})
})
})
Loading