侧边栏壁纸
博主头像
张种恩的技术小栈博主等级

行动起来,活在当下

  • 累计撰写 748 篇文章
  • 累计创建 65 个标签
  • 累计收到 39 条评论

目 录CONTENT

文章目录

通过API来管理Harbor

zze
zze
2020-11-20 / 0 评论 / 1 点赞 / 711 阅读 / 9305 字

起因

今天碰到一个很淡疼的问题,公司要求在一个环境搭建一个 Harbor,然而,这个环境只能通过跳板机进入,所以 Harbor 搭建起来之后,根本不能通过公司 PC 的浏览器去访问 Harbor 的管理界面。。。怎么办呢?最后就打算自己写一个管理工具直接调用 API 去管理 Harbor 了,初版效果如下:

$ harbor_client 
            list_project:查看 Project 列表;
            list_repository <project_name>:查看指定 Project 下的 Repository 列表;
            list_tags <repository_name>:查看指定 Repository 下的 Tag 列表;
            create_project <project_name>:创建一个新的 Project;
            delete_repository <repostory_name>:删除一个 Repostiroy;
            delete_project <project_name>:删除一个 Project;

这几个操作已经足够日常使用了,后续有新需求再加就行,下面附上 Python 脚本内容。

脚本

import requests
import json
import sys


class Harbor:
    def __init__(self, user, pwd, host):
        self.user = user
        self.pwd = pwd
        self.host = host
        self.auth = (self.user, self.pwd)

    def base_get(self, url, data={}):
        full_url = '{}{}'.format(self.host, url)
        resp = requests.get(full_url, params=data, headers={
            'Content-Type': 'application/json'
        }, auth=self.auth)
        return json.loads(resp.text)

    def base_delete(self, url, data={}):
        full_url = '{}{}'.format(self.host, url)
        resp = requests.delete(full_url, params=data, headers={
            'Content-Type': 'application/json'
        }, auth=self.auth)
        try:
            json_data = json.loads(resp.text)
            return json_data
        except Exception:
            return {}

    def base_post(self, url, data={}):
        full_url = '{}{}'.format(self.host, url)
        resp = requests.post(full_url, data=json.dumps(data), headers={
            'Content-Type': 'application/json'
        }, auth=self.auth)
        try:
            json_data = json.loads(resp.text)
            return json_data
        except Exception:
            return {}

    def get_project_by_project_name(self, project_name):
        url = '/api/projects'
        data = {
            'name': project_name
        }
        resp = self.base_get(url, data)
        if resp:
            return resp[0]
        else:
            return None

    def list_project(self):
        url = '/api/projects'

        project_list = self.base_get(url)
        for project in project_list:
            # creation_time = project['creation_time']
            # creation_time = creation_time.replace('T', ' ').replace('Z', ' ')
            # if '.' in creation_time:
            #     creation_time = creation_time[0:creation_time.index('.')]
            # print('name: {}, repo_count: {}, creation_time: {}'.format(project['name'],
            #                                                            project['repo_count'], creation_time))
            print('{} ({})'.format(project['name'], project['repo_count']))

    def list_repository_by_project_name(self, project_name):
        url = '/api/repositories'
        project = self.get_project_by_project_name(project_name)
        if not project:
            print('project [{}] is not exists'.format(project_name))
            return

        data = {
            'project_id': project['project_id']
        }
        resp = self.base_get(url, data=data)
        if 'code' in resp:
            print('{}'.format(resp['message']))
        else:
            repository_list = resp
            for repository in repository_list:
                print('{} ({})'.format(repository['name'],
                                       repository['tags_count']))

    def create_project(self, project_name):
        project = self.get_project_by_project_name(project_name)
        if project:
            print('{} is exists'.format(project_name))
            return
        url = '/api/projects'
        data = {
            'project_name': project_name,
            'public': 1
        }
        resp = self.base_post(url, data)
        if 'code' in resp:
            print('create project [{}] failed, msg: [{}]'.format(project_name, resp['message']))
        else:
            print('create project [{}] success'.format(project_name))

    def list_tags_by_repository_name(self, repository_name):
        repository_name = repository_name.replace('/', '%2F')
        url = '/api/repositories/{}/tags'.format(repository_name)
        tag_list = self.base_get(url)
        for tag in tag_list:
            print(tag['name'])

    def delete_repository_by_repository_name(self, repository_name):
        repository_name = repository_name.replace('/', '%2F')
        url = '/api/repositories/{}'.format(repository_name)
        resp = self.base_delete(url)
        if 'code' in resp:
            print(resp['message'])
        else:
            print('repository [{}] delete success'.format(repository_name))

    def delete_project_by_project_name(self, project_name):
        project = self.get_project_by_project_name(project_name)
        if not project:
            print('project [{}] not exists'.format(project_name))
            return
        url = '/api/projects/{}'.format(project['project_id'])
        resp = self.base_delete(url)
        if 'code' in resp:
            print(resp['message'])
        elif not resp:
            print('project [{}] delete success'.format(project_name))

    def show_help(self):
        print('''\
            list_project:查看 Project 列表;
            list_repository <project_name>:查看指定 Project 下的 Repository 列表;
            list_tags <repository_name>:查看指定 Repository 下的 Tag 列表;
            create_project <project_name>:创建一个新的 Project;
            delete_repository <repostory_name>:删除一个 Repostiroy;
            delete_project <project_name>:删除一个 Project;
        ''')


if __name__ == '__main__':
    harbor = Harbor('admin', 'Harbor12345', 'http://10.0.1.111')
    if len(sys.argv) == 1:
        harbor.show_help()
        sys.exit(1)

    op = sys.argv[1]
    op_map = {
        'help': 'show_help',
        'list_project': 'list_project',
        'list_repository': 'list_repository_by_project_name',
        'list_tags': 'list_tags_by_repository_name',
        'create_project': 'create_project',
        'delete_repository': 'delete_repository_by_repository_name',
        'delete_project': 'delete_project_by_project_name'
    }
    if op not in op_map:
        harbor.show_help()
        sys.exit(1)

    try:
        getattr(harbor, op_map[op])(*sys.argv[2:])
    except Exception as e:
        print('err: {}'.format(e))
        harbor.show_help()

Harbor API

另外提一嘴 Harbor 的 API 怎么查看。Harbor 是支持通过 Swagger 暴露 API 的,但是默认并没有对外暴露 HTTP 可视化接口,经过调研查看接口最简单的方案如下。

首先,我这里的 Harbor 是通过 docker-compose 部署的,部署完毕后会启动如下容器:

$ docker ps 
CONTAINER ID        IMAGE                                 COMMAND                  CREATED             STATUS                    PORTS                       NAMES
6c003358094b        goharbor/harbor-jobservice:v1.10.6    "/harbor/harbor_jobs…"   47 minutes ago      Up 47 minutes (healthy)                               harbor-jobservice
e7b0dfb9d735        goharbor/nginx-photon:v1.10.6         "nginx -g 'daemon of…"   47 minutes ago      Up 47 minutes (healthy)   0.0.0.0:80->8080/tcp        nginx
56eefdeec930        goharbor/harbor-core:v1.10.6          "/harbor/harbor_core"    47 minutes ago      Up 47 minutes (healthy)                               harbor-core
f41c4b51267d        goharbor/harbor-portal:v1.10.6        "nginx -g 'daemon of…"   47 minutes ago      Up 47 minutes (healthy)   8080/tcp                    harbor-portal
215356963d1b        goharbor/harbor-db:v1.10.6            "/docker-entrypoint.…"   47 minutes ago      Up 47 minutes (healthy)   5432/tcp                    harbor-db
30772919ed2b        goharbor/registry-photon:v1.10.6      "/home/harbor/entryp…"   47 minutes ago      Up 47 minutes (healthy)   5000/tcp                    registry
b62e6e7acb47        goharbor/harbor-registryctl:v1.10.6   "/home/harbor/start.…"   47 minutes ago      Up 47 minutes (healthy)                               registryctl
49f3da43be30        goharbor/redis-photon:v1.10.6         "redis-server /etc/r…"   47 minutes ago      Up 47 minutes (healthy)   6379/tcp                    redis
b8671322a3b2        goharbor/harbor-log:v1.10.6           "/bin/sh -c /usr/loc…"   47 minutes ago      Up 47 minutes (healthy)   127.0.0.1:1514->10514/tcp   harbor-log

这里我们可以进入 harbor-portal 这个容器,在该容器中有一个 YAML 文件专门用来保存 Harbor API 的内容,如下:

$ docker exec -it harbor-portal bash
nginx [ / ]$ head -10 /usr/share/nginx/html/swagger.yaml
swagger: '2.0'
info:
  title: Harbor API
  description: These APIs provide services for manipulating Harbor project.
  version: 1.10.0
host: localhost
schemes:
  - http
  - https
basePath: /api
...

拷贝上述容器中的内容,浏览器打开 https://editor.swagger.io/ ,屏幕左侧是一个编辑框,我们将上述容器中的 swagger.yaml 的内容拷贝到这个编辑框,就能实时在右侧预览到排版好的 API 说明啦~

image.png

1

评论区