Archive Type

This document describes the design of the archive resource type for downloading and extracting archives.

Overview

The archive resource manages remote archives with three phases:

  • Download: Fetch archive from a URL to local filesystem
  • Extract: Unpack archive contents to a target directory
  • Cleanup: Optionally remove the archive file after extraction

These phases are conditional based on current state and configuration.

Provider Interface

Archive providers must implement the ArchiveProvider interface:

type ArchiveProvider interface {
    model.Provider

    Download(ctx context.Context, properties *model.ArchiveResourceProperties, log model.Logger) error
    Extract(ctx context.Context, properties *model.ArchiveResourceProperties, log model.Logger) error
    Status(ctx context.Context, properties *model.ArchiveResourceProperties) (*model.ArchiveState, error)
}

Method Responsibilities

MethodResponsibility
StatusQuery archive file existence, checksum, attributes, and creates file
DownloadFetch archive from URL, verify checksum, set ownership
ExtractUnpack archive contents to extract parent directory

Status Response

The Status method returns an ArchiveState containing:

type ArchiveState struct {
    CommonResourceState
    Metadata *ArchiveMetadata
}

type ArchiveMetadata struct {
    Name          string    // Archive file path
    Checksum      string    // SHA256 hash of archive
    ArchiveExists bool      // Whether archive file exists
    CreatesExists bool      // Whether creates marker file exists
    Owner         string    // Archive file owner
    Group         string    // Archive file group
    MTime         time.Time // Modification time
    Size          int64     // File size in bytes
    Provider      string    // Provider name (e.g., "http")
}

The Ensure field in CommonResourceState is set to:

  • present if the archive file exists
  • absent if the archive file does not exist

Available Providers

ProviderSourceDocumentation
httpHTTP/HTTPS URLsHTTP

Ensure States

ValueDescription
presentArchive must be downloaded (and optionally extracted)
absentArchive file must not exist

Supported Archive Formats

ExtensionDescription
.tar.gz, .tgzGzip-compressed tar archive
.tarUncompressed tar archive
.zipZIP archive

The URL and local file name must have matching archive type extensions.

Apply Logic

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ Get current state via Status()          β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                  β”‚
                  β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ Is current state desired state?         β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
              Yes β”‚         No
                  β–Ό         β”‚
          β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”     β”‚
          β”‚ No change β”‚     β”‚
          β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜     β”‚
                            β–Ό
              β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
              β”‚ What is desired ensure? β”‚
              β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                            β”‚
            β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
            β”‚ absent                        β”‚ present
            β–Ό                               β–Ό
    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”             β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
    β”‚ Remove archiveβ”‚             β”‚ Download needed?    β”‚
    β”‚ file          β”‚             β”‚ (checksum mismatch  β”‚
    β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜             β”‚  or file missing)   β”‚
                                  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                                        Yes β”‚         No
                                            β–Ό         β”‚
                                    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”     β”‚
                                    β”‚ Download  β”‚     β”‚
                                    β””β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”˜     β”‚
                                          β”‚           β”‚
                                          β–Ό           β–Ό
                                  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
                                  β”‚ Extract needed?         β”‚
                                  β”‚ (extract_parent set AND β”‚
                                  β”‚  (download occurred OR  β”‚
                                  β”‚   creates file missing))β”‚
                                  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                                        Yes β”‚         No
                                            β–Ό         β”‚
                                    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”     β”‚
                                    β”‚ Extract   β”‚     β”‚
                                    β””β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”˜     β”‚
                                          β”‚           β”‚
                                          β–Ό           β–Ό
                                  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
                                  β”‚ Cleanup enabled?        β”‚
                                  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                                        Yes β”‚         No
                                            β–Ό         β–Ό
                                    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”
                                    β”‚ Remove    β”‚ β”‚ Done  β”‚
                                    β”‚ archive   β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”˜
                                    β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Idempotency

The archive resource uses multiple checks for idempotency:

State Checks (in order)

  1. Ensure absent: Archive file must not exist
  2. Creates file: If creates is set, the marker file must exist
  3. Archive existence: If cleanup: false, archive must exist
  4. Owner/Group: Archive file attributes must match
  5. Checksum: If specified, archive checksum must match

Decision Table

ConditionStable?
ensure: absent + archive missingYes
ensure: absent + archive existsNo (remove)
creates file existsYes (skip all)
creates file missingNo (extract needed)
cleanup: false + archive missingNo (download needed)
Archive checksum mismatchNo (re-download needed)
Archive owner/group mismatchNo (re-download needed)

Creates Property

The creates property provides idempotency for extraction:

- archive:
    - /tmp/app.tar.gz:
        url: https://example.com/app.tar.gz
        extract_parent: /opt/app
        creates: /opt/app/bin/app
        owner: root
        group: root

Behavior:

  • If /opt/app/bin/app exists, skip download and extraction
  • Useful when extracted files indicate successful prior extraction
  • Prevents re-extraction on every run

Cleanup Property

The cleanup property removes the archive after extraction:

- archive:
    - /tmp/app.tar.gz:
        url: https://example.com/app.tar.gz
        extract_parent: /opt/app
        creates: /opt/app/bin/app
        cleanup: true
        owner: root
        group: root

Requirements:

  • extract_parent must be set (cleanup only makes sense with extraction)
  • creates must be set to track extraction state

Behavior:

  • After successful extraction, remove the archive file
  • On subsequent runs, creates file prevents re-download

Checksum Verification

When checksum is specified:

- archive:
    - /tmp/app.tar.gz:
        url: https://example.com/app.tar.gz
        checksum: "a1b2c3d4..."
        owner: root
        group: root

Behavior:

  • Downloaded file is verified against SHA256 checksum
  • Existing file checksum is compared to detect changes
  • Checksum mismatch triggers re-download
  • Download fails if fetched content doesn’t match

Authentication

Archives support two authentication methods:

Basic Authentication

- archive:
    - /tmp/app.tar.gz:
        url: https://private.example.com/app.tar.gz
        username: deploy
        password: "{{ lookup('data.password') }}"
        owner: root
        group: root

Custom Headers

- archive:
    - /tmp/app.tar.gz:
        url: https://api.example.com/releases/app.tar.gz
        headers:
          Authorization: "Bearer {{ lookup('data.token') }}"
        owner: root
        group: root

Required Properties

PropertyRequiredDescription
urlYesSource URL for download
ownerYesUsername that owns the archive file
groupYesGroup that owns the archive file

URL Validation

URLs are validated during resource creation:

  • Must be valid URL format
  • Scheme must be http or https
  • Path must end with supported archive extension
  • Extension must match the name property extension

Noop Mode

In noop mode, the archive type:

  1. Queries current state normally
  2. Computes what actions would be taken
  3. Sets appropriate NoopMessage:
    • “Would have downloaded”
    • “Would have extracted”
    • “Would have cleaned up”
    • “Would have removed”
  4. Reports Changed: true if changes would occur
  5. Does not call provider Download/Extract methods
  6. Does not remove files

Multiple actions are joined with “. " (e.g., “Would have downloaded. Would have extracted”).

Desired State Validation

After applying changes (in non-noop mode), the type verifies the archive reached the desired state by calling Status() again and checking all conditions. If validation fails, ErrDesiredStateFailed is returned.