-
Notifications
You must be signed in to change notification settings - Fork 17
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
chore: extract deployment check functions to new file for ease of mai…
…ntenance (#1472) ## Description `helpers.ts` is bloated and adds to maintenance overhead. By moving some deployment-related functions to a separate file we reduce the cognitive load faced by any developer working withing `helpers.ts` and associated tests. End to End Test: <!-- if applicable --> (See [Pepr Excellent Examples](https://github.com/defenseunicorns/pepr-excellent-examples)) ## Related Issue Relates to #1397 ## Type of change - [ ] Bug fix (non-breaking change which fixes an issue) - [ ] New feature (non-breaking change which adds functionality) - [x] Other (security config, docs update, etc) ## Checklist before merging - [x] Unit, [Journey](https://github.com/defenseunicorns/pepr/tree/main/journey), [E2E Tests](https://github.com/defenseunicorns/pepr-excellent-examples), [docs](https://github.com/defenseunicorns/pepr/tree/main/docs), [adr](https://github.com/defenseunicorns/pepr/tree/main/adr) added or updated as needed - [x] [Contributor Guide Steps](https://docs.pepr.dev/main/contribute/#submitting-a-pull-request) followed
- Loading branch information
Showing
5 changed files
with
290 additions
and
278 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,243 @@ | ||
import { describe, jest, test, beforeEach, afterEach, expect } from "@jest/globals"; | ||
import { K8s, GenericClass, KubernetesObject } from "kubernetes-fluent-client"; | ||
import { K8sInit } from "kubernetes-fluent-client/dist/fluent/types"; | ||
import { checkDeploymentStatus, namespaceDeploymentsReady } from "./deploymentChecks"; | ||
|
||
jest.mock("kubernetes-fluent-client", () => { | ||
return { | ||
K8s: jest.fn(), | ||
kind: jest.fn(), | ||
}; | ||
}); | ||
|
||
describe("namespaceDeploymentsReady", () => { | ||
const mockK8s = jest.mocked(K8s); | ||
|
||
beforeEach(() => { | ||
jest.useFakeTimers(); | ||
}); | ||
afterEach(() => { | ||
jest.clearAllMocks(); | ||
jest.resetAllMocks(); | ||
jest.useRealTimers(); | ||
}); | ||
|
||
test("should return true if all deployments are ready", async () => { | ||
const deployments = { | ||
items: [ | ||
{ | ||
metadata: { | ||
name: "watcher", | ||
namespace: "pepr-system", | ||
}, | ||
spec: { | ||
replicas: 1, | ||
}, | ||
status: { | ||
readyReplicas: 1, | ||
}, | ||
}, | ||
{ | ||
metadata: { | ||
name: "admission", | ||
namespace: "pepr-system", | ||
}, | ||
spec: { | ||
replicas: 2, | ||
}, | ||
status: { | ||
readyReplicas: 2, | ||
}, | ||
}, | ||
], | ||
}; | ||
|
||
mockK8s.mockImplementation(<T extends GenericClass, K extends KubernetesObject>() => { | ||
return { | ||
InNamespace: jest.fn().mockReturnThis(), | ||
Get: () => deployments, | ||
} as unknown as K8sInit<T, K>; | ||
}); | ||
|
||
const expected = true; | ||
const result = await namespaceDeploymentsReady(); | ||
expect(result).toBe(expected); | ||
}); | ||
|
||
test("should call checkDeploymentStatus if any deployments are not ready", async () => { | ||
const deployments = { | ||
items: [ | ||
{ | ||
metadata: { | ||
name: "watcher", | ||
namespace: "pepr-system", | ||
}, | ||
spec: { | ||
replicas: 1, | ||
}, | ||
status: { | ||
readyReplicas: 1, | ||
}, | ||
}, | ||
{ | ||
metadata: { | ||
name: "admission", | ||
namespace: "pepr-system", | ||
}, | ||
spec: { | ||
replicas: 2, | ||
}, | ||
status: { | ||
readyReplicas: 1, | ||
}, | ||
}, | ||
], | ||
}; | ||
|
||
const deployments2 = { | ||
items: [ | ||
{ | ||
metadata: { | ||
name: "watcher", | ||
namespace: "pepr-system", | ||
}, | ||
spec: { | ||
replicas: 1, | ||
}, | ||
status: { | ||
readyReplicas: 1, | ||
}, | ||
}, | ||
{ | ||
metadata: { | ||
name: "admission", | ||
namespace: "pepr-system", | ||
}, | ||
spec: { | ||
replicas: 2, | ||
}, | ||
status: { | ||
readyReplicas: 2, | ||
}, | ||
}, | ||
], | ||
}; | ||
|
||
mockK8s | ||
.mockImplementation(<T extends GenericClass, K extends KubernetesObject>() => { | ||
return { | ||
InNamespace: jest.fn().mockReturnThis(), | ||
Get: () => deployments, | ||
} as unknown as K8sInit<T, K>; | ||
}) | ||
.mockImplementation(<T extends GenericClass, K extends KubernetesObject>() => { | ||
return { | ||
InNamespace: jest.fn().mockReturnThis(), | ||
Get: () => deployments2, | ||
} as unknown as K8sInit<T, K>; | ||
}); | ||
|
||
const expected = true; | ||
const result = await namespaceDeploymentsReady(); | ||
|
||
expect(result).toBe(expected); | ||
|
||
expect(mockK8s).toHaveBeenCalledTimes(1); | ||
}); | ||
}); | ||
|
||
describe("checkDeploymentStatus", () => { | ||
const mockK8s = jest.mocked(K8s); | ||
|
||
beforeEach(() => { | ||
jest.useFakeTimers(); | ||
}); | ||
afterEach(() => { | ||
jest.clearAllMocks(); | ||
jest.resetAllMocks(); | ||
jest.useRealTimers(); | ||
}); | ||
test("should return true if all deployments are ready", async () => { | ||
const deployments = { | ||
items: [ | ||
{ | ||
metadata: { | ||
name: "watcher", | ||
namespace: "pepr-system", | ||
}, | ||
spec: { | ||
replicas: 1, | ||
}, | ||
status: { | ||
readyReplicas: 1, | ||
}, | ||
}, | ||
{ | ||
metadata: { | ||
name: "admission", | ||
namespace: "pepr-system", | ||
}, | ||
spec: { | ||
replicas: 2, | ||
}, | ||
status: { | ||
readyReplicas: 2, | ||
}, | ||
}, | ||
], | ||
}; | ||
|
||
mockK8s.mockImplementation(<T extends GenericClass, K extends KubernetesObject>() => { | ||
return { | ||
InNamespace: jest.fn().mockReturnThis(), | ||
Get: () => deployments, | ||
} as unknown as K8sInit<T, K>; | ||
}); | ||
|
||
const expected = true; | ||
const result = await checkDeploymentStatus("pepr-system"); | ||
expect(result).toBe(expected); | ||
}); | ||
|
||
test("should return false if any deployments are not ready", async () => { | ||
const deployments = { | ||
items: [ | ||
{ | ||
metadata: { | ||
name: "watcher", | ||
namespace: "pepr-system", | ||
}, | ||
spec: { | ||
replicas: 1, | ||
}, | ||
status: { | ||
readyReplicas: 1, | ||
}, | ||
}, | ||
{ | ||
metadata: { | ||
name: "admission", | ||
namespace: "pepr-system", | ||
}, | ||
spec: { | ||
replicas: 2, | ||
}, | ||
status: { | ||
readyReplicas: 1, | ||
}, | ||
}, | ||
], | ||
}; | ||
|
||
mockK8s.mockImplementation(<T extends GenericClass, K extends KubernetesObject>() => { | ||
return { | ||
InNamespace: jest.fn().mockReturnThis(), | ||
Get: () => deployments, | ||
} as unknown as K8sInit<T, K>; | ||
}); | ||
|
||
const expected = false; | ||
const result = await checkDeploymentStatus("pepr-system"); | ||
expect(result).toBe(expected); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
// check to see if all replicas are ready for all deployments in the pepr-system namespace | ||
|
||
import { K8s, kind } from "kubernetes-fluent-client"; | ||
import Log from "./logger"; | ||
|
||
// returns true if all deployments are ready, false otherwise | ||
export async function checkDeploymentStatus(namespace: string) { | ||
const deployments = await K8s(kind.Deployment).InNamespace(namespace).Get(); | ||
let status = false; | ||
let readyCount = 0; | ||
|
||
for (const deployment of deployments.items) { | ||
const readyReplicas = deployment.status?.readyReplicas ? deployment.status?.readyReplicas : 0; | ||
if (deployment.status?.readyReplicas !== deployment.spec?.replicas) { | ||
Log.info( | ||
`Waiting for deployment ${deployment.metadata?.name} rollout to finish: ${readyReplicas} of ${deployment.spec?.replicas} replicas are available`, | ||
); | ||
} else { | ||
Log.info( | ||
`Deployment ${deployment.metadata?.name} rolled out: ${readyReplicas} of ${deployment.spec?.replicas} replicas are available`, | ||
); | ||
readyCount++; | ||
} | ||
} | ||
if (readyCount === deployments.items.length) { | ||
status = true; | ||
} | ||
return status; | ||
} | ||
|
||
// wait for all deployments in the pepr-system namespace to be ready | ||
export async function namespaceDeploymentsReady(namespace: string = "pepr-system") { | ||
Log.info(`Checking ${namespace} deployments status...`); | ||
let ready = false; | ||
while (!ready) { | ||
ready = await checkDeploymentStatus(namespace); | ||
if (ready) { | ||
return ready; | ||
} | ||
await new Promise(resolve => setTimeout(resolve, 1000)); | ||
} | ||
Log.info(`All ${namespace} deployments are ready`); | ||
} |
Oops, something went wrong.