github.com/m3db/m3@v1.5.1-0.20231129193456-75a402aa583b/src/cmd/services/m3dbnode/main/main_test.go (about)

     1  //go:build big
     2  // +build big
     3  
     4  //
     5  // Copyright (c) 2017 Uber Technologies, Inc.
     6  //
     7  // Permission is hereby granted, free of charge, to any person obtaining a copy
     8  // of this software and associated documentation files (the "Software"), to deal
     9  // in the Software without restriction, including without limitation the rights
    10  // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
    11  // copies of the Software, and to permit persons to whom the Software is
    12  // furnished to do so, subject to the following conditions:
    13  //
    14  // The above copyright notice and this permission notice shall be included in
    15  // all copies or substantial portions of the Software.
    16  //
    17  // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    18  // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    19  // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    20  // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    21  // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    22  // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
    23  // THE SOFTWARE.
    24  
    25  package main_test
    26  
    27  import (
    28  	"fmt"
    29  	"net/http"
    30  	"strconv"
    31  	"sync"
    32  	"testing"
    33  	"text/template"
    34  	"time"
    35  
    36  	"github.com/m3db/m3/src/cluster/integration/etcd"
    37  	"github.com/m3db/m3/src/cluster/placement"
    38  	"github.com/m3db/m3/src/cluster/services"
    39  	"github.com/m3db/m3/src/cmd/services/m3dbnode/config"
    40  	"github.com/m3db/m3/src/dbnode/client"
    41  	"github.com/m3db/m3/src/dbnode/kvconfig"
    42  	"github.com/m3db/m3/src/dbnode/server"
    43  	xconfig "github.com/m3db/m3/src/x/config"
    44  	"github.com/m3db/m3/src/x/ident"
    45  	"github.com/m3db/m3/src/x/instrument"
    46  	xtime "github.com/m3db/m3/src/x/time"
    47  
    48  	"github.com/stretchr/testify/assert"
    49  	"github.com/stretchr/testify/require"
    50  	"go.uber.org/zap"
    51  )
    52  
    53  // TestConfig tests booting a server using file based configuration.
    54  func TestConfig(t *testing.T) {
    55  	// Embedded kv
    56  	embeddedKV, err := etcd.New(etcd.NewOptions().SetInitTimeout(30 * time.Second))
    57  	require.NoError(t, err)
    58  	defer func() {
    59  		require.NoError(t, embeddedKV.Close())
    60  	}()
    61  	require.NoError(t, embeddedKV.Start())
    62  
    63  	// Create config file
    64  	tmpl, err := template.New("config").Parse(testConfig + kvConfigPortion)
    65  	require.NoError(t, err)
    66  
    67  	configFd, cleanup := tempFile(t, "config.yaml")
    68  	defer cleanup()
    69  
    70  	logFile, cleanupLogFile := tempFileTouch(t, "m3dbnode.log")
    71  	defer cleanupLogFile()
    72  
    73  	configServiceCacheDir, cleanupConfigServiceCacheDir := tempDir(t, "kv")
    74  	defer cleanupConfigServiceCacheDir()
    75  
    76  	dataDir, cleanupDataDir := tempDir(t, "data")
    77  	defer cleanupDataDir()
    78  
    79  	servicePort := nextServicePort()
    80  	err = tmpl.Execute(configFd, struct {
    81  		HostID                string
    82  		LogFile               string
    83  		DataDir               string
    84  		ServicePort           string
    85  		ServiceName           string
    86  		ServiceEnv            string
    87  		ServiceZone           string
    88  		ConfigServiceCacheDir string
    89  		EtcdEndpoints         string
    90  	}{
    91  		HostID:                hostID,
    92  		LogFile:               logFile,
    93  		DataDir:               dataDir,
    94  		ServicePort:           strconv.Itoa(int(servicePort)),
    95  		ServiceName:           serviceName,
    96  		ServiceEnv:            serviceEnv,
    97  		ServiceZone:           serviceZone,
    98  		ConfigServiceCacheDir: configServiceCacheDir,
    99  		EtcdEndpoints:         yamlArray(t, embeddedKV.Endpoints()),
   100  	})
   101  	require.NoError(t, err)
   102  
   103  	// Setup the placement
   104  	var cfg config.Configuration
   105  	err = xconfig.LoadFile(&cfg, configFd.Name(), xconfig.Options{})
   106  	require.NoError(t, err)
   107  
   108  	discoveryCfg := cfg.DB.DiscoveryOrDefault()
   109  	envCfg, err := discoveryCfg.EnvironmentConfig(hostID)
   110  	require.NoError(t, err)
   111  
   112  	syncCluster, err := envCfg.Services.SyncCluster()
   113  	require.NoError(t, err)
   114  	configSvcClient, err := syncCluster.Service.NewClient(instrument.NewOptions().
   115  		SetLogger(zap.NewNop()))
   116  	require.NoError(t, err)
   117  
   118  	svcs, err := configSvcClient.Services(services.NewOverrideOptions())
   119  	require.NoError(t, err)
   120  
   121  	serviceID := services.NewServiceID().
   122  		SetName(serviceName).
   123  		SetEnvironment(serviceEnv).
   124  		SetZone(serviceZone)
   125  
   126  	metadata := services.NewMetadata().
   127  		SetPort(servicePort).
   128  		SetLivenessInterval(time.Minute).
   129  		SetHeartbeatInterval(10 * time.Second)
   130  
   131  	err = svcs.SetMetadata(serviceID, metadata)
   132  	require.NoError(t, err)
   133  
   134  	placementOpts := placement.NewOptions().
   135  		SetValidZone(serviceZone)
   136  	placementSvc, err := svcs.PlacementService(serviceID, placementOpts)
   137  	require.NoError(t, err)
   138  
   139  	var (
   140  		instance = placement.NewInstance().
   141  				SetID(hostID).
   142  				SetEndpoint(endpoint("127.0.0.1", servicePort)).
   143  				SetPort(servicePort).
   144  				SetIsolationGroup("local").
   145  				SetWeight(1).
   146  				SetZone(serviceZone)
   147  		instances = []placement.Instance{instance}
   148  		// Reduce number of shards to avoid having to tune F.D limits.
   149  		shards   = 4
   150  		replicas = 1
   151  	)
   152  
   153  	_, err = placementSvc.BuildInitialPlacement(instances, shards, replicas)
   154  	require.NoError(t, err)
   155  
   156  	// Setup the namespace
   157  	ns, err := newNamespaceProtoValue(namespaceID)
   158  	require.NoError(t, err)
   159  
   160  	kvStore, err := configSvcClient.KV()
   161  	require.NoError(t, err)
   162  
   163  	_, err = kvStore.Set(kvconfig.NamespacesKey, ns)
   164  	require.NoError(t, err)
   165  
   166  	// Run server
   167  	var (
   168  		interruptCh = make(chan error, 1)
   169  		bootstrapCh = make(chan struct{}, 1)
   170  		serverWg    sync.WaitGroup
   171  	)
   172  	serverWg.Add(1)
   173  	go func() {
   174  		server.Run(server.RunOptions{
   175  			ConfigFile:  configFd.Name(),
   176  			BootstrapCh: bootstrapCh,
   177  			InterruptCh: interruptCh,
   178  		})
   179  		serverWg.Done()
   180  	}()
   181  	defer func() {
   182  		// Resetting DefaultServeMux to prevent multiple assignments
   183  		// to /debug/dump in Server.Run()
   184  		http.DefaultServeMux = http.NewServeMux()
   185  	}()
   186  
   187  	// Wait for bootstrap
   188  	<-bootstrapCh
   189  
   190  	// Create client, read and write some data
   191  	// NB(r): Make sure client config points to the root config
   192  	// service since we're going to instantiate the client configuration
   193  	// just by itself.
   194  	cfg.DB.Client.EnvironmentConfig = &envCfg
   195  
   196  	cli, err := cfg.DB.Client.NewClient(client.ConfigurationParameters{})
   197  	require.NoError(t, err)
   198  
   199  	adminCli := cli.(client.AdminClient)
   200  	adminSession, err := adminCli.DefaultAdminSession()
   201  	require.NoError(t, err)
   202  	defer adminSession.Close()
   203  
   204  	// Propagation of shard state from Initializing --> Available post-bootstrap is eventually
   205  	// consistent, so we must wait.
   206  	waitUntilAllShardsAreAvailable(t, adminSession)
   207  
   208  	// Cast to narrower-interface instead of grabbing DefaultSession to make sure
   209  	// we use the same topology.Map that we validated in waitUntilAllShardsAreAvailable.
   210  	session := adminSession.(client.Session)
   211  
   212  	start := xtime.Now().Add(-time.Minute)
   213  	values := []struct {
   214  		value float64
   215  		at    xtime.UnixNano
   216  		unit  xtime.Unit
   217  	}{
   218  		{value: 1.0, at: start, unit: xtime.Second},
   219  		{value: 2.0, at: start.Add(1 * time.Second), unit: xtime.Second},
   220  		{value: 3.0, at: start.Add(2 * time.Second), unit: xtime.Second},
   221  	}
   222  
   223  	for _, v := range values {
   224  		err := session.Write(ident.StringID(namespaceID), ident.StringID("foo"), v.at, v.value, v.unit, nil)
   225  		require.NoError(t, err)
   226  	}
   227  
   228  	// Account for first value inserted at xtime.Second precision
   229  	fetchStart := start.Truncate(time.Second)
   230  
   231  	// Account for last value being inserted at xtime.Second and
   232  	// the "end" param to fetch being exclusive
   233  	fetchEnd := values[len(values)-1].at.Truncate(time.Second).Add(time.Nanosecond)
   234  
   235  	iter, err := session.Fetch(ident.StringID(namespaceID), ident.StringID("foo"), fetchStart, fetchEnd)
   236  	require.NoError(t, err)
   237  
   238  	for _, v := range values {
   239  		require.True(t, iter.Next())
   240  		dp, unit, _ := iter.Current()
   241  		assert.Equal(t, v.value, dp.Value)
   242  		// Account for xtime.Second precision on values going in
   243  		expectAt := v.at.Truncate(time.Second)
   244  		assert.Equal(t, expectAt, dp.TimestampNanos)
   245  		assert.Equal(t, v.unit, unit)
   246  	}
   247  
   248  	// Wait for server to stop
   249  	interruptCh <- fmt.Errorf("test complete")
   250  	serverWg.Wait()
   251  }
   252  
   253  // TestEmbeddedConfig tests booting a server using an embedded KV.
   254  func TestEmbeddedConfig(t *testing.T) {
   255  	// Create config file
   256  	tmpl, err := template.New("config").Parse(testConfig + embeddedKVConfigPortion)
   257  	require.NoError(t, err)
   258  
   259  	configFd, cleanup := tempFile(t, "config.yaml")
   260  	defer cleanup()
   261  
   262  	logFile, cleanupLogFile := tempFileTouch(t, "m3dbnode.log")
   263  	defer cleanupLogFile()
   264  
   265  	configServiceCacheDir, cleanupConfigServiceCacheDir := tempDir(t, "kv")
   266  	defer cleanupConfigServiceCacheDir()
   267  
   268  	embeddedKVDir, cleanupEmbeddedKVDir := tempDir(t, "embedded")
   269  	defer cleanupEmbeddedKVDir()
   270  
   271  	dataDir, cleanupDataDir := tempDir(t, "data")
   272  	defer cleanupDataDir()
   273  
   274  	servicePort := nextServicePort()
   275  	err = tmpl.Execute(configFd, struct {
   276  		HostID                 string
   277  		LogFile                string
   278  		DataDir                string
   279  		ServicePort            string
   280  		ServiceName            string
   281  		ServiceEnv             string
   282  		ServiceZone            string
   283  		ConfigServiceCacheDir  string
   284  		EmbeddedKVDir          string
   285  		LPURL                  string
   286  		LCURL                  string
   287  		APURL                  string
   288  		ACURL                  string
   289  		EtcdEndpoint           string
   290  		InitialClusterHostID   string
   291  		InitialClusterEndpoint string
   292  	}{
   293  		HostID:                 hostID,
   294  		LogFile:                logFile,
   295  		DataDir:                dataDir,
   296  		ServicePort:            strconv.Itoa(int(servicePort)),
   297  		ServiceName:            serviceName,
   298  		ServiceEnv:             serviceEnv,
   299  		ServiceZone:            serviceZone,
   300  		ConfigServiceCacheDir:  configServiceCacheDir,
   301  		EmbeddedKVDir:          embeddedKVDir,
   302  		LPURL:                  lpURL,
   303  		LCURL:                  lcURL,
   304  		APURL:                  apURL,
   305  		ACURL:                  acURL,
   306  		EtcdEndpoint:           etcdEndpoint,
   307  		InitialClusterHostID:   initialClusterHostID,
   308  		InitialClusterEndpoint: initialClusterEndpoint,
   309  	})
   310  	require.NoError(t, err)
   311  
   312  	// Run server
   313  	var (
   314  		interruptCh  = make(chan error, 1)
   315  		bootstrapCh  = make(chan struct{}, 1)
   316  		embeddedKVCh = make(chan struct{}, 1)
   317  		serverWg     sync.WaitGroup
   318  	)
   319  	serverWg.Add(1)
   320  	go func() {
   321  		server.Run(server.RunOptions{
   322  			ConfigFile:   configFd.Name(),
   323  			BootstrapCh:  bootstrapCh,
   324  			EmbeddedKVCh: embeddedKVCh,
   325  			InterruptCh:  interruptCh,
   326  		})
   327  		serverWg.Done()
   328  	}()
   329  	defer func() {
   330  		// Resetting DefaultServeMux to prevent multiple assignments
   331  		// to /debug/dump in Server.Run()
   332  		http.DefaultServeMux = http.NewServeMux()
   333  	}()
   334  
   335  	// Wait for embedded KV to be up.
   336  	<-embeddedKVCh
   337  
   338  	// Setup the placement
   339  	var cfg config.Configuration
   340  	err = xconfig.LoadFile(&cfg, configFd.Name(), xconfig.Options{})
   341  	require.NoError(t, err)
   342  
   343  	discoveryCfg := cfg.DB.DiscoveryOrDefault()
   344  	envCfg, err := discoveryCfg.EnvironmentConfig(hostID)
   345  	require.NoError(t, err)
   346  
   347  	syncCluster, err := envCfg.Services.SyncCluster()
   348  	require.NoError(t, err)
   349  	configSvcClient, err := syncCluster.Service.NewClient(instrument.NewOptions().
   350  		SetLogger(zap.NewNop()))
   351  	require.NoError(t, err)
   352  
   353  	svcs, err := configSvcClient.Services(services.NewOverrideOptions())
   354  	require.NoError(t, err)
   355  
   356  	serviceID := services.NewServiceID().
   357  		SetName(serviceName).
   358  		SetEnvironment(serviceEnv).
   359  		SetZone(serviceZone)
   360  
   361  	metadata := services.NewMetadata().
   362  		SetPort(servicePort).
   363  		SetLivenessInterval(time.Minute).
   364  		SetHeartbeatInterval(10 * time.Second)
   365  
   366  	err = svcs.SetMetadata(serviceID, metadata)
   367  	require.NoError(t, err)
   368  
   369  	placementOpts := placement.NewOptions().
   370  		SetValidZone(serviceZone)
   371  	placementSvc, err := svcs.PlacementService(serviceID, placementOpts)
   372  	require.NoError(t, err)
   373  
   374  	var (
   375  		instance = placement.NewInstance().
   376  				SetID(hostID).
   377  				SetEndpoint(endpoint("127.0.0.1", servicePort)).
   378  				SetPort(servicePort).
   379  				SetIsolationGroup("local").
   380  				SetWeight(1).
   381  				SetZone(serviceZone)
   382  		instances = []placement.Instance{instance}
   383  		// Use a low number of shards to avoid having to tune F.D limits.
   384  		shards   = 4
   385  		replicas = 1
   386  	)
   387  
   388  	_, err = placementSvc.BuildInitialPlacement(instances, shards, replicas)
   389  	require.NoError(t, err)
   390  
   391  	// Setup the namespace
   392  	ns, err := newNamespaceProtoValue(namespaceID)
   393  	require.NoError(t, err)
   394  
   395  	kvStore, err := configSvcClient.KV()
   396  	require.NoError(t, err)
   397  
   398  	_, err = kvStore.Set(kvconfig.NamespacesKey, ns)
   399  	require.NoError(t, err)
   400  
   401  	// Wait for bootstrap
   402  	<-bootstrapCh
   403  
   404  	// Create client, read and write some data
   405  	// NB(r): Make sure client config points to the root config
   406  	// service since we're going to instantiate the client configuration
   407  	// just by itself.
   408  	cfg.DB.Client.EnvironmentConfig = &envCfg
   409  
   410  	cli, err := cfg.DB.Client.NewClient(client.ConfigurationParameters{})
   411  	require.NoError(t, err)
   412  
   413  	adminCli := cli.(client.AdminClient)
   414  	adminSession, err := adminCli.DefaultAdminSession()
   415  	require.NoError(t, err)
   416  	defer adminSession.Close()
   417  
   418  	// Propagation of shard state from Initializing --> Available post-bootstrap is eventually
   419  	// consistent, so we must wait.
   420  	waitUntilAllShardsAreAvailable(t, adminSession)
   421  
   422  	// Cast to narrower-interface instead of grabbing DefaultSession to make sure
   423  	// we use the same topology.Map that we validated in waitUntilAllShardsAreAvailable.
   424  	session := adminSession.(client.Session)
   425  
   426  	start := xtime.Now().Add(-time.Minute)
   427  	values := []struct {
   428  		value float64
   429  		at    xtime.UnixNano
   430  		unit  xtime.Unit
   431  	}{
   432  		{value: 1.0, at: start, unit: xtime.Second},
   433  		{value: 2.0, at: start.Add(1 * time.Second), unit: xtime.Second},
   434  		{value: 3.0, at: start.Add(2 * time.Second), unit: xtime.Second},
   435  	}
   436  
   437  	for _, v := range values {
   438  		err := session.Write(ident.StringID(namespaceID), ident.StringID("foo"), v.at, v.value, v.unit, nil)
   439  		require.NoError(t, err)
   440  	}
   441  
   442  	// Account for first value inserted at xtime.Second precision
   443  	fetchStart := start.Truncate(time.Second)
   444  
   445  	// Account for last value being inserted at xtime.Second and
   446  	// the "end" param to fetch being exclusive
   447  	fetchEnd := values[len(values)-1].at.Truncate(time.Second).Add(time.Nanosecond)
   448  
   449  	iter, err := session.Fetch(ident.StringID(namespaceID), ident.StringID("foo"), fetchStart, fetchEnd)
   450  	require.NoError(t, err)
   451  
   452  	for _, v := range values {
   453  		require.True(t, iter.Next())
   454  		dp, unit, _ := iter.Current()
   455  		assert.Equal(t, v.value, dp.Value)
   456  		// Account for xtime.Second precision on values going in
   457  		expectAt := v.at.Truncate(time.Second)
   458  		assert.Equal(t, expectAt, dp.TimestampNanos)
   459  		assert.Equal(t, v.unit, unit)
   460  	}
   461  
   462  	// Wait for server to stop
   463  	interruptCh <- fmt.Errorf("test complete")
   464  	serverWg.Wait()
   465  }
   466  
   467  var (
   468  	testConfig = `
   469  db:
   470      logging:
   471          level: info
   472          file: {{.LogFile}}
   473  
   474      metrics:
   475          prometheus:
   476              handlerPath: /metrics
   477              listenAddress: 0.0.0.0:9005
   478              onError: none
   479          sanitization: prometheus
   480          samplingRate: 1.0
   481          extended: detailed
   482  
   483      listenAddress: 0.0.0.0:{{.ServicePort}}
   484      clusterListenAddress: 0.0.0.0:9001
   485      httpNodeListenAddress: 0.0.0.0:9002
   486      httpClusterListenAddress: 0.0.0.0:9003
   487      debugListenAddress: 0.0.0.0:9004
   488  
   489      hostID:
   490          resolver: config
   491          value: {{.HostID}}
   492  
   493      client:
   494          writeConsistencyLevel: majority
   495          readConsistencyLevel: unstrict_majority
   496          connectConsistencyLevel: any
   497          writeTimeout: 10s
   498          fetchTimeout: 15s
   499          connectTimeout: 20s
   500          writeRetry:
   501              initialBackoff: 500ms
   502              backoffFactor: 3
   503              maxRetries: 2
   504              jitter: true
   505          fetchRetry:
   506              initialBackoff: 500ms
   507              backoffFactor: 2
   508              maxRetries: 3
   509              jitter: true
   510          backgroundHealthCheckFailLimit: 4
   511          backgroundHealthCheckFailThrottleFactor: 0.5
   512  
   513      gcPercentage: 100
   514  
   515      writeNewSeriesAsync: true
   516      writeNewSeriesBackoffDuration: 2ms
   517  
   518      commitlog:
   519          flushMaxBytes: 524288
   520          flushEvery: 1s
   521          queue:
   522              calculationType: fixed
   523              size: 2097152
   524  
   525      filesystem:
   526          filePathPrefix: {{.DataDir}}
   527          writeBufferSize: 65536
   528          dataReadBufferSize: 65536
   529          infoReadBufferSize: 128
   530          seekReadBufferSize: 4096
   531          throughputLimitMbps: 100.0
   532          throughputCheckEvery: 128
   533  
   534      repair:
   535          enabled: false
   536          throttle: 2m
   537          checkInterval: 1m
   538  
   539      pooling:
   540          blockAllocSize: 16
   541          type: simple
   542          seriesPool:
   543              size: 128
   544              lowWatermark: 0.01
   545              highWatermark: 0.02
   546          blockPool:
   547              size: 128
   548              lowWatermark: 0.01
   549              highWatermark: 0.02
   550          encoderPool:
   551              size: 128
   552              lowWatermark: 0.01
   553              highWatermark: 0.02
   554          closersPool:
   555              size: 128
   556              lowWatermark: 0.01
   557              highWatermark: 0.02
   558          contextPool:
   559              size: 128
   560              lowWatermark: 0.01
   561              highWatermark: 0.02
   562          segmentReaderPool:
   563              size: 128
   564              lowWatermark: 0.01
   565              highWatermark: 0.02
   566          iteratorPool:
   567              size: 128
   568              lowWatermark: 0.01
   569              highWatermark: 0.02
   570          fetchBlockMetadataResultsPool:
   571              size: 128
   572              capacity: 32
   573              lowWatermark: 0.01
   574              highWatermark: 0.02
   575          fetchBlocksMetadataResultsPool:
   576              size: 128
   577              capacity: 128
   578              lowWatermark: 0.01
   579              highWatermark: 0.02
   580          replicaMetadataSlicePool:
   581              size: 128
   582              capacity: 3
   583              lowWatermark: 0.01
   584              highWatermark: 0.02
   585          blockMetadataPool:
   586              size: 128
   587              lowWatermark: 0.01
   588              highWatermark: 0.02
   589          blockMetadataSlicePool:
   590              size: 128
   591              capacity: 32
   592              lowWatermark: 0.01
   593              highWatermark: 0.02
   594          blocksMetadataPool:
   595              size: 128
   596              lowWatermark: 0.01
   597              highWatermark: 0.02
   598          blocksMetadataSlicePool:
   599              size: 128
   600              capacity: 128
   601              lowWatermark: 0.01
   602              highWatermark: 0.02
   603          identifierPool:
   604              size: 128
   605              lowWatermark: 0.01
   606              highWatermark: 0.02
   607          bufferBucketPool:
   608              size: 128
   609              lowWatermark: 0.01
   610              highWatermark: 0.02
   611          bufferBucketVersionsPool:
   612              size: 128
   613              lowWatermark: 0.01
   614              highWatermark: 0.02
   615          bytesPool:
   616              buckets:
   617                  - capacity: 32
   618                    size: 128
   619                  - capacity: 512
   620                    size: 128
   621                  - capacity: 4096
   622                    size: 128
   623  `
   624  
   625  	kvConfigPortion = `
   626      discovery:
   627          config:
   628              service:
   629                  env: {{.ServiceEnv}}
   630                  zone: {{.ServiceZone}}
   631                  service: {{.ServiceName}}
   632                  cacheDir: {{.ConfigServiceCacheDir}}
   633                  etcdClusters:
   634                      - zone: {{.ServiceZone}}
   635                        endpoints: {{.EtcdEndpoints}}
   636                        autoSyncInterval: -1
   637  `
   638  
   639  	embeddedKVConfigPortion = `
   640      discovery:
   641          config:
   642              service:
   643                  env: {{.ServiceEnv}}
   644                  zone: {{.ServiceZone}}
   645                  service: {{.ServiceName}}
   646                  cacheDir: {{.ConfigServiceCacheDir}}
   647                  etcdClusters:
   648                      - zone: {{.ServiceZone}}
   649                        endpoints:
   650                            - {{.EtcdEndpoint}}
   651              seedNodes:
   652                  rootDir: {{.EmbeddedKVDir}}
   653                  listenPeerUrls:
   654                      - {{.LPURL}}
   655                  listenClientUrls:
   656                      - {{.LCURL}}
   657                  initialAdvertisePeerUrls:
   658                      - {{.APURL}}
   659                  advertiseClientUrls:
   660                      - {{.ACURL}}
   661                  initialCluster:
   662                      - hostID: {{.InitialClusterHostID}}
   663                        endpoint: {{.InitialClusterEndpoint}}
   664  `
   665  )