diff --git a/apidump/apidump.go b/apidump/apidump.go index babab1c..070a0fc 100644 --- a/apidump/apidump.go +++ b/apidump/apidump.go @@ -177,7 +177,7 @@ func (a *apidump) LookupService() error { frontClient := rest.NewFrontClient(a.Domain, a.ClientID) if a.PostmanCollectionID != "" { - backendSvc, err := util.GetServiceIDByPostmanCollectionID(frontClient, a.PostmanCollectionID) + backendSvc, err := util.GetOrCreateServiceIDByPostmanCollectionID(frontClient, a.PostmanCollectionID) if err != nil { return err } diff --git a/cmd/internal/kube/inject.go b/cmd/internal/kube/inject.go index 4a124aa..bc20444 100644 --- a/cmd/internal/kube/inject.go +++ b/cmd/internal/kube/inject.go @@ -325,7 +325,7 @@ func lookupService(postmanCollectionID, serviceName string) error { frontClient := rest.NewFrontClient(rest.Domain, telemetry.GetClientID()) if postmanCollectionID != "" { - _, err := util.GetServiceIDByPostmanCollectionID(frontClient, postmanCollectionID) + _, err := util.GetOrCreateServiceIDByPostmanCollectionID(frontClient, postmanCollectionID) if err != nil { return err } diff --git a/rest/models.go b/rest/models.go index 9653286..6748824 100644 --- a/rest/models.go +++ b/rest/models.go @@ -20,6 +20,12 @@ type Service struct { type User = api_schema.UserResponse type CreateServiceResponse struct { - RequestID string `json:"request_id"` + RequestID akid.RequestID `json:"request_id"` ResourceID akid.ServiceID `json:"resource_id"` } + +type CreateServiceErrorResponse struct { + RequestID akid.RequestID `json:"request_id"` + Message string `json:"message"` + ResourceID string `json:"resource_id"` +} diff --git a/util/util.go b/util/util.go index d893de1..3709dfc 100644 --- a/util/util.go +++ b/util/util.go @@ -2,6 +2,7 @@ package util import ( "context" + "encoding/json" "fmt" "net/http" "strings" @@ -93,24 +94,10 @@ func GetServiceIDByName(c rest.FrontClient, name string) (akid.ServiceID, error) return akid.ServiceID{}, errors.Errorf("cannot determine project ID for %s", name) } -func GetServiceIDByPostmanCollectionID(c rest.FrontClient, collectionID string) (akid.ServiceID, error) { - // Normalize the collectionID. - collectionID = strings.ToLower(collectionID) - unexpectedErrMsg := "Something went wrong while starting the Agent. " + - "Please contact Postman support (observability-support@postman.com) with the error details" - - if id, found := postmanCollectionIDCache.Get(collectionID); found { - printer.Stderr.Debugf("Cached collectionID %q is %q\n", collectionID, akid.String(id.(akid.ServiceID))) - return id.(akid.ServiceID), nil - } - - // Fill cache. - ctx, cancel := context.WithTimeout(context.Background(), apiTimeout) - defer cancel() +func GetServiceIDByPostmanCollectionID(c rest.FrontClient, ctx context.Context, collectionID string) (akid.ServiceID, error) { services, err := c.GetServices(ctx) if err != nil { - printer.Stderr.Debugf("Failed to get list of services associated with the API Key: %s\n", err) - return akid.ServiceID{}, errors.Wrap(err, unexpectedErrMsg) + return akid.ServiceID{}, err } var result akid.ServiceID @@ -128,13 +115,38 @@ func GetServiceIDByPostmanCollectionID(c rest.FrontClient, collectionID string) if strings.EqualFold(collectionID, svcCollectionID) { result = svc.ID - postmanCollectionIDCache.Set(svcCollectionID, svc.ID, cache.DefaultExpiration) } } - if (result != akid.ServiceID{}) { - printer.Stderr.Debugf("Postman collectionID %q is %q\n", collectionID, result) - return result, nil + return result, nil +} + +func GetOrCreateServiceIDByPostmanCollectionID(c rest.FrontClient, collectionID string) (akid.ServiceID, error) { + // Normalize the collectionID. + collectionID = strings.ToLower(collectionID) + unexpectedErrMsg := "Something went wrong while starting the Agent. " + + "Please contact Postman support (observability-support@postman.com) with the error details" + failedToCreateServiceErrMsg := "Failed to create service for given collectionID: %s\n" + + if id, found := postmanCollectionIDCache.Get(collectionID); found { + printer.Stderr.Debugf("Cached collectionID %q is %q\n", collectionID, akid.String(id.(akid.ServiceID))) + return id.(akid.ServiceID), nil + } + + // Fetch service and fill cache + ctx, cancel := context.WithTimeout(context.Background(), apiTimeout) + defer cancel() + + serviceID, err := GetServiceIDByPostmanCollectionID(c, ctx, collectionID) + if err != nil { + printer.Stderr.Debugf("Failed to get list of services associated with the API Key: %s\n", err) + return akid.ServiceID{}, errors.Wrap(err, unexpectedErrMsg) + } + + if (serviceID != akid.ServiceID{}) { + printer.Stderr.Debugf("ServiceID for Postman collectionID %q is %q\n", collectionID, serviceID) + postmanCollectionIDCache.Set(collectionID, serviceID, cache.DefaultExpiration) + return serviceID, nil } name := postmanRandomName() @@ -142,10 +154,33 @@ func GetServiceIDByPostmanCollectionID(c rest.FrontClient, collectionID string) // Create service for given postman collectionID resp, err := c.CreateService(ctx, name, collectionID) if err != nil { - printer.Stderr.Debugf("Failed to create service for given collectionID: %s\n", err) + httpErr, ok := err.(rest.HTTPError) + if !ok { + printer.Stderr.Debugf(failedToCreateServiceErrMsg, err) + return akid.ServiceID{}, errors.Wrap(err, unexpectedErrMsg) + } + + var errorResponse rest.CreateServiceErrorResponse + if err := json.Unmarshal(httpErr.Body, &errorResponse); err != nil { + printer.Stderr.Debugf(failedToCreateServiceErrMsg, err) + return akid.ServiceID{}, errors.Wrap(err, unexpectedErrMsg) + } - if httpErr, ok := err.(rest.HTTPError); ok && httpErr.StatusCode == 403 { - error := fmt.Errorf("You cannot send traffic to the collection with ID %s. "+ + if httpErr.StatusCode == 409 && errorResponse.Message == "collection_already_mapped" { + serviceID, err := GetServiceIDByPostmanCollectionID(c, ctx, collectionID) + if err != nil { + printer.Stderr.Debugf(failedToCreateServiceErrMsg, err) + return akid.ServiceID{}, errors.Wrap(err, unexpectedErrMsg) + } + + if (serviceID != akid.ServiceID{}) { + printer.Stderr.Debugf("ServiceID for Postman collectionID %q is %q\n", collectionID, serviceID) + postmanCollectionIDCache.Set(collectionID, serviceID, cache.DefaultExpiration) + return serviceID, nil + } + + } else if httpErr.StatusCode == 403 { + error := fmt.Errorf("you cannot send traffic to the collection with ID %s. "+ "Ensure that your collection ID is correct and that you have edit permissions on the collection. "+ "If you do not have edit permissions, please contact the workspace administrator to add you as a collection editor.", collectionID) return akid.ServiceID{}, error @@ -155,6 +190,7 @@ func GetServiceIDByPostmanCollectionID(c rest.FrontClient, collectionID string) } printer.Debugf("Got service ID %s\n", resp.ResourceID) + postmanCollectionIDCache.Set(collectionID, resp.ResourceID, cache.DefaultExpiration) return resp.ResourceID, nil }