diff --git a/infra/feast-operator/api/v1alpha1/featurestore_types.go b/infra/feast-operator/api/v1alpha1/featurestore_types.go index 756c2e17ab1..191505cc6a6 100644 --- a/infra/feast-operator/api/v1alpha1/featurestore_types.go +++ b/infra/feast-operator/api/v1alpha1/featurestore_types.go @@ -68,8 +68,8 @@ const ( // FeatureStoreSpec defines the desired state of FeatureStore type FeatureStoreSpec struct { - // +kubebuilder:validation:Pattern="^[A-Za-z0-9][A-Za-z0-9_]*$" - // FeastProject is the Feast project id. This can be any alphanumeric string with underscores, but it cannot start with an underscore. Required. + // +kubebuilder:validation:Pattern="^[A-Za-z0-9][A-Za-z0-9_-]*$" + // FeastProject is the Feast project id. This can be any alphanumeric string with underscores and hyphens, but it cannot start with an underscore or hyphen. Required. FeastProject string `json:"feastProject"` FeastProjectDir *FeastProjectDir `json:"feastProjectDir,omitempty"` Services *FeatureStoreServices `json:"services,omitempty"` diff --git a/infra/feast-operator/bundle/manifests/feast.dev_featurestores.yaml b/infra/feast-operator/bundle/manifests/feast.dev_featurestores.yaml index 701ed9bf052..5afa2ea3704 100644 --- a/infra/feast-operator/bundle/manifests/feast.dev_featurestores.yaml +++ b/infra/feast-operator/bundle/manifests/feast.dev_featurestores.yaml @@ -463,7 +463,7 @@ spec: type: object feastProject: description: FeastProject is the Feast project id. - pattern: ^[A-Za-z0-9][A-Za-z0-9_]*$ + pattern: ^[A-Za-z0-9][A-Za-z0-9_-]*$ type: string feastProjectDir: description: FeastProjectDir defines how to create the feast project @@ -4423,7 +4423,7 @@ spec: type: object feastProject: description: FeastProject is the Feast project id. - pattern: ^[A-Za-z0-9][A-Za-z0-9_]*$ + pattern: ^[A-Za-z0-9][A-Za-z0-9_-]*$ type: string feastProjectDir: description: FeastProjectDir defines how to create the feast project diff --git a/infra/feast-operator/config/crd/bases/feast.dev_featurestores.yaml b/infra/feast-operator/config/crd/bases/feast.dev_featurestores.yaml index 360fbba5453..8debe3639f9 100644 --- a/infra/feast-operator/config/crd/bases/feast.dev_featurestores.yaml +++ b/infra/feast-operator/config/crd/bases/feast.dev_featurestores.yaml @@ -463,7 +463,7 @@ spec: type: object feastProject: description: FeastProject is the Feast project id. - pattern: ^[A-Za-z0-9][A-Za-z0-9_]*$ + pattern: ^[A-Za-z0-9][A-Za-z0-9_-]*$ type: string feastProjectDir: description: FeastProjectDir defines how to create the feast project @@ -4423,7 +4423,7 @@ spec: type: object feastProject: description: FeastProject is the Feast project id. - pattern: ^[A-Za-z0-9][A-Za-z0-9_]*$ + pattern: ^[A-Za-z0-9][A-Za-z0-9_-]*$ type: string feastProjectDir: description: FeastProjectDir defines how to create the feast project diff --git a/infra/feast-operator/dist/install.yaml b/infra/feast-operator/dist/install.yaml index 6c742822352..0cb55ee9000 100644 --- a/infra/feast-operator/dist/install.yaml +++ b/infra/feast-operator/dist/install.yaml @@ -471,7 +471,7 @@ spec: type: object feastProject: description: FeastProject is the Feast project id. - pattern: ^[A-Za-z0-9][A-Za-z0-9_]*$ + pattern: ^[A-Za-z0-9][A-Za-z0-9_-]*$ type: string feastProjectDir: description: FeastProjectDir defines how to create the feast project @@ -4431,7 +4431,7 @@ spec: type: object feastProject: description: FeastProject is the Feast project id. - pattern: ^[A-Za-z0-9][A-Za-z0-9_]*$ + pattern: ^[A-Za-z0-9][A-Za-z0-9_-]*$ type: string feastProjectDir: description: FeastProjectDir defines how to create the feast project diff --git a/infra/feast-operator/docs/api/markdown/ref.md b/infra/feast-operator/docs/api/markdown/ref.md index 9452d9c838b..68978a08cf0 100644 --- a/infra/feast-operator/docs/api/markdown/ref.md +++ b/infra/feast-operator/docs/api/markdown/ref.md @@ -218,7 +218,7 @@ _Appears in:_ | Field | Description | | --- | --- | -| `feastProject` _string_ | FeastProject is the Feast project id. This can be any alphanumeric string with underscores, but it cannot start with an underscore. Required. | +| `feastProject` _string_ | FeastProject is the Feast project id. This can be any alphanumeric string with underscores and hyphens, but it cannot start with an underscore or hyphen. Required. | | `feastProjectDir` _[FeastProjectDir](#feastprojectdir)_ | | | `services` _[FeatureStoreServices](#featurestoreservices)_ | | | `authz` _[AuthzConfig](#authzconfig)_ | | diff --git a/sdk/python/feast/project.py b/sdk/python/feast/project.py index d9ec45dcc9b..b9cbaddcaad 100644 --- a/sdk/python/feast/project.py +++ b/sdk/python/feast/project.py @@ -117,7 +117,7 @@ def is_valid(self): if not is_valid_name(self.name): raise ValueError( f"Project name, {self.name}, should only have " - f"alphanumerical values and underscores but not start with an underscore." + f"alphanumerical values, underscores, and hyphens but not start with an underscore or hyphen." ) @classmethod diff --git a/sdk/python/feast/repo_config.py b/sdk/python/feast/repo_config.py index 948410c8861..f699aeaa198 100644 --- a/sdk/python/feast/repo_config.py +++ b/sdk/python/feast/repo_config.py @@ -485,7 +485,7 @@ def _validate_project_name(cls, v: str) -> str: if not is_valid_name(v): raise ValueError( f"Project name, {v}, should only have " - f"alphanumerical values and underscores but not start with an underscore." + f"alphanumerical values, underscores, and hyphens but not start with an underscore or hyphen." ) return v diff --git a/sdk/python/feast/repo_operations.py b/sdk/python/feast/repo_operations.py index c5837b0cdb1..72d892e5a0a 100644 --- a/sdk/python/feast/repo_operations.py +++ b/sdk/python/feast/repo_operations.py @@ -396,7 +396,7 @@ def apply_total(repo_config: RepoConfig, repo_path: Path, skip_source_validation if not is_valid_name(project.name): print( f"{project.name} is not valid. Project name should only have " - f"alphanumerical values and underscores but not start with an underscore." + f"alphanumerical values, underscores, and hyphens but not start with an underscore or hyphen." ) sys.exit(1) # TODO: When we support multiple projects in a single repo, we should filter repo contents by project. Currently there is no way to associate Feast objects to project. @@ -445,7 +445,7 @@ def init_repo(repo_name: str, template: str): if not is_valid_name(repo_name): raise BadParameter( - message="Name should be alphanumeric values and underscores but not start with an underscore", + message="Name should be alphanumeric values, underscores, and hyphens but not start with an underscore or hyphen", param_hint="PROJECT_DIRECTORY", ) repo_path = Path(os.path.join(Path.cwd(), repo_name)) @@ -506,8 +506,10 @@ def init_repo(repo_name: str, template: str): def is_valid_name(name: str) -> bool: - """A name should be alphanumeric values and underscores but not start with an underscore""" - return not name.startswith("_") and re.compile(r"\W+").search(name) is None + """A name should be alphanumeric values, underscores, and hyphens but not start with an underscore""" + return ( + not name.startswith(("_", "-")) and re.compile(r"[^\w-]+").search(name) is None + ) def generate_project_name() -> str: diff --git a/sdk/python/tests/unit/infra/scaffolding/test_repo_config.py b/sdk/python/tests/unit/infra/scaffolding/test_repo_config.py index bd6283e2d7b..c825bfd41b7 100644 --- a/sdk/python/tests/unit/infra/scaffolding/test_repo_config.py +++ b/sdk/python/tests/unit/infra/scaffolding/test_repo_config.py @@ -169,12 +169,12 @@ def test_invalid_project_name(): _test_config( dedent( """ - project: foo-1 + project: -foo registry: "registry.db" provider: local """ ), - expect_error="alphanumerical values ", + expect_error="alphanumerical values, underscores, and hyphens ", ) _test_config( @@ -185,7 +185,7 @@ def test_invalid_project_name(): provider: local """ ), - expect_error="alphanumerical values ", + expect_error="alphanumerical values, underscores, and hyphens ", ) diff --git a/sdk/python/tests/unit/test_repo_operations_validate_feast_project_name.py b/sdk/python/tests/unit/test_repo_operations_validate_feast_project_name.py index 33d1d5307d6..f404b600477 100644 --- a/sdk/python/tests/unit/test_repo_operations_validate_feast_project_name.py +++ b/sdk/python/tests/unit/test_repo_operations_validate_feast_project_name.py @@ -11,9 +11,9 @@ def test_is_valid_name(): ("invalid_name_", True), ("12345678901234567", True), ("too_long_name_123", True), + ("hyphen-name", True), # Invalid project name cases ("_invalidName", False), - ("invalid-Name", False), ("invalid name", False), ("invalid@name", False), ("invalid$name", False),