Skip to content

Commit

Permalink
Added utility version check (FSOC-24) (#145)
Browse files Browse the repository at this point in the history
---------

Co-authored-by: GDW1 <guwilks@cisco.com>
  • Loading branch information
Guy Wilks and GDW1 authored Jul 17, 2023
1 parent 6523417 commit b177e7f
Show file tree
Hide file tree
Showing 2 changed files with 118 additions and 2 deletions.
51 changes: 49 additions & 2 deletions cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,10 @@ import (
"fmt"
"os"
"path"
"strconv"
"time"

"github.com/Masterminds/semver/v3"
"github.com/apex/log"
"github.com/apex/log/handlers/json"
"github.com/apex/log/handlers/multi"
Expand All @@ -39,10 +42,18 @@ var cfgProfile string
var outputFormat string

const (
FSOC_CONFIG_ENVVAR = "FSOC_CONFIG"
FSOC_PROFILE_ENVVAR = "FSOC_PROFILE"
FSOC_CONFIG_ENVVAR = "FSOC_CONFIG"
FSOC_PROFILE_ENVVAR = "FSOC_PROFILE"
FSOC_NO_VERSION_CHECK = "FSOC_NO_VERSION_CHECK"
)

const (
secondInDay = time.Hour * 24
timestampFileName = "fsoc.timestamp"
)

var updateChannel chan *semver.Version

// rootCmd represents the base command when called without any subcommands
// TODO: replace github link "for more info" with Cisco DevNet link for fsoc once published
var rootCmd = &cobra.Command{
Expand All @@ -69,6 +80,7 @@ For more information, see https://github.com/cisco-open/fsoc
NOTE: fsoc is in alpha; breaking changes may occur`,
PersistentPreRun: preExecHook,
PersistentPostRun: postExecHook,
TraverseChildren: true,
DisableAutoGenTag: true,
}
Expand All @@ -93,6 +105,7 @@ func init() {
rootCmd.PersistentFlags().BoolP("verbose", "v", false, "Enable detailed output")
rootCmd.PersistentFlags().Bool("curl", false, "Log curl equivalent for platform API calls (implies --verbose)")
rootCmd.PersistentFlags().String("log", path.Join(os.TempDir(), "fsoc.log"), "determines the location of the fsoc log file")
rootCmd.PersistentFlags().Bool("no-version-check", false, "Removes daily check for new versions of FSOC")
rootCmd.SetOut(os.Stdout)
rootCmd.SetErr(os.Stderr)
rootCmd.SetIn(os.Stdin)
Expand Down Expand Up @@ -222,6 +235,40 @@ func preExecHook(cmd *cobra.Command, args []string) {
log.Fatalf("fsoc is not configured, please use \"fsoc config set\" to configure an initial context")
}
}

// Do version checking
noVerCheck, _ := cmd.Flags().GetBool("no-version-check")
envNoVerCheck, err := strconv.ParseBool(os.Getenv(FSOC_NO_VERSION_CHECK))
if err != nil {
envNoVerCheck = false
}
noVerCheck = noVerCheck || envNoVerCheck
updateChecked := int(time.Now().Unix())-getLastVersionCheckTime() > int(secondInDay) && !noVerCheck
if updateChecked {
updateChannel = make(chan *semver.Version)
go version.CheckForUpdate(updateChannel)
}
// Create new timestamp file
_ = os.Remove(os.TempDir() + timestampFileName)
_, err = os.Create(os.TempDir() + timestampFileName)
if err != nil {
log.Errorf("failed to create version check timestamp file: %v", err.Error())
}
}

func getLastVersionCheckTime() int {
fInfo, err := os.Stat(os.TempDir() + timestampFileName)
if err != nil {
return 0
}
return int(fInfo.ModTime().Unix())
}

func postExecHook(cmd *cobra.Command, args []string) {
if updateChannel != nil {
var updateSemVar = <-updateChannel
version.CompareAndLogVersions(updateSemVar)
}
}

func bypassConfig(cmd *cobra.Command) bool {
Expand Down
69 changes: 69 additions & 0 deletions cmd/version/version_check.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
package version

import (
"fmt"
"net/http"
"strings"

"github.com/Masterminds/semver/v3"
"github.com/apex/log"
)

const (
versionLatestURL = "https://github.com/cisco-open/fsoc/releases/latest"
)

func GetLatestVersion() (string, error) {
// Open HTTP client which will not follow redirect
client := &http.Client{
CheckRedirect: func(req *http.Request, via []*http.Request) error { // no redirect
return http.ErrUseLastResponse
},
}
resp, err := client.Get(versionLatestURL)
if err != nil {
return "", err
}
// Redirected link should look like https://github.com/cisco-open/fsoc/releases/tag/{VERSION}
split := strings.Split(resp.Header.Get("Location"), "/")
if len(split) < 1 {
return "", fmt.Errorf("version request did not return a version")
}
return split[len(split)-1], nil
}

func CheckForUpdate(versionChannel chan *semver.Version) {
log.Infof("Checking for newer version of FSOC")
newestVersion, err := GetLatestVersion()
log.WithField("Latest FSOC version", newestVersion).Infof("Latest fsoc version available: %s", newestVersion)
if err != nil {
log.Warnf(err.Error())
}
newestVersionSemVar, err := semver.NewVersion(newestVersion)
if err != nil {
log.WithField("unparseable version", newestVersion).Warnf("Could not parse version string: %v", err.Error())
}
versionChannel <- newestVersionSemVar
}

func CompareAndLogVersions(newestVersionSemVar *semver.Version) {
currentVersion := GetVersion()
currentVersionSemVer := ConvertVerToSemVar(currentVersion)
newerVersionAvailable := currentVersionSemVer.Compare(newestVersionSemVar) == -1
var debugFields = log.Fields{"IsLatestVersionDifferent": newerVersionAvailable, "CurrentVersion": currentVersionSemVer.String(), "LatestVersion": newestVersionSemVar.String()}

if newerVersionAvailable {
log.WithFields(debugFields).Warnf("There is a newer version of FSOC available, please upgrade from version")
} else {
log.WithFields(debugFields)
}

}

func ConvertVerToSemVar(data VersionData) *semver.Version {
return semver.New(
uint64(data.VersionMajor),
uint64(data.VersionMajor),
uint64(data.VersionMajor),
data.VersionMeta, "")
}

0 comments on commit b177e7f

Please sign in to comment.