예제 #1
0
def test_build_generics_symlink_directory_outside(tmp_path, emitter):
    """Ignores (with warning) a symlink pointing a dir outside projects dir."""
    project_dir = tmp_path / "test-project"
    project_dir.mkdir()

    metadata = project_dir / CHARM_METADATA
    metadata.write_text("name: crazycharm")
    build_dir = project_dir / BUILD_DIRNAME
    build_dir.mkdir()
    entrypoint = project_dir / "crazycharm.py"
    entrypoint.touch()

    outside_project = tmp_path / "dangerous"
    outside_project.mkdir()
    the_symlink = project_dir / "external-dir"
    the_symlink.symlink_to(outside_project)

    builder = CharmBuilder(
        charmdir=project_dir,
        builddir=build_dir,
        entrypoint=entrypoint,
    )
    builder.handle_generic_paths()

    assert not (build_dir / "external-dir").exists()
    expected = "Ignoring symlink because targets outside the project: 'external-dir'"
    emitter.assert_trace(expected)
예제 #2
0
def test_build_generics_ignored_file(tmp_path, emitter):
    """Don't include ignored filed."""
    build_dir = tmp_path / BUILD_DIRNAME
    build_dir.mkdir()
    metadata = tmp_path / CHARM_METADATA
    metadata.write_text("name: crazycharm")

    # create two files (and the needed entrypoint)
    file1 = tmp_path / "file1.txt"
    file1.touch()
    file2 = tmp_path / "file2.txt"
    file2.touch()
    entrypoint = tmp_path / "crazycharm.py"
    entrypoint.touch()

    builder = CharmBuilder(
        charmdir=tmp_path,
        builddir=build_dir,
        entrypoint=entrypoint,
    )

    # set it up to ignore file 2 and make it work
    builder.ignore_rules.extend_patterns(["file2.*"])
    builder.handle_generic_paths()

    assert (build_dir / "file1.txt").exists()
    assert not (build_dir / "file2.txt").exists()

    expected = "Ignoring file because of rules: 'file2.txt'"
    emitter.assert_trace(expected)
예제 #3
0
def test_build_generics_different_filetype(tmp_path, emitter, monkeypatch):
    """Ignores whatever is not a regular file, symlink or dir."""
    # change into the tmp path and do everything locally, because otherwise the socket path
    # will be too long for mac os
    monkeypatch.chdir(tmp_path)

    metadata = tmp_path / CHARM_METADATA
    metadata.write_text("name: crazycharm")
    build_dir = pathlib.Path(BUILD_DIRNAME)
    build_dir.mkdir()
    entrypoint = pathlib.Path("crazycharm.py")
    entrypoint.touch()

    # create a socket
    sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
    sock.bind("test-socket")

    builder = CharmBuilder(
        charmdir=tmp_path,
        builddir=build_dir,
        entrypoint=tmp_path / entrypoint,
    )
    builder.handle_generic_paths()

    assert not (build_dir / "test-socket").exists()
    expected = "Ignoring file because of type: 'test-socket'"
    emitter.assert_trace(expected)
예제 #4
0
def test_build_generics_symlink_deep(tmp_path):
    """Correctly re-links a symlink across deep dirs."""
    build_dir = tmp_path / BUILD_DIRNAME
    build_dir.mkdir()

    metadata = tmp_path / CHARM_METADATA
    metadata.write_text("name: crazycharm")
    entrypoint = tmp_path / "crazycharm.py"
    entrypoint.touch()

    dir1 = tmp_path / "dir1"
    dir1.mkdir()
    dir2 = tmp_path / "dir2"
    dir2.mkdir()
    original_target = dir1 / "file.real"
    original_target.touch()
    the_symlink = dir2 / "file.link"
    the_symlink.symlink_to(original_target)

    builder = CharmBuilder(
        charmdir=tmp_path,
        builddir=build_dir,
        entrypoint=entrypoint,
    )
    builder.handle_generic_paths()

    built_symlink = build_dir / "dir2" / "file.link"
    assert built_symlink.is_symlink()
    assert built_symlink.resolve() == build_dir / "dir1" / "file.real"
    real_link = os.readlink(str(built_symlink))
    assert real_link == "../dir1/file.real"
예제 #5
0
def test_build_generics_symlink_dir(tmp_path):
    """Respects a symlinked dir."""
    build_dir = tmp_path / BUILD_DIRNAME
    build_dir.mkdir()

    metadata = tmp_path / CHARM_METADATA
    metadata.write_text("name: crazycharm")
    entrypoint = tmp_path / "crazycharm.py"
    entrypoint.touch()
    somedir = tmp_path / "somedir"
    somedir.mkdir()
    somefile = somedir / "sanity check"
    somefile.touch()
    the_symlink = tmp_path / "thelink"
    the_symlink.symlink_to(somedir)

    builder = CharmBuilder(
        charmdir=tmp_path,
        builddir=build_dir,
        entrypoint=entrypoint,
    )
    builder.handle_generic_paths()

    built_symlink = build_dir / "thelink"
    assert built_symlink.is_symlink()
    assert built_symlink.resolve() == build_dir / "somedir"
    real_link = os.readlink(str(built_symlink))
    assert real_link == "somedir"

    # as a sanity check, the file inside the linked dir should exist
    assert (build_dir / "thelink" / "sanity check").exists()
예제 #6
0
def test_build_generics_symlink_file_outside(tmp_path, caplog):
    """Ignores (with warning) a symlink pointing a file outside projects dir."""
    caplog.set_level(logging.WARNING)

    project_dir = tmp_path / "test-project"
    project_dir.mkdir()

    metadata = project_dir / CHARM_METADATA
    metadata.write_text("name: crazycharm")
    build_dir = project_dir / BUILD_DIRNAME
    build_dir.mkdir()
    entrypoint = project_dir / "crazycharm.py"
    entrypoint.touch()

    outside_project = tmp_path / "dangerous.txt"
    outside_project.touch()
    the_symlink = project_dir / "external-file"
    the_symlink.symlink_to(outside_project)

    builder = CharmBuilder(
        charmdir=project_dir,
        builddir=build_dir,
        entrypoint=entrypoint,
    )
    builder.handle_generic_paths()

    assert not (build_dir / "external-file").exists()
    expected = "Ignoring symlink because targets outside the project: 'external-file'"
    assert expected in [rec.message for rec in caplog.records]
예제 #7
0
def test_build_generics_ignored_dir(tmp_path, caplog):
    """Don't include ignored dir."""
    caplog.set_level(logging.DEBUG)
    build_dir = tmp_path / BUILD_DIRNAME
    build_dir.mkdir()
    metadata = tmp_path / CHARM_METADATA
    metadata.write_text("name: crazycharm")

    # create two files (and the needed entrypoint)
    dir1 = tmp_path / "dir1"
    dir1.mkdir()
    dir2 = tmp_path / "dir2"
    dir2.mkdir()
    entrypoint = tmp_path / "crazycharm.py"
    entrypoint.touch()

    builder = CharmBuilder(
        charmdir=tmp_path,
        builddir=build_dir,
        entrypoint=entrypoint,
    )

    # set it up to ignore dir 2 and make it work
    builder.ignore_rules.extend_patterns(["dir2"])
    builder.handle_generic_paths()

    assert (build_dir / "dir1").exists()
    assert not (build_dir / "dir2").exists()

    expected = "Ignoring directory because of rules: 'dir2'"
    assert expected in [rec.message for rec in caplog.records]
예제 #8
0
def test_build_generics_simple_files(tmp_path):
    """Check transferred metadata and simple entrypoint, also return proper linked entrypoint."""
    build_dir = tmp_path / BUILD_DIRNAME
    build_dir.mkdir()

    metadata = tmp_path / CHARM_METADATA
    metadata.write_text("name: crazycharm")

    entrypoint = tmp_path / "crazycharm.py"
    entrypoint.touch()

    builder = CharmBuilder(
        charmdir=tmp_path,
        builddir=build_dir,
        entrypoint=entrypoint,
    )
    linked_entrypoint = builder.handle_generic_paths()

    # check files are there, are files, and are really hard links (so no
    # check for permissions needed)
    built_metadata = build_dir / CHARM_METADATA
    assert built_metadata.is_file()
    assert built_metadata.stat().st_ino == metadata.stat().st_ino

    built_entrypoint = build_dir / "crazycharm.py"
    assert built_entrypoint.is_file()
    assert built_entrypoint.stat().st_ino == entrypoint.stat().st_ino

    assert linked_entrypoint == built_entrypoint
예제 #9
0
def test_build_generics_simple_dir(tmp_path):
    """Check transferred any directory, with proper permissions."""
    build_dir = tmp_path / BUILD_DIRNAME
    build_dir.mkdir()
    entrypoint = tmp_path / "crazycharm.py"
    entrypoint.touch()
    metadata = tmp_path / CHARM_METADATA
    metadata.write_text("name: crazycharm")

    somedir = tmp_path / "somedir"
    somedir.mkdir(mode=0o700)

    builder = CharmBuilder(
        charmdir=tmp_path,
        builddir=build_dir,
        entrypoint=entrypoint,
    )
    builder.handle_generic_paths()

    built_dir = build_dir / "somedir"
    assert built_dir.is_dir()
    assert built_dir.stat().st_mode & 0xFFF == 0o700
예제 #10
0
def test_build_generics_symlink_file(tmp_path):
    """Respects a symlinked file."""
    build_dir = tmp_path / BUILD_DIRNAME
    build_dir.mkdir()

    metadata = tmp_path / CHARM_METADATA
    metadata.write_text("name: crazycharm")
    entrypoint = tmp_path / "crazycharm.py"
    entrypoint.touch()
    the_symlink = tmp_path / "somehook.py"
    the_symlink.symlink_to(entrypoint)

    builder = CharmBuilder(
        charmdir=tmp_path,
        builddir=build_dir,
        entrypoint=entrypoint,
    )
    builder.handle_generic_paths()

    built_symlink = build_dir / "somehook.py"
    assert built_symlink.is_symlink()
    assert built_symlink.resolve() == build_dir / "crazycharm.py"
    real_link = os.readlink(str(built_symlink))
    assert real_link == "crazycharm.py"
예제 #11
0
def _test_build_generics_tree(tmp_path, *, expect_hardlinks):
    build_dir = tmp_path / BUILD_DIRNAME
    build_dir.mkdir()

    # create this structure:
    # ├─ crazycharm.py  (entrypoint)
    # ├─ file1.txt
    # ├─ dir1
    # │  └─ dir3  (ignored!)
    # └─ dir2
    #    ├─ file2.txt
    #    ├─ file3.txt  (ignored!)
    #    ├─ dir4  (ignored!)
    #    │   └─ file4.txt
    #    └─ dir5
    entrypoint = tmp_path / "crazycharm.py"
    entrypoint.touch()
    metadata = tmp_path / CHARM_METADATA
    metadata.write_text("name: crazycharm")
    file1 = tmp_path / "file1.txt"
    file1.touch()
    dir1 = tmp_path / "dir1"
    dir1.mkdir()
    dir3 = dir1 / "dir3"
    dir3.mkdir()
    dir2 = tmp_path / "dir2"
    dir2.mkdir()
    file2 = dir2 / "file2.txt"
    file2.touch()
    file3 = dir2 / "file3.txt"
    file3.touch()
    dir4 = dir2 / "dir4"
    dir4.mkdir()
    file4 = dir4 / "file4.txt"
    file4.touch()
    dir5 = dir2 / "dir5"
    dir5.mkdir()

    builder = CharmBuilder(
        charmdir=tmp_path,
        builddir=build_dir,
        entrypoint=entrypoint,
    )

    # set it up to ignore some stuff and make it work
    builder.ignore_rules.extend_patterns(
        [
            "dir1/dir3",
            "dir2/file3.txt",
            "dir2/dir4",
        ]
    )
    builder.handle_generic_paths()

    assert (build_dir / "crazycharm.py").exists()
    assert (build_dir / "file1.txt").exists()
    assert (build_dir / "dir1").exists()
    assert not (build_dir / "dir1" / "dir3").exists()
    assert (build_dir / "dir2").exists()
    assert (build_dir / "dir2" / "file2.txt").exists()
    assert not (build_dir / "dir2" / "file3.txt").exists()
    assert not (build_dir / "dir2" / "dir4").exists()
    assert (build_dir / "dir2" / "dir5").exists()

    for (p1, p2) in [
        (build_dir / "crazycharm.py", entrypoint),
        (build_dir / "file1.txt", file1),
        (build_dir / "dir2" / "file2.txt", file2),
    ]:
        if expect_hardlinks:
            # they're hard links
            assert p1.samefile(p2)
        else:
            # they're *not* hard links
            assert not p1.samefile(p2)
            # but they're essentially the same
            assert filecmp.cmp(str(p1), str(p2), shallow=False)
            assert p1.stat().st_mode == p2.stat().st_mode
            assert p1.stat().st_size == p2.stat().st_size
            assert p1.stat().st_atime == pytest.approx(p2.stat().st_atime)
            assert p1.stat().st_mtime == pytest.approx(p2.stat().st_mtime)