github.com/pingcap/ticdc@v0.0.0-20220526033649-485a10ef2652/pkg/version/check.go (about) 1 // Copyright 2021 PingCAP, Inc. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // See the License for the specific language governing permissions and 12 // limitations under the License. 13 14 package version 15 16 import ( 17 "context" 18 "encoding/json" 19 "fmt" 20 "io/ioutil" 21 "net/http" 22 "regexp" 23 "strings" 24 25 "github.com/pingcap/ticdc/cdc/model" 26 27 "github.com/coreos/go-semver/semver" 28 "github.com/pingcap/kvproto/pkg/metapb" 29 "github.com/pingcap/log" 30 cerror "github.com/pingcap/ticdc/pkg/errors" 31 "github.com/pingcap/ticdc/pkg/httputil" 32 "github.com/pingcap/ticdc/pkg/security" 33 pd "github.com/tikv/pd/client" 34 "go.uber.org/zap" 35 ) 36 37 // minPDVersion is the version of the minimal compatible PD. 38 var minPDVersion *semver.Version = semver.New("4.0.0-rc.1") 39 40 // MinTiKVVersion is the version of the minimal compatible TiKV. 41 var MinTiKVVersion *semver.Version = semver.New("4.0.0-rc.1") 42 43 // MinTiCDCVersion is the version of the first TiCDC release 44 var MinTiCDCVersion *semver.Version = semver.New("4.0.1") 45 46 var versionHash = regexp.MustCompile("-[0-9]+-g[0-9a-f]{7,}(-dev)?") 47 48 func removeVAndHash(v string) string { 49 if v == "" { 50 return v 51 } 52 v = versionHash.ReplaceAllLiteralString(v, "") 53 v = strings.TrimSuffix(v, "-dirty") 54 return strings.TrimPrefix(v, "v") 55 } 56 57 // CheckClusterVersion check TiKV and PD version. 58 func CheckClusterVersion( 59 ctx context.Context, client pd.Client, pdHTTP string, credential *security.Credential, errorTiKVIncompat bool, 60 ) error { 61 err := CheckStoreVersion(ctx, client, 0 /* check all TiKV */) 62 if err != nil { 63 if errorTiKVIncompat { 64 return err 65 } 66 log.Warn("check TiKV version failed", zap.Error(err)) 67 } 68 69 httpCli, err := httputil.NewClient(credential) 70 if err != nil { 71 return err 72 } 73 // See more: https://github.com/pingcap/pd/blob/v4.0.0-rc.1/server/api/version.go 74 pdVer := struct { 75 Version string `json:"version"` 76 }{} 77 78 req, err := http.NewRequestWithContext( 79 ctx, http.MethodGet, fmt.Sprintf("%s/pd/api/v1/version", pdHTTP), nil) 80 if err != nil { 81 return cerror.WrapError(cerror.ErrCheckClusterVersionFromPD, err) 82 } 83 84 resp, err := httpCli.Do(req) 85 if err != nil { 86 return cerror.WrapError(cerror.ErrCheckClusterVersionFromPD, err) 87 } 88 defer resp.Body.Close() 89 90 if resp.StatusCode < 200 || resp.StatusCode >= 300 { 91 arg := fmt.Sprintf("response status: %s", resp.Status) 92 return cerror.ErrCheckClusterVersionFromPD.GenWithStackByArgs(arg) 93 } 94 95 content, err := ioutil.ReadAll(resp.Body) 96 if err != nil { 97 return cerror.WrapError(cerror.ErrCheckClusterVersionFromPD, err) 98 } 99 100 err = json.Unmarshal(content, &pdVer) 101 if err != nil { 102 return cerror.WrapError(cerror.ErrCheckClusterVersionFromPD, err) 103 } 104 105 ver, err := semver.NewVersion(removeVAndHash(pdVer.Version)) 106 if err != nil { 107 return cerror.WrapError(cerror.ErrNewSemVersion, err) 108 } 109 110 ord := ver.Compare(*minPDVersion) 111 if ord < 0 { 112 arg := fmt.Sprintf("PD %s is not supported, require minimal version %s", 113 removeVAndHash(pdVer.Version), minPDVersion) 114 return cerror.ErrVersionIncompatible.GenWithStackByArgs(arg) 115 } 116 return nil 117 } 118 119 // CheckStoreVersion checks whether the given TiKV is compatible with this CDC. 120 // If storeID is 0, it checks all TiKV. 121 func CheckStoreVersion(ctx context.Context, client pd.Client, storeID uint64) error { 122 var stores []*metapb.Store 123 var err error 124 if storeID == 0 { 125 stores, err = client.GetAllStores(ctx, pd.WithExcludeTombstone()) 126 } else { 127 stores = make([]*metapb.Store, 1) 128 stores[0], err = client.GetStore(ctx, storeID) 129 } 130 if err != nil { 131 return cerror.WrapError(cerror.ErrGetAllStoresFailed, err) 132 } 133 134 for _, s := range stores { 135 ver, err := semver.NewVersion(removeVAndHash(s.Version)) 136 if err != nil { 137 return cerror.WrapError(cerror.ErrNewSemVersion, err) 138 } 139 ord := ver.Compare(*MinTiKVVersion) 140 if ord < 0 { 141 arg := fmt.Sprintf("TiKV %s is not supported, require minimal version %s", 142 removeVAndHash(s.Version), MinTiKVVersion) 143 return cerror.ErrVersionIncompatible.GenWithStackByArgs(arg) 144 } 145 } 146 return nil 147 } 148 149 // TiCDCClusterVersion is the version of TiCDC cluster 150 type TiCDCClusterVersion struct { 151 *semver.Version 152 isUnknown bool 153 } 154 155 // IsUnknown returns whether this is an unknown version 156 func (v *TiCDCClusterVersion) IsUnknown() bool { 157 return v.isUnknown 158 } 159 160 // ShouldEnableOldValueByDefault returns whether old value should be enabled by default 161 func (v *TiCDCClusterVersion) ShouldEnableOldValueByDefault() bool { 162 // we assume the unknown version to be the latest version 163 return v.isUnknown || !v.LessThan(*semver.New("5.0.0-rc")) 164 } 165 166 // ShouldEnableUnifiedSorterByDefault returns whether Unified Sorter should be enabled by default 167 func (v *TiCDCClusterVersion) ShouldEnableUnifiedSorterByDefault() bool { 168 if v.isUnknown { 169 // we assume the unknown version to be the latest version 170 return true 171 } 172 // x >= 4.0.13 AND x != 5.0.0-rc 173 if v.String() == "5.0.0-rc" { 174 return false 175 } 176 return !v.LessThan(*semver.New("4.0.13")) || (v.Major == 4 && v.Minor == 0 && v.Patch == 13) 177 } 178 179 // TiCDCClusterVersionUnknown is a read-only variable to represent the unknown cluster version 180 var TiCDCClusterVersionUnknown = TiCDCClusterVersion{isUnknown: true} 181 182 // GetTiCDCClusterVersion returns the version of ticdc cluster 183 func GetTiCDCClusterVersion(captureInfos []*model.CaptureInfo) (TiCDCClusterVersion, error) { 184 if len(captureInfos) == 0 { 185 return TiCDCClusterVersionUnknown, nil 186 } 187 var minVer *semver.Version 188 for _, captureInfo := range captureInfos { 189 var ver *semver.Version 190 var err error 191 if captureInfo.Version != "" { 192 ver, err = semver.NewVersion(removeVAndHash(captureInfo.Version)) 193 } else { 194 ver = MinTiCDCVersion 195 } 196 if err != nil { 197 return TiCDCClusterVersionUnknown, cerror.WrapError(cerror.ErrNewSemVersion, err) 198 } 199 if minVer == nil || ver.Compare(*minVer) < 0 { 200 minVer = ver 201 } 202 } 203 return TiCDCClusterVersion{minVer, false}, nil 204 }