github.com/aporeto-inc/trireme-lib@v10.358.0+incompatible/monitor/internal/k8s/event_handler.go (about) 1 package k8smonitor 2 3 import ( 4 "context" 5 "fmt" 6 7 "go.aporeto.io/enforcerd/internal/extractors/containermetadata" 8 "go.aporeto.io/enforcerd/trireme-lib/common" 9 "go.aporeto.io/enforcerd/trireme-lib/monitor/external" 10 11 "go.uber.org/zap" 12 13 corev1 "k8s.io/api/core/v1" 14 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 15 ) 16 17 var _ external.ReceiveEvents = &K8sMonitor{} 18 19 func (m *K8sMonitor) isCniInstalledOrRuncProxyStarted() bool { 20 m.extMonitorStartedLock.RLock() 21 defer m.extMonitorStartedLock.RUnlock() 22 return m.cniInstalledOrRuncProxyStarted 23 } 24 25 // SenderReady will be called by the sender to notify the receiver that the sender 26 // is now ready to send events. 27 func (m *K8sMonitor) SenderReady() { 28 m.extMonitorStartedLock.Lock() 29 m.cniInstalledOrRuncProxyStarted = true 30 m.extMonitorStartedLock.Unlock() 31 close(m.cniInstalledOrRuncProxyStartedCh) 32 zap.L().Debug("K8sMonitor: CNI plugin is installed and configured or runc-proxy has started") 33 } 34 35 // Event will receive event `data` for processing a common.Event in the monitor. 36 // The sent data is implementation specific - therefore it has no type in the interface. 37 // If the sent data is of an unexpected type, its implementor must return an error 38 // indicating so. 39 func (m *K8sMonitor) Event(ctx context.Context, ev common.Event, data interface{}) error { 40 // the data is expected to be of type 41 kmd, ok := data.(containermetadata.CommonKubernetesContainerMetadata) 42 if !ok { 43 return fmt.Errorf("K8sMonitor: invalid data type: %T", data) 44 } 45 46 switch ev { 47 case common.EventStart: 48 if err := m.startEvent(ctx, kmd, 0); err != nil { 49 // TODO: handle retries that we can handle 50 return fmt.Errorf("K8sMonitor: startEvent: %s", err) 51 } 52 case common.EventDestroy: 53 if err := m.destroyEvent(ctx, kmd); err != nil { 54 return fmt.Errorf("K8sMonitor: destroyEvent: %s", err) 55 } 56 default: 57 return fmt.Errorf("K8sMonitor: unexpected event %s", ev) 58 } 59 return nil 60 } 61 62 type startEventFunc func(context.Context, containermetadata.CommonKubernetesContainerMetadata, uint) error 63 64 func (m *K8sMonitor) startEvent(ctx context.Context, kmd containermetadata.CommonKubernetesContainerMetadata, retry uint) error { 65 switch kmd.Kind() { 66 case containermetadata.PodSandbox: 67 zap.L().Debug("K8sMonitor: startEvent: PodSandbox", zap.String("sandboxID", kmd.ID()), zap.String("podName", kmd.PodName()), zap.String("podNamespace", kmd.PodNamespace())) 68 // get pod 69 pod, err := m.getPod(ctx, kmd.PodNamespace(), kmd.PodName()) 70 if err != nil { 71 // fire off a retry for this, but simply return with the error 72 go m.startEventRetry(kmd, retry+1) 73 return err 74 } 75 // this should never happen, but if it does, simply return 76 if pod.Spec.HostNetwork { 77 return nil 78 } 79 if err := m.podCache.Set(kmd.ID(), pod); err != nil { 80 return err 81 } 82 83 // metadata exraction 84 runtime, err := m.metadataExtractor(ctx, pod, kmd.NetNSPath()) 85 if err != nil { 86 return err 87 } 88 if err := m.runtimeCache.Set(kmd.ID(), runtime); err != nil { 89 return err 90 } 91 92 return m.handlers.Policy.HandlePUEvent(ctx, kmd.ID(), common.EventStart, runtime) 93 94 case containermetadata.PodContainer: 95 zap.L().Debug("K8sMonitor: startEvent: PodContainer", zap.String("id", kmd.ID()), zap.String("sandboxID", kmd.PodSandboxID()), zap.String("podName", kmd.PodName()), zap.String("podNamespace", kmd.PodNamespace())) 96 // as we don't handle host network containers, this is a noop 97 return nil 98 default: 99 return fmt.Errorf("K8sMonitor: unexpected container kind for start event: %s", kmd.Kind()) 100 } 101 } 102 103 type destroyEventFunc func(context.Context, containermetadata.CommonKubernetesContainerMetadata) error 104 105 func (m *K8sMonitor) destroyEvent(ctx context.Context, kmd containermetadata.CommonKubernetesContainerMetadata) error { 106 switch kmd.Kind() { 107 case containermetadata.PodSandbox: 108 zap.L().Debug("K8sMonitor: destroyEvent: PodSandbox", zap.String("sandboxID", kmd.ID())) 109 runtime := m.runtimeCache.Get(kmd.ID()) 110 if runtime == nil { 111 // destroy event was sent previously, not a problem, just return 112 zap.L().Debug("K8sMonitor: destroyEvent: sandbox not in runtime cache") 113 return nil 114 } 115 116 // simply delete it from the caches and send a destroy event 117 // even if that fails in the policy engine, there is nothing we can do about it 118 m.runtimeCache.Delete(kmd.ID()) 119 m.podCache.Delete(kmd.ID()) 120 return m.handlers.Policy.HandlePUEvent(ctx, kmd.ID(), common.EventDestroy, runtime) 121 122 case containermetadata.PodContainer: 123 // if this is a container event that belongs to an existing sandbox 124 // we can simply return, we don't need to do anything 125 return nil 126 127 default: 128 return fmt.Errorf("K8sMonitor: unexpected container kind for destroy event: %s", kmd.Kind()) 129 } 130 } 131 132 type stopEventFunc func(context.Context, string) error 133 134 func (m *K8sMonitor) stopEvent(ctx context.Context, sandboxID string) error { 135 zap.L().Debug("K8sMonitor: stopEvent", zap.String("sandboxID", sandboxID)) 136 runtime := m.runtimeCache.Get(sandboxID) 137 if runtime == nil { 138 // destroy event had been sent already, not a problem, simply return 139 zap.L().Debug("K8sMonitor: stopEvent: sandbox not in runtime cache") 140 return nil 141 } 142 143 return m.handlers.Policy.HandlePUEvent(ctx, sandboxID, common.EventStop, runtime) 144 } 145 146 type updateEventFunc func(context.Context, string) error 147 148 func (m *K8sMonitor) updateEvent(ctx context.Context, sandboxID string) error { 149 zap.L().Debug("K8sMonitor: updateEvent", zap.String("sandboxID", sandboxID)) 150 runtime := m.runtimeCache.Get(sandboxID) 151 if runtime == nil { 152 // destroy event had been sent already, not a problem, simply return 153 zap.L().Debug("K8sMonitor: updateEvent: sandbox not in runtime cache") 154 return nil 155 } 156 157 pod := m.podCache.Get(sandboxID) 158 if pod == nil { 159 // destroy event had been sent already, not a problem, simply return 160 zap.L().Debug("K8sMonitor: updateEvent: pod not in pod cache") 161 return nil 162 } 163 164 // run metadata extraction again 165 // don't forget to update the runtime cache 166 runtime, err := m.metadataExtractor(ctx, pod, runtime.NSPath()) 167 if err != nil { 168 return err 169 } 170 if err := m.runtimeCache.Set(sandboxID, runtime); err != nil { 171 return err 172 } 173 174 // send an update event 175 return m.handlers.Policy.HandlePUEvent(ctx, sandboxID, common.EventUpdate, runtime) 176 } 177 178 // getPod tries to get the pod from the internal informer cache first, and falls back to the Kubernetes API if that fails 179 // the cache is being kept up-to-date by Kubernetes internals, we don't need to care about this 180 // NOTE: do not confuse the informer cache with the podCache from this package! 181 func (m *K8sMonitor) getPod(ctx context.Context, namespace, name string) (*corev1.Pod, error) { 182 pod, err := m.podLister.Pods(namespace).Get(name) 183 if err != nil { 184 zap.L().Debug("K8sMonitor: getPod: failed to get pod from cache. Using Kubernetes API directly now instead...", zap.String("name", name), zap.String("namespace", namespace), zap.Error(err)) 185 return m.kubeClient.CoreV1().Pods(namespace).Get(ctx, name, metav1.GetOptions{}) 186 } 187 return pod, nil 188 }