github.com/pingcap/tiflow@v0.0.0-20240520035814-5bf52d54e205/dm/pkg/upgrade/version.go (about)

     1  // Copyright 2020 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 upgrade
    15  
    16  import (
    17  	"encoding/json"
    18  
    19  	"github.com/pingcap/tiflow/dm/common"
    20  	"github.com/pingcap/tiflow/dm/pkg/etcdutil"
    21  	"github.com/pingcap/tiflow/dm/pkg/terror"
    22  	"github.com/pingcap/tiflow/pkg/version"
    23  	clientv3 "go.etcd.io/etcd/client/v3"
    24  )
    25  
    26  const (
    27  	// The current internal version number of the DM cluster used when upgrading from an older version.
    28  	// NOTE: +1 when a new incompatible version is introduced, so it's different from the release version.
    29  	// NOTE: it's the version of the cluster (= the version of DM-master leader now), other component versions are not recorded yet.
    30  	currentInternalNo uint64 = 4
    31  	// The minimum internal version number of the DM cluster used when importing from v1.0.x.
    32  	minInternalNo uint64 = 0
    33  )
    34  
    35  var (
    36  	// CurrentVersion represents the current version of the cluster.
    37  	CurrentVersion = NewVersion(currentInternalNo, version.ReleaseVersion)
    38  	// MinVersion represents the minimum version of the cluster.
    39  	// this version only be set after finished importing from v1.0.x, but has not upgraded to the current version.
    40  	MinVersion = NewVersion(minInternalNo, "min-ver")
    41  )
    42  
    43  // Version represents the version of the DM cluster used when upgrading from an older version.
    44  type Version struct {
    45  	InternalNo uint64 `json:"internal-no"` // internal version number
    46  	ReleaseVer string `json:"release-ver"` // release version, like `v2.0.0`, human readable
    47  }
    48  
    49  // NewVersion creates a new instance of Version.
    50  func NewVersion(internalNo uint64, releaseVer string) Version {
    51  	return Version{
    52  		InternalNo: internalNo,
    53  		ReleaseVer: releaseVer,
    54  	}
    55  }
    56  
    57  // Compare compares the version with another version.
    58  // NOTE: we only compare `InternalNo` now.
    59  func (v Version) Compare(other Version) int {
    60  	if v.InternalNo < other.InternalNo {
    61  		return -1
    62  	} else if v.InternalNo > other.InternalNo {
    63  		return 1
    64  	}
    65  	return 0
    66  }
    67  
    68  // NotSet returns whether the version is not set.
    69  func (v Version) NotSet() bool {
    70  	return v.InternalNo == 0 && v.ReleaseVer == ""
    71  }
    72  
    73  // String implements Stringer interface.
    74  func (v Version) String() string {
    75  	s, _ := v.toJSON()
    76  	return s
    77  }
    78  
    79  // toJSON returns the string of JSON represent.
    80  func (v Version) toJSON() (string, error) {
    81  	data, err := json.Marshal(v)
    82  	if err != nil {
    83  		return "", err
    84  	}
    85  	return string(data), nil
    86  }
    87  
    88  // versionFromJSON constructs version from its JSON represent.
    89  func versionFromJSON(s string) (v Version, err error) {
    90  	err = json.Unmarshal([]byte(s), &v)
    91  	return
    92  }
    93  
    94  // PutVersion puts the version into etcd.
    95  func PutVersion(cli *clientv3.Client, ver Version) (int64, error) {
    96  	value, err := ver.toJSON()
    97  	if err != nil {
    98  		return 0, err
    99  	}
   100  	op := clientv3.OpPut(common.ClusterVersionKey, value)
   101  	_, rev, err := etcdutil.DoTxnWithRepeatable(cli, etcdutil.ThenOpFunc(op))
   102  	return rev, err
   103  }
   104  
   105  // GetVersion gets the version from etcd.
   106  func GetVersion(cli *clientv3.Client) (Version, int64, error) {
   107  	txnResp, rev, err := etcdutil.DoTxnWithRepeatable(cli, etcdutil.ThenOpFunc(clientv3.OpGet(common.ClusterVersionKey)))
   108  	if err != nil {
   109  		return Version{}, 0, err
   110  	}
   111  	verResp := txnResp.Responses[0].GetResponseRange()
   112  	if verResp.Count == 0 {
   113  		return Version{}, rev, nil
   114  	} else if verResp.Count > 1 {
   115  		// this should not happen
   116  		return Version{}, rev, terror.ErrUpgradeVersionEtcdFail.Generatef("too many versions (%d) exist", verResp.Count)
   117  	}
   118  
   119  	ver, err := versionFromJSON(string(verResp.Kvs[0].Value))
   120  	if err != nil {
   121  		return Version{}, rev, err
   122  	}
   123  	return ver, rev, nil
   124  }