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  }