Skip to content

Commit 9ccec21

Browse files
authored
fix(e2e): update rpc-go v3 CLI flags (#3407)
- Update rpc.exe/docker invocations to rpc-go v3 Kong CLI syntax (--json, --skip-amt-cert-check, --password, -configv2 -> --profile), fixing the JSON.parse failures caused by the old single-dash flags. - Extract getAmtInfo/getAmtVersion and buildInfoCommand/ buildActivateCommand/buildDeactivateCommand into activation.spec.ts so cloud and console specs share one implementation instead of duplicating command-string building and amtinfo parsing. - Replace the per-file updateCommandsForVersion closures with a single root-level before() hook that builds the activate command once per spec file using the detected AMT version.
1 parent 50a7656 commit 9ccec21

3 files changed

Lines changed: 207 additions & 105 deletions

File tree

cypress/e2e/integration/rpc/activation.spec.ts

Lines changed: 117 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -74,10 +74,125 @@ export const execWithRetry = (
7474
return attemptExec(1)
7575
}
7676

77+
// Runs `rpc amtinfo` and parses the JSON result. Tolerates leading log noise
78+
// (e.g. logrus-formatted warnings) by locating the first '{' in the output.
79+
export const getAmtInfo = (
80+
infoCommand: string,
81+
config: Cypress.ExecOptions = execConfig
82+
): Cypress.Chainable<AMTInfo> => {
83+
return cy.exec(infoCommand, config).then((result) => {
84+
const { stdout, stderr, combined } = buildOutput(result)
85+
return cy.log(combined).then(() => {
86+
const source = stdout.length > 0 ? stdout : stderr
87+
const jsonStart = source.indexOf('{')
88+
if (jsonStart < 0) {
89+
throw new Error(`rpc amtinfo did not return JSON. Output:\n${combined}`)
90+
}
91+
const jsonOutput = source.substring(jsonStart)
92+
return JSON.parse(jsonOutput) as AMTInfo
93+
})
94+
})
95+
}
96+
97+
// Extracts the major AMT version (e.g. "16.1.5" -> "16") from an AMTInfo object.
98+
export const getAmtVersion = (amtInfo: AMTInfo): string => {
99+
const versions: string[] = amtInfo.amt.split('.')
100+
return versions.length > 1 ? versions[0] : '0'
101+
}
102+
103+
// rpc-go has reported the not-yet-activated control mode under different
104+
// strings across versions/builds; treat either as "not activated".
105+
export const notActivatedControlModes: string[] = ['pre-provisioning state', 'not activated']
106+
77107
// ---- Computed environment flags -------------------------------------------
78108

79-
// Exported so sub-specs can import them directly when run standalone.
80-
const isCloud: boolean = Cypress.env('CLOUD') === 'true' || Cypress.env('CLOUD') === true
109+
// Exported so sub-specs (and the builders below) can use it to pick the
110+
// cloud vs. console command variant.
111+
export const isCloud: boolean = Cypress.env('CLOUD') === 'true' || Cypress.env('CLOUD') === true
112+
113+
// ---- Shared rpc-go command builders ---------------------------------------
114+
//
115+
// rpc-go v3 (Kong CLI) requires --long/-short flag syntax; bare single-dash
116+
// long flags (e.g. -json, -configv2) are no longer accepted. Commands are run
117+
// either via Docker (Linux/Mac) or directly via rpc.exe (Windows). Each builder
118+
// uses `isCloud` to decide between the cloud (RPS/wss) and console (local
119+
// profile) variant of the command.
120+
121+
export interface RpcCommandOptions {
122+
isWin: boolean
123+
rpcDockerImage: string
124+
volumeMount?: string
125+
}
126+
127+
const buildRpcCommand = (opts: RpcCommandOptions, winExe: string, args: string): string => {
128+
if (opts.isWin) {
129+
return `${winExe} ${args}`
130+
}
131+
const volumeFlag = opts.volumeMount ? ` -v ${opts.volumeMount}` : ''
132+
return `docker run --rm --network host --device=/dev/mei0${volumeFlag} ${opts.rpcDockerImage} ${args}`
133+
}
134+
135+
export const buildInfoCommand = (opts: RpcCommandOptions): string => buildRpcCommand(opts, 'rpc.exe', 'amtinfo --json')
136+
137+
export interface ActivateCommandOptions {
138+
isWin: boolean
139+
rpcDockerImage: string
140+
amtVersion: string
141+
// console-only
142+
profileYamlFile?: string
143+
encryptionKey?: string
144+
// cloud-only
145+
fqdn?: string
146+
profileName?: string
147+
}
148+
149+
export const buildActivateCommand = (opts: ActivateCommandOptions): string => {
150+
const cmd = `activate`
151+
const common_flag = `-v --json`
152+
if (isCloud) {
153+
const flagPart = parseInt(opts.amtVersion) <= 18 ? ' -tls-tunnel' : ''
154+
const args = `${cmd} -u wss://${opts.fqdn}/activate --profile ${opts.profileName} -n${flagPart} ${common_flag}`
155+
return buildRpcCommand({ isWin: opts.isWin, rpcDockerImage: opts.rpcDockerImage }, 'rpc.exe', args)
156+
}
157+
158+
const profileDir = opts.profileYamlFile
159+
? opts.profileYamlFile.substring(0, opts.profileYamlFile.lastIndexOf('/'))
160+
: ''
161+
const profileFileName = opts.profileYamlFile
162+
? opts.profileYamlFile.substring(opts.profileYamlFile.lastIndexOf('/') + 1)
163+
: ''
164+
const profilePath = opts.isWin ? opts.profileYamlFile : `/config/${profileFileName}`
165+
const flagPart = parseInt(opts.amtVersion) <= 18 ? '' : ' --skip-amt-cert-check'
166+
const args = `${cmd} --profile ${profilePath} --key ${opts.encryptionKey}${flagPart} ${common_flag}`
167+
return buildRpcCommand(
168+
{ isWin: opts.isWin, rpcDockerImage: opts.rpcDockerImage, volumeMount: `${profileDir}:/config` },
169+
'rpc.exe',
170+
args
171+
)
172+
}
173+
174+
export interface DeactivateCommandOptions {
175+
isWin: boolean
176+
rpcDockerImage: string
177+
password: string
178+
amtVersion: string
179+
// cloud-only
180+
fqdn?: string
181+
}
182+
183+
export const buildDeactivateCommand = (opts: DeactivateCommandOptions): string => {
184+
const cmd = `deactivate`
185+
const common_flag = `-v -f --json --password ${opts.password}`
186+
if (isCloud) {
187+
const flagPart = parseInt(opts.amtVersion) <= 18 ? ' -tls-tunnel' : ''
188+
const args = `${cmd} -u wss://${opts.fqdn}/activate -n${flagPart} ${common_flag}`
189+
return buildRpcCommand({ isWin: opts.isWin, rpcDockerImage: opts.rpcDockerImage }, 'rpc.exe', args)
190+
}
191+
192+
const flagPart = parseInt(opts.amtVersion) <= 18 ? '' : ' --skip-amt-cert-check'
193+
const args = `${cmd} --local${flagPart} ${common_flag}`
194+
return buildRpcCommand({ isWin: opts.isWin, rpcDockerImage: opts.rpcDockerImage }, 'rpc.exe', args)
195+
}
81196

82197
declare const require: (id: string) => unknown
83198

cypress/e2e/integration/rpc/cloud.activation.spec.ts

Lines changed: 44 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,18 @@
1111
// ISOLATE guard below acts as a safety net.
1212
// ---------------------------------------------------------------------------
1313

14-
import { AMTInfo, execConfig, buildOutput, execWithRetry } from './activation.spec'
14+
import {
15+
AMTInfo,
16+
execConfig,
17+
buildOutput,
18+
execWithRetry,
19+
buildInfoCommand,
20+
buildActivateCommand,
21+
buildDeactivateCommand,
22+
getAmtInfo,
23+
getAmtVersion,
24+
notActivatedControlModes
25+
} from './activation.spec'
1526

1627
if (Cypress.env('ISOLATE').charAt(0).toLowerCase() !== 'y') {
1728
{
@@ -25,72 +36,52 @@ if (Cypress.env('ISOLATE').charAt(0).toLowerCase() !== 'y') {
2536
const parts: string[] = profileName ? profileName.split('-') : []
2637
const isAdminControlModeProfile = parts.length > 0 && parts[0] === 'acmactivate'
2738
const isWin = Cypress.platform === 'win32'
28-
let majorVersion = ''
2939

30-
// Default: use Docker (Linux/Mac); Windows overrides below use rpc.exe directly
31-
let infoCommand = `docker run --rm --network host --device=/dev/mei0 ${rpcDockerImage} amtinfo --json`
40+
// Default: use Docker (Linux/Mac); Windows overrides handled internally by the builders.
41+
const infoCommand = buildInfoCommand({ isWin, rpcDockerImage })
3242
let activateCommand = ''
3343
let deactivateCommand = ''
34-
35-
const buildActivateCommand = (useFlag: boolean, flag = '') => {
36-
const flagPart = useFlag ? ` ${flag}` : ''
37-
if (isWin) {
38-
return `rpc.exe activate -u wss://${fqdn}/activate -v -n${flagPart} --profile ${profileName} --json`
39-
}
40-
return `docker run --rm --network host --device=/dev/mei0 ${rpcDockerImage} activate -u wss://${fqdn}/activate -v -n${flagPart} --profile ${profileName} --json`
41-
}
42-
43-
const buildDeactivateCommand = () => {
44-
if (isWin) {
45-
return `rpc.exe deactivate -u wss://${fqdn}/activate -v -n -f --json --password ${password}`
46-
}
47-
return `docker run --rm --network host --device=/dev/mei0 ${rpcDockerImage} deactivate -u wss://${fqdn}/activate -v -n -f --json --password ${password}`
48-
}
49-
50-
const updateCommandsForVersion = (version: string): void => {
51-
if (parseInt(version) <= 18) {
52-
cy.log(`AMT version ${version} detected (<= 18), using -tls-tunnel flag for activate`)
53-
activateCommand = buildActivateCommand(true, '-tls-tunnel')
54-
} else {
55-
cy.log(`AMT version ${version} detected (> 18), no extra flag needed`)
56-
activateCommand = buildActivateCommand(false)
57-
}
58-
deactivateCommand = buildDeactivateCommand()
59-
}
60-
61-
if (isWin) {
62-
infoCommand = 'rpc.exe amtinfo --json'
63-
}
64-
65-
// Set initial commands without flag (will be updated based on AMT version)
66-
activateCommand = buildActivateCommand(false)
67-
deactivateCommand = buildDeactivateCommand()
44+
let amtVersion = ''
45+
46+
before(() => {
47+
getAmtInfo(infoCommand).then((info) => {
48+
amtVersion = getAmtVersion(info)
49+
activateCommand = buildActivateCommand({
50+
isWin,
51+
rpcDockerImage,
52+
amtVersion,
53+
fqdn,
54+
profileName
55+
})
56+
deactivateCommand = buildDeactivateCommand({
57+
isWin,
58+
rpcDockerImage,
59+
password,
60+
amtVersion,
61+
fqdn
62+
})
63+
})
64+
})
6865

6966
describe('Device Activation - Cloud', () => {
7067
context('TC_ACTIVATION_DEVICE_ACTIVATE_AND_DEACTIVATE', () => {
7168
beforeEach(() => {
7269
cy.setup()
73-
cy.exec(infoCommand, execConfig).then((result) => {
74-
const { stdout, stderr, combined } = buildOutput(result)
75-
cy.log(combined)
76-
const source = stdout.length > 0 ? stdout : stderr
77-
amtInfo = JSON.parse(source)
78-
const versions: string[] = amtInfo.amt.split('.')
79-
majorVersion = versions.length > 1 ? versions[0] : '0'
80-
updateCommandsForVersion(majorVersion)
70+
getAmtInfo(infoCommand).then((info) => {
71+
amtInfo = info
8172
})
8273
cy.wait(1000)
8374
})
8475

8576
it('Should Activate Device', () => {
86-
expect(amtInfo.controlMode).to.contain('pre-provisioning state')
77+
expect(amtInfo.controlMode).to.be.oneOf(notActivatedControlModes)
8778

8879
execWithRetry(activateCommand, execConfig).then((result) => {
8980
const { stdout, stderr, combined } = buildOutput(result)
9081
cy.log(combined)
9182
const primaryOutput = stdout.length > 0 ? stdout : stderr
9283

93-
if (parseInt(majorVersion) < 12 && parseInt(amtInfo.buildNumber) < 3000) {
84+
if (parseInt(amtVersion) < 12 && parseInt(amtInfo.buildNumber) < 3000) {
9485
expect(combined).to.contain(
9586
'Only version 10.0.47 with build greater than 3000 can be remotely configured'
9687
)
@@ -106,7 +97,7 @@ if (Cypress.env('ISOLATE').charAt(0).toLowerCase() !== 'y') {
10697

10798
if (parts[2] === 'CIRA') {
10899
expect(combined).to.match(/CIRA: Configured/i)
109-
} else if (parseInt(majorVersion) >= 19) {
100+
} else if (parseInt(amtVersion) >= 19) {
110101
expect(combined).to.match(/TLS: Already Configured/i)
111102
} else {
112103
expect(combined).to.match(/TLS: Configured/i)
@@ -131,10 +122,7 @@ if (Cypress.env('ISOLATE').charAt(0).toLowerCase() !== 'y') {
131122
cy.wait(120000)
132123

133124
// Re-query amtinfo after activation to get the updated IP address
134-
cy.exec(infoCommand, execConfig).then((postActivationResult) => {
135-
const { stdout, stderr } = buildOutput(postActivationResult)
136-
const source = stdout.length > 0 ? stdout : stderr
137-
const postActivationInfo: AMTInfo = JSON.parse(source)
125+
getAmtInfo(infoCommand).then((postActivationInfo) => {
138126
cy.log(`Post-activation wired IP: ${postActivationInfo.wiredAdapter?.ipAddress}`)
139127
cy.log(`Post-activation wireless IP: ${postActivationInfo.wirelessAdapter?.ipAddress}`)
140128

@@ -154,7 +142,7 @@ if (Cypress.env('ISOLATE').charAt(0).toLowerCase() !== 'y') {
154142
cy.get('[data-cy="serialNumber"]').should('not.be.empty')
155143
cy.get('[data-cy="provisioningMode"]').should('not.be.empty')
156144

157-
if (parseInt(majorVersion) < 11) {
145+
if (parseInt(amtVersion) < 11) {
158146
cy.get('[data-cy="biosManufacturer"]').should('not.be.empty')
159147
cy.get('[data-cy="biosVersion"]').should('not.be.empty')
160148
cy.get('[data-cy="biosReleaseData"]').should('not.be.empty')
@@ -171,7 +159,7 @@ if (Cypress.env('ISOLATE').charAt(0).toLowerCase() !== 'y') {
171159
})
172160

173161
it('should NOT deactivate device - invalid password', () => {
174-
if (amtInfo.controlMode !== 'pre-provisioning state') {
162+
if (!notActivatedControlModes.includes(amtInfo.controlMode)) {
175163
const invalidCommand =
176164
deactivateCommand.slice(0, deactivateCommand.indexOf('--password')) + '--password invalidpassword'
177165
execWithRetry(invalidCommand, execConfig).then((result) => {
@@ -183,7 +171,7 @@ if (Cypress.env('ISOLATE').charAt(0).toLowerCase() !== 'y') {
183171
})
184172

185173
it('should deactivate device', () => {
186-
if (amtInfo.controlMode !== 'pre-provisioning state') {
174+
if (!notActivatedControlModes.includes(amtInfo.controlMode)) {
187175
execWithRetry(deactivateCommand, execConfig).then((result) => {
188176
const { combined } = buildOutput(result)
189177
cy.log(combined)
@@ -196,17 +184,6 @@ if (Cypress.env('ISOLATE').charAt(0).toLowerCase() !== 'y') {
196184
})
197185

198186
context('Negative Activation Test', () => {
199-
beforeEach(() => {
200-
cy.exec(infoCommand, execConfig).then((result) => {
201-
const { stdout, stderr } = buildOutput(result)
202-
const source = stdout.length > 0 ? stdout : stderr
203-
const info = JSON.parse(source)
204-
const versions: string[] = info.amt.split('.')
205-
const version = versions.length > 1 ? versions[0] : '0'
206-
updateCommandsForVersion(version)
207-
})
208-
})
209-
210187
if (isAdminControlModeProfile) {
211188
it('Should NOT activate ACM when domain suffix is not registered in RPS', () => {
212189
activateCommand += ' -d dontmatch.com'

0 commit comments

Comments
 (0)