diff --git a/.circleci/config.yml b/.circleci/config.yml index 971d35b..e4a3112 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -2,7 +2,7 @@ version: 2 jobs: lerna_publish: docker: - - image: circleci/node:12 + - image: circleci/node:20 working_directory: ~/repo steps: - checkout @@ -56,7 +56,7 @@ jobs: # - /tmp/layercache.tar build_and_test: docker: - - image: circleci/node:12 + - image: circleci/node:20 working_directory: ~/repo steps: - checkout diff --git a/packages/deployer/src/shepherd.ts b/packages/deployer/src/shepherd.ts index b8a703e..9a957de 100755 --- a/packages/deployer/src/shepherd.ts +++ b/packages/deployer/src/shepherd.ts @@ -143,7 +143,7 @@ function loginToRegistryWithPrompt() { } } -function main() { +async function main() { if (process.argv.indexOf("--version") > 0) { printVersions() process.exit(0) @@ -190,12 +190,18 @@ function main() { } let stateStoreBackend: IStorageBackend - + const mockUiPusher = { + pushDeploymentStateToUI: async () => { + logger.info("Would have pushed to ui, were it not a dryrun") + return + }, + } let uiDataPusher: IPushToShepherdUI if (dryRun) { logger.info(`NOTE: Dryrun does not take deployment state into account and assumes everything needs to be deployed.`) stateStoreBackend = InMemoryStore() + uiDataPusher = mockUiPusher } else { if (process.env.SHEPHERD_PG_HOST) { logger.info(`Using postgres state store on ${process.env.SHEPHERD_PG_HOST}`) @@ -214,6 +220,8 @@ function main() { if (Boolean(process.env.SHEPHERD_UI_API_ENDPOINT)) { logger.info(`Shepherd UI API endpoint configured ${process.env.SHEPHERD_UI_API_ENDPOINT}`) uiDataPusher = CreatePushApi(process.env.SHEPHERD_UI_API_ENDPOINT, console) + } else { + uiDataPusher = mockUiPusher } } @@ -238,96 +246,89 @@ function main() { printUsage() process.exit(255) } + try { + await stateStoreBackend.connect() + let releaseStateStore = ReleaseStateStore({ + storageBackend: stateStoreBackend, + }) + let upstreamDeploymentConfig = createUpstreamTriggerDeploymentConfig(logger) + upstreamDeploymentConfig.loadFromEnvironment(herdFilePath, process.env) - stateStoreBackend - .connect() - .then(function() { - let releaseStateStore = ReleaseStateStore({ - storageBackend: stateStoreBackend, - }) - let upstreamDeploymentConfig = createUpstreamTriggerDeploymentConfig(logger) - upstreamDeploymentConfig.loadFromEnvironment(herdFilePath, process.env) - - if (upstreamDeploymentConfig.herdFileEditNeeded()) { - upgradeOrAddDeploymentInFile(upstreamDeploymentConfig, logger) - } - - let loaderContext = createLoaderContext({ - stateStore: releaseStateStore, - logger: logger, - featureDeploymentConfig: upstreamDeploymentConfig, - uiPusher: uiDataPusher, - environment: environment, - exec: exec, - }) + if (upstreamDeploymentConfig.herdFileEditNeeded()) { + upgradeOrAddDeploymentInFile(upstreamDeploymentConfig, logger) + } - logger.info("Calculating deployment of herd from file " + herdFilePath + " for environment " + environment) - - loaderContext.loader - .loadHerd(herdFilePath) - .then(function(plan: IDeploymentOrchestration) { - plan.printPlan(logger) - if (exportDocuments) { - logger.info("Testrun mode set - exporting all deployment documents to " + outputDirectory) - logger.info("Testrun mode set - no deployments will be performed") - plan - .exportDeploymentActions(outputDirectory as TFileSystemPath) - .then(function() { - terminateProcess(0) - }) - .catch(function(writeError: Error) { - logger.error("Error exporting deployment document! ", writeError) - terminateProcess(255) - }) - } else { - // TODOLATER Rollback on kube config - - let dryRunString = `${dryRun ? " dryrun" : ""}` - - logger.info(`Executing deployment plan${dryRunString}... `) - plan - .executePlans({ - dryRun: dryRun, - dryRunOutputDir: outputDirectory, - pushToUi: pushToUi, - waitForRollout: waitForRollout, - rolloutWaitSeconds: rolloutTimeout, - logContext: defaultLogContext, - }) - .then(function(planResults: IDeploymentPlanExecutionResult[]) { - // Exceptions from plan execution are logged immediately. Here we render only a summary of deployment results. - const failedPlans = planResults.filter(planExecutionResult => { - return planExecutionResult.actionExecutionError !== undefined - }) - if (failedPlans.length > 0) { - renderPlanFailureSummary(logger, failedPlans) - return terminateProcess(failedPlans.length) - } - logger.info(`...plan${dryRunString} execution complete. Exiting shepherd.`) - setTimeout(() => { - terminateProcess(0) - }, 1000) - }) - .catch(function(err: Error) { - renderPlanExecutionError(logger, err, defaultLogContext) - terminateProcess(255) - }) - } - }) - .catch(function(loadError) { - logger.debug(`Plan load error, with stack`, loadError) - logger.error(`Plan load error. ${loadError.message}`) - if (loadError.context) { - logger.error(` ${JSON.stringify(loadError.context)}`) - } - stateStoreBackend.disconnect() - process.exit(255) - }) + let loaderContext = createLoaderContext({ + stateStore: releaseStateStore, + logger: logger, + featureDeploymentConfig: upstreamDeploymentConfig, + uiPusher: uiDataPusher, + environment: environment, + exec: exec, }) - .catch(function(err: Error) { - console.error("Connection/migration error", err) + logger.info("Calculating deployment of herd from file " + herdFilePath + " for environment " + environment) + + try { + const plan: IDeploymentOrchestration = await loaderContext.loader.loadHerd(herdFilePath) + + plan.printPlan(logger) + if (exportDocuments) { + logger.info("Testrun mode set - exporting all deployment documents to " + outputDirectory) + logger.info("Testrun mode set - no deployments will be performed") + plan + .exportDeploymentActions(outputDirectory as TFileSystemPath) + .then(function() { + terminateProcess(0) + }) + .catch(function(writeError: Error) { + logger.error("Error exporting deployment document! ", writeError) + terminateProcess(255) + }) + } else { + // TODOLATER Rollback on kube config + + let dryRunString = `${dryRun ? " dryrun" : ""}` + + logger.info(`Executing deployment plan${dryRunString}... `) + try { + const planResults: IDeploymentPlanExecutionResult[] = await plan.executePlans({ + dryRun: dryRun, + dryRunOutputDir: outputDirectory, + pushToUi: pushToUi, + waitForRollout: waitForRollout, + rolloutWaitSeconds: rolloutTimeout, + logContext: defaultLogContext, + }) + // Exceptions from plan execution are logged immediately. Here we render only a summary of deployment results. + const failedPlans = planResults.filter(planExecutionResult => { + return planExecutionResult.actionExecutionError !== undefined + }) + if (failedPlans.length > 0) { + renderPlanFailureSummary(logger, failedPlans) + return terminateProcess(failedPlans.length) + } + logger.info(`...plan${dryRunString} execution complete. Exiting shepherd.`) + setTimeout(() => { + terminateProcess(0) + }, 1000) + } catch (execError) { + renderPlanExecutionError(logger, execError, defaultLogContext) + terminateProcess(255) + } + } + } catch (loadError) { + logger.debug(`Plan load error, with stack`, loadError) + logger.error(`Plan load error. ${loadError.message}`) + if (loadError.context) { + logger.error(` ${JSON.stringify(loadError.context)}`) + } + stateStoreBackend.disconnect() process.exit(255) - }) + } + } catch (connError) { + console.error("Connection/migration error", connError) + process.exit(255) + } } main() diff --git a/packages/deployer/src/triggered-deployment/create-upstream-trigger-deployment-config.ts b/packages/deployer/src/triggered-deployment/create-upstream-trigger-deployment-config.ts index 7c7e639..3018359 100644 --- a/packages/deployer/src/triggered-deployment/create-upstream-trigger-deployment-config.ts +++ b/packages/deployer/src/triggered-deployment/create-upstream-trigger-deployment-config.ts @@ -74,12 +74,16 @@ export function createUpstreamTriggerDeploymentConfig(logger: ILog): IConfigureU }, loadFromEnvironment(herdFilePath: TFileSystemPath, environment: typeof process.env = process.env): void { let upstreamImageUrl: string = "" - if (environment.UPSTREAM_IMAGE_NAME && environment.UPSTREAM_IMAGE_TAG) { upstreamImageUrl = environment.UPSTREAM_IMAGE_NAME + ":" + environment.UPSTREAM_IMAGE_TAG } else if (environment.UPSTREAM_IMAGE_URL) { upstreamImageUrl = environment.UPSTREAM_IMAGE_URL } + logger.info( + `Look for the herd up stream. upstreamImageUrl: ${ + environment.UPSTREAM_IMAGE_URL + }, hasUpstreamHerdKey: ${Boolean(environment.UPSTREAM_HERD_KEY)}` + ) if (Boolean(upstreamImageUrl) && environment.UPSTREAM_HERD_KEY) { logger.info("Upstream information available, using to modify deployment.") const dockerUrl = parseImageUrl(upstreamImageUrl)