github.com/aldelo/common@v1.5.1/wrapper/cloudmap/cloudmap.go (about)

     1  package cloudmap
     2  
     3  /*
     4   * Copyright 2020-2023 Aldelo, LP
     5   *
     6   * Licensed under the Apache License, Version 2.0 (the "License");
     7   * you may not use this file except in compliance with the License.
     8   * You may obtain a copy of the License at
     9   *
    10   *     http://www.apache.org/licenses/LICENSE-2.0
    11   *
    12   * Unless required by applicable law or agreed to in writing, software
    13   * distributed under the License is distributed on an "AS IS" BASIS,
    14   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    15   * See the License for the specific language governing permissions and
    16   * limitations under the License.
    17   */
    18  
    19  // =================================================================================================================
    20  // AWS CREDENTIAL:
    21  //		use $> aws configure (to set aws access key and secret to target machine)
    22  //		Store AWS Access ID and Secret Key into Default Profile Using '$ aws configure' cli
    23  //
    24  // To Install & Setup AWS CLI on Host:
    25  //		1) https://docs.aws.amazon.com/cli/latest/userguide/install-cliv2-linux.html
    26  //				On Ubuntu, if host does not have zip and unzip:
    27  //					$> sudo apt install zip
    28  //					$> sudo apt install unzip
    29  //				On Ubuntu, to install AWS CLI v2:
    30  //					$> curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip"
    31  //					$> unzip awscliv2.zip
    32  //					$> sudo ./aws/install
    33  //		2) $> aws configure set region awsRegionName --profile default
    34  // 		3) $> aws configure
    35  //				follow prompts to enter Access ID and Secret Key
    36  //
    37  // AWS Region Name Reference:
    38  //		us-west-2, us-east-1, ap-northeast-1, etc
    39  //		See: https://docs.aws.amazon.com/general/latest/gr/rande.html
    40  // =================================================================================================================
    41  
    42  import (
    43  	"context"
    44  	"errors"
    45  	"fmt"
    46  	util "github.com/aldelo/common"
    47  	awshttp2 "github.com/aldelo/common/wrapper/aws"
    48  	"github.com/aldelo/common/wrapper/aws/awsregion"
    49  	"github.com/aldelo/common/wrapper/cloudmap/sdhealthchecktype"
    50  	"github.com/aldelo/common/wrapper/cloudmap/sdnamespacefilter"
    51  	"github.com/aldelo/common/wrapper/cloudmap/sdoperationfilter"
    52  	"github.com/aldelo/common/wrapper/xray"
    53  	"github.com/aws/aws-sdk-go/aws"
    54  	"github.com/aws/aws-sdk-go/aws/session"
    55  	"github.com/aws/aws-sdk-go/service/servicediscovery"
    56  	awsxray "github.com/aws/aws-xray-sdk-go/xray"
    57  	"net/http"
    58  	"time"
    59  )
    60  
    61  // ================================================================================================================
    62  // STRUCTS
    63  // ================================================================================================================
    64  
    65  // CloudMap struct encapsulates the AWS CloudMap access functionality
    66  type CloudMap struct {
    67  	// define the AWS region that KMS is located at
    68  	AwsRegion awsregion.AWSRegion
    69  
    70  	// custom http2 client options
    71  	HttpOptions *awshttp2.HttpClientSettings
    72  
    73  	// store aws session object
    74  	sdClient *servicediscovery.ServiceDiscovery
    75  
    76  	_parentSegment *xray.XRayParentSegment
    77  }
    78  
    79  // DnsConf represents a dns config option to be used by CreateService
    80  // for this project we will only use:
    81  //  1. ipv4 - dns record type A
    82  //  2. srv
    83  //
    84  // TTL = dns record time to live in seconds
    85  // MultiValue = true: route 53 returns up to 8 healthy targets if health check is enabled (otherwise, all targets assumed healthy)
    86  //
    87  //	false: route 53 uses WEIGHTED to return a random healthy target if health check is enabled (if no healthy target found, then any random target is used)
    88  //
    89  // SRV = true: dns use SRV; false: dns use A
    90  type DnsConf struct {
    91  	TTL        int64
    92  	MultiValue bool
    93  	SRV        bool
    94  }
    95  
    96  // HealthCheckConf represents target health check configuration
    97  //
    98  // Custom = true: use HealthCheckCustomConfig (for Http, Public Dns, Private Dns namespaces)
    99  //
   100  //	false: use HealthCheckConfig (for Public Dns namespace only)
   101  //
   102  // FailureThreshold = if Custom is true:
   103  //
   104  //			*) number of 30-second intervals that cloud map waits after
   105  //			   UpdateInstanceCustomHealthStatus is executed,
   106  //			   before changing the target health status
   107  //	  if Custom is false:
   108  //			*) number of consecutive times health checks of target
   109  //			   must pass or fail for route 53 to consider healthy or unhealthy
   110  //
   111  // PubDns_HealthCheck_Type = for public dns namespace only: the endpoint protocol type used for health check
   112  // PubDns_HealthCheck_Path = for public dns namespace only: (Http and Https type ONLY),
   113  //
   114  //	path to service that responds to health check, that returns http status 2xx or 3xx as healthy
   115  type HealthCheckConf struct {
   116  	Custom                  bool
   117  	FailureThreshold        int64
   118  	PubDns_HealthCheck_Type sdhealthchecktype.SdHealthCheckType
   119  	PubDns_HealthCheck_Path string
   120  }
   121  
   122  // ================================================================================================================
   123  // STRUCTS FUNCTIONS
   124  // ================================================================================================================
   125  
   126  // ----------------------------------------------------------------------------------------------------------------
   127  // utility functions
   128  // ----------------------------------------------------------------------------------------------------------------
   129  
   130  // Connect will establish a connection to the CloudMap service
   131  func (sd *CloudMap) Connect(parentSegment ...*xray.XRayParentSegment) (err error) {
   132  	if xray.XRayServiceOn() {
   133  		if len(parentSegment) > 0 {
   134  			sd._parentSegment = parentSegment[0]
   135  		}
   136  
   137  		seg := xray.NewSegment("Cloudmap-Connect", sd._parentSegment)
   138  		defer seg.Close()
   139  		defer func() {
   140  			_ = seg.Seg.AddMetadata("Cloudmap-AWS-Region", sd.AwsRegion)
   141  
   142  			if err != nil {
   143  				_ = seg.Seg.AddError(err)
   144  			}
   145  		}()
   146  
   147  		err = sd.connectInternal()
   148  
   149  		if err == nil {
   150  			awsxray.AWS(sd.sdClient.Client)
   151  		}
   152  
   153  		return err
   154  	} else {
   155  		return sd.connectInternal()
   156  	}
   157  }
   158  
   159  // Connect will establish a connection to the CloudMap service
   160  func (sd *CloudMap) connectInternal() error {
   161  	// clean up prior sd client
   162  	sd.sdClient = nil
   163  
   164  	if !sd.AwsRegion.Valid() || sd.AwsRegion == awsregion.UNKNOWN {
   165  		return errors.New("Connect To CloudMap Failed: (AWS Session Error) " + "Region is Required")
   166  	}
   167  
   168  	// create custom http2 client if needed
   169  	var httpCli *http.Client
   170  	var httpErr error
   171  
   172  	if sd.HttpOptions == nil {
   173  		sd.HttpOptions = new(awshttp2.HttpClientSettings)
   174  	}
   175  
   176  	// use custom http2 client
   177  	h2 := &awshttp2.AwsHttp2Client{
   178  		Options: sd.HttpOptions,
   179  	}
   180  
   181  	if httpCli, httpErr = h2.NewHttp2Client(); httpErr != nil {
   182  		return errors.New("Connect to CloudMap Failed: (AWS Session Error) " + "Create Custom Http2 Client Errored = " + httpErr.Error())
   183  	}
   184  
   185  	// establish aws session connection
   186  	if sess, err := session.NewSession(
   187  		&aws.Config{
   188  			Region:     aws.String(sd.AwsRegion.Key()),
   189  			HTTPClient: httpCli,
   190  		}); err != nil {
   191  		// aws session error
   192  		return errors.New("Connect To CloudMap Failed: (AWS Session Error) " + err.Error())
   193  	} else {
   194  		// create cached objects for shared use
   195  		sd.sdClient = servicediscovery.New(sess)
   196  
   197  		if sd.sdClient == nil {
   198  			return errors.New("Connect To CloudMap Client Failed: (New CloudMap Client Connection) " + "Connection Object Nil")
   199  		}
   200  
   201  		return nil
   202  	}
   203  }
   204  
   205  // Disconnect clear client
   206  func (sd *CloudMap) Disconnect() {
   207  	sd.sdClient = nil
   208  }
   209  
   210  // toTags converts map of tags to slice of tags
   211  func (sd *CloudMap) toTags(tagsMap map[string]string) (t []*servicediscovery.Tag) {
   212  	if tagsMap != nil {
   213  		for k, v := range tagsMap {
   214  			t = append(t, &servicediscovery.Tag{
   215  				Key:   aws.String(k),
   216  				Value: aws.String(v),
   217  			})
   218  		}
   219  	}
   220  	return
   221  }
   222  
   223  // UpdateParentSegment updates this struct's xray parent segment, if no parent segment, set nil
   224  func (sd *CloudMap) UpdateParentSegment(parentSegment *xray.XRayParentSegment) {
   225  	sd._parentSegment = parentSegment
   226  }
   227  
   228  // ----------------------------------------------------------------------------------------------------------------
   229  // namespace functions
   230  // ----------------------------------------------------------------------------------------------------------------
   231  
   232  // CreateHttpNamespace creates an http namespace for AWS cloud map
   233  //
   234  // Service instances registered to http namespace can be discovered using DiscoverInstances(),
   235  //
   236  //	however, service instances cannot be discovered via dns
   237  //
   238  // Parameters:
   239  //  1. name = (required) name of the http namespace to create
   240  //  2. creatorRequestId = (required) random and unique string to identify this create namespace action (such as uuid)
   241  //  3. description = (optional) http namespace description
   242  //  4. tags = (optional) one or more key value pairs to store as namespace tags
   243  //  5. timeOutDuration = (optional) maximum time before timeout via context
   244  //
   245  // Return Values:
   246  //  1. operationId = string representing the identifier to be used to check on operation status at a later time
   247  //  2. err = contains error info if error was encountered
   248  func (sd *CloudMap) CreateHttpNamespace(name string,
   249  	creatorRequestId string,
   250  	description string,
   251  	tags map[string]string,
   252  	timeOutDuration ...time.Duration) (operationId string, err error) {
   253  	segCtx := context.Background()
   254  	segCtxSet := false
   255  
   256  	seg := xray.NewSegmentNullable("Cloudmap-CreateHttpNamespace", sd._parentSegment)
   257  
   258  	if seg != nil {
   259  		segCtx = seg.Ctx
   260  		segCtxSet = true
   261  
   262  		defer seg.Close()
   263  		defer func() {
   264  			_ = seg.Seg.AddMetadata("Cloudmap-CreateHttpNamespace-Name", name)
   265  			_ = seg.Seg.AddMetadata("Cloudmap-CreateHttpNamespace-CreatorRequestID", creatorRequestId)
   266  			_ = seg.Seg.AddMetadata("Cloudmap-CreateHttpNamespace-Result-OperationID", operationId)
   267  
   268  			if err != nil {
   269  				_ = seg.Seg.AddError(err)
   270  			}
   271  		}()
   272  	}
   273  
   274  	// validate
   275  	if sd.sdClient == nil {
   276  		err = fmt.Errorf("CloudMap CreateHttpNamespace Failed: " + "SD Client is Required")
   277  		return "", err
   278  	}
   279  
   280  	if util.LenTrim(name) == 0 {
   281  		err = fmt.Errorf("CloudMap CreateHttpNamespace Failed: " + "Name is Required")
   282  		return "", err
   283  	}
   284  
   285  	if util.LenTrim(creatorRequestId) == 0 {
   286  		err = fmt.Errorf("CloudMap CreateHttpNamespace Failed: " + "CreatorRequestId is Required")
   287  		return "", err
   288  	}
   289  
   290  	// define input
   291  	input := &servicediscovery.CreateHttpNamespaceInput{
   292  		Name:             aws.String(name),
   293  		CreatorRequestId: aws.String(creatorRequestId),
   294  	}
   295  
   296  	if util.LenTrim(description) > 0 {
   297  		input.Description = aws.String(description)
   298  	}
   299  
   300  	if tags != nil {
   301  		t := sd.toTags(tags)
   302  
   303  		if len(t) > 0 {
   304  			if len(t) > 50 {
   305  				err = fmt.Errorf("CloudMap CreateHttpNamespace Failed: " + "Tags Maximum Entries is 50")
   306  				return "", err
   307  			}
   308  
   309  			input.Tags = t
   310  		}
   311  	}
   312  
   313  	// invoke action
   314  	var output *servicediscovery.CreateHttpNamespaceOutput
   315  
   316  	if len(timeOutDuration) > 0 {
   317  		ctx, cancel := context.WithTimeout(segCtx, timeOutDuration[0])
   318  		defer cancel()
   319  
   320  		output, err = sd.sdClient.CreateHttpNamespaceWithContext(ctx, input)
   321  	} else {
   322  		if segCtxSet {
   323  			output, err = sd.sdClient.CreateHttpNamespaceWithContext(segCtx, input)
   324  		} else {
   325  			output, err = sd.sdClient.CreateHttpNamespace(input)
   326  		}
   327  	}
   328  
   329  	if err != nil {
   330  		err = fmt.Errorf("CloudMap CreateHttpNamespace Failed: (Create Action) " + err.Error())
   331  		return "", err
   332  	}
   333  
   334  	// action completed
   335  	return *output.OperationId, nil
   336  }
   337  
   338  // CreatePrivateDnsNamespace creates a private dns based namespace, visible only inside a specified aws vpc,
   339  //
   340  //	this namespace defines service naming scheme,
   341  //	for example:
   342  //		if namespace is named as 'example.com', and service is named as 'xyz-service',
   343  //		the resulting dns name for the service will be 'xyz-service.example.com'
   344  //
   345  // Parameters:
   346  //  1. name = (required) name of the private dns namespace to create
   347  //  2. creatorRequestId = (required) random and unique string to identify this create namespace action (such as uuid)
   348  //  3. vpc = (required) aws vpc id that this private dns associated with
   349  //  4. description = (optional) private dns namespace description
   350  //  5. tags = (optional) one or more key value pairs to store as namespace tags
   351  //  6. timeOutDuration = (optional) maximum time before timeout via context
   352  //
   353  // Return Values:
   354  //  1. operationId = string representing the identifier to be used to check on operation status at a later time
   355  //  2. err = contains error info if error was encountered
   356  func (sd *CloudMap) CreatePrivateDnsNamespace(name string,
   357  	creatorRequestId string,
   358  	vpc string,
   359  	description string,
   360  	tags map[string]string,
   361  	timeOutDuration ...time.Duration) (operationId string, err error) {
   362  	segCtx := context.Background()
   363  	segCtxSet := false
   364  
   365  	seg := xray.NewSegmentNullable("Cloudmap-CreatePrivateDnsNamespace", sd._parentSegment)
   366  
   367  	if seg != nil {
   368  		segCtx = seg.Ctx
   369  		segCtxSet = true
   370  
   371  		defer seg.Close()
   372  		defer func() {
   373  			_ = seg.Seg.AddMetadata("Cloudmap-CreatePrivateDnsNamespace-Name", name)
   374  			_ = seg.Seg.AddMetadata("Cloudmap-CreatePrivateDnsNamespace-CreatorRequestID", creatorRequestId)
   375  			_ = seg.Seg.AddMetadata("Cloudmap-CreatePrivateDnsNamespace-VPC", vpc)
   376  			_ = seg.Seg.AddMetadata("Cloudmap-CreatePrivateDnsNamespace-Result-OperationID", operationId)
   377  
   378  			if err != nil {
   379  				_ = seg.Seg.AddError(err)
   380  			}
   381  		}()
   382  	}
   383  
   384  	// validate
   385  	if sd.sdClient == nil {
   386  		err = errors.New("CloudMap CreatePrivateDnsNamespace Failed: " + "SD Client is Required")
   387  		return "", err
   388  	}
   389  
   390  	if util.LenTrim(name) == 0 {
   391  		err = errors.New("CloudMap CreatePrivateDnsNamespace Failed: " + "Name is Required")
   392  		return "", err
   393  	}
   394  
   395  	if util.LenTrim(creatorRequestId) == 0 {
   396  		err = errors.New("CloudMap CreatePrivateDnsNamespace Failed: " + "CreatorRequestId is Required")
   397  		return "", err
   398  	}
   399  
   400  	if util.LenTrim(vpc) == 0 {
   401  		err = errors.New("CloudMap CreatePrivateDnsNamespace Failed: " + "VPC is Required")
   402  		return "", err
   403  	}
   404  
   405  	// define input
   406  	input := &servicediscovery.CreatePrivateDnsNamespaceInput{
   407  		Name:             aws.String(name),
   408  		CreatorRequestId: aws.String(creatorRequestId),
   409  		Vpc:              aws.String(vpc),
   410  	}
   411  
   412  	if util.LenTrim(description) > 0 {
   413  		input.Description = aws.String(description)
   414  	}
   415  
   416  	if tags != nil {
   417  		t := sd.toTags(tags)
   418  
   419  		if len(t) > 0 {
   420  			if len(t) > 50 {
   421  				err = errors.New("CloudMap CreatePrivateDnsNamespace Failed: " + "Tags Maximum Entries is 50")
   422  				return "", err
   423  			}
   424  
   425  			input.Tags = t
   426  		}
   427  	}
   428  
   429  	// invoke action
   430  	var output *servicediscovery.CreatePrivateDnsNamespaceOutput
   431  
   432  	if len(timeOutDuration) > 0 {
   433  		ctx, cancel := context.WithTimeout(segCtx, timeOutDuration[0])
   434  		defer cancel()
   435  
   436  		output, err = sd.sdClient.CreatePrivateDnsNamespaceWithContext(ctx, input)
   437  	} else {
   438  		if segCtxSet {
   439  			output, err = sd.sdClient.CreatePrivateDnsNamespaceWithContext(segCtx, input)
   440  		} else {
   441  			output, err = sd.sdClient.CreatePrivateDnsNamespace(input)
   442  		}
   443  	}
   444  
   445  	if err != nil {
   446  		err = errors.New("CloudMap CreatePrivateDnsNamespace Failed: (Create Action) " + err.Error())
   447  		return "", err
   448  	}
   449  
   450  	// action completed
   451  	return *output.OperationId, nil
   452  }
   453  
   454  // CreatePublicDnsNamespace creates a public dns based namespace, accessible via the public internet,
   455  //
   456  //	this namespace defines service naming scheme,
   457  //	for example:
   458  //		if namespace is named as 'example.com', and service is named as 'xyz-service',
   459  //		the resulting dns name for the service will be 'xyz-service.example.com'
   460  //
   461  // Parameters:
   462  //  1. name = (required) name of the public dns namespace to create
   463  //  2. creatorRequestId = (required) random and unique string to identify this create namespace action (such as uuid)
   464  //  3. description = (optional) public dns namespace description
   465  //  4. tags = (optional) one or more key value pairs to store as namespace tags
   466  //  5. timeOutDuration = (optional) maximum time before timeout via context
   467  //
   468  // Return Values:
   469  //  1. operationId = string representing the identifier to be used to check on operation status at a later time
   470  //  2. err = contains error info if error was encountered
   471  func (sd *CloudMap) CreatePublicDnsNamespace(name string,
   472  	creatorRequestId string,
   473  	description string,
   474  	tags map[string]string,
   475  	timeOutDuration ...time.Duration) (operationId string, err error) {
   476  	segCtx := context.Background()
   477  	segCtxSet := false
   478  
   479  	seg := xray.NewSegmentNullable("Cloudmap-CreatePublicDnsNamespace", sd._parentSegment)
   480  
   481  	if seg != nil {
   482  		segCtx = seg.Ctx
   483  		segCtxSet = true
   484  
   485  		defer seg.Close()
   486  		defer func() {
   487  			_ = seg.Seg.AddMetadata("Cloudmap-CreatePublicDnsNamespace-Name", name)
   488  			_ = seg.Seg.AddMetadata("Cloudmap-CreatePublicDnsNamespace-CreatorRequestID", creatorRequestId)
   489  			_ = seg.Seg.AddMetadata("Cloudmap-CreatePublicDnsNamespace-Result-OperationID", operationId)
   490  
   491  			if err != nil {
   492  				_ = seg.Seg.AddError(err)
   493  			}
   494  		}()
   495  	}
   496  
   497  	// validate
   498  	if sd.sdClient == nil {
   499  		err = errors.New("CloudMap CreatePublicDnsNamespace Failed: " + "SD Client is Required")
   500  		return "", err
   501  	}
   502  
   503  	if util.LenTrim(name) == 0 {
   504  		err = errors.New("CloudMap CreatePublicDnsNamespace Failed: " + "Name is Required")
   505  		return "", err
   506  	}
   507  
   508  	if util.LenTrim(creatorRequestId) == 0 {
   509  		err = errors.New("CloudMap CreatePublicDnsNamespace Failed: " + "CreatorRequestId is Required")
   510  		return "", err
   511  	}
   512  
   513  	// define input
   514  	input := &servicediscovery.CreatePublicDnsNamespaceInput{
   515  		Name:             aws.String(name),
   516  		CreatorRequestId: aws.String(creatorRequestId),
   517  	}
   518  
   519  	if util.LenTrim(description) > 0 {
   520  		input.Description = aws.String(description)
   521  	}
   522  
   523  	if tags != nil {
   524  		t := sd.toTags(tags)
   525  
   526  		if len(t) > 0 {
   527  			if len(t) > 50 {
   528  				err = errors.New("CloudMap CreatePublicDnsNamespace Failed: " + "Tags Maximum Entries is 50")
   529  				return "", err
   530  			}
   531  
   532  			input.Tags = t
   533  		}
   534  	}
   535  
   536  	// invoke action
   537  	var output *servicediscovery.CreatePublicDnsNamespaceOutput
   538  
   539  	if len(timeOutDuration) > 0 {
   540  		ctx, cancel := context.WithTimeout(segCtx, timeOutDuration[0])
   541  		defer cancel()
   542  
   543  		output, err = sd.sdClient.CreatePublicDnsNamespaceWithContext(ctx, input)
   544  	} else {
   545  		if segCtxSet {
   546  			output, err = sd.sdClient.CreatePublicDnsNamespaceWithContext(segCtx, input)
   547  		} else {
   548  			output, err = sd.sdClient.CreatePublicDnsNamespace(input)
   549  		}
   550  	}
   551  
   552  	if err != nil {
   553  		err = errors.New("CloudMap CreatePublicDnsNamespace Failed: (Create Action) " + err.Error())
   554  		return "", err
   555  	}
   556  
   557  	// action completed
   558  	return *output.OperationId, nil
   559  }
   560  
   561  // GetNamespace gets the information about a specific namespace
   562  //
   563  // Parameters:
   564  //  1. namespaceId = (required) namespace id used for search
   565  //  2. timeOutDuration = (optional) maximum time before timeout via context
   566  //
   567  // Return Values:
   568  //  1. namespace = sd namespace object found
   569  //  2. err = error info if any
   570  func (sd *CloudMap) GetNamespace(namespaceId string, timeOutDuration ...time.Duration) (namespace *servicediscovery.Namespace, err error) {
   571  	segCtx := context.Background()
   572  	segCtxSet := false
   573  
   574  	seg := xray.NewSegmentNullable("Cloudmap-GetNamespace", sd._parentSegment)
   575  
   576  	if seg != nil {
   577  		segCtx = seg.Ctx
   578  		segCtxSet = true
   579  
   580  		defer seg.Close()
   581  		defer func() {
   582  			_ = seg.Seg.AddMetadata("Cloudmap-GetNamespace-NamespaceID", namespaceId)
   583  			_ = seg.Seg.AddMetadata("Cloudmap-GetNamespace-Result-NamespaceObject", namespace)
   584  
   585  			if err != nil {
   586  				_ = seg.Seg.AddError(err)
   587  			}
   588  		}()
   589  	}
   590  
   591  	// validate
   592  	if sd.sdClient == nil {
   593  		err = errors.New("CloudMap GetNamespace Failed: " + "SD Client is Required")
   594  		return nil, err
   595  	}
   596  
   597  	if util.LenTrim(namespaceId) == 0 {
   598  		err = errors.New("CloudMap GetNamespace Failed: " + "NamespaceId is Required")
   599  		return nil, err
   600  	}
   601  
   602  	// define input
   603  	input := &servicediscovery.GetNamespaceInput{
   604  		Id: aws.String(namespaceId),
   605  	}
   606  
   607  	// invoke action
   608  	var output *servicediscovery.GetNamespaceOutput
   609  
   610  	if len(timeOutDuration) > 0 {
   611  		ctx, cancel := context.WithTimeout(segCtx, timeOutDuration[0])
   612  		defer cancel()
   613  
   614  		output, err = sd.sdClient.GetNamespaceWithContext(ctx, input)
   615  	} else {
   616  		if segCtxSet {
   617  			output, err = sd.sdClient.GetNamespaceWithContext(segCtx, input)
   618  		} else {
   619  			output, err = sd.sdClient.GetNamespace(input)
   620  		}
   621  	}
   622  
   623  	if err != nil {
   624  		// handle error
   625  		err = errors.New("CloudMap GetNamespace Failed: (Get Action) " + err.Error())
   626  		return nil, err
   627  	}
   628  
   629  	return output.Namespace, nil
   630  }
   631  
   632  // ListNamespaces gets summary information about namespaces created already
   633  //
   634  // Parameters:
   635  //  1. filter = (optional) specifies namespace filter options
   636  //  2. maxResults = (optional) specifies maximum count to return
   637  //  3. nextToken = (optional) if initial action, leave blank; if this is a subsequent action to get more, input the moreNextToken returned from a prior action
   638  //  4. timeOutDuration = (optional) maximum time before timeout via context
   639  //
   640  // Return Values:
   641  //  1. namespaces = slice of sd namespace summary objects
   642  //  2. moreNextToken = if more data exists, this token can be used in a subsequent action via nextToken parameter
   643  //  3. err = error info if any
   644  func (sd *CloudMap) ListNamespaces(filter *sdnamespacefilter.SdNamespaceFilter,
   645  	maxResults *int64,
   646  	nextToken *string,
   647  	timeOutDuration ...time.Duration) (namespaces []*servicediscovery.NamespaceSummary, moreNextToken string, err error) {
   648  	segCtx := context.Background()
   649  	segCtxSet := false
   650  
   651  	seg := xray.NewSegmentNullable("Cloudmap-ListNamespaces", sd._parentSegment)
   652  
   653  	if seg != nil {
   654  		segCtx = seg.Ctx
   655  		segCtxSet = true
   656  
   657  		defer seg.Close()
   658  		defer func() {
   659  			_ = seg.Seg.AddMetadata("Cloudmap-ListNamespaces-Filter", filter)
   660  			_ = seg.Seg.AddMetadata("Cloudmap-ListNamespaces-Max-Results", maxResults)
   661  			_ = seg.Seg.AddMetadata("Cloudmap-ListNamespaces-Next-Token", nextToken)
   662  			_ = seg.Seg.AddMetadata("Cloudmap-ListNamespaces-Result-Namespaces", namespaces)
   663  			_ = seg.Seg.AddMetadata("Cloudmap-ListNamespaces-Result-Next-Token", moreNextToken)
   664  
   665  			if err != nil {
   666  				_ = seg.Seg.AddError(err)
   667  			}
   668  		}()
   669  	}
   670  
   671  	// validate
   672  	if sd.sdClient == nil {
   673  		err = errors.New("CloudMap ListNamespaces Failed: " + "SD Client is Required")
   674  		return nil, "", err
   675  	}
   676  
   677  	if maxResults != nil {
   678  		if *maxResults <= 0 {
   679  			err = errors.New("CloudMap ListNamespaces Failed: " + "MaxResults Must Be Greater Than Zero")
   680  			return nil, "", err
   681  		}
   682  	}
   683  
   684  	// define input
   685  	input := &servicediscovery.ListNamespacesInput{}
   686  
   687  	if filter != nil && filter.Valid() && *filter != sdnamespacefilter.UNKNOWN {
   688  		input.Filters = []*servicediscovery.NamespaceFilter{
   689  			{
   690  				Name: aws.String("TYPE"),
   691  			},
   692  		}
   693  
   694  		switch *filter {
   695  		case sdnamespacefilter.PrivateDnsNamespace:
   696  			input.Filters[0].Condition = aws.String("EQ")
   697  			input.Filters[0].Values = []*string{
   698  				aws.String("DNS_PRIVATE"),
   699  			}
   700  		case sdnamespacefilter.PublicDnsNamespace:
   701  			input.Filters[0].Condition = aws.String("EQ")
   702  			input.Filters[0].Values = []*string{
   703  				aws.String("DNS_PUBLIC"),
   704  			}
   705  		case sdnamespacefilter.Both:
   706  			input.Filters[0].Condition = aws.String("IN")
   707  			input.Filters[0].Values = []*string{
   708  				aws.String("DNS_PRIVATE"),
   709  				aws.String("DNS_PUBLIC"),
   710  			}
   711  		}
   712  	}
   713  
   714  	if maxResults != nil {
   715  		input.MaxResults = maxResults
   716  	}
   717  
   718  	if nextToken != nil {
   719  		if util.LenTrim(*nextToken) > 0 {
   720  			input.NextToken = nextToken
   721  		}
   722  	}
   723  
   724  	// invoke action
   725  	var output *servicediscovery.ListNamespacesOutput
   726  
   727  	if len(timeOutDuration) > 0 {
   728  		ctx, cancel := context.WithTimeout(segCtx, timeOutDuration[0])
   729  		defer cancel()
   730  
   731  		output, err = sd.sdClient.ListNamespacesWithContext(ctx, input)
   732  	} else {
   733  		if segCtxSet {
   734  			output, err = sd.sdClient.ListNamespacesWithContext(segCtx, input)
   735  		} else {
   736  			output, err = sd.sdClient.ListNamespaces(input)
   737  		}
   738  	}
   739  
   740  	if err != nil {
   741  		// handle error
   742  		err = errors.New("CloudMap ListNamespaces Failed: (List Action) " + err.Error())
   743  		return nil, "", err
   744  	}
   745  
   746  	return output.Namespaces, *output.NextToken, nil
   747  }
   748  
   749  // ListNamespacesPages gets summary information about namespaces created already
   750  // (issues multiple page requests until max results is met or all data is retrieved)
   751  //
   752  // Parameters:
   753  //  1. filter = (optional) specifies namespace filter options
   754  //  2. maxResults = (optional) specifies maximum count to return
   755  //  3. nextToken = (optional) if initial action, leave blank; if this is a subsequent action to get more, input the moreNextToken returned from a prior action
   756  //  4. timeOutDuration = (optional) maximum time before timeout via context
   757  //
   758  // Return Values:
   759  //  1. namespaces = slice of sd namespace summary objects
   760  //  2. moreNextToken = if more data exists, this token can be used in a subsequent action via nextToken parameter
   761  //  3. err = error info if any
   762  func (sd *CloudMap) ListNamespacesPages(filter *sdnamespacefilter.SdNamespaceFilter,
   763  	maxResults *int64,
   764  	nextToken *string,
   765  	timeOutDuration ...time.Duration) (namespaces []*servicediscovery.NamespaceSummary, moreNextToken string, err error) {
   766  	segCtx := context.Background()
   767  	segCtxSet := false
   768  
   769  	seg := xray.NewSegmentNullable("Cloudmap-ListNamespacesPages", sd._parentSegment)
   770  
   771  	if seg != nil {
   772  		segCtx = seg.Ctx
   773  		segCtxSet = true
   774  
   775  		defer seg.Close()
   776  		defer func() {
   777  			_ = seg.Seg.AddMetadata("Cloudmap-ListNamespacesPages-Filter", filter)
   778  			_ = seg.Seg.AddMetadata("Cloudmap-ListNamespacesPages-Max-Results", maxResults)
   779  			_ = seg.Seg.AddMetadata("Cloudmap-ListNamespacesPages-Next-Token", nextToken)
   780  			_ = seg.Seg.AddMetadata("Cloudmap-ListNamespacesPages-Result-Namespaces", namespaces)
   781  			_ = seg.Seg.AddMetadata("Cloudmap-ListNamespacesPages-Result-Next-Token", moreNextToken)
   782  
   783  			if err != nil {
   784  				_ = seg.Seg.AddError(err)
   785  			}
   786  		}()
   787  	}
   788  
   789  	// validate
   790  	if sd.sdClient == nil {
   791  		err = errors.New("CloudMap ListNamespacesPages Failed: " + "SD Client is Required")
   792  		return nil, "", err
   793  	}
   794  
   795  	if maxResults != nil {
   796  		if *maxResults <= 0 {
   797  			err = errors.New("CloudMap ListNamespacesPages Failed: " + "MaxResults Must Be Greater Than Zero")
   798  			return nil, "", err
   799  		}
   800  	}
   801  
   802  	// define input
   803  	input := &servicediscovery.ListNamespacesInput{}
   804  
   805  	if filter != nil && filter.Valid() && *filter != sdnamespacefilter.UNKNOWN {
   806  		input.Filters = []*servicediscovery.NamespaceFilter{
   807  			{
   808  				Name: aws.String("TYPE"),
   809  			},
   810  		}
   811  
   812  		switch *filter {
   813  		case sdnamespacefilter.PrivateDnsNamespace:
   814  			input.Filters[0].Condition = aws.String("EQ")
   815  			input.Filters[0].Values = []*string{
   816  				aws.String("DNS_PRIVATE"),
   817  			}
   818  		case sdnamespacefilter.PublicDnsNamespace:
   819  			input.Filters[0].Condition = aws.String("EQ")
   820  			input.Filters[0].Values = []*string{
   821  				aws.String("DNS_PUBLIC"),
   822  			}
   823  		case sdnamespacefilter.Both:
   824  			input.Filters[0].Condition = aws.String("IN")
   825  			input.Filters[0].Values = []*string{
   826  				aws.String("DNS_PRIVATE"),
   827  				aws.String("DNS_PUBLIC"),
   828  			}
   829  		}
   830  	}
   831  
   832  	if maxResults != nil {
   833  		input.MaxResults = maxResults
   834  	}
   835  
   836  	if nextToken != nil {
   837  		if util.LenTrim(*nextToken) > 0 {
   838  			input.NextToken = nextToken
   839  		}
   840  	}
   841  
   842  	// invoke action
   843  	fn := func(pageOutput *servicediscovery.ListNamespacesOutput, lastPage bool) bool {
   844  		if pageOutput != nil {
   845  			moreNextToken = *pageOutput.NextToken
   846  			namespaces = append(namespaces, pageOutput.Namespaces...)
   847  		}
   848  
   849  		return !lastPage
   850  	}
   851  
   852  	if len(timeOutDuration) > 0 {
   853  		ctx, cancel := context.WithTimeout(segCtx, timeOutDuration[0])
   854  		defer cancel()
   855  
   856  		err = sd.sdClient.ListNamespacesPagesWithContext(ctx, input, fn)
   857  	} else {
   858  		if segCtxSet {
   859  			err = sd.sdClient.ListNamespacesPagesWithContext(segCtx, input, fn)
   860  		} else {
   861  			err = sd.sdClient.ListNamespacesPages(input, fn)
   862  		}
   863  	}
   864  
   865  	if err != nil {
   866  		// handle error
   867  		err = errors.New("CloudMap ListNamespacesPages Failed: (ListPages Action) " + err.Error())
   868  		return nil, "", err
   869  	}
   870  
   871  	return namespaces, moreNextToken, nil
   872  }
   873  
   874  // DeleteNamespace deletes an existing namespace, however if namespace still has attached services, then action will fail
   875  //
   876  // Parameters:
   877  //  1. namespaceId = (required) namespace id to delete
   878  //
   879  // Return Values:
   880  //  1. operationId = represents the operation to be used for status check on this action via GetOperation()
   881  //  2. err = error info if any
   882  func (sd *CloudMap) DeleteNamespace(namespaceId string, timeOutDuration ...time.Duration) (operationId string, err error) {
   883  	segCtx := context.Background()
   884  	segCtxSet := false
   885  
   886  	seg := xray.NewSegmentNullable("Cloudmap-DeleteNamespace", sd._parentSegment)
   887  
   888  	if seg != nil {
   889  		segCtx = seg.Ctx
   890  		segCtxSet = true
   891  
   892  		defer seg.Close()
   893  		defer func() {
   894  			_ = seg.Seg.AddMetadata("Cloudmap-DeleteNamespace-NamespaceID", namespaceId)
   895  			_ = seg.Seg.AddMetadata("Cloudmap-DeleteNamespace-Result-OperationID", operationId)
   896  
   897  			if err != nil {
   898  				_ = seg.Seg.AddError(err)
   899  			}
   900  		}()
   901  	}
   902  
   903  	// validate
   904  	if sd.sdClient == nil {
   905  		err = errors.New("CloudMap DeleteNamespace Failed: " + "SD Client is Required")
   906  		return "", err
   907  	}
   908  
   909  	if util.LenTrim(namespaceId) == 0 {
   910  		err = errors.New("CloudMap DeleteNamespace Failed: " + "NamespaceId is Required")
   911  		return "", err
   912  	}
   913  
   914  	// define input
   915  	input := &servicediscovery.DeleteNamespaceInput{
   916  		Id: aws.String(namespaceId),
   917  	}
   918  
   919  	// invoke action
   920  	var output *servicediscovery.DeleteNamespaceOutput
   921  
   922  	if len(timeOutDuration) > 0 {
   923  		ctx, cancel := context.WithTimeout(segCtx, timeOutDuration[0])
   924  		defer cancel()
   925  
   926  		output, err = sd.sdClient.DeleteNamespaceWithContext(ctx, input)
   927  	} else {
   928  		if segCtxSet {
   929  			output, err = sd.sdClient.DeleteNamespaceWithContext(segCtx, input)
   930  		} else {
   931  			output, err = sd.sdClient.DeleteNamespace(input)
   932  		}
   933  	}
   934  
   935  	if err != nil {
   936  		// handle error
   937  		err = errors.New("CloudMap DeleteNamespace Failed: (Delete Action) " + err.Error())
   938  		return "", err
   939  	}
   940  
   941  	return *output.OperationId, nil
   942  }
   943  
   944  // ----------------------------------------------------------------------------------------------------------------
   945  // service functions
   946  // ----------------------------------------------------------------------------------------------------------------
   947  
   948  // CreateService creates a service under a specific namespace
   949  //
   950  // # After service is created, use RegisterInstance() to register an instance for the given service
   951  //
   952  // Parameters:
   953  //  1. name = (required) name of the service to create, under the given namespaceId
   954  //  2. creatorRequestId = (required) random and unique string to identify this create service action (such as uuid)
   955  //  3. namespaceId = (required) namespace that this service be created under
   956  //  4. dnsConf = (conditional) required for public and private dns namespaces, configures the dns parameters for this service
   957  //  5. healthCheckConf = (optional) nil will not set health check, otherwise sets a health check condition for this services' instances
   958  //  6. description = (optional) public dns namespace description
   959  //  7. tags = (optional) one or more key value pairs to store as namespace tags
   960  //  8. timeOutDuration = (optional) maximum time before timeout via context
   961  //
   962  // Return Values:
   963  //  1. service = service object that was created
   964  //  2. err = contains error info if error was encountered
   965  func (sd *CloudMap) CreateService(name string,
   966  	creatorRequestId string,
   967  	namespaceId string,
   968  	dnsConf *DnsConf,
   969  	healthCheckConf *HealthCheckConf,
   970  	description string,
   971  	tags map[string]string,
   972  	timeOutDuration ...time.Duration) (service *servicediscovery.Service, err error) {
   973  	segCtx := context.Background()
   974  	segCtxSet := false
   975  
   976  	seg := xray.NewSegmentNullable("Cloudmap-CreateService", sd._parentSegment)
   977  
   978  	if seg != nil {
   979  		segCtx = seg.Ctx
   980  		segCtxSet = true
   981  
   982  		defer seg.Close()
   983  		defer func() {
   984  			_ = seg.Seg.AddMetadata("Cloudmap-CreateService-ServiceName", name)
   985  			_ = seg.Seg.AddMetadata("Cloudmap-CreateService-CreatorRequestID", creatorRequestId)
   986  			_ = seg.Seg.AddMetadata("Cloudmap-CreateService-NamespaceID", namespaceId)
   987  			_ = seg.Seg.AddMetadata("Cloudmap-CreateService-DNSConf", dnsConf)
   988  			_ = seg.Seg.AddMetadata("Cloudmap-CreateService-HealthCheckConf", healthCheckConf)
   989  			_ = seg.Seg.AddMetadata("Cloudmap-CreateService-Result-ServiceObject", service)
   990  
   991  			if err != nil {
   992  				_ = seg.Seg.AddError(err)
   993  			}
   994  		}()
   995  	}
   996  
   997  	// validate
   998  	if sd.sdClient == nil {
   999  		err = errors.New("CloudMap CreateService Failed: " + "SD Client is Required")
  1000  		return nil, err
  1001  	}
  1002  
  1003  	if util.LenTrim(name) == 0 {
  1004  		err = errors.New("CloudMap CreateService Failed: " + "Name is Required")
  1005  		return nil, err
  1006  	}
  1007  
  1008  	if util.LenTrim(creatorRequestId) == 0 {
  1009  		err = errors.New("CloudMap CreateService Failed: " + "CreatorRequestId is Required")
  1010  		return nil, err
  1011  	}
  1012  
  1013  	if util.LenTrim(namespaceId) == 0 {
  1014  		err = errors.New("CloudMap CreateService Failed: " + "NamespaceId is Required")
  1015  		return nil, err
  1016  	}
  1017  
  1018  	if dnsConf != nil {
  1019  		// dns conf set, public or private dns namespace only
  1020  		if dnsConf.TTL <= 0 {
  1021  			dnsConf.TTL = 300 // default to 5 minutes ttl if not specified
  1022  		}
  1023  
  1024  		if healthCheckConf != nil {
  1025  			if healthCheckConf.FailureThreshold <= 0 {
  1026  				healthCheckConf.FailureThreshold = 1
  1027  			}
  1028  
  1029  			if healthCheckConf.Custom {
  1030  				healthCheckConf.PubDns_HealthCheck_Type = sdhealthchecktype.UNKNOWN
  1031  				healthCheckConf.PubDns_HealthCheck_Path = ""
  1032  			} else {
  1033  				if !healthCheckConf.PubDns_HealthCheck_Type.Valid() || healthCheckConf.PubDns_HealthCheck_Type == sdhealthchecktype.UNKNOWN {
  1034  					err = errors.New("CloudMap CreateService Failed: " + "Public Dns Namespace Health Check Requires Endpoint Type")
  1035  					return nil, err
  1036  				}
  1037  
  1038  				if healthCheckConf.PubDns_HealthCheck_Type == sdhealthchecktype.TCP {
  1039  					healthCheckConf.PubDns_HealthCheck_Path = ""
  1040  				} else {
  1041  					if util.LenTrim(healthCheckConf.PubDns_HealthCheck_Path) == 0 {
  1042  						err = errors.New("CloudMap CreateService Failed: " + "Health Check Resource Path is Required for HTTP & HTTPS Types")
  1043  						return nil, err
  1044  					}
  1045  				}
  1046  			}
  1047  		}
  1048  	} else {
  1049  		// if dns is not defined, this is api only, health check must be custom
  1050  		if !healthCheckConf.Custom {
  1051  			err = errors.New("CloudMap CreateService Failed: " + "Route 53 Health Check is for Private or Public Dns Namespaces Only")
  1052  			return nil, err
  1053  		}
  1054  	}
  1055  
  1056  	// define input
  1057  	input := &servicediscovery.CreateServiceInput{
  1058  		Name:             aws.String(name),
  1059  		CreatorRequestId: aws.String(creatorRequestId),
  1060  		NamespaceId:      aws.String(namespaceId),
  1061  	}
  1062  
  1063  	if util.LenTrim(description) > 0 {
  1064  		input.Description = aws.String(description)
  1065  	}
  1066  
  1067  	if tags != nil {
  1068  		t := sd.toTags(tags)
  1069  
  1070  		if len(t) > 0 {
  1071  			if len(t) > 50 {
  1072  				err = errors.New("CloudMap CreateService Failed: " + "Tags Maximum Entries is 50")
  1073  				return nil, err
  1074  			}
  1075  
  1076  			input.Tags = t
  1077  		}
  1078  	}
  1079  
  1080  	if dnsConf != nil {
  1081  		routingPolicy := "MULTIVALUE"
  1082  
  1083  		if !dnsConf.MultiValue {
  1084  			routingPolicy = "WEIGHTED"
  1085  		}
  1086  
  1087  		dnsType := "A"
  1088  
  1089  		if dnsConf.SRV {
  1090  			dnsType = "SRV"
  1091  		}
  1092  
  1093  		input.DnsConfig = &servicediscovery.DnsConfig{
  1094  			RoutingPolicy: aws.String(routingPolicy),
  1095  			DnsRecords: []*servicediscovery.DnsRecord{
  1096  				{
  1097  					TTL:  aws.Int64(dnsConf.TTL),
  1098  					Type: aws.String(dnsType),
  1099  				},
  1100  			},
  1101  		}
  1102  	}
  1103  
  1104  	if healthCheckConf != nil {
  1105  		if healthCheckConf.Custom {
  1106  			// custom health config
  1107  			input.HealthCheckCustomConfig = &servicediscovery.HealthCheckCustomConfig{
  1108  				FailureThreshold: aws.Int64(healthCheckConf.FailureThreshold),
  1109  			}
  1110  		} else {
  1111  			// public dns health config
  1112  			input.HealthCheckConfig = &servicediscovery.HealthCheckConfig{
  1113  				FailureThreshold: aws.Int64(healthCheckConf.FailureThreshold),
  1114  				Type:             aws.String(healthCheckConf.PubDns_HealthCheck_Type.Key()),
  1115  			}
  1116  
  1117  			if util.LenTrim(healthCheckConf.PubDns_HealthCheck_Path) > 0 {
  1118  				input.HealthCheckConfig.SetResourcePath(healthCheckConf.PubDns_HealthCheck_Path)
  1119  			}
  1120  		}
  1121  	}
  1122  
  1123  	// invoke action
  1124  	var output *servicediscovery.CreateServiceOutput
  1125  
  1126  	if len(timeOutDuration) > 0 {
  1127  		ctx, cancel := context.WithTimeout(segCtx, timeOutDuration[0])
  1128  		defer cancel()
  1129  
  1130  		output, err = sd.sdClient.CreateServiceWithContext(ctx, input)
  1131  	} else {
  1132  		if segCtxSet {
  1133  			output, err = sd.sdClient.CreateServiceWithContext(segCtx, input)
  1134  		} else {
  1135  			output, err = sd.sdClient.CreateService(input)
  1136  		}
  1137  	}
  1138  
  1139  	if err != nil {
  1140  		// handle error
  1141  		err = errors.New("CloudMap CreateService Failed: (Create Action) " + err.Error())
  1142  		return nil, err
  1143  	}
  1144  
  1145  	return output.Service, nil
  1146  }
  1147  
  1148  // UpdateService submits request for the following operations:
  1149  //  1. update the TTL for existing dnsRecords configurations
  1150  //  2. add, update, or delete HealthCheckConfig for a specified service,
  1151  //     HealthCheckCustomConfig cannot be added, updated or deleted via UpdateService action
  1152  //
  1153  // Notes:
  1154  //  1. public and private dns namespaces,
  1155  //     a) if any existing dnsRecords or healthCheckConfig configurations are omitted from the UpdateService request,
  1156  //     those omitted configurations ARE deleted from the service
  1157  //     b) if any existing HealthCheckCustomConfig configurations are omitted from the UpdateService request,
  1158  //     the omitted configurations ARE NOT deleted from the service
  1159  //  2. when settings are updated for a service,
  1160  //     aws cloud map also updates the corresponding settings in all the records and health checks,
  1161  //     that were created by the given service
  1162  //
  1163  // Parameters:
  1164  //  1. serviceId = (required) service to update
  1165  //  2. dnsConfUpdate = (required) update dns config to this value, if nil, existing dns configuration will be removed from service
  1166  //  3. healthCheckConf = (optional) update health check config to this value, if nil, existing health check config will be removed from service
  1167  //  4. descriptionUpdate = (optional) service description to update, if nil, existing description will be removed from service
  1168  //  5. timeOutDuration = (optional) maximum time before timeout via context
  1169  //
  1170  // Return Values:
  1171  //  1. operationId = this action's operation id to be used in GetOperation for status check
  1172  //  2. err = contains error info if error was encountered
  1173  func (sd *CloudMap) UpdateService(serviceId string,
  1174  	dnsConfUpdate *DnsConf,
  1175  	healthCheckConfUpdate *HealthCheckConf,
  1176  	descriptionUpdate *string,
  1177  	timeOutDuration ...time.Duration) (operationId string, err error) {
  1178  	segCtx := context.Background()
  1179  	segCtxSet := false
  1180  
  1181  	seg := xray.NewSegmentNullable("Cloudmap-UpdateService", sd._parentSegment)
  1182  
  1183  	if seg != nil {
  1184  		segCtx = seg.Ctx
  1185  		segCtxSet = true
  1186  
  1187  		defer seg.Close()
  1188  		defer func() {
  1189  			_ = seg.Seg.AddMetadata("Cloudmap-UpdateService-ServiceID", serviceId)
  1190  			_ = seg.Seg.AddMetadata("Cloudmap-UpdateService-DNSConfUpdate", dnsConfUpdate)
  1191  			_ = seg.Seg.AddMetadata("Cloudmap-UpdateService-HealthCheckConfUpdate", healthCheckConfUpdate)
  1192  			_ = seg.Seg.AddMetadata("Cloudmap-UpdateService-DescriptionUpdate", descriptionUpdate)
  1193  			_ = seg.Seg.AddMetadata("Cloudmap-UpdateService-Result-OperationID", operationId)
  1194  
  1195  			if err != nil {
  1196  				_ = seg.Seg.AddError(err)
  1197  			}
  1198  		}()
  1199  	}
  1200  
  1201  	// validate
  1202  	if sd.sdClient == nil {
  1203  		err = errors.New("CloudMap UpdateService Failed: " + "SD Client is Required")
  1204  		return "", err
  1205  	}
  1206  
  1207  	if util.LenTrim(serviceId) == 0 {
  1208  		err = errors.New("CloudMap UpdateService Failed: " + "ServiceId is Required")
  1209  		return "", err
  1210  	}
  1211  
  1212  	if dnsConfUpdate == nil {
  1213  		err = errors.New("CloudMap UpdateService Failed: " + "Dns Config Update is Required")
  1214  		return "", err
  1215  	}
  1216  
  1217  	if healthCheckConfUpdate != nil && healthCheckConfUpdate.Custom {
  1218  		err = errors.New("CloudMap UpdateService Failed: " + "Health Check Custom Config Cannot Be Updated")
  1219  		return "", err
  1220  	}
  1221  
  1222  	// dns conf set, public or private dns namespace only
  1223  	if dnsConfUpdate.TTL <= 0 {
  1224  		dnsConfUpdate.TTL = 300 // default to 5 minutes ttl if not specified
  1225  	}
  1226  
  1227  	if healthCheckConfUpdate != nil {
  1228  		if healthCheckConfUpdate.FailureThreshold <= 0 {
  1229  			healthCheckConfUpdate.FailureThreshold = 1
  1230  		}
  1231  
  1232  		if !healthCheckConfUpdate.PubDns_HealthCheck_Type.Valid() || healthCheckConfUpdate.PubDns_HealthCheck_Type == sdhealthchecktype.UNKNOWN {
  1233  			err = errors.New("CloudMap UpdateService Failed: " + "Public Dns Namespace Health Check Requires Endpoint Type")
  1234  			return "", err
  1235  		}
  1236  
  1237  		if healthCheckConfUpdate.PubDns_HealthCheck_Type == sdhealthchecktype.TCP {
  1238  			healthCheckConfUpdate.PubDns_HealthCheck_Path = ""
  1239  		} else {
  1240  			if util.LenTrim(healthCheckConfUpdate.PubDns_HealthCheck_Path) == 0 {
  1241  				err = errors.New("CloudMap UpdateService Failed: " + "Health Check Resource Path is Required for HTTP & HTTPS Types")
  1242  				return "", err
  1243  			}
  1244  		}
  1245  	}
  1246  
  1247  	// define input
  1248  	input := &servicediscovery.UpdateServiceInput{
  1249  		Id: aws.String(serviceId),
  1250  	}
  1251  
  1252  	input.Service = &servicediscovery.ServiceChange{}
  1253  
  1254  	if descriptionUpdate != nil {
  1255  		if util.LenTrim(*descriptionUpdate) > 0 {
  1256  			input.Service.Description = descriptionUpdate
  1257  		}
  1258  	}
  1259  
  1260  	// dns update is TTL only but must provide existing dns type
  1261  	dnsType := "A"
  1262  
  1263  	if dnsConfUpdate.SRV {
  1264  		dnsType = "SRV"
  1265  	}
  1266  
  1267  	input.Service.DnsConfig = &servicediscovery.DnsConfigChange{
  1268  		DnsRecords: []*servicediscovery.DnsRecord{
  1269  			{
  1270  				TTL:  aws.Int64(dnsConfUpdate.TTL),
  1271  				Type: aws.String(dnsType),
  1272  			},
  1273  		},
  1274  	}
  1275  
  1276  	if healthCheckConfUpdate != nil {
  1277  		// update public dns health config
  1278  		input.Service.HealthCheckConfig = &servicediscovery.HealthCheckConfig{
  1279  			FailureThreshold: aws.Int64(healthCheckConfUpdate.FailureThreshold),
  1280  			Type:             aws.String(healthCheckConfUpdate.PubDns_HealthCheck_Type.Key()),
  1281  		}
  1282  
  1283  		if util.LenTrim(healthCheckConfUpdate.PubDns_HealthCheck_Path) > 0 {
  1284  			input.Service.HealthCheckConfig.ResourcePath = aws.String(healthCheckConfUpdate.PubDns_HealthCheck_Path)
  1285  		}
  1286  	}
  1287  
  1288  	// invoke action
  1289  	var output *servicediscovery.UpdateServiceOutput
  1290  
  1291  	if len(timeOutDuration) > 0 {
  1292  		ctx, cancel := context.WithTimeout(segCtx, timeOutDuration[0])
  1293  		defer cancel()
  1294  
  1295  		output, err = sd.sdClient.UpdateServiceWithContext(ctx, input)
  1296  	} else {
  1297  		if segCtxSet {
  1298  			output, err = sd.sdClient.UpdateServiceWithContext(segCtx, input)
  1299  		} else {
  1300  			output, err = sd.sdClient.UpdateService(input)
  1301  		}
  1302  	}
  1303  
  1304  	if err != nil {
  1305  		// handle error
  1306  		err = errors.New("CloudMap UpdateService Failed: (Update Action) " + err.Error())
  1307  		return "", err
  1308  	}
  1309  
  1310  	return *output.OperationId, nil
  1311  }
  1312  
  1313  // GetService gets a specified service's settings
  1314  //
  1315  // Parameters:
  1316  //  1. serviceId = (required) get service based on this service id
  1317  //  2. timeOutDuration = (optional) maximum time before timeout via context
  1318  //
  1319  // Return Values:
  1320  //  1. service = service object found based on the provided serviceId
  1321  //  2. err = contains error info if error was encountered
  1322  func (sd *CloudMap) GetService(serviceId string, timeOutDuration ...time.Duration) (service *servicediscovery.Service, err error) {
  1323  	segCtx := context.Background()
  1324  	segCtxSet := false
  1325  
  1326  	seg := xray.NewSegmentNullable("Cloudmap-GetService", sd._parentSegment)
  1327  
  1328  	if seg != nil {
  1329  		segCtx = seg.Ctx
  1330  		segCtxSet = true
  1331  
  1332  		defer seg.Close()
  1333  		defer func() {
  1334  			_ = seg.Seg.AddMetadata("Cloudmap-GetService-ServiceID", serviceId)
  1335  			_ = seg.Seg.AddMetadata("Cloudmap-GetService-Result-Service", service)
  1336  
  1337  			if err != nil {
  1338  				_ = seg.Seg.AddError(err)
  1339  			}
  1340  		}()
  1341  	}
  1342  
  1343  	// validate
  1344  	if sd.sdClient == nil {
  1345  		err = errors.New("CloudMap GetService Failed: " + "SD Client is Required")
  1346  		return nil, err
  1347  	}
  1348  
  1349  	if util.LenTrim(serviceId) == 0 {
  1350  		err = errors.New("CloudMap GetService Failed: " + "ServiceId is Required")
  1351  		return nil, err
  1352  	}
  1353  
  1354  	// define input
  1355  	input := &servicediscovery.GetServiceInput{
  1356  		Id: aws.String(serviceId),
  1357  	}
  1358  
  1359  	// invoke action
  1360  	var output *servicediscovery.GetServiceOutput
  1361  
  1362  	if len(timeOutDuration) > 0 {
  1363  		ctx, cancel := context.WithTimeout(segCtx, timeOutDuration[0])
  1364  		defer cancel()
  1365  
  1366  		output, err = sd.sdClient.GetServiceWithContext(ctx, input)
  1367  	} else {
  1368  		if segCtxSet {
  1369  			output, err = sd.sdClient.GetServiceWithContext(segCtx, input)
  1370  		} else {
  1371  			output, err = sd.sdClient.GetService(input)
  1372  		}
  1373  	}
  1374  
  1375  	if err != nil {
  1376  		// handle error
  1377  		err = errors.New("CloudMap GetService Failed: (Get Action) " + err.Error())
  1378  		return nil, err
  1379  	}
  1380  
  1381  	return output.Service, nil
  1382  }
  1383  
  1384  // ListServices lists summary information about all the services associated with one or more namespaces
  1385  //
  1386  // Parameters:
  1387  //  1. filter = (optional) filter by namespace(s) as specified, slice of namespaceId to filter
  1388  //  2. maxResults = (optional) specifies maximum count to return
  1389  //  3. nextToken = (optional) if initial action, leave blank; if this is a subsequent action to get more, input the moreNextToken returned from a prior action
  1390  //  4. timeOutDuration = (optional) maximum time before timeout via context
  1391  //
  1392  // Return Values:
  1393  //  1. services = slice of sd service summary objects
  1394  //  2. moreNextToken = if more data exists, this token can be used in a subsequent action via nextToken parameter
  1395  //  3. err = error info if any
  1396  func (sd *CloudMap) ListServices(filter []string,
  1397  	maxResults *int64,
  1398  	nextToken *string,
  1399  	timeOutDuration ...time.Duration) (services []*servicediscovery.ServiceSummary, moreNextToken string, err error) {
  1400  	segCtx := context.Background()
  1401  	segCtxSet := false
  1402  
  1403  	seg := xray.NewSegmentNullable("Cloudmap-ListServices", sd._parentSegment)
  1404  
  1405  	if seg != nil {
  1406  		segCtx = seg.Ctx
  1407  		segCtxSet = true
  1408  
  1409  		defer seg.Close()
  1410  		defer func() {
  1411  			_ = seg.Seg.AddMetadata("Cloudmap-ListServices-Filter", filter)
  1412  			_ = seg.Seg.AddMetadata("Cloudmap-ListServices-MaxResults", maxResults)
  1413  			_ = seg.Seg.AddMetadata("Cloudmap-ListServices-NextToken", nextToken)
  1414  			_ = seg.Seg.AddMetadata("Cloudmap-ListServices-Result-Services", services)
  1415  			_ = seg.Seg.AddMetadata("Cloudmap-ListServices-Result-NextToken", moreNextToken)
  1416  
  1417  			if err != nil {
  1418  				_ = seg.Seg.AddError(err)
  1419  			}
  1420  		}()
  1421  	}
  1422  
  1423  	// validate
  1424  	if sd.sdClient == nil {
  1425  		err = errors.New("CloudMap ListService Failed: " + "SD Client is Required")
  1426  		return nil, "", err
  1427  	}
  1428  
  1429  	if maxResults != nil {
  1430  		if *maxResults <= 0 {
  1431  			err = errors.New("CloudMap ListServices Failed: " + "MaxResults Must Be Greater Than Zero")
  1432  			return nil, "", err
  1433  		}
  1434  	}
  1435  
  1436  	// define input
  1437  	input := &servicediscovery.ListServicesInput{}
  1438  
  1439  	if len(filter) == 1 {
  1440  		input.Filters = []*servicediscovery.ServiceFilter{
  1441  			{
  1442  				Name:      aws.String("NAMESPACE_ID"),
  1443  				Condition: aws.String("EQ"),
  1444  				Values: []*string{
  1445  					aws.String(filter[0]),
  1446  				},
  1447  			},
  1448  		}
  1449  	} else if len(filter) > 1 {
  1450  		input.Filters = []*servicediscovery.ServiceFilter{
  1451  			{
  1452  				Name:      aws.String("NAMESPACE_ID"),
  1453  				Condition: aws.String("IN"),
  1454  			},
  1455  		}
  1456  
  1457  		var fv []string
  1458  
  1459  		for _, v := range filter {
  1460  			fv = append(fv, v)
  1461  		}
  1462  
  1463  		input.Filters[0].Values = aws.StringSlice(fv)
  1464  	}
  1465  
  1466  	if maxResults != nil {
  1467  		input.MaxResults = maxResults
  1468  	}
  1469  
  1470  	if nextToken != nil {
  1471  		if util.LenTrim(*nextToken) > 0 {
  1472  			input.NextToken = nextToken
  1473  		}
  1474  	}
  1475  
  1476  	// invoke action
  1477  	var output *servicediscovery.ListServicesOutput
  1478  
  1479  	if len(timeOutDuration) > 0 {
  1480  		ctx, cancel := context.WithTimeout(segCtx, timeOutDuration[0])
  1481  		defer cancel()
  1482  
  1483  		output, err = sd.sdClient.ListServicesWithContext(ctx, input)
  1484  	} else {
  1485  		if segCtxSet {
  1486  			output, err = sd.sdClient.ListServicesWithContext(segCtx, input)
  1487  		} else {
  1488  			output, err = sd.sdClient.ListServices(input)
  1489  		}
  1490  	}
  1491  
  1492  	if err != nil {
  1493  		// handle error
  1494  		err = errors.New("CloudMap ListServices Failed: (List Action) " + err.Error())
  1495  		return nil, "", err
  1496  	}
  1497  
  1498  	return output.Services, *output.NextToken, nil
  1499  }
  1500  
  1501  // ListServicesPages lists summary information about all the services associated with one or more namespaces
  1502  // (issues multiple page requests until max results is met or all data is retrieved)
  1503  //
  1504  // Parameters:
  1505  //  1. filter = (optional) filter by namespace(s) as specified, slice of namespaceId to filter
  1506  //  2. maxResults = (optional) specifies maximum count to return
  1507  //  3. nextToken = (optional) if initial action, leave blank; if this is a subsequent action to get more, input the moreNextToken returned from a prior action
  1508  //  4. timeOutDuration = (optional) maximum time before timeout via context
  1509  //
  1510  // Return Values:
  1511  //  1. namespaces = slice of sd service summary objects
  1512  //  2. moreNextToken = if more data exists, this token can be used in a subsequent action via nextToken parameter
  1513  //  3. err = error info if any
  1514  func (sd *CloudMap) ListServicesPages(filter []string,
  1515  	maxResults *int64,
  1516  	nextToken *string,
  1517  	timeOutDuration ...time.Duration) (services []*servicediscovery.ServiceSummary, moreNextToken string, err error) {
  1518  	segCtx := context.Background()
  1519  	segCtxSet := false
  1520  
  1521  	seg := xray.NewSegmentNullable("Cloudmap-ListServicesPages", sd._parentSegment)
  1522  
  1523  	if seg != nil {
  1524  		segCtx = seg.Ctx
  1525  		segCtxSet = true
  1526  
  1527  		defer seg.Close()
  1528  		defer func() {
  1529  			_ = seg.Seg.AddMetadata("Cloudmap-ListServicesPages-Filter", filter)
  1530  			_ = seg.Seg.AddMetadata("Cloudmap-ListServicesPages-MaxResults", maxResults)
  1531  			_ = seg.Seg.AddMetadata("Cloudmap-ListServicesPages-NextToken", nextToken)
  1532  			_ = seg.Seg.AddMetadata("Cloudmap-ListServicesPages-Result-Services", services)
  1533  			_ = seg.Seg.AddMetadata("Cloudmap-ListServicesPages-Result-NextToken", moreNextToken)
  1534  
  1535  			if err != nil {
  1536  				_ = seg.Seg.AddError(err)
  1537  			}
  1538  		}()
  1539  	}
  1540  
  1541  	// validate
  1542  	if sd.sdClient == nil {
  1543  		err = errors.New("CloudMap ListServicesPages Failed: " + "SD Client is Required")
  1544  		return nil, "", err
  1545  	}
  1546  
  1547  	if maxResults != nil {
  1548  		if *maxResults <= 0 {
  1549  			err = errors.New("CloudMap ListServicesPages Failed: " + "MaxResults Must Be Greater Than Zero")
  1550  			return nil, "", err
  1551  		}
  1552  	}
  1553  
  1554  	// define input
  1555  	input := &servicediscovery.ListServicesInput{}
  1556  
  1557  	if len(filter) == 1 {
  1558  		input.Filters = []*servicediscovery.ServiceFilter{
  1559  			{
  1560  				Name:      aws.String("NAMESPACE_ID"),
  1561  				Condition: aws.String("EQ"),
  1562  				Values: []*string{
  1563  					aws.String(filter[0]),
  1564  				},
  1565  			},
  1566  		}
  1567  	} else if len(filter) > 1 {
  1568  		input.Filters = []*servicediscovery.ServiceFilter{
  1569  			{
  1570  				Name:      aws.String("NAMESPACE_ID"),
  1571  				Condition: aws.String("IN"),
  1572  			},
  1573  		}
  1574  
  1575  		var fv []string
  1576  
  1577  		for _, v := range filter {
  1578  			fv = append(fv, v)
  1579  		}
  1580  
  1581  		input.Filters[0].Values = aws.StringSlice(fv)
  1582  	}
  1583  
  1584  	if maxResults != nil {
  1585  		input.MaxResults = maxResults
  1586  	}
  1587  
  1588  	if nextToken != nil {
  1589  		if util.LenTrim(*nextToken) > 0 {
  1590  			input.NextToken = nextToken
  1591  		}
  1592  	}
  1593  
  1594  	// invoke action
  1595  	fn := func(pageOutput *servicediscovery.ListServicesOutput, lastPage bool) bool {
  1596  		if pageOutput != nil {
  1597  			moreNextToken = *pageOutput.NextToken
  1598  			services = append(services, pageOutput.Services...)
  1599  		}
  1600  
  1601  		return !lastPage
  1602  	}
  1603  
  1604  	if len(timeOutDuration) > 0 {
  1605  		ctx, cancel := context.WithTimeout(segCtx, timeOutDuration[0])
  1606  		defer cancel()
  1607  
  1608  		err = sd.sdClient.ListServicesPagesWithContext(ctx, input, fn)
  1609  	} else {
  1610  		if segCtxSet {
  1611  			err = sd.sdClient.ListServicesPagesWithContext(segCtx, input, fn)
  1612  		} else {
  1613  			err = sd.sdClient.ListServicesPages(input, fn)
  1614  		}
  1615  	}
  1616  
  1617  	if err != nil {
  1618  		// handle error
  1619  		err = errors.New("CloudMap ListServicesPages Failed: (ListPages Action) " + err.Error())
  1620  		return nil, "", err
  1621  	}
  1622  
  1623  	return services, moreNextToken, nil
  1624  }
  1625  
  1626  // DeleteService deletes the specified service,
  1627  //
  1628  //	if the service still contains one or more registered instances, the delete action will fail
  1629  //
  1630  // Parameters:
  1631  //  1. serviceId = (required) service to be deleted via the specified service id
  1632  //  2. timeOutDuration = (optional) maximum time before timeout via context
  1633  //
  1634  // Return Values:
  1635  //  1. err = nil indicates success; contains error info if error was encountered
  1636  func (sd *CloudMap) DeleteService(serviceId string, timeOutDuration ...time.Duration) (err error) {
  1637  	segCtx := context.Background()
  1638  	segCtxSet := false
  1639  
  1640  	seg := xray.NewSegmentNullable("Cloudmap-DeleteService", sd._parentSegment)
  1641  
  1642  	if seg != nil {
  1643  		segCtx = seg.Ctx
  1644  		segCtxSet = true
  1645  
  1646  		defer seg.Close()
  1647  		defer func() {
  1648  			_ = seg.Seg.AddMetadata("Cloudmap-DeleteService-ServiceID", serviceId)
  1649  
  1650  			if err != nil {
  1651  				_ = seg.Seg.AddError(err)
  1652  			}
  1653  		}()
  1654  	}
  1655  
  1656  	// validate
  1657  	if sd.sdClient == nil {
  1658  		err = errors.New("CloudMap DeleteService Failed: " + "SD Client is Required")
  1659  		return err
  1660  	}
  1661  
  1662  	if util.LenTrim(serviceId) == 0 {
  1663  		err = errors.New("CloudMap DeleteService Failed: " + "ServiceId is Required")
  1664  		return err
  1665  	}
  1666  
  1667  	// define input
  1668  	input := &servicediscovery.DeleteServiceInput{
  1669  		Id: aws.String(serviceId),
  1670  	}
  1671  
  1672  	// invoke action
  1673  	if len(timeOutDuration) > 0 {
  1674  		ctx, cancel := context.WithTimeout(segCtx, timeOutDuration[0])
  1675  		defer cancel()
  1676  
  1677  		_, err = sd.sdClient.DeleteServiceWithContext(ctx, input)
  1678  	} else {
  1679  		if segCtxSet {
  1680  			_, err = sd.sdClient.DeleteServiceWithContext(segCtx, input)
  1681  		} else {
  1682  			_, err = sd.sdClient.DeleteService(input)
  1683  		}
  1684  	}
  1685  
  1686  	if err != nil {
  1687  		// handle error
  1688  		err = errors.New("CloudMap DeleteService Failed: (Delete Action) " + err.Error())
  1689  		return err
  1690  	}
  1691  
  1692  	return nil
  1693  }
  1694  
  1695  // ----------------------------------------------------------------------------------------------------------------
  1696  // instance functions
  1697  // ----------------------------------------------------------------------------------------------------------------
  1698  
  1699  // RegisterInstance creates or updates one or more records,
  1700  //
  1701  //	and optionally creates a health check based on settings from the specified service
  1702  //
  1703  // When RegisterInstance() request is submitted:
  1704  //  1. for each dns record defined in the service as specified by ServiceId,
  1705  //     a record is created or updated in the hosted zone that is associated with the corresponding namespace
  1706  //  2. if the service includes HealthCheckConfig,
  1707  //     a health check is created based on the settings in the health check configuration
  1708  //  3. the health check is associated with each of the new or updated records (if applicable)
  1709  //
  1710  // # One RegisterInstance() request must complete before another is submitted
  1711  //
  1712  // When AWS cloud map receives a dns query for the specified dns name,
  1713  //  1. if the health check is healthy, all records returned
  1714  //  2. if the health check is unhealthy, applicable value for the last healthy instance is returned
  1715  //  3. if health check configuration wasn't specified, then all records are returned regardless healthy or otherwise
  1716  //
  1717  // Parameters:
  1718  //  1. serviceId = (required) register instance to this serviceId
  1719  //  2. instanceId = (required) unique value for this instance, if instanceId already exists, this action will update instead of new
  1720  //  3. creatorRequestId = (required) unique request id to use in case of a failure (during fail-retry, use the same creatorRequestId
  1721  //  4. attributes = (required) map of attributes to register for this instance with the given serviceId, keys are as follows:
  1722  //     a) AWS_ALIAS_DNS_NAME = instruct cloud map to create route 53 alias record to route traffic to an ELB,
  1723  //     set the dns name associated with the load balancer to this key,
  1724  //     the associated service RoutingPolicy must be WEIGHTED,
  1725  //     when this key is set, DO NOT set values to any other AWS_INSTANCE attributes
  1726  //     b) AWS_EC2_INSTANCE_ID = for http namespace only, sets this instance's EC2 instance ID,
  1727  //     when this key is set, ONLY OTHER key allowed is AWS_INIT_HEALTH_STATUS,
  1728  //     when this key is set, the AWS_INSTANCE_IPV4 attribute will be filled with the primary private IPv4 address
  1729  //     c) AWS_INIT_HEALTH_STATUS = if associated service includes HealthCheckCustomConfig,
  1730  //     then this key may be optionally set to specify the initial status of custom health check: HEALTHY or UNHEALTHY,
  1731  //     if this key is not set, then initial status is HEALTHY
  1732  //     d) AWS_INSTANCE_IPV4 = if associated service dns record type is A, then set the IPv4 address to this key,
  1733  //     this key is required for service dns record type A
  1734  //     e) AWS_INSTANCE_PORT = if associated service includes HealthCheckConfig,
  1735  //     set the port for this endpoint that route 53 will send health check request to,
  1736  //     this key is required for service having HealthCheckConfig set
  1737  //     f) Custom Attributes = up to 30 custom attribute key value pairs,
  1738  //     key must not exceed 255 chars, value must not exceed 1024 chars,
  1739  //     total of all custom attribute key value pairs combined cannot exceed 5000 chars
  1740  //
  1741  // Return Values:
  1742  //  1. operationId = identifier to be used with GetOperation for status check (to verify completion of action)
  1743  //  2. err = contains error info if any
  1744  func (sd *CloudMap) RegisterInstance(serviceId string,
  1745  	instanceId string,
  1746  	creatorRequestId string,
  1747  	attributes map[string]string,
  1748  	timeOutDuration ...time.Duration) (operationId string, err error) {
  1749  	segCtx := context.Background()
  1750  	segCtxSet := false
  1751  
  1752  	seg := xray.NewSegmentNullable("Cloudmap-RegisterInstance", sd._parentSegment)
  1753  
  1754  	if seg != nil {
  1755  		segCtx = seg.Ctx
  1756  		segCtxSet = true
  1757  
  1758  		defer seg.Close()
  1759  		defer func() {
  1760  			_ = seg.Seg.AddMetadata("Cloudmap-RegisterInstance-ServiceID", serviceId)
  1761  			_ = seg.Seg.AddMetadata("Cloudmap-RegisterInstance-InstanceID", instanceId)
  1762  			_ = seg.Seg.AddMetadata("Cloudmap-RegisterInstance-CreatorRequestID", creatorRequestId)
  1763  			_ = seg.Seg.AddMetadata("Cloudmap-RegisterInstance-Attributes", attributes)
  1764  			_ = seg.Seg.AddMetadata("Cloudmap-RegisterInstance-Result-OperationID", operationId)
  1765  
  1766  			if err != nil {
  1767  				_ = seg.Seg.AddError(err)
  1768  			}
  1769  		}()
  1770  	}
  1771  
  1772  	// validate
  1773  	if sd.sdClient == nil {
  1774  		err = errors.New("CloudMap RegisterInstance Failed: " + "SD Client is Required")
  1775  		return "", err
  1776  	}
  1777  
  1778  	if util.LenTrim(serviceId) == 0 {
  1779  		err = errors.New("CloudMap RegisterInstance Failed: " + "ServiceId is Required")
  1780  		return "", err
  1781  	}
  1782  
  1783  	if util.LenTrim(instanceId) == 0 {
  1784  		err = errors.New("CloudMap RegisterInstance Failed: " + "InstanceId is Required")
  1785  		return "", err
  1786  	}
  1787  
  1788  	if util.LenTrim(creatorRequestId) == 0 {
  1789  		err = errors.New("CloudMap RegisterInstance Failed: " + "CreatorRequestId is Required")
  1790  		return "", err
  1791  	}
  1792  
  1793  	if attributes == nil {
  1794  		err = errors.New("CloudMap RegisterInstance Failed: " + "Attributes are Required (nil)")
  1795  		return "", err
  1796  	}
  1797  
  1798  	if len(attributes) == 0 {
  1799  		err = errors.New("CloudMap RegisterInstance Failed: " + "Attributes Are Required (len = 0)")
  1800  		return "", err
  1801  	}
  1802  
  1803  	// define input
  1804  	input := &servicediscovery.RegisterInstanceInput{
  1805  		InstanceId:       aws.String(instanceId),
  1806  		CreatorRequestId: aws.String(creatorRequestId),
  1807  		ServiceId:        aws.String(serviceId),
  1808  		Attributes:       aws.StringMap(attributes),
  1809  	}
  1810  
  1811  	// invoke action
  1812  	var output *servicediscovery.RegisterInstanceOutput
  1813  
  1814  	if len(timeOutDuration) > 0 {
  1815  		ctx, cancel := context.WithTimeout(segCtx, timeOutDuration[0])
  1816  		defer cancel()
  1817  
  1818  		output, err = sd.sdClient.RegisterInstanceWithContext(ctx, input)
  1819  	} else {
  1820  		if segCtxSet {
  1821  			output, err = sd.sdClient.RegisterInstanceWithContext(segCtx, input)
  1822  		} else {
  1823  			output, err = sd.sdClient.RegisterInstance(input)
  1824  		}
  1825  	}
  1826  
  1827  	if err != nil {
  1828  		// handle error
  1829  		err = errors.New("CloudMap RegisterInstance Failed: (Register Action) " + err.Error())
  1830  		return "", err
  1831  	}
  1832  
  1833  	return *output.OperationId, nil
  1834  }
  1835  
  1836  // UpdateInstanceCustomHealthStatus submits a request to change the health status of a custom health check,
  1837  //
  1838  //	to healthy or unhealthy
  1839  //
  1840  // This action works only with configuration of Custom Health Checks,
  1841  //
  1842  //	which was defined using HealthCheckCustomConfig when creating a service
  1843  //
  1844  // This action cannot be used to change the status of a route 53 health check,
  1845  //
  1846  //	which was defined using HealthCheckConfig when creating a service
  1847  //
  1848  // Parameters:
  1849  //  1. instanceId = (required) update healthy status to this instanceId
  1850  //  2. serviceId = (required) the associated service
  1851  //  3. isHealthy = specify the health status during this update action
  1852  //  4. timeOutDuration = (optional) maximum time before timeout via context
  1853  //
  1854  // Return Values:
  1855  //  1. err = nil indicates success; otherwise error info is included
  1856  func (sd *CloudMap) UpdateInstanceCustomHealthStatus(instanceId string,
  1857  	serviceId string,
  1858  	isHealthy bool,
  1859  	timeOutDuration ...time.Duration) (err error) {
  1860  	segCtx := context.Background()
  1861  	segCtxSet := false
  1862  
  1863  	seg := xray.NewSegmentNullable("Cloudmap-UpdateInstanceCustomHealthStatus", sd._parentSegment)
  1864  
  1865  	if seg != nil {
  1866  		segCtx = seg.Ctx
  1867  		segCtxSet = true
  1868  
  1869  		defer seg.Close()
  1870  		defer func() {
  1871  			_ = seg.Seg.AddMetadata("Cloudmap-UpdateInstanceCustomHealthStatus-InstanceID", instanceId)
  1872  			_ = seg.Seg.AddMetadata("Cloudmap-UpdateInstanceCustomHealthStatus-ServiceID", serviceId)
  1873  			_ = seg.Seg.AddMetadata("Cloudmap-UpdateInstanceCustomHealthStatus-IsHealthy", isHealthy)
  1874  
  1875  			if err != nil {
  1876  				_ = seg.Seg.AddError(err)
  1877  			}
  1878  		}()
  1879  	}
  1880  
  1881  	// validate
  1882  	if sd.sdClient == nil {
  1883  		err = errors.New("CloudMap UpdateInstanceCustomHealthStatus Failed: " + "SD Client is Required")
  1884  		return err
  1885  	}
  1886  
  1887  	if util.LenTrim(instanceId) == 0 {
  1888  		err = errors.New("CloudMap UpdateInstanceCustomHealthStatus Failed: " + "InstanceId is Required")
  1889  		return err
  1890  	}
  1891  
  1892  	if util.LenTrim(serviceId) == 0 {
  1893  		err = errors.New("CloudMap UpdateInstanceCustomHealthStatus Failed: " + "ServiceId is Required")
  1894  		return err
  1895  	}
  1896  
  1897  	// define input
  1898  	healthStatus := ""
  1899  
  1900  	if isHealthy {
  1901  		healthStatus = "HEALTHY"
  1902  	} else {
  1903  		healthStatus = "UNHEALTHY"
  1904  	}
  1905  
  1906  	input := &servicediscovery.UpdateInstanceCustomHealthStatusInput{
  1907  		InstanceId: aws.String(instanceId),
  1908  		ServiceId:  aws.String(serviceId),
  1909  		Status:     aws.String(healthStatus),
  1910  	}
  1911  
  1912  	// invoke action
  1913  	if len(timeOutDuration) > 0 {
  1914  		ctx, cancel := context.WithTimeout(segCtx, timeOutDuration[0])
  1915  		defer cancel()
  1916  
  1917  		_, err = sd.sdClient.UpdateInstanceCustomHealthStatusWithContext(ctx, input)
  1918  	} else {
  1919  		if segCtxSet {
  1920  			_, err = sd.sdClient.UpdateInstanceCustomHealthStatusWithContext(segCtx, input)
  1921  		} else {
  1922  			_, err = sd.sdClient.UpdateInstanceCustomHealthStatus(input)
  1923  		}
  1924  	}
  1925  
  1926  	if err != nil {
  1927  		// handle error
  1928  		err = errors.New("CloudMap UpdateInstanceCustomHealthStatus Failed: (Update Action) " + err.Error())
  1929  		return err
  1930  	}
  1931  
  1932  	return nil
  1933  }
  1934  
  1935  // DeregisterInstance deletes the route 53 dns record and health check (if any),
  1936  //
  1937  //	that was created by cloud map for the specified instance
  1938  //
  1939  // Parameters:
  1940  //  1. instanceId = (required) instance to deregister
  1941  //  2. serviceId = (required) the associated service
  1942  //
  1943  // Return Values:
  1944  //  1. operationId = operation identifier to be used with GetOperation for action completion status check
  1945  //  2. err = error info if any
  1946  func (sd *CloudMap) DeregisterInstance(instanceId string,
  1947  	serviceId string,
  1948  	timeOutDuration ...time.Duration) (operationId string, err error) {
  1949  	segCtx := context.Background()
  1950  	segCtxSet := false
  1951  
  1952  	seg := xray.NewSegmentNullable("Cloudmap-DeregisterInstance", sd._parentSegment)
  1953  
  1954  	if seg != nil {
  1955  		segCtx = seg.Ctx
  1956  		segCtxSet = true
  1957  
  1958  		defer seg.Close()
  1959  		defer func() {
  1960  			_ = seg.Seg.AddMetadata("Cloudmap-DeregisterInstance-InstanceID", instanceId)
  1961  			_ = seg.Seg.AddMetadata("Cloudmap-DeregisterInstance-ServiceID", serviceId)
  1962  			_ = seg.Seg.AddMetadata("Cloudmap-DeregisterInstance-Result-OperationID", operationId)
  1963  
  1964  			if err != nil {
  1965  				_ = seg.Seg.AddError(err)
  1966  			}
  1967  		}()
  1968  	}
  1969  
  1970  	// validate
  1971  	if sd.sdClient == nil {
  1972  		err = errors.New("CloudMap DeregisterInstance Failed: " + "SD Client is Required")
  1973  		return "", err
  1974  	}
  1975  
  1976  	if util.LenTrim(instanceId) == 0 {
  1977  		err = errors.New("CloudMap DeregisterInstance Failed: " + "InstanceId is Required")
  1978  		return "", err
  1979  	}
  1980  
  1981  	if util.LenTrim(serviceId) == 0 {
  1982  		err = errors.New("CloudMap DeregisterInstance Failed: " + "ServiceId is Required")
  1983  		return "", err
  1984  	}
  1985  
  1986  	// define input
  1987  	input := &servicediscovery.DeregisterInstanceInput{
  1988  		InstanceId: aws.String(instanceId),
  1989  		ServiceId:  aws.String(serviceId),
  1990  	}
  1991  
  1992  	// invoke action
  1993  	var output *servicediscovery.DeregisterInstanceOutput
  1994  
  1995  	if len(timeOutDuration) > 0 {
  1996  		ctx, cancel := context.WithTimeout(segCtx, timeOutDuration[0])
  1997  		defer cancel()
  1998  
  1999  		output, err = sd.sdClient.DeregisterInstanceWithContext(ctx, input)
  2000  	} else {
  2001  		if segCtxSet {
  2002  			output, err = sd.sdClient.DeregisterInstanceWithContext(segCtx, input)
  2003  		} else {
  2004  			output, err = sd.sdClient.DeregisterInstance(input)
  2005  		}
  2006  	}
  2007  
  2008  	if err != nil {
  2009  		// handle error
  2010  		err = errors.New("CloudMap DeregisterInstance Failed: (Deregister Action) " + err.Error())
  2011  		return "", err
  2012  	}
  2013  
  2014  	return *output.OperationId, nil
  2015  }
  2016  
  2017  // GetInstance gets information about a specified instance
  2018  //
  2019  // Parameters:
  2020  //  1. instanceId = (required) instance to get
  2021  //  2. serviceId = (required) the associated service
  2022  //
  2023  // Return Values:
  2024  //  1. instance = instance object retrieved
  2025  //  2. err = error info if any
  2026  func (sd *CloudMap) GetInstance(instanceId string,
  2027  	serviceId string,
  2028  	timeOutDuration ...time.Duration) (instance *servicediscovery.Instance, err error) {
  2029  	segCtx := context.Background()
  2030  	segCtxSet := false
  2031  
  2032  	seg := xray.NewSegmentNullable("Cloudmap-GetInstance", sd._parentSegment)
  2033  
  2034  	if seg != nil {
  2035  		segCtx = seg.Ctx
  2036  		segCtxSet = true
  2037  
  2038  		defer seg.Close()
  2039  		defer func() {
  2040  			_ = seg.Seg.AddMetadata("Cloudmap-GetInstance-InstanceID", instanceId)
  2041  			_ = seg.Seg.AddMetadata("Cloudmap-GetInstance-ServiceID", serviceId)
  2042  			_ = seg.Seg.AddMetadata("Cloudmap-GetInstance-Result-Instance", instance)
  2043  
  2044  			if err != nil {
  2045  				_ = seg.Seg.AddError(err)
  2046  			}
  2047  		}()
  2048  	}
  2049  
  2050  	// validate
  2051  	if sd.sdClient == nil {
  2052  		err = errors.New("CloudMap GetInstance Failed: " + "SD Client is Required")
  2053  		return nil, err
  2054  	}
  2055  
  2056  	if util.LenTrim(instanceId) == 0 {
  2057  		err = errors.New("CloudMap GetInstance Failed: " + "InstanceId is Required")
  2058  		return nil, err
  2059  	}
  2060  
  2061  	if util.LenTrim(serviceId) == 0 {
  2062  		err = errors.New("CloudMap GetInstance Failed: " + "ServiceId is Required")
  2063  		return nil, err
  2064  	}
  2065  
  2066  	// define input
  2067  	input := &servicediscovery.GetInstanceInput{
  2068  		InstanceId: aws.String(instanceId),
  2069  		ServiceId:  aws.String(serviceId),
  2070  	}
  2071  
  2072  	// invoke action
  2073  	var output *servicediscovery.GetInstanceOutput
  2074  
  2075  	if len(timeOutDuration) > 0 {
  2076  		ctx, cancel := context.WithTimeout(segCtx, timeOutDuration[0])
  2077  		defer cancel()
  2078  
  2079  		output, err = sd.sdClient.GetInstanceWithContext(ctx, input)
  2080  	} else {
  2081  		if segCtxSet {
  2082  			output, err = sd.sdClient.GetInstanceWithContext(segCtx, input)
  2083  		} else {
  2084  			output, err = sd.sdClient.GetInstance(input)
  2085  		}
  2086  	}
  2087  
  2088  	if err != nil {
  2089  		// handle error
  2090  		err = errors.New("CloudMap GetInstance Failed: (Get Action) " + err.Error())
  2091  		return nil, err
  2092  	}
  2093  
  2094  	return output.Instance, nil
  2095  }
  2096  
  2097  // GetInstancesHealthStatus gets the current health status (healthy, unhealthy, unknown) of one or more instances,
  2098  //
  2099  //	that are associated with a specified service
  2100  //
  2101  // # There is a brief delay between register an instance and when the health status for the instance is available
  2102  //
  2103  // Parameters:
  2104  //  1. serviceId = (required) service id assciated with the instances being checked
  2105  //  2. instanceIds = (optional) list of instance ids to check health status on, if omitted, then all instances of given service is checked
  2106  //  3. maxResults = (optional) specifies maximum count to return
  2107  //  4. nextToken = (optional) if initial action, leave blank; if this is a subsequent action to get more, input the moreNextToken returned from a prior action
  2108  //  5. timeOutDuration = (optional) maximum time before timeout via context
  2109  //
  2110  // Return Values:
  2111  //  1. status = map of instance status (key = instance id, value = health status 'healthy', 'unhealthy', 'unknown')
  2112  //  2. moreNextToken = if more data exists, this token can be used in a subsequent action via nextToken parameter
  2113  //  3. err = error info if any
  2114  func (sd *CloudMap) GetInstancesHealthStatus(serviceId string,
  2115  	instanceIds []string,
  2116  	maxResults *int64,
  2117  	nextToken *string,
  2118  	timeOutDuration ...time.Duration) (status map[string]string, moreNextToken string, err error) {
  2119  	segCtx := context.Background()
  2120  	segCtxSet := false
  2121  
  2122  	seg := xray.NewSegmentNullable("Cloudmap-GetInstancesHealthStatus", sd._parentSegment)
  2123  
  2124  	if seg != nil {
  2125  		segCtx = seg.Ctx
  2126  		segCtxSet = true
  2127  
  2128  		defer seg.Close()
  2129  		defer func() {
  2130  			_ = seg.Seg.AddMetadata("Cloudmap-GetInstancesHealthStatus-ServiceID", serviceId)
  2131  			_ = seg.Seg.AddMetadata("Cloudmap-GetInstancesHealthStatus-InstanceIDs", instanceIds)
  2132  			_ = seg.Seg.AddMetadata("Cloudmap-GetInstancesHealthStatus-MaxResults", maxResults)
  2133  			_ = seg.Seg.AddMetadata("Cloudmap-GetInstancesHealthStatus-NextToken", nextToken)
  2134  			_ = seg.Seg.AddMetadata("Cloudmap-GetInstancesHealthStatus-Result-Status", status)
  2135  			_ = seg.Seg.AddMetadata("Cloudmap-GetInstancesHealthStatus-Result-NextToken", moreNextToken)
  2136  
  2137  			if err != nil {
  2138  				_ = seg.Seg.AddError(err)
  2139  			}
  2140  		}()
  2141  	}
  2142  
  2143  	// validate
  2144  	if sd.sdClient == nil {
  2145  		err = errors.New("CloudMap GetInstancesHealthStatus Failed: " + "SD Client is Required")
  2146  		return nil, "", err
  2147  	}
  2148  
  2149  	if maxResults != nil {
  2150  		if *maxResults <= 0 {
  2151  			err = errors.New("CloudMap GetInstancesHealthStatus Failed: " + "MaxResults Must Be Greater Than Zero")
  2152  			return nil, "", err
  2153  		}
  2154  	}
  2155  
  2156  	// define input
  2157  	input := &servicediscovery.GetInstancesHealthStatusInput{
  2158  		ServiceId: aws.String(serviceId),
  2159  	}
  2160  
  2161  	if len(instanceIds) > 0 {
  2162  		input.Instances = aws.StringSlice(instanceIds)
  2163  	}
  2164  
  2165  	if maxResults != nil {
  2166  		input.MaxResults = maxResults
  2167  	}
  2168  
  2169  	if nextToken != nil {
  2170  		if util.LenTrim(*nextToken) > 0 {
  2171  			input.NextToken = nextToken
  2172  		}
  2173  	}
  2174  
  2175  	// invoke action
  2176  	var output *servicediscovery.GetInstancesHealthStatusOutput
  2177  
  2178  	if len(timeOutDuration) > 0 {
  2179  		ctx, cancel := context.WithTimeout(segCtx, timeOutDuration[0])
  2180  		defer cancel()
  2181  
  2182  		output, err = sd.sdClient.GetInstancesHealthStatusWithContext(ctx, input)
  2183  	} else {
  2184  		if segCtxSet {
  2185  			output, err = sd.sdClient.GetInstancesHealthStatusWithContext(segCtx, input)
  2186  		} else {
  2187  			output, err = sd.sdClient.GetInstancesHealthStatus(input)
  2188  		}
  2189  	}
  2190  
  2191  	if err != nil {
  2192  		// handle error
  2193  		err = errors.New("CloudMap GetInstancesHealthStatus Failed: (Get Action) " + err.Error())
  2194  		return nil, "", err
  2195  	}
  2196  
  2197  	return aws.StringValueMap(output.Status), *output.NextToken, nil
  2198  }
  2199  
  2200  // GetInstancesHealthStatusPages gets the current health status (healthy, unhealthy, unknown) of one or more instances,
  2201  //
  2202  //	that are associated with a specified service
  2203  //	(issues multiple page requests until max results is met or all data is retrieved)
  2204  //
  2205  // # There is a brief delay between register an instance and when the health status for the instance is available
  2206  //
  2207  // Parameters:
  2208  //  1. serviceId = (required) service id assciated with the instances being checked
  2209  //  2. instanceIds = (optional) list of instance ids to check health status on, if omitted, then all instances of given service is checked
  2210  //  3. maxResults = (optional) specifies maximum count to return
  2211  //  4. nextToken = (optional) if initial action, leave blank; if this is a subsequent action to get more, input the moreNextToken returned from a prior action
  2212  //  5. timeOutDuration = (optional) maximum time before timeout via context
  2213  //
  2214  // Return Values:
  2215  //  1. status = map of instance status (key = instance id, value = health status 'healthy', 'unhealthy', 'unknown')
  2216  //  2. moreNextToken = if more data exists, this token can be used in a subsequent action via nextToken parameter
  2217  //  3. err = error info if any
  2218  func (sd *CloudMap) GetInstancesHealthStatusPages(serviceId string,
  2219  	instanceIds []string,
  2220  	maxResults *int64,
  2221  	nextToken *string,
  2222  	timeOutDuration ...time.Duration) (status map[string]string, moreNextToken string, err error) {
  2223  	segCtx := context.Background()
  2224  	segCtxSet := false
  2225  
  2226  	seg := xray.NewSegmentNullable("Cloudmap-GetInstancesHealthStatusPages", sd._parentSegment)
  2227  
  2228  	if seg != nil {
  2229  		segCtx = seg.Ctx
  2230  		segCtxSet = true
  2231  
  2232  		defer seg.Close()
  2233  		defer func() {
  2234  			_ = seg.Seg.AddMetadata("Cloudmap-GetInstancesHealthStatusPages-ServiceID", serviceId)
  2235  			_ = seg.Seg.AddMetadata("Cloudmap-GetInstancesHealthStatusPages-InstanceIDs", instanceIds)
  2236  			_ = seg.Seg.AddMetadata("Cloudmap-GetInstancesHealthStatusPages-MaxResults", maxResults)
  2237  			_ = seg.Seg.AddMetadata("Cloudmap-GetInstancesHealthStatusPages-NextToken", nextToken)
  2238  			_ = seg.Seg.AddMetadata("Cloudmap-GetInstancesHealthStatusPages-Result-Status", status)
  2239  			_ = seg.Seg.AddMetadata("Cloudmap-GetInstancesHealthStatusPages-Result-NextToken", moreNextToken)
  2240  
  2241  			if err != nil {
  2242  				_ = seg.Seg.AddError(err)
  2243  			}
  2244  		}()
  2245  	}
  2246  
  2247  	// validate
  2248  	if sd.sdClient == nil {
  2249  		err = errors.New("CloudMap GetInstancesHealthStatusPages Failed: " + "SD Client is Required")
  2250  		return nil, "", err
  2251  	}
  2252  
  2253  	if maxResults != nil {
  2254  		if *maxResults <= 0 {
  2255  			err = errors.New("CloudMap GetInstancesHealthStatusPages Failed: " + "MaxResults Must Be Greater Than Zero")
  2256  			return nil, "", err
  2257  		}
  2258  	}
  2259  
  2260  	// define input
  2261  	input := &servicediscovery.GetInstancesHealthStatusInput{
  2262  		ServiceId: aws.String(serviceId),
  2263  	}
  2264  
  2265  	if len(instanceIds) > 0 {
  2266  		input.Instances = aws.StringSlice(instanceIds)
  2267  	}
  2268  
  2269  	if maxResults != nil {
  2270  		input.MaxResults = maxResults
  2271  	}
  2272  
  2273  	if nextToken != nil {
  2274  		if util.LenTrim(*nextToken) > 0 {
  2275  			input.NextToken = nextToken
  2276  		}
  2277  	}
  2278  
  2279  	// invoke action
  2280  	fn := func(pageOutput *servicediscovery.GetInstancesHealthStatusOutput, lastPage bool) bool {
  2281  		if pageOutput != nil {
  2282  			moreNextToken = *pageOutput.NextToken
  2283  			m := aws.StringValueMap(pageOutput.Status)
  2284  
  2285  			if status == nil {
  2286  				status = make(map[string]string)
  2287  			}
  2288  
  2289  			for k, v := range m {
  2290  				status[k] = v
  2291  			}
  2292  		}
  2293  
  2294  		return !lastPage
  2295  	}
  2296  
  2297  	if len(timeOutDuration) > 0 {
  2298  		ctx, cancel := context.WithTimeout(segCtx, timeOutDuration[0])
  2299  		defer cancel()
  2300  
  2301  		err = sd.sdClient.GetInstancesHealthStatusPagesWithContext(ctx, input, fn)
  2302  	} else {
  2303  		if segCtxSet {
  2304  			err = sd.sdClient.GetInstancesHealthStatusPagesWithContext(segCtx, input, fn)
  2305  		} else {
  2306  			err = sd.sdClient.GetInstancesHealthStatusPages(input, fn)
  2307  		}
  2308  	}
  2309  
  2310  	if err != nil {
  2311  		// handle error
  2312  		err = errors.New("CloudMap GetInstancesHealthStatusPages Failed: (ListPages Action) " + err.Error())
  2313  		return nil, "", err
  2314  	}
  2315  
  2316  	return status, moreNextToken, nil
  2317  }
  2318  
  2319  // DiscoverInstances discovers registered instances for a specified namespace and service
  2320  //
  2321  // Notes:
  2322  //  1. Used to discover instances for any type of namespace (http, private dns, public dns)
  2323  //  2. For public and private dns namespaces,
  2324  //     may also use dns queries to discover distances instead
  2325  //
  2326  // Parameters:
  2327  //  1. namespaceName = (required) name of the namespace to be discovered
  2328  //  2. serviceName = (required) name of the service to be discovered
  2329  //  3. isHealthy = (required) discover healthy or unhealthy instances
  2330  //  4. queryParameters = (optional) map of key value pairs, containing custom attributes registered during RegisterInstance,
  2331  //     if custom attributes is specified, all attributes in the queryParameters must match for the instance to discover
  2332  //  5. maxResults = (optional) max count of discovered instances to return, if not specified, up to 100 is returned
  2333  //  6. timeOutDuration = (optional) maximum time before timeout via context
  2334  //
  2335  // Return Values:
  2336  //  1. instances = slice of discovered instance objects
  2337  //  2. err = error info if any
  2338  func (sd *CloudMap) DiscoverInstances(namespaceName string,
  2339  	serviceName string,
  2340  	isHealthy bool,
  2341  	queryParameters map[string]string,
  2342  	maxResults *int64,
  2343  	timeOutDuration ...time.Duration) (instances []*servicediscovery.HttpInstanceSummary, err error) {
  2344  	segCtx := context.Background()
  2345  	segCtxSet := false
  2346  
  2347  	seg := xray.NewSegmentNullable("Cloudmap-DiscoverInstances", sd._parentSegment)
  2348  
  2349  	if seg != nil {
  2350  		segCtx = seg.Ctx
  2351  		segCtxSet = true
  2352  
  2353  		defer seg.Close()
  2354  		defer func() {
  2355  			_ = seg.Seg.AddMetadata("Cloudmap-DiscoverInstances-NamespaceName", namespaceName)
  2356  			_ = seg.Seg.AddMetadata("Cloudmap-DiscoverInstances-ServiceName", serviceName)
  2357  			_ = seg.Seg.AddMetadata("Cloudmap-DiscoverInstances-IsHealthy", isHealthy)
  2358  			_ = seg.Seg.AddMetadata("Cloudmap-DiscoverInstances-QueryParameters", queryParameters)
  2359  			_ = seg.Seg.AddMetadata("Cloudmap-DiscoverInstances-MaxResults", maxResults)
  2360  			_ = seg.Seg.AddMetadata("Cloudmap-DiscoverInstances-Result-Instances", instances)
  2361  
  2362  			if err != nil {
  2363  				_ = seg.Seg.AddError(err)
  2364  			}
  2365  		}()
  2366  	}
  2367  
  2368  	// validate
  2369  	if sd.sdClient == nil {
  2370  		err = errors.New("CloudMap DiscoverInstances Failed: " + "SD Client is Required")
  2371  		return nil, err
  2372  	}
  2373  
  2374  	if util.LenTrim(namespaceName) == 0 {
  2375  		err = errors.New("CloudMap DiscoverInstances Failed: " + "Namespace Name is Required")
  2376  		return nil, err
  2377  	}
  2378  
  2379  	if util.LenTrim(serviceName) == 0 {
  2380  		err = errors.New("CloudMap DiscoverInstances Failed: " + "Service Name is Required")
  2381  		return nil, err
  2382  	}
  2383  
  2384  	// define input
  2385  	healthStatus := ""
  2386  
  2387  	if isHealthy {
  2388  		healthStatus = "HEALTHY"
  2389  	} else {
  2390  		healthStatus = "UNHEALTHY"
  2391  	}
  2392  
  2393  	input := &servicediscovery.DiscoverInstancesInput{
  2394  		NamespaceName: aws.String(namespaceName),
  2395  		ServiceName:   aws.String(serviceName),
  2396  		HealthStatus:  aws.String(healthStatus),
  2397  	}
  2398  
  2399  	if queryParameters != nil && len(queryParameters) > 0 {
  2400  		input.QueryParameters = aws.StringMap(queryParameters)
  2401  	}
  2402  
  2403  	if maxResults != nil && *maxResults > 0 {
  2404  		input.MaxResults = maxResults
  2405  	}
  2406  
  2407  	// invoke action
  2408  	var output *servicediscovery.DiscoverInstancesOutput
  2409  
  2410  	if len(timeOutDuration) > 0 {
  2411  		ctx, cancel := context.WithTimeout(segCtx, timeOutDuration[0])
  2412  		defer cancel()
  2413  
  2414  		output, err = sd.sdClient.DiscoverInstancesWithContext(ctx, input)
  2415  	} else {
  2416  		if segCtxSet {
  2417  			output, err = sd.sdClient.DiscoverInstancesWithContext(segCtx, input)
  2418  		} else {
  2419  			output, err = sd.sdClient.DiscoverInstances(input)
  2420  		}
  2421  	}
  2422  
  2423  	if err != nil {
  2424  		// handle error
  2425  		err = errors.New("CloudMap DiscoverInstances Failed: (Discover Action) " + err.Error())
  2426  		return nil, err
  2427  	}
  2428  
  2429  	return output.Instances, nil
  2430  }
  2431  
  2432  // ListInstances lists summary information about the instances registered using a specified service
  2433  //
  2434  // Parameters:
  2435  //  1. serviceId = (required) service id assciated with the instances being checked
  2436  //  2. maxResults = (optional) specifies maximum count to return
  2437  //  3. nextToken = (optional) if initial action, leave blank; if this is a subsequent action to get more, input the moreNextToken returned from a prior action
  2438  //  4. timeOutDuration = (optional) maximum time before timeout via context
  2439  //
  2440  // Return Values:
  2441  //  1. instances = slice of sd instance summary objects
  2442  //  2. moreNextToken = if more data exists, this token can be used in a subsequent action via nextToken parameter
  2443  //  3. err = error info if any
  2444  func (sd *CloudMap) ListInstances(serviceId string,
  2445  	maxResults *int64,
  2446  	nextToken *string,
  2447  	timeOutDuration ...time.Duration) (instances []*servicediscovery.InstanceSummary, moreNextToken string, err error) {
  2448  	segCtx := context.Background()
  2449  	segCtxSet := false
  2450  
  2451  	seg := xray.NewSegmentNullable("Cloudmap-ListInstances", sd._parentSegment)
  2452  
  2453  	if seg != nil {
  2454  		segCtx = seg.Ctx
  2455  		segCtxSet = true
  2456  
  2457  		defer seg.Close()
  2458  		defer func() {
  2459  			_ = seg.Seg.AddMetadata("Cloudmap-ListInstances-ServiceID", serviceId)
  2460  			_ = seg.Seg.AddMetadata("Cloudmap-ListInstances-MaxResults", maxResults)
  2461  			_ = seg.Seg.AddMetadata("Cloudmap-ListInstances-NextToken", nextToken)
  2462  			_ = seg.Seg.AddMetadata("Cloudmap-ListInstances-Result-Instances", instances)
  2463  			_ = seg.Seg.AddMetadata("Cloudmap-ListInstances-Result-NextToken", moreNextToken)
  2464  
  2465  			if err != nil {
  2466  				_ = seg.Seg.AddError(err)
  2467  			}
  2468  		}()
  2469  	}
  2470  
  2471  	// validate
  2472  	if sd.sdClient == nil {
  2473  		err = errors.New("CloudMap ListInstances Failed: " + "SD Client is Required")
  2474  		return nil, "", err
  2475  	}
  2476  
  2477  	if util.LenTrim(serviceId) == 0 {
  2478  		err = errors.New("CloudMap ListInstances Failed: " + "Service ID is Required")
  2479  		return nil, "", err
  2480  	}
  2481  
  2482  	if maxResults != nil {
  2483  		if *maxResults <= 0 {
  2484  			err = errors.New("CloudMap ListInstances Failed: " + "MaxResults Must Be Greater Than Zero")
  2485  			return nil, "", err
  2486  		}
  2487  	}
  2488  
  2489  	// define input
  2490  	input := &servicediscovery.ListInstancesInput{
  2491  		ServiceId: aws.String(serviceId),
  2492  	}
  2493  
  2494  	if maxResults != nil {
  2495  		input.MaxResults = maxResults
  2496  	}
  2497  
  2498  	if nextToken != nil {
  2499  		if util.LenTrim(*nextToken) > 0 {
  2500  			input.NextToken = nextToken
  2501  		}
  2502  	}
  2503  
  2504  	// invoke action
  2505  	var output *servicediscovery.ListInstancesOutput
  2506  
  2507  	if len(timeOutDuration) > 0 {
  2508  		ctx, cancel := context.WithTimeout(segCtx, timeOutDuration[0])
  2509  		defer cancel()
  2510  
  2511  		output, err = sd.sdClient.ListInstancesWithContext(ctx, input)
  2512  	} else {
  2513  		if segCtxSet {
  2514  			output, err = sd.sdClient.ListInstancesWithContext(segCtx, input)
  2515  		} else {
  2516  			output, err = sd.sdClient.ListInstances(input)
  2517  		}
  2518  	}
  2519  
  2520  	if err != nil {
  2521  		// handle error
  2522  		err = errors.New("CloudMap ListInstances Failed: (List Action) " + err.Error())
  2523  		return nil, "", err
  2524  	}
  2525  
  2526  	return output.Instances, *output.NextToken, nil
  2527  }
  2528  
  2529  // ListInstancesPages lists summary information about the instances registered using a specified service
  2530  // (issues multiple page requests until max results is met or all data is retrieved)
  2531  //
  2532  // Parameters:
  2533  //  1. serviceId = (required) service id assciated with the instances being checked
  2534  //  2. maxResults = (optional) specifies maximum count to return
  2535  //  3. nextToken = (optional) if initial action, leave blank; if this is a subsequent action to get more, input the moreNextToken returned from a prior action
  2536  //  4. timeOutDuration = (optional) maximum time before timeout via context
  2537  //
  2538  // Return Values:
  2539  //  1. instances = slice of sd instance summary objects
  2540  //  2. moreNextToken = if more data exists, this token can be used in a subsequent action via nextToken parameter
  2541  //  3. err = error info if any
  2542  func (sd *CloudMap) ListInstancesPages(serviceId string,
  2543  	maxResults *int64,
  2544  	nextToken *string,
  2545  	timeOutDuration ...time.Duration) (instances []*servicediscovery.InstanceSummary, moreNextToken string, err error) {
  2546  	segCtx := context.Background()
  2547  	segCtxSet := false
  2548  
  2549  	seg := xray.NewSegmentNullable("Cloudmap-ListInstancesPages", sd._parentSegment)
  2550  
  2551  	if seg != nil {
  2552  		segCtx = seg.Ctx
  2553  		segCtxSet = true
  2554  
  2555  		defer seg.Close()
  2556  		defer func() {
  2557  			_ = seg.Seg.AddMetadata("Cloudmap-ListInstancesPages-ServiceID", serviceId)
  2558  			_ = seg.Seg.AddMetadata("Cloudmap-ListInstancesPages-MaxResults", maxResults)
  2559  			_ = seg.Seg.AddMetadata("Cloudmap-ListInstancesPages-NextToken", nextToken)
  2560  			_ = seg.Seg.AddMetadata("Cloudmap-ListInstancesPages-Result-Instances", instances)
  2561  			_ = seg.Seg.AddMetadata("Cloudmap-ListInstancesPages-Result-NextToken", moreNextToken)
  2562  
  2563  			if err != nil {
  2564  				_ = seg.Seg.AddError(err)
  2565  			}
  2566  		}()
  2567  	}
  2568  
  2569  	// validate
  2570  	if sd.sdClient == nil {
  2571  		err = errors.New("CloudMap ListInstancesPages Failed: " + "SD Client is Required")
  2572  		return nil, "", err
  2573  	}
  2574  
  2575  	if util.LenTrim(serviceId) == 0 {
  2576  		err = errors.New("CloudMap ListInstancesPages Failed: " + "Service ID is Required")
  2577  		return nil, "", err
  2578  	}
  2579  
  2580  	if maxResults != nil {
  2581  		if *maxResults <= 0 {
  2582  			err = errors.New("CloudMap ListInstancesPages Failed: " + "MaxResults Must Be Greater Than Zero")
  2583  			return nil, "", err
  2584  		}
  2585  	}
  2586  
  2587  	// define input
  2588  	input := &servicediscovery.ListInstancesInput{
  2589  		ServiceId: aws.String(serviceId),
  2590  	}
  2591  
  2592  	if maxResults != nil {
  2593  		input.MaxResults = maxResults
  2594  	}
  2595  
  2596  	if nextToken != nil {
  2597  		if util.LenTrim(*nextToken) > 0 {
  2598  			input.NextToken = nextToken
  2599  		}
  2600  	}
  2601  
  2602  	// invoke action
  2603  	fn := func(pageOutput *servicediscovery.ListInstancesOutput, lastPage bool) bool {
  2604  		if pageOutput != nil {
  2605  			moreNextToken = *pageOutput.NextToken
  2606  			instances = append(instances, pageOutput.Instances...)
  2607  		}
  2608  
  2609  		return !lastPage
  2610  	}
  2611  
  2612  	if len(timeOutDuration) > 0 {
  2613  		ctx, cancel := context.WithTimeout(segCtx, timeOutDuration[0])
  2614  		defer cancel()
  2615  
  2616  		err = sd.sdClient.ListInstancesPagesWithContext(ctx, input, fn)
  2617  	} else {
  2618  		if segCtxSet {
  2619  			err = sd.sdClient.ListInstancesPagesWithContext(segCtx, input, fn)
  2620  		} else {
  2621  			err = sd.sdClient.ListInstancesPages(input, fn)
  2622  		}
  2623  	}
  2624  
  2625  	if err != nil {
  2626  		// handle error
  2627  		err = errors.New("CloudMap ListInstancesPages Failed: (ListPages Action) " + err.Error())
  2628  		return nil, "", err
  2629  	}
  2630  
  2631  	return instances, moreNextToken, nil
  2632  }
  2633  
  2634  // ----------------------------------------------------------------------------------------------------------------
  2635  // operation functions
  2636  // ----------------------------------------------------------------------------------------------------------------
  2637  
  2638  // GetOperation gets information about any operation that returned an operationId in the response,
  2639  //
  2640  //	such as CreateHttpNamespace(), CreateService(), etc
  2641  //
  2642  // Parameters:
  2643  //  1. operationId = (required) the operation to retrieve, operationId is obtained during Create, and other related actions
  2644  //  2. timeOutDuration = (optional) maximum time before timeout via context
  2645  //
  2646  // Return Values:
  2647  //  1. operation = operation object retrieved
  2648  //     a) Targets = evaluate Targets to retrieve namespaceId, serviceId, InstanceId etc, using NAMESPACE, SERVICE, INSTANCE key names
  2649  //  2. err = error info any
  2650  func (sd *CloudMap) GetOperation(operationId string, timeOutDuration ...time.Duration) (operation *servicediscovery.Operation, err error) {
  2651  	segCtx := context.Background()
  2652  	segCtxSet := false
  2653  
  2654  	seg := xray.NewSegmentNullable("Cloudmap-GetOperation", sd._parentSegment)
  2655  
  2656  	if seg != nil {
  2657  		segCtx = seg.Ctx
  2658  		segCtxSet = true
  2659  
  2660  		defer seg.Close()
  2661  		defer func() {
  2662  			_ = seg.Seg.AddMetadata("Cloudmap-GetOperation-OperationID", operationId)
  2663  			_ = seg.Seg.AddMetadata("Cloudmap-GetOperation-Result-Operation", operation)
  2664  
  2665  			if err != nil {
  2666  				_ = seg.Seg.AddError(err)
  2667  			}
  2668  		}()
  2669  	}
  2670  
  2671  	// validate
  2672  	if sd.sdClient == nil {
  2673  		err = errors.New("CloudMap GetOperation Failed: " + "SD Client is Required")
  2674  		return nil, err
  2675  	}
  2676  
  2677  	if util.LenTrim(operationId) == 0 {
  2678  		err = errors.New("CloudMap GetOperation Failed: " + "OperationId is Required")
  2679  		return nil, err
  2680  	}
  2681  
  2682  	// define input
  2683  	input := &servicediscovery.GetOperationInput{
  2684  		OperationId: aws.String(operationId),
  2685  	}
  2686  
  2687  	// invoke action
  2688  	var output *servicediscovery.GetOperationOutput
  2689  
  2690  	if len(timeOutDuration) > 0 {
  2691  		ctx, cancel := context.WithTimeout(segCtx, timeOutDuration[0])
  2692  		defer cancel()
  2693  
  2694  		output, err = sd.sdClient.GetOperationWithContext(ctx, input)
  2695  	} else {
  2696  		if segCtxSet {
  2697  			output, err = sd.sdClient.GetOperationWithContext(segCtx, input)
  2698  		} else {
  2699  			output, err = sd.sdClient.GetOperation(input)
  2700  		}
  2701  	}
  2702  
  2703  	if err != nil {
  2704  		// handle error
  2705  		err = errors.New("CloudMap GetOperation Failed: (Get Action) " + err.Error())
  2706  		return nil, err
  2707  	}
  2708  
  2709  	return output.Operation, nil
  2710  }
  2711  
  2712  // ListOperations lists operations that match the criteria specified in parameters
  2713  //
  2714  // Parameters:
  2715  //  1. filter = (optional) map of filter operations (EQ_ filters allow single value per key)
  2716  //     a) EQ_Status / IN_Status = Valid Values: SUBMITTED, PENDING, SUCCEED, FAIL
  2717  //     b) EQ_Type / IN_Type = Valid Values: CREATE_NAMESPACE, DELETE_NAMESPACE, UPDATE_SERVICE, REGISTER_INSTANCE, DEREGISTER_INSTANCE
  2718  //     c) BETWEEN_UpdateDate = begin and end in Unix DateTime in UTC
  2719  //  2. maxResults = (optional) specifies maximum count to return
  2720  //  3. nextToken = (optional) if initial action, leave blank; if this is a subsequent action to get more, input the moreNextToken returned from a prior action
  2721  //  4. timeOutDuration = (optional) maximum time before timeout via context
  2722  //
  2723  // Return Values:
  2724  //  1. operations = slice of sd operation summary objects
  2725  //     a) Targets = evaluate Targets to retrieve namespaceId, serviceId, InstanceId etc, using NAMESPACE, SERVICE, INSTANCE key names
  2726  //  2. moreNextToken = if more data exists, this token can be used in a subsequent action via nextToken parameter
  2727  //  3. err = error info if any
  2728  func (sd *CloudMap) ListOperations(filter map[sdoperationfilter.SdOperationFilter][]string,
  2729  	maxResults *int64,
  2730  	nextToken *string,
  2731  	timeOutDuration ...time.Duration) (operations []*servicediscovery.OperationSummary, moreNextToken string, err error) {
  2732  	segCtx := context.Background()
  2733  	segCtxSet := false
  2734  
  2735  	seg := xray.NewSegmentNullable("Cloudmap-ListOperations", sd._parentSegment)
  2736  
  2737  	if seg != nil {
  2738  		segCtx = seg.Ctx
  2739  		segCtxSet = true
  2740  
  2741  		defer seg.Close()
  2742  		defer func() {
  2743  			_ = seg.Seg.AddMetadata("Cloudmap-ListOperations-Filter", filter)
  2744  			_ = seg.Seg.AddMetadata("Cloudmap-ListOperations-MaxResults", maxResults)
  2745  			_ = seg.Seg.AddMetadata("Cloudmap-ListOperations-NextToken", nextToken)
  2746  			_ = seg.Seg.AddMetadata("Cloudmap-ListOperations-Result-Operations", operations)
  2747  			_ = seg.Seg.AddMetadata("Cloudmap-ListOperations-Result-NextToken", moreNextToken)
  2748  
  2749  			if err != nil {
  2750  				_ = seg.Seg.AddError(err)
  2751  			}
  2752  		}()
  2753  	}
  2754  
  2755  	// validate
  2756  	if sd.sdClient == nil {
  2757  		err = errors.New("CloudMap ListOperations Failed: " + "SD Client is Required")
  2758  		return nil, "", err
  2759  	}
  2760  
  2761  	if maxResults != nil {
  2762  		if *maxResults <= 0 {
  2763  			err = errors.New("CloudMap ListOperations Failed: " + "MaxResults Must Be Greater Than Zero")
  2764  			return nil, "", err
  2765  		}
  2766  	}
  2767  
  2768  	// define input
  2769  	input := &servicediscovery.ListOperationsInput{}
  2770  
  2771  	if filter != nil {
  2772  		var opFilters []*servicediscovery.OperationFilter
  2773  
  2774  		for fk, fv := range filter {
  2775  			var sdof *servicediscovery.OperationFilter
  2776  
  2777  			switch fk {
  2778  			case sdoperationfilter.EQ_NameSpaceID:
  2779  				if len(fv) == 1 {
  2780  					sdof = &servicediscovery.OperationFilter{
  2781  						Name:      aws.String("NAMESPACE_ID"),
  2782  						Condition: aws.String("EQ"),
  2783  						Values:    aws.StringSlice(fv),
  2784  					}
  2785  				}
  2786  			case sdoperationfilter.EQ_ServiceID:
  2787  				if len(fv) == 1 {
  2788  					sdof = &servicediscovery.OperationFilter{
  2789  						Name:      aws.String("SERVICE_ID"),
  2790  						Condition: aws.String("EQ"),
  2791  						Values:    aws.StringSlice(fv),
  2792  					}
  2793  				}
  2794  			case sdoperationfilter.EQ_Status:
  2795  				if len(fv) == 1 {
  2796  					sdof = &servicediscovery.OperationFilter{
  2797  						Name:      aws.String("STATUS"),
  2798  						Condition: aws.String("EQ"),
  2799  						Values:    aws.StringSlice(fv),
  2800  					}
  2801  				}
  2802  			case sdoperationfilter.EQ_Type:
  2803  				if len(fv) == 1 {
  2804  					sdof = &servicediscovery.OperationFilter{
  2805  						Name:      aws.String("TYPE"),
  2806  						Condition: aws.String("EQ"),
  2807  						Values:    aws.StringSlice(fv),
  2808  					}
  2809  				}
  2810  			case sdoperationfilter.IN_Status:
  2811  				if len(fv) > 0 {
  2812  					sdof = &servicediscovery.OperationFilter{
  2813  						Name:      aws.String("STATUS"),
  2814  						Condition: aws.String("IN"),
  2815  						Values:    aws.StringSlice(fv),
  2816  					}
  2817  				}
  2818  			case sdoperationfilter.IN_Type:
  2819  				if len(fv) > 0 {
  2820  					sdof = &servicediscovery.OperationFilter{
  2821  						Name:      aws.String("TYPE"),
  2822  						Condition: aws.String("IN"),
  2823  						Values:    aws.StringSlice(fv),
  2824  					}
  2825  				}
  2826  			case sdoperationfilter.BETWEEN_UpdateDate:
  2827  				if len(fv) == 2 {
  2828  					sdof = &servicediscovery.OperationFilter{
  2829  						Name:      aws.String("UPDATE_DATE"),
  2830  						Condition: aws.String("BETWEEN"),
  2831  						Values:    aws.StringSlice(fv),
  2832  					}
  2833  				}
  2834  			}
  2835  
  2836  			if sdof != nil {
  2837  				opFilters = append(opFilters, sdof)
  2838  			}
  2839  		}
  2840  
  2841  		if len(opFilters) > 0 {
  2842  			input.Filters = opFilters
  2843  		}
  2844  	}
  2845  
  2846  	if maxResults != nil {
  2847  		input.MaxResults = maxResults
  2848  	}
  2849  
  2850  	if nextToken != nil {
  2851  		if util.LenTrim(*nextToken) > 0 {
  2852  			input.NextToken = nextToken
  2853  		}
  2854  	}
  2855  
  2856  	// invoke action
  2857  	var output *servicediscovery.ListOperationsOutput
  2858  
  2859  	if len(timeOutDuration) > 0 {
  2860  		ctx, cancel := context.WithTimeout(segCtx, timeOutDuration[0])
  2861  		defer cancel()
  2862  
  2863  		output, err = sd.sdClient.ListOperationsWithContext(ctx, input)
  2864  	} else {
  2865  		if segCtxSet {
  2866  			output, err = sd.sdClient.ListOperationsWithContext(segCtx, input)
  2867  		} else {
  2868  			output, err = sd.sdClient.ListOperations(input)
  2869  		}
  2870  	}
  2871  
  2872  	if err != nil {
  2873  		// handle error
  2874  		err = errors.New("CloudMap ListOperations Failed: (List Action) " + err.Error())
  2875  		return nil, "", err
  2876  	}
  2877  
  2878  	return output.Operations, *output.NextToken, nil
  2879  }
  2880  
  2881  // ListOperationsPages lists operations that match the criteria specified in parameters
  2882  // (issues multiple page requests until max results is met or all data is retrieved)
  2883  //
  2884  // Parameters:
  2885  //  1. filter = (optional) map of filter operations (EQ_ filters allow single value per key)
  2886  //     a) EQ_Status / IN_Status = Valid Values: SUBMITTED, PENDING, SUCCEED, FAIL
  2887  //     b) EQ_Type / IN_Type = Valid Values: CREATE_NAMESPACE, DELETE_NAMESPACE, UPDATE_SERVICE, REGISTER_INSTANCE, DEREGISTER_INSTANCE
  2888  //     c) BETWEEN_UpdateDate = begin and end in Unix DateTime in UTC
  2889  //  2. maxResults = (optional) specifies maximum count to return
  2890  //  3. nextToken = (optional) if initial action, leave blank; if this is a subsequent action to get more, input the moreNextToken returned from a prior action
  2891  //  4. timeOutDuration = (optional) maximum time before timeout via context
  2892  //
  2893  // Return Values:
  2894  //  1. operations = slice of sd operation summary objects
  2895  //     a) Targets = evaluate Targets to retrieve namespaceId, serviceId, InstanceId etc, using NAMESPACE, SERVICE, INSTANCE key names
  2896  //  2. moreNextToken = if more data exists, this token can be used in a subsequent action via nextToken parameter
  2897  //  3. err = error info if any
  2898  func (sd *CloudMap) ListOperationsPages(filter map[sdoperationfilter.SdOperationFilter][]string,
  2899  	maxResults *int64,
  2900  	nextToken *string,
  2901  	timeOutDuration ...time.Duration) (operations []*servicediscovery.OperationSummary, moreNextToken string, err error) {
  2902  	segCtx := context.Background()
  2903  	segCtxSet := false
  2904  
  2905  	seg := xray.NewSegmentNullable("Cloudmap-ListOperationsPages", sd._parentSegment)
  2906  
  2907  	if seg != nil {
  2908  		segCtx = seg.Ctx
  2909  		segCtxSet = true
  2910  
  2911  		defer seg.Close()
  2912  		defer func() {
  2913  			_ = seg.Seg.AddMetadata("Cloudmap-ListOperationsPages-Filter", filter)
  2914  			_ = seg.Seg.AddMetadata("Cloudmap-ListOperationsPages-MaxResults", maxResults)
  2915  			_ = seg.Seg.AddMetadata("Cloudmap-ListOperationsPages-NextToken", nextToken)
  2916  			_ = seg.Seg.AddMetadata("Cloudmap-ListOperationsPages-Result-Operations", operations)
  2917  			_ = seg.Seg.AddMetadata("Cloudmap-ListOperationsPages-Result-NextToken", moreNextToken)
  2918  
  2919  			if err != nil {
  2920  				_ = seg.Seg.AddError(err)
  2921  			}
  2922  		}()
  2923  	}
  2924  
  2925  	// validate
  2926  	if sd.sdClient == nil {
  2927  		err = errors.New("CloudMap ListOperationsPages Failed: " + "SD Client is Required")
  2928  		return nil, "", err
  2929  	}
  2930  
  2931  	if maxResults != nil {
  2932  		if *maxResults <= 0 {
  2933  			err = errors.New("CloudMap ListOperationsPages Failed: " + "MaxResults Must Be Greater Than Zero")
  2934  			return nil, "", err
  2935  		}
  2936  	}
  2937  
  2938  	// define input
  2939  	input := &servicediscovery.ListOperationsInput{}
  2940  
  2941  	if filter != nil {
  2942  		var opFilters []*servicediscovery.OperationFilter
  2943  
  2944  		for fk, fv := range filter {
  2945  			var sdof *servicediscovery.OperationFilter
  2946  
  2947  			switch fk {
  2948  			case sdoperationfilter.EQ_NameSpaceID:
  2949  				if len(fv) == 1 {
  2950  					sdof = &servicediscovery.OperationFilter{
  2951  						Name:      aws.String("NAMESPACE_ID"),
  2952  						Condition: aws.String("EQ"),
  2953  						Values:    aws.StringSlice(fv),
  2954  					}
  2955  				}
  2956  			case sdoperationfilter.EQ_ServiceID:
  2957  				if len(fv) == 1 {
  2958  					sdof = &servicediscovery.OperationFilter{
  2959  						Name:      aws.String("SERVICE_ID"),
  2960  						Condition: aws.String("EQ"),
  2961  						Values:    aws.StringSlice(fv),
  2962  					}
  2963  				}
  2964  			case sdoperationfilter.EQ_Status:
  2965  				if len(fv) == 1 {
  2966  					sdof = &servicediscovery.OperationFilter{
  2967  						Name:      aws.String("STATUS"),
  2968  						Condition: aws.String("EQ"),
  2969  						Values:    aws.StringSlice(fv),
  2970  					}
  2971  				}
  2972  			case sdoperationfilter.EQ_Type:
  2973  				if len(fv) == 1 {
  2974  					sdof = &servicediscovery.OperationFilter{
  2975  						Name:      aws.String("TYPE"),
  2976  						Condition: aws.String("EQ"),
  2977  						Values:    aws.StringSlice(fv),
  2978  					}
  2979  				}
  2980  			case sdoperationfilter.IN_Status:
  2981  				if len(fv) > 0 {
  2982  					sdof = &servicediscovery.OperationFilter{
  2983  						Name:      aws.String("STATUS"),
  2984  						Condition: aws.String("IN"),
  2985  						Values:    aws.StringSlice(fv),
  2986  					}
  2987  				}
  2988  			case sdoperationfilter.IN_Type:
  2989  				if len(fv) > 0 {
  2990  					sdof = &servicediscovery.OperationFilter{
  2991  						Name:      aws.String("TYPE"),
  2992  						Condition: aws.String("IN"),
  2993  						Values:    aws.StringSlice(fv),
  2994  					}
  2995  				}
  2996  			case sdoperationfilter.BETWEEN_UpdateDate:
  2997  				if len(fv) == 2 {
  2998  					sdof = &servicediscovery.OperationFilter{
  2999  						Name:      aws.String("UPDATE_DATE"),
  3000  						Condition: aws.String("BETWEEN"),
  3001  						Values:    aws.StringSlice(fv),
  3002  					}
  3003  				}
  3004  			}
  3005  
  3006  			if sdof != nil {
  3007  				opFilters = append(opFilters, sdof)
  3008  			}
  3009  		}
  3010  
  3011  		if len(opFilters) > 0 {
  3012  			input.Filters = opFilters
  3013  		}
  3014  	}
  3015  
  3016  	if maxResults != nil {
  3017  		input.MaxResults = maxResults
  3018  	}
  3019  
  3020  	if nextToken != nil {
  3021  		if util.LenTrim(*nextToken) > 0 {
  3022  			input.NextToken = nextToken
  3023  		}
  3024  	}
  3025  
  3026  	// invoke action
  3027  	fn := func(pageOutput *servicediscovery.ListOperationsOutput, lastPage bool) bool {
  3028  		if pageOutput != nil {
  3029  			moreNextToken = *pageOutput.NextToken
  3030  			operations = append(operations, pageOutput.Operations...)
  3031  		}
  3032  
  3033  		return !lastPage
  3034  	}
  3035  
  3036  	if len(timeOutDuration) > 0 {
  3037  		ctx, cancel := context.WithTimeout(segCtx, timeOutDuration[0])
  3038  		defer cancel()
  3039  
  3040  		err = sd.sdClient.ListOperationsPagesWithContext(ctx, input, fn)
  3041  	} else {
  3042  		if segCtxSet {
  3043  			err = sd.sdClient.ListOperationsPagesWithContext(segCtx, input, fn)
  3044  		} else {
  3045  			err = sd.sdClient.ListOperationsPages(input, fn)
  3046  		}
  3047  	}
  3048  
  3049  	if err != nil {
  3050  		// handle error
  3051  		err = errors.New("CloudMap ListOperationsPages Failed: (ListPages Action) " + err.Error())
  3052  		return nil, "", err
  3053  	}
  3054  
  3055  	return operations, moreNextToken, nil
  3056  }