github.com/tilt-dev/tilt@v0.33.15-0.20240515162809-0a22ed45d8a0/internal/controllers/core/dockercomposeservice/project.go (about) 1 package dockercomposeservice 2 3 import ( 4 "context" 5 6 "github.com/tilt-dev/tilt/internal/controllers/apicmp" 7 "github.com/tilt-dev/tilt/internal/dockercompose" 8 "github.com/tilt-dev/tilt/pkg/apis/core/v1alpha1" 9 "github.com/tilt-dev/tilt/pkg/logger" 10 ) 11 12 // Sync all the project watches with the dockercompose objects 13 // we're currently tracking. 14 func (r *Reconciler) manageOwnedProjectWatches(ctx context.Context) { 15 r.mu.Lock() 16 defer r.mu.Unlock() 17 18 running := map[string]bool{} 19 for key := range r.projectWatches { 20 running[key] = true 21 } 22 23 owned := map[string]bool{} 24 for _, result := range r.results { 25 hash := result.ProjectHash 26 owned[hash] = true 27 28 if hash != "" && !running[hash] { 29 ctx, cancel := context.WithCancel(ctx) 30 pw := &ProjectWatch{ 31 ctx: ctx, 32 cancel: cancel, 33 project: result.Spec.Project, 34 hash: hash, 35 } 36 r.projectWatches[hash] = pw 37 go r.runProjectWatch(pw) 38 running[hash] = true 39 } 40 } 41 42 for key := range r.projectWatches { 43 if !owned[key] { 44 r.projectWatches[key].cancel() 45 delete(r.projectWatches, key) 46 } 47 } 48 } 49 50 // Stream events from the docker-compose project and 51 // fan them out to each service in the project. 52 func (r *Reconciler) runProjectWatch(pw *ProjectWatch) { 53 defer func() { 54 r.mu.Lock() 55 delete(r.projectWatches, pw.hash) 56 r.mu.Unlock() 57 pw.cancel() 58 }() 59 60 ctx := pw.ctx 61 project := pw.project 62 ch, err := r.dcc.StreamEvents(ctx, project) 63 if err != nil { 64 // TODO(nick): Figure out where this error should be published. 65 return 66 } 67 68 for { 69 select { 70 case evtJson, ok := <-ch: 71 if !ok { 72 return 73 } 74 evt, err := dockercompose.EventFromJsonStr(evtJson) 75 if err != nil { 76 logger.Get(ctx).Debugf("[dcwatch] failed to unmarshal dc event '%s' with err: %v", evtJson, err) 77 continue 78 } 79 80 if evt.Type != dockercompose.TypeContainer { 81 continue 82 } 83 84 containerJSON, err := r.dc.ContainerInspect(ctx, evt.ID) 85 if err != nil { 86 logger.Get(ctx).Debugf("[dcwatch] inspecting container: %v", err) 87 continue 88 } 89 90 if containerJSON.ContainerJSONBase == nil || containerJSON.ContainerJSONBase.State == nil { 91 logger.Get(ctx).Debugf("[dcwatch] inspecting container: no state found") 92 continue 93 } 94 95 cState := containerJSON.ContainerJSONBase.State 96 dcState := dockercompose.ToContainerState(cState) 97 r.recordContainerEvent(evt, dcState) 98 99 case <-ctx.Done(): 100 return 101 } 102 } 103 } 104 105 // Record the container event and re-reconcile the dockercompose service. 106 func (r *Reconciler) recordContainerEvent(evt dockercompose.Event, state *v1alpha1.DockerContainerState) { 107 r.mu.Lock() 108 defer r.mu.Unlock() 109 110 result, ok := r.resultsByServiceName[evt.Service] 111 if !ok { 112 return 113 } 114 115 if apicmp.DeepEqual(state, result.Status.ContainerState) { 116 return 117 } 118 119 // No need to copy because this is a value struct. 120 update := result.Status 121 update.ContainerID = evt.ID 122 update.ContainerState = state 123 result.Status = update 124 r.requeuer.Add(result.Name) 125 }