16
16
package main
17
17
18
18
import (
19
+ "bufio"
19
20
"context"
20
21
"encoding/json"
21
22
"errors"
@@ -313,6 +314,9 @@ func govc(ctx context.Context, args ...string) error {
313
314
fmt .Fprintf (os .Stderr , "$ govc %v\n " , strings .Join (args , " " ))
314
315
out , err := exec .CommandContext (ctx , "govc" , args ... ).CombinedOutput ()
315
316
if err != nil {
317
+ if isFileSystemReadOnly () {
318
+ out = append (out , "; filesystem is read-only" ... )
319
+ }
316
320
return fmt .Errorf ("govc %s ...: %v, %s" , args [0 ], err , out )
317
321
}
318
322
return nil
@@ -372,7 +376,7 @@ func getState(ctx context.Context) (*State, error) {
372
376
373
377
var hosts elementList
374
378
if err := govcJSONDecode (ctx , & hosts , "ls" , "-json" , "/MacStadium-ATL/host/MacMini_Cluster" ); err != nil {
375
- return nil , fmt .Errorf ("Reading /MacStadium-ATL/host/MacMini_Cluster: %v" , err )
379
+ return nil , fmt .Errorf ("getState: reading /MacStadium-ATL/host/MacMini_Cluster: %v" , err )
376
380
}
377
381
for _ , h := range hosts .Elements {
378
382
if h .Object .Self .Type == "HostSystem" {
@@ -384,7 +388,7 @@ func getState(ctx context.Context) (*State, error) {
384
388
385
389
var vms elementList
386
390
if err := govcJSONDecode (ctx , & vms , "ls" , "-json" , "/MacStadium-ATL/vm" ); err != nil {
387
- return nil , fmt .Errorf ("Reading /MacStadium-ATL/vm: %v" , err )
391
+ return nil , fmt .Errorf ("getState: reading /MacStadium-ATL/vm: %v" , err )
388
392
}
389
393
for _ , h := range vms .Elements {
390
394
if h .Object .Self .Type != "VirtualMachine" {
@@ -528,6 +532,8 @@ var status struct {
528
532
lastCheck time.Time
529
533
lastLog string
530
534
lastState * State
535
+ warnings []string
536
+ errors []string
531
537
}
532
538
533
539
func init () {
@@ -581,14 +587,20 @@ func autoAdjust() {
581
587
582
588
st , err := getState (ctx )
583
589
if err != nil {
584
- log .Printf ("getting VMWare state: %v" , err )
590
+ status .Lock ()
591
+ status .errors = []string {err .Error ()}
592
+ status .Unlock ()
593
+ log .Print (err )
585
594
return
586
595
}
596
+ var warnings , errors []string
587
597
defer func () {
588
598
// Set status.lastState once we're now longer using it.
589
599
if st != nil {
590
600
status .Lock ()
591
601
status .lastState = st
602
+ status .warnings = warnings
603
+ status .errors = errors
592
604
status .Unlock ()
593
605
}
594
606
}()
@@ -597,12 +609,14 @@ func autoAdjust() {
597
609
req = req .WithContext (ctx )
598
610
res , err := http .DefaultClient .Do (req )
599
611
if err != nil {
612
+ errors = append (errors , fmt .Sprintf ("getting /status/reverse.json from coordinator: %v" , err ))
600
613
log .Printf ("getting reverse status: %v" , err )
601
614
return
602
615
}
603
616
defer res .Body .Close ()
604
617
var rstat types.ReverseBuilderStatus
605
618
if err := json .NewDecoder (res .Body ).Decode (& rstat ); err != nil {
619
+ errors = append (errors , fmt .Sprintf ("decoding /status/reverse.json from coordinator: %v" , err ))
606
620
log .Printf ("decoding reverse.json: %v" , err )
607
621
return
608
622
}
@@ -618,6 +632,7 @@ func autoAdjust() {
618
632
}
619
633
620
634
// Destroy running VMs that appear to be dead and not connected to the coordinator.
635
+ // TODO: do these all concurrently.
621
636
dirty := false
622
637
for name , vi := range st .VMInfo {
623
638
if vi .BootTime .After (time .Now ().Add (- 3 * time .Minute )) {
@@ -632,18 +647,22 @@ func autoAdjust() {
632
647
// Look it up by its slot name instead.
633
648
rh = revHost [vi .SlotName ]
634
649
}
635
- if rh == nil { // || (!rh.Busy && rh.ConnectedSec > 50 && rh.HostType == "host-darwin-10_12") {
650
+ if rh == nil {
636
651
log .Printf ("Destroying VM %q unknown to coordinator..." , name )
637
652
err := govc (ctx , "vm.destroy" , name )
638
653
log .Printf ("vm.destroy(%q) = %v" , name , err )
639
654
dirty = true
655
+ if err != nil {
656
+ warnings = append (warnings , fmt .Sprintf ("vm.destroy(%q) = %v" , name , err ))
657
+ }
640
658
}
641
659
}
642
660
for {
643
661
if dirty {
644
662
st , err = getState (ctx )
645
663
if err != nil {
646
- log .Printf ("getState: %v" , err )
664
+ errors = append (errors , err .Error ())
665
+ log .Print (err )
647
666
return
648
667
}
649
668
}
@@ -661,7 +680,9 @@ func autoAdjust() {
661
680
dedupLogf ("Have capacity for %d more Mac VMs; creating requested 10.%d ..." , canCreate , ver )
662
681
slotName , err := st .CreateMac (ctx , ver )
663
682
if err != nil {
664
- log .Printf ("Error creating 10.%d: %v" , ver , err )
683
+ errStr := fmt .Sprintf ("Error creating 10.%d: %v" , ver , err )
684
+ errors = append (errors , errStr )
685
+ log .Print (errStr )
665
686
return
666
687
}
667
688
log .Printf ("Created 10.%d VM on %q" , ver , slotName )
@@ -715,10 +736,14 @@ func handleStatus(w http.ResponseWriter, r *http.Request) {
715
736
LastCheck string
716
737
LastLog string
717
738
LastState * State
739
+ Warnings []string
740
+ Errors []string
718
741
}{
719
742
LastCheck : status .lastCheck .UTC ().Format (time .RFC3339 ),
720
743
LastLog : status .lastLog ,
721
744
LastState : status .lastState ,
745
+ Warnings : status .warnings ,
746
+ Errors : status .errors ,
722
747
}
723
748
j , _ := json .MarshalIndent (res , "" , "\t " )
724
749
w .Write (j )
@@ -823,3 +848,22 @@ func (h onlyAtRoot) ServeHTTP(w http.ResponseWriter, r *http.Request) {
823
848
}
824
849
h .h .ServeHTTP (w , r )
825
850
}
851
+
852
+ func isFileSystemReadOnly () bool {
853
+ f , err := os .Open ("/proc/mounts" )
854
+ if err != nil {
855
+ return false
856
+ }
857
+ defer f .Close ()
858
+ // Look for line:
859
+ // /dev/sda1 / ext4 rw,relatime,errors=remount-ro,data=ordered 0 0
860
+ bs := bufio .NewScanner (f )
861
+ for bs .Scan () {
862
+ f := strings .Fields (bs .Text ())
863
+ mountPoint , state := f [1 ], f [3 ]
864
+ if mountPoint == "/" {
865
+ return strings .HasPrefix (state , "ro," )
866
+ }
867
+ }
868
+ return false
869
+ }
0 commit comments