func isCompatibleWithVers(vers map[string]*version.Versions, local types.ID, minV, maxV *semver.Version) bool { var ok bool for id, v := range vers { // ignore comparison with local version if id == local.String() { continue } if v == nil { continue } clusterv, err := semver.NewVersion(v.Cluster) if err != nil { plog.Errorf("cannot understand the cluster version of member %s (%v)", id, err) continue } if clusterv.LessThan(*minV) { plog.Warningf("the running cluster version(%v) is lower than the minimal cluster version(%v) supported", clusterv.String(), minV.String()) return false } if maxV.LessThan(*clusterv) { plog.Warningf("the running cluster version(%v) is higher than the maximum cluster version(%v) supported", clusterv.String(), maxV.String()) return false } ok = true } return ok }
func mustSaveClusterVersionToBackend(be backend.Backend, ver *semver.Version) { ckey := backendClusterVersionKey() tx := be.BatchTx() tx.Lock() defer tx.Unlock() tx.UnsafePut(clusterBucketName, ckey, []byte(ver.String())) }
func (c *cluster) SetVersion(ver *semver.Version) { c.Lock() defer c.Unlock() if c.version != nil { plog.Noticef("updated the cluster version from %v to %v", c.version.String(), ver.String()) } else { plog.Noticef("set the initial cluster version to %v", ver.String()) } c.version = ver }
// describe returns a word describing the process we're about to do ("update", "downgrading", etc) func describe(a, b semver.Version, verb bool) string { if verb && a.LessThan(b) { return "Downgrading" } else if verb { return "Upgrading" } else if a.LessThan(b) { return "Downgrade" } return "Upgrade" }
func (c *RaftCluster) SetVersion(ver *semver.Version) { c.Lock() defer c.Unlock() if c.version != nil { plog.Noticef("updated the cluster version from %v to %v", version.Cluster(c.version.String()), version.Cluster(ver.String())) } else { plog.Noticef("set the initial cluster version to %v", version.Cluster(ver.String())) } c.version = ver mustDetectDowngrade(c.version) }
// UpdateCapability updates the enabledMap when the cluster version increases. func UpdateCapability(v *semver.Version) { if v == nil { // if recovered but version was never set by cluster return } enableMapMu.Lock() if curVersion != nil && !curVersion.LessThan(*v) { enableMapMu.Unlock() return } curVersion = v enabledMap = capabilityMaps[curVersion.String()] enableMapMu.Unlock() plog.Infof("enabled capabilities for version %s", version.Cluster(v.String())) }
func (c *RaftCluster) SetVersion(ver *semver.Version, onSet func(*semver.Version)) { c.Lock() defer c.Unlock() if c.version != nil { plog.Noticef("updated the cluster version from %v to %v", version.Cluster(c.version.String()), version.Cluster(ver.String())) } else { plog.Noticef("set the initial cluster version to %v", version.Cluster(ver.String())) } c.version = ver mustDetectDowngrade(c.version) if c.store != nil { mustSaveClusterVersionToStore(c.store, ver) } if c.be != nil { mustSaveClusterVersionToBackend(c.be, ver) } onSet(ver) }
// LatestDaemonVersion attempts to retrieve the latest version of fleetd // that has been registered in the Registry. It returns the version if // it can be determined (or nil otherwise), and any error encountered. func (r *EtcdRegistry) LatestDaemonVersion() (*semver.Version, error) { machs, err := r.Machines() if err != nil { if isEtcdError(err, etcd.ErrorCodeKeyNotFound) { err = nil } return nil, err } var lv *semver.Version for _, m := range machs { v, err := semver.NewVersion(m.Version) if err != nil { continue } else if lv == nil || lv.LessThan(*v) { lv = v } } return lv, nil }
// capabilityLoop checks the cluster version every 500ms and updates // the enabledMap when the cluster version increased. // capabilityLoop MUST be ran in a goroutine before checking capability // or using capabilityHandler. func capabilityLoop(s *etcdserver.EtcdServer) { stopped := s.StopNotify() var pv *semver.Version for { if v := s.ClusterVersion(); v != pv { if pv == nil || (v != nil && pv.LessThan(*v)) { pv = v enableMapMu.Lock() enabledMap = capabilityMaps[pv.String()] enableMapMu.Unlock() plog.Infof("enabled capabilities for version %s", pv) } } select { case <-stopped: return case <-time.After(500 * time.Millisecond): } } }
// isMinImageVersion will return true if the supplied version is greater then // or equal to the current CoreOS release indicated by the given release // channel. func isMinImageVersion(minVersion semver.Version, release string) (bool, error) { metaData, err := coreosutil.GetAMIData(release) if err != nil { return false, fmt.Errorf("Unable to retrieve current release channel version: %v", err) } version, ok := metaData["release_info"]["version"] if !ok { return false, fmt.Errorf("Error parsing image metadata for version") } current, err := semver.NewVersion(version) if err != nil { return false, fmt.Errorf("Error parsing semver from image version %v", err) } if minVersion.LessThan(*current) { return false, nil } return true, nil }
func mustSaveClusterVersionToStore(s store.Store, ver *semver.Version) { if _, err := s.Set(StoreClusterVersionKey(), false, ver.String(), store.TTLOptionSet{ExpireTime: store.Permanent}); err != nil { plog.Panicf("save cluster version should never fail: %v", err) } }
func MustDetectDowngrade(cv *semver.Version) { lv := semver.Must(semver.NewVersion(version.Version)) // only keep major.minor version for comparison against cluster version lv = &semver.Version{Major: lv.Major, Minor: lv.Minor} if cv != nil && lv.LessThan(*cv) { plog.Fatalf("cluster cannot be downgraded (current version: %s is lower than determined cluster version: %s).", version.Version, version.Cluster(cv.String())) } }
func changeRefs(data []byte, major *semver.Version) (changed []byte, versions semver.Versions, err error) { var hlinei, hlinej int // HEAD reference line start/end var mlinei, mlinej int // master reference line start/end var vrefhash string var vrefname string var vrefv *semver.Version // Record all available versions, the locations of the master and HEAD lines, // and details of the best reference satisfying the requested major version. versions = semver.Versions{} sdata := string(data) for i, j := 0, 0; i < len(data); i = j { size, err := strconv.ParseInt(sdata[i:i+4], 16, 32) if err != nil { return nil, nil, fmt.Errorf("cannot parse refs line size: %s", string(data[i:i+4])) } if size == 0 { size = 4 } j = i + int(size) if j > len(sdata) { return nil, nil, fmt.Errorf("incomplete refs data received from GitHub") } if sdata[0] == '#' { continue } hashi := i + 4 hashj := strings.IndexByte(sdata[hashi:j], ' ') if hashj < 0 || hashj != 40 { continue } hashj += hashi namei := hashj + 1 namej := strings.IndexAny(sdata[namei:j], "\n\x00") if namej < 0 { namej = j } else { namej += namei } name := sdata[namei:namej] if name == "HEAD" { hlinei = i hlinej = j } if name == "refs/heads/master" { mlinei = i mlinej = j } if strings.HasPrefix(name, "refs/tags/v") { if !strings.HasSuffix(name, "^{}") { continue // Only accept annotated tags. } // Annotated tag is peeled off and overrides the same version just parsed. name = name[:len(name)-3] v, err := semver.NewVersion(name[strings.IndexByte(name, 'v')+1:]) if err == nil { versions = append(versions, v) if major.Major == v.Major && (vrefv == nil || v == vrefv || vrefv.LessThan(*v)) { vrefv = v vrefhash = sdata[hashi:hashj] vrefname = name } } } } // If the file has no HEAD line or the version was not found, report as unavailable. if hlinei == 0 || vrefhash == "" { return nil, nil, ErrNoVersion } var buf bytes.Buffer buf.Grow(len(data) + 256) // Copy the header as-is. buf.Write(data[:hlinei]) // Extract the original capabilities. caps := "" if i := strings.Index(sdata[hlinei:hlinej], "\x00"); i > 0 { caps = strings.Replace(sdata[hlinei+i+1:hlinej-1], "symref=", "oldref=", -1) } // Insert the HEAD reference line with the right hash and a proper symref capability. var line string if strings.HasPrefix(vrefname, "refs/heads/") { if caps == "" { line = fmt.Sprintf("%s HEAD\x00symref=HEAD:%s\n", vrefhash, vrefname) } else { line = fmt.Sprintf("%s HEAD\x00symref=HEAD:%s %s\n", vrefhash, vrefname, caps) } } else { if caps == "" { line = fmt.Sprintf("%s HEAD\n", vrefhash) } else { line = fmt.Sprintf("%s HEAD\x00%s\n", vrefhash, caps) } } fmt.Fprintf(&buf, "%04x%s", 4+len(line), line) // Insert the master reference line. line = fmt.Sprintf("%s refs/heads/master\n", vrefhash) fmt.Fprintf(&buf, "%04x%s", 4+len(line), line) // Append the rest, dropping the original master line if necessary. if mlinei > 0 { buf.Write(data[hlinej:mlinei]) buf.Write(data[mlinej:]) } else { buf.Write(data[hlinej:]) } return buf.Bytes(), versions, nil }