Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ACNA-3231 | E2e tests failure #815

Open
wants to merge 6 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 17 additions & 7 deletions src/commands/app/deploy.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ const BaseCommand = require('../../BaseCommand')
const BuildCommand = require('./build')
const webLib = require('@adobe/aio-lib-web')
const { Flags } = require('@oclif/core')
const { createWebExportFilter, runInProcess, buildExtensionPointPayloadWoMetadata, buildExcShellViewExtensionMetadata, getCliInfo } = require('../../lib/app-helper')
const { createWebExportFilter, runInProcess, buildExtensionPointPayloadWoMetadata, buildExcShellViewExtensionMetadata, getCliInfo, checkifAccessTokenExists } = require('../../lib/app-helper')
const rtLib = require('@adobe/aio-lib-runtime')
const LogForwarding = require('../../lib/log-forwarding')
const { sendAuditLogs, getAuditLogEvent, getFilesCountWithExtension } = require('../../lib/audit-logger')
Expand All @@ -35,6 +35,8 @@ class Deploy extends BuildCommand {
flags['web-assets'] = flags['web-assets'] && !flags.action
flags.publish = flags.publish && !flags.action

const doesTokenExists = await checkifAccessTokenExists()

const deployConfigs = await this.getAppExtConfigs(flags)
const keys = Object.keys(deployConfigs)
const values = Object.values(deployConfigs)
Expand All @@ -52,7 +54,7 @@ class Deploy extends BuildCommand {

try {
const aioConfig = (await this.getFullConfig()).aio
const cliDetails = await getCliInfo()
const cliDetails = doesTokenExists ? await getCliInfo() : null
amulyakashyap09 marked this conversation as resolved.
Show resolved Hide resolved

// 1. update log forwarding configuration
// note: it is possible that .aio file does not exist, which means there is no local lg config
Expand All @@ -64,7 +66,7 @@ class Deploy extends BuildCommand {
const lfConfig = lf.getLocalConfigWithSecrets()
if (lfConfig.isDefined()) {
await lf.updateServerConfig(lfConfig)
spinner.succeed(chalk.green(`Log forwarding is set to '${lfConfig.getDestination()}'`))
spinner.succeed(chalk.green(`\nLog forwarding is set to '${lfConfig.getDestination()}'`))
} else {
if (flags.verbose) {
spinner.info(chalk.dim('Log forwarding is not updated: no configuration is provided'))
Expand Down Expand Up @@ -94,8 +96,12 @@ class Deploy extends BuildCommand {

// 3. send deploy log event
const logEvent = getAuditLogEvent(flags, aioConfig.project, 'AB_APP_DEPLOY')
if (logEvent) {
await sendAuditLogs(cliDetails.accessToken, logEvent, cliDetails.env)
if (logEvent && cliDetails) {
try {
await sendAuditLogs(cliDetails.accessToken, logEvent, cliDetails.env)
} catch (error) {
this.log(chalk.red(chalk.bold('Error: Audit Log Service Error: Failed to send audit log event for deployment.')))
amulyakashyap09 marked this conversation as resolved.
Show resolved Hide resolved
}
} else {
this.log(chalk.red(chalk.bold('Warning: No valid config data found to send audit log event for deployment.')))
}
Expand All @@ -111,9 +117,13 @@ class Deploy extends BuildCommand {
if (v.app.hasFrontend && flags['web-assets']) {
const opItems = getFilesCountWithExtension(v.web.distProd)
const assetDeployedLogEvent = getAuditLogEvent(flags, aioConfig.project, 'AB_APP_ASSETS_DEPLOYED')
if (assetDeployedLogEvent) {
if (assetDeployedLogEvent && cliDetails) {
assetDeployedLogEvent.data.opItems = opItems
await sendAuditLogs(cliDetails.accessToken, assetDeployedLogEvent, cliDetails.env)
try {
await sendAuditLogs(cliDetails.accessToken, logEvent, cliDetails.env)
} catch (error) {
this.log(chalk.red(chalk.bold('Error: Audit Log Service Error: Failed to send audit log event for deployment.')))
amulyakashyap09 marked this conversation as resolved.
Show resolved Hide resolved
}
}
}
}
Expand Down
21 changes: 15 additions & 6 deletions src/commands/app/undeploy.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,15 @@ const { Flags } = require('@oclif/core')

const BaseCommand = require('../../BaseCommand')
const webLib = require('@adobe/aio-lib-web')
const { runInProcess, buildExtensionPointPayloadWoMetadata, getCliInfo } = require('../../lib/app-helper')
const { runInProcess, buildExtensionPointPayloadWoMetadata, getCliInfo, checkifAccessTokenExists } = require('../../lib/app-helper')
const rtLib = require('@adobe/aio-lib-runtime')
const { sendAuditLogs, getAuditLogEvent } = require('../../lib/audit-logger')

class Undeploy extends BaseCommand {
async run () {
// cli input
const { flags } = await this.parse(Undeploy)
const doesTokenExists = await checkifAccessTokenExists()

const undeployConfigs = await this.getAppExtConfigs(flags)
let libConsoleCLI
Expand All @@ -46,12 +47,16 @@ class Undeploy extends BaseCommand {
const spinner = ora()
try {
const aioConfig = (await this.getFullConfig()).aio
const cliDetails = await getCliInfo()
const cliDetails = doesTokenExists ? await getCliInfo() : null
amulyakashyap09 marked this conversation as resolved.
Show resolved Hide resolved
const logEvent = getAuditLogEvent(flags, aioConfig.project, 'AB_APP_UNDEPLOY')

// 1.1. send audit log event for successful undeploy
if (logEvent) {
await sendAuditLogs(cliDetails.accessToken, logEvent, cliDetails.env)
if (logEvent && cliDetails) {
try {
await sendAuditLogs(cliDetails.accessToken, logEvent, cliDetails.env)
} catch (error) {
this.log(chalk.red(chalk.bold('Error: Audit Log Service Error: Failed to send audit log event for deployment.')))
amulyakashyap09 marked this conversation as resolved.
Show resolved Hide resolved
}
} else {
this.log(chalk.red(chalk.bold('Warning: No valid config data found to send audit log event for deployment.')))
}
Expand All @@ -61,8 +66,12 @@ class Undeploy extends BaseCommand {
const v = values[i]
await this.undeployOneExt(k, v, flags, spinner)
const assetUndeployLogEvent = getAuditLogEvent(flags, aioConfig.project, 'AB_APP_ASSETS_UNDEPLOYED')
if (assetUndeployLogEvent) {
await sendAuditLogs(cliDetails.accessToken, assetUndeployLogEvent, cliDetails.env)
if (assetUndeployLogEvent && cliDetails) {
try {
await sendAuditLogs(cliDetails.accessToken, assetUndeployLogEvent, cliDetails.env)
} catch (error) {
this.log(chalk.red(chalk.bold('Error: Audit Log Service Error: Failed to send audit log event for deployment.')))
amulyakashyap09 marked this conversation as resolved.
Show resolved Hide resolved
}
}
}

Expand Down
32 changes: 30 additions & 2 deletions src/lib/app-helper.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@ const { EOL } = require('os')
const { getCliEnv } = require('@adobe/aio-lib-env')
const yaml = require('js-yaml')
const RuntimeLib = require('@adobe/aio-lib-runtime')
const { exec } = require('child_process')
const util = require('util')
const execAsync = util.promisify(exec)

/** @private */
function isNpmInstalled () {
Expand Down Expand Up @@ -155,7 +158,7 @@ async function runScript (command, dir, cmdArgs = []) {
aioLogger.debug(`Killing ${command} event hook long-running process (pid: ${pid})`)
process.kill(pid, 'SIGTERM')
} catch (_) {
// do nothing if pid not found
// do nothing if pid not found
}
})
}
Expand Down Expand Up @@ -567,6 +570,30 @@ function getObjectValue (obj, key) {
return keys.filter(o => o.trim()).reduce((o, i) => o && getObjectProp(o, i), obj)
}

/**
* Function to run `aio config get ims.contexts.cli.access_token.token` command and retrieve the token
* This function is used to check if the access token exists in the CLI context or not
* @returns {Promise<boolean>} Resolves to the access token if it exists, else false
*/
const checkifAccessTokenExists = async () => {
try {
const { stdout, stderr } = await execAsync('aio config get ims.contexts.cli.access_token.token')
amulyakashyap09 marked this conversation as resolved.
Show resolved Hide resolved
if (stderr) {
console.warn(`Warning: ${stderr}`)
return false
}
if (!stdout || stdout === '') {
console.info('No token found')
return false
}
console.log(`Access token found: ${stdout}`)
return true
} catch (error) {
console.error(`Error retrieving token: ${error.message}`)
return false
}
}

module.exports = {
getObjectValue,
getObjectProp,
Expand Down Expand Up @@ -596,5 +623,6 @@ module.exports = {
buildExtensionPointPayloadWoMetadata,
buildExcShellViewExtensionMetadata,
atLeastOne,
deleteUserConfig
deleteUserConfig,
checkifAccessTokenExists
}
4 changes: 2 additions & 2 deletions src/lib/audit-logger.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ const chalk = require('chalk')
const OPERATIONS = {
AB_APP_DEPLOY: 'ab_app_deploy',
AB_APP_UNDEPLOY: 'ab_app_undeploy',
AB_APP_TEST: 'ab_app_test', // todo : remove after testing
AB_APP_TEST: 'ab_app_test',
AB_APP_ASSETS_DEPLOYED: 'ab_app_assets_deployed',
AB_APP_ASSETS_UNDEPLOYED: 'ab_app_assets_undeployed'
}
Expand All @@ -33,7 +33,7 @@ const AUDIT_SERVICE_ENPOINTS = {
* @param {string} env valid env stage|prod
*/
async function sendAuditLogs (accessToken, logEvent, env = 'prod') {
const url = AUDIT_SERVICE_ENPOINTS[env]
const url = AUDIT_SERVICE_ENPOINTS[env] ?? AUDIT_SERVICE_ENPOINTS.prod
amulyakashyap09 marked this conversation as resolved.
Show resolved Hide resolved
const payload = {
event: logEvent
}
Expand Down
87 changes: 86 additions & 1 deletion test/commands/app/deploy.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,7 @@ beforeEach(() => {
}
})
LogForwarding.init.mockResolvedValue(mockLogForwarding)
helpers.checkifAccessTokenExists.mockResolvedValue(true)
})

test('exports', async () => {
Expand Down Expand Up @@ -1302,7 +1303,7 @@ describe('run', () => {
expect(auditLogger.sendAuditLogs).toHaveBeenCalledWith(mockToken, expect.objectContaining({ orgId: mockOrg, projectId: mockProject, workspaceId: mockWorkspaceId, workspaceName: mockWorkspaceName }), mockEnv)
})

test('Do not send audit logs for successful app deploy', async () => {
test('Do not send audit logs for successful app deploy, if no logevent is present', async () => {
const mockToken = 'mocktoken'
const mockEnv = 'stage'
const mockOrg = 'mockorg'
Expand Down Expand Up @@ -1338,6 +1339,43 @@ describe('run', () => {
expect(mockWebLib.deployWeb).toHaveBeenCalledTimes(1)
})

test('Do not send audit logs for successful app deploy, if no-login case', async () => {
const mockToken = 'mocktoken'
const mockEnv = 'stage'
const mockOrg = 'mockorg'
const mockProject = 'mockproject'
const mockWorkspaceId = 'mockworkspaceid'
const mockWorkspaceName = 'mockworkspacename'
helpers.getCliInfo.mockResolvedValueOnce({
accessToken: mockToken,
env: mockEnv
})
helpers.checkifAccessTokenExists.mockResolvedValueOnce(false)
command.getFullConfig = jest.fn().mockReturnValue({
aio: {
project: {
id: mockProject,
org: {
id: mockOrg
},
workspace: {
id: mockWorkspaceId,
name: mockWorkspaceName
}
}
}
})

auditLogger.getAuditLogEvent.mockImplementation((flags, project, event) => null)

command.getAppExtConfigs.mockResolvedValueOnce(createAppConfig(command.appConfig))

await command.run()
expect(command.error).toHaveBeenCalledTimes(0)
expect(mockRuntimeLib.deployActions).toHaveBeenCalledTimes(1)
expect(mockWebLib.deployWeb).toHaveBeenCalledTimes(1)
})

test('Send audit logs for successful app deploy + web assets', async () => {
const mockToken = 'mocktoken'
const mockEnv = 'stage'
Expand Down Expand Up @@ -1377,4 +1415,51 @@ describe('run', () => {
expect(auditLogger.getFilesCountWithExtension).toHaveBeenCalledTimes(2)
expect(auditLogger.sendAuditLogs).toHaveBeenCalledWith(mockToken, expect.objectContaining({ orgId: mockOrg, projectId: mockProject, workspaceId: mockWorkspaceId, workspaceName: mockWorkspaceName }), mockEnv)
})

test('Should deploy successfully even if Audit log service is unavailable', async () => {
const mockToken = 'mocktoken'
const mockEnv = 'stage'
const mockOrg = 'mockorg'
const mockProject = 'mockproject'
const mockWorkspaceId = 'mockworkspaceid'
const mockWorkspaceName = 'mockworkspacename'
helpers.getCliInfo.mockResolvedValueOnce({
accessToken: mockToken,
env: mockEnv
})
command.getFullConfig = jest.fn().mockReturnValue({
aio: {
project: {
id: mockProject,
org: {
id: mockOrg
},
workspace: {
id: mockWorkspaceId,
name: mockWorkspaceName
}
}
}
})

auditLogger.sendAuditLogs.mockRejectedValue({
message: 'Internal Server Error',
status: 500
})

command.getAppExtConfigs.mockResolvedValueOnce(createAppConfig(command.appConfig))

await command.run()
expect(command.log).toHaveBeenCalledWith(
expect.stringContaining('skipping publish phase...')
)

expect(command.log).toHaveBeenCalledWith(
expect.stringContaining('Successful deployment 🏄')
)
expect(auditLogger.sendAuditLogs).toHaveBeenCalledTimes(2)
expect(command.error).toHaveBeenCalledTimes(0)
expect(mockRuntimeLib.deployActions).toHaveBeenCalledTimes(1)
expect(mockWebLib.deployWeb).toHaveBeenCalledTimes(1)
})
})
71 changes: 70 additions & 1 deletion test/commands/app/undeploy.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -476,6 +476,8 @@ describe('run', () => {
const mockWorkspaceId = 'mockworkspaceid'
const mockWorkspaceName = 'mockworkspacename'

helpers.checkifAccessTokenExists.mockResolvedValue(true)

command.getFullConfig = jest.fn().mockReturnValue({
aio: {
project: {
Expand All @@ -500,7 +502,38 @@ describe('run', () => {
expect(auditLogger.sendAuditLogs).toHaveBeenCalledWith(mockToken, expect.objectContaining({ orgId: mockOrg, projectId: mockProject, workspaceId: mockWorkspaceId, workspaceName: mockWorkspaceName }), mockEnv)
})

test('Do not Send audit logs for successful app undeploy', async () => {
test('Do not Send audit logs for successful app undeploy if no-login case', async () => {
const mockOrg = 'mockorg'
const mockProject = 'mockproject'
const mockWorkspaceId = 'mockworkspaceid'
const mockWorkspaceName = 'mockworkspacename'

helpers.checkifAccessTokenExists.mockResolvedValueOnce(false)

command.getFullConfig = jest.fn().mockReturnValue({
aio: {
project: {
id: mockProject,
org: {
id: mockOrg
},
workspace: {
id: mockWorkspaceId,
name: mockWorkspaceName
}
}
}
})
command.getAppExtConfigs.mockResolvedValueOnce(createAppConfig(command.appConfig))

await command.run()
expect(command.error).toHaveBeenCalledTimes(0)
expect(mockRuntimeLib.undeployActions).toHaveBeenCalledTimes(1)
expect(mockWebLib.undeployWeb).toHaveBeenCalledTimes(1)
expect(auditLogger.sendAuditLogs.mock.calls.length).toBe(0)
})

test('Do not Send audit logs for successful app undeploy, if no logevent is present', async () => {
const mockOrg = 'mockorg'
const mockProject = 'mockproject'
const mockWorkspaceId = 'mockworkspaceid'
Expand Down Expand Up @@ -531,4 +564,40 @@ describe('run', () => {
expect(auditLogger.sendAuditLogs.mock.calls.length).toBe(0)
expect(command.log).toHaveBeenCalledWith(expect.stringMatching(/Warning: No valid config data found to send audit log event for deployment/))
})

test('Should app undeploy successfully even if Audit Log Service is not available', async () => {
const mockOrg = 'mockorg'
const mockProject = 'mockproject'
const mockWorkspaceId = 'mockworkspaceid'
const mockWorkspaceName = 'mockworkspacename'

command.getFullConfig = jest.fn().mockReturnValue({
aio: {
project: {
id: mockProject,
org: {
id: mockOrg
},
workspace: {
id: mockWorkspaceId,
name: mockWorkspaceName
}
}
}
})
command.getAppExtConfigs.mockResolvedValueOnce(createAppConfig(command.appConfig))
helpers.checkifAccessTokenExists.mockResolvedValue(true)

auditLogger.sendAuditLogs.mockRejectedValue({
message: 'Internal Server Error',
status: 500
})

await command.run()
expect(command.error).toHaveBeenCalledTimes(0)
expect(mockRuntimeLib.undeployActions).toHaveBeenCalledTimes(1)
expect(mockWebLib.undeployWeb).toHaveBeenCalledTimes(1)
expect(auditLogger.sendAuditLogs).toHaveBeenCalledTimes(2)
expect(command.log).toHaveBeenCalledWith(expect.stringMatching('Error: Audit Log Service Error: Failed to send audit log event for deployment.'))
amulyakashyap09 marked this conversation as resolved.
Show resolved Hide resolved
})
})
Loading