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 }