▾ added internal/ci/ci.go +28 -0 Viewed
new file mode 100644 @@ -0,0 +1,28 @@ 1 + // Package ci binds the guest runner to cmd/ciguest: one runner, one watch 2 + // loop, one fatal path. 3 + package ci 4 + 5 + import ( 6 + "context" 7 + "fmt" 8 + "os" 9 + 10 + "dev.rickub.com/rick/portal/internal/watcher" 11 + ) 12 + 13 + // Runner is the guest watch loop bound to a context. 14 + type Runner struct { ctx context . Context } 15 + 16 + // New returns a runner bound to ctx. 17 + func New ( ctx context . Context ) * Runner { return & Runner { ctx : ctx } } 18 + 19 + // Watch drives the loop on the default 30s poll until the context is 20 + // cancelled or a poll fails. 21 + func ( r * Runner ) Watch ( ) error { return watcher . Watch ( "30s" ) } 22 + 23 + // Fatal reports err on stderr and exits non-zero, as the supervisor 24 + // expects of a fail-fast guest. 25 + func Fatal ( err error ) { 26 + fmt . Fprintln ( os . Stderr , "ciguest:" , err ) 27 + os . Exit ( 1 ) 28 + }
new file mode 100644 @@ -0,0 +1,28 @@ 1 + // Package ci binds the guest runner to cmd/ciguest: one runner, one watch 2 + // loop, one fatal path. 3 + package ci 4 + 5 + import ( 6 + "context" 7 + "fmt" 8 + "os" 9 + 10 + "dev.rickub.com/rick/portal/internal/watcher" 11 + ) 12 + 13 + // Runner is the guest watch loop bound to a context. 14 + type Runner struct { ctx context . Context } 15 + 16 + // New returns a runner bound to ctx. 17 + func New ( ctx context . Context ) * Runner { return & Runner { ctx : ctx } } 18 + 19 + // Watch drives the loop on the default 30s poll until the context is 20 + // cancelled or a poll fails. 21 + func ( r * Runner ) Watch ( ) error { return watcher . Watch ( "30s" ) } 22 + 23 + // Fatal reports err on stderr and exits non-zero, as the supervisor 24 + // expects of a fail-fast guest. 25 + func Fatal ( err error ) { 26 + fmt . Fprintln ( os . Stderr , "ciguest:" , err ) 27 + os . Exit ( 1 ) 28 + }
▾ modified internal/watcher/watch.go +4 -14 Viewed
@@ -1,6 +1,10 @@ 1 1 package watcher 2 2 3 3 // Watch drives the guest loop: poll, apply, report. 4 + // 5 + // Fail fast: a silent watcher is worse than a dead one, so any poll or 6 + // apply error is returned to the caller (cmd/ciguest exits non-zero and 7 + // the supervisor restarts us) instead of being logged and forgotten. 4 8 func Watch ( interval string ) error { 5 9 tick := parseInterval ( interval ) 6 10 for range tick { @@ -10,17 +14,3 @@ func Watch(interval string) error { 10 14 } 11 15 return nil 12 16 } 13 - // iteration 1 14 - // iteration 2 15 - // iteration 3 16 - // iteration 4 17 - // iteration 5 18 - // iteration 6 19 - // iteration 7 20 - // iteration 8 21 - // iteration 9 22 - // iteration 10 23 - // iteration 11 24 - // iteration 12 25 - // iteration 13 26 - // iteration 14
@@ -1,6 +1,10 @@ 1 package watcher 1 package watcher 2 2 3 // Watch drives the guest loop: poll, apply, report. 3 // Watch drives the guest loop: poll, apply, report. 4 + // 5 + // Fail fast: a silent watcher is worse than a dead one, so any poll or 6 + // apply error is returned to the caller (cmd/ciguest exits non-zero and 7 + // the supervisor restarts us) instead of being logged and forgotten. 4 func Watch ( interval string ) error { 8 func Watch ( interval string ) error { 5 tick := parseInterval ( interval ) 9 tick := parseInterval ( interval ) 6 for range tick { 10 for range tick { @@ -10,17 +14,3 @@ func Watch(interval string) error { 10 } 14 } 11 return nil 15 return nil 12 } 16 } 13 - // iteration 1 14 - // iteration 2 15 - // iteration 3 16 - // iteration 4 17 - // iteration 5 18 - // iteration 6 19 - // iteration 7 20 - // iteration 8 21 - // iteration 9 22 - // iteration 10 23 - // iteration 11 24 - // iteration 12 25 - // iteration 13 26 - // iteration 14
▾ added internal/watcher/watch_test.go +20 -0 Viewed
new file mode 100644 @@ -0,0 +1,20 @@ 1 + package watcher 2 + 3 + import "testing" 4 + 5 + func TestParseIntervalFallsBackToDefaultOnGarbage ( t * testing . T ) { 6 + for _ , in := range [ ] string { "" , "nonsense" , "0s" , "-5m" } { 7 + if parseInterval ( in ) == nil { 8 + t . Fatalf ( "parseInterval(%q) returned nil" , in ) 9 + } 10 + } 11 + } 12 + 13 + func TestApplyIsIdempotent ( t * testing . T ) { 14 + if err := apply ( ) ; err != nil { 15 + t . Fatalf ( "first apply: %v" , err ) 16 + } 17 + if err := apply ( ) ; err != nil { 18 + t . Fatalf ( "second apply: %v" , err ) 19 + } 20 + }
new file mode 100644 @@ -0,0 +1,20 @@ 1 + package watcher 2 + 3 + import "testing" 4 + 5 + func TestParseIntervalFallsBackToDefaultOnGarbage ( t * testing . T ) { 6 + for _ , in := range [ ] string { "" , "nonsense" , "0s" , "-5m" } { 7 + if parseInterval ( in ) == nil { 8 + t . Fatalf ( "parseInterval(%q) returned nil" , in ) 9 + } 10 + } 11 + } 12 + 13 + func TestApplyIsIdempotent ( t * testing . T ) { 14 + if err := apply ( ) ; err != nil { 15 + t . Fatalf ( "first apply: %v" , err ) 16 + } 17 + if err := apply ( ) ; err != nil { 18 + t . Fatalf ( "second apply: %v" , err ) 19 + } 20 + }