Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,25 @@ Eliminates the runtime-failure surface (sentinel-stub returning `ErrApplyV1Remov

## [Unreleased]

### Added

- Parent-bound `digitalocean.database_user` resources and explicit user/admin
rotation epochs, with sensitive output routing and persisted prior-state
recovery through the released Workflow SDK.
- Targeted App Platform one-off jobs with exact component/deployment ownership,
bounded status/log/cancellation, and restartable cleanup after ambiguous
responses, including provider-encrypted secret normalization.
- Read-only live Droplet pricing checks for exact slug/region and maximum
monthly cost, enforced before mutation rather than from a static catalog.
- Credential-free CI executes the released native wfctl saved-plan/state proof
and requires genuine cleanup/redaction evidence; skipped proof is not success.

### Changed

- Minimum supported Workflow engine is `0.86.1` for the public prior-state SDK.
Provider behavior remains plugin-owned; no app-specific recovery integration
or live deployment is implied by this release.

### Changed

- **App Platform deployment strategy naming** — the legacy
Expand Down
9 changes: 8 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ DigitalOcean IaC provider for the [GoCodeAlone/workflow](https://github.com/GoCo
| `infra.app_domain` | App Platform domain binding |
| `infra.k8s_cluster` | DigitalOcean Kubernetes (DOKS) |
| `infra.database` | Managed database (PostgreSQL, MySQL, Redis, MongoDB) |
| `digitalocean.database_user` | Parent-bound managed database user with explicit credential rotation |
| `infra.cache` | Managed Redis cache |
| `infra.load_balancer` | Load balancer |
| `infra.vpc` | Virtual Private Cloud |
Expand All @@ -25,6 +26,12 @@ DigitalOcean IaC provider for the [GoCodeAlone/workflow](https://github.com/GoCo
| `infra.iam_role` | IAM role (declarative) |
| `infra.api_gateway` | API gateway |

`digitalocean.database_user` is a provider-owned resource type. wfctl v0.86.1
supports its driver and saved-plan execution, but its YAML desired-resource
discovery currently omits provider-specific types. Before applying a YAML plan,
verify that every declared resource appears; do not accept an incomplete plan.
Generic host discovery needs an upstream correction, not a provider type alias.

## Quick start

See [`examples/minimal/config.yaml`](examples/minimal/config.yaml) for a minimal working configuration.
Expand Down Expand Up @@ -144,7 +151,7 @@ The nested shape matches Workflow DNS replay fixtures while keeping existing fla

## Requirements

- workflow engine ≥ `0.57.1`
- workflow engine ≥ `0.86.1`
- `DIGITALOCEAN_TOKEN` environment variable set to a valid DO personal access token

## Contributing
Expand Down
56 changes: 55 additions & 1 deletion cmd/plugin/plugin.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
"license": "MIT",
"type": "external",
"tier": "community",
"minEngineVersion": "0.80.1",
"minEngineVersion": "0.86.1",
"required_secrets": [
{
"name": "DIGITALOCEAN_TOKEN",
Expand All @@ -24,11 +24,14 @@
"workflow.plugin.external.iac.IaCProviderValidator",
"workflow.plugin.external.iac.IaCProviderDriftConfigDetector",
"workflow.plugin.external.iac.IaCProviderLogCapture",
"workflow.plugin.external.iac.IaCProviderRunner",
"workflow.plugin.external.iac.IaCProviderJobCanceler",
"workflow.plugin.external.iac.IaCProviderRequirementMapper",
"workflow.plugin.external.iac.IaCProviderRegionLister",
"workflow.plugin.external.iac.IaCProviderOwnership",
"workflow.plugin.external.iac.IaCProviderFinalizer",
"workflow.plugin.external.iac.ResourceDriver",
"workflow.plugin.external.iac.ResourceSensitiveInputDeclarer",
"workflow.plugin.external.iac.IaCStateBackend"
],
"keywords": [
Expand Down Expand Up @@ -88,6 +91,7 @@
"infra.app_domain",
"infra.k8s_cluster",
"infra.database",
"digitalocean.database_user",
"infra.cache",
"infra.load_balancer",
"infra.vpc",
Expand All @@ -102,6 +106,56 @@
"infra.api_gateway"
],
"configSchema": {
"digitalocean.database_user": {
"description": "Managed database user with provider-generated credentials and opt-in epoch-controlled password rotation.",
"fields": {
"database_id": {
"type": "string",
"required": true,
"description": "Parent managed database's canonical UUID."
},
"username": {
"type": "string",
"required": true,
"description": "Nonempty database username. The provider escapes it in API paths and resource identifiers."
},
"rotation_epoch": {
"type": "string",
"required": false,
"description": "Nonempty opaque epoch. Changing it requests password rotation; returned password and TLS URI are sensitive outputs, not desired inputs."
}
}
},
"infra.database": {
"description": "Managed database. Administrator password rotation is opt-in and separate from ordinary configuration updates.",
"fields": {
"admin_rotation_epoch": {
"type": "string",
"required": false,
"description": "Nonempty opaque epoch. Changing it requests administrator password rotation; omission preserves legacy behavior."
}
}
},
"infra.droplet": {
"description": "DigitalOcean Droplet with optional fresh catalog price validation before plan, create, or destructive replacement.",
"fields": {
"size": {
"type": "string",
"required": false,
"description": "Explicit size slug required when max_monthly_usd is present."
},
"region": {
"type": "string",
"required": false,
"description": "Explicit region slug required when max_monthly_usd is present."
},
"max_monthly_usd": {
"type": "number",
"required": false,
"description": "Positive finite monthly USD cap. A fresh Sizes API quote must confirm exact size and region availability and price at or below this cap; uncapped resources make no price lookup."
}
}
},
"infra.container_service": {
"expose": {
"type": "string",
Expand Down
43 changes: 22 additions & 21 deletions go.mod
Original file line number Diff line number Diff line change
@@ -1,18 +1,19 @@
module github.com/GoCodeAlone/workflow-plugin-digitalocean

go 1.26.4
go 1.26.5

require (
github.com/GoCodeAlone/workflow v0.85.2
github.com/GoCodeAlone/workflow v0.86.1
github.com/GoCodeAlone/workflow-plugin-control-plane v0.1.0
github.com/aws/aws-sdk-go-v2 v1.41.6
github.com/aws/aws-sdk-go-v2/config v1.32.16
github.com/aws/aws-sdk-go-v2/credentials v1.19.15
github.com/aws/aws-sdk-go-v2/service/s3 v1.97.2
github.com/digitalocean/godo v1.189.0
github.com/google/uuid v1.6.0
github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674
golang.org/x/oauth2 v0.36.0
google.golang.org/grpc v1.81.1
google.golang.org/grpc v1.83.2
google.golang.org/protobuf v1.36.12-0.20260120151049-f2248ac996af
gopkg.in/yaml.v3 v3.0.1
)
Expand All @@ -21,9 +22,9 @@ require (
github.com/BurntSushi/toml v1.6.0 // indirect
github.com/DataDog/datadog-go/v5 v5.8.3 // indirect
github.com/GoCodeAlone/go-plugin v1.7.0 // indirect
github.com/GoCodeAlone/modular v1.13.5 // indirect
github.com/GoCodeAlone/modular/modules/auth v1.17.0 // indirect
github.com/GoCodeAlone/modular/modules/cache v1.17.0 // indirect
github.com/GoCodeAlone/modular v1.13.6 // indirect
github.com/GoCodeAlone/modular/modules/auth v1.19.0 // indirect
github.com/GoCodeAlone/modular/modules/cache v1.19.0 // indirect
github.com/GoCodeAlone/modular/modules/eventbus/v2 v2.10.0 // indirect
github.com/GoCodeAlone/modular/modules/jsonschema v1.17.0 // indirect
github.com/GoCodeAlone/modular/modules/reverseproxy/v2 v2.10.0 // indirect
Expand All @@ -32,7 +33,7 @@ require (
github.com/Microsoft/go-winio v0.6.2 // indirect
github.com/RoaringBitmap/roaring v1.9.4 // indirect
github.com/Workiva/go-datastructures v1.1.7 // indirect
github.com/andybalholm/brotli v1.2.1 // indirect
github.com/andybalholm/brotli v1.2.2 // indirect
github.com/armon/go-metrics v0.4.1 // indirect
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.8 // indirect
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.22 // indirect
Expand Down Expand Up @@ -73,13 +74,13 @@ require (
github.com/go-jose/go-jose/v4 v4.1.4 // indirect
github.com/go-logr/logr v1.4.3 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/go-openapi/jsonpointer v0.24.0 // indirect
github.com/gobwas/glob v0.2.3 // indirect
github.com/godbus/dbus/v5 v5.2.2 // indirect
github.com/golang-jwt/jwt/v5 v5.3.1 // indirect
github.com/golobby/cast v1.3.3 // indirect
github.com/google/btree v1.1.3 // indirect
github.com/google/go-querystring v1.2.0 // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/grpc-ecosystem/grpc-gateway/v2 v2.29.0 // indirect
github.com/hashicorp/errwrap v1.1.0 // indirect
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
Expand Down Expand Up @@ -112,8 +113,8 @@ require (
github.com/jcmturner/gokrb5/v8 v8.4.4 // indirect
github.com/jcmturner/rpc/v2 v2.0.3 // indirect
github.com/json-iterator/go v1.1.13-0.20220915233716-71ac16282d12 // indirect
github.com/klauspost/compress v1.18.6 // indirect
github.com/klauspost/cpuid/v2 v2.3.0 // indirect
github.com/klauspost/compress v1.19.0 // indirect
github.com/klauspost/cpuid/v2 v2.4.0 // indirect
github.com/mattn/go-colorable v0.1.15 // indirect
github.com/mattn/go-isatty v0.0.22 // indirect
github.com/miekg/dns v1.1.72 // indirect
Expand Down Expand Up @@ -146,8 +147,8 @@ require (
github.com/tidwall/btree v1.8.1 // indirect
github.com/tidwall/match v1.2.0 // indirect
github.com/tidwall/redcon v1.6.2 // indirect
github.com/tochemey/goakt/v4 v4.2.12 // indirect
github.com/tochemey/olric v0.3.10 // indirect
github.com/tochemey/goakt/v4 v4.3.1 // indirect
github.com/tochemey/olric v0.3.11 // indirect
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
github.com/vmihailenco/msgpack/v5 v5.4.1 // indirect
github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect
Expand All @@ -173,16 +174,16 @@ require (
go.uber.org/zap v1.28.0 // indirect
go.yaml.in/yaml/v2 v2.4.4 // indirect
golang.org/x/arch v0.28.0 // indirect
golang.org/x/crypto v0.53.0 // indirect
golang.org/x/mod v0.37.0 // indirect
golang.org/x/net v0.56.0 // indirect
golang.org/x/sync v0.21.0 // indirect
golang.org/x/sys v0.46.0 // indirect
golang.org/x/text v0.38.0 // indirect
golang.org/x/crypto v0.57.0 // indirect
golang.org/x/mod v0.41.0 // indirect
golang.org/x/net v0.58.0 // indirect
golang.org/x/sync v0.23.0 // indirect
golang.org/x/sys v0.48.0 // indirect
golang.org/x/text v0.42.0 // indirect
golang.org/x/time v0.15.0 // indirect
golang.org/x/tools v0.46.0 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20260622175928-b703f567277d // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20260622175928-b703f567277d // indirect
golang.org/x/tools v0.49.0 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20260630182238-925bb5da69e7 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20260630182238-925bb5da69e7 // indirect
google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.6.2 // indirect
modernc.org/libc v1.73.4 // indirect
modernc.org/mathutil v1.7.1 // indirect
Expand Down
Loading
Loading