The easiest way to avoid problems with symlinks in file resources in Puppet is to avoid managing symlinks with Puppet. However, some of your Puppet managed applications might require symlinks. This article includes examples of common issues with symlinks in file resources with Puppet and how to avoid them: unexpected file copies, broken symlinks, and symlinks that point to other symlinks.
Version and installation information
PE version: Any
OS: All POSIX operating systems, and Windows Vista/Server 2008 and newer if the Puppet agent's user account has the Create Symbolic Links privilege.
Solution
Puppet's file resources provide a flexible way to manage files and directories, and can also manage symbolic links (also called symlinks or soft links) on filesystems that support them. However, file resources designed to manage symlinks can behave in unexpected or unintuitive ways.
Relying on Puppet to handle complex filesystem structures with many symlinks, or symlinks with complex relationships to each other causes problems. To prevent issues, deploy symlinks simply. Create and write tests for your modules using the Puppet Development Kit (PDK). The PDK documentation includes resources for writing and running unit tests that can catch unexpected behavior before you deploy code.
For more information about the file resource attribute behaviors discussed in this article, see the Additional resources listed at the end of the article.
Common issues
Use the following examples to learn how to resolve issues with symlinks.
Assumptions
In the following examples, assume that the following exist on the agent:
/tmp/real_file # A file
/tmp/real_dir/ # A directory
/tmp/link_file # A symlink to real_file
And that the following exist on the the primary server (called the master in older versions of Puppet Enterprise):
/etc/puppetlabs/code/environments/production/modules/ntp/files/foo_real_file # A file
/etc/puppetlabs/code/environments/production/modules/ntp/files/foo_link_file # A symlink to foo_real_file
Unexpected file copy
To create a symlink to an existing file, you write the following file resource:
file { '/tmp/a_file':
ensure => link,
links => manage,
source => 'file:///tmp/real_file',
}
Expected behavior: Puppet creates a symbolic link at /tmp/a_file
that points to /tmp/real_file
.
Actual behavior: Puppet copies /tmp/real_file
to /tmp/a_file
.
Why?
-
The
ensure => link
attribute and value require thetarget
attribute to be set. -
The
source
attribute andensure => link
apply to files and links respectively, and they are mutually exclusive. If you provide asource
, Puppet assumes you provided a it because you want to manage files, not links, soensure => link
is ignored. -
The behavior of the
links
attribute changes depending on how it is being used. When performing file operations, it determines only how Puppet handles links it encounters, but it behaves differently when the result of a file resource is a copy operation. -
Because Puppet ignores
ensure
in this case, andlinks
has no relevance to the operation, Puppet copiessource
as the resource title (or namevar, if one is provided).
Fixing it
To create a file symlink, use the target
attribute:
file { '/tmp/a_file':
ensure => link,
target => '/tmp/real_file',
}
Note: target
is mutually exclusive with source
and content
.
Broken symlink
To create a symlink to a file served by a Puppet module (files with puppet://
URIs), you write the following file resource:
file { '/tmp/a_file':
ensure => link,
links => manage,
source => 'puppet:///modules/ntp/module_file',
}
Expected behavior: Puppet creates a symbolic link on the agent at /tmp/a_file
to the location of a_file
in the ntp
Puppet module.
Actual behavior: Puppet creates a symbolic link on the agent at /tmp/a_file
that points to the path of a_file
's location on the the primary server, resulting in a broken symlink.
Why?
Without explicit information about the target file and path, Puppet agent interprets the puppet://
URI resource as a directory of symlinks and the puppet://
file's location on the primary server as the path. It attempts to create that recursive directory of symlinks, even though the local target path might not exist.
Fixing it
To create a symlink to a file created from a puppet://
URI, first use a file
resource to create the target file where you want it on the agent, and then use a separate file
resource to create the symlink to that local path:
file { '/tmp/module_file`:
ensure => file,
source => 'puppet:///modules/ntp/module_file',
before => File['/tmp/a_file'],
}
file { '/tmp/a_file':
ensure => link,
target => '/tmp/module_file',
}
Unexpected double symlink
To copy a file designated by the target of a symlink (or to create an implicitly defined symlink with the same target as another symlink), you write the following file resource:
file { '/tmp/a_file':
ensure => '/tmp/link_file',
links => follow,
}
Expected behavior:
You expect one of the following two things:
-
By declaring
links => follow
, you expect Puppet to follow the symlink at/tmp/link_file
to/tmp/real_file
, and then make a copy ofreal_file
to the title/namevar value,/tmp/a_file
without asource
value. -
This type of
file
resource definition results in an implicitly defined symlink, and you expect a symlink to be created at/tmp/a_file
which points to/tmp/real_file
, the target of/tmp/link_file
.
Actual behavior: Puppet creates a symlink at /tmp/a_file
which points to /tmp/link_file
, resulting in a symlink that points to a symlink which points to a file.
Why?
-
Providing a path as the value of
ensure
is implicitly equivalent to specifyingensure => link
, and atarget
attribute that uses theensure
value's path as its value.Note: The implicit
ensure => <PATH>
symlink creation behavior was deprecated in Puppet 4.3.0 but remains present in Puppet 6. -
When
links => follow
is set, Puppet follows the symlink to its target only when the file resource results in a file being copied—not when creating a symlink. -
Puppet can't create symlinks that use another symlink's target as their own.
Fixing it
You cannot rely on symlink targets as a source of information for file
resource paths because Puppet can't create a symlink that uses another symlink's target as its own. Instead, define common paths using variables and Hiera data, describe how and where to create symlinks using simple file
resources, and create or manage symlinks using Puppet only when absolutely necessary.
When copying a file whose path is designated by a symlink target, describe the resource as explicitly as possible:
# Copy the file designated by the target of the symlink at /tmp/link_file to /tmp/a_file
file { '/tmp/a_file':
ensure => file,
links => follow,
source => '/tmp/link_file',
}
Additional resources
For more information on the file resource attribute behaviors discussed in this article, see the file
resource documentation, including:
- The
ensure
attribute - The
target
attribute - The
source
attribute - The
links
attribute
How can we improve this article?
0 comments
Please sign in to leave a comment.
Related articles