Skip to content

Commit 77982c3

Browse files
authored
feat: allow data connectors to be skipped when launching sessions (#3873)
The data-service API is changed to allow not mounting data connectors at session launch. With this change, the "Skip" action now means "do not mount this data connector", which is what Renku should do in most cases (trying to mount without credentials or with invalid credentials leads to session nodes being banned e.g. from SFTP hosts).
1 parent f93d71e commit 77982c3

11 files changed

+244
-186
lines changed

client/src/features/dataConnectorsV2/components/useDataConnectorConfiguration.hook.ts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -85,14 +85,17 @@ export default function useDataConnectorConfiguration({
8585
const savedCredentialFields = dataConnectorSecrets
8686
? dataConnectorSecrets[dataConnector.id]?.map((s) => s.name)
8787
: [];
88-
return {
88+
const result: SessionStartDataConnectorConfiguration = {
8989
active: true,
9090
dataConnector,
91-
sensitiveFieldDefinitions,
92-
sensitiveFieldValues,
9391
saveCredentials: false,
9492
savedCredentialFields,
93+
sensitiveFieldDefinitions,
94+
sensitiveFieldValues,
95+
skip: false,
96+
touched: false,
9597
};
98+
return result;
9699
}),
97100
[dataConnectors, dataConnectorSecrets]
98101
);

client/src/features/project/utils/projectCloudStorage.utils.test.ts

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,16 @@
11
import type { SessionStartDataConnectorConfiguration } from "../../sessionsV2/startSessionOptionsV2.types";
22
import type { CloudStorageSchema } from "../components/cloudStorage/projectCloudStorage.types";
33
import {
4+
dataConnectorsOverrideFromConfig,
45
getSchemaOptions,
5-
storageDefinitionFromConfig,
66
} from "./projectCloudStorage.utils";
77

88
describe("storageDefinitionFromConfig", () => {
99
it("should return the correct storage definition", () => {
1010
const config: SessionStartDataConnectorConfiguration = {
1111
active: true,
12+
skip: false,
13+
touched: true,
1214
dataConnector: {
1315
id: "ULID-1",
1416
etag: "foo",
@@ -79,21 +81,19 @@ describe("storageDefinitionFromConfig", () => {
7981
saveCredentials: false,
8082
savedCredentialFields: [],
8183
};
82-
const result = storageDefinitionFromConfig(config);
83-
expect(result).toEqual({
84-
configuration: {
85-
type: "s3",
86-
provider: "AWS",
87-
access_key_id: "access key",
88-
secret_access_key: "secret key",
84+
const result = dataConnectorsOverrideFromConfig(config);
85+
expect(result).toEqual([
86+
{
87+
configuration: {
88+
type: "s3",
89+
provider: "AWS",
90+
access_key_id: "access key",
91+
secret_access_key: "secret key",
92+
},
93+
data_connector_id: "ULID-1",
94+
skip: false,
8995
},
90-
name: "example-storage",
91-
readonly: true,
92-
source_path: "bucket/my-source",
93-
storage_id: "ULID-1",
94-
storage_type: "s3",
95-
target_path: "external_storage/aws",
96-
});
96+
]);
9797
});
9898

9999
it("should return the correct schema options", () => {

client/src/features/project/utils/projectCloudStorage.utils.ts

Lines changed: 26 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,13 @@
1616
* limitations under the License.
1717
*/
1818

19+
import { type SessionDataConnectorOverride } from "~/features/sessionsV2/api/sessionsV2.api";
1920
import type {
2021
RCloneConfig,
2122
RCloneOption,
2223
} from "../../dataConnectorsV2/api/data-connectors.api";
2324
import { hasSchemaAccessMode } from "../../dataConnectorsV2/components/dataConnector.utils";
24-
import type { SessionCloudStorageV2 } from "../../sessionsV2/sessionsV2.types";
25+
import type { SessionStartDataConnectorConfiguration } from "../../sessionsV2/startSessionOptionsV2.types";
2526
import type { CloudStorageGet } from "../components/cloudStorage/api/projectCloudStorage.api";
2627
import {
2728
CLOUD_OPTIONS_OVERRIDE,
@@ -35,17 +36,15 @@ import {
3536
STORAGES_WITH_ACCESS_MODE,
3637
} from "../components/cloudStorage/projectCloudStorage.constants";
3738
import type {
39+
CloudStorage,
3840
CloudStorageCredential,
3941
CloudStorageDetails,
4042
CloudStorageOptionTypes,
41-
CloudStorageSchemaOption,
4243
CloudStorageProvider,
4344
CloudStorageSchema,
44-
CloudStorage,
45+
CloudStorageSchemaOption,
4546
} from "../components/cloudStorage/projectCloudStorage.types";
4647

47-
import { SessionStartDataConnectorConfiguration } from "../../sessionsV2/startSessionOptionsV2.types";
48-
4948
const LAST_POSITION = 1000;
5049

5150
export interface CloudStorageOptions extends RCloneOption {
@@ -324,8 +323,12 @@ export function findSensitive(
324323

325324
export function storageDefinitionAfterSavingCredentialsFromConfig(
326325
cs: SessionStartDataConnectorConfiguration
327-
) {
328-
const newCs = { ...cs, saveCredentials: false };
326+
): SessionStartDataConnectorConfiguration {
327+
const newCs: SessionStartDataConnectorConfiguration = {
328+
...cs,
329+
saveCredentials: false,
330+
touched: false,
331+
};
329332
const newStorage = { ...newCs.dataConnector.storage };
330333
// The following two lines remove the sensitive fields from the storage configuration,
331334
// which should be ok, but isn't; so keep in the sensitive fields.
@@ -339,27 +342,27 @@ export function storageDefinitionAfterSavingCredentialsFromConfig(
339342
return newCs;
340343
}
341344

342-
export function storageDefinitionFromConfig(
345+
export function dataConnectorsOverrideFromConfig(
343346
config: SessionStartDataConnectorConfiguration
344-
): SessionCloudStorageV2 {
345-
const storageDefinition = config.dataConnector.storage;
346-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
347-
const { sensitive_fields, ...s } = config.dataConnector.storage;
348-
const newStorageDefinition = {
349-
...s,
350-
name: config.dataConnector.slug,
351-
storage_id: config.dataConnector.id,
352-
};
353-
newStorageDefinition.configuration = { ...storageDefinition.configuration };
354-
const sensitiveFieldValues = config.sensitiveFieldValues;
355-
Object.entries(sensitiveFieldValues).forEach(([name, value]) => {
347+
): SessionDataConnectorOverride[] {
348+
if (!config.skip && !config.touched) {
349+
return [];
350+
}
351+
352+
const configuration = { ...config.dataConnector.storage.configuration };
353+
Object.entries(config.sensitiveFieldValues).forEach(([name, value]) => {
356354
if (value != null && value !== "") {
357-
newStorageDefinition.configuration[name] = value;
355+
configuration[name] = value;
358356
} else {
359-
delete newStorageDefinition.configuration[name];
357+
delete configuration[name];
360358
}
361359
});
362-
return newStorageDefinition;
360+
const override: SessionDataConnectorOverride = {
361+
skip: config.skip,
362+
data_connector_id: config.dataConnector.id,
363+
configuration,
364+
};
365+
return [override];
363366
}
364367

365368
function overrideOptions(

client/src/features/sessionsV2/DataConnectorSecretsModal.tsx

Lines changed: 11 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ const CONTEXT_STRINGS = {
6060
dataCy: "session-data-connector-credentials-modal",
6161
header: "Session Storage Credentials",
6262
testError:
63-
"The data connector could not be mounted. Please retry with different credentials, or skip the test. If you skip, the data connector will still try to mount, using the provided credentials, at session launch time.",
63+
"The data connector could not be mounted. Please retry with different credentials, or skip the data connector. If you skip, the data connector will not be mounted in the session.",
6464
},
6565
storage: {
6666
continueButton: "Test and Save",
@@ -222,6 +222,8 @@ export default function DataConnectorSecretsModal({
222222
newCloudStorageConfigs[index] = {
223223
...dataConnectorConfigs[index],
224224
active: false,
225+
saveCredentials: false,
226+
skip: true,
225227
};
226228
setDataConnectorConfigs(newCloudStorageConfigs);
227229
onNext(newCloudStorageConfigs);
@@ -232,7 +234,10 @@ export default function DataConnectorSecretsModal({
232234
if (dataConnectorConfigs == null || dataConnectorConfigs.length < 1)
233235
return;
234236

235-
const config = { ...dataConnectorConfigs[index] };
237+
const config: DataConnectorConfiguration = {
238+
...dataConnectorConfigs[index],
239+
touched: true,
240+
};
236241
const sensitiveFieldValues = { ...config.sensitiveFieldValues };
237242
const { saveCredentials } = options;
238243
if (saveCredentials === true || saveCredentials === false) {
@@ -348,12 +353,7 @@ function CredentialsButtons({
348353
<XLg className={cx("bi", "me-1")} />
349354
Cancel
350355
</Button>
351-
{context === "session" && (
352-
<SkipConnectionTestButton
353-
onSkip={onSkip}
354-
validationResult={validationResult}
355-
/>
356-
)}
356+
{context === "session" && <SkipConnectionTestButton onSkip={onSkip} />}
357357
{context === "storage" && (
358358
<ClearCredentialsButton
359359
onSkip={onSkip}
@@ -440,6 +440,7 @@ function ProgressBreadcrumbs({
440440
newCloudStorageConfigs[idx] = {
441441
...dataConnectorConfigs[idx],
442442
active: true,
443+
skip: false,
443444
};
444445
setDataConnectorConfigs(newCloudStorageConfigs);
445446
setIndex(idx);
@@ -614,8 +615,7 @@ function SensitiveFieldInput({
614615

615616
function SkipConnectionTestButton({
616617
onSkip,
617-
validationResult,
618-
}: Pick<CredentialsButtonsProps, "onSkip" | "validationResult">) {
618+
}: Pick<CredentialsButtonsProps, "onSkip">) {
619619
const skipButtonRef = useRef<HTMLAnchorElement>(null);
620620
return (
621621
<>
@@ -626,12 +626,7 @@ function SkipConnectionTestButton({
626626
</Button>
627627
</span>
628628
<UncontrolledTooltip target={skipButtonRef}>
629-
Skip the connection test. At session launch, the storage will try to
630-
mount
631-
{validationResult.isError
632-
? " using the provided credentials"
633-
: " without any credentials"}
634-
.
629+
Skip the data connector. It will not be mounted in the session.
635630
</UncontrolledTooltip>
636631
</>
637632
);

0 commit comments

Comments
 (0)