11package targetselector
22
33import (
4+ "context"
5+ "github.com/loft-sh/devspace/pkg/util/stringutil"
6+ metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
47 "sort"
58 "strings"
69 "sync"
@@ -13,10 +16,14 @@ import (
1316)
1417
1518// NewUntilNewestRunningWaitingStrategy creates a new waiting strategy
16- func NewUntilNewestRunningWaitingStrategy (initialDelay time.Duration ) WaitingStrategy {
19+ func NewUntilNewestRunningWaitingStrategy (initialDelay time.Duration , client kubectl. Client , namespace string ) WaitingStrategy {
1720 return & untilNewestRunning {
1821 initialDelay : time .Now ().Add (initialDelay ),
19- lastWarning : time .Now ().Add (initialDelay ),
22+ podInfoPrinter : & PodInfoPrinter {
23+ lastWarning : time .Now ().Add (initialDelay ),
24+ namespace : namespace ,
25+ client : client ,
26+ },
2027 }
2128}
2229
@@ -25,27 +32,26 @@ func NewUntilNewestRunningWaitingStrategy(initialDelay time.Duration) WaitingStr
2532type untilNewestRunning struct {
2633 initialDelay time.Time
2734
28- lastMutex sync.Mutex
29- lastWarning time.Time
35+ podInfoPrinter * PodInfoPrinter
3036}
3137
3238func (u * untilNewestRunning ) SelectPod (pods []* v1.Pod , log log.Logger ) (bool , * v1.Pod , error ) {
3339 now := time .Now ()
3440 if now .Before (u .initialDelay ) {
3541 return false , nil , nil
3642 } else if len (pods ) == 0 {
37- u .printNotFoundWarning (log )
43+ u .podInfoPrinter . PrintNotFoundWarning (log )
3844 return false , nil , nil
3945 }
4046
4147 sort .Slice (pods , func (i , j int ) bool {
4248 return selector .SortPodsByNewest (pods , i , j )
4349 })
4450 if HasPodProblem (pods [0 ]) {
45- u .printPodWarning (pods [0 ], log )
51+ u .podInfoPrinter . PrintPodWarning (pods [0 ], log )
4652 return false , nil , nil
4753 } else if kubectl .GetPodStatus (pods [0 ]) != "Running" {
48- u .printPodInfo (pods [0 ], log )
54+ u .podInfoPrinter . PrintPodInfo (pods [0 ], log )
4955 return false , nil , nil
5056 }
5157
@@ -57,56 +63,148 @@ func (u *untilNewestRunning) SelectContainer(containers []*selector.SelectedPodC
5763 if now .Before (u .initialDelay ) {
5864 return false , nil , nil
5965 } else if len (containers ) == 0 {
60- u .printNotFoundWarning (log )
66+ u .podInfoPrinter . PrintNotFoundWarning (log )
6167 return false , nil , nil
6268 }
6369
6470 sort .Slice (containers , func (i , j int ) bool {
6571 return selector .SortContainersByNewest (containers , i , j )
6672 })
6773 if HasPodProblem (containers [0 ].Pod ) {
68- u .printPodWarning (containers [0 ].Pod , log )
74+ u .podInfoPrinter . PrintPodWarning (containers [0 ].Pod , log )
6975 return false , nil , nil
7076 } else if ! IsContainerRunning (containers [0 ]) {
71- u .printPodInfo (containers [0 ].Pod , log )
77+ u .podInfoPrinter . PrintPodInfo (containers [0 ].Pod , log )
7278 return false , nil , nil
7379 }
7480
7581 return true , containers [0 ], nil
7682}
7783
78- func (u * untilNewestRunning ) printPodInfo (pod * v1.Pod , log log.Logger ) {
84+ type PodInfoPrinter struct {
85+ lastMutex sync.Mutex
86+ lastWarning time.Time
87+
88+ namespace string
89+ client kubectl.Client
90+ shownEvents []string
91+ }
92+
93+ func (u * PodInfoPrinter ) PrintPodInfo (pod * v1.Pod , log log.Logger ) {
7994 u .lastMutex .Lock ()
8095 defer u .lastMutex .Unlock ()
8196
8297 if time .Since (u .lastWarning ) > time .Second * 10 {
8398 status := kubectl .GetPodStatus (pod )
84- log .Infof ("DevSpace is waiting, because Pod %s/%s has status: %s" , pod .Namespace , pod .Name , status )
99+ log .Infof ("DevSpace is waiting, because Pod %s has status: %s" , pod .Name , status )
100+
101+ u .shownEvents = displayWarnings (relevantObjectsFromPod (pod ), pod .Namespace , u .client , u .shownEvents , log )
85102 u .lastWarning = time .Now ()
86103 }
87104}
88105
89- func (u * untilNewestRunning ) printNotFoundWarning (log log.Logger ) {
106+ func (u * PodInfoPrinter ) PrintNotFoundWarning (log log.Logger ) {
90107 u .lastMutex .Lock ()
91108 defer u .lastMutex .Unlock ()
92109
93110 if time .Since (u .lastWarning ) > time .Second * 10 {
94111 log .Warnf ("DevSpace still couldn't find any Pods that match the selector. DevSpace will continue waiting, but this operation might timeout" )
112+
113+ u .shownEvents = displayWarnings ([]relevantObject {
114+ {
115+ Kind : "StatefulSet" ,
116+ },
117+ {
118+ Kind : "Deployment" ,
119+ },
120+ {
121+ Kind : "ReplicaSet" ,
122+ },
123+ }, u .namespace , u .client , u .shownEvents , log )
95124 u .lastWarning = time .Now ()
96125 }
97126}
98127
99- func (u * untilNewestRunning ) printPodWarning (pod * v1.Pod , log log.Logger ) {
128+ func (u * PodInfoPrinter ) PrintPodWarning (pod * v1.Pod , log log.Logger ) {
100129 u .lastMutex .Lock ()
101130 defer u .lastMutex .Unlock ()
102131
103132 if time .Since (u .lastWarning ) > time .Second * 10 {
104133 status := kubectl .GetPodStatus (pod )
105- log .Warnf ("Pod %s/%s has critical status: %s. DevSpace will continue waiting, but this operation might timeout" , pod . Namespace , pod .Name , status )
134+ log .Warnf ("Pod %s has critical status: %s. DevSpace will continue waiting, but this operation might timeout" , pod .Name , status )
106135 u .lastWarning = time .Now ()
107136 }
108137}
109138
139+ type relevantObject struct {
140+ Kind string
141+ Name string
142+ UID string
143+ }
144+
145+ func displayWarnings (relevantObjects []relevantObject , namespace string , client kubectl.Client , filter []string , log log.Logger ) []string {
146+ events , err := client .KubeClient ().CoreV1 ().Events (namespace ).List (context .TODO (), metav1.ListOptions {})
147+ if err != nil {
148+ log .Debugf ("Error retrieving pod events: %v" , err )
149+ return nil
150+ }
151+
152+ sort .Slice (events .Items , func (i , j int ) bool {
153+ return events .Items [i ].CreationTimestamp .Unix () > events .Items [j ].CreationTimestamp .Unix ()
154+ })
155+ for _ , event := range events .Items {
156+ if event .Type != "Warning" {
157+ continue
158+ } else if stringutil .Contains (filter , event .Name ) {
159+ continue
160+ } else if ! eventMatches (& event , relevantObjects ) {
161+ continue
162+ }
163+
164+ log .Warnf ("%s %s: %s (%s)" , event .InvolvedObject .Kind , event .InvolvedObject .Name , event .Message , event .Reason )
165+ filter = append (filter , event .Name )
166+ }
167+
168+ return filter
169+ }
170+
171+ func relevantObjectsFromPod (pod * v1.Pod ) []relevantObject {
172+ // search for persistent volume claims
173+ objects := []relevantObject {
174+ {
175+ Kind : "Pod" ,
176+ Name : pod .Name ,
177+ UID : string (pod .UID ),
178+ },
179+ }
180+ for _ , v := range pod .Spec .Volumes {
181+ if v .PersistentVolumeClaim != nil {
182+ objects = append (objects , relevantObject {
183+ Kind : "PersistentVolumeClaim" ,
184+ Name : v .PersistentVolumeClaim .ClaimName ,
185+ })
186+ }
187+
188+ }
189+ return objects
190+ }
191+
192+ func eventMatches (event * v1.Event , objects []relevantObject ) bool {
193+ for _ , o := range objects {
194+ if o .Name != "" && event .InvolvedObject .Name != o .Name {
195+ continue
196+ } else if o .Kind != "" && event .InvolvedObject .Kind != o .Kind {
197+ continue
198+ } else if o .UID != "" && string (event .InvolvedObject .UID ) != o .UID {
199+ continue
200+ }
201+
202+ return true
203+ }
204+
205+ return false
206+ }
207+
110208func IsContainerRunning (container * selector.SelectedPodContainer ) bool {
111209 if container .Pod .DeletionTimestamp != nil {
112210 return false
0 commit comments