Skip to content

Commit

Permalink
meta: disallow snap channel in default_provider (#4096)
Browse files Browse the repository at this point in the history
Add extra validations for default_provider.
  • Loading branch information
syu-w authored Apr 19, 2023
1 parent 945e5ba commit 603e149
Show file tree
Hide file tree
Showing 6 changed files with 90 additions and 0 deletions.
10 changes: 10 additions & 0 deletions snapcraft/meta/snap_yaml.py
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,16 @@ def _validate_target_not_empty(cls, val):
raise ValueError("value cannot be empty")
return val

@validator("default_provider")
@classmethod
def _validate_default_provider(cls, default_provider):
if default_provider and "/" in default_provider:
raise ValueError(
"Specifying a snap channel in 'default_provider' is not supported: "
f"{default_provider}"
)
return default_provider

@property
def provider(self) -> Optional[str]:
"""Return the default content provider name."""
Expand Down
19 changes: 19 additions & 0 deletions snapcraft/projects.py
Original file line number Diff line number Diff line change
Expand Up @@ -374,6 +374,16 @@ class ContentPlug(ProjectModel):
target: str
default_provider: Optional[str]

@pydantic.validator("default_provider")
@classmethod
def _validate_default_provider(cls, default_provider):
if default_provider and "/" in default_provider:
raise ValueError(
"Specifying a snap channel in 'default_provider' is not supported: "
f"{default_provider}"
)
return default_provider


MANDATORY_ADOPTABLE_FIELDS = ("version", "summary", "description")

Expand Down Expand Up @@ -441,9 +451,18 @@ def _validate_plugs(cls, plugs):
raise ValueError(
f"ContentPlug '{plug_name}' must have a 'target' parameter."
)

if isinstance(plug, list):
raise ValueError(f"Plug '{plug_name}' cannot be a list.")

if isinstance(plug, dict) and plug.get("default-provider"):
default_provider: str = plug.get("default-provider", "")
if "/" in default_provider:
raise ValueError(
"Specifying a snap channel in 'default_provider' is not supported: "
f"{default_provider}"
)

if plug is None:
empty_plugs.append(plug_name)

Expand Down
9 changes: 9 additions & 0 deletions snapcraft_legacy/internal/meta/plugs.py
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,15 @@ def validate(self) -> None:
message="`target` is required for content slot",
)

if self._default_provider and "/" in self._default_provider:
raise PlugValidationError(
plug_name=self.plug_name,
message=(
"Specifying a snap channel in 'default_provider' is not supported: "
f"{self._default_provider}"
)
)

@classmethod
def from_dict(cls, *, plug_dict: Dict[str, str], plug_name: str) -> "ContentPlug":
interface = plug_dict.get("interface")
Expand Down
17 changes: 17 additions & 0 deletions tests/legacy/unit/meta/test_plugs.py
Original file line number Diff line number Diff line change
Expand Up @@ -127,3 +127,20 @@ def test_basic_from_dict_no_default(self):
plug = ContentPlug.from_dict(plug_dict=plug_dict, plug_name=plug_name)

self.assertThat(plug.provider, Is(None))

def test_basic_default_provider_with_channel(self):
plug_dict = OrderedDict(
{
"interface": "content",
"content": "content",
"target": "target",
"default-provider": "gtk-common-themes:gtk-3-themes/edge",
}
)
plug_name = "plug-test"

plug = ContentPlug.from_dict(
plug_dict=plug_dict,
plug_name=plug_name,
)
self.assertRaises(errors.PlugValidationError, plug.validate)
17 changes: 17 additions & 0 deletions tests/unit/meta/test_snap_yaml.py
Original file line number Diff line number Diff line change
Expand Up @@ -699,6 +699,23 @@ def test_content_plug_provider():
assert plug.provider == "gtk-common-themes"


def test_content_plug_provider_with_channel():
plug_dict = {
"interface": "content",
"content": "foo",
"target": "target",
"default-provider": "gtk-common-themes:gtk-3-themes/edge",
}

error = (
"Specifying a snap channel in 'default_provider' is not supported: "
"gtk-common-themes:gtk-3-themes/edge"
)

with pytest.raises(pydantic.ValidationError, match=error):
ContentPlug.unmarshal(plug_dict)


def test_get_content_plugs():
yaml_data = textwrap.dedent(
"""\
Expand Down
18 changes: 18 additions & 0 deletions tests/unit/test_projects.py
Original file line number Diff line number Diff line change
Expand Up @@ -501,6 +501,24 @@ def test_project_get_content_snaps(self, project_yaml_data):
project = Project.unmarshal(project_yaml_data(plugs=content_plug_data))
assert project.get_content_snaps() == ["test-provider"]

def test_project_default_provider_with_channel(self, project_yaml_data):
content_plug_data = {
"content-interface": {
"interface": "content",
"target": "test-target",
"content": "test-content",
"default-provider": "test-provider/edge",
}
}

error = (
"Specifying a snap channel in 'default_provider' is not supported: "
"test-provider/edge"
)

with pytest.raises(errors.ProjectValidationError, match=error):
Project.unmarshal(project_yaml_data(plugs=content_plug_data))

@pytest.mark.parametrize("decl_type", ["symlink", "bind", "bind-file", "type"])
def test_project_layout(self, decl_type, project_yaml_data):
project = Project.unmarshal(
Expand Down

0 comments on commit 603e149

Please sign in to comment.