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 }