helm源码分析-list命令


| 阅读 |,阅读约 4 分钟
| 复制链接:

Overview

helm源码分析-list命令

  • helm list 主要用于查看已经安装的release列表
  • 源码位置:cmd/helm/list.go

实现逻辑

helm list 的代码比较简单,没有复杂的处理逻辑,核心逻辑都委托给action模块实现了。核心其实就包括两步:

  • 构造action的客户端

  • 调用action的Run方法得到Release列表

    1
    

client := action.NewList(cfg) client.Run()

 1
 2- action实现列表查询的逻辑与底层的存储驱动有关
 3
 4- 默认的secret存储驱动实现:`查询所有含有owner=helm这个label的secret`
 5- configmap存储驱动实现:`查询所有含有owner=helm这个label的configmap`
 6- memory存储驱动实现:`查询Memory对象中存储的数据`
 7- sql存储驱动实现:`查询releases_v1表中的记录`
 8
 9## 源码分析
10
11### 代码入口
12
13```go
14func newListCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
15// 内部调用调用pkg/action/list.go模块
16// 先构造action的客户端
17  client := action.NewList(cfg)
18  var outfmt output.Format
19
20  cmd := &cobra.Command{
21      Use:               "list",
22      Short:             "list releases",
23      Long:              listHelp,
24  // 可以使用 helm ls 命令
25      Aliases:           []string{"ls"},
26      Args:              require.NoArgs,
27      ValidArgsFunction: noCompletions,
28      RunE: func(cmd *cobra.Command, args []string) error {
29        ...
30    // 调用action模块的 Run 函数
31    // 返回的结果是 Release 列表
32          results, err := client.Run()
33          ...
34    // 后面都是结果的处理
35      },
36  }
37
38// 下面是入参的处理
39  f := cmd.Flags()
40  ...
41}

action.Run函数

 1func (l *List) Run() ([]*release.Release, error) {
 2  ...
 3  // 调用List方法获取所有的Release
 4	results, err := l.cfg.Releases.List(func(rel *release.Release) bool {
 5    // 跳过不满足过滤条件的结果
 6		if filter != nil && !filter.MatchString(rel.Name) {
 7			return false
 8		}
 9		return true
10	})
11	...
12
13	// 过滤掉不满足状态条件的结果
14	results = l.filterStateMask(results)
15
16	// 过滤掉不满足label条件的结果
17	selectorObj, err := labels.Parse(l.Selector)
18	if err != nil {
19		return nil, err
20	}
21	results = l.filterSelector(results, selectorObj)
22
23  // 对结果排序
24	l.sort(results)
25  ...
26	return results, err
27}

Releases.List函数

这个是个接口,具体的实现跟配置的存储驱动有关,存储驱动包括:

  • secret
  • configmap
  • sql(只支持postgresql)
  • memory

secret存储驱动代码分析

  • 根据owner=helm标签来选择secret

源码位置:pkg/storage/driver/secret.go

 1func (secrets *Secrets) sList(filter func(*rspb.Release) bool) ([]*rspb.Release, error) {
 2  // 构造Label选择器,选择owner=helm的label
 3	lsel := kblabels.Set{"owner": "helm"}.AsSelector()
 4	opts := metav1.ListOptions{LabelSelector: lsel.String()}
 5
 6  // 调用k8s的原生api,获取所有owner=helm的secret
 7	list, err := secrets.impl.List(context.Background(), opts)
 8	...
 9  // 结果处理
10	for _, item := range list.Items {
11    // 获取每个secret中的数据,转换为Release对象
12		rls, err := decodeRelease(string(item.Data["release"]))
13		...
14	}
15	return results, nil
16}

configmap存储驱动代码分析

  • 根据owner=helm标签来选择configmap

源码位置:pkg/storage/driver/cfgmaps.go

 1func (cfgmaps *ConfigMaps) List(filter func(*rspb.Release) bool) ([]*rspb.Release, error) {
 2	 // 构造Label选择器,选择owner=helm的label
 3  lsel := kblabels.Set{"owner": "helm"}.AsSelector()
 4	opts := metav1.ListOptions{LabelSelector: lsel.String()}
 5
 6  // 调用k8s的原生api,获取所有owner=helm的configmap
 7	list, err := cfgmaps.impl.List(context.Background(), opts)
 8	...
 9	for _, item := range list.Items {
10    // 获取每个configmap中的数据,转换为Release对象
11		rls, err := decodeRelease(item.Data["release"])
12		...
13	}
14	return results, nil
15}

memory存储驱动代码实现分析

  • 查询Memory这个数据结构

源码位置:pkg/storage/driver/memory.go

 1func (mem *Memory) List(filter func(*rspb.Release) bool) ([]*rspb.Release, error) {
 2  ...
 3	for namespace := range mem.cache {
 4		if mem.namespace != "" {
 5			// Should only list releases of this namespace
 6			namespace = mem.namespace
 7		}
 8    // 查询 Memory 中的cache信息
 9		for _, recs := range mem.cache[namespace] {
10			recs.Iter(func(_ int, rec *record) bool {
11				if filter(rec.rls) {
12					ls = append(ls, rec.rls)
13				}
14				return true
15			})
16		}
17		if mem.namespace != "" {
18			// Should only list releases of this namespace
19			break
20		}
21	}
22	return ls, nil
23}
24
25// 内存作为存储驱动的数据主要存放在Memory这个对象中
26type Memory struct {
27	sync.RWMutex
28	namespace string
29	// 以命名空间为key的 Release 集合
30	cache map[string]memReleases
31}

sql存储驱动代码实现分析

  • 通过查询 releases_v1 表得到release列表

源码位置:pkg/storage/driver/sql.go

 1func (s *SQL) List(filter func(*rspb.Release) bool) ([]*rspb.Release, error) {
 2  // 拼接sql查询语句
 3  // select body from releases_v1 where owner = helm
 4  // 如果指定了命名空间,再追加where条件:and namespace = {ns}
 5	sb := s.statementBuilder.
 6		Select(sqlReleaseTableBodyColumn).
 7		From(sqlReleaseTableName).
 8		Where(sq.Eq{sqlReleaseTableOwnerColumn: sqlReleaseDefaultOwner})
 9
10	// If a namespace was specified, we only list releases from that namespace
11	if s.namespace != "" {
12		sb = sb.Where(sq.Eq{sqlReleaseTableNamespaceColumn: s.namespace})
13	}
14
15	query, args, err := sb.ToSql()
16	if err != nil {
17		s.Log("failed to build query: %v", err)
18		return nil, err
19	}
20
21	var records = []SQLReleaseWrapper{}
22	if err := s.db.Select(&records, query, args...); err != nil {
23		s.Log("list: failed to list: %v", err)
24		return nil, err
25	}
26
27	var releases []*rspb.Release
28	for _, record := range records {
29    // 将查询结果转换为 Release 对象
30		release, err := decodeRelease(record.Body)
31		if err != nil {
32			s.Log("list: failed to decode release: %v: %v", record, err)
33			continue
34		}
35		if filter(release) {
36			releases = append(releases, release)
37		}
38	}
39
40	return releases, nil
41}