From 4d6c5b431597e3fb4bd9311612e6e2b51ba4e46b Mon Sep 17 00:00:00 2001 From: yackob03 Date: Mon, 30 Sep 2013 00:19:43 -0400 Subject: [PATCH 1/3] The button for make private should match the button for make public. Tweak the copy about who may access a repository in either state. --- static/partials/repo-admin.html | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/static/partials/repo-admin.html b/static/partials/repo-admin.html index a7372e46b..eaad59c9c 100644 --- a/static/partials/repo-admin.html +++ b/static/partials/repo-admin.html @@ -61,7 +61,7 @@
- This repository is currently private. Only users on the above access list may interact with it. + This repository is currently private. Only users on the above access list may view and interact with it.
@@ -71,10 +71,10 @@
- This repository is currently public and visible to all users. + This repository is currently public and is visible to all users, and may be pulled by all users.
- +
From abdf731f7a88cb20179b2a405cd96d96640a5842 Mon Sep 17 00:00:00 2001 From: yackob03 Date: Mon, 30 Sep 2013 14:39:10 -0400 Subject: [PATCH 2/3] Combine the registry and index processes. --- config.py | 2 +- endpoints/index.py | 5 +---- registry_wsgi.py | 13 ------------- wsgi.py | 4 +++- 4 files changed, 5 insertions(+), 19 deletions(-) delete mode 100644 registry_wsgi.py diff --git a/config.py b/config.py index 373dfaea2..136d11945 100644 --- a/config.py +++ b/config.py @@ -14,4 +14,4 @@ class MailConfig(object): class ProductionConfig(FlaskConfig, MailConfig): - pass + REGISTRY_SERVER = 'localhost:5000' diff --git a/endpoints/index.py b/endpoints/index.py index e6708ed6e..b54d08e80 100644 --- a/endpoints/index.py +++ b/endpoints/index.py @@ -19,15 +19,12 @@ from auth.permissions import (ModifyRepositoryPermission, logger = logging.getLogger(__name__) -REGISTRY_SERVER = 'localhost:5003' - - def generate_headers(f): @wraps(f) def wrapper(namespace, repository, *args, **kwargs): response = f(namespace, repository, *args, **kwargs) - response.headers['X-Docker-Endpoints'] = REGISTRY_SERVER + response.headers['X-Docker-Endpoints'] = app.config['REGISTRY_SERVER'] has_token_request = request.headers.get('X-Docker-Token', '') diff --git a/registry_wsgi.py b/registry_wsgi.py deleted file mode 100644 index 29e3c3112..000000000 --- a/registry_wsgi.py +++ /dev/null @@ -1,13 +0,0 @@ -import logging - -from app import app - -import endpoints.registry -import endpoints.tags - -if __name__ == '__main__': - FORMAT = '%(asctime)-15s - %(levelname)s - %(pathname)s - ' + \ - '%(funcName)s - %(message)s' - logging.basicConfig(format=FORMAT, level=logging.DEBUG) - - app.run(port=5000, debug=True) diff --git a/wsgi.py b/wsgi.py index f46fe3444..b598e2fc6 100644 --- a/wsgi.py +++ b/wsgi.py @@ -5,10 +5,12 @@ from app import app import endpoints.index import endpoints.api import endpoints.web +import endpoints.tags +import endpoints.registry if __name__ == '__main__': FORMAT = '%(asctime)-15s - %(levelname)s - %(pathname)s - ' + \ '%(funcName)s - %(message)s' logging.basicConfig(format=FORMAT, level=logging.DEBUG) - app.run(port=5002, debug=True) + app.run(port=5001, debug=True) From ea6df2b72546e23ffaa076ec238f02e52ed5c937 Mon Sep 17 00:00:00 2001 From: yackob03 Date: Mon, 30 Sep 2013 15:30:00 -0400 Subject: [PATCH 3/3] Add image ancestry and update the test db accordingly. --- data/database.py | 3 +++ data/model.py | 37 ++++++++++++++++++++++++++----------- endpoints/api.py | 9 +++++++-- endpoints/registry.py | 8 +++++++- test.db | Bin 38912 -> 40960 bytes 5 files changed, 43 insertions(+), 14 deletions(-) diff --git a/data/database.py b/data/database.py index cf83b50e0..13791af53 100644 --- a/data/database.py +++ b/data/database.py @@ -89,6 +89,9 @@ class Image(BaseModel): comment = CharField(null=True) repository = ForeignKeyField(Repository) + # '/' separated list of ancestory ids, e.g. /1/2/6/7/10/ + ancestors = CharField(index=True, default='/', max_length=65535) + class Meta: database = db indexes = ( diff --git a/data/model.py b/data/model.py index b3edfac08..c70013b9b 100644 --- a/data/model.py +++ b/data/model.py @@ -198,7 +198,7 @@ def set_image_checksum(image_id, repository, checksum): def set_image_metadata(image_id, namespace_name, repository_name, - created_date_str, comment): + created_date_str, comment, parent=None): joined = Image.select().join(Repository) image_list = list(joined.where(Repository.name == repository_name, Repository.namespace == namespace_name, @@ -210,6 +210,10 @@ def set_image_metadata(image_id, namespace_name, repository_name, fetched = image_list[0] fetched.created = dateutil.parser.parse(created_date_str) fetched.comment = comment + + if parent: + fetched.ancestors = '%s%s/' % (parent.ancestors, parent.id) + fetched.save() return fetched @@ -220,15 +224,6 @@ def get_repository_images(namespace_name, repository_name): Repository.namespace == namespace_name) -def get_tag_images(namespace_name, repository_name, tag_name): - joined = Image.select().join(RepositoryTag).join(Repository) - fetched = list(joined.where(Repository.name == repository_name, - Repository.namespace == namespace_name, - RepositoryTag.name == tag_name)) - - return fetched - - def list_repository_tags(namespace_name, repository_name): select = RepositoryTag.select(RepositoryTag, Image) with_repo = select.join(Repository) @@ -238,13 +233,33 @@ def list_repository_tags(namespace_name, repository_name): def get_tag_image(namespace_name, repository_name, tag_name): - fetched = get_tag_images(namespace_name, repository_name, tag_name) + joined = Image.select().join(RepositoryTag).join(Repository) + fetched = joined.where(Repository.name == repository_name, + Repository.namespace == namespace_name, + RepositoryTag.name == tag_name) + if not fetched: raise DataModelException('Unable to find image for tag.') return fetched[0] +def get_image_by_id(image_id): + return Image.get(Image.image_id == image_id) + + +def get_parent_images(image_obj): + """ Returns a list of parent Image objects in chronilogical order. """ + parents = image_obj.ancestors + parent_db_ids = parents.strip('/').split('/') + + or_clauses = [(Image.id == db_id) for db_id in parent_db_ids] + parent_images = Image.select().where(reduce(operator.or_, or_clauses)) + id_to_image = {unicode(image.id): image for image in parent_images} + + return [id_to_image[parent_id] for parent_id in parent_db_ids] + + def create_or_update_tag(namespace_name, repository_name, tag_name, tag_image_id): repo = Repository.get(Repository.name == repository_name, diff --git a/endpoints/api.py b/endpoints/api.py index 67a1079d3..7c1cadc1c 100644 --- a/endpoints/api.py +++ b/endpoints/api.py @@ -188,10 +188,15 @@ def role_view(repo_perm_obj): def list_tag_images(namespace, repository, tag): permission = ReadRepositoryPermission(namespace, repository) if permission.can() or model.repository_is_public(namespace, repository): - images = model.get_tag_images(namespace, repository, tag) + tag_image = model.get_tag_image(namespace, repository, tag) + parent_images = model.get_parent_images(tag_image) + + parents = list(parent_images) + parents.reverse() + all_images = [tag_image] + parents return jsonify({ - 'images': [image_view(image) for image in images] + 'images': [image_view(image) for image in all_images] }) abort(403) # Permission denied diff --git a/endpoints/registry.py b/endpoints/registry.py index e1bdc6d42..2cad43ee8 100644 --- a/endpoints/registry.py +++ b/endpoints/registry.py @@ -290,8 +290,14 @@ def put_image_json(namespace, repository, image_id): # If we reach that point, it means that this is a new image or a retry # on a failed push # save the metadata + if parent_id: + parent_obj = model.get_image_by_id(parent_id) + else: + parent_obj = None + model.set_image_metadata(image_id, namespace, repository, - data.get('created'), data.get('comment')) + data.get('created'), data.get('comment'), + parent_obj) store.put_content(mark_path, 'true') store.put_content(json_path, request.data) generate_ancestry(namespace, repository, image_id, parent_id) diff --git a/test.db b/test.db index a1177a60b9dadc0d9bfe307a64958bef9a1c7e8f..de63aaddb19afeab70cc4994e163a6e246e00172 100644 GIT binary patch delta 2340 zcmZuye{56N6~6bL_nvbd8z+R+IKinK(k@wW?Dza^$B1dXCUz2%I1oD_Mv;mWLNH_m zNU|hk6@7G4rTx;}tNxOz{;~ZzDiZupg$iPmhBm?Ikh*nj>KGjiwy-Tsz}Q-CyZ1TG zz}u7F)xGDPbH8&wf9Lq6Me@=G^4;3XEJCP}zn?#K`7NQ^Eb@P{S>`4gZi^4k!l&>M z?!jHSU8t0f0I91j+y!qLd=I|7WQL$SOKiwv%3q>KW=1A*qYYZNLDd@4M-NVLR7xD-2>`d`-Mm)nvFx$j;c>rkb#g?7TtRRJ-i91RfRHOo=2^ln zKVNpDbBDUCE7_JvcN)*D@%8}%T2Kx7gE4<7=u^X6weVJ5Z_z?gO%H3bg8Ud8J2HDZ zQbuMdoyjD0RS*527&X!o)b&s}qDnl5Vj~qwr6b+pSbxu8S4NAplOoKCH5`4b9%+fR zMzoNsazaH+cUT+hZtaP8M72zZ-W6Ppc`QV2ZHcKNEfNWG#{HBUt$mrccyBt*D{1Xl zyNoA`3C09SR1N8>8pI-^nHp`WOguHPvon_LOojF&hDt)BTXn5PQzKfq@b5OeRQ?ff zkeA_2_z}Fzf0#))u>t(Jlto1tOKFmYv6Kf%7)uKUVJt0$RDqwCCTmCQ`Hu@X5PSxI zhrhv};WpghdHoF7^*#p`!ji;Nc-U7V*{>t`2k-DZaGkq+03MqpBTTvKY``~Mc%X2J zQ%nnwcCV8hg9r_x+jx%DlHcXc_QJyOH;DbD*KKpV-T3Ps=6Yczvc*@QJv=%#H9dK3 zs@^wf^tW{y{f&`uI23O3CHvC8zfP$X)#h(swBgNwjJzM@7kxU3d!KwqHT@SG&@ z?64@q@8M&(2=BooSmw=i8P3BZFFg;V>kzU*6%Mops0=O~u&E?cS)ko85RJcX;2}T<$B` z@v#@y_+o+inrGKe^}fEOuikR0_ccDoYSTUstC(+4o^I=p8)>#QzlohrC~SDX2G_9J zc|Z2CXX>lj+4)Xf%i#EP_!;)aQ`5=VhJ(_R|xtxRdMKgYiUmnk&^t z>{v$Cn2O=Zhd*-g*LuW&_h4Fd13Nt6*WE<$Pd=;v1UI=RKLob?vc2BMdBIeK%KYCG zW3VQ6iNLP|`^zhe5V%8vtBAMfGW-^<@}zzU?C0Nf1|1UiV6qBUE>X{FexyvI)mCys zQ9woz86nqT?~<7&WM&hS76NQ>!OM0oG-EIO+JcTZv!#VTFuir`=v)jpSaP^9m#nZ9 ziG~sR5Pyb-@s_;V<1~B0j!dW7)a$2iX0Y??1477VFK?PP^SiHcvyi_zz)Q2t2CZVw z{7)*m_sJ$)UNoBH+{&qEn_vCS(>y=`%1A>HCI|5k&^^9=#bOqrYkA`-r@4&_$+`~V zDxnE>?K_7nfOnx8Eu+ZKv&vx?yYcN)E)p=e+5X3QvO@TFDytuwsf)cmvz?uMpxw4X;&%IlJ_P!p z5?V(77$b@81;y77B7cm6l#LM$QQ{Af1P~uVi~_+xl$O{M6g5il-tI1hWs=RzIrp6X z&iT%F&+e0C_fhiNV#_9kP!anjAKSz;iggpR|5Bi1E&*=wgC+1U+=bh4D`pW!AT|rR zMmPkkr!)n|qr`x0n(kS#y<>PV($?EiC`Sqjkn zXvap;AFd9!tgmgXT_*+QvdZ;|!7ccKeUy| zma^La;|&&0gGmP%69OQL$5!ag#uFHRhf8pRHFgB@azT%51sEk6*cRE=)8A!pi)`;8 z^lNWcK3ahF$eJahIPv^|R|znS8N{D3%RsKdl6eq_L$O0qj0c@Ua1~C$Z3cE67Ul>H zDg}vtvpTyVLtqFG0n!!_$ zwywe-Yznsq%ake-ANBadSxY5OuS4>ARIf{BG;*MDWqp%c9$GKAmIdmhN@pVJVGh%W z>W~!0CwZNO<%pucA>7zhu2wgB)uvXzKcGI^j^ZnkC5PMRkrbbkjl#}T>cVv`f$GpY zwYs53YObqKNnwgo98T5iR$}+dO+wlMt|!<7hv7>$hyfVqN=oHX&z-46>9{kM31MD{ zDV+j$rnW-n)tOSP3B^d=H82wWyj*m2CTfidbwn^-LGTaE!LM*7X4&v9EE2hJ4WL69 ziv$cL{t^2-oFSNZBD52m@w=oHbQ4-ntZCyb#H8)WHe_dK<2|)>dea=eKcb5X?Q@Vv z63R>pQ-Ep2F9=M-T+H(Pb)bKAJz=_z*p|BvcVH%F>Anb0b9liZVDmk6{KZ!04BTLM;79n5ZIjcC{ttj&U9Y1TcBUD@ z$Qb|wbL3C93ppmdAF;Li8C1eXyr1-ud-yV0PClN}MzJBF!&*89`rKHl(Udd}F+)rm zGt4k1jh=m_#s@j0ca&ze_0kixynRO&Gwlf^z-3#`cajhwhrfFD;*lP;~IuXBfSo0zI)OQ%z&4$-Mfjq?!B!xzb32u^8cY}UM3L|VGGGK4tC zN76JGF(Ny&dqjHgz=vi{g=7Hc0cN7Pg+v5q5r;F<$$iAiM))EVm3urA9WWL=6Plc+1r3P=JC(-$^!d=TM-q!&C> r8pSNm_#ke2%uh%6kC{OWhzaK^`W|z>^!6_9G(B)`WX@0C+J)hN*{L76