// ParseImageTag parses the docker image tag string and returns a validated Version or error func ParseImageTag(s string) (CombinedVersion, error) { // try parse as a SEMVER prefix := "" version, err := semver.Parse(s) if err == nil { return CombinedVersion{Prefix: prefix, Version: version}, nil } // accept format such as `vSEMVER` if vIndex := strings.IndexRune(s, 'v'); vIndex == 0 { prefix = s[:vIndex+1] version, err = semver.Parse(s[vIndex+1:]) if err == nil { return CombinedVersion{Prefix: prefix, Version: version}, nil } } // accept format such as `ANY-SEMVER` if firstHyphenIndex := strings.IndexRune(s, '-'); firstHyphenIndex != -1 { prefix = s[:firstHyphenIndex+1] version, err = semver.Parse(s[firstHyphenIndex+1:]) if err == nil { return CombinedVersion{Prefix: prefix, Version: version}, nil } } return CombinedVersion{}, err }
// UnmarshalJSON implements json.Unmarshaler and postprocesses of ProducerTopics and VersionObj func (p *Producer) UnmarshalJSON(b []byte) error { var r struct { RemoteAddress string `json:"remote_address"` Hostname string `json:"hostname"` BroadcastAddress string `json:"broadcast_address"` TCPPort int `json:"tcp_port"` HTTPPort int `json:"http_port"` Version string `json:"version"` Topics []string `json:"topics"` Tombstoned []bool `json:"tombstones"` } if err := json.Unmarshal(b, &r); err != nil { return err } *p = Producer{ RemoteAddress: r.RemoteAddress, Hostname: r.Hostname, BroadcastAddress: r.BroadcastAddress, TCPPort: r.TCPPort, HTTPPort: r.HTTPPort, Version: r.Version, } for i, t := range r.Topics { p.Topics = append(p.Topics, ProducerTopic{Topic: t, Tombstoned: r.Tombstoned[i]}) } version, err := semver.Parse(p.Version) if err != nil { version, _ = semver.Parse("0.0.0") } p.VersionObj = version return nil }
// ToValidSemver attempts to return the input as valid semver. // If the input fails to parse as semver, it appends .0 or .0.0 to the input and retries // If this is still not valid semver, it returns an error func (s SemverConverter) ToValidSemver(input string) (semver.Version, error) { v, err := semver.Parse(input) if err == nil { return v, nil } s.logger.Info(fmt.Sprintf( "failed to parse semver: '%s', appending zeros and trying again", input, )) maybeSemver := input segs := strings.SplitN(maybeSemver, ".", 3) switch len(segs) { case 2: maybeSemver += ".0" case 1: maybeSemver += ".0.0" } v, err = semver.Parse(maybeSemver) if err == nil { return v, nil } s.logger.Info(fmt.Sprintf( "still failed to parse semver: '%s', giving up", maybeSemver, )) return semver.Version{}, err }
// PrioritizeTags orders a set of image tags with a few conventions: // // 1. the "latest" tag, if present, should be first // 2. any tags that represent a semantic major version ("5", "v5") should be next, in descending order // 3. any tags that represent a semantic minor version ("5.1", "v5.1") should be next, in descending order // 4. any tags that represent a full semantic version ("5.1.3-other", "v5.1.3-other") should be next, in descending order // 5. any remaining tags should be sorted in lexicographic order // // The method updates the tags in place. func PrioritizeTags(tags []string) { remaining := tags finalTags := make([]string, 0, len(tags)) for i, tag := range tags { if tag == DefaultImageTag { tags[0], tags[i] = tags[i], tags[0] finalTags = append(finalTags, tags[0]) remaining = tags[1:] break } } exact := make(map[string]string) var major, minor, micro semver.Versions other := make([]string, 0, len(remaining)) for _, tag := range remaining { short := strings.TrimLeft(tag, "v") v, err := semver.Parse(short) switch { case err == nil: exact[v.String()] = tag micro = append(micro, v) continue case reMajorSemantic.MatchString(short): if v, err = semver.Parse(short + ".0.0"); err == nil { exact[v.String()] = tag major = append(major, v) continue } case reMinorSemantic.MatchString(short): if v, err = semver.Parse(short + ".0"); err == nil { exact[v.String()] = tag minor = append(minor, v) continue } } other = append(other, tag) } sort.Sort(sort.Reverse(major)) sort.Sort(sort.Reverse(minor)) sort.Sort(sort.Reverse(micro)) sort.Sort(sort.StringSlice(other)) for _, v := range major { finalTags = append(finalTags, exact[v.String()]) } for _, v := range minor { finalTags = append(finalTags, exact[v.String()]) } for _, v := range micro { finalTags = append(finalTags, exact[v.String()]) } for _, v := range other { finalTags = append(finalTags, v) } copy(tags, finalTags) }
func (r releases) Less(i, j int) bool { v1, err := semver.Parse(r[i].Version) if err != nil { panic(err) } v2, err := semver.Parse(r[j].Version) if err != nil { writeLn(r[j].Version) panic(err) } return v1.LT(v2) }
// Version returns the Docker version and whether it is a Red Hat distro version func (h *Helper) Version() (*semver.Version, bool, error) { glog.V(5).Infof("Retrieving Docker version") env, err := h.client.Version() if err != nil { glog.V(2).Infof("Error retrieving version: %v", err) return nil, false, err } glog.V(5).Infof("Docker version results: %#v", env) versionStr := env.Get("Version") if len(versionStr) == 0 { return nil, false, errors.New("did not get a version") } glog.V(5).Infof("Version: %s", versionStr) dockerVersion, err := semver.Parse(versionStr) if err != nil { glog.V(2).Infof("Error parsing Docker version %q", versionStr) return nil, false, err } isRedHat := false packageVersion := env.Get("PkgVersion") if len(packageVersion) > 0 { isRedHat = fedoraPackage.MatchString(packageVersion) || rhelPackage.MatchString(packageVersion) } return &dockerVersion, isRedHat, nil }
func FromSource(source models.Source) (Driver, error) { var initialVersion semver.Version if source.InitialVersion != "" { version, err := semver.Parse(source.InitialVersion) if err != nil { return nil, fmt.Errorf("invalid initial version (%s): %s", source.InitialVersion, err) } initialVersion = version } else { initialVersion = semver.Version{Major: 0, Minor: 0, Patch: 0} } switch source.Driver { case models.DriverUnspecified, models.DriverS3: auth := aws.Auth{ AccessKey: source.AccessKeyID, SecretKey: source.SecretAccessKey, } regionName := source.RegionName if len(regionName) == 0 { regionName = aws.USEast.Name } region, ok := aws.Regions[regionName] if !ok { return nil, errors.New(fmt.Sprintf("no such region '%s'", regionName)) } if len(source.Endpoint) != 0 { region = aws.Region{ S3Endpoint: fmt.Sprintf("https://%s", source.Endpoint), } } client := s3.New(auth, region) bucket := client.Bucket(source.Bucket) return &S3Driver{ InitialVersion: initialVersion, Bucket: bucket, Key: source.Key, }, nil case models.DriverGit: return &GitDriver{ InitialVersion: initialVersion, URI: source.URI, Branch: source.Branch, PrivateKey: source.PrivateKey, File: source.File, }, nil default: return nil, fmt.Errorf("unknown driver: %s", source.Driver) } }
func checkSchema() { var count int defer writeConstants() db.DB.Table("constants").Where("schema_version = ?", schemaVersion.String()).Count(&count) if count == 1 { return } currStr := getCurrConstants().SchemaVersion if currStr == "" { db.DB.Save(&Constant{ schemaVersion.String(), }) //Initial database migration whitelist_id_string() //Write current schema version return } if v, _ := semver.Parse(currStr); v.Major < schemaVersion.Major { logrus.Warningf("Incompatible schema change detected (%s), attempting to migrate to (%s).", currStr, schemaVersion.String()) for i := v.Major + 1; i <= schemaVersion.Major; i++ { logrus.Debugf("Calling migration routine for %d.0.0", i) f := migrationRoutines[i] f() } } }
func getLatestVersion(zipFile []byte, prefix, suffix string) (string, error) { r, err := zip.NewReader(bytes.NewReader(zipFile), int64(len(zipFile))) if err != nil { return "", err } latest := "" for _, f := range r.File { artifact := path.Base(f.Name) if strings.HasPrefix(artifact, prefix) && strings.HasSuffix(artifact, suffix) { v := extractVersion(artifact) if latest == "" { latest = v } v1, err := semver.Parse(v) if err != nil { return "", err } l := semver.MustParse(latest) if v1.GT(l) { latest = v } } } return latest, nil }
func mustParse(s string) semver.Version { v, err := semver.Parse(s) if err != nil { panic(`semver: Parse(` + s + `): ` + err.Error()) } return v }
// bumpedVersion returns new bumped-up version according to given spec. func (conf Config) bumpedVersion(version string) (string, error) { if conf.Exact != "" { exact, err := semver.New(conf.Exact) if err != nil { return "", err } return exact.String(), nil } v, err := semver.Parse(version) if err != nil { return "", err } if conf.MajorDelta > 0 { v.Major = v.Major + conf.MajorDelta v.Minor = 0 v.Patch = 0 } else if conf.MinorDelta > 0 { v.Minor = v.Minor + conf.MinorDelta v.Patch = 0 } else if conf.PatchDelta > 0 { v.Patch = v.Patch + conf.PatchDelta } return v.String(), nil }
func Parse(versionString string) (*Version, error) { v, err := semver.Parse(versionString) if err != nil { return nil, err } return &Version{v}, nil }
// Parse will try to parse the data and determine which specific version of schema to further parse. func (parser YAMLParser) Parse(data []byte) (common.OpenControl, error) { if data == nil || len(data) == 0 { return nil, common.ErrNoDataToParse } b := Base{} err := yaml.Unmarshal(data, &b) if err != nil { return nil, errors.New(ErrMalformedBaseYamlPrefix + " - " + err.Error()) } var opencontrol common.OpenControl var parseError error v, err := semver.Parse(b.SchemaVersion) if err != nil { return nil, common.ErrCantParseSemver } switch { case SchemaV1_0_0.Equals(v): opencontrol = new(v1_0_0.OpenControl) parseError = yaml.Unmarshal(data, opencontrol) default: return nil, common.ErrUnknownSchemaVersion } if parseError != nil { return nil, parseError } return opencontrol, nil }
// GetLookupdProducers returns a ProducerList metadata for each node connected to the given lookupds func (c *ClusterInfo) GetLookupdProducers(lookupdHTTPAddrs []string) (ProducerList, error) { var success bool var output []*Producer var lock sync.Mutex var wg sync.WaitGroup allProducers := make(map[string]*Producer) maxVersion, _ := semver.Parse("0.0.0") type respType struct { Producers []*Producer `json:"producers"` } for _, addr := range lookupdHTTPAddrs { wg.Add(1) endpoint := fmt.Sprintf("http://%s/nodes", addr) c.logf("LOOKUPD: querying %s", endpoint) go func(addr string, endpoint string) { defer wg.Done() var resp respType err := http_api.NegotiateV1(endpoint, &resp) if err != nil { c.logf("ERROR: lookupd %s - %s", endpoint, err) return } lock.Lock() defer lock.Unlock() success = true for _, producer := range resp.Producers { key := producer.TCPAddress() p, ok := allProducers[key] if !ok { if maxVersion.LT(producer.VersionObj) { maxVersion = producer.VersionObj } sort.Sort(producer.Topics) p = producer allProducers[key] = p output = append(output, p) } p.RemoteAddresses = append(p.RemoteAddresses, fmt.Sprintf("%s/%s", addr, producer.Address())) } }(addr, endpoint) } wg.Wait() if success == false { return nil, errors.New("unable to query any lookupd") } for _, producer := range allProducers { if producer.VersionObj.LT(maxVersion) { producer.OutOfDate = true } } sort.Sort(ProducersByHost{output}) return output, nil }
// Parse will try to parse the data and determine which specific version of schema to further parse. func Parse(parser common.SchemaParser, data []byte) (common.BaseSchema, error) { if data == nil || len(data) == 0 { return nil, ErrNoDataToParse } base := common.Base{} err := yaml.Unmarshal(data, &base) if err != nil { return nil, errors.New(ErrMalformedBaseYamlPrefix + " - " + err.Error()) } var schema common.BaseSchema var parseError error v, err := semver.Parse(base.SchemaVersion) if err != nil { return nil, ErrCantParseSemver } switch { case SchemaV1_0_0.Equals(v): schema, parseError = parser.ParseV1_0_0(data) default: return nil, ErrUnknownSchemaVersion } if parseError != nil { return nil, parseError } return schema, nil }
func setInitDynamicDefaults(cfg *kubeadmapi.MasterConfiguration) error { // Auto-detect the IP if len(cfg.API.AdvertiseAddresses) == 0 { ip, err := netutil.ChooseHostInterface() if err != nil { return err } cfg.API.AdvertiseAddresses = []string{ip.String()} } // Validate version argument ver, err := kubeadmutil.KubernetesReleaseVersion(cfg.KubernetesVersion) if err != nil { if cfg.KubernetesVersion != kubeadmapiext.DefaultKubernetesVersion { return err } else { ver = kubeadmapiext.DefaultKubernetesFallbackVersion } } cfg.KubernetesVersion = ver fmt.Println("[init] Using Kubernetes version:", ver) // Omit the "v" in the beginning, otherwise semver will fail // If the version is newer than the specified version, RBAC v1beta1 support is enabled in the apiserver so we can default to RBAC k8sVersion, err := semver.Parse(cfg.KubernetesVersion[1:]) if k8sVersion.GT(allowAllMaxVersion) { cfg.AuthorizationMode = "RBAC" } fmt.Println("[init] Using Authorization mode:", cfg.AuthorizationMode) // Warn about the limitations with the current cloudprovider solution. if cfg.CloudProvider != "" { fmt.Println("[init] WARNING: For cloudprovider integrations to work --cloud-provider must be set for all kubelets in the cluster.") fmt.Println("\t(/etc/systemd/system/kubelet.service.d/10-kubeadm.conf should be edited for this purpose)") } // Validate token if any, otherwise generate if cfg.Discovery.Token != nil { if cfg.Discovery.Token.ID != "" && cfg.Discovery.Token.Secret != "" { fmt.Printf("[init] A token has been provided, validating [%s]\n", kubeadmutil.BearerToken(cfg.Discovery.Token)) if valid, err := kubeadmutil.ValidateToken(cfg.Discovery.Token); valid == false { return err } } else { fmt.Println("[init] A token has not been provided, generating one") if err := kubeadmutil.GenerateToken(cfg.Discovery.Token); err != nil { return err } } // If there aren't any addresses specified, default to the first advertised address which can be user-provided or the default network interface's IP address if len(cfg.Discovery.Token.Addresses) == 0 { cfg.Discovery.Token.Addresses = []string{cfg.API.AdvertiseAddresses[0] + ":" + strconv.Itoa(kubeadmapiext.DefaultDiscoveryBindPort)} } } return nil }
func Parse(value string) Version { semver, err := semver.Parse(value) if err != nil { return Version{Alias: value} } return Version{SemVer: semver} }
func (cfg *Config) isNewerVersion(newer string) bool { nv, err := semver.Parse(newer) if err != nil { log.Errorf("Bad version string on update: %v", err) return false } return nv.GT(cfg.version) }
// UnmarshalYAML is a overridden implementation of YAML parsing the component.yaml // This method is similar to the one found here: http://choly.ca/post/go-json-marshalling/ // This is necessary because we want to have backwards compatibility with parsing the old types of version 2.0 // (type =float). // To compensate for that, we have to hand roll our own UnmarshalYAML that can decide what to do for parsing // the older version of type float and converting it into semver. In addition, we will use this logic to parse strings // into semver. func (b *Base) UnmarshalYAML(unmarshal func(v interface{}) error) error { // When we call "unmarshal" callback on an object, it will call that object's "UnmarshalYAML" if defined. // Since we are currently in the implementation of Component's "UnmarshalYAML", when finally we call // unmarshal again, if it's on type Component, we would end up in a recursive infinite loop. // To prevent this, we create a separate type, called Alias. type Alias Base // Create an anonymous struct with an interface{} type for the schema_version that we want to parse aux := &struct { SchemaVersion interface{} `yaml:"schema_version" json:"schema_version"` Alias `yaml:",inline"` }{ Alias: (Alias)(*b), } // Call unmarshal on the new Alias type. Don't return the error yet because we want to gather more information // if we can below. err := unmarshal(&aux) // Create a placeholder variable for the converted semver. var ver semver.Version // Create a placeholder variable for the error. var versionErr error // Store the version value for conciseness. value := aux.SchemaVersion // Try to cast the value from interface{} to certain types. switch v := value.(type) { // For float types, which are the old types, we need to upcast it to semver if it's an older version. case float32, float64: switch v { // Schema Version started being documented with "2.0". // We should be able to parse it for backwards compatibility. // All future versioning should be in semver format already. case 2.0: ver = semver.MustParse("2.0.0") // If not the older version, it needs to be in semver format, send an error. default: return BaseComponentParseError{fmt.Sprintf("Version %v is not in semver format", v)} } // The interface type will default to string if not numeric which is what all semver types will be initially. case string: ver, versionErr = semver.Parse(v) if versionErr != nil { return BaseComponentParseError{constants.ErrMissingVersion} } // In the case, it's just missing completely. default: return BaseComponentParseError{constants.ErrMissingVersion} } // Copy everything from the Alias back to the original component. *b = (Base)(aux.Alias) // Get the version b.SchemaVersion = ver return err }
func getAPIServerCommand(cfg *kubeadmapi.MasterConfiguration) []string { command := append(getComponentBaseCommand(apiServer), "--insecure-bind-address=127.0.0.1", "--admission-control=NamespaceLifecycle,LimitRanger,ServiceAccount,PersistentVolumeLabel,DefaultStorageClass,ResourceQuota", "--service-cluster-ip-range="+cfg.Networking.ServiceSubnet, "--service-account-key-file="+kubeadmapi.GlobalEnvParams.HostPKIPath+"/apiserver-key.pem", "--client-ca-file="+kubeadmapi.GlobalEnvParams.HostPKIPath+"/ca.pem", "--tls-cert-file="+kubeadmapi.GlobalEnvParams.HostPKIPath+"/apiserver.pem", "--tls-private-key-file="+kubeadmapi.GlobalEnvParams.HostPKIPath+"/apiserver-key.pem", "--token-auth-file="+kubeadmapi.GlobalEnvParams.HostPKIPath+"/tokens.csv", fmt.Sprintf("--secure-port=%d", cfg.API.BindPort), "--allow-privileged", ) // Use first address we are given if len(cfg.API.AdvertiseAddresses) > 0 { command = append(command, fmt.Sprintf("--advertise-address=%s", cfg.API.AdvertiseAddresses[0])) } if len(cfg.KubernetesVersion) != 0 { // If the k8s version is v1.5-something, this argument is set and makes `kubectl logs` and `kubectl exec` // work on bare-metal where hostnames aren't usually resolvable // Omit the "v" in the beginning, otherwise semver will fail k8sVersion, err := semver.Parse(cfg.KubernetesVersion[1:]) if err == nil && k8sVersion.GTE(preferredAddressMinimumVersion) { command = append(command, "--kubelet-preferred-address-types=InternalIP,ExternalIP,Hostname") } } // Check if the user decided to use an external etcd cluster if len(cfg.Etcd.Endpoints) > 0 { command = append(command, fmt.Sprintf("--etcd-servers=%s", strings.Join(cfg.Etcd.Endpoints, ","))) } else { command = append(command, "--etcd-servers=http://127.0.0.1:2379") } // Is etcd secured? if cfg.Etcd.CAFile != "" { command = append(command, fmt.Sprintf("--etcd-cafile=%s", cfg.Etcd.CAFile)) } if cfg.Etcd.CertFile != "" && cfg.Etcd.KeyFile != "" { etcdClientFileArg := fmt.Sprintf("--etcd-certfile=%s", cfg.Etcd.CertFile) etcdKeyFileArg := fmt.Sprintf("--etcd-keyfile=%s", cfg.Etcd.KeyFile) command = append(command, etcdClientFileArg, etcdKeyFileArg) } if cfg.CloudProvider != "" { command = append(command, "--cloud-provider="+cfg.CloudProvider) // Only append the --cloud-config option if there's a such file if _, err := os.Stat(DefaultCloudConfigPath); err == nil { command = append(command, "--cloud-config="+DefaultCloudConfigPath) } } return command }
// useDiscoveryRESTMapper checks the server version to see if its recent enough to have // enough discovery information avaiable to reliably build a RESTMapper. If not, use the // hardcoded mapper in this client (legacy behavior) func useDiscoveryRESTMapper(serverVersion string) bool { serverSemVer, err := semver.Parse(serverVersion[1:]) if err != nil { return false } if serverSemVer.LT(semver.MustParse("1.3.0")) { return false } return true }
// useDiscoveryRESTMapper checks the server version to see if its recent enough to have // enough discovery information available to reliably build a RESTMapper. If not, use the // hardcoded mapper in this client (legacy behavior) func useDiscoveryRESTMapper(serverVersion string) bool { if len(serverVersion) == 0 { return false } serverSemVer, err := semver.Parse(serverVersion[1:]) if err != nil { return false } return serverSemVer.GE(semver.MustParse("1.3.0")) }
// GetVersion returns a semver.Version object by querying /info func GetVersion(addr string) (semver.Version, error) { endpoint := fmt.Sprintf("http://%s/info", addr) log.Printf("version negotiation %s", endpoint) info, err := http_api.NegotiateV1("GET", endpoint, nil) if err != nil { log.Printf("ERROR: %s - %s", endpoint, err) return semver.Version{}, err } version := info.Get("version").MustString("unknown") return semver.Parse(version) }
// Helper for getting the docker version. func getDockerVersion(cadvisor cadvisor.Interface) semver.Version { var fallback semver.Version // Fallback to zero-value by default. versions, err := cadvisor.VersionInfo() if err != nil { glog.Errorf("Error requesting cAdvisor VersionInfo: %v", err) return fallback } dockerVersion, err := semver.Parse(versions.DockerVersion) if err != nil { glog.Errorf("Error parsing docker version %q: %v", versions.DockerVersion, err) return fallback } return dockerVersion }
func checkSemVer(spec rspec.Spec, rootfs string, hostCheck bool) (msgs []string) { logrus.Debugf("check semver") version := spec.Version _, err := semver.Parse(version) if err != nil { msgs = append(msgs, fmt.Sprintf("%q is not valid SemVer: %s", version, err.Error())) } if version != rspec.Version { msgs = append(msgs, fmt.Sprintf("internal error: validate currently only handles version %s, but the supplied configuration targets %s", rspec.Version, version)) } return }
// GetVersion returns a semver.Version object by querying /info func (c *ClusterInfo) GetVersion(addr string) (semver.Version, error) { endpoint := fmt.Sprintf("http://%s/info", addr) var resp struct { Version string `json:'version'` } err := c.client.NegotiateV1(endpoint, &resp) if err != nil { return semver.Version{}, err } if resp.Version == "" { resp.Version = "unknown" } return semver.Parse(resp.Version) }
func main() { if len(os.Args) < 2 { println("usage: " + os.Args[0] + " <destination>") os.Exit(1) } destination := os.Args[1] err := os.MkdirAll(destination, 0755) if err != nil { fatal("creating destination", err) } var request models.InRequest err = json.NewDecoder(os.Stdin).Decode(&request) if err != nil { fatal("reading request", err) } inputVersion, err := semver.Parse(request.Version.Number) if err != nil { fatal("parsing semantic version", err) } bumped := version.BumpFromParams(request.Params.Bump, request.Params.Pre).Apply(inputVersion) if !bumped.Equals(inputVersion) { fmt.Fprintf(os.Stderr, "bumped locally from %s to %s\n", inputVersion, bumped) } numberFile, err := os.Create(filepath.Join(destination, "number")) if err != nil { fatal("opening number file", err) } defer numberFile.Close() _, err = fmt.Fprintf(numberFile, "%s", bumped.String()) if err != nil { fatal("writing to number file", err) } json.NewEncoder(os.Stdout).Encode(models.InResponse{ Version: request.Version, Metadata: models.Metadata{ {"number", request.Version.Number}, }, }) }
func parseTemplate(tpl *Template) { // Parse the version version, err := semver.Parse(tpl.Version) if err != nil { log.WithFields(logrus.Fields{ "id": tpl.ID, "version": tpl.Version, "error": err.Error(), }).Error("Unable to parse template's version") return } // Parse the subject template stpl, err := ttl.New(tpl.Name + "_subject").Parse(tpl.Subject) if err != nil { log.WithFields(logrus.Fields{ "id": tpl.ID, "error": err.Error(), }).Error("Unable to parse template's subject") return } tpl.SubjectTpl = stpl // Parse the body template btpl, err := htl.New(tpl.Name + "_body").Parse(tpl.Body) if err != nil { log.WithFields(logrus.Fields{ "id": tpl.ID, "error": err.Error(), }).Error("Unable to parse templates's body") return } tpl.BodyTpl = btpl // Prepare template storages if _, ok := templates[tpl.Name]; !ok { templates[tpl.Name] = map[string]*Template{} } if _, ok := templateVersions[tpl.Name]; !ok { templateVersions[tpl.Name] = semver.Versions{} } // Put it into the templates storage templates[tpl.Name][version.String()] = tpl templateVersions[tpl.Name] = append(templateVersions[tpl.Name], version) log.Printf("Loaded template %s %s", tpl.Name, tpl.Version) }
// GetVersion returns a semver.Version object by querying /info func (c *ClusterInfo) GetVersion(addr string) (semver.Version, error) { endpoint := fmt.Sprintf("http://%s/info", addr) c.logf("version negotiation %s", endpoint) var resp struct { Version string `json:'version'` } err := http_api.NegotiateV1(endpoint, &resp) if err != nil { c.logf("ERROR: %s - %s", endpoint, err) return semver.Version{}, err } if resp.Version == "" { resp.Version = "unknown" } return semver.Parse(resp.Version) }
// getReleases queries github for all product releases. func (g *ReleaseManager) getReleases() ([]Release, error) { var releases []Release for page := 1; true; page++ { opt := &github.ListOptions{Page: page} rels, _, err := g.client.Repositories.ListReleases(g.owner, g.repo, opt) if err != nil { return nil, err } if len(rels) == 0 { break } releases = make([]Release, 0, len(rels)) for i := range rels { version := *rels[i].TagName v, err := semver.Parse(version) if err != nil { log.Debugf("Release %q is not semantically versioned (%q). Skipping.", version, err) continue } rel := Release{ id: *rels[i].ID, URL: *rels[i].ZipballURL, Version: v, } rel.Assets = make([]Asset, 0, len(rels[i].Assets)) for _, asset := range rels[i].Assets { rel.Assets = append(rel.Assets, Asset{ id: *asset.ID, Name: *asset.Name, URL: *asset.BrowserDownloadURL, }) } log.Debugf("Release %q has %d assets...", version, len(rel.Assets)) releases = append(releases, rel) } } sort.Sort(sort.Reverse(releasesByID(releases))) return releases, nil }