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