2 Commits

Author SHA1 Message Date
benvin 0561bae10e Merge pull request 'feat: support per-remote upstream timeouts' (#9) from benvin/remote-upstream-timeouts into main
ci/woodpecker/tag/release Pipeline failed
Reviewed-on: #9
2026-07-02 22:23:04 +10:00
unkinben 4dd290518d feat: support per-remote upstream timeouts
ci/woodpecker/pr/build Pipeline was successful
ci/woodpecker/pr/test Pipeline was successful
ci/woodpecker/pr/pre-commit Pipeline was successful
Add upstream_dial_timeout, upstream_tls_timeout and
upstream_response_header_timeout (seconds; 0 = server default) to the
remote resource and data source, matching the artifactapi server. Wire
them through the API model, schema, create/read/update mapping, docs and
unit tests.
2026-07-02 22:19:39 +10:00
5 changed files with 76 additions and 0 deletions
+3
View File
@@ -82,6 +82,9 @@ Available resource types:
| `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
+12
View File
@@ -45,6 +45,10 @@ func (d *remoteDataSource) Schema(_ context.Context, _ datasource.SchemaRequest,
"stale_on_error": schema.BoolAttribute{Computed: true},
"releases_remote": schema.StringAttribute{Computed: true},
"managed_by": schema.StringAttribute{Computed: true},
"upstream_dial_timeout": schema.Int64Attribute{Computed: true},
"upstream_tls_timeout": schema.Int64Attribute{Computed: true},
"upstream_response_header_timeout": schema.Int64Attribute{Computed: true},
},
}
}
@@ -68,6 +72,10 @@ type remoteDataSourceModel struct {
StaleOnError types.Bool `tfsdk:"stale_on_error"`
ReleasesRemote types.String `tfsdk:"releases_remote"`
ManagedBy types.String `tfsdk:"managed_by"`
UpstreamDialTimeout types.Int64 `tfsdk:"upstream_dial_timeout"`
UpstreamTLSTimeout types.Int64 `tfsdk:"upstream_tls_timeout"`
UpstreamResponseHeaderTimeout types.Int64 `tfsdk:"upstream_response_header_timeout"`
}
func (d *remoteDataSource) Configure(_ context.Context, req datasource.ConfigureRequest, resp *datasource.ConfigureResponse) {
@@ -114,6 +122,10 @@ func (d *remoteDataSource) Read(ctx context.Context, req datasource.ReadRequest,
StaleOnError: types.BoolValue(remote.StaleOnError),
ReleasesRemote: types.StringValue(remote.ReleasesRemote),
ManagedBy: types.StringValue(remote.ManagedBy),
UpstreamDialTimeout: types.Int64Value(remote.UpstreamDialTimeout),
UpstreamTLSTimeout: types.Int64Value(remote.UpstreamTLSTimeout),
UpstreamResponseHeaderTimeout: types.Int64Value(remote.UpstreamResponseHeaderTimeout),
}
resp.Diagnostics.Append(resp.State.Set(ctx, state)...)
}
+4
View File
@@ -22,6 +22,10 @@ type remoteAPI struct {
StaleOnError bool `json:"stale_on_error"`
ReleasesRemote string `json:"releases_remote,omitempty"`
ManagedBy string `json:"managed_by,omitempty"`
UpstreamDialTimeout int64 `json:"upstream_dial_timeout"`
UpstreamTLSTimeout int64 `json:"upstream_tls_timeout"`
UpstreamResponseHeaderTimeout int64 `json:"upstream_response_header_timeout"`
}
type virtualAPI struct {
+24
View File
@@ -44,6 +44,10 @@ type remoteResourceModel struct {
QuarantineDays types.Int64 `tfsdk:"quarantine_days"`
StaleOnError types.Bool `tfsdk:"stale_on_error"`
ReleasesRemote types.String `tfsdk:"releases_remote"`
UpstreamDialTimeout types.Int64 `tfsdk:"upstream_dial_timeout"`
UpstreamTLSTimeout types.Int64 `tfsdk:"upstream_tls_timeout"`
UpstreamResponseHeaderTimeout types.Int64 `tfsdk:"upstream_response_header_timeout"`
}
func newRemoteResource(packageType string) func() resource.Resource {
@@ -144,6 +148,18 @@ func (r *remoteResource) Schema(_ context.Context, _ resource.SchemaRequest, res
Description: "Name of the CDN remote for download URL rewriting (terraform only).",
Optional: true, Computed: true, Default: stringdefault.StaticString(""),
},
"upstream_dial_timeout": schema.Int64Attribute{
Description: "Upstream TCP connect timeout in seconds (0 = server default).",
Optional: true, Computed: true, Default: int64default.StaticInt64(0),
},
"upstream_tls_timeout": schema.Int64Attribute{
Description: "Upstream TLS handshake timeout in seconds (0 = server default).",
Optional: true, Computed: true, Default: int64default.StaticInt64(0),
},
"upstream_response_header_timeout": schema.Int64Attribute{
Description: "Upstream response-header timeout in seconds (0 = server default).",
Optional: true, Computed: true, Default: int64default.StaticInt64(0),
},
}
resp.Schema = schema.Schema{
@@ -268,6 +284,10 @@ func (r *remoteResource) modelToAPI(ctx context.Context, m remoteResourceModel)
QuarantineEnabled: m.QuarantineEnabled.ValueBool(),
QuarantineDays: m.QuarantineDays.ValueInt64(),
StaleOnError: m.StaleOnError.ValueBool(),
UpstreamDialTimeout: m.UpstreamDialTimeout.ValueInt64(),
UpstreamTLSTimeout: m.UpstreamTLSTimeout.ValueInt64(),
UpstreamResponseHeaderTimeout: m.UpstreamResponseHeaderTimeout.ValueInt64(),
}
api.Patterns = listToStrings(ctx, m.Patterns)
api.Blocklist = listToStrings(ctx, m.Blocklist)
@@ -299,6 +319,10 @@ func (r *remoteResource) apiToModel(ctx context.Context, api remoteAPI) remoteRe
BanTagsEnabled: types.BoolValue(api.BanTagsEnabled),
BanTags: stringsToList(ctx, api.BanTags),
ReleasesRemote: types.StringValue(api.ReleasesRemote),
UpstreamDialTimeout: types.Int64Value(api.UpstreamDialTimeout),
UpstreamTLSTimeout: types.Int64Value(api.UpstreamTLSTimeout),
UpstreamResponseHeaderTimeout: types.Int64Value(api.UpstreamResponseHeaderTimeout),
}
return m
}
+33
View File
@@ -31,10 +31,18 @@ func TestModelToAPI_FullFields(t *testing.T) {
QuarantineDays: types.Int64Value(7),
StaleOnError: types.BoolValue(false),
ReleasesRemote: types.StringValue("cdn-remote"),
UpstreamDialTimeout: types.Int64Value(3),
UpstreamTLSTimeout: types.Int64Value(4),
UpstreamResponseHeaderTimeout: types.Int64Value(5),
}
api := r.modelToAPI(ctx, model)
if api.UpstreamDialTimeout != 3 || api.UpstreamTLSTimeout != 4 || api.UpstreamResponseHeaderTimeout != 5 {
t.Errorf("upstream timeouts: got %d/%d/%d, want 3/4/5",
api.UpstreamDialTimeout, api.UpstreamTLSTimeout, api.UpstreamResponseHeaderTimeout)
}
if api.Name != "my-remote" {
t.Errorf("Name: expected my-remote, got %s", api.Name)
}
@@ -211,10 +219,22 @@ func TestAPIToModel_FullFields(t *testing.T) {
StaleOnError: false,
ReleasesRemote: "cdn-remote",
ManagedBy: "terraform",
UpstreamDialTimeout: 3,
UpstreamTLSTimeout: 4,
UpstreamResponseHeaderTimeout: 5,
}
model := r.apiToModel(ctx, api)
if model.UpstreamDialTimeout.ValueInt64() != 3 ||
model.UpstreamTLSTimeout.ValueInt64() != 4 ||
model.UpstreamResponseHeaderTimeout.ValueInt64() != 5 {
t.Errorf("upstream timeouts: got %d/%d/%d, want 3/4/5",
model.UpstreamDialTimeout.ValueInt64(),
model.UpstreamTLSTimeout.ValueInt64(),
model.UpstreamResponseHeaderTimeout.ValueInt64())
}
if model.Name.ValueString() != "my-remote" {
t.Errorf("Name: expected my-remote, got %s", model.Name.ValueString())
}
@@ -324,12 +344,24 @@ func TestModelToAPI_RoundTrip(t *testing.T) {
QuarantineDays: 3,
StaleOnError: true,
ReleasesRemote: "",
UpstreamDialTimeout: 5,
UpstreamTLSTimeout: 0,
UpstreamResponseHeaderTimeout: 45,
}
// API -> Model -> API round-trip
model := r.apiToModel(ctx, original)
result := r.modelToAPI(ctx, model)
if result.UpstreamDialTimeout != original.UpstreamDialTimeout ||
result.UpstreamTLSTimeout != original.UpstreamTLSTimeout ||
result.UpstreamResponseHeaderTimeout != original.UpstreamResponseHeaderTimeout {
t.Errorf("upstream timeouts round-trip: got %d/%d/%d, want %d/%d/%d",
result.UpstreamDialTimeout, result.UpstreamTLSTimeout, result.UpstreamResponseHeaderTimeout,
original.UpstreamDialTimeout, original.UpstreamTLSTimeout, original.UpstreamResponseHeaderTimeout)
}
if result.Name != original.Name {
t.Errorf("Name: expected %s, got %s", original.Name, result.Name)
}
@@ -405,6 +437,7 @@ func TestRemoteResource_Schema(t *testing.T) {
"ban_tags_enabled", "ban_tags",
"quarantine_enabled", "quarantine_days",
"stale_on_error", "releases_remote",
"upstream_dial_timeout", "upstream_tls_timeout", "upstream_response_header_timeout",
}
for _, attr := range expectedAttrs {