splitDockerDomain(s)
splitDockerDomain(s)
在第90行,通过字段其实大概就能明白它的意思。字段切分。具体怎么实现的呢?
strings.IndexRune(name, '/')
方法,可以参考go strings方法,IndexRune
返回字符/
在字符串s中第一次出现的位置,如果找不到,则返回-1。domain
就等于docker.io
,在代码中已初始化好,name
等于remainder
如果镜像中有‘/’呢,例如:kainlite/kube-image-bouncer:latest,domain取kainlite,remainder取kube-image-bouncer:latest
假如domain等于index.docker.io,就重定义为docker.io
假如domain等于docker.io,并且再次确认remainder不包含‘/’,就将remainder再次重新定义 officialRepoName + "/" + remainder
splitDockerDomain(s)函数分析完毕。继续ParseNormalizedNamed(s string) (Named, error)的分析:
定义个remoteName
tagSep := strings.IndexRune(remainder, ‘:’),即确认:的位置,如果>-1,即如果存在,则
tagSep等于镜像的标签,否则,就等于镜像的名字
strings.ToLower将s中的所有字符修改为其小写格式,如果remotename存在大写的话则不符合仓库名字规范。
parse
Parse(domain + "/" + remainder)
是什么意思呢?
又是好大的一个函数。
那我们慢慢来分析吧
referenceRegexp.FindStringSubmatch(s)
referenceRegexp.FindStringSubmatch(s)
是啥意思?翻译过来就是ReferenceRegexp
是完全支持的引用格式。regexp被锚定,并具有名称、标记和摘要组件的捕获组。描述看不懂。还是具体代码逻辑吧
anchored
大体明白了,就是通过添加开始和结束分隔符锚定正则表达式并作为返回值。
正则表达式字符的字符串转换字符串形式。
capture
函数与anchored
大同小异,只是将含有正则表达式字符的字符串转换数组形式并作为返回值。
optional
似乎是对字符串可有可无的一种设定,并且optional
字面意思是可选的。
literal
函数是什么意思?翻译是将文本编译为文本正则表达式,转义任何regexp保留字符。字面非常容易理解。
QuoteMeta
返回一个字符串,该字符串转义参数文本中的所有正则表达式元字符;返回的字符串是与文本匹配的正则表达式。- var match = regexp.MustCompile
- re := regexp.MustCompile(正则字符串)
- re就是单纯的正则表达式了。
LiteralPrefix
返回一个字面值字符串,该字符串必须以正则表达式re的任何匹配项开始。如果字面值字符串包含整个正则表达式,则返回布尔值true。
后面的函数方法其实大同小异,这里不一一拆解。
回到函数ReferenceRegexp.FindStringSubmatch(s),FindStringSubmatch(s)是regexp的方法FindStringSubmatch返回一个字符串切片,其中包含正则表达式在s中最左匹配的文本,以及它的子表达式的匹配(如果有的话),如包注释中的’Submatch’描述中定义的那样。返回值为nil表示没有匹配。
ReferenceRegexp函数其实设定了一个正则表达式,使字符串s能够通过我们希望的分割的方式,形成一个切片。它的关键之处是获取s的正则表达式,融入到设定的规则之中。
假如matches为nil,报错
假如matches[1]长度>255,也会报错
nameMatch := anchoredNameRegexp.FindStringSubmatch(matches[1])
这段代码其实是对domain
的再度切片,如果它较多的递级目录,那我们就要对目录与仓库名称做出区分。
这里定义了一个关于镜像仓库名与标签的结构体,这是非常重要的一步。
ref := reference{ namedRepository: repo, tag: matches[2], }
matches[3]
是什么? matches[2]
是镜像的版本标签,在这之后,又是通过@分割当然是一个哈希字符串的内容,是可有可无的。所以有下面的一个判断。
if matches[3] != "" { var err error ref.digest, err = digest.Parse(matches[3]) if err != nil { return nil, err } }
digest包正是处理哈希字符串的。
对包含镜像的仓库、标签、哈希值的ref结构体再次检查判断,有什么输出什么。并重构还有该特性的结构体。一共有三类结构体。如下:
到这里,关于parse
的函数分析已经结束了,这一路下来学到了不少方法,不能说全盘了解,但调用的机理已经摸透。
named, isNamed := ref.(Named)
是个非常有趣的值得摸索记住的。我不懂,结构体跟点跟括号。后续研究。,但根据下面的代码逻辑推理,可能:
r.Name() + ":" + r.tag + "@" + r.digest.String()
Named
是管道,ref是结构体reference
结构体的定义,那么自然会承接Reference
管道中的refence
的String()
剩下要做的就是看看这个parse函数返回值被如何用了,自然可以推理出来,如下
strings.HasSuffix
判断字符串s是否以某个字符串结尾,
TagNameOnly
函数很好理解,就是判断镜像有没有标签,如果没有标签就添加默认"latest
"。
那我们来看看他怎么实现的吧。
IsNameOnly
函数在helpers.go
来到这里我们又遇到了ref.(NamedTagged),看到这里,也许有一丝明白过来,这个对ref的结构体属性的判断,根据结构体中的属性是否包含值,通过接口组合成一个判断类型。之前,named, isNamed := ref.(Named)代表返回一个完整的镜像+标签的内容,并判断是否正常返回,这里代表是否只返回标签,也就是只判断是否有标签。下面的ref.(Canonical)则是判断标签是否有哈希值。
最后,如果没有任何标签,即为true,继续执行namedTagged, err := WithTag(ref, defaultTag)
withtag
函数传递两个函数,获取的镜像的信息,和latest标签。
这个对已经获取的Named的对象重新进行定义,目的是为了把默认没有标签的镜像打上latest标签。
当withtag函数执行结束,TagNameOnly(named)也就返回新的镜像结构体,然后通过String()方法,字符串化,Strings.HasSuffix方法判断这个字符串是否以:latest结尾。根据正确与否返回true或false。
IsUsingLatestTag函数的逻辑终于也就结束了。
回到我们的image_policy代码中来。
下面就是根据判断是否有latest做出终端日志输出判断,并且打个是否allow的标签。
我们完成了哪些部分的代码分析呢?真的是九牛一毛。
4. echo包
下面只需要分析明白如下内容。
echo web框架是go语言开发的一种高性能,可扩展,轻量级的web框架。
echo框架真的非常简单,几行代码就可以启动一个高性能的http服务端。
echo.Context表示当前 HTTP 请求的上下文。它保存请求和响应引用、路径、路径参数、数据、注册处理程序和 API 以读取请求和写入响应。由于 Context 是一个接口,因此很容易使用自定义 API 对其进行扩展。
handlerFunc定义http请求,匿名函数像变量一样返回给echo.HandlerFunc
c.bind(&imageReview)意思是通过将请求参数绑定到一个名叫imageReview的struct对象的方式获取数据。这种方式获取请求参数支持json、xml、k/v键值对等多种方式。
该函数最终返回一个json格式的pod拉去一个镜像的属性信息,并伴随http请求状态。
分析到这里,我们完成了对Image_Policy.go的步步分析,也深刻的体会到读源码的重要性。
5. 总结
我对go有了以下感悟:
go的logo是一只土拨鼠,土拨鼠的习性是挖地洞,因此,我们在学习go的时候要有土拨鼠的挖洞精神,在每个包、每个函数、每个方法、每个接口、每个结构体找到它最真实的面孔。
echo web框架的使用,请参考这篇文章
匿名函数作为返回值的使用
匿名函数由一个不带函数名的函数声明和函数体组成。匿名函数的优越性在于可以直接使用函数内的变量,不必申明。这是它最大的特点。这里将一个函数当做一个变量一样的操作作为函数的返回值输出给echo.HandlerFunc
依赖包的结构体具体特性进行变量声明,方便简化代码的逻辑。例如:var imageReview v1alpha1.ImageReview
谁在调用image_policy.go ,是main.go,在下一篇接文章,我们慢慢分析它的逻辑是什么。