Skip to content

Commit d5417ff

Browse files
committed
emit warning on no liveness probe defined for pods
This patch iterates through pod specs, listing specs with no liveness probes set. Any replication controllers whose deployment is fulfilled by a DeploymentConfig are ignored. **Example** ``` $ oc create -f pkg/api/graph/test/simple-deployment deploymentconfig "simple-deployment" created $ oc get all NAME REVISION DESIRED CURRENT TRIGGERED BY dc/simple-deployment 1 1 0 config NAME DESIRED CURRENT READY AGE rc/simple-deployment-1 0 0 0 1m NAME READY STATUS RESTARTS AGE po/simple-deployment-1-deploy 0/1 ContainerCreating 0 1m $ oc status -v In project test on server https://10.111.123.56:8443 dc/simple-deployment deploys docker.io/openshift/deployment-example:v1 deployment #1 pending 26 seconds ago Warnings: * pod/simple-deployment-1-deploy has no liveness probe to verify pods are still running. try: oc set probe pod/simple-deployment-1-deploy --liveness ... * dc/simple-deployment has no liveness probe to verify pods are still running. try: oc set probe dc/simple-deployment --liveness ... View details with 'oc describe <resource>/<name>' or list everything with 'oc get all'. ```
1 parent 8f6030a commit d5417ff

File tree

4 files changed

+130
-2
lines changed

4 files changed

+130
-2
lines changed

pkg/api/kubegraph/analysis/podspec.go

Lines changed: 86 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,16 @@ import (
88
osgraph "github.com/openshift/origin/pkg/api/graph"
99
kubeedges "github.com/openshift/origin/pkg/api/kubegraph"
1010
kubegraph "github.com/openshift/origin/pkg/api/kubegraph/nodes"
11+
kubenodes "github.com/openshift/origin/pkg/api/kubegraph/nodes"
12+
deploygraph "github.com/openshift/origin/pkg/deploy/graph"
13+
deploynodes "github.com/openshift/origin/pkg/deploy/graph/nodes"
14+
"k8s.io/kubernetes/pkg/util/sets"
1115
)
1216

1317
const (
14-
UnmountableSecretWarning = "UnmountableSecret"
15-
MissingSecretWarning = "MissingSecret"
18+
UnmountableSecretWarning = "UnmountableSecret"
19+
MissingSecretWarning = "MissingSecret"
20+
MissingLivenessProbeWarning = "MissingLivenessProbe"
1621
)
1722

1823
// FindUnmountableSecrets inspects all PodSpecs for any Secret reference that isn't listed as mountable by the referenced ServiceAccount
@@ -75,6 +80,65 @@ func FindMissingSecrets(g osgraph.Graph, f osgraph.Namer) []osgraph.Marker {
7580
return markers
7681
}
7782

83+
// FindMissingLivenessProbes inspects all PodSpecs for missing liveness probes and generates a list of non-duplicate markers
84+
func FindMissingLivenessProbes(g osgraph.Graph, f osgraph.Namer, setProbeCommand string) []osgraph.Marker {
85+
markers := []osgraph.Marker{}
86+
appendedNodes := sets.NewString()
87+
ignoredNodes := findDeploymentEdgeKinds(g)
88+
89+
for _, uncastPodSpecNode := range g.NodesByKind(kubegraph.PodSpecNodeKind) {
90+
podSpecNode := uncastPodSpecNode.(*kubegraph.PodSpecNode)
91+
podsWithoutLivenessProbes := CheckForLivenessProbes(g, podSpecNode)
92+
93+
topLevelNode := osgraph.GetTopLevelContainerNode(g, podSpecNode)
94+
topLevelString := f.ResourceName(topLevelNode)
95+
96+
// prevent duplicate markers
97+
if appendedNodes.Has(topLevelString) {
98+
continue
99+
}
100+
101+
// ignore replication controllers owned by DeploymentConfigs
102+
if isNodeInList(topLevelNode, ignoredNodes) {
103+
continue
104+
}
105+
106+
appendedNodes.Insert(topLevelString)
107+
for _, node := range podsWithoutLivenessProbes {
108+
markers = append(markers, osgraph.Marker{
109+
Node: podSpecNode,
110+
RelatedNodes: []graph.Node{node},
111+
112+
Severity: osgraph.WarningSeverity,
113+
Key: MissingLivenessProbeWarning,
114+
Message: fmt.Sprintf("%s has no liveness probe to verify pods are still running.",
115+
topLevelString),
116+
Suggestion: osgraph.Suggestion(fmt.Sprintf("%s %s --liveness ...", setProbeCommand, topLevelString)),
117+
})
118+
}
119+
}
120+
121+
return markers
122+
}
123+
124+
func CheckForLivenessProbes(g osgraph.Graph, podSpecNode *kubegraph.PodSpecNode) []*kubegraph.PodSpecNode {
125+
noLivenessProbes := []*kubegraph.PodSpecNode{}
126+
127+
hasLivenessProbe := false
128+
for _, container := range podSpecNode.PodSpec.Containers {
129+
if container.LivenessProbe != nil {
130+
hasLivenessProbe = true
131+
break
132+
}
133+
}
134+
135+
if !hasLivenessProbe {
136+
noLivenessProbes = append(noLivenessProbes, podSpecNode)
137+
}
138+
139+
return noLivenessProbes
140+
}
141+
78142
// CheckForUnmountableSecrets checks to be sure that all the referenced secrets are mountable (by service account)
79143
func CheckForUnmountableSecrets(g osgraph.Graph, podSpecNode *kubegraph.PodSpecNode) []*kubegraph.SecretNode {
80144
saNodes := g.SuccessorNodesByNodeAndEdgeKind(podSpecNode, kubegraph.ServiceAccountNodeKind, kubeedges.ReferencedServiceAccountEdgeKind)
@@ -122,3 +186,23 @@ func CheckMissingMountedSecrets(g osgraph.Graph, podSpecNode *kubegraph.PodSpecN
122186

123187
return missingSecrets
124188
}
189+
190+
// findDeploymentEdgeKinds returns all replication controller nodes
191+
// whose deployment is being fulfilled by a DeploymentConfig
192+
func findDeploymentEdgeKinds(g osgraph.Graph) []graph.Node {
193+
nodeFilter := osgraph.NodesOfKind(deploynodes.DeploymentConfigNodeKind)
194+
edgeFilter := osgraph.EdgesOfKind(deploygraph.DeploymentEdgeKind)
195+
196+
subGraph := g.Subgraph(nodeFilter, edgeFilter)
197+
return subGraph.NodesByKind(kubenodes.ReplicationControllerNodeKind)
198+
199+
}
200+
201+
func isNodeInList(node graph.Node, nodeList []graph.Node) bool {
202+
for _, n := range nodeList {
203+
if n == node {
204+
return true
205+
}
206+
}
207+
return false
208+
}

pkg/api/kubegraph/analysis/podspec_test.go

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,3 +80,23 @@ func TestUnmountableSecrets(t *testing.T) {
8080
t.Errorf("expected %v, got %v", expectedSecret2, markers)
8181
}
8282
}
83+
84+
func TestMissingLivenessProbes(t *testing.T) {
85+
g, _, err := osgraphtest.BuildGraph("../../../api/graph/test/simple-deployment.yaml")
86+
if err != nil {
87+
t.Fatalf("unexpected error: %v", err)
88+
}
89+
90+
kubeedges.AddAllExposedPodEdges(g)
91+
92+
markers := FindMissingLivenessProbes(g, osgraph.DefaultNamer, "oc set probe")
93+
if e, a := 1, len(markers); e != a {
94+
t.Fatalf("expected %v, got %v", e, a)
95+
}
96+
97+
actualDC := osgraph.GetTopLevelContainerNode(g, markers[0].Node)
98+
expectedDC := g.Find(osgraph.UniqueName("DeploymentConfig|/simple-deployment"))
99+
if e, a := expectedDC.ID(), actualDC.ID(); e != a {
100+
t.Errorf("expected %v, got %v", e, a)
101+
}
102+
}

pkg/cmd/cli/describe/projectstatus.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -383,6 +383,9 @@ func getMarkerScanners(logsCommandName, securityPolicyCommandFormat, setProbeCom
383383
func(g osgraph.Graph, f osgraph.Namer) []osgraph.Marker {
384384
return deployanalysis.FindDeploymentConfigReadinessWarnings(g, f, setProbeCommandName)
385385
},
386+
func(g osgraph.Graph, f osgraph.Namer) []osgraph.Marker {
387+
return kubeanalysis.FindMissingLivenessProbes(g, f, setProbeCommandName)
388+
},
386389
routeanalysis.FindPortMappingIssues,
387390
routeanalysis.FindMissingTLSTerminationType,
388391
routeanalysis.FindPathBasedPassthroughRoutes,

test/cmd/set-liveness-probe.sh

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
#!/bin/bash
2+
source "$(dirname "${BASH_SOURCE}")/../../hack/lib/init.sh"
3+
trap os::test::junit::reconcile_output EXIT
4+
5+
# Cleanup cluster resources created by this test
6+
(
7+
set +e
8+
oc delete all,templates --all
9+
exit 0
10+
) &>/dev/null
11+
12+
13+
os::test::junit::declare_suite_start "cmd/set-probe-liveness"
14+
# This test setting a liveness probe, without warning about replication controllers whose deployment depends on deployment configs
15+
os::cmd::expect_success_and_text 'oc create -f pkg/api/graph/test/simple-deployment.yaml' 'deploymentconfig "simple-deployment" created'
16+
os::cmd::expect_success_and_text 'oc status -v' 'dc/simple-deployment has no liveness probe'
17+
os::cmd::expect_success_and_not_text 'oc status -v' 'rc/simple-deployment-1 has no liveness probe'
18+
os::cmd::expect_success_and_text 'oc set probe dc/node --liveness --get-url=http://google.com:80' 'deploymentconfig "simple-deployment" updated'
19+
os::cmd::expect_success_and_not_text 'oc status -v' 'dc/simple-deployment has no liveness probe'
20+
echo "set-probe-liveness: ok"
21+
os::test::junit::declare_suite_end

0 commit comments

Comments
 (0)