github.com/koko1123/flow-go-1@v0.29.6/cmd/scaffold_test.go (about)

     1  package cmd
     2  
     3  import (
     4  	"bytes"
     5  	"context"
     6  	"errors"
     7  	"fmt"
     8  	"io"
     9  	"net/http"
    10  	"os"
    11  	"path/filepath"
    12  	"strings"
    13  	"sync"
    14  	"testing"
    15  	"time"
    16  
    17  	gcemd "cloud.google.com/go/compute/metadata"
    18  	"github.com/hashicorp/go-multierror"
    19  	"github.com/stretchr/testify/assert"
    20  	"github.com/stretchr/testify/require"
    21  	"google.golang.org/api/option"
    22  	"google.golang.org/grpc"
    23  	"google.golang.org/grpc/credentials/insecure"
    24  
    25  	"github.com/koko1123/flow-go-1/cmd/bootstrap/utils"
    26  	"github.com/koko1123/flow-go-1/model/bootstrap"
    27  	"github.com/koko1123/flow-go-1/module"
    28  	"github.com/koko1123/flow-go-1/module/component"
    29  	"github.com/koko1123/flow-go-1/module/irrecoverable"
    30  	"github.com/koko1123/flow-go-1/module/profiler"
    31  	"github.com/koko1123/flow-go-1/utils/unittest"
    32  )
    33  
    34  // TestLoadSecretsEncryptionKey checks that the key file is read correctly if it exists
    35  // and returns the expected sentinel error if it does not exist.
    36  func TestLoadSecretsEncryptionKey(t *testing.T) {
    37  	myID := unittest.IdentifierFixture()
    38  
    39  	unittest.RunWithTempDir(t, func(dir string) {
    40  		path := filepath.Join(dir, fmt.Sprintf(bootstrap.PathSecretsEncryptionKey, myID))
    41  
    42  		t.Run("should return ErrNotExist if file doesn't exist", func(t *testing.T) {
    43  			require.NoFileExists(t, path)
    44  			_, err := loadSecretsEncryptionKey(dir, myID)
    45  			assert.Error(t, err)
    46  			assert.True(t, errors.Is(err, os.ErrNotExist))
    47  		})
    48  
    49  		t.Run("should return key and no error if file exists", func(t *testing.T) {
    50  			err := os.MkdirAll(filepath.Join(dir, bootstrap.DirPrivateRoot, fmt.Sprintf("private-node-info_%v", myID)), 0700)
    51  			require.NoError(t, err)
    52  			key, err := utils.GenerateSecretsDBEncryptionKey()
    53  			require.NoError(t, err)
    54  			err = os.WriteFile(path, key, 0700)
    55  			require.NoError(t, err)
    56  
    57  			data, err := loadSecretsEncryptionKey(dir, myID)
    58  			assert.NoError(t, err)
    59  			assert.Equal(t, key, data)
    60  		})
    61  	})
    62  }
    63  
    64  // Test the components are started in the correct order, and are run serially
    65  func TestComponentsRunSerially(t *testing.T) {
    66  	ctx, cancel := context.WithCancel(context.Background())
    67  	signalerCtx, _ := irrecoverable.WithSignaler(ctx)
    68  
    69  	nb := FlowNode("scaffold test")
    70  	nb.componentBuilder = component.NewComponentManagerBuilder()
    71  
    72  	logger := &testLog{}
    73  
    74  	name1 := "component 1"
    75  	nb.Component(name1, func(node *NodeConfig) (module.ReadyDoneAware, error) {
    76  		logger.Logf("%s initialized", name1)
    77  		return newMockReadyDone(logger, name1), nil
    78  	})
    79  
    80  	name2 := "component 2"
    81  	nb.Component(name2, func(node *NodeConfig) (module.ReadyDoneAware, error) {
    82  		logger.Logf("%s initialized", name2)
    83  		c := newMockComponent(logger, name2)
    84  		c.startFn = func(ctx irrecoverable.SignalerContext, name string) {
    85  			// add delay to test components are run serially
    86  			time.Sleep(5 * time.Millisecond)
    87  		}
    88  		return c, nil
    89  	})
    90  
    91  	name3 := "component 3"
    92  	nb.Component(name3, func(node *NodeConfig) (module.ReadyDoneAware, error) {
    93  		logger.Logf("%s initialized", name3)
    94  		return newMockReadyDone(logger, name3), nil
    95  	})
    96  
    97  	err := nb.handleComponents()
    98  	assert.NoError(t, err)
    99  
   100  	cm := nb.componentBuilder.Build()
   101  
   102  	cm.Start(signalerCtx)
   103  	<-cm.Ready()
   104  	cancel()
   105  	<-cm.Done()
   106  
   107  	logs := logger.logs
   108  
   109  	assert.Len(t, logs, 10)
   110  
   111  	// components are initialized in a specific order, so check that the order is correct
   112  	startLogs := logs[:len(logs)-3]
   113  	assert.Equal(t, []string{
   114  		"component 1 initialized",
   115  		"component 1 ready",
   116  		"component 2 initialized",
   117  		"component 2 started",
   118  		"component 2 ready",
   119  		"component 3 initialized",
   120  		"component 3 ready",
   121  	}, startLogs)
   122  
   123  	// components are stopped via context cancellation, so the specific order is random
   124  	doneLogs := logs[len(logs)-3:]
   125  	assert.ElementsMatch(t, []string{
   126  		"component 1 done",
   127  		"component 2 done",
   128  		"component 3 done",
   129  	}, doneLogs)
   130  }
   131  
   132  func TestPostShutdown(t *testing.T) {
   133  	nb := FlowNode("scaffold test")
   134  
   135  	logger := testLog{}
   136  
   137  	err1 := errors.New("error 1")
   138  	err3 := errors.New("error 3")
   139  	errExpected := multierror.Append(&multierror.Error{}, err1, err3)
   140  	nb.
   141  		ShutdownFunc(func() error {
   142  			logger.Log("shutdown 1")
   143  			return err1
   144  		}).
   145  		ShutdownFunc(func() error {
   146  			logger.Log("shutdown 2")
   147  			return nil
   148  		}).
   149  		ShutdownFunc(func() error {
   150  			logger.Log("shutdown 3")
   151  			return err3
   152  		})
   153  
   154  	err := nb.postShutdown()
   155  	assert.EqualError(t, err, errExpected.Error())
   156  
   157  	logs := logger.logs
   158  	assert.Len(t, logs, 3)
   159  	assert.Equal(t, []string{
   160  		"shutdown 1",
   161  		"shutdown 2",
   162  		"shutdown 3",
   163  	}, logs)
   164  }
   165  
   166  func TestOverrideComponent(t *testing.T) {
   167  	ctx, cancel := context.WithCancel(context.Background())
   168  	signalerCtx, _ := irrecoverable.WithSignaler(ctx)
   169  
   170  	nb := FlowNode("scaffold test")
   171  	nb.componentBuilder = component.NewComponentManagerBuilder()
   172  
   173  	logger := &testLog{}
   174  
   175  	name1 := "component 1"
   176  	nb.Component(name1, func(node *NodeConfig) (module.ReadyDoneAware, error) {
   177  		logger.Logf("%s initialized", name1)
   178  		return newMockReadyDone(logger, name1), nil
   179  	})
   180  
   181  	name2 := "component 2"
   182  	nb.Component(name2, func(node *NodeConfig) (module.ReadyDoneAware, error) {
   183  		logger.Logf("%s initialized", name2)
   184  		return newMockReadyDone(logger, name2), nil
   185  	})
   186  
   187  	name3 := "component 3"
   188  	nb.Component(name3, func(node *NodeConfig) (module.ReadyDoneAware, error) {
   189  		logger.Logf("%s initialized", name3)
   190  		return newMockReadyDone(logger, name3), nil
   191  	})
   192  
   193  	// Overrides second component
   194  	nb.OverrideComponent(name2, func(node *NodeConfig) (module.ReadyDoneAware, error) {
   195  		logger.Logf("%s overridden", name2)
   196  		return newMockReadyDone(logger, name2), nil
   197  	})
   198  
   199  	err := nb.handleComponents()
   200  	assert.NoError(t, err)
   201  
   202  	cm := nb.componentBuilder.Build()
   203  
   204  	cm.Start(signalerCtx)
   205  
   206  	<-cm.Ready()
   207  
   208  	logs := logger.logs
   209  
   210  	assert.Len(t, logs, 6)
   211  
   212  	// components are initialized in a specific order, so check that the order is correct
   213  	assert.Equal(t, []string{
   214  		"component 1 initialized",
   215  		"component 1 ready",
   216  		"component 2 overridden", // overridden version of 2 should be initialized.
   217  		"component 2 ready",
   218  		"component 3 initialized",
   219  		"component 3 ready",
   220  	}, logs)
   221  
   222  	cancel()
   223  	<-cm.Done()
   224  }
   225  
   226  func TestOverrideModules(t *testing.T) {
   227  	ctx, cancel := context.WithCancel(context.Background())
   228  	signalerCtx, _ := irrecoverable.WithSignaler(ctx)
   229  
   230  	nb := FlowNode("scaffold test")
   231  	nb.componentBuilder = component.NewComponentManagerBuilder()
   232  
   233  	logger := &testLog{}
   234  
   235  	name1 := "module 1"
   236  	nb.Module(name1, func(nodeConfig *NodeConfig) error {
   237  		logger.Logf("%s initialized", name1)
   238  		return nil
   239  	})
   240  
   241  	name2 := "module 2"
   242  	nb.Module(name2, func(nodeConfig *NodeConfig) error {
   243  		logger.Logf("%s initialized", name2)
   244  		return nil
   245  	})
   246  
   247  	name3 := "module 3"
   248  	nb.Module(name3, func(nodeConfig *NodeConfig) error {
   249  		logger.Logf("%s initialized", name3)
   250  		return nil
   251  	})
   252  
   253  	// Overrides second module
   254  	nb.OverrideModule(name2, func(nodeConfig *NodeConfig) error {
   255  		logger.Logf("%s overridden", name2)
   256  		return nil
   257  	})
   258  
   259  	err := nb.handleModules()
   260  	assert.NoError(t, err)
   261  
   262  	cm := nb.componentBuilder.Build()
   263  	require.NoError(t, err)
   264  
   265  	cm.Start(signalerCtx)
   266  
   267  	<-cm.Ready()
   268  
   269  	logs := logger.logs
   270  
   271  	assert.Len(t, logs, 3)
   272  
   273  	// components are initialized in a specific order, so check that the order is correct
   274  	assert.Equal(t, []string{
   275  		"module 1 initialized",
   276  		"module 2 overridden", // overridden version of 2 should be initialized.
   277  		"module 3 initialized",
   278  	}, logs)
   279  
   280  	cancel()
   281  	<-cm.Done()
   282  }
   283  
   284  type testComponentDefinition struct {
   285  	name         string
   286  	factory      ReadyDoneFactory
   287  	errorHandler component.OnError
   288  }
   289  
   290  func runRestartableTest(t *testing.T, components []testComponentDefinition, expectedErr error, shutdown <-chan struct{}) {
   291  	ctx, cancel := context.WithCancel(context.Background())
   292  	signalerCtx, errChan := irrecoverable.WithSignaler(ctx)
   293  
   294  	go func() {
   295  		select {
   296  		case <-ctx.Done():
   297  			return
   298  		case err := <-errChan:
   299  			if expectedErr == nil {
   300  				assert.NoError(t, err, "unexpected unhandled irrecoverable error")
   301  			} else {
   302  				assert.ErrorIs(t, err, expectedErr)
   303  			}
   304  		}
   305  	}()
   306  
   307  	nb := FlowNode("scaffold test")
   308  	nb.componentBuilder = component.NewComponentManagerBuilder()
   309  
   310  	for _, c := range components {
   311  		if c.errorHandler == nil {
   312  			nb.Component(c.name, c.factory)
   313  		} else {
   314  			nb.RestartableComponent(c.name, c.factory, c.errorHandler)
   315  		}
   316  	}
   317  
   318  	err := nb.handleComponents()
   319  	assert.NoError(t, err)
   320  
   321  	cm := nb.componentBuilder.Build()
   322  
   323  	cm.Start(signalerCtx)
   324  
   325  	<-shutdown
   326  	cancel()
   327  
   328  	<-cm.Done()
   329  }
   330  
   331  func TestRestartableRestartsSuccessfully(t *testing.T) {
   332  
   333  	logger := &testLog{}
   334  	shutdown := make(chan struct{})
   335  
   336  	name := "component 1"
   337  	err := fmt.Errorf("%s error", name)
   338  
   339  	starts := 0
   340  	factory := func(node *NodeConfig) (module.ReadyDoneAware, error) {
   341  		logger.Logf("%s initialized", name)
   342  		c := newMockComponent(logger, name)
   343  		c.startFn = func(signalCtx irrecoverable.SignalerContext, name string) {
   344  			go func() {
   345  				<-c.Ready()
   346  				starts++
   347  				if starts == 1 {
   348  					signalCtx.Throw(err)
   349  				}
   350  				close(shutdown)
   351  			}()
   352  		}
   353  		return c, nil
   354  	}
   355  
   356  	runRestartableTest(t, []testComponentDefinition{
   357  		{
   358  			name:         name,
   359  			factory:      factory,
   360  			errorHandler: testErrorHandler(logger, err),
   361  		},
   362  	}, nil, shutdown)
   363  
   364  	assert.Equal(t, []string{
   365  		"component 1 initialized",
   366  		"component 1 started",
   367  		"component 1 ready",
   368  		"component 1 done",
   369  		"handled error: component 1 error",
   370  		"component 1 initialized",
   371  		"component 1 started",
   372  		"component 1 ready",
   373  		"component 1 done",
   374  	}, logger.logs)
   375  }
   376  
   377  func TestRestartableStopsSuccessfully(t *testing.T) {
   378  	logger := &testLog{}
   379  	shutdown := make(chan struct{})
   380  
   381  	name := "component 1"
   382  	err := fmt.Errorf("%s error", name)
   383  	unexpectedErr := fmt.Errorf("%s unexpected error", name)
   384  
   385  	starts := 0
   386  	factory := func(node *NodeConfig) (module.ReadyDoneAware, error) {
   387  		logger.Logf("%s initialized", name)
   388  		c := newMockComponent(logger, name)
   389  		c.startFn = func(signalCtx irrecoverable.SignalerContext, name string) {
   390  			go func() {
   391  				<-c.Ready()
   392  				starts++
   393  				if starts < 2 {
   394  					signalCtx.Throw(err)
   395  				}
   396  				if starts == 2 {
   397  					defer close(shutdown)
   398  					signalCtx.Throw(unexpectedErr)
   399  				}
   400  			}()
   401  		}
   402  		return c, nil
   403  	}
   404  
   405  	runRestartableTest(t, []testComponentDefinition{
   406  		{
   407  			name:         name,
   408  			factory:      factory,
   409  			errorHandler: testErrorHandler(logger, err),
   410  		},
   411  	}, unexpectedErr, shutdown)
   412  
   413  	assert.Equal(t, []string{
   414  		"component 1 initialized",
   415  		"component 1 started",
   416  		"component 1 ready",
   417  		"component 1 done",
   418  		"handled error: component 1 error",
   419  		"component 1 initialized",
   420  		"component 1 started",
   421  		"component 1 ready",
   422  		"component 1 done",
   423  		"handled unexpected error: component 1 unexpected error",
   424  	}, logger.logs)
   425  }
   426  
   427  func TestRestartableWithMultipleComponents(t *testing.T) {
   428  	logger := &testLog{}
   429  	shutdown := make(chan struct{})
   430  
   431  	// synchronization is needed since RestartableComponents are non-blocking
   432  	readyComponents := sync.WaitGroup{}
   433  	readyComponents.Add(3)
   434  
   435  	c1 := func() testComponentDefinition {
   436  		name := "component 1"
   437  		factory := func(node *NodeConfig) (module.ReadyDoneAware, error) {
   438  			logger.Logf("%s initialized", name)
   439  			c := newMockReadyDone(logger, name)
   440  			c.readyFn = func(name string) {
   441  				// delay to demonstrate that components are started serially
   442  				time.Sleep(5 * time.Millisecond)
   443  				readyComponents.Done()
   444  			}
   445  			return c, nil
   446  		}
   447  
   448  		return testComponentDefinition{
   449  			name:    name,
   450  			factory: factory,
   451  		}
   452  	}
   453  
   454  	c2Initialized := make(chan struct{})
   455  	c2 := func() testComponentDefinition {
   456  		name := "component 2"
   457  		err := fmt.Errorf("%s error", name)
   458  		factory := func(node *NodeConfig) (module.ReadyDoneAware, error) {
   459  			defer close(c2Initialized)
   460  			logger.Logf("%s initialized", name)
   461  			c := newMockComponent(logger, name)
   462  			c.startFn = func(ctx irrecoverable.SignalerContext, name string) {
   463  				// delay to demonstrate the RestartableComponent startup is non-blocking
   464  				time.Sleep(5 * time.Millisecond)
   465  			}
   466  			c.readyFn = func(name string) {
   467  				readyComponents.Done()
   468  			}
   469  			return c, nil
   470  		}
   471  
   472  		return testComponentDefinition{
   473  			name:         name,
   474  			factory:      factory,
   475  			errorHandler: testErrorHandler(logger, err),
   476  		}
   477  	}
   478  
   479  	c3 := func() testComponentDefinition {
   480  		name := "component 3"
   481  		err := fmt.Errorf("%s error", name)
   482  		starts := 0
   483  		factory := func(node *NodeConfig) (module.ReadyDoneAware, error) {
   484  			logger.Logf("%s initialized", name)
   485  			c := newMockComponent(logger, name)
   486  			c.startFn = func(signalCtx irrecoverable.SignalerContext, name string) {
   487  				go func() {
   488  					<-c.Ready()
   489  					starts++
   490  					if starts == 1 {
   491  						signalCtx.Throw(err)
   492  					}
   493  					<-c2Initialized // can't use ready since it may not be initialized yet
   494  					readyComponents.Done()
   495  				}()
   496  			}
   497  			return c, nil
   498  		}
   499  
   500  		return testComponentDefinition{
   501  			name:         name,
   502  			factory:      factory,
   503  			errorHandler: testErrorHandler(logger, err),
   504  		}
   505  	}
   506  
   507  	go func() {
   508  		readyComponents.Wait()
   509  		close(shutdown)
   510  	}()
   511  
   512  	runRestartableTest(t, []testComponentDefinition{c1(), c2(), c3()}, nil, shutdown)
   513  
   514  	logs := logger.logs
   515  
   516  	// make sure component 1 is started and ready before any other components start
   517  	assert.Equal(t, []string{
   518  		"component 1 initialized",
   519  		"component 1 ready",
   520  	}, logs[:2])
   521  
   522  	// now split logs by component, and verify we got the right messages/order
   523  	component1 := []string{}
   524  	component2 := []string{}
   525  	component3 := []string{}
   526  	unexpected := []string{}
   527  	for _, l := range logs {
   528  		switch {
   529  		case strings.Contains(l, "component 1"):
   530  			component1 = append(component1, l)
   531  		case strings.Contains(l, "component 2"):
   532  			component2 = append(component2, l)
   533  		case strings.Contains(l, "component 3"):
   534  			component3 = append(component3, l)
   535  		default:
   536  			unexpected = append(unexpected, l)
   537  		}
   538  	}
   539  
   540  	// no unexpected logs
   541  	assert.Len(t, unexpected, 0)
   542  
   543  	assert.Equal(t, []string{
   544  		"component 1 initialized",
   545  		"component 1 ready",
   546  		"component 1 done",
   547  	}, component1)
   548  
   549  	assert.Equal(t, []string{
   550  		"component 2 initialized",
   551  		"component 2 started",
   552  		"component 2 ready",
   553  		"component 2 done",
   554  	}, component2)
   555  
   556  	assert.Equal(t, []string{
   557  		"component 3 initialized",
   558  		"component 3 started",
   559  		"component 3 ready",
   560  		"component 3 done",
   561  		"handled error: component 3 error",
   562  		"component 3 initialized",
   563  		"component 3 started",
   564  		"component 3 ready",
   565  		"component 3 done",
   566  	}, component3)
   567  
   568  	// components are stopped via context cancellation, so the specific order is random
   569  	doneLogs := logs[len(logs)-3:]
   570  	assert.ElementsMatch(t, []string{
   571  		"component 1 done",
   572  		"component 2 done",
   573  		"component 3 done",
   574  	}, doneLogs)
   575  }
   576  
   577  func testErrorHandler(logger *testLog, expected error) component.OnError {
   578  	return func(err error) component.ErrorHandlingResult {
   579  		if errors.Is(err, expected) {
   580  			logger.Logf("handled error: %s", err)
   581  			return component.ErrorHandlingRestart
   582  		}
   583  		logger.Logf("handled unexpected error: %s", err)
   584  		return component.ErrorHandlingStop
   585  	}
   586  }
   587  
   588  // TestDependableComponentWaitForDependencies tests that dependable components are started after
   589  // their dependencies are ready
   590  // In this test:
   591  // * Components 1 & 2 are DependableComponents
   592  // * Component 3 is a normal Component
   593  // * 1 depends on 3
   594  // * 2 depends on 1
   595  // * Start order should be 3, 1, 2
   596  // run test 10 times to ensure order is consistent
   597  func TestDependableComponentWaitForDependencies(t *testing.T) {
   598  	for i := 0; i < 10; i++ {
   599  		testDependableComponentWaitForDependencies(t)
   600  	}
   601  }
   602  
   603  func testDependableComponentWaitForDependencies(t *testing.T) {
   604  	ctx, cancel := context.WithCancel(context.Background())
   605  	signalerCtx, _ := irrecoverable.WithSignaler(ctx)
   606  
   607  	nb := FlowNode("scaffold test")
   608  	nb.componentBuilder = component.NewComponentManagerBuilder()
   609  
   610  	logger := &testLog{}
   611  
   612  	component1Dependable := module.NewProxiedReadyDoneAware()
   613  	component3Dependable := module.NewProxiedReadyDoneAware()
   614  
   615  	name1 := "component 1"
   616  	nb.DependableComponent(name1, func(node *NodeConfig) (module.ReadyDoneAware, error) {
   617  		logger.Logf("%s initialized", name1)
   618  		c := newMockComponent(logger, name1)
   619  		component1Dependable.Init(c)
   620  		return c, nil
   621  	}, &DependencyList{[]module.ReadyDoneAware{component3Dependable}})
   622  
   623  	name2 := "component 2"
   624  	nb.DependableComponent(name2, func(node *NodeConfig) (module.ReadyDoneAware, error) {
   625  		logger.Logf("%s initialized", name2)
   626  		return newMockComponent(logger, name2), nil
   627  	}, &DependencyList{[]module.ReadyDoneAware{component1Dependable}})
   628  
   629  	name3 := "component 3"
   630  	nb.Component(name3, func(node *NodeConfig) (module.ReadyDoneAware, error) {
   631  		logger.Logf("%s initialized", name3)
   632  		c := newMockComponent(logger, name3)
   633  		c.startFn = func(ctx irrecoverable.SignalerContext, name string) {
   634  			// add delay to test components are run serially
   635  			time.Sleep(5 * time.Millisecond)
   636  		}
   637  		component3Dependable.Init(c)
   638  		return c, nil
   639  	})
   640  
   641  	err := nb.handleComponents()
   642  	require.NoError(t, err)
   643  
   644  	cm := nb.componentBuilder.Build()
   645  
   646  	cm.Start(signalerCtx)
   647  	<-cm.Ready()
   648  
   649  	cancel()
   650  	<-cm.Done()
   651  
   652  	logs := logger.logs
   653  
   654  	assert.Len(t, logs, 12)
   655  
   656  	// components are initialized in a specific order, so check that the order is correct
   657  	startLogs := logs[:len(logs)-3]
   658  	assert.Equal(t, []string{
   659  		"component 3 initialized",
   660  		"component 3 started",
   661  		"component 3 ready",
   662  		"component 1 initialized",
   663  		"component 1 started",
   664  		"component 1 ready",
   665  		"component 2 initialized",
   666  		"component 2 started",
   667  		"component 2 ready",
   668  	}, startLogs)
   669  
   670  	// components are stopped via context cancellation, so the specific order is random
   671  	doneLogs := logs[len(logs)-3:]
   672  	assert.ElementsMatch(t, []string{
   673  		"component 1 done",
   674  		"component 2 done",
   675  		"component 3 done",
   676  	}, doneLogs)
   677  }
   678  
   679  func TestCreateUploader(t *testing.T) {
   680  	t.Parallel()
   681  	t.Run("create uploader", func(t *testing.T) {
   682  		t.Parallel()
   683  		nb := FlowNode("scaffold_uploader")
   684  		mockHttp := &http.Client{
   685  			Transport: &mockRoundTripper{
   686  				DoFunc: func(req *http.Request) (*http.Response, error) {
   687  					switch req.URL.Path {
   688  					case "/computeMetadata/v1/project/project-id":
   689  						return &http.Response{
   690  							StatusCode: 200,
   691  							Body:       io.NopCloser(bytes.NewBufferString("test-project-id")),
   692  						}, nil
   693  					case "/computeMetadata/v1/instance/id":
   694  						return &http.Response{
   695  							StatusCode: 200,
   696  							Body:       io.NopCloser(bytes.NewBufferString("test-instance-id")),
   697  						}, nil
   698  					default:
   699  						return nil, fmt.Errorf("unexpected request: %s", req.URL.Path)
   700  					}
   701  				},
   702  			},
   703  		}
   704  
   705  		testClient := gcemd.NewClient(mockHttp)
   706  		uploader, err := nb.createGCEProfileUploader(
   707  			testClient,
   708  
   709  			option.WithoutAuthentication(),
   710  			option.WithGRPCDialOption(grpc.WithTransportCredentials(insecure.NewCredentials())),
   711  		)
   712  		require.NoError(t, err)
   713  		require.NotNil(t, uploader)
   714  
   715  		uploaderImpl, ok := uploader.(*profiler.UploaderImpl)
   716  		require.True(t, ok)
   717  
   718  		assert.Equal(t, "test-project-id", uploaderImpl.Deployment.ProjectId)
   719  		assert.Equal(t, "unknown-scaffold_uploader", uploaderImpl.Deployment.Target)
   720  		assert.Equal(t, "test-instance-id", uploaderImpl.Deployment.Labels["instance"])
   721  		assert.Equal(t, "undefined-undefined", uploaderImpl.Deployment.Labels["version"])
   722  	})
   723  }
   724  
   725  type mockRoundTripper struct {
   726  	DoFunc func(req *http.Request) (*http.Response, error)
   727  }
   728  
   729  func (m *mockRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) {
   730  	return m.DoFunc(req)
   731  }