github.com/tilt-dev/tilt@v0.33.15-0.20240515162809-0a22ed45d8a0/internal/controllers/core/dockercomposeservice/logs.go (about) 1 package dockercomposeservice 2 3 import ( 4 "context" 5 "fmt" 6 "time" 7 8 apierrors "k8s.io/apimachinery/pkg/api/errors" 9 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 10 "k8s.io/apimachinery/pkg/types" 11 "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" 12 "sigs.k8s.io/controller-runtime/pkg/reconcile" 13 14 "github.com/tilt-dev/tilt/internal/controllers/apicmp" 15 "github.com/tilt-dev/tilt/pkg/apis/core/v1alpha1" 16 ) 17 18 // Each DockerComposeService object owns a DockerComposeLogStream object of the same name. 19 // 20 // NOTE(nick): I think this might be the wrong API. Currently we model DockerCompose as two objects: 21 // 22 // 1. A service object with a Spec that tells us how to create the service and 23 // a Status that contains the container state 24 // 2. A log object with a Spec that tells us how to watch logs. 25 // 26 // I think the better way to model this would be to more clearly separate "spinning up a service" 27 // from "watching a service", so we'd actually have: 28 // 29 // 1. A service object with a Spec that tells us how to create the service and 30 // a Status that tells us whether the creation succeeded. 31 // 2. A monitor object with a status that contains both the container state and the log state. 32 // 33 // But moving to this system will be easier once everything is in the API server. 34 func (r *Reconciler) manageOwnedLogStream(ctx context.Context, nn types.NamespacedName, obj *v1alpha1.DockerComposeService) (reconcile.Result, error) { 35 if obj != nil && (obj.Status.ApplyError != "" || obj.Status.ContainerID == "") { 36 isDisabled := obj.Status.DisableStatus != nil && 37 obj.Status.DisableStatus.State == v1alpha1.DisableStateDisabled 38 if !isDisabled { 39 // If the DockerCompose is in an error state or hasn't deployed anything, 40 // don't reconcile the log object. 41 return reconcile.Result{}, nil 42 } 43 } 44 45 var existing v1alpha1.DockerComposeLogStream 46 err := r.ctrlClient.Get(ctx, nn, &existing) 47 isNotFound := apierrors.IsNotFound(err) 48 if err != nil && !isNotFound { 49 return reconcile.Result{}, 50 fmt.Errorf("failed to fetch managed DockerComposeLogStream objects for DockerComposeService %s: %v", 51 nn.Name, err) 52 } 53 54 desired, err := r.toDesiredLogStream(obj) 55 if err != nil { 56 return reconcile.Result{}, fmt.Errorf("generating dockercomposelogstream: %v", err) 57 } 58 59 if isNotFound { 60 if desired == nil { 61 return reconcile.Result{}, nil // Nothing to do. 62 } 63 64 err := r.ctrlClient.Create(ctx, desired) 65 if err != nil { 66 if apierrors.IsAlreadyExists(err) { 67 return reconcile.Result{RequeueAfter: time.Second}, nil 68 } 69 return reconcile.Result{}, fmt.Errorf("creating dockercomposelogstream: %v", err) 70 } 71 return reconcile.Result{}, nil 72 } 73 74 if desired == nil { 75 err := r.ctrlClient.Delete(ctx, &existing) 76 if err != nil && !apierrors.IsNotFound(err) { 77 return reconcile.Result{}, fmt.Errorf("deleting dockercomposelogstream: %v", err) 78 } 79 return reconcile.Result{}, nil 80 } 81 82 if !apicmp.DeepEqual(existing.Spec, desired.Spec) || 83 !apicmp.DeepEqual(existing.ObjectMeta.Annotations, desired.ObjectMeta.Annotations) { 84 existing = *existing.DeepCopy() 85 existing.ObjectMeta.Annotations = desired.ObjectMeta.Annotations 86 existing.Spec = desired.Spec 87 err = r.ctrlClient.Update(ctx, &existing) 88 if err != nil { 89 if apierrors.IsConflict(err) { 90 return reconcile.Result{RequeueAfter: time.Second}, nil 91 } 92 return reconcile.Result{}, fmt.Errorf("updating dockercomposelogstream: %v", err) 93 } 94 } 95 96 return reconcile.Result{}, nil 97 } 98 99 // Construct the desired DockerComposeLogStream 100 func (r *Reconciler) toDesiredLogStream(obj *v1alpha1.DockerComposeService) (*v1alpha1.DockerComposeLogStream, error) { 101 if obj == nil { 102 return nil, nil 103 } 104 105 if obj.Status.DisableStatus != nil && obj.Status.DisableStatus.State == v1alpha1.DisableStateDisabled { 106 return nil, nil 107 } 108 109 desired := &v1alpha1.DockerComposeLogStream{ 110 ObjectMeta: metav1.ObjectMeta{ 111 Name: obj.Name, 112 Annotations: map[string]string{ 113 v1alpha1.AnnotationManifest: obj.Annotations[v1alpha1.AnnotationManifest], 114 v1alpha1.AnnotationSpanID: obj.Annotations[v1alpha1.AnnotationSpanID], 115 }, 116 }, 117 Spec: v1alpha1.DockerComposeLogStreamSpec{ 118 Service: obj.Spec.Service, 119 Project: obj.Spec.Project, 120 }, 121 } 122 123 err := controllerutil.SetControllerReference(obj, desired, r.ctrlClient.Scheme()) 124 if err != nil { 125 return nil, err 126 } 127 return desired, nil 128 }