github.com/GoogleContainerTools/skaffold@v1.39.18/pkg/skaffold/kubernetes/debugging/container_manager.go (about) 1 /* 2 Copyright 2019 The Skaffold Authors 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package debugging 18 19 import ( 20 "context" 21 "encoding/json" 22 23 v1 "k8s.io/api/core/v1" 24 "k8s.io/apimachinery/pkg/watch" 25 26 "github.com/GoogleContainerTools/skaffold/pkg/skaffold/debug/types" 27 "github.com/GoogleContainerTools/skaffold/pkg/skaffold/event" 28 eventV2 "github.com/GoogleContainerTools/skaffold/pkg/skaffold/event/v2" 29 "github.com/GoogleContainerTools/skaffold/pkg/skaffold/kubernetes" 30 "github.com/GoogleContainerTools/skaffold/pkg/skaffold/output/log" 31 ) 32 33 var ( 34 // For testing 35 notifyDebuggingContainerStarted = event.DebuggingContainerStarted 36 notifyDebuggingContainerTerminated = event.DebuggingContainerTerminated 37 debuggingContainerStartedV2 = eventV2.DebuggingContainerStarted 38 debuggingContainerTerminatedV2 = eventV2.DebuggingContainerTerminated 39 ) 40 41 type ContainerManager struct { 42 podWatcher kubernetes.PodWatcher 43 active map[string]string // set of containers that have been notified 44 events chan kubernetes.PodEvent 45 stopWatcher func() 46 namespaces *[]string 47 kubeContext string 48 } 49 50 func NewContainerManager(podSelector kubernetes.PodSelector, namespaces *[]string, kubeContext string) *ContainerManager { 51 // Create the channel here as Stop() may be called before Start() when a build fails, thus 52 // avoiding the possibility of closing a nil channel. Channels are cheap. 53 return &ContainerManager{ 54 podWatcher: kubernetes.NewPodWatcher(podSelector), 55 active: map[string]string{}, 56 events: make(chan kubernetes.PodEvent), 57 stopWatcher: func() {}, 58 namespaces: namespaces, 59 kubeContext: kubeContext, 60 } 61 } 62 63 func (d *ContainerManager) Start(ctx context.Context) error { 64 if d == nil { 65 // debug mode probably not enabled 66 return nil 67 } 68 69 d.podWatcher.Register(d.events) 70 stopWatcher, err := d.podWatcher.Start(ctx, d.kubeContext, *d.namespaces) 71 if err != nil { 72 return err 73 } 74 75 go func() { 76 defer stopWatcher() 77 l := log.Entry(ctx) 78 defer l.Tracef("containerManager: cease waiting for pod events") 79 l.Tracef("containerManager: waiting for pod events") 80 for { 81 select { 82 case <-ctx.Done(): 83 l.Tracef("containerManager: context canceled, ignoring") 84 case evt, ok := <-d.events: 85 if !ok { 86 l.Tracef("containerManager: channel closed, returning") 87 return 88 } 89 90 d.checkPod(evt.Type, evt.Pod) 91 } 92 } 93 }() 94 95 return nil 96 } 97 98 func (d *ContainerManager) Stop() { 99 // if nil then debug mode probably not enabled 100 if d == nil { 101 return 102 } 103 d.podWatcher.Deregister(d.events) 104 } 105 106 func (d *ContainerManager) Name() string { 107 return "Debug Manager" 108 } 109 110 func (d *ContainerManager) checkPod(evtType watch.EventType, pod *v1.Pod) { 111 debugConfigString, found := pod.Annotations[types.DebugConfig] 112 if !found { 113 return 114 } 115 var configurations map[string]types.ContainerDebugConfiguration 116 if err := json.Unmarshal([]byte(debugConfigString), &configurations); err != nil { 117 log.Entry(context.TODO()).Warnf("Unable to parse debug-config for pod %s/%s: '%s'", pod.Namespace, pod.Name, debugConfigString) 118 return 119 } 120 for _, c := range pod.Status.ContainerStatuses { 121 // only examine debuggable containers 122 if config, found := configurations[c.Name]; found { 123 key := pod.Namespace + "/" + pod.Name + "/" + c.Name 124 // only notify of first appearance or disappearance 125 _, seen := d.active[key] 126 switch { 127 case evtType != watch.Deleted && c.State.Running != nil && !seen: 128 d.active[key] = key 129 notifyDebuggingContainerStarted( 130 pod.Name, 131 c.Name, 132 pod.Namespace, 133 config.Artifact, 134 config.Runtime, 135 config.WorkingDir, 136 config.Ports) 137 debuggingContainerStartedV2(pod.Name, c.Name, pod.Namespace, config.Artifact, config.Runtime, config.WorkingDir, config.Ports) 138 139 case (evtType == watch.Deleted || c.State.Terminated != nil) && seen: 140 delete(d.active, key) 141 notifyDebuggingContainerTerminated(pod.Name, c.Name, pod.Namespace, 142 config.Artifact, 143 config.Runtime, 144 config.WorkingDir, 145 config.Ports) 146 debuggingContainerTerminatedV2(pod.Name, c.Name, pod.Namespace, config.Artifact, config.Runtime, config.WorkingDir, config.Ports) 147 } 148 } 149 } 150 }