github.com/Axway/agent-sdk@v1.1.101/pkg/apic/service.go (about)

     1  package apic
     2  
     3  import (
     4  	"encoding/binary"
     5  	"encoding/json"
     6  	"fmt"
     7  	"net/http"
     8  	"strconv"
     9  
    10  	defs "github.com/Axway/agent-sdk/pkg/apic/definitions"
    11  	"github.com/Axway/agent-sdk/pkg/util"
    12  
    13  	coreapi "github.com/Axway/agent-sdk/pkg/api"
    14  	v1 "github.com/Axway/agent-sdk/pkg/apic/apiserver/models/api/v1"
    15  	management "github.com/Axway/agent-sdk/pkg/apic/apiserver/models/management/v1alpha1"
    16  	utilerrors "github.com/Axway/agent-sdk/pkg/util/errors"
    17  )
    18  
    19  type actionType int
    20  
    21  const (
    22  	addAPI    = iota
    23  	updateAPI = iota
    24  )
    25  
    26  const (
    27  	tenMB = 10485760
    28  )
    29  
    30  // PublishService - processes the API to create/update apiservice, revision, instance
    31  func (c *ServiceClient) PublishService(serviceBody *ServiceBody) (*management.APIService, error) {
    32  	logger := c.logger.WithField("serviceName", serviceBody.NameToPush).WithField("apiID", serviceBody.RestAPIID)
    33  	if serviceBody.PrimaryKey != "" {
    34  		logger = logger.WithField("primaryKey", serviceBody.PrimaryKey)
    35  	}
    36  	// if the team is set in the config, use that team name and id for all services
    37  	if c.cfg.GetTeamName() != "" {
    38  		if teamID, found := c.getTeamFromCache(c.cfg.GetTeamName()); found {
    39  			serviceBody.TeamName = c.cfg.GetTeamName()
    40  			serviceBody.teamID = teamID
    41  			logger.Debugf("setting team name (%s) and team id (%s)", serviceBody.TeamName, serviceBody.teamID)
    42  		}
    43  	}
    44  
    45  	// there is a current envoy restriction with the payload size (10mb). Quick check on the size
    46  	if binary.Size(serviceBody.SpecDefinition) >= tenMB {
    47  		// if greater than 10mb, return
    48  		err := fmt.Errorf("service %s carries a payload greater than 10mb. Service not created", serviceBody.APIName)
    49  		logger.WithError(err).Error("error processing service")
    50  		return nil, err
    51  	}
    52  
    53  	// API Service
    54  	logger.Trace("processing service")
    55  	apiSvc, err := c.processService(serviceBody)
    56  	if err != nil {
    57  		logger.WithError(err).Error("processing service")
    58  		return nil, err
    59  	}
    60  	// Update description title after creating APIService to include the stage name if it exists
    61  	c.postAPIServiceUpdate(serviceBody)
    62  
    63  	// RevisionProcessor
    64  	logger.Trace("processing revision")
    65  	err = c.processRevision(serviceBody)
    66  	if err != nil {
    67  		logger.WithError(err).Error("processing revision")
    68  		return nil, err
    69  	}
    70  
    71  	// InstanceProcessor
    72  	logger.Trace("processing instance")
    73  	err = c.processInstance(serviceBody)
    74  	if err != nil {
    75  		logger.WithError(err).Error("processing instance")
    76  		return nil, err
    77  	}
    78  
    79  	logger.Trace("adding spec hashes to service")
    80  	serviceBody.specHashes[serviceBody.specHash] = serviceBody.serviceContext.revisionName
    81  	details := util.GetAgentDetails(apiSvc)
    82  	details[specHashes] = serviceBody.specHashes
    83  	util.SetAgentDetails(apiSvc, details)
    84  	if err := c.CreateSubResource(apiSvc.ResourceMeta, map[string]interface{}{defs.XAgentDetails: details}); err != nil {
    85  		logger.Error("error adding spec hashes in x-agent-details, retrying")
    86  		// if the update failed try once more
    87  		if err := c.CreateSubResource(apiSvc.ResourceMeta, map[string]interface{}{defs.XAgentDetails: details}); err != nil {
    88  			logger.WithError(err).Error("could not add spec hashes in x-agent-details")
    89  		}
    90  	}
    91  	ri, _ := apiSvc.AsInstance()
    92  	c.caches.AddAPIService(ri)
    93  	if err != nil {
    94  		logger.WithError(err).Error("adding service to cache")
    95  	}
    96  
    97  	return apiSvc, nil
    98  }
    99  
   100  // DeleteServiceByName -
   101  func (c *ServiceClient) DeleteServiceByName(name string) error {
   102  	_, err := c.apiServiceDeployAPI(http.MethodDelete, c.cfg.GetServicesURL()+"/"+name, nil)
   103  	if err != nil {
   104  		return err
   105  	}
   106  	return nil
   107  }
   108  
   109  // DeleteAPIServiceInstance deletes an api service instance in central by name
   110  func (c *ServiceClient) DeleteAPIServiceInstance(name string) error {
   111  	_, err := c.apiServiceDeployAPI(http.MethodDelete, c.cfg.GetInstancesURL()+"/"+name, nil)
   112  	if err != nil && err.Error() != strconv.Itoa(http.StatusNotFound) {
   113  		return err
   114  	}
   115  	return nil
   116  }
   117  
   118  // postApiServiceUpdate - called after APIService was created or updated.
   119  // Update description and title after updating or creating APIService to include the stage name if it exists
   120  func (c *ServiceClient) postAPIServiceUpdate(serviceBody *ServiceBody) {
   121  	if serviceBody.Stage != "" {
   122  		stageDisplay := serviceBody.Stage
   123  		if serviceBody.StageDisplayName != "" {
   124  			stageDisplay = serviceBody.StageDisplayName
   125  		}
   126  
   127  		stageDescription := fmt.Sprintf("%s: %s", serviceBody.StageDescriptor, stageDisplay)
   128  		if len(serviceBody.Description) > 0 {
   129  			stageDescription = fmt.Sprintf(", %s", stageDescription)
   130  			if len(serviceBody.Description)+len(stageDescription) >= maxDescriptionLength {
   131  				description := serviceBody.Description[0 : maxDescriptionLength-len(strEllipsis)-len(stageDescription)]
   132  				serviceBody.Description = fmt.Sprintf("%s%s%s", description, strEllipsis, stageDescription)
   133  			} else {
   134  				serviceBody.Description = fmt.Sprintf("%s%s", serviceBody.Description, stageDescription)
   135  			}
   136  		} else {
   137  			serviceBody.Description = stageDescription
   138  		}
   139  		serviceBody.NameToPush = fmt.Sprintf("%v (%s: %v)", serviceBody.NameToPush, serviceBody.StageDescriptor, stageDisplay)
   140  	} else if c.cfg.GetAppendEnvironmentToTitle() {
   141  		// Append the environment name to the title, if set
   142  		serviceBody.NameToPush = fmt.Sprintf("%v (%v)", serviceBody.NameToPush, c.cfg.GetEnvironmentName())
   143  	}
   144  }
   145  
   146  func buildAgentDetailsSubResource(
   147  	serviceBody *ServiceBody, isAPIService bool, additional map[string]interface{},
   148  ) map[string]interface{} {
   149  	details := make(map[string]interface{})
   150  
   151  	externalAPIID := serviceBody.RestAPIID
   152  	// check to see if is an APIService
   153  	if !isAPIService && serviceBody.Stage != "" {
   154  		details[defs.AttrExternalAPIStage] = serviceBody.Stage
   155  	}
   156  	if serviceBody.PrimaryKey != "" {
   157  		details[defs.AttrExternalAPIPrimaryKey] = serviceBody.PrimaryKey
   158  	}
   159  
   160  	details[defs.AttrExternalAPIID] = externalAPIID
   161  	details[defs.AttrExternalAPIName] = serviceBody.APIName
   162  	details[defs.AttrCreatedBy] = serviceBody.CreatedBy
   163  
   164  	return util.MergeMapStringInterface(details, additional)
   165  }
   166  
   167  func isValidAuthPolicy(auth string) bool {
   168  	for _, item := range ValidPolicies {
   169  		if item == auth {
   170  			return true
   171  		}
   172  	}
   173  	return false
   174  }
   175  
   176  // Sanitize name to be path friendly and follow this regex: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*
   177  func sanitizeAPIName(name string) string {
   178  	return util.ConvertToDomainNameCompliant(name)
   179  }
   180  
   181  // apiServiceDeployAPI -
   182  func (c *ServiceClient) apiServiceDeployAPI(method, url string, buffer []byte) (*v1.ResourceInstance, error) {
   183  	ri, err := c.executeAPIServiceAPI(method, url, buffer)
   184  	return ri, err
   185  }
   186  
   187  // executeAPIServiceAPI -
   188  func (c *ServiceClient) executeAPIServiceAPI(method, url string, buffer []byte) (*v1.ResourceInstance, error) {
   189  	headers, err := c.createHeader()
   190  	if err != nil {
   191  		return nil, err
   192  	}
   193  
   194  	request := coreapi.Request{
   195  		Method:      method,
   196  		URL:         url,
   197  		QueryParams: nil,
   198  		Headers:     headers,
   199  		Body:        buffer,
   200  	}
   201  	response, err := c.apiClient.Send(request)
   202  	if err != nil {
   203  		return nil, err
   204  	}
   205  	//  Check to see if rollback was processed
   206  	if method == http.MethodDelete && response.Code == http.StatusNoContent {
   207  		return nil, nil
   208  	}
   209  
   210  	if response.Code >= http.StatusBadRequest {
   211  		responseErr := readResponseErrors(response.Code, response.Body)
   212  		return nil, utilerrors.Wrap(ErrRequestQuery, responseErr)
   213  	}
   214  	ri := &v1.ResourceInstance{}
   215  	json.Unmarshal(response.Body, ri)
   216  	return ri, nil
   217  }