起因
今天碰到一个很淡疼的问题,公司要求在一个环境搭建一个 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 说明啦~
评论区