feat(proto): report per-repository sync errors, drop the reason from mirror_failed and tidy mirror docs
6 files · +57 -67
M docs/services.md +2 -2
| @@ -34,7 +34,7 @@ No new token scope is introduced. | |||
| 34 | 34 | | `SetRepoMirrorExcluded` | `repo:admin` on the repository. Opts out of or into the global template; a URL override wins over exclusion | | |
| 35 | 35 | | `GetMirrorStatus` | Read access to the repository. Error `detail` is returned only with `repo:admin` | | |
| 36 | 36 | | `ListMirrorStatus` | Owner rights | | |
| 37 | - | | `SyncMirror` | `repo:admin` for `repo`, or owner rights for `all=true` (with empty `repo`). Returns per-repository results, including queued and no-op indicators | | |
| 37 | + | | `SyncMirror` | `repo:admin` for `repo`, or owner rights for `all=true` (with empty `repo`). Returns per-repository results, including queued and no-op indicators and a per-repository `error`; `all=true` continues past failing repositories and skips disabled ones | | |
| 38 | 38 | ||
| 39 | 39 | Cron expressions have five fields, run in UTC and must be at least five minutes | |
| 40 | 40 | apart. Mirrors push the default branch (or every branch with `all_branches`) and | |
| @@ -49,4 +49,4 @@ starts failing (ok or disabled to failing or paused, once per incident), and `mi | |||
| 49 | 49 | (field 24, `MirrorRecovered`) when a failing or paused mirror succeeds again. | |
| 50 | 50 | These are transition events, not events for every attempt. They are delivered on | |
| 51 | 51 | `repo:<name>` and `home`, subject to repository visibility. Neither event carries | |
| 52 | - | a URL or error detail. | |
| 52 | + | a URL, reason or error detail (`MirrorFailed` field 4, `reason`, was removed). | |
| @@ -34,7 +34,7 @@ No new token scope is introduced. | |||||
| 34 | | `SetRepoMirrorExcluded` | `repo:admin` on the repository. Opts out of or into the global template; a URL override wins over exclusion | | 34 | | `SetRepoMirrorExcluded` | `repo:admin` on the repository. Opts out of or into the global template; a URL override wins over exclusion | | ||
| 35 | | `GetMirrorStatus` | Read access to the repository. Error `detail` is returned only with `repo:admin` | | 35 | | `GetMirrorStatus` | Read access to the repository. Error `detail` is returned only with `repo:admin` | | ||
| 36 | | `ListMirrorStatus` | Owner rights | | 36 | | `ListMirrorStatus` | Owner rights | | ||
| 37 | - | | `SyncMirror` | `repo:admin` for `repo`, or owner rights for `all=true` (with empty `repo`). Returns per-repository results, including queued and no-op indicators | | 37 | + | | `SyncMirror` | `repo:admin` for `repo`, or owner rights for `all=true` (with empty `repo`). Returns per-repository results, including queued and no-op indicators and a per-repository `error`; `all=true` continues past failing repositories and skips disabled ones | |
| 38 | 38 | ||||
| 39 | Cron expressions have five fields, run in UTC and must be at least five minutes | 39 | Cron expressions have five fields, run in UTC and must be at least five minutes | ||
| 40 | apart. Mirrors push the default branch (or every branch with `all_branches`) and | 40 | apart. Mirrors push the default branch (or every branch with `all_branches`) and | ||
| @@ -49,4 +49,4 @@ starts failing (ok or disabled to failing or paused, once per incident), and `mi | |||||
| 49 | (field 24, `MirrorRecovered`) when a failing or paused mirror succeeds again. | 49 | (field 24, `MirrorRecovered`) when a failing or paused mirror succeeds again. | ||
| 50 | These are transition events, not events for every attempt. They are delivered on | 50 | These are transition events, not events for every attempt. They are delivered on | ||
| 51 | `repo:<name>` and `home`, subject to repository visibility. Neither event carries | 51 | `repo:<name>` and `home`, subject to repository visibility. Neither event carries | ||
| 52 | - | a URL or error detail. | 52 | + | a URL, reason or error detail (`MirrorFailed` field 4, `reason`, was removed). |
M gen/ermite/event.pb.go +6 -16 generated file: collapsed
M gen/ermite/mirror.pb.go +27 -21 generated file: collapsed
M gen_test.go +1 -1
| @@ -598,7 +598,7 @@ func TestMirrorWire(t *testing.T) { | |||
| 598 | 598 | for _, in := range []proto.Message{ | |
| 599 | 599 | &ermite.SetMirrorConfigRequest{Enabled: proto.Bool(false), UrlTemplate: proto.String(""), Cron: proto.String(""), AllBranches: proto.Bool(false)}, | |
| 600 | 600 | &ermite.SetRepoMirrorRequest{Repo: "repo", AllBranches: proto.Bool(false)}, | |
| 601 | - | &ermite.Event{Kind: &ermite.Event_MirrorFailed{MirrorFailed: &ermite.MirrorFailed{Repo: "repo", State: ermite.MirrorState_MIRROR_STATE_PAUSED, FailureClass: "diverged", Reason: "remote diverged"}}}, | |
| 601 | + | &ermite.Event{Kind: &ermite.Event_MirrorFailed{MirrorFailed: &ermite.MirrorFailed{Repo: "repo", State: ermite.MirrorState_MIRROR_STATE_PAUSED, FailureClass: "diverged"}}}, | |
| 602 | 602 | &ermite.Event{Kind: &ermite.Event_MirrorRecovered{MirrorRecovered: &ermite.MirrorRecovered{Repo: "repo"}}}, | |
| 603 | 603 | } { | |
| 604 | 604 | b, err := proto.Marshal(in) | |
| @@ -598,7 +598,7 @@ func TestMirrorWire(t *testing.T) { | |||||
| 598 | for _, in := range []proto.Message{ | 598 | for _, in := range []proto.Message{ | ||
| 599 | &ermite.SetMirrorConfigRequest{Enabled: proto.Bool(false), UrlTemplate: proto.String(""), Cron: proto.String(""), AllBranches: proto.Bool(false)}, | 599 | &ermite.SetMirrorConfigRequest{Enabled: proto.Bool(false), UrlTemplate: proto.String(""), Cron: proto.String(""), AllBranches: proto.Bool(false)}, | ||
| 600 | &ermite.SetRepoMirrorRequest{Repo: "repo", AllBranches: proto.Bool(false)}, | 600 | &ermite.SetRepoMirrorRequest{Repo: "repo", AllBranches: proto.Bool(false)}, | ||
| 601 | - | &ermite.Event{Kind: &ermite.Event_MirrorFailed{MirrorFailed: &ermite.MirrorFailed{Repo: "repo", State: ermite.MirrorState_MIRROR_STATE_PAUSED, FailureClass: "diverged", Reason: "remote diverged"}}}, | 601 | + | &ermite.Event{Kind: &ermite.Event_MirrorFailed{MirrorFailed: &ermite.MirrorFailed{Repo: "repo", State: ermite.MirrorState_MIRROR_STATE_PAUSED, FailureClass: "diverged"}}}, |
| 602 | &ermite.Event{Kind: &ermite.Event_MirrorRecovered{MirrorRecovered: &ermite.MirrorRecovered{Repo: "repo"}}}, | 602 | &ermite.Event{Kind: &ermite.Event_MirrorRecovered{MirrorRecovered: &ermite.MirrorRecovered{Repo: "repo"}}}, | ||
| 603 | } { | 603 | } { | ||
| 604 | b, err := proto.Marshal(in) | 604 | b, err := proto.Marshal(in) | ||
M proto/ermite/event.proto +6 -5
| @@ -314,9 +314,9 @@ message JobUpdated { | |||
| 314 | 314 | ||
| 315 | 315 | // MirrorFailed reports the start of a failure incident: a transition from ok | |
| 316 | 316 | // or disabled into failing or paused. It is sent once per incident, not for | |
| 317 | - | // each failed attempt nor for failing to paused. Servers with feature "mirror" deliver it | |
| 318 | - | // on "repo:<name>" and "home", subject to repository visibility. It carries no | |
| 319 | - | // URL or error detail. | |
| 317 | + | // each failed attempt nor for failing to paused. Servers with feature "mirror" | |
| 318 | + | // deliver it on "repo:<name>" and "home", subject to repository visibility. It | |
| 319 | + | // carries no URL, reason or error detail; read the status for those. | |
| 320 | 320 | message MirrorFailed { | |
| 321 | 321 | // Repo is the repository name. | |
| 322 | 322 | string repo = 1; | |
| @@ -324,8 +324,9 @@ message MirrorFailed { | |||
| 324 | 324 | MirrorState state = 2; | |
| 325 | 325 | // FailureClass is the failure category. | |
| 326 | 326 | string failure_class = 3; | |
| 327 | - | // Reason explains the failure without a URL or error detail. | |
| 328 | - | string reason = 4; | |
| 327 | + | ||
| 328 | + | reserved 4; | |
| 329 | + | reserved "reason"; | |
| 329 | 330 | } | |
| 330 | 331 | ||
| 331 | 332 | // MirrorRecovered reports a successful sync after failing or paused. Delivery | |
| @@ -314,9 +314,9 @@ message JobUpdated { | |||||
| 314 | 314 | ||||
| 315 | // MirrorFailed reports the start of a failure incident: a transition from ok | 315 | // MirrorFailed reports the start of a failure incident: a transition from ok | ||
| 316 | // or disabled into failing or paused. It is sent once per incident, not for | 316 | // or disabled into failing or paused. It is sent once per incident, not for | ||
| 317 | - | // each failed attempt nor for failing to paused. Servers with feature "mirror" deliver it | 317 | + | // each failed attempt nor for failing to paused. Servers with feature "mirror" |
| 318 | - | // on "repo:<name>" and "home", subject to repository visibility. It carries no | 318 | + | // deliver it on "repo:<name>" and "home", subject to repository visibility. It |
| 319 | - | // URL or error detail. | 319 | + | // carries no URL, reason or error detail; read the status for those. |
| 320 | message MirrorFailed { | 320 | message MirrorFailed { | ||
| 321 | // Repo is the repository name. | 321 | // Repo is the repository name. | ||
| 322 | string repo = 1; | 322 | string repo = 1; | ||
| @@ -324,8 +324,9 @@ message MirrorFailed { | |||||
| 324 | MirrorState state = 2; | 324 | MirrorState state = 2; | ||
| 325 | // FailureClass is the failure category. | 325 | // FailureClass is the failure category. | ||
| 326 | string failure_class = 3; | 326 | string failure_class = 3; | ||
| 327 | - | // Reason explains the failure without a URL or error detail. | 327 | + | |
| 328 | - | string reason = 4; | 328 | + | reserved 4; |
| 329 | + | reserved "reason"; | |||
| 329 | } | 330 | } | ||
| 330 | 331 | ||||
| 331 | // MirrorRecovered reports a successful sync after failing or paused. Delivery | 332 | // MirrorRecovered reports a successful sync after failing or paused. Delivery | ||
M proto/ermite/mirror.proto +15 -22
| @@ -6,18 +6,13 @@ package ermite; | |||
| 6 | 6 | ||
| 7 | 7 | import "google/protobuf/timestamp.proto"; | |
| 8 | 8 | ||
| 9 | - | // MirrorService configures the push mirrors of the server. Global settings | |
| 10 | - | // and key | |
| 11 | - | // rotation need the admin token or a token with every scope and no repository | |
| 12 | - | // or | |
| 13 | - | // branch limits; per-repository settings and SyncMirror need repo:admin on | |
| 14 | - | // the | |
| 15 | - | // repository; status needs read access. GetMirrorKey needs a non-anonymous | |
| 16 | - | // caller. | |
| 17 | - | // ListMirrorStatus and SyncMirror with All need owner rights. No new token | |
| 18 | - | // scope | |
| 19 | - | // is required. Servers implementing this service list "mirror" in | |
| 20 | - | // GetVersionResponse.features. | |
| 9 | + | // MirrorService configures the push mirrors of the server. Global settings and | |
| 10 | + | // key rotation need the admin token or a token with every scope and no | |
| 11 | + | // repository or branch limits; per-repository settings and SyncMirror need | |
| 12 | + | // repo:admin on the repository; status needs read access. GetMirrorKey needs a | |
| 13 | + | // non-anonymous caller. ListMirrorStatus and SyncMirror with All need owner | |
| 14 | + | // rights. No new token scope is required. Servers implementing this service | |
| 15 | + | // list "mirror" in GetVersionResponse.features. | |
| 21 | 16 | service MirrorService { | |
| 22 | 17 | // GetMirrorKey returns the public mirror key, generating it on first use. | |
| 23 | 18 | // Needs a non-anonymous caller. Fails with failed_precondition without | |
| @@ -127,15 +122,13 @@ message SetMirrorConfigResponse { | |||
| 127 | 122 | ||
| 128 | 123 | // MirrorSource says which configuration applies to a repository. | |
| 129 | 124 | enum MirrorSource { | |
| 130 | - | // Unspecified. | |
| 125 | + | // MIRROR_SOURCE_DISABLED: nothing applies; MIRROR_SOURCE_GLOBAL: the global | |
| 126 | + | // template; MIRROR_SOURCE_OVERRIDE: a per-repository URL; | |
| 127 | + | // MIRROR_SOURCE_EXCLUDED: opted out of the global template. | |
| 131 | 128 | MIRROR_SOURCE_UNSPECIFIED = 0; | |
| 132 | - | // Disabled. | |
| 133 | 129 | MIRROR_SOURCE_DISABLED = 1; | |
| 134 | - | // Global. | |
| 135 | 130 | MIRROR_SOURCE_GLOBAL = 2; | |
| 136 | - | // Override. | |
| 137 | 131 | MIRROR_SOURCE_OVERRIDE = 3; | |
| 138 | - | // Excluded. | |
| 139 | 132 | MIRROR_SOURCE_EXCLUDED = 4; | |
| 140 | 133 | } | |
| 141 | 134 | ||
| @@ -203,15 +196,12 @@ message SetRepoMirrorExcludedResponse { | |||
| 203 | 196 | ||
| 204 | 197 | // MirrorState is the current state of a push mirror. | |
| 205 | 198 | enum MirrorState { | |
| 206 | - | // Unspecified. | |
| 199 | + | // MIRROR_STATE_OK: last sync succeeded; MIRROR_STATE_FAILING: retrying with | |
| 200 | + | // backoff; MIRROR_STATE_PAUSED: stopped until a forced sync or a config change. | |
| 207 | 201 | MIRROR_STATE_UNSPECIFIED = 0; | |
| 208 | - | // Disabled. | |
| 209 | 202 | MIRROR_STATE_DISABLED = 1; | |
| 210 | - | // Ok. | |
| 211 | 203 | MIRROR_STATE_OK = 2; | |
| 212 | - | // Failing. | |
| 213 | 204 | MIRROR_STATE_FAILING = 3; | |
| 214 | - | // Paused. | |
| 215 | 205 | MIRROR_STATE_PAUSED = 4; | |
| 216 | 206 | } | |
| 217 | 207 | ||
| @@ -287,6 +277,9 @@ message SyncMirrorResult { | |||
| 287 | 277 | bool no_op = 3; | |
| 288 | 278 | // Status is the current mirror status. | |
| 289 | 279 | MirrorStatus status = 4; | |
| 280 | + | // Error is a human-readable failure of this repository's sync, empty on | |
| 281 | + | // success. It never contains the mirror URL. | |
| 282 | + | string error = 5; | |
| 290 | 283 | } | |
| 291 | 284 | ||
| 292 | 285 | // SyncMirrorResponse holds the result of SyncMirror. | |
| @@ -6,18 +6,13 @@ package ermite; | |||||
| 6 | 6 | ||||
| 7 | import "google/protobuf/timestamp.proto"; | 7 | import "google/protobuf/timestamp.proto"; | ||
| 8 | 8 | ||||
| 9 | - | // MirrorService configures the push mirrors of the server. Global settings | 9 | + | // MirrorService configures the push mirrors of the server. Global settings and |
| 10 | - | // and key | 10 | + | // key rotation need the admin token or a token with every scope and no |
| 11 | - | // rotation need the admin token or a token with every scope and no repository | 11 | + | // repository or branch limits; per-repository settings and SyncMirror need |
| 12 | - | // or | 12 | + | // repo:admin on the repository; status needs read access. GetMirrorKey needs a |
| 13 | - | // branch limits; per-repository settings and SyncMirror need repo:admin on | 13 | + | // non-anonymous caller. ListMirrorStatus and SyncMirror with All need owner |
| 14 | - | // the | 14 | + | // rights. No new token scope is required. Servers implementing this service |
| 15 | - | // repository; status needs read access. GetMirrorKey needs a non-anonymous | 15 | + | // list "mirror" in GetVersionResponse.features. |
| 16 | - | // caller. | |||
| 17 | - | // ListMirrorStatus and SyncMirror with All need owner rights. No new token | |||
| 18 | - | // scope | |||
| 19 | - | // is required. Servers implementing this service list "mirror" in | |||
| 20 | - | // GetVersionResponse.features. | |||
| 21 | service MirrorService { | 16 | service MirrorService { | ||
| 22 | // GetMirrorKey returns the public mirror key, generating it on first use. | 17 | // GetMirrorKey returns the public mirror key, generating it on first use. | ||
| 23 | // Needs a non-anonymous caller. Fails with failed_precondition without | 18 | // Needs a non-anonymous caller. Fails with failed_precondition without | ||
| @@ -127,15 +122,13 @@ message SetMirrorConfigResponse { | |||||
| 127 | 122 | ||||
| 128 | // MirrorSource says which configuration applies to a repository. | 123 | // MirrorSource says which configuration applies to a repository. | ||
| 129 | enum MirrorSource { | 124 | enum MirrorSource { | ||
| 130 | - | // Unspecified. | 125 | + | // MIRROR_SOURCE_DISABLED: nothing applies; MIRROR_SOURCE_GLOBAL: the global |
| 126 | + | // template; MIRROR_SOURCE_OVERRIDE: a per-repository URL; | |||
| 127 | + | // MIRROR_SOURCE_EXCLUDED: opted out of the global template. | |||
| 131 | MIRROR_SOURCE_UNSPECIFIED = 0; | 128 | MIRROR_SOURCE_UNSPECIFIED = 0; | ||
| 132 | - | // Disabled. | |||
| 133 | MIRROR_SOURCE_DISABLED = 1; | 129 | MIRROR_SOURCE_DISABLED = 1; | ||
| 134 | - | // Global. | |||
| 135 | MIRROR_SOURCE_GLOBAL = 2; | 130 | MIRROR_SOURCE_GLOBAL = 2; | ||
| 136 | - | // Override. | |||
| 137 | MIRROR_SOURCE_OVERRIDE = 3; | 131 | MIRROR_SOURCE_OVERRIDE = 3; | ||
| 138 | - | // Excluded. | |||
| 139 | MIRROR_SOURCE_EXCLUDED = 4; | 132 | MIRROR_SOURCE_EXCLUDED = 4; | ||
| 140 | } | 133 | } | ||
| 141 | 134 | ||||
| @@ -203,15 +196,12 @@ message SetRepoMirrorExcludedResponse { | |||||
| 203 | 196 | ||||
| 204 | // MirrorState is the current state of a push mirror. | 197 | // MirrorState is the current state of a push mirror. | ||
| 205 | enum MirrorState { | 198 | enum MirrorState { | ||
| 206 | - | // Unspecified. | 199 | + | // MIRROR_STATE_OK: last sync succeeded; MIRROR_STATE_FAILING: retrying with |
| 200 | + | // backoff; MIRROR_STATE_PAUSED: stopped until a forced sync or a config change. | |||
| 207 | MIRROR_STATE_UNSPECIFIED = 0; | 201 | MIRROR_STATE_UNSPECIFIED = 0; | ||
| 208 | - | // Disabled. | |||
| 209 | MIRROR_STATE_DISABLED = 1; | 202 | MIRROR_STATE_DISABLED = 1; | ||
| 210 | - | // Ok. | |||
| 211 | MIRROR_STATE_OK = 2; | 203 | MIRROR_STATE_OK = 2; | ||
| 212 | - | // Failing. | |||
| 213 | MIRROR_STATE_FAILING = 3; | 204 | MIRROR_STATE_FAILING = 3; | ||
| 214 | - | // Paused. | |||
| 215 | MIRROR_STATE_PAUSED = 4; | 205 | MIRROR_STATE_PAUSED = 4; | ||
| 216 | } | 206 | } | ||
| 217 | 207 | ||||
| @@ -287,6 +277,9 @@ message SyncMirrorResult { | |||||
| 287 | bool no_op = 3; | 277 | bool no_op = 3; | ||
| 288 | // Status is the current mirror status. | 278 | // Status is the current mirror status. | ||
| 289 | MirrorStatus status = 4; | 279 | MirrorStatus status = 4; | ||
| 280 | + | // Error is a human-readable failure of this repository's sync, empty on | |||
| 281 | + | // success. It never contains the mirror URL. | |||
| 282 | + | string error = 5; | |||
| 290 | } | 283 | } | ||
| 291 | 284 | ||||
| 292 | // SyncMirrorResponse holds the result of SyncMirror. | 285 | // SyncMirrorResponse holds the result of SyncMirror. | ||