Skip to content

Commit 3a6a875

Browse files
committed
feat: inject SCC attribute when creating DevWorkspaceTemplate
Add SCC attribute to DevWorkspaceTemplate during workspace creation when container run or build capabilities are enabled. This ensures new workspaces get the correct SCC configuration. Signed-off-by: Oleksii Orel <oorel@redhat.com>
1 parent a27b3f8 commit 3a6a875

File tree

3 files changed

+215
-28
lines changed

3 files changed

+215
-28
lines changed

packages/dashboard-frontend/src/services/workspace-client/devworkspace/__tests__/devWorkspaceClient.create.spec.ts

Lines changed: 122 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -103,13 +103,20 @@ describe('DevWorkspace client, create', () => {
103103
const internalPluginRegistryUrl = 'http://internal.plugin.registry.url';
104104
const openVSXUrl = 'http://openvsx.url';
105105

106+
const config = {
107+
pluginRegistryURL: pluginRegistryUrl,
108+
pluginRegistryInternalURL: internalPluginRegistryUrl,
109+
pluginRegistry: {
110+
disableInternalRegistry: false,
111+
openVSXURL: openVSXUrl,
112+
},
113+
} as any;
114+
106115
await client.createDevWorkspaceTemplate(
107116
namespace,
108117
testDevWorkspace,
109118
testDevWorkspaceTemplate,
110-
pluginRegistryUrl,
111-
internalPluginRegistryUrl,
112-
openVSXUrl,
119+
config,
113120
);
114121

115122
expect(spyCreateWorkspaceTemplate).toHaveBeenCalledWith(
@@ -148,13 +155,17 @@ describe('DevWorkspace client, create', () => {
148155
title: clusterConsoleTitle,
149156
};
150157

158+
const config = {
159+
pluginRegistry: {
160+
disableInternalRegistry: true,
161+
},
162+
} as any;
163+
151164
await client.createDevWorkspaceTemplate(
152165
namespace,
153166
testDevWorkspace,
154167
testDevWorkspaceTemplate,
155-
undefined,
156-
undefined,
157-
undefined,
168+
config,
158169
clusterConsole,
159170
);
160171

@@ -183,17 +194,20 @@ describe('DevWorkspace client, create', () => {
183194
});
184195

185196
it('should add owner reference to devWorkspace template to allow automatic cleanup', async () => {
186-
const pluginRegistryUrl = 'http://plugin.registry.url';
187-
const internalPluginRegistryUrl = 'http://internal.plugin.registry.url';
188-
const openVSXUrl = 'http://openvsx.url';
197+
const config = {
198+
pluginRegistryURL: 'http://plugin.registry.url',
199+
pluginRegistryInternalURL: 'http://internal.plugin.registry.url',
200+
pluginRegistry: {
201+
disableInternalRegistry: false,
202+
openVSXURL: 'http://openvsx.url',
203+
},
204+
} as any;
189205

190206
await client.createDevWorkspaceTemplate(
191207
namespace,
192208
testDevWorkspace,
193209
testDevWorkspaceTemplate,
194-
pluginRegistryUrl,
195-
internalPluginRegistryUrl,
196-
openVSXUrl,
210+
config,
197211
);
198212

199213
expect(spyCreateWorkspaceTemplate).toHaveBeenCalledWith(
@@ -210,6 +224,102 @@ describe('DevWorkspace client, create', () => {
210224
);
211225
});
212226

227+
it('should add SCC attribute when containerRun capabilities are enabled', async () => {
228+
const config = {
229+
pluginRegistry: {
230+
disableInternalRegistry: true,
231+
},
232+
containerRun: {
233+
disableContainerRunCapabilities: false,
234+
containerRunConfiguration: {
235+
openShiftSecurityContextConstraint: 'container-run',
236+
},
237+
},
238+
} as any;
239+
240+
await client.createDevWorkspaceTemplate(
241+
namespace,
242+
testDevWorkspace,
243+
testDevWorkspaceTemplate,
244+
config,
245+
);
246+
247+
expect(spyCreateWorkspaceTemplate).toHaveBeenCalledWith(
248+
expect.objectContaining({
249+
spec: expect.objectContaining({
250+
attributes: expect.objectContaining({
251+
'controller.devfile.io/scc': 'container-run',
252+
}),
253+
}),
254+
}),
255+
);
256+
});
257+
258+
it('should add SCC attribute from containerBuild when containerRun is disabled', async () => {
259+
const config = {
260+
pluginRegistry: {
261+
disableInternalRegistry: true,
262+
},
263+
containerRun: {
264+
disableContainerRunCapabilities: true,
265+
},
266+
containerBuild: {
267+
disableContainerBuildCapabilities: false,
268+
containerBuildConfiguration: {
269+
openShiftSecurityContextConstraint: 'container-build',
270+
},
271+
},
272+
} as any;
273+
274+
await client.createDevWorkspaceTemplate(
275+
namespace,
276+
testDevWorkspace,
277+
testDevWorkspaceTemplate,
278+
config,
279+
);
280+
281+
expect(spyCreateWorkspaceTemplate).toHaveBeenCalledWith(
282+
expect.objectContaining({
283+
spec: expect.objectContaining({
284+
attributes: expect.objectContaining({
285+
'controller.devfile.io/scc': 'container-build',
286+
}),
287+
}),
288+
}),
289+
);
290+
});
291+
292+
it('should not add SCC attribute when both container capabilities are disabled', async () => {
293+
const config = {
294+
pluginRegistry: {
295+
disableInternalRegistry: true,
296+
},
297+
containerRun: {
298+
disableContainerRunCapabilities: true,
299+
},
300+
containerBuild: {
301+
disableContainerBuildCapabilities: true,
302+
},
303+
} as any;
304+
305+
await client.createDevWorkspaceTemplate(
306+
namespace,
307+
testDevWorkspace,
308+
testDevWorkspaceTemplate,
309+
config,
310+
);
311+
312+
expect(spyCreateWorkspaceTemplate).toHaveBeenCalledWith(
313+
expect.objectContaining({
314+
spec: expect.not.objectContaining({
315+
attributes: expect.objectContaining({
316+
'controller.devfile.io/scc': expect.anything(),
317+
}),
318+
}),
319+
}),
320+
);
321+
});
322+
213323
it('should add routingClass if it does not exist', async () => {
214324
const routingClass = 'che';
215325
const response = {

packages/dashboard-frontend/src/services/workspace-client/devworkspace/devWorkspaceClient.ts

Lines changed: 88 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -188,9 +188,7 @@ export class DevWorkspaceClient {
188188
defaultNamespace: string,
189189
devWorkspace: devfileApi.DevWorkspace,
190190
devWorkspaceTemplateResource: devfileApi.DevWorkspaceTemplate,
191-
pluginRegistryUrl: string | undefined,
192-
pluginRegistryInternalUrl: string | undefined,
193-
openVSXUrl: string | undefined,
191+
config: api.IServerConfig,
194192
clusterConsole?: {
195193
url: string;
196194
title: string;
@@ -208,6 +206,13 @@ export class DevWorkspaceClient {
208206
},
209207
];
210208

209+
// Extract URLs from config
210+
const pluginRegistryUrl = !config.pluginRegistry?.disableInternalRegistry
211+
? config.pluginRegistryURL
212+
: config.pluginRegistry?.externalPluginRegistries?.[0]?.url;
213+
const pluginRegistryInternalUrl = config.pluginRegistryInternalURL;
214+
const openVSXUrl = config.pluginRegistry?.openVSXURL;
215+
211216
this.addEnvVarsToContainers(
212217
devWorkspaceTemplateResource.spec?.components,
213218
pluginRegistryUrl,
@@ -216,9 +221,89 @@ export class DevWorkspaceClient {
216221
clusterConsole,
217222
);
218223

224+
// Add SCC attribute if container capabilities are enabled
225+
this.addSccAttributeToTemplate(devWorkspaceTemplateResource, config);
226+
219227
return DwtApi.createTemplate(devWorkspaceTemplateResource);
220228
}
221229

230+
/**
231+
* Injects the container scc attribute into the DevWorkspaceTemplateResource
232+
* and adds HOST_USERS environment variable to all containers
233+
* based on the CR `disableContainerBuildCapabilities` or `disableContainerRunCapabilities` fields value.
234+
*/
235+
private addSccAttributeToTemplate(
236+
devWorkspaceTemplateResource: devfileApi.DevWorkspaceTemplate,
237+
config: api.IServerConfig,
238+
): void {
239+
let sccName: string | undefined;
240+
let hostUsers = true;
241+
242+
if (!config.containerRun?.disableContainerRunCapabilities) {
243+
// container run capabilities is enabled.
244+
sccName = config.containerRun?.containerRunConfiguration?.openShiftSecurityContextConstraint;
245+
hostUsers = false;
246+
} else if (!config.containerBuild?.disableContainerBuildCapabilities) {
247+
// container build capabilities is enabled.
248+
sccName =
249+
config.containerBuild?.containerBuildConfiguration?.openShiftSecurityContextConstraint;
250+
hostUsers = false;
251+
}
252+
253+
if (!sccName) {
254+
return;
255+
}
256+
257+
if (!devWorkspaceTemplateResource.spec) {
258+
devWorkspaceTemplateResource.spec = {};
259+
}
260+
if (!devWorkspaceTemplateResource.spec.attributes) {
261+
devWorkspaceTemplateResource.spec.attributes = {};
262+
}
263+
devWorkspaceTemplateResource.spec.attributes[DEVWORKSPACE_CONTAINER_SCC_ATTR] = sccName;
264+
265+
// Add HOST_USERS environment variable to all containers
266+
this.addHostUsersEnvVarToTemplate(devWorkspaceTemplateResource, hostUsers);
267+
}
268+
269+
/**
270+
* Adds HOST_USERS environment variable to all containers in the DevWorkspaceTemplate.
271+
*/
272+
private addHostUsersEnvVarToTemplate(
273+
devWorkspaceTemplateResource: devfileApi.DevWorkspaceTemplate,
274+
hostUsers: boolean,
275+
): void {
276+
const components = devWorkspaceTemplateResource.spec?.components;
277+
if (!components) {
278+
return;
279+
}
280+
281+
const envVar = { name: this.hostUsersEnvName, value: hostUsers.toString() };
282+
283+
for (const component of components) {
284+
const container = component.container;
285+
if (container === undefined) {
286+
continue;
287+
}
288+
289+
if (!container.env) {
290+
container.env = [envVar];
291+
continue;
292+
}
293+
294+
const existingEnvIndex = container.env.findIndex(env => env.name === this.hostUsersEnvName);
295+
if (existingEnvIndex >= 0) {
296+
// If value is different, replace it
297+
if (container.env[existingEnvIndex].value !== hostUsers.toString()) {
298+
container.env[existingEnvIndex] = envVar;
299+
}
300+
} else {
301+
// If environment variable is absent, add it
302+
container.env.push(envVar);
303+
}
304+
}
305+
}
306+
222307
/**
223308
* propagate the plugin registry, plugin internal registry,
224309
* and dashboard URLs into the components containers

packages/dashboard-frontend/src/store/Workspaces/devWorkspaces/actions/actionCreators/createWorkspaceFromResources.ts

Lines changed: 5 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,8 @@ import { AppThunk } from '@/store';
1818
import { selectApplications } from '@/store/ClusterInfo';
1919
import { selectDefaultNamespace } from '@/store/InfrastructureNamespaces';
2020
import { verifyAuthorized } from '@/store/SanityCheck';
21-
import {
22-
selectDefaultEditor,
23-
selectOpenVSXUrl,
24-
selectPluginRegistryInternalUrl,
25-
selectPluginRegistryUrl,
26-
} from '@/store/ServerConfig';
21+
import { getDefaultEditor } from '@/store/ServerConfig/helpers';
22+
import { selectServerConfigState } from '@/store/ServerConfig/selectors';
2723
import { getDevWorkspaceClient } from '@/store/Workspaces/devWorkspaces/actions/actionCreators/helpers';
2824
import { updateDevWorkspaceTemplate } from '@/store/Workspaces/devWorkspaces/actions/actionCreators/helpers/editorImage';
2925
import {
@@ -43,10 +39,8 @@ export const createWorkspaceFromResources =
4339
async (dispatch, getState) => {
4440
const state = getState();
4541
const defaultKubernetesNamespace = selectDefaultNamespace(state);
46-
const openVSXUrl = selectOpenVSXUrl(state);
47-
const pluginRegistryUrl = selectPluginRegistryUrl(state);
48-
const pluginRegistryInternalUrl = selectPluginRegistryInternalUrl(state);
49-
const cheEditor = editor ? editor : selectDefaultEditor(state);
42+
const serverConfig = selectServerConfigState(state).config;
43+
const cheEditor = editor || getDefaultEditor(serverConfig);
5044
const defaultNamespace = defaultKubernetesNamespace.name;
5145
const customName = params.name;
5246

@@ -84,9 +78,7 @@ export const createWorkspaceFromResources =
8478
defaultNamespace,
8579
createResp.devWorkspace,
8680
devWorkspaceTemplate,
87-
pluginRegistryUrl,
88-
pluginRegistryInternalUrl,
89-
openVSXUrl,
81+
serverConfig,
9082
clusterConsole,
9183
);
9284

0 commit comments

Comments
 (0)