File Type
This document describes the design of the file resource type for managing files and directories.
Overview
The file resource manages files and directories with three aspects:
- Existence: Whether the file/directory exists or is absent
- Content: The contents of a file (from inline content or source file)
- Attributes: Owner, group, and permissions
Provider Interface
File providers must implement the FileProvider interface:
Method Responsibilities
| Method | Responsibility |
|---|---|
Status | Query current file state (existence, type, content hash, attributes) |
Store | Create or update a file with content and attributes |
SetAttributes | Update owner, group and mode on an existing file without changing its content |
CreateDirectory | Create a directory with attributes |
Remove | Remove a file or directory; honors force for non-empty directories |
Status Response
The Status method returns a FileState containing:
The Ensure field in CommonResourceState is set to:
presentif a regular file existsdirectoryif a directory existsabsentif the path does not exist
Available Providers
| Provider | Platform | Documentation |
|---|---|---|
posix | Unix/Linux | Posix |
Ensure States
| Value | Description |
|---|---|
present | Path must be a regular file with specified content |
absent | Path must not exist (see Removal Behavior) |
directory | Path must be a directory |
Content Sources
Files can receive content from two mutually exclusive sources:
| Property | Description |
|---|---|
contents | Inline string content (template-resolved) |
source | Path to local file to copy from |
When using source, the path is relative to the manifest’s working directory if one is set.
Attribute-only Management
A file resource that omits both content and source manages only owner, group and mode. The file’s contents are left untouched. This is useful when another resource (typically an exec or package) produces the file and CCM is responsible for enforcing its permissions.
Rules:
- If the file exists, only owner, group and mode are adjusted. Content is never read or written.
- If the file does not exist, an empty file is created with the requested owner, group and mode. This matches the behavior of Puppet’s
file { ensure => present }with nocontentorsource. - If the path exists as a directory, the apply fails. Use
ensure: directoryto manage directories. - Symlinks are rejected by the posix provider’s
SetAttributesimplementation to avoid silently mutating the target through the link.
To create an explicit empty file rather than enter attribute-only mode, set content: "". An omitted content: and content: null are equivalent and both mean “do not manage content”.
content and source remain mutually exclusive. Setting both is rejected at validation time.
Required Properties
Unlike some resources, file resources require explicit attributes:
| Property | Required | Description |
|---|---|---|
owner | Yes, except absent | Username or numeric UID that owns the file |
group | Yes, except absent | Group name or numeric GID that owns the file |
mode | Yes, except absent | Permissions in octal notation |
This prevents accidental creation of files with default or inherited permissions.
When ensure: absent, owner, group, and mode are optional. They describe a desired on-disk state and are not consulted during removal. Manifests that only ever remove a path may omit them.
A purely-numeric value for owner or group is always interpreted as a UID or GID respectively, without consulting /etc/passwd or /etc/group. This matches the semantics of chown(1) for numeric arguments and allows the resource to be applied on systems where the target account exists only by ID (containers, mounted volumes from other hosts, namespaced filesystems).
Removal Behavior
The force property controls how ensure: absent handles directories.
| Property | Default | Description |
|---|---|---|
force | false | When true, allow ensure: absent to remove non-empty directories |
Rules:
forceis only valid whenensure: absent. Combining it with any other ensure value is rejected at validation time.force: truecannot be used withname: /. All other paths are allowed; CCM does not maintain a blocklist of system directories.- For regular files and symlinks,
forcehas no effect; both forms call into the same removal path. - If a directory is a symlink, only the symlink itself is removed. The target directory is left untouched. See the posix provider for details.
- Without
force, attempting to remove a non-empty directory fails with a hint thatforce: trueis required. Onceforceis removed from the manifest, future applies will fail again if the directory is repopulated. That is intentional: the manifest must opt in to destructive behavior every time it is applied.
Apply Logic
Idempotency
The file resource checks multiple attributes for idempotency:
State Checks (in order)
- Ensure match: Current type matches desired (
present/absent/directory) - Content match: SHA256 checksum of contents matches (for
ensure: present, skipped in attribute-only mode) - Owner match: Current owner matches desired, comparing by numeric UID when either side is a numeric value or resolves to one
- Group match: Current group matches desired, comparing by numeric GID when either side is a numeric value or resolves to one
- Mode match: Current permissions match desired
Decision Table
| Desired | Current State | Action |
|---|---|---|
absent | absent | None |
absent | present (file or symlink) | Remove |
absent | empty directory | Remove |
absent | non-empty directory + force: false | Error: directory is not empty |
absent | non-empty directory + force: true | Remove recursively |
directory | directory + matching attrs | None |
directory | absent/present | CreateDirectory |
directory | directory + wrong attrs | CreateDirectory (updates attrs) |
present (content set) | present + matching all | None |
present (content set) | absent | Store |
present (content set) | present + wrong content | Store |
present (content set) | present + wrong attrs | Store |
present (attrs-only) | present + matching attrs | None |
present (attrs-only) | present + wrong attrs | SetAttributes |
present (attrs-only) | absent | Store (creates empty file with attrs) |
present (any mode) | directory | Error: path exists as a directory |
Content Comparison
Content is compared using SHA256 checksums:
| Source | Checksum Method |
|---|---|
contents property | Sha256HashBytes([]byte(contents)) |
source property | Sha256HashFile(adjustedPath) |
| Existing file | Sha256HashFile(filePath) |
Mode Validation
File modes are validated during resource creation:
Valid Formats:
"0644"- Standard octal"644"- Without leading zero"0o755"- With0oprefix"0O700"- With0Oprefix
Validation Rules:
- Must be valid octal number (digits 0-7)
- Must be β€
0777(no setuid/setgid/sticky via mode)
Path Validation
File paths must be:
- Absolute (start with
/) - Clean (no
.or..components,filepath.Clean(path) == path)
Working Directory
When a manifest has a working directory (e.g., extracted from an archive), the source property is resolved relative to it:
This allows manifests bundled with their source files to use relative paths.
Noop Mode
In noop mode, the file type:
- Queries current state normally
- Computes content checksums
- Logs what actions would be taken
- Sets appropriate
NoopMessage:- “Would have created the file”
- “Would have created an empty file with requested attributes” (attribute-only mode, file absent)
- “Would have updated attributes” (attribute-only mode, attribute drift)
- “Would have created directory”
- “Would have removed the file” (regular file or symlink)
- “Would have removed the directory” (directory,
forcenot set) - “Would have recursively removed the directory” (directory,
force: true)
- Reports
Changed: trueif changes would occur - Does not call provider Store/CreateDirectory methods
- Does not remove files
Desired State Validation
After applying changes (in non-noop mode), the type verifies the file reached the desired state by calling Status() again and checking all attributes match. If validation fails, ErrDesiredStateFailed is returned.