github.com/koderover/helm@v2.17.0+incompatible/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  	if len(req.StatusCodes) == 0 {
    32  		req.StatusCodes = []release.Status_Code{release.Status_DEPLOYED}
    33  	}
    34  
    35  	//rels, err := s.env.Releases.ListDeployed()
    36  	rels, err := s.env.Releases.ListFilterAll(func(r *release.Release) bool {
    37  		for _, sc := range req.StatusCodes {
    38  			if sc == r.Info.Status.Code {
    39  				return true
    40  			}
    41  		}
    42  		return false
    43  	})
    44  	if err != nil {
    45  		return err
    46  	}
    47  
    48  	if req.Namespace != "" {
    49  		rels, err = filterByNamespace(req.Namespace, rels)
    50  		if err != nil {
    51  			return err
    52  		}
    53  	}
    54  
    55  	if len(req.Filter) != 0 {
    56  		rels, err = filterReleases(req.Filter, rels)
    57  		if err != nil {
    58  			return err
    59  		}
    60  	}
    61  
    62  	total := int64(len(rels))
    63  
    64  	switch req.SortBy {
    65  	case services.ListSort_NAME:
    66  		relutil.SortByName(rels)
    67  	case services.ListSort_LAST_RELEASED:
    68  		relutil.SortByDate(rels)
    69  	case services.ListSort_CHART_NAME:
    70  		relutil.SortByChartName(rels)
    71  	}
    72  
    73  	if req.SortOrder == services.ListSort_DESC {
    74  		ll := len(rels)
    75  		rr := make([]*release.Release, ll)
    76  		for i, item := range rels {
    77  			rr[ll-i-1] = item
    78  		}
    79  		rels = rr
    80  	}
    81  
    82  	l := int64(len(rels))
    83  	if req.Offset != "" {
    84  
    85  		i := -1
    86  		for ii, cur := range rels {
    87  			if cur.Name == req.Offset {
    88  				i = ii
    89  			}
    90  		}
    91  		if i == -1 {
    92  			return fmt.Errorf("offset %q not found", req.Offset)
    93  		}
    94  
    95  		if len(rels) < i {
    96  			return fmt.Errorf("no items after %q", req.Offset)
    97  		}
    98  
    99  		rels = rels[i:]
   100  		l = int64(len(rels))
   101  	}
   102  
   103  	if req.Limit == 0 {
   104  		req.Limit = ListDefaultLimit
   105  	}
   106  
   107  	next := ""
   108  	if l > req.Limit {
   109  		next = rels[req.Limit].Name
   110  		rels = rels[0:req.Limit]
   111  		l = int64(len(rels))
   112  	}
   113  	res := &services.ListReleasesResponse{
   114  		Next:  next,
   115  		Count: l,
   116  		Total: total,
   117  	}
   118  	chunks := s.partition(rels[:min(len(rels), int(req.Limit))], maxMsgSize-proto.Size(res))
   119  	for res.Releases = range chunks {
   120  		if err := stream.Send(res); err != nil {
   121  			for range chunks { // drain
   122  			}
   123  			return err
   124  		}
   125  	}
   126  	return nil
   127  }
   128  
   129  // partition packs releases into slices up to the capacity cap in bytes.
   130  func (s *ReleaseServer) partition(rels []*release.Release, cap int) <-chan []*release.Release {
   131  	chunks := make(chan []*release.Release, 1)
   132  	go func() {
   133  		var (
   134  			fill = 0 // fill is space available to fill
   135  			size int // size is size of a release
   136  		)
   137  		var chunk []*release.Release
   138  		for _, rls := range rels {
   139  			if size = proto.Size(rls); size+fill > cap {
   140  				// Over-cap, push chunk onto channel to send over gRPC stream
   141  				s.Log("partitioned at %d with %d releases (cap=%d)", fill, len(chunk), cap)
   142  				chunks <- chunk
   143  				// reset partitioning state
   144  				chunk = nil
   145  				fill = 0
   146  			}
   147  			chunk = append(chunk, rls)
   148  			fill += size
   149  		}
   150  		if len(chunk) > 0 {
   151  			// send remaining if any
   152  			chunks <- chunk
   153  		}
   154  		close(chunks)
   155  	}()
   156  	return chunks
   157  }
   158  
   159  func filterByNamespace(namespace string, rels []*release.Release) ([]*release.Release, error) {
   160  	matches := []*release.Release{}
   161  	for _, r := range rels {
   162  		if namespace == r.Namespace {
   163  			matches = append(matches, r)
   164  		}
   165  	}
   166  	return matches, nil
   167  }
   168  
   169  func filterReleases(filter string, rels []*release.Release) ([]*release.Release, error) {
   170  	preg, err := regexp.Compile(filter)
   171  	if err != nil {
   172  		return rels, err
   173  	}
   174  	matches := []*release.Release{}
   175  	for _, r := range rels {
   176  		if preg.MatchString(r.Name) {
   177  			matches = append(matches, r)
   178  		}
   179  	}
   180  	return matches, nil
   181  }