Skip to content

Commit

Permalink
[OBS-137] Handle 409 response of create service to again check for se…
Browse files Browse the repository at this point in the history
…rvice (#232)

* Create a new model to parse error response
* Handle the 409 conflict case. Check for error message and call get
services again
* Try only one time after that bail out with error
* Other small fixes: Add to cache after create service flow, small model
change
  • Loading branch information
mudit-postman committed Sep 14, 2023
1 parent 6932d4c commit e4b857e
Show file tree
Hide file tree
Showing 4 changed files with 68 additions and 26 deletions.
2 changes: 1 addition & 1 deletion apidump/apidump.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand Down
2 changes: 1 addition & 1 deletion cmd/internal/kube/inject.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand Down
8 changes: 7 additions & 1 deletion rest/models.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"`
}
82 changes: 59 additions & 23 deletions util/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package util

import (
"context"
"encoding/json"
"fmt"
"net/http"
"strings"
Expand Down Expand Up @@ -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
Expand All @@ -128,24 +115,72 @@ 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()
printer.Debugf("Found no service for given collectionID: %s, creating a new service %q\n", collectionID, name)
// 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
Expand All @@ -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
}
Expand Down

0 comments on commit e4b857e

Please sign in to comment.