Register resources per file and split resource docs #31

Merged
benvin merged 1 commits from benvin/modular-registry into main 2026-10-17 15:31:07 +11:00
62 changed files with 977 additions and 437 deletions
+4 -212
View File
@@ -46,219 +46,11 @@ provider "artifactapi" {
| `endpoint` | Yes | ArtifactAPI server endpoint URL |
| `token` | No | Bearer token sent as `Authorization` header; falls back to `ARTIFACTAPI_TOKEN` |
## Resources
## Resources and Data Sources
### Remote Resources
Per-type remote resources manage upstream repository proxies. Each type applies its own mutability classification rules automatically (e.g., Docker classifies tag manifests as mutable and blobs as immutable; Helm classifies `index.yaml` as mutable).
Available resource types:
- `artifactapi_remote_generic`
- `artifactapi_remote_docker`
- `artifactapi_remote_helm`
- `artifactapi_remote_pypi`
- `artifactapi_remote_npm`
- `artifactapi_remote_rpm`
- `artifactapi_remote_deb`
- `artifactapi_remote_alpine`
- `artifactapi_remote_puppet`
- `artifactapi_remote_terraform`
- `artifactapi_remote_goproxy`
- `artifactapi_remote_cargo`
- `artifactapi_remote_gems`
- `artifactapi_remote_github_rpm`
- `artifactapi_remote_github_deb`
- `artifactapi_remote_github_alpine`
#### Common Attributes
| Attribute | Required | Default | Description |
|----------------------|----------|---------|-------------------------------------------------------------------|
| `name` | Yes | | Unique name (forces replacement on change) |
| `base_url` | Yes | | Upstream repository URL |
| `description` | No | `""` | Human-readable description |
| `username` | No | `""` | Upstream auth username (sensitive) |
| `password` | No | `""` | Upstream auth password (sensitive) |
| `immutable_ttl` | No | `0` | TTL in seconds for immutable artifacts (0 = cache forever) |
| `mutable_ttl` | No | `3600` | TTL in seconds for mutable artifacts |
| `check_mutable` | No | `true` | Enable conditional revalidation for mutable artifacts |
| `patterns` | No | | Allowlist of path patterns to proxy (empty = all) |
| `blocklist` | No | | Paths to always deny (checked before patterns) |
| `mutable_patterns` | No | | Override: treat matching paths as mutable |
| `immutable_patterns` | No | | Override: treat matching paths as immutable |
| `quarantine_enabled` | No | `false` | Enable quarantine for new artifacts |
| `quarantine_days` | No | `3` | Days to quarantine new artifacts |
| `stale_on_error` | No | `true` | Serve stale cache when upstream is unreachable |
| `upstream_dial_timeout` | No | `0` | Upstream TCP connect timeout in seconds (0 = server default) |
| `upstream_tls_timeout` | No | `0` | Upstream TLS handshake timeout in seconds (0 = server default) |
| `upstream_response_header_timeout` | No | `0` | Upstream response-header timeout in seconds (0 = server default) |
#### rpm / deb / alpine-specific Attributes
| Attribute | Default | Description |
|-------------------|---------------|-------------------------------------------------------------------------------------------------|
| `mirrorlist` | | Extra upstream mirror base URLs. Requests are load-balanced with failover across `base_url` + `mirrorlist`. |
| `mirror_strategy` | `round_robin` | Mirror load-balancing strategy across `base_url` + `mirrorlist`. One of `round_robin` or `least_conn`. |
Only valid on the `artifactapi_remote_rpm`, `artifactapi_remote_deb`, and
`artifactapi_remote_alpine` resources. Setting either on any other remote type is
rejected at plan time.
```hcl
resource "artifactapi_remote_rpm" "epel" {
name = "epel-9"
base_url = "https://download.example.com/pub/epel/9/Everything/x86_64"
mirrorlist = [
"https://mirror-a.example.net/epel/9/Everything/x86_64",
"https://mirror-b.example.org/epel/9/Everything/x86_64",
]
mirror_strategy = "least_conn"
}
```
#### Docker-specific Attributes
| Attribute | Default | Description |
|--------------------|---------|----------------------------|
| `ban_tags_enabled` | `false` | Enable tag banning |
| `ban_tags` | | List of tags to ban |
#### Terraform / github_rpm / github_deb / github_alpine-specific Attributes
| Attribute | Default | Description |
|-------------------|---------|----------------------------------------------------------|
| `releases_remote` | `""` | Name of a backend remote that serves the download bytes. Terraform uses it for download URL rewriting; `github_rpm`/`github_deb`/`github_alpine` 302-redirect `.rpm`/`.deb`/`.apk` downloads to it. |
#### github_rpm-specific notes
`artifactapi_remote_github_rpm` exposes a GitHub repo's releases as a `dnf`/`yum`
repository without precaching packages. Set `base_url` to the releases API root
(`https://api.github.com/repos/{owner}/{repo}`) and `releases_remote` to a
generic `github.com` remote that streams the `.rpm` bytes. `patterns` filters
which release assets become packages (regex on asset filename). `password` may
hold a token for private repos or higher API rate limits. See
`examples/resources/artifactapi_remote_github_rpm/main.tf`.
#### github_deb-specific notes
`artifactapi_remote_github_deb` is the Debian/apt analog of `github_rpm`: it
exposes a GitHub repo's release `.deb` assets as an `apt` repository, synthesizing
the apt index (`Packages`/`Release`) from release metadata without precaching
packages. Set `base_url` to the releases API root
(`https://api.github.com/repos/{owner}/{repo}`) and `releases_remote` to a
generic `github.com` remote that streams the `.deb` bytes; `.deb` downloads
302-redirect there. `patterns` filters which release assets become packages
(regex on asset filename). `password` may hold a token for private repos or
higher API rate limits. See
`examples/resources/artifactapi_remote_github_deb/main.tf`.
#### github_alpine-specific notes
`artifactapi_remote_github_alpine` is the Alpine/apk analog of `github_rpm`: it
exposes a GitHub repo's release `.apk` assets as an `apk` repository, synthesizing
the apk index (`APKINDEX.tar.gz`) from release metadata without precaching
packages. Set `base_url` to the releases API root
(`https://api.github.com/repos/{owner}/{repo}`) and `releases_remote` to a
generic `github.com` remote that streams the `.apk` bytes; `.apk` downloads
302-redirect there. `patterns` filters which release assets become packages
(regex on asset filename). `password` may hold a token for private repos or
higher API rate limits. See
`examples/resources/artifactapi_remote_github_alpine/main.tf`.
#### Example
```hcl
resource "artifactapi_remote_docker" "dockerhub" {
name = "dockerhub"
base_url = "https://registry-1.docker.io"
immutable_ttl = 0
mutable_ttl = 300
ban_tags_enabled = true
ban_tags = ["latest"]
patterns = [
"^library/postgres",
"^library/redis",
]
}
```
### Local Resources
Local resources manage repositories that ArtifactAPI hosts directly (rather than
proxying an upstream) — each is a real registry for its package type.
Available resource types:
- `artifactapi_local_docker` — a container registry (Docker Registry HTTP API V2, push and pull)
- `artifactapi_local_pypi`
- `artifactapi_local_npm` — an npm registry (`npm publish` and `npm install`)
- `artifactapi_local_rpm`
- `artifactapi_local_deb`
- `artifactapi_local_alpine`
- `artifactapi_local_helm` — a chart repository (index.yaml generated from uploaded charts)
- `artifactapi_local_terraform`
Each takes just `name` (required, forces replacement) and an optional
`description`.
```hcl
resource "artifactapi_local_docker" "internal" {
name = "docker-internal"
description = "Internal container image registry"
}
```
Images push and pull against `<endpoint>/<name>/<image>:<tag>`, e.g.
`docker push artifactapi.example.com/docker-internal/myapp:latest`.
### Virtual Resources
Virtual repositories merge multiple remotes of the same package type into a single endpoint.
```hcl
resource "artifactapi_virtual" "helm" {
name = "helm"
package_type = "helm"
description = "All helm repos merged"
members = [
artifactapi_remote_helm.jetstack.name,
artifactapi_remote_helm.hashicorp_helm.name,
]
}
```
| Attribute | Required | Description |
|----------------|----------|-------------------------------------------|
| `name` | Yes | Unique name (forces replacement on change)|
| `package_type` | Yes | Package type of member remotes |
| `description` | No | Human-readable description |
| `members` | Yes | List of remote names to include |
## Data Sources
### `artifactapi_remote`
Read an existing remote's configuration.
```hcl
data "artifactapi_remote" "dockerhub" {
name = "dockerhub"
}
```
### `artifactapi_virtual`
Read an existing virtual repository's configuration.
```hcl
data "artifactapi_virtual" "helm" {
name = "helm"
}
```
Each resource is documented in [`docs/resources/`](docs/resources/) and each data
source in [`docs/data-sources/`](docs/data-sources/), with a matching example under
[`examples/`](examples/).
## Import
+9
View File
@@ -0,0 +1,9 @@
# `artifactapi_remote` (Data Source)
Read an existing remote's configuration.
```hcl
data "artifactapi_remote" "dockerhub" {
name = "dockerhub"
}
```
+9
View File
@@ -0,0 +1,9 @@
# `artifactapi_virtual` (Data Source)
Read an existing virtual repository's configuration.
```hcl
data "artifactapi_virtual" "helm" {
name = "helm"
}
```
+11
View File
@@ -0,0 +1,11 @@
# `artifactapi_local_alpine`
Local resources manage repositories that ArtifactAPI hosts directly (rather than
proxying an upstream) — each is a real registry for its package type.
Each takes just `name` (required, forces replacement) and an optional
`description`.
## Example
See `examples/resources/artifactapi_local_alpine/main.tf`.
+11
View File
@@ -0,0 +1,11 @@
# `artifactapi_local_deb`
Local resources manage repositories that ArtifactAPI hosts directly (rather than
proxying an upstream) — each is a real registry for its package type.
Each takes just `name` (required, forces replacement) and an optional
`description`.
## Example
See `examples/resources/artifactapi_local_deb/main.tf`.
+21
View File
@@ -0,0 +1,21 @@
# `artifactapi_local_docker`
Local resources manage repositories that ArtifactAPI hosts directly (rather than
proxying an upstream) — each is a real registry for its package type.
A container registry (Docker Registry HTTP API V2, push and pull).
Each takes just `name` (required, forces replacement) and an optional
`description`.
## Example
```hcl
resource "artifactapi_local_docker" "internal" {
name = "docker-internal"
description = "Internal container image registry"
}
```
Images push and pull against `<endpoint>/<name>/<image>:<tag>`, e.g.
`docker push artifactapi.example.com/docker-internal/myapp:latest`.
+11
View File
@@ -0,0 +1,11 @@
# `artifactapi_local_generic`
Local resources manage repositories that ArtifactAPI hosts directly (rather than
proxying an upstream) — each is a real registry for its package type.
Each takes just `name` (required, forces replacement) and an optional
`description`.
## Example
See `examples/resources/artifactapi_local_generic/main.tf`.
+13
View File
@@ -0,0 +1,13 @@
# `artifactapi_local_helm`
Local resources manage repositories that ArtifactAPI hosts directly (rather than
proxying an upstream) — each is a real registry for its package type.
A chart repository (index.yaml generated from uploaded charts).
Each takes just `name` (required, forces replacement) and an optional
`description`.
## Example
See `examples/resources/artifactapi_local_helm/main.tf`.
+13
View File
@@ -0,0 +1,13 @@
# `artifactapi_local_npm`
Local resources manage repositories that ArtifactAPI hosts directly (rather than
proxying an upstream) — each is a real registry for its package type.
An npm registry (`npm publish` and `npm install`).
Each takes just `name` (required, forces replacement) and an optional
`description`.
## Example
See `examples/resources/artifactapi_local_npm/main.tf`.
+11
View File
@@ -0,0 +1,11 @@
# `artifactapi_local_pypi`
Local resources manage repositories that ArtifactAPI hosts directly (rather than
proxying an upstream) — each is a real registry for its package type.
Each takes just `name` (required, forces replacement) and an optional
`description`.
## Example
See `examples/resources/artifactapi_local_pypi/main.tf`.
+11
View File
@@ -0,0 +1,11 @@
# `artifactapi_local_rpm`
Local resources manage repositories that ArtifactAPI hosts directly (rather than
proxying an upstream) — each is a real registry for its package type.
Each takes just `name` (required, forces replacement) and an optional
`description`.
## Example
See `examples/resources/artifactapi_local_rpm/main.tf`.
+11
View File
@@ -0,0 +1,11 @@
# `artifactapi_local_terraform`
Local resources manage repositories that ArtifactAPI hosts directly (rather than
proxying an upstream) — each is a real registry for its package type.
Each takes just `name` (required, forces replacement) and an optional
`description`.
## Example
See `examples/resources/artifactapi_local_terraform/main.tf`.
+53
View File
@@ -0,0 +1,53 @@
# `artifactapi_remote_alpine`
Per-type remote resources manage upstream repository proxies. Each type applies its own mutability classification rules automatically (e.g., Docker classifies tag manifests as mutable and blobs as immutable; Helm classifies `index.yaml` as mutable).
## Common Attributes
| Attribute | Required | Default | Description |
|----------------------|----------|---------|-------------------------------------------------------------------|
| `name` | Yes | | Unique name (forces replacement on change) |
| `base_url` | Yes | | Upstream repository URL |
| `description` | No | `""` | Human-readable description |
| `username` | No | `""` | Upstream auth username (sensitive) |
| `password` | No | `""` | Upstream auth password (sensitive) |
| `immutable_ttl` | No | `0` | TTL in seconds for immutable artifacts (0 = cache forever) |
| `mutable_ttl` | No | `3600` | TTL in seconds for mutable artifacts |
| `check_mutable` | No | `true` | Enable conditional revalidation for mutable artifacts |
| `patterns` | No | | Allowlist of path patterns to proxy (empty = all) |
| `blocklist` | No | | Paths to always deny (checked before patterns) |
| `mutable_patterns` | No | | Override: treat matching paths as mutable |
| `immutable_patterns` | No | | Override: treat matching paths as immutable |
| `quarantine_enabled` | No | `false` | Enable quarantine for new artifacts |
| `quarantine_days` | No | `3` | Days to quarantine new artifacts |
| `stale_on_error` | No | `true` | Serve stale cache when upstream is unreachable |
| `upstream_dial_timeout` | No | `0` | Upstream TCP connect timeout in seconds (0 = server default) |
| `upstream_tls_timeout` | No | `0` | Upstream TLS handshake timeout in seconds (0 = server default) |
| `upstream_response_header_timeout` | No | `0` | Upstream response-header timeout in seconds (0 = server default) |
## rpm / deb / alpine-specific Attributes
| Attribute | Default | Description |
|-------------------|---------------|-------------------------------------------------------------------------------------------------|
| `mirrorlist` | | Extra upstream mirror base URLs. Requests are load-balanced with failover across `base_url` + `mirrorlist`. |
| `mirror_strategy` | `round_robin` | Mirror load-balancing strategy across `base_url` + `mirrorlist`. One of `round_robin` or `least_conn`. |
Only valid on the `artifactapi_remote_rpm`, `artifactapi_remote_deb`, and
`artifactapi_remote_alpine` resources. Setting either on any other remote type is
rejected at plan time.
```hcl
resource "artifactapi_remote_rpm" "epel" {
name = "epel-9"
base_url = "https://download.example.com/pub/epel/9/Everything/x86_64"
mirrorlist = [
"https://mirror-a.example.net/epel/9/Everything/x86_64",
"https://mirror-b.example.org/epel/9/Everything/x86_64",
]
mirror_strategy = "least_conn"
}
```
## Example
See `examples/resources/artifactapi_remote_alpine/main.tf`.
+30
View File
@@ -0,0 +1,30 @@
# `artifactapi_remote_cargo`
Per-type remote resources manage upstream repository proxies. Each type applies its own mutability classification rules automatically (e.g., Docker classifies tag manifests as mutable and blobs as immutable; Helm classifies `index.yaml` as mutable).
## Common Attributes
| Attribute | Required | Default | Description |
|----------------------|----------|---------|-------------------------------------------------------------------|
| `name` | Yes | | Unique name (forces replacement on change) |
| `base_url` | Yes | | Upstream repository URL |
| `description` | No | `""` | Human-readable description |
| `username` | No | `""` | Upstream auth username (sensitive) |
| `password` | No | `""` | Upstream auth password (sensitive) |
| `immutable_ttl` | No | `0` | TTL in seconds for immutable artifacts (0 = cache forever) |
| `mutable_ttl` | No | `3600` | TTL in seconds for mutable artifacts |
| `check_mutable` | No | `true` | Enable conditional revalidation for mutable artifacts |
| `patterns` | No | | Allowlist of path patterns to proxy (empty = all) |
| `blocklist` | No | | Paths to always deny (checked before patterns) |
| `mutable_patterns` | No | | Override: treat matching paths as mutable |
| `immutable_patterns` | No | | Override: treat matching paths as immutable |
| `quarantine_enabled` | No | `false` | Enable quarantine for new artifacts |
| `quarantine_days` | No | `3` | Days to quarantine new artifacts |
| `stale_on_error` | No | `true` | Serve stale cache when upstream is unreachable |
| `upstream_dial_timeout` | No | `0` | Upstream TCP connect timeout in seconds (0 = server default) |
| `upstream_tls_timeout` | No | `0` | Upstream TLS handshake timeout in seconds (0 = server default) |
| `upstream_response_header_timeout` | No | `0` | Upstream response-header timeout in seconds (0 = server default) |
## Example
See `examples/resources/artifactapi_remote_cargo/main.tf`.
+53
View File
@@ -0,0 +1,53 @@
# `artifactapi_remote_deb`
Per-type remote resources manage upstream repository proxies. Each type applies its own mutability classification rules automatically (e.g., Docker classifies tag manifests as mutable and blobs as immutable; Helm classifies `index.yaml` as mutable).
## Common Attributes
| Attribute | Required | Default | Description |
|----------------------|----------|---------|-------------------------------------------------------------------|
| `name` | Yes | | Unique name (forces replacement on change) |
| `base_url` | Yes | | Upstream repository URL |
| `description` | No | `""` | Human-readable description |
| `username` | No | `""` | Upstream auth username (sensitive) |
| `password` | No | `""` | Upstream auth password (sensitive) |
| `immutable_ttl` | No | `0` | TTL in seconds for immutable artifacts (0 = cache forever) |
| `mutable_ttl` | No | `3600` | TTL in seconds for mutable artifacts |
| `check_mutable` | No | `true` | Enable conditional revalidation for mutable artifacts |
| `patterns` | No | | Allowlist of path patterns to proxy (empty = all) |
| `blocklist` | No | | Paths to always deny (checked before patterns) |
| `mutable_patterns` | No | | Override: treat matching paths as mutable |
| `immutable_patterns` | No | | Override: treat matching paths as immutable |
| `quarantine_enabled` | No | `false` | Enable quarantine for new artifacts |
| `quarantine_days` | No | `3` | Days to quarantine new artifacts |
| `stale_on_error` | No | `true` | Serve stale cache when upstream is unreachable |
| `upstream_dial_timeout` | No | `0` | Upstream TCP connect timeout in seconds (0 = server default) |
| `upstream_tls_timeout` | No | `0` | Upstream TLS handshake timeout in seconds (0 = server default) |
| `upstream_response_header_timeout` | No | `0` | Upstream response-header timeout in seconds (0 = server default) |
## rpm / deb / alpine-specific Attributes
| Attribute | Default | Description |
|-------------------|---------------|-------------------------------------------------------------------------------------------------|
| `mirrorlist` | | Extra upstream mirror base URLs. Requests are load-balanced with failover across `base_url` + `mirrorlist`. |
| `mirror_strategy` | `round_robin` | Mirror load-balancing strategy across `base_url` + `mirrorlist`. One of `round_robin` or `least_conn`. |
Only valid on the `artifactapi_remote_rpm`, `artifactapi_remote_deb`, and
`artifactapi_remote_alpine` resources. Setting either on any other remote type is
rejected at plan time.
```hcl
resource "artifactapi_remote_rpm" "epel" {
name = "epel-9"
base_url = "https://download.example.com/pub/epel/9/Everything/x86_64"
mirrorlist = [
"https://mirror-a.example.net/epel/9/Everything/x86_64",
"https://mirror-b.example.org/epel/9/Everything/x86_64",
]
mirror_strategy = "least_conn"
}
```
## Example
See `examples/resources/artifactapi_remote_deb/main.tf`.
+52
View File
@@ -0,0 +1,52 @@
# `artifactapi_remote_docker`
Per-type remote resources manage upstream repository proxies. Each type applies its own mutability classification rules automatically (e.g., Docker classifies tag manifests as mutable and blobs as immutable; Helm classifies `index.yaml` as mutable).
## Common Attributes
| Attribute | Required | Default | Description |
|----------------------|----------|---------|-------------------------------------------------------------------|
| `name` | Yes | | Unique name (forces replacement on change) |
| `base_url` | Yes | | Upstream repository URL |
| `description` | No | `""` | Human-readable description |
| `username` | No | `""` | Upstream auth username (sensitive) |
| `password` | No | `""` | Upstream auth password (sensitive) |
| `immutable_ttl` | No | `0` | TTL in seconds for immutable artifacts (0 = cache forever) |
| `mutable_ttl` | No | `3600` | TTL in seconds for mutable artifacts |
| `check_mutable` | No | `true` | Enable conditional revalidation for mutable artifacts |
| `patterns` | No | | Allowlist of path patterns to proxy (empty = all) |
| `blocklist` | No | | Paths to always deny (checked before patterns) |
| `mutable_patterns` | No | | Override: treat matching paths as mutable |
| `immutable_patterns` | No | | Override: treat matching paths as immutable |
| `quarantine_enabled` | No | `false` | Enable quarantine for new artifacts |
| `quarantine_days` | No | `3` | Days to quarantine new artifacts |
| `stale_on_error` | No | `true` | Serve stale cache when upstream is unreachable |
| `upstream_dial_timeout` | No | `0` | Upstream TCP connect timeout in seconds (0 = server default) |
| `upstream_tls_timeout` | No | `0` | Upstream TLS handshake timeout in seconds (0 = server default) |
| `upstream_response_header_timeout` | No | `0` | Upstream response-header timeout in seconds (0 = server default) |
## Docker-specific Attributes
| Attribute | Default | Description |
|--------------------|---------|----------------------------|
| `ban_tags_enabled` | `false` | Enable tag banning |
| `ban_tags` | | List of tags to ban |
## Example
```hcl
resource "artifactapi_remote_docker" "dockerhub" {
name = "dockerhub"
base_url = "https://registry-1.docker.io"
immutable_ttl = 0
mutable_ttl = 300
ban_tags_enabled = true
ban_tags = ["latest"]
patterns = [
"^library/postgres",
"^library/redis",
]
}
```
+30
View File
@@ -0,0 +1,30 @@
# `artifactapi_remote_gems`
Per-type remote resources manage upstream repository proxies. Each type applies its own mutability classification rules automatically (e.g., Docker classifies tag manifests as mutable and blobs as immutable; Helm classifies `index.yaml` as mutable).
## Common Attributes
| Attribute | Required | Default | Description |
|----------------------|----------|---------|-------------------------------------------------------------------|
| `name` | Yes | | Unique name (forces replacement on change) |
| `base_url` | Yes | | Upstream repository URL |
| `description` | No | `""` | Human-readable description |
| `username` | No | `""` | Upstream auth username (sensitive) |
| `password` | No | `""` | Upstream auth password (sensitive) |
| `immutable_ttl` | No | `0` | TTL in seconds for immutable artifacts (0 = cache forever) |
| `mutable_ttl` | No | `3600` | TTL in seconds for mutable artifacts |
| `check_mutable` | No | `true` | Enable conditional revalidation for mutable artifacts |
| `patterns` | No | | Allowlist of path patterns to proxy (empty = all) |
| `blocklist` | No | | Paths to always deny (checked before patterns) |
| `mutable_patterns` | No | | Override: treat matching paths as mutable |
| `immutable_patterns` | No | | Override: treat matching paths as immutable |
| `quarantine_enabled` | No | `false` | Enable quarantine for new artifacts |
| `quarantine_days` | No | `3` | Days to quarantine new artifacts |
| `stale_on_error` | No | `true` | Serve stale cache when upstream is unreachable |
| `upstream_dial_timeout` | No | `0` | Upstream TCP connect timeout in seconds (0 = server default) |
| `upstream_tls_timeout` | No | `0` | Upstream TLS handshake timeout in seconds (0 = server default) |
| `upstream_response_header_timeout` | No | `0` | Upstream response-header timeout in seconds (0 = server default) |
## Example
See `examples/resources/artifactapi_remote_gems/main.tf`.
+30
View File
@@ -0,0 +1,30 @@
# `artifactapi_remote_generic`
Per-type remote resources manage upstream repository proxies. Each type applies its own mutability classification rules automatically (e.g., Docker classifies tag manifests as mutable and blobs as immutable; Helm classifies `index.yaml` as mutable).
## Common Attributes
| Attribute | Required | Default | Description |
|----------------------|----------|---------|-------------------------------------------------------------------|
| `name` | Yes | | Unique name (forces replacement on change) |
| `base_url` | Yes | | Upstream repository URL |
| `description` | No | `""` | Human-readable description |
| `username` | No | `""` | Upstream auth username (sensitive) |
| `password` | No | `""` | Upstream auth password (sensitive) |
| `immutable_ttl` | No | `0` | TTL in seconds for immutable artifacts (0 = cache forever) |
| `mutable_ttl` | No | `3600` | TTL in seconds for mutable artifacts |
| `check_mutable` | No | `true` | Enable conditional revalidation for mutable artifacts |
| `patterns` | No | | Allowlist of path patterns to proxy (empty = all) |
| `blocklist` | No | | Paths to always deny (checked before patterns) |
| `mutable_patterns` | No | | Override: treat matching paths as mutable |
| `immutable_patterns` | No | | Override: treat matching paths as immutable |
| `quarantine_enabled` | No | `false` | Enable quarantine for new artifacts |
| `quarantine_days` | No | `3` | Days to quarantine new artifacts |
| `stale_on_error` | No | `true` | Serve stale cache when upstream is unreachable |
| `upstream_dial_timeout` | No | `0` | Upstream TCP connect timeout in seconds (0 = server default) |
| `upstream_tls_timeout` | No | `0` | Upstream TLS handshake timeout in seconds (0 = server default) |
| `upstream_response_header_timeout` | No | `0` | Upstream response-header timeout in seconds (0 = server default) |
## Example
See `examples/resources/artifactapi_remote_generic/main.tf`.
+49
View File
@@ -0,0 +1,49 @@
# `artifactapi_remote_github_alpine`
Per-type remote resources manage upstream repository proxies. Each type applies its own mutability classification rules automatically (e.g., Docker classifies tag manifests as mutable and blobs as immutable; Helm classifies `index.yaml` as mutable).
## Common Attributes
| Attribute | Required | Default | Description |
|----------------------|----------|---------|-------------------------------------------------------------------|
| `name` | Yes | | Unique name (forces replacement on change) |
| `base_url` | Yes | | Upstream repository URL |
| `description` | No | `""` | Human-readable description |
| `username` | No | `""` | Upstream auth username (sensitive) |
| `password` | No | `""` | Upstream auth password (sensitive) |
| `immutable_ttl` | No | `0` | TTL in seconds for immutable artifacts (0 = cache forever) |
| `mutable_ttl` | No | `3600` | TTL in seconds for mutable artifacts |
| `check_mutable` | No | `true` | Enable conditional revalidation for mutable artifacts |
| `patterns` | No | | Allowlist of path patterns to proxy (empty = all) |
| `blocklist` | No | | Paths to always deny (checked before patterns) |
| `mutable_patterns` | No | | Override: treat matching paths as mutable |
| `immutable_patterns` | No | | Override: treat matching paths as immutable |
| `quarantine_enabled` | No | `false` | Enable quarantine for new artifacts |
| `quarantine_days` | No | `3` | Days to quarantine new artifacts |
| `stale_on_error` | No | `true` | Serve stale cache when upstream is unreachable |
| `upstream_dial_timeout` | No | `0` | Upstream TCP connect timeout in seconds (0 = server default) |
| `upstream_tls_timeout` | No | `0` | Upstream TLS handshake timeout in seconds (0 = server default) |
| `upstream_response_header_timeout` | No | `0` | Upstream response-header timeout in seconds (0 = server default) |
## Terraform / github_rpm / github_deb / github_alpine-specific Attributes
| Attribute | Default | Description |
|-------------------|---------|----------------------------------------------------------|
| `releases_remote` | `""` | Name of a backend remote that serves the download bytes. Terraform uses it for download URL rewriting; `github_rpm`/`github_deb`/`github_alpine` 302-redirect `.rpm`/`.deb`/`.apk` downloads to it. |
## Notes
`artifactapi_remote_github_alpine` is the Alpine/apk analog of `github_rpm`: it
exposes a GitHub repo's release `.apk` assets as an `apk` repository, synthesizing
the apk index (`APKINDEX.tar.gz`) from release metadata without precaching
packages. Set `base_url` to the releases API root
(`https://api.github.com/repos/{owner}/{repo}`) and `releases_remote` to a
generic `github.com` remote that streams the `.apk` bytes; `.apk` downloads
302-redirect there. `patterns` filters which release assets become packages
(regex on asset filename). `password` may hold a token for private repos or
higher API rate limits. See
`examples/resources/artifactapi_remote_github_alpine/main.tf`.
## Example
See `examples/resources/artifactapi_remote_github_alpine/main.tf`.
+49
View File
@@ -0,0 +1,49 @@
# `artifactapi_remote_github_deb`
Per-type remote resources manage upstream repository proxies. Each type applies its own mutability classification rules automatically (e.g., Docker classifies tag manifests as mutable and blobs as immutable; Helm classifies `index.yaml` as mutable).
## Common Attributes
| Attribute | Required | Default | Description |
|----------------------|----------|---------|-------------------------------------------------------------------|
| `name` | Yes | | Unique name (forces replacement on change) |
| `base_url` | Yes | | Upstream repository URL |
| `description` | No | `""` | Human-readable description |
| `username` | No | `""` | Upstream auth username (sensitive) |
| `password` | No | `""` | Upstream auth password (sensitive) |
| `immutable_ttl` | No | `0` | TTL in seconds for immutable artifacts (0 = cache forever) |
| `mutable_ttl` | No | `3600` | TTL in seconds for mutable artifacts |
| `check_mutable` | No | `true` | Enable conditional revalidation for mutable artifacts |
| `patterns` | No | | Allowlist of path patterns to proxy (empty = all) |
| `blocklist` | No | | Paths to always deny (checked before patterns) |
| `mutable_patterns` | No | | Override: treat matching paths as mutable |
| `immutable_patterns` | No | | Override: treat matching paths as immutable |
| `quarantine_enabled` | No | `false` | Enable quarantine for new artifacts |
| `quarantine_days` | No | `3` | Days to quarantine new artifacts |
| `stale_on_error` | No | `true` | Serve stale cache when upstream is unreachable |
| `upstream_dial_timeout` | No | `0` | Upstream TCP connect timeout in seconds (0 = server default) |
| `upstream_tls_timeout` | No | `0` | Upstream TLS handshake timeout in seconds (0 = server default) |
| `upstream_response_header_timeout` | No | `0` | Upstream response-header timeout in seconds (0 = server default) |
## Terraform / github_rpm / github_deb / github_alpine-specific Attributes
| Attribute | Default | Description |
|-------------------|---------|----------------------------------------------------------|
| `releases_remote` | `""` | Name of a backend remote that serves the download bytes. Terraform uses it for download URL rewriting; `github_rpm`/`github_deb`/`github_alpine` 302-redirect `.rpm`/`.deb`/`.apk` downloads to it. |
## Notes
`artifactapi_remote_github_deb` is the Debian/apt analog of `github_rpm`: it
exposes a GitHub repo's release `.deb` assets as an `apt` repository, synthesizing
the apt index (`Packages`/`Release`) from release metadata without precaching
packages. Set `base_url` to the releases API root
(`https://api.github.com/repos/{owner}/{repo}`) and `releases_remote` to a
generic `github.com` remote that streams the `.deb` bytes; `.deb` downloads
302-redirect there. `patterns` filters which release assets become packages
(regex on asset filename). `password` may hold a token for private repos or
higher API rate limits. See
`examples/resources/artifactapi_remote_github_deb/main.tf`.
## Example
See `examples/resources/artifactapi_remote_github_deb/main.tf`.
+46
View File
@@ -0,0 +1,46 @@
# `artifactapi_remote_github_rpm`
Per-type remote resources manage upstream repository proxies. Each type applies its own mutability classification rules automatically (e.g., Docker classifies tag manifests as mutable and blobs as immutable; Helm classifies `index.yaml` as mutable).
## Common Attributes
| Attribute | Required | Default | Description |
|----------------------|----------|---------|-------------------------------------------------------------------|
| `name` | Yes | | Unique name (forces replacement on change) |
| `base_url` | Yes | | Upstream repository URL |
| `description` | No | `""` | Human-readable description |
| `username` | No | `""` | Upstream auth username (sensitive) |
| `password` | No | `""` | Upstream auth password (sensitive) |
| `immutable_ttl` | No | `0` | TTL in seconds for immutable artifacts (0 = cache forever) |
| `mutable_ttl` | No | `3600` | TTL in seconds for mutable artifacts |
| `check_mutable` | No | `true` | Enable conditional revalidation for mutable artifacts |
| `patterns` | No | | Allowlist of path patterns to proxy (empty = all) |
| `blocklist` | No | | Paths to always deny (checked before patterns) |
| `mutable_patterns` | No | | Override: treat matching paths as mutable |
| `immutable_patterns` | No | | Override: treat matching paths as immutable |
| `quarantine_enabled` | No | `false` | Enable quarantine for new artifacts |
| `quarantine_days` | No | `3` | Days to quarantine new artifacts |
| `stale_on_error` | No | `true` | Serve stale cache when upstream is unreachable |
| `upstream_dial_timeout` | No | `0` | Upstream TCP connect timeout in seconds (0 = server default) |
| `upstream_tls_timeout` | No | `0` | Upstream TLS handshake timeout in seconds (0 = server default) |
| `upstream_response_header_timeout` | No | `0` | Upstream response-header timeout in seconds (0 = server default) |
## Terraform / github_rpm / github_deb / github_alpine-specific Attributes
| Attribute | Default | Description |
|-------------------|---------|----------------------------------------------------------|
| `releases_remote` | `""` | Name of a backend remote that serves the download bytes. Terraform uses it for download URL rewriting; `github_rpm`/`github_deb`/`github_alpine` 302-redirect `.rpm`/`.deb`/`.apk` downloads to it. |
## Notes
`artifactapi_remote_github_rpm` exposes a GitHub repo's releases as a `dnf`/`yum`
repository without precaching packages. Set `base_url` to the releases API root
(`https://api.github.com/repos/{owner}/{repo}`) and `releases_remote` to a
generic `github.com` remote that streams the `.rpm` bytes. `patterns` filters
which release assets become packages (regex on asset filename). `password` may
hold a token for private repos or higher API rate limits. See
`examples/resources/artifactapi_remote_github_rpm/main.tf`.
## Example
See `examples/resources/artifactapi_remote_github_rpm/main.tf`.
+30
View File
@@ -0,0 +1,30 @@
# `artifactapi_remote_goproxy`
Per-type remote resources manage upstream repository proxies. Each type applies its own mutability classification rules automatically (e.g., Docker classifies tag manifests as mutable and blobs as immutable; Helm classifies `index.yaml` as mutable).
## Common Attributes
| Attribute | Required | Default | Description |
|----------------------|----------|---------|-------------------------------------------------------------------|
| `name` | Yes | | Unique name (forces replacement on change) |
| `base_url` | Yes | | Upstream repository URL |
| `description` | No | `""` | Human-readable description |
| `username` | No | `""` | Upstream auth username (sensitive) |
| `password` | No | `""` | Upstream auth password (sensitive) |
| `immutable_ttl` | No | `0` | TTL in seconds for immutable artifacts (0 = cache forever) |
| `mutable_ttl` | No | `3600` | TTL in seconds for mutable artifacts |
| `check_mutable` | No | `true` | Enable conditional revalidation for mutable artifacts |
| `patterns` | No | | Allowlist of path patterns to proxy (empty = all) |
| `blocklist` | No | | Paths to always deny (checked before patterns) |
| `mutable_patterns` | No | | Override: treat matching paths as mutable |
| `immutable_patterns` | No | | Override: treat matching paths as immutable |
| `quarantine_enabled` | No | `false` | Enable quarantine for new artifacts |
| `quarantine_days` | No | `3` | Days to quarantine new artifacts |
| `stale_on_error` | No | `true` | Serve stale cache when upstream is unreachable |
| `upstream_dial_timeout` | No | `0` | Upstream TCP connect timeout in seconds (0 = server default) |
| `upstream_tls_timeout` | No | `0` | Upstream TLS handshake timeout in seconds (0 = server default) |
| `upstream_response_header_timeout` | No | `0` | Upstream response-header timeout in seconds (0 = server default) |
## Example
See `examples/resources/artifactapi_remote_goproxy/main.tf`.
+30
View File
@@ -0,0 +1,30 @@
# `artifactapi_remote_helm`
Per-type remote resources manage upstream repository proxies. Each type applies its own mutability classification rules automatically (e.g., Docker classifies tag manifests as mutable and blobs as immutable; Helm classifies `index.yaml` as mutable).
## Common Attributes
| Attribute | Required | Default | Description |
|----------------------|----------|---------|-------------------------------------------------------------------|
| `name` | Yes | | Unique name (forces replacement on change) |
| `base_url` | Yes | | Upstream repository URL |
| `description` | No | `""` | Human-readable description |
| `username` | No | `""` | Upstream auth username (sensitive) |
| `password` | No | `""` | Upstream auth password (sensitive) |
| `immutable_ttl` | No | `0` | TTL in seconds for immutable artifacts (0 = cache forever) |
| `mutable_ttl` | No | `3600` | TTL in seconds for mutable artifacts |
| `check_mutable` | No | `true` | Enable conditional revalidation for mutable artifacts |
| `patterns` | No | | Allowlist of path patterns to proxy (empty = all) |
| `blocklist` | No | | Paths to always deny (checked before patterns) |
| `mutable_patterns` | No | | Override: treat matching paths as mutable |
| `immutable_patterns` | No | | Override: treat matching paths as immutable |
| `quarantine_enabled` | No | `false` | Enable quarantine for new artifacts |
| `quarantine_days` | No | `3` | Days to quarantine new artifacts |
| `stale_on_error` | No | `true` | Serve stale cache when upstream is unreachable |
| `upstream_dial_timeout` | No | `0` | Upstream TCP connect timeout in seconds (0 = server default) |
| `upstream_tls_timeout` | No | `0` | Upstream TLS handshake timeout in seconds (0 = server default) |
| `upstream_response_header_timeout` | No | `0` | Upstream response-header timeout in seconds (0 = server default) |
## Example
See `examples/resources/artifactapi_remote_helm/main.tf`.
+30
View File
@@ -0,0 +1,30 @@
# `artifactapi_remote_npm`
Per-type remote resources manage upstream repository proxies. Each type applies its own mutability classification rules automatically (e.g., Docker classifies tag manifests as mutable and blobs as immutable; Helm classifies `index.yaml` as mutable).
## Common Attributes
| Attribute | Required | Default | Description |
|----------------------|----------|---------|-------------------------------------------------------------------|
| `name` | Yes | | Unique name (forces replacement on change) |
| `base_url` | Yes | | Upstream repository URL |
| `description` | No | `""` | Human-readable description |
| `username` | No | `""` | Upstream auth username (sensitive) |
| `password` | No | `""` | Upstream auth password (sensitive) |
| `immutable_ttl` | No | `0` | TTL in seconds for immutable artifacts (0 = cache forever) |
| `mutable_ttl` | No | `3600` | TTL in seconds for mutable artifacts |
| `check_mutable` | No | `true` | Enable conditional revalidation for mutable artifacts |
| `patterns` | No | | Allowlist of path patterns to proxy (empty = all) |
| `blocklist` | No | | Paths to always deny (checked before patterns) |
| `mutable_patterns` | No | | Override: treat matching paths as mutable |
| `immutable_patterns` | No | | Override: treat matching paths as immutable |
| `quarantine_enabled` | No | `false` | Enable quarantine for new artifacts |
| `quarantine_days` | No | `3` | Days to quarantine new artifacts |
| `stale_on_error` | No | `true` | Serve stale cache when upstream is unreachable |
| `upstream_dial_timeout` | No | `0` | Upstream TCP connect timeout in seconds (0 = server default) |
| `upstream_tls_timeout` | No | `0` | Upstream TLS handshake timeout in seconds (0 = server default) |
| `upstream_response_header_timeout` | No | `0` | Upstream response-header timeout in seconds (0 = server default) |
## Example
See `examples/resources/artifactapi_remote_npm/main.tf`.
+30
View File
@@ -0,0 +1,30 @@
# `artifactapi_remote_puppet`
Per-type remote resources manage upstream repository proxies. Each type applies its own mutability classification rules automatically (e.g., Docker classifies tag manifests as mutable and blobs as immutable; Helm classifies `index.yaml` as mutable).
## Common Attributes
| Attribute | Required | Default | Description |
|----------------------|----------|---------|-------------------------------------------------------------------|
| `name` | Yes | | Unique name (forces replacement on change) |
| `base_url` | Yes | | Upstream repository URL |
| `description` | No | `""` | Human-readable description |
| `username` | No | `""` | Upstream auth username (sensitive) |
| `password` | No | `""` | Upstream auth password (sensitive) |
| `immutable_ttl` | No | `0` | TTL in seconds for immutable artifacts (0 = cache forever) |
| `mutable_ttl` | No | `3600` | TTL in seconds for mutable artifacts |
| `check_mutable` | No | `true` | Enable conditional revalidation for mutable artifacts |
| `patterns` | No | | Allowlist of path patterns to proxy (empty = all) |
| `blocklist` | No | | Paths to always deny (checked before patterns) |
| `mutable_patterns` | No | | Override: treat matching paths as mutable |
| `immutable_patterns` | No | | Override: treat matching paths as immutable |
| `quarantine_enabled` | No | `false` | Enable quarantine for new artifacts |
| `quarantine_days` | No | `3` | Days to quarantine new artifacts |
| `stale_on_error` | No | `true` | Serve stale cache when upstream is unreachable |
| `upstream_dial_timeout` | No | `0` | Upstream TCP connect timeout in seconds (0 = server default) |
| `upstream_tls_timeout` | No | `0` | Upstream TLS handshake timeout in seconds (0 = server default) |
| `upstream_response_header_timeout` | No | `0` | Upstream response-header timeout in seconds (0 = server default) |
## Example
See `examples/resources/artifactapi_remote_puppet/main.tf`.
+30
View File
@@ -0,0 +1,30 @@
# `artifactapi_remote_pypi`
Per-type remote resources manage upstream repository proxies. Each type applies its own mutability classification rules automatically (e.g., Docker classifies tag manifests as mutable and blobs as immutable; Helm classifies `index.yaml` as mutable).
## Common Attributes
| Attribute | Required | Default | Description |
|----------------------|----------|---------|-------------------------------------------------------------------|
| `name` | Yes | | Unique name (forces replacement on change) |
| `base_url` | Yes | | Upstream repository URL |
| `description` | No | `""` | Human-readable description |
| `username` | No | `""` | Upstream auth username (sensitive) |
| `password` | No | `""` | Upstream auth password (sensitive) |
| `immutable_ttl` | No | `0` | TTL in seconds for immutable artifacts (0 = cache forever) |
| `mutable_ttl` | No | `3600` | TTL in seconds for mutable artifacts |
| `check_mutable` | No | `true` | Enable conditional revalidation for mutable artifacts |
| `patterns` | No | | Allowlist of path patterns to proxy (empty = all) |
| `blocklist` | No | | Paths to always deny (checked before patterns) |
| `mutable_patterns` | No | | Override: treat matching paths as mutable |
| `immutable_patterns` | No | | Override: treat matching paths as immutable |
| `quarantine_enabled` | No | `false` | Enable quarantine for new artifacts |
| `quarantine_days` | No | `3` | Days to quarantine new artifacts |
| `stale_on_error` | No | `true` | Serve stale cache when upstream is unreachable |
| `upstream_dial_timeout` | No | `0` | Upstream TCP connect timeout in seconds (0 = server default) |
| `upstream_tls_timeout` | No | `0` | Upstream TLS handshake timeout in seconds (0 = server default) |
| `upstream_response_header_timeout` | No | `0` | Upstream response-header timeout in seconds (0 = server default) |
## Example
See `examples/resources/artifactapi_remote_pypi/main.tf`.
+53
View File
@@ -0,0 +1,53 @@
# `artifactapi_remote_rpm`
Per-type remote resources manage upstream repository proxies. Each type applies its own mutability classification rules automatically (e.g., Docker classifies tag manifests as mutable and blobs as immutable; Helm classifies `index.yaml` as mutable).
## Common Attributes
| Attribute | Required | Default | Description |
|----------------------|----------|---------|-------------------------------------------------------------------|
| `name` | Yes | | Unique name (forces replacement on change) |
| `base_url` | Yes | | Upstream repository URL |
| `description` | No | `""` | Human-readable description |
| `username` | No | `""` | Upstream auth username (sensitive) |
| `password` | No | `""` | Upstream auth password (sensitive) |
| `immutable_ttl` | No | `0` | TTL in seconds for immutable artifacts (0 = cache forever) |
| `mutable_ttl` | No | `3600` | TTL in seconds for mutable artifacts |
| `check_mutable` | No | `true` | Enable conditional revalidation for mutable artifacts |
| `patterns` | No | | Allowlist of path patterns to proxy (empty = all) |
| `blocklist` | No | | Paths to always deny (checked before patterns) |
| `mutable_patterns` | No | | Override: treat matching paths as mutable |
| `immutable_patterns` | No | | Override: treat matching paths as immutable |
| `quarantine_enabled` | No | `false` | Enable quarantine for new artifacts |
| `quarantine_days` | No | `3` | Days to quarantine new artifacts |
| `stale_on_error` | No | `true` | Serve stale cache when upstream is unreachable |
| `upstream_dial_timeout` | No | `0` | Upstream TCP connect timeout in seconds (0 = server default) |
| `upstream_tls_timeout` | No | `0` | Upstream TLS handshake timeout in seconds (0 = server default) |
| `upstream_response_header_timeout` | No | `0` | Upstream response-header timeout in seconds (0 = server default) |
## rpm / deb / alpine-specific Attributes
| Attribute | Default | Description |
|-------------------|---------------|-------------------------------------------------------------------------------------------------|
| `mirrorlist` | | Extra upstream mirror base URLs. Requests are load-balanced with failover across `base_url` + `mirrorlist`. |
| `mirror_strategy` | `round_robin` | Mirror load-balancing strategy across `base_url` + `mirrorlist`. One of `round_robin` or `least_conn`. |
Only valid on the `artifactapi_remote_rpm`, `artifactapi_remote_deb`, and
`artifactapi_remote_alpine` resources. Setting either on any other remote type is
rejected at plan time.
```hcl
resource "artifactapi_remote_rpm" "epel" {
name = "epel-9"
base_url = "https://download.example.com/pub/epel/9/Everything/x86_64"
mirrorlist = [
"https://mirror-a.example.net/epel/9/Everything/x86_64",
"https://mirror-b.example.org/epel/9/Everything/x86_64",
]
mirror_strategy = "least_conn"
}
```
## Example
See `examples/resources/artifactapi_remote_rpm/main.tf`.
+36
View File
@@ -0,0 +1,36 @@
# `artifactapi_remote_terraform`
Per-type remote resources manage upstream repository proxies. Each type applies its own mutability classification rules automatically (e.g., Docker classifies tag manifests as mutable and blobs as immutable; Helm classifies `index.yaml` as mutable).
## Common Attributes
| Attribute | Required | Default | Description |
|----------------------|----------|---------|-------------------------------------------------------------------|
| `name` | Yes | | Unique name (forces replacement on change) |
| `base_url` | Yes | | Upstream repository URL |
| `description` | No | `""` | Human-readable description |
| `username` | No | `""` | Upstream auth username (sensitive) |
| `password` | No | `""` | Upstream auth password (sensitive) |
| `immutable_ttl` | No | `0` | TTL in seconds for immutable artifacts (0 = cache forever) |
| `mutable_ttl` | No | `3600` | TTL in seconds for mutable artifacts |
| `check_mutable` | No | `true` | Enable conditional revalidation for mutable artifacts |
| `patterns` | No | | Allowlist of path patterns to proxy (empty = all) |
| `blocklist` | No | | Paths to always deny (checked before patterns) |
| `mutable_patterns` | No | | Override: treat matching paths as mutable |
| `immutable_patterns` | No | | Override: treat matching paths as immutable |
| `quarantine_enabled` | No | `false` | Enable quarantine for new artifacts |
| `quarantine_days` | No | `3` | Days to quarantine new artifacts |
| `stale_on_error` | No | `true` | Serve stale cache when upstream is unreachable |
| `upstream_dial_timeout` | No | `0` | Upstream TCP connect timeout in seconds (0 = server default) |
| `upstream_tls_timeout` | No | `0` | Upstream TLS handshake timeout in seconds (0 = server default) |
| `upstream_response_header_timeout` | No | `0` | Upstream response-header timeout in seconds (0 = server default) |
## Terraform / github_rpm / github_deb / github_alpine-specific Attributes
| Attribute | Default | Description |
|-------------------|---------|----------------------------------------------------------|
| `releases_remote` | `""` | Name of a backend remote that serves the download bytes. Terraform uses it for download URL rewriting; `github_rpm`/`github_deb`/`github_alpine` 302-redirect `.rpm`/`.deb`/`.apk` downloads to it. |
## Example
See `examples/resources/artifactapi_remote_terraform/main.tf`.
+23
View File
@@ -0,0 +1,23 @@
# `artifactapi_virtual`
Virtual repositories merge multiple remotes of the same package type into a single endpoint.
```hcl
resource "artifactapi_virtual" "helm" {
name = "helm"
package_type = "helm"
description = "All helm repos merged"
members = [
artifactapi_remote_helm.jetstack.name,
artifactapi_remote_helm.hashicorp_helm.name,
]
}
```
| Attribute | Required | Description |
|----------------|----------|-------------------------------------------|
| `name` | Yes | Unique name (forces replacement on change)|
| `package_type` | Yes | Package type of member remotes |
| `description` | No | Human-readable description |
| `members` | Yes | List of remote names to include |
+2
View File
@@ -15,6 +15,8 @@ type remoteDataSource struct {
client *apiClient
}
func init() { registerDataSource(NewRemoteDataSource) }
func NewRemoteDataSource() datasource.DataSource {
return &remoteDataSource{}
}
+2
View File
@@ -15,6 +15,8 @@ type virtualDataSource struct {
client *apiClient
}
func init() { registerDataSource(NewVirtualDataSource) }
func NewVirtualDataSource() datasource.DataSource {
return &virtualDataSource{}
}
+2 -32
View File
@@ -67,39 +67,9 @@ func (p *ArtifactAPIProvider) Configure(ctx context.Context, req provider.Config
}
func (p *ArtifactAPIProvider) Resources(_ context.Context) []func() resource.Resource {
return []func() resource.Resource{
newRemoteResource("generic"),
newRemoteResource("docker"),
newRemoteResource("helm"),
newRemoteResource("pypi"),
newRemoteResource("npm"),
newRemoteResource("rpm"),
newRemoteResource("deb"),
newRemoteResource("alpine"),
newRemoteResource("puppet"),
newRemoteResource("terraform"),
newRemoteResource("goproxy"),
newRemoteResource("cargo"),
newRemoteResource("gems"),
newRemoteResource("github_rpm"),
newRemoteResource("github_deb"),
newRemoteResource("github_alpine"),
NewVirtualResource,
NewLocalTerraformResource,
NewLocalPyPIResource,
NewLocalNPMResource,
NewLocalRPMResource,
NewLocalDebResource,
NewLocalAlpineResource,
NewLocalHelmResource,
NewLocalDockerResource,
NewLocalGenericResource,
}
return sortedByTypeName(resourceRegistry, resourceTypeName)
}
func (p *ArtifactAPIProvider) DataSources(_ context.Context) []func() datasource.DataSource {
return []func() datasource.DataSource{
NewRemoteDataSource,
NewVirtualDataSource,
}
return sortedByTypeName(dataSourceRegistry, dataSourceTypeName)
}
+49 -96
View File
@@ -2,11 +2,13 @@ package provider
import (
"context"
"os"
"path/filepath"
"slices"
"strings"
"testing"
"github.com/hashicorp/terraform-plugin-framework/datasource"
fwprovider "github.com/hashicorp/terraform-plugin-framework/provider"
"github.com/hashicorp/terraform-plugin-framework/resource"
)
func TestNew_ReturnsFactory(t *testing.T) {
@@ -62,114 +64,65 @@ func TestProvider_Schema(t *testing.T) {
}
}
func TestProvider_Resources(t *testing.T) {
func TestProvider_Resources_Invariants(t *testing.T) {
p := &ArtifactAPIProvider{version: "1.0.0"}
resources := p.Resources(context.Background())
// 16 remote resource types + 1 virtual + 9 local = 26
expectedCount := 26
if len(resources) != expectedCount {
t.Fatalf("expected %d resources, got %d", expectedCount, len(resources))
}
// Verify each factory produces a valid resource
for i, factory := range resources {
r := factory()
if r == nil {
t.Errorf("resource factory %d returned nil", i)
}
var names []string
for _, f := range p.Resources(context.Background()) {
names = append(names, resourceTypeName(f))
}
checkRegistered(t, names, "resources")
}
func TestProvider_Resources_ContainsExpectedTypes(t *testing.T) {
func TestProvider_DataSources_Invariants(t *testing.T) {
p := &ArtifactAPIProvider{version: "1.0.0"}
resources := p.Resources(context.Background())
// Collect type names by calling Metadata on each
typeNames := make(map[string]bool)
for _, factory := range resources {
r := factory()
req := resource.MetadataRequest{ProviderTypeName: "artifactapi"}
var resp resource.MetadataResponse
r.Metadata(context.Background(), req, &resp)
typeNames[resp.TypeName] = true
}
expected := []string{
"artifactapi_remote_generic",
"artifactapi_remote_docker",
"artifactapi_remote_helm",
"artifactapi_remote_pypi",
"artifactapi_remote_npm",
"artifactapi_remote_rpm",
"artifactapi_remote_deb",
"artifactapi_remote_alpine",
"artifactapi_remote_puppet",
"artifactapi_remote_terraform",
"artifactapi_remote_goproxy",
"artifactapi_remote_cargo",
"artifactapi_remote_gems",
"artifactapi_remote_github_rpm",
"artifactapi_remote_github_deb",
"artifactapi_remote_github_alpine",
"artifactapi_virtual",
"artifactapi_local_terraform",
"artifactapi_local_pypi",
"artifactapi_local_npm",
"artifactapi_local_rpm",
"artifactapi_local_deb",
"artifactapi_local_alpine",
"artifactapi_local_helm",
"artifactapi_local_docker",
"artifactapi_local_generic",
}
for _, name := range expected {
if !typeNames[name] {
t.Errorf("missing expected resource type: %s", name)
}
var names []string
for _, f := range p.DataSources(context.Background()) {
names = append(names, dataSourceTypeName(f))
}
checkRegistered(t, names, "data-sources")
}
func TestProvider_DataSources(t *testing.T) {
p := &ArtifactAPIProvider{version: "1.0.0"}
dataSources := p.DataSources(context.Background())
expectedCount := 2
if len(dataSources) != expectedCount {
t.Fatalf("expected %d data sources, got %d", expectedCount, len(dataSources))
func checkRegistered(t *testing.T, names []string, kind string) {
t.Helper()
if len(names) == 0 {
t.Fatalf("no %s registered", kind)
}
// Verify each factory produces a valid data source
for i, factory := range dataSources {
ds := factory()
if ds == nil {
t.Errorf("data source factory %d returned nil", i)
if !slices.IsSorted(names) {
t.Errorf("%s not sorted by type name: %v", kind, names)
}
seen := map[string]bool{}
for _, n := range names {
if !strings.HasPrefix(n, "artifactapi_") {
t.Errorf("%s: type name %q lacks artifactapi_ prefix", kind, n)
}
if seen[n] {
t.Errorf("%s: duplicate type name %q", kind, n)
}
seen[n] = true
if _, err := os.Stat(filepath.Join("../../examples", kind, n, "main.tf")); err != nil {
t.Errorf("%s: %s has no example: %v", kind, n, err)
}
if _, err := os.Stat(filepath.Join("../../docs", kind, strings.TrimPrefix(n, "artifactapi_")+".md")); err != nil {
t.Errorf("%s: %s has no docs page: %v", kind, n, err)
}
}
}
func TestProvider_DataSources_ContainsExpectedTypes(t *testing.T) {
p := &ArtifactAPIProvider{version: "1.0.0"}
dataSources := p.DataSources(context.Background())
typeNames := make(map[string]bool)
for _, factory := range dataSources {
ds := factory()
req := datasource.MetadataRequest{ProviderTypeName: "artifactapi"}
var resp datasource.MetadataResponse
ds.Metadata(context.Background(), req, &resp)
typeNames[resp.TypeName] = true
dirs, err := os.ReadDir(filepath.Join("../../examples", kind))
if err != nil {
t.Fatal(err)
}
expected := []string{
"artifactapi_remote",
"artifactapi_virtual",
for _, d := range dirs {
if !seen[d.Name()] {
t.Errorf("examples/%s/%s does not match a registered type", kind, d.Name())
}
}
for _, name := range expected {
if !typeNames[name] {
t.Errorf("missing expected data source type: %s", name)
docs, err := filepath.Glob(filepath.Join("../../docs", kind, "*.md"))
if err != nil {
t.Fatal(err)
}
for _, d := range docs {
if n := "artifactapi_" + strings.TrimSuffix(filepath.Base(d), ".md"); !seen[n] {
t.Errorf("docs/%s/%s does not match a registered type", kind, filepath.Base(d))
}
}
}
+41
View File
@@ -0,0 +1,41 @@
package provider
import (
"context"
"slices"
"strings"
"github.com/hashicorp/terraform-plugin-framework/datasource"
"github.com/hashicorp/terraform-plugin-framework/resource"
)
var (
resourceRegistry []func() resource.Resource
dataSourceRegistry []func() datasource.DataSource
)
func registerResource(f func() resource.Resource) {
resourceRegistry = append(resourceRegistry, f)
}
func registerDataSource(f func() datasource.DataSource) {
dataSourceRegistry = append(dataSourceRegistry, f)
}
func resourceTypeName(f func() resource.Resource) string {
var resp resource.MetadataResponse
f().Metadata(context.Background(), resource.MetadataRequest{ProviderTypeName: "artifactapi"}, &resp)
return resp.TypeName
}
func dataSourceTypeName(f func() datasource.DataSource) string {
var resp datasource.MetadataResponse
f().Metadata(context.Background(), datasource.MetadataRequest{ProviderTypeName: "artifactapi"}, &resp)
return resp.TypeName
}
func sortedByTypeName[T any](fs []T, name func(T) string) []T {
out := slices.Clone(fs)
slices.SortFunc(out, func(a, b T) int { return strings.Compare(name(a), name(b)) })
return out
}
@@ -27,6 +27,8 @@ type localAlpineResourceModel struct {
Description types.String `tfsdk:"description"`
}
func init() { registerResource(NewLocalAlpineResource) }
func NewLocalAlpineResource() resource.Resource {
return &localAlpineResource{}
}
+2
View File
@@ -27,6 +27,8 @@ type localDebResourceModel struct {
Description types.String `tfsdk:"description"`
}
func init() { registerResource(NewLocalDebResource) }
func NewLocalDebResource() resource.Resource {
return &localDebResource{}
}
@@ -27,6 +27,8 @@ type localDockerResourceModel struct {
Description types.String `tfsdk:"description"`
}
func init() { registerResource(NewLocalDockerResource) }
func NewLocalDockerResource() resource.Resource {
return &localDockerResource{}
}
@@ -27,6 +27,8 @@ type localGenericResourceModel struct {
Description types.String `tfsdk:"description"`
}
func init() { registerResource(NewLocalGenericResource) }
func NewLocalGenericResource() resource.Resource {
return &localGenericResource{}
}
+2
View File
@@ -27,6 +27,8 @@ type localHelmResourceModel struct {
Description types.String `tfsdk:"description"`
}
func init() { registerResource(NewLocalHelmResource) }
func NewLocalHelmResource() resource.Resource {
return &localHelmResource{}
}
+2
View File
@@ -27,6 +27,8 @@ type localNPMResourceModel struct {
Description types.String `tfsdk:"description"`
}
func init() { registerResource(NewLocalNPMResource) }
func NewLocalNPMResource() resource.Resource {
return &localNPMResource{}
}
+2
View File
@@ -27,6 +27,8 @@ type localPyPIResourceModel struct {
Description types.String `tfsdk:"description"`
}
func init() { registerResource(NewLocalPyPIResource) }
func NewLocalPyPIResource() resource.Resource {
return &localPyPIResource{}
}
+2
View File
@@ -27,6 +27,8 @@ type localRPMResourceModel struct {
Description types.String `tfsdk:"description"`
}
func init() { registerResource(NewLocalRPMResource) }
func NewLocalRPMResource() resource.Resource {
return &localRPMResource{}
}
@@ -27,6 +27,8 @@ type localTerraformResourceModel struct {
Description types.String `tfsdk:"description"`
}
func init() { registerResource(NewLocalTerraformResource) }
func NewLocalTerraformResource() resource.Resource {
return &localTerraformResource{}
}
-19
View File
@@ -71,25 +71,6 @@ func newRemoteResource(packageType string) func() resource.Resource {
}
}
func NewRemoteGeneric() resource.Resource { return &remoteResource{packageType: "generic"} }
func NewRemoteDocker() resource.Resource { return &remoteResource{packageType: "docker"} }
func NewRemoteHelm() resource.Resource { return &remoteResource{packageType: "helm"} }
func NewRemotePyPI() resource.Resource { return &remoteResource{packageType: "pypi"} }
func NewRemoteNPM() resource.Resource { return &remoteResource{packageType: "npm"} }
func NewRemoteRPM() resource.Resource { return &remoteResource{packageType: "rpm"} }
func NewRemoteDeb() resource.Resource { return &remoteResource{packageType: "deb"} }
func NewRemoteAlpine() resource.Resource { return &remoteResource{packageType: "alpine"} }
func NewRemotePuppet() resource.Resource { return &remoteResource{packageType: "puppet"} }
func NewRemoteTerraform() resource.Resource { return &remoteResource{packageType: "terraform"} }
func NewRemoteGoProxy() resource.Resource { return &remoteResource{packageType: "goproxy"} }
func NewRemoteCargo() resource.Resource { return &remoteResource{packageType: "cargo"} }
func NewRemoteGems() resource.Resource { return &remoteResource{packageType: "gems"} }
func NewRemoteGitHubRPM() resource.Resource { return &remoteResource{packageType: "github_rpm"} }
func NewRemoteGitHubDeb() resource.Resource { return &remoteResource{packageType: "github_deb"} }
func NewRemoteGitHubAlpine() resource.Resource {
return &remoteResource{packageType: "github_alpine"}
}
func (r *remoteResource) Metadata(_ context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) {
resp.TypeName = req.ProviderTypeName + "_remote_" + r.packageType
}
@@ -0,0 +1,3 @@
package provider
func init() { registerResource(newRemoteResource("alpine")) }
@@ -0,0 +1,3 @@
package provider
func init() { registerResource(newRemoteResource("cargo")) }
+3
View File
@@ -0,0 +1,3 @@
package provider
func init() { registerResource(newRemoteResource("deb")) }
@@ -0,0 +1,3 @@
package provider
func init() { registerResource(newRemoteResource("docker")) }
@@ -0,0 +1,3 @@
package provider
func init() { registerResource(newRemoteResource("gems")) }
@@ -0,0 +1,3 @@
package provider
func init() { registerResource(newRemoteResource("generic")) }
@@ -0,0 +1,3 @@
package provider
func init() { registerResource(newRemoteResource("github_alpine")) }
@@ -0,0 +1,3 @@
package provider
func init() { registerResource(newRemoteResource("github_deb")) }
@@ -0,0 +1,3 @@
package provider
func init() { registerResource(newRemoteResource("github_rpm")) }
@@ -0,0 +1,3 @@
package provider
func init() { registerResource(newRemoteResource("goproxy")) }
@@ -0,0 +1,3 @@
package provider
func init() { registerResource(newRemoteResource("helm")) }
+3
View File
@@ -0,0 +1,3 @@
package provider
func init() { registerResource(newRemoteResource("npm")) }
@@ -0,0 +1,3 @@
package provider
func init() { registerResource(newRemoteResource("puppet")) }
@@ -0,0 +1,3 @@
package provider
func init() { registerResource(newRemoteResource("pypi")) }
+3
View File
@@ -0,0 +1,3 @@
package provider
func init() { registerResource(newRemoteResource("rpm")) }
@@ -0,0 +1,3 @@
package provider
func init() { registerResource(newRemoteResource("terraform")) }
+24 -78
View File
@@ -150,26 +150,9 @@ func TestModelToAPI_NullLists(t *testing.T) {
func TestModelToAPI_PackageTypeFromResource(t *testing.T) {
ctx := context.Background()
tests := []struct {
pkgType string
}{
{"generic"},
{"docker"},
{"helm"},
{"pypi"},
{"npm"},
{"rpm"},
{"alpine"},
{"puppet"},
{"terraform"},
{"goproxy"},
{"cargo"},
{"gems"},
}
for _, tt := range tests {
t.Run(tt.pkgType, func(t *testing.T) {
r := &remoteResource{packageType: tt.pkgType}
for _, pkgType := range registeredRemoteKinds(t) {
t.Run(pkgType, func(t *testing.T) {
r := &remoteResource{packageType: pkgType}
model := remoteResourceModel{
Name: types.StringValue("test"),
BaseURL: types.StringValue("https://example.com"),
@@ -191,8 +174,8 @@ func TestModelToAPI_PackageTypeFromResource(t *testing.T) {
ReleasesRemote: types.StringValue(""),
}
api := r.modelToAPI(ctx, model)
if api.PackageType != tt.pkgType {
t.Errorf("expected package_type %s, got %s", tt.pkgType, api.PackageType)
if api.PackageType != pkgType {
t.Errorf("expected package_type %s, got %s", pkgType, api.PackageType)
}
})
}
@@ -404,33 +387,33 @@ func TestModelToAPI_RoundTrip(t *testing.T) {
}
func TestRemoteResource_Metadata(t *testing.T) {
tests := []struct {
pkgType string
expected string
}{
{"generic", "artifactapi_remote_generic"},
{"docker", "artifactapi_remote_docker"},
{"helm", "artifactapi_remote_helm"},
{"pypi", "artifactapi_remote_pypi"},
{"npm", "artifactapi_remote_npm"},
{"github_rpm", "artifactapi_remote_github_rpm"},
{"github_deb", "artifactapi_remote_github_deb"},
{"github_alpine", "artifactapi_remote_github_alpine"},
}
for _, tt := range tests {
t.Run(tt.pkgType, func(t *testing.T) {
r := &remoteResource{packageType: tt.pkgType}
for _, pkgType := range registeredRemoteKinds(t) {
t.Run(pkgType, func(t *testing.T) {
r := &remoteResource{packageType: pkgType}
req := resource.MetadataRequest{ProviderTypeName: "artifactapi"}
var resp resource.MetadataResponse
r.Metadata(context.Background(), req, &resp)
if resp.TypeName != tt.expected {
t.Errorf("expected %s, got %s", tt.expected, resp.TypeName)
if expected := "artifactapi_remote_" + pkgType; resp.TypeName != expected {
t.Errorf("expected %s, got %s", expected, resp.TypeName)
}
})
}
}
func registeredRemoteKinds(t *testing.T) []string {
t.Helper()
var kinds []string
for _, f := range resourceRegistry {
if rr, ok := f().(*remoteResource); ok {
kinds = append(kinds, rr.packageType)
}
}
if len(kinds) == 0 {
t.Fatal("no remote kinds registered")
}
return kinds
}
func TestModelToAPI_GitHubRPM(t *testing.T) {
ctx := context.Background()
r := &remoteResource{packageType: "github_rpm"}
@@ -510,43 +493,6 @@ func TestRemoteResource_Schema(t *testing.T) {
}
}
func TestNewRemoteResource_Constructors(t *testing.T) {
tests := []struct {
name string
fn func() resource.Resource
expected string
}{
{"generic", func() resource.Resource { return NewRemoteGeneric() }, "generic"},
{"docker", func() resource.Resource { return NewRemoteDocker() }, "docker"},
{"helm", func() resource.Resource { return NewRemoteHelm() }, "helm"},
{"pypi", func() resource.Resource { return NewRemotePyPI() }, "pypi"},
{"npm", func() resource.Resource { return NewRemoteNPM() }, "npm"},
{"rpm", func() resource.Resource { return NewRemoteRPM() }, "rpm"},
{"alpine", func() resource.Resource { return NewRemoteAlpine() }, "alpine"},
{"puppet", func() resource.Resource { return NewRemotePuppet() }, "puppet"},
{"terraform", func() resource.Resource { return NewRemoteTerraform() }, "terraform"},
{"goproxy", func() resource.Resource { return NewRemoteGoProxy() }, "goproxy"},
{"cargo", func() resource.Resource { return NewRemoteCargo() }, "cargo"},
{"gems", func() resource.Resource { return NewRemoteGems() }, "gems"},
{"github_rpm", func() resource.Resource { return NewRemoteGitHubRPM() }, "github_rpm"},
{"github_deb", func() resource.Resource { return NewRemoteGitHubDeb() }, "github_deb"},
{"github_alpine", func() resource.Resource { return NewRemoteGitHubAlpine() }, "github_alpine"},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
r := tt.fn()
rr, ok := r.(*remoteResource)
if !ok {
t.Fatal("expected *remoteResource")
}
if rr.packageType != tt.expected {
t.Errorf("expected packageType %s, got %s", tt.expected, rr.packageType)
}
})
}
}
func TestModelToAPI_Mirrorlist(t *testing.T) {
ctx := context.Background()
mirrors := []string{"https://mirror-a.example.com/rpm", "https://mirror-b.example.com/rpm"}
+2
View File
@@ -29,6 +29,8 @@ type virtualResourceModel struct {
Members types.List `tfsdk:"members"`
}
func init() { registerResource(NewVirtualResource) }
func NewVirtualResource() resource.Resource {
return &virtualResource{}
}