github.com/latiif/helm@v2.15.0+incompatible/pkg/storage/storage.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 storage // import "k8s.io/helm/pkg/storage"
    18  
    19  import (
    20  	"fmt"
    21  	"strings"
    22  
    23  	rspb "k8s.io/helm/pkg/proto/hapi/release"
    24  	relutil "k8s.io/helm/pkg/releaseutil"
    25  	"k8s.io/helm/pkg/storage/driver"
    26  )
    27  
    28  // NoReleasesErr indicates that a given release cannot be found
    29  const NoReleasesErr = "has no deployed releases"
    30  
    31  // Storage represents a storage engine for a Release.
    32  type Storage struct {
    33  	driver.Driver
    34  
    35  	// MaxHistory specifies the maximum number of historical releases that will
    36  	// be retained, including the most recent release. Values of 0 or less are
    37  	// ignored (meaning no limits are imposed).
    38  	MaxHistory int
    39  
    40  	Log func(string, ...interface{})
    41  }
    42  
    43  // Get retrieves the release from storage. An error is returned
    44  // if the storage driver failed to fetch the release, or the
    45  // release identified by the key, version pair does not exist.
    46  func (s *Storage) Get(name string, version int32) (*rspb.Release, error) {
    47  	s.Log("getting release %q", makeKey(name, version))
    48  	return s.Driver.Get(makeKey(name, version))
    49  }
    50  
    51  // Create creates a new storage entry holding the release. An
    52  // error is returned if the storage driver failed to store the
    53  // release, or a release with identical key already exists.
    54  func (s *Storage) Create(rls *rspb.Release) error {
    55  	s.Log("creating release %q", makeKey(rls.Name, rls.Version))
    56  	if s.MaxHistory > 0 {
    57  		// Want to make space for one more release.
    58  		s.removeLeastRecent(rls.Name, s.MaxHistory-1)
    59  	}
    60  	return s.Driver.Create(makeKey(rls.Name, rls.Version), rls)
    61  }
    62  
    63  // Update update the release in storage. An error is returned if the
    64  // storage backend fails to update the release or if the release
    65  // does not exist.
    66  func (s *Storage) Update(rls *rspb.Release) error {
    67  	s.Log("updating release %q", makeKey(rls.Name, rls.Version))
    68  	return s.Driver.Update(makeKey(rls.Name, rls.Version), rls)
    69  }
    70  
    71  // Delete deletes the release from storage. An error is returned if
    72  // the storage backend fails to delete the release or if the release
    73  // does not exist.
    74  func (s *Storage) Delete(name string, version int32) (*rspb.Release, error) {
    75  	s.Log("deleting release %q", makeKey(name, version))
    76  	return s.Driver.Delete(makeKey(name, version))
    77  }
    78  
    79  // ListReleases returns all releases from storage. An error is returned if the
    80  // storage backend fails to retrieve the releases.
    81  func (s *Storage) ListReleases() ([]*rspb.Release, error) {
    82  	s.Log("listing all releases in storage")
    83  	return s.Driver.List(func(_ *rspb.Release) bool { return true })
    84  }
    85  
    86  // ListDeleted returns all releases with Status == DELETED. An error is returned
    87  // if the storage backend fails to retrieve the releases.
    88  func (s *Storage) ListDeleted() ([]*rspb.Release, error) {
    89  	s.Log("listing deleted releases in storage")
    90  	return s.Driver.List(func(rls *rspb.Release) bool {
    91  		return relutil.StatusFilter(rspb.Status_DELETED).Check(rls)
    92  	})
    93  }
    94  
    95  // ListDeployed returns all releases with Status == DEPLOYED. An error is returned
    96  // if the storage backend fails to retrieve the releases.
    97  func (s *Storage) ListDeployed() ([]*rspb.Release, error) {
    98  	s.Log("listing all deployed releases in storage")
    99  	return s.Driver.List(func(rls *rspb.Release) bool {
   100  		return relutil.StatusFilter(rspb.Status_DEPLOYED).Check(rls)
   101  	})
   102  }
   103  
   104  // ListFilterAll returns the set of releases satisfying the predicate
   105  // (filter0 && filter1 && ... && filterN), i.e. a Release is included in the results
   106  // if and only if all filters return true.
   107  func (s *Storage) ListFilterAll(fns ...relutil.FilterFunc) ([]*rspb.Release, error) {
   108  	s.Log("listing all releases with filter")
   109  	return s.Driver.List(func(rls *rspb.Release) bool {
   110  		return relutil.All(fns...).Check(rls)
   111  	})
   112  }
   113  
   114  // ListFilterAny returns the set of releases satisfying the predicate
   115  // (filter0 || filter1 || ... || filterN), i.e. a Release is included in the results
   116  // if at least one of the filters returns true.
   117  func (s *Storage) ListFilterAny(fns ...relutil.FilterFunc) ([]*rspb.Release, error) {
   118  	s.Log("listing any releases with filter")
   119  	return s.Driver.List(func(rls *rspb.Release) bool {
   120  		return relutil.Any(fns...).Check(rls)
   121  	})
   122  }
   123  
   124  // Deployed returns the last deployed release with the provided release name, or
   125  // returns ErrReleaseNotFound if not found.
   126  func (s *Storage) Deployed(name string) (*rspb.Release, error) {
   127  	ls, err := s.DeployedAll(name)
   128  	if err != nil {
   129  		if strings.Contains(err.Error(), "not found") {
   130  			return nil, fmt.Errorf("%q %s", name, NoReleasesErr)
   131  		}
   132  		return nil, err
   133  	}
   134  
   135  	if len(ls) == 0 {
   136  		return nil, fmt.Errorf("%q %s", name, NoReleasesErr)
   137  	}
   138  
   139  	return ls[0], err
   140  }
   141  
   142  // DeployedAll returns all deployed releases with the provided name, or
   143  // returns ErrReleaseNotFound if not found.
   144  func (s *Storage) DeployedAll(name string) ([]*rspb.Release, error) {
   145  	s.Log("getting deployed releases from %q history", name)
   146  
   147  	ls, err := s.Driver.Query(map[string]string{
   148  		"NAME":   name,
   149  		"OWNER":  "TILLER",
   150  		"STATUS": "DEPLOYED",
   151  	})
   152  	if err == nil {
   153  		return ls, nil
   154  	}
   155  	if strings.Contains(err.Error(), "not found") {
   156  		return nil, fmt.Errorf("%q %s", name, NoReleasesErr)
   157  	}
   158  	return nil, err
   159  }
   160  
   161  // History returns the revision history for the release with the provided name, or
   162  // returns ErrReleaseNotFound if no such release name exists.
   163  func (s *Storage) History(name string) ([]*rspb.Release, error) {
   164  	s.Log("getting release history for %q", name)
   165  
   166  	return s.Driver.Query(map[string]string{"NAME": name, "OWNER": "TILLER"})
   167  }
   168  
   169  // removeLeastRecent removes items from history until the length number of releases
   170  // does not exceed max.
   171  //
   172  // We allow max to be set explicitly so that calling functions can "make space"
   173  // for the new records they are going to write.
   174  func (s *Storage) removeLeastRecent(name string, max int) error {
   175  	if max < 0 {
   176  		return nil
   177  	}
   178  	h, err := s.History(name)
   179  	if err != nil {
   180  		return err
   181  	}
   182  	if len(h) <= max {
   183  		return nil
   184  	}
   185  
   186  	// We want oldest to newest
   187  	relutil.SortByRevision(h)
   188  
   189  	lastDeployed, err := s.Deployed(name)
   190  	if err != nil {
   191  		return err
   192  	}
   193  
   194  	var toDelete []*rspb.Release
   195  	for _, rel := range h {
   196  		// once we have enough releases to delete to reach the max, stop
   197  		if len(h)-len(toDelete) == max {
   198  			break
   199  		}
   200  		if lastDeployed != nil {
   201  			if rel.GetVersion() != lastDeployed.GetVersion() {
   202  				toDelete = append(toDelete, rel)
   203  			}
   204  		} else {
   205  			toDelete = append(toDelete, rel)
   206  		}
   207  	}
   208  
   209  	// Delete as many as possible. In the case of API throughput limitations,
   210  	// multiple invocations of this function will eventually delete them all.
   211  	errors := []error{}
   212  	for _, rel := range toDelete {
   213  		err = s.deleteReleaseVersion(name, rel.GetVersion())
   214  		if err != nil {
   215  			errors = append(errors, err)
   216  		}
   217  	}
   218  
   219  	s.Log("Pruned %d record(s) from %s with %d error(s)", len(toDelete), name, len(errors))
   220  	switch c := len(errors); c {
   221  	case 0:
   222  		return nil
   223  	case 1:
   224  		return errors[0]
   225  	default:
   226  		return fmt.Errorf("encountered %d deletion errors. First is: %s", c, errors[0])
   227  	}
   228  }
   229  
   230  func (s *Storage) deleteReleaseVersion(name string, version int32) error {
   231  	key := makeKey(name, version)
   232  	_, err := s.Delete(name, version)
   233  	if err != nil {
   234  		s.Log("error pruning %s from release history: %s", key, err)
   235  		return err
   236  	}
   237  	return nil
   238  }
   239  
   240  // Last fetches the last revision of the named release.
   241  func (s *Storage) Last(name string) (*rspb.Release, error) {
   242  	s.Log("getting last revision of %q", name)
   243  	h, err := s.History(name)
   244  	if err != nil {
   245  		return nil, err
   246  	}
   247  	if len(h) == 0 {
   248  		return nil, fmt.Errorf("no revision for release %q", name)
   249  	}
   250  
   251  	relutil.Reverse(h, relutil.SortByRevision)
   252  	return h[0], nil
   253  }
   254  
   255  // makeKey concatenates a release name and version into
   256  // a string with format ```<release_name>#v<version>```.
   257  // This key is used to uniquely identify storage objects.
   258  func makeKey(rlsname string, version int32) string {
   259  	return fmt.Sprintf("%s.v%d", rlsname, version)
   260  }
   261  
   262  // Init initializes a new storage backend with the driver d.
   263  // If d is nil, the default in-memory driver is used.
   264  func Init(d driver.Driver) *Storage {
   265  	// default driver is in memory
   266  	if d == nil {
   267  		d = driver.NewMemory()
   268  	}
   269  	return &Storage{
   270  		Driver: d,
   271  		Log:    func(_ string, _ ...interface{}) {},
   272  	}
   273  }