github.com/wangchanggan/helm@v0.0.0-20211020154240-11b1b7d5406d/pkg/tiller/release_list.go (about)

     1  /*
     2  Copyright The Helm Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package tiller
    18  
    19  import (
    20  	"fmt"
    21  	"regexp"
    22  
    23  	"github.com/golang/protobuf/proto"
    24  	"k8s.io/helm/pkg/proto/hapi/release"
    25  	"k8s.io/helm/pkg/proto/hapi/services"
    26  	relutil "k8s.io/helm/pkg/releaseutil"
    27  )
    28  
    29  // ListReleases lists the releases found by the server.
    30  func (s *ReleaseServer) ListReleases(req *services.ListReleasesRequest, stream services.ReleaseService_ListReleasesServer) error {
    31  	// 检查客户端需要查询的Release状态码。
    32  	if len(req.StatusCodes) == 0 {
    33  		req.StatusCodes = []release.Status_Code{release.Status_DEPLOYED}
    34  	}
    35  
    36  	//rels, err := s.env.Releases.ListDeployed()
    37  	// 调用s.env.Releases.ListFilterAll查询对应的Release。
    38  	rels, err := s.env.Releases.ListFilterAll(func(r *release.Release) bool {
    39  		for _, sc := range req.StatusCodes {
    40  			if sc == r.Info.Status.Code {
    41  				return true
    42  			}
    43  		}
    44  		return false
    45  	})
    46  	if err != nil {
    47  		return err
    48  	}
    49  
    50  	// 根据命名空间判断是否是需要的Release。
    51  	if req.Namespace != "" {
    52  		rels, err = filterByNamespace(req.Namespace, rels)
    53  		if err != nil {
    54  			return err
    55  		}
    56  	}
    57  
    58  	// 根据客户端传递来的过滤值进行对应的过滤。
    59  	if len(req.Filter) != 0 {
    60  		rels, err = filterReleases(req.Filter, rels)
    61  		if err != nil {
    62  			return err
    63  		}
    64  	}
    65  
    66  	total := int64(len(rels))
    67  
    68  	// 根据客户端传递过来的排序方法进行排序。
    69  	switch req.SortBy {
    70  	case services.ListSort_NAME:
    71  		relutil.SortByName(rels)
    72  	case services.ListSort_LAST_RELEASED:
    73  		relutil.SortByDate(rels)
    74  	case services.ListSort_CHART_NAME:
    75  		relutil.SortByChartName(rels)
    76  	}
    77  
    78  	if req.SortOrder == services.ListSort_DESC {
    79  		ll := len(rels)
    80  		rr := make([]*release.Release, ll)
    81  		for i, item := range rels {
    82  			rr[ll-i-1] = item
    83  		}
    84  		rels = rr
    85  	}
    86  
    87  	l := int64(len(rels))
    88  	if req.Offset != "" {
    89  
    90  		i := -1
    91  		for ii, cur := range rels {
    92  			if cur.Name == req.Offset {
    93  				i = ii
    94  			}
    95  		}
    96  		if i == -1 {
    97  			return fmt.Errorf("offset %q not found", req.Offset)
    98  		}
    99  
   100  		if len(rels) < i {
   101  			return fmt.Errorf("no items after %q", req.Offset)
   102  		}
   103  
   104  		rels = rels[i:]
   105  		l = int64(len(rels))
   106  	}
   107  
   108  	if req.Limit == 0 {
   109  		req.Limit = ListDefaultLimit
   110  	}
   111  
   112  	next := ""
   113  	if l > req.Limit {
   114  		next = rels[req.Limit].Name
   115  		rels = rels[0:req.Limit]
   116  		l = int64(len(rels))
   117  	}
   118  	res := &services.ListReleasesResponse{
   119  		Next:  next,
   120  		Count: l,
   121  		Total: total,
   122  	}
   123  	chunks := s.partition(rels[:min(len(rels), int(req.Limit))], maxMsgSize-proto.Size(res))
   124  	for res.Releases = range chunks {
   125  		if err := stream.Send(res); err != nil {
   126  			for range chunks { // drain
   127  			}
   128  			return err
   129  		}
   130  	}
   131  	return nil
   132  }
   133  
   134  // partition packs releases into slices up to the capacity cap in bytes.
   135  func (s *ReleaseServer) partition(rels []*release.Release, cap int) <-chan []*release.Release {
   136  	chunks := make(chan []*release.Release, 1)
   137  	go func() {
   138  		var (
   139  			fill = 0 // fill is space available to fill
   140  			size int // size is size of a release
   141  		)
   142  		var chunk []*release.Release
   143  		for _, rls := range rels {
   144  			if size = proto.Size(rls); size+fill > cap {
   145  				// Over-cap, push chunk onto channel to send over gRPC stream
   146  				s.Log("partitioned at %d with %d releases (cap=%d)", fill, len(chunk), cap)
   147  				chunks <- chunk
   148  				// reset partitioning state
   149  				chunk = nil
   150  				fill = 0
   151  			}
   152  			chunk = append(chunk, rls)
   153  			fill += size
   154  		}
   155  		if len(chunk) > 0 {
   156  			// send remaining if any
   157  			chunks <- chunk
   158  		}
   159  		close(chunks)
   160  	}()
   161  	return chunks
   162  }
   163  
   164  func filterByNamespace(namespace string, rels []*release.Release) ([]*release.Release, error) {
   165  	matches := []*release.Release{}
   166  	for _, r := range rels {
   167  		if namespace == r.Namespace {
   168  			matches = append(matches, r)
   169  		}
   170  	}
   171  	return matches, nil
   172  }
   173  
   174  func filterReleases(filter string, rels []*release.Release) ([]*release.Release, error) {
   175  	preg, err := regexp.Compile(filter)
   176  	if err != nil {
   177  		return rels, err
   178  	}
   179  	matches := []*release.Release{}
   180  	for _, r := range rels {
   181  		if preg.MatchString(r.Name) {
   182  			matches = append(matches, r)
   183  		}
   184  	}
   185  	return matches, nil
   186  }