Skip to content

Commit

Permalink
Disallow troublesome characters in environment names (#40)
Browse files Browse the repository at this point in the history
* Mark failed environments as failed
  • Loading branch information
sersorrel authored Jan 31, 2024
1 parent ea9186f commit 366e148
Show file tree
Hide file tree
Showing 3 changed files with 59 additions and 8 deletions.
4 changes: 4 additions & 0 deletions softpack_core/artifacts.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,13 +53,15 @@ class State(Enum):

ready = 'ready'
queued = 'queued'
failed = 'failed'


class Artifacts:
"""Artifacts repo access class."""

environments_root = "environments"
environments_file = "softpack.yml"
builder_out = "builder.out"
module_file = "module"
readme_file = "README.md"
built_by_softpack_file = ".built_by_softpack"
Expand Down Expand Up @@ -131,6 +133,8 @@ def spec(self) -> Box:

if Artifacts.module_file in self.obj:
info["state"] = State.ready
elif Artifacts.builder_out in self.obj:
info["state"] = State.failed
else:
info["state"] = State.queued

Expand Down
22 changes: 16 additions & 6 deletions softpack_core/schemas/environment.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
"""

import io
import re
from dataclasses import dataclass
from pathlib import Path
from traceback import format_exception_only
Expand Down Expand Up @@ -146,15 +147,24 @@ class EnvironmentInput:
description: str
packages: list[PackageInput]

def validate(cls) -> Union[None, InvalidInputError]:
"""Validate that all values have been supplied.
def validate(self) -> Union[None, InvalidInputError]:
"""Validate all values.
Checks all values have been supplied.
Checks that name consists only of alphanumerics, dash, and underscore.
Returns:
None if good, or InvalidInputError if not all values supplied.
"""
if any(len(value) == 0 for value in vars(cls).values()):
if any(len(value) == 0 for value in vars(self).values()):
return InvalidInputError(message="all fields must be filled in")

if not re.fullmatch(r"^[a-zA-Z0-9_-]+$", self.name):
return InvalidInputError(
message="name must only contain alphanumerics, "
"dash, and underscore"
)

return None

@classmethod
Expand Down Expand Up @@ -239,9 +249,9 @@ def create(cls, env: EnvironmentInput) -> CreateResponse: # type: ignore
Returns:
A message confirming the success or failure of the operation.
"""
result = env.validate()
if result is not None:
return result
input_err = env.validate()
if input_err is not None:
return input_err

name = env.name
version = 1
Expand Down
41 changes: 39 additions & 2 deletions tests/integration/test_environment.py
Original file line number Diff line number Diff line change
Expand Up @@ -93,12 +93,34 @@ def test_create(httpx_post, testable_env_input: EnvironmentInput) -> None:
)
assert file_in_remote(path)

orig_name = testable_env_input.name

def test_create_name_empty_disallowed(httpx_post, testable_env_input):
testable_env_input.name = ""
result = Environment.create(testable_env_input)
assert isinstance(result, InvalidInputError)

testable_env_input.name = orig_name

def test_create_name_spaces_disallowed(httpx_post, testable_env_input):
testable_env_input.name = "names cannot have spaces"
result = Environment.create(testable_env_input)
assert isinstance(result, InvalidInputError)


def test_create_name_slashes_disallowed(httpx_post, testable_env_input):
testable_env_input.name = "names/cannot/have/slashes"
result = Environment.create(testable_env_input)
assert isinstance(result, InvalidInputError)


def test_create_name_dashes_and_number_first_allowed(
httpx_post, testable_env_input
):
testable_env_input.name = "7-zip_piz-7"
result = Environment.create(testable_env_input)
assert isinstance(result, CreateEnvironmentSuccess)


def test_create_path_invalid_disallowed(httpx_post, testable_env_input):
testable_env_input.path = "invalid/path"
result = Environment.create(testable_env_input)
assert isinstance(result, InvalidInputError)
Expand Down Expand Up @@ -225,6 +247,21 @@ async def test_states(httpx_post, testable_env_input):
assert env.type == Artifacts.built_by_softpack
assert env.state == State.queued

upload = UploadFile(
filename=Artifacts.builder_out, file=io.BytesIO(b"some output")
)

result = await Environment.write_artifact(
file=upload,
folder_path=f"{testable_env_input.path}/{testable_env_input.name}-1",
file_name=upload.filename,
)
assert isinstance(result, WriteArtifactSuccess)

env = get_env_from_iter(testable_env_input.name + "-1")
assert env is not None
assert env.state == State.failed

upload = UploadFile(
filename=Artifacts.module_file, file=io.BytesIO(b"#%Module")
)
Expand Down

0 comments on commit 366e148

Please sign in to comment.