Use docker/distribution library to resolve image reference.
Signed-off-by: Random-Liu <lantaol@google.com>
This commit is contained in:
		
							
								
								
									
										10
									
								
								Godeps/Godeps.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										10
									
								
								Godeps/Godeps.json
									
									
									
										generated
									
									
									
								
							| @@ -151,6 +151,16 @@ | ||||
| 			"Comment": "v1.1.0", | ||||
| 			"Rev": "346938d642f2ec3594ed81d874461961cd0faa76" | ||||
| 		}, | ||||
| 		{ | ||||
| 			"ImportPath": "github.com/docker/distribution/digestset", | ||||
| 			"Comment": "v2.6.0-rc.1-130-gb38e5838", | ||||
| 			"Rev": "b38e5838b7b2f2ad48e06ec4b500011976080621" | ||||
| 		}, | ||||
| 		{ | ||||
| 			"ImportPath": "github.com/docker/distribution/reference", | ||||
| 			"Comment": "v2.6.0-rc.1-130-gb38e5838", | ||||
| 			"Rev": "b38e5838b7b2f2ad48e06ec4b500011976080621" | ||||
| 		}, | ||||
| 		{ | ||||
| 			"ImportPath": "github.com/docker/docker/pkg/random", | ||||
| 			"Comment": "v1.13.1", | ||||
|   | ||||
| @@ -20,28 +20,42 @@ import ( | ||||
| 	"encoding/json" | ||||
| 	"fmt" | ||||
|  | ||||
| 	"github.com/kubernetes-incubator/cri-containerd/pkg/metadata" | ||||
|  | ||||
| 	"github.com/containerd/containerd/content" | ||||
| 	containerdimages "github.com/containerd/containerd/images" | ||||
| 	"github.com/containerd/containerd/remotes" | ||||
| 	"github.com/containerd/containerd/remotes/docker" | ||||
| 	"github.com/docker/distribution/reference" | ||||
| 	"github.com/golang/glog" | ||||
| 	imagespec "github.com/opencontainers/image-spec/specs-go/v1" | ||||
| 	"golang.org/x/net/context" | ||||
| 	"k8s.io/kubernetes/pkg/kubelet/api/v1alpha1/runtime" | ||||
|  | ||||
| 	"github.com/kubernetes-incubator/cri-containerd/pkg/metadata" | ||||
| ) | ||||
|  | ||||
| // PullImage pulls an image with authentication config. | ||||
| // TODO(mikebrow): add authentication | ||||
| // TODO(mikebrow): harden api (including figuring out at what layer we should be blocking on duplicate requests.) | ||||
| func (c *criContainerdService) PullImage(ctx context.Context, r *runtime.PullImageRequest) (*runtime.PullImageResponse, error) { | ||||
| func (c *criContainerdService) PullImage(ctx context.Context, r *runtime.PullImageRequest) (retRes *runtime.PullImageResponse, retErr error) { | ||||
| 	glog.V(2).Infof("PullImage %q with auth config %+v", r.GetImage().GetImage(), r.GetAuth()) | ||||
| 	defer func() { | ||||
| 		if retErr == nil { | ||||
| 			glog.V(2).Infof("PullImage returns image reference %q", retRes.GetImageRef()) | ||||
| 		} | ||||
| 	}() | ||||
|  | ||||
| 	var ( | ||||
| 		err  error | ||||
| 		size int64 | ||||
| 		desc imagespec.Descriptor | ||||
| 	) | ||||
|  | ||||
| 	image := r.GetImage().Image | ||||
| 	image, err := normalizeImageRef(r.GetImage().GetImage()) | ||||
| 	if err != nil { | ||||
| 		return nil, fmt.Errorf("failed to parse image reference %q: %v", r.GetImage().GetImage(), err) | ||||
| 	} | ||||
| 	if r.GetImage().GetImage() != image { | ||||
| 		glog.V(4).Info("PullImage using normalized image ref: %q", image) | ||||
| 	} | ||||
|  | ||||
| 	if desc, size, err = c.pullImage(ctx, image); err != nil { | ||||
| 		return nil, fmt.Errorf("failed to pull image %q: %v", image, err) | ||||
| @@ -66,6 +80,17 @@ func (c *criContainerdService) PullImage(ctx context.Context, r *runtime.PullIma | ||||
| 	return &runtime.PullImageResponse{ImageRef: digest}, err | ||||
| } | ||||
|  | ||||
| // normalizeImageRef normalizes the image reference following the docker convention. This is added | ||||
| // mainly for backward compatibility. | ||||
| func normalizeImageRef(ref string) (string, error) { | ||||
| 	named, err := reference.ParseNormalizedNamed(ref) | ||||
| 	if err != nil { | ||||
| 		return "", err | ||||
| 	} | ||||
| 	named = reference.TagNameOnly(named) | ||||
| 	return named.String(), nil | ||||
| } | ||||
|  | ||||
| // imageReferenceResolver attempts to resolve the image reference into a name | ||||
| // and manifest via the containerd library call.. | ||||
| // | ||||
|   | ||||
							
								
								
									
										45
									
								
								pkg/server/image_pull_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										45
									
								
								pkg/server/image_pull_test.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,45 @@ | ||||
| /* | ||||
| Copyright 2017 The Kubernetes Authors. | ||||
|  | ||||
| Licensed under the Apache License, Version 2.0 (the "License"); | ||||
| you may not use this file except in compliance with the License. | ||||
| You may obtain a copy of the License at | ||||
|  | ||||
|     http://www.apache.org/licenses/LICENSE-2.0 | ||||
|  | ||||
| Unless required by applicable law or agreed to in writing, software | ||||
| distributed under the License is distributed on an "AS IS" BASIS, | ||||
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
| See the License for the specific language governing permissions and | ||||
| limitations under the License. | ||||
| */ | ||||
|  | ||||
| package server | ||||
|  | ||||
| import ( | ||||
| 	"testing" | ||||
|  | ||||
| 	"github.com/containerd/containerd/reference" | ||||
| 	"github.com/stretchr/testify/assert" | ||||
| ) | ||||
|  | ||||
| func TestNormalizeImageRef(t *testing.T) { | ||||
| 	for _, ref := range []string{ | ||||
| 		"busybox",        // has nothing | ||||
| 		"busybox:latest", // only has tag | ||||
| 		"busybox@sha256:e6693c20186f837fc393390135d8a598a96a833917917789d63766cab6c59582", // only has digest | ||||
| 		"library/busybox",                  // only has path | ||||
| 		"docker.io/busybox",                // only has hostname | ||||
| 		"docker.io/library/busybox",        // has no tag | ||||
| 		"docker.io/busybox:latest",         // has no path | ||||
| 		"library/busybox:latest",           // has no hostname | ||||
| 		"docker.io/library/busybox:latest", // full reference | ||||
| 		"gcr.io/library/busybox",           // gcr reference | ||||
| 	} { | ||||
| 		t.Logf("TestCase %q", ref) | ||||
| 		normalized, err := normalizeImageRef(ref) | ||||
| 		assert.NoError(t, err) | ||||
| 		_, err = reference.Parse(normalized) | ||||
| 		assert.NoError(t, err, "%q should be containerd supported reference", normalized) | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										182
									
								
								vendor/github.com/docker/distribution/AUTHORS
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										182
									
								
								vendor/github.com/docker/distribution/AUTHORS
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,182 @@ | ||||
| a-palchikov <deemok@gmail.com> | ||||
| Aaron Lehmann <aaron.lehmann@docker.com> | ||||
| Aaron Schlesinger <aschlesinger@deis.com> | ||||
| Aaron Vinson <avinson.public@gmail.com> | ||||
| Adam Duke <adam.v.duke@gmail.com> | ||||
| Adam Enger <adamenger@gmail.com> | ||||
| Adrian Mouat <adrian.mouat@gmail.com> | ||||
| Ahmet Alp Balkan <ahmetalpbalkan@gmail.com> | ||||
| Alex Chan <alex.chan@metaswitch.com> | ||||
| Alex Elman <aelman@indeed.com> | ||||
| Alexey Gladkov <gladkov.alexey@gmail.com> | ||||
| allencloud <allen.sun@daocloud.io> | ||||
| amitshukla <ashukla73@hotmail.com> | ||||
| Amy Lindburg <amy.lindburg@docker.com> | ||||
| Andrew Hsu <andrewhsu@acm.org> | ||||
| Andrew Meredith <andymeredith@gmail.com> | ||||
| Andrew T Nguyen <andrew.nguyen@docker.com> | ||||
| Andrey Kostov <kostov.andrey@gmail.com> | ||||
| Andy Goldstein <agoldste@redhat.com> | ||||
| Anis Elleuch <vadmeste@gmail.com> | ||||
| Anton Tiurin <noxiouz@yandex.ru> | ||||
| Antonio Mercado <amercado@thinknode.com> | ||||
| Antonio Murdaca <runcom@redhat.com> | ||||
| Anusha Ragunathan <anusha@docker.com> | ||||
| Arien Holthuizen <aholthuizen@schubergphilis.com> | ||||
| Arnaud Porterie <arnaud.porterie@docker.com> | ||||
| Arthur Baars <arthur@semmle.com> | ||||
| Asuka Suzuki <hello@tanksuzuki.com> | ||||
| Avi Miller <avi.miller@oracle.com> | ||||
| Ayose Cazorla <ayosec@gmail.com> | ||||
| BadZen <dave.trombley@gmail.com> | ||||
| Ben Bodenmiller <bbodenmiller@hotmail.com> | ||||
| Ben Firshman <ben@firshman.co.uk> | ||||
| bin liu <liubin0329@gmail.com> | ||||
| Brian Bland <brian.bland@docker.com> | ||||
| burnettk <burnettk@gmail.com> | ||||
| Carson A <ca@carsonoid.net> | ||||
| Cezar Sa Espinola <cezarsa@gmail.com> | ||||
| Charles Smith <charles.smith@docker.com> | ||||
| Chris Dillon <squarism@gmail.com> | ||||
| cuiwei13 <cuiwei13@pku.edu.cn> | ||||
| cyli <cyli@twistedmatrix.com> | ||||
| Daisuke Fujita <dtanshi45@gmail.com> | ||||
| Daniel Huhn <daniel@danielhuhn.de> | ||||
| Darren Shepherd <darren@rancher.com> | ||||
| Dave Trombley <dave.trombley@gmail.com> | ||||
| Dave Tucker <dt@docker.com> | ||||
| David Lawrence <david.lawrence@docker.com> | ||||
| David Verhasselt <david@crowdway.com> | ||||
| David Xia <dxia@spotify.com> | ||||
| davidli <wenquan.li@hp.com> | ||||
| Dejan Golja <dejan@golja.org> | ||||
| Derek McGowan <derek@mcgstyle.net> | ||||
| Diogo Mónica <diogo.monica@gmail.com> | ||||
| DJ Enriquez <dj.enriquez@infospace.com> | ||||
| Donald Huang <don.hcd@gmail.com> | ||||
| Doug Davis <dug@us.ibm.com> | ||||
| Edgar Lee <edgar.lee@docker.com> | ||||
| Eric Yang <windfarer@gmail.com> | ||||
| Fabio Berchtold <jamesclonk@jamesclonk.ch> | ||||
| Fabio Huser <fabio@fh1.ch> | ||||
| farmerworking <farmerworking@gmail.com> | ||||
| Felix Yan <felixonmars@archlinux.org> | ||||
| Florentin Raud <florentin.raud@gmail.com> | ||||
| Frank Chen <frankchn@gmail.com> | ||||
| Frederick F. Kautz IV <fkautz@alumni.cmu.edu> | ||||
| gabriell nascimento <gabriell@bluesoft.com.br> | ||||
| Gleb Schukin <gschukin@ptsecurity.com> | ||||
| harche <p.harshal@gmail.com> | ||||
| Henri Gomez <henri.gomez@gmail.com> | ||||
| Hu Keping <hukeping@huawei.com> | ||||
| Hua Wang <wanghua.humble@gmail.com> | ||||
| HuKeping <hukeping@huawei.com> | ||||
| Ian Babrou <ibobrik@gmail.com> | ||||
| igayoso <igayoso@gmail.com> | ||||
| Jack Griffin <jackpg14@gmail.com> | ||||
| James Findley <jfindley@fastmail.com> | ||||
| Jason Freidman <jason.freidman@gmail.com> | ||||
| Jason Heiss <jheiss@aput.net> | ||||
| Jeff Nickoloff <jeff@allingeek.com> | ||||
| Jess Frazelle <acidburn@google.com> | ||||
| Jessie Frazelle <jessie@docker.com> | ||||
| jhaohai <jhaohai@foxmail.com> | ||||
| Jianqing Wang <tsing@jianqing.org> | ||||
| Jihoon Chung <jihoon@gmail.com> | ||||
| Joao Fernandes <joao.fernandes@docker.com> | ||||
| John Mulhausen <john@docker.com> | ||||
| John Starks <jostarks@microsoft.com> | ||||
| Jon Johnson <jonjohnson@google.com> | ||||
| Jon Poler <jonathan.poler@apcera.com> | ||||
| Jonathan Boulle <jonathanboulle@gmail.com> | ||||
| Jordan Liggitt <jliggitt@redhat.com> | ||||
| Josh Chorlton <josh.chorlton@docker.com> | ||||
| Josh Hawn <josh.hawn@docker.com> | ||||
| Julien Fernandez <julien.fernandez@gmail.com> | ||||
| Ke Xu <leonhartx.k@gmail.com> | ||||
| Keerthan Mala <kmala@engineyard.com> | ||||
| Kelsey Hightower <kelsey.hightower@gmail.com> | ||||
| Kenneth Lim <kennethlimcp@gmail.com> | ||||
| Kenny Leung <kleung@google.com> | ||||
| Li Yi <denverdino@gmail.com> | ||||
| Liu Hua <sdu.liu@huawei.com> | ||||
| liuchang0812 <liuchang0812@gmail.com> | ||||
| Lloyd Ramey <lnr0626@gmail.com> | ||||
| Louis Kottmann <louis.kottmann@gmail.com> | ||||
| Luke Carpenter <x@rubynerd.net> | ||||
| Marcus Martins <marcus@docker.com> | ||||
| Mary Anthony <mary@docker.com> | ||||
| Matt Bentley <mbentley@mbentley.net> | ||||
| Matt Duch <matt@learnmetrics.com> | ||||
| Matt Moore <mattmoor@google.com> | ||||
| Matt Robenolt <matt@ydekproductions.com> | ||||
| Matthew Green <greenmr@live.co.uk> | ||||
| Michael Prokop <mika@grml.org> | ||||
| Michal Minar <miminar@redhat.com> | ||||
| Michal Minář <miminar@redhat.com> | ||||
| Mike Brown <brownwm@us.ibm.com> | ||||
| Miquel Sabaté <msabate@suse.com> | ||||
| Misty Stanley-Jones <misty@apache.org> | ||||
| Misty Stanley-Jones <misty@docker.com> | ||||
| Morgan Bauer <mbauer@us.ibm.com> | ||||
| moxiegirl <mary@docker.com> | ||||
| Nathan Sullivan <nathan@nightsys.net> | ||||
| nevermosby <robolwq@qq.com> | ||||
| Nghia Tran <tcnghia@gmail.com> | ||||
| Nikita Tarasov <nikita@mygento.ru> | ||||
| Noah Treuhaft <noah.treuhaft@docker.com> | ||||
| Nuutti Kotivuori <nuutti.kotivuori@poplatek.fi> | ||||
| Oilbeater <liumengxinfly@gmail.com> | ||||
| Olivier Gambier <olivier@docker.com> | ||||
| Olivier Jacques <olivier.jacques@hp.com> | ||||
| Omer Cohen <git@omer.io> | ||||
| Patrick Devine <patrick.devine@docker.com> | ||||
| Phil Estes <estesp@linux.vnet.ibm.com> | ||||
| Philip Misiowiec <philip@atlashealth.com> | ||||
| Pierre-Yves Ritschard <pyr@spootnik.org> | ||||
| Qiao Anran <qiaoanran@gmail.com> | ||||
| Randy Barlow <randy@electronsweatshop.com> | ||||
| Richard Scothern <richard.scothern@docker.com> | ||||
| Rodolfo Carvalho <rhcarvalho@gmail.com> | ||||
| Rusty Conover <rusty@luckydinosaur.com> | ||||
| Sean Boran <Boran@users.noreply.github.com> | ||||
| Sebastiaan van Stijn <github@gone.nl> | ||||
| Sebastien Coavoux <s.coavoux@free.fr> | ||||
| Serge Dubrouski <sergeyfd@gmail.com> | ||||
| Sharif Nassar <sharif@mrwacky.com> | ||||
| Shawn Falkner-Horine <dreadpirateshawn@gmail.com> | ||||
| Shreyas Karnik <karnik.shreyas@gmail.com> | ||||
| Simon Thulbourn <simon+github@thulbourn.com> | ||||
| spacexnice <yaoyao.xyy@alibaba-inc.com> | ||||
| Spencer Rinehart <anubis@overthemonkey.com> | ||||
| Stan Hu <stanhu@gmail.com> | ||||
| Stefan Majewsky <stefan.majewsky@sap.com> | ||||
| Stefan Weil <sw@weilnetz.de> | ||||
| Stephen J Day <stephen.day@docker.com> | ||||
| Sungho Moon <sungho.moon@navercorp.com> | ||||
| Sven Dowideit <SvenDowideit@home.org.au> | ||||
| Sylvain Baubeau <sbaubeau@redhat.com> | ||||
| Ted Reed <ted.reed@gmail.com> | ||||
| tgic <farmer1992@gmail.com> | ||||
| Thomas Sjögren <konstruktoid@users.noreply.github.com> | ||||
| Tianon Gravi <admwiggin@gmail.com> | ||||
| Tibor Vass <teabee89@gmail.com> | ||||
| Tonis Tiigi <tonistiigi@gmail.com> | ||||
| Tony Holdstock-Brown <tony@docker.com> | ||||
| Trevor Pounds <trevor.pounds@gmail.com> | ||||
| Troels Thomsen <troels@thomsen.io> | ||||
| Victor Vieux <vieux@docker.com> | ||||
| Victoria Bialas <victoria.bialas@docker.com> | ||||
| Vincent Batts <vbatts@redhat.com> | ||||
| Vincent Demeester <vincent@sbr.pm> | ||||
| Vincent Giersch <vincent.giersch@ovh.net> | ||||
| W. Trevor King <wking@tremily.us> | ||||
| weiyuan.yl <weiyuan.yl@alibaba-inc.com> | ||||
| xg.song <xg.song@venusource.com> | ||||
| xiekeyang <xiekeyang@huawei.com> | ||||
| Yann ROBERT <yann.robert@anantaplex.fr> | ||||
| yaoyao.xyy <yaoyao.xyy@alibaba-inc.com> | ||||
| yuexiao-wang <wang.yuexiao@zte.com.cn> | ||||
| yuzou <zouyu7@huawei.com> | ||||
| zhouhaibing089 <zhouhaibing089@gmail.com> | ||||
| 姜继忠 <jizhong.jiangjz@alibaba-inc.com> | ||||
							
								
								
									
										202
									
								
								vendor/github.com/docker/distribution/LICENSE
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										202
									
								
								vendor/github.com/docker/distribution/LICENSE
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,202 @@ | ||||
| Apache License | ||||
|                            Version 2.0, January 2004 | ||||
|                         http://www.apache.org/licenses/ | ||||
|  | ||||
|    TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION | ||||
|  | ||||
|    1. Definitions. | ||||
|  | ||||
|       "License" shall mean the terms and conditions for use, reproduction, | ||||
|       and distribution as defined by Sections 1 through 9 of this document. | ||||
|  | ||||
|       "Licensor" shall mean the copyright owner or entity authorized by | ||||
|       the copyright owner that is granting the License. | ||||
|  | ||||
|       "Legal Entity" shall mean the union of the acting entity and all | ||||
|       other entities that control, are controlled by, or are under common | ||||
|       control with that entity. For the purposes of this definition, | ||||
|       "control" means (i) the power, direct or indirect, to cause the | ||||
|       direction or management of such entity, whether by contract or | ||||
|       otherwise, or (ii) ownership of fifty percent (50%) or more of the | ||||
|       outstanding shares, or (iii) beneficial ownership of such entity. | ||||
|  | ||||
|       "You" (or "Your") shall mean an individual or Legal Entity | ||||
|       exercising permissions granted by this License. | ||||
|  | ||||
|       "Source" form shall mean the preferred form for making modifications, | ||||
|       including but not limited to software source code, documentation | ||||
|       source, and configuration files. | ||||
|  | ||||
|       "Object" form shall mean any form resulting from mechanical | ||||
|       transformation or translation of a Source form, including but | ||||
|       not limited to compiled object code, generated documentation, | ||||
|       and conversions to other media types. | ||||
|  | ||||
|       "Work" shall mean the work of authorship, whether in Source or | ||||
|       Object form, made available under the License, as indicated by a | ||||
|       copyright notice that is included in or attached to the work | ||||
|       (an example is provided in the Appendix below). | ||||
|  | ||||
|       "Derivative Works" shall mean any work, whether in Source or Object | ||||
|       form, that is based on (or derived from) the Work and for which the | ||||
|       editorial revisions, annotations, elaborations, or other modifications | ||||
|       represent, as a whole, an original work of authorship. For the purposes | ||||
|       of this License, Derivative Works shall not include works that remain | ||||
|       separable from, or merely link (or bind by name) to the interfaces of, | ||||
|       the Work and Derivative Works thereof. | ||||
|  | ||||
|       "Contribution" shall mean any work of authorship, including | ||||
|       the original version of the Work and any modifications or additions | ||||
|       to that Work or Derivative Works thereof, that is intentionally | ||||
|       submitted to Licensor for inclusion in the Work by the copyright owner | ||||
|       or by an individual or Legal Entity authorized to submit on behalf of | ||||
|       the copyright owner. For the purposes of this definition, "submitted" | ||||
|       means any form of electronic, verbal, or written communication sent | ||||
|       to the Licensor or its representatives, including but not limited to | ||||
|       communication on electronic mailing lists, source code control systems, | ||||
|       and issue tracking systems that are managed by, or on behalf of, the | ||||
|       Licensor for the purpose of discussing and improving the Work, but | ||||
|       excluding communication that is conspicuously marked or otherwise | ||||
|       designated in writing by the copyright owner as "Not a Contribution." | ||||
|  | ||||
|       "Contributor" shall mean Licensor and any individual or Legal Entity | ||||
|       on behalf of whom a Contribution has been received by Licensor and | ||||
|       subsequently incorporated within the Work. | ||||
|  | ||||
|    2. Grant of Copyright License. Subject to the terms and conditions of | ||||
|       this License, each Contributor hereby grants to You a perpetual, | ||||
|       worldwide, non-exclusive, no-charge, royalty-free, irrevocable | ||||
|       copyright license to reproduce, prepare Derivative Works of, | ||||
|       publicly display, publicly perform, sublicense, and distribute the | ||||
|       Work and such Derivative Works in Source or Object form. | ||||
|  | ||||
|    3. Grant of Patent License. Subject to the terms and conditions of | ||||
|       this License, each Contributor hereby grants to You a perpetual, | ||||
|       worldwide, non-exclusive, no-charge, royalty-free, irrevocable | ||||
|       (except as stated in this section) patent license to make, have made, | ||||
|       use, offer to sell, sell, import, and otherwise transfer the Work, | ||||
|       where such license applies only to those patent claims licensable | ||||
|       by such Contributor that are necessarily infringed by their | ||||
|       Contribution(s) alone or by combination of their Contribution(s) | ||||
|       with the Work to which such Contribution(s) was submitted. If You | ||||
|       institute patent litigation against any entity (including a | ||||
|       cross-claim or counterclaim in a lawsuit) alleging that the Work | ||||
|       or a Contribution incorporated within the Work constitutes direct | ||||
|       or contributory patent infringement, then any patent licenses | ||||
|       granted to You under this License for that Work shall terminate | ||||
|       as of the date such litigation is filed. | ||||
|  | ||||
|    4. Redistribution. You may reproduce and distribute copies of the | ||||
|       Work or Derivative Works thereof in any medium, with or without | ||||
|       modifications, and in Source or Object form, provided that You | ||||
|       meet the following conditions: | ||||
|  | ||||
|       (a) You must give any other recipients of the Work or | ||||
|           Derivative Works a copy of this License; and | ||||
|  | ||||
|       (b) You must cause any modified files to carry prominent notices | ||||
|           stating that You changed the files; and | ||||
|  | ||||
|       (c) You must retain, in the Source form of any Derivative Works | ||||
|           that You distribute, all copyright, patent, trademark, and | ||||
|           attribution notices from the Source form of the Work, | ||||
|           excluding those notices that do not pertain to any part of | ||||
|           the Derivative Works; and | ||||
|  | ||||
|       (d) If the Work includes a "NOTICE" text file as part of its | ||||
|           distribution, then any Derivative Works that You distribute must | ||||
|           include a readable copy of the attribution notices contained | ||||
|           within such NOTICE file, excluding those notices that do not | ||||
|           pertain to any part of the Derivative Works, in at least one | ||||
|           of the following places: within a NOTICE text file distributed | ||||
|           as part of the Derivative Works; within the Source form or | ||||
|           documentation, if provided along with the Derivative Works; or, | ||||
|           within a display generated by the Derivative Works, if and | ||||
|           wherever such third-party notices normally appear. The contents | ||||
|           of the NOTICE file are for informational purposes only and | ||||
|           do not modify the License. You may add Your own attribution | ||||
|           notices within Derivative Works that You distribute, alongside | ||||
|           or as an addendum to the NOTICE text from the Work, provided | ||||
|           that such additional attribution notices cannot be construed | ||||
|           as modifying the License. | ||||
|  | ||||
|       You may add Your own copyright statement to Your modifications and | ||||
|       may provide additional or different license terms and conditions | ||||
|       for use, reproduction, or distribution of Your modifications, or | ||||
|       for any such Derivative Works as a whole, provided Your use, | ||||
|       reproduction, and distribution of the Work otherwise complies with | ||||
|       the conditions stated in this License. | ||||
|  | ||||
|    5. Submission of Contributions. Unless You explicitly state otherwise, | ||||
|       any Contribution intentionally submitted for inclusion in the Work | ||||
|       by You to the Licensor shall be under the terms and conditions of | ||||
|       this License, without any additional terms or conditions. | ||||
|       Notwithstanding the above, nothing herein shall supersede or modify | ||||
|       the terms of any separate license agreement you may have executed | ||||
|       with Licensor regarding such Contributions. | ||||
|  | ||||
|    6. Trademarks. This License does not grant permission to use the trade | ||||
|       names, trademarks, service marks, or product names of the Licensor, | ||||
|       except as required for reasonable and customary use in describing the | ||||
|       origin of the Work and reproducing the content of the NOTICE file. | ||||
|  | ||||
|    7. Disclaimer of Warranty. Unless required by applicable law or | ||||
|       agreed to in writing, Licensor provides the Work (and each | ||||
|       Contributor provides its Contributions) on an "AS IS" BASIS, | ||||
|       WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or | ||||
|       implied, including, without limitation, any warranties or conditions | ||||
|       of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A | ||||
|       PARTICULAR PURPOSE. You are solely responsible for determining the | ||||
|       appropriateness of using or redistributing the Work and assume any | ||||
|       risks associated with Your exercise of permissions under this License. | ||||
|  | ||||
|    8. Limitation of Liability. In no event and under no legal theory, | ||||
|       whether in tort (including negligence), contract, or otherwise, | ||||
|       unless required by applicable law (such as deliberate and grossly | ||||
|       negligent acts) or agreed to in writing, shall any Contributor be | ||||
|       liable to You for damages, including any direct, indirect, special, | ||||
|       incidental, or consequential damages of any character arising as a | ||||
|       result of this License or out of the use or inability to use the | ||||
|       Work (including but not limited to damages for loss of goodwill, | ||||
|       work stoppage, computer failure or malfunction, or any and all | ||||
|       other commercial damages or losses), even if such Contributor | ||||
|       has been advised of the possibility of such damages. | ||||
|  | ||||
|    9. Accepting Warranty or Additional Liability. While redistributing | ||||
|       the Work or Derivative Works thereof, You may choose to offer, | ||||
|       and charge a fee for, acceptance of support, warranty, indemnity, | ||||
|       or other liability obligations and/or rights consistent with this | ||||
|       License. However, in accepting such obligations, You may act only | ||||
|       on Your own behalf and on Your sole responsibility, not on behalf | ||||
|       of any other Contributor, and only if You agree to indemnify, | ||||
|       defend, and hold each Contributor harmless for any liability | ||||
|       incurred by, or claims asserted against, such Contributor by reason | ||||
|       of your accepting any such warranty or additional liability. | ||||
|  | ||||
|    END OF TERMS AND CONDITIONS | ||||
|  | ||||
|    APPENDIX: How to apply the Apache License to your work. | ||||
|  | ||||
|       To apply the Apache License to your work, attach the following | ||||
|       boilerplate notice, with the fields enclosed by brackets "{}" | ||||
|       replaced with your own identifying information. (Don't include | ||||
|       the brackets!)  The text should be enclosed in the appropriate | ||||
|       comment syntax for the file format. We also recommend that a | ||||
|       file or class name and description of purpose be included on the | ||||
|       same "printed page" as the copyright notice for easier | ||||
|       identification within third-party archives. | ||||
|  | ||||
|    Copyright {yyyy} {name of copyright owner} | ||||
|  | ||||
|    Licensed under the Apache License, Version 2.0 (the "License"); | ||||
|    you may not use this file except in compliance with the License. | ||||
|    You may obtain a copy of the License at | ||||
|  | ||||
|        http://www.apache.org/licenses/LICENSE-2.0 | ||||
|  | ||||
|    Unless required by applicable law or agreed to in writing, software | ||||
|    distributed under the License is distributed on an "AS IS" BASIS, | ||||
|    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
|    See the License for the specific language governing permissions and | ||||
|    limitations under the License. | ||||
|  | ||||
							
								
								
									
										247
									
								
								vendor/github.com/docker/distribution/digestset/set.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										247
									
								
								vendor/github.com/docker/distribution/digestset/set.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,247 @@ | ||||
| package digestset | ||||
|  | ||||
| import ( | ||||
| 	"errors" | ||||
| 	"sort" | ||||
| 	"strings" | ||||
| 	"sync" | ||||
|  | ||||
| 	digest "github.com/opencontainers/go-digest" | ||||
| ) | ||||
|  | ||||
| var ( | ||||
| 	// ErrDigestNotFound is used when a matching digest | ||||
| 	// could not be found in a set. | ||||
| 	ErrDigestNotFound = errors.New("digest not found") | ||||
|  | ||||
| 	// ErrDigestAmbiguous is used when multiple digests | ||||
| 	// are found in a set. None of the matching digests | ||||
| 	// should be considered valid matches. | ||||
| 	ErrDigestAmbiguous = errors.New("ambiguous digest string") | ||||
| ) | ||||
|  | ||||
| // Set is used to hold a unique set of digests which | ||||
| // may be easily referenced by easily  referenced by a string | ||||
| // representation of the digest as well as short representation. | ||||
| // The uniqueness of the short representation is based on other | ||||
| // digests in the set. If digests are omitted from this set, | ||||
| // collisions in a larger set may not be detected, therefore it | ||||
| // is important to always do short representation lookups on | ||||
| // the complete set of digests. To mitigate collisions, an | ||||
| // appropriately long short code should be used. | ||||
| type Set struct { | ||||
| 	mutex   sync.RWMutex | ||||
| 	entries digestEntries | ||||
| } | ||||
|  | ||||
| // NewSet creates an empty set of digests | ||||
| // which may have digests added. | ||||
| func NewSet() *Set { | ||||
| 	return &Set{ | ||||
| 		entries: digestEntries{}, | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // checkShortMatch checks whether two digests match as either whole | ||||
| // values or short values. This function does not test equality, | ||||
| // rather whether the second value could match against the first | ||||
| // value. | ||||
| func checkShortMatch(alg digest.Algorithm, hex, shortAlg, shortHex string) bool { | ||||
| 	if len(hex) == len(shortHex) { | ||||
| 		if hex != shortHex { | ||||
| 			return false | ||||
| 		} | ||||
| 		if len(shortAlg) > 0 && string(alg) != shortAlg { | ||||
| 			return false | ||||
| 		} | ||||
| 	} else if !strings.HasPrefix(hex, shortHex) { | ||||
| 		return false | ||||
| 	} else if len(shortAlg) > 0 && string(alg) != shortAlg { | ||||
| 		return false | ||||
| 	} | ||||
| 	return true | ||||
| } | ||||
|  | ||||
| // Lookup looks for a digest matching the given string representation. | ||||
| // If no digests could be found ErrDigestNotFound will be returned | ||||
| // with an empty digest value. If multiple matches are found | ||||
| // ErrDigestAmbiguous will be returned with an empty digest value. | ||||
| func (dst *Set) Lookup(d string) (digest.Digest, error) { | ||||
| 	dst.mutex.RLock() | ||||
| 	defer dst.mutex.RUnlock() | ||||
| 	if len(dst.entries) == 0 { | ||||
| 		return "", ErrDigestNotFound | ||||
| 	} | ||||
| 	var ( | ||||
| 		searchFunc func(int) bool | ||||
| 		alg        digest.Algorithm | ||||
| 		hex        string | ||||
| 	) | ||||
| 	dgst, err := digest.Parse(d) | ||||
| 	if err == digest.ErrDigestInvalidFormat { | ||||
| 		hex = d | ||||
| 		searchFunc = func(i int) bool { | ||||
| 			return dst.entries[i].val >= d | ||||
| 		} | ||||
| 	} else { | ||||
| 		hex = dgst.Hex() | ||||
| 		alg = dgst.Algorithm() | ||||
| 		searchFunc = func(i int) bool { | ||||
| 			if dst.entries[i].val == hex { | ||||
| 				return dst.entries[i].alg >= alg | ||||
| 			} | ||||
| 			return dst.entries[i].val >= hex | ||||
| 		} | ||||
| 	} | ||||
| 	idx := sort.Search(len(dst.entries), searchFunc) | ||||
| 	if idx == len(dst.entries) || !checkShortMatch(dst.entries[idx].alg, dst.entries[idx].val, string(alg), hex) { | ||||
| 		return "", ErrDigestNotFound | ||||
| 	} | ||||
| 	if dst.entries[idx].alg == alg && dst.entries[idx].val == hex { | ||||
| 		return dst.entries[idx].digest, nil | ||||
| 	} | ||||
| 	if idx+1 < len(dst.entries) && checkShortMatch(dst.entries[idx+1].alg, dst.entries[idx+1].val, string(alg), hex) { | ||||
| 		return "", ErrDigestAmbiguous | ||||
| 	} | ||||
|  | ||||
| 	return dst.entries[idx].digest, nil | ||||
| } | ||||
|  | ||||
| // Add adds the given digest to the set. An error will be returned | ||||
| // if the given digest is invalid. If the digest already exists in the | ||||
| // set, this operation will be a no-op. | ||||
| func (dst *Set) Add(d digest.Digest) error { | ||||
| 	if err := d.Validate(); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	dst.mutex.Lock() | ||||
| 	defer dst.mutex.Unlock() | ||||
| 	entry := &digestEntry{alg: d.Algorithm(), val: d.Hex(), digest: d} | ||||
| 	searchFunc := func(i int) bool { | ||||
| 		if dst.entries[i].val == entry.val { | ||||
| 			return dst.entries[i].alg >= entry.alg | ||||
| 		} | ||||
| 		return dst.entries[i].val >= entry.val | ||||
| 	} | ||||
| 	idx := sort.Search(len(dst.entries), searchFunc) | ||||
| 	if idx == len(dst.entries) { | ||||
| 		dst.entries = append(dst.entries, entry) | ||||
| 		return nil | ||||
| 	} else if dst.entries[idx].digest == d { | ||||
| 		return nil | ||||
| 	} | ||||
|  | ||||
| 	entries := append(dst.entries, nil) | ||||
| 	copy(entries[idx+1:], entries[idx:len(entries)-1]) | ||||
| 	entries[idx] = entry | ||||
| 	dst.entries = entries | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| // Remove removes the given digest from the set. An err will be | ||||
| // returned if the given digest is invalid. If the digest does | ||||
| // not exist in the set, this operation will be a no-op. | ||||
| func (dst *Set) Remove(d digest.Digest) error { | ||||
| 	if err := d.Validate(); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	dst.mutex.Lock() | ||||
| 	defer dst.mutex.Unlock() | ||||
| 	entry := &digestEntry{alg: d.Algorithm(), val: d.Hex(), digest: d} | ||||
| 	searchFunc := func(i int) bool { | ||||
| 		if dst.entries[i].val == entry.val { | ||||
| 			return dst.entries[i].alg >= entry.alg | ||||
| 		} | ||||
| 		return dst.entries[i].val >= entry.val | ||||
| 	} | ||||
| 	idx := sort.Search(len(dst.entries), searchFunc) | ||||
| 	// Not found if idx is after or value at idx is not digest | ||||
| 	if idx == len(dst.entries) || dst.entries[idx].digest != d { | ||||
| 		return nil | ||||
| 	} | ||||
|  | ||||
| 	entries := dst.entries | ||||
| 	copy(entries[idx:], entries[idx+1:]) | ||||
| 	entries = entries[:len(entries)-1] | ||||
| 	dst.entries = entries | ||||
|  | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| // All returns all the digests in the set | ||||
| func (dst *Set) All() []digest.Digest { | ||||
| 	dst.mutex.RLock() | ||||
| 	defer dst.mutex.RUnlock() | ||||
| 	retValues := make([]digest.Digest, len(dst.entries)) | ||||
| 	for i := range dst.entries { | ||||
| 		retValues[i] = dst.entries[i].digest | ||||
| 	} | ||||
|  | ||||
| 	return retValues | ||||
| } | ||||
|  | ||||
| // ShortCodeTable returns a map of Digest to unique short codes. The | ||||
| // length represents the minimum value, the maximum length may be the | ||||
| // entire value of digest if uniqueness cannot be achieved without the | ||||
| // full value. This function will attempt to make short codes as short | ||||
| // as possible to be unique. | ||||
| func ShortCodeTable(dst *Set, length int) map[digest.Digest]string { | ||||
| 	dst.mutex.RLock() | ||||
| 	defer dst.mutex.RUnlock() | ||||
| 	m := make(map[digest.Digest]string, len(dst.entries)) | ||||
| 	l := length | ||||
| 	resetIdx := 0 | ||||
| 	for i := 0; i < len(dst.entries); i++ { | ||||
| 		var short string | ||||
| 		extended := true | ||||
| 		for extended { | ||||
| 			extended = false | ||||
| 			if len(dst.entries[i].val) <= l { | ||||
| 				short = dst.entries[i].digest.String() | ||||
| 			} else { | ||||
| 				short = dst.entries[i].val[:l] | ||||
| 				for j := i + 1; j < len(dst.entries); j++ { | ||||
| 					if checkShortMatch(dst.entries[j].alg, dst.entries[j].val, "", short) { | ||||
| 						if j > resetIdx { | ||||
| 							resetIdx = j | ||||
| 						} | ||||
| 						extended = true | ||||
| 					} else { | ||||
| 						break | ||||
| 					} | ||||
| 				} | ||||
| 				if extended { | ||||
| 					l++ | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 		m[dst.entries[i].digest] = short | ||||
| 		if i >= resetIdx { | ||||
| 			l = length | ||||
| 		} | ||||
| 	} | ||||
| 	return m | ||||
| } | ||||
|  | ||||
| type digestEntry struct { | ||||
| 	alg    digest.Algorithm | ||||
| 	val    string | ||||
| 	digest digest.Digest | ||||
| } | ||||
|  | ||||
| type digestEntries []*digestEntry | ||||
|  | ||||
| func (d digestEntries) Len() int { | ||||
| 	return len(d) | ||||
| } | ||||
|  | ||||
| func (d digestEntries) Less(i, j int) bool { | ||||
| 	if d[i].val != d[j].val { | ||||
| 		return d[i].val < d[j].val | ||||
| 	} | ||||
| 	return d[i].alg < d[j].alg | ||||
| } | ||||
|  | ||||
| func (d digestEntries) Swap(i, j int) { | ||||
| 	d[i], d[j] = d[j], d[i] | ||||
| } | ||||
							
								
								
									
										42
									
								
								vendor/github.com/docker/distribution/reference/helpers.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										42
									
								
								vendor/github.com/docker/distribution/reference/helpers.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,42 @@ | ||||
| package reference | ||||
|  | ||||
| import "path" | ||||
|  | ||||
| // IsNameOnly returns true if reference only contains a repo name. | ||||
| func IsNameOnly(ref Named) bool { | ||||
| 	if _, ok := ref.(NamedTagged); ok { | ||||
| 		return false | ||||
| 	} | ||||
| 	if _, ok := ref.(Canonical); ok { | ||||
| 		return false | ||||
| 	} | ||||
| 	return true | ||||
| } | ||||
|  | ||||
| // FamiliarName returns the familiar name string | ||||
| // for the given named, familiarizing if needed. | ||||
| func FamiliarName(ref Named) string { | ||||
| 	if nn, ok := ref.(normalizedNamed); ok { | ||||
| 		return nn.Familiar().Name() | ||||
| 	} | ||||
| 	return ref.Name() | ||||
| } | ||||
|  | ||||
| // FamiliarString returns the familiar string representation | ||||
| // for the given reference, familiarizing if needed. | ||||
| func FamiliarString(ref Reference) string { | ||||
| 	if nn, ok := ref.(normalizedNamed); ok { | ||||
| 		return nn.Familiar().String() | ||||
| 	} | ||||
| 	return ref.String() | ||||
| } | ||||
|  | ||||
| // FamiliarMatch reports whether ref matches the specified pattern. | ||||
| // See https://godoc.org/path#Match for supported patterns. | ||||
| func FamiliarMatch(pattern string, ref Reference) (bool, error) { | ||||
| 	matched, err := path.Match(pattern, FamiliarString(ref)) | ||||
| 	if namedRef, isNamed := ref.(Named); isNamed && !matched { | ||||
| 		matched, _ = path.Match(pattern, FamiliarName(namedRef)) | ||||
| 	} | ||||
| 	return matched, err | ||||
| } | ||||
							
								
								
									
										170
									
								
								vendor/github.com/docker/distribution/reference/normalize.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										170
									
								
								vendor/github.com/docker/distribution/reference/normalize.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,170 @@ | ||||
| package reference | ||||
|  | ||||
| import ( | ||||
| 	"errors" | ||||
| 	"fmt" | ||||
| 	"strings" | ||||
|  | ||||
| 	"github.com/docker/distribution/digestset" | ||||
| 	"github.com/opencontainers/go-digest" | ||||
| ) | ||||
|  | ||||
| var ( | ||||
| 	legacyDefaultDomain = "index.docker.io" | ||||
| 	defaultDomain       = "docker.io" | ||||
| 	officialRepoName    = "library" | ||||
| 	defaultTag          = "latest" | ||||
| ) | ||||
|  | ||||
| // normalizedNamed represents a name which has been | ||||
| // normalized and has a familiar form. A familiar name | ||||
| // is what is used in Docker UI. An example normalized | ||||
| // name is "docker.io/library/ubuntu" and corresponding | ||||
| // familiar name of "ubuntu". | ||||
| type normalizedNamed interface { | ||||
| 	Named | ||||
| 	Familiar() Named | ||||
| } | ||||
|  | ||||
| // ParseNormalizedNamed parses a string into a named reference | ||||
| // transforming a familiar name from Docker UI to a fully | ||||
| // qualified reference. If the value may be an identifier | ||||
| // use ParseAnyReference. | ||||
| func ParseNormalizedNamed(s string) (Named, error) { | ||||
| 	if ok := anchoredIdentifierRegexp.MatchString(s); ok { | ||||
| 		return nil, fmt.Errorf("invalid repository name (%s), cannot specify 64-byte hexadecimal strings", s) | ||||
| 	} | ||||
| 	domain, remainder := splitDockerDomain(s) | ||||
| 	var remoteName string | ||||
| 	if tagSep := strings.IndexRune(remainder, ':'); tagSep > -1 { | ||||
| 		remoteName = remainder[:tagSep] | ||||
| 	} else { | ||||
| 		remoteName = remainder | ||||
| 	} | ||||
| 	if strings.ToLower(remoteName) != remoteName { | ||||
| 		return nil, errors.New("invalid reference format: repository name must be lowercase") | ||||
| 	} | ||||
|  | ||||
| 	ref, err := Parse(domain + "/" + remainder) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	named, isNamed := ref.(Named) | ||||
| 	if !isNamed { | ||||
| 		return nil, fmt.Errorf("reference %s has no name", ref.String()) | ||||
| 	} | ||||
| 	return named, nil | ||||
| } | ||||
|  | ||||
| // splitDockerDomain splits a repository name to domain and remotename string. | ||||
| // If no valid domain is found, the default domain is used. Repository name | ||||
| // needs to be already validated before. | ||||
| func splitDockerDomain(name string) (domain, remainder string) { | ||||
| 	i := strings.IndexRune(name, '/') | ||||
| 	if i == -1 || (!strings.ContainsAny(name[:i], ".:") && name[:i] != "localhost") { | ||||
| 		domain, remainder = defaultDomain, name | ||||
| 	} else { | ||||
| 		domain, remainder = name[:i], name[i+1:] | ||||
| 	} | ||||
| 	if domain == legacyDefaultDomain { | ||||
| 		domain = defaultDomain | ||||
| 	} | ||||
| 	if domain == defaultDomain && !strings.ContainsRune(remainder, '/') { | ||||
| 		remainder = officialRepoName + "/" + remainder | ||||
| 	} | ||||
| 	return | ||||
| } | ||||
|  | ||||
| // familiarizeName returns a shortened version of the name familiar | ||||
| // to to the Docker UI. Familiar names have the default domain | ||||
| // "docker.io" and "library/" repository prefix removed. | ||||
| // For example, "docker.io/library/redis" will have the familiar | ||||
| // name "redis" and "docker.io/dmcgowan/myapp" will be "dmcgowan/myapp". | ||||
| // Returns a familiarized named only reference. | ||||
| func familiarizeName(named namedRepository) repository { | ||||
| 	repo := repository{ | ||||
| 		domain: named.Domain(), | ||||
| 		path:   named.Path(), | ||||
| 	} | ||||
|  | ||||
| 	if repo.domain == defaultDomain { | ||||
| 		repo.domain = "" | ||||
| 		// Handle official repositories which have the pattern "library/<official repo name>" | ||||
| 		if split := strings.Split(repo.path, "/"); len(split) == 2 && split[0] == officialRepoName { | ||||
| 			repo.path = split[1] | ||||
| 		} | ||||
| 	} | ||||
| 	return repo | ||||
| } | ||||
|  | ||||
| func (r reference) Familiar() Named { | ||||
| 	return reference{ | ||||
| 		namedRepository: familiarizeName(r.namedRepository), | ||||
| 		tag:             r.tag, | ||||
| 		digest:          r.digest, | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func (r repository) Familiar() Named { | ||||
| 	return familiarizeName(r) | ||||
| } | ||||
|  | ||||
| func (t taggedReference) Familiar() Named { | ||||
| 	return taggedReference{ | ||||
| 		namedRepository: familiarizeName(t.namedRepository), | ||||
| 		tag:             t.tag, | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func (c canonicalReference) Familiar() Named { | ||||
| 	return canonicalReference{ | ||||
| 		namedRepository: familiarizeName(c.namedRepository), | ||||
| 		digest:          c.digest, | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // TagNameOnly adds the default tag "latest" to a reference if it only has | ||||
| // a repo name. | ||||
| func TagNameOnly(ref Named) Named { | ||||
| 	if IsNameOnly(ref) { | ||||
| 		namedTagged, err := WithTag(ref, defaultTag) | ||||
| 		if err != nil { | ||||
| 			// Default tag must be valid, to create a NamedTagged | ||||
| 			// type with non-validated input the WithTag function | ||||
| 			// should be used instead | ||||
| 			panic(err) | ||||
| 		} | ||||
| 		return namedTagged | ||||
| 	} | ||||
| 	return ref | ||||
| } | ||||
|  | ||||
| // ParseAnyReference parses a reference string as a possible identifier, | ||||
| // full digest, or familiar name. | ||||
| func ParseAnyReference(ref string) (Reference, error) { | ||||
| 	if ok := anchoredIdentifierRegexp.MatchString(ref); ok { | ||||
| 		return digestReference("sha256:" + ref), nil | ||||
| 	} | ||||
| 	if dgst, err := digest.Parse(ref); err == nil { | ||||
| 		return digestReference(dgst), nil | ||||
| 	} | ||||
|  | ||||
| 	return ParseNormalizedNamed(ref) | ||||
| } | ||||
|  | ||||
| // ParseAnyReferenceWithSet parses a reference string as a possible short | ||||
| // identifier to be matched in a digest set, a full digest, or familiar name. | ||||
| func ParseAnyReferenceWithSet(ref string, ds *digestset.Set) (Reference, error) { | ||||
| 	if ok := anchoredShortIdentifierRegexp.MatchString(ref); ok { | ||||
| 		dgst, err := ds.Lookup(ref) | ||||
| 		if err == nil { | ||||
| 			return digestReference(dgst), nil | ||||
| 		} | ||||
| 	} else { | ||||
| 		if dgst, err := digest.Parse(ref); err == nil { | ||||
| 			return digestReference(dgst), nil | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	return ParseNormalizedNamed(ref) | ||||
| } | ||||
							
								
								
									
										433
									
								
								vendor/github.com/docker/distribution/reference/reference.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										433
									
								
								vendor/github.com/docker/distribution/reference/reference.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,433 @@ | ||||
| // Package reference provides a general type to represent any way of referencing images within the registry. | ||||
| // Its main purpose is to abstract tags and digests (content-addressable hash). | ||||
| // | ||||
| // Grammar | ||||
| // | ||||
| // 	reference                       := name [ ":" tag ] [ "@" digest ] | ||||
| //	name                            := [domain '/'] path-component ['/' path-component]* | ||||
| //	domain                          := domain-component ['.' domain-component]* [':' port-number] | ||||
| //	domain-component                := /([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9-]*[a-zA-Z0-9])/ | ||||
| //	port-number                     := /[0-9]+/ | ||||
| //	path-component                  := alpha-numeric [separator alpha-numeric]* | ||||
| // 	alpha-numeric                   := /[a-z0-9]+/ | ||||
| //	separator                       := /[_.]|__|[-]*/ | ||||
| // | ||||
| //	tag                             := /[\w][\w.-]{0,127}/ | ||||
| // | ||||
| //	digest                          := digest-algorithm ":" digest-hex | ||||
| //	digest-algorithm                := digest-algorithm-component [ digest-algorithm-separator digest-algorithm-component ] | ||||
| //	digest-algorithm-separator      := /[+.-_]/ | ||||
| //	digest-algorithm-component      := /[A-Za-z][A-Za-z0-9]*/ | ||||
| //	digest-hex                      := /[0-9a-fA-F]{32,}/ ; At least 128 bit digest value | ||||
| // | ||||
| //	identifier                      := /[a-f0-9]{64}/ | ||||
| //	short-identifier                := /[a-f0-9]{6,64}/ | ||||
| package reference | ||||
|  | ||||
| import ( | ||||
| 	"errors" | ||||
| 	"fmt" | ||||
| 	"strings" | ||||
|  | ||||
| 	"github.com/opencontainers/go-digest" | ||||
| ) | ||||
|  | ||||
| const ( | ||||
| 	// NameTotalLengthMax is the maximum total number of characters in a repository name. | ||||
| 	NameTotalLengthMax = 255 | ||||
| ) | ||||
|  | ||||
| var ( | ||||
| 	// ErrReferenceInvalidFormat represents an error while trying to parse a string as a reference. | ||||
| 	ErrReferenceInvalidFormat = errors.New("invalid reference format") | ||||
|  | ||||
| 	// ErrTagInvalidFormat represents an error while trying to parse a string as a tag. | ||||
| 	ErrTagInvalidFormat = errors.New("invalid tag format") | ||||
|  | ||||
| 	// ErrDigestInvalidFormat represents an error while trying to parse a string as a tag. | ||||
| 	ErrDigestInvalidFormat = errors.New("invalid digest format") | ||||
|  | ||||
| 	// ErrNameContainsUppercase is returned for invalid repository names that contain uppercase characters. | ||||
| 	ErrNameContainsUppercase = errors.New("repository name must be lowercase") | ||||
|  | ||||
| 	// ErrNameEmpty is returned for empty, invalid repository names. | ||||
| 	ErrNameEmpty = errors.New("repository name must have at least one component") | ||||
|  | ||||
| 	// ErrNameTooLong is returned when a repository name is longer than NameTotalLengthMax. | ||||
| 	ErrNameTooLong = fmt.Errorf("repository name must not be more than %v characters", NameTotalLengthMax) | ||||
|  | ||||
| 	// ErrNameNotCanonical is returned when a name is not canonical. | ||||
| 	ErrNameNotCanonical = errors.New("repository name must be canonical") | ||||
| ) | ||||
|  | ||||
| // Reference is an opaque object reference identifier that may include | ||||
| // modifiers such as a hostname, name, tag, and digest. | ||||
| type Reference interface { | ||||
| 	// String returns the full reference | ||||
| 	String() string | ||||
| } | ||||
|  | ||||
| // Field provides a wrapper type for resolving correct reference types when | ||||
| // working with encoding. | ||||
| type Field struct { | ||||
| 	reference Reference | ||||
| } | ||||
|  | ||||
| // AsField wraps a reference in a Field for encoding. | ||||
| func AsField(reference Reference) Field { | ||||
| 	return Field{reference} | ||||
| } | ||||
|  | ||||
| // Reference unwraps the reference type from the field to | ||||
| // return the Reference object. This object should be | ||||
| // of the appropriate type to further check for different | ||||
| // reference types. | ||||
| func (f Field) Reference() Reference { | ||||
| 	return f.reference | ||||
| } | ||||
|  | ||||
| // MarshalText serializes the field to byte text which | ||||
| // is the string of the reference. | ||||
| func (f Field) MarshalText() (p []byte, err error) { | ||||
| 	return []byte(f.reference.String()), nil | ||||
| } | ||||
|  | ||||
| // UnmarshalText parses text bytes by invoking the | ||||
| // reference parser to ensure the appropriately | ||||
| // typed reference object is wrapped by field. | ||||
| func (f *Field) UnmarshalText(p []byte) error { | ||||
| 	r, err := Parse(string(p)) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	f.reference = r | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| // Named is an object with a full name | ||||
| type Named interface { | ||||
| 	Reference | ||||
| 	Name() string | ||||
| } | ||||
|  | ||||
| // Tagged is an object which has a tag | ||||
| type Tagged interface { | ||||
| 	Reference | ||||
| 	Tag() string | ||||
| } | ||||
|  | ||||
| // NamedTagged is an object including a name and tag. | ||||
| type NamedTagged interface { | ||||
| 	Named | ||||
| 	Tag() string | ||||
| } | ||||
|  | ||||
| // Digested is an object which has a digest | ||||
| // in which it can be referenced by | ||||
| type Digested interface { | ||||
| 	Reference | ||||
| 	Digest() digest.Digest | ||||
| } | ||||
|  | ||||
| // Canonical reference is an object with a fully unique | ||||
| // name including a name with domain and digest | ||||
| type Canonical interface { | ||||
| 	Named | ||||
| 	Digest() digest.Digest | ||||
| } | ||||
|  | ||||
| // namedRepository is a reference to a repository with a name. | ||||
| // A namedRepository has both domain and path components. | ||||
| type namedRepository interface { | ||||
| 	Named | ||||
| 	Domain() string | ||||
| 	Path() string | ||||
| } | ||||
|  | ||||
| // Domain returns the domain part of the Named reference | ||||
| func Domain(named Named) string { | ||||
| 	if r, ok := named.(namedRepository); ok { | ||||
| 		return r.Domain() | ||||
| 	} | ||||
| 	domain, _ := splitDomain(named.Name()) | ||||
| 	return domain | ||||
| } | ||||
|  | ||||
| // Path returns the name without the domain part of the Named reference | ||||
| func Path(named Named) (name string) { | ||||
| 	if r, ok := named.(namedRepository); ok { | ||||
| 		return r.Path() | ||||
| 	} | ||||
| 	_, path := splitDomain(named.Name()) | ||||
| 	return path | ||||
| } | ||||
|  | ||||
| func splitDomain(name string) (string, string) { | ||||
| 	match := anchoredNameRegexp.FindStringSubmatch(name) | ||||
| 	if len(match) != 3 { | ||||
| 		return "", name | ||||
| 	} | ||||
| 	return match[1], match[2] | ||||
| } | ||||
|  | ||||
| // SplitHostname splits a named reference into a | ||||
| // hostname and name string. If no valid hostname is | ||||
| // found, the hostname is empty and the full value | ||||
| // is returned as name | ||||
| // DEPRECATED: Use Domain or Path | ||||
| func SplitHostname(named Named) (string, string) { | ||||
| 	if r, ok := named.(namedRepository); ok { | ||||
| 		return r.Domain(), r.Path() | ||||
| 	} | ||||
| 	return splitDomain(named.Name()) | ||||
| } | ||||
|  | ||||
| // Parse parses s and returns a syntactically valid Reference. | ||||
| // If an error was encountered it is returned, along with a nil Reference. | ||||
| // NOTE: Parse will not handle short digests. | ||||
| func Parse(s string) (Reference, error) { | ||||
| 	matches := ReferenceRegexp.FindStringSubmatch(s) | ||||
| 	if matches == nil { | ||||
| 		if s == "" { | ||||
| 			return nil, ErrNameEmpty | ||||
| 		} | ||||
| 		if ReferenceRegexp.FindStringSubmatch(strings.ToLower(s)) != nil { | ||||
| 			return nil, ErrNameContainsUppercase | ||||
| 		} | ||||
| 		return nil, ErrReferenceInvalidFormat | ||||
| 	} | ||||
|  | ||||
| 	if len(matches[1]) > NameTotalLengthMax { | ||||
| 		return nil, ErrNameTooLong | ||||
| 	} | ||||
|  | ||||
| 	var repo repository | ||||
|  | ||||
| 	nameMatch := anchoredNameRegexp.FindStringSubmatch(matches[1]) | ||||
| 	if nameMatch != nil && len(nameMatch) == 3 { | ||||
| 		repo.domain = nameMatch[1] | ||||
| 		repo.path = nameMatch[2] | ||||
| 	} else { | ||||
| 		repo.domain = "" | ||||
| 		repo.path = matches[1] | ||||
| 	} | ||||
|  | ||||
| 	ref := reference{ | ||||
| 		namedRepository: repo, | ||||
| 		tag:             matches[2], | ||||
| 	} | ||||
| 	if matches[3] != "" { | ||||
| 		var err error | ||||
| 		ref.digest, err = digest.Parse(matches[3]) | ||||
| 		if err != nil { | ||||
| 			return nil, err | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	r := getBestReferenceType(ref) | ||||
| 	if r == nil { | ||||
| 		return nil, ErrNameEmpty | ||||
| 	} | ||||
|  | ||||
| 	return r, nil | ||||
| } | ||||
|  | ||||
| // ParseNamed parses s and returns a syntactically valid reference implementing | ||||
| // the Named interface. The reference must have a name and be in the canonical | ||||
| // form, otherwise an error is returned. | ||||
| // If an error was encountered it is returned, along with a nil Reference. | ||||
| // NOTE: ParseNamed will not handle short digests. | ||||
| func ParseNamed(s string) (Named, error) { | ||||
| 	named, err := ParseNormalizedNamed(s) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	if named.String() != s { | ||||
| 		return nil, ErrNameNotCanonical | ||||
| 	} | ||||
| 	return named, nil | ||||
| } | ||||
|  | ||||
| // WithName returns a named object representing the given string. If the input | ||||
| // is invalid ErrReferenceInvalidFormat will be returned. | ||||
| func WithName(name string) (Named, error) { | ||||
| 	if len(name) > NameTotalLengthMax { | ||||
| 		return nil, ErrNameTooLong | ||||
| 	} | ||||
|  | ||||
| 	match := anchoredNameRegexp.FindStringSubmatch(name) | ||||
| 	if match == nil || len(match) != 3 { | ||||
| 		return nil, ErrReferenceInvalidFormat | ||||
| 	} | ||||
| 	return repository{ | ||||
| 		domain: match[1], | ||||
| 		path:   match[2], | ||||
| 	}, nil | ||||
| } | ||||
|  | ||||
| // WithTag combines the name from "name" and the tag from "tag" to form a | ||||
| // reference incorporating both the name and the tag. | ||||
| func WithTag(name Named, tag string) (NamedTagged, error) { | ||||
| 	if !anchoredTagRegexp.MatchString(tag) { | ||||
| 		return nil, ErrTagInvalidFormat | ||||
| 	} | ||||
| 	var repo repository | ||||
| 	if r, ok := name.(namedRepository); ok { | ||||
| 		repo.domain = r.Domain() | ||||
| 		repo.path = r.Path() | ||||
| 	} else { | ||||
| 		repo.path = name.Name() | ||||
| 	} | ||||
| 	if canonical, ok := name.(Canonical); ok { | ||||
| 		return reference{ | ||||
| 			namedRepository: repo, | ||||
| 			tag:             tag, | ||||
| 			digest:          canonical.Digest(), | ||||
| 		}, nil | ||||
| 	} | ||||
| 	return taggedReference{ | ||||
| 		namedRepository: repo, | ||||
| 		tag:             tag, | ||||
| 	}, nil | ||||
| } | ||||
|  | ||||
| // WithDigest combines the name from "name" and the digest from "digest" to form | ||||
| // a reference incorporating both the name and the digest. | ||||
| func WithDigest(name Named, digest digest.Digest) (Canonical, error) { | ||||
| 	if !anchoredDigestRegexp.MatchString(digest.String()) { | ||||
| 		return nil, ErrDigestInvalidFormat | ||||
| 	} | ||||
| 	var repo repository | ||||
| 	if r, ok := name.(namedRepository); ok { | ||||
| 		repo.domain = r.Domain() | ||||
| 		repo.path = r.Path() | ||||
| 	} else { | ||||
| 		repo.path = name.Name() | ||||
| 	} | ||||
| 	if tagged, ok := name.(Tagged); ok { | ||||
| 		return reference{ | ||||
| 			namedRepository: repo, | ||||
| 			tag:             tagged.Tag(), | ||||
| 			digest:          digest, | ||||
| 		}, nil | ||||
| 	} | ||||
| 	return canonicalReference{ | ||||
| 		namedRepository: repo, | ||||
| 		digest:          digest, | ||||
| 	}, nil | ||||
| } | ||||
|  | ||||
| // TrimNamed removes any tag or digest from the named reference. | ||||
| func TrimNamed(ref Named) Named { | ||||
| 	domain, path := SplitHostname(ref) | ||||
| 	return repository{ | ||||
| 		domain: domain, | ||||
| 		path:   path, | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func getBestReferenceType(ref reference) Reference { | ||||
| 	if ref.Name() == "" { | ||||
| 		// Allow digest only references | ||||
| 		if ref.digest != "" { | ||||
| 			return digestReference(ref.digest) | ||||
| 		} | ||||
| 		return nil | ||||
| 	} | ||||
| 	if ref.tag == "" { | ||||
| 		if ref.digest != "" { | ||||
| 			return canonicalReference{ | ||||
| 				namedRepository: ref.namedRepository, | ||||
| 				digest:          ref.digest, | ||||
| 			} | ||||
| 		} | ||||
| 		return ref.namedRepository | ||||
| 	} | ||||
| 	if ref.digest == "" { | ||||
| 		return taggedReference{ | ||||
| 			namedRepository: ref.namedRepository, | ||||
| 			tag:             ref.tag, | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	return ref | ||||
| } | ||||
|  | ||||
| type reference struct { | ||||
| 	namedRepository | ||||
| 	tag    string | ||||
| 	digest digest.Digest | ||||
| } | ||||
|  | ||||
| func (r reference) String() string { | ||||
| 	return r.Name() + ":" + r.tag + "@" + r.digest.String() | ||||
| } | ||||
|  | ||||
| func (r reference) Tag() string { | ||||
| 	return r.tag | ||||
| } | ||||
|  | ||||
| func (r reference) Digest() digest.Digest { | ||||
| 	return r.digest | ||||
| } | ||||
|  | ||||
| type repository struct { | ||||
| 	domain string | ||||
| 	path   string | ||||
| } | ||||
|  | ||||
| func (r repository) String() string { | ||||
| 	return r.Name() | ||||
| } | ||||
|  | ||||
| func (r repository) Name() string { | ||||
| 	if r.domain == "" { | ||||
| 		return r.path | ||||
| 	} | ||||
| 	return r.domain + "/" + r.path | ||||
| } | ||||
|  | ||||
| func (r repository) Domain() string { | ||||
| 	return r.domain | ||||
| } | ||||
|  | ||||
| func (r repository) Path() string { | ||||
| 	return r.path | ||||
| } | ||||
|  | ||||
| type digestReference digest.Digest | ||||
|  | ||||
| func (d digestReference) String() string { | ||||
| 	return digest.Digest(d).String() | ||||
| } | ||||
|  | ||||
| func (d digestReference) Digest() digest.Digest { | ||||
| 	return digest.Digest(d) | ||||
| } | ||||
|  | ||||
| type taggedReference struct { | ||||
| 	namedRepository | ||||
| 	tag string | ||||
| } | ||||
|  | ||||
| func (t taggedReference) String() string { | ||||
| 	return t.Name() + ":" + t.tag | ||||
| } | ||||
|  | ||||
| func (t taggedReference) Tag() string { | ||||
| 	return t.tag | ||||
| } | ||||
|  | ||||
| type canonicalReference struct { | ||||
| 	namedRepository | ||||
| 	digest digest.Digest | ||||
| } | ||||
|  | ||||
| func (c canonicalReference) String() string { | ||||
| 	return c.Name() + "@" + c.digest.String() | ||||
| } | ||||
|  | ||||
| func (c canonicalReference) Digest() digest.Digest { | ||||
| 	return c.digest | ||||
| } | ||||
							
								
								
									
										143
									
								
								vendor/github.com/docker/distribution/reference/regexp.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										143
									
								
								vendor/github.com/docker/distribution/reference/regexp.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,143 @@ | ||||
| package reference | ||||
|  | ||||
| import "regexp" | ||||
|  | ||||
| var ( | ||||
| 	// alphaNumericRegexp defines the alpha numeric atom, typically a | ||||
| 	// component of names. This only allows lower case characters and digits. | ||||
| 	alphaNumericRegexp = match(`[a-z0-9]+`) | ||||
|  | ||||
| 	// separatorRegexp defines the separators allowed to be embedded in name | ||||
| 	// components. This allow one period, one or two underscore and multiple | ||||
| 	// dashes. | ||||
| 	separatorRegexp = match(`(?:[._]|__|[-]*)`) | ||||
|  | ||||
| 	// nameComponentRegexp restricts registry path component names to start | ||||
| 	// with at least one letter or number, with following parts able to be | ||||
| 	// separated by one period, one or two underscore and multiple dashes. | ||||
| 	nameComponentRegexp = expression( | ||||
| 		alphaNumericRegexp, | ||||
| 		optional(repeated(separatorRegexp, alphaNumericRegexp))) | ||||
|  | ||||
| 	// domainComponentRegexp restricts the registry domain component of a | ||||
| 	// repository name to start with a component as defined by DomainRegexp | ||||
| 	// and followed by an optional port. | ||||
| 	domainComponentRegexp = match(`(?:[a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9-]*[a-zA-Z0-9])`) | ||||
|  | ||||
| 	// DomainRegexp defines the structure of potential domain components | ||||
| 	// that may be part of image names. This is purposely a subset of what is | ||||
| 	// allowed by DNS to ensure backwards compatibility with Docker image | ||||
| 	// names. | ||||
| 	DomainRegexp = expression( | ||||
| 		domainComponentRegexp, | ||||
| 		optional(repeated(literal(`.`), domainComponentRegexp)), | ||||
| 		optional(literal(`:`), match(`[0-9]+`))) | ||||
|  | ||||
| 	// TagRegexp matches valid tag names. From docker/docker:graph/tags.go. | ||||
| 	TagRegexp = match(`[\w][\w.-]{0,127}`) | ||||
|  | ||||
| 	// anchoredTagRegexp matches valid tag names, anchored at the start and | ||||
| 	// end of the matched string. | ||||
| 	anchoredTagRegexp = anchored(TagRegexp) | ||||
|  | ||||
| 	// DigestRegexp matches valid digests. | ||||
| 	DigestRegexp = match(`[A-Za-z][A-Za-z0-9]*(?:[-_+.][A-Za-z][A-Za-z0-9]*)*[:][[:xdigit:]]{32,}`) | ||||
|  | ||||
| 	// anchoredDigestRegexp matches valid digests, anchored at the start and | ||||
| 	// end of the matched string. | ||||
| 	anchoredDigestRegexp = anchored(DigestRegexp) | ||||
|  | ||||
| 	// NameRegexp is the format for the name component of references. The | ||||
| 	// regexp has capturing groups for the domain and name part omitting | ||||
| 	// the separating forward slash from either. | ||||
| 	NameRegexp = expression( | ||||
| 		optional(DomainRegexp, literal(`/`)), | ||||
| 		nameComponentRegexp, | ||||
| 		optional(repeated(literal(`/`), nameComponentRegexp))) | ||||
|  | ||||
| 	// anchoredNameRegexp is used to parse a name value, capturing the | ||||
| 	// domain and trailing components. | ||||
| 	anchoredNameRegexp = anchored( | ||||
| 		optional(capture(DomainRegexp), literal(`/`)), | ||||
| 		capture(nameComponentRegexp, | ||||
| 			optional(repeated(literal(`/`), nameComponentRegexp)))) | ||||
|  | ||||
| 	// ReferenceRegexp is the full supported format of a reference. The regexp | ||||
| 	// is anchored and has capturing groups for name, tag, and digest | ||||
| 	// components. | ||||
| 	ReferenceRegexp = anchored(capture(NameRegexp), | ||||
| 		optional(literal(":"), capture(TagRegexp)), | ||||
| 		optional(literal("@"), capture(DigestRegexp))) | ||||
|  | ||||
| 	// IdentifierRegexp is the format for string identifier used as a | ||||
| 	// content addressable identifier using sha256. These identifiers | ||||
| 	// are like digests without the algorithm, since sha256 is used. | ||||
| 	IdentifierRegexp = match(`([a-f0-9]{64})`) | ||||
|  | ||||
| 	// ShortIdentifierRegexp is the format used to represent a prefix | ||||
| 	// of an identifier. A prefix may be used to match a sha256 identifier | ||||
| 	// within a list of trusted identifiers. | ||||
| 	ShortIdentifierRegexp = match(`([a-f0-9]{6,64})`) | ||||
|  | ||||
| 	// anchoredIdentifierRegexp is used to check or match an | ||||
| 	// identifier value, anchored at start and end of string. | ||||
| 	anchoredIdentifierRegexp = anchored(IdentifierRegexp) | ||||
|  | ||||
| 	// anchoredShortIdentifierRegexp is used to check if a value | ||||
| 	// is a possible identifier prefix, anchored at start and end | ||||
| 	// of string. | ||||
| 	anchoredShortIdentifierRegexp = anchored(ShortIdentifierRegexp) | ||||
| ) | ||||
|  | ||||
| // match compiles the string to a regular expression. | ||||
| var match = regexp.MustCompile | ||||
|  | ||||
| // literal compiles s into a literal regular expression, escaping any regexp | ||||
| // reserved characters. | ||||
| func literal(s string) *regexp.Regexp { | ||||
| 	re := match(regexp.QuoteMeta(s)) | ||||
|  | ||||
| 	if _, complete := re.LiteralPrefix(); !complete { | ||||
| 		panic("must be a literal") | ||||
| 	} | ||||
|  | ||||
| 	return re | ||||
| } | ||||
|  | ||||
| // expression defines a full expression, where each regular expression must | ||||
| // follow the previous. | ||||
| func expression(res ...*regexp.Regexp) *regexp.Regexp { | ||||
| 	var s string | ||||
| 	for _, re := range res { | ||||
| 		s += re.String() | ||||
| 	} | ||||
|  | ||||
| 	return match(s) | ||||
| } | ||||
|  | ||||
| // optional wraps the expression in a non-capturing group and makes the | ||||
| // production optional. | ||||
| func optional(res ...*regexp.Regexp) *regexp.Regexp { | ||||
| 	return match(group(expression(res...)).String() + `?`) | ||||
| } | ||||
|  | ||||
| // repeated wraps the regexp in a non-capturing group to get one or more | ||||
| // matches. | ||||
| func repeated(res ...*regexp.Regexp) *regexp.Regexp { | ||||
| 	return match(group(expression(res...)).String() + `+`) | ||||
| } | ||||
|  | ||||
| // group wraps the regexp in a non-capturing group. | ||||
| func group(res ...*regexp.Regexp) *regexp.Regexp { | ||||
| 	return match(`(?:` + expression(res...).String() + `)`) | ||||
| } | ||||
|  | ||||
| // capture wraps the expression in a capturing group. | ||||
| func capture(res ...*regexp.Regexp) *regexp.Regexp { | ||||
| 	return match(`(` + expression(res...).String() + `)`) | ||||
| } | ||||
|  | ||||
| // anchored anchors the regular expression by adding start and end delimiters. | ||||
| func anchored(res ...*regexp.Regexp) *regexp.Regexp { | ||||
| 	return match(`^` + expression(res...).String() + `$`) | ||||
| } | ||||
		Reference in New Issue
	
	Block a user
	 Random-Liu
					Random-Liu