k8s-deploy/src/utilities/manifestUpdateUtils.ts
dependabot[bot] e5725dfe9f
Bump the actions group across 1 directory with 14 updates (#338)
* Bump the actions group across 1 directory with 14 updates

Bumps the actions group with 14 updates in the / directory:

| Package | From | To |
| --- | --- | --- |
| [@actions/core](https://github.com/actions/toolkit/tree/HEAD/packages/core) | `1.10.0` | `1.10.1` |
| [@actions/io](https://github.com/actions/toolkit/tree/HEAD/packages/io) | `1.1.2` | `1.1.3` |
| [@actions/tool-cache](https://github.com/actions/toolkit/tree/HEAD/packages/tool-cache) | `1.1.2` | `2.0.1` |
| [@octokit/core](https://github.com/octokit/core.js) | `3.6.0` | `6.1.2` |
| [@octokit/plugin-retry](https://github.com/octokit/plugin-retry.js) | `3.0.9` | `7.1.2` |
| [@types/minipass](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/minipass) | `3.1.2` | `3.3.5` |
| [js-yaml](https://github.com/nodeca/js-yaml) | `3.13.1` | `4.1.0` |
| [@types/js-yaml](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/js-yaml) | `3.12.7` | `4.0.9` |
| [@types/jest](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/jest) | `26.0.24` | `29.5.13` |
| [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) | `12.20.55` | `22.7.4` |
| [@vercel/ncc](https://github.com/vercel/ncc) | `0.36.1` | `0.38.2` |
| [prettier](https://github.com/prettier/prettier) | `2.8.8` | `3.3.3` |
| [ts-jest](https://github.com/kulshekhar/ts-jest) | `29.2.3` | `29.2.5` |
| [typescript](https://github.com/microsoft/TypeScript) | `5.5.4` | `5.6.2` |



Updates `@actions/core` from 1.10.0 to 1.10.1
- [Changelog](https://github.com/actions/toolkit/blob/main/packages/core/RELEASES.md)
- [Commits](https://github.com/actions/toolkit/commits/HEAD/packages/core)

Updates `@actions/io` from 1.1.2 to 1.1.3
- [Changelog](https://github.com/actions/toolkit/blob/main/packages/io/RELEASES.md)
- [Commits](https://github.com/actions/toolkit/commits/HEAD/packages/io)

Updates `@actions/tool-cache` from 1.1.2 to 2.0.1
- [Changelog](https://github.com/actions/toolkit/blob/main/packages/tool-cache/RELEASES.md)
- [Commits](https://github.com/actions/toolkit/commits/@actions/artifact@2.0.1/packages/tool-cache)

Updates `@octokit/core` from 3.6.0 to 6.1.2
- [Release notes](https://github.com/octokit/core.js/releases)
- [Commits](https://github.com/octokit/core.js/compare/v3.6.0...v6.1.2)

Updates `@octokit/plugin-retry` from 3.0.9 to 7.1.2
- [Release notes](https://github.com/octokit/plugin-retry.js/releases)
- [Commits](https://github.com/octokit/plugin-retry.js/compare/v3.0.9...v7.1.2)

Updates `@types/minipass` from 3.1.2 to 3.3.5
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/minipass)

Updates `js-yaml` from 3.13.1 to 4.1.0
- [Changelog](https://github.com/nodeca/js-yaml/blob/master/CHANGELOG.md)
- [Commits](https://github.com/nodeca/js-yaml/compare/3.13.1...4.1.0)

Updates `@types/js-yaml` from 3.12.7 to 4.0.9
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/js-yaml)

Updates `@types/jest` from 26.0.24 to 29.5.13
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/jest)

Updates `@types/js-yaml` from 3.12.7 to 4.0.9
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/js-yaml)

Updates `@types/node` from 12.20.55 to 22.7.4
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node)

Updates `@vercel/ncc` from 0.36.1 to 0.38.2
- [Release notes](https://github.com/vercel/ncc/releases)
- [Commits](https://github.com/vercel/ncc/compare/0.36.1...0.38.2)

Updates `prettier` from 2.8.8 to 3.3.3
- [Release notes](https://github.com/prettier/prettier/releases)
- [Changelog](https://github.com/prettier/prettier/blob/main/CHANGELOG.md)
- [Commits](https://github.com/prettier/prettier/compare/2.8.8...3.3.3)

Updates `ts-jest` from 29.2.3 to 29.2.5
- [Release notes](https://github.com/kulshekhar/ts-jest/releases)
- [Changelog](https://github.com/kulshekhar/ts-jest/blob/main/CHANGELOG.md)
- [Commits](https://github.com/kulshekhar/ts-jest/compare/v29.2.3...v29.2.5)

Updates `typescript` from 5.5.4 to 5.6.2
- [Release notes](https://github.com/microsoft/TypeScript/releases)
- [Changelog](https://github.com/microsoft/TypeScript/blob/main/azure-pipelines.release.yml)
- [Commits](https://github.com/microsoft/TypeScript/compare/v5.5.4...v5.6.2)

---
updated-dependencies:
- dependency-name: "@actions/core"
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: actions
- dependency-name: "@actions/io"
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: actions
- dependency-name: "@actions/tool-cache"
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: actions
- dependency-name: "@octokit/core"
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: actions
- dependency-name: "@octokit/plugin-retry"
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: actions
- dependency-name: "@types/minipass"
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: actions
- dependency-name: js-yaml
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: actions
- dependency-name: "@types/js-yaml"
  dependency-type: direct:development
  update-type: version-update:semver-major
  dependency-group: actions
- dependency-name: "@types/jest"
  dependency-type: direct:development
  update-type: version-update:semver-major
  dependency-group: actions
- dependency-name: "@types/js-yaml"
  dependency-type: direct:development
  update-type: version-update:semver-major
  dependency-group: actions
- dependency-name: "@types/node"
  dependency-type: direct:development
  update-type: version-update:semver-major
  dependency-group: actions
- dependency-name: "@vercel/ncc"
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: actions
- dependency-name: prettier
  dependency-type: direct:development
  update-type: version-update:semver-major
  dependency-group: actions
- dependency-name: ts-jest
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: actions
- dependency-name: typescript
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: actions
...

Signed-off-by: dependabot[bot] <support@github.com>

* code changes to use yaml.loadAll and upgrade of octokit version

* few code changes to handle errors

* apply prettier formatting

* downgrade prettier version since actionsx/prettier@v3 doesn't support the latest version

* adding try catch to handle yaml loading

* addressing comments

* updating assertions for name

* apply prettier code

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Vidya Reddy <59590642+Vidya2606@users.noreply.github.com>
2024-10-03 17:08:48 -04:00

333 lines
8.9 KiB
TypeScript

import * as core from '@actions/core'
import * as fs from 'fs'
import * as yaml from 'js-yaml'
import * as path from 'path'
import * as fileHelper from './fileUtils'
import {moveFileToTmpDir} from './fileUtils'
import {
InputObjectKindNotDefinedError,
InputObjectMetadataNotDefinedError,
isWorkloadEntity,
KubernetesWorkload,
NullInputObjectError
} from '../types/kubernetesTypes'
import {
getSpecSelectorLabels,
setSpecSelectorLabels
} from './manifestSpecLabelUtils'
import {
getImagePullSecrets,
setImagePullSecrets
} from './manifestPullSecretUtils'
import {Resource} from '../types/kubectl'
import {K8sObject} from '../types/k8sObject'
export function updateManifestFiles(manifestFilePaths: string[]) {
if (manifestFilePaths?.length === 0) {
throw new Error('Manifest files not provided')
}
// move original set of input files to tmp dir
const manifestFilesInTempDir = moveFilesToTmpDir(manifestFilePaths)
// update container images
const containers: string[] = core.getInput('images').split('\n')
const manifestFiles = updateContainerImagesInManifestFiles(
manifestFilesInTempDir,
containers
)
// update pull secrets
const imagePullSecrets: string[] = core
.getInput('imagepullsecrets')
.split('\n')
.filter((secret) => secret.trim().length > 0)
return updateImagePullSecretsInManifestFiles(manifestFiles, imagePullSecrets)
}
export function moveFilesToTmpDir(filepaths: string[]): string[] {
return filepaths.map((filename) => {
return moveFileToTmpDir(filename)
})
}
export function UnsetClusterSpecificDetails(resource: any) {
if (!resource) {
return
}
// Unset cluster specific details in the object
if (!!resource) {
const {metadata, status} = resource
if (!!metadata) {
resource.metadata = {
annotations: metadata.annotations,
labels: metadata.labels,
name: metadata.name
}
}
if (!!status) {
resource.status = {}
}
}
}
function updateContainerImagesInManifestFiles(
filePaths: string[],
containers: string[]
): string[] {
if (filePaths?.length <= 0) return filePaths
// update container images
filePaths.forEach((filePath: string) => {
let contents = fs.readFileSync(filePath).toString()
containers.forEach((container: string) => {
let [imageName] = container.split(':')
if (imageName.indexOf('@') > 0) {
imageName = imageName.split('@')[0]
}
if (contents.indexOf(imageName) > 0)
contents = substituteImageNameInSpecFile(
contents,
imageName,
container
)
})
// write updated files
fs.writeFileSync(path.join(filePath), contents)
})
return filePaths
}
/*
Example:
Input of
currentString: `image: "example/example-image"`
imageName: `example/example-image`
imageNameWithNewTag: `example/example-image:identifiertag`
would return
`image: "example/example-image:identifiertag"`
*/
export function substituteImageNameInSpecFile(
spec: string,
imageName: string,
imageNameWithNewTag: string
) {
if (spec.indexOf(imageName) < 0) return spec
return spec.split('\n').reduce((acc, line) => {
const imageKeyword = line.match(/^ *-? *image:/)
if (imageKeyword) {
let [currentImageName] = line
.substring(imageKeyword[0].length) // consume the line from keyword onwards
.trim()
.replace(/[',"]/g, '') // replace allowed quotes with nothing
.split(':')
if (currentImageName?.indexOf(' ') > 0) {
currentImageName = currentImageName.split(' ')[0] // remove comments
}
if (currentImageName === imageName) {
return acc + `${imageKeyword[0]} ${imageNameWithNewTag}\n`
}
}
return acc + line + '\n'
}, '')
}
export function getReplicaCount(inputObject: any): any {
if (!inputObject) throw NullInputObjectError
if (!inputObject.kind) {
throw InputObjectKindNotDefinedError
}
const {kind} = inputObject
if (
kind.toLowerCase() !== KubernetesWorkload.POD.toLowerCase() &&
kind.toLowerCase() !== KubernetesWorkload.DAEMON_SET.toLowerCase()
)
return inputObject.spec.replicas
return 0
}
export function updateObjectLabels(
inputObject: any,
newLabels: Map<string, string>,
override: boolean = false
) {
if (!inputObject) throw NullInputObjectError
if (!inputObject.metadata) throw InputObjectMetadataNotDefinedError
if (!newLabels) return
if (override) {
inputObject.metadata.labels = newLabels
} else {
let existingLabels =
inputObject.metadata.labels || new Map<string, string>()
Object.keys(newLabels).forEach(
(key) => (existingLabels[key] = newLabels[key])
)
inputObject.metadata.labels = existingLabels
}
}
export function updateObjectAnnotations(
inputObject: any,
newAnnotations: Map<string, string>,
override: boolean = false
) {
if (!inputObject) throw NullInputObjectError
if (!inputObject.metadata) throw InputObjectMetadataNotDefinedError
if (!newAnnotations) return
if (override) {
inputObject.metadata.annotations = newAnnotations
} else {
const existingAnnotations =
inputObject.metadata.annotations || new Map<string, string>()
Object.keys(newAnnotations).forEach(
(key) => (existingAnnotations[key] = newAnnotations[key])
)
inputObject.metadata.annotations = existingAnnotations
}
}
export function updateImagePullSecrets(
inputObject: any,
newImagePullSecrets: string[],
override: boolean = false
) {
if (!inputObject?.spec || !newImagePullSecrets) return
const newImagePullSecretsObjects = Array.from(
newImagePullSecrets,
(name) => {
return {name}
}
)
let existingImagePullSecretObjects: any = getImagePullSecrets(inputObject)
if (override) {
existingImagePullSecretObjects = newImagePullSecretsObjects
} else {
existingImagePullSecretObjects = existingImagePullSecretObjects || []
existingImagePullSecretObjects = existingImagePullSecretObjects.concat(
newImagePullSecretsObjects
)
}
setImagePullSecrets(inputObject, existingImagePullSecretObjects)
}
export function updateSelectorLabels(
inputObject: any,
newLabels: Map<string, string>,
override: boolean
) {
if (!inputObject) throw NullInputObjectError
if (!inputObject.kind) throw InputObjectKindNotDefinedError
if (!newLabels) return
if (inputObject.kind.toLowerCase() === KubernetesWorkload.POD.toLowerCase())
return
let existingLabels = getSpecSelectorLabels(inputObject)
if (override) {
existingLabels = newLabels
} else {
existingLabels = existingLabels || new Map<string, string>()
Object.keys(newLabels).forEach(
(key) => (existingLabels[key] = newLabels[key])
)
}
setSpecSelectorLabels(inputObject, existingLabels)
}
export function getResources(
filePaths: string[],
filterResourceTypes: string[]
): Resource[] {
if (!filePaths) return []
const resources: Resource[] = []
filePaths.forEach((filePath: string) => {
try {
const fileContents = fs.readFileSync(filePath).toString()
const inputObjects: K8sObject[] = yaml.loadAll(
fileContents
) as K8sObject[]
inputObjects.forEach((inputObject) => {
const inputObjectKind = inputObject?.kind || ''
if (
filterResourceTypes.filter(
(type) => inputObjectKind.toLowerCase() === type.toLowerCase()
).length > 0
) {
resources.push({
type: inputObject.kind,
name: inputObject.metadata.name,
namespace: inputObject?.metadata?.namespace
})
}
})
} catch (error) {
core.error(`Failed to process file at ${filePath}: ${error.message}`)
throw error
}
})
return resources
}
function updateImagePullSecretsInManifestFiles(
filePaths: string[],
imagePullSecrets: string[]
): string[] {
if (imagePullSecrets?.length <= 0) return filePaths
const newObjectsList = []
filePaths.forEach((filePath: string) => {
try {
const fileContents = fs.readFileSync(filePath).toString()
yaml.loadAll(fileContents, (inputObject: any) => {
if (inputObject?.kind) {
const {kind} = inputObject
if (isWorkloadEntity(kind)) {
updateImagePullSecrets(inputObject, imagePullSecrets)
}
newObjectsList.push(inputObject)
}
})
} catch (error) {
core.error(`Failed to process file at ${filePath}: ${error.message}`)
throw error
}
})
return fileHelper.writeObjectsToFile(newObjectsList)
}