Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fsoc 24 #145

Merged
merged 16 commits into from
Jul 17, 2023
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) {
GDW1 marked this conversation as resolved.
Show resolved Hide resolved
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, "")
}