github.com/Axway/agent-sdk@v1.1.101/pkg/apic/apiserviceinstance.go (about) 1 package apic 2 3 import ( 4 "encoding/json" 5 "errors" 6 "net/http" 7 8 "github.com/Axway/agent-sdk/pkg/util" 9 10 coreapi "github.com/Axway/agent-sdk/pkg/api" 11 apiv1 "github.com/Axway/agent-sdk/pkg/apic/apiserver/models/api/v1" 12 management "github.com/Axway/agent-sdk/pkg/apic/apiserver/models/management/v1alpha1" 13 defs "github.com/Axway/agent-sdk/pkg/apic/definitions" 14 utilerrors "github.com/Axway/agent-sdk/pkg/util/errors" 15 "github.com/Axway/agent-sdk/pkg/util/log" 16 ) 17 18 func buildAPIServiceInstanceMarketplaceSpec( 19 serviceBody *ServiceBody, 20 endpoints []management.ApiServiceInstanceSpecEndpoint, 21 knownCRDs []string, 22 ) management.ApiServiceInstanceSpec { 23 return management.ApiServiceInstanceSpec{ 24 ApiServiceRevision: serviceBody.serviceContext.revisionName, 25 Endpoint: endpoints, 26 CredentialRequestDefinitions: knownCRDs, 27 AccessRequestDefinition: serviceBody.ardName, 28 } 29 } 30 31 func (c *ServiceClient) checkCredentialRequestDefinitions(serviceBody *ServiceBody) []string { 32 allowedOAuthMethods := make([]string, 0) 33 if c.cfg != nil && c.cfg.GetCredentialConfig() != nil { 34 allowedOAuthMethods = c.cfg.GetCredentialConfig().GetAllowedOAuthMethods() 35 } 36 37 crds := serviceBody.GetCredentialRequestDefinitions(allowedOAuthMethods) 38 39 // remove any crd not in the cache 40 knownCRDs := make([]string, 0) 41 // Check if request definitions are allowed. False would indicate the service is Unpublished 42 if serviceBody.requestDefinitionsAllowed { 43 for _, crd := range crds { 44 if def, err := c.caches.GetCredentialRequestDefinitionByName(crd); err == nil && def != nil { 45 knownCRDs = append(knownCRDs, crd) 46 } 47 } 48 } else { 49 log.Warnf("removed existing credential request definitions for instance %s. Contact your system administrator for further assistance", serviceBody.APIName) 50 } 51 52 return knownCRDs 53 } 54 55 func (c *ServiceClient) checkAccessRequestDefinition(serviceBody *ServiceBody) { 56 ard := serviceBody.ardName 57 58 // Check if request definitions are allowed. False would indicate the service is Unpublished 59 if serviceBody.requestDefinitionsAllowed { 60 if def, err := c.caches.GetAccessRequestDefinitionByName(ard); err == nil && def != nil { 61 return 62 } 63 } else { 64 log.Warnf("removed existing access request definitions for instance %s. Contact your system administrator for further assistance", serviceBody.APIName) 65 } 66 67 serviceBody.ardName = "" 68 } 69 70 func (c *ServiceClient) buildAPIServiceInstance( 71 serviceBody *ServiceBody, 72 name string, 73 endpoints []management.ApiServiceInstanceSpecEndpoint, 74 ) *management.APIServiceInstance { 75 76 c.checkAccessRequestDefinition(serviceBody) 77 spec := buildAPIServiceInstanceMarketplaceSpec(serviceBody, endpoints, c.checkCredentialRequestDefinitions(serviceBody)) 78 79 owner, _ := c.getOwnerObject(serviceBody, false) // owner, _ := at this point, we don't need to validate error on getOwnerObject. This is used for subresource status update 80 instance := &management.APIServiceInstance{ 81 ResourceMeta: apiv1.ResourceMeta{ 82 GroupVersionKind: management.APIServiceInstanceGVK(), 83 Name: name, 84 Title: serviceBody.NameToPush, 85 Attributes: util.CheckEmptyMapStringString(serviceBody.InstanceAttributes), 86 Tags: mapToTagsArray(serviceBody.Tags, c.cfg.GetTagsToPublish()), 87 Metadata: apiv1.Metadata{ 88 Scope: apiv1.MetadataScope{ 89 Kind: management.EnvironmentGVK().Kind, 90 Name: c.cfg.GetEnvironmentName(), 91 }, 92 }, 93 }, 94 Spec: spec, 95 Owner: owner, 96 } 97 buildAPIServiceInstanceSourceSubResource(instance, serviceBody) 98 99 instDetails := util.MergeMapStringInterface(serviceBody.ServiceAgentDetails, serviceBody.InstanceAgentDetails) 100 details := buildAgentDetailsSubResource(serviceBody, false, instDetails) 101 util.SetAgentDetails(instance, details) 102 103 return instance 104 } 105 106 func (c *ServiceClient) updateAPIServiceInstance( 107 serviceBody *ServiceBody, 108 instance *management.APIServiceInstance, 109 endpoints []management.ApiServiceInstanceSpecEndpoint, 110 ) *management.APIServiceInstance { 111 owner, _ := c.getOwnerObject(serviceBody, false) 112 instance.GroupVersionKind = management.APIServiceInstanceGVK() 113 instance.Metadata.ResourceVersion = "" 114 instance.Title = serviceBody.NameToPush 115 instance.Attributes = util.CheckEmptyMapStringString(serviceBody.InstanceAttributes) 116 instance.Tags = mapToTagsArray(serviceBody.Tags, c.cfg.GetTagsToPublish()) 117 118 c.checkAccessRequestDefinition(serviceBody) 119 instance.Spec = buildAPIServiceInstanceMarketplaceSpec(serviceBody, endpoints, c.checkCredentialRequestDefinitions(serviceBody)) 120 121 instance.Owner = owner 122 buildAPIServiceInstanceSourceSubResource(instance, serviceBody) 123 124 details := util.MergeMapStringInterface(serviceBody.ServiceAgentDetails, serviceBody.InstanceAgentDetails) 125 util.SetAgentDetails(instance, buildAgentDetailsSubResource(serviceBody, false, details)) 126 127 return instance 128 } 129 130 func buildAPIServiceInstanceSourceSubResource(instance *management.APIServiceInstance, serviceBody *ServiceBody) *management.ApiServiceInstanceSource { 131 serviceBody.serviceContext.updateInstanceSource = false 132 133 source := instance.Source 134 if source == nil { 135 instance.Source = &management.ApiServiceInstanceSource{} 136 source = instance.Source 137 } 138 139 dataplaneType := serviceBody.GetDataplaneType() 140 if dataplaneType != "" { 141 if source.DataplaneType == nil { 142 source.DataplaneType = &management.ApiServiceInstanceSourceDataplaneType{} 143 } 144 if serviceBody.IsDesignDataplane() { 145 if source.DataplaneType.Design != dataplaneType.String() { 146 source.DataplaneType.Design = dataplaneType.String() 147 serviceBody.serviceContext.updateInstanceSource = true 148 } 149 } else if source.DataplaneType.Managed != dataplaneType.String() { 150 source.DataplaneType.Managed = dataplaneType.String() 151 serviceBody.serviceContext.updateInstanceSource = true 152 } 153 } 154 155 referencedInstance := serviceBody.GetReferenceInstanceName() 156 if referencedInstance != "" { 157 if source.References == nil { 158 source.References = &management.ApiServiceInstanceSourceReferences{} 159 } 160 if source.References.ApiServiceInstance != referencedInstance { 161 source.References.ApiServiceInstance = serviceBody.GetReferenceInstanceName() 162 serviceBody.serviceContext.updateInstanceSource = true 163 } 164 } 165 return nil 166 } 167 168 // processInstance - Creates or updates an API Service Instance based on the current API Service Revision. 169 func (c *ServiceClient) processInstance(serviceBody *ServiceBody) error { 170 endpoints, err := createInstanceEndpoint(serviceBody.Endpoints) 171 if err != nil { 172 return err 173 } 174 175 // creating new instance 176 instance := c.buildAPIServiceInstance(serviceBody, getRevisionPrefix(serviceBody), endpoints) 177 178 if serviceBody.serviceContext.serviceAction == updateAPI { 179 prevInst, err := c.getInstance(serviceBody, c.createAPIServerURL(instance.GetKindLink())) 180 if err != nil { 181 return err 182 } 183 184 if prevInst != nil { 185 // updating existing instance 186 instance = c.updateAPIServiceInstance(serviceBody, prevInst, endpoints) 187 } 188 } 189 190 addSpecHashToResource(instance) 191 192 ri, err := c.CreateOrUpdateResource(instance) 193 if err == nil { 194 err = c.updateAPIServiceInstanceSubresources(ri, instance, serviceBody) 195 } 196 197 if err != nil { 198 if serviceBody.serviceContext.serviceAction == addAPI { 199 _, rollbackErr := c.rollbackAPIService(serviceBody.serviceContext.serviceName) 200 if rollbackErr != nil { 201 return errors.New(err.Error() + rollbackErr.Error()) 202 } 203 } 204 return err 205 } 206 207 c.caches.AddAPIServiceInstance(ri) 208 serviceBody.serviceContext.instanceName = instance.Name 209 210 return err 211 } 212 213 func (c *ServiceClient) updateAPIServiceInstanceSubresources(ri apiv1.Interface, instance *management.APIServiceInstance, serviceBody *ServiceBody) error { 214 subResources := make(map[string]interface{}) 215 if serviceBody.serviceContext.updateInstanceSource && instance.Source != nil { 216 subResources[management.ApiServiceInstanceSourceSubResourceName] = instance.Source 217 } 218 219 if len(subResources) > 0 { 220 inst, _ := ri.AsInstance() 221 return c.CreateSubResource(inst.ResourceMeta, subResources) 222 } 223 return nil 224 } 225 226 func createInstanceEndpoint(endpoints []EndpointDefinition) ([]management.ApiServiceInstanceSpecEndpoint, error) { 227 endPoints := make([]management.ApiServiceInstanceSpecEndpoint, 0) 228 229 // To set your own endpoints call AddServiceEndpoint/SetServiceEndpoint on the ServiceBodyBuilder. 230 // Any endpoints provided from the ServiceBodyBuilder will override the endpoints found in the spec. 231 if len(endpoints) > 0 { 232 for _, endpointDef := range endpoints { 233 endPoints = append(endPoints, management.ApiServiceInstanceSpecEndpoint{ 234 Host: endpointDef.Host, 235 Port: endpointDef.Port, 236 Protocol: endpointDef.Protocol, 237 Routing: management.ApiServiceInstanceSpecRouting{ 238 BasePath: endpointDef.BasePath, 239 Details: endpointDef.Details, 240 }, 241 }) 242 } 243 } else { 244 log.Debug("Processing API service instance with no endpoint") 245 } 246 247 return endPoints, nil 248 } 249 250 func (c *ServiceClient) getInstance(serviceBody *ServiceBody, url string) (*management.APIServiceInstance, error) { 251 queryParams := map[string]string{ 252 "query": "metadata.references.name==" + serviceBody.serviceContext.revisionName, 253 } 254 instances, err := c.GetAPIServiceInstances(queryParams, url) 255 if err != nil { 256 return nil, err 257 } 258 if len(instances) == 1 { 259 // return only instance 260 return instances[0], nil 261 } 262 263 // check the instance for the stage agent details 264 for _, i := range instances { 265 stage, err := util.GetAgentDetailsValue(i, defs.AttrExternalAPIStage) 266 if err != nil { 267 continue 268 } 269 if stage == serviceBody.Stage { 270 // found the stage match 271 return i, nil 272 } 273 } 274 275 // if no instance found 276 return nil, nil 277 } 278 279 // GetAPIServiceInstanceByName - Returns the API service instance for specified name 280 func (c *ServiceClient) GetAPIServiceInstanceByName(name string) (*management.APIServiceInstance, error) { 281 headers, err := c.createHeader() 282 if err != nil { 283 return nil, err 284 } 285 286 request := coreapi.Request{ 287 Method: coreapi.GET, 288 URL: c.cfg.GetInstancesURL() + "/" + name, 289 Headers: headers, 290 } 291 292 response, err := c.apiClient.Send(request) 293 if err != nil { 294 return nil, err 295 } 296 if response.Code != http.StatusOK { 297 if response.Code != http.StatusNotFound { 298 responseErr := readResponseErrors(response.Code, response.Body) 299 return nil, utilerrors.Wrap(ErrRequestQuery, responseErr) 300 } 301 return nil, nil 302 } 303 apiInstance := new(management.APIServiceInstance) 304 err = json.Unmarshal(response.Body, apiInstance) 305 return apiInstance, err 306 }