什么是Visitor

Visitor定义在/pkg/kubectl/resource/visitor.go中。我们先来看visitor的定义:

1
2
3
4
5
type Visitor interface {
Visit(VisitorFunc) error
}
type VisitorFunc func(*Info, error) error

可以看出,只要实现了Visit(VisitorFunc) error方法的结构体都可以称为Visitor,相当简洁。

其中VisitorFunc的参数为*Info及error,所以可以推断,VisitorFunc()可以对Info及Error进行处理。

Visitor最大的特性就是每个Visitor实现了一个特性,然后可以嵌套使用。Visitor可以分为两类,第一类是生成info,第二类是处理info。我们现在先记着info是用来存储REST请求的返回结果的。

产生info的visitor有:FileVisitor, StreamVisitor, URLVisitor, Selector。

处理info的visitor有:VisitorList, EagerVisitorList, DecoratedVisitor, ContinueOnErrorVisitor, FlattenListVisitor, FlattenListVisitor等。此处需要说明下,有些Visitor只处理了Error,但我们仍把它归为处理info的Visitor。

一般来说,如果info已经生成,那么Visitor嵌套中的Visitor只要info处理Visitor即可;如果没有info,则最里面的Visitor要在fn()调用之前生成info,以供其他Visitor处理。

Visitor嵌套原理

先来看一个Visitor的定义:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
//***修饰作用***//
type DecoratedVisitor struct {
visitor Visitor
decorators []VisitorFunc
}
// Visit implements Visitor
func (v DecoratedVisitor) Visit(fn VisitorFunc) error {
return v.visitor.Visit(func(info *Info, err error) error {
if err != nil {
return err
}
//***使用decorator对info进行处理***//
for i := range v.decorators {
if err := v.decorators[i](info, nil); err != nil {
return err
}
}
return fn(info, nil)
})
}

一般来说,一个Visitor(父Visitor)结构体中包含另一个Visitor(子Visitor)。父Visitor中的Visit()方法会调用子Visitor的Visit()方法,并传入一个匿名的visitorFunc,这个匿名的visitorFunc在子Visitor看来就是fn。

再来看下Visitor如何嵌套使用,下面代码摘自/pkg/kubectl/resource/builder.go中的Do()函数:

1
2
3
4
5
if b.continueOnError {
r.visitor = ContinueOnErrorVisitor{r.visitor}
}
return r
}

嵌套也很简单,就是用一个Visitor去封装另一个Visitor。

所以可以仿照代码写个Demo:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
package main
import (
"fmt"
)
type Visitor interface {
Visit(VisitorFunc) error
}
type VisitorFunc func() error
type Visitor1 struct {
}
func (l Visitor1) Visit(fn VisitorFunc) error {
fmt.Println("In Visitor1 before fn")
fn()
fmt.Println("In Visitor1 after fn")
return nil
}
type Visitor2 struct {
visitor Visitor
}
func (l Visitor2) Visit(fn VisitorFunc) error {
return l.visitor.Visit(func() error {
fmt.Println("In Visitor2 before fn")
fn()
fmt.Println("In Visitor2 after fn")
return nil
})
}
type Visitor3 struct {
visitor Visitor
}
func (l Visitor3) Visit(fn VisitorFunc) error {
return l.visitor.Visit(func() error {
fmt.Println("In Visitor3 before fn")
fn()
fmt.Println("In Visitor3 after fn")
return nil
})
}
func main() {
var visitor Visitor = new(Visitor1)
visitor = Visitor2{visitor}
visitor = Visitor3{visitor}
visitor.Visit(func() error {
fmt.Println("In visitFunc")
return nil
})
}

Demo中把Visitor定义的visitFunc代码分为”before fn”和”after fn”。来看执行结果:

1
2
3
4
5
6
7
In Visitor1 before fn
In Visitor2 before fn
In Visitor3 before fn
In visitFunc
In Visitor3 after fn
In Visitor2 after fn
In Visitor1 after fn

所以可以看出,virsitor3{visitor2{visitor1}}顺序下,最先执行的是Visitor1中visitFunc的fn()之前的代码,然后是Visitor2和VIsitor3中fn()之前的代码。接着执行visitor3中的visitFunc。最后又按visitor3,visitor2,visitor1的顺序执行fn()后的代码。通俗地讲,外边的Visitor的visitorFunc会嵌入到里边Visitor的fn处。

在Kubectl中,还有类VisitorList的Visitor。我们再来看个更复杂的Demo:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
package main
import (
"fmt"
)
type Visitor interface {
Visit(VisitorFunc) error
}
type VisitorFunc func() error
type VisitorList []Visitor
func (l VisitorList) Visit(fn VisitorFunc) error {
for i := range l {
if err := l[i].Visit(func() error {
fmt.Println("In VisitorList before fn")
fn()
fmt.Println("In VisitorList after fn")
return nil
}); err != nil {
return err
}
}
return nil
}
type Visitor1 struct {
}
func (l Visitor1) Visit(fn VisitorFunc) error {
fmt.Println("In Visitor1 before fn")
fn()
fmt.Println("In Visitor1 after fn")
return nil
}
type Visitor2 struct {
visitor Visitor
}
func (l Visitor2) Visit(fn VisitorFunc) error {
fmt.Println("In Visitor2 before Visit")
l.visitor.Visit(func() error {
fmt.Println("In Visitor2 before fn")
fn()
fmt.Println("In Visitor2 after fn")
return nil
})
fmt.Println("In Visitor2 before Visit")
return nil
}
type Visitor3 struct {
visitor Visitor
}
func (l Visitor3) Visit(fn VisitorFunc) error {
fmt.Println("In Visitor3 before Visit")
l.visitor.Visit(func() error {
fmt.Println("In Visitor3 before fn")
fn()
fmt.Println("In Visitor3 after fn")
return nil
})
fmt.Println("In Visitor3 after Visit")
return nil
}
func main() {
var visitor Visitor = new(Visitor1)
var visitors []Visitor
visitors = append(visitors, visitor)
visitors = append(visitors, visitor)
visitor = Visitor2{VisitorList(visitors)}
visitor = Visitor3{visitor}
visitor.Visit(func() error {
fmt.Println("In visitFunc")
return nil
})
}

执行结果为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
In Visitor3 before Visit
In Visitor2 before Visit
In Visitor1 before fn
In VisitorList before fn
In Visitor2 before fn
In Visitor3 before fn
In visitFunc
In Visitor3 after fn
In Visitor2 after fn
In VisitorList after fn
In Visitor1 after fn
In Visitor1 before fn
In VisitorList before fn
In Visitor2 before fn
In Visitor3 before fn
In visitFunc
In Visitor3 after fn
In Visitor2 after fn
In VisitorList after fn
In Visitor1 after fn
In Visitor2 before Visit
In Visitor3 after Visit

添加了visitorList后,由于visitorList包含两个visitor1,相当于visitorList外边的Visitor被嵌入到Visitor1中两次,所以,整个打印过程执行了两次,且visitorList中定义的函数内容会在visitor1和visitor2之间执行。

而visitFunc之前的代码的是先执行最外面的Visitor,再执行里面的Visitor;visitFunc之后的代码是先执行最里面的Visitor,再执行外面的Visitor。

VisitorList

VisitorList包含多个visitor,在迭代包含的visitor时,一遇到错误就返回。

1
2
3
4
5
6
7
8
9
10
11
type VisitorList []Visitor
// Visit implements Visitor
func (l VisitorList) Visit(fn VisitorFunc) error {
for i := range l {
if err := l[i].Visit(fn); err != nil {
return err
}
}
return nil
}

EagerVisitorList

EagerVisitorList在迭代包含的visitor时,遇到错误会断续迭代,最后一起返回所有错误。

此处可以体会下匿名函数的魅力,所以匿名函数中的err都会被收集到EagerVisitorList的Visit()方法中的errs中。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
type EagerVisitorList []Visitor
// Visit implements Visitor, and gathers errors that occur during processing until
// all sub visitors have been visited.
func (l EagerVisitorList) Visit(fn VisitorFunc) error {
errs := []error(nil)
for i := range l {
if err := l[i].Visit(func(info *Info, err error) error {
if err != nil {
errs = append(errs, err)
return nil
}
if err := fn(info, nil); err != nil {
errs = append(errs, err)
}
return nil
}); err != nil {
errs = append(errs, err)
}
}
return utilerrors.NewAggregate(errs)
}

URLVisitor

URLVisitor获取指定URL的内容,并对内容进行格式检查,然后把内容转换为info。URL封装了StreamVisitor。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
type URLVisitor struct {
URL *url.URL
*StreamVisitor
HttpAttemptCount int
}
func (v *URLVisitor) Visit(fn VisitorFunc) error {
body, err := readHttpWithRetries(httpgetImpl, time.Second, v.URL.String(), v.HttpAttemptCount)
if err != nil {
return err
}
defer body.Close()
v.StreamVisitor.Reader = body
return v.StreamVisitor.Visit(fn)
}

DecoratedVisitor

DecoratedVisitor会调用decorators对info和err进行处理。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
type DecoratedVisitor struct {
visitor Visitor
decorators []VisitorFunc
}
// NewDecoratedVisitor will create a visitor that invokes the provided visitor functions before
// the user supplied visitor function is invoked, giving them the opportunity to mutate the Info
// object or terminate early with an error.
func NewDecoratedVisitor(v Visitor, fn ...VisitorFunc) Visitor {
if len(fn) == 0 {
return v
}
return DecoratedVisitor{v, fn}
}
// Visit implements Visitor
func (v DecoratedVisitor) Visit(fn VisitorFunc) error {
return v.visitor.Visit(func(info *Info, err error) error {
if err != nil {
return err
}
//***使用decorator对info进行处理***//
for i := range v.decorators {
if err := v.decorators[i](info, nil); err != nil {
return err
}
}
return fn(info, nil)
})
}

ContinueOnErrorVisitor

ContinueOnErrorVisitor会收集子Visitor产生的错误,并返回。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
type ContinueOnErrorVisitor struct {
Visitor
}
func (v ContinueOnErrorVisitor) Visit(fn VisitorFunc) error {
errs := []error{}
err := v.Visitor.Visit(func(info *Info, err error) error {
if err != nil {
errs = append(errs, err)
return nil
}
//***此处传入fn的错误是nil***//
if err := fn(info, nil); err != nil {
errs = append(errs, err)
}
return nil
})
if err != nil {
errs = append(errs, err)
}
if len(errs) == 1 {
return errs[0]
}
return utilerrors.NewAggregate(errs)
}

FlattenListVisitor

FlattenListVisitor可以对列表中的每个元素进行处理。如果有错误发生,则返回。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
type FlattenListVisitor struct {
Visitor
*Mapper
}
// NewFlattenListVisitor creates a visitor that will expand list style runtime.Objects
// into individual items and then visit them individually.
func NewFlattenListVisitor(v Visitor, mapper *Mapper) Visitor {
return FlattenListVisitor{v, mapper}
}
func (v FlattenListVisitor) Visit(fn VisitorFunc) error {
return v.Visitor.Visit(func(info *Info, err error) error {
if err != nil {
return err
}
if info.Object == nil {
return fn(info, nil)
}
//***提取列表***//
items, err := meta.ExtractList(info.Object)
if err != nil {
return fn(info, nil)
}
if errs := runtime.DecodeList(items, struct {
runtime.ObjectTyper
runtime.Decoder
}{v.Mapper, v.Mapper.Decoder}); len(errs) > 0 {
return utilerrors.NewAggregate(errs)
}
// If we have a GroupVersionKind on the list, prioritize that when asking for info on the objects contained in the list
var preferredGVKs []unversioned.GroupVersionKind
if info.Mapping != nil && !info.Mapping.GroupVersionKind.Empty() {
preferredGVKs = append(preferredGVKs, info.Mapping.GroupVersionKind)
}
//***对列表中每一个元素进行下一步处理***//
for i := range items {
item, err := v.InfoForObject(items[i], preferredGVKs)
if err != nil {
return err
}
if len(info.ResourceVersion) != 0 {
item.ResourceVersion = info.ResourceVersion
}
if err := fn(item, nil); err != nil {
return err
}
}
return nil
})
}

FileVisitor

FileVisitor用来访问文件,并生成Info。FileVisitor是对StreamVisitor的封装。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
type FileVisitor struct {
Path string
*StreamVisitor
}
// Visit in a FileVisitor is just taking care of opening/closing files
func (v *FileVisitor) Visit(fn VisitorFunc) error {
var f *os.File
if v.Path == constSTDINstr {
f = os.Stdin
} else {
var err error
if f, err = os.Open(v.Path); err != nil {
return err
}
}
defer f.Close()
v.StreamVisitor.Reader = f
return v.StreamVisitor.Visit(fn)
}

StreamVisitor

StreamVisitor 从io.reader中获取数据流,然后转换成json格式,最后进行schema检查,封装成info对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
type StreamVisitor struct {
io.Reader
*Mapper
Source string
Schema validation.Schema
}
// NewStreamVisitor is a helper function that is useful when we want to change the fields of the struct but keep calls the same.
func NewStreamVisitor(r io.Reader, mapper *Mapper, source string, schema validation.Schema) *StreamVisitor {
return &StreamVisitor{
Reader: r,
Mapper: mapper,
Source: source,
Schema: schema,
}
}
// Visit implements Visitor over a stream. StreamVisitor is able to distinct multiple resources in one stream.
func (v *StreamVisitor) Visit(fn VisitorFunc) error {
d := yaml.NewYAMLOrJSONDecoder(v.Reader, 4096)
for {
ext := runtime.RawExtension{}
//***StreamVisitor使用NewYAMLOrJSONDecoder(定义在/pkg/util/yaml/decoder.go中)对YAML进行解析***//
if err := d.Decode(&ext); err != nil {
if err == io.EOF {
return nil
}
return err
}
// TODO: This needs to be able to handle object in other encodings and schemas.
ext.Raw = bytes.TrimSpace(ext.Raw)
if len(ext.Raw) == 0 || bytes.Equal(ext.Raw, []byte("null")) {
continue
}
if err := ValidateSchema(ext.Raw, v.Schema); err != nil {
return fmt.Errorf("error validating %q: %v", v.Source, err)
}
//***调用InfoForData()(在mapper.go中)生成info信息***//
//***在InfoForData中会把json对象转换成对应的struct类型***//
info, err := v.InfoForData(ext.Raw, v.Source)
if err != nil {
if fnErr := fn(info, err); fnErr != nil {
return fnErr
}
continue
}
if err := fn(info, nil); err != nil {
return err
}
}
}

FilteredVisitor

FilteredVisitor可以检查info是否满足某些条件。如果满足条件,则往下执行,否则返回err。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
type FilteredVisitor struct {
visitor Visitor
filters []FilterFunc
}
func NewFilteredVisitor(v Visitor, fn ...FilterFunc) Visitor {
if len(fn) == 0 {
return v
}
return FilteredVisitor{v, fn}
}
func (v FilteredVisitor) Visit(fn VisitorFunc) error {
return v.visitor.Visit(func(info *Info, err error) error {
if err != nil {
return err
}
for _, filter := range v.filters {
ok, err := filter(info, nil)
if err != nil {
return err
}
if !ok {
return nil
}
}
return fn(info, nil)
})
}