github.com/koderover/helm@v2.17.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  	// Sometimes Tiller's database gets corrupted and multiple releases are DEPLOYED. Take the latest.
   140  	relutil.Reverse(ls, relutil.SortByRevision)
   141  
   142  	return ls[0], err
   143  }
   144  
   145  // DeployedAll returns all deployed releases with the provided name, or
   146  // returns ErrReleaseNotFound if not found.
   147  func (s *Storage) DeployedAll(name string) ([]*rspb.Release, error) {
   148  	s.Log("getting deployed releases from %q history", name)
   149  
   150  	ls, err := s.Driver.Query(map[string]string{
   151  		"NAME":   name,
   152  		"OWNER":  "TILLER",
   153  		"STATUS": "DEPLOYED",
   154  	})
   155  	if err == nil {
   156  		return ls, nil
   157  	}
   158  	if strings.Contains(err.Error(), "not found") {
   159  		return nil, fmt.Errorf("%q %s", name, NoReleasesErr)
   160  	}
   161  	return nil, err
   162  }
   163  
   164  // History returns the revision history for the release with the provided name, or
   165  // returns ErrReleaseNotFound if no such release name exists.
   166  func (s *Storage) History(name string) ([]*rspb.Release, error) {
   167  	s.Log("getting release history for %q", name)
   168  
   169  	return s.Driver.Query(map[string]string{"NAME": name, "OWNER": "TILLER"})
   170  }
   171  
   172  // removeLeastRecent removes items from history until the length number of releases
   173  // does not exceed max.
   174  //
   175  // We allow max to be set explicitly so that calling functions can "make space"
   176  // for the new records they are going to write.
   177  func (s *Storage) removeLeastRecent(name string, max int) error {
   178  	if max < 0 {
   179  		return nil
   180  	}
   181  	h, err := s.History(name)
   182  	if err != nil {
   183  		return err
   184  	}
   185  	if len(h) <= max {
   186  		return nil
   187  	}
   188  
   189  	// We want oldest to newest
   190  	relutil.SortByRevision(h)
   191  
   192  	lastDeployed, err := s.Deployed(name)
   193  	if err != nil {
   194  		return err
   195  	}
   196  
   197  	var toDelete []*rspb.Release
   198  	for _, rel := range h {
   199  		// once we have enough releases to delete to reach the max, stop
   200  		if len(h)-len(toDelete) == max {
   201  			break
   202  		}
   203  		if lastDeployed != nil {
   204  			if rel.GetVersion() != lastDeployed.GetVersion() {
   205  				toDelete = append(toDelete, rel)
   206  			}
   207  		} else {
   208  			toDelete = append(toDelete, rel)
   209  		}
   210  	}
   211  
   212  	// Delete as many as possible. In the case of API throughput limitations,
   213  	// multiple invocations of this function will eventually delete them all.
   214  	errors := []error{}
   215  	for _, rel := range toDelete {
   216  		err = s.deleteReleaseVersion(name, rel.GetVersion())
   217  		if err != nil {
   218  			errors = append(errors, err)
   219  		}
   220  	}
   221  
   222  	s.Log("Pruned %d record(s) from %s with %d error(s)", len(toDelete), name, len(errors))
   223  	switch c := len(errors); c {
   224  	case 0:
   225  		return nil
   226  	case 1:
   227  		return errors[0]
   228  	default:
   229  		return fmt.Errorf("encountered %d deletion errors. First is: %s", c, errors[0])
   230  	}
   231  }
   232  
   233  func (s *Storage) deleteReleaseVersion(name string, version int32) error {
   234  	key := makeKey(name, version)
   235  	_, err := s.Delete(name, version)
   236  	if err != nil {
   237  		s.Log("error pruning %s from release history: %s", key, err)
   238  		return err
   239  	}
   240  	return nil
   241  }
   242  
   243  // Last fetches the last revision of the named release.
   244  func (s *Storage) Last(name string) (*rspb.Release, error) {
   245  	s.Log("getting last revision of %q", name)
   246  	h, err := s.History(name)
   247  	if err != nil {
   248  		return nil, err
   249  	}
   250  	if len(h) == 0 {
   251  		return nil, fmt.Errorf("no revision for release %q", name)
   252  	}
   253  
   254  	relutil.Reverse(h, relutil.SortByRevision)
   255  	return h[0], nil
   256  }
   257  
   258  // makeKey concatenates a release name and version into
   259  // a string with format ```<release_name>#v<version>```.
   260  // This key is used to uniquely identify storage objects.
   261  func makeKey(rlsname string, version int32) string {
   262  	return fmt.Sprintf("%s.v%d", rlsname, version)
   263  }
   264  
   265  // Init initializes a new storage backend with the driver d.
   266  // If d is nil, the default in-memory driver is used.
   267  func Init(d driver.Driver) *Storage {
   268  	// default driver is in memory
   269  	if d == nil {
   270  		d = driver.NewMemory()
   271  	}
   272  	return &Storage{
   273  		Driver: d,
   274  		Log:    func(_ string, _ ...interface{}) {},
   275  	}
   276  }