github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/clusterversion/keyed_versions.go (about)

     1  // Copyright 2017 The Cockroach Authors.
     2  //
     3  // Use of this software is governed by the Business Source License
     4  // included in the file licenses/BSL.txt.
     5  //
     6  // As of the Change Date specified in that file, in accordance with
     7  // the Business Source License, use of this software will be governed
     8  // by the Apache License, Version 2.0, included in the file
     9  // licenses/APL.txt.
    10  
    11  package clusterversion
    12  
    13  import (
    14  	"context"
    15  	"fmt"
    16  	"strings"
    17  
    18  	"github.com/cockroachdb/cockroach/pkg/roachpb"
    19  	"github.com/cockroachdb/cockroach/pkg/util/log"
    20  	"github.com/cockroachdb/errors"
    21  	"github.com/kr/pretty"
    22  )
    23  
    24  // keyedVersion associates a key to a version.
    25  type keyedVersion struct {
    26  	Key VersionKey
    27  	roachpb.Version
    28  }
    29  
    30  // keyedVersions is a container for managing the versions of CockroachDB.
    31  type keyedVersions []keyedVersion
    32  
    33  // MustByKey asserts that the version specified by this key exists, and returns it.
    34  func (kv keyedVersions) MustByKey(k VersionKey) roachpb.Version {
    35  	key := int(k)
    36  	if key >= len(kv) || key < 0 {
    37  		log.Fatalf(context.Background(), "version with key %d does not exist, have:\n%s",
    38  			key, pretty.Sprint(kv))
    39  	}
    40  	return kv[key].Version
    41  }
    42  
    43  // Validate makes sure that the keyedVersions are sorted chronologically, that
    44  // their keys correspond to their position in the list, and that no obsolete
    45  // versions (i.e. known to always be active) are present.
    46  func (kv keyedVersions) Validate() error {
    47  	type majorMinor struct {
    48  		major, minor int32
    49  		vs           []keyedVersion
    50  	}
    51  	// byRelease maps major.minor to a slice of versions that were first
    52  	// released right after major.minor. For example, a version 2.1-12 would
    53  	// first be released in 19.1, and would be slotted under 2.1. We'll need
    54  	// this to determine which versions are always active with a binary built
    55  	// from the current SHA.
    56  	var byRelease []majorMinor
    57  	for i, namedVersion := range kv {
    58  		if int(namedVersion.Key) != i {
    59  			return errors.Errorf("version %s should have key %d but has %d",
    60  				namedVersion, i, namedVersion.Key)
    61  		}
    62  		if i > 0 {
    63  			prev := kv[i-1]
    64  			if !prev.Version.Less(namedVersion.Version) {
    65  				return errors.Errorf("version %s must be larger than %s", namedVersion, prev)
    66  			}
    67  		}
    68  		mami := majorMinor{major: namedVersion.Major, minor: namedVersion.Minor}
    69  		n := len(byRelease)
    70  		if n == 0 || byRelease[n-1].major != mami.major || byRelease[n-1].minor != mami.minor {
    71  			// Add new entry to the slice.
    72  			byRelease = append(byRelease, mami)
    73  			n++
    74  		}
    75  		// Add to existing entry.
    76  		byRelease[n-1].vs = append(byRelease[n-1].vs, namedVersion)
    77  	}
    78  
    79  	// Iterate through all versions known to be active. For example, if
    80  	//
    81  	//   byRelease = ["2.0", "2.1", "19.1"]
    82  	//
    83  	// then we know that the current release cycle is 19.2, so mixed version
    84  	// clusters are running at least 19.1, so anything slotted under 2.1 (like
    85  	// 2.1-12) and 2.0 is always-on. To avoid interfering with backports, we're
    86  	// a bit more lenient and allow one more release cycle until validation fails.
    87  	// In the above example, we would tolerate 2.1-x but not 2.0-x.
    88  	if n := len(byRelease) - 4; n >= 0 {
    89  		var buf strings.Builder
    90  		for i, mami := range byRelease[:n+1] {
    91  			s := "next release"
    92  			if i+1 < len(byRelease)-1 {
    93  				nextMM := byRelease[i+1]
    94  				s = fmt.Sprintf("%d.%d", nextMM.major, nextMM.minor)
    95  			}
    96  			for _, nv := range mami.vs {
    97  				fmt.Fprintf(&buf, "introduced in %s: %s\n", s, nv.Key)
    98  			}
    99  		}
   100  		mostRecentRelease := byRelease[len(byRelease)-1]
   101  		return errors.Errorf(
   102  			"found versions that are always active because %d.%d is already "+
   103  				"released; these should be removed:\n%s",
   104  			mostRecentRelease.minor, mostRecentRelease.major,
   105  			buf.String(),
   106  		)
   107  	}
   108  	return nil
   109  }