github.com/alibaba/ilogtail/pkg@v0.0.0-20250526110833-c53b480d046c/helper/containercenter/container_export.go (about) 1 // Copyright 2022 iLogtail Authors 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package containercenter 16 17 import ( 18 "context" 19 "fmt" 20 "regexp" 21 "strings" 22 "time" 23 24 "github.com/docker/docker/api/types" 25 "github.com/docker/docker/api/types/events" 26 docker "github.com/docker/docker/client" 27 28 "github.com/alibaba/ilogtail/pkg/logger" 29 ) 30 31 type ContainerMeta struct { 32 PodName string 33 K8sNamespace string 34 ContainerName string 35 Image string 36 K8sLabels map[string]string 37 ContainerLabels map[string]string 38 Env map[string]string 39 } 40 41 type DockerInfoDetailWithFilteredEnvAndLabel struct { 42 Detail *DockerInfoDetail 43 Env map[string]string 44 ContainerLabels map[string]string 45 K8sLabels map[string]string 46 } 47 48 func GetContainersLastUpdateTime() int64 { 49 return getContainerCenterInstance().getLastUpdateMapTime() 50 } 51 52 // GetContainerMeta get a thread safe container meta struct. 53 func GetContainerMeta(containerID string) *ContainerMeta { 54 getFunc := func() *ContainerMeta { 55 instance := getContainerCenterInstance() 56 instance.lock.RLock() 57 defer instance.lock.RUnlock() 58 detail, ok := instance.containerMap[containerID] 59 if !ok { 60 return nil 61 } 62 c := &ContainerMeta{ 63 K8sLabels: make(map[string]string), 64 ContainerLabels: make(map[string]string), 65 Env: make(map[string]string), 66 } 67 if detail.K8SInfo.ContainerName == "" { 68 c.ContainerName = detail.ContainerNameTag["_container_name_"] 69 } else { 70 c.ContainerName = detail.K8SInfo.ContainerName 71 } 72 c.K8sNamespace = detail.K8SInfo.Namespace 73 c.PodName = detail.K8SInfo.Pod 74 c.Image = detail.ContainerNameTag["_image_name_"] 75 for k, v := range detail.K8SInfo.Labels { 76 c.K8sLabels[k] = v 77 } 78 for k, v := range detail.ContainerInfo.Config.Labels { 79 c.ContainerLabels[k] = v 80 } 81 for _, env := range detail.ContainerInfo.Config.Env { 82 var envKey, envValue string 83 splitArray := strings.SplitN(env, "=", 2) 84 if len(splitArray) < 2 { 85 envKey = splitArray[0] 86 } else { 87 envKey = splitArray[0] 88 envValue = splitArray[1] 89 } 90 c.Env[envKey] = envValue 91 } 92 return c 93 } 94 meta := getFunc() 95 if meta != nil { 96 return meta 97 } 98 if err := containerFindingManager.FetchOne(containerID); err != nil { 99 logger.Debugf(context.Background(), "cannot fetch container for %s, error is %v", containerID, err) 100 return nil 101 } 102 return getFunc() 103 } 104 105 func ProcessContainerAllInfo(processor func(*DockerInfoDetail)) { 106 getContainerCenterInstance().processAllContainerInfo(processor) 107 } 108 109 func GetContainerBySpecificInfo(filter func(*DockerInfoDetail) bool) (infoList []*DockerInfoDetail) { 110 return getContainerCenterInstance().getAllSpecificInfo(filter) 111 } 112 113 // GetContainerByAcceptedInfo gathers all info of containers that match the input parameters. 114 // Two conditions (&&) for matched container: 115 // 1. has a label in @includeLabel and don't have any label in @excludeLabel. 116 // 2. has a env in @includeEnv and don't have any env in @excludeEnv. 117 // If the input parameters is empty, then all containers are matched. 118 // It returns a map contains docker container info. 119 func GetContainerByAcceptedInfo( 120 includeLabel map[string]string, 121 excludeLabel map[string]string, 122 includeLabelRegex map[string]*regexp.Regexp, 123 excludeLabelRegex map[string]*regexp.Regexp, 124 includeEnv map[string]string, 125 excludeEnv map[string]string, 126 includeEnvRegex map[string]*regexp.Regexp, 127 excludeEnvRegex map[string]*regexp.Regexp, 128 k8sFilter *K8SFilter, 129 ) map[string]*DockerInfoDetail { 130 return getContainerCenterInstance().getAllAcceptedInfo(includeLabel, excludeLabel, includeLabelRegex, excludeLabelRegex, includeEnv, excludeEnv, includeEnvRegex, excludeEnvRegex, k8sFilter) 131 } 132 133 // GetContainerByAcceptedInfoV2 works like GetContainerByAcceptedInfo, but uses less CPU. 134 // It reduces CPU cost by using full list and match list to find containers that 135 // need to be check. 136 // 137 // deleted = fullList - containerMap 138 // newList = containerMap - fullList 139 // matchList -= deleted + filter(newList) 140 // matchAddedList: new container ID for current config 141 // matchDeletedList: deleted container ID for current config 142 // fullAddedList = newList 143 // fullDeletedList = deleted 144 // return len(deleted), len(filter(newList)), matchAddedList, matchDeletedList, fullAddedList, fullDeletedList 145 // 146 // @param fullList [in,out]: all containers. 147 // @param matchList [in,out]: all matched containers. 148 // 149 // It returns two integers and four list 150 // two integers: the number of new matched containers and deleted containers. 151 // four list: new matched containers list, deleted matched containers list, added containers list, delete containers list 152 func GetContainerByAcceptedInfoV2( 153 fullList map[string]bool, 154 matchList map[string]*DockerInfoDetail, 155 includeLabel map[string]string, 156 excludeLabel map[string]string, 157 includeLabelRegex map[string]*regexp.Regexp, 158 excludeLabelRegex map[string]*regexp.Regexp, 159 includeEnv map[string]string, 160 excludeEnv map[string]string, 161 includeEnvRegex map[string]*regexp.Regexp, 162 excludeEnvRegex map[string]*regexp.Regexp, 163 k8sFilter *K8SFilter, 164 ) (newCount, delCount int, matchAddedList, matchDeletedList []string) { 165 return getContainerCenterInstance().getAllAcceptedInfoV2( 166 fullList, matchList, includeLabel, excludeLabel, includeLabelRegex, excludeLabelRegex, includeEnv, excludeEnv, includeEnvRegex, excludeEnvRegex, k8sFilter) 167 168 } 169 170 func GetDiffContainers(fullList map[string]struct{}) (fullAddedList, fullDeletedList []string) { 171 return getContainerCenterInstance().getDiffContainers(fullList) 172 } 173 174 // SplitRegexFromMap extract regex from user config 175 // regex must begin with ^ and end with $(we only check ^) 176 func SplitRegexFromMap(input map[string]string) (staticResult map[string]string, regexResult map[string]*regexp.Regexp, err error) { 177 staticResult = make(map[string]string) 178 regexResult = make(map[string]*regexp.Regexp) 179 for key, value := range input { 180 if strings.HasPrefix(value, "^") { 181 reg, err := regexp.Compile(value) 182 if err != nil { 183 err = fmt.Errorf("key : %s, value : %s is not valid regex, err is %s", key, value, err.Error()) 184 return input, nil, err 185 } 186 regexResult[key] = reg 187 } else { 188 staticResult[key] = value 189 } 190 } 191 return staticResult, regexResult, nil 192 } 193 194 func CreateDockerClient(opt ...docker.Opt) (client *docker.Client, err error) { 195 opt = append(opt, docker.FromEnv) 196 client, err = docker.NewClientWithOpts(opt...) 197 if err != nil { 198 return nil, err 199 } 200 // add dockerClient connectivity tests 201 pingCtx, cancel := context.WithTimeout(context.Background(), time.Second*5) 202 defer cancel() 203 ping, err := client.Ping(pingCtx) 204 if err != nil { 205 return nil, err 206 } 207 client.NegotiateAPIVersionPing(ping) 208 return 209 } 210 211 func RegisterDockerEventListener(c chan events.Message) { 212 getContainerCenterInstance().registerEventListener(c) 213 } 214 215 func UnRegisterDockerEventListener(c chan events.Message) { 216 getContainerCenterInstance().unRegisterEventListener(c) 217 } 218 219 func Init() { 220 getContainerCenterInstance() 221 } 222 223 func CreateContainerInfoDetail(info types.ContainerJSON, envConfigPrefix string, selfConfigFlag bool) *DockerInfoDetail { 224 return getContainerCenterInstance().CreateInfoDetail(info, envConfigPrefix, selfConfigFlag) 225 } 226 227 // for test 228 func GetContainerMap() map[string]*DockerInfoDetail { 229 instance := getContainerCenterInstance() 230 instance.lock.RLock() 231 defer instance.lock.RUnlock() 232 return instance.containerMap 233 } 234 235 func GetAllContainerToRecord(envSet, labelSet, k8sLabelSet map[string]struct{}, containerIds map[string]struct{}) []*DockerInfoDetailWithFilteredEnvAndLabel { 236 instance := getContainerCenterInstance() 237 instance.lock.RLock() 238 defer instance.lock.RUnlock() 239 result := make([]*DockerInfoDetailWithFilteredEnvAndLabel, 0) 240 if len(containerIds) > 0 { 241 for key := range containerIds { 242 value, ok := instance.containerMap[key] 243 if !ok { 244 continue 245 } 246 result = append(result, CastContainerDetail(value, envSet, labelSet, k8sLabelSet)) 247 } 248 } else { 249 for _, value := range instance.containerMap { 250 result = append(result, CastContainerDetail(value, envSet, labelSet, k8sLabelSet)) 251 } 252 } 253 return result 254 } 255 256 func GetAllContainerIncludeEnvAndLabelToRecord(envSet, labelSet, k8sLabelSet, diffEnvSet, diffLabelSet, diffK8sLabelSet map[string]struct{}) []*DockerInfoDetailWithFilteredEnvAndLabel { 257 instance := getContainerCenterInstance() 258 instance.lock.RLock() 259 defer instance.lock.RUnlock() 260 result := make([]*DockerInfoDetailWithFilteredEnvAndLabel, 0) 261 for _, value := range instance.containerMap { 262 match := false 263 if len(diffEnvSet) > 0 { 264 for _, env := range value.ContainerInfo.Config.Env { 265 splitArray := strings.SplitN(env, "=", 2) 266 envKey := splitArray[0] 267 if len(splitArray) != 2 { 268 continue 269 } 270 _, ok := diffEnvSet[envKey] 271 if ok { 272 match = true 273 } 274 } 275 } 276 if len(diffLabelSet) > 0 { 277 if !match { 278 for key := range value.ContainerInfo.Config.Labels { 279 _, ok := diffLabelSet[key] 280 if ok { 281 match = true 282 } 283 } 284 } 285 } 286 if len(diffK8sLabelSet) > 0 { 287 if !match { 288 for key := range value.K8SInfo.Labels { 289 _, ok := diffK8sLabelSet[key] 290 if ok { 291 match = true 292 } 293 } 294 } 295 } 296 if match { 297 result = append(result, CastContainerDetail(value, envSet, labelSet, k8sLabelSet)) 298 } 299 } 300 return result 301 } 302 303 func CastContainerDetail(containerInfo *DockerInfoDetail, envSet, labelSet, k8sLabelSet map[string]struct{}) *DockerInfoDetailWithFilteredEnvAndLabel { 304 newEnv := make(map[string]string) 305 for _, env := range containerInfo.ContainerInfo.Config.Env { 306 splitArray := strings.SplitN(env, "=", 2) 307 envKey := splitArray[0] 308 if len(splitArray) != 2 { 309 continue 310 } 311 envValue := splitArray[1] 312 _, ok := envSet[envKey] 313 if ok { 314 newEnv[envKey] = envValue 315 } 316 } 317 newLabels := make(map[string]string) 318 for key, value := range containerInfo.ContainerInfo.Config.Labels { 319 _, ok := labelSet[key] 320 if ok { 321 newLabels[key] = value 322 } 323 } 324 newK8sLabels := make(map[string]string) 325 for key, value := range containerInfo.K8SInfo.Labels { 326 _, ok := k8sLabelSet[key] 327 if ok { 328 newK8sLabels[key] = value 329 } 330 } 331 return &DockerInfoDetailWithFilteredEnvAndLabel{ 332 Detail: containerInfo, 333 Env: newEnv, 334 ContainerLabels: newLabels, 335 K8sLabels: newK8sLabels, 336 } 337 }