Skip to content

Commit

Permalink
refactor: encryption routine simpler
Browse files Browse the repository at this point in the history
  • Loading branch information
Maurice Faber committed Aug 30, 2021
1 parent 773de4e commit 3289eb4
Show file tree
Hide file tree
Showing 3 changed files with 23 additions and 79 deletions.
5 changes: 1 addition & 4 deletions src/cmd/index.ts
@@ -1,5 +1,4 @@
import { CommandModule } from 'yargs'
import { module as genSopsModule } from './gen-sops'
import { module as applyModule } from './apply'
import { module as bashModule } from './bash'
import { module as bootstrapModule } from './bootstrap'
Expand All @@ -11,11 +10,11 @@ import { module as diffModule } from './diff'
import { module as encryptModule } from './encrypt'
import { module as filesModule } from './files'
import { module as genDroneModule } from './gen-drone'
import { module as genSopsModule } from './gen-sops'
import { module as hfModule } from './hf'
import { module as lintModule } from './lint'
import { module as playgroundModule } from './playground'
import { module as pullModule } from './pull'
import { module as rotateKeysModule } from './rotate-keys'
import { module as scoreTemplatesModule } from './score-templates'
import { module as serverModule } from './server'
import { module as statusModule } from './status'
Expand All @@ -42,7 +41,6 @@ export { module as genSops } from './gen-sops'
export { module as hf } from './hf'
export { module as lint } from './lint'
export { module as pull } from './pull'
export { module as rotateKeys } from './rotate-keys'
export { module as scoreTemplates } from './score-templates'
export { module as server } from './server'
export { module as status } from './status'
Expand Down Expand Up @@ -71,7 +69,6 @@ export const commands: CommandModule[] = [
lintModule,
playgroundModule,
pullModule,
rotateKeysModule,
scoreTemplatesModule,
serverModule,
statusModule,
Expand Down
27 changes: 0 additions & 27 deletions src/cmd/rotate-keys.ts

This file was deleted.

70 changes: 22 additions & 48 deletions src/common/crypt.ts
@@ -1,7 +1,7 @@
import { EventEmitter } from 'events'
import { existsSync, statSync, utimesSync, writeFileSync } from 'fs'
import { chunk } from 'lodash-es'
import { $, cd, chalk, nothrow, ProcessOutput } from 'zx'
import { $, cd, ProcessOutput } from 'zx'
import { env } from './envalid'
import { BasicArguments, OtomiDebugger, readdirRecurse, rootDir, terminal } from './utils'

Expand Down Expand Up @@ -38,27 +38,28 @@ const getAllSecretFiles = async () => {
}

type CR = {
condition?: (path: string, file: string) => boolean
condition?: (file: string) => boolean
cmd: CryptType
post?: (result: ProcessOutput, path: string, file: string) => void
post?: (file: string) => void
}

const processFileChunk = async (crypt: CR, files: string[]): Promise<ProcessOutput[]> => {
const processFileChunk = async (crypt: CR, files: string[]): Promise<(ProcessOutput | undefined)[]> => {
const commands = files.map(async (file) => {
if (!crypt.condition || crypt.condition(env.ENV_DIR, file)) {
if (!crypt.condition || crypt.condition(file)) {
debug.debug(`${crypt.cmd} ${file}`)
const result = await nothrow($`${crypt.cmd.split(' ')} ${file}`)
if (crypt.post) crypt.post(result, env.ENV_DIR, file)
const result = $`${crypt.cmd.split(' ')} ${file}`
result.then((res) => {
if (crypt.post) crypt.post(file)
return res
})
return result
}
return undefined
})
const results = (await Promise.all(commands)).filter(Boolean) as ProcessOutput[]
results.filter((res: ProcessOutput) => res.exitCode !== 0).map((val) => debug.warn(val))
return results
return Promise.all(commands)
}

const runOnSecretFiles = async (crypt: CR, filesArgs: string[] = []): Promise<ProcessOutput[] | undefined> => {
const runOnSecretFiles = async (crypt: CR, filesArgs: string[] = []): Promise<void> => {
let files: string[] = filesArgs
cd(env.ENV_DIR)

Expand All @@ -70,30 +71,27 @@ const runOnSecretFiles = async (crypt: CR, filesArgs: string[] = []): Promise<Pr
const filesChunked = chunk(files, chunkSize)

const eventEmitterDefaultListeners = EventEmitter.defaultMaxListeners
// EventEmitter.defaultMaxListeners is 10, if we increate chunkSize in the future then this line will prevent it from crashing
// EventEmitter.defaultMaxListeners is 10, if we increase chunkSize in the future then this line will prevent it from crashing
if (chunkSize + 2 > EventEmitter.defaultMaxListeners) EventEmitter.defaultMaxListeners = chunkSize + 2
debug.debug(`runOnSecretFiles: ${crypt.cmd}`)
try {
const results: ProcessOutput[] = []
// eslint-disable-next-line no-restricted-syntax
for (const fileChunk of filesChunked) {
// eslint-disable-next-line no-await-in-loop
const chunkResult = await processFileChunk(crypt, fileChunk)
results.push(...chunkResult)
await processFileChunk(crypt, fileChunk)
}
return results
return
} catch (error) {
debug.error(error)
return undefined
return
} finally {
cd(rootDir)
EventEmitter.defaultMaxListeners = eventEmitterDefaultListeners
}
}

const matchTimestamps = (res: ProcessOutput, path: string, file: string) => {
if (res.exitCode !== 0) return
const absFilePath = `${path}/${file}`
const matchTimestamps = (file: string) => {
const absFilePath = `${env.ENV_DIR}/${file}`
if (!existsSync(`${absFilePath}.dec`)) return

const encTS = statSync(absFilePath)
Expand All @@ -116,9 +114,7 @@ export const decrypt = async (...files: string[]): Promise<void> => {
await runOnSecretFiles(
{
cmd: CryptType.DECRYPT,
post: (r, p, f) => {
matchTimestamps(r, p, f)
},
post: matchTimestamps,
},
files,
)
Expand All @@ -136,8 +132,8 @@ export const encrypt = async (...files: string[]): Promise<void> => {
debug.info('Starting encryption')
await runOnSecretFiles(
{
condition: (path: string, file: string): boolean => {
const absFilePath = `${path}/${file}`
condition: (file: string): boolean => {
const absFilePath = `${env.ENV_DIR}/${file}`

const decExists = existsSync(`${absFilePath}.dec`)
if (!decExists) {
Expand All @@ -161,32 +157,10 @@ export const encrypt = async (...files: string[]): Promise<void> => {
return false
},
cmd: CryptType.ENCRYPT,
post: (r, p, f) => {
debug.debug(r.stdout.trim())
matchTimestamps(r, p, f)
},
post: matchTimestamps,
},
files,
)

debug.info('Encryption is done')
}

export const rotate = async (): Promise<void> => {
const namespace = 'rotate'
debug = terminal(namespace)
if (!existsSync(`${env.ENV_DIR}/.sops.yaml`)) {
debug.debug('Skipping rotation')
return
}
await runOnSecretFiles({
cmd: CryptType.ROTATE,
post: (result: ProcessOutput, path: string, file: string) => {
if (result.exitCode === 0) {
debug.info(`Rotating sops key for '${chalk.italic(file)}' ${chalk.greenBright('succeeded')}`)
} else {
debug.warn(`Rotating sops key for '${chalk.italic(file)}' ${chalk.redBright('failed')}`)
}
},
})
}

0 comments on commit 3289eb4

Please sign in to comment.