Skip to content

Commit 35d8545

Browse files
authored
fix: support of activation non default podman machines (#138)
Signed-off-by: Denis Golovin <[email protected]>
1 parent 8a1dc61 commit 35d8545

5 files changed

+134
-109
lines changed

src/extension.spec.ts

+5-2
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ import {
2727
} from '@podman-desktop/api';
2828
import { authentication, commands } from '@podman-desktop/api';
2929
import * as podmanCli from './podman-cli';
30+
import * as subscription from './subscription';
3031
import { ExtensionTelemetryLogger } from './telemetry';
3132
import { OrganizationService } from '@redhat-developer/rhsm-client';
3233

@@ -175,7 +176,8 @@ suite('signin command telemetry reports', () => {
175176
};
176177
},
177178
);
178-
vi.spyOn(podmanCli, 'isPodmanMachineRunning').mockReturnValue(false);
179+
vi.spyOn(subscription, 'isRedHatRegistryConfigured').mockResolvedValue(true);
180+
vi.spyOn(podmanCli, 'getRunningPodmanMachineName').mockReturnValue(undefined);
179181
await extension.activate(createExtContext());
180182
expect(commandFunctionCopy!).toBeDefined();
181183
await commandFunctionCopy!();
@@ -238,7 +240,8 @@ suite('signin command telemetry reports', () => {
238240
};
239241
},
240242
);
241-
vi.spyOn(podmanCli, 'isPodmanMachineRunning').mockReturnValue(true);
243+
vi.spyOn(subscription, 'isRedHatRegistryConfigured').mockReturnValue(true);
244+
vi.spyOn(podmanCli, 'getRunningPodmanMachineName').mockReturnValue('machine1');
242245
vi.spyOn(OrganizationService.prototype, 'checkOrgScaCapability').mockResolvedValue({ body: {} });
243246
await extension.activate(createExtContext());
244247
expect(commandFunctionCopy!).toBeDefined();

src/extension.ts

+27-61
Original file line numberDiff line numberDiff line change
@@ -21,23 +21,25 @@ import { getAuthConfig } from './configuration';
2121
import { onDidChangeSessions, RedHatAuthenticationService } from './authentication-service';
2222
import { ServiceAccountV1, ContainerRegistryAuthorizerClient } from '@redhat-developer/rhcra-client';
2323
import path from 'node:path';
24-
import { homedir } from 'node:os';
25-
import { accessSync, constants, readFileSync } from 'node:fs';
24+
import { readFileSync } from 'node:fs';
2625
import {
2726
runRpmInstallSubscriptionManager,
2827
runSubscriptionManager,
2928
runSubscriptionManagerActivationStatus,
3029
runSubscriptionManagerRegister,
3130
runSubscriptionManagerUnregister,
3231
runCreateFactsFile,
33-
isPodmanMachineRunning,
3432
runStartPodmanMachine,
3533
runStopPodmanMachine,
34+
getRunningPodmanMachineName,
3635
} from './podman-cli';
3736
import { SubscriptionManagerClient } from '@redhat-developer/rhsm-client';
3837
import { isLinux } from './util';
3938
import { SSOStatusBarItem } from './status-bar-item';
4039
import { ExtensionTelemetryLogger as TelemetryLogger } from './telemetry';
40+
import { signIntoRedHatDeveloperAccount } from './subscription';
41+
import { isRedHatRegistryConfigured } from './subscription';
42+
import { REGISTRY_REDHAT_IO } from './subscription';
4143

4244
let authenticationServicePromise: Promise<RedHatAuthenticationService>;
4345
let currentSession: extensionApi.AuthenticationSession | undefined;
@@ -70,21 +72,6 @@ function parseJwt(token: string) {
7072
return JSON.parse(jsonPayload);
7173
}
7274

73-
async function signIntoRedHatDeveloperAccount(
74-
createIfNone = true,
75-
): Promise<extensionApi.AuthenticationSession | undefined> {
76-
return extensionApi.authentication.getSession(
77-
'redhat.authentication-provider',
78-
[
79-
'api.iam.registry_service_accounts', //scope that gives access to hydra service accounts API
80-
'api.console',
81-
], // scope that gives access to console.redhat.com APIs
82-
{ createIfNone }, // will request to login in browser if session does not exists
83-
);
84-
}
85-
86-
const REGISTRY_REDHAT_IO = 'registry.redhat.io';
87-
8875
async function createRegistry(
8976
username: string,
9077
secret: string,
@@ -109,29 +96,6 @@ function removeRegistry(serverUrl: string = REGISTRY_REDHAT_IO): void {
10996
});
11097
}
11198

112-
// TODO: add listRegistries to registry API to allow search by
113-
// registry URL
114-
function isRedHatRegistryConfigured(): boolean {
115-
const pathToAuthJson = path.join(homedir(), '.config', 'containers', 'auth.json');
116-
let configured = false;
117-
try {
118-
// TODO: handle all kind problems with file existence, accessibility and parsable content
119-
accessSync(pathToAuthJson, constants.R_OK);
120-
const authFileContent = readFileSync(pathToAuthJson, { encoding: 'utf8' });
121-
const authFileJson: {
122-
auths?: {
123-
[registryUrl: string]: {
124-
auth: string;
125-
};
126-
};
127-
} = JSON.parse(authFileContent);
128-
configured = authFileJson?.auths?.hasOwnProperty(REGISTRY_REDHAT_IO) || false;
129-
} catch (_notAccessibleError) {
130-
// if file is not there, ignore and return default value
131-
}
132-
return configured;
133-
}
134-
13599
async function createOrReuseRegistryServiceAccount(): Promise<void> {
136100
const currentSession = await signIntoRedHatDeveloperAccount();
137101
const accessTokenJson = parseJwt(currentSession!.accessToken);
@@ -162,7 +126,7 @@ async function createOrReuseRegistryServiceAccount(): Promise<void> {
162126
);
163127
}
164128

165-
async function createOrReuseActivationKey() {
129+
async function createOrReuseActivationKey(machineName: string) {
166130
const currentSession = await signIntoRedHatDeveloperAccount();
167131
const accessTokenJson = parseJwt(currentSession!.accessToken);
168132
const client = new SubscriptionManagerClient({
@@ -184,7 +148,7 @@ async function createOrReuseActivationKey() {
184148
});
185149
}
186150

187-
await runSubscriptionManagerRegister('podman-desktop', accessTokenJson.organization.id);
151+
await runSubscriptionManagerRegister(machineName, 'podman-desktop', accessTokenJson.organization.id);
188152
}
189153

190154
async function isSimpleContentAccessEnabled(): Promise<boolean> {
@@ -197,28 +161,31 @@ async function isSimpleContentAccessEnabled(): Promise<boolean> {
197161
return response.body && response.body.simpleContentAccess === 'enabled';
198162
}
199163

200-
async function isSubscriptionManagerInstalled(): Promise<boolean> {
201-
const exitCode = await runSubscriptionManager();
164+
async function isSubscriptionManagerInstalled(machineName: string): Promise<boolean> {
165+
const exitCode = await runSubscriptionManager(machineName);
202166
return exitCode === 0;
203167
}
204168

205-
async function installSubscriptionManger() {
169+
async function installSubscriptionManger(machineName: string) {
206170
try {
207-
return await runRpmInstallSubscriptionManager();
171+
return await runRpmInstallSubscriptionManager(machineName);
208172
} catch (err) {
209173
console.error(`Subscription manager installation failed. ${String(err)}`);
210174
TelemetryLogger.logError('subscriptionManagerInstallationError', { error: String(err) });
211175
throw err;
212176
}
213177
}
214178

215-
async function isPodmanVmSubscriptionActivated() {
216-
const exitCode = await runSubscriptionManagerActivationStatus();
179+
async function isPodmanVmSubscriptionActivated(machineName: string) {
180+
const exitCode = await runSubscriptionManagerActivationStatus(machineName);
217181
return exitCode === 0;
218182
}
219183

220184
async function removeSession(sessionId: string): Promise<void> {
221-
runSubscriptionManagerUnregister().catch(console.error); // ignore error in case vm subscription activation failed on login
185+
const machineName = getRunningPodmanMachineName();
186+
if (machineName) {
187+
runSubscriptionManagerUnregister(machineName).catch(console.error); // ignore error in case vm subscription activation failed on login
188+
}
222189
removeRegistry(); // never fails, even if registry does not exist
223190
const service = await getAuthenticationService();
224191
const session = await service.removeSession(sessionId);
@@ -276,7 +243,8 @@ async function configureRegistryAndActivateSubscription() {
276243
title: 'Activating Red Hat Subscription',
277244
},
278245
async progress => {
279-
if (!isPodmanMachineRunning()) {
246+
const podmanRunningMachineName = getRunningPodmanMachineName();
247+
if (!podmanRunningMachineName) {
280248
if (isLinux()) {
281249
await extensionApi.window.showInformationMessage(
282250
'Signing into a Red Hat account requires a running Podman machine, and is currently not supported on a Linux host. Please start a Podman machine and try again.',
@@ -301,18 +269,17 @@ async function configureRegistryAndActivateSubscription() {
301269
}
302270
throw new Error('SCA is not enabled and message closed');
303271
}
304-
305-
if (!(await isSubscriptionManagerInstalled())) {
306-
await installSubscriptionManger();
307-
await runStopPodmanMachine();
308-
await runStartPodmanMachine();
272+
if (!(await isSubscriptionManagerInstalled(podmanRunningMachineName))) {
273+
await installSubscriptionManger(podmanRunningMachineName);
274+
await runStopPodmanMachine(podmanRunningMachineName);
275+
await runStartPodmanMachine(podmanRunningMachineName);
309276
}
310-
if (!(await isPodmanVmSubscriptionActivated())) {
277+
if (!(await isPodmanVmSubscriptionActivated(podmanRunningMachineName))) {
311278
const facts = {
312279
supported_architectures: 'aarch64,x86_64',
313280
};
314-
await runCreateFactsFile(JSON.stringify(facts, undefined, 2));
315-
await createOrReuseActivationKey();
281+
await runCreateFactsFile(podmanRunningMachineName, JSON.stringify(facts, undefined, 2));
282+
await createOrReuseActivationKey(podmanRunningMachineName);
316283
}
317284
}
318285
},
@@ -326,9 +293,8 @@ async function configureRegistryAndActivateSubscription() {
326293
if (!telemetryData.successful && currentSession?.id) {
327294
removeSession(currentSession.id); // if at least one fail, remove session
328295
}
329-
330-
TelemetryLogger.logUsage('signin', telemetryData);
331296
}
297+
TelemetryLogger.logUsage('signin', telemetryData);
332298
}
333299

334300
export async function activate(context: extensionApi.ExtensionContext): Promise<void> {

src/podman-cli.spec.ts

+22-19
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ beforeEach(() => {
6767

6868
test('runSubscriptionManager returns 0 when it is installed', async () => {
6969
vi.mocked(podmanProcess.exec).mockResolvedValue(runResult);
70-
const result = await runSubscriptionManager();
70+
const result = await runSubscriptionManager('machine1');
7171
expect(result).toBe(0);
7272
});
7373

@@ -78,15 +78,15 @@ test('runSubscriptionManager returns 1 when it is not installed', async () => {
7878
stderr: 'stderr output',
7979
toString: () => 'error message',
8080
});
81-
const result = await runSubscriptionManager();
81+
const result = await runSubscriptionManager('machine1');
8282
expect(result).toBe(1);
8383
});
8484

8585
test('runRpmInstallSubscription manager returns 0 when successful', async () => {
8686
vi.mocked(podmanProcess.exec).mockResolvedValue(runResult);
87-
const result = await runRpmInstallSubscriptionManager();
87+
const result = await runRpmInstallSubscriptionManager('machine1');
8888
expect(result).toBe(runResult);
89-
expect(podmanProcess.exec).toBeCalledWith(getPodmanCli(), PODMAN_COMMANDS.RPM_INSTALL_SM());
89+
expect(podmanProcess.exec).toBeCalledWith(getPodmanCli(), PODMAN_COMMANDS.RPM_INSTALL_SM('machine1'));
9090
});
9191

9292
test('runRpmInstallSubscription manager returns none 0 error code when failed and send telemetry', async () => {
@@ -96,7 +96,7 @@ test('runRpmInstallSubscription manager returns none 0 error code when failed an
9696
});
9797
const consoleError = vi.spyOn(console, 'error');
9898
let error: Error | undefined;
99-
const result = await runRpmInstallSubscriptionManager().catch(err => {
99+
const result = await runRpmInstallSubscriptionManager('machine1').catch(err => {
100100
error = err;
101101
});
102102
expect(String(error)).toBe(String(runError));
@@ -111,7 +111,7 @@ test('runRpmInstallSubscription manager returns none 0 error code when failed an
111111

112112
test('runSubscriptionManagerActivationStatus returns 0 when it has subscription activated', async () => {
113113
vi.mocked(podmanProcess.exec).mockResolvedValue(runResult);
114-
const result = await runSubscriptionManagerActivationStatus();
114+
const result = await runSubscriptionManagerActivationStatus('machine1');
115115
expect(result).toBe(0);
116116
});
117117

@@ -122,17 +122,17 @@ test('runSubscriptionManagerActivationStatus returns 1 when it has no active sub
122122
stderr: 'stderr output',
123123
toString: () => 'error message',
124124
});
125-
const result = await runSubscriptionManagerActivationStatus();
125+
const result = await runSubscriptionManagerActivationStatus('machine1');
126126
expect(result).toBe(1);
127127
});
128128

129129
test('runSubscriptionManagerRegister returns 0 when successful', async () => {
130130
vi.mocked(podmanProcess.exec).mockResolvedValue(runResult);
131-
const result = await runSubscriptionManagerRegister('activation-key-name', 'orgId');
131+
const result = await runSubscriptionManagerRegister('machine1', 'activation-key-name', 'orgId');
132132
expect(result).toBe(runResult);
133133
expect(podmanProcess.exec).toBeCalledWith(
134134
getPodmanCli(),
135-
PODMAN_COMMANDS.SM_ACTIVATE_SUBS('activation-key-name', 'orgId'),
135+
PODMAN_COMMANDS.SM_ACTIVATE_SUBS('machine1', 'activation-key-name', 'orgId'),
136136
);
137137
});
138138

@@ -143,7 +143,7 @@ test('runSubscriptionManagerRegister manager returns none 0 error code when fail
143143
});
144144
const consoleError = vi.spyOn(console, 'error');
145145
let error: Error | undefined;
146-
const result = await runSubscriptionManagerRegister('activation-key-name', 'orgId').catch(err => {
146+
const result = await runSubscriptionManagerRegister('machine1', 'activation-key-name', 'orgId').catch(err => {
147147
error = err;
148148
});
149149
expect(String(error)).toBe(String(runError));
@@ -158,9 +158,12 @@ test('runSubscriptionManagerRegister manager returns none 0 error code when fail
158158

159159
test('runCreateFactsFile returns 0 when successful', async () => {
160160
vi.mocked(podmanProcess.exec).mockResolvedValue(runResult);
161-
const result = await runCreateFactsFile('{"field":"value"}');
161+
const result = await runCreateFactsFile('machine1', '{"field":"value"}');
162162
expect(result).toBe(runResult);
163-
expect(podmanProcess.exec).toBeCalledWith(getPodmanCli(), PODMAN_COMMANDS.CREATE_FACTS_FILE('{"field":"value"}'));
163+
expect(podmanProcess.exec).toBeCalledWith(
164+
getPodmanCli(),
165+
PODMAN_COMMANDS.CREATE_FACTS_FILE('machine1', '{"field":"value"}'),
166+
);
164167
});
165168

166169
test('runCreateFactsFile manager returns none 0 error code when failed and send telemetry', async () => {
@@ -170,7 +173,7 @@ test('runCreateFactsFile manager returns none 0 error code when failed and send
170173
});
171174
const consoleError = vi.spyOn(console, 'error');
172175
let error: Error | undefined;
173-
const result = await runCreateFactsFile('{"field":"value"}').catch(err => {
176+
const result = await runCreateFactsFile('machine1', '{"field":"value"}').catch(err => {
174177
error = err;
175178
});
176179
expect(String(error)).toBe(String(runError));
@@ -185,9 +188,9 @@ test('runCreateFactsFile manager returns none 0 error code when failed and send
185188

186189
test('runStopPodmanMachine returns 0 when successful', async () => {
187190
vi.mocked(podmanProcess.exec).mockResolvedValue(runResult);
188-
const result = await runStopPodmanMachine();
191+
const result = await runStopPodmanMachine('machine1');
189192
expect(result).toBe(runResult);
190-
expect(podmanProcess.exec).toBeCalledWith(getPodmanCli(), PODMAN_COMMANDS.MACHINE_STOP());
193+
expect(podmanProcess.exec).toBeCalledWith(getPodmanCli(), PODMAN_COMMANDS.MACHINE_STOP('machine1'));
191194
});
192195

193196
test('runStopPodmanMachine manager returns none 0 error code when failed and send telemetry', async () => {
@@ -197,7 +200,7 @@ test('runStopPodmanMachine manager returns none 0 error code when failed and sen
197200
});
198201
const consoleError = vi.spyOn(console, 'error');
199202
let error: Error | undefined;
200-
const result = await runStopPodmanMachine().catch(err => {
203+
const result = await runStopPodmanMachine('machine1').catch(err => {
201204
error = err;
202205
});
203206
expect(String(error)).toBe(String(runError));
@@ -212,9 +215,9 @@ test('runStopPodmanMachine manager returns none 0 error code when failed and sen
212215

213216
test('runStartPodmanMachine returns 0 when successful', async () => {
214217
vi.mocked(podmanProcess.exec).mockResolvedValue(runResult);
215-
const result = await runStartPodmanMachine();
218+
const result = await runStartPodmanMachine('machine1');
216219
expect(result).toBe(runResult);
217-
expect(podmanProcess.exec).toBeCalledWith(getPodmanCli(), PODMAN_COMMANDS.MACHINE_START());
220+
expect(podmanProcess.exec).toBeCalledWith(getPodmanCli(), PODMAN_COMMANDS.MACHINE_START('machine1'));
218221
});
219222

220223
test('runStartPodmanMachine manager returns none 0 error code when failed and send telemetry', async () => {
@@ -224,7 +227,7 @@ test('runStartPodmanMachine manager returns none 0 error code when failed and se
224227
});
225228
const consoleError = vi.spyOn(console, 'error');
226229
let error: Error | undefined;
227-
const result = await runStartPodmanMachine().catch(err => {
230+
const result = await runStartPodmanMachine('machine1').catch(err => {
228231
error = err;
229232
});
230233
expect(String(error)).toBe(String(runError));

0 commit comments

Comments
 (0)