func checkFleetVersion() { output, err := utils.ExecCmdGetOutput("fleetctl") if err != nil { log.Fatal("fleetctl is required in PATH") } scanner := bufio.NewScanner(strings.NewReader(output)) for scanner.Scan() { line := scanner.Text() if strings.TrimSpace(line) == "VERSION:" { scanner.Scan() versionString := strings.TrimSpace(scanner.Text()) version, err := semver.NewVersion(versionString) if err != nil { log.Error("Cannot parse version of fleetctl", versionString) os.Exit(1) } supported, _ := semver.NewVersion(FLEET_SUPPORTED_VERSION) if version.LessThan(*supported) { log.Error("fleetctl version in your path is too old. Require >= " + FLEET_SUPPORTED_VERSION) os.Exit(1) } break } } }
// CanUseIptablesProxier returns true if we should use the iptables Proxier // instead of the "classic" userspace Proxier. This is determined by checking // the iptables version and for the existence of kernel features. It may return // an error if it fails to get the iptables version without error, in which // case it will also return false. func CanUseIptablesProxier(iptver IptablesVersioner) (bool, error) { minVersion, err := semver.NewVersion(iptablesMinVersion) if err != nil { return false, err } // returns "X.Y.Z" versionString, err := iptver.GetVersion() if err != nil { return false, err } version, err := semver.NewVersion(versionString) if err != nil { return false, err } if version.LessThan(*minVersion) { return false, nil } // Check for the required sysctls. We don't care about the value, just // that it exists. If this Proxier is chosen, we'll iniialize it as we // need. // TODO: we should inject a sysctl.Interface like we do for iptables _, err = utilsysctl.GetSysctl(sysctlRouteLocalnet) if err != nil { return false, err } return true, nil }
// decideClusterVersion decides the cluster version based on the versions map. // The returned version is the min server version in the map, or nil if the min // version in unknown. func decideClusterVersion(vers map[string]*version.Versions) *semver.Version { var cv *semver.Version lv := semver.Must(semver.NewVersion(version.Version)) for mid, ver := range vers { if ver == nil { return nil } v, err := semver.NewVersion(ver.Server) if err != nil { plog.Errorf("cannot understand the version of member %s (%v)", mid, err) return nil } if lv.LessThan(*v) { plog.Warningf("the local etcd version %s is not up-to-date", lv.String()) plog.Warningf("member %s has a higher version %s", mid, ver.Server) } if cv == nil { cv = v } else if v.LessThan(*cv) { cv = v } } return cv }
func checkRktVersion() { output, err := utils.ExecCmdGetOutput("rkt") if err != nil { panic("rkt is required in PATH") } scanner := bufio.NewScanner(strings.NewReader(output)) for scanner.Scan() { line := scanner.Text() if strings.TrimSpace(line) == "VERSION:" { scanner.Scan() versionString := strings.TrimSpace(scanner.Text()) version, err := semver.NewVersion(versionString) if err != nil { panic("Cannot parse version of rkt" + versionString) } supported, _ := semver.NewVersion(RKT_SUPPORTED_VERSION) if version.LessThan(*supported) { panic("rkt version in your path is too old. Require >= " + RKT_SUPPORTED_VERSION) } break } } }
// Checks if iptables version has a "wait" flag func getIptablesWaitFlag(vstring string) []string { version, err := semver.NewVersion(vstring) if err != nil { glog.Errorf("vstring (%s) is not a valid version string: %v", vstring, err) return nil } minVersion, err := semver.NewVersion(MinWaitVersion) if err != nil { glog.Errorf("MinWaitVersion (%s) is not a valid version string: %v", MinWaitVersion, err) return nil } if version.LessThan(*minVersion) { return nil } minVersion, err = semver.NewVersion(MinWait2Version) if err != nil { glog.Errorf("MinWait2Version (%s) is not a valid version string: %v", MinWait2Version, err) return nil } if version.LessThan(*minVersion) { return []string{"-w"} } else { return []string{"-w2"} } }
func TestServerVersion(t *testing.T) { tests := []struct { h http.Header wv *semver.Version }{ // backward compatibility with etcd 2.0 { http.Header{}, semver.Must(semver.NewVersion("2.0.0")), }, { http.Header{"X-Server-Version": []string{"2.1.0"}}, semver.Must(semver.NewVersion("2.1.0")), }, { http.Header{"X-Server-Version": []string{"2.1.0-alpha.0+git"}}, semver.Must(semver.NewVersion("2.1.0-alpha.0+git")), }, } for i, tt := range tests { v := serverVersion(tt.h) if v.String() != tt.wv.String() { t.Errorf("#%d: version = %s, want %s", i, v, tt.wv) } } }
func (mw *SemVerMiddleware) MiddlewareFunc(handler rest.HandlerFunc) rest.HandlerFunc { minVersion, err := semver.NewVersion(mw.MinVersion) if err != nil { panic(err) } maxVersion, err := semver.NewVersion(mw.MaxVersion) if err != nil { panic(err) } return func(writer rest.ResponseWriter, request *rest.Request) { version, err := semver.NewVersion(request.PathParam("version")) if err != nil { rest.Error(writer, "Invalid version: "+err.Error(), http.StatusBadRequest) return } if version.LessThan(*minVersion) { rest.Error(writer, "Min supported version is "+minVersion.String(), http.StatusBadRequest) return } if maxVersion.LessThan(*version) { rest.Error(writer, "Max supported version is "+maxVersion.String(), http.StatusBadRequest) return } request.Env["VERSION"] = version handler(writer, request) } }
func TestCheckStreamSupport(t *testing.T) { tests := []struct { v *semver.Version t streamType w bool }{ // support { semver.Must(semver.NewVersion("2.1.0")), streamTypeMsgAppV2, true, }, // ignore patch { semver.Must(semver.NewVersion("2.1.9")), streamTypeMsgAppV2, true, }, // ignore prerelease { semver.Must(semver.NewVersion("2.1.0-alpha")), streamTypeMsgAppV2, true, }, } for i, tt := range tests { if g := checkStreamSupport(tt.v, tt.t); g != tt.w { t.Errorf("#%d: check = %v, want %v", i, g, tt.w) } } }
// ShouldUseIptablesProxier returns true if we should use the iptables Proxier // instead of the "classic" userspace Proxier. This is determined by checking // the iptables version and for the existence of kernel features. It may return // an error if it fails to get the itpables version without error, in which // case it will also return false. func ShouldUseIptablesProxier() (bool, error) { exec := utilexec.New() minVersion, err := semver.NewVersion(iptablesMinVersion) if err != nil { return false, err } // returns "X.X.X", err versionString, err := utiliptables.GetIptablesVersionString(exec) if err != nil { return false, err } version, err := semver.NewVersion(versionString) if err != nil { return false, err } if version.LessThan(*minVersion) { return false, nil } // Check for the required sysctls. We don't care about the value, just // that it exists. If this Proxier is chosen, we'll iniialize it as we // need. _, err = utilsysctl.GetSysctl(sysctlRouteLocalnet) if err != nil { return false, err } return true, nil }
func TestDecideClusterVersion(t *testing.T) { tests := []struct { vers map[string]*version.Versions wdver *semver.Version }{ { map[string]*version.Versions{"a": {Server: "2.0.0"}}, semver.Must(semver.NewVersion("2.0.0")), }, // unknown { map[string]*version.Versions{"a": nil}, nil, }, { map[string]*version.Versions{"a": {Server: "2.0.0"}, "b": {Server: "2.1.0"}, "c": {Server: "2.1.0"}}, semver.Must(semver.NewVersion("2.0.0")), }, { map[string]*version.Versions{"a": {Server: "2.1.0"}, "b": {Server: "2.1.0"}, "c": {Server: "2.1.0"}}, semver.Must(semver.NewVersion("2.1.0")), }, { map[string]*version.Versions{"a": nil, "b": {Server: "2.1.0"}, "c": {Server: "2.1.0"}}, nil, }, } for i, tt := range tests { dver := decideClusterVersion(tt.vers) if !reflect.DeepEqual(dver, tt.wdver) { t.Errorf("#%d: ver = %+v, want %+v", i, dver, tt.wdver) } } }
// isCompatibleWithCluster return true if the local member has a compatible version with // the current running cluster. // The version is considered as compatible when at least one of the other members in the cluster has a // cluster version in the range of [MinClusterVersion, Version] and no known members has a cluster version // out of the range. // We set this rule since when the local member joins, another member might be offline. func isCompatibleWithCluster(cl Cluster, local types.ID, rt http.RoundTripper) bool { vers := getVersions(cl, local, rt) minV := semver.Must(semver.NewVersion(version.MinClusterVersion)) maxV := semver.Must(semver.NewVersion(version.Version)) maxV = &semver.Version{ Major: maxV.Major, Minor: maxV.Minor, } return isCompatibleWithVers(vers, local, minV, maxV) }
// checkVersionCompability checks whether the given version is compatible // with the local version. func checkVersionCompability(name string, server, minCluster *semver.Version) error { localServer := semver.Must(semver.NewVersion(version.Version)) localMinCluster := semver.Must(semver.NewVersion(version.MinClusterVersion)) if compareMajorMinorVersion(server, localMinCluster) == -1 { return fmt.Errorf("remote version is too low: remote[%s]=%s, local=%s", name, server, localServer) } if compareMajorMinorVersion(minCluster, localServer) == 1 { return fmt.Errorf("local version is too low: remote[%s]=%s, local=%s", name, server, localServer) } return nil }
// GetUpdatePackage returns an update package for the instance/application // provided. The instance details and the application it's running will be // registered in CoreRoller (or updated if it's already registered). func (api *API) GetUpdatePackage(instanceID, instanceIP, instanceVersion, appID, groupID string) (*Package, error) { instance, err := api.RegisterInstance(instanceID, instanceIP, instanceVersion, appID, groupID) if err != nil { return nil, ErrRegisterInstanceFailed } if instance.Application.Status.Valid { switch int(instance.Application.Status.Int64) { case InstanceStatusUpdateGranted, InstanceStatusDownloading, InstanceStatusDownloaded, InstanceStatusInstalled: return nil, ErrUpdateInProgressOnInstance } } group, err := api.GetGroup(groupID) if err != nil { return nil, err } if group.Channel == nil || group.Channel.Package == nil { _ = api.newGroupActivityEntry(activityPackageNotFound, activityWarning, "0.0.0", appID, groupID) return nil, ErrNoPackageFound } instanceSemver, _ := semver.NewVersion(instanceVersion) packageSemver, _ := semver.NewVersion(group.Channel.Package.Version) if !instanceSemver.LessThan(*packageSemver) { return nil, ErrNoUpdatePackageAvailable } updatesStats, err := api.getGroupUpdatesStats(group) if err != nil { return nil, ErrGetUpdatesStatsFailed } if err := api.enforceRolloutPolicy(instance, group, updatesStats); err != nil { return nil, err } if err := api.grantUpdate(instance.ID, appID, group.Channel.Package.Version); err != nil { return nil, ErrGrantingUpdate } if updatesStats.UpdatesToCurrentVersionGranted == 0 { _ = api.newGroupActivityEntry(activityRolloutStarted, activityInfo, group.Channel.Package.Version, appID, group.ID) } if !group.RolloutInProgress { _ = api.setGroupRolloutInProgress(groupID, true) } _ = api.updateInstanceStatus(instance.ID, appID, InstanceStatusUpdateGranted) return group.Channel.Package, nil }
func main() { vA, err := semver.NewVersion(os.Args[1]) if err != nil { fmt.Println(err.Error()) } vB, err := semver.NewVersion(os.Args[2]) if err != nil { fmt.Println(err.Error()) } fmt.Printf("%s < %s == %t\n", vA, vB, vA.LessThan(*vB)) }
func init() { sv, err := semver.NewVersion(strings.TrimPrefix(Version, "v")) if err != nil { panic(fmt.Sprintf("bad version string: %v", err)) } SemVersion = *sv }
func newRktVersion(version string) (rktVersion, error) { sem, err := semver.NewVersion(version) if err != nil { return rktVersion{}, err } return rktVersion{sem}, nil }
func (a *applierV2store) Put(r *pb.Request) Response { ttlOptions := toTTLOptions(r) exists, existsSet := pbutil.GetBool(r.PrevExist) switch { case existsSet: if exists { if r.PrevIndex == 0 && r.PrevValue == "" { return toResponse(a.s.store.Update(r.Path, r.Val, ttlOptions)) } else { return toResponse(a.s.store.CompareAndSwap(r.Path, r.PrevValue, r.PrevIndex, r.Val, ttlOptions)) } } return toResponse(a.s.store.Create(r.Path, r.Dir, r.Val, false, ttlOptions)) case r.PrevIndex > 0 || r.PrevValue != "": return toResponse(a.s.store.CompareAndSwap(r.Path, r.PrevValue, r.PrevIndex, r.Val, ttlOptions)) default: if storeMemberAttributeRegexp.MatchString(r.Path) { id := membership.MustParseMemberIDFromKey(path.Dir(r.Path)) var attr membership.Attributes if err := json.Unmarshal([]byte(r.Val), &attr); err != nil { plog.Panicf("unmarshal %s should never fail: %v", r.Val, err) } a.s.cluster.UpdateAttributes(id, attr) // return an empty response since there is no consumer. return Response{} } if r.Path == membership.StoreClusterVersionKey() { a.s.cluster.SetVersion(semver.Must(semver.NewVersion(r.Val))) // return an empty response since there is no consumer. return Response{} } return toResponse(a.s.store.Set(r.Path, r.Dir, r.Val, ttlOptions)) } }
// Checks if iptables has the "-C" flag func getIptablesHasCheckCommand(vstring string) bool { minVersion, err := semver.NewVersion(MinCheckVersion) if err != nil { glog.Errorf("MinCheckVersion (%s) is not a valid version string: %v", MinCheckVersion, err) return true } version, err := semver.NewVersion(vstring) if err != nil { glog.Errorf("vstring (%s) is not a valid version string: %v", vstring, err) return true } if version.LessThan(*minVersion) { return false } return true }
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 }
// applyRequest interprets r as a call to store.X and returns a Response interpreted // from store.Event func (s *EtcdServer) applyRequest(r pb.Request) Response { f := func(ev *store.Event, err error) Response { return Response{Event: ev, err: err} } expr := timeutil.UnixNanoToTime(r.Expiration) refresh, _ := pbutil.GetBool(r.Refresh) ttlOptions := store.TTLOptionSet{ExpireTime: expr, Refresh: refresh} switch r.Method { case "POST": return f(s.store.Create(r.Path, r.Dir, r.Val, true, ttlOptions)) case "PUT": exists, existsSet := pbutil.GetBool(r.PrevExist) switch { case existsSet: if exists { if r.PrevIndex == 0 && r.PrevValue == "" { return f(s.store.Update(r.Path, r.Val, ttlOptions)) } else { return f(s.store.CompareAndSwap(r.Path, r.PrevValue, r.PrevIndex, r.Val, ttlOptions)) } } return f(s.store.Create(r.Path, r.Dir, r.Val, false, ttlOptions)) case r.PrevIndex > 0 || r.PrevValue != "": return f(s.store.CompareAndSwap(r.Path, r.PrevValue, r.PrevIndex, r.Val, ttlOptions)) default: // TODO (yicheng): cluster should be the owner of cluster prefix store // we should not modify cluster store here. if storeMemberAttributeRegexp.MatchString(r.Path) { id := mustParseMemberIDFromKey(path.Dir(r.Path)) var attr Attributes if err := json.Unmarshal([]byte(r.Val), &attr); err != nil { plog.Panicf("unmarshal %s should never fail: %v", r.Val, err) } ok := s.cluster.UpdateAttributes(id, attr) if !ok { return Response{} } } if r.Path == path.Join(StoreClusterPrefix, "version") { s.cluster.SetVersion(semver.Must(semver.NewVersion(r.Val))) } return f(s.store.Set(r.Path, r.Dir, r.Val, ttlOptions)) } case "DELETE": switch { case r.PrevIndex > 0 || r.PrevValue != "": return f(s.store.CompareAndDelete(r.Path, r.PrevValue, r.PrevIndex)) default: return f(s.store.Delete(r.Path, r.Dir, r.Recursive)) } case "QGET": return f(s.store.Get(r.Path, r.Recursive, r.Sorted)) case "SYNC": s.store.DeleteExpiredKeys(time.Unix(0, r.Time)) return Response{} default: // This should never be reached, but just in case: return Response{err: ErrUnknownMethod} } }
// serverVersion returns the min cluster version from the given header. func minClusterVersion(h http.Header) *semver.Version { verStr := h.Get("X-Min-Cluster-Version") // backward compatibility with etcd 2.0 if verStr == "" { verStr = "2.0.0" } return semver.Must(semver.NewVersion(verStr)) }
func version() string { version, err := semver.NewVersion(VERSION) if err != nil { errorMessage := fmt.Sprintf("Error with version number: %v", VERSION) log.Panicln(errorMessage, err.Error()) } return version.String() }
func TestCheckVersionCompatibility(t *testing.T) { ls := semver.Must(semver.NewVersion(version.Version)) lmc := semver.Must(semver.NewVersion(version.MinClusterVersion)) tests := []struct { server *semver.Version minCluster *semver.Version wok bool }{ // the same version as local { ls, lmc, true, }, // one version lower { lmc, &semver.Version{}, true, }, // one version higher { &semver.Version{Major: ls.Major + 1}, ls, true, }, // too low version { &semver.Version{Major: lmc.Major - 1}, &semver.Version{}, false, }, // too high version { &semver.Version{Major: ls.Major + 1, Minor: 1}, &semver.Version{Major: ls.Major + 1}, false, }, } for i, tt := range tests { err := checkVersionCompability("", tt.server, tt.minCluster) if ok := err == nil; ok != tt.wok { t.Errorf("#%d: ok = %v, want %v", i, ok, tt.wok) } } }
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 newFakeRegistryForCheckVersion(v string) registry.ClusterRegistry { sv, err := semver.NewVersion(v) if err != nil { panic(err) } return registry.NewFakeClusterRegistry(sv, 0) }
func (c *RaftCluster) Version() *semver.Version { c.Lock() defer c.Unlock() if c.version == nil { return nil } return semver.Must(semver.NewVersion(c.version.String())) }
// ShouldUseIptablesProxier returns true if we should use the iptables Proxier instead of // the userspace Proxier. // This is determined by the iptables version. It may return an erorr if it fails to get the // itpables version without error, in which case it will also return false. func ShouldUseIptablesProxier() (bool, error) { exec := utilexec.New() minVersion, err := semver.NewVersion(IPTABLES_MIN_VERSION) if err != nil { return false, err } // returns "vX.X.X", err versionString, err := utiliptables.GetIptablesVersionString(exec) if err != nil { return false, err } // make a semver of the part after the v in "vX.X.X" version, err := semver.NewVersion(versionString[1:]) if err != nil { return false, err } return !version.LessThan(*minVersion), nil }
func clusterVersionFromStore(st store.Store) *semver.Version { e, err := st.Get(path.Join(storePrefix, "version"), false, false) if err != nil { if isKeyNotFound(err) { return nil } plog.Panicf("unexpected error (%v) when getting cluster version from store", err) } return semver.Must(semver.NewVersion(*e.Node.Value)) }
func TestCompareMajorMinorVersion(t *testing.T) { tests := []struct { va, vb *semver.Version w int }{ // equal to { semver.Must(semver.NewVersion("2.1.0")), semver.Must(semver.NewVersion("2.1.0")), 0, }, // smaller than { semver.Must(semver.NewVersion("2.0.0")), semver.Must(semver.NewVersion("2.1.0")), -1, }, // bigger than { semver.Must(semver.NewVersion("2.2.0")), semver.Must(semver.NewVersion("2.1.0")), 1, }, // ignore patch { semver.Must(semver.NewVersion("2.1.1")), semver.Must(semver.NewVersion("2.1.0")), 0, }, // ignore prerelease { semver.Must(semver.NewVersion("2.1.0-alpha.0")), semver.Must(semver.NewVersion("2.1.0")), 0, }, } for i, tt := range tests { if g := compareMajorMinorVersion(tt.va, tt.vb); g != tt.w { t.Errorf("#%d: compare = %d, want %d", i, g, tt.w) } } }
// protocolVersionsAreCompatible checks that the two implementations // can talk to each other. It will use semver, but for now while // we're in tight development, we will return false for minor version // changes too. func protocolVersionsAreCompatible(v1, v2 string) bool { if strings.HasPrefix(v1, "ipfs/") { v1 = v1[5:] } if strings.HasPrefix(v2, "ipfs/") { v2 = v2[5:] } v1s, err := semver.NewVersion(v1) if err != nil { return false } v2s, err := semver.NewVersion(v2) if err != nil { return false } return v1s.Major == v2s.Major && v1s.Minor == v2s.Minor }