pyinfra-dev / pyinfra-dev/pyinfra

files.link operation fails for hard link

Open
#945 0 comments 1 reaction 0 assignees View on GitHub

Nobody has claimed this yet.

bug
Dominant language
Python
Stars
6k
Forks
548
Avg merge
7d 17h
Merged PRs (30d)
13

Description

Describe the bug

The files.link() operation checks if the path already exists is a symlink, but fails if the path is hardlinked.

To Reproduce

With the following deploy script "test-link.py":

from pyinfra.operations import files

files.file('/tmp/foo')
files.link('/tmp/foo-symlink', target='/tmp/foo')
files.link('/tmp/foo-hardlink', target='/tmp/foo', symbolic=False)

On the first run (clipped for brevity):

$ pyinfra @local test-link.py
...
--> Beginning operation run...
--> Starting operation: Files/File (/tmp/foo)
    [@local] Success

--> Starting operation: Files/Link (/tmp/foo-symlink, target=/tmp/foo)
    [@local] Success

--> Starting operation: Files/Link (/tmp/foo-hardlink, target=/tmp/foo, symbolic=False)
    [@local] Success

On subsequent runs:

$ pyinfra @local test-link.py 
...
--> Preparing Operations...
    Loading: test-link.py
--> pyinfra error: /tmp/foo-hardlink exists and is not a link

Resultant generated output of initial deploy:

$ ls -li /tmp/foo*
673685 -rw-r--r-- 2 dt dt 0 Jan 23 18:21 /tmp/foo
673685 -rw-r--r-- 2 dt dt 0 Jan 23 18:21 /tmp/foo-hardlink
673686 lrwxrwxrwx 1 dt dt 8 Jan 23 18:21 /tmp/foo-symlink -> /tmp/foo

(note that the inode number of foo and foo-hardlink are the same, and the link count is two).

Expected behavior

The files.link() operation skips the hardlink creation as the path and target already exists, and are the same.

I started coding a fix for this, but it involves extending the amount of information returned by the file.File fact (adding inode, device id and nlink count to the stats, which would then be compared between path and target), but was unsure if this is the direction that Nick would want to go in.

Meta

  • Include output of pyinfra --support.
--> Support information:

    If you are having issues with pyinfra or wish to make feature requests, please
    check out the GitHub issues at https://github.com/Fizzadar/pyinfra/issues .
    When adding an issue, be sure to include the following:

    System: Linux
      Platform: Linux-5.10.0-20-amd64-x86_64-with-glibc2.31
      Release: 5.10.0-20-amd64
      Machine: x86_64
    pyinfra: v2.6.1
    Executable: /home/dt/Work/pyinfra/pyinfra-git/venv/bin/pyinfra
    Python: 3.9.2 (CPython, GCC 10.2.1 20210110)
  • How was pyinfra installed (source/pip)?
    • v2.6.1: git+venv+setup
    • v2.6: pip+venv (issue occurs on both)

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start by inspecting the files.link operation and the file.File fact, using test-link.py to reproduce the first and subsequent runs. Check how existing symlinks are detected and how file metadata is exposed. Done means rerunning the script succeeds without error and an existing hard link to the target is left unchanged.

Written by the indexing model from the issue text.

Assessment

Tech stack
python
Domain
devops, infrastructure
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
38/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.