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  }