From afa1871ef321eecc778566eeb133736c7ed0226d Mon Sep 17 00:00:00 2001 From: shydefoo Date: Wed, 15 May 2024 17:06:37 +0800 Subject: [PATCH 01/10] Minor features to mlp * Modify project creation form to conditionally allow custom team/stream * Add new validation for team and stream to match k8s valid label values, except for capital letters --- api/cmd/serve.go | 2 + ui/packages/app/src/config.js | 4 +- .../src/project_setting/form/ProjectForm.js | 54 +++++++++++++------ ui/packages/app/src/validation/validation.js | 9 ++++ 4 files changed, 51 insertions(+), 18 deletions(-) diff --git a/api/cmd/serve.go b/api/cmd/serve.go index 9d5eaf65..9849c96f 100644 --- a/api/cmd/serve.go +++ b/api/cmd/serve.go @@ -114,6 +114,8 @@ type uiEnvHandler struct { Streams config.Streams `json:"REACT_APP_STREAMS"` Docs config.Documentations `json:"REACT_APP_DOC_LINKS"` MaxAuthzCacheExpiryMinutes string `json:"REACT_APP_MAX_AUTHZ_CACHE_EXPIRY_MINUTES"` + AllowCustomStream bool `json:"REACT_APP_CUSTOM_STREAM"` + AllowCustomTeam bool `json:"REACT_APP_CUSTOM_TEAM"` } func (h uiEnvHandler) handler(w http.ResponseWriter, r *http.Request) { diff --git a/ui/packages/app/src/config.js b/ui/packages/app/src/config.js index 3ce499f5..e836613c 100644 --- a/ui/packages/app/src/config.js +++ b/ui/packages/app/src/config.js @@ -33,7 +33,9 @@ const config = { ), CLOCKWORK_UI_HOMEPAGE: getEnv("REACT_APP_CLOCKWORK_UI_HOMEPAGE"), - KUBEFLOW_UI_HOMEPAGE: getEnv("REACT_APP_KUBEFLOW_UI_HOMEPAGE") + KUBEFLOW_UI_HOMEPAGE: getEnv("REACT_APP_KUBEFLOW_UI_HOMEPAGE"), + ALLOW_CUSTOM_STREAM: getEnv("REACT_APP_CUSTOM_STREAM"), + ALLOW_CUSTOM_TEAM: getEnv("REACT_APP_CUSTOM_TEAM") }; export default config; diff --git a/ui/packages/app/src/project_setting/form/ProjectForm.js b/ui/packages/app/src/project_setting/form/ProjectForm.js index 2a61f88a..452a6b56 100644 --- a/ui/packages/app/src/project_setting/form/ProjectForm.js +++ b/ui/packages/app/src/project_setting/form/ProjectForm.js @@ -13,7 +13,10 @@ import { addToast, EuiComboBoxSelect, useMlpApi } from "@caraml-dev/ui-lib"; import { ProjectFormContext } from "./context"; import { EmailTextArea } from "./EmailTextArea"; import { Labels } from "./Labels"; -import { isDNS1123Label } from "../../validation/validation"; +import { + isDNS1123Label, + isValidK8sLabelValue +} from "../../validation/validation"; import config from "../../config"; import { useNavigate } from "react-router-dom"; @@ -61,10 +64,10 @@ const ProjectForm = () => { const [isValidStream, setIsValidStream] = useState(false); const onStreamChange = selectedStream => { if (selectedStream !== project.stream) { - let isValid = isDNS1123Label(selectedStream); + let isValid = isValidK8sLabelValue(selectedStream); if (!isValid) { setStreamError( - "Stream name is invalid. It should contain only lowercase alphanumeric and dash (-)" + "Stream name is invalid. It should contain only lowercase alphanumeric and dash (-), or underscore (_) or period (.)" ); } setIsValidStream(isValid); @@ -76,10 +79,10 @@ const ProjectForm = () => { const [isValidTeam, setIsValidTeam] = useState(false); const onTeamChange = selectedTeam => { if (selectedTeam !== project.team) { - let isValid = isDNS1123Label(selectedTeam); + let isValid = isValidK8sLabelValue(selectedTeam); if (!isValid) { setTeamError( - "Team name is invalid. It should contain only lowercase alphanumeric and dash (-)" + "Team name is invalid. It should contain only lowercase alphanumeric and dash (-), or underscore (_) or period (.)" ); } setIsValidTeam(isValid); @@ -193,24 +196,41 @@ const ProjectForm = () => { title={

Stream

} description="Product stream the project belongs to"> - + {config.ALLOW_CUSTOM_STREAM ? ( + + ) : ( + + )} Team} description="Owner of the project"> - + {config.ALLOW_CUSTOM_TEAM ? ( + + ) : ( + + )} { const expression = /^\w+([.-]?\w+)*@\w+([.-]?\w+)*(\.\w{2,3})+$/; return expression.test(email); }; + +// See https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/#syntax-and-character-set for full details +export const isValidK8sLabelValue = value => { + const expression = /^[a-z0-9]([_.-a-z0-9]*[a-z0-9])?$/; + if (value === undefined || value.length > DNS1123LabelMaxLength) { + return false; + } + return expression.test(value); +}; From 76c975bbb256e55d3ec7c82aae195fdc75d4a61c Mon Sep 17 00:00:00 2001 From: shydefoo Date: Wed, 15 May 2024 17:36:20 +0800 Subject: [PATCH 02/10] Fix naming convention --- api/cmd/serve.go | 2 -- api/config/config.go | 9 +++++++-- api/config/config_test.go | 14 ++++++++++---- ui/packages/app/src/config.js | 4 ++-- ui/packages/app/src/validation/validation.js | 2 +- 5 files changed, 20 insertions(+), 11 deletions(-) diff --git a/api/cmd/serve.go b/api/cmd/serve.go index 9849c96f..9d5eaf65 100644 --- a/api/cmd/serve.go +++ b/api/cmd/serve.go @@ -114,8 +114,6 @@ type uiEnvHandler struct { Streams config.Streams `json:"REACT_APP_STREAMS"` Docs config.Documentations `json:"REACT_APP_DOC_LINKS"` MaxAuthzCacheExpiryMinutes string `json:"REACT_APP_MAX_AUTHZ_CACHE_EXPIRY_MINUTES"` - AllowCustomStream bool `json:"REACT_APP_CUSTOM_STREAM"` - AllowCustomTeam bool `json:"REACT_APP_CUSTOM_TEAM"` } func (h uiEnvHandler) handler(w http.ResponseWriter, r *http.Request) { diff --git a/api/config/config.go b/api/config/config.go index f78994ef..ffd56397 100644 --- a/api/config/config.go +++ b/api/config/config.go @@ -118,6 +118,9 @@ type UIConfig struct { ClockworkUIHomepage string `json:"REACT_APP_CLOCKWORK_UI_HOMEPAGE"` KubeflowUIHomepage string `json:"REACT_APP_KUBEFLOW_UI_HOMEPAGE"` + + AllowCustomStream bool `json:"REACT_APP_ALLOW_CUSTOM_STREAM"` + AllowCustomTeam bool `json:"REACT_APP_ALLOW_CUSTOM_TEAM"` } // Transform env variables to the format consumed by koanf. @@ -213,8 +216,10 @@ var defaultConfig = &Config{ TrackingURL: "", }, UI: &UIConfig{ - IndexPath: "index.html", - StaticPath: "ui/build", + IndexPath: "index.html", + StaticPath: "ui/build", + AllowCustomTeam: true, + AllowCustomStream: true, }, DefaultSecretStorage: &SecretStorage{ Name: "internal", diff --git a/api/config/config_test.go b/api/config/config_test.go index 7a23cb20..5f18c0ca 100644 --- a/api/config/config_test.go +++ b/api/config/config_test.go @@ -85,8 +85,10 @@ func TestLoad(t *testing.T) { "EmptyStream": {}, }, UI: &config.UIConfig{ - StaticPath: "ui/build", - IndexPath: "index.html", + StaticPath: "ui/build", + IndexPath: "index.html", + AllowCustomTeam: true, + AllowCustomStream: true, }, DefaultSecretStorage: &config.SecretStorage{ Name: "default-secret-storage", @@ -142,8 +144,10 @@ func TestLoad(t *testing.T) { "EmptyStream": {}, }, UI: &config.UIConfig{ - StaticPath: "ui/build", - IndexPath: "index.html", + StaticPath: "ui/build", + IndexPath: "index.html", + AllowCustomTeam: true, + AllowCustomStream: true, }, DefaultSecretStorage: &config.SecretStorage{ Name: "default-secret-storage", @@ -240,6 +244,8 @@ func TestLoad(t *testing.T) { ClockworkUIHomepage: "http://clockwork.dev", KubeflowUIHomepage: "http://kubeflow.org", + AllowCustomTeam: true, + AllowCustomStream: true, }, DefaultSecretStorage: &config.SecretStorage{ Name: "default-secret-storage", diff --git a/ui/packages/app/src/config.js b/ui/packages/app/src/config.js index e836613c..0a09abd6 100644 --- a/ui/packages/app/src/config.js +++ b/ui/packages/app/src/config.js @@ -34,8 +34,8 @@ const config = { CLOCKWORK_UI_HOMEPAGE: getEnv("REACT_APP_CLOCKWORK_UI_HOMEPAGE"), KUBEFLOW_UI_HOMEPAGE: getEnv("REACT_APP_KUBEFLOW_UI_HOMEPAGE"), - ALLOW_CUSTOM_STREAM: getEnv("REACT_APP_CUSTOM_STREAM"), - ALLOW_CUSTOM_TEAM: getEnv("REACT_APP_CUSTOM_TEAM") + ALLOW_CUSTOM_STREAM: getEnv("REACT_APP_ALLOW_CUSTOM_STREAM") || true, + ALLOW_CUSTOM_TEAM: getEnv("REACT_APP_ALLOW_CUSTOM_TEAM") || true }; export default config; diff --git a/ui/packages/app/src/validation/validation.js b/ui/packages/app/src/validation/validation.js index 17e37642..acf90613 100644 --- a/ui/packages/app/src/validation/validation.js +++ b/ui/packages/app/src/validation/validation.js @@ -26,7 +26,7 @@ export const validateEmail = email => { // See https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/#syntax-and-character-set for full details export const isValidK8sLabelValue = value => { - const expression = /^[a-z0-9]([_.-a-z0-9]*[a-z0-9])?$/; + const expression = /^[a-z0-9]([_.\-a-z0-9]*[a-z0-9])?$/; if (value === undefined || value.length > DNS1123LabelMaxLength) { return false; } From 8335d4b4ad82a4491356b844e0528ddf0571c540 Mon Sep 17 00:00:00 2001 From: shydefoo Date: Mon, 20 May 2024 17:37:52 +0800 Subject: [PATCH 03/10] Refactor code to Stream and Team components --- .../src/project_setting/form/ProjectForm.js | 61 ------------------- .../app/src/project_setting/form/Stream.js | 29 ++++++--- .../app/src/project_setting/form/Team.js | 28 ++++++--- 3 files changed, 38 insertions(+), 80 deletions(-) diff --git a/ui/packages/app/src/project_setting/form/ProjectForm.js b/ui/packages/app/src/project_setting/form/ProjectForm.js index 99fdeedb..244b9260 100644 --- a/ui/packages/app/src/project_setting/form/ProjectForm.js +++ b/ui/packages/app/src/project_setting/form/ProjectForm.js @@ -17,7 +17,6 @@ import { isDNS1123Label, isValidK8sLabelValue } from "../../validation/validation"; -import config from "../../config"; import { Stream } from "./Stream"; import { Team } from "./Team"; import { useNavigate } from "react-router-dom"; @@ -50,33 +49,6 @@ const ProjectForm = () => { }; const [isValidStream, setIsValidStream] = useState(false); - const onStreamChange = selectedStream => { - if (selectedStream !== project.stream) { - let isValid = isValidK8sLabelValue(selectedStream); - if (!isValid) { - setStreamError( - "Stream name is invalid. It should contain only lowercase alphanumeric and dash (-), or underscore (_) or period (.)" - ); - } - setIsValidStream(isValid); - setStream(selectedStream); - } - }; - - const [teamError, setTeamError] = useState(""); - const [isValidTeam, setIsValidTeam] = useState(false); - const onTeamChange = selectedTeam => { - if (selectedTeam !== project.team) { - let isValid = isValidK8sLabelValue(selectedTeam); - if (!isValid) { - setTeamError( - "Team name is invalid. It should contain only lowercase alphanumeric and dash (-), or underscore (_) or period (.)" - ); - } - setIsValidTeam(isValid); - setTeam(selectedTeam); - } - }; useEffect(() => { if (!project.team) { @@ -158,22 +130,6 @@ const ProjectForm = () => { Stream} description="Product stream the project belongs to"> - - {config.ALLOW_CUSTOM_STREAM ? ( - - ) : ( - - )} - { Team} description="Owner of the project"> - - {config.ALLOW_CUSTOM_TEAM ? ( - - ) : ( - - )} - { - let isValid = isDNS1123Label(stream); + let isValid = isValidK8sLabelValue(stream); if (!isValid) { setStreamError( - "Stream name is invalid. It should contain only lowercase alphanumeric and dash (-), and must start and end with an alphanumeric character" + "Stream name is invalid. It should contain only lowercase alphanumeric and dash (-), or underscore (_) or period (.)" ); } setIsValidStream(isValid); @@ -33,13 +33,22 @@ export const Stream = ({ return ( - + {config.ALLOW_CUSTOM_STREAM ? ( + + ) : ( + + )} ); }; diff --git a/ui/packages/app/src/project_setting/form/Team.js b/ui/packages/app/src/project_setting/form/Team.js index 307c75a6..e8f1d8dc 100644 --- a/ui/packages/app/src/project_setting/form/Team.js +++ b/ui/packages/app/src/project_setting/form/Team.js @@ -1,7 +1,7 @@ import React, { useState, useEffect, useMemo } from "react"; import { EuiFormRow } from "@elastic/eui"; import { EuiComboBoxSelect } from "@caraml-dev/ui-lib"; -import { isDNS1123Label } from "../../validation/validation"; +import { isValidK8sLabelValue } from "../../validation/validation"; import config from "../../config"; export const Team = ({ @@ -21,7 +21,7 @@ export const Team = ({ const [teamError, setTeamError] = useState(""); const onTeamChange = team => { - let isValid = isDNS1123Label(team); + let isValid = isValidK8sLabelValue(team); if (!isValid) { setTeamError( "Team name is invalid. It should contain only lowercase alphanumeric and dash (-), and must start and end with an alphanumeric character" @@ -39,13 +39,23 @@ export const Team = ({ return ( - + {config.ALLOW_CUSTOM_TEAM ? ( + + ) : ( + + )} ); }; From 7fefd8a156e99be232ae43364fd67bc88deafdfa Mon Sep 17 00:00:00 2001 From: shydefoo Date: Mon, 20 May 2024 17:39:07 +0800 Subject: [PATCH 04/10] Fix formatting --- api/config/config.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/api/config/config.go b/api/config/config.go index a49c33eb..70257638 100644 --- a/api/config/config.go +++ b/api/config/config.go @@ -119,9 +119,9 @@ type UIConfig struct { ClockworkUIHomepage string `json:"REACT_APP_CLOCKWORK_UI_HOMEPAGE"` KubeflowUIHomepage string `json:"REACT_APP_KUBEFLOW_UI_HOMEPAGE"` - AllowCustomStream bool `json:"REACT_APP_ALLOW_CUSTOM_STREAM"` - AllowCustomTeam bool `json:"REACT_APP_ALLOW_CUSTOM_TEAM"` - ProjectInfoUpdateEnabled bool `json:"REACT_APP_PROJECT_INFO_UPDATE_ENABLED"` + AllowCustomStream bool `json:"REACT_APP_ALLOW_CUSTOM_STREAM"` + AllowCustomTeam bool `json:"REACT_APP_ALLOW_CUSTOM_TEAM"` + ProjectInfoUpdateEnabled bool `json:"REACT_APP_PROJECT_INFO_UPDATE_ENABLED"` } // Transform env variables to the format consumed by koanf. From 08da24e67ce24bd482656842720b8e1a0eaa32bd Mon Sep 17 00:00:00 2001 From: shydefoo Date: Mon, 20 May 2024 17:57:22 +0800 Subject: [PATCH 05/10] Fix linting issue --- ui/packages/app/src/project_setting/form/ProjectForm.js | 5 +---- ui/packages/app/src/project_setting/form/Team.js | 2 +- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/ui/packages/app/src/project_setting/form/ProjectForm.js b/ui/packages/app/src/project_setting/form/ProjectForm.js index 244b9260..7e64e928 100644 --- a/ui/packages/app/src/project_setting/form/ProjectForm.js +++ b/ui/packages/app/src/project_setting/form/ProjectForm.js @@ -13,10 +13,7 @@ import { addToast, useMlpApi } from "@caraml-dev/ui-lib"; import { ProjectFormContext } from "./context"; import { EmailTextArea } from "./EmailTextArea"; import { Labels } from "./Labels"; -import { - isDNS1123Label, - isValidK8sLabelValue -} from "../../validation/validation"; +import { isDNS1123Label } from "../../validation/validation"; import { Stream } from "./Stream"; import { Team } from "./Team"; import { useNavigate } from "react-router-dom"; diff --git a/ui/packages/app/src/project_setting/form/Team.js b/ui/packages/app/src/project_setting/form/Team.js index e8f1d8dc..6b227779 100644 --- a/ui/packages/app/src/project_setting/form/Team.js +++ b/ui/packages/app/src/project_setting/form/Team.js @@ -24,7 +24,7 @@ export const Team = ({ let isValid = isValidK8sLabelValue(team); if (!isValid) { setTeamError( - "Team name is invalid. It should contain only lowercase alphanumeric and dash (-), and must start and end with an alphanumeric character" + "Team name is invalid. It should contain only lowercase alphanumeric and dash (-) or underscore (_) or period (.), and must start and end with an alphanumeric character" ); } setIsValidTeam(isValid); From b5f9baea901b0650a6994c2ae97106d9810c3bae Mon Sep 17 00:00:00 2001 From: shydefoo Date: Mon, 20 May 2024 17:59:42 +0800 Subject: [PATCH 06/10] Fix formatting --- api/cmd/bootstrap_test.go | 5 +++-- api/config/config_test.go | 8 ++++---- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/api/cmd/bootstrap_test.go b/api/cmd/bootstrap_test.go index 6f7ba410..227c70cd 100644 --- a/api/cmd/bootstrap_test.go +++ b/api/cmd/bootstrap_test.go @@ -3,10 +3,11 @@ package cmd import ( "testing" - "github.com/caraml-dev/mlp/api/pkg/authz/enforcer" - enforcerMock "github.com/caraml-dev/mlp/api/pkg/authz/enforcer/mocks" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" + + "github.com/caraml-dev/mlp/api/pkg/authz/enforcer" + enforcerMock "github.com/caraml-dev/mlp/api/pkg/authz/enforcer/mocks" ) func TestStartKetoBootsrap(t *testing.T) { diff --git a/api/config/config_test.go b/api/config/config_test.go index 0753325c..c6f2b59e 100644 --- a/api/config/config_test.go +++ b/api/config/config_test.go @@ -242,10 +242,10 @@ func TestLoad(t *testing.T) { StaticPath: "ui/build", IndexPath: "index.html", - ClockworkUIHomepage: "http://clockwork.dev", - KubeflowUIHomepage: "http://kubeflow.org", - AllowCustomTeam: true, - AllowCustomStream: true, + ClockworkUIHomepage: "http://clockwork.dev", + KubeflowUIHomepage: "http://kubeflow.org", + AllowCustomTeam: true, + AllowCustomStream: true, ProjectInfoUpdateEnabled: true, }, DefaultSecretStorage: &config.SecretStorage{ From 66e3d3c19ef3fbba7920b5e17e6a9c805ebb21db Mon Sep 17 00:00:00 2001 From: shydefoo Date: Tue, 21 May 2024 19:41:03 +0800 Subject: [PATCH 07/10] Address review comments --- .../app/src/project_setting/form/Stream.js | 22 +++++------------ .../app/src/project_setting/form/Team.js | 24 ++++++------------- 2 files changed, 13 insertions(+), 33 deletions(-) diff --git a/ui/packages/app/src/project_setting/form/Stream.js b/ui/packages/app/src/project_setting/form/Stream.js index cb050967..d721e7af 100644 --- a/ui/packages/app/src/project_setting/form/Stream.js +++ b/ui/packages/app/src/project_setting/form/Stream.js @@ -33,22 +33,12 @@ export const Stream = ({ return ( - {config.ALLOW_CUSTOM_STREAM ? ( - - ) : ( - - )} + ); }; diff --git a/ui/packages/app/src/project_setting/form/Team.js b/ui/packages/app/src/project_setting/form/Team.js index 6b227779..06709c88 100644 --- a/ui/packages/app/src/project_setting/form/Team.js +++ b/ui/packages/app/src/project_setting/form/Team.js @@ -39,23 +39,13 @@ export const Team = ({ return ( - {config.ALLOW_CUSTOM_TEAM ? ( - - ) : ( - - )} + ); }; From 44f173f36d1ef45f2942256ba374d29f390549c5 Mon Sep 17 00:00:00 2001 From: shydefoo Date: Mon, 27 May 2024 11:03:18 +0800 Subject: [PATCH 08/10] Fix pr comments --- ui/packages/app/src/project_setting/form/Stream.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ui/packages/app/src/project_setting/form/Stream.js b/ui/packages/app/src/project_setting/form/Stream.js index d721e7af..7ace47e1 100644 --- a/ui/packages/app/src/project_setting/form/Stream.js +++ b/ui/packages/app/src/project_setting/form/Stream.js @@ -24,7 +24,7 @@ export const Stream = ({ let isValid = isValidK8sLabelValue(stream); if (!isValid) { setStreamError( - "Stream name is invalid. It should contain only lowercase alphanumeric and dash (-), or underscore (_) or period (.)" + "Stream name is invalid. It should contain only lowercase alphanumeric and dash (-), or underscore (_) or period (.), and must start and end with an alphanumeric character" ); } setIsValidStream(isValid); @@ -38,6 +38,7 @@ export const Stream = ({ options={streamOptions} onChange={onStreamChange} onCreateOption={config.ALLOW_CUSTOM_STREAM ? onStreamChange : undefined} + isDiasbled={isDisabled} /> ); From f192839cf1a51ca74cf2a4071af1a2f52219fa56 Mon Sep 17 00:00:00 2001 From: shydefoo Date: Mon, 27 May 2024 13:48:01 +0800 Subject: [PATCH 09/10] Remove unnecessary useEffect() --- ui/packages/app/src/project_setting/form/ProjectForm.js | 5 ----- 1 file changed, 5 deletions(-) diff --git a/ui/packages/app/src/project_setting/form/ProjectForm.js b/ui/packages/app/src/project_setting/form/ProjectForm.js index 7e64e928..5526cecb 100644 --- a/ui/packages/app/src/project_setting/form/ProjectForm.js +++ b/ui/packages/app/src/project_setting/form/ProjectForm.js @@ -47,11 +47,6 @@ const ProjectForm = () => { const [isValidStream, setIsValidStream] = useState(false); - useEffect(() => { - if (!project.team) { - setIsValidTeam(false); - } - }, [project.team]); const [isValidTeam, setIsValidTeam] = useState(false); const onAdminValueChange = emails => { From 46bc0fd611e3ef1787415cf29b3fedfaa09f11bf Mon Sep 17 00:00:00 2001 From: shydefoo Date: Mon, 27 May 2024 14:03:57 +0800 Subject: [PATCH 10/10] Fix label validation function --- .../app/src/project_setting/form/Labels.js | 6 +++--- .../app/src/project_setting/form/ProjectForm.js | 4 ++-- .../app/src/project_setting/form/Stream.js | 4 ++-- ui/packages/app/src/project_setting/form/Team.js | 4 ++-- .../project_info/ProjectInfoForm.js | 12 +++++++----- ui/packages/app/src/validation/validation.js | 15 ++++----------- 6 files changed, 20 insertions(+), 25 deletions(-) diff --git a/ui/packages/app/src/project_setting/form/Labels.js b/ui/packages/app/src/project_setting/form/Labels.js index 660d83a2..d1b469d9 100644 --- a/ui/packages/app/src/project_setting/form/Labels.js +++ b/ui/packages/app/src/project_setting/form/Labels.js @@ -8,7 +8,7 @@ import { EuiButton, EuiFormRow } from "@elastic/eui"; -import { isDNS1123Label } from "../../validation/validation"; +import { isValidK8sLabelKeyValue } from "../../validation/validation"; export const Labels = ({ labels, @@ -52,7 +52,7 @@ export const Labels = ({ newItems[idx] = { ...newItems[idx], key: newKey, - isKeyValid: isDNS1123Label(newKey) + isKeyValid: isValidK8sLabelKeyValue(newKey) }; setItems(newItems); onChange(newItems); @@ -66,7 +66,7 @@ export const Labels = ({ newItems[idx] = { ...newItems[idx], value: newValue, - isValueValid: isDNS1123Label(newValue) + isValueValid: isValidK8sLabelKeyValue(newValue) }; setItems(newItems); onChange(newItems); diff --git a/ui/packages/app/src/project_setting/form/ProjectForm.js b/ui/packages/app/src/project_setting/form/ProjectForm.js index 5526cecb..3685f913 100644 --- a/ui/packages/app/src/project_setting/form/ProjectForm.js +++ b/ui/packages/app/src/project_setting/form/ProjectForm.js @@ -13,7 +13,7 @@ import { addToast, useMlpApi } from "@caraml-dev/ui-lib"; import { ProjectFormContext } from "./context"; import { EmailTextArea } from "./EmailTextArea"; import { Labels } from "./Labels"; -import { isDNS1123Label } from "../../validation/validation"; +import { isValidK8sLabelKeyValue } from "../../validation/validation"; import { Stream } from "./Stream"; import { Team } from "./Team"; import { useNavigate } from "react-router-dom"; @@ -35,7 +35,7 @@ const ProjectForm = () => { const [isValidProject, setIsValidProject] = useState(false); const onProjectChange = e => { const newValue = e.target.value; - let isValid = isDNS1123Label(newValue); + let isValid = isValidK8sLabelKeyValue(newValue); if (!isValid) { setProjectError( "Project name is invalid. It should contain only lowercase alphanumeric and dash ('-')" diff --git a/ui/packages/app/src/project_setting/form/Stream.js b/ui/packages/app/src/project_setting/form/Stream.js index 7ace47e1..18d3dc07 100644 --- a/ui/packages/app/src/project_setting/form/Stream.js +++ b/ui/packages/app/src/project_setting/form/Stream.js @@ -1,7 +1,7 @@ import React, { useState, useMemo } from "react"; import { EuiFormRow } from "@elastic/eui"; import { EuiComboBoxSelect } from "@caraml-dev/ui-lib"; -import { isValidK8sLabelValue } from "../../validation/validation"; +import { isValidK8sLabelKeyValue } from "../../validation/validation"; import config from "../../config"; export const Stream = ({ @@ -21,7 +21,7 @@ export const Stream = ({ const [streamError, setStreamError] = useState(""); const onStreamChange = stream => { - let isValid = isValidK8sLabelValue(stream); + let isValid = isValidK8sLabelKeyValue(stream); if (!isValid) { setStreamError( "Stream name is invalid. It should contain only lowercase alphanumeric and dash (-), or underscore (_) or period (.), and must start and end with an alphanumeric character" diff --git a/ui/packages/app/src/project_setting/form/Team.js b/ui/packages/app/src/project_setting/form/Team.js index 06709c88..17a77014 100644 --- a/ui/packages/app/src/project_setting/form/Team.js +++ b/ui/packages/app/src/project_setting/form/Team.js @@ -1,7 +1,7 @@ import React, { useState, useEffect, useMemo } from "react"; import { EuiFormRow } from "@elastic/eui"; import { EuiComboBoxSelect } from "@caraml-dev/ui-lib"; -import { isValidK8sLabelValue } from "../../validation/validation"; +import { isValidK8sLabelKeyValue } from "../../validation/validation"; import config from "../../config"; export const Team = ({ @@ -21,7 +21,7 @@ export const Team = ({ const [teamError, setTeamError] = useState(""); const onTeamChange = team => { - let isValid = isValidK8sLabelValue(team); + let isValid = isValidK8sLabelKeyValue(team); if (!isValid) { setTeamError( "Team name is invalid. It should contain only lowercase alphanumeric and dash (-) or underscore (_) or period (.), and must start and end with an alphanumeric character" diff --git a/ui/packages/app/src/project_setting/project_info/ProjectInfoForm.js b/ui/packages/app/src/project_setting/project_info/ProjectInfoForm.js index 9c77433b..8a944468 100644 --- a/ui/packages/app/src/project_setting/project_info/ProjectInfoForm.js +++ b/ui/packages/app/src/project_setting/project_info/ProjectInfoForm.js @@ -9,7 +9,7 @@ import { } from "@elastic/eui"; import SubmitProjectInfoForm from "./SubmitProjectInfoForm"; import config from "../../config"; -import { isDNS1123Label } from "../../validation/validation"; +import { isValidK8sLabelKeyValue } from "../../validation/validation"; import { ProjectFormContext } from "../form/context"; import { Labels } from "../form/Labels"; import { Stream } from "../form/Stream"; @@ -21,9 +21,11 @@ const ProjectInfoForm = ({ originalProject, fetchUpdates }) => { ); const [isValidStream, setIsValidStream] = useState( - isDNS1123Label(project.stream) + isValidK8sLabelKeyValue(project.stream) + ); + const [isValidTeam, setIsValidTeam] = useState( + isValidK8sLabelKeyValue(project.team) ); - const [isValidTeam, setIsValidTeam] = useState(isDNS1123Label(project.team)); const [isValidLabels, setIsValidLabels] = useState( project.labels.length === 0 @@ -31,8 +33,8 @@ const ProjectInfoForm = ({ originalProject, fetchUpdates }) => { : project.labels.reduce((labelsValid, label) => { return ( labelsValid && - isDNS1123Label(label.key) && - isDNS1123Label(label.value) + isValidK8sLabelKeyValue(label.key) && + isValidK8sLabelKeyValue(label.value) ); }, true) ); diff --git a/ui/packages/app/src/validation/validation.js b/ui/packages/app/src/validation/validation.js index acf90613..2259b571 100644 --- a/ui/packages/app/src/validation/validation.js +++ b/ui/packages/app/src/validation/validation.js @@ -1,7 +1,9 @@ // Test whether the value follow RFC1123 format const DNS1123LabelMaxLength = 63; -export const isDNS1123Label = value => { - const expression = /^[a-z0-9]([-a-z0-9]*[a-z0-9])?$/; + +// See https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/#syntax-and-character-set for full details +export const isValidK8sLabelKeyValue = value => { + const expression = /^[a-z0-9]([_.\-a-z0-9]*[a-z0-9])?$/; if (value === undefined || value.length > DNS1123LabelMaxLength) { return false; } @@ -23,12 +25,3 @@ export const validateEmail = email => { const expression = /^\w+([.-]?\w+)*@\w+([.-]?\w+)*(\.\w{2,3})+$/; return expression.test(email); }; - -// See https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/#syntax-and-character-set for full details -export const isValidK8sLabelValue = value => { - const expression = /^[a-z0-9]([_.\-a-z0-9]*[a-z0-9])?$/; - if (value === undefined || value.length > DNS1123LabelMaxLength) { - return false; - } - return expression.test(value); -};