github.com/Beeketing/helm@v2.12.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 "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  const NoReleasesErr = "has no deployed releases"
    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 int32) (*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 int32) (*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  // ListDeleted returns all releases with Status == DELETED. An error is returned
    86  // if the storage backend fails to retrieve the releases.
    87  func (s *Storage) ListDeleted() ([]*rspb.Release, error) {
    88  	s.Log("listing deleted releases in storage")
    89  	return s.Driver.List(func(rls *rspb.Release) bool {
    90  		return relutil.StatusFilter(rspb.Status_DELETED).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.Status_DEPLOYED).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, fmt.Errorf("%q %s", name, NoReleasesErr)
   130  		}
   131  		return nil, err
   132  	}
   133  
   134  	if len(ls) == 0 {
   135  		return nil, fmt.Errorf("%q %s", name, NoReleasesErr)
   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":  "TILLER",
   149  		"STATUS": "DEPLOYED",
   150  	})
   151  	if err == nil {
   152  		return ls, nil
   153  	}
   154  	if strings.Contains(err.Error(), "not found") {
   155  		return nil, fmt.Errorf("%q %s", name, NoReleasesErr)
   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": "TILLER"})
   166  }
   167  
   168  // removeLeastRecent removes items from history until the length 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  
   185  	// We want oldest to newest
   186  	relutil.SortByRevision(h)
   187  
   188  	lastDeployed, err := s.Deployed(name)
   189  	if err != nil {
   190  		return err
   191  	}
   192  
   193  	var toDelete []*rspb.Release
   194  	for _, rel := range h {
   195  		// once we have enough releases to delete to reach the max, stop
   196  		if len(h)-len(toDelete) == max {
   197  			break
   198  		}
   199  		if lastDeployed != nil {
   200  			if rel.GetVersion() != lastDeployed.GetVersion() {
   201  				toDelete = append(toDelete, rel)
   202  			}
   203  		} else {
   204  			toDelete = append(toDelete, rel)
   205  		}
   206  	}
   207  
   208  	// Delete as many as possible. In the case of API throughput limitations,
   209  	// multiple invocations of this function will eventually delete them all.
   210  	errors := []error{}
   211  	for _, rel := range toDelete {
   212  		err = s.deleteReleaseVersion(name, rel.GetVersion())
   213  		if err != nil {
   214  			errors = append(errors, err)
   215  		}
   216  	}
   217  
   218  	s.Log("Pruned %d record(s) from %s with %d error(s)", len(toDelete), name, len(errors))
   219  	switch c := len(errors); c {
   220  	case 0:
   221  		return nil
   222  	case 1:
   223  		return errors[0]
   224  	default:
   225  		return fmt.Errorf("encountered %d deletion errors. First is: %s", c, errors[0])
   226  	}
   227  }
   228  
   229  func (s *Storage) deleteReleaseVersion(name string, version int32) error {
   230  	key := makeKey(name, version)
   231  	_, err := s.Delete(name, version)
   232  	if err != nil {
   233  		s.Log("error pruning %s from release history: %s", key, err)
   234  		return err
   235  	}
   236  	return nil
   237  }
   238  
   239  // Last fetches the last revision of the named release.
   240  func (s *Storage) Last(name string) (*rspb.Release, error) {
   241  	s.Log("getting last revision of %q", name)
   242  	h, err := s.History(name)
   243  	if err != nil {
   244  		return nil, err
   245  	}
   246  	if len(h) == 0 {
   247  		return nil, fmt.Errorf("no revision for release %q", name)
   248  	}
   249  
   250  	relutil.Reverse(h, relutil.SortByRevision)
   251  	return h[0], nil
   252  }
   253  
   254  // makeKey concatenates a release name and version into
   255  // a string with format ```<release_name>#v<version>```.
   256  // This key is used to uniquely identify storage objects.
   257  func makeKey(rlsname string, version int32) string {
   258  	return fmt.Sprintf("%s.v%d", rlsname, version)
   259  }
   260  
   261  // Init initializes a new storage backend with the driver d.
   262  // If d is nil, the default in-memory driver is used.
   263  func Init(d driver.Driver) *Storage {
   264  	// default driver is in memory
   265  	if d == nil {
   266  		d = driver.NewMemory()
   267  	}
   268  	return &Storage{
   269  		Driver: d,
   270  		Log:    func(_ string, _ ...interface{}) {},
   271  	}
   272  }