Skip to content

Commit 0c08360

Browse files
amulyakashyap09MichaelGoberlingAmulya Kashyapshazron
authored
ACNA-3275 point app plugin to deploy service (#852)
* wip: use ims token to auth runtime deploy actions * default values set * added test cases * fix: missed process env value * Updated code as per comments on PR * Updated code as per comments on PR * Updated code as per comments on PR * Updated code as per comments on PR * feat: ACNA-3602 - Added the Feature Flag for Deploy Service * Updated lib-runtime-dependency * fixed default runtime url * Fixed a scenario, where user does config api host and default runtime url needs to be in place --------- Co-authored-by: Michael Goberling <mgoberling@adobe.com> Co-authored-by: Amulya Kashyap <amulyak@adobe.com> Co-authored-by: Shazron Abdullah <36107+shazron@users.noreply.github.com>
1 parent 8d7f1ee commit 0c08360

9 files changed

Lines changed: 286 additions & 4 deletions

File tree

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
"@adobe/aio-lib-core-networking": "^5",
1313
"@adobe/aio-lib-env": "^3",
1414
"@adobe/aio-lib-ims": "^7",
15-
"@adobe/aio-lib-runtime": "^7.0.1",
15+
"@adobe/aio-lib-runtime": "^7.1.0",
1616
"@adobe/aio-lib-templates": "^3",
1717
"@adobe/aio-lib-web": "^7",
1818
"@adobe/generator-aio-app": "^7",

src/commands/app/config/set/log-forwarding.js

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,16 @@ governing permissions and limitations under the License.
1212
const BaseCommand = require('../../../../BaseCommand')
1313
const LogForwarding = require('../../../../lib/log-forwarding')
1414
const aioLogger = require('@adobe/aio-lib-core-logging')('@adobe/aio-cli-plugin-app:lf:set', { provider: 'debug' })
15+
const { setRuntimeApiHostAndAuthHandler } = require('../../../../lib/auth-helper')
1516

1617
class LogForwardingCommand extends BaseCommand {
1718
async run () {
18-
const lf = await LogForwarding.init((await this.getFullConfig()).aio)
19+
let aioConfig = (await this.getFullConfig()).aio
20+
// TODO: remove this check once the deploy service is enabled by default
21+
if (process.env.IS_DEPLOY_SERVICE_ENABLED === 'true') {
22+
aioConfig = setRuntimeApiHostAndAuthHandler(aioConfig)
23+
}
24+
const lf = await LogForwarding.init(aioConfig)
1925

2026
const destination = await this.promptDestination(lf.getSupportedDestinations())
2127
const destinationSettingsConfig = lf.getSettingsConfig(destination)

src/commands/app/deploy.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ const { runInProcess, buildExtensionPointPayloadWoMetadata, buildExcShellViewExt
2222
const rtLib = require('@adobe/aio-lib-runtime')
2323
const LogForwarding = require('../../lib/log-forwarding')
2424
const { sendAuditLogs, getAuditLogEvent, getFilesCountWithExtension } = require('../../lib/audit-logger')
25+
const { setRuntimeApiHostAndAuthHandler } = require('../../lib/auth-helper')
2526
const logActions = require('../../lib/log-actions')
2627

2728
const PRE_DEPLOY_EVENT_REG = 'pre-deploy-event-reg'
@@ -100,7 +101,8 @@ class Deploy extends BuildCommand {
100101
// - break into smaller pieces deploy, allowing to first deploy all actions then all web assets
101102
for (let i = 0; i < keys.length; ++i) {
102103
const k = keys[i]
103-
const v = values[i]
104+
const v = setRuntimeApiHostAndAuthHandler(values[i])
105+
104106
await this.deploySingleConfig(k, v, flags, spinner)
105107
if (v.app.hasFrontend && flags['web-assets']) {
106108
const opItems = getFilesCountWithExtension(v.web.distProd)

src/commands/app/undeploy.js

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ const webLib = require('@adobe/aio-lib-web')
2020
const { runInProcess, buildExtensionPointPayloadWoMetadata, getCliInfo } = require('../../lib/app-helper')
2121
const rtLib = require('@adobe/aio-lib-runtime')
2222
const { sendAuditLogs, getAuditLogEvent } = require('../../lib/audit-logger')
23+
const { setRuntimeApiHostAndAuthHandler } = require('../../lib/auth-helper')
2324

2425
class Undeploy extends BaseCommand {
2526
async run () {
@@ -55,7 +56,9 @@ class Undeploy extends BaseCommand {
5556

5657
for (let i = 0; i < keys.length; ++i) {
5758
const k = keys[i]
58-
const v = values[i]
59+
// TODO: remove this check once the deploy service is enabled by default
60+
const v = process.env.IS_DEPLOY_SERVICE_ENABLED === 'true' ? setRuntimeApiHostAndAuthHandler(values[i]) : values[i]
61+
5962
await this.undeployOneExt(k, v, flags, spinner)
6063
const assetUndeployLogEvent = getAuditLogEvent(flags, aioConfig.project, 'AB_APP_ASSETS_UNDEPLOYED')
6164
// send logs for case of web-assets undeployment

src/lib/auth-helper.js

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
/*
2+
Copyright 2024 Adobe. All rights reserved.
3+
This file is licensed to you under the Apache License, Version 2.0 (the "License");
4+
you may not use this file except in compliance with the License. You may obtain a copy
5+
of the License at http://www.apache.org/licenses/LICENSE-2.0
6+
Unless required by applicable law or agreed to in writing, software distributed under
7+
the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
8+
OF ANY KIND, either express or implied. See the License for the specific language
9+
governing permissions and limitations under the License.
10+
*/
11+
12+
const { getToken, context } = require('@adobe/aio-lib-ims')
13+
const { CLI } = require('@adobe/aio-lib-ims/src/context')
14+
const { getCliEnv } = require('@adobe/aio-lib-env')
15+
const defaultRuntimeUrl = 'https://adobeioruntime.net'
16+
17+
/**
18+
* For use with the openwhisk client js library to send a bearer token instead of basic
19+
* auth to the openwhisk service. Set this to the auth_handler option when initializing
20+
*/
21+
const bearerAuthHandler = {
22+
getAuthHeader: async () => {
23+
await context.setCli({ 'cli.bare-output': true }, false) // set this globally
24+
25+
const env = getCliEnv()
26+
27+
console.debug(`Retrieving CLI Token using env=${env}`)
28+
const accessToken = await getToken(CLI)
29+
30+
return `Bearer ${accessToken}`
31+
}
32+
}
33+
34+
const setRuntimeApiHostAndAuthHandler = (config) => {
35+
// TODO: remove this check once the deploy service is enabled by default
36+
if (process.env.IS_DEPLOY_SERVICE_ENABLED === 'true') {
37+
const aioConfig = (config && 'runtime' in config) ? config : null
38+
if (aioConfig) {
39+
aioConfig.runtime.apihost = process.env.AIO_RUNTIME_APIHOST ?? defaultRuntimeUrl
40+
aioConfig.runtime.auth_handler = bearerAuthHandler
41+
return aioConfig
42+
}
43+
const owConfig = (config && 'ow' in config) ? config : null
44+
if (owConfig) {
45+
owConfig.ow.apihost = process.env.AIO_RUNTIME_APIHOST ?? defaultRuntimeUrl
46+
owConfig.ow.auth_handler = bearerAuthHandler
47+
return owConfig
48+
}
49+
} else {
50+
if (config && config.runtime) {
51+
config.runtime.apihost = process.env.AIO_RUNTIME_APIHOST ?? defaultRuntimeUrl
52+
}
53+
if (config && config.ow) {
54+
config.ow.apihost = process.env.AIO_RUNTIME_APIHOST ?? defaultRuntimeUrl
55+
}
56+
}
57+
return config
58+
}
59+
60+
module.exports = {
61+
bearerAuthHandler,
62+
setRuntimeApiHostAndAuthHandler
63+
}

test/commands/app/config/set/log-forwarding.test.js

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,9 @@ const { stdout } = require('stdout-stderr')
1414
const TheCommand = require('../../../../../src/commands/app/config/set/log-forwarding.js')
1515
const LogForwarding = require('../../../../../src/lib/log-forwarding')
1616

17+
jest.mock('../../../../../src/lib/auth-helper')
18+
const authHelper = require('../../../../../src/lib/auth-helper')
19+
1720
jest.mock('../../../../../src/lib/log-forwarding', () => {
1821
const orig = jest.requireActual('../../../../../src/lib/log-forwarding')
1922
return {
@@ -46,6 +49,7 @@ beforeEach(async () => {
4649
getConfigFromJson: jest.fn()
4750
}
4851
LogForwarding.init.mockResolvedValue(lf)
52+
authHelper.setRuntimeApiHostAndAuthHandler.mockImplementation(aioConfig => aioConfig)
4953
})
5054

5155
test('set log forwarding destination and save local', async () => {
@@ -86,6 +90,50 @@ test('set log forwarding destination and save local', async () => {
8690
expect(setCall).toHaveBeenCalledWith(new LogForwarding.LogForwardingConfig(destination, input))
8791
expect(localSetCall).toHaveBeenCalledTimes(1)
8892
expect(localSetCall).toHaveBeenCalledWith(new LogForwarding.LogForwardingConfig(destination, fullSanitizedSettings))
93+
expect(authHelper.setRuntimeApiHostAndAuthHandler).not.toHaveBeenCalled()
94+
})
95+
96+
test('should Invoke setRuntimeApiHostAndAuthHandler if IS_DEPLOY_SERVICE_ENABLED = ture and set log forwarding destination', async () => {
97+
process.env.IS_DEPLOY_SERVICE_ENABLED = true
98+
const destination = 'destination'
99+
const input = {
100+
field_one: 'val_one',
101+
field_two: 'val_two',
102+
secret: 'val_secret'
103+
}
104+
command.prompt.mockResolvedValueOnce({ type: destination })
105+
command.prompt.mockResolvedValueOnce(input)
106+
const serverSanitizedSettings = {
107+
field_one: 'val_one',
108+
field_two: 'val_two sanitized'
109+
}
110+
const fullSanitizedSettings = {
111+
field_one: 'val_one',
112+
field_two: 'val_two sanitized',
113+
secret: 'val_secret'
114+
}
115+
const setCall = jest.fn().mockResolvedValue({
116+
destination: serverSanitizedSettings
117+
})
118+
const localSetCall = jest.fn()
119+
lf.updateServerConfig = setCall
120+
lf.updateLocalConfig = localSetCall.mockResolvedValue()
121+
lf.getConfigFromJson.mockReturnValue(new LogForwarding.LogForwardingConfig(destination, serverSanitizedSettings))
122+
123+
await expect(command.run()).resolves.not.toThrow()
124+
expect(command.prompt).toHaveBeenNthCalledWith(1, [{
125+
name: 'type',
126+
message: 'select log forwarding destination',
127+
type: 'list',
128+
choices: [{ name: 'Destination', value: 'destination' }]
129+
}])
130+
expect(stdout.output).toMatch(`Log forwarding is set to '${destination}'\nLog forwarding settings are saved to the local configuration`)
131+
expect(setCall).toHaveBeenCalledTimes(1)
132+
expect(setCall).toHaveBeenCalledWith(new LogForwarding.LogForwardingConfig(destination, input))
133+
expect(localSetCall).toHaveBeenCalledTimes(1)
134+
expect(localSetCall).toHaveBeenCalledWith(new LogForwarding.LogForwardingConfig(destination, fullSanitizedSettings))
135+
expect(authHelper.setRuntimeApiHostAndAuthHandler).toHaveBeenCalledTimes(1)
136+
process.env.IS_DEPLOY_SERVICE_ENABLED = false
89137
})
90138

91139
test('set log forwarding destination and fail save local', async () => {

test/commands/app/deploy.test.js

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,9 @@ const helpers = require('../../../src/lib/app-helper.js')
2424
jest.mock('../../../src/lib/audit-logger.js')
2525
const auditLogger = require('../../../src/lib/audit-logger.js')
2626

27+
jest.mock('../../../src/lib/auth-helper')
28+
const authHelper = require('../../../src/lib/auth-helper')
29+
2730
const mockWebLib = require('@adobe/aio-lib-web')
2831
const mockRuntimeLib = require('@adobe/aio-lib-runtime')
2932

@@ -191,6 +194,7 @@ beforeEach(() => {
191194
'1 HTML page(s)'
192195
]
193196
})
197+
authHelper.setRuntimeApiHostAndAuthHandler.mockImplementation((aioConfig) => aioConfig)
194198
helpers.getCliInfo.mockImplementation(() => {
195199
return {
196200
accessToken: 'mocktoken',
@@ -1293,6 +1297,45 @@ describe('run', () => {
12931297
expect(command.error).toHaveBeenCalledTimes(1)
12941298
})
12951299

1300+
test('Should invoke setRuntimeApiHostAndAuthHandler if IS_DEPLOY_SERVICE_ENABLED = true', async () => {
1301+
process.env.IS_DEPLOY_SERVICE_ENABLED = true
1302+
1303+
const mockToken = 'mocktoken'
1304+
const mockEnv = 'stage'
1305+
const mockOrg = 'mockorg'
1306+
const mockProject = 'mockproject'
1307+
const mockWorkspaceId = 'mockworkspaceid'
1308+
const mockWorkspaceName = 'mockworkspacename'
1309+
helpers.getCliInfo.mockResolvedValueOnce({
1310+
accessToken: mockToken,
1311+
env: mockEnv
1312+
})
1313+
command.getFullConfig = jest.fn().mockReturnValue({
1314+
aio: {
1315+
project: {
1316+
id: mockProject,
1317+
org: {
1318+
id: mockOrg
1319+
},
1320+
workspace: {
1321+
id: mockWorkspaceId,
1322+
name: mockWorkspaceName
1323+
}
1324+
}
1325+
}
1326+
})
1327+
command.getAppExtConfigs.mockResolvedValueOnce(createAppConfig(command.appConfig))
1328+
1329+
await command.run()
1330+
expect(command.error).toHaveBeenCalledTimes(0)
1331+
expect(mockRuntimeLib.deployActions).toHaveBeenCalledTimes(1)
1332+
expect(mockWebLib.deployWeb).toHaveBeenCalledTimes(1)
1333+
expect(auditLogger.sendAuditLogs).toHaveBeenCalledTimes(1)
1334+
expect(authHelper.setRuntimeApiHostAndAuthHandler).toHaveBeenCalledTimes(1)
1335+
expect(auditLogger.sendAuditLogs).toHaveBeenCalledWith(mockToken, expect.objectContaining({ orgId: mockOrg, projectId: mockProject, workspaceId: mockWorkspaceId, workspaceName: mockWorkspaceName }), mockEnv)
1336+
process.env.IS_DEPLOY_SERVICE_ENABLED = false
1337+
})
1338+
12961339
test('Send audit logs for successful app deploy', async () => {
12971340
const mockToken = 'mocktoken'
12981341
const mockEnv = 'stage'

test/commands/app/undeploy.test.js

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,9 @@ const helpers = require('../../../src/lib/app-helper.js')
2020
jest.mock('../../../src/lib/audit-logger.js')
2121
const auditLogger = require('../../../src/lib/audit-logger.js')
2222

23+
jest.mock('../../../src/lib/auth-helper.js')
24+
const authHelper = require('../../../src/lib/auth-helper.js')
25+
2326
const mockFS = require('fs-extra')
2427
jest.mock('fs-extra')
2528

@@ -78,6 +81,7 @@ beforeEach(() => {
7881
env: 'stage'
7982
}
8083
})
84+
authHelper.setRuntimeApiHostAndAuthHandler.mockImplementation(aioConfig => aioConfig)
8185
jest.clearAllMocks()
8286
})
8387

@@ -472,6 +476,38 @@ describe('run', () => {
472476
expect(command.error).toHaveBeenCalledTimes(1)
473477
})
474478

479+
test('Should invoke setRuntimeApiHostAndAuthHandler if IS_DEPLOY_SERVICE_ENABLED = true', async () => {
480+
const mockOrg = 'mockorg'
481+
const mockProject = 'mockproject'
482+
const mockWorkspaceId = 'mockworkspaceid'
483+
const mockWorkspaceName = 'mockworkspacename'
484+
485+
process.env.IS_DEPLOY_SERVICE_ENABLED = true
486+
487+
command.getFullConfig = jest.fn().mockReturnValue({
488+
aio: {
489+
project: {
490+
id: mockProject,
491+
org: {
492+
id: mockOrg
493+
},
494+
workspace: {
495+
id: mockWorkspaceId,
496+
name: mockWorkspaceName
497+
}
498+
}
499+
}
500+
})
501+
command.getAppExtConfigs.mockResolvedValueOnce(createAppConfig(command.appConfig))
502+
503+
await command.run()
504+
expect(command.error).toHaveBeenCalledTimes(0)
505+
expect(mockRuntimeLib.undeployActions).toHaveBeenCalledTimes(1)
506+
expect(mockWebLib.undeployWeb).toHaveBeenCalledTimes(1)
507+
expect(authHelper.setRuntimeApiHostAndAuthHandler).toHaveBeenCalledTimes(1)
508+
process.env.IS_DEPLOY_SERVICE_ENABLED = false
509+
})
510+
475511
test('Send audit logs for successful app undeploy', async () => {
476512
const mockToken = 'mocktoken'
477513
const mockEnv = 'stage'
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
const { bearerAuthHandler, setRuntimeApiHostAndAuthHandler } = require('../../../src/lib/auth-helper')
2+
const { getToken, context } = require('@adobe/aio-lib-ims')
3+
const { CLI } = require('@adobe/aio-lib-ims/src/context')
4+
const { getCliEnv } = require('@adobe/aio-lib-env')
5+
6+
jest.mock('@adobe/aio-lib-ims')
7+
jest.mock('@adobe/aio-lib-env')
8+
9+
describe('bearerAuthHandler', () => {
10+
beforeEach(() => {
11+
jest.clearAllMocks()
12+
})
13+
14+
test('getAuthHeader should return a Bearer token', async () => {
15+
const mockToken = 'mocked-token'
16+
getToken.mockResolvedValue(mockToken)
17+
getCliEnv.mockReturnValue('test-env')
18+
19+
const result = await bearerAuthHandler.getAuthHeader()
20+
21+
expect(context.setCli).toHaveBeenCalledWith({ 'cli.bare-output': true }, false)
22+
expect(getCliEnv).toHaveBeenCalled()
23+
expect(getToken).toHaveBeenCalledWith(CLI)
24+
expect(result).toBe(`Bearer ${mockToken}`)
25+
})
26+
})
27+
28+
describe('setRuntimeApiHostAndAuthHandler', () => {
29+
const defaultRuntimeUrl = 'https://adobeioruntime.net'
30+
beforeEach(() => {
31+
jest.clearAllMocks()
32+
process.env.IS_DEPLOY_SERVICE_ENABLED = 'true'
33+
})
34+
35+
test('should set runtime.apihost and runtime.auth_handler when config has runtime', () => {
36+
const config = { runtime: {} }
37+
const result = setRuntimeApiHostAndAuthHandler(config)
38+
39+
expect(result.runtime.apihost).toBe(process.env.AIO_RUNTIME_APIHOST ?? defaultRuntimeUrl)
40+
expect(result.runtime.auth_handler).toBe(bearerAuthHandler)
41+
})
42+
43+
test('should set ow.apihost and ow.auth_handler when config has ow', () => {
44+
const config = { ow: {} }
45+
const result = setRuntimeApiHostAndAuthHandler(config)
46+
47+
expect(result.ow.apihost).toBe(process.env.AIO_RUNTIME_APIHOST ?? defaultRuntimeUrl)
48+
expect(result.ow.auth_handler).toBe(bearerAuthHandler)
49+
})
50+
51+
test('should return config unchanged when config has neither runtime nor ow', () => {
52+
const config = { other: {} }
53+
const result = setRuntimeApiHostAndAuthHandler(config)
54+
55+
expect(result).toBe(config)
56+
})
57+
58+
test('should return null when config is null', () => {
59+
const result = setRuntimeApiHostAndAuthHandler(null)
60+
61+
expect(result).toBeNull()
62+
})
63+
64+
test('should set default runtime.apihost only config has runtime', () => {
65+
process.env.IS_DEPLOY_SERVICE_ENABLED = 'false'
66+
const config = { runtime: {} }
67+
const result = setRuntimeApiHostAndAuthHandler(config)
68+
69+
expect(result.runtime.apihost).toBe(defaultRuntimeUrl)
70+
expect(result.runtime.auth_handler).toBeUndefined()
71+
})
72+
73+
test('should set default ow.apihost only config has openwhisk', () => {
74+
process.env.IS_DEPLOY_SERVICE_ENABLED = 'false'
75+
const config = { ow: {} }
76+
const result = setRuntimeApiHostAndAuthHandler(config)
77+
78+
expect(result.ow.apihost).toBe(defaultRuntimeUrl)
79+
expect(result.ow.auth_handler).toBeUndefined()
80+
})
81+
})

0 commit comments

Comments
 (0)