github.com/x-helm/helm@v3.0.0-beta.3+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  // Deployed returns the last deployed release with the provided release name, or
   104  // returns ErrReleaseNotFound if not found.
   105  func (s *Storage) Deployed(name string) (*rspb.Release, error) {
   106  	ls, err := s.DeployedAll(name)
   107  	if err != nil {
   108  		if strings.Contains(err.Error(), "not found") {
   109  			return nil, errors.Errorf("%q has no deployed releases", name)
   110  		}
   111  		return nil, err
   112  	}
   113  
   114  	if len(ls) == 0 {
   115  		return nil, errors.Errorf("%q has no deployed releases", name)
   116  	}
   117  
   118  	return ls[0], err
   119  }
   120  
   121  // DeployedAll returns all deployed releases with the provided name, or
   122  // returns ErrReleaseNotFound if not found.
   123  func (s *Storage) DeployedAll(name string) ([]*rspb.Release, error) {
   124  	s.Log("getting deployed releases from %q history", name)
   125  
   126  	ls, err := s.Driver.Query(map[string]string{
   127  		"name":   name,
   128  		"owner":  "helm",
   129  		"status": "deployed",
   130  	})
   131  	if err == nil {
   132  		return ls, nil
   133  	}
   134  	if strings.Contains(err.Error(), "not found") {
   135  		return nil, errors.Errorf("%q has no deployed releases", name)
   136  	}
   137  	return nil, err
   138  }
   139  
   140  // History returns the revision history for the release with the provided name, or
   141  // returns ErrReleaseNotFound if no such release name exists.
   142  func (s *Storage) History(name string) ([]*rspb.Release, error) {
   143  	s.Log("getting release history for %q", name)
   144  
   145  	return s.Driver.Query(map[string]string{"name": name, "owner": "helm"})
   146  }
   147  
   148  // removeLeastRecent removes items from history until the lengh number of releases
   149  // does not exceed max.
   150  //
   151  // We allow max to be set explicitly so that calling functions can "make space"
   152  // for the new records they are going to write.
   153  func (s *Storage) removeLeastRecent(name string, max int) error {
   154  	if max < 0 {
   155  		return nil
   156  	}
   157  	h, err := s.History(name)
   158  	if err != nil {
   159  		return err
   160  	}
   161  	if len(h) <= max {
   162  		return nil
   163  	}
   164  	overage := len(h) - max
   165  
   166  	// We want oldest to newest
   167  	relutil.SortByRevision(h)
   168  
   169  	// Delete as many as possible. In the case of API throughput limitations,
   170  	// multiple invocations of this function will eventually delete them all.
   171  	toDelete := h[0:overage]
   172  	errs := []error{}
   173  	for _, rel := range toDelete {
   174  		key := makeKey(name, rel.Version)
   175  		_, innerErr := s.Delete(name, rel.Version)
   176  		if innerErr != nil {
   177  			s.Log("error pruning %s from release history: %s", key, innerErr)
   178  			errs = append(errs, innerErr)
   179  		}
   180  	}
   181  
   182  	s.Log("Pruned %d record(s) from %s with %d error(s)", len(toDelete), name, len(errs))
   183  	switch c := len(errs); c {
   184  	case 0:
   185  		return nil
   186  	case 1:
   187  		return errs[0]
   188  	default:
   189  		return errors.Errorf("encountered %d deletion errors. First is: %s", c, errs[0])
   190  	}
   191  }
   192  
   193  // Last fetches the last revision of the named release.
   194  func (s *Storage) Last(name string) (*rspb.Release, error) {
   195  	s.Log("getting last revision of %q", name)
   196  	h, err := s.History(name)
   197  	if err != nil {
   198  		return nil, err
   199  	}
   200  	if len(h) == 0 {
   201  		return nil, errors.Errorf("no revision for release %q", name)
   202  	}
   203  
   204  	relutil.Reverse(h, relutil.SortByRevision)
   205  	return h[0], nil
   206  }
   207  
   208  // makeKey concatenates a release name and version into
   209  // a string with format ```<release_name>#v<version>```.
   210  // This key is used to uniquely identify storage objects.
   211  func makeKey(rlsname string, version int) string {
   212  	return fmt.Sprintf("%s.v%d", rlsname, version)
   213  }
   214  
   215  // Init initializes a new storage backend with the driver d.
   216  // If d is nil, the default in-memory driver is used.
   217  func Init(d driver.Driver) *Storage {
   218  	// default driver is in memory
   219  	if d == nil {
   220  		d = driver.NewMemory()
   221  	}
   222  	return &Storage{
   223  		Driver: d,
   224  		Log:    func(_ string, _ ...interface{}) {},
   225  	}
   226  }