github.com/Axway/agent-sdk@v1.1.101/pkg/cmd/agentversionjob.go (about) 1 package cmd 2 3 import ( 4 "encoding/json" 5 6 "github.com/Axway/agent-sdk/pkg/config" 7 "github.com/Axway/agent-sdk/pkg/util" 8 9 "regexp" 10 "strconv" 11 "strings" 12 13 "net/http" 14 15 coreapi "github.com/Axway/agent-sdk/pkg/api" 16 "github.com/Axway/agent-sdk/pkg/jobs" 17 "github.com/Axway/agent-sdk/pkg/util/errors" 18 log "github.com/Axway/agent-sdk/pkg/util/log" 19 ) 20 21 const ( 22 avcCronSchedule = "@daily" 23 jfrogURL = "https://axway.jfrog.io/ui/api/v1/ui/treebrowser" 24 ) 25 26 var agentNameToRepoPath = map[string]string{ 27 "AWSDiscoveryAgent": "aws-apigw-discovery-agent", 28 "AWSTraceabilityAgent": "aws-apigw-traceability-agent", 29 "AzureDiscoveryAgent": "azure-discovery-agent", 30 "AzureTraceabilityAgent": "azure-traceability-agent", 31 "EnterpriseEdgeGatewayDiscoveryAgent": "v7-discovery-agent", 32 "EnterpriseEdgeGatewayTraceabilityAgent": "v7-traceability-agent", 33 } 34 35 type version struct { 36 major, minor, patch int 37 val string 38 } 39 40 type jfrogRequest struct { 41 Type string `json:"type"` 42 RepoType string `json:"repoType"` 43 RepoKey string `json:"repoKey"` 44 Path string `json:"path"` 45 Text string `json:"text"` 46 } 47 48 type jfrogData struct { 49 Items []jfrogItem `json:"data"` 50 } 51 52 type jfrogItem struct { 53 RepoKey string `json:"repoKey,omitempty"` 54 Path string `json:"path,omitempty"` 55 Version string `json:"text"` 56 RepoType string `json:"repoType,omitempty"` 57 HasChild bool `json:"hasChild,omitempty"` 58 Local bool `json:"local,omitempty"` 59 Type string `json:"type,omitempty"` 60 Compacted bool `json:"compacted,omitempty"` 61 Cached bool `json:"cached,omitempty"` 62 Trash bool `json:"trash,omitempty"` 63 Distribution bool `json:"distribution,omitempty"` 64 } 65 66 // AgentVersionCheckJob - polls for agent versions 67 type AgentVersionCheckJob struct { 68 jobs.Job 69 apiClient coreapi.Client 70 requestBytes []byte 71 buildVersion string 72 headers map[string]string 73 } 74 75 // NewAgentVersionCheckJob - creates a new agent version check job structure 76 func NewAgentVersionCheckJob(cfg config.CentralConfig) (*AgentVersionCheckJob, error) { 77 // get current build version 78 buildVersion, err := getBuildVersion() 79 if err != nil { 80 log.Trace(err) 81 return nil, err 82 } 83 84 if _, found := agentNameToRepoPath[BuildAgentName]; !found { 85 err := errors.ErrStartingVersionChecker.FormatError("empty or generic data plane type name") 86 log.Trace(err) 87 return nil, err 88 } 89 90 // create the request body for each check 91 requestBody := jfrogRequest{ 92 Type: "junction", 93 RepoType: "virtual", 94 RepoKey: "ampc-public-docker-release", 95 Path: "agent/" + agentNameToRepoPath[BuildAgentName], 96 Text: agentNameToRepoPath[BuildAgentName], 97 } 98 requestBytes, err := json.Marshal(requestBody) 99 if err != nil { 100 log.Trace(err) 101 return nil, err 102 } 103 104 return &AgentVersionCheckJob{ 105 apiClient: coreapi.NewClient(cfg.GetTLSConfig(), cfg.GetProxyURL(), 106 coreapi.WithTimeout(cfg.GetClientTimeout()), 107 coreapi.WithSingleURL()), 108 buildVersion: buildVersion, 109 requestBytes: requestBytes, 110 headers: map[string]string{ 111 "X-Requested-With": "XMLHttpRequest", 112 "Host": "axway.jfrog.io", 113 "Content-Length": strconv.Itoa(len(requestBytes)), 114 "Content-Type": "application/json", 115 }, 116 }, nil 117 } 118 119 // Ready - 120 func (avj *AgentVersionCheckJob) Ready() bool { 121 return true 122 } 123 124 // Status - 125 func (avj *AgentVersionCheckJob) Status() error { 126 return nil 127 } 128 129 // Execute - run agent version check job one time 130 func (avj *AgentVersionCheckJob) Execute() error { 131 err := avj.getJFrogVersions() 132 if err != nil { 133 log.Trace(err) 134 // Could not get update from jfrog. Warn that we could not determine version and continue processing 135 log.Warn("Agent cannot determine the next available release. Be aware that your agent could be outdated.") 136 } else { 137 // Successfully got jfrog version. Now compare build to latest version 138 if isVersionStringOlder(avj.buildVersion, config.AgentLatestVersion) { 139 log.Warnf("New version available. Please consider upgrading from version %s to version %s", avj.buildVersion, config.AgentLatestVersion) 140 } 141 } 142 return nil 143 } 144 145 // getJFrogVersions - obtaining the versions from JFrog website 146 // **Note** polling the jfrog website is the current solution to obtaining the list of versions 147 // In the future, adding a (Generic) resource for grouping versions together under the same scope is a possible solution 148 // ie: a new unscoped resource that represents the platform services, so that other products can plug in their releases. 149 func (avj *AgentVersionCheckJob) getJFrogVersions() error { 150 request := coreapi.Request{ 151 Method: http.MethodPost, 152 URL: jfrogURL, 153 Headers: avj.headers, 154 Body: avj.requestBytes, 155 } 156 response, err := avj.apiClient.Send(request) 157 if err != nil { 158 return err 159 } 160 161 jfrogResponse := jfrogData{} 162 err = json.Unmarshal(response.Body, &jfrogResponse) 163 if err != nil { 164 return err 165 } 166 167 config.AgentLatestVersion = avj.getLatestVersionFromJFrog(jfrogResponse.Items) 168 return nil 169 } 170 171 func getBuildVersion() (string, error) { 172 //remove -SHA from build version 173 versionNoSHA := strings.Split(BuildVersion, "-")[0] 174 175 //regex check for semantic versioning 176 semVerRegexp := regexp.MustCompile(`\d.\d.\d`) 177 if versionNoSHA == "" || !semVerRegexp.MatchString(versionNoSHA) { 178 return "", errors.ErrStartingVersionChecker.FormatError("build version is missing or of noncompliant semantic versioning") 179 } 180 return versionNoSHA, nil 181 } 182 183 // isVersionStringOlder - return true if version of str1 is older than str2 184 func isVersionStringOlder(build string, latest string) bool { 185 vB := getSemVer(build) 186 vL := getSemVer(latest) 187 188 return isVersionSmaller(vB, vL) 189 } 190 191 // isVersionSmaller - return true if version1 smaller than version2 192 func isVersionSmaller(v1 version, v2 version) bool { 193 if v1.major < v2.major { 194 return true 195 } 196 if v1.major == v2.major { 197 if v1.minor < v2.minor { 198 return true 199 } 200 if v1.minor == v2.minor && v1.patch < v2.patch { 201 return true 202 } 203 } 204 return false 205 } 206 207 func (avj *AgentVersionCheckJob) getLatestVersionFromJFrog(jfrogItems []jfrogItem) string { 208 tempMaxVersion := version{ 209 major: 0, 210 minor: 0, 211 patch: 0, 212 val: "", 213 } 214 re := regexp.MustCompile(`\d{8}`) 215 216 for _, item := range jfrogItems { 217 //trimming version from jfrog webpage 218 if item.Version != "latest" && item.Version != "" { 219 v := getSemVer(item.Version) 220 // avoid a version with an 8 digit date as the patch number: 1.0.20210421 221 if !re.MatchString(strconv.Itoa(v.patch)) && isVersionSmaller(tempMaxVersion, v) { 222 copyVersionStruct(&tempMaxVersion, v) 223 } 224 } 225 } 226 return tempMaxVersion.val 227 } 228 229 // getSemVer - getting a semantic version struct from version string 230 // pre-req is that string is already in semantic versioning with major, minor, and patch 231 func getSemVer(str string) version { 232 s := strings.Split(str, ".") 233 maj, err := strconv.Atoi(s[0]) 234 min, err2 := strconv.Atoi(s[1]) 235 pat, err3 := strconv.Atoi(s[2]) 236 if err == nil && err2 == nil && err3 == nil { 237 v := version{ 238 major: maj, 239 minor: min, 240 patch: pat, 241 val: str, 242 } 243 return v 244 } 245 return version{} 246 } 247 248 // copyVersionStruct - copying version2 into version1 struct by value 249 func copyVersionStruct(v1 *version, v2 version) { 250 v1.major = v2.major 251 v1.minor = v2.minor 252 v1.patch = v2.patch 253 v1.val = v2.val 254 } 255 256 // startVersionCheckJobs - starts both a single run and continuous checks 257 func startVersionCheckJobs(cfg config.CentralConfig, agentFeaturesCfg config.AgentFeaturesConfig) { 258 if !util.IsNotTest() || !agentFeaturesCfg.VersionCheckerEnabled() { 259 return 260 } 261 // register the agent version checker single run job 262 checkJob, err := NewAgentVersionCheckJob(cfg) 263 if err != nil { 264 log.Errorf("could not create the agent version checker: %v", err.Error()) 265 return 266 } 267 if id, err := jobs.RegisterSingleRunJobWithName(checkJob, "Version Check"); err == nil { 268 log.Tracef("registered agent version checker job: %s", id) 269 } 270 if id, err := jobs.RegisterScheduledJobWithName(checkJob, avcCronSchedule, "Version Check Schedule"); err == nil { 271 log.Tracef("registered agent version checker cronjob: %s", id) 272 } 273 }