diff --git a/pkg/analyze/analyzer.go b/pkg/analyze/analyzer.go index b7088a7a..fe4685f7 100644 --- a/pkg/analyze/analyzer.go +++ b/pkg/analyze/analyzer.go @@ -205,6 +205,20 @@ func Analyze(analyzer *troubleshootv1beta2.Analyze, getFile getCollectedFileCont } return results, nil } + if analyzer.JobStatus != nil { + isExcluded, err := isExcluded(analyzer.JobStatus.Exclude) + if err != nil { + return nil, err + } + if isExcluded { + return nil, nil + } + results, err := analyzeJobStatus(analyzer.JobStatus, findFiles) + if err != nil { + return nil, err + } + return results, nil + } if analyzer.ClusterPodStatuses != nil { isExcluded, err := isExcluded(analyzer.ClusterPodStatuses.Exclude) if err != nil { diff --git a/pkg/analyze/data_test.go b/pkg/analyze/data_test.go index b847c3c6..f25b4605 100644 --- a/pkg/analyze/data_test.go +++ b/pkg/analyze/data_test.go @@ -1,574 +1,14 @@ package analyzer -var collectedDeployments = `[ - { - "metadata": { - "name": "kotsadm-api", - "namespace": "default", - "selfLink": "/apis/apps/v1/namespaces/default/deployments/kotsadm-api", - "uid": "56526035-cd29-4d08-8375-291503b1a006", - "resourceVersion": "1583068", - "generation": 1, - "creationTimestamp": "2019-11-07T00:34:32Z", - "labels": { - "app.kubernetes.io/managed-by": "skaffold-v0.41.0" - }, - "annotations": { - "deployment.kubernetes.io/revision": "1", - "kubectl.kubernetes.io/last-applied-configuration": "{\"apiVersion\":\"apps/v1\",\"kind\":\"Deployment\",\"metadata\":{\"annotations\":{},\"labels\":{\"app.kubernetes.io/managed-by\":\"skaffold-v0.41.0\",\"skaffold.dev/builder\":\"local\",\"skaffold.dev/cleanup\":\"true\",\"skaffold.dev/deployer\":\"kustomize\",\"skaffold.dev/docker-api-version\":\"1.40\",\"skaffold.dev/run-id\":\"98f0a02b-9739-4d94-ba11-3e4d273c743e\",\"skaffold.dev/tag-policy\":\"git-commit\",\"skaffold.dev/tail\":\"true\"},\"name\":\"kotsadm-api\",\"namespace\":\"default\"},\"spec\":{\"selector\":{\"matchLabels\":{\"app\":\"kotsadm-api\"}},\"template\":{\"metadata\":{\"labels\":{\"app\":\"kotsadm-api\",\"app.kubernetes.io/managed-by\":\"skaffold-v0.41.0\",\"skaffold.dev/builder\":\"local\",\"skaffold.dev/cleanup\":\"true\",\"skaffold.dev/deployer\":\"kustomize\",\"skaffold.dev/docker-api-version\":\"1.40\",\"skaffold.dev/run-id\":\"98f0a02b-9739-4d94-ba11-3e4d273c743e\",\"skaffold.dev/tag-policy\":\"git-commit\",\"skaffold.dev/tail\":\"true\"}},\"spec\":{\"affinity\":{\"podAffinity\":{\"preferredDuringSchedulingIgnoredDuringExecution\":[{\"podAffinityTerm\":{\"labelSelector\":{\"matchExpressions\":[{\"key\":\"app\",\"operator\":\"In\",\"values\":[\"ship-www\"]}]},\"topologyKey\":\"kubernetes.io/hostname\"},\"weight\":1}]},\"podAntiAffinity\":{\"preferredDuringSchedulingIgnoredDuringExecution\":[{\"podAffinityTerm\":{\"labelSelector\":{\"matchExpressions\":[{\"key\":\"app\",\"operator\":\"In\",\"values\":[\"kotsadm-api\"]}]},\"topologyKey\":\"kubernetes.io/hostname\"},\"weight\":2}]}},\"containers\":[{\"env\":[{\"name\":\"DEV_NAMESPACE\",\"value\":\"test\"},{\"name\":\"LOG_LEVEL\",\"value\":\"debug\"},{\"name\":\"SESSION_KEY\",\"valueFrom\":{\"secretKeyRef\":{\"key\":\"key\",\"name\":\"session\"}}},{\"name\":\"POSTGRES_URI\",\"valueFrom\":{\"secretKeyRef\":{\"key\":\"uri\",\"name\":\"ship-postgres\"}}},{\"name\":\"API_ENCRYPTION_KEY\",\"value\":\"IvWItkB8+ezMisPjSMBknT1PdKjBx7Xc/txZqOP8Y2Oe7+Jy\"},{\"name\":\"INIT_SERVER_URI\",\"value\":\"http://init-server:3000\"},{\"name\":\"WATCH_SERVER_URI\",\"value\":\"http://watch-server:3000\"},{\"name\":\"PINO_LOG_PRETTY\",\"value\":\"1\"},{\"name\":\"S3_BUCKET_NAME\",\"value\":\"shipbucket\"},{\"name\":\"AIRGAP_BUNDLE_S3_BUCKET\",\"value\":\"airgap\"},{\"name\":\"S3_ENDPOINT\",\"value\":\"http://kotsadm-s3.default.svc.cluster.local:4569/\"},{\"name\":\"S3_ACCESS_KEY_ID\",\"value\":\"***HIDDEN***\"},{\"name\":\"S3_SECRET_ACCESS_KEY\",\"value\":\"***HIDDEN***\"},{\"name\":\"S3_BUCKET_ENDPOINT\",\"value\":\"true\"},{\"name\":\"GITHUB_CLIENT_ID\",\"valueFrom\":{\"secretKeyRef\":{\"key\":\"client-id\",\"name\":\"github-app\"}}},{\"name\":\"GITHUB_CLIENT_SECRET\",\"valueFrom\":{\"secretKeyRef\":{\"key\":\"client-secret\",\"name\":\"github-app\"}}},{\"name\":\"GITHUB_INTEGRATION_ID\",\"valueFrom\":{\"secretKeyRef\":{\"key\":\"integration-id\",\"name\":\"github-app\"}}},{\"name\":\"GITHUB_PRIVATE_KEY_FILE\",\"value\":\"/keys/github/private-key.pem\"},{\"name\":\"SHIP_API_ENDPOINT\",\"value\":\"http://kotsadm-api.default.svc.cluster.local:3000\"},{\"name\":\"SHIP_API_ADVERTISE_ENDPOINT\",\"value\":\"http://localhost:30065\"},{\"name\":\"GRAPHQL_PREM_ENDPOINT\",\"value\":\"http://graphql-api-prem:3000/graphql\"},{\"name\":\"AUTO_CREATE_CLUSTER\",\"value\":\"1\"},{\"name\":\"AUTO_CREATE_CLUSTER_NAME\",\"value\":\"microk8s\"},{\"name\":\"AUTO_CREATE_CLUSTER_TOKEN\",\"value\":\"***HIDDEN***\"},{\"name\":\"ENABLE_SHIP\",\"value\":\"1\"},{\"name\":\"ENABLE_KOTS\",\"value\":\"1\"},{\"name\":\"ENABLE_KURL\",\"value\":\"1\"},{\"name\":\"POD_NAMESPACE\",\"valueFrom\":{\"fieldRef\":{\"fieldPath\":\"metadata.namespace\"}}}],\"image\":\"localhost:32000/kotsadm-api:v1.0.1-30-g8fa13e34-dirty@sha256:4a0ca1a2eae46472bd2d454f9e763a458ddd172689e179b17054262507bb4fc8\",\"imagePullPolicy\":\"IfNotPresent\",\"name\":\"kotsadm-api\",\"ports\":[{\"containerPort\":3000,\"name\":\"http\"},{\"containerPort\":9229,\"name\":\"debug\"}],\"readinessProbe\":{\"httpGet\":{\"path\":\"/healthz\",\"port\":3000},\"initialDelaySeconds\":2,\"periodSeconds\":2},\"volumeMounts\":[{\"mountPath\":\"/keys/github\",\"name\":\"github-app-private-key\",\"readOnly\":true}]}],\"restartPolicy\":\"Always\",\"securityContext\":{\"runAsUser\":0},\"serviceAccount\":\"kotsadm-api\",\"volumes\":[{\"name\":\"github-app-private-key\",\"secret\":{\"secretName\":\"github-app-private-key\"}}]}}}}\n" - } - }, - "spec": { - "replicas": 1, - "selector": { - "matchLabels": { - "app": "kotsadm-api" - } - }, - "template": { - "metadata": { - "creationTimestamp": null, - "labels": { - "app": "kotsadm-api" - } - }, - "spec": { - "containers": [ - { - "name": "kotsadm-api", - "image": "localhost:32000/kotsadm-api:v1.0.1-30-g8fa13e34-dirty@sha256:4a0ca1a2eae46472bd2d454f9e763a458ddd172689e179b17054262507bb4fc8", - "ports": [ - { - "name": "http", - "containerPort": 3000, - "protocol": "TCP" - } - ], - "env": [ - { - "name": "DEV_NAMESPACE", - "value": "test" - }, - { - "name": "POD_NAMESPACE", - "valueFrom": { - "fieldRef": { - "apiVersion": "v1", - "fieldPath": "metadata.namespace" - } - } - } - ], - "resources": {}, - "readinessProbe": { - "httpGet": { - "path": "/healthz", - "port": 3000, - "scheme": "HTTP" - }, - "initialDelaySeconds": 2, - "timeoutSeconds": 1, - "periodSeconds": 2, - "successThreshold": 1, - "failureThreshold": 3 - }, - "terminationMessagePath": "/dev/termination-log", - "terminationMessagePolicy": "File", - "imagePullPolicy": "IfNotPresent" - } - ], - "restartPolicy": "Always", - "terminationGracePeriodSeconds": 30, - "dnsPolicy": "ClusterFirst", - "serviceAccountName": "kotsadm-api", - "serviceAccount": "kotsadm-api", - "schedulerName": "default-scheduler" - } - }, - "strategy": { - "type": "RollingUpdate", - "rollingUpdate": { - "maxUnavailable": "25%", - "maxSurge": "25%" - } - }, - "revisionHistoryLimit": 10, - "progressDeadlineSeconds": 600 - }, - "status": { - "observedGeneration": 1, - "replicas": 1, - "updatedReplicas": 1, - "readyReplicas": 1, - "availableReplicas": 1, - "conditions": [ - { - "type": "Available", - "status": "True", - "lastUpdateTime": "2019-11-07T00:35:23Z", - "lastTransitionTime": "2019-11-07T00:35:23Z", - "reason": "MinimumReplicasAvailable", - "message": "Deployment has minimum availability." - }, - { - "type": "Progressing", - "status": "True", - "lastUpdateTime": "2019-11-07T00:35:23Z", - "lastTransitionTime": "2019-11-07T00:34:32Z", - "reason": "NewReplicaSetAvailable", - "message": "ReplicaSet \"kotsadm-api-6f4b994bd5\" has successfully progressed." - } - ] - } - }, - { - "metadata": { - "name": "kotsadm-operator", - "namespace": "default", - "selfLink": "/apis/apps/v1/namespaces/default/deployments/kotsadm-operator", - "uid": "cfae9877-eef4-44c9-acac-0bf0d1aa547e", - "resourceVersion": "1583379", - "generation": 2, - "creationTimestamp": "2019-11-07T00:34:32Z", - "labels": { - "app.kubernetes.io/managed-by": "skaffold-v0.41.0" - }, - "annotations": { - "deployment.kubernetes.io/revision": "2", - "kubectl.kubernetes.io/last-applied-configuration": "{\"apiVersion\":\"apps/v1\",\"kind\":\"Deployment\",\"metadata\":{\"annotations\":{},\"labels\":{\"app.kubernetes.io/managed-by\":\"skaffold-v0.41.0\",\"skaffold.dev/builder\":\"local\",\"skaffold.dev/cleanup\":\"true\",\"skaffold.dev/deployer\":\"kustomize\",\"skaffold.dev/docker-api-version\":\"1.40\",\"skaffold.dev/run-id\":\"98f0a02b-9739-4d94-ba11-3e4d273c743e\",\"skaffold.dev/tag-policy\":\"git-commit\",\"skaffold.dev/tail\":\"true\"},\"name\":\"kotsadm-operator\",\"namespace\":\"default\"},\"spec\":{\"selector\":{\"matchLabels\":{\"app\":\"kotsadm-operator\"}},\"template\":{\"metadata\":{\"labels\":{\"app\":\"kotsadm-operator\",\"app.kubernetes.io/managed-by\":\"skaffold-v0.41.0\",\"skaffold.dev/builder\":\"local\",\"skaffold.dev/cleanup\":\"true\",\"skaffold.dev/deployer\":\"kustomize\",\"skaffold.dev/docker-api-version\":\"1.40\",\"skaffold.dev/run-id\":\"98f0a02b-9739-4d94-ba11-3e4d273c743e\",\"skaffold.dev/tag-policy\":\"git-commit\",\"skaffold.dev/tail\":\"true\"}},\"spec\":{\"containers\":[{\"env\":[{\"name\":\"KOTSADM_API_ENDPOINT\",\"value\":\"http://kotsadm-api:3000\"},{\"name\":\"KOTSADM_TOKEN\",\"value\":\"***HIDDEN***\"},{\"name\":\"KOTSADM_TARGET_NAMESPACE\",\"value\":\"test\"}],\"image\":\"localhost:32000/kotsadm-operator:v1.0.1-30-g8fa13e34-dirty@sha256:177c15b6399717048e9355bc8fd8b8ed213be90615c7c6ee7b7fdcee50aca6c5\",\"imagePullPolicy\":\"Always\",\"name\":\"kotsadm-operator\",\"resources\":{\"limits\":{\"cpu\":\"200m\",\"memory\":\"1000Mi\"},\"requests\":{\"cpu\":\"100m\",\"memory\":\"500Mi\"}}}],\"restartPolicy\":\"Always\"}}}}\n" - } - }, - "spec": { - "replicas": 1, - "selector": { - "matchLabels": { - "app": "kotsadm-operator" - } - }, - "template": { - "metadata": { - "creationTimestamp": null, - "labels": { - "app": "kotsadm-operator" - } - }, - "spec": { - "containers": [ - { - "name": "kotsadm-operator", - "image": "localhost:32000/kotsadm-operator:v1.0.1-30-g8fa13e34-dirty@sha256:177c15b6399717048e9355bc8fd8b8ed213be90615c7c6ee7b7fdcee50aca6c5", - "env": [ - { - "name": "KOTSADM_API_ENDPOINT", - "value": "http://kotsadm-api:3000" - }, - { - "name": "KOTSADM_TOKEN", - "value": "***HIDDEN***" - }, - { - "name": "KOTSADM_TARGET_NAMESPACE", - "value": "test" - } - ], - "resources": { - "limits": { - "cpu": "200m", - "memory": "1000Mi" - }, - "requests": { - "cpu": "100m", - "memory": "500Mi" - } - }, - "terminationMessagePath": "/dev/termination-log", - "terminationMessagePolicy": "File", - "imagePullPolicy": "Always" - } - ], - "restartPolicy": "Always", - "terminationGracePeriodSeconds": 30, - "dnsPolicy": "ClusterFirst", - "securityContext": {}, - "schedulerName": "default-scheduler" - } - }, - "strategy": { - "type": "RollingUpdate", - "rollingUpdate": { - "maxUnavailable": "25%", - "maxSurge": "25%" - } - }, - "revisionHistoryLimit": 10, - "progressDeadlineSeconds": 600 - }, - "status": { - "observedGeneration": 2, - "replicas": 1, - "updatedReplicas": 1, - "readyReplicas": 1, - "availableReplicas": 1, - "conditions": [ - { - "type": "Available", - "status": "True", - "lastUpdateTime": "2019-11-07T00:34:37Z", - "lastTransitionTime": "2019-11-07T00:34:37Z", - "reason": "MinimumReplicasAvailable", - "message": "Deployment has minimum availability." - }, - { - "type": "Progressing", - "status": "True", - "lastUpdateTime": "2019-11-07T00:36:34Z", - "lastTransitionTime": "2019-11-07T00:34:32Z", - "reason": "NewReplicaSetAvailable", - "message": "ReplicaSet \"kotsadm-operator-5b5c977699\" has successfully progressed." - } - ] - } - }, - { - "metadata": { - "name": "kotsadm-postgres-watch", - "namespace": "default", - "selfLink": "/apis/apps/v1/namespaces/default/deployments/kotsadm-postgres-watch", - "uid": "ec195b07-4fd2-4bbe-8b01-4cbdfbe0e79d", - "resourceVersion": "1582762", - "generation": 1, - "creationTimestamp": "2019-11-07T00:34:39Z", - "annotations": { - "deployment.kubernetes.io/revision": "1" - }, - "ownerReferences": [ - { - "apiVersion": "databases.schemahero.io/v1alpha2", - "kind": "Database", - "name": "kotsadm-postgres", - "uid": "e6d51ce6-c4c1-428f-9e8e-56f1a3a43588", - "controller": true, - "blockOwnerDeletion": true - } - ] - }, - "spec": { - "replicas": 1, - "selector": { - "matchLabels": { - "deployment": "kotsadm-postgreswatch" - } - }, - "template": { - "metadata": { - "creationTimestamp": null, - "labels": { - "deployment": "kotsadm-postgreswatch" - } - }, - "spec": { - "containers": [ - { - "name": "schemahero", - "image": "schemahero/schemahero:alpha", - "args": [ - "watch", - "--driver", - "postgres", - "--uri", - "postgres://shipcloud:password@postgres.default.svc.cluster.local:5432/shipcloud?sslmode=disable", - "--namespace", - "default", - "--instance", - "kotsadm-postgres" - ], - "resources": {}, - "terminationMessagePath": "/dev/termination-log", - "terminationMessagePolicy": "File", - "imagePullPolicy": "Always" - } - ], - "restartPolicy": "Always", - "terminationGracePeriodSeconds": 30, - "dnsPolicy": "ClusterFirst", - "serviceAccountName": "kotsadm-postgres", - "serviceAccount": "kotsadm-postgres", - "securityContext": {}, - "schedulerName": "default-scheduler" - } - }, - "strategy": { - "type": "RollingUpdate", - "rollingUpdate": { - "maxUnavailable": "25%", - "maxSurge": "25%" - } - }, - "revisionHistoryLimit": 10, - "progressDeadlineSeconds": 600 - }, - "status": { - "observedGeneration": 1, - "replicas": 1, - "updatedReplicas": 1, - "readyReplicas": 1, - "availableReplicas": 1, - "conditions": [ - { - "type": "Available", - "status": "True", - "lastUpdateTime": "2019-11-07T00:35:00Z", - "lastTransitionTime": "2019-11-07T00:35:00Z", - "reason": "MinimumReplicasAvailable", - "message": "Deployment has minimum availability." - }, - { - "type": "Progressing", - "status": "True", - "lastUpdateTime": "2019-11-07T00:35:00Z", - "lastTransitionTime": "2019-11-07T00:34:39Z", - "reason": "NewReplicaSetAvailable", - "message": "ReplicaSet \"kotsadm-postgres-watch-5cf76f4c45\" has successfully progressed." - } - ] - } - }, - { - "metadata": { - "name": "kotsadm-web", - "namespace": "default", - "selfLink": "/apis/apps/v1/namespaces/default/deployments/kotsadm-web", - "uid": "4e657f8a-5edb-498b-9402-1f93f51f5dda", - "resourceVersion": "1582354", - "generation": 1, - "creationTimestamp": "2019-11-07T00:34:32Z", - "labels": { - "app.kubernetes.io/managed-by": "skaffold-v0.41.0", - "skaffold.dev/builder": "local", - "skaffold.dev/cleanup": "true", - "skaffold.dev/deployer": "kustomize", - "skaffold.dev/docker-api-version": "1.40", - "skaffold.dev/run-id": "98f0a02b-9739-4d94-ba11-3e4d273c743e", - "skaffold.dev/tag-policy": "git-commit", - "skaffold.dev/tail": "true" - }, - "annotations": { - "deployment.kubernetes.io/revision": "1", - "kubectl.kubernetes.io/last-applied-configuration": "{\"apiVersion\":\"apps/v1\",\"kind\":\"Deployment\",\"metadata\":{\"annotations\":{},\"labels\":{\"app.kubernetes.io/managed-by\":\"skaffold-v0.41.0\",\"skaffold.dev/builder\":\"local\",\"skaffold.dev/cleanup\":\"true\",\"skaffold.dev/deployer\":\"kustomize\",\"skaffold.dev/docker-api-version\":\"1.40\",\"skaffold.dev/run-id\":\"98f0a02b-9739-4d94-ba11-3e4d273c743e\",\"skaffold.dev/tag-policy\":\"git-commit\",\"skaffold.dev/tail\":\"true\"},\"name\":\"kotsadm-web\",\"namespace\":\"default\"},\"spec\":{\"selector\":{\"matchLabels\":{\"app\":\"kotsadm-web\"}},\"template\":{\"metadata\":{\"labels\":{\"app\":\"kotsadm-web\",\"app.kubernetes.io/managed-by\":\"skaffold-v0.41.0\",\"skaffold.dev/builder\":\"local\",\"skaffold.dev/cleanup\":\"true\",\"skaffold.dev/deployer\":\"kustomize\",\"skaffold.dev/docker-api-version\":\"1.40\",\"skaffold.dev/run-id\":\"98f0a02b-9739-4d94-ba11-3e4d273c743e\",\"skaffold.dev/tag-policy\":\"git-commit\",\"skaffold.dev/tail\":\"true\"}},\"spec\":{\"containers\":[{\"env\":[{\"name\":\"GITHUB_CLIENT_ID\",\"valueFrom\":{\"secretKeyRef\":{\"key\":\"client-id\",\"name\":\"github-app\"}}},{\"name\":\"GITHUB_INSTALL_URL\",\"valueFrom\":{\"secretKeyRef\":{\"key\":\"install-url\",\"name\":\"github-app\"}}},{\"name\":\"SHIP_CLUSTER_API_SERVER\",\"value\":\"http://localhost:30065\"},{\"name\":\"SHIP_CLUSTER_WEB_URI\",\"value\":\"http://localhost:8000\"}],\"image\":\"localhost:32000/kotsadm-web:v1.0.1-30-g8fa13e34@sha256:5b5b5b640b6e09d8b3185d4ae15ac4dc558d4e2ea034ac3e567d8cce04eadb9c\",\"imagePullPolicy\":\"IfNotPresent\",\"name\":\"kotsadm-web\",\"ports\":[{\"containerPort\":8000,\"name\":\"http\"}]}]}}}}\n" - } - }, - "spec": { - "replicas": 1, - "selector": { - "matchLabels": { - "app": "kotsadm-web" - } - }, - "template": { - "metadata": { - "creationTimestamp": null, - "labels": { - "app": "kotsadm-web", - "app.kubernetes.io/managed-by": "skaffold-v0.41.0", - "skaffold.dev/builder": "local", - "skaffold.dev/cleanup": "true", - "skaffold.dev/deployer": "kustomize", - "skaffold.dev/docker-api-version": "1.40", - "skaffold.dev/run-id": "98f0a02b-9739-4d94-ba11-3e4d273c743e", - "skaffold.dev/tag-policy": "git-commit", - "skaffold.dev/tail": "true" - } - }, - "spec": { - "containers": [ - { - "name": "kotsadm-web", - "image": "localhost:32000/kotsadm-web:v1.0.1-30-g8fa13e34@sha256:5b5b5b640b6e09d8b3185d4ae15ac4dc558d4e2ea034ac3e567d8cce04eadb9c", - "ports": [ - { - "name": "http", - "containerPort": 8000, - "protocol": "TCP" - } - ], - "env": [ - { - "name": "GITHUB_CLIENT_ID", - "valueFrom": { - "secretKeyRef": { - "name": "github-app", - "key": "client-id" - } - } - } - ], - "resources": {}, - "terminationMessagePath": "/dev/termination-log", - "terminationMessagePolicy": "File", - "imagePullPolicy": "IfNotPresent" - } - ], - "restartPolicy": "Always", - "terminationGracePeriodSeconds": 30, - "dnsPolicy": "ClusterFirst", - "securityContext": {}, - "schedulerName": "default-scheduler" - } - }, - "strategy": { - "type": "RollingUpdate", - "rollingUpdate": { - "maxUnavailable": "25%", - "maxSurge": "25%" - } - }, - "revisionHistoryLimit": 10, - "progressDeadlineSeconds": 600 - }, - "status": { - "observedGeneration": 1, - "replicas": 1, - "updatedReplicas": 1, - "readyReplicas": 1, - "availableReplicas": 1, - "conditions": [ - { - "type": "Available", - "status": "True", - "lastUpdateTime": "2019-11-07T00:34:39Z", - "lastTransitionTime": "2019-11-07T00:34:39Z", - "reason": "MinimumReplicasAvailable", - "message": "Deployment has minimum availability." - }, - { - "type": "Progressing", - "status": "True", - "lastUpdateTime": "2019-11-07T00:34:39Z", - "lastTransitionTime": "2019-11-07T00:34:32Z", - "reason": "NewReplicaSetAvailable", - "message": "ReplicaSet \"kotsadm-web-79bfb95c48\" has successfully progressed." - } - ] - } - } - ]` +import ( + _ "embed" +) -var collectedNodes = `[ - { - "apiVersion": "v1", - "kind": "Node", - "metadata": { - "annotations": { - "node.alpha.kubernetes.io/ttl": "0", - "volumes.kubernetes.io/controller-managed-attach-detach": "true" - }, - "creationTimestamp": "2019-10-23T18:16:43Z", - "labels": { - "beta.kubernetes.io/arch": "amd64", - "beta.kubernetes.io/os": "linux", - "kubernetes.io/arch": "amd64", - "kubernetes.io/hostname": "repldev-marc", - "kubernetes.io/os": "linux", - "microk8s.io/cluster": "true" - }, - "name": "repldev-marc", - "resourceVersion": "1769699", - "selfLink": "/api/v1/nodes/repldev-marc", - "uid": "cd30c57f-b445-437f-9473-f13343124030" - }, - "spec": {}, - "status": { - "addresses": [ - { - "address": "10.168.0.26", - "type": "InternalIP" - }, - { - "address": "repldev-marc", - "type": "Hostname" - } - ], - "allocatable": { - "cpu": "8", - "ephemeral-storage": "1015018628Ki", - "hugepages-1Gi": "0", - "hugepages-2Mi": "0", - "memory": "30770604Ki", - "pods": "110" - }, - "capacity": { - "cpu": "8", - "ephemeral-storage": "1016067204Ki", - "hugepages-1Gi": "0", - "hugepages-2Mi": "0", - "memory": "30873004Ki", - "pods": "110" - }, - "conditions": [ - { - "lastHeartbeatTime": "2019-11-08T17:03:39Z", - "lastTransitionTime": "2019-10-31T21:28:36Z", - "message": "kubelet has sufficient memory available", - "reason": "KubeletHasSufficientMemory", - "status": "False", - "type": "MemoryPressure" - }, - { - "lastHeartbeatTime": "2019-11-08T17:03:39Z", - "lastTransitionTime": "2019-10-31T21:28:36Z", - "message": "kubelet has no disk pressure", - "reason": "KubeletHasNoDiskPressure", - "status": "False", - "type": "DiskPressure" - }, - { - "lastHeartbeatTime": "2019-11-08T17:03:39Z", - "lastTransitionTime": "2019-10-31T21:28:36Z", - "message": "kubelet has sufficient PID available", - "reason": "KubeletHasSufficientPID", - "status": "False", - "type": "PIDPressure" - }, - { - "lastHeartbeatTime": "2019-11-08T17:03:39Z", - "lastTransitionTime": "2019-10-31T21:28:36Z", - "message": "kubelet is posting ready status. AppArmor enabled", - "reason": "KubeletReady", - "status": "True", - "type": "Ready" - } - ], - "daemonEndpoints": { - "kubeletEndpoint": { - "Port": 10250 - } - }, - "images": [ - { - "names": [ - "localhost:32000/kotsadm-api@sha256:d4821b65869454dfac53ad01f295740df6fcd52711f0dcf6aa9d7e515f7ebe3c" - ], - "sizeBytes": 755312372 - }, - { - "names": [ - "localhost:32000/kotsadm-api@sha256:fc3c971facc9dbd1b07e19c1ebb33c6361dd219af8efed0616afd1278f81fa4e" - ], - "sizeBytes": 755312032 - } - ], - "nodeInfo": { - "architecture": "amd64", - "bootID": "3401cdf2-129c-473d-a50c-723afd7378d3", - "containerRuntimeVersion": "containerd://1.2.5", - "kernelVersion": "5.0.0-1021-gcp", - "kubeProxyVersion": "v1.16.2", - "kubeletVersion": "v1.16.2", - "machineID": "97f4a34d2aa9e26785177a6b64fb9108", - "operatingSystem": "linux", - "osImage": "Ubuntu 18.04.2 LTS", - "systemUUID": "9dc594e5-ac7b-c649-e61f-cad715a28f79" - } - } - } -]` +//go:embed files/deployments.json +var collectedDeployments string + +//go:embed files/nodes.json +var collectedNodes string + +//go:embed files/jobs.json +var collectedJobs string diff --git a/pkg/analyze/files/deployments.json b/pkg/analyze/files/deployments.json new file mode 100644 index 00000000..0bd65a3e --- /dev/null +++ b/pkg/analyze/files/deployments.json @@ -0,0 +1,452 @@ +[ + { + "metadata": { + "name": "kotsadm-api", + "namespace": "default", + "selfLink": "/apis/apps/v1/namespaces/default/deployments/kotsadm-api", + "uid": "56526035-cd29-4d08-8375-291503b1a006", + "resourceVersion": "1583068", + "generation": 1, + "creationTimestamp": "2019-11-07T00:34:32Z", + "labels": { + "app.kubernetes.io/managed-by": "skaffold-v0.41.0" + }, + "annotations": { + "deployment.kubernetes.io/revision": "1", + "kubectl.kubernetes.io/last-applied-configuration": "{\"apiVersion\":\"apps/v1\",\"kind\":\"Deployment\",\"metadata\":{\"annotations\":{},\"labels\":{\"app.kubernetes.io/managed-by\":\"skaffold-v0.41.0\",\"skaffold.dev/builder\":\"local\",\"skaffold.dev/cleanup\":\"true\",\"skaffold.dev/deployer\":\"kustomize\",\"skaffold.dev/docker-api-version\":\"1.40\",\"skaffold.dev/run-id\":\"98f0a02b-9739-4d94-ba11-3e4d273c743e\",\"skaffold.dev/tag-policy\":\"git-commit\",\"skaffold.dev/tail\":\"true\"},\"name\":\"kotsadm-api\",\"namespace\":\"default\"},\"spec\":{\"selector\":{\"matchLabels\":{\"app\":\"kotsadm-api\"}},\"template\":{\"metadata\":{\"labels\":{\"app\":\"kotsadm-api\",\"app.kubernetes.io/managed-by\":\"skaffold-v0.41.0\",\"skaffold.dev/builder\":\"local\",\"skaffold.dev/cleanup\":\"true\",\"skaffold.dev/deployer\":\"kustomize\",\"skaffold.dev/docker-api-version\":\"1.40\",\"skaffold.dev/run-id\":\"98f0a02b-9739-4d94-ba11-3e4d273c743e\",\"skaffold.dev/tag-policy\":\"git-commit\",\"skaffold.dev/tail\":\"true\"}},\"spec\":{\"affinity\":{\"podAffinity\":{\"preferredDuringSchedulingIgnoredDuringExecution\":[{\"podAffinityTerm\":{\"labelSelector\":{\"matchExpressions\":[{\"key\":\"app\",\"operator\":\"In\",\"values\":[\"ship-www\"]}]},\"topologyKey\":\"kubernetes.io/hostname\"},\"weight\":1}]},\"podAntiAffinity\":{\"preferredDuringSchedulingIgnoredDuringExecution\":[{\"podAffinityTerm\":{\"labelSelector\":{\"matchExpressions\":[{\"key\":\"app\",\"operator\":\"In\",\"values\":[\"kotsadm-api\"]}]},\"topologyKey\":\"kubernetes.io/hostname\"},\"weight\":2}]}},\"containers\":[{\"env\":[{\"name\":\"DEV_NAMESPACE\",\"value\":\"test\"},{\"name\":\"LOG_LEVEL\",\"value\":\"debug\"},{\"name\":\"SESSION_KEY\",\"valueFrom\":{\"secretKeyRef\":{\"key\":\"key\",\"name\":\"session\"}}},{\"name\":\"POSTGRES_URI\",\"valueFrom\":{\"secretKeyRef\":{\"key\":\"uri\",\"name\":\"ship-postgres\"}}},{\"name\":\"API_ENCRYPTION_KEY\",\"value\":\"IvWItkB8+ezMisPjSMBknT1PdKjBx7Xc/txZqOP8Y2Oe7+Jy\"},{\"name\":\"INIT_SERVER_URI\",\"value\":\"http://init-server:3000\"},{\"name\":\"WATCH_SERVER_URI\",\"value\":\"http://watch-server:3000\"},{\"name\":\"PINO_LOG_PRETTY\",\"value\":\"1\"},{\"name\":\"S3_BUCKET_NAME\",\"value\":\"shipbucket\"},{\"name\":\"AIRGAP_BUNDLE_S3_BUCKET\",\"value\":\"airgap\"},{\"name\":\"S3_ENDPOINT\",\"value\":\"http://kotsadm-s3.default.svc.cluster.local:4569/\"},{\"name\":\"S3_ACCESS_KEY_ID\",\"value\":\"***HIDDEN***\"},{\"name\":\"S3_SECRET_ACCESS_KEY\",\"value\":\"***HIDDEN***\"},{\"name\":\"S3_BUCKET_ENDPOINT\",\"value\":\"true\"},{\"name\":\"GITHUB_CLIENT_ID\",\"valueFrom\":{\"secretKeyRef\":{\"key\":\"client-id\",\"name\":\"github-app\"}}},{\"name\":\"GITHUB_CLIENT_SECRET\",\"valueFrom\":{\"secretKeyRef\":{\"key\":\"client-secret\",\"name\":\"github-app\"}}},{\"name\":\"GITHUB_INTEGRATION_ID\",\"valueFrom\":{\"secretKeyRef\":{\"key\":\"integration-id\",\"name\":\"github-app\"}}},{\"name\":\"GITHUB_PRIVATE_KEY_FILE\",\"value\":\"/keys/github/private-key.pem\"},{\"name\":\"SHIP_API_ENDPOINT\",\"value\":\"http://kotsadm-api.default.svc.cluster.local:3000\"},{\"name\":\"SHIP_API_ADVERTISE_ENDPOINT\",\"value\":\"http://localhost:30065\"},{\"name\":\"GRAPHQL_PREM_ENDPOINT\",\"value\":\"http://graphql-api-prem:3000/graphql\"},{\"name\":\"AUTO_CREATE_CLUSTER\",\"value\":\"1\"},{\"name\":\"AUTO_CREATE_CLUSTER_NAME\",\"value\":\"microk8s\"},{\"name\":\"AUTO_CREATE_CLUSTER_TOKEN\",\"value\":\"***HIDDEN***\"},{\"name\":\"ENABLE_SHIP\",\"value\":\"1\"},{\"name\":\"ENABLE_KOTS\",\"value\":\"1\"},{\"name\":\"ENABLE_KURL\",\"value\":\"1\"},{\"name\":\"POD_NAMESPACE\",\"valueFrom\":{\"fieldRef\":{\"fieldPath\":\"metadata.namespace\"}}}],\"image\":\"localhost:32000/kotsadm-api:v1.0.1-30-g8fa13e34-dirty@sha256:4a0ca1a2eae46472bd2d454f9e763a458ddd172689e179b17054262507bb4fc8\",\"imagePullPolicy\":\"IfNotPresent\",\"name\":\"kotsadm-api\",\"ports\":[{\"containerPort\":3000,\"name\":\"http\"},{\"containerPort\":9229,\"name\":\"debug\"}],\"readinessProbe\":{\"httpGet\":{\"path\":\"/healthz\",\"port\":3000},\"initialDelaySeconds\":2,\"periodSeconds\":2},\"volumeMounts\":[{\"mountPath\":\"/keys/github\",\"name\":\"github-app-private-key\",\"readOnly\":true}]}],\"restartPolicy\":\"Always\",\"securityContext\":{\"runAsUser\":0},\"serviceAccount\":\"kotsadm-api\",\"volumes\":[{\"name\":\"github-app-private-key\",\"secret\":{\"secretName\":\"github-app-private-key\"}}]}}}}\n" + } + }, + "spec": { + "replicas": 1, + "selector": { + "matchLabels": { + "app": "kotsadm-api" + } + }, + "template": { + "metadata": { + "creationTimestamp": null, + "labels": { + "app": "kotsadm-api" + } + }, + "spec": { + "containers": [ + { + "name": "kotsadm-api", + "image": "localhost:32000/kotsadm-api:v1.0.1-30-g8fa13e34-dirty@sha256:4a0ca1a2eae46472bd2d454f9e763a458ddd172689e179b17054262507bb4fc8", + "ports": [ + { + "name": "http", + "containerPort": 3000, + "protocol": "TCP" + } + ], + "env": [ + { + "name": "DEV_NAMESPACE", + "value": "test" + }, + { + "name": "POD_NAMESPACE", + "valueFrom": { + "fieldRef": { + "apiVersion": "v1", + "fieldPath": "metadata.namespace" + } + } + } + ], + "resources": {}, + "readinessProbe": { + "httpGet": { + "path": "/healthz", + "port": 3000, + "scheme": "HTTP" + }, + "initialDelaySeconds": 2, + "timeoutSeconds": 1, + "periodSeconds": 2, + "successThreshold": 1, + "failureThreshold": 3 + }, + "terminationMessagePath": "/dev/termination-log", + "terminationMessagePolicy": "File", + "imagePullPolicy": "IfNotPresent" + } + ], + "restartPolicy": "Always", + "terminationGracePeriodSeconds": 30, + "dnsPolicy": "ClusterFirst", + "serviceAccountName": "kotsadm-api", + "serviceAccount": "kotsadm-api", + "schedulerName": "default-scheduler" + } + }, + "strategy": { + "type": "RollingUpdate", + "rollingUpdate": { + "maxUnavailable": "25%", + "maxSurge": "25%" + } + }, + "revisionHistoryLimit": 10, + "progressDeadlineSeconds": 600 + }, + "status": { + "observedGeneration": 1, + "replicas": 1, + "updatedReplicas": 1, + "readyReplicas": 1, + "availableReplicas": 1, + "conditions": [ + { + "type": "Available", + "status": "True", + "lastUpdateTime": "2019-11-07T00:35:23Z", + "lastTransitionTime": "2019-11-07T00:35:23Z", + "reason": "MinimumReplicasAvailable", + "message": "Deployment has minimum availability." + }, + { + "type": "Progressing", + "status": "True", + "lastUpdateTime": "2019-11-07T00:35:23Z", + "lastTransitionTime": "2019-11-07T00:34:32Z", + "reason": "NewReplicaSetAvailable", + "message": "ReplicaSet \"kotsadm-api-6f4b994bd5\" has successfully progressed." + } + ] + } + }, + { + "metadata": { + "name": "kotsadm-operator", + "namespace": "default", + "selfLink": "/apis/apps/v1/namespaces/default/deployments/kotsadm-operator", + "uid": "cfae9877-eef4-44c9-acac-0bf0d1aa547e", + "resourceVersion": "1583379", + "generation": 2, + "creationTimestamp": "2019-11-07T00:34:32Z", + "labels": { + "app.kubernetes.io/managed-by": "skaffold-v0.41.0" + }, + "annotations": { + "deployment.kubernetes.io/revision": "2", + "kubectl.kubernetes.io/last-applied-configuration": "{\"apiVersion\":\"apps/v1\",\"kind\":\"Deployment\",\"metadata\":{\"annotations\":{},\"labels\":{\"app.kubernetes.io/managed-by\":\"skaffold-v0.41.0\",\"skaffold.dev/builder\":\"local\",\"skaffold.dev/cleanup\":\"true\",\"skaffold.dev/deployer\":\"kustomize\",\"skaffold.dev/docker-api-version\":\"1.40\",\"skaffold.dev/run-id\":\"98f0a02b-9739-4d94-ba11-3e4d273c743e\",\"skaffold.dev/tag-policy\":\"git-commit\",\"skaffold.dev/tail\":\"true\"},\"name\":\"kotsadm-operator\",\"namespace\":\"default\"},\"spec\":{\"selector\":{\"matchLabels\":{\"app\":\"kotsadm-operator\"}},\"template\":{\"metadata\":{\"labels\":{\"app\":\"kotsadm-operator\",\"app.kubernetes.io/managed-by\":\"skaffold-v0.41.0\",\"skaffold.dev/builder\":\"local\",\"skaffold.dev/cleanup\":\"true\",\"skaffold.dev/deployer\":\"kustomize\",\"skaffold.dev/docker-api-version\":\"1.40\",\"skaffold.dev/run-id\":\"98f0a02b-9739-4d94-ba11-3e4d273c743e\",\"skaffold.dev/tag-policy\":\"git-commit\",\"skaffold.dev/tail\":\"true\"}},\"spec\":{\"containers\":[{\"env\":[{\"name\":\"KOTSADM_API_ENDPOINT\",\"value\":\"http://kotsadm-api:3000\"},{\"name\":\"KOTSADM_TOKEN\",\"value\":\"***HIDDEN***\"},{\"name\":\"KOTSADM_TARGET_NAMESPACE\",\"value\":\"test\"}],\"image\":\"localhost:32000/kotsadm-operator:v1.0.1-30-g8fa13e34-dirty@sha256:177c15b6399717048e9355bc8fd8b8ed213be90615c7c6ee7b7fdcee50aca6c5\",\"imagePullPolicy\":\"Always\",\"name\":\"kotsadm-operator\",\"resources\":{\"limits\":{\"cpu\":\"200m\",\"memory\":\"1000Mi\"},\"requests\":{\"cpu\":\"100m\",\"memory\":\"500Mi\"}}}],\"restartPolicy\":\"Always\"}}}}\n" + } + }, + "spec": { + "replicas": 1, + "selector": { + "matchLabels": { + "app": "kotsadm-operator" + } + }, + "template": { + "metadata": { + "creationTimestamp": null, + "labels": { + "app": "kotsadm-operator" + } + }, + "spec": { + "containers": [ + { + "name": "kotsadm-operator", + "image": "localhost:32000/kotsadm-operator:v1.0.1-30-g8fa13e34-dirty@sha256:177c15b6399717048e9355bc8fd8b8ed213be90615c7c6ee7b7fdcee50aca6c5", + "env": [ + { + "name": "KOTSADM_API_ENDPOINT", + "value": "http://kotsadm-api:3000" + }, + { + "name": "KOTSADM_TOKEN", + "value": "***HIDDEN***" + }, + { + "name": "KOTSADM_TARGET_NAMESPACE", + "value": "test" + } + ], + "resources": { + "limits": { + "cpu": "200m", + "memory": "1000Mi" + }, + "requests": { + "cpu": "100m", + "memory": "500Mi" + } + }, + "terminationMessagePath": "/dev/termination-log", + "terminationMessagePolicy": "File", + "imagePullPolicy": "Always" + } + ], + "restartPolicy": "Always", + "terminationGracePeriodSeconds": 30, + "dnsPolicy": "ClusterFirst", + "securityContext": {}, + "schedulerName": "default-scheduler" + } + }, + "strategy": { + "type": "RollingUpdate", + "rollingUpdate": { + "maxUnavailable": "25%", + "maxSurge": "25%" + } + }, + "revisionHistoryLimit": 10, + "progressDeadlineSeconds": 600 + }, + "status": { + "observedGeneration": 2, + "replicas": 1, + "updatedReplicas": 1, + "readyReplicas": 1, + "availableReplicas": 1, + "conditions": [ + { + "type": "Available", + "status": "True", + "lastUpdateTime": "2019-11-07T00:34:37Z", + "lastTransitionTime": "2019-11-07T00:34:37Z", + "reason": "MinimumReplicasAvailable", + "message": "Deployment has minimum availability." + }, + { + "type": "Progressing", + "status": "True", + "lastUpdateTime": "2019-11-07T00:36:34Z", + "lastTransitionTime": "2019-11-07T00:34:32Z", + "reason": "NewReplicaSetAvailable", + "message": "ReplicaSet \"kotsadm-operator-5b5c977699\" has successfully progressed." + } + ] + } + }, + { + "metadata": { + "name": "kotsadm-postgres-watch", + "namespace": "default", + "selfLink": "/apis/apps/v1/namespaces/default/deployments/kotsadm-postgres-watch", + "uid": "ec195b07-4fd2-4bbe-8b01-4cbdfbe0e79d", + "resourceVersion": "1582762", + "generation": 1, + "creationTimestamp": "2019-11-07T00:34:39Z", + "annotations": { + "deployment.kubernetes.io/revision": "1" + }, + "ownerReferences": [ + { + "apiVersion": "databases.schemahero.io/v1alpha2", + "kind": "Database", + "name": "kotsadm-postgres", + "uid": "e6d51ce6-c4c1-428f-9e8e-56f1a3a43588", + "controller": true, + "blockOwnerDeletion": true + } + ] + }, + "spec": { + "replicas": 1, + "selector": { + "matchLabels": { + "deployment": "kotsadm-postgreswatch" + } + }, + "template": { + "metadata": { + "creationTimestamp": null, + "labels": { + "deployment": "kotsadm-postgreswatch" + } + }, + "spec": { + "containers": [ + { + "name": "schemahero", + "image": "schemahero/schemahero:alpha", + "args": [ + "watch", + "--driver", + "postgres", + "--uri", + "postgres://shipcloud:password@postgres.default.svc.cluster.local:5432/shipcloud?sslmode=disable", + "--namespace", + "default", + "--instance", + "kotsadm-postgres" + ], + "resources": {}, + "terminationMessagePath": "/dev/termination-log", + "terminationMessagePolicy": "File", + "imagePullPolicy": "Always" + } + ], + "restartPolicy": "Always", + "terminationGracePeriodSeconds": 30, + "dnsPolicy": "ClusterFirst", + "serviceAccountName": "kotsadm-postgres", + "serviceAccount": "kotsadm-postgres", + "securityContext": {}, + "schedulerName": "default-scheduler" + } + }, + "strategy": { + "type": "RollingUpdate", + "rollingUpdate": { + "maxUnavailable": "25%", + "maxSurge": "25%" + } + }, + "revisionHistoryLimit": 10, + "progressDeadlineSeconds": 600 + }, + "status": { + "observedGeneration": 1, + "replicas": 1, + "updatedReplicas": 1, + "readyReplicas": 1, + "availableReplicas": 1, + "conditions": [ + { + "type": "Available", + "status": "True", + "lastUpdateTime": "2019-11-07T00:35:00Z", + "lastTransitionTime": "2019-11-07T00:35:00Z", + "reason": "MinimumReplicasAvailable", + "message": "Deployment has minimum availability." + }, + { + "type": "Progressing", + "status": "True", + "lastUpdateTime": "2019-11-07T00:35:00Z", + "lastTransitionTime": "2019-11-07T00:34:39Z", + "reason": "NewReplicaSetAvailable", + "message": "ReplicaSet \"kotsadm-postgres-watch-5cf76f4c45\" has successfully progressed." + } + ] + } + }, + { + "metadata": { + "name": "kotsadm-web", + "namespace": "default", + "selfLink": "/apis/apps/v1/namespaces/default/deployments/kotsadm-web", + "uid": "4e657f8a-5edb-498b-9402-1f93f51f5dda", + "resourceVersion": "1582354", + "generation": 1, + "creationTimestamp": "2019-11-07T00:34:32Z", + "labels": { + "app.kubernetes.io/managed-by": "skaffold-v0.41.0", + "skaffold.dev/builder": "local", + "skaffold.dev/cleanup": "true", + "skaffold.dev/deployer": "kustomize", + "skaffold.dev/docker-api-version": "1.40", + "skaffold.dev/run-id": "98f0a02b-9739-4d94-ba11-3e4d273c743e", + "skaffold.dev/tag-policy": "git-commit", + "skaffold.dev/tail": "true" + }, + "annotations": { + "deployment.kubernetes.io/revision": "1", + "kubectl.kubernetes.io/last-applied-configuration": "{\"apiVersion\":\"apps/v1\",\"kind\":\"Deployment\",\"metadata\":{\"annotations\":{},\"labels\":{\"app.kubernetes.io/managed-by\":\"skaffold-v0.41.0\",\"skaffold.dev/builder\":\"local\",\"skaffold.dev/cleanup\":\"true\",\"skaffold.dev/deployer\":\"kustomize\",\"skaffold.dev/docker-api-version\":\"1.40\",\"skaffold.dev/run-id\":\"98f0a02b-9739-4d94-ba11-3e4d273c743e\",\"skaffold.dev/tag-policy\":\"git-commit\",\"skaffold.dev/tail\":\"true\"},\"name\":\"kotsadm-web\",\"namespace\":\"default\"},\"spec\":{\"selector\":{\"matchLabels\":{\"app\":\"kotsadm-web\"}},\"template\":{\"metadata\":{\"labels\":{\"app\":\"kotsadm-web\",\"app.kubernetes.io/managed-by\":\"skaffold-v0.41.0\",\"skaffold.dev/builder\":\"local\",\"skaffold.dev/cleanup\":\"true\",\"skaffold.dev/deployer\":\"kustomize\",\"skaffold.dev/docker-api-version\":\"1.40\",\"skaffold.dev/run-id\":\"98f0a02b-9739-4d94-ba11-3e4d273c743e\",\"skaffold.dev/tag-policy\":\"git-commit\",\"skaffold.dev/tail\":\"true\"}},\"spec\":{\"containers\":[{\"env\":[{\"name\":\"GITHUB_CLIENT_ID\",\"valueFrom\":{\"secretKeyRef\":{\"key\":\"client-id\",\"name\":\"github-app\"}}},{\"name\":\"GITHUB_INSTALL_URL\",\"valueFrom\":{\"secretKeyRef\":{\"key\":\"install-url\",\"name\":\"github-app\"}}},{\"name\":\"SHIP_CLUSTER_API_SERVER\",\"value\":\"http://localhost:30065\"},{\"name\":\"SHIP_CLUSTER_WEB_URI\",\"value\":\"http://localhost:8000\"}],\"image\":\"localhost:32000/kotsadm-web:v1.0.1-30-g8fa13e34@sha256:5b5b5b640b6e09d8b3185d4ae15ac4dc558d4e2ea034ac3e567d8cce04eadb9c\",\"imagePullPolicy\":\"IfNotPresent\",\"name\":\"kotsadm-web\",\"ports\":[{\"containerPort\":8000,\"name\":\"http\"}]}]}}}}\n" + } + }, + "spec": { + "replicas": 1, + "selector": { + "matchLabels": { + "app": "kotsadm-web" + } + }, + "template": { + "metadata": { + "creationTimestamp": null, + "labels": { + "app": "kotsadm-web", + "app.kubernetes.io/managed-by": "skaffold-v0.41.0", + "skaffold.dev/builder": "local", + "skaffold.dev/cleanup": "true", + "skaffold.dev/deployer": "kustomize", + "skaffold.dev/docker-api-version": "1.40", + "skaffold.dev/run-id": "98f0a02b-9739-4d94-ba11-3e4d273c743e", + "skaffold.dev/tag-policy": "git-commit", + "skaffold.dev/tail": "true" + } + }, + "spec": { + "containers": [ + { + "name": "kotsadm-web", + "image": "localhost:32000/kotsadm-web:v1.0.1-30-g8fa13e34@sha256:5b5b5b640b6e09d8b3185d4ae15ac4dc558d4e2ea034ac3e567d8cce04eadb9c", + "ports": [ + { + "name": "http", + "containerPort": 8000, + "protocol": "TCP" + } + ], + "env": [ + { + "name": "GITHUB_CLIENT_ID", + "valueFrom": { + "secretKeyRef": { + "name": "github-app", + "key": "client-id" + } + } + } + ], + "resources": {}, + "terminationMessagePath": "/dev/termination-log", + "terminationMessagePolicy": "File", + "imagePullPolicy": "IfNotPresent" + } + ], + "restartPolicy": "Always", + "terminationGracePeriodSeconds": 30, + "dnsPolicy": "ClusterFirst", + "securityContext": {}, + "schedulerName": "default-scheduler" + } + }, + "strategy": { + "type": "RollingUpdate", + "rollingUpdate": { + "maxUnavailable": "25%", + "maxSurge": "25%" + } + }, + "revisionHistoryLimit": 10, + "progressDeadlineSeconds": 600 + }, + "status": { + "observedGeneration": 1, + "replicas": 1, + "updatedReplicas": 1, + "readyReplicas": 1, + "availableReplicas": 1, + "conditions": [ + { + "type": "Available", + "status": "True", + "lastUpdateTime": "2019-11-07T00:34:39Z", + "lastTransitionTime": "2019-11-07T00:34:39Z", + "reason": "MinimumReplicasAvailable", + "message": "Deployment has minimum availability." + }, + { + "type": "Progressing", + "status": "True", + "lastUpdateTime": "2019-11-07T00:34:39Z", + "lastTransitionTime": "2019-11-07T00:34:32Z", + "reason": "NewReplicaSetAvailable", + "message": "ReplicaSet \"kotsadm-web-79bfb95c48\" has successfully progressed." + } + ] + } + } +] \ No newline at end of file diff --git a/pkg/analyze/files/jobs.json b/pkg/analyze/files/jobs.json new file mode 100644 index 00000000..541891a0 --- /dev/null +++ b/pkg/analyze/files/jobs.json @@ -0,0 +1,318 @@ +[ + { + "metadata": { + "name": "pre-install-job", + "namespace": "test", + "uid": "4545c8ba-0462-45a6-96c3-793a545dfcf6", + "resourceVersion": "8303004", + "creationTimestamp": "2021-10-05T19:11:43Z", + "labels": { + "kots.io/app-slug": "test", + "kots.io/backup": "velero" + }, + "annotations": { + "helm.sh/hook": "pre-install", + "helm.sh/hook-weight": "2", + "kots.io/app-slug": "test", + "kubectl.kubernetes.io/last-applied-configuration": "{\"apiVersion\":\"batch/v1\",\"kind\":\"Job\",\"metadata\":{\"annotations\":{\"helm.sh/hook\":\"pre-install\",\"helm.sh/hook-weight\":\"2\",\"kots.io/app-slug\":\"test\"},\"labels\":{\"kots.io/app-slug\":\"test\",\"kots.io/backup\":\"velero\"},\"name\":\"pre-install-job\",\"namespace\":\"test\"},\"spec\":{\"backoffLimit\":3,\"template\":{\"metadata\":{\"annotations\":{\"kots.io/app-slug\":\"test\"},\"labels\":{\"kots.io/app-slug\":\"test\",\"kots.io/backup\":\"velero\"},\"name\":\"security-test\"},\"spec\":{\"containers\":[{\"command\":[\"sh\",\"-c\",\"sleep 10\"],\"image\":\"alpine\",\"imagePullPolicy\":\"IfNotPresent\",\"name\":\"hook-test\"}],\"restartPolicy\":\"Never\"}}}}\n" + }, + "managedFields": [ + { + "manager": "kubectl-client-side-apply", + "operation": "Update", + "apiVersion": "batch/v1", + "time": "2021-10-05T19:11:43Z", + "fieldsType": "FieldsV1", + "fieldsV1": { + "f:metadata": { + "f:annotations": { + ".": {}, + "f:helm.sh/hook": {}, + "f:helm.sh/hook-weight": {}, + "f:kots.io/app-slug": {}, + "f:kubectl.kubernetes.io/last-applied-configuration": {} + }, + "f:labels": { + ".": {}, + "f:kots.io/app-slug": {}, + "f:kots.io/backup": {} + } + }, + "f:spec": { + "f:backoffLimit": {}, + "f:completions": {}, + "f:parallelism": {}, + "f:template": { + "f:metadata": { + "f:annotations": { + ".": {}, + "f:kots.io/app-slug": {} + }, + "f:labels": { + ".": {}, + "f:kots.io/app-slug": {}, + "f:kots.io/backup": {} + }, + "f:name": {} + }, + "f:spec": { + "f:containers": { + "k:{\"name\":\"hook-test\"}": { + ".": {}, + "f:command": {}, + "f:image": {}, + "f:imagePullPolicy": {}, + "f:name": {}, + "f:resources": {}, + "f:terminationMessagePath": {}, + "f:terminationMessagePolicy": {} + } + }, + "f:dnsPolicy": {}, + "f:restartPolicy": {}, + "f:schedulerName": {}, + "f:securityContext": {}, + "f:terminationGracePeriodSeconds": {} + } + } + } + } + }, + { + "manager": "k3s", + "operation": "Update", + "apiVersion": "batch/v1", + "time": "2021-10-05T19:11:55Z", + "fieldsType": "FieldsV1", + "fieldsV1": { + "f:status": { + "f:completionTime": {}, + "f:conditions": {}, + "f:startTime": {}, + "f:succeeded": {} + } + } + } + ] + }, + "spec": { + "parallelism": 1, + "completions": 1, + "backoffLimit": 3, + "selector": { + "matchLabels": { + "controller-uid": "4545c8ba-0462-45a6-96c3-793a545dfcf6" + } + }, + "template": { + "metadata": { + "name": "security-test", + "creationTimestamp": null, + "labels": { + "controller-uid": "4545c8ba-0462-45a6-96c3-793a545dfcf6", + "job-name": "pre-install-job", + "kots.io/app-slug": "test", + "kots.io/backup": "velero" + }, + "annotations": { + "kots.io/app-slug": "test" + } + }, + "spec": { + "containers": [ + { + "name": "hook-test", + "image": "alpine", + "command": [ + "sh", + "-c", + "sleep 10" + ], + "resources": {}, + "terminationMessagePath": "/dev/termination-log", + "terminationMessagePolicy": "File", + "imagePullPolicy": "IfNotPresent" + } + ], + "restartPolicy": "Never", + "terminationGracePeriodSeconds": 30, + "dnsPolicy": "ClusterFirst", + "securityContext": {}, + "schedulerName": "default-scheduler" + } + } + }, + "status": { + "conditions": [ + { + "type": "Complete", + "status": "True", + "lastProbeTime": "2021-10-05T19:11:55Z", + "lastTransitionTime": "2021-10-05T19:11:55Z" + } + ], + "startTime": "2021-10-05T19:11:43Z", + "completionTime": "2021-10-05T19:11:55Z", + "succeeded": 1 + } + }, + { + "metadata": { + "name": "post-install-job", + "namespace": "test", + "uid": "13844969-d21a-4514-8bed-66157f216af7", + "resourceVersion": "8303007", + "creationTimestamp": "2021-10-05T19:11:43Z", + "labels": { + "kots.io/app-slug": "test", + "kots.io/backup": "velero" + }, + "annotations": { + "helm.sh/hook": "post-install", + "helm.sh/hook-weight": "2", + "kots.io/app-slug": "test", + "kubectl.kubernetes.io/last-applied-configuration": "{\"apiVersion\":\"batch/v1\",\"kind\":\"Job\",\"metadata\":{\"annotations\":{\"helm.sh/hook\":\"post-install\",\"helm.sh/hook-weight\":\"2\",\"kots.io/app-slug\":\"test\"},\"labels\":{\"kots.io/app-slug\":\"test\",\"kots.io/backup\":\"velero\"},\"name\":\"post-install-job\",\"namespace\":\"test\"},\"spec\":{\"backoffLimit\":3,\"template\":{\"metadata\":{\"annotations\":{\"kots.io/app-slug\":\"test\"},\"labels\":{\"kots.io/app-slug\":\"test\",\"kots.io/backup\":\"velero\"},\"name\":\"security-test\"},\"spec\":{\"containers\":[{\"command\":[\"sh\",\"-c\",\"sleep 10\"],\"image\":\"alpine\",\"imagePullPolicy\":\"IfNotPresent\",\"name\":\"hook-test\"}],\"restartPolicy\":\"Never\"}}}}\n" + }, + "managedFields": [ + { + "manager": "kubectl-client-side-apply", + "operation": "Update", + "apiVersion": "batch/v1", + "time": "2021-10-05T19:11:43Z", + "fieldsType": "FieldsV1", + "fieldsV1": { + "f:metadata": { + "f:annotations": { + ".": {}, + "f:helm.sh/hook": {}, + "f:helm.sh/hook-weight": {}, + "f:kots.io/app-slug": {}, + "f:kubectl.kubernetes.io/last-applied-configuration": {} + }, + "f:labels": { + ".": {}, + "f:kots.io/app-slug": {}, + "f:kots.io/backup": {} + } + }, + "f:spec": { + "f:backoffLimit": {}, + "f:completions": {}, + "f:parallelism": {}, + "f:template": { + "f:metadata": { + "f:annotations": { + ".": {}, + "f:kots.io/app-slug": {} + }, + "f:labels": { + ".": {}, + "f:kots.io/app-slug": {}, + "f:kots.io/backup": {} + }, + "f:name": {} + }, + "f:spec": { + "f:containers": { + "k:{\"name\":\"hook-test\"}": { + ".": {}, + "f:command": {}, + "f:image": {}, + "f:imagePullPolicy": {}, + "f:name": {}, + "f:resources": {}, + "f:terminationMessagePath": {}, + "f:terminationMessagePolicy": {} + } + }, + "f:dnsPolicy": {}, + "f:restartPolicy": {}, + "f:schedulerName": {}, + "f:securityContext": {}, + "f:terminationGracePeriodSeconds": {} + } + } + } + } + }, + { + "manager": "k3s", + "operation": "Update", + "apiVersion": "batch/v1", + "time": "2021-10-05T19:11:55Z", + "fieldsType": "FieldsV1", + "fieldsV1": { + "f:status": { + "f:completionTime": {}, + "f:conditions": {}, + "f:startTime": {}, + "f:succeeded": {} + } + } + } + ] + }, + "spec": { + "parallelism": 1, + "completions": 1, + "backoffLimit": 3, + "selector": { + "matchLabels": { + "controller-uid": "13844969-d21a-4514-8bed-66157f216af7" + } + }, + "template": { + "metadata": { + "name": "security-test", + "creationTimestamp": null, + "labels": { + "controller-uid": "13844969-d21a-4514-8bed-66157f216af7", + "job-name": "post-install-job", + "kots.io/app-slug": "test", + "kots.io/backup": "velero" + }, + "annotations": { + "kots.io/app-slug": "test" + } + }, + "spec": { + "containers": [ + { + "name": "hook-test", + "image": "alpine", + "command": [ + "sh", + "-c", + "sleep 10" + ], + "resources": {}, + "terminationMessagePath": "/dev/termination-log", + "terminationMessagePolicy": "File", + "imagePullPolicy": "IfNotPresent" + } + ], + "restartPolicy": "Never", + "terminationGracePeriodSeconds": 30, + "dnsPolicy": "ClusterFirst", + "securityContext": {}, + "schedulerName": "default-scheduler" + } + } + }, + "status": { + "conditions": [ + { + "type": "Failed", + "status": "True", + "lastProbeTime": "2021-10-05T19:11:55Z", + "lastTransitionTime": "2021-10-05T19:11:55Z", + "message": "Job has reached the specified backoff limit", + "reason": "BackoffLimitExceeded" + } + ], + "startTime": "2021-10-05T19:11:43Z", + "completionTime": "2021-10-05T19:11:55Z", + "failed": 1 + } + } +] diff --git a/pkg/analyze/files/nodes.json b/pkg/analyze/files/nodes.json new file mode 100644 index 00000000..3ffe7516 --- /dev/null +++ b/pkg/analyze/files/nodes.json @@ -0,0 +1,119 @@ +[ + { + "apiVersion": "v1", + "kind": "Node", + "metadata": { + "annotations": { + "node.alpha.kubernetes.io/ttl": "0", + "volumes.kubernetes.io/controller-managed-attach-detach": "true" + }, + "creationTimestamp": "2019-10-23T18:16:43Z", + "labels": { + "beta.kubernetes.io/arch": "amd64", + "beta.kubernetes.io/os": "linux", + "kubernetes.io/arch": "amd64", + "kubernetes.io/hostname": "repldev-marc", + "kubernetes.io/os": "linux", + "microk8s.io/cluster": "true" + }, + "name": "repldev-marc", + "resourceVersion": "1769699", + "selfLink": "/api/v1/nodes/repldev-marc", + "uid": "cd30c57f-b445-437f-9473-f13343124030" + }, + "spec": {}, + "status": { + "addresses": [ + { + "address": "10.168.0.26", + "type": "InternalIP" + }, + { + "address": "repldev-marc", + "type": "Hostname" + } + ], + "allocatable": { + "cpu": "8", + "ephemeral-storage": "1015018628Ki", + "hugepages-1Gi": "0", + "hugepages-2Mi": "0", + "memory": "30770604Ki", + "pods": "110" + }, + "capacity": { + "cpu": "8", + "ephemeral-storage": "1016067204Ki", + "hugepages-1Gi": "0", + "hugepages-2Mi": "0", + "memory": "30873004Ki", + "pods": "110" + }, + "conditions": [ + { + "lastHeartbeatTime": "2019-11-08T17:03:39Z", + "lastTransitionTime": "2019-10-31T21:28:36Z", + "message": "kubelet has sufficient memory available", + "reason": "KubeletHasSufficientMemory", + "status": "False", + "type": "MemoryPressure" + }, + { + "lastHeartbeatTime": "2019-11-08T17:03:39Z", + "lastTransitionTime": "2019-10-31T21:28:36Z", + "message": "kubelet has no disk pressure", + "reason": "KubeletHasNoDiskPressure", + "status": "False", + "type": "DiskPressure" + }, + { + "lastHeartbeatTime": "2019-11-08T17:03:39Z", + "lastTransitionTime": "2019-10-31T21:28:36Z", + "message": "kubelet has sufficient PID available", + "reason": "KubeletHasSufficientPID", + "status": "False", + "type": "PIDPressure" + }, + { + "lastHeartbeatTime": "2019-11-08T17:03:39Z", + "lastTransitionTime": "2019-10-31T21:28:36Z", + "message": "kubelet is posting ready status. AppArmor enabled", + "reason": "KubeletReady", + "status": "True", + "type": "Ready" + } + ], + "daemonEndpoints": { + "kubeletEndpoint": { + "Port": 10250 + } + }, + "images": [ + { + "names": [ + "localhost:32000/kotsadm-api@sha256:d4821b65869454dfac53ad01f295740df6fcd52711f0dcf6aa9d7e515f7ebe3c" + ], + "sizeBytes": 755312372 + }, + { + "names": [ + "localhost:32000/kotsadm-api@sha256:fc3c971facc9dbd1b07e19c1ebb33c6361dd219af8efed0616afd1278f81fa4e" + ], + "sizeBytes": 755312032 + } + ], + "nodeInfo": { + "architecture": "amd64", + "bootID": "3401cdf2-129c-473d-a50c-723afd7378d3", + "containerRuntimeVersion": "containerd://1.2.5", + "kernelVersion": "5.0.0-1021-gcp", + "kubeProxyVersion": "v1.16.2", + "kubeletVersion": "v1.16.2", + "machineID": "97f4a34d2aa9e26785177a6b64fb9108", + "operatingSystem": "linux", + "osImage": "Ubuntu 18.04.2 LTS", + "systemUUID": "9dc594e5-ac7b-c649-e61f-cad715a28f79" + } + } + } +] \ No newline at end of file diff --git a/pkg/analyze/job_status.go b/pkg/analyze/job_status.go new file mode 100644 index 00000000..6871dabf --- /dev/null +++ b/pkg/analyze/job_status.go @@ -0,0 +1,235 @@ +package analyzer + +import ( + "encoding/json" + "fmt" + "path/filepath" + "strconv" + "strings" + + "github.com/pkg/errors" + troubleshootv1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2" + batchv1 "k8s.io/api/batch/v1" +) + +func analyzeJobStatus(analyzer *troubleshootv1beta2.JobStatus, getFileContents func(string) (map[string][]byte, error)) ([]*AnalyzeResult, error) { + if analyzer.Name == "" { + return analyzeAllJobStatuses(analyzer, getFileContents) + } else { + return analyzeOneJobStatus(analyzer, getFileContents) + } +} + +func analyzeOneJobStatus(analyzer *troubleshootv1beta2.JobStatus, getFileContents func(string) (map[string][]byte, error)) ([]*AnalyzeResult, error) { + files, err := getFileContents(filepath.Join("cluster-resources", "jobs", fmt.Sprintf("%s.json", analyzer.Namespace))) + if err != nil { + return nil, errors.Wrap(err, "failed to read collected deployments from namespace") + } + + var result *AnalyzeResult + for _, collected := range files { // only 1 file here + var jobs []batchv1.Job + if err := json.Unmarshal(collected, &jobs); err != nil { + return nil, errors.Wrap(err, "failed to unmarshal deployment list") + } + + var job *batchv1.Job + for _, j := range jobs { + if j.Name == analyzer.Name { + job = j.DeepCopy() + break + } + } + + if job == nil { + // there's not an error, but maybe the requested deployment is not even deployed + result = &AnalyzeResult{ + Title: fmt.Sprintf("%s Job Status", analyzer.Name), + IconKey: "kubernetes_deployment_status", // TODO: need new icon + IconURI: "https://troubleshoot.sh/images/analyzer-icons/deployment-status.svg?w=17&h=17", // TODO: need new icon + IsFail: true, + Message: fmt.Sprintf("The job %q was not found", analyzer.Name), + } + } else { + result, err = jobStatus(analyzer.Outcomes, job) + if err != nil { + return nil, errors.Wrap(err, "failed to process status") + } + } + } + + return []*AnalyzeResult{result}, nil +} + +func analyzeAllJobStatuses(analyzer *troubleshootv1beta2.JobStatus, getFileContents func(string) (map[string][]byte, error)) ([]*AnalyzeResult, error) { + var fileName string + if analyzer.Namespace != "" { + fileName = filepath.Join("cluster-resources", "jobs", fmt.Sprintf("%s.json", analyzer.Namespace)) + } else { + fileName = filepath.Join("cluster-resources", "jobs", "*.json") + } + + files, err := getFileContents(fileName) + if err != nil { + return nil, errors.Wrap(err, "failed to read collected deployments from file") + } + + results := []*AnalyzeResult{} + for _, collected := range files { + var jobs []batchv1.Job + if err := json.Unmarshal(collected, &jobs); err != nil { + return nil, errors.Wrap(err, "failed to unmarshal deployment list") + } + + for _, job := range jobs { + if job.Spec.Completions == nil && job.Status.Succeeded > 1 { + continue + } + + if job.Spec.Completions != nil && *job.Spec.Completions == job.Status.Succeeded { + continue + } + + if job.Status.Failed == 0 { + continue + } + + result := &AnalyzeResult{ + Title: fmt.Sprintf("%s/%s Deployment Status", job.Namespace, job.Name), + IconKey: "kubernetes_deployment_status", + IconURI: "https://troubleshoot.sh/images/analyzer-icons/deployment-status.svg?w=17&h=17", + IsFail: true, + Message: fmt.Sprintf("The job %s/%s is not complete", job.Namespace, job.Name), + } + + results = append(results, result) + } + } + + return results, nil +} + +func jobStatus(outcomes []*troubleshootv1beta2.Outcome, job *batchv1.Job) (*AnalyzeResult, error) { + result := &AnalyzeResult{ + Title: fmt.Sprintf("%s Status", job.Name), + IconKey: "kubernetes_deployment_status", // TODO: needs new icon + IconURI: "https://troubleshoot.sh/images/analyzer-icons/deployment-status.svg?w=17&h=17", // TODO: needs new icon + } + + // ordering from the spec is important, the first one that matches returns + for _, outcome := range outcomes { + if outcome.Fail != nil { + if outcome.Fail.When == "" { + result.IsFail = true + result.Message = outcome.Fail.Message + result.URI = outcome.Fail.URI + + return result, nil + } + + match, err := compareJobStatusToWhen(outcome.Fail.When, job) + if err != nil { + return nil, errors.Wrap(err, "failed to parse fail range") + } + + if match { + result.IsFail = true + result.Message = outcome.Fail.Message + result.URI = outcome.Fail.URI + + return result, nil + } + } else if outcome.Warn != nil { + if outcome.Warn.When == "" { + result.IsWarn = true + result.Message = outcome.Warn.Message + result.URI = outcome.Warn.URI + + return result, nil + } + + match, err := compareJobStatusToWhen(outcome.Warn.When, job) + if err != nil { + return nil, errors.Wrap(err, "failed to parse warn range") + } + + if match { + result.IsWarn = true + result.Message = outcome.Warn.Message + result.URI = outcome.Warn.URI + + return result, nil + } + } else if outcome.Pass != nil { + if outcome.Pass.When == "" { + result.IsPass = true + result.Message = outcome.Pass.Message + result.URI = outcome.Pass.URI + + return result, nil + } + + match, err := compareJobStatusToWhen(outcome.Pass.When, job) + if err != nil { + return nil, errors.Wrap(err, "failed to parse pass range") + } + + if match { + result.IsPass = true + result.Message = outcome.Pass.Message + result.URI = outcome.Pass.URI + + return result, nil + } + } + } + + return result, nil +} + +func compareJobStatusToWhen(when string, job *batchv1.Job) (bool, error) { + parts := strings.Split(strings.TrimSpace(when), " ") + + // we can make this a lot more flexible + if len(parts) != 3 { + return false, errors.Errorf("unable to parse when range: %s", when) + } + + value, err := strconv.Atoi(parts[2]) + if err != nil { + return false, errors.Wrapf(err, "failed to parse when value: %s", parts[2]) + } + + var actual int32 + switch parts[0] { + case "succeeded": + actual = job.Status.Succeeded + case "failed": + actual = job.Status.Failed + default: + return false, errors.Errorf("unknown when value: %s", parts[0]) + } + + switch parts[1] { + case "=": + fallthrough + case "==": + fallthrough + case "===": + return actual == int32(value), nil + + case "<": + return actual < int32(value), nil + + case ">": + return actual > int32(value), nil + + case "<=": + return actual <= int32(value), nil + + case ">=": + return actual >= int32(value), nil + } + + return false, errors.Errorf("unknown comparator: %q", parts[1]) +} diff --git a/pkg/analyze/job_status_test.go b/pkg/analyze/job_status_test.go new file mode 100644 index 00000000..d0576802 --- /dev/null +++ b/pkg/analyze/job_status_test.go @@ -0,0 +1,143 @@ +package analyzer + +import ( + "testing" + + troubleshootv1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func Test_JobStatus(t *testing.T) { + tests := []struct { + name string + analyzer troubleshootv1beta2.JobStatus + expectResult []*AnalyzeResult + files map[string][]byte + }{ + { + name: "1/1, pass when = 1", + analyzer: troubleshootv1beta2.JobStatus{ + Outcomes: []*troubleshootv1beta2.Outcome{ + { + Pass: &troubleshootv1beta2.SingleOutcome{ + When: "succeeded == 1", + Message: "pass", + }, + }, + { + Fail: &troubleshootv1beta2.SingleOutcome{ + Message: "fail", + }, + }, + }, + Namespace: "test", + Name: "pre-install-job", + }, + expectResult: []*AnalyzeResult{ + { + IsPass: true, + IsWarn: false, + IsFail: false, + Title: "pre-install-job Status", + Message: "pass", + IconKey: "kubernetes_deployment_status", + IconURI: "https://troubleshoot.sh/images/analyzer-icons/deployment-status.svg?w=17&h=17", + }, + }, + files: map[string][]byte{ + "cluster-resources/jobs/test.json": []byte(collectedJobs), + }, + }, + { + name: "1/1, fail when < 2", + analyzer: troubleshootv1beta2.JobStatus{ + Outcomes: []*troubleshootv1beta2.Outcome{ + { + Fail: &troubleshootv1beta2.SingleOutcome{ + When: "succeeded < 2", + Message: "fail", + }, + }, + { + Pass: &troubleshootv1beta2.SingleOutcome{ + Message: "pass", + }, + }, + }, + Namespace: "test", + Name: "pre-install-job", + }, + expectResult: []*AnalyzeResult{ + { + IsPass: false, + IsWarn: false, + IsFail: true, + Title: "pre-install-job Status", + Message: "fail", + IconKey: "kubernetes_deployment_status", + IconURI: "https://troubleshoot.sh/images/analyzer-icons/deployment-status.svg?w=17&h=17", + }, + }, + files: map[string][]byte{ + "cluster-resources/jobs/test.json": []byte(collectedJobs), + }, + }, + { + name: "1/1, fail when failed > 0", + analyzer: troubleshootv1beta2.JobStatus{ + Outcomes: []*troubleshootv1beta2.Outcome{ + { + Pass: &troubleshootv1beta2.SingleOutcome{ + When: "succeeded = 1", + Message: "pass", + }, + }, + { + Fail: &troubleshootv1beta2.SingleOutcome{ + When: "failed > 0", + Message: "fail", + }, + }, + { + Fail: &troubleshootv1beta2.SingleOutcome{ + Message: "default fail", + }, + }, + }, + Namespace: "test", + Name: "post-install-job", + }, + expectResult: []*AnalyzeResult{ + { + IsPass: false, + IsWarn: false, + IsFail: true, + Title: "post-install-job Status", + Message: "fail", + IconKey: "kubernetes_deployment_status", + IconURI: "https://troubleshoot.sh/images/analyzer-icons/deployment-status.svg?w=17&h=17", + }, + }, + files: map[string][]byte{ + "cluster-resources/jobs/test.json": []byte(collectedJobs), + }, + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + req := require.New(t) + + getFiles := func(n string) (map[string][]byte, error) { + return test.files, nil + } + + actual, err := analyzeJobStatus(&test.analyzer, getFiles) + req.NoError(err) + + assert.Equal(t, test.expectResult, actual) + + }) + } +} diff --git a/pkg/apis/troubleshoot/v1beta2/analyzer_shared.go b/pkg/apis/troubleshoot/v1beta2/analyzer_shared.go index 7dda948d..35a1fe52 100644 --- a/pkg/apis/troubleshoot/v1beta2/analyzer_shared.go +++ b/pkg/apis/troubleshoot/v1beta2/analyzer_shared.go @@ -63,6 +63,13 @@ type StatefulsetStatus struct { Name string `json:"name" yaml:"name"` } +type JobStatus struct { + AnalyzeMeta `json:",inline" yaml:",inline"` + Outcomes []*Outcome `json:"outcomes" yaml:"outcomes"` + Namespace string `json:"namespace" yaml:"namespace"` + Name string `json:"name" yaml:"name"` +} + type ClusterPodStatuses struct { AnalyzeMeta `json:",inline" yaml:",inline"` Outcomes []*Outcome `json:"outcomes" yaml:"outcomes"` @@ -168,6 +175,7 @@ type Analyze struct { ImagePullSecret *ImagePullSecret `json:"imagePullSecret,omitempty" yaml:"imagePullSecret,omitempty"` DeploymentStatus *DeploymentStatus `json:"deploymentStatus,omitempty" yaml:"deploymentStatus,omitempty"` StatefulsetStatus *StatefulsetStatus `json:"statefulsetStatus,omitempty" yaml:"statefulsetStatus,omitempty"` + JobStatus *JobStatus `json:"jobStatus,omitempty" yaml:"jobStatus,omitempty"` ClusterPodStatuses *ClusterPodStatuses `json:"clusterPodStatuses,omitempty" yaml:"clusterPodStatuses,omitempty"` ContainerRuntime *ContainerRuntime `json:"containerRuntime,omitempty" yaml:"containerRuntime,omitempty"` Distribution *Distribution `json:"distribution,omitempty" yaml:"distribution,omitempty"` diff --git a/pkg/apis/troubleshoot/v1beta2/zz_generated.deepcopy.go b/pkg/apis/troubleshoot/v1beta2/zz_generated.deepcopy.go index b2b8b4a7..fc01b4d7 100644 --- a/pkg/apis/troubleshoot/v1beta2/zz_generated.deepcopy.go +++ b/pkg/apis/troubleshoot/v1beta2/zz_generated.deepcopy.go @@ -97,6 +97,11 @@ func (in *Analyze) DeepCopyInto(out *Analyze) { *out = new(StatefulsetStatus) (*in).DeepCopyInto(*out) } + if in.JobStatus != nil { + in, out := &in.JobStatus, &out.JobStatus + *out = new(JobStatus) + (*in).DeepCopyInto(*out) + } if in.ClusterPodStatuses != nil { in, out := &in.ClusterPodStatuses, &out.ClusterPodStatuses *out = new(ClusterPodStatuses) @@ -2099,6 +2104,33 @@ func (in *Ingress) DeepCopy() *Ingress { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *JobStatus) DeepCopyInto(out *JobStatus) { + *out = *in + out.AnalyzeMeta = in.AnalyzeMeta + if in.Outcomes != nil { + in, out := &in.Outcomes, &out.Outcomes + *out = make([]*Outcome, len(*in)) + for i := range *in { + if (*in)[i] != nil { + in, out := &(*in)[i], &(*out)[i] + *out = new(Outcome) + (*in).DeepCopyInto(*out) + } + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new JobStatus. +func (in *JobStatus) DeepCopy() *JobStatus { + if in == nil { + return nil + } + out := new(JobStatus) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *KernelModulesAnalyze) DeepCopyInto(out *KernelModulesAnalyze) { *out = *in