Skip to content

Commit

Permalink
APIGOV-28932 - remove jfrog call and use agentstate (#842)
Browse files Browse the repository at this point in the history
* APIGOV-28932 - remove jfrog call and use agentstate

* MR issue

* MR issues

* use logger from agentVersionCheck struct

* MR issue
  • Loading branch information
dgghinea authored Nov 15, 2024
1 parent ee300db commit 3106f41
Showing 1 changed file with 46 additions and 199 deletions.
245 changes: 46 additions & 199 deletions pkg/cmd/agentversionjob.go
Original file line number Diff line number Diff line change
@@ -1,118 +1,47 @@
package cmd

import (
"encoding/json"
"fmt"

"github.com/Axway/agent-sdk/pkg/agent"
"github.com/Axway/agent-sdk/pkg/agent/resource"
management "github.com/Axway/agent-sdk/pkg/apic/apiserver/models/management/v1alpha1"
"github.com/Axway/agent-sdk/pkg/config"
"github.com/Axway/agent-sdk/pkg/util"

"regexp"
"strconv"
"strings"

"net/http"

coreapi "github.com/Axway/agent-sdk/pkg/api"
"github.com/Axway/agent-sdk/pkg/jobs"
"github.com/Axway/agent-sdk/pkg/util/errors"
log "github.com/Axway/agent-sdk/pkg/util/log"
)

const (
avcCronSchedule = "@daily"
jfrogURL = "https://axway.jfrog.io/ui/api/v1/ui/treebrowser"
)

var agentNameToRepoPath = map[string]string{
"AWSDiscoveryAgent": "aws-apigw-discovery-agent",
"AWSTraceabilityAgent": "aws-apigw-traceability-agent",
"AzureDiscoveryAgent": "azure-discovery-agent",
"AzureTraceabilityAgent": "azure-traceability-agent",
"EnterpriseEdgeGatewayDiscoveryAgent": "v7-discovery-agent",
"EnterpriseEdgeGatewayTraceabilityAgent": "v7-traceability-agent",
}

type version struct {
major, minor, patch int
val string
}

type jfrogRequest struct {
Type string `json:"type"`
RepoType string `json:"repoType"`
RepoKey string `json:"repoKey"`
Path string `json:"path"`
Text string `json:"text"`
}

type jfrogData struct {
Items []jfrogItem `json:"data"`
}

type jfrogItem struct {
RepoKey string `json:"repoKey,omitempty"`
Path string `json:"path,omitempty"`
Version string `json:"text"`
RepoType string `json:"repoType,omitempty"`
HasChild bool `json:"hasChild,omitempty"`
Local bool `json:"local,omitempty"`
Type string `json:"type,omitempty"`
Compacted bool `json:"compacted,omitempty"`
Cached bool `json:"cached,omitempty"`
Trash bool `json:"trash,omitempty"`
Distribution bool `json:"distribution,omitempty"`
}
agentStateCurrent = "current"
agentStateAvailable = "available"
agentStateOutdated = "outdated"
agentStateRetracted = "retracted"
)

// AgentVersionCheckJob - polls for agent versions
type AgentVersionCheckJob struct {
jobs.Job
apiClient coreapi.Client
requestBytes []byte
buildVersion string
headers map[string]string
logger log.FieldLogger
manager resource.Manager
}

// NewAgentVersionCheckJob - creates a new agent version check job structure
func NewAgentVersionCheckJob(cfg config.CentralConfig) (*AgentVersionCheckJob, error) {
// get current build version
buildVersion, err := getBuildVersion()
if err != nil {
log.Trace(err)
return nil, err
}

if _, found := agentNameToRepoPath[BuildAgentName]; !found {
err := errors.ErrStartingVersionChecker.FormatError("empty or generic data plane type name")
log.Trace(err)
return nil, err
}

// create the request body for each check
requestBody := jfrogRequest{
Type: "junction",
RepoType: "virtual",
RepoKey: "ampc-public-docker-release",
Path: "agent/" + agentNameToRepoPath[BuildAgentName],
Text: agentNameToRepoPath[BuildAgentName],
}
requestBytes, err := json.Marshal(requestBody)
if err != nil {
log.Trace(err)
return nil, err
manager := agent.GetAgentResourceManager()
if manager == nil {
return nil, errors.ErrStartingVersionChecker.FormatError("could not get the agent resource manager")
}

return &AgentVersionCheckJob{
apiClient: coreapi.NewClient(cfg.GetTLSConfig(), cfg.GetProxyURL(),
coreapi.WithTimeout(cfg.GetClientTimeout()),
coreapi.WithSingleURL()),
buildVersion: buildVersion,
requestBytes: requestBytes,
headers: map[string]string{
"X-Requested-With": "XMLHttpRequest",
"Host": "axway.jfrog.io",
"Content-Length": strconv.Itoa(len(requestBytes)),
"Content-Type": "application/json",
},
manager: manager,
logger: log.NewFieldLogger().
WithPackage("sdk.cmd").
WithComponent("agentVersionJob"),
}, nil
}

Expand All @@ -128,129 +57,47 @@ func (avj *AgentVersionCheckJob) Status() error {

// Execute - run agent version check job one time
func (avj *AgentVersionCheckJob) Execute() error {
err := avj.getJFrogVersions()
if err != nil {
log.Trace(err)
// Could not get update from jfrog. Warn that we could not determine version and continue processing
log.Warn("Agent cannot determine the next available release. Be aware that your agent could be outdated.")
} else {
// Successfully got jfrog version. Now compare build to latest version
if isVersionStringOlder(avj.buildVersion, config.AgentLatestVersion) {
log.Warnf("New version available. Please consider upgrading from version %s to version %s", avj.buildVersion, config.AgentLatestVersion)
}
}
return nil
}

// getJFrogVersions - obtaining the versions from JFrog website
// **Note** polling the jfrog website is the current solution to obtaining the list of versions
// In the future, adding a (Generic) resource for grouping versions together under the same scope is a possible solution
// ie: a new unscoped resource that represents the platform services, so that other products can plug in their releases.
func (avj *AgentVersionCheckJob) getJFrogVersions() error {
request := coreapi.Request{
Method: http.MethodPost,
URL: jfrogURL,
Headers: avj.headers,
Body: avj.requestBytes,
}
response, err := avj.apiClient.Send(request)
state, err := avj.getAgentState()
if err != nil {
return err
avj.logger.WithError(err).Warn("agent cannot determine the current available release. Be aware that your agent could be outdated.")
return nil
}

jfrogResponse := jfrogData{}
err = json.Unmarshal(response.Body, &jfrogResponse)
if err != nil {
return err
switch state {
case agentStateCurrent:
avj.logger.Trace("agent is up to date.")
case agentStateAvailable:
avj.logger.Warn("please be aware that there is a newer agent version available.")
case agentStateOutdated:
avj.logger.Error("current agent version is no longer supported. We strongly advise to update the agent as soon as possible.")
case agentStateRetracted:
avj.logger.Error("current agent version has a known issue, please update the agent immediately.")
}

config.AgentLatestVersion = avj.getLatestVersionFromJFrog(jfrogResponse.Items)
return nil
}

func getBuildVersion() (string, error) {
//remove -SHA from build version
versionNoSHA := strings.Split(BuildVersion, "-")[0]

//regex check for semantic versioning
semVerRegexp := regexp.MustCompile(`\d.\d.\d`)
if versionNoSHA == "" || !semVerRegexp.MatchString(versionNoSHA) {
return "", errors.ErrStartingVersionChecker.FormatError("build version is missing or of noncompliant semantic versioning")
func (avj *AgentVersionCheckJob) getAgentState() (string, error) {
agentRes := avj.manager.GetAgentResource()
if agentRes == nil {
return "", fmt.Errorf("could not get the agent resource")
}
return versionNoSHA, nil
}

// isVersionStringOlder - return true if version of str1 is older than str2
func isVersionStringOlder(build string, latest string) bool {
vB := getSemVer(build)
vL := getSemVer(latest)

return isVersionSmaller(vB, vL)
}

// isVersionSmaller - return true if version1 smaller than version2
func isVersionSmaller(v1 version, v2 version) bool {
if v1.major < v2.major {
return true
}
if v1.major == v2.major {
if v1.minor < v2.minor {
return true
switch agentRes.GetGroupVersionKind().Kind {
case "TraceabilityAgent":
ta := management.NewTraceabilityAgent("", "")
if err := ta.FromInstance(agentRes); err != nil {
return "", fmt.Errorf("could not convert resource instance to TraceabilityAgent resource")
}
if v1.minor == v2.minor && v1.patch < v2.patch {
return true
return ta.Agentstate.Update, nil
case "DiscoveryAgent":
da := management.NewDiscoveryAgent("", "")
if err := da.FromInstance(agentRes); err != nil {
return "", fmt.Errorf("could not convert resource instance to DiscoveryAgent resource")
}
return da.Agentstate.Update, nil
}
return false
}

func (avj *AgentVersionCheckJob) getLatestVersionFromJFrog(jfrogItems []jfrogItem) string {
tempMaxVersion := version{
major: 0,
minor: 0,
patch: 0,
val: "",
}
re := regexp.MustCompile(`\d{8}`)

for _, item := range jfrogItems {
//trimming version from jfrog webpage
if item.Version != "latest" && item.Version != "" {
v := getSemVer(item.Version)
// avoid a version with an 8 digit date as the patch number: 1.0.20210421
if !re.MatchString(strconv.Itoa(v.patch)) && isVersionSmaller(tempMaxVersion, v) {
copyVersionStruct(&tempMaxVersion, v)
}
}
}
return tempMaxVersion.val
}

// getSemVer - getting a semantic version struct from version string
// pre-req is that string is already in semantic versioning with major, minor, and patch
func getSemVer(str string) version {
s := strings.Split(str, ".")
maj, err := strconv.Atoi(s[0])
min, err2 := strconv.Atoi(s[1])
pat, err3 := strconv.Atoi(s[2])
if err == nil && err2 == nil && err3 == nil {
v := version{
major: maj,
minor: min,
patch: pat,
val: str,
}
return v
}
return version{}
}

// copyVersionStruct - copying version2 into version1 struct by value
func copyVersionStruct(v1 *version, v2 version) {
v1.major = v2.major
v1.minor = v2.minor
v1.patch = v2.patch
v1.val = v2.val
return "", fmt.Errorf("agent resource is neither Discovery nor Traceability")
}

// startVersionCheckJobs - starts both a single run and continuous checks
Expand Down

0 comments on commit 3106f41

Please sign in to comment.