diff --git a/core/src/actions/base.ts b/core/src/actions/base.ts index e76afb096e..7817102945 100644 --- a/core/src/actions/base.ts +++ b/core/src/actions/base.ts @@ -59,7 +59,7 @@ import { PickTypeByKind } from "../graph/config-graph" import { DeployAction } from "./deploy" import { TestAction } from "./test" import { RunAction } from "./run" -import { uuidv4 } from "../util/util" +import { uuidv4 } from "../util/random" // TODO-G2: split this file diff --git a/core/src/actions/types.ts b/core/src/actions/types.ts index 07017b83ca..ffb3ca3d0d 100644 --- a/core/src/actions/types.ts +++ b/core/src/actions/types.ts @@ -98,8 +98,23 @@ export interface ActionConfigTypes { Test: TestActionConfig } -// See https://melvingeorge.me/blog/convert-array-into-string-literal-union-type-typescript -export const actionStateTypes = ["getting-status", "ready", "not-ready", "processing", "failed", "unknown"] as const +/** + * These are the states returned from actions. + * + * See https://melvingeorge.me/blog/convert-array-into-string-literal-union-type-typescript + */ +export const actionStateTypes = ["ready", "not-ready", "processing", "failed", "unknown"] as const +export type ActionState = typeof actionStateTypes[number] + +/** + * These are the states emitted in status events. Here, we include additional states to help distinguish status event + * emitted around status/cache checks VS statuses emitted around the execution after a failed status check. + */ +export const actionStateTypesForEvent = [ + ...actionStateTypes, + "getting-status", + "cached", +] as const /** * This type represents the lifecycle of an individual action execution. * @@ -110,7 +125,7 @@ export const actionStateTypes = ["getting-status", "ready", "not-ready", "proces * initial state: "getting-status" * final state: "ready" or "failed" * - * "getting-status" -> "ready" | "not-ready" + * "getting-status" -> "cached" | "not-ready" * "not-ready" -> "processing" * "processing" -> "ready" | "failed" * ``` @@ -125,10 +140,13 @@ export const actionStateTypes = ["getting-status", "ready", "not-ready", "proces * - Or for a Kubernetes deployment, this might involve checking if the requested resources are already live and up * to date. * - * - `"ready"`: An up-to-date result exists for the action. - * - This state can be reached by a status check that returned a successful cache hit (e.g. an up-to-date build - * artifact / deployed resource / test result / run result already exists), or by successfully processing the - * action after getting a `"not-ready"` state from the status check. + * - `"cached"`: This state indicates a cache hit. An up-to-date result exists for the action. + * - This state can be reached e.g. when a status check indicates that an up-to-date build artifact / deployed + * resource / test result / run result already exists. + * + * - `"ready"`: The action was executed successfully, and an up-to-date result exists for the action. + * - This state can be reached by successfully processing the action after getting a `"not-ready"` state from the + * status check. * * - `"not-ready"`: No result (or no healthy result) for the action exists with the requested version. * - This state is reached by a status check that doesn't find an up-to-date result (e.g. no up-to-date container @@ -140,7 +158,11 @@ export const actionStateTypes = ["getting-status", "ready", "not-ready", "proces * - `"failed"`: The action was executed, but a failure or error occurred, so no up-to-date result was created for * the action. */ -export type ActionState = typeof actionStateTypes[number] +export type ActionStateForEvent = typeof actionStateTypesForEvent[number] + +export const stateForCacheStatusEvent = (state: ActionState): ActionStateForEvent => { + return state === "ready" ? "cached" : state +} export interface ActionStatus< T extends BaseAction = BaseAction, diff --git a/core/src/events.ts b/core/src/events.ts index d47cbeef9c..f15654425e 100644 --- a/core/src/events.ts +++ b/core/src/events.ts @@ -18,9 +18,9 @@ import type { CommandInfo } from "./plugin-context" import type { ActionReference } from "./config/common" import type { GraphResult } from "./graph/results" import { NamespaceStatus } from "./types/namespace" -import { sanitizeValue } from "./util/logging" -import { ActionState } from "./actions/types" import { BuildState } from "./plugin/handlers/Build/get-status" +import { ActionStateForEvent } from "./actions/types" +import { sanitizeValue } from "./util/logging" export type GardenEventListener = (payload: Events[T]) => void @@ -109,7 +109,7 @@ export interface ActionStatusPayload { moduleName: string | null // DEPRECATED: Remove in 0.14 startedAt: string completedAt?: string - state: ActionState + state: ActionStateForEvent status: S } diff --git a/core/src/plugins/exec/deploy.ts b/core/src/plugins/exec/deploy.ts index 769fbe7866..c1a83892bc 100644 --- a/core/src/plugins/exec/deploy.ts +++ b/core/src/plugins/exec/deploy.ts @@ -25,7 +25,7 @@ import { ExecLogsFollower } from "./logs" import { PluginContext } from "../../plugin-context" import { ExecDeploy } from "./config" import { DeployActionHandler } from "../../plugin/action-types" -import { DeployStatus } from "../../plugin/handlers/Deploy/get-status" +import { deployStateToActionState, DeployStatus } from "../../plugin/handlers/Deploy/get-status" import { Resolved } from "../../actions/types" import { convertCommandSpec, execRun, getDefaultEnvVars } from "./common" import { kill } from "process" @@ -50,7 +50,7 @@ export const getExecDeployStatus: DeployActionHandler<"getStatus", ExecDeploy> = const state = result.exitCode === 0 ? "ready" : "outdated" return { - state, + state: deployStateToActionState(state), detail: { state, version: action.versionString(), @@ -64,7 +64,7 @@ export const getExecDeployStatus: DeployActionHandler<"getStatus", ExecDeploy> = const state = "unknown" return { - state, + state: deployStateToActionState(state), detail: { state, version: action.versionString(), detail: {} }, outputs: { log: "", diff --git a/core/src/router/build.ts b/core/src/router/build.ts index d957456a65..59b043a25d 100644 --- a/core/src/router/build.ts +++ b/core/src/router/build.ts @@ -11,9 +11,8 @@ import chalk from "chalk" import { renderOutputStream } from "../util/util" import { PluginEventBroker } from "../plugin-context" import { BaseRouterParams, createActionRouter } from "./base" -import { ActionState } from "../actions/types" +import { ActionState, stateForCacheStatusEvent } from "../actions/types" import { PublishActionResult } from "../plugin/handlers/Build/publish" -import { uuidv4 } from "../util/random" export const buildRouter = (baseParams: BaseRouterParams) => createActionRouter("Build", baseParams, { @@ -42,14 +41,15 @@ export const buildRouter = (baseParams: BaseRouterParams) => handlerType: "getStatus", defaultHandler: async () => ({ state: "unknown", detail: {}, outputs: {} }), }) + const { state } = status // TODO-G2: only validate if state is ready? await router.validateActionOutputs(action, "runtime", status.outputs) garden.events.emit("buildStatus", { ...payloadAttrs, completedAt: new Date().toISOString(), - state: status.state, - status: { state: status.state === "ready" ? "fetched" : "outdated" }, + state: stateForCacheStatusEvent(state), + status: { state: state === "ready" ? "fetched" : "outdated" }, }) return status }, diff --git a/core/src/router/deploy.ts b/core/src/router/deploy.ts index 6698376c80..db12960d2c 100644 --- a/core/src/router/deploy.ts +++ b/core/src/router/deploy.ts @@ -8,7 +8,7 @@ import chalk from "chalk" import { omit } from "lodash" -import { ActionState } from "../actions/types" +import { ActionState, stateForCacheStatusEvent } from "../actions/types" import { PluginEventBroker } from "../plugin-context" import { DeployState } from "../types/service" import { renderOutputStream } from "../util/util" @@ -159,7 +159,7 @@ export const deployRouter = (baseParams: BaseRouterParams) => garden.events.emit("deployStatus", { ...payloadAttrs, completedAt: new Date().toISOString(), - state: result.state, + state: stateForCacheStatusEvent(result.state), status: omit(result.detail, "detail") }) diff --git a/core/src/router/run.ts b/core/src/router/run.ts index 63d82a44c2..83d67ce552 100644 --- a/core/src/router/run.ts +++ b/core/src/router/run.ts @@ -9,7 +9,7 @@ import { realpath } from "fs-extra" import normalizePath from "normalize-path" import tmp from "tmp-promise" -import { ActionState } from "../actions/types" +import { ActionState, stateForCacheStatusEvent } from "../actions/types" import { PluginEventBroker } from "../plugin-context" import { runStatusForEventPayload } from "../plugin/base" import { copyArtifacts, getArtifactKey } from "../util/artifacts" @@ -124,7 +124,7 @@ export const runRouter = (baseParams: BaseRouterParams) => garden.events.emit("runStatus", { ...payloadAttrs, - state: result.state, + state: stateForCacheStatusEvent(result.state), completedAt: new Date().toISOString(), status: runStatusForEventPayload(result.detail), }) diff --git a/core/src/router/test.ts b/core/src/router/test.ts index 21f09ee514..5bcf6dd6c4 100644 --- a/core/src/router/test.ts +++ b/core/src/router/test.ts @@ -8,7 +8,7 @@ import { realpath } from "fs-extra" import normalizePath from "normalize-path" -import { ActionState } from "../actions/types" +import { ActionState, stateForCacheStatusEvent } from "../actions/types" import { PluginEventBroker } from "../plugin-context" import { runStatusForEventPayload } from "../plugin/base" import { copyArtifacts, getArtifactKey } from "../util/artifacts" @@ -122,7 +122,7 @@ export const testRouter = (baseParams: BaseRouterParams) => garden.events.emit("testStatus", { ...payloadAttrs, - state: result.state, + state: stateForCacheStatusEvent(result.state), completedAt: new Date().toISOString(), status: runStatusForEventPayload(result.detail), }) diff --git a/core/test/unit/src/router/build.ts b/core/test/unit/src/router/build.ts index 15b5a80ecb..7dae2e034f 100644 --- a/core/test/unit/src/router/build.ts +++ b/core/test/unit/src/router/build.ts @@ -64,7 +64,7 @@ describe("build actions", () => { expect(event2.name).to.eql("buildStatus") expect(event2.payload.moduleName).to.eql("module-a") expect(event2.payload.actionUid).to.eql(event1.payload.actionUid) - expect(event2.payload.state).to.eql("ready") + expect(event2.payload.state).to.eql("cached") expect(event2.payload.status.state).to.eql("fetched") }) }) diff --git a/core/test/unit/src/router/deploy.ts b/core/test/unit/src/router/deploy.ts index 1ed2562358..b0edc6b2d8 100644 --- a/core/test/unit/src/router/deploy.ts +++ b/core/test/unit/src/router/deploy.ts @@ -80,7 +80,7 @@ describe("deploy actions", () => { expect(event2.payload.actionVersion).to.eql(resolvedDeployAction.versionString()) expect(event2.payload.moduleName).to.eql("module-a") expect(event2.payload.actionUid).to.eql(event1.payload.actionUid) - expect(event2.payload.state).to.eql("ready") + expect(event2.payload.state).to.eql("cached") expect(event2.payload.status.state).to.eql("ready") }) diff --git a/core/test/unit/src/router/run.ts b/core/test/unit/src/router/run.ts index 82ad2caf5c..4c50e4387d 100644 --- a/core/test/unit/src/router/run.ts +++ b/core/test/unit/src/router/run.ts @@ -93,7 +93,7 @@ describe("run actions", () => { expect(event2.name).to.eql("runStatus") expect(event2.payload.moduleName).to.eql("module-a") expect(event2.payload.actionUid).to.eql(event1.payload.actionUid) - expect(event2.payload.state).to.eql("ready") + expect(event2.payload.state).to.eql("cached") expect(event2.payload.status.state).to.eql("succeeded") }) diff --git a/core/test/unit/src/router/test.ts b/core/test/unit/src/router/test.ts index 6e5b1183db..f86628226c 100644 --- a/core/test/unit/src/router/test.ts +++ b/core/test/unit/src/router/test.ts @@ -201,7 +201,7 @@ describe("test actions", () => { expect(event2.name).to.eql("testStatus") expect(event1.payload.moduleName).to.eql("module-a") expect(event2.payload.actionUid).to.eql(event1.payload.actionUid) - expect(event2.payload.state).to.eql("ready") + expect(event2.payload.state).to.eql("cached") expect(event2.payload.status.state).to.eql("succeeded") }) })