github.com/cloud-green/juju@v0.0.0-20151002100041-a00291338d3d/provider/gce/testing_test.go (about)

     1  // Copyright 2014 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package gce
     5  
     6  import (
     7  	"fmt"
     8  	"strings"
     9  
    10  	gitjujutesting "github.com/juju/testing"
    11  	jc "github.com/juju/testing/checkers"
    12  	"github.com/juju/utils/arch"
    13  	gc "gopkg.in/check.v1"
    14  
    15  	"github.com/juju/juju/cloudconfig/instancecfg"
    16  	"github.com/juju/juju/cloudconfig/providerinit"
    17  	"github.com/juju/juju/constraints"
    18  	"github.com/juju/juju/environs"
    19  	"github.com/juju/juju/environs/config"
    20  	"github.com/juju/juju/environs/imagemetadata"
    21  	"github.com/juju/juju/environs/instances"
    22  	"github.com/juju/juju/environs/simplestreams"
    23  	"github.com/juju/juju/instance"
    24  	"github.com/juju/juju/network"
    25  	"github.com/juju/juju/provider/common"
    26  	"github.com/juju/juju/provider/gce/google"
    27  	"github.com/juju/juju/testing"
    28  	"github.com/juju/juju/tools"
    29  	"github.com/juju/juju/version"
    30  )
    31  
    32  // These values are fake GCE auth credentials for use in tests.
    33  const (
    34  	ClientName  = "ba9876543210-0123456789abcdefghijklmnopqrstuv"
    35  	ClientID    = ClientName + ".apps.googleusercontent.com"
    36  	ClientEmail = ClientName + "@developer.gserviceaccount.com"
    37  	PrivateKey  = `-----BEGIN PRIVATE KEY-----
    38  ...
    39  ...
    40  ...
    41  ...
    42  ...
    43  ...
    44  ...
    45  ...
    46  ...
    47  ...
    48  ...
    49  ...
    50  ...
    51  ...
    52  -----END PRIVATE KEY-----
    53  `
    54  )
    55  
    56  // These are fake config values for use in tests.
    57  var (
    58  	AuthFile = fmt.Sprintf(`{
    59    "private_key_id": "abcdef0123456789abcdef0123456789abcdef01",
    60    "private_key": "%s",
    61    "client_email": "%s",
    62    "client_id": "%s",
    63    "type": "service_account"
    64  }`, strings.Replace(PrivateKey, "\n", "\\n", -1), ClientEmail, ClientID)
    65  
    66  	ConfigAttrs = testing.FakeConfig().Merge(testing.Attrs{
    67  		"type":           "gce",
    68  		"auth-file":      "",
    69  		"private-key":    PrivateKey,
    70  		"client-id":      ClientID,
    71  		"client-email":   ClientEmail,
    72  		"region":         "home",
    73  		"project-id":     "my-juju",
    74  		"image-endpoint": "https://www.googleapis.com",
    75  		"uuid":           "2d02eeac-9dbb-11e4-89d3-123b93f75cba",
    76  	})
    77  )
    78  
    79  type BaseSuiteUnpatched struct {
    80  	gitjujutesting.IsolationSuite
    81  
    82  	Config    *config.Config
    83  	EnvConfig *environConfig
    84  	Env       *environ
    85  	Prefix    string
    86  
    87  	Addresses       []network.Address
    88  	BaseInstance    *google.Instance
    89  	BaseDisk        *google.Disk
    90  	Instance        *environInstance
    91  	InstName        string
    92  	UbuntuMetadata  map[string]string
    93  	WindowsMetadata map[string]string
    94  	StartInstArgs   environs.StartInstanceParams
    95  	InstanceType    instances.InstanceType
    96  
    97  	Ports []network.PortRange
    98  }
    99  
   100  var _ environs.Environ = (*environ)(nil)
   101  var _ simplestreams.HasRegion = (*environ)(nil)
   102  var _ instance.Instance = (*environInstance)(nil)
   103  
   104  func (s *BaseSuiteUnpatched) SetUpTest(c *gc.C) {
   105  	s.IsolationSuite.SetUpTest(c)
   106  
   107  	s.initEnv(c)
   108  	s.initInst(c)
   109  	s.initNet(c)
   110  }
   111  
   112  func (s *BaseSuiteUnpatched) initEnv(c *gc.C) {
   113  	s.Env = &environ{
   114  		name: "google",
   115  	}
   116  	cfg := s.NewConfig(c, nil)
   117  	s.setConfig(c, cfg)
   118  }
   119  
   120  func (s *BaseSuiteUnpatched) initInst(c *gc.C) {
   121  	tools := []*tools.Tools{{
   122  		Version: version.Binary{Arch: arch.AMD64, Series: "trusty"},
   123  		URL:     "https://example.org",
   124  	}}
   125  
   126  	cons := constraints.Value{InstanceType: &allInstanceTypes[0].Name}
   127  
   128  	instanceConfig, err := instancecfg.NewBootstrapInstanceConfig(cons, "trusty")
   129  	c.Assert(err, jc.ErrorIsNil)
   130  
   131  	instanceConfig.Tools = tools[0]
   132  	instanceConfig.AuthorizedKeys = s.Config.AuthorizedKeys()
   133  
   134  	userData, err := providerinit.ComposeUserData(instanceConfig, nil, GCERenderer{})
   135  	c.Assert(err, jc.ErrorIsNil)
   136  
   137  	authKeys, err := google.FormatAuthorizedKeys(instanceConfig.AuthorizedKeys, "ubuntu")
   138  	c.Assert(err, jc.ErrorIsNil)
   139  
   140  	s.UbuntuMetadata = map[string]string{
   141  		metadataKeyIsState:   metadataValueTrue,
   142  		metadataKeyCloudInit: string(userData),
   143  		metadataKeyEncoding:  "base64",
   144  		metadataKeySSHKeys:   authKeys,
   145  	}
   146  	s.WindowsMetadata = map[string]string{
   147  		metadataKeyWindowsUserdata: string(userData),
   148  		metadataKeyWindowsSysprep:  fmt.Sprintf(winSetHostnameScript, "juju.*"),
   149  	}
   150  	s.Addresses = []network.Address{{
   151  		Value: "10.0.0.1",
   152  		Type:  network.IPv4Address,
   153  		Scope: network.ScopeCloudLocal,
   154  	}}
   155  	s.Instance = s.NewInstance(c, "spam")
   156  	s.BaseInstance = s.Instance.base
   157  	s.InstName = s.Prefix + "machine-spam"
   158  
   159  	s.StartInstArgs = environs.StartInstanceParams{
   160  		InstanceConfig: instanceConfig,
   161  		Tools:          tools,
   162  		Constraints:    cons,
   163  		//Placement: "",
   164  		//DistributionGroup: nil,
   165  	}
   166  
   167  	s.InstanceType = allInstanceTypes[0]
   168  	// Storage
   169  	s.BaseDisk = &google.Disk{
   170  		Id:     1234567,
   171  		Name:   "home-zone--c930380d-8337-4bf5-b07a-9dbb5ae771e4",
   172  		Zone:   "home-zone",
   173  		Status: google.StatusReady,
   174  		Size:   1024,
   175  	}
   176  }
   177  
   178  func (s *BaseSuiteUnpatched) initNet(c *gc.C) {
   179  	s.Ports = []network.PortRange{{
   180  		FromPort: 80,
   181  		ToPort:   80,
   182  		Protocol: "tcp",
   183  	}}
   184  }
   185  
   186  func (s *BaseSuiteUnpatched) setConfig(c *gc.C, cfg *config.Config) {
   187  	s.Config = cfg
   188  	ecfg, err := newValidConfig(cfg, configDefaults)
   189  	c.Assert(err, jc.ErrorIsNil)
   190  	s.EnvConfig = ecfg
   191  	uuid, _ := cfg.UUID()
   192  	s.Env.uuid = uuid
   193  	s.Env.ecfg = s.EnvConfig
   194  	s.Prefix = "juju-" + uuid + "-"
   195  }
   196  
   197  func (s *BaseSuiteUnpatched) NewConfig(c *gc.C, updates testing.Attrs) *config.Config {
   198  	var err error
   199  	cfg := testing.EnvironConfig(c)
   200  	cfg, err = cfg.Apply(ConfigAttrs)
   201  	c.Assert(err, jc.ErrorIsNil)
   202  	cfg, err = cfg.Apply(updates)
   203  	c.Assert(err, jc.ErrorIsNil)
   204  	return cfg
   205  }
   206  
   207  func (s *BaseSuiteUnpatched) UpdateConfig(c *gc.C, attrs map[string]interface{}) {
   208  	cfg, err := s.Config.Apply(attrs)
   209  	c.Assert(err, jc.ErrorIsNil)
   210  	s.setConfig(c, cfg)
   211  }
   212  
   213  func (s *BaseSuiteUnpatched) NewBaseInstance(c *gc.C, id string) *google.Instance {
   214  	diskSpec := google.DiskSpec{
   215  		SizeHintGB: 15,
   216  		ImageURL:   "some/image/path",
   217  		Boot:       true,
   218  		Scratch:    false,
   219  		Readonly:   false,
   220  		AutoDelete: true,
   221  	}
   222  	instanceSpec := google.InstanceSpec{
   223  		ID:                id,
   224  		Type:              "mtype",
   225  		Disks:             []google.DiskSpec{diskSpec},
   226  		Network:           google.NetworkSpec{Name: "somenetwork"},
   227  		NetworkInterfaces: []string{"somenetif"},
   228  		Metadata:          s.UbuntuMetadata,
   229  		Tags:              []string{id},
   230  	}
   231  	summary := google.InstanceSummary{
   232  		ID:        id,
   233  		ZoneName:  "home-zone",
   234  		Status:    google.StatusRunning,
   235  		Metadata:  s.UbuntuMetadata,
   236  		Addresses: s.Addresses,
   237  	}
   238  	return google.NewInstance(summary, &instanceSpec)
   239  }
   240  
   241  func (s *BaseSuiteUnpatched) NewInstance(c *gc.C, id string) *environInstance {
   242  	base := s.NewBaseInstance(c, id)
   243  	return newInstance(base, s.Env)
   244  }
   245  
   246  type BaseSuite struct {
   247  	BaseSuiteUnpatched
   248  
   249  	FakeConn    *fakeConn
   250  	FakeCommon  *fakeCommon
   251  	FakeEnviron *fakeEnviron
   252  	FakeImages  *fakeImages
   253  }
   254  
   255  func (s *BaseSuite) SetUpTest(c *gc.C) {
   256  	s.BaseSuiteUnpatched.SetUpTest(c)
   257  
   258  	s.FakeConn = &fakeConn{}
   259  	s.FakeCommon = &fakeCommon{}
   260  	s.FakeEnviron = &fakeEnviron{}
   261  	s.FakeImages = &fakeImages{}
   262  
   263  	// Patch out all expensive external deps.
   264  	s.Env.gce = s.FakeConn
   265  	s.PatchValue(&newConnection, func(*environConfig) (gceConnection, error) {
   266  		return s.FakeConn, nil
   267  	})
   268  	s.PatchValue(&supportedArchitectures, s.FakeCommon.SupportedArchitectures)
   269  	s.PatchValue(&bootstrap, s.FakeCommon.Bootstrap)
   270  	s.PatchValue(&destroyEnv, s.FakeCommon.Destroy)
   271  	s.PatchValue(&availabilityZoneAllocations, s.FakeCommon.AvailabilityZoneAllocations)
   272  	s.PatchValue(&buildInstanceSpec, s.FakeEnviron.BuildInstanceSpec)
   273  	s.PatchValue(&getHardwareCharacteristics, s.FakeEnviron.GetHardwareCharacteristics)
   274  	s.PatchValue(&newRawInstance, s.FakeEnviron.NewRawInstance)
   275  	s.PatchValue(&findInstanceSpec, s.FakeEnviron.FindInstanceSpec)
   276  	s.PatchValue(&getInstances, s.FakeEnviron.GetInstances)
   277  	s.PatchValue(&imageMetadataFetch, s.FakeImages.ImageMetadataFetch)
   278  }
   279  
   280  func (s *BaseSuite) CheckNoAPI(c *gc.C) {
   281  	c.Check(s.FakeConn.Calls, gc.HasLen, 0)
   282  }
   283  
   284  // TODO(ericsnow) Move fakeCallArgs, fakeCall, and fake to the testing repo?
   285  
   286  type FakeCallArgs map[string]interface{}
   287  
   288  type FakeCall struct {
   289  	FuncName string
   290  	Args     FakeCallArgs
   291  }
   292  
   293  type fake struct {
   294  	calls []FakeCall
   295  
   296  	Err        error
   297  	FailOnCall int
   298  }
   299  
   300  func (f *fake) err() error {
   301  	if len(f.calls) != f.FailOnCall+1 {
   302  		return nil
   303  	}
   304  	return f.Err
   305  }
   306  
   307  func (f *fake) addCall(funcName string, args FakeCallArgs) {
   308  	f.calls = append(f.calls, FakeCall{
   309  		FuncName: funcName,
   310  		Args:     args,
   311  	})
   312  }
   313  
   314  func (f *fake) CheckCalls(c *gc.C, expected []FakeCall) {
   315  	c.Check(f.calls, jc.DeepEquals, expected)
   316  }
   317  
   318  type fakeCommon struct {
   319  	fake
   320  
   321  	Arches      []string
   322  	Arch        string
   323  	Series      string
   324  	BSFinalizer environs.BootstrapFinalizer
   325  	AZInstances []common.AvailabilityZoneInstances
   326  }
   327  
   328  func (fc *fakeCommon) SupportedArchitectures(env environs.Environ, cons *imagemetadata.ImageConstraint) ([]string, error) {
   329  	fc.addCall("SupportedArchitectures", FakeCallArgs{
   330  		"env":  env,
   331  		"cons": cons,
   332  	})
   333  	return fc.Arches, fc.err()
   334  }
   335  
   336  func (fc *fakeCommon) Bootstrap(ctx environs.BootstrapContext, env environs.Environ, params environs.BootstrapParams) (string, string, environs.BootstrapFinalizer, error) {
   337  	fc.addCall("Bootstrap", FakeCallArgs{
   338  		"ctx":    ctx,
   339  		"env":    env,
   340  		"params": params,
   341  	})
   342  	return fc.Arch, fc.Series, fc.BSFinalizer, fc.err()
   343  }
   344  
   345  func (fc *fakeCommon) Destroy(env environs.Environ) error {
   346  	fc.addCall("Destroy", FakeCallArgs{
   347  		"env": env,
   348  	})
   349  	return fc.err()
   350  }
   351  
   352  func (fc *fakeCommon) AvailabilityZoneAllocations(env common.ZonedEnviron, group []instance.Id) ([]common.AvailabilityZoneInstances, error) {
   353  	fc.addCall("AvailabilityZoneAllocations", FakeCallArgs{
   354  		"env":   env,
   355  		"group": group,
   356  	})
   357  	return fc.AZInstances, fc.err()
   358  }
   359  
   360  type fakeEnviron struct {
   361  	fake
   362  
   363  	Inst  *google.Instance
   364  	Insts []instance.Instance
   365  	Hwc   *instance.HardwareCharacteristics
   366  	Spec  *instances.InstanceSpec
   367  }
   368  
   369  func (fe *fakeEnviron) GetInstances(env *environ) ([]instance.Instance, error) {
   370  	fe.addCall("GetInstances", FakeCallArgs{
   371  		"env": env,
   372  	})
   373  	return fe.Insts, fe.err()
   374  }
   375  
   376  func (fe *fakeEnviron) BuildInstanceSpec(env *environ, args environs.StartInstanceParams) (*instances.InstanceSpec, error) {
   377  	fe.addCall("BuildInstanceSpec", FakeCallArgs{
   378  		"env":  env,
   379  		"args": args,
   380  	})
   381  	return fe.Spec, fe.err()
   382  }
   383  
   384  func (fe *fakeEnviron) GetHardwareCharacteristics(env *environ, spec *instances.InstanceSpec, inst *environInstance) *instance.HardwareCharacteristics {
   385  	fe.addCall("GetHardwareCharacteristics", FakeCallArgs{
   386  		"env":  env,
   387  		"spec": spec,
   388  		"inst": inst,
   389  	})
   390  	return fe.Hwc
   391  }
   392  
   393  func (fe *fakeEnviron) NewRawInstance(env *environ, args environs.StartInstanceParams, spec *instances.InstanceSpec) (*google.Instance, error) {
   394  	fe.addCall("NewRawInstance", FakeCallArgs{
   395  		"env":  env,
   396  		"args": args,
   397  		"spec": spec,
   398  	})
   399  	return fe.Inst, fe.err()
   400  }
   401  
   402  func (fe *fakeEnviron) FindInstanceSpec(env *environ, stream string, ic *instances.InstanceConstraint) (*instances.InstanceSpec, error) {
   403  	fe.addCall("FindInstanceSpec", FakeCallArgs{
   404  		"env":    env,
   405  		"stream": stream,
   406  		"ic":     ic,
   407  	})
   408  	return fe.Spec, fe.err()
   409  }
   410  
   411  type fakeImages struct {
   412  	fake
   413  
   414  	Metadata    []*imagemetadata.ImageMetadata
   415  	ResolveInfo *simplestreams.ResolveInfo
   416  }
   417  
   418  func (fi *fakeImages) ImageMetadataFetch(sources []simplestreams.DataSource, cons *imagemetadata.ImageConstraint, onlySigned bool) ([]*imagemetadata.ImageMetadata, *simplestreams.ResolveInfo, error) {
   419  	return fi.Metadata, fi.ResolveInfo, fi.err()
   420  }
   421  
   422  // TODO(ericsnow) Refactor fakeConnCall and fakeConn to embed fakeCall and fake.
   423  
   424  type fakeConnCall struct {
   425  	FuncName string
   426  
   427  	ID           string
   428  	IDs          []string
   429  	ZoneName     string
   430  	ZoneNames    []string
   431  	Prefix       string
   432  	Statuses     []string
   433  	InstanceSpec google.InstanceSpec
   434  	FirewallName string
   435  	PortRanges   []network.PortRange
   436  	Region       string
   437  	Disks        []google.DiskSpec
   438  	VolumeName   string
   439  	InstanceId   string
   440  	Mode         string
   441  }
   442  
   443  type fakeConn struct {
   444  	Calls []fakeConnCall
   445  
   446  	Inst       *google.Instance
   447  	Insts      []google.Instance
   448  	PortRanges []network.PortRange
   449  	Zones      []google.AvailabilityZone
   450  
   451  	GoogleDisks   []*google.Disk
   452  	GoogleDisk    *google.Disk
   453  	AttachedDisk  *google.AttachedDisk
   454  	AttachedDisks []*google.AttachedDisk
   455  
   456  	Err        error
   457  	FailOnCall int
   458  }
   459  
   460  func (fc *fakeConn) err() error {
   461  	if len(fc.Calls) != fc.FailOnCall+1 {
   462  		return nil
   463  	}
   464  	return fc.Err
   465  }
   466  
   467  func (fc *fakeConn) VerifyCredentials() error {
   468  	fc.Calls = append(fc.Calls, fakeConnCall{
   469  		FuncName: "",
   470  	})
   471  	return fc.err()
   472  }
   473  
   474  func (fc *fakeConn) Instance(id, zone string) (google.Instance, error) {
   475  	fc.Calls = append(fc.Calls, fakeConnCall{
   476  		FuncName: "Instance",
   477  		ID:       id,
   478  		ZoneName: zone,
   479  	})
   480  	return *fc.Inst, fc.err()
   481  }
   482  
   483  func (fc *fakeConn) Instances(prefix string, statuses ...string) ([]google.Instance, error) {
   484  	fc.Calls = append(fc.Calls, fakeConnCall{
   485  		FuncName: "Instances",
   486  		Prefix:   prefix,
   487  		Statuses: statuses,
   488  	})
   489  	return fc.Insts, fc.err()
   490  }
   491  
   492  func (fc *fakeConn) AddInstance(spec google.InstanceSpec, zones ...string) (*google.Instance, error) {
   493  	fc.Calls = append(fc.Calls, fakeConnCall{
   494  		FuncName:     "AddInstance",
   495  		InstanceSpec: spec,
   496  		ZoneNames:    zones,
   497  	})
   498  	return fc.Inst, fc.err()
   499  }
   500  
   501  func (fc *fakeConn) RemoveInstances(prefix string, ids ...string) error {
   502  	fc.Calls = append(fc.Calls, fakeConnCall{
   503  		FuncName: "RemoveInstances",
   504  		Prefix:   prefix,
   505  		IDs:      ids,
   506  	})
   507  	return fc.err()
   508  }
   509  
   510  func (fc *fakeConn) Ports(fwname string) ([]network.PortRange, error) {
   511  	fc.Calls = append(fc.Calls, fakeConnCall{
   512  		FuncName:     "Ports",
   513  		FirewallName: fwname,
   514  	})
   515  	return fc.PortRanges, fc.err()
   516  }
   517  
   518  func (fc *fakeConn) OpenPorts(fwname string, ports ...network.PortRange) error {
   519  	fc.Calls = append(fc.Calls, fakeConnCall{
   520  		FuncName:     "OpenPorts",
   521  		FirewallName: fwname,
   522  		PortRanges:   ports,
   523  	})
   524  	return fc.err()
   525  }
   526  
   527  func (fc *fakeConn) ClosePorts(fwname string, ports ...network.PortRange) error {
   528  	fc.Calls = append(fc.Calls, fakeConnCall{
   529  		FuncName:     "ClosePorts",
   530  		FirewallName: fwname,
   531  		PortRanges:   ports,
   532  	})
   533  	return fc.err()
   534  }
   535  
   536  func (fc *fakeConn) AvailabilityZones(region string) ([]google.AvailabilityZone, error) {
   537  	fc.Calls = append(fc.Calls, fakeConnCall{
   538  		FuncName: "AvailabilityZones",
   539  		Region:   region,
   540  	})
   541  	return fc.Zones, fc.err()
   542  }
   543  
   544  func (fc *fakeConn) CreateDisks(zone string, disks []google.DiskSpec) ([]*google.Disk, error) {
   545  	fc.Calls = append(fc.Calls, fakeConnCall{
   546  		FuncName: "CreateDisks",
   547  		ZoneName: zone,
   548  		Disks:    disks,
   549  	})
   550  	return fc.GoogleDisks, fc.err()
   551  }
   552  
   553  func (fc *fakeConn) Disks(zone string) ([]*google.Disk, error) {
   554  	fc.Calls = append(fc.Calls, fakeConnCall{
   555  		FuncName: "Disks",
   556  		ZoneName: zone,
   557  	})
   558  	return fc.GoogleDisks, fc.err()
   559  }
   560  
   561  func (fc *fakeConn) RemoveDisk(zone, id string) error {
   562  	fc.Calls = append(fc.Calls, fakeConnCall{
   563  		FuncName: "RemoveDisk",
   564  		ZoneName: zone,
   565  		ID:       id,
   566  	})
   567  	return fc.err()
   568  }
   569  
   570  func (fc *fakeConn) Disk(zone, id string) (*google.Disk, error) {
   571  	fc.Calls = append(fc.Calls, fakeConnCall{
   572  		FuncName: "Disk",
   573  		ZoneName: zone,
   574  		ID:       id,
   575  	})
   576  	return fc.GoogleDisk, fc.err()
   577  }
   578  
   579  func (fc *fakeConn) AttachDisk(zone, volumeName, instanceId string, mode google.DiskMode) (*google.AttachedDisk, error) {
   580  	fc.Calls = append(fc.Calls, fakeConnCall{
   581  		FuncName:   "AttachDisk",
   582  		ZoneName:   zone,
   583  		VolumeName: volumeName,
   584  		InstanceId: instanceId,
   585  		Mode:       string(mode),
   586  	})
   587  	return fc.AttachedDisk, fc.err()
   588  }
   589  
   590  func (fc *fakeConn) DetachDisk(zone, instanceId, volumeName string) error {
   591  	fc.Calls = append(fc.Calls, fakeConnCall{
   592  		FuncName:   "DetachDisk",
   593  		ZoneName:   zone,
   594  		InstanceId: instanceId,
   595  		VolumeName: volumeName,
   596  	})
   597  	return fc.err()
   598  }
   599  
   600  func (fc *fakeConn) InstanceDisks(zone, instanceId string) ([]*google.AttachedDisk, error) {
   601  	fc.Calls = append(fc.Calls, fakeConnCall{
   602  		FuncName:   "InstanceDisks",
   603  		ZoneName:   zone,
   604  		InstanceId: instanceId,
   605  	})
   606  	return fc.AttachedDisks, fc.err()
   607  }
   608  
   609  func (fc *fakeConn) WasCalled(funcName string) (bool, []fakeConnCall) {
   610  	var calls []fakeConnCall
   611  	called := false
   612  	for _, call := range fc.Calls {
   613  		if call.FuncName == funcName {
   614  			called = true
   615  			calls = append(calls, call)
   616  		}
   617  	}
   618  	return called, calls
   619  }