@adobe/aio-cli-plugin-app 8.4.0 → 8.5.0-0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -72,12 +72,14 @@ class Undeploy extends BaseCommand {
72
72
  }
73
73
 
74
74
  async undeployOneExt (extName, config, flags, spinner) {
75
- const onProgress = !flags.verbose ? info => {
76
- spinner.text = info
77
- } : info => {
78
- spinner.info(chalk.dim(`${info}`))
79
- spinner.start()
80
- }
75
+ const onProgress = !flags.verbose
76
+ ? info => {
77
+ spinner.text = info
78
+ }
79
+ : info => {
80
+ spinner.info(chalk.dim(`${info}`))
81
+ spinner.start()
82
+ }
81
83
  // undeploy
82
84
  try {
83
85
  await runScript(config.hooks['pre-app-undeploy'])
@@ -17,7 +17,7 @@ const { deployActions } = require('@adobe/aio-lib-runtime')
17
17
  * Deploys actions.
18
18
  *
19
19
  * @param {object} config see src/lib/config-loader.js
20
- * @param {boolean} isLocal=false set to true if it's a local deploy
20
+ * @param {boolean} isLocal default false, set to true if it's a local deploy
21
21
  * @param {Function} [log] a log function
22
22
  * @param {boolean} filter true if a filter by built actions is desired.
23
23
  */
@@ -0,0 +1,259 @@
1
+ /*
2
+ Copyright 2020 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
+
7
+ Unless required by applicable law or agreed to in writing, software distributed under
8
+ the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
9
+ OF ANY KIND, either express or implied. See the License for the specific language
10
+ governing permissions and limitations under the License.
11
+ */
12
+
13
+ const rtLib = require('@adobe/aio-lib-runtime')
14
+ const { writeAio, writeEnv } = require('./import')
15
+ const crypto = require('crypto')
16
+ const fs = require('fs-extra')
17
+ const path = require('path')
18
+
19
+ const SECRET_FIELD_TYPE = 'password'
20
+ const CHECKSUM_DIR = 'dist'
21
+ const CHECKSUM_FILE = 'log-forwarding-config.sha256'
22
+ const IGNORED_REMOTE_SETTINGS = ['updated_at']
23
+
24
+ class LogForwarding {
25
+ constructor (aioConfig) {
26
+ this.aioConfig = aioConfig
27
+ }
28
+
29
+ async init () {
30
+ rtLib.utils.checkOpenWhiskCredentials({ ow: this.aioConfig.runtime })
31
+ this.logForwarding = await getRTLogForwarding(this.aioConfig.runtime)
32
+ return this
33
+ }
34
+
35
+ getLocalConfig () {
36
+ const config = this.aioConfig.project.workspace.log_forwarding
37
+ try {
38
+ return this.getConfigFromJson(config)
39
+ } catch (e) {
40
+ throw new Error('Incorrect local log forwarding configuration. ' + e.message)
41
+ }
42
+ }
43
+
44
+ getLocalConfigWithSecrets () {
45
+ const config = this.getLocalConfig()
46
+ const destination = config.getDestination()
47
+ const settings = config.getSettings()
48
+ if (config.isDefined()) {
49
+ const destinationSettings = this.logForwarding.getDestinationSettings(destination)
50
+ const missingSecrets = []
51
+ destinationSettings.forEach(e => {
52
+ if (e.type === SECRET_FIELD_TYPE) {
53
+ const secretVarName = getSecretVarName(destination, e.name)
54
+ if (process.env[secretVarName] !== undefined) {
55
+ settings[e.name] = process.env[secretVarName]
56
+ } else {
57
+ missingSecrets.push(secretVarName)
58
+ }
59
+ }
60
+ })
61
+ if (missingSecrets.length > 0) {
62
+ throw new Error('Required secrets are missing in environment variables: ' + missingSecrets.join(', ') + '. ' +
63
+ 'Make sure these variables are set in .env file')
64
+ }
65
+ }
66
+ return new LogForwardingConfig(destination, settings)
67
+ }
68
+
69
+ async getServerConfig () {
70
+ try {
71
+ return this.getConfigFromJson(await this.logForwarding.get())
72
+ } catch (e) {
73
+ throw new Error('Incorrect log forwarding configuration on server. ' + e.message)
74
+ }
75
+ }
76
+
77
+ /**
78
+ * Convert JSON config to Log Forwarding object
79
+ *
80
+ * @param {object} configJson Config in JSON format
81
+ * @returns {LogForwardingConfig} Config
82
+ */
83
+ getConfigFromJson (configJson) {
84
+ let destination
85
+ let settings
86
+
87
+ if (configJson !== undefined && configJson !== null && !Array.isArray(configJson) && typeof configJson === 'object') {
88
+ const destinations = Object.keys(configJson)
89
+ if (destinations.length === 1) {
90
+ destination = destinations[0]
91
+ settings = configJson[destination]
92
+ } else {
93
+ throw new Error(`Configuration has ${destinations.length} destinations. Exactly one must be defined.`)
94
+ }
95
+ }
96
+ return new LogForwardingConfig(destination, settings)
97
+ }
98
+
99
+ getSupportedDestinations () {
100
+ return this.logForwarding.getSupportedDestinations()
101
+ }
102
+
103
+ getSettingsConfig (destination) {
104
+ return this.logForwarding.getDestinationSettings(destination)
105
+ }
106
+
107
+ async updateLocalConfig (lfConfig) {
108
+ const destination = lfConfig.getDestination()
109
+ const destinationSettings = this.logForwarding.getDestinationSettings(destination)
110
+ const projectConfig = {
111
+ project: this.aioConfig.project
112
+ }
113
+
114
+ const nonSecretSettings = {}
115
+ const secretSettings = {}
116
+
117
+ const settings = lfConfig.getSettings()
118
+ Object.keys(settings)
119
+ .filter(e => !IGNORED_REMOTE_SETTINGS.includes(e))
120
+ .forEach(k => {
121
+ const destFieldSettings = destinationSettings.find(i => i.name === k)
122
+ if (destFieldSettings.type === SECRET_FIELD_TYPE) {
123
+ secretSettings[getSecretVarName(destination, k)] = settings[k]
124
+ } else {
125
+ nonSecretSettings[k] = settings[k]
126
+ }
127
+ })
128
+
129
+ projectConfig.project.workspace.log_forwarding = {
130
+ [destination]: nonSecretSettings
131
+ }
132
+ const interactive = false
133
+ const merge = true
134
+ await writeAio(projectConfig, '', { interactive, merge })
135
+ await writeEnv({}, '', { interactive, merge }, secretSettings)
136
+ }
137
+
138
+ isLocalConfigChanged () {
139
+ if (fs.pathExistsSync(path.join(CHECKSUM_DIR, CHECKSUM_FILE))) {
140
+ const oldChecksum = fs.readFileSync(path.join(CHECKSUM_DIR, CHECKSUM_FILE)).toString()
141
+ const config = this.getLocalConfigWithSecrets()
142
+ const newChecksum = getChecksum(config)
143
+ return oldChecksum !== newChecksum
144
+ } else {
145
+ return true
146
+ }
147
+ }
148
+
149
+ async updateServerConfig (lfConfig) {
150
+ const res = await this.logForwarding.setDestination(lfConfig.getDestination(), lfConfig.getSettings())
151
+ const checksum = getChecksum(lfConfig)
152
+ fs.ensureDirSync(CHECKSUM_DIR)
153
+ fs.writeFile(path.join(CHECKSUM_DIR, CHECKSUM_FILE), checksum, { flags: 'w' })
154
+ return res
155
+ }
156
+ }
157
+
158
+ class LogForwardingConfig {
159
+ constructor (destination, settings) {
160
+ this.destination = destination
161
+ this.settings = settings
162
+ }
163
+
164
+ getDestination () {
165
+ return this.destination
166
+ }
167
+
168
+ getSettings () {
169
+ return this.settings
170
+ }
171
+
172
+ isDefined () {
173
+ return this.destination !== undefined
174
+ }
175
+
176
+ isDefault () {
177
+ return !this.isDefined() || this.getDestination() === 'adobe_io_runtime'
178
+ }
179
+
180
+ isEqual (config) {
181
+ return (this.isDefault() && config.isDefault()) ||
182
+ (this.destination === config.getDestination() && shallowEqual(this.settings, config.settings))
183
+ }
184
+ }
185
+
186
+ /**
187
+ * Init Log Forwarding
188
+ *
189
+ * @param {object} aioConfig aio Config
190
+ * @returns {Promise<LogForwarding>} Log Forwarding
191
+ */
192
+ async function init (aioConfig) {
193
+ const lf = new LogForwarding(aioConfig)
194
+ return await lf.init()
195
+ }
196
+
197
+ /**
198
+ * Get Runtime Log Forwarding
199
+ *
200
+ * @param {object} rtConfig Runtime config
201
+ * @returns {Promise<LogForwarding>} Log Forwarding
202
+ */
203
+ async function getRTLogForwarding (rtConfig) {
204
+ const rt = await rtLib.init({
205
+ ...rtConfig,
206
+ api_key: rtConfig.auth
207
+ })
208
+ return rt.logForwarding
209
+ }
210
+
211
+ /**
212
+ * Compare to log forwarding configs
213
+ *
214
+ * @param {LogForwardingConfig} config1 Config
215
+ * @param {LogForwardingConfig} config2 Config
216
+ * @returns {boolean} Are configs equal
217
+ */
218
+ function shallowEqual (config1, config2) {
219
+ // updated_at exists on server only and does not impact actual configuration
220
+ const keys1 = Object.keys(config1).filter(e => !IGNORED_REMOTE_SETTINGS.includes(e))
221
+ const keys2 = Object.keys(config2).filter(e => !IGNORED_REMOTE_SETTINGS.includes(e))
222
+ if (keys1.length !== keys2.length) {
223
+ return false
224
+ }
225
+ for (const key of keys1) {
226
+ if (config1[key] !== config2[key]) {
227
+ return false
228
+ }
229
+ }
230
+ return true
231
+ }
232
+
233
+ /**
234
+ * Get secret variable name for the given destination and settings field
235
+ *
236
+ * @param {string} destination Destination
237
+ * @param {string} fieldName Field name
238
+ * @returns {string} Variable name
239
+ */
240
+ function getSecretVarName (destination, fieldName) {
241
+ return destination.toUpperCase() + '__' + fieldName.toUpperCase()
242
+ }
243
+
244
+ /**
245
+ * Generate checksum for the config
246
+ *
247
+ * @param {LogForwardingConfig} config Config
248
+ * @returns {string} Checksum
249
+ */
250
+ function getChecksum (config) {
251
+ return crypto.createHash('sha256')
252
+ .update(JSON.stringify(config))
253
+ .digest('hex')
254
+ }
255
+
256
+ module.exports = {
257
+ init,
258
+ LogForwardingConfig
259
+ }