diff --git a/src/converters/entities/ContainerEntityConverter.test.ts b/src/converters/entities/ContainerEntityConverter.test.ts index dd8f9c7..275f099 100644 --- a/src/converters/entities/ContainerEntityConverter.test.ts +++ b/src/converters/entities/ContainerEntityConverter.test.ts @@ -250,7 +250,14 @@ test("convert containers", async () => { }; const entities = createContainerEntities([ - { pods, serviceAccounts: [], services: [], routes: [], project }, + { + pods, + serviceAccounts: [], + services: [], + routes: [], + project, + deployments: [], + }, ]); expect(entities).toEqual([ diff --git a/src/converters/entities/DeploymentEntityConverter.test.ts b/src/converters/entities/DeploymentEntityConverter.test.ts new file mode 100644 index 0000000..ad7f180 --- /dev/null +++ b/src/converters/entities/DeploymentEntityConverter.test.ts @@ -0,0 +1,338 @@ +import { createDeploymentEntities } from "./DeploymentEntityConverter"; + +test("convert deployments", async () => { + const deployments = [ + { + metadata: { + name: "jenkins-1", + namespace: "test-jenkins-dualboot", + selfLink: + "/api/v1/namespaces/test-jenkins-dualboot/replicationcontrollers/jenkins-1", + uid: "c46d03c5-61bf-11e9-b220-0a2a2b777307", + resourceVersion: "3601372850", + generation: 3, + creationTimestamp: "2019-04-18T09:53:07Z", + labels: { + app: "jenkins-persistent", + "openshift.io/deployment-config.name": "jenkins", + template: "jenkins-persistent-template", + }, + annotations: { + "openshift.io/deployer-pod.completed-at": + "2019-04-18 09:56:04 +0000 UTC", + "openshift.io/deployer-pod.created-at": + "2019-04-18 09:53:07 +0000 UTC", + "openshift.io/deployer-pod.name": "jenkins-1-deploy", + "openshift.io/deployment-config.latest-version": "1", + "openshift.io/deployment-config.name": "jenkins", + "openshift.io/deployment.phase": "Complete", + "openshift.io/deployment.replicas": "1", + "openshift.io/deployment.status-reason": "config change", + "openshift.io/encoded-deployment-config": + '{"kind":"DeploymentConfig","apiVersion":"apps.openshift.io/v1","metadata":{"name":"jenkins","namespace":"test-jenkins-dualboot","selfLink":"/apis/apps.openshift.io/v1/namespaces/test-jenkins-dualboot/deploymentconfigs/jenkins","uid":"c3cb3220-61bf-11e9-b220-0a2a2b777307","resourceVersion":"3548833710","generation":2,"creationTimestamp":"2019-04-18T09:53:06Z","labels":{"app":"jenkins-persistent","template":"jenkins-persistent-template"},"annotations":{"template.alpha.openshift.io/wait-for-ready":"true"}},"spec":{"strategy":{"type":"Recreate","recreateParams":{"timeoutSeconds":600},"resources":{},"activeDeadlineSeconds":21600},"triggers":[{"type":"ImageChange","imageChangeParams":{"automatic":true,"containerNames":["jenkins"],"from":{"kind":"ImageStreamTag","namespace":"openshift","name":"jenkins:2"},"lastTriggeredImage":"docker-registry.default.svc:5000/openshift/jenkins@sha256:3b6ad6c87518f153c6c38c5190f6d95535af3c8210adc8904d6a7f7f4180a36f"}},{"type":"ConfigChange"}],"replicas":1,"revisionHistoryLimit":10,"test":false,"selector":{"name":"jenkins"},"template":{"metadata":{"creationTimestamp":null,"labels":{"name":"jenkins"}},"spec":{"volumes":[{"name":"jenkins-data","persistentVolumeClaim":{"claimName":"jenkins"}}],"containers":[{"name":"jenkins","image":"docker-registry.default.svc:5000/openshift/jenkins@sha256:3b6ad6c87518f153c6c38c5190f6d95535af3c8210adc8904d6a7f7f4180a36f","env":[{"name":"OPENSHIFT_ENABLE_OAUTH","value":"true"},{"name":"OPENSHIFT_ENABLE_REDIRECT_PROMPT","value":"true"},{"name":"DISABLE_ADMINISTRATIVE_MONITORS","value":"true"},{"name":"KUBERNETES_MASTER","value":"https://kubernetes.default:443"},{"name":"KUBERNETES_TRUST_CERTIFICATES","value":"true"},{"name":"JENKINS_SERVICE_NAME","value":"jenkins"},{"name":"JNLP_SERVICE_NAME","value":"jenkins-jnlp"},{"name":"ENABLE_FATAL_ERROR_LOG_FILE","value":"false"}],"resources":{"limits":{"memory":"512Mi"}},"volumeMounts":[{"name":"jenkins-data","mountPath":"/var/lib/jenkins"}],"livenessProbe":{"httpGet":{"path":"/login","port":8080,"scheme":"HTTP"},"initialDelaySeconds":420,"timeoutSeconds":240,"periodSeconds":360,"successThreshold":1,"failureThreshold":2},"readinessProbe":{"httpGet":{"path":"/login","port":8080,"scheme":"HTTP"},"initialDelaySeconds":3,"timeoutSeconds":240,"periodSeconds":10,"successThreshold":1,"failureThreshold":3},"terminationMessagePath":"/dev/termination-log","terminationMessagePolicy":"File","imagePullPolicy":"IfNotPresent","securityContext":{"capabilities":{},"privileged":false}}],"restartPolicy":"Always","terminationGracePeriodSeconds":30,"dnsPolicy":"ClusterFirst","serviceAccountName":"jenkins","serviceAccount":"jenkins","securityContext":{},"schedulerName":"default-scheduler"}}},"status":{"latestVersion":1,"observedGeneration":1,"replicas":0,"updatedReplicas":0,"availableReplicas":0,"unavailableReplicas":0,"details":{"message":"config change","causes":[{"type":"ConfigChange"}]},"conditions":[{"type":"Available","status":"False","lastUpdateTime":"2019-04-18T09:53:06Z","lastTransitionTime":"2019-04-18T09:53:06Z","message":"Deployment config does not have minimum availability."}]}}\n', + }, + ownerReferences: [ + { + apiVersion: "apps.openshift.io/v1", + kind: "DeploymentConfig", + name: "jenkins", + uid: "c3cb3220-61bf-11e9-b220-0a2a2b777307", + controller: true, + blockOwnerDeletion: true, + }, + ], + }, + spec: { + replicas: 0, + selector: { + deployment: "jenkins-1", + deploymentconfig: "jenkins", + name: "jenkins", + }, + template: { + metadata: { + creationTimestamp: null, + labels: { + deployment: "jenkins-1", + deploymentconfig: "jenkins", + name: "jenkins", + }, + annotations: { + "openshift.io/deployment-config.latest-version": "1", + "openshift.io/deployment-config.name": "jenkins", + "openshift.io/deployment.name": "jenkins-1", + }, + }, + spec: { + volumes: [ + { + name: "jenkins-data", + persistentVolumeClaim: { claimName: "jenkins" }, + }, + ], + containers: [ + { + name: "jenkins", + image: + "docker-registry.default.svc:5000/openshift/jenkins@sha256:3b6ad6c87518f153c6c38c5190f6d95535af3c8210adc8904d6a7f7f4180a36f", + env: [ + { name: "OPENSHIFT_ENABLE_OAUTH", value: "true" }, + { + name: "OPENSHIFT_ENABLE_REDIRECT_PROMPT", + value: "true", + }, + { + name: "DISABLE_ADMINISTRATIVE_MONITORS", + value: "true", + }, + { + name: "KUBERNETES_MASTER", + value: "https://kubernetes.default:443", + }, + { name: "KUBERNETES_TRUST_CERTIFICATES", value: "true" }, + { name: "JENKINS_SERVICE_NAME", value: "jenkins" }, + { name: "JNLP_SERVICE_NAME", value: "jenkins-jnlp" }, + { name: "ENABLE_FATAL_ERROR_LOG_FILE", value: "false" }, + ], + resources: { limits: { memory: "512Mi" } }, + volumeMounts: [ + { name: "jenkins-data", mountPath: "/var/lib/jenkins" }, + ], + livenessProbe: { + httpGet: { + path: "/login", + port: 8080, + scheme: "HTTP", + }, + initialDelaySeconds: 420, + timeoutSeconds: 240, + periodSeconds: 360, + successThreshold: 1, + failureThreshold: 2, + }, + readinessProbe: { + httpGet: { + path: "/login", + port: 8080, + scheme: "HTTP", + }, + initialDelaySeconds: 3, + timeoutSeconds: 240, + periodSeconds: 10, + successThreshold: 1, + failureThreshold: 3, + }, + terminationMessagePath: "/dev/termination-log", + terminationMessagePolicy: "File", + imagePullPolicy: "IfNotPresent", + securityContext: { capabilities: {}, privileged: false }, + }, + ], + restartPolicy: "Always", + terminationGracePeriodSeconds: 30, + dnsPolicy: "ClusterFirst", + serviceAccountName: "jenkins", + serviceAccount: "jenkins", + securityContext: {}, + schedulerName: "default-scheduler", + }, + }, + }, + status: { replicas: 0, observedGeneration: 3 }, + }, + { + metadata: { + name: "jenkins-2", + namespace: "test-jenkins-dualboot", + selfLink: + "/api/v1/namespaces/test-jenkins-dualboot/replicationcontrollers/jenkins-2", + uid: "69d811b4-77d1-11e9-9823-0a69cdf75e6f", + resourceVersion: "3603567139", + generation: 2, + creationTimestamp: "2019-05-16T11:54:51Z", + labels: { + app: "jenkins-persistent", + "openshift.io/deployment-config.name": "jenkins", + template: "jenkins-persistent-template", + }, + annotations: { + "openshift.io/deployer-pod.completed-at": + "2019-05-16 11:58:12 +0000 UTC", + "openshift.io/deployer-pod.created-at": + "2019-05-16 11:54:51 +0000 UTC", + "openshift.io/deployer-pod.name": "jenkins-2-deploy", + "openshift.io/deployer-pod.started-at": + "2019-05-16 11:54:52 +0000 UTC", + "openshift.io/deployment-config.latest-version": "2", + "openshift.io/deployment-config.name": "jenkins", + "openshift.io/deployment.phase": "Complete", + "openshift.io/deployment.replicas": "1", + "openshift.io/deployment.status-reason": "config change", + "openshift.io/encoded-deployment-config": + '{"kind":"DeploymentConfig","apiVersion":"apps.openshift.io/v1","metadata":{"name":"jenkins","namespace":"test-jenkins-dualboot","selfLink":"/apis/apps.openshift.io/v1/namespaces/test-jenkins-dualboot/deploymentconfigs/jenkins","uid":"c3cb3220-61bf-11e9-b220-0a2a2b777307","resourceVersion":"3601372568","generation":3,"creationTimestamp":"2019-04-18T09:53:06Z","labels":{"app":"jenkins-persistent","template":"jenkins-persistent-template"},"annotations":{"template.alpha.openshift.io/wait-for-ready":"true"}},"spec":{"strategy":{"type":"Recreate","customParams":{},"recreateParams":{"timeoutSeconds":500},"resources":{},"activeDeadlineSeconds":21600},"triggers":[{"type":"ImageChange","imageChangeParams":{"automatic":true,"containerNames":["jenkins"],"from":{"kind":"ImageStreamTag","namespace":"openshift","name":"jenkins:2"},"lastTriggeredImage":"docker-registry.default.svc:5000/openshift/jenkins@sha256:3b6ad6c87518f153c6c38c5190f6d95535af3c8210adc8904d6a7f7f4180a36f"}},{"type":"ConfigChange"}],"replicas":1,"revisionHistoryLimit":10,"test":false,"selector":{"name":"jenkins"},"template":{"metadata":{"creationTimestamp":null,"labels":{"name":"jenkins"}},"spec":{"volumes":[{"name":"jenkins-data","persistentVolumeClaim":{"claimName":"jenkins"}}],"containers":[{"name":"jenkins","image":"docker-registry.default.svc:5000/openshift/jenkins@sha256:3b6ad6c87518f153c6c38c5190f6d95535af3c8210adc8904d6a7f7f4180a36f","env":[{"name":"OPENSHIFT_ENABLE_OAUTH","value":"true"},{"name":"OPENSHIFT_ENABLE_REDIRECT_PROMPT","value":"true"},{"name":"DISABLE_ADMINISTRATIVE_MONITORS","value":"true"},{"name":"KUBERNETES_MASTER","value":"https://kubernetes.default:443"},{"name":"KUBERNETES_TRUST_CERTIFICATES","value":"true"},{"name":"JENKINS_SERVICE_NAME","value":"jenkins"},{"name":"JNLP_SERVICE_NAME","value":"jenkins-jnlp"},{"name":"ENABLE_FATAL_ERROR_LOG_FILE","value":"false"},{"name":"CUSTOM","value":"123"}],"resources":{"limits":{"memory":"512Mi"}},"volumeMounts":[{"name":"jenkins-data","mountPath":"/var/lib/jenkins"}],"livenessProbe":{"httpGet":{"path":"/login","port":8080,"scheme":"HTTP"},"initialDelaySeconds":420,"timeoutSeconds":240,"periodSeconds":360,"successThreshold":1,"failureThreshold":2},"readinessProbe":{"httpGet":{"path":"/login","port":8080,"scheme":"HTTP"},"initialDelaySeconds":3,"timeoutSeconds":240,"periodSeconds":10,"successThreshold":1,"failureThreshold":3},"terminationMessagePath":"/dev/termination-log","terminationMessagePolicy":"File","imagePullPolicy":"IfNotPresent","securityContext":{"capabilities":{},"privileged":false}}],"restartPolicy":"Always","terminationGracePeriodSeconds":30,"dnsPolicy":"ClusterFirst","serviceAccountName":"jenkins","serviceAccount":"jenkins","securityContext":{},"schedulerName":"default-scheduler"}}},"status":{"latestVersion":2,"observedGeneration":2,"replicas":1,"updatedReplicas":1,"availableReplicas":1,"unavailableReplicas":0,"details":{"message":"config change","causes":[{"type":"ConfigChange"}]},"conditions":[{"type":"Progressing","status":"True","lastUpdateTime":"2019-04-18T09:56:05Z","lastTransitionTime":"2019-04-18T09:56:05Z","reason":"NewReplicationControllerAvailable","message":"replication controller \\"jenkins-1\\" successfully rolled out"},{"type":"Available","status":"True","lastUpdateTime":"2019-05-15T15:07:31Z","lastTransitionTime":"2019-05-15T15:07:31Z","message":"Deployment config has minimum availability."}],"readyReplicas":1}}\n', + }, + ownerReferences: [ + { + apiVersion: "apps.openshift.io/v1", + kind: "DeploymentConfig", + name: "jenkins", + uid: "c3cb3220-61bf-11e9-b220-0a2a2b777307", + controller: true, + blockOwnerDeletion: true, + }, + ], + }, + spec: { + replicas: 1, + selector: { + deployment: "jenkins-2", + deploymentconfig: "jenkins", + name: "jenkins", + }, + template: { + metadata: { + creationTimestamp: null, + labels: { + deployment: "jenkins-2", + deploymentconfig: "jenkins", + name: "jenkins", + }, + annotations: { + "openshift.io/deployment-config.latest-version": "2", + "openshift.io/deployment-config.name": "jenkins", + "openshift.io/deployment.name": "jenkins-2", + }, + }, + spec: { + volumes: [ + { + name: "jenkins-data", + persistentVolumeClaim: { claimName: "jenkins" }, + }, + ], + containers: [ + { + name: "jenkins", + image: + "docker-registry.default.svc:5000/openshift/jenkins@sha256:3b6ad6c87518f153c6c38c5190f6d95535af3c8210adc8904d6a7f7f4180a36f", + env: [ + { name: "OPENSHIFT_ENABLE_OAUTH", value: "true" }, + { + name: "OPENSHIFT_ENABLE_REDIRECT_PROMPT", + value: "true", + }, + { + name: "DISABLE_ADMINISTRATIVE_MONITORS", + value: "true", + }, + { + name: "KUBERNETES_MASTER", + value: "https://kubernetes.default:443", + }, + { name: "KUBERNETES_TRUST_CERTIFICATES", value: "true" }, + { name: "JENKINS_SERVICE_NAME", value: "jenkins" }, + { name: "JNLP_SERVICE_NAME", value: "jenkins-jnlp" }, + { name: "ENABLE_FATAL_ERROR_LOG_FILE", value: "false" }, + { name: "CUSTOM", value: "123" }, + ], + resources: { limits: { memory: "512Mi" } }, + volumeMounts: [ + { name: "jenkins-data", mountPath: "/var/lib/jenkins" }, + ], + livenessProbe: { + httpGet: { + path: "/login", + port: 8080, + scheme: "HTTP", + }, + initialDelaySeconds: 420, + timeoutSeconds: 240, + periodSeconds: 360, + successThreshold: 1, + failureThreshold: 2, + }, + readinessProbe: { + httpGet: { + path: "/login", + port: 8080, + scheme: "HTTP", + }, + initialDelaySeconds: 3, + timeoutSeconds: 240, + periodSeconds: 10, + successThreshold: 1, + failureThreshold: 3, + }, + terminationMessagePath: "/dev/termination-log", + terminationMessagePolicy: "File", + imagePullPolicy: "IfNotPresent", + securityContext: { capabilities: {}, privileged: false }, + }, + ], + restartPolicy: "Always", + terminationGracePeriodSeconds: 30, + dnsPolicy: "ClusterFirst", + serviceAccountName: "jenkins", + serviceAccount: "jenkins", + securityContext: {}, + schedulerName: "default-scheduler", + }, + }, + }, + status: { + replicas: 1, + fullyLabeledReplicas: 1, + observedGeneration: 2, + }, + }, + ]; + + const project = { + metadata: { + name: "default", + selfLink: "/apis/project.openshift.io/v1/projects/default", + uid: "39537998-5bb8-11e9-8c30-4e620801d617", + resourceVersion: "1284", + creationTimestamp: "2019-04-10T17:44:00Z", + annotations: {}, + }, + spec: { + finalizers: ["kubernetes"], + }, + status: { + phase: "Active", + }, + }; + + const entities = createDeploymentEntities([ + { + pods: [], + serviceAccounts: [], + services: [], + routes: [], + project, + deployments: deployments as any, + }, + ]); + + expect(entities).toEqual([ + { + _class: ["Deployment", "Group"], + _key: "openshift_deployment_c46d03c5-61bf-11e9-b220-0a2a2b777307", + _type: "openshift_deployment", + createdOn: 1555581187000, + displayName: "jenkins-1", + isActive: false, + name: "jenkins-1", + }, + { + _class: ["Deployment", "Group"], + _key: "openshift_deployment_69d811b4-77d1-11e9-9823-0a69cdf75e6f", + _type: "openshift_deployment", + createdOn: 1558007691000, + displayName: "jenkins-2", + isActive: true, + name: "jenkins-2", + }, + ]); +}); diff --git a/src/converters/entities/DeploymentEntityConverter.ts b/src/converters/entities/DeploymentEntityConverter.ts new file mode 100644 index 0000000..b6a5e37 --- /dev/null +++ b/src/converters/entities/DeploymentEntityConverter.ts @@ -0,0 +1,32 @@ +import { + DEPLOYMENT_ENTITY_CLASS, + DEPLOYMENT_ENTITY_TYPE, + DeploymentEntity, +} from "../../jupiterone"; +import { NamespaceData } from "../../openshift/types"; +import { generateEntityKey } from "../../utils/generateKeys"; +import getTime from "../../utils/getTime"; + +export function createDeploymentEntities( + namespaces: NamespaceData[], +): DeploymentEntity[] { + const defaultEntities: DeploymentEntity[] = []; + + return namespaces.reduce((acc, namespace) => { + const entities = namespace.deployments.reduce((deployments, item) => { + const entity: DeploymentEntity = { + _class: DEPLOYMENT_ENTITY_CLASS, + _key: generateEntityKey(DEPLOYMENT_ENTITY_TYPE, item.metadata.uid), + _type: DEPLOYMENT_ENTITY_TYPE, + displayName: item.metadata.name, + name: item.metadata.name, + isActive: item.status.replicas > 0, + createdOn: getTime(item.metadata.creationTimestamp)!, + }; + + return [...deployments, entity]; + }, defaultEntities); + + return [...acc, ...entities]; + }, defaultEntities); +} diff --git a/src/converters/entities/GroupEntityConverter.test.ts b/src/converters/entities/GroupEntityConverter.test.ts index 9d7213e..7a233e1 100644 --- a/src/converters/entities/GroupEntityConverter.test.ts +++ b/src/converters/entities/GroupEntityConverter.test.ts @@ -31,10 +31,8 @@ test("convert groups", async () => { _class: "UserGroup", _key: "openshift_user_group_259b87ab-5c46-11e9-82b7-4e620801d617", _type: "openshift_user_group", - creationTimestamp: "2019-04-11T10:39:55Z", + createdOn: 1554979195000, displayName: "first-group", - generation: undefined, - namespace: undefined, resourceVersion: "34739", uid: "259b87ab-5c46-11e9-82b7-4e620801d617", }, @@ -42,10 +40,8 @@ test("convert groups", async () => { _class: "UserGroup", _key: "openshift_user_group_2aafc5a6-5c46-11e9-82b7-4e620801d617", _type: "openshift_user_group", - creationTimestamp: "2019-04-11T10:40:04Z", + createdOn: 1554979204000, displayName: "second-group", - generation: undefined, - namespace: undefined, resourceVersion: "34775", uid: "2aafc5a6-5c46-11e9-82b7-4e620801d617", }, diff --git a/src/converters/entities/GroupEntityConverter.ts b/src/converters/entities/GroupEntityConverter.ts index 931793d..e5caabd 100644 --- a/src/converters/entities/GroupEntityConverter.ts +++ b/src/converters/entities/GroupEntityConverter.ts @@ -7,6 +7,7 @@ import { import { Group } from "../../openshift/types"; import { generateEntityKey } from "../../utils/generateKeys"; +import getTime from "../../utils/getTime"; export function createGroupEntities(data: Group[]): GroupEntity[] { return data.map(d => { @@ -19,7 +20,7 @@ export function createGroupEntities(data: Group[]): GroupEntity[] { namespace: d.metadata.namespace, generation: d.metadata.generation, resourceVersion: d.metadata.resourceVersion, - creationTimestamp: d.metadata.creationTimestamp, + createdOn: getTime(d.metadata.creationTimestamp)!, }; return group; }); diff --git a/src/converters/entities/PodEntityConverter.test.ts b/src/converters/entities/PodEntityConverter.test.ts index 5122f86..ac5f711 100644 --- a/src/converters/entities/PodEntityConverter.test.ts +++ b/src/converters/entities/PodEntityConverter.test.ts @@ -250,7 +250,14 @@ test("convert pods", async () => { }; const entities = createPodEntities([ - { pods, serviceAccounts: [], services: [], routes: [], project }, + { + pods, + serviceAccounts: [], + services: [], + routes: [], + project, + deployments: [], + }, ]); expect(entities).toEqual([ @@ -258,7 +265,7 @@ test("convert pods", async () => { _class: ["Cluster", "Task"], _key: "openshift_pod_c764e7d4-61bf-11e9-b220-0a2a2b777307", _type: "openshift_pod", - creationTimestamp: "2019-04-18T09:53:12Z", + createdOn: 1555581192000, displayName: "jenkins-1-vqgxg", hostIP: "172.31.56.129", name: "jenkins-1-vqgxg", @@ -268,7 +275,7 @@ test("convert pods", async () => { podIP: "10.131.29.54", qosClass: "Burstable", resourceVersion: "3548837654", - startTime: "2019-04-18T09:53:12Z", + startTime: 1555581192000, uid: "c764e7d4-61bf-11e9-b220-0a2a2b777307", }, ]); diff --git a/src/converters/entities/PodEntityConverter.ts b/src/converters/entities/PodEntityConverter.ts index c22fe83..60f4bd1 100644 --- a/src/converters/entities/PodEntityConverter.ts +++ b/src/converters/entities/PodEntityConverter.ts @@ -1,6 +1,7 @@ import { POD_ENTITY_CLASS, POD_ENTITY_TYPE, PodEntity } from "../../jupiterone"; import { NamespaceData } from "../../openshift/types"; import { generateEntityKey } from "../../utils/generateKeys"; +import getTime from "../../utils/getTime"; export function createPodEntities(namespaces: NamespaceData[]): PodEntity[] { const defaultEntities: PodEntity[] = []; @@ -15,13 +16,13 @@ export function createPodEntities(namespaces: NamespaceData[]): PodEntity[] { uid: pod.metadata.uid, namespace: pod.metadata.namespace, resourceVersion: pod.metadata.resourceVersion, - creationTimestamp: pod.metadata.creationTimestamp, + createdOn: getTime(pod.metadata.creationTimestamp)!, name: pod.metadata.name, nodeName: pod.spec.nodeName, phase: pod.status.phase, hostIP: pod.status.hostIP, podIP: pod.status.podIP, - startTime: pod.status.startTime, + startTime: getTime(pod.status.startTime)!, qosClass: pod.status.qosClass, }; diff --git a/src/converters/entities/ProjectEntityConverter.test.ts b/src/converters/entities/ProjectEntityConverter.test.ts index 0674086..0161e19 100644 --- a/src/converters/entities/ProjectEntityConverter.test.ts +++ b/src/converters/entities/ProjectEntityConverter.test.ts @@ -31,7 +31,7 @@ test("convert projects", async () => { _class: "Project", _key: "openshift_project_39537998-5bb8-11e9-8c30-4e620801d617", _type: "openshift_project", - creationTimestamp: "2019-04-10T17:44:00Z", + createdOn: 1554918240000, displayName: "default", resourceVersion: "1284", uid: "39537998-5bb8-11e9-8c30-4e620801d617", diff --git a/src/converters/entities/ProjectEntityConverter.ts b/src/converters/entities/ProjectEntityConverter.ts index a9fb57d..d561820 100644 --- a/src/converters/entities/ProjectEntityConverter.ts +++ b/src/converters/entities/ProjectEntityConverter.ts @@ -6,6 +6,7 @@ import { import { Project } from "../../openshift/types"; import { generateEntityKey } from "../../utils/generateKeys"; +import getTime from "../../utils/getTime"; export function createProjectEntities(data: Project[]): ProjectEntity[] { return data.map(d => { @@ -18,7 +19,7 @@ export function createProjectEntities(data: Project[]): ProjectEntity[] { namespace: d.metadata.namespace, generation: d.metadata.generation, resourceVersion: d.metadata.resourceVersion, - creationTimestamp: d.metadata.creationTimestamp, + createdOn: getTime(d.metadata.creationTimestamp)!, }; return project; diff --git a/src/converters/entities/RouteEntityConverter.test.ts b/src/converters/entities/RouteEntityConverter.test.ts index 01ca8cf..cab9a7e 100644 --- a/src/converters/entities/RouteEntityConverter.test.ts +++ b/src/converters/entities/RouteEntityConverter.test.ts @@ -75,7 +75,14 @@ test("convert routes", async () => { }; const entities = createRouteEntities([ - { routes, serviceAccounts: [], services: [], pods: [], project }, + { + routes, + serviceAccounts: [], + services: [], + pods: [], + project, + deployments: [], + }, ]); expect(entities).toEqual([ @@ -83,7 +90,7 @@ test("convert routes", async () => { _class: "Domain", _key: "openshift_route_c3cd6d2e-61bf-11e9-9c2a-0ab8769191d3", _type: "openshift_route", - creationTimestamp: "2019-04-18T09:53:06Z", + createdOn: 1555581186000, displayName: "jenkins", host: "jenkins-example_namespace.7e14.starter-us-west-2.openshiftapps.com", diff --git a/src/converters/entities/RouteEntityConverter.ts b/src/converters/entities/RouteEntityConverter.ts index 786a7ef..59528f3 100644 --- a/src/converters/entities/RouteEntityConverter.ts +++ b/src/converters/entities/RouteEntityConverter.ts @@ -5,6 +5,7 @@ import { } from "../../jupiterone"; import { NamespaceData } from "../../openshift/types"; import { generateEntityKey } from "../../utils/generateKeys"; +import getTime from "../../utils/getTime"; export function createRouteEntities( namespaces: NamespaceData[], @@ -21,7 +22,7 @@ export function createRouteEntities( uid: route.metadata.uid, namespace: route.metadata.namespace, resourceVersion: route.metadata.resourceVersion, - creationTimestamp: route.metadata.creationTimestamp, + createdOn: getTime(route.metadata.creationTimestamp)!, name: route.metadata.name, host: route.spec.host, }; diff --git a/src/converters/entities/ServiceAccountEntityConverter.test.ts b/src/converters/entities/ServiceAccountEntityConverter.test.ts index c45eb11..e1ad46b 100644 --- a/src/converters/entities/ServiceAccountEntityConverter.test.ts +++ b/src/converters/entities/ServiceAccountEntityConverter.test.ts @@ -46,7 +46,14 @@ test("convert service accounts", async () => { }; const entities = createServiceAccountEntities([ - { routes: [], serviceAccounts, services: [], pods: [], project }, + { + routes: [], + serviceAccounts, + services: [], + pods: [], + project, + deployments: [], + }, ]); expect(entities).toEqual([ @@ -54,7 +61,7 @@ test("convert service accounts", async () => { _class: "User", _key: "openshift_service_account_c077817b-61bf-11e9-b220-0a2a2b777307", _type: "openshift_service_account", - creationTimestamp: "2019-04-18T09:53:00Z", + createdOn: 1555581180000, displayName: "builder", name: "builder", namespace: "example_namespace", diff --git a/src/converters/entities/ServiceAccountEntityConverter.ts b/src/converters/entities/ServiceAccountEntityConverter.ts index 47ae1f1..b28cc78 100644 --- a/src/converters/entities/ServiceAccountEntityConverter.ts +++ b/src/converters/entities/ServiceAccountEntityConverter.ts @@ -5,6 +5,7 @@ import { } from "../../jupiterone"; import { NamespaceData } from "../../openshift/types"; import { generateEntityKey } from "../../utils/generateKeys"; +import getTime from "../../utils/getTime"; export function createServiceAccountEntities( namespaces: NamespaceData[], @@ -25,7 +26,7 @@ export function createServiceAccountEntities( uid: srvAcc.metadata.uid, namespace: srvAcc.metadata.namespace, resourceVersion: srvAcc.metadata.resourceVersion, - creationTimestamp: srvAcc.metadata.creationTimestamp, + createdOn: getTime(srvAcc.metadata.creationTimestamp)!, name: srvAcc.metadata.name, }; diff --git a/src/converters/entities/ServiceEntityConverter.test.ts b/src/converters/entities/ServiceEntityConverter.test.ts index f9de189..21b4323 100644 --- a/src/converters/entities/ServiceEntityConverter.test.ts +++ b/src/converters/entities/ServiceEntityConverter.test.ts @@ -60,7 +60,14 @@ test("convert services", async () => { }; const entities = createServiceEntities([ - { routes: [], serviceAccounts: [], services, pods: [], project }, + { + routes: [], + serviceAccounts: [], + services, + pods: [], + project, + deployments: [], + }, ]); expect(entities).toEqual([ @@ -69,7 +76,7 @@ test("convert services", async () => { _key: "openshift_service_c3ee12dc-61bf-11e9-ad62-0a69cdf75e6f", _type: "openshift_service", clusterIP: "172.30.122.71", - creationTimestamp: "2019-04-18T09:53:06Z", + createdOn: 1555581186000, displayName: "jenkins", name: "jenkins", namespace: "example_namespace", diff --git a/src/converters/entities/ServiceEntityConverter.ts b/src/converters/entities/ServiceEntityConverter.ts index 2fb8b6e..83003af 100644 --- a/src/converters/entities/ServiceEntityConverter.ts +++ b/src/converters/entities/ServiceEntityConverter.ts @@ -5,6 +5,7 @@ import { } from "../../jupiterone"; import { NamespaceData } from "../../openshift/types"; import { generateEntityKey } from "../../utils/generateKeys"; +import getTime from "../../utils/getTime"; export function createServiceEntities( namespaces: NamespaceData[], @@ -21,7 +22,7 @@ export function createServiceEntities( uid: srv.metadata.uid, namespace: srv.metadata.namespace, resourceVersion: srv.metadata.resourceVersion, - creationTimestamp: srv.metadata.creationTimestamp, + createdOn: getTime(srv.metadata.creationTimestamp)!, name: srv.metadata.name, clusterIP: srv.spec.clusterIP, type: srv.spec.type, diff --git a/src/converters/entities/UserEntityConverter.test.ts b/src/converters/entities/UserEntityConverter.test.ts index 1a1d52c..257e4c9 100644 --- a/src/converters/entities/UserEntityConverter.test.ts +++ b/src/converters/entities/UserEntityConverter.test.ts @@ -34,10 +34,9 @@ test("convert users", async () => { _class: "User", _key: "openshift_user_ad550b5e-5bb8-11e9-a9f5-4e620801d617", _type: "openshift_user", - creationTimestamp: "2019-04-10T17:47:15Z", + createdOn: 1554918435000, displayName: "developer", fullName: "", - generation: undefined, resourceVersion: "2142", uid: "ad550b5e-5bb8-11e9-a9f5-4e620801d617", }, @@ -45,10 +44,9 @@ test("convert users", async () => { _class: "User", _key: "openshift_user_eabd5afd-5c45-11e9-82b7-4e620801d617", _type: "openshift_user", - creationTimestamp: "2019-04-11T10:38:17Z", + createdOn: 1554979097000, displayName: "test-user", fullName: "User Test", - generation: undefined, resourceVersion: "47231", uid: "eabd5afd-5c45-11e9-82b7-4e620801d617", }, diff --git a/src/converters/entities/UserEntityConverter.ts b/src/converters/entities/UserEntityConverter.ts index 2361127..376a408 100644 --- a/src/converters/entities/UserEntityConverter.ts +++ b/src/converters/entities/UserEntityConverter.ts @@ -6,6 +6,7 @@ import { import { User } from "../../openshift/types"; import { generateEntityKey } from "../../utils/generateKeys"; +import getTime from "../../utils/getTime"; export function createUserEntities(data: User[]): UserEntity[] { return data.map(u => { @@ -18,7 +19,7 @@ export function createUserEntities(data: User[]): UserEntity[] { fullName: u.fullName || "", generation: u.metadata.generation, resourceVersion: u.metadata.resourceVersion, - creationTimestamp: u.metadata.creationTimestamp, + createdOn: getTime(u.metadata.creationTimestamp)!, }; return user; diff --git a/src/converters/entities/index.ts b/src/converters/entities/index.ts index 2803bc6..7bf19b1 100644 --- a/src/converters/entities/index.ts +++ b/src/converters/entities/index.ts @@ -1,6 +1,7 @@ export { createAccountEntity } from "./AccountEntityConverter"; export { createGroupEntities } from "./GroupEntityConverter"; export { createContainerEntities } from "./ContainerEntityConverter"; +export { createDeploymentEntities } from "./DeploymentEntityConverter"; export { createPodEntities } from "./PodEntityConverter"; export { createProjectEntities } from "./ProjectEntityConverter"; export { createRouteEntities } from "./RouteEntityConverter"; diff --git a/src/converters/relationships/NamespaceRelationshipsConverter.test.ts b/src/converters/relationships/NamespaceRelationshipsConverter.test.ts index 3f8aea3..5341a6b 100644 --- a/src/converters/relationships/NamespaceRelationshipsConverter.test.ts +++ b/src/converters/relationships/NamespaceRelationshipsConverter.test.ts @@ -1,5 +1,11 @@ import { readFileSync } from "fs"; import { + DEPLOYMENT_ENTITY_TYPE, + POD_ENTITY_TYPE, + PROJECT_DEPLOYMENT_RELATIONSHIP_CLASS, + PROJECT_DEPLOYMENT_RELATIONSHIP_TYPE, + PROJECT_POD_RELATIONSHIP_CLASS, + PROJECT_POD_RELATIONSHIP_TYPE, PROJECT_ROUTE_RELATIONSHIP_CLASS, PROJECT_ROUTE_RELATIONSHIP_TYPE, PROJECT_SERVICE_ACCOUNT_RELATIONSHIP_CLASS, @@ -44,6 +50,22 @@ test("convert namespace relationships", async () => { PROJECT_SERVICE_ACCOUNT_RELATIONSHIP_CLASS, ); + const pods = createNamespaceRelationships( + namespaces, + "pods", + POD_ENTITY_TYPE, + PROJECT_POD_RELATIONSHIP_TYPE, + PROJECT_POD_RELATIONSHIP_CLASS, + ); + + const deployments = createNamespaceRelationships( + namespaces, + "deployments", + DEPLOYMENT_ENTITY_TYPE, + PROJECT_DEPLOYMENT_RELATIONSHIP_TYPE, + PROJECT_DEPLOYMENT_RELATIONSHIP_CLASS, + ); + expect(routes).toEqual([ { _class: "HAS", @@ -107,4 +129,64 @@ test("convert namespace relationships", async () => { _type: "openshift_project_has_service_account", }, ]); + expect(pods).toEqual([ + { + _class: "HAS", + _fromEntityKey: "openshift_project_39537998-5bb8-11e9-8c30-4e620801d617", + _key: + "openshift_project_39537998-5bb8-11e9-8c30-4e620801d617_has_openshift_pod_c764e7d4-61bf-11e9-b220-0a2a2b777307", + _toEntityKey: "openshift_pod_c764e7d4-61bf-11e9-b220-0a2a2b777307", + _type: "openshift_project_has_pod", + }, + { + _class: "HAS", + _fromEntityKey: "openshift_project_39537998-5bb8-11e9-8c30-4e620801d617", + _key: + "openshift_project_39537998-5bb8-11e9-8c30-4e620801d617_has_openshift_pod_c764e7d4-61bf-11e9-b220-0a2a2b777307", + _toEntityKey: "openshift_pod_c764e7d4-61bf-11e9-b220-0a2a2b777307", + _type: "openshift_project_has_pod", + }, + { + _class: "HAS", + _fromEntityKey: "openshift_project_39537998-5bb8-11e9-8c30-4e620801d617", + _key: + "openshift_project_39537998-5bb8-11e9-8c30-4e620801d617_has_openshift_pod_c3ee12dc-61bf-11e9-ad62-000000000000", + _toEntityKey: "openshift_pod_c3ee12dc-61bf-11e9-ad62-000000000000", + _type: "openshift_project_has_pod", + }, + { + _class: "HAS", + _fromEntityKey: "openshift_project_39537998-5bb8-11e9-8c30-4e620801d617", + _key: + "openshift_project_39537998-5bb8-11e9-8c30-4e620801d617_has_openshift_pod_c3ee12dc-61bf-11e9-ad62-000000000000", + _toEntityKey: "openshift_pod_c3ee12dc-61bf-11e9-ad62-000000000000", + _type: "openshift_project_has_pod", + }, + { + _class: "HAS", + _fromEntityKey: "openshift_project_39537998-5bb8-11e9-8c30-4e620801d617", + _key: + "openshift_project_39537998-5bb8-11e9-8c30-4e620801d617_has_openshift_pod_c3ee12dc-61bf-11e9-ad62-000000000000", + _toEntityKey: "openshift_pod_c3ee12dc-61bf-11e9-ad62-000000000000", + _type: "openshift_project_has_pod", + }, + ]); + expect(deployments).toEqual([ + { + _class: "HAS", + _fromEntityKey: "openshift_project_39537998-5bb8-11e9-8c30-4e620801d617", + _key: + "openshift_project_39537998-5bb8-11e9-8c30-4e620801d617_has_openshift_deployment_c46d03c5-61bf-11e9-b220-0a2a2b777307", + _toEntityKey: "openshift_deployment_c46d03c5-61bf-11e9-b220-0a2a2b777307", + _type: "openshift_project_has_deployment", + }, + { + _class: "HAS", + _fromEntityKey: "openshift_project_39537998-5bb8-11e9-8c30-4e620801d617", + _key: + "openshift_project_39537998-5bb8-11e9-8c30-4e620801d617_has_openshift_deployment_69d811b4-77d1-11e9-9823-0a69cdf75e6f", + _toEntityKey: "openshift_deployment_69d811b4-77d1-11e9-9823-0a69cdf75e6f", + _type: "openshift_project_has_deployment", + }, + ]); }); diff --git a/src/converters/relationships/NamespaceRelationshipsConverter.ts b/src/converters/relationships/NamespaceRelationshipsConverter.ts index b76f1ee..3ea959a 100644 --- a/src/converters/relationships/NamespaceRelationshipsConverter.ts +++ b/src/converters/relationships/NamespaceRelationshipsConverter.ts @@ -11,7 +11,7 @@ import { RelationshipFromIntegration } from "@jupiterone/jupiter-managed-integra export function createNamespaceRelationships( namespaces: NamespaceData[], - objectKey: "routes" | "services" | "serviceAccounts" | "pods", + objectKey: "routes" | "services" | "serviceAccounts" | "pods" | "deployments", objectEntityType: string, relationshipType: string, relationshipClass: string, diff --git a/src/converters/relationships/PodContainerRelationshipConverter.test.ts b/src/converters/relationships/PodContainerRelationshipConverter.test.ts index 55668cf..d3f6a9c 100644 --- a/src/converters/relationships/PodContainerRelationshipConverter.test.ts +++ b/src/converters/relationships/PodContainerRelationshipConverter.test.ts @@ -30,5 +30,14 @@ test("convert namespace relationships", async () => { "openshift_container_c3ee12dc-61bf-11e9-ad62-000000000000_rails", _type: "openshift_pod_has_container", }, + { + _class: "HAS", + _fromEntityKey: "openshift_pod_c3ee12dc-61bf-11e9-ad62-000000000000", + _key: + "openshift_pod_c3ee12dc-61bf-11e9-ad62-000000000000_has_openshift_container_c3ee12dc-61bf-11e9-ad62-000000000000_test", + _toEntityKey: + "openshift_container_c3ee12dc-61bf-11e9-ad62-000000000000_test", + _type: "openshift_pod_has_container", + }, ]); }); diff --git a/src/converters/relationships/PodDeploymentRelationshipConverter.test.ts b/src/converters/relationships/PodDeploymentRelationshipConverter.test.ts new file mode 100644 index 0000000..ef81403 --- /dev/null +++ b/src/converters/relationships/PodDeploymentRelationshipConverter.test.ts @@ -0,0 +1,40 @@ +import { readFileSync } from "fs"; +import { NamespaceData } from "../../openshift/types"; +import { createPodDeploymentRelationships } from "./PodDeploymentRelationshipConverter"; + +test("convert namespace relationships", async () => { + const file = readFileSync( + `${__dirname}/../../../test/fixtures/namespace-objects.json`, + ); + const namespace = JSON.parse(file.toString()) as NamespaceData; + const namespaces = [namespace]; + + const relationships = createPodDeploymentRelationships(namespaces); + + expect(relationships).toEqual([ + { + _class: "HAS", + _fromEntityKey: "openshift_pod_c764e7d4-61bf-11e9-b220-0a2a2b777307", + _key: + "openshift_pod_c764e7d4-61bf-11e9-b220-0a2a2b777307_has_openshift_deployment_c46d03c5-61bf-11e9-b220-0a2a2b777307", + _toEntityKey: "openshift_deployment_c46d03c5-61bf-11e9-b220-0a2a2b777307", + _type: "openshift_pod_has_deployment", + }, + { + _class: "HAS", + _fromEntityKey: "openshift_pod_c3ee12dc-61bf-11e9-ad62-000000000000", + _key: + "openshift_pod_c3ee12dc-61bf-11e9-ad62-000000000000_has_openshift_deployment_c46d03c5-61bf-11e9-b220-0a2a2b777307", + _toEntityKey: "openshift_deployment_c46d03c5-61bf-11e9-b220-0a2a2b777307", + _type: "openshift_pod_has_deployment", + }, + { + _class: "HAS", + _fromEntityKey: "openshift_pod_c3ee12dc-61bf-11e9-ad62-000000000000", + _key: + "openshift_pod_c3ee12dc-61bf-11e9-ad62-000000000000_has_openshift_deployment_c46d03c5-61bf-11e9-b220-0a2a2b777307", + _toEntityKey: "openshift_deployment_c46d03c5-61bf-11e9-b220-0a2a2b777307", + _type: "openshift_pod_has_deployment", + }, + ]); +}); diff --git a/src/converters/relationships/PodDeploymentRelationshipConverter.ts b/src/converters/relationships/PodDeploymentRelationshipConverter.ts new file mode 100644 index 0000000..cc96ca9 --- /dev/null +++ b/src/converters/relationships/PodDeploymentRelationshipConverter.ts @@ -0,0 +1,57 @@ +import { DEPLOYMENT_ENTITY_TYPE, POD_ENTITY_TYPE } from "../../jupiterone"; +import { + POD_DEPLOYMENT_RELATIONSHIP_CLASS, + POD_DEPLOYMENT_RELATIONSHIP_TYPE, + PodDeploymentRelationship, +} from "../../jupiterone/relationships"; +import { NamespaceData, Pod } from "../../openshift/types"; + +import { + generateEntityKey, + generateRelationshipKey, +} from "../../utils/generateKeys"; + +export function createPodDeploymentRelationships( + namespaces: NamespaceData[], +): PodDeploymentRelationship[] { + const defaultRelationships: PodDeploymentRelationship[] = []; + + return namespaces.reduce((relationships, namespace) => { + return [...relationships, ...createNsPodDeploymentRelationships(namespace)]; + }, defaultRelationships); +} + +function createNsPodDeploymentRelationships( + namespace: NamespaceData, +): PodDeploymentRelationship[] { + const defaultRelationships: PodDeploymentRelationship[] = []; + + return namespace.pods.reduce((relationships, pod) => { + return [...relationships, ...createPodRelationships(pod)]; + }, defaultRelationships); +} + +function createPodRelationships(pod: Pod): PodDeploymentRelationship[] { + if (!pod.metadata.ownerReferences) { + return []; + } + return pod.metadata.ownerReferences.map(deployment => { + const parentKey = generateEntityKey(POD_ENTITY_TYPE, pod.metadata.uid); + const childKey = generateEntityKey(DEPLOYMENT_ENTITY_TYPE, deployment.uid); + const relationshipKey = generateRelationshipKey( + parentKey, + childKey, + POD_DEPLOYMENT_RELATIONSHIP_CLASS, + ); + + const relationship: PodDeploymentRelationship = { + _class: POD_DEPLOYMENT_RELATIONSHIP_CLASS, + _fromEntityKey: parentKey, + _key: relationshipKey, + _type: POD_DEPLOYMENT_RELATIONSHIP_TYPE, + _toEntityKey: childKey, + }; + + return relationship; + }); +} diff --git a/src/converters/relationships/index.ts b/src/converters/relationships/index.ts index c8c6478..28580a2 100644 --- a/src/converters/relationships/index.ts +++ b/src/converters/relationships/index.ts @@ -17,3 +17,6 @@ export { export { createPodContainerRelationships, } from "./PodContainerRelationshipConverter"; +export { + createPodDeploymentRelationships, +} from "./PodDeploymentRelationshipConverter"; diff --git a/src/executionHandler.test.ts b/src/executionHandler.test.ts index 820245e..7c54c44 100644 --- a/src/executionHandler.test.ts +++ b/src/executionHandler.test.ts @@ -65,6 +65,7 @@ beforeEach(() => { fetchNamespaceRoutes: jest.fn().mockReturnValue([]), fetchNamespacePods: jest.fn().mockReturnValue([]), fetchNamespaceServices: jest.fn().mockReturnValue([]), + fetchNamespaceDeployments: jest.fn().mockReturnValue([]), } as unknown) as OpenShiftClient; executionContext = ({ diff --git a/src/jupiterone/entities/DeploymentEntity.ts b/src/jupiterone/entities/DeploymentEntity.ts new file mode 100644 index 0000000..bd01831 --- /dev/null +++ b/src/jupiterone/entities/DeploymentEntity.ts @@ -0,0 +1,10 @@ +import { EntityFromIntegration } from "@jupiterone/jupiter-managed-integration-sdk"; + +export const DEPLOYMENT_ENTITY_TYPE = "openshift_deployment"; +export const DEPLOYMENT_ENTITY_CLASS = ["Deployment", "Group"]; + +export interface DeploymentEntity extends EntityFromIntegration { + name: string; + isActive: boolean; + createdOn: number; +} diff --git a/src/jupiterone/entities/GroupEntity.ts b/src/jupiterone/entities/GroupEntity.ts index 7891dc1..0c8cf82 100644 --- a/src/jupiterone/entities/GroupEntity.ts +++ b/src/jupiterone/entities/GroupEntity.ts @@ -8,5 +8,5 @@ export interface GroupEntity extends EntityFromIntegration { resourceVersion: string; generation?: number; namespace?: string; - creationTimestamp: string; + createdOn: number; } diff --git a/src/jupiterone/entities/PodEntity.ts b/src/jupiterone/entities/PodEntity.ts index 93814f6..20dc4ef 100644 --- a/src/jupiterone/entities/PodEntity.ts +++ b/src/jupiterone/entities/PodEntity.ts @@ -8,11 +8,11 @@ export interface PodEntity extends EntityFromIntegration { name: string; resourceVersion: string; namespace?: string; - creationTimestamp: string; + createdOn: number; nodeName: string; phase: string; hostIP: string; podIP: string; - startTime: string; + startTime: number; qosClass: string; } diff --git a/src/jupiterone/entities/ProjectEntity.ts b/src/jupiterone/entities/ProjectEntity.ts index d743455..64e303e 100644 --- a/src/jupiterone/entities/ProjectEntity.ts +++ b/src/jupiterone/entities/ProjectEntity.ts @@ -8,5 +8,5 @@ export interface ProjectEntity extends EntityFromIntegration { resourceVersion: string; generation?: number; namespace?: string; - creationTimestamp: string; + createdOn: number; } diff --git a/src/jupiterone/entities/RouteEntity.ts b/src/jupiterone/entities/RouteEntity.ts index f070bbd..a5a6dc7 100644 --- a/src/jupiterone/entities/RouteEntity.ts +++ b/src/jupiterone/entities/RouteEntity.ts @@ -8,6 +8,6 @@ export interface RouteEntity extends EntityFromIntegration { name: string; resourceVersion: string; namespace?: string; - creationTimestamp: string; + createdOn: number; host: string; } diff --git a/src/jupiterone/entities/ServiceAccountEntity.ts b/src/jupiterone/entities/ServiceAccountEntity.ts index d0427d6..df0c5d1 100644 --- a/src/jupiterone/entities/ServiceAccountEntity.ts +++ b/src/jupiterone/entities/ServiceAccountEntity.ts @@ -8,5 +8,5 @@ export interface ServiceAccountEntity extends EntityFromIntegration { name: string; resourceVersion: string; namespace?: string; - creationTimestamp: string; + createdOn: number; } diff --git a/src/jupiterone/entities/ServiceEntity.ts b/src/jupiterone/entities/ServiceEntity.ts index a5b30d1..0b37dc9 100644 --- a/src/jupiterone/entities/ServiceEntity.ts +++ b/src/jupiterone/entities/ServiceEntity.ts @@ -8,7 +8,7 @@ export interface ServiceEntity extends EntityFromIntegration { name: string; resourceVersion: string; namespace?: string; - creationTimestamp: string; + createdOn: number; clusterIP: string; type: string; } diff --git a/src/jupiterone/entities/UserEntity.ts b/src/jupiterone/entities/UserEntity.ts index 32cb05c..4900aa5 100644 --- a/src/jupiterone/entities/UserEntity.ts +++ b/src/jupiterone/entities/UserEntity.ts @@ -8,6 +8,6 @@ export interface UserEntity extends EntityFromIntegration { resourceVersion: string; generation?: number; namespace?: string; - creationTimestamp: string; + createdOn: number; fullName: string; } diff --git a/src/jupiterone/entities/index.ts b/src/jupiterone/entities/index.ts index ac4af3e..2293a15 100644 --- a/src/jupiterone/entities/index.ts +++ b/src/jupiterone/entities/index.ts @@ -7,3 +7,4 @@ export * from "./RouteEntity"; export * from "./ServiceAccountEntity"; export * from "./ServiceEntity"; export * from "./UserEntity"; +export * from "./DeploymentEntity"; diff --git a/src/jupiterone/fetchEntitiesAndRelationships.ts b/src/jupiterone/fetchEntitiesAndRelationships.ts index ed3a77e..5c85830 100644 --- a/src/jupiterone/fetchEntitiesAndRelationships.ts +++ b/src/jupiterone/fetchEntitiesAndRelationships.ts @@ -12,17 +12,21 @@ export interface JupiterOneEntitiesData { routes: Entities.RouteEntity[]; serviceAccounts: Entities.ServiceAccountEntity[]; containers: Entities.ContainerEntity[]; + deployments: Entities.DeploymentEntity[]; } export interface JupiterOneRelationshipsData { accountGroupRelationships: Relationships.AccountGroupRelationship[]; accountProjectRelationships: Relationships.AccountProjectRelationship[]; + projectDeploymentRelationships: Relationships.ProjectDeploymentRelationship[]; + projectPodRelationships: Relationships.ProjectPodRelationship[]; projectRouteRelationships: Relationships.ProjectRouteRelationship[]; projectServiceAccountRelationships: Relationships.ProjectServiceAccountRelationship[]; projectServiceRelationships: Relationships.ProjectServiceRelationship[]; routeServiceRelationships: Relationships.RouteServiceRelationship[]; servicePodRelationships: Relationships.ServicePodRelationship[]; podContainerRelationships: Relationships.PodContainerRelationship[]; + podDeploymentRelationships: Relationships.PodDeploymentRelationship[]; userGroupRelationships: Relationships.UserGroupRelationship[]; } @@ -56,7 +60,14 @@ async function fetchEntities( ), ]); - const [projects, pods, services, routes, containers] = await Promise.all([ + const [ + projects, + pods, + services, + routes, + containers, + deployments, + ] = await Promise.all([ graph.findEntitiesByType( Entities.PROJECT_ENTITY_TYPE, ), @@ -68,6 +79,9 @@ async function fetchEntities( graph.findEntitiesByType( Entities.CONTAINER_ENTITY_TYPE, ), + graph.findEntitiesByType( + Entities.DEPLOYMENT_ENTITY_TYPE, + ), ]); return { @@ -80,6 +94,7 @@ async function fetchEntities( routes, serviceAccounts, containers, + deployments, }; } @@ -103,12 +118,15 @@ export async function fetchRelationships( ]); const [ + projectDeploymentRelationships, + projectPodRelationships, projectRouteRelationships, projectServiceAccountRelationships, projectServiceRelationships, routeServiceRelationships, servicePodRelationships, podContainerRelationships, + podDeploymentRelationships, ] = await Promise.all([ graph.findRelationshipsByType( Relationships.PROJECT_ROUTE_RELATIONSHIP_TYPE, @@ -128,11 +146,22 @@ export async function fetchRelationships( graph.findRelationshipsByType( Relationships.POD_CONTAINER_RELATIONSHIP_TYPE, ), + graph.findRelationshipsByType( + Relationships.POD_DEPLOYMENT_RELATIONSHIP_TYPE, + ), + graph.findRelationshipsByType( + Relationships.PROJECT_DEPLOYMENT_RELATIONSHIP_TYPE, + ), + graph.findRelationshipsByType( + Relationships.PROJECT_POD_RELATIONSHIP_TYPE, + ), ]); return { accountGroupRelationships, accountProjectRelationships, + projectDeploymentRelationships, + projectPodRelationships, projectRouteRelationships, projectServiceAccountRelationships, projectServiceRelationships, @@ -140,5 +169,6 @@ export async function fetchRelationships( servicePodRelationships, userGroupRelationships, podContainerRelationships, + podDeploymentRelationships, }; } diff --git a/src/jupiterone/relationships/PodDeploymentRelationship.ts b/src/jupiterone/relationships/PodDeploymentRelationship.ts new file mode 100644 index 0000000..bfe8b62 --- /dev/null +++ b/src/jupiterone/relationships/PodDeploymentRelationship.ts @@ -0,0 +1,8 @@ +import { RelationshipFromIntegration } from "@jupiterone/jupiter-managed-integration-sdk"; + +export interface PodDeploymentRelationship extends RelationshipFromIntegration { + id?: number; +} + +export const POD_DEPLOYMENT_RELATIONSHIP_TYPE = "openshift_pod_has_deployment"; +export const POD_DEPLOYMENT_RELATIONSHIP_CLASS = "HAS"; diff --git a/src/jupiterone/relationships/ProjectDeploymentRelationship.ts b/src/jupiterone/relationships/ProjectDeploymentRelationship.ts new file mode 100644 index 0000000..229d2d7 --- /dev/null +++ b/src/jupiterone/relationships/ProjectDeploymentRelationship.ts @@ -0,0 +1,10 @@ +import { RelationshipFromIntegration } from "@jupiterone/jupiter-managed-integration-sdk"; + +export interface ProjectDeploymentRelationship + extends RelationshipFromIntegration { + id?: number; +} + +export const PROJECT_DEPLOYMENT_RELATIONSHIP_TYPE = + "openshift_project_has_deployment"; +export const PROJECT_DEPLOYMENT_RELATIONSHIP_CLASS = "HAS"; diff --git a/src/jupiterone/relationships/ProjectPodRelationship.ts b/src/jupiterone/relationships/ProjectPodRelationship.ts new file mode 100644 index 0000000..e91233b --- /dev/null +++ b/src/jupiterone/relationships/ProjectPodRelationship.ts @@ -0,0 +1,8 @@ +import { RelationshipFromIntegration } from "@jupiterone/jupiter-managed-integration-sdk"; + +export interface ProjectPodRelationship extends RelationshipFromIntegration { + id?: number; +} + +export const PROJECT_POD_RELATIONSHIP_TYPE = "openshift_project_has_pod"; +export const PROJECT_POD_RELATIONSHIP_CLASS = "HAS"; diff --git a/src/jupiterone/relationships/index.ts b/src/jupiterone/relationships/index.ts index 06f1a00..b2c6050 100644 --- a/src/jupiterone/relationships/index.ts +++ b/src/jupiterone/relationships/index.ts @@ -1,9 +1,12 @@ export * from "./AccountGroupRelationship"; export * from "./AccountProjectRelationship"; +export * from "./ProjectDeploymentRelationship"; +export * from "./ProjectPodRelationship"; export * from "./ProjectRouteRelationship"; export * from "./ProjectServiceAccountRelationship"; export * from "./ProjectServiceRelationship"; export * from "./PodContainerRelationship"; +export * from "./PodDeploymentRelationship"; export * from "./RouteServiceRelationship"; export * from "./ServicePodRelationship"; export * from "./UserGroupRelationship"; diff --git a/src/openshift/OpenShiftClient.test.ts b/src/openshift/OpenShiftClient.test.ts index 83f1175..9dc00cc 100644 --- a/src/openshift/OpenShiftClient.test.ts +++ b/src/openshift/OpenShiftClient.test.ts @@ -135,6 +135,16 @@ describe("OpenShiftClient fetch ok data", () => { nockDone(); }); + test("fetchNamespaceDeployments ok", async () => { + const { nockDone } = await nock.back("namespace-deployments-ok.json", { + before: prepareScope, + }); + const client = await getAuthenticatedClient(); + const response = await client.fetchNamespaceDeployments(EXAMPLE_NAMESPACE); + expect(response.length).not.toEqual(0); + nockDone(); + }); + test("fetchNamespaceRoutes ok", async () => { const { nockDone } = await nock.back("namespace-routes-ok.json", { before: prepareScope, diff --git a/src/openshift/OpenShiftClient.ts b/src/openshift/OpenShiftClient.ts index 73d6d8b..183d125 100644 --- a/src/openshift/OpenShiftClient.ts +++ b/src/openshift/OpenShiftClient.ts @@ -2,6 +2,7 @@ const openshiftRestClient = require("openshift-rest-client").OpenshiftClient; import { + Deployment, Group, Pod, Project, @@ -90,6 +91,18 @@ export default class OpenShiftClient { return routes; } + public async fetchNamespaceDeployments( + namespace: string, + ): Promise { + const { + body: { items: deployments }, + } = await this.restClient.api.v1 + .namespaces(namespace) + .replicationcontrollers.get(); + + return deployments; + } + public async fetchNamespacePods(namespace: string): Promise { const { body: { items: pods }, diff --git a/src/openshift/fetchOpenshiftData.ts b/src/openshift/fetchOpenshiftData.ts index ed70d33..95b2c6e 100644 --- a/src/openshift/fetchOpenshiftData.ts +++ b/src/openshift/fetchOpenshiftData.ts @@ -22,11 +22,18 @@ async function fetchNamespaceData( client: OpenShiftClient, ): Promise { const namespace = project.metadata.name; - const [serviceAccounts, services, pods, routes] = await Promise.all([ + const [ + serviceAccounts, + services, + pods, + routes, + deployments, + ] = await Promise.all([ client.fetchNamespaceServiceAccounts(namespace), client.fetchNamespaceServices(namespace), client.fetchNamespacePods(namespace), client.fetchNamespaceRoutes(namespace), + client.fetchNamespaceDeployments(namespace), ]); return { @@ -35,5 +42,6 @@ async function fetchNamespaceData( services, pods, routes, + deployments, }; } diff --git a/src/openshift/types.ts b/src/openshift/types.ts index e4d6f01..1f15eac 100644 --- a/src/openshift/types.ts +++ b/src/openshift/types.ts @@ -12,6 +12,16 @@ interface Metadata { labels?: { [name: string]: string; }; + ownerReferences?: OwnerReference[]; +} + +export interface OwnerReference { + apiVersion: string; + kind: string; + name: string; + uid: string; + controller: boolean; + blockOwnerDeletion: boolean; } export interface OpenshiftEntity { @@ -73,9 +83,11 @@ interface Volume { defaultMode: number; }; } + interface Secret { name: string; } + interface EnvVar { name: string; value: string; @@ -195,6 +207,24 @@ export interface Project extends OpenshiftEntity { }; } +export interface Deployment extends OpenshiftEntity { + spec: { + replicas: number; + template: { + spec: { + containers: Container[]; + }; + }; + }; + status: { + replicas: number; + fullyLabeledReplicas?: number; + readyReplicas?: number; + availableReplicas?: number; + observedGeneration?: number; + }; +} + export interface ServiceAccount extends OpenshiftEntity { secrets: Secret[]; imagePullSecrets: Secret[]; @@ -239,4 +269,5 @@ export interface NamespaceData { services: Service[]; pods: Pod[]; routes: Route[]; + deployments: Deployment[]; } diff --git a/src/persister/publishChanges.ts b/src/persister/publishChanges.ts index e70b664..576734c 100644 --- a/src/persister/publishChanges.ts +++ b/src/persister/publishChanges.ts @@ -7,7 +7,17 @@ import { import * as EntityConverters from "../converters/entities"; import * as RelationshipConverters from "../converters/relationships"; +import { + DEPLOYMENT_ENTITY_TYPE, + POD_ENTITY_TYPE, +} from "../jupiterone/entities"; import * as Entities from "../jupiterone/entities"; +import { + PROJECT_DEPLOYMENT_RELATIONSHIP_CLASS, + PROJECT_DEPLOYMENT_RELATIONSHIP_TYPE, + PROJECT_POD_RELATIONSHIP_CLASS, + PROJECT_POD_RELATIONSHIP_TYPE, +} from "../jupiterone/relationships"; import * as Relationships from "../jupiterone/relationships"; import { IntegrationInstance } from "@jupiterone/jupiter-managed-integration-sdk"; @@ -122,6 +132,9 @@ export function convertEntities( openshiftDataModel.namespaces, ), users: EntityConverters.createUserEntities(openshiftDataModel.users), + deployments: EntityConverters.createDeploymentEntities( + openshiftDataModel.namespaces, + ), }; } @@ -155,6 +168,22 @@ export function convertRelationships( Relationships.PROJECT_SERVICE_ACCOUNT_RELATIONSHIP_CLASS, ); + const projectPodRelationships = RelationshipConverters.createNamespaceRelationships( + openshiftDataModel.namespaces, + "pods", + POD_ENTITY_TYPE, + PROJECT_POD_RELATIONSHIP_TYPE, + PROJECT_POD_RELATIONSHIP_CLASS, + ); + + const projectDeploymentRelationships = RelationshipConverters.createNamespaceRelationships( + openshiftDataModel.namespaces, + "deployments", + DEPLOYMENT_ENTITY_TYPE, + PROJECT_DEPLOYMENT_RELATIONSHIP_TYPE, + PROJECT_DEPLOYMENT_RELATIONSHIP_CLASS, + ); + return { accountGroupRelationships: RelationshipConverters.createAccountGroupRelationships( openshiftDataModel.groups, @@ -171,12 +200,17 @@ export function convertRelationships( projectRouteRelationships, projectServiceAccountRelationships, projectServiceRelationships, + projectDeploymentRelationships, + projectPodRelationships, routeServiceRelationships: RelationshipConverters.createRouteServiceRelationships( openshiftDataModel.namespaces, ), podContainerRelationships: RelationshipConverters.createPodContainerRelationships( openshiftDataModel.namespaces, ), + podDeploymentRelationships: RelationshipConverters.createPodDeploymentRelationships( + openshiftDataModel.namespaces, + ), servicePodRelationships: RelationshipConverters.createServicePodRelationships( openshiftDataModel.namespaces, ), diff --git a/src/utils/getTime.test.ts b/src/utils/getTime.test.ts new file mode 100644 index 0000000..a979f1b --- /dev/null +++ b/src/utils/getTime.test.ts @@ -0,0 +1,16 @@ +import getTime from "./getTime"; + +test("should return number from string", () => { + const testData: string = "2019-04-22T21:43:53.000Z"; + expect(getTime(testData)).toEqual(1555969433000); +}); + +test("should return number from Date", () => { + const testData: Date = new Date("2019-04-22T21:43:53.000Z"); + expect(getTime(testData)).toEqual(1555969433000); +}); + +test("should return undefined from undefined", () => { + const testData = undefined; + expect(getTime(testData)).toEqual(undefined); +}); diff --git a/src/utils/getTime.ts b/src/utils/getTime.ts new file mode 100644 index 0000000..d26b7c1 --- /dev/null +++ b/src/utils/getTime.ts @@ -0,0 +1,5 @@ +export default function getTime( + time: Date | string | undefined, +): number | undefined { + return time ? new Date(time).getTime() : undefined; +} diff --git a/test/fixtures/namespace-deployments-ok.json b/test/fixtures/namespace-deployments-ok.json new file mode 100644 index 0000000..918741a --- /dev/null +++ b/test/fixtures/namespace-deployments-ok.json @@ -0,0 +1,327 @@ +[ + { + "scope": "https://api.starter-us-west-2.openshift.com:443", + "method": "GET", + "path": "/api/v1/namespaces/example_namespace/replicationcontrollers", + "body": "", + "status": 200, + "response": { + "kind": "ReplicationController", + "apiVersion": "v1", + "metadata": { + "selfLink": "/api/v1/namespaces/example_namespace/replicationcontrollers", + "resourceVersion": "3549052628" + }, + "items": [ + { + "metadata": { + "name": "jenkins-1", + "namespace": "test-jenkins-dualboot", + "selfLink": "/api/v1/namespaces/test-jenkins-dualboot/replicationcontrollers/jenkins-1", + "uid": "c46d03c5-61bf-11e9-b220-0a2a2b777307", + "resourceVersion": "3601372850", + "generation": 3, + "creationTimestamp": "2019-04-18T09:53:07Z", + "labels": { + "app": "jenkins-persistent", + "openshift.io/deployment-config.name": "jenkins", + "template": "jenkins-persistent-template" + }, + "annotations": { + "openshift.io/deployer-pod.completed-at": "2019-04-18 09:56:04 +0000 UTC", + "openshift.io/deployer-pod.created-at": "2019-04-18 09:53:07 +0000 UTC", + "openshift.io/deployer-pod.name": "jenkins-1-deploy", + "openshift.io/deployment-config.latest-version": "1", + "openshift.io/deployment-config.name": "jenkins", + "openshift.io/deployment.phase": "Complete", + "openshift.io/deployment.replicas": "1", + "openshift.io/deployment.status-reason": "config change", + "openshift.io/encoded-deployment-config": "{\"kind\":\"DeploymentConfig\",\"apiVersion\":\"apps.openshift.io/v1\",\"metadata\":{\"name\":\"jenkins\",\"namespace\":\"test-jenkins-dualboot\",\"selfLink\":\"/apis/apps.openshift.io/v1/namespaces/test-jenkins-dualboot/deploymentconfigs/jenkins\",\"uid\":\"c3cb3220-61bf-11e9-b220-0a2a2b777307\",\"resourceVersion\":\"3548833710\",\"generation\":2,\"creationTimestamp\":\"2019-04-18T09:53:06Z\",\"labels\":{\"app\":\"jenkins-persistent\",\"template\":\"jenkins-persistent-template\"},\"annotations\":{\"template.alpha.openshift.io/wait-for-ready\":\"true\"}},\"spec\":{\"strategy\":{\"type\":\"Recreate\",\"recreateParams\":{\"timeoutSeconds\":600},\"resources\":{},\"activeDeadlineSeconds\":21600},\"triggers\":[{\"type\":\"ImageChange\",\"imageChangeParams\":{\"automatic\":true,\"containerNames\":[\"jenkins\"],\"from\":{\"kind\":\"ImageStreamTag\",\"namespace\":\"openshift\",\"name\":\"jenkins:2\"},\"lastTriggeredImage\":\"docker-registry.default.svc:5000/openshift/jenkins@sha256:3b6ad6c87518f153c6c38c5190f6d95535af3c8210adc8904d6a7f7f4180a36f\"}},{\"type\":\"ConfigChange\"}],\"replicas\":1,\"revisionHistoryLimit\":10,\"test\":false,\"selector\":{\"name\":\"jenkins\"},\"template\":{\"metadata\":{\"creationTimestamp\":null,\"labels\":{\"name\":\"jenkins\"}},\"spec\":{\"volumes\":[{\"name\":\"jenkins-data\",\"persistentVolumeClaim\":{\"claimName\":\"jenkins\"}}],\"containers\":[{\"name\":\"jenkins\",\"image\":\"docker-registry.default.svc:5000/openshift/jenkins@sha256:3b6ad6c87518f153c6c38c5190f6d95535af3c8210adc8904d6a7f7f4180a36f\",\"env\":[{\"name\":\"OPENSHIFT_ENABLE_OAUTH\",\"value\":\"true\"},{\"name\":\"OPENSHIFT_ENABLE_REDIRECT_PROMPT\",\"value\":\"true\"},{\"name\":\"DISABLE_ADMINISTRATIVE_MONITORS\",\"value\":\"true\"},{\"name\":\"KUBERNETES_MASTER\",\"value\":\"https://kubernetes.default:443\"},{\"name\":\"KUBERNETES_TRUST_CERTIFICATES\",\"value\":\"true\"},{\"name\":\"JENKINS_SERVICE_NAME\",\"value\":\"jenkins\"},{\"name\":\"JNLP_SERVICE_NAME\",\"value\":\"jenkins-jnlp\"},{\"name\":\"ENABLE_FATAL_ERROR_LOG_FILE\",\"value\":\"false\"}],\"resources\":{\"limits\":{\"memory\":\"512Mi\"}},\"volumeMounts\":[{\"name\":\"jenkins-data\",\"mountPath\":\"/var/lib/jenkins\"}],\"livenessProbe\":{\"httpGet\":{\"path\":\"/login\",\"port\":8080,\"scheme\":\"HTTP\"},\"initialDelaySeconds\":420,\"timeoutSeconds\":240,\"periodSeconds\":360,\"successThreshold\":1,\"failureThreshold\":2},\"readinessProbe\":{\"httpGet\":{\"path\":\"/login\",\"port\":8080,\"scheme\":\"HTTP\"},\"initialDelaySeconds\":3,\"timeoutSeconds\":240,\"periodSeconds\":10,\"successThreshold\":1,\"failureThreshold\":3},\"terminationMessagePath\":\"/dev/termination-log\",\"terminationMessagePolicy\":\"File\",\"imagePullPolicy\":\"IfNotPresent\",\"securityContext\":{\"capabilities\":{},\"privileged\":false}}],\"restartPolicy\":\"Always\",\"terminationGracePeriodSeconds\":30,\"dnsPolicy\":\"ClusterFirst\",\"serviceAccountName\":\"jenkins\",\"serviceAccount\":\"jenkins\",\"securityContext\":{},\"schedulerName\":\"default-scheduler\"}}},\"status\":{\"latestVersion\":1,\"observedGeneration\":1,\"replicas\":0,\"updatedReplicas\":0,\"availableReplicas\":0,\"unavailableReplicas\":0,\"details\":{\"message\":\"config change\",\"causes\":[{\"type\":\"ConfigChange\"}]},\"conditions\":[{\"type\":\"Available\",\"status\":\"False\",\"lastUpdateTime\":\"2019-04-18T09:53:06Z\",\"lastTransitionTime\":\"2019-04-18T09:53:06Z\",\"message\":\"Deployment config does not have minimum availability.\"}]}}\n" + }, + "ownerReferences": [ + { + "apiVersion": "apps.openshift.io/v1", + "kind": "DeploymentConfig", + "name": "jenkins", + "uid": "c3cb3220-61bf-11e9-b220-0a2a2b777307", + "controller": true, + "blockOwnerDeletion": true + } + ] + }, + "spec": { + "replicas": 0, + "selector": { + "deployment": "jenkins-1", + "deploymentconfig": "jenkins", + "name": "jenkins" + }, + "template": { + "metadata": { + "creationTimestamp": null, + "labels": { + "deployment": "jenkins-1", + "deploymentconfig": "jenkins", + "name": "jenkins" + }, + "annotations": { + "openshift.io/deployment-config.latest-version": "1", + "openshift.io/deployment-config.name": "jenkins", + "openshift.io/deployment.name": "jenkins-1" + } + }, + "spec": { + "volumes": [ + { + "name": "jenkins-data", + "persistentVolumeClaim": { "claimName": "jenkins" } + } + ], + "containers": [ + { + "name": "jenkins", + "image": "docker-registry.default.svc:5000/openshift/jenkins@sha256:3b6ad6c87518f153c6c38c5190f6d95535af3c8210adc8904d6a7f7f4180a36f", + "env": [ + { "name": "OPENSHIFT_ENABLE_OAUTH", "value": "true" }, + { + "name": "OPENSHIFT_ENABLE_REDIRECT_PROMPT", + "value": "true" + }, + { + "name": "DISABLE_ADMINISTRATIVE_MONITORS", + "value": "true" + }, + { + "name": "KUBERNETES_MASTER", + "value": "https://kubernetes.default:443" + }, + { + "name": "KUBERNETES_TRUST_CERTIFICATES", + "value": "true" + }, + { "name": "JENKINS_SERVICE_NAME", "value": "jenkins" }, + { "name": "JNLP_SERVICE_NAME", "value": "jenkins-jnlp" }, + { + "name": "ENABLE_FATAL_ERROR_LOG_FILE", + "value": "false" + } + ], + "resources": { "limits": { "memory": "512Mi" } }, + "volumeMounts": [ + { + "name": "jenkins-data", + "mountPath": "/var/lib/jenkins" + } + ], + "livenessProbe": { + "httpGet": { + "path": "/login", + "port": 8080, + "scheme": "HTTP" + }, + "initialDelaySeconds": 420, + "timeoutSeconds": 240, + "periodSeconds": 360, + "successThreshold": 1, + "failureThreshold": 2 + }, + "readinessProbe": { + "httpGet": { + "path": "/login", + "port": 8080, + "scheme": "HTTP" + }, + "initialDelaySeconds": 3, + "timeoutSeconds": 240, + "periodSeconds": 10, + "successThreshold": 1, + "failureThreshold": 3 + }, + "terminationMessagePath": "/dev/termination-log", + "terminationMessagePolicy": "File", + "imagePullPolicy": "IfNotPresent", + "securityContext": { + "capabilities": {}, + "privileged": false + } + } + ], + "restartPolicy": "Always", + "terminationGracePeriodSeconds": 30, + "dnsPolicy": "ClusterFirst", + "serviceAccountName": "jenkins", + "serviceAccount": "jenkins", + "securityContext": {}, + "schedulerName": "default-scheduler" + } + } + }, + "status": { "replicas": 0, "observedGeneration": 3 } + }, + { + "metadata": { + "name": "jenkins-2", + "namespace": "test-jenkins-dualboot", + "selfLink": "/api/v1/namespaces/test-jenkins-dualboot/replicationcontrollers/jenkins-2", + "uid": "69d811b4-77d1-11e9-9823-0a69cdf75e6f", + "resourceVersion": "3603567139", + "generation": 2, + "creationTimestamp": "2019-05-16T11:54:51Z", + "labels": { + "app": "jenkins-persistent", + "openshift.io/deployment-config.name": "jenkins", + "template": "jenkins-persistent-template" + }, + "annotations": { + "openshift.io/deployer-pod.completed-at": "2019-05-16 11:58:12 +0000 UTC", + "openshift.io/deployer-pod.created-at": "2019-05-16 11:54:51 +0000 UTC", + "openshift.io/deployer-pod.name": "jenkins-2-deploy", + "openshift.io/deployer-pod.started-at": "2019-05-16 11:54:52 +0000 UTC", + "openshift.io/deployment-config.latest-version": "2", + "openshift.io/deployment-config.name": "jenkins", + "openshift.io/deployment.phase": "Complete", + "openshift.io/deployment.replicas": "1", + "openshift.io/deployment.status-reason": "config change", + "openshift.io/encoded-deployment-config": "{\"kind\":\"DeploymentConfig\",\"apiVersion\":\"apps.openshift.io/v1\",\"metadata\":{\"name\":\"jenkins\",\"namespace\":\"test-jenkins-dualboot\",\"selfLink\":\"/apis/apps.openshift.io/v1/namespaces/test-jenkins-dualboot/deploymentconfigs/jenkins\",\"uid\":\"c3cb3220-61bf-11e9-b220-0a2a2b777307\",\"resourceVersion\":\"3601372568\",\"generation\":3,\"creationTimestamp\":\"2019-04-18T09:53:06Z\",\"labels\":{\"app\":\"jenkins-persistent\",\"template\":\"jenkins-persistent-template\"},\"annotations\":{\"template.alpha.openshift.io/wait-for-ready\":\"true\"}},\"spec\":{\"strategy\":{\"type\":\"Recreate\",\"customParams\":{},\"recreateParams\":{\"timeoutSeconds\":500},\"resources\":{},\"activeDeadlineSeconds\":21600},\"triggers\":[{\"type\":\"ImageChange\",\"imageChangeParams\":{\"automatic\":true,\"containerNames\":[\"jenkins\"],\"from\":{\"kind\":\"ImageStreamTag\",\"namespace\":\"openshift\",\"name\":\"jenkins:2\"},\"lastTriggeredImage\":\"docker-registry.default.svc:5000/openshift/jenkins@sha256:3b6ad6c87518f153c6c38c5190f6d95535af3c8210adc8904d6a7f7f4180a36f\"}},{\"type\":\"ConfigChange\"}],\"replicas\":1,\"revisionHistoryLimit\":10,\"test\":false,\"selector\":{\"name\":\"jenkins\"},\"template\":{\"metadata\":{\"creationTimestamp\":null,\"labels\":{\"name\":\"jenkins\"}},\"spec\":{\"volumes\":[{\"name\":\"jenkins-data\",\"persistentVolumeClaim\":{\"claimName\":\"jenkins\"}}],\"containers\":[{\"name\":\"jenkins\",\"image\":\"docker-registry.default.svc:5000/openshift/jenkins@sha256:3b6ad6c87518f153c6c38c5190f6d95535af3c8210adc8904d6a7f7f4180a36f\",\"env\":[{\"name\":\"OPENSHIFT_ENABLE_OAUTH\",\"value\":\"true\"},{\"name\":\"OPENSHIFT_ENABLE_REDIRECT_PROMPT\",\"value\":\"true\"},{\"name\":\"DISABLE_ADMINISTRATIVE_MONITORS\",\"value\":\"true\"},{\"name\":\"KUBERNETES_MASTER\",\"value\":\"https://kubernetes.default:443\"},{\"name\":\"KUBERNETES_TRUST_CERTIFICATES\",\"value\":\"true\"},{\"name\":\"JENKINS_SERVICE_NAME\",\"value\":\"jenkins\"},{\"name\":\"JNLP_SERVICE_NAME\",\"value\":\"jenkins-jnlp\"},{\"name\":\"ENABLE_FATAL_ERROR_LOG_FILE\",\"value\":\"false\"},{\"name\":\"CUSTOM\",\"value\":\"123\"}],\"resources\":{\"limits\":{\"memory\":\"512Mi\"}},\"volumeMounts\":[{\"name\":\"jenkins-data\",\"mountPath\":\"/var/lib/jenkins\"}],\"livenessProbe\":{\"httpGet\":{\"path\":\"/login\",\"port\":8080,\"scheme\":\"HTTP\"},\"initialDelaySeconds\":420,\"timeoutSeconds\":240,\"periodSeconds\":360,\"successThreshold\":1,\"failureThreshold\":2},\"readinessProbe\":{\"httpGet\":{\"path\":\"/login\",\"port\":8080,\"scheme\":\"HTTP\"},\"initialDelaySeconds\":3,\"timeoutSeconds\":240,\"periodSeconds\":10,\"successThreshold\":1,\"failureThreshold\":3},\"terminationMessagePath\":\"/dev/termination-log\",\"terminationMessagePolicy\":\"File\",\"imagePullPolicy\":\"IfNotPresent\",\"securityContext\":{\"capabilities\":{},\"privileged\":false}}],\"restartPolicy\":\"Always\",\"terminationGracePeriodSeconds\":30,\"dnsPolicy\":\"ClusterFirst\",\"serviceAccountName\":\"jenkins\",\"serviceAccount\":\"jenkins\",\"securityContext\":{},\"schedulerName\":\"default-scheduler\"}}},\"status\":{\"latestVersion\":2,\"observedGeneration\":2,\"replicas\":1,\"updatedReplicas\":1,\"availableReplicas\":1,\"unavailableReplicas\":0,\"details\":{\"message\":\"config change\",\"causes\":[{\"type\":\"ConfigChange\"}]},\"conditions\":[{\"type\":\"Progressing\",\"status\":\"True\",\"lastUpdateTime\":\"2019-04-18T09:56:05Z\",\"lastTransitionTime\":\"2019-04-18T09:56:05Z\",\"reason\":\"NewReplicationControllerAvailable\",\"message\":\"replication controller \\\"jenkins-1\\\" successfully rolled out\"},{\"type\":\"Available\",\"status\":\"True\",\"lastUpdateTime\":\"2019-05-15T15:07:31Z\",\"lastTransitionTime\":\"2019-05-15T15:07:31Z\",\"message\":\"Deployment config has minimum availability.\"}],\"readyReplicas\":1}}\n" + }, + "ownerReferences": [ + { + "apiVersion": "apps.openshift.io/v1", + "kind": "DeploymentConfig", + "name": "jenkins", + "uid": "c3cb3220-61bf-11e9-b220-0a2a2b777307", + "controller": true, + "blockOwnerDeletion": true + } + ] + }, + "spec": { + "replicas": 1, + "selector": { + "deployment": "jenkins-2", + "deploymentconfig": "jenkins", + "name": "jenkins" + }, + "template": { + "metadata": { + "creationTimestamp": null, + "labels": { + "deployment": "jenkins-2", + "deploymentconfig": "jenkins", + "name": "jenkins" + }, + "annotations": { + "openshift.io/deployment-config.latest-version": "2", + "openshift.io/deployment-config.name": "jenkins", + "openshift.io/deployment.name": "jenkins-2" + } + }, + "spec": { + "volumes": [ + { + "name": "jenkins-data", + "persistentVolumeClaim": { "claimName": "jenkins" } + } + ], + "containers": [ + { + "name": "jenkins", + "image": "docker-registry.default.svc:5000/openshift/jenkins@sha256:3b6ad6c87518f153c6c38c5190f6d95535af3c8210adc8904d6a7f7f4180a36f", + "env": [ + { "name": "OPENSHIFT_ENABLE_OAUTH", "value": "true" }, + { + "name": "OPENSHIFT_ENABLE_REDIRECT_PROMPT", + "value": "true" + }, + { + "name": "DISABLE_ADMINISTRATIVE_MONITORS", + "value": "true" + }, + { + "name": "KUBERNETES_MASTER", + "value": "https://kubernetes.default:443" + }, + { + "name": "KUBERNETES_TRUST_CERTIFICATES", + "value": "true" + }, + { "name": "JENKINS_SERVICE_NAME", "value": "jenkins" }, + { "name": "JNLP_SERVICE_NAME", "value": "jenkins-jnlp" }, + { + "name": "ENABLE_FATAL_ERROR_LOG_FILE", + "value": "false" + }, + { "name": "CUSTOM", "value": "123" } + ], + "resources": { "limits": { "memory": "512Mi" } }, + "volumeMounts": [ + { + "name": "jenkins-data", + "mountPath": "/var/lib/jenkins" + } + ], + "livenessProbe": { + "httpGet": { + "path": "/login", + "port": 8080, + "scheme": "HTTP" + }, + "initialDelaySeconds": 420, + "timeoutSeconds": 240, + "periodSeconds": 360, + "successThreshold": 1, + "failureThreshold": 2 + }, + "readinessProbe": { + "httpGet": { + "path": "/login", + "port": 8080, + "scheme": "HTTP" + }, + "initialDelaySeconds": 3, + "timeoutSeconds": 240, + "periodSeconds": 10, + "successThreshold": 1, + "failureThreshold": 3 + }, + "terminationMessagePath": "/dev/termination-log", + "terminationMessagePolicy": "File", + "imagePullPolicy": "IfNotPresent", + "securityContext": { + "capabilities": {}, + "privileged": false + } + } + ], + "restartPolicy": "Always", + "terminationGracePeriodSeconds": 30, + "dnsPolicy": "ClusterFirst", + "serviceAccountName": "jenkins", + "serviceAccount": "jenkins", + "securityContext": {}, + "schedulerName": "default-scheduler" + } + } + }, + "status": { + "replicas": 1, + "fullyLabeledReplicas": 1, + "observedGeneration": 2 + } + } + ] + }, + "rawHeaders": [ + "Cache-Control", + "no-store", + "Content-Type", + "application/json", + "Date", + "Thu, 18 Apr 2019 12:23:54 GMT", + "Connection", + "close", + "Transfer-Encoding", + "chunked" + ] + } +] diff --git a/test/fixtures/namespace-objects.json b/test/fixtures/namespace-objects.json index 3a53cc4..8fe3979 100644 --- a/test/fixtures/namespace-objects.json +++ b/test/fixtures/namespace-objects.json @@ -472,6 +472,132 @@ "containerStatuses": [], "qosClass": "Burstable" } + }, + { + "metadata": { + "name": "test-1-vqgxg", + "generateName": "test-1-", + "namespace": "example_namespace", + "selfLink": "/api/v1/namespaces/example_namespace/pods/test-1-vqgxg", + "uid": "c3ee12dc-61bf-11e9-ad62-000000000000", + "resourceVersion": "3548837654", + "creationTimestamp": "2019-04-18T09:53:12Z", + "labels": { + "deployment": "test-1", + "deploymentconfig": "test", + "name": "test" + }, + "annotations": { + "kubernetes.io/limit-ranger": "LimitRanger plugin set: cpu request for container rails; cpu limit for container test", + "openshift.io/deployment-config.latest-version": "1", + "openshift.io/deployment-config.name": "test", + "openshift.io/deployment.name": "test-1", + "openshift.io/scc": "restricted" + } + }, + "spec": { + "volumes": [], + "containers": [ + { + "name": "test", + "image": "docker-registry.default.svc:5000/openshift/test@sha256:3b6ad6c87518f153c6c38c5190f6d95535af3c8210adc8904d6a7f7f4180a36f", + "env": [], + "resources": { + "limits": { + "cpu": "1", + "memory": "512Mi" + }, + "requests": { + "cpu": "20m", + "memory": "256Mi" + } + }, + "volumeMounts": [ + { + "name": "test-data", + "mountPath": "/var/lib/test" + }, + { + "name": "rails-token-w64vt", + "readOnly": true, + "mountPath": "/var/run/secrets/kubernetes.io/serviceaccount" + } + ], + "livenessProbe": { + "httpGet": { + "path": "/login", + "port": 8080, + "scheme": "HTTP" + }, + "initialDelaySeconds": 420, + "timeoutSeconds": 240, + "periodSeconds": 360, + "successThreshold": 1, + "failureThreshold": 2 + }, + "readinessProbe": { + "httpGet": { + "path": "/login", + "port": 8080, + "scheme": "HTTP" + }, + "initialDelaySeconds": 3, + "timeoutSeconds": 240, + "periodSeconds": 10, + "successThreshold": 1, + "failureThreshold": 3 + }, + "terminationMessagePath": "/dev/termination-log", + "terminationMessagePolicy": "File", + "imagePullPolicy": "Always", + "securityContext": { + "capabilities": { + "drop": ["KILL", "MKNOD", "NET_RAW", "SETGID", "SETUID"] + }, + "privileged": false, + "runAsUser": 1034810000 + } + } + ], + "restartPolicy": "Always", + "terminationGracePeriodSeconds": 30, + "dnsPolicy": "ClusterFirst", + "nodeSelector": { + "type": "compute" + }, + "serviceAccountName": "test", + "serviceAccount": "test", + "nodeName": "ip-172-31-56-129.us-west-2.compute.internal", + "securityContext": { + "seLinuxOptions": { + "level": "s0:c187,c14" + }, + "fsGroup": 1034810000 + }, + "imagePullSecrets": [ + { + "name": "test-dockercfg-vbsxs" + } + ], + "schedulerName": "default-scheduler", + "tolerations": [ + { + "key": "node.kubernetes.io/memory-pressure", + "operator": "Exists", + "effect": "NoSchedule" + } + ], + "priority": 0 + }, + "status": { + "phase": "Running", + "conditions": [], + "hostIP": "172.31.56.129", + "podIP": "10.131.29.54", + "startTime": "2019-04-18T09:53:12Z", + "containerStatuses": [], + "qosClass": "Burstable" + } } ], "serviceAccounts": [ @@ -732,5 +858,279 @@ "status": { "phase": "Active" } - } + }, + "deployments": [ + { + "metadata": { + "name": "jenkins-1", + "namespace": "test-jenkins-dualboot", + "selfLink": "/api/v1/namespaces/test-jenkins-dualboot/replicationcontrollers/jenkins-1", + "uid": "c46d03c5-61bf-11e9-b220-0a2a2b777307", + "resourceVersion": "3601372850", + "generation": 3, + "creationTimestamp": "2019-04-18T09:53:07Z", + "labels": { + "app": "jenkins-persistent", + "openshift.io/deployment-config.name": "jenkins", + "template": "jenkins-persistent-template" + }, + "annotations": { + "openshift.io/deployer-pod.completed-at": "2019-04-18 09:56:04 +0000 UTC", + "openshift.io/deployer-pod.created-at": "2019-04-18 09:53:07 +0000 UTC", + "openshift.io/deployer-pod.name": "jenkins-1-deploy", + "openshift.io/deployment-config.latest-version": "1", + "openshift.io/deployment-config.name": "jenkins", + "openshift.io/deployment.phase": "Complete", + "openshift.io/deployment.replicas": "1", + "openshift.io/deployment.status-reason": "config change", + "openshift.io/encoded-deployment-config": "{\"kind\":\"DeploymentConfig\",\"apiVersion\":\"apps.openshift.io/v1\",\"metadata\":{\"name\":\"jenkins\",\"namespace\":\"test-jenkins-dualboot\",\"selfLink\":\"/apis/apps.openshift.io/v1/namespaces/test-jenkins-dualboot/deploymentconfigs/jenkins\",\"uid\":\"c3cb3220-61bf-11e9-b220-0a2a2b777307\",\"resourceVersion\":\"3548833710\",\"generation\":2,\"creationTimestamp\":\"2019-04-18T09:53:06Z\",\"labels\":{\"app\":\"jenkins-persistent\",\"template\":\"jenkins-persistent-template\"},\"annotations\":{\"template.alpha.openshift.io/wait-for-ready\":\"true\"}},\"spec\":{\"strategy\":{\"type\":\"Recreate\",\"recreateParams\":{\"timeoutSeconds\":600},\"resources\":{},\"activeDeadlineSeconds\":21600},\"triggers\":[{\"type\":\"ImageChange\",\"imageChangeParams\":{\"automatic\":true,\"containerNames\":[\"jenkins\"],\"from\":{\"kind\":\"ImageStreamTag\",\"namespace\":\"openshift\",\"name\":\"jenkins:2\"},\"lastTriggeredImage\":\"docker-registry.default.svc:5000/openshift/jenkins@sha256:3b6ad6c87518f153c6c38c5190f6d95535af3c8210adc8904d6a7f7f4180a36f\"}},{\"type\":\"ConfigChange\"}],\"replicas\":1,\"revisionHistoryLimit\":10,\"test\":false,\"selector\":{\"name\":\"jenkins\"},\"template\":{\"metadata\":{\"creationTimestamp\":null,\"labels\":{\"name\":\"jenkins\"}},\"spec\":{\"volumes\":[{\"name\":\"jenkins-data\",\"persistentVolumeClaim\":{\"claimName\":\"jenkins\"}}],\"containers\":[{\"name\":\"jenkins\",\"image\":\"docker-registry.default.svc:5000/openshift/jenkins@sha256:3b6ad6c87518f153c6c38c5190f6d95535af3c8210adc8904d6a7f7f4180a36f\",\"env\":[{\"name\":\"OPENSHIFT_ENABLE_OAUTH\",\"value\":\"true\"},{\"name\":\"OPENSHIFT_ENABLE_REDIRECT_PROMPT\",\"value\":\"true\"},{\"name\":\"DISABLE_ADMINISTRATIVE_MONITORS\",\"value\":\"true\"},{\"name\":\"KUBERNETES_MASTER\",\"value\":\"https://kubernetes.default:443\"},{\"name\":\"KUBERNETES_TRUST_CERTIFICATES\",\"value\":\"true\"},{\"name\":\"JENKINS_SERVICE_NAME\",\"value\":\"jenkins\"},{\"name\":\"JNLP_SERVICE_NAME\",\"value\":\"jenkins-jnlp\"},{\"name\":\"ENABLE_FATAL_ERROR_LOG_FILE\",\"value\":\"false\"}],\"resources\":{\"limits\":{\"memory\":\"512Mi\"}},\"volumeMounts\":[{\"name\":\"jenkins-data\",\"mountPath\":\"/var/lib/jenkins\"}],\"livenessProbe\":{\"httpGet\":{\"path\":\"/login\",\"port\":8080,\"scheme\":\"HTTP\"},\"initialDelaySeconds\":420,\"timeoutSeconds\":240,\"periodSeconds\":360,\"successThreshold\":1,\"failureThreshold\":2},\"readinessProbe\":{\"httpGet\":{\"path\":\"/login\",\"port\":8080,\"scheme\":\"HTTP\"},\"initialDelaySeconds\":3,\"timeoutSeconds\":240,\"periodSeconds\":10,\"successThreshold\":1,\"failureThreshold\":3},\"terminationMessagePath\":\"/dev/termination-log\",\"terminationMessagePolicy\":\"File\",\"imagePullPolicy\":\"IfNotPresent\",\"securityContext\":{\"capabilities\":{},\"privileged\":false}}],\"restartPolicy\":\"Always\",\"terminationGracePeriodSeconds\":30,\"dnsPolicy\":\"ClusterFirst\",\"serviceAccountName\":\"jenkins\",\"serviceAccount\":\"jenkins\",\"securityContext\":{},\"schedulerName\":\"default-scheduler\"}}},\"status\":{\"latestVersion\":1,\"observedGeneration\":1,\"replicas\":0,\"updatedReplicas\":0,\"availableReplicas\":0,\"unavailableReplicas\":0,\"details\":{\"message\":\"config change\",\"causes\":[{\"type\":\"ConfigChange\"}]},\"conditions\":[{\"type\":\"Available\",\"status\":\"False\",\"lastUpdateTime\":\"2019-04-18T09:53:06Z\",\"lastTransitionTime\":\"2019-04-18T09:53:06Z\",\"message\":\"Deployment config does not have minimum availability.\"}]}}\n" + }, + "ownerReferences": [ + { + "apiVersion": "apps.openshift.io/v1", + "kind": "DeploymentConfig", + "name": "jenkins", + "uid": "c3cb3220-61bf-11e9-b220-0a2a2b777307", + "controller": true, + "blockOwnerDeletion": true + } + ] + }, + "spec": { + "replicas": 0, + "selector": { + "deployment": "jenkins-1", + "deploymentconfig": "jenkins", + "name": "jenkins" + }, + "template": { + "metadata": { + "creationTimestamp": null, + "labels": { + "deployment": "jenkins-1", + "deploymentconfig": "jenkins", + "name": "jenkins" + }, + "annotations": { + "openshift.io/deployment-config.latest-version": "1", + "openshift.io/deployment-config.name": "jenkins", + "openshift.io/deployment.name": "jenkins-1" + } + }, + "spec": { + "volumes": [ + { + "name": "jenkins-data", + "persistentVolumeClaim": { "claimName": "jenkins" } + } + ], + "containers": [ + { + "name": "jenkins", + "image": "docker-registry.default.svc:5000/openshift/jenkins@sha256:3b6ad6c87518f153c6c38c5190f6d95535af3c8210adc8904d6a7f7f4180a36f", + "env": [ + { "name": "OPENSHIFT_ENABLE_OAUTH", "value": "true" }, + { + "name": "OPENSHIFT_ENABLE_REDIRECT_PROMPT", + "value": "true" + }, + { + "name": "DISABLE_ADMINISTRATIVE_MONITORS", + "value": "true" + }, + { + "name": "KUBERNETES_MASTER", + "value": "https://kubernetes.default:443" + }, + { "name": "KUBERNETES_TRUST_CERTIFICATES", "value": "true" }, + { "name": "JENKINS_SERVICE_NAME", "value": "jenkins" }, + { "name": "JNLP_SERVICE_NAME", "value": "jenkins-jnlp" }, + { "name": "ENABLE_FATAL_ERROR_LOG_FILE", "value": "false" } + ], + "resources": { "limits": { "memory": "512Mi" } }, + "volumeMounts": [ + { "name": "jenkins-data", "mountPath": "/var/lib/jenkins" } + ], + "livenessProbe": { + "httpGet": { + "path": "/login", + "port": 8080, + "scheme": "HTTP" + }, + "initialDelaySeconds": 420, + "timeoutSeconds": 240, + "periodSeconds": 360, + "successThreshold": 1, + "failureThreshold": 2 + }, + "readinessProbe": { + "httpGet": { + "path": "/login", + "port": 8080, + "scheme": "HTTP" + }, + "initialDelaySeconds": 3, + "timeoutSeconds": 240, + "periodSeconds": 10, + "successThreshold": 1, + "failureThreshold": 3 + }, + "terminationMessagePath": "/dev/termination-log", + "terminationMessagePolicy": "File", + "imagePullPolicy": "IfNotPresent", + "securityContext": { "capabilities": {}, "privileged": false } + } + ], + "restartPolicy": "Always", + "terminationGracePeriodSeconds": 30, + "dnsPolicy": "ClusterFirst", + "serviceAccountName": "jenkins", + "serviceAccount": "jenkins", + "securityContext": {}, + "schedulerName": "default-scheduler" + } + } + }, + "status": { "replicas": 0, "observedGeneration": 3 } + }, + { + "metadata": { + "name": "jenkins-2", + "namespace": "test-jenkins-dualboot", + "selfLink": "/api/v1/namespaces/test-jenkins-dualboot/replicationcontrollers/jenkins-2", + "uid": "69d811b4-77d1-11e9-9823-0a69cdf75e6f", + "resourceVersion": "3603567139", + "generation": 2, + "creationTimestamp": "2019-05-16T11:54:51Z", + "labels": { + "app": "jenkins-persistent", + "openshift.io/deployment-config.name": "jenkins", + "template": "jenkins-persistent-template" + }, + "annotations": { + "openshift.io/deployer-pod.completed-at": "2019-05-16 11:58:12 +0000 UTC", + "openshift.io/deployer-pod.created-at": "2019-05-16 11:54:51 +0000 UTC", + "openshift.io/deployer-pod.name": "jenkins-2-deploy", + "openshift.io/deployer-pod.started-at": "2019-05-16 11:54:52 +0000 UTC", + "openshift.io/deployment-config.latest-version": "2", + "openshift.io/deployment-config.name": "jenkins", + "openshift.io/deployment.phase": "Complete", + "openshift.io/deployment.replicas": "1", + "openshift.io/deployment.status-reason": "config change", + "openshift.io/encoded-deployment-config": "{\"kind\":\"DeploymentConfig\",\"apiVersion\":\"apps.openshift.io/v1\",\"metadata\":{\"name\":\"jenkins\",\"namespace\":\"test-jenkins-dualboot\",\"selfLink\":\"/apis/apps.openshift.io/v1/namespaces/test-jenkins-dualboot/deploymentconfigs/jenkins\",\"uid\":\"c3cb3220-61bf-11e9-b220-0a2a2b777307\",\"resourceVersion\":\"3601372568\",\"generation\":3,\"creationTimestamp\":\"2019-04-18T09:53:06Z\",\"labels\":{\"app\":\"jenkins-persistent\",\"template\":\"jenkins-persistent-template\"},\"annotations\":{\"template.alpha.openshift.io/wait-for-ready\":\"true\"}},\"spec\":{\"strategy\":{\"type\":\"Recreate\",\"customParams\":{},\"recreateParams\":{\"timeoutSeconds\":500},\"resources\":{},\"activeDeadlineSeconds\":21600},\"triggers\":[{\"type\":\"ImageChange\",\"imageChangeParams\":{\"automatic\":true,\"containerNames\":[\"jenkins\"],\"from\":{\"kind\":\"ImageStreamTag\",\"namespace\":\"openshift\",\"name\":\"jenkins:2\"},\"lastTriggeredImage\":\"docker-registry.default.svc:5000/openshift/jenkins@sha256:3b6ad6c87518f153c6c38c5190f6d95535af3c8210adc8904d6a7f7f4180a36f\"}},{\"type\":\"ConfigChange\"}],\"replicas\":1,\"revisionHistoryLimit\":10,\"test\":false,\"selector\":{\"name\":\"jenkins\"},\"template\":{\"metadata\":{\"creationTimestamp\":null,\"labels\":{\"name\":\"jenkins\"}},\"spec\":{\"volumes\":[{\"name\":\"jenkins-data\",\"persistentVolumeClaim\":{\"claimName\":\"jenkins\"}}],\"containers\":[{\"name\":\"jenkins\",\"image\":\"docker-registry.default.svc:5000/openshift/jenkins@sha256:3b6ad6c87518f153c6c38c5190f6d95535af3c8210adc8904d6a7f7f4180a36f\",\"env\":[{\"name\":\"OPENSHIFT_ENABLE_OAUTH\",\"value\":\"true\"},{\"name\":\"OPENSHIFT_ENABLE_REDIRECT_PROMPT\",\"value\":\"true\"},{\"name\":\"DISABLE_ADMINISTRATIVE_MONITORS\",\"value\":\"true\"},{\"name\":\"KUBERNETES_MASTER\",\"value\":\"https://kubernetes.default:443\"},{\"name\":\"KUBERNETES_TRUST_CERTIFICATES\",\"value\":\"true\"},{\"name\":\"JENKINS_SERVICE_NAME\",\"value\":\"jenkins\"},{\"name\":\"JNLP_SERVICE_NAME\",\"value\":\"jenkins-jnlp\"},{\"name\":\"ENABLE_FATAL_ERROR_LOG_FILE\",\"value\":\"false\"},{\"name\":\"CUSTOM\",\"value\":\"123\"}],\"resources\":{\"limits\":{\"memory\":\"512Mi\"}},\"volumeMounts\":[{\"name\":\"jenkins-data\",\"mountPath\":\"/var/lib/jenkins\"}],\"livenessProbe\":{\"httpGet\":{\"path\":\"/login\",\"port\":8080,\"scheme\":\"HTTP\"},\"initialDelaySeconds\":420,\"timeoutSeconds\":240,\"periodSeconds\":360,\"successThreshold\":1,\"failureThreshold\":2},\"readinessProbe\":{\"httpGet\":{\"path\":\"/login\",\"port\":8080,\"scheme\":\"HTTP\"},\"initialDelaySeconds\":3,\"timeoutSeconds\":240,\"periodSeconds\":10,\"successThreshold\":1,\"failureThreshold\":3},\"terminationMessagePath\":\"/dev/termination-log\",\"terminationMessagePolicy\":\"File\",\"imagePullPolicy\":\"IfNotPresent\",\"securityContext\":{\"capabilities\":{},\"privileged\":false}}],\"restartPolicy\":\"Always\",\"terminationGracePeriodSeconds\":30,\"dnsPolicy\":\"ClusterFirst\",\"serviceAccountName\":\"jenkins\",\"serviceAccount\":\"jenkins\",\"securityContext\":{},\"schedulerName\":\"default-scheduler\"}}},\"status\":{\"latestVersion\":2,\"observedGeneration\":2,\"replicas\":1,\"updatedReplicas\":1,\"availableReplicas\":1,\"unavailableReplicas\":0,\"details\":{\"message\":\"config change\",\"causes\":[{\"type\":\"ConfigChange\"}]},\"conditions\":[{\"type\":\"Progressing\",\"status\":\"True\",\"lastUpdateTime\":\"2019-04-18T09:56:05Z\",\"lastTransitionTime\":\"2019-04-18T09:56:05Z\",\"reason\":\"NewReplicationControllerAvailable\",\"message\":\"replication controller \\\"jenkins-1\\\" successfully rolled out\"},{\"type\":\"Available\",\"status\":\"True\",\"lastUpdateTime\":\"2019-05-15T15:07:31Z\",\"lastTransitionTime\":\"2019-05-15T15:07:31Z\",\"message\":\"Deployment config has minimum availability.\"}],\"readyReplicas\":1}}\n" + }, + "ownerReferences": [ + { + "apiVersion": "apps.openshift.io/v1", + "kind": "DeploymentConfig", + "name": "jenkins", + "uid": "c3cb3220-61bf-11e9-b220-0a2a2b777307", + "controller": true, + "blockOwnerDeletion": true + } + ] + }, + "spec": { + "replicas": 1, + "selector": { + "deployment": "jenkins-2", + "deploymentconfig": "jenkins", + "name": "jenkins" + }, + "template": { + "metadata": { + "creationTimestamp": null, + "labels": { + "deployment": "jenkins-2", + "deploymentconfig": "jenkins", + "name": "jenkins" + }, + "annotations": { + "openshift.io/deployment-config.latest-version": "2", + "openshift.io/deployment-config.name": "jenkins", + "openshift.io/deployment.name": "jenkins-2" + } + }, + "spec": { + "volumes": [ + { + "name": "jenkins-data", + "persistentVolumeClaim": { "claimName": "jenkins" } + } + ], + "containers": [ + { + "name": "jenkins", + "image": "docker-registry.default.svc:5000/openshift/jenkins@sha256:3b6ad6c87518f153c6c38c5190f6d95535af3c8210adc8904d6a7f7f4180a36f", + "env": [ + { "name": "OPENSHIFT_ENABLE_OAUTH", "value": "true" }, + { + "name": "OPENSHIFT_ENABLE_REDIRECT_PROMPT", + "value": "true" + }, + { + "name": "DISABLE_ADMINISTRATIVE_MONITORS", + "value": "true" + }, + { + "name": "KUBERNETES_MASTER", + "value": "https://kubernetes.default:443" + }, + { "name": "KUBERNETES_TRUST_CERTIFICATES", "value": "true" }, + { "name": "JENKINS_SERVICE_NAME", "value": "jenkins" }, + { "name": "JNLP_SERVICE_NAME", "value": "jenkins-jnlp" }, + { "name": "ENABLE_FATAL_ERROR_LOG_FILE", "value": "false" }, + { "name": "CUSTOM", "value": "123" } + ], + "resources": { "limits": { "memory": "512Mi" } }, + "volumeMounts": [ + { "name": "jenkins-data", "mountPath": "/var/lib/jenkins" } + ], + "livenessProbe": { + "httpGet": { + "path": "/login", + "port": 8080, + "scheme": "HTTP" + }, + "initialDelaySeconds": 420, + "timeoutSeconds": 240, + "periodSeconds": 360, + "successThreshold": 1, + "failureThreshold": 2 + }, + "readinessProbe": { + "httpGet": { + "path": "/login", + "port": 8080, + "scheme": "HTTP" + }, + "initialDelaySeconds": 3, + "timeoutSeconds": 240, + "periodSeconds": 10, + "successThreshold": 1, + "failureThreshold": 3 + }, + "terminationMessagePath": "/dev/termination-log", + "terminationMessagePolicy": "File", + "imagePullPolicy": "IfNotPresent", + "securityContext": { "capabilities": {}, "privileged": false } + } + ], + "restartPolicy": "Always", + "terminationGracePeriodSeconds": 30, + "dnsPolicy": "ClusterFirst", + "serviceAccountName": "jenkins", + "serviceAccount": "jenkins", + "securityContext": {}, + "schedulerName": "default-scheduler" + } + } + }, + "status": { + "replicas": 1, + "fullyLabeledReplicas": 1, + "observedGeneration": 2 + } + } + ] }