python获取dockerhub仓库最新版本的镜像

python获取dockerhub仓库最新版本的镜像

有些docker仓库的latest镜像仅表示最后上传的镜像版本,不是实际上的最新版本程序(比如gitlab/gitlab-ce),为了自动获取docker仓库里符合我们要求的镜像,写了一个脚本。

思路分析

获取符合要求的镜像,那么很显然要分为三部分实现:

  1. 获取所有镜像标签
  2. 筛选符合要求的镜像
  3. 拉取镜像

其中,获取所有镜像标签简单调用API即可获取,拉取镜像也能通过执行本地命令进行,筛选镜像则需要配置好过滤器。
因此,决定实现两个类:过滤器、仓库获取器及两个过程函数用于加载配置和执行拉取过程。

代码实现

先设计好配置文件,想实现以上功能,需要知道镜像仓库的地址、过滤条件,其中过滤条件包括几种过滤方法,设计如下:

libraries:
  - gitlab:
      repo: "gitlab/gitlab-ce"
      base_host: "" # 基础仓库链接,默认为dockerhub
      tag_filter: # 过滤器
        exclude: # tag中不出现的词
          - "14."
          - "latest"
        include: # tag中必须包含的词,任选其一
          - "15.11"
          - "15.10"
        py_code: # 作为参数输入后执行结果为true的tag,需要有名字
          - "lambda remote_tag: list(map(int, '15.11.0-ce.0'.replace('-ce', '').split('.'))) < list(map(int, remote_tag.replace('-ce', '').split('.')))"
        regex: # 符合正则表达式的tag,re.match(regex,str)返回true的str
          - ".*ee.*"

  - openvas:
      repo: "immauss/openvas"
      base_host: "" # 基础仓库链接,默认为dockerhub
      tag_filter: # 过滤器
        exclude:
          - "" # tag中不出现的词
        include:
          - "latest" # tag中必须包含的词
        py_code:
          - "" # 作为参数输入后执行结果为true的tag
        regex:
          - "" # 符合正则表达式的tag

对于筛选方式,简单说明如下:

  1. exclude: tag中不存在的字符串
  2. include: tag中至少包含1个的字符串
  3. py_code: 一个参数为获取到的镜像tag的lambda函数,返回true时满足过滤条件;如果有多个lambda函数,返回需要满足全部才能通过筛选。
  4. regex: 正则表达式,满足所有正则表达式的tag才能通过筛选

过滤器

class Tag_Filter:
    exclude: list = []  # tag中不能出现的词,全都不允许出现
    include: list = []  # tag中必须包含的词,任一满足即可通过筛选
    py_code: list = []  # tag需要满足的python判断方法,满足所有判断方法才能通过筛选
    regex_filter: list = []  # 符合正则表达式的tag,需要满足所有正则表达式才能通过筛选

    def __init__(self) -> None:
        self.exclude = []
        self.include = []
        self.py_code = []
        self.regex_filter = []
        return

    # 定义一个类的构造函数,它接受一个 `tag_filter` 字典作为参数。
    # 在构造函数中,我们从 `tag_filter` 字典中获取了四个列表:`exclude`、`include`、`py_code` 和 `regex`。
    # 对于每个列表,我们都使用列表推导式来过滤掉空字符串,并将结果赋值给相应的实例变量。
    def __init__(self, tag_filter: dict) -> None:

        # 从 tag_filter 字典中获取 exclude 列表,并过滤掉值为空字符串的元素
        # Get the exclude list from the tag_filter dictionary and filter out element which is empty strings
        self.exclude = [
            word
            for word in list(tag_filter["exclude"])
            if word != ""
        ]

        # 从 tag_filter 字典中获取 include 列表,并过滤掉值为空字符串的元素
        # Get the include list from the tag_filter dictionary and filter out element which is empty strings
        self.include = [
            word
            for word in list(tag_filter["include"])
            if word != ""
        ]

        # 从 tag_filter 字典中获取 py_code 列表,并过滤掉值为空字符串的元素
        # Get the py_code list from the tag_filter dictionary and filter out element which is empty strings
        self.py_code = [
            code
            for code in list(tag_filter["py_code"])
            if code != ""
        ]

        # 从 tag_filter 字典中获取 regex 列表,并过滤掉值为空字符串的元素
        # Get the regex list from the tag_filter dictionary and filter out element which is empty strings
        self.regex_filter = [
            regex for regex in list(tag_filter["regex"])
            if regex != ""
        ]
        return

仓库获取器

class Image_Getter:
    repo: str = ""
    base_host: str = ""
    tag_filter: Tag_Filter = None

    # Constract func by None
    def __init__(self) -> None:
        self.repo = ""
        self.base_host = ""
        self.tag_filter = None
        return

    # Constract func by config(type dict)
    def __init__(self, config: dict) -> None:
        self.repo = config["repo"]

        base_host = config['base_host']
        if not base_host.endswith("/") and base_host != "":
            base_host += "/"
        self.base_host = "https://registry.hub.docker.com/v2/repositories/" if base_host == "" else base_host

        self.tag_filter = Tag_Filter(config['tag_filter'])

        return

以上是声明部分和构造函数。
接下来定义一个名为 get_remote_tags 的成员函数,它用来获取远程标签并进行过滤。函数首先构造一个 URL,然后使用 requests 模块来获取远程标签。获取到的标签会根据正则表达式进行过滤,只保留符合条件的标签。接下来,函数会获取 includeexcludepy_code 列表。如果 includeexclude 都为空,则直接返回过滤后的标签。否则,函数会进一步过滤标签,只保留符合 includeexcludepy_code 条件的标签。

    def get_remote_tags(self) -> list:
        # 构造获取仓库的标签的 URL
        # Construct the URL to get remote tags
        url = f"{
      
      self.base_host}{
      
      self.repo}/tags?page_size=50"

        # 获取远程仓库的标签并过滤
        # Get remote tags and filter them
        tags = [
            result['name']
            for result in requests.get(url).json()['results']
            if all(
                re.match(regex, str(result['name']))
                for regex in self.tag_filter.regex_filter
            )
        ]

        # 获取 include 和 exclude 列表以及 py_code 列表
        # Get the include and exclude lists and the py_code list
        include = self.tag_filter.include
        exclude = self.tag_filter.exclude
        py_codes = self.tag_filter.py_code

        # 如果 include 和 exclude 都为空,则直接返回 tags
        # If both include and exclude are empty, return tags directly
        if not include and not exclude:
            return tags
        else:
            # 过滤标签
            # Filter tags
            filtered_tags = []
            for tag in tags:
                if include and not any(i in tag for i in include):
                    continue
                if exclude and any(e in tag for e in exclude):
                    continue
                if py_codes and not all(eval(f"({
      
      py_code})('{
      
      tag}')") for py_code in py_codes):
                    continue
                filtered_tags.append(tag)
            return filtered_tags
        return []

配置加载

配置加载很简单,调用yaml库即可

def load_config(config_path: str = 'dockerhub.yaml') -> dict:
    with open(config_path, 'r') as f:
        config = yaml.load(f, Loader=yaml.FullLoader)
    return dict(config)

拉取

定义一个名为 pull_latest_image 的函数,它用来从 Docker Hub 拉取最新的镜像。
函数首先加载配置文件,然后遍历配置文件中指定的每个仓库。对于每个仓库,函数会使用 Image_Getter 类来获取要拉取的镜像列表。
接下来,函数会遍历镜像列表,并使用 subprocess.run 函数来执行 docker pull 命令,拉取每个镜像。
在拉取过程中,函数会输出调试信息,包括正在拉取的镜像名称、命令执行结果以及可能的错误信息。

def pull_latest_image(config_path: str = 'dockerhub.yaml') -> int:
    # 加载配置文件
    # Load the configuration file
    config = load_config(config_path)

    # 遍历配置文件中的每个库
    # Iterate over each library in the configuration file
    for library in config['libraries']:
        name = list(library.keys())[0]
        getter = Image_Getter(library[name])

        # 获取要拉取的镜像列表
        # Get the list of images to pull
        image_to_pull_list = [
            f"{
      
      getter.repo}:{
      
      tag}"
            for tag in getter.get_remote_tags()
        ]

        # 拉取镜像
        # Pull the images
        for image in image_to_pull_list:
            print(f"Pulling image: {
      
      image}")
            command = ["docker", "pull", image]
            result = subprocess.run(
                command,
                stdout=subprocess.PIPE,
                stderr=subprocess.PIPE
            )

            # 检查命令执行结果并输出调试信息
            # Check the command execution result and output debug information
            if result.returncode == 0:
                print(f"Successfully pulled image: {
      
      image}")
            else:
                print(
                    f"Failed to pull image: {
      
      image}. Error: {
      
      result.stderr.decode('utf-8')}")
                return result.returncode
    return 0

总结

以上代码和配置文件实现了拉取gitlab-ce高于15.11.0的镜像,本代码需要安装pyyaml和requests模块才能顺利运行。

备注

注释部分由new-bing完善。

猜你喜欢

转载自blog.csdn.net/u013943146/article/details/130433142