Skip to content

Commit

Permalink
feat(helmfile-dependency-check): new helmfile deps strategy (#63)
Browse files Browse the repository at this point in the history
* initial commit

* updated readme and dist

* updated test

* updated test

* added example outputs to readme

* made mock more generic

* pass working dir to shell command

Co-authored-by: Charles Sullivan <[email protected]>
Co-authored-by: Marcin Dobosz <[email protected]>
  • Loading branch information
3 people authored Dec 2, 2020
1 parent c6a6e4c commit 5c2b0e3
Show file tree
Hide file tree
Showing 15 changed files with 374 additions and 232 deletions.
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
# github-actions

Repository of generic reusable Github Actions

#### [`build-workspace-matrix`](build-workspace-matrix)
Expand All @@ -20,3 +21,7 @@ Wraps certain [`terraform`](https://www.terraform.io/docs/commands/index.html) c
#### [`update-issue`](update-issue)

Creates, updates, or closes an issue matching a given title based on other parameters.

#### [`helmfile-dependency-check`](helmfile-dependency-check)

Checks if there is a valid `helmfile.yaml` in the working directory. Executes `helmfile deps` and checks if there are any chart upgrades available.
10 changes: 10 additions & 0 deletions __mocks__/child_process.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
const child_process = jest.createMockFromModule("child_process")

function execSync(command: string): Buffer {
const buffer = Buffer.from(`executed: ${command}`)
return buffer
}

(child_process as any).execSync = execSync

module.exports = child_process
29 changes: 29 additions & 0 deletions __mocks__/fs.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
const fs = jest.genMockFromModule("fs");

let mockFiles: string[] | null = null;

function __setMockFiles(newMockFiles: string[]): void {
mockFiles = newMockFiles
}

function readFileSync(path: string, encoding: string): string {
if (mockFiles.length === 0) {
return ""
}
return mockFiles.shift()
}

function existsSync(path: string): boolean {
if (RegExp(/helmfile-missing\/helmfile\.yaml/).exec(path)) {
return false
} else if (RegExp(/helmfile-lock-missing\/helmfile\.lock/).exec(path)) {
return false
}
return true
}

(fs as any).__setMockFiles = __setMockFiles;
(fs as any).readFileSync = readFileSync;
(fs as any).existsSync = existsSync;

module.exports = fs;
96 changes: 94 additions & 2 deletions helmfile-dependency-check/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ Simple action to check if there are updates available for charts defined in a `h
Helmfile Lock States:

- `missing` - helmfile.lock is missing (helmfile.yaml had repositories to check against)
- `update_available` - Chart updates were found in upstream repos
- `update_available` - Chart updates were found
- `fresh` - Charts are all up to date

## Optional Inputs
Expand All @@ -30,10 +30,102 @@ outputs:
value: ${{ steps.main.outputs.helmfile-lock-updates }}
```
## Example
## Example Usage
```yaml
- uses: iStreamPlanet/github-actions/helmfile-dependency-check@main
with:
working_directory: .
```
## Update Available Example
helmfile.lock:
```
version: v0.125.0
dependencies:
- name: datadog
repository: https://helm.datadoghq.com
version: 2.4.39
- name: external-dns
repository: https://charts.bitnami.com/bitnami
version: 2.24.1
- name: kube-state-metrics
repository: https://charts.helm.sh/stable
version: 2.9.2
- name: metrics-server
repository: https://charts.helm.sh/stable
version: 2.11.2
- name: spotinst-kubernetes-cluster-controller
repository: https://spotinst.github.io/spotinst-kubernetes-helm-charts
version: 1.0.78
digest: sha256:aa71df80f65944a2281269415dc193943f50d9d7bf2763017ee1d9d9539ac116
generated: "2020-11-06T15:12:37.407096-08:00"
```
Raw output:
```
::set-output name=helmfile-lock-state::update_available
::set-output name=helmfile-lock-updates::[{"name":"kube-state-metrics","repository":"https://charts.helm.sh/stable","currentVer":"2.9.2","upgradeVer":"2.9.4"},{"name":"metrics-server","repository":"https://charts.helm.sh/stable","currentVer":"2.11.2","upgradeVer":"2.11.4"},{"name":"spotinst-kubernetes-cluster-controller","repository":"https://spotinst.github.io/spotinst-kubernetes-helm-charts","currentVer":"1.0.78","upgradeVer":"1.0.79"}]
```

Formatted:

```yaml
[
{
"name": "kube-state-metrics",
"repository": "https://charts.helm.sh/stable",
"currentVer": "2.9.2",
"upgradeVer": "2.9.4",
},
{
"name": "metrics-server",
"repository": "https://charts.helm.sh/stable",
"currentVer": "2.11.2",
"upgradeVer": "2.11.4",
},
{
"name": "spotinst-kubernetes-cluster-controller",
"repository": "https://spotinst.github.io/spotinst-kubernetes-helm-charts",
"currentVer": "1.0.78",
"upgradeVer": "1.0.79",
},
]
```

## Fresh Example

helmfile.lock

```
version: v0.125.0
dependencies:
- name: datadog
repository: https://helm.datadoghq.com
version: 2.4.39
- name: external-dns
repository: https://charts.bitnami.com/bitnami
version: 2.24.1
- name: kube-state-metrics
repository: https://charts.helm.sh/stable
version: 2.9.4
- name: metrics-server
repository: https://charts.helm.sh/stable
version: 2.11.4
- name: spotinst-kubernetes-cluster-controller
repository: https://spotinst.github.io/spotinst-kubernetes-helm-charts
version: 1.0.79
digest: sha256:ff3b0ad50accb9a12898773752efe2cbdc7004003be46506c68a3f70cb28ff34
generated: "2020-11-30T13:15:00.561501952-08:00"
```

Raw output:

```
new helmfile.lock was not generated
::set-output name=helmfile-lock-state::fresh
::set-output name=helmfile-lock-updates::[]
```
173 changes: 135 additions & 38 deletions helmfile-dependency-check/__test__/main.test.ts
Original file line number Diff line number Diff line change
@@ -1,72 +1,169 @@
import * as core from "@actions/core"
jest.mock("fs")
jest.mock("child_process")

import * as path from "path"
import * as nock from "nock"
import { readFileSync } from "fs"
import { helmfileDepCheck } from "../helmfileDepCheck"

const baseDir = "helmfile-dependency-check/__test__/test-data/"

const {readFileSync} = jest.requireActual("fs")

beforeEach(() => {
jest.resetModules()

// index.yaml will be returned for all https GET requests
const helmfilePath = path.join(baseDir, "index.yaml")
const helmfileContent = readFileSync(helmfilePath, "utf-8")
nock(/.*/).persist().get(/.*index.yaml/).reply(200, helmfileContent)
require("fs").__setMockFiles([])
})

afterEach(() => {
delete process.env["INPUT_WORKING_DIRECTORY"]
})

describe("helmfile-dep-update", () => {
it("helmfile missing", async () => {
process.env["INPUT_WORKING_DIRECTORY"] = path.join(baseDir, "helmfile-missing")
const setOutputMock = jest.spyOn(core, "setOutput")
it("helmfile missing", () => {
const workingDir = path.join(baseDir, "helmfile-missing")
process.env["INPUT_WORKING_DIRECTORY"] = workingDir

await helmfileDepCheck()
const setOutputMock = jest.spyOn(require("@actions/core"), "setOutput")

require("../helmfileDepCheck").helmfileDepCheck()

expect(setOutputMock).toHaveBeenCalledWith("helmfile-lock-state", "fresh")
expect(setOutputMock).toHaveBeenCalledWith("helmfile-lock-updates", [])
})
it("helmfile missing repositories", async () => {
process.env["INPUT_WORKING_DIRECTORY"] = path.join(baseDir, "helmfile-no-repository")
const setOutputMock = jest.spyOn(core, "setOutput")
it("helmfile missing repositories", () => {
const workingDir = path.join(baseDir, "helmfile-no-repositories")
process.env["INPUT_WORKING_DIRECTORY"] = workingDir

const helmfilePath = path.join(workingDir, "helmfile.yaml")
const helmfileContent = readFileSync(helmfilePath, "utf-8")

await helmfileDepCheck()
const mockFiles = [
helmfileContent,
]
require("fs").__setMockFiles(mockFiles)

const setOutputMock = jest.spyOn(require("@actions/core"), "setOutput")

require("../helmfileDepCheck").helmfileDepCheck()

expect(setOutputMock).toHaveBeenCalledWith("helmfile-lock-state", "fresh")
expect(setOutputMock).toHaveBeenCalledWith("helmfile-lock-updates", [])
})
it("helmfile lock fresh", async () => {
process.env["INPUT_WORKING_DIRECTORY"] = path.join(baseDir, "helmfile-lock-fresh")
const setOutputMock = jest.spyOn(core, "setOutput")
it("helmfile lock fresh", () => {
const workingDir = path.join(baseDir, "helmfile-lock-fresh")
process.env["INPUT_WORKING_DIRECTORY"] = workingDir

const helmfilePath = path.join(workingDir, "helmfile.yaml")
const helmfileContent = readFileSync(helmfilePath, "utf-8")

const helmfileLockPath = path.join(workingDir, "helmfile.lock")
const helmfileLockContent = readFileSync(helmfileLockPath, "utf-8")

await helmfileDepCheck()
const freshHelmfileLockPath = path.join(workingDir, "helmfile_fresh.lock")
const freshHelmfileLockContent = readFileSync(freshHelmfileLockPath, "utf-8")

const mockFiles = [
helmfileContent,
helmfileLockContent,
freshHelmfileLockContent,
]
require("fs").__setMockFiles(mockFiles)

const setOutputMock = jest.spyOn(require("@actions/core"), "setOutput")
const logMock = jest.spyOn(global.console, "log")

require("../helmfileDepCheck").helmfileDepCheck()

expect(setOutputMock).toHaveBeenCalledWith("helmfile-lock-state", "fresh")
expect(setOutputMock).toHaveBeenCalledWith("helmfile-lock-updates", [])
expect(logMock).toHaveBeenCalledWith("executed: helmfile deps")
})
it("helmfile lock update", async () => {
process.env["INPUT_WORKING_DIRECTORY"] = path.join(baseDir, "helmfile-lock-update")
const setOutputMock = jest.spyOn(core, "setOutput")

await helmfileDepCheck()

const updateData = {
name: "datadog",
repository: "https://helm.datadoghq.com/index.yaml",
currentVer: "2.4.39",
latestVer: "2.5.1"
}
it("helmfile lock no updates", () => {
const workingDir = path.join(baseDir, "helmfile-lock-fresh")
process.env["INPUT_WORKING_DIRECTORY"] = workingDir

const helmfilePath = path.join(workingDir, "helmfile.yaml")
const helmfileContent = readFileSync(helmfilePath, "utf-8")

const helmfileLockPath = path.join(workingDir, "helmfile.lock")
const helmfileLockContent = readFileSync(helmfileLockPath, "utf-8")

const freshHelmfileLockPath = path.join(workingDir, "helmfile.lock")
const freshHelmfileLockContent = readFileSync(freshHelmfileLockPath, "utf-8")

const mockFiles = [
helmfileContent,
helmfileLockContent,
freshHelmfileLockContent,
]
require("fs").__setMockFiles(mockFiles)

const setOutputMock = jest.spyOn(require("@actions/core"), "setOutput")
const logMock = jest.spyOn(global.console, "log")

require("../helmfileDepCheck").helmfileDepCheck()

expect(setOutputMock).toHaveBeenCalledWith("helmfile-lock-state", "fresh")
expect(setOutputMock).toHaveBeenCalledWith("helmfile-lock-updates", [])
expect(logMock).toHaveBeenCalledWith("new helmfile.lock was not generated")
})
it("helmfile lock update", () => {
const workingDir = path.join(baseDir, "helmfile-lock-update")
process.env["INPUT_WORKING_DIRECTORY"] = workingDir

const helmfilePath = path.join(workingDir, "helmfile.yaml")
const helmfileContent = readFileSync(helmfilePath, "utf-8")

const helmfileLockPath = path.join(workingDir, "helmfile.lock")
const helmfileLockContent = readFileSync(helmfileLockPath, "utf-8")

const updatedHelmfileLockPath = path.join(workingDir, "helmfile_updated.lock")
const updatedHelmfileLockContent = readFileSync(updatedHelmfileLockPath, "utf-8")

const mockFiles = [
helmfileContent,
helmfileLockContent,
updatedHelmfileLockContent,
]
require("fs").__setMockFiles(mockFiles)

const setOutputMock = jest.spyOn(require("@actions/core"), "setOutput")
const logMock = jest.spyOn(global.console, "log")

require("../helmfileDepCheck").helmfileDepCheck()

const updateData = [
{
name: "datadog",
repository: "https://helm.datadoghq.com",
currentVer: "2.4.39",
upgradeVer: "2.5.1"
},
{
name: "spotinst-kubernetes-cluster-controller",
repository: "https://spotinst.github.io/spotinst-kubernetes-helm-charts",
currentVer: "1.0.78",
upgradeVer: "1.0.79"
}
]

expect(setOutputMock).toHaveBeenCalledWith("helmfile-lock-state", "update_available")
expect(setOutputMock).toHaveBeenCalledWith("helmfile-lock-updates", [updateData])
expect(setOutputMock).toHaveBeenCalledWith("helmfile-lock-updates", updateData)
expect(logMock).toHaveBeenCalledWith("executed: helmfile deps")
})
it("helmfile lock missing", async () => {
process.env["INPUT_WORKING_DIRECTORY"] = path.join(baseDir, "helmfile-lock-missing")
const setOutputMock = jest.spyOn(core, "setOutput")
it("helmfile lock missing", () => {
const workingDir = path.join(baseDir, "helmfile-lock-missing")
process.env["INPUT_WORKING_DIRECTORY"] = workingDir

const helmfilePath = path.join(workingDir, "helmfile.yaml")
const helmfileContent = readFileSync(helmfilePath, "utf-8")

const mockFiles = [
helmfileContent,
]
require("fs").__setMockFiles(mockFiles)

const setOutputMock = jest.spyOn(require("@actions/core"), "setOutput")

await helmfileDepCheck()
require("../helmfileDepCheck").helmfileDepCheck()

expect(setOutputMock).toHaveBeenCalledWith("helmfile-lock-state", "missing")
expect(setOutputMock).toHaveBeenCalledWith("helmfile-lock-updates", [])
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,4 @@ dependencies:
repository: https://spotinst.github.io/spotinst-kubernetes-helm-charts
version: 1.0.78
digest: sha256:aa71df80f65944a2281269415dc193943f50d9d7bf2763017ee1d9d9539ac116
generated: "2020-11-06T15:12:37.407096-08:00"
generated: "2019-11-06T15:12:37.407096-08:00"
Loading

0 comments on commit 5c2b0e3

Please sign in to comment.