From 6e39240050195c64088b4f1043e824035cbc0528 Mon Sep 17 00:00:00 2001 From: Jason Collins Date: Fri, 1 Nov 2024 13:55:38 -0700 Subject: [PATCH] updates found while testing --- pkg/agent/handler/accessrequest.go | 60 +++++++++++++---- .../v1alpha1/model_access_request_spec.go | 5 +- pkg/config/agentfeaturesconfig.go | 6 ++ pkg/config/metricserviceconfig.go | 64 +++++++++++++++++-- pkg/customunit/client.go | 2 +- pkg/transaction/metric/cachestorage.go | 2 +- pkg/transaction/metric/metricscollector.go | 2 +- pkg/transaction/metric/util.go | 2 +- 8 files changed, 118 insertions(+), 25 deletions(-) diff --git a/pkg/agent/handler/accessrequest.go b/pkg/agent/handler/accessrequest.go index d9066ded6..b778441ed 100644 --- a/pkg/agent/handler/accessrequest.go +++ b/pkg/agent/handler/accessrequest.go @@ -2,11 +2,13 @@ package handler import ( "context" + "encoding/json" "fmt" agentcache "github.com/Axway/agent-sdk/pkg/agent/cache" "github.com/Axway/agent-sdk/pkg/amplify/agent/customunits" apiv1 "github.com/Axway/agent-sdk/pkg/apic/apiserver/models/api/v1" + "github.com/Axway/agent-sdk/pkg/apic/apiserver/models/catalog/v1" management "github.com/Axway/agent-sdk/pkg/apic/apiserver/models/management/v1alpha1" defs "github.com/Axway/agent-sdk/pkg/apic/definitions" prov "github.com/Axway/agent-sdk/pkg/apic/provisioning" @@ -138,10 +140,10 @@ func (h *accessRequestHandler) onPending(ctx context.Context, ar *management.Acc data := map[string]interface{}{} status, accessData := h.prov.AccessRequestProvision(req) - if status.GetStatus() == prov.Success { + if status.GetStatus() == prov.Success && len(ar.Spec.AdditionalQuotas) > 0 { metricServicesConfigs := h.metricServicesConfig // Build quota info - quotaInfo, err := h.buildQuotaInfo(ctx, ar) + quotaInfo, err := h.buildQuotaInfo(ctx, ar, app) if err != nil { log.WithError(err).Errorf("error building quota info") h.onError(ctx, ar, err) @@ -153,7 +155,7 @@ func (h *accessRequestHandler) onPending(ctx context.Context, ar *management.Acc factory := customunit.NewQuotaEnforcementClientFactory(config.URL, quotaInfo) client, _ := factory(ctx) response, err := client.QuotaEnforcementInfo() - if err == nil { + if err != nil { // if error from QE and reject on fail, we return the error back to the central if response.Error != "" && config.RejectOnFailEnabled() { errMessage = errMessage + fmt.Sprintf("TODO: message: %s", err.Error()) @@ -297,29 +299,57 @@ func (h *accessRequestHandler) getARD(ctx context.Context, ar *management.Access return ard, err } -func (h *accessRequestHandler) buildQuotaInfo(ctx context.Context, ar *management.AccessRequest) (*customunits.QuotaInfo, error) { - // Get service instance from access request to fetch the api service +type reference struct { + Kind string `json:"kind"` + Name string `json:"name"` + Unit string `json:"unit"` +} + +func (h *accessRequestHandler) getQuotaInfo(ar *management.AccessRequest) (string, int) { + index := 0 + if len(ar.Spec.AdditionalQuotas) < index+1 { + return "", 0 + } + + q := ar.Spec.AdditionalQuotas[index] + for _, r := range ar.References { + d, _ := json.Marshal(r) + ref := &reference{} + json.Unmarshal(d, ref) + if ref.Kind == catalog.QuotaGVK().Kind && ref.Name == q.Name { + return ref.Unit, int(q.Limit) + } + } + return "", 0 +} + +func (h *accessRequestHandler) buildQuotaInfo(ctx context.Context, ar *management.AccessRequest, app *management.ManagedApplication) (*customunits.QuotaInfo, error) { + unitRef, count := h.getQuotaInfo(ar) + if unitRef == "" { + return nil, nil + } + instance, err := h.getServiceInstance(ctx, ar) if err != nil { return nil, err } + + // Get service instance from access request to fetch the api service serviceRef := instance.GetReferenceByGVK(management.APIServiceGVK()) - serviceID := serviceRef.ID - service := h.cache.GetAPIServiceWithAPIID(serviceID) - extAPIID, err := util.GetAgentDetailsValue(service, defs.AttrExternalAPIID) + service := h.cache.GetAPIServiceWithName(serviceRef.Name) + if service == nil { + return nil, fmt.Errorf("could not find service connected to quota") + } + extAPIID, err := util.GetAgentDetailsValue(instance, defs.AttrExternalAPIID) if err != nil { return nil, err } - // Get app info from the access request - appRef := instance.GetReferenceByGVK(management.ManagedApplicationGVK()) - appName := appRef.Name - app := h.cache.GetManagedApplicationByName(appName) q := &customunits.QuotaInfo{ ApiInfo: &customunits.APIInfo{ ServiceDetails: util.GetAgentDetailStrings(service), ServiceName: service.Name, - ServiceID: serviceID, + ServiceID: service.Metadata.ID, ExternalAPIID: extAPIID, }, AppInfo: &customunits.AppInfo{ @@ -327,6 +357,10 @@ func (h *accessRequestHandler) buildQuotaInfo(ctx context.Context, ar *managemen AppName: app.Name, AppID: app.Metadata.ID, }, + Quota: &customunits.Quota{ + Count: int64(count), + Unit: unitRef, + }, } return q, nil diff --git a/pkg/apic/apiserver/models/management/v1alpha1/model_access_request_spec.go b/pkg/apic/apiserver/models/management/v1alpha1/model_access_request_spec.go index acbe5c7f5..b94071253 100644 --- a/pkg/apic/apiserver/models/management/v1alpha1/model_access_request_spec.go +++ b/pkg/apic/apiserver/models/management/v1alpha1/model_access_request_spec.go @@ -9,6 +9,7 @@ type AccessRequestSpec struct { // The name of an ManagedApplication resource that groups set of credentials. ManagedApplication string `json:"managedApplication"` // The value that matches the AccessRequestDefinition schema linked to the referenced APIServiceInstance. (management.v1alpha1.AccessRequest) - Data map[string]interface{} `json:"data"` - Quota *AccessRequestSpecQuota `json:"quota,omitempty"` + Data map[string]interface{} `json:"data"` + Quota *AccessRequestSpecQuota `json:"quota,omitempty"` + AdditionalQuotas []AccessRequestSpecAdditionalQuotas `json:"additionalQuotas,omitempty"` } diff --git a/pkg/config/agentfeaturesconfig.go b/pkg/config/agentfeaturesconfig.go index c53e41ec9..e634cc5a1 100644 --- a/pkg/config/agentfeaturesconfig.go +++ b/pkg/config/agentfeaturesconfig.go @@ -102,6 +102,7 @@ func AddAgentFeaturesConfigProperties(props properties.Properties) { props.AddBoolProperty(pathVersionChecker, true, "Controls whether the agent SDK version checker will be enabled or not") props.AddBoolProperty(pathPersistCache, true, "Controls whether the agent SDK will persist agent cache or not") props.AddBoolProperty(pathAgentStatusUpdates, true, "Controls whether the agent should manage the status update or not") + addMetricServicesProperties(props) addExternalIDPProperties(props) } @@ -114,6 +115,11 @@ func ParseAgentFeaturesConfig(props properties.Properties) (AgentFeaturesConfig, PersistCache: props.BoolPropertyValueOrTrue(pathPersistCache), AgentStatusUpdates: props.BoolPropertyValueOrTrue(pathAgentStatusUpdates), } + metricSvsCfgs, err := parseMetricServicesConfig(props) + if err != nil { + return nil, err + } + cfg.MetricServicesConfigs = metricSvsCfgs externalIDPCfg, err := parseExternalIDPConfig(props) if err != nil { return nil, err diff --git a/pkg/config/metricserviceconfig.go b/pkg/config/metricserviceconfig.go index 4475942e8..9d8a1e2ce 100644 --- a/pkg/config/metricserviceconfig.go +++ b/pkg/config/metricserviceconfig.go @@ -1,17 +1,32 @@ package config import ( + "encoding/json" + "fmt" "net/url" + "strconv" + "github.com/Axway/agent-sdk/pkg/cmd/properties" "github.com/Axway/agent-sdk/pkg/util/exception" ) const ( - pathMetricServiceEnable = "agentFeatures.metricServices.enable" - pathMetricServiceURL = "agentFeatures.metricServices.url" - pathRejectOnFail = "agentFeatures.metricServices.rejectOnFail" + pathMetricServices = "agentFeatures.metricServices" + pathMetricServiceEnable = "enable" + pathMetricServiceURL = "url" + pathRejectOnFail = "rejectOnFail" ) +var metricServiceProps = []string{ + pathMetricServiceEnable, + pathMetricServiceURL, + pathRejectOnFail, +} + +func addMetricServicesProperties(props properties.Properties) { + props.AddObjectSliceProperty(pathMetricServices, metricServiceProps) +} + type MetricServiceConfig interface { MetricServiceEnabled() bool GetMetricServiceURL() string @@ -21,9 +36,15 @@ type MetricServiceConfig interface { type MetricServiceConfiguration struct { MetricServiceConfig - Enable bool `config:"enable"` // set to true to have the sdk initiate the connection to the custom metric service - URL string `config:"url"` // set the url that the agent will connect to the metric service on - RejectOnFail bool `config:"rejectOnFail"` // set to true to reject the access request if the quota enforcement call fails + Enable bool // set to true to have the sdk initiate the connection to the custom metric service + URL string // set the url that the agent will connect to the metric service on + RejectOnFail bool // set to true to reject the access request if the quota enforcement call fails +} + +type metricsvcconfig struct { + Enable string `json:"enable"` + URL string `json:"url"` + RejectOnFail string `json:"rejectOnFail"` } func (a *MetricServiceConfiguration) validate() { @@ -47,3 +68,34 @@ func (c *MetricServiceConfiguration) GetMetricServiceURL() string { func (c *MetricServiceConfiguration) RejectOnFailEnabled() bool { return c.RejectOnFail } + +func parseMetricServicesConfig(props properties.Properties) ([]MetricServiceConfiguration, error) { + svcCfgList := props.ObjectSlicePropertyValue(pathMetricServices) + + cfgs := []MetricServiceConfiguration{} + + for _, svcCfgProps := range svcCfgList { + svcCfg := metricsvcconfig{} + + buf, _ := json.Marshal(svcCfgProps) + err := json.Unmarshal(buf, &svcCfg) + if err != nil { + return nil, fmt.Errorf("error parsing metrics service configuration, %s", err) + } + cfgs = append(cfgs, MetricServiceConfiguration{ + Enable: parseBool(svcCfg.Enable), + URL: svcCfg.URL, + RejectOnFail: parseBool(svcCfg.RejectOnFail), + }) + } + + return cfgs, nil +} + +func parseBool(str string) bool { + v, err := strconv.ParseBool(str) + if err != nil { + return false + } + return v +} diff --git a/pkg/customunit/client.go b/pkg/customunit/client.go index f1ff1d446..1c5cfd4e5 100644 --- a/pkg/customunit/client.go +++ b/pkg/customunit/client.go @@ -110,6 +110,7 @@ func NewCustomMetricReportingClientFactory(url string, agentCache cache.Manager) dialOpts: []grpc.DialOption{ grpc.WithTransportCredentials(insecure.NewCredentials()), }, + timer: time.NewTimer(time.Hour), } for _, o := range opts { @@ -184,7 +185,6 @@ func (c *customUnitMetricReportingClient) processMetrics() { } c.reportMetrics(metricReport) } - } } diff --git a/pkg/transaction/metric/cachestorage.go b/pkg/transaction/metric/cachestorage.go index 56f3bdc18..759d68510 100644 --- a/pkg/transaction/metric/cachestorage.go +++ b/pkg/transaction/metric/cachestorage.go @@ -178,7 +178,7 @@ func (c *cacheStorage) loadMetrics(storageCache cache.Cache) { continue } - c.collector.AddCustomMetricDetail(CustomMetricDetail{ + c.collector.AddCustomMetricDetail(models.CustomMetricDetail{ APIDetails: apiDetails, AppDetails: appDetails, UnitDetails: models.Unit{ diff --git a/pkg/transaction/metric/metricscollector.go b/pkg/transaction/metric/metricscollector.go index a206c593a..d8c3f8cc4 100644 --- a/pkg/transaction/metric/metricscollector.go +++ b/pkg/transaction/metric/metricscollector.go @@ -412,7 +412,7 @@ func (c *collector) createMetric(detail transactionContext) *centralMetric { API: c.createAPIDetail(detail.APIDetails), AssetResource: c.getAssetResource(accessRequest), ProductPlan: c.getProductPlan(accessRequest), - Observation: &ObservationDetails{ + Observation: &models.ObservationDetails{ Start: now().Unix(), }, EventID: uuid.NewString(), diff --git a/pkg/transaction/metric/util.go b/pkg/transaction/metric/util.go index bedc2686a..ab88ae595 100644 --- a/pkg/transaction/metric/util.go +++ b/pkg/transaction/metric/util.go @@ -11,7 +11,7 @@ import ( func centralMetricFromAPIMetric(in *APIMetric) *centralMetric { out := ¢ralMetric{ EventID: in.EventID, - Observation: &ObservationDetails{ + Observation: &models.ObservationDetails{ Start: in.Observation.Start, }, }