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

APIGOV-28932 - remove jfrog call and use agentstate #842

Open
wants to merge 5 commits into
base: main
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 {
dgghinea marked this conversation as resolved.
Show resolved Hide resolved
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